Skip to content

fix: yield to billion-context native mode via action-time env re-check (#405) - #406

Open
ranxianglei wants to merge 2 commits into
masterfrom
2026-09-16_billion-context-native-yield
Open

ranxianglei wants to merge 2 commits into
masterfrom
2026-09-16_billion-context-native-yield

Conversation

@ranxianglei

@ranxianglei ranxianglei commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Problem (#405)

billion-context's OpenCode 2.x native mode (PR ranxianglei/billion-context#824) owns compression from a package-installed plugin that rewrites model-API requests at http.request time. ACP's self-disable signals are all sampled statically:

  1. index.ts setup check reads BILLION_CONTEXT_PROXY once — native mode writes it only after async proxy bootstrap → missed;
  2. config hook / lib/bili-proxy.ts rely on /bili/ in provider baseURL — native mode never touches configured baseURLs → invisible.

Result: with both plugins installed, native mode double-compresses. The native entry ships a synchronous marker for exactly this handoff (markNativeHost() sets process.env.BILLION_CONTEXT_NATIVE = "opencode" at module evaluation, before any await, first-writer-wins) — but it can land after ACP setup sampled env.

Fix

Re-sample the owner markers at action time, reusing the existing yield path:

  • lib/bili-proxy.ts: new pure detectBiliEnvYield(env?) ("launcher" | "native" | null, launcher precedence — native bootstrap refuses to run while BILLION_CONTEXT_PROXY is set) + describeBiliEnvYield(), injectable env for tests;
  • index.ts: setup fast path covers both markers; guard() and the config hook re-sample env on every invocation/config run (one-time log per source per factory instance); deny-tools block extracted to a shared helper used by both the new env branch and the existing /bili/ branch (whose log message is preserved verbatim);
  • lib/compress/types.ts: resolveToolContext() — first statement of every ACP tool's execute — throws a clear "disabled" error when a marker is set at action time. One chokepoint gates all five tools without touching the five tool files; covers the residual window where a tool call lands after the last config run.

Non-latching (unsetting the marker restores ACP), no state-format/API changes, hot path stays O(1) with no I/O.

Tests

New tests/bili-native-yield.test.ts (10 tests through the real factory): unit detector + precedence, setup fast path ×2 markers, runtime marker → config denies all 5 tools / all hooks no-op / tool execute throws, launcher-marker parity, re-enable after unsetting, exactly-one yield log across repeated invocations.

Mutation-checked: with index.ts + lib/compress/types.ts reverted to master, 7/10 fail; with the fix, 10/10 pass. Full suite: 1273 pass / 0 fail; typecheck + build clean.

Trade-off (fail-closed)

If the native marker is set but billion-context's own async bootstrap later fails, ACP stays yielded for that process lifetime — recovery is removing the native plugin and restarting. Deliberate: failing open risks double compression (the bug this PR fixes); a failed bootstrap at least leaves compression visibly unowned. The marker is only written when the native gate passes at module evaluation, so this window is narrow.

Review

Dual-agent review (independent source + test reviewers). Findings addressed in commit 32d94ac: prettier violations in two tracked files; stale-flag edge (env branch returned before assigning disabledByBiliProxy — flag now computed before either branch); launcher log wording restored byte-identical to master via new biliYieldLogMessage(); tool-gate test extended from 2 to all five tools; hooks-no-op test extended to cover command.execute.before; re-enable test wrapped in try/finally; sibling bili-proxy-integration.test.ts now also clears BILLION_CONTEXT_NATIVE. Accepted info-level item: the one-time-log dedup var never resets within a process lifetime (cosmetic — detection itself re-samples env on every call). Re-verified after fixes: typecheck clean, 1273/1273 tests pass, build OK.

Devlog: devlog/2026-09-16_billion-context-native-yield/ (REQ + DESIGN + WORKLOG).

Fixes #405

#405)

Native mode (billion-context #820/#824) writes BILLION_CONTEXT_NATIVE at the
plugin's module evaluation — possibly after ACP setup sampled env — and never
touches provider baseURLs, so no pre-existing self-disable signal could see it.

- lib/bili-proxy.ts: pure detectBiliEnvYield()/describeBiliEnvYield() covering
  both owner markers (launcher precedence), injectable env for tests
- index.ts: setup fast path covers both markers; guard() and config hook
  re-sample env at action time with one-time log per source; deny-tools block
  extracted to a shared helper
- lib/compress/types.ts: resolveToolContext() throws when a marker is set at
  action time — single chokepoint gating all five ACP tools
- tests/bili-native-yield.test.ts: 10 tests (unit + integration through the
  real factory); mutation-checked: 7/10 fail on master, 10/10 pass with fix
@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown

📦 Built Plugin Artifact

Branch: 2026-09-16_billion-context-native-yield (32d94ac)

Option A — Install from npm PR tag (recommended)

opencode plugin opencode-acp@pr-406 --global

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

Option B — Install from GitHub

opencode plugin "github:ranxianglei/opencode-acp#2026-09-16_billion-context-native-yield" --global

Option C — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf opencode-acp-pr406.tgz
cp -r package/dist ~/.cache/opencode/packages/opencode-acp@latest/node_modules/opencode-acp/dist
  1. Restart opencode to pick up changes.

This comment is automatically updated on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

Verified PR #406 (fixes #405). This is already-completed agent work (ework-agent-pr marker), so I pulled the branch, cross-checked the code, and re-ran verification locally rather than redoing it.

Code review

  • Diff is focused and clean: 3 source files + 1 test + 3 devlog (REQ/DESIGN/WORKLOG all present; folder matches branch name). No version bump, no lockfile churn, no unrelated/whitespace changes.
  • Design is correct — all three yield signals now re-sample env at action time instead of trusting a startup snapshot:
    • lib/bili-proxy.ts: new pure detectBiliEnvYield(env?) ("launcher" > "native" precedence — native bootstrap refuses to run while BILLION_CONTEXT_PROXY is set) + describeBiliEnvYield(), injectable env for tests.
    • index.ts: setup fast path covers both markers; guard() re-samples on every hook call; config hook re-samples each run; the deny-tools block is extracted to a shared denyAcpTools() used by both the new env branch and the existing /bili/ branch (whose log message is preserved verbatim). One-time log per source via announcedEnvYieldSource — no spam on the per-request hot path.
    • lib/compress/types.ts: resolveToolContext() throws a clear "disabled" error when a marker is set at action time. Confirmed it is the first statement of execute() in all five tools (lib/compress/range.ts:102, lib/compress/status.ts:662, lib/compress/decompress.ts:277, plus recap/search) — so the single chokepoint genuinely gates every ACP tool without touching the five tool files, covering the residual window where a tool call lands after the last config run.
  • Non-latching holds end-to-end: unsetting the marker → detectBiliEnvYield() returns null → guard resumes + next config run stops denying. No state-format or API change; hot path stays O(1) with no I/O.

Local verification (independent re-run at head 278bea6)

  • New tests/bili-native-yield.test.ts: 10/10 pass
  • npm run typecheck: clean
  • npm run build: clean
  • Full suite: 1273 pass / 0 fail

CI status

  • build, build-artifact, test (22), test (24), pr-validation — all success
  • e2e — still running; I'll follow up when it lands.

Remaining gates before merge

  • e2e check green.
  • Per AGENTS.md §5.3 / §5.6, source + test changes need ≥2 independent agent reviews — this is my first round; a second independent review is still outstanding.
  • PR merge is human-only (§5.1.1.2) — I will not merge; please merge once the above are satisfied.

中文摘要:已核对 #406 代码与设计(action-time env 重采样 + resolveToolContext 单点拦截全部 5 个工具),本地复跑新测试 10/10、typecheck/build 干净、全量 1273 通过 0 失败;CI 除 e2e 外全绿,e2e 转绿并补齐第二个独立 agent review 后即可由人工合并。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

Follow-up — CI is now fully green

Check Result
pr-validation success
build success
build-artifact success
test (22) success
test (24) success
e2e success

On the "behind master" state: the branch is 15 commits behind, but I verified zero file overlap between those master commits (paper docs, quality-gate rejection, lib/hooks.ts, READMEs) and this PR's files (index.ts, lib/bili-proxy.ts, lib/compress/types.ts, tests/bili-native-yield.test.ts, devlog). So it merges/rebases cleanly — no conflicts. A rebase onto current master is optional for tidiness, not required.

Status: ready for the second independent agent review + human merge. Per AGENTS.md §5.3/§5.6 the source + test changes still need a second independent reviewer (this was my first round), and per §5.1.1.2 the merge itself is human-only — I won't merge it.

中文摘要:e2e 已转绿,全部 6 项 CI 检查通过;分支虽落后 master 15 个提交但与其改动零文件重叠、可干净合并(可选 rebase)。代码与测试我已复核无误,待第二个独立 agent review + 人工合并即可。

- prettier: collapse index.ts import line, wrap detectBiliEnvYield signature (F1)
- config hook: compute disabledByBiliProxy before either branch so the flag is never stale when an env marker and a /bili/ provider change together (F2)
- restore master's exact launcher log wording via new biliYieldLogMessage() (F4)
- tests: tool-gate now covers all 5 tools; hooks-no-op covers command.execute.before; re-enable test wrapped in try/finally; sibling bili-proxy-integration test also clears BILLION_CONTEXT_NATIVE
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.

与 billion-context native 模式共存:为 BILLION_CONTEXT_NATIVE 增加动作时退让检查

1 participant