6.2 KiB
OpenCode Message Protocol
Bottom line
OpenCode has the cleanest transcript shape of the three systems reviewed so far. If Happy wants a strong protocol reference for app + server + session UI, this is the one to steal from first.
Core transcript model
The key design is: message envelope first, typed parts second.
- messages are stable top-level records with IDs, session linkage, model/provider metadata, path, token usage, cost, and error state
- content is not a single blob; it is an ordered list of typed parts
- important part kinds include
text,reasoning,tool,file,snapshot,patch,agent,subtask,step-start,step-finish, andcompaction - this makes streaming, partial updates, replay, and debug rendering much cleaner than mutating one giant assistant string
Primary source files:
../happy-adjacent/research/opencode/packages/opencode/src/session/message-v2.ts../happy-adjacent/research/opencode/packages/sdk/js/src/v2/gen/types.gen.ts
Live event model
OpenCode separates full transcript state from incremental transport events.
- canonical live events include
message.updated,message.part.updated,message.part.delta,message.part.removed,message.removed,session.status,todo.updated, andpermission.asked message.part.deltais the streaming primitive for appending text into a named field- later
message.part.updatedevents can replace or supersede earlier deltas - the UI reducer explicitly merges stream events into cached transcript state
This is a very good pattern for Happy: use events for freshness, not as the only source of truth.
Primary source files:
../happy-adjacent/research/opencode/packages/opencode/src/session/index.ts../happy-adjacent/research/opencode/packages/app/src/context/global-sync/event-reducer.ts
Subagents and task delegation
OpenCode models delegation as child sessions, not inline mystery behavior.
- the
tasktool chooses a non-primary agent and creates or resumes a child session task_idis really the child session ID, so delegation is resumable- subagent intent is visible in the parent transcript as a tool call and related subtask part
- tool permissions for subagents are intentionally constrained
This is much closer to what Happy should want than flattening delegated work into one chat thread without identity.
Primary source files:
../happy-adjacent/research/opencode/packages/opencode/src/tool/task.ts../happy-adjacent/research/opencode/packages/opencode/src/agent/agent.ts
Task tracking / todos
Todos are a first-class session store.
- todo state is separate from transcript parts
- write behavior is whole-list replacement with ordered rows
- schema is intentionally tiny:
content,status,priority - UI gets a proper todo dock instead of scraping plans from text
This is a strong design signal for Happy: todos should be their own state channel.
Primary source files:
../happy-adjacent/research/opencode/packages/opencode/src/tool/todo.ts../happy-adjacent/research/opencode/packages/opencode/src/session/todo.ts
Modes, models, and permissions
OpenCode treats these as explicit state, not just prompt flavor.
- newer logic keys on
agent,providerID,modelID, andvariant - plan/build/general/explore are modeled as agent choices more than a free-form mode string
- permissions are first-class requests with
id,sessionID,permission, patterns, metadata, and decision mode - decisions can be
once,always, orreject - permission rules are pattern-based and support auto-unblocking pending matching requests
This is a good reference for Happy's app model even if Happy keeps its own policy engine.
Primary source files:
../happy-adjacent/research/opencode/packages/opencode/src/permission/index.ts../happy-adjacent/research/opencode/packages/opencode/src/permission/evaluate.ts../happy-adjacent/research/opencode/packages/app/src/context/permission.tsx
Sandbox and isolation
OpenCode is weaker on true sandboxing than Codex.
- the main isolation story is workspace and git worktree separation
- extra workspaces are treated like sandboxes in product language
- the server routes operations by workspace directory
- there is not much evidence here of strong OS-level sandbox policy comparable to Codex
Takeaway for Happy: copy the workspace isolation ideas, not the lack of a deeper sandbox layer.
Primary source files:
../happy-adjacent/research/opencode/packages/opencode/src/worktree/index.ts../happy-adjacent/research/opencode/packages/opencode/src/control-plane/workspace.ts
Sync and server architecture
This is probably the most valuable follow-up topic.
- the app listens to a global SSE stream
- events are then fanned into per-directory caches and reducers
- full session history is fetched separately when needed
- the client batches and coalesces updates instead of repainting on every raw event
- the control plane already looks ready for non-local workspace adaptors later
This is a much better direction for Happy than a single opaque message pipeline.
Primary source files:
../happy-adjacent/research/opencode/packages/opencode/src/control-plane/workspace-server/routes.ts../happy-adjacent/research/opencode/packages/opencode/src/control-plane/sse.ts../happy-adjacent/research/opencode/packages/app/src/context/global-sync.tsx../happy-adjacent/research/opencode/packages/app/src/context/sync.tsx
Context debug surface
The user feedback here is correct and important.
- clicking the context usage field opens a context tab
- that tab shows a useful breakdown of model/provider/context usage
- it also exposes raw message-plus-parts state, effectively a built-in protocol debugger
Happy should copy this idea.
Primary source files:
../happy-adjacent/research/opencode/packages/app/src/components/session-context-usage.tsx../happy-adjacent/research/opencode/packages/app/src/components/session/session-context-tab.tsx
What Happy should steal
- envelope + typed-parts transcript structure
- child-session subagent model with resumable IDs
- first-class todo and permission stores
- event stream for freshness plus fetch API for hydration
- built-in raw context/messages inspector in the UI