@openai/agents-openai 0.5.3 → 0.6.0

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.
@@ -6,8 +6,80 @@ import { HEADERS } from "./defaults.mjs";
6
6
  import { ResponsesWebSocketConnection, ResponsesWebSocketInternalError, isWebSocketNotOpenError, shouldWrapNoEventWebSocketError, throwIfAborted, webSocketFrameToText, withAbortSignal, withTimeout, } from "./responsesWebSocketConnection.mjs";
7
7
  import { applyHeadersToAccumulator, createHeaderAccumulator, ensureResponsesWebSocketPath, headerAccumulatorToRecord, headerAccumulatorToSDKHeaders, mergeQueryParamsIntoURL, splitResponsesTransportOverrides, } from "./responsesTransportUtils.mjs";
8
8
  import { CodeInterpreterStatus, FileSearchStatus, ImageGenerationStatus, WebSearchStatus, } from "./tools.mjs";
9
- import { camelOrSnakeToSnakeCase } from "./utils/providerData.mjs";
10
- import { encodeUint8ArrayToBase64 } from '@openai/agents-core/utils';
9
+ import { camelOrSnakeToSnakeCase, getSnakeCasedProviderDataWithoutReservedKeys, } from "./utils/providerData.mjs";
10
+ import { encodeUint8ArrayToBase64, getToolSearchExecution, getToolSearchProviderCallId, } from '@openai/agents-core/utils';
11
+ /**
12
+ * Tool search outputs are replayed through agents-core protocol items, which use camelCase
13
+ * field names, while the Responses API wire shape uses snake_case. Keep this codec even with
14
+ * first-class upstream types because the local protocol still normalizes these payloads.
15
+ */
16
+ function toOpenAIToolSearchOutputToolPayload(tool, _withinNamespace = false) {
17
+ if (!tool || typeof tool !== 'object' || Array.isArray(tool)) {
18
+ return tool;
19
+ }
20
+ if (tool.type === 'tool_reference' && typeof tool.functionName === 'string') {
21
+ return {
22
+ type: 'tool_reference',
23
+ function_name: tool.functionName,
24
+ ...(typeof tool.namespace === 'string'
25
+ ? { namespace: tool.namespace }
26
+ : {}),
27
+ };
28
+ }
29
+ if (tool.type === 'namespace' && Array.isArray(tool.tools)) {
30
+ return {
31
+ ...tool,
32
+ tools: tool.tools.map((entry) => toOpenAIToolSearchOutputToolPayload(entry, true)),
33
+ };
34
+ }
35
+ if (tool.type === 'function') {
36
+ const { deferLoading, ...rest } = tool;
37
+ return {
38
+ ...rest,
39
+ ...(typeof deferLoading === 'boolean'
40
+ ? { defer_loading: deferLoading }
41
+ : {}),
42
+ };
43
+ }
44
+ return tool;
45
+ }
46
+ function fromOpenAIToolSearchOutputToolPayload(tool, _withinNamespace = false) {
47
+ if (!tool || typeof tool !== 'object' || Array.isArray(tool)) {
48
+ return tool;
49
+ }
50
+ if (tool.type === 'tool_reference' &&
51
+ typeof tool.function_name === 'string') {
52
+ return {
53
+ type: 'tool_reference',
54
+ functionName: tool.function_name,
55
+ ...(typeof tool.namespace === 'string'
56
+ ? { namespace: tool.namespace }
57
+ : {}),
58
+ };
59
+ }
60
+ if (tool.type === 'namespace' && Array.isArray(tool.tools)) {
61
+ return {
62
+ ...tool,
63
+ tools: tool.tools.map((entry) => fromOpenAIToolSearchOutputToolPayload(entry, true)),
64
+ };
65
+ }
66
+ if (tool.type === 'function') {
67
+ const { defer_loading, ...rest } = tool;
68
+ return {
69
+ ...rest,
70
+ ...(typeof defer_loading === 'boolean'
71
+ ? { deferLoading: defer_loading }
72
+ : {}),
73
+ };
74
+ }
75
+ return tool;
76
+ }
77
+ function hasSerializedComputerDisplayMetadata(tool) {
78
+ return (typeof tool.environment === 'string' &&
79
+ Array.isArray(tool.dimensions) &&
80
+ tool.dimensions.length === 2 &&
81
+ tool.dimensions.every((value) => typeof value === 'number'));
82
+ }
11
83
  const HostedToolChoice = z.enum([
12
84
  'file_search',
13
85
  'web_search',
@@ -16,12 +88,16 @@ const HostedToolChoice = z.enum([
16
88
  'image_generation',
17
89
  'mcp',
18
90
  // Specialized local tools
19
- 'computer_use_preview',
20
91
  'shell',
21
92
  'apply_patch',
22
93
  ]);
23
94
  const DefaultToolChoice = z.enum(['auto', 'required', 'none']);
24
- function getToolChoice(toolChoice) {
95
+ const BuiltinComputerToolChoice = z.enum([
96
+ 'computer',
97
+ 'computer_use',
98
+ 'computer_use_preview',
99
+ ]);
100
+ function getToolChoice(toolChoice, options) {
25
101
  if (typeof toolChoice === 'undefined') {
26
102
  return undefined;
27
103
  }
@@ -29,12 +105,202 @@ function getToolChoice(toolChoice) {
29
105
  if (resultDefaultCheck.success) {
30
106
  return resultDefaultCheck.data;
31
107
  }
108
+ const builtinComputerToolChoice = BuiltinComputerToolChoice.safeParse(toolChoice);
109
+ if (builtinComputerToolChoice.success) {
110
+ if (hasBuiltinComputerTool(options?.tools) ||
111
+ options?.allowPromptSuppliedComputerTool === true) {
112
+ return getBuiltinComputerToolChoice(builtinComputerToolChoice.data, {
113
+ model: options?.model,
114
+ });
115
+ }
116
+ if (builtinComputerToolChoice.data === 'computer_use_preview') {
117
+ return { type: 'computer_use_preview' };
118
+ }
119
+ return { type: 'function', name: builtinComputerToolChoice.data };
120
+ }
32
121
  const result = HostedToolChoice.safeParse(toolChoice);
33
122
  if (result.success) {
34
123
  return { type: result.data };
35
124
  }
36
125
  return { type: 'function', name: toolChoice };
37
126
  }
127
+ function normalizeToolSearchStatus(status) {
128
+ return status === 'in_progress' ||
129
+ status === 'completed' ||
130
+ status === 'incomplete'
131
+ ? status
132
+ : null;
133
+ }
134
+ function hasBuiltinComputerTool(tools) {
135
+ return (tools ?? []).some((tool) => tool.type === 'computer' ||
136
+ tool.type === 'computer_use' ||
137
+ tool.type === 'computer_use_preview');
138
+ }
139
+ function isPreviewComputerModel(model) {
140
+ return typeof model === 'string' && model.startsWith('computer-use-preview');
141
+ }
142
+ function shouldUsePreviewComputerTool(options) {
143
+ if (isPreviewComputerModel(options?.model)) {
144
+ return true;
145
+ }
146
+ if (typeof options?.model === 'string') {
147
+ return false;
148
+ }
149
+ if (options?.toolChoice === 'computer' ||
150
+ options?.toolChoice === 'computer_use') {
151
+ return false;
152
+ }
153
+ return true;
154
+ }
155
+ function getBuiltinComputerToolChoice(toolChoice, options) {
156
+ if (shouldUsePreviewComputerTool({
157
+ model: options?.model,
158
+ toolChoice,
159
+ })) {
160
+ return { type: 'computer_use_preview' };
161
+ }
162
+ if (toolChoice === 'computer_use') {
163
+ return { type: 'computer_use' };
164
+ }
165
+ return { type: 'computer' };
166
+ }
167
+ function isBuiltinComputerToolType(type) {
168
+ return (type === 'computer' ||
169
+ type === 'computer_use' ||
170
+ type === 'computer_use_preview');
171
+ }
172
+ function isCompatibleBuiltinComputerToolChoice(toolChoiceType, toolType) {
173
+ if (!isBuiltinComputerToolType(toolChoiceType)) {
174
+ return false;
175
+ }
176
+ if (toolChoiceType === 'computer_use_preview') {
177
+ return toolType === 'computer_use_preview';
178
+ }
179
+ return toolType === 'computer';
180
+ }
181
+ function isToolChoiceAvailable(toolChoice, tools) {
182
+ if (toolChoice === 'auto' || toolChoice === 'none') {
183
+ return true;
184
+ }
185
+ if (toolChoice === 'required') {
186
+ return tools.length > 0;
187
+ }
188
+ if (toolChoice.type === 'function') {
189
+ return hasFunctionToolChoiceName(toolChoice.name, tools);
190
+ }
191
+ return tools.some((tool) => isCompatibleBuiltinComputerToolChoice(toolChoice.type, tool.type)
192
+ ? true
193
+ : tool.type === toolChoice.type);
194
+ }
195
+ function hasFunctionToolChoiceName(toolChoiceName, tools, namespacePrefix) {
196
+ return (findFunctionToolChoice(toolChoiceName, tools, namespacePrefix) !== undefined);
197
+ }
198
+ function findFunctionToolChoice(toolChoiceName, tools, namespacePrefix) {
199
+ for (const tool of tools) {
200
+ if (isNamedFunctionTool(tool)) {
201
+ const qualifiedName = namespacePrefix
202
+ ? `${namespacePrefix}.${tool.name}`
203
+ : tool.name;
204
+ if (toolChoiceName === qualifiedName) {
205
+ return tool;
206
+ }
207
+ continue;
208
+ }
209
+ if (isNamespaceTool(tool)) {
210
+ const nestedNamespace = namespacePrefix
211
+ ? `${namespacePrefix}.${tool.name}`
212
+ : tool.name;
213
+ const matchedTool = findFunctionToolChoice(toolChoiceName, tool.tools, nestedNamespace);
214
+ if (matchedTool) {
215
+ return matchedTool;
216
+ }
217
+ }
218
+ }
219
+ return undefined;
220
+ }
221
+ function collectAvailableToolChoiceNames(tools, namespacePrefix) {
222
+ const availableToolChoices = [];
223
+ for (const tool of tools) {
224
+ if (isNamedFunctionTool(tool)) {
225
+ availableToolChoices.push(namespacePrefix ? `${namespacePrefix}.${tool.name}` : tool.name);
226
+ continue;
227
+ }
228
+ if (isNamespaceTool(tool)) {
229
+ const nestedNamespace = namespacePrefix
230
+ ? `${namespacePrefix}.${tool.name}`
231
+ : tool.name;
232
+ availableToolChoices.push(...collectAvailableToolChoiceNames(tool.tools, nestedNamespace));
233
+ continue;
234
+ }
235
+ availableToolChoices.push(tool.type);
236
+ }
237
+ return availableToolChoices;
238
+ }
239
+ function isNamedFunctionTool(tool) {
240
+ return (tool.type === 'function' &&
241
+ typeof tool.name === 'string');
242
+ }
243
+ function isNamespaceTool(tool) {
244
+ const candidate = tool;
245
+ return (tool.type === 'namespace' &&
246
+ typeof candidate.name === 'string' &&
247
+ Array.isArray(candidate.tools));
248
+ }
249
+ function getExtraBodyToolsForToolChoiceValidation(extraBody) {
250
+ if (!extraBody || !Array.isArray(extraBody.tools)) {
251
+ return [];
252
+ }
253
+ return extraBody.tools;
254
+ }
255
+ function assertSupportedToolChoice(toolChoice, tools, options) {
256
+ const allowPromptSuppliedTools = options?.allowPromptSuppliedTools === true;
257
+ if (!toolChoice ||
258
+ toolChoice === 'auto' ||
259
+ toolChoice === 'required' ||
260
+ toolChoice === 'none' ||
261
+ toolChoice.type !== 'function') {
262
+ return;
263
+ }
264
+ const matchedFunctionTool = findFunctionToolChoice(toolChoice.name, tools);
265
+ if (!matchedFunctionTool &&
266
+ allowPromptSuppliedTools &&
267
+ toolChoice.name !== 'tool_search') {
268
+ return;
269
+ }
270
+ if (matchedFunctionTool
271
+ ?.defer_loading === true) {
272
+ throw new UserError(`modelSettings.toolChoice="${toolChoice.name}" cannot force a deferred function tool in Responses. Use "auto" so tool_search can load it.`);
273
+ }
274
+ if (toolChoice.name === 'tool_search' &&
275
+ !hasFunctionToolChoiceName(toolChoice.name, tools)) {
276
+ throw new UserError('modelSettings.toolChoice="tool_search" is only supported for a custom function named "tool_search". Responses does not support forcing the built-in tool_search tool. Use "auto" instead.');
277
+ }
278
+ }
279
+ function getCompatibleToolChoice(toolChoice, tools, options) {
280
+ const allowPromptSuppliedTools = options?.allowPromptSuppliedTools === true;
281
+ if (typeof toolChoice === 'undefined') {
282
+ return undefined;
283
+ }
284
+ if (isToolChoiceAvailable(toolChoice, tools) || allowPromptSuppliedTools) {
285
+ return toolChoice;
286
+ }
287
+ const availableToolChoices = [
288
+ ...new Set(collectAvailableToolChoiceNames(tools)),
289
+ ];
290
+ const availableToolChoicesMessage = availableToolChoices.length > 0
291
+ ? ` Available tools: ${availableToolChoices.join(', ')}.`
292
+ : ' No tools are available in the outgoing Responses request.';
293
+ if (toolChoice === 'required') {
294
+ throw new UserError(`modelSettings.toolChoice="required" requires at least one available tool in the outgoing Responses request.${availableToolChoicesMessage}`);
295
+ }
296
+ if (toolChoice === 'auto' || toolChoice === 'none') {
297
+ throw new Error(`Unexpected unavailable tool choice: ${JSON.stringify(toolChoice)}`);
298
+ }
299
+ if (toolChoice.type === 'function') {
300
+ throw new UserError(`modelSettings.toolChoice="${toolChoice.name}" does not match any available tool in the outgoing Responses request.${availableToolChoicesMessage}`);
301
+ }
302
+ throw new UserError(`modelSettings.toolChoice="${toolChoice.type}" is unavailable in the outgoing Responses request.${availableToolChoicesMessage}`);
303
+ }
38
304
  function getResponseFormat(outputType, otherProperties) {
39
305
  if (outputType === 'text') {
40
306
  return otherProperties;
@@ -88,12 +354,13 @@ function convertLegacyToolOutputContent(output) {
88
354
  const legacyImageUrl = output.imageUrl;
89
355
  const legacyFileId = output.fileId;
90
356
  const dataValue = output.data;
357
+ const topLevelInlineMediaType = getImageInlineMediaType(output);
91
358
  if (typeof output.image === 'string' && output.image.length > 0) {
92
359
  structured.image = output.image;
93
360
  }
94
361
  else if (isRecord(output.image)) {
95
362
  const imageObj = output.image;
96
- const inlineMediaType = getImageInlineMediaType(imageObj);
363
+ const inlineMediaType = getImageInlineMediaType(imageObj) ?? topLevelInlineMediaType;
97
364
  if (typeof imageObj.url === 'string' && imageObj.url.length > 0) {
98
365
  structured.image = imageObj.url;
99
366
  }
@@ -133,7 +400,7 @@ function convertLegacyToolOutputContent(output) {
133
400
  base64Data = encodeUint8ArrayToBase64(dataValue);
134
401
  }
135
402
  if (base64Data) {
136
- structured.image = base64Data;
403
+ structured.image = formatInlineData(base64Data, topLevelInlineMediaType);
137
404
  }
138
405
  }
139
406
  if (output.providerData) {
@@ -378,9 +645,16 @@ function getImageInlineMediaType(source) {
378
645
  if (typeof source.mediaType === 'string' && source.mediaType.length > 0) {
379
646
  return source.mediaType;
380
647
  }
648
+ if (typeof source.mimeType === 'string' &&
649
+ source.mimeType.length > 0) {
650
+ return source.mimeType;
651
+ }
381
652
  return undefined;
382
653
  }
383
654
  function formatInlineData(data, mediaType) {
655
+ if (typeof data === 'string' && data.startsWith('data:')) {
656
+ return data;
657
+ }
384
658
  const base64 = typeof data === 'string' ? data : encodeUint8ArrayToBase64(data);
385
659
  return mediaType ? `data:${mediaType};base64,${base64}` : base64;
386
660
  }
@@ -497,11 +771,70 @@ function toOpenAIShellEnvironment(environment) {
497
771
  }
498
772
  throw new UserError(`Unsupported shell environment type: ${String(environment.type)}`);
499
773
  }
500
- function getTools(tools, handoffs) {
774
+ function getTools(tools, handoffs, options) {
501
775
  const openaiTools = [];
502
776
  const include = [];
777
+ const namespaceStateByName = new Map();
778
+ let hasDeferredSearchableTool = false;
779
+ let hasToolSearch = false;
780
+ const usePreviewComputerTool = shouldUsePreviewComputerTool({
781
+ model: options?.model,
782
+ toolChoice: options?.toolChoice,
783
+ });
503
784
  for (const tool of tools) {
504
- const { tool: openaiTool, include: openaiIncludes } = converTool(tool);
785
+ if (tool.type === 'function') {
786
+ const isDeferredFunction = tool.deferLoading === true;
787
+ hasDeferredSearchableTool ||= isDeferredFunction;
788
+ const namespaceName = typeof tool.namespace === 'string' ? tool.namespace.trim() : '';
789
+ if (namespaceName.length > 0) {
790
+ const namespaceDescription = typeof tool.namespaceDescription === 'string'
791
+ ? tool.namespaceDescription.trim()
792
+ : '';
793
+ if (namespaceDescription.length === 0) {
794
+ throw new UserError(`All tools in namespace "${namespaceName}" must provide a non-empty description.`);
795
+ }
796
+ let namespaceState = namespaceStateByName.get(namespaceName);
797
+ if (!namespaceState) {
798
+ namespaceState = {
799
+ index: openaiTools.length,
800
+ description: namespaceDescription,
801
+ functionNames: new Set(),
802
+ tools: [],
803
+ };
804
+ namespaceStateByName.set(namespaceName, namespaceState);
805
+ openaiTools.push({});
806
+ }
807
+ else if (namespaceState.description !== namespaceDescription) {
808
+ throw new UserError(`All tools in namespace "${namespaceName}" must share the same description.`);
809
+ }
810
+ const { tool: openaiTool, include: openaiIncludes } = converTool(tool, {
811
+ usePreviewComputerTool,
812
+ });
813
+ if (namespaceState.functionNames.has(tool.name)) {
814
+ throw new UserError(`Namespace "${namespaceName}" cannot contain duplicate function tool name "${tool.name}".`);
815
+ }
816
+ namespaceState.functionNames.add(tool.name);
817
+ namespaceState.tools.push(openaiTool);
818
+ if (openaiIncludes && openaiIncludes.length > 0) {
819
+ for (const item of openaiIncludes) {
820
+ include.push(item);
821
+ }
822
+ }
823
+ continue;
824
+ }
825
+ }
826
+ if (tool.type === 'hosted_tool' &&
827
+ tool.providerData?.type === 'tool_search') {
828
+ hasToolSearch = true;
829
+ }
830
+ if (tool.type === 'hosted_tool' &&
831
+ tool.providerData?.type === 'mcp' &&
832
+ tool.providerData.defer_loading === true) {
833
+ hasDeferredSearchableTool = true;
834
+ }
835
+ const { tool: openaiTool, include: openaiIncludes } = converTool(tool, {
836
+ usePreviewComputerTool,
837
+ });
505
838
  openaiTools.push(openaiTool);
506
839
  if (openaiIncludes && openaiIncludes.length > 0) {
507
840
  for (const item of openaiIncludes) {
@@ -509,31 +842,57 @@ function getTools(tools, handoffs) {
509
842
  }
510
843
  }
511
844
  }
845
+ if (hasDeferredSearchableTool && !hasToolSearch) {
846
+ throw new UserError('Deferred function tools and hosted MCP tools with deferLoading: true require toolSearchTool() in the same request.');
847
+ }
848
+ for (const [namespaceName, namespaceState,] of namespaceStateByName.entries()) {
849
+ openaiTools[namespaceState.index] = {
850
+ type: 'namespace',
851
+ name: namespaceName,
852
+ description: namespaceState.description,
853
+ tools: namespaceState.tools,
854
+ };
855
+ }
512
856
  return {
513
857
  tools: [...openaiTools, ...handoffs.map(getHandoffTool)],
514
858
  include,
515
859
  };
516
860
  }
517
- function converTool(tool) {
861
+ function converTool(tool, options) {
518
862
  if (tool.type === 'function') {
863
+ const openaiTool = {
864
+ type: 'function',
865
+ name: tool.name,
866
+ description: tool.description,
867
+ parameters: tool.parameters,
868
+ strict: tool.strict,
869
+ };
870
+ if (tool.deferLoading) {
871
+ openaiTool.defer_loading = true;
872
+ }
519
873
  return {
520
- tool: {
521
- type: 'function',
522
- name: tool.name,
523
- description: tool.description,
524
- parameters: tool.parameters,
525
- strict: tool.strict,
526
- },
874
+ tool: openaiTool,
527
875
  include: undefined,
528
876
  };
529
877
  }
530
878
  else if (tool.type === 'computer') {
879
+ if (options?.usePreviewComputerTool) {
880
+ if (!hasSerializedComputerDisplayMetadata(tool)) {
881
+ throw new UserError('Preview computer tools require environment and dimensions. Provide them on your Computer implementation or target a GA computer model such as gpt-5.4.');
882
+ }
883
+ return {
884
+ tool: {
885
+ type: 'computer_use_preview',
886
+ environment: tool.environment,
887
+ display_width: tool.dimensions[0],
888
+ display_height: tool.dimensions[1],
889
+ },
890
+ include: undefined,
891
+ };
892
+ }
531
893
  return {
532
894
  tool: {
533
- type: 'computer_use_preview',
534
- environment: tool.environment,
535
- display_width: tool.dimensions[0],
536
- display_height: tool.dimensions[1],
895
+ type: 'computer',
537
896
  },
538
897
  include: undefined,
539
898
  };
@@ -604,6 +963,17 @@ function converTool(tool) {
604
963
  include: undefined,
605
964
  };
606
965
  }
966
+ else if (tool.providerData?.type === 'tool_search') {
967
+ return {
968
+ tool: {
969
+ type: 'tool_search',
970
+ execution: tool.providerData.execution,
971
+ description: tool.providerData.description,
972
+ parameters: tool.providerData.parameters,
973
+ },
974
+ include: undefined,
975
+ };
976
+ }
607
977
  else if (tool.providerData?.type === 'image_generation') {
608
978
  return {
609
979
  tool: {
@@ -623,17 +993,22 @@ function converTool(tool) {
623
993
  };
624
994
  }
625
995
  else if (tool.providerData?.type === 'mcp') {
996
+ const openaiTool = {
997
+ type: 'mcp',
998
+ server_label: tool.providerData.server_label,
999
+ server_url: tool.providerData.server_url,
1000
+ connector_id: tool.providerData.connector_id,
1001
+ authorization: tool.providerData.authorization,
1002
+ allowed_tools: tool.providerData.allowed_tools,
1003
+ headers: tool.providerData.headers,
1004
+ require_approval: convertMCPRequireApproval(tool.providerData.require_approval),
1005
+ server_description: tool.providerData.server_description,
1006
+ };
1007
+ if (tool.providerData.defer_loading === true) {
1008
+ openaiTool.defer_loading = true;
1009
+ }
626
1010
  return {
627
- tool: {
628
- type: 'mcp',
629
- server_label: tool.providerData.server_label,
630
- server_url: tool.providerData.server_url,
631
- connector_id: tool.providerData.connector_id,
632
- authorization: tool.providerData.authorization,
633
- allowed_tools: tool.providerData.allowed_tools,
634
- headers: tool.providerData.headers,
635
- require_approval: convertMCPRequireApproval(tool.providerData.require_approval),
636
- },
1011
+ tool: openaiTool,
637
1012
  include: undefined,
638
1013
  };
639
1014
  }
@@ -672,7 +1047,10 @@ function getInputMessageContent(entry) {
672
1047
  return {
673
1048
  type: 'input_text',
674
1049
  text: entry.text,
675
- ...camelOrSnakeToSnakeCase(entry.providerData),
1050
+ ...getSnakeCasedProviderDataWithoutReservedKeys(entry.providerData, [
1051
+ 'type',
1052
+ 'text',
1053
+ ]),
676
1054
  };
677
1055
  }
678
1056
  else if (entry.type === 'input_image') {
@@ -694,7 +1072,12 @@ function getInputMessageContent(entry) {
694
1072
  }
695
1073
  return {
696
1074
  ...imageEntry,
697
- ...camelOrSnakeToSnakeCase(entry.providerData),
1075
+ ...getSnakeCasedProviderDataWithoutReservedKeys(entry.providerData, [
1076
+ 'type',
1077
+ 'detail',
1078
+ 'image_url',
1079
+ 'file_id',
1080
+ ]),
698
1081
  };
699
1082
  }
700
1083
  else if (entry.type === 'input_file') {
@@ -739,25 +1122,54 @@ function getInputMessageContent(entry) {
739
1122
  }
740
1123
  return {
741
1124
  ...fileEntry,
742
- ...camelOrSnakeToSnakeCase(entry.providerData),
1125
+ ...getSnakeCasedProviderDataWithoutReservedKeys(entry.providerData, [
1126
+ 'type',
1127
+ 'file_data',
1128
+ 'file_url',
1129
+ 'file_id',
1130
+ 'filename',
1131
+ ]),
743
1132
  };
744
1133
  }
745
1134
  throw new UserError(`Unsupported input content type: ${JSON.stringify(entry)}`);
746
1135
  }
1136
+ function getProviderDataField(providerData, keys) {
1137
+ if (!providerData ||
1138
+ typeof providerData !== 'object' ||
1139
+ Array.isArray(providerData)) {
1140
+ return undefined;
1141
+ }
1142
+ const record = providerData;
1143
+ for (const key of keys) {
1144
+ if (typeof record[key] !== 'undefined') {
1145
+ return record[key];
1146
+ }
1147
+ }
1148
+ return undefined;
1149
+ }
747
1150
  function getOutputMessageContent(entry) {
748
1151
  if (entry.type === 'output_text') {
1152
+ const annotations = getProviderDataField(entry.providerData, ['annotations']);
1153
+ const normalizedAnnotations = Array.isArray(annotations) ? annotations : [];
749
1154
  return {
750
1155
  type: 'output_text',
751
1156
  text: entry.text,
752
- annotations: [],
753
- ...camelOrSnakeToSnakeCase(entry.providerData),
1157
+ annotations: normalizedAnnotations,
1158
+ ...getSnakeCasedProviderDataWithoutReservedKeys(entry.providerData, [
1159
+ 'type',
1160
+ 'text',
1161
+ 'annotations',
1162
+ ]),
754
1163
  };
755
1164
  }
756
1165
  if (entry.type === 'refusal') {
757
1166
  return {
758
1167
  type: 'refusal',
759
1168
  refusal: entry.refusal,
760
- ...camelOrSnakeToSnakeCase(entry.providerData),
1169
+ ...getSnakeCasedProviderDataWithoutReservedKeys(entry.providerData, [
1170
+ 'type',
1171
+ 'refusal',
1172
+ ]),
761
1173
  };
762
1174
  }
763
1175
  throw new UserError(`Unsupported output content type: ${JSON.stringify(entry)}`);
@@ -768,7 +1180,11 @@ function getMessageItem(item) {
768
1180
  id: item.id,
769
1181
  role: 'system',
770
1182
  content: item.content,
771
- ...camelOrSnakeToSnakeCase(item.providerData),
1183
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1184
+ 'id',
1185
+ 'role',
1186
+ 'content',
1187
+ ]),
772
1188
  };
773
1189
  }
774
1190
  if (item.role === 'user') {
@@ -777,14 +1193,22 @@ function getMessageItem(item) {
777
1193
  id: item.id,
778
1194
  role: 'user',
779
1195
  content: item.content,
780
- ...camelOrSnakeToSnakeCase(item.providerData),
1196
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1197
+ 'id',
1198
+ 'role',
1199
+ 'content',
1200
+ ]),
781
1201
  };
782
1202
  }
783
1203
  return {
784
1204
  id: item.id,
785
1205
  role: 'user',
786
1206
  content: item.content.map(getInputMessageContent),
787
- ...camelOrSnakeToSnakeCase(item.providerData),
1207
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1208
+ 'id',
1209
+ 'role',
1210
+ 'content',
1211
+ ]),
788
1212
  };
789
1213
  }
790
1214
  if (item.role === 'assistant') {
@@ -794,7 +1218,13 @@ function getMessageItem(item) {
794
1218
  role: 'assistant',
795
1219
  content: item.content.map(getOutputMessageContent),
796
1220
  status: item.status,
797
- ...camelOrSnakeToSnakeCase(item.providerData),
1221
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1222
+ 'type',
1223
+ 'id',
1224
+ 'role',
1225
+ 'content',
1226
+ 'status',
1227
+ ]),
798
1228
  };
799
1229
  return assistantMessage;
800
1230
  }
@@ -841,6 +1271,52 @@ function getInputItems(input) {
841
1271
  if (isMessageItem(item)) {
842
1272
  return getMessageItem(item);
843
1273
  }
1274
+ if (item.type === 'tool_search_call') {
1275
+ const status = normalizeToolSearchStatus(item.status);
1276
+ const callId = getToolSearchProviderCallId(item);
1277
+ const execution = getToolSearchExecution(item);
1278
+ const toolSearchCall = {
1279
+ type: 'tool_search_call',
1280
+ id: item.id,
1281
+ ...(status !== null ? { status } : {}),
1282
+ arguments: item.arguments,
1283
+ ...(callId ? { call_id: callId } : {}),
1284
+ ...(execution ? { execution } : {}),
1285
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1286
+ 'type',
1287
+ 'id',
1288
+ 'status',
1289
+ 'arguments',
1290
+ 'call_id',
1291
+ 'callId',
1292
+ 'execution',
1293
+ ]),
1294
+ };
1295
+ return toolSearchCall;
1296
+ }
1297
+ if (item.type === 'tool_search_output') {
1298
+ const status = normalizeToolSearchStatus(item.status);
1299
+ const callId = getToolSearchProviderCallId(item);
1300
+ const execution = getToolSearchExecution(item);
1301
+ const toolSearchOutput = {
1302
+ type: 'tool_search_output',
1303
+ id: item.id,
1304
+ ...(status !== null ? { status } : {}),
1305
+ tools: item.tools.map((tool) => toOpenAIToolSearchOutputToolPayload(tool)),
1306
+ ...(callId ? { call_id: callId } : {}),
1307
+ ...(execution ? { execution } : {}),
1308
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1309
+ 'type',
1310
+ 'id',
1311
+ 'status',
1312
+ 'tools',
1313
+ 'call_id',
1314
+ 'callId',
1315
+ 'execution',
1316
+ ]),
1317
+ };
1318
+ return toolSearchOutput;
1319
+ }
844
1320
  if (item.type === 'function_call') {
845
1321
  const entry = {
846
1322
  id: item.id,
@@ -849,7 +1325,18 @@ function getInputItems(input) {
849
1325
  call_id: item.callId,
850
1326
  arguments: item.arguments,
851
1327
  status: item.status,
852
- ...camelOrSnakeToSnakeCase(item.providerData),
1328
+ ...(typeof item.namespace === 'string'
1329
+ ? { namespace: item.namespace }
1330
+ : {}),
1331
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1332
+ 'id',
1333
+ 'type',
1334
+ 'name',
1335
+ 'call_id',
1336
+ 'arguments',
1337
+ 'status',
1338
+ 'namespace',
1339
+ ]),
853
1340
  };
