@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
984 lines • 39.2 kB
JavaScript
import { z } from 'zod';
import { Agent } from "./agent.mjs";
import { getAgentToolSourceAgent } from "./agentToolSourceRegistry.mjs";
import { RunMessageOutputItem, RunToolApprovalItem, RunToolCallItem, RunToolCallOutputItem, RunReasoningItem, RunHandoffCallItem, RunHandoffOutputItem, } from "./items.mjs";
import { RunContext } from "./runContext.mjs";
import { getTurnInput } from "./runner/items.mjs";
import { AgentToolUseTracker } from "./runner/toolUseTracker.mjs";
import { nextStepSchema } from "./runner/steps.mjs";
import { SystemError, UserError } from "./errors.mjs";
import { getGlobalTraceProvider } from "./tracing/provider.mjs";
import { Usage } from "./usage.mjs";
import { getCurrentTrace } from "./tracing/index.mjs";
import logger from "./logger.mjs";
import { handoff } from "./handoff.mjs";
import * as protocol from "./types/protocol.mjs";
import { safeExecute } from "./utils/safeExecute.mjs";
/**
* 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.
*/
export const CURRENT_SCHEMA_VERSION = '1.5';
const SUPPORTED_SCHEMA_VERSIONS = [
'1.0',
'1.1',
'1.2',
'1.3',
'1.4',
CURRENT_SCHEMA_VERSION,
];
const $schemaVersion = z.enum(SUPPORTED_SCHEMA_VERSIONS);
const serializedAgentSchema = z.object({
name: z.string(),
});
const serializedSpanBase = z.object({
object: z.literal('trace.span'),
id: z.string(),
trace_id: z.string(),
parent_id: z.string().nullable(),
started_at: z.string().nullable(),
ended_at: z.string().nullable(),
error: z
.object({
message: z.string(),
data: z.record(z.string(), z.any()).optional(),
})
.nullable(),
span_data: z.record(z.string(), z.any()),
});
const SerializedSpan = serializedSpanBase.extend({
previous_span: z.lazy(() => SerializedSpan).optional(),
});
const requestUsageSchema = z.object({
inputTokens: z.number(),
outputTokens: z.number(),
totalTokens: z.number(),
inputTokensDetails: z.record(z.string(), z.number()).optional(),
outputTokensDetails: z.record(z.string(), z.number()).optional(),
endpoint: z.string().optional(),
});
const usageSchema = z.object({
requests: z.number(),
inputTokens: z.number(),
outputTokens: z.number(),
totalTokens: z.number(),
inputTokensDetails: z.array(z.record(z.string(), z.number())).optional(),
outputTokensDetails: z.array(z.record(z.string(), z.number())).optional(),
requestUsageEntries: z.array(requestUsageSchema).optional(),
});
const modelResponseSchema = z.object({
usage: usageSchema,
output: z.array(protocol.OutputModelItem),
responseId: z.string().optional(),
providerData: z.record(z.string(), z.any()).optional(),
});
const itemSchema = z.discriminatedUnion('type', [
z.object({
type: z.literal('message_output_item'),
rawItem: protocol.AssistantMessageItem,
agent: serializedAgentSchema,
}),
z.object({
type: z.literal('tool_call_item'),
rawItem: protocol.ToolCallItem.or(protocol.HostedToolCallItem),
agent: serializedAgentSchema,
}),
z.object({
type: z.literal('tool_call_output_item'),
rawItem: protocol.FunctionCallResultItem.or(protocol.ComputerCallResultItem)
.or(protocol.ShellCallResultItem)
.or(protocol.ApplyPatchCallResultItem),
agent: serializedAgentSchema,
output: z.string(),
}),
z.object({
type: z.literal('reasoning_item'),
rawItem: protocol.ReasoningItem,
agent: serializedAgentSchema,
}),
z.object({
type: z.literal('handoff_call_item'),
rawItem: protocol.FunctionCallItem,
agent: serializedAgentSchema,
}),
z.object({
type: z.literal('handoff_output_item'),
rawItem: protocol.FunctionCallResultItem,
sourceAgent: serializedAgentSchema,
targetAgent: serializedAgentSchema,
}),
z.object({
type: z.literal('tool_approval_item'),
rawItem: protocol.FunctionCallItem.or(protocol.HostedToolCallItem)
.or(protocol.ComputerUseCallItem)
.or(protocol.ShellCallItem)
.or(protocol.ApplyPatchCallItem),
agent: serializedAgentSchema,
toolName: z.string().optional(),
}),
]);
const serializedTraceSchema = z.object({
object: z.literal('trace'),
id: z.string(),
workflow_name: z.string(),
group_id: z.string().nullable(),
metadata: z.record(z.string(), 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: z.string().optional().nullable(),
});
const serializedProcessedResponseSchema = z.object({
newItems: z.array(itemSchema),
toolsUsed: z.array(z.string()),
handoffs: z.array(z.object({
toolCall: z.any(),
handoff: z.any(),
})),
functions: z.array(z.object({
toolCall: z.any(),
tool: z.any(),
})),
computerActions: z.array(z.object({
toolCall: z.any(),
computer: z.any(),
})),
shellActions: z
.array(z.object({
toolCall: z.any(),
shell: z.any(),
}))
.optional(),
applyPatchActions: z
.array(z.object({
toolCall: z.any(),
applyPatch: z.any(),
}))
.optional(),
mcpApprovalRequests: z
.array(z.object({
requestItem: z.object({
// protocol.HostedToolCallItem
rawItem: z.object({
type: z.literal('hosted_tool_call'),
name: z.string(),
arguments: z.string().optional(),
status: z.string().optional(),
output: z.string().optional(),
// this always exists but marked as optional for early version compatibility; when releasing 1.0, we can remove the nullable and optional
providerData: z.record(z.string(), z.any()).nullable().optional(),
}),
}),
// HostedMCPTool
mcpTool: z.object({
type: z.literal('hosted_tool'),
name: z.literal('hosted_mcp'),
providerData: z.record(z.string(), z.any()),
}),
}))
.optional(),
});
const guardrailFunctionOutputSchema = z.object({
tripwireTriggered: z.boolean(),
outputInfo: z.any(),
});
const toolGuardrailBehaviorSchema = z.discriminatedUnion('type', [
z.object({ type: z.literal('allow') }),
z.object({
type: z.literal('rejectContent'),
message: z.string(),
}),
z.object({ type: z.literal('throwException') }),
]);
const toolGuardrailFunctionOutputSchema = z.object({
outputInfo: z.any().optional(),
behavior: toolGuardrailBehaviorSchema,
});
const toolGuardrailMetadataSchema = z.object({
type: z.union([z.literal('tool_input'), z.literal('tool_output')]),
name: z.string(),
});
const inputGuardrailResultSchema = z.object({
guardrail: z.object({
type: z.literal('input'),
name: z.string(),
}),
output: guardrailFunctionOutputSchema,
});
const outputGuardrailResultSchema = z.object({
guardrail: z.object({
type: z.literal('output'),
name: z.string(),
}),
agentOutput: z.any(),
agent: serializedAgentSchema,
output: guardrailFunctionOutputSchema,
});
const toolInputGuardrailResultSchema = z.object({
guardrail: toolGuardrailMetadataSchema.extend({
type: z.literal('tool_input'),
}),
output: toolGuardrailFunctionOutputSchema,
});
const toolOutputGuardrailResultSchema = z.object({
guardrail: toolGuardrailMetadataSchema.extend({
type: z.literal('tool_output'),
}),
output: toolGuardrailFunctionOutputSchema,
});
export const SerializedRunState = z.object({
$schemaVersion,
currentTurn: z.number(),
currentAgent: serializedAgentSchema,
originalInput: z.string().or(z.array(protocol.ModelItem)),
modelResponses: z.array(modelResponseSchema),
context: z.object({
usage: usageSchema,
approvals: z.record(z.string(), z.object({
approved: z.array(z.string()).or(z.boolean()),
rejected: z.array(z.string()).or(z.boolean()),
})),
context: z.record(z.string(), z.any()),
toolInput: z.any().optional(),
}),
toolUseTracker: z.record(z.string(), z.array(z.string())),
maxTurns: z.number(),
currentAgentSpan: SerializedSpan.nullable().optional(),
noActiveAgentRun: z.boolean(),
inputGuardrailResults: z.array(inputGuardrailResultSchema),
outputGuardrailResults: z.array(outputGuardrailResultSchema),
toolInputGuardrailResults: z
.array(toolInputGuardrailResultSchema)
.optional()
.default([]),
toolOutputGuardrailResults: z
.array(toolOutputGuardrailResultSchema)
.optional()
.default([]),
currentTurnInProgress: z.boolean().optional(),
currentStep: nextStepSchema.optional(),
lastModelResponse: modelResponseSchema.optional(),
generatedItems: z.array(itemSchema),
pendingAgentToolRuns: z.record(z.string(), z.string()).optional().default({}),
lastProcessedResponse: serializedProcessedResponseSchema.optional(),
currentTurnPersistedItemCount: z.number().int().min(0).optional(),
conversationId: z.string().optional(),
previousResponseId: z.string().optional(),
reasoningItemIdPolicy: z.enum(['preserve', 'omit']).optional(),
trace: serializedTraceSchema.nullable(),
});
/**
* 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.
*/
export 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;
/**
* The agent currently handling the conversation.
*/
_currentAgent;
/**
* Original user input prior to any processing.
*/
_originalInput;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
constructor(context, originalInput, startingAgent, maxTurns) {
this._context = context;
this._originalInput = structuredClone(originalInput);
this._modelResponses = [];
this._currentAgentSpan = undefined;
this._currentAgent = startingAgent;
this._reasoningItemIdPolicy = undefined;
this._toolUseTracker = new AgentToolUseTracker();
this._pendingAgentToolRuns = new Map();
this._generatedItems = [];
this._currentTurnPersistedItemCount = 0;
this._maxTurns = maxTurns;
this._inputGuardrailResults = [];
this._outputGuardrailResults = [];
this._toolInputGuardrailResults = [];
this._toolOutputGuardrailResults = [];
this._trace = 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;
}
/**
* 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;
}
/**
* 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);
}
/**
* 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 getTurnInput(this._originalInput, this._generatedItems, this._reasoningItemIdPolicy);
}
/**
* 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
: [];
}
getPendingAgentToolRunKey(toolName, callId) {
return `${toolName}:${callId}`;
}
getPendingAgentToolRun(toolName, callId) {
return this._pendingAgentToolRuns.get(this.getPendingAgentToolRunKey(toolName, callId));
}
hasPendingAgentToolRun(toolName, callId) {
return this._pendingAgentToolRuns.has(this.getPendingAgentToolRunKey(toolName, callId));
}
setPendingAgentToolRun(toolName, callId, serializedState) {
this._pendingAgentToolRuns.set(this.getPendingAgentToolRunKey(toolName, callId), serializedState);
}
clearPendingAgentToolRun(toolName, callId) {
this._pendingAgentToolRuns.delete(this.getPendingAgentToolRunKey(toolName, callId));
}
/**
* 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.
*/
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 allow the tool to be used multiple
* times throughout the run, set the `alwaysReject` option to `true`.
*
* @param approvalItem - The tool call approval item to reject.
* @param options - Options for the rejection.
*/
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 = {}) {
const includeTracingApiKey = options.includeTracingApiKey === true;
const output = {
$schemaVersion: CURRENT_SCHEMA_VERSION,
currentTurn: this._currentTurn,
currentAgent: {
name: this._currentAgent.name,
},
originalInput: this._originalInput,
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,
providerData: response.providerData,
};
}),
context: this._context.toJSON(),
toolUseTracker: this._toolUseTracker.toJSON(),
maxTurns: this._maxTurns,
currentAgentSpan: this._currentAgentSpan?.toJSON(),
noActiveAgentRun: this._noActiveAgentRun,
currentTurnInProgress: this._currentTurnInProgress,
inputGuardrailResults: this._inputGuardrailResults,
outputGuardrailResults: this._outputGuardrailResults.map((r) => ({
...r,
agent: r.agent.toJSON(),
})),
toolInputGuardrailResults: this._toolInputGuardrailResults,
toolOutputGuardrailResults: this._toolOutputGuardrailResults,
currentStep: this._currentStep,
lastModelResponse: this._lastTurnResponse,
generatedItems: this._generatedItems.map((item) => item.toJSON()),
pendingAgentToolRuns: Object.fromEntries(this._pendingAgentToolRuns.entries()),
currentTurnPersistedItemCount: this._currentTurnPersistedItemCount,
lastProcessedResponse: this._lastProcessedResponse,
conversationId: this._conversationId,
previousResponseId: this._previousResponseId,
reasoningItemIdPolicy: this._reasoningItemIdPolicy,
trace: this._trace
? this._trace.toJSON({ includeTracingApiKey })
: null,
};
// parsing the schema to ensure the output is valid for reparsing
const parsed = SerializedRunState.safeParse(output);
if (!parsed.success) {
throw new SystemError(`Failed to serialize run state. ${parsed.error.message}`);
}
return parsed.data;
}
/**
* 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.
*/
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,
});
}
}
async function buildRunStateFromString(initialAgent, str, options = {}) {
const [parsingError, jsonResult] = await safeExecute(() => JSON.parse(str));
if (parsingError) {
throw new UserError(`Failed to parse run state. ${parsingError instanceof Error ? parsingError.message : String(parsingError)}`);
}
const currentSchemaVersion = jsonResult.$schemaVersion;
if (!currentSchemaVersion) {
throw new UserError('Run state is missing schema version');
}
if (!SUPPORTED_SCHEMA_VERSIONS.includes(currentSchemaVersion)) {
throw new UserError(`Run state schema version ${currentSchemaVersion} is not supported. Please use version ${CURRENT_SCHEMA_VERSION}.`);
}
const stateJson = SerializedRunState.parse(JSON.parse(str));
return buildRunStateFromJson(initialAgent, stateJson, options);
}
async function buildRunStateFromJson(initialAgent, stateJson, options = {}) {
const agentMap = buildAgentMap(initialAgent);
const contextOverride = options.contextOverride;
const contextStrategy = options.contextStrategy ?? 'merge';
//
// Rebuild the context
//
const context = contextOverride ??
new RunContext(stateJson.context.context);
if (contextOverride) {
if (contextStrategy === 'merge') {
context._mergeApprovals(stateJson.context.approvals);
}
}
else {
context._rebuildApprovals(stateJson.context.approvals);
}
const shouldRestoreToolInput = !contextOverride || contextStrategy === 'merge';
if (shouldRestoreToolInput &&
typeof stateJson.context.toolInput !== 'undefined' &&
typeof context.toolInput === 'undefined') {
context.toolInput = stateJson.context.toolInput;
}
//
// Find the current agent from the initial agent
//
const currentAgent = agentMap.get(stateJson.currentAgent.name);
if (!currentAgent) {
throw new UserError(`Agent ${stateJson.currentAgent.name} not found`);
}
const state = new RunState(context, '', currentAgent, stateJson.maxTurns);
state._currentTurn = stateJson.currentTurn;
state._currentTurnInProgress = stateJson.currentTurnInProgress ?? false;
state._conversationId = stateJson.conversationId ?? undefined;
state._previousResponseId = stateJson.previousResponseId ?? undefined;
state._reasoningItemIdPolicy = stateJson.reasoningItemIdPolicy ?? undefined;
// rebuild tool use tracker
state._toolUseTracker = new AgentToolUseTracker();
for (const [agentName, toolNames] of Object.entries(stateJson.toolUseTracker)) {
state._toolUseTracker.addToolUse(agentMap.get(agentName), toolNames, { allowEmpty: true });
}
state._pendingAgentToolRuns = new Map(Object.entries(stateJson.pendingAgentToolRuns ?? {}));
// rebuild current agent span
if (stateJson.currentAgentSpan) {
if (!stateJson.trace) {
logger.warn('Trace is not set, skipping tracing setup');
}
const trace = 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: agentMap.get(r.agent.name),
}));
state._toolInputGuardrailResults =
stateJson.toolInputGuardrailResults;
state._toolOutputGuardrailResults =
stateJson.toolOutputGuardrailResults;
state._currentStep = stateJson.currentStep;
state._originalInput = stateJson.originalInput;
state._modelResponses = stateJson.modelResponses.map(deserializeModelResponse);
state._lastTurnResponse = stateJson.lastModelResponse
? deserializeModelResponse(stateJson.lastModelResponse)
: undefined;
state._generatedItems = stateJson.generatedItems.map((item) => deserializeItem(item, agentMap));
state._currentTurnPersistedItemCount =
stateJson.currentTurnPersistedItemCount ?? 0;
state._lastProcessedResponse = stateJson.lastProcessedResponse
? await deserializeProcessedResponse(agentMap, state._currentAgent, state._context, stateJson.lastProcessedResponse)
: undefined;
if (stateJson.currentStep?.type === 'next_step_handoff') {
state._currentStep = {
type: 'next_step_handoff',
newAgent: agentMap.get(stateJson.currentStep.newAgent.name),
};
}
else if (stateJson.currentStep?.type === 'next_step_interruption') {
state._currentStep = {
type: 'next_step_interruption',
data: {
...stateJson.currentStep.data,
interruptions: deserializeInterruptions(stateJson.currentStep.data?.interruptions, agentMap, state._currentAgent),
},
};
}
return state;
}
/**
* @internal
*/
export function buildAgentMap(initialAgent) {
const map = new Map();
const queue = [initialAgent];
while (queue.length > 0) {
const currentAgent = queue.shift();
if (map.has(currentAgent.name)) {
continue;
}
map.set(currentAgent.name, currentAgent);
for (const handoff of currentAgent.handoffs) {
if (handoff instanceof Agent) {
if (!map.has(handoff.name)) {
queue.push(handoff);
}
}
else if (handoff.agent) {
if (!map.has(handoff.agent.name)) {
queue.push(handoff.agent);
}
}
}
for (const tool of currentAgent.tools) {
const sourceAgent = getAgentToolSourceAgent(tool);
if (sourceAgent && !map.has(sourceAgent.name)) {
queue.push(sourceAgent);
}
}
}
return map;
}
/**
* @internal
*/
export function deserializeSpan(trace, serializedSpan) {
const spanData = serializedSpan.span_data;
const previousSpan = serializedSpan.previous_span
? deserializeSpan(trace, serializedSpan.previous_span)
: undefined;
const span = 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
*/
export function deserializeModelResponse(serializedModelResponse) {
const usage = new Usage(serializedModelResponse.usage);
return {
usage,
output: serializedModelResponse.output.map((item) => protocol.OutputModelItem.parse(item)),
responseId: serializedModelResponse.responseId,
providerData: serializedModelResponse.providerData,
};
}
/**
* @internal
*/
export function deserializeItem(serializedItem, agentMap) {
switch (serializedItem.type) {
case 'message_output_item':
return new RunMessageOutputItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name));
case 'tool_call_item':
return new RunToolCallItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name));
case 'tool_call_output_item':
return new RunToolCallOutputItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name), serializedItem.output);
case 'reasoning_item':
return new RunReasoningItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name));
case 'handoff_call_item':
return new RunHandoffCallItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name));
case 'handoff_output_item':
return new RunHandoffOutputItem(serializedItem.rawItem, agentMap.get(serializedItem.sourceAgent.name), agentMap.get(serializedItem.targetAgent.name));
case 'tool_approval_item':
return new RunToolApprovalItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name), serializedItem.toolName);
}
}
function deserializeInterruptionItem(serializedItem, agentMap, currentAgent) {
if (serializedItem instanceof RunToolApprovalItem) {
return serializedItem;
}
const parsed = itemSchema.safeParse(serializedItem);
if (parsed.success) {
if (parsed.data.type === 'tool_approval_item') {
const mappedAgent = agentMap.get(parsed.data.agent.name) ?? currentAgent;
return new RunToolApprovalItem(parsed.data.rawItem, mappedAgent, parsed.data.toolName);
}
const item = deserializeItem(parsed.data, agentMap);
return item instanceof 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 mappedAgent = (agentName ? agentMap.get(agentName) : undefined) ?? currentAgent;
const toolName = typeof value.toolName === 'string'
? value.toolName
: typeof rawItem.name === 'string'
? rawItem.name
: undefined;
return new RunToolApprovalItem(value.rawItem, mappedAgent, toolName);
}
function deserializeInterruptions(serializedInterruptions, agentMap, currentAgent) {
if (!Array.isArray(serializedInterruptions)) {
return [];
}
return serializedInterruptions
.map((item) => deserializeInterruptionItem(item, agentMap, currentAgent))
.filter((item) => item instanceof RunToolApprovalItem);
}
/**
* @internal
*/
async function deserializeProcessedResponse(agentMap, currentAgent, context, serializedProcessedResponse) {
const allTools = await currentAgent.getAllTools(context);
const tools = new Map(allTools
.filter((tool) => tool.type === 'function')
.map((tool) => [tool.name, tool]));
const computerTools = new Map(allTools
.filter((tool) => tool.type === 'computer')
.map((tool) => [tool.name, tool]));
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 handoffs = new Map(currentAgent.handoffs.map((entry) => {
if (entry instanceof Agent) {
return [entry.name, handoff(entry)];
}
return [entry.toolName, entry];
}));
const result = {
newItems: serializedProcessedResponse.newItems.map((item) => deserializeItem(item, agentMap)),
toolsUsed: serializedProcessedResponse.toolsUsed,
handoffs: serializedProcessedResponse.handoffs.map((handoff) => {
if (!handoffs.has(handoff.handoff.toolName)) {
throw new UserError(`Handoff ${handoff.handoff.toolName} not found`);
}
return {
toolCall: handoff.toolCall,
handoff: handoffs.get(handoff.handoff.toolName),
};
}),
functions: await Promise.all(serializedProcessedResponse.functions.map(async (functionCall) => {
if (!tools.has(functionCall.tool.name)) {
throw new UserError(`Tool ${functionCall.tool.name} not found`);
}
return {
toolCall: functionCall.toolCall,
tool: tools.get(functionCall.tool.name),
};
})),
computerActions: serializedProcessedResponse.computerActions.map((computerAction) => {
const toolName = computerAction.computer.name;
if (!computerTools.has(toolName)) {
throw new UserError(`Computer tool ${toolName} not found`);
}
return {
toolCall: computerAction.toolCall,
computer: computerTools.get(toolName),
};
}),
shellActions: (serializedProcessedResponse.shellActions ?? []).map((shellAction) => {
const toolName = shellAction.shell.name;
if (!shellTools.has(toolName)) {
throw new UserError(`Shell tool ${toolName} not found`);
}
return {
toolCall: shellAction.toolCall,
shell: shellTools.get(toolName),
};
}),
applyPatchActions: (serializedProcessedResponse.applyPatchActions ?? []).map((applyPatchAction) => {
const toolName = applyPatchAction.applyPatch.name;
if (!applyPatchTools.has(toolName)) {
throw new UserError(`Apply patch tool ${toolName} not found`);
}
return {
toolCall: applyPatchAction.toolCall,
applyPatch: applyPatchTools.get(toolName),
};
}),
mcpApprovalRequests: (serializedProcessedResponse.mcpApprovalRequests ?? []).map((approvalRequest) => ({
requestItem: new RunToolApprovalItem(approvalRequest.requestItem
.rawItem, currentAgent),
mcpTool: approvalRequest.mcpTool,
})),
};
return {
...result,
hasToolsOrApprovalsToRun() {
return (result.handoffs.length > 0 ||
result.functions.length > 0 ||
result.mcpApprovalRequests.length > 0 ||
result.computerActions.length > 0 ||
result.shellActions.length > 0 ||
result.applyPatchActions.length > 0);
},
};
}
//# sourceMappingURL=runState.mjs.map