@jsonstudio/llms 0.6.1172 → 0.6.1397

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 (167) 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/compat/profiles/chat-gemini.json +5 -0
  10. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +47 -56
  11. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +1 -13
  12. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +748 -52
  13. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +18 -38
  14. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +6 -0
  15. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
  16. package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.d.ts +10 -0
  17. package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.js +142 -0
  18. package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.d.ts +6 -0
  19. package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.js +79 -0
  20. package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.d.ts +3 -0
  21. package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.js +46 -0
  22. package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.d.ts +8 -0
  23. package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.js +366 -0
  24. package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.d.ts +9 -0
  25. package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.js +390 -0
  26. package/dist/conversion/hub/pipeline/hub-pipeline/node-results.d.ts +3 -0
  27. package/dist/conversion/hub/pipeline/hub-pipeline/node-results.js +14 -0
  28. package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.d.ts +2 -0
  29. package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.js +144 -0
  30. package/dist/conversion/hub/pipeline/hub-pipeline/policy.d.ts +4 -0
  31. package/dist/conversion/hub/pipeline/hub-pipeline/policy.js +32 -0
  32. package/dist/conversion/hub/pipeline/hub-pipeline/protocol.d.ts +8 -0
  33. package/dist/conversion/hub/pipeline/hub-pipeline/protocol.js +63 -0
  34. package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.d.ts +2 -0
  35. package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.js +43 -0
  36. package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.d.ts +1 -0
  37. package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.js +29 -0
  38. package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.d.ts +2 -0
  39. package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.js +16 -0
  40. package/dist/conversion/hub/pipeline/hub-pipeline/types.d.ts +116 -0
  41. package/dist/conversion/hub/pipeline/hub-pipeline/types.js +1 -0
  42. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +3 -95
  43. package/dist/conversion/hub/pipeline/hub-pipeline.js +19 -1281
  44. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +1 -1
  45. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +7 -0
  46. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +65 -1
  47. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +25 -22
  48. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +1 -1
  49. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.d.ts +1 -1
  50. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.js +2 -2
  51. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_thought_signature_inject/index.d.ts +10 -0
  52. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_thought_signature_inject/index.js +172 -0
  53. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
  54. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +1 -1
  55. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +1 -1
  56. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +11 -11
  57. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage2_format_parse/index.js +1 -1
  58. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.d.ts +1 -0
  59. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.js +4 -2
  60. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_thought_signature_capture/index.d.ts +10 -0
  61. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_thought_signature_capture/index.js +71 -0
  62. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.d.ts +1 -0
  63. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +17 -9
  64. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage2_sse_stream/index.js +2 -2
  65. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +40 -2
  66. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.js +1 -1
  67. package/dist/conversion/hub/pipeline/target-utils.js +9 -5
  68. package/dist/conversion/hub/pipeline/thought-signature/thought-signature-center.d.ts +14 -0
  69. package/dist/conversion/hub/pipeline/thought-signature/thought-signature-center.js +289 -0
  70. package/dist/conversion/hub/process/chat-process.js +256 -16
  71. package/dist/conversion/hub/response/provider-response.d.ts +8 -0
  72. package/dist/conversion/hub/response/provider-response.js +91 -27
  73. package/dist/conversion/hub/response/response-mappers.d.ts +10 -3
  74. package/dist/conversion/hub/response/response-mappers.js +30 -6
  75. package/dist/conversion/hub/response/response-runtime.js +4 -38
  76. package/dist/conversion/hub/snapshot-recorder.js +5 -1
  77. package/dist/conversion/hub/standardized-bridge.js +23 -15
  78. package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +36 -5
  79. package/dist/conversion/responses/responses-openai-bridge.js +20 -4
  80. package/dist/conversion/shared/gemini-tool-utils.d.ts +8 -1
  81. package/dist/conversion/shared/gemini-tool-utils.js +580 -108
  82. package/dist/conversion/shared/jsonish.js +1 -1
  83. package/dist/conversion/shared/mcp-injection.js +67 -33
  84. package/dist/conversion/shared/openai-finalizer.js +2 -1
  85. package/dist/conversion/shared/openai-message-normalize.js +76 -21
  86. package/dist/conversion/shared/responses-output-builder.js +6 -0
  87. package/dist/conversion/shared/runtime-metadata.d.ts +7 -0
  88. package/dist/conversion/shared/runtime-metadata.js +23 -0
  89. package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
  90. package/dist/conversion/shared/text-markup-normalizer.js +284 -4
  91. package/dist/conversion/shared/tool-canonicalizer.js +2 -1
  92. package/dist/conversion/shared/tool-governor.js +3 -3
  93. package/dist/filters/engine.js +5 -5
  94. package/dist/filters/special/request-tool-list-filter.js +194 -60
  95. package/dist/filters/special/request-tools-normalize.js +1 -1
  96. package/dist/filters/special/response-tool-text-canonicalize.d.ts +4 -7
  97. package/dist/filters/special/response-tool-text-canonicalize.js +7 -35
  98. package/dist/filters/special/tool-filter-hooks.js +58 -62
  99. package/dist/guidance/index.js +5 -1
  100. package/dist/http/sse-response.js +6 -6
  101. package/dist/router/virtual-router/bootstrap.js +54 -4
  102. package/dist/router/virtual-router/engine-health.d.ts +1 -1
  103. package/dist/router/virtual-router/engine-health.js +11 -110
  104. package/dist/router/virtual-router/engine-selection/alias-selection.d.ts +30 -0
  105. package/dist/router/virtual-router/engine-selection/alias-selection.js +237 -0
  106. package/dist/router/virtual-router/engine-selection/context-weight-multipliers.d.ts +11 -0
  107. package/dist/router/virtual-router/engine-selection/context-weight-multipliers.js +23 -0
  108. package/dist/router/virtual-router/engine-selection/direct-provider-model.d.ts +9 -0
  109. package/dist/router/virtual-router/engine-selection/direct-provider-model.js +49 -0
  110. package/dist/router/virtual-router/engine-selection/instruction-target.d.ts +6 -0
  111. package/dist/router/virtual-router/engine-selection/instruction-target.js +54 -0
  112. package/dist/router/virtual-router/engine-selection/key-parsing.d.ts +8 -0
  113. package/dist/router/virtual-router/engine-selection/key-parsing.js +64 -0
  114. package/dist/router/virtual-router/engine-selection/route-utils.d.ts +12 -0
  115. package/dist/router/virtual-router/engine-selection/route-utils.js +150 -0
  116. package/dist/router/virtual-router/engine-selection/routing-state-filter.d.ts +4 -0
  117. package/dist/router/virtual-router/engine-selection/routing-state-filter.js +50 -0
  118. package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +39 -0
  119. package/dist/router/virtual-router/engine-selection/selection-deps.js +1 -0
  120. package/dist/router/virtual-router/engine-selection/sticky-pool.d.ts +11 -0
  121. package/dist/router/virtual-router/engine-selection/sticky-pool.js +109 -0
  122. package/dist/router/virtual-router/engine-selection/tier-priority.d.ts +12 -0
  123. package/dist/router/virtual-router/engine-selection/tier-priority.js +55 -0
  124. package/dist/router/virtual-router/engine-selection/tier-selection-select.d.ts +22 -0
  125. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +423 -0
  126. package/dist/router/virtual-router/engine-selection/tier-selection.d.ts +3 -0
  127. package/dist/router/virtual-router/engine-selection/tier-selection.js +228 -0
  128. package/dist/router/virtual-router/engine-selection.d.ts +4 -30
  129. package/dist/router/virtual-router/engine-selection.js +10 -962
  130. package/dist/router/virtual-router/engine.d.ts +1 -0
  131. package/dist/router/virtual-router/engine.js +64 -11
  132. package/dist/router/virtual-router/routing-instructions.js +6 -1
  133. package/dist/router/virtual-router/stop-message-state-sync.d.ts +5 -0
  134. package/dist/router/virtual-router/stop-message-state-sync.js +6 -14
  135. package/dist/router/virtual-router/types.d.ts +38 -1
  136. package/dist/servertool/clock/config.d.ts +8 -0
  137. package/dist/servertool/clock/config.js +22 -0
  138. package/dist/servertool/clock/log.d.ts +3 -0
  139. package/dist/servertool/clock/log.js +13 -0
  140. package/dist/servertool/clock/task-store.d.ts +1 -1
  141. package/dist/servertool/clock/task-store.js +1 -1
  142. package/dist/servertool/clock/tasks.js +1 -1
  143. package/dist/servertool/engine.js +146 -21
  144. package/dist/servertool/handlers/clock-auto.js +11 -6
  145. package/dist/servertool/handlers/clock.js +36 -10
  146. package/dist/servertool/handlers/followup-request-builder.js +8 -2
  147. package/dist/servertool/handlers/gemini-empty-reply-continue.js +15 -9
  148. package/dist/servertool/handlers/iflow-model-error-retry.js +6 -4
  149. package/dist/servertool/handlers/recursive-detection-guard.js +4 -2
  150. package/dist/servertool/handlers/stop-message-auto.js +100 -10
  151. package/dist/servertool/handlers/vision.js +4 -1
  152. package/dist/servertool/handlers/web-search.js +3 -1
  153. package/dist/servertool/pending-session.d.ts +19 -0
  154. package/dist/servertool/pending-session.js +97 -0
  155. package/dist/servertool/reenter-backend.js +5 -3
  156. package/dist/servertool/server-side-tools.js +235 -6
  157. package/dist/servertool/types.d.ts +13 -0
  158. package/dist/sse/json-to-sse/event-generators/responses.js +1 -1
  159. package/dist/sse/shared/chat-serializer.js +2 -2
  160. package/dist/sse/shared/constants.js +1 -1
  161. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +7 -1
  162. package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
  163. package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
  164. package/dist/tools/apply-patch/execution-capturer.js +1 -1
  165. package/dist/tools/exec-command/normalize.js +4 -0
  166. package/dist/tools/exec-command/regression-capturer.js +1 -1
  167. package/package.json +10 -5
