@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
@@ -1,6 +1,6 @@
1
1
  import { recordStage } from '../../../stages/utils.js';
2
2
  export async function runReqInboundStage1FormatParse(options) {
3
3
  const envelope = (await options.formatAdapter.parseRequest(options.rawRequest, options.adapterContext));
4
- recordStage(options.stageRecorder, 'req_inbound_stage1_format_parse', envelope);
4
+ recordStage(options.stageRecorder, 'chat_process.req.stage1.format_parse', envelope);
5
5
  return envelope;
6
6
  }
@@ -7,6 +7,13 @@ export interface ReqInboundStage2SemanticMapOptions {
7
7
  adapterContext: AdapterContext;
8
8
  formatEnvelope: FormatEnvelope<JsonObject>;
9
9
  semanticMapper: Pick<SemanticMapper, 'toChat'>;
10
+ /**
11
+ * Mappable cross-protocol semantics (e.g. /v1/responses submit resume) must be
12
+ * lifted into chat semantics before entering chat_process.
13
+ *
14
+ * This must never be stored in metadata/AdapterContext.
15
+ */
16
+ responsesResume?: JsonObject;
10
17
  stageRecorder?: StageRecorder;
11
18
  }
12
19
  export interface ReqInboundStage2SemanticMapResult {
@@ -2,6 +2,7 @@ import { chatEnvelopeToStandardized } from '../../../../standardized-bridge.js';
2
2
  import { validateChatEnvelope } from '../../../../../shared/chat-envelope-validator.js';
3
3
  import { applyHubOperationTableInbound } from '../../../../operation-table/operation-table-runner.js';
4
4
  import { recordStage } from '../../../stages/utils.js';
5
+ import { isJsonObject, jsonClone } from '../../../../types/json.js';
5
6
  export async function runReqInboundStage2SemanticMap(options) {
6
7
  const chatEnvelope = await options.semanticMapper.toChat(options.formatEnvelope, options.adapterContext);
7
8
  applyHubOperationTableInbound({
@@ -9,11 +10,74 @@ export async function runReqInboundStage2SemanticMap(options) {
9
10
  chatEnvelope,
10
11
  adapterContext: options.adapterContext
11
12
  });
13
+ // Semantic Gate (request): before entering chat_process, lift any mappable protocol semantics
14
+ // into ChatEnvelope.semantics. Do not persist these in metadata.
15
+ try {
16
+ if (!chatEnvelope.semantics || typeof chatEnvelope.semantics !== 'object') {
17
+ chatEnvelope.semantics = {};
18
+ }
19
+ const semantics = chatEnvelope.semantics;
20
+ if (!semantics.tools || !isJsonObject(semantics.tools)) {
21
+ semantics.tools = {};
22
+ }
23
+ const toolsNode = semantics.tools;
24
+ const rawTools = Array.isArray(options.formatEnvelope.payload?.tools)
25
+ ? (options.formatEnvelope.payload.tools ?? [])
26
+ : [];
27
+ if (rawTools.length && toolsNode.clientToolsRaw === undefined) {
28
+ toolsNode.clientToolsRaw = jsonClone(rawTools);
29
+ }
30
+ if (options.responsesResume && isJsonObject(options.responsesResume)) {
31
+ if (!semantics.responses || !isJsonObject(semantics.responses)) {
32
+ semantics.responses = {};
33
+ }
34
+ const responsesNode = semantics.responses;
35
+ if (responsesNode.resume === undefined) {
36
+ responsesNode.resume = jsonClone(options.responsesResume);
37
+ }
38
+ // If this is a Responses submit/resume hop, tool outputs may exist only in resume metadata.
39
+ // Lift them into canonical chat.toolOutputs so chat_process sees a single unified surface.
40
+ const hasDirectToolOutputs = Array.isArray(chatEnvelope.toolOutputs) && chatEnvelope.toolOutputs.length > 0;
41
+ if (!hasDirectToolOutputs) {
42
+ const detailed = Array.isArray(options.responsesResume.toolOutputsDetailed)
43
+ ? options.responsesResume.toolOutputsDetailed
44
+ : undefined;
45
+ if (detailed && detailed.length) {
46
+ const mapped = detailed
47
+ .map((entry, index) => {
48
+ if (!entry || typeof entry !== 'object')
49
+ return undefined;
50
+ const callId = typeof entry.callId === 'string' && String(entry.callId).trim().length
51
+ ? String(entry.callId).trim()
52
+ : typeof entry.originalId === 'string' && String(entry.originalId).trim().length
53
+ ? String(entry.originalId).trim()
54
+ : `resume_tool_${index + 1}`;
55
+ const outputText = typeof entry.outputText === 'string'
56
+ ? entry.outputText
57
+ : (() => { try {
58
+ return JSON.stringify(entry.outputText ?? '');
59
+ }
60
+ catch {
61
+ return String(entry.outputText ?? '');
62
+ } })();
63
+ return { tool_call_id: callId, content: outputText };
64
+ })
65
+ .filter(Boolean);
66
+ if (mapped.length) {
67
+ chatEnvelope.toolOutputs = mapped;
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ catch {
74
+ // fail-fast is enforced at chat_process entry; this gate is best-effort
75
+ }
12
76
  validateChatEnvelope(chatEnvelope, {
13
77
  stage: 'req_inbound',
14
78
  direction: 'request'
15
79
  });
16
- recordStage(options.stageRecorder, 'req_inbound_stage2_semantic_map', chatEnvelope);
80
+ recordStage(options.stageRecorder, 'chat_process.req.stage2.semantic_map', chatEnvelope);
17
81
  const standardizedRequest = chatEnvelopeToStandardized(chatEnvelope, {
18
82
  adapterContext: options.adapterContext,
19
83
  endpoint: options.adapterContext.entryEndpoint,
@@ -1,26 +1,7 @@
1
1
  import { captureResponsesContext, buildChatRequestFromResponses } from '../../../../../responses/responses-openai-bridge.js';
2
+ import { captureResponsesRequestContext } from '../../../../../shared/responses-conversation-store.js';
2
3
  import { recordStage } from '../../../stages/utils.js';
3
4
  export async function runReqInboundStage3ContextCapture(options) {
4
- // 对由 server-side 工具触发的二跳/内部跳转请求(例如 stopMessage followup),
5
- // 跳过工具输出扫描与 apply_patch 诊断日志,避免在内部流中重复放大客户端已收到的
6
- // 工具错误信息。此类请求的工具治理在 chat-process 阶段完成,这里仅保留最小快照。
7
- try {
8
- const ctx = options.adapterContext;
9
- const followupFlag = ctx?.serverToolFollowup;
10
- const isFollowup = followupFlag === true ||
11
- (typeof followupFlag === 'string' && followupFlag.trim().toLowerCase() === 'true');
12
- if (isFollowup) {
13
- const snapshot = {
14
- providerProtocol: options.adapterContext.providerProtocol ?? 'unknown',
15
- tool_outputs: []
16
- };
17
- recordStage(options.stageRecorder, 'req_inbound_stage3_context_capture', snapshot);
18
- return snapshot;
19
- }
20
- }
21
- catch {
22
- // best-effort: 若检测 serverToolFollowup 失败,则继续走通用路径
23
- }
24
5
  let context;
25
6
  if (options.captureContext) {
26
7
  try {
@@ -37,7 +18,7 @@ export async function runReqInboundStage3ContextCapture(options) {
37
18
  const snapshot = context
38
19
  ? augmentContextSnapshot(context, fallbackSnapshot)
39
20
  : fallbackSnapshot;
40
- recordStage(options.stageRecorder, 'req_inbound_stage3_context_capture', snapshot);
21
+ recordStage(options.stageRecorder, 'chat_process.req.stage3.context_capture', snapshot);
41
22
  return context ?? snapshot;
42
23
  }
43
24
  export function runChatContextCapture(options) {
@@ -73,6 +54,28 @@ export function captureResponsesContextSnapshot(options) {
73
54
  catch {
74
55
  // best-effort context capture
75
56
  }
57
+ // OpenAI Responses tool loop: store the request context keyed by requestId so that
58
+ // `/v1/responses/:id/submit_tool_outputs` can resume the conversation later.
59
+ //
60
+ // This must be done on the hub pipeline inbound path (not in host/provider), because:
61
+ // - the tool loop is a client-protocol behavior (/v1/responses), independent of providerProtocol;
62
+ // - providers must remain transport-only;
63
+ // - the host may later enhance requestId with providerKey/model for logging, which is handled via rebind.
64
+ try {
65
+ const requestId = typeof options.adapterContext.requestId === 'string' && options.adapterContext.requestId.trim().length
66
+ ? options.adapterContext.requestId
67
+ : undefined;
68
+ if (requestId) {
69
+ captureResponsesRequestContext({
70
+ requestId,
71
+ payload: options.rawRequest,
72
+ context: context
73
+ });
74
+ }
75
+ }
76
+ catch {
77
+ // best-effort: capture failures must not block the request path
78
+ }
76
79
  return context;
77
80
  }
78
81
  function captureChatContextSnapshot(options) {
@@ -306,7 +309,7 @@ function buildApplyPatchDiagnostics(output) {
306
309
  return '\n\n[RouteCodex precheck] apply_patch 参数解析失败:缺少字段 "input"。当前 RouteCodex 期望 { input, patch } 形态,并且两个字段都应包含完整统一 diff 文本。';
307
310
  }
308
311
  if (output.includes('invalid type: map, expected a string')) {
309
- return '\n\n[RouteCodex precheck] apply_patch 参数类型错误:检测到 JSON 对象(map),但客户端期望字符串。请先对参数做 JSON.stringify 再写入 arguments,或直接提供 { patch: \"<统一 diff>\" } 形式。';
312
+ return '\n\n[RouteCodex precheck] apply_patch 参数类型错误:检测到 JSON 对象(map),但客户端期望字符串。请先对参数做 JSON.stringify 再写入 arguments,或直接提供 { patch: "<统一 diff>" } 形式。';
310
313
  }
311
314
  return undefined;
312
315
  }
@@ -28,7 +28,7 @@ export async function runReqOutboundStage1SemanticMap(options) {
28
28
  formatEnvelope,
29
29
  adapterContext: options.adapterContext
30
30
  });
31
- recordStage(options.stageRecorder, 'req_outbound_stage1_semantic_map', chatEnvelope);
31
+ recordStage(options.stageRecorder, 'chat_process.req.stage6.outbound.semantic_map', chatEnvelope);
32
32
  return { chatEnvelope, formatEnvelope };
33
33
  }
34
34
  function attachToolOutputsFromContext(chatEnvelope, snapshot) {
@@ -5,7 +5,7 @@ import type { FormatAdapter, StageRecorder } from '../../../../format-adapters/i
5
5
  export interface ReqOutboundStage2FormatBuildOptions {
6
6
  formatEnvelope: FormatEnvelope<JsonObject>;
7
7
  adapterContext: AdapterContext;
8
- formatAdapter: Pick<FormatAdapter, 'buildResponse'>;
8
+ formatAdapter: Pick<FormatAdapter, 'buildRequest'>;
9
9
  stageRecorder?: StageRecorder;
10
10
  }
11
11
  export declare function runReqOutboundStage2FormatBuild(options: ReqOutboundStage2FormatBuildOptions): Promise<JsonObject>;
@@ -1,9 +1,9 @@
1
1
  import { recordStage } from '../../../stages/utils.js';
2
2
  const PRIVATE_FIELD_PREFIX = '__rcc_';
3
3
  export async function runReqOutboundStage2FormatBuild(options) {
4
- const payload = (await options.formatAdapter.buildResponse(options.formatEnvelope, options.adapterContext));
4
+ const payload = (await options.formatAdapter.buildRequest(options.formatEnvelope, options.adapterContext));
5
5
  stripPrivateFields(payload);
6
- recordStage(options.stageRecorder, 'req_outbound_stage2_format_build', payload);
6
+ recordStage(options.stageRecorder, 'chat_process.req.stage7.outbound.format_build', payload);
7
7
  return payload;
8
8
  }
9
9
  function stripPrivateFields(value) {
@@ -0,0 +1,10 @@
1
+ import type { AdapterContext } from '../../../../types/chat-envelope.js';
2
+ import type { JsonObject } from '../../../../types/json.js';
3
+ import type { StageRecorder } from '../../../../format-adapters/index.js';
4
+ type ProviderPayload = JsonObject;
5
+ export declare function runReqOutboundStage2ThoughtSignatureInject(options: {
6
+ payload: ProviderPayload;
7
+ adapterContext: AdapterContext;
8
+ stageRecorder?: StageRecorder;
9
+ }): Promise<ProviderPayload>;
10
+ export {};
@@ -0,0 +1,172 @@
1
+ import { recordStage } from '../../../stages/utils.js';
2
+ import { SKIP_THOUGHT_SIGNATURE, buildThoughtSignatureSessionKey, getCachedThoughtSignature, getLastThoughtSignature, shouldHandleThoughtSignature } from '../../../thought-signature/thought-signature-center.js';
3
+ import { filterInvalidThinkingBlocks, removeTrailingUnsignedThinkingBlocks } from '../../../../../shared/thought-signature-validator.js';
4
+ function isThinkingPart(part) {
5
+ if (part.thought === true)
6
+ return true;
7
+ const type = typeof part.type === 'string' ? String(part.type).toLowerCase() : '';
8
+ return type === 'thinking' || type === 'reasoning' || type === 'redacted_thinking';
9
+ }
10
+ function readThinkingText(part) {
11
+ const text = part.text ?? part.thinking;
12
+ if (typeof text === 'string' && text.trim())
13
+ return text.trim();
14
+ return undefined;
15
+ }
16
+ function hasSignedThinking(parts) {
17
+ return parts.some((part) => {
18
+ if (!isThinkingPart(part))
19
+ return false;
20
+ const sig = part.thoughtSignature ?? part.signature;
21
+ return typeof sig === 'string' && sig.trim().length >= 10;
22
+ });
23
+ }
24
+ function hasToolUse(parts) {
25
+ return parts.some((part) => {
26
+ if (part.functionCall || part.function_call)
27
+ return true;
28
+ if (part.type === 'tool_use' || part.type === 'tool_result')
29
+ return true;
30
+ return false;
31
+ });
32
+ }
33
+ function injectGeminiPayload(payload, sessionKey) {
34
+ const contents = Array.isArray(payload.contents) ? payload.contents : [];
35
+ for (const content of contents) {
36
+ const parts = Array.isArray(content.parts) ? content.parts : [];
37
+ if (!parts.length)
38
+ continue;
39
+ // Deep-filter invalid thinking parts before injection.
40
+ const cleanedParts = [];
41
+ for (const part of parts) {
42
+ if (!part || typeof part !== 'object') {
43
+ cleanedParts.push(part);
44
+ continue;
45
+ }
46
+ if (!isThinkingPart(part)) {
47
+ cleanedParts.push(part);
48
+ continue;
49
+ }
50
+ const text = readThinkingText(part);
51
+ const sig = part.thoughtSignature;
52
+ const sigValid = typeof sig === 'string' && sig.trim().length >= 10
53
+ ? true
54
+ : Boolean(sig && (!text || !String(text).trim().length));
55
+ if (sigValid) {
56
+ cleanedParts.push(part);
57
+ }
58
+ else if (text && text.trim()) {
59
+ cleanedParts.push({ text });
60
+ }
61
+ }
62
+ content.parts = cleanedParts;
63
+ const effectiveParts = cleanedParts;
64
+ const toolUsePresent = hasToolUse(effectiveParts);
65
+ let signedThinking = hasSignedThinking(effectiveParts);
66
+ for (const part of effectiveParts) {
67
+ if (!isThinkingPart(part))
68
+ continue;
69
+ const text = readThinkingText(part);
70
+ const sig = part.thoughtSignature;
71
+ if (typeof sig === 'string' && sig.trim()) {
72
+ continue;
73
+ }
74
+ const cached = text ? getCachedThoughtSignature(sessionKey, text) : undefined;
75
+ if (cached) {
76
+ part.thoughtSignature = cached;
77
+ signedThinking = true;
78
+ }
79
+ else if (text) {
80
+ part.thoughtSignature = SKIP_THOUGHT_SIGNATURE;
81
+ }
82
+ }
83
+ if (toolUsePresent && !signedThinking) {
84
+ const last = getLastThoughtSignature(sessionKey);
85
+ if (last) {
86
+ effectiveParts.unshift({ thought: true, text: last.text, thoughtSignature: last.signature });
87
+ }
88
+ else {
89
+ effectiveParts.unshift({ thought: true, text: '', thoughtSignature: SKIP_THOUGHT_SIGNATURE });
90
+ }
91
+ }
92
+ }
93
+ }
94
+ function injectAnthropicPayload(payload, sessionKey) {
95
+ const messages = Array.isArray(payload.messages) ? payload.messages : [];
96
+ for (const message of messages) {
97
+ if (message.role !== 'assistant')
98
+ continue;
99
+ const content = Array.isArray(message.content) ? message.content : [];
100
+ if (!content.length)
101
+ continue;
102
+ // Deep-filter invalid thinking blocks before injection.
103
+ const wrapper = { role: 'assistant', content: [...content] };
104
+ filterInvalidThinkingBlocks([wrapper]);
105
+ if (Array.isArray(wrapper.content)) {
106
+ removeTrailingUnsignedThinkingBlocks(wrapper.content);
107
+ }
108
+ message.content = wrapper.content;
109
+ const effectiveContent = Array.isArray(wrapper.content) ? wrapper.content : content;
110
+ const toolUsePresent = effectiveContent.some((block) => block && typeof block === 'object' && (block.type === 'tool_use' || block.type === 'tool_result'));
111
+ let signedThinking = effectiveContent.some((block) => {
112
+ if (!block || typeof block !== 'object')
113
+ return false;
114
+ const type = typeof block.type === 'string' ? String(block.type).toLowerCase() : '';
115
+ if (type !== 'thinking' && type !== 'redacted_thinking')
116
+ return false;
117
+ const sig = block.signature;
118
+ return typeof sig === 'string' && sig.trim().length >= 10;
119
+ });
120
+ for (const block of effectiveContent) {
121
+ if (!block || typeof block !== 'object')
122
+ continue;
123
+ const type = typeof block.type === 'string' ? String(block.type).toLowerCase() : '';
124
+ if (type !== 'thinking' && type !== 'redacted_thinking')
125
+ continue;
126
+ const text = typeof block.thinking === 'string'
127
+ ? String(block.thinking).trim()
128
+ : typeof block.text === 'string'
129
+ ? String(block.text).trim()
130
+ : '';
131
+ const sig = block.signature;
132
+ if (typeof sig === 'string' && sig.trim())
133
+ continue;
134
+ const cached = text ? getCachedThoughtSignature(sessionKey, text) : undefined;
135
+ if (cached) {
136
+ block.signature = cached;
137
+ signedThinking = true;
138
+ }
139
+ }
140
+ if (toolUsePresent && !signedThinking) {
141
+ const last = getLastThoughtSignature(sessionKey);
142
+ if (last) {
143
+ effectiveContent.unshift({ type: 'thinking', thinking: last.text, signature: last.signature });
144
+ }
145
+ else {
146
+ effectiveContent.unshift({ type: 'thinking', thinking: '', signature: SKIP_THOUGHT_SIGNATURE });
147
+ }
148
+ }
149
+ }
150
+ }
151
+ export async function runReqOutboundStage2ThoughtSignatureInject(options) {
152
+ const { payload, adapterContext } = options;
153
+ if (!shouldHandleThoughtSignature(adapterContext)) {
154
+ return payload;
155
+ }
156
+ const sessionKey = buildThoughtSignatureSessionKey(adapterContext, payload);
157
+ if (!sessionKey) {
158
+ return payload;
159
+ }
160
+ const protocol = String(adapterContext.providerProtocol || '').toLowerCase();
161
+ if (protocol === 'gemini-chat') {
162
+ injectGeminiPayload(payload, sessionKey);
163
+ }
164
+ else if (protocol === 'anthropic-messages') {
165
+ injectAnthropicPayload(payload, sessionKey);
166
+ }
167
+ recordStage(options.stageRecorder, 'chat_process.req.stage7.thought_signature_inject', {
168
+ applied: true,
169
+ sessionKey
170
+ });
171
+ return payload;
172
+ }
@@ -6,7 +6,7 @@ function pickCompatProfile(adapterContext) {
6
6
  export async function runReqOutboundStage3Compat(options) {
7
7
  const profile = pickCompatProfile(options.adapterContext);
8
8
  const result = applyRequestCompat(profile, options.payload, { adapterContext: options.adapterContext });
9
- options.stageRecorder?.record('req_outbound_stage3_compat', {
9
+ options.stageRecorder?.record('chat_process.req.stage8.outbound.compat', {
10
10
  applied: Boolean(result.appliedProfile),
11
11
  profile: result.appliedProfile || profile || 'passthrough'
12
12
  });
@@ -15,7 +15,7 @@ export async function runReqOutboundStage3Compat(options) {
15
15
  export function runRespInboundStageCompatResponse(options) {
16
16
  const profile = pickCompatProfile(options.adapterContext);
17
17
  const result = applyResponseCompat(profile, options.payload, { adapterContext: options.adapterContext });
18
- options.stageRecorder?.record('resp_inbound_stage_compat', {
18
+ options.stageRecorder?.record('chat_process.resp.stage3.compat', {
19
19
  applied: Boolean(result.appliedProfile),
20
20
  profile: result.appliedProfile || profile || 'passthrough'
21
21
  });
@@ -10,7 +10,7 @@ export async function runReqProcessStage1ToolGovernance(options) {
10
10
  requestId: options.requestId
11
11
  });
12
12
  if (result.processedRequest) {
13
- recordStage(options.stageRecorder, 'req_process_stage1_tool_governance', result.processedRequest);
13
+ recordStage(options.stageRecorder, 'chat_process.req.stage4.tool_governance', result.processedRequest);
14
14
  // Best-effort: capture apply_patch execution failures reported by tool role messages.
15
15
  // This is for errorsamples collection only and must not affect runtime behavior.
16
16
  captureApplyPatchExecutionFailuresFromProcessedRequest(result.processedRequest);
@@ -5,7 +5,7 @@ export function runReqProcessStage2RouteSelect(options) {
5
5
  const result = options.routerEngine.route(options.request, options.metadataInput);
6
6
  applyTargetMetadata(options.normalizedMetadata, result.target, result.decision.routeName, previousModel);
7
7
  applyTargetToSubject(options.request, result.target, previousModel);
8
- recordStage(options.stageRecorder, 'req_process_stage2_route_select', {
8
+ recordStage(options.stageRecorder, 'chat_process.req.stage5.route_select', {
9
9
  target: result.target,
10
10
  decision: result.decision,
11
11
  diagnostics: result.diagnostics
@@ -43,43 +43,43 @@ export async function runRespInboundStage1SseDecode(options) {
43
43
  // 某些 mock-provider / 捕获样本在 SSE 连接被异常终止时会携带 error 标记,
44
44
  // 即使仍保留 __sse_responses 流,也应视为上游异常并终止。
45
45
  if (wrapperError) {
46
- recordStage(options.stageRecorder, 'resp_inbound_stage1_sse_decode', {
46
+ recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
47
47
  streamDetected: Boolean(stream),
48
48
  decoded: false,
49
49
  protocol: options.providerProtocol,
50
50
  reason: 'sse_wrapper_error',
51
51
  error: wrapperError
52
52
  });
53
- throw new ProviderProtocolError(`[resp_inbound_stage1_sse_decode] Upstream SSE terminated: ${wrapperError}`, {
53
+ throw new ProviderProtocolError(`[chat_process.resp.stage1.sse_decode] Upstream SSE terminated: ${wrapperError}`, {
54
54
  code: 'SSE_DECODE_ERROR',
55
55
  protocol: options.providerProtocol,
56
56
  providerType: resolveProviderType(options.providerProtocol),
57
57
  details: {
58
- phase: 'resp_inbound_stage1_sse_decode',
58
+ phase: 'chat_process.resp.stage1.sse_decode',
59
59
  requestId: options.adapterContext.requestId,
60
60
  message: wrapperError
61
61
  }
62
62
  });
63
63
  }
64
64
  if (!stream) {
65
- recordStage(options.stageRecorder, 'resp_inbound_stage1_sse_decode', {
65
+ recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
66
66
  streamDetected: false
67
67
  });
68
68
  return { payload: options.payload, decodedFromSse: false };
69
69
  }
70
70
  if (!supportsSseProtocol(options.providerProtocol)) {
71
- recordStage(options.stageRecorder, 'resp_inbound_stage1_sse_decode', {
71
+ recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
72
72
  streamDetected: true,
73
73
  decoded: false,
74
74
  reason: 'protocol_unsupported',
75
75
  protocol: options.providerProtocol
76
76
  });
77
- throw new ProviderProtocolError(`[resp_inbound_stage1_sse_decode] Protocol ${options.providerProtocol} does not support SSE decoding`, {
77
+ throw new ProviderProtocolError(`[chat_process.resp.stage1.sse_decode] Protocol ${options.providerProtocol} does not support SSE decoding`, {
78
78
  code: 'SSE_DECODE_ERROR',
79
79
  protocol: options.providerProtocol,
80
80
  providerType: resolveProviderType(options.providerProtocol),
81
81
  details: {
82
- phase: 'resp_inbound_stage1_sse_decode',
82
+ phase: 'chat_process.resp.stage1.sse_decode',
83
83
  reason: 'protocol_unsupported'
84
84
  }
85
85
  });
@@ -90,7 +90,7 @@ export async function runRespInboundStage1SseDecode(options) {
90
90
  requestId: options.adapterContext.requestId,
91
91
  model: options.adapterContext.modelId
92
92
  }));
93
- recordStage(options.stageRecorder, 'resp_inbound_stage1_sse_decode', {
93
+ recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
94
94
  streamDetected: true,
95
95
  decoded: true,
96
96
  protocol: options.providerProtocol
@@ -99,18 +99,18 @@ export async function runRespInboundStage1SseDecode(options) {
99
99
  }
100
100
  catch (error) {
101
101
  const message = error instanceof Error ? error.message : String(error);
102
- recordStage(options.stageRecorder, 'resp_inbound_stage1_sse_decode', {
102
+ recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
103
103
  streamDetected: true,
104
104
  decoded: false,
105
105
  protocol: options.providerProtocol,
106
106
  error: message
107
107
  });
108
- throw new ProviderProtocolError(`[resp_inbound_stage1_sse_decode] Failed to decode SSE payload for protocol ${options.providerProtocol}: ${message}`, {
108
+ throw new ProviderProtocolError(`[chat_process.resp.stage1.sse_decode] Failed to decode SSE payload for protocol ${options.providerProtocol}: ${message}`, {
109
109
  code: 'SSE_DECODE_ERROR',
110
110
  protocol: options.providerProtocol,
111
111
  providerType: resolveProviderType(options.providerProtocol),
112
112
  details: {
113
- phase: 'resp_inbound_stage1_sse_decode',
113
+ phase: 'chat_process.resp.stage1.sse_decode',
114
114
  requestId: options.adapterContext.requestId,
115
115
  message
116
116
  }
@@ -1,6 +1,6 @@
1
1
  import { recordStage } from '../../../stages/utils.js';
2
2
  export async function runRespInboundStage2FormatParse(options) {
3
3
  const envelope = (await options.formatAdapter.parseResponse(options.payload, options.adapterContext));
4
- recordStage(options.stageRecorder, 'resp_inbound_stage2_format_parse', envelope);
4
+ recordStage(options.stageRecorder, 'chat_process.resp.stage2.format_parse', envelope);
5
5
  return envelope;
6
6
  }
@@ -7,6 +7,7 @@ export interface RespInboundStage3SemanticMapOptions {
7
7
  adapterContext: AdapterContext;
8
8
  formatEnvelope: FormatEnvelope<JsonObject>;
9
9
  mapper: ResponseMapper;
10
+ requestSemantics?: JsonObject;
10
11
  stageRecorder?: StageRecorder;
11
12
  }
12
13
  export declare function runRespInboundStage3SemanticMap(options: RespInboundStage3SemanticMapOptions): Promise<ChatCompletionLike>;
@@ -1,6 +1,8 @@
1
1
  import { recordStage } from '../../../stages/utils.js';
2
2
  export async function runRespInboundStage3SemanticMap(options) {
3
- const chatResponse = await options.mapper.toChatCompletion(options.formatEnvelope, options.adapterContext);
4
- recordStage(options.stageRecorder, 'resp_inbound_stage3_semantic_map', chatResponse);
3
+ const chatResponse = await options.mapper.toChatCompletion(options.formatEnvelope, options.adapterContext, {
4
+ requestSemantics: options.requestSemantics
5
+ });
6
+ recordStage(options.stageRecorder, 'chat_process.resp.stage4.semantic_map_to_chat', chatResponse);
5
7
  return chatResponse;
6
8
  }
@@ -0,0 +1,10 @@
1
+ import type { AdapterContext } from '../../../../types/chat-envelope.js';
2
+ import type { JsonObject } from '../../../../types/json.js';
3
+ import type { StageRecorder } from '../../../../format-adapters/index.js';
4
+ type ProviderPayload = JsonObject;
5
+ export declare function runRespInboundStage3ThoughtSignatureCapture(options: {
6
+ payload: ProviderPayload;
7
+ adapterContext: AdapterContext;
8
+ stageRecorder?: StageRecorder;
9
+ }): Promise<ProviderPayload>;
10
+ export {};
@@ -0,0 +1,71 @@
1
+ import { recordStage } from '../../../stages/utils.js';
2
+ import { buildThoughtSignatureSessionKey, cacheThoughtSignature, shouldHandleThoughtSignature } from '../../../thought-signature/thought-signature-center.js';
3
+ function captureGeminiPayload(payload, sessionKey) {
4
+ const candidates = Array.isArray(payload.candidates) ? payload.candidates : [];
5
+ for (const candidate of candidates) {
6
+ const content = candidate?.content;
7
+ if (!content || typeof content !== 'object')
8
+ continue;
9
+ const parts = Array.isArray(content.parts) ? content.parts : [];
10
+ if (!parts.length)
11
+ continue;
12
+ let buffer = '';
13
+ for (const part of parts) {
14
+ if (!part || typeof part !== 'object')
15
+ continue;
16
+ if (part.thought === true || String(part.type || '').toLowerCase() === 'thinking') {
17
+ const text = part.text ?? part.thinking;
18
+ if (typeof text === 'string' && text.trim()) {
19
+ buffer += text;
20
+ }
21
+ }
22
+ const sig = part.thoughtSignature;
23
+ if (typeof sig === 'string' && sig.trim() && buffer.trim()) {
24
+ cacheThoughtSignature(sessionKey, buffer, sig);
25
+ }
26
+ }
27
+ }
28
+ }
29
+ function captureAnthropicPayload(payload, sessionKey) {
30
+ const content = Array.isArray(payload.content) ? payload.content : [];
31
+ if (!content.length)
32
+ return;
33
+ let buffer = '';
34
+ for (const block of content) {
35
+ if (!block || typeof block !== 'object')
36
+ continue;
37
+ const type = typeof block.type === 'string' ? String(block.type).toLowerCase() : '';
38
+ if (type === 'thinking' || type === 'redacted_thinking') {
39
+ const text = block.thinking ?? block.text;
40
+ if (typeof text === 'string' && text.trim()) {
41
+ buffer += text;
42
+ }
43
+ }
44
+ const sig = block.signature;
45
+ if (typeof sig === 'string' && sig.trim() && buffer.trim()) {
46
+ cacheThoughtSignature(sessionKey, buffer, sig);
47
+ }
48
+ }
49
+ }
50
+ export async function runRespInboundStage3ThoughtSignatureCapture(options) {
51
+ const { payload, adapterContext } = options;
52
+ if (!shouldHandleThoughtSignature(adapterContext)) {
53
+ return payload;
54
+ }
55
+ const sessionKey = buildThoughtSignatureSessionKey(adapterContext, payload);
56
+ if (!sessionKey) {
57
+ return payload;
58
+ }
59
+ const protocol = String(adapterContext.providerProtocol || '').toLowerCase();
60
+ if (protocol === 'gemini-chat') {
61
+ captureGeminiPayload(payload, sessionKey);
62
+ }
63
+ else if (protocol === 'anthropic-messages') {
64
+ captureAnthropicPayload(payload, sessionKey);
65
+ }
66
+ recordStage(options.stageRecorder, 'chat_process.resp.stage3.thought_signature_capture', {
67
+ applied: true,
68
+ sessionKey
69
+ });
70
+ return payload;
71
+ }
@@ -7,6 +7,7 @@ export interface RespOutboundStage1ClientRemapOptions {
7
7
  clientProtocol: ClientProtocol;
8
8
  requestId: string;
9
9
  adapterContext?: AdapterContext;
10
+ requestSemantics?: JsonObject;
10
11
  stageRecorder?: StageRecorder;
11
12
  }
12
13
  export declare function runRespOutboundStage1ClientRemap(options: RespOutboundStage1ClientRemapOptions): JsonObject;