import type { AgentRunStore } from "./store.js"; import type { CommandRecord, FailureKind, JsonRecord, JsonValue, RunEvent, RunRecord, RunnerJobRecord, TerminalStatus } from "../common/types.js"; import { boundedTextSummary, outputBytesFromPayload, outputTruncatedFromPayload } from "../common/output.js"; const maxToolCallSummaryItems = 40; const toolCallCommandLimitChars = 600; const toolCallFieldLimitChars = 200; const RESULT_EVENT_PAGE_LIMIT = 500; const RESULT_EVENT_MAX_PAGES = 200; interface ResultEventPage { events: RunEvent[]; capped: boolean; nextAfterSeq: number | null; } interface AssistantReplySummary { text: string; seq: number | null; source: string | null; final: boolean; replyAuthority: boolean; textTruncated: boolean; outputTruncated: boolean; } interface TerminalClassificationInput { terminal: TerminalStatus | null; terminalSource: string; failureKind: FailureKind | null; failureMessage: string | null; timeoutBudget: JsonRecord; transportDisconnect: RunEvent | null; lastActivity: JsonRecord | null; command: CommandRecord | null; } export async function buildRunResult(store: AgentRunStore, runId: string, commandId?: string): Promise { const run = await store.getRun(runId); const command = await selectCommand(store, runId, commandId); const eventPage = await listResultEvents(store, runId); const events = eventPage.events; const scopedEvents = command ? eventsForCommand(events, command.id) : events; const jobs = await store.listRunnerJobs(runId, command?.id); const latestJob = jobs.at(-1) ?? null; const commandTerminal = command ? terminalFromCommand(command) : null; const terminalEventStatus = terminalFromEvents(scopedEvents); const preliminaryTerminal = commandTerminal ?? terminalEventStatus ?? run.terminalStatus; const failureKind = resultFailureKind(run, command, scopedEvents, preliminaryTerminal); const terminal = resultTerminal(commandTerminal, terminalEventStatus, run.terminalStatus, failureKind); const terminalSource = resultTerminalSource(commandTerminal, terminalEventStatus, run.terminalStatus, failureKind); const failureMessage = resultFailureMessage(run, command, scopedEvents, terminal); const failureDetails = resultFailureDetails(scopedEvents, terminal); const reply = assistantReply(scopedEvents); const blocker = terminal === "blocked" || terminal === "failed" ? { failureKind, message: failureMessage, details: failureDetails } : null; const liveness = livenessSnapshot(run, command, events, scopedEvents, terminal, failureKind, failureMessage); const terminalClassification = terminalClassificationSummary({ terminal, terminalSource, failureKind, failureMessage, liveness }); const steerDelivery = command?.type === "steer" ? steerDeliverySummary(events, command.id) : null; return { runId: run.id, commandId: command?.id ?? commandId ?? null, attemptId: latestJob?.attemptId ?? attemptFromEvents(events), runnerId: latestJob?.runnerId ?? null, jobName: latestJob?.jobName ?? null, namespace: latestJob?.namespace ?? null, status: command?.state ?? run.status, runStatus: run.status, commandState: command?.state ?? null, terminalStatus: terminal, terminalSource, completed: terminal === "completed", reply: reply.text, finalResponse: { text: reply.text, seq: reply.seq, source: reply.source, final: reply.final, replyAuthority: reply.replyAuthority, textTruncated: reply.textTruncated, outputTruncated: reply.outputTruncated, }, finalAssistantSeq: reply.seq, finalAssistantSource: reply.source, finalAssistantTextTruncated: reply.textTruncated, finalAssistantOutputTruncated: reply.outputTruncated, failureKind, failureMessage, failureDetails, terminalClassification, blocker, liveness, ...(steerDelivery ? { steerDelivery } : {}), lastSeq: events.at(-1)?.seq ?? 0, eventCount: events.length, eventsCapped: eventPage.capped, nextAfterSeq: eventPage.nextAfterSeq, scopedEventCount: scopedEvents.length, scopedLastSeq: scopedEvents.at(-1)?.seq ?? 0, artifactSummary: artifactSummary(scopedEvents), toolCallSummary: toolCallSummary(scopedEvents), sessionRef: sessionSummary(run), resourceBundleRef: resourceBundleSummary(run, events), runnerJobCount: jobs.length, }; } function livenessSnapshot(run: RunRecord, command: CommandRecord | null, events: RunEvent[], scopedEvents: RunEvent[], terminal: TerminalStatus | null, failureKind: FailureKind | null, failureMessage: string | null): JsonRecord { const nowMs = Date.now(); const active = terminal === null && !runIsTerminal(run) && !commandIsTerminal(command); const lastEvent = events.at(-1) ?? null; const lastVisibleActivity = latestVisibleActivity(scopedEvents); const lastBusinessActivity = latestBusinessActivity(scopedEvents); const lastCommandActivity = lastBusinessActivity ?? latestLivenessActivity(scopedEvents); const lease = leaseSummary(run, nowMs); const transportDisconnect = latestTransportDisconnect(scopedEvents); const lastActivity = livenessActivitySummary(lastCommandActivity, nowMs); const timeoutBudget = timeoutBudgetSummary(run, command, terminal, failureKind, nowMs); const terminalClassification = terminalClassificationFromEvidence({ terminal, terminalSource: "liveness", failureKind, failureMessage, timeoutBudget, transportDisconnect, lastActivity, command }); const phase = livenessPhase({ active, command, lastVisibleActivity, leaseExpired: lease.leaseExpired, transportDisconnect, timeoutBudget, lastActivity }); const afterSeq = lastEvent?.seq ?? 0; return { phase, active, observedAt: new Date(nowMs).toISOString(), runStatus: run.status, commandId: command?.id ?? null, commandType: command?.type ?? null, commandState: command?.state ?? null, lastSeq: lastEvent?.seq ?? 0, lastEventAt: lastEvent?.createdAt ?? null, lastEventAgeMs: lastEvent ? ageMs(lastEvent.createdAt, nowMs) : null, lastActivity, lastCommandActivity: lastActivity, timeoutBudget, terminalClassification, lease, transportDisconnect: transportDisconnect ? livenessActivitySummary(transportDisconnect, nowMs) : null, recoveryActions: recoveryActions({ run, command, afterSeq, active, terminal, failureKind, failureMessage }), valuesPrinted: false, }; } function livenessPhase(input: { active: boolean; command: CommandRecord | null; lastVisibleActivity: RunEvent | null; leaseExpired: boolean | null; transportDisconnect: RunEvent | null; timeoutBudget: JsonRecord; lastActivity: JsonRecord | null }): string { if (!input.active) return "terminal"; if (input.command?.state === "pending") return "waiting-runner"; if (input.leaseExpired === true) return "runner-heartbeat-stale"; if (input.transportDisconnect) return "transport-disconnected"; if (input.lastVisibleActivity?.type === "tool_call") { const status = input.lastVisibleActivity.payload.status; if (status === "inProgress" || status === "running") return "waiting-tool"; if (status === "completed") return "idle-after-tool"; } if (input.lastVisibleActivity?.type === "command_output") return "waiting-tool-output"; if (input.lastVisibleActivity?.type === "assistant_message") return "waiting-model-output"; const remainingMs = numberJsonValue(input.timeoutBudget.remainingMs); const timeoutMs = numberJsonValue(input.timeoutBudget.timeoutMs); const activityAgeMs = numberJsonValue(input.lastActivity?.ageMs); const inactiveThresholdMs = timeoutMs === null ? 300_000 : Math.min(300_000, Math.max(60_000, Math.floor(timeoutMs / 4))); if (remainingMs !== null && remainingMs <= Math.min(120_000, Math.max(10_000, Math.floor((timeoutMs ?? 120_000) / 10))) && (activityAgeMs === null || activityAgeMs >= inactiveThresholdMs)) return "runner-stdio-inactive"; return "waiting-model"; } function terminalClassificationSummary(input: { terminal: TerminalStatus | null; terminalSource: string; failureKind: FailureKind | null; failureMessage: string | null; liveness: JsonRecord }): JsonRecord { const livenessClassification = jsonRecordValue(input.liveness.terminalClassification); return { ...(livenessClassification ?? {}), terminalStatus: input.terminal, terminalSource: input.terminalSource, failureKind: input.failureKind, failureMessage: input.failureMessage ? boundedTextSummary(input.failureMessage, { limitChars: 240 }).text as string : null, valuesPrinted: false, }; } function terminalClassificationFromEvidence(input: TerminalClassificationInput): JsonRecord { const timeoutState = stringJsonValue(input.timeoutBudget.state); const hardTimeout = input.failureKind === "backend-timeout" || timeoutState === "timed-out"; const providerKind = providerFailureCategory(input.failureKind); const cancelled = input.terminal === "cancelled" || input.failureKind === "cancelled"; const taskFailure = input.terminal === "failed" && input.failureKind !== null && !hardTimeout && !providerKind && !infrastructureFailureKind(input.failureKind); let category = "unknown"; let confidence = "low"; let providerEvidence = "not-applicable"; let reason = "terminal state is not yet available"; if (input.terminal === "completed") { category = "completed"; confidence = "high"; reason = "command completed successfully"; } else if (cancelled) { category = "cancelled"; confidence = "high"; reason = "terminal status or failureKind is cancelled"; } else if (providerKind) { category = providerKind; confidence = "high"; providerEvidence = "failure-kind"; reason = `failureKind ${input.failureKind} is provider-specific`; } else if (hardTimeout && input.transportDisconnect) { category = "execution-hard-timeout"; confidence = "medium"; providerEvidence = "observed-transport-disconnect"; reason = "hard timeout is terminal and a backend transport/app-server close event was observed, but existing events do not prove the model provider caused it"; } else if (hardTimeout) { category = "execution-hard-timeout"; confidence = "high"; providerEvidence = "insufficient"; reason = "hard timeout is terminal; no provider-specific failure event was recorded"; } else if (input.terminal === "blocked") { category = "blocked"; confidence = "high"; reason = `terminal status is blocked${input.failureKind ? ` with failureKind ${input.failureKind}` : ""}`; } else if (taskFailure) { category = "task-failed"; confidence = "medium"; reason = `terminal failure is not timeout, cancellation, provider-specific, or infrastructure-classified${input.failureKind ? `; failureKind=${input.failureKind}` : ""}`; } else if (input.terminal === "failed" && infrastructureFailureKind(input.failureKind)) { category = "infrastructure-failed"; confidence = "medium"; reason = `failureKind ${input.failureKind} is infrastructure/backend classified`; } return { category, confidence, providerEvidence, providerInterruption: providerEvidence === "failure-kind" || providerEvidence === "observed-transport-disconnect" ? providerEvidence : "not-established", providerInterruptionKnown: providerEvidence === "failure-kind", providerInterruptionReason: providerEvidence === "failure-kind" ? "provider-specific failureKind is authoritative" : providerEvidence === "observed-transport-disconnect" ? "transport disconnect was observed, but current events cannot distinguish provider outage from runner/backend shutdown during timeout" : providerEvidence === "insufficient" ? "no provider-specific error or disconnect evidence was recorded" : null, hardTimeout, timeoutState, transportDisconnectObserved: Boolean(input.transportDisconnect), transportDisconnectSeq: input.transportDisconnect?.seq ?? null, lastActivityKind: stringJsonValue(input.lastActivity?.activityKind), lastActivitySeq: numberJsonValue(input.lastActivity?.sourceSeq), commandId: input.command?.id ?? null, reason, valuesPrinted: false, }; } function providerFailureCategory(failureKind: FailureKind | null): string | null { if (!failureKind) return null; if (failureKind === "provider-stream-disconnected") return "provider-interrupted"; if (failureKind.startsWith("provider-")) return "provider-failed"; return null; } function infrastructureFailureKind(failureKind: FailureKind | null): boolean { if (!failureKind) return false; return failureKind.startsWith("backend-") || failureKind === "runner-lease-conflict" || failureKind === "infra-failed" || failureKind === "thread-resume-failed"; } function timeoutBudgetSummary(run: RunRecord, command: CommandRecord | null, terminal: TerminalStatus | null, failureKind: FailureKind | null, nowMs: number): JsonRecord { const timeoutMs = typeof run.executionPolicy.timeoutMs === "number" && Number.isFinite(run.executionPolicy.timeoutMs) && run.executionPolicy.timeoutMs > 0 ? Math.trunc(run.executionPolicy.timeoutMs) : null; const startedAt = command?.acknowledgedAt ?? command?.createdAt ?? run.updatedAt ?? run.createdAt; const startedMs = Date.parse(startedAt); const elapsedMs = timeoutMs !== null && Number.isFinite(startedMs) ? Math.max(0, nowMs - startedMs) : null; const remainingMs = timeoutMs !== null && elapsedMs !== null ? Math.max(0, timeoutMs - elapsedMs) : null; const approachingThresholdMs = timeoutMs === null ? null : Math.min(120_000, Math.max(10_000, Math.floor(timeoutMs / 10))); let state = "unknown"; if (timeoutMs !== null && elapsedMs !== null) { if (terminal !== null) state = failureKind === "backend-timeout" ? "timed-out" : "terminal"; else if (remainingMs === 0) state = "overdue"; else if (approachingThresholdMs !== null && remainingMs !== null && remainingMs <= approachingThresholdMs) state = "approaching-hard-timeout"; else state = "within-budget"; } return { timeoutMs, source: "executionPolicy.timeoutMs", startedAt, elapsedMs, remainingMs, approachingThresholdMs, state, hardTimeout: true, valuesPrinted: false, }; } function leaseSummary(run: RunRecord, nowMs: number): JsonRecord & { leaseExpired: boolean | null } { const leaseExpiresMs = run.leaseExpiresAt ? Date.parse(run.leaseExpiresAt) : NaN; const hasLease = Boolean(run.claimedBy && run.leaseExpiresAt && Number.isFinite(leaseExpiresMs)); const leaseExpired = run.claimedBy ? (hasLease ? leaseExpiresMs <= nowMs : true) : null; return { claimedBy: run.claimedBy, leaseExpiresAt: run.leaseExpiresAt, leaseExpired, leaseRemainingMs: hasLease ? Math.max(0, leaseExpiresMs - nowMs) : null, valuesPrinted: false, }; } function latestLivenessActivity(events: RunEvent[]): RunEvent | null { return [...events].reverse().find(isLivenessActivityEvent) ?? null; } function latestVisibleActivity(events: RunEvent[]): RunEvent | null { return [...events].reverse().find((event) => event.type === "assistant_message" || event.type === "tool_call" || event.type === "command_output" || event.type === "diff" || event.type === "error" || event.type === "terminal_status") ?? null; } function latestBusinessActivity(events: RunEvent[]): RunEvent | null { return [...events].reverse().find((event) => event.type === "assistant_message" || event.type === "tool_call" || event.type === "command_output" || event.type === "diff") ?? null; } function isLivenessActivityEvent(event: RunEvent): boolean { if (event.type === "assistant_message" || event.type === "tool_call" || event.type === "command_output" || event.type === "diff" || event.type === "error" || event.type === "terminal_status") return true; if (event.type !== "backend_status") return false; const phase = typeof event.payload.phase === "string" ? event.payload.phase : ""; return ["backend-turn-started", "thread/start:completed", "thread/resume:completed", "turn/start:completed", "backend-turn-finished", "codex-app-server-closed"].includes(phase); } function latestTransportDisconnect(events: RunEvent[]): RunEvent | null { return [...events].reverse().find((event) => { const phase = typeof event.payload.phase === "string" ? event.payload.phase : ""; return (event.type === "error" && phase.includes("transport")) || (event.type === "backend_status" && phase === "codex-app-server-closed"); }) ?? null; } function livenessActivitySummary(event: RunEvent | null, nowMs: number): JsonRecord | null { if (!event) return null; return { seq: event.seq, sourceSeq: event.seq, eventId: event.id, type: event.type, activityKind: activityKind(event), phase: typeof event.payload.phase === "string" ? event.payload.phase : null, status: typeof event.payload.status === "string" ? event.payload.status : null, toolName: typeof event.payload.toolName === "string" ? event.payload.toolName : null, itemId: typeof event.payload.itemId === "string" ? event.payload.itemId : null, exitCode: normalizedExitCode(event.payload.exitCode), commandId: typeof event.payload.commandId === "string" ? event.payload.commandId : null, createdAt: event.createdAt, ageMs: ageMs(event.createdAt, nowMs), summary: activityTextSummary(event), valuesPrinted: false, }; } function activityKind(event: RunEvent): string { if (event.type === "tool_call") { const status = typeof event.payload.status === "string" ? event.payload.status : ""; if (status === "running" || status === "inProgress") return "tool-in-flight"; if (status === "completed") return "tool-completed"; if (status === "cancelled") return "tool-cancelled"; if (status === "failed") return "tool-failed"; return "tool"; } if (event.type === "command_output") return "tool-output"; if (event.type === "assistant_message") return event.payload.progress === true ? "assistant-progress" : "assistant-output"; if (event.type === "diff") return "diff"; if (event.type === "error") return "error"; if (event.type === "terminal_status") return "terminal"; return "backend-activity"; } function activityTextSummary(event: RunEvent): string | null { if (event.type === "tool_call") { const name = typeof event.payload.toolName === "string" ? event.payload.toolName : typeof event.payload.type === "string" ? event.payload.type : "tool"; const status = typeof event.payload.status === "string" ? event.payload.status : "observed"; return `${name} ${status}`; } const fromSummary = typeof event.payload.outputSummary === "string" ? event.payload.outputSummary : typeof event.payload.summary === "string" ? event.payload.summary : null; const text = fromSummary ?? textPayload(event.payload) ?? (typeof event.payload.phase === "string" ? event.payload.phase : null); if (!text) return null; return boundedTextSummary(text.replace(/\s+/gu, " ").trim(), { limitChars: 240 }).text as string; } function ageMs(value: string, nowMs: number): number | null { const parsed = Date.parse(value); return Number.isFinite(parsed) ? Math.max(0, nowMs - parsed) : null; } function recoveryActions(input: { run: RunRecord; command: CommandRecord | null; afterSeq: number; active: boolean; terminal: TerminalStatus | null; failureKind: FailureKind | null; failureMessage: string | null }): JsonRecord[] { const { run, command, afterSeq, active, terminal, failureKind, failureMessage } = input; const sessionId = run.sessionRef?.sessionId ?? null; const traceCommand = sessionId ? `./scripts/agentrun sessions trace ${sessionId} --after-seq ${afterSeq} --limit 100 --run-id ${run.id}` : `./scripts/agentrun runs events ${run.id} --after-seq ${afterSeq} --limit 100 --summary`; const outputCommand = sessionId ? `./scripts/agentrun sessions output ${sessionId} --after-seq ${afterSeq} --limit 100 --run-id ${run.id}` : null; const actions: JsonRecord[] = [ { action: "poll-trace", runId: run.id, commandId: command?.id ?? null, afterSeq, command: traceCommand, valuesPrinted: false }, ]; if (outputCommand) actions.push({ action: "poll-output", runId: run.id, commandId: command?.id ?? null, afterSeq, command: outputCommand, valuesPrinted: false }); if (active) { if (sessionId) actions.push({ action: "steer-session", sessionId, runId: run.id, commandId: command?.id ?? null, command: `./scripts/agentrun sessions steer ${sessionId} --prompt-stdin`, valuesPrinted: false }); if (command) actions.push({ action: "cancel-command", runId: run.id, commandId: command.id, command: `./scripts/agentrun commands cancel ${command.id} --reason `, valuesPrinted: false }); else actions.push({ action: "cancel-run", runId: run.id, command: `./scripts/agentrun runs cancel ${run.id} --reason `, valuesPrinted: false }); return actions; } if (terminal === "failed" || terminal === "blocked" || terminal === "cancelled") { if (command) actions.push({ action: "inspect-result", runId: run.id, commandId: command.id, command: `./scripts/agentrun commands result ${command.id} --run-id ${run.id}`, valuesPrinted: false }); if (sessionId) actions.push({ action: "resume-session", sessionId, command: `./scripts/agentrun sessions turn ${sessionId} --prompt-stdin`, valuesPrinted: false }); if (failureKind === "backend-timeout") actions.push({ action: "split-task", reason: "backend-timeout", hint: "把大 patch / 长工具链拆成更短 turn 后用同一 session 续跑", failureMessage: failureMessage ? boundedTextSummary(failureMessage, { limitChars: 200 }).text as string : null, valuesPrinted: false }); else actions.push({ action: "retry-or-split", reason: failureKind ?? "terminal", hint: "先读 trace/output 的 detail id,再决定 steer、重跑或拆分", valuesPrinted: false }); } return actions; } function numberJsonValue(value: JsonValue | undefined): number | null { return typeof value === "number" && Number.isFinite(value) ? value : null; } function stringJsonValue(value: JsonValue | undefined): string | null { return typeof value === "string" && value.length > 0 ? value : null; } function jsonRecordValue(value: unknown): JsonRecord | null { return typeof value === "object" && value !== null && !Array.isArray(value) ? value as JsonRecord : null; } function steerDeliverySummary(events: RunEvent[], commandId: string): JsonRecord { const related = events.filter((event) => event.payload.commandId === commandId); const completed = latestPhaseEvent(related, "turn/steer:completed"); const acknowledged = latestPhaseEvent(related, "steer-command-acknowledged"); const failed = [...related].reverse().find((event) => event.type === "error") ?? null; const deliverySeq = completed?.seq ?? failed?.seq ?? acknowledged?.seq ?? related.at(-1)?.seq ?? null; const targetCommandId = targetCommandIdFromEvents(related); const targetFollowUp = targetCommandId && deliverySeq !== null ? targetFollowUpAfterSeq(events, targetCommandId, deliverySeq) : null; return { commandId, targetCommandId, deliveryState: failed ? "failed" : completed ? "forwarded-to-backend" : acknowledged ? "acknowledged-by-runner" : "not-observed", backendAccepted: Boolean(completed), targetFollowUpObserved: targetFollowUp?.observed ?? null, targetFollowUpSeq: targetFollowUp?.seq ?? null, targetFollowUpType: targetFollowUp?.type ?? null, targetFollowUpPhase: targetFollowUp?.phase ?? null, semantics: "steer completion means the backend accepted the turn/steer RPC; target turn progress is reported by the target command liveness and is not implied by the steer command terminal state", valuesPrinted: false, }; } function latestPhaseEvent(events: RunEvent[], phase: string): RunEvent | null { return [...events].reverse().find((event) => event.payload.phase === phase) ?? null; } function targetCommandIdFromEvents(events: RunEvent[]): string | null { for (const event of [...events].reverse()) { const targetCommandId = event.payload.targetCommandId; if (typeof targetCommandId === "string" && targetCommandId.length > 0) return targetCommandId; } return null; } function targetFollowUpAfterSeq(events: RunEvent[], targetCommandId: string, afterSeq: number): JsonRecord { const event = events.find((item) => item.seq > afterSeq && item.payload.commandId === targetCommandId && isTargetFollowUpEvent(item)); return { observed: Boolean(event), seq: event?.seq ?? null, type: event?.type ?? null, phase: typeof event?.payload.phase === "string" ? event.payload.phase : null, }; } function isTargetFollowUpEvent(event: RunEvent): boolean { return event.type === "assistant_message" || event.type === "tool_call" || event.type === "command_output" || event.type === "diff" || event.type === "error" || event.type === "terminal_status"; } function runIsTerminal(run: RunRecord): boolean { return run.status === "completed" || run.status === "failed" || run.status === "blocked" || run.status === "cancelled"; } function commandIsTerminal(command: CommandRecord | null): boolean { return command ? terminalFromCommand(command) !== null : false; } async function listResultEvents(store: AgentRunStore, runId: string): Promise { const events: RunEvent[] = []; let afterSeq = 0; for (let pageIndex = 0; pageIndex < RESULT_EVENT_MAX_PAGES; pageIndex += 1) { const page = await store.listEvents(runId, afterSeq, RESULT_EVENT_PAGE_LIMIT); if (page.length === 0) return { events, capped: false, nextAfterSeq: null }; events.push(...page); const nextAfterSeq = page.at(-1)?.seq ?? afterSeq; if (nextAfterSeq <= afterSeq) return { events, capped: true, nextAfterSeq: afterSeq }; afterSeq = nextAfterSeq; if (page.length < RESULT_EVENT_PAGE_LIMIT) return { events, capped: false, nextAfterSeq: null }; } return { events, capped: true, nextAfterSeq: events.at(-1)?.seq ?? afterSeq }; } async function selectCommand(store: AgentRunStore, runId: string, commandId?: string): Promise { if (commandId) { const command = await store.getCommand(commandId); return command.runId === runId ? command : null; } const commands = await store.listCommands(runId, 0, 100); return commands.at(-1) ?? null; } function terminalFromEvents(events: RunEvent[]): TerminalStatus | null { for (const event of [...events].reverse()) { if (event.type !== "terminal_status") continue; const value = event.payload.terminalStatus; if (value === "completed" || value === "failed" || value === "blocked" || value === "cancelled") return value; } return null; } function terminalFromCommand(command: CommandRecord): TerminalStatus | null { if (command.state === "completed") return "completed"; if (command.state === "failed") return "failed"; if (command.state === "cancelled") return "cancelled"; return null; } function resultTerminal(commandTerminal: TerminalStatus | null, terminalEventStatus: TerminalStatus | null, runTerminalStatus: TerminalStatus | null, failureKind: FailureKind | null): TerminalStatus | null { if (commandTerminal === "failed" && terminalEventStatus === "blocked" && failureKind === "required-skill-unavailable") return "blocked"; return commandTerminal ?? terminalEventStatus ?? runTerminalStatus; } function resultTerminalSource(commandTerminal: TerminalStatus | null, terminalEventStatus: TerminalStatus | null, runTerminalStatus: TerminalStatus | null, failureKind: FailureKind | null): string { if (commandTerminal === "failed" && terminalEventStatus === "blocked" && failureKind === "required-skill-unavailable") return "terminal_status-event"; if (commandTerminal) return "command-record"; if (terminalEventStatus) return "terminal_status-event"; if (runTerminalStatus) return "run-record"; return "none"; } function eventsForCommand(events: RunEvent[], commandId: string): RunEvent[] { const scoped = events.filter((event) => event.payload.commandId === commandId || typeof event.payload.commandId !== "string"); return scoped.length > 0 ? scoped : events; } function failureKindFromEvents(events: RunEvent[]): FailureKind | null { for (const event of [...events].reverse()) { const value = event.payload.failureKind; if (isFailureKind(value)) return value; } return null; } function resultFailureKind(run: RunRecord, command: CommandRecord | null, events: RunEvent[], terminal: TerminalStatus | null): FailureKind | null { if (terminal === "completed") return null; if (command) return failureKindFromEvents(events); return run.failureKind ?? failureKindFromEvents(events); } function isFailureKind(value: unknown): value is FailureKind { return typeof value === "string" && [ "cancelled", "tenant-policy-denied", "secret-unavailable", "prompt-unavailable", "prompt-too-large", "required-skill-unavailable", "skill-unavailable", "runner-lease-conflict", "backend-failed", "backend-timeout", "backend-response-invalid", "thread-resume-failed", "provider-auth-failed", "provider-invalid-tool-call", "provider-compact-unsupported", "provider-rate-limited", "provider-unavailable", "provider-stream-disconnected", "provider-refused-retry-recovered", "infra-failed", "schema-invalid", ].includes(value); } function messageFromEvents(events: RunEvent[]): string | null { for (const event of [...events].reverse()) { const value = event.payload.message; if (typeof value === "string" && value.length > 0) return value; } return null; } function resultFailureMessage(run: RunRecord, command: CommandRecord | null, events: RunEvent[], terminal: TerminalStatus | null): string | null { if (terminal === "completed") return null; if (command) return messageFromEvents(events); return run.failureMessage ?? messageFromEvents(events); } function detailsFromEvents(events: RunEvent[]): JsonRecord | null { for (const event of [...events].reverse()) { const value = event.payload.details; if (typeof value === "object" && value !== null && !Array.isArray(value)) return value as JsonRecord; } return null; } function resultFailureDetails(events: RunEvent[], terminal: TerminalStatus | null): JsonRecord | null { return terminal === "completed" ? null : detailsFromEvents(events); } function assistantReply(events: RunEvent[]): AssistantReplySummary { const assistantEvents = events.filter((event) => event.type === "assistant_message"); const latestAuthoritative = [...assistantEvents].reverse().find((event) => (event.payload.replyAuthority === true || event.payload.final === true) && textPayload(event.payload).length > 0); if (latestAuthoritative) return assistantReplySummary(latestAuthoritative); const latestAssistant = [...assistantEvents].reverse().find((event) => textPayload(event.payload).length > 0); if (latestAssistant) return assistantReplySummary(latestAssistant); return { text: "", seq: null, source: null, final: false, replyAuthority: false, textTruncated: false, outputTruncated: false }; } function assistantReplySummary(event: RunEvent): AssistantReplySummary { return { text: textPayload(event.payload), seq: event.seq, source: typeof event.payload.source === "string" ? event.payload.source : null, final: event.payload.final === true, replyAuthority: event.payload.replyAuthority === true, textTruncated: event.payload.textTruncated === true, outputTruncated: event.payload.outputTruncated === true, }; } function textPayload(payload: JsonRecord): string { for (const key of ["text", "content", "delta"]) { const value = payload[key]; if (typeof value === "string") return value; } return ""; } function artifactSummary(events: RunEvent[]): JsonRecord { let commandOutputEvents = 0; let diffEvents = 0; let toolCallEvents = 0; let outputChars = 0; let outputBytes = 0; let outputTruncatedEvents = 0; const streamSummary: Record = { stdout: { events: 0, outputBytes: 0, outputTruncated: false }, stderr: { events: 0, outputBytes: 0, outputTruncated: false }, }; for (const event of events) { if (event.type === "command_output") { commandOutputEvents += 1; const text = textPayload(event.payload); outputChars += text.length; const bytes = outputBytesFromPayload(event.payload); outputBytes += bytes; const truncated = outputTruncatedFromPayload(event.payload); if (truncated) outputTruncatedEvents += 1; const stream = event.payload.stream === "stderr" ? "stderr" : "stdout"; streamSummary[stream].events += 1; streamSummary[stream].outputBytes += bytes; streamSummary[stream].outputTruncated ||= truncated; } if (event.type === "diff") diffEvents += 1; if (event.type === "tool_call") toolCallEvents += 1; } return { commandOutputEvents, diffEvents, toolCallEvents, outputChars, outputBytes, truncatedEvents: outputTruncatedEvents, outputTruncatedEvents, stdoutSummary: streamSummary.stdout, stderrSummary: streamSummary.stderr }; } function toolCallSummary(events: RunEvent[]): JsonRecord { const toolCallEvents = events.filter((event) => event.type === "tool_call"); const statusCounts: Record = {}; const exitCodeCounts: Record = {}; for (const event of toolCallEvents) { const status = typeof event.payload.status === "string" && event.payload.status.length > 0 ? event.payload.status : "unknown"; statusCounts[status] = (statusCounts[status] ?? 0) + 1; const exitCode = normalizedExitCode(event.payload.exitCode); if (exitCode !== null) exitCodeCounts[String(exitCode)] = (exitCodeCounts[String(exitCode)] ?? 0) + 1; } const window = toolCallEvents.slice(-maxToolCallSummaryItems); return { count: toolCallEvents.length, statusCounts, exitCodeCounts, items: window.map((event) => toolCallItemSummary(event)), itemsOmitted: Math.max(0, toolCallEvents.length - window.length), itemWindow: "latest", valuesPrinted: false, }; } function toolCallItemSummary(event: RunEvent): JsonRecord { const payload = event.payload; return { seq: event.seq, createdAt: event.createdAt, method: boundedOptionalString(payload.method, toolCallFieldLimitChars), toolName: boundedOptionalString(payload.toolName, toolCallFieldLimitChars), type: boundedOptionalString(payload.type, toolCallFieldLimitChars), itemId: boundedOptionalString(payload.itemId, toolCallFieldLimitChars), status: boundedOptionalString(payload.status, toolCallFieldLimitChars), exitCode: normalizedExitCode(payload.exitCode), processId: boundedOptionalString(payload.processId, toolCallFieldLimitChars), cwd: boundedOptionalString(payload.cwd, toolCallFieldLimitChars), command: boundedOptionalString(payload.command, toolCallCommandLimitChars), commandTruncated: optionalStringTruncated(payload.command, toolCallCommandLimitChars), valuesPrinted: false, }; } function boundedOptionalString(value: JsonValue | undefined, limitChars: number): string | null { if (typeof value !== "string") return null; return boundedTextSummary(value, { limitChars }).text as string; } function optionalStringTruncated(value: JsonValue | undefined, limitChars: number): boolean { if (typeof value !== "string") return false; return boundedTextSummary(value, { limitChars }).outputTruncated === true; } function normalizedExitCode(value: JsonValue | undefined): number | null { return typeof value === "number" && Number.isFinite(value) ? value : null; } function attemptFromEvents(events: RunEvent[]): string | null { for (const event of [...events].reverse()) { const value = event.payload.attemptId; if (typeof value === "string") return value; } return null; } function sessionSummary(run: RunRecord): JsonRecord | null { if (!run.sessionRef) return null; return { sessionId: run.sessionRef.sessionId, conversationId: run.sessionRef.conversationId ?? null, threadId: run.sessionRef.threadId ?? null, expiresAt: run.sessionRef.expiresAt ?? null, metadataKeys: Object.keys(run.sessionRef.metadata ?? {}).sort(), valuesPrinted: false, }; } function resourceBundleSummary(run: RunRecord, events: RunEvent[]): JsonRecord | null { if (!run.resourceBundleRef) return null; const materialized = events.find((event) => event.type === "backend_status" && event.payload.phase === "resource-bundle-materialized")?.payload ?? null; return { kind: run.resourceBundleRef.kind, repoUrl: run.resourceBundleRef.repoUrl, commitId: run.resourceBundleRef.commitId ?? null, ref: run.resourceBundleRef.ref ?? null, bundles: { count: run.resourceBundleRef.bundles.length, items: run.resourceBundleRef.bundles.map((item) => ({ name: item.name ?? null, repoUrl: item.repoUrl ?? run.resourceBundleRef?.repoUrl ?? null, commitId: item.commitId ?? run.resourceBundleRef?.commitId ?? null, ref: item.ref ?? run.resourceBundleRef?.ref ?? null, subpath: item.subpath, targetPath: item.targetPath, valuesPrinted: false })), valuesPrinted: false, }, promptRefs: run.resourceBundleRef.promptRefs ? { count: run.resourceBundleRef.promptRefs.length, names: run.resourceBundleRef.promptRefs.map((item) => item.name), required: run.resourceBundleRef.promptRefs.filter((item) => item.required === true).map((item) => item.name), valuesPrinted: false } : { count: 0, names: [], required: [], valuesPrinted: false }, requiredSkills: run.resourceBundleRef.requiredSkills ? { count: run.resourceBundleRef.requiredSkills.length, names: run.resourceBundleRef.requiredSkills.map((item) => item.name), valuesPrinted: false } : { count: 0, names: [], valuesPrinted: false }, materialized: materialized as JsonValue, }; }