@jsonstudio/llms 0.6.802 → 0.6.954

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 (188) hide show
  1. package/dist/bridge/routecodex-adapter.d.ts +74 -0
  2. package/dist/config-unified/enhanced-path-resolver.d.ts +5 -0
  3. package/dist/config-unified/unified-config.d.ts +26 -0
  4. package/dist/conversion/codec-registry.d.ts +10 -0
  5. package/dist/conversion/codecs/gemini-openai-codec.d.ts +16 -0
  6. package/dist/conversion/codecs/openai-openai-codec.d.ts +12 -0
  7. package/dist/conversion/codecs/responses-openai-codec.d.ts +12 -0
  8. package/dist/conversion/compat/profiles/chat-gemini.json +12 -0
  9. package/dist/conversion/config/config-manager.d.ts +212 -0
  10. package/dist/conversion/hub/config/types.d.ts +26 -0
  11. package/dist/conversion/hub/core/detour-registry.d.ts +9 -0
  12. package/dist/conversion/hub/core/hub-context.d.ts +21 -0
  13. package/dist/conversion/hub/core/index.d.ts +3 -0
  14. package/dist/conversion/hub/core/stage-driver.d.ts +30 -0
  15. package/dist/conversion/hub/format-adapters/anthropic-format-adapter.d.ts +16 -0
  16. package/dist/conversion/hub/format-adapters/chat-format-adapter.d.ts +17 -0
  17. package/dist/conversion/hub/format-adapters/gemini-format-adapter.d.ts +16 -0
  18. package/dist/conversion/hub/format-adapters/index.d.ts +21 -0
  19. package/dist/conversion/hub/hub-feature.d.ts +1 -0
  20. package/dist/conversion/hub/node-support.d.ts +19 -0
  21. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +11 -0
  22. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
  23. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +7 -0
  24. package/dist/conversion/hub/pipeline/hub-pipeline.js +113 -17
  25. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +6 -3
  26. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +4 -0
  27. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +23 -1
  28. package/dist/conversion/hub/pipelines/inbound.d.ts +22 -0
  29. package/dist/conversion/hub/pipelines/outbound.d.ts +22 -0
  30. package/dist/conversion/hub/policy/policy-engine.d.ts +46 -0
  31. package/dist/conversion/hub/policy/policy-engine.js +176 -0
  32. package/dist/conversion/hub/policy/protocol-spec.d.ts +50 -0
  33. package/dist/conversion/hub/policy/protocol-spec.js +105 -0
  34. package/dist/conversion/hub/process/chat-process.d.ts +32 -0
  35. package/dist/conversion/hub/registry.d.ts +28 -0
  36. package/dist/conversion/hub/response/chat-response-utils.d.ts +6 -0
  37. package/dist/conversion/hub/response/provider-response.js +31 -0
  38. package/dist/conversion/hub/semantic-mappers/chat-mapper.js +32 -1
  39. package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +7 -0
  40. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +96 -1
  41. package/dist/conversion/hub/semantic-mappers/index.d.ts +4 -0
  42. package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +21 -0
  43. package/dist/conversion/hub/standardized-bridge.d.ts +12 -0
  44. package/dist/conversion/hub/types/chat-schema.d.ts +112 -0
  45. package/dist/conversion/hub/types/errors.d.ts +5 -0
  46. package/dist/conversion/hub/types/format-envelope.d.ts +7 -0
  47. package/dist/conversion/hub/types/index.d.ts +6 -0
  48. package/dist/conversion/hub/types/json.d.ts +9 -0
  49. package/dist/conversion/hub/types/node.d.ts +31 -0
  50. package/dist/conversion/responses/responses-openai-bridge.js +263 -10
  51. package/dist/conversion/schema-validator.d.ts +7 -0
  52. package/dist/conversion/shared/args-mapping.d.ts +18 -0
  53. package/dist/conversion/shared/chat-request-filters.d.ts +9 -0
  54. package/dist/conversion/shared/errors.d.ts +1 -1
  55. package/dist/conversion/shared/gemini-tool-utils.js +105 -1
  56. package/dist/conversion/shared/jsonish.d.ts +3 -0
  57. package/dist/conversion/shared/mcp-injection.d.ts +2 -0
  58. package/dist/conversion/shared/media.d.ts +1 -0
  59. package/dist/conversion/shared/openai-message-normalize.d.ts +1 -0
  60. package/dist/conversion/shared/payload-budget.d.ts +13 -0
  61. package/dist/conversion/shared/reasoning-mapping.d.ts +5 -0
  62. package/dist/conversion/shared/responses-request-adapter.d.ts +1 -28
  63. package/dist/conversion/shared/responses-request-adapter.js +1 -430
  64. package/dist/conversion/shared/snapshot-hooks.js +58 -3
  65. package/dist/conversion/shared/tool-governor.js +8 -2
  66. package/dist/conversion/shared/tool-harvester.d.ts +31 -0
  67. package/dist/conversion/shared/tool-mapping.js +10 -29
  68. package/dist/conversion/types.d.ts +33 -0
  69. package/dist/filters/builtin/add-fields-filter.d.ts +8 -0
  70. package/dist/filters/builtin/blacklist-filter.d.ts +8 -0
  71. package/dist/filters/builtin/whitelist-filter.d.ts +8 -0
  72. package/dist/filters/engine.d.ts +16 -0
  73. package/dist/filters/special/request-tool-choice-policy.d.ts +11 -0
  74. package/dist/filters/special/response-finish-invariants.d.ts +11 -0
  75. package/dist/filters/special/response-openai-to-responses-bridge.d.ts +13 -0
  76. package/dist/filters/special/response-tool-arguments-blacklist.d.ts +12 -0
  77. package/dist/filters/special/response-tool-arguments-schema-converge.d.ts +13 -0
  78. package/dist/filters/special/response-tool-arguments-stringify.d.ts +9 -0
  79. package/dist/filters/special/response-tool-arguments-whitelist.d.ts +11 -0
  80. package/dist/filters/special/tool-filter-hooks.d.ts +19 -0
  81. package/dist/filters/special/tool-post-constraints.d.ts +31 -0
  82. package/dist/filters/types.d.ts +68 -0
  83. package/dist/filters/utils/fieldmap-loader.d.ts +2 -0
  84. package/dist/filters/utils/snapshot-writer.d.ts +10 -0
  85. package/dist/guidance/index.d.ts +3 -0
  86. package/dist/guidance/index.js +78 -83
  87. package/dist/http/sse-response.d.ts +22 -0
  88. package/dist/router/virtual-router/bootstrap.d.ts +6 -0
  89. package/dist/router/virtual-router/bootstrap.js +49 -5
  90. package/dist/router/virtual-router/classifier.d.ts +10 -0
  91. package/dist/router/virtual-router/engine-selection.js +98 -11
  92. package/dist/router/virtual-router/engine.js +177 -31
  93. package/dist/router/virtual-router/error-center.d.ts +10 -0
  94. package/dist/router/virtual-router/features.d.ts +3 -0
  95. package/dist/router/virtual-router/routing-instructions.d.ts +23 -1
  96. package/dist/router/virtual-router/routing-instructions.js +120 -30
  97. package/dist/router/virtual-router/types.d.ts +11 -0
  98. package/dist/servertool/engine.js +192 -17
  99. package/dist/servertool/handlers/apply-patch-guard.js +269 -0
  100. package/dist/servertool/handlers/exec-command-guard.js +558 -0
  101. package/dist/servertool/handlers/followup-message-trimmer.d.ts +16 -0
  102. package/dist/servertool/handlers/followup-message-trimmer.js +198 -0
  103. package/dist/servertool/handlers/followup-request-builder.d.ts +17 -0
  104. package/dist/servertool/handlers/followup-request-builder.js +122 -0
  105. package/dist/servertool/handlers/gemini-empty-reply-continue.js +252 -51
  106. package/dist/servertool/handlers/iflow-model-error-retry.js +12 -22
  107. package/dist/servertool/handlers/stop-message-auto.js +237 -75
  108. package/dist/servertool/handlers/vision.js +15 -27
  109. package/dist/servertool/handlers/web-search.js +17 -43
  110. package/dist/servertool/server-side-tools.d.ts +3 -0
  111. package/dist/servertool/server-side-tools.js +3 -0
  112. package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +2 -1
  113. package/dist/sse/json-to-sse/chat-json-to-sse-converter.d.ts +80 -0
  114. package/dist/sse/json-to-sse/event-generators/chat.d.ts +55 -0
  115. package/dist/sse/json-to-sse/event-generators/responses.d.ts +99 -0
  116. package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +2 -1
  117. package/dist/sse/json-to-sse/responses-json-to-sse-converter.d.ts +80 -0
  118. package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +1 -1
  119. package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +2 -2
  120. package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +1 -1
  121. package/dist/sse/json-to-sse/sequencers/responses-sequencer.d.ts +40 -0
  122. package/dist/sse/shared/chat-serializer.d.ts +4 -0
  123. package/dist/sse/shared/constants.d.ts +272 -0
  124. package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +1 -1
  125. package/dist/sse/shared/serializers/base-serializer.d.ts +158 -0
  126. package/dist/sse/shared/serializers/chat-event-serializer.d.ts +82 -0
  127. package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +1 -1
  128. package/dist/sse/shared/serializers/index.d.ts +2 -1
  129. package/dist/sse/shared/serializers/responses-event-serializer.d.ts +123 -0
  130. package/dist/sse/shared/serializers/types.d.ts +51 -0
  131. package/dist/sse/shared/utils.d.ts +254 -0
  132. package/dist/sse/shared/writer.d.ts +2 -2
  133. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -1
  134. package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +1 -1
  135. package/dist/sse/sse-to-json/builders/response-builder.d.ts +1 -1
  136. package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +2 -1
  137. package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +2 -1
  138. package/dist/sse/sse-to-json/parsers/sse-parser.d.ts +73 -0
  139. package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
  140. package/dist/sse/types/chat-types.d.ts +1 -1
  141. package/dist/sse/types/responses-types.d.ts +1 -1
  142. package/dist/tools/apply-patch/execution-capturer.d.ts +13 -0
  143. package/dist/tools/apply-patch/execution-capturer.js +158 -0
  144. package/dist/tools/apply-patch/regression-capturer.d.ts +1 -0
  145. package/dist/tools/apply-patch/regression-capturer.js +5 -4
  146. package/dist/tools/apply-patch/structured.js +109 -13
  147. package/dist/tools/apply-patch/validator.js +112 -18
  148. package/dist/tools/tool-registry.d.ts +8 -0
  149. package/dist/tools/tool-registry.js +2 -1
  150. package/package.json +4 -4
  151. package/dist/conversion/compat/actions/apply-patch-format-fixer.js +0 -233
  152. package/dist/conversion/config/compat-profiles.json +0 -38
  153. package/dist/conversion/hub/pipeline/context-limit.d.ts +0 -13
  154. package/dist/conversion/hub/pipeline/context-limit.js +0 -55
  155. package/dist/conversion/hub/response/server-side-tools.d.ts +0 -26
  156. package/dist/conversion/hub/response/server-side-tools.js +0 -383
  157. package/dist/conversion/shared/bridge-conversation-store.d.ts +0 -41
  158. package/dist/conversion/shared/bridge-conversation-store.js +0 -279
  159. package/dist/conversion/shared/bridge-request-adapter.d.ts +0 -28
  160. package/dist/conversion/shared/bridge-request-adapter.js +0 -430
  161. package/dist/conversion/shared/responses-id-utils.js +0 -42
  162. package/dist/conversion/shared/responses-instructions.js +0 -113
  163. package/dist/conversion/shared/responses-message-utils.d.ts +0 -15
  164. package/dist/conversion/shared/responses-message-utils.js +0 -206
  165. package/dist/conversion/shared/responses-metadata.js +0 -1
  166. package/dist/conversion/shared/responses-output-utils.d.ts +0 -7
  167. package/dist/conversion/shared/responses-output-utils.js +0 -108
  168. package/dist/conversion/shared/responses-types.d.ts +0 -33
  169. package/dist/conversion/shared/tool-normalizers.d.ts +0 -4
  170. package/dist/conversion/shared/tool-normalizers.js +0 -84
  171. package/dist/filters/special/request-streaming-to-nonstreaming.d.ts +0 -13
  172. package/dist/filters/special/request-streaming-to-nonstreaming.js +0 -39
  173. package/dist/filters/special/response-apply-patch-toon-decode.d.ts +0 -23
  174. package/dist/filters/special/response-apply-patch-toon-decode.js +0 -460
  175. package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +0 -10
  176. package/dist/filters/special/response-tool-arguments-toon-decode.js +0 -154
  177. package/dist/servertool/flow-types.d.ts +0 -40
  178. package/dist/servertool/flow-types.js +0 -1
  179. package/dist/servertool/orchestration-types.d.ts +0 -33
  180. package/dist/servertool/orchestration-types.js +0 -1
  181. package/dist/servertool/vision-tool.d.ts +0 -2
  182. package/dist/servertool/vision-tool.js +0 -185
  183. package/dist/tools/patch-args-normalizer.d.ts +0 -15
  184. package/dist/tools/patch-args-normalizer.js +0 -472
  185. package/dist/utils/toon.d.ts +0 -4
  186. package/dist/utils/toon.js +0 -75
  187. /package/dist/{conversion/compat/actions/apply-patch-format-fixer.d.ts → servertool/handlers/apply-patch-guard.d.ts} +0 -0
  188. /package/dist/{conversion/shared/responses-types.js → servertool/handlers/exec-command-guard.d.ts} +0 -0
