Skip to content

fix(agent): restore /acp status command under OpenCode V2 (#809) - #850

Merged
ranxianglei merged 13 commits into
feat/pi-native-pluginfrom
2026-09-15_opencode-native-plugin
Sep 17, 2026
Merged

ranxianglei merged 13 commits into
feat/pi-native-pluginfrom
2026-09-15_opencode-native-plugin

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

What & why

Restores the /acp status command for OpenCode 2.x native mode.

Root cause: /acp was implemented only against the legacy .server() plugin API (src/agent/opencode.ts). The V2 migration makes opencode load plugins as {id, setup} and never call .server(), so /acp was orphaned — it never registered, and native mode looked "inactive".

This registers /acp in the shared createOpencodeV2Setup factory so BOTH the launcher entry and the native entry surface it.

API contract (verified against @opencode/cli v2.0.3 source)

  • Commands: ctx.command.transform(editor.add({ name, description, execute })); execute(input) receives input.sessionID.
  • Panel render: ctx.session.synthetic({ sessionID, text }) inserts a synthetic message that does NOT trigger a model turn (V2 replacement for the legacy ignored no-reply prompt).

/acp fetches <proxy>/__bili/plugin/status?conversationID=<sid>&fallback=latest and renders the panel (idle/error notice when no proxy). Inert-safe on surfaces without ctx.command; TUI-only.

Scope check: the legacy .server() registered ONLY command["acp"] (src/agent/opencode.ts:98/:104) — no other local slash commands exist in src/agent/, so /acp is the complete set of orphaned commands.

Files

  • src/agent/opencode-v2.ts: add command seam to V2PluginContext + V2CommandEditor types; register /acp after tool registration.
  • tests/opencode-v2.test.ts: fake ctx gains command.transform + session.synthetic; fake proxy serves /status; 2 new tests (panel render, no-proxy notice).

Verification

  • typecheck clean
  • full suite: 1654 pass / 0 fail / 2 skip (win32-only)
  • build OK; dist markers confirmed in both agent entries
  • clean single-commit delta vs feat/pi-native-plugin (merge-base 1d4f38c, +133/-1, 2 files)

Refs #809. (#809 intentionally left open per owner — baseline reference.)

Note: this is the /acp delta only. If you'd rather bundle it with the #842 coexistence gate (#840) before it lands on the umbrella, say so and I'll close this and re-cut from the updated branch.

The /acp status command was implemented only against the legacy .server()
plugin API and orphaned by the V2 migration — OpenCode 2.x loads `{id, setup}`
and never calls `.server()`, so /acp never registered (native mode looked
"inactive").

Register /acp in the shared createOpencodeV2Setup factory so BOTH launcher and
native modes surface it. API verified against @opencode/cli v2.0.3 source:
  - ctx.command.transform(editor.add({name, description, execute})) — commands
    carry an execute(input) handler (input.sessionID is provided).
  - ctx.session.synthetic({sessionID, text}) — inserts a synthetic message that
    does NOT trigger a model turn (the V2 replacement for the legacy ignored
    no-reply prompt).

/acp fetches <proxy>/__bili/plugin/status?conversationId=<sid>&fallback=latest
and renders the panel (or idle/error notice) via synthetic. Inert-safe on
surfaces without ctx.command; TUI-only (`run` dispatches no slash commands).

- src/agent/opencode-v2.ts: add command seam to V2PluginContext + V2CommandEditor
  types; register /acp after tool registration.
- tests/opencode-v2.test.ts: fake ctx gains command.transform + session.synthetic;
  fake proxy serves /status; 2 new tests (panel render, no-proxy notice).

typecheck clean; full suite 1654 pass / 0 fail / 2 skip (win32-only); build OK;
dist markers confirmed in both agent entries. Refs #809.
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Review of the /acp V2 restore (commit 11a82d7) — verified, ready to land.

What I did: pulled the branch (opencode-native-plugin @ 11a82d7, identical to origin/2026-09-15_opencode-native-plugin), cross-checked the diff against the server contract, and re-ran the full verification gauntlet locally. This is a review of completed work — I did not redo it.

Diff integrity — matches your description exactly: single commit on baseline 1d4f38c, +133/-1 across 2 files (src/agent/opencode-v2.ts +71/-1, tests/opencode-v2.test.ts +63).

Contract cross-check (all correct):

  • Handler sends conversationId=…&fallback=latest (src/agent/opencode-v2.ts:231); server reads params.get("conversationId") (src/server.ts:733). Names match. (Minor: the issue body writes conversationID with a capital ID — cosmetic typo in the writeup; the code itself is correct.)
  • Response shape {ok, panel, error} matches handlePluginStatus (src/plugin.ts:521-537): {ok:true, panel,…} on success, {ok:false, error} on unknown/no-session.
  • Faithful port of the legacy /acp (src/agent/opencode.ts:108/:116): same status URL, same panel/idle/error branching, same fetchProxyVersion fallback. V2 renders via ctx.session.synthetic (non-model-turn), TUI-only, inert-safe through optional chaining when ctx.command is absent.
  • console.error in the agent file is fine — src/agent/* runs in-host (client-side), not in the proxy process, and every sibling agent module already uses console.error (the "no console.error" rule targets server-side modules).
  • Scope claim holds: /acp is the only local slash command registered under src/agent/; nothing else was orphaned by the V2 migration.

Verification gauntlet (re-ran, all green, matches your numbers):

  • typecheck: clean
  • npm test: 1654 pass / 0 fail / 2 skip (win32-only)
  • build: OK
  • dist markers: Show ACP status present in both dist/agent/opencode.js (×2 = V2 setup() + legacy server()) and dist/agent/opencode-native.js (×1 = V2 only) — as expected.

Landing path (your actual question):
The umbrella feat/pi-native-plugin has advanced past 1d4f38c to ca4b1ab (it already absorbed the earlier part of this branch via PR #824). I verified:

Recommendation: land /acp standalone as its own small PR against the current feat/pi-native-plugin tip. Rationale: it's a self-contained single commit, rebases cleanly with no conflicts, has zero file overlap with the #842/#840 gate, and is independently green — so there's no technical coupling forcing a bundle. Keeping it separate means the coexistence gate can't block it (and vice versa), and each lands with a smaller, easier review. Bundling only pays off if there's a shared-file conflict or ordering dependency, and there isn't one here. (Final call is yours — happy to re-cut if you'd rather bundle.)

I have not pushed or opened anything — confirm standalone vs. bundle and I'll cut the PR accordingly.

中文摘要:独立复核了 /acp V2 恢复提交(11a82d7)——diff 与描述一致(+133/-1、2 文件),契约核对无误,typecheck / 测试(1654 通过 / 0 失败 / 2 跳过)/ 构建 / dist 标记全部复现通过;该单提交可零冲突落到当前 umbrella 尖端、与 #840 共存门文件零重叠,建议作为独立小 PR 合入而非捆绑。

ework-agent and others added 2 commits September 16, 2026 21:57
…t, inert-safe registration)

F1: check empty-sessionID guard before the disabled branch so a missing id never reaches ctx.session.synthetic with an empty id. F2: read status via shared fetchStatus (5s STATUS_TIMEOUT_MS soft-fail) instead of a bare unbounded fetch. F3: surface status.error in the no-panel fall-through. F4: wrap command registration in its own try/catch so an unsupported editor.add degrades to no-/acp instead of rejecting setup. F5: console.warn on the no-sessionID path so a broken host contract surfaces instead of dead silence.
OpenCode V2's session.synthetic defaults to delivery="steer" and, unless
resume:false, calls execution.wake(sessionID) — every /acp invocation
started a model turn on behalf of a status query. Observed live in TUI:
the model answered its own injected status panel with an unprompted
assistant message. Pass resume:false so rendering stays inert.
The /acp panel was carried only in the synthetic message's text. OpenCode V2's
TUI renders a synthetic message as a Notice row only when its display text fits
the timeline cap (~1KB; message-timeline.tsx canRenderImmediately), so real
panels were silently dropped: users saw nothing after /acp.

The panel now goes to description (verbatim under 1024 chars, leading lines +
[panel truncated] above) and the model-facing body is an inert one-line note
(V2_SYNTHETIC_TEXT) so the model context never repeats panel content.

Verified live in a v2.0.4 TUI: short panels render fully as the notice row,
a >1.5KB panel renders its leading lines plus the marker; no model turn is
triggered (pairs with the no-wake fix #879).
…o-wake

fix(agent): /acp must not wake the model under OpenCode V2
…isible-panel

fix(agent): make opencode v2 /acp status panel visible in the TUI
ework-agent and others added 2 commits September 17, 2026 11:18
PR #878 was merged only into 2026-09-17_opencode-v2-acp-no-wake (tip 40c4970), so the #850 head was missing the panel-visibility fix. This fold makes 2026-09-15_opencode-native-plugin carry the complete /acp stack: register (11a82d7) + harden (f857ff1) + no-wake (d6d60f8, #875) + visible panel (304110a, #878).
… model request

Before the conversation's first model request the proxy knows no ACP session;
handlePluginStatus answers that with HTTP 404 {ok:false}. shared.fetchStatus()
soft-fails every non-2xx into undefined, so the v2 renderer fell through both
the panel and the ok===false branches and printed 'proxy returned no status
panel' instead of the friendly idle line.

Handle the undefined result explicitly: probe the manifest, and when it
answers render 'billion-context@<v> — proxy connected, no ACP session yet…';
when nothing answers, say the proxy is unreachable. Second-and-later calls were
already correct (panel or ok:false path). Same soft-fail contract as v1's
inline fetch, without changing the endpoint's wire semantics.
…dle-first

fix(agent): first /acp under opencode v2 shows the idle notice, not 'no status panel'
@ranxianglei

Copy link
Copy Markdown
Owner Author

继续

…ive plugin instead of standing down

The pseudo-attach hole: presetting BILLION_CONTEXT_PROXY and running bare
opencode disarmed the native bootstrap entirely (no spawn, no attach) while
tools still discovered the proxy through the env — model traffic silently
went direct, the proxy never saw the session, and every tool forward 404'd
("unknown plugin conversation"). planNativeOpencode now treats the preset
value as an external attach target (probe + rewrite + stamp, same semantics
as BILLION_CONTEXT_ATTACH; explicit ATTACH wins when both are set). Kill
switches (BILLION_CONTEXT_PLUGIN=0, BILI_NATIVE_OPENCODE=0) and /bili/
launches (BILI_PROVIDER_REWRITES) still stand down; a garbage preset falls
back to spawn.

Also: the opencode v2 plugin tool forward now returns guided text when the
proxy reports the conversation was never registered, instead of surfacing
the raw 404 that sent the model down a "refresh/restart" tangent.

Verified in-container end-to-end (real qwen upstream) across all four
postures: proxy (baseURL), launcher (bili opencode), native spawn (bare
opencode + plugin), and preset-env attach — session registration, live
conversation, acp_status real execution, /acp panel render.
…-modes

fix(agent): opencode v2 preset-env attach — close the pseudo-attach 404 hole (three-mode delivery verified)
ranxianglei pushed a commit that referenced this pull request Sep 17, 2026
The history rewrite that dropped the tracked iso/build artifact also swallowed
three unrelated dev-tool scripts (registry/codex-models snapshot refreshers,
dump analyzer) into commit e59776b, whose message describes the tr_ keying fix.
package.json still wires 'npm run registry:snapshot' / 'codex-models:snapshot'
to them, so the deletion broke those scripts. Restore verbatim from the #850
base.
Sisyphus and others added 2 commits September 17, 2026 22:42
…rst)

The forwarded kernel acp_status returns the legacy flat report while the
/acp command shows the rich status panel — tool and command disagreed.
acp_status is read-only, so the plugin now reads the panel from the
proxy's status endpoint first and falls back to forwardTool when no panel
is served (older proxy, unknown conversation).

Fixes #911
…-first

fix(agent): opencode v2 acp_status renders the status panel (panel-first)
@ranxianglei
ranxianglei merged commit 6159d25 into feat/pi-native-plugin Sep 17, 2026
1 check passed
cskwork pushed a commit to cskwork/billion-context that referenced this pull request Oct 21, 2026
The history rewrite that dropped the tracked iso/build artifact also swallowed
three unrelated dev-tool scripts (registry/codex-models snapshot refreshers,
dump analyzer) into commit 6f5ea52, whose message describes the tr_ keying fix.
package.json still wires 'npm run registry:snapshot' / 'codex-models:snapshot'
to them, so the deletion broke those scripts. Restore verbatim from the ranxianglei#850
base.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant