Skip to content

feat: opencode v1 — keep opencode-acp legacy sessions working under bili (#920) - #923

Merged
ranxianglei merged 1 commit into
masterfrom
2026-09-18_opencode-acp-legacy-bridge
Sep 18, 2026
Merged

ranxianglei merged 1 commit into
masterfrom
2026-09-18_opencode-acp-legacy-bridge

Conversation

@ranxianglei

@ranxianglei ranxianglei commented Sep 18, 2026

Copy link
Copy Markdown
Owner

What

On OpenCode 1.x hosts, bili opencode now runs new sessions through the bili proxy while existing opencode-acp ("legacy") sessions keep working with their own in-process machinery — per the user-directed design in #920. Previously acp globally self-disabled on /bili/ baseURLs and its store (~/.local/share/opencode/storage/plugin/acp/<sessionID>.json) became unreachable: decompress on an old ref returned [Block … not found] and numbering restarted.

Bili-side absorption, zero opencode-acp changes:

  • src/agent/acp-bridge.ts (new) — imports the installed opencode-acp package as a library (await import() around a temporarily unset BILLION_CONTEXT_PROXY, saved/restored in finally). Wraps every hook behind a per-session legacy gate = an acp store file exists for this session; non-legacy sessions are never touched, so acp's eager registry.getOrCreate adoption never fires. Its config hook is excluded entirely (that's where the global /bili/ self-disable lives). Tool executors are gated too (v1 tool registry is process-global/static — no per-request filtering hook exists). Any failure (package missing / import fails / version ≠ 1 / hooks shape mismatch) → null → exact pre-change behavior.
  • src/agent/opencode.ts — V1 plugin merges the bridge hooks + acp's DCP tool map, registers /dcp alongside /acp, chains command handlers (acp first — its abort sentinel wins for legacy sessions), and adds a chat.headers hook stamping x-bili-plugin-bypass: 1 on legacy-session requests (verified the v1.14.46 binary routes plugin headers onto the real LLM request). V2 setup() untouched.
  • src/server.ts — new bypass mode: x-bili-plugin-bypass: 1 → raw passthrough via the existing forward() ahead of JSON.parse / window resolution / session binding / injection / compression. Also flips the three wire-tool inject helpers from union to drop-same-named-then-inject: in proxy mode bili owns the compression tool names, so upstream sees exactly one definition per name (opencode ships acp's static DCP schemas in every body).
  • src/launcher.ts — the temp-config clone strips opencode-acp entries (string / tuple / object forms; fork-safe matcher — my-opencode-acp-fork survives) so the host never loads acp armed; the first stripped spec rides along as BILI_OPENCODE_ACP_SPEC so the bridge imports exactly the copy the host would have loaded.

Both compression modes reasoned (§6)

  • Bypass precedes the mode decision itself → neutral to both; it only ever fires for sessions the thin plugin has verified as legacy.
  • The inject-drop change only affects proxy mode (the helpers are never called when pluginMode is set), so plugin-mode clients see no behavior change.

Graceful degradation (documented in README zh/en)

Package not found / import fails / not v1 → the plugin behaves exactly as before this change; legacy sessions degrade the way they did when opencode-acp self-disabled on /bili/ baseURLs. One once-per-process warning is logged.

Pre-flight results (rebased head 0ff133a, base = current master 0cfde7a)

  • Rebased onto live master after ~35 PRs landed while this branch was open; two conflicts (launcher.ts opencode env block, server.ts import line) resolved as union of intent — master's [Suggestion] 建议 launcher 一并覆盖常见小写代理变量(http_proxy / https_proxy) #890 stripInheritedProxy env fix preserved alongside the opencode v1: keep opencode-acp legacy sessions working after switching to bili #920 strip-from-clone mechanism.
  • npm run typecheck: clean
  • npm test: 1751 pass, 0 fail, 2 skipped (pre-existing gated skips)
  • npm run build: success; dist sanity checked (BILI_OPENCODE_ACP_SPEC in dist/agent/opencode.js, x-bili-plugin-bypass in dist/index.js)
  • New tests: tests/acp-bridge.test.ts (probe order/authority, validation rejections, store-dir resolution matrix, env save/restore around acp init, full gating matrix, all degradation paths) · tests/fix-920-bypass-drop.test.ts (bypass byte-fidelity incl. unparseable bodies, proxy-mode same-name drop) · tests/launcher.test.ts (updated expectations + all-spec-forms strip)
  • CI on the rebased head: all greentest (ubuntu-latest, 24) ✅ · test (windows-latest, 24) ✅ · real codex vs deterministic fake upstream (zero tokens)

No config schema, persistence format, or cross-repo dependency changes. No version bump (content branch).

Closes #920

…ili (#920)

On OpenCode 1.x hosts, `bili opencode` now runs new sessions through the
bili proxy while existing opencode-acp ("legacy") sessions keep working
with their own in-process machinery:

- src/agent/acp-bridge.ts (new): imports the installed opencode-acp
  package as a library (BILLION_CONTEXT_PROXY temporarily unset around
  import + init), wraps its hooks behind a per-session legacy gate
  (acp store-file existence), excludes its config hook (the global
  /bili/ self-disable), and degrades to null on any failure.
- src/agent/opencode.ts: the V1 plugin merges the bridge hooks/tools,
  registers /dcp, chains command handlers (acp first), and stamps
  x-bili-plugin-bypass: 1 on legacy-session requests via chat.headers.
- src/server.ts: honors x-bili-plugin-bypass as raw passthrough ahead of
  any pipeline processing; wire-tool injection drops same-named client
  tools so upstream sees one definition per name in proxy mode.
- src/launcher.ts: the temp-config clone strips opencode-acp entries
  (all spec forms) so the host never loads it armed; the first stripped
  spec rides along as BILI_OPENCODE_ACP_SPEC for the bridge.
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Reviewing PR #923 (branch 2026-09-18_opencode-acp-legacy-bridge → master). Preliminary findings while I verify locally:

  • Code review (src/agent/acp-bridge.ts, opencode.ts, server.ts, launcher.ts, util.ts): the design is sound. Legacy-gated hook wrapping, the config hook correctly excluded from wrapped (so the plugin's own config hook survives the Object.assign), env save/restore around the acp import, drop-same-named inject scoped to proxy mode, and full graceful degradation all check out. Both compression modes are reasoned correctly (§6).
  • ⚠️ Blocker — stale base: the branch is 60 commits behind current master and GitHub reports mergeable_state: dirty — content conflicts in src/launcher.ts and src/server.ts (both hot files touched by concurrent PRs). It needs a rebase onto current master before merge.
  • ⚠️ CI: the head commit has 0 check-runs (combined status pending) — CI appears not to have completed. The full E2E A/B also wasn't run from the author's env (no local sglang upstream); ci-e2e.yml dispatch is recommended.

Running local typecheck/test/build and a rebase dry-run now; will follow up with the full verdict.

@ranxianglei
ranxianglei force-pushed the 2026-09-18_opencode-acp-legacy-bridge branch from 3917336 to 0ff133a Compare September 18, 2026 02:45
@github-actions

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-09-18_opencode-acp-legacy-bridge (0ff133a)

Option A — Install from npm PR tag (recommended)

npm install -g billion-context@pr-923

Each push to this PR publishes a new version under the pr-923 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr923.tgz
npm install -g package

Install instructions are refreshed on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Rebased onto current master (0cfde7a, head now 0ff133a) — ~35 PRs had landed while this branch was open. Two conflicts, both resolved as union of intent:

Evidence on the rebased head: typecheck clean · npm test 1751 pass / 0 fail / 2 skipped · build green · CI all green (test ubuntu-24 ✅, test windows-24 ✅, zero-token codex e2e ✅). PR body updated accordingly.

中文摘要:分支已 rebase 到最新 master(解决 launcher/server 两处冲突,均按双方意图合并,保留了 #890 的 env 剥离修复),rebase 后全量测试与 CI 全绿,可以合并。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 PR #923 review complete — independent verification done. Code is correct; the single red CI cell is an environmental flake, now re-running.

I re-ran the checks myself on the rebased head (0ff133a, base master 0cfde7a) rather than trusting earlier notes. Live state right now: PR open, not draft, mergeable=true, mergeable_state=blocked — blocked only by the still-running CI job, no conflict.

Local (Node v22.23.2):

  • npm run typecheck → clean
  • npm run build (tsup) → success
  • npm test1751 pass / 0 fail / 2 skipped (the 2 skips are pre-existing gated tests)
  • Bundle wiring confirmed in dist: BILI_OPENCODE_ACP_SPEC, x-bili-plugin-bypass, acp-bridge refs present; version unchanged at 0.1.118 (correct — content branch, no bump).

Code review (reasoned across both compression modes, §6): sound, no correctness issues. Highlights: acp-bridge.ts imports opencode-acp as a library and deliberately excludes the config hook from wrapped so the plugin's own config hook survives the Object.assign; every stateful hook gates on legacy-store existence before touching acp's handler; the proxy env is saved/restored around the import; full graceful degradation to null on any failure. In server.ts the three wire-tool inject helpers flip union→drop-same-named-then-inject, scoped to proxy mode only (they never run in plugin mode), so clients shipping their own ACP tool schemas see exactly one definition per name = bili's.

CI correction: the earlier "CI all green" note missed one cell. On head 0ff133a:

  • ✅ PR Build Artifact · e2e-fake-codex · CI Windows codex overlay · test ubuntu-22 · test ubuntu-24 · test windows-24
  • test (windows-latest, 22)not a code regression. It's 2 assertions in tests/ca-combined.test.ts (#152 ensureRootCA writes combined-ca.pem… :36 and #152 combined bundle re-merges… :48, both assert … includes(mitmRoot)), and the same log shows [dump] SSE stream dump failed … ENOSPC: no space left on device. That Windows runner hit disk-full mid-run, so the CA-bundle write/read-back failed. This PR touches none of src/ca.ts / src/mitm.ts / tests/ca-combined.test.ts, and identical code passed on windows-24 / ubuntu-22 / ubuntu-24 → environmental flake.

I've already triggered a re-run of just that failed job (rerun-failed-jobs → HTTP 201 accepted); the CI run is in_progress again and should go green shortly.

Bottom line: no code changes needed (nothing to fix); I did not modify or merge anything. Once the re-running CI cell is green, the PR is ready for you to merge. Optional follow-up: dispatch ci-e2e.yml (hosted) for the full E2E A/B of the legacy-session path, which isn't exercised from this sandbox.

中文摘要:代码本身正确(本地 typecheck / build / 1751 通过、0 失败,全绿);唯一红的 CI 是某个 Windows runner 磁盘写满(ENOSPC)导致未被本 PR 触及的 CA 证书测试断言失败——属环境抖动而非回归;我已触发该 job 重跑(现运行中),转绿后即可由你合并。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Re-run landed green — PR #923 is now fully verified. The re-run of test (windows-latest, 22) passed (fresh runner, no disk pressure), confirming the earlier failure was the ENOSPC environmental flake and not a code issue. The full CI run (35300590242) is now success — every cell green: version-guard · ubuntu-22 · ubuntu-24 · windows-22 · windows-24 (alongside the separate Build Artifact / e2e-fake-codex / codex-overlay runs, all already green).

Nothing else outstanding on my side: code reviewed across both compression modes, local typecheck/build/test clean, dist wiring confirmed, version untouched. The PR is ready for you to merge whenever — (reminder: merging stays human-only per repo rules; I have not merged or modified anything.) Optional: dispatch ci-e2e.yml (hosted) for the full E2E A/B of the legacy-session path.

中文摘要:重跑后 windows-22 通过,确认此前失败是磁盘写满(ENOSPC)的环境抖动而非代码问题;整条 CI 现已全绿,PR #923 验证完毕,可由你合并。

@ranxianglei
ranxianglei merged commit 5443769 into master Sep 18, 2026
12 of 13 checks passed
ranxianglei added a commit that referenced this pull request Sep 18, 2026
Union of the pi/opencode native branch stack (PRs #706/#919/#921 + #927
jsonc fix) with master's #923 lineage:

- Legacy opencode-acp sessions: keep the branch's opencode-legacy.ts
  (docker E2E verified on opencode 1.18.31 + acp 1.18.1: absorbed-acp
  compress/decompress/acp_status + x-bili-plugin-bypass verbatim
  forwarding) as the single legacy machinery; master's acp-bridge.ts is
  dropped as the duplicate. Its BILI_OPENCODE_ACP_SPEC launcher hint is
  adopted into legacyAcpCandidates (authoritative when the cache slot
  exists).
- src/agent/opencode.ts stays the branch's thin version-dispatch entry
  (V1 -> opencode-native server, V2 -> opencode-v2 setup).
- pi.ts: event-time proxy ownership gate (#519 native) UNION master's
  omp auto_compaction_start announcement tracking (#851).
- server.ts: master's early-return legacy bypass (BILI_PLUGIN_BYPASS_HEADER
  from util) replaces the branch's late pipeline exclusion; branch's
  pluginBypassHeader plumbing removed.
- plugin-install.ts: branch's full native installer (jsonc-first #927,
  plugin-entry + compaction.auto + stripLegacyOpencodeAcp) + master's
  claudeStatus isPlainMcpObject hardening (#839).
- Docs: table rows and launcher notes merged to describe both the native
  installer and the launcher legacy-session behavior; CHANGELOG union.

Verified: typecheck clean, 1822 tests (1820 pass / 2 E2E-gated skip),
build ok.
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.

opencode v1: keep opencode-acp legacy sessions working after switching to bili

1 participant