Repository navigation
fix: preserve non-text blocks in Anthropic tool_result via sidecar - #367
Conversation
… from folding
OpenAI chat wire: user content parts that are neither text nor image_url (e.g. DeepSeek Files API {type:"file"} refs) were silently dropped by the parse/rebuild round-trip (ranxianglei/billion-context#1205). Non-text parts now ride rawOpenaiContentParts verbatim in wire order; coreToOpenai already re-emits them unchanged. A lone string-URL image keeps the legacy singular sidecar shape byte-stable (id seed untouched).
Compression: messages carrying media payloads (image/file sidecars) can no longer be folded into a summary — folding destroys bytes that live outside msg.text and hosts cannot resend (ranxianglei/billion-context#1188). Such messages get a BLOCKED ref, are excluded from recommended ranges, and are carved out of applied ranges with an operator warning. Escape hatch remains opt-in stripImages.
Behavior change: image/attachment messages were foldable before (pixels permanently lost); they are never folded now. Full suite: 1029/1029.
tool_result content arrays carrying image blocks lost them at parse time (c.text is undefined for non-text blocks) and coreToAnthropic rebuilt content from m.text alone, so every proxy round-trip silently dropped tool-produced images. Store the original block in rawAnthropicBlock when the content array holds non-text blocks and re-emit it verbatim; extend hasMediaPayload to recognize that carrier so such results are never folded. Text extraction and id seeds are byte-identical to before.
📦 Built Package ArtifactBranch: Option A — Install from npm PR tag (recommended)In your adapter project: npm install acp-kernel@pr-367Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf acp-kernel-pr367.tgz
npm install ./packageThis comment is automatically updated on each push. |
[bot] 🏷 Review complete on head ① Original issue — direction correct. Root cause verified on master ② Regression inventory (existing behaviors on touched paths):
③ Code. Minimal (4 files, +244/−5), mirrors the existing user-image sidecar pattern. Mechanism chain verified end-to-end: ④ Tests. Real teeth: round-trip tests pin the exact bug shape ( ⑤ Diff. Clean — every line serves this PR; no whitespace churn, no unrelated files. CI green on both heads (typecheck+test+build on Node 22/24); confirmed Non-blocking nits (leave or file later, your call):
Process notes: duplicate screening found no parallel work; the two out-of-scope blind spots (anthropic unknown top-level block, google remote Recommendation: mergeable once #365 lands (or rebase to master after that merge — either works, 中文摘要:审核了 PR #367(Anthropic tool_result 内嵌图片在解析期静默丢失的修复,sidecar 载体 + 折叠保护),已逐行核对根因、四处既有行为无回归、7 个新测试断言有牙且钉住精确 bug 形状,diff 干净、CI 全绿——可以合并(需先合 #365)。 |
Closes #366
问题
anthropicToCore()的tool_result分支对结构化 content 数组只提取文本(src/wire/anthropic.ts:101),image block 没有.text→ 像素在解析期蒸发;tool_result 消息不挂 sidecar,重建侧只从m.text还原 content。宿主经 Anthropic 线发送带图工具结果时每一轮往返都静默丢图(与 billion-context#1205 同类,但发生在 tool_result 内嵌场景,新会话第一条就丢)。修复(与 #365 同一原则)
tool_resultblock 存入既有rawAnthropicBlock字段(bili-message.ts:27-29的文档早已预留该用途)。纯文本 tool_result 不挂 sidecar,保持旧形状字节稳定。coreToAnthropic的 tool-result 分支优先使用 sidecar verbatim(与 user image 同路径),content 顺序、is_error、cache_control全部保留。hasMediaPayload(src/protected.ts)识别该载体——rawAnthropicBlock.type === "tool_result"且 content 数组含非 text block 时为 true。由此自动接入 fix: preserve unknown OpenAI content parts and protect media payloads from folding #365 的三处机制:BLOCKED ref(assignRefsNode)、compressible-range gap(buildCompressibleRanges)、显式 range 的 media carve-out + warning(applySingleRange)。行为变更披露(old → new)
AnthropicToolResult.content类型string | AnthropicTextBlock[]string | (AnthropicTextBlock | AnthropicImage)[]—— 跨仓注意:adapter 若对 tool_result content 做c.text映射会收到编译错误,需处理 image 变体不变的:text 提取逻辑与 id seed 逐字节一致(升级前后同一逻辑消息得到同一 id,ref 不漂移);纯文本 array / string content 的 tool_result 仍按旧路径重建为 join 字符串;OpenAI/Responses/Google 线不受影响。
验证
"done\n"、image 消失;修复后[text, image]原序逐字节恢复,hasMediaPayload = true。format:check在本仓库 master 上即有 119 个文件的既有失败(无 .prettierrc,代码风格与 prettier 默认不符);CI 实际只跑 typecheck+test+build,本 PR 未引入任何新的格式违规,也未顺带 reformat(避免 mass whitespace diff)。依赖
基于 #365 的 head(
90588d1)stacked——hasMediaPayload由 #365 引入。请先合 #365;合并后本 PR 可直接合,或我 rebase 到 master。范围外(issue 中列出的其他盲区)
issue 提到的 anthropic 未知 top-level block 无 default 分支、google 线远程 fileData 引用未挂 sidecar——按「一个 issue 一个 scope」未在本 PR 处理,是否立项请 owner 定。