@jsonstudio/llms 0.6.954 → 0.6.1164

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 (130) hide show
  1. package/dist/conversion/hub/operation-table/operation-table-runner.d.ts +18 -0
  2. package/dist/conversion/hub/operation-table/operation-table-runner.js +158 -0
  3. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.d.ts +8 -0
  4. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +303 -0
  5. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.d.ts +8 -0
  6. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +413 -0
  7. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.d.ts +7 -0
  8. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +841 -0
  9. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.d.ts +21 -0
  10. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +535 -0
  11. package/dist/conversion/hub/ops/operations.d.ts +19 -0
  12. package/dist/conversion/hub/ops/operations.js +126 -0
  13. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +9 -0
  14. package/dist/conversion/hub/pipeline/hub-pipeline.js +489 -19
  15. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +6 -0
  16. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +11 -0
  17. package/dist/conversion/hub/policy/policy-engine.js +41 -9
  18. package/dist/conversion/hub/policy/protocol-spec.d.ts +25 -0
  19. package/dist/conversion/hub/policy/protocol-spec.js +73 -23
  20. package/dist/conversion/hub/process/chat-process.js +252 -41
  21. package/dist/conversion/hub/response/provider-response.js +175 -2
  22. package/dist/conversion/hub/response/response-runtime.js +1 -1
  23. package/dist/conversion/hub/semantic-mappers/anthropic-mapper.d.ts +1 -8
  24. package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +1 -365
  25. package/dist/conversion/hub/semantic-mappers/chat-mapper.d.ts +1 -8
  26. package/dist/conversion/hub/semantic-mappers/chat-mapper.js +1 -467
  27. package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +1 -7
  28. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +1 -903
  29. package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +1 -21
  30. package/dist/conversion/hub/semantic-mappers/responses-mapper.js +1 -593
  31. package/dist/conversion/hub/tool-surface/tool-surface-engine.d.ts +18 -0
  32. package/dist/conversion/hub/tool-surface/tool-surface-engine.js +571 -0
  33. package/dist/conversion/responses/responses-openai-bridge.js +14 -2
  34. package/dist/conversion/shared/bridge-message-utils.js +2 -8
  35. package/dist/conversion/shared/bridge-policies.js +5 -105
  36. package/dist/conversion/shared/gemini-tool-utils.js +89 -15
  37. package/dist/conversion/shared/protocol-field-allowlists.d.ts +7 -0
  38. package/dist/conversion/shared/protocol-field-allowlists.js +145 -0
  39. package/dist/conversion/shared/reasoning-tool-normalizer.js +4 -2
  40. package/dist/conversion/shared/snapshot-hooks.js +166 -3
  41. package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
  42. package/dist/conversion/shared/text-markup-normalizer.js +345 -9
  43. package/dist/conversion/shared/thought-signature-validator.d.ts +52 -0
  44. package/dist/conversion/shared/thought-signature-validator.js +170 -0
  45. package/dist/conversion/shared/tool-argument-repairer.d.ts +39 -0
  46. package/dist/conversion/shared/tool-argument-repairer.js +56 -0
  47. package/dist/conversion/shared/tool-call-id-manager.d.ts +113 -0
  48. package/dist/conversion/shared/tool-call-id-manager.js +231 -0
  49. package/dist/conversion/shared/tool-canonicalizer.js +2 -11
  50. package/dist/router/virtual-router/bootstrap.js +54 -5
  51. package/dist/router/virtual-router/engine-selection.js +132 -42
  52. package/dist/router/virtual-router/engine.d.ts +3 -0
  53. package/dist/router/virtual-router/engine.js +142 -33
  54. package/dist/router/virtual-router/health-weighted.d.ts +25 -0
  55. package/dist/router/virtual-router/health-weighted.js +63 -0
  56. package/dist/router/virtual-router/load-balancer.d.ts +2 -0
  57. package/dist/router/virtual-router/load-balancer.js +45 -16
  58. package/dist/router/virtual-router/routing-instructions.js +17 -1
  59. package/dist/router/virtual-router/sticky-session-store.js +136 -24
  60. package/dist/router/virtual-router/stop-message-file-resolver.d.ts +1 -0
  61. package/dist/router/virtual-router/stop-message-file-resolver.js +74 -0
  62. package/dist/router/virtual-router/stop-message-state-sync.d.ts +15 -0
  63. package/dist/router/virtual-router/stop-message-state-sync.js +57 -0
  64. package/dist/router/virtual-router/types.d.ts +70 -0
  65. package/dist/servertool/clock/config.d.ts +7 -0
  66. package/dist/servertool/clock/config.js +27 -0
  67. package/dist/servertool/clock/daemon.d.ts +3 -0
  68. package/dist/servertool/clock/daemon.js +79 -0
  69. package/dist/servertool/clock/io.d.ts +2 -0
  70. package/dist/servertool/clock/io.js +13 -0
  71. package/dist/servertool/clock/paths.d.ts +4 -0
  72. package/dist/servertool/clock/paths.js +25 -0
  73. package/dist/servertool/clock/session-store.d.ts +3 -0
  74. package/dist/servertool/clock/session-store.js +56 -0
  75. package/dist/servertool/clock/state.d.ts +5 -0
  76. package/dist/servertool/clock/state.js +62 -0
  77. package/dist/servertool/clock/task-store.d.ts +5 -0
  78. package/dist/servertool/clock/task-store.js +4 -0
  79. package/dist/servertool/clock/tasks.d.ts +17 -0
  80. package/dist/servertool/clock/tasks.js +221 -0
  81. package/dist/servertool/clock/types.d.ts +36 -0
  82. package/dist/servertool/clock/types.js +1 -0
  83. package/dist/servertool/engine.d.ts +2 -0
  84. package/dist/servertool/engine.js +161 -7
  85. package/dist/servertool/followup-shadow.d.ts +16 -0
  86. package/dist/servertool/followup-shadow.js +145 -0
  87. package/dist/servertool/handlers/apply-patch-guard.js +1 -265
  88. package/dist/servertool/handlers/clock-auto.d.ts +1 -0
  89. package/dist/servertool/handlers/clock-auto.js +160 -0
  90. package/dist/servertool/handlers/clock.d.ts +1 -0
  91. package/dist/servertool/handlers/clock.js +197 -0
  92. package/dist/servertool/handlers/exec-command-guard.js +7 -555
  93. package/dist/servertool/handlers/followup-request-builder.d.ts +15 -7
  94. package/dist/servertool/handlers/followup-request-builder.js +248 -28
  95. package/dist/servertool/handlers/gemini-empty-reply-continue.js +62 -169
  96. package/dist/servertool/handlers/iflow-model-error-retry.js +18 -28
  97. package/dist/servertool/handlers/recursive-detection-guard.d.ts +1 -0
  98. package/dist/servertool/handlers/recursive-detection-guard.js +333 -0
  99. package/dist/servertool/handlers/stop-message-auto.js +47 -175
  100. package/dist/servertool/handlers/vision.d.ts +7 -1
  101. package/dist/servertool/handlers/vision.js +61 -117
  102. package/dist/servertool/handlers/web-search.d.ts +7 -1
  103. package/dist/servertool/handlers/web-search.js +122 -105
  104. package/dist/servertool/reenter-backend.d.ts +23 -0
  105. package/dist/servertool/reenter-backend.js +18 -0
  106. package/dist/servertool/server-side-tools.d.ts +3 -2
  107. package/dist/servertool/server-side-tools.js +64 -10
  108. package/dist/servertool/types.d.ts +92 -3
  109. package/dist/sse/json-to-sse/event-generators/responses.js +3 -21
  110. package/dist/sse/shared/serializers/responses-event-serializer.d.ts +8 -0
  111. package/dist/sse/shared/serializers/responses-event-serializer.js +19 -0
  112. package/dist/sse/shared/writer.js +24 -7
  113. package/dist/tools/apply-patch/execution-capturer.js +3 -1
  114. package/dist/tools/apply-patch/json/parse-loose.d.ts +3 -0
  115. package/dist/tools/apply-patch/json/parse-loose.js +139 -0
  116. package/dist/tools/apply-patch/patch-text/context-diff.d.ts +1 -0
  117. package/dist/tools/apply-patch/patch-text/context-diff.js +173 -0
  118. package/dist/tools/apply-patch/patch-text/git-diff.d.ts +1 -0
  119. package/dist/tools/apply-patch/patch-text/git-diff.js +138 -0
  120. package/dist/tools/apply-patch/patch-text/looks-like-patch.d.ts +1 -0
  121. package/dist/tools/apply-patch/patch-text/looks-like-patch.js +13 -0
  122. package/dist/tools/apply-patch/patch-text/normalize.d.ts +3 -0
  123. package/dist/tools/apply-patch/patch-text/normalize.js +262 -0
  124. package/dist/tools/apply-patch/structured/coercion.d.ts +3 -0
  125. package/dist/tools/apply-patch/structured/coercion.js +82 -0
  126. package/dist/tools/apply-patch/validation/shared.d.ts +3 -0
  127. package/dist/tools/apply-patch/validation/shared.js +6 -0
  128. package/dist/tools/apply-patch/validator.d.ts +2 -2
  129. package/dist/tools/apply-patch/validator.js +6 -556
  130. package/package.json +1 -1
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Tool Argument Repairer
3
+ *
4
+ * Tool calls require `function.arguments` to be a JSON string. Models often emit JSON-ish
5
+ * payloads (single quotes, fenced blocks, comments, trailing commas, etc.).
6
+ *
7
+ * This module provides a deterministic, best-effort repair surface that returns a JSON string
8
+ * or `{}` and never throws.
9
+ */
10
+ import { repairArgumentsToString } from './jsonish.js';
11
+ export class ToolArgumentRepairer {
12
+ repairToString(args) {
13
+ return repairArgumentsToString(args);
14
+ }
15
+ validateAndRepair(toolName, args) {
16
+ const repaired = this.repairToString(args);
17
+ try {
18
+ JSON.parse(repaired);
19
+ return {
20
+ repaired,
21
+ success: true
22
+ };
23
+ }
24
+ catch (error) {
25
+ return {
26
+ repaired,
27
+ success: false,
28
+ error: error instanceof Error ? error.message : String(error)
29
+ };
30
+ }
31
+ }
32
+ /**
33
+ * 批量修复工具参数
34
+ *
35
+ * @param toolCalls - 工具调用列表
36
+ * @returns 修复后的工具调用列表
37
+ */
38
+ repairToolCalls(toolCalls) {
39
+ return toolCalls.map((call) => ({
40
+ name: call.name,
41
+ arguments: this.repairToString(call.arguments)
42
+ }));
43
+ }
44
+ }
45
+ /**
46
+ * 快捷函数:修复工具参数
47
+ */
48
+ export function repairToolArguments(args) {
49
+ return new ToolArgumentRepairer().repairToString(args);
50
+ }
51
+ /**
52
+ * 快捷函数:验证并修复工具参数
53
+ */
54
+ export function validateToolArguments(toolName, args) {
55
+ return new ToolArgumentRepairer().validateAndRepair(toolName, args);
56
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Tool Call ID Manager
3
+ *
4
+ * 提供统一的工具调用 ID 生成、规范化和风格管理功能。
5
+ * 支持 'fc' (function call) 和 'preserve' 两种风格。
6
+ */
7
+ import type { JsonObject } from '../hub/types/json.js';
8
+ export type ToolCallIdStyle = 'fc' | 'preserve';
9
+ export interface ToolCallIdManagerOptions {
10
+ /**
11
+ * ID 风格
12
+ * - 'fc': 使用 fc_ 前缀的短 ID(如 fc_abc123)
13
+ * - 'preserve': 保留原始 ID
14
+ */
15
+ style?: ToolCallIdStyle;
16
+ /**
17
+ * ID 前缀(仅用于 'fc' 风格)
18
+ * 默认 'fc_'
19
+ */
20
+ prefix?: string;
21
+ /**
22
+ * ID 长度(仅用于 'fc' 风格)
23
+ * 默认 8
24
+ */
25
+ idLength?: number;
26
+ }
27
+ /**
28
+ * 工具调用 ID 管理器
29
+ */
30
+ export declare class ToolCallIdManager {
31
+ private options;
32
+ private counter;
33
+ constructor(options?: ToolCallIdManagerOptions);
34
+ /**
35
+ * 生成新的工具调用 ID
36
+ */
37
+ generateId(): string;
38
+ /**
39
+ * 规范化工具调用 ID
40
+ *
41
+ * @param id - 原始 ID
42
+ * @returns 规范化后的 ID
43
+ */
44
+ normalizeId(id: string | undefined | null): string;
45
+ /**
46
+ * 规范化工具调用 ID(带别名注册)
47
+ *
48
+ * @param id - 原始 ID
49
+ * @param aliasMap - 别名映射(用于 preserve 风格)
50
+ * @returns 规范化后的 ID
51
+ */
52
+ normalizeIdWithAlias(id: string | undefined | null, aliasMap?: Map<string, string>): string;
53
+ /**
54
+ * 批量规范化工具调用 ID
55
+ *
56
+ * @param ids - ID 列表
57
+ * @returns 规范化后的 ID 列表
58
+ */
59
+ normalizeIds(ids: (string | undefined | null)[]): string[];
60
+ /**
61
+ * 检查 ID 是否为有效的 fc_ 风格
62
+ */
63
+ isValidFcStyle(id: string): boolean;
64
+ /**
65
+ * 从 ID 中提取基础部分(移除前缀)
66
+ */
67
+ extractBaseId(id: string): string;
68
+ /**
69
+ * 重置计数器
70
+ */
71
+ resetCounter(): void;
72
+ /**
73
+ * 获取当前配置
74
+ */
75
+ getOptions(): ToolCallIdManagerOptions;
76
+ /**
77
+ * 更新配置
78
+ */
79
+ updateOptions(options: Partial<ToolCallIdManagerOptions>): void;
80
+ /**
81
+ * 生成 UUID(用于 preserve 风格)
82
+ */
83
+ private generateUUID;
84
+ }
85
+ /**
86
+ * 规范化工具调用 ID 值
87
+ *
88
+ * @param value - ID 值(可能是字符串或其他类型)
89
+ * @param forceGenerate - 如果为 true,总是生成新 ID
90
+ * @returns 规范化后的 ID 字符串
91
+ */
92
+ export declare function normalizeIdValue(value: unknown, forceGenerate?: boolean): string;
93
+ /**
94
+ * 从对象中提取工具调用 ID
95
+ *
96
+ * @param obj - 包含 ID 的对象
97
+ * @returns 提取的 ID
98
+ */
99
+ export declare function extractToolCallId(obj: unknown): string | undefined;
100
+ /**
101
+ * 创建工具调用 ID 转换器(用于 Responses 格式)
102
+ *
103
+ * @param style - ID 风格
104
+ * @returns 转换器函数
105
+ */
106
+ export declare function createToolCallIdTransformer(style: ToolCallIdStyle): ((id: string) => string) | null;
107
+ /**
108
+ * 在消息列表中强制应用工具调用 ID 风格
109
+ *
110
+ * @param messages - 消息列表
111
+ * @param transformer - ID 转换器
112
+ */
113
+ export declare function enforceToolCallIdStyle(messages: JsonObject[], transformer: (id: string) => string): void;
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Tool Call ID Manager
3
+ *
4
+ * 提供统一的工具调用 ID 生成、规范化和风格管理功能。
5
+ * 支持 'fc' (function call) 和 'preserve' 两种风格。
6
+ */
7
+ const DEFAULT_OPTIONS = {
8
+ style: 'fc',
9
+ prefix: 'fc_',
10
+ idLength: 8
11
+ };
12
+ /**
13
+ * 工具调用 ID 管理器
14
+ */
15
+ export class ToolCallIdManager {
16
+ options;
17
+ counter = 0;
18
+ constructor(options) {
19
+ this.options = { ...DEFAULT_OPTIONS, ...options };
20
+ }
21
+ /**
22
+ * 生成新的工具调用 ID
23
+ */
24
+ generateId() {
25
+ if (this.options.style === 'fc') {
26
+ return `${this.options.prefix}${this.counter++}`;
27
+ }
28
+ // preserve 风格:使用 UUID
29
+ return this.generateUUID();
30
+ }
31
+ /**
32
+ * 规范化工具调用 ID
33
+ *
34
+ * @param id - 原始 ID
35
+ * @returns 规范化后的 ID
36
+ */
37
+ normalizeId(id) {
38
+ if (!id) {
39
+ return this.generateId();
40
+ }
41
+ const trimmed = id.trim();
42
+ if (!trimmed) {
43
+ return this.generateId();
44
+ }
45
+ if (this.options.style === 'fc') {
46
+ // fc 风格:提取或生成 fc_ 风格 ID
47
+ const fcMatch = trimmed.match(/fc_([a-z0-9]+)/i);
48
+ if (fcMatch) {
49
+ return `fc_${fcMatch[1]}`;
50
+ }
51
+ // 如果不是 fc_ 风格,生成新的 fc_ ID
52
+ return this.generateId();
53
+ }
54
+ // preserve 风格:保留原始 ID
55
+ return trimmed;
56
+ }
57
+ /**
58
+ * 规范化工具调用 ID(带别名注册)
59
+ *
60
+ * @param id - 原始 ID
61
+ * @param aliasMap - 别名映射(用于 preserve 风格)
62
+ * @returns 规范化后的 ID
63
+ */
64
+ normalizeIdWithAlias(id, aliasMap) {
65
+ if (!id) {
66
+ return this.generateId();
67
+ }
68
+ const trimmed = id.trim();
69
+ if (!trimmed) {
70
+ return this.generateId();
71
+ }
72
+ if (this.options.style === 'fc') {
73
+ return this.normalizeId(id);
74
+ }
75
+ // preserve 风格:检查是否已有别名
76
+ if (aliasMap && aliasMap.has(trimmed)) {
77
+ return aliasMap.get(trimmed);
78
+ }
79
+ // 没有别名,保留原始 ID
80
+ return trimmed;
81
+ }
82
+ /**
83
+ * 批量规范化工具调用 ID
84
+ *
85
+ * @param ids - ID 列表
86
+ * @returns 规范化后的 ID 列表
87
+ */
88
+ normalizeIds(ids) {
89
+ return ids.map(id => this.normalizeId(id));
90
+ }
91
+ /**
92
+ * 检查 ID 是否为有效的 fc_ 风格
93
+ */
94
+ isValidFcStyle(id) {
95
+ return /^fc_[a-z0-9]+$/i.test(id);
96
+ }
97
+ /**
98
+ * 从 ID 中提取基础部分(移除前缀)
99
+ */
100
+ extractBaseId(id) {
101
+ if (this.options.style === 'fc') {
102
+ const match = id.match(/^fc_(.+)$/i);
103
+ return match ? match[1] : id;
104
+ }
105
+ return id;
106
+ }
107
+ /**
108
+ * 重置计数器
109
+ */
110
+ resetCounter() {
111
+ this.counter = 0;
112
+ }
113
+ /**
114
+ * 获取当前配置
115
+ */
116
+ getOptions() {
117
+ return { ...this.options };
118
+ }
119
+ /**
120
+ * 更新配置
121
+ */
122
+ updateOptions(options) {
123
+ this.options = { ...this.options, ...options };
124
+ }
125
+ /**
126
+ * 生成 UUID(用于 preserve 风格)
127
+ */
128
+ generateUUID() {
129
+ // 简单的 UUID 生成(生产环境建议使用 crypto.randomUUID())
130
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
131
+ const r = Math.random() * 16 | 0;
132
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
133
+ return v.toString(16);
134
+ });
135
+ }
136
+ }
137
+ /**
138
+ * 规范化工具调用 ID 值
139
+ *
140
+ * @param value - ID 值(可能是字符串或其他类型)
141
+ * @param forceGenerate - 如果为 true,总是生成新 ID
142
+ * @returns 规范化后的 ID 字符串
143
+ */
144
+ export function normalizeIdValue(value, forceGenerate = false) {
145
+ if (forceGenerate) {
146
+ const manager = new ToolCallIdManager();
147
+ return manager.generateId();
148
+ }
149
+ if (typeof value === 'string' && value.trim()) {
150
+ return value.trim();
151
+ }
152
+ // 生成默认 ID
153
+ const manager = new ToolCallIdManager();
154
+ return manager.generateId();
155
+ }
156
+ /**
157
+ * 从对象中提取工具调用 ID
158
+ *
159
+ * @param obj - 包含 ID 的对象
160
+ * @returns 提取的 ID
161
+ */
162
+ export function extractToolCallId(obj) {
163
+ if (!obj || typeof obj !== 'object') {
164
+ return undefined;
165
+ }
166
+ const record = obj;
167
+ // 按优先级查找:tool_call_id > call_id > id > tool_use_id
168
+ const id = record.tool_call_id ??
169
+ record.call_id ??
170
+ record.id ??
171
+ record.tool_use_id;
172
+ return typeof id === 'string' ? id : undefined;
173
+ }
174
+ /**
175
+ * 创建工具调用 ID 转换器(用于 Responses 格式)
176
+ *
177
+ * @param style - ID 风格
178
+ * @returns 转换器函数
179
+ */
180
+ export function createToolCallIdTransformer(style) {
181
+ const manager = new ToolCallIdManager({ style });
182
+ const aliasMap = new Map();
183
+ if (style === 'fc') {
184
+ return (id) => manager.normalizeId(id);
185
+ }
186
+ if (style === 'preserve') {
187
+ return (id) => {
188
+ const normalized = manager.normalizeIdWithAlias(id, aliasMap);
189
+ // 如果 ID 是新的,注册别名
190
+ if (!aliasMap.has(id)) {
191
+ aliasMap.set(id, normalized);
192
+ }
193
+ return normalized;
194
+ };
195
+ }
196
+ return null;
197
+ }
198
+ /**
199
+ * 在消息列表中强制应用工具调用 ID 风格
200
+ *
201
+ * @param messages - 消息列表
202
+ * @param transformer - ID 转换器
203
+ */
204
+ export function enforceToolCallIdStyle(messages, transformer) {
205
+ if (!messages || !Array.isArray(messages)) {
206
+ return;
207
+ }
208
+ for (const message of messages) {
209
+ if (!message || typeof message !== 'object')
210
+ continue;
211
+ const role = message.role;
212
+ // 处理 assistant 消息的 tool_calls
213
+ if (role === 'assistant' && Array.isArray(message.tool_calls)) {
214
+ for (const call of message.tool_calls) {
215
+ if (!call || typeof call !== 'object')
216
+ continue;
217
+ const id = extractToolCallId(call);
218
+ if (id) {
219
+ call.id = transformer(id);
220
+ }
221
+ }
222
+ }
223
+ // 处理 tool 消息的 tool_call_id
224
+ if (role === 'tool') {
225
+ const id = extractToolCallId(message);
226
+ if (id) {
227
+ message.tool_call_id = transformer(id);
228
+ }
229
+ }
230
+ }
231
+ }
@@ -1,17 +1,8 @@
1
1
  // Minimal canonicalizer to ensure tool_calls invariants without altering semantics
2
+ import { repairToolArguments } from './tool-argument-repairer.js';
2
3
  function isObject(v) {
3
4
  return !!v && typeof v === 'object' && !Array.isArray(v);
4
5
  }
5
- function repairArgumentsToString(args) {
6
- if (typeof args === 'string')
7
- return args;
8
- try {
9
- return JSON.stringify(args ?? {});
10
- }
11
- catch {
12
- return String(args);
13
- }
14
- }
15
6
  export function canonicalizeChatResponseTools(payload) {
16
7
  try {
17
8
  const out = isObject(payload) ? JSON.parse(JSON.stringify(payload)) : payload;
@@ -36,7 +27,7 @@ export function canonicalizeChatResponseTools(payload) {
36
27
  try {
37
28
  const fn = tc && tc.function ? tc.function : undefined;
38
29
  if (fn)
39
- fn.arguments = repairArgumentsToString(fn.arguments);
30
+ fn.arguments = repairToolArguments(fn.arguments);
40
31
  }
41
32
  catch { /* ignore */ }
42
33
  }
@@ -30,6 +30,7 @@ export function bootstrapVirtualRouterConfig(input) {
30
30
  const webSearch = normalizeWebSearch(section.webSearch, routingSource);
31
31
  validateWebSearchRouting(webSearch, routingSource);
32
32
  const execCommandGuard = normalizeExecCommandGuard(section.execCommandGuard);
33
+ const clock = normalizeClock(section.clock);
33
34
  const { runtimeEntries, aliasIndex, modelIndex } = buildProviderRuntimeEntries(providersSource);
34
35
  const { routing, targetKeys } = expandRoutingTable(routingSource, aliasIndex);
35
36
  if (!routing.default || routing.default.length === 0) {
@@ -67,7 +68,8 @@ export function bootstrapVirtualRouterConfig(input) {
67
68
  health,
68
69
  contextRouting,
69
70
  ...(webSearch ? { webSearch } : {}),
70
- ...(execCommandGuard ? { execCommandGuard } : {})
71
+ ...(execCommandGuard ? { execCommandGuard } : {}),
72
+ ...(clock ? { clock } : {})
71
73
  };
72
74
  return {
73
75
  config,
@@ -90,7 +92,31 @@ function extractVirtualRouterSection(input) {
90
92
  const contextRouting = normalizeContextRouting(section.contextRouting ?? root.contextRouting);
91
93
  const webSearch = section.webSearch ?? root.webSearch;
92
94
  const execCommandGuard = section.execCommandGuard ?? root.execCommandGuard;
93
- return { providers, routing, classifier, loadBalancing, health, contextRouting, webSearch, execCommandGuard };
95
+ const clock = section.clock ?? root.clock;
96
+ return { providers, routing, classifier, loadBalancing, health, contextRouting, webSearch, execCommandGuard, clock };
97
+ }
98
+ function normalizeClock(raw) {
99
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
100
+ return undefined;
101
+ }
102
+ const record = raw;
103
+ const enabled = record.enabled === true ||
104
+ (typeof record.enabled === 'string' && record.enabled.trim().toLowerCase() === 'true') ||
105
+ (typeof record.enabled === 'number' && record.enabled === 1);
106
+ if (!enabled) {
107
+ return undefined;
108
+ }
109
+ const out = { enabled: true };
110
+ if (typeof record.retentionMs === 'number' && Number.isFinite(record.retentionMs) && record.retentionMs >= 0) {
111
+ out.retentionMs = Math.floor(record.retentionMs);
112
+ }
113
+ if (typeof record.dueWindowMs === 'number' && Number.isFinite(record.dueWindowMs) && record.dueWindowMs >= 0) {
114
+ out.dueWindowMs = Math.floor(record.dueWindowMs);
115
+ }
116
+ if (typeof record.tickMs === 'number' && Number.isFinite(record.tickMs) && record.tickMs >= 0) {
117
+ out.tickMs = Math.floor(record.tickMs);
118
+ }
119
+ return out;
94
120
  }
95
121
  function buildProviderRuntimeEntries(providers) {
96
122
  const runtimeEntries = {};
@@ -1130,9 +1156,32 @@ function normalizeLoadBalancing(input) {
1130
1156
  weightsEntries[key] = value;
1131
1157
  }
1132
1158
  }
1133
- return Object.keys(weightsEntries).length
1134
- ? { strategy, weights: weightsEntries }
1135
- : { strategy };
1159
+ const healthWeightedRaw = asRecord(record.healthWeighted);
1160
+ const healthWeighted = Object.keys(healthWeightedRaw).length > 0
1161
+ ? {
1162
+ ...(typeof healthWeightedRaw.enabled === 'boolean' ? { enabled: healthWeightedRaw.enabled } : {}),
1163
+ ...(typeof healthWeightedRaw.recoverToBestOnRetry === 'boolean'
1164
+ ? { recoverToBestOnRetry: healthWeightedRaw.recoverToBestOnRetry }
1165
+ : {}),
1166
+ ...(typeof healthWeightedRaw.baseWeight === 'number' && Number.isFinite(healthWeightedRaw.baseWeight)
1167
+ ? { baseWeight: healthWeightedRaw.baseWeight }
1168
+ : {}),
1169
+ ...(typeof healthWeightedRaw.minMultiplier === 'number' && Number.isFinite(healthWeightedRaw.minMultiplier)
1170
+ ? { minMultiplier: healthWeightedRaw.minMultiplier }
1171
+ : {}),
1172
+ ...(typeof healthWeightedRaw.beta === 'number' && Number.isFinite(healthWeightedRaw.beta)
1173
+ ? { beta: healthWeightedRaw.beta }
1174
+ : {}),
1175
+ ...(typeof healthWeightedRaw.halfLifeMs === 'number' && Number.isFinite(healthWeightedRaw.halfLifeMs)
1176
+ ? { halfLifeMs: healthWeightedRaw.halfLifeMs }
1177
+ : {})
1178
+ }
1179
+ : undefined;
1180
+ return {
1181
+ strategy,
1182
+ ...(Object.keys(weightsEntries).length ? { weights: weightsEntries } : {}),
1183
+ ...(healthWeighted ? { healthWeighted } : {})
1184
+ };
1136
1185
  }
1137
1186
  function coerceRatio(value) {
1138
1187
  if (typeof value === 'number' && Number.isFinite(value)) {