Skip to content

Latest commit

 

History

History
38 lines (30 loc) · 4.48 KB

opencode-embed.md

File metadata and controls

38 lines (30 loc) · 4.48 KB
kind explanation
adrs
22
25
55

OpenCode Embed

How is OpenCode's chat rendered inside the Opsiforce frontend, and how does it reach the pod through the platform's own auth and proxy?

How it works

  • The frontend imports OpenCode's Solid.js components (AppBaseProviders, AppInterface) directly: a custom Vite resolver maps @opencode/* to the vendored source under frontend/opencode/, and the components render inline in the platform SPA. The vendored tree is an upstream release tag copied wholesale, minus upstream's agent instructions; the frontend declares the npm packages it imports and mirrors upstream's Vite settings for the Office previews.
  • The @/ alias points into the vendored OpenCode tree and ~/ into platform code; the vendored tree is read-only and fixes happen on the consuming side.
  • Chat boot runs in a separate Solid render root behind a connecting overlay that is dismissed by observing the DOM, never inside a platform Suspense boundary (ADR-0026).
  • The embed reaches the pod's OpenCode through /api/proxy/{environmentId} like every other client (Request Flows); OpenCode v2's Basic auth is injected by agent-control inside the pod, so no external caller changes, and the embed's fetch adapter reloads the page when the platform session expires (a 401) instead of letting OpenCode ask for a server URL (ADR-0031).
  • The embed is chat-only: CSS scoped to the chat tab hides OpenCode's own chrome (titlebar, side region, review toggle, attach button) and every surface its built-in GUI extensions add (terminal dock, narrow-screen view switcher, their slash commands, the debug bar); the chat header keeps timeline search, shows context usage read-only (its click and tooltip only open the hidden side region), and hides Details, a popover of OpenCode's own workspace, git and extension state.
  • The platform and the embed share one TanStack QueryClient, and the platform forces the theme; every platform read goes through the store-backed Query Adapter so refetches never detach the chat's DOM.
  • A mutation observer over the embed's rendered markdown decorates /workspace/... links into file cards (File preview).
  • The chat tab stays mounted after the user switches tabs, so returning to it never reloads the conversation (Pod Tools).
  • The same embed is mounted a second time on a Builder chat at /store/agents/{id}/builder and /store/skills/{id}/builder, which is carried by a Project like any other and connects to its Development ProjectEnvironment; it renders the ordinary Project chassis beside its Test Project, and the boot, the router and the proxy path are unchanged.
  • The platform restyles the embed only from outside: opencode-overrides.tsx sets OpenCode's own preferences, and opencode-overrides.css hides the internals the platform replaces (title bar, session tabs, review toggle) and lets the composer's agent, model and variant pickers shrink so they never cover its Send button.
  • Every platform helper that reaches into the embed's markup — dictation, file upload, workspace file links, the App preview iframe — is scoped to the host element its own instance rendered into, never to document, because two embeds are visible at once on a Builder chat (ADR-0067).

Code

  • frontend/vite-plugins/ — the resolver that maps the OpenCode packages to the vendored source.
  • frontend/opencode/ — vendored, never edited.
  • frontend/src/components/project/project-chat-tab.tsx — the render root, the overlay and the fetch adapter.
  • frontend/src/components/project/opencode-overrides.css — the .oc-chat-only rules that hide everything but the chat.
  • frontend/src/pages/store/builder-page.tsx — the Builder chat page that mounts the same component.
  • proxy/cmd/agent-control/opencode_proxy.go — Basic auth injection in front of OpenCode.

Related