@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
@@ -302,16 +302,6 @@ export async function convertProviderResponse(options) {
302
302
  // 始终返回完整的 ChatCompletion JSON,便于在 llms 内部直接解析,而不是拿到
303
303
  // __sse_responses 可读流。
304
304
  const wantsStream = isFollowup ? false : options.wantsStream;
305
- try {
306
- // eslint-disable-next-line no-console
307
- console.log(`\x1b[38;5;33m[servertool][orchestrator][debug] requestId=${options.context.requestId} ` +
308
- `protocol=${options.providerProtocol} endpoint=${options.entryEndpoint} ` +
309
- `skipServerTools=${skipServerTools} hasInvoker=${Boolean(options.providerInvoker)} ` +
310
- `hasReenter=${Boolean(options.reenterPipeline)}\x1b[0m`);
311
- }
312
- catch {
313
- /* logging best-effort */
314
- }
315
305
  const displayModel = extractDisplayModel(options.context);
316
306
  const clientFacingRequestId = extractClientFacingRequestId(options.context) ?? options.context.requestId;
317
307
  const plan = PROVIDER_RESPONSE_REGISTRY[options.providerProtocol];
@@ -387,14 +377,6 @@ export async function convertProviderResponse(options) {
387
377
  // 使用新的 ChatEnvelope 级别的 servertool 实现
388
378
  let effectiveChatResponse = chatResponse;
389
379
  if (!skipServerTools && options.reenterPipeline) {
390
- try {
391
- // eslint-disable-next-line no-console
392
- console.log(`\x1b[38;5;33m[servertool][orchestrator] start requestId=${options.context.requestId} ` +
393
- `protocol=${options.providerProtocol} endpoint=${options.entryEndpoint}\x1b[0m`);
394
- }
395
- catch {
396
- /* logging best-effort */
397
- }
398
380
  const orchestration = await runServerToolOrchestration({
399
381
  chat: chatResponse,
400
382
  adapterContext: options.context,
@@ -406,15 +388,6 @@ export async function convertProviderResponse(options) {
406
388
  reenterPipeline: options.reenterPipeline
407
389
  });
408
390
  if (orchestration.executed) {
409
- const flowLabel = orchestration.flowId ?? 'servertool_flow';
410
- try {
411
- // eslint-disable-next-line no-console
412
- console.log(`\x1b[38;5;33m[servertool][orchestrator] completed requestId=${options.context.requestId} ` +
413
- `mode=${flowLabel}\x1b[0m`);
414
- }
415
- catch {
416
- /* logging best-effort */
417
- }
418
391
  effectiveChatResponse = orchestration.chat;
419
392
  recordStage(options.stageRecorder, 'chat_process.resp.stage5.servertool_orchestration', {
420
393
  executed: true,
@@ -424,14 +397,6 @@ export async function convertProviderResponse(options) {
424
397
  });
425
398
  }
426
399
  else {
427
- try {
428
- // eslint-disable-next-line no-console
429
- console.log(`\x1b[38;5;33m[servertool][orchestrator] skipped requestId=${options.context.requestId} ` +
430
- 'reason=no_servertool_match\x1b[0m');
431
- }
432
- catch {
433
- /* logging best-effort */
434
- }
435
400
  recordStage(options.stageRecorder, 'chat_process.resp.stage5.servertool_orchestration', {
436
401
  executed: false,
437
402
  inputShape: detectProviderResponseShape(chatResponse)
@@ -10,6 +10,8 @@ export interface ResponsesRequestContext extends Unknown {
10
10
  input?: BridgeInputItem[];
11
11
  include?: unknown;
12
12
  store?: unknown;
13
+ serviceTier?: unknown;
14
+ truncation?: unknown;
13
15
  toolChoice?: unknown;
14
16
  parallelToolCalls?: boolean;
15
17
  metadata?: JsonObject;
@@ -12,6 +12,7 @@ import { normalizeMessageReasoningTools } from '../shared/reasoning-tool-normali
12
12
  import { createBridgeActionState, runBridgeActionPipeline } from '../shared/bridge-actions.js';
13
13
  import { resolveBridgePolicy, resolvePolicyActions } from '../shared/bridge-policies.js';
14
14
  import { buildResponsesOutputFromChat } from '../shared/responses-output-builder.js';
15
+ import { consumeResponsesPayloadSnapshot, consumeResponsesPassthrough } from '../shared/responses-reasoning-registry.js';
15
16
  function isObject(v) {
16
17
  return !!v && typeof v === 'object' && !Array.isArray(v);
17
18
  }
@@ -50,11 +51,39 @@ function filterBridgeInputForUpstream(input, options) {
50
51
  export function captureResponsesContext(payload, dto) {
51
52
  const preservedInput = cloneBridgeEntries(payload.input);
52
53
  ensureBridgeInstructions(payload);
54
+ const inheritedParameters = payload.parameters && typeof payload.parameters === 'object'
55
+ ? { ...payload.parameters }
56
+ : {};
57
+ const topLevelParameterKeys = [
58
+ 'temperature',
59
+ 'top_p',
60
+ 'max_tokens',
61
+ 'max_output_tokens',
62
+ 'seed',
63
+ 'logit_bias',
64
+ 'user',
65
+ 'parallel_tool_calls',
66
+ 'tool_choice',
67
+ 'response_format',
68
+ 'service_tier',
69
+ 'truncation',
70
+ 'include',
71
+ 'store',
72
+ 'prompt_cache_key',
73
+ 'reasoning'
74
+ ];
75
+ for (const key of topLevelParameterKeys) {
76
+ if (payload[key] !== undefined && inheritedParameters[key] === undefined) {
77
+ inheritedParameters[key] = payload[key];
78
+ }
79
+ }
53
80
  const context = {
54
81
  requestId: dto?.route?.requestId,
55
82
  input: preservedInput,
56
83
  include: payload.include,
57
84
  store: payload.store,
85
+ serviceTier: payload.service_tier,
86
+ truncation: payload.truncation,
58
87
  toolChoice: payload.tool_choice,
59
88
  parallelToolCalls: typeof payload.parallel_tool_calls === 'boolean' ? payload.parallel_tool_calls : undefined,
60
89
  metadata: (payload.metadata && typeof payload.metadata === 'object') ? payload.metadata : undefined,
@@ -62,8 +91,8 @@ export function captureResponsesContext(payload, dto) {
62
91
  stream: typeof payload.stream === 'boolean' ? payload.stream : undefined,
63
92
  isChatPayload: Array.isArray(payload.messages)
64
93
  };
65
- if (payload.parameters && typeof payload.parameters === 'object') {
66
- context.parameters = { ...payload.parameters };
94
+ if (Object.keys(inheritedParameters).length) {
95
+ context.parameters = inheritedParameters;
67
96
  }
68
97
  if (typeof payload.instructions === 'string' && payload.instructions.trim().length) {
69
98
  context.systemInstruction = payload.instructions;
@@ -331,11 +360,14 @@ export function buildResponsesRequestFromChat(payload, ctx, extras) {
331
360
  out.tools = mergedTools;
332
361
  }
333
362
  const passthroughKeys = [
363
+ 'temperature',
334
364
  'tool_choice',
335
365
  'parallel_tool_calls',
336
366
  'response_format',
337
367
  'user',
338
368
  'top_p',
369
+ 'prompt_cache_key',
370
+ 'reasoning',
339
371
  'logit_bias',
340
372
  'seed'
341
373
  ];
@@ -473,6 +505,18 @@ export function buildResponsesRequestFromChat(payload, ctx, extras) {
473
505
  else if (metadataExtraFields?.response_format !== undefined && out.response_format === undefined) {
474
506
  out.response_format = metadataExtraFields.response_format;
475
507
  }
508
+ if (ctx?.serviceTier !== undefined && out.service_tier === undefined) {
509
+ out.service_tier = ctx.serviceTier;
510
+ }
511
+ else if (metadataExtraFields?.service_tier !== undefined && out.service_tier === undefined) {
512
+ out.service_tier = metadataExtraFields.service_tier;
513
+ }
514
+ if (ctx?.truncation !== undefined && out.truncation === undefined) {
515
+ out.truncation = ctx.truncation;
516
+ }
517
+ else if (metadataExtraFields?.truncation !== undefined && out.truncation === undefined) {
518
+ out.truncation = metadataExtraFields.truncation;
519
+ }
476
520
  if (ctx?.metadata && Object.keys(ctx.metadata).length) {
477
521
  out.metadata = { ...ctx.metadata };
478
522
  }
@@ -498,6 +542,12 @@ export function buildResponsesRequestFromChat(payload, ctx, extras) {
498
542
  'parallel_tool_calls',
499
543
  'tool_choice',
500
544
  'response_format',
545
+ 'service_tier',
546
+ 'truncation',
547
+ 'include',
548
+ 'store',
549
+ 'prompt_cache_key',
550
+ 'reasoning',
501
551
  'stream'
502
552
  ]);
503
553
  // Back-compat: StandardizedRequest uses max_tokens; map it to Responses max_output_tokens.
@@ -575,6 +625,95 @@ function extractMetadataExtraFields(metadata) {
575
625
  function isPlainObject(value) {
576
626
  return Boolean(value && typeof value === 'object' && !Array.isArray(value));
577
627
  }
628
+ function deepCloneRecord(value) {
629
+ try {
630
+ const structuredCloneImpl = globalThis.structuredClone;
631
+ if (typeof structuredCloneImpl === 'function') {
632
+ return structuredCloneImpl(value);
633
+ }
634
+ }
635
+ catch {
636
+ // ignore structuredClone failures
637
+ }
638
+ try {
639
+ return JSON.parse(JSON.stringify(value));
640
+ }
641
+ catch {
642
+ return { ...value };
643
+ }
644
+ }
645
+ function isMissingResponseField(value) {
646
+ if (value === undefined || value === null) {
647
+ return true;
648
+ }
649
+ if (Array.isArray(value)) {
650
+ return value.length === 0;
651
+ }
652
+ return false;
653
+ }
654
+ function mergeResponseOutputItems(baseOutput, sourceOutput) {
655
+ if (!Array.isArray(baseOutput) || !Array.isArray(sourceOutput)) {
656
+ return baseOutput;
657
+ }
658
+ const sourceById = new Map();
659
+ sourceOutput.forEach((entry) => {
660
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
661
+ return;
662
+ }
663
+ const id = typeof entry.id === 'string' ? entry.id.trim() : '';
664
+ if (!id.length) {
665
+ return;
666
+ }
667
+ sourceById.set(id, entry);
668
+ });
669
+ return baseOutput.map((entry, index) => {
670
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
671
+ return entry;
672
+ }
673
+ const baseItem = deepCloneRecord(entry);
674
+ const baseId = typeof baseItem.id === 'string' ? String(baseItem.id).trim() : '';
675
+ let sourceItem;
676
+ if (baseId.length) {
677
+ sourceItem = sourceById.get(baseId);
678
+ }
679
+ if (!sourceItem) {
680
+ const candidate = sourceOutput[index];
681
+ if (candidate && typeof candidate === 'object' && !Array.isArray(candidate)) {
682
+ sourceItem = candidate;
683
+ }
684
+ }
685
+ if (!sourceItem) {
686
+ return baseItem;
687
+ }
688
+ if (isMissingResponseField(baseItem.content) && sourceItem.content !== undefined) {
689
+ baseItem.content = deepCloneRecord({ value: sourceItem.content }).value;
690
+ }
691
+ if (isMissingResponseField(baseItem.summary) && sourceItem.summary !== undefined) {
692
+ baseItem.summary = deepCloneRecord({ value: sourceItem.summary }).value;
693
+ }
694
+ return baseItem;
695
+ });
696
+ }
697
+ function mergeResponseTopLevelFields(options) {
698
+ const merged = deepCloneRecord(options.base);
699
+ const source = options.source;
700
+ const sourceWins = options.sourceWinsKeys ?? [];
701
+ for (const key of sourceWins) {
702
+ if (source[key] !== undefined) {
703
+ merged[key] = deepCloneRecord({ value: source[key] }).value;
704
+ }
705
+ }
706
+ const passthroughKeys = ['metadata', 'temperature', 'top_p', 'prompt_cache_key', 'reasoning'];
707
+ for (const key of passthroughKeys) {
708
+ if (source[key] !== undefined && merged[key] === undefined) {
709
+ merged[key] = deepCloneRecord({ value: source[key] }).value;
710
+ }
711
+ }
712
+ if (merged.output !== undefined && source.output !== undefined) {
713
+ merged.output = mergeResponseOutputItems(merged.output, source.output);
714
+ }
715
+ return merged;
716
+ }
578
717
  export function buildResponsesPayloadFromChat(payload, context) {
579
718
  if (!payload || typeof payload !== 'object')
580
719
  return payload;
@@ -584,6 +723,11 @@ export function buildResponsesPayloadFromChat(payload, context) {
584
723
  if (response.object === 'response' && Array.isArray(response.output)) {
585
724
  return response;
586
725
  }
726
+ const snapshotLookupKey = resolveSnapshotLookupKey(response, context);
727
+ const snapshotPayload = snapshotLookupKey ? consumeResponsesPayloadSnapshot(snapshotLookupKey) : undefined;
728
+ const passthroughPayload = snapshotLookupKey ? consumeResponsesPassthrough(snapshotLookupKey) : undefined;
729
+ const sourceForRetention = (passthroughPayload && typeof passthroughPayload === 'object' ? passthroughPayload : undefined) ??
730
+ (snapshotPayload && typeof snapshotPayload === 'object' ? snapshotPayload : undefined);
587
731
  const hasChoicesArray = Array.isArray(response.choices);
588
732
  const choicesLength = hasChoicesArray ? response.choices.length : 0;
589
733
  // Graceful fallback for provider payloads that do not contain a valid
@@ -634,10 +778,17 @@ export function buildResponsesPayloadFromChat(payload, context) {
634
778
  else if (typeof context?.requestId === 'string') {
635
779
  out.request_id = context.requestId;
636
780
  }
637
- if (out.metadata) {
638
- stripInternalToolingMetadata(out.metadata);
781
+ const mergedFallback = sourceForRetention && typeof sourceForRetention === 'object'
782
+ ? mergeResponseTopLevelFields({
783
+ base: out,
784
+ source: sourceForRetention,
785
+ sourceWinsKeys: ['metadata', 'temperature', 'top_p', 'prompt_cache_key', 'reasoning']
786
+ })
787
+ : out;
788
+ if (mergedFallback.metadata) {
789
+ stripInternalToolingMetadata(mergedFallback.metadata);
639
790
  }
640
- return out;
791
+ return mergedFallback;
641
792
  }
642
793
  const canonical = canonicalizeChatResponseTools(response);
643
794
  const choices = Array.isArray(canonical?.choices) ? canonical.choices : [];
@@ -742,10 +893,17 @@ export function buildResponsesPayloadFromChat(payload, context) {
742
893
  else if (typeof context?.requestId === 'string') {
743
894
  out.request_id = context.requestId;
744
895
  }
745
- if (out.metadata) {
746
- stripInternalToolingMetadata(out.metadata);
896
+ const merged = sourceForRetention && typeof sourceForRetention === 'object'
897
+ ? mergeResponseTopLevelFields({
898
+ base: out,
899
+ source: sourceForRetention,
900
+ sourceWinsKeys: ['metadata', 'temperature', 'top_p', 'prompt_cache_key', 'reasoning']
901
+ })
902
+ : out;
903
+ if (merged.metadata) {
904
+ stripInternalToolingMetadata(merged.metadata);
747
905
  }
748
- return out;
906
+ return merged;
749
907
  }
750
908
  function normalizeResponsesToolCallArgumentsForClient(responsesPayload, context) {
751
909
  const toolsRaw = Array.isArray(context?.toolsRaw) ? context?.toolsRaw : [];
@@ -182,7 +182,8 @@ const ANTHROPIC_TOP_LEVEL_FIELDS = new Set([
182
182
  'max_output_tokens',
183
183
  'metadata',
184
184
  'stream',
185
- 'tool_choice'
185
+ 'tool_choice',
186
+ 'thinking'
186
187
  ]);
187
188
  export function normalizeAnthropicToolName(value) {
188
189
  if (typeof value !== 'string') {
@@ -951,6 +952,14 @@ export function buildAnthropicRequestFromOpenAIChat(chatReq) {
951
952
  if (normalizedToolChoice !== undefined) {
952
953
  out.tool_choice = normalizedToolChoice;
953
954
  }
955
+ if (requestBody.thinking !== undefined) {
956
+ try {
957
+ out.thinking = JSON.parse(JSON.stringify(requestBody.thinking));
958
+ }
959
+ catch {
960
+ out.thinking = requestBody.thinking;
961
+ }
962
+ }
954
963
  try {
955
964
  if (requestBody.metadata && typeof requestBody.metadata === 'object') {
956
965
  out.metadata = JSON.parse(JSON.stringify(requestBody.metadata));
@@ -1,7 +1,7 @@
1
1
  export declare const OPENAI_CHAT_ALLOWED_FIELDS: readonly ["messages", "tools", "tool_outputs", "model", "temperature", "top_p", "top_k", "max_tokens", "frequency_penalty", "presence_penalty", "logit_bias", "response_format", "parallel_tool_calls", "tool_choice", "seed", "user", "metadata", "stop", "stop_sequences", "stream"];
2
2
  export declare const ANTHROPIC_ALLOWED_FIELDS: readonly ["model", "messages", "tools", "system", "stop_sequences", "temperature", "top_p", "top_k", "max_tokens", "max_output_tokens", "metadata", "stream", "tool_choice"];
3
- export declare const OPENAI_RESPONSES_ALLOWED_FIELDS: readonly ["id", "object", "created_at", "model", "status", "input", "instructions", "output", "output_text", "required_action", "response_id", "previous_response_id", "tool_outputs", "tools", "metadata", "include", "store", "user", "response_format", "temperature", "top_p", "top_k", "max_tokens", "max_output_tokens", "logit_bias", "seed", "parallel_tool_calls", "tool_choice", "stream", "instructions_is_raw"];
3
+ export declare const OPENAI_RESPONSES_ALLOWED_FIELDS: readonly ["id", "object", "created_at", "model", "status", "input", "instructions", "output", "output_text", "required_action", "response_id", "previous_response_id", "tool_outputs", "tools", "metadata", "include", "store", "user", "response_format", "temperature", "top_p", "top_k", "max_tokens", "max_output_tokens", "logit_bias", "seed", "parallel_tool_calls", "tool_choice", "prompt_cache_key", "reasoning", "stream", "instructions_is_raw"];
4
4
  export declare const GEMINI_ALLOWED_FIELDS: readonly ["model", "contents", "systemInstruction", "system_instruction", "generationConfig", "generation_config", "safetySettings", "safety_settings", "metadata", "toolConfig", "tool_config", "tools", "tool_choice", "parallelToolCalls", "parallel_tool_calls", "responseMimeType", "response_mime_type", "stopSequences", "stop_sequences", "cachedContent", "prompt", "response", "candidates", "usageMetadata", "responseMetadata", "promptFeedback", "modelVersion", "client", "user", "stream"];
5
- export declare const OPENAI_RESPONSES_PARAMETERS_WRAPPER_ALLOW_KEYS: readonly ["temperature", "top_p", "max_output_tokens", "seed", "logit_bias", "user", "parallel_tool_calls", "tool_choice", "response_format", "stream", "stop", "stop_sequences", "modalities", "top_k"];
5
+ export declare const OPENAI_RESPONSES_PARAMETERS_WRAPPER_ALLOW_KEYS: readonly ["temperature", "top_p", "max_output_tokens", "seed", "logit_bias", "user", "parallel_tool_calls", "tool_choice", "response_format", "prompt_cache_key", "reasoning", "stream", "stop", "stop_sequences", "modalities", "top_k"];
6
6
  export declare const OPENAI_CHAT_PARAMETERS_WRAPPER_ALLOW_KEYS: readonly ["temperature", "top_p", "top_k", "max_tokens", "frequency_penalty", "presence_penalty", "logit_bias", "seed", "user", "parallel_tool_calls", "tool_choice", "response_format", "stream", "stop", "stop_sequences"];
7
7
  export declare const ANTHROPIC_PARAMETERS_WRAPPER_ALLOW_KEYS: readonly ["stop_sequences", "temperature", "top_p", "top_k", "max_tokens", "max_output_tokens", "metadata", "stream", "tool_choice"];
@@ -64,6 +64,8 @@ export const OPENAI_RESPONSES_ALLOWED_FIELDS = [
64
64
  'seed',
65
65
  'parallel_tool_calls',
66
66
  'tool_choice',
67
+ 'prompt_cache_key',
68
+ 'reasoning',
67
69
  'stream',
68
70
  'instructions_is_raw'
69
71
  ];
@@ -109,6 +111,8 @@ export const OPENAI_RESPONSES_PARAMETERS_WRAPPER_ALLOW_KEYS = [
109
111
  'parallel_tool_calls',
110
112
  'tool_choice',
111
113
  'response_format',
114
+ 'prompt_cache_key',
115
+ 'reasoning',
112
116
  'stream',
113
117
  'stop',
114
118
  'stop_sequences',
@@ -13,6 +13,100 @@ function isTruthyEnv(value) {
13
13
  const v = typeof value === 'string' ? value.trim().toLowerCase() : '';
14
14
  return v === '1' || v === 'true' || v === 'yes' || v === 'on';
15
15
  }
16
+ function isApplyPatchPayloadCandidate(value) {
17
+ if (typeof value !== 'string')
18
+ return false;
19
+ const text = value.trim();
20
+ if (!text)
21
+ return false;
22
+ return (text.startsWith('*** Begin Patch') ||
23
+ text.startsWith('*** Update File:') ||
24
+ text.startsWith('*** Add File:') ||
25
+ text.startsWith('*** Delete File:') ||
26
+ text.startsWith('--- a/') ||
27
+ text.startsWith('--- '));
28
+ }
29
+ function extractApplyPatchPayloadFromExecArgs(rawArgs) {
30
+ const argsStr = repairArgumentsToString(rawArgs);
31
+ let parsed;
32
+ try {
33
+ parsed = JSON.parse(argsStr);
34
+ }
35
+ catch {
36
+ parsed = parseLenient(argsStr);
37
+ }
38
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
39
+ return null;
40
+ }
41
+ const obj = parsed;
42
+ const commandValue = obj.command ?? obj.cmd;
43
+ if (Array.isArray(commandValue)) {
44
+ const tokens = commandValue.map((entry) => (typeof entry === 'string' ? entry : String(entry ?? '')));
45
+ if (!tokens.length)
46
+ return null;
47
+ const commandToken = tokens[0]?.trim().toLowerCase() || '';
48
+ const isApplyPatchCommand = commandToken === 'apply_patch' || commandToken.endsWith('/apply_patch') || commandToken.endsWith('\\apply_patch');
49
+ if (!isApplyPatchCommand) {
50
+ return null;
51
+ }
52
+ const patchText = tokens.slice(1).join('\n').trim();
53
+ return isApplyPatchPayloadCandidate(patchText) ? patchText : null;
54
+ }
55
+ if (typeof commandValue === 'string') {
56
+ const raw = commandValue.trim();
57
+ if (!raw)
58
+ return null;
59
+ if (!raw.toLowerCase().startsWith('apply_patch'))
60
+ return null;
61
+ const patchText = raw.slice('apply_patch'.length).trim();
62
+ return isApplyPatchPayloadCandidate(patchText) ? patchText : null;
63
+ }
64
+ return null;
65
+ }
66
+ function rewriteExecCommandApplyPatchCall(fn) {
67
+ if (!fn)
68
+ return false;
69
+ const currentName = typeof fn.name === 'string' ? String(fn.name).trim().toLowerCase() : '';
70
+ if (currentName !== 'exec_command')
71
+ return false;
72
+ const patch = extractApplyPatchPayloadFromExecArgs(fn.arguments);
73
+ if (!patch)
74
+ return false;
75
+ fn.name = 'apply_patch';
76
+ fn.arguments = JSON.stringify({ patch, input: '' });
77
+ return true;
78
+ }
79
+ const NESTED_APPLY_PATCH_POLICY_MARKER = '[Codex NestedApplyPatch Policy]';
80
+ function buildNestedApplyPatchPolicyNotice(rewriteCount) {
81
+ const count = Number.isFinite(rewriteCount) && rewriteCount > 0 ? Math.floor(rewriteCount) : 0;
82
+ return [
83
+ NESTED_APPLY_PATCH_POLICY_MARKER,
84
+ 'Forbidden usage detected: apply_patch must NEVER be called via exec_command or shell (detected=' + count + ').',
85
+ 'The call was auto-rewritten to apply_patch for compatibility this turn.',
86
+ 'Next action rule: call apply_patch directly; do not nest apply_patch inside exec_command/shell.',
87
+ '禁止通过 exec_command/shell 嵌套调用 apply_patch;本轮已自动改写,后续必须直接调用 apply_patch。'
88
+ ].join('\n');
89
+ }
90
+ function injectNestedApplyPatchPolicyNotice(messages, rewriteCount) {
91
+ if (!Array.isArray(messages) || rewriteCount <= 0) {
92
+ return;
93
+ }
94
+ const exists = messages.some((entry) => {
95
+ if (!entry || typeof entry !== 'object')
96
+ return false;
97
+ if (entry.role !== 'system')
98
+ return false;
99
+ const content = typeof entry.content === 'string' ? String(entry.content) : '';
100
+ return content.includes(NESTED_APPLY_PATCH_POLICY_MARKER);
101
+ });
102
+ if (exists) {
103
+ return;
104
+ }
105
+ messages.push({
106
+ role: 'system',
107
+ content: buildNestedApplyPatchPolicyNotice(rewriteCount)
108
+ });
109
+ }
16
110
  function tryWriteSnapshot(options, stage, data) {
17
111
  try {
18
112
  // 仅在 verbose 级别保存快照(环境变量)
@@ -238,6 +332,7 @@ export function normalizeApplyPatchToolCallsOnResponse(chat) {
238
332
  for (const tc of tcs) {
239
333
  try {
240
334
  const fn = tc && tc.function ? tc.function : undefined;
335
+ rewriteExecCommandApplyPatchCall(fn);
241
336
  const name = typeof fn?.name === 'string' ? String(fn.name).trim().toLowerCase() : '';
242
337
  if (name !== 'apply_patch')
243
338
  continue;
@@ -301,6 +396,7 @@ function normalizeSpecialToolCallsOnRequest(request) {
301
396
  if (lastAssistantIndex === -1) {
302
397
  return out;
303
398
  }
399
+ let rewrittenNestedApplyPatchCount = 0;
304
400
  for (let i = 0; i < messages.length; i += 1) {
305
401
  const msg = messages[i];
306
402
  if (!msg || typeof msg !== 'object')
@@ -313,6 +409,9 @@ function normalizeSpecialToolCallsOnRequest(request) {
313
409
  for (const tc of tcs) {
314
410
  try {
315
411
  const fn = tc && tc.function ? tc.function : undefined;
412
+ if (rewriteExecCommandApplyPatchCall(fn)) {
413
+ rewrittenNestedApplyPatchCount += 1;
414
+ }
316
415
  const name = typeof fn?.name === 'string' ? String(fn.name).trim().toLowerCase() : '';
317
416
  const rawArgs = fn?.arguments;
318
417
  // apply_patch 兼容:统一生成 { patch, input }
@@ -372,6 +471,9 @@ function normalizeSpecialToolCallsOnRequest(request) {
372
471
  }
373
472
  }
374
473
  }
474
+ if (rewrittenNestedApplyPatchCount > 0) {
475
+ injectNestedApplyPatchPolicyNotice(messages, rewrittenNestedApplyPatchCount);
476
+ }
375
477
  return out;
376
478
  }
377
479
  catch {
@@ -71,6 +71,17 @@ function buildApplyPatchGuidanceText() {
71
71
  ].join('\n');
72
72
  return { marker, text };
73
73
  }
74
+ function augmentExecCommand(fn) {
75
+ const marker = '[Codex ExecCommand Guidance]';
76
+ const guidance = [
77
+ marker,
78
+ 'FORBIDDEN: Do NOT call apply_patch via exec_command/shell. Use apply_patch tool directly.',
79
+ 'If you need to edit files, call apply_patch with patch text only.',
80
+ 'exec_command is only for shell commands that are NOT apply_patch.',
81
+ '禁止通过 exec_command/shell 嵌套调用 apply_patch;修改文件必须直接调用 apply_patch 工具。'
82
+ ].join('\n');
83
+ fn.description = appendOnce(fn.description, guidance, marker);
84
+ }
74
85
  function augmentShell(fn) {
75
86
  const marker = '[Codex Shell Guidance]';
76
87
  const guidance = [
@@ -184,6 +195,8 @@ export function augmentOpenAITools(tools) {
184
195
  try {
185
196
  if (n === 'shell')
186
197
  augmentShell(fn);
198
+ else if (n === 'exec_command')
199
+ augmentExecCommand(fn);
187
200
  else if (n === 'apply_patch') {
188
201
  const { marker, text } = buildApplyPatchGuidanceText();
189
202
  fn.description = appendOnce(fn.description, text, marker);
@@ -219,6 +232,9 @@ export function augmentAnthropicTools(tools) {
219
232
  if (name) {
220
233
  const n = name.trim();
221
234
  try {
235
+ if (n === 'exec_command') {
236
+ augmentExecCommand(copy);
237
+ }
222
238
  if (n === 'apply_patch') {
223
239
  schema.properties.patch = {
224
240
  type: 'string',
@@ -269,6 +285,7 @@ export function buildSystemToolGuidance() {
269
285
  lines.push(bullet('function.arguments must be a single JSON string. / arguments 必须是单个 JSON 字符串。'));
270
286
  lines.push(bullet('shell: Place ALL intent into the command argv array only; do not invent extra keys. / shell 所有意图写入 command 数组,不要添加额外键名。'));
271
287
  lines.push(bullet('File writes are FORBIDDEN via shell (no redirection, no here-doc, no sed -i, no ed -s, no tee). Use apply_patch ONLY. / 通过 shell 写文件一律禁止(不得使用重定向、heredoc、sed -i、ed -s、tee);必须使用 apply_patch。'));
288
+ lines.push(bullet('NEVER wrap apply_patch inside exec_command/shell. Direct apply_patch tool call only. / 严禁在 exec_command/shell 中嵌套 apply_patch,必须直接调用 apply_patch。'));
272
289
  lines.push(bullet('apply_patch: Before writing, always read the target file first and compute changes against the latest content using appropriate tools. / apply_patch 在写入前必须先通过合适的工具读取目标文件最新内容,并基于该内容生成变更。'));
273
290
  lines.push(bullet('apply_patch: Send patch text only. Supported formats: "*** Begin Patch" OR GNU unified diff. "*** Update File" never implicitly creates files—use "*** Add File:" (or /dev/null diff). / apply_patch:仅发送补丁文本,支持 "*** Begin Patch" 或 GNU diff;"*** Update File" 不会隐式创建文件。'));
274
291
  lines.push(bullet('apply_patch: For a given file, prefer one contiguous change block per call; if you need to touch non-adjacent regions, split them into multiple apply_patch calls. / apply_patch 修改同一文件时尽量只提交一段连续补丁,多个不相邻位置请拆成多次调用。'));
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_MODEL_CONTEXT_TOKENS, VirtualRouterError, VirtualRouterErrorCode } from './types.js';
2
- import { scanOAuthTokenFiles } from './token-file-scanner.js';
2
+ import { scanDeepSeekAccountTokenFiles, scanOAuthTokenFiles } from './token-file-scanner.js';
3
3
  const DEFAULT_CLASSIFIER = {
4
4
  longContextThresholdTokens: 180000,
5
5
  thinkingKeywords: ['think step', 'analysis', 'reasoning', '仔细分析', '深度思考'],
@@ -196,6 +196,7 @@ function buildProviderRuntimeEntries(providers) {
196
196
  modelStreaming: normalizedProvider.modelStreaming,
197
197
  modelContextTokens: normalizedProvider.modelContextTokens,
198
198
  defaultContextTokens: normalizedProvider.defaultContextTokens,
199
+ ...(normalizedProvider.deepseek ? { deepseek: normalizedProvider.deepseek } : {}),
199
200
  ...(normalizedProvider.serverToolsDisabled ? { serverToolsDisabled: true } : {})
200
201
  };
201
202
  }
@@ -304,6 +305,7 @@ function buildProviderProfiles(targetKeys, runtimeEntries) {
304
305
  responsesConfig: runtime.responsesConfig,
305
306
  streaming: streamingPref,
306
307
  maxContextTokens: contextTokens,
308
+ ...(runtime.deepseek ? { deepseek: runtime.deepseek } : {}),
307
309
  ...(runtime.serverToolsDisabled ? { serverToolsDisabled: true } : {})
308
310
  };
309
311
  targetRuntime[targetKey] = {
@@ -526,6 +528,7 @@ function normalizeProvider(providerId, raw) {
526
528
  const streaming = resolveProviderStreamingPreference(provider, responsesNode);
527
529
  const modelStreaming = normalizeModelStreaming(provider);
528
530
  const { modelContextTokens, defaultContextTokens } = normalizeModelContextTokens(provider);
531
+ const deepseek = normalizeDeepSeekOptions(provider);
529
532
  const serverToolsDisabled = provider.serverToolsDisabled === true ||
530
533
  (typeof provider.serverToolsDisabled === 'string' &&
531
534
  provider.serverToolsDisabled.trim().toLowerCase() === 'true') ||
@@ -545,6 +548,7 @@ function normalizeProvider(providerId, raw) {
545
548
  modelStreaming,
546
549
  modelContextTokens,
547
550
  defaultContextTokens,
551
+ ...(deepseek ? { deepseek } : {}),
548
552
  ...(serverToolsDisabled ? { serverToolsDisabled: true } : {})
549
553
  };
550
554
  }
@@ -631,6 +635,31 @@ function normalizeModelContextTokens(provider) {
631
635
  defaultContextTokens: defaultCandidate
632
636
  };
633
637
  }
638
+ function normalizeDeepSeekOptions(provider) {
639
+ const direct = asRecord(provider.deepseek);
640
+ const ext = asRecord(asRecord(provider.extensions)?.deepseek);
641
+ const source = Object.keys(direct).length ? direct : ext;
642
+ if (!source || !Object.keys(source).length) {
643
+ return undefined;
644
+ }
645
+ const strictToolRequired = typeof source.strictToolRequired === 'boolean'
646
+ ? source.strictToolRequired
647
+ : typeof source.strictToolRequired === 'string'
648
+ ? source.strictToolRequired.trim().toLowerCase() === 'true'
649
+ : undefined;
650
+ const textToolFallback = typeof source.textToolFallback === 'boolean'
651
+ ? source.textToolFallback
652
+ : typeof source.textToolFallback === 'string'
653
+ ? source.textToolFallback.trim().toLowerCase() === 'true'
654
+ : undefined;
655
+ if (strictToolRequired === undefined && textToolFallback === undefined) {
656
+ return undefined;
657
+ }
658
+ return {
659
+ ...(strictToolRequired !== undefined ? { strictToolRequired } : {}),
660
+ ...(textToolFallback !== undefined ? { textToolFallback } : {})
661
+ };
662
+ }
634
663
  function resolveStreamingPreference(model) {
635
664
  return (coerceStreamingPreference(model.streaming) ??
636
665
  coerceStreamingPreference(model.stream) ??
@@ -1029,6 +1058,22 @@ function extractProviderAuthEntries(providerId, raw) {
1029
1058
  }
1030
1059
  }
1031
1060
  }
1061
+ // DeepSeek account 多 token 自动扫描:
1062
+ // - 仅在未显式声明多 key 时触发;
1063
+ // - 从 ~/.routecodex/auth/deepseek-account-*.json 自动发现多个账号;
1064
+ // - alias 直接取文件名后缀,路由目标 deepseek-web.<model> 会自动扩展到所有 alias。
1065
+ const baseRawType = String(baseTypeInfo.raw ?? baseTypeSource ?? '').trim().toLowerCase();
1066
+ if (baseType === 'apiKey' && baseRawType === 'deepseek-account' && !hasExplicitEntries) {
1067
+ const tokenFiles = scanDeepSeekAccountTokenFiles();
1068
+ for (const match of tokenFiles) {
1069
+ const authConfig = {
1070
+ ...defaults,
1071
+ type: baseTypeSource ?? 'deepseek-account',
1072
+ tokenFile: match.filePath
1073
+ };
1074
+ pushEntry(match.alias, authConfig);
1075
+ }
1076
+ }
1032
1077
  if (!entries.length) {
1033
1078
  const fallbackExtras = {
1034
1079
  value: readOptionalString(auth.value),