Skip to content

fix(session): 工具调用结果在实时显示时不再丢失 content#27

Open
KingingWang wants to merge 1 commit into
a9gent:mainfrom
KingingWang:fix/stream-tool-call-content
Open

fix(session): 工具调用结果在实时显示时不再丢失 content#27
KingingWang wants to merge 1 commit into
a9gent:mainfrom
KingingWang:fix/stream-tool-call-content

Conversation

@KingingWang
Copy link
Copy Markdown
Contributor

@KingingWang KingingWang commented May 15, 2026

close #26

现象

通过 ACP 协议接入的 agent(例如 codes / Claude Code 的 ACP 适配)调用 shell、grep、web_fetch、git_log、read_file 这类工具时,前端 SessionViewer 的 ToolCallCard 没有展开按钮,看不到执行结果。对照思考过程卡片(agent_thought_chunk),展开按钮一直是正常的,这让体验很割裂。

代表性截图(简化):

🔧 other  git log -3
🔧 other  git diff 5fee96f
execute shell: cd /workspace && cargo test ...

每行就一个标题,没有 chevron,无法看到 stdout 或错误。

根因

streaming 推送路径上对 ToolCall.Content 做了过滤:server/internal/api/usecase/session.goattachSessionUpdates 对每条 update 都调了 normalizeAgentUpdatePathscompactAgentUpdate,两个函数都会经过 session.PreserveToolCallContent 把不在 {edit, delete, move, ask_user, todo, task} 集合里的工具的 Content 整体置 nil

这导致 execute(shell)、search(grep/glob/ls)、fetch(web_fetch)、other(git_log/MCP)、read(read_file)等工具的结果在到达前端之前就被剥光了

到了前端 web/src/components/stream/ToolCallCard.tsx

const hasResult = !!result;
const hasDetails = hasContent || hasLocations || hasResult;

hasDetails 永远是 false,自然就不渲染 chevron。

需要强调一点:这不是 ACP 协议的要求。官方 ACP 规范的 ToolCallKind 只有 read / edit / delete / move / search / execute / think / fetch / switch_mode / other 这 10 种,没有任何条款规定 client 可以基于 kind 丢弃 content。task / ask_user / todo 是我们自己扩展出来的 kind。把“是否展示 content”这个 UI 决策耦合到 kind 上,会让所有合规的 ACP server 都被迫给 mindfs 做特殊适配,这是不健康的协议交互方式。

修复

streaming 路径不剥 Content,持久化路径保持原样压缩。前端始终能看到 agent 实时发过来的工具输出;落盘体积不受影响。

server/internal/api/usecase/session.go

  1. attachSessionUpdates 中删除 update = compactAgentUpdate(update) 一行
  2. normalizeAgentUpdatePaths 不再按 kind 分支决定是否保留 content,无条件遍历 toolCall.Content 做路径归一化
  3. 删除孤立的 compactAgentUpdate 函数(只此一处调用)

持久化压缩链路不变:Manager.AddExchangeAuxCompactExchangeAuxCompactToolCallserver/internal/session/types.go)继续工作,SQLite/JSON 归档大小不会膨胀。

影响面

  • ✅ 实时 streaming 时,所有 ACP 工具调用的执行结果都能在前端展开查看,体验和 thought_chunk 卡片一致。
  • ✅ 持久化的 aux 数据保持瘦身,行为和现在完全一致。
  • ✅ 对 ACP 协议交互 zero impact,任何 spec-compliant agent(codes、Claude Code、Zed、Gemini ACP 等)都能直接受益,不需要做任何客户端适配。
  • ⚠️ 已知遗留:历史会话(磁盘上已经被 CompactToolCall 压缩过的 aux 条目)再次打开时,展开按钮仍然不会出现。这是一个独立问题——如果要让历史会话也保留 content,需要同时放宽 session/types.goPreserveToolCallContent,但代价是 SQLite/JSON 体积增加(主要是 shell stdout 和 read_file 全文)。本 PR 不涉及,留给后续讨论。

验证

go test ./server/internal/api/usecase

Keep tool call content intact on the realtime update path so the UI can expose results for ACP-compliant tools while leaving persisted aux compaction unchanged.

Constraint: Realtime display needs full tool output, but persisted SQLite/JSON aux data must remain compact.\nRejected: Widening PreserveToolCallContent for persisted history | would increase stored shell/read_file payload size and change historical persistence behavior.\nConfidence: high\nScope-risk: narrow\nDirective: Do not reintroduce kind-based content stripping on streaming updates; keep storage compaction decisions in the persistence layer.\nTested: go test ./server/internal/api/usecase\nNot-tested: Browser UI smoke test for live chevron rendering.
@yandc
Copy link
Copy Markdown
Contributor

yandc commented May 16, 2026

这个是特意这么改的,toolcall 结果比较大,稍微长点的对话就有 10M,远程容易卡顿。

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.

工具调用结果在前端无法展开:streaming 路径上 content 被 kind 过滤剥离

2 participants