Repository navigation
Description
Tracking issue for #394 phase 7 — wiring packages/opencode-headless as the third provider. This is the acceptance test of the plug-and-play architecture: opencode has no PTY, no screen, no keystrokes, no transcript files.
Contract surface (compiler-enforced once 'opencode' joins AGENT_PROVIDER_KINDS)
registry.main (createSession→AgentSession, listSessions, getProjectDir, resolveTranscriptPath, deliverPrompt) · registry.renderer.capabilities (identity, conditionViews/policy, mapper, extractProviderSessionId, composerSubmit, flags) · registry.renderer (TileLeaf) · registry.setup (binaryName/label/required/detail) · build plumbing (vite aliases + tsconfig paths + headlessExclude).
Clean mappings
OpencodeHeadless(spawn mode via SpawnedServer) →OpencodeSession implements AgentSession;ready→started(projectDir undefined — optional by contract),exit→exit(null→-1), ScreenChannelactivity→process-state, CommittedChannelentry→jsonl-entry(syntheticopencode://session/<id>file arg; resume replay arrives as a live burst the bulk ingest already handles),history_error/sse-error→jsonl-error, SemanticChannelevent→semantic-event(vocabulary already matches the app's fold).- deliverPrompt via HTTP
prompt()— strictly better than both PTY protocols (no readiness gate, no paste confirm). - Conditions: SSE
permission/questionevents → the OPEN snapshot union (opencode.permission,opencode.question) with custom actions only;resolveCondition→PermissionService.reply/replyQuestion— exactly what the phase-2a contract comment predicted. - Identity/commands: pure data; spawn/split palette entries derive automatically (phase 4).
Degraded-by-design (no PTY/screen)
pty-data/agent-pty-data never emitted (AgentTerminalLeaf needs a capability gate — else permanently blank xterm) · screen never emitted (all consumers degrade free; paste-baseline is Claude-only) · write/resize no-ops · conditionPolicy.actionKinds MUST be empty (keystroke routing would silently eat keys) · composerSubmit must NOT use io.send — needs a new session:deliver-prompt IPC onto the existing manager.deliverPromptToAgent.
Hard blockers → smallest viable resolutions
- No offline session listing (SyncClient needs a running server; SQLite storage, no walkable files; the package's planned
transcript/SessionList.tswas never implemented) → v1:listSessions: async () => []; real fix: ephemeralopencode servelister or app-side JSONL mirror. - No durable transcript file →
resolveTranscriptPath: async () => null(all consumers typestring|null); initial history comes from the start-time committed replay, NOT the file loader. Real fix later: app-owned JSONL mirror written from committed events (also unlocks history paging + switch sources). - Composer input path — the new IPC is mandatory for a usable pane (small: handler + preload method + 10-line composerSubmit).
message.updatedre-emission vs feed dedupe — assistant messages update as parts complete; naive uuid-dedupe freezes rows at first partial. v1: forward onlytime.completedmessages (semantic streaming card covers the live view — same division of labor as Claude/Codex).- First
required: falsesetup provider — verify SetupGate soft-handles a missing optional binary.
Wiring order
- Build plumbing + add
'opencode'to AGENT_PROVIDER_KINDS (compiler enumerates the checklist) - Minimal registry stubs (compiles; opencode appears in spawn UI)
OpencodeSessionruntime (event translation; semantic streaming renders even pre-mapper)- Transcript mapper + extractProviderSessionId (feed rows + durable identity)
- deliverPrompt + composer IPC (interactive; MCP orchestration free)
- Conditions (permission/question views + resolveCondition — last must-have; without it the agent silently stalls on first permission)
- Follow-ups: ephemeral lister, JSONL mirror, AgentTerminalLeaf gate, dangerousMode/MCP config injection, atp codec for switching
Merge policy: steps 1–6 land as one branch so what ships is a usable pane, not a spawnable-but-mute one.
Package-side gaps to fix in opencode-headless: missing transcript/SessionList.ts/ProjectKey.ts; ScreenChannel.publishSnapshot is dead code.
Part of #394.
Activity
Phase 7 step 3: real OpencodeSession runtime (#406)
Phase 7 step 4: real opencode transcript mapper (#406)
Phase 7 step 5: opencode prompt delivery over HTTP (#406)
Phase 7 step 6: opencode conditions + HTTP permission resolve (#406)
Merge pull request #408 from Juliusolsson05/feat/providers-phase7-ope…
Juliusolsson05 commentedon Jul 6, 2026
Closing — the useful parts of this tracker landed across #408/#411/#412/#413/#415/#417/#419 plus opencode-headless#1/#2 (spawn, commands, full rendering pipeline, session ownership, tool/todo rows — verified against a live server session). The honest remainder is re-scoped elsewhere: resume UX + switching for OpenCode is blocked on agent-transcript-parser#5 (neutral-hub codecs) and will be tracked with that campaign; subagent rendering is #178's scope; ingestion residue folds into the rendering-engine rewrite. This issue's phase-numbered naming outlived its usefulness.
feat(providers): gate features by declared capability, not agent-hood