@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
5,182 lines • 249 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RunState = exports.SerializedRunState = exports.SUPPORTED_SCHEMA_VERSIONS = exports.CURRENT_SCHEMA_VERSION = exports.buildAgentIdentityMap = void 0;
exports.getPendingSessionWriteAppendItems = getPendingSessionWriteAppendItems;
exports.recordPendingSessionWriteTerminalProducer = recordPendingSessionWriteTerminalProducer;
exports.clearPendingSessionWriteTerminalProducer = clearPendingSessionWriteTerminalProducer;
exports.assertPendingSessionWriteOwnership = assertPendingSessionWriteOwnership;
exports.assertPendingSessionCompactionTerminalAuthority = assertPendingSessionCompactionTerminalAuthority;
exports.capturePendingSessionWriteTerminalFinalization = capturePendingSessionWriteTerminalFinalization;
exports.rehydrateProcessedResponseTools = rehydrateProcessedResponseTools;
exports.buildAgentMap = buildAgentMap;
exports.deserializeSpan = deserializeSpan;
exports.deserializeModelResponse = deserializeModelResponse;
exports.deserializeItem = deserializeItem;
const zod_1 = require("zod");
const _shims_1 = require("@openai/agents-core/_shims");
const agent_1 = require("./agent.js");
const agentToolSourceRegistry_1 = require("./agentToolSourceRegistry.js");
const runStateIdentity_1 = require("./runStateIdentity.js");
const toolSearchAttribution_1 = require("./runner/toolSearchAttribution.js");
const runStateLegacyCompaction_1 = require("./runStateLegacyCompaction.js");
var runStateIdentity_2 = require("./runStateIdentity.js");
Object.defineProperty(exports, "buildAgentIdentityMap", { enumerable: true, get: function () { return runStateIdentity_2.buildAgentIdentityMap; } });
const items_1 = require("./items.js");
const runContext_1 = require("./runContext.js");
const items_2 = require("./runner/items.js");
const sessionItems_1 = require("./runner/sessionItems.js");
const conversation_1 = require("./runner/conversation.js");
const toolUseTracker_1 = require("./runner/toolUseTracker.js");
const steps_1 = require("./runner/steps.js");
const types_1 = require("./runner/types.js");
const blockedOutputPersistence_1 = require("./runner/blockedOutputPersistence.js");
const errors_1 = require("./errors.js");
const provider_1 = require("./tracing/provider.js");
const usage_1 = require("./usage.js");
const tracing_1 = require("./tracing/index.js");
const logger_1 = __importDefault(require("./logger.js"));
const protocol = __importStar(require("./types/protocol.js"));
const session_1 = require("./sandbox/session.js");
const safeExecute_1 = require("./utils/safeExecute.js");
const smartString_1 = require("./utils/smartString.js");
const tool_1 = require("./tool.js");
const toolIdentity_1 = require("./toolIdentity.js");
const toolSearch_1 = require("./utils/toolSearch.js");
const toolSearch_2 = require("./runner/toolSearch.js");
const modelPreparation_1 = require("./runner/modelPreparation.js");
const toolCaller_1 = require("./runner/toolCaller.js");
const toolRehydration_1 = require("./sandbox/runtime/toolRehydration.js");
const toolResultCorrelation_1 = require("./runner/toolResultCorrelation.js");
const toolInvocation_1 = require("./toolInvocation.js");
const sessionState_1 = require("./sandbox/runtime/sessionState.js");
/**
* The schema version of the serialized run state. This is used to ensure that the serialized
* run state is compatible with the current version of the SDK.
* If anything in this schema changes, the version will have to be incremented.
*
* Version history.
* - 1.0: Initial serialized RunState schema.
* - 1.1: Adds optional currentTurnInProgress, conversationId, and previousResponseId fields,
* plus broader tool_call_output_item rawItem variants for non-function tools. Older 1.0
* payloads remain readable but resumes may lack mid-turn or server-managed context precision.
* - 1.2: Adds pendingAgentToolRuns for nested agent tool resumption.
* - 1.3: Adds computer tool approval items to serialized tool_approval_item unions.
* - 1.4: Adds optional toolInput to serialized run context.
* - 1.5: Adds optional reasoningItemIdPolicy to preserve reasoning input policy across resume.
* - 1.6: Adds optional requestId to serialized model responses.
* - 1.7: Adds optional approval rejection messages.
* - 1.8: Adds tool search item variants, batched computer actions, and GA computer tool
* aliasing to serialized run state payloads.
* - 1.9: Adds optional sandbox session persistence with a versioned session-state
* envelope for sandbox-agent resume.
* - 1.10: Adds optional stable agent identity keys so duplicate-name agent graphs can
* serialize and resume without ambiguous name resolution.
* - 1.11: Allows null maxTurns to persist runs without a turn limit.
* - 1.12: Adds optional missing function tool calls to processed responses.
* - 1.13: Adds optional SDK-only customData on tool output run items.
* - 1.14: Adds Programmatic Tool Calling program items, outputs, caller linkage,
* and optional assistant message phases.
* - 1.15: Adds reconstructable sandbox environment value references and sandbox
* session-state envelope version 2.
* - 1.16: Adds compaction items, category-aware function tool keys, and agent-scoped
* function approvals, including aliases for legacy pending-run accessors.
* - 1.17: Adds durable local tool execution provenance and blocked-output session
* persistence bookkeeping.
* - 1.18: Adds sandbox session-state envelope version 3 so credential-redacted
* mount authority cannot be consumed by older SDKs, and binds per-call approvals
* and committed local tool results to canonical invocation fingerprints. It also
* scopes persistent hosted MCP approvals by server label and tool name, preserves
* JSON-compatible tool outputs as structured data, and adds durable pending input
* and accepted-response resume state.
* - 1.19: Lets an exact call approval decision override a sticky decision for the
* same canonical approval identity, and adds sandbox session-state envelope version 4
* so Docker network-isolation state cannot be consumed by older SDKs that would drop it
* during container replacement.
* - 1.20: Preserves resolved function identities on tool call items and adds sandbox
* session-state envelope version 5 so Docker labels cannot be
* consumed by older SDKs that would drop them during container replacement, preserves
* exact current-response ownership for serialized approval resumes, and checkpoints
* unacknowledged ordinary Session appends completed during approval resume, including
* filtered handoff input held until its source append settles.
* - 1.21: Re-evaluates conditional policies against refreshed normalized input
* on approval resume. Normalized values and comparison evidence are never serialized.
* Also preserves original local MCP recipients. Historical function calls without
* recipient provenance may be rejected or retained as completed, but cannot execute.
* Records completion of initial input guardrails. Started snapshots without this
* evidence require a fresh run rather than resuming unvalidated input.
* - 1.22: Records hosted MCP namespace discovery provenance. Older namespace-only
* search outputs require rediscovery; explicit MCP descriptors remain usable.
* - 1.23: Preserves nested numeric token usage breakdowns.
* - 1.24: Preserves arbitrary JSON token usage metadata, child-owned approval slots,
* and completion evidence for handoff failures returned to the source agent.
* Adds sandbox session-state envelope version 6 so client-owned
* unresolved environment references cannot be consumed by older SDKs that
* would resolve them in the runner process.
*/
exports.CURRENT_SCHEMA_VERSION = '1.24';
exports.SUPPORTED_SCHEMA_VERSIONS = [
'1.0',
'1.1',
'1.2',
'1.3',
'1.4',
'1.5',
'1.6',
'1.7',
'1.8',
'1.9',
'1.10',
'1.11',
'1.12',
'1.13',
'1.14',
'1.15',
'1.16',
'1.17',
'1.18',
'1.19',
'1.20',
'1.21',
'1.22',
'1.23',
exports.CURRENT_SCHEMA_VERSION,
];
const $schemaVersion = zod_1.z.enum(exports.SUPPORTED_SCHEMA_VERSIONS);
function schemaVersionSupportsV118State(schemaVersion) {
return (schemaVersion === '1.18' || schemaVersionSupportsV119State(schemaVersion));
}
function schemaVersionSupportsV119State(schemaVersion) {
return (schemaVersion === '1.19' || schemaVersionSupportsV120State(schemaVersion));
}
function schemaVersionSupportsV120State(schemaVersion) {
return (schemaVersion === '1.20' ||
schemaVersion === '1.21' ||
schemaVersion === '1.22' ||
schemaVersion === '1.23' ||
schemaVersion === exports.CURRENT_SCHEMA_VERSION);
}
function schemaVersionSupportsV116State(schemaVersion) {
return (schemaVersion === '1.16' ||
schemaVersion === '1.17' ||
schemaVersionSupportsV118State(schemaVersion));
}
function schemaVersionSupportsV117State(schemaVersion) {
return (schemaVersion === '1.17' || schemaVersionSupportsV118State(schemaVersion));
}
const pendingSessionHistoryTransactionSchema = zod_1.z
.object({
operationId: zod_1.z.string().min(1),
transactionKind: zod_1.z.enum(['blocked_append', 'accepted_replace']),
runItemIndexes: zod_1.z.array(zod_1.z.number().int().min(0)),
replaceRunItemIndexes: zod_1.z.array(zod_1.z.number().int().min(0)),
alreadyPersistedCount: zod_1.z.number().int().min(0),
persistedItemCount: zod_1.z.number().int().min(0),
deferredItemIndexes: zod_1.z.array(zod_1.z.number().int().min(0)),
})
.strict();
const pendingSessionWriteBaseSchema = {
sessionId: zod_1.z.string().min(1),
alreadyPersistedCount: zod_1.z.number().int().min(0),
persistedItemCount: zod_1.z.number().int().min(1),
reasoningItemIdPolicy: zod_1.z.enum(['preserve', 'omit']),
// Kept separately from canonical append items until the source turn is durable.
handoffInput: zod_1.z.lazy(() => handoffInputSnapshotSchema).optional(),
terminalToolFinalization: zod_1.z
.object({
behavior: zod_1.z.enum([
'stop_on_first_tool',
'stop_at_tool_names',
'function',
]),
selectedCallId: zod_1.z.string().min(1).optional(),
selectedGeneratedItemIndex: zod_1.z.number().int().min(0).optional(),
finalOutput: zod_1.z.string(),
})
.strict()
.optional(),
};
const pendingSessionWriteSchema = zod_1.z.discriminatedUnion('phase', [
zod_1.z
.object({
...pendingSessionWriteBaseSchema,
phase: zod_1.z.literal('prepared'),
})
.strict(),
zod_1.z
.object({
...pendingSessionWriteBaseSchema,
phase: zod_1.z.literal('append_ready'),
beforeItems: zod_1.z.array(protocol.ModelItem),
comparableAppendItems: zod_1.z.array(protocol.ModelItem).min(1),
})
.strict(),
zod_1.z
.object({
...pendingSessionWriteBaseSchema,
phase: zod_1.z.literal('compaction_pending'),
})
.strict(),
]);
function getPendingSessionWriteAppendItems(state, pending) {
const ownedItems = (0, sessionItems_1.normalizeItemsForSessionPersistence)((0, items_2.extractOutputItemsFromRunItems)(state._generatedItems.slice(pending.alreadyPersistedCount, pending.persistedItemCount), pending.reasoningItemIdPolicy));
if (ownedItems.length === 0) {
throw new errors_1.UserError('RunState pending Session write does not own a persistable generated item.');
}
const inputPrefix = state._currentTurnSessionHistoryTransactionInputItems ?? [];
if ((inputPrefix.length > 0 &&
state._currentTurnSessionHistoryTransactionSessionId === undefined) ||
(state._currentTurnSessionHistoryTransactionSessionId !== undefined &&
state._currentTurnSessionHistoryTransactionSessionId !==
pending.sessionId)) {
throw new errors_1.UserError('RunState pending Session write input is missing its transaction-aware Session binding.');
}
return (0, sessionItems_1.normalizeItemsForSessionPersistence)([...inputPrefix, ...ownedItems]);
}
const pendingSessionWriteTerminalProducers = new WeakMap();
function recordPendingSessionWriteTerminalProducer(state, producer) {
if (producer) {
pendingSessionWriteTerminalProducers.set(state, producer);
}
else {
pendingSessionWriteTerminalProducers.delete(state);
}
}
function clearPendingSessionWriteTerminalProducer(state) {
pendingSessionWriteTerminalProducers.delete(state);
}
function getLocalToolResultCallId(rawItem) {
switch (rawItem.type) {
case 'function_call_result':
case 'computer_call_result':
case 'shell_call_output':
case 'apply_patch_call_output':
return rawItem.callId;
default:
return undefined;
}
}
function isTrackedLocalToolResult(rawItem) {
return (rawItem.type === 'function_call_result' ||
rawItem.type === 'computer_call_result' ||
rawItem.type === 'shell_call_output' ||
rawItem.type === 'apply_patch_call_output');
}
function getSerializedLocalToolIdentity(toolCall, approvalNames, agent, functionToolStateKey) {
if (toolCall.type === 'function_call') {
const callIdentity = (0, toolIdentity_1.getFunctionToolStateKeyForCall)(toolCall, toolCall.name);
if (functionToolStateKey !== undefined) {
if (functionToolStateKey !== callIdentity &&
((0, toolIdentity_1.getToolCallNamespace)(toolCall) ||
functionToolStateKey !==
(0, toolIdentity_1.getFunctionToolLookupKey)(toolCall.name, toolCall.name))) {
throw new errors_1.UserError('RunState function tool identity does not match its call.');
}
return functionToolStateKey;
}
return callIdentity;
}
const callId = (0, toolInvocation_1.getToolInvocationCallId)(toolCall);
if (callId) {
const approvalName = approvalNames.get(callId);
if (approvalName) {
return approvalName;
}
}
const expectedType = toolCall.type === 'computer_call'
? 'computer'
: toolCall.type === 'shell_call'
? 'shell'
: 'apply_patch';
return agent.tools.find((tool) => tool.type === expectedType)?.name;
}
function toolResultMatchesCall(toolCall, result) {
const callCorrelation = (0, toolResultCorrelation_1.getToolResultCorrelationForCall)(toolCall);
const resultCorrelation = (0, toolResultCorrelation_1.getToolResultCorrelationForResult)(result);
if (!callCorrelation ||
!resultCorrelation ||
(0, toolResultCorrelation_1.getToolResultCorrelationKey)(callCorrelation) !==
(0, toolResultCorrelation_1.getToolResultCorrelationKey)(resultCorrelation)) {
return false;
}
const callCaller = (0, toolInvocation_1.getCanonicalToolCaller)(toolCall);
const resultCaller = (0, toolInvocation_1.getCanonicalToolCaller)(result);
if (callCaller.type !== resultCaller.type ||
(callCaller.type === 'program' &&
(resultCaller.type !== 'program' ||
callCaller.callerId !== resultCaller.callerId))) {
return false;
}
if (toolCall.type !== 'function_call') {
return true;
}
return (result.type === 'function_call_result' &&
result.name === toolCall.name &&
result.namespace === toolCall.namespace);
}
function toolItemsHaveSameCaller(left, right) {
const leftCaller = (0, toolInvocation_1.getCanonicalToolCaller)(left);
const rightCaller = (0, toolInvocation_1.getCanonicalToolCaller)(right);
return (leftCaller.type === rightCaller.type &&
(leftCaller.type !== 'program' ||
(rightCaller.type === 'program' &&
leftCaller.callerId === rightCaller.callerId)));
}
function getAgentInvocationMap(records, agent) {
const existing = records.get(agent);
if (existing) {
return existing;
}
const byCallId = new Map();
records.set(agent, byCallId);
return byCallId;
}
function getAgentAmbiguousCallIds(records, agent) {
const existing = records.get(agent);
if (existing) {
return existing;
}
const callIds = new Set();
records.set(agent, callIds);
return callIds;
}
function recordObservedToolInvocation(observed, ambiguous, agent, callId, fingerprint) {
const agentObserved = getAgentInvocationMap(observed, agent);
const previous = agentObserved.get(callId);
if (previous !== undefined && previous !== fingerprint) {
getAgentAmbiguousCallIds(ambiguous, agent).add(callId);
agentObserved.delete(callId);
}
else if (!ambiguous.get(agent)?.has(callId)) {
agentObserved.set(callId, fingerprint);
}
}
function serializeAgentInvocationRecords(records, agentIdentityKeys) {
const serialized = [];
for (const [agent, invocations] of records) {
const agentIdentity = agentIdentityKeys.get(agent);
if (agentIdentity && invocations.size > 0) {
serialized.push({
agentIdentity,
invocations: Object.fromEntries(invocations),
});
}
}
return serialized;
}
function serializeAgentCallIdSets(records, agentIdentityKeys) {
const serialized = [];
for (const [agent, callIds] of records) {
const agentIdentity = agentIdentityKeys.get(agent);
if (agentIdentity && callIds.size > 0) {
serialized.push({ agentIdentity, callIds: [...callIds].sort() });
}
}
return serialized;
}
function deserializeAgentInvocationRecords(records, agentsByIdentity) {
const deserialized = new Map();
const seenAgentIdentities = new Set();
for (const { agentIdentity, invocations } of records) {
const agent = agentsByIdentity.get(agentIdentity);
if (!agent || seenAgentIdentities.has(agentIdentity)) {
throw new errors_1.UserError('RunState contains invalid completed tool invocation ownership for the current agent graph.');
}
seenAgentIdentities.add(agentIdentity);
deserialized.set(agent, new Map(Object.entries(invocations)));
}
return deserialized;
}
function deserializeAgentCallIdSets(records, agentsByIdentity) {
const deserialized = new Map();
const seenAgentIdentities = new Set();
for (const { agentIdentity, callIds } of records) {
const agent = agentsByIdentity.get(agentIdentity);
if (!agent || seenAgentIdentities.has(agentIdentity)) {
throw new errors_1.UserError('RunState contains invalid ambiguous tool invocation ownership for the current agent graph.');
}
seenAgentIdentities.add(agentIdentity);
deserialized.set(agent, new Set(callIds));
}
return deserialized;
}
function inferCompletedToolInvocations(generatedItems) {
const approvalNames = new Map();
const pendingLocalCalls = new Map();
const pendingHostedMcpCalls = new Map();
const observed = new Map();
const completed = new Map();
const ambiguous = new Map();
const evidence = new Map();
// Completed calls retain their execution-time identity even when a handoff
// filter removes discovery history. Completion evidence is checked separately.
for (const item of generatedItems) {
if (item instanceof items_1.RunToolApprovalItem) {
const callId = (0, toolInvocation_1.getToolInvocationCallId)(item.rawItem);
if (!callId || !item.name) {
throw new errors_1.UserError('RunState contains a tool approval request without a canonical invocation identity.');
}
const toolName = item.functionToolStateKey ?? item.name;
getAgentInvocationMap(approvalNames, item.agent).set(callId, toolName);
const pending = pendingLocalCalls.get(item.agent)?.get(callId);
if (pending) {
if ((0, toolInvocation_1.getToolInvocationFingerprint)('', pending.toolCall) !==
(0, toolInvocation_1.getToolInvocationFingerprint)('', item.rawItem)) {
getAgentAmbiguousCallIds(ambiguous, item.agent).add(callId);
observed.get(item.agent)?.delete(callId);
}
else if (!completed.get(item.agent)?.has(callId)) {
observed.get(item.agent)?.delete(callId);
recordObservedToolInvocation(observed, ambiguous, item.agent, callId, (0, toolInvocation_1.getToolInvocationFingerprint)(toolName, pending.toolCall));
}
}
recordObservedToolInvocation(observed, ambiguous, item.agent, callId, (0, toolInvocation_1.getToolInvocationFingerprint)(toolName, item.rawItem));
if (item.rawItem.type === 'function_call' ||
item.rawItem.type === 'computer_call' ||
item.rawItem.type === 'shell_call' ||
item.rawItem.type === 'apply_patch_call') {
getAgentInvocationMap(pendingLocalCalls, item.agent).set(callId, {
toolCall: item.rawItem,
identityResolved: true,
evidence: pending ? [...pending.evidence, item] : [item],
});
}
continue;
}
if (item instanceof items_1.RunHandoffCallItem) {
const rawItem = item.rawItem;
const callId = (0, toolInvocation_1.getToolInvocationCallId)(rawItem);
if (!callId) {
throw new errors_1.UserError('RunState contains a handoff call without a non-empty call ID.');
}
const fingerprint = (0, toolInvocation_1.getToolInvocationFingerprint)((0, toolInvocation_1.getHandoffToolInvocationName)(rawItem.name), rawItem);
recordObservedToolInvocation(observed, ambiguous, item.agent, callId, fingerprint);
getAgentInvocationMap(pendingLocalCalls, item.agent).set(callId, {
toolCall: rawItem,
identityResolved: true,
evidence: [item],
});
continue;
}
if (item instanceof items_1.RunToolCallItem) {
const rawItem = item.rawItem;
if (rawItem.type === 'hosted_tool_call' &&
(rawItem.providerData?.type === 'mcp_approval_request' ||
rawItem.name === 'mcp_approval_request')) {
const providerData = rawItem.providerData;
if (typeof providerData?.id !== 'string' ||
providerData.id.length === 0 ||
typeof providerData.name !== 'string' ||
providerData.name.length === 0) {
throw new errors_1.UserError('RunState contains a hosted MCP approval request without a canonical invocation identity.');
}
recordObservedToolInvocation(observed, ambiguous, item.agent, providerData.id, (0, toolInvocation_1.getToolInvocationFingerprint)(providerData.name, rawItem));
getAgentInvocationMap(pendingHostedMcpCalls, item.agent).set(providerData.id, { toolCall: rawItem, evidence: [item] });
continue;
}
if (rawItem.type === 'hosted_tool_call' &&
rawItem.name === 'mcp_approval_response') {
const providerData = rawItem.providerData;
const callId = providerData?.approval_request_id;
if (typeof callId !== 'string' || callId.length === 0) {
throw new errors_1.UserError('RunState contains a hosted MCP approval response without a non-empty approval request ID.');
}
const pending = pendingHostedMcpCalls.get(item.agent)?.get(callId);
if (!pending || !toolItemsHaveSameCaller(pending.toolCall, rawItem)) {
getAgentAmbiguousCallIds(ambiguous, item.agent).add(callId);
observed.get(item.agent)?.delete(callId);
}
pendingHostedMcpCalls.get(item.agent)?.delete(callId);
const fingerprint = observed.get(item.agent)?.get(callId);
if (fingerprint !== undefined &&
!ambiguous.get(item.agent)?.has(callId)) {
getAgentInvocationMap(completed, item.agent).set(callId, fingerprint);
getAgentInvocationMap(evidence, item.agent).set(callId, [
...(pending?.evidence ?? []),
item,
]);
}
continue;
}
if (rawItem.type !== 'function_call' &&
rawItem.type !== 'computer_call' &&
rawItem.type !== 'shell_call' &&
rawItem.type !== 'apply_patch_call') {
continue;
}
const callId = (0, toolInvocation_1.getToolInvocationCallId)(rawItem);
if (!callId) {
throw new errors_1.UserError('RunState contains a local tool call without a non-empty call ID.');
}
const toolName = getSerializedLocalToolIdentity(rawItem, approvalNames.get(item.agent) ?? new Map(), item.agent, item.functionToolStateKey);
const agentPending = getAgentInvocationMap(pendingLocalCalls, item.agent);
const previousPending = agentPending.get(callId);
if (previousPending &&
(0, toolInvocation_1.getToolInvocationFingerprint)('', previousPending.toolCall) !==
(0, toolInvocation_1.getToolInvocationFingerprint)('', rawItem)) {
getAgentAmbiguousCallIds(ambiguous, item.agent).add(callId);
agentPending.delete(callId);
observed.get(item.agent)?.delete(callId);
continue;
}
if (!ambiguous.get(item.agent)?.has(callId)) {
agentPending.set(callId, {
toolCall: rawItem,
identityResolved: toolName !== undefined,
evidence: [item],
});
}
if (!toolName) {
continue;
}
const fingerprint = (0, toolInvocation_1.getToolInvocationFingerprint)(toolName, rawItem);
recordObservedToolInvocation(observed, ambiguous, item.agent, callId, fingerprint);
continue;
}
if (item instanceof items_1.RunToolCallOutputItem ||
item instanceof items_1.RunHandoffOutputItem) {
if (item instanceof items_1.RunHandoffOutputItem) {
const callId = item.rawItem.callId;
if (!callId) {
throw new errors_1.UserError('RunState contains a handoff output without a non-empty call ID.');
}
const pending = pendingLocalCalls.get(item.sourceAgent)?.get(callId);
if (!pending ||
!toolResultMatchesCall(pending.toolCall, item.rawItem)) {
getAgentAmbiguousCallIds(ambiguous, item.sourceAgent).add(callId);
observed.get(item.sourceAgent)?.delete(callId);
}
pendingLocalCalls.get(item.sourceAgent)?.delete(callId);
const fingerprint = observed.get(item.sourceAgent)?.get(callId);
if (fingerprint !== undefined &&
!ambiguous.get(item.sourceAgent)?.has(callId)) {
getAgentInvocationMap(completed, item.sourceAgent).set(callId, fingerprint);
getAgentInvocationMap(evidence, item.sourceAgent).set(callId, [
...(pending?.evidence ?? []),
item,
]);
}
continue;
}
if (!isTrackedLocalToolResult(item.rawItem)) {
continue;
}
const callId = getLocalToolResultCallId(item.rawItem);
if (!callId) {
throw new errors_1.UserError('RunState contains a local tool output without a non-empty call ID.');
}
const pending = pendingLocalCalls.get(item.agent)?.get(callId);
if (!pending ||
!pending.identityResolved ||
!toolResultMatchesCall(pending.toolCall, item.rawItem)) {
getAgentAmbiguousCallIds(ambiguous, item.agent).add(callId);
observed.get(item.agent)?.delete(callId);
}
pendingLocalCalls.get(item.agent)?.delete(callId);
const fingerprint = callId
? observed.get(item.agent)?.get(callId)
: undefined;
if (callId &&
fingerprint !== undefined &&
!ambiguous.get(item.agent)?.has(callId)) {
getAgentInvocationMap(completed, item.agent).set(callId, fingerprint);
getAgentInvocationMap(evidence, item.agent).set(callId, [
...(pending?.evidence ?? []),
item,
]);
}
}
}
return { completed, ambiguous, evidence };
}
function rebuildLegacyCompletedToolInvocations(state, generatedItems) {
const inferred = inferCompletedToolInvocations(generatedItems);
state._completedToolInvocations = inferred.completed;
state._ambiguousToolInvocationCallIds = inferred.ambiguous;
}
function getPerCallApprovalIds(record) {
return new Set([
...(Array.isArray(record.approved) ? record.approved : []),
...(Array.isArray(record.rejected) ? record.rejected : []),
]);
}
function validateCurrentApprovalInvocationRecords(context) {
const invocationRecords = new Map((context.approvalInvocations ?? []).map((record) => [
record.agentIdentity,
record.invocations,
]));
for (const record of context.functionApprovals ?? []) {
const invocations = invocationRecords.get(record.agentIdentity);
for (const approval of Object.values(record.approvals)) {
for (const callId of getPerCallApprovalIds(approval)) {
if (invocations?.[callId] === undefined) {
throw new errors_1.UserError('RunState contains a per-call function approval without its canonical invocation binding.');
}
}
}
}
for (const record of context.approvalInvocations ?? []) {
for (const approval of Object.values(record.approvals)) {
for (const callId of getPerCallApprovalIds(approval)) {
if (record.invocations[callId] === undefined) {
throw new errors_1.UserError('RunState contains a per-call tool approval without its canonical invocation binding.');
}
}
}
}
}
function isRecoveredHandoffEvidence(evidence) {
return (evidence.items[0] instanceof items_1.RunHandoffCallItem &&
evidence.items[1] instanceof items_1.RunToolCallOutputItem);
}
function validateToolInvocationCompletionEvidence(agent, callId, evidence) {
const [callItem, outputItem] = evidence.items;
const toolName = (0, toolInvocation_1.getToolInvocationNameFromFingerprint)(evidence.fingerprint);
let toolCall;
let expectedToolName;
let evidenceCallId;
let correlated = false;
if (callItem instanceof items_1.RunHandoffCallItem) {
toolCall = callItem.rawItem;
expectedToolName = (0, toolInvocation_1.getHandoffToolInvocationName)(toolCall.name);
evidenceCallId = toolCall.callId;
correlated =
callItem.agent === agent &&
((outputItem instanceof items_1.RunHandoffOutputItem &&
outputItem.sourceAgent === agent) ||
(outputItem instanceof items_1.RunToolCallOutputItem &&
outputItem.agent === agent &&
outputItem.executionStatus === 'executed' &&
outputItem.rawItem.type === 'function_call_result' &&
outputItem.rawItem.status === 'completed')) &&
toolResultMatchesCall(toolCall, outputItem.rawItem);
}
else if (callItem instanceof items_1.RunToolCallItem ||
callItem instanceof items_1.RunToolApprovalItem) {
const rawToolCall = callItem.rawItem;
if (rawToolCall.type === 'hosted_tool_call') {
toolCall = rawToolCall;
const providerData = toolCall.providerData;
const hostedToolName = typeof providerData?.name === 'string'
? providerData.name
: callItem instanceof items_1.RunToolApprovalItem
? callItem.name
: undefined;
expectedToolName = hostedToolName
? (0, toolInvocation_1.getHostedMcpApprovalToolName)(hostedToolName, toolCall)
: undefined;
evidenceCallId =
typeof providerData?.id === 'string'
? providerData.id
: (0, toolInvocation_1.getToolInvocationCallId)(toolCall);
const outputProviderData = outputItem instanceof items_1.RunToolCallItem &&
outputItem.rawItem.type === 'hosted_tool_call'
? outputItem.rawItem.providerData
: undefined;
correlated =
callItem.agent === agent &&
outputItem instanceof items_1.RunToolCallItem &&
outputItem.agent === agent &&
outputItem.rawItem.type === 'hosted_tool_call' &&
outputItem.rawItem.name === 'mcp_approval_response' &&
outputProviderData?.approval_request_id === callId &&
toolItemsHaveSameCaller(toolCall, outputItem.rawItem);
}
else if (rawToolCall.type === 'function_call' ||
rawToolCall.type === 'computer_call' ||
rawToolCall.type === 'shell_call' ||
rawToolCall.type === 'apply_patch_call') {
toolCall = rawToolCall;
expectedToolName =
toolCall.type === 'function_call'
? callItem instanceof items_1.RunToolApprovalItem
? (callItem.functionToolStateKey ?? callItem.name)
: callItem.functionToolStateKey !== undefined
? getSerializedLocalToolIdentity(toolCall, new Map(), agent, callItem.functionToolStateKey)
: (0, toolIdentity_1.getFunctionToolLegacyStateKeyFromStateKey)(toolName ?? '') ===
(0, toolIdentity_1.getFunctionToolLegacyStateKeyFromStateKey)((0, toolIdentity_1.getFunctionToolStateKeyForCall)(toolCall, toolCall.name) ??
'')
? toolName
: undefined
: callItem instanceof items_1.RunToolApprovalItem
? callItem.name
: toolName;
evidenceCallId = (0, toolInvocation_1.getToolInvocationCallId)(toolCall);
correlated =
callItem.agent === agent &&
outputItem instanceof items_1.RunToolCallOutputItem &&
outputItem.agent === agent &&
isTrackedLocalToolResult(outputItem.rawItem) &&
toolResultMatchesCall(toolCall, outputItem.rawItem);
}
else {
throw new errors_1.UserError('RunState completed tool invocation evidence contains an unsupported call item.');
}
}
else {
throw new errors_1.UserError('RunState completed tool invocation evidence is missing its canonical call item.');
}
if (!toolName ||
!expectedToolName ||
expectedToolName !== toolName ||
evidenceCallId !== callId ||
(0, toolInvocation_1.getToolInvocationFingerprint)(toolName, toolCall) !== evidence.fingerprint ||
!correlated) {
throw new errors_1.UserError('RunState completed tool invocation evidence is not a correlated canonical completion.');
}
return evidence.fingerprint;
}
function findToolInvocationCompletionEvidence(agent, callId, fingerprint, outputItem, candidates) {
for (let index = candidates.length - 1; index >= 0; index -= 1) {
const candidate = candidates[index];
if (candidate === outputItem) {
continue;
}
const evidence = {
fingerprint,
items: [candidate, outputItem],
};
try {
validateToolInvocationCompletionEvidence(agent, callId, evidence);
return evidence;
}
catch (error) {
if (!(error instanceof errors_1.UserError)) {
throw error;
}
}
}
return undefined;
}
function getCompletionOutputCallId(item, agent) {
if (item instanceof items_1.RunHandoffOutputItem) {
return item.sourceAgent === agent ? item.rawItem.callId : undefined;
}
if (item instanceof items_1.RunToolCallOutputItem) {
return item.agent === agent && isTrackedLocalToolResult(item.rawItem)
? getLocalToolResultCallId(item.rawItem)
: undefined;
}
if (item instanceof items_1.RunToolCallItem &&
item.agent === agent &&
item.rawItem.type === 'hosted_tool_call' &&
item.rawItem.name === 'mcp_approval_response') {
const providerData = item.rawItem.providerData;
return typeof providerData?.approval_request_id === 'string'
? providerData.approval_request_id
: undefined;
}
return undefined;
}
function validateGeneratedHistoryForCompletionEvidence(generatedItems, agent, callId, evidence) {
const outputItems = generatedItems.filter((item) => getCompletionOutputCallId(item, agent) === callId);
for (const outputItem of outputItems) {
if (!findToolInvocationCompletionEvidence(agent, callId, evidence.fingerprint, outputItem, [evidence.items[0], ...generatedItems])) {
throw new errors_1.UserError('RunState generated item history conflicts with completed tool invocation evidence.');
}
}
}
function validateCurrentCompletedToolInvocations(state, generatedItems, schemaVersion) {
const serializedCompleted = state._completedToolInvocations;
const inferred = inferCompletedToolInvocations(generatedItems);
const inferredCompleted = inferred.completed;
const evidenceCompleted = new Map();
for (const [agent, invocations] of state._completedToolInvocationEvidence) {
for (const [callId, evidence] of invocations) {
if (schemaVersion !== exports.CURRENT_SCHEMA_VERSION &&
isRecoveredHandoffEvidence(evidence)) {
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support recovered handoff evidence.`);
}
validateGeneratedHistoryForCompletionEvidence(generatedItems, agent, callId, evidence);
getAgentInvocationMap(evidenceCompleted, agent).set(callId, validateToolInvocationCompletionEvidence(agent, callId, evidence));
}
}
for (const [agent, invocations] of inferredCompleted) {
const serializedInvocations = serializedCompleted.get(agent);
for (const [callId, fingerprint] of invocations) {
if (serializedInvocations?.get(callId) !== fingerprint) {
throw new errors_1.UserError('RunState completed tool invocation records do not match the generated item history.');
}
}
}
for (const [agent, invocations] of serializedCompleted) {
const evidenceInvocations = evidenceCompleted.get(agent);
for (const [callId, fingerprint] of invocations) {
if (evidenceInvocations?.get(callId) !== fingerprint) {
throw new errors_1.UserError('RunState completed tool invocation records do not match correlated completion evidence.');
}
}
}
for (const [agent, invocations] of evidenceCompleted) {
const serializedInvocations = serializedCompleted.get(agent);
for (const [callId, fingerprint] of invocations) {
if (serializedInvocations?.get(callId) !== fingerprint) {
throw new errors_1.UserError('RunState completed tool invocation evidence does not match completed invocation authority.');
}
}
}
const inferredAmbiguous = inferred.ambiguous;
for (const [agent, callIds] of inferredAmbiguous) {
const serializedCallIds = state._ambiguousToolInvocationCallIds.get(agent);
for (const callId of callIds) {
if (evidenceCompleted.get(agent)?.has(callId)) {
continue;
}
if (!serializedCallIds?.has(callId)) {
throw new errors_1.UserError('RunState ambiguous tool invocation records do not match the generated item history.');
}
}
}
}
const serializedAgentSchema = zod_1.z.object({
name: zod_1.z.string(),
identity: zod_1.z.string().optional(),
});
const serializedSpanBase = zod_1.z.object({
object: zod_1.z.literal('trace.span'),
id: zod_1.z.string(),
trace_id: zod_1.z.string(),
parent_id: zod_1.z.string().nullable(),
started_at: zod_1.z.string().nullable(),
ended_at: zod_1.z.string().nullable(),
error: zod_1.z
.object({
message: zod_1.z.string(),
data: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
})
.nullable(),
span_data: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
});
const SerializedSpan = serializedSpanBase.extend({
previous_span: zod_1.z.lazy(() => SerializedSpan).optional(),
});
const requestUsageSchema = zod_1.z.object({
inputTokens: zod_1.z.number(),
outputTokens: zod_1.z.number(),
totalTokens: zod_1.z.number(),
inputTokensDetails: protocol.TokenDetails.optional(),
outputTokensDetails: protocol.TokenDetails.optional(),
endpoint: zod_1.z.string().optional(),
});
const usageSchema = zod_1.z.object({
requests: zod_1.z.number(),
inputTokens: zod_1.z.number(),
outputTokens: zod_1.z.number(),
totalTokens: zod_1.z.number(),
inputTokensDetails: zod_1.z.array(protocol.TokenDetails).optional(),
outputTokensDetails: zod_1.z.array(protocol.TokenDetails).optional(),
requestUsageEntries: zod_1.z.array(requestUsageSchema).optional(),
});
// Called only after SerializedRunState validates and bounds every details tree.
// Arrays and nonnumeric leaves need the JSON metadata format introduced in 1.24.
function isNumericTokenDetails(value) {
return (typeof value === 'number' ||
(value !== null &&
typeof value === 'object' &&
!Array.isArray(value) &&
Object.values(value).every(isNumericTokenDetails)));
}
function usageSupportsV123(usage) {
return ((usage.inputTokensDetails ?? []).every(isNumericTokenDetails) &&
(usage.outputTokensDetails ?? []).every(isNumericTokenDetails) &&
(usage.requestUsageEntries ?? []).every((entry) => isNumericTokenDetails(entry.inputTokensDetails ?? {}) &&
isNumericTokenDetails(entry.outputTokensDetails ?? {})));
}
const modelResponseSchema = zod_1.z.object({
usage: usageSchema,
output: zod_1.z.array(protocol.OutputModelItem),
responseId: zod_1.z.string().optional(),
requestId: zod_1.z.string().optional(),
providerData: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
});
const INVALID_JSON_COMPATIBLE_VALUE = Symbol('invalidJsonCompatibleValue');
function normalizeJsonCompatibleValue(value) {
try {
return normalizeJsonCompatibleValueInternal(value, new Set());
}
catch {
return INVALID_JSON_COMPATIBLE_VALUE;
}
}
function normalizeJsonCompatibleValueInternal(value, ancestors) {
if (value === null) {
return null;
}
if (typeof value === 'string' || typeof value === 'boolean') {
return value;
}
if (typeof value === 'number') {
if (!Number.isFinite(value)) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
return Object.is(value, -0) ? 0 : value;
}
if (typeof value !== 'object' || ancestors.has(value)) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
if (hasUnsupportedToJsonProtocol(value)) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
ancestors.add(value);
try {
if (Array.isArray(value)) {
if (Object.getOwnPropertySymbols(value).length > 0) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, 'length');
if (!lengthDescriptor || !('value' in lengthDescriptor)) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
const normalized = [];
for (let index = 0; index < lengthDescriptor.value; index += 1) {
const descriptor = Object.getOwnPropertyDescriptor(value, index);
if (!descriptor || !('value' in descriptor)) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
const entry = normalizeJsonCompatibleValueInternal(descriptor.value, ancestors);
if (entry === INVALID_JSON_COMPATIBLE_VALUE) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
normalized.push(entry);
}
return normalized;
}
const prototype = Object.getPrototypeOf(value);
if (prototype !== Object.prototype && prototype !== null) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
const entries = [];
for (const key of Reflect.ownKeys(value)) {
if (typeof key !== 'string') {
return INVALID_JSON_COMPATIBLE_VALUE;
}
const descriptor = Object.getOwnPropertyDescriptor(value, key);
if (!descriptor) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
if (!descriptor.enumerable) {
continue;
}
if (!('value' in descriptor)) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
const entry = normalizeJsonCompatibleValueInternal(descriptor.value, ancestors);
if (entry === INVALID_JSON_COMPATIBLE_VALUE) {
return INVALID_JSON_COMPATIBLE_VALUE;
}
entries.push([key, entry]);
}
return Object.fromEntries(entries);
}
finally {
ancestors.delete(value);
}
}
function hasUnsupportedToJsonProtocol(value) {
let current = value;
while (current !== null) {
const descriptor = Object.getOwnPropertyDescriptor(current, 'toJSON');
if (descriptor) {
return !('value' in descriptor) || typeof descriptor.value === 'function';
}
current = Object.getPrototypeOf(current);
}
return false;
}
const jsonCompatibleValueSchema = zod_1.z.preprocess(normalizeJsonCompatibleValue, zod_1.z.custom((value) => value !== INVALID_JSON_COMPATIBLE_VALUE, { message: 'Expected JSON-compatible data.' }));
const itemSchema = zod_1.z.discriminatedUnion('type', [
zod_1.z.object({
type: zod_1.z.literal('input_item'),
rawItem: protocol.ModelItem,
agent: serializedAgentSchema,
inputId: zod_1.z.string().min(1),
}),
zod_1.z.object({
type: zod_1.z.literal('message_output_item'),
rawItem: protocol.AssistantMessageItem,
agent: serializedAgentSchema,
}),
zod_1.z.object({
type: zod_1.z.literal('tool_search_call_item'),
rawItem: protocol.ToolSearchCallItem,
agent: serializedAgentSchema,
}),
zod_1.z.object({
type: zod_1.z.literal('tool_search_output_item'),
rawItem: protocol.ToolSearchOutputItem,
agent: serializedAgentSchema,
}),
zod_1.z.object({
type: zod_1.z.literal('tool_call_item'),
rawItem: protocol.ToolCallItem.or(protocol.HostedToolCallItem),
agent: serializedAgentSchema,
functionToolStateKey: zod_1.z.string().optional(),
}),
zod_1.z.object({
type: zod_1.z.literal('tool_call_output_item'),
rawItem: protocol.FunctionCallResultItem.or(protocol.ComputerCallResultItem)
.or(protocol.ShellCallResultItem)
.or(protocol.ApplyPatchCallResultItem)
.or(protocol.ProgramCallResultItem),
agent: serializedAgentSchema,
output: jsonCompatibleValueSchema,
customData: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
executionStatus: zod_1.z.literal('executed').optional(),
}),
zod_1.z.object({
type: zod_1.z.literal('reasoning_item'),
rawItem: protocol.ReasoningItem,
agent: serializedAgentSchema,
}),
zod_1.z.object({
type: zod_1.z.literal('compaction_item'),
rawItem: protocol.CompactionItem,
agent: serializedAgentSchema,
}),
zod_1.z.object({
type: zod_1.z.literal('handoff_call_item'),
rawItem: protocol.FunctionCallItem,
agent: serializedAgentSchema,
}),
zod_1.z.object({
type: zod_1.z.literal('handoff_output_item'),
rawItem: protocol.FunctionCallResultItem,
sourceAgent: serializedAgentSchema,
targetAgent: serializedAgentSchema,
}),
zod_1.z.object({
type: zod_1.z.literal('tool_approval_item'),
rawItem: protocol.FunctionCallItem.or(protocol.HostedToolCallItem)
.or(protocol.ComputerUseCallItem)
.or(protocol.ShellCallItem)
.or(protocol.ApplyPatchCallItem),
agent: serializedAgentSchema,
toolName: zod_1.z.string().optional(),
functionToolStateKey: zod_1.z.string().optional(),
}),
]);
const serializedToolInvocationCompletionEvidenceItemSchema = zod_1.z.union([
zod_1.z
.object({
generatedItemIndex: zod_1.z.number().int().min(0),
})
.strict(),
zod_1.z
.object({
item: itemSchema,
})
.strict(),
]);
const serializedToolInvocationCompletionEvidenceSchema = zod_1.z
.object({
fingerprint: zod_1.z.string(),
items: zod_1.z.tuple([
serializedToolInvocationCompletionEvidenceItemSchema,
serializedToolInvocationCompletionEvidenceItemSchema,
]),
})
.strict();
function serializeAgentInvocationEvidence(records, agentIdentityKeys, generatedItems) {
const serialized = [];
const generatedItemIndexes = new Map(generatedItems.map((item, index) => [item, index]));
for (const [agent, invocations] of records) {
const agentIdentity = agentIdentityKeys.get(agent);
if (!agentIdentity || invocations.size === 0) {
continue;
}
serialized.push({
agentIdentity,
invocations: Object.fromEntries([...invocations].map(([callId, evidence]) => [
callId,
{
fingerprint: evidence.fingerprint,
items: evidence.items.map((item) => {
const generatedItemIndex = generatedItemIndexes.get(item);
return generatedItemIndex === undefined
? { item: serializeRunItem(item, agentIdentityKeys) }
: { generatedItemIndex };
}),
},
])),
});
}
return serialized;
}
function deserializeAgentInvocationEvidence(records, agentsByIdentity, generatedItems) {
const deserialized = new Map();
const seenAgentIdentities = new Set();
for (const { agentIdentity, invocations } of records) {
const agent = agentsByIdentity.get(agentIdentity);
if (!agent || seenAgentIdentities.has(agentIdentity)) {
throw new errors_1.UserError('RunState contains invalid completed tool invocation evidence ownership for the current agent graph.');
}
seenAgentIdentities.add(agentIdentity);
deserialized.set(agent, new Map(Object.entries(invocations).map(([callId, evidence]) => [
callId,
{
fingerprint: evidence.fingerprint,
items: evidence.items.map((reference) => {
if ('generatedItemIndex' in reference) {
const generatedItem = generatedItems[reference.generatedItemIndex];
if (!generatedItem) {
throw new errors_1.UserError('RunState completed tool invocation evidence references a missing generated item.');
}
return generatedItem;
}
return deserializeItem(reference.item, agentsByIdentity);
}),
},
])));
}
return deserialized;
}
const serializedTraceSchema = zod_1.z.object({
object: zod_1.z.literal('trace'),
id: zod_1.z.string(),
workflow_name: zod_1.z.string(),
group_id: zod_1.z.string().nullable(),
metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
// Populated only if the trace was created with a per-run tracingApiKey (e.g., Runner.run({ tracing: { apiKey } }))
// and serialization opts in to include it. By default this is omitted to avoid persisting secrets.
tracing_api_key: zod_1.z.string().optional().nullable(),
});
const sandboxSessionStateEnvelopeSchema = zod_1.z
.object({
version: zod_1.z.union(session_1.SUPPORTED_SANDBOX_SESSION_STATE_VERSIONS.map((version) => zod_1.z.literal(version))),
backendId: zod_1.z.string(),
environmentPreparation: zod_1.z.literal('client').optional(),
manifest: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
snapshot: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).nullable().optional(),
snapshotFingerprint: zod_1.z.string().nullable().optional(),
snapshotFingerprintVersion: zod_1.z.string().nullable().optional(),
workspaceReady: zod_1.z.boolean(),
exposedPorts: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
providerState: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
})
.superRefine((envelope, context) => {
if (envelope.environmentPreparation === 'client' &&
envelope.version !== session_1.SANDBOX_SESSION_STATE_VERSION) {
context.addIssue({
code: 'custom',
message: 'Client-owned environment preparation requires the current sandbox session envelope version.',
});
}
});
const sandboxSessionEntrySchema = zod_1.z.object({
backendId: zod_1.z.string(),
currentAgentKey: zod_1.z.string(),
currentAgentName: zod_1.z.string(),
sessionState: sandboxSessionStateEnvelopeSchema,
preservedOwnedSession: zod_1.z.boolean().optional(),
reuseLiveSession: zod_1.z.boolean().optional(),
requiresFreshCreation: zod_1.z.boolean().optional(),
});
const sandboxStateSchema = zod_1.z.object({
backendId: zod_1.z.string(),
currentAgentKey: zod_1.z.string(),
currentAgentName: zod_1.z.string(),
sessionState: sandboxSessionStateEnvelopeSchema,
sessionsByAgent: zod_1.z.record(zod_1.z.string(), sandboxSessionEntrySchema),
});
const handoffInputSnapshotSchema = zod_1.z.object({
originalInput: zod_1.z.string().or(zod_1.z.array(protocol.ModelItem)),
generatedItems: zod_1.z.array(itemSchema),
});
const serializedProcessedResponseSchema = zod_1.z.object({
newItems: zod_1.z.array(itemSchema),
toolsUsed: zod_1.z.array(zod_1.z.string()),
handoffs: zod_1.z.array(zod_1.z.object({
toolCall: zod_1.z.any(),
handoff: zod_1.z.any(),
targetAgent: serializedAgentSchema.optional(),
})),
functions: zod_1.z.array(zod_1.z.object({
toolCall: zod_1.z.any(),
tool: zod_1.z.any(),
mcpToolBinding: zod_1.z
.union([
zod_1.z.object({
serverName: zod_1.z.string(),
toolName: zod_1.z.string(),
serverIndex: zod_1.z.number().int().nonnegative().nullable(),
}),
zod_1.z.null(),
zod_1.z.literal('unknown'),
])
.optional(),
})),
functionToolsNotFound: zod_1.z
.array(zod_1.z.object({
toolCall: zod_1.z.any(),
toolName: zod_1.z.string(),
// Describes the error only; persisted input cannot select search execution.
reason: zod_1.z.literal('not_loaded').optional(),
}))
.optional(),
computerActions: zod_1.z.array(zod_1.z.object({
toolCall: zod_1.z.any(),
computer: zod_1.z.any(),
})),
shellActions: zod_1.z
.array(zod_1.z.object({
toolCall: zod_1.z.any(),
shell: zod_1.z.any(),
}))
.optional(),
applyPatchActions: zod_1.z
.array(zod_1.z.object({
toolCall: zod_1.z.any(),
applyPatch: zod_1.z.any(),
}))
.optional(),
mcpApprovalRequests: zod_1.z
.array(zod_1.z.object({
requestItem: zod_1.z.object({
// protocol.HostedToolCallItem
rawItem: zod_1.z.object({
type: zod_1.z.literal('hosted_tool_call'),
name: zod_1.z.string(),
arguments: zod_1.z.string().optional(),
status: zod_1.z.string().optional(),
output: zod_1.z.string().optional(),
caller: protocol.ToolCaller.optional(),
// this always exists but marked as optional for early version compatibility; when releasing 1.0, we can remove the nullable and optional
providerData: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).nullable().optional(),
}),
}),
// HostedMCPTool
mcpTool: zod_1.z.object({
type: zod_1.z.literal('hosted_tool'),
name: zod_1.z.literal('hosted_mcp'),
providerData: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
}),
}))
.optional(),
});
const currentResponseGeneratedItemOwnershipSchema = zod_1.z
.object({
generatedItemStartIndex: zod_1.z.number().int().min(0),
generatedItemEndIndexExclusive: zod_1.z.number().int().min(0),
interruptionGeneratedItemIndexes: zod_1.z.array(zod_1.z.number().int().min(0).nullable()),
toolOutputGuardrailResultStartIndex: zod_1.z.number().int().min(0),
})
.strict();
const guardrailFunctionOutputSchema = zod_1.z.object({
tripwireTriggered: zod_1.z.boolean(),
outputInfo: zod_1.z.any().optional(),
});
const toolGuardrailBehaviorSchema = zod_1.z.discriminatedUnion('type', [
zod_1.z.object({ type: zod_1.z.literal('allow') }),
zod_1.z.object({
type: zod_1.z.literal('rejectContent'),
message: zod_1.z.string(),
}),
zod_1.z.object({ type: zod_1.z.literal('throwException') }),
]);
const toolGuardrailFunctionOutputSchema = zod_1.z.object({
outputInfo: zod_1.z.any().optional(),
behavior: toolGuardrailBehaviorSchema,
});
const toolGuardrailMetadataSchema = zod_1.z.object({
type: zod_1.z.union([zod_1.z.literal('tool_input'), zod_1.z.literal('tool_output')]),
name: zod_1.z.string(),
});
const inputGuardrailResultSchema = zod_1.z.object({
guardrail: zod_1.z.object({
type: zod_1.z.literal('input'),
name: zod_1.z.string(),
}),
output: guardrailFunctionOutputSchema,
});
const outputGuardrailResultSchema = zod_1.z.object({
guardrail: zod_1.z.object({
type: zod_1.z.literal('output'),
name: zod_1.z.string(),
}),
agentOutput: zod_1.z.any(),
agent: serializedAgentSchema,
output: guardrailFunctionOutputSchema,
});
const toolInputGuardrailResultSchema = zod_1.z.object({
guardrail: toolGuardrailMetadataSchema.extend({
type: zod_1.z.literal('tool_input'),
}),
output: toolGuardrailFunctionOutputSchema,
});
const toolOutputGuardrailResultSchema = zod_1.z.object({
guardrail: toolGuardrailMetadataSchema.extend({
type: zod_1.z.literal('tool_output'),
}),
output: toolGuardrailFunctionOutputSchema,
});
const approvalRecordSchema = zod_1.z.object({
approved: zod_1.z.array(zod_1.z.string()).or(zod_1.z.boolean()),
rejected: zod_1.z.array(zod_1.z.string()).or(zod_1.z.boolean()),
messages: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional(),
stickyRejectMessage: zod_1.z.string().optional(),
});
const canonicalFunctionToolStateKeySchema = zod_1.z
.string()
.refine((value) => (0, toolIdentity_1.getFunctionToolLegacyStateKeyFromStateKey)(value) !== undefined, 'Function approval keys must use a canonical category-aware identity.');
const functionApprovalsSchema = zod_1.z.array(zod_1.z.object({
agentIdentity: zod_1.z.string(),
approvals: zod_1.z.record(canonicalFunctionToolStateKeySchema, approvalRecordSchema),
}));
const functionApprovalContextSchema = zod_1.z.object({
functionApprovals: functionApprovalsSchema.optional(),
legacyFunctionApprovals: zod_1.z
.record(zod_1.z.string(), approvalRecordSchema)
.optional(),
approvalInvocations: zod_1.z
.array(zod_1.z.object({
agentIdentity: zod_1.z.string(),
invocations: zod_1.z.record(zod_1.z.string(), zod_1.z.string()),
approvals: zod_1.z.record(zod_1.z.string(), approvalRecordSchema),
}))
.optional(),
});
exports.SerializedRunState = zod_1.z.object({
$schemaVersion,
currentTurn: zod_1.z.number(),
currentAgent: serializedAgentSchema,
originalInput: zod_1.z.string().or(zod_1.z.array(protocol.ModelItem)),
pendingInput: zod_1.z.array(protocol.ModelItem).optional(),
modelResponses: zod_1.z.array(modelResponseSchema),
context: zod_1.z.object({
usage: usageSchema,
approvals: zod_1.z.record(zod_1.z.string(), approvalRecordSchema),
hostedMcpApprovals: zod_1.z.record(zod_1.z.string(), approvalRecordSchema).optional(),
functionApprovals: functionApprovalsSchema.optional(),
legacyFunctionApprovals: zod_1.z
.record(zod_1.z.string(), approvalRecordSchema)
.optional(),
approvalInvocations: zod_1.z
.array(zod_1.z.object({
agentIdentity: zod_1.z.string(),
invocations: zod_1.z.record(zod_1.z.string(), zod_1.z.string()),
approvals: zod_1.z.record(zod_1.z.string(), approvalRecordSchema),
}))
.optional(),
context: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
toolInput: zod_1.z.any().optional(),
}),
toolUseTracker: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string())),
maxTurns: zod_1.z.number().nullable(),
currentAgentSpan: SerializedSpan.nullable().optional(),
noActiveAgentRun: zod_1.z.boolean(),
inputGuardrailResults: zod_1.z.array(inputGuardrailResultSchema),
initialInputGuardrailsCompleted: zod_1.z.boolean().optional(),
outputGuardrailResults: zod_1.z.array(outputGuardrailResultSchema),
toolInputGuardrailResults: zod_1.z
.array(toolInputGuardrailResultSchema)
.optional()
.default([]),
toolOutputGuardrailResults: zod_1.z
.array(toolOutputGuardrailResultSchema)
.optional()
.default([]),
currentTurnInProgress: zod_1.z.boolean().optional(),
currentStep: steps_1.nextStepSchema.optional(),
lastModelResponse: modelResponseSchema.optional(),
generatedItems: zod_1.z.array(itemSchema),
currentResponseGeneratedItemOwnership: currentResponseGeneratedItemOwnershipSchema.optional(),
pendingAgentToolRuns: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional().default({}),
pendingAgentToolRunAliases: zod_1.z
.record(zod_1.z.string(), zod_1.z.string())
.optional()
.default({}),
lastProcessedResponse: serializedProcessedResponseSchema.optional(),
currentTurnPersistedItemCount: zod_1.z.number().int().min(0).optional(),
currentTurnSessionWriteCompactedItemCount: zod_1.z.number().int().min(0).optional(),
currentTurnDeferredSessionItemIndexes: zod_1.z
.array(zod_1.z.number().int().min(0))
.optional(),
currentTurnBlockedSessionStartIndex: zod_1.z.number().int().min(0).optional(),
currentTurnExecutedWithSessionBinding: zod_1.z.literal(true).optional(),
currentTurnSessionInputItems: zod_1.z.array(protocol.ModelItem).optional(),
pendingSessionHistoryTransaction: pendingSessionHistoryTransactionSchema.optional(),
pendingSessionWrite: pendingSessionWriteSchema.optional(),
completedToolInvocations: zod_1.z
.array(zod_1.z.object({
agentIdentity: zod_1.z.string(),
invocations: zod_1.z.record(zod_1.z.string(), zod_1.z.string()),
}))
.optional(),
completedToolInvocationEvidence: zod_1.z
.array(zod_1.z.object({
agentIdentity: zod_1.z.string(),
invocations: zod_1.z.record(zod_1.z.string(), serializedToolInvocationCompletionEvidenceSchema),
}))
.optional(),
ambiguousToolInvocationCallIds: zod_1.z
.array(zod_1.z.object({
agentIdentity: zod_1.z.string(),
callIds: zod_1.z.array(zod_1.z.string()),
}))
.optional(),
pendingLegacyCompactionSessionItems: zod_1.z.array(protocol.ModelItem).optional(),
conversationId: zod_1.z.string().optional(),
previousResponseId: zod_1.z.string().optional(),
reasoningItemIdPolicy: zod_1.z.enum(['preserve', 'omit']).optional(),
trace: serializedTraceSchema.nullable(),
sandbox: sandboxStateSchema.optional(),
});
function findUniqueContiguousIdentityStart(items, sequence) {
if (sequence.length === 0 || sequence.length > items.length) {
return undefined;
}
let match;
for (let start = 0; start <= items.length - sequence.length; start += 1) {
if (!sequence.every((item, offset) => {
const candidate = items[start + offset];
if (candidate === item) {
return true;
}
try {
return (candidate !== undefined &&
candidate.type === item.type &&
'rawItem' in candidate &&
'rawItem' in item &&
candidate.rawItem === item.rawItem);
}
catch {
return false;
}
})) {
continue;
}
if (match !== undefined) {
return undefined;
}
match = start;
}
return match;
}
function responseOwnsFunctionCall(response, call) {
const matches = response?.output.filter((item) => item.type === 'function_call' && item.callId === call.callId);
if (matches?.length !== 1)
return false;
const candidate = matches[0];
return ((0, toolInvocation_1.getToolInvocationFingerprint)((0, toolIdentity_1.getFunctionToolStateKeyForCall)(candidate) ?? candidate.name, candidate) ===
(0, toolInvocation_1.getToolInvocationFingerprint)((0, toolIdentity_1.getFunctionToolStateKeyForCall)(call) ?? call.name, call));
}
function captureCurrentResponseGeneratedItemOwnership(state) {
if (state._currentStep?.type !== 'next_step_interruption') {
return undefined;
}
const processedItems = state._lastProcessedResponse?.newItems ?? [];
const interruptions = state._currentStep.data.interruptions;
if (processedItems.length === 0 || interruptions.length === 0) {
return undefined;
}
const generatedItemStartIndex = findUniqueContiguousIdentityStart(state._generatedItems, processedItems);
if (generatedItemStartIndex === undefined) {
return undefined;
}
const processedEndIndex = generatedItemStartIndex + processedItems.length;
const interruptionGeneratedItemIndexes = [];
let previousParentIndex = processedEndIndex - 1;
let captureNestedOwnership;
for (const interruption of interruptions) {
const matchingIndexes = state._generatedItems.flatMap((item, index) => {
if (index < processedEndIndex) {
return [];
}
if (item === interruption) {
return [index];
}
try {
if ('rawItem' in item && item.rawItem === interruption.rawItem) {
return [index];
}
// A partial live resume can recreate the remaining approval wrapper. In that case,
// use the existing canonical invocation identity, but only inside the already-proven
// terminal current-response range and only when it selects one candidate.
return item instanceof items_1.RunToolApprovalItem &&
item.agent === interruption.agent &&
(0, toolInvocation_1.getToolInvocationCallId)(item.rawItem) ===
(0, toolInvocation_1.getToolInvocationCallId)(interruption.rawItem) &&
(0, toolInvocation_1.getToolInvocationFingerprint)(item.functionToolStateKey ?? item.name ?? '', item.rawItem) ===
(0, toolInvocation_1.getToolInvocationFingerprint)(interruption.functionToolStateKey ?? interruption.name ?? '', interruption.rawItem)
? [index]
: [];
}
catch {
return [];
}
});
if (matchingIndexes.length === 0 &&
(captureNestedOwnership ??= (0, blockedOutputPersistence_1.needsNestedApprovalOwnership)(state, generatedItemStartIndex)) &&
state._ownsNestedApproval(interruption, interruptions)) {
interruptionGeneratedItemIndexes.push(null);
continue;
}
if (matchingIndexes.length !== 1 ||
matchingIndexes[0] <= previousParentIndex) {
return undefined;
}
previousParentIndex = matchingIndexes[0];
interruptionGeneratedItemIndexes.push(previousParentIndex);
}
const lastModelResponseIndex = state._modelResponses.lastIndexOf(state._lastTurnResponse);
if (state._lastTurnResponse === undefined ||
lastModelResponseIndex !== state._modelResponses.length - 1 ||
state._modelResponses.indexOf(state._lastTurnResponse) !==
lastModelResponseIndex) {
return undefined;
}
const toolOutputGuardrailResultStartIndex = (0, blockedOutputPersistence_1.getCurrentResponseToolOutputGuardrailResultStart)(state);
if (toolOutputGuardrailResultStartIndex === undefined ||
toolOutputGuardrailResultStartIndex >
state._toolOutputGuardrailResults.length) {
return undefined;
}
return {
generatedItemStartIndex,
generatedItemEndIndexExclusive: state._generatedItems.length,
interruptionGeneratedItemIndexes,
toolOutputGuardrailResultStartIndex,
};
}
function serializedValuesEqual(left, right) {
try {
return JSON.stringify(left) === JSON.stringify(right);
}
catch {
return false;
}
}
function throwCurrentResponseOwnershipError(reason = 'invalid boundary') {
throw new errors_1.UserError(`RunState current-response generated-item ownership has an ${reason}. Start a new run from safe input.`);
}
function validateCurrentResponseGeneratedItemOwnership(stateJson, ownership) {
// Validate only serialized values here, before tool rehydration or caller-owned context work.
// Deserialized object identity is restored only after the complete terminal boundary is proven.
const processedItems = stateJson.lastProcessedResponse?.newItems;
const serializedInterruptions = stateJson.currentStep?.type === 'next_step_interruption' &&
Array.isArray(stateJson.currentStep.data?.interruptions)
? stateJson.currentStep.data.interruptions
: undefined;
const generatedEnd = ownership.generatedItemEndIndexExclusive;
const processedEnd = ownership.generatedItemStartIndex + (processedItems?.length ?? 0);
if (!processedItems ||
processedItems.length === 0 ||
!serializedInterruptions ||
serializedInterruptions.length === 0 ||
!stateJson.lastModelResponse ||
stateJson.modelResponses.length === 0 ||
generatedEnd !== stateJson.generatedItems.length ||
ownership.generatedItemStartIndex >= generatedEnd ||
processedEnd > generatedEnd ||
ownership.interruptionGeneratedItemIndexes.length !==
serializedInterruptions.length ||
ownership.toolOutputGuardrailResultStartIndex >
stateJson.toolOutputGuardrailResults.length ||
!serializedValuesEqual(stateJson.lastModelResponse, stateJson.modelResponses.at(-1))) {
throwCurrentResponseOwnershipError();
}
for (const [offset, processedItem] of processedItems.entries()) {
if (!serializedValuesEqual(processedItem, stateJson.generatedItems[ownership.generatedItemStartIndex + offset])) {
throwCurrentResponseOwnershipError();
}
}
const interruptionIndexes = new Set();
let previousParentIndex = processedEnd - 1;
for (const [offset, interruptionIndex,] of ownership.interruptionGeneratedItemIndexes.entries()) {
const parsedInterruption = itemSchema.safeParse(serializedInterruptions[offset]);
if (interruptionIndex === null) {
if (stateJson.$schemaVersion !== exports.CURRENT_SCHEMA_VERSION ||
!parsedInterruption.success ||
parsedInterruption.data.type !== 'tool_approval_item' ||
parsedInterruption.data.rawItem.type !== 'function_call') {
throwCurrentResponseOwnershipError('invalid child approval');
}
// The pending invocation is checked against the configured asTool source after rehydration.
continue;
}
const mismatch = !serializedValuesEqual(parsedInterruption.success ? parsedInterruption.data : undefined, stateJson.generatedItems[interruptionIndex]);
if (!parsedInterruption.success ||
parsedInterruption.data.type !== 'tool_approval_item' ||
interruptionIndex <= previousParentIndex ||
interruptionIndex >= generatedEnd ||
mismatch) {
throwCurrentResponseOwnershipError(mismatch ? 'interruption content mismatch' : 'interruption index error');
}
previousParentIndex = interruptionIndex;
interruptionIndexes.add(interruptionIndex);
}
const unownedApproval = stateJson.generatedItems
.slice(processedEnd, generatedEnd)
.some((item, offset) => item.type === 'tool_approval_item' &&
!interruptionIndexes.has(processedEnd + offset));
if (unownedApproval) {
throwCurrentResponseOwnershipError();
}
}
function cloneInterruptionSnapshot(interruption) {
let rawItem;
try {
rawItem = structuredClone(interruption.rawItem);
}
catch {
throw new errors_1.UserError('Cannot read RunState interruptions because an interruption raw item is not cloneable.');
}
if (containsSharedArrayBuffer(rawItem)) {
throw new errors_1.UserError('Cannot read RunState interruptions because an interruption raw item contains shared memory.');
}
return new items_1.RunToolApprovalItem(rawItem, interruption.agent, interruption.toolName, interruption.functionToolStateKey);
}
function containsSharedArrayBuffer(value) {
if (typeof SharedArrayBuffer === 'undefined') {
return false;
}
const visited = new Set();
const pending = [value];
while (pending.length > 0) {
const current = pending.pop();
if (!current || typeof current !== 'object') {
continue;
}
if (current instanceof SharedArrayBuffer) {
return true;
}
if (ArrayBuffer.isView(current) &&
current.buffer instanceof SharedArrayBuffer) {
return true;
}
if (typeof WebAssembly !== 'undefined' &&
current instanceof WebAssembly.Memory &&
current.buffer instanceof SharedArrayBuffer) {
return true;
}
if (visited.has(current)) {
continue;
}
visited.add(current);
if (current instanceof Map) {
for (const [key, entry] of current) {
pending.push(key, entry);
}
continue;
}
if (current instanceof Set) {
for (const entry of current) {
pending.push(entry);
}
continue;
}
for (const key of Reflect.ownKeys(current)) {
const descriptor = Object.getOwnPropertyDescriptor(current, key);
if (descriptor && 'value' in descriptor) {
pending.push(descriptor.value);
}
}
}
return false;
}
/**
* Serializable snapshot of an agent's run, including context, usage and trace.
* While this class has publicly writable properties (prefixed with `_`), they are not meant to be
* used directly. To read these properties, use the `RunResult` instead.
*
* Manipulation of the state directly can lead to unexpected behavior and should be avoided.
* Instead, use the `approve` and `reject` methods to interact with the state.
*/
class RunState {
/**
* Current turn number in the conversation.
*/
_currentTurn = 0;
/**
* Whether the current turn has already been counted (useful when resuming mid-turn).
*/
_currentTurnInProgress = false;
/** Whether the complete initial input guardrail batch passed, including an empty batch. */
_initialInputGuardrailsCompleted = false;
/**
* The agent currently handling the conversation.
*/
_currentAgent;
/**
* The root agent that started the run.
*/
#startingAgent;
/**
* Original user input prior to any processing.
*/
_originalInput;
/**
* Input staged for admission immediately before the next resumed model call.
*/
_pendingInput;
/**
* Responses from the model so far.
*/
_modelResponses;
/**
* Conversation identifier when the server manages conversation history.
*/
_conversationId;
/**
* Latest response identifier returned by the server for server-managed conversations.
*/
_previousResponseId;
/**
* Runtime options that control how run items are converted into model turn input.
* This value is serialized so resumed runs keep the same turn-input behavior.
*/
_reasoningItemIdPolicy;
/**
* Effective model settings used for the most recent model call.
*/
_lastModelSettings;
/**
* Active tracing span for the current agent if tracing is enabled.
*/
_currentAgentSpan;
/**
* Run context tracking approvals, usage, and other metadata.
*/
_context;
/**
* Runtime-only metadata for the current nested agent-tool invocation.
*/
_agentToolInvocation;
/**
* The usage aggregated for this run. This includes per-request breakdowns when available.
*/
get usage() {
return this._context.usage;
}
/**
* Tracks what tools each agent has used.
*/
_toolUseTracker;
/**
* Serialized pending nested agent runs keyed by tool name and call id.
*/
_pendingAgentToolRuns;
/**
* Pending conditional approval input lives only in the owning RunState.
* Neither normalized values nor comparison evidence are serialized.
* @internal
*/
_pendingFunctionToolApprovals = new Map();
/**
* Legacy pending-run keys mapped to their canonical category-aware keys.
*/
_pendingAgentToolRunAliases;
/**
* Canonical invocation fingerprints whose local tool outputs are committed to run history.
*/
_completedToolInvocations;
/**
* Completed invocations whose supporting run items were removed by a
* supported history replacement.
*/
_completedToolInvocationEvidence;
/**
* Canonical invocations observed in the active run before their outputs are committed.
*/
_observedToolInvocations;
/**
* Legacy call IDs whose serialized history cannot identify one canonical invocation.
*/
_ambiguousToolInvocationCallIds;
/**
* Items generated by the agent during the run.
*/
_generatedItems;
/**
* Number of `_generatedItems` already flushed to session storage for the current turn.
*
* Persisting the entire turn on every save would duplicate responses and tool outputs.
* Instead, `saveToSession` appends only the delta since the previous write. This counter
* tracks how many generated run items from *this turn* were already written so the next
* save can slice off only the new entries. When a turn is interrupted (e.g., awaiting tool
* approval) and later resumed, we rewind the counter before continuing so the pending tool
* output still gets stored.
*/
_currentTurnPersistedItemCount;
/**
* Persisted-item count whose resumed-write recovery input compaction has settled.
*/
_currentTurnSessionWriteCompactedItemCount;
/**
* Current-turn item indexes intentionally deferred when a blocked output persisted only a
* replay-safe subset. A later accepted resume replaces the sparse suffix in original order.
*/
_currentTurnDeferredSessionItemIndexes;
/**
* First current-turn index governed by the sparse blocked-output session suffix.
*/
_currentTurnBlockedSessionStartIndex;
/**
* Logical session bound to the current turn before a transaction-aware tool effect can occur.
*/
_currentTurnSessionHistoryTransactionSessionId;
/**
* Effective reasoning-item ID policy frozen for the current transaction-aware session turn.
*/
_currentTurnSessionReasoningItemIdPolicy;
/**
* Normalized session input frozen before the current transaction-aware model request.
*/
_currentTurnSessionHistoryTransactionInputItems;
/**
* Whether the current live batch may later replace its sparse suffix with accepted output.
*/
_currentTurnSessionHistoryTransactionCanReplaceAcceptedOutput;
/**
* Current step reconstructed from serialized state. This reference is not serialized and is
* cleared implicitly when the live runner installs another step.
*/
_serializedCurrentStep;
/**
* Stable per-run identifier used to derive idempotent session history transaction IDs.
*/
_sessionHistoryTransactionId;
/**
* Transaction reconstruction data awaiting confirmation from the session backend. Its
* generated-item indexes, operation identity, and post-commit bookkeeping remain stable across
* retries on the same live RunState; input stays in the separately frozen current-turn snapshot.
*/
_pendingSessionHistoryTransaction;
/**
* Ordinary Session append whose outcome must be reconciled before another resumed model call.
*/
_pendingSessionWrite;
/**
* Prevents two live resume attempts from executing or reconciling the same resumed Session
* append concurrently.
*/
_resumedSessionWriteInProgress;
/**
* Compaction marker and persisted suffix that an ordinary session must reconcile once after a
* pre-1.16 snapshot is restored. The field remains serialized until reconciliation succeeds.
*/
_pendingLegacyCompactionSessionItems;
/**
* Maximum allowed turns before forcing termination.
*/
_maxTurns;
/**
* Whether the run has an active agent step in progress.
*/
_noActiveAgentRun = true;
/**
* Last model response for the previous turn.
*/
_lastTurnResponse;
/**
* Results from input guardrails applied to the run.
*/
_inputGuardrailResults;
/**
* Results from output guardrails applied to the run.
*/
_outputGuardrailResults;
/**
* Results from tool input guardrails applied during tool execution.
*/
_toolInputGuardrailResults;
/**
* Results from tool output guardrails applied during tool execution.
*/
_toolOutputGuardrailResults;
/**
* Next step computed for the agent to take.
*/
_currentStep = undefined;
/**
* Indicates how the final output was produced for the current run.
* This value is not serialized.
*/
_finalOutputSource;
/**
* Parsed model response after applying guardrails and tools.
*/
_lastProcessedResponse = undefined;
/**
* Trace associated with this run if tracing is enabled.
*/
_trace = null;
/**
* Runtime-only tool_search-loaded tools, scoped by agent object and preserved across turns for
* the lifetime of this in-memory run.
*/
_toolSearchRuntimeToolsByAgent = new Map();
/**
* Persisted sandbox session metadata for sandbox-agent resume.
*/
_sandbox = undefined;
constructor(context, originalInput, startingAgent, maxTurns) {
this._context = context;
this._agentToolInvocation = undefined;
this._originalInput = structuredClone(originalInput);
this._pendingInput = [];
this._modelResponses = [];
this._currentAgentSpan = undefined;
this._currentAgent = startingAgent;
this.#startingAgent = startingAgent;
this._reasoningItemIdPolicy = undefined;
this._toolUseTracker = new toolUseTracker_1.AgentToolUseTracker();
this._pendingAgentToolRuns = new Map();
this._pendingAgentToolRunAliases = new Map();
this._completedToolInvocations = new Map();
this._completedToolInvocationEvidence = new Map();
this._observedToolInvocations = new Map();
this._ambiguousToolInvocationCallIds = new Map();
this._generatedItems = [];
this._currentTurnPersistedItemCount = 0;
this._currentTurnSessionWriteCompactedItemCount = undefined;
this._currentTurnDeferredSessionItemIndexes = new Set();
this._currentTurnBlockedSessionStartIndex = undefined;
this._currentTurnSessionHistoryTransactionSessionId = undefined;
this._currentTurnSessionReasoningItemIdPolicy = undefined;
this._currentTurnSessionHistoryTransactionInputItems = undefined;
this._currentTurnSessionHistoryTransactionCanReplaceAcceptedOutput =
undefined;
this._serializedCurrentStep = undefined;
this._sessionHistoryTransactionId = (0, _shims_1.randomUUID)();
this._pendingSessionHistoryTransaction = undefined;
this._pendingSessionWrite = undefined;
this._resumedSessionWriteInProgress = false;
this._pendingLegacyCompactionSessionItems = undefined;
this._maxTurns = maxTurns;
this._inputGuardrailResults = [];
this._outputGuardrailResults = [];
this._toolInputGuardrailResults = [];
this._toolOutputGuardrailResults = [];
this._trace = (0, tracing_1.getCurrentTrace)();
}
/**
* Updates server-managed conversation identifiers as a single operation.
*/
setConversationContext(conversationId, previousResponseId) {
this._conversationId = conversationId;
this._previousResponseId = previousResponseId;
}
/**
* Updates runtime options for converting run items into turn input.
*/
setReasoningItemIdPolicy(policy) {
this._reasoningItemIdPolicy = policy;
}
/**
* Updates the agent span associated with the current run.
*/
setCurrentAgentSpan(span) {
this._currentAgentSpan = span;
}
/**
* Clears the restored trace and current agent span from this run state.
*
* Use this before resuming a serialized state when the resumed run should attach
* to the current ambient trace instead of the trace persisted in the state.
*/
clearTrace() {
this._trace = null;
this._currentAgentSpan = undefined;
}
/**
* Validate and observe a canonical local tool invocation before side effects.
* @internal
* @returns Whether the exact invocation already has a committed output.
*/
_observeToolInvocation(agent, callId, fingerprint) {
const completed = this._preflightToolInvocation(agent, callId, fingerprint);
if (!completed) {
getAgentInvocationMap(this._observedToolInvocations, agent).set(callId, fingerprint);
}
return completed;
}
/**
* Validate a canonical local tool invocation without recording it.
* @internal
*/
_preflightToolInvocation(agent, callId, fingerprint) {
this._context._validateAgentApprovalInvocation(agent, callId, fingerprint);
const ambiguous = this._ambiguousToolInvocationCallIds.get(agent);
if (ambiguous?.has(callId)) {
throw new errors_1.ModelBehaviorError(`Tool call ID ${callId} has ambiguous invocation history and cannot be executed safely.`, this);
}
const completed = this._completedToolInvocations.get(agent)?.get(callId);
if (completed !== undefined) {
if (completed !== fingerprint) {
throw new errors_1.ModelBehaviorError(`Tool call ID ${callId} was reused for a different invocation after its output was committed.`, this);
}
return true;
}
const observed = this._observedToolInvocations.get(agent)?.get(callId);
if (observed !== undefined && observed !== fingerprint) {
throw new errors_1.ModelBehaviorError(`Tool call ID ${callId} was reused for different tool invocations in the same run.`, this);
}
return false;
}
/**
* Commit fingerprints for local tool output items added to run history.
* @internal
*/
_commitToolInvocations(items) {
const evidenceCandidates = [...this._generatedItems, ...items];
const commit = (agent, callId, outputItem) => {
const fingerprint = this._observedToolInvocations.get(agent)?.get(callId);
if (fingerprint === undefined) {
return;
}
const evidence = findToolInvocationCompletionEvidence(agent, callId, fingerprint, outputItem, evidenceCandidates);
if (!evidence) {
return;
}
// Preserve the execution-time owner when resuming older pending calls that
// predate call-item identity metadata. Observed invocations are runtime-only.
const toolName = (0, toolInvocation_1.getToolInvocationNameFromFingerprint)(fingerprint);
for (const candidate of evidenceCandidates) {
if (toolName !== undefined &&
candidate instanceof items_1.RunToolCallItem &&
candidate.functionToolStateKey === undefined &&
candidate.agent === agent &&
candidate.rawItem.type === 'function_call' &&
candidate.rawItem.callId === callId &&
!(0, toolIdentity_1.getToolCallNamespace)(candidate.rawItem) &&
toolName ===
(0, toolIdentity_1.getFunctionToolLookupKey)(candidate.rawItem.name, candidate.rawItem.name) &&
(0, toolInvocation_1.getToolInvocationFingerprint)(toolName, candidate.rawItem) ===
fingerprint) {
candidate.functionToolStateKey = toolName;
}
}
getAgentInvocationMap(this._completedToolInvocations, agent).set(callId, fingerprint);
getAgentInvocationMap(this._completedToolInvocationEvidence, agent).set(callId, evidence);
};
for (const item of items) {
if (item instanceof items_1.RunToolCallItem &&
item.rawItem.type === 'hosted_tool_call' &&
item.rawItem.name === 'mcp_approval_response') {
const providerData = item.rawItem.providerData;
const callId = providerData?.approval_request_id;
if (typeof callId !== 'string' || callId.length === 0) {
throw new errors_1.ModelBehaviorError('Hosted MCP approval response is missing a non-empty approval request ID.', this);
}
commit(item.agent, callId, item);
continue;
}
if (item instanceof items_1.RunHandoffOutputItem) {
const callId = item.rawItem.callId;
if (!callId) {
throw new errors_1.ModelBehaviorError('Handoff output is missing a non-empty call ID.', this);
}
commit(item.sourceAgent, callId, item);
continue;
}
if (!(item instanceof items_1.RunToolCallOutputItem)) {
continue;
}
if (!isTrackedLocalToolResult(item.rawItem)) {
continue;
}
const callId = getLocalToolResultCallId(item.rawItem);
if (!callId) {
throw new errors_1.ModelBehaviorError('Tool output is missing a non-empty call ID.', this);
}
commit(item.agent, callId, item);
}
}
/**
* Replaces generated history after completion evidence has been committed.
* @internal
*/
_replaceGeneratedItems(generatedItems) {
this._generatedItems = generatedItems;
}
/** @internal Captures an input view without retaining mutable filter-owned items. */
_captureHandoffInput(originalInput, generatedItems) {
const identities = (0, runStateIdentity_1.buildAgentIdentityMap)(this.#startingAgent);
return structuredClone(handoffInputSnapshotSchema.parse({
originalInput,
generatedItems: generatedItems.map((item) => serializeRunItem(item, identities.byAgent)),
}));
}
/** @internal Reuses ordinary RunState item reconstruction for the accepted input view. */
_deserializeHandoffInput(input) {
const identities = (0, runStateIdentity_1.buildAgentIdentityMap)(this.#startingAgent);
return {
originalInput: structuredClone(input.originalInput),
generatedItems: input.generatedItems.map((item) => deserializeItem(item, identities.byIdentity)),
};
}
/** @internal Resolves discovery ownership within the complete starting Agent graph. */
_getToolSearchAgentName(agent) {
return (0, toolSearchAttribution_1.getToolSearchAgentName)(agent, (0, runStateIdentity_1.buildAgentIdentityMap)(this.#startingAgent).byAgent.keys());
}
getOrCreateToolSearchRuntimeToolState(agent) {
let state = this._toolSearchRuntimeToolsByAgent.get(agent);
if (!state) {
state = {
keyedEntries: new Map(),
routedOwners: new Map(),
nextOrder: 0,
};
this._toolSearchRuntimeToolsByAgent.set(agent, state);
}
return state;
}
recordToolSearchRuntimeTools(agent, toolSearchOutput, tools) {
const runtimeState = this.getOrCreateToolSearchRuntimeToolState(agent);
const entry = {
order: runtimeState.nextOrder++,
tools,
};
const replacementKey = (0, toolSearch_1.getToolSearchOutputReplacementKey)(toolSearchOutput);
if (replacementKey) {
const previousEntry = runtimeState.keyedEntries.get(replacementKey);
if (previousEntry) {
for (const [routingKey, owner] of runtimeState.routedOwners) {
if (owner.entry === previousEntry) {
runtimeState.routedOwners.delete(routingKey);
}
}
}
runtimeState.keyedEntries.set(replacementKey, entry);
}
for (const [toolIndex, tool] of tools.entries()) {
const routingKey = (0, tool_1.getToolSearchRuntimeRoutingKey)(tool);
if (!routingKey) {
continue;
}
runtimeState.routedOwners.set(routingKey, {
entry,
tool,
toolIndex,
});
}
}
getToolSearchRuntimeToolsForOutput(agent, toolSearchOutput) {
const replacementKey = (0, toolSearch_1.getToolSearchOutputReplacementKey)(toolSearchOutput);
if (!replacementKey) {
return [];
}
const runtimeState = this._toolSearchRuntimeToolsByAgent.get(agent);
const entry = runtimeState?.keyedEntries.get(replacementKey);
if (!runtimeState || !entry) {
return [];
}
return entry.tools.filter((tool) => {
const routingKey = (0, tool_1.getToolSearchRuntimeRoutingKey)(tool);
return (typeof routingKey === 'string' &&
runtimeState.routedOwners.get(routingKey)?.entry === entry);
});
}
getToolSearchRuntimeTools(agent) {
const runtimeState = this._toolSearchRuntimeToolsByAgent.get(agent);
if (!runtimeState) {
return [];
}
return [...runtimeState.routedOwners.values()]
.sort((left, right) => left.entry.order - right.entry.order ||
left.toolIndex - right.toolIndex)
.map((owner) => owner.tool);
}
/**
* Switches the active agent handling the run.
*/
setCurrentAgent(agent) {
this._currentAgent = agent;
}
/**
* Returns the agent currently handling the run.
*/
get currentAgent() {
return this._currentAgent;
}
/**
* Resets the counter that tracks how many items were persisted for the current turn.
*/
resetTurnPersistence() {
this._currentTurnPersistedItemCount = 0;
this._currentTurnSessionWriteCompactedItemCount = undefined;
this._currentTurnDeferredSessionItemIndexes.clear();
}
/**
* Rewinds the persisted item counter when pending approvals require re-writing outputs.
*/
rewindTurnPersistence(count) {
if (count <= 0) {
return;
}
this._currentTurnPersistedItemCount = Math.max(0, this._currentTurnPersistedItemCount - count);
if (this._currentTurnSessionWriteCompactedItemCount !==
this._currentTurnPersistedItemCount) {
this._currentTurnSessionWriteCompactedItemCount = undefined;
}
}
/**
* The history of the agent run. This includes the input items and the new items generated during the run.
*
* This can be used as inputs for the next agent run.
*/
get history() {
return (0, items_2.getTurnInput)(this._originalInput, this._generatedItems, this._reasoningItemIdPolicy);
}
/**
* Returns a copy of input staged for the next resumed model call.
*/
get pendingInput() {
return structuredClone(this._pendingInput);
}
/**
* Stages input for admission immediately before the next resumed model call.
*/
addInput(input) {
const currentStep = this._currentStep;
if (currentStep?.type !== 'next_step_interruption' &&
currentStep?.type !== 'next_step_run_again') {
throw new errors_1.UserError('Cannot add input to a terminal RunState', this);
}
if (this._maxTurns !== null &&
this._currentTurn >= this._maxTurns &&
!this._currentTurnInProgress) {
throw new errors_1.UserError('Cannot add input to a RunState with no remaining model turns', this);
}
if (currentStep.type === 'next_step_interruption') {
if (currentStep.data?.responseAccepted === true) {
throw new errors_1.UserError('Cannot add input while an accepted model response is awaiting local processing', this);
}
const toolUseBehavior = this._currentAgent.toolUseBehavior;
const interruptions = this.getInterruptions();
const stopsBeforeNextModel = toolUseBehavior === 'stop_on_first_tool' ||
(typeof toolUseBehavior === 'object' &&
toolUseBehavior.stopAtToolNames.some((toolName) => interruptions.some((item) => item.name === toolName ||
(item.rawItem.type === 'function_call' &&
(0, toolIdentity_1.getToolCallName)(item.rawItem) === toolName))));
if (stopsBeforeNextModel || typeof toolUseBehavior === 'function') {
throw new errors_1.UserError('Cannot add input to an interrupted RunState whose tool result may end the run', this);
}
}
const normalized = (0, items_2.toAgentInputList)(input).map((item) => protocol.ModelItem.parse(item));
this._pendingInput.push(...structuredClone(normalized));
}
/**
* Removes all input staged for the next resumed model call.
*/
clearPendingInput() {
this._pendingInput = [];
}
/**
* Returns all interruptions if the current step is an interruption otherwise returns an empty array.
*/
getInterruptions() {
if (this._currentStep?.type !== 'next_step_interruption') {
return [];
}
const interruptions = this._currentStep.data.interruptions;
return Array.isArray(interruptions)
? interruptions.map(cloneInterruptionSnapshot)
: [];
}
/** @internal Proves a propagated approval belongs to exactly one pending child invocation. */
_ownsNestedApproval(interruption, interruptions) {
const { agent, rawItem } = interruption;
if (agent === this._currentAgent ||
rawItem.type !== 'function_call' ||
interruptions.filter((item) => item.agent === agent &&
(0, toolInvocation_1.getToolInvocationCallId)(item.rawItem) === rawItem.callId).length !== 1) {
return false;
}
// Child snapshots use identities relative to their own starting agent.
// An asTool source is the root of its own identity map, including when names collide.
const expected = itemSchema.safeParse({
...interruption.toJSON(),
agent: { name: agent.name },
});
if (!expected.success)
return false;
let found = false;
for (const run of this._lastProcessedResponse?.functions ?? []) {
if ((0, agentToolSourceRegistry_1.getAgentToolSourceAgent)(run.tool) !== agent)
continue;
const [key] = (0, toolIdentity_1.getFunctionToolStateKeys)(run.tool, run.availableFunctionTools ?? [run.tool]);
if (!key)
continue;
const pending = this.getPendingAgentToolRun(key, run.toolCall.callId);
if (!pending)
continue;
try {
const child = exports.SerializedRunState.parse(JSON.parse(pending));
if (child.currentStep?.type !== 'next_step_interruption' ||
!Array.isArray(child.currentStep.data?.interruptions))
return false;
for (const [offset, entry,] of child.currentStep.data.interruptions.entries()) {
const candidate = itemSchema.parse(entry);
if (candidate.type !== 'tool_approval_item' ||
candidate.rawItem.type !== 'function_call' ||
candidate.rawItem.callId !== rawItem.callId)
continue;
if (found ||
!serializedValuesEqual(candidate, expected.data) ||
!serializedValuesEqual(candidate.agent, child.currentAgent) ||
!responseOwnsFunctionCall(this._lastTurnResponse, run.toolCall) ||
!responseOwnsFunctionCall(child.lastModelResponse, rawItem))
return false;
const ownership = child.currentResponseGeneratedItemOwnership;
if (!ownership ||
typeof ownership.interruptionGeneratedItemIndexes[offset] !==
'number')
return false;
assertSchemaVersionSupportsCurrentResponseGeneratedItemOwnership(child.$schemaVersion, child);
found = true;
}
}
catch {
return false;
}
}
return found;
}
getPendingAgentToolRunKey(toolName, callId) {
return `${toolName}:${callId}`;
}
resolvePendingAgentToolRunKey(toolName, callId) {
const key = this.getPendingAgentToolRunKey(toolName, callId);
return this._pendingAgentToolRunAliases.get(key) ?? key;
}
getPendingAgentToolRun(toolName, callId) {
return this._pendingAgentToolRuns.get(this.resolvePendingAgentToolRunKey(toolName, callId));
}
hasPendingAgentToolRun(toolName, callId) {
return this._pendingAgentToolRuns.has(this.resolvePendingAgentToolRunKey(toolName, callId));
}
setPendingAgentToolRun(toolName, callId, serializedState, aliases = []) {
const canonicalKey = this.resolvePendingAgentToolRunKey(toolName, callId);
this._pendingAgentToolRuns.set(canonicalKey, serializedState);
for (const alias of aliases) {
const aliasKey = this.getPendingAgentToolRunKey(alias, callId);
if (aliasKey === canonicalKey) {
continue;
}
this._pendingAgentToolRuns.delete(aliasKey);
this._pendingAgentToolRunAliases.set(aliasKey, canonicalKey);
}
}
clearPendingAgentToolRun(toolName, callId) {
const requestedKey = this.getPendingAgentToolRunKey(toolName, callId);
const canonicalKey = this.resolvePendingAgentToolRunKey(toolName, callId);
this._pendingAgentToolRuns.delete(canonicalKey);
this._pendingAgentToolRuns.delete(requestedKey);
for (const [aliasKey, targetKey] of this._pendingAgentToolRunAliases) {
if (aliasKey === requestedKey || targetKey === canonicalKey) {
this._pendingAgentToolRuns.delete(aliasKey);
this._pendingAgentToolRunAliases.delete(aliasKey);
}
}
}
/**
* Approves a tool call requested by the agent through an interruption and approval item request.
*
* To approve the request use this method and then run the agent again with the same state object
* to continue the execution.
*
* By default it will only approve the current tool call. To allow the tool to be used multiple
* times throughout the run, set the `alwaysApprove` option to `true`.
*
* @param approvalItem - The tool call approval item to approve.
* @param options - Options for the approval.
* @param options.alwaysApprove - Approve this tool for all future calls in this run.
*/
approve(approvalItem, options = {
alwaysApprove: false,
}) {
this._context.approveTool(approvalItem, options);
}
/**
* Rejects a tool call requested by the agent through an interruption and approval item request.
*
* To reject the request use this method and then run the agent again with the same state object
* to continue the execution.
*
* By default it will only reject the current tool call. To reject the tool for all future
* calls throughout the run, set the `alwaysReject` option to `true`.
*
* When `message` is provided, it is used as the rejection text sent to the model.
* Otherwise, `toolErrorFormatter` (if configured) or the SDK default is used.
*
* @param approvalItem - The tool call approval item to reject.
* @param options - Options for the rejection.
* @param options.alwaysReject - Reject this tool for all future calls in this run.
* @param options.message - The rejection text sent to the model.
* If not provided, `toolErrorFormatter` (if configured) or the SDK default is used.
*/
reject(approvalItem, options = {
alwaysReject: false,
}) {
this._context.rejectTool(approvalItem, options);
}
/**
* Serializes the run state to a JSON object.
*
* This method is used to serialize the run state to a JSON object that can be used to
* resume the run later.
*
* @returns The serialized run state.
*/
/**
* Serializes the run state. By default, tracing API keys are omitted to prevent
* accidental persistence of secrets. Pass `includeTracingApiKey: true` only when you
* intentionally need to migrate a run along with its tracing credentials (e.g., to
* rehydrate in a separate process that lacks the original environment variables).
*/
toJSON(options = {}) {
assertSandboxStateUsesCurrentSessionEnvelope(this._sandbox);
const agentIdentity = (0, runStateIdentity_1.buildAgentIdentityMap)(this.#startingAgent);
const completedToolInvocations = new Map([...this._completedToolInvocations].map(([agent, invocations]) => [
agent,
new Map(invocations),
]));
const inferredInvocations = inferCompletedToolInvocations(this._generatedItems);
for (const [agent, invocations] of inferredInvocations.completed) {
const completedByAgent = getAgentInvocationMap(completedToolInvocations, agent);
for (const [callId, fingerprint] of invocations) {
const existing = completedByAgent.get(callId);
if (existing !== undefined && existing !== fingerprint) {
throw new errors_1.UserError('RunState completed tool invocation records conflict with the generated item history.');
}
completedByAgent.set(callId, fingerprint);
}
}
const completedToolInvocationEvidence = new Map([...this._completedToolInvocationEvidence].map(([agent, invocations]) => [
agent,
new Map(invocations),
]));
for (const [agent, invocations] of inferredInvocations.completed) {
for (const [callId, fingerprint] of invocations) {
const evidenceByAgent = getAgentInvocationMap(completedToolInvocationEvidence, agent);
if (!evidenceByAgent.has(callId)) {
const inferredEvidence = inferredInvocations.evidence
.get(agent)
?.get(callId);
const outputItem = inferredEvidence?.at(-1);
const evidence = outputItem && inferredEvidence
? findToolInvocationCompletionEvidence(agent, callId, fingerprint, outputItem, inferredEvidence)
: undefined;
if (evidence) {
evidenceByAgent.set(callId, evidence);
}
}
}
}
let hasRecoveredHandoff = false;
for (const [agent, invocations] of completedToolInvocationEvidence) {
const completedByAgent = completedToolInvocations.get(agent);
for (const [callId, evidence] of invocations) {
hasRecoveredHandoff ||= isRecoveredHandoffEvidence(evidence);
validateGeneratedHistoryForCompletionEvidence(this._generatedItems, agent, callId, evidence);
const fingerprint = validateToolInvocationCompletionEvidence(agent, callId, evidence);
if (completedByAgent?.get(callId) !== fingerprint) {
throw new errors_1.UserError('RunState completed tool invocation evidence lacks completed invocation authority.');
}
}
}
for (const [agent, invocations] of completedToolInvocations) {
const evidenceByAgent = completedToolInvocationEvidence.get(agent);
for (const [callId, fingerprint] of invocations) {
if (evidenceByAgent?.get(callId)?.fingerprint !== fingerprint) {
throw new errors_1.UserError('RunState completed tool invocation records lack correlated completion evidence.');
}
}
}
const ambiguousToolInvocationCallIds = new Map([...this._ambiguousToolInvocationCallIds].map(([agent, callIds]) => [
agent,
new Set(callIds),
]));
for (const [agent, callIds] of inferredInvocations.ambiguous) {
const ambiguousByAgent = getAgentAmbiguousCallIds(ambiguousToolInvocationCallIds, agent);
for (const callId of callIds) {
if (completedToolInvocationEvidence.get(agent)?.has(callId)) {
continue;
}
ambiguousByAgent.add(callId);
}
}
const includeTracingApiKey = options.includeTracingApiKey === true;
const contextJson = this._context._toJSONForRunState(agentIdentity.byAgent);
const currentResponseGeneratedItemOwnership = captureCurrentResponseGeneratedItemOwnership(this);
const output = {
$schemaVersion: exports.CURRENT_SCHEMA_VERSION,
currentTurn: this._currentTurn,
currentAgent: serializeAgentReference(this._currentAgent, agentIdentity.byAgent),
originalInput: this._originalInput,
pendingInput: this._pendingInput.length > 0 ? this._pendingInput : undefined,
modelResponses: this._modelResponses.map((response) => {
return {
usage: {
requests: response.usage.requests,
inputTokens: response.usage.inputTokens,
outputTokens: response.usage.outputTokens,
totalTokens: response.usage.totalTokens,
inputTokensDetails: response.usage.inputTokensDetails,
outputTokensDetails: response.usage.outputTokensDetails,
...(response.usage.requestUsageEntries &&
response.usage.requestUsageEntries.length > 0
? {
requestUsageEntries: response.usage.requestUsageEntries.map((entry) => ({
inputTokens: entry.inputTokens,
outputTokens: entry.outputTokens,
totalTokens: entry.totalTokens,
inputTokensDetails: entry.inputTokensDetails,
outputTokensDetails: entry.outputTokensDetails,
...(entry.endpoint ? { endpoint: entry.endpoint } : {}),
})),
}
: {}),
},
output: response.output,
responseId: response.responseId,
requestId: response.requestId,
providerData: response.providerData,
};
}),
context: contextJson,
toolUseTracker: this._toolUseTracker.toJSON({
agentIdentityKeys: agentIdentity.byAgent,
}),
maxTurns: this._maxTurns,
currentAgentSpan: this._currentAgentSpan?.toJSON(),
noActiveAgentRun: this._noActiveAgentRun,
currentTurnInProgress: this._currentTurnInProgress,
inputGuardrailResults: this._inputGuardrailResults,
initialInputGuardrailsCompleted: this._initialInputGuardrailsCompleted,
outputGuardrailResults: this._outputGuardrailResults.map((r) => ({
...r,
agent: serializeAgentReference(r.agent, agentIdentity.byAgent),
})),
toolInputGuardrailResults: this._toolInputGuardrailResults,
toolOutputGuardrailResults: this._toolOutputGuardrailResults,
currentStep: serializeCurrentStep(this._currentStep, agentIdentity.byAgent),
lastModelResponse: this._lastTurnResponse,
generatedItems: this._generatedItems.map((item) => serializeRunItem(item, agentIdentity.byAgent)),
currentResponseGeneratedItemOwnership,
pendingAgentToolRuns: Object.fromEntries(this._pendingAgentToolRuns.entries()),
pendingAgentToolRunAliases: Object.fromEntries(this._pendingAgentToolRunAliases.entries()),
completedToolInvocations: serializeAgentInvocationRecords(completedToolInvocations, agentIdentity.byAgent),
completedToolInvocationEvidence: serializeAgentInvocationEvidence(completedToolInvocationEvidence, agentIdentity.byAgent, this._generatedItems),
ambiguousToolInvocationCallIds: serializeAgentCallIdSets(ambiguousToolInvocationCallIds, agentIdentity.byAgent),
currentTurnPersistedItemCount: this._currentTurnPersistedItemCount,
currentTurnSessionWriteCompactedItemCount: this._currentTurnSessionWriteCompactedItemCount,
currentTurnDeferredSessionItemIndexes: this._currentTurnDeferredSessionItemIndexes.size > 0
? [...this._currentTurnDeferredSessionItemIndexes].sort((left, right) => left - right)
: undefined,
currentTurnBlockedSessionStartIndex: this._currentTurnBlockedSessionStartIndex,
currentTurnExecutedWithSessionBinding: this._currentTurnSessionHistoryTransactionSessionId !== undefined &&
(0, blockedOutputPersistence_1.hasBlockedOutputExecutionEffect)(this._generatedItems, this._currentTurnPersistedItemCount)
? true
: undefined,
currentTurnSessionInputItems: (this._pendingSessionWrite !== undefined ||
this._currentTurnSessionHistoryTransactionSessionId === undefined) &&
this._currentTurnSessionHistoryTransactionInputItems !== undefined &&
this._currentTurnSessionHistoryTransactionInputItems.length > 0 &&
(0, blockedOutputPersistence_1.hasBlockedOutputExecutionEffect)(this._generatedItems, this._currentTurnPersistedItemCount)
? this._currentTurnSessionHistoryTransactionInputItems
: undefined,
pendingSessionHistoryTransaction: this._pendingSessionHistoryTransaction,
pendingSessionWrite: this._pendingSessionWrite
? structuredClone(this._pendingSessionWrite)
: undefined,
pendingLegacyCompactionSessionItems: this._pendingLegacyCompactionSessionItems,
lastProcessedResponse: this._lastProcessedResponse
? serializeProcessedResponse(this._lastProcessedResponse, agentIdentity.byAgent)
: undefined,
conversationId: this._conversationId,
previousResponseId: this._previousResponseId,
reasoningItemIdPolicy: this._reasoningItemIdPolicy,
trace: this._trace
? this._trace.toJSON({ includeTracingApiKey })
: null,
sandbox: this._sandbox,
};
// parsing the schema to ensure the output is valid for reparsing
const parsed = exports.SerializedRunState.safeParse(output);
if (!parsed.success) {
throw new errors_1.SystemError(`Failed to serialize run state. ${parsed.error.message}`);
}
// Keep ordinary snapshots readable by 0.21.0. Rich metadata is preserved,
// never filtered to force an older format. Each nested writer selects its
// own version; do not downgrade an envelope containing a newer child.
const state = parsed.data;
const usages = [
state.context.usage,
...state.modelResponses.map((response) => response.usage),
...(state.lastModelResponse ? [state.lastModelResponse.usage] : []),
];
const nestedRunsSupportV123 = Object.values(state.pendingAgentToolRuns).every((serialized) => {
try {
const nested = JSON.parse(serialized);
return (nested !== null &&
typeof nested === 'object' &&
'$schemaVersion' in nested &&
nested.$schemaVersion !== exports.CURRENT_SCHEMA_VERSION &&
$schemaVersion.safeParse(nested.$schemaVersion).success);
}
catch {
return false;
}
});
const sandboxSupportsV123 = !state.sandbox ||
[
state.sandbox.sessionState,
...Object.values(state.sandbox.sessionsByAgent).map((entry) => entry.sessionState),
].every((envelope) => envelope.version <= 5);
if (!hasRecoveredHandoff &&
!state.currentResponseGeneratedItemOwnership?.interruptionGeneratedItemIndexes.includes(null) &&
usages.every(usageSupportsV123) &&
nestedRunsSupportV123 &&
sandboxSupportsV123) {
state.$schemaVersion = '1.23';
}
return state;
}
/**
* Serializes the run state to a string.
*
* This method is used to serialize the run state to a string that can be used to
* resume the run later.
*
* @returns The serialized run state.
*/
toString(options = {}) {
return JSON.stringify(this.toJSON(options));
}
/**
* Deserializes a run state from a string.
*
* This method is used to deserialize a run state from a string that was serialized using the
* `toString` method.
* Pending function calls from snapshots without recipient provenance cannot execute;
* start a new run instead. Rejected and completed calls remain non-executing.
* MCP calls require the original server names, list positions, and raw tool names.
* These bindings detect routing changes, not snapshot tampering or changed transport
* settings or credentials under the same identity.
*/
static async fromString(initialAgent, str) {
return buildRunStateFromString(initialAgent, str);
}
static async fromStringWithContext(initialAgent, str, context, options = {}) {
return buildRunStateFromString(initialAgent, str, {
contextOverride: context,
contextStrategy: options.contextStrategy,
});
}
}
exports.RunState = RunState;
async function buildRunStateFromString(initialAgent, str, options = {}) {
const [parsingError, jsonResult] = await (0, safeExecute_1.safeExecute)(() => JSON.parse(str));
if (parsingError) {
throw new errors_1.UserError(`Failed to parse run state. ${parsingError instanceof Error ? parsingError.message : String(parsingError)}`);
}
const currentSchemaVersion = jsonResult.$schemaVersion;
if (!currentSchemaVersion) {
throw new errors_1.UserError('Run state is missing schema version');
}
if (!exports.SUPPORTED_SCHEMA_VERSIONS.includes(currentSchemaVersion)) {
throw new errors_1.UserError(`Run state schema version ${currentSchemaVersion} is not supported. Please use version ${exports.CURRENT_SCHEMA_VERSION}.`);
}
validateFunctionApprovalEnvelope(currentSchemaVersion, jsonResult);
const hasPendingInputField = jsonResult !== null &&
typeof jsonResult === 'object' &&
hasOwnProperty(jsonResult, 'pendingInput');
const hasPendingSessionWriteField = jsonResult !== null &&
typeof jsonResult === 'object' &&
hasOwnProperty(jsonResult, 'pendingSessionWrite');
const hasSessionWriteCompactedItemCountField = jsonResult !== null &&
typeof jsonResult === 'object' &&
hasOwnProperty(jsonResult, 'currentTurnSessionWriteCompactedItemCount');
const stateJson = exports.SerializedRunState.parse(jsonResult);
assertSchemaVersionSupportsStructuredToolOutputs(currentSchemaVersion, stateJson);
assertSchemaVersionSupportsToolSearch(currentSchemaVersion, stateJson);
assertSchemaVersionSupportsCustomData(currentSchemaVersion, stateJson);
assertSchemaVersionSupportsProgrammaticToolCalling(currentSchemaVersion, stateJson);
assertSchemaVersionSupportsSandboxSessionEnvelope(currentSchemaVersion, stateJson);
assertSchemaVersionSupportsCompactionItems(currentSchemaVersion, stateJson);
assertSchemaVersionSupportsOutputGuardrailSessionPersistence(currentSchemaVersion, stateJson);
assertSchemaVersionSupportsCurrentResponseGeneratedItemOwnership(currentSchemaVersion, stateJson);
assertSchemaVersionSupportsPendingSessionWrite(currentSchemaVersion, stateJson, hasPendingSessionWriteField, hasSessionWriteCompactedItemCountField);
assertSchemaVersionSupportsPendingInput(currentSchemaVersion, stateJson, hasPendingInputField);
const normalizedState = schemaVersionSupportsV116State(currentSchemaVersion)
? { stateJson }
: (0, runStateLegacyCompaction_1.rehydrateLegacyCompactionRunItems)(stateJson);
const state = await buildRunStateFromJson(initialAgent, normalizedState.stateJson, options);
if (normalizedState.sessionReconciliation) {
const { generatedInsertionIndex, previousPersistedItemCount } = normalizedState.sessionReconciliation;
state._pendingLegacyCompactionSessionItems = (0, items_2.extractOutputItemsFromRunItems)(state._generatedItems.slice(generatedInsertionIndex, previousPersistedItemCount + 1));
state._currentTurnPersistedItemCount = previousPersistedItemCount + 1;
}
return state;
}
function validateFunctionApprovalEnvelope(schemaVersion, stateJson) {
if (!stateJson || typeof stateJson !== 'object') {
return;
}
const context = stateJson.context;
if (!context || typeof context !== 'object') {
return;
}
const hasFunctionApprovals = Object.prototype.hasOwnProperty.call(context, 'functionApprovals');
const hasLegacyFunctionApprovals = Object.prototype.hasOwnProperty.call(context, 'legacyFunctionApprovals');
if (!hasFunctionApprovals && !hasLegacyFunctionApprovals) {
return;
}
if (!schemaVersionSupportsV116State(schemaVersion)) {
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support owner-scoped function approvals. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
if (!functionApprovalContextSchema.safeParse(context).success) {
throw new errors_1.UserError('Failed to parse owner-scoped function approvals because their structure does not match the supported schema.');
}
}
function assertSchemaVersionSupportsToolSearch(schemaVersion, stateJson) {
if (schemaVersion === '1.8' ||
schemaVersion === '1.9' ||
schemaVersion === '1.10' ||
schemaVersion === '1.11' ||
schemaVersion === '1.12' ||
schemaVersion === '1.13' ||
schemaVersion === '1.14' ||
schemaVersion === '1.15' ||
schemaVersion === '1.16' ||
schemaVersion === '1.17' ||
schemaVersionSupportsV118State(schemaVersion)) {
return;
}
if (!containsSerializedToolSearchState(stateJson)) {
return;
}
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support tool_search items. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
function assertSchemaVersionSupportsCustomData(schemaVersion, stateJson) {
if (schemaVersion === '1.13' ||
schemaVersion === '1.14' ||
schemaVersion === '1.15' ||
schemaVersion === '1.16' ||
schemaVersion === '1.17' ||
schemaVersionSupportsV118State(schemaVersion)) {
return;
}
if (!containsSerializedToolOutputCustomData(stateJson)) {
return;
}
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support tool output customData. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
function assertSchemaVersionSupportsStructuredToolOutputs(schemaVersion, stateJson) {
if (schemaVersionSupportsV118State(schemaVersion) ||
!containsStructuredToolOutputState(stateJson)) {
return;
}
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support structured tool output values. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
function schemaVersionSupportsAgentIdentity(schemaVersion) {
return (schemaVersion === '1.10' ||
schemaVersion === '1.11' ||
schemaVersion === '1.12' ||
schemaVersion === '1.13' ||
schemaVersion === '1.14' ||
schemaVersion === '1.15' ||
schemaVersion === '1.16' ||
schemaVersion === '1.17' ||
schemaVersionSupportsV118State(schemaVersion));
}
function assertSchemaVersionSupportsProgrammaticToolCalling(schemaVersion, stateJson) {
if (schemaVersion === '1.14' ||
schemaVersion === '1.15' ||
schemaVersion === '1.16' ||
schemaVersion === '1.17' ||
schemaVersionSupportsV118State(schemaVersion)) {
return;
}
if (!containsProgrammaticToolCallingState(stateJson)) {
return;
}
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support Programmatic Tool Calling items. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
function assertSchemaVersionSupportsSandboxSessionEnvelope(schemaVersion, stateJson) {
if (!stateJson.sandbox) {
return;
}
const envelopes = [
stateJson.sandbox.sessionState,
...Object.values(stateJson.sandbox.sessionsByAgent).map((entry) => entry.sessionState),
];
const supportsVersion2 = schemaVersion === '1.15' ||
schemaVersion === '1.16' ||
schemaVersion === '1.17' ||
schemaVersionSupportsV118State(schemaVersion);
const supportsVersion3 = schemaVersionSupportsV118State(schemaVersion);
const supportsVersion4 = schemaVersionSupportsV119State(schemaVersion);
const supportsVersion5 = schemaVersionSupportsV120State(schemaVersion);
if (envelopes.every((envelope) => envelope.version === 1 ||
(envelope.version === 2 && supportsVersion2) ||
(envelope.version === 3 && supportsVersion3) ||
(envelope.version === 4 && supportsVersion4) ||
(envelope.version === 5 && supportsVersion5) ||
(envelope.version === session_1.SANDBOX_SESSION_STATE_VERSION &&
schemaVersion === exports.CURRENT_SCHEMA_VERSION))) {
return;
}
const rejectedVersion = envelopes.find((envelope) => envelope.version !== 1 &&
!(envelope.version === 2 && supportsVersion2) &&
!(envelope.version === 3 && supportsVersion3) &&
!(envelope.version === 4 && supportsVersion4) &&
!(envelope.version === 5 && supportsVersion5) &&
!(envelope.version === session_1.SANDBOX_SESSION_STATE_VERSION &&
schemaVersion === exports.CURRENT_SCHEMA_VERSION))?.version;
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support sandbox session state version ${rejectedVersion}. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
function assertSandboxStateUsesCurrentSessionEnvelope(sandbox) {
if (!sandbox) {
return;
}
const legacyVersions = [
sandbox.sessionState.version,
...Object.values(sandbox.sessionsByAgent).map((entry) => entry.sessionState.version),
].filter((version) => version !== 5 && version !== session_1.SANDBOX_SESSION_STATE_VERSION);
if (legacyVersions.length === 0) {
return;
}
throw new errors_1.UserError('Legacy sandbox session state must be resumed through the Runner before it can be serialized with the current RunState schema.');
}
/**
* Rejects older schemas that carry the new wrapper. Current snapshots treat generated items as
* the replay authority because a supported handoff input filter may remove or replace raw model
* output before it becomes history.
*/
function assertSchemaVersionSupportsCompactionItems(schemaVersion, stateJson) {
if (schemaVersionSupportsV116State(schemaVersion)) {
return;
}
if (!containsSerializedCompactionRunItems(stateJson) &&
stateJson.pendingLegacyCompactionSessionItems === undefined) {
return;
}
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support compaction items. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
function assertSchemaVersionSupportsOutputGuardrailSessionPersistence(schemaVersion, stateJson) {
if (schemaVersionSupportsV117State(schemaVersion)) {
validateOutputGuardrailSessionPersistenceState(stateJson);
return;
}
const hasExecutionProvenance = [
...stateJson.generatedItems,
...(stateJson.lastProcessedResponse?.newItems ?? []),
].some((item) => item.type === 'tool_call_output_item' &&
item.executionStatus !== undefined);
const hasV117PersistenceEnvelope = stateJson.currentTurnDeferredSessionItemIndexes !== undefined ||
stateJson.currentTurnBlockedSessionStartIndex !== undefined ||
stateJson.currentTurnExecutedWithSessionBinding !== undefined ||
stateJson.currentTurnSessionInputItems !== undefined ||
stateJson.pendingSessionHistoryTransaction !== undefined;
if (!hasV117PersistenceEnvelope && !hasExecutionProvenance) {
return;
}
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support output guardrail session persistence state. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
function assertSchemaVersionSupportsCurrentResponseGeneratedItemOwnership(schemaVersion, stateJson) {
const ownership = stateJson.currentResponseGeneratedItemOwnership;
if (ownership === undefined) {
return;
}
if (!schemaVersionSupportsV120State(schemaVersion)) {
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support current-response generated-item ownership. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
validateCurrentResponseGeneratedItemOwnership(stateJson, ownership);
}
function assertSchemaVersionSupportsPendingSessionWrite(schemaVersion, stateJson, hasPendingSessionWriteField, hasSessionWriteCompactedItemCountField) {
if (!schemaVersionSupportsV120State(schemaVersion)) {
if (!hasPendingSessionWriteField &&
!hasSessionWriteCompactedItemCountField) {
return;
}
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support pending Session writes. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
const pending = stateJson.pendingSessionWrite;
const compactedItemCount = stateJson.currentTurnSessionWriteCompactedItemCount;
const persistedItemCount = stateJson.currentTurnPersistedItemCount ?? 0;
if (compactedItemCount !== undefined &&
(pending !== undefined ||
compactedItemCount !== persistedItemCount ||
(stateJson.currentStep?.type !== 'next_step_run_again' &&
stateJson.currentStep?.type !== 'next_step_interruption' &&
stateJson.currentStep?.type !== 'next_step_final_output'))) {
throw new errors_1.UserError('RunState resumed Session write compaction marker is invalid.');
}
if (pending === undefined) {
return;
}
const alreadyPersistedCount = persistedItemCount;
const terminalFinalization = pending.terminalToolFinalization;
const terminalStep = stateJson.currentStep?.type === 'next_step_final_output';
const terminalFinalizationShapeIsInvalid = terminalFinalization
? terminalFinalization.behavior === 'function'
? terminalFinalization.selectedCallId !== undefined ||
terminalFinalization.selectedGeneratedItemIndex !== undefined
: terminalFinalization.selectedCallId === undefined ||
terminalFinalization.selectedGeneratedItemIndex === undefined
: terminalStep;
const pendingCompaction = pending.phase === 'compaction_pending';
const expectedPersistedItemCount = pendingCompaction
? pending.persistedItemCount
: pending.alreadyPersistedCount;
if ((stateJson.currentStep?.type !== 'next_step_run_again' &&
stateJson.currentStep?.type !== 'next_step_interruption' &&
stateJson.currentStep?.type !== 'next_step_final_output') ||
terminalStep !== (terminalFinalization !== undefined) ||
terminalFinalizationShapeIsInvalid ||
expectedPersistedItemCount !== alreadyPersistedCount ||
pending.persistedItemCount <= pending.alreadyPersistedCount ||
pending.persistedItemCount !== stateJson.generatedItems.length ||
stateJson.pendingSessionHistoryTransaction !== undefined ||
stateJson.pendingLegacyCompactionSessionItems !== undefined ||
(stateJson.currentTurnDeferredSessionItemIndexes?.length ?? 0) > 0 ||
stateJson.currentTurnBlockedSessionStartIndex !== undefined ||
(pendingCompaction &&
(stateJson.currentTurnExecutedWithSessionBinding !== undefined ||
stateJson.currentTurnSessionInputItems !== undefined)) ||
(stateJson.currentTurnSessionInputItems !== undefined &&
stateJson.currentTurnExecutedWithSessionBinding !== true) ||
stateJson.conversationId !== undefined ||
stateJson.previousResponseId !== undefined) {
throw new errors_1.UserError('RunState pending Session write is invalid.');
}
}
function assertPendingSessionWriteOwnership(state, pending, options = {}) {
const pendingCompaction = pending.phase === 'compaction_pending';
const ownsTerminalToolFinalization = state._currentStep?.type === 'next_step_final_output' &&
ownsPendingTerminalToolFinalization(state, pending, options);
if ((state._currentStep?.type !== 'next_step_run_again' &&
state._currentStep?.type !== 'next_step_interruption' &&
!ownsTerminalToolFinalization) ||
state._currentTurnPersistedItemCount !==
(pendingCompaction
? pending.persistedItemCount
: pending.alreadyPersistedCount) ||
pending.persistedItemCount <= pending.alreadyPersistedCount ||
pending.persistedItemCount !== state._generatedItems.length ||
state._currentTurnSessionWriteCompactedItemCount !== undefined ||
state._pendingSessionHistoryTransaction !== undefined ||
state._pendingLegacyCompactionSessionItems !== undefined ||
state._currentTurnDeferredSessionItemIndexes.size > 0 ||
state._currentTurnBlockedSessionStartIndex !== undefined ||
(pendingCompaction &&
(state._currentTurnSessionHistoryTransactionSessionId !== undefined ||
state._currentTurnSessionHistoryTransactionInputItems !== undefined)) ||
state._conversationId !== undefined ||
state._previousResponseId !== undefined) {
throw new errors_1.UserError('RunState pending Session write is invalid.');
}
getPendingSessionWriteAppendItems(state, pending);
if (pending.handoffInput) {
if (state._currentStep?.type !== 'next_step_run_again' ||
!state._noActiveAgentRun ||
state._currentTurnInProgress ||
!state._generatedItems.some((item) => item instanceof items_1.RunHandoffOutputItem &&
item.targetAgent === state._currentAgent)) {
throw new errors_1.UserError('RunState pending handoff input is invalid.');
}
state._deserializeHandoffInput(pending.handoffInput);
}
}
function getPendingSessionWriteBehaviorKind(behavior) {
if (behavior === 'stop_on_first_tool') {
return 'stop_on_first_tool';
}
if (typeof behavior === 'object') {
return 'stop_at_tool_names';
}
if (typeof behavior === 'function') {
return 'function';
}
return undefined;
}
/** @internal */
function assertPendingSessionCompactionTerminalAuthority(state) {
const currentStep = state._currentStep;
if (currentStep?.type !== 'next_step_final_output') {
return;
}
const behavior = state._currentAgent.toolUseBehavior;
const producer = pendingSessionWriteTerminalProducers.get(state);
if (producer !== undefined &&
getPendingSessionWriteBehaviorKind(producer.behavior) !==
getPendingSessionWriteBehaviorKind(behavior)) {
throw new errors_1.UserError('RunState pending Session compaction terminal output is invalid.');
}
if (typeof behavior !== 'function') {
if (!(0, blockedOutputPersistence_1.hasDeterministicTerminalToolOutputSource)(state)) {
throw new errors_1.UserError('RunState pending Session compaction terminal output is invalid.');
}
return;
}
const completedInvocations = state._completedToolInvocationEvidence.get(state._currentAgent);
const validProducer = producer !== undefined &&
producer.behavior === behavior &&
producer.finalOutput === currentStep.output &&
producer.selectedCallId === undefined &&
producer.resultItems.length > 0 &&
producer.resultItems.length === producer.resultCallIds.length &&
producer.resultItems.every((item, index) => {
const callId = producer.resultCallIds[index];
const generatedItemIndex = state._generatedItems.indexOf(item);
return (callId !== undefined &&
generatedItemIndex >= 0 &&
generatedItemIndex < state._currentTurnPersistedItemCount &&
getCompletionOutputCallId(item, state._currentAgent) === callId &&
completedInvocations?.get(callId)?.items[1] === item);
});
if (!validProducer) {
throw new errors_1.UserError('RunState pending Session compaction terminal output is invalid.');
}
}
function capturePendingSessionWriteTerminalFinalization(state) {
const producer = pendingSessionWriteTerminalProducers.get(state);
if (!producer || state._currentStep?.type !== 'next_step_final_output') {
return undefined;
}
const behavior = getPendingSessionWriteBehaviorKind(producer.behavior);
if (!behavior) {
return undefined;
}
const selectedResultIndex = producer.selectedCallId
? producer.resultCallIds.indexOf(producer.selectedCallId)
: -1;
const selectedGeneratedItemIndex = selectedResultIndex >= 0
? state._generatedItems.indexOf(producer.resultItems[selectedResultIndex])
: undefined;
if (behavior !== 'function' &&
(selectedGeneratedItemIndex === undefined || selectedGeneratedItemIndex < 0)) {
return undefined;
}
return {
behavior,
selectedCallId: producer.selectedCallId,
selectedGeneratedItemIndex,
finalOutput: producer.finalOutput,
};
}
function getPendingDeclarativeTerminalCandidate(state, item, completedInvocations) {
if (!(item instanceof items_1.RunToolCallOutputItem) ||
item.agent !== state._currentAgent ||
item.rawItem.type !== 'function_call_result' ||
item.rawItem.status === 'incomplete' ||
(0, toolInvocation_1.getCanonicalToolCaller)(item.rawItem).type === 'program') {
return undefined;
}
const callId = getCompletionOutputCallId(item, state._currentAgent);
const completionEvidence = callId
? completedInvocations?.get(callId)
: undefined;
if (!callId ||
!completionEvidence ||
completionEvidence.items[0] instanceof items_1.RunHandoffCallItem ||
completionEvidence.items[1] !== item) {
return undefined;
}
return { callId, completionEvidence, outputItem: item };
}
function selectPendingDeclarativeTerminalCandidate(behavior, candidates) {
if (behavior === 'stop_on_first_tool') {
return candidates[0];
}
if (typeof behavior !== 'object') {
return undefined;
}
return candidates.find(({ completionEvidence }) => {
const callItem = completionEvidence.items[0];
if ((!(callItem instanceof items_1.RunToolCallItem) &&
!(callItem instanceof items_1.RunToolApprovalItem)) ||
callItem.rawItem.type !== 'function_call') {
return false;
}
const invocationName = (0, toolInvocation_1.getToolInvocationNameFromFingerprint)(completionEvidence.fingerprint);
const qualifiedName = invocationName
? (0, toolIdentity_1.getFunctionToolLegacyStateKeyFromStateKey)(invocationName)
: undefined;
const callName = (0, toolIdentity_1.getToolCallName)(callItem.rawItem);
return behavior.stopAtToolNames.some((toolName) => toolName === callName || toolName === qualifiedName);
});
}
function ownsPendingTerminalToolFinalization(state, pending, options) {
const provenance = pending.terminalToolFinalization;
const currentStep = state._currentStep;
if (!provenance ||
(provenance.behavior === 'function' &&
options.allowUnhydratedTerminalToolStep === true) ||
currentStep?.type !== 'next_step_final_output' ||
currentStep.output !== provenance.finalOutput ||
getPendingSessionWriteBehaviorKind(state._currentAgent.toolUseBehavior) !==
provenance.behavior ||
(options.allowUnhydratedTerminalToolStep !== true &&
!(0, blockedOutputPersistence_1.hasTerminalToolOutputSource)(state))) {
return false;
}
const completedInvocations = state._completedToolInvocationEvidence.get(state._currentAgent);
const pendingGeneratedItems = state._generatedItems.slice(pending.alreadyPersistedCount, pending.persistedItemCount);
if (pendingGeneratedItems.some((item) => {
const callId = getCompletionOutputCallId(item, state._currentAgent);
const completionEvidence = callId
? completedInvocations?.get(callId)
: undefined;
return (completionEvidence !== undefined && completionEvidence.items[1] !== item);
})) {
return false;
}
const liveProducer = options.allowUnhydratedTerminalToolStep === true
? undefined
: pendingSessionWriteTerminalProducers.get(state);
if (provenance.behavior === 'function') {
return Boolean(liveProducer &&
liveProducer.behavior === state._currentAgent.toolUseBehavior &&
liveProducer.finalOutput === provenance.finalOutput &&
liveProducer.selectedCallId === undefined &&
liveProducer.resultItems.length > 0 &&
liveProducer.resultItems.every((item, index) => {
const generatedItemIndex = state._generatedItems.indexOf(item);
const callId = liveProducer.resultCallIds[index];
return (callId !== undefined &&
generatedItemIndex >= pending.alreadyPersistedCount &&
generatedItemIndex < pending.persistedItemCount &&
completedInvocations?.has(callId));
}));
}
const selectedCallId = provenance.selectedCallId;
const selectedGeneratedItemIndex = provenance.selectedGeneratedItemIndex;
if (!selectedCallId ||
selectedGeneratedItemIndex === undefined ||
selectedGeneratedItemIndex < pending.alreadyPersistedCount ||
selectedGeneratedItemIndex >= pending.persistedItemCount) {
return false;
}
const selectedOutput = state._generatedItems[selectedGeneratedItemIndex];
const completionEvidence = completedInvocations?.get(selectedCallId);
if (!(selectedOutput instanceof items_1.RunToolCallOutputItem) ||
getCompletionOutputCallId(selectedOutput, state._currentAgent) !==
selectedCallId ||
!completionEvidence ||
(0, smartString_1.toSmartString)(selectedOutput.output) !== provenance.finalOutput) {
return false;
}
const currentBehavior = state._currentAgent.toolUseBehavior;
if (liveProducer &&
liveProducer.finalOutput === provenance.finalOutput &&
liveProducer.selectedCallId === selectedCallId) {
const liveCandidates = liveProducer.resultItems.map((item, index) => {
const candidate = getPendingDeclarativeTerminalCandidate(state, item, completedInvocations);
const generatedItemIndex = state._generatedItems.indexOf(item);
return candidate &&
liveProducer.resultCallIds[index] === candidate.callId &&
generatedItemIndex >= pending.alreadyPersistedCount &&
generatedItemIndex < pending.persistedItemCount
? candidate
: undefined;
});
if (liveCandidates.every((candidate) => candidate !== undefined)) {
return (selectPendingDeclarativeTerminalCandidate(currentBehavior, liveCandidates)?.outputItem === selectedOutput);
}
}
const restoredCandidates = pendingGeneratedItems.flatMap((item) => {
const candidate = getPendingDeclarativeTerminalCandidate(state, item, completedInvocations);
return candidate ? [candidate] : [];
});
return (selectPendingDeclarativeTerminalCandidate(currentBehavior, restoredCandidates)?.outputItem === selectedOutput);
}
function assertSchemaVersionSupportsPendingInput(schemaVersion, stateJson, hasPendingInputField) {
if (schemaVersionSupportsV118State(schemaVersion)) {
validateAcceptedResponseState(stateJson);
return;
}
const hasInputItems = [
...stateJson.generatedItems,
...(stateJson.lastProcessedResponse?.newItems ?? []),
].some((item) => item.type === 'input_item');
const hasAcceptedResponseState = hasAcceptedResponseStepFields(stateJson.currentStep);
if (!hasPendingInputField &&
(stateJson.pendingInput?.length ?? 0) === 0 &&
!hasInputItems &&
!hasAcceptedResponseState) {
return;
}
throw new errors_1.UserError(`Run state schema version ${schemaVersion} does not support pending input. Please reserialize the run state with schema ${exports.CURRENT_SCHEMA_VERSION}.`);
}
function hasAcceptedResponseStepFields(currentStep) {
if (currentStep?.type === 'next_step_interruption') {
return (hasOwnProperty(currentStep.data, 'responseAccepted') ||
hasOwnProperty(currentStep.data, 'localProcessingStarted'));
}
return (currentStep?.type === 'next_step_final_output' &&
(currentStep.responseAccepted !== undefined ||
currentStep.localFinalizationStarted !== undefined));
}
function hasOwnProperty(record, key) {
return Object.prototype.hasOwnProperty.call(record, key);
}
function validateAcceptedResponseState(stateJson) {
const currentStep = stateJson.currentStep;
let responseAccepted = false;
let invalidProgress = false;
if (currentStep?.type === 'next_step_interruption') {
const hasAccepted = hasOwnProperty(currentStep.data, 'responseAccepted');
const hasStarted = hasOwnProperty(currentStep.data, 'localProcessingStarted');
responseAccepted = currentStep.data.responseAccepted === true;
invalidProgress =
(hasAccepted && !responseAccepted) ||
(hasStarted && currentStep.data.localProcessingStarted !== true) ||
(hasStarted && !responseAccepted);
}
else if (currentStep?.type === 'next_step_final_output') {
responseAccepted = currentStep.responseAccepted === true;
invalidProgress =
currentStep.localFinalizationStarted === true && !responseAccepted;
}
if (invalidProgress) {
throw new errors_1.UserError('RunState accepted response progress is invalid.');
}
if (responseAccepted &&
!(0, conversation_1.getServerConversationOwner)(stateJson.conversationId, stateJson.previousResponseId)) {
throw new errors_1.UserError('Accepted model response state requires exactly one server-managed conversation owner.');
}
}
function validateOutputGuardrailSessionPersistenceState(stateJson) {
const persistedItemCount = stateJson.currentTurnPersistedItemCount ?? 0;
const deferredIndexes = stateJson.currentTurnDeferredSessionItemIndexes ?? [];
if (deferredIndexes.length > 0 ||
stateJson.currentTurnBlockedSessionStartIndex !== undefined ||
(stateJson.currentTurnExecutedWithSessionBinding === true &&
stateJson.pendingSessionWrite === undefined) ||
stateJson.pendingSessionHistoryTransaction !== undefined) {
throw new errors_1.UserError('Serialized output guardrail session transaction authority cannot be resumed safely. Start a new run from the persisted session history.');
}
if (persistedItemCount > stateJson.generatedItems.length) {
throw new errors_1.UserError('Run state contains unsupported serialized output guardrail session transaction state.');
}
const hasUnsupportedExecutionProvenance = [
...stateJson.generatedItems,
...(stateJson.lastProcessedResponse?.newItems ?? []),
].some((item) => item.type === 'tool_call_output_item' &&
item.executionStatus === 'executed' &&
item.rawItem.type !== 'function_call_result' &&
item.rawItem.type !== 'shell_call_output' &&
item.rawItem.type !== 'computer_call_result' &&
item.rawItem.type !== 'apply_patch_call_output');
if (hasUnsupportedExecutionProvenance) {
throw new errors_1.UserError('Run state contains execution provenance for an unsupported tool output type.');
}
}
function containsSerializedCompactionRunItems(stateJson) {
return (containsCompactionRunItems(stateJson.generatedItems) ||
containsCompactionRunItems(stateJson.lastProcessedResponse?.newItems));
}
function containsCompactionRunItems(items) {
return Boolean(items?.some((item) => item.type === 'compaction_item'));
}
function containsProgrammaticToolCallingState(stateJson) {
return (containsProgrammaticToolCallingProtocolItems(stateJson.originalInput) ||
stateJson.modelResponses.some(containsProgrammaticToolCallingInModelResponse) ||
containsProgrammaticToolCallingInModelResponse(stateJson.lastModelResponse) ||
containsProgrammaticToolCallingRunItems(stateJson.generatedItems) ||
containsProgrammaticToolCallingInProcessedResponse(stateJson.lastProcessedResponse));
}
function containsProgrammaticToolCallingInModelResponse(modelResponse) {
return Boolean(modelResponse?.output.some(isProgrammaticToolCallingProtocolItem));
}
function containsProgrammaticToolCallingRunItems(items) {
return Boolean(items?.some((item) => isProgrammaticToolCallingProtocolItem(item.rawItem)));
}
function containsProgrammaticToolCallingProtocolItems(items) {
return Array.isArray(items)
? items.some(isProgrammaticToolCallingProtocolItem)
: false;
}
function containsProgrammaticToolCallingInProcessedResponse(processedResponse) {
if (!processedResponse) {
return false;
}
return (containsProgrammaticToolCallingRunItems(processedResponse.newItems) ||
processedResponse.functions.some(({ toolCall }) => isProgrammaticToolCallingProtocolItem(toolCall)) ||
(processedResponse.functionToolsNotFound ?? []).some(({ toolCall }) => isProgrammaticToolCallingProtocolItem(toolCall)) ||
(processedResponse.shellActions ?? []).some(({ toolCall }) => isProgrammaticToolCallingProtocolItem(toolCall)) ||
(processedResponse.applyPatchActions ?? []).some(({ toolCall }) => isProgrammaticToolCallingProtocolItem(toolCall)));
}
function isProgrammaticToolCallingProtocolItem(value) {
if (!value || typeof value !== 'object') {
return false;
}
const item = value;
if (item.type === 'program' || item.type === 'program_output') {
return true;
}
if (item.type !== 'function_call' &&
item.type !== 'function_call_result' &&
item.type !== 'shell_call' &&
item.type !== 'shell_call_output' &&
item.type !== 'apply_patch_call' &&
item.type !== 'apply_patch_call_output' &&
item.type !== 'hosted_tool_call') {
return false;
}
return (item.caller?.type === 'program' && typeof item.caller.callerId === 'string');
}
function containsSerializedToolSearchState(stateJson) {
return (containsToolSearchProtocolItems(stateJson.originalInput) ||
containsToolSearchInModelResponses(stateJson.modelResponses) ||
containsToolSearchInModelResponse(stateJson.lastModelResponse) ||
containsToolSearchRunItems(stateJson.generatedItems) ||
containsToolSearchInProcessedResponse(stateJson.lastProcessedResponse));
}
function containsToolSearchInModelResponses(modelResponses) {
return modelResponses.some(containsToolSearchInModelResponse);
}
function containsToolSearchInModelResponse(modelResponse) {
return Boolean(modelResponse?.output.some((item) => isToolSearchProtocolType(item.type)));
}
function containsToolSearchRunItems(items) {
return Boolean(items?.some((item) => item.type === 'tool_search_call_item' ||
item.type === 'tool_search_output_item' ||
isToolSearchProtocolType(item.rawItem?.type)));
}
function containsToolSearchProtocolItems(items) {
return Array.isArray(items)
? items.some((item) => isToolSearchProtocolType(item.type))
: false;
}
function containsToolSearchInProcessedResponse(processedResponse) {
return containsToolSearchRunItems(processedResponse?.newItems);
}
function containsSerializedToolOutputCustomData(stateJson) {
return (containsToolOutputCustomDataRunItems(stateJson.generatedItems) ||
containsToolOutputCustomDataRunItems(stateJson.lastProcessedResponse?.newItems));
}
function containsStructuredToolOutputState(stateJson) {
if (containsStructuredToolOutputRunItems(stateJson.generatedItems) ||
containsStructuredToolOutputRunItems(stateJson.lastProcessedResponse?.newItems)) {
return true;
}
return Boolean(stateJson.completedToolInvocationEvidence?.some(({ invocations }) => Object.values(invocations).some(({ items }) => items.some((reference) => 'item' in reference &&
reference.item.type === 'tool_call_output_item' &&
typeof reference.item.output !== 'string'))));
}
function containsStructuredToolOutputRunItems(items) {
return Boolean(items?.some((item) => item.type === 'tool_call_output_item' &&
typeof item.output !== 'string'));
}
function containsToolOutputCustomDataRunItems(items) {
return Boolean(items?.some((item) => item.type === 'tool_call_output_item' &&
Object.prototype.hasOwnProperty.call(item, 'customData')));
}
function isToolSearchProtocolType(type) {
return type === 'tool_search_call' || type === 'tool_search_output';
}
function addSerializedRuntimeToolKey(runtimeToolKeys, runtimeToolKey) {
if (runtimeToolKeys.has(runtimeToolKey)) {
throw new errors_1.UserError('Serialized client tool_search output contains multiple tools with the same routed identity. Assign unique tool names or namespaces before resuming RunState.');
}
runtimeToolKeys.add(runtimeToolKey);
}
function collectSerializedRuntimeToolKeys(value, runtimeToolKeys, namespace, validateRecognizedShape = false) {
if (!value || typeof value !== 'object') {
return;
}
const candidate = value;
if (candidate.type === 'namespace') {
if (typeof candidate.name !== 'string' ||
candidate.name.length === 0 ||
!Array.isArray(candidate.tools)) {
if (validateRecognizedShape) {
throw new errors_1.UserError('Serialized client tool_search output contains a namespace without a valid routed identity.');
}
return;
}
const nestedNamespace = typeof candidate.name === 'string' ? candidate.name : namespace;
for (const nestedTool of candidate.tools) {
collectSerializedRuntimeToolKeys(nestedTool, runtimeToolKeys, nestedNamespace, true);
}
return;
}
const explicitNamespace = typeof candidate.namespace === 'string' && candidate.namespace.length > 0
? candidate.namespace
: namespace;
if (candidate.type === 'function') {
if (typeof candidate.name !== 'string' || candidate.name.length === 0) {
if (validateRecognizedShape) {
throw new errors_1.UserError('Serialized client tool_search output contains a function without a valid routed identity.');
}
return;
}
const directNamespace = typeof candidate.namespace === 'string' && candidate.namespace.length > 0
? candidate.namespace
: undefined;
if (candidate.name === namespace || candidate.name === directNamespace) {
throw new errors_1.UserError('Responses tool search reserves same-name namespaces for deferred top-level function tools. Rename the namespace or tool name to avoid ambiguous dispatch.');
}
const lookupKey = (0, toolIdentity_1.getFunctionToolLookupKey)(candidate.name, explicitNamespace ??
(candidate.deferLoading === true || candidate.defer_loading === true
? candidate.name
: undefined));
if (lookupKey) {
addSerializedRuntimeToolKey(runtimeToolKeys, lookupKey);
}
return;
}
if (candidate.type === 'mcp') {
if (typeof candidate.server_label !== 'string' ||
candidate.server_label.length === 0) {
if (validateRecognizedShape) {
throw new errors_1.UserError('Serialized client tool_search output contains an MCP tool without a valid routed identity.');
}
return;
}
addSerializedRuntimeToolKey(runtimeToolKeys, `mcp:${candidate.server_label}`);
return;
}
if (!candidate.providerData || typeof candidate.providerData !== 'object') {
return;
}
collectSerializedRuntimeToolKeys(candidate.providerData, runtimeToolKeys, explicitNamespace, false);
}
function getSerializedRuntimeToolKeys(toolSearchOutput) {
const runtimeToolKeys = new Set();
for (const tool of toolSearchOutput.tools) {
collectSerializedRuntimeToolKeys(tool, runtimeToolKeys, undefined, true);
}
return runtimeToolKeys;
}
function getRuntimeToolKeys(runtimeTools, options = {}) {
const runtimeToolKeys = new Set();
const runtimeToolsByKey = new Map();
for (const tool of runtimeTools) {
const runtimeToolKey = (0, tool_1.getToolSearchRuntimeRoutingKey)(tool);
if (!runtimeToolKey) {
if (options.allowUnsupported) {
continue;
}
throw new errors_1.UserError('Client tool_search execute() returned an unsupported runtime tool during RunState rehydration.');
}
if (options.rejectDuplicateKeys && runtimeToolsByKey.has(runtimeToolKey)) {
throw new errors_1.UserError('Client tool_search execute() returned multiple tools with the same routed identity during RunState rehydration.');
}
runtimeToolsByKey.set(runtimeToolKey, tool);
runtimeToolKeys.add(runtimeToolKey);
}
return runtimeToolKeys;
}
function formatRuntimeToolKeys(runtimeToolKeys) {
return [...runtimeToolKeys].sort().join(', ');
}
function selectSerializedRuntimeTools(args) {
const { agent, toolSearchCall, expectedRuntimeToolKeys, enabledRuntimeTools, } = args;
const actualRuntimeToolKeys = getRuntimeToolKeys(enabledRuntimeTools, {
rejectDuplicateKeys: true,
});
const hasExpectedKeys = [...expectedRuntimeToolKeys].every((runtimeToolKey) => actualRuntimeToolKeys.has(runtimeToolKey));
const hasActualKeys = [...actualRuntimeToolKeys].every((runtimeToolKey) => expectedRuntimeToolKeys.has(runtimeToolKey));
if (hasExpectedKeys && hasActualKeys) {
return enabledRuntimeTools.filter((runtimeTool) => {
const runtimeToolKey = (0, tool_1.getToolSearchRuntimeRoutingKey)(runtimeTool);
return (runtimeToolKey != null && expectedRuntimeToolKeys.has(runtimeToolKey));
});
}
if (logger_1.default.dontLogToolData) {
throw new errors_1.UserError('RunState cannot resume custom client tool_search because the registered execute callback returned different runtime tools than the serialized state.');
}
const callId = (0, toolSearch_1.resolveToolSearchCallId)(toolSearchCall);
throw new errors_1.UserError(`RunState cannot resume custom client tool_search call ${callId} for agent ${agent.name} because the registered execute callback returned runtime tools [${formatRuntimeToolKeys(actualRuntimeToolKeys)}] but the serialized state expects [${formatRuntimeToolKeys(expectedRuntimeToolKeys)}].`);
}
async function getConfiguredAgentTools(args) {
const { agent, context, configuredToolsByAgent } = args;
const existing = configuredToolsByAgent.get(agent);
if (existing) {
return existing;
}
const configuredTools = (await agent.getAllTools(context));
configuredToolsByAgent.set(agent, configuredTools);
return configuredTools;
}
function throwAmbiguousFunctionCallId(agent, callId, routedToolKeys) {
if (logger_1.default.dontLogToolData) {
throw new errors_1.UserError('RunState cannot resume because a function call ID is associated with multiple routed tool identities.');
}
throw new errors_1.UserError(`RunState cannot resume function call ${callId} for agent ${agent.name} because the call ID is reused across routed tool identities [${[
...routedToolKeys,
].join(', ')}]. Use a unique call ID for each function call.`);
}
function assertUnambiguousFunctionCallIds(state, agentMap, serializedProcessedResponse) {
const deferredFunctionCallExpectations = [];
const generatedCallsByAgent = new Map();
for (const item of state._generatedItems) {
if (!(item instanceof items_1.RunToolCallItem) ||
item.rawItem.type !== 'function_call') {
continue;
}
const agent = item.agent;
const routedToolKey = (0, toolIdentity_1.getFunctionToolStateKeyForCall)(item.rawItem);
if (!routedToolKey) {
continue;
}
const callsById = generatedCallsByAgent.get(agent) ?? new Map();
const previousRoutedToolKey = callsById.get(item.rawItem.callId);
if (previousRoutedToolKey && previousRoutedToolKey !== routedToolKey) {
throwAmbiguousFunctionCallId(agent, item.rawItem.callId, [
previousRoutedToolKey,
routedToolKey,
]);
}
callsById.set(item.rawItem.callId, routedToolKey);
generatedCallsByAgent.set(agent, callsById);
}
const processedCallsByAgent = new Map();
if (serializedProcessedResponse) {
const agent = state._currentAgent;
const processedCallsById = new Map();
for (const functionCall of serializedProcessedResponse.functions) {
const toolCall = functionCall.toolCall;
const rawRoutedToolKey = (0, toolIdentity_1.getFunctionToolStateKeyForCall)(toolCall);
if (!rawRoutedToolKey) {
continue;
}
const serializedToolStateKey = (0, toolIdentity_1.getToolCallNamespace)(toolCall)
? rawRoutedToolKey
: (0, toolIdentity_1.getFunctionToolStateKey)(functionCall.tool);
const resolvedRoutedToolKey = serializedToolStateKey
? (0, toolIdentity_1.getFunctionToolStateKeyForResolvedCall)(toolCall, functionCall.tool, serializedToolStateKey)
: rawRoutedToolKey;
if (!resolvedRoutedToolKey) {
throwAmbiguousFunctionCallId(agent, toolCall.callId, [
rawRoutedToolKey,
serializedToolStateKey,
]);
}
if (resolvedRoutedToolKey !== rawRoutedToolKey) {
deferredFunctionCallExpectations.push({
agent,
toolCall,
resolvedRoutedToolKey,
});
}
const callId = toolCall.callId;
const previousProcessedIdentity = processedCallsById.get(callId);
if (previousProcessedIdentity &&
previousProcessedIdentity.resolvedRoutedToolKey !==
resolvedRoutedToolKey) {
throwAmbiguousFunctionCallId(agent, callId, [
previousProcessedIdentity.resolvedRoutedToolKey,
resolvedRoutedToolKey,
]);
}
const generatedRoutedToolKey = generatedCallsByAgent
.get(agent)
?.get(callId);
if (generatedRoutedToolKey &&
generatedRoutedToolKey !== rawRoutedToolKey) {
throwAmbiguousFunctionCallId(agent, callId, [
generatedRoutedToolKey,
rawRoutedToolKey,
]);
}
processedCallsById.set(callId, {
rawRoutedToolKey,
resolvedRoutedToolKey,
});
}
processedCallsByAgent.set(agent, processedCallsById);
}
if (state._currentStep?.type !== 'next_step_interruption') {
return deferredFunctionCallExpectations;
}
for (const value of state._currentStep.data?.interruptions ?? []) {
if (!value || typeof value !== 'object') {
continue;
}
const interruption = value;
if (!interruption.rawItem ||
typeof interruption.rawItem !== 'object' ||
interruption.rawItem.type !== 'function_call') {
continue;
}
const rawItem = interruption.rawItem;
const rawItemRoutedToolKey = (0, toolIdentity_1.getFunctionToolStateKeyForCall)(rawItem);
if (!rawItemRoutedToolKey) {
continue;
}
const interruptionAgent = interruption.agent && typeof interruption.agent === 'object'
? resolveSerializedAgent(interruption.agent, agentMap, state._currentAgent)
: state._currentAgent;
const generatedRoutedToolKey = generatedCallsByAgent
.get(interruptionAgent)
?.get(rawItem.callId);
if (generatedRoutedToolKey &&
generatedRoutedToolKey !== rawItemRoutedToolKey) {
throwAmbiguousFunctionCallId(interruptionAgent, rawItem.callId, [
generatedRoutedToolKey,
rawItemRoutedToolKey,
]);
}
const processedIdentity = processedCallsByAgent
.get(interruptionAgent)
?.get(rawItem.callId);
if (processedIdentity &&
processedIdentity.rawRoutedToolKey !== rawItemRoutedToolKey) {
throwAmbiguousFunctionCallId(interruptionAgent, rawItem.callId, [
processedIdentity.rawRoutedToolKey,
rawItemRoutedToolKey,
]);
}
const expectedRoutedToolKey = processedIdentity?.resolvedRoutedToolKey ?? rawItemRoutedToolKey;
if (typeof interruption.functionToolStateKey === 'string') {
const validationRequiresPendingNestedState = interruptionAgent !== state._currentAgent && !processedIdentity;
if (!validationRequiresPendingNestedState &&
interruption.functionToolStateKey !== expectedRoutedToolKey) {
throwAmbiguousFunctionCallId(interruptionAgent, rawItem.callId, [
expectedRoutedToolKey,
interruption.functionToolStateKey,
]);
}
}
}
return deferredFunctionCallExpectations;
}
function collectDeferredToolSearchProvenanceByCallId(args) {
const { state, effectiveToolSearchOutputs, serializedRuntimeToolKeysByOutput, } = args;
const effectiveOutputs = new Set(effectiveToolSearchOutputs);
const routedOwnersByAgent = new Map();
const entriesByReplacementKeyByAgent = new Map();
const deferredKeysByCallIdByAgent = new Map();
for (const item of state._generatedItems) {
if (item instanceof items_1.RunToolSearchOutputItem && effectiveOutputs.has(item)) {
const agent = item.agent;
const routedOwners = routedOwnersByAgent.get(agent) ?? new Map();
const entriesByReplacementKey = entriesByReplacementKeyByAgent.get(agent) ?? new Map();
const replacementKey = (0, toolSearch_1.getToolSearchOutputReplacementKey)(item.rawItem);
if (replacementKey) {
const previousEntry = entriesByReplacementKey.get(replacementKey);
if (previousEntry) {
for (const [routedKey, owner] of routedOwners) {
if (owner === previousEntry) {
routedOwners.delete(routedKey);
}
}
}
entriesByReplacementKey.set(replacementKey, item);
}
for (const routedKey of serializedRuntimeToolKeysByOutput.get(item) ??
[]) {
routedOwners.set(routedKey, item);
}
routedOwnersByAgent.set(agent, routedOwners);
entriesByReplacementKeyByAgent.set(agent, entriesByReplacementKey);
continue;
}
if (!(item instanceof items_1.RunToolCallItem) ||
item.rawItem.type !== 'function_call' ||
(0, toolIdentity_1.getToolCallNamespace)(item.rawItem)) {
continue;
}
const name = item.rawItem.name;
const bareKey = (0, toolIdentity_1.getFunctionToolLookupKey)(name);
const deferredKey = (0, toolIdentity_1.getFunctionToolLookupKey)(name, name);
const routedOwners = routedOwnersByAgent.get(item.agent);
if (!bareKey ||
!deferredKey ||
routedOwners?.has(bareKey) ||
!routedOwners?.has(deferredKey)) {
continue;
}
const keysByCallId = deferredKeysByCallIdByAgent.get(item.agent) ?? new Map();
keysByCallId.set(item.rawItem.callId, deferredKey);
deferredKeysByCallIdByAgent.set(item.agent, keysByCallId);
}
return deferredKeysByCallIdByAgent;
}
function assertDeferredFunctionCallProvenance(args) {
const { expectations, capabilitySnapshotsByAgent, deferredToolSearchKeysByCallIdByAgent, } = args;
for (const expectation of expectations) {
const snapshot = capabilitySnapshotsByAgent.get(expectation.agent);
if (snapshot?.handoffMap.has(expectation.toolCall.name)) {
throwAmbiguousFunctionCallId(expectation.agent, expectation.toolCall.callId, [expectation.toolCall.name, expectation.resolvedRoutedToolKey]);
}
const configuredOwner = snapshot
? (0, toolIdentity_1.resolveFunctionToolCall)(expectation.toolCall, snapshot.functionMap)
: undefined;
if (configuredOwner) {
if ((0, toolIdentity_1.getFunctionToolStateKey)(configuredOwner) ===
expectation.resolvedRoutedToolKey) {
continue;
}
throwAmbiguousFunctionCallId(expectation.agent, expectation.toolCall.callId, [
(0, toolIdentity_1.getFunctionToolStateKey)(configuredOwner) ?? configuredOwner.name,
expectation.resolvedRoutedToolKey,
]);
}
if (deferredToolSearchKeysByCallIdByAgent
.get(expectation.agent)
?.get(expectation.toolCall.callId) !== expectation.resolvedRoutedToolKey) {
throwAmbiguousFunctionCallId(expectation.agent, expectation.toolCall.callId, [
(0, toolIdentity_1.getFunctionToolStateKeyForCall)(expectation.toolCall) ??
expectation.toolCall.name,
expectation.resolvedRoutedToolKey,
]);
}
}
}
async function rehydrateToolSearchRuntimeTools(state, options) {
const deferredFunctionCallExpectations = assertUnambiguousFunctionCallIds(state, options.agentMap, options.serializedProcessedResponse);
const configuredToolsByAgent = new Map();
const capabilitySnapshotsByAgent = new Map();
const toolSearchCallOccurrences = [];
const latestOccurrenceByAgentAndCallId = new Map();
const pendingOccurrencesByAgent = new Map();
for (const item of state._generatedItems) {
if (item instanceof items_1.RunToolSearchCallItem) {
if ((0, toolSearch_1.getToolSearchExecution)(item.rawItem) === 'server') {
continue;
}
const callId = (0, toolSearch_1.resolveToolSearchCallId)(item.rawItem);
const agent = item.agent;
const occurrence = {
pendingCall: {
agent,
toolSearchCall: item.rawItem,
},
};
toolSearchCallOccurrences.push(occurrence);
const pendingOccurrences = pendingOccurrencesByAgent.get(agent) ?? [];
pendingOccurrences.push(occurrence);
pendingOccurrencesByAgent.set(agent, pendingOccurrences);
const occurrencesByCallId = latestOccurrenceByAgentAndCallId.get(agent) ?? new Map();
occurrencesByCallId.set(callId, occurrence);
latestOccurrenceByAgentAndCallId.set(agent, occurrencesByCallId);
continue;
}
if (!(item instanceof items_1.RunToolSearchOutputItem) ||
(0, toolSearch_1.getToolSearchExecution)(item.rawItem) === 'server') {
continue;
}
const agent = item.agent;
const explicitCallId = (0, toolSearch_1.getToolSearchProviderCallId)(item.rawItem);
const pendingOccurrences = pendingOccurrencesByAgent.get(agent) ?? [];
const occurrence = explicitCallId
? latestOccurrenceByAgentAndCallId.get(agent)?.get(explicitCallId)
: pendingOccurrences.shift();
if (!occurrence) {
const callId = (0, toolSearch_1.resolveToolSearchCallId)(item.rawItem);
throw new errors_1.UserError(logger_1.default.dontLogToolData
? 'RunState cannot resume custom client tool_search because the serialized state is missing the matching tool_search call item.'
: `RunState cannot resume custom client tool_search output ${callId} for agent ${item.agent.name} because the serialized state is missing the matching tool_search call item.`);
}
if (explicitCallId) {
const pendingIndex = pendingOccurrences.indexOf(occurrence);
if (pendingIndex >= 0) {
pendingOccurrences.splice(pendingIndex, 1);
}
}
occurrence.output = item;
}
const effectiveToolSearchOutputs = toolSearchCallOccurrences
.map((occurrence) => occurrence.output)
.filter((item) => Boolean(item));
const occurrencesByOutput = new Map(toolSearchCallOccurrences.flatMap((occurrence) => occurrence.output ? [[occurrence.output, occurrence]] : []));
const serializedRuntimeToolKeysByOutput = new Map();
for (const item of effectiveToolSearchOutputs) {
serializedRuntimeToolKeysByOutput.set(item, getSerializedRuntimeToolKeys(item.rawItem));
}
const agentsToPrepare = new Set(effectiveToolSearchOutputs.map((item) => item.agent));
if (options.serializedProcessedResponse) {
agentsToPrepare.add(state._currentAgent);
}
if (options.prepareCurrentAgentForLegacyApprovals) {
agentsToPrepare.add(state._currentAgent);
}
for (const agent of agentsToPrepare) {
const configuredTools = await getConfiguredAgentTools({
agent,
context: state._context,
configuredToolsByAgent,
});
const existingRuntimeTools = state.getToolSearchRuntimeTools(agent);
const enabledRuntimeTools = await getEnabledToolSearchRuntimeTools(state, agent);
const capabilities = (0, modelPreparation_1.resolveModelVisibleToolNameCollisions)([...configuredTools, ...enabledRuntimeTools], await agent.getEnabledHandoffs(state._context), 'warn');
const availableTools = [...capabilities.tools];
capabilitySnapshotsByAgent.set(agent, {
availableTools,
callbackTools: [...availableTools],
handoffs: capabilities.handoffs,
functionMap: (0, toolIdentity_1.buildFunctionToolLookupMap)(availableTools.filter((tool) => tool.type === 'function')),
functionToolsByCallId: new Map(),
runtimeFunctionTools: new Set(existingRuntimeTools.filter((tool) => tool.type === 'function')),
handoffMap: new Map(capabilities.handoffs.map((handoff) => [handoff.toolName, handoff])),
mcpToolMap: new Map(availableTools
.filter((tool) => tool.type === 'hosted_tool' && tool.providerData?.type === 'mcp')
.map((tool) => [tool.providerData.server_label, tool])),
replaceableRuntimeToolKeys: new Set(existingRuntimeTools
.map((tool) => (0, tool_1.getToolSearchRuntimeRoutingKey)(tool))
.filter((key) => typeof key === 'string')),
});
}
assertDeferredFunctionCallProvenance({
expectations: deferredFunctionCallExpectations,
capabilitySnapshotsByAgent,
deferredToolSearchKeysByCallIdByAgent: collectDeferredToolSearchProvenanceByCallId({
state: state,
effectiveToolSearchOutputs,
serializedRuntimeToolKeysByOutput,
}),
});
if (schemaVersionSupportsV116State(options.schemaVersion) &&
options.serializedProcessedResponse) {
const currentAgent = state._currentAgent;
const currentSnapshot = capabilitySnapshotsByAgent.get(currentAgent);
for (const serializedHandoff of options.serializedProcessedResponse
.handoffs) {
if (!serializedHandoff.targetAgent) {
throw new errors_1.UserError('Run state handoff is missing its required target agent identity.');
}
const targetAgent = resolveSerializedAgent(serializedHandoff.targetAgent, options.agentMap);
const handoff = currentSnapshot.handoffMap.get(serializedHandoff.handoff.toolName);
if (!handoff || handoff.agent !== targetAgent) {
throw new errors_1.UserError(`Handoff ${serializedHandoff.handoff.toolName} not found`);
}
}
}
for (const agent of new Set(effectiveToolSearchOutputs.map((item) => item.agent))) {
(0, toolSearch_2.validateClientToolSearchSupport)(capabilitySnapshotsByAgent.get(agent).availableTools);
}
const rehydrationRecords = new Map();
for (const item of effectiveToolSearchOutputs) {
const callId = (0, toolSearch_1.resolveToolSearchCallId)(item.rawItem);
const agent = item.agent;
const snapshot = capabilitySnapshotsByAgent.get(agent);
const expectedRuntimeToolKeys = serializedRuntimeToolKeysByOutput.get(item);
const pendingCall = occurrencesByOutput.get(item).pendingCall;
const toolSearchTool = (0, toolSearch_2.getClientToolSearchHelper)(snapshot.availableTools);
const hasCustomExecutor = Boolean(toolSearchTool && (0, tool_1.getClientToolSearchExecutor)(toolSearchTool));
if (expectedRuntimeToolKeys.size === 0) {
rehydrationRecords.set(item, {
pendingCall,
expectedRuntimeToolKeys,
...(hasCustomExecutor ? { toolSearchTool } : {}),
});
continue;
}
if (!hasCustomExecutor) {
const availableRuntimeToolKeys = getRuntimeToolKeys(snapshot.availableTools, { allowUnsupported: true });
if ([...expectedRuntimeToolKeys].every((runtimeToolKey) => availableRuntimeToolKeys.has(runtimeToolKey))) {
rehydrationRecords.set(item, {
pendingCall,
expectedRuntimeToolKeys,
});
continue;
}
if (logger_1.default.dontLogToolData) {
throw new errors_1.UserError('RunState cannot resume custom client tool_search because the agent no longer provides toolSearchTool({ execution: "client", execute }).');
}
throw new errors_1.UserError(`RunState cannot resume custom client tool_search call ${callId} for agent ${pendingCall.agent.name} because the agent no longer provides toolSearchTool({ execution: "client", execute }).`);
}
rehydrationRecords.set(item, {
pendingCall,
expectedRuntimeToolKeys,
toolSearchTool,
});
}
for (const generatedItem of state._generatedItems) {
if (!(generatedItem instanceof items_1.RunToolSearchOutputItem)) {
continue;
}
const record = rehydrationRecords.get(generatedItem);
if (!record?.toolSearchTool) {
continue;
}
const agent = record.pendingCall.agent;
const snapshot = capabilitySnapshotsByAgent.get(agent);
const { runtimeTools, callbackRuntimeTools } = await (0, toolSearch_2.executeCustomClientToolSearch)({
agent,
runContext: state._context,
toolSearchCall: record.pendingCall.toolSearchCall,
toolSearchTool: record.toolSearchTool,
tools: snapshot.callbackTools,
});
const serializedRuntimeTools = selectSerializedRuntimeTools({
agent,
toolSearchCall: record.pendingCall.toolSearchCall,
expectedRuntimeToolKeys: record.expectedRuntimeToolKeys,
enabledRuntimeTools: runtimeTools,
});
record.runtimeTools = serializedRuntimeTools;
snapshot.callbackTools.push(...callbackRuntimeTools);
}
for (const generatedItem of state._generatedItems) {
if (generatedItem instanceof items_1.RunToolSearchOutputItem) {
const record = rehydrationRecords.get(generatedItem);
if (!record?.runtimeTools) {
continue;
}
const agent = record.pendingCall.agent;
const snapshot = capabilitySnapshotsByAgent.get(agent);
const replacedRuntimeTools = state.getToolSearchRuntimeToolsForOutput(agent, generatedItem.rawItem);
const registeredRuntimeTools = (0, toolSearch_2.registerRuntimeToolSearchTools)({
availableTools: snapshot.availableTools,
functionMap: snapshot.functionMap,
handoffMap: snapshot.handoffMap,
mcpToolMap: snapshot.mcpToolMap,
replaceableRuntimeToolKeys: snapshot.replaceableRuntimeToolKeys,
replacedRuntimeTools,
runtimeTools: record.runtimeTools,
});
for (const runtimeTool of registeredRuntimeTools) {
if (runtimeTool.type === 'function') {
snapshot.runtimeFunctionTools.add(runtimeTool);
}
}
state.recordToolSearchRuntimeTools(agent, generatedItem.rawItem, registeredRuntimeTools);
continue;
}
if (generatedItem instanceof items_1.RunToolCallItem &&
generatedItem.rawItem.type === 'function_call') {
const agent = generatedItem.agent;
const snapshot = capabilitySnapshotsByAgent.get(agent);
if (!snapshot) {
continue;
}
const functionTool = (0, toolIdentity_1.resolveFunctionToolCall)(generatedItem.rawItem, snapshot.functionMap);
if (functionTool) {
if (snapshot.runtimeFunctionTools.has(functionTool)) {
snapshot.functionToolsByCallId.set(generatedItem.rawItem.callId, functionTool);
}
}
}
}
return capabilitySnapshotsByAgent;
}
function restoreCurrentResponseProcessedOwnership(state, stateJson) {
const ownership = stateJson.currentResponseGeneratedItemOwnership;
if (!ownership || !state._lastProcessedResponse) {
return;
}
const processedItemCount = stateJson.lastProcessedResponse?.newItems.length ?? 0;
state._lastProcessedResponse.newItems = state._generatedItems.slice(ownership.generatedItemStartIndex, ownership.generatedItemStartIndex + processedItemCount);
state._lastTurnResponse = state._modelResponses.at(-1);
(0, blockedOutputPersistence_1.restoreCurrentResponseToolOutputGuardrailResultStart)(state, ownership.toolOutputGuardrailResultStartIndex, ownership.interruptionGeneratedItemIndexes.includes(null));
}
function restoreCurrentResponseInterruptionOwnership(stateJson, state, interruptions) {
const ownership = stateJson.currentResponseGeneratedItemOwnership;
if (!ownership) {
return interruptions;
}
return ownership.interruptionGeneratedItemIndexes.map((index, offset) => {
if (index === null) {
const interruption = interruptions[offset];
if (!interruption ||
!state._ownsNestedApproval(interruption, interruptions)) {
throwCurrentResponseOwnershipError('unproven child approval');
}
return interruption;
}
const interruption = state._generatedItems[index];
if (!(interruption instanceof items_1.RunToolApprovalItem)) {
throwCurrentResponseOwnershipError();
}
return interruption;
});
}
async function buildRunStateFromJson(initialAgent, stateJson, options = {}) {
const schemaVersion = stateJson.$schemaVersion;
const serializedApprovals = stateJson.context.approvals;
if (schemaVersionSupportsV118State(schemaVersion) &&
(stateJson.context.approvalInvocations === undefined ||
stateJson.completedToolInvocations === undefined ||
stateJson.completedToolInvocationEvidence === undefined ||
stateJson.ambiguousToolInvocationCallIds === undefined)) {
throw new errors_1.UserError(`RunState schema ${schemaVersion} requires explicit approval, completed, completion-evidence, and ambiguous invocation records.`);
}
if (schemaVersionSupportsV118State(schemaVersion)) {
validateCurrentApprovalInvocationRecords(stateJson.context);
}
const agentMap = schemaVersionSupportsAgentIdentity(schemaVersion)
? (0, runStateIdentity_1.buildAgentIdentityMap)(initialAgent).byIdentity
: buildAgentMap(initialAgent);
const generatedItems = stateJson.generatedItems.map((item) => deserializeItem(item, agentMap));
const contextOverride = options.contextOverride;
const contextStrategy = options.contextStrategy ?? 'merge';
let deferredLegacyApprovalContext;
//
// Rebuild the context
//
const context = contextOverride
? contextOverride._cloneForRunStateDeserialization()
: new runContext_1.RunContext(stateJson.context.context);
context._validateFunctionApprovalOwners(stateJson.context.functionApprovals ?? [], agentMap);
if (schemaVersionSupportsV118State(schemaVersion)) {
context._validateApprovalInvocations(stateJson.context.approvalInvocations ?? [], agentMap);
}
if (contextOverride) {
if (contextStrategy === 'merge') {
if (schemaVersionSupportsV116State(schemaVersion)) {
context._mergeApprovals(serializedApprovals);
if (schemaVersionSupportsV118State(schemaVersion)) {
context._mergeHostedMcpApprovals(stateJson.context.hostedMcpApprovals ?? {});
}
context._mergeFunctionApprovals(stateJson.context.functionApprovals ?? [], agentMap);
context._mergeLegacyFunctionApprovals(stateJson.context.legacyFunctionApprovals ?? {});
if (schemaVersionSupportsV118State(schemaVersion)) {
context._mergeApprovalInvocations(stateJson.context.approvalInvocations ?? [], agentMap);
}
}
else {
deferredLegacyApprovalContext = new runContext_1.RunContext(stateJson.context.context);
deferredLegacyApprovalContext._rebuildApprovals(serializedApprovals);
deferredLegacyApprovalContext._rebuildLegacyFunctionApprovals(serializedApprovals);
}
}
}
else {
context._rebuildApprovals(serializedApprovals);
context._rebuildHostedMcpApprovals(schemaVersionSupportsV118State(schemaVersion)
? (stateJson.context.hostedMcpApprovals ?? {})
: {});
context._rebuildFunctionApprovals(stateJson.context.functionApprovals ?? [], agentMap);
context._rebuildLegacyFunctionApprovals(schemaVersionSupportsV116State(schemaVersion)
? (stateJson.context.legacyFunctionApprovals ?? {})
: serializedApprovals);
context._rebuildApprovalInvocations(schemaVersionSupportsV118State(schemaVersion)
? (stateJson.context.approvalInvocations ?? [])
: [], agentMap);
}
const shouldRestoreSerializedContext = !contextOverride || contextStrategy === 'merge';
if (shouldRestoreSerializedContext &&
typeof stateJson.context.toolInput !== 'undefined' &&
typeof context.toolInput === 'undefined') {
context.toolInput = stateJson.context.toolInput;
}
// Restore the aggregated run usage. toJSON serializes context.usage, but a
// freshly constructed RunContext starts with an empty Usage, so without this
// a resumed run reports zero token usage.
//
// Only restore on the no-override path. A caller-supplied RunContext is
// authoritative and owns its usage accounting: its counters do not reveal
// whether its Usage is newly owned or shared with another run (for example a
// nested agent-tool resume passes a context whose usage aggregate is shared
// with the outer run), so it is left untouched.
if (!contextOverride) {
context.usage = new usage_1.Usage(stateJson.context.usage);
}
//
// Find the current agent from the initial agent
//
const currentAgent = resolveSerializedAgent(stateJson.currentAgent, agentMap);
const state = new RunState(context, '', initialAgent, stateJson.maxTurns);
state._currentAgent = currentAgent;
state._currentTurn = stateJson.currentTurn;
state._currentTurnInProgress = stateJson.currentTurnInProgress ?? false;
state._initialInputGuardrailsCompleted =
stateJson.initialInputGuardrailsCompleted ?? false;
state._conversationId = stateJson.conversationId ?? undefined;
state._previousResponseId = stateJson.previousResponseId ?? undefined;
state._reasoningItemIdPolicy = stateJson.reasoningItemIdPolicy ?? undefined;
// rebuild tool use tracker
state._toolUseTracker = new toolUseTracker_1.AgentToolUseTracker();
for (const [agentName, toolNames] of Object.entries(stateJson.toolUseTracker)) {
const agent = agentMap.get(agentName);
if (!agent) {
throw new errors_1.UserError(`Agent ${agentName} not found`);
}
state._toolUseTracker.addToolUse(agent, toolNames, {
allowEmpty: true,
});
}
state._pendingAgentToolRuns = new Map(Object.entries(stateJson.pendingAgentToolRuns ?? {}));
state._pendingAgentToolRunAliases = new Map();
state._completedToolInvocations = schemaVersionSupportsV118State(schemaVersion)
? deserializeAgentInvocationRecords(stateJson.completedToolInvocations ?? [], agentMap)
: new Map();
state._completedToolInvocationEvidence = schemaVersionSupportsV118State(schemaVersion)
? deserializeAgentInvocationEvidence(stateJson.completedToolInvocationEvidence ?? [], agentMap, generatedItems)
: new Map();
state._observedToolInvocations = new Map();
state._ambiguousToolInvocationCallIds = schemaVersionSupportsV118State(schemaVersion)
? deserializeAgentCallIdSets(stateJson.ambiguousToolInvocationCallIds ?? [], agentMap)
: new Map();
if (schemaVersionSupportsV118State(schemaVersion)) {
validateCurrentCompletedToolInvocations(state, generatedItems, schemaVersion);
}
// rebuild current agent span
if (stateJson.currentAgentSpan) {
if (!stateJson.trace) {
logger_1.default.warn('Trace is not set, skipping tracing setup');
}
const trace = (0, provider_1.getGlobalTraceProvider)().createTrace({
traceId: stateJson.trace?.id,
name: stateJson.trace?.workflow_name,
groupId: stateJson.trace?.group_id ?? undefined,
metadata: stateJson.trace?.metadata,
tracingApiKey: stateJson.trace?.tracing_api_key ?? undefined,
});
state._currentAgentSpan = deserializeSpan(trace, stateJson.currentAgentSpan);
state._trace = trace;
}
state._noActiveAgentRun = stateJson.noActiveAgentRun;
state._inputGuardrailResults =
stateJson.inputGuardrailResults;
state._outputGuardrailResults = stateJson.outputGuardrailResults.map((r) => ({
...r,
agent: resolveSerializedAgent(r.agent, agentMap),
}));
state._toolInputGuardrailResults =
stateJson.toolInputGuardrailResults;
state._toolOutputGuardrailResults =
stateJson.toolOutputGuardrailResults;
state._currentStep = stateJson.currentStep;
state._originalInput = stateJson.originalInput;
state._pendingInput = structuredClone(stateJson.pendingInput ?? []);
state._modelResponses = stateJson.modelResponses.map(deserializeModelResponse);
state._lastTurnResponse = stateJson.lastModelResponse
? deserializeModelResponse(stateJson.lastModelResponse)
: undefined;
state._generatedItems = generatedItems;
state._currentTurnPersistedItemCount =
stateJson.currentTurnPersistedItemCount ?? 0;
state._currentTurnSessionWriteCompactedItemCount =
schemaVersionSupportsV120State(schemaVersion)
? stateJson.currentTurnSessionWriteCompactedItemCount
: undefined;
const supportsOutputGuardrailSessionPersistence = schemaVersionSupportsV117State(schemaVersion);
const deferredSessionItemIndexes = supportsOutputGuardrailSessionPersistence
? (stateJson.currentTurnDeferredSessionItemIndexes ?? [])
: [];
state._currentTurnDeferredSessionItemIndexes = new Set(deferredSessionItemIndexes);
state._currentTurnBlockedSessionStartIndex = undefined;
const restoredPendingSessionBinding = stateJson.currentTurnExecutedWithSessionBinding === true &&
stateJson.pendingSessionWrite !== undefined
? stateJson.pendingSessionWrite.sessionId
: undefined;
state._currentTurnSessionHistoryTransactionSessionId =
restoredPendingSessionBinding;
state._currentTurnSessionReasoningItemIdPolicy = restoredPendingSessionBinding
? stateJson.pendingSessionWrite?.reasoningItemIdPolicy
: undefined;
state._currentTurnSessionHistoryTransactionInputItems =
schemaVersionSupportsV117State(schemaVersion)
? stateJson.currentTurnSessionInputItems
: undefined;
state._currentTurnSessionHistoryTransactionCanReplaceAcceptedOutput =
undefined;
state._sessionHistoryTransactionId = (0, _shims_1.randomUUID)();
state._pendingSessionHistoryTransaction = undefined;
state._pendingSessionWrite = stateJson.pendingSessionWrite
? structuredClone(stateJson.pendingSessionWrite)
: undefined;
state._resumedSessionWriteInProgress = false;
state._pendingLegacyCompactionSessionItems =
stateJson.pendingLegacyCompactionSessionItems;
if (state._pendingSessionWrite?.phase === 'compaction_pending' &&
state._currentStep?.type === 'next_step_final_output' &&
typeof state._currentAgent.toolUseBehavior === 'function') {
throw new errors_1.UserError('RunState pending Session compaction terminal output is invalid.');
}
if (state._pendingSessionWrite) {
assertPendingSessionWriteOwnership(state, state._pendingSessionWrite, {
allowUnhydratedTerminalToolStep: true,
});
}
state._sandbox = stateJson.sandbox
? (0, sessionState_1.sanitizeSerializedSandboxState)(stateJson.sandbox)
: undefined;
const capabilitySnapshotsByAgent = await rehydrateToolSearchRuntimeTools(state, {
agentMap,
schemaVersion,
serializedProcessedResponse: stateJson.lastProcessedResponse,
prepareCurrentAgentForLegacyApprovals: !schemaVersionSupportsV116State(schemaVersion) &&
shouldRestoreSerializedContext &&
Object.keys(stateJson.context.approvals).length > 0,
});
const currentCapabilitySnapshot = capabilitySnapshotsByAgent.get(state._currentAgent);
state._lastProcessedResponse = stateJson.lastProcessedResponse
? await deserializeProcessedResponse(agentMap, state, stateJson.lastProcessedResponse, {
executionTools: currentCapabilitySnapshot.availableTools,
executionHandoffs: currentCapabilitySnapshot.handoffs,
executionFunctionToolsByCallId: currentCapabilitySnapshot.functionToolsByCallId,
})
: undefined;
restoreCurrentResponseProcessedOwnership(state, stateJson);
restorePendingAgentToolRunAliases(state, stateJson.pendingAgentToolRunAliases ?? {});
if (stateJson.currentStep?.type === 'next_step_handoff') {
state._currentStep = {
type: 'next_step_handoff',
newAgent: resolveSerializedAgent(stateJson.currentStep.newAgent, agentMap),
};
}
else if (stateJson.currentStep?.type === 'next_step_interruption') {
const interruptions = restoreCurrentResponseInterruptionOwnership(stateJson, state, deserializeInterruptions(stateJson.currentStep.data?.interruptions, agentMap, state._currentAgent));
rebindInterruptionFunctionToolStateKeys(interruptions, state._lastProcessedResponse, state._currentAgent, state, schemaVersion);
if (!schemaVersionSupportsV118State(schemaVersion) &&
shouldRestoreSerializedContext) {
restoreLegacyHostedMcpApprovalsForPendingRequests(context, stateJson.context.approvals, interruptions, schemaVersion === '1.17');
}
state._currentStep = {
type: 'next_step_interruption',
data: {
...stateJson.currentStep.data,
interruptions,
},
};
}
const legacyAvailableToolsByAgent = new Map();
for (const agent of new Set(agentMap.values())) {
legacyAvailableToolsByAgent.set(agent, [
...agent.tools,
]);
}
for (const [agent, snapshot] of capabilitySnapshotsByAgent) {
const configuredTools = legacyAvailableToolsByAgent.get(agent) ?? [];
legacyAvailableToolsByAgent.set(agent, [
...new Set([...configuredTools, ...snapshot.availableTools]),
]);
}
const legacyFunctionApprovalKeys = migrateLegacyFunctionToolState(state, schemaVersion, shouldRestoreSerializedContext, deferredLegacyApprovalContext, legacyAvailableToolsByAgent);
const legacyApprovalContext = deferredLegacyApprovalContext ?? context;
if (!schemaVersionSupportsV116State(schemaVersion)) {
legacyApprovalContext._retainLegacyFunctionApprovals(legacyFunctionApprovalKeys);
legacyApprovalContext._removeMigratedFunctionApprovalsFromAggregate(legacyFunctionApprovalKeys, new Set([...legacyAvailableToolsByAgent.values()].flatMap((tools) => tools.flatMap((tool) => tool.type !== 'function' && typeof tool.name === 'string'
? [tool.name]
: []))));
}
if (deferredLegacyApprovalContext) {
context._mergeApprovalStatePreservingExactKeys(deferredLegacyApprovalContext, legacyFunctionApprovalKeys);
}
if (!schemaVersionSupportsV118State(schemaVersion)) {
rebuildLegacyCompletedToolInvocations(state, generatedItems);
for (const run of state._lastProcessedResponse?.functions ?? []) {
const owner = state._lastProcessedResponse?.newItems.find((item) => item instanceof items_1.RunToolCallItem && item.rawItem === run.toolCall)?.agent;
context._bindLegacyApprovalInvocation(new items_1.RunToolApprovalItem(run.toolCall, owner ?? state._currentAgent, run.tool.name, (0, toolIdentity_1.getFunctionToolStateKey)(run.tool) ??
(0, toolIdentity_1.getFunctionToolStateKeyForCall)(run.toolCall, run.tool.name)));
}
for (const interruption of state.getInterruptions()) {
context._bindLegacyApprovalInvocation(interruption);
}
}
const interruptions = state.getInterruptions();
for (const toolRun of state._lastProcessedResponse?.functions ?? []) {
const call = toolRun.toolCall;
const owner = state._lastProcessedResponse?.newItems.find((item) => item instanceof items_1.RunToolCallItem && item.rawItem === call)?.agent ?? state._currentAgent;
const isPending = interruptions.some((item) => item.agent === owner &&
item.rawItem.type === 'function_call' &&
item.rawItem.callId === call.callId) ||
(0, toolIdentity_1.getFunctionToolStateKeys)(toolRun.tool, toolRun.availableFunctionTools ?? [toolRun.tool]).some((key) => state.hasPendingAgentToolRun(key, call.callId));
if (!isPending)
continue;
const invocation = (0, toolInvocation_1.getToolInvocationFingerprint)((0, toolIdentity_1.getFunctionToolQualifiedName)(toolRun.tool) ?? toolRun.tool.name, call);
if (!(0, tool_1.hasDynamicFunctionToolApprovalPolicy)(toolRun.tool))
continue;
// Restored calls have no retained normalized input. Re-evaluate the current
// policy before honoring a decision for refreshed successful input.
let records = state._pendingFunctionToolApprovals.get(owner);
if (!records)
state._pendingFunctionToolApprovals.set(owner, (records = new Map()));
records.set(call.callId, { invocation });
}
if (contextOverride) {
const commitCandidate = contextOverride._cloneForRunStateDeserialization();
commitCandidate._mergeApprovalState(context);
contextOverride._replaceApprovalState(commitCandidate);
if (contextStrategy === 'merge' &&
contextOverride.toolInput === undefined &&
context.toolInput !== undefined) {
contextOverride.toolInput = context.toolInput;
}
state._context = contextOverride;
}
state._serializedCurrentStep = state._currentStep;
if ((0, blockedOutputPersistence_1.hasPendingApprovedToolInputCompaction)(state)) {
assertPendingSessionCompactionTerminalAuthority(state);
}
return state;
}
/**
* @internal
*/
async function rehydrateProcessedResponseTools(initialAgent, state, executionTools) {
if (!state._lastProcessedResponse) {
return;
}
const agentIdentity = (0, runStateIdentity_1.buildAgentIdentityMap)(initialAgent);
const serializedProcessedResponse = serializeProcessedResponse(state._lastProcessedResponse, agentIdentity.byAgent);
const executionFunctionToolsByCallId = new Map(state._lastProcessedResponse.functions.flatMap((functionCall) => functionCall.preserveToolOnExecutionRehydration
? [[functionCall.toolCall.callId, functionCall.tool]]
: []));
state._lastProcessedResponse = await deserializeProcessedResponse(agentIdentity.byIdentity, state, serializedProcessedResponse, {
executionTools,
executionFunctionToolsByCallId,
allowSerializedExecutionToolPlaceholder: false,
});
}
/**
* @internal
*/
function buildAgentMap(initialAgent) {
const map = new Map();
const visitedAgents = new Set();
const queue = [initialAgent];
while (queue.length > 0) {
const currentAgent = queue.shift();
if (visitedAgents.has(currentAgent)) {
continue;
}
visitedAgents.add(currentAgent);
const existingAgent = map.get(currentAgent.name);
if (existingAgent && existingAgent !== currentAgent) {
throw new errors_1.UserError(`Duplicate agent name "${currentAgent.name}" detected. Use unique agent names when serializing RunState.`);
}
map.set(currentAgent.name, currentAgent);
for (const handoff of currentAgent.handoffs) {
if (handoff instanceof agent_1.Agent) {
queue.push(handoff);
}
else if (handoff.agent) {
queue.push(handoff.agent);
}
}
for (const tool of currentAgent.tools) {
const sourceAgent = (0, agentToolSourceRegistry_1.getAgentToolSourceAgent)(tool);
if (sourceAgent) {
queue.push(sourceAgent);
}
}
}
return map;
}
function serializeAgentReference(agent, agentIdentityKeys) {
const identity = agentIdentityKeys.get(agent);
if (!identity || identity === agent.name) {
return { name: agent.name };
}
return { name: agent.name, identity };
}
function resolveSerializedAgent(serializedAgent, agentMap, fallbackAgent) {
const identity = serializedAgent.identity ?? serializedAgent.name;
const agent = agentMap.get(identity);
if (agent) {
return agent;
}
if (!serializedAgent.identity && fallbackAgent) {
return fallbackAgent;
}
if (serializedAgent.identity) {
throw new errors_1.UserError(`Agent identity ${serializedAgent.identity} not found`);
}
throw new errors_1.UserError(`Agent ${serializedAgent.name} not found`);
}
function serializeRunItem(item, agentIdentityKeys) {
const serialized = item.toJSON();
if (item instanceof items_1.RunToolCallOutputItem) {
const normalizedOutput = jsonCompatibleValueSchema.safeParse(item.output);
if (normalizedOutput.success) {
serialized.output = normalizedOutput.data;
}
}
switch (item.type) {
case 'handoff_output_item':
serialized.sourceAgent = serializeAgentReference(item.sourceAgent, agentIdentityKeys);
serialized.targetAgent = serializeAgentReference(item.targetAgent, agentIdentityKeys);
return serialized;
default:
serialized.agent = serializeAgentReference(item.agent, agentIdentityKeys);
return serialized;
}
}
function serializeCurrentStep(currentStep, agentIdentityKeys) {
if (!currentStep) {
return undefined;
}
if (currentStep.type === 'next_step_handoff') {
return {
...currentStep,
newAgent: serializeAgentReference(currentStep.newAgent, agentIdentityKeys),
};
}
if (currentStep.type === 'next_step_interruption') {
const interruptions = Array.isArray(currentStep.data?.interruptions)
? currentStep.data.interruptions.map((item) => item instanceof items_1.RunToolApprovalItem
? serializeRunItem(item, agentIdentityKeys)
: item)
: currentStep.data?.interruptions;
return {
...currentStep,
data: {
...currentStep.data,
interruptions,
},
};
}
return currentStep;
}
function serializeProcessedResponse(processedResponse, agentIdentityKeys) {
return {
...processedResponse,
newItems: processedResponse.newItems.map((item) => serializeRunItem(item, agentIdentityKeys)),
mcpApprovalRequests: processedResponse.mcpApprovalRequests.map(({ requestItem, mcpTool }) => ({
requestItem: { rawItem: requestItem.rawItem },
// Resume resolves the live tool from current agent configuration.
mcpTool: {
type: mcpTool.type,
name: mcpTool.name,
providerData: {
type: 'mcp',
server_label: mcpTool.providerData.server_label,
},
},
})),
functions: processedResponse.functions.map(({ toolCall, tool, mcpToolBinding }) => ({
toolCall,
tool,
mcpToolBinding: mcpToolBinding === undefined
? (0, toolIdentity_1.getMcpToolBinding)(tool)
: mcpToolBinding,
})),
handoffs: processedResponse.handoffs.map(({ toolCall, handoff: processedHandoff }) => ({
toolCall,
handoff: processedHandoff,
targetAgent: serializeAgentReference(processedHandoff.agent, agentIdentityKeys),
})),
};
}
/**
* @internal
*/
function deserializeSpan(trace, serializedSpan) {
const spanData = serializedSpan.span_data;
const previousSpan = serializedSpan.previous_span
? deserializeSpan(trace, serializedSpan.previous_span)
: undefined;
const span = (0, provider_1.getGlobalTraceProvider)().createSpan({
spanId: serializedSpan.id,
traceId: serializedSpan.trace_id,
parentId: serializedSpan.parent_id ?? undefined,
startedAt: serializedSpan.started_at ?? undefined,
endedAt: serializedSpan.ended_at ?? undefined,
data: spanData,
}, trace);
span.previousSpan = previousSpan;
return span;
}
/**
* @internal
*/
function deserializeModelResponse(serializedModelResponse) {
const usage = new usage_1.Usage(serializedModelResponse.usage);
return {
usage,
output: serializedModelResponse.output.map((item) => protocol.OutputModelItem.parse(item)),
responseId: serializedModelResponse.responseId,
requestId: serializedModelResponse.requestId,
providerData: serializedModelResponse.providerData,
};
}
/**
* @internal
*/
function deserializeItem(serializedItem, agentMap) {
switch (serializedItem.type) {
case 'input_item':
return new items_1.RunInputItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.agent, agentMap), serializedItem.inputId);
case 'message_output_item':
return new items_1.RunMessageOutputItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.agent, agentMap));
case 'tool_search_call_item':
return new items_1.RunToolSearchCallItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.agent, agentMap));
case 'tool_search_output_item':
return new items_1.RunToolSearchOutputItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.agent, agentMap));
case 'tool_call_item':
return new items_1.RunToolCallItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.agent, agentMap), serializedItem.functionToolStateKey);
case 'tool_call_output_item':
return new items_1.RunToolCallOutputItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.agent, agentMap), serializedItem.output, serializedItem.customData, serializedItem.executionStatus);
case 'reasoning_item':
return new items_1.RunReasoningItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.agent, agentMap));
case 'compaction_item':
return new items_1.RunCompactionItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.agent, agentMap));
case 'handoff_call_item':
return new items_1.RunHandoffCallItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.agent, agentMap));
case 'handoff_output_item':
return new items_1.RunHandoffOutputItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.sourceAgent, agentMap), resolveSerializedAgent(serializedItem.targetAgent, agentMap));
case 'tool_approval_item':
return new items_1.RunToolApprovalItem(serializedItem.rawItem, resolveSerializedAgent(serializedItem.agent, agentMap), serializedItem.toolName, serializedItem.functionToolStateKey);
}
}
function deserializeInterruptionItem(serializedItem, agentMap, currentAgent) {
if (serializedItem instanceof items_1.RunToolApprovalItem) {
return serializedItem;
}
const parsed = itemSchema.safeParse(serializedItem);
if (parsed.success) {
if (parsed.data.type === 'tool_approval_item') {
const mappedAgent = resolveSerializedAgent(parsed.data.agent, agentMap, currentAgent);
return new items_1.RunToolApprovalItem(parsed.data.rawItem, mappedAgent, parsed.data.toolName, parsed.data.functionToolStateKey);
}
const item = deserializeItem(parsed.data, agentMap);
return item instanceof items_1.RunToolApprovalItem ? item : undefined;
}
if (!serializedItem || typeof serializedItem !== 'object') {
return undefined;
}
const value = serializedItem;
if (!value.rawItem || typeof value.rawItem !== 'object') {
return undefined;
}
const rawItem = value.rawItem;
if (rawItem.type !== 'function_call' &&
rawItem.type !== 'hosted_tool_call' &&
rawItem.type !== 'computer_call' &&
rawItem.type !== 'shell_call' &&
rawItem.type !== 'apply_patch_call') {
return undefined;
}
const agentName = value.agent && typeof value.agent.name === 'string'
? value.agent.name
: undefined;
const agentIdentity = value.agent && typeof value.agent.identity === 'string'
? value.agent.identity
: undefined;
const mappedAgent = agentName || agentIdentity
? resolveSerializedAgent({
name: agentName ?? currentAgent.name,
identity: agentIdentity,
}, agentMap, currentAgent)
: currentAgent;
const toolName = typeof value.toolName === 'string'
? value.toolName
: typeof rawItem.name === 'string'
? rawItem.name
: undefined;
const functionToolStateKey = typeof value.functionToolStateKey === 'string'
? value.functionToolStateKey
: undefined;
return new items_1.RunToolApprovalItem(value.rawItem, mappedAgent, toolName, functionToolStateKey);
}
function deserializeInterruptions(serializedInterruptions, agentMap, currentAgent) {
if (!Array.isArray(serializedInterruptions)) {
return [];
}
return serializedInterruptions
.map((item) => deserializeInterruptionItem(item, agentMap, currentAgent))
.filter((item) => item instanceof items_1.RunToolApprovalItem);
}
function restoreLegacyHostedMcpApprovalsForPendingRequests(context, legacyApprovals, approvalItems, restoreStickyDecisions) {
const pendingByTool = new Map();
const stickyClaimantsByTool = new Map();
for (const [index, approvalItem] of approvalItems.entries()) {
const identity = (0, toolIdentity_1.getHostedMcpApprovalRequestIdentity)(approvalItem);
const requestKey = identity
? (0, toolIdentity_1.getHostedMcpApprovalRequestKey)(identity)
: undefined;
const rawItem = approvalItem.rawItem;
const providerData = rawItem.providerData;
const toolName = identity?.toolName ?? approvalItem.name;
const requestId = identity?.requestId ??
(typeof rawItem.callId === 'string'
? rawItem.callId
: typeof rawItem.id === 'string'
? rawItem.id
: typeof providerData?.itemId === 'string'
? providerData.itemId
: typeof providerData?.id === 'string'
? providerData.id
: undefined);
if (!toolName) {
continue;
}
const claimantKey = requestKey ?? JSON.stringify(['legacy_pending_claim', index]);
const stickyClaimants = stickyClaimantsByTool.get(toolName) ?? new Map();
stickyClaimants.set(claimantKey, requestKey ? approvalItem : undefined);
stickyClaimantsByTool.set(toolName, stickyClaimants);
if (!requestId) {
continue;
}
const pendingByRequestId = pendingByTool.get(toolName) ?? new Map();
const claimants = pendingByRequestId.get(requestId) ?? new Map();
claimants.set(claimantKey, requestKey ? approvalItem : undefined);
pendingByRequestId.set(requestId, claimants);
pendingByTool.set(toolName, pendingByRequestId);
}
for (const [toolName, pendingByRequestId] of pendingByTool) {
const legacy = legacyApprovals[toolName];
if (!legacy) {
continue;
}
const hasStickyDecision = restoreStickyDecisions &&
(legacy.approved === true || legacy.rejected === true);
if (hasStickyDecision) {
const claimants = stickyClaimantsByTool.get(toolName) ?? new Map();
if (claimants.size === 1) {
const approvalItem = claimants.values().next().value;
const requestId = approvalItem
? (0, toolIdentity_1.getHostedMcpApprovalRequestIdentity)(approvalItem)?.requestId
: undefined;
if (approvalItem && requestId) {
restoreLegacyHostedMcpApprovalDecision(context, approvalItem, legacy, requestId, true);
}
}
continue;
}
for (const [requestId, claimants] of pendingByRequestId) {
if (claimants.size !== 1) {
continue;
}
const approvalItem = claimants.values().next().value;
if (approvalItem) {
restoreLegacyHostedMcpApprovalDecision(context, approvalItem, legacy, requestId, false);
}
}
}
}
function restoreLegacyHostedMcpApprovalDecision(context, approvalItem, legacy, requestId, restoreStickyDecisions) {
const decision = resolveLegacyHostedMcpApproval(legacy, requestId, restoreStickyDecisions);
if (decision === true) {
context.approveTool(approvalItem);
}
else if (decision === false) {
context.rejectTool(approvalItem, {
message: legacy.messages?.[requestId] ??
(restoreStickyDecisions && legacy.rejected === true
? legacy.stickyRejectMessage
: undefined),
});
}
}
function resolveLegacyHostedMcpApproval(legacy, requestId, restoreStickyDecisions) {
const stickyApproval = restoreStickyDecisions && legacy.approved === true;
const stickyRejection = restoreStickyDecisions && legacy.rejected === true;
if (stickyApproval || stickyRejection) {
return stickyApproval;
}
const exactApproval = Array.isArray(legacy.approved) && legacy.approved.includes(requestId);
const exactRejection = Array.isArray(legacy.rejected) && legacy.rejected.includes(requestId);
if (exactApproval || exactRejection) {
return exactApproval;
}
return undefined;
}
function rebindInterruptionFunctionToolStateKeys(interruptions, processedResponse, processedAgent, state, schemaVersion) {
if (!processedResponse) {
for (const interruption of interruptions) {
if (interruption.rawItem.type !== 'function_call' ||
!interruption.functionToolStateKey) {
continue;
}
const rawStateKey = (0, toolIdentity_1.getFunctionToolStateKeyForCall)(interruption.rawItem);
if (rawStateKey && interruption.functionToolStateKey !== rawStateKey) {
throwAmbiguousFunctionCallId(interruption.agent, interruption.rawItem.callId, [rawStateKey, interruption.functionToolStateKey]);
}
}
return;
}
const functionsByCallId = new Map(processedResponse.functions.map((functionCall) => [
functionCall.toolCall.callId,
functionCall,
]));
for (const interruption of interruptions) {
if (interruption.rawItem.type !== 'function_call') {
continue;
}
let stateKey;
if (interruption.agent === processedAgent) {
stateKey = (0, toolIdentity_1.getFunctionToolStateKey)(functionsByCallId.get(interruption.rawItem.callId)?.tool);
}
else {
stateKey = findNestedInterruptionFunctionToolStateKey(state, interruption);
if (schemaVersionSupportsV116State(schemaVersion)) {
const rawStateKey = (0, toolIdentity_1.getFunctionToolStateKeyForCall)(interruption.rawItem);
const serializedStateKey = interruption.functionToolStateKey;
const expectedStateKey = stateKey ?? rawStateKey;
if (serializedStateKey &&
expectedStateKey &&
serializedStateKey !== expectedStateKey) {
throwAmbiguousFunctionCallId(interruption.agent, interruption.rawItem.callId, [expectedStateKey, serializedStateKey]);
}
}
}
if (stateKey) {
interruption.functionToolStateKey = stateKey;
}
}
}
function findNestedInterruptionFunctionToolStateKey(state, interruption) {
if (interruption.rawItem.type !== 'function_call') {
return undefined;
}
const serializedPendingStates = new Set();
for (const functionCall of state._lastProcessedResponse?.functions ?? []) {
if ((0, agentToolSourceRegistry_1.getAgentToolSourceAgent)(functionCall.tool) !== interruption.agent) {
continue;
}
const stateKeys = (0, toolIdentity_1.getFunctionToolStateKeys)(functionCall.tool, functionCall.availableFunctionTools ?? [functionCall.tool]);
for (const stateKey of stateKeys) {
const serializedState = state.getPendingAgentToolRun(stateKey, functionCall.toolCall.callId);
if (serializedState) {
serializedPendingStates.add(serializedState);
}
}
}
const resolvedStateKeys = new Set();
for (const serializedState of serializedPendingStates) {
const resolvedStateKey = getSerializedNestedInterruptionFunctionToolStateKey(serializedState, interruption.rawItem);
if (resolvedStateKey) {
resolvedStateKeys.add(resolvedStateKey);
}
}
if (resolvedStateKeys.size > 1) {
throwAmbiguousFunctionCallId(interruption.agent, interruption.rawItem.callId, [...resolvedStateKeys]);
}
return resolvedStateKeys.values().next().value;
}
function getSerializedNestedInterruptionFunctionToolStateKey(serializedState, interruptionRawItem) {
let nestedState;
try {
nestedState = JSON.parse(serializedState);
}
catch {
return undefined;
}
if (!nestedState || typeof nestedState !== 'object') {
return undefined;
}
const candidate = nestedState;
if (candidate.currentStep?.type !== 'next_step_interruption' ||
!Array.isArray(candidate.currentStep.data?.interruptions) ||
!candidate.currentStep.data.interruptions.some((value) => {
if (!value || typeof value !== 'object') {
return false;
}
const rawItem = value.rawItem;
return (rawItem != null &&
typeof rawItem === 'object' &&
rawItem.type === 'function_call' &&
rawItem.callId ===
interruptionRawItem.callId &&
(0, toolIdentity_1.getFunctionToolStateKeyForCall)(rawItem) ===
(0, toolIdentity_1.getFunctionToolStateKeyForCall)(interruptionRawItem));
}) ||
!Array.isArray(candidate.lastProcessedResponse?.functions)) {
return undefined;
}
const stateKeys = new Set();
for (const value of candidate.lastProcessedResponse.functions) {
if (!value || typeof value !== 'object') {
continue;
}
const functionCall = value;
if (!functionCall.toolCall ||
typeof functionCall.toolCall !== 'object' ||
functionCall.toolCall.callId !==
interruptionRawItem.callId) {
continue;
}
const stateKey = (0, toolIdentity_1.getFunctionToolStateKey)(functionCall.tool);
if (stateKey &&
(0, toolIdentity_1.getFunctionToolStateKeyForResolvedCall)(interruptionRawItem, functionCall.tool, stateKey) === stateKey) {
stateKeys.add(stateKey);
}
}
if (stateKeys.size !== 1) {
return undefined;
}
return stateKeys.values().next().value;
}
function restorePendingAgentToolRunAliases(state, serializedAliases) {
const aliasEntries = Object.entries(serializedAliases);
if (aliasEntries.length === 0) {
return;
}
const validAliases = new Map();
for (const functionCall of state._lastProcessedResponse?.functions ?? []) {
const [canonicalToolName, ...aliases] = (0, toolIdentity_1.getFunctionToolStateKeys)(functionCall.tool, functionCall.availableFunctionTools ?? [functionCall.tool]);
if (!canonicalToolName) {
continue;
}
const callId = functionCall.toolCall.callId;
const canonicalKey = `${canonicalToolName}:${callId}`;
for (const alias of aliases) {
validAliases.set(`${alias}:${callId}`, canonicalKey);
}
}
for (const [aliasKey, canonicalKey] of aliasEntries) {
if (validAliases.get(aliasKey) !== canonicalKey ||
!state._pendingAgentToolRuns.has(canonicalKey)) {
throw new errors_1.UserError('Run state pending agent tool aliases do not match the reconstructed pending function calls.');
}
}
state._pendingAgentToolRunAliases = new Map(aliasEntries);
}
function migrateLegacyFunctionToolState(state, schemaVersion, migrateApprovals, approvalContext = state._context, availableToolsByAgent = new Map()) {
if (schemaVersionSupportsV116State(schemaVersion)) {
return new Set();
}
const approvalCallsByLegacyKey = new Map();
const approvalOwnersByLegacyKey = new Map();
const addApprovalOwner = (legacyKey, agent, canonicalKey) => {
const ownersByAgent = approvalOwnersByLegacyKey.get(legacyKey) ??
new Map();
const ownerKeys = ownersByAgent.get(agent) ?? new Set();
ownerKeys.add(canonicalKey);
ownersByAgent.set(agent, ownerKeys);
approvalOwnersByLegacyKey.set(legacyKey, ownersByAgent);
};
const addApprovalCall = (legacyKey, agent, canonicalKey, callId) => {
addApprovalOwner(legacyKey, agent, canonicalKey);
const callsByAgent = approvalCallsByLegacyKey.get(legacyKey) ??
new Map();
const callsByCanonicalKey = callsByAgent.get(agent) ?? new Map();
const callIds = callsByCanonicalKey.get(canonicalKey) ?? new Set();
callIds.add(callId);
callsByCanonicalKey.set(canonicalKey, callIds);
callsByAgent.set(agent, callsByCanonicalKey);
approvalCallsByLegacyKey.set(legacyKey, callsByAgent);
};
for (const [agent, availableTools] of availableToolsByAgent) {
for (const tool of availableTools) {
if (tool.type !== 'function') {
continue;
}
const canonicalKey = (0, toolIdentity_1.getFunctionToolStateKey)(tool);
const legacyKey = (0, toolIdentity_1.getFunctionToolQualifiedName)(tool) ?? tool.name;
if (!canonicalKey || canonicalKey === legacyKey) {
continue;
}
addApprovalOwner(legacyKey, agent, canonicalKey);
}
}
for (const functionCall of state._lastProcessedResponse?.functions ?? []) {
const canonicalKey = (0, toolIdentity_1.getFunctionToolStateKey)(functionCall.tool);
const legacyKey = (0, toolIdentity_1.getFunctionToolQualifiedName)(functionCall.tool) ?? functionCall.tool.name;
if (!canonicalKey || canonicalKey === legacyKey) {
continue;
}
const callId = functionCall.toolCall.callId;
const pendingState = state.getPendingAgentToolRun(legacyKey, callId);
if (pendingState !== undefined) {
state.setPendingAgentToolRun(canonicalKey, callId, pendingState, [
legacyKey,
]);
}
if (!migrateApprovals) {
continue;
}
addApprovalCall(legacyKey, state._currentAgent, canonicalKey, callId);
}
if (state._currentStep?.type === 'next_step_interruption') {
for (const interruption of state._currentStep.data.interruptions) {
if (interruption.rawItem.type !== 'function_call' ||
!interruption.functionToolStateKey) {
continue;
}
const legacyKey = (0, toolIdentity_1.getFunctionToolLegacyStateKeyFromStateKey)(interruption.functionToolStateKey);
if (!legacyKey) {
continue;
}
addApprovalCall(legacyKey, interruption.agent, interruption.functionToolStateKey, interruption.rawItem.callId);
}
}
if (migrateApprovals) {
for (const [legacyKey, ownersByAgent] of approvalOwnersByLegacyKey) {
const ownerEntries = [...ownersByAgent].flatMap(([agent, canonicalKeys]) => [...canonicalKeys].map((canonicalKey) => ({ agent, canonicalKey })));
if (ownerEntries.length !== 1) {
continue;
}
const [{ agent, canonicalKey }] = ownerEntries;
approvalContext._migrateToolApproval(agent, legacyKey, canonicalKey, [], true);
}
}
for (const [legacyKey, callsByAgent] of approvalCallsByLegacyKey) {
const callOwners = new Map();
for (const callsByCanonicalKey of callsByAgent.values()) {
for (const callIds of callsByCanonicalKey.values()) {
for (const callId of callIds) {
callOwners.set(callId, (callOwners.get(callId) ?? 0) + 1);
}
}
}
for (const [agent, callsByCanonicalKey] of callsByAgent) {
for (const [canonicalKey, callIds] of callsByCanonicalKey) {
for (const callId of callIds) {
const remainingOwners = (callOwners.get(callId) ?? 1) - 1;
callOwners.set(callId, remainingOwners);
approvalContext._migrateToolApproval(agent, legacyKey, canonicalKey, [callId], false, remainingOwners > 0);
}
}
}
}
return new Set(approvalOwnersByLegacyKey.keys());
}
/**
* @internal
*/
function unresolvedFunctionTool(call, serialized) {
const name = serialized.name ?? call.name;
// This placeholder preserves history/rejection identity and can never execute.
return {
type: 'function',
name,
description: '',
parameters: {
type: 'object',
properties: {},
required: [],
additionalProperties: false,
},
strict: true,
deferLoading: serialized.deferLoading,
...(call.namespace && call.namespace !== name
? { [toolIdentity_1.FUNCTION_TOOL_NAMESPACE]: call.namespace }
: {}),
needsApproval: async () => true,
isEnabled: async () => false,
invoke: async () => {
throw new errors_1.UserError('Original function tool is unavailable; restore its configuration or start a new run.');
},
};
}
async function deserializeProcessedResponse(agentMap, state, serializedProcessedResponse, options = {}) {
const currentAgent = state._currentAgent;
const allTools = options.executionTools
? options.executionTools
: [
...(await currentAgent.getAllTools(state._context)),
...(await getEnabledToolSearchRuntimeTools(state, currentAgent)),
];
const baseAgentTools = currentAgent.tools;
const allowSerializedExecutionToolPlaceholder = options.allowSerializedExecutionToolPlaceholder ?? true;
const tools = (0, toolIdentity_1.buildFunctionToolLookupMap)(allTools.filter((tool) => tool.type === 'function'));
const computerTools = new Map(allTools
.filter((tool) => tool.type === 'computer')
.map((tool) => [tool.name, tool]));
const resolveComputerTool = (toolName) => {
const exactMatch = computerTools.get(toolName);
if (exactMatch) {
return exactMatch;
}
if (toolName === 'computer') {
return computerTools.get('computer_use_preview');
}
if (toolName === 'computer_use_preview') {
return computerTools.get('computer');
}
return undefined;
};
const shellTools = new Map(allTools
.filter((tool) => tool.type === 'shell')
.map((tool) => [tool.name, tool]));
const applyPatchTools = new Map(allTools
.filter((tool) => tool.type === 'apply_patch')
.map((tool) => [tool.name, tool]));
const mcpTools = new Map(allTools
.filter((tool) => tool.type === 'hosted_tool' &&
tool.name === 'hosted_mcp' &&
tool.providerData?.type === 'mcp')
.map((tool) => [tool.providerData.server_label, tool]));
const enabledHandoffs = options.executionHandoffs
? options.executionHandoffs
: await currentAgent.getEnabledHandoffs(state._context);
const handoffs = new Map(enabledHandoffs.map((entry) => [entry.toolName, entry]));
const result = {
newItems: serializedProcessedResponse.newItems.map((item) => deserializeItem(item, agentMap)),
toolsUsed: serializedProcessedResponse.toolsUsed,
handoffs: serializedProcessedResponse.handoffs.map((serializedHandoff) => {
const toolName = serializedHandoff.handoff.toolName;
const resolvedHandoff = handoffs.get(toolName);
const serializedTargetAgent = serializedHandoff.targetAgent
? resolveSerializedAgent(serializedHandoff.targetAgent, agentMap)
: undefined;
const targetMatches = serializedTargetAgent
? resolvedHandoff?.agent === serializedTargetAgent
: resolvedHandoff?.agentName === serializedHandoff.handoff.agentName;
if (!resolvedHandoff || !targetMatches) {
throw new errors_1.UserError(`Handoff ${toolName} not found`);
}
(0, toolCaller_1.ensureToolCallerAllowed)(serializedHandoff.toolCall, undefined, resolvedHandoff.toolName, currentAgent);
return {
toolCall: serializedHandoff.toolCall,
handoff: resolvedHandoff,
};
}),
functions: await Promise.all(serializedProcessedResponse.functions.map(async (functionCall) => {
const toolIdentity = (0, toolIdentity_1.getToolCallDisplayName)(functionCall.toolCall) ??
functionCall.tool.name;
const exactRuntimeTool = options.executionFunctionToolsByCallId?.get(functionCall.toolCall.callId);
if (exactRuntimeTool &&
!(0, toolIdentity_1.getFunctionToolStateKeyForResolvedCall)(functionCall.toolCall, exactRuntimeTool)) {
throwAmbiguousFunctionCallId(currentAgent, functionCall.toolCall.callId, [
(0, toolIdentity_1.getFunctionToolStateKey)(exactRuntimeTool) ??
exactRuntimeTool.name,
(0, toolIdentity_1.getFunctionToolStateKeyForCall)(functionCall.toolCall) ?? functionCall.tool.name,
]);
}
// Missing provenance must survive every rewrite, including sandbox rebinding.
const mcpToolBinding = functionCall.mcpToolBinding === undefined
? 'unknown'
: functionCall.mcpToolBinding;
const resolvedTool = exactRuntimeTool ??
(0, toolIdentity_1.resolveFunctionToolCall)(functionCall.toolCall, tools) ??
(0, toolRehydration_1.getSerializedFunctionToolPlaceholder)({
agent: currentAgent,
baseAgentTools,
serializedTool: functionCall.tool,
toolCall: functionCall.toolCall,
toolIdentity,
allowSerializedExecutionToolPlaceholder,
}) ??
(mcpToolBinding !== null ||
state._completedToolInvocations
.get(currentAgent)
?.has(functionCall.toolCall.callId)
? unresolvedFunctionTool(functionCall.toolCall, functionCall.tool)
: undefined);
if (!resolvedTool) {
throw new errors_1.UserError(`Tool ${toolIdentity} not found`);
}
(0, toolCaller_1.ensureToolCallerAllowed)(functionCall.toolCall, resolvedTool.allowedCallers, (0, toolIdentity_1.getFunctionToolQualifiedName)(resolvedTool) ?? resolvedTool.name, currentAgent);
return (0, types_1.createToolRunFunction)({
toolCall: functionCall.toolCall,
tool: resolvedTool,
availableFunctionTools: [
...new Set([...tools.values(), resolvedTool]),
],
preserveToolOnExecutionRehydration: Boolean(exactRuntimeTool),
mcpToolBinding,
});
})),
functionToolsNotFound: serializedProcessedResponse.functionToolsNotFound ?? [],
computerActions: serializedProcessedResponse.computerActions.map((computerAction) => {
const toolName = computerAction.computer.name;
const computerTool = resolveComputerTool(toolName) ??
(0, toolRehydration_1.getSerializedComputerToolPlaceholder)({
agent: currentAgent,
baseAgentTools,
serializedTool: computerAction.computer,
toolName,
allowSerializedExecutionToolPlaceholder,
});
if (!computerTool) {
throw new errors_1.UserError(`Computer tool ${toolName} not found`);
}
return {
toolCall: computerAction.toolCall,
computer: computerTool,
};
}),
shellActions: (serializedProcessedResponse.shellActions ?? []).map((shellAction) => {
const toolName = shellAction.shell.name;
const shellTool = shellTools.get(toolName) ??
(0, toolRehydration_1.getSerializedShellToolPlaceholder)({
agent: currentAgent,
baseAgentTools,
serializedTool: shellAction.shell,
toolName,
allowSerializedExecutionToolPlaceholder,
});
if (!shellTool) {
throw new errors_1.UserError(`Shell tool ${toolName} not found`);
}
(0, toolCaller_1.ensureToolCallerAllowed)(shellAction.toolCall, shellTool.allowedCallers, shellTool.name, currentAgent);
return {
toolCall: shellAction.toolCall,
shell: shellTool,
};
}),
applyPatchActions: (serializedProcessedResponse.applyPatchActions ?? []).map((applyPatchAction) => {
const toolName = applyPatchAction.applyPatch.name;
const applyPatchTool = applyPatchTools.get(toolName) ??
(0, toolRehydration_1.getSerializedApplyPatchToolPlaceholder)({
agent: currentAgent,
baseAgentTools,
serializedTool: applyPatchAction.applyPatch,
toolName,
allowSerializedExecutionToolPlaceholder,
});
if (!applyPatchTool) {
throw new errors_1.UserError(`Apply patch tool ${toolName} not found`);
}
(0, toolCaller_1.ensureToolCallerAllowed)(applyPatchAction.toolCall, applyPatchTool.allowedCallers, applyPatchTool.name, currentAgent);
return {
toolCall: applyPatchAction.toolCall,
applyPatch: applyPatchTool,
};
}),
mcpApprovalRequests: (serializedProcessedResponse.mcpApprovalRequests ?? []).map((approvalRequest) => {
const rawItem = approvalRequest.requestItem
.rawItem;
const rawServerLabel = rawItem.providerData?.server_label;
const serializedServerLabel = approvalRequest.mcpTool.providerData.server_label;
const serverLabel = typeof rawServerLabel === 'string'
? rawServerLabel
: typeof serializedServerLabel === 'string'
? serializedServerLabel
: undefined;
if (!serverLabel) {
throw new errors_1.UserError('MCP approval request is missing a server label');
}
const mcpTool = mcpTools.get(serverLabel);
if (!mcpTool) {
throw new errors_1.UserError(`MCP tool ${serverLabel} not found`);
}
(0, toolCaller_1.ensureToolCallerAllowed)(rawItem, mcpTool.providerData.allowed_callers, serverLabel, currentAgent);
return {
requestItem: new items_1.RunToolApprovalItem(rawItem, currentAgent),
mcpTool,
};
}),
};
return {
...result,
hasToolsOrApprovalsToRun() {
return (result.handoffs.length > 0 ||
result.functions.length > 0 ||
result.functionToolsNotFound.length > 0 ||
result.mcpApprovalRequests.length > 0 ||
result.computerActions.length > 0 ||
result.shellActions.length > 0 ||
result.applyPatchActions.length > 0);
},
};
}
async function getEnabledToolSearchRuntimeTools(state, agent) {
return (0, toolSearch_2.filterEnabledToolSearchRuntimeTools)({
runtimeTools: state.getToolSearchRuntimeTools(agent),
runContext: state._context,
agent,
});
}
//# sourceMappingURL=runState.js.map