Skip to content

[PR] fix: acp-loop re-request rewrites thread.continue to thread.create on the Anthropic production path (#2701 finding 2) | [PR] fix: acp-loop re-request 在 anthropic 线上把 thread.continue 改写为 thread.create(#2701 finding 2) - #2706

Closed
ranxianglei wants to merge 1 commit into
masterfrom
2026-10-10_threads-continue-repair

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

关联 issue

#2701(Threads 下 bili 压缩回路在 continue 轮次全面失效)——本 PR 只修其中的 finding 2(立刻可做的 bugfix):continue 轮次上模型调用 compress → bili 本地执行后 re-request 复用客户端的 previous_message_id(round 1 已 continue 过它)→ 上游 400 "has already been continued" → 当前轮以错误结束、折叠丢失、下一轮重付 ~21 万 input token。

findings 1/3/4(nudge 失明 / 折叠只能 create 送达 / 紧急兜底无从生效)与建议 2–4(C 方案主动 create 改写、A 方案取舍、文档建议)是设计决策,留在 #2701 线程等 owner 拍板,故本 PR 不 close #2701。

机制与修法

src/loop/adapter-anthropic.ts 的 buildRequest 以 { ...body, system, messages } 重建 re-request——messages 是全量重构视图(coreToAnthropic 整个 kernel 状态,含折叠后视图),从来不是增量;但顶层 thread 字段原样透传。于是 re-request 带着已被 round 1 消费的 previous_message_id 发出 → 上游按「每个 response id 至多 continue 一次」规则拒绝。

修法:re-request 重建时若原 body 带 thread.type === "continue",改写为 { type: "create" }。create 是唯一与全量视图语义一致的形态:新 Thread 以折叠后视图播种,宿主下一次 continue(引用本 re-request 的响应 id)即继承压缩后的 Thread——这正是 #2701 finding 3 用真实流量验证过的继承路径。不能像 responses 线 #1954 那样直接剥掉字段:anthropic 响应不带 thread 字段就不属于任何 Thread,宿主的后续 continue 会直接 thread_not_found。

所有 re-request/retry 路径(proxy-tool re-request core.ts:1215、thinking-strip 重试 core.ts:1255、三处 nudge 重试 core.ts:804/850/908)都走同一个 adapter.buildRequest,一处修改全部覆盖。

行为变更披露(旧 → 新)

旧行为 新行为
anthropic 线 + proxy mode + 原请求为 thread.continue 时,acp-loop 本地执行 proxy tool 后的 re-request 原样携带客户端的 thread.continue → 必然 400(id 已消费),轮次死亡、折叠丢失 改写为 thread.create,全量折叠后视图播种新 Thread;宿主后续 continue 继承折叠态

不受影响(各有断言钉住):

无新配置项、无阈值/时序/输出格式变化、stream 形态不变。

测试

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-10-10_threads-continue-repair (71b06a7)

Option A — Install this exact build (recommended)

npm install -g billion-context@0.0.2923-pr.2706.5996df4

This exact version is immutable and reproducible. The rolling pr tag tracks the newest test build across all PRs.

Option B — Download artifact

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

Install instructions are refreshed on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 收到。识别为已完成工作的 [PR](ework-agent-pr 标记),不重做——正在拉分支核对代码、确认 CI 状态,随后按八问体例报结论。

@ranxianglei
ranxianglei force-pushed the 2026-10-10_threads-continue-repair branch from b48d0f4 to 71b06a7 Compare October 11, 2026 00:24
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 核对进展(代码已核完,CI 红因已定位并处理)

代码核对 —— 全部通过

  • 核心修法:src/loop/adapter-anthropic.ts buildRequest 仅在原 body 带 thread.type === "continue" 时改写为 { type: "create" };create / 无 thread 字段两种情况原样不动(F7c/F7d 断言钉住,含「不发明字段」守卫)
  • 确认全部 5 条 re-request/retry 路径都走同一个 adapter.buildRequest(core.ts:804/850/908 三处 nudge 重试、:1215 proxy-tool re-request、:1255 thinking-strip 重试),一处修改全覆盖属实
  • 新 e2e tests/issue2701-threads-recreate.test.ts:脚本化上游严格复刻 Anthropic once-only continue 规则(二次 continue 同一 id → 生产同款 400「has already been continued」);hermetic(listen(0) 端口、SessionStore 禁用、registry 清空);断言有牙——round-1 body 的 thread 深等于客户端所发(steady path 未动)、round-2 = create、已消费 id 不出现在 round-2、折叠落地(post-fold 视图到上游)、客户端收到 round-2 文本、流内无 upstream error
  • 独立复验了回归钉住声明:本地临时还原 adapter 修复后该 e2e 确实失败(1 fail,即生产同款症状),恢复修复后 38/38 全绿(loop-adapters 全套 + 该 e2e)

CI 红 = stale base,与本 PR 无关

已采取行动:分支 rebase 到当前 master(ba0b532f,含 #2702)后推送——b48d0f42 → 71b06a78,线性单提交保留,无冲突。rebase 后本地 typecheck 干净(两条 stripImages 红随 #2702 消失)。等新 CI 全绿后按八问给最终结论。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 PR 核对完成,全部验证通过。判定:可合并(CI 全绿、mergeable,等人工合并)。

按八问逐项:

  1. 是什么问题:anthropic 线上客户端请求为 thread.continue 时,acp-loop 本地执行 proxy tool(如 compress)后向上游 re-request,仍携带 round 1 已消费的 previous_message_id → 上游按「一个 response id 至多 continue 一次」规则 400("has already been continued")→ 当前轮以错误结束、折叠丢失、下一轮重付 ~21 万 input token([Bug][claude MITM] Threads 下 bili 压缩回路在 continue 轮次全面失效:nudge 失明、mid-turn compress 400(previous_message_id already continued)、折叠只能在 create 送达 #2701 finding 2)。
  2. 什么原因导致的:adapter 的 buildRequest 以 { ...body, system, messages } 重建 re-request——messages 是全量重构视图(kernel 整个状态含折叠后视图),但顶层 thread 字段原样透传自原 body。全量视图与 continue 的增量语义互斥:continue 引用的 id 已被 round 1 消费,必然被拒。
  3. 影响范围与严重程度:仅当 anthropic 线 + proxy mode(本地执行路径)+ 原请求 thread.continue(Claude Code/Desktop 等 Threads 宿主)三者同时成立;此场景下每次 mid-turn 压缩都死,折叠无法送达——是 continue 轮次压缩回路完全失效,非间歇问题。其他线 / 非 Threads 客户端 / plugin mode 不受影响。
  4. 怎么修复的:在 re-request 重建的唯一汇聚点把 thread.continue 改写为 { type: "create" }——create 是唯一与全量视图语义一致的形态:新 Thread 以折叠后视图播种,宿主下一次 continue(引用本 re-request 的响应 id)即继承压缩后的 Thread([Bug][claude MITM] Threads 下 bili 压缩回路在 continue 轮次全面失效:nudge 失明、mid-turn compress 400(previous_message_id already continued)、折叠只能在 create 送达 #2701 finding 3 真实流量验证过的继承路径)。刻意没有照搬 responses 线 [Bug][Responses HTTP] 删除 previous_response_id 时未重建增量请求历史,导致上下文静默丢失 #1954 的剥字段方案:anthropic 响应不带 thread 字段就不属于任何 Thread,宿主后续 continue 会直接 thread_not_found。五处 re-request/retry 路径(nudge ×3、proxy-tool re-request core.ts:1215、thinking-strip 重试 core.ts:1255)共用这一个 buildRequest,一处修改全覆盖——已逐一核实调用点。
  5. 是否有回归问题:无。逐项审过既有行为:steady path 转发不动(e2e 断言 round-1 body 的 thread 深等于客户端所发);非 Threads 客户端零变化、不发明字段(F7d);原 thread.create 不改动(F7d);openai/responses/google 线未触碰,responses 保留 [Bug][Responses HTTP] 删除 previous_response_id 时未重建增量请求历史,导致上下文静默丢失 #1954 strip+warn;plugin mode 不走本地执行路径;stream 形态、阈值、时序、输出格式均不变。验证证据:定向套件 38/38 绿;独立复验回归钉住——revert 修复后新 e2e 按预期失败(复刻生产症状),恢复后通过;rebase 到当前 master 后 CI 全 12 lane 绿(含 typecheck + ubuntu/windows × node 22/24 全测试矩阵)。
  6. 是否破坏缓存:否。只改 re-request body 的 thread 字段,不触碰消息重构顺序、前缀缓存断点或去重键;以折叠后视图播种的新 Thread 正是设计意图(折叠立即生效于服务端 Thread 状态)。
  7. 是否改了设计方向:否。现有机制内 bugfix,无新架构元素。[Bug][claude MITM] Threads 下 bili 压缩回路在 continue 轮次全面失效:nudge 失明、mid-turn compress 400(previous_message_id already continued)、折叠只能在 create 送达 #2701 的 findings 1/3/4(nudge 失明 / 折叠只能 create 送达 / 紧急兜底)与建议 2–4(C 方案主动 create 改写等)是设计决策,留在 [Bug][claude MITM] Threads 下 bili 压缩回路在 continue 轮次全面失效:nudge 失明、mid-turn compress 400(previous_message_id already continued)、折叠只能在 create 送达 #2701 线程等拍板,本 PR 不 close [Bug][claude MITM] Threads 下 bili 压缩回路在 continue 轮次全面失效:nudge 失明、mid-turn compress 400(previous_message_id already continued)、折叠只能在 create 送达 #2701。
  8. 是否引入了新的配置:无。无新 env/config/默认值;仅新增一条 info 级日志([acp-loop] re-request rewrote thread.continue → thread.create ...)。

行为变更披露(旧→新):anthropic 线 + proxy mode + 原请求 thread.continue 时,acp-loop 本地执行 proxy tool 后的 re-request:旧 = 原样携带已消费的 thread.continue → 必然 400、轮次死亡;新 = 改写为 thread.create,全量折叠后视图播种新 Thread,宿主后续 continue 继承折叠态。其余场景字节级不变(各有断言钉住)。

过程说明:

收工判定:可合并 —— 代码方向、机制正确性、回归钉住、CI 均已核验,请人工合并 #2706 。

@ranxianglei ranxianglei changed the title fix: acp-loop re-request 在 anthropic 线上把 thread.continue 改写为 thread.create(#2701 finding 2) [PR] fix: acp-loop re-request rewrites thread.continue to thread.create on the Anthropic production path (#2701 finding 2) | [PR] fix: acp-loop re-request 在 anthropic 线上把 thread.continue 改写为 thread.create(#2701 finding 2) Oct 16, 2026
@ranxianglei

Copy link
Copy Markdown
Owner Author

(ai: zhipuai-lb/glm-5.3)

孤儿票清理:标题指向的 issue 已关闭,修复已由其他路径落地。关单;误判请 reopen。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant