@jsonstudio/llms 0.6.1739 → 0.6.1890

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 (107) hide show
  1. package/dist/conversion/compat/actions/deepseek-web-request.d.ts +3 -0
  2. package/dist/conversion/compat/actions/deepseek-web-request.js +350 -0
  3. package/dist/conversion/compat/actions/deepseek-web-response.d.ts +3 -0
  4. package/dist/conversion/compat/actions/deepseek-web-response.js +886 -0
  5. package/dist/conversion/compat/actions/gemini-cli-request.js +3 -1
  6. package/dist/conversion/compat/profiles/chat-deepseek-web.json +18 -0
  7. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +166 -2
  8. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +169 -0
  9. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +6 -0
  10. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +12 -0
  11. package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -0
  12. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +4 -0
  13. package/dist/conversion/hub/pipeline/hub-pipeline.js +365 -144
  14. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +9 -0
  15. package/dist/conversion/hub/policy/policy-engine.d.ts +2 -0
  16. package/dist/conversion/hub/policy/policy-engine.js +8 -0
  17. package/dist/conversion/hub/process/chat-process.js +466 -16
  18. package/dist/conversion/hub/response/provider-response.js +0 -35
  19. package/dist/conversion/responses/responses-openai-bridge.d.ts +2 -0
  20. package/dist/conversion/responses/responses-openai-bridge.js +166 -8
  21. package/dist/conversion/shared/anthropic-message-utils.js +10 -1
  22. package/dist/conversion/shared/protocol-field-allowlists.d.ts +2 -2
  23. package/dist/conversion/shared/protocol-field-allowlists.js +4 -0
  24. package/dist/conversion/shared/tool-governor.js +102 -0
  25. package/dist/guidance/index.js +17 -0
  26. package/dist/router/virtual-router/bootstrap.js +46 -1
  27. package/dist/router/virtual-router/classifier.js +59 -4
  28. package/dist/router/virtual-router/engine/health/index.js +6 -6
  29. package/dist/router/virtual-router/engine/routing-state/store.js +16 -3
  30. package/dist/router/virtual-router/engine-logging.js +62 -24
  31. package/dist/router/virtual-router/engine-selection/route-utils.js +20 -20
  32. package/dist/router/virtual-router/engine-selection/tier-selection.js +2 -2
  33. package/dist/router/virtual-router/engine.d.ts +3 -1
  34. package/dist/router/virtual-router/engine.js +359 -39
  35. package/dist/router/virtual-router/features.js +2 -1
  36. package/dist/router/virtual-router/pre-command-file-resolver.d.ts +2 -0
  37. package/dist/router/virtual-router/pre-command-file-resolver.js +90 -0
  38. package/dist/router/virtual-router/provider-registry.js +3 -1
  39. package/dist/router/virtual-router/routing-instructions.d.ts +15 -1
  40. package/dist/router/virtual-router/routing-instructions.js +110 -151
  41. package/dist/router/virtual-router/routing-pre-command-actions.d.ts +3 -0
  42. package/dist/router/virtual-router/routing-pre-command-actions.js +26 -0
  43. package/dist/router/virtual-router/routing-pre-command-parser.d.ts +2 -0
  44. package/dist/router/virtual-router/routing-pre-command-parser.js +85 -0
  45. package/dist/router/virtual-router/routing-pre-command-state-codec.d.ts +3 -0
  46. package/dist/router/virtual-router/routing-pre-command-state-codec.js +24 -0
  47. package/dist/router/virtual-router/routing-stop-message-actions.d.ts +2 -0
  48. package/dist/router/virtual-router/routing-stop-message-actions.js +96 -0
  49. package/dist/router/virtual-router/routing-stop-message-parser.d.ts +3 -0
  50. package/dist/router/virtual-router/routing-stop-message-parser.js +142 -0
  51. package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +4 -0
  52. package/dist/router/virtual-router/routing-stop-message-state-codec.js +85 -0
  53. package/dist/router/virtual-router/sticky-session-store.js +206 -57
  54. package/dist/router/virtual-router/stop-message-stage-template-files.d.ts +12 -0
  55. package/dist/router/virtual-router/stop-message-stage-template-files.js +67 -0
  56. package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
  57. package/dist/router/virtual-router/stop-message-state-sync.js +5 -0
  58. package/dist/router/virtual-router/token-file-scanner.d.ts +9 -0
  59. package/dist/router/virtual-router/token-file-scanner.js +64 -3
  60. package/dist/router/virtual-router/tool-signals.d.ts +5 -0
  61. package/dist/router/virtual-router/tool-signals.js +42 -3
  62. package/dist/router/virtual-router/types.d.ts +19 -1
  63. package/dist/router/virtual-router/types.js +1 -0
  64. package/dist/servertool/clock/config.d.ts +1 -1
  65. package/dist/servertool/clock/config.js +27 -4
  66. package/dist/servertool/clock/state.js +41 -2
  67. package/dist/servertool/clock/task-store.d.ts +2 -2
  68. package/dist/servertool/clock/task-store.js +1 -1
  69. package/dist/servertool/clock/tasks.d.ts +3 -1
  70. package/dist/servertool/clock/tasks.js +209 -18
  71. package/dist/servertool/clock/types.d.ts +17 -0
  72. package/dist/servertool/continue-execution/log.d.ts +3 -0
  73. package/dist/servertool/continue-execution/log.js +13 -0
  74. package/dist/servertool/engine.js +414 -68
  75. package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +6 -6
  76. package/dist/servertool/handlers/clock-auto.js +54 -71
  77. package/dist/servertool/handlers/clock.js +121 -6
  78. package/dist/servertool/handlers/continue-execution.d.ts +1 -0
  79. package/dist/servertool/handlers/continue-execution.js +91 -0
  80. package/dist/servertool/handlers/followup-request-builder.js +13 -0
  81. package/dist/servertool/handlers/gemini-empty-reply-continue.js +1 -1
  82. package/dist/servertool/handlers/iflow-model-error-retry.js +1 -1
  83. package/dist/servertool/handlers/recursive-detection-guard.js +1 -1
  84. package/dist/servertool/handlers/stop-message-auto.js +386 -257
  85. package/dist/servertool/handlers/stop-message-stage-policy.d.ts +43 -0
  86. package/dist/servertool/handlers/stop-message-stage-policy.js +684 -0
  87. package/dist/servertool/handlers/vision.js +1 -1
  88. package/dist/servertool/log/progress-file.d.ts +14 -0
  89. package/dist/servertool/log/progress-file.js +88 -0
  90. package/dist/servertool/pre-command-hooks.d.ts +17 -0
  91. package/dist/servertool/pre-command-hooks.js +491 -0
  92. package/dist/servertool/registry.d.ts +23 -6
  93. package/dist/servertool/registry.js +66 -1
  94. package/dist/servertool/server-side-tools.d.ts +1 -0
  95. package/dist/servertool/server-side-tools.js +216 -14
  96. package/dist/servertool/stop-gateway-context.d.ts +14 -0
  97. package/dist/servertool/stop-gateway-context.js +167 -0
  98. package/dist/servertool/stop-message-compare-context.d.ts +24 -0
  99. package/dist/servertool/stop-message-compare-context.js +133 -0
  100. package/dist/servertool/types.d.ts +12 -0
  101. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -0
  102. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +36 -1
  103. package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +3 -0
  104. package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +3 -0
  105. package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +118 -1
  106. package/dist/tools/apply-patch/args-normalizer/default-actions.js +1 -1
  107. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  import { Readable } from 'node:stream';
2
2
  import { isJsonObject, jsonClone } from '../types/json.js';
3
3
  import { VirtualRouterEngine } from '../../../router/virtual-router/engine.js';
4
+ import { parseRoutingInstructions } from '../../../router/virtual-router/routing-instructions.js';
4
5
  import { providerErrorCenter } from '../../../router/virtual-router/error-center.js';
5
6
  import { providerSuccessCenter } from '../../../router/virtual-router/success-center.js';
6
7
  import { defaultSseCodecRegistry } from '../../../sse/index.js';
@@ -108,6 +109,83 @@ function extractHubShadowCompareConfig(metadata) {
108
109
  }
109
110
  return { baselineMode: modeCandidate };
110
111
  }
112
+ const PASSTHROUGH_CANONICAL_CHAT_KEYS = new Set([
113
+ 'model',
114
+ 'messages',
115
+ 'tools',
116
+ 'parameters',
117
+ 'metadata',
118
+ 'semantics',
119
+ 'stream'
120
+ ]);
121
+ function cloneJsonRecord(payload) {
122
+ try {
123
+ return jsonClone(payload);
124
+ }
125
+ catch {
126
+ return { ...payload };
127
+ }
128
+ }
129
+ function collectPassthroughTodoTopLevelKeys(payload) {
130
+ return Object.keys(payload)
131
+ .filter((key) => !PASSTHROUGH_CANONICAL_CHAT_KEYS.has(key))
132
+ .sort();
133
+ }
134
+ function buildPassthroughAudit(rawInbound, providerProtocol) {
135
+ return {
136
+ raw: {
137
+ inbound: cloneJsonRecord(rawInbound)
138
+ },
139
+ todo: {
140
+ inbound: {
141
+ unmappedTopLevelKeys: collectPassthroughTodoTopLevelKeys(rawInbound),
142
+ providerProtocol,
143
+ note: 'passthrough_mode_parse_record_only'
144
+ }
145
+ }
146
+ };
147
+ }
148
+ function annotatePassthroughGovernanceSkip(audit) {
149
+ const todo = audit.todo && typeof audit.todo === 'object' && !Array.isArray(audit.todo)
150
+ ? audit.todo
151
+ : (audit.todo = {});
152
+ todo.governance = {
153
+ skipped: true,
154
+ reason: 'process_mode_passthrough'
155
+ };
156
+ }
157
+ function attachPassthroughProviderInputAudit(audit, providerPayload, providerProtocol) {
158
+ const raw = audit.raw && typeof audit.raw === 'object' && !Array.isArray(audit.raw)
159
+ ? audit.raw
160
+ : (audit.raw = {});
161
+ raw.providerInput = cloneJsonRecord(providerPayload);
162
+ const todo = audit.todo && typeof audit.todo === 'object' && !Array.isArray(audit.todo)
163
+ ? audit.todo
164
+ : (audit.todo = {});
165
+ todo.outbound = {
166
+ unmappedTopLevelKeys: collectPassthroughTodoTopLevelKeys(providerPayload),
167
+ providerProtocol,
168
+ note: 'provider_payload_not_mapped_back_to_chat_semantics'
169
+ };
170
+ }
171
+ function hasInstructionRequestedPassthrough(messages) {
172
+ if (!Array.isArray(messages) || messages.length === 0) {
173
+ return false;
174
+ }
175
+ try {
176
+ const instructions = parseRoutingInstructions(messages);
177
+ return instructions.some((instruction) => instruction.processMode === 'passthrough');
178
+ }
179
+ catch {
180
+ return false;
181
+ }
182
+ }
183
+ function resolveActiveProcessMode(baseMode, messages) {
184
+ if (baseMode === 'passthrough') {
185
+ return 'passthrough';
186
+ }
187
+ return hasInstructionRequestedPassthrough(messages) ? 'passthrough' : 'chat';
188
+ }
111
189
  export class HubPipeline {
112
190
  routerEngine;
113
191
  config;
@@ -277,6 +355,15 @@ export class HubPipeline {
277
355
  catch {
278
356
  // best-effort: do not block request handling due to metadata propagation failures
279
357
  }
358
+ const activeProcessMode = resolveActiveProcessMode(normalized.processMode, standardizedRequest.messages);
359
+ if (activeProcessMode !== normalized.processMode) {
360
+ normalized.processMode = activeProcessMode;
361
+ normalized.metadata = normalized.metadata || {};
362
+ normalized.metadata.processMode = activeProcessMode;
363
+ }
364
+ const passthroughAudit = activeProcessMode === 'passthrough'
365
+ ? buildPassthroughAudit(rawRequest, normalized.providerProtocol)
366
+ : undefined;
280
367
  const inboundEnd = Date.now();
281
368
  const nodeResults = [];
282
369
  nodeResults.push({
@@ -313,7 +400,7 @@ export class HubPipeline {
313
400
  }
314
401
  normalized.metadata = metaBase;
315
402
  let processedRequest;
316
- if (normalized.processMode !== 'passthrough') {
403
+ if (activeProcessMode !== 'passthrough') {
317
404
  assertNoMappableSemanticsInMetadata(metaBase, 'chat_process.request.entry');
318
405
  const processResult = await runReqProcessStage1ToolGovernance({
319
406
  request: standardizedRequest,
@@ -339,6 +426,20 @@ export class HubPipeline {
339
426
  nodeResults.push(this.convertProcessNodeResult('chat_process.req.stage4.tool_governance', processResult.nodeResult));
340
427
  }
341
428
  }
429
+ else {
430
+ nodeResults.push({
431
+ id: 'chat_process.req.stage4.tool_governance',
432
+ success: true,
433
+ metadata: {
434
+ node: 'chat_process.req.stage4.tool_governance',
435
+ skipped: true,
436
+ reason: 'process_mode_passthrough_parse_record_only'
437
+ }
438
+ });
439
+ if (passthroughAudit) {
440
+ annotatePassthroughGovernanceSkip(passthroughAudit);
441
+ }
442
+ }
342
443
  let workingRequest = processedRequest ?? standardizedRequest;
343
444
  // 使用与 VirtualRouter 一致的 tiktoken 计数逻辑,对标准化请求进行一次
344
445
  // 上下文 token 估算,供后续 usage 归一化与统计使用。
@@ -405,9 +506,15 @@ export class HubPipeline {
405
506
  stageRecorder: inboundRecorder
406
507
  });
407
508
  const stopMessageState = this.routerEngine.getStopMessageState(metadataInput);
408
- if (stopMessageState && normalized.metadata && typeof normalized.metadata === 'object') {
509
+ const preCommandState = this.routerEngine.getPreCommandState(metadataInput);
510
+ if ((stopMessageState || preCommandState) && normalized.metadata && typeof normalized.metadata === 'object') {
409
511
  const rt = ensureRuntimeMetadata(normalized.metadata);
410
- rt.stopMessageState = stopMessageState;
512
+ if (stopMessageState) {
513
+ rt.stopMessageState = stopMessageState;
514
+ }
515
+ if (preCommandState) {
516
+ rt.preCommandState = preCommandState;
517
+ }
411
518
  }
412
519
  // Emit virtual router hit log for debugging (orange [virtual-router] ...)
413
520
  try {
@@ -423,21 +530,15 @@ export class HubPipeline {
423
530
  // logging must not break routing
424
531
  }
425
532
  const outboundStream = this.resolveOutboundStreamIntent(routing.target?.streaming);
426
- workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream);
533
+ workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream, activeProcessMode);
427
534
  const outboundAdapterContext = this.buildAdapterContext(normalized, routing.target);
428
535
  if (routing.target?.compatibilityProfile) {
429
536
  outboundAdapterContext.compatibilityProfile = routing.target.compatibilityProfile;
430
537
  }
431
538
  const outboundProtocol = outboundAdapterContext.providerProtocol;
432
- const protocolSwitch = outboundProtocol !== normalized.providerProtocol;
433
- const outboundHooks = protocolSwitch ? this.resolveProtocolHooks(outboundProtocol) : hooks;
434
- if (!outboundHooks) {
435
- throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
436
- }
437
- const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : semanticMapper;
438
- const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : formatAdapter;
439
- const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
440
- const outboundContextSnapshot = protocolSwitch ? undefined : contextSnapshot;
539
+ if (activeProcessMode === 'passthrough' && outboundProtocol !== normalized.providerProtocol) {
540
+ throw new Error(`[HubPipeline] passthrough requires matching protocols: entry=${normalized.providerProtocol}, target=${outboundProtocol}`);
541
+ }
441
542
  // Snapshots must be grouped by entry endpoint (client-facing protocol), not by provider protocol.
442
543
  // Otherwise one request would be split across multiple folders (e.g. openai-responses + anthropic-messages),
443
544
  // which breaks codex-samples correlation.
@@ -446,83 +547,118 @@ export class HubPipeline {
446
547
  });
447
548
  const outboundStart = Date.now();
448
549
  let providerPayload;
449
- const outboundStage1 = await runReqOutboundStage1SemanticMap({
450
- request: workingRequest,
451
- adapterContext: outboundAdapterContext,
452
- semanticMapper: outboundSemanticMapper,
453
- contextSnapshot: outboundContextSnapshot,
454
- contextMetadataKey: outboundContextMetadataKey,
455
- stageRecorder: outboundRecorder
456
- });
457
- let formattedPayload = await runReqOutboundStage2FormatBuild({
458
- formatEnvelope: outboundStage1.formatEnvelope,
459
- adapterContext: outboundAdapterContext,
460
- formatAdapter: outboundFormatAdapter,
461
- stageRecorder: outboundRecorder
462
- });
463
- formattedPayload = await runReqOutboundStage3Compat({
464
- payload: formattedPayload,
465
- adapterContext: outboundAdapterContext,
466
- stageRecorder: outboundRecorder
467
- });
468
550
  let shadowBaselineProviderPayload;
469
- if (shadowCompareBaselineMode) {
470
- const baselinePolicy = {
471
- ...(effectivePolicy ?? {}),
472
- mode: shadowCompareBaselineMode
473
- };
474
- // Compute a baseline provider payload in the *same execution*, without recording
475
- // snapshots/diffs and without re-running the full pipeline. This avoids side effects
476
- // (conversation store, followup captures, etc.) that a second execute() would trigger.
477
- const baselineFormatted = typeof globalThis.structuredClone === 'function'
478
- ? globalThis.structuredClone(formattedPayload)
479
- : jsonClone(formattedPayload);
480
- let baselinePayload = applyHubProviderOutboundPolicy({
481
- policy: baselinePolicy,
551
+ if (activeProcessMode === 'passthrough') {
552
+ providerPayload = cloneJsonRecord(rawRequest);
553
+ if (typeof outboundStream === 'boolean') {
554
+ providerPayload.stream = outboundStream;
555
+ }
556
+ if (passthroughAudit) {
557
+ attachPassthroughProviderInputAudit(passthroughAudit, providerPayload, outboundProtocol);
558
+ }
559
+ }
560
+ else {
561
+ const protocolSwitch = outboundProtocol !== normalized.providerProtocol;
562
+ const outboundHooks = protocolSwitch ? this.resolveProtocolHooks(outboundProtocol) : hooks;
563
+ if (!outboundHooks) {
564
+ throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
565
+ }
566
+ const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : semanticMapper;
567
+ const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : formatAdapter;
568
+ const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
569
+ const outboundContextSnapshot = protocolSwitch ? undefined : contextSnapshot;
570
+ const outboundStage1 = await runReqOutboundStage1SemanticMap({
571
+ request: workingRequest,
572
+ adapterContext: outboundAdapterContext,
573
+ semanticMapper: outboundSemanticMapper,
574
+ contextSnapshot: outboundContextSnapshot,
575
+ contextMetadataKey: outboundContextMetadataKey,
576
+ stageRecorder: outboundRecorder
577
+ });
578
+ let formattedPayload = await runReqOutboundStage2FormatBuild({
579
+ formatEnvelope: outboundStage1.formatEnvelope,
580
+ adapterContext: outboundAdapterContext,
581
+ formatAdapter: outboundFormatAdapter,
582
+ stageRecorder: outboundRecorder
583
+ });
584
+ formattedPayload = await runReqOutboundStage3Compat({
585
+ payload: formattedPayload,
586
+ adapterContext: outboundAdapterContext,
587
+ stageRecorder: outboundRecorder
588
+ });
589
+ if (shadowCompareBaselineMode) {
590
+ const baselinePolicy = {
591
+ ...(effectivePolicy ?? {}),
592
+ mode: shadowCompareBaselineMode
593
+ };
594
+ // Compute a baseline provider payload in the *same execution*, without recording
595
+ // snapshots/diffs and without re-running the full pipeline. This avoids side effects
596
+ // (conversation store, followup captures, etc.) that a second execute() would trigger.
597
+ const baselineFormatted = typeof globalThis.structuredClone === 'function'
598
+ ? globalThis.structuredClone(formattedPayload)
599
+ : jsonClone(formattedPayload);
600
+ let baselinePayload = applyHubProviderOutboundPolicy({
601
+ policy: baselinePolicy,
602
+ providerProtocol: outboundProtocol,
603
+ compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
604
+ ? outboundAdapterContext.compatibilityProfile
605
+ : undefined,
606
+ payload: baselineFormatted,
607
+ stageRecorder: undefined,
608
+ requestId: normalized.id
609
+ });
610
+ baselinePayload = applyProviderOutboundToolSurface({
611
+ config: this.config.toolSurface,
612
+ providerProtocol: outboundProtocol,
613
+ payload: baselinePayload,
614
+ stageRecorder: undefined,
615
+ requestId: normalized.id
616
+ });
617
+ shadowBaselineProviderPayload = baselinePayload;
618
+ }
619
+ // Phase 0/1: observe provider outbound payload violations before any enforcement rewrites.
620
+ // This provides black-box visibility into what the pipeline would have sent upstream.
621
+ recordHubPolicyObservation({
622
+ policy: effectivePolicy,
623
+ providerProtocol: outboundProtocol,
624
+ compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
625
+ ? outboundAdapterContext.compatibilityProfile
626
+ : undefined,
627
+ payload: formattedPayload,
628
+ stageRecorder: outboundRecorder,
629
+ requestId: normalized.id
630
+ });
631
+ providerPayload = applyHubProviderOutboundPolicy({
632
+ policy: effectivePolicy,
482
633
  providerProtocol: outboundProtocol,
483
- payload: baselineFormatted,
484
- stageRecorder: undefined,
634
+ compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
635
+ ? outboundAdapterContext.compatibilityProfile
636
+ : undefined,
637
+ payload: formattedPayload,
638
+ stageRecorder: outboundRecorder,
485
639
  requestId: normalized.id
486
640
  });
487
- baselinePayload = applyProviderOutboundToolSurface({
641
+ providerPayload = applyProviderOutboundToolSurface({
488
642
  config: this.config.toolSurface,
489
643
  providerProtocol: outboundProtocol,
490
- payload: baselinePayload,
491
- stageRecorder: undefined,
644
+ payload: providerPayload,
645
+ stageRecorder: outboundRecorder,
646
+ requestId: normalized.id
647
+ });
648
+ recordHubPolicyObservation({
649
+ policy: effectivePolicy,
650
+ providerProtocol: outboundProtocol,
651
+ compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
652
+ ? outboundAdapterContext.compatibilityProfile
653
+ : undefined,
654
+ payload: providerPayload,
655
+ stageRecorder: outboundRecorder,
492
656
  requestId: normalized.id
493
657
  });
494
- shadowBaselineProviderPayload = baselinePayload;
658
+ if (passthroughAudit) {
659
+ attachPassthroughProviderInputAudit(passthroughAudit, providerPayload, outboundProtocol);
660
+ }
495
661
  }
496
- // Phase 0/1: observe provider outbound payload violations before any enforcement rewrites.
497
- // This provides black-box visibility into what the pipeline would have sent upstream.
498
- recordHubPolicyObservation({
499
- policy: effectivePolicy,
500
- providerProtocol: outboundProtocol,
501
- payload: formattedPayload,
502
- stageRecorder: outboundRecorder,
503
- requestId: normalized.id
504
- });
505
- providerPayload = applyHubProviderOutboundPolicy({
506
- policy: effectivePolicy,
507
- providerProtocol: outboundProtocol,
508
- payload: formattedPayload,
509
- stageRecorder: outboundRecorder,
510
- requestId: normalized.id
511
- });
512
- providerPayload = applyProviderOutboundToolSurface({
513
- config: this.config.toolSurface,
514
- providerProtocol: outboundProtocol,
515
- payload: providerPayload,
516
- stageRecorder: outboundRecorder,
517
- requestId: normalized.id
518
- });
519
- recordHubPolicyObservation({
520
- policy: effectivePolicy,
521
- providerProtocol: outboundProtocol,
522
- payload: providerPayload,
523
- stageRecorder: outboundRecorder,
524
- requestId: normalized.id
525
- });
526
662
  const outboundEnd = Date.now();
527
663
  nodeResults.push({
528
664
  id: 'req_outbound',
@@ -570,6 +706,7 @@ export class HubPipeline {
570
706
  providerProtocol: outboundProtocol,
571
707
  stream: normalized.stream,
572
708
  processMode: normalized.processMode,
709
+ ...(passthroughAudit ? { passthroughAudit } : {}),
573
710
  routeHint: normalized.routeHint,
574
711
  target: routing.target,
575
712
  ...(typeof outboundStream === 'boolean' ? { providerStream: outboundStream } : {}),
@@ -702,6 +839,15 @@ export class HubPipeline {
702
839
  }
703
840
  normalized.metadata = metaBase;
704
841
  const standardizedRequest = standardizedRequestBase;
842
+ const activeProcessMode = resolveActiveProcessMode(normalized.processMode, standardizedRequest.messages);
843
+ if (activeProcessMode !== normalized.processMode) {
844
+ normalized.processMode = activeProcessMode;
845
+ normalized.metadata = normalized.metadata || {};
846
+ normalized.metadata.processMode = activeProcessMode;
847
+ }
848
+ const passthroughAudit = activeProcessMode === 'passthrough'
849
+ ? buildPassthroughAudit(rawPayload, normalized.providerProtocol)
850
+ : undefined;
705
851
  // Semantic Gate (chat_process entry): lift any mappable protocol semantics from metadata into request.semantics.
706
852
  // This is the last chance before entering chat_process; after this point we fail-fast on banned metadata keys.
707
853
  try {
@@ -739,7 +885,7 @@ export class HubPipeline {
739
885
  disableSnapshots: normalized.disableSnapshots === true
740
886
  });
741
887
  let processedRequest;
742
- if (normalized.processMode !== 'passthrough') {
888
+ if (activeProcessMode !== 'passthrough') {
743
889
  assertNoMappableSemanticsInMetadata(metaBase, 'chat_process.request.entry');
744
890
  const processResult = await runReqProcessStage1ToolGovernance({
745
891
  request: standardizedRequest,
@@ -765,6 +911,20 @@ export class HubPipeline {
765
911
  nodeResults.push(this.convertProcessNodeResult('chat_process.req.stage4.tool_governance', processResult.nodeResult));
766
912
  }
767
913
  }
914
+ else {
915
+ nodeResults.push({
916
+ id: 'chat_process.req.stage4.tool_governance',
917
+ success: true,
918
+ metadata: {
919
+ node: 'chat_process.req.stage4.tool_governance',
920
+ skipped: true,
921
+ reason: 'process_mode_passthrough_parse_record_only'
922
+ }
923
+ });
924
+ if (passthroughAudit) {
925
+ annotatePassthroughGovernanceSkip(passthroughAudit);
926
+ }
927
+ }
768
928
  let workingRequest = processedRequest ?? standardizedRequest;
769
929
  // Token estimate for stats/diagnostics (best-effort).
770
930
  try {
@@ -827,9 +987,15 @@ export class HubPipeline {
827
987
  stageRecorder
828
988
  });
829
989
  const stopMessageState = this.routerEngine.getStopMessageState(metadataInput);
830
- if (stopMessageState && normalized.metadata && typeof normalized.metadata === 'object') {
990
+ const preCommandState = this.routerEngine.getPreCommandState(metadataInput);
991
+ if ((stopMessageState || preCommandState) && normalized.metadata && typeof normalized.metadata === 'object') {
831
992
  const rt = ensureRuntimeMetadata(normalized.metadata);
832
- rt.stopMessageState = stopMessageState;
993
+ if (stopMessageState) {
994
+ rt.stopMessageState = stopMessageState;
995
+ }
996
+ if (preCommandState) {
997
+ rt.preCommandState = preCommandState;
998
+ }
833
999
  }
834
1000
  // Emit virtual router hit log for debugging (same as inbound path).
835
1001
  try {
@@ -845,75 +1011,101 @@ export class HubPipeline {
845
1011
  // ignore
846
1012
  }
847
1013
  const outboundStream = this.resolveOutboundStreamIntent(routing.target?.streaming);
848
- workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream);
1014
+ workingRequest = this.applyOutboundStreamPreference(workingRequest, outboundStream, activeProcessMode);
849
1015
  const outboundAdapterContext = this.buildAdapterContext(normalized, routing.target);
850
1016
  if (routing.target?.compatibilityProfile) {
851
1017
  outboundAdapterContext.compatibilityProfile = routing.target.compatibilityProfile;
852
1018
  }
853
1019
  const outboundProtocol = outboundAdapterContext.providerProtocol;
854
- const protocolSwitch = outboundProtocol !== normalized.providerProtocol;
855
- const outboundHooks = protocolSwitch ? this.resolveProtocolHooks(outboundProtocol) : hooks;
856
- if (!outboundHooks) {
857
- throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
858
- }
859
- const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : hooks.createSemanticMapper();
860
- const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : hooks.createFormatAdapter();
861
- const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
862
- const outboundContextSnapshot = undefined;
1020
+ if (activeProcessMode === 'passthrough' && outboundProtocol !== normalized.providerProtocol) {
1021
+ throw new Error(`[HubPipeline] passthrough requires matching protocols: entry=${normalized.providerProtocol}, target=${outboundProtocol}`);
1022
+ }
863
1023
  const outboundRecorder = this.maybeCreateStageRecorder(outboundAdapterContext, normalized.entryEndpoint, {
864
1024
  disableSnapshots: normalized.disableSnapshots === true
865
1025
  });
866
1026
  const outboundStart = Date.now();
867
1027
  let providerPayload;
868
- const outboundStage1 = await runReqOutboundStage1SemanticMap({
869
- request: workingRequest,
870
- adapterContext: outboundAdapterContext,
871
- semanticMapper: outboundSemanticMapper,
872
- contextSnapshot: outboundContextSnapshot,
873
- contextMetadataKey: outboundContextMetadataKey,
874
- stageRecorder: outboundRecorder
875
- });
876
- let formattedPayload = await runReqOutboundStage2FormatBuild({
877
- formatEnvelope: outboundStage1.formatEnvelope,
878
- adapterContext: outboundAdapterContext,
879
- formatAdapter: outboundFormatAdapter,
880
- stageRecorder: outboundRecorder
881
- });
882
- formattedPayload = await runReqOutboundStage3Compat({
883
- payload: formattedPayload,
884
- adapterContext: outboundAdapterContext,
885
- stageRecorder: outboundRecorder
886
- });
887
- // Phase 0/1: observe + enforce provider outbound policy and tool surface (same as inbound path).
888
- const effectivePolicy = normalized.policyOverride ?? this.config.policy;
889
- recordHubPolicyObservation({
890
- policy: effectivePolicy,
891
- providerProtocol: outboundProtocol,
892
- payload: formattedPayload,
893
- stageRecorder: outboundRecorder,
894
- requestId: normalized.id
895
- });
896
- providerPayload = applyHubProviderOutboundPolicy({
897
- policy: effectivePolicy,
898
- providerProtocol: outboundProtocol,
899
- payload: formattedPayload,
900
- stageRecorder: outboundRecorder,
901
- requestId: normalized.id
902
- });
903
- providerPayload = applyProviderOutboundToolSurface({
904
- config: this.config.toolSurface,
905
- providerProtocol: outboundProtocol,
906
- payload: providerPayload,
907
- stageRecorder: outboundRecorder,
908
- requestId: normalized.id
909
- });
910
- recordHubPolicyObservation({
911
- policy: effectivePolicy,
912
- providerProtocol: outboundProtocol,
913
- payload: providerPayload,
914
- stageRecorder: outboundRecorder,
915
- requestId: normalized.id
916
- });
1028
+ if (activeProcessMode === 'passthrough') {
1029
+ providerPayload = cloneJsonRecord(rawPayloadInput);
1030
+ if (typeof outboundStream === 'boolean') {
1031
+ providerPayload.stream = outboundStream;
1032
+ }
1033
+ if (passthroughAudit) {
1034
+ attachPassthroughProviderInputAudit(passthroughAudit, providerPayload, outboundProtocol);
1035
+ }
1036
+ }
1037
+ else {
1038
+ const protocolSwitch = outboundProtocol !== normalized.providerProtocol;
1039
+ const outboundHooks = protocolSwitch ? this.resolveProtocolHooks(outboundProtocol) : hooks;
1040
+ if (!outboundHooks) {
1041
+ throw new Error(`[HubPipeline] Unsupported provider protocol for hub pipeline: ${outboundProtocol}`);
1042
+ }
1043
+ const outboundSemanticMapper = protocolSwitch ? outboundHooks.createSemanticMapper() : hooks.createSemanticMapper();
1044
+ const outboundFormatAdapter = protocolSwitch ? outboundHooks.createFormatAdapter() : hooks.createFormatAdapter();
1045
+ const outboundContextMetadataKey = protocolSwitch ? outboundHooks.contextMetadataKey : hooks.contextMetadataKey;
1046
+ const outboundContextSnapshot = undefined;
1047
+ const outboundStage1 = await runReqOutboundStage1SemanticMap({
1048
+ request: workingRequest,
1049
+ adapterContext: outboundAdapterContext,
1050
+ semanticMapper: outboundSemanticMapper,
1051
+ contextSnapshot: outboundContextSnapshot,
1052
+ contextMetadataKey: outboundContextMetadataKey,
1053
+ stageRecorder: outboundRecorder
1054
+ });
1055
+ let formattedPayload = await runReqOutboundStage2FormatBuild({
1056
+ formatEnvelope: outboundStage1.formatEnvelope,
1057
+ adapterContext: outboundAdapterContext,
1058
+ formatAdapter: outboundFormatAdapter,
1059
+ stageRecorder: outboundRecorder
1060
+ });
1061
+ formattedPayload = await runReqOutboundStage3Compat({
1062
+ payload: formattedPayload,
1063
+ adapterContext: outboundAdapterContext,
1064
+ stageRecorder: outboundRecorder
1065
+ });
1066
+ // Phase 0/1: observe + enforce provider outbound policy and tool surface (same as inbound path).
1067
+ const effectivePolicy = normalized.policyOverride ?? this.config.policy;
1068
+ recordHubPolicyObservation({
1069
+ policy: effectivePolicy,
1070
+ providerProtocol: outboundProtocol,
1071
+ compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
1072
+ ? outboundAdapterContext.compatibilityProfile
1073
+ : undefined,
1074
+ payload: formattedPayload,
1075
+ stageRecorder: outboundRecorder,
1076
+ requestId: normalized.id
1077
+ });
1078
+ providerPayload = applyHubProviderOutboundPolicy({
1079
+ policy: effectivePolicy,
1080
+ providerProtocol: outboundProtocol,
1081
+ compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
1082
+ ? outboundAdapterContext.compatibilityProfile
1083
+ : undefined,
1084
+ payload: formattedPayload,
1085
+ stageRecorder: outboundRecorder,
1086
+ requestId: normalized.id
1087
+ });
1088
+ providerPayload = applyProviderOutboundToolSurface({
1089
+ config: this.config.toolSurface,
1090
+ providerProtocol: outboundProtocol,
1091
+ payload: providerPayload,
1092
+ stageRecorder: outboundRecorder,
1093
+ requestId: normalized.id
1094
+ });
1095
+ recordHubPolicyObservation({
1096
+ policy: effectivePolicy,
1097
+ providerProtocol: outboundProtocol,
1098
+ compatibilityProfile: typeof outboundAdapterContext.compatibilityProfile === 'string'
1099
+ ? outboundAdapterContext.compatibilityProfile
1100
+ : undefined,
1101
+ payload: providerPayload,
1102
+ stageRecorder: outboundRecorder,
1103
+ requestId: normalized.id
1104
+ });
1105
+ if (passthroughAudit) {
1106
+ attachPassthroughProviderInputAudit(passthroughAudit, providerPayload, outboundProtocol);
1107
+ }
1108
+ }
917
1109
  const outboundEnd = Date.now();
918
1110
  nodeResults.push({
919
1111
  id: 'req_outbound',
@@ -943,6 +1135,7 @@ export class HubPipeline {
943
1135
  providerProtocol: outboundProtocol,
944
1136
  stream: normalized.stream,
945
1137
  processMode: normalized.processMode,
1138
+ ...(passthroughAudit ? { passthroughAudit } : {}),
946
1139
  routeHint: normalized.routeHint,
947
1140
  target: routing.target,
948
1141
  ...(typeof outboundStream === 'boolean' ? { providerStream: outboundStream } : {})
@@ -1075,6 +1268,17 @@ export class HubPipeline {
1075
1268
  toolCallIdStyle,
1076
1269
  ...(compatibilityProfile ? { compatibilityProfile } : {})
1077
1270
  };
1271
+ const targetDeepseek = isJsonObject(target?.deepseek)
1272
+ ? jsonClone(target.deepseek)
1273
+ : undefined;
1274
+ if (targetDeepseek) {
1275
+ adapterContext.deepseek = targetDeepseek;
1276
+ const rtCarrier = isJsonObject(adapterContext.__rt)
1277
+ ? { ...adapterContext.__rt }
1278
+ : {};
1279
+ rtCarrier.deepseek = targetDeepseek;
1280
+ adapterContext.__rt = rtCarrier;
1281
+ }
1078
1282
  const runtime = metadata.runtime;
1079
1283
  if (runtime && typeof runtime === 'object' && !Array.isArray(runtime)) {
1080
1284
  adapterContext.runtime = jsonClone(runtime);
@@ -1100,10 +1304,24 @@ export class HubPipeline {
1100
1304
  if (typeof metadata.assignedModelId === 'string') {
1101
1305
  adapterContext.modelId = metadata.assignedModelId;
1102
1306
  }
1307
+ const estimatedInputTokens = Number(metadata.estimatedInputTokens ??
1308
+ metadata.estimated_tokens ??
1309
+ metadata.estimatedTokens);
1310
+ if (Number.isFinite(estimatedInputTokens) && estimatedInputTokens > 0) {
1311
+ adapterContext.estimatedInputTokens = Math.max(1, Math.round(estimatedInputTokens));
1312
+ }
1103
1313
  const rt = cloneRuntimeMetadata(metadata);
1104
1314
  if (rt) {
1105
1315
  adapterContext.__rt = rt;
1106
1316
  }
1317
+ const capturedChatRequest = metadata.capturedChatRequest &&
1318
+ typeof metadata.capturedChatRequest === 'object' &&
1319
+ !Array.isArray(metadata.capturedChatRequest)
1320
+ ? jsonClone(metadata.capturedChatRequest)
1321
+ : undefined;
1322
+ if (capturedChatRequest) {
1323
+ adapterContext.capturedChatRequest = capturedChatRequest;
1324
+ }
1107
1325
  const sessionId = typeof metadata.sessionId === 'string'
1108
1326
  ? metadata.sessionId.trim()
1109
1327
  : '';
@@ -1324,10 +1542,13 @@ export class HubPipeline {
1324
1542
  }
1325
1543
  return undefined;
1326
1544
  }
1327
- applyOutboundStreamPreference(request, stream) {
1545
+ applyOutboundStreamPreference(request, stream, processMode) {
1328
1546
  if (!request || typeof request !== 'object') {
1329
1547
  return request;
1330
1548
  }
1549
+ if (processMode === 'passthrough' && stream === undefined) {
1550
+ return request;
1551
+ }
1331
1552
  const ops = [];
1332
1553
  if (typeof stream === 'boolean') {
1333
1554
  ops.push({ op: 'set_request_parameter_fields', fields: { stream } });