Skip to content

Track real-host promotion gate for experimental OpenCode 2 adapter #27

Description

@ByBrawe

Why this stays experimental

OpenCode 2 support remains evidence-gated. Stable npm compatibility stays @opencode-ai/plugin >=1.4.0 <2; no stable OpenCode 2 claim is made until exact current-beta lifecycle behavior is proven end to end.

Current evidence — 2026-08-18

1. Project-plugin readiness race is handled

Current @opencode-ai/cli@next resolves to opencode2 v0.0.0-beta-17498 in the real-host gate. Diagnostic #87 proved the first correctly scoped project /api/plugin read can be empty before plugin initialization finishes; a fresh read on the same service exposes the registry and sentinel setup marker. Main has bounded same-service readiness without weakening wrong/rejected/global Location failures.

2. Exact-beta adapter activation is now green

The earlier setup crash TypeError: undefined is not an object (evaluating '_.name.replace') was isolated to the V2 tool registration signature. Beta-17498 exposes tools.add as a one-argument function and expects one complete Tool definition object. The previous adapter used the older three-argument form, so beta treated the string name as the Tool value and later failed while reading name.

PR #162 fixed only that contract mismatch: when the host exposes one-argument tools.add, the adapter registers { name, ...definition, codemode: false }; legacy/prototype test hosts retain the existing three-argument path. Exact-head Experimental OpenCode 2 Host passed on beta-17498, and #162 merged as ba5738b....

Activation is therefore no longer the promotion blocker.

3. Real /goal behavior is blocked by the current beta host surface

Test-only PR #163 exercised the unchanged post-#162 production adapter on exact beta-17498 with a real declared goal command and a deterministic local provider. It was intentionally closed without merge after collecting the runtime shape.

Observed behavior:

  • command.transform runs and can mutate the existing goal command, but the later built-in opencode.config.command transform overwrites that mutation with the declared command template before model dispatch.
  • session.hook("request", ...) registers successfully but does not fire on the real command/provider path.
  • session.hook("context", ...) does fire on each provider turn and exposes { sessionID, agent, model, system, messages, tools }.
  • tool.transform registration succeeds, but plugin-added Goals tools are not materialized into the effective request tool set. Diagnostic tools with codemode omitted, false, and true were all absent from both the context hook's tools map and the provider request.
  • The managed /goal bridge reaches the model path as ordinary user text. The corresponding context message has an empty metadata object.
  • ctx.event.subscribe() exposes the global beta event stream. A real /goal invocation becomes session.inbox.enqueued with an ordinary { type: "user", payload: { text }, delivery: "steer" } item, then session.inbox.delivered. No command name, command-origin marker, capability ID, or other unforgeable invocation identity is present in the observed public event/message shape.

4. Security consequence

The managed bridge text cannot be used as the authorization signal for lifecycle mutation because normal user input can reproduce the same text. Without an unforgeable host-origin signal, the adapter cannot safely distinguish a real /goal invocation from forged ordinary chat.

Separately, even correctly registered plugin tools do not currently reach the effective request tool set on beta-17498. Therefore there is no evidence-backed path on this beta to grant request-scoped opencode_goals_v2_control, execute the lifecycle mutation, and then prove replay is closed.

Do not replace this with a guessed text-marker/nonce scheme derived only from model-visible content.

5. Upstream watch target

anomalyco/opencode#35408 ([FEATURE]: Add V2 model-context plugin hook) tracks the missing provider-turn seam for mutating the final model-facing context/tools before inference. It directly matches the observed tool-materialization gap.

No matching open upstream issue was found for exposing an unforgeable custom-command origin/capability identity to V2 plugins. That remains a separate requirement unless the host adds an equivalent direct command interception API.

Remaining promotion gate

  • bounded same-service readiness without weakening Location requirements
  • Goals adapter activation on the exact current beta
  • host surface can materialize plugin tools into the effective request tool set
  • host surface exposes an unforgeable /goal invocation identity or equivalent direct command interception capability
  • predeclared-command / direct-command path → control tool → exact persisted Goal behavior
  • mismatched lifecycle arguments fail closed before persistence
  • ordinary/post-tool requests cannot reuse mutating lifecycle control and retain read-only Goal inspection
  • any anti-replay contract is derived from real-host evidence and guarded adversarially
  • Plan safety and fail-closed Location behavior have real-host coverage
  • completion/recovery parity scope is explicitly resolved before any stable support claim
  • opencode2-experimental adversarial category remains 100%
  • stable V1 CI, host progress, restart recovery, release readiness, and Actions Security Gate remain green on the promotion head

Resume rule

Resume V2 behavior implementation only when the exact beta/public V2 plugin surface provides both required capabilities above (or one equivalent host API that directly and safely handles custom command invocation plus request-scoped tool exposure). Until then, stable V1 remains the supported path and OpenCode 2 remains experimental/unsupported for lifecycle behavior.

Historical experiments and exact-beta logs remain in the issue comments.

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