Agent trace logs are now stored in-memory (pilotRunStore) and broadcast through the existing /ws/crm-updates WebSocket channel. When a client reconnects, it receives a pilot.catchup with all accumulated logs so the user sees agent progress even after page reload. Three new WS event types: pilot.trace, pilot.finished, pilot.catchup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
export type PilotRunLog = { id: string; text: string; at: string };
|
|
|
|
export type PilotRun = {
|
|
status: "running" | "finished" | "error";
|
|
logs: PilotRunLog[];
|
|
startedAt: string;
|
|
finishedAt?: string;
|
|
};
|
|
|
|
const activeRuns = new Map<string, PilotRun>();
|
|
|
|
const MAX_AGE_MS = 10 * 60 * 1000; // 10 minutes
|
|
|
|
function cleanup() {
|
|
const now = Date.now();
|
|
for (const [key, run] of activeRuns) {
|
|
if (now - new Date(run.startedAt).getTime() > MAX_AGE_MS) {
|
|
activeRuns.delete(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function startPilotRun(conversationId: string) {
|
|
cleanup();
|
|
activeRuns.set(conversationId, {
|
|
status: "running",
|
|
logs: [],
|
|
startedAt: new Date().toISOString(),
|
|
});
|
|
}
|
|
|
|
export function addPilotTrace(conversationId: string, text: string) {
|
|
const run = activeRuns.get(conversationId);
|
|
if (!run || run.status !== "running") return;
|
|
run.logs.push({
|
|
id: `${Date.now()}-${Math.floor(Math.random() * 1_000_000)}`,
|
|
text,
|
|
at: new Date().toISOString(),
|
|
});
|
|
}
|
|
|
|
export function finishPilotRun(conversationId: string, status: "finished" | "error" = "finished") {
|
|
const run = activeRuns.get(conversationId);
|
|
if (!run) return;
|
|
run.status = status;
|
|
run.finishedAt = new Date().toISOString();
|
|
// Keep for a short time so late-connecting clients can see it finished
|
|
setTimeout(() => {
|
|
if (activeRuns.get(conversationId) === run) {
|
|
activeRuns.delete(conversationId);
|
|
}
|
|
}, 5000);
|
|
}
|
|
|
|
export function getActivePilotRun(conversationId: string): PilotRun | null {
|
|
const run = activeRuns.get(conversationId);
|
|
if (!run || run.status !== "running") return null;
|
|
return run;
|
|
}
|