@jsonstudio/llms 0.6.2979 → 0.6.3214

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 (242) hide show
  1. package/dist/conversion/args-mapping.js +8 -0
  2. package/dist/conversion/{shared/bridge-actions.js → bridge-actions.js} +2 -1
  3. package/dist/conversion/{shared/bridge-id-utils.js → bridge-id-utils.js} +1 -1
  4. package/dist/conversion/{shared/bridge-instructions.js → bridge-instructions.js} +1 -1
  5. package/dist/conversion/{shared/bridge-message-utils.d.ts → bridge-message-utils.d.ts} +1 -1
  6. package/dist/conversion/{shared/bridge-message-utils.js → bridge-message-utils.js} +5 -149
  7. package/dist/conversion/{shared/bridge-metadata.js → bridge-metadata.js} +1 -1
  8. package/dist/conversion/{shared/bridge-policies.js → bridge-policies.js} +1 -1
  9. package/dist/conversion/codecs/gemini-openai-codec.js +27 -8
  10. package/dist/conversion/codecs/responses-openai-codec.js +1 -1
  11. package/dist/conversion/{shared/compaction-detect.d.ts → compaction-detect.d.ts} +1 -1
  12. package/dist/conversion/compaction-detect.js +4 -0
  13. package/dist/conversion/compat/actions/apply-patch-fixer.js +2 -2
  14. package/dist/conversion/compat/actions/deepseek-web-response.d.ts +0 -1
  15. package/dist/conversion/compat/actions/deepseek-web-response.js +15 -405
  16. package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +1 -1
  17. package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.js +1 -1
  18. package/dist/conversion/compat/actions/qwen-transform.js +74 -2
  19. package/dist/conversion/compat/actions/snapshot.js +1 -1
  20. package/dist/conversion/compat/antigravity-session-signature.js +36 -0
  21. package/dist/conversion/compat/profiles/chat-deepseek-web.json +0 -22
  22. package/dist/conversion/compat/profiles/chat-glm.json +251 -72
  23. package/dist/conversion/compat/profiles/chat-iflow.json +174 -39
  24. package/dist/conversion/compat/profiles/chat-lmstudio.json +43 -14
  25. package/dist/conversion/hub/operation-table/operation-table-runner.js +2 -2
  26. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +1 -1
  27. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +7 -4
  28. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +2 -2
  29. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +2 -8
  30. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +1 -0
  31. package/dist/conversion/hub/pipeline/hub-pipeline.js +50 -3
  32. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +1 -1
  33. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +62 -0
  34. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +3 -1
  35. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +1 -1
  36. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/chat-process-semantics-bridge.d.ts +1 -1
  37. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +42 -29
  38. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.js +12 -0
  39. package/dist/conversion/hub/policy/protocol-spec.js +1 -1
  40. package/dist/conversion/hub/process/chat-process-clock-reminders.js +1 -1
  41. package/dist/conversion/hub/process/chat-process-clock-tools.js +1 -1
  42. package/dist/conversion/hub/process/chat-process-continue-execution.js +1 -1
  43. package/dist/conversion/hub/process/chat-process-servertool-orchestration.js +1 -1
  44. package/dist/conversion/hub/process/chat-process-web-search.js +1 -1
  45. package/dist/conversion/hub/response/provider-response.js +14 -5
  46. package/dist/conversion/hub/response/response-mappers.js +23 -1
  47. package/dist/conversion/hub/response/response-runtime.js +28 -5
  48. package/dist/conversion/hub/snapshot-recorder.js +1 -1
  49. package/dist/conversion/hub/tool-governance/engine.d.ts +8 -0
  50. package/dist/conversion/hub/tool-governance/engine.js +25 -68
  51. package/dist/conversion/hub/tool-governance/rules.js +73 -69
  52. package/dist/conversion/hub/tool-surface/tool-surface-engine.js +1 -1
  53. package/dist/conversion/index.d.ts +1 -2
  54. package/dist/conversion/index.js +1 -2
  55. package/dist/conversion/{shared/jsonish.js → jsonish.js} +1 -1
  56. package/dist/conversion/{shared/mcp-injection.js → mcp-injection.js} +1 -1
  57. package/dist/conversion/media.js +4 -0
  58. package/dist/conversion/{shared/metadata-passthrough.d.ts → metadata-passthrough.d.ts} +1 -1
  59. package/dist/conversion/{shared/metadata-passthrough.js → metadata-passthrough.js} +2 -2
  60. package/dist/conversion/payload-budget.js +47 -0
  61. package/dist/conversion/protocol-field-allowlists.d.ts +7 -0
  62. package/dist/conversion/protocol-field-allowlists.js +9 -0
  63. package/dist/conversion/{shared/protocol-state.d.ts → protocol-state.d.ts} +2 -2
  64. package/dist/conversion/{shared/protocol-state.js → protocol-state.js} +2 -2
  65. package/dist/conversion/{shared/errors.d.ts → provider-protocol-error.d.ts} +0 -3
  66. package/dist/conversion/provider-protocol-error.js +25 -0
  67. package/dist/conversion/responses/responses-openai-bridge/response-payload.js +8 -5
  68. package/dist/conversion/responses/responses-openai-bridge/types.d.ts +1 -1
  69. package/dist/conversion/responses/responses-openai-bridge.d.ts +1 -1
  70. package/dist/conversion/responses/responses-openai-bridge.js +43 -10
  71. package/dist/conversion/{shared/runtime-metadata.d.ts → runtime-metadata.d.ts} +1 -1
  72. package/dist/conversion/{shared/runtime-metadata.js → runtime-metadata.js} +2 -2
  73. package/dist/conversion/shared/anthropic-message-utils.js +19 -8
  74. package/dist/conversion/shared/chat-request-filters.d.ts +3 -4
  75. package/dist/conversion/shared/chat-request-filters.js +22 -78
  76. package/dist/conversion/shared/gemini-tool-utils.d.ts +1 -1
  77. package/dist/conversion/shared/openai-finalizer.js +1 -0
  78. package/dist/conversion/shared/openai-message-normalize.js +2 -2
  79. package/dist/conversion/shared/reasoning-normalizer.js +6 -0
  80. package/dist/conversion/shared/reasoning-utils.js +5 -2
  81. package/dist/conversion/shared/responses-conversation-store.js +1 -1
  82. package/dist/conversion/shared/responses-output-builder.js +55 -11
  83. package/dist/conversion/shared/responses-reasoning-registry.d.ts +14 -2
  84. package/dist/conversion/shared/responses-reasoning-registry.js +34 -6
  85. package/dist/conversion/shared/responses-response-utils.js +99 -9
  86. package/dist/conversion/shared/responses-tool-utils.js +1 -1
  87. package/dist/conversion/shared/text-markup-normalizer/normalize.d.ts +1 -1
  88. package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -2
  89. package/dist/conversion/shared/text-markup-normalizer.js +1 -1
  90. package/dist/conversion/shared/tool-filter-pipeline.js +1 -1
  91. package/dist/conversion/shared/tool-governor.js +3 -3
  92. package/dist/conversion/shared/tool-mapping.d.ts +1 -1
  93. package/dist/conversion/{shared/snapshot-utils.d.ts → snapshot-utils.d.ts} +11 -0
  94. package/dist/conversion/{shared/snapshot-utils.js → snapshot-utils.js} +14 -23
  95. package/dist/conversion/types/text-markup-normalizer.d.ts +13 -0
  96. package/dist/conversion/types/text-markup-normalizer.js +1 -0
  97. package/dist/filters/special/request-tools-normalize.js +1 -1
  98. package/dist/filters/special/response-tool-text-canonicalize.js +2 -2
  99. package/dist/native/router_hotpath_napi.node +0 -0
  100. package/dist/quota/quota-manager.js +31 -59
  101. package/dist/quota/quota-state.js +14 -7
  102. package/dist/router/virtual-router/bootstrap/profile-builder.d.ts +1 -0
  103. package/dist/router/virtual-router/bootstrap/profile-builder.js +13 -0
  104. package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +2 -0
  105. package/dist/router/virtual-router/bootstrap/provider-normalization.js +4 -1
  106. package/dist/router/virtual-router/bootstrap/streaming-helpers.d.ts +7 -0
  107. package/dist/router/virtual-router/bootstrap/streaming-helpers.js +44 -0
  108. package/dist/router/virtual-router/bootstrap.js +2 -0
  109. package/dist/router/virtual-router/engine/routing-state/store.d.ts +1 -2
  110. package/dist/router/virtual-router/engine/routing-state/store.js +2 -2
  111. package/dist/router/virtual-router/engine-legacy/config.d.ts +11 -0
  112. package/dist/router/virtual-router/engine-legacy/config.js +108 -0
  113. package/dist/router/virtual-router/engine-legacy/direct-model.d.ts +10 -0
  114. package/dist/router/virtual-router/engine-legacy/direct-model.js +38 -0
  115. package/dist/router/virtual-router/engine-legacy/health.d.ts +13 -0
  116. package/dist/router/virtual-router/engine-legacy/health.js +104 -0
  117. package/dist/router/virtual-router/engine-legacy/helpers.d.ts +16 -0
  118. package/dist/router/virtual-router/engine-legacy/helpers.js +226 -0
  119. package/dist/router/virtual-router/engine-legacy/route-finalize.d.ts +9 -0
  120. package/dist/router/virtual-router/engine-legacy/route-finalize.js +84 -0
  121. package/dist/router/virtual-router/engine-legacy/route-selection.d.ts +17 -0
  122. package/dist/router/virtual-router/engine-legacy/route-selection.js +205 -0
  123. package/dist/router/virtual-router/engine-legacy/route-state-allowlist.d.ts +3 -0
  124. package/dist/router/virtual-router/engine-legacy/route-state-allowlist.js +36 -0
  125. package/dist/router/virtual-router/engine-legacy/route-state.d.ts +12 -0
  126. package/dist/router/virtual-router/engine-legacy/route-state.js +386 -0
  127. package/dist/router/virtual-router/engine-legacy/route-utils.d.ts +19 -0
  128. package/dist/router/virtual-router/engine-legacy/route-utils.js +212 -0
  129. package/dist/router/virtual-router/engine-legacy/routing.d.ts +8 -0
  130. package/dist/router/virtual-router/engine-legacy/routing.js +8 -0
  131. package/dist/router/virtual-router/engine-legacy/selection-core.d.ts +28 -0
  132. package/dist/router/virtual-router/engine-legacy/selection-core.js +112 -0
  133. package/dist/router/virtual-router/engine-legacy/selection-state.d.ts +16 -0
  134. package/dist/router/virtual-router/engine-legacy/selection-state.js +187 -0
  135. package/dist/router/virtual-router/engine-legacy/state-accessors.d.ts +21 -0
  136. package/dist/router/virtual-router/engine-legacy/state-accessors.js +118 -0
  137. package/dist/router/virtual-router/engine-legacy.d.ts +123 -0
  138. package/dist/router/virtual-router/engine-legacy.js +194 -0
  139. package/dist/router/virtual-router/engine-logging.d.ts +2 -0
  140. package/dist/router/virtual-router/engine-logging.js +7 -2
  141. package/dist/router/virtual-router/engine-selection/key-parsing.js +0 -3
  142. package/dist/router/virtual-router/engine-selection/native-chat-request-filter-semantics.d.ts +1 -0
  143. package/dist/router/virtual-router/engine-selection/native-chat-request-filter-semantics.js +54 -0
  144. package/dist/router/virtual-router/engine-selection/native-hub-bridge-policy-semantics.d.ts +10 -0
  145. package/dist/router/virtual-router/engine-selection/native-hub-bridge-policy-semantics.js +67 -0
  146. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-governance-semantics.d.ts +22 -0
  147. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-governance-semantics.js +154 -0
  148. package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +38 -2
  149. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +75 -0
  150. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +205 -0
  151. package/dist/router/virtual-router/engine-selection/native-snapshot-hooks.d.ts +2 -0
  152. package/dist/router/virtual-router/engine-selection/native-snapshot-hooks.js +69 -0
  153. package/dist/router/virtual-router/engine-selection/native-virtual-router-engine-proxy.d.ts +16 -0
  154. package/dist/router/virtual-router/engine-selection/native-virtual-router-engine-proxy.js +14 -0
  155. package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.d.ts +2 -0
  156. package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.js +86 -0
  157. package/dist/router/virtual-router/engine-selection/tier-selection-quota-integration.js +100 -0
  158. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +99 -0
  159. package/dist/router/virtual-router/engine.d.ts +22 -105
  160. package/dist/router/virtual-router/engine.js +274 -1641
  161. package/dist/router/virtual-router/load-balancer.d.ts +8 -0
  162. package/dist/router/virtual-router/load-balancer.js +65 -2
  163. package/dist/router/virtual-router/provider-registry.js +2 -0
  164. package/dist/router/virtual-router/routing-instructions/clean.d.ts +3 -0
  165. package/dist/router/virtual-router/routing-instructions/clean.js +34 -0
  166. package/dist/router/virtual-router/routing-instructions/parse.d.ts +18 -0
  167. package/dist/router/virtual-router/routing-instructions/parse.js +377 -0
  168. package/dist/router/virtual-router/routing-instructions/state.d.ts +4 -0
  169. package/dist/router/virtual-router/routing-instructions/state.js +245 -0
  170. package/dist/router/virtual-router/routing-instructions/types.d.ts +70 -0
  171. package/dist/router/virtual-router/routing-instructions/types.js +2 -0
  172. package/dist/router/virtual-router/routing-instructions.d.ts +5 -89
  173. package/dist/router/virtual-router/routing-instructions.js +4 -655
  174. package/dist/router/virtual-router/sticky-session-store.d.ts +4 -0
  175. package/dist/router/virtual-router/sticky-session-store.js +19 -81
  176. package/dist/router/virtual-router/tool-signals.js +21 -3
  177. package/dist/router/virtual-router/types.d.ts +4 -0
  178. package/dist/servertool/clock/session-scope.js +32 -1
  179. package/dist/servertool/engine.js +79 -8
  180. package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +1 -1
  181. package/dist/servertool/handlers/clock-auto.js +1 -1
  182. package/dist/servertool/handlers/clock.js +1 -1
  183. package/dist/servertool/handlers/compaction-detect.d.ts +1 -1
  184. package/dist/servertool/handlers/compaction-detect.js +1 -1
  185. package/dist/servertool/handlers/gemini-empty-reply-continue.js +1 -1
  186. package/dist/servertool/handlers/iflow-model-error-retry.js +1 -1
  187. package/dist/servertool/handlers/recursive-detection-guard.js +1 -1
  188. package/dist/servertool/handlers/review.js +1 -1
  189. package/dist/servertool/handlers/stop-message-auto/iflow-followup.js +1 -1
  190. package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +1 -1
  191. package/dist/servertool/handlers/stop-message-auto.js +1 -1
  192. package/dist/servertool/handlers/vision.js +1 -1
  193. package/dist/servertool/handlers/web-search.js +1 -1
  194. package/dist/servertool/reenter-backend.js +1 -1
  195. package/dist/servertool/server-side-tools.js +2 -2
  196. package/dist/servertool/stop-gateway-context.js +1 -1
  197. package/dist/servertool/stop-message-compare-context.js +1 -1
  198. package/dist/sse/json-to-sse/event-generators/responses.d.ts +4 -0
  199. package/dist/sse/json-to-sse/event-generators/responses.js +95 -1
  200. package/dist/sse/json-to-sse/sequencers/responses-sequencer.js +6 -4
  201. package/dist/sse/sse-to-json/builders/response-builder.d.ts +8 -0
  202. package/dist/sse/sse-to-json/builders/response-builder.js +162 -4
  203. package/dist/sse/sse-to-json/responses-sse-to-json-converter.js +2 -0
  204. package/dist/sse/types/responses-types.d.ts +6 -2
  205. package/dist/tools/apply-patch/structured/coercion.js +5 -0
  206. package/dist/tools/args-json.js +29 -0
  207. package/package.json +8 -5
  208. package/dist/conversion/shared/args-mapping.js +0 -77
  209. package/dist/conversion/shared/compaction-detect.js +0 -4
  210. package/dist/conversion/shared/errors.js +0 -31
  211. package/dist/conversion/shared/media.js +0 -4
  212. package/dist/conversion/shared/payload-budget.js +0 -165
  213. package/dist/conversion/shared/protocol-field-allowlists.d.ts +0 -7
  214. package/dist/conversion/shared/protocol-field-allowlists.js +0 -149
  215. package/dist/conversion/shared/snapshot-hooks.d.ts +0 -11
  216. package/dist/conversion/shared/snapshot-hooks.js +0 -503
  217. package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.d.ts +0 -2
  218. package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.js +0 -129
  219. package/dist/conversion/shared/text-markup-normalizer/extractors-json.d.ts +0 -4
  220. package/dist/conversion/shared/text-markup-normalizer/extractors-json.js +0 -637
  221. package/dist/conversion/shared/text-markup-normalizer/extractors-shared.d.ts +0 -21
  222. package/dist/conversion/shared/text-markup-normalizer/extractors-shared.js +0 -177
  223. package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.d.ts +0 -5
  224. package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.js +0 -385
  225. package/dist/conversion/shared/text-markup-normalizer/extractors-xml.d.ts +0 -10
  226. package/dist/conversion/shared/text-markup-normalizer/extractors-xml.js +0 -602
  227. package/dist/conversion/shared/text-markup-normalizer/extractors.d.ts +0 -5
  228. package/dist/conversion/shared/text-markup-normalizer/extractors.js +0 -4
  229. package/dist/conversion/shared/tool-canonicalizer.d.ts +0 -2
  230. package/dist/conversion/shared/tool-canonicalizer.js +0 -38
  231. /package/dist/conversion/{shared/args-mapping.d.ts → args-mapping.d.ts} +0 -0
  232. /package/dist/conversion/{shared/bridge-actions.d.ts → bridge-actions.d.ts} +0 -0
  233. /package/dist/conversion/{shared/bridge-id-utils.d.ts → bridge-id-utils.d.ts} +0 -0
  234. /package/dist/conversion/{shared/bridge-instructions.d.ts → bridge-instructions.d.ts} +0 -0
  235. /package/dist/conversion/{shared/bridge-metadata.d.ts → bridge-metadata.d.ts} +0 -0
  236. /package/dist/conversion/{shared/bridge-policies.d.ts → bridge-policies.d.ts} +0 -0
  237. /package/dist/conversion/{shared/jsonish.d.ts → jsonish.d.ts} +0 -0
  238. /package/dist/conversion/{shared/mcp-injection.d.ts → mcp-injection.d.ts} +0 -0
  239. /package/dist/conversion/{shared/media.d.ts → media.d.ts} +0 -0
  240. /package/dist/conversion/{shared/payload-budget.d.ts → payload-budget.d.ts} +0 -0
  241. /package/dist/conversion/{shared → types}/bridge-message-types.d.ts +0 -0
  242. /package/dist/conversion/{shared → types}/bridge-message-types.js +0 -0
