blob: eb043762de0d48f14875f253f02368bc2d076524 [file]
import {
isPartialRuntimeEvent,
isTerminalRuntimeEvent,
runtimeEventHasModelVisibleContent,
type RuntimeEvent,
type RuntimeEventFunctionCallContent,
type RuntimeEventFunctionResponseContent,
} from '@maka/core/runtime-event';
import { resolveRuntimeRecovery, type RuntimeRecoveryResolution } from './recovery-resolver.js';
export type ToolOperationStatus =
| 'succeeded'
| 'failed'
| 'indeterminate'
| 'not_dispatched'
| 'parked'
| 'corruption';
export interface ToolOperation {
toolCallId: string;
toolName: string;
args: unknown;
status: ToolOperationStatus;
callRuntimeEventId: string;
responseRuntimeEventId?: string;
responseIsError?: boolean;
}
export type ResumePlanDisposition = 'safe_replay' | 'blocked';
export type RuntimeContinuationRevalidationCode =
| 'continuation_claim_conflict'
| 'target_run_conflict'
| 'source_identity_changed'
| 'source_terminal_changed'
| 'source_cwd_changed'
| 'source_high_water_changed'
| 'source_ledger_identity_changed'
| 'source_replay_changed'
| 'workspace_identity_changed'
| 'background_operation_started'
| 'tool_catalog_changed'
| 'workspace_checkpoint_changed';
export class RuntimeContinuationRevalidationError extends Error {
readonly code: RuntimeContinuationRevalidationCode;
constructor(code: RuntimeContinuationRevalidationCode, message: string) {
super(message);
this.name = 'RuntimeContinuationRevalidationError';
this.code = code;
}
}
export type ResumePlanDiagnosticCode =
| 'pending_tool_result'
| 'unmatched_tool_result'
| 'tool_name_mismatch'
| 'runtime_offset_mismatch'
| 'pending_permission'
| 'workspace_identity_mismatch'
| 'background_operation_pending'
| 'tool_catalog_mismatch'
| 'runtime_ledger_unreadable'
| 'terminal_repair_failed'
| 'workspace_cwd_mismatch'
| 'workspace_location_changed'
| 'runtime_ledger_empty'
| 'runtime_identity_mismatch'
| 'continuation_identity_reused'
| 'provider_resume_head_unsupported'
| 'provider_resume_boundary_unsupported'
| 'workspace_ref_missing'
| 'checkpoint_restore_failed'
| 'source_run_unreadable'
| 'continuation_already_exists'
| 'workspace_identity_missing'
| 'safety_observation_unavailable'
| 'resume_feature_disabled'
| 'resume_candidate_missing'
| 'tool_not_dispatched'
| 'tool_recovery_parked'
| 'tool_recovery_corruption'
| 'tool_ledger_corruption'
| 'duplicate_event_id'
| 'semantic_lane_conflict'
| 'protocol_marker_invalid';
export type ResumeRejectionReason =
| 'runtime_offset_mismatch'
| 'dangling_tool_state'
| 'pending_permission'
| 'workspace_identity_mismatch'
| 'background_operation_pending'
| 'tool_catalog_mismatch'
| 'runtime_ledger_unreadable'
| 'terminal_repair_failed'
| 'workspace_cwd_mismatch'
| 'runtime_ledger_empty'
| 'runtime_identity_mismatch'
| 'continuation_identity_reused'
| 'provider_resume_head_unsupported'
| 'provider_resume_boundary_unsupported'
| 'workspace_ref_missing'
| 'checkpoint_restore_failed'
| 'source_run_unreadable'
| 'continuation_already_exists'
| 'workspace_identity_missing'
| 'safety_observation_unavailable'
| 'resume_feature_disabled'
| 'resume_candidate_missing';
export interface ResumePlanDiagnostic {
code: ResumePlanDiagnosticCode;
message: string;
eventId?: string;
toolCallId?: string;
toolName?: string;
detail?: Record<string, unknown>;
}
export interface ResumePlan {
disposition: ResumePlanDisposition;
operations: ToolOperation[];
diagnostics: ResumePlanDiagnostic[];
rejectionReasons: ResumeRejectionReason[];
requiresVerification: boolean;
sourceRuntimeEventHighWater: number;
directive?: string;
runtimeEvents: RuntimeEvent[];
replayRuntimeEvents: RuntimeEvent[];
}
export interface BuildResumePlanOptions {
expectedRuntimeEventHighWater?: number;
}
export type RuntimeResumeFailpointId =
| 'P0'
| 'P1'
| 'P2'
| 'P3'
| 'P4'
| 'P5'
| 'P6'
| 'P7'
| 'P8'
| 'P9'
| 'P10'
| 'P11';
export type RuntimeResumeCommittedPrefix =
| 'before_function_call'
| 'after_function_call'
| 'after_function_response'
| 'after_terminal_event';
export interface RuntimeResumeFailpointSpec {
id: RuntimeResumeFailpointId;
boundary: string;
/** Last fully committed RuntimeEvent prefix that Phase 0 may inspect. */
committedPrefix: RuntimeResumeCommittedPrefix;
}
/**
* Stable crash-injection catalog owned by the Phase 0 process harness.
* Later phases may map these labels to richer boundaries, but this catalog
* only reasons about the last fully committed RuntimeEvent prefix.
*/
export const RUNTIME_RESUME_FAILPOINTS = [
{ id: 'P0', boundary: 'before tool preparation (T1)', committedPrefix: 'before_function_call' },
{
id: 'P1',
boundary: 'function_call committed before prepared journal',
committedPrefix: 'after_function_call',
},
{
id: 'P2',
boundary: 'prepared journal committed before implementation',
committedPrefix: 'after_function_call',
},
{ id: 'P3', boundary: 'tool implementation in progress', committedPrefix: 'after_function_call' },
{
id: 'P4',
boundary: 'side effect completed before outcome transaction (T2)',
committedPrefix: 'after_function_call',
},
{
id: 'P5',
boundary: 'function_response committed before outcome journal',
committedPrefix: 'after_function_response',
},
{
id: 'P6',
boundary: 'outcome transaction committed before model result delivery',
committedPrefix: 'after_function_response',
},
{
id: 'P7',
boundary: 'tool result delivered before the next provider step',
committedPrefix: 'after_function_response',
},
{
id: 'P8',
boundary: 'terminal RuntimeEvent commit',
committedPrefix: 'after_function_response',
},
{ id: 'P9', boundary: 'terminal run header commit', committedPrefix: 'after_terminal_event' },
{ id: 'P10', boundary: 'recovery decision commit', committedPrefix: 'after_terminal_event' },
{ id: 'P11', boundary: 'continuation run creation', committedPrefix: 'after_terminal_event' },
] as const satisfies readonly RuntimeResumeFailpointSpec[];
export interface ContinuationIdentity {
invocationId: string;
runId: string;
turnId: string;
}
export interface SafeBoundaryContinuationFacts {
ledgerReadable: boolean;
terminalRepairSucceeded: boolean;
sourceCwd: string;
currentCwd: string;
sourceWorkspaceIdentity: string;
currentWorkspaceIdentity: string;
backgroundOperationsSettled: boolean;
availableToolNames: readonly string[];
continuationIdentity: ContinuationIdentity;
/** User-anchored replay prefix inherited from continuation ancestors. */
priorRuntimeContext?: readonly RuntimeEvent[];
expectedRuntimeEventHighWater?: number;
workspaceCheckpoint?: {
ref?: string;
restored: boolean;
runtimeEventHighWater: number;
};
}
export interface RuntimeContinuation {
sessionId: string;
invocationId: string;
runId: string;
turnId: string;
sourceInvocationId: string;
sourceRunId: string;
sourceTurnId: string;
sourceRuntimeEventHighWater: number;
/** Replay events owned by the immediate source run. */
sourceRuntimeContext?: RuntimeEvent[];
/** Full user-anchored provider history, including continuation ancestors. */
runtimeContext: RuntimeEvent[];
safetySnapshot: RuntimeContinuationSafetySnapshot;
}
export interface RuntimeContinuationSafetySnapshot {
workspaceIdentity: string;
backgroundOperationsSettled: true;
availableToolNames: string[];
workspaceCheckpoint?: {
ref: string;
runtimeEventHighWater: number;
};
}
export interface RuntimeContinuationSafetyObservation {
workspaceIdentity: string;
/** Current location is diagnostic only and never participates in identity. */
workspacePath?: string;
backgroundOperationsSettled: boolean;
availableToolNames: readonly string[];
workspaceCheckpoint?: {
ref?: string;
restored: boolean;
runtimeEventHighWater: number;
};
}
export interface SafeBoundaryContinuationPlan {
disposition: 'continue' | 'park';
rejectionReasons: ResumeRejectionReason[];
diagnostics: ResumePlanDiagnostic[];
continuation?: RuntimeContinuation;
}
export interface RuntimeContinuationPlannerInput {
sessionId: string;
sourceRunId: string;
currentCwd: string;
sourceWorkspaceIdentity: string;
currentWorkspaceIdentity: string;
backgroundOperationsSettled: boolean;
availableToolNames: readonly string[];
expectedRuntimeEventHighWater?: number;
workspaceCheckpoint?: SafeBoundaryContinuationFacts['workspaceCheckpoint'];
}
export interface RuntimeContinuationPlannerDeps {
readSourceRun(
sessionId: string,
runId: string,
): Promise<{
cwd: string;
status: string;
continuationSource?: {
sourceInvocationId: string;
sourceRunId: string;
sourceTurnId: string;
sourceRuntimeEventHighWater: number;
};
}>;
readRuntimeEvents(sessionId: string, runId: string): Promise<RuntimeEvent[]>;
findExistingContinuation?(
sessionId: string,
sourceRunId: string,
sourceRuntimeEventHighWater: number,
): Promise<{ runId: string } | undefined>;
newId(): string;
}
export class RuntimeContinuationPlanner {
constructor(private readonly deps: RuntimeContinuationPlannerDeps) {}
async plan(input: RuntimeContinuationPlannerInput): Promise<SafeBoundaryContinuationPlan> {
let sourceRun: Awaited<ReturnType<RuntimeContinuationPlannerDeps['readSourceRun']>>;
try {
sourceRun = await this.deps.readSourceRun(input.sessionId, input.sourceRunId);
} catch {
return parkedPlan('source_run_unreadable', 'source AgentRun could not be read');
}
let events: RuntimeEvent[];
try {
events = await this.deps.readRuntimeEvents(input.sessionId, input.sourceRunId);
} catch {
return parkedPlan(
'runtime_ledger_unreadable',
'RuntimeEvent ledger could not be read reliably',
);
}
if (
events.some(
(event) => event.sessionId !== input.sessionId || event.runId !== input.sourceRunId,
)
) {
return parkedPlan(
'runtime_identity_mismatch',
'RuntimeEvent ledger does not belong to the requested source run',
);
}
let priorRuntimeContext: RuntimeEvent[] = [];
try {
priorRuntimeContext = await this.readPriorRuntimeContext(
input.sessionId,
sourceRun.continuationSource,
);
} catch {
return parkedPlan(
'runtime_ledger_unreadable',
'continuation ancestor RuntimeEvent ledger could not be read reliably',
);
}
const existingContinuation = await this.deps.findExistingContinuation?.(
input.sessionId,
input.sourceRunId,
events.length,
);
if (existingContinuation) {
return parkedPlan(
'continuation_already_exists',
'source run already has a continuation child',
{ continuationRunId: existingContinuation.runId },
);
}
return buildSafeBoundaryContinuationPlan(events, {
ledgerReadable: true,
terminalRepairSucceeded: hasConsistentTerminalBoundary(sourceRun.status, events),
sourceCwd: sourceRun.cwd,
currentCwd: input.currentCwd,
sourceWorkspaceIdentity: input.sourceWorkspaceIdentity,
currentWorkspaceIdentity: input.currentWorkspaceIdentity,
backgroundOperationsSettled: input.backgroundOperationsSettled,
availableToolNames: input.availableToolNames,
continuationIdentity: {
invocationId: this.deps.newId(),
runId: this.deps.newId(),
turnId: this.deps.newId(),
},
...(priorRuntimeContext.length > 0 ? { priorRuntimeContext } : {}),
...(input.expectedRuntimeEventHighWater !== undefined
? { expectedRuntimeEventHighWater: input.expectedRuntimeEventHighWater }
: {}),
...(input.workspaceCheckpoint !== undefined
? { workspaceCheckpoint: input.workspaceCheckpoint }
: {}),
});
}
private async readPriorRuntimeContext(
sessionId: string,
initial: Awaited<
ReturnType<RuntimeContinuationPlannerDeps['readSourceRun']>
>['continuationSource'],
): Promise<RuntimeEvent[]> {
const segments: RuntimeEvent[][] = [];
const seen = new Set<string>();
let source = initial;
while (source) {
const current = source;
if (seen.has(current.sourceRunId)) throw new Error('continuation source cycle');
seen.add(current.sourceRunId);
const [run, events] = await Promise.all([
this.deps.readSourceRun(sessionId, current.sourceRunId),
this.deps.readRuntimeEvents(sessionId, current.sourceRunId),
]);
if (events.length < current.sourceRuntimeEventHighWater) {
throw new Error('continuation ancestor high-water is unavailable');
}
const prefix = events.slice(0, current.sourceRuntimeEventHighWater);
if (
prefix.some(
(event) =>
event.sessionId !== sessionId ||
event.invocationId !== current.sourceInvocationId ||
event.runId !== current.sourceRunId ||
event.turnId !== current.sourceTurnId,
)
) {
throw new Error('continuation ancestor identity mismatch');
}
segments.unshift(buildResumeReplayRuntimeEvents(prefix));
source = run.continuationSource;
}
return segments.flat();
}
}
function isTerminalRunStatus(status: string): boolean {
return status === 'completed' || status === 'failed' || status === 'cancelled';
}
function hasConsistentTerminalBoundary(
runStatus: string,
events: readonly RuntimeEvent[],
): boolean {
if (!isTerminalRunStatus(runStatus)) return false;
const terminalEvents = events.filter(
(event) => !isPartialRuntimeEvent(event) && isTerminalRuntimeEvent(event),
);
if (terminalEvents.length !== 1) return false;
const eventStatus = terminalEvents[0]?.status;
if (eventStatus === 'aborted' || eventStatus === 'cancelled') {
return runStatus === 'cancelled';
}
return eventStatus === runStatus;
}
export const INDETERMINATE_TOOL_RESULT_DIRECTIVE = [
'Tool execution was interrupted before a matching committed tool result was found.',
'The side effects may or may not have occurred.',
'Do not retry the tool call immediately.',
'Use read-only inspection tools to verify the current state before deciding the next step.',
].join(' ');
export function projectToolOperationsFromRuntimeEvents(
events: readonly RuntimeEvent[],
): ToolOperation[] {
return projectToolOperations(events, resolveRuntimeRecovery(events));
}
function projectToolOperations(
events: readonly RuntimeEvent[],
recovery: RuntimeRecoveryResolution,
): ToolOperation[] {
const callsByEventId = new Map(
events.flatMap((event) =>
event.content?.kind === 'function_call' ? [[event.id, event.content] as const] : [],
),
);
return recovery.decisions.flatMap((decision) => {
if (!decision.callRuntimeEventId) return [];
const call = callsByEventId.get(decision.callRuntimeEventId);
if (!call) return [];
const status: ToolOperationStatus =
decision.status === 'completed'
? decision.responseIsError
? 'failed'
: 'succeeded'
: decision.status === 'definitely_not_dispatched'
? 'not_dispatched'
: decision.status;
return [
{
toolCallId: decision.toolCallId,
toolName: call.name,
args: call.args,
status,
callRuntimeEventId: decision.callRuntimeEventId,
...(decision.responseRuntimeEventId
? { responseRuntimeEventId: decision.responseRuntimeEventId }
: {}),
...(decision.responseIsError !== undefined
? { responseIsError: decision.responseIsError }
: {}),
},
];
});
}
export function buildResumePlanFromRuntimeEvents(
events: readonly RuntimeEvent[],
options: BuildResumePlanOptions = {},
): ResumePlan {
const recovery = resolveRuntimeRecovery(events);
const operations = projectToolOperations(events, recovery);
const sourceRuntimeEventHighWater = events.length;
const diagnostics = collectResumeDiagnostics(events, operations, options, recovery);
const rejectionReasons = deriveRejectionReasons(diagnostics);
const requiresVerification = operations.some((operation) => operation.status === 'indeterminate');
const disposition: ResumePlanDisposition =
rejectionReasons.length === 0 && !requiresVerification && !recovery.hasCorruption
? 'safe_replay'
: 'blocked';
return {
disposition,
operations,
diagnostics,
rejectionReasons,
requiresVerification,
sourceRuntimeEventHighWater,
...(requiresVerification ? { directive: INDETERMINATE_TOOL_RESULT_DIRECTIVE } : {}),
runtimeEvents: [...events],
replayRuntimeEvents: buildResumeReplayRuntimeEvents(events),
};
}
export function buildResumeReplayRuntimeEvents(events: readonly RuntimeEvent[]): RuntimeEvent[] {
const pairedCallIds = collectPairedCallIds(events);
const replayEvents: RuntimeEvent[] = [];
for (const event of events) {
if (isPartialRuntimeEvent(event)) continue;
const content = event.content;
if (!content) {
replayEvents.push(event);
continue;
}
if (content.kind === 'function_call') {
if (pairedCallIds.has(content.id)) replayEvents.push(event);
continue;
}
if (content.kind === 'function_response') {
if (pairedCallIds.has(content.id)) replayEvents.push(event);
continue;
}
replayEvents.push(event);
}
return replayEvents;
}
export function buildSafeBoundaryContinuationPlan(
events: readonly RuntimeEvent[],
facts: SafeBoundaryContinuationFacts,
): SafeBoundaryContinuationPlan {
const expectedRuntimeEventHighWater =
facts.workspaceCheckpoint?.runtimeEventHighWater ?? facts.expectedRuntimeEventHighWater;
const replayPlan = buildResumePlanFromRuntimeEvents(events, {
...(expectedRuntimeEventHighWater !== undefined ? { expectedRuntimeEventHighWater } : {}),
});
const phaseOneDiagnostics = collectPendingPermissionDiagnostics(events);
const phaseOneRejectionReasons: ResumeRejectionReason[] = [];
if (phaseOneDiagnostics.length > 0) phaseOneRejectionReasons.push('pending_permission');
const source = events[0];
if (!source) {
phaseOneDiagnostics.push({
code: 'runtime_ledger_empty',
message: 'safe-boundary continuation requires at least one RuntimeEvent',
});
phaseOneRejectionReasons.push('runtime_ledger_empty');
} else {
const mismatchedEvent = events.find(
(event) =>
event.sessionId !== source.sessionId ||
event.invocationId !== source.invocationId ||
event.runId !== source.runId ||
event.turnId !== source.turnId,
);
if (mismatchedEvent) {
phaseOneDiagnostics.push({
code: 'runtime_identity_mismatch',
message: 'RuntimeEvent ledger contains more than one source execution identity',
eventId: mismatchedEvent.id,
});
phaseOneRejectionReasons.push('runtime_identity_mismatch');
}
if (
facts.continuationIdentity.invocationId === source.invocationId ||
facts.continuationIdentity.runId === source.runId ||
facts.continuationIdentity.turnId === source.turnId
) {
phaseOneDiagnostics.push({
code: 'continuation_identity_reused',
message: 'continuation must use fresh invocation, run, and turn identities',
});
phaseOneRejectionReasons.push('continuation_identity_reused');
}
}
if (!facts.ledgerReadable) {
phaseOneDiagnostics.push({
code: 'runtime_ledger_unreadable',
message: 'RuntimeEvent ledger could not be read reliably',
});
phaseOneRejectionReasons.push('runtime_ledger_unreadable');
}
if (!facts.terminalRepairSucceeded) {
phaseOneDiagnostics.push({
code: 'terminal_repair_failed',
message: 'source run terminal repair did not complete successfully',
});
phaseOneRejectionReasons.push('terminal_repair_failed');
}
if (normalizeCwd(facts.sourceCwd) !== normalizeCwd(facts.currentCwd)) {
phaseOneDiagnostics.push({
code: 'workspace_location_changed',
message: 'workspace location differs from the source resume boundary',
detail: { sourceCwd: facts.sourceCwd, currentCwd: facts.currentCwd },
});
}
if (facts.sourceWorkspaceIdentity !== facts.currentWorkspaceIdentity) {
phaseOneDiagnostics.push({
code: 'workspace_identity_mismatch',
message: 'current workspace identity differs from the source resume boundary',
detail: {
sourceWorkspaceIdentity: facts.sourceWorkspaceIdentity,
currentWorkspaceIdentity: facts.currentWorkspaceIdentity,
},
});
phaseOneRejectionReasons.push('workspace_identity_mismatch');
}
if (!facts.backgroundOperationsSettled) {
phaseOneDiagnostics.push({
code: 'background_operation_pending',
message: 'a background or child operation is not settled',
});
phaseOneRejectionReasons.push('background_operation_pending');
}
if (facts.workspaceCheckpoint) {
if (!facts.workspaceCheckpoint.ref) {
phaseOneDiagnostics.push({
code: 'workspace_ref_missing',
message: 'workspace checkpoint does not contain a restorable ref',
});
phaseOneRejectionReasons.push('workspace_ref_missing');
} else if (!facts.workspaceCheckpoint.restored) {
phaseOneDiagnostics.push({
code: 'checkpoint_restore_failed',
message: 'workspace checkpoint ref could not be restored',
detail: { workspaceRef: facts.workspaceCheckpoint.ref },
});
phaseOneRejectionReasons.push('checkpoint_restore_failed');
}
}
const modelRuntimeContext = [
...(facts.priorRuntimeContext ?? []),
...replayPlan.replayRuntimeEvents,
];
const availableToolNames = new Set(facts.availableToolNames);
const unavailableToolNames = [
...new Set(
modelRuntimeContext
.flatMap((event) => (event.content?.kind === 'function_call' ? [event.content.name] : []))
.filter((toolName) => !availableToolNames.has(toolName)),
),
].sort();
if (unavailableToolNames.length > 0) {
phaseOneDiagnostics.push({
code: 'tool_catalog_mismatch',
message: 'one or more tools from the source boundary are unavailable',
detail: { unavailableToolNames },
});
phaseOneRejectionReasons.push('tool_catalog_mismatch');
}
const firstModelVisibleEvent = modelRuntimeContext.find(runtimeEventHasModelVisibleContent);
if (
source &&
!phaseOneRejectionReasons.includes('runtime_identity_mismatch') &&
firstModelVisibleEvent?.role !== 'user'
) {
phaseOneDiagnostics.push({
code: 'provider_resume_head_unsupported',
message: 'provider replay must start at a user boundary for continuation',
...(firstModelVisibleEvent ? { eventId: firstModelVisibleEvent.id } : {}),
detail: { firstRole: firstModelVisibleEvent?.role ?? null },
});
phaseOneRejectionReasons.push('provider_resume_head_unsupported');
}
const lastModelVisibleEvent = findLastModelVisibleEvent(modelRuntimeContext);
if (
source &&
!phaseOneRejectionReasons.includes('runtime_identity_mismatch') &&
lastModelVisibleEvent?.role !== 'user' &&
lastModelVisibleEvent?.role !== 'tool'
) {
phaseOneDiagnostics.push({
code: 'provider_resume_boundary_unsupported',
message: 'provider replay must end at a user or tool boundary for continuation',
...(lastModelVisibleEvent ? { eventId: lastModelVisibleEvent.id } : {}),
detail: { lastRole: lastModelVisibleEvent?.role ?? null },
});
phaseOneRejectionReasons.push('provider_resume_boundary_unsupported');
}
if (replayPlan.disposition !== 'safe_replay' || phaseOneRejectionReasons.length > 0 || !source) {
return {
disposition: 'park',
rejectionReasons: [...replayPlan.rejectionReasons, ...phaseOneRejectionReasons],
diagnostics: [...replayPlan.diagnostics, ...phaseOneDiagnostics],
};
}
return {
disposition: 'continue',
rejectionReasons: [],
diagnostics: phaseOneDiagnostics,
continuation: {
sessionId: source.sessionId,
...facts.continuationIdentity,
sourceInvocationId: source.invocationId,
sourceRunId: source.runId,
sourceTurnId: source.turnId,
sourceRuntimeEventHighWater: replayPlan.sourceRuntimeEventHighWater,
...(facts.priorRuntimeContext?.length
? { sourceRuntimeContext: replayPlan.replayRuntimeEvents }
: {}),
runtimeContext: modelRuntimeContext,
safetySnapshot: {
workspaceIdentity: facts.currentWorkspaceIdentity,
backgroundOperationsSettled: true,
availableToolNames: [...new Set(facts.availableToolNames)].sort(),
...(facts.workspaceCheckpoint?.ref
? {
workspaceCheckpoint: {
ref: facts.workspaceCheckpoint.ref,
runtimeEventHighWater: facts.workspaceCheckpoint.runtimeEventHighWater,
},
}
: {}),
},
},
};
}
function collectPendingPermissionDiagnostics(
events: readonly RuntimeEvent[],
): ResumePlanDiagnostic[] {
const pending = new Map<string, RuntimeEvent>();
for (const event of events) {
if (isPartialRuntimeEvent(event)) continue;
const request = event.actions?.permissionRequest;
if (request) pending.set(request.requestId, event);
const decision = event.actions?.permissionDecision;
if (decision) pending.delete(decision.requestId);
const accepted = event.actions?.permissionAnswerAccepted;
if (accepted) pending.delete(accepted.requestId);
const closed = event.actions?.permissionClosureAccepted;
if (closed) pending.delete(closed.requestId);
}
return [...pending.entries()].map(([requestId, event]) => ({
code: 'pending_permission',
message: 'permission request has no committed decision',
eventId: event.id,
detail: { requestId },
}));
}
function normalizeCwd(value: string): string {
const normalized = value.replaceAll('\\', '/').replace(/\/+$/, '');
return /^[A-Za-z]:\//.test(normalized) ? normalized.toLowerCase() : normalized;
}
function findLastModelVisibleEvent(events: readonly RuntimeEvent[]): RuntimeEvent | undefined {
for (let index = events.length - 1; index >= 0; index -= 1) {
const event = events[index];
if (event && runtimeEventHasModelVisibleContent(event)) return event;
}
return undefined;
}
function parkedPlan(
reason: ResumeRejectionReason & ResumePlanDiagnosticCode,
message: string,
detail?: Record<string, unknown>,
): SafeBoundaryContinuationPlan {
return {
disposition: 'park',
rejectionReasons: [reason],
diagnostics: [{ code: reason, message, ...(detail ? { detail } : {}) }],
};
}
function collectResumeDiagnostics(
events: readonly RuntimeEvent[],
operations: readonly ToolOperation[],
options: BuildResumePlanOptions,
recovery: RuntimeRecoveryResolution,
): ResumePlanDiagnostic[] {
const diagnostics: ResumePlanDiagnostic[] = [];
const operationsById = new Map(operations.map((operation) => [operation.toolCallId, operation]));
if (
options.expectedRuntimeEventHighWater !== undefined &&
options.expectedRuntimeEventHighWater !== events.length
) {
diagnostics.push({
code: 'runtime_offset_mismatch',
message: 'RuntimeEvent high-water does not match the expected checkpoint offset',
detail: {
expectedRuntimeEventHighWater: options.expectedRuntimeEventHighWater,
actualRuntimeEventHighWater: events.length,
},
});
}
for (const operation of operations) {
if (operation.status === 'indeterminate') {
diagnostics.push({
code: 'pending_tool_result',
message: 'function_call has no matching committed function_response',
eventId: operation.callRuntimeEventId,
toolCallId: operation.toolCallId,
toolName: operation.toolName,
});
} else if (operation.status === 'not_dispatched') {
diagnostics.push({
code: 'tool_not_dispatched',
message: 'function_call did not cross the durable tool dispatch boundary',
eventId: operation.callRuntimeEventId,
toolCallId: operation.toolCallId,
toolName: operation.toolName,
});
} else if (operation.status === 'corruption') {
diagnostics.push({
code: 'tool_recovery_corruption',
message: 'tool recovery facts conflict',
eventId: operation.callRuntimeEventId,
toolCallId: operation.toolCallId,
toolName: operation.toolName,
});
} else if (operation.status === 'parked') {
diagnostics.push({
code: 'tool_recovery_parked',
message: 'tool recovery reached a terminal parked decision',
eventId: operation.callRuntimeEventId,
toolCallId: operation.toolCallId,
toolName: operation.toolName,
});
}
}
for (const issue of recovery.issues) {
if (issue.code === 'protocol_marker_invalid') {
diagnostics.push({
code: issue.code,
message: 'runtime protocol marker is only valid on the first canonical event',
eventId: issue.eventId,
});
} else if (issue.code === 'duplicate_event_id') {
diagnostics.push({
code: issue.code,
message: 'immutable RuntimeEvent identity is duplicated',
eventId: issue.eventId,
});
} else if (issue.code === 'semantic_lane_conflict') {
diagnostics.push({
code: issue.code,
message: 'RuntimeEvent claims more than one authoritative tool semantic lane',
eventId: issue.eventId,
});
} else {
diagnostics.push({
code: 'tool_ledger_corruption',
message: `immutable tool ledger is corrupt: ${issue.code}`,
eventId: issue.eventId,
detail: { issueCode: issue.code },
});
}
}
for (const decision of recovery.decisions) {
if (
decision.status !== 'corruption' ||
decision.callRuntimeEventId ||
decision.reason === 'orphan_response'
)
continue;
diagnostics.push({
code: 'tool_recovery_corruption',
message: `tool recovery fact is corrupt: ${decision.reason}`,
eventId: decision.dispatchRuntimeEventId ?? decision.responseRuntimeEventId,
toolCallId: decision.toolCallId,
...(decision.toolName ? { toolName: decision.toolName } : {}),
});
}
for (const event of events) {
if (isPartialRuntimeEvent(event)) continue;
const content = event.content;
if (content?.kind !== 'function_response') continue;
const operation = operationsById.get(content.id);
if (!operation) {
diagnostics.push({
code: 'unmatched_tool_result',
message: 'function_response has no prior matching function_call',
eventId: event.id,
toolCallId: content.id,
toolName: content.name,
});
continue;
}
if (operation.toolName !== content.name) {
diagnostics.push({
code: 'tool_name_mismatch',
message: 'function_response tool name differs from matching function_call',
eventId: event.id,
toolCallId: content.id,
toolName: content.name,
detail: {
callToolName: operation.toolName,
responseToolName: content.name,
},
});
}
}
return diagnostics;
}
function deriveRejectionReasons(
diagnostics: readonly ResumePlanDiagnostic[],
): ResumeRejectionReason[] {
const reasons = new Set<ResumeRejectionReason>();
for (const diagnostic of diagnostics) {
switch (diagnostic.code) {
case 'runtime_offset_mismatch':
reasons.add('runtime_offset_mismatch');
break;
case 'pending_tool_result':
case 'tool_not_dispatched':
case 'tool_recovery_parked':
case 'tool_recovery_corruption':
case 'tool_ledger_corruption':
case 'duplicate_event_id':
case 'semantic_lane_conflict':
case 'protocol_marker_invalid':
case 'unmatched_tool_result':
case 'tool_name_mismatch':
reasons.add('dangling_tool_state');
break;
}
}
return [...reasons];
}
function collectPairedCallIds(events: readonly RuntimeEvent[]): Set<string> {
const calls = new Map<string, RuntimeEventFunctionCallContent>();
const paired = new Set<string>();
for (const event of events) {
if (isPartialRuntimeEvent(event)) continue;
const content = event.content;
if (content?.kind === 'function_call') {
calls.set(content.id, content);
continue;
}
if (content?.kind === 'function_response' && hasMatchingCall(calls.get(content.id), content)) {
paired.add(content.id);
}
}
return paired;
}
function hasMatchingCall(
call: RuntimeEventFunctionCallContent | undefined,
response: RuntimeEventFunctionResponseContent,
): boolean {
return call !== undefined && call.name === response.name;
}