Skip to content

Complete OpenCode command integration — phase 4b sweep of remaining two-provider guards - #413

Merged
Juliusolsson05 merged 1 commit into
mainfrom
fix/providers-phase-4b-command-guards
Jul 6, 2026
Merged

Juliusolsson05 merged 1 commit into
mainfrom
fix/providers-phase-4b-command-guards

Conversation

@Juliusolsson05

Copy link
Copy Markdown
Owner

Follow-up to #408 (Phase 7: wire OpenCode as the third plug-and-play provider) and the earlier #395/#397/…/#404/#405/#409 phases. Closes the OpenCode spawn silent-fail and the 8 command surfaces around it.

Why

Registering OpenCode as an AgentProviderKind (#395) and wiring it through the registry (#408, #411, #412) was necessary but not sufficient: about 15 hand-written if (kind !== 'claude' && kind !== 'codex') return guards survived across the renderer command / modal surface, and two more in providerSwitch/ silently mistreated OpenCode as Codex. Symptom the user hit: clicking OpenCode in the New Agent overlay in Dispatch mode did nothing — no spawn, no toast, no log entry.

What changed (11 files, +164/-30)

Primary bug (silent OpenCode spawn)

  • NewAgentPlacementOverlay.tsx:118, 139 — commitKind()'s Linked and Dispatch branches both dropped kind === 'opencode' silently. Replaced with isAgentProviderKind() so any registered provider spawns cleanly. Stale "Claude or Codex" prose in the surrounding comment updated.

Registry-driven guard sweep (14 sites)

Every remaining kind !== 'claude' && kind !== 'codex' in the renderer replaced with isAgentProviderKind(kind):

  • sessionCommands.ts:294, 340, 386, 432, 551, 587 — six commands (four enable-*-mcp toggles, Copy Resume Command, Duplicate Agent). Every one had when: isAgentProviderKind(kind) allowing OpenCode through, followed by a stale run: re-narrow that filtered it back out.
  • orchestrationMcp.ts:157 — matchingOrchestrationSessionIds was excluding OpenCode orchestration children from the parent's visible list; list_agents / close_run returned the wrong scope.
  • hook/actions/reader.ts:40 — Reader Mode refused OpenCode panes even though every registered provider ships a transcript view.
  • hook/actions/session.ts:971 — softReloadAgentView refused OpenCode panes. It's provider-neutral (renderer-only), so the guard was dead weight.
  • CloseOldAgentsModal.tsx:138 — bulk-close preview silently omitted OpenCode agents.
  • loadWorktreeDump.ts:119 — per-worktree activity view hid OpenCode sessions from their worktree.
  • prompt-templates/templates.ts:89 — templates that reference "the other agents in this tab" silently narrowed the LLM's context window by dropping OpenCode.

All downstream fns (buildProviderResumeCommand, duplicateSession, replaceSession) already accept AgentProviderKind — no downstream change needed.

Data-integrity fixes (silent branch-fallthrough → fail loudly)

  • providerSwitch/duplicateSession.ts:66 — if (provider === 'claude') … else duplicateCodex(...) treated OpenCode as Codex silently, reading its server session id as a Codex rollout uuid and writing malformed output. Replaced with explicit dispatch that throws for unimplemented providers; the Duplicate Agent command's catch now surfaces the message as a pane toast.
  • providerSwitch/rewindSession.ts:83 — same silent fallthrough. Same fail-loud dispatch.

Stale toast copy

  • hook/actions/provider.ts:110, 177 — "Only Claude and Codex panes can reload/support rewind" → "Only agent panes can reload/support rewind" so the messages stay correct for any current or future provider set.

Explicitly out of scope (deferred to follow-up PRs)

  • BulkProviderSwitchModal N-way rewrite and switchProvider engine dispatch — blocked on agent-transcript-parser#5 (opencode codec). switchProvider already fails loudly for unsupported pairs (see the throw at line 75), and the modal only offers translatable pairs, so both are honest today.
  • useIpcSubscriptions ingestion shape-sniff and sessionIndex opencode discovery — OpenCode's on-disk model is server-based, not JSONL. Needs its own architectural slice.
  • Command descriptions/keywords that still say "Claude and Codex agents" — cosmetic; the runtime is correct.

Verification

npx tsc --noEmit -p tsconfig.node.json — 0 errors
npx tsc --noEmit -p tsconfig.web.json — 0 errors (with cleared .tsc-out/)

Zero remaining kind !== 'claude' && kind !== 'codex' in the tree.

Per repo convention, no new tests added.

Test plan

  • Kill and relaunch npm run dev
  • New Agent (⌘N) → picker shows Claude / Codex / OpenCode
  • Click OpenCode in Grid mode → advances to placement step ✓
  • Enter Dispatch mode, click OpenCode → spawns (this was the primary bug)
  • Enter Tiled Dispatch, focus a lane, click OpenCode → spawns in that lane's project
  • Enter Reader Mode on an OpenCode pane → opens
  • Cmd Palette on an OpenCode pane → the four enable-*-mcp toggles show + work; Copy Resume Command copies opencode --session <id>; Soft Reload works
  • Duplicate Agent on OpenCode pane → shows a clear "no duplicate implementation for provider 'opencode' yet" toast instead of doing nothing
  • Switch Provider on OpenCode pane → shows the existing "OpenCode panes can't switch provider yet" toast (unchanged from #411)

🤖 Generated with Claude Code

Phase 7 (#408) registered OpenCode as the third agent provider, but a
sweep across the command + modal surface left ~15 hardcoded
"kind !== 'claude' && kind !== 'codex'" guards behind. The visible
consequence: clicking "OpenCode" in the New Agent overlay silently
did nothing in Dispatch mode, and every OpenCode pane went through
9 command surfaces that behaved as if the pane were terminal-kind.

Primary bug (silent OpenCode spawn):

* NewAgentPlacementOverlay.tsx:118, 139 — commitKind()'s Linked and
  Dispatch branches both dropped `kind === 'opencode'` without a
  toast. Replaced with isAgentProviderKind() so any registered
  agent provider spawns cleanly. Stale "Claude or Codex" prose in
  the surrounding comment rewritten to reflect the registry.

Every remaining two-provider negative guard in the renderer replaced
with the same registry predicate:

* sessionCommands.ts:294, 340, 386, 432, 551, 587 — six commands
  (four enable-*-mcp toggles, Copy Resume Command, Duplicate Agent).
  Every one had `when: isAgentProviderKind(kind)` already allowing
  OpenCode through, followed by a stale `run:` re-narrow that
  filtered it back out. All downstream fns
  (buildProviderResumeCommand, duplicateSession, replaceSession)
  already accept AgentProviderKind — no downstream change needed.
* orchestrationMcp.ts:157 — matchingOrchestrationSessionIds was
  silently excluding OpenCode orchestration children from the
  parent's visible list; list_agents and close_run would return
  the wrong scope.
* hook/actions/reader.ts:40 — toggleReaderMode refused to enter
  Reader Mode on OpenCode panes even though every registered
  provider ships a transcript view under src/providers/<kind>/.
* hook/actions/session.ts:971 — softReloadAgentView refused to
  refresh OpenCode panes. It's provider-neutral (renderer-only), so
  the pair guard was pure dead weight.
* CloseOldAgentsModal.tsx:138 — bulk-close preview silently omitted
  OpenCode agents from the list and count.
* loadWorktreeDump.ts:119 — per-worktree activity view hid OpenCode
  sessions from their worktree even when they were on-disk.
* prompt-templates/templates.ts:89 — templates that reference "the
  other agents in this tab" silently narrowed the LLM's context
  window by dropping OpenCode.

Data-integrity fixes (silent branch-fallthrough → fail loudly):

* providerSwitch/duplicateSession.ts:66 — `if (provider === 'claude')
  … else duplicateCodex(...)` treated OpenCode as Codex silently,
  reading its server session id as a Codex rollout uuid and writing
  malformed output. Replaced with explicit dispatch that throws for
  unimplemented providers; the Duplicate Agent command's catch now
  surfaces the thrown message as a pane toast.
* providerSwitch/rewindSession.ts:83 — same silent fallthrough. Same
  fail-loud dispatch; the rewind path's UI-level anchor union
  already keeps OpenCode from reaching this on the happy path, but
  MCP / test callers now get a clear error instead of corruption.

Stale toast copy:

* hook/actions/provider.ts:110, 177 — "Only Claude and Codex panes
  can reload/support rewind" replaced with registry-neutral wording
  ("Only agent panes can reload/support rewind") so the messages
  stay correct for any current or future provider set.

Explicitly out of scope (each is a separate PR):

* BulkProviderSwitchModal N-way rewrite and switchProvider engine
  dispatch — blocked on agent-transcript-parser#5 (opencode codec).
  switchProvider already fails loudly for unsupported pairs, and
  the modal only offers the pairs the engine translates, so both
  are honest today.
* useIpcSubscriptions ingestion shape-sniff fallback and
  sessionIndex opencode discovery — OpenCode's on-disk model is
  server-based, not JSONL, so this needs its own architectural
  slice.
* Command descriptions/keywords that still say "Claude and Codex
  agents" — cosmetic; the runtime is correct.

Verification: `npx tsc --noEmit` clean on both tsconfig.node and
tsconfig.web with a cleared incremental cache. No new tests per
the repo's "don't add tests in feature/fix PRs" convention.

Fixes the OpenCode spawn silent-fail. Closes the last two-provider
literal in every renderer command surface.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant