Persist active run event history

This commit is contained in:
武阳
2026-05-07 17:40:46 +08:00
parent 847b74ffb8
commit d57570e527
5 changed files with 316 additions and 24 deletions
@@ -64,12 +64,25 @@ export type ClawRunStatus = {
| "interrupted";
current_stage?: string;
started_at?: number;
events?: ClawRunEvent[];
};
type ClawActiveRunStatus = ClawRunStatus & {
status: "queued" | "running";
};
type ClawRunEvent = {
type?: string;
tool_name?: string;
tool_call_id?: string;
arguments?: unknown;
assistant_content?: string;
ok?: boolean;
metadata?: {
output_preview?: unknown;
};
};
type ClawStoredToolCall = {
id?: string;
name?: string;
@@ -471,11 +484,7 @@ export function toReplayRepository(
}
if (isActiveRunStatus(runStatus)) {
const id = `${fallbackSessionId}-run-${runStatus.run_id ?? "active"}`;
const text =
runStatus.status === "queued"
? "当前会话已有任务在执行,本轮正在排队。"
: runStatus.current_stage || "后端正在执行这个会话。";
const parts = [{ type: "reasoning", text }] as UIMessage["parts"];
const parts = buildActiveRunParts(runStatus) as UIMessage["parts"];
const uiMessage: UIMessage = {
id,
role: "assistant",
@@ -520,6 +529,92 @@ function isActiveRunStatus(
return runStatus?.status === "queued" || runStatus?.status === "running";
}
function buildActiveRunParts(runStatus: ClawActiveRunStatus) {
const eventLines = summarizeRunEvents(runStatus);
const fallback =
runStatus.status === "queued"
? "当前会话已有任务在执行,本轮正在排队。"
: runStatus.current_stage || "后端正在执行这个会话。";
const reasoningText = eventLines.length ? eventLines.join("\n") : fallback;
return [
{ type: "reasoning", text: reasoningText },
...buildRunToolParts(runStatus.events ?? []),
];
}
function summarizeRunEvents(runStatus: ClawActiveRunStatus) {
const lines: string[] = [];
for (const event of runStatus.events ?? []) {
if (event.type === "run_queued") {
lines.push("当前会话已有任务在执行,本轮正在排队。");
}
if (event.type === "run_started") {
lines.push("后端已开始执行本轮任务。");
}
if (event.type === "tool_start") {
lines.push(
event.tool_name ? `调用工具 ${event.tool_name}` : "正在调用工具",
);
}
if (event.type === "tool_result") {
lines.push(
event.tool_name ? `工具完成 ${event.tool_name}` : "工具调用完成",
);
}
if (event.type === "final_text_start") {
lines.push("正在整理回复");
}
if (event.type === "user_review_required") {
lines.push("等待用户 review");
}
}
if (runStatus.current_stage && lines.at(-1) !== runStatus.current_stage) {
lines.push(runStatus.current_stage);
}
return lines.slice(-40);
}
function buildRunToolParts(events: readonly ClawRunEvent[]) {
const resultById = new Map<string, ClawRunEvent>();
for (const event of events) {
if (event.type === "tool_result" && event.tool_call_id) {
resultById.set(event.tool_call_id, event);
}
}
const seen = new Set<string>();
return events.flatMap((event) => {
if (
event.type !== "tool_start" ||
!event.tool_call_id ||
!event.tool_name ||
seen.has(event.tool_call_id)
) {
return [];
}
seen.add(event.tool_call_id);
const result = resultById.get(event.tool_call_id);
const outputPreview = result?.metadata?.output_preview;
const output =
typeof outputPreview === "string"
? outputPreview
: result
? result.ok === false
? "工具执行失败,等待最终结果汇总。"
: "工具调用完成,等待最终结果汇总。"
: undefined;
return [
{
type: "dynamic-tool",
toolName: event.tool_name,
toolCallId: event.tool_call_id,
state: output === undefined ? "input-available" : "output-available",
input: attachStageNote(event.arguments ?? {}, event.assistant_content),
...(output === undefined ? {} : { output }),
},
];
});
}
function getReplayMessages(
messages: readonly ClawStoredMessage[],
runStatus?: ClawRunStatus | null,