Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:40:49 +08:00

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