@jsonstudio/llms 0.6.1164 → 0.6.1354

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 (164) hide show
  1. package/dist/conversion/codecs/gemini-openai-codec.d.ts +3 -1
  2. package/dist/conversion/codecs/gemini-openai-codec.js +10 -4
  3. package/dist/conversion/compat/actions/gemini-web-search.d.ts +1 -1
  4. package/dist/conversion/compat/actions/gemini-web-search.js +5 -2
  5. package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +12 -0
  6. package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +199 -0
  7. package/dist/conversion/compat/actions/iflow-web-search.d.ts +1 -1
  8. package/dist/conversion/compat/actions/iflow-web-search.js +5 -2
  9. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +47 -56
  10. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +1 -13
  11. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +523 -50
  12. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +18 -38
  13. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +6 -0
  14. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
  15. package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.d.ts +10 -0
  16. package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.js +134 -0
  17. package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.d.ts +6 -0
  18. package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.js +79 -0
  19. package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.d.ts +3 -0
  20. package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.js +46 -0
  21. package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.d.ts +8 -0
  22. package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.js +366 -0
  23. package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.d.ts +9 -0
  24. package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.js +384 -0
  25. package/dist/conversion/hub/pipeline/hub-pipeline/node-results.d.ts +3 -0
  26. package/dist/conversion/hub/pipeline/hub-pipeline/node-results.js +14 -0
  27. package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.d.ts +2 -0
  28. package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.js +144 -0
  29. package/dist/conversion/hub/pipeline/hub-pipeline/policy.d.ts +4 -0
  30. package/dist/conversion/hub/pipeline/hub-pipeline/policy.js +32 -0
  31. package/dist/conversion/hub/pipeline/hub-pipeline/protocol.d.ts +8 -0
  32. package/dist/conversion/hub/pipeline/hub-pipeline/protocol.js +63 -0
  33. package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.d.ts +2 -0
  34. package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.js +43 -0
  35. package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.d.ts +1 -0
  36. package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.js +29 -0
  37. package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.d.ts +2 -0
  38. package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.js +16 -0
  39. package/dist/conversion/hub/pipeline/hub-pipeline/types.d.ts +116 -0
  40. package/dist/conversion/hub/pipeline/hub-pipeline/types.js +1 -0
  41. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +3 -95
  42. package/dist/conversion/hub/pipeline/hub-pipeline.js +19 -1281
  43. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +1 -1
  44. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +7 -0
  45. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +65 -1
  46. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +25 -22
  47. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +1 -1
  48. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.d.ts +1 -1
  49. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.js +2 -2
  50. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
  51. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +1 -1
  52. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +1 -1
  53. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +11 -11
  54. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage2_format_parse/index.js +1 -1
  55. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.d.ts +1 -0
  56. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.js +4 -2
  57. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.d.ts +1 -0
  58. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +17 -9
  59. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage2_sse_stream/index.js +2 -2
  60. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +40 -2
  61. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.js +1 -1
  62. package/dist/conversion/hub/pipeline/target-utils.js +9 -5
  63. package/dist/conversion/hub/process/chat-process.js +256 -16
  64. package/dist/conversion/hub/response/provider-response.d.ts +8 -0
  65. package/dist/conversion/hub/response/provider-response.js +85 -27
  66. package/dist/conversion/hub/response/response-mappers.d.ts +10 -3
  67. package/dist/conversion/hub/response/response-mappers.js +30 -6
  68. package/dist/conversion/hub/response/response-runtime.js +4 -38
  69. package/dist/conversion/hub/snapshot-recorder.js +5 -1
  70. package/dist/conversion/hub/standardized-bridge.js +23 -15
  71. package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +36 -5
  72. package/dist/conversion/responses/responses-openai-bridge.js +20 -4
  73. package/dist/conversion/shared/gemini-tool-utils.d.ts +8 -1
  74. package/dist/conversion/shared/gemini-tool-utils.js +580 -108
  75. package/dist/conversion/shared/jsonish.js +1 -1
  76. package/dist/conversion/shared/mcp-injection.js +67 -33
  77. package/dist/conversion/shared/openai-finalizer.js +2 -1
  78. package/dist/conversion/shared/openai-message-normalize.js +76 -21
  79. package/dist/conversion/shared/responses-output-builder.js +6 -0
  80. package/dist/conversion/shared/runtime-metadata.d.ts +7 -0
  81. package/dist/conversion/shared/runtime-metadata.js +23 -0
  82. package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
  83. package/dist/conversion/shared/text-markup-normalizer.js +284 -4
  84. package/dist/conversion/shared/tool-canonicalizer.js +2 -1
  85. package/dist/conversion/shared/tool-governor.js +3 -3
  86. package/dist/filters/engine.js +5 -5
  87. package/dist/filters/special/request-tool-list-filter.js +194 -60
  88. package/dist/filters/special/request-tools-normalize.js +1 -1
  89. package/dist/filters/special/response-tool-text-canonicalize.d.ts +4 -7
  90. package/dist/filters/special/response-tool-text-canonicalize.js +7 -35
  91. package/dist/filters/special/tool-filter-hooks.js +58 -62
  92. package/dist/guidance/index.js +5 -1
  93. package/dist/http/sse-response.js +6 -6
  94. package/dist/router/virtual-router/bootstrap.js +65 -5
  95. package/dist/router/virtual-router/context-advisor.d.ts +4 -0
  96. package/dist/router/virtual-router/context-advisor.js +3 -0
  97. package/dist/router/virtual-router/context-weighted.d.ts +31 -0
  98. package/dist/router/virtual-router/context-weighted.js +54 -0
  99. package/dist/router/virtual-router/engine-health.d.ts +1 -1
  100. package/dist/router/virtual-router/engine-health.js +11 -110
  101. package/dist/router/virtual-router/engine-selection/alias-selection.d.ts +15 -0
  102. package/dist/router/virtual-router/engine-selection/alias-selection.js +156 -0
  103. package/dist/router/virtual-router/engine-selection/context-weight-multipliers.d.ts +11 -0
  104. package/dist/router/virtual-router/engine-selection/context-weight-multipliers.js +23 -0
  105. package/dist/router/virtual-router/engine-selection/direct-provider-model.d.ts +9 -0
  106. package/dist/router/virtual-router/engine-selection/direct-provider-model.js +49 -0
  107. package/dist/router/virtual-router/engine-selection/instruction-target.d.ts +6 -0
  108. package/dist/router/virtual-router/engine-selection/instruction-target.js +54 -0
  109. package/dist/router/virtual-router/engine-selection/key-parsing.d.ts +8 -0
  110. package/dist/router/virtual-router/engine-selection/key-parsing.js +64 -0
  111. package/dist/router/virtual-router/engine-selection/route-utils.d.ts +12 -0
  112. package/dist/router/virtual-router/engine-selection/route-utils.js +150 -0
  113. package/dist/router/virtual-router/engine-selection/routing-state-filter.d.ts +4 -0
  114. package/dist/router/virtual-router/engine-selection/routing-state-filter.js +50 -0
  115. package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +39 -0
  116. package/dist/router/virtual-router/engine-selection/selection-deps.js +1 -0
  117. package/dist/router/virtual-router/engine-selection/sticky-pool.d.ts +11 -0
  118. package/dist/router/virtual-router/engine-selection/sticky-pool.js +109 -0
  119. package/dist/router/virtual-router/engine-selection/tier-priority.d.ts +12 -0
  120. package/dist/router/virtual-router/engine-selection/tier-priority.js +55 -0
  121. package/dist/router/virtual-router/engine-selection/tier-selection-select.d.ts +22 -0
  122. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +400 -0
  123. package/dist/router/virtual-router/engine-selection/tier-selection.d.ts +3 -0
  124. package/dist/router/virtual-router/engine-selection/tier-selection.js +225 -0
  125. package/dist/router/virtual-router/engine-selection.d.ts +4 -30
  126. package/dist/router/virtual-router/engine-selection.js +10 -815
  127. package/dist/router/virtual-router/engine.d.ts +1 -0
  128. package/dist/router/virtual-router/engine.js +55 -10
  129. package/dist/router/virtual-router/routing-instructions.js +6 -1
  130. package/dist/router/virtual-router/stop-message-state-sync.d.ts +5 -0
  131. package/dist/router/virtual-router/stop-message-state-sync.js +6 -14
  132. package/dist/router/virtual-router/types.d.ts +53 -1
  133. package/dist/servertool/clock/config.d.ts +8 -0
  134. package/dist/servertool/clock/config.js +22 -0
  135. package/dist/servertool/clock/log.d.ts +3 -0
  136. package/dist/servertool/clock/log.js +13 -0
  137. package/dist/servertool/clock/task-store.d.ts +1 -1
  138. package/dist/servertool/clock/task-store.js +1 -1
  139. package/dist/servertool/clock/tasks.js +1 -1
  140. package/dist/servertool/engine.js +146 -21
  141. package/dist/servertool/handlers/clock-auto.js +11 -6
  142. package/dist/servertool/handlers/clock.js +36 -10
  143. package/dist/servertool/handlers/followup-request-builder.js +8 -2
  144. package/dist/servertool/handlers/gemini-empty-reply-continue.js +15 -9
  145. package/dist/servertool/handlers/iflow-model-error-retry.js +6 -4
  146. package/dist/servertool/handlers/recursive-detection-guard.js +4 -2
  147. package/dist/servertool/handlers/stop-message-auto.js +100 -10
  148. package/dist/servertool/handlers/vision.js +4 -1
  149. package/dist/servertool/handlers/web-search.js +3 -1
  150. package/dist/servertool/pending-session.d.ts +19 -0
  151. package/dist/servertool/pending-session.js +97 -0
  152. package/dist/servertool/reenter-backend.js +5 -3
  153. package/dist/servertool/server-side-tools.js +235 -6
  154. package/dist/servertool/types.d.ts +13 -0
  155. package/dist/sse/json-to-sse/event-generators/responses.js +1 -1
  156. package/dist/sse/shared/chat-serializer.js +2 -2
  157. package/dist/sse/shared/constants.js +1 -1
  158. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +7 -1
  159. package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
  160. package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
  161. package/dist/tools/apply-patch/execution-capturer.js +1 -1
  162. package/dist/tools/exec-command/normalize.js +4 -0
  163. package/dist/tools/exec-command/regression-capturer.js +1 -1
  164. package/package.json +10 -5
