Skip to content

[OpenCode V2][2/5] Add opt-in V2 transport, session lifecycle and location mapping #3

Closed

Description

@tbrandenburg

Parent epic: #1 · Planned contribution: upstream PR 2/5 · Depends on #2 · Upstream discussion: Intelligent-Internet#550

Goal

Implement a working, explicitly selected OpenCode V2 HTTP/backend connection and session slice, while leaving V1 as the default and refusing all unfinished V2 A2A execution/extension behavior. Deliver a small upstream PR reviewers can test independently, without pretending that chat is implemented yet.

Background

The current src/opencode_a2a/opencode_upstream_client.py uses V1 paths (/session, /event, /session/{id}/message), V1 directory/workspace query semantics, and expects JSON payloads for many operations. V2 routes are under /api/* and often wrap success results in { "data": ... }, while some mutations return HTTP 204. V2 location references are not a simple rename of every V1 query parameter.

Sources:

KISS

  • Add one V2 adapter implementing only server identity, create/get/list/continue-session primitives needed by next PR; keep common HTTP/auth/concurrency handling in existing code.
  • Add OPENCODE_API_VERSION=v1|v2 with v1 default; explicit version selection, no auto-detection or fallback.
  • Do not implement V2 A2A prompting, streaming, forms, command/shell, workspace/worktree extras or broad session extension parity in this PR.
  • Do not change A2A 1.0 protocol version, application ports, or inbound auth configuration.
  • Until PR 3, selecting V2 for A2A prompt execution must fail fast with a clear unsupported/incomplete-backend error, not accept and hang or return a fake completed task.
  • Advertise only the V2 capabilities actually usable; hide/reject all unimplemented OpenCode-specific methods now, not only in PR 5.

Detailed implementation plan (junior-friendly)

  1. Read [OpenCode V2][1/5] Extract typed upstream adapter boundary without changing V1 behavior #2's resulting contract and current source: src/opencode_a2a/config.py, opencode_upstream_client.py, execution/session_manager.py, server/application.py, server/agent_card.py, contracts/extensions/capabilities.py. Check current upstream OpenCode API reference for the exact pinned V2 binary; save representative /api/info, /api/session request/response fixtures.
  2. Add a validated enum/literal setting OPENCODE_API_VERSION defaulting to v1. Update config tests to cover omitted setting, explicit v1, explicit v2 and invalid values. Avoid "auto" as an accepted value for now.
  3. Add a small factory or selector that injects V1UpstreamAdapter or V2UpstreamAdapter into the existing executor/session manager. Preserve old constructor compatibility where feasible, and preserve startup/shutdown close() ownership.
  4. Implement V2 requests for GET /api/info, GET /api/location, POST /api/session, GET /api/session/{sessionID}, and the minimum list/lookup needed for continuity. Distinguish optional location query objects, JSON body and absolute directory paths using the published schema, not V1's ?directory= approach. Resolve workspace root paths server-side only; reject unauthorized or outside-root directory references before sending.
  5. Reuse httpx connection pool, Basic auth, trace headers, request/stream budgets, timeout and redaction. Accept both non-default upstream port and externally supplied OPENCODE_BASE_URL. Do not implement process management or dynamic port discovery.
  6. Make response decoding protocol-aware: check status/media type, parse documented V2 data envelopes, accept exact 204 success for no-body actions, reject HTML SPA fallback or mismatched V1/V2 server versions with actionable diagnostics. Preserve typed errors without logging tokens or request bodies.
  7. Add strict initial capability gates: V2 A2A execution and unmapped opencode.* controls are unavailable. Ensure Agent Card/extension discovery does not suggest they work; direct calls must receive a stable unsupported-operation error rather than hitting a V1 route. Avoid disabling standard A2A discovery entirely if a truthful limited card can be served; document readiness/limitations clearly.
  8. Add V2 unit fixture tests and real HTTP-boundary tests for status codes, query/body construction, response envelope shape, auth, 401/404/409, protocol mismatch, bad location, non-JSON responses and partial-state handling. Re-run V1 tests unmodified.
  9. Record implemented routes, explicitly disabled features and PR 3 dependencies in docs/compatibility.md, labelled experimental V2 session transport only, not "OpenCode V2 supported".

Pitfalls to avoid

  • Prefixing every V1 endpoint with /api without checking its V2 contract.
  • Assuming every successful V2 route returns JSON (some return 204).
  • Reusing V1 directory params for V2 location.
  • Assuming V2 default external-directory permissions sandbox work; V2 permits external directories by default, so retain/enforce gateway workspace policy and specify safe upstream permission policy.
  • Starting /api/event only after a prompt is already sent and assuming no events can be lost.
  • Returning an "accepted" task as "completed" when prompt execution remains unimplemented.
  • Bypassing A2A session ownership in direct session continuation; V2 IDs remain untrusted input.

Final manual E2E acceptance test (PR 2's transport/session vertical slice)

Prerequisites: pinned real OpenCode V1 and V2 binaries, isolated temp HOME/XDG/workspace per version, valid upstream Basic auth, and throwaway ports. This PR is not expected to pass a V2 prompt E2E until PR 3.

  1. With OPENCODE_API_VERSION=v1 (and separately with env var omitted), start the existing A2A service and run the existing real V1 bash ./scripts/live_opencode_smoke.sh without skipping prompt. Confirm unchanged behavior.
  2. Start the pinned V2 opencode serve on a local non-default port with Basic authentication. Verify GET /api/info returns V2 identity. Instantiate the new V2 adapter via the same application selection/factory and create, fetch and list a session within the configured workspace. Assert stable session ID and correct location.
  3. Try an invalid upstream password, unavailable server, and a deliberate v1/v2 config mismatch; verify early, understandable failure, not an HTML parse traceback.
  4. Try a path outside OPENCODE_WORKSPACE_ROOT (including normalized traversal or symlink escape) and, where relevant, an unowned existing session; verify rejection before an unauthorized operation.
  5. Send an A2A prompt to V2 mode; verify the clear not-yet-supported response and no false TASK_STATE_COMPLETED status. Inspect discovery and confirm no unfinished V2 extensions are advertised.
  6. Capture sanitized HTTP status/route evidence, session ID, exact V2 release, and pass/fail output in the PR. Do not call this full V2 chat E2E.

Acceptance criteria

  • Explicit, validated API selector; unchanged V1 default.
  • V2 authenticated /api/info, location and session create/get/list work against a pinned real V2 service.
  • Shared HTTP auth, timeouts, error handling, redaction and concurrency guards are reused.
  • Location/workspace restrictions and session ownership are not weakened.
  • Non-JSON/mismatch failures and 204 handling are correct.
  • Unsupported V2 execution/extensions are gated in dispatch and capability discovery, with informative errors.
  • V1 unit, HTTP-boundary and real smoke tests still pass.
  • PR 2 live V2 session vertical slice passes; limitations clearly documented.

Definition of Done

Next: #4 (V2 execution), which must consume the admitted prompt and correlate it to a true terminal result.

Activity

  1. tbrandenburg commented on Oct 25, 2026

    @tbrandenburg
    OwnerAuthor

    Session slice 更新

    上游 session PR 仍是明确 stacked Draft,依赖 typed boundary:Intelligent-Internet#552 。Exact head cc8470d doctor:890 passed,92.88% coverage;默认 V2 discovery/dispatch gating 修复已追加,未重写历史。

    单独 OPENCODE_API_VERSION=v2 仍不启用 A2A execution。额外的 exclusive profile 使用固定 workspace;默认 session-only location mapping 保持不变。最终 acceptance stack 另提供 hardened、isolated 2.0.28 session smoke。未关闭本 issue;完整范围和上游顺序见 #1 / #7。

  2. tbrandenburg commented on Oct 25, 2026

    @tbrandenburg
    OwnerAuthor

    Fork session-transport scope 完成:明确 reconciliation

    #7 已 normal merge 到 fork main 19943955704bf05347619ca5b98addb94a683c97;GitHub-only experimental fork prerelease 已发布。

    本 session slice 的显式 selector、V2 info/location/session create/get/list、Basic auth 与默认 fail-closed gates 已交付。Main 重新执行真实 OpenCode 2.0.28 bash ./scripts/live_opencode_v2_session_smoke.sh:Basic-auth rejection 和 routes 通过,未提交 model prompt。V1 默认不变;只选 OPENCODE_API_VERSION=v2 不启用 execution;还需 OPENCODE_V2_EXECUTION=exclusive 与完整 operator boundary。36 个 default/opt-in focused tests 通过;candidate/main doctor 各 1021 passed、conformance 各 8/8。Recorded pinned V1/V2 real-model evidence 与 main 生产 source digest 精确匹配:evidence。

    按本 issue 允许的 “upstream merge 或明确 reconciliation” closure 条件,本次授权的 fork merge 记录为 fork scope reconciliation,关闭 session-transport implementation issue。不是 upstream 已合并:Intelligent-Internet#552 仍 open,分支未改写/删除。后续 unrestricted execution 不在此 closure 范围,由 #4/#9 与 epic #1 继续跟踪。

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