@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
892 lines • 42.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processModelResponse = processModelResponse;
exports.processModelResponseAsync = processModelResponseAsync;
const errors_1 = require("../errors.js");
const items_1 = require("../items.js");
const tool_1 = require("../tool.js");
const context_1 = require("../tracing/context.js");
const toolIdentity_1 = require("../toolIdentity.js");
const utils_1 = require("../utils/index.js");
const types_1 = require("./types.js");
const toolSearch_1 = require("./toolSearch.js");
const toolCaller_1 = require("./toolCaller.js");
const items_2 = require("./items.js");
const toolSearchAttribution_1 = require("./toolSearchAttribution.js");
function ensureToolAvailable(tool, message, data) {
if (!tool) {
(0, context_1.addErrorToCurrentSpan)({
message,
data,
});
throw new errors_1.ModelBehaviorError(message);
}
return tool;
}
function ensureProgrammaticToolCallingAvailable(output, tools, agent, options) {
const programmaticTool = tools.find((tool) => tool.type === 'hosted_tool' &&
tool.providerData?.type === 'programmatic_tool_calling');
if (programmaticTool || options.allowPromptSuppliedTools === true) {
return;
}
ensureToolAvailable(programmaticTool, `Model produced a program item without programmaticToolCallingTool() in Agent (${agent.name}).`, {
agent_name: agent.name,
tool_call_id: output.callId,
});
}
const MCP_HOSTED_CALL_TYPES = new Set([
'mcp_call',
'mcp_list_tools',
'mcp_approval_request',
'mcp_approval_response',
]);
function ensureHostedToolCallAllowed(output, tools, mcpToolMap, loadedState, agent) {
const providerType = output.providerData?.type;
const isCodeInterpreterCall = providerType === 'code_interpreter_call' ||
providerType === 'code_interpreter' ||
output.name === 'code_interpreter_call' ||
output.name === 'code_interpreter';
if (isCodeInterpreterCall) {
const codeInterpreterTool = tools.find((tool) => tool.type === 'hosted_tool' &&
tool.providerData?.type === 'code_interpreter');
if (!codeInterpreterTool) {
return;
}
(0, toolCaller_1.ensureToolCallerAllowed)(output, codeInterpreterTool.providerData?.allowed_callers, codeInterpreterTool.name, agent);
return;
}
if (!MCP_HOSTED_CALL_TYPES.has(providerType) &&
!MCP_HOSTED_CALL_TYPES.has(output.name)) {
return;
}
const serverLabel = output.providerData?.server_label;
if (typeof serverLabel !== 'string') {
return;
}
const mcpTool = mcpToolMap.get(serverLabel);
if (!mcpTool) {
return;
}
(0, toolCaller_1.ensureToolCallerAllowed)(output, mcpTool.providerData.allowed_callers, serverLabel, agent);
// Hosted MCP discovery can be reported before the tool-search result.
// Listing tools does not execute them or mark the server as loaded.
if (providerType === 'mcp_list_tools' || output.name === 'mcp_list_tools') {
return;
}
if (mcpTool.providerData.defer_loading !== true ||
[
...loadedState.keyedToolSearchOutputsByKey.values(),
...loadedState.anonymousToolSearchOutputs,
].some((searchOutput) => (0, toolSearch_1.toolSearchOutputLoadsHostedMcpTool)(searchOutput, serverLabel, output.providerData?.name))) {
return;
}
const message = `Model produced deferred MCP call ${serverLabel} before it was loaded via tool_search.`;
(0, context_1.addErrorToCurrentSpan)({
message,
data: {
agent_name: agent.name,
mcp_server_label: serverLabel,
tool_call_id: output.id,
},
});
throw new errors_1.ModelBehaviorError(message);
}
function handleToolCallAction({ output, tool, agent, errorMessage, errorData, items, toolsUsed, actions, buildAction, }) {
const resolvedTool = ensureToolAvailable(tool, errorMessage, errorData);
(0, toolCaller_1.ensureToolCallerAllowed)(output, resolvedTool.allowedCallers, resolvedTool.name, agent);
items.push(new items_1.RunToolCallItem(output, agent));
toolsUsed.push(resolvedTool.name);
actions.push(buildAction(resolvedTool));
}
function recordHandoffRequest(output, handoff, agent, items, toolsUsed, handoffs, completedInvocations) {
toolsUsed.push(output.name);
const request = { toolCall: output, handoff };
if (completedInvocations?.has(output.callId) ||
(handoffs.length > 0 &&
!completedInvocations?.has(handoffs[0].toolCall.callId))) {
handoffs.push(request);
return;
}
// Keep the first fresh request first for execution and managed continuation.
// Preflight still validates the identity of every request, including replays.
handoffs.unshift(request);
items.push(new items_1.RunHandoffCallItem(output, agent));
}
function resolveFunctionOrHandoff(toolCall, handoffMap, functionMap) {
const namespace = (0, toolIdentity_1.getToolCallNamespace)(toolCall);
if (!namespace) {
const handoff = handoffMap.get(toolCall.name);
if (handoff) {
return { type: 'handoff', handoff };
}
}
const functionTool = (0, toolIdentity_1.resolveFunctionToolCall)(toolCall, functionMap);
if (!functionTool) {
return {
type: 'not_found',
toolName: (0, toolIdentity_1.getToolCallNamespace)(toolCall) &&
(0, toolIdentity_1.getToolCallNamespace)(toolCall) !== toolCall.name
? `${(0, toolIdentity_1.getToolCallNamespace)(toolCall)}.${toolCall.name}`
: toolCall.name,
};
}
return { type: 'function', tool: functionTool };
}
function throwFunctionToolNotFound(toolName, agent) {
const message = `Tool ${toolName} not found in agent ${agent.name}.`;
(0, context_1.addErrorToCurrentSpan)({
message,
data: {
tool_name: toolName,
agent_name: agent.name,
},
});
throw new errors_1.ModelBehaviorError(message);
}
function recordMissingFunctionTool(output, toolName, agent, items, toolsUsed, functionToolsNotFound, reason) {
toolsUsed.push(toolName);
items.push(new items_1.RunToolCallItem(output, agent));
functionToolsNotFound.push({
toolCall: output,
toolName,
...(reason ? { reason } : {}),
});
}
function normalizeFunctionToolCallForStorage(toolCall, tool) {
const namespace = (0, toolIdentity_1.getToolCallNamespace)(toolCall);
const explicitNamespace = (0, toolIdentity_1.getFunctionToolNamespace)(tool);
const qualifiedToolName = (0, toolIdentity_1.getFunctionToolQualifiedName)(tool);
if (namespace ||
!explicitNamespace ||
!qualifiedToolName ||
toolCall.name !== qualifiedToolName) {
return toolCall;
}
return {
...toolCall,
name: tool.name,
namespace: explicitNamespace,
};
}
function getRawAgentInputItem(item) {
if (item &&
typeof item === 'object' &&
'rawItem' in item &&
item.rawItem &&
typeof item.rawItem === 'object') {
return item.rawItem;
}
return item;
}
function refreshLoadedDeferredToolNames(state) {
state.loadedToolNames.clear();
for (const toolSearchOutput of state.keyedToolSearchOutputsByKey.values()) {
(0, toolSearch_1.addLoadedToolNamesFromToolSearchOutput)(toolSearchOutput, state.loadedToolNames);
}
for (const toolSearchOutput of state.anonymousToolSearchOutputs) {
(0, toolSearch_1.addLoadedToolNamesFromToolSearchOutput)(toolSearchOutput, state.loadedToolNames);
}
}
function recordLoadedToolSearchOutput(state, toolSearchOutput) {
const replacementKey = (0, utils_1.getToolSearchOutputReplacementKey)(toolSearchOutput);
if (replacementKey) {
state.keyedToolSearchOutputsByKey.set(replacementKey, toolSearchOutput);
}
else {
state.anonymousToolSearchOutputs.push(toolSearchOutput);
}
refreshLoadedDeferredToolNames(state);
}
function collectLoadedDeferredToolStateFromHistory(items, agent, agentName) {
const state = {
anonymousToolSearchOutputs: [],
keyedToolSearchOutputsByKey: new Map(),
loadedToolNames: new Set(),
};
// Use the active history boundary without discarding the surviving item owners.
for (const item of (0, items_2.trimToLatestCompaction)(items)) {
if ((item instanceof items_1.RunToolSearchOutputItem ||
item instanceof items_1.RunInputItem) &&
item.agent !== agent) {
continue;
}
const rawItem = getRawAgentInputItem(item);
if (rawItem?.type !== 'tool_search_output') {
continue;
}
// Raw history has no live owner; only an unambiguous recorded name can restore discovery.
if (!(item instanceof items_1.RunToolSearchOutputItem) &&
(typeof agentName !== 'string' ||
rawItem.toolSearchAgentName !== agentName)) {
continue;
}
const replacementKey = (0, utils_1.getToolSearchOutputReplacementKey)(rawItem);
if (replacementKey) {
state.keyedToolSearchOutputsByKey.set(replacementKey, rawItem);
}
else {
state.anonymousToolSearchOutputs.push(rawItem);
}
}
refreshLoadedDeferredToolNames(state);
return state;
}
function seedHostedMcpToolsFromLoadedDeferredToolState(state, mcpToolMap, preserveExistingServerLabels) {
for (const toolSearchOutput of state.keyedToolSearchOutputsByKey.values()) {
(0, toolSearch_1.addHostedMcpToolsFromToolSearchOutput)(toolSearchOutput, mcpToolMap, {
preserveExistingServerLabels,
});
}
for (const toolSearchOutput of state.anonymousToolSearchOutputs) {
(0, toolSearch_1.addHostedMcpToolsFromToolSearchOutput)(toolSearchOutput, mcpToolMap, {
preserveExistingServerLabels,
});
}
}
function buildFunctionToolMap(tools) {
return (0, toolIdentity_1.buildFunctionToolLookupMap)(tools.filter((t) => t.type === 'function'));
}
function getUnresolvedClientToolSearchCalls(modelResponse, hasClientToolSearchTool) {
const clientToolSearchCalls = [];
const pendingCalls = [];
const latestCallByMatchKey = new Map();
const resolvedCalls = new Set();
for (const output of modelResponse.output) {
if (output.type === 'tool_search_call') {
const execution = (0, utils_1.getToolSearchExecution)(output);
if (execution === 'client' ||
(typeof execution === 'undefined' && hasClientToolSearchTool)) {
clientToolSearchCalls.push(output);
pendingCalls.push(output);
const matchKey = (0, utils_1.getToolSearchMatchKey)(output);
if (matchKey) {
latestCallByMatchKey.set(matchKey, output);
}
}
continue;
}
if (output.type !== 'tool_search_output' ||
(0, utils_1.getToolSearchExecution)(output) === 'server') {
continue;
}
const explicitCallId = (0, utils_1.getToolSearchProviderCallId)(output);
const matchedCall = explicitCallId
? latestCallByMatchKey.get(explicitCallId)
: pendingCalls.shift();
if (!matchedCall) {
continue;
}
resolvedCalls.add(matchedCall);
const pendingIndex = pendingCalls.indexOf(matchedCall);
if (pendingIndex >= 0) {
pendingCalls.splice(pendingIndex, 1);
}
}
return clientToolSearchCalls.filter((call) => !resolvedCalls.has(call));
}
function buildGeneratedClientToolSearchOutputMap(modelResponse, tools, hasClientToolSearchTool) {
const clientToolSearchCalls = getUnresolvedClientToolSearchCalls(modelResponse, hasClientToolSearchTool);
const generatedOutputs = new Map();
for (const toolSearchCall of clientToolSearchCalls) {
generatedOutputs.set(toolSearchCall, (0, toolSearch_1.createBuiltInClientToolSearchOutput)(toolSearchCall, tools));
}
return generatedOutputs;
}
async function buildGeneratedClientToolSearchOutputMapAsync(args) {
const { agent, handoffs, modelResponse, replaceableRuntimeToolKeys, runContext, tools, } = args;
const clientToolSearchTool = (0, toolSearch_1.getClientToolSearchHelper)(tools);
const hasClientToolSearchTool = typeof clientToolSearchTool !== 'undefined';
const executionTools = [...tools];
const validationAvailableTools = [...tools];
const validationFunctionMap = buildFunctionToolMap(tools);
const validationHandoffMap = new Map(handoffs.map((handoff) => [handoff.toolName, handoff]));
const validationMcpToolMap = new Map(tools
.filter((tool) => tool.type === 'hosted_tool' && tool.providerData?.type === 'mcp')
.map((tool) => [tool.providerData.server_label, tool]));
const validationReplaceableRuntimeToolKeys = new Set(replaceableRuntimeToolKeys);
const clientToolSearchCalls = getUnresolvedClientToolSearchCalls(modelResponse, hasClientToolSearchTool);
const generatedOutputs = new Map();
for (const toolSearchCall of clientToolSearchCalls) {
if (clientToolSearchTool &&
(0, tool_1.getClientToolSearchExecutor)(clientToolSearchTool)) {
const generatedOutput = await (0, toolSearch_1.executeCustomClientToolSearch)({
agent,
runContext,
toolSearchCall,
toolSearchTool: clientToolSearchTool,
tools: executionTools,
});
(0, toolSearch_1.registerRuntimeToolSearchTools)({
availableTools: validationAvailableTools,
functionMap: validationFunctionMap,
handoffMap: validationHandoffMap,
mcpToolMap: validationMcpToolMap,
replaceableRuntimeToolKeys: validationReplaceableRuntimeToolKeys,
runtimeTools: generatedOutput.runtimeTools,
});
generatedOutputs.set(toolSearchCall, generatedOutput);
executionTools.push(...generatedOutput.callbackRuntimeTools);
continue;
}
generatedOutputs.set(toolSearchCall, {
output: (0, toolSearch_1.createBuiltInClientToolSearchOutput)(toolSearchCall, executionTools),
runtimeTools: [],
callbackRuntimeTools: [],
});
}
return generatedOutputs;
}
function checkDeferredFunctionToolLoaded(toolCall, tool, loadedToolNames, agent, behavior) {
if (tool.deferLoading !== true) {
return true;
}
const explicitNamespace = (0, toolIdentity_1.getFunctionToolNamespace)(tool);
const qualifiedName = (0, toolIdentity_1.getFunctionToolQualifiedName)(tool);
const isLoaded = (qualifiedName ? loadedToolNames.has(qualifiedName) : false) ||
((!explicitNamespace || explicitNamespace === tool.name) &&
loadedToolNames.has(tool.name));
if (isLoaded) {
return true;
}
if (behavior === 'return_error_to_model') {
return false;
}
const toolName = qualifiedName ?? tool.name;
const message = `Model produced deferred function call ${toolName} before it was loaded via tool_search.`;
(0, context_1.addErrorToCurrentSpan)({
message,
data: {
agent_name: agent.name,
tool_name: toolName,
tool_call_id: toolCall.callId,
},
});
throw new errors_1.ModelBehaviorError(message);
}
function parseShellCallStatus(status) {
if (status === 'in_progress' ||
status === 'completed' ||
status === 'incomplete') {
return status;
}
return undefined;
}
function isShellCallPendingStatus(status) {
if (typeof status === 'undefined') {
return true;
}
return parseShellCallStatus(status) === 'in_progress';
}
function hasPendingShellOutputStatus(output) {
const outputStatus = output.status ?? output.providerData?.status;
if (typeof outputStatus !== 'string') {
return false;
}
return isShellCallPendingStatus(outputStatus);
}
/**
* Walks a raw model response and classifies each item so the runner can schedule follow-up work.
* Returns both the serializable RunItems (for history/streaming) and the actionable tool metadata.
*/
function processModelResponse(modelResponse, agent, tools, handoffs, priorItems = [], toolNotFoundBehavior = 'raise_error', processingOptions = {}) {
(0, items_2.assertValidCompactionItems)(modelResponse.output);
const items = [];
const runHandoffs = [];
const runFunctions = [];
const functionToolsNotFound = [];
const runComputerActions = [];
const runShellActions = [];
let hasHostedShellCall = false;
const runApplyPatchActions = [];
const runMCPApprovalRequests = [];
const toolsUsed = [];
const handoffMap = new Map(handoffs.map((h) => [h.toolName, h]));
// Resolve tools upfront so we can look up the concrete handler in O(1) while iterating outputs.
const functionMap = buildFunctionToolMap(tools);
const computerTool = tools.find((t) => t.type === 'computer');
const shellTool = tools.find((t) => t.type === 'shell');
const applyPatchTool = tools.find((t) => t.type === 'apply_patch');
const mcpToolMap = new Map(tools
.filter((t) => t.type === 'hosted_tool' && t.providerData?.type === 'mcp')
.map((t) => t)
.map((t) => [t.providerData.server_label, t]));
const originalMcpServerLabels = new Set(mcpToolMap.keys());
const hasClientToolSearchTool = tools.some((tool) => tool.type === 'hosted_tool' &&
tool.providerData?.type === 'tool_search' &&
tool.providerData.execution === 'client');
const loadedDeferredToolState = collectLoadedDeferredToolStateFromHistory(priorItems, agent, processingOptions.toolSearchAgentName);
seedHostedMcpToolsFromLoadedDeferredToolState(loadedDeferredToolState, mcpToolMap, originalMcpServerLabels);
const generatedClientToolSearchOutputsByCall = buildGeneratedClientToolSearchOutputMap(modelResponse, tools, hasClientToolSearchTool);
let hasGeneratedClientToolSearchOutputs = false;
for (const output of modelResponse.output) {
if (output.type === 'message') {
if (output.role === 'assistant') {
items.push(new items_1.RunMessageOutputItem(output, agent));
}
}
else if (output.type === 'tool_search_call') {
items.push(new items_1.RunToolSearchCallItem(output, agent));
toolsUsed.push('tool_search');
const generatedOutput = generatedClientToolSearchOutputsByCall.get(output);
if (generatedOutput) {
items.push(new items_1.RunToolSearchOutputItem((0, toolSearchAttribution_1.attributeToolSearchOutput)(generatedOutput, processingOptions.toolSearchAgentName), agent));
recordLoadedToolSearchOutput(loadedDeferredToolState, generatedOutput);
(0, toolSearch_1.addHostedMcpToolsFromToolSearchOutput)(generatedOutput, mcpToolMap, {
preserveExistingServerLabels: originalMcpServerLabels,
});
hasGeneratedClientToolSearchOutputs = true;
}
}
else if (output.type === 'tool_search_output') {
const recordedOutput = (0, toolSearch_1.recordHostedMcpDiscovery)(output, [
...agent.tools,
...tools,
]);
items.push(new items_1.RunToolSearchOutputItem((0, toolSearchAttribution_1.attributeToolSearchOutput)(recordedOutput, processingOptions.toolSearchAgentName), agent));
recordLoadedToolSearchOutput(loadedDeferredToolState, recordedOutput);
(0, toolSearch_1.addHostedMcpToolsFromToolSearchOutput)(output, mcpToolMap, {
preserveExistingServerLabels: originalMcpServerLabels,
});
}
else if (output.type === 'program') {
ensureProgrammaticToolCallingAvailable(output, tools, agent, processingOptions);
items.push(new items_1.RunToolCallItem(output, agent));
toolsUsed.push('programmatic_tool_calling');
}
else if (output.type === 'program_output') {
items.push(new items_1.RunToolCallOutputItem(output, agent, output.output));
}
else if (output.type === 'hosted_tool_call') {
ensureHostedToolCallAllowed(output, tools, mcpToolMap, loadedDeferredToolState, agent);
items.push(new items_1.RunToolCallItem(output, agent));
const toolName = output.name;
toolsUsed.push(toolName);
if (output.providerData?.type === 'mcp_approval_request' ||
output.name === 'mcp_approval_request') {
// Hosted remote MCP server's approval process
const providerData = output.providerData;
const mcpServerLabel = providerData.server_label;
const mcpServerTool = mcpToolMap.get(mcpServerLabel);
if (typeof mcpServerTool === 'undefined') {
const message = `MCP server (${mcpServerLabel}) not found in Agent (${agent.name})`;
(0, context_1.addErrorToCurrentSpan)({
message,
data: { mcp_server_label: mcpServerLabel },
});
throw new errors_1.ModelBehaviorError(message);
}
// Do this approval later:
// We support both onApproval callback (like the Python SDK does) and HITL patterns.
const approvalItem = new items_1.RunToolApprovalItem({
type: 'hosted_tool_call',
// We must use this name to align with the name sent from the servers
name: providerData.name,
id: providerData.id,
status: 'in_progress',
providerData,
...(output.caller ? { caller: output.caller } : {}),
}, agent);
runMCPApprovalRequests.push({
requestItem: approvalItem,
mcpTool: mcpServerTool,
});
if (!mcpServerTool.providerData.on_approval) {
// When onApproval function exists, it confirms the approval right after this.
// Thus, this approval item must be appended only for the next turn interruption patterns.
items.push(approvalItem);
}
}
}
else if (output.type === 'reasoning') {
items.push(new items_1.RunReasoningItem(output, agent));
}
else if (output.type === 'compaction') {
items.push(new items_1.RunCompactionItem(output, agent));
}
else if (output.type === 'computer_call') {
handleToolCallAction({
output,
tool: computerTool,
agent,
errorMessage: 'Model produced computer action without a computer tool.',
errorData: { agent_name: agent.name },
items,
toolsUsed,
actions: runComputerActions,
buildAction: (resolvedTool) => ({
toolCall: output,
computer: resolvedTool,
}),
});
}
else if (output.type === 'shell_call') {
const resolvedShellTool = ensureToolAvailable(shellTool, 'Model produced shell action without a shell tool.', { agent_name: agent.name });
(0, toolCaller_1.ensureToolCallerAllowed)(output, resolvedShellTool.allowedCallers, resolvedShellTool.name, agent);
items.push(new items_1.RunToolCallItem(output, agent));
toolsUsed.push(resolvedShellTool.name);
const shellEnvironmentType = resolvedShellTool.environment?.type ?? 'local';
// Hosted container shell is executed by the API provider, so no local action is queued.
if (shellEnvironmentType !== 'local') {
if (isShellCallPendingStatus(output.status)) {
hasHostedShellCall = true;
}
continue;
}
if (!resolvedShellTool.shell) {
const message = 'Model produced local shell action without a local shell implementation.';
(0, context_1.addErrorToCurrentSpan)({
message,
data: { agent_name: agent.name },
});
throw new errors_1.ModelBehaviorError(message);
}
runShellActions.push({
toolCall: output,
shell: resolvedShellTool,
});
}
else if (output.type === 'shell_call_output') {
items.push(new items_1.RunToolCallOutputItem(output, agent, output.output));
if (hasPendingShellOutputStatus(output)) {
hasHostedShellCall = true;
}
}
else if (output.type === 'apply_patch_call') {
handleToolCallAction({
output,
tool: applyPatchTool,
agent,
errorMessage: 'Model produced apply_patch action without an apply_patch tool.',
errorData: { agent_name: agent.name },
items,
toolsUsed,
actions: runApplyPatchActions,
buildAction: (resolvedTool) => ({
toolCall: output,
applyPatch: resolvedTool,
}),
});
}
/*
* Intentionally skip returning here so function_call processing can still
* run when output.type matches other tool call types.
*/
if (output.type !== 'function_call') {
continue;
}
const resolved = resolveFunctionOrHandoff(output, handoffMap, functionMap);
if (resolved.type === 'not_found') {
if (toolNotFoundBehavior !== 'return_error_to_model') {
throwFunctionToolNotFound(resolved.toolName, agent);
}
recordMissingFunctionTool(output, resolved.toolName, agent, items, toolsUsed, functionToolsNotFound);
}
else if (resolved.type === 'handoff') {
(0, toolCaller_1.ensureToolCallerAllowed)(output, undefined, resolved.handoff.toolName, agent);
recordHandoffRequest(output, resolved.handoff, agent, items, toolsUsed, runHandoffs, processingOptions.completedInvocations);
}
else {
(0, toolCaller_1.ensureToolCallerAllowed)(output, resolved.tool.allowedCallers, (0, toolIdentity_1.getFunctionToolQualifiedName)(resolved.tool) ?? resolved.tool.name, agent);
if (!checkDeferredFunctionToolLoaded(output, resolved.tool, loadedDeferredToolState.loadedToolNames, agent, toolNotFoundBehavior)) {
recordMissingFunctionTool(normalizeFunctionToolCallForStorage(output, resolved.tool), (0, toolIdentity_1.getFunctionToolQualifiedName)(resolved.tool) ?? resolved.tool.name, agent, items, toolsUsed, functionToolsNotFound, 'not_loaded');
continue;
}
const normalizedToolCall = normalizeFunctionToolCallForStorage(output, resolved.tool);
toolsUsed.push((0, toolIdentity_1.getFunctionToolQualifiedName)(resolved.tool) ?? resolved.tool.name);
items.push(new items_1.RunToolCallItem(normalizedToolCall, agent, !(0, toolIdentity_1.getToolCallNamespace)(normalizedToolCall) &&
(0, toolIdentity_1.isDeferredTopLevelFunctionTool)(resolved.tool)
? (0, toolIdentity_1.getFunctionToolStateKey)(resolved.tool)
: undefined));
runFunctions.push((0, types_1.createToolRunFunction)({
toolCall: normalizedToolCall,
tool: resolved.tool,
availableFunctionTools: [...new Set(functionMap.values())],
}));
}
}
return {
newItems: items,
handoffs: runHandoffs,
functions: runFunctions,
functionToolsNotFound,
computerActions: runComputerActions,
shellActions: runShellActions,
applyPatchActions: runApplyPatchActions,
mcpApprovalRequests: runMCPApprovalRequests,
toolsUsed: toolsUsed,
hasToolsOrApprovalsToRun() {
return (runHandoffs.length > 0 ||
runFunctions.length > 0 ||
functionToolsNotFound.length > 0 ||
runMCPApprovalRequests.length > 0 ||
runComputerActions.length > 0 ||
runShellActions.length > 0 ||
hasHostedShellCall ||
runApplyPatchActions.length > 0 ||
hasGeneratedClientToolSearchOutputs);
},
};
}
async function processModelResponseAsync(modelResponse, agent, tools, handoffs, state, priorItems = [], toolNotFoundBehavior = 'raise_error', processingOptions = {}) {
(0, items_2.assertValidCompactionItems)(modelResponse.output);
processingOptions = {
...processingOptions,
toolSearchAgentName: state._getToolSearchAgentName(agent),
completedInvocations: state._completedToolInvocations.get(agent),
};
const clientToolSearchTool = (0, toolSearch_1.getClientToolSearchHelper)(tools);
const hasCustomClientToolSearchExecutor = Boolean(clientToolSearchTool && (0, tool_1.getClientToolSearchExecutor)(clientToolSearchTool));
const hasRelevantClientToolSearchCall = modelResponse.output.some((output) => output.type === 'tool_search_call' &&
((0, utils_1.getToolSearchExecution)(output) === 'client' ||
(typeof (0, utils_1.getToolSearchExecution)(output) === 'undefined' &&
typeof clientToolSearchTool !== 'undefined')));
if (!hasCustomClientToolSearchExecutor || !hasRelevantClientToolSearchCall) {
return processModelResponse(modelResponse, agent, tools, handoffs, priorItems, toolNotFoundBehavior, processingOptions);
}
processingOptions.beforeClientToolSearch?.();
const items = [];
const runHandoffs = [];
const runFunctions = [];
const functionToolsNotFound = [];
const runComputerActions = [];
const runShellActions = [];
let hasHostedShellCall = false;
const runApplyPatchActions = [];
const runMCPApprovalRequests = [];
const toolsUsed = [];
const handoffMap = new Map(handoffs.map((h) => [h.toolName, h]));
const functionMap = buildFunctionToolMap(tools);
const computerTool = tools.find((t) => t.type === 'computer');
const shellTool = tools.find((t) => t.type === 'shell');
const applyPatchTool = tools.find((t) => t.type === 'apply_patch');
const mcpToolMap = new Map(tools
.filter((t) => t.type === 'hosted_tool' && t.providerData?.type === 'mcp')
.map((t) => t)
.map((t) => [t.providerData.server_label, t]));
const originalMcpServerLabels = new Set(mcpToolMap.keys());
const replaceableRuntimeToolKeys = new Set(state
.getToolSearchRuntimeTools(agent)
.map((tool) => (0, tool_1.getToolSearchRuntimeRoutingKey)(tool))
.filter((key) => typeof key === 'string'));
const loadedDeferredToolState = collectLoadedDeferredToolStateFromHistory(priorItems, agent, processingOptions.toolSearchAgentName);
seedHostedMcpToolsFromLoadedDeferredToolState(loadedDeferredToolState, mcpToolMap, originalMcpServerLabels);
const generatedClientToolSearchOutputsByCall = await buildGeneratedClientToolSearchOutputMapAsync({
agent,
handoffs,
modelResponse,
replaceableRuntimeToolKeys,
runContext: state._context,
tools,
});
let hasGeneratedClientToolSearchOutputs = false;
const availableTools = [...tools];
for (const output of modelResponse.output) {
if (output.type === 'message') {
if (output.role === 'assistant') {
items.push(new items_1.RunMessageOutputItem(output, agent));
}
}
else if (output.type === 'tool_search_call') {
items.push(new items_1.RunToolSearchCallItem(output, agent));
toolsUsed.push('tool_search');
const generatedOutput = generatedClientToolSearchOutputsByCall.get(output);
if (generatedOutput) {
items.push(new items_1.RunToolSearchOutputItem((0, toolSearchAttribution_1.attributeToolSearchOutput)(generatedOutput.output, processingOptions.toolSearchAgentName), agent));
recordLoadedToolSearchOutput(loadedDeferredToolState, generatedOutput.output);
(0, toolSearch_1.addHostedMcpToolsFromToolSearchOutput)(generatedOutput.output, mcpToolMap, {
preserveExistingServerLabels: originalMcpServerLabels,
});
const replacedRuntimeTools = state.getToolSearchRuntimeToolsForOutput(agent, generatedOutput.output);
const registeredRuntimeTools = (0, toolSearch_1.registerRuntimeToolSearchTools)({
availableTools,
functionMap,
handoffMap,
mcpToolMap,
replaceableRuntimeToolKeys,
replacedRuntimeTools,
runtimeTools: generatedOutput.runtimeTools,
});
state.recordToolSearchRuntimeTools(agent, generatedOutput.output, registeredRuntimeTools);
hasGeneratedClientToolSearchOutputs = true;
}
}
else if (output.type === 'tool_search_output') {
const recordedOutput = (0, toolSearch_1.recordHostedMcpDiscovery)(output, [
...agent.tools,
...availableTools,
]);
items.push(new items_1.RunToolSearchOutputItem((0, toolSearchAttribution_1.attributeToolSearchOutput)(recordedOutput, processingOptions.toolSearchAgentName), agent));
recordLoadedToolSearchOutput(loadedDeferredToolState, recordedOutput);
(0, toolSearch_1.addHostedMcpToolsFromToolSearchOutput)(output, mcpToolMap, {
preserveExistingServerLabels: originalMcpServerLabels,
});
}
else if (output.type === 'program') {
ensureProgrammaticToolCallingAvailable(output, availableTools, agent, processingOptions);
items.push(new items_1.RunToolCallItem(output, agent));
toolsUsed.push('programmatic_tool_calling');
}
else if (output.type === 'program_output') {
items.push(new items_1.RunToolCallOutputItem(output, agent, output.output));
}
else if (output.type === 'hosted_tool_call') {
ensureHostedToolCallAllowed(output, availableTools, mcpToolMap, loadedDeferredToolState, agent);
items.push(new items_1.RunToolCallItem(output, agent));
const toolName = output.name;
toolsUsed.push(toolName);
if (output.providerData?.type === 'mcp_approval_request' ||
output.name === 'mcp_approval_request') {
const providerData = output.providerData;
const mcpServerLabel = providerData.server_label;
const mcpServerTool = mcpToolMap.get(mcpServerLabel);
if (typeof mcpServerTool === 'undefined') {
const message = `MCP server (${mcpServerLabel}) not found in Agent (${agent.name})`;
(0, context_1.addErrorToCurrentSpan)({
message,
data: { mcp_server_label: mcpServerLabel },
});
throw new errors_1.ModelBehaviorError(message);
}
const approvalItem = new items_1.RunToolApprovalItem({
type: 'hosted_tool_call',
name: providerData.name,
id: providerData.id,
status: 'in_progress',
providerData,
...(output.caller ? { caller: output.caller } : {}),
}, agent);
runMCPApprovalRequests.push({
requestItem: approvalItem,
mcpTool: mcpServerTool,
});
if (!mcpServerTool.providerData.on_approval) {
items.push(approvalItem);
}
}
}
else if (output.type === 'reasoning') {
items.push(new items_1.RunReasoningItem(output, agent));
}
else if (output.type === 'compaction') {
items.push(new items_1.RunCompactionItem(output, agent));
}
else if (output.type === 'computer_call') {
handleToolCallAction({
output,
tool: computerTool,
agent,
errorMessage: 'Model produced computer action without a computer tool.',
errorData: { agent_name: agent.name },
items,
toolsUsed,
actions: runComputerActions,
buildAction: (resolvedTool) => ({
toolCall: output,
computer: resolvedTool,
}),
});
}
else if (output.type === 'shell_call') {
const resolvedShellTool = ensureToolAvailable(shellTool, 'Model produced shell action without a shell tool.', { agent_name: agent.name });
(0, toolCaller_1.ensureToolCallerAllowed)(output, resolvedShellTool.allowedCallers, resolvedShellTool.name, agent);
items.push(new items_1.RunToolCallItem(output, agent));
toolsUsed.push(resolvedShellTool.name);
const shellEnvironmentType = resolvedShellTool.environment?.type ?? 'local';
if (shellEnvironmentType !== 'local') {
if (isShellCallPendingStatus(output.status)) {
hasHostedShellCall = true;
}
continue;
}
if (!resolvedShellTool.shell) {
const message = 'Model produced local shell action without a local shell implementation.';
(0, context_1.addErrorToCurrentSpan)({
message,
data: { agent_name: agent.name },
});
throw new errors_1.ModelBehaviorError(message);
}
runShellActions.push({
toolCall: output,
shell: resolvedShellTool,
});
}
else if (output.type === 'shell_call_output') {
items.push(new items_1.RunToolCallOutputItem(output, agent, output.output));
if (hasPendingShellOutputStatus(output)) {
hasHostedShellCall = true;
}
}
else if (output.type === 'apply_patch_call') {
handleToolCallAction({
output,
tool: applyPatchTool,
agent,
errorMessage: 'Model produced apply_patch action without an apply_patch tool.',
errorData: { agent_name: agent.name },
items,
toolsUsed,
actions: runApplyPatchActions,
buildAction: (resolvedTool) => ({
toolCall: output,
applyPatch: resolvedTool,
}),
});
}
if (output.type !== 'function_call') {
continue;
}
const resolved = resolveFunctionOrHandoff(output, handoffMap, functionMap);
if (resolved.type === 'not_found') {
if (toolNotFoundBehavior !== 'return_error_to_model') {
throwFunctionToolNotFound(resolved.toolName, agent);
}
recordMissingFunctionTool(output, resolved.toolName, agent, items, toolsUsed, functionToolsNotFound);
}
else if (resolved.type === 'handoff') {
(0, toolCaller_1.ensureToolCallerAllowed)(output, undefined, resolved.handoff.toolName, agent);
recordHandoffRequest(output, resolved.handoff, agent, items, toolsUsed, runHandoffs, processingOptions.completedInvocations);
}
else {
(0, toolCaller_1.ensureToolCallerAllowed)(output, resolved.tool.allowedCallers, (0, toolIdentity_1.getFunctionToolQualifiedName)(resolved.tool) ?? resolved.tool.name, agent);
if (!checkDeferredFunctionToolLoaded(output, resolved.tool, loadedDeferredToolState.loadedToolNames, agent, toolNotFoundBehavior)) {
recordMissingFunctionTool(normalizeFunctionToolCallForStorage(output, resolved.tool), (0, toolIdentity_1.getFunctionToolQualifiedName)(resolved.tool) ?? resolved.tool.name, agent, items, toolsUsed, functionToolsNotFound, 'not_loaded');
continue;
}
const normalizedToolCall = normalizeFunctionToolCallForStorage(output, resolved.tool);
toolsUsed.push((0, toolIdentity_1.getFunctionToolQualifiedName)(resolved.tool) ?? resolved.tool.name);
items.push(new items_1.RunToolCallItem(normalizedToolCall, agent, !(0, toolIdentity_1.getToolCallNamespace)(normalizedToolCall) &&
(0, toolIdentity_1.isDeferredTopLevelFunctionTool)(resolved.tool)
? (0, toolIdentity_1.getFunctionToolStateKey)(resolved.tool)
: undefined));
runFunctions.push((0, types_1.createToolRunFunction)({
toolCall: normalizedToolCall,
tool: resolved.tool,
availableFunctionTools: [...new Set(functionMap.values())],
}));
}
}
return {
newItems: items,
handoffs: runHandoffs,
functions: runFunctions,
functionToolsNotFound,
computerActions: runComputerActions,
shellActions: runShellActions,
applyPatchActions: runApplyPatchActions,
mcpApprovalRequests: runMCPApprovalRequests,
toolsUsed,
hasToolsOrApprovalsToRun() {
return (runHandoffs.length > 0 ||
runFunctions.length > 0 ||
functionToolsNotFound.length > 0 ||
runMCPApprovalRequests.length > 0 ||
runComputerActions.length > 0 ||
runShellActions.length > 0 ||
hasHostedShellCall ||
runApplyPatchActions.length > 0 ||
hasGeneratedClientToolSearchOutputs);
},
};
}
//# sourceMappingURL=modelOutputs.js.map