import { classifyGatewayStaleInstall } from "../../gateway/stale-install.js"; import { emitAgentEvent } from "../../infra/agent-events.js"; import { formatErrorMessageForDisplay } from "../../infra/error-diagnostics.js"; import { formatErrorMessage } from "../../infra/errors.js"; import { createSubsystemLogger } from "../../logging/subsystem.js"; import { normalizeAgentRunTerminalDeliverySnapshot } from "../agent-run-terminal-delivery.js"; import type { AgentRunTerminalOutcome } from "../agent-run-terminal-outcome.js"; import { normalizeAgentRunTerminalReceipt } from "../agent-run-terminal-receipt.js"; import type { EmbeddedAgentRunEntryTerminal } from "../embedded-agent-runner/run-entry.js"; import { getFailoverErrorCode } from "../failover/error.js"; import { renderFailoverCodeUserCopy } from "../failover/user-copy.js"; import { AGENT_RUN_SUPERSEDED_STOP_REASON, resolveAgentRunAbortLifecycleFields, resolveAgentRunErrorLifecycleFields, } from "../run-termination.js"; import type { AgentAttemptLifecycleState } from "./attempt-callbacks.js"; import type { AgentAttemptResult } from "./runtime-loaders.js"; const log = createSubsystemLogger("agents/agent-command"); const formatLifecycleError = (error: unknown): string => { const staleInstall = classifyGatewayStaleInstall(error); return staleInstall ? staleInstall.error.message : formatErrorMessageForDisplay(error, renderFailoverCodeUserCopy(getFailoverErrorCode(error))); }; function resolveTerminalLogLevel( outcome: AgentRunTerminalOutcome, ): "info" | "warn" | "error" | undefined { if (!outcome.stopReason || outcome.stopReason === "end_turn") { return undefined; } if (outcome.reason === "completed") { return "info"; } return outcome.status === "timeout" ? "warn" : "error"; } export function applyAgentRunAbortMetadata( result: T, signal: AbortSignal | undefined, ): T { const abortFields = resolveAgentRunAbortLifecycleFields(signal); if (abortFields.aborted !== true) { return result; } return { ...result, meta: { ...result.meta, ...abortFields, }, }; } export function createAgentCommandLifecycle(params: { runId: string; lifecycleGeneration: () => string; startedAt: number; abortSignal?: AbortSignal; state: AgentAttemptLifecycleState; }) { let lifecycleFinishingEmitted = false; const resolveResultError = (runResult: AgentAttemptResult, includeErrorPayload: boolean) => params.state.lifecycleError ?? (includeErrorPayload ? runResult.payloads?.find( (payload) => payload.isError === true && typeof payload.text === "string", )?.text : undefined) ?? (runResult.meta.error ? runResult.meta.error.message.trim() || "Agent run failed" : undefined); const resolveTerminalError = ( runResult: AgentAttemptResult, fallbackExhausted: boolean, terminal: EmbeddedAgentRunEntryTerminal, ) => params.state.lifecycleError ?? (terminal.outcome.status === "timeout" ? terminal.outcome.error : resolveResultError(runResult, fallbackExhausted)) ?? (fallbackExhausted ? "All model fallback candidates failed" : "Agent run failed"); const emitTerminalPhase = ( phase: "finishing" | "end" | "error", terminal: EmbeddedAgentRunEntryTerminal, error = terminal.outcome.status === "timeout" ? terminal.outcome.error : undefined, ) => { const { aborted, yielded, replayInvalid, terminalReply } = terminal.metadata; const terminalDelivery = normalizeAgentRunTerminalDeliverySnapshot( terminal.metadata.terminalDelivery, ); const terminalReceipt = normalizeAgentRunTerminalReceipt(terminal.metadata.terminalReceipt); const { stopReason, livenessState, timeoutPhase, providerStarted } = terminal.outcome; const abortFields = resolveAgentRunAbortLifecycleFields(params.abortSignal); emitAgentEvent({ runId: params.runId, lifecycleGeneration: params.lifecycleGeneration(), stream: "lifecycle", data: { phase, startedAt: params.startedAt, endedAt: Date.now(), aborted: typeof aborted === "boolean" ? aborted : false, stopReason, ...(yielded === true ? { yielded } : {}), ...(replayInvalid === true ? { replayInvalid } : {}), ...(livenessState ? { livenessState } : {}), ...(timeoutPhase ? { timeoutPhase } : {}), ...(providerStarted !== undefined ? { providerStarted } : {}), ...(error ? { error: formatErrorMessage(error) } : {}), ...(error && params.state.lifecycleErrorObservation ? { errorObservation: params.state.lifecycleErrorObservation } : {}), // Finishing is an attempt fence, not the outer execution's final publication. ...(phase !== "finishing" ? { executionSettled: true } : {}), ...(terminalDelivery ? { terminalDelivery } : {}), ...(terminalReceipt ? { terminalReceipt } : {}), ...(terminalReply ? { terminalReply } : {}), ...(stopReason === AGENT_RUN_SUPERSEDED_STOP_REASON ? { aborted: true, stopReason } : abortFields), }, }); }; return { emitBasicError(error: unknown, extraData?: Record) { if (params.state.lifecycleEnded) { return; } params.state.lifecycleEnded = true; emitAgentEvent({ runId: params.runId, lifecycleGeneration: params.lifecycleGeneration(), stream: "lifecycle", data: { phase: "error", startedAt: params.startedAt, endedAt: Date.now(), error: formatLifecycleError(error), ...(params.state.lifecycleErrorObservation ? { errorObservation: params.state.lifecycleErrorObservation } : {}), ...extraData, executionSettled: true, }, }); }, emitFinishing(terminal: EmbeddedAgentRunEntryTerminal) { if ( params.state.lifecycleEnded || params.state.lifecycleFinishing || lifecycleFinishingEmitted ) { return; } lifecycleFinishingEmitted = true; params.state.lifecycleFinishing = true; emitTerminalPhase("finishing", terminal); }, emitEnd(terminal: EmbeddedAgentRunEntryTerminal) { if (params.state.lifecycleEnded) { return; } params.state.lifecycleEnded = true; const stopReason = terminal.outcome.stopReason; const logLevel = resolveTerminalLogLevel(terminal.outcome); if (logLevel) { log[logLevel](`[agent] run ${params.runId} ended with stopReason=${stopReason}`); } emitTerminalPhase("end", terminal); }, resolveResultError, resolveTerminalError, emitResultError( runResult: AgentAttemptResult, fallbackExhausted: boolean, terminal: EmbeddedAgentRunEntryTerminal, ) { if (params.state.lifecycleEnded) { return; } params.state.lifecycleEnded = true; const error = resolveTerminalError(runResult, fallbackExhausted, terminal); emitTerminalPhase("error", terminal, error); }, emitPostTurnError(error: unknown, terminal: EmbeddedAgentRunEntryTerminal) { if (params.state.lifecycleEnded) { return; } params.state.lifecycleEnded = true; const terminalDelivery = normalizeAgentRunTerminalDeliverySnapshot( terminal.metadata.terminalDelivery, ); emitAgentEvent({ runId: params.runId, lifecycleGeneration: params.lifecycleGeneration(), stream: "lifecycle", data: { phase: "error", startedAt: params.startedAt, endedAt: Date.now(), error: formatLifecycleError(error), ...(terminalDelivery ? { terminalDelivery } : {}), ...resolveAgentRunErrorLifecycleFields(error, params.abortSignal), executionSettled: true, }, }); }, }; }