@@ -0,0 +1,390 @@
1
+ import { jsonClone } from '../../types/json.js';
2
+ import { ensureRuntimeMetadata, readRuntimeMetadata } from '../../../shared/runtime-metadata.js';
3
+ import { computeRequestTokens } from '../../../../router/virtual-router/token-estimator.js';
4
+ import { isCompactionRequest } from '../../../shared/compaction-detect.js';
5
+ import { recordHubPolicyObservation, applyHubProviderOutboundPolicy } from '../../policy/policy-engine.js';
6
+ import { applyProviderOutboundToolSurface } from '../../tool-surface/tool-surface-engine.js';
7
+ import { runReqInboundStage1FormatParse } from '../stages/req_inbound/req_inbound_stage1_format_parse/index.js';
8
+ import { runReqInboundStage2SemanticMap } from '../stages/req_inbound/req_inbound_stage2_semantic_map/index.js';
9
+ import { runReqProcessStage1ToolGovernance } from '../stages/req_process/req_process_stage1_tool_governance/index.js';
10
+ import { runReqProcessStage2RouteSelect } from '../stages/req_process/req_process_stage2_route_select/index.js';
11
+ import { runReqOutboundStage1SemanticMap } from '../stages/req_outbound/req_outbound_stage1_semantic_map/index.js';
12
+ import { runReqOutboundStage2FormatBuild } from '../stages/req_outbound/req_outbound_stage2_format_build/index.js';
13
+ import { runReqOutboundStage2ThoughtSignatureInject } from '../stages/req_outbound/req_outbound_stage2_thought_signature_inject/index.js';
14
+ import { runReqOutboundStage3Compat } from '../stages/req_outbound/req_outbound_stage3_compat/index.js';
15
+ import { extractSessionIdentifiersFromMetadata } from '../session-identifiers.js';
16
+ import { buildAdapterContext, maybeCreateStageRecorder, resolveOutboundStreamIntent, applyOutboundStreamPreference } from './adapter-context.js';
17
+ import { resolveClientProtocolFromEntryEndpoint } from './protocol.js';
18
+ import { assertNoMappableSemanticsInMetadata } from './semantic-gate.js';
19
+ import { convertProcessNodeResult } from './node-results.js';
20
+ import { resolveApplyPatchToolModeFromEnv, resolveApplyPatchToolModeFromTools } from './apply-patch-tool-mode.js';
21
+ import { injectServerToolRuntimeConfig } from './servertool-runtime-config.js';
22
+ function asJsonObject(value) {
23
+ if (!value || typeof value !== 'object') {
24
+ throw new Error('Responses pipeline requires JSON object payload');
25
+ }
26
+ return value;
27
+ }
28
+ function readResponsesResumeFromSemantics(request) {
29
+ try {
30
+ const semantics = request?.semantics;
31
+ const node = semantics && typeof semantics === 'object' && !Array.isArray(semantics) ? semantics.responses : undefined;
32
+ const resume = node && typeof node === 'object' && !Array.isArray(node) ? node.resume : undefined;
33
+ return resume && typeof resume === 'object' && !Array.isArray(resume) ? resume : undefined;
34
+ }
35
+ catch {
36
+ return undefined;
37
+ }
38
+ }
39
+ export async function executeRequestStagePipeline(options) {
40
+ const { config, routerEngine, normalized, hooks } = options;
41
+ const formatAdapter = hooks.createFormatAdapter();
42
+ const semanticMapper = hooks.createSemanticMapper();
43
+ const rawRequest = asJsonObject(normalized.payload);
44
+ // Detect applyPatchToolMode (runtime/tooling hint). Client tool schemas are captured as chat semantics
45
+ // in req_inbound_stage2_semantic_map; they must not be stored in metadata.
46
+ try {
47
+ const toolsRaw = Array.isArray(rawRequest?.tools) ? rawRequest.tools : null;
48
+ const applyPatchToolMode = resolveApplyPatchToolModeFromEnv() ?? resolveApplyPatchToolModeFromTools(toolsRaw);
49
+ if (applyPatchToolMode) {
50
+ normalized.metadata = normalized.metadata || {};
51
+ const rt = ensureRuntimeMetadata(normalized.metadata);
52
+ rt.applyPatchToolMode = applyPatchToolMode;
53
+ }
54
+ }
55
+ catch {
56
+ // best-effort: do not block request handling due to tool scan failures
57
+ }
58
+ if (isCompactionRequest(rawRequest)) {
59
+ normalized.metadata = normalized.metadata || {};
60
+ const rt = ensureRuntimeMetadata(normalized.metadata);
61
+ rt.compactionRequest = true;
62
+ }
63
+ const effectivePolicy = normalized.policyOverride ?? config.policy;
64
+ const shadowCompareBaselineMode = normalized.shadowCompare?.baselineMode;
65
+ const inboundAdapterContext = buildAdapterContext(normalized);
66
+ const inboundRecorder = maybeCreateStageRecorder(inboundAdapterContext, normalized.entryEndpoint, {
67
+ disableSnapshots: normalized.disableSnapshots === true
68
+ });
69
+ const inboundStart = Date.now();
70
+ recordHubPolicyObservation({
71
+ policy: effectivePolicy,
72
+ providerProtocol: resolveClientProtocolFromEntryEndpoint(normalized.entryEndpoint),
73
+ payload: rawRequest,
74
+ phase: 'client_inbound',
75
+ stageRecorder: inboundRecorder,
76
+ requestId: normalized.id
77
+ });
78
+ const formatEnvelope = await runReqInboundStage1FormatParse({
79
+ rawRequest,
80
+ adapterContext: inboundAdapterContext,
81
+ formatAdapter,
82
+ stageRecorder: inboundRecorder
83
+ });
84
+ const responsesResumeFromMetadata = normalized.metadata && typeof normalized.metadata.responsesResume === 'object'
85
+ ? normalized.metadata.responsesResume
86
+ : undefined;
87
+ const inboundStage2 = await runReqInboundStage2SemanticMap({
88
+ adapterContext: inboundAdapterContext,
89
+ formatEnvelope,
90
+ semanticMapper,
91
+ ...(responsesResumeFromMetadata ? { responsesResume: responsesResumeFromMetadata } : {}),
92
+ stageRecorder: inboundRecorder
93
+ });
94
+ if (responsesResumeFromMetadata &&
95
+ normalized.metadata &&
96
+ Object.prototype.hasOwnProperty.call(normalized.metadata, 'responsesResume')) {
97
+ delete normalized.metadata.responsesResume;
98
+ }
99
+ const contextSnapshot = await hooks.captureContext({
100
+ rawRequest,
101
+ adapterContext: inboundAdapterContext,
102
+ stageRecorder: inboundRecorder
103
+ });
104
+ const standardizedRequest = inboundStage2.standardizedRequest;
105
+ try {
106
+ const rt = readRuntimeMetadata(normalized.metadata);
107
+ const mode = String(rt?.applyPatchToolMode || '').trim().toLowerCase();
108
+ if (mode === 'freeform' || mode === 'schema') {
109
+ standardizedRequest.metadata.applyPatchToolMode = mode;
110
+ }
111
+ }
112
+ catch {
113
+ // best-effort: do not block request handling due to metadata propagation failures
114
+ }
115
+ const inboundEnd = Date.now();
116
+ const nodeResults = [];
117
+ nodeResults.push({
118
+ id: 'req_inbound',
119
+ success: true,
120
+ metadata: {
121
+ node: 'req_inbound',
122
+ executionTime: inboundEnd - inboundStart,
123
+ startTime: inboundStart,
124
+ endTime: inboundEnd,
125
+ dataProcessed: {
126
+ messages: standardizedRequest.messages.length,
127
+ tools: standardizedRequest.tools?.length ?? 0
128
+ }
129
+ }
130
+ });
131
+ const metaBase = {
132
+ ...(normalized.metadata ?? {})
133
+ };
134
+ injectServerToolRuntimeConfig(config, metaBase);
135
+ normalized.metadata = metaBase;
136
+ let processedRequest;
137
+ if (normalized.processMode !== 'passthrough') {
138
+ assertNoMappableSemanticsInMetadata(metaBase, 'chat_process.request.entry');
139
+ const processResult = await runReqProcessStage1ToolGovernance({
140
+ request: standardizedRequest,
141
+ rawPayload: rawRequest,
142
+ metadata: metaBase,
143
+ entryEndpoint: normalized.entryEndpoint,
144
+ requestId: normalized.id,
145
+ stageRecorder: inboundRecorder
146
+ });
147
+ processedRequest = processResult.processedRequest;
148
+ try {
149
+ const reservation = processedRequest?.metadata?.__clockReservation;
150
+ if (reservation && typeof reservation === 'object') {
151
+ metaBase.__clockReservation = reservation;
152
+ }
153
+ }
154
+ catch {
155
+ // best-effort: do not block request handling due to metadata propagation failures
156
+ }
157
+ if (processResult.nodeResult) {
158
+ nodeResults.push(convertProcessNodeResult('chat_process.req.stage4.tool_governance', processResult.nodeResult));
159
+ }
160
+ }
161
+ let workingRequest = processedRequest ?? standardizedRequest;
162
+ try {
163
+ const estimatedTokens = computeRequestTokens(workingRequest, '');
164
+ if (typeof estimatedTokens === 'number' && Number.isFinite(estimatedTokens) && estimatedTokens > 0) {
165
+ normalized.metadata = normalized.metadata || {};
166
+ normalized.metadata.estimatedInputTokens = estimatedTokens;
167
+ }
168
+ }
169
+ catch {
170
+ // ignore
171
+ }
172
+ const normalizedMeta = normalized.metadata;
173
+ const responsesResume = readResponsesResumeFromSemantics(workingRequest);
174
+ const stdMetadata = workingRequest?.metadata;
175
+ const hasImageAttachment = (stdMetadata?.hasImageAttachment === true || stdMetadata?.hasImageAttachment === 'true') ||
176
+ (normalizedMeta?.hasImageAttachment === true || normalizedMeta?.hasImageAttachment === 'true');
177
+ const serverToolRequired = stdMetadata?.webSearchEnabled === true || stdMetadata?.serverToolRequired === true;
178
+ const sessionIdentifiers = extractSessionIdentifiersFromMetadata(normalized.metadata);
179
+ if (sessionIdentifiers.sessionId && normalized.metadata && typeof normalized.metadata === 'object') {
180
+ normalized.metadata.sessionId = sessionIdentifiers.sessionId;
181
+ }
182
+ if (sessionIdentifiers.conversationId && normalized.metadata && typeof normalized.metadata === 'object') {
183
+ normalized.metadata.conversationId = sessionIdentifiers.conversationId;
184
+ }
185
+ const disableStickyRoutes = readRuntimeMetadata(normalized.metadata)?.disableStickyRoutes === true;
186
+ const shadowCompareForcedProviderKey = normalizedMeta && typeof normalizedMeta.__shadowCompareForcedProviderKey === 'string'
187
+ ? String(normalizedMeta.__shadowCompareForcedProviderKey)
188
+ : '';
189
+ const disabledProviderKeyAliases = normalizedMeta && Array.isArray(normalizedMeta.disabledProviderKeyAliases)
190
+ ? normalizedMeta.disabledProviderKeyAliases
191
+ : null;
192
+ const metadataInput = {
193
+ requestId: normalized.id,
194
+ entryEndpoint: normalized.entryEndpoint,
195
+ processMode: normalized.processMode,
196
+ stream: normalized.stream,
197
+ direction: normalized.direction,
198
+ providerProtocol: normalized.providerProtocol,
199
+ routeHint: normalized.routeHint,
200
+ stage: normalized.stage,
201
+ responsesResume: responsesResume,
202
+ ...(disableStickyRoutes ? { disableStickyRoutes: true } : {}),
203
+ ...(serverToolRequired ? { serverToolRequired: true } : {}),
204
+ ...(sessionIdentifiers.sessionId ? { sessionId: sessionIdentifiers.sessionId } : {}),
205
+ ...(sessionIdentifiers.conversationId ? { conversationId: sessionIdentifiers.conversationId } : {}),
206
+ ...(shadowCompareForcedProviderKey.trim().length
207
+ ? { __shadowCompareForcedProviderKey: shadowCompareForcedProviderKey.trim() }
208
+ : {}),
209
+ ...(disabledProviderKeyAliases && disabledProviderKeyAliases.length
210
+ ? { disabledProviderKeyAliases: disabledProviderKeyAliases }
211
+ : {})
212
+ };
213
+ const routing = runReqProcessStage2RouteSelect({
214
+ routerEngine,
215
+ request: workingRequest,
216
+ metadataInput,
217
+ normalizedMetadata: normalized.metadata,
218
+ stageRecorder: inboundRecorder
219
+ });
220
+ const stopMessageState = routerEngine.getStopMessageState(metadataInput);
221
+ if (stopMessageState && normalized.metadata && typeof normalized.metadata === 'object') {
222
+ const rt = ensureRuntimeMetadata(normalized.metadata);
223
+ rt.stopMessageState = stopMessageState;
224
+ }
225
+ try {
226
+ const routeName = routing.decision?.routeName;
227
+ const providerKey = routing.target?.providerKey;
228
+ const modelId = workingRequest.model;
229
+ const logger = (normalized.metadata && normalized.metadata.logger);
230
+ if (logger && typeof logger.logVirtualRouterHit === 'function' && routeName && providerKey) {
231
+ logger.logVirtualRouterHit(routeName, providerKey, typeof modelId === 'string' ? modelId : undefined);
232
+ }
233
+ }
234
+ catch {
235
+ // ignore
236
+ }
237
+ const outboundStream = resolveOutboundStreamIntent(routing.target?.streaming);
238
+ workingRequest = applyOutboundStreamPreference(workingRequest, outboundStream);
239
+ const outboundAdapterContext = buildAdapterContext(normalized, routing.target);
240
+ if (routing.target?.compatibilityProfile) {
241
+ outboundAdapterContext.compatibilityProfile = routing.target.compatibilityProfile;
242
+ }
243
+ const outboundProtocol = outboundAdapterContext.providerProtocol;
244
+ const protocolSwitch = outboundProtocol !== normalized.providerProtocol;
245
+ const outboundHooks = protocolSwitch ? options.resolveProtocolHooks(outboundProtocol) : hooks;
246
+ if (!outboundHooks) {
247
+ throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
248
+ }
249
+ const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : semanticMapper;
250
+ const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : formatAdapter;
251
+ const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
252
+ const outboundContextSnapshot = protocolSwitch ? undefined : contextSnapshot;
253
+ const outboundRecorder = maybeCreateStageRecorder(outboundAdapterContext, normalized.entryEndpoint, {
254
+ disableSnapshots: normalized.disableSnapshots === true
255
+ });
256
+ const outboundStart = Date.now();
257
+ const outboundStage1 = await runReqOutboundStage1SemanticMap({
258
+ request: workingRequest,
259
+ adapterContext: outboundAdapterContext,
260
+ semanticMapper: outboundSemanticMapper,
261
+ contextSnapshot: outboundContextSnapshot,
262
+ contextMetadataKey: outboundContextMetadataKey,
263
+ stageRecorder: outboundRecorder
264
+ });
265
+ let formattedPayload = await runReqOutboundStage2FormatBuild({
266
+ formatEnvelope: outboundStage1.formatEnvelope,
267
+ adapterContext: outboundAdapterContext,
268
+ formatAdapter: outboundFormatAdapter,
269
+ stageRecorder: outboundRecorder
270
+ });
271
+ formattedPayload = await runReqOutboundStage2ThoughtSignatureInject({
272
+ payload: formattedPayload,
273
+ adapterContext: outboundAdapterContext,
274
+ stageRecorder: outboundRecorder
275
+ });
276
+ formattedPayload = await runReqOutboundStage3Compat({
277
+ payload: formattedPayload,
278
+ adapterContext: outboundAdapterContext,
279
+ stageRecorder: outboundRecorder
280
+ });
281
+ let shadowBaselineProviderPayload;
282
+ if (shadowCompareBaselineMode) {
283
+ const baselinePolicy = {
284
+ ...(effectivePolicy ?? {}),
285
+ mode: shadowCompareBaselineMode
286
+ };
287
+ const baselineFormatted = typeof globalThis.structuredClone === 'function'
288
+ ? globalThis.structuredClone(formattedPayload)
289
+ : jsonClone(formattedPayload);
290
+ let baselinePayload = applyHubProviderOutboundPolicy({
291
+ policy: baselinePolicy,
292
+ providerProtocol: outboundProtocol,
293
+ payload: baselineFormatted,
294
+ stageRecorder: undefined,
295
+ requestId: normalized.id
296
+ });
297
+ baselinePayload = applyProviderOutboundToolSurface({
298
+ config: config.toolSurface,
299
+ providerProtocol: outboundProtocol,
300
+ payload: baselinePayload,
301
+ stageRecorder: undefined,
302
+ requestId: normalized.id
303
+ });
304
+ shadowBaselineProviderPayload = baselinePayload;
305
+ }
306
+ recordHubPolicyObservation({
307
+ policy: effectivePolicy,
308
+ providerProtocol: outboundProtocol,
309
+ payload: formattedPayload,
310
+ stageRecorder: outboundRecorder,
311
+ requestId: normalized.id
312
+ });
313
+ let providerPayload = applyHubProviderOutboundPolicy({
314
+ policy: effectivePolicy,
315
+ providerProtocol: outboundProtocol,
316
+ payload: formattedPayload,
317
+ stageRecorder: outboundRecorder,
318
+ requestId: normalized.id
319
+ });
320
+ providerPayload = applyProviderOutboundToolSurface({
321
+ config: config.toolSurface,
322
+ providerProtocol: outboundProtocol,
323
+ payload: providerPayload,
324
+ stageRecorder: outboundRecorder,
325
+ requestId: normalized.id
326
+ });
327
+ recordHubPolicyObservation({
328
+ policy: effectivePolicy,
329
+ providerProtocol: outboundProtocol,
330
+ payload: providerPayload,
331
+ stageRecorder: outboundRecorder,
332
+ requestId: normalized.id
333
+ });
334
+ const outboundEnd = Date.now();
335
+ nodeResults.push({
336
+ id: 'req_outbound',
337
+ success: true,
338
+ metadata: {
339
+ node: 'req_outbound',
340
+ executionTime: outboundEnd - outboundStart,
341
+ startTime: outboundStart,
342
+ endTime: outboundEnd,
343
+ dataProcessed: {
344
+ messages: workingRequest.messages.length,
345
+ tools: workingRequest.tools?.length ?? 0
346
+ }
347
+ }
348
+ });
349
+ const capturedChatRequest = {
350
+ model: workingRequest.model,
351
+ messages: jsonClone(workingRequest.messages),
352
+ tools: workingRequest.tools ? jsonClone(workingRequest.tools) : workingRequest.tools,
353
+ parameters: workingRequest.parameters
354
+ ? jsonClone(workingRequest.parameters)
355
+ : workingRequest.parameters
356
+ };
357
+ const metadata = {
358
+ ...normalized.metadata,
359
+ ...(hasImageAttachment ? { hasImageAttachment: true } : {}),
360
+ capturedChatRequest,
361
+ entryEndpoint: normalized.entryEndpoint,
362
+ providerProtocol: outboundProtocol,
363
+ stream: normalized.stream,
364
+ processMode: normalized.processMode,
365
+ routeHint: normalized.routeHint,
366
+ target: routing.target,
367
+ ...(typeof outboundStream === 'boolean' ? { providerStream: outboundStream } : {}),
368
+ ...(shadowBaselineProviderPayload
369
+ ? {
370
+ hubShadowCompare: {
371
+ baselineMode: shadowCompareBaselineMode,
372
+ candidateMode: (effectivePolicy?.mode ?? 'off'),
373
+ providerProtocol: outboundProtocol,
374
+ baselineProviderPayload: shadowBaselineProviderPayload
375
+ }
376
+ }
377
+ : {})
378
+ };
379
+ return {
380
+ requestId: normalized.id,
381
+ providerPayload,
382
+ standardizedRequest,
383
+ processedRequest,
384
+ routingDecision: routing.decision,
385
+ routingDiagnostics: routing.diagnostics,
386
+ target: routing.target,
387
+ metadata,
388
+ nodeResults
389
+ };
390
+ }
@@ -0,0 +1,3 @@
1
+ import type { HubPipelineNodeResult } from './types.js';
2
+ import type { HubProcessNodeResult } from '../../process/chat-process.js';
3
+ export declare function convertProcessNodeResult(id: string, result: HubProcessNodeResult): HubPipelineNodeResult;
@@ -0,0 +1,14 @@
1
+ export function convertProcessNodeResult(id, result) {
2
+ return {
3
+ id,
4
+ success: result.success,
5
+ metadata: result.metadata,
6
+ error: result.error
7
+ ? {
8
+ code: result.error.code ?? 'hub_chat_process_error',
9
+ message: result.error.message,
10
+ details: result.error.details
11
+ }
12
+ : undefined
13
+ };
14
+ }
@@ -0,0 +1,2 @@
1
+ import type { HubPipelineRequest, NormalizedRequest } from './types.js';
2
+ export declare function normalizeHubPipelineRequest(request: HubPipelineRequest): Promise<NormalizedRequest>;
@@ -0,0 +1,144 @@
1
+ import { Readable } from 'node:stream';
2
+ import { defaultSseCodecRegistry } from '../../../../sse/index.js';
3
+ import { extractHubPolicyOverride, extractHubShadowCompareConfig } from './policy.js';
4
+ import { normalizeEndpoint, resolveProviderProtocol, resolveSseProtocolFromMetadata } from './protocol.js';
5
+ function unwrapReadable(payload) {
6
+ if (!payload) {
7
+ return null;
8
+ }
9
+ if (payload instanceof Readable) {
10
+ return payload;
11
+ }
12
+ if (payload && typeof payload === 'object' && 'readable' in payload) {
13
+ const candidate = payload.readable;
14
+ if (candidate instanceof Readable) {
15
+ return candidate;
16
+ }
17
+ }
18
+ return null;
19
+ }
20
+ function resolveSseProtocol(context) {
21
+ const explicitProtocol = resolveSseProtocolFromMetadata(context.metadata);
22
+ if (explicitProtocol) {
23
+ return explicitProtocol;
24
+ }
25
+ return context.providerProtocol;
26
+ }
27
+ function extractModelHint(metadata) {
28
+ if (typeof metadata.model === 'string' && metadata.model.trim()) {
29
+ return metadata.model;
30
+ }
31
+ const provider = metadata.provider;
32
+ const candidates = [provider?.model, provider?.modelId, provider?.defaultModel];
33
+ for (const candidate of candidates) {
34
+ if (typeof candidate === 'string' && candidate.trim()) {
35
+ return candidate;
36
+ }
37
+ }
38
+ return undefined;
39
+ }
40
+ async function convertSsePayload(stream, context) {
41
+ const protocol = resolveSseProtocol(context);
42
+ const codec = defaultSseCodecRegistry.get(protocol);
43
+ try {
44
+ const result = await codec.convertSseToJson(stream, {
45
+ requestId: context.requestId,
46
+ model: extractModelHint(context.metadata),
47
+ direction: 'request'
48
+ });
49
+ if (!result || typeof result !== 'object') {
50
+ throw new Error('SSE conversion returned empty payload');
51
+ }
52
+ return result;
53
+ }
54
+ catch (error) {
55
+ const message = error instanceof Error ? error.message : String(error ?? 'Unknown error');
56
+ throw new Error(`Failed to convert SSE payload for protocol ${protocol}: ${message}`);
57
+ }
58
+ }
59
+ async function materializePayload(payload, context, resolvedStream) {
60
+ const stream = resolvedStream ?? unwrapReadable(payload);
61
+ if (stream) {
62
+ return await convertSsePayload(stream, context);
63
+ }
64
+ if (!payload || typeof payload !== 'object') {
65
+ throw new Error('HubPipeline requires JSON object payload');
66
+ }
67
+ return payload;
68
+ }
69
+ export async function normalizeHubPipelineRequest(request) {
70
+ if (!request || typeof request !== 'object') {
71
+ throw new Error('HubPipeline requires request payload');
72
+ }
73
+ const id = request.id || `req_${Date.now()}`;
74
+ const endpoint = normalizeEndpoint(request.endpoint);
75
+ const metadataRecord = {
76
+ ...(request.metadata ?? {})
77
+ };
78
+ const policyOverride = extractHubPolicyOverride(metadataRecord);
79
+ if (Object.prototype.hasOwnProperty.call(metadataRecord, '__hubPolicyOverride')) {
80
+ delete metadataRecord.__hubPolicyOverride;
81
+ }
82
+ const shadowCompare = extractHubShadowCompareConfig(metadataRecord);
83
+ if (Object.prototype.hasOwnProperty.call(metadataRecord, '__hubShadowCompare')) {
84
+ delete metadataRecord.__hubShadowCompare;
85
+ }
86
+ const disableSnapshots = metadataRecord.__disableHubSnapshots === true;
87
+ if (Object.prototype.hasOwnProperty.call(metadataRecord, '__disableHubSnapshots')) {
88
+ delete metadataRecord.__disableHubSnapshots;
89
+ }
90
+ const hubEntryRaw = typeof metadataRecord.__hubEntry === 'string'
91
+ ? String(metadataRecord.__hubEntry).trim().toLowerCase()
92
+ : '';
93
+ const hubEntryMode = hubEntryRaw === 'chat_process' || hubEntryRaw === 'chat-process' || hubEntryRaw === 'chatprocess'
94
+ ? 'chat_process'
95
+ : undefined;
96
+ if (Object.prototype.hasOwnProperty.call(metadataRecord, '__hubEntry')) {
97
+ delete metadataRecord.__hubEntry;
98
+ }
99
+ const entryEndpoint = typeof metadataRecord.entryEndpoint === 'string' ? normalizeEndpoint(metadataRecord.entryEndpoint) : endpoint;
100
+ const providerProtocol = resolveProviderProtocol(metadataRecord.providerProtocol);
101
+ const processMode = metadataRecord.processMode === 'passthrough' ? 'passthrough' : 'chat';
102
+ const direction = metadataRecord.direction === 'response' ? 'response' : 'request';
103
+ const stage = metadataRecord.stage === 'outbound' ? 'outbound' : 'inbound';
104
+ const resolvedReadable = unwrapReadable(request.payload);
105
+ const stream = Boolean(metadataRecord.stream ||
106
+ resolvedReadable ||
107
+ (request.payload && typeof request.payload === 'object' && request.payload.stream));
108
+ const payload = await materializePayload(request.payload, {
109
+ requestId: id,
110
+ entryEndpoint,
111
+ providerProtocol,
112
+ metadata: metadataRecord
113
+ }, resolvedReadable);
114
+ const normalizedMetadata = {
115
+ ...metadataRecord,
116
+ entryEndpoint,
117
+ providerProtocol,
118
+ processMode,
119
+ direction,
120
+ stage,
121
+ stream
122
+ };
123
+ const routeHint = typeof metadataRecord.routeHint === 'string' ? metadataRecord.routeHint : undefined;
124
+ if (routeHint) {
125
+ normalizedMetadata.routeHint = routeHint;
126
+ }
127
+ return {
128
+ id,
129
+ endpoint,
130
+ entryEndpoint,
131
+ providerProtocol,
132
+ payload,
133
+ metadata: normalizedMetadata,
134
+ policyOverride: policyOverride ?? undefined,
135
+ shadowCompare: shadowCompare ?? undefined,
136
+ disableSnapshots,
137
+ processMode,
138
+ direction,
139
+ stage,
140
+ stream,
141
+ routeHint,
142
+ ...(hubEntryMode ? { hubEntryMode } : {})
143
+ };
144
+ }
@@ -0,0 +1,4 @@
1
+ import type { HubPolicyConfig } from '../../policy/policy-engine.js';
2
+ import type { HubShadowCompareRequestConfig } from './types.js';
3
+ export declare function extractHubPolicyOverride(metadata: Record<string, unknown> | undefined): HubPolicyConfig | undefined;
4
+ export declare function extractHubShadowCompareConfig(metadata: Record<string, unknown> | undefined): HubShadowCompareRequestConfig | undefined;
@@ -0,0 +1,32 @@
1
+ export function extractHubPolicyOverride(metadata) {
2
+ const raw = metadata ? metadata.__hubPolicyOverride : undefined;
3
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
4
+ return undefined;
5
+ }
6
+ const obj = raw;
7
+ const mode = typeof obj.mode === 'string' ? obj.mode.trim().toLowerCase() : '';
8
+ const sampleRate = typeof obj.sampleRate === 'number' && Number.isFinite(obj.sampleRate) ? obj.sampleRate : undefined;
9
+ if (mode !== 'off' && mode !== 'observe' && mode !== 'enforce') {
10
+ return undefined;
11
+ }
12
+ return {
13
+ mode: mode,
14
+ ...(sampleRate !== undefined ? { sampleRate } : {})
15
+ };
16
+ }
17
+ export function extractHubShadowCompareConfig(metadata) {
18
+ const raw = metadata ? metadata.__hubShadowCompare : undefined;
19
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
20
+ return undefined;
21
+ }
22
+ const obj = raw;
23
+ const modeCandidate = typeof obj.baselineMode === 'string'
24
+ ? obj.baselineMode.trim().toLowerCase()
25
+ : typeof obj.mode === 'string'
26
+ ? obj.mode.trim().toLowerCase()
27
+ : '';
28
+ if (modeCandidate !== 'off' && modeCandidate !== 'observe' && modeCandidate !== 'enforce') {
29
+ return undefined;
30
+ }
31
+ return { baselineMode: modeCandidate };
32
+ }
@@ -0,0 +1,8 @@
1
+ import type { SseProtocol } from '../../../../sse/index.js';
2
+ import type { ProviderProtocol } from './types.js';
3
+ export declare function normalizeEndpoint(endpoint: string): string;
4
+ export declare function resolveProviderProtocol(value: unknown): ProviderProtocol;
5
+ export declare const PROVIDER_PROTOCOL_ALIASES: Record<string, ProviderProtocol>;
6
+ export declare function resolveEndpointForProviderProtocol(protocol?: string): string | undefined;
7
+ export declare function resolveSseProtocolFromMetadata(metadata: Record<string, unknown>): SseProtocol | undefined;
8
+ export declare function resolveClientProtocolFromEntryEndpoint(entryEndpoint: string): ProviderProtocol;
@@ -0,0 +1,63 @@
1
+ export function normalizeEndpoint(endpoint) {
2
+ if (!endpoint)
3
+ return '/v1/chat/completions';
4
+ const trimmed = endpoint.trim();
5
+ if (!trimmed)
6
+ return '/v1/chat/completions';
7
+ const normalized = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
8
+ return normalized.replace(/\/{2,}/g, '/');
9
+ }
10
+ export function resolveProviderProtocol(value) {
11
+ if (typeof value !== 'string' || !value.trim()) {
12
+ return 'openai-chat';
13
+ }
14
+ const normalized = value.trim().toLowerCase();
15
+ const mapped = PROVIDER_PROTOCOL_ALIASES[normalized];
16
+ if (mapped) {
17
+ return mapped;
18
+ }
19
+ throw new Error(`[HubPipeline] Unsupported providerProtocol "${value}". Configure a valid protocol (openai-chat|openai-responses|anthropic-messages|gemini-chat).`);
20
+ }
21
+ export const PROVIDER_PROTOCOL_ALIASES = {
22
+ 'openai-chat': 'openai-chat',
23
+ openai: 'openai-chat',
24
+ chat: 'openai-chat',
25
+ responses: 'openai-responses',
26
+ 'openai-responses': 'openai-responses',
27
+ anthropic: 'anthropic-messages',
28
+ 'anthropic-messages': 'anthropic-messages',
29
+ messages: 'anthropic-messages',
30
+ gemini: 'gemini-chat',
31
+ 'google-gemini': 'gemini-chat',
32
+ 'gemini-chat': 'gemini-chat'
33
+ };
34
+ export function resolveEndpointForProviderProtocol(protocol) {
35
+ if (!protocol) {
36
+ return undefined;
37
+ }
38
+ const value = protocol.toLowerCase();
39
+ if (value === 'openai-responses')
40
+ return '/v1/responses';
41
+ if (value === 'openai-chat')
42
+ return '/v1/chat/completions';
43
+ if (value === 'anthropic-messages' || value === 'anthropic')
44
+ return '/v1/messages';
45
+ if (value === 'gemini-chat' || value === 'gemini' || value === 'google-gemini')
46
+ return '/v1/responses';
47
+ return undefined;
48
+ }
49
+ export function resolveSseProtocolFromMetadata(metadata) {
50
+ const candidate = metadata.sseProtocol ?? metadata.clientSseProtocol ?? metadata.routeSseProtocol;
51
+ if (typeof candidate !== 'string' || !candidate.trim()) {
52
+ return undefined;
53
+ }
54
+ return resolveProviderProtocol(candidate);
55
+ }
56
+ export function resolveClientProtocolFromEntryEndpoint(entryEndpoint) {
57
+ const lowered = String(entryEndpoint || '').toLowerCase();
58
+ if (lowered.includes('/v1/responses'))
59
+ return 'openai-responses';
60
+ if (lowered.includes('/v1/messages'))
61
+ return 'anthropic-messages';
62
+ return 'openai-chat';
63
+ }
@@ -0,0 +1,2 @@
1
+ import type { ProviderProtocol, RequestStageHooks } from './types.js';
2
+ export declare function resolveProtocolHooks(protocol: ProviderProtocol): RequestStageHooks<Record<string, unknown>> | undefined;