UNPKG

@openai/agents-core

Version:

The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.

667 lines 27.2 kB
"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.CURRENT_SCHEMA_VERSION = void 0; exports.buildAgentMap = buildAgentMap; exports.deserializeSpan = deserializeSpan; exports.deserializeModelResponse = deserializeModelResponse; exports.deserializeItem = deserializeItem; const zod_1 = require("zod"); const agent_1 = require("./agent.js"); const items_1 = require("./items.js"); const runContext_1 = require("./runContext.js"); const run_1 = require("./run.js"); const runImplementation_1 = require("./runImplementation.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 handoff_1 = require("./handoff.js"); const protocol = __importStar(require("./types/protocol.js")); const safeExecute_1 = require("./utils/safeExecute.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. */ exports.CURRENT_SCHEMA_VERSION = '1.0'; const $schemaVersion = zod_1.z.literal(exports.CURRENT_SCHEMA_VERSION); const serializedAgentSchema = zod_1.z.object({ name: zod_1.z.string(), }); 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 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(), }); const modelResponseSchema = zod_1.z.object({ usage: usageSchema, output: zod_1.z.array(protocol.OutputModelItem), responseId: zod_1.z.string().optional(), providerData: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(), }); const itemSchema = zod_1.z.discriminatedUnion('type', [ 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_call_item'), rawItem: protocol.ToolCallItem.or(protocol.HostedToolCallItem), agent: serializedAgentSchema, }), zod_1.z.object({ type: zod_1.z.literal('tool_call_output_item'), rawItem: protocol.FunctionCallResultItem, agent: serializedAgentSchema, output: zod_1.z.string(), }), 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('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), agent: serializedAgentSchema, }), ]); 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()), }); 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(), })), functions: zod_1.z.array(zod_1.z.object({ toolCall: zod_1.z.any(), tool: zod_1.z.any(), })), computerActions: zod_1.z.array(zod_1.z.object({ toolCall: zod_1.z.any(), computer: zod_1.z.any(), })), 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(), // 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 guardrailFunctionOutputSchema = zod_1.z.object({ tripwireTriggered: zod_1.z.boolean(), outputInfo: zod_1.z.any(), }); 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, }); 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)), modelResponses: zod_1.z.array(modelResponseSchema), context: zod_1.z.object({ usage: usageSchema, approvals: zod_1.z.record(zod_1.z.string(), 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()), })), context: zod_1.z.record(zod_1.z.string(), zod_1.z.any()), }), toolUseTracker: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string())), maxTurns: zod_1.z.number(), currentAgentSpan: SerializedSpan.nullable().optional(), noActiveAgentRun: zod_1.z.boolean(), inputGuardrailResults: zod_1.z.array(inputGuardrailResultSchema), outputGuardrailResults: zod_1.z.array(outputGuardrailResultSchema), currentStep: runImplementation_1.nextStepSchema.optional(), lastModelResponse: modelResponseSchema.optional(), generatedItems: zod_1.z.array(itemSchema), lastProcessedResponse: serializedProcessedResponseSchema.optional(), currentTurnPersistedItemCount: zod_1.z.number().int().min(0).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. */ class RunState { /** * Current turn number in the conversation. */ _currentTurn = 0; /** * The agent currently handling the conversation. */ _currentAgent; /** * Original user input prior to any processing. */ _originalInput; /** * Responses from the model so far. */ _modelResponses; /** * Active tracing span for the current agent if tracing is enabled. */ _currentAgentSpan; /** * Run context tracking approvals, usage, and other metadata. */ _context; /** * Tracks what tools each agent has used. */ _toolUseTracker; /** * 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; /** * Next step computed for the agent to take. */ _currentStep = undefined; /** * 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._toolUseTracker = new runImplementation_1.AgentToolUseTracker(); this._generatedItems = []; this._currentTurnPersistedItemCount = 0; this._maxTurns = maxTurns; this._inputGuardrailResults = []; this._outputGuardrailResults = []; this._trace = (0, tracing_1.getCurrentTrace)(); } /** * 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, run_1.getTurnInput)(this._originalInput, this._generatedItems); } /** * Returns all interruptions if the current step is an interruption otherwise returns an empty array. */ getInterruptions() { if (this._currentStep?.type !== 'next_step_interruption') { return []; } return this._currentStep.data.interruptions; } /** * 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. */ toJSON() { const output = { $schemaVersion: exports.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, }, 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, inputGuardrailResults: this._inputGuardrailResults, outputGuardrailResults: this._outputGuardrailResults.map((r) => ({ ...r, agent: r.agent.toJSON(), })), currentStep: this._currentStep, lastModelResponse: this._lastTurnResponse, generatedItems: this._generatedItems.map((item) => item.toJSON()), currentTurnPersistedItemCount: this._currentTurnPersistedItemCount, lastProcessedResponse: this._lastProcessedResponse, trace: this._trace ? this._trace.toJSON() : null, }; // 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}`); } 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() { return JSON.stringify(this.toJSON()); } /** * 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) { 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 (currentSchemaVersion !== exports.CURRENT_SCHEMA_VERSION) { throw new errors_1.UserError(`Run state schema version ${currentSchemaVersion} is not supported. Please use version ${exports.CURRENT_SCHEMA_VERSION}`); } const stateJson = exports.SerializedRunState.parse(JSON.parse(str)); const agentMap = buildAgentMap(initialAgent); // // Rebuild the context // const context = new runContext_1.RunContext(stateJson.context.context); context._rebuildApprovals(stateJson.context.approvals); // // Find the current agent from the initial agent // const currentAgent = agentMap.get(stateJson.currentAgent.name); if (!currentAgent) { throw new errors_1.UserError(`Agent ${stateJson.currentAgent.name} not found`); } const state = new RunState(context, '', currentAgent, stateJson.maxTurns); state._currentTurn = stateJson.currentTurn; // rebuild tool use tracker state._toolUseTracker = new runImplementation_1.AgentToolUseTracker(); for (const [agentName, toolNames] of Object.entries(stateJson.toolUseTracker)) { state._toolUseTracker.addToolUse(agentMap.get(agentName), toolNames); } // 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, }); 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._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), }; } return state; } } exports.RunState = RunState; /** * @internal */ 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_1.Agent) { if (!map.has(handoff.name)) { queue.push(handoff); } } else if (handoff.agent) { if (!map.has(handoff.agent.name)) { queue.push(handoff.agent); } } } } return map; } /** * @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(); usage.requests = serializedModelResponse.usage.requests; usage.inputTokens = serializedModelResponse.usage.inputTokens; usage.outputTokens = serializedModelResponse.usage.outputTokens; usage.totalTokens = serializedModelResponse.usage.totalTokens; return { usage, output: serializedModelResponse.output.map((item) => protocol.OutputModelItem.parse(item)), responseId: serializedModelResponse.responseId, providerData: serializedModelResponse.providerData, }; } /** * @internal */ function deserializeItem(serializedItem, agentMap) { switch (serializedItem.type) { case 'message_output_item': return new items_1.RunMessageOutputItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name)); case 'tool_call_item': return new items_1.RunToolCallItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name)); case 'tool_call_output_item': return new items_1.RunToolCallOutputItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name), serializedItem.output); case 'reasoning_item': return new items_1.RunReasoningItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name)); case 'handoff_call_item': return new items_1.RunHandoffCallItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name)); case 'handoff_output_item': return new items_1.RunHandoffOutputItem(serializedItem.rawItem, agentMap.get(serializedItem.sourceAgent.name), agentMap.get(serializedItem.targetAgent.name)); case 'tool_approval_item': return new items_1.RunToolApprovalItem(serializedItem.rawItem, agentMap.get(serializedItem.agent.name)); } } /** * @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 handoffs = new Map(currentAgent.handoffs.map((entry) => { if (entry instanceof agent_1.Agent) { return [entry.name, (0, handoff_1.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 errors_1.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 errors_1.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 errors_1.UserError(`Computer tool ${toolName} not found`); } return { toolCall: computerAction.toolCall, computer: computerTools.get(toolName), }; }), mcpApprovalRequests: (serializedProcessedResponse.mcpApprovalRequests ?? []).map((approvalRequest) => ({ requestItem: new items_1.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); }, }; } //# sourceMappingURL=runState.js.map