854
1341
  return entry;
855
1342
  }
@@ -861,45 +1348,98 @@ function getInputItems(input) {
861
1348
  call_id: item.callId,
862
1349
  output: normalizedOutput,
863
1350
  status: item.status,
864
- ...camelOrSnakeToSnakeCase(item.providerData),
1351
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1352
+ 'type',
1353
+ 'id',
1354
+ 'call_id',
1355
+ 'output',
1356
+ 'status',
1357
+ 'namespace',
1358
+ ]),
865
1359
  };
866
1360
  return entry;
867
1361
  }
868
1362
  if (item.type === 'reasoning') {
1363
+ const encryptedContent = getProviderDataField(item.providerData, [
1364
+ 'encryptedContent',
1365
+ 'encrypted_content',
1366
+ ]);
869
1367
  const entry = {
870
1368
  id: item.id,
871
1369
  type: 'reasoning',
872
1370
  summary: item.content.map((content) => ({
873
1371
  type: 'summary_text',
874
1372
  text: content.text,
875
- ...camelOrSnakeToSnakeCase(content.providerData),
1373
+ ...getSnakeCasedProviderDataWithoutReservedKeys(content.providerData, ['type', 'text']),
876
1374
  })),
877
- encrypted_content: item.providerData?.encryptedContent,
878
- ...camelOrSnakeToSnakeCase(item.providerData),
1375
+ ...(typeof encryptedContent === 'string'
1376
+ ? { encrypted_content: encryptedContent }
1377
+ : {}),
1378
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1379
+ 'id',
1380
+ 'type',
1381
+ 'summary',
1382
+ 'encrypted_content',
1383
+ ]),
879
1384
  };
