@jsonstudio/llms 0.6.1892 → 0.6.2172

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 (159) hide show
  1. package/dist/conversion/compat/actions/deepseek-web-request.js +16 -2
  2. package/dist/conversion/compat/actions/deepseek-web-response.d.ts +7 -1
  3. package/dist/conversion/compat/actions/deepseek-web-response.js +302 -40
  4. package/dist/conversion/compat/actions/harvest-tool-calls-from-text.d.ts +5 -0
  5. package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +7 -4
  6. package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +1 -0
  7. package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +12 -0
  8. package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +1 -1
  9. package/dist/conversion/compat/actions/tool-text-request-guidance.d.ts +9 -0
  10. package/dist/conversion/compat/actions/tool-text-request-guidance.js +177 -0
  11. package/dist/conversion/compat/antigravity-session-signature.d.ts +6 -0
  12. package/dist/conversion/compat/antigravity-session-signature.js +15 -0
  13. package/dist/conversion/compat/profiles/chat-deepseek-web.json +52 -1
  14. package/dist/conversion/compat/profiles/chat-glm.json +22 -0
  15. package/dist/conversion/compat/profiles/chat-iflow.json +4 -0
  16. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +13 -27
  17. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +10 -1
  18. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +13 -4
  19. package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -53
  20. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +8 -0
  21. package/dist/conversion/hub/pipeline/hub-pipeline.js +8 -4
  22. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +191 -9
  23. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +118 -15
  24. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +65 -2
  25. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage3_servertool_orchestration/index.d.ts +34 -0
  26. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage3_servertool_orchestration/index.js +75 -0
  27. package/dist/conversion/hub/process/chat-process.js +85 -18
  28. package/dist/conversion/hub/response/provider-response.js +21 -50
  29. package/dist/conversion/hub/response/response-runtime.js +71 -10
  30. package/dist/conversion/responses/responses-openai-bridge/response-payload.d.ts +3 -0
  31. package/dist/conversion/responses/responses-openai-bridge/response-payload.js +576 -0
  32. package/dist/conversion/responses/responses-openai-bridge/types.d.ts +42 -0
  33. package/dist/conversion/responses/responses-openai-bridge/types.js +1 -0
  34. package/dist/conversion/responses/responses-openai-bridge.d.ts +3 -44
  35. package/dist/conversion/responses/responses-openai-bridge.js +193 -504
  36. package/dist/conversion/shared/anthropic-message-utils.js +82 -2
  37. package/dist/conversion/shared/bridge-message-utils.js +92 -39
  38. package/dist/conversion/shared/snapshot-hooks.js +8 -13
  39. package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.d.ts +2 -0
  40. package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.js +129 -0
  41. package/dist/conversion/shared/text-markup-normalizer/extractors-json.d.ts +4 -0
  42. package/dist/conversion/shared/text-markup-normalizer/extractors-json.js +637 -0
  43. package/dist/conversion/shared/text-markup-normalizer/extractors-shared.d.ts +21 -0
  44. package/dist/conversion/shared/text-markup-normalizer/extractors-shared.js +177 -0
  45. package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.d.ts +5 -0
  46. package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.js +385 -0
  47. package/dist/conversion/shared/text-markup-normalizer/extractors-xml.d.ts +10 -0
  48. package/dist/conversion/shared/text-markup-normalizer/extractors-xml.js +602 -0
  49. package/dist/conversion/shared/text-markup-normalizer/extractors.d.ts +5 -0
  50. package/dist/conversion/shared/text-markup-normalizer/extractors.js +4 -0
  51. package/dist/conversion/shared/text-markup-normalizer/normalize.d.ts +2 -0
  52. package/dist/conversion/shared/text-markup-normalizer/normalize.js +76 -0
  53. package/dist/conversion/shared/text-markup-normalizer.d.ts +3 -25
  54. package/dist/conversion/shared/text-markup-normalizer.js +2 -1386
  55. package/dist/conversion/shared/tool-governor.js +136 -10
  56. package/dist/filters/utils/snapshot-writer.js +3 -3
  57. package/dist/router/virtual-router/bootstrap/auth-utils.d.ts +6 -0
  58. package/dist/router/virtual-router/bootstrap/auth-utils.js +288 -0
  59. package/dist/router/virtual-router/bootstrap/claude-code-helpers.d.ts +11 -0
  60. package/dist/router/virtual-router/bootstrap/claude-code-helpers.js +18 -0
  61. package/dist/router/virtual-router/bootstrap/config-defaults.d.ts +5 -0
  62. package/dist/router/virtual-router/bootstrap/config-defaults.js +13 -0
  63. package/dist/router/virtual-router/bootstrap/config-normalizers.d.ts +4 -0
  64. package/dist/router/virtual-router/bootstrap/config-normalizers.js +106 -0
  65. package/dist/router/virtual-router/bootstrap/profile-builder.d.ts +7 -0
  66. package/dist/router/virtual-router/bootstrap/profile-builder.js +68 -0
  67. package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +40 -0
  68. package/dist/router/virtual-router/bootstrap/provider-normalization.js +212 -0
  69. package/dist/router/virtual-router/bootstrap/responses-helpers.d.ts +15 -0
  70. package/dist/router/virtual-router/bootstrap/responses-helpers.js +65 -0
  71. package/dist/router/virtual-router/bootstrap/routing-config.d.ts +23 -0
  72. package/dist/router/virtual-router/bootstrap/routing-config.js +293 -0
  73. package/dist/router/virtual-router/bootstrap/streaming-helpers.d.ts +12 -0
  74. package/dist/router/virtual-router/bootstrap/streaming-helpers.js +128 -0
  75. package/dist/router/virtual-router/bootstrap/utils.d.ts +5 -0
  76. package/dist/router/virtual-router/bootstrap/utils.js +41 -0
  77. package/dist/router/virtual-router/bootstrap/web-search-config.d.ts +4 -0
  78. package/dist/router/virtual-router/bootstrap/web-search-config.js +131 -0
  79. package/dist/router/virtual-router/bootstrap.d.ts +0 -4
  80. package/dist/router/virtual-router/bootstrap.js +31 -1275
  81. package/dist/router/virtual-router/classifier.js +32 -14
  82. package/dist/router/virtual-router/engine/antigravity/alias-lease.js +2 -2
  83. package/dist/router/virtual-router/engine/cooldown-manager.d.ts +34 -0
  84. package/dist/router/virtual-router/engine/cooldown-manager.js +118 -0
  85. package/dist/router/virtual-router/engine/route-analytics.d.ts +28 -0
  86. package/dist/router/virtual-router/engine/route-analytics.js +44 -0
  87. package/dist/router/virtual-router/engine/routing-pools/index.js +165 -4
  88. package/dist/router/virtual-router/engine/sticky-session-manager.d.ts +29 -0
  89. package/dist/router/virtual-router/engine/sticky-session-manager.js +55 -0
  90. package/dist/router/virtual-router/engine-logging.d.ts +42 -1
  91. package/dist/router/virtual-router/engine-logging.js +82 -15
  92. package/dist/router/virtual-router/engine-selection/multimodal-capability.d.ts +3 -0
  93. package/dist/router/virtual-router/engine-selection/multimodal-capability.js +26 -0
  94. package/dist/router/virtual-router/engine-selection/route-utils.js +6 -2
  95. package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +1 -0
  96. package/dist/router/virtual-router/engine-selection/tier-selection.js +31 -1
  97. package/dist/router/virtual-router/engine.d.ts +21 -7
  98. package/dist/router/virtual-router/engine.js +198 -194
  99. package/dist/router/virtual-router/features.js +12 -4
  100. package/dist/router/virtual-router/message-utils.d.ts +8 -0
  101. package/dist/router/virtual-router/message-utils.js +170 -45
  102. package/dist/router/virtual-router/pre-command-file-resolver.js +40 -2
  103. package/dist/router/virtual-router/routing-instructions.d.ts +8 -0
  104. package/dist/router/virtual-router/routing-instructions.js +18 -2
  105. package/dist/router/virtual-router/routing-stop-message-actions.js +34 -10
  106. package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +2 -0
  107. package/dist/router/virtual-router/routing-stop-message-state-codec.js +50 -1
  108. package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
  109. package/dist/router/virtual-router/stop-message-state-sync.js +3 -0
  110. package/dist/router/virtual-router/token-counter.js +51 -10
  111. package/dist/router/virtual-router/tool-signals.js +4 -0
  112. package/dist/router/virtual-router/types.d.ts +15 -0
  113. package/dist/servertool/clock/session-scope.d.ts +3 -0
  114. package/dist/servertool/clock/session-scope.js +52 -0
  115. package/dist/servertool/clock/state.js +9 -0
  116. package/dist/servertool/clock/tasks.js +12 -1
  117. package/dist/servertool/clock/types.d.ts +3 -0
  118. package/dist/servertool/engine.js +177 -31
  119. package/dist/servertool/handlers/clock-auto.js +2 -8
  120. package/dist/servertool/handlers/clock.js +6 -9
  121. package/dist/servertool/handlers/recursive-detection-guard.js +53 -14
  122. package/dist/servertool/handlers/stop-message-auto/blocked-report.d.ts +16 -0
  123. package/dist/servertool/handlers/stop-message-auto/blocked-report.js +349 -0
  124. package/dist/servertool/handlers/stop-message-auto/iflow-followup.d.ts +23 -0
  125. package/dist/servertool/handlers/stop-message-auto/iflow-followup.js +503 -0
  126. package/dist/servertool/handlers/stop-message-auto/routing-state.d.ts +38 -0
  127. package/dist/servertool/handlers/stop-message-auto/routing-state.js +149 -0
  128. package/dist/servertool/handlers/stop-message-auto/runtime-utils.d.ts +67 -0
  129. package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +387 -0
  130. package/dist/servertool/handlers/stop-message-auto.d.ts +1 -1
  131. package/dist/servertool/handlers/stop-message-auto.js +80 -556
  132. package/dist/servertool/handlers/stop-message-stage-policy/bd-runtime.d.ts +18 -0
  133. package/dist/servertool/handlers/stop-message-stage-policy/bd-runtime.js +398 -0
  134. package/dist/servertool/handlers/stop-message-stage-policy/decision.d.ts +9 -0
  135. package/dist/servertool/handlers/stop-message-stage-policy/decision.js +127 -0
  136. package/dist/servertool/handlers/stop-message-stage-policy/observation.d.ts +2 -0
  137. package/dist/servertool/handlers/stop-message-stage-policy/observation.js +179 -0
  138. package/dist/servertool/handlers/stop-message-stage-policy/templates.d.ts +4 -0
  139. package/dist/servertool/handlers/stop-message-stage-policy/templates.js +96 -0
  140. package/dist/servertool/handlers/stop-message-stage-policy/text-utils.d.ts +9 -0
  141. package/dist/servertool/handlers/stop-message-stage-policy/text-utils.js +89 -0
  142. package/dist/servertool/handlers/stop-message-stage-policy/types.d.ts +59 -0
  143. package/dist/servertool/handlers/stop-message-stage-policy/types.js +1 -0
  144. package/dist/servertool/handlers/stop-message-stage-policy.d.ts +3 -43
  145. package/dist/servertool/handlers/stop-message-stage-policy.js +2 -684
  146. package/dist/servertool/handlers/web-search.js +117 -0
  147. package/dist/servertool/server-side-tools.d.ts +0 -1
  148. package/dist/servertool/server-side-tools.js +4 -3
  149. package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
  150. package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +1 -0
  151. package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +110 -37
  152. package/dist/telemetry/stats-center.d.ts +9 -0
  153. package/dist/telemetry/stats-center.js +29 -1
  154. package/dist/tools/apply-patch/structured/coercion.js +3 -11
  155. package/dist/tools/exec-command/validator.d.ts +1 -0
  156. package/dist/tools/exec-command/validator.js +132 -0
  157. package/dist/tools/tool-registry.d.ts +1 -0
  158. package/dist/tools/tool-registry.js +1 -1
  159. package/package.json +1 -1
