Skip to content

fix(agent-core-v2): gate image formats on hook-blocked prompts - #2948

Open
Win-Hao wants to merge 1 commit into
MoonshotAI:mainfrom
Win-Hao:fix/hook-blocked-image-gate
Open

fix(agent-core-v2): gate image formats on hook-blocked prompts#2948
Win-Hao wants to merge 1 commit into
MoonshotAI:mainfrom
Win-Hao:fix/hook-blocked-image-gate

Conversation

@Win-Hao

@Win-Hao Win-Hao commented Aug 15, 2026

Copy link
Copy Markdown

相关 Issue

Resolves #2947

问题

见关联 issue。概括:自 #1626(v2 引擎的图片格式闸门,对应 v1 的 #1536)起,每个 prompt 入口在把图片写进会话历史前都会过一次 gateImageFormatParts——坏图 / provider 不接受的格式换成 [Image omitted: …] 文字说明,被接受的图片按规范形式转发(image/jpgimage/jpeg、按字节嗅探纠正 MIME)。prompt / steer 两类 step request 是 SDK/RPC 路径的"last-funnel"兜底。

这道兜底漏了一条分支:UserPromptSubmit 钩子把 prompt 拦下时,调度器不走 step request,而是自己把用户消息写进历史(AgentPromptService.appendPrompt),这次写入没有过闸门。被拦下的 prompt 于是带着原始 image_url part 留在历史里(并落盘 wire.jsonl),而同一条 prompt 不被拦时会被过滤。该分支从 #1626 落地起就没过闸,release 0.34.0 ~ 0.36.1 均为同一段代码。

触发需同时满足:① 一条会拦 prompt 的 UserPromptSubmit 钩子(exit 2 / block);② 一张闸门本来会改写的图片,从"引擎闸门是唯一一道闸"的入口进来——kimi web 的 REST(source.kind: "url"data: URL 在边缘不做检查;kind: "base64"image/jpg 这类别名且未触发压缩时原样到引擎)或 SDK / klient 通道(VS Code 扩展、嵌入方;例如扩展名与字节不符的文件)。TUI 粘贴(魔数嗅探)与 ACP(提交前先过闸)不受影响。

后果(实测,Anthropic 方言,用只记录请求体的假 provider):这条消息之后每一轮随历史发出,请求 converter 抛 Invalid data URL for image … / Unsupported media type for base64 image: image/jpg,引擎的图片格式恢复把本轮请求里所有图片(含用户后来正常发的好图)换成占位文字后重发,日志可见 provider rejected an image in the request; resending with rejected media stripped;恢复按 turn 记录,之后每轮如此,直到会话清空——模型在该会话里再也看不见任何图片。OpenAI 兼容方言把 URL 原样发给 provider,能否恢复取决于 400 文案是否命中内置正则,否则该轮直接失败、之后每轮都失败。两种情况都严格差于正常路径(坏图变一句无害文字、好图始终可见)。

改动内容

  • AgentPromptService.appendPrompt 在写入前对 content 过同一道 gateImageFormatParts,防空判断改成基于过滤后的 content——与 UserMessageStepRequest 在正常路径 / steer 路径上的处理一致。caption 投递与 undo 归属不变。
  • promptService.test.ts 新增回归用例 gates hook-blocked prompt images too,紧跟既有的正常路径 / steer 路径闸门用例;在 main 上失败、修复后通过。
  • changeset:@moonshot-ai/kimi-code patch。

验证:promptService.test.ts 13/13;packages/agent-core-v2/test/agent + test/app + kap-server prompts.test.ts 共 157 个文件 / 2825 个用例全绿;agent-core-v2 包内 tsc --noEmit 无错误;oxlint --type-aware 对改动文件无新增 warning。另用真实 kimi web + 真实 [[hooks]] 子进程 + curl,以及 @moonshot-ai/kimi-code-sdk 两条路径做了端到端复现:main 上原始图片进历史,本分支上变为 [Image omitted: …];这些端到端复现脚本不在本 PR 内。

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

A prompt refused by a UserPromptSubmit hook is written into the history by
the prompt scheduler itself rather than through a step request, and that
write skipped gateImageFormatParts. Malformed or unsupported images (and
alias-MIME data URLs) therefore entered the session history unchanged
whenever a hook blocked the prompt, bypassing the last-funnel gate that
prompts reaching the model go through. Apply the same gate on the blocked
path and cover it with a regression test.
@changeset-bot

changeset-bot Bot commented Aug 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 78071cd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

被 UserPromptSubmit 钩子拦下的 prompt 不过图片格式闸门,坏图原样写进会话历史

1 participant