Skip to content

feat(dispatcher): add opencode CLI support - #89

Merged
zxkane merged 1 commit into
mainfrom
feat/dispatcher-opencode
May 10, 2026
Merged

feat(dispatcher): add opencode CLI support#89
zxkane merged 1 commit into
mainfrom
feat/dispatcher-opencode

Conversation

@zxkane

@zxkane zxkane commented May 10, 2026

Copy link
Copy Markdown
Owner

Summary

Add first-class opencode (anomalyco/opencode) support to the dispatcher's agent abstraction layer. opencode is a provider-agnostic open-source coding agent CLI that, like codex, mints its own session id per invocation. Mirrors the pattern established in #88: capture the CLI-minted id from the JSON stream into a sidecar, feed it back on resume.

Why

After #88 fixed the codex branch with a session-id-capture pattern, opencode's identical wrinkle (CLI mints the id; caller can't pre-mint) became cheap to address with the same approach. README #87 already lists opencode as the next CLI to integrate; this closes that follow-up.

Design

opencode JSON shape (verified against opencode run --format json --pure "test" on v1.14.46):

{"type":"step_start","timestamp":1778415469963,"sessionID":"ses_1ee2d8d...","part":...}

Every event carries "sessionID":"ses_<base62>" — not gated on a specific event type. The capture filter records the first occurrence (which arrives in the very first event), validates the format with ^ses_[A-Za-z0-9]+$, and writes it to ${pid_dir}/opencode-session-${dispatcher_session_id}.

  • run_agent opencode case: opencode run --format json [--title <session_name>] [PROMPT] piped through _opencode_capture_session.
  • resume_agent opencode case: reads the captured id via _opencode_session_id and calls opencode run --session <id> --format json [PROMPT]. Falls back to run_agent when the sidecar is missing or malformed (defense-in-depth: even though pid_dir is mode 0700, a malformed sidecar value would otherwise be passed to opencode run --session ... as raw shell input).

