// 运行状态 —— 跨 Studio 与底部抽屉共享。 import type { ExecEvent } from "./api"; export type RunPhase = "idle" | "submitting" | "streaming" | "done" | "error"; export interface RunEvent { t: number; // 相对开始的毫秒 label: string; } export interface RunState { phase: RunPhase; taskId?: string; output: string; events: RunEvent[]; exec: ExecEvent[]; // 后端回流的节点级执行轨迹(运行·观测) error?: string; } export const emptyRun: RunState = { phase: "idle", output: "", events: [], exec: [] }; // ---- 执行轨迹派生:把扁平 ExecEvent 流归并为按节点聚合的轨迹 ---- export type NodeStatus = "running" | "done" | "error" | "info"; export interface NodeTrace { node: string; kind: string; // memory|tool|prompt|model|plan|section|render|system label: string; status: NodeStatus; ms?: number; detail?: string; notes: string[]; // info 子事件(如检索到的参考资料预览) order: number; } // deriveNodes 把事件流按 node 归并:start→running,end→done(带耗时),error→error,info→点事件/附注。 export function deriveNodes(events: ExecEvent[]): NodeTrace[] { const map = new Map(); let order = 0; for (const e of events) { let n = map.get(e.node); if (!n) { n = { node: e.node, kind: e.kind, label: e.label, status: "info", notes: [], order: order++ }; map.set(e.node, n); } if (e.label) n.label = e.label; n.kind = e.kind; switch (e.phase) { case "start": if (n.status !== "done" && n.status !== "error") n.status = "running"; break; case "end": n.status = "done"; n.ms = e.ms; if (e.detail) n.detail = e.detail; break; case "error": n.status = "error"; n.ms = e.ms; if (e.detail) n.detail = e.detail; break; case "info": if (e.detail) n.notes.push(e.detail); else if (e.label) n.notes.push(e.label); break; } } return [...map.values()].sort((a, b) => a.order - b.order); }