Model: glm-5.3
压缩重请求后的拼接流导致下游 usage 双计(假 EMERGENCY)
证据(issue #292 日志)
13:26:21.161Z [366af3e19c251c72] [acp-usage] round 1 input=261206 cached=118663 (cache hit 45%)
13:26:21.250Z [366af3e19c251c72] nudge INJECT T1: usage=131% (261206/200000) ... reason="EMERGENCY T1"
261206 = 142543 + 118663 精确对上。机制:内层执行 compress 后向上游重请求,返回下游的流是两轮拼接:round 1 的 message_start(cache_read=118663)+ 合成 terminal(input_tokens=142543, cache_read=0)。下游解析只在 > 0 时更新(src/loop/adapter-anthropic.ts:297-299),terminal 的 0 无法覆盖 round 1 旧值 → 142543 + 118663 双计 → 131% 假超窗 → 误触发 EMERGENCY nudge + preflight 压缩。
该守卫本身有正当理由(src/loop/adapter-anthropic.ts:291-296 注释:部分 relay 会在 message_delta 回显 input_tokens: 0,采纳会低估上下文、压缩永不触发),不能简单去掉。
修复要求(两层任一或组合,建议都做)
- 生成侧(治本):acp-loop 多轮拼接流的合成 terminal 携带完整权威 usage(
input_tokens + cache_read_input_tokens 都填终轮真实值),让下游能原子采纳
- 解析侧(防御):
adapter-anthropic.ts 当 message_delta/terminal 的 usage 对象同时含 input_tokens(>0)与 cache_read_input_tokens 字段时,按完整对象原子更新(0 值可覆盖旧值);仅当字段残缺时维持现行 >0 守卫
验收(单测)
构造拼接流:round1 message_start(input=50000, cache_read=118663)+ 合成 terminal(input=142543, cache_read=0)→ 断言记录的总输入为 142543(非 261206);再构造 relay 回显 input_tokens:0 场景 → 断言不覆盖 message_start 真值(现行行为保持)。
Model: glm-5.3
压缩重请求后的拼接流导致下游 usage 双计(假 EMERGENCY)
证据(issue #292 日志)
261206 = 142543 + 118663 精确对上。机制:内层执行 compress 后向上游重请求,返回下游的流是两轮拼接:round 1 的
message_start(cache_read=118663)+ 合成 terminal(input_tokens=142543, cache_read=0)。下游解析只在> 0时更新(src/loop/adapter-anthropic.ts:297-299),terminal 的 0 无法覆盖 round 1 旧值 → 142543 + 118663 双计 → 131% 假超窗 → 误触发 EMERGENCY nudge + preflight 压缩。该守卫本身有正当理由(
src/loop/adapter-anthropic.ts:291-296注释:部分 relay 会在message_delta回显input_tokens: 0,采纳会低估上下文、压缩永不触发),不能简单去掉。修复要求(两层任一或组合,建议都做)
input_tokens+cache_read_input_tokens都填终轮真实值),让下游能原子采纳adapter-anthropic.ts当message_delta/terminal 的 usage 对象同时含input_tokens(>0)与cache_read_input_tokens字段时,按完整对象原子更新(0 值可覆盖旧值);仅当字段残缺时维持现行>0守卫验收(单测)
构造拼接流:round1
message_start(input=50000, cache_read=118663)+ 合成 terminal(input=142543, cache_read=0)→ 断言记录的总输入为 142543(非 261206);再构造 relay 回显input_tokens:0场景 → 断言不覆盖message_start真值(现行行为保持)。