Skip to content

fix #645: suppress #408 host-usage backfill for codex (report folded usage) - #647

Open
ranxianglei wants to merge 3 commits into
masterfrom
2026-09-08_codex-usage-backfill-exempt
Open

fix #645: suppress #408 host-usage backfill for codex (report folded usage)#647
ranxianglei wants to merge 3 commits into
masterfrom
2026-09-08_codex-usage-backfill-exempt

Conversation

@ranxianglei

@ranxianglei ranxianglei commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Problem (#645)

After v0.1.88 (which shipped the #408 host-usage backfill), Codex displays a
broken context metric: 1315/950k (138%) for a conversation whose real
post-fold usage is ~75k. The user's own proxy log shows the mechanism:

nudge idle: usage=7% (74941/1050000)          <- internal ledger (post-fold)
host usage backfill armed: +1234768 tok       <- #408 credit added to input_tokens

74,941 + 1,234,768 ≈ 1,311,663 ≈ the displayed 1315k.

Why the backfill is wrong for codex

  • Virtual number: the model never receives the baseline — the forwarded
    request really is ~75k.
  • Drifts turn-to-turn: it mixes real post-fold usage with a character-based
    estimate of the folded-out tokens, so the metric can decrement without any
    compress firing (reported in [Question] Previously, Codex displayed the context window size after compression, but it now appears to show an unexpected/incorrect value. #645).
  • Exceeds the window: 1315/950k = 138%.
  • Drives nothing in codex: codex's auto-compact keys off total_tokens
    (codex-rs session/context_window.rsget_total_token_usage()), and the
    backfill only touches input_tokens on the responses wire — so the inflated
    number currently produces only the broken display, no compaction benefit.

Fix

Extend the #590 (pi) / #623 (omp) exemption to codex in
armHostUsageCredit: codex now reports the folded request's own usage.
Detected by User-Agent because bili-launched codex sessions carry
pluginAgent "mcp" (shared with claude) and can't be gated on the header.

Field-verified UA matcher (#645): the reporter's codex is the TypeScript
SDK client
, whose UA is codex_sdk_ts/<ver> … (codex_exec; <ver>)not
covered by the original codex_cli_rs/ / codex_exec/ prefixes (the
codex_exec text is only inside a parenthetical). Codex ships multiple clients
with different UA prefixes, so isCodexClient now also matches codex
anywhere in the UA (case-sensitive) in addition to the known prefixes — current
and future codex client variants are all detected. (Confirmed via a temporary
[codex-ua-debug] log line in a diagnostic prerelease, since removed.)

Safety nets are unchanged: ACP compression (nudge → preflight) runs on the
real post-fold number, and codex's native auto-compact remains the backstop at
90% of its window (#321 design). The uncompressed baseline is still visible in
the /acp panel (hostContextTokens) and the proxy log.

Tests

Pre-flight

  • npm run typecheck — clean
  • npm test — 1237/1238 pass; the 1 failure (launcher.test.ts: resolveClientCommand: codex/claude resolve to themselves) is pre-existing on
    clean master (this environment resolves codex to /usr/bin/codex; the test
    expects the bare name) and unrelated to this change
  • npm run build — success; published prerelease 0.1.96-pr.647.472 verified
    to contain the matcher and no diagnostic

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

📦 Built Package Artifact

Branch: 2026-09-08_codex-usage-backfill-exempt (bad67d2)

Option A — Install from npm PR tag (recommended)

npm install -g billion-context@pr-647

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

Option B — Download artifact

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

This comment is automatically updated on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Review: APPROVE

Checked out the actual PR commit (497d6d3) and ran the full pre-flight against it — not just the description.

Pre-flight (verified on 497d6d3):

  • npm run typecheck — clean
  • npm test1236/1237 pass. The 1 failure is resolveClientCommand: codex/claude resolve to themselves (tests/launcher.test.ts:24): this env resolves bare codex/usr/bin/codex, so the deep-equal against 'codex' fails. Confirmed pre-existing & unrelated — the PR touches neither src/launcher.ts nor tests/launcher.test.ts (empty diff on both).
  • npm run build — success (tsup, 262ms)
  • Both new tests pass and are not vacuous: real kernel fold (sentinel SENTINEL_FOLD_GONE in the folded assistant msg), !body.includes(sentinel) proves the fold actually happened, and the control test pins the non-codex side (input_tokens > 1000).

Design — sound, single-point source fix:

  • armHostUsageCredit now returns early for isCodexClient(headers), so hostCreditTokens = 0. That propagates cleanly to every consumer (backfillHostUsage no-ops at credit<=0; panel hostContextTokens; log). All 3 call sites (prepareAnthropic/Openai/Responses) updated.
  • UA is the right signal. isCodexClient (codex_cli_rs/, codex_exec/) is the same predicate native-compact interception already uses (src/server.ts:2589), so no new trust surface. And the pluginAgent gate genuinely can't work here: the MCP shell registers agent: "mcp" (src/mcp.ts:146), shared by the codex and claude launcher paths — so "mcp" can't distinguish codex. Confirmed the rationale holds.
  • Inert + broken display, not a compaction driver. On the responses wire backfillHostUsage only touches input_tokens (never total_tokens), and codex's auto-compact keys off total_tokens — so the backfill drove nothing and only produced the 1315/950k footer. Exempting it is safe even if that codex-rs detail were wrong: ACP nudge→preflight runs on the real post-fold number, and codex native auto-compact at 90% remains the backstop.
  • Both modes covered. Gated on UA (not mode), so it exempts codex whether it arrives via the MCP shell (plugin mode, pluginAgent "mcp") or plain /bili/ (proxy mode). pi/omp still take the earlier pluginAgent early-return; non-codex plain clients keep the 代理压缩不上报改写量,宿主上下文会计塌缩至 ~0(omps recordAnchoredHistoryRewrite 零调用);且持久化 lastInputTokens 恢复无钳制,旧版负值升级后复活 #408 backfill (control test).

One minor, non-blocking nit (PR description, not code):
The line "The uncompressed baseline is still visible in the /acp panel (hostContextTokens) and the proxy log" isn't accurate for codex. With hostCreditTokens = 0, hostContextTokens = total + 0 = the folded total (panel reads it at src/plugin.ts:429/449), and the host usage backfill armed: +N tok line (src/server.ts:1843) is no longer printed. The baseline is intentionally no longer reported for codex — same as pi/omp. The code behavior is correct; just suggest tightening that sentence to "the host and /acp panel now show the folded (forwarded) usage for codex; the uncompressed baseline is no longer reported (consistent with #590/#623)."

No code changes needed. Good fix.

ework-agent added 2 commits September 8, 2026 22:20
Logs the request User-Agent + isCodexClient result + pluginAgent per request so we can see exactly what UA the field codex sends and why the #645 exemption is not firing. Temporary; will be removed (with any matcher adjustment) in a follow-up commit before merge.
The field codex (issue #645) is the TypeScript SDK client, whose UA is 'codex_sdk_ts/<ver> ... (codex_exec; <ver>)' — not covered by the codex_cli_rs/ / codex_exec/ prefixes, so the #645 backfill exemption never fired and the >100% context display persisted. isCodexClient now also matches 'codex' anywhere in the UA (case-sensitive), so current and future codex client variants are all detected. Removes the temporary [codex-ua-debug] log block added to confirm the field UA. Adds regression tests for the SDK UA and the lenient fallback.
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