feat(agent): add OpenCode adapter#498
Merged
sergeyklay merged 2 commits intomainfrom Apr 26, 2026
Merged
Conversation
Implements the fork-per-turn OpenCode adapter using `opencode run --format json`. - command.go: config parsing, argument/env construction, permission policy synthesis, and SSH remote command construction - parse.go: JSON envelope parsing, part parsers, and export-based token usage recovery via `opencode export --sanitize` - opencode.go: session lifecycle, subprocess management, event normalization, and stall watchdog integration - Full test suite: unit tests with fixtures, subprocess-backed Unix tests, and env-gated integration tests (SORTIE_OPENCODE_TEST=1) - Register adapter in binary and add registry metadata assertion - Add *_unix_test.go file nesting pattern to VS Code explorer settings Closes #476
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
Adds a new internal/agent/opencode implementation of domain.AgentAdapter so Sortie can orchestrate the OpenCode CLI (opencode run --format json) with per-turn subprocess lifecycles, stdout JSONL normalization, permission-policy env synthesis, SSH execution support, and export-based token usage recovery.
Changes:
- Register new agent kind
opencodein the agent registry and Sortie main binary. - Implement OpenCode adapter lifecycle + subprocess/event parsing + token usage export parsing.
- Add unit + (guarded) integration tests plus JSONL/JSON fixtures for parsing and lifecycle cases.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/registry/adapter_meta_test.go | Ensures opencode registration metadata is exercised in real-registration tests. |
| cmd/sortie/main.go | Adds blank import so the opencode adapter registers in the built binary. |
| internal/agent/opencode/opencode.go | Core adapter implementation: fork-per-turn process lifecycle, reader/wait coordination, event normalization, export usage emission. |
| internal/agent/opencode/command.go | Builds opencode run args, managed env, permission policy, and SSH remote command prefix. |
| internal/agent/opencode/parse.go | Parses JSONL run envelopes and opencode export output into normalized token usage. |
| internal/agent/opencode/command_test.go | Unit tests for args/env/policy construction and SSH command assembly. |
| internal/agent/opencode/parse_test.go | Unit tests for JSONL envelope parsing and export-output parsing. |
| internal/agent/opencode/parse_unix_test.go | Unix-only tests for export-usage subprocess invocation (local + SSH). |
| internal/agent/opencode/opencode_test.go | Unix-only tests for session lifecycle, cancellation, oversized lines, and event semantics. |
| internal/agent/opencode/integration_test.go | Guarded integration tests against a real OpenCode binary (SORTIE_OPENCODE_TEST=1). |
| internal/agent/opencode/testdata/simple_turn.jsonl | Fixture: basic step/text/finish event flow. |
| internal/agent/opencode/testdata/resume_turn.jsonl | Fixture: resume-style step flow. |
| internal/agent/opencode/testdata/tool_success.jsonl | Fixture: tool_use success envelope with large output. |
| internal/agent/opencode/testdata/permission_warning_then_error.txt | Fixture: plain-text permission warning + tool_use error JSON line. |
| internal/agent/opencode/testdata/malformed_event.jsonl | Fixture: unknown event type handling. |
| internal/agent/opencode/testdata/logical_failure_exit0.jsonl | Fixture: logical failure via error envelope despite exit code 0. |
| internal/agent/opencode/testdata/export_usage.json | Fixture: opencode export --sanitize usage payload with tokens/model/cost. |
| internal/agent/opencode/testdata/export_usage_missing_tokens.json | Fixture: export payload missing token info. |
| .vscode/settings.json | Updates file nesting to include _unix_test.go alongside other Go test files. |
Fix the OpenCode session handle and teardown behavior after PR review. - return the resumed session ID instead of leaking workspace paths via Session.ID - honor StopSession(ctx) with a bounded graceful shutdown path - correct the managed-environment warning text in export usage recovery - remove the dead errors import placeholder and add regression coverage for stop deadlines
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🎯 Scope & Context
Type: Feat
Intent: Adds a fork-per-turn OpenCode adapter so Sortie can orchestrate the OpenCode CLI agent (
opencode run --format json). The adapter handles subprocess lifecycle, JSON event normalization, permission policy synthesis, SSH remote execution, and export-based token accounting.Related Issues: #476
🧭 Reviewer Guide
Complexity: High
Entry Point
internal/agent/opencode/opencode.go— contains the adapter lifecycle (StartSession,RunTurn,StopSession), subprocess management, single stdout reader goroutine, stall watchdog integration, and event normalization. Understanding the read loop andwaitResultchannel is critical to reviewing concurrency safety.Sensitive Areas
internal/agent/opencode/opencode.go: subprocess fork-per-turn lifecycle, mutex discipline, and process-group teardown — review for races and deadlocksinternal/agent/opencode/command.go: permission policy synthesis and SSH remote command construction — workspace path containment and env scrubbinginternal/agent/opencode/parse.go:queryExportUsagespawns a subprocess to recover token counts; argument construction must match OpenCode CLI contractdomain.AgentAdapterinterface