feat: broadcast pilot agent traces via WebSocket for live status on reconnect
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>
This commit is contained in:
@@ -6,6 +6,8 @@ import { buildChangeSet, captureSnapshot } from "../utils/changeSet";
|
||||
import { persistAiMessage, runCrmAgentFor, type AgentTraceEvent } from "../agent/crmAgent";
|
||||
import type { PilotContextPayload } from "../agent/crmAgent";
|
||||
import type { ChangeSet } from "../utils/changeSet";
|
||||
import { startPilotRun, addPilotTrace, finishPilotRun } from "../utils/pilotRunStore";
|
||||
import { broadcastToConversation } from "../routes/ws/crm-updates";
|
||||
|
||||
function extractMessageText(message: any): string {
|
||||
if (!message || !Array.isArray(message.parts)) return "";
|
||||
@@ -140,6 +142,7 @@ export default defineEventHandler(async (event) => {
|
||||
execute: async ({ writer }) => {
|
||||
const textId = `text-${Date.now()}`;
|
||||
writer.write({ type: "start" });
|
||||
startPilotRun(auth.conversationId);
|
||||
try {
|
||||
const snapshotBefore = await captureSnapshot(prisma, auth.teamId);
|
||||
|
||||
@@ -163,13 +166,17 @@ export default defineEventHandler(async (event) => {
|
||||
requestId,
|
||||
conversationId: auth.conversationId,
|
||||
onTrace: async (trace: AgentTraceEvent) => {
|
||||
const traceText = humanizeTraceText(trace);
|
||||
const traceAt = new Date().toISOString();
|
||||
writer.write({
|
||||
type: "data-agent-log",
|
||||
data: {
|
||||
requestId,
|
||||
at: new Date().toISOString(),
|
||||
text: humanizeTraceText(trace),
|
||||
},
|
||||
data: { requestId, at: traceAt, text: traceText },
|
||||
});
|
||||
addPilotTrace(auth.conversationId, traceText);
|
||||
broadcastToConversation(auth.conversationId, {
|
||||
type: "pilot.trace",
|
||||
text: traceText,
|
||||
at: traceAt,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -205,6 +212,9 @@ export default defineEventHandler(async (event) => {
|
||||
});
|
||||
}
|
||||
|
||||
finishPilotRun(auth.conversationId, "finished");
|
||||
broadcastToConversation(auth.conversationId, { type: "pilot.finished", at: new Date().toISOString() });
|
||||
|
||||
writer.write({ type: "text-start", id: textId });
|
||||
writer.write({ type: "text-delta", id: textId, delta: reply.text });
|
||||
writer.write({ type: "text-end", id: textId });
|
||||
@@ -224,6 +234,9 @@ export default defineEventHandler(async (event) => {
|
||||
transient: false,
|
||||
});
|
||||
|
||||
finishPilotRun(auth.conversationId, "error");
|
||||
broadcastToConversation(auth.conversationId, { type: "pilot.finished", at: new Date().toISOString() });
|
||||
|
||||
writer.write({
|
||||
type: "data-agent-log",
|
||||
data: {
|
||||
|
||||
Reference in New Issue
Block a user