Repository navigation
Detect host-rewritten tool-call ids in fold-drift diagnostics + host id-stability contract - #2400
Conversation
…gnostics (#2396) A host projecting one conversation across providers may rewrite the authoritative tool-call ids inside its projection (#2396: Prime switched a conversation from Codex to a custom Responses provider and its converter sent stored composite ids re-sanitized on both sides of every pair). Both claim passes key on toolCallId, so every folded tool pair stopped matching and compressed outputs re-entered the wire unfolded (~66k billed tokens re-sent at ~560k against a 500k window). Detection only — never repaired: pairing a missing covered id with an inbound twin under a different id would be a guess, i.e. misattribution (MESSAGE-IDENTITY.md failure-mode axiom). A no-toolCallId normalized identity (FoldAnchor.m, additive persisted field, backfilled while bytes live) lets planReconciliation count rewrite suspects and name them in the three drift log lines. The #2334 work-count pins move 8->16 and 12->16 (the no-tool norm rides along on tool-anchor rebuilds); steady-state cost stays zero. Evidence window is the first drift pass per episode (the order backbone rolls onto rewritten ids afterwards) — pinned by an e2e test that also asserts the escalation error does NOT claim evidence its own pass cannot see.
PLUGIN.md gains obligation 5: keep tool-call ids byte-stable within one conversation, including across provider projections, and canonicalize at the host boundary before the payload reaches the proxy. MESSAGE-IDENTITY.md (en + zh) records the #2396 violation as the one external dependency of the content-hash identity premise, why implicit aliasing is rejected (misattribution > non-recognition), and points at the new detection in src/fold-reconcile.ts.
📦 Built Package ArtifactBranch: Option A — Install this exact build (recommended)npm install -g billion-context@0.0.2589-pr.2400.4bd07b0This exact version is immutable and reproducible. The rolling Option B — Download artifact
tar xzf billion-context-pr2400.tgz
npm install -g packageInstall instructions are refreshed on each push. |
[bot] 🏷 PR #2400 Review 结论:可合(mergeable) — detection-only + 契约文档,零行为回归,门禁全绿。已本地取证(非「应该能过」):定向套件 ① 方向关(审原 issue #2396):根因判定正确——宿主(Prime)把同一会话跨 provider 投影时改写了权威 tool-call id;这是唯一「按会话协议唯一且客户端重序列化后原样存活」的字段被打破,bili 两条 claim pass 以它为键必然失配。bili 选择只检测、绝不猜修复(猜测 = misattribution = 更贵的失败模式,MESSAGE-IDENTITY.md 公理),契约落文档——scope 切分正确;真正的修复属于宿主边界(#2396 workaround 即参考实现)。 ② 回归审查(既有行为逐项清点):diff 只碰
③ 代码审查:逻辑成立——suspect 定义 = 「缺失 covered id 的去 toolCallId 归一化身份,与某个携带不同 toolCallId 的入站孪生相同」。region-bounded(只用 churn region 候选,避开每卡住 pass O(N_tool) 全文哈希)。诚实性有测试钉住:升级 error 行不得声称其自身 pass 看不到的 suspect 证据。 ④ 测试有效性:真测试非恒真。正例(同内容不同 id → suspect=1、claims=0)+ 三负例(stable-id churn 仍走 byTool 认领 / 真编辑内容不算 suspect / v0 无 m 保持 residual-pinned)+ e2e provider-switch 全生命周期(work count、日志点名、escalate-once、effectiveMessageIds 不改写、error 行 doesNotMatch 假证据)+ v0 backfill。断言有牙(精确计数 / deepEqual claims / 正则子串),bug 回归时抓得住。 ⑤ diff 整洁度:5 文件 +253/−13,两个聚焦 commit( 八问:
附带:state-coherence(双 resident 实例 21 vs 27 blocks)已由前序会话单独立项 #2401(带来源标记),正确分离、不混入本 PR。review 未发现需顺手修的小问题(命名 / 注释 / 文档措辞均与代码事实一致)。 收工判定:可合。分支 base == 当前 master、本地定向 42/42 + typecheck 干净 + 全部门禁绿、CI ubuntu 双 node 全量绿(仅剩 windows 两条腿在跑)。真正的修复属于 Prime 宿主一侧(边界规范化 id);bili 交付的是可辨识的诊断 + 落地的契约。 |
📦 Merged Build PublishedThis merge to master was published as npm install -g billion-context@0.0.159-master.c2dc81b # this exact build
npm install -g billion-context@master # always the newest merged build
|
Closes #2396
Problem
When a host projects one conversation across providers and rewrites the authoritative tool-call ids inside its projection, bili's fold-reconcile layer (#1921) loses coverage of every folded tool pair: both claim passes key on
toolCallId, so covered ids stop matching and compressed tool outputs re-enter the wire unfolded. #2396 documents this in production: Prime switched one conversation from Codex to a custom Responses provider, and its foreign-provider converter re-sanitized stored composite ids (call_switch_seed|fc_0_0— Codex lane serializescall_switch_seed; foreign lane sendscall_switch_seed_fc_0_0) on both sides of every pair. Context billed at ~66,525 input tokens re-entered at 558,541/562,549 against a 500,000-token window.The root cause belongs to the host (it broke the only protocol-stable pairing factor). Bili's response here is honest diagnostics + the documented contract — NOT repair-by-guessing: pairing a missing covered id with an inbound twin under a different id would be a guess, i.e. misattribution, the expensive failure mode per MESSAGE-IDENTITY.md.
Changes
fix(fold-reconcile) — detection only, never repaired:
FoldAnchorgains optionalm= sha256-16 ofrole\0contentType\0toolName\0normalizedText(no toolCallId) — additive persisted metadata; pre-[Bug][Prime/Responses] provider switch rewrites composite tool IDs and reintroduces compressed history #2396 anchors are backfilled while their bytes are still on the wire (same-id ⇒ same identity fields ⇒ exact recompute).normalizedIdentityNoToolCallId(); increments the existing perf: 长会话每次请求重复计算并丢弃超上限的压缩锚点 #2334 work-count seam.planReconciliationgainsidRewriteSuspects: after the claim passes, each unmatched covered id whose anchor has bothtandm, and whosemmatches an unclaimed inbound candidate carrying a different non-empty toolCallId, is counted.docs — the host contract:
Tests (targeted suite green locally: 42/42)
mstays residual-pinned (suspects 0).reconcileFoldCoverage: pass 1 seeds verbatim (resend); pass 2 rewrites all ids → unmatched 10, workCount 20, warn names the suspects + [Bug][Prime/Responses] provider switch rewrites composite tool IDs and reintroduces compressed history #2396; passes 3–4 → [dsh] 原生压缩拦截门的 SHAPE 信号(≤4 条消息)在 dsh-compaction-basic 0.2.0-rc.2 上恒假 → 压缩静默落地 → 1510 条 fold 锚点永久失配,连刷 166 条 no-anchor-match 且面板仍报 netSaved 355.9M #2193 escalation error; blockeffectiveMessageIdsnever mutated (detection only); the escalation error is asserted NOT to claim suspect evidence its own pass cannot see.mwhile bytes are on the wire.Config surface
No new config field, env var, or CLI flag. Persisted metadata is additive: fold anchors may carry one extra optional short-hash field; old records read fine, missing
m= pre-#2396 record = no evidence (never an error).Behavior changes (disclosed)
Known limitation
Suspect evidence exists only in the FIRST drift pass of an episode: the order backbone rolls onto the rewritten ids afterwards, so later passes (and the escalation error) report the generic causes. Deliberate — persisting suspect state into the drift-episode machinery is a separate mechanism family, and widening the scan to all inbound messages would add O(N_tool) full-text norms per stuck pass (a #2334 regression). Pinned by test.
The real fix belongs to the host (canonicalize ids at its boundary; the #2396 workaround is the reference implementation). If an owner wants a proxy-side escape hatch, the right shape is an explicit authoritative alias map (management endpoint per session), not implicit guessing — left as a decision point in #2396, not built here.