@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
@@ -0,0 +1,105 @@
1
+ const RESPONSES_SPEC = {
2
+ id: 'openai-responses',
3
+ providerOutbound: {
4
+ enforceEnabled: true,
5
+ forbidWrappers: [
6
+ {
7
+ code: 'forbid_wrapper',
8
+ path: 'parameters',
9
+ detail: 'Responses provider payload must not contain a top-level parameters wrapper (expects flattened fields).'
10
+ },
11
+ {
12
+ code: 'forbid_wrapper',
13
+ path: 'request',
14
+ detail: 'Responses provider payload must not contain a nested request wrapper.'
15
+ }
16
+ ],
17
+ reservedKeyPrefixes: ['__', '_'],
18
+ flattenWrappers: [
19
+ {
20
+ wrapperKey: 'request',
21
+ onlyIfTargetMissing: true
22
+ },
23
+ {
24
+ wrapperKey: 'parameters',
25
+ onlyIfTargetMissing: true,
26
+ aliasKeys: {
27
+ max_tokens: 'max_output_tokens'
28
+ },
29
+ allowKeys: [
30
+ 'temperature',
31
+ 'top_p',
32
+ 'max_output_tokens',
33
+ 'seed',
34
+ 'logit_bias',
35
+ 'user',
36
+ 'parallel_tool_calls',
37
+ 'tool_choice',
38
+ 'response_format',
39
+ 'stream',
40
+ 'stop',
41
+ 'stop_sequences',
42
+ 'modalities',
43
+ 'top_k'
44
+ ]
45
+ }
46
+ ]
47
+ }
48
+ };
49
+ const DEFAULT_SPEC = {
50
+ id: 'openai-chat',
51
+ providerOutbound: {
52
+ enforceEnabled: false,
53
+ forbidWrappers: [
54
+ {
55
+ code: 'forbid_wrapper',
56
+ path: 'parameters',
57
+ detail: 'OpenAI Chat provider payload must not contain a top-level parameters wrapper (expects flattened fields).'
58
+ },
59
+ {
60
+ code: 'forbid_wrapper',
61
+ path: 'request',
62
+ detail: 'OpenAI Chat provider payload must not contain a nested request wrapper.'
63
+ }
64
+ ],
65
+ reservedKeyPrefixes: ['__', '_'],
66
+ flattenWrappers: []
67
+ }
68
+ };
69
+ export const HUB_PROTOCOL_SPECS = {
70
+ 'openai-chat': DEFAULT_SPEC,
71
+ 'openai-responses': RESPONSES_SPEC,
72
+ 'anthropic-messages': {
73
+ id: 'anthropic-messages',
74
+ providerOutbound: {
75
+ enforceEnabled: false,
76
+ forbidWrappers: [
77
+ {
78
+ code: 'forbid_wrapper',
79
+ path: 'parameters',
80
+ detail: 'Anthropic Messages provider payload must not contain a top-level parameters wrapper.'
81
+ },
82
+ {
83
+ code: 'forbid_wrapper',
84
+ path: 'request',
85
+ detail: 'Anthropic Messages provider payload must not contain a nested request wrapper.'
86
+ }
87
+ ],
88
+ reservedKeyPrefixes: ['__', '_'],
89
+ flattenWrappers: []
90
+ }
91
+ },
92
+ 'gemini-chat': {
93
+ id: 'gemini-chat',
94
+ providerOutbound: {
95
+ enforceEnabled: false,
96
+ forbidWrappers: [],
97
+ reservedKeyPrefixes: ['__', '_'],
98
+ flattenWrappers: []
99
+ }
100
+ }
101
+ };
102
+ export function resolveHubProtocolSpec(protocol) {
103
+ const normalized = (protocol || '').trim().toLowerCase();
104
+ return HUB_PROTOCOL_SPECS[normalized] ?? DEFAULT_SPEC;
105
+ }
@@ -0,0 +1,32 @@
1
+ import type { ProcessedRequest, StandardizedRequest } from '../types/standardized.js';
2
+ import type { JsonObject } from '../types/json.js';
3
+ export interface HubChatProcessOptions {
4
+ request: StandardizedRequest;
5
+ requestId: string;
6
+ entryEndpoint: string;
7
+ rawPayload: Record<string, unknown>;
8
+ metadata: Record<string, unknown>;
9
+ }
10
+ export interface HubChatProcessResult {
11
+ processedRequest?: ProcessedRequest;
12
+ nodeResult: HubProcessNodeResult;
13
+ }
14
+ export interface HubProcessNodeResult {
15
+ success: boolean;
16
+ metadata: {
17
+ node: string;
18
+ executionTime: number;
19
+ startTime: number;
20
+ endTime: number;
21
+ dataProcessed?: {
22
+ messages: number;
23
+ tools: number;
24
+ };
25
+ };
26
+ error?: {
27
+ code?: string;
28
+ message: string;
29
+ details?: JsonObject;
30
+ };
31
+ }
32
+ export declare function runHubChatProcess(options: HubChatProcessOptions): Promise<HubChatProcessResult>;
@@ -0,0 +1,28 @@
1
+ import type { InboundPlan, InboundPassthroughConfig, InboundStage } from './pipelines/inbound.js';
2
+ import type { OutboundPlan, OutboundPassthroughConfig, OutboundStage } from './pipelines/outbound.js';
3
+ import type { FormatAdapter, SemanticMapper } from './format-adapters/index.js';
4
+ interface InboundPlanFactory {
5
+ protocol: string;
6
+ stages: InboundStage[];
7
+ passthrough?: InboundPassthroughConfig;
8
+ createFormatAdapter?: () => FormatAdapter;
9
+ createSemanticMapper?: () => SemanticMapper;
10
+ }
11
+ interface OutboundPlanFactory {
12
+ protocol: string;
13
+ stages: OutboundStage[];
14
+ passthrough?: OutboundPassthroughConfig;
15
+ createFormatAdapter?: () => FormatAdapter;
16
+ createSemanticMapper?: () => SemanticMapper;
17
+ }
18
+ export interface HubProtocolDefinition {
19
+ protocol: string;
20
+ inbound: InboundPlanFactory;
21
+ outbound: OutboundPlanFactory;
22
+ }
23
+ export declare const HUB_PROTOCOL_REGISTRY: Record<string, HubProtocolDefinition>;
24
+ export declare function createProtocolPlans(protocol: string): {
25
+ inbound: InboundPlan;
26
+ outbound: OutboundPlan;
27
+ };
28
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { ProcessedRequest } from '../types/standardized.js';
2
+ interface BuildProcessedRequestOptions {
3
+ stream?: boolean;
4
+ }
5
+ export declare function buildProcessedRequestFromChatResponse(chatResponse: any, options?: BuildProcessedRequestOptions): ProcessedRequest;
6
+ export {};
@@ -1,4 +1,5 @@
1
1
  import { recordStage } from '../pipeline/stages/utils.js';