Test Plan

  • Live verification: installed opencode v1.14.46 locally, ran opencode run --format json --pure "test", confirmed JSON shape; ran resume with the captured id, confirmed it works.
  • 31 new cases in tests/unit/test-lib-agent-opencode.sh:
    • Source-of-truth grep (opencode case present, uses run --format json, resume uses --session)
    • run_agent argv shape + sessionID capture (stub emits the actual opencode JSON shape)
    • resume_agent uses captured sessionID
    • Fallback when sidecar missing (logs diagnostic, calls run_agent shape, creates fresh sidecar)
    • Crash path (opencode exits before any JSON event → no sidecar, exit code surfaced)
    • Malformed-sidecar regression (manually planted ses_with;injection\hazard→ regex rejects, fallback triggers, garbage never reachesopencode run --session`)
  • Codex tests still pass (27/27)
  • Full unit suite: 37/37 test files pass
  • bash -n clean on lib-agent.sh
  • shellcheck: only SC1091-info for lib-config.sh source
  • Code-reviewer agent run: zero findings ≥ 80 confidence
  • CI checks pass

Backwards Compatibility

  • AGENT_CMD=opencode was previously routed to the generic <cli> -p <prompt> fallback. With this PR it's routed to a first-class branch — the new behavior is strictly more correct (no -p flag misuse).
  • claude / codex / kiro branches unchanged.
  • Sidecar paths are new files under an already-existing per-user dir (mode 0700).

Open follow-ups (not in scope)

  • --dangerously-skip-permissions flag wiring when AGENT_PERMISSION_MODE=bypassPermissions. The codex branch has the same gap; would prefer a single PR that addresses both consistently rather than per-CLI ad-hoc fixes.

Checklist

  • Design described in commit message + this PR body
  • Test cases documented (31 cases with thorough behavioral + regression coverage)
  • Build/tests pass
  • Code review passed (zero blocking findings)

opencode (anomalyco/opencode) is a provider-agnostic open-source coding
agent CLI. Like codex, it mints its own session id per invocation and
does not accept a caller-provided id. Add first-class support by
mirroring the codex pattern from #88: capture the CLI-minted session id
from the JSON event stream into a sidecar under pid_dir_for_project()
keyed by the dispatcher's session_id, then feed it back to
`opencode run --session <id>` on resume.

JSON shape verified against opencode v1.14.46:
  {"type":"step_start","timestamp":...,"sessionID":"ses_<base62>",...}
The sessionID is on every event (not gated on a specific type) so the
capture filter records the first occurrence; a regex validator
(`^ses_[A-Za-z0-9]+$`) protects the downstream `--session <id>`
invocation against injection from a malformed sidecar.

- run_agent opencode branch: `opencode run --format json [--title
  <session_name>] [PROMPT]` piped through _opencode_capture_session.
- resume_agent opencode branch: reads the captured sessionID via
  _opencode_session_id and calls `opencode run --session <id> ...`.
  Falls back to a fresh run when the sidecar is missing or malformed
  (same defensive pattern as the codex/kiro branches).
- Tests: 31 cases in tests/unit/test-lib-agent-opencode.sh covering
  source-of-truth grep, run_agent argv shape + sessionID capture,
  resume using captured sid, fallback when sidecar missing, crash
  path, and a malformed-sidecar regression that verifies the regex
  validator triggers fallback rather than passing garbage to opencode.
- README: add opencode to the top blurb, AGENT_CMD allowed values, and
  the Supported Agent CLIs table (Full support row).
- autonomous.conf.example: add a one-paragraph comment naming the four
  first-class CLIs.

Full unit suite: 37/37 test files pass.
@zxkane zxkane added the pipeline-docs:none Attests this PR has no observable pipeline behavior change, exempting it from the docs-update gate label May 10, 2026

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR successfully adds first-class opencode CLI support to the dispatcher's agent abstraction layer. The implementation mirrors the proven codex pattern (CLI-minted session ID capture) and includes comprehensive testing.

Implementation Quality:

  • Follows established architecture patterns from the codex branch
  • Proper error handling for all failure modes (CLI crashes, missing sidecars, malformed data)
  • Security defenses in place: symlink checks (CWE-59), regex validation to prevent injection attacks
  • Comprehensive test suite with 31 test cases covering behavioral verification and edge cases

Verification:

  • All changes are consistent across documentation, configuration, implementation, and tests
  • The regex pattern ^ses_[A-Za-z0-9]+$ correctly validates opencode's session ID format
  • Fallback behavior matches the defensive pattern used in other CLI branches

No blocking issues found. The code is ready for merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

@zxkane
zxkane merged commit 3633219 into main May 10, 2026
5 checks passed
@zxkane
zxkane deleted the feat/dispatcher-opencode branch May 10, 2026 12:29
zxkane added a commit that referenced this pull request May 10, 2026
## Summary

Document the two prerequisites for running opencode under the
dispatcher: (1) `opencode providers login` to set up credentials, and
(2) explicit `AGENT_DEV_MODEL` / `AGENT_REVIEW_MODEL` in
`autonomous.conf` (opencode is provider-agnostic — no default model).
Also flag that `AGENT_PERMISSION_MODE=bypassPermissions` isn't yet wired
to opencode's `--dangerously-skip-permissions` flag (same gap as codex).

## Why

After #89 added first-class opencode support, a user reading the
README's "Full support" badge and setting `AGENT_CMD=opencode` would get
a silently stalled pipeline: opencode enters a session but produces no
output without credentials and an explicit model. Spelling out the
prereqs up-front prevents the foot-gun.

## What changed

- **README → Supported Agent CLIs**: opencode row now has a † footnote
covering auth + explicit model + the bypassPermissions gap.
- **README → variable table**: the `AGENT_CMD` row gets an inline
pointer to the footnote so users editing `autonomous.conf` see the
warning without scrolling.
- **`autonomous.conf.example`**: the `AGENT_CMD` comment and the
`AGENT_DEV_MODEL` / `AGENT_REVIEW_MODEL` comments now explicitly call
out that empty model values are valid for claude/codex but invalid for
opencode.

## Test Plan

- [x] No code changes; `bash -n` clean on `autonomous.conf.example`
- [ ] CI checks pass

## Checklist

- [x] Docs only
- [x] Build/tests not affected
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pipeline-docs:none Attests this PR has no observable pipeline behavior change, exempting it from the docs-update gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant