import type { AgentState } from "@/features/agents/state/store"; import type { TranscriptAppendMeta } from "@/features/agents/state/transcript"; import type { ChatEventPayload } from "@/features/agents/state/runtimeEventBridge"; import { decideRuntimeChatEvent, type RuntimePolicyIntent } from "@/features/agents/state/runtimeEventPolicy"; import { deriveChatTerminalDecision, type ChatTerminalDecision, type RuntimeTerminalState, } from "@/features/agents/state/runtimeTerminalWorkflow"; import { formatMetaMarkdown, formatThinkingMarkdown, isUiMetadataPrefix, } from "@/lib/text/message-extract"; export type RuntimeChatWorkflowCommand = | { kind: "applyChatTerminalDecision"; decision: ChatTerminalDecision } | { kind: "applyPolicyIntents"; intents: RuntimePolicyIntent[] } | { kind: "appendOutput"; line: string; transcript: TranscriptAppendMeta } | { kind: "appendToolLines"; lines: string[]; timestampMs: number } | { kind: "applyTerminalCommit"; runId: string; seq: number | null } | { kind: "appendAbortedIfNotSuppressed"; timestampMs: number } | { kind: "logMetric"; metric: string; meta: Record } | { kind: "markThinkingDebugSession"; sessionKey: string } | { kind: "logWarn"; message: string; meta?: unknown }; export type RuntimeChatWorkflowInput = { payload: ChatEventPayload; agentId: string; agent: AgentState | undefined; activeRunId: string | null; runtimeTerminalState: RuntimeTerminalState; role: unknown; nowMs: number; nextTextRaw: string | null; nextText: string | null; nextThinking: string | null; toolLines: string[]; isToolRole: boolean; assistantCompletionAt: number | null; finalAssistantText: string | null; hasThinkingStarted: boolean; hasTraceInOutput: boolean; isThinkingDebugSessionSeen: boolean; thinkingStartedAtMs: number | null; }; export type RuntimeChatWorkflowResult = { commands: RuntimeChatWorkflowCommand[]; }; const terminalAssistantMetaEntryId = (runId?: string | null) => { const key = runId?.trim() ?? ""; return key ? `run:${key}:assistant:meta` : undefined; }; const terminalAssistantFinalEntryId = (runId?: string | null) => { const key = runId?.trim() ?? ""; return key ? `run:${key}:assistant:final` : undefined; }; const resolveTerminalSeq = (payload: ChatEventPayload): number | null => { const seq = payload.seq; if (typeof seq !== "number" || !Number.isFinite(seq)) return null; return seq; }; const summarizeThinkingMessage = (message: unknown) => { if (!message || typeof message !== "object") { return { type: typeof message }; } const record = message as Record; const summary: Record = { keys: Object.keys(record) }; const content = record.content; if (Array.isArray(content)) { summary.contentTypes = content.map((item) => { if (item && typeof item === "object") { const entry = item as Record; return typeof entry.type === "string" ? entry.type : "object"; } return typeof item; }); } else if (typeof content === "string") { summary.contentLength = content.length; } if (typeof record.text === "string") { summary.textLength = record.text.length; } for (const key of ["analysis", "reasoning", "thinking"]) { const value = record[key]; if (typeof value === "string") { summary[`${key}Length`] = value.length; } else if (value && typeof value === "object") { summary[`${key}Keys`] = Object.keys(value as Record); } } return summary; }; export const planRuntimeChatEvent = ( input: RuntimeChatWorkflowInput ): RuntimeChatWorkflowResult => { const commands: RuntimeChatWorkflowCommand[] = []; const { payload, agentId, agent, activeRunId, runtimeTerminalState, role, nowMs, nextTextRaw, nextText, nextThinking, toolLines, isToolRole, assistantCompletionAt, finalAssistantText, hasThinkingStarted, hasTraceInOutput, isThinkingDebugSessionSeen, thinkingStartedAtMs, } = input; if (payload.state === "delta") { if (typeof nextTextRaw === "string" && isUiMetadataPrefix(nextTextRaw.trim())) { return { commands }; } const deltaIntents = decideRuntimeChatEvent({ agentId, state: payload.state, runId: payload.runId ?? null, role, activeRunId, agentStatus: agent?.status ?? "idle", now: nowMs, agentRunStartedAt: agent?.runStartedAt ?? null, nextThinking, nextText, hasThinkingStarted, isClosedRun: false, isStaleTerminal: false, shouldRequestHistoryRefresh: false, shouldUpdateLastResult: false, shouldSetRunIdle: false, shouldSetRunError: false, lastResultText: null, assistantCompletionAt: null, shouldQueueLatestUpdate: false, latestUpdateMessage: null, }); const hasOnlyDeltaCleanup = deltaIntents.length > 0 && deltaIntents.every((intent) => intent.kind === "clearRunTracking"); if (hasOnlyDeltaCleanup) { commands.push({ kind: "applyPolicyIntents", intents: deltaIntents }); return { commands }; } if (deltaIntents.some((intent) => intent.kind === "ignore")) { return { commands }; } commands.push({ kind: "applyPolicyIntents", intents: deltaIntents }); if (toolLines.length > 0) { commands.push({ kind: "appendToolLines", lines: toolLines, timestampMs: nowMs, }); } return { commands }; } const shouldRequestHistoryRefresh = payload.state === "final" && !nextThinking && role === "assistant" && Boolean(agent) && !hasTraceInOutput; const shouldUpdateLastResult = payload.state === "final" && !isToolRole && typeof finalAssistantText === "string"; const shouldQueueLatestUpdate = payload.state === "final" && Boolean(agent?.lastUserMessage && !agent.latestOverride); const terminalSeq = payload.state === "final" ? resolveTerminalSeq(payload) : null; const chatTerminalDecision = payload.state === "final" ? deriveChatTerminalDecision({ state: runtimeTerminalState, runId: payload.runId, isFinal: true, seq: terminalSeq, }) : null; if (chatTerminalDecision) { commands.push({ kind: "applyChatTerminalDecision", decision: chatTerminalDecision, }); } if (payload.state === "final" && payload.runId && chatTerminalDecision?.isStaleTerminal) { commands.push({ kind: "logMetric", metric: "stale_terminal_chat_event_ignored", meta: { runId: payload.runId, seq: terminalSeq, lastTerminalSeq: chatTerminalDecision.lastTerminalSeqBeforeFinal, commitSource: chatTerminalDecision.commitSourceBeforeFinal, }, }); } const chatIntents = decideRuntimeChatEvent({ agentId, state: payload.state, runId: payload.runId ?? null, role, activeRunId, agentStatus: agent?.status ?? "idle", now: nowMs, agentRunStartedAt: agent?.runStartedAt ?? null, nextThinking, nextText, hasThinkingStarted, isClosedRun: false, isStaleTerminal: chatTerminalDecision?.isStaleTerminal ?? false, shouldRequestHistoryRefresh, shouldUpdateLastResult, shouldSetRunIdle: Boolean(payload.runId && agent?.runId === payload.runId && payload.state !== "error"), shouldSetRunError: Boolean(payload.runId && agent?.runId === payload.runId && payload.state === "error"), lastResultText: shouldUpdateLastResult ? finalAssistantText : null, assistantCompletionAt: payload.state === "final" ? assistantCompletionAt : null, shouldQueueLatestUpdate, latestUpdateMessage: shouldQueueLatestUpdate ? (agent?.lastUserMessage ?? null) : null, }); const hasOnlyRunCleanup = chatIntents.length > 0 && chatIntents.every((intent) => intent.kind === "clearRunTracking"); if (hasOnlyRunCleanup) { commands.push({ kind: "applyPolicyIntents", intents: chatIntents }); return { commands }; } if (chatIntents.some((intent) => intent.kind === "ignore")) { return { commands }; } if (payload.state === "final") { if (payload.runId && chatTerminalDecision?.fallbackCommittedBeforeFinal && role === "assistant" && !isToolRole) { commands.push({ kind: "logMetric", metric: "lifecycle_fallback_replaced_by_chat_final", meta: { runId: payload.runId, seq: terminalSeq, lastTerminalSeq: chatTerminalDecision.lastTerminalSeqBeforeFinal ?? null, }, }); } if (!nextThinking && role === "assistant" && !isThinkingDebugSessionSeen) { commands.push({ kind: "markThinkingDebugSession", sessionKey: payload.sessionKey, }); commands.push({ kind: "logWarn", message: "No thinking trace extracted from chat event.", meta: { sessionKey: payload.sessionKey, message: summarizeThinkingMessage(payload.message ?? payload), }, }); } const thinkingText = nextThinking ?? agent?.thinkingTrace ?? null; const thinkingLine = thinkingText ? formatThinkingMarkdown(thinkingText) : ""; if (role === "assistant" && typeof assistantCompletionAt === "number") { const thinkingDurationMs = typeof thinkingStartedAtMs === "number" ? Math.max(0, assistantCompletionAt - thinkingStartedAtMs) : null; commands.push({ kind: "appendOutput", line: formatMetaMarkdown({ role: "assistant", timestamp: assistantCompletionAt, thinkingDurationMs, }), transcript: { source: "runtime-chat", runId: payload.runId ?? null, sessionKey: payload.sessionKey, timestampMs: assistantCompletionAt, role: "assistant", kind: "meta", entryId: terminalAssistantMetaEntryId(payload.runId ?? null), confirmed: true, }, }); } if (thinkingLine) { commands.push({ kind: "appendOutput", line: thinkingLine, transcript: { source: "runtime-chat", runId: payload.runId ?? null, sessionKey: payload.sessionKey, timestampMs: assistantCompletionAt ?? nowMs, role: "assistant", kind: "thinking", }, }); } if (toolLines.length > 0) { commands.push({ kind: "appendToolLines", lines: toolLines, timestampMs: assistantCompletionAt ?? nowMs, }); } if (!isToolRole && typeof finalAssistantText === "string") { commands.push({ kind: "appendOutput", line: finalAssistantText, transcript: { source: "runtime-chat", runId: payload.runId ?? null, sessionKey: payload.sessionKey, timestampMs: assistantCompletionAt ?? nowMs, role: "assistant", kind: "assistant", entryId: terminalAssistantFinalEntryId(payload.runId ?? null), confirmed: true, }, }); } if (payload.runId) { commands.push({ kind: "applyTerminalCommit", runId: payload.runId, seq: terminalSeq, }); } commands.push({ kind: "applyPolicyIntents", intents: chatIntents }); return { commands }; } if (payload.state === "aborted") { commands.push({ kind: "appendAbortedIfNotSuppressed", timestampMs: nowMs, }); commands.push({ kind: "applyPolicyIntents", intents: chatIntents }); return { commands }; } if (payload.state === "error") { commands.push({ kind: "appendOutput", line: payload.errorMessage ? `Error: ${payload.errorMessage}` : "Run error.", transcript: { source: "runtime-chat", runId: payload.runId ?? null, sessionKey: payload.sessionKey, timestampMs: nowMs, role: "assistant", kind: "assistant", }, }); commands.push({ kind: "applyPolicyIntents", intents: chatIntents }); } return { commands }; };