@galaxy-yearn/codex-deepseek-gateway 0.1.4 → 0.1.6

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.
package/src/protocol.js CHANGED
@@ -1,5 +1,10 @@
1
1
  import { generateId, isObject, normalizeRole, toText } from './common.js';
2
- import { deepseekReasoningPayload, resolveModelAlias } from './model-map.js';
2
+ import {
3
+ DEFAULT_MODEL_ALIASES,
4
+ deepseekReasoningPayload,
5
+ isDeprecatedModel,
6
+ resolveModelAlias,
7
+ } from './model-map.js';
3
8
 
4
9
  const TEXT_PART_TYPES = new Set(['input_text', 'output_text', 'text']);
5
10
  const INPUT_CONTENT_PART_TYPES = new Set(['input_text', 'input_image', 'input_file', 'input_audio']);
@@ -10,6 +15,7 @@ const TOOL_CALL_TYPES = new Set([
10
15
  'computer_call',
11
16
  'mcp_call',
12
17
  'web_search_call',
18
+ 'tool_search_call',
13
19
  'file_search_call',
14
20
  'code_interpreter_call',
15
21
  'image_generation_call',
@@ -21,18 +27,47 @@ const TOOL_OUTPUT_TYPES = new Set([
21
27
  'computer_call_output',
22
28
  'mcp_call_output',
23
29
  'web_search_call_output',
30
+ 'tool_search_output',
24
31
  'file_search_call_output',
25
32
  'code_interpreter_call_output',
26
33
  'image_generation_call_output',
27
34
  ]);
28
- const CHAT_HISTORY_IGNORED_TOOL_ITEM_TYPES = new Set(['web_search_call', 'web_search_call_output']);
35
+ const CHAT_HISTORY_IGNORED_TOOL_ITEM_TYPES = new Set([
36
+ 'web_search_call',
37
+ 'web_search_call_output',
38
+ 'tool_search_call',
39
+ 'tool_search_output',
40
+ ]);
29
41
  const EMULATED_HOSTED_TOOL_TYPES = new Set(['web_search', 'web_search_preview']);
42
+ const DEEPSEEK_TOOL_INSTRUCTIONS_MARKER = 'The tools in this request are real callable functions available now.';
43
+ const DEEPSEEK_TOOL_INSTRUCTIONS = [
44
+ DEEPSEEK_TOOL_INSTRUCTIONS_MARKER,
45
+ 'When a tool is needed, respond with Chat Completions tool_calls and JSON arguments matching the schema.',
46
+ 'Do not write pseudo XML, DSML, or JSON tool calls in assistant text.',
47
+ 'Do not claim a listed function is unavailable because its name is unfamiliar.',
48
+ ].join(' ');
49
+ const DEEPSEEK_TOOL_DESCRIPTION_CHARS = 220;
50
+ const DEEPSEEK_SCHEMA_DESCRIPTION_CHARS = 160;
51
+ const CODEX_REASONING_EFFORTS = ['low', 'medium', 'high', 'xhigh'];
52
+ const CODEX_SPAWN_AGENT_TOOL_NAME = 'multi_agent_v1__spawn_agent';
53
+ const CODEX_TOOL_SEARCH_TOOL_NAME = 'tool_search';
30
54
 
31
55
  function jsonString(value) {
32
56
  if (typeof value === 'string') return value;
33
57
  return JSON.stringify(value ?? {});
34
58
  }
35
59
 
60
+ function parseJsonObject(value) {
61
+ if (isObject(value)) return value;
62
+ if (typeof value !== 'string') return {};
63
+ try {
64
+ const parsed = JSON.parse(value);
65
+ return isObject(parsed) ? parsed : {};
66
+ } catch {
67
+ return {};
68
+ }
69
+ }
70
+
36
71
  function sanitizeFunctionName(name, fallback = 'tool_call') {
37
72
  const candidate = String(name || fallback)
38
73
  .replace(/[^a-zA-Z0-9_-]/g, '_')
@@ -41,6 +76,34 @@ function sanitizeFunctionName(name, fallback = 'tool_call') {
41
76
  return candidate || fallback;
42
77
  }
43
78
 
79
+ function toolNamespace(tool) {
80
+ if (!isObject(tool)) return '';
81
+ return String(tool.namespace || tool.function?.namespace || '').trim();
82
+ }
83
+
84
+ function toolBaseName(tool, fallback = 'tool_call') {
85
+ if (!isObject(tool)) return fallback;
86
+ if (isObject(tool.function) && tool.function.name) return tool.function.name;
87
+ return tool.name || tool.tool_name || tool.toolName || tool.server_label || fallback;
88
+ }
89
+
90
+ function encodeToolName(namespace, name, fallback = 'tool_call') {
91
+ const baseName = sanitizeFunctionName(name, fallback);
92
+ if (!namespace) return baseName;
93
+ const encodedNamespace = sanitizeFunctionName(namespace, 'namespace');
94
+ return sanitizeFunctionName(`${encodedNamespace}__${baseName}`, fallback);
95
+ }
96
+
97
+ function decodedToolName(name, toolNames) {
98
+ const value = String(name || '');
99
+ const known = toolNames?.get(value);
100
+ return known ? { ...known } : { name: value };
101
+ }
102
+
103
+ function isCodexToolSearchTool(name) {
104
+ return String(name || '') === CODEX_TOOL_SEARCH_TOOL_NAME;
105
+ }
106
+
44
107
  function omitUndefined(value) {
45
108
  if (!isObject(value)) return value;
46
109
  const result = {};
@@ -50,6 +113,19 @@ function omitUndefined(value) {
50
113
  return result;
51
114
  }
52
115
 
116
+ function compactText(value) {
117
+ return String(value ?? '').replace(/\s+/g, ' ').trim();
118
+ }
119
+
120
+ function shortenText(value, maxChars) {
121
+ const text = compactText(value);
122
+ if (!text || text.length <= maxChars) return text;
123
+ const softLimit = Math.max(24, Math.floor(maxChars * 0.65));
124
+ const slice = text.slice(0, maxChars - 3);
125
+ const breakAt = Math.max(slice.lastIndexOf('. '), slice.lastIndexOf('; '), slice.lastIndexOf(', '), slice.lastIndexOf(' '));
126
+ return `${slice.slice(0, breakAt >= softLimit ? breakAt : slice.length).trimEnd()}...`;
127
+ }
128
+
53
129
  function textPart(text) {
54
130
  return { type: 'text', text: String(text ?? '') };
55
131
  }
@@ -137,8 +213,9 @@ function toolCallId(item) {
137
213
  }
138
214
 
139
215
  function toolName(item) {
140
- return sanitizeFunctionName(
141
- item.name ||
216
+ return encodeToolName(
217
+ toolNamespace(item),
218
+ item.name ||
142
219
  item.tool_name ||
143
220
  item.toolName ||
144
221
  item.server_label ||
@@ -148,6 +225,7 @@ function toolName(item) {
148
225
 
149
226
  function toolArguments(item) {
150
227
  if (item.type === 'function_call') return jsonString(item.arguments ?? {});
228
+ if (item.type === 'tool_search_call') return jsonString(item.arguments ?? {});
151
229
  if (item.arguments !== undefined) return jsonString(item.arguments);
152
230
  if (item.input !== undefined) return jsonString({ input: item.input });
153
231
  if (item.action !== undefined) return jsonString({ action: item.action });
@@ -170,35 +248,55 @@ function chatToolCallFromResponseItem(item) {
170
248
 
171
249
  function reasoningTextFromItem(item) {
172
250
  if (!isObject(item)) return '';
173
- const parts = [];
174
- if (typeof item.reasoning_content === 'string') parts.push(item.reasoning_content);
175
- if (typeof item.text === 'string') parts.push(item.text);
176
- if (typeof item.content === 'string') parts.push(item.content);
251
+ if (typeof item.reasoning_content === 'string') return normalizeReasoningContent(item.reasoning_content);
252
+ const rawParts = [];
253
+ if (typeof item.text === 'string') rawParts.push(item.text);
254
+ if (typeof item.content === 'string') rawParts.push(item.content);
177
255
  if (Array.isArray(item.content)) {
178
256
  for (const part of item.content) {
179
257
  if (typeof part === 'string') {
180
- parts.push(part);
258
+ rawParts.push(part);
181
259
  continue;
182
260
  }
183
261
  if (!isObject(part)) continue;
184
262
  if (part.type === 'reasoning_text' || part.type === 'text' || part.type === 'output_text') {
185
- parts.push(String(part.text ?? part.content ?? ''));
263
+ rawParts.push(String(part.text ?? part.content ?? ''));
186
264
  }
187
265
  }
188
266
  }
189
- if (Array.isArray(item.summary)) {
190
- for (const part of item.summary) {
191
- if (typeof part === 'string') {
192
- parts.push(part);
193
- continue;
194
- }
195
- if (!isObject(part)) continue;
196
- if (part.type === 'summary_text' || part.type === 'text') {
197
- parts.push(String(part.text ?? part.content ?? ''));
198
- }
199
- }
200
- }
201
- return parts.filter(Boolean).join('');
267
+ const rawText = rawParts.filter(Boolean).join('');
268
+ return normalizeReasoningContent(rawText);
269
+ }
270
+
271
+ function normalizeReasoningContent(text) {
272
+ return String(text ?? '').replace(/\r\n?/g, '\n').trimEnd();
273
+ }
274
+
275
+ function normalizeReasoningDisplayText(text) {
276
+ const value = normalizeReasoningContent(text);
277
+ if (!value) return '';
278
+ const plain = value
279
+ .split('\n')
280
+ .map((line) => line
281
+ .replace(/^[ \t]{0,3}(?:`{3,}|~{3,}).*$/, '')
282
+ .replace(/^[ \t]{0,3}#{1,6}[ \t]+/, '')
283
+ .replace(/^[ \t]{0,3}>[ \t]?/, '')
284
+ .replace(/^[ \t]*[-*+][ \t]+(?:\[[ xX]\][ \t]+)?/, '\u2022 ')
285
+ .replace(/^[ \t]*(\d+)\.[ \t]+/, '$1) ')
286
+ .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
287
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
288
+ .replace(/`([^`]*)`/g, '$1')
289
+ .replace(/(\*\*|__)([^*_]+)\1/g, '$2')
290
+ .replace(/(\*|_)([^*_]+)\1/g, '$2')
291
+ .replace(/[*_`~]/g, '')
292
+ .replace(/\\([\\`*_{}[\]()#+\-.!>])/g, '$1')
293
+ .trimEnd())
294
+ .join('\n');
295
+ return plain.replace(/^\n+/, '').trimEnd();
296
+ }
297
+
298
+ function reasoningDisplayText(text) {
299
+ return normalizeReasoningDisplayText(text);
202
300
  }
203
301
 
204
302
  function toolOutputContent(item) {
@@ -346,24 +444,28 @@ function extractMessagesFromResponsesInput(input) {
346
444
 
347
445
  function normalizeTool(tool) {
348
446
  if (!isObject(tool)) return tool;
447
+ if (tool.type === 'namespace') return null;
349
448
  if (typeof tool.type === 'string' && EMULATED_HOSTED_TOOL_TYPES.has(tool.type)) {
350
449
  return null;
351
450
  }
352
451
  if (tool.type === 'function' && isObject(tool.function)) {
452
+ const { namespace, ...fn } = tool.function;
353
453
  return {
354
- ...tool,
355
- function: {
356
- ...tool.function,
357
- name: sanitizeFunctionName(tool.function.name),
358
- parameters: normalizeJsonSchemaObject(tool.function.parameters),
359
- },
454
+ type: 'function',
455
+ function: omitUndefined({
456
+ ...fn,
457
+ name: encodeToolName(toolNamespace(tool), fn.name),
458
+ description: fn.description ?? tool.description,
459
+ parameters: normalizeJsonSchemaObject(fn.parameters ?? tool.parameters ?? tool.input_schema),
460
+ strict: fn.strict ?? tool.strict,
461
+ }),
360
462
  };
361
463
  }
362
464
  if (tool.type === 'function' || tool.type === 'custom' || tool.name || tool.parameters || tool.input_schema) {
363
465
  return {
364
466
  type: 'function',
365
467
  function: omitUndefined({
366
- name: sanitizeFunctionName(tool.name || tool.type),
468
+ name: encodeToolName(toolNamespace(tool), toolBaseName(tool, tool.type)),
367
469
  description: tool.description,
368
470
  parameters: normalizeJsonSchemaObject(tool.parameters || tool.input_schema),
369
471
  strict: tool.strict,
@@ -373,6 +475,75 @@ function normalizeTool(tool) {
373
475
  return null;
374
476
  }
375
477
 
478
+ function expandNamespaceTool(tool) {
479
+ if (!isObject(tool) || tool.type !== 'namespace') return [tool];
480
+ const namespace = tool.name || tool.namespace || tool.server_label;
481
+ const children = Array.isArray(tool.tools) ? tool.tools : [];
482
+ return children
483
+ .filter(isObject)
484
+ .map((child) => {
485
+ if (child.type === 'function' && isObject(child.function)) {
486
+ return {
487
+ ...child,
488
+ namespace: child.namespace || namespace,
489
+ function: {
490
+ ...child.function,
491
+ namespace: child.function.namespace || child.namespace || namespace,
492
+ },
493
+ };
494
+ }
495
+ return {
496
+ ...child,
497
+ namespace: child.namespace || namespace,
498
+ };
499
+ });
500
+ }
501
+
502
+ function expandTools(tools) {
503
+ if (!Array.isArray(tools)) return [];
504
+ return tools.flatMap(expandNamespaceTool);
505
+ }
506
+
507
+ function normalizeTools(tools) {
508
+ const seen = new Set();
509
+ const normalized = [];
510
+ for (const tool of expandTools(tools).map(normalizeTool).filter(Boolean)) {
511
+ const name = tool?.type === 'function' ? tool.function?.name : '';
512
+ if (name) {
513
+ if (seen.has(name)) continue;
514
+ seen.add(name);
515
+ }
516
+ normalized.push(tool);
517
+ }
518
+ return normalized.length ? normalized : undefined;
519
+ }
520
+
521
+ function collectToolSearchOutputTools(input) {
522
+ const discovered = [];
523
+ const scan = (value) => {
524
+ if (Array.isArray(value)) {
525
+ for (const item of value) scan(item);
526
+ return;
527
+ }
528
+ if (!isObject(value)) return;
529
+ if (value.type === 'tool_search_output' && Array.isArray(value.tools)) {
530
+ discovered.push(...value.tools.filter(isObject));
531
+ return;
532
+ }
533
+ if (Array.isArray(value.input)) scan(value.input);
534
+ if (Array.isArray(value.content)) scan(value.content);
535
+ };
536
+ scan(input);
537
+ return discovered;
538
+ }
539
+
540
+ function mergeToolsWithToolSearchOutput(requestTools, input) {
541
+ const discovered = collectToolSearchOutputTools(input);
542
+ if (!discovered.length) return requestTools;
543
+ const base = Array.isArray(requestTools) ? requestTools : [];
544
+ return base.concat(discovered);
545
+ }
546
+
376
547
  function normalizeJsonSchemaObject(schema) {
377
548
  if (!isObject(schema)) {
378
549
  return { type: 'object', properties: {}, additionalProperties: true };
@@ -405,7 +576,7 @@ function normalizeToolChoice(toolChoice) {
405
576
  if (toolChoice.type === 'function' && toolChoice.name) {
406
577
  return {
407
578
  type: 'function',
408
- function: { name: sanitizeFunctionName(toolChoice.name) },
579
+ function: { name: encodeToolName(toolNamespace(toolChoice), toolChoice.name) },
409
580
  };
410
581
  }
411
582
  if (toolChoice.type === 'function' && isObject(toolChoice.function)) {
@@ -413,7 +584,7 @@ function normalizeToolChoice(toolChoice) {
413
584
  ...toolChoice,
414
585
  function: {
415
586
  ...toolChoice.function,
416
- name: sanitizeFunctionName(toolChoice.function.name),
587
+ name: encodeToolName(toolNamespace(toolChoice), toolChoice.function.name),
417
588
  },
418
589
  };
419
590
  }
@@ -424,7 +595,8 @@ function normalizeToolChoice(toolChoice) {
424
595
  return {
425
596
  type: 'function',
426
597
  function: {
427
- name: sanitizeFunctionName(
598
+ name: encodeToolName(
599
+ toolNamespace(toolChoice),
428
600
  toolChoice.name ||
429
601
  toolChoice.tool_name ||
430
602
  toolChoice.toolName ||
@@ -437,6 +609,45 @@ function normalizeToolChoice(toolChoice) {
437
609
  return toolChoice;
438
610
  }
439
611
 
612
+ function toolChoiceEntryName(entry) {
613
+ if (!isObject(entry)) return '';
614
+ if (entry.type === 'function' && entry.name) return encodeToolName(toolNamespace(entry), entry.name);
615
+ if (entry.type === 'function' && isObject(entry.function)) {
616
+ return encodeToolName(toolNamespace(entry), entry.function.name);
617
+ }
618
+ return encodeToolName(
619
+ toolNamespace(entry),
620
+ entry.name ||
621
+ entry.tool_name ||
622
+ entry.toolName ||
623
+ entry.server_label ||
624
+ entry.type,
625
+ );
626
+ }
627
+
628
+ function allowedToolNames(toolChoice) {
629
+ if (!isObject(toolChoice) || toolChoice.type !== 'allowed_tools') return null;
630
+ const entries = Array.isArray(toolChoice.tools)
631
+ ? toolChoice.tools
632
+ : Array.isArray(toolChoice.allowed_tools)
633
+ ? toolChoice.allowed_tools
634
+ : Array.isArray(toolChoice.names)
635
+ ? toolChoice.names.map((name) => ({ type: 'function', name }))
636
+ : [];
637
+ const names = entries.map(toolChoiceEntryName).filter(Boolean);
638
+ return names.length ? new Set(names) : null;
639
+ }
640
+
641
+ function applyAllowedTools(tools, toolChoice) {
642
+ const allowed = allowedToolNames(toolChoice);
643
+ if (!allowed || !Array.isArray(tools)) return tools;
644
+ const filtered = tools.filter((tool) => {
645
+ const name = tool?.type === 'function' ? tool.function?.name : '';
646
+ return !name || allowed.has(name);
647
+ });
648
+ return filtered.length ? filtered : undefined;
649
+ }
650
+
440
651
  function unwrapJsonString(value) {
441
652
  if (typeof value !== 'string') return value;
442
653
  const trimmed = value.trim();
@@ -469,10 +680,63 @@ function coerceValueForSchema(value, schema) {
469
680
  return value;
470
681
  }
471
682
 
683
+ function toolSearchArgumentsFromText(argumentsText) {
684
+ const parsed = parseJsonObject(argumentsText);
685
+ const args = {};
686
+ if (parsed.limit !== undefined) {
687
+ const limit = Number(parsed.limit);
688
+ if (Number.isFinite(limit)) args.limit = limit;
689
+ }
690
+ if (parsed.query !== undefined) args.query = String(parsed.query);
691
+ return args;
692
+ }
693
+
694
+ function finalizeToolSearchCallItemArguments(item) {
695
+ if (!item || item.type !== 'tool_search_call') return;
696
+ if (typeof item.arguments === 'string') {
697
+ item.arguments = toolSearchArgumentsFromText(item.arguments);
698
+ } else if (!isObject(item.arguments)) {
699
+ item.arguments = {};
700
+ }
701
+ }
702
+
703
+ function convertItemToToolSearchCall(item) {
704
+ if (!item || item.type === 'tool_search_call') return item;
705
+ item.type = 'tool_search_call';
706
+ item.id = generateId('tsc');
707
+ item.execution = 'client';
708
+ delete item.name;
709
+ delete item.namespace;
710
+ return item;
711
+ }
712
+
713
+ function responseItemFromChatToolCall(toolCall, toolNames) {
714
+ const callId = toolCall.id || generateId('call');
715
+ if (isCodexToolSearchTool(toolCall.function?.name)) {
716
+ return {
717
+ type: 'tool_search_call',
718
+ id: generateId('tsc'),
719
+ call_id: callId,
720
+ status: 'completed',
721
+ execution: 'client',
722
+ arguments: toolSearchArgumentsFromText(toolCall.function?.arguments || ''),
723
+ };
724
+ }
725
+ const decoded = decodedToolName(toolCall.function?.name, toolNames);
726
+ return omitUndefined({
727
+ type: 'function_call',
728
+ id: callId,
729
+ call_id: callId,
730
+ name: decoded.name,
731
+ namespace: decoded.namespace,
732
+ arguments: toolCall.function?.arguments || '',
733
+ status: 'completed',
734
+ });
735
+ }
736
+
472
737
  function buildToolSchemas(tools) {
473
738
  const schemas = new Map();
474
- if (!Array.isArray(tools)) return schemas;
475
- for (const tool of tools) {
739
+ for (const tool of expandTools(tools)) {
476
740
  const normalizedTool = normalizeTool(tool);
477
741
  const fn = normalizedTool?.type === 'function' ? normalizedTool.function : null;
478
742
  if (!fn?.name) continue;
@@ -481,6 +745,160 @@ function buildToolSchemas(tools) {
481
745
  return schemas;
482
746
  }
483
747
 
748
+ function buildToolNames(tools) {
749
+ const names = new Map();
750
+ for (const tool of expandTools(tools)) {
751
+ const namespace = toolNamespace(tool);
752
+ if (!namespace) continue;
753
+ const normalizedTool = normalizeTool(tool);
754
+ const fn = normalizedTool?.type === 'function' ? normalizedTool.function : null;
755
+ if (!fn?.name) continue;
756
+ names.set(fn.name, {
757
+ namespace: sanitizeFunctionName(namespace, 'namespace'),
758
+ name: sanitizeFunctionName(toolBaseName(tool, tool.type)),
759
+ });
760
+ }
761
+ return names;
762
+ }
763
+
764
+ function simplifyJsonSchemaDescriptions(schema) {
765
+ if (Array.isArray(schema)) return schema.map(simplifyJsonSchemaDescriptions);
766
+ if (!isObject(schema)) return schema;
767
+ const next = {};
768
+ for (const [key, value] of Object.entries(schema)) {
769
+ if (key === 'description' && typeof value === 'string') {
770
+ const description = shortenText(value, DEEPSEEK_SCHEMA_DESCRIPTION_CHARS);
771
+ if (description) next.description = description;
772
+ continue;
773
+ }
774
+ next[key] = isObject(value) || Array.isArray(value) ? simplifyJsonSchemaDescriptions(value) : value;
775
+ }
776
+ return next;
777
+ }
778
+
779
+ function readableToolName(name) {
780
+ return compactText(String(name || '').replace(/[_-]+/g, ' '));
781
+ }
782
+
783
+ function requiredParametersText(parameters) {
784
+ const required = Array.isArray(parameters?.required) ? parameters.required.filter((name) => typeof name === 'string') : [];
785
+ return required.length ? ` Required: ${required.join(', ')}.` : '';
786
+ }
787
+
788
+ function defaultDeepSeekToolDescription(name) {
789
+ const readable = readableToolName(name);
790
+ return compactText(`Call ${readable || name || 'this function'} when needed.`);
791
+ }
792
+
793
+ function deepSeekGatewayModelAliases(config = {}) {
794
+ const aliases = isObject(config.modelAliases) && Object.keys(config.modelAliases).length
795
+ ? config.modelAliases
796
+ : DEFAULT_MODEL_ALIASES;
797
+ const spawnConfig = { ...config, upstreamProvider: 'deepseek', modelAliases: aliases };
798
+ const names = [];
799
+ for (const name of Object.keys(aliases)) {
800
+ if (!name || isDeprecatedModel(name)) continue;
801
+ const alias = resolveModelAlias(name, spawnConfig);
802
+ if (isDeprecatedModel(alias.upstreamModel)) continue;
803
+ names.push(name);
804
+ }
805
+ return [...new Set(names)];
806
+ }
807
+
808
+ function enumStringProperty(property, values, description) {
809
+ const source = isObject(property) ? property : {};
810
+ const next = { ...source };
811
+ delete next.const;
812
+ delete next.oneOf;
813
+ delete next.anyOf;
814
+ delete next.allOf;
815
+ delete next.examples;
816
+ if (next.default !== undefined && !values.includes(next.default)) delete next.default;
817
+ next.type = 'string';
818
+ next.enum = values;
819
+ next.description = description;
820
+ return next;
821
+ }
822
+
823
+ function deepSeekSpawnAgentParameters(parameters, config = {}) {
824
+ const next = normalizeJsonSchemaObject(parameters);
825
+ const properties = isObject(next.properties) ? { ...next.properties } : {};
826
+ const modelAliases = deepSeekGatewayModelAliases(config);
827
+ properties.model = enumStringProperty(
828
+ properties.model,
829
+ modelAliases,
830
+ 'Model for the sub-agent. Omit to inherit.',
831
+ );
832
+ properties.reasoning_effort = enumStringProperty(
833
+ properties.reasoning_effort,
834
+ CODEX_REASONING_EFFORTS,
835
+ 'Codex reasoning effort for the sub-agent. Omit to inherit.',
836
+ );
837
+ return {
838
+ ...next,
839
+ properties,
840
+ };
841
+ }
842
+
843
+ function deepSeekSpawnAgentDescription() {
844
+ const description = [
845
+ 'Spawn a Codex-native sub-agent.',
846
+ 'Omit model/reasoning_effort to inherit.',
847
+ 'Valid values are enforced by the schema.',
848
+ ].filter(Boolean).join(' ');
849
+ return shortenText(description, DEEPSEEK_TOOL_DESCRIPTION_CHARS);
850
+ }
851
+
852
+ function isCodexSpawnAgentTool(name) {
853
+ return name === CODEX_SPAWN_AGENT_TOOL_NAME || name === 'spawn_agent';
854
+ }
855
+
856
+ function simplifyToolForDeepSeek(tool, config = {}) {
857
+ if (tool?.type !== 'function' || !isObject(tool.function)) return tool;
858
+ const fn = tool.function;
859
+ let parameters = simplifyJsonSchemaDescriptions(fn.parameters);
860
+ if (isCodexSpawnAgentTool(fn.name)) {
861
+ parameters = deepSeekSpawnAgentParameters(parameters, config);
862
+ }
863
+ const requiredText = requiredParametersText(parameters);
864
+ const descriptionBudget = Math.max(32, DEEPSEEK_TOOL_DESCRIPTION_CHARS - requiredText.length);
865
+ const baseDescription = isCodexSpawnAgentTool(fn.name)
866
+ ? deepSeekSpawnAgentDescription()
867
+ : shortenText(fn.description, descriptionBudget) || defaultDeepSeekToolDescription(fn.name);
868
+ const description = shortenText(`${baseDescription}${requiredText}`, DEEPSEEK_TOOL_DESCRIPTION_CHARS);
869
+ return {
870
+ ...tool,
871
+ function: omitUndefined({
872
+ ...fn,
873
+ description,
874
+ parameters,
875
+ }),
876
+ };
877
+ }
878
+
879
+ function hasDeepSeekToolInstructions(messages) {
880
+ return messages.some((message) => message?.role === 'system' && toText(message.content).includes(DEEPSEEK_TOOL_INSTRUCTIONS_MARKER));
881
+ }
882
+
883
+ function addDeepSeekToolInstructions(messages, tools) {
884
+ if (!Array.isArray(tools) || !tools.length || hasDeepSeekToolInstructions(messages)) return messages;
885
+ if (!messages.length || messages[0]?.role !== 'system') {
886
+ return [{ role: 'system', content: DEEPSEEK_TOOL_INSTRUCTIONS }, ...messages];
887
+ }
888
+ return [
889
+ {
890
+ ...messages[0],
891
+ content: `${toText(messages[0].content)}\n\n${DEEPSEEK_TOOL_INSTRUCTIONS}`,
892
+ },
893
+ ...messages.slice(1),
894
+ ];
895
+ }
896
+
897
+ function adaptToolsForProvider(tools, provider, config = {}) {
898
+ if (provider !== 'deepseek' || !Array.isArray(tools)) return tools;
899
+ return tools.map((tool) => simplifyToolForDeepSeek(tool, config));
900
+ }
901
+
484
902
  function normalizeToolCallArguments(name, argumentsText, toolSchema) {
485
903
  if (!argumentsText) return '';
486
904
  if (!isObject(toolSchema) || !isObject(toolSchema.properties)) return argumentsText;
@@ -503,9 +921,66 @@ function normalizeToolCallArguments(name, argumentsText, toolSchema) {
503
921
  }
504
922
  }
505
923
 
506
- function normalizeFunctionCallItemArguments(item, toolSchemas) {
924
+ function normalizeReasoningEffortName(value) {
925
+ return value == null ? '' : String(value).toLowerCase().replaceAll('_', '-');
926
+ }
927
+
928
+ function normalizeCodexSpawnAgentArguments(encodedName, argumentsText, config = {}) {
929
+ if (!isCodexSpawnAgentTool(encodedName) || !argumentsText) return argumentsText;
930
+ let parsed;
931
+ try {
932
+ parsed = JSON.parse(argumentsText);
933
+ } catch {
934
+ return argumentsText;
935
+ }
936
+ if (!isObject(parsed)) return argumentsText;
937
+ const next = { ...parsed };
938
+ let changed = false;
939
+ const allowedModels = deepSeekGatewayModelAliases(config);
940
+
941
+ if (typeof next.model === 'string') {
942
+ const requestedModel = next.model;
943
+ if (allowedModels.length && !allowedModels.includes(requestedModel)) {
944
+ delete next.model;
945
+ changed = true;
946
+ }
947
+ }
948
+
949
+ if (typeof next.reasoning_effort === 'string') {
950
+ const requestedEffort = normalizeReasoningEffortName(next.reasoning_effort);
951
+ if (!CODEX_REASONING_EFFORTS.includes(requestedEffort)) {
952
+ delete next.reasoning_effort;
953
+ changed = true;
954
+ return JSON.stringify(next);
955
+ }
956
+ }
957
+
958
+ return changed ? JSON.stringify(next) : argumentsText;
959
+ }
960
+
961
+ function normalizeFunctionCallItemArguments(item, toolSchemas, config = {}) {
507
962
  if (!item || item.type !== 'function_call') return;
508
- item.arguments = normalizeToolCallArguments(item.name, item.arguments, toolSchemas?.get(item.name));
963
+ const encodedName = encodeToolName(item.namespace, item.name);
964
+ item.arguments = normalizeToolCallArguments(encodedName, item.arguments, toolSchemas?.get(encodedName));
965
+ item.arguments = normalizeCodexSpawnAgentArguments(encodedName, item.arguments, config);
966
+ }
967
+
968
+ function sanitizeToolCallsForChatCompletion(toolCalls) {
969
+ return toolCalls.map((toolCall) => {
970
+ if (!isObject(toolCall)) return toolCall;
971
+ const fn = isObject(toolCall.function) ? toolCall.function : {};
972
+ const { namespace, ...functionFields } = fn;
973
+ const name = fn.name || toolCall.name || 'tool_call';
974
+ const toolCallNamespace = toolCall.namespace || namespace || '';
975
+ return {
976
+ id: toolCall.id,
977
+ type: toolCall.type || 'function',
978
+ function: {
979
+ ...functionFields,
980
+ name: encodeToolName(toolCallNamespace, name),
981
+ },
982
+ };
983
+ });
509
984
  }
510
985
 
511
986
  function sanitizeMessageForChatCompletion(message, provider = 'generic') {
@@ -515,7 +990,9 @@ function sanitizeMessageForChatCompletion(message, provider = 'generic') {
515
990
  content: contentToChatContent(message.content),
516
991
  };
517
992
  if (message.name !== undefined) sanitized.name = message.name;
518
- if (Array.isArray(message.tool_calls)) sanitized.tool_calls = message.tool_calls;
993
+ if (Array.isArray(message.tool_calls)) {
994
+ sanitized.tool_calls = sanitizeToolCallsForChatCompletion(message.tool_calls);
995
+ }
519
996
  if (message.tool_call_id !== undefined) sanitized.tool_call_id = message.tool_call_id;
520
997
  if (provider === 'deepseek' && message.role === 'assistant' && typeof message.reasoning_content === 'string') {
521
998
  sanitized.reasoning_content = message.reasoning_content;
@@ -543,32 +1020,8 @@ function normalizeOutputTextPart(text, annotations = []) {
543
1020
  };
544
1021
  }
545
1022
 
546
- function normalizeReasoningSummaryText(text) {
547
- const value = String(text ?? '').replace(/\r\n?/g, '\n');
548
- if (!value) return '';
549
- const plain = value
550
- .split('\n')
551
- .map((line) => line
552
- .replace(/^[ \t]{0,3}(?:`{3,}|~{3,}).*$/, '')
553
- .replace(/^[ \t]{0,3}#{1,6}[ \t]+/, '')
554
- .replace(/^[ \t]{0,3}>[ \t]?/, '')
555
- .replace(/^[ \t]*[-*+][ \t]+(?:\[[ xX]\][ \t]+)?/, '\u2022 ')
556
- .replace(/^[ \t]*(\d+)\.[ \t]+/, '$1) ')
557
- .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
558
- .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
559
- .replace(/`([^`]*)`/g, '$1')
560
- .replace(/(\*\*|__)([^*_]+)\1/g, '$2')
561
- .replace(/(\*|_)([^*_]+)\1/g, '$2')
562
- .replace(/[*_`~]/g, '')
563
- .replace(/\\([\\`*_{}[\]()#+\-.!>])/g, '$1')
564
- .trimEnd())
565
- .join('\n');
566
- return plain.replace(/^\n+/, '').trimEnd();
567
- }
568
-
569
1023
  function reasoningSummaryText(text) {
570
- const summary = normalizeReasoningSummaryText(text);
571
- return summary ? `**Reasoning**\n\n${summary}` : '';
1024
+ return reasoningDisplayText(text);
572
1025
  }
573
1026
 
574
1027
  function normalizeSummaryTextPart(text) {
@@ -611,6 +1064,16 @@ function snapshotResponsePart(part) {
611
1064
 
612
1065
  function snapshotResponseItem(item) {
613
1066
  if (!isObject(item)) return item;
1067
+ if (item.type === 'tool_search_call') {
1068
+ return omitUndefined({
1069
+ type: item.type,
1070
+ id: item.id,
1071
+ call_id: item.call_id,
1072
+ status: item.status,
1073
+ execution: item.execution,
1074
+ arguments: isObject(item.arguments) ? { ...item.arguments } : toolSearchArgumentsFromText(item.arguments),
1075
+ });
1076
+ }
614
1077
  return {
615
1078
  ...item,
616
1079
  content: Array.isArray(item.content) ? item.content.map(snapshotResponsePart) : item.content,
@@ -676,6 +1139,7 @@ export function normalizeResponsesRequest(request) {
676
1139
  const model = request.model || request.model_id || request.upstream_model || '';
677
1140
  const instructions = normalizeInstructions(request.instructions);
678
1141
  const responseFormat = request.response_format || request.text?.format;
1142
+ const tools = mergeToolsWithToolSearchOutput(request.tools, request.input ?? request);
679
1143
  return {
680
1144
  model,
681
1145
  messages,
@@ -685,7 +1149,7 @@ export function normalizeResponsesRequest(request) {
685
1149
  max_tokens: request.max_output_tokens ?? request.max_tokens,
686
1150
  stop: request.stop,
687
1151
  stream: Boolean(request.stream),
688
- tools: request.tools,
1152
+ tools,
689
1153
  tool_choice: request.tool_choice,
690
1154
  parallel_tool_calls: request.parallel_tool_calls,
691
1155
  presence_penalty: request.presence_penalty,
@@ -710,7 +1174,7 @@ export function toChatCompletionsRequest(normalized, overrides = {}) {
710
1174
  messages.push({ role: 'system', content: normalized.instructions });
711
1175
  }
712
1176
  messages.push(...normalized.messages);
713
- const tools = Array.isArray(normalized.tools) ? normalized.tools.map(normalizeTool).filter(Boolean) : undefined;
1177
+ const tools = applyAllowedTools(normalizeTools(normalized.tools), normalized.tool_choice);
714
1178
  const request = {
715
1179
  model: overrides.model || normalized.model,
716
1180
  messages,
@@ -757,16 +1221,14 @@ export function assistantMessageFromResponseOutput(output) {
757
1221
  id: item.call_id || item.id,
758
1222
  type: 'function',
759
1223
  function: {
760
- name: item.name,
1224
+ name: encodeToolName(item.namespace, item.name),
761
1225
  arguments: item.arguments || '',
762
1226
  },
763
1227
  }));
764
1228
  if (toolCalls.length) assistant.tool_calls = toolCalls;
765
1229
  const reasoningContent = reasoningItems
766
- .map((item) => item.content || [])
767
- .flat()
768
- .filter((part) => part.type === 'reasoning_text')
769
- .map((part) => part.text)
1230
+ .map((item) => reasoningTextFromItem(item))
1231
+ .filter(Boolean)
770
1232
  .join('');
771
1233
  if (reasoningContent) assistant.reasoning_content = reasoningContent;
772
1234
  return assistant;
@@ -820,6 +1282,8 @@ export function toProviderChatCompletionsRequest(chatRequest, config = {}) {
820
1282
  if (provider === 'deepseek') {
821
1283
  delete request.reasoning_effort;
822
1284
  delete request.parallel_tool_calls;
1285
+ request.tools = adaptToolsForProvider(request.tools, provider, config);
1286
+ request.messages = addDeepSeekToolInstructions(request.messages, request.tools);
823
1287
  if (request.user !== undefined) {
824
1288
  request.user_id = request.user;
825
1289
  delete request.user;
@@ -949,12 +1413,13 @@ export function createResponseEnvelope({
949
1413
  });
950
1414
  }
951
1415
 
952
- export function convertChatCompletionToResponses({ completion, model, previousResponseId, normalized, responseId = generateId('resp') }) {
1416
+ export function convertChatCompletionToResponses({ completion, model, previousResponseId, normalized, responseId = generateId('resp'), config = {} }) {
953
1417
  const createdAt = completion.created || Date.now() / 1000;
954
1418
  const choice = Array.isArray(completion.choices) ? completion.choices[0] : null;
955
1419
  const message = choice?.message || {};
956
1420
  const content = chatContentToResponseOutputParts(message.content);
957
1421
  const toolSchemas = buildToolSchemas(normalized?.tools);
1422
+ const toolNames = buildToolNames(normalized?.tools);
958
1423
  const output = [];
959
1424
 
960
1425
  if (content.length || message.tool_calls?.length) {
@@ -970,27 +1435,20 @@ export function convertChatCompletionToResponses({ completion, model, previousRe
970
1435
 
971
1436
  if (Array.isArray(message.tool_calls)) {
972
1437
  for (const toolCall of message.tool_calls) {
973
- const callId = toolCall.id || generateId('call');
974
- const item = {
975
- type: 'function_call',
976
- id: callId,
977
- call_id: callId,
978
- name: toolCall.function?.name,
979
- arguments: toolCall.function?.arguments || '',
980
- status: 'completed',
981
- };
982
- normalizeFunctionCallItemArguments(item, toolSchemas);
1438
+ const item = responseItemFromChatToolCall(toolCall, toolNames);
1439
+ normalizeFunctionCallItemArguments(item, toolSchemas, config);
983
1440
  output.push(item);
984
1441
  }
985
1442
  }
986
1443
 
987
1444
  if (message.reasoning_content) {
988
- const reasoningText = String(message.reasoning_content);
1445
+ const reasoningText = normalizeReasoningContent(message.reasoning_content);
989
1446
  output.unshift({
990
1447
  type: 'reasoning',
991
1448
  id: generateId('rs'),
992
1449
  summary: [normalizeSummaryTextPart(reasoningSummaryText(reasoningText))],
993
- content: [normalizeReasoningTextPart(reasoningText)],
1450
+ content: [],
1451
+ reasoning_content: reasoningText,
994
1452
  encrypted_content: null,
995
1453
  status: 'completed',
996
1454
  });
@@ -1020,6 +1478,7 @@ export class ResponsesStreamMapper {
1020
1478
  bufferOutputUntilDone = false,
1021
1479
  emitReasoningSummary = true,
1022
1480
  emitReasoningText = false,
1481
+ config = {},
1023
1482
  } = {}) {
1024
1483
  this.responseId = responseId;
1025
1484
  this.model = model;
@@ -1048,6 +1507,8 @@ export class ResponsesStreamMapper {
1048
1507
  this.reasoningItemAdded = false;
1049
1508
  this.streamedReasoningSummaryText = '';
1050
1509
  this.toolSchemas = buildToolSchemas(normalized?.tools);
1510
+ this.toolNames = buildToolNames(normalized?.tools);
1511
+ this.config = config;
1051
1512
  }
1052
1513
 
1053
1514
  nextSequence() {
@@ -1114,7 +1575,7 @@ export class ResponsesStreamMapper {
1114
1575
  type: 'reasoning',
1115
1576
  status: 'in_progress',
1116
1577
  summary: [],
1117
- content: [normalizeReasoningTextPart('')],
1578
+ content: this.emitReasoningText ? [normalizeReasoningTextPart('')] : [],
1118
1579
  encrypted_content: null,
1119
1580
  };
1120
1581
  this.output.push(this.reasoningItem);
@@ -1133,6 +1594,7 @@ export class ResponsesStreamMapper {
1133
1594
 
1134
1595
  ensureReasoningContentPart(events) {
1135
1596
  if (!this.emitReasoningText || this.reasoningContentAdded || !this.reasoningItem) return;
1597
+ if (!this.reasoningItem.content[0]) this.reasoningItem.content[0] = normalizeReasoningTextPart('');
1136
1598
  this.reasoningContentAdded = true;
1137
1599
  events.push({
1138
1600
  type: 'response.content_part.added',
@@ -1144,6 +1606,19 @@ export class ResponsesStreamMapper {
1144
1606
  });
1145
1607
  }
1146
1608
 
1609
+ syncReasoningItemContent() {
1610
+ if (!this.reasoningItem) return;
1611
+ const reasoningText = normalizeReasoningContent(this.reasoningText);
1612
+ if (this.emitReasoningText) {
1613
+ if (!this.reasoningItem.content[0]) this.reasoningItem.content[0] = normalizeReasoningTextPart('');
1614
+ this.reasoningItem.content[0].text = reasoningText;
1615
+ } else {
1616
+ this.reasoningItem.content = [];
1617
+ }
1618
+ if (reasoningText) this.reasoningItem.reasoning_content = reasoningText;
1619
+ else delete this.reasoningItem.reasoning_content;
1620
+ }
1621
+
1147
1622
  appendReasoningSummaryDelta(events) {
1148
1623
  if (!this.emitReasoningSummary || !this.reasoningItem) return;
1149
1624
  if (!this.reasoningSummaryAdded) {
@@ -1234,7 +1709,7 @@ export class ResponsesStreamMapper {
1234
1709
  const events = [];
1235
1710
  const added = this.ensureReasoningItem();
1236
1711
  if (added) events.push(added);
1237
- this.reasoningItem.content[0].text = this.reasoningText;
1712
+ this.syncReasoningItemContent();
1238
1713
  this.appendReasoningSummaryDelta(events);
1239
1714
  return events;
1240
1715
  }
@@ -1245,12 +1720,12 @@ export class ResponsesStreamMapper {
1245
1720
  type: 'reasoning',
1246
1721
  status: 'in_progress',
1247
1722
  summary: [],
1248
- content: [normalizeReasoningTextPart('')],
1723
+ content: this.emitReasoningText ? [normalizeReasoningTextPart('')] : [],
1249
1724
  encrypted_content: null,
1250
1725
  };
1251
1726
  this.output.push(this.reasoningItem);
1252
1727
  }
1253
- this.reasoningItem.content[0].text = this.reasoningText;
1728
+ this.syncReasoningItemContent();
1254
1729
  return [];
1255
1730
  }
1256
1731
 
@@ -1258,7 +1733,7 @@ export class ResponsesStreamMapper {
1258
1733
  const added = this.ensureReasoningItem();
1259
1734
  if (added) events.push(added);
1260
1735
  this.ensureReasoningContentPart(events);
1261
- this.reasoningItem.content[0].text = this.reasoningText;
1736
+ this.syncReasoningItemContent();
1262
1737
  this.appendReasoningSummaryDelta(events);
1263
1738
  if (this.emitReasoningText) {
1264
1739
  events.push({
@@ -1279,18 +1754,31 @@ export class ResponsesStreamMapper {
1279
1754
  let item = this.toolItems.get(index);
1280
1755
  if (!item) {
1281
1756
  const callId = toolCall.id || generateId('call');
1282
- item = {
1283
- id: callId,
1284
- type: 'function_call',
1285
- status: 'in_progress',
1286
- call_id: callId,
1287
- name: toolCall.function?.name || '',
1288
- arguments: '',
1289
- };
1757
+ item = isCodexToolSearchTool(toolCall.function?.name)
1758
+ ? {
1759
+ id: generateId('tsc'),
1760
+ type: 'tool_search_call',
1761
+ status: 'in_progress',
1762
+ call_id: callId,
1763
+ execution: 'client',
1764
+ arguments: '',
1765
+ }
1766
+ : {
1767
+ id: callId,
1768
+ type: 'function_call',
1769
+ status: 'in_progress',
1770
+ call_id: callId,
1771
+ ...decodedToolName(toolCall.function?.name, this.toolNames),
1772
+ arguments: '',
1773
+ };
1290
1774
  this.toolItems.set(index, item);
1291
1775
  this.output.push(item);
1292
1776
  }
1293
- if (toolCall.function?.name) item.name = toolCall.function.name;
1777
+ if (isCodexToolSearchTool(toolCall.function?.name)) {
1778
+ item = convertItemToToolSearchCall(item);
1779
+ } else if (toolCall.function?.name && item.type !== 'tool_search_call') {
1780
+ Object.assign(item, decodedToolName(toolCall.function.name, this.toolNames));
1781
+ }
1294
1782
  item.arguments += toolCall.function?.arguments || '';
1295
1783
  return [];
1296
1784
  }
@@ -1303,14 +1791,23 @@ export class ResponsesStreamMapper {
1303
1791
  let item = this.toolItems.get(index);
1304
1792
  if (!item) {
1305
1793
  const callId = toolCall.id || generateId('call');
1306
- item = {
1307
- id: callId,
1308
- type: 'function_call',
1309
- status: 'in_progress',
1310
- call_id: callId,
1311
- name: toolCall.function?.name || '',
1312
- arguments: '',
1313
- };
1794
+ item = isCodexToolSearchTool(toolCall.function?.name)
1795
+ ? {
1796
+ id: generateId('tsc'),
1797
+ type: 'tool_search_call',
1798
+ status: 'in_progress',
1799
+ call_id: callId,
1800
+ execution: 'client',
1801
+ arguments: '',
1802
+ }
1803
+ : {
1804
+ id: callId,
1805
+ type: 'function_call',
1806
+ status: 'in_progress',
1807
+ call_id: callId,
1808
+ ...decodedToolName(toolCall.function?.name, this.toolNames),
1809
+ arguments: '',
1810
+ };
1314
1811
  this.toolItems.set(index, item);
1315
1812
  this.output.push(item);
1316
1813
  events.push({
@@ -1320,10 +1817,14 @@ export class ResponsesStreamMapper {
1320
1817
  item: snapshotResponseItem(item),
1321
1818
  });
1322
1819
  }
1323
- if (toolCall.function?.name) item.name = toolCall.function.name;
1820
+ if (isCodexToolSearchTool(toolCall.function?.name)) {
1821
+ item = convertItemToToolSearchCall(item);
1822
+ } else if (toolCall.function?.name && item.type !== 'tool_search_call') {
1823
+ Object.assign(item, decodedToolName(toolCall.function.name, this.toolNames));
1824
+ }
1324
1825
  const delta = toolCall.function?.arguments || '';
1325
1826
  item.arguments += delta;
1326
- if (delta) {
1827
+ if (delta && item.type === 'function_call') {
1327
1828
  events.push({
1328
1829
  type: 'response.function_call_arguments.delta',
1329
1830
  sequence_number: this.nextSequence(),
@@ -1347,7 +1848,7 @@ export class ResponsesStreamMapper {
1347
1848
 
1348
1849
  if (this.reasoningItem) {
1349
1850
  this.reasoningItem.status = status;
1350
- this.reasoningItem.content[0].text = this.reasoningText;
1851
+ this.syncReasoningItemContent();
1351
1852
  }
1352
1853
  if (this.messageItem) {
1353
1854
  this.messageItem.status = status;
@@ -1421,19 +1922,23 @@ export class ResponsesStreamMapper {
1421
1922
 
1422
1923
  for (const item of this.toolItems.values()) {
1423
1924
  item.status = status;
1424
- item.arguments = normalizeToolCallArguments(item.name, item.arguments, this.toolSchemas.get(item.name));
1925
+ if (item.type === 'tool_search_call') finalizeToolSearchCallItemArguments(item);
1926
+ else normalizeFunctionCallItemArguments(item, this.toolSchemas, this.config);
1425
1927
  const outputIndex = this.output.indexOf(item);
1426
1928
  events.push({
1427
1929
  type: 'response.output_item.added',
1428
1930
  sequence_number: this.nextSequence(),
1429
1931
  output_index: outputIndex,
1430
- item: snapshotResponseItem({
1932
+ item: snapshotResponseItem(item.type === 'tool_search_call' ? {
1933
+ ...item,
1934
+ status: 'in_progress',
1935
+ } : {
1431
1936
  ...item,
1432
1937
  status: 'in_progress',
1433
1938
  arguments: '',
1434
1939
  }),
1435
1940
  });
1436
- if (item.arguments) {
1941
+ if (item.type === 'function_call' && item.arguments) {
1437
1942
  events.push({
1438
1943
  type: 'response.function_call_arguments.delta',
1439
1944
  sequence_number: this.nextSequence(),
@@ -1442,13 +1947,15 @@ export class ResponsesStreamMapper {
1442
1947
  delta: item.arguments,
1443
1948
  });
1444
1949
  }
1445
- events.push({
1446
- type: 'response.function_call_arguments.done',
1447
- sequence_number: this.nextSequence(),
1448
- output_index: outputIndex,
1449
- item_id: item.id,
1450
- arguments: item.arguments,
1451
- });
1950
+ if (item.type === 'function_call') {
1951
+ events.push({
1952
+ type: 'response.function_call_arguments.done',
1953
+ sequence_number: this.nextSequence(),
1954
+ output_index: outputIndex,
1955
+ item_id: item.id,
1956
+ arguments: item.arguments,
1957
+ });
1958
+ }
1452
1959
  events.push({
1453
1960
  type: 'response.output_item.done',
1454
1961
  sequence_number: this.nextSequence(),
@@ -1487,7 +1994,7 @@ export class ResponsesStreamMapper {
1487
1994
  this.reasoningSummaryAdded = true;
1488
1995
  this.reasoningItem.summary.push(normalizeSummaryTextPart(reasoningSummaryText(this.reasoningText)));
1489
1996
  }
1490
- this.reasoningItem.content[0].text = this.reasoningText;
1997
+ this.syncReasoningItemContent();
1491
1998
  if (this.reasoningItem.summary[0]) {
1492
1999
  this.reasoningItem.summary[0].text = reasoningSummaryText(this.reasoningText);
1493
2000
  }
@@ -1533,14 +2040,17 @@ export class ResponsesStreamMapper {
1533
2040
  events.push(...this.closeReasoningItem(finishReason === 'length' ? 'incomplete' : 'completed'));
1534
2041
  for (const item of this.toolItems.values()) {
1535
2042
  item.status = finishReason === 'length' ? 'incomplete' : 'completed';
1536
- normalizeFunctionCallItemArguments(item, this.toolSchemas);
1537
- events.push({
1538
- type: 'response.function_call_arguments.done',
1539
- sequence_number: this.nextSequence(),
1540
- output_index: this.output.indexOf(item),
1541
- item_id: item.id,
1542
- arguments: item.arguments,
1543
- });
2043
+ if (item.type === 'tool_search_call') finalizeToolSearchCallItemArguments(item);
2044
+ else normalizeFunctionCallItemArguments(item, this.toolSchemas, this.config);
2045
+ if (item.type === 'function_call') {
2046
+ events.push({
2047
+ type: 'response.function_call_arguments.done',
2048
+ sequence_number: this.nextSequence(),
2049
+ output_index: this.output.indexOf(item),
2050
+ item_id: item.id,
2051
+ arguments: item.arguments,
2052
+ });
2053
+ }
1544
2054
  events.push({
1545
2055
  type: 'response.output_item.done',
1546
2056
  sequence_number: this.nextSequence(),
@@ -1587,7 +2097,7 @@ export class ResponsesStreamMapper {
1587
2097
  ...this.reasoningItem,
1588
2098
  status: 'in_progress',
1589
2099
  summary: [],
1590
- content: [normalizeReasoningTextPart('')],
2100
+ content: this.emitReasoningText ? [normalizeReasoningTextPart('')] : [],
1591
2101
  }),
1592
2102
  });
1593
2103
  }
@@ -1640,7 +2150,7 @@ export class ResponsesStreamMapper {
1640
2150
  this.reasoningItem.summary[0].text = summaryText;
1641
2151
  }
1642
2152
  this.reasoningItem.status = status;
1643
- this.reasoningItem.content[0].text = this.reasoningText;
2153
+ this.syncReasoningItemContent();
1644
2154
  return events;
1645
2155
  }
1646
2156
 
@@ -1648,7 +2158,7 @@ export class ResponsesStreamMapper {
1648
2158
  if (!this.reasoningItem || this.reasoningItemDone) return [];
1649
2159
  this.reasoningItemDone = true;
1650
2160
  this.reasoningItem.status = status;
1651
- this.reasoningItem.content[0].text = this.reasoningText;
2161
+ this.syncReasoningItemContent();
1652
2162
  const summaryText = reasoningSummaryText(this.reasoningText);
1653
2163
  if (this.emitReasoningSummary && this.reasoningText && !this.reasoningSummaryAdded) {
1654
2164
  this.reasoningSummaryAdded = true;