98e40dac97
CLI Smoke Test / smoke-test-linux (20) (push) Has been cancelled
CLI Smoke Test / smoke-test-linux (24) (push) Has been cancelled
CLI Smoke Test / smoke-test-windows (20) (push) Has been cancelled
CLI Smoke Test / smoke-test-windows (24) (push) Has been cancelled
Expo App TypeScript typecheck / typecheck (push) Has been cancelled
150 lines
6.2 KiB
Markdown
150 lines
6.2 KiB
Markdown
# 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`, and `compaction`
|
|
- 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`, and `permission.asked`
|
|
- `message.part.delta` is the streaming primitive for appending text into a named field
|
|
- later `message.part.updated` events 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 `task` tool chooses a non-primary agent and creates or resumes a child session
|
|
- `task_id` is 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`, and `variant`
|
|
- 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`, or `reject`
|
|
- 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
|