Repository navigation
fix: invert the Responses instructions fingerprint into a codex/claude-only allowlist - #1107
Conversation
📦 Built Package ArtifactBranch: Option A — Install from npm PR tag (recommended)npm install -g billion-context@pr-1107Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf billion-context-pr1107.tgz
npm install -g packageInstall instructions are refreshed on each push. |
…e-only allowlist The fingerprint's only real defendants are codex (task-id reuse across personas, #150; modern codex subagent threads already isolate via x-codex-turn-metadata -> thread-id verbatim) and claude-over-Responses (#970 shared subagent ids). For everyone else - opencode (#1102), grok, mcode, plugin lanes, and the generic x-session-id / body session_id relay majority (#1106) - instructions drift mid-id means the same conversation evolved (upgrade / plugin / AGENTS.md), and fingerprinting forks an orphan |sub:<fp> session that resets all compression state. instructionsFingerprintExempt -> instructionsFingerprintApplies: OFF by default, ON only for x-codex-turn-metadata / codex user-agent / x-claude-code-session-id. The x-bili-plugin-instructions-mutable declaration is vestigial (kept in the protocol for older proxies). Fixes #1106
f68cb23 to
ed38270
Compare
[bot] 🏷 Reviewed this PR (it's already-open work, not redoing it). Findings so far: Code review — logic verified correct
Diff hygiene — clean: 6 files, every line on-purpose, no whitespace churn, no version bump, no generated/lock-file noise. Rebase + local gate on current master — the branch was behind by the persist-zstd merge (#1083). I rebased it onto
Rebased head pushed to the PR branch ( CI — original head Per repo policy (§7.4) this flips a default on the identity/session-keying path, so it stays human-merge regardless of gate results. 中文摘要:审核通过——instructions 指纹从豁免清单反转为 codex/claude 白名单的逻辑正确、diff 干净,已 rebase 到最新 master 并本地全量复验(typecheck/test/build 全绿),等 rebase 后 CI 跑完即可合并。 |
Review follow-up on the allowlist flip (#1107): the inline /codex/i regex was a second, case-insensitive definition of "codex traffic" alongside isCodexClient (the #645 convention used by every other codex path in server.ts). A case-insensitive match would pull non-codex relays with "Codex"-shaped UAs back into the instructions fingerprint and re-fork them mid-conversation - exactly what #1106 removes. Reuse isCodexClient so the process has one definition; also pin two test edges: a header-walk signal outranking x-claude-code-session-id takes claude out of the allowlist, and "Codex"-shaped UAs stay verbatim.
[bot] 🏷 CI now fully green on the rebased head ( Two notes for whoever merges:
中文摘要:rebase 到最新 master 后本地与 CI 全绿(typecheck / 2189 tests / build / 8 项 CI),代码审查无遗留问题,等待人工 review + 合并即可。 |
…-sharing hosts dsh stamps ONE x-bili-plugin-conversation id on every model request of a session, INCLUDING the auto-review classifyRisk() calls (fixed REVIEW_POLICY system + a freshly-flattened user blob, fired before every tool call under the Auto permission tier). Those review requests walked the full pipeline under the MAIN conversation id: they overwrote the usage baseline (#1916), evicted remembered snapshots (#1307's guard was a partial stopgap), and fed orphan-GC — while the review blob is synthesized fresh every time, so compressing it has zero reuse value. Key dsh traffic by id + system hash (kernel subagentNamespace anchor semantics): the FIRST system seen under the id keeps the raw key (the main turn claims it — dsh composes its main system once per session and puts the volatile time-context in a USER message, so main turns never fork mid-conversation), a different system forks onto <id>|sub:<fp> (the review persona), and an empty system is non-anchoring (verbatim key, system-less auxiliary calls keep riding the main session). - src/session-id.ts: dshPersonaFingerprintApplies (evidence-permitlist, the #1104/#1107 discipline — do NOT enable wholesale, #1106) + openaiSystemTextForPersona. - src/server.ts: fork applied on the openai and anthropic lanes; personaForked sessions record under their suffixed id (recordPluginSession) so the single-valued conversations map keeps the raw key owned by the main session (#970 discipline). - AGENTS.md: Key Design Decisions #9 — the rule for future hosts that reuse one session id across personas. - tests: fork/stability/allowlist/anthropic-lane coverage (peekSession — getSession is get-or-create and would mint the sessions we assert about). Refs #1916 #1307 #1314
Fixes #1106
What
Inverts
instructionsFingerprintExempt(#1104's trust list) intoinstructionsFingerprintApplies— an allowlist. The instructions fingerprint now participates in the compression-session key ONLY for:x-codex-turn-metadatapresent (any thread incl. root) oruser-agentmatchescodex(Codex 集成:guardian 审批请求进入压缩管线,导致明确授权无法被识别 #150: task-level ids reused across tasks; modern subagent threads already isolate via thread-id verbatim, which bypasses this predicate)x-claude-code-session-idwins the header walk (Subagent first token stalls 1-2min: withSessionLock covers full forward + slow flash upstream first byte #970: subagents share the main id; the Anthropic path splits them viaclaudeSubagentSplit, untouched)Everyone else keys verbatim: opencode (#1102 — now covered by the default), grok/mcode, plugin conversation lanes, generic
x-session-id/ bodysession_id(the relay-station majority, #1106). For them, instructions drift mid-id = same conversation evolved (upgrade / plugin / AGENTS.md edit), and the old default forked an orphan|sub:<fp>session resetting all compression state.The
x-bili-plugin-instructions-mutabledeclaration is now vestigial — hosts keep stamping it for protocol compatibility with older proxies; PLUGIN.md updated to say so.Why an allowlist, not a config knob
Same reasoning as #1104, inverted: the fingerprint only ever defended two verified id-sharing personas (codex, claude). A generic client that shares one id across personas and separates them ONLY by instructions is not known; if one appears, it joins the allowlist with evidence (issue documents this residual risk).
Changes
src/session-id.ts—instructionsFingerprintApplies()(codex turn-metadata header / codex UA / claude header → true; everything else false)src/server.ts— responses keying branch swapped to the inversesrc/plugin.ts/PLUGIN.md— mutable-flag comment/doc updated (vestigial, protocol-stable)tests/responses-instructions-drift.test.ts— unit tests rewritten for the allowlist; new e2e: generic relay client + instructions drift → ONE session (the Responses instructions fingerprint should be an allowlist (codex/claude only), not a default — generic relay clients fork on every system-prompt change #1106 regression), plugin lane without the mutable flag → ONE session (flag no longer required); kept the [Bug] opencode system-context reconcile:每次 AGENTS.md 编辑使 Responses 会话 key 里的 instructions 指纹漂移,压缩状态被重置(master 现存) #1102 opencode e2e unchangedtests/subagent-namespace.test.ts— Codex 集成:guardian 审批请求进入压缩管线,导致明确授权无法被识别 #150 guardian e2e now sends a codex user-agent (metadata-lesscodex_cli_rs/0.146.0, matching the real client); asserts the split still fires for codexPre-flight
npm run typecheck✅npm test✅ 2172 pass / 0 fail (2 pre-existing E2E skips)npm run build✅Review note
This flips a default on the identity path (wire/session keying, both modes reasoned) — per repo policy it stays human-merge.