@@ -424,14 +424,40 @@ export function buildResponsesRequestFromChat(payload, ctx, extras) {
424
424
  else if (isPlainObject(metadataExtraFields?.metadata)) {
425
425
  out.metadata = { ...metadataExtraFields.metadata };
426
426
  }
427
- if (ctx?.parameters && Object.keys(ctx.parameters).length) {
428
- out.parameters = { ...ctx.parameters };
429
- }
430
- else if (chat.parameters && typeof chat.parameters === 'object') {
431
- out.parameters = { ...chat.parameters };
432
- }
433
- else if (isPlainObject(metadataExtraFields?.parameters)) {
434
- out.parameters = { ...metadataExtraFields.parameters };
427
+ // Some upstream `/v1/responses` providers reject an unknown top-level `parameters` object
428
+ // (e.g. HTTP 400: Unsupported parameter: parameters).
429
+ // We accept `parameters` as an internal carrier (capturedChatRequest.parameters, followups),
430
+ // but must flatten supported fields into the top-level request shape before sending upstream.
431
+ const parametersCandidate = (ctx?.parameters && Object.keys(ctx.parameters).length ? { ...ctx.parameters } : undefined) ??
432
+ (chat.parameters && typeof chat.parameters === 'object' && !Array.isArray(chat.parameters) ? { ...chat.parameters } : undefined) ??
433
+ (isPlainObject(metadataExtraFields?.parameters) ? { ...metadataExtraFields.parameters } : undefined);
434
+ if (parametersCandidate) {
435
+ const allowed = new Set([
436
+ // Common OpenAI Responses parameters
437
+ 'temperature',
438
+ 'top_p',
439
+ 'max_output_tokens',
440
+ 'seed',
441
+ 'logit_bias',
442
+ 'user',
443
+ 'parallel_tool_calls',
444
+ 'tool_choice',
445
+ 'response_format',
446
+ 'stream'
447
+ ]);
448
+ // Back-compat: StandardizedRequest uses max_tokens; map it to Responses max_output_tokens.
449
+ if (parametersCandidate.max_output_tokens === undefined && parametersCandidate.max_tokens !== undefined) {
450
+ parametersCandidate.max_output_tokens = parametersCandidate.max_tokens;
451
+ }
452
+ for (const [key, value] of Object.entries(parametersCandidate)) {
453
+ if (!allowed.has(key))
454
+ continue;
455
+ if (key === 'stream')
456
+ continue; // handled by resolvedStream above
457
+ if (out[key] === undefined) {
458
+ out[key] = value;
459
+ }
460
+ }
435
461
  }
436
462
  ensureBridgeInstructions(out);
437
463
  return { request: out, originalSystemMessages };
@@ -590,8 +616,21 @@ export function buildResponsesPayloadFromChat(payload, context) {
590
616
  });
591
617
  }
