Bug 报告:插件 output.render 返回裸字符串会让整个会话历史不可加载 #5647
luoxin10086
started this conversation in
General
Replies: 1 comment
|
补充:完整修复方案含第二处补丁(P0-3, append 时 fail-fast) 上面的 P0-1 修复了写路径的源头(render 契约违规不再产生坏记录)。但同一个根本问题还有另一半:Session.append 目前只校验 JSON 可序列化与 surface 元数据,从不校验消息形状——形状校验只在加载/seed 边界执行。也就是说,即使 P0-1 合入,未来任何其他 producer 写出形状违规的消息(比如空 callId,正如 streamed tool-call identity 那次修复的场景),仍然会"写入时无报错、下次加载整条历史不可加载"。 建议的 P0-3:在 Session.append 里、surfaceManager.validateNext 之前,调用加载器同款的 assertMessageEventShape。它对非消息事件类型直接 return,热路径无额外开销;坏消息则在 append 处当场抛错、永不进入持久化日志。 完整 diff 与测试材料已备好(含 Agent Note 双语版)。如果团队认可方向,仓库开放外部 PR 后我可一并提交 P0-1 + P0-3。 |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
问题概述
ToolOutputDefinition.render 的类型是 ContentBlock[],但运行时没有任何强制校验。createSuccessResult 会把 render 的返回值原样持久化进 tool/result 消息——所以一个违反契约的插件(render 返回裸字符串)会写出一条 block content 为字符串的记录。会话加载器(assertMessageEventShape)要求那里必须是 ContentBlock[],于是第一条这样的记录就让整条会话历史不可加载(SessionPersistenceCorruptionError)——而且只在下次加载时才暴露,写入时完全没有报错。
复现
dsh-ssh-ops v0.2.1 的 sftp_/tunnel_ 工具从 output.render 返回裸字符串,已导致多个会话报错:history unavailable … must contain one tool-result block。
根因
形状契约只存在于 TypeScript 类型里,且只在加载/seed 边界强制执行——写路径(Session.append / createSuccessResult)从不校验。这与之前空 callId 的修复(streamed tool-call identity)是同一类失败模式,但发生在通用写路径层面。
建议修复(已备好,可随时提交)
在 createSuccessResult(所有成功工具结果的唯一收口)规范化 render 投影:非数组值包装成 text block(裸字符串原样、其他 JSON stringify),并通过 ctx.logger 告警。已含回归测试 render-write-guard.spec.ts(2 个用例;既有 392 个 tools 测试全过;tsc + oxlint 干净)。Agent Note 与 diff 材料已备齐,仓库开放外部 PR 后即可提交。
受影响用户的即时缓解
我做了个 dsh-session-doctor 插件(scan/repair/watch),可检测并无损修复受影响的会话。
All reactions