工具调用改成「一回合一个折叠块」:结构与锚点

- 新增 ToolCallGroup.tsx:块头一行汇总(按 kind 固定顺序计数拼接),展开态每行一条工具,行可二级展开看命令 / 文件变更 / 输出
- 新增 toolCallGroupPresentation.ts:汇总与行文案纯函数(组件文件不导出非组件值)
- 删除 ToolCallCard.tsx:一回合多卡片的旧形态不再使用
- ProjectSupervisorView:工具调用块插到该回合 assistant 消息之前;实时回合还没有 assistant 消息时先落在消息流末尾
- 汇总 / 行文案按契约固定:已执行 N 个命令、… / 已运行、已编辑、已调用、已搜索、已整理上下文、已执行
- appSurface 用例改为断言折叠块:默认折叠、行数 = 工具数、回读历史插在 assistant 消息之前
- 新增 tool-call-group.suite.ts:汇总文案、行文案、默认折叠、行二级展开、空集合不渲染
- 技术方案文档同步第 4 节:一回合一个折叠块的 DOM 与文案契约
This commit is contained in:
2026-09-15 17:29:36 +08:00
parent a60c04b53d
commit 35a46e9a44
8 changed files with 765 additions and 241 deletions
@@ -48,32 +48,49 @@ toolCalls?: DirectTurnToolCall[] | null;
- 历史文件缺失 → 返回空数组,不报错。
- 单行损坏 → 跳过该行继续,不整体失败(与 Codex item 流一样是"尽力而为"的展示数据,不是业务真相)。
### 4. 前端合并与渲染
### 4. 前端合并与渲染(2026-09 修订:一回合一个折叠块)
- 加载对话时把回读结果按 `turnId` 归并进消息流:工具调用卡插在**同一回合最后一条 assistant 消息之后**,同一回合内按 `startedAt` 升序。
- 实时回合(`directCodexProductRuntime` 且 `activeDirectCodexTurnRef` 命中)时,卡片跟着事件增量更新;回合结束后由持久化数据接管(不出现重复卡片,同一 `id` 只渲染一次)。
- 卡片 DOM 与交互(对齐 Codex):
- 加载对话时把回读结果按 `turnId` 归并进消息流:**同一回合的工具调用收成一个折叠块**,块插在该回合 **assistant 消息之前**(Codex 是「工具在上、答复在下」),同一回合内按 `startedAt` 升序。
- 实时回合(`directCodexProductRuntime` 且 `activeDirectCodexTurnRef` 命中)时,块跟着事件增量更新;回合的 assistant 消息还没落盘时,块落在消息流末尾(下一条 assistant 消息一落盘,块就回到它之前),回合结束后由持久化数据接管(不出现重复块,同一 `id` 每条工具只渲染一行)。
- 块 DOM 与交互(对齐 Codex):
```html
<section class="agent-tool-call" data-kind="command" data-status="running">
<button type="button" class="agent-tool-call-head" aria-expanded="false">
<span class="agent-tool-call-icon" aria-hidden="true"></span>
<span class="agent-tool-call-title">执行命令</span>
<span class="agent-tool-call-summary">npm run build</span>
<span class="agent-tool-call-status">执行中</span>
<svg class="agent-tool-call-chevron" aria-hidden="true"></svg>
<section class="agent-tool-call-group" data-testid="agent-tool-call-group" data-status="completed">
<button type="button" class="agent-tool-call-group-head" aria-expanded="false" aria-controls="…"
aria-label="已执行 2 个命令、1 个文件变更,用时 42秒">
<span class="agent-tool-call-group-icon" aria-hidden="true"></span>
<span class="agent-tool-call-group-summary">已执行 2 个命令、1 个文件变更</span>
<span class="agent-tool-call-group-time">14:20 → 14:21</span>
<span class="agent-tool-call-group-duration">用时 42秒</span>
<svg class="agent-tool-call-group-chevron" aria-hidden="true"></svg>
</button>
<div class="agent-tool-call-body" hidden>
<pre class="agent-tool-call-command">...</pre>
<ul class="agent-tool-call-changes"><li>game/src/x.ts <small>新增</small></li></ul>
<pre class="agent-tool-call-output">...</pre>
<div class="agent-tool-call-group-body" hidden>
<ul class="agent-tool-call-group-rows">
<li class="agent-tool-call-group-row" data-testid="agent-tool-call-row" data-kind="command" data-duration-ms="12300">
<button type="button" class="agent-tool-call-row-head" aria-expanded="false" aria-controls="…"
aria-label="已运行 npm run build,耗时 12.3s">
<span class="agent-tool-call-row-icon" aria-hidden="true"></span>
<span class="agent-tool-call-row-text">已运行 npm run build</span>
<span class="agent-tool-call-row-duration">12.3s</span>
<svg class="agent-tool-call-row-chevron" aria-hidden="true"></svg>
</button>
<div class="agent-tool-call-row-detail" hidden>
<pre class="agent-tool-call-row-command">…</pre>
<ul class="agent-tool-call-row-changes"><li><span>game/src/x.ts</span><small>新增</small></li></ul>
<pre class="agent-tool-call-row-output">…</pre>
</div>
</li>
</ul>
</div>
</section>
```
- 必须用 `<button aria-expanded>` + `hidden` 控制展开(键盘可达、可读屏),`aria-label` 说明「执行命令:npm run build」。
- 默认折叠;`running` 时标题右侧显示进行中状态点;`failed` 时标题与状态文案用错误色(用现有 `--platform-*` 变量,不新增色值)。
- 输入框、消息气泡、消息列表滚动模型**不变**;卡片只是消息流里的一个块。
- 文案规则(按 kind,不允许自由发挥):
- 块头汇总按 kind 计数、顺序固定 `command → file_change → mcp_tool → web_search → context_compaction → other`,标签 `命令`/`文件变更`/`工具调用`/`联网搜索`/`上下文整理`/`其他操作`,形如 `已执行 5 个命令、2 个文件变更`;空集合不渲染块。
- 行文案:`command` → `已运行 {summary}`、`file_change` → `已编辑 {summary}`、`mcp_tool` → `已调用 {summary}`、`web_search` → `已搜索 {summary}`、`context_compaction` → `已整理上下文`、`other` → `已执行 {summary}`;`failed` 行加 `失败` 并用现有 `--platform-*` 错误色。
- 耗时:单条工具 `startedAt` → `updatedAt`,块头 = 该回合所有工具的 `min(startedAt)` → `max(updatedAt)`。`startedAt` 为 0 或 `updatedAt < startedAt` 时不显示耗时(不显示 `0s` / 负数)。
- 必须用 `<button aria-expanded>` + `hidden` 控制展开(键盘可达、可读屏),块头与行都是按钮:`aria-label` = 汇总 / 行文案 + 耗时;默认折叠。
- 输入框、消息气泡、消息列表滚动模型**不变**;块只是消息流里的一个块。
## 验收判据(每条都要有可复现证据)