修复 CC 执行过程展示

保留无参数无输出的 MCP 工具过程卡片

让完成回合的执行过程默认展开并支持重开查看

补充前端回归测试并同步工具卡片规范
This commit is contained in:
kdletters
2026-10-06 13:39:02 +08:00
parent 11ae36ea81
commit c54c3f7400
5 changed files with 49 additions and 5 deletions
@@ -204,6 +204,11 @@ describe('前插锚点的标记', () => {
't1:f',
'usage',
]);
expect(
view.container
.querySelector('[data-testid="turn-process"]')
?.hasAttribute('open'),
).toBe(true);
});
});
@@ -76,6 +76,7 @@ function renderTurnProcess(turn: DirectChatTurn, streamingKey: string | null) {
data-testid="turn-process"
data-turn-key={turn.key}
data-block-key={PROCESS_BLOCK_KEY}
open
>
<summary>执行过程</summary>
<div className="message-turn-process-body">{blocks}</div>
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest';
import { projectDirectThreadItem } from './directThreadItemProjection';
describe('DirectProject 工具条目投影', () => {
it('CC 无参数、无输出的工具在开始和完成后都保留卡片', () => {
const started = projectDirectThreadItem(
{
itemType: 'mcpToolCall',
itemId: 'cc-tool-1',
tool: 'mcp__agc__read_project_context',
arguments: '',
output: null,
status: 'inProgress',
at: 1,
},
{ startedAt: 2 },
);
const completed = projectDirectThreadItem(
{
itemType: 'mcpToolCall',
itemId: 'cc-tool-1',
tool: 'mcp__agc__read_project_context',
arguments: '',
output: null,
status: 'completed',
at: 3,
},
{ completedAt: 4 },
);
expect(started?.kind).toBe('tool');
expect(started?.toolCall?.status).toBe('running');
expect(started?.toolCall?.summary).toBe('mcp__agc__read_project_context');
expect(completed?.kind).toBe('tool');
expect(completed?.toolCall?.status).toBe('completed');
});
});
@@ -173,9 +173,9 @@ function buildToolCard(
if (input.command && !input.outputOnly) detail.command = input.command;
if (input.output) detail.output = input.output;
if (changes.length > 0) detail.changes = changes;
// 没有命令 / 输出 / 文件明细的条目不渲染成卡片:一张空卡片对用户没有信息量。
if (!detail.command && !detail.output && !detail.changes?.length) return null;
// CC 的 MCP 工具可能在开始事件和完成事件里都没有可展开正文;工具名、状态和操作数
// 仍然是有效的过程证据,不能因为缺少输入/输出就把整张卡丢掉,否则实时、收口和历史
// 三条路径都会表现成“没有执行过程”。
const summarySource =
(input.kind === 'mcp_tool' ? (input.tool ?? '') : '') ||
detail.command ||
@@ -62,7 +62,7 @@
### 前端合并与渲染(回合唯一归属,连续工具成块)
- 加载对话时按历史条目进入 `project.jsonl` 的原始顺序投影:用户消息、文本与工具块保持原序,连续工具合为一块、遇到文本另起一块,不按 `turnId` 重新归并、也不再有 `seq` 交替。
- 回合完成后,中间文本及所有工具块统一收进默认关闭的“执行过程”;最终回复及失败提示留在外面。展开后仍按原顺序查看中间输出和工具详情;运行中不使用外层折叠区。用户消息的发送时间从消息自身的历史时间读取,不能拿工具起点补造。
- 回合完成后,中间文本及所有工具块统一收进默认展开的“执行过程”;最终回复及失败提示留在外面。用户仍可收起过程块,重开项目后过程默认再次可见;运行中不使用外层折叠区。用户消息的发送时间从消息自身的历史时间读取,不能拿工具起点补造。
- 实时与回读共用同一投影,正文、工具和耗时不另建实时/未归属渲染出口。先在完整历史按消息身份关联,再分页;禁止按第 N 个工具回合匹配第 N 条用户消息。详情通过当前回合 `callId` 关联;同项目回读与实时增量幂等合并,切项目清空旧状态。完整合同见 [AGC 实施计划](./【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md) 的“DirectProject 回合展示唯一归属”。
- 块 DOM 与交互(对齐 Codex):
@@ -105,7 +105,7 @@
- 当前 Agent 与策划 Agent 共用 `packages/shared` 的 `AgentMessageContent` 表现组件:正文为 14px / `--platform-text-strong`,思考、中间输出和工具调用为 12px / `--platform-text-soft`。实时与历史思考共用同一个折叠入口;工具输入输出继承过程色,失败状态保留错误色。Markdown 标题、表格及代码高亮在过程区同步弱化,最终回复和文档预览仍保留正常排版,不按 Agent 类型复制样式。输入提示与禁用状态保持原有反馈。
- Windows 命令展示:仅 `command` 卡片识别 `pwsh` / `powershell`(含完整路径、`.exe`、常见启动选项)的 `-Command` / `-c` 外层包装,摘要和展开输入只展示脚本正文,并解开单个 shell 参数的引用拼接。摘要优先读取已脱敏的 `detail.command`,再按首行 120 字符截断,避免历史摘要被可执行文件路径占满。无法识别的启动方式、`-File`、`-EncodedCommand`、普通命令和 MCP 输入原样展示;执行参数、持久化原文、脱敏和输出均不改变。
- 调试属性:块与行都带 `data-duration-ms`(原始毫秒,无法计算时为空串)与稳定 `data-testid`(块 `agent-tool-call-group`、行 `agent-tool-call-row`)。
- 必须用 `<button aria-expanded>` + `hidden` 控制展开(键盘可达、可读屏),块头与行都是按钮:`aria-label` = 汇总 / 行文案 + 耗时;默认折叠。
- 必须用 `<button aria-expanded>` + `hidden` 控制行级展开(键盘可达、可读屏),块头与行都是按钮:`aria-label` = 汇总 / 行文案 + 耗时;回合完成后的外层过程块默认展开,用户可手动收起。
- 输入框、消息气泡、消息列表滚动模型**不变**;块只是消息流里的一个块。
## 验收判据(每条都要有可复现证据)