880
1385
  return entry;
881
1386
  }
882
1387
  if (item.type === 'computer_call') {
1388
+ const pendingSafetyChecks = getProviderDataField(item.providerData, ['pendingSafetyChecks', 'pending_safety_checks']);
1389
+ const normalizedPendingSafetyChecks = Array.isArray(pendingSafetyChecks) ? pendingSafetyChecks : [];
1390
+ const batchedActions = Array.isArray(item.actions)
1391
+ ? (item
1392
+ .actions ?? [])
1393
+ : [];
1394
+ const actionPayload = batchedActions.length > 0
1395
+ ? {
1396
+ action: item.action ?? batchedActions[0],
1397
+ actions: batchedActions,
1398
+ }
1399
+ : item.action
1400
+ ? { action: item.action }
1401
+ : {};
883
1402
  const entry = {
884
1403
  type: 'computer_call',
885
1404
  call_id: item.callId,
886
1405
  id: item.id,
887
- action: item.action,
888
1406
  status: item.status,
889
- pending_safety_checks: [],
890
- ...camelOrSnakeToSnakeCase(item.providerData),
1407
+ pending_safety_checks: normalizedPendingSafetyChecks,
1408
+ ...actionPayload,
1409
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1410
+ 'type',
1411
+ 'call_id',
1412
+ 'id',
1413
+ 'action',
1414
+ 'actions',
1415
+ 'status',
1416
+ 'pending_safety_checks',
1417
+ ]),
891
1418
  };
892
1419
  return entry;
893
1420
  }
894
1421
  if (item.type === 'computer_call_result') {
1422
+ const acknowledgedSafetyChecks = getProviderDataField(item.providerData, [
1423
+ 'acknowledgedSafetyChecks',
1424
+ 'acknowledged_safety_checks',
1425
+ ]);
895
1426
  const entry = {
896
1427
  type: 'computer_call_output',
897
1428
  id: item.id,
898
1429
  call_id: item.callId,
899
1430
  output: buildResponseOutput(item),
900
1431
  status: item.providerData?.status,
901
- acknowledged_safety_checks: item.providerData?.acknowledgedSafetyChecks,
902
- ...camelOrSnakeToSnakeCase(item.providerData),
1432
+ acknowledged_safety_checks: Array.isArray(acknowledgedSafetyChecks)
1433
+ ? acknowledgedSafetyChecks
1434
+ : [],
1435
+ ...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
1436
+ 'type',
1437
+ 'id',
1438
+ 'call_id',
1439
+ 'output',
1440
+ 'status',
1441
+ 'acknowledged_safety_checks',
1442
+ ]),
903
1443
  };
904
1444
  return entry;
905
1445
  }
@@ -1152,6 +1692,30 @@ function convertToOutputItem(items) {
1152
1692
  providerData,
1153
1693
  };
