diff --git a/AGENTS.md b/AGENTS.md
index 5ec1ef0..47f7817 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,6 +1,6 @@
# Repository Guidelines (Code-Verified)
-> Last verified against current codebase: 2026-06-21
+> Last verified against current codebase: 2026-06-25
> Scope: `/Users/jojo/Desktop/agents/正在修复中/agents`
这份文档基于当前仓库实际代码重写。若与未来代码冲突,以代码为准并及时更新本文档。
@@ -183,6 +183,14 @@
8. **窗口设最大尺寸 + 内部滚动**:所有弹窗 / 面板 / 列表设置 `max-height` / `max-width`,超出由内部容器滚动,不顶大整个窗口;滚动条要么隐藏,要么做样式适配,不暴露原生粗滚动条。
9. **实体面板禁止半透明**:实体 UI 容器(对话区 / 侧栏 / 下拉/二级菜单 / 抽屉 / 对话框本体 / 状态条 / tooltip / 输入栏)背景必须不透明,且不加 `backdrop-filter` 磨砂。唯一例外:遮罩层 scrim(`--overlay-scrim`,`position:fixed;inset:0`)和刻意玻璃质感装饰保留半透明。
+## 5.6) 工具结果显示规范(强制)
+
+> 适用范围:所有使用 `tool-result-meta` / `tool-result-content` 结构渲染的工具结果(如 `static/src/components/chat/actions/ToolAction.vue`、`toolRenderers.ts` 等)。
+> 约束级别:新增或修改工具结果渲染时**必须遵守**。
+
+1. **meta 部分只放参数**:`tool-result-meta` 仅用于展示工具调用的参数、配置、状态标识等元信息。例如:状态、ID、路径、任务描述、超时时间等。禁止在 meta 区域放置执行结果、统计摘要、输出内容等。
+2. **content 部分只放结果**:`tool-result-content` 仅用于展示工具执行后的结果、输出、统计、总结等。例如:文件内容、命令输出、搜索命中、执行统计(工作时间 / 调用次数 / 工具次数)、最终回复摘要等。禁止在 content 区域重复展示工具参数。
+
## 6) Git 工作流(开发 + Review)
### 6.1 核心原则
diff --git a/static/src/components/chat/actions/ToolAction.vue b/static/src/components/chat/actions/ToolAction.vue
index 532c658..c364abc 100644
--- a/static/src/components/chat/actions/ToolAction.vue
+++ b/static/src/components/chat/actions/ToolAction.vue
@@ -89,6 +89,22 @@ function formatBytes(bytes: number): string {
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
}
+function formatDuration(seconds: number): string {
+ if (seconds <= 0) return '0 秒';
+ if (seconds < 60) return `${seconds} 秒`;
+ const minutes = Math.floor(seconds / 60);
+ const remainingSeconds = seconds % 60;
+ if (minutes < 60) {
+ return remainingSeconds > 0 ? `${minutes} 分 ${remainingSeconds} 秒` : `${minutes} 分`;
+ }
+ const hours = Math.floor(minutes / 60);
+ const remainingMinutes = minutes % 60;
+ if (remainingMinutes === 0 && remainingSeconds === 0) {
+ return `${hours} 小时`;
+ }
+ return `${hours} 小时 ${remainingMinutes} 分 ${remainingSeconds} 秒`;
+}
+
function formatToolStatusLabel(result: any, successLabel: string, failedLabel = '✗ 失败'): string {
const state = String(result?.status || props.action?.tool?.status || '').toLowerCase();
if (state === 'awaiting_user_answer') {
@@ -1099,6 +1115,18 @@ function renderCreateSubAgent(result: any, args: any): string {
const taskDescription = args.task ?? '';
const message = result.message ?? result.summary ?? '';
+ const stats = result.stats || {};
+ const runtimeSeconds = result.runtime_seconds ?? result.elapsed_seconds ?? stats.runtime_seconds ?? 0;
+ const apiCalls = stats.api_calls ?? stats.turn_count ?? 0;
+ const toolCount =
+ (stats.files_read || 0) +
+ (stats.write_files || 0) +
+ (stats.edit_files || 0) +
+ (stats.searches || 0) +
+ (stats.web_pages || 0) +
+ (stats.commands || 0);
+ const hasStats = runtimeSeconds > 0 || apiCalls > 0 || toolCount > 0;
+
let html = '
';
- if (message) {
- html += '';
- html += `
${escapeHtml(String(message))}
`;
+ if (message || hasStats) {
+ html += '
';
+ if (hasStats) {
+ html += '
执行统计
';
+ if (runtimeSeconds > 0) {
+ html += `
工作时间:${formatDuration(runtimeSeconds)}
`;
+ }
+ if (apiCalls > 0) {
+ html += `
调用次数:${apiCalls} 次
`;
+ }
+ if (toolCount > 0) {
+ html += `
工具次数:${toolCount} 次
`;
+ }
+ }
+ if (message) {
+ if (hasStats) {
+ html += '
最终回复
';
+ }
+ html += `
${escapeHtml(String(message))}
`;
+ }
html += '
';
}
@@ -1143,7 +1188,7 @@ function renderTerminateSubAgent(result: any, args: any): string {
html += '
';
if (message) {
- html += '';
+ html += '
';
html += `
${escapeHtml(String(message))}
`;
html += '
';
}
@@ -1196,7 +1241,7 @@ function renderGetSubAgentStatus(result: any, args: any): string {
html += '
';
if (summary) {
- html += '';
+ html += '
';
html += `
${escapeHtml(String(summary))}
`;
html += '
';
}
diff --git a/static/src/components/chat/actions/toolRenderers.ts b/static/src/components/chat/actions/toolRenderers.ts
index 22b6b06..d20f3e6 100644
--- a/static/src/components/chat/actions/toolRenderers.ts
+++ b/static/src/components/chat/actions/toolRenderers.ts
@@ -14,6 +14,22 @@ export function formatBytes(bytes: number): string {
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
}
+function formatDuration(seconds: number): string {
+ if (seconds <= 0) return '0 秒';
+ if (seconds < 60) return `${seconds} 秒`;
+ const minutes = Math.floor(seconds / 60);
+ const remainingSeconds = seconds % 60;
+ if (minutes < 60) {
+ return remainingSeconds > 0 ? `${minutes} 分 ${remainingSeconds} 秒` : `${minutes} 分`;
+ }
+ const hours = Math.floor(minutes / 60);
+ const remainingMinutes = minutes % 60;
+ if (remainingMinutes === 0 && remainingSeconds === 0) {
+ return `${hours} 小时`;
+ }
+ return `${hours} 小时 ${remainingMinutes} 分 ${remainingSeconds} 秒`;
+}
+
function formatToolStatusLabel(result: any, successLabel: string, failedLabel = '✗ 失败'): string {
const state = String(result?.status || '').toLowerCase();
if (state === 'awaiting_user_answer') {
@@ -1067,6 +1083,18 @@ function renderCreateSubAgent(result: any, args: any): string {
const taskDescription = args.task ?? '';
const message = result.message ?? result.summary ?? '';
+ const stats = result.stats || {};
+ const runtimeSeconds = result.runtime_seconds ?? result.elapsed_seconds ?? stats.runtime_seconds ?? 0;
+ const apiCalls = stats.api_calls ?? stats.turn_count ?? 0;
+ const toolCount =
+ (stats.files_read || 0) +
+ (stats.write_files || 0) +
+ (stats.edit_files || 0) +
+ (stats.searches || 0) +
+ (stats.web_pages || 0) +
+ (stats.commands || 0);
+ const hasStats = runtimeSeconds > 0 || apiCalls > 0 || toolCount > 0;
+
let html = '
';
- if (message) {
- html += '
';
- html += `
${escapeHtml(String(message))}
`;
+ if (message || hasStats) {
+ html += '
';
+ if (hasStats) {
+ html += '
执行统计
';
+ if (runtimeSeconds > 0) {
+ html += `
工作时间:${formatDuration(runtimeSeconds)}
`;
+ }
+ if (apiCalls > 0) {
+ html += `
调用次数:${apiCalls} 次
`;
+ }
+ if (toolCount > 0) {
+ html += `
工具次数:${toolCount} 次
`;
+ }
+ }
+ if (message) {
+ if (hasStats) {
+ html += '
最终回复
';
+ }
+ html += `
${escapeHtml(String(message))}
`;
+ }
html += '
';
}
@@ -1111,7 +1156,7 @@ function renderTerminateSubAgent(result: any, args: any): string {
html += '
';
if (message) {
- html += '
';
+ html += '
';
html += `
${escapeHtml(String(message))}
`;
html += '
';
}
@@ -1164,7 +1209,7 @@ function renderGetSubAgentStatus(result: any, args: any): string {
html += '
';
if (summary) {
- html += '
';
+ html += '
';
html += `
${escapeHtml(String(summary))}
`;
html += '
';
}