| kind | explanation | |||
|---|---|---|---|---|
| adrs |
|
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?
- The frontend imports OpenCode's Solid.js components (
AppBaseProviders,AppInterface) directly: a custom Vite resolver maps@opencode/*to the vendored source underfrontend/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}/builderand/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.tsxsets OpenCode's own preferences, andopencode-overrides.csshides 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).
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-onlyrules 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.
- Query Adapter — why platform reads avoid the stock TanStack adapter.
- Request Flows — the agent proxy path the embed uses.
- Connection — how a program reaches the same runtime.
- Pod Tools — the tabs around the chat.