@@ -118,6 +118,105 @@ function enabled() {
118
118
  }
119
119
  }
120
120
  // 已移除所有 rcc.tool.v1 相关处理:不再识别或剥离 rcc 封装
121
+ // Extract <tool:NAME>...</tool:NAME> blocks used by some tool-mode outputs.
122
+ // Example:
123
+ // <tool:exec_command>
124
+ // <command>cd /repo && npm test</command>
125
+ // <timeout_ms>120000</timeout_ms>
126
+ // <requires_approval>false</requires_approval>
127
+ // </tool:exec_command>
128
+ export function extractToolNamespaceXmlBlocksFromText(text) {
129
+ try {
130
+ if (typeof text !== 'string' || !text)
131
+ return null;
132
+ if (!text.includes('<tool:'))
133
+ return null;
134
+ const out = [];
135
+ const blockRe = /<\s*tool:([A-Za-z0-9_]+)\s*>([\s\S]*?)<\/\s*tool:\s*\1\s*>/gi;
136
+ let bm;
137
+ while ((bm = blockRe.exec(text)) !== null) {
138
+ const rawName = String(bm[1] || '').trim();
139
+ const lname = rawName.toLowerCase();
140
+ if (!lname || !KNOWN_TOOLS.has(lname))
141
+ continue;
142
+ const inner = String(bm[2] || '');
143
+ const argsObj = {};
144
+ const kvRe = /<\s*([A-Za-z_][A-Za-z0-9_]*)\s*>([\s\S]*?)<\/\s*\1\s*>/g;
145
+ let km;
146
+ while ((km = kvRe.exec(inner)) !== null) {
147
+ const rawKey = String(km[1] || '').trim();
148
+ const key = normalizeKey(rawKey).toLowerCase();
149
+ if (!key)
150
+ continue;
151
+ if (key === 'requires_approval')
152
+ continue;
153
+ const rawVal = String(km[2] ?? '');
154
+ const value = tryParsePrimitiveValue(rawVal);
155
+ if (lname === 'exec_command' && (key === 'command' || key === 'cmd')) {
156
+ const cmd = coerceCommandValueToString(value);
157
+ if (cmd.trim().length) {
158
+ argsObj.cmd = cmd;
159
+ argsObj.command = cmd;
160
+ }
161
+ continue;
162
+ }
163
+ if (lname === 'exec_command' && (key === 'cwd' || key === 'workdir')) {
164
+ const wd = String(value ?? rawVal).trim();
165
+ if (wd)
166
+ argsObj.workdir = wd;
167
+ continue;
168
+ }
169
+ if (lname === 'write_stdin') {
170
+ if (key === 'input' || key === 'data' || key === 'chars' || key === 'text') {
171
+ argsObj.chars = rawVal;
172
+ continue;
173
+ }
174
+ if (key === 'session_id') {
175
+ const n = Number.parseInt(String(value), 10);
176
+ argsObj.session_id = Number.isFinite(n) ? n : value;
177
+ continue;
178
+ }
179
+ }
180
+ if (lname === 'apply_patch' && (key === 'patch' || key === 'input' || key === 'text' || key === 'instructions')) {
181
+ const patchText = typeof value === 'string' ? value : rawVal;
182
+ if (typeof patchText === 'string' && patchText.trim().length) {
183
+ argsObj.patch = patchText;
184
+ }
185
+ continue;
186
+ }
187
+ argsObj[key] = value;
188
+ }
189
+ const filtered = filterArgsForTool(lname, argsObj);
190
+ if (lname === 'exec_command') {
191
+ const cmd = typeof filtered?.cmd === 'string' ? String(filtered.cmd).trim() : '';
192
+ if (!cmd)
193
+ continue;
194
+ }
195
+ if (lname === 'write_stdin') {
196
+ const sid = filtered?.session_id;
197
+ if (sid === undefined || sid === null || String(sid).trim().length === 0)
198
+ continue;
199
+ }
200
+ if (lname === 'apply_patch') {
201
+ const patchText = typeof filtered?.patch === 'string' ? String(filtered.patch).trim() : '';
202
+ if (!patchText)
203
+ continue;
204
+ }
205
+ let argsStr = '{}';
206
+ try {
207
+ argsStr = JSON.stringify(filtered);
208
+ }
209
+ catch {
210
+ argsStr = '{}';
211
+ }
212
+ out.push({ id: `call_${Math.random().toString(36).slice(2, 10)}`, name: lname, args: argsStr });
213
+ }
214
+ return out.length ? out : null;
215
+ }
216
+ catch {
217
+ return null;
218
+ }
219
+ }
121
220
  function extractStructuredApplyPatchPayloads(text) {
122
221
  const payloads = [];
123
222
  try {
@@ -160,6 +259,27 @@ function tryParseJsonValue(text) {
160
259
  return null;
161
260
  }
162
261
  }
262
+ function tryParsePrimitiveValue(text) {
263
+ try {
264
+ const trimmed = String(text ?? '').trim();
265
+ if (trimmed.length === 0)
266
+ return '';
267
+ const parsed = tryParseJsonValue(trimmed);
268
+ if (parsed !== null)
269
+ return parsed;
270
+ if (trimmed === 'true' || trimmed === 'false')
271
+ return trimmed === 'true';
272
+ if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
273
+ const n = Number(trimmed);
274
+ if (Number.isFinite(n))
275
+ return n;
276
+ }
277
+ return trimmed;
278
+ }
279
+ catch {
280
+ return String(text ?? '');
281
+ }
282
+ }
163
283
  function coerceCommandValueToString(value) {
164
284
  try {
165
285
  if (value === null || value === undefined)
@@ -197,6 +317,8 @@ function looksLikeBrokenToolMarkup(text) {
197
317
  if (!t)
198
318
  return false;
199
319
  return (t.includes('<exec_command') ||
320
+ t.includes('<invoke') ||
321
+ t.includes('<function_calls') ||
200
322
  t.includes('<parameter') ||
201
323
  t.includes('<tool_call') ||
202
324
  t.includes('</tool_call') ||
@@ -329,6 +451,162 @@ export function extractParameterXmlToolsFromText(text) {
329
451
  return null;
330
452
  }
331
453
  }
454
+ function applyInvokeParameterAliases(toolName, argsObj) {
455
+ try {
456
+ const lname = String(toolName || '').toLowerCase();
457
+ if (lname === 'write_stdin') {
458
+ // Some models emit `data` / `wait` instead of our canonical `chars` / `yield_time_ms`.
459
+ if (Object.prototype.hasOwnProperty.call(argsObj, 'data')) {
460
+ const data = argsObj.data;
461
+ if (!Object.prototype.hasOwnProperty.call(argsObj, 'chars') && !Object.prototype.hasOwnProperty.call(argsObj, 'text')) {
462
+ argsObj.chars = data == null ? '' : String(data);
463
+ }
464
+ delete argsObj.data;
465
+ }
466
+ if (Object.prototype.hasOwnProperty.call(argsObj, 'wait') && !Object.prototype.hasOwnProperty.call(argsObj, 'yield_time_ms')) {
467
+ const wait = argsObj.wait;
468
+ let ms;
469
+ if (typeof wait === 'number' && Number.isFinite(wait)) {
470
+ // Heuristic: small values are usually seconds; large values are likely already ms.
471
+ ms = wait <= 1000 ? Math.round(wait * 1000) : Math.round(wait);
472
+ }
473
+ else if (typeof wait === 'string' && wait.trim().length) {
474
+ const num = Number(wait.trim());
475
+ if (Number.isFinite(num)) {
476
+ ms = num <= 1000 ? Math.round(num * 1000) : Math.round(num);
477
+ }
478
+ }
479
+ if (typeof ms === 'number' && Number.isFinite(ms) && ms >= 0) {
480
+ argsObj.yield_time_ms = ms;
481
+ }
482
+ delete argsObj.wait;
483
+ }
484
+ }
485
+ }
486
+ catch {
487
+ // ignore
488
+ }
489
+ }
490
+ // Extract <function_calls><invoke name="..."><parameter name="...">...</parameter>...</invoke> blocks.
491
+ // Example seen in Gemini CLI / antigravity compact output:
492
+ // <function_calls>
493
+ // <invoke name="write_stdin">
494
+ // <parameter name="session_id">91806</parameter>
495
+ // <parameter name="data"></parameter>
496
+ // <parameter name="wait">10</parameter>
497
+ // </invoke>
498
+ // </function_calls>
499
+ export function extractInvokeToolsFromText(text) {
500
+ try {
501
+ if (typeof text !== 'string' || !text)
502
+ return null;
503
+ const out = [];
504
+ const invokeRe = /<\s*invoke\s+name\s*=\s*"([^">]+)"\s*>([\s\S]*?)<\/\s*invoke\s*>/gi;
505
+ let im;
506
+ while ((im = invokeRe.exec(text)) !== null) {
507
+ const rawName = (im[1] || '').trim();
508
+ const inner = (im[2] || '').trim();
509
+ if (!rawName || !inner)
510
+ continue;
511
+ const lname = rawName.toLowerCase();
512
+ const argsObj = {};
513
+ const paramRe = /<\s*parameter\s+name\s*=\s*"([^">]+)"\s*>([\s\S]*?)<\/\s*parameter\s*>/gi;
514
+ let pm;
515
+ while ((pm = paramRe.exec(inner)) !== null) {
516
+ const rawKey = (pm[1] || '').trim();
517
+ const key = normalizeKey(rawKey);
518
+ if (!key)
519
+ continue;
520
+ const rawVal = (pm[2] ?? '').toString();
521
+ const value = tryParsePrimitiveValue(rawVal);
522
+ if (lname === 'exec_command' && (key.toLowerCase() === 'command' || key.toLowerCase() === 'cmd')) {
523
+ const cmd = coerceCommandValueToString(value);
524
+ if (cmd.trim().length) {
525
+ argsObj.cmd = cmd;
526
+ argsObj.command = cmd;
527
+ }
528
+ continue;
529
+ }
530
+ argsObj[key] = value;
531
+ }
532
+ applyInvokeParameterAliases(lname, argsObj);
533
+ const filtered = filterArgsForTool(lname, argsObj);
534
+ let argsStr = '{}';
535
+ try {
536
+ argsStr = JSON.stringify(filtered);
537
+ }
538
+ catch {
539
+ argsStr = '{}';
540
+ }
541
+ out.push({ id: `call_${Math.random().toString(36).slice(2, 10)}`, name: lname, args: argsStr });
542
+ }
543
+ // Some Gemini CLI / antigravity variants emit a looser XML shape:
544
+ // <function_calls>
545
+ // <invoke>
546
+ // <tool_name>exec_command</tool_name>
547
+ // <parameters>
548
+ // <command>echo hi</command>
549
+ // </parameters>
550
+ // </invoke>
551
+ // </function_calls>
552
+ //
553
+ // Best-effort: only accept known tools, and only read simple <parameters><k>v</k></parameters>.
554
+ if (out.length === 0) {
555
+ const invokeBareRe = /<\s*invoke\s*>([\s\S]*?)<\/\s*invoke\s*>/gi;
556
+ let bm;
557
+ while ((bm = invokeBareRe.exec(text)) !== null) {
558
+ const inner = (bm[1] || '').trim();
559
+ if (!inner)
560
+ continue;
561
+ const toolNameMatch = /<\s*(?:tool_name|toolname|tool)\s*>\s*([\s\S]*?)\s*<\/\s*(?:tool_name|toolname|tool)\s*>/i.exec(inner);
562
+ const rawName = (toolNameMatch?.[1] || '').trim();
563
+ if (!rawName)
564
+ continue;
565
+ const lname = rawName.toLowerCase();
566
+ if (!KNOWN_TOOLS.has(lname))
567
+ continue;
568
+ const argsObj = {};
569
+ const paramsMatch = /<\s*parameters\s*>([\s\S]*?)<\/\s*parameters\s*>/i.exec(inner);
570
+ if (paramsMatch && paramsMatch[1]) {
571
+ const paramsInner = String(paramsMatch[1] || '');
572
+ const kvRe = /<\s*([A-Za-z_][A-Za-z0-9_]*)\s*>([\s\S]*?)<\/\s*\1\s*>/g;
573
+ let km;
574
+ while ((km = kvRe.exec(paramsInner)) !== null) {
575
+ const rawKey = (km[1] || '').trim();
576
+ const key = normalizeKey(rawKey);
577
+ if (!key)
578
+ continue;
579
+ const rawVal = (km[2] ?? '').toString();
580
+ const value = tryParsePrimitiveValue(rawVal);
581
+ if (lname === 'exec_command' && (key.toLowerCase() === 'command' || key.toLowerCase() === 'cmd')) {
582
+ const cmd = coerceCommandValueToString(value);
583
+ if (cmd.trim().length) {
584
+ argsObj.cmd = cmd;
585
+ argsObj.command = cmd;
586
+ }
587
+ continue;
588
+ }
589
+ argsObj[key] = value;
590
+ }
591
+ }
592
+ applyInvokeParameterAliases(lname, argsObj);
593
+ const filtered = filterArgsForTool(lname, argsObj);
594
+ let argsStr = '{}';
595
+ try {
596
+ argsStr = JSON.stringify(filtered);
597
+ }
598
+ catch {
599
+ argsStr = '{}';
600
+ }
601
+ out.push({ id: `call_${Math.random().toString(36).slice(2, 10)}`, name: lname, args: argsStr });
602
+ }
603
+ }
604
+ return out.length ? out : null;
605
+ }
606
+ catch {
607
+ return null;
608
+ }
609
+ }
332
610
  function tryParseJsonArrayOnLine(line) {
333
611
  try {
334
612
  const trimmed = String(line || '').trim();
@@ -477,14 +755,14 @@ export function extractXMLToolCallsFromText(text) {
477
755
  continue;
478
756
  // 1) try function/name tags
479
757
  let name = '';
480
- const fnTag = inner.match(/<\s*(?:function|name)\s*>\s*([a-zA-Z0-9_\-\.]+)\s*<\/(?:function|name)\s*>/i);
758
+ const fnTag = inner.match(/<\s*(?:function|name)\s*>\s*([a-zA-Z0-9_.-]+)\s*<\/(?:function|name)\s*>/i);
481
759
  if (fnTag && fnTag[1]) {
482
760
  name = String(fnTag[1]).trim();
483
761
  }
484
762
  else {
485
763
  // 2) else pick the first non-empty line without tags as the name
486
764
  const lines = inner.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
487
- const candidate = lines.find(l => !l.startsWith('<') && /^[a-zA-Z0-9_\-\.]+$/.test(l));
765
+ const candidate = lines.find(l => !l.startsWith('<') && /^[a-zA-Z0-9_.-]+$/.test(l));
488
766
  if (candidate)
489
767
  name = candidate;
490
768
  }
@@ -562,7 +840,7 @@ export function extractXMLToolCallsFromText(text) {
562
840
  const before = text.slice(0, start);
563
841
  const lines = before.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
564
842
  const cand = lines.length ? lines[lines.length - 1] : '';
565
- if (/^[a-zA-Z0-9_\-\.]{2,}$/.test(cand))
843
+ if (/^[a-zA-Z0-9_.-]{2,}$/.test(cand))
566
844
  name = cand;
567
845
  }
568
846
  catch { /* ignore */ }
@@ -727,7 +1005,9 @@ export function normalizeAssistantTextToToolCalls(message) {
727
1005
  for (const text of candidates) {
728
1006
  // Order: xml-like <tool_call> → apply_patch → execute blocks → 简单 XML 工具(list_directory 等) → bare command salvage
729
1007
  calls =
730
- extractParameterXmlToolsFromText(text) ||
1008
+ extractToolNamespaceXmlBlocksFromText(text) ||
1009
+ extractParameterXmlToolsFromText(text) ||
1010
+ extractInvokeToolsFromText(text) ||
731
1011
  extractXMLToolCallsFromText(text) ||
732
1012
  extractApplyPatchCallsFromText(text) ||
733
1013
  extractExecuteBlocksFromText(text) ||
@@ -14,8 +14,9 @@ export function canonicalizeChatResponseTools(payload) {
14
14
  continue;
15
15
  // ensure arguments is string and content is null when tool_calls present
16
16
  try {
17
- if (!ch.finish_reason)
17
+ if (String(ch.finish_reason || '').toLowerCase() !== 'tool_calls') {
18
18
  ch.finish_reason = 'tool_calls';
19
+ }
19
20
  }
20
21
  catch { /* ignore */ }
21
22
  try {
@@ -1,5 +1,5 @@
1
1
  // Unified tool governance API (标准)
2
- // Centralizes tool augmentation, guidance injection/refinement, and textual→tool_calls canonicalization
2
+ // Centralizes tool augmentation, guidance injection/refinement, and structured tool_calls canonicalization
3
3
  // canonicalizer 按需加载(避免在请求侧仅注入时引入不必要的模块)
4
4
  // enforceChatBudget: 为避免在请求侧引入多余依赖,这里提供最小实现(保留形状,不裁剪)。
5
5
  import { augmentOpenAITools } from '../../guidance/index.js';
@@ -45,7 +45,7 @@ function tryWriteSnapshot(options, stage, data) {
45
45
  * Process OpenAI Chat request (messages/tools) with unified 标准 governance.
46
46
  * - Augment tools (strict schemas)
47
47
  * - Inject/Refine system tool guidance (idempotent)
48
- * - Canonicalize textual tool markup to tool_calls; set content=null when applicable
48
+ * - Canonicalize structured tool_calls; set content=null when applicable
49
49
  */
50
50
  const IMAGE_EXT_RE = /\.(png|jpe?g|gif|webp|bmp|svg)(?:[?#].*)?$/i;
51
51
  function hasImageReference(messages) {
@@ -223,7 +223,7 @@ export function processChatRequestTools(request, opts) {
223
223
  }
224
224
  /**
225
225
  * Process OpenAI Chat response (choices[0].message) with unified 标准 governance.
226
- * - Canonicalize textual tool markup to tool_calls; ensure finish_reason and content=null policy
226
+ * - Canonicalize structured tool_calls; ensure finish_reason and content=null policy
227
227
  */
228
228
  import { repairArgumentsToString, parseLenient } from './jsonish.js';
229
229
  export function normalizeApplyPatchToolCallsOnResponse(chat) {
@@ -13,7 +13,7 @@ export class FilterEngine {
13
13
  try {
14
14
  await writeFilterSnapshot({ requestId: ctx.requestId, endpoint: ctx.endpoint, profile: ctx.profile, stage, tag: 'begin', data: out });
15
15
  }
16
- catch { }
16
+ catch { /* ignore */ }
17
17
  // stage pre filters
18
18
  for (const f of this.filters) {
19
19
  if (f.stage !== stage)
@@ -25,7 +25,7 @@ export class FilterEngine {
25
25
  try {
26
26
  await writeFilterSnapshot({ requestId: ctx.requestId, endpoint: ctx.endpoint, profile: ctx.profile, stage, name: f.name, tag: 'after', data: out });
27
27
  }
28
- catch { }
28
+ catch { /* ignore */ }
29
29
  }
30
30
  // field map in map stages
31
31
  if (stage === 'request_map' || stage === 'response_map') {
@@ -33,17 +33,17 @@ export class FilterEngine {
33
33
  try {
34
34
  await writeFilterSnapshot({ requestId: ctx.requestId, endpoint: ctx.endpoint, profile: ctx.profile, stage, tag: 'map_before', data: out });
35
35
  }
36
- catch { }
36
+ catch { /* ignore */ }
37
37
  out = this.applyFieldMap(out, rules);
38
38
  try {
39
39
  await writeFilterSnapshot({ requestId: ctx.requestId, endpoint: ctx.endpoint, profile: ctx.profile, stage, tag: 'map_after', data: out });
40
40
  }
41
- catch { }
41
+ catch { /* ignore */ }
42
42
  }
43
43
  try {
44
44
  await writeFilterSnapshot({ requestId: ctx.requestId, endpoint: ctx.endpoint, profile: ctx.profile, stage, tag: 'end', data: out });
45
45
  }
46
- catch { }
46
+ catch { /* ignore */ }
47
47
  return out;
48
48
  }
49
49
  applyFieldMap(input, rules) {