@@ -3,6 +3,7 @@ const DEFAULT_OPTIONS = {
3
3
  textToolFallback: true
4
4
  };
5
5
  const SEARCH_ROUTE_PREFIXES = ['web_search', 'search'];
6
+ const TOOL_TEXT_GUIDANCE_MARKER = 'Tool-call output contract (STRICT)';
6
7
  const readString = (value) => {
7
8
  if (typeof value !== 'string') {
8
9
  return undefined;
@@ -164,7 +165,7 @@ function buildToolFallbackInstruction(toolsRaw, requireToolCall) {
164
165
  '',
165
166
  schemas.join('\n\n'),
166
167
  '',
167
- 'Tool-call output contract (STRICT):',
168
+ `${TOOL_TEXT_GUIDANCE_MARKER}:`,
168
169
  '1) If you call a tool, your ENTIRE assistant output must be a single JSON object.',
169
170
  '2) Use this exact top-level shape (and key names):',
170
171
  '{"tool_calls":[{"name":"tool_name","input":{"arg":"value"}}]}',
@@ -180,6 +181,17 @@ function buildToolFallbackInstruction(toolsRaw, requireToolCall) {
180
181
  '{"tool_calls":[{"name":"exec_command","input":{"cmd":"pnpm -v","workdir":"/workspace"}}]}'
181
182
  ].join('\n');
182
183
  }
184
+ function hasToolGuidanceMarker(messages) {
185
+ for (const item of messages) {
186
+ if (typeof item?.text !== 'string') {
187
+ continue;
188
+ }
189
+ if (item.text.includes(TOOL_TEXT_GUIDANCE_MARKER)) {
190
+ return true;
191
+ }
192
+ }
193
+ return false;
194
+ }
183
195
  function isToolChoiceRequired(root) {
184
196
  const toolChoice = root.tool_choice;
185
197
  if (typeof toolChoice === 'string') {
@@ -224,7 +236,9 @@ function toPromptMessages(root, options) {
224
236
  messages.push({ role, text });
225
237
  }
226
238
  if (options.textToolFallback) {
227
- const instruction = buildToolFallbackInstruction(root.tools, isToolChoiceRequired(root));
239
+ const instruction = hasToolGuidanceMarker(messages)
240
+ ? ''
241
+ : buildToolFallbackInstruction(root.tools, isToolChoiceRequired(root));
228
242
  if (instruction) {
229
243
  const first = messages[0];
230
244
  if (first && first.role === 'system') {
@@ -1,3 +1,9 @@
1
1
  import type { AdapterContext } from '../../hub/types/chat-envelope.js';
2
2
  import type { JsonObject } from '../../hub/types/json.js';
3
- export declare function applyDeepSeekWebResponseTransform(payload: JsonObject, adapterContext?: AdapterContext): JsonObject;
3
+ import { type TextMarkupNormalizeOptions } from '../../shared/text-markup-normalizer.js';
4
+ export interface DeepSeekWebResponseConfig {
5
+ strictToolRequired?: boolean;
6
+ textToolFallback?: boolean;
7
+ textNormalizer?: TextMarkupNormalizeOptions;
8
+ }
9
+ export declare function applyDeepSeekWebResponseTransform(payload: JsonObject, adapterContext?: AdapterContext, config?: DeepSeekWebResponseConfig): JsonObject;
@@ -3,8 +3,23 @@ import { validateToolCall } from '../../../tools/tool-registry.js';
3
3
  import { encoding_for_model, get_encoding } from 'tiktoken';
4
4
  const DEFAULT_OPTIONS = {
5
5
  strictToolRequired: true,
6
- textToolFallback: true
6
+ textToolFallback: true,
7
+ textNormalizer: undefined
7
8
  };
9
+ const SHELL_LIKE_TOOL_NAMES = new Set(['exec_command', 'shell_command', 'shell', 'bash', 'terminal']);
10
+ const SHELL_TOOL_NAME_ALIASES = new Map([
11
+ ['shell_command', 'exec_command'],
12
+ ['shell', 'exec_command'],
13
+ ['bash', 'exec_command'],
14
+ ['terminal', 'exec_command']
15
+ ]);
16
+ const SHELL_ALLOWED_TOOL_CANDIDATES = [
17
+ 'exec_command',
18
+ 'shell_command',
19
+ 'bash',
20
+ 'shell',
21
+ 'terminal'
22
+ ];
8
23
  const DEFAULT_TOKEN_ENCODING = 'cl100k_base';
9
24
  const tokenEncoderCache = new Map();
10
25
  let defaultTokenEncoder = null;
@@ -47,6 +62,21 @@ function readBoolean(input, fallback) {
47
62
  }
48
63
  return fallback;
49
64
  }
65
+ function readOptionalBoolean(input) {
66
+ if (typeof input === 'boolean') {
67
+ return input;
68
+ }
69
+ if (typeof input === 'string') {
70
+ const normalized = input.trim().toLowerCase();
71
+ if (['true', '1', 'yes', 'on'].includes(normalized)) {
72
+ return true;
73
+ }
74
+ if (['false', '0', 'no', 'off'].includes(normalized)) {
75
+ return false;
76
+ }
77
+ }
78
+ return undefined;
79
+ }
50
80
  function getTokenEncoder(modelHint) {
51
81
  const normalized = typeof modelHint === 'string' ? modelHint.trim() : '';
52
82
  if (normalized) {
@@ -80,7 +110,7 @@ function countTextTokens(text, modelHint) {
80
110
  return 0;
81
111
  }
82
112
  }
83
- function resolveOptions(adapterContext) {
113
+ function resolveOptions(adapterContext, config) {
84
114
  const context = (adapterContext ?? {});
85
115
  const deepseekNode = isRecord(context.deepseek)
86
116
  ? context.deepseek
@@ -88,11 +118,23 @@ function resolveOptions(adapterContext) {
88
118
  ? context.__rt.deepseek
89
119
  : undefined;
90
120
  if (!deepseekNode) {
91
- return { ...DEFAULT_OPTIONS };
121
+ return {
122
+ ...DEFAULT_OPTIONS,
123
+ strictToolRequired: readOptionalBoolean(config?.strictToolRequired) ?? DEFAULT_OPTIONS.strictToolRequired,
124
+ textToolFallback: readOptionalBoolean(config?.textToolFallback) ?? DEFAULT_OPTIONS.textToolFallback,
125
+ textNormalizer: config?.textNormalizer
126
+ };
92
127
  }
128
+ const strictOverride = readOptionalBoolean(config?.strictToolRequired);
129
+ const fallbackOverride = readOptionalBoolean(config?.textToolFallback);
93
130
  return {
94
- strictToolRequired: readBoolean(deepseekNode.strictToolRequired, DEFAULT_OPTIONS.strictToolRequired),
95
- textToolFallback: readBoolean(deepseekNode.textToolFallback, DEFAULT_OPTIONS.textToolFallback)
131
+ strictToolRequired: strictOverride ??
132
+ readBoolean(deepseekNode.strictToolRequired, DEFAULT_OPTIONS.strictToolRequired),
133
+ textToolFallback: fallbackOverride ??
134
+ readBoolean(deepseekNode.textToolFallback, DEFAULT_OPTIONS.textToolFallback),
135
+ textNormalizer: config?.textNormalizer && typeof config.textNormalizer === 'object'
136
+ ? config.textNormalizer
137
+ : undefined
96
138
  };
97
139
  }
98
140
  function resolveCapturedRequest(adapterContext) {
@@ -226,6 +268,19 @@ function resolveRequestedToolNames(captured) {
226
268
  names.add(name);
227
269
  }
228
270
  }
271
+ const toolChoice = captured.tool_choice;
272
+ if (isRecord(toolChoice)) {
273
+ const type = readString(toolChoice.type)?.toLowerCase();
274
+ if (type === 'function') {
275
+ const fnNode = isRecord(toolChoice.function)
276
+ ? toolChoice.function
277
+ : undefined;
278
+ const forcedName = readString(fnNode?.name) ?? readString(toolChoice.name);
279
+ if (forcedName) {
280
+ names.add(forcedName);
281
+ }
282
+ }
283
+ }
229
284
  return names;
230
285
  }
231
286
  function isToolChoiceRequired(captured) {
@@ -362,6 +417,128 @@ function normalizeToolArguments(value, toolName) {
362
417
  return null;
363
418
  }
364
419
  }
420
+ function hasAllowedToolName(allowed, candidate) {
421
+ if (allowed.has(candidate)) {
422
+ return true;
423
+ }
424
+ const lowered = candidate.toLowerCase();
425
+ for (const name of allowed) {
426
+ if (name.toLowerCase() === lowered) {
427
+ return true;
428
+ }
429
+ }
430
+ return false;
431
+ }
432
+ function findAllowedToolNameCaseInsensitive(allowed, candidate) {
433
+ if (allowed.has(candidate)) {
434
+ return candidate;
435
+ }
436
+ const lowered = candidate.toLowerCase();
437
+ for (const name of allowed) {
438
+ if (name.toLowerCase() === lowered) {
439
+ return name;
440
+ }
441
+ }
442
+ return undefined;
443
+ }
444
+ function resolveAllowedShellToolName(allowedToolNames) {
445
+ for (const candidate of SHELL_ALLOWED_TOOL_CANDIDATES) {
446
+ const resolved = findAllowedToolNameCaseInsensitive(allowedToolNames, candidate);
447
+ if (resolved) {
448
+ return resolved;
449
+ }
450
+ }
451
+ return undefined;
452
+ }
453
+ function resolveToolNameForCompat(rawName, allowedToolNames) {
454
+ const normalizedRawName = rawName.toLowerCase();
455
+ const alias = SHELL_TOOL_NAME_ALIASES.get(normalizedRawName);
456
+ const shellLike = SHELL_LIKE_TOOL_NAMES.has(normalizedRawName) || alias === 'exec_command';
457
+ if (allowedToolNames.size > 0 && shellLike) {
458
+ const allowedShell = resolveAllowedShellToolName(allowedToolNames);
459
+ if (allowedShell) {
460
+ return allowedShell;
461
+ }
462
+ }
463
+ if (!alias) {
464
+ return findAllowedToolNameCaseInsensitive(allowedToolNames, rawName) ?? rawName;
465
+ }
466
+ if (allowedToolNames.size === 0) {
467
+ return alias;
468
+ }
469
+ const allowedAlias = findAllowedToolNameCaseInsensitive(allowedToolNames, alias);
470
+ if (allowedAlias) {
471
+ return allowedAlias;
472
+ }
473
+ const allowedRaw = findAllowedToolNameCaseInsensitive(allowedToolNames, rawName);
474
+ if (allowedRaw) {
475
+ return allowedRaw;
476
+ }
477
+ return alias;
478
+ }
479
+ function readCommandFromShellArgs(args) {
480
+ const input = isRecord(args.input) ? args.input : undefined;
481
+ const direct = readString(args.cmd) ??
482
+ readString(args.command) ??
483
+ readString(args.script) ??
484
+ (() => {
485
+ const arr = Array.isArray(args.command) ? args.command : Array.isArray(args.cmd) ? args.cmd : undefined;
486
+ if (!arr)
487
+ return undefined;
488
+ const tokens = arr.map((item) => (item == null ? '' : String(item).trim())).filter((item) => item.length > 0);
489
+ return tokens.length ? tokens.join(' ') : undefined;
490
+ })();
491
+ if (direct) {
492
+ return direct;
493
+ }
494
+ if (!input) {
495
+ return undefined;
496
+ }
497
+ return (readString(input.cmd) ??
498
+ readString(input.command) ??
499
+ readString(input.script) ??
500
+ (() => {
501
+ const arr = Array.isArray(input.command) ? input.command : Array.isArray(input.cmd) ? input.cmd : undefined;
502
+ if (!arr)
503
+ return undefined;
504
+ const tokens = arr.map((item) => (item == null ? '' : String(item).trim())).filter((item) => item.length > 0);
505
+ return tokens.length ? tokens.join(' ') : undefined;
506
+ })());
507
+ }
508
+ function readWorkdirFromShellArgs(args) {
509
+ const input = isRecord(args.input) ? args.input : undefined;
510
+ return (readString(args.workdir) ??
511
+ readString(args.cwd) ??
512
+ readString(args.workDir) ??
513
+ readString(input?.workdir) ??
514
+ readString(input?.cwd));
515
+ }
516
+ function normalizeShellLikeArguments(argsString, argsSource) {
517
+ const parsed = tryParseJsonText(argsString);
518
+ const args = isRecord(parsed) ? { ...parsed } : isRecord(argsSource) ? { ...argsSource } : {};
519
+ const cmd = readCommandFromShellArgs(args);
520
+ if (!cmd) {
521
+ return argsString;
522
+ }
523
+ const workdir = readWorkdirFromShellArgs(args);
524
+ const next = {
525
+ ...args,
526
+ cmd,
527
+ command: cmd
528
+ };
529
+ if (workdir) {
530
+ next.workdir = workdir;
531
+ }
532
+ if (Object.prototype.hasOwnProperty.call(next, 'toon')) {
533
+ delete next.toon;
534
+ }
535
+ try {
536
+ return JSON.stringify(next);
537
+ }
538
+ catch {
539
+ return JSON.stringify({ cmd, command: cmd, ...(workdir ? { workdir } : {}) });
540
+ }
541
+ }
365
542
  function coerceToolCallsValue(toolCallsRaw) {
366
543
  if (Array.isArray(toolCallsRaw)) {
367
544
  return toolCallsRaw;
@@ -380,13 +557,11 @@ function coerceToolCallsValue(toolCallsRaw) {
380
557
  }
381
558
  function normalizeToolCallEntry(entry, allowedToolNames, callIdPrefix, index) {
382
559
  const fn = isRecord(entry.function) ? entry.function : undefined;
383
- const name = readString(entry.name) ?? readString(fn?.name);
384
- if (!name) {
385
- return null;
386
- }
387
- if (allowedToolNames.size > 0 && !allowedToolNames.has(name)) {
560
+ const rawName = readString(entry.name) ?? readString(fn?.name);
561
+ if (!rawName) {
388
562
  return null;
389
563
  }
564
+ const name = resolveToolNameForCompat(rawName, allowedToolNames);
390
565
  const argsSource = entry.input !== undefined
391
566
  ? entry.input
392
567
  : entry.arguments !== undefined
@@ -396,6 +571,9 @@ function normalizeToolCallEntry(entry, allowedToolNames, callIdPrefix, index) {
396
571
  if (!normalizedArgs) {
397
572
  return null;
398
573
  }
574
+ const finalArgs = SHELL_LIKE_TOOL_NAMES.has(name.toLowerCase())
575
+ ? normalizeShellLikeArguments(normalizedArgs, argsSource)
576
+ : normalizedArgs;
399
577
  const id = readString(entry.id) ??
400
578
  readString(entry.call_id) ??
401
579
  `${callIdPrefix}_${index + 1}`;
@@ -404,7 +582,7 @@ function normalizeToolCallEntry(entry, allowedToolNames, callIdPrefix, index) {
404
582
  type: 'function',
405
583
  function: {
406
584
  name,
407
- arguments: normalizedArgs
585
+ arguments: finalArgs
408
586
  }
409
587
  };
410
588
  }
@@ -473,17 +651,41 @@ function normalizeFunctionResultsMarkupText(value) {
473
651
  changed: true
474
652
  };
475
653
  }
654
+ function normalizeCommentaryMarkupText(value) {
655
+ const capturedCommentary = [];
656
+ let changed = false;
657
+ const stripped = value.replace(/<\s*commentary\s*>([\s\S]*?)<\s*\/\s*commentary\s*>/gi, (_match, inner) => {
658
+ changed = true;
659
+ const text = typeof inner === 'string' ? inner.trim() : '';
660
+ if (text.length) {
661
+ capturedCommentary.push(text);
662
+ }
663
+ return '';
664
+ });
665
+ if (!changed) {
666
+ return { text: value, changed: false };
667
+ }
668
+ const collapsed = stripped.replace(/\n{3,}/g, '\n\n').trim();
669
+ if (collapsed.length) {
670
+ return { text: collapsed, changed: true };
671
+ }
672
+ return {
673
+ text: capturedCommentary.join('\n\n').trim(),
674
+ changed: true
675
+ };
676
+ }
476
677
  function harvestFunctionResultsMarkup(message) {
477
678
  let harvested = false;
478
679
  const apply = (input) => {
479
680
  if (typeof input !== 'string') {
480
681
  return undefined;
481
682
  }
482
- const normalized = normalizeFunctionResultsMarkupText(input);
483
- if (normalized.changed) {
683
+ const functionResultsNormalized = normalizeFunctionResultsMarkupText(input);
684
+ if (functionResultsNormalized.changed) {
484
685
  harvested = true;
485
686
  }
486
- return normalized.text;
687
+ const commentaryNormalized = normalizeCommentaryMarkupText(functionResultsNormalized.text);
688
+ return commentaryNormalized.text;
487
689
  };
488
690
  if (typeof message.content === 'string') {
489
691
  const next = apply(message.content);
@@ -545,29 +747,87 @@ function extractToolCallsFromDeepSeekJsonText(text, allowedToolNames, callIdPref
545
747
  }
546
748
  }
547
749
  };
548
- const parsedRoot = tryParseJsonText(trimmed);
549
- if (parsedRoot !== null) {
550
- parseCandidate(parsedRoot);
551
- }
552
- if (normalized.length > 0) {
553
- return normalized;
554
- }
555
- const toolCallPattern = /\{\s*["']tool_calls["']\s*:\s*\[(.*?)\]\s*\}/gs;
556
- let match;
557
- while ((match = toolCallPattern.exec(trimmed)) !== null) {
558
- const segment = match[1];
559
- const parsed = tryParseJsonText(`{"tool_calls":[${segment}]}`);
560
- if (parsed !== null) {
561
- parseCandidate(parsed);
750
+ const standalonePayload = (() => {
751
+ const trimDeepSeekTailArtifacts = (value) => {
752
+ let out = value.trim();
753
+ // Remove DeepSeek sentinel tails (e.g. <|end|>, <| User |>).
754
+ out = out
755
+ .replace(/(?:<[\u007C\uFF5C]\s*end[^>\n]*[\u007C\uFF5C]>\s*)+$/i, '')
756
+ .replace(/(?:<[\u007C\uFF5C]\s*(?:user|assistant|system|tool|observation)[^>\n]*[\u007C\uFF5C]>\s*)+$/i, '')
757
+ .trim();
758
+ return out;
759
+ };
760
+ const extractLeadingJsonObject = (value) => {
761
+ const trimmedValue = value.trimStart();
762
+ if (!trimmedValue.startsWith('{')) {
763
+ return undefined;
764
+ }
765
+ let depth = 0;
766
+ let inString = false;
767
+ let escaped = false;
768
+ for (let i = 0; i < trimmedValue.length; i += 1) {
769
+ const ch = trimmedValue[i] ?? '';
770
+ if (inString) {
771
+ if (escaped) {
772
+ escaped = false;
773
+ continue;
774
+ }
775
+ if (ch === '\\') {
776
+ escaped = true;
777
+ continue;
778
+ }
779
+ if (ch === '"') {
780
+ inString = false;
781
+ }
782
+ continue;
783
+ }
784
+ if (ch === '"') {
785
+ inString = true;
786
+ continue;
787
+ }
788
+ if (ch === '{') {
789
+ depth += 1;
790
+ continue;
791
+ }
792
+ if (ch === '}') {
793
+ depth -= 1;
794
+ if (depth === 0) {
795
+ return trimmedValue.slice(0, i + 1).trim();
796
+ }
797
+ continue;
798
+ }
799
+ }
800
+ return undefined;
801
+ };
802
+ let candidate = trimDeepSeekTailArtifacts(trimmed);
803
+ const fenced = candidate.match(/^```(?:json|tool_call|tool_calls)?\s*([\s\S]*?)\s*```/i);
804
+ if (fenced?.[1]) {
805
+ candidate = trimDeepSeekTailArtifacts(fenced[1]);
806
+ }
807
+ const leadingObject = extractLeadingJsonObject(candidate);
808
+ if (!leadingObject) {
809
+ return undefined;
810
+ }
811
+ if (!/["']tool_calls["']\s*:/.test(leadingObject)) {
812
+ return undefined;
813
+ }
814
+ return leadingObject;
815
+ })();
816
+ if (standalonePayload) {
817
+ const parsedRoot = tryParseJsonText(standalonePayload);
818
+ if (parsedRoot !== null) {
819
+ parseCandidate(parsedRoot);
562
820
  }
563
821
  }
564
822
  if (normalized.length === 0) {
565
- const hasToolCallsMarker = /tool_calls/i.test(trimmed);
566
- const hasApplyPatchMarker = /apply_patch/i.test(trimmed);
823
+ const sourceForRepair = standalonePayload ?? trimmed;
824
+ const hasToolCallsMarker = /tool_calls/i.test(sourceForRepair);
825
+ const hasApplyPatchMarker = /apply_patch/i.test(sourceForRepair);
567
826
  if (hasToolCallsMarker && hasApplyPatchMarker) {
568
- const patchMatch = trimmed.match(/\*\*\*\s*Begin Patch[\s\S]*?\*\*\*\s*End Patch/);
827
+ const patchMatch = sourceForRepair.match(/\*\*\*\s*Begin Patch[\s\S]*?\*\*\*\s*End Patch/);
569
828
  const patchText = readString(patchMatch?.[0]);
570
- if (patchText && (allowedToolNames.size === 0 || allowedToolNames.has('apply_patch'))) {
829
+ const applyPatchAllowed = allowedToolNames.size === 0 || hasAllowedToolName(allowedToolNames, 'apply_patch');
830
+ if (patchText && applyPatchAllowed) {
571
831
  const validation = validateToolCall('apply_patch', patchText);
572
832
  if (validation.ok && typeof validation.normalizedArgs === 'string' && validation.normalizedArgs.trim().length > 0) {
573
833
  normalized.push({
@@ -683,9 +943,11 @@ function extractCommandCandidates(text) {
683
943
  return commands;
684
944
  }
685
945
  function normalizeMessageCommandToolCall(message, allowedToolNames, callIdPrefix) {
686
- if (allowedToolNames.size > 0 && !allowedToolNames.has('exec_command')) {
687
- return false;
688
- }
946
+ const commandToolName = hasAllowedToolName(allowedToolNames, 'exec_command')
947
+ ? 'exec_command'
948
+ : hasAllowedToolName(allowedToolNames, 'shell_command')
949
+ ? 'shell_command'
950
+ : 'exec_command';
689
951
  const candidates = collectMessageTextCandidates(message);
690
952
  if (!candidates.length) {
691
953
  return false;
@@ -701,8 +963,8 @@ function normalizeMessageCommandToolCall(message, allowedToolNames, callIdPrefix
701
963
  id: `${callIdPrefix}_cmd_1`,
702
964
  type: 'function',
703
965
  function: {
704
- name: 'exec_command',
705
- arguments: JSON.stringify({ cmd })
966
+ name: commandToolName,
967
+ arguments: JSON.stringify({ cmd, command: cmd })
706
968
  }
707
969
  }
708
970
  ];
@@ -799,7 +1061,7 @@ function normalizeChoice(choice, options, allowedToolNames, callIdPrefix) {
799
1061
  harvestedFunctionResults: harvestFunctionResultsMarkup(message)
800
1062
  };
801
1063
  }
802
- const transformed = normalizeAssistantTextToToolCalls(message);
1064
+ const transformed = normalizeAssistantTextToToolCalls(message, options.textNormalizer);
803
1065
  if (transformed !== message) {
804
1066
  choice.message = transformed;
805
1067
  }
@@ -839,13 +1101,13 @@ function writeCompatState(root, state, source, harvestedFunctionResults) {
839
1101
  };
840
1102
  root.metadata = metadata;
841
1103
  }
842
- export function applyDeepSeekWebResponseTransform(payload, adapterContext) {
1104
+ export function applyDeepSeekWebResponseTransform(payload, adapterContext, config) {
843
1105
  if (!payload || typeof payload !== 'object') {
844
1106
  return payload;
845
1107
  }
846
1108
  const cloned = structuredClone(payload);
847
1109
  const root = normalizeDeepSeekBusinessEnvelope(cloned, adapterContext);
848
- const options = resolveOptions(adapterContext);
1110
+ const options = resolveOptions(adapterContext, config);
849
1111
  const captured = resolveCapturedRequest(adapterContext);
850
1112
  const allowedToolNames = resolveRequestedToolNames(captured);
851
1113
  const toolChoiceRequired = isToolChoiceRequired(captured);
@@ -1,4 +1,8 @@
1
1
  import type { JsonObject } from '../../hub/types/json.js';
2
+ import { type TextMarkupNormalizeOptions } from '../../shared/text-markup-normalizer.js';
3
+ export interface HarvestToolCallsFromTextConfig {
4
+ normalizer?: TextMarkupNormalizeOptions;
5
+ }
2
6
  /**
3
7
  * Harvest tool calls from assistant textual markup into OpenAI `tool_calls`.
4
8
  *
@@ -8,3 +12,4 @@ import type { JsonObject } from '../../hub/types/json.js';
8
12
  * This action is response-only and provider-scoped via compatibility profiles.
9
13
  */
10
14
  export declare function harvestToolCallsFromText(payload: JsonObject): JsonObject;
15
+ export declare function harvestToolCallsFromTextWithConfig(payload: JsonObject, config?: HarvestToolCallsFromTextConfig): JsonObject;
@@ -23,7 +23,7 @@ function extractResponsesMessageText(item) {
23
23
  }
24
24
  return parts.join('\n').trim();
25
25
  }
26
- function harvestToolCallsFromResponsesPayloadInPlace(root) {
26
+ function harvestToolCallsFromResponsesPayloadInPlace(root, config) {
27
27
  const output = Array.isArray(root.output) ? root.output : [];
28
28
  if (!output.length)
29
29
  return false;
@@ -49,7 +49,7 @@ function harvestToolCallsFromResponsesPayloadInPlace(root) {
49
49
  nextOutput.push(item);
50
50
  continue;
51
51
  }
52
- const normalized = normalizeAssistantTextToToolCalls({ role: 'assistant', content: text });
52
+ const normalized = normalizeAssistantTextToToolCalls({ role: 'assistant', content: text }, config?.normalizer);
53
53
  const toolCalls = Array.isArray(normalized.tool_calls) ? normalized.tool_calls : [];
54
54
  if (!toolCalls.length) {
55
55
  nextOutput.push(item);
@@ -91,12 +91,15 @@ function harvestToolCallsFromResponsesPayloadInPlace(root) {
91
91
  * This action is response-only and provider-scoped via compatibility profiles.
92
92
  */
93
93
  export function harvestToolCallsFromText(payload) {
94
+ return harvestToolCallsFromTextWithConfig(payload);
95
+ }
96
+ export function harvestToolCallsFromTextWithConfig(payload, config) {
94
97
  const root = structuredClone(payload);
95
98
  const choices = Array.isArray(root.choices) ? root.choices : [];
96
99
  // Responses provider payload: harvest into canonical Responses output items first.
97
100
  // This allows the normal semantic mapper (Responses → Chat) to surface tool_calls for servertool orchestration.
98
101
  if (!choices.length) {
99
- harvestToolCallsFromResponsesPayloadInPlace(root);
102
+ harvestToolCallsFromResponsesPayloadInPlace(root, config);
100
103
  return root;
101
104
  }
102
105
  for (const choice of choices) {
@@ -105,7 +108,7 @@ export function harvestToolCallsFromText(payload) {
105
108
  const message = choice.message;
106
109
  if (!isRecord(message))
107
110
  continue;
108
- const normalized = normalizeAssistantTextToToolCalls(message);
111
+ const normalized = normalizeAssistantTextToToolCalls(message, config?.normalizer);
109
112
  if (normalized !== message) {
110
113
  choice.message = normalized;
111
114
  const hasToolCalls = Array.isArray(normalized.tool_calls) && normalized.tool_calls.length > 0;
@@ -9,4 +9,5 @@ import type { JsonObject } from '../../hub/types/json.js';
9
9
  */
10
10
  export declare function applyIflowToolTextFallback(payload: JsonObject, options?: {
11
11
  models?: string[];
12
+ routeId?: string;
12
13
  }): JsonObject;
@@ -2,6 +2,13 @@ const isRecord = (value) => typeof value === 'object' && value !== null && !Arra
2
2
  function normalizeModel(value) {
3
3
  return typeof value === 'string' ? value.trim().toLowerCase() : '';
4
4
  }
5
+ function isWebSearchRoute(routeId) {
6
+ if (typeof routeId !== 'string') {
7
+ return false;
8
+ }
9
+ const normalized = routeId.trim().toLowerCase();
10
+ return normalized.startsWith('web_search') || normalized.startsWith('search');
11
+ }
5
12
  function hasNonEmptyArray(value) {
6
13
  return Array.isArray(value) && value.length > 0;
7
14
  }
@@ -169,6 +176,11 @@ export function applyIflowToolTextFallback(payload, options) {
169
176
  return payload;
170
177
  }
171
178
  const root = structuredClone(payload);
179
+ if (isWebSearchRoute(options?.routeId)) {
180
+ // web_search/search routes are independent transport paths and must not be coupled
181
+ // to model-gated fallback heuristics.
182
+ return root;
183
+ }
172
184
  const model = normalizeModel(root.model);
173
185
  const models = Array.isArray(options?.models) ? options.models.map((m) => normalizeModel(m)).filter(Boolean) : [];
174
186
  if (!model || !models.includes(model)) {
@@ -1,7 +1,7 @@
1
1
  function isRecord(value) {
2
2
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
3
3
  }
4
- const ORPHAN_TAG_RE = /^\s*(?:[•*+-]\s*)?<\/?\s*function_calls\s*\/?\s*>\s*$/i;
4
+ const ORPHAN_TAG_RE = /^\s*(?:[•*+-]\s*)?(?:<\/?\s*function_calls\s*\/?\s*>|<\/\s*(?:parameter|function|tool_call)\s*>)\s*$/i;
5
5
  function stripOrphanTagLines(text) {
6
6
  const raw = String(text ?? '');
7
7
  if (!raw)
@@ -0,0 +1,9 @@
1
+ import type { JsonObject } from '../../hub/types/json.js';
2
+ export interface ToolTextRequestGuidanceConfig {
3
+ enabled?: boolean;
4
+ marker?: string;
5
+ instruction?: string;
6
+ requireTools?: boolean;
7
+ includeToolNames?: boolean;
8
+ }
9
+ export declare function applyToolTextRequestGuidance(payload: JsonObject, config?: ToolTextRequestGuidanceConfig): JsonObject;