Repository navigation
fix(v2): stop compaction usage from becoming phantom system overhead (#421) - #430
ranxianglei wants to merge 2 commits into
Conversation
…421) V2 native compaction projects the checkpoint as an assistant carrying the compaction REQUEST's token usage verbatim. cacheSystemPromptTokens used that assistant as its calibration anchor, storing the request size (repro: 653136) as permanent system overhead and driving hard-guard budgets negative (-285904 on a 400k model) while actual provider usage stayed small. - calibrateSystemOverhead: never anchor on summary assistants or on assistants created before state.lastCompaction; subtract the full wire-visible pre-anchor prefix (BPE-exact), excluding host-ignored user parts and ACP-owned notices. Plain-text sessions calibrate exactly as before (#255 suite unchanged). - cacheSystemPromptTokens: merge heuristic residual with a measured current-wire floor (max), record provenance in new transient SessionState.systemPromptTokensSource (heuristic|measured). - v2/context.ts: measure actual outgoing system parts + rendered ACP system prompt each transaction and pass as the floor. - Invalidation: resetOnCompaction and mid-session model switch now clear the cached estimate so post-compaction/reload recalibrates cleanly. - /acp context breakdown labels the system segment [measured]/[estimated]. Tests: tests/v2-compaction-system-overhead.test.ts (14 regression tests; verified to fail against pre-fix code, which stores the exact 653136 phantom). Full suite 1424/1425 (soft-block /tmp EACCES is environmental). Devlog: devlog/2026-09-17_v2-compaction-phantom-system/{REQ,DESIGN,WORKLOG}.md
[bot] 🏷 收到,开始核验这个 PR(不重做,按 [PR] 流程:拉分支核对代码 + 确认 CI + 报告结论)。已确认 PR 基于 |
…fallback (#421) The /acp context fallback in lib/compress/status.ts already passed state.lastCompaction to the live estimator, but the sibling fallback in estimateContextComposition (lib/messages/inject/utils.ts) did not. After resetOnCompaction invalidates the cached estimate, a pre-compaction assistant could anchor calibration again and resurface the stale overhead in nudge/context-usage math. Pass state?.lastCompaction ?? 0 and add a regression test (fails against the unfixed code).
核验完成(按 [PR] 流程:拉分支独立验证,未重做)。结论:修复正确、可合并,另补了一处漏网的小缺口(已直接修进 PR 分支)。 独立验证结果(全部通过)
审查中发现并已直接修复的一处缺口(commit 给 owner 的两点提示
合并请人工操作(我无权 merge)。 中文摘要:核验了 #421 幻影 system 开销修复的正确性(旧代码复现存入 653136、新代码不存),并直接修复了 live fallback 漏传 lastCompaction 的一处同类缺口(附回归测试,已推回 PR 分支),typecheck/build/全量测试均过(唯一失败为沙箱 /tmp 只读的环境问题);另注意此 PR base 非 master,CI 不会运行;可以合并。 |
Closes #421
Problem
V2 native compaction projects the checkpoint as an assistant carrying the compaction request's token usage verbatim (
lib/v2/projection/shared.tsmakeAssistantInfo).cacheSystemPromptTokensused that assistant as its calibration anchor, permanently storing the request size as "system + tool schemas" overhead. Repro from the issue: input 653137 → stored 653136 phantom; on a 400000-token model the hard-guard budget becomes −285904, producing negative-budget log spam and unnecessary tool clearing while real provider usage stays far smaller.Fix
lib/token-utils.ts— newcalibrateSystemOverhead(state, messages): never anchors oninfo.summary === trueassistants or assistants created beforestate.lastCompaction; subtracts the full wire-visible pre-anchor prefix with BPE-exact counts (excluding host-ignored user parts and ACP-owned notices).estimateSystemPromptTokens(messages, lastCompaction = 0)delegates to it.lib/ui/utils.ts—cacheSystemPromptTokens(state, messages, measuredSystemTokens?)merges the heuristic residual with a measured current-wire floor viamax(), recording provenance in new transient fieldSessionState.systemPromptTokensSource(heuristic|measured). The [FIX estimateSystemPromptTokens inconsistent between nudge and acp_status after compression #255] stable-cache early return is preserved.lib/v2/context.ts— measures the actual outgoing system parts (Σ countTokens(event.system[].text)+ rendered ACP system prompt) each transaction and passes it as the floor. V1 callers omit it → pure heuristic, unchanged.resetOnCompaction(lib/state/utils.ts) and the mid-session model-switch block (lib/messages/transform.ts) now clear the cached estimate, so first-request-after-compaction and after reload recalibrate cleanly instead of inheriting a stale/phantom value.lib/compress/status.ts—/acp contextbreakdown labels the system segment[measured]/[estimated], distinguishing actual wire accounting from heuristics (issue acceptance criterion).No persisted-state format change (
systemPromptTokens*were already transient), no config change, all API extensions are optional trailing parameters.Verification
tests/v2-compaction-system-overhead.test.ts— 14/14 pass: the exact V2: compaction usage becomes phantom system overhead and negative context budgets #421 repro (fresh state + reload both store no phantom), post-compaction anchor calibrates to real wire overhead with nonnegative budget at the reported 400k limit, measured-floor/provenance behavior, invalidation paths, and estimateSystemPromptTokens inconsistent between nudge and acp_status after compression #255 compatibility (plain-text sessions calibrate byte-for-byte as before).lib/reverted to pre-fix code, the originalcacheSystemPromptTokenson the repro input stores exactly 653136; fixed code storesundefined.tests/soft-block.test.tsmodule-loadEACCES /tmp— sandbox mounts/tmpread-only, pre-existing environmental issue, unrelated).npm run typecheckclean,npm run buildgreen, touched files prettier-clean.devlog/2026-09-17_v2-compaction-phantom-system/{REQ,DESIGN,WORKLOG}.md.中文摘要:修复了 V2 原生压缩后校准锚点误用 compaction 请求用量、把 653136 tokens 当成永久 system 开销导致硬护栏预算为负(-285904)的问题——排除 summary/过期锚点、按当前实际出站 system 部件做测量下限、在压缩与模型切换时失效缓存,并在 /acp context 中标注 measured/estimated 来源。