Skip to content

[OpenCode V2][Epic] Deliver opt-in server API V2 support in five reviewable upstream PRs #1

Closed

Description

@tbrandenburg

Goal

Deliver genuinely usable, opt-in OpenCode V2 server API support in opencode-a2a while retaining the verified OpenCode V1 path and the existing A2A 1.0 public contract. Track five small, understandable, independently reviewed upstream PRs, not a gateway rewrite.

Upstream coordination: Intelligent-Internet#550
Source baseline when planned: 5d4700204445ce658361010cbda2fd344bc60dec (fork matched upstream).
Not to be confused with: upstream A2A SDK V2 lifecycle PR Intelligent-Internet#538.

Background

The current adapter calls OpenCode V1 endpoints such as POST /session, POST /session/{sessionID}/message and GET /event; docs/compatibility.md declares V1 1.18.x, verified on 1.18.19. OpenCode V2 uses /api/* with different request/response and execution semantics. V2 POST /api/session/{sessionID}/prompt returns an admitted inbox input, not a generated assistant result. V2 GET /api/event is volatile. Its default external-directory permission is broader than V1.

References: https://opencode.ai/v2/docs/migrate-v1#server-api-and-clients and https://opencode.ai/v2/docs/api .

KISS / architectural rules

  1. One A2A runtime, two thin OpenCode adapters. Keep A2A auth, task persistence, session ownership, workspace policy, output formatting, and common event rendering shared.
  2. Expose a minimal typed contract for session, admission, correlated execution updates, outcome, and interventions; no universal "all agent runtimes" framework.
  3. Separate accepted from completed. A task completes only on correlated terminal evidence, never solely on HTTP 200 or unrelated session.idle.
  4. V1 remains the default. Explicit V2 selection; no auto-detection or silent fallback in this epic.
  5. Honest capabilities throughout: any unimplemented V2 extension/model/tool behavior is hidden from discovery and rejected explicitly, starting with the first V2 PR.
  6. Each PR must work independently against upstream main, or be clearly marked as a draft with documented dependency; never ask maintainers to review a giant stacked diff as one PR.
  7. Preserve existing security: auth, identity-to-session ownership, location/workspace restrictions, concurrency budgets, cancellation semantics, and log redaction.

Planned upstream PR sequence (one fork issue each)

Each child issue must contain a junior-friendly implementation checklist, pitfalls, unit/HTTP tests, final manual E2E acceptance instructions, acceptance criteria, and Definition of Done. Cross-link its upstream PR and this epic. Update source paths and API details if upstream changes before implementation.

Execution plan refinement — applies to issues #4–#6 (October 25, 2026)

Work on #2 and #3 is left unchanged; this is a forward-looking refinement for implementation issues #4, #5 and #6, not a request to redo V1 extraction or V2 session transport. Issue #3 is reported as the user's current stage, not marked complete or merged (verify its actual PR/branch state before starting dependent work).

KISS decision: No custom message polling daemon, no new SDK/client stack, no forced experimental-log dependency, and no speculative generalized transport framework. Do not promise seamless replay when the only active V2 source is the volatile global event feed.

Reference contracts: https://opencode.ai/v2/docs/api (session.prompt, event.subscribe, experimental session.log, session.inbox, session.interrupt, session.permission.reply, session.form.reply). Final implementation must verify these against its pinned OpenCode server build.

Epic-level pitfall gates (must not regress)

  • Prompt correlation: admitted input ID vs assistant output ID vs A2A task ID. Concurrent or queued prompts and unrelated idle events must not complete another task.
  • Event loss: volatile V2 events do not imply replay; reconnect must reconcile against authoritative session state or fail as uncertain, never fabricate success or blindly resubmit. Experimental /api/experimental/session/{sessionID}/log may be evaluated, but cannot be assumed supported without version-pinned evidence.
  • Security: OpenCode V2 defaults external_directory to allow; test explicit upstream policy and gateway path restrictions. Reject ownership mismatches and invalid location changes before privileged calls.
  • Response typing: V2 has data-wrapped JSON results and 204 No Content mutations; never assume every successful call yields a JSON object or V1-shaped payload.
  • Model selection: V2 model switch is session-scoped, whereas this adapter advertises request-scoped model selection; maintain semantic isolation or reject the override.
  • Tool bridge: existing a2a_call/tool-result reinjection assumes V1-style parts. Do not claim it works in V2 until proven end-to-end; gate and document unsupported behavior.
  • Compatibility: SDK "V2" is not OpenCode server V2. Keep V1 live test and V2 pinned test isolated (different binaries/config HOME/XDG/workspaces), with no claims based only on mocked responses.
  • Staging: after PR 2/3/4, partially supported V2 must be clearly experimental/limited; advertised features match implemented and tested features.

Detailed implementation / tracking steps (for a junior contributor)

  1. Read AGENTS.md, CONTRIBUTING.md, docs/maintainer-architecture.md, docs/security-architecture.md, docs/compatibility.md and scripts/README.md.
  2. Before each PR, confirm current upstream head/maintainer feedback on [Question] OpenCode V2 server API 兼容性与支持计划 Intelligent-Internet/opencode-a2a#550; branch from the latest upstream main (not from a chain of unmerged feature branches unless visibly marked stacked).
  3. Implement only the child issue scope. Reuse opencode_upstream_client.py, execution/*, server/*, contracts/extensions/* and existing tests; don't reorganize unrelated modules.
  4. For each PR run bash ./scripts/doctor.sh, relevant targeted tests, and the issue's manual E2E scenario. Record exact OpenCode and Python/adapter versions and command output.
  5. Open a narrowly scoped upstream PR describing why, code-path changes, what is intentionally unsupported, security impact, tests, and dependencies. Link child issue and upstream [Question] OpenCode V2 server API 兼容性与支持计划 Intelligent-Internet/opencode-a2a#550.
  6. Mark an epic checkbox only after the PR is reviewed/merged upstream (or record clearly if fork-only). Do not close this epic before the acceptance matrix below passes.

Final manual E2E acceptance (epic release gate)

Use real pinned V1 and V2 opencode serve installations, isolated temporary HOME/XDG state, isolated workspace dirs and ports, a configured real model, local-only upstream listeners, upstream Basic auth and separate inbound A2A credentials. Record exact versions and results:

  1. Run the existing V1 bash ./scripts/live_opencode_smoke.sh and verify streaming completes with the expected assistant text; repeat with a non-streaming A2A send.
  2. Switch the adapter to V2 explicitly, verify GET /api/info, create a new session, stream a prompt to a terminal A2A task, and continue the same session/context with a follow-up.
  3. Repeat the central V2 request over the other supported A2A transport (REST and JSON-RPC). Verify no unsupported feature is advertised in Agent Card or extension discovery.
  4. Execute a task that emits reasoning and a tool update; verify correct block metadata, final artifact snapshot and persisted terminal state.
  5. Create two independent A2A tasks and queued/overlapping prompts; verify no cross-task output or premature completion. Trigger failure, cancellation and an upstream event disconnect; never get false success.
  6. Trigger one human-approved permission request and one form; verify input-required, authorized reply, continued execution, rejection and cross-principal denial.
  7. Try unauthorized inbound A2A calls, unauthorized upstream HTTP, cross-owner session reuse, prohibited workspace/outside-root path, and an unsupported V2 extension; all must fail safely and predictably.
  8. Confirm V2 request-scoped model handling is semantically verified or explicitly unavailable. Confirm a2a_call/tool-result round trip is either proven working or explicitly not advertised.
  9. Run deterministic suites and bash ./scripts/conformance.sh as well as pinned live checks. Publish the pass/fail table and known limitations in docs/compatibility.md.

If real model credentials/manual interaction are unavailable, mark the relevant gate not verified and keep the epic open: do not substitute mocks for a passed live acceptance test.

Epic acceptance criteria

  • Each child PR has independent reviewable change scope and linked test evidence.
  • Unchanged V1 default path passes all existing regression + live smoke tests.
  • Opt-in V2 can create/continue sessions and complete real streamed + unary A2A tasks.
  • V2 request correlation, terminal/failure/cancellation, and intervention workflows are tested.
  • No V2 operation violates existing ownership/workspace/security boundaries.
  • Capability advertisements and error behavior match actual support on both versions.
  • Pinned compatibility matrix, manual E2E evidence, and upstream maintenance policy/decision are documented.

Definition of Done

  • All five implementation issues resolved with upstream PR links and merged/reconciled status.
  • CI/typecheck/linters/contract tests and the final manual V1/V2 E2E acceptance pass.
  • No known P0/P1 semantic or security regressions; unresolved optional features explicitly gated and documented.
  • Upstream maintainer decisions from [Question] OpenCode V2 server API 兼容性与支持计划 Intelligent-Internet/opencode-a2a#550 recorded; docs distinguish OpenCode API V2 from A2A SDK V2.
  • Epic is closed only with a linked release/merge summary and tested upstream version numbers.

Explicitly out of scope

Automatic API-version detection, speculative multi-runtime abstraction, a general cleanup, V1 deprecation, and full parity for every OpenCode-private session/workspace extension. These may be separate non-blocking follow-ups.

Activity

  1. tbrandenburg commented on Oct 25, 2026

    @tbrandenburg
    OwnerAuthor

    受限 V2 profile:实现与 scoped live 验收结果

    本轮用户明确授权 private / gateway-exclusive / single-flight 首批范围。该 profile 已通过真实模型 API 验收,不等同于非独占 shared-session 支持,也不会静默关闭原 epic 的广义验收。未解决的通用关联/恢复/取消单独跟踪 #9。

    • V1 默认不变;V2 单独选择仍 fail closed,只有额外的 OPENCODE_V2_EXECUTION=exclusive 才启用受限路径。
    • 专用 private namespace、一个 gateway/upstream pair、separate credentials、固定 workspace、durable owner、受保护配置、无外部或自主 writers 是硬部署前提;应用不自动认证网络拓扑。
    • 当前 committed author gate:1021 passed,0 failed;92.65% coverage,typing/lint/format/secret scan、critical coverage、build/wheel smoke 通过;conformance 8/8。
    • 实际 V2 2.0.28 / openai/gpt-6-luna-fast:REST/JSON-RPC stream/unary、follow-up、parallel/serialized sessions、local queued/active cancellation、read allow/reject、string/multiselect、invalid/stale/cross-owner callbacks、session hijack/location denials,positive matrix 14 admissions / 1 SSE。
    • 六个真实 upstream negative runs(gap、lost terminal/admission、provider reject、pending-form gap、expiry):明确 non-success/拒绝,每次一个 admission,没有 blind resend。
    • V1 1.18.19:expanded live matrix 12/12;只检查 assistant artifacts;真实 permission 文件 side effect/拒绝无文件;6/6 cancellation 后额外 46 秒无 canceled→failed/completed 覆盖。
    • 保留限制:unrestricted shared writers、transparent replay/restart continuation、unary interventions、rich forms、attachments、request-scoped models、arbitrary tools/a2a_call 不支持。Live reasoning NOT VERIFIED。

    代码与 sanitized evidence:

    上游顺序:typed boundary → session slice → bounded native engine → exclusive activation/interventions → acceptance harness。跨仓库链接:

    每个 stacked PR 明确说明依赖,不伪装为独立 diff。未 force-push、自动 merge 或关闭 tracking issue。请 maintainer 明确接受 restricted-first 范围,并按依赖顺序审查;upstream 尚未发布该支持。各 PR 的 exact-head QA 记录与 author live/source-identity 证据分开陈述。

  2. tbrandenburg commented on Oct 25, 2026

    @tbrandenburg
    OwnerAuthor

    Fork 已合并并发布 prerelease;upstream / 广义范围保持待完成

    #7 已 normal merge 到本 fork main:19943955704bf05347619ca5b98addb94a683c97。GitHub-only prerelease:1.4.1rc1+fork.v2exclusive,标题为 OpenCode V2 2.0.28 — experimental private exclusive execution;未发布 PyPI。

    Candidate da51304 与 merge tree 相同。两次 exact-head doctor 均 1021 passed / 0 failed(candidate 92.65%、main 92.67% coverage),两次 conformance 均 8/8;main real 2.0.28 session/auth smoke 通过,36 个 default/opt-in focused tests 通过,tagged wheel/sdist smoke 与下载 checksum 通过,runtime/dev/build audit 未发现已知漏洞。GitHub 无已上报 CI checks;不冒称 hosted CI 通过。

    Recorded V1 1.18.19 / V2 2.0.28 real-model evidence 的生产 Python digest 与 released main 精确一致:ecd2b57cfab3506704aeabacb8acdc58d4ddc9e6f4bd578be691536e8cf3bef8。本轮复核历史 live evidence,没有重跑完整 model matrix。证据与边界:acceptance、deployment contract。V1 默认;V2 execution 仍 double opt-in,private network/credentials/fixed workspace/fresh state/单 gateway pair 等要求不变。

    Issue disposition

    Upstream stack ancestry:不做 cosmetic history rewrite

    五个 upstream PR 仍 open,分支未修改/删除:
    Intelligent-Internet#551 → Intelligent-Internet#552 → Intelligent-Internet#553 → Intelligent-Internet#554 → Intelligent-Internet#555 。

    实际检查:Intelligent-Internet#552 不包含 Intelligent-Internet#551 最新 commit 的 ancestry,但 65c15e9 与其 73ffcd8 是 patch-equivalent outcome-safety fix;Intelligent-Internet#555 不包含 Intelligent-Internet#554 最新四个 commits 的 ancestry,但包含对应 follow-up fixes,Intelligent-Internet#554/Intelligent-Internet#555 的生产 src/ 无 diff。两处都不是 fast-forward;为了没有 source 收益的 graph 整理追加重复 merge 并不必要,因此本次安全保留原 stack。

    后续如 maintainer 要求 ancestry reconciliation:按 Intelligent-Internet#551→Intelligent-Internet#552→Intelligent-Internet#553→Intelligent-Internet#554→Intelligent-Internet#555 顺序,在各 child 的临时 reconciliation branch 上 non-destructive merge 最新 parent;先检查冲突/最终 source diff,再对每个新 head 运行 doctor 和对应 smoke,然后按 maintainer 决定正常推送或开 follow-up PR。若 upstream squash/改变 base,先从其最终 accepted commit 建新分支,仅移植尚未包含的 diff,再按相同顺序验证;不要 force-push 当前 published PR branches,也不要凭 patch-ID 自动丢弃非等价 fix。本次未重建 stack、未合并 upstream PR。

  3. tbrandenburg commented on Oct 25, 2026

    @tbrandenburg
    OwnerAuthor

    已合并 fork,PR 与 main hosted CI green

    Fork PR #11 已 normal merge,main commit 37a49bbd3d81adb777b85d9616b883009d8719a4。Merge tree 与 tested candidate f224037 相同。

    现在 V2 是 API default;V1 需要 OPENCODE_API_VERSION=v1。V2 execution 仍需 OPENCODE_V2_EXECUTION=exclusive,private deployment 前提和 unsupported scope 不变。没有自动 fallback,没有改写旧 prerelease/tag 或发布 PyPI。

    Upstream follow-up Intelligent-Internet#556 保持 stacked Draft,关联 Intelligent-Internet#550 ,依赖既有前置贡献。Exact upstream head fab667a 独立 doctor 1026 passed、conformance 8/8,但 hosted CI 仍为 action_required / 需要 maintainer approval,不是 green 或 upstream merge。五个既有 contribution heads 保持原样。

    Fork tracking #10 已完成关闭;原广义 epic 和未解决的 V1 attribution / V2 unrestricted-session scope 不因此关闭。Live reasoning 仍 NOT VERIFIED。

  4. tbrandenburg commented on Oct 25, 2026

    @tbrandenburg
    OwnerAuthor

    Fork 交付结案;停止扩展本 epic

    #11 已合并到 37a49bbd3d81adb777b85d9616b883009d8719a4,新 1.4.1rc2+fork.v2exclusive prerelease 已发布。Main hosted CI 五个 jobs SUCCESS;doctor 1026 passed,conformance 8/8。旧 tag/release 保留,未发布 PyPI。

    Breaking configuration change:V2 默认选中,但 execution 仍 disabled;只有明确 OPENCODE_V2_EXECUTION=exclusive 且满足 private deployment 前提才启用。V1 需 OPENCODE_API_VERSION=v1。

    #4–#6 的 execution、interventions、acceptance 已在 restricted fork profile 内交付;其更广义目标仍 deferred,不能据此宣称原广义 DoD 完成或 upstream 已合并。

    按用户最终 disposition,归档 #1 与 #4–#6(not planned 指不再在此继续扩大原广义 scope,不是否定已发布的 restricted 实现)。本 epic 不再追加工程任务。仅保留 #8(V1 external-writer attribution) 和 #9(V2 unrestricted sessions) 作为未来工程调查;upstream contribution PRs 保持供维护者后续审查。

  5. tbrandenburg commented on Oct 25, 2026

    @tbrandenburg
    OwnerAuthor

    已交付:restricted fork scope

    受支持的 private-exclusive V2 实现已通过 #7、#11 交付到 fork main,并发布 1.4.1rc2+fork.v2exclusive;具备 pinned V1/V2 acceptance evidence 和 green hosted CI。

    按用户最终确认,本 issue 以 completed:在 fork 明确受限范围内已交付 关闭,取代此前 not planned 的归档原因。原始更广义验收要求并未全部实现:unrestricted shared-session execution 仍由 #9 跟踪(deferred);其他 unsupported capabilities 继续在兼容性文档明确列出,目前不作实施承诺。Upstream contribution PRs 独立保持 open,fork 交付不等于 upstream acceptance。

    #8(V1 attribution risk)与 #9 保持 open;本 issue 不再承担后续工程任务。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions