| import { isPermissionMode, type PermissionMode } from './permission.js'; |
| import { isCollaborationMode, type CollaborationMode } from './collaboration.js'; |
| import { |
| isAgentSwarmAuthorizationSource, |
| isEffectiveOrchestrationSource, |
| isOrchestrationMode, |
| type AgentSwarmAuthorizationSource, |
| type EffectiveOrchestrationSource, |
| type OrchestrationMode, |
| } from './orchestration.js'; |
| import type { BackendKind } from './session.js'; |
| import { |
| defineObjectShape, |
| hasExactShape, |
| isFiniteNumber, |
| isOptionalString, |
| isRecord, |
| } from './record-schema.js'; |
| import type { AgentGraphIntentClaim } from './agent-graph-control.js'; |
| import { isToolMode, type ToolMode } from './tool-mode.js'; |
| |
| export const AGENT_RUN_STATUSES = [ |
| 'created', |
| 'running', |
| 'waiting_for_user', |
| 'completed', |
| 'failed', |
| 'cancelled', |
| ] as const; |
| |
| export type AgentRunStatus = (typeof AGENT_RUN_STATUSES)[number]; |
| |
| export interface AgentRunContinuationSourceV1 { |
| sourceInvocationId: string; |
| sourceRunId: string; |
| sourceTurnId: string; |
| sourceRuntimeEventHighWater: number; |
| } |
| |
| export interface AgentRunContinuationSourceV2 extends AgentRunContinuationSourceV1 { |
| protocol: 'continuation_source_v2'; |
| claimId: string; |
| boundaryDigest: `sha256:${string}`; |
| sourcePrefixDigest: `sha256:${string}`; |
| replayManifestDigest: `sha256:${string}`; |
| } |
| |
| export type AgentRunContinuationSource = |
| | AgentRunContinuationSourceV1 |
| | AgentRunContinuationSourceV2; |
| |
| export type RootExecutionDescriptor = |
| | { |
| kind: 'external_message'; |
| inputDigest?: `sha256:${string}`; |
| maxSteps?: number; |
| } |
| | { kind: 'regenerate'; sourceTurnId: string } |
| | { kind: 'context_compact' } |
| | { kind: 'automation'; automationId: string } |
| | { kind: 'goal'; goalId: string } |
| | { |
| kind: 'agent_graph_supervisor_wake'; |
| graphId: string; |
| wakeId: string; |
| attemptId: string; |
| } |
| | { |
| kind: 'safe_boundary_continuation'; |
| sourceInvocationId: string; |
| sourceRunId: string; |
| sourceTurnId: string; |
| sourceRuntimeEventHighWater: number; |
| claimId: string; |
| boundaryDigest: `sha256:${string}`; |
| providerReplayDigest: `sha256:${string}`; |
| safetyDigest: `sha256:${string}`; |
| targetInvocationId: string; |
| } |
| | { |
| kind: 'linked_child_initial'; |
| agentId: string; |
| agentName: string; |
| } |
| | { |
| kind: 'linked_child_resume'; |
| agentId: string; |
| agentName: string; |
| sourceRunId: string; |
| } |
| | { |
| kind: 'linked_child_provider_retry'; |
| agentId: string; |
| agentName: string; |
| sourceRunId: string; |
| } |
| | { |
| kind: 'claimed_agent_graph_intent'; |
| claim: AgentGraphIntentClaim; |
| agentId: string; |
| agentName: string; |
| }; |
| |
| const AGENT_RUN_CONTINUATION_SOURCE_V1_SHAPE = defineObjectShape<AgentRunContinuationSourceV1>()( |
| ['sourceInvocationId', 'sourceRunId', 'sourceTurnId', 'sourceRuntimeEventHighWater'], |
| [], |
| ); |
| const AGENT_RUN_CONTINUATION_SOURCE_V2_SHAPE = defineObjectShape<AgentRunContinuationSourceV2>()( |
| [ |
| 'protocol', |
| 'claimId', |
| 'boundaryDigest', |
| 'sourceInvocationId', |
| 'sourceRunId', |
| 'sourceTurnId', |
| 'sourceRuntimeEventHighWater', |
| 'sourcePrefixDigest', |
| 'replayManifestDigest', |
| ], |
| [], |
| ); |
| |
| export interface AgentRunHeader { |
| runId: string; |
| /** Durable Runtime invocation spine. Optional only for legacy run headers. */ |
| invocationId?: string; |
| sessionId: string; |
| turnId: string; |
| status: AgentRunStatus; |
| backendKind: BackendKind; |
| llmConnectionSlug: string; |
| modelId: string; |
| cwd: string; |
| /** Authoritative host identity for the workspace observed when the run was created. */ |
| workspaceIdentity?: string; |
| permissionMode: PermissionMode; |
| /** Snapshot of the session collaboration mode. Optional on legacy runs. */ |
| collaborationMode?: CollaborationMode; |
| /** Effective orchestration mode for this run. Optional on legacy runs. */ |
| orchestrationMode?: OrchestrationMode; |
| /** Whether the effective mode came from the session or this turn. */ |
| orchestrationSource?: EffectiveOrchestrationSource; |
| /** Narrow authority for the parent agent_swarm envelope. */ |
| agentSwarmAuthorization?: AgentSwarmAuthorizationSource; |
| /** Effective tool protocol for this run. Optional on legacy runs. */ |
| toolMode?: ToolMode; |
| createdAt: number; |
| updatedAt: number; |
| completedAt?: number; |
| parentRunId?: string; |
| /** Immediate child AgentRun continued by this run. */ |
| resumedFromRunId?: string; |
| /** Immediate child AgentRun whose provider step is retried by this run. */ |
| retriedFromRunId?: string; |
| agentId?: string; |
| agentName?: string; |
| parentTurnId?: string; |
| retriedFromTurnId?: string; |
| regeneratedFromTurnId?: string; |
| branchOfTurnId?: string; |
| parentSessionId?: string; |
| /** Durable claim that this run is the continuation child for one source boundary. */ |
| continuationSource?: AgentRunContinuationSource; |
| /** Non-user trigger for this run (e.g. a scheduled automation fire). */ |
| automationId?: string; |
| /** Host-owned Goal generation that triggered this continuation Run. */ |
| goalId?: string; |
| /** Durable graph milestone that caused this host-authored supervisor turn. */ |
| agentGraphWakeId?: string; |
| /** Durable delivery attempt for this host-authored supervisor turn. */ |
| agentGraphWakeAttemptId?: string; |
| /** Positive identity for a host-authored root that has no message lineage. */ |
| rootExecutionKind?: 'context_compact'; |
| failureClass?: string; |
| failureMessage?: string; |
| abortSource?: string; |
| traceWriteError?: string; |
| } |
| |
| type HostedRootExecutionDescriptor = Extract< |
| RootExecutionDescriptor, |
| { |
| kind: |
| | 'regenerate' |
| | 'context_compact' |
| | 'automation' |
| | 'goal' |
| | 'agent_graph_supervisor_wake' |
| | 'safe_boundary_continuation'; |
| } |
| >; |
| |
| export function agentRunMatchesHostedRootExecution( |
| run: AgentRunHeader, |
| execution: HostedRootExecutionDescriptor, |
| ): boolean { |
| if (execution.kind !== 'context_compact' && run.rootExecutionKind !== undefined) return false; |
| if (execution.kind === 'regenerate') { |
| return ( |
| run.parentTurnId === execution.sourceTurnId && |
| run.regeneratedFromTurnId === execution.sourceTurnId && |
| run.parentRunId === undefined && |
| run.resumedFromRunId === undefined && |
| run.retriedFromRunId === undefined && |
| run.agentId === undefined && |
| run.agentName === undefined && |
| run.retriedFromTurnId === undefined && |
| run.branchOfTurnId === undefined && |
| run.parentSessionId === undefined && |
| run.continuationSource === undefined && |
| run.automationId === undefined && |
| run.goalId === undefined && |
| run.agentGraphWakeId === undefined && |
| run.agentGraphWakeAttemptId === undefined |
| ); |
| } |
| if (execution.kind === 'context_compact') { |
| return ( |
| run.rootExecutionKind === 'context_compact' && |
| run.parentTurnId === undefined && |
| run.regeneratedFromTurnId === undefined && |
| run.parentRunId === undefined && |
| run.resumedFromRunId === undefined && |
| run.retriedFromRunId === undefined && |
| run.agentId === undefined && |
| run.agentName === undefined && |
| run.retriedFromTurnId === undefined && |
| run.branchOfTurnId === undefined && |
| run.parentSessionId === undefined && |
| run.continuationSource === undefined && |
| run.automationId === undefined && |
| run.goalId === undefined && |
| run.agentGraphWakeId === undefined && |
| run.agentGraphWakeAttemptId === undefined |
| ); |
| } |
| if (execution.kind === 'safe_boundary_continuation') { |
| const source = run.continuationSource; |
| return ( |
| run.invocationId === execution.targetInvocationId && |
| run.parentRunId === execution.sourceRunId && |
| run.parentTurnId === execution.sourceTurnId && |
| source !== undefined && |
| 'protocol' in source && |
| source.protocol === 'continuation_source_v2' && |
| source.sourceInvocationId === execution.sourceInvocationId && |
| source.sourceRunId === execution.sourceRunId && |
| source.sourceTurnId === execution.sourceTurnId && |
| source.sourceRuntimeEventHighWater === execution.sourceRuntimeEventHighWater && |
| source.claimId === execution.claimId && |
| source.boundaryDigest === execution.boundaryDigest && |
| source.replayManifestDigest === execution.boundaryDigest && |
| run.resumedFromRunId === undefined && |
| run.retriedFromRunId === undefined && |
| run.agentId === undefined && |
| run.agentName === undefined && |
| run.retriedFromTurnId === undefined && |
| run.regeneratedFromTurnId === undefined && |
| run.branchOfTurnId === undefined && |
| run.parentSessionId === undefined && |
| run.automationId === undefined && |
| run.goalId === undefined && |
| run.agentGraphWakeId === undefined && |
| run.agentGraphWakeAttemptId === undefined |
| ); |
| } |
| const authorityMatches = hostedRootAuthorityMatches(run, execution); |
| return ( |
| authorityMatches && |
| run.parentRunId === undefined && |
| run.resumedFromRunId === undefined && |
| run.retriedFromRunId === undefined && |
| run.agentId === undefined && |
| run.agentName === undefined && |
| run.parentTurnId === undefined && |
| run.retriedFromTurnId === undefined && |
| run.regeneratedFromTurnId === undefined && |
| run.branchOfTurnId === undefined && |
| run.parentSessionId === undefined && |
| run.continuationSource === undefined |
| ); |
| } |
| |
| function hostedRootAuthorityMatches( |
| run: AgentRunHeader, |
| execution: Exclude< |
| HostedRootExecutionDescriptor, |
| { kind: 'regenerate' | 'context_compact' | 'safe_boundary_continuation' } |
| >, |
| ): boolean { |
| switch (execution.kind) { |
| case 'automation': |
| return ( |
| run.automationId === execution.automationId && |
| run.goalId === undefined && |
| run.agentGraphWakeId === undefined && |
| run.agentGraphWakeAttemptId === undefined |
| ); |
| case 'goal': |
| return ( |
| run.goalId === execution.goalId && |
| run.automationId === undefined && |
| run.agentGraphWakeId === undefined && |
| run.agentGraphWakeAttemptId === undefined |
| ); |
| case 'agent_graph_supervisor_wake': |
| return ( |
| execution.wakeId.startsWith(`${execution.graphId}:`) && |
| run.agentGraphWakeId === execution.wakeId && |
| run.agentGraphWakeAttemptId === execution.attemptId && |
| run.orchestrationMode === 'graph' && |
| run.orchestrationSource === 'turn_override' && |
| run.agentSwarmAuthorization === 'none' && |
| run.automationId === undefined && |
| run.goalId === undefined |
| ); |
| } |
| } |
| |
| export interface AgentRunInputSummary { |
| textLength: number; |
| attachmentCount: number; |
| } |
| |
| export const AGENT_RUN_EVENT_TYPES = [ |
| 'run_created', |
| 'run_started', |
| 'turn_started', |
| 'sandbox_context_resolved', |
| 'plan_context_resolved', |
| 'plan_submitted', |
| 'plan_execution_started', |
| 'plan_progress_updated', |
| 'plan_execution_completed', |
| 'plan_execution_cancelled', |
| 'plan_execution_interrupted', |
| 'plan_execution_resumed', |
| 'plan_transition_failed', |
| 'graph_supervisor_yielded', |
| 'run_status_changed', |
| 'model_resolved', |
| 'model_resolve_failed', |
| 'model_stream_started', |
| 'model_stream_completed', |
| 'model_stream_failed', |
| 'send_diagnostics_recorded', |
| 'tool_started', |
| 'tool_completed', |
| 'tool_failed', |
| 'skill_catalog_built', |
| 'skill_searched', |
| 'skill_loaded', |
| 'skill_load_failed', |
| 'permission_requested', |
| 'permission_decided', |
| 'permission_failed', |
| 'approval_routed', |
| 'auto_review_started', |
| 'auto_review_decided', |
| 'auto_review_failed', |
| 'sandbox_escalation_requested', |
| 'sandbox_escalation_granted', |
| 'sandbox_escalation_denied', |
| 'sandbox_escalation_applied', |
| 'sandbox_escalation_failed', |
| 'sandbox_denial_detected', |
| 'provider_request_captured', |
| 'provider_request_attempt_recorded', |
| 'model_call_attempt_recorded', |
| 'history_compact_checkpoint_recorded', |
| 'active_full_compact_block_recorded', |
| 'semantic_compact_block_recorded', |
| 'task_gate_decided', |
| 'abort_requested', |
| 'run_completed', |
| 'run_failed', |
| 'run_cancelled', |
| 'trace_write_failed', |
| 'event_corrupt', |
| ] as const; |
| |
| export type AgentRunEventType = (typeof AGENT_RUN_EVENT_TYPES)[number]; |
| |
| /** |
| * A decoded ledger record. The ledger is append-only and outlives any single build, so `type` is |
| * an open string: a reader must accept a type another version wrote, whether that version retired |
| * the writer or has not shipped yet (#1942). The envelope around `type` is still validated, so |
| * this tolerance does not extend to a record that gained or lost a field. |
| */ |
| export interface AgentRunEvent { |
| type: string; |
| id: string; |
| runId: string; |
| sessionId: string; |
| turnId: string; |
| ts: number; |
| message?: string; |
| data?: Record<string, unknown>; |
| } |
| |
| /** |
| * What this build may append. `AGENT_RUN_EVENT_TYPES` is the emitted catalogue, not the readable |
| * one, so it stays free to shrink when a writer retires while a misspelled or retired type fails |
| * to compile at the append that would persist it. |
| */ |
| export interface EmittedAgentRunEvent extends AgentRunEvent { |
| type: AgentRunEventType; |
| } |
| |
| const EMITTED_AGENT_RUN_EVENT_TYPES: ReadonlySet<string> = new Set(AGENT_RUN_EVENT_TYPES); |
| |
| /** Whether this build emits `type`, and so knows what its record means. */ |
| export function isEmittedAgentRunEventType(type: string): type is AgentRunEventType { |
| return EMITTED_AGENT_RUN_EVENT_TYPES.has(type); |
| } |
| |
| const AGENT_RUN_HEADER_SHAPE = defineObjectShape<AgentRunHeader>()( |
| [ |
| 'runId', |
| 'sessionId', |
| 'turnId', |
| 'status', |
| 'backendKind', |
| 'llmConnectionSlug', |
| 'modelId', |
| 'cwd', |
| 'permissionMode', |
| 'createdAt', |
| 'updatedAt', |
| ], |
| [ |
| 'invocationId', |
| 'completedAt', |
| 'parentRunId', |
| 'resumedFromRunId', |
| 'retriedFromRunId', |
| 'agentId', |
| 'agentName', |
| 'parentTurnId', |
| 'retriedFromTurnId', |
| 'regeneratedFromTurnId', |
| 'branchOfTurnId', |
| 'parentSessionId', |
| 'workspaceIdentity', |
| 'continuationSource', |
| 'automationId', |
| 'goalId', |
| 'agentGraphWakeId', |
| 'agentGraphWakeAttemptId', |
| 'rootExecutionKind', |
| 'failureClass', |
| 'failureMessage', |
| 'abortSource', |
| 'traceWriteError', |
| 'collaborationMode', |
| 'orchestrationMode', |
| 'orchestrationSource', |
| 'agentSwarmAuthorization', |
| 'toolMode', |
| ], |
| ); |
| |
| const AGENT_RUN_EVENT_SHAPE = defineObjectShape<AgentRunEvent>()( |
| ['type', 'id', 'runId', 'sessionId', 'turnId', 'ts'], |
| ['message', 'data'], |
| ); |
| |
| export function decodeAgentRunHeader(value: unknown): AgentRunHeader { |
| if (!isRecord(value) || !hasExactShape(value, AGENT_RUN_HEADER_SHAPE)) { |
| throw new Error('Invalid AgentRun header schema'); |
| } |
| const status = |
| value.status === 'waiting_permission' ? ('waiting_for_user' as const) : value.status; |
| const valid = |
| typeof value.runId === 'string' && |
| typeof value.sessionId === 'string' && |
| typeof value.turnId === 'string' && |
| (AGENT_RUN_STATUSES as readonly unknown[]).includes(status) && |
| isBackendKind(value.backendKind) && |
| typeof value.llmConnectionSlug === 'string' && |
| typeof value.modelId === 'string' && |
| typeof value.cwd === 'string' && |
| isPermissionMode(value.permissionMode) && |
| (value.collaborationMode === undefined || isCollaborationMode(value.collaborationMode)) && |
| (value.orchestrationMode === undefined || isOrchestrationMode(value.orchestrationMode)) && |
| (value.orchestrationSource === undefined || |
| isEffectiveOrchestrationSource(value.orchestrationSource)) && |
| (value.agentSwarmAuthorization === undefined || |
| isAgentSwarmAuthorizationSource(value.agentSwarmAuthorization)) && |
| (value.rootExecutionKind === undefined || value.rootExecutionKind === 'context_compact') && |
| !(value.automationId !== undefined && value.goalId !== undefined) && |
| (value.toolMode === undefined || isToolMode(value.toolMode)) && |
| isFiniteNumber(value.createdAt) && |
| isFiniteNumber(value.updatedAt) && |
| isOptionalString(value.invocationId) && |
| (value.completedAt === undefined || isFiniteNumber(value.completedAt)) && |
| [ |
| value.parentRunId, |
| value.resumedFromRunId, |
| value.retriedFromRunId, |
| value.agentId, |
| value.agentName, |
| value.parentTurnId, |
| value.retriedFromTurnId, |
| value.regeneratedFromTurnId, |
| value.branchOfTurnId, |
| value.parentSessionId, |
| value.workspaceIdentity, |
| value.automationId, |
| value.goalId, |
| value.agentGraphWakeId, |
| value.agentGraphWakeAttemptId, |
| value.failureClass, |
| value.failureMessage, |
| value.abortSource, |
| value.traceWriteError, |
| ].every(isOptionalString) && |
| (value.continuationSource === undefined || |
| isAgentRunContinuationSource(value.continuationSource)); |
| if (!valid) throw new Error('Invalid AgentRun header schema'); |
| if (status !== value.status) return { ...value, status } as unknown as AgentRunHeader; |
| return value as unknown as AgentRunHeader; |
| } |
| |
| function isAgentRunContinuationSource(value: unknown): value is AgentRunContinuationSource { |
| if (!isRecord(value)) return false; |
| const common = |
| typeof value.sourceInvocationId === 'string' && |
| typeof value.sourceRunId === 'string' && |
| typeof value.sourceTurnId === 'string' && |
| typeof value.sourceRuntimeEventHighWater === 'number' && |
| Number.isSafeInteger(value.sourceRuntimeEventHighWater) && |
| value.sourceRuntimeEventHighWater >= 0; |
| if (!common) return false; |
| if (hasExactShape(value, AGENT_RUN_CONTINUATION_SOURCE_V1_SHAPE)) return true; |
| return ( |
| hasExactShape(value, AGENT_RUN_CONTINUATION_SOURCE_V2_SHAPE) && |
| value.protocol === 'continuation_source_v2' && |
| typeof value.claimId === 'string' && |
| value.claimId.length > 0 && |
| typeof value.sourceInvocationId === 'string' && |
| value.sourceInvocationId.length > 0 && |
| typeof value.sourceRunId === 'string' && |
| value.sourceRunId.length > 0 && |
| typeof value.sourceTurnId === 'string' && |
| value.sourceTurnId.length > 0 && |
| typeof value.sourceRuntimeEventHighWater === 'number' && |
| value.sourceRuntimeEventHighWater > 0 && |
| isSha256Digest(value.boundaryDigest) && |
| isSha256Digest(value.sourcePrefixDigest) && |
| isSha256Digest(value.replayManifestDigest) && |
| value.replayManifestDigest === value.boundaryDigest |
| ); |
| } |
| |
| function isSha256Digest(value: unknown): value is `sha256:${string}` { |
| return typeof value === 'string' && /^sha256:[0-9a-f]{64}$/.test(value); |
| } |
| |
| export function decodeAgentRunEvent(value: unknown): AgentRunEvent { |
| if ( |
| !isRecord(value) || |
| !hasExactShape(value, AGENT_RUN_EVENT_SHAPE) || |
| typeof value.type !== 'string' || |
| value.type.trim().length === 0 || |
| typeof value.id !== 'string' || |
| typeof value.runId !== 'string' || |
| typeof value.sessionId !== 'string' || |
| typeof value.turnId !== 'string' || |
| !isFiniteNumber(value.ts) || |
| !isOptionalString(value.message) || |
| (value.data !== undefined && !isRecord(value.data)) |
| ) { |
| throw new Error('Invalid AgentRun event schema'); |
| } |
| return value as unknown as AgentRunEvent; |
| } |
| |
| function isBackendKind(value: unknown): value is BackendKind { |
| return value === 'ai-sdk' || value === 'fake' || value === 'pi-agent'; |
| } |
| |
| export interface AgentRunStore { |
| createRun(header: AgentRunHeader, options?: { durable?: boolean }): Promise<AgentRunHeader>; |
| updateRun( |
| sessionId: string, |
| runId: string, |
| patch: Partial<AgentRunHeader>, |
| options?: { durable?: boolean }, |
| ): Promise<AgentRunHeader>; |
| readRun(sessionId: string, runId: string): Promise<AgentRunHeader>; |
| listSessionRuns(sessionId: string): Promise<AgentRunHeader[]>; |
| appendEvent( |
| sessionId: string, |
| runId: string, |
| event: EmittedAgentRunEvent, |
| options?: { durable?: boolean }, |
| ): Promise<void>; |
| readEvents(sessionId: string, runId: string): Promise<AgentRunEvent[]>; |
| /** `undefined` means uninitialized; `null` is an initialized empty projection. */ |
| readEventProjection?( |
| sessionId: string, |
| type: AgentRunEventType, |
| ): Promise<AgentRunEvent | null | undefined>; |
| /** Rewrites derived state after the canonical event ledger repairs an absent or damaged projection. */ |
| repairEventProjection?( |
| sessionId: string, |
| type: AgentRunEventType, |
| event: AgentRunEvent | null, |
| options?: { replaceEventId?: string }, |
| ): Promise<void>; |
| } |
| |
| /** |
| * Whether a run contributes directly to the owning session's transcript. |
| * Top-level continuations carry parent lineage for recovery, but unlike |
| * child-agent runs their output remains part of the parent session |
| * conversation. A legacy child retry may also carry continuation authority; |
| * its agent identity keeps it outside the owning session transcript. |
| */ |
| export function isSessionInlineRun(run: { |
| readonly parentRunId?: string; |
| readonly continuationSource?: unknown; |
| readonly agentId?: string; |
| }): boolean { |
| return ( |
| run.parentRunId === undefined || |
| (run.continuationSource !== undefined && run.agentId === undefined) |
| ); |
| } |