@openai/agents-core 0.2.1 → 0.3.1

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 (87) hide show
  1. package/dist/editor.d.ts +38 -0
  2. package/dist/editor.js +3 -0
  3. package/dist/editor.js.map +1 -0
  4. package/dist/editor.mjs +2 -0
  5. package/dist/editor.mjs.map +1 -0
  6. package/dist/extensions/handoffFilters.js +4 -0
  7. package/dist/extensions/handoffFilters.js.map +1 -1
  8. package/dist/extensions/handoffFilters.mjs +4 -0
  9. package/dist/extensions/handoffFilters.mjs.map +1 -1
  10. package/dist/index.d.ts +8 -2
  11. package/dist/index.js +8 -2
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +3 -1
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/items.d.ts +518 -4
  16. package/dist/items.js +22 -1
  17. package/dist/items.js.map +1 -1
  18. package/dist/items.mjs +22 -1
  19. package/dist/items.mjs.map +1 -1
  20. package/dist/memory/memorySession.d.ts +22 -0
  21. package/dist/memory/memorySession.js +64 -0
  22. package/dist/memory/memorySession.js.map +1 -0
  23. package/dist/memory/memorySession.mjs +60 -0
  24. package/dist/memory/memorySession.mjs.map +1 -0
  25. package/dist/memory/session.d.ts +36 -0
  26. package/dist/memory/session.js +3 -0
  27. package/dist/memory/session.js.map +1 -0
  28. package/dist/memory/session.mjs +2 -0
  29. package/dist/memory/session.mjs.map +1 -0
  30. package/dist/metadata.js +2 -2
  31. package/dist/metadata.mjs +2 -2
  32. package/dist/model.d.ts +10 -2
  33. package/dist/run.d.ts +88 -8
  34. package/dist/run.js +859 -347
  35. package/dist/run.js.map +1 -1
  36. package/dist/run.mjs +859 -347
  37. package/dist/run.mjs.map +1 -1
  38. package/dist/runContext.js +2 -2
  39. package/dist/runContext.js.map +1 -1
  40. package/dist/runContext.mjs +2 -2
  41. package/dist/runContext.mjs.map +1 -1
  42. package/dist/runImplementation.d.ts +37 -3
  43. package/dist/runImplementation.js +1116 -319
  44. package/dist/runImplementation.js.map +1 -1
  45. package/dist/runImplementation.mjs +1106 -317
  46. package/dist/runImplementation.mjs.map +1 -1
  47. package/dist/runState.d.ts +4453 -785
  48. package/dist/runState.js +62 -3
  49. package/dist/runState.js.map +1 -1
  50. package/dist/runState.mjs +62 -3
  51. package/dist/runState.mjs.map +1 -1
  52. package/dist/shell.d.ts +36 -0
  53. package/dist/shell.js +3 -0
  54. package/dist/shell.js.map +1 -0
  55. package/dist/shell.mjs +2 -0
  56. package/dist/shell.mjs.map +1 -0
  57. package/dist/tool.d.ts +62 -1
  58. package/dist/tool.js +30 -0
  59. package/dist/tool.js.map +1 -1
  60. package/dist/tool.mjs +28 -0
  61. package/dist/tool.mjs.map +1 -1
  62. package/dist/types/aliases.d.ts +3 -3
  63. package/dist/types/protocol.d.ts +5470 -1519
  64. package/dist/types/protocol.js +74 -1
  65. package/dist/types/protocol.js.map +1 -1
  66. package/dist/types/protocol.mjs +73 -0
  67. package/dist/types/protocol.mjs.map +1 -1
  68. package/dist/utils/applyDiff.d.ts +9 -0
  69. package/dist/utils/applyDiff.js +275 -0
  70. package/dist/utils/applyDiff.js.map +1 -0
  71. package/dist/utils/applyDiff.mjs +272 -0
  72. package/dist/utils/applyDiff.mjs.map +1 -0
  73. package/dist/utils/index.d.ts +1 -0
  74. package/dist/utils/index.js +3 -1
  75. package/dist/utils/index.js.map +1 -1
  76. package/dist/utils/index.mjs +1 -0
  77. package/dist/utils/index.mjs.map +1 -1
  78. package/dist/utils/serialize.js +12 -0
  79. package/dist/utils/serialize.js.map +1 -1
  80. package/dist/utils/serialize.mjs +12 -0
  81. package/dist/utils/serialize.mjs.map +1 -1
  82. package/dist/utils/smartString.d.ts +9 -0
  83. package/dist/utils/smartString.js +15 -0
  84. package/dist/utils/smartString.js.map +1 -1
  85. package/dist/utils/smartString.mjs +14 -3
  86. package/dist/utils/smartString.mjs.map +1 -1
  87. package/package.json +3 -3
@@ -7,25 +7,75 @@ import { getLastTextFromOutputMessage } from "./utils/messages.mjs";
7
7
  import { withFunctionSpan, withHandoffSpan } from "./tracing/createSpans.mjs";
8
8
  import { getSchemaAndParserFromInputType } from "./utils/tools.mjs";
9
9
  import { encodeUint8ArrayToBase64 } from "./utils/base64.mjs";
10
+ import { isArrayBufferView, isNodeBuffer, isSerializedBufferSnapshot, toSmartString, } from "./utils/smartString.mjs";
10
11
  import { safeExecute } from "./utils/safeExecute.mjs";
11
12
  import { addErrorToCurrentSpan } from "./tracing/context.mjs";
12
13
  import { RunItemStreamEvent } from "./events.mjs";
13
14
  import { z } from 'zod';
14
- import { toSmartString } from "./utils/smartString.mjs";
15
15
  import { isZodObject } from "./utils/index.mjs";
16
+ function isApprovalItemLike(value) {
17
+ if (!value || typeof value !== 'object') {
18
+ return false;
19
+ }
20
+ if (!('rawItem' in value)) {
21
+ return false;
22
+ }
23
+ const rawItem = value.rawItem;
24
+ if (!rawItem || typeof rawItem !== 'object') {
25
+ return false;
26
+ }
27
+ const itemType = rawItem.type;
28
+ return itemType === 'function_call' || itemType === 'hosted_tool_call';
29
+ }
30
+ function getApprovalIdentity(approval) {
31
+ const rawItem = approval.rawItem;
32
+ if (!rawItem) {
33
+ return undefined;
34
+ }
35
+ if (rawItem.type === 'function_call' && rawItem.callId) {
36
+ return `function_call:${rawItem.callId}`;
37
+ }
38
+ if ('callId' in rawItem && rawItem.callId) {
39
+ return `${rawItem.type}:${rawItem.callId}`;
40
+ }
41
+ const id = 'id' in rawItem ? rawItem.id : undefined;
42
+ if (id) {
43
+ return `${rawItem.type}:${id}`;
44
+ }
45
+ const providerData = typeof rawItem.providerData === 'object' && rawItem.providerData
46
+ ? rawItem.providerData
47
+ : undefined;
48
+ if (providerData?.id) {
49
+ return `${rawItem.type}:provider:${providerData.id}`;
50
+ }
51
+ const agentName = 'agent' in approval && approval.agent ? approval.agent.name : '';
52
+ try {
53
+ return `${agentName}:${rawItem.type}:${JSON.stringify(rawItem)}`;
54
+ }
55
+ catch {
56
+ return `${agentName}:${rawItem.type}`;
57
+ }
58
+ }
16
59
  /**
17
60
  * @internal
61
+ * Walks a raw model response and classifies each item so the runner can schedule follow-up work.
62
+ * Returns both the serializable RunItems (for history/streaming) and the actionable tool metadata.
18
63
  */
19
64
  export function processModelResponse(modelResponse, agent, tools, handoffs) {
20
65
  const items = [];
21
66
  const runHandoffs = [];
22
67
  const runFunctions = [];
23
68
  const runComputerActions = [];
69
+ const runShellActions = [];
70
+ const runApplyPatchActions = [];
24
71
  const runMCPApprovalRequests = [];
25
72
  const toolsUsed = [];
26
73
  const handoffMap = new Map(handoffs.map((h) => [h.toolName, h]));
74
+ // Resolve tools upfront so we can look up the concrete handler in O(1) while iterating outputs.
27
75
  const functionMap = new Map(tools.filter((t) => t.type === 'function').map((t) => [t.name, t]));
28
76
  const computerTool = tools.find((t) => t.type === 'computer');
77
+ const shellTool = tools.find((t) => t.type === 'shell');
78
+ const applyPatchTool = tools.find((t) => t.type === 'apply_patch');
29
79
  const mcpToolMap = new Map(tools
30
80
  .filter((t) => t.type === 'hosted_tool' && t.providerData?.type === 'mcp')
31
81
  .map((t) => t)
@@ -95,6 +145,40 @@ export function processModelResponse(modelResponse, agent, tools, handoffs) {
95
145
  computer: computerTool,
96
146
  });
97
147
  }
148
+ else if (output.type === 'shell_call') {
149
+ items.push(new RunToolCallItem(output, agent));
150
+ toolsUsed.push('shell');
151
+ if (!shellTool) {
152
+ addErrorToCurrentSpan({
153
+ message: 'Model produced shell action without a shell tool.',
154
+ data: {
155
+ agent_name: agent.name,
156
+ },
157
+ });
158
+ throw new ModelBehaviorError('Model produced shell action without a shell tool.');
159
+ }
160
+ runShellActions.push({
161
+ toolCall: output,
162
+ shell: shellTool,
163
+ });
164
+ }
165
+ else if (output.type === 'apply_patch_call') {
166
+ items.push(new RunToolCallItem(output, agent));
167
+ toolsUsed.push('apply_patch');
168
+ if (!applyPatchTool) {
169
+ addErrorToCurrentSpan({
170
+ message: 'Model produced apply_patch action without an apply_patch tool.',
171
+ data: {
172
+ agent_name: agent.name,
173
+ },
174
+ });
175
+ throw new ModelBehaviorError('Model produced apply_patch action without an apply_patch tool.');
176
+ }
177
+ runApplyPatchActions.push({
178
+ toolCall: output,
179
+ applyPatch: applyPatchTool,
180
+ });
181
+ }
98
182
  if (output.type !== 'function_call') {
99
183
  continue;
100
184
  }
@@ -131,13 +215,17 @@ export function processModelResponse(modelResponse, agent, tools, handoffs) {
131
215
  handoffs: runHandoffs,
132
216
  functions: runFunctions,
133
217
  computerActions: runComputerActions,
218
+ shellActions: runShellActions,
219
+ applyPatchActions: runApplyPatchActions,
134
220
  mcpApprovalRequests: runMCPApprovalRequests,
135
221
  toolsUsed: toolsUsed,
136
222
  hasToolsOrApprovalsToRun() {
137
223
  return (runHandoffs.length > 0 ||
138
224
  runFunctions.length > 0 ||
139
225
  runMCPApprovalRequests.length > 0 ||
140
- runComputerActions.length > 0);
226
+ runComputerActions.length > 0 ||
227
+ runShellActions.length > 0 ||
228
+ runApplyPatchActions.length > 0);
141
229
  },
142
230
  };
143
231
  }
@@ -158,6 +246,10 @@ export const nextStepSchema = z.discriminatedUnion('type', [
158
246
  data: z.record(z.string(), z.any()),
159
247
  }),
160
248
  ]);
249
+ /**
250
+ * Internal convenience wrapper that groups the outcome of a single agent turn. It lets the caller
251
+ * update the RunState in one shot and decide which step to execute next.
252
+ */
161
253
  class SingleStepResult {
162
254
  originalInput;
163
255
  modelResponse;
@@ -166,7 +258,7 @@ class SingleStepResult {
166
258
  nextStep;
167
259
  constructor(
168
260
  /**
169
- * The input items i.e. the items before run() was called. May be muted by handoff input filters
261
+ * The input items (i.e., the items before run() was called). May be mutated by handoff input filters.
170
262
  */
171
263
  originalInput,
172
264
  /**
@@ -200,6 +292,8 @@ class SingleStepResult {
200
292
  }
201
293
  /**
202
294
  * @internal
295
+ * Resets the tool choice when the agent is configured to prefer a fresh tool selection after
296
+ * any tool usage. This prevents the provider from reusing stale tool hints across turns.
203
297
  */
204
298
  export function maybeResetToolChoice(agent, toolUseTracker, modelSettings) {
205
299
  if (agent.resetToolChoice && toolUseTracker.hasUsedTools(agent)) {
@@ -209,33 +303,104 @@ export function maybeResetToolChoice(agent, toolUseTracker, modelSettings) {
209
303
  }
210
304
  /**
211
305
  * @internal
306
+ * Continues a turn that was previously interrupted waiting for tool approval. Executes the now
307
+ * approved tools and returns the resulting step transition.
212
308
  */
213
- export async function executeInterruptedToolsAndSideEffects(agent, originalInput, originalPreStepItems, newResponse, processedResponse, runner, state) {
309
+ export async function resolveInterruptedTurn(agent, originalInput, originalPreStepItems, newResponse, processedResponse, runner, state) {
214
310
  // call_ids for function tools
215
311
  const functionCallIds = originalPreStepItems
216
312
  .filter((item) => item instanceof RunToolApprovalItem &&
217
313
  'callId' in item.rawItem &&
218
314
  item.rawItem.type === 'function_call')
219
315
  .map((item) => item.rawItem.callId);
316
+ // We already persisted the turn once when the approval interrupt was raised, so the
317
+ // counter reflects the approval items as "flushed". When we resume the same turn we need
318
+ // to rewind it so the eventual tool output for this call is still written to the session.
319
+ const pendingApprovalItems = state
320
+ .getInterruptions()
321
+ .filter(isApprovalItemLike);
322
+ if (pendingApprovalItems.length > 0) {
323
+ const pendingApprovalIdentities = new Set();
324
+ for (const approval of pendingApprovalItems) {
325
+ const identity = getApprovalIdentity(approval);
326
+ if (identity) {
327
+ pendingApprovalIdentities.add(identity);
328
+ }
329
+ }
330
+ if (pendingApprovalIdentities.size > 0) {
331
+ let rewindCount = 0;
332
+ for (let index = originalPreStepItems.length - 1; index >= 0; index--) {
333
+ const item = originalPreStepItems[index];
334
+ if (!(item instanceof RunToolApprovalItem)) {
335
+ continue;
336
+ }
337
+ const identity = getApprovalIdentity(item);
338
+ if (!identity) {
339
+ continue;
340
+ }
341
+ if (!pendingApprovalIdentities.has(identity)) {
342
+ continue;
343
+ }
344
+ rewindCount++;
345
+ pendingApprovalIdentities.delete(identity);
346
+ if (pendingApprovalIdentities.size === 0) {
347
+ break;
348
+ }
349
+ }
350
+ // Persisting the approval request already advanced the counter once, so undo the increment
351
+ // to make sure we write the final tool output back to the session when the turn resumes.
352
+ if (rewindCount > 0) {
353
+ state._currentTurnPersistedItemCount = Math.max(0, state._currentTurnPersistedItemCount - rewindCount);
354
+ }
355
+ }
356
+ }
220
357
  // Run function tools that require approval after they get their approval results
221
358
  const functionToolRuns = processedResponse.functions.filter((run) => {
222
359
  return functionCallIds.includes(run.toolCall.callId);
223
360
  });
224
361
  const functionResults = await executeFunctionToolCalls(agent, functionToolRuns, runner, state);
225
- // Create the initial set of the output items
226
- const newItems = functionResults.map((r) => r.runItem);
362
+ // There is no built-in HITL approval surface for computer tools today, so every pending action
363
+ // is executed immediately when the turn resumes.
364
+ const computerResults = processedResponse.computerActions.length > 0
365
+ ? await executeComputerActions(agent, processedResponse.computerActions, runner, state._context)
366
+ : [];
367
+ // When resuming we receive the original RunItem references; suppress duplicates so history and streaming do not double-emit the same items.
368
+ const originalPreStepItemSet = new Set(originalPreStepItems);
369
+ const newItems = [];
370
+ const newItemsSet = new Set();
371
+ const appendIfNew = (item) => {
372
+ if (originalPreStepItemSet.has(item) || newItemsSet.has(item)) {
373
+ return;
374
+ }
375
+ newItems.push(item);
376
+ newItemsSet.add(item);
377
+ };
378
+ for (const result of functionResults) {
379
+ appendIfNew(result.runItem);
380
+ }
381
+ for (const result of computerResults) {
382
+ appendIfNew(result);
383
+ }
227
384
  // Run MCP tools that require approval after they get their approval results
228
385
  const mcpApprovalRuns = processedResponse.mcpApprovalRequests.filter((run) => {
229
386
  return (run.requestItem.type === 'tool_approval_item' &&
230
387
  run.requestItem.rawItem.type === 'hosted_tool_call' &&
231
388
  run.requestItem.rawItem.providerData?.type === 'mcp_approval_request');
232
389
  });
390
+ // Hosted MCP approvals may still be waiting on a human decision when the turn resumes.
391
+ const pendingHostedMCPApprovals = new Set();
392
+ const pendingHostedMCPApprovalIds = new Set();
393
+ // Keep track of approvals we still need to surface next turn so HITL flows can resume cleanly.
233
394
  for (const run of mcpApprovalRuns) {
234
395
  // the approval_request_id "mcpr_123..."
235
- const approvalRequestId = run.requestItem.rawItem.id;
396
+ const rawItem = run.requestItem.rawItem;
397
+ if (rawItem.type !== 'hosted_tool_call') {
398
+ continue;
399
+ }
400
+ const approvalRequestId = rawItem.id;
236
401
  const approved = state._context.isToolApproved({
237
402
  // Since this item name must be the same with the one sent from Responses API server
238
- toolName: run.requestItem.rawItem.name,
403
+ toolName: rawItem.name,
239
404
  callId: approvalRequestId,
240
405
  });
241
406
  if (typeof approved !== 'undefined') {
@@ -245,50 +410,101 @@ export async function executeInterruptedToolsAndSideEffects(agent, originalInput
245
410
  reason: undefined,
246
411
  };
247
412
  // Tell Responses API server the approval result in the next turn
248
- newItems.push(new RunToolCallItem({
413
+ const responseItem = new RunToolCallItem({
249
414
  type: 'hosted_tool_call',
250
415
  name: 'mcp_approval_response',
251
416
  providerData,
252
- }, agent));
417
+ }, agent);
418
+ appendIfNew(responseItem);
419
+ }
420
+ else {
421
+ pendingHostedMCPApprovals.add(run.requestItem);
422
+ pendingHostedMCPApprovalIds.add(approvalRequestId);
423
+ functionResults.push({
424
+ type: 'hosted_mcp_tool_approval',
425
+ tool: run.mcpTool,
426
+ runItem: run.requestItem,
427
+ });
428
+ appendIfNew(run.requestItem);
253
429
  }
254
430
  }
255
- const checkToolOutput = await checkForFinalOutputFromTools(agent, functionResults, state);
256
- // Exclude the tool approval items, which should not be sent to Responses API,
257
- // from the SingleStepResult's preStepItems
431
+ // Server-managed conversations rely on preStepItems to re-surface pending approvals.
432
+ // Keep unresolved hosted MCP approvals in place so HITL flows still have something to approve next turn.
433
+ // Drop resolved approval placeholders so they are not replayed on the next turn, but keep
434
+ // pending approvals in place to signal the outstanding work to the UI and session store.
258
435
  const preStepItems = originalPreStepItems.filter((item) => {
259
- return !(item instanceof RunToolApprovalItem);
436
+ if (!(item instanceof RunToolApprovalItem)) {
437
+ return true;
438
+ }
439
+ if (item.rawItem.type === 'hosted_tool_call' &&
440
+ item.rawItem.providerData?.type === 'mcp_approval_request') {
441
+ if (pendingHostedMCPApprovals.has(item)) {
442
+ return true;
443
+ }
444
+ const approvalRequestId = item.rawItem.id;
445
+ if (approvalRequestId) {
446
+ return pendingHostedMCPApprovalIds.has(approvalRequestId);
447
+ }
448
+ return false;
449
+ }
450
+ return false;
260
451
  });
261
- if (checkToolOutput.isFinalOutput) {
262
- runner.emit('agent_end', state._context, agent, checkToolOutput.finalOutput);
263
- agent.emit('agent_end', state._context, checkToolOutput.finalOutput);
264
- return new SingleStepResult(originalInput, newResponse, preStepItems, newItems, {
265
- type: 'next_step_final_output',
266
- output: checkToolOutput.finalOutput,
267
- });
268
- }
269
- else if (checkToolOutput.isInterrupted) {
270
- return new SingleStepResult(originalInput, newResponse, preStepItems, newItems, {
271
- type: 'next_step_interruption',
272
- data: {
273
- interruptions: checkToolOutput.interruptions,
274
- },
275
- });
452
+ const completedStep = await maybeCompleteTurnFromToolResults({
453
+ agent,
454
+ runner,
455
+ state,
456
+ functionResults,
457
+ originalInput,
458
+ newResponse,
459
+ preStepItems,
460
+ newItems,
461
+ });
462
+ if (completedStep) {
463
+ return completedStep;
276
464
  }
277
465
  // we only ran new tools and side effects. We need to run the rest of the agent
278
466
  return new SingleStepResult(originalInput, newResponse, preStepItems, newItems, { type: 'next_step_run_again' });
279
467
  }
280
468
  /**
281
469
  * @internal
470
+ * Executes every follow-up action the model requested (function tools, computer actions, MCP flows),
471
+ * appends their outputs to the run history, and determines the next step for the agent loop.
282
472
  */
283
- export async function executeToolsAndSideEffects(agent, originalInput, originalPreStepItems, newResponse, processedResponse, runner, state) {
473
+ export async function resolveTurnAfterModelResponse(agent, originalInput, originalPreStepItems, newResponse, processedResponse, runner, state) {
474
+ // Reuse the same array reference so we can compare object identity when deciding whether to
475
+ // append new items, ensuring we never double-stream existing RunItems.
284
476
  const preStepItems = originalPreStepItems;
285
- let newItems = processedResponse.newItems;
286
- const [functionResults, computerResults] = await Promise.all([
477
+ const seenItems = new Set(originalPreStepItems);
478
+ const newItems = [];
479
+ const appendIfNew = (item) => {
480
+ if (seenItems.has(item)) {
481
+ return;
482
+ }
483
+ newItems.push(item);
484
+ seenItems.add(item);
485
+ };
486
+ for (const item of processedResponse.newItems) {
487
+ appendIfNew(item);
488
+ }
489
+ // Run function tools and computer actions in parallel; neither depends on the other's side effects.
490
+ const [functionResults, computerResults, shellResults, applyPatchResults] = await Promise.all([
287
491
  executeFunctionToolCalls(agent, processedResponse.functions, runner, state),
288
492
  executeComputerActions(agent, processedResponse.computerActions, runner, state._context),
493
+ executeShellActions(agent, processedResponse.shellActions, runner, state._context),
494
+ executeApplyPatchOperations(agent, processedResponse.applyPatchActions, runner, state._context),
289
495
  ]);
290
- newItems = newItems.concat(functionResults.map((r) => r.runItem));
291
- newItems = newItems.concat(computerResults);
496
+ for (const result of functionResults) {
497
+ appendIfNew(result.runItem);
498
+ }
499
+ for (const item of computerResults) {
500
+ appendIfNew(item);
501
+ }
502
+ for (const item of shellResults) {
503
+ appendIfNew(item);
504
+ }
505
+ for (const item of applyPatchResults) {
506
+ appendIfNew(item);
507
+ }
292
508
  // run hosted MCP approval requests
293
509
  if (processedResponse.mcpApprovalRequests.length > 0) {
294
510
  for (const approvalRequest of processedResponse.mcpApprovalRequests) {
@@ -334,28 +550,26 @@ export async function executeToolsAndSideEffects(agent, originalInput, originalP
334
550
  if (processedResponse.handoffs.length > 0) {
335
551
  return await executeHandoffCalls(agent, originalInput, preStepItems, newItems, newResponse, processedResponse.handoffs, runner, state._context);
336
552
  }
337
- const checkToolOutput = await checkForFinalOutputFromTools(agent, functionResults, state);
338
- if (checkToolOutput.isFinalOutput) {
339
- runner.emit('agent_end', state._context, agent, checkToolOutput.finalOutput);
340
- agent.emit('agent_end', state._context, checkToolOutput.finalOutput);
341
- return new SingleStepResult(originalInput, newResponse, preStepItems, newItems, {
342
- type: 'next_step_final_output',
343
- output: checkToolOutput.finalOutput,
344
- });
345
- }
346
- else if (checkToolOutput.isInterrupted) {
347
- return new SingleStepResult(originalInput, newResponse, preStepItems, newItems, {
348
- type: 'next_step_interruption',
349
- data: {
350
- interruptions: checkToolOutput.interruptions,
351
- },
352
- });
553
+ const completedStep = await maybeCompleteTurnFromToolResults({
554
+ agent,
555
+ runner,
556
+ state,
557
+ functionResults,
558
+ originalInput,
559
+ newResponse,
560
+ preStepItems,
561
+ newItems,
562
+ });
563
+ if (completedStep) {
564
+ return completedStep;
353
565
  }
354
566
  // If the model issued any tool calls or handoffs in this turn,
355
567
  // we must NOT treat any assistant message in the same turn as the final output.
356
568
  // We should run the loop again so the model can see the tool results and respond.
357
569
  const hadToolCallsOrActions = (processedResponse.functions?.length ?? 0) > 0 ||
358
570
  (processedResponse.computerActions?.length ?? 0) > 0 ||
571
+ (processedResponse.shellActions?.length ?? 0) > 0 ||
572
+ (processedResponse.applyPatchActions?.length ?? 0) > 0 ||
359
573
  (processedResponse.mcpApprovalRequests?.length ?? 0) > 0 ||
360
574
  (processedResponse.handoffs?.length ?? 0) > 0;
361
575
  if (hadToolCallsOrActions) {
@@ -371,6 +585,7 @@ export async function executeToolsAndSideEffects(agent, originalInput, originalP
371
585
  if (typeof potentialFinalOutput === 'undefined') {
372
586
  return new SingleStepResult(originalInput, newResponse, preStepItems, newItems, { type: 'next_step_run_again' });
373
587
  }
588
+ // Keep looping if any tool output placeholders still require an approval follow-up.
374
589
  const hasPendingToolsOrApprovals = functionResults.some((result) => result.runItem instanceof RunToolApprovalItem);
375
590
  if (!hasPendingToolsOrApprovals) {
376
591
  if (agent.outputType === 'text') {
@@ -397,8 +612,32 @@ export async function executeToolsAndSideEffects(agent, originalInput, originalP
397
612
  }
398
613
  return new SingleStepResult(originalInput, newResponse, preStepItems, newItems, { type: 'next_step_run_again' });
399
614
  }
615
+ // Consolidates the logic that determines whether tool results yielded a final answer,
616
+ // triggered an interruption, or require the agent loop to continue running.
617
+ async function maybeCompleteTurnFromToolResults({ agent, runner, state, functionResults, originalInput, newResponse, preStepItems, newItems, }) {
618
+ const toolOutcome = await checkForFinalOutputFromTools(agent, functionResults, state);
619
+ if (toolOutcome.isFinalOutput) {
620
+ runner.emit('agent_end', state._context, agent, toolOutcome.finalOutput);
621
+ agent.emit('agent_end', state._context, toolOutcome.finalOutput);
622
+ return new SingleStepResult(originalInput, newResponse, preStepItems, newItems, {
623
+ type: 'next_step_final_output',
624
+ output: toolOutcome.finalOutput,
625
+ });
626
+ }
627
+ if (toolOutcome.isInterrupted) {
628
+ return new SingleStepResult(originalInput, newResponse, preStepItems, newItems, {
629
+ type: 'next_step_interruption',
630
+ data: {
631
+ interruptions: toolOutcome.interruptions,
632
+ },
633
+ });
634
+ }
635
+ return null;
636
+ }
400
637
  /**
401
638
  * @internal
639
+ * Normalizes tool outputs once so downstream code works with fully structured protocol items.
640
+ * Doing this here keeps API surface stable even when providers add new shapes.
402
641
  */
403
642
  export function getToolCallOutputItem(toolCall, output) {
404
643
  const maybeStructuredOutputs = normalizeStructuredToolOutputs(output);
@@ -423,283 +662,57 @@ export function getToolCallOutputItem(toolCall, output) {
423
662
  },
424
663
  };
425
664
  }
426
- /**
427
- * Accepts whatever the tool returned and attempts to coerce it into the structured protocol
428
- * shapes we expose to downstream model adapters (input_text/input_image/input_file). Tools are
429
- * allowed to return either a single structured object or an array of them; anything else falls
430
- * back to the legacy string pipeline.
431
- */
432
- function normalizeStructuredToolOutputs(output) {
433
- if (Array.isArray(output)) {
434
- const structured = [];
435
- for (const item of output) {
436
- const normalized = normalizeStructuredToolOutput(item);
437
- if (!normalized) {
438
- return null;
439
- }
440
- structured.push(normalized);
441
- }
442
- return structured;
665
+ function normalizeFileValue(value) {
666
+ const directFile = value.file;
667
+ if (typeof directFile === 'string' && directFile.length > 0) {
668
+ return directFile;
443
669
  }
444
- const normalized = normalizeStructuredToolOutput(output);
445
- return normalized ? [normalized] : null;
670
+ const normalizedObject = normalizeFileObjectCandidate(directFile);
671
+ if (normalizedObject) {
672
+ return normalizedObject;
673
+ }
674
+ const legacyValue = normalizeLegacyFileValue(value);
675
+ if (legacyValue) {
676
+ return legacyValue;
677
+ }
678
+ return null;
446
679
  }
447
- /**
448
- * Best-effort normalization of a single tool output item. If the object already matches the
449
- * protocol shape we simply cast it; otherwise we copy the recognised fields into the canonical
450
- * structure. Returning null lets the caller know we should revert to plain-string handling.
451
- */
452
- function normalizeStructuredToolOutput(value) {
680
+ function normalizeFileObjectCandidate(value) {
453
681
  if (!isRecord(value)) {
454
682
  return null;
455
683
  }
456
- const type = value.type;
457
- if (type === 'text' && typeof value.text === 'string') {
458
- const output = { type: 'text', text: value.text };
459
- if (isRecord(value.providerData)) {
460
- output.providerData = value.providerData;
461
- }
462
- return output;
463
- }
464
- if (type === 'image') {
465
- const output = { type: 'image' };
466
- let imageString;
467
- let imageFileId;
468
- const fallbackImageMediaType = isNonEmptyString(value.mediaType)
469
- ? value.mediaType
470
- : undefined;
471
- const imageField = value.image;
472
- if (typeof imageField === 'string' && imageField.length > 0) {
473
- imageString = imageField;
474
- }
475
- else if (isRecord(imageField)) {
476
- const imageObj = imageField;
477
- const inlineMediaType = isNonEmptyString(imageObj.mediaType)
478
- ? imageObj.mediaType
479
- : fallbackImageMediaType;
480
- if (isNonEmptyString(imageObj.url)) {
481
- imageString = imageObj.url;
482
- }
483
- else if (isNonEmptyString(imageObj.data)) {
484
- imageString = toInlineImageString(imageObj.data, inlineMediaType);
485
- }
486
- else if (imageObj.data instanceof Uint8Array &&
487
- imageObj.data.length > 0) {
488
- imageString = toInlineImageString(imageObj.data, inlineMediaType);
489
- }
490
- if (!imageString) {
491
- const candidateId = (isNonEmptyString(imageObj.fileId) && imageObj.fileId) ||
492
- (isNonEmptyString(imageObj.id) && imageObj.id) ||
493
- undefined;
494
- if (candidateId) {
495
- imageFileId = candidateId;
496
- }
497
- }
498
- }
499
- if (!imageString &&
500
- typeof value.imageUrl === 'string' &&
501
- value.imageUrl.length > 0) {
502
- imageString = value.imageUrl;
503
- }
504
- if (!imageFileId &&
505
- typeof value.fileId === 'string' &&
506
- value.fileId.length > 0) {
507
- imageFileId = value.fileId;
508
- }
509
- if (!imageString &&
510
- typeof value.data === 'string' &&
511
- value.data.length > 0) {
512
- imageString = fallbackImageMediaType
513
- ? toInlineImageString(value.data, fallbackImageMediaType)
514
- : value.data;
515
- }
516
- else if (!imageString &&
517
- value.data instanceof Uint8Array &&
518
- value.data.length > 0) {
519
- imageString = toInlineImageString(value.data, fallbackImageMediaType);
520
- }
521
- if (typeof value.detail === 'string' && value.detail.length > 0) {
522
- output.detail = value.detail;
523
- }
524
- if (imageString) {
525
- output.image = imageString;
526
- }
527
- else if (imageFileId) {
528
- output.image = { fileId: imageFileId };
529
- }
530
- else {
684
+ if ('data' in value && value.data !== undefined) {
685
+ const dataValue = value.data;
686
+ const hasStringData = typeof dataValue === 'string' && dataValue.length > 0;
687
+ const hasBinaryData = dataValue instanceof Uint8Array && dataValue.length > 0;
688
+ if (!hasStringData && !hasBinaryData) {
531
689
  return null;
532
690
  }
533
- if (isRecord(value.providerData)) {
534
- output.providerData = value.providerData;
691
+ if (!isNonEmptyString(value.mediaType) ||
692
+ !isNonEmptyString(value.filename)) {
693
+ return null;
535
694
  }
536
- return output;
695
+ return {
696
+ data: typeof dataValue === 'string' ? dataValue : new Uint8Array(dataValue),
697
+ mediaType: value.mediaType,
698
+ filename: value.filename,
699
+ };
537
700
  }
538
- if (type === 'file') {
539
- const fileValue = normalizeFileValue(value);
540
- if (!fileValue) {
541
- return null;
701
+ if (isNonEmptyString(value.url)) {
702
+ const result = { url: value.url };
703
+ if (isNonEmptyString(value.filename)) {
704
+ result.filename = value.filename;
542
705
  }
543
- const output = { type: 'file', file: fileValue };
544
- if (isRecord(value.providerData)) {
545
- output.providerData = value.providerData;
706
+ return result;
707
+ }
708
+ const referencedId = (isNonEmptyString(value.id) && value.id) ||
709
+ (isNonEmptyString(value.fileId) && value.fileId);
710
+ if (referencedId) {
711
+ const result = { id: referencedId };
712
+ if (isNonEmptyString(value.filename)) {
713
+ result.filename = value.filename;
546
714
  }
547
- return output;
548
- }
549
- return null;
550
- }
551
- /**
552
- * Translates the normalized tool output into the protocol `input_*` items. This is the last hop
553
- * before we hand the data to model-specific adapters, so we generate the exact schema expected by
554
- * the protocol definitions.
555
- */
556
- function convertStructuredToolOutputToInputItem(output) {
557
- if (output.type === 'text') {
558
- const result = {
559
- type: 'input_text',
560
- text: output.text,
561
- };
562
- if (output.providerData) {
563
- result.providerData = output.providerData;
564
- }
565
- return result;
566
- }
567
- if (output.type === 'image') {
568
- const result = { type: 'input_image' };
569
- if (typeof output.detail === 'string' && output.detail.length > 0) {
570
- result.detail = output.detail;
571
- }
572
- if (typeof output.image === 'string' && output.image.length > 0) {
573
- result.image = output.image;
574
- }
575
- else if (isRecord(output.image)) {
576
- const imageObj = output.image;
577
- const inlineMediaType = isNonEmptyString(imageObj.mediaType)
578
- ? imageObj.mediaType
579
- : undefined;
580
- if (isNonEmptyString(imageObj.url)) {
581
- result.image = imageObj.url;
582
- }
583
- else if (isNonEmptyString(imageObj.data)) {
584
- result.image =
585
- inlineMediaType && !imageObj.data.startsWith('data:')
586
- ? asDataUrl(imageObj.data, inlineMediaType)
587
- : imageObj.data;
588
- }
589
- else if (imageObj.data instanceof Uint8Array &&
590
- imageObj.data.length > 0) {
591
- const base64 = encodeUint8ArrayToBase64(imageObj.data);
592
- result.image = asDataUrl(base64, inlineMediaType);
593
- }
594
- else {
595
- const referencedId = (isNonEmptyString(imageObj.fileId) && imageObj.fileId) ||
596
- (isNonEmptyString(imageObj.id) && imageObj.id) ||
597
- undefined;
598
- if (referencedId) {
599
- result.image = { id: referencedId };
600
- }
601
- }
602
- }
603
- if (output.providerData) {
604
- result.providerData = output.providerData;
605
- }
606
- return result;
607
- }
608
- if (output.type === 'file') {
609
- const result = { type: 'input_file' };
610
- const fileValue = output.file;
611
- if (typeof fileValue === 'string') {
612
- result.file = fileValue;
613
- }
614
- else if (fileValue && typeof fileValue === 'object') {
615
- const record = fileValue;
616
- if ('data' in record && record.data) {
617
- const mediaType = record.mediaType ?? 'text/plain';
618
- if (typeof record.data === 'string') {
619
- result.file = asDataUrl(record.data, mediaType);
620
- }
621
- else {
622
- const base64 = encodeUint8ArrayToBase64(record.data);
623
- result.file = asDataUrl(base64, mediaType);
624
- }
625
- }
626
- else if (typeof record.url === 'string' && record.url.length > 0) {
627
- result.file = { url: record.url };
628
- }
629
- else {
630
- const referencedId = (typeof record.id === 'string' &&
631
- record.id.length > 0 &&
632
- record.id) ||
633
- (typeof record.fileId === 'string' && record.fileId.length > 0
634
- ? record.fileId
635
- : undefined);
636
- if (referencedId) {
637
- result.file = { id: referencedId };
638
- }
639
- }
640
- if (typeof record.filename === 'string' && record.filename.length > 0) {
641
- result.filename = record.filename;
642
- }
643
- }
644
- if (output.providerData) {
645
- result.providerData = output.providerData;
646
- }
647
- return result;
648
- }
649
- const exhaustiveCheck = output;
650
- return exhaustiveCheck;
651
- }
652
- function normalizeFileValue(value) {
653
- const directFile = value.file;
654
- if (typeof directFile === 'string' && directFile.length > 0) {
655
- return directFile;
656
- }
657
- const normalizedObject = normalizeFileObjectCandidate(directFile);
658
- if (normalizedObject) {
659
- return normalizedObject;
660
- }
661
- const legacyValue = normalizeLegacyFileValue(value);
662
- if (legacyValue) {
663
- return legacyValue;
664
- }
665
- return null;
666
- }
667
- function normalizeFileObjectCandidate(value) {
668
- if (!isRecord(value)) {
669
- return null;
670
- }
671
- if ('data' in value && value.data !== undefined) {
672
- const dataValue = value.data;
673
- const hasStringData = typeof dataValue === 'string' && dataValue.length > 0;
674
- const hasBinaryData = dataValue instanceof Uint8Array && dataValue.length > 0;
675
- if (!hasStringData && !hasBinaryData) {
676
- return null;
677
- }
678
- if (!isNonEmptyString(value.mediaType) ||
679
- !isNonEmptyString(value.filename)) {
680
- return null;
681
- }
682
- return {
683
- data: typeof dataValue === 'string' ? dataValue : new Uint8Array(dataValue),
684
- mediaType: value.mediaType,
685
- filename: value.filename,
686
- };
687
- }
688
- if (isNonEmptyString(value.url)) {
689
- const result = { url: value.url };
690
- if (isNonEmptyString(value.filename)) {
691
- result.filename = value.filename;
692
- }
693
- return result;
694
- }
695
- const referencedId = (isNonEmptyString(value.id) && value.id) ||
696
- (isNonEmptyString(value.fileId) && value.fileId);
697
- if (referencedId) {
698
- const result = { id: referencedId };
699
- if (isNonEmptyString(value.filename)) {
700
- result.filename = value.filename;
701
- }
702
- return result;
715
+ return result;
703
716
  }
704
717
  return null;
705
718
  }
@@ -759,6 +772,8 @@ function asDataUrl(base64, mediaType) {
759
772
  }
760
773
  /**
761
774
  * @internal
775
+ * Runs every function tool call requested by the model and returns their outputs alongside
776
+ * the `RunItem` instances that should be appended to history.
762
777
  */
763
778
  export async function executeFunctionToolCalls(agent, toolRuns, runner, state) {
764
779
  async function runSingleTool(toolRun) {
@@ -771,6 +786,7 @@ export async function executeFunctionToolCalls(agent, toolRuns, runner, state) {
771
786
  parsedArgs = JSON.parse(parsedArgs);
772
787
  }
773
788
  }
789
+ // Some tools require a human or policy check before execution; defer until approval is recorded.
774
790
  const needsApproval = await toolRun.tool.needsApproval(state._context, parsedArgs, toolRun.toolCall.callId);
775
791
  if (needsApproval) {
776
792
  const approval = state._context.isToolApproved({
@@ -922,8 +938,210 @@ async function _runComputerActionAndScreenshot(computer, toolCall) {
922
938
  }
923
939
  throw new Error('Computer does not implement screenshot()');
924
940
  }
941
+ function toErrorMessage(error) {
942
+ if (error instanceof Error) {
943
+ return error.message || error.toString();
944
+ }
945
+ try {
946
+ return JSON.stringify(error);
947
+ }
948
+ catch {
949
+ return String(error);
950
+ }
951
+ }
952
+ export async function executeShellActions(agent, actions, runner, runContext, customLogger = undefined) {
953
+ const _logger = customLogger ?? logger;
954
+ const results = [];
955
+ for (const action of actions) {
956
+ const shellTool = action.shell;
957
+ const toolCall = action.toolCall;
958
+ const approvalItem = new RunToolApprovalItem(toolCall, agent, shellTool.name);
959
+ const requiresApproval = await shellTool.needsApproval(runContext, toolCall.action, toolCall.callId);
960
+ if (requiresApproval) {
961
+ if (shellTool.onApproval) {
962
+ const decision = await shellTool.onApproval(runContext, approvalItem);
963
+ if (decision.approve === true) {
964
+ runContext.approveTool(approvalItem);
965
+ }
966
+ else if (decision.approve === false) {
967
+ runContext.rejectTool(approvalItem);
968
+ }
969
+ }
970
+ const approval = runContext.isToolApproved({
971
+ toolName: shellTool.name,
972
+ callId: toolCall.callId,
973
+ });
974
+ if (approval === false) {
975
+ const response = 'Tool execution was not approved.';
976
+ const rejectionOutput = {
977
+ stdout: '',
978
+ stderr: response,
979
+ outcome: { type: 'exit', exitCode: null },
980
+ };
981
+ results.push(new RunToolCallOutputItem({
982
+ type: 'shell_call_output',
983
+ callId: toolCall.callId,
984
+ output: [rejectionOutput],
985
+ }, agent, response));
986
+ continue;
987
+ }
988
+ if (approval !== true) {
989
+ results.push(approvalItem);
990
+ continue;
991
+ }
992
+ }
993
+ runner.emit('agent_tool_start', runContext, agent, shellTool, {
994
+ toolCall,
995
+ });
996
+ if (typeof agent.emit === 'function') {
997
+ agent.emit('agent_tool_start', runContext, shellTool, { toolCall });
998
+ }
999
+ let shellOutputs;
1000
+ const providerMeta = {};
1001
+ let maxOutputLength;
1002
+ try {
1003
+ const shellResult = await shellTool.shell.run(toolCall.action);
1004
+ shellOutputs = shellResult.output ?? [];
1005
+ if (shellResult.providerData) {
1006
+ Object.assign(providerMeta, shellResult.providerData);
1007
+ }
1008
+ if (typeof shellResult.maxOutputLength === 'number') {
1009
+ maxOutputLength = shellResult.maxOutputLength;
1010
+ }
1011
+ }
1012
+ catch (err) {
1013
+ const errorText = toErrorMessage(err);
1014
+ shellOutputs = [
1015
+ {
1016
+ stdout: '',
1017
+ stderr: errorText,
1018
+ outcome: { type: 'exit', exitCode: null },
1019
+ },
1020
+ ];
1021
+ _logger.error('Failed to execute shell action:', err);
1022
+ }
1023
+ shellOutputs = shellOutputs ?? [];
1024
+ runner.emit('agent_tool_end', runContext, agent, shellTool, JSON.stringify(shellOutputs), {
1025
+ toolCall,
1026
+ });
1027
+ if (typeof agent.emit === 'function') {
1028
+ agent.emit('agent_tool_end', runContext, shellTool, JSON.stringify(shellOutputs), {
1029
+ toolCall,
1030
+ });
1031
+ }
1032
+ const rawItem = {
1033
+ type: 'shell_call_output',
1034
+ callId: toolCall.callId,
1035
+ output: shellOutputs ?? [],
1036
+ };
1037
+ if (typeof maxOutputLength === 'number') {
1038
+ rawItem.maxOutputLength = maxOutputLength;
1039
+ }
1040
+ if (Object.keys(providerMeta).length > 0) {
1041
+ rawItem.providerData = providerMeta;
1042
+ }
1043
+ results.push(new RunToolCallOutputItem(rawItem, agent, rawItem.output));
1044
+ }
1045
+ return results;
1046
+ }
1047
+ export async function executeApplyPatchOperations(agent, actions, runner, runContext, customLogger = undefined) {
1048
+ const _logger = customLogger ?? logger;
1049
+ const results = [];
1050
+ for (const action of actions) {
1051
+ const applyPatchTool = action.applyPatch;
1052
+ const toolCall = action.toolCall;
1053
+ const approvalItem = new RunToolApprovalItem(toolCall, agent, applyPatchTool.name);
1054
+ const requiresApproval = await applyPatchTool.needsApproval(runContext, toolCall.operation, toolCall.callId);
1055
+ if (requiresApproval) {
1056
+ if (applyPatchTool.onApproval) {
1057
+ const decision = await applyPatchTool.onApproval(runContext, approvalItem);
1058
+ if (decision.approve === true) {
1059
+ runContext.approveTool(approvalItem);
1060
+ }
1061
+ else if (decision.approve === false) {
1062
+ runContext.rejectTool(approvalItem);
1063
+ }
1064
+ }
1065
+ const approval = runContext.isToolApproved({
1066
+ toolName: applyPatchTool.name,
1067
+ callId: toolCall.callId,
1068
+ });
1069
+ if (approval === false) {
1070
+ const response = 'Tool execution was not approved.';
1071
+ results.push(new RunToolCallOutputItem({
1072
+ type: 'apply_patch_call_output',
1073
+ callId: toolCall.callId,
1074
+ status: 'failed',
1075
+ output: response,
1076
+ }, agent, response));
1077
+ continue;
1078
+ }
1079
+ if (approval !== true) {
1080
+ results.push(approvalItem);
1081
+ continue;
1082
+ }
1083
+ }
1084
+ runner.emit('agent_tool_start', runContext, agent, applyPatchTool, {
1085
+ toolCall,
1086
+ });
1087
+ if (typeof agent.emit === 'function') {
1088
+ agent.emit('agent_tool_start', runContext, applyPatchTool, {
1089
+ toolCall,
1090
+ });
1091
+ }
1092
+ let status = 'completed';
1093
+ let output = '';
1094
+ try {
1095
+ let result;
1096
+ switch (toolCall.operation.type) {
1097
+ case 'create_file':
1098
+ result = await applyPatchTool.editor.createFile(toolCall.operation);
1099
+ break;
1100
+ case 'update_file':
1101
+ result = await applyPatchTool.editor.updateFile(toolCall.operation);
1102
+ break;
1103
+ case 'delete_file':
1104
+ result = await applyPatchTool.editor.deleteFile(toolCall.operation);
1105
+ break;
1106
+ default:
1107
+ throw new Error('Unsupported apply_patch operation');
1108
+ }
1109
+ if (result && typeof result.status === 'string') {
1110
+ status = result.status;
1111
+ }
1112
+ if (result && typeof result.output === 'string') {
1113
+ output = result.output;
1114
+ }
1115
+ }
1116
+ catch (err) {
1117
+ status = 'failed';
1118
+ output = toErrorMessage(err);
1119
+ _logger.error('Failed to execute apply_patch operation:', err);
1120
+ }
1121
+ runner.emit('agent_tool_end', runContext, agent, applyPatchTool, output, {
1122
+ toolCall,
1123
+ });
1124
+ if (typeof agent.emit === 'function') {
1125
+ agent.emit('agent_tool_end', runContext, applyPatchTool, output, {
1126
+ toolCall,
1127
+ });
1128
+ }
1129
+ const rawItem = {
1130
+ type: 'apply_patch_call_output',
1131
+ callId: toolCall.callId,
1132
+ status,
1133
+ };
1134
+ if (output) {
1135
+ rawItem.output = output;
1136
+ }
1137
+ results.push(new RunToolCallOutputItem(rawItem, agent, output));
1138
+ }
1139
+ return results;
1140
+ }
925
1141
  /**
926
1142
  * @internal
1143
+ * Executes any computer-use actions emitted by the model and returns the resulting items so the
1144
+ * run history reflects the computer session.
927
1145
  */
928
1146
  export async function executeComputerActions(agent, actions, runner, runContext, customLogger = undefined) {
929
1147
  const _logger = customLogger ?? logger;
@@ -956,7 +1174,7 @@ export async function executeComputerActions(agent, actions, runner, runContext,
956
1174
  toolCall,
957
1175
  });
958
1176
  }
959
- // Always return a screenshot as a base64 data URL
1177
+ // Return the screenshot as a data URL when available; fall back to an empty string on failures.
960
1178
  const imageUrl = output ? `data:image/png;base64,${output}` : '';
961
1179
  const rawItem = {
962
1180
  type: 'computer_call_result',
@@ -969,6 +1187,8 @@ export async function executeComputerActions(agent, actions, runner, runContext,
969
1187
  }
970
1188
  /**
971
1189
  * @internal
1190
+ * Drives handoff calls by invoking the downstream agent and capturing any generated items so
1191
+ * the current agent can continue with the new context.
972
1192
  */
973
1193
  export async function executeHandoffCalls(agent, originalInput, preStepItems, newStepItems, newResponse, runHandoffs, runner, runContext) {
974
1194
  newStepItems = [...newStepItems];
@@ -1037,6 +1257,8 @@ const NOT_FINAL_OUTPUT = {
1037
1257
  };
1038
1258
  /**
1039
1259
  * @internal
1260
+ * Determines whether tool executions produced a final agent output, triggered an interruption,
1261
+ * or whether the agent loop should continue collecting more responses.
1040
1262
  */
1041
1263
  export async function checkForFinalOutputFromTools(agent, toolResults, state) {
1042
1264
  if (toolResults.length === 0) {
@@ -1163,4 +1385,571 @@ export class AgentToolUseTracker {
1163
1385
  }));
1164
1386
  }
1165
1387
  }
1388
+ /**
1389
+ * @internal
1390
+ * Convert a user-provided input into a list of input items.
1391
+ */
1392
+ export function toInputItemList(input) {
1393
+ if (typeof input === 'string') {
1394
+ return [
1395
+ {
1396
+ type: 'message',
1397
+ role: 'user',
1398
+ content: input,
1399
+ },
1400
+ ];
1401
+ }
1402
+ return [...input];
1403
+ }
1404
+ /**
1405
+ * @internal
1406
+ * Extract model output items from run items, excluding tool approval items.
1407
+ */
1408
+ export function extractOutputItemsFromRunItems(items) {
1409
+ return items
1410
+ .filter((item) => item.type !== 'tool_approval_item')
1411
+ .map((item) => item.rawItem);
1412
+ }
1413
+ function normalizeItemsForSessionPersistence(items) {
1414
+ // Persisted sessions must avoid raw binary so we convert every item into a JSON-safe shape before writing to storage.
1415
+ return items.map((item) => sanitizeValueForSession(stripTransientCallIds(item)));
1416
+ }
1417
+ function sanitizeValueForSession(value, context = {}) {
1418
+ if (value === null || value === undefined) {
1419
+ return value;
1420
+ }
1421
+ // Convert supported binary payloads into ArrayBuffer views before serialization.
1422
+ const binary = toUint8ArrayIfBinary(value);
1423
+ if (binary) {
1424
+ return toDataUrlFromBytes(binary, context.mediaType);
1425
+ }
1426
+ if (Array.isArray(value)) {
1427
+ return value.map((entry) => sanitizeValueForSession(entry, context));
1428
+ }
1429
+ if (!isPlainObject(value)) {
1430
+ return value;
1431
+ }
1432
+ const record = value;
1433
+ const result = {};
1434
+ const mediaType = typeof record.mediaType === 'string' && record.mediaType.length > 0
1435
+ ? record.mediaType
1436
+ : context.mediaType;
1437
+ for (const [key, entry] of Object.entries(record)) {
1438
+ // Propagate explicit media type only when walking into binary payload containers.
1439
+ const nextContext = key === 'data' || key === 'fileData' ? { mediaType } : context;
1440
+ result[key] = sanitizeValueForSession(entry, nextContext);
1441
+ }
1442
+ return result;
1443
+ }
1444
+ function toUint8ArrayIfBinary(value) {
1445
+ // Normalize the diverse binary containers we may receive into a shared Uint8Array view.
1446
+ if (value instanceof ArrayBuffer) {
1447
+ return new Uint8Array(value);
1448
+ }
1449
+ if (isArrayBufferView(value)) {
1450
+ const view = value;
1451
+ return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
1452
+ }
1453
+ if (isNodeBuffer(value)) {
1454
+ const view = value;
1455
+ return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
1456
+ }
1457
+ if (isSerializedBufferSnapshot(value)) {
1458
+ const snapshot = value;
1459
+ return Uint8Array.from(snapshot.data);
1460
+ }
1461
+ return undefined;
1462
+ }
1463
+ function toDataUrlFromBytes(bytes, mediaType) {
1464
+ // Convert binary payloads into a durable data URL so session files remain self-contained.
1465
+ const base64 = encodeUint8ArrayToBase64(bytes);
1466
+ // Note that OpenAI Responses API never accepts application/octet-stream as a media type,
1467
+ // so we fall back to text/plain; that said, tools are supposed to return a valid media type when this utility is used.
1468
+ const type = mediaType && !mediaType.startsWith('data:') ? mediaType : 'text/plain';
1469
+ return `data:${type};base64,${base64}`;
1470
+ }
1471
+ function isPlainObject(value) {
1472
+ if (typeof value !== 'object' || value === null) {
1473
+ return false;
1474
+ }
1475
+ const proto = Object.getPrototypeOf(value);
1476
+ return proto === Object.prototype || proto === null;
1477
+ }
1478
+ function stripTransientCallIds(value) {
1479
+ if (value === null || value === undefined) {
1480
+ return value;
1481
+ }
1482
+ if (Array.isArray(value)) {
1483
+ return value.map((entry) => stripTransientCallIds(entry));
1484
+ }
1485
+ if (!isPlainObject(value)) {
1486
+ return value;
1487
+ }
1488
+ const record = value;
1489
+ const result = {};
1490
+ const isProtocolItem = typeof record.type === 'string' && record.type.length > 0;
1491
+ const shouldStripId = isProtocolItem && shouldStripIdForType(record.type);
1492
+ for (const [key, entry] of Object.entries(record)) {
1493
+ if (shouldStripId && key === 'id') {
1494
+ continue;
1495
+ }
1496
+ result[key] = stripTransientCallIds(entry);
1497
+ }
1498
+ return result;
1499
+ }
1500
+ function shouldStripIdForType(type) {
1501
+ switch (type) {
1502
+ case 'function_call':
1503
+ case 'function_call_result':
1504
+ return true;
1505
+ default:
1506
+ return false;
1507
+ }
1508
+ }
1509
+ /**
1510
+ * @internal
1511
+ * Persist full turn (input + outputs) for non-streaming runs.
1512
+ */
1513
+ // Persists the combination of user inputs (possibly filtered) and model outputs for a completed turn.
1514
+ export async function saveToSession(session, sessionInputItems, result) {
1515
+ if (!session) {
1516
+ return;
1517
+ }
1518
+ const inputItems = sessionInputItems ?? [];
1519
+ const state = result.state;
1520
+ const alreadyPersisted = state._currentTurnPersistedItemCount ?? 0;
1521
+ // Persist only the portion of _generatedItems that has not yet been stored for this turn.
1522
+ const newRunItems = result.newItems.slice(alreadyPersisted);
1523
+ if (process.env.OPENAI_AGENTS__DEBUG_SAVE_SESSION) {
1524
+ console.debug('saveToSession:newRunItems', newRunItems.map((item) => item.type));
1525
+ }
1526
+ const outputItems = extractOutputItemsFromRunItems(newRunItems);
1527
+ const itemsToSave = [...inputItems, ...outputItems];
1528
+ if (itemsToSave.length === 0) {
1529
+ state._currentTurnPersistedItemCount =
1530
+ alreadyPersisted + newRunItems.length;
1531
+ return;
1532
+ }
1533
+ const sanitizedItems = normalizeItemsForSessionPersistence(itemsToSave);
1534
+ await session.addItems(sanitizedItems);
1535
+ state._currentTurnPersistedItemCount = alreadyPersisted + newRunItems.length;
1536
+ }
1537
+ /**
1538
+ * @internal
1539
+ * Persist only the user input for streaming runs at start.
1540
+ */
1541
+ // For streaming runs we persist user input as soon as it is sent so reconnections can resume.
1542
+ export async function saveStreamInputToSession(session, sessionInputItems) {
1543
+ if (!session) {
1544
+ return;
1545
+ }
1546
+ if (!sessionInputItems || sessionInputItems.length === 0) {
1547
+ return;
1548
+ }
1549
+ const sanitizedInput = normalizeItemsForSessionPersistence(sessionInputItems);
1550
+ await session.addItems(sanitizedInput);
1551
+ }
1552
+ /**
1553
+ * @internal
1554
+ * Persist only the model outputs for streaming runs at the end of a turn.
1555
+ */
1556
+ // Complements saveStreamInputToSession by recording the streaming outputs at the end of the turn.
1557
+ export async function saveStreamResultToSession(session, result) {
1558
+ if (!session) {
1559
+ return;
1560
+ }
1561
+ const state = result.state;
1562
+ const alreadyPersisted = state._currentTurnPersistedItemCount ?? 0;
1563
+ const newRunItems = result.newItems.slice(alreadyPersisted);
1564
+ const itemsToSave = extractOutputItemsFromRunItems(newRunItems);
1565
+ if (itemsToSave.length === 0) {
1566
+ state._currentTurnPersistedItemCount =
1567
+ alreadyPersisted + newRunItems.length;
1568
+ return;
1569
+ }
1570
+ const sanitizedItems = normalizeItemsForSessionPersistence(itemsToSave);
1571
+ await session.addItems(sanitizedItems);
1572
+ state._currentTurnPersistedItemCount = alreadyPersisted + newRunItems.length;
1573
+ }
1574
+ export async function prepareInputItemsWithSession(input, session, sessionInputCallback, options) {
1575
+ if (!session) {
1576
+ return {
1577
+ preparedInput: input,
1578
+ sessionItems: undefined,
1579
+ };
1580
+ }
1581
+ const includeHistoryInPreparedInput = options?.includeHistoryInPreparedInput ?? true;
1582
+ const preserveDroppedNewItems = options?.preserveDroppedNewItems ?? false;
1583
+ const history = await session.getItems();
1584
+ const newInputItems = Array.isArray(input)
1585
+ ? [...input]
1586
+ : toInputItemList(input);
1587
+ if (!sessionInputCallback) {
1588
+ return {
1589
+ preparedInput: includeHistoryInPreparedInput
1590
+ ? [...history, ...newInputItems]
1591
+ : newInputItems,
1592
+ sessionItems: newInputItems,
1593
+ };
1594
+ }
1595
+ // Capture snapshots before invoking the callback so we can reason about the original state even
1596
+ // if the callback mutates the history array in-place.
1597
+ const historySnapshot = history.slice();
1598
+ const newInputSnapshot = newInputItems.slice();
1599
+ // Delegate history reconciliation to the user-supplied callback. It must return a concrete list
1600
+ // to keep downstream model requests well-typed.
1601
+ const combined = await sessionInputCallback(history, newInputItems);
1602
+ if (!Array.isArray(combined)) {
1603
+ throw new UserError('Session input callback must return an array of AgentInputItem objects.');
1604
+ }
1605
+ const historyCounts = buildItemFrequencyMap(historySnapshot);
1606
+ const newInputCounts = buildItemFrequencyMap(newInputSnapshot);
1607
+ const historyRefs = buildItemReferenceMap(historySnapshot);
1608
+ const newInputRefs = buildItemReferenceMap(newInputSnapshot);
1609
+ const appended = [];
1610
+ for (const item of combined) {
1611
+ const key = sessionItemKey(item);
1612
+ if (consumeReference(newInputRefs, key, item)) {
1613
+ decrementCount(newInputCounts, key);
1614
+ appended.push(item);
1615
+ continue;
1616
+ }
1617
+ // Prioritize exact history matches before payload-based counts so callbacks that surface
1618
+ // history ahead of identical new inputs keep previously persisted items out of the new queue.
1619
+ if (consumeReference(historyRefs, key, item)) {
1620
+ decrementCount(historyCounts, key);
1621
+ continue;
1622
+ }
1623
+ const historyRemaining = historyCounts.get(key) ?? 0;
1624
+ if (historyRemaining > 0) {
1625
+ historyCounts.set(key, historyRemaining - 1);
1626
+ continue;
1627
+ }
1628
+ const newRemaining = newInputCounts.get(key) ?? 0;
1629
+ if (newRemaining > 0) {
1630
+ newInputCounts.set(key, newRemaining - 1);
1631
+ appended.push(item);
1632
+ continue;
1633
+ }
1634
+ appended.push(item);
1635
+ }
1636
+ // Preserve redacted inputs for model delivery when requested (e.g. server-managed histories).
1637
+ const preparedItems = includeHistoryInPreparedInput
1638
+ ? combined
1639
+ : appended.length > 0
1640
+ ? appended
1641
+ : preserveDroppedNewItems
1642
+ ? newInputSnapshot
1643
+ : [];
1644
+ return {
1645
+ preparedInput: preparedItems,
1646
+ // Respect callbacks that intentionally drop the latest inputs (e.g. to redact sensitive
1647
+ // values) by persisting only the items they kept in the combined array.
1648
+ sessionItems: appended,
1649
+ };
1650
+ }
1651
+ /**
1652
+ * Accepts whatever the tool returned and attempts to coerce it into the structured protocol
1653
+ * shapes we expose to downstream model adapters (input_text/input_image/input_file). Tools are
1654
+ * allowed to return either a single structured object or an array of them; anything else falls
1655
+ * back to the legacy string pipeline.
1656
+ */
1657
+ function normalizeStructuredToolOutputs(output) {
1658
+ if (Array.isArray(output)) {
1659
+ const structured = [];
1660
+ for (const item of output) {
1661
+ const normalized = normalizeStructuredToolOutput(item);
1662
+ if (!normalized) {
1663
+ return null;
1664
+ }
1665
+ structured.push(normalized);
1666
+ }
1667
+ return structured;
1668
+ }
1669
+ const normalized = normalizeStructuredToolOutput(output);
1670
+ return normalized ? [normalized] : null;
1671
+ }
1672
+ /**
1673
+ * Best-effort normalization of a single tool output item. If the object already matches the
1674
+ * protocol shape we simply cast it; otherwise we copy the recognised fields into the canonical
1675
+ * structure. Returning null lets the caller know we should revert to plain-string handling.
1676
+ */
1677
+ function normalizeStructuredToolOutput(value) {
1678
+ if (!isRecord(value)) {
1679
+ return null;
1680
+ }
1681
+ const type = value.type;
1682
+ if (type === 'text' && typeof value.text === 'string') {
1683
+ const output = { type: 'text', text: value.text };
1684
+ if (isRecord(value.providerData)) {
1685
+ output.providerData = value.providerData;
1686
+ }
1687
+ return output;
1688
+ }
1689
+ if (type === 'image') {
1690
+ const output = { type: 'image' };
1691
+ let imageString;
1692
+ let imageFileId;
1693
+ const fallbackImageMediaType = isNonEmptyString(value.mediaType)
1694
+ ? value.mediaType
1695
+ : undefined;
1696
+ const imageField = value.image;
1697
+ if (typeof imageField === 'string' && imageField.length > 0) {
1698
+ imageString = imageField;
1699
+ }
1700
+ else if (isRecord(imageField)) {
1701
+ const imageObj = imageField;
1702
+ const inlineMediaType = isNonEmptyString(imageObj.mediaType)
1703
+ ? imageObj.mediaType
1704
+ : fallbackImageMediaType;
1705
+ if (isNonEmptyString(imageObj.url)) {
1706
+ imageString = imageObj.url;
1707
+ }
1708
+ else if (isNonEmptyString(imageObj.data)) {
1709
+ imageString = toInlineImageString(imageObj.data, inlineMediaType);
1710
+ }
1711
+ else if (imageObj.data instanceof Uint8Array &&
1712
+ imageObj.data.length > 0) {
1713
+ imageString = toInlineImageString(imageObj.data, inlineMediaType);
1714
+ }
1715
+ if (!imageString) {
1716
+ const candidateId = (isNonEmptyString(imageObj.fileId) && imageObj.fileId) ||
1717
+ (isNonEmptyString(imageObj.id) && imageObj.id) ||
1718
+ undefined;
1719
+ if (candidateId) {
1720
+ imageFileId = candidateId;
1721
+ }
1722
+ }
1723
+ }
1724
+ if (!imageString &&
1725
+ typeof value.imageUrl === 'string' &&
1726
+ value.imageUrl.length > 0) {
1727
+ imageString = value.imageUrl;
1728
+ }
1729
+ if (!imageFileId &&
1730
+ typeof value.fileId === 'string' &&
1731
+ value.fileId.length > 0) {
1732
+ imageFileId = value.fileId;
1733
+ }
1734
+ if (!imageString &&
1735
+ typeof value.data === 'string' &&
1736
+ value.data.length > 0) {
1737
+ imageString = fallbackImageMediaType
1738
+ ? toInlineImageString(value.data, fallbackImageMediaType)
1739
+ : value.data;
1740
+ }
1741
+ else if (!imageString &&
1742
+ value.data instanceof Uint8Array &&
1743
+ value.data.length > 0) {
1744
+ imageString = toInlineImageString(value.data, fallbackImageMediaType);
1745
+ }
1746
+ if (typeof value.detail === 'string' && value.detail.length > 0) {
1747
+ output.detail = value.detail;
1748
+ }
1749
+ if (imageString) {
1750
+ output.image = imageString;
1751
+ }
1752
+ else if (imageFileId) {
1753
+ output.image = { fileId: imageFileId };
1754
+ }
1755
+ else {
1756
+ return null;
1757
+ }
1758
+ if (isRecord(value.providerData)) {
1759
+ output.providerData = value.providerData;
1760
+ }
1761
+ return output;
1762
+ }
1763
+ if (type === 'file') {
1764
+ const fileValue = normalizeFileValue(value);
1765
+ if (!fileValue) {
1766
+ return null;
1767
+ }
1768
+ const output = { type: 'file', file: fileValue };
1769
+ if (isRecord(value.providerData)) {
1770
+ output.providerData = value.providerData;
1771
+ }
1772
+ return output;
1773
+ }
1774
+ return null;
1775
+ }
1776
+ /**
1777
+ * Translates the normalized tool output into the protocol `input_*` items. This is the last hop
1778
+ * before we hand the data to model-specific adapters, so we generate the exact schema expected by
1779
+ * the protocol definitions.
1780
+ */
1781
+ function convertStructuredToolOutputToInputItem(output) {
1782
+ if (output.type === 'text') {
1783
+ const result = {
1784
+ type: 'input_text',
1785
+ text: output.text,
1786
+ };
1787
+ if (output.providerData) {
1788
+ result.providerData = output.providerData;
1789
+ }
1790
+ return result;
1791
+ }
1792
+ if (output.type === 'image') {
1793
+ const result = { type: 'input_image' };
1794
+ if (typeof output.detail === 'string' && output.detail.length > 0) {
1795
+ result.detail = output.detail;
1796
+ }
1797
+ if (typeof output.image === 'string' && output.image.length > 0) {
1798
+ result.image = output.image;
1799
+ }
1800
+ else if (isRecord(output.image)) {
1801
+ const imageObj = output.image;
1802
+ const inlineMediaType = isNonEmptyString(imageObj.mediaType)
1803
+ ? imageObj.mediaType
1804
+ : undefined;
1805
+ if (isNonEmptyString(imageObj.url)) {
1806
+ result.image = imageObj.url;
1807
+ }
1808
+ else if (isNonEmptyString(imageObj.data)) {
1809
+ result.image =
1810
+ inlineMediaType && !imageObj.data.startsWith('data:')
1811
+ ? asDataUrl(imageObj.data, inlineMediaType)
1812
+ : imageObj.data;
1813
+ }
1814
+ else if (imageObj.data instanceof Uint8Array &&
1815
+ imageObj.data.length > 0) {
1816
+ const base64 = encodeUint8ArrayToBase64(imageObj.data);
1817
+ result.image = asDataUrl(base64, inlineMediaType);
1818
+ }
1819
+ else {
1820
+ const referencedId = (isNonEmptyString(imageObj.fileId) && imageObj.fileId) ||
1821
+ (isNonEmptyString(imageObj.id) && imageObj.id) ||
1822
+ undefined;
1823
+ if (referencedId) {
1824
+ result.image = { id: referencedId };
1825
+ }
1826
+ }
1827
+ }
1828
+ if (output.providerData) {
1829
+ result.providerData = output.providerData;
1830
+ }
1831
+ return result;
1832
+ }
1833
+ if (output.type === 'file') {
1834
+ const result = { type: 'input_file' };
1835
+ const fileValue = output.file;
1836
+ if (typeof fileValue === 'string') {
1837
+ result.file = fileValue;
1838
+ }
1839
+ else if (fileValue && typeof fileValue === 'object') {
1840
+ const record = fileValue;
1841
+ if ('data' in record && record.data) {
1842
+ const mediaType = record.mediaType ?? 'text/plain';
1843
+ if (typeof record.data === 'string') {
1844
+ result.file = asDataUrl(record.data, mediaType);
1845
+ }
1846
+ else {
1847
+ const base64 = encodeUint8ArrayToBase64(record.data);
1848
+ result.file = asDataUrl(base64, mediaType);
1849
+ }
1850
+ }
1851
+ else if (typeof record.url === 'string' && record.url.length > 0) {
1852
+ result.file = { url: record.url };
1853
+ }
1854
+ else {
1855
+ const referencedId = (typeof record.id === 'string' &&
1856
+ record.id.length > 0 &&
1857
+ record.id) ||
1858
+ (typeof record.fileId === 'string' && record.fileId.length > 0
1859
+ ? record.fileId
1860
+ : undefined);
1861
+ if (referencedId) {
1862
+ result.file = { id: referencedId };
1863
+ }
1864
+ }
1865
+ if (typeof record.filename === 'string' && record.filename.length > 0) {
1866
+ result.filename = record.filename;
1867
+ }
1868
+ }
1869
+ if (output.providerData) {
1870
+ result.providerData = output.providerData;
1871
+ }
1872
+ return result;
1873
+ }
1874
+ const exhaustiveCheck = output;
1875
+ return exhaustiveCheck;
1876
+ }
1877
+ function buildItemFrequencyMap(items) {
1878
+ const counts = new Map();
1879
+ for (const item of items) {
1880
+ const key = sessionItemKey(item);
1881
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1882
+ }
1883
+ return counts;
1884
+ }
1885
+ function buildItemReferenceMap(items) {
1886
+ const refs = new Map();
1887
+ for (const item of items) {
1888
+ const key = sessionItemKey(item);
1889
+ const list = refs.get(key);
1890
+ if (list) {
1891
+ list.push(item);
1892
+ }
1893
+ else {
1894
+ refs.set(key, [item]);
1895
+ }
1896
+ }
1897
+ return refs;
1898
+ }
1899
+ function consumeReference(refs, key, target) {
1900
+ const candidates = refs.get(key);
1901
+ if (!candidates || candidates.length === 0) {
1902
+ return false;
1903
+ }
1904
+ const index = candidates.findIndex((candidate) => candidate === target);
1905
+ if (index === -1) {
1906
+ return false;
1907
+ }
1908
+ candidates.splice(index, 1);
1909
+ if (candidates.length === 0) {
1910
+ refs.delete(key);
1911
+ }
1912
+ return true;
1913
+ }
1914
+ function decrementCount(map, key) {
1915
+ const remaining = (map.get(key) ?? 0) - 1;
1916
+ if (remaining <= 0) {
1917
+ map.delete(key);
1918
+ }
1919
+ else {
1920
+ map.set(key, remaining);
1921
+ }
1922
+ }
1923
+ function sessionItemKey(item) {
1924
+ return JSON.stringify(item, sessionSerializationReplacer);
1925
+ }
1926
+ function sessionSerializationReplacer(_key, value) {
1927
+ if (value instanceof ArrayBuffer) {
1928
+ return {
1929
+ __type: 'ArrayBuffer',
1930
+ data: encodeUint8ArrayToBase64(new Uint8Array(value)),
1931
+ };
1932
+ }
1933
+ if (isArrayBufferView(value)) {
1934
+ const view = value;
1935
+ return {
1936
+ __type: view.constructor.name,
1937
+ data: encodeUint8ArrayToBase64(new Uint8Array(view.buffer, view.byteOffset, view.byteLength)),
1938
+ };
1939
+ }
1940
+ if (isNodeBuffer(value)) {
1941
+ const view = value;
1942
+ return {
1943
+ __type: 'Buffer',
1944
+ data: encodeUint8ArrayToBase64(new Uint8Array(view.buffer, view.byteOffset, view.byteLength)),
1945
+ };
1946
+ }
1947
+ if (isSerializedBufferSnapshot(value)) {
1948
+ return {
1949
+ __type: 'Buffer',
1950
+ data: encodeUint8ArrayToBase64(Uint8Array.from(value.data)),
1951
+ };
1952
+ }
1953
+ return value;
1954
+ }
1166
1955
  //# sourceMappingURL=runImplementation.mjs.map