@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
2,344 lines • 143 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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Runner = exports.getTurnInput = exports.selectModel = exports.getTracing = void 0;
exports.run = run;
const agentToolStream_1 = require("./runner/agentToolStream.js");
const agent_1 = require("./agent.js");
const events_1 = require("./events.js");
const errors_1 = require("./errors.js");
const guardrail_1 = require("./guardrail.js");
const lifecycle_1 = require("./lifecycle.js");
const logger_1 = __importStar(require("./logger.js"));
const providers_1 = require("./providers.js");
const runContext_1 = require("./runContext.js");
const result_1 = require("./result.js");
const runState_1 = require("./runState.js");
const context_1 = require("./tracing/context.js");
const config_1 = require("./tracing/config.js");
const usage_1 = require("./usage.js");
const tools_1 = require("./utils/tools.js");
const rawUsage_1 = require("./utils/rawUsage.js");
const constants_1 = require("./runner/constants.js");
const protocol_1 = require("./types/protocol.js");
const runtime_1 = require("./sandbox/runtime/index.js");
const conversation_1 = require("./runner/conversation.js");
const guardrails_1 = require("./runner/guardrails.js");
const modelSettings_1 = require("./runner/modelSettings.js");
const modelRetry_1 = require("./runner/modelRetry.js");
const modelOutputs_1 = require("./runner/modelOutputs.js");
const streaming_1 = require("./runner/streaming.js");
const sessionPersistence_1 = require("./runner/sessionPersistence.js");
const turnResolution_1 = require("./runner/turnResolution.js");
const blockedOutputPersistence_1 = require("./runner/blockedOutputPersistence.js");
const outputGuardrailBlockedMessage_1 = require("./runner/outputGuardrailBlockedMessage.js");
const turnPreparation_1 = require("./runner/turnPreparation.js");
const pendingInput_1 = require("./runner/pendingInput.js");
const modelPreparation_1 = require("./runner/modelPreparation.js");
const runLoop_1 = require("./runner/runLoop.js");
const tracing_1 = require("./runner/tracing.js");
const usageTracking_1 = require("./runner/usageTracking.js");
const invocationContext_1 = require("./runner/invocationContext.js");
const traces_1 = require("./tracing/traces.js");
const utils_1 = require("./tracing/utils.js");
const items_1 = require("./runner/items.js");
const errorHandlers_1 = require("./runner/errorHandlers.js");
const sandbox_1 = require("./runner/sandbox.js");
const streamReconciliation_1 = require("./runner/streamReconciliation.js");
const runConfig_1 = require("./runner/runConfig.js");
function hasRetainableBlockedOutputEffect(state) {
return (0, blockedOutputPersistence_1.hasBlockedOutputExecutionEffect)(state._generatedItems, state._currentTurnPersistedItemCount);
}
function commitDeferredRunErrorItemAfterPartialPersistence(state, preparedErrorOutput) {
if (preparedErrorOutput?.deferredItem &&
state._currentTurnPersistedItemCount > state._generatedItems.length) {
state._generatedItems.push(preparedErrorOutput.deferredItem);
return true;
}
return false;
}
var tracing_2 = require("./runner/tracing.js");
Object.defineProperty(exports, "getTracing", { enumerable: true, get: function () { return tracing_2.getTracing; } });
var modelSettings_2 = require("./runner/modelSettings.js");
Object.defineProperty(exports, "selectModel", { enumerable: true, get: function () { return modelSettings_2.selectModel; } });
var items_2 = require("./runner/items.js");
Object.defineProperty(exports, "getTurnInput", { enumerable: true, get: function () { return items_2.getTurnInput; } });
class LazyDefaultModelProvider {
#modelProvider;
getModel(modelName) {
const modelProvider = this.#modelProvider ?? (0, providers_1.getDefaultModelProvider)();
this.#modelProvider = modelProvider;
return modelProvider.getModel(modelName);
}
}
function isNoopTrace(trace) {
return trace instanceof traces_1.NoopTrace || trace?.traceId === utils_1.NOOP_TRACE_OR_SPAN_ID;
}
function rollbackUnstartedTurn(state, snapshot) {
if (!snapshot) {
return false;
}
state._currentTurn = snapshot.currentTurn;
state._currentTurnInProgress = snapshot.currentTurnInProgress;
return true;
}
function recordModelFailureUsage(usage, state, recordUsage) {
state.usage.add(usage);
recordUsage(usage);
}
function assertPendingInputServerOwnership(state, conversationId, previousResponseId) {
const serverManagesConversation = Boolean(conversationId || previousResponseId);
if (state._pendingInput.length === 0 ||
!serverManagesConversation ||
(0, conversation_1.getServerConversationOwner)(conversationId, previousResponseId)) {
return;
}
throw new errors_1.UserError('Pending RunState input requires exactly one server-managed conversation owner', state);
}
async function run(agent, input, options) {
const runner = getDefaultRunner();
if (options?.stream) {
return await runner.run(agent, input, options);
}
else {
return await runner.run(agent, input, options);
}
}
/**
* Orchestrates agent execution, including guardrails, tool calls, session persistence, and
* tracing. Reuse a `Runner` instance when you want consistent configuration across multiple runs.
*/
class Runner extends lifecycle_1.RunHooks {
config;
traceOverrides;
/**
* Creates a runner with optional defaults that apply to every subsequent run invocation.
*
* @param config - Overrides for models, guardrails, tracing, or session behavior.
*/
constructor(config = {}) {
super();
this.config = {
modelProvider: config.modelProvider ?? new LazyDefaultModelProvider(),
model: config.model,
modelSettings: config.modelSettings,
handoffInputFilter: config.handoffInputFilter,
inputGuardrails: config.inputGuardrails,
outputGuardrails: config.outputGuardrails,
tracingDisabled: config.tracingDisabled ?? false,
traceIncludeSensitiveData: config.traceIncludeSensitiveData ?? true,
workflowName: config.workflowName ?? 'Agent workflow',
traceId: config.traceId,
groupId: config.groupId,
traceMetadata: config.traceMetadata,
tracing: config.tracing,
sandbox: config.sandbox,
toolExecution: (0, runConfig_1.validateToolExecutionConfig)(config.toolExecution),
toolNotFoundBehavior: config.toolNotFoundBehavior ?? 'raise_error',
toolNameCollisionPolicy: (0, runConfig_1.validateToolNameCollisionPolicy)(config.toolNameCollisionPolicy),
sessionInputCallback: config.sessionInputCallback,
callModelInputFilter: config.callModelInputFilter,
toolErrorFormatter: config.toolErrorFormatter,
outputGuardrailBlockedMessage: config.outputGuardrailBlockedMessage,
reasoningItemIdPolicy: config.reasoningItemIdPolicy,
};
this.traceOverrides = {
...(config.traceId !== undefined ? { traceId: config.traceId } : {}),
...(config.workflowName !== undefined
? { workflowName: config.workflowName }
: {}),
...(config.groupId !== undefined ? { groupId: config.groupId } : {}),
...(config.traceMetadata !== undefined
? { traceMetadata: config.traceMetadata }
: {}),
...(config.tracing?.apiKey !== undefined
? { tracingApiKey: config.tracing.apiKey }
: {}),
};
this.inputGuardrailDefs = (config.inputGuardrails ?? []).map(guardrail_1.defineInputGuardrail);
this.outputGuardrailDefs = (config.outputGuardrails ?? []).map(guardrail_1.defineOutputGuardrail);
}
async run(agent, input, options = {
stream: false,
context: undefined,
}) {
if (input instanceof runState_1.RunState &&
(input._currentTurn > 0 || input._pendingInput.length > 0) &&
!input._initialInputGuardrailsCompleted) {
throw new errors_1.UserError('Cannot resume this checkpoint because completion of initial input guardrails is unverified. Start a fresh run with the original input and context.', input);
}
this.#validateModelTimeoutForAgent(input instanceof runState_1.RunState ? input._currentAgent : agent);
if (input instanceof runState_1.RunState) {
if (isNoopTrace(input._trace)) {
input._trace = null;
}
if (input._currentAgentSpan?.spanId === utils_1.NOOP_TRACE_OR_SPAN_ID) {
input.setCurrentAgentSpan(undefined);
}
}
const taskSpanName = this.#getTaskSpanName(input instanceof runState_1.RunState ? input._trace?.name : undefined);
const capturedInvocationTraceContext = (0, context_1.getCurrentTraceContext)();
const invocationTraceContext = isNoopTrace(capturedInvocationTraceContext?.trace)
? undefined
: capturedInvocationTraceContext;
const configuredInvocationSpanParent = (0, invocationContext_1.getRunnerInvocationSpanParent)(this);
const invocationSpanParent = configuredInvocationSpanParent ??
(input instanceof runState_1.RunState && input._trace
? input._trace
: (invocationTraceContext?.span ?? invocationTraceContext?.trace));
const resolvedOptions = options ?? { stream: false, context: undefined };
// Per-run options take precedence over runner defaults for session memory behavior.
const sessionInputCallback = resolvedOptions.sessionInputCallback ?? this.config.sessionInputCallback;
// Likewise allow callers to override callModelInputFilter on individual runs.
const callModelInputFilter = resolvedOptions.callModelInputFilter ?? this.config.callModelInputFilter;
// Per-run callback can override runner-level tool error formatting defaults.
const toolErrorFormatter = resolvedOptions.toolErrorFormatter ?? this.config.toolErrorFormatter;
const outputGuardrailBlockedMessage = resolvedOptions.outputGuardrailBlockedMessage === undefined
? this.config.outputGuardrailBlockedMessage
: resolvedOptions.outputGuardrailBlockedMessage;
const reasoningItemIdPolicy = resolvedOptions.reasoningItemIdPolicy ??
(input instanceof runState_1.RunState ? input._reasoningItemIdPolicy : undefined) ??
this.config.reasoningItemIdPolicy;
const toolExecution = (0, runConfig_1.validateToolExecutionConfig)(resolvedOptions.toolExecution ?? this.config.toolExecution);
const toolNotFoundBehavior = resolvedOptions.toolNotFoundBehavior ?? this.config.toolNotFoundBehavior;
const toolNameCollisionPolicy = (0, runConfig_1.validateToolNameCollisionPolicy)(resolvedOptions.toolNameCollisionPolicy === undefined
? this.config.toolNameCollisionPolicy
: resolvedOptions.toolNameCollisionPolicy);
const hasCallModelInputFilter = Boolean(callModelInputFilter);
const tracingConfig = (0, config_1.mergeTracingConfig)(this.config.tracing, resolvedOptions.tracing);
const runContext = input instanceof runState_1.RunState
? input._context
: resolvedOptions.context instanceof runContext_1.RunContext
? resolvedOptions.context
: new runContext_1.RunContext(resolvedOptions.context);
const traceOverrides = {
...this.traceOverrides,
...(resolvedOptions.tracing?.apiKey !== undefined
? { tracingApiKey: resolvedOptions.tracing.apiKey }
: {}),
};
const effectiveOptions = {
...resolvedOptions,
context: runContext,
sessionInputCallback,
callModelInputFilter,
toolErrorFormatter,
outputGuardrailBlockedMessage,
reasoningItemIdPolicy,
toolExecution,
toolNotFoundBehavior,
toolNameCollisionPolicy,
tracing: tracingConfig,
};
const useTaskAndTurnSpans = !this.config.tracingDisabled && (0, config_1.includeTaskAndTurnSpans)(tracingConfig);
const resumingFromState = input instanceof runState_1.RunState;
// A resumed turn may be inactive but already have persisted items.
const preserveTurnPersistenceOnResume = resumingFromState &&
(input._currentTurnInProgress === true ||
input._currentTurnPersistedItemCount >
0);
const resumedConversationId = resumingFromState
? input._conversationId
: undefined;
const resumedPreviousResponseId = resumingFromState
? input._previousResponseId
: undefined;
const serverManagesConversation = Boolean(effectiveOptions.conversationId ?? resumedConversationId) ||
Boolean(effectiveOptions.previousResponseId ?? resumedPreviousResponseId);
// When the server tracks conversation history we defer to it for previous turns so local session
// persistence can focus solely on the new delta being generated in this process.
const session = effectiveOptions.session;
const sessionCompaction = (0, sessionPersistence_1.getSessionCompactionState)(session, input instanceof runState_1.RunState ? input : undefined);
const resumedState = resumingFromState
? input
: undefined;
if (resumedState?._resumedSessionWriteInProgress) {
throw new errors_1.UserError('The same RunState cannot resume or reconcile a Session write concurrently.');
}
const resumedStateRequiresInputCompaction = resumedState !== undefined &&
(0, blockedOutputPersistence_1.hasPendingApprovedToolInputCompaction)(resumedState);
if (resumedStateRequiresInputCompaction) {
if (serverManagesConversation || session === undefined) {
throw new errors_1.UserError('A pending Session compaction requires the same ordinary local Session to resume safely.');
}
(0, runState_1.assertPendingSessionCompactionTerminalAuthority)(resumedState);
}
const releaseResumedSessionWriteOperation = resumedState &&
(resumedState._pendingSessionWrite !== undefined ||
resumedState._currentTurnSessionWriteCompactedItemCount !== undefined ||
resumedStateRequiresInputCompaction ||
resumedState._currentStep?.type === 'next_step_interruption')
? (0, sessionPersistence_1.acquireResumedSessionWriteOperation)(resumedState)
: undefined;
let releaseResumedSessionWriteOperationOnReturn = true;
try {
let provisionalSessionHistoryTransactionSessionId;
let provisionalSessionHistoryTransactionInputItems;
let pendingSessionWriteReconciled = false;
if (resumingFromState) {
(0, runLoop_1.assertAcceptedResponseContinuationAuthority)(resumedState, effectiveOptions.conversationId, effectiveOptions.previousResponseId);
const hadSessionBinding = resumedState._currentTurnSessionHistoryTransactionSessionId !==
undefined;
const portableInputItems = resumedState._currentTurnSessionHistoryTransactionInputItems;
(0, blockedOutputPersistence_1.assertResumedSessionOutputGuardrailSafety)(resumedState, session, this.#agentHasOutputGuardrail(resumedState._currentAgent));
pendingSessionWriteReconciled =
await (0, sessionPersistence_1.reconcilePendingSessionWriteBeforeRun)(session, resumedState, {
serverManagesConversation,
operationAlreadyOwned: releaseResumedSessionWriteOperation !== undefined,
});
resumedState.setReasoningItemIdPolicy(reasoningItemIdPolicy);
await (0, sessionPersistence_1.prepareSessionHistoryTransactionsForRun)(session, resumedState, {
serverManagesConversation,
});
if (!hadSessionBinding) {
provisionalSessionHistoryTransactionSessionId =
resumedState._currentTurnSessionHistoryTransactionSessionId;
provisionalSessionHistoryTransactionInputItems = portableInputItems;
}
}
const sessionPersistence = (0, sessionPersistence_1.createSessionPersistenceTracker)({
session,
runContext,
compactionState: sessionCompaction,
hasCallModelInputFilter,
persistInput: sessionPersistence_1.saveStreamInputToSession,
resumingFromState,
resumedSessionInputItems: resumingFromState
? input
._currentTurnSessionHistoryTransactionInputItems
: undefined,
});
let preparedInput = input;
if (!(preparedInput instanceof runState_1.RunState)) {
const prepared = await (0, sessionPersistence_1.prepareInputItemsWithSession)(preparedInput, session, sessionInputCallback, {
// When the server tracks conversation state we only send the new turn inputs;
// previous messages are recovered via conversationId/previousResponseId.
includeHistoryInPreparedInput: !serverManagesConversation,
preserveDroppedNewItems: serverManagesConversation,
reasoningItemIdPolicy,
}, runContext, sessionCompaction);
if (serverManagesConversation && session) {
// When the server manages memory we only persist the new turn inputs locally so the
// conversation service stays the single source of truth for prior exchanges.
const sessionItems = prepared.sessionItems;
if (sessionItems && sessionItems.length > 0) {
preparedInput = sessionItems;
}
else {
preparedInput = prepared.preparedInput;
}
}
else {
preparedInput = prepared.preparedInput;
}
sessionPersistence?.setPreparedItems(prepared.sessionItems, prepared.preparedInput);
}
// Streaming runs persist the input asynchronously, so track a one-shot helper
// that can be awaited from multiple branches without double-writing.
const ensureStreamInputPersisted = sessionPersistence?.buildPersistInputOnce(serverManagesConversation);
const executeRun = async (effectiveInvocationSpanParent = invocationSpanParent) => {
if (effectiveOptions.stream) {
const streamResult = await this.#runIndividualStream(agent, preparedInput, taskSpanName, effectiveOptions, ensureStreamInputPersisted, sessionPersistence?.setPreparedTurnItems, sessionPersistence?.recordTurnItems, sessionPersistence?.getItemsForPersistence, preserveTurnPersistenceOnResume, provisionalSessionHistoryTransactionSessionId, provisionalSessionHistoryTransactionInputItems, {
sdkSessionId: async () => await session?.getSessionId(),
inputOverride: () => session
? sessionPersistence?.getItemsForPersistence()
: undefined,
}, effectiveInvocationSpanParent, pendingSessionWriteReconciled, sessionCompaction);
if (releaseResumedSessionWriteOperation) {
releaseResumedSessionWriteOperationOnReturn = false;
void streamResult.completed.then(releaseResumedSessionWriteOperation, releaseResumedSessionWriteOperation);
}
return streamResult;
}
const runResult = await this.#runIndividualNonStream(agent, preparedInput, effectiveOptions, taskSpanName, sessionPersistence?.setPreparedTurnItems, sessionPersistence?.recordTurnItems, sessionPersistence?.getItemsForPersistence, preserveTurnPersistenceOnResume, provisionalSessionHistoryTransactionSessionId, provisionalSessionHistoryTransactionInputItems, {
sdkSessionId: async () => await session?.getSessionId(),
inputOverride: () => session
? sessionPersistence?.getItemsForPersistence()
: undefined,
}, effectiveInvocationSpanParent, session && sessionPersistence && !serverManagesConversation
? async (result, persistenceOptions) => {
await (0, sessionPersistence_1.saveToSession)(session, sessionPersistence.getItemsForPersistence(), result, persistenceOptions);
}
: undefined, pendingSessionWriteReconciled, sessionCompaction);
return runResult;
};
if (this.config.tracingDisabled) {
const disabledTrace = new traces_1.NoopTrace();
if (preparedInput instanceof runState_1.RunState) {
preparedInput._currentAgentSpan?.end();
preparedInput._trace = null;
preparedInput.setCurrentAgentSpan(undefined);
}
return await (0, context_1.withTrace)(disabledTrace, async () => {
try {
const result = await executeRun(disabledTrace);
const clearDisabledTraceState = () => {
result.state._trace = null;
result.state.setCurrentAgentSpan(undefined);
};
if (result instanceof result_1.StreamedRunResult) {
void result.completed.then(clearDisabledTraceState, clearDisabledTraceState);
}
else {
clearDisabledTraceState();
}
return result;
}
catch (error) {
if (error instanceof errors_1.AgentsError && error.state) {
error.state._trace = null;
error.state.setCurrentAgentSpan(undefined);
}
throw error;
}
});
}
if (preparedInput instanceof runState_1.RunState && preparedInput._trace) {
const applied = (0, tracing_1.applyTraceOverrides)(preparedInput._trace, preparedInput._currentAgentSpan, traceOverrides);
preparedInput._trace = applied.trace;
preparedInput._currentAgentSpan = applied.currentSpan;
return await (0, context_1.withTrace)(preparedInput._trace, async () => {
if (preparedInput._currentAgentSpan && !useTaskAndTurnSpans) {
(0, context_1.setCurrentSpan)(preparedInput._currentAgentSpan);
}
return executeRun();
});
}
const executeInInvocationTrace = async (effectiveInvocationSpanParent = invocationSpanParent) => {
if (preparedInput instanceof runState_1.RunState && !preparedInput._trace) {
preparedInput._trace = (0, context_1.getCurrentTrace)();
}
return executeRun(effectiveInvocationSpanParent);
};
if (invocationTraceContext) {
return await (0, context_1.withTraceContext)(invocationTraceContext, executeInInvocationTrace);
}
return await (0, context_1.withTrace)(this.config.workflowName ?? 'Agent workflow', async (trace) => executeInInvocationTrace(trace), {
traceId: this.config.traceId,
groupId: this.config.groupId,
metadata: this.config.traceMetadata,
// Per-run tracing config overrides exporter defaults such as environment API key.
tracingApiKey: tracingConfig?.apiKey,
});
}
finally {
if (releaseResumedSessionWriteOperationOnReturn) {
releaseResumedSessionWriteOperation?.();
}
}
}
// --------------------------------------------------------------
// Internals
// --------------------------------------------------------------
#getTaskSpanName(restoredWorkflowName) {
return (this.traceOverrides.workflowName ??
restoredWorkflowName ??
this.config.workflowName ??
'Agent workflow');
}
inputGuardrailDefs;
outputGuardrailDefs;
#agentHasOutputGuardrail(agent) {
return Boolean(this.outputGuardrailDefs.length > 0 || agent.outputGuardrails.length > 0);
}
#captureResponseGuardrailBoundary(state, overwrite) {
(0, blockedOutputPersistence_1.captureCurrentResponseToolOutputGuardrailResultStart)(state, overwrite, this.#agentHasOutputGuardrail(state._currentAgent));
}
#shouldDeferInterruptedSessionItems(state) {
return (0, blockedOutputPersistence_1.shouldDeferInterruptedSessionItems)(state, this.#agentHasOutputGuardrail(state._currentAgent));
}
/**
* @internal
* Resolves the effective model once so both run loops obey the same precedence rules.
*/
async #resolveModelForAgent(agent) {
const explicitlyModelSet = (agent.model !== undefined &&
agent.model !== agent_1.Agent.DEFAULT_MODEL_PLACEHOLDER) ||
(this.config.model !== undefined &&
this.config.model !== agent_1.Agent.DEFAULT_MODEL_PLACEHOLDER);
const selectedModel = (0, modelSettings_1.selectModel)(agent.model, this.config.model);
const resolvedModelName = typeof selectedModel === 'string' ? selectedModel : undefined;
const resolvedModel = typeof selectedModel === 'string'
? await this.config.modelProvider.getModel(selectedModel)
: selectedModel;
return { model: resolvedModel, explicitlyModelSet, resolvedModelName };
}
async #resolveSandboxRuntimeModelForAgent(agent) {
if (!(0, sandbox_1.isSandboxRuntimeAgent)(agent)) {
return this.config.model;
}
const resolved = await this.#resolveModelForAgent(agent);
if (resolved.resolvedModelName &&
resolved.resolvedModelName.trim().length > 0) {
return {
model: resolved.resolvedModelName,
modelInstance: resolved.model,
};
}
return resolved.model;
}
#getAgentToolParentRunConfig(options) {
const hasSandboxOverride = typeof options.sandbox !== 'undefined';
const hasToolExecutionOverride = typeof options.toolExecution !== 'undefined';
const hasToolNotFoundBehaviorOverride = typeof options.toolNotFoundBehavior !== 'undefined';
const hasToolNameCollisionPolicyOverride = typeof options.toolNameCollisionPolicy !== 'undefined';
const hasTracingOverride = typeof options.tracing !== 'undefined';
if (!hasSandboxOverride &&
!hasToolExecutionOverride &&
!hasToolNotFoundBehaviorOverride &&
!hasToolNameCollisionPolicyOverride &&
!hasTracingOverride) {
return this.config;
}
return {
...this.config,
...(hasSandboxOverride ? { sandbox: options.sandbox } : {}),
...(hasToolExecutionOverride
? { toolExecution: options.toolExecution }
: {}),
...(hasToolNotFoundBehaviorOverride
? { toolNotFoundBehavior: options.toolNotFoundBehavior }
: {}),
...(hasToolNameCollisionPolicyOverride
? { toolNameCollisionPolicy: options.toolNameCollisionPolicy }
: {}),
...(hasTracingOverride ? { tracing: options.tracing } : {}),
};
}
/**
* @internal
*/
async #runIndividualNonStream(startingAgent, input, options, taskSpanName, sessionTurnInputUpdate,
// sessionInputUpdate lets the caller adjust queued session items after filters run so we
// persist exactly what we send to the model (e.g., after redactions or truncation).
sessionInputUpdate, getSessionInputForPersistence, preserveTurnPersistenceOnResume, provisionalSessionHistoryTransactionSessionId, provisionalSessionHistoryTransactionInputItems, sandboxMemoryRunContext, invocationSpanParent, persistResult, pendingSessionWriteReconciled = false, sessionCompaction) {
return (0, context_1.withNewSpanContext)(async () => {
// if we have a saved state we use that one, otherwise we create a new one
const isResumedState = input instanceof runState_1.RunState;
const state = isResumedState
? input
: new runState_1.RunState(options.context instanceof runContext_1.RunContext
? options.context
: new runContext_1.RunContext(options.context), input, startingAgent, options.maxTurns === undefined
? constants_1.DEFAULT_MAX_TURNS
: options.maxTurns);
(0, sessionPersistence_1.bindSessionCompactionState)(state, sessionCompaction);
this.#validateModelTimeoutForAgent(state._currentAgent);
if (isResumedState) {
state._agentToolInvocation = undefined;
if (options.maxTurns !== undefined) {
state._maxTurns = options.maxTurns;
}
}
const sandboxRuntime = new runtime_1.SandboxRuntimeManager({
startingAgent,
sandboxConfig: options.sandbox ?? this.config.sandbox,
runState: isResumedState
? state
: undefined,
});
const agentToolParentRunConfig = this.#getAgentToolParentRunConfig(options);
const resolvedReasoningItemIdPolicy = options.reasoningItemIdPolicy ??
(isResumedState ? state._reasoningItemIdPolicy : undefined) ??
this.config.reasoningItemIdPolicy;
state.setReasoningItemIdPolicy(resolvedReasoningItemIdPolicy);
const resolvedConversationId = options.conversationId ??
(isResumedState ? state._conversationId : undefined);
const resolvedPreviousResponseId = options.previousResponseId ??
(isResumedState ? state._previousResponseId : undefined);
const serverManagesConversation = Boolean(resolvedConversationId || resolvedPreviousResponseId);
assertPendingInputServerOwnership(state, resolvedConversationId, resolvedPreviousResponseId);
if (!isResumedState) {
await (0, sessionPersistence_1.prepareSessionHistoryTransactionsForRun)(options.session, state, {
serverManagesConversation,
});
}
if (!isResumedState) {
state.setConversationContext(resolvedConversationId, resolvedPreviousResponseId);
}
const serverConversationTracker = serverManagesConversation
? new conversation_1.ServerConversationTracker({
conversationId: resolvedConversationId,
previousResponseId: resolvedPreviousResponseId,
reasoningItemIdPolicy: resolvedReasoningItemIdPolicy,
})
: undefined;
if (serverConversationTracker && isResumedState) {
serverConversationTracker.primeFromState({
originalInput: state._originalInput,
generatedItems: state._generatedItems,
modelResponses: state._modelResponses,
});
state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
}
const toolErrorFormatter = options.toolErrorFormatter ?? this.config.toolErrorFormatter;
const outputGuardrailBlockedMessage = options.outputGuardrailBlockedMessage === undefined
? this.config.outputGuardrailBlockedMessage
: options.outputGuardrailBlockedMessage;
const useTaskAndTurnSpans = !this.config.tracingDisabled &&
(0, config_1.includeTaskAndTurnSpans)(options.tracing);
const resumingInterruptedTurn = isResumedState && state._currentStep?.type === 'next_step_interruption';
const invocationSpans = useTaskAndTurnSpans
? (0, tracing_1.startRunnerInvocationSpans)({
name: taskSpanName,
agent: state._currentAgent,
restoredAgentSpan: isResumedState
? state._currentAgentSpan
: undefined,
resumeInterruptedTurn: resumingInterruptedTurn,
parent: invocationSpanParent,
})
: undefined;
const taskSpan = invocationSpans?.taskSpan;
const optOutResumeAgentSpan = resumingInterruptedTurn && !useTaskAndTurnSpans
? (0, tracing_1.ensureActiveAgentSpanForInterruptedResume)({
agent: state._currentAgent,
restoredAgentSpan: isResumedState
? state._currentAgentSpan
: undefined,
parent: invocationSpanParent ?? (0, context_1.getCurrentTrace)() ?? undefined,
})
: undefined;
if (useTaskAndTurnSpans && isResumedState) {
state.setCurrentAgentSpan(invocationSpans?.agentSpan);
}
else if (optOutResumeAgentSpan) {
state.setCurrentAgentSpan(optOutResumeAgentSpan);
}
// Tracks when we resume an approval interruption so the next run-again step stays in the same turn.
let continuingInterruptedTurn = false;
let runError;
const attemptedRunErrorHandlers = new WeakSet();
let currentTurnSpan;
let turnPendingModelRequest;
let guardrailTracker = (0, guardrails_1.createGuardrailTracker)();
const parentUsageRecorder = (0, usageTracking_1.getRunnerParentUsageRecorder)(this);
const recordUsage = (usage) => {
(0, tracing_1.recordRunnerSpanUsage)(taskSpan, usage);
(0, tracing_1.recordRunnerSpanUsage)(currentTurnSpan, usage);
parentUsageRecorder?.(usage);
};
(0, usageTracking_1.setRunStateUsageRecorder)(state, recordUsage);
let completedResult;
let persistenceCheckpoint;
let cancelledOutputRedacted = false;
const resumedStateHasPersistedToolOutput = isResumedState && (0, blockedOutputPersistence_1.hasPersistedToolOutput)(state);
let approvedToolCheckpointCompacted = pendingSessionWriteReconciled ||
state._currentTurnSessionWriteCompactedItemCount ===
state._currentTurnPersistedItemCount ||
(resumedStateHasPersistedToolOutput &&
!(0, blockedOutputPersistence_1.hasPendingApprovedToolInputCompaction)(state));
let approvedToolCheckpointRequiresLocalInputCompaction = resumedStateHasPersistedToolOutput;
let approvedToolCheckpointModelResponseCount = state._modelResponses.length;
let approvedToolFinalSessionWritePreparation;
let deferredApprovedToolFinalSessionWritePreparation;
let completedResultPersisted = false;
const completeResult = (result) => {
completedResult = result;
return result;
};
const persistNonStreamingResult = async (result, overrideOptions) => {
if (this.#shouldDeferInterruptedSessionItems(result.state)) {
return;
}
const hasUnpersistedItems = result.newItems.length > state._currentTurnPersistedItemCount ||
(overrideOptions?.additionalRunItems?.length ?? 0) > 0;
const modelResponseAdvanced = result.rawResponses.length > approvedToolCheckpointModelResponseCount;
const compactionOptions = approvedToolCheckpointRequiresLocalInputCompaction
? approvedToolCheckpointCompacted &&
!hasUnpersistedItems &&
!modelResponseAdvanced
? { runCompaction: false }
: { compactionMode: 'input' }
: undefined;
const persistenceOptions = {
...compactionOptions,
...overrideOptions,
};
const finalSessionWritePreparation = approvedToolFinalSessionWritePreparation;
await persistResult?.(result, {
...persistenceOptions,
resumedSessionWritePreparation: finalSessionWritePreparation ??
persistenceOptions.resumedSessionWritePreparation,
});
if (finalSessionWritePreparation) {
approvedToolFinalSessionWritePreparation = undefined;
if (persistenceOptions.deferResumedSessionWrite !== true) {
approvedToolCheckpointCompacted = true;
approvedToolCheckpointModelResponseCount =
result.rawResponses.length;
}
}
};
const activateDeferredApprovedToolFinalSessionWrite = () => {
const preparation = deferredApprovedToolFinalSessionWritePreparation;
if (!preparation) {
return;
}
if (state._pendingSessionHistoryTransaction !== undefined ||
state._currentTurnDeferredSessionItemIndexes.size > 0 ||
state._currentTurnBlockedSessionStartIndex !== undefined) {
deferredApprovedToolFinalSessionWritePreparation = undefined;
return;
}
if (!options.session || serverManagesConversation) {
throw new errors_1.UserError('Resumed Session write preparation has no ordinary Session owner.');
}
deferredApprovedToolFinalSessionWritePreparation = undefined;
approvedToolFinalSessionWritePreparation = preparation;
};
const recordNonStreamingError = (error) => {
if (state._currentAgentSpan) {
state._currentAgentSpan.setError({
message: 'Error in agent run',
data: {
error: (0, tracing_1.getRunnerSpanErrorDetails)(error, this.config.traceIncludeSensitiveData),
},
});
}
(0, tracing_1.setRunnerSpanError)(currentTurnSpan, error, this.config.traceIncludeSensitiveData);
(0, tracing_1.setRunnerSpanError)(taskSpan, error, this.config.traceIncludeSensitiveData);
runError = error;
};
const finalizeCurrentOutput = async (preparedErrorOutput) => {
const currentStep = state._currentStep;
if (currentStep?.type !== 'next_step_final_output') {
throw new errors_1.ModelBehaviorError('Expected a final output step while finalizing the run.', state);
}
(0, runLoop_1.markAcceptedResponseFinalizationStarted)(state);
(0, blockedOutputPersistence_1.normalizeSerializedOutputGuardrailBlockedMessage)(state);
const resolveBlockedMessage = (0, outputGuardrailBlockedMessage_1.createOutputGuardrailBlockedMessageResolver)(outputGuardrailBlockedMessage, state);
const outputGuardrailResultStart = state._outputGuardrailResults.length;
let outputGuardrailBlocked = false;
try {
await (0, guardrails_1.finalizeOutputGuardrails)({
state,
runnerOutputGuardrails: this.outputGuardrailDefs,
output: currentStep.output,
redactedOutput: outputGuardrailBlockedMessage_1.OUTPUT_GUARDRAIL_BLOCKED_TOOL_OUTPUT,
guardedTerminalToolOutput: (0, blockedOutputPersistence_1.hasTerminalToolOutputSource)(state),
signal: options.signal,
sanitizeRejectedOutput: (...args) => (0, blockedOutputPersistence_1.sanitizeBlockedTerminalToolOutput)(...args, resolveBlockedMessage, options.signal, () => {
outputGuardrailBlocked = true;
}),
persistBlockedOutput: persistResult
? async () => persistResult(new result_1.RunResult(state), {
outputBlocked: true,
})
: undefined,
persistUnblockedFailure: async () => {
activateDeferredApprovedToolFinalSessionWrite();
await persistNonStreamingResult(new result_1.RunResult(state));
completedResultPersisted = true;
},
onBlockedOutput: () => {
outputGuardrailBlocked = true;
},
releaseBlockedOutputPersistence: sessionPersistence_1.releaseUnusedSessionHistoryTransactionBinding,
});
}
catch (error) {
if (!outputGuardrailBlocked &&
(deferredApprovedToolFinalSessionWritePreparation !== undefined ||
state._pendingSessionWrite !== undefined ||
approvedToolCheckpointCompacted)) {
state._outputGuardrailResults.splice(outputGuardrailResultStart);
if (deferredApprovedToolFinalSessionWritePreparation !== undefined &&
state._pendingSessionWrite === undefined) {
try {
activateDeferredApprovedToolFinalSessionWrite();
await persistNonStreamingResult(new result_1.RunResult(state), { deferResumedSessionWrite: true });
}
catch (checkpointError) {
if (state._pendingSessionWrite === undefined) {
throw checkpointError;
}
}
}
}
throw error;
}
if (state._serializedCurrentStep === currentStep &&
hasRetainableBlockedOutputEffect(state)) {
(0, sessionPersistence_1.releaseProvisionalSessionHistoryTransactionBinding)(state, provisionalSessionHistoryTransactionSessionId, provisionalSessionHistoryTransactionInputItems);
throw new errors_1.UserError('Accepted final output cannot be resumed directly from serialized terminal state. Start a new run from persisted session history.');
}
activateDeferredApprovedToolFinalSessionWrite();
(0, tracing_1.finishRunnerSpan)(currentTurnSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(state, undefined);
currentTurnSpan = undefined;
const result = new result_1.RunResult(state);
try {
await persistNonStreamingResult(result, preparedErrorOutput?.deferredItem
? { additionalRunItems: [preparedErrorOutput.deferredItem] }
: undefined);
}
catch (error) {
state._outputGuardrailResults.splice(outputGuardrailResultStart);
commitDeferredRunErrorItemAfterPartialPersistence(state, preparedErrorOutput);
throw error;
}
if (preparedErrorOutput?.deferredItem) {
state._generatedItems.push(preparedErrorOutput.deferredItem);
}
completedResultPersisted = true;
state._currentTurnInProgress = false;
this.emit('agent_end', state._context, state._currentAgent, currentStep.output);
state._currentAgent.emit('agent_end', state._context, currentStep.output);
return completeResult(result);
};
try {
while (true) {
// if we don't have a current step, we treat this as a new run
state._currentStep = state._currentStep ?? {
type: 'next_step_run_again',
};
if ((0, runLoop_1.isAcceptedResponseCheckpoint)(state)) {
this.#captureResponseGuardrailBoundary(state, false);
await (0, runLoop_1.resumeAcceptedModelResponse)({
state,
runner: this,
toolErrorFormatter,
agentToolParentRunConfig,
signal: options.signal,
validateHandoffAgent: (handoffAgent) => {
this.#validateModelTimeoutForAgent(handoffAgent);
},
});
}
if (state._currentStep.type === 'next_step_interruption') {
await (0, sandbox_1.prepareSandboxInterruptedTurnResume)({
startingAgent,
state,
sandboxRuntime,
runConfigModel: await this.#resolveSandboxRuntimeModelForAgent(state._currentAgent),
toolNameCollisionPolicy: options.toolNameCollisionPolicy,
tracingParent: (0, invocationContext_1.getRunStateTurnSpanParent)(state) ?? state._currentAgentSpan,
});
if (useTaskAndTurnSpans) {
currentTurnSpan = (0, tracing_1.ensureTurnSpan)(currentTurnSpan, state._currentTurn, state._currentAgent.name, state._currentAgentSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(state, currentTurnSpan.span);
}
const { interruptedOutcome, approvedToolCheckpointDeferred } = await (async () => {
let resumedSessionWritePreparation;
this.#captureResponseGuardrailBoundary(state, false);
const outcome = await (0, runLoop_1.resumeInterruptedTurn)({
state,
runner: this,
toolErrorFormatter,
agentToolParentRunConfig,
signal: options.signal,
validateHandoffAgent: (handoffAgent) => {
this.#validateModelTimeoutForAgent(handoffAgent);
},
beforeResumedSideEffects: async () => {
if (persistResult &&
options.session &&
!serverManagesConversation) {
resumedSessionWritePreparation =
await (0, sessionPersistence_1.prepareResumedSessionWrite)(options.session, state);
}
return resumedSessionWritePreparation;
},
});
const checkpointDeferred = outcome.resumedSideEffects &&
outcome.nextStep.type === 'next_step_final_output' &&
this.#agentHasOutputGuardrail(state._currentAgent);
if (outcome.resumedSideEffects) {
const approvedToolResult = new result_1.RunResult(state);
approvedToolCheckpointRequiresLocalInputCompaction = true;
if (persistResult &&
!this.#shouldDeferInterruptedSessionItems(state)) {
if (options.session &&
!serverManagesConversation &&
!resumedSessionWritePreparation) {
throw new errors_1.UserError('Resumed Session write preparation is missing after approved tool execution.');
}
if (checkpointDeferred) {
deferredApprovedToolFinalSessionWritePreparation =
resumedSessionWritePreparation;
}
else if (outcome.nextStep.type === 'next_step_final_output') {
approvedToolFinalSessionWritePreparation =
resumedSessionWritePreparation;
}
if (outcome.nextStep.type !== 'next_step_final_output') {
await persistResult(approvedToolResult, {
compactionMode: 'input',
resumedSessionWritePreparation,
});
approvedToolCheckpointCompacted = true;
approvedToolCheckpointModelResponseCount =
approvedToolResult.rawResponses.length;
}
else {
approvedToolFinalSessionWritePreparation ??=
checkpointDeferred
? undefined
: resumedSessionWritePreparation;
}
}
}
return {
interruptedOutcome: outcome,
approvedToolCheckpointDeferred: checkpointDeferred,
};
})();
if (options.signal?.aborted && !approvedToolCheckpointDeferred) {
persistenceCheckpoint = new result_1.RunResult(state);
}
options.signal?.throwIfAborted();
// Don't reset counter here - resolveInterruptedTurn already adjusted it via rewind logic
// The counter will be reset when _currentTurn is incremented (starting a new turn)
const { shouldReturn, shouldContinue } = (0, runLoop_1.handleInterruptedOutcome)({
state,
outcome: interruptedOutcome,
setContinuingInterruptedTurn: (value) => {
continuingInterruptedTurn = value;
},
});
// Approval resolution belongs to the previous turn; any next model call
// starts a new counted turn with its own span and usage.
(0, tracing_1.finishRunnerSpan)(currentTurnSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(state, undefined);
currentTurnSpan = undefined;
if (shouldReturn) {
// we are still in an interruption, so we need to avoid an infinite loop
return completeResult(new result_1.RunResult(state));
}
if (shouldContinue) {
continue;
}
}
if (state._currentStep.type === 'next_step_run_again') {
this.#validateModelTimeoutForAgent(state._currentAgent);
if (approvedToolCheckpointCompacted &&
state._currentTurnSessionHistoryTransactionSessionId === undefined) {
await (0, sessionPersistence_1.prepareSessionHistoryTransactionsForRun)(options.session, state, { serverManagesConversation: false });
}
const wasContinuingInterruptedTurn = continuingInterruptedTurn;
continuingInterruptedTurn = false;
guardrailTracker = (0, guardrails_1.createGuardrailTracker)();
const previousTurn = state._currentTurn;
turnPendingModelRequest = {
currentTurn: previousTurn,
currentTurnInProgress: state._currentTurnInProgress,
};
const previousPersistedCount = state._currentTurnPersistedItemCount;
const previousGeneratedCount = state._generatedItems.length;
const { turnInput, pendingInputItems, pendingInputSourceItems } = await (0, turnPreparation_1.prepareTurn)({
state,
input: state._originalInput,
generatedItems: state._generatedItems,
isResumedState,
preserveTurnPersistenceOnResume,
continuingInterruptedTurn: wasContinuingInterruptedTurn,
serverConversationTracker,
inputGuardrailDefs: this.inputGuardrailDefs,
guardrailHandlers: {
onParallelPromise: guardrailTracker.setPromise,
onParallelError: guardrailTracker.setError,
},
emitAgentStart: (context, agent, inputItems) => {
this.emit('agent_start', context, agent, inputItems);
},
onAgentSpanReady: useTaskAndTurnSpans
? (turn, agentName) => {
currentTurnSpan = (0, tracing_1.ensureTurnSpan)(currentTurnSpan, turn, agentName, state._currentAgentSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(state, currentTurnSpan.span);
}
: undefined,
agentSpanParent: taskSpan?.span ?? invocationSpanParent,
});
if (preserveTurnPersistenceOnResume &&
state._currentTurn > previousTurn &&
previousPersistedCount <= previousGeneratedCount) {
// Preserve persisted offsets from a resumed run to avoid re-saving prior items.
state._currentTurnPersistedItemCount = previousPersistedCount;
}
const sessionPreparedTurnInput = [...turnInput];
const preparedSandboxAgent = await sandboxRuntime.prepareAgent({
currentAgent: state._currentAgent,
turnInput,
runConfigModel: await this.#resolveSandboxRuntimeModelForAgent(state._currentAgent),
tracingParent: currentTurnSpan?.span ?? state._currentAgentSpan,
});
sessionTurnInputUpdate?.(sessionPreparedTurnInput, preparedSandboxAgent.turnInput);
const processedPendingInputItems = (0, pendingInput_1.mapPendingInputAfterContextProcessing)(pendingInputItems, sessionPreparedTurnInput, preparedSandboxAgent.turnInput);
const artifacts = await (0, modelPreparation_1.prepareAgentArtifacts)(state, preparedSandboxAgent.executionAgent, options.toolNameCollisionPolicy);
const preparedCall = await this.#prepareModelCall(state, preparedSandboxAgent.executionAgent, options, artifacts, preparedSandboxAgent.turnInput, serverConversationTracker, sessionInputUpdate);
(0, sessionPersistence_1.captureSessionHistoryTransactionInputItems)(options.session, state, getSessionInputForPersistence?.());
await guardrailTracker.throwIfError();
const requiresPendingInputAdmissionCheckpoint = pendingInputItems.length > 0 ||
(!serverConversationTracker && (0, pendingInput_1.hasUnpersistedRunInput)(state));
if (requiresPendingInputAdmissionCheckpoint) {
options.signal?.throwIfAborted();
}
const admittedPendingInput = (0, pendingInput_1.selectPendingInputForAdmission)(processedPendingInputItems, preparedCall);
let localPendingInputCommitted = false;
const commitLocalPendingInput = async () => {
if (serverConversationTracker || localPendingInputCommitted) {
return;
}
localPendingInputCommitted = true;
(0, pendingInput_1.commitPendingInput)(state, pendingInputItems, admittedPendingInput, pendingInputSourceItems);
if ((0, pendingInput_1.hasUnpersistedRunInput)(state)) {
await (0, sessionPersistence_1.saveToSession)(options.session, undefined, new result_1.RunResult(state), { runCompaction: false });
}
if (requiresPendingInputAdmissionCheckpoint) {
options.signal?.throwIfAborted();
}
};
const deferLocalPendingInputAdmission = !serverConversationTracker &&
pendingInputItems.length > 0 &&
guardrailTracker.pending;
if (!deferLocalPendingInputAdmission) {
await commitLocalPendingInput();
}
const modelRequest = {
systemInstructions: preparedCall.modelInput.instructions,
prompt: preparedCall.prompt,
// Explicit agent/run config models should take precedence over prompt defaults.
...(preparedCall.explicitlyModelSet
? { overridePromptModel: true }
: {}),
input: preparedCall.modelInput.input,
previousResponseId: preparedCall.previousResponseId,
conversationId: preparedCall.conversationId,
modelSettings: preparedCall.modelSettings,
_internal: preparedCall.modelRequestInternal,
tools: preparedCall.serializedTools,
toolsExplicitlyProvided: preparedCall.toolsExplicitlyProvided,
outputType: (0, tools_1.convertAgentOutputTypeToSerializable)(state._currentAgent.outputType),
handoffs: preparedCall.serializedHandoffs,
tracing: (0, tracing_1.getTracing)(this.config.tracingDisabled, this.config.traceIncludeSensitiveData),
signal: options.signal,
};
turnPendingModelRequest = undefined;
let serverInputMarked = false;
const markServerInputAccepted = (responseAvailable = true) => {
if (serverInputMarked || !serverConversationTracker) {
return;
}
serverConversationTracker.markInputAsSent(preparedCall.filterApplied
? preparedCall.sourceItems
: preparedCall.turnInput, {
filterApplied: preparedCall.filterApplied,
allTurnItems: preparedCall.turnInput,
});
(0, pendingInput_1.commitPendingInput)(state, pendingInputItems, admittedPendingInput, pendingInputSourceItems);
if (pendingInputItems.length > 0) {
if (admittedPendingInput.length > 0) {
serverConversationTracker.markInputAsSent(admittedPendingInput.map((item) => item.rawItem));
}
}
if (pendingInputItems.length > 0 || !responseAvailable) {
if (!responseAvailable) {
state._lastTurnResponse = undefined;
}
state._lastProcessedResponse = undefined;
state._currentStep = {
type: 'next_step_interruption',
data: { interruptions: [], responseAccepted: true },
};
}
serverInputMarked = true;
};
const completeCompactionExchange = (0, sessionPersistence_1.prepareSessionCompactionExchange)(options.session, state, preparedCall.modelInput.input);
const pendingModelResponse = (0, modelRetry_1.getResponseWithRetry)(preparedCall.model, modelRequest, {
onModelFailureUsage: (usage) => recordModelFailureUsage(usage, state, recordUsage),
...(serverConversationTracker
? {
onPossiblyAcceptedRequestFailure: () => markServerInputAccepted(false),
}
: {}),
});
if (deferLocalPendingInputAdmission) {
const modelResponseOutcome = pendingModelResponse.then((response) => ({ status: 'fulfilled', response }), (error) => ({ status: 'rejected', error }));
try {
await guardrailTracker.awaitCompletion();
await commitLocalPendingInput();
}
catch (error) {
// The request already started in parallel, so drain it before
// surfacing a guardrail or persistence failure.
await modelResponseOutcome;
throw error;
}
const outcome = await modelResponseOutcome;
if (outcome.status === 'rejected') {
throw outcome.error;
}
state._lastTurnResponse = outcome.response;
}
else {
state._lastTurnResponse = await pendingModelResponse;
}
if (serverConversationTracker) {
markServerInputAccepted();
}
completeCompactionExchange(state._lastTurnResponse.output, state._lastTurnResponse.responseId);
state._modelResponses.push(state._lastTurnResponse);
state._context.usage.add(state._lastTurnResponse.usage);
recordUsage(state._lastTurnResponse.usage);
state._noActiveAgentRun = false;
// After each turn record the items echoed by the server so future requests only
// include the incremental inputs that have not yet been acknowledged.
serverConversationTracker?.trackServerItems(state._lastTurnResponse);
if (serverConversationTracker) {
state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
}
const processedResponse = await (0, modelOutputs_1.processModelResponseAsync)(state._lastTurnResponse, state._currentAgent, preparedCall.tools, preparedCall.handoffs, state,
// Loading authority retains the agent ownership lost in model input.
[
...(Array.isArray(state._originalInput)
? state._originalInput
: []),
...state._generatedItems,
], options.toolNotFoundBehavior, {
allowPromptSuppliedTools: preparedCall.allowPromptSuppliedTools,
beforeClientToolSearch: () => (0, turnResolution_1.preflightModelResponseToolInvocations)(state._currentAgent, state, state._lastTurnResponse, preparedCall.tools, preparedCall.handoffs, this, options.signal),
});
serverConversationTracker?.trackServerItems(state._lastTurnResponse, processedResponse.newItems);
state._lastProcessedResponse = processedResponse;
(0, modelPreparation_1.trackDeferredToolRecovery)(state, state._currentAgent, processedResponse);
const suppressedToolCalls = (0, turnResolution_1.preflightToolInvocations)(state._currentAgent, state, processedResponse);
await guardrailTracker.awaitCompletion();
(0, runLoop_1.markAcceptedResponseProcessingStarted)(state);
this.#captureResponseGuardrailBoundary(state, true);
const turnResult = await (0, turnResolution_1.resolveTurnAfterModelResponse)(state._currentAgent, state._originalInput, state._generatedItems, state._lastTurnResponse, state._lastProcessedResponse, this, state, toolErrorFormatter, agentToolParentRunConfig, options.errorHandlers, options.signal, suppressedToolCalls, attemptedRunErrorHandlers, (handoffAgent) => this.#validateModelTimeoutForAgent(handoffAgent));
if (turnResult.nextStep.type === 'next_step_handoff') {
this.#validateModelTimeoutForAgent(turnResult.nextStep.newAgent);
}
(0, runLoop_1.applyTurnResult)({
state,
turnResult,
agent: state._currentAgent,
toolsUsed: state._lastProcessedResponse?.toolsUsed ?? [],
resetTurnPersistence: !isResumedState,
});
if (options.signal?.aborted) {
const guardedFinalOutput = turnResult.nextStep.type === 'next_step_final_output' &&
this.#agentHasOutputGuardrail(state._currentAgent);
if (guardedFinalOutput && !isResumedState) {
// Ordinary callers cannot recover the runner-owned state from
// the original abort reason. Keep a replay-safe completion
// record without publishing the unchecked tool output.
(0, blockedOutputPersistence_1.redactCancelledResponseToolOutputs)(state, 'Tool output discarded because the run was cancelled before output validation.');
cancelledOutputRedacted = true;
}
if (!guardedFinalOutput || cancelledOutputRedacted) {
persistenceCheckpoint = new result_1.RunResult(state);
}
// Caller-owned RunState retains the candidate for validation on resume.
}
options.signal?.throwIfAborted();
if (turnResult.nextStep.type !== 'next_step_final_output') {
(0, tracing_1.finishRunnerSpan)(currentTurnSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(state, undefined);
currentTurnSpan = undefined;
}
}
const currentStep = state._currentStep;
if (!currentStep) {
logger_1.default.debug('Running next loop');
continue;
}
switch (currentStep.type) {
case 'next_step_final_output':
if (options.signal?.aborted) {
(0, sessionPersistence_1.releaseProvisionalSessionHistoryTransactionBinding)(state, provisionalSessionHistoryTransactionSessionId, provisionalSessionHistoryTransactionInputItems);
}
options.signal?.throwIfAborted();
return await finalizeCurrentOutput();
case 'next_step_handoff':
this.#validateModelTimeoutForAgent(currentStep.newAgent);
state.setCurrentAgent(currentStep.newAgent);
if (state._currentAgentSpan) {
state._currentAgentSpan.end();
(0, context_1.resetCurrentSpan)();
state.setCurrentAgentSpan(undefined);
}
state._noActiveAgentRun = true;
state._currentTurnInProgress = false;
// We've processed the handoff, so we need to run the loop again.
state._currentStep = { type: 'next_step_run_again' };
break;
case 'next_step_interruption':
// Interrupted. Don't run any guardrails.
return completeResult(new result_1.RunResult(state));
case 'next_step_run_again':
state._currentTurnInProgress = false;
logger_1.default.debug('Running next loop');
break;
default:
logger_1.default.debug('Running next loop');
}
}
}
catch (caughtError) {
if (guardrailTracker.pending) {
await guardrailTracker.awaitCompletion({ suppressErrors: true });
}
const err = guardrailTracker.failed
? guardrailTracker.error
: caughtError;
if (guardrailTracker.failed) {
(0, errorHandlers_1.invalidateAcceptedResponseReplayEvidence)(state);
}
const restoredPendingTurn = rollbackUnstartedTurn(state, turnPendingModelRequest);
turnPendingModelRequest = undefined;
if (!restoredPendingTurn) {
state._currentTurnInProgress = false;
}
(0, errorHandlers_1.attachRunStateToError)(err, state);
(0, sessionPersistence_1.releaseUnusedSessionHistoryTransactionBinding)(state);
const errorHandled = state._pendingSessionWrite
? undefined
: await (0, errorHandlers_1.prepareRunErrorFinalOutput)({
error: err,
state,
errorHandlers: options.errorHandlers,
responseAccepted: (0, runLoop_1.isAcceptedResponseCheckpoint)(state),
attemptedErrors: attemptedRunErrorHandlers,
});
if (errorHandled) {
try {
return await finalizeCurrentOutput(errorHandled);
}
catch (finalizationError) {
recordNonStreamingError(finalizationError);
throw finalizationError;
}
}
recordNonStreamingError(err);
throw err;
}
finally {
(0, tracing_1.finishRunnerSpan)(currentTurnSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(state, undefined);
const preserveSandboxSessions = state._currentStep?.type === 'next_step_interruption';
try {
try {
await (0, sandbox_1.finalizeSandboxRuntime)({
state: state,
sandboxRuntime,
preserveSessionsForInterruption: preserveSandboxSessions,
finishAgentSpanForInterruption: Boolean(taskSpan) || runError !== undefined,
runError,
groupId: this.config.groupId,
memoryContext: sandboxMemoryRunContext,
runAgent: async (agent, input, runOptions) => await this.run(agent, input, runOptions),
tracingParent: taskSpan?.span ??
state._currentAgentSpan ??
invocationSpanParent,
});
}
catch (error) {
(0, tracing_1.setRunnerSpanError)(taskSpan, error, this.config.traceIncludeSensitiveData);
await Promise.reject(error);
}
const resultToPersist = completedResult ?? persistenceCheckpoint;
if (resultToPersist && !completedResultPersisted) {
try {
await persistNonStreamingResult(resultToPersist,
// The current response is already redacted; append the full
// suffix so earlier completed turns remain replayable.
cancelledOutputRedacted ? { runCompaction: false } : undefined);
}
catch (error) {
(0, tracing_1.setRunnerSpanError)(taskSpan, error, this.config.traceIncludeSensitiveData);
await Promise.reject(error);
}
}
}
finally {
(0, tracing_1.finishRunnerSpan)(taskSpan);
}
}
});
}
/**
* @internal
*/
async #runStreamLoop(result, startingAgent, sandboxRuntime, options, isResumedState, ensureStreamInputPersisted, sessionTurnInputUpdate, sessionInputUpdate, getStreamInputForPersistence, preserveTurnPersistenceOnResume, provisionalSessionHistoryTransactionSessionId, provisionalSessionHistoryTransactionInputItems, sandboxMemoryRunContext, taskSpan, invocationSpanParent, pendingSessionWriteReconciled = false) {
const resolvedReasoningItemIdPolicy = options.reasoningItemIdPolicy ??
(isResumedState ? result.state._reasoningItemIdPolicy : undefined) ??
this.config.reasoningItemIdPolicy;
result.state.setReasoningItemIdPolicy(resolvedReasoningItemIdPolicy);
const resolvedConversationId = options.conversationId ?? result.state._conversationId;
const resolvedPreviousResponseId = options.previousResponseId ?? result.state._previousResponseId;
const serverManagesConversation = Boolean(resolvedConversationId) || Boolean(resolvedPreviousResponseId);
assertPendingInputServerOwnership(result.state, resolvedConversationId, resolvedPreviousResponseId);
const serverConversationTracker = serverManagesConversation
? new conversation_1.ServerConversationTracker({
conversationId: resolvedConversationId,
previousResponseId: resolvedPreviousResponseId,
reasoningItemIdPolicy: resolvedReasoningItemIdPolicy,
})
: undefined;
if (serverConversationTracker) {
result.state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
}
let sentInputToModel = false;
let streamInputPersisted = false;
let guardrailTracker = (0, guardrails_1.createGuardrailTracker)();
const persistStreamInputIfNeeded = async () => {
if (streamInputPersisted || !ensureStreamInputPersisted) {
return;
}
// Both success and error paths call this helper, so guard against multiple writes.
await ensureStreamInputPersisted();
streamInputPersisted = true;
(0, sessionPersistence_1.markSessionHistoryTransactionInputPersisted)(result.state);
};
const awaitInputGuardrails = async () => {
await guardrailTracker.awaitCompletion();
if (guardrailTracker.failed) {
throw guardrailTracker.error;
}
};
if (serverConversationTracker && isResumedState) {
serverConversationTracker.primeFromState({
originalInput: result.state._originalInput,
generatedItems: result.state._generatedItems,
modelResponses: result.state._modelResponses,
});
result.state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
}
const toolErrorFormatter = options.toolErrorFormatter ?? this.config.toolErrorFormatter;
const outputGuardrailBlockedMessage = options.outputGuardrailBlockedMessage === undefined
? this.config.outputGuardrailBlockedMessage
: options.outputGuardrailBlockedMessage;
const agentToolParentRunConfig = this.#getAgentToolParentRunConfig(options);
const useTaskAndTurnSpans = !this.config.tracingDisabled && (0, config_1.includeTaskAndTurnSpans)(options.tracing);
// Tracks when we resume an approval interruption so the next run-again step stays in the same turn.
let continuingInterruptedTurn = false;
let runError;
const attemptedRunErrorHandlers = new WeakSet();
let suppressStreamInputPersistence = false;
const resumedStateHasPersistedToolOutput = isResumedState && (0, blockedOutputPersistence_1.hasPersistedToolOutput)(result.state);
let approvedToolCheckpointCompacted = pendingSessionWriteReconciled ||
result.state._currentTurnSessionWriteCompactedItemCount ===
result.state._currentTurnPersistedItemCount ||
(resumedStateHasPersistedToolOutput &&
!(0, blockedOutputPersistence_1.hasPendingApprovedToolInputCompaction)(result.state));
let approvedToolCheckpointRequiresLocalInputCompaction = resumedStateHasPersistedToolOutput;
let approvedToolCheckpointModelResponseCount = result.rawResponses.length;
let approvedToolFinalSessionWritePreparation;
let deferredApprovedToolFinalSessionWritePreparation;
let currentTurnSpan;
let turnPendingModelRequest;
let commitDeferredLocalPendingInput;
const parentUsageRecorder = (0, usageTracking_1.getRunnerParentUsageRecorder)(this);
const recordUsage = (usage) => {
(0, tracing_1.recordRunnerSpanUsage)(taskSpan, usage);
(0, tracing_1.recordRunnerSpanUsage)(currentTurnSpan, usage);
parentUsageRecorder?.(usage);
};
(0, usageTracking_1.setRunStateUsageRecorder)(result.state, recordUsage);
const saveStreamResultWithCompactionOwnership = async (overrideOptions) => {
if (this.#shouldDeferInterruptedSessionItems(result.state)) {
return;
}
const hasUnpersistedItems = result.newItems.length > result.state._currentTurnPersistedItemCount ||
(overrideOptions?.additionalRunItems?.length ?? 0) > 0;
const modelResponseAdvanced = result.rawResponses.length > approvedToolCheckpointModelResponseCount;
const compactionOptions = approvedToolCheckpointRequiresLocalInputCompaction
? approvedToolCheckpointCompacted &&
!hasUnpersistedItems &&
!modelResponseAdvanced
? { runCompaction: false }
: { compactionMode: 'input' }
: undefined;
const persistenceOptions = {
...compactionOptions,
...overrideOptions,
};
const finalSessionWritePreparation = approvedToolFinalSessionWritePreparation;
const sessionInputItems = streamInputPersisted
? undefined
: getStreamInputForPersistence?.();
// A remote session may commit addItems before rejecting, and compaction happens after the
// append. Once the combined save starts, retrying the input is therefore unsafe.
streamInputPersisted = true;
await (0, sessionPersistence_1.saveStreamResultToSession)(options.session, result, {
...persistenceOptions,
resumedSessionWritePreparation: finalSessionWritePreparation ??
persistenceOptions.resumedSessionWritePreparation,
}, sessionInputItems);
if (finalSessionWritePreparation) {
approvedToolFinalSessionWritePreparation = undefined;
if (persistenceOptions.deferResumedSessionWrite !== true) {
approvedToolCheckpointCompacted = true;
approvedToolCheckpointModelResponseCount = result.rawResponses.length;
}
}
};
const activateDeferredApprovedToolFinalSessionWrite = () => {
const preparation = deferredApprovedToolFinalSessionWritePreparation;
if (!preparation) {
return;
}
if (result.state._pendingSessionHistoryTransaction !== undefined ||
result.state._currentTurnDeferredSessionItemIndexes.size > 0 ||
result.state._currentTurnBlockedSessionStartIndex !== undefined) {
deferredApprovedToolFinalSessionWritePreparation = undefined;
return;
}
if (!options.session || serverManagesConversation) {
throw new errors_1.UserError('Resumed Session write preparation has no ordinary Session owner.');
}
deferredApprovedToolFinalSessionWritePreparation = undefined;
approvedToolFinalSessionWritePreparation = preparation;
};
const recordStreamingError = (error) => {
if (result.state._currentAgentSpan) {
result.state._currentAgentSpan.setError({
message: 'Error in agent run',
data: {
error: (0, tracing_1.getRunnerSpanErrorDetails)(error, this.config.traceIncludeSensitiveData),
},
});
}
(0, tracing_1.setRunnerSpanError)(currentTurnSpan, error, this.config.traceIncludeSensitiveData);
(0, tracing_1.setRunnerSpanError)(taskSpan, error, this.config.traceIncludeSensitiveData);
runError = error;
};
const finalizeStreamOutput = async (preparedErrorOutput) => {
const currentStep = result.state._currentStep;
if (currentStep?.type !== 'next_step_final_output') {
throw new errors_1.ModelBehaviorError('Expected a final output step while finalizing the run.', result.state);
}
(0, runLoop_1.markAcceptedResponseFinalizationStarted)(result.state);
result._hideFinalOutput();
(0, blockedOutputPersistence_1.normalizeSerializedOutputGuardrailBlockedMessage)(result.state);
const resolveBlockedMessage = (0, outputGuardrailBlockedMessage_1.createOutputGuardrailBlockedMessageResolver)(outputGuardrailBlockedMessage, result.state);
const outputGuardrailResultStart = result.state._outputGuardrailResults.length;
let outputGuardrailBlocked = false;
try {
await (0, guardrails_1.finalizeOutputGuardrails)({
state: result.state,
runnerOutputGuardrails: this.outputGuardrailDefs,
output: currentStep.output,
redactedOutput: outputGuardrailBlockedMessage_1.OUTPUT_GUARDRAIL_BLOCKED_TOOL_OUTPUT,
guardedTerminalToolOutput: (0, blockedOutputPersistence_1.hasTerminalToolOutputSource)(result.state),
signal: options.signal,
sanitizeRejectedOutput: (...args) => (0, blockedOutputPersistence_1.sanitizeBlockedTerminalToolOutput)(...args, resolveBlockedMessage, options.signal, () => {
outputGuardrailBlocked = true;
}),
persistBlockedOutput: !serverManagesConversation
? async () => saveStreamResultWithCompactionOwnership({
outputBlocked: true,
})
: undefined,
persistUnblockedFailure: !serverManagesConversation
? async () => {
activateDeferredApprovedToolFinalSessionWrite();
await saveStreamResultWithCompactionOwnership();
}
: undefined,
onBlockedOutput: () => {
outputGuardrailBlocked = true;
},
releaseBlockedOutputPersistence: sessionPersistence_1.releaseUnusedSessionHistoryTransactionBinding,
});
}
catch (error) {
if (!outputGuardrailBlocked &&
(deferredApprovedToolFinalSessionWritePreparation !== undefined ||
result.state._pendingSessionWrite !== undefined ||
approvedToolCheckpointCompacted)) {
result.state._outputGuardrailResults.splice(outputGuardrailResultStart);
if (deferredApprovedToolFinalSessionWritePreparation !== undefined &&
result.state._pendingSessionWrite === undefined) {
try {
activateDeferredApprovedToolFinalSessionWrite();
await saveStreamResultWithCompactionOwnership({
deferResumedSessionWrite: true,
});
}
catch (checkpointError) {
if (result.state._pendingSessionWrite === undefined) {
throw checkpointError;
}
}
}
}
throw error;
}
if (result.state._serializedCurrentStep === currentStep &&
hasRetainableBlockedOutputEffect(result.state)) {
(0, sessionPersistence_1.releaseProvisionalSessionHistoryTransactionBinding)(result.state, provisionalSessionHistoryTransactionSessionId, provisionalSessionHistoryTransactionInputItems);
throw new errors_1.UserError('Accepted final output cannot be resumed directly from serialized terminal state. Start a new run from persisted session history.');
}
activateDeferredApprovedToolFinalSessionWrite();
(0, tracing_1.finishRunnerSpan)(currentTurnSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(result.state, undefined);
currentTurnSpan = undefined;
result.state._currentTurnInProgress = false;
// Guardrails must succeed before persisting session memory to avoid storing blocked outputs.
if (!serverManagesConversation) {
try {
await saveStreamResultWithCompactionOwnership(preparedErrorOutput?.deferredItem
? { additionalRunItems: [preparedErrorOutput.deferredItem] }
: undefined);
}
catch (error) {
result.state._outputGuardrailResults.splice(outputGuardrailResultStart);
const itemCommitted = commitDeferredRunErrorItemAfterPartialPersistence(result.state, preparedErrorOutput);
if (itemCommitted && preparedErrorOutput?.deferredItem) {
(0, streaming_1.streamStepItemsToRunResult)(result, [
preparedErrorOutput.deferredItem,
]);
recordStreamingError(error);
result._raiseError(error, { preserveQueuedItems: true });
return;
}
throw error;
}
}
if (preparedErrorOutput?.deferredItem) {
result.state._generatedItems.push(preparedErrorOutput.deferredItem);
}
if (preparedErrorOutput?.deferredItem) {
(0, streaming_1.streamStepItemsToRunResult)(result, [preparedErrorOutput.deferredItem]);
result._preserveQueuedItemsOnError();
}
result._revealFinalOutput();
this.emit('agent_end', result.state._context, result.state._currentAgent, currentStep.output);
result.state._currentAgent.emit('agent_end', result.state._context, currentStep.output);
};
try {
while (true) {
// Let the current action batch settle, but never start new work after
// cancellation once a turn has already begun. Preserve the existing
// first-request behavior for an initially aborted stream.
if (result.cancelled &&
(result.state._currentTurn > 0 ||
result.state._currentStep?.type === 'next_step_final_output')) {
if (result.state._currentStep?.type === 'next_step_final_output') {
(0, sessionPersistence_1.releaseProvisionalSessionHistoryTransactionBinding)(result.state, provisionalSessionHistoryTransactionSessionId, provisionalSessionHistoryTransactionInputItems);
}
return;
}
const currentAgent = result.state._currentAgent;
result.state._currentStep = result.state._currentStep ?? {
type: 'next_step_run_again',
};
if ((0, runLoop_1.isAcceptedResponseCheckpoint)(result.state)) {
this.#captureResponseGuardrailBoundary(result.state, false);
await (0, runLoop_1.resumeAcceptedModelResponse)({
state: result.state,
runner: this,
toolErrorFormatter,
agentToolParentRunConfig,
signal: options.signal,
validateHandoffAgent: (handoffAgent) => {
this.#validateModelTimeoutForAgent(handoffAgent);
},
onStepItems: (turnResult) => {
(0, streaming_1.addStepToRunResult)(result, turnResult);
},
});
}
if (result.state._currentStep.type === 'next_step_interruption') {
await (0, sandbox_1.prepareSandboxInterruptedTurnResume)({
startingAgent,
state: result.state,
sandboxRuntime,
runConfigModel: await this.#resolveSandboxRuntimeModelForAgent(result.state._currentAgent),
toolNameCollisionPolicy: options.toolNameCollisionPolicy,
tracingParent: (0, invocationContext_1.getRunStateTurnSpanParent)(result.state) ??
result.state._currentAgentSpan,
});
if (useTaskAndTurnSpans) {
currentTurnSpan = (0, tracing_1.ensureTurnSpan)(currentTurnSpan, result.state._currentTurn, result.state._currentAgent.name, result.state._currentAgentSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(result.state, currentTurnSpan.span);
}
const interruptedOutcome = await (async () => {
let resumedSessionWritePreparation;
this.#captureResponseGuardrailBoundary(result.state, false);
const outcome = await (0, runLoop_1.resumeInterruptedTurn)({
state: result.state,
runner: this,
toolErrorFormatter,
agentToolParentRunConfig,
signal: options.signal,
validateHandoffAgent: (handoffAgent) => {
this.#validateModelTimeoutForAgent(handoffAgent);
},
onStepItems: (turnResult) => {
(0, streaming_1.addStepToRunResult)(result, turnResult);
},
onHandoff: (agent) => {
result._addItem(new events_1.RunAgentUpdatedStreamEvent(agent));
},
beforeResumedSideEffects: async () => {
if (options.session && !serverManagesConversation) {
resumedSessionWritePreparation =
await (0, sessionPersistence_1.prepareResumedSessionWrite)(options.session, result.state);
}
return resumedSessionWritePreparation;
},
});
if (outcome.resumedSideEffects) {
const checkpointDeferred = outcome.nextStep.type === 'next_step_final_output' &&
this.#agentHasOutputGuardrail(result.state._currentAgent);
approvedToolCheckpointRequiresLocalInputCompaction = true;
if (!serverManagesConversation &&
options.session &&
!this.#shouldDeferInterruptedSessionItems(result.state)) {
if (!resumedSessionWritePreparation) {
throw new errors_1.UserError('Resumed Session write preparation is missing after approved tool execution.');
}
if (checkpointDeferred) {
deferredApprovedToolFinalSessionWritePreparation =
resumedSessionWritePreparation;
}
else if (outcome.nextStep.type === 'next_step_final_output') {
approvedToolFinalSessionWritePreparation =
resumedSessionWritePreparation;
}
else {
await (0, sessionPersistence_1.saveStreamResultToSession)(options.session, result, {
compactionMode: 'input',
resumedSessionWritePreparation,
});
approvedToolCheckpointCompacted = true;
approvedToolCheckpointModelResponseCount =
result.rawResponses.length;
}
}
}
return outcome;
})();
// Don't reset counter here - resolveInterruptedTurn already adjusted it via rewind logic
// The counter will be reset when _currentTurn is incremented (starting a new turn)
const { shouldReturn, shouldContinue } = (0, runLoop_1.handleInterruptedOutcome)({
state: result.state,
outcome: interruptedOutcome,
setContinuingInterruptedTurn: (value) => {
continuingInterruptedTurn = value;
},
});
// Approval resolution belongs to the previous turn; any next model call
// starts a new counted turn with its own span and usage.
(0, tracing_1.finishRunnerSpan)(currentTurnSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(result.state, undefined);
currentTurnSpan = undefined;
if (shouldReturn) {
// we are still in an interruption, so we need to avoid an infinite loop
return;
}
if (shouldContinue) {
continue;
}
}
if (result.state._currentStep.type === 'next_step_run_again') {
this.#validateModelTimeoutForAgent(result.state._currentAgent);
commitDeferredLocalPendingInput = undefined;
if (approvedToolCheckpointCompacted &&
result.state._currentTurnSessionHistoryTransactionSessionId ===
undefined) {
await (0, sessionPersistence_1.prepareSessionHistoryTransactionsForRun)(options.session, result.state, { serverManagesConversation: false });
}
guardrailTracker = (0, guardrails_1.createGuardrailTracker)();
const wasContinuingInterruptedTurn = continuingInterruptedTurn;
continuingInterruptedTurn = false;
const previousTurn = result.state._currentTurn;
turnPendingModelRequest = {
currentTurn: previousTurn,
currentTurnInProgress: result.state._currentTurnInProgress,
};
const previousPersistedCount = result.state._currentTurnPersistedItemCount;
const previousGeneratedCount = result.state._generatedItems.length;
const preparedTurn = await (0, turnPreparation_1.prepareTurn)({
state: result.state,
input: result.input,
generatedItems: result.newItems,
isResumedState,
preserveTurnPersistenceOnResume,
continuingInterruptedTurn: wasContinuingInterruptedTurn,
serverConversationTracker,
inputGuardrailDefs: this.inputGuardrailDefs,
guardrailHandlers: {
onParallelPromise: guardrailTracker.setPromise,
onParallelError: (err) => {
guardrailTracker.setError(err);
},
},
emitAgentStart: (context, agent, inputItems) => {
this.emit('agent_start', context, agent, inputItems);
},
onAgentSpanReady: useTaskAndTurnSpans
? (turn, agentName) => {
currentTurnSpan = (0, tracing_1.ensureTurnSpan)(currentTurnSpan, turn, agentName, result.state._currentAgentSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(result.state, currentTurnSpan.span);
}
: undefined,
agentSpanParent: taskSpan?.span ?? invocationSpanParent,
});
if (preserveTurnPersistenceOnResume &&
result.state._currentTurn > previousTurn &&
previousPersistedCount <= previousGeneratedCount) {
// Preserve persisted offsets from a resumed run to avoid re-saving prior items.
result.state._currentTurnPersistedItemCount =
previousPersistedCount;
}
const { turnInput, pendingInputItems, pendingInputSourceItems } = preparedTurn;
// If guardrails are still running, defer input persistence until they finish.
const sessionPreparedTurnInput = [...turnInput];
const preparedSandboxAgent = await sandboxRuntime.prepareAgent({
currentAgent: result.state._currentAgent,
turnInput,
runConfigModel: await this.#resolveSandboxRuntimeModelForAgent(result.state._currentAgent),
tracingParent: currentTurnSpan?.span ?? result.state._currentAgentSpan,
});
sessionTurnInputUpdate?.(sessionPreparedTurnInput, preparedSandboxAgent.turnInput);
const processedPendingInputItems = (0, pendingInput_1.mapPendingInputAfterContextProcessing)(pendingInputItems, sessionPreparedTurnInput, preparedSandboxAgent.turnInput);
const artifacts = await (0, modelPreparation_1.prepareAgentArtifacts)(result.state, preparedSandboxAgent.executionAgent, options.toolNameCollisionPolicy);
const preparedCall = await this.#prepareModelCall(result.state, preparedSandboxAgent.executionAgent, options, artifacts, preparedSandboxAgent.turnInput, serverConversationTracker, sessionInputUpdate);
(0, sessionPersistence_1.captureSessionHistoryTransactionInputItems)(options.session, result.state, getStreamInputForPersistence?.());
await guardrailTracker.throwIfError();
// Once a logical turn is established, do not start another model
// request if cancellation arrives during asynchronous preparation.
if ((sentInputToModel || isResumedState) && result.cancelled) {
rollbackUnstartedTurn(result.state, turnPendingModelRequest);
turnPendingModelRequest = undefined;
return;
}
const admittedPendingInput = (0, pendingInput_1.selectPendingInputForAdmission)(processedPendingInputItems, preparedCall);
let localPendingInputCommitted = false;
const commitLocalPendingInput = async () => {
if (serverConversationTracker || localPendingInputCommitted) {
return;
}
localPendingInputCommitted = true;
(0, pendingInput_1.commitPendingInput)(result.state, pendingInputItems, admittedPendingInput, pendingInputSourceItems);
if ((0, pendingInput_1.hasUnpersistedRunInput)(result.state)) {
await (0, sessionPersistence_1.saveStreamResultToSession)(options.session, result, {
runCompaction: false,
});
}
};
const deferLocalPendingInputAdmission = !serverConversationTracker &&
pendingInputItems.length > 0 &&
guardrailTracker.pending;
if (deferLocalPendingInputAdmission) {
commitDeferredLocalPendingInput = async () => {
commitDeferredLocalPendingInput = undefined;
await commitLocalPendingInput();
};
}
else {
await commitLocalPendingInput();
if ((sentInputToModel || isResumedState) && result.cancelled) {
rollbackUnstartedTurn(result.state, turnPendingModelRequest);
turnPendingModelRequest = undefined;
return;
}
}
let finalResponse = undefined;
const abortReconciliationState = (0, streamReconciliation_1.createStreamAbortReconciliationState)();
let inputMarked = false;
let responseAcceptedCheckpointed = false;
let streamEventCount = 0;
let lastStreamEventType;
let timedOutModelCallFailed = false;
const markServerInputAccepted = (responseAvailable = true) => {
if (!serverConversationTracker) {
return;
}
if (!inputMarked) {
// Mark inputs after the first stream event, an explicit abort after
// request start, or provider advice that the failed request may have
// been accepted.
// Record the exact input that was sent so the server tracker can advance safely.
serverConversationTracker.markInputAsSent(preparedCall.filterApplied
? preparedCall.sourceItems
: preparedCall.turnInput, {
filterApplied: preparedCall.filterApplied,
allTurnItems: preparedCall.turnInput,
});
(0, pendingInput_1.commitPendingInput)(result.state, pendingInputItems, admittedPendingInput, pendingInputSourceItems);
if (pendingInputItems.length > 0) {
if (admittedPendingInput.length > 0) {
serverConversationTracker.markInputAsSent(admittedPendingInput.map((item) => item.rawItem));
}
}
inputMarked = true;
}
if (!responseAcceptedCheckpointed &&
(pendingInputItems.length > 0 || !responseAvailable)) {
if (!responseAvailable) {
result.state._lastTurnResponse = undefined;
}
result.state._lastProcessedResponse = undefined;
result.state._currentStep = {
type: 'next_step_interruption',
data: { interruptions: [], responseAccepted: true },
};
responseAcceptedCheckpointed = true;
}
};
const reconcileStreamAbortIfNeeded = async () => {
if (!serverConversationTracker ||
!(0, streamReconciliation_1.shouldReconcileStreamAbort)(abortReconciliationState)) {
return;
}
const reconciliationInput = (0, streamReconciliation_1.buildAbortReconciliationInput)(abortReconciliationState);
try {
const reconciliationResponse = await (0, modelRetry_1.getResponseWithRetry)(preparedCall.model, {
systemInstructions: preparedCall.modelInput.instructions,
prompt: preparedCall.prompt,
...(preparedCall.explicitlyModelSet
? { overridePromptModel: true }
: {}),
input: reconciliationInput,
previousResponseId: (0, streamReconciliation_1.getAbortReconciliationPreviousResponseId)(abortReconciliationState, preparedCall),
conversationId: preparedCall.conversationId,
modelSettings: preparedCall.modelSettings,
_internal: preparedCall.modelRequestInternal,
tools: preparedCall.serializedTools,
toolsExplicitlyProvided: preparedCall.toolsExplicitlyProvided,
handoffs: preparedCall.serializedHandoffs,
outputType: (0, tools_1.convertAgentOutputTypeToSerializable)(currentAgent.outputType),
tracing: (0, tracing_1.getTracing)(this.config.tracingDisabled, this.config.traceIncludeSensitiveData),
}, {
onModelFailureUsage: (usage) => recordModelFailureUsage(usage, result.state, recordUsage),
});
(0, streamReconciliation_1.markAbortReconciliationComplete)(abortReconciliationState, reconciliationResponse);
result.state._context.usage.add(reconciliationResponse.usage);
recordUsage(reconciliationResponse.usage);
serverConversationTracker.trackServerItems(reconciliationResponse);
result.state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
}
catch (error) {
(0, logger_1.logModelAndToolActionDebug)(logger_1.default, 'Failed to reconcile streamed tool calls after abort.', error);
}
};
const modelRequest = {
systemInstructions: preparedCall.modelInput.instructions,
prompt: preparedCall.prompt,
// Streaming requests should also honor explicitly chosen models.
...(preparedCall.explicitlyModelSet
? { overridePromptModel: true }
: {}),
input: preparedCall.modelInput.input,
previousResponseId: preparedCall.previousResponseId,
conversationId: preparedCall.conversationId,
modelSettings: preparedCall.modelSettings,
_internal: preparedCall.modelRequestInternal,
tools: preparedCall.serializedTools,
toolsExplicitlyProvided: preparedCall.toolsExplicitlyProvided,
handoffs: preparedCall.serializedHandoffs,
outputType: (0, tools_1.convertAgentOutputTypeToSerializable)(currentAgent.outputType),
tracing: (0, tracing_1.getTracing)(this.config.tracingDisabled, this.config.traceIncludeSensitiveData),
signal: options.signal,
};
// Publish the turn only after the complete request is constructed,
// immediately before the model request starts.
turnPendingModelRequest = undefined;
result.currentTurn = result.state._currentTurn;
sentInputToModel = true;
const completeCompactionExchange = (0, sessionPersistence_1.prepareSessionCompactionExchange)(options.session, result.state, preparedCall.modelInput.input);
try {
for await (const event of (0, modelRetry_1.getStreamedResponseWithRetry)(preparedCall.model, modelRequest, {
onModelFailureUsage: (usage) => recordModelFailureUsage(usage, result.state, recordUsage),
onModelTimeout: () => {
timedOutModelCallFailed = true;
},
...(serverConversationTracker
? {
onPossiblyAcceptedRequestFailure: () => markServerInputAccepted(false),
}
: {}),
})) {
streamEventCount++;
lastStreamEventType = event.type;
markServerInputAccepted(pendingInputItems.length === 0);
await guardrailTracker.throwIfError();
(0, streamReconciliation_1.recordStreamEventForAbortReconciliation)(abortReconciliationState, event);
if (event.type === 'response_done') {
(0, items_1.assertValidCompactionItems)(event.response.output);
let rawUsage;
if (modelRequest.modelSettings.preserveRawUsage === true) {
try {
rawUsage = (0, rawUsage_1.snapshotRawUsage)(event.response.rawUsage);
}
catch {
rawUsage = undefined;
}
}
const parsed = protocol_1.StreamEventResponseCompleted.parse({
type: event.type,
...(event.providerData
? { providerData: event.providerData }
: {}),
response: {
id: event.response.id,
requestId: event.response.requestId,
usage: event.response.usage,
output: event.response.output,
...(event.response.providerData
? { providerData: event.response.providerData }
: {}),
...(rawUsage !== undefined ? { rawUsage } : {}),
},
});
finalResponse = {
usage: new usage_1.Usage(parsed.response.usage),
output: parsed.response.output,
responseId: parsed.response.id,
requestId: parsed.response.requestId,
...(rawUsage !== undefined ? { rawUsage } : {}),
};
result.state._lastTurnResponse = finalResponse;
result.state._context.usage.add(finalResponse.usage);
recordUsage(finalResponse.usage);
}
if (result.cancelled) {
// When the user's code exits a loop to consume the stream, we need to break
// this loop to prevent internal false errors and unnecessary processing
await awaitInputGuardrails();
await commitDeferredLocalPendingInput?.();
await reconcileStreamAbortIfNeeded();
return;
}
result._addItem(new events_1.RunRawModelStreamEvent(event));
}
}
catch (error) {
if ((0, streaming_1.isAbortError)(error)) {
if (sentInputToModel) {
markServerInputAccepted();
}
await awaitInputGuardrails();
await commitDeferredLocalPendingInput?.();
await reconcileStreamAbortIfNeeded();
return;
}
if (error instanceof errors_1.ModelTimeoutError || timedOutModelCallFailed) {
if (streamEventCount > 0) {
markServerInputAccepted(false);
}
await awaitInputGuardrails();
await commitDeferredLocalPendingInput?.();
await reconcileStreamAbortIfNeeded();
}
throw error;
}
if (finalResponse) {
markServerInputAccepted();
}
await awaitInputGuardrails();
await commitDeferredLocalPendingInput?.();
if (result.cancelled) {
return;
}
result.state._noActiveAgentRun = false;
if (!finalResponse) {
const lastEventKind = protocol_1.StreamEvent.options.find((schema) => schema.shape.type.value === lastStreamEventType)?.shape.type.value ??
(streamEventCount > 0 ? 'unknown' : 'none');
throw new errors_1.ModelBehaviorError(`Model did not produce a final response! Stream ended before response_done (event_count=${streamEventCount}, last_event_type=${lastEventKind}).`, result.state);
}
result.state._lastTurnResponse = finalResponse;
completeCompactionExchange(finalResponse.output, finalResponse.responseId);
// Keep the tracker in sync with the streamed response so reconnections remain accurate.
serverConversationTracker?.trackServerItems(finalResponse);
if (serverConversationTracker) {
result.state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
}
result.state._modelResponses.push(result.state._lastTurnResponse);
const processedResponse = await (0, modelOutputs_1.processModelResponseAsync)(result.state._lastTurnResponse, currentAgent, preparedCall.tools, preparedCall.handoffs, result.state,
// Loading authority retains the agent ownership lost in model input.
[
...(Array.isArray(result.state._originalInput)
? result.state._originalInput
: []),
...result.state._generatedItems,
], options.toolNotFoundBehavior, {
allowPromptSuppliedTools: preparedCall.allowPromptSuppliedTools,
beforeClientToolSearch: () => (0, turnResolution_1.preflightModelResponseToolInvocations)(currentAgent, result.state, result.state._lastTurnResponse, preparedCall.tools, preparedCall.handoffs, this, options.signal),
});
serverConversationTracker?.trackServerItems(result.state._lastTurnResponse, processedResponse.newItems);
result.state._lastProcessedResponse = processedResponse;
(0, modelPreparation_1.trackDeferredToolRecovery)(result.state, currentAgent, processedResponse);
const suppressedToolCalls = (0, turnResolution_1.preflightToolInvocations)(currentAgent, result.state, processedResponse);
const streamableItems = (0, turnResolution_1.filterSuppressedToolCallItems)(processedResponse.newItems, suppressedToolCalls);
// Record the items emitted directly from the model response so we do not
// stream them again after tools and other side effects finish.
const preToolItems = new Set(streamableItems);
if (preToolItems.size > 0) {
(0, streaming_1.streamStepItemsToRunResult)(result, streamableItems);
}
(0, runLoop_1.markAcceptedResponseProcessingStarted)(result.state);
this.#captureResponseGuardrailBoundary(result.state, true);
const turnResult = await (0, turnResolution_1.resolveTurnAfterModelResponse)(currentAgent, result.state._originalInput, result.state._generatedItems, result.state._lastTurnResponse, result.state._lastProcessedResponse, this, result.state, toolErrorFormatter, agentToolParentRunConfig, options.errorHandlers, options.signal, suppressedToolCalls, attemptedRunErrorHandlers, (handoffAgent) => this.#validateModelTimeoutForAgent(handoffAgent));
if (turnResult.nextStep.type === 'next_step_handoff') {
this.#validateModelTimeoutForAgent(turnResult.nextStep.newAgent);
}
(0, runLoop_1.applyTurnResult)({
state: result.state,
turnResult,
agent: currentAgent,
toolsUsed: processedResponse.toolsUsed,
resetTurnPersistence: !isResumedState,
onStepItems: (step) => {
(0, streaming_1.addStepToRunResult)(result, step, { skipItems: preToolItems });
},
});
if (turnResult.nextStep.type !== 'next_step_final_output') {
(0, tracing_1.finishRunnerSpan)(currentTurnSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(result.state, undefined);
currentTurnSpan = undefined;
}
}
const currentStep = result.state._currentStep;
switch (currentStep.type) {
case 'next_step_final_output':
await finalizeStreamOutput();
return;
case 'next_step_interruption':
// We are done for now. Don't run any output guardrails.
if (!serverManagesConversation) {
await saveStreamResultWithCompactionOwnership();
}
return;
case 'next_step_handoff':
this.#validateModelTimeoutForAgent(currentStep.newAgent);
result.state.setCurrentAgent(currentStep.newAgent);
if (result.state._currentAgentSpan) {
result.state._currentAgentSpan.end();
(0, context_1.resetCurrentSpan)();
}
result.state.setCurrentAgentSpan(undefined);
result._addItem(new events_1.RunAgentUpdatedStreamEvent(result.state._currentAgent));
result.state._noActiveAgentRun = true;
result.state._currentTurnInProgress = false;
// We've processed the handoff, so we need to run the loop again.
result.state._currentStep = {
type: 'next_step_run_again',
};
break;
case 'next_step_run_again':
result.state._currentTurnInProgress = false;
logger_1.default.debug('Running next loop');
break;
default:
logger_1.default.debug('Running next loop');
}
}
}
catch (caughtError) {
if (guardrailTracker.pending) {
await guardrailTracker.awaitCompletion({ suppressErrors: true });
}
if (!guardrailTracker.failed) {
await commitDeferredLocalPendingInput?.();
}
const error = guardrailTracker.failed
? guardrailTracker.error
: caughtError;
const restoredPendingTurn = rollbackUnstartedTurn(result.state, turnPendingModelRequest);
turnPendingModelRequest = undefined;
if (!restoredPendingTurn) {
result.state._currentTurnInProgress = false;
}
(0, errorHandlers_1.attachRunStateToError)(error, result.state);
(0, sessionPersistence_1.releaseUnusedSessionHistoryTransactionBinding)(result.state);
suppressStreamInputPersistence =
error instanceof items_1.CompactionItemValidationError;
const errorHandled = result.state._pendingSessionWrite
? undefined
: await (0, errorHandlers_1.prepareRunErrorFinalOutput)({
error,
state: result.state,
errorHandlers: options.errorHandlers,
streamResult: result,
responseAccepted: (0, runLoop_1.isAcceptedResponseCheckpoint)(result.state),
attemptedErrors: attemptedRunErrorHandlers,
});
if (errorHandled) {
try {
await finalizeStreamOutput(errorHandled);
return;
}
catch (finalizationError) {
recordStreamingError(finalizationError);
throw finalizationError;
}
}
recordStreamingError(error);
throw error;
}
finally {
(0, tracing_1.finishRunnerSpan)(currentTurnSpan);
(0, invocationContext_1.setRunStateTurnSpanParent)(result.state, undefined);
if (guardrailTracker.pending) {
await guardrailTracker.awaitCompletion({ suppressErrors: true });
}
if (sentInputToModel &&
!streamInputPersisted &&
!guardrailTracker.failed &&
!suppressStreamInputPersistence) {
await persistStreamInputIfNeeded();
}
const preserveSandboxSessions = result.state._currentStep?.type === 'next_step_interruption' ||
(result.cancelled &&
result.state._currentStep?.type !== 'next_step_final_output' &&
runError === undefined);
try {
try {
await (0, sandbox_1.finalizeSandboxRuntime)({
state: result.state,
sandboxRuntime,
preserveSessionsForInterruption: preserveSandboxSessions,
finishAgentSpanForInterruption: Boolean(taskSpan) || runError !== undefined,
runError,
groupId: this.config.groupId,
memoryContext: sandboxMemoryRunContext,
runAgent: async (agent, input, runOptions) => await this.run(agent, input, runOptions),
tracingParent: taskSpan?.span ??
result.state._currentAgentSpan ??
invocationSpanParent,
});
}
catch (error) {
(0, tracing_1.setRunnerSpanError)(taskSpan, error, this.config.traceIncludeSensitiveData);
await Promise.reject(error);
}
}
finally {
(0, tracing_1.finishRunnerSpan)(taskSpan);
}
}
}
/**
* @internal
*/
async #runIndividualStream(agent, input, taskSpanName, options, ensureStreamInputPersisted, sessionTurnInputUpdate, sessionInputUpdate, getStreamInputForPersistence, preserveTurnPersistenceOnResume, provisionalSessionHistoryTransactionSessionId, provisionalSessionHistoryTransactionInputItems, sandboxMemoryRunContext, invocationSpanParent, pendingSessionWriteReconciled = false, sessionCompaction) {
options = options ?? {};
return (0, context_1.withNewSpanContext)(async () => {
// Initialize or reuse existing state
const isResumedState = input instanceof runState_1.RunState;
const state = isResumedState
? input
: new runState_1.RunState(options.context instanceof runContext_1.RunContext
? options.context
: new runContext_1.RunContext(options.context), input, agent, options.maxTurns === undefined
? constants_1.DEFAULT_MAX_TURNS
: options.maxTurns);
(0, sessionPersistence_1.bindSessionCompactionState)(state, sessionCompaction);
this.#validateModelTimeoutForAgent(state._currentAgent);
if (isResumedState) {
state._agentToolInvocation = undefined;
if (options.maxTurns !== undefined) {
state._maxTurns = options.maxTurns;
}
}
const resolvedReasoningItemIdPolicy = options.reasoningItemIdPolicy ??
(isResumedState ? state._reasoningItemIdPolicy : undefined) ??
this.config.reasoningItemIdPolicy;
state.setReasoningItemIdPolicy(resolvedReasoningItemIdPolicy);
const resolvedConversationId = options.conversationId ??
(isResumedState ? state._conversationId : undefined);
const resolvedPreviousResponseId = options.previousResponseId ??
(isResumedState ? state._previousResponseId : undefined);
if (!isResumedState) {
await (0, sessionPersistence_1.prepareSessionHistoryTransactionsForRun)(options.session, state, {
serverManagesConversation: Boolean(resolvedConversationId || resolvedPreviousResponseId),
});
}
const useTaskAndTurnSpans = !this.config.tracingDisabled &&
(0, config_1.includeTaskAndTurnSpans)(options.tracing);
const resumingInterruptedTurn = isResumedState && state._currentStep?.type === 'next_step_interruption';
const invocationSpans = useTaskAndTurnSpans
? (0, tracing_1.startRunnerInvocationSpans)({
name: taskSpanName,
agent: state._currentAgent,
restoredAgentSpan: isResumedState
? state._currentAgentSpan
: undefined,
resumeInterruptedTurn: resumingInterruptedTurn,
parent: invocationSpanParent,
})
: undefined;
const taskSpan = invocationSpans?.taskSpan;
const optOutResumeAgentSpan = resumingInterruptedTurn && !useTaskAndTurnSpans
? (0, tracing_1.ensureActiveAgentSpanForInterruptedResume)({
agent: state._currentAgent,
restoredAgentSpan: isResumedState
? state._currentAgentSpan
: undefined,
parent: invocationSpanParent ?? (0, context_1.getCurrentTrace)() ?? undefined,
})
: undefined;
if (useTaskAndTurnSpans && isResumedState) {
state.setCurrentAgentSpan(invocationSpans?.agentSpan);
}
else if (optOutResumeAgentSpan) {
state.setCurrentAgentSpan(optOutResumeAgentSpan);
}
const sandboxRuntime = new runtime_1.SandboxRuntimeManager({
startingAgent: agent,
sandboxConfig: options.sandbox ?? this.config.sandbox,
runState: isResumedState
? state
: undefined,
});
if (!isResumedState) {
state.setConversationContext(resolvedConversationId, resolvedPreviousResponseId);
}
// Initialize the streamed result with existing state
const result = new result_1.StreamedRunResult({
signal: options.signal,
state,
});
result._setAgentToolStreamBuffer((0, agentToolStream_1.getAgentToolStreamBuffer)(this));
const streamOptions = {
...options,
signal: result._getAbortSignal(),
};
// Setup defaults
result.maxTurns = state._maxTurns;
// Continue the stream loop without blocking
const streamLoopPromise = this.#runStreamLoop(result, agent, sandboxRuntime, streamOptions, isResumedState, ensureStreamInputPersisted, sessionTurnInputUpdate, sessionInputUpdate, getStreamInputForPersistence, preserveTurnPersistenceOnResume, provisionalSessionHistoryTransactionSessionId, provisionalSessionHistoryTransactionInputItems, sandboxMemoryRunContext, taskSpan, invocationSpanParent, pendingSessionWriteReconciled).then(() => {
result._done();
}, (err) => {
result._raiseError(err);
});
// Attach the stream loop promise so trace end waits for the loop to complete
result._setStreamLoopPromise(streamLoopPromise);
return result;
});
}
/**
* @internal
* Validates timeout settings before turn preparation can run hooks, persist
* input, or initialize sandbox resources.
*/
#validateModelTimeoutForAgent(executionAgent) {
const agentModelSettings = executionAgent.hasExplicitModelSettings()
? executionAgent.modelSettings
: undefined;
(0, modelRetry_1.validateModelTimeoutMs)((0, modelSettings_1.mergeModelSettings)(this.config.modelSettings, agentModelSettings));
}
/**
* @internal
* Applies call-level filters and merges session updates so the model request mirrors exactly
* what we persisted for history.
*/
async #prepareModelCall(state, executionAgent, options, artifacts, turnInput, serverConversationTracker, sessionInputUpdate) {
const { model, explicitlyModelSet, resolvedModelName } = await this.#resolveModelForAgent(executionAgent);
const hasExplicitAgentModelSettings = executionAgent.hasExplicitModelSettings();
// Store is caller-owned even when reasoning/text settings are implicit defaults.
const agentModelSettings = hasExplicitAgentModelSettings
? executionAgent.modelSettings
: Object.prototype.hasOwnProperty.call(executionAgent.modelSettings, 'store')
? { store: executionAgent.modelSettings.store }
: undefined;
// Only adapters that honor prompt model selection can replace the default model.
const promptOwnsModel = model.supportsPromptModelSelection === true &&
executionAgent.prompt !== undefined &&
!explicitlyModelSet;
const implicitModelSettings = hasExplicitAgentModelSettings || promptOwnsModel
? undefined
: (0, runConfig_1.getImplicitModelSettingsForResolvedModel)(explicitlyModelSet, resolvedModelName);
let modelSettings = (0, modelSettings_1.mergeModelSettings)(implicitModelSettings, this.config.modelSettings);
modelSettings = (0, modelSettings_1.mergeModelSettings)(modelSettings, agentModelSettings);
// Record effort ownership before legacy model cleanup can remove an explicit choice.
const hasReasoningEffort = modelSettings.reasoning?.effort !== undefined ||
modelSettings.providerData?.reasoning_effort !== undefined;
const modelRequestInternal = {
reasoningEffortImplicit: hasReasoningEffort
? implicitModelSettings?.reasoning?.effort !== undefined &&
!hasExplicitTopLevelReasoningEffort(this.config.modelSettings) &&
!hasExplicitTopLevelReasoningEffort(agentModelSettings)
: undefined,
tracingParent: (0, invocationContext_1.getRunStateTurnSpanParent)(state) ?? state._currentAgentSpan,
toolNameCollisionPolicy: options.toolNameCollisionPolicy ?? 'warn',
};
modelSettings = (0, modelSettings_1.adjustModelSettingsForLegacyModel)(explicitlyModelSet, agentModelSettings ?? implicitModelSettings ?? {}, modelSettings, resolvedModelName);
modelSettings = (0, modelSettings_1.maybeResetToolChoice)(state._currentAgent, state._toolUseTracker, modelSettings);
(0, modelRetry_1.validateModelTimeoutMs)(modelSettings);
state._lastModelSettings = modelSettings;
const systemInstructions = await executionAgent.getSystemPrompt(state._context);
const prompt = await executionAgent.getPrompt(state._context);
const allowPromptSuppliedTools = Boolean(prompt) &&
!(artifacts.toolsExplicitlyProvided &&
artifacts.serializedTools.length === 0 &&
artifacts.serializedHandoffs.length === 0);
const { modelInput, sourceItems, persistedItems, sourceMatchKinds, filterApplied, preserveInputIdentity, } = await (0, conversation_1.applyCallModelInputFilter)(state._currentAgent, options.callModelInputFilter, state._context, turnInput, systemInstructions);
// Persist normalized clones so session history mirrors the exact model payload. An empty array
// is intentional when a filter removes everything.
sessionInputUpdate?.(sourceItems, persistedItems);
const previousResponseId = serverConversationTracker?.previousResponseId ??
options.previousResponseId;
const conversationId = serverConversationTracker?.conversationId ?? options.conversationId;
return {
...artifacts,
model,
explicitlyModelSet,
modelRequestInternal,
modelSettings,
modelInput,
prompt,
allowPromptSuppliedTools,
previousResponseId,
conversationId,
sourceItems,
persistedItems,
sourceMatchKinds,
filterApplied,
preserveInputIdentity,
turnInput,
};
}
}
exports.Runner = Runner;
// internal helpers and constants
let defaultRunner;
function hasExplicitTopLevelReasoningEffort(settings) {
return settings?.reasoning?.effort !== undefined;
}
const getDefaultRunner = () => {
if (!defaultRunner) {
defaultRunner = new Runner();
}
return defaultRunner;
};
//# sourceMappingURL=run.js.map