1154
1694
  }
1695
+ else if (item.type === 'tool_search_call') {
1696
+ const { id, type: _type, status, arguments: args, ...providerData } = item;
1697
+ const output = {
1698
+ type: 'tool_search_call',
1699
+ id,
1700
+ status,
1701
+ arguments: args,
1702
+ providerData,
1703
+ };
1704
+ return output;
1705
+ }
1706
+ else if (item.type === 'tool_search_output') {
1707
+ const { id, type: _type, status, tools, ...providerData } = item;
1708
+ const output = {
1709
+ type: 'tool_search_output',
1710
+ id,
1711
+ status,
1712
+ tools: Array.isArray(tools)
1713
+ ? tools.map((tool) => fromOpenAIToolSearchOutputToolPayload(tool))
1714
+ : [],
1715
+ providerData,
1716
+ };
1717
+ return output;
1718
+ }
1155
1719
  else if (item.type === 'file_search_call' ||
1156
1720
  item.type === 'web_search_call' ||
1157
1721
  item.type === 'image_generation_call' ||
@@ -1174,12 +1738,14 @@ function convertToOutputItem(items) {
1174
1738
  return output;
1175
1739
  }
1176
1740
  else if (item.type === 'function_call') {
1177
- const { call_id, name, status, arguments: args, ...providerData } = item;
1741
+ const functionCall = item;
1742
+ const { call_id, name, namespace, status, arguments: args, ...providerData } = functionCall;
1178
1743
  const output = {
1179
1744
  type: 'function_call',
1180
- id: item.id,
1745
+ id: functionCall.id,
1181
1746
  callId: call_id,
1182
1747
  name,
1748
+ ...(typeof namespace === 'string' ? { namespace } : {}),
1183
1749
  status,
1184
1750
  arguments: args,
1185
1751
  providerData,
@@ -1187,12 +1753,13 @@ function convertToOutputItem(items) {
1187
1753
  return output;
1188
1754
  }
1189
1755
  else if (item.type === 'function_call_output') {
1190
- const { call_id, status, output: rawOutput, name: toolName, function_name: functionName, ...providerData } = item;
1756
+ const { call_id, status, output: rawOutput, name: toolName, function_name: functionName, namespace, ...providerData } = item;
1191
1757
  const output = {
1192
1758
  type: 'function_call_result',
1193
1759
  id: item.id,
1194
1760
  callId: call_id,
1195
1761
  name: toolName ?? functionName ?? call_id,
1762
+ ...(typeof namespace === 'string' ? { namespace } : {}),
1196
1763
  status: status ?? 'completed',
1197
1764
  output: convertFunctionCallOutputToProtocol(rawOutput),
1198
1765
  providerData,
@@ -1200,13 +1767,18 @@ function convertToOutputItem(items) {
1200
1767
  return output;
1201
1768
  }
1202
1769
  else if (item.type === 'computer_call') {
1203
- const { call_id, status, action, ...providerData } = item;
1770
+ const { call_id, status, action, actions, ...providerData } = item;
1771
+ const normalizedActions = Array.isArray(actions) && actions.length > 0 ? actions : undefined;
1772
+ if (!normalizedActions && !action) {
1773
+ throw new UserError(`Unsupported computer call item without an action or actions: ${JSON.stringify(item)}`);
1774
+ }
1204
1775
  const output = {
1205
1776
  type: 'computer_call',
1206
1777
  id: item.id,
1207
1778
  callId: call_id,
1208
1779
  status,
1209
- action,
1780
+ action: action ?? normalizedActions?.[0],
1781
+ ...(normalizedActions ? { actions: normalizedActions } : {}),
1210
1782
  providerData,
1211
1783
  };
1212
1784
  return output;
@@ -1486,9 +2058,30 @@ export class OpenAIResponsesModel {
1486
2058
  }
1487
2059
  _buildResponsesCreateRequest(request, stream) {
1488
2060
  const input = getInputItems(request.input);
1489
- const { tools, include } = getTools(request.tools, request.handoffs);
1490
- const toolChoice = getToolChoice(request.modelSettings.toolChoice);
2061
+ const prompt = getPrompt(request.prompt);
2062
+ // When a prompt template already declares a model, skip sending the agent's default model.
2063
+ // If the caller explicitly requests an override, include the resolved model name in the request.
2064
+ const shouldSendModel = !request.prompt || request.overridePromptModel === true;
2065
+ const effectiveRequestModel = shouldSendModel ? this._model : undefined;
1491
2066
  const { providerData: providerDataWithoutTransport, overrides: transportOverrides, } = splitResponsesTransportOverrides(request.modelSettings.providerData);
2067
+ const { tools, include } = getTools(request.tools, request.handoffs, {
2068
+ model: effectiveRequestModel,
2069
+ toolChoice: request.modelSettings.toolChoice,
2070
+ });
2071
+ const toolChoiceValidationTools = [
2072
+ ...tools,
2073
+ ...getExtraBodyToolsForToolChoiceValidation(transportOverrides.extraBody),
2074
+ ];
2075
+ const allowPromptSuppliedTools = Boolean(request.prompt) &&
2076
+ !(request.toolsExplicitlyProvided === true && tools.length === 0);
2077
+ const toolChoice = getToolChoice(request.modelSettings.toolChoice, {
2078
+ tools: toolChoiceValidationTools,
2079
+ model: effectiveRequestModel,
2080
+ allowPromptSuppliedComputerTool: allowPromptSuppliedTools,
2081
+ });
2082
+ assertSupportedToolChoice(toolChoice, toolChoiceValidationTools, {
2083
+ allowPromptSuppliedTools,
2084
+ });
1492
2085
  const { text, ...restOfProviderData } = providerDataWithoutTransport;
1493
2086
  if (request.modelSettings.reasoning) {
1494
2087
  // Merge top-level reasoning settings with provider data.
@@ -1503,25 +2096,19 @@ export class OpenAIResponsesModel {
1503
2096
  mergedText = { ...request.modelSettings.text, ...text };
1504
2097
  }
1505
2098
  const responseFormat = getResponseFormat(request.outputType, mergedText);
1506
- const prompt = getPrompt(request.prompt);
1507
2099
  let parallelToolCalls = undefined;
1508
2100
  if (typeof request.modelSettings.parallelToolCalls === 'boolean') {
1509
- if (request.modelSettings.parallelToolCalls && tools.length === 0) {
1510
- throw new Error('Parallel tool calls are not supported without tools');
1511
- }
1512
2101
  parallelToolCalls = request.modelSettings.parallelToolCalls;
1513
2102
  }
1514
- // When a prompt template already declares a model, skip sending the agent's default model.
1515
- // If the caller explicitly requests an override, include the resolved model name in the request.
1516
- const shouldSendModel = !request.prompt || request.overridePromptModel === true;
1517
2103
  const shouldSendTools = tools.length > 0 ||
1518
2104
  request.toolsExplicitlyProvided === true ||
1519
2105
  !request.prompt;
1520
- const shouldOmitToolChoice = Boolean(request.prompt) &&
1521
- !shouldSendTools &&
1522
- typeof toolChoice === 'object';
2106
+ const compatibleToolChoice = getCompatibleToolChoice(toolChoice, toolChoiceValidationTools, {
2107
+ allowPromptSuppliedTools,
2108
+ });
2109
+ const shouldOmitToolChoice = typeof compatibleToolChoice === 'undefined';
1523
2110
  let requestData = {
1524
- ...(shouldSendModel ? { model: this._model } : {}),
2111
+ ...(effectiveRequestModel ? { model: effectiveRequestModel } : {}),
1525
2112
  instructions: normalizeInstructions(request.systemInstructions),
1526
2113
  input,
1527
2114
  include,
@@ -1538,7 +2125,7 @@ export class OpenAIResponsesModel {
1538
2125
  truncation: request.modelSettings.truncation,
1539
2126
  max_output_tokens: request.modelSettings.maxTokens,
1540
2127
  ...(!shouldOmitToolChoice
1541
- ? { tool_choice: toolChoice }
2128
+ ? { tool_choice: compatibleToolChoice }
1542
2129
  : {}),
1543
2130
  parallel_tool_calls: parallelToolCalls,
1544
2131
  stream,