@@ -0,0 +1,118 @@
1
+ import { resolveStopMessageScope, getRoutingInstructionState, persistRoutingInstructionState } from '../engine/routing-state/store.js';
2
+ import { hasClientInjectScopedFields, stripClientInjectScopedFields } from './helpers.js';
3
+ import { flattenPoolTargets } from './route-utils.js';
4
+ export function getStopMessageState(engine, metadata) {
5
+ const hasArmedStopState = (candidate) => {
6
+ if (!candidate) {
7
+ return false;
8
+ }
9
+ const text = typeof candidate.stopMessageText === 'string' ? candidate.stopMessageText.trim() : '';
10
+ const maxRepeats = typeof candidate.stopMessageMaxRepeats === 'number' && Number.isFinite(candidate.stopMessageMaxRepeats)
11
+ ? Math.max(1, Math.floor(candidate.stopMessageMaxRepeats))
12
+ : 0;
13
+ const mode = typeof candidate.stopMessageStageMode === 'string'
14
+ ? candidate.stopMessageStageMode.trim().toLowerCase()
15
+ : '';
16
+ if (mode === 'off') {
17
+ return false;
18
+ }
19
+ return Boolean(text) && maxRepeats > 0;
20
+ };
21
+ const stopMessageScope = resolveStopMessageScope(metadata);
22
+ if (!stopMessageScope) {
23
+ const sessionScope = engine.resolveSessionScope(metadata);
24
+ if (sessionScope) {
25
+ const legacyState = getRoutingInstructionState(sessionScope, engine.routingInstructionState, engine.routingStateStore);
26
+ if (hasClientInjectScopedFields(legacyState)) {
27
+ const cleared = stripClientInjectScopedFields(legacyState);
28
+ engine.routingInstructionState.set(sessionScope, cleared);
29
+ persistRoutingInstructionState(sessionScope, cleared, engine.routingStateStore);
30
+ }
31
+ }
32
+ return null;
33
+ }
34
+ const effectiveState = getRoutingInstructionState(stopMessageScope, engine.routingInstructionState, engine.routingStateStore);
35
+ if (!hasArmedStopState(effectiveState)) {
36
+ return null;
37
+ }
38
+ const text = typeof effectiveState.stopMessageText === 'string' ? effectiveState.stopMessageText.trim() : '';
39
+ const maxRepeats = typeof effectiveState.stopMessageMaxRepeats === 'number' && Number.isFinite(effectiveState.stopMessageMaxRepeats)
40
+ ? Math.max(1, Math.floor(effectiveState.stopMessageMaxRepeats))
41
+ : 0;
42
+ const stageModeRaw = typeof effectiveState.stopMessageStageMode === 'string'
43
+ ? effectiveState.stopMessageStageMode.trim().toLowerCase()
44
+ : '';
45
+ const stageModeNormalized = stageModeRaw === 'on' || stageModeRaw === 'off' || stageModeRaw === 'auto'
46
+ ? stageModeRaw
47
+ : undefined;
48
+ const aiModeRaw = typeof effectiveState.stopMessageAiMode === 'string'
49
+ ? effectiveState.stopMessageAiMode.trim().toLowerCase()
50
+ : '';
51
+ const aiModeNormalized = aiModeRaw === 'on' || aiModeRaw === 'off' ? aiModeRaw : undefined;
52
+ if (stageModeNormalized === 'off' || !text || maxRepeats <= 0) {
53
+ return null;
54
+ }
55
+ return {
56
+ ...(text ? { stopMessageText: text } : {}),
57
+ stopMessageMaxRepeats: maxRepeats,
58
+ ...(typeof effectiveState.stopMessageSource === 'string' && effectiveState.stopMessageSource.trim()
59
+ ? { stopMessageSource: effectiveState.stopMessageSource.trim() }
60
+ : {}),
61
+ ...(typeof effectiveState.stopMessageUsed === 'number' && Number.isFinite(effectiveState.stopMessageUsed)
62
+ ? { stopMessageUsed: Math.max(0, Math.floor(effectiveState.stopMessageUsed)) }
63
+ : {}),
64
+ ...(typeof effectiveState.stopMessageUpdatedAt === 'number' && Number.isFinite(effectiveState.stopMessageUpdatedAt)
65
+ ? { stopMessageUpdatedAt: effectiveState.stopMessageUpdatedAt }
66
+ : {}),
67
+ ...(typeof effectiveState.stopMessageLastUsedAt === 'number' && Number.isFinite(effectiveState.stopMessageLastUsedAt)
68
+ ? { stopMessageLastUsedAt: effectiveState.stopMessageLastUsedAt }
69
+ : {}),
70
+ ...(stageModeNormalized ? { stopMessageStageMode: stageModeNormalized } : {}),
71
+ ...(aiModeNormalized ? { stopMessageAiMode: aiModeNormalized } : {}),
72
+ ...(typeof effectiveState.stopMessageAiSeedPrompt === 'string' && effectiveState.stopMessageAiSeedPrompt.trim()
73
+ ? { stopMessageAiSeedPrompt: effectiveState.stopMessageAiSeedPrompt.trim() }
74
+ : {}),
75
+ ...(Array.isArray(effectiveState.stopMessageAiHistory)
76
+ ? { stopMessageAiHistory: effectiveState.stopMessageAiHistory }
77
+ : {})
78
+ };
79
+ }
80
+ export function getPreCommandState(engine, metadata) {
81
+ const stopMessageScope = resolveStopMessageScope(metadata);
82
+ if (!stopMessageScope) {
83
+ return null;
84
+ }
85
+ const effectiveState = getRoutingInstructionState(stopMessageScope, engine.routingInstructionState, engine.routingStateStore);
86
+ if (!effectiveState) {
87
+ return null;
88
+ }
89
+ const scriptPath = typeof effectiveState.preCommandScriptPath === 'string' ? effectiveState.preCommandScriptPath.trim() : '';
90
+ if (!scriptPath) {
91
+ return null;
92
+ }
93
+ return {
94
+ preCommandScriptPath: scriptPath,
95
+ ...(typeof effectiveState.preCommandSource === 'string' && effectiveState.preCommandSource.trim()
96
+ ? { preCommandSource: effectiveState.preCommandSource.trim() }
97
+ : {}),
98
+ ...(typeof effectiveState.preCommandUpdatedAt === 'number' && Number.isFinite(effectiveState.preCommandUpdatedAt)
99
+ ? { preCommandUpdatedAt: effectiveState.preCommandUpdatedAt }
100
+ : {})
101
+ };
102
+ }
103
+ export function getStatus(engine) {
104
+ const routes = {};
105
+ for (const [route, pools] of Object.entries(engine.routing)) {
106
+ const stats = engine.routeAnalytics.getRouteStats(route);
107
+ routes[route] = {
108
+ providers: flattenPoolTargets(engine, pools),
109
+ hits: stats?.hits ?? 0,
110
+ lastUsedProvider: stats?.lastProvider,
111
+ ...(stats?.lastHit ? { lastHit: { ...stats.lastHit } } : {})
112
+ };
113
+ }
114
+ return {
115
+ routes,
116
+ health: engine.healthManager.getSnapshot()
117
+ };
118
+ }
@@ -0,0 +1,123 @@
1
+ import { ProviderHealthManager } from './health-manager.js';
2
+ import { ProviderRegistry } from './provider-registry.js';
3
+ import { RouteLoadBalancer } from './load-balancer.js';
4
+ import { RoutingClassifier } from './classifier.js';
5
+ import { ContextAdvisor } from './context-advisor.js';
6
+ import type { ProcessedRequest, StandardizedRequest } from '../../conversion/hub/types/standardized.js';
7
+ import { type RoutingPools, type RoutingDecision, type RoutingDiagnostics, type StopMessageStateSnapshot, type PreCommandStateSnapshot, type RoutePoolTier, type RouterMetadataInput, type RoutingFeatures, type VirtualRouterConfig, type VirtualRouterContextRoutingConfig, type TargetMetadata, type ProviderFailureEvent, type ProviderErrorEvent, type ProviderSuccessEvent, type VirtualRouterHealthStore } from './types.js';
8
+ import type { RoutingInstructionState } from './routing-instructions.js';
9
+ import type { ProviderQuotaView } from './types.js';
10
+ import type { RoutingInstructionStateStoreLike } from './engine/routing-state/store.js';
11
+ import { RouteAnalytics } from './engine/route-analytics.js';
12
+ import { StickySessionManager } from './engine/sticky-session-manager.js';
13
+ import { CooldownManager } from './engine/cooldown-manager.js';
14
+ import { getStatus as getStatusImpl } from './engine-legacy/state-accessors.js';
15
+ export declare class VirtualRouterEngine {
16
+ routing: RoutingPools;
17
+ readonly providerRegistry: ProviderRegistry;
18
+ readonly healthManager: ProviderHealthManager;
19
+ loadBalancer: RouteLoadBalancer;
20
+ classifier: RoutingClassifier;
21
+ readonly contextAdvisor: ContextAdvisor;
22
+ contextRouting: VirtualRouterContextRoutingConfig | undefined;
23
+ readonly routeAnalytics: RouteAnalytics;
24
+ stickySessionManager: StickySessionManager;
25
+ cooldownManager: CooldownManager;
26
+ antigravityLeasePersistence: {
27
+ loadedOnce: boolean;
28
+ loadedMtimeMs: number | null;
29
+ flushTimer: ReturnType<typeof setTimeout> | null;
30
+ };
31
+ readonly debug: Console;
32
+ healthConfig: VirtualRouterConfig['health'] | null;
33
+ readonly statsCenter: import("../../index.js").StatsCenter;
34
+ webSearchForce: boolean;
35
+ healthStore?: VirtualRouterHealthStore;
36
+ routingStateStore: RoutingInstructionStateStoreLike;
37
+ routingInstructionState: Map<string, RoutingInstructionState>;
38
+ quotaView?: ProviderQuotaView;
39
+ /**
40
+ * Backward-compatible test/debug surface used by existing regression scripts.
41
+ * Keep this as a read-only view over StickySessionManager storage.
42
+ */
43
+ get antigravitySessionAliasStore(): Map<string, string>;
44
+ constructor(deps?: {
45
+ healthStore?: VirtualRouterHealthStore;
46
+ routingStateStore?: RoutingInstructionStateStoreLike;
47
+ quotaView?: ProviderQuotaView;
48
+ });
49
+ updateDeps(deps: {
50
+ healthStore?: VirtualRouterHealthStore | null;
51
+ routingStateStore?: RoutingInstructionStateStoreLike | null;
52
+ quotaView?: ProviderQuotaView | null;
53
+ }): void;
54
+ initialize(config: VirtualRouterConfig): void;
55
+ route(request: StandardizedRequest | ProcessedRequest, metadata: RouterMetadataInput): {
56
+ target: TargetMetadata;
57
+ decision: RoutingDecision;
58
+ diagnostics: RoutingDiagnostics;
59
+ };
60
+ getStopMessageState(metadata: RouterMetadataInput): StopMessageStateSnapshot | null;
61
+ getPreCommandState(metadata: RouterMetadataInput): PreCommandStateSnapshot | null;
62
+ handleProviderFailure(event: ProviderFailureEvent): void;
63
+ handleProviderError(event: ProviderErrorEvent): void;
64
+ handleProviderSuccess(event: ProviderSuccessEvent): void;
65
+ getStatus(): ReturnType<typeof getStatusImpl>;
66
+ normalizeRouteAlias(routeName: string | undefined): string;
67
+ buildRouteCandidates(requestedRoute: string, classificationCandidates: string[] | undefined, features: RoutingFeatures): string[];
68
+ reorderForInlineVision(routeNames: string[]): string[];
69
+ reorderForPreferredModel(routeNames: string[], modelId: string): string[];
70
+ routeSupportsModel(routeName: string, modelId: string): boolean;
71
+ routeSupportsInlineVision(routeName: string): boolean;
72
+ sortByPriority(routeNames: string[]): string[];
73
+ routeWeight(routeName: string): number;
74
+ routeHasForceFlag(routeName: string): boolean;
75
+ routeHasTargets(pools?: RoutePoolTier[]): boolean;
76
+ hasPrimaryPool(pools?: RoutePoolTier[]): boolean;
77
+ sortRoutePools(pools?: RoutePoolTier[]): RoutePoolTier[];
78
+ flattenPoolTargets(pools?: RoutePoolTier[]): string[];
79
+ markProviderCooldown(providerKey: string, cooldownMs: number | undefined): void;
80
+ clearProviderCooldown(providerKey: string): void;
81
+ isProviderCoolingDown(providerKey: string): boolean;
82
+ getProviderCooldownRemainingMs(providerKey: string): number;
83
+ restoreHealthFromStore(): void;
84
+ buildHealthSnapshot(): import("./types.js").VirtualRouterHealthSnapshot;
85
+ persistHealthSnapshot(): void;
86
+ parseDirectProviderModel(model: string | undefined): {
87
+ providerId: string;
88
+ modelId: string;
89
+ } | null;
90
+ shouldFallbackDirectModelForMedia(direct: {
91
+ providerId: string;
92
+ modelId: string;
93
+ }, features: RoutingFeatures): boolean;
94
+ selectProvider(requestedRoute: string, metadata: RouterMetadataInput, classification: any, features: RoutingFeatures, routingState?: RoutingInstructionState): {
95
+ providerKey: string;
96
+ routeUsed: string;
97
+ pool: string[];
98
+ poolId?: string;
99
+ };
100
+ selectFromCandidates(routes: string[], metadata: RouterMetadataInput, classification: any, features: RoutingFeatures, state: RoutingInstructionState, requiredProviderKeys?: Set<string>, allowAliasRotation?: boolean): {
101
+ providerKey: string;
102
+ routeUsed: string;
103
+ pool: string[];
104
+ poolId?: string;
105
+ };
106
+ selectFromStickyPool(stickyKeySet: Set<string>, metadata: RouterMetadataInput, features: RoutingFeatures, state: RoutingInstructionState, allowAliasRotation?: boolean): {
107
+ providerKey: string;
108
+ routeUsed: string;
109
+ pool: string[];
110
+ poolId?: string;
111
+ } | null;
112
+ extractExcludedProviderKeySet(metadata: RouterMetadataInput | undefined): Set<string>;
113
+ resolveSelectionPenalty(providerKey: string): number | undefined;
114
+ resolveInstructionProcessModeForSelection(providerKey: string, routingState: RoutingInstructionState): 'chat' | 'passthrough' | undefined;
115
+ resolveInstructionTarget(target: NonNullable<RoutingInstructionState['forcedTarget']>): {
116
+ mode: 'exact' | 'filter';
117
+ keys: string[];
118
+ } | null;
119
+ buildStickyRouteCandidatesFromFiltered(filteredCandidates: string[], stickyKeySet: Set<string>): string[];
120
+ filterCandidatesByRoutingState(routes: string[], state: RoutingInstructionState): string[];
121
+ resolveStickyKey(metadata: RouterMetadataInput): string | undefined;
122
+ resolveSessionScope(metadata: RouterMetadataInput): string | undefined;
123
+ }
@@ -0,0 +1,194 @@
1
+ import { ProviderHealthManager } from './health-manager.js';
2
+ import { ProviderRegistry } from './provider-registry.js';
3
+ import { RouteLoadBalancer } from './load-balancer.js';
4
+ import { RoutingClassifier } from './classifier.js';
5
+ import { ContextAdvisor } from './context-advisor.js';
6
+ import { getStatsCenter } from '../../telemetry/stats-center.js';
7
+ import { loadRoutingInstructionStateSync, saveRoutingInstructionStateAsync, saveRoutingInstructionStateSync } from './sticky-session-store.js';
8
+ import { resolveStickyKey as resolveStickyKeyImpl, resolveSessionScope as resolveSessionScopeImpl } from './engine/routing-state/keys.js';
9
+ import { RouteAnalytics } from './engine/route-analytics.js';
10
+ import { StickySessionManager } from './engine/sticky-session-manager.js';
11
+ import { CooldownManager } from './engine/cooldown-manager.js';
12
+ import { routeRequest } from './engine-legacy/routing.js';
13
+ import { updateDeps as updateDepsImpl, initialize as initializeImpl } from './engine-legacy/config.js';
14
+ import { handleProviderError as handleProviderErrorImpl, handleProviderFailure as handleProviderFailureImpl, handleProviderSuccess as handleProviderSuccessImpl, buildHealthSnapshot as buildHealthSnapshotImpl, persistHealthSnapshot as persistHealthSnapshotImpl, markProviderCooldown as markProviderCooldownImpl, clearProviderCooldown as clearProviderCooldownImpl, isProviderCoolingDown as isProviderCoolingDownImpl, getProviderCooldownRemainingMs as getProviderCooldownRemainingMsImpl, restoreHealthFromStore as restoreHealthFromStoreImpl } from './engine-legacy/health.js';
15
+ import { getStopMessageState as getStopMessageStateImpl, getPreCommandState as getPreCommandStateImpl, getStatus as getStatusImpl } from './engine-legacy/state-accessors.js';
16
+ import { selectProvider as selectProviderImpl, selectFromCandidates as selectFromCandidatesImpl, selectFromStickyPool as selectFromStickyPoolImpl, extractExcludedProviderKeySet as extractExcludedProviderKeySetImpl } from './engine-legacy/selection-core.js';
17
+ import { parseDirectProviderModel as parseDirectProviderModelImpl, shouldFallbackDirectModelForMedia as shouldFallbackDirectModelForMediaImpl } from './engine-legacy/direct-model.js';
18
+ import { normalizeRouteAlias as normalizeRouteAliasImpl, buildRouteCandidates as buildRouteCandidatesImpl, reorderForInlineVision as reorderForInlineVisionImpl, reorderForPreferredModel as reorderForPreferredModelImpl, routeSupportsModel as routeSupportsModelImpl, routeSupportsInlineVision as routeSupportsInlineVisionImpl, sortByPriority as sortByPriorityImpl, routeWeight as routeWeightImpl, routeHasForceFlag as routeHasForceFlagImpl, routeHasTargets as routeHasTargetsImpl, hasPrimaryPool as hasPrimaryPoolImpl, sortRoutePools as sortRoutePoolsImpl, flattenPoolTargets as flattenPoolTargetsImpl } from './engine-legacy/route-utils.js';
19
+ import { resolveSelectionPenalty as resolveSelectionPenaltyImpl, resolveInstructionProcessModeForSelection as resolveInstructionProcessModeForSelectionImpl, resolveInstructionTarget as resolveInstructionTargetImpl, filterCandidatesByRoutingState as filterCandidatesByRoutingStateImpl, buildStickyRouteCandidatesFromFiltered as buildStickyRouteCandidatesFromFilteredImpl } from './engine-legacy/selection-state.js';
20
+ export class VirtualRouterEngine {
21
+ routing = {};
22
+ providerRegistry = new ProviderRegistry();
23
+ healthManager = new ProviderHealthManager();
24
+ loadBalancer = new RouteLoadBalancer();
25
+ classifier = new RoutingClassifier({});
26
+ contextAdvisor = new ContextAdvisor();
27
+ contextRouting;
28
+ routeAnalytics = new RouteAnalytics();
29
+ stickySessionManager = new StickySessionManager();
30
+ cooldownManager;
31
+ antigravityLeasePersistence = { loadedOnce: false, loadedMtimeMs: null, flushTimer: null };
32
+ debug = console; // thin hook; host may monkey-patch for colored logging
33
+ healthConfig = null;
34
+ statsCenter = getStatsCenter();
35
+ // Derived flags from VirtualRouterConfig/routing used by process / response layers.
36
+ webSearchForce = false;
37
+ healthStore;
38
+ routingStateStore = {
39
+ loadSync: loadRoutingInstructionStateSync,
40
+ saveAsync: saveRoutingInstructionStateAsync,
41
+ saveSync: saveRoutingInstructionStateSync
42
+ };
43
+ routingInstructionState = new Map();
44
+ quotaView;
45
+ /**
46
+ * Backward-compatible test/debug surface used by existing regression scripts.
47
+ * Keep this as a read-only view over StickySessionManager storage.
48
+ */
49
+ get antigravitySessionAliasStore() {
50
+ return this.stickySessionManager.getAllStores().sessionAliasStore;
51
+ }
52
+ constructor(deps) {
53
+ this.cooldownManager = new CooldownManager({
54
+ healthStore: deps?.healthStore,
55
+ healthConfig: this.healthConfig,
56
+ quotaView: deps?.quotaView
57
+ });
58
+ if (deps?.healthStore) {
59
+ this.healthStore = deps.healthStore;
60
+ }
61
+ if (deps?.routingStateStore) {
62
+ this.routingStateStore = deps.routingStateStore;
63
+ }
64
+ if (deps?.quotaView) {
65
+ this.quotaView = deps.quotaView;
66
+ }
67
+ }
68
+ updateDeps(deps) {
69
+ updateDepsImpl(this, deps);
70
+ }
71
+ initialize(config) {
72
+ initializeImpl(this, config);
73
+ }
74
+ route(request, metadata) {
75
+ return routeRequest(this, request, metadata);
76
+ }
77
+ getStopMessageState(metadata) {
78
+ return getStopMessageStateImpl(this, metadata);
79
+ }
80
+ getPreCommandState(metadata) {
81
+ return getPreCommandStateImpl(this, metadata);
82
+ }
83
+ handleProviderFailure(event) {
84
+ handleProviderFailureImpl(this, event);
85
+ }
86
+ handleProviderError(event) {
87
+ handleProviderErrorImpl(this, event);
88
+ }
89
+ handleProviderSuccess(event) {
90
+ handleProviderSuccessImpl(this, event);
91
+ }
92
+ getStatus() {
93
+ return getStatusImpl(this);
94
+ }
95
+ normalizeRouteAlias(routeName) {
96
+ return normalizeRouteAliasImpl(routeName);
97
+ }
98
+ buildRouteCandidates(requestedRoute, classificationCandidates, features) {
99
+ return buildRouteCandidatesImpl(this, requestedRoute, classificationCandidates, features);
100
+ }
101
+ reorderForInlineVision(routeNames) {
102
+ return reorderForInlineVisionImpl(this, routeNames);
103
+ }
104
+ reorderForPreferredModel(routeNames, modelId) {
105
+ return reorderForPreferredModelImpl(this, routeNames, modelId);
106
+ }
107
+ routeSupportsModel(routeName, modelId) {
108
+ return routeSupportsModelImpl(this, routeName, modelId);
109
+ }
110
+ routeSupportsInlineVision(routeName) {
111
+ return routeSupportsInlineVisionImpl(this, routeName);
112
+ }
113
+ sortByPriority(routeNames) {
114
+ return sortByPriorityImpl(routeNames);
115
+ }
116
+ routeWeight(routeName) {
117
+ return routeWeightImpl(routeName);
118
+ }
119
+ routeHasForceFlag(routeName) {
120
+ return routeHasForceFlagImpl(this, routeName);
121
+ }
122
+ routeHasTargets(pools) {
123
+ return routeHasTargetsImpl(this, pools);
124
+ }
125
+ hasPrimaryPool(pools) {
126
+ return hasPrimaryPoolImpl(this, pools);
127
+ }
128
+ sortRoutePools(pools) {
129
+ return sortRoutePoolsImpl(this, pools);
130
+ }
131
+ flattenPoolTargets(pools) {
132
+ return flattenPoolTargetsImpl(this, pools);
133
+ }
134
+ markProviderCooldown(providerKey, cooldownMs) {
135
+ markProviderCooldownImpl(this, providerKey, cooldownMs);
136
+ }
137
+ clearProviderCooldown(providerKey) {
138
+ clearProviderCooldownImpl(this, providerKey);
139
+ }
140
+ isProviderCoolingDown(providerKey) {
141
+ return isProviderCoolingDownImpl(this, providerKey);
142
+ }
143
+ getProviderCooldownRemainingMs(providerKey) {
144
+ return getProviderCooldownRemainingMsImpl(this, providerKey);
145
+ }
146
+ restoreHealthFromStore() {
147
+ restoreHealthFromStoreImpl(this);
148
+ }
149
+ buildHealthSnapshot() {
150
+ return buildHealthSnapshotImpl(this);
151
+ }
152
+ persistHealthSnapshot() {
153
+ persistHealthSnapshotImpl(this);
154
+ }
155
+ parseDirectProviderModel(model) {
156
+ return parseDirectProviderModelImpl(this, model);
157
+ }
158
+ shouldFallbackDirectModelForMedia(direct, features) {
159
+ return shouldFallbackDirectModelForMediaImpl(this, direct, features);
160
+ }
161
+ selectProvider(requestedRoute, metadata, classification, features, routingState) {
162
+ return selectProviderImpl(this, requestedRoute, metadata, classification, features, routingState);
163
+ }
164
+ selectFromCandidates(routes, metadata, classification, features, state, requiredProviderKeys, allowAliasRotation) {
165
+ return selectFromCandidatesImpl(this, routes, metadata, classification, features, state, requiredProviderKeys, allowAliasRotation);
166
+ }
167
+ selectFromStickyPool(stickyKeySet, metadata, features, state, allowAliasRotation) {
168
+ return selectFromStickyPoolImpl(this, stickyKeySet, metadata, features, state, allowAliasRotation);
169
+ }
170
+ extractExcludedProviderKeySet(metadata) {
171
+ return extractExcludedProviderKeySetImpl(this, metadata);
172
+ }
173
+ resolveSelectionPenalty(providerKey) {
174
+ return resolveSelectionPenaltyImpl(this, providerKey);
175
+ }
176
+ resolveInstructionProcessModeForSelection(providerKey, routingState) {
177
+ return resolveInstructionProcessModeForSelectionImpl(this, providerKey, routingState);
178
+ }
179
+ resolveInstructionTarget(target) {
180
+ return resolveInstructionTargetImpl(this, target);
181
+ }
182
+ buildStickyRouteCandidatesFromFiltered(filteredCandidates, stickyKeySet) {
183
+ return buildStickyRouteCandidatesFromFilteredImpl(this, filteredCandidates, stickyKeySet);
184
+ }
185
+ filterCandidatesByRoutingState(routes, state) {
186
+ return filterCandidatesByRoutingStateImpl(this, routes, state);
187
+ }
188
+ resolveStickyKey(metadata) {
189
+ return resolveStickyKeyImpl(metadata);
190
+ }
191
+ resolveSessionScope(metadata) {
192
+ return resolveSessionScopeImpl(metadata);
193
+ }
194
+ }
@@ -19,6 +19,7 @@ export type StopMessageRuntimeSummary = {
19
19
  };
20
20
  export type VirtualRouterHitRecord = {
21
21
  timestampMs: number;
22
+ requestId?: string;
22
23
  routeName: string;
23
24
  poolId?: string;
24
25
  providerKey: string;
@@ -34,6 +35,7 @@ export type VirtualRouterHitEventMeta = {
34
35
  entryEndpoint?: string;
35
36
  };
36
37
  export declare function createVirtualRouterHitRecord(input: {
38
+ requestId?: string;
37
39
  routeName: string;
38
40
  poolId?: string;
39
41
  providerKey: string;
@@ -65,6 +65,7 @@ export function createVirtualRouterHitRecord(input) {
65
65
  timestampMs: typeof input.timestampMs === 'number' && Number.isFinite(input.timestampMs)
66
66
  ? input.timestampMs
67
67
  : Date.now(),
68
+ ...(input.requestId ? { requestId: input.requestId } : {}),
68
69
  routeName: input.routeName,
69
70
  ...(input.poolId ? { poolId: input.poolId } : {}),
70
71
  providerKey: input.providerKey,
@@ -249,6 +250,8 @@ export function formatVirtualRouterHit(record) {
249
250
  const { providerLabel, resolvedModel } = describeTargetProvider(record.providerKey, record.modelId);
250
251
  const routeLabel = record.poolId ? `${record.routeName}/${record.poolId}` : record.routeName;
251
252
  const targetLabel = `${routeLabel} -> ${providerLabel}${resolvedModel ? '.' + resolvedModel : ''}`;
253
+ const requestId = typeof record.requestId === 'string' ? record.requestId : '';
254
+ const requestLabel = requestId && !requestId.includes('unknown') ? ` req=${requestId}` : '';
252
255
  const stickyText = formatStickyScope(record.stickyScope);
253
256
  const stickyLabel = stickyText ? ` ${stickyColor}[sticky:${stickyText}]${reset}` : '';
254
257
  const reasonLabel = record.hitReason ? ` reason=${record.hitReason}` : '';
@@ -276,7 +279,7 @@ export function formatVirtualRouterHit(record) {
276
279
  }
277
280
  stopLabel = ` ${stopColor}[stopMessage:${parts.join(' ')}]${reset}`;
278
281
  }
279
- return `${prefix} ${timeLabel} ${routeColor}${targetLabel}${stickyLabel}${reasonLabel}${requestTokenLabel}${penaltyLabel}${stopLabel}${reset}`;
282
+ return `${prefix} ${timeLabel}${requestLabel} ${routeColor}${targetLabel}${stickyLabel}${reasonLabel}${requestTokenLabel}${penaltyLabel}${stopLabel}${reset}`;
280
283
  }
281
284
  catch {
282
285
  const now = new Date(record.timestampMs);
@@ -284,6 +287,8 @@ export function formatVirtualRouterHit(record) {
284
287
  const routeLabel = record.poolId ? `${record.routeName}/${record.poolId}` : record.routeName;
285
288
  const stickyText = formatStickyScope(record.stickyScope);
286
289
  const stickyLabel = stickyText ? ` [sticky:${stickyText}]` : '';
290
+ const requestId = typeof record.requestId === 'string' ? record.requestId : '';
291
+ const requestLabel = requestId && !requestId.includes('unknown') ? ` req=${requestId}` : '';
287
292
  const requestTokenLabel = typeof record.requestTokens === 'number' && Number.isFinite(record.requestTokens)
288
293
  ? ` reqTokens=${Math.max(0, Math.round(record.requestTokens))}`
289
294
  : '';
@@ -298,6 +303,6 @@ export function formatVirtualRouterHit(record) {
298
303
  const left = stop.remaining >= 0 ? String(stop.remaining) : 'n/a';
299
304
  stopLabel = ` [stopMessage:${safeText} mode=${stop.mode} round=${rounds} active=${stop.active ? 'yes' : 'no'} left=${left}]`;
300
305
  }
301
- return `[virtual-router-hit] ${timestamp} ${routeLabel} -> ${record.providerKey}${record.modelId ? '.' + record.modelId : ''}${stickyLabel}${record.hitReason ? ` reason=${record.hitReason}` : ''}${requestTokenLabel}${penaltyLabel}${stopLabel}`;
306
+ return `[virtual-router-hit] ${timestamp}${requestLabel} ${routeLabel} -> ${record.providerKey}${record.modelId ? '.' + record.modelId : ''}${stickyLabel}${record.hitReason ? ` reason=${record.hitReason}` : ''}${requestTokenLabel}${penaltyLabel}${stopLabel}`;
302
307
  }
303
308
  }
@@ -13,9 +13,6 @@ export function extractKeyAlias(providerKey) {
13
13
  return normalizeAliasDescriptor(alias);
14
14
  }
15
15
  export function normalizeAliasDescriptor(alias) {
16
- if (/^\d+-/.test(alias)) {
17
- return alias.replace(/^\d+-/, '');
18
- }
19
16
  return alias;
20
17
  }
21
18
  export function extractKeyIndex(providerKey) {
@@ -0,0 +1 @@
1
+ export declare function buildGovernedFilterPayloadWithNativeFallback(request: unknown): Record<string, unknown>;
@@ -0,0 +1,54 @@
1
+ import { failNativeRequired, isNativeDisabledByEnv } from './native-router-hotpath-policy.js';
2
+ import { loadNativeRouterHotpathBindingForInternalUse } from './native-router-hotpath.js';
3
+ function readNativeFunction(name) {
4
+ const binding = loadNativeRouterHotpathBindingForInternalUse();
5
+ const fn = binding?.[name];
6
+ return typeof fn === 'function' ? fn : null;
7
+ }
8
+ function safeStringify(value) {
9
+ try {
10
+ return JSON.stringify(value);
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ }
16
+ function parseOutputRecord(raw) {
17
+ try {
18
+ const parsed = JSON.parse(raw);
19
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
20
+ return null;
21
+ }
22
+ return parsed;
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
28
+ export function buildGovernedFilterPayloadWithNativeFallback(request) {
29
+ const capability = 'buildGovernedFilterPayloadJson';
30
+ const fail = (reason) => failNativeRequired(capability, reason);
31
+ if (isNativeDisabledByEnv()) {
32
+ return fail('native disabled');
33
+ }
34
+ const fn = readNativeFunction(capability);
35
+ if (!fn) {
36
+ return fail();
37
+ }
38
+ const requestJson = safeStringify(request);
39
+ if (!requestJson) {
40
+ return fail('json stringify failed');
41
+ }
42
+ try {
43
+ const raw = fn(requestJson);
44
+ if (typeof raw !== 'string' || !raw) {
45
+ return fail('empty result');
46
+ }
47
+ const parsed = parseOutputRecord(raw);
48
+ return parsed ?? fail('invalid payload');
49
+ }
50
+ catch (error) {
51
+ const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
52
+ return fail(reason);
53
+ }
54
+ }
@@ -47,6 +47,15 @@ export interface NativeProtocolSpec {
47
47
  providerOutbound: NativeProviderOutboundPolicySpec;
48
48
  toolSurface: NativeToolSurfaceSpec;
49
49
  }
50
+ export interface NativeHubProtocolAllowlists {
51
+ openaiChatAllowedFields: string[];
52
+ openaiChatParametersWrapperAllowKeys: string[];
53
+ openaiResponsesAllowedFields: string[];
54
+ openaiResponsesParametersWrapperAllowKeys: string[];
55
+ anthropicAllowedFields: string[];
56
+ anthropicParametersWrapperAllowKeys: string[];
57
+ geminiAllowedFields: string[];
58
+ }
50
59
  export declare function resolveHubProtocolSpecWithNative(input: {
51
60
  protocol?: string;
52
61
  allowlists: {
@@ -59,4 +68,5 @@ export declare function resolveHubProtocolSpecWithNative(input: {
59
68
  geminiAllowedFields: readonly string[];
60
69
  };
61
70
  }): NativeProtocolSpec;
71
+ export declare function resolveHubProtocolAllowlistsWithNative(): NativeHubProtocolAllowlists;
62
72
  export {};
@@ -298,6 +298,50 @@ function parseProtocolSpec(raw) {
298
298
  return null;
299
299
  }
300
300
  }
301
+ function parseStringArrayValue(value) {
302
+ if (!Array.isArray(value)) {
303
+ return null;
304
+ }
305
+ const out = value.filter((entry) => typeof entry === 'string');
306
+ return out;
307
+ }
308
+ function parseHubProtocolAllowlists(raw) {
309
+ try {
310
+ const parsed = JSON.parse(raw);
311
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
312
+ return null;
313
+ }
314
+ const row = parsed;
315
+ const openaiChatAllowedFields = parseStringArrayValue(row.openaiChatAllowedFields);
316
+ const openaiChatParametersWrapperAllowKeys = parseStringArrayValue(row.openaiChatParametersWrapperAllowKeys);
317
+ const openaiResponsesAllowedFields = parseStringArrayValue(row.openaiResponsesAllowedFields);
318
+ const openaiResponsesParametersWrapperAllowKeys = parseStringArrayValue(row.openaiResponsesParametersWrapperAllowKeys);
319
+ const anthropicAllowedFields = parseStringArrayValue(row.anthropicAllowedFields);
320
+ const anthropicParametersWrapperAllowKeys = parseStringArrayValue(row.anthropicParametersWrapperAllowKeys);
321
+ const geminiAllowedFields = parseStringArrayValue(row.geminiAllowedFields);
322
+ if (!openaiChatAllowedFields ||
323
+ !openaiChatParametersWrapperAllowKeys ||
324
+ !openaiResponsesAllowedFields ||
325
+ !openaiResponsesParametersWrapperAllowKeys ||
326
+ !anthropicAllowedFields ||
327
+ !anthropicParametersWrapperAllowKeys ||
328
+ !geminiAllowedFields) {
329
+ return null;
330
+ }
331
+ return {
332
+ openaiChatAllowedFields,
333
+ openaiChatParametersWrapperAllowKeys,
334
+ openaiResponsesAllowedFields,
335
+ openaiResponsesParametersWrapperAllowKeys,
336
+ anthropicAllowedFields,
337
+ anthropicParametersWrapperAllowKeys,
338
+ geminiAllowedFields
339
+ };
340
+ }
341
+ catch {
342
+ return null;
343
+ }
344
+ }
301
345
  export function resolveHubProtocolSpecWithNative(input) {
302
346
  const capability = 'resolveHubProtocolSpecJson';
303
347
  const fail = (reason) => failNativeRequired(capability, reason);
@@ -336,3 +380,26 @@ export function resolveHubProtocolSpecWithNative(input) {
336
380
  return fail(reason);
337
381
  }
338
382
  }
383
+ export function resolveHubProtocolAllowlistsWithNative() {
384
+ const capability = 'resolveHubProtocolAllowlistsJson';
385
+ const fail = (reason) => failNativeRequired(capability, reason);
386
+ if (isNativeDisabledByEnv()) {
387
+ return fail('native disabled');
388
+ }
389
+ const fn = readNativeFunction(capability);
390
+ if (!fn) {
391
+ return fail();
392
+ }
393
+ try {
394
+ const raw = fn();
395
+ if (typeof raw !== 'string' || !raw) {
396
+ return fail('empty result');
397
+ }
398
+ const parsed = parseHubProtocolAllowlists(raw);
399
+ return parsed ?? fail('invalid payload');
400
+ }
401
+ catch (error) {
402
+ const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
403
+ return fail(reason);
404
+ }
405
+ }