2
+ import { recordHubPolicyObservation } from '../policy/policy-engine.js';
2
3
  import { ChatFormatAdapter } from '../format-adapters/chat-format-adapter.js';
3
4
  import { ResponsesFormatAdapter } from '../format-adapters/responses-format-adapter.js';
4
5
  import { AnthropicFormatAdapter } from '../format-adapters/anthropic-format-adapter.js';
@@ -141,6 +142,21 @@ export async function convertProviderResponse(options) {
141
142
  adapterContext: options.context,
142
143
  stageRecorder: options.stageRecorder
143
144
  });
145
+ // Phase 0/1: observe provider inbound payload violations (best-effort; no rewrites here).
146
+ try {
147
+ if (formatEnvelope.payload && typeof formatEnvelope.payload === 'object' && !Array.isArray(formatEnvelope.payload)) {
148
+ recordHubPolicyObservation({
149
+ phase: 'provider_inbound',
150
+ providerProtocol: options.providerProtocol,
151
+ payload: formatEnvelope.payload,
152
+ stageRecorder: options.stageRecorder,
153
+ requestId: options.context.requestId
154
+ });
155
+ }
156
+ }
157
+ catch {
158
+ // never break response conversion
159
+ }
144
160
  const chatResponse = await runRespInboundStage3SemanticMap({
145
161
  adapterContext: options.context,
146
162
  formatEnvelope,
@@ -218,6 +234,21 @@ export async function convertProviderResponse(options) {
218
234
  stageRecorder: options.stageRecorder
219
235
  });
220
236
  applyModelOverride(clientPayload, displayModel);
237
+ // Phase 0/1: observe client outbound payload violations (best-effort; no rewrites here).
238
+ try {
239
+ if (clientPayload && typeof clientPayload === 'object' && !Array.isArray(clientPayload)) {
240
+ recordHubPolicyObservation({
241
+ phase: 'client_outbound',
242
+ providerProtocol: clientProtocol,
243
+ payload: clientPayload,
244
+ stageRecorder: options.stageRecorder,
245
+ requestId: options.context.requestId
246
+ });
247
+ }
248
+ }
249
+ catch {
250
+ // never break response conversion
251
+ }
221
252
  const outbound = await runRespOutboundStage2SseStream({
222
253
  clientPayload,
223
254
  clientProtocol,
@@ -59,6 +59,25 @@ function normalizeToolContent(content) {
59
59
  return String(content ?? '');
60
60
  }
61
61
  }
62
+ function maybeAugmentRouteCodexApplyPatchPrecheck(content) {
63
+ if (!content || typeof content !== 'string') {
64
+ return content;
65
+ }
66
+ if (content.includes('[RouteCodex precheck]')) {
67
+ return content;
68
+ }
69
+ const lower = content.toLowerCase();
70
+ if (!lower.includes('failed to parse function arguments')) {
71
+ return content;
72
+ }
73
+ if (content.includes('missing field `input`')) {
74
+ return `${content}\n\n[RouteCodex precheck] apply_patch 参数解析失败:缺少字段 "input"。当前 RouteCodex 期望 { input, patch } 形态,并且两个字段都应包含完整统一 diff 文本。`;
75
+ }
76
+ if (content.includes('invalid type: map, expected a string')) {
77
+ return `${content}\n\n[RouteCodex precheck] apply_patch 参数类型错误:检测到 JSON 对象(map),但客户端期望字符串。请先对参数做 JSON.stringify 再写入 arguments,或直接提供 { patch: "<统一 diff>" } 形式。`;
78
+ }
79
+ return content;
80
+ }
62
81
  export function maybeAugmentApplyPatchErrorContent(content, toolName) {
63
82
  if (!content)
64
83
  return content;
@@ -175,9 +194,21 @@ function normalizeChatMessages(raw) {
175
194
  return;
176
195
  }
177
196
  const nameValue = typeof value.name === 'string' && value.name.trim().length ? value.name : undefined;
197
+ const normalizedToolOutput = normalizeToolContent(value.content ?? value.output);
198
+ const routeCodexPrechecked = maybeAugmentRouteCodexApplyPatchPrecheck(normalizedToolOutput);
199
+ if (routeCodexPrechecked !== normalizedToolOutput) {
200
+ // Keep tool role message content aligned with outbound provider requests (e.g. Chat→Responses),
201
+ // while avoiding double-injection.
202
+ if (typeof chatMessage.content === 'string' || chatMessage.content === undefined || chatMessage.content === null) {
203
+ chatMessage.content = routeCodexPrechecked;
204
+ }
205
+ else if (typeof chatMessage.output === 'string') {
206
+ chatMessage.output = routeCodexPrechecked;
207
+ }
208
+ }
178
209
  const outputEntry = {
179
210
  tool_call_id: toolCallId,
180
- content: normalizeToolContent(value.content ?? value.output),
211
+ content: routeCodexPrechecked,
181
212
  name: nameValue
182
213
  };
183
214
  outputEntry.content = maybeAugmentApplyPatchErrorContent(outputEntry.content, outputEntry.name);
@@ -0,0 +1,7 @@
1
+ import type { SemanticMapper } from '../format-adapters/index.js';
2
+ import type { AdapterContext, ChatEnvelope } from '../types/chat-envelope.js';
3
+ import type { FormatEnvelope } from '../types/format-envelope.js';
4
+ export declare class GeminiSemanticMapper implements SemanticMapper {
5
+ toChat(format: FormatEnvelope, ctx: AdapterContext): Promise<ChatEnvelope>;
6
+ fromChat(chat: ChatEnvelope, ctx: AdapterContext): Promise<FormatEnvelope>;
7
+ }
@@ -387,6 +387,8 @@ function buildGeminiRequestFromChat(chat, metadata) {
387
387
  const omitFunctionCallPartsForCli = providerIdPrefix === 'gemini-cli';
388
388
  const semanticsNode = readGeminiSemantics(chat);
389
389
  const systemTextBlocksFromSemantics = readSystemTextBlocksFromSemantics(chat);
390
+ const bridgeDefs = chat.tools && chat.tools.length ? mapChatToolsToBridge(chat.tools) : undefined;
391
+ const toolSchemaKeys = bridgeDefs ? buildToolSchemaKeyMap(bridgeDefs) : new Map();
390
392
  const sourceMessages = chat.messages;
391
393
  // 收集当前 ChatEnvelope 中 assistant/tool_calls 的 id,用于过滤孤立的 tool_result:
392
394
  // 只有在本轮对话中存在对应 tool_call 的 tool_result 才允许映射为 Gemini functionResponse。
@@ -449,6 +451,7 @@ function buildGeminiRequestFromChat(chat, metadata) {
449
451
  else {
450
452
  argsStruct = fn.arguments ?? {};
451
453
  }
454
+ argsStruct = alignToolCallArgsToSchema({ toolName: name, args: argsStruct, schemaKeys: toolSchemaKeys });
452
455
  let argsJson = cloneAsJsonValue(argsStruct);
453
456
  // Gemini / Antigravity 期望 functionCall.args 为对象(Struct),
454
457
  // 若顶层为数组或原始类型,则包装到 value 字段下,避免产生非法的 list 形状。
@@ -514,7 +517,6 @@ function buildGeminiRequestFromChat(chat, metadata) {
514
517
  }
515
518
  }
516
519
  if (chat.tools && chat.tools.length) {
517
- const bridgeDefs = mapChatToolsToBridge(chat.tools);
518
520
  const geminiTools = buildGeminiToolsFromBridge(bridgeDefs);
519
521
  if (geminiTools) {
520
522
  request.tools = geminiTools;
@@ -577,6 +579,99 @@ function buildGeminiRequestFromChat(chat, metadata) {
577
579
  const compatRequest = applyClaudeThinkingToolSchemaCompat(request, adapterContext);
578
580
  return compatRequest;
579
581
  }
582
+ function isPlainRecord(value) {
583
+ return !!value && typeof value === 'object' && !Array.isArray(value);
584
+ }
585
+ function buildToolSchemaKeyMap(defs) {
586
+ const map = new Map();
587
+ for (const def of defs) {
588
+ const fnNode = def && typeof def === 'object' && def.function && typeof def.function === 'object'
589
+ ? def.function
590
+ : undefined;
591
+ const name = typeof fnNode?.name === 'string'
592
+ ? fnNode.name
593
+ : typeof def?.name === 'string'
594
+ ? String(def.name)
595
+ : '';
596
+ if (!name || !name.trim())
597
+ continue;
598
+ const parameters = (fnNode && fnNode.parameters) ??
599
+ (def.parameters);
600
+ if (!isPlainRecord(parameters))
601
+ continue;
602
+ const props = parameters.properties;
603
+ if (!isPlainRecord(props))
604
+ continue;
605
+ const keys = Object.keys(props).filter((k) => typeof k === 'string' && k.trim().length > 0);
606
+ if (!keys.length)
607
+ continue;
608
+ map.set(name, new Set(keys));
609
+ }
610
+ return map;
611
+ }
612
+ function alignToolCallArgsToSchema(options) {
613
+ const name = typeof options.toolName === 'string' ? options.toolName.trim() : '';
614
+ if (!name)
615
+ return options.args;
616
+ const schema = options.schemaKeys.get(name);
617
+ if (!schema || schema.size === 0) {
618
+ return options.args;
619
+ }
620
+ if (!isPlainRecord(options.args)) {
621
+ return options.args;
622
+ }
623
+ const lowered = name.toLowerCase();
624
+ const next = { ...options.args };
625
+ // Align historical Codex tool args to the *declared schema* for Gemini.
626
+ // Gemini validates historical functionCall.args against tool declarations, so mismatches like:
627
+ // - exec_command: { cmd } vs schema { command } (or vice-versa)
628
+ // - apply_patch: { patch/input } vs schema { instructions } (or vice-versa)
629
+ // can cause MALFORMED_FUNCTION_CALL and empty responses.
630
+ if (lowered === 'exec_command') {
631
+ // Prefer the declared schema key; do not delete keys blindly.
632
+ if (schema.has('cmd') && !Object.prototype.hasOwnProperty.call(next, 'cmd') && Object.prototype.hasOwnProperty.call(next, 'command')) {
633
+ next.cmd = next.command;
634
+ }
635
+ if (schema.has('command') && !Object.prototype.hasOwnProperty.call(next, 'command') && Object.prototype.hasOwnProperty.call(next, 'cmd')) {
636
+ next.command = next.cmd;
637
+ }
638
+ }
639
+ else if (lowered === 'write_stdin') {
640
+ if (schema.has('chars') && !Object.prototype.hasOwnProperty.call(next, 'chars') && Object.prototype.hasOwnProperty.call(next, 'text')) {
641
+ next.chars = next.text;
642
+ }
643
+ if (schema.has('text') && !Object.prototype.hasOwnProperty.call(next, 'text') && Object.prototype.hasOwnProperty.call(next, 'chars')) {
644
+ next.text = next.chars;
645
+ }
646
+ }
647
+ else if (lowered === 'apply_patch') {
648
+ if (schema.has('instructions') && !Object.prototype.hasOwnProperty.call(next, 'instructions')) {
649
+ const patch = typeof next.patch === 'string' ? next.patch : undefined;
650
+ const input = typeof next.input === 'string' ? next.input : undefined;
651
+ const candidate = patch && patch.trim().length ? patch : input && input.trim().length ? input : undefined;
652
+ if (candidate) {
653
+ next.instructions = candidate;
654
+ }
655
+ }
656
+ if (schema.has('patch') && !Object.prototype.hasOwnProperty.call(next, 'patch')) {
657
+ const input = typeof next.input === 'string' ? next.input : undefined;
658
+ if (input && input.trim().length) {
659
+ next.patch = input;
660
+ }
661
+ }
662
+ }
663
+ // Prune to schema keys for known Codex tools to reduce strict upstream validation failures.
664
+ if (lowered === 'exec_command' || lowered === 'write_stdin' || lowered === 'apply_patch') {
665
+ const pruned = {};
666
+ for (const key of schema) {
667
+ if (Object.prototype.hasOwnProperty.call(next, key)) {
668
+ pruned[key] = next[key];
669
+ }
670
+ }
671
+ return pruned;
672
+ }
673
+ return next;
674
+ }
580
675
  function buildGenerationConfigFromParameters(parameters) {
581
676
  const config = {};
582
677
  for (const { source, target } of GENERATION_CONFIG_KEYS) {
@@ -0,0 +1,4 @@
1
+ export { ChatSemanticMapper } from './chat-mapper.js';
2
+ export { AnthropicSemanticMapper } from './anthropic-mapper.js';
3
+ export { ResponsesSemanticMapper } from './responses-mapper.js';
4
+ export { GeminiSemanticMapper } from './gemini-mapper.js';
@@ -0,0 +1,21 @@
1
+ import type { SemanticMapper } from '../format-adapters/index.js';
2
+ import type { AdapterContext, ChatEnvelope } from '../types/chat-envelope.js';
3
+ import type { FormatEnvelope } from '../types/format-envelope.js';
4
+ import { type JsonObject, type JsonValue } from '../types/json.js';
5
+ interface ResponsesToolOutputEntry extends JsonObject {
6
+ tool_call_id?: string;
7
+ call_id?: string;
8
+ id?: string;
9
+ output?: JsonValue;
10
+ name?: string;
11
+ }
12
+ interface ResponsesPayload extends JsonObject {
13
+ input?: JsonValue[];
14
+ tools?: JsonValue[];
15
+ tool_outputs?: ResponsesToolOutputEntry[];
16
+ }
17
+ export declare class ResponsesSemanticMapper implements SemanticMapper {
18
+ toChat(format: FormatEnvelope<ResponsesPayload>, ctx: AdapterContext): Promise<ChatEnvelope>;
19
+ fromChat(chat: ChatEnvelope, ctx: AdapterContext): Promise<FormatEnvelope>;
20
+ }
21
+ export {};
@@ -0,0 +1,12 @@
1
+ import type { StandardizedRequest } from './types/standardized.js';
2
+ import type { AdapterContext, ChatEnvelope } from './types/chat-envelope.js';
3
+ export interface ChatToStandardizedOptions {
4
+ adapterContext: AdapterContext;
5
+ endpoint: string;
6
+ requestId?: string;
7
+ }
8
+ export interface StandardizedToChatOptions {
9
+ adapterContext: AdapterContext;
10
+ }
11
+ export declare function chatEnvelopeToStandardized(chat: ChatEnvelope, options: ChatToStandardizedOptions): StandardizedRequest;
12
+ export declare function standardizedToChatEnvelope(request: StandardizedRequest, options: StandardizedToChatOptions): ChatEnvelope;
@@ -0,0 +1,112 @@
1
+ import type { JsonObject, JsonValue } from './json.js';
2
+ export type ChatHubVersion = 'chat-hub@1';
3
+ export interface ConversationReference {
4
+ readonly id?: string;
5
+ readonly parentIds?: readonly string[];
6
+ readonly metadata?: JsonObject;
7
+ }
8
+ export interface InstructionSegment {
9
+ readonly id?: string;
10
+ readonly source: string;
11
+ readonly content: string;
12
+ readonly metadata?: JsonObject;
13
+ }
14
+ export interface InstructionsEnvelope {
15
+ readonly segments: readonly InstructionSegment[];
16
+ }
17
+ export type MessageRole = 'system' | 'user' | 'assistant' | 'tool';
18
+ interface BaseContentBlock {
19
+ readonly type: string;
20
+ readonly metadata?: JsonObject;
21
+ }
22
+ export interface TextContentBlock extends BaseContentBlock {
23
+ readonly type: 'text';
24
+ readonly text: string;
25
+ readonly annotations?: readonly JsonObject[];
26
+ }
27
+ export interface ImageContentBlock extends BaseContentBlock {
28
+ readonly type: 'image';
29
+ readonly mimeType?: string;
30
+ readonly data?: string;
31
+ readonly uri?: string;
32
+ }
33
+ export interface AudioContentBlock extends BaseContentBlock {
34
+ readonly type: 'audio';
35
+ readonly mimeType?: string;
36
+ readonly data?: string;
37
+ readonly uri?: string;
38
+ }
39
+ export interface DataContentBlock extends BaseContentBlock {
40
+ readonly type: 'data';
41
+ readonly mimeType?: string;
42
+ readonly value: JsonValue;
43
+ }
44
+ export interface ToolCallContentBlock extends BaseContentBlock {
45
+ readonly type: 'tool_call';
46
+ readonly toolCallId: string;
47
+ readonly name: string;
48
+ readonly arguments: JsonObject;
49
+ }
50
+ export interface ToolResultContentBlock extends BaseContentBlock {
51
+ readonly type: 'tool_result';
52
+ readonly toolCallId: string;
53
+ readonly output: readonly TextContentBlock[];
54
+ readonly isError?: boolean;
55
+ }
56
+ export type ChatContentBlock = TextContentBlock | ImageContentBlock | AudioContentBlock | DataContentBlock | ToolCallContentBlock | ToolResultContentBlock;
57
+ export interface ChatMessage {
58
+ readonly id?: string;
59
+ readonly role: MessageRole;
60
+ readonly name?: string;
61
+ readonly content: readonly ChatContentBlock[];
62
+ readonly metadata?: JsonObject;
63
+ }
64
+ export interface ChatToolDefinition {
65
+ readonly id?: string;
66
+ readonly type: 'function' | string;
67
+ readonly name: string;
68
+ readonly description?: string;
69
+ readonly parameters?: JsonObject;
70
+ readonly strict?: boolean;
71
+ readonly metadata?: JsonObject;
72
+ }
73
+ export interface ChatAttachment {
74
+ readonly id: string;
75
+ readonly kind: 'file' | 'image' | 'audio';
76
+ readonly uri?: string;
77
+ readonly mimeType?: string;
78
+ readonly metadata?: JsonObject;
79
+ }
80
+ export interface ChatGenerationConfig {
81
+ readonly temperature?: number;
82
+ readonly topP?: number;
83
+ readonly topK?: number;
84
+ readonly maxOutputTokens?: number;
85
+ readonly stopSequences?: readonly string[];
86
+ readonly stream?: boolean;
87
+ readonly parallelToolCalls?: boolean;
88
+ readonly responseFormat?: JsonObject;
89
+ readonly metadata?: JsonObject;
90
+ }
91
+ export interface DetourEntry {
92
+ readonly path: string;
93
+ readonly value?: JsonValue;
94
+ readonly reason: string;
95
+ readonly stage: string;
96
+ }
97
+ export interface DetourState {
98
+ readonly inbound: readonly DetourEntry[];
99
+ readonly outbound: readonly DetourEntry[];
100
+ }
101
+ export interface ChatHubEnvelope {
102
+ readonly version: ChatHubVersion;
103
+ readonly conversation?: ConversationReference;
104
+ readonly instructions?: InstructionsEnvelope;
105
+ readonly messages: readonly ChatMessage[];
106
+ readonly tools?: readonly ChatToolDefinition[];
107
+ readonly config?: ChatGenerationConfig;
108
+ readonly attachments?: readonly ChatAttachment[];
109
+ readonly detours?: DetourState;
110
+ readonly metadata?: JsonObject;
111
+ }
112
+ export {};
@@ -0,0 +1,5 @@
1
+ export declare class ConversionError extends Error {
2
+ readonly nodeId?: string;
3
+ readonly stage?: string;
4
+ constructor(message: string, nodeId?: string, stage?: string, cause?: Error);
5
+ }
@@ -0,0 +1,7 @@
1
+ import type { JsonObject } from './json.js';
2
+ export interface FormatEnvelope<TPayload extends JsonObject = JsonObject> {
3
+ protocol: string;
4
+ direction: 'request' | 'response';
5
+ payload: TPayload;
6
+ meta?: JsonObject;
7
+ }
@@ -0,0 +1,6 @@
1
+ export * from './chat-envelope.js';
2
+ export * from './format-envelope.js';
3
+ export * from './json.js';
4
+ export * from './standardized.js';
5
+ export * from './errors.js';
6
+ export * from './node.js';
@@ -0,0 +1,9 @@
1
+ export type JsonPrimitive = string | number | boolean | null;
2
+ export type JsonValue = JsonPrimitive | JsonValue[] | JsonObject;
3
+ export interface JsonObject {
4
+ [key: string]: JsonValue;
5
+ }
6
+ export type JsonArray = JsonValue[];
7
+ export declare function isJsonObject(value: JsonValue | null | undefined): value is JsonObject;
8
+ export declare function isJsonArray(value: JsonValue | null | undefined): value is JsonArray;
9
+ export declare function jsonClone<T extends JsonValue>(value: T): T;
@@ -0,0 +1,31 @@
1
+ import type { JsonObject } from './json.js';
2
+ export interface HubNodeRequestContext {
3
+ id: string;
4
+ endpoint: string;
5
+ context?: Record<string, unknown>;
6
+ }
7
+ export interface HubNodeContext {
8
+ request: HubNodeRequestContext;
9
+ }
10
+ export interface HubNodeResult {
11
+ success: boolean;
12
+ data?: Record<string, unknown>;
13
+ error?: {
14
+ code: string;
15
+ message: string;
16
+ details?: JsonObject;
17
+ cause?: Error;
18
+ };
19
+ metadata: {
20
+ node: string;
21
+ executionTime: number;
22
+ startTime: number;
23
+ endTime: number;
24
+ dataProcessed?: {
25
+ messages?: number;
26
+ tools?: number;
27
+ tokens?: number;
28
+ responseSize?: number;
29
+ };
30
+ };
31
+ }