fix(web): 智能体汇报展示层兜底与进度口径(FIX-7)

- 汇报正文里的 UUID、能力 ID、原因码展示时换成名称和人话,原文收进「正文原文」,历史不改
- 步骤进度统一 stepProgress:分子只数完成,跳过单独计(「4/6 · 跳过 2」)
- 进度行不再把失败的调用算进动作计数(3 次被拒的派发曾写成「派出了 5 个子会话」);
  实际结果行按会话树显示派出的子会话数
- 会话标题里的 ISO 时间与 download_failed 等码转人话

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-30 00:00:03 +08:00
1 parent 1b22bb5131
commit 417af9f253
9 files changed
+393 -27

No files matched your search

@@ -62,14 +62,15 @@ const joinNouns = (xs: string[]) =>
/**
* 「查了 1 个站点、2 个下载任务和媒体库,搜了 3 次资源」:按能力元数据的 verb / noun 计数,
* 同一动作的对象合并,动作按第一次出现排序;被拒绝和隐藏的调用不计入。超过 4 种动作只写前 3 种。
* 同一动作的对象合并,动作按第一次出现排序;被拒绝、失败和隐藏的调用不计入(失败的在进度行另计「N 项失败」,
* 否则 3 次被预算拒绝的派发 + 2 次成功会写成「派出了 5 个子会话」)。超过 4 种动作只写前 3 种。
*/
export function activitySummary(calls: Pick<ToolCallView, "capabilityId" | "status" | "outputSummary">[]): string {
const groups: Group[] = [];
for (const c of calls) {
const a = activityOf(c.capabilityId);
if (a.hidden) continue;
if (c.status === "denied" || authDenial(c)) continue;
if (c.status === "denied" || c.status === "failed" || authDenial(c)) continue;
let g = groups.find((x) => x.verb === a.verb);
if (!g) {
g = { verb: a.verb, nouns: [] };
@@ -85,6 +86,34 @@ export function activitySummary(calls: Pick<ToolCallView, "capabilityId" | "stat
return groups.map(text).join(",");
}
// ── 步骤进度 ──
type StepLike = { status: "pending" | "running" | "done" | "skipped" | "failed"; title: string };
/**
* 步骤进度(概况条与展开的步骤清单共用一个口径):分子只数完成的步骤,跳过的单独计。
* 「4/6 · 跳过 2」;全部结束且有跳过时状态写「完成 4 · 跳过 2」,不再写「全部完成」。
* pct 按已结束(完成 + 跳过 + 失败)算进度条宽度。
*/
export function stepProgress(steps: readonly StepLike[]) {
const done = steps.filter((s) => s.status === "done").length;
const skipped = steps.filter((s) => s.status === "skipped").length;
const failedStep = steps.find((s) => s.status === "failed") ?? null;
const failed = steps.filter((s) => s.status === "failed").length;
const total = steps.length;
const current = steps.find((s) => s.status === "running") ?? steps.find((s) => s.status === "pending") ?? null;
const count = `${done}/${total}${skipped ? ` · 跳过 ${skipped}` : ""}`;
const status = failedStep
? `失败:${failedStep.title}`
: current
? `当前:${current.title}`
: skipped
? `完成 ${done} · 跳过 ${skipped}`
: "全部完成";
const pct = total ? Math.round(((done + skipped + failed) / total) * 100) : 0;
return { done, skipped, failed, total, current, failedStep, count, status, pct };
}
/** 正在进行的那一步:「正在检测站点」 */
export function runningText(call: Pick<ToolCallView, "capabilityId">): string {
const a = activityOf(call.capabilityId);