6a44c778c3
Rectification dropped tool.activity started events and treated length finishes as completed. Share generation settings with consultation, keep the activity line through streaming, and name multi-domain chart calculation. Co-authored-by: Cursor <cursoragent@cursor.com>
81 lines
2.4 KiB
TypeScript
81 lines
2.4 KiB
TypeScript
import type { AgentExecutionReceipt, PublicActivityPhase, WorkflowReceipt } from "./consultation-agent-events.ts";
|
|
|
|
export const ACTIVITY_ELAPSED_VISIBLE_AFTER_MS = 8_000;
|
|
export const ACTIVITY_COMPLETED_TRAIL_LIMIT = 3;
|
|
|
|
export type AgentActivityView = Readonly<{
|
|
phase: PublicActivityPhase;
|
|
label: string;
|
|
startedAt?: number;
|
|
completedTrail?: string;
|
|
}>;
|
|
|
|
export function activityCompletedTrail(steps: readonly string[]): string | undefined {
|
|
if (steps.length === 0) return undefined;
|
|
return `已完成:${steps.slice(-ACTIVITY_COMPLETED_TRAIL_LIMIT).join(" · ")}`;
|
|
}
|
|
|
|
export function nextActivityView(
|
|
previous: AgentActivityView | undefined,
|
|
next: Omit<AgentActivityView, "startedAt">,
|
|
now = Date.now(),
|
|
): AgentActivityView {
|
|
const startedAt = previous?.label === next.label && previous.startedAt ? previous.startedAt : now;
|
|
const completedTrail = next.phase === "answer-composition"
|
|
? undefined
|
|
: next.completedTrail !== undefined
|
|
? next.completedTrail || undefined
|
|
: previous?.completedTrail;
|
|
return {
|
|
phase: next.phase,
|
|
label: next.label,
|
|
startedAt,
|
|
...(completedTrail ? { completedTrail } : {}),
|
|
};
|
|
}
|
|
|
|
export function activityElapsedLabel(startedAt: number, now: number): string | null {
|
|
const elapsedMs = now - startedAt;
|
|
if (elapsedMs < ACTIVITY_ELAPSED_VISIBLE_AFTER_MS) return null;
|
|
return `已用时 ${Math.floor(elapsedMs / 1000)} 秒`;
|
|
}
|
|
|
|
export type ChatMessage = {
|
|
readonly role: "user" | "assistant";
|
|
readonly text: string;
|
|
readonly techniqueTruth?: string;
|
|
readonly agentExecutionReceipt?: AgentExecutionReceipt;
|
|
readonly workflowReceipt?: WorkflowReceipt;
|
|
};
|
|
|
|
export type ChatMessageView = ChatMessage & {
|
|
readonly renderKey: string;
|
|
readonly state: "settled" | "streaming" | "thinking";
|
|
readonly activity?: AgentActivityView;
|
|
};
|
|
|
|
export function chatMessageViews(
|
|
messages: readonly ChatMessage[],
|
|
loading: boolean,
|
|
streamingText: string,
|
|
activity?: AgentActivityView,
|
|
): readonly ChatMessageView[] {
|
|
const settled = messages.map((message, index) => ({
|
|
...message,
|
|
renderKey: `message-${index}`,
|
|
state: "settled" as const,
|
|
}));
|
|
if (!loading || messages.at(-1)?.role === "assistant") return settled;
|
|
|
|
return [
|
|
...settled,
|
|
{
|
|
role: "assistant",
|
|
text: streamingText,
|
|
renderKey: `message-${messages.length}`,
|
|
state: streamingText ? "streaming" : "thinking",
|
|
activity,
|
|
},
|
|
];
|
|
}
|