@openai/agents-core 0.3.6 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/dist/errors.d.ts +40 -0
  2. package/dist/errors.js +38 -1
  3. package/dist/errors.js.map +1 -1
  4. package/dist/errors.mjs +34 -0
  5. package/dist/errors.mjs.map +1 -1
  6. package/dist/index.d.ts +6 -2
  7. package/dist/index.js +13 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +3 -1
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/metadata.js +2 -2
  12. package/dist/metadata.mjs +2 -2
  13. package/dist/result.d.ts +17 -0
  14. package/dist/result.js +71 -24
  15. package/dist/result.js.map +1 -1
  16. package/dist/result.mjs +69 -22
  17. package/dist/result.mjs.map +1 -1
  18. package/dist/run.d.ts +14 -47
  19. package/dist/run.js +421 -994
  20. package/dist/run.js.map +1 -1
  21. package/dist/run.mjs +405 -978
  22. package/dist/run.mjs.map +1 -1
  23. package/dist/runState.d.ts +1286 -172
  24. package/dist/runState.js +146 -16
  25. package/dist/runState.js.map +1 -1
  26. package/dist/runState.mjs +142 -12
  27. package/dist/runState.mjs.map +1 -1
  28. package/dist/runner/constants.d.ts +1 -0
  29. package/dist/runner/constants.js +6 -0
  30. package/dist/runner/constants.js.map +1 -0
  31. package/dist/runner/constants.mjs +3 -0
  32. package/dist/runner/constants.mjs.map +1 -0
  33. package/dist/runner/conversation.d.ts +85 -0
  34. package/dist/runner/conversation.js +275 -0
  35. package/dist/runner/conversation.js.map +1 -0
  36. package/dist/runner/conversation.mjs +269 -0
  37. package/dist/runner/conversation.mjs.map +1 -0
  38. package/dist/runner/guardrails.d.ts +23 -0
  39. package/dist/runner/guardrails.js +174 -0
  40. package/dist/runner/guardrails.js.map +1 -0
  41. package/dist/runner/guardrails.mjs +166 -0
  42. package/dist/runner/guardrails.mjs.map +1 -0
  43. package/dist/runner/items.d.ts +18 -0
  44. package/dist/runner/items.js +89 -0
  45. package/dist/runner/items.js.map +1 -0
  46. package/dist/runner/items.mjs +79 -0
  47. package/dist/runner/items.mjs.map +1 -0
  48. package/dist/runner/mcpApprovals.d.ts +25 -0
  49. package/dist/runner/mcpApprovals.js +66 -0
  50. package/dist/runner/mcpApprovals.js.map +1 -0
  51. package/dist/runner/mcpApprovals.mjs +63 -0
  52. package/dist/runner/mcpApprovals.mjs.map +1 -0
  53. package/dist/runner/modelOutputs.d.ts +10 -0
  54. package/dist/runner/modelOutputs.js +206 -0
  55. package/dist/runner/modelOutputs.js.map +1 -0
  56. package/dist/runner/modelOutputs.mjs +203 -0
  57. package/dist/runner/modelOutputs.mjs.map +1 -0
  58. package/dist/runner/modelPreparation.d.ts +8 -0
  59. package/dist/runner/modelPreparation.js +41 -0
  60. package/dist/runner/modelPreparation.js.map +1 -0
  61. package/dist/runner/modelPreparation.mjs +38 -0
  62. package/dist/runner/modelPreparation.mjs.map +1 -0
  63. package/dist/runner/modelSettings.d.ts +20 -0
  64. package/dist/runner/modelSettings.js +97 -0
  65. package/dist/runner/modelSettings.js.map +1 -0
  66. package/dist/runner/modelSettings.mjs +92 -0
  67. package/dist/runner/modelSettings.mjs.map +1 -0
  68. package/dist/runner/runLoop.d.ts +32 -0
  69. package/dist/runner/runLoop.js +62 -0
  70. package/dist/runner/runLoop.js.map +1 -0
  71. package/dist/runner/runLoop.mjs +57 -0
  72. package/dist/runner/runLoop.mjs.map +1 -0
  73. package/dist/runner/sessionPersistence.d.ts +26 -0
  74. package/dist/runner/sessionPersistence.js +441 -0
  75. package/dist/runner/sessionPersistence.js.map +1 -0
  76. package/dist/runner/sessionPersistence.mjs +431 -0
  77. package/dist/runner/sessionPersistence.mjs.map +1 -0
  78. package/dist/runner/steps.d.ts +48 -0
  79. package/dist/runner/steps.js +40 -0
  80. package/dist/runner/steps.js.map +1 -0
  81. package/dist/runner/steps.mjs +36 -0
  82. package/dist/runner/steps.mjs.map +1 -0
  83. package/dist/runner/streaming.d.ts +9 -0
  84. package/dist/runner/streaming.js +74 -0
  85. package/dist/runner/streaming.js.map +1 -0
  86. package/dist/runner/streaming.mjs +65 -0
  87. package/dist/runner/streaming.mjs.map +1 -0
  88. package/dist/runner/toolExecution.d.ts +15 -0
  89. package/dist/runner/toolExecution.js +997 -0
  90. package/dist/runner/toolExecution.js.map +1 -0
  91. package/dist/runner/toolExecution.mjs +984 -0
  92. package/dist/runner/toolExecution.mjs.map +1 -0
  93. package/dist/runner/toolUseTracker.d.ts +9 -0
  94. package/dist/runner/toolUseTracker.js +34 -0
  95. package/dist/runner/toolUseTracker.js.map +1 -0
  96. package/dist/runner/toolUseTracker.mjs +30 -0
  97. package/dist/runner/toolUseTracker.mjs.map +1 -0
  98. package/dist/runner/tracing.d.ts +23 -0
  99. package/dist/runner/tracing.js +45 -0
  100. package/dist/runner/tracing.js.map +1 -0
  101. package/dist/runner/tracing.mjs +41 -0
  102. package/dist/runner/tracing.mjs.map +1 -0
  103. package/dist/runner/turnPreparation.d.ts +30 -0
  104. package/dist/runner/turnPreparation.js +80 -0
  105. package/dist/runner/turnPreparation.js.map +1 -0
  106. package/dist/runner/turnPreparation.mjs +74 -0
  107. package/dist/runner/turnPreparation.mjs.map +1 -0
  108. package/dist/runner/turnResolution.d.ts +3 -0
  109. package/dist/runner/turnResolution.js +531 -0
  110. package/dist/runner/turnResolution.js.map +1 -0
  111. package/dist/runner/turnResolution.mjs +526 -0
  112. package/dist/runner/turnResolution.mjs.map +1 -0
  113. package/dist/runner/types.d.ts +66 -0
  114. package/dist/runner/types.js +3 -0
  115. package/dist/runner/types.js.map +1 -0
  116. package/dist/runner/types.mjs +2 -0
  117. package/dist/runner/types.mjs.map +1 -0
  118. package/dist/shims/mcp-server/node.js +76 -30
  119. package/dist/shims/mcp-server/node.js.map +1 -1
  120. package/dist/shims/mcp-server/node.mjs +76 -30
  121. package/dist/shims/mcp-server/node.mjs.map +1 -1
  122. package/dist/tool.d.ts +28 -2
  123. package/dist/tool.js +7 -1
  124. package/dist/tool.js.map +1 -1
  125. package/dist/tool.mjs +8 -2
  126. package/dist/tool.mjs.map +1 -1
  127. package/dist/toolGuardrail.d.ts +101 -0
  128. package/dist/toolGuardrail.js +58 -0
  129. package/dist/toolGuardrail.js.map +1 -0
  130. package/dist/toolGuardrail.mjs +51 -0
  131. package/dist/toolGuardrail.mjs.map +1 -0
  132. package/dist/tracing/config.d.ts +3 -0
  133. package/dist/tracing/config.js +3 -0
  134. package/dist/tracing/config.js.map +1 -0
  135. package/dist/tracing/config.mjs +2 -0
  136. package/dist/tracing/config.mjs.map +1 -0
  137. package/dist/tracing/context.d.ts +2 -0
  138. package/dist/tracing/context.js +95 -24
  139. package/dist/tracing/context.js.map +1 -1
  140. package/dist/tracing/context.mjs +95 -24
  141. package/dist/tracing/context.mjs.map +1 -1
  142. package/dist/tracing/createSpans.d.ts +11 -11
  143. package/dist/tracing/index.d.ts +2 -0
  144. package/dist/tracing/index.js.map +1 -1
  145. package/dist/tracing/index.mjs.map +1 -1
  146. package/dist/tracing/provider.js +54 -4
  147. package/dist/tracing/provider.js.map +1 -1
  148. package/dist/tracing/provider.mjs +54 -4
  149. package/dist/tracing/provider.mjs.map +1 -1
  150. package/dist/tracing/spans.d.ts +2 -0
  151. package/dist/tracing/spans.js +6 -0
  152. package/dist/tracing/spans.js.map +1 -1
  153. package/dist/tracing/spans.mjs +6 -0
  154. package/dist/tracing/spans.mjs.map +1 -1
  155. package/dist/tracing/traces.d.ts +11 -1
  156. package/dist/tracing/traces.js +15 -2
  157. package/dist/tracing/traces.js.map +1 -1
  158. package/dist/tracing/traces.mjs +15 -2
  159. package/dist/tracing/traces.mjs.map +1 -1
  160. package/dist/types/protocol.d.ts +11 -0
  161. package/dist/types/protocol.js +1 -0
  162. package/dist/types/protocol.js.map +1 -1
  163. package/dist/types/protocol.mjs +1 -0
  164. package/dist/types/protocol.mjs.map +1 -1
  165. package/dist/utils/binary.d.ts +6 -0
  166. package/dist/utils/binary.js +53 -0
  167. package/dist/utils/binary.js.map +1 -0
  168. package/dist/utils/binary.mjs +49 -0
  169. package/dist/utils/binary.mjs.map +1 -0
  170. package/dist/utils/toolGuardrails.d.ts +24 -0
  171. package/dist/utils/toolGuardrails.js +58 -0
  172. package/dist/utils/toolGuardrails.js.map +1 -0
  173. package/dist/utils/toolGuardrails.mjs +54 -0
  174. package/dist/utils/toolGuardrails.mjs.map +1 -0
  175. package/package.json +4 -3
  176. package/dist/runImplementation.d.ts +0 -161
  177. package/dist/runImplementation.js +0 -2054
  178. package/dist/runImplementation.js.map +0 -1
  179. package/dist/runImplementation.mjs +0 -2028
  180. package/dist/runImplementation.mjs.map +0 -1
package/dist/run.js CHANGED
@@ -3,32 +3,40 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Runner = void 0;
6
+ exports.Runner = exports.getTurnInput = exports.selectModel = exports.getTracing = void 0;
7
7
  exports.run = run;
8
- exports.getTurnInput = getTurnInput;
9
- exports.selectModel = selectModel;
10
- exports.getTracing = getTracing;
11
8
  const agent_1 = require("./agent.js");
9
+ const events_1 = require("./events.js");
10
+ const errors_1 = require("./errors.js");
12
11
  const guardrail_1 = require("./guardrail.js");
12
+ const lifecycle_1 = require("./lifecycle.js");
13
+ const logger_1 = __importDefault(require("./logger.js"));
13
14
  const providers_1 = require("./providers.js");
14
15
  const runContext_1 = require("./runContext.js");
15
16
  const result_1 = require("./result.js");
16
- const lifecycle_1 = require("./lifecycle.js");
17
- const logger_1 = __importDefault(require("./logger.js"));
18
- const serialize_1 = require("./utils/serialize.js");
19
- const errors_1 = require("./errors.js");
20
- const runImplementation_1 = require("./runImplementation.js");
17
+ const runState_1 = require("./runState.js");
21
18
  const tool_1 = require("./tool.js");