592
618
  }
593
- catch {
594
- // best-effort bridge policy application
619
+ catch (error) {
620
+ const message = error instanceof Error
621
+ ? error.message
622
+ : typeof error === 'string'
623
+ ? error
624
+ : 'unknown_error';
625
+ try {
626
+ // eslint-disable-next-line no-console
627
+ console.error(`\x1b[31m[responses-bridge][response_outbound] bridge action pipeline failed requestId=${context?.requestId ?? 'unknown'} error=${message}\x1b[0m`);
628
+ }
629
+ catch {
630
+ // ignore logging failures
631
+ }
632
+ // Do not fail-close here: preserve protocol-correct payload shape and let the client
633
+ // handle any downstream validation errors. The bridge policy pipeline is best-effort.
595
634
  }
596
635
  }
597
636
  if (message) {
@@ -625,6 +664,10 @@ export function buildResponsesPayloadFromChat(payload, context) {
625
664
  out.usage = outputBuild.usage;
626
665
  if (outputBuild.requiredAction)
627
666
  out.required_action = outputBuild.requiredAction;
667
+ // Normalize tool_call arguments for client-declared tool formats (e.g. format:"freeform").
668
+ // If we cannot repair/match the declared schema/format, we fall back to returning the
669
+ // original tool-call arguments and let the client surface the error.
670
+ normalizeResponsesToolCallArgumentsForClient(out, context);
628
671
  // Do not inject captured tool results here; keep Chat back-half behavior standard.
629
672
  if (context) {
630
673
  for (const k of ['metadata', 'parallel_tool_calls', 'tool_choice', 'include']) {
@@ -649,6 +692,216 @@ export function buildResponsesPayloadFromChat(payload, context) {
649
692
  }
650
693
  return out;
651
694
  }
695
+ function normalizeResponsesToolCallArgumentsForClient(responsesPayload, context) {
696
+ const toolsRaw = Array.isArray(context?.toolsRaw) ? context?.toolsRaw : [];
697
+ if (!toolsRaw.length) {
698
+ return;
699
+ }
700
+ const toolIndex = buildClientToolIndex(toolsRaw);
701
+ if (!toolIndex.size) {
702
+ return;
703
+ }
704
+ const normalizeCallArgs = (toolName, argsRaw) => {
705
+ const spec = toolIndex.get(toolName);
706
+ if (!spec)
707
+ return argsRaw;
708
+ // format:"freeform" means client expects raw text (not JSON) in the arguments field.
709
+ if (typeof spec.format === 'string' && spec.format.trim().toLowerCase() === 'freeform') {
710
+ const rawText = extractFreeformTextFromArgs(argsRaw);
711
+ if (rawText && rawText.trim().length) {
712
+ return rawText;
713
+ }
714
+ return argsRaw;
715
+ }
716
+ // Schema-aware key alignment for common server-side tools.
717
+ // If we cannot repair, keep original args and let the client surface the error.
718
+ const params = spec.parameters;
719
+ if (!params)
720
+ return argsRaw;
721
+ const schema = extractJsonSchemaLike(params);
722
+ if (!schema)
723
+ return argsRaw;
724
+ const parsed = tryParseJson(argsRaw);
725
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
726
+ return argsRaw;
727
+ }
728
+ const record = parsed;
729
+ const repaired = repairToolArgsBySchemaKeys(toolName, record, schema);
730
+ if (!repaired) {
731
+ return argsRaw;
732
+ }
733
+ try {
734
+ return JSON.stringify(repaired);
735
+ }
736
+ catch {
737
+ return argsRaw;
738
+ }
739
+ };
740
+ // 1) output[] function_call items
741
+ const output = Array.isArray(responsesPayload.output) ? responsesPayload.output : [];
742
+ for (const item of output) {
743
+ if (!item || typeof item !== 'object')
744
+ continue;
745
+ const type = typeof item.type === 'string' ? String(item.type).trim().toLowerCase() : '';
746
+ if (type !== 'function_call')
747
+ continue;
748
+ const name = typeof item.name === 'string' ? String(item.name).trim() : '';
749
+ if (!name)
750
+ continue;
751
+ if (!toolIndex.has(name))
752
+ continue;
753
+ item.arguments = normalizeCallArgs(name, item.arguments);
754
+ }
755
+ // 2) required_action.submit_tool_outputs.tool_calls[] tool calls
756
+ const toolCalls = responsesPayload?.required_action?.submit_tool_outputs?.tool_calls;
757
+ const calls = Array.isArray(toolCalls) ? toolCalls : [];
758
+ for (const call of calls) {
759
+ if (!call || typeof call !== 'object')
760
+ continue;
761
+ const fn = call.function && typeof call.function === 'object' && !Array.isArray(call.function) ? call.function : null;
762
+ const name = typeof fn?.name === 'string' ? String(fn.name).trim() : '';
763
+ if (!name)
764
+ continue;
765
+ if (!toolIndex.has(name))
766
+ continue;
767
+ fn.arguments = normalizeCallArgs(name, fn.arguments);
768
+ }
769
+ }
770
+ function buildClientToolIndex(toolsRaw) {
771
+ const index = new Map();
772
+ for (const tool of toolsRaw) {
773
+ if (!tool || typeof tool !== 'object' || Array.isArray(tool))
774
+ continue;
775
+ const t = tool;
776
+ const fn = t.function && typeof t.function === 'object' && !Array.isArray(t.function) ? t.function : undefined;
777
+ const nameRaw = (fn && typeof fn.name === 'string' ? fn.name : undefined) ?? (typeof t.name === 'string' ? t.name : undefined);
778
+ const name = typeof nameRaw === 'string' ? nameRaw.trim() : '';
779
+ if (!name)
780
+ continue;
781
+ const formatRaw = (typeof t.format === 'string' ? t.format : undefined) ??
782
+ (fn && typeof fn.format === 'string' ? fn.format : undefined);
783
+ const parametersRaw = (fn && fn.parameters && typeof fn.parameters === 'object' && !Array.isArray(fn.parameters) ? fn.parameters : undefined) ??
784
+ (t.parameters && typeof t.parameters === 'object' && !Array.isArray(t.parameters) ? t.parameters : undefined) ??
785
+ undefined;
786
+ index.set(name, {
787
+ name,
788
+ ...(typeof formatRaw === 'string' && formatRaw.trim().length ? { format: formatRaw.trim() } : {}),
789
+ ...(parametersRaw ? { parameters: parametersRaw } : {})
790
+ });
791
+ }
792
+ return index;
793
+ }
794
+ function tryParseJson(value) {
795
+ if (typeof value !== 'string')
796
+ return null;
797
+ const trimmed = value.trim();
798
+ if (!trimmed)
799
+ return null;
800
+ if (!(trimmed.startsWith('{') || trimmed.startsWith('[')))
801
+ return null;
802
+ try {
803
+ return JSON.parse(trimmed);
804
+ }
805
+ catch {
806
+ return null;
807
+ }
808
+ }
809
+ function looksLikeApplyPatchText(value) {
810
+ const trimmed = value.trim();
811
+ if (!trimmed)
812
+ return false;
813
+ return trimmed.includes('*** Begin Patch') && trimmed.includes('*** End Patch');
814
+ }
815
+ function extractFreeformTextFromArgs(argsRaw) {
816
+ if (typeof argsRaw === 'string') {
817
+ const trimmed = argsRaw.trim();
818
+ if (!trimmed)
819
+ return null;
820
+ // If JSON wrapper, extract common fields first.
821
+ const parsed = tryParseJson(trimmed);
822
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
823
+ const rec = parsed;
824
+ for (const key of ['instructions', 'patch', 'input', 'text']) {
825
+ if (typeof rec[key] === 'string' && rec[key].trim().length) {
826
+ return String(rec[key]).trim();
827
+ }
828
+ }
829
+ }
830
+ // If it's already freeform (not a JSON wrapper), keep.
831
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[') && looksLikeApplyPatchText(trimmed)) {
832
+ return trimmed;
833
+ }
834
+ // Unknown freeform: return as-is so client can error.
835
+ return trimmed;
836
+ }
837
+ // If arguments is an object (malformed), try to extract patch-like fields.
838
+ if (argsRaw && typeof argsRaw === 'object' && !Array.isArray(argsRaw)) {
839
+ const rec = argsRaw;
840
+ for (const key of ['instructions', 'patch', 'input', 'text']) {
841
+ if (typeof rec[key] === 'string' && rec[key].trim().length) {
842
+ return String(rec[key]).trim();
843
+ }
844
+ }
845
+ }
846
+ return null;
847
+ }
848
+ function extractJsonSchemaLike(parameters) {
849
+ const required = Array.isArray(parameters.required)
850
+ ? parameters.required.filter((v) => typeof v === 'string' && v.trim().length > 0).map((v) => v.trim())
851
+ : [];
852
+ const props = parameters.properties && typeof parameters.properties === 'object' && !Array.isArray(parameters.properties)
853
+ ? parameters.properties
854
+ : {};
855
+ const properties = Object.keys(props);
856
+ if (!required.length && !properties.length) {
857
+ return null;
858
+ }
859
+ const additionalProperties = typeof parameters.additionalProperties === 'boolean' ? parameters.additionalProperties : true;
860
+ return { required, properties, additionalProperties };
861
+ }
862
+ function repairToolArgsBySchemaKeys(toolName, record, schema) {
863
+ // Apply known alias mappings only if the target keys are present in schema properties.
864
+ const wants = new Set(schema.properties);
865
+ const out = { ...record };
866
+ if (toolName === 'exec_command') {
867
+ if (wants.has('cmd') && out.command !== undefined && out.cmd === undefined)
868
+ out.cmd = out.command;
869
+ if (wants.has('command') && out.cmd !== undefined && out.command === undefined)
870
+ out.command = out.cmd;
871
+ }
872
+ if (toolName === 'write_stdin') {
873
+ if (wants.has('chars') && out.text !== undefined && out.chars === undefined)
874
+ out.chars = out.text;
875
+ if (wants.has('text') && out.chars !== undefined && out.text === undefined)
876
+ out.text = out.chars;
877
+ }
878
+ if (toolName === 'apply_patch') {
879
+ if (wants.has('instructions') && out.instructions === undefined) {
880
+ if (typeof out.patch === 'string' && out.patch.trim().length)
881
+ out.instructions = out.patch;
882
+ else if (typeof out.input === 'string' && out.input.trim().length)
883
+ out.instructions = out.input;
884
+ }
885
+ if (wants.has('patch') && out.patch === undefined) {
886
+ if (typeof out.instructions === 'string' && out.instructions.trim().length)
887
+ out.patch = out.instructions;
888
+ else if (typeof out.input === 'string' && out.input.trim().length)
889
+ out.patch = out.input;
890
+ }
891
+ }
892
+ for (const key of schema.required) {
893
+ if (!Object.prototype.hasOwnProperty.call(out, key)) {
894
+ return null;
895
+ }
896
+ }
897
+ if (schema.additionalProperties === false && schema.properties.length > 0) {
898
+ for (const key of Object.keys(out)) {
899
+ if (!wants.has(key))
900
+ delete out[key];
901
+ }
902
+ }
903
+ return out;
904
+ }
652
905
  function unwrapData(value) {
653
906
  let current = value;
654
907
  const seen = new Set();
@@ -0,0 +1,7 @@
1
+ export declare class SchemaValidator {
2
+ private readonly baseDir;
3
+ private readonly cache;
4
+ constructor(baseDir?: string);
5
+ validate(schemaPath: string | undefined, payload: unknown, context: string): Promise<void>;
6
+ private getValidator;
7
+ }
@@ -0,0 +1,18 @@
1
+ export type Unknown = Record<string, unknown>;
2
+ export interface NormalizeResult<T = Record<string, unknown>> {
3
+ ok: boolean;
4
+ value?: T;
5
+ errors?: string[];
6
+ }
7
+ type JsonSchema = {
8
+ type?: string | string[];
9
+ properties?: Record<string, JsonSchema & {
10
+ ['x-aliases']?: string[];
11
+ }>;
12
+ required?: string[];
13
+ items?: JsonSchema;
14
+ additionalProperties?: boolean;
15
+ };
16
+ export declare function normalizeArgsBySchema(input: any, schema?: JsonSchema): NormalizeResult;
17
+ export declare function normalizeTools(tools: any[]): Unknown[];
18
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { ConversionContext, ConversionProfile } from '../types.js';
2
+ /**
3
+ * 统一的 Chat 请求侧过滤链路。
4
+ *
5
+ * 目标:
6
+ * - 所有进入 Provider 的 Chat 形状请求(无论入口为 /v1/chat、/v1/responses 还是 /v1/messages),
7
+ * 都在这里走同一套工具治理与参数标准化逻辑。
8
+ */
9
+ export declare function runStandardChatRequestFilters(chatRequest: any, profile: ConversionProfile, context: ConversionContext): Promise<any>;
@@ -1,4 +1,4 @@
1
- export type ProviderProtocolErrorCode = 'TOOL_PROTOCOL_ERROR' | 'SSE_DECODE_ERROR' | 'MALFORMED_RESPONSE' | 'MALFORMED_REQUEST' | 'SERVERTOOL_FOLLOWUP_FAILED' | 'SERVERTOOL_TIMEOUT' | 'SERVERTOOL_HANDLER_FAILED';
1
+ export type ProviderProtocolErrorCode = 'TOOL_PROTOCOL_ERROR' | 'SSE_DECODE_ERROR' | 'MALFORMED_RESPONSE' | 'MALFORMED_REQUEST' | 'SERVERTOOL_FOLLOWUP_FAILED' | 'SERVERTOOL_EMPTY_FOLLOWUP' | 'SERVERTOOL_TIMEOUT' | 'SERVERTOOL_HANDLER_FAILED';
2
2
  export type ProviderErrorCategory = 'EXTERNAL_ERROR' | 'TOOL_ERROR' | 'INTERNAL_ERROR';
3
3
  export interface ProviderProtocolErrorOptions {
4
4
  code: ProviderProtocolErrorCode;
@@ -2,6 +2,38 @@ import { jsonClone } from '../hub/types/json.js';
2
2
  function isPlainRecord(value) {
3
3
  return !!value && typeof value === 'object' && !Array.isArray(value);
4
4
  }
5
+ function pickBestSchemaVariant(variants) {
6
+ if (!Array.isArray(variants) || variants.length === 0) {
7
+ return { type: 'object', properties: {} };
8
+ }
9
+ const score = (value) => {
10
+ if (!isPlainRecord(value)) {
11
+ return 0;
12
+ }
13
+ const type = typeof value.type === 'string' ? value.type.trim().toLowerCase() : '';
14
+ // Prefer 'string' to maximize compatibility (can carry arbitrary serialized content),
15
+ // then object/array, then primitives.
16
+ if (type === 'string')
17
+ return 100;
18
+ if (type === 'object')
19
+ return 80;
20
+ if (type === 'array')
21
+ return 60;
22
+ if (type === 'integer' || type === 'number' || type === 'boolean')
23
+ return 50;
24
+ return 10;
25
+ };
26
+ let best = variants[0];
27
+ let bestScore = score(best);
28
+ for (const candidate of variants.slice(1)) {
29
+ const candidateScore = score(candidate);
30
+ if (candidateScore > bestScore) {
31
+ best = candidate;
32
+ bestScore = candidateScore;
33
+ }
34
+ }
35
+ return best;
36
+ }
5
37
  function cloneParameters(value) {
6
38
  if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
7
39
  return value;
@@ -10,6 +42,28 @@ function cloneParameters(value) {
10
42
  return value.map((entry) => cloneParameters(entry));
11
43
  }
12
44
  if (isPlainRecord(value)) {
45
+ // Gemini function_declarations.parameters only support a subset of JSON Schema.
46
+ // Additionally, recent Antigravity/Gemini backends may produce MALFORMED_FUNCTION_CALL
47
+ // when schemas include combinators (oneOf/anyOf/allOf). Prefer a safe single-variant schema.
48
+ const combinator = Array.isArray(value.oneOf)
49
+ ? 'oneOf'
50
+ : Array.isArray(value.anyOf)
51
+ ? 'anyOf'
52
+ : Array.isArray(value.allOf)
53
+ ? 'allOf'
54
+ : undefined;
55
+ if (combinator) {
56
+ const variants = value[combinator];
57
+ const chosen = pickBestSchemaVariant(Array.isArray(variants) ? variants : []);
58
+ const simplified = cloneParameters(chosen);
59
+ // Preserve description if present on the wrapper node.
60
+ if (isPlainRecord(simplified) &&
61
+ typeof value.description === 'string' &&
62
+ typeof simplified.description !== 'string') {
63
+ simplified.description = String(value.description);
64
+ }
65
+ return simplified;
66
+ }
13
67
  const cloned = {};
14
68
  for (const [key, entry] of Object.entries(value)) {
15
69
  // Gemini function_declarations.parameters only support a subset of JSON Schema.
@@ -19,6 +73,13 @@ function cloneParameters(value) {
19
73
  continue;
20
74
  if (key === 'exclusiveMinimum' || key === 'exclusiveMaximum' || key === 'propertyNames')
21
75
  continue;
76
+ // Keep Gemini tool schemas mostly permissive to avoid upstream MALFORMED_FUNCTION_CALL on strict validation.
77
+ // We selectively re-introduce safe required fields for critical tools in `buildGeminiToolsFromBridge`.
78
+ if (key === 'required' || key === 'additionalProperties')
79
+ continue;
80
+ // Combinators are handled at the node level above.
81
+ if (key === 'oneOf' || key === 'anyOf' || key === 'allOf')
82
+ continue;
22
83
  }
23
84
  cloned[key] = cloneParameters(entry);
24
85
  }
@@ -105,6 +166,49 @@ export function buildGeminiToolsFromBridge(defs) {
105
166
  return undefined;
106
167
  }
107
168
  const tools = [];
169
+ const applyFixups = (name, parameters) => {
170
+ if (!parameters || typeof parameters !== 'object' || Array.isArray(parameters)) {
171
+ return parameters;
172
+ }
173
+ const params = parameters;
174
+ const propsRaw = params.properties;
175
+ const props = isPlainRecord(propsRaw) ? propsRaw : {};
176
+ const lowered = String(name || '').trim().toLowerCase();
177
+ if (lowered === 'exec_command') {
178
+ // Keep Gemini tool schema aligned with historical functionCall args. We observed large
179
+ // conversations containing exec_command args as { command, workdir } and strict Gemini
180
+ // validation will emit MALFORMED_FUNCTION_CALL when schema/args mismatch.
181
+ if (!Object.prototype.hasOwnProperty.call(props, 'command') && Object.prototype.hasOwnProperty.call(props, 'cmd')) {
182
+ props.command = props.cmd;
183
+ try {
184
+ delete props.cmd;
185
+ }
186
+ catch {
187
+ // ignore
188
+ }
189
+ }
190
+ if (!Object.prototype.hasOwnProperty.call(props, 'command')) {
191
+ props.command = { type: 'string' };
192
+ }
193
+ params.properties = props;
194
+ params.required = ['command'];
195
+ return params;
196
+ }
197
+ if (lowered === 'write_stdin') {
198
+ params.required = ['session_id'];
199
+ return params;
200
+ }
201
+ if (lowered === 'apply_patch') {
202
+ // Force a non-empty patch payload; leaving the schema fully permissive results in repeated `{}` tool calls.
203
+ if (!Object.prototype.hasOwnProperty.call(props, 'patch')) {
204
+ props.patch = { type: 'string' };
205
+ }
206
+ params.properties = props;
207
+ params.required = ['patch'];
208
+ return params;
209
+ }
210
+ return params;
211
+ };
108
212
  defs.forEach((def) => {
109
213
  if (!def || typeof def !== 'object') {
110
214
  return;
@@ -123,7 +227,7 @@ export function buildGeminiToolsFromBridge(defs) {
123
227
  : typeof def.description === 'string'
124
228
  ? def.description
125
229
  : undefined;
126
- const parameters = cloneParameters(fnNode?.parameters ?? def.parameters ?? { type: 'object', properties: {} });
230
+ const parameters = applyFixups(name, cloneParameters(fnNode?.parameters ?? def.parameters ?? { type: 'object', properties: {} }));
127
231
  tools.push({
128
232
  functionDeclarations: [
129
233
  {
@@ -0,0 +1,3 @@
1
+ export declare function tryParseJson<T = unknown>(s: unknown): T | unknown;
2
+ export declare function parseLenient(value: unknown): unknown;
3
+ export declare function repairArgumentsToString(value: unknown): string;
@@ -0,0 +1,2 @@
1
+ export declare function injectMcpToolsForChat(tools: any[] | undefined, discoveredServers: string[]): any[];
2
+ export declare function injectMcpToolsForResponses(tools: any[] | undefined, discoveredServers: string[]): any[];
@@ -0,0 +1 @@
1
+ export declare function isImagePath(p: unknown): boolean;
@@ -0,0 +1 @@
1
+ export declare function normalizeChatRequest(request: any): any;
@@ -0,0 +1,13 @@
1
+ export declare function resolveBudgetForModelSync(modelId: string): {
2
+ maxBytes: number;
3
+ safetyRatio: number;
4
+ allowedBytes: number;
5
+ source: string;
6
+ };
7
+ export declare const resolveBudgetForModel: (modelId: string) => Promise<{
8
+ maxBytes: number;
9
+ safetyRatio: number;
10
+ allowedBytes: number;
11
+ source: string;
12
+ }>;
13
+ export declare function enforceChatBudget(chat: any, modelId: string): any;
@@ -0,0 +1,5 @@
1
+ export interface ReasoningItem {
2
+ type: 'reasoning';
3
+ content: string;
4
+ }
5
+ export declare function mapReasoningContentToResponsesOutput(reasoningContent: any): ReasoningItem[];
@@ -1,28 +1 @@
1
- import type { BridgeInputItem, BridgeToolDefinition } from './bridge-message-types.js';
2
- export interface ResponsesRequestContext {
3
- requestId?: string;
4
- instructions?: string;
5
- originalSystemMessages?: string[];
6
- input?: BridgeInputItem[];
7
- include?: unknown;
8
- store?: unknown;
9
- toolChoice?: unknown;
10
- parallelToolCalls?: boolean;
11
- metadata?: Record<string, unknown>;
12
- responseFormat?: unknown;
13
- toolsRaw?: BridgeToolDefinition[];
14
- stream?: boolean;
15
- instructionsIsRaw?: boolean;
16
- isChatPayload?: boolean;
17
- isResponsesPayload?: boolean;
18
- }
19
- export interface BuildChatRequestResult {
20
- request: Record<string, unknown>;
21
- toolsNormalized?: Array<Record<string, unknown>>;
22
- }
23
- export declare function captureResponsesContext(payload: Record<string, unknown>, dto?: {
24
- route?: {
25
- requestId?: string;
26
- };
27
- }): ResponsesRequestContext;
28
- export declare function buildChatRequestFromResponses(payload: Record<string, unknown>, context: ResponsesRequestContext): BuildChatRequestResult;
1
+ export { captureResponsesContext, buildChatRequestFromResponses } from '../responses/responses-openai-bridge.js';