| 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<T extends { meta: object }>( |
| 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 } |
| : {}), |
| |
| ...(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<string, unknown>) { |
| 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, |
| }, |
| }); |
| }, |
| }; |
| } |
|
|