22
19
  const context_1 = require("./tracing/context.js");
23
- const tracing_1 = require("./tracing/index.js");
24
20
  const usage_1 = require("./usage.js");
25
- const events_1 = require("./events.js");
26
- const runState_1 = require("./runState.js");
27
- const protocol_1 = require("./types/protocol.js");
28
21
  const tools_1 = require("./utils/tools.js");
29
- const defaultModel_1 = require("./defaultModel.js");
30
- const base64_1 = require("./utils/base64.js");
31
- const smartString_1 = require("./utils/smartString.js");
22
+ const constants_1 = require("./runner/constants.js");
23
+ const protocol_1 = require("./types/protocol.js");
24
+ const conversation_1 = require("./runner/conversation.js");
25
+ const guardrails_1 = require("./runner/guardrails.js");
26
+ const modelSettings_1 = require("./runner/modelSettings.js");
27
+ const modelOutputs_1 = require("./runner/modelOutputs.js");
28
+ const streaming_1 = require("./runner/streaming.js");
29
+ const sessionPersistence_1 = require("./runner/sessionPersistence.js");
30
+ const turnResolution_1 = require("./runner/turnResolution.js");
31
+ const turnPreparation_1 = require("./runner/turnPreparation.js");
32
+ const runLoop_1 = require("./runner/runLoop.js");
33
+ const tracing_1 = require("./runner/tracing.js");
34
+ var tracing_2 = require("./runner/tracing.js");
35
+ Object.defineProperty(exports, "getTracing", { enumerable: true, get: function () { return tracing_2.getTracing; } });
36
+ var modelSettings_2 = require("./runner/modelSettings.js");
37
+ Object.defineProperty(exports, "selectModel", { enumerable: true, get: function () { return modelSettings_2.selectModel; } });
38
+ var items_1 = require("./runner/items.js");
39
+ Object.defineProperty(exports, "getTurnInput", { enumerable: true, get: function () { return items_1.getTurnInput; } });
32
40
  async function run(agent, input, options) {
33
41
  const runner = getDefaultRunner();
34
42
  if (options?.stream) {
@@ -64,6 +72,7 @@ class Runner extends lifecycle_1.RunHooks {
64
72
  traceId: config.traceId,
65
73
  groupId: config.groupId,
66
74
  traceMetadata: config.traceMetadata,
75
+ tracing: config.tracing,
67
76
  sessionInputCallback: config.sessionInputCallback,
68
77
  callModelInputFilter: config.callModelInputFilter,
69
78
  };
@@ -80,148 +89,35 @@ class Runner extends lifecycle_1.RunHooks {
80
89
  // Likewise allow callers to override callModelInputFilter on individual runs.
81
90
  const callModelInputFilter = resolvedOptions.callModelInputFilter ?? this.config.callModelInputFilter;
82
91
  const hasCallModelInputFilter = Boolean(callModelInputFilter);
92
+ const tracingConfig = resolvedOptions.tracing ?? this.config.tracing;
83
93
  const effectiveOptions = {
84
94
  ...resolvedOptions,
85
95
  sessionInputCallback,
86
96
  callModelInputFilter,
87
97
  };
88
- const serverManagesConversation = Boolean(effectiveOptions.conversationId) ||
89
- Boolean(effectiveOptions.previousResponseId);
98
+ const resumingFromState = input instanceof runState_1.RunState;
99
+ const preserveTurnPersistenceOnResume = resumingFromState &&
100
+ input._currentTurnInProgress === true;
101
+ const resumedConversationId = resumingFromState
102
+ ? input._conversationId
103
+ : undefined;
104
+ const resumedPreviousResponseId = resumingFromState
105
+ ? input._previousResponseId
106
+ : undefined;
107
+ const serverManagesConversation = Boolean(effectiveOptions.conversationId ?? resumedConversationId) ||
108
+ Boolean(effectiveOptions.previousResponseId ?? resumedPreviousResponseId);
90
109
  // When the server tracks conversation history we defer to it for previous turns so local session
91
110
  // persistence can focus solely on the new delta being generated in this process.
92
111
  const session = effectiveOptions.session;
93
- const resumingFromState = input instanceof runState_1.RunState;
94
- let sessionInputOriginalSnapshot = session && resumingFromState ? [] : undefined;
95
- let sessionInputFilteredSnapshot = undefined;
96
- // Tracks remaining persistence slots per AgentInputItem key so resumed sessions only write each original occurrence once.
97
- let sessionInputPendingWriteCounts = session && resumingFromState ? new Map() : undefined;
98
- // Keeps track of which inputs should be written back to session memory. `sourceItems` reflects
99
- // the original objects (so we can respect resume counts) while `filteredItems`, when present,
100
- // contains the filtered/redacted clones that must be persisted for history.
101
- // The helper reconciles the filtered copies produced by callModelInputFilter with their original
102
- // counterparts so resume-from-state bookkeeping stays consistent and duplicate references only
103
- // consume a single persistence slot.
104
- const recordSessionItemsForPersistence = (sourceItems, filteredItems) => {
105
- const pendingWriteCounts = sessionInputPendingWriteCounts;
106
- if (filteredItems !== undefined) {
107
- if (!pendingWriteCounts) {
108
- sessionInputFilteredSnapshot = filteredItems.map((item) => structuredClone(item));
109
- return;
110
- }
111
- const persistableItems = [];
112
- const sourceOccurrenceCounts = new WeakMap();
113
- // Track how many times each original object appears so duplicate references only consume one persistence slot.
114
- for (const source of sourceItems) {
115
- if (!source || typeof source !== 'object') {
116
- continue;
117
- }
118
- const nextCount = (sourceOccurrenceCounts.get(source) ?? 0) + 1;
119
- sourceOccurrenceCounts.set(source, nextCount);
120
- }
121
- // Let filtered items without a one-to-one source match claim any remaining persistence count.
122
- const consumeAnyPendingWriteSlot = () => {
123
- for (const [key, remaining] of pendingWriteCounts) {
124
- if (remaining > 0) {
125
- pendingWriteCounts.set(key, remaining - 1);
126
- return true;
127
- }
128
- }
129
- return false;
130
- };
131
- for (let i = 0; i < filteredItems.length; i++) {
132
- const filteredItem = filteredItems[i];
133
- if (!filteredItem) {
134
- continue;
135
- }
136
- let allocated = false;
137
- const source = sourceItems[i];
138
- if (source && typeof source === 'object') {
139
- const pendingOccurrences = (sourceOccurrenceCounts.get(source) ?? 0) - 1;
140
- sourceOccurrenceCounts.set(source, pendingOccurrences);
141
- if (pendingOccurrences > 0) {
142
- continue;
143
- }
144
- const sourceKey = getAgentInputItemKey(source);
145
- const remaining = pendingWriteCounts.get(sourceKey) ?? 0;
146
- if (remaining > 0) {
147
- pendingWriteCounts.set(sourceKey, remaining - 1);
148
- persistableItems.push(structuredClone(filteredItem));
149
- allocated = true;
150
- continue;
151
- }
152
- }
153
- const filteredKey = getAgentInputItemKey(filteredItem);
154
- const filteredRemaining = pendingWriteCounts.get(filteredKey) ?? 0;
155
- if (filteredRemaining > 0) {
156
- pendingWriteCounts.set(filteredKey, filteredRemaining - 1);
157
- persistableItems.push(structuredClone(filteredItem));
158
- allocated = true;
159
- continue;
160
- }
161
- if (!source && consumeAnyPendingWriteSlot()) {
162
- persistableItems.push(structuredClone(filteredItem));
163
- allocated = true;
164
- }
165
- if (!allocated &&
166
- !source &&
167
- sessionInputFilteredSnapshot === undefined) {
168
- // Preserve at least one copy so later persistence resolves even when no counters remain.
169
- persistableItems.push(structuredClone(filteredItem));
170
- }
171
- }
172
- if (persistableItems.length > 0 ||
173
- sessionInputFilteredSnapshot === undefined) {
174
- sessionInputFilteredSnapshot = persistableItems;
175
- }
176
- return;
177
- }
178
- const filtered = [];
179
- if (!pendingWriteCounts) {
180
- for (const item of sourceItems) {
181
- if (!item) {
182
- continue;
183
- }
184
- filtered.push(structuredClone(item));
185
- }
186
- }
187
- else {
188
- for (const item of sourceItems) {
189
- if (!item) {
190
- continue;
191
- }
192
- const key = getAgentInputItemKey(item);
193
- const remaining = pendingWriteCounts.get(key) ?? 0;
194
- if (remaining <= 0) {
195
- continue;
196
- }
197
- pendingWriteCounts.set(key, remaining - 1);
198
- filtered.push(structuredClone(item));
199
- }
200
- }
201
- if (filtered.length > 0) {
202
- sessionInputFilteredSnapshot = filtered;
203
- }
204
- else if (sessionInputFilteredSnapshot === undefined) {
205
- sessionInputFilteredSnapshot = [];
206
- }
207
- };
208
- // Determine which items should be committed to session memory for this turn.
209
- // Filters take precedence because they reflect the exact payload delivered to the model.
210
- const resolveSessionItemsForPersistence = () => {
211
- if (sessionInputFilteredSnapshot !== undefined) {
212
- return sessionInputFilteredSnapshot;
213
- }
214
- if (hasCallModelInputFilter) {
215
- return undefined;
216
- }
217
- return sessionInputOriginalSnapshot;
218
- };
112
+ const sessionPersistence = (0, sessionPersistence_1.createSessionPersistenceTracker)({
113
+ session,
114
+ hasCallModelInputFilter,
115
+ persistInput: sessionPersistence_1.saveStreamInputToSession,
116
+ resumingFromState,
117
+ });
219
118
  let preparedInput = input;
220
119
  if (!(preparedInput instanceof runState_1.RunState)) {
221
- if (session && Array.isArray(preparedInput) && !sessionInputCallback) {
222
- throw new errors_1.UserError('RunConfig.sessionInputCallback must be provided when using session history with list inputs.');
223
- }
224
- const prepared = await (0, runImplementation_1.prepareInputItemsWithSession)(preparedInput, session, sessionInputCallback, {
120
+ const prepared = await (0, sessionPersistence_1.prepareInputItemsWithSession)(preparedInput, session, sessionInputCallback, {
225
121
  // When the server tracks conversation state we only send the new turn inputs;
226
122
  // previous messages are recovered via conversationId/previousResponseId.
227
123
  includeHistoryInPreparedInput: !serverManagesConversation,
@@ -241,48 +137,21 @@ class Runner extends lifecycle_1.RunHooks {
241
137
  else {
242
138
  preparedInput = prepared.preparedInput;
243
139
  }
244
- if (session) {
245
- const items = prepared.sessionItems ?? [];
246
- // Clone the items that will be persisted so later mutations (filters, hooks) cannot desync history.
247
- sessionInputOriginalSnapshot = items.map((item) => structuredClone(item));
248
- // Reset pending counts so each prepared item reserves exactly one write slot until filters resolve matches.
249
- sessionInputPendingWriteCounts = new Map();
250
- for (const item of items) {
251
- const key = getAgentInputItemKey(item);
252
- sessionInputPendingWriteCounts.set(key, (sessionInputPendingWriteCounts.get(key) ?? 0) + 1);
253
- }
254
- }
140
+ sessionPersistence?.setPreparedItems(prepared.sessionItems);
255
141
  }
256
142
  // Streaming runs persist the input asynchronously, so track a one-shot helper
257
143
  // that can be awaited from multiple branches without double-writing.
258
- let ensureStreamInputPersisted;
259
- // Sessions remain usable alongside server-managed conversations (e.g., OpenAIConversationsSession)
260
- // so callers can reuse callbacks, resume-from-state logic, and other helpers without duplicating
261
- // remote history, so persistence is gated on serverManagesConversation.
262
- if (session && !serverManagesConversation) {
263
- let persisted = false;
264
- ensureStreamInputPersisted = async () => {
265
- if (persisted) {
266
- return;
267
- }
268
- const itemsToPersist = resolveSessionItemsForPersistence();
269
- if (!itemsToPersist || itemsToPersist.length === 0) {
270
- return;
271
- }
272
- persisted = true;
273
- await (0, runImplementation_1.saveStreamInputToSession)(session, itemsToPersist);
274
- };
275
- }
144
+ const ensureStreamInputPersisted = sessionPersistence?.buildPersistInputOnce(serverManagesConversation);
276
145
  const executeRun = async () => {
277
146
  if (effectiveOptions.stream) {
278
- const streamResult = await this.#runIndividualStream(agent, preparedInput, effectiveOptions, ensureStreamInputPersisted, recordSessionItemsForPersistence);
147
+ const streamResult = await this.#runIndividualStream(agent, preparedInput, effectiveOptions, ensureStreamInputPersisted, sessionPersistence?.recordTurnItems, preserveTurnPersistenceOnResume);
279
148
  return streamResult;
280
149
  }
281
- const runResult = await this.#runIndividualNonStream(agent, preparedInput, effectiveOptions, recordSessionItemsForPersistence);
150
+ const runResult = await this.#runIndividualNonStream(agent, preparedInput, effectiveOptions, sessionPersistence?.recordTurnItems, preserveTurnPersistenceOnResume);
282
151
  // See note above: allow sessions to run for callbacks/state but skip writes when the server
283
152
  // is the source of truth for transcript history.
284
- if (session && !serverManagesConversation) {
285
- await (0, runImplementation_1.saveToSession)(session, resolveSessionItemsForPersistence(), runResult);
153
+ if (sessionPersistence && !serverManagesConversation) {
154
+ await (0, sessionPersistence_1.saveToSession)(session, sessionPersistence.getItemsForPersistence(), runResult);
286
155
  }
287
156
  return runResult;
288
157
  };
@@ -299,6 +168,8 @@ class Runner extends lifecycle_1.RunHooks {
299
168
  name: this.config.workflowName,
300
169
  groupId: this.config.groupId,
301
170
  metadata: this.config.traceMetadata,
171
+ // Per-run tracing config overrides exporter defaults such as environment API key.
172
+ tracingApiKey: tracingConfig?.apiKey,
302
173
  });
303
174
  }
304
175
  // --------------------------------------------------------------
@@ -306,23 +177,6 @@ class Runner extends lifecycle_1.RunHooks {
306
177
  // --------------------------------------------------------------
307
178
  inputGuardrailDefs;
308
179
  outputGuardrailDefs;
309
- #getInputGuardrailDefinitions(state) {
310
- return this.inputGuardrailDefs.concat(state._currentAgent.inputGuardrails.map(guardrail_1.defineInputGuardrail));
311
- }
312
- #splitInputGuardrails(state) {
313
- const guardrails = this.#getInputGuardrailDefinitions(state);
314
- const blocking = [];
315
- const parallel = [];
316
- for (const guardrail of guardrails) {
317
- if (guardrail.runInParallel === false) {
318
- blocking.push(guardrail);
319
- }
320
- else {
321
- parallel.push(guardrail);
322
- }
323
- }
324
- return { blocking, parallel };
325
- }
326
180
  /**
327
181
  * @internal
328
182
  * Resolves the effective model once so both run loops obey the same precedence rules.
@@ -332,11 +186,12 @@ class Runner extends lifecycle_1.RunHooks {
332
186
  agent.model !== agent_1.Agent.DEFAULT_MODEL_PLACEHOLDER) ||
333
187
  (this.config.model !== undefined &&
334
188
  this.config.model !== agent_1.Agent.DEFAULT_MODEL_PLACEHOLDER);
335
- let resolvedModel = selectModel(agent.model, this.config.model);
336
- if (typeof resolvedModel === 'string') {
337
- resolvedModel = await this.config.modelProvider.getModel(resolvedModel);
338
- }
339
- return { model: resolvedModel, explictlyModelSet };
189
+ const selectedModel = (0, modelSettings_1.selectModel)(agent.model, this.config.model);
190
+ const resolvedModelName = typeof selectedModel === 'string' ? selectedModel : undefined;
191
+ const resolvedModel = typeof selectedModel === 'string'
192
+ ? await this.config.modelProvider.getModel(selectedModel)
193
+ : selectedModel;
194
+ return { model: resolvedModel, explictlyModelSet, resolvedModelName };
340
195
  }
341
196
  /**
342
197
  * @internal
@@ -344,7 +199,7 @@ class Runner extends lifecycle_1.RunHooks {
344
199
  async #runIndividualNonStream(startingAgent, input, options,
345
200
  // sessionInputUpdate lets the caller adjust queued session items after filters run so we
346
201
  // persist exactly what we send to the model (e.g., after redactions or truncation).
347
- sessionInputUpdate) {
202
+ sessionInputUpdate, preserveTurnPersistenceOnResume) {
348
203
  return (0, context_1.withNewSpanContext)(async () => {
349
204
  // if we have a saved state we use that one, otherwise we create a new one
350
205
  const isResumedState = input instanceof runState_1.RunState;
@@ -352,11 +207,18 @@ class Runner extends lifecycle_1.RunHooks {
352
207
  ? input
353
208
  : new runState_1.RunState(options.context instanceof runContext_1.RunContext
354
209
  ? options.context
355
- : new runContext_1.RunContext(options.context), input, startingAgent, options.maxTurns ?? DEFAULT_MAX_TURNS);
356
- const serverConversationTracker = options.conversationId || options.previousResponseId
357
- ? new ServerConversationTracker({
358
- conversationId: options.conversationId,
359
- previousResponseId: options.previousResponseId,
210
+ : new runContext_1.RunContext(options.context), input, startingAgent, options.maxTurns ?? constants_1.DEFAULT_MAX_TURNS);
211
+ const resolvedConversationId = options.conversationId ??
212
+ (isResumedState ? state._conversationId : undefined);
213
+ const resolvedPreviousResponseId = options.previousResponseId ??
214
+ (isResumedState ? state._previousResponseId : undefined);
215
+ if (!isResumedState) {
216
+ state.setConversationContext(resolvedConversationId, resolvedPreviousResponseId);
217
+ }
218
+ const serverConversationTracker = resolvedConversationId || resolvedPreviousResponseId
219
+ ? new conversation_1.ServerConversationTracker({
220
+ conversationId: resolvedConversationId,
221
+ previousResponseId: resolvedPreviousResponseId,
360
222
  })
361
223
  : undefined;
362
224
  if (serverConversationTracker && isResumedState) {
@@ -365,7 +227,9 @@ class Runner extends lifecycle_1.RunHooks {
365
227
  generatedItems: state._generatedItems,
366
228
  modelResponses: state._modelResponses,
367
229
  });
230
+ state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
368
231
  }
232
+ // Tracks when we resume an approval interruption so the next run-again step stays in the same turn.
369
233
  let continuingInterruptedTurn = false;
370
234
  try {
371
235
  while (true) {
@@ -378,66 +242,60 @@ class Runner extends lifecycle_1.RunHooks {
378
242
  if (!state._lastTurnResponse || !state._lastProcessedResponse) {
379
243
  throw new errors_1.UserError('No model response found in previous state', state);
380
244
  }
381
- const turnResult = await (0, runImplementation_1.resolveInterruptedTurn)(state._currentAgent, state._originalInput, state._generatedItems, state._lastTurnResponse, state._lastProcessedResponse, this, state);
382
- state._toolUseTracker.addToolUse(state._currentAgent, state._lastProcessedResponse.toolsUsed);
383
- state._originalInput = turnResult.originalInput;
384
- state._generatedItems = turnResult.generatedItems;
245
+ const interruptedOutcome = await (0, runLoop_1.resumeInterruptedTurn)({
246
+ state,
247
+ runner: this,
248
+ });
385
249
  // Don't reset counter here - resolveInterruptedTurn already adjusted it via rewind logic
386
250
  // The counter will be reset when _currentTurn is incremented (starting a new turn)
387
- if (turnResult.nextStep.type === 'next_step_interruption') {
251
+ const { shouldReturn, shouldContinue } = (0, runLoop_1.handleInterruptedOutcome)({
252
+ state,
253
+ outcome: interruptedOutcome,
254
+ setContinuingInterruptedTurn: (value) => {
255
+ continuingInterruptedTurn = value;
256
+ },
257
+ });
258
+ if (shouldReturn) {
388
259
  // we are still in an interruption, so we need to avoid an infinite loop
389
- state._currentStep = turnResult.nextStep;
390
260
  return new result_1.RunResult(state);
391
261
  }
392
- // If continuing from interruption with next_step_run_again, set step to undefined
393
- // so the loop treats it as a new step without incrementing the turn.
394
- // The counter has already been adjusted by resolveInterruptedTurn's rewind logic.
395
- if (turnResult.nextStep.type === 'next_step_run_again') {
396
- continuingInterruptedTurn = true;
397
- state._currentStep = undefined;
262
+ if (shouldContinue) {
398
263
  continue;
399
264
  }
400
- continuingInterruptedTurn = false;
401
- state._currentStep = turnResult.nextStep;
402
265
  }
403
266
  if (state._currentStep.type === 'next_step_run_again') {
404
- const artifacts = await prepareAgentArtifacts(state);
405
- const isResumingFromInterruption = isResumedState && continuingInterruptedTurn;
267
+ const wasContinuingInterruptedTurn = continuingInterruptedTurn;
406
268
  continuingInterruptedTurn = false;
407
- // Do not advance the turn when resuming from an interruption; the next model call is
408
- // still part of the same logical turn.
409
- if (!isResumingFromInterruption) {
410
- state._currentTurn++;
411
- state._currentTurnPersistedItemCount = 0;
412
- }
413
- if (state._currentTurn > state._maxTurns) {
414
- state._currentAgentSpan?.setError({
415
- message: 'Max turns exceeded',
416
- data: { max_turns: state._maxTurns },
417
- });
418
- throw new errors_1.MaxTurnsExceededError(`Max turns (${state._maxTurns}) exceeded`, state);
419
- }
420
- logger_1.default.debug(`Running agent ${state._currentAgent.name} (turn ${state._currentTurn})`);
421
- let parallelGuardrailPromise;
422
- // Only run input guardrails on the first turn of a new run.
423
- if (state._currentTurn === 1 && !isResumingFromInterruption) {
424
- const guardrails = this.#splitInputGuardrails(state);
425
- if (guardrails.blocking.length > 0) {
426
- await this.#runInputGuardrails(state, guardrails.blocking);
427
- }
428
- if (guardrails.parallel.length > 0) {
429
- parallelGuardrailPromise = this.#runInputGuardrails(state, guardrails.parallel);
430
- parallelGuardrailPromise.catch(() => { });
431
- }
432
- }
433
- const turnInput = serverConversationTracker
434
- ? serverConversationTracker.prepareInput(state._originalInput, state._generatedItems)
435
- : getTurnInput(state._originalInput, state._generatedItems);
436
- if (state._noActiveAgentRun) {
437
- state._currentAgent.emit('agent_start', state._context, state._currentAgent, turnInput);
438
- this.emit('agent_start', state._context, state._currentAgent, turnInput);
269
+ const guardrailTracker = (0, guardrails_1.createGuardrailTracker)();
270
+ const previousTurn = state._currentTurn;
271
+ const previousPersistedCount = state._currentTurnPersistedItemCount;
272
+ const previousGeneratedCount = state._generatedItems.length;
273
+ const { artifacts, turnInput, parallelGuardrailPromise } = await (0, turnPreparation_1.prepareTurn)({
274
+ state,
275
+ input: state._originalInput,
276
+ generatedItems: state._generatedItems,
277
+ isResumedState,
278
+ preserveTurnPersistenceOnResume,
279
+ continuingInterruptedTurn: wasContinuingInterruptedTurn,
280
+ serverConversationTracker,
281
+ inputGuardrailDefs: this.inputGuardrailDefs,
282
+ guardrailHandlers: {
283
+ onParallelStart: guardrailTracker.markPending,
284
+ onParallelError: guardrailTracker.setError,
285
+ },
286
+ emitAgentStart: (context, agent, inputItems) => {
287
+ this.emit('agent_start', context, agent, inputItems);
288
+ },
289
+ });
290
+ if (preserveTurnPersistenceOnResume &&
291
+ state._currentTurn > previousTurn &&
292
+ previousPersistedCount <= previousGeneratedCount) {
293
+ // Preserve persisted offsets from a resumed run to avoid re-saving prior items.
294
+ state._currentTurnPersistedItemCount = previousPersistedCount;
439
295
  }
296
+ guardrailTracker.setPromise(parallelGuardrailPromise);
440
297
  const preparedCall = await this.#prepareModelCall(state, options, artifacts, turnInput, serverConversationTracker, sessionInputUpdate);
298
+ guardrailTracker.throwIfError();
441
299
  state._lastTurnResponse = await preparedCall.model.getResponse({
442
300
  systemInstructions: preparedCall.modelInput.instructions,
443
301
  prompt: preparedCall.prompt,
@@ -453,59 +311,74 @@ class Runner extends lifecycle_1.RunHooks {
453
311
  toolsExplicitlyProvided: preparedCall.toolsExplicitlyProvided,
454
312
  outputType: (0, tools_1.convertAgentOutputTypeToSerializable)(state._currentAgent.outputType),
455
313
  handoffs: preparedCall.serializedHandoffs,
456
- tracing: getTracing(this.config.tracingDisabled, this.config.traceIncludeSensitiveData),
314
+ tracing: (0, tracing_1.getTracing)(this.config.tracingDisabled, this.config.traceIncludeSensitiveData),
457
315
  signal: options.signal,
458
316
  });
317
+ if (serverConversationTracker) {
318
+ serverConversationTracker.markInputAsSent(preparedCall.sourceItems, {
319
+ filterApplied: preparedCall.filterApplied,
320
+ allTurnItems: preparedCall.turnInput,
321
+ });
322
+ }
459
323
  state._modelResponses.push(state._lastTurnResponse);
460
324
  state._context.usage.add(state._lastTurnResponse.usage);
461
325
  state._noActiveAgentRun = false;
462
326
  // After each turn record the items echoed by the server so future requests only
463
327
  // include the incremental inputs that have not yet been acknowledged.
464
328
  serverConversationTracker?.trackServerItems(state._lastTurnResponse);
465
- const processedResponse = (0, runImplementation_1.processModelResponse)(state._lastTurnResponse, state._currentAgent, preparedCall.tools, preparedCall.handoffs);
466
- state._lastProcessedResponse = processedResponse;
467
- const turnResult = await (0, runImplementation_1.resolveTurnAfterModelResponse)(state._currentAgent, state._originalInput, state._generatedItems, state._lastTurnResponse, state._lastProcessedResponse, this, state);
468
- state._toolUseTracker.addToolUse(state._currentAgent, state._lastProcessedResponse.toolsUsed);
469
- state._originalInput = turnResult.originalInput;
470
- state._generatedItems = turnResult.generatedItems;
471
- if (turnResult.nextStep.type === 'next_step_run_again') {
472
- state._currentTurnPersistedItemCount = 0;
473
- }
474
- state._currentStep = turnResult.nextStep;
475
- if (parallelGuardrailPromise) {
476
- await parallelGuardrailPromise;
477
- }
478
- }
479
- if (state._currentStep &&
480
- state._currentStep.type === 'next_step_final_output') {
481
- await this.#runOutputGuardrails(state, state._currentStep.output);
482
- this.emit('agent_end', state._context, state._currentAgent, state._currentStep.output);
483
- state._currentAgent.emit('agent_end', state._context, state._currentStep.output);
484
- return new result_1.RunResult(state);
485
- }
486
- else if (state._currentStep &&
487
- state._currentStep.type === 'next_step_handoff') {
488
- state._currentAgent = state._currentStep.newAgent;
489
- if (state._currentAgentSpan) {
490
- state._currentAgentSpan.end();
491
- (0, context_1.resetCurrentSpan)();
492
- state._currentAgentSpan = undefined;
329
+ if (serverConversationTracker) {
330
+ state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
493
331
  }
494
- state._noActiveAgentRun = true;
495
- // we've processed the handoff, so we need to run the loop again
496
- state._currentStep = { type: 'next_step_run_again' };
497
- }
498
- else if (state._currentStep &&
499
- state._currentStep.type === 'next_step_interruption') {
500
- // interrupted. Don't run any guardrails
501
- return new result_1.RunResult(state);
332
+ const processedResponse = (0, modelOutputs_1.processModelResponse)(state._lastTurnResponse, state._currentAgent, preparedCall.tools, preparedCall.handoffs);
333
+ state._lastProcessedResponse = processedResponse;
334
+ const turnResult = await (0, turnResolution_1.resolveTurnAfterModelResponse)(state._currentAgent, state._originalInput, state._generatedItems, state._lastTurnResponse, state._lastProcessedResponse, this, state);
335
+ (0, runLoop_1.applyTurnResult)({
336
+ state,
337
+ turnResult,
338
+ agent: state._currentAgent,
339
+ toolsUsed: state._lastProcessedResponse?.toolsUsed ?? [],
340
+ resetTurnPersistence: !isResumedState,
341
+ });
342
+ await guardrailTracker.awaitCompletion();
502
343
  }
503
- else {
344
+ const currentStep = state._currentStep;
345
+ if (!currentStep) {
504
346
  logger_1.default.debug('Running next loop');
347
+ continue;
348
+ }
349
+ switch (currentStep.type) {
350
+ case 'next_step_final_output':
351
+ await (0, guardrails_1.runOutputGuardrails)(state, this.outputGuardrailDefs, currentStep.output);
352
+ state._currentTurnInProgress = false;
353
+ this.emit('agent_end', state._context, state._currentAgent, currentStep.output);
354
+ state._currentAgent.emit('agent_end', state._context, currentStep.output);
355
+ return new result_1.RunResult(state);
356
+ case 'next_step_handoff':
357
+ state.setCurrentAgent(currentStep.newAgent);
358
+ if (state._currentAgentSpan) {
359
+ state._currentAgentSpan.end();
360
+ (0, context_1.resetCurrentSpan)();
361
+ state.setCurrentAgentSpan(undefined);
362
+ }
363
+ state._noActiveAgentRun = true;
364
+ state._currentTurnInProgress = false;
365
+ // We've processed the handoff, so we need to run the loop again.
366
+ state._currentStep = { type: 'next_step_run_again' };
367
+ break;
368
+ case 'next_step_interruption':
369
+ // Interrupted. Don't run any guardrails.
370
+ return new result_1.RunResult(state);
371
+ case 'next_step_run_again':
372
+ state._currentTurnInProgress = false;
373
+ logger_1.default.debug('Running next loop');
374
+ break;
375
+ default:
376
+ logger_1.default.debug('Running next loop');
505
377
  }
506
378
  }
507
379
  }
508
380
  catch (err) {
381
+ state._currentTurnInProgress = false;
509
382
  if (state._currentAgentSpan) {
510
383
  state._currentAgentSpan.setError({
511
384
  message: 'Error in agent run',
@@ -536,16 +409,22 @@ class Runner extends lifecycle_1.RunHooks {
536
409
  /**
537
410
  * @internal
538
411
  */
539
- async #runStreamLoop(result, options, isResumedState, ensureStreamInputPersisted, sessionInputUpdate) {
540
- const serverManagesConversation = Boolean(options.conversationId) || Boolean(options.previousResponseId);
412
+ async #runStreamLoop(result, options, isResumedState, ensureStreamInputPersisted, sessionInputUpdate, preserveTurnPersistenceOnResume) {
413
+ const resolvedConversationId = options.conversationId ?? result.state._conversationId;
414
+ const resolvedPreviousResponseId = options.previousResponseId ?? result.state._previousResponseId;
415
+ const serverManagesConversation = Boolean(resolvedConversationId) || Boolean(resolvedPreviousResponseId);
541
416
  const serverConversationTracker = serverManagesConversation
542
- ? new ServerConversationTracker({
543
- conversationId: options.conversationId,
544
- previousResponseId: options.previousResponseId,
417
+ ? new conversation_1.ServerConversationTracker({
418
+ conversationId: resolvedConversationId,
419
+ previousResponseId: resolvedPreviousResponseId,
545
420
  })
546
421
  : undefined;
547
- let handedInputToModel = false;
422
+ if (serverConversationTracker) {
423
+ result.state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
424
+ }
425
+ let sentInputToModel = false;
548
426
  let streamInputPersisted = false;
427
+ let guardrailTracker = (0, guardrails_1.createGuardrailTracker)();
549
428
  const persistStreamInputIfNeeded = async () => {
550
429
  if (streamInputPersisted || !ensureStreamInputPersisted) {
551
430
  return;
@@ -554,13 +433,27 @@ class Runner extends lifecycle_1.RunHooks {
554
433
  await ensureStreamInputPersisted();
555
434
  streamInputPersisted = true;
556
435
  };
436
+ let parallelGuardrailPromise;
437
+ const awaitGuardrailsAndPersistInput = async () => {
438
+ await guardrailTracker.awaitCompletion();
439
+ if (guardrailTracker.failed) {
440
+ throw guardrailTracker.error;
441
+ }
442
+ if (sentInputToModel &&
443
+ !streamInputPersisted &&
444
+ !guardrailTracker.failed) {
445
+ await persistStreamInputIfNeeded();
446
+ }
447
+ };
557
448
  if (serverConversationTracker && isResumedState) {
558
449
  serverConversationTracker.primeFromState({
559
450
  originalInput: result.state._originalInput,
560
451
  generatedItems: result.state._generatedItems,
561
452
  modelResponses: result.state._modelResponses,
562
453
  });
454
+ result.state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
563
455
  }
456
+ // Tracks when we resume an approval interruption so the next run-again step stays in the same turn.
564
457
  let continuingInterruptedTurn = false;
565
458
  try {
566
459
  while (true) {
@@ -574,119 +467,149 @@ class Runner extends lifecycle_1.RunHooks {
574
467
  !result.state._lastProcessedResponse) {
575
468
  throw new errors_1.UserError('No model response found in previous state', result.state);
576
469
  }
577
- const turnResult = await (0, runImplementation_1.resolveInterruptedTurn)(result.state._currentAgent, result.state._originalInput, result.state._generatedItems, result.state._lastTurnResponse, result.state._lastProcessedResponse, this, result.state);
578
- (0, runImplementation_1.addStepToRunResult)(result, turnResult);
579
- result.state._toolUseTracker.addToolUse(result.state._currentAgent, result.state._lastProcessedResponse.toolsUsed);
580
- result.state._originalInput = turnResult.originalInput;
581
- result.state._generatedItems = turnResult.generatedItems;
470
+ const interruptedOutcome = await (0, runLoop_1.resumeInterruptedTurn)({
471
+ state: result.state,
472
+ runner: this,
473
+ onStepItems: (turnResult) => {
474
+ (0, streaming_1.addStepToRunResult)(result, turnResult);
475
+ },
476
+ });
582
477
  // Don't reset counter here - resolveInterruptedTurn already adjusted it via rewind logic
583
478
  // The counter will be reset when _currentTurn is incremented (starting a new turn)
584
- if (turnResult.nextStep.type === 'next_step_interruption') {
479
+ const { shouldReturn, shouldContinue } = (0, runLoop_1.handleInterruptedOutcome)({
480
+ state: result.state,
481
+ outcome: interruptedOutcome,
482
+ setContinuingInterruptedTurn: (value) => {
483
+ continuingInterruptedTurn = value;
484
+ },
485
+ });
486
+ if (shouldReturn) {
585
487
  // we are still in an interruption, so we need to avoid an infinite loop
586
- result.state._currentStep = turnResult.nextStep;
587
488
  return;
588
489
  }
589
- // If continuing from interruption with next_step_run_again, set step to undefined
590
- // so the loop treats it as a new step without incrementing the turn.
591
- // The counter has already been adjusted by resolveInterruptedTurn's rewind logic.
592
- if (turnResult.nextStep.type === 'next_step_run_again') {
593
- continuingInterruptedTurn = true;
594
- result.state._currentStep = undefined;
490
+ if (shouldContinue) {
595
491
  continue;
596
492
  }
597
- continuingInterruptedTurn = false;
598
- result.state._currentStep = turnResult.nextStep;
599
493
  }
600
494
  if (result.state._currentStep.type === 'next_step_run_again') {
601
- const artifacts = await prepareAgentArtifacts(result.state);
602
- const isResumingFromInterruption = isResumedState && continuingInterruptedTurn;
495
+ parallelGuardrailPromise = undefined;
496
+ guardrailTracker = (0, guardrails_1.createGuardrailTracker)();
497
+ const wasContinuingInterruptedTurn = continuingInterruptedTurn;
603
498
  continuingInterruptedTurn = false;
604
- // Do not advance the turn when resuming from an interruption; the next model call is
605
- // still part of the same logical turn.
606
- if (!isResumingFromInterruption) {
607
- result.state._currentTurn++;
608
- result.state._currentTurnPersistedItemCount = 0;
609
- }
610
- if (result.state._currentTurn > result.state._maxTurns) {
611
- result.state._currentAgentSpan?.setError({
612
- message: 'Max turns exceeded',
613
- data: { max_turns: result.state._maxTurns },
499
+ const previousTurn = result.state._currentTurn;
500
+ const previousPersistedCount = result.state._currentTurnPersistedItemCount;
501
+ const previousGeneratedCount = result.state._generatedItems.length;
502
+ const preparedTurn = await (0, turnPreparation_1.prepareTurn)({
503
+ state: result.state,
504
+ input: result.input,
505
+ generatedItems: result.newItems,
506
+ isResumedState,
507
+ preserveTurnPersistenceOnResume,
508
+ continuingInterruptedTurn: wasContinuingInterruptedTurn,
509
+ serverConversationTracker,
510
+ inputGuardrailDefs: this.inputGuardrailDefs,
511
+ guardrailHandlers: {
512
+ onParallelStart: () => {
513
+ guardrailTracker.markPending();
514
+ },
515
+ onParallelError: (err) => {
516
+ guardrailTracker.setError(err);
517
+ },
518
+ },
519
+ emitAgentStart: (context, agent, inputItems) => {
520
+ this.emit('agent_start', context, agent, inputItems);
521
+ },
522
+ });
523
+ if (preserveTurnPersistenceOnResume &&
524
+ result.state._currentTurn > previousTurn &&
525
+ previousPersistedCount <= previousGeneratedCount) {
526
+ // Preserve persisted offsets from a resumed run to avoid re-saving prior items.
527
+ result.state._currentTurnPersistedItemCount =
528
+ previousPersistedCount;
529
+ }
530
+ const { artifacts, turnInput } = preparedTurn;
531
+ parallelGuardrailPromise = preparedTurn.parallelGuardrailPromise;
532
+ guardrailTracker.setPromise(parallelGuardrailPromise);
533
+ // If guardrails are still running, defer input persistence until they finish.
534
+ const delayStreamInputPersistence = guardrailTracker.pending;
535
+ const preparedCall = await this.#prepareModelCall(result.state, options, artifacts, turnInput, serverConversationTracker, sessionInputUpdate);
536
+ guardrailTracker.throwIfError();
537
+ let finalResponse = undefined;
538
+ let inputMarked = false;
539
+ const markInputOnce = () => {
540
+ if (inputMarked || !serverConversationTracker) {
541
+ return;
542
+ }
543
+ // We only mark inputs as sent after receiving the first stream event,
544
+ // which is the earliest reliable confirmation that the server accepted
545
+ // the request. If the stream fails before any events, leave inputs
546
+ // unmarked so a retry can resend safely.
547
+ // Record the exact input that was sent so the server tracker can advance safely.
548
+ serverConversationTracker.markInputAsSent(preparedCall.sourceItems, {
549
+ filterApplied: preparedCall.filterApplied,
550
+ allTurnItems: preparedCall.turnInput,
614
551
  });
615
- throw new errors_1.MaxTurnsExceededError(`Max turns (${result.state._maxTurns}) exceeded`, result.state);
552
+ inputMarked = true;
553
+ };
554
+ sentInputToModel = true;
555
+ if (!delayStreamInputPersistence) {
556
+ await persistStreamInputIfNeeded();
616
557
  }
617
- logger_1.default.debug(`Running agent ${currentAgent.name} (turn ${result.state._currentTurn})`);
618
- let guardrailError;
619
- let parallelGuardrailPromise;
620
- // Only run input guardrails on the first turn of a new run.
621
- if (result.state._currentTurn === 1 && !isResumingFromInterruption) {
622
- const guardrails = this.#splitInputGuardrails(result.state);
623
- if (guardrails.blocking.length > 0) {
624
- await this.#runInputGuardrails(result.state, guardrails.blocking);
625
- }
626
- if (guardrails.parallel.length > 0) {
627
- const promise = this.#runInputGuardrails(result.state, guardrails.parallel);
628
- parallelGuardrailPromise = promise.catch((err) => {
629
- guardrailError = err;
630
- return [];
631
- });
558
+ try {
559
+ for await (const event of preparedCall.model.getStreamedResponse({
560
+ systemInstructions: preparedCall.modelInput.instructions,
561
+ prompt: preparedCall.prompt,
562
+ // Streaming requests should also honor explicitly chosen models.
563
+ ...(preparedCall.explictlyModelSet
564
+ ? { overridePromptModel: true }
565
+ : {}),
566
+ input: preparedCall.modelInput.input,
567
+ previousResponseId: preparedCall.previousResponseId,
568
+ conversationId: preparedCall.conversationId,
569
+ modelSettings: preparedCall.modelSettings,
570
+ tools: preparedCall.serializedTools,
571
+ toolsExplicitlyProvided: preparedCall.toolsExplicitlyProvided,
572
+ handoffs: preparedCall.serializedHandoffs,
573
+ outputType: (0, tools_1.convertAgentOutputTypeToSerializable)(currentAgent.outputType),
574
+ tracing: (0, tracing_1.getTracing)(this.config.tracingDisabled, this.config.traceIncludeSensitiveData),
575
+ signal: options.signal,
576
+ })) {
577
+ guardrailTracker.throwIfError();
578
+ markInputOnce();
579
+ if (event.type === 'response_done') {
580
+ const parsed = protocol_1.StreamEventResponseCompleted.parse(event);
581
+ finalResponse = {
582
+ usage: new usage_1.Usage(parsed.response.usage),
583
+ output: parsed.response.output,
584
+ responseId: parsed.response.id,
585
+ };
586
+ result.state._context.usage.add(finalResponse.usage);
587
+ }
588
+ if (result.cancelled) {
589
+ // When the user's code exits a loop to consume the stream, we need to break
590
+ // this loop to prevent internal false errors and unnecessary processing
591
+ await awaitGuardrailsAndPersistInput();
592
+ return;
593
+ }
594
+ result._addItem(new events_1.RunRawModelStreamEvent(event));
632
595
  }
633
596
  }
634
- const turnInput = serverConversationTracker
635
- ? serverConversationTracker.prepareInput(result.input, result.newItems)
636
- : getTurnInput(result.input, result.newItems);
637
- if (result.state._noActiveAgentRun) {
638
- currentAgent.emit('agent_start', result.state._context, currentAgent, turnInput);
639
- this.emit('agent_start', result.state._context, currentAgent, turnInput);
640
- }
641
- let finalResponse = undefined;
642
- const preparedCall = await this.#prepareModelCall(result.state, options, artifacts, turnInput, serverConversationTracker, sessionInputUpdate);
643
- if (guardrailError) {
644
- throw guardrailError;
645
- }
646
- handedInputToModel = true;
647
- await persistStreamInputIfNeeded();
648
- for await (const event of preparedCall.model.getStreamedResponse({
649
- systemInstructions: preparedCall.modelInput.instructions,
650
- prompt: preparedCall.prompt,
651
- // Streaming requests should also honor explicitly chosen models.
652
- ...(preparedCall.explictlyModelSet
653
- ? { overridePromptModel: true }
654
- : {}),
655
- input: preparedCall.modelInput.input,
656
- previousResponseId: preparedCall.previousResponseId,
657
- conversationId: preparedCall.conversationId,
658
- modelSettings: preparedCall.modelSettings,
659
- tools: preparedCall.serializedTools,
660
- toolsExplicitlyProvided: preparedCall.toolsExplicitlyProvided,
661
- handoffs: preparedCall.serializedHandoffs,
662
- outputType: (0, tools_1.convertAgentOutputTypeToSerializable)(currentAgent.outputType),
663
- tracing: getTracing(this.config.tracingDisabled, this.config.traceIncludeSensitiveData),
664
- signal: options.signal,
665
- })) {
666
- if (guardrailError) {
667
- throw guardrailError;
668
- }
669
- if (event.type === 'response_done') {
670
- const parsed = protocol_1.StreamEventResponseCompleted.parse(event);
671
- finalResponse = {
672
- usage: new usage_1.Usage(parsed.response.usage),
673
- output: parsed.response.output,
674
- responseId: parsed.response.id,
675
- };
676
- result.state._context.usage.add(finalResponse.usage);
677
- }
678
- if (result.cancelled) {
679
- // When the user's code exits a loop to consume the stream, we need to break
680
- // this loop to prevent internal false errors and unnecessary processing
597
+ catch (error) {
598
+ if ((0, streaming_1.isAbortError)(error)) {
599
+ if (sentInputToModel) {
600
+ markInputOnce();
601
+ }
602
+ await awaitGuardrailsAndPersistInput();
681
603
  return;
682
604
  }
683
- result._addItem(new events_1.RunRawModelStreamEvent(event));
605
+ throw error;
684
606
  }
685
- if (parallelGuardrailPromise) {
686
- await parallelGuardrailPromise;
687
- if (guardrailError) {
688
- throw guardrailError;
689
- }
607
+ if (finalResponse) {
608
+ markInputOnce();
609
+ }
610
+ await awaitGuardrailsAndPersistInput();
611
+ if (result.cancelled) {
612
+ return;
690
613
  }
691
614
  result.state._noActiveAgentRun = false;
692
615
  if (!finalResponse) {
@@ -695,68 +618,82 @@ class Runner extends lifecycle_1.RunHooks {
695
618
  result.state._lastTurnResponse = finalResponse;
696
619
  // Keep the tracker in sync with the streamed response so reconnections remain accurate.
697
620
  serverConversationTracker?.trackServerItems(finalResponse);
621
+ if (serverConversationTracker) {
622
+ result.state.setConversationContext(serverConversationTracker.conversationId, serverConversationTracker.previousResponseId);
623
+ }
698
624
  result.state._modelResponses.push(result.state._lastTurnResponse);
699
- const processedResponse = (0, runImplementation_1.processModelResponse)(result.state._lastTurnResponse, currentAgent, preparedCall.tools, preparedCall.handoffs);
625
+ const processedResponse = (0, modelOutputs_1.processModelResponse)(result.state._lastTurnResponse, currentAgent, preparedCall.tools, preparedCall.handoffs);
700
626
  result.state._lastProcessedResponse = processedResponse;
701
627
  // Record the items emitted directly from the model response so we do not
702
628
  // stream them again after tools and other side effects finish.
703
629
  const preToolItems = new Set(processedResponse.newItems);
704
630
  if (preToolItems.size > 0) {
705
- (0, runImplementation_1.streamStepItemsToRunResult)(result, processedResponse.newItems);
706
- }
707
- const turnResult = await (0, runImplementation_1.resolveTurnAfterModelResponse)(currentAgent, result.state._originalInput, result.state._generatedItems, result.state._lastTurnResponse, result.state._lastProcessedResponse, this, result.state);
708
- (0, runImplementation_1.addStepToRunResult)(result, turnResult, {
709
- skipItems: preToolItems,
631
+ (0, streaming_1.streamStepItemsToRunResult)(result, processedResponse.newItems);
632
+ }
633
+ const turnResult = await (0, turnResolution_1.resolveTurnAfterModelResponse)(currentAgent, result.state._originalInput, result.state._generatedItems, result.state._lastTurnResponse, result.state._lastProcessedResponse, this, result.state);
634
+ (0, runLoop_1.applyTurnResult)({
635
+ state: result.state,
636
+ turnResult,
637
+ agent: currentAgent,
638
+ toolsUsed: processedResponse.toolsUsed,
639
+ resetTurnPersistence: !isResumedState,
640
+ onStepItems: (step) => {
641
+ (0, streaming_1.addStepToRunResult)(result, step, { skipItems: preToolItems });
642
+ },
710
643
  });
711
- result.state._toolUseTracker.addToolUse(currentAgent, processedResponse.toolsUsed);
712
- result.state._originalInput = turnResult.originalInput;
713
- result.state._generatedItems = turnResult.generatedItems;
714
- if (turnResult.nextStep.type === 'next_step_run_again') {
715
- result.state._currentTurnPersistedItemCount = 0;
716
- }
717
- result.state._currentStep = turnResult.nextStep;
718
644
  }
719
- if (result.state._currentStep.type === 'next_step_final_output') {
720
- await this.#runOutputGuardrails(result.state, result.state._currentStep.output);
721
- await persistStreamInputIfNeeded();
722
- // Guardrails must succeed before persisting session memory to avoid storing blocked outputs.
723
- if (!serverManagesConversation) {
724
- await (0, runImplementation_1.saveStreamResultToSession)(options.session, result);
725
- }
726
- this.emit('agent_end', result.state._context, currentAgent, result.state._currentStep.output);
727
- currentAgent.emit('agent_end', result.state._context, result.state._currentStep.output);
728
- return;
729
- }
730
- else if (result.state._currentStep.type === 'next_step_interruption') {
731
- // we are done for now. Don't run any output guardrails
732
- await persistStreamInputIfNeeded();
733
- if (!serverManagesConversation) {
734
- await (0, runImplementation_1.saveStreamResultToSession)(options.session, result);
735
- }
736
- return;
737
- }
738
- else if (result.state._currentStep.type === 'next_step_handoff') {
739
- result.state._currentAgent = result.state._currentStep
740
- ?.newAgent;
741
- if (result.state._currentAgentSpan) {
742
- result.state._currentAgentSpan.end();
743
- (0, context_1.resetCurrentSpan)();
744
- }
745
- result.state._currentAgentSpan = undefined;
746
- result._addItem(new events_1.RunAgentUpdatedStreamEvent(result.state._currentAgent));
747
- result.state._noActiveAgentRun = true;
748
- // we've processed the handoff, so we need to run the loop again
749
- result.state._currentStep = {
750
- type: 'next_step_run_again',
751
- };
752
- }
753
- else {
754
- logger_1.default.debug('Running next loop');
645
+ const currentStep = result.state._currentStep;
646
+ switch (currentStep.type) {
647
+ case 'next_step_final_output':
648
+ await (0, guardrails_1.runOutputGuardrails)(result.state, this.outputGuardrailDefs, currentStep.output);
649
+ result.state._currentTurnInProgress = false;
650
+ await persistStreamInputIfNeeded();
651
+ // Guardrails must succeed before persisting session memory to avoid storing blocked outputs.
652
+ if (!serverManagesConversation) {
653
+ await (0, sessionPersistence_1.saveStreamResultToSession)(options.session, result);
654
+ }
655
+ this.emit('agent_end', result.state._context, currentAgent, currentStep.output);
656
+ currentAgent.emit('agent_end', result.state._context, currentStep.output);
657
+ return;
658
+ case 'next_step_interruption':
659
+ // We are done for now. Don't run any output guardrails.
660
+ await persistStreamInputIfNeeded();
661
+ if (!serverManagesConversation) {
662
+ await (0, sessionPersistence_1.saveStreamResultToSession)(options.session, result);
663
+ }
664
+ return;
665
+ case 'next_step_handoff':
666
+ result.state.setCurrentAgent(currentStep.newAgent);
667
+ if (result.state._currentAgentSpan) {
668
+ result.state._currentAgentSpan.end();
669
+ (0, context_1.resetCurrentSpan)();
670
+ }
671
+ result.state.setCurrentAgentSpan(undefined);
672
+ result._addItem(new events_1.RunAgentUpdatedStreamEvent(result.state._currentAgent));
673
+ result.state._noActiveAgentRun = true;
674
+ result.state._currentTurnInProgress = false;
675
+ // We've processed the handoff, so we need to run the loop again.
676
+ result.state._currentStep = {
677
+ type: 'next_step_run_again',
678
+ };
679
+ break;
680
+ case 'next_step_run_again':
681
+ result.state._currentTurnInProgress = false;
682
+ logger_1.default.debug('Running next loop');
683
+ break;
684
+ default:
685
+ logger_1.default.debug('Running next loop');
755
686
  }
756
687
  }
757
688
  }
758
689
  catch (error) {
759
- if (handedInputToModel && !streamInputPersisted) {
690
+ result.state._currentTurnInProgress = false;
691
+ if (guardrailTracker.pending) {
692
+ await guardrailTracker.awaitCompletion({ suppressErrors: true });
693
+ }
694
+ if (sentInputToModel &&
695
+ !streamInputPersisted &&
696
+ !guardrailTracker.failed) {
760
697
  await persistStreamInputIfNeeded();
761
698
  }
762
699
  if (result.state._currentAgentSpan) {
@@ -768,6 +705,14 @@ class Runner extends lifecycle_1.RunHooks {
768
705
  throw error;
769
706
  }
770
707
  finally {
708
+ if (guardrailTracker.pending) {
709
+ await guardrailTracker.awaitCompletion({ suppressErrors: true });
710
+ }
711
+ if (sentInputToModel &&
712
+ !streamInputPersisted &&
713
+ !guardrailTracker.failed) {
714
+ await persistStreamInputIfNeeded();
715
+ }
771
716
  if (result.state._currentStep?.type !== 'next_step_interruption') {
772
717
  try {
773
718
  await (0, tool_1.disposeResolvedComputers)({ runContext: result.state._context });
@@ -787,7 +732,7 @@ class Runner extends lifecycle_1.RunHooks {
787
732
  /**
788
733
  * @internal
789
734
  */
790
- async #runIndividualStream(agent, input, options, ensureStreamInputPersisted, sessionInputUpdate) {
735
+ async #runIndividualStream(agent, input, options, ensureStreamInputPersisted, sessionInputUpdate, preserveTurnPersistenceOnResume) {
791
736
  options = options ?? {};
792
737
  return (0, context_1.withNewSpanContext)(async () => {
793
738
  // Initialize or reuse existing state
@@ -796,16 +741,27 @@ class Runner extends lifecycle_1.RunHooks {
796
741
  ? input
797
742
  : new runState_1.RunState(options.context instanceof runContext_1.RunContext
798
743
  ? options.context
799
- : new runContext_1.RunContext(options.context), input, agent, options.maxTurns ?? DEFAULT_MAX_TURNS);
744
+ : new runContext_1.RunContext(options.context), input, agent, options.maxTurns ?? constants_1.DEFAULT_MAX_TURNS);
745
+ const resolvedConversationId = options.conversationId ??
746
+ (isResumedState ? state._conversationId : undefined);
747
+ const resolvedPreviousResponseId = options.previousResponseId ??
748
+ (isResumedState ? state._previousResponseId : undefined);
749
+ if (!isResumedState) {
750
+ state.setConversationContext(resolvedConversationId, resolvedPreviousResponseId);
751
+ }
800
752
  // Initialize the streamed result with existing state
801
753
  const result = new result_1.StreamedRunResult({
802
754
  signal: options.signal,
803
755
  state,
804
756
  });
757
+ const streamOptions = {
758
+ ...options,
759
+ signal: result._getAbortSignal(),
760
+ };
805
761
  // Setup defaults
806
- result.maxTurns = options.maxTurns ?? state._maxTurns;
762
+ result.maxTurns = streamOptions.maxTurns ?? state._maxTurns;
807
763
  // Continue the stream loop without blocking
808
- const streamLoopPromise = this.#runStreamLoop(result, options, isResumedState, ensureStreamInputPersisted, sessionInputUpdate).then(() => {
764
+ const streamLoopPromise = this.#runStreamLoop(result, streamOptions, isResumedState, ensureStreamInputPersisted, sessionInputUpdate, preserveTurnPersistenceOnResume).then(() => {
809
765
  result._done();
810
766
  }, (err) => {
811
767
  result._raiseError(err);
@@ -815,108 +771,22 @@ class Runner extends lifecycle_1.RunHooks {
815
771
  return result;
816
772
  });
817
773
  }
818
- async #runInputGuardrails(state, guardrailsOverride) {
819
- const guardrails = guardrailsOverride ?? this.#getInputGuardrailDefinitions(state);
820
- if (guardrails.length > 0) {
821
- const guardrailArgs = {
822
- agent: state._currentAgent,
823
- input: state._originalInput,
824
- context: state._context,
825
- };
826
- try {
827
- const results = await Promise.all(guardrails.map(async (guardrail) => {
828
- return (0, tracing_1.withGuardrailSpan)(async (span) => {
829
- const result = await guardrail.run(guardrailArgs);
830
- span.spanData.triggered = result.output.tripwireTriggered;
831
- return result;
832
- }, { data: { name: guardrail.name } }, state._currentAgentSpan);
833
- }));
834
- state._inputGuardrailResults.push(...results);
835
- for (const result of results) {
836
- if (result.output.tripwireTriggered) {
837
- if (state._currentAgentSpan) {
838
- state._currentAgentSpan.setError({
839
- message: 'Guardrail tripwire triggered',
840
- data: { guardrail: result.guardrail.name },
841
- });
842
- }
843
- throw new errors_1.InputGuardrailTripwireTriggered(`Input guardrail triggered: ${JSON.stringify(result.output.outputInfo)}`, result, state);
844
- }
845
- }
846
- return results;
847
- }
848
- catch (e) {
849
- if (e instanceof errors_1.InputGuardrailTripwireTriggered) {
850
- throw e;
851
- }
852
- // roll back the current turn to enable reruns
853
- state._currentTurn--;
854
- throw new errors_1.GuardrailExecutionError(`Input guardrail failed to complete: ${e}`, e, state);
855
- }
856
- }
857
- return [];
858
- }
859
- async #runOutputGuardrails(state, output) {
860
- const guardrails = this.outputGuardrailDefs.concat(state._currentAgent.outputGuardrails.map(guardrail_1.defineOutputGuardrail));
861
- if (guardrails.length > 0) {
862
- const agentOutput = state._currentAgent.processFinalOutput(output);
863
- const runOutput = getTurnInput([], state._generatedItems);
864
- const guardrailArgs = {
865
- agent: state._currentAgent,
866
- agentOutput,
867
- context: state._context,
868
- details: {
869
- modelResponse: state._lastTurnResponse,
870
- output: runOutput,
871
- },
872
- };
873
- try {
874
- const results = await Promise.all(guardrails.map(async (guardrail) => {
875
- return (0, tracing_1.withGuardrailSpan)(async (span) => {
876
- const result = await guardrail.run(guardrailArgs);
877
- span.spanData.triggered = result.output.tripwireTriggered;
878
- return result;
879
- }, { data: { name: guardrail.name } }, state._currentAgentSpan);
880
- }));
881
- for (const result of results) {
882
- if (result.output.tripwireTriggered) {
883
- if (state._currentAgentSpan) {
884
- state._currentAgentSpan.setError({
885
- message: 'Guardrail tripwire triggered',
886
- data: { guardrail: result.guardrail.name },
887
- });
888
- }
889
- throw new errors_1.OutputGuardrailTripwireTriggered(`Output guardrail triggered: ${JSON.stringify(result.output.outputInfo)}`, result, state);
890
- }
891
- }
892
- }
893
- catch (e) {
894
- if (e instanceof errors_1.OutputGuardrailTripwireTriggered) {
895
- throw e;
896
- }
897
- throw new errors_1.GuardrailExecutionError(`Output guardrail failed to complete: ${e}`, e, state);
898
- }
899
- }
900
- }
901
774
  /**
902
775
  * @internal
903
776
  * Applies call-level filters and merges session updates so the model request mirrors exactly
904
777
  * what we persisted for history.
905
778
  */
906
779
  async #prepareModelCall(state, options, artifacts, turnInput, serverConversationTracker, sessionInputUpdate) {
907
- const { model, explictlyModelSet } = await this.#resolveModelForAgent(state._currentAgent);
780
+ const { model, explictlyModelSet, resolvedModelName } = await this.#resolveModelForAgent(state._currentAgent);
908
781
  let modelSettings = {
909
782
  ...this.config.modelSettings,
910
783
  ...state._currentAgent.modelSettings,
911
784
  };
912
- modelSettings = adjustModelSettingsForNonGPT5RunnerModel(explictlyModelSet, state._currentAgent.modelSettings, model, modelSettings);
913
- modelSettings = (0, runImplementation_1.maybeResetToolChoice)(state._currentAgent, state._toolUseTracker, modelSettings);
785
+ modelSettings = (0, modelSettings_1.adjustModelSettingsForNonGPT5RunnerModel)(explictlyModelSet, state._currentAgent.modelSettings, model, modelSettings, resolvedModelName);
786
+ modelSettings = (0, modelSettings_1.maybeResetToolChoice)(state._currentAgent, state._toolUseTracker, modelSettings);
914
787
  const systemInstructions = await state._currentAgent.getSystemPrompt(state._context);
915
788
  const prompt = await state._currentAgent.getPrompt(state._context);
916
- const { modelInput, sourceItems, persistedItems, filterApplied } = await applyCallModelInputFilter(state._currentAgent, options.callModelInputFilter, state._context, turnInput, systemInstructions);
917
- // Inform the tracker which exact original objects made it to the provider so future turns
918
- // only send the delta that has not yet been acknowledged by the server.
919
- serverConversationTracker?.markInputAsSent(sourceItems);
789
+ const { modelInput, sourceItems, persistedItems, filterApplied } = await (0, conversation_1.applyCallModelInputFilter)(state._currentAgent, options.callModelInputFilter, state._context, turnInput, systemInstructions);
920
790
  // Provide filtered clones whenever filters run so session history mirrors the model payload.
921
791
  // Returning an empty array is intentional: it tells the session layer to persist "nothing"
922
792
  // instead of falling back to the unfiltered originals when the filter redacts everything.
@@ -933,462 +803,19 @@ class Runner extends lifecycle_1.RunHooks {
933
803
  prompt,
934
804
  previousResponseId,
935
805
  conversationId,
936
- };
937
- }
938
- }
939
- exports.Runner = Runner;
940
- /**
941
- * Constructs the model input array for the current turn by combining the original turn input with
942
- * any new run items (excluding tool approval placeholders). This helps ensure that repeated calls
943
- * to the Responses API only send newly generated content.
944
- *
945
- * See: https://platform.openai.com/docs/guides/conversation-state?api-mode=responses.
946
- */
947
- function getTurnInput(originalInput, generatedItems) {
948
- const rawItems = generatedItems
949
- .filter((item) => item.type !== 'tool_approval_item') // don't include approval items to avoid double function calls
950
- .map((item) => item.rawItem);
951
- return [...toAgentInputList(originalInput), ...rawItems];
952
- }
953
- // --------------------------------------------------------------
954
- // Internal helpers
955
- // --------------------------------------------------------------
956
- const DEFAULT_MAX_TURNS = 10;
957
- let _defaultRunner = undefined;
958
- function getDefaultRunner() {
959
- if (_defaultRunner) {
960
- return _defaultRunner;
961
- }
962
- _defaultRunner = new Runner();
963
- return _defaultRunner;
964
- }
965
- /**
966
- * Resolves the effective model for the next turn by giving precedence to the agent-specific
967
- * configuration when present, otherwise falling back to the runner-level default.
968
- */
969
- function selectModel(agentModel, runConfigModel) {
970
- // When initializing an agent without model name, the model property is set to an empty string. So,
971
- // * agentModel === Agent.DEFAULT_MODEL_PLACEHOLDER & runConfigModel exists, runConfigModel will be used
972
- // * agentModel is set, the agentModel will be used over runConfigModel
973
- if ((typeof agentModel === 'string' &&
974
- agentModel !== agent_1.Agent.DEFAULT_MODEL_PLACEHOLDER) ||
975
- agentModel // any truthy value
976
- ) {
977
- return agentModel;
978
- }
979
- return runConfigModel ?? agentModel ?? agent_1.Agent.DEFAULT_MODEL_PLACEHOLDER;
980
- }
981
- /**
982
- * Normalizes tracing configuration into the format expected by model providers.
983
- * Returns `false` to disable tracing, `true` to include full payload data, or
984
- * `'enabled_without_data'` to omit sensitive content while still emitting spans.
985
- */
986
- function getTracing(tracingDisabled, traceIncludeSensitiveData) {
987
- if (tracingDisabled) {
988
- return false;
989
- }
990
- if (traceIncludeSensitiveData) {
991
- return true;
992
- }
993
- return 'enabled_without_data';
994
- }
995
- /**
996
- * @internal
997
- */
998
- async function applyCallModelInputFilter(agent, callModelInputFilter, context, inputItems, systemInstructions) {
999
- const cloneInputItems = (items, map) => items.map((item) => {
1000
- const cloned = structuredClone(item);
1001
- if (map && cloned && typeof cloned === 'object') {
1002
- map.set(cloned, item);
1003
- }
1004
- return cloned;
1005
- });
1006
- // Record the relationship between the cloned array passed to filters and the original inputs.
1007
- const cloneMap = new WeakMap();
1008
- const originalPool = buildAgentInputPool(inputItems);
1009
- const fallbackOriginals = [];
1010
- // Track any original object inputs so filtered replacements can still mark them as delivered.
1011
- for (const item of inputItems) {
1012
- if (item && typeof item === 'object') {
1013
- fallbackOriginals.push(item);
1014
- }
1015
- }
1016
- const removeFromFallback = (candidate) => {
1017
- if (!candidate || typeof candidate !== 'object') {
1018
- return;
1019
- }
1020
- const index = fallbackOriginals.findIndex((original) => original === candidate);
1021
- if (index !== -1) {
1022
- fallbackOriginals.splice(index, 1);
1023
- }
1024
- };
1025
- const takeFallbackOriginal = () => {
1026
- const next = fallbackOriginals.shift();
1027
- if (next) {
1028
- removeAgentInputFromPool(originalPool, next);
1029
- }
1030
- return next;
1031
- };
1032
- // Always create a deep copy so downstream mutations inside filters cannot affect
1033
- // the cached turn state.
1034
- const clonedBaseInput = cloneInputItems(inputItems, cloneMap);
1035
- const base = {
1036
- input: clonedBaseInput,
1037
- instructions: systemInstructions,
1038
- };
1039
- if (!callModelInputFilter) {
1040
- return {
1041
- modelInput: base,
1042
- sourceItems: [...inputItems],
1043
- persistedItems: [],
1044
- filterApplied: false,
1045
- };
1046
- }
1047
- try {
1048
- const result = await callModelInputFilter({
1049
- modelData: base,
1050
- agent,
1051
- context: context.context,
1052
- });
1053
- if (!result || !Array.isArray(result.input)) {
1054
- throw new errors_1.UserError('callModelInputFilter must return a ModelInputData object with an input array.');
1055
- }
1056
- // Preserve a pointer to the original object backing each filtered clone so downstream
1057
- // trackers can keep their bookkeeping consistent even after redaction.
1058
- const sourceItems = result.input.map((item) => {
1059
- if (!item || typeof item !== 'object') {
1060
- return undefined;
1061
- }
1062
- const original = cloneMap.get(item);
1063
- if (original) {
1064
- removeFromFallback(original);
1065
- removeAgentInputFromPool(originalPool, original);
1066
- return original;
1067
- }
1068
- const key = getAgentInputItemKey(item);
1069
- const matchedByContent = takeAgentInputFromPool(originalPool, key);
1070
- if (matchedByContent) {
1071
- removeFromFallback(matchedByContent);
1072
- return matchedByContent;
1073
- }
1074
- const fallback = takeFallbackOriginal();
1075
- if (fallback) {
1076
- return fallback;
1077
- }
1078
- return undefined;
1079
- });
1080
- const clonedFilteredInput = cloneInputItems(result.input);
1081
- return {
1082
- modelInput: {
1083
- input: clonedFilteredInput,
1084
- instructions: typeof result.instructions === 'undefined'
1085
- ? systemInstructions
1086
- : result.instructions,
1087
- },
1088
806
  sourceItems,
1089
- persistedItems: clonedFilteredInput.map((item) => structuredClone(item)),
1090
- filterApplied: true,
807
+ filterApplied,
808
+ turnInput,
1091
809
  };
1092
810
  }
1093
- catch (error) {
1094
- (0, context_1.addErrorToCurrentSpan)({
1095
- message: 'Error in callModelInputFilter',
1096
- data: { error: String(error) },
1097
- });
1098
- throw error;
1099
- }
1100
811
  }
1101
- // Tracks which items have already been sent to or received from the Responses API when the caller
1102
- // supplies `conversationId`/`previousResponseId`. This ensures we only send the delta each turn.
1103
- class ServerConversationTracker {
1104
- // Conversation ID:
1105
- // - https://platform.openai.com/docs/guides/conversation-state?api-mode=responses#using-the-conversations-api
1106
- // - https://platform.openai.com/docs/api-reference/conversations/create
1107
- conversationId;
1108
- // Previous Response ID:
1109
- // https://platform.openai.com/docs/guides/conversation-state?api-mode=responses#passing-context-from-the-previous-response
1110
- previousResponseId;
1111
- // Using this flag because WeakSet does not provide a way to check its size
1112
- sentInitialInput = false;
1113
- // The items already sent to the model; using WeakSet for memory efficiency
1114
- sentItems = new WeakSet();
1115
- // The items received from the server; using WeakSet for memory efficiency
1116
- serverItems = new WeakSet();
1117
- // Track initial input items that have not yet been sent so they can be retried on later turns.
1118
- remainingInitialInput = null;
1119
- constructor({ conversationId, previousResponseId, }) {
1120
- this.conversationId = conversationId ?? undefined;
1121
- this.previousResponseId = previousResponseId ?? undefined;
1122
- }
1123
- /**
1124
- * Pre-populates tracker caches from an existing RunState when resuming server-managed runs.
1125
- */
1126
- primeFromState({ originalInput, generatedItems, modelResponses, }) {
1127
- if (this.sentInitialInput) {
1128
- return;
1129
- }
1130
- for (const item of toAgentInputList(originalInput)) {
1131
- if (item && typeof item === 'object') {
1132
- this.sentItems.add(item);
1133
- }
1134
- }
1135
- this.sentInitialInput = true;
1136
- this.remainingInitialInput = null;
1137
- const latestResponse = modelResponses[modelResponses.length - 1];
1138
- for (const response of modelResponses) {
1139
- for (const item of response.output) {
1140
- if (item && typeof item === 'object') {
1141
- this.serverItems.add(item);
1142
- }
1143
- }
1144
- }
1145
- if (!this.conversationId && latestResponse?.responseId) {
1146
- this.previousResponseId = latestResponse.responseId;
1147
- }
1148
- for (const item of generatedItems) {
1149
- const rawItem = item.rawItem;
1150
- if (!rawItem || typeof rawItem !== 'object') {
1151
- continue;
1152
- }
1153
- if (this.serverItems.has(rawItem)) {
1154
- this.sentItems.add(rawItem);
1155
- }
1156
- }
1157
- }
1158
- /**
1159
- * Records the raw items returned by the server so future delta calculations skip them.
1160
- * Also captures the latest response identifier to chain follow-up calls when possible.
1161
- */
1162
- trackServerItems(modelResponse) {
1163
- if (!modelResponse) {
1164
- return;
1165
- }
1166
- for (const item of modelResponse.output) {
1167
- if (item && typeof item === 'object') {
1168
- this.serverItems.add(item);
1169
- }
1170
- }
1171
- if (!this.conversationId && modelResponse.responseId) {
1172
- this.previousResponseId = modelResponse.responseId;
1173
- }
1174
- }
1175
- /**
1176
- * Returns the minimum set of items that still need to be delivered to the server for the
1177
- * current turn. This includes the original turn inputs (until acknowledged) plus any
1178
- * newly generated items that have not yet been echoed back by the API.
1179
- */
1180
- prepareInput(originalInput, generatedItems) {
1181
- const inputItems = [];
1182
- if (!this.sentInitialInput) {
1183
- const initialItems = toAgentInputList(originalInput);
1184
- // Preserve the full initial payload so a filter can drop items without losing their originals.
1185
- inputItems.push(...initialItems);
1186
- this.remainingInitialInput = initialItems.filter((item) => Boolean(item) && typeof item === 'object');
1187
- this.sentInitialInput = true;
1188
- }
1189
- else if (this.remainingInitialInput &&
1190
- this.remainingInitialInput.length > 0) {
1191
- // Re-queue prior initial items until the tracker confirms they were delivered to the API.
1192
- inputItems.push(...this.remainingInitialInput);
1193
- }
1194
- for (const item of generatedItems) {
1195
- if (item.type === 'tool_approval_item') {
1196
- continue;
1197
- }
1198
- const rawItem = item.rawItem;
1199
- if (!rawItem || typeof rawItem !== 'object') {
1200
- continue;
1201
- }
1202
- if (this.sentItems.has(rawItem) || this.serverItems.has(rawItem)) {
1203
- continue;
1204
- }
1205
- inputItems.push(rawItem);
1206
- }
1207
- return inputItems;
1208
- }
1209
- /**
1210
- * Marks the provided originals as delivered so future turns do not resend them and any
1211
- * pending initial inputs can be dropped once the server acknowledges receipt.
1212
- */
1213
- markInputAsSent(items) {
1214
- if (!items.length) {
1215
- return;
1216
- }
1217
- const delivered = new Set();
1218
- for (const item of items) {
1219
- if (!item || typeof item !== 'object' || delivered.has(item)) {
1220
- continue;
1221
- }
1222
- // Some inputs may be repeated in the filtered list; only mark unique originals once.
1223
- delivered.add(item);
1224
- this.sentItems.add(item);
1225
- }
1226
- if (!this.remainingInitialInput ||
1227
- this.remainingInitialInput.length === 0) {
1228
- return;
1229
- }
1230
- this.remainingInitialInput = this.remainingInitialInput.filter((item) => !delivered.has(item));
1231
- if (this.remainingInitialInput.length === 0) {
1232
- this.remainingInitialInput = null;
1233
- }
1234
- }
1235
- }
1236
- /**
1237
- * When the default model is a GPT-5 variant, agents may carry GPT-5-specific providerData
1238
- * (e.g., reasoning effort, text verbosity). If a run resolves to a non-GPT-5 model and the
1239
- * agent relied on the default model (i.e., no explicit model set), these GPT-5-only settings
1240
- * are incompatible and should be stripped to avoid runtime errors.
1241
- */
1242
- function adjustModelSettingsForNonGPT5RunnerModel(explictlyModelSet, agentModelSettings, runnerModel, modelSettings) {
1243
- if (
1244
- // gpt-5 is enabled for the default model for agents
1245
- (0, defaultModel_1.isGpt5Default)() &&
1246
- // explicitly set model for the agent
1247
- explictlyModelSet &&
1248
- // this runner uses a non-gpt-5 model
1249
- (typeof runnerModel !== 'string' ||
1250
- !(0, defaultModel_1.gpt5ReasoningSettingsRequired)(runnerModel)) &&
1251
- (agentModelSettings.providerData?.reasoning ||
1252
- agentModelSettings.providerData?.text?.verbosity ||
1253
- agentModelSettings.providerData?.reasoning_effort)) {
1254
- const copiedModelSettings = { ...modelSettings };
1255
- // the incompatible parameters should be removed to avoid runtime errors
1256
- delete copiedModelSettings.providerData?.reasoning;
1257
- delete copiedModelSettings.providerData?.text?.verbosity;
1258
- delete copiedModelSettings.providerData?.reasoning_effort;
1259
- if (copiedModelSettings.reasoning) {
1260
- delete copiedModelSettings.reasoning.effort;
1261
- delete copiedModelSettings.reasoning.summary;
1262
- }
1263
- if (copiedModelSettings.text) {
1264
- delete copiedModelSettings.text.verbosity;
1265
- }
1266
- return copiedModelSettings;
1267
- }
1268
- return modelSettings;
1269
- }
1270
- /**
1271
- * @internal
1272
- * Collects tools/handoffs early so we can annotate spans before model execution begins.
1273
- */
1274
- async function prepareAgentArtifacts(state) {
1275
- const handoffs = await state._currentAgent.getEnabledHandoffs(state._context);
1276
- const tools = await state._currentAgent.getAllTools(state._context);
1277
- const computerTools = tools.filter((tool) => tool.type === 'computer');
1278
- if (computerTools.length > 0) {
1279
- await Promise.all(computerTools.map(async (tool) => {
1280
- await (0, tool_1.resolveComputer)({ tool, runContext: state._context });
1281
- }));
1282
- }
1283
- if (!state._currentAgentSpan) {
1284
- const handoffNames = handoffs.map((h) => h.agentName);
1285
- state._currentAgentSpan = (0, tracing_1.createAgentSpan)({
1286
- data: {
1287
- name: state._currentAgent.name,
1288
- handoffs: handoffNames,
1289
- tools: tools.map((t) => t.name),
1290
- output_type: state._currentAgent.outputSchemaName,
1291
- },
1292
- });
1293
- state._currentAgentSpan.start();
1294
- (0, context_1.setCurrentSpan)(state._currentAgentSpan);
1295
- }
1296
- else {
1297
- state._currentAgentSpan.spanData.tools = tools.map((t) => t.name);
1298
- }
1299
- return {
1300
- handoffs,
1301
- tools,
1302
- serializedHandoffs: handoffs.map((handoff) => (0, serialize_1.serializeHandoff)(handoff)),
1303
- serializedTools: tools.map((tool) => (0, serialize_1.serializeTool)(tool)),
1304
- toolsExplicitlyProvided: state._currentAgent.hasExplicitToolConfig(),
1305
- };
1306
- }
1307
- function getAgentInputItemKey(item) {
1308
- // Deep serialization keeps binary inputs comparable after filters clone them.
1309
- return JSON.stringify(item, agentInputSerializationReplacer);
1310
- }
1311
- function buildAgentInputPool(items) {
1312
- // Track every original object so filters can safely return cloned copies.
1313
- const pool = new Map();
1314
- for (const item of items) {
1315
- const key = getAgentInputItemKey(item);
1316
- const existing = pool.get(key);
1317
- if (existing) {
1318
- existing.push(item);
1319
- }
1320
- else {
1321
- pool.set(key, [item]);
1322
- }
1323
- }
1324
- return pool;
1325
- }
1326
- function takeAgentInputFromPool(pool, key) {
1327
- // Prefer reusing the earliest untouched original to keep ordering stable.
1328
- const candidates = pool.get(key);
1329
- if (!candidates || candidates.length === 0) {
1330
- return undefined;
1331
- }
1332
- const [first] = candidates;
1333
- candidates.shift();
1334
- if (candidates.length === 0) {
1335
- pool.delete(key);
1336
- }
1337
- return first;
1338
- }
1339
- function removeAgentInputFromPool(pool, item) {
1340
- // Remove exactly the matched instance so duplicate payloads remain available.
1341
- const key = getAgentInputItemKey(item);
1342
- const candidates = pool.get(key);
1343
- if (!candidates || candidates.length === 0) {
1344
- return;
1345
- }
1346
- const index = candidates.findIndex((candidate) => candidate === item);
1347
- if (index === -1) {
1348
- return;
1349
- }
1350
- candidates.splice(index, 1);
1351
- if (candidates.length === 0) {
1352
- pool.delete(key);
1353
- }
1354
- }
1355
- function agentInputSerializationReplacer(_key, value) {
1356
- // Mirror runImplementation serialization so buffer snapshots round-trip.
1357
- if (value instanceof ArrayBuffer) {
1358
- return {
1359
- __type: 'ArrayBuffer',
1360
- data: (0, base64_1.encodeUint8ArrayToBase64)(new Uint8Array(value)),
1361
- };
1362
- }
1363
- if ((0, smartString_1.isArrayBufferView)(value)) {
1364
- const view = value;
1365
- return {
1366
- __type: view.constructor.name,
1367
- data: (0, base64_1.encodeUint8ArrayToBase64)(new Uint8Array(view.buffer, view.byteOffset, view.byteLength)),
1368
- };
1369
- }
1370
- if ((0, smartString_1.isNodeBuffer)(value)) {
1371
- const view = value;
1372
- return {
1373
- __type: 'Buffer',
1374
- data: (0, base64_1.encodeUint8ArrayToBase64)(new Uint8Array(view.buffer, view.byteOffset, view.byteLength)),
1375
- };
1376
- }
1377
- if ((0, smartString_1.isSerializedBufferSnapshot)(value)) {
1378
- return {
1379
- __type: 'Buffer',
1380
- data: (0, base64_1.encodeUint8ArrayToBase64)(Uint8Array.from(value.data)),
1381
- };
1382
- }
1383
- return value;
1384
- }
1385
- // Normalizes user-provided input into the structure the model expects. Strings become user messages,
1386
- // arrays are kept as-is so downstream loops can treat both scenarios uniformly.
1387
- function toAgentInputList(originalInput) {
1388
- // Allow callers to pass plain strings while preserving original item order.
1389
- if (typeof originalInput === 'string') {
1390
- return [{ type: 'message', role: 'user', content: originalInput }];
812
+ exports.Runner = Runner;
813
+ // internal helpers and constants
814
+ let defaultRunner;
815
+ const getDefaultRunner = () => {
816
+ if (!defaultRunner) {
817
+ defaultRunner = new Runner();
1391
818
  }
1392
- return [...originalInput];
1393
- }
819
+ return defaultRunner;
820
+ };
1394
821
  //# sourceMappingURL=run.js.map