crosscheck-mcp 0.2.29 → 0.2.31
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.
- package/dist/browser-ext.cjs +1129 -987
- package/dist/browser-ext.cjs.map +1 -1
- package/dist/browser-ext.d.cts +2 -0
- package/dist/browser-ext.d.ts +2 -0
- package/dist/browser-ext.js +1129 -987
- package/dist/browser-ext.js.map +1 -1
- package/dist/embed.cjs +434 -243
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.d.cts +44 -3
- package/dist/embed.d.ts +44 -3
- package/dist/embed.js +428 -242
- package/dist/embed.js.map +1 -1
- package/dist/node-stdio.cjs +360 -210
- package/dist/node-stdio.cjs.map +1 -1
- package/dist/node-stdio.d.cts +1 -1
- package/dist/node-stdio.d.ts +1 -1
- package/dist/node-stdio.js +357 -207
- package/dist/node-stdio.js.map +1 -1
- package/dist/{types-BcprisDl.d.cts → types-HA3W47O4.d.cts} +13 -2
- package/dist/{types-BcprisDl.d.ts → types-HA3W47O4.d.ts} +13 -2
- package/package.json +3 -2
package/dist/browser-ext.cjs
CHANGED
|
@@ -39,426 +39,35 @@ module.exports = __toCommonJS(browser_ext_exports);
|
|
|
39
39
|
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
40
40
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
41
41
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
completion_per_1k: 0.025,
|
|
63
|
-
cached_per_1k: 5e-4
|
|
64
|
-
},
|
|
65
|
-
"claude-opus-4-8": {
|
|
66
|
-
prompt_per_1k: 5e-3,
|
|
67
|
-
completion_per_1k: 0.025,
|
|
68
|
-
cached_per_1k: 5e-4
|
|
69
|
-
},
|
|
70
|
-
"claude-opus-5-5": {
|
|
71
|
-
prompt_per_1k: 4e-3,
|
|
72
|
-
completion_per_1k: 0.02,
|
|
73
|
-
cached_per_1k: 2e-4
|
|
74
|
-
},
|
|
75
|
-
"claude-opus-5": {
|
|
76
|
-
prompt_per_1k: 5e-3,
|
|
77
|
-
completion_per_1k: 0.025,
|
|
78
|
-
cached_per_1k: 5e-4
|
|
79
|
-
},
|
|
80
|
-
"claude-fable-5": {
|
|
81
|
-
prompt_per_1k: 0.01,
|
|
82
|
-
completion_per_1k: 0.05,
|
|
83
|
-
cached_per_1k: 1e-3
|
|
84
|
-
},
|
|
85
|
-
"claude-sonnet-4-5": {
|
|
86
|
-
prompt_per_1k: 3e-3,
|
|
87
|
-
completion_per_1k: 0.015,
|
|
88
|
-
cached_per_1k: 3e-4
|
|
89
|
-
},
|
|
90
|
-
"claude-sonnet-4-6": {
|
|
91
|
-
prompt_per_1k: 3e-3,
|
|
92
|
-
completion_per_1k: 0.015,
|
|
93
|
-
cached_per_1k: 3e-4
|
|
94
|
-
},
|
|
95
|
-
"claude-haiku-4-5": {
|
|
96
|
-
prompt_per_1k: 1e-3,
|
|
97
|
-
completion_per_1k: 5e-3,
|
|
98
|
-
cached_per_1k: 1e-4
|
|
99
|
-
},
|
|
100
|
-
"claude-3-5-sonnet": {
|
|
101
|
-
prompt_per_1k: 3e-3,
|
|
102
|
-
completion_per_1k: 0.015,
|
|
103
|
-
cached_per_1k: 3e-4
|
|
104
|
-
},
|
|
105
|
-
"claude-3-5-haiku": {
|
|
106
|
-
prompt_per_1k: 8e-4,
|
|
107
|
-
completion_per_1k: 4e-3,
|
|
108
|
-
cached_per_1k: 8e-5
|
|
109
|
-
},
|
|
110
|
-
"claude-fable-5-1": {
|
|
111
|
-
prompt_per_1k: 0.01,
|
|
112
|
-
completion_per_1k: 0.05,
|
|
113
|
-
cached_per_1k: 25e-5
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
openai: {
|
|
117
|
-
_source: {
|
|
118
|
-
url: "https://developers.openai.com/api/docs/pricing",
|
|
119
|
-
verified_at: "2026-09-27"
|
|
120
|
-
},
|
|
121
|
-
"gpt-6-astra": {
|
|
122
|
-
prompt_per_1k: 0.01,
|
|
123
|
-
completion_per_1k: 0.05,
|
|
124
|
-
cached_per_1k: 1e-3
|
|
125
|
-
},
|
|
126
|
-
"gpt-6-sol": {
|
|
127
|
-
prompt_per_1k: 2e-3,
|
|
128
|
-
completion_per_1k: 0.01,
|
|
129
|
-
cached_per_1k: 2e-4
|
|
130
|
-
},
|
|
131
|
-
"gpt-6-luna": {
|
|
132
|
-
prompt_per_1k: 1e-4,
|
|
133
|
-
completion_per_1k: 5e-4,
|
|
134
|
-
cached_per_1k: 1e-5
|
|
135
|
-
},
|
|
136
|
-
"gpt-5.6-sol": {
|
|
137
|
-
prompt_per_1k: 4e-3,
|
|
138
|
-
completion_per_1k: 0.02,
|
|
139
|
-
cached_per_1k: 4e-4
|
|
140
|
-
},
|
|
141
|
-
"gpt-5.6-terra": {
|
|
142
|
-
prompt_per_1k: 2e-3,
|
|
143
|
-
completion_per_1k: 0.012,
|
|
144
|
-
cached_per_1k: 2e-4
|
|
145
|
-
},
|
|
146
|
-
"gpt-5.6-luna": {
|
|
147
|
-
prompt_per_1k: 2e-4,
|
|
148
|
-
completion_per_1k: 12e-4,
|
|
149
|
-
cached_per_1k: 2e-5
|
|
150
|
-
},
|
|
151
|
-
"gpt-5.6": {
|
|
152
|
-
prompt_per_1k: 4e-3,
|
|
153
|
-
completion_per_1k: 0.02,
|
|
154
|
-
cached_per_1k: 4e-4
|
|
155
|
-
},
|
|
156
|
-
"gpt-5.5": {
|
|
157
|
-
prompt_per_1k: 5e-3,
|
|
158
|
-
completion_per_1k: 0.03,
|
|
159
|
-
cached_per_1k: 5e-4
|
|
160
|
-
},
|
|
161
|
-
"gpt-5": {
|
|
162
|
-
prompt_per_1k: 125e-5,
|
|
163
|
-
completion_per_1k: 0.01,
|
|
164
|
-
cached_per_1k: 125e-6
|
|
165
|
-
},
|
|
166
|
-
"gpt-5-mini": {
|
|
167
|
-
prompt_per_1k: 25e-5,
|
|
168
|
-
completion_per_1k: 2e-3,
|
|
169
|
-
cached_per_1k: 25e-6
|
|
170
|
-
},
|
|
171
|
-
"gpt-5-nano": {
|
|
172
|
-
prompt_per_1k: 5e-5,
|
|
173
|
-
completion_per_1k: 4e-4,
|
|
174
|
-
cached_per_1k: 5e-6
|
|
175
|
-
},
|
|
176
|
-
"gpt-4o": {
|
|
177
|
-
prompt_per_1k: 25e-4,
|
|
178
|
-
completion_per_1k: 0.01,
|
|
179
|
-
cached_per_1k: 125e-5
|
|
180
|
-
},
|
|
181
|
-
"gpt-4o-mini": {
|
|
182
|
-
prompt_per_1k: 15e-5,
|
|
183
|
-
completion_per_1k: 6e-4,
|
|
184
|
-
cached_per_1k: 75e-6
|
|
185
|
-
},
|
|
186
|
-
o1: {
|
|
187
|
-
prompt_per_1k: 0.015,
|
|
188
|
-
completion_per_1k: 0.06,
|
|
189
|
-
cached_per_1k: 75e-4
|
|
190
|
-
},
|
|
191
|
-
"o1-mini": {
|
|
192
|
-
prompt_per_1k: 3e-3,
|
|
193
|
-
completion_per_1k: 0.012,
|
|
194
|
-
cached_per_1k: 15e-4
|
|
195
|
-
},
|
|
196
|
-
o3: {
|
|
197
|
-
prompt_per_1k: 2e-3,
|
|
198
|
-
completion_per_1k: 8e-3,
|
|
199
|
-
cached_per_1k: 5e-4
|
|
200
|
-
},
|
|
201
|
-
"o3-mini": {
|
|
202
|
-
prompt_per_1k: 11e-4,
|
|
203
|
-
completion_per_1k: 44e-4,
|
|
204
|
-
cached_per_1k: 55e-5
|
|
205
|
-
},
|
|
206
|
-
"gpt-5-pro": {
|
|
207
|
-
prompt_per_1k: 0.015,
|
|
208
|
-
completion_per_1k: 0.12,
|
|
209
|
-
cached_per_1k: 0
|
|
210
|
-
},
|
|
211
|
-
"gpt-5.2-pro": {
|
|
212
|
-
prompt_per_1k: 0.021,
|
|
213
|
-
completion_per_1k: 0.168,
|
|
214
|
-
cached_per_1k: 0
|
|
215
|
-
},
|
|
216
|
-
"gpt-5.4-pro": {
|
|
217
|
-
prompt_per_1k: 0.03,
|
|
218
|
-
completion_per_1k: 0.18,
|
|
219
|
-
cached_per_1k: 0
|
|
220
|
-
},
|
|
221
|
-
"gpt-5.5-pro": {
|
|
222
|
-
prompt_per_1k: 0.03,
|
|
223
|
-
completion_per_1k: 0.18,
|
|
224
|
-
cached_per_1k: 0
|
|
225
|
-
},
|
|
226
|
-
"o1-pro": {
|
|
227
|
-
prompt_per_1k: 0.15,
|
|
228
|
-
completion_per_1k: 0.6,
|
|
229
|
-
cached_per_1k: 0
|
|
230
|
-
},
|
|
231
|
-
"o3-pro": {
|
|
232
|
-
prompt_per_1k: 0.02,
|
|
233
|
-
completion_per_1k: 0.08,
|
|
234
|
-
cached_per_1k: 0
|
|
235
|
-
}
|
|
236
|
-
},
|
|
237
|
-
xai: {
|
|
238
|
-
"grok-4-latest": {
|
|
239
|
-
prompt_per_1k: 3e-3,
|
|
240
|
-
completion_per_1k: 0.015,
|
|
241
|
-
cached_per_1k: 75e-5
|
|
242
|
-
},
|
|
243
|
-
"grok-3": {
|
|
244
|
-
prompt_per_1k: 3e-3,
|
|
245
|
-
completion_per_1k: 0.015,
|
|
246
|
-
cached_per_1k: 15e-4
|
|
247
|
-
},
|
|
248
|
-
"grok-3-mini": {
|
|
249
|
-
prompt_per_1k: 3e-4,
|
|
250
|
-
completion_per_1k: 5e-4,
|
|
251
|
-
cached_per_1k: 15e-5
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
|
-
gemini: {
|
|
255
|
-
"gemini-3.1-pro-preview": {
|
|
256
|
-
prompt_per_1k: 2e-3,
|
|
257
|
-
completion_per_1k: 0.012,
|
|
258
|
-
cached_per_1k: 2e-4
|
|
259
|
-
},
|
|
260
|
-
"gemini-3.5-flash": {
|
|
261
|
-
prompt_per_1k: 15e-4,
|
|
262
|
-
completion_per_1k: 9e-3,
|
|
263
|
-
cached_per_1k: 15e-5
|
|
264
|
-
},
|
|
265
|
-
"gemini-2.5-flash": {
|
|
266
|
-
prompt_per_1k: 3e-4,
|
|
267
|
-
completion_per_1k: 25e-4,
|
|
268
|
-
cached_per_1k: 75e-6
|
|
269
|
-
},
|
|
270
|
-
"gemini-2.5-flash-lite": {
|
|
271
|
-
prompt_per_1k: 1e-4,
|
|
272
|
-
completion_per_1k: 4e-4,
|
|
273
|
-
cached_per_1k: 25e-6
|
|
274
|
-
},
|
|
275
|
-
"gemini-3.8-flash": {
|
|
276
|
-
prompt_per_1k: 75e-5,
|
|
277
|
-
completion_per_1k: 375e-5,
|
|
278
|
-
cached_per_1k: 75e-6
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
mistral: {
|
|
282
|
-
"mistral-large-latest": {
|
|
283
|
-
prompt_per_1k: 2e-3,
|
|
284
|
-
completion_per_1k: 6e-3,
|
|
285
|
-
cached_per_1k: 2e-4
|
|
286
|
-
},
|
|
287
|
-
"mistral-small-latest": {
|
|
288
|
-
prompt_per_1k: 2e-4,
|
|
289
|
-
completion_per_1k: 6e-4,
|
|
290
|
-
cached_per_1k: 2e-5
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
groq: {
|
|
294
|
-
"llama-3.3-70b-versatile": {
|
|
295
|
-
prompt_per_1k: 59e-5,
|
|
296
|
-
completion_per_1k: 79e-5,
|
|
297
|
-
cached_per_1k: 295e-6
|
|
298
|
-
},
|
|
299
|
-
"llama-3.1-8b-instant": {
|
|
300
|
-
prompt_per_1k: 5e-5,
|
|
301
|
-
completion_per_1k: 8e-5,
|
|
302
|
-
cached_per_1k: 25e-6
|
|
303
|
-
}
|
|
304
|
-
},
|
|
305
|
-
deepseek: {
|
|
306
|
-
"deepseek-chat": {
|
|
307
|
-
prompt_per_1k: 27e-5,
|
|
308
|
-
completion_per_1k: 11e-4,
|
|
309
|
-
cached_per_1k: 7e-5
|
|
310
|
-
},
|
|
311
|
-
"deepseek-reasoner": {
|
|
312
|
-
prompt_per_1k: 55e-5,
|
|
313
|
-
completion_per_1k: 22e-4,
|
|
314
|
-
cached_per_1k: 14e-5
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
|
-
kimi: {
|
|
318
|
-
"kimi-k3": {
|
|
319
|
-
prompt_per_1k: 3e-3,
|
|
320
|
-
completion_per_1k: 0.015,
|
|
321
|
-
cached_per_1k: 3e-4
|
|
322
|
-
}
|
|
323
|
-
},
|
|
324
|
-
qwen: {
|
|
325
|
-
"qwen3.8-max": {
|
|
326
|
-
prompt_per_1k: 2e-3,
|
|
327
|
-
completion_per_1k: 6e-3,
|
|
328
|
-
cached_per_1k: 2e-4
|
|
329
|
-
},
|
|
330
|
-
"qwen3.7-max": {
|
|
331
|
-
prompt_per_1k: 125e-5,
|
|
332
|
-
completion_per_1k: 375e-5,
|
|
333
|
-
cached_per_1k: 125e-6
|
|
334
|
-
}
|
|
335
|
-
},
|
|
336
|
-
_tiers: {
|
|
337
|
-
low: {
|
|
338
|
-
description: "Cheap, fast models for simple subtasks (extraction, formatting, short summaries).",
|
|
339
|
-
models: [
|
|
340
|
-
{
|
|
341
|
-
provider: "openai",
|
|
342
|
-
model: "gpt-4o-mini"
|
|
343
|
-
},
|
|
344
|
-
{
|
|
345
|
-
provider: "openai",
|
|
346
|
-
model: "gpt-5-mini"
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
provider: "anthropic",
|
|
350
|
-
model: "claude-haiku-4-5"
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
provider: "anthropic",
|
|
354
|
-
model: "claude-3-5-haiku"
|
|
355
|
-
},
|
|
356
|
-
{
|
|
357
|
-
provider: "gemini",
|
|
358
|
-
model: "gemini-3.5-flash"
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
provider: "gemini",
|
|
362
|
-
model: "gemini-2.5-flash"
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
provider: "gemini",
|
|
366
|
-
model: "gemini-2.5-flash-lite"
|
|
367
|
-
},
|
|
368
|
-
{
|
|
369
|
-
provider: "xai",
|
|
370
|
-
model: "grok-3-mini"
|
|
371
|
-
},
|
|
372
|
-
{
|
|
373
|
-
provider: "groq",
|
|
374
|
-
model: "llama-3.1-8b-instant"
|
|
375
|
-
},
|
|
376
|
-
{
|
|
377
|
-
provider: "deepseek",
|
|
378
|
-
model: "deepseek-chat"
|
|
379
|
-
}
|
|
380
|
-
]
|
|
381
|
-
},
|
|
382
|
-
med: {
|
|
383
|
-
description: "Mid-tier models for reasoning, code, multi-step thinking.",
|
|
384
|
-
models: [
|
|
385
|
-
{
|
|
386
|
-
provider: "anthropic",
|
|
387
|
-
model: "claude-sonnet-4-5"
|
|
388
|
-
},
|
|
389
|
-
{
|
|
390
|
-
provider: "anthropic",
|
|
391
|
-
model: "claude-sonnet-4-6"
|
|
392
|
-
},
|
|
393
|
-
{
|
|
394
|
-
provider: "openai",
|
|
395
|
-
model: "gpt-4o"
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
provider: "openai",
|
|
399
|
-
model: "o3-mini"
|
|
400
|
-
},
|
|
401
|
-
{
|
|
402
|
-
provider: "gemini",
|
|
403
|
-
model: "gemini-3.1-pro-preview"
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
provider: "xai",
|
|
407
|
-
model: "grok-3"
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
provider: "mistral",
|
|
411
|
-
model: "mistral-large-latest"
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
provider: "deepseek",
|
|
415
|
-
model: "deepseek-reasoner"
|
|
416
|
-
},
|
|
417
|
-
{
|
|
418
|
-
provider: "groq",
|
|
419
|
-
model: "llama-3.3-70b-versatile"
|
|
420
|
-
}
|
|
421
|
-
]
|
|
422
|
-
},
|
|
423
|
-
high: {
|
|
424
|
-
description: "Most capable models for hard reasoning, planning, audits.",
|
|
425
|
-
models: [
|
|
426
|
-
{
|
|
427
|
-
provider: "anthropic",
|
|
428
|
-
model: "claude-opus-4-5"
|
|
429
|
-
},
|
|
430
|
-
{
|
|
431
|
-
provider: "anthropic",
|
|
432
|
-
model: "claude-opus-4-7"
|
|
433
|
-
},
|
|
434
|
-
{
|
|
435
|
-
provider: "anthropic",
|
|
436
|
-
model: "claude-fable-5"
|
|
437
|
-
},
|
|
438
|
-
{
|
|
439
|
-
provider: "openai",
|
|
440
|
-
model: "gpt-5.5"
|
|
441
|
-
},
|
|
442
|
-
{
|
|
443
|
-
provider: "openai",
|
|
444
|
-
model: "gpt-5"
|
|
445
|
-
},
|
|
446
|
-
{
|
|
447
|
-
provider: "openai",
|
|
448
|
-
model: "o1"
|
|
449
|
-
},
|
|
450
|
-
{
|
|
451
|
-
provider: "openai",
|
|
452
|
-
model: "o3"
|
|
453
|
-
},
|
|
454
|
-
{
|
|
455
|
-
provider: "xai",
|
|
456
|
-
model: "grok-4-latest"
|
|
457
|
-
}
|
|
458
|
-
]
|
|
42
|
+
// src/core/retarget.ts
|
|
43
|
+
function retargetProvider(p, newModel, fallbackModels) {
|
|
44
|
+
const chain = fallbackModels && fallbackModels.length > 0 ? fallbackModels : void 0;
|
|
45
|
+
if (p.model === newModel && chain === void 0) return p;
|
|
46
|
+
return {
|
|
47
|
+
name: p.name,
|
|
48
|
+
model: newModel,
|
|
49
|
+
...chain ? { fallbackModels: chain } : {},
|
|
50
|
+
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function loadProviderWeights(storage, names) {
|
|
54
|
+
const out = {};
|
|
55
|
+
for (const raw of names) {
|
|
56
|
+
const name = raw.toLowerCase();
|
|
57
|
+
if (name in out) continue;
|
|
58
|
+
const row = await storage.getProviderStats(name);
|
|
59
|
+
if (!row) {
|
|
60
|
+
out[name] = 0.5;
|
|
61
|
+
continue;
|
|
459
62
|
}
|
|
63
|
+
const total = row.wins + row.losses + row.abstains;
|
|
64
|
+
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
460
65
|
}
|
|
461
|
-
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/core/super-mode.ts
|
|
70
|
+
var import_node_async_hooks = require("async_hooks");
|
|
462
71
|
|
|
463
72
|
// src/core/pricing.ts
|
|
464
73
|
var import_node_fs = require("fs");
|
|
@@ -611,6 +220,11 @@ function reasoningSharesOutputBudget(provider) {
|
|
|
611
220
|
}
|
|
612
221
|
|
|
613
222
|
// src/providers/types.ts
|
|
223
|
+
var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
|
|
224
|
+
function imagesOf(m) {
|
|
225
|
+
if (m.role !== "user" || !Array.isArray(m.images)) return [];
|
|
226
|
+
return m.images.filter((i) => Boolean(i) && typeof i.data === "string" && i.data.length > 0 && IMAGE_MEDIA_TYPES.includes(i.mediaType));
|
|
227
|
+
}
|
|
614
228
|
var ProviderError = class extends Error {
|
|
615
229
|
name = "ProviderError";
|
|
616
230
|
kind;
|
|
@@ -999,7 +613,12 @@ function buildAnthropicRequest(opts) {
|
|
|
999
613
|
}
|
|
1000
614
|
continue;
|
|
1001
615
|
}
|
|
1002
|
-
|
|
616
|
+
const text = typeof m.content === "string" ? m.content : "";
|
|
617
|
+
const images = imagesOf(m);
|
|
618
|
+
convo.push(images.length ? { role: m.role, content: [
|
|
619
|
+
...images.map((i) => ({ type: "image", source: { type: "base64", media_type: i.mediaType, data: i.data } })),
|
|
620
|
+
{ type: "text", text }
|
|
621
|
+
] } : { role: m.role, content: text });
|
|
1003
622
|
}
|
|
1004
623
|
const body = {
|
|
1005
624
|
model: opts.model,
|
|
@@ -1239,7 +858,10 @@ function buildGeminiRequest(opts) {
|
|
|
1239
858
|
const role = m.role === "user" ? "user" : "model";
|
|
1240
859
|
contents.push({
|
|
1241
860
|
role,
|
|
1242
|
-
parts: [
|
|
861
|
+
parts: [
|
|
862
|
+
...imagesOf(m).map((i) => ({ inline_data: { mime_type: i.mediaType, data: i.data } })),
|
|
863
|
+
{ text: typeof m.content === "string" ? m.content : "" }
|
|
864
|
+
]
|
|
1243
865
|
});
|
|
1244
866
|
}
|
|
1245
867
|
const body = {
|
|
@@ -1308,7 +930,192 @@ function parseGeminiResponse(opts) {
|
|
|
1308
930
|
);
|
|
1309
931
|
}
|
|
1310
932
|
}
|
|
1311
|
-
function applyPricing2(usage, pricing) {
|
|
933
|
+
function applyPricing2(usage, pricing) {
|
|
934
|
+
const { cost_usd, estimated } = calculateCost(
|
|
935
|
+
pricing,
|
|
936
|
+
usage.provider,
|
|
937
|
+
usage.model,
|
|
938
|
+
usage.prompt_tokens,
|
|
939
|
+
usage.completion_tokens,
|
|
940
|
+
usage.cached_tokens
|
|
941
|
+
);
|
|
942
|
+
return {
|
|
943
|
+
...usage,
|
|
944
|
+
cost_usd,
|
|
945
|
+
estimated: usage.estimated || estimated
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
async function sendGemini(args) {
|
|
949
|
+
const { url, headers, body } = buildGeminiRequest({
|
|
950
|
+
model: args.model,
|
|
951
|
+
apiKey: args.apiKey,
|
|
952
|
+
messages: args.messages,
|
|
953
|
+
maxTokens: args.maxTokens,
|
|
954
|
+
// args.temperature is deliberately NOT forwarded: Gemini ignores it and
|
|
955
|
+
// upcoming models reject it. See the header note.
|
|
956
|
+
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
957
|
+
});
|
|
958
|
+
const doFetch = args.fetchImpl ?? globalThis.fetch;
|
|
959
|
+
const init = {
|
|
960
|
+
method: "POST",
|
|
961
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
962
|
+
body: JSON.stringify(body)
|
|
963
|
+
};
|
|
964
|
+
if (args.signal) init.signal = args.signal;
|
|
965
|
+
const attemptOnce = async () => {
|
|
966
|
+
let respLike;
|
|
967
|
+
try {
|
|
968
|
+
respLike = await doFetch(url, init);
|
|
969
|
+
} catch (e) {
|
|
970
|
+
const d = diagnoseNetworkError(e);
|
|
971
|
+
if (!looksLikeNetworkFailure(d)) throw e;
|
|
972
|
+
throw new ProviderError(d.kind, networkErrorMessage("gemini", d), {
|
|
973
|
+
transient: d.transient
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
const status = respLike.status;
|
|
977
|
+
if (status >= 200 && status < 300) {
|
|
978
|
+
let parsed;
|
|
979
|
+
try {
|
|
980
|
+
parsed = await respLike.json();
|
|
981
|
+
} catch (e) {
|
|
982
|
+
throw new ProviderError("parse", `gemini: response body not JSON: ${e.message}`);
|
|
983
|
+
}
|
|
984
|
+
const { text, usage, finishReason } = parseGeminiResponse({
|
|
985
|
+
resp: parsed,
|
|
986
|
+
model: args.model,
|
|
987
|
+
purpose: args.purpose ?? "worker"
|
|
988
|
+
});
|
|
989
|
+
if (text.trim() === "" && finishReason === "MAX_TOKENS") {
|
|
990
|
+
throw new ProviderError(
|
|
991
|
+
"parse",
|
|
992
|
+
`gemini: the answer was cut off before any visible text \u2014 thinking used the whole output budget (${usage.completion_tokens} tokens). Raise CROSSCHECK_GEMINI_THINKING_HEADROOM_TOKENS or the purpose's budget.`,
|
|
993
|
+
{ transient: false }
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
return { text, attempts: 1, usage: applyPricing2(usage, args.pricing) };
|
|
997
|
+
}
|
|
998
|
+
const bodyText = await respLike.text().catch(() => "");
|
|
999
|
+
throw httpFailureToProviderError("gemini", status, bodyText, parseRetryAfter(respLike), args.model);
|
|
1000
|
+
};
|
|
1001
|
+
await acquireRateLimit("gemini", args.signal ? { signal: args.signal } : void 0);
|
|
1002
|
+
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/providers/openai-compatible.ts
|
|
1006
|
+
var OPENAI_COMPAT_DEFAULT_URLS = {
|
|
1007
|
+
openai: "https://api.openai.com/v1/chat/completions",
|
|
1008
|
+
xai: "https://api.x.ai/v1/chat/completions",
|
|
1009
|
+
mistral: "https://api.mistral.ai/v1/chat/completions",
|
|
1010
|
+
groq: "https://api.groq.com/openai/v1/chat/completions",
|
|
1011
|
+
deepseek: "https://api.deepseek.com/v1/chat/completions",
|
|
1012
|
+
// Moonshot AI (Kimi). OpenAI-compatible chat completions endpoint.
|
|
1013
|
+
kimi: "https://api.moonshot.ai/v1/chat/completions",
|
|
1014
|
+
// Alibaba Cloud DashScope (Qwen). International endpoint — the mainland
|
|
1015
|
+
// China endpoint (dashscope.aliyuncs.com) 403s/404s from outside China in
|
|
1016
|
+
// a way that can look like a bad key; this is the deliberate default for
|
|
1017
|
+
// a US-based deployment.
|
|
1018
|
+
qwen: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions"
|
|
1019
|
+
};
|
|
1020
|
+
var OPENAI_COMPAT_NATIVE_STRUCTURED = /* @__PURE__ */ new Set([
|
|
1021
|
+
"openai"
|
|
1022
|
+
]);
|
|
1023
|
+
function supportsNativeJsonSchema(provider, model) {
|
|
1024
|
+
if (!OPENAI_COMPAT_NATIVE_STRUCTURED.has(provider.toLowerCase())) {
|
|
1025
|
+
return false;
|
|
1026
|
+
}
|
|
1027
|
+
const m = model.toLowerCase();
|
|
1028
|
+
if (/^o1(?:-|$)/.test(m)) return false;
|
|
1029
|
+
return true;
|
|
1030
|
+
}
|
|
1031
|
+
function buildOpenAICompatibleRequest(opts) {
|
|
1032
|
+
const msgs = opts.messages.map((m) => {
|
|
1033
|
+
const text = typeof m.content === "string" ? m.content : "";
|
|
1034
|
+
const images = imagesOf(m);
|
|
1035
|
+
return images.length ? { role: m.role, content: [{ type: "text", text }, ...images.map((i) => ({ type: "image_url", image_url: { url: `data:${i.mediaType};base64,${i.data}` } }))] } : { role: m.role, content: text };
|
|
1036
|
+
});
|
|
1037
|
+
const body = {
|
|
1038
|
+
model: opts.model,
|
|
1039
|
+
messages: msgs
|
|
1040
|
+
};
|
|
1041
|
+
if (supportsTemperature(opts.provider, opts.model)) {
|
|
1042
|
+
body.max_tokens = opts.maxTokens;
|
|
1043
|
+
body.temperature = opts.temperature;
|
|
1044
|
+
} else {
|
|
1045
|
+
body.max_completion_tokens = opts.maxTokens;
|
|
1046
|
+
}
|
|
1047
|
+
if (opts.jsonSchema && supportsNativeJsonSchema(opts.provider, opts.model)) {
|
|
1048
|
+
body.response_format = {
|
|
1049
|
+
type: "json_schema",
|
|
1050
|
+
json_schema: {
|
|
1051
|
+
name: "structured_output",
|
|
1052
|
+
schema: opts.jsonSchema,
|
|
1053
|
+
// NOT strict. Strict mode requires `additionalProperties: false` on
|
|
1054
|
+
// every object and every property listed in `required` — the
|
|
1055
|
+
// engine's schemas don't meet that, so every structured OpenAI call
|
|
1056
|
+
// (planner, judges, synthesis) was a 400, unnoticed until the canary
|
|
1057
|
+
// grew a structured check (2026-10-11). Non-strict still shapes the
|
|
1058
|
+
// reply to the schema, and structured.ts validates it against the
|
|
1059
|
+
// full schema regardless, so strictness added a failure mode and no
|
|
1060
|
+
// safety.
|
|
1061
|
+
strict: false
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
const headers = {
|
|
1066
|
+
"content-type": "application/json",
|
|
1067
|
+
Authorization: `Bearer ${opts.apiKey}`
|
|
1068
|
+
};
|
|
1069
|
+
return { url: opts.url, headers, body };
|
|
1070
|
+
}
|
|
1071
|
+
function parseOpenAICompatibleResponse(opts) {
|
|
1072
|
+
const r = opts.resp ?? {};
|
|
1073
|
+
let text;
|
|
1074
|
+
try {
|
|
1075
|
+
const choices = r["choices"];
|
|
1076
|
+
if (!Array.isArray(choices) || choices.length === 0) {
|
|
1077
|
+
throw new Error("choices missing or empty");
|
|
1078
|
+
}
|
|
1079
|
+
const first = choices[0];
|
|
1080
|
+
const message = first["message"];
|
|
1081
|
+
if (!message || typeof message["content"] !== "string") {
|
|
1082
|
+
throw new Error("message.content missing or not string");
|
|
1083
|
+
}
|
|
1084
|
+
text = message["content"];
|
|
1085
|
+
} catch (e) {
|
|
1086
|
+
throw new ProviderError(
|
|
1087
|
+
"parse",
|
|
1088
|
+
`${opts.provider}: unexpected response shape: ${JSON.stringify(r).slice(0, 200)}`
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
const u = r["usage"] ?? {};
|
|
1092
|
+
const details = u["prompt_tokens_details"] ?? {};
|
|
1093
|
+
const cached = Math.trunc(Number(details["cached_tokens"] ?? 0)) || 0;
|
|
1094
|
+
const promptTokens = Math.trunc(Number(u["prompt_tokens"] ?? 0)) || 0;
|
|
1095
|
+
const reportedCompletion = Math.trunc(Number(u["completion_tokens"] ?? 0)) || 0;
|
|
1096
|
+
const reportedTotal = Math.trunc(Number(u["total_tokens"] ?? 0)) || 0;
|
|
1097
|
+
const derivedCompletion = reportedTotal > promptTokens ? reportedTotal - promptTokens : reportedCompletion;
|
|
1098
|
+
const completionTokens = Math.max(reportedCompletion, derivedCompletion);
|
|
1099
|
+
const usage = {
|
|
1100
|
+
provider: opts.provider,
|
|
1101
|
+
model: opts.model,
|
|
1102
|
+
prompt_tokens: promptTokens,
|
|
1103
|
+
completion_tokens: completionTokens,
|
|
1104
|
+
cached_tokens: cached,
|
|
1105
|
+
// Use the response's reported total_tokens directly. Python's
|
|
1106
|
+
// `Usage.to_dict()` falls back to prompt+completion only when
|
|
1107
|
+
// total_tokens is 0/missing; mirror that.
|
|
1108
|
+
total_tokens: reportedTotal,
|
|
1109
|
+
cost_usd: 0,
|
|
1110
|
+
estimated: Object.keys(u).length === 0,
|
|
1111
|
+
purpose: opts.purpose
|
|
1112
|
+
};
|
|
1113
|
+
if (usage.total_tokens === 0) {
|
|
1114
|
+
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
|
|
1115
|
+
}
|
|
1116
|
+
return { text, usage };
|
|
1117
|
+
}
|
|
1118
|
+
function applyPricing3(usage, pricing) {
|
|
1312
1119
|
const { cost_usd, estimated } = calculateCost(
|
|
1313
1120
|
pricing,
|
|
1314
1121
|
usage.provider,
|
|
@@ -1323,20 +1130,36 @@ function applyPricing2(usage, pricing) {
|
|
|
1323
1130
|
estimated: usage.estimated || estimated
|
|
1324
1131
|
};
|
|
1325
1132
|
}
|
|
1326
|
-
async function
|
|
1327
|
-
const { url, headers, body } =
|
|
1133
|
+
async function sendOpenAICompatible(args) {
|
|
1134
|
+
const { url, headers, body } = buildOpenAICompatibleRequest({
|
|
1135
|
+
provider: args.provider,
|
|
1328
1136
|
model: args.model,
|
|
1329
1137
|
apiKey: args.apiKey,
|
|
1138
|
+
url: args.url,
|
|
1330
1139
|
messages: args.messages,
|
|
1331
1140
|
maxTokens: args.maxTokens,
|
|
1332
|
-
|
|
1333
|
-
// upcoming models reject it. See the header note.
|
|
1141
|
+
temperature: args.temperature,
|
|
1334
1142
|
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
1335
1143
|
});
|
|
1144
|
+
if (args.provider === "openai" && isReasoningModel("openai", args.model) && !/^o1(?:-|$)/i.test(args.model)) {
|
|
1145
|
+
const effort = (process.env["CROSSCHECK_OPENAI_REASONING_EFFORT"] ?? "low").toLowerCase();
|
|
1146
|
+
if (effort === "minimal" || effort === "low" || effort === "medium" || effort === "high") {
|
|
1147
|
+
body.reasoning_effort = effort;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
if (isReasoningModel(args.provider, args.model) && // Only where the cap is a SHARED budget. On a provider that bounds visible
|
|
1151
|
+
// output only (xAI), headroom cannot protect the answer from being crowded
|
|
1152
|
+
// out — nothing is crowding it — so it would merely authorise a
|
|
1153
|
+
// 25,000-token reply and the bill that comes with it.
|
|
1154
|
+
reasoningSharesOutputBudget(args.provider) && typeof body.max_completion_tokens === "number") {
|
|
1155
|
+
const raw = Number(process.env["CROSSCHECK_OPENAI_REASONING_HEADROOM_TOKENS"]);
|
|
1156
|
+
const headroom = Number.isFinite(raw) && raw >= 0 ? Math.trunc(raw) : 25e3;
|
|
1157
|
+
body.max_completion_tokens += headroom;
|
|
1158
|
+
}
|
|
1336
1159
|
const doFetch = args.fetchImpl ?? globalThis.fetch;
|
|
1337
1160
|
const init = {
|
|
1338
1161
|
method: "POST",
|
|
1339
|
-
headers
|
|
1162
|
+
headers,
|
|
1340
1163
|
body: JSON.stringify(body)
|
|
1341
1164
|
};
|
|
1342
1165
|
if (args.signal) init.signal = args.signal;
|
|
@@ -1347,7 +1170,7 @@ async function sendGemini(args) {
|
|
|
1347
1170
|
} catch (e) {
|
|
1348
1171
|
const d = diagnoseNetworkError(e);
|
|
1349
1172
|
if (!looksLikeNetworkFailure(d)) throw e;
|
|
1350
|
-
throw new ProviderError(d.kind, networkErrorMessage(
|
|
1173
|
+
throw new ProviderError(d.kind, networkErrorMessage(args.provider, d), {
|
|
1351
1174
|
transient: d.transient
|
|
1352
1175
|
});
|
|
1353
1176
|
}
|
|
@@ -1357,373 +1180,808 @@ async function sendGemini(args) {
|
|
|
1357
1180
|
try {
|
|
1358
1181
|
parsed = await respLike.json();
|
|
1359
1182
|
} catch (e) {
|
|
1360
|
-
throw new ProviderError("parse",
|
|
1183
|
+
throw new ProviderError("parse", `${args.provider}: response body not JSON: ${e.message}`);
|
|
1361
1184
|
}
|
|
1362
|
-
const { text, usage
|
|
1185
|
+
const { text, usage } = parseOpenAICompatibleResponse({
|
|
1363
1186
|
resp: parsed,
|
|
1187
|
+
provider: args.provider,
|
|
1364
1188
|
model: args.model,
|
|
1365
1189
|
purpose: args.purpose ?? "worker"
|
|
1366
1190
|
});
|
|
1367
|
-
|
|
1368
|
-
throw new ProviderError(
|
|
1369
|
-
"parse",
|
|
1370
|
-
`gemini: the answer was cut off before any visible text \u2014 thinking used the whole output budget (${usage.completion_tokens} tokens). Raise CROSSCHECK_GEMINI_THINKING_HEADROOM_TOKENS or the purpose's budget.`,
|
|
1371
|
-
{ transient: false }
|
|
1372
|
-
);
|
|
1373
|
-
}
|
|
1374
|
-
return { text, attempts: 1, usage: applyPricing2(usage, args.pricing) };
|
|
1191
|
+
return { text, attempts: 1, usage: applyPricing3(usage, args.pricing) };
|
|
1375
1192
|
}
|
|
1376
1193
|
const bodyText = await respLike.text().catch(() => "");
|
|
1377
|
-
throw httpFailureToProviderError(
|
|
1194
|
+
throw httpFailureToProviderError(args.provider, status, bodyText, parseRetryAfter(respLike), args.model);
|
|
1195
|
+
};
|
|
1196
|
+
await acquireRateLimit(args.provider, args.signal ? { signal: args.signal } : void 0);
|
|
1197
|
+
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// src/providers/registry.ts
|
|
1201
|
+
var DEFAULT_MODELS = {
|
|
1202
|
+
anthropic: "claude-opus-5-5",
|
|
1203
|
+
openai: "gpt-6-sol",
|
|
1204
|
+
xai: "grok-4-latest",
|
|
1205
|
+
mistral: "mistral-large-latest",
|
|
1206
|
+
groq: "llama-3.3-70b-versatile",
|
|
1207
|
+
deepseek: "deepseek-chat",
|
|
1208
|
+
gemini: "gemini-3.8-flash",
|
|
1209
|
+
kimi: "kimi-k3",
|
|
1210
|
+
qwen: "qwen3.8-max"
|
|
1211
|
+
};
|
|
1212
|
+
var DEFAULT_FALLBACKS = {
|
|
1213
|
+
// gpt-5.6 requires a billing-enabled account and is excluded from the free
|
|
1214
|
+
// tier, so gpt-5 remains the broadly-available floor. One hop, not a
|
|
1215
|
+
// ladder — an intermediate 5.5 step would just be another model to reason
|
|
1216
|
+
// about.
|
|
1217
|
+
openai: ["gpt-5"],
|
|
1218
|
+
// 3.8 Flash is recent enough that some projects won't have it yet.
|
|
1219
|
+
gemini: ["gemini-3.1-pro-preview"],
|
|
1220
|
+
// Opus 5.5 is new enough that an account could still be waiting on it.
|
|
1221
|
+
// Opus 5 is the same family one step back — and this chain is consulted
|
|
1222
|
+
// ONLY for a model-access failure, never for a bad key or a rate limit,
|
|
1223
|
+
// so it can't quietly downgrade anyone who simply hit a 429.
|
|
1224
|
+
anthropic: ["claude-opus-5"]
|
|
1225
|
+
};
|
|
1226
|
+
var MODEL_ENV_VARS = {
|
|
1227
|
+
anthropic: "ANTHROPIC_MODEL",
|
|
1228
|
+
openai: "OPENAI_MODEL",
|
|
1229
|
+
xai: "XAI_MODEL",
|
|
1230
|
+
mistral: "MISTRAL_MODEL",
|
|
1231
|
+
groq: "GROQ_MODEL",
|
|
1232
|
+
deepseek: "DEEPSEEK_MODEL",
|
|
1233
|
+
gemini: "GEMINI_MODEL",
|
|
1234
|
+
kimi: "KIMI_MODEL",
|
|
1235
|
+
qwen: "QWEN_MODEL"
|
|
1236
|
+
};
|
|
1237
|
+
function parseFallbackModels(raw) {
|
|
1238
|
+
if (!raw) return [];
|
|
1239
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1240
|
+
}
|
|
1241
|
+
function fallbackModelsFor(env, provider) {
|
|
1242
|
+
const envVar = MODEL_ENV_VARS[provider];
|
|
1243
|
+
const fromEnv = envVar ? parseFallbackModels(env[`${envVar}_FALLBACKS`]) : [];
|
|
1244
|
+
if (fromEnv.length > 0) return fromEnv;
|
|
1245
|
+
const pinned = envVar ? env[envVar] : void 0;
|
|
1246
|
+
if (pinned && pinned !== DEFAULT_MODELS[provider]) return [];
|
|
1247
|
+
return DEFAULT_FALLBACKS[provider] ?? [];
|
|
1248
|
+
}
|
|
1249
|
+
var DEFAULT_EXCLUDED_PROVIDERS = ["mistral", "groq"];
|
|
1250
|
+
function excludedProviders(env) {
|
|
1251
|
+
const parse = (v) => (v ?? "").split(",").map((x) => x.trim().toLowerCase()).filter((x) => x !== "");
|
|
1252
|
+
const excluded = new Set(
|
|
1253
|
+
parse(env["CROSSCHECK_EXCLUDE_PROVIDERS"]).length > 0 ? parse(env["CROSSCHECK_EXCLUDE_PROVIDERS"]) : DEFAULT_EXCLUDED_PROVIDERS
|
|
1254
|
+
);
|
|
1255
|
+
for (const name of parse(env["CROSSCHECK_ENABLE_PROVIDERS"])) excluded.delete(name);
|
|
1256
|
+
return excluded;
|
|
1257
|
+
}
|
|
1258
|
+
function buildProviders(opts) {
|
|
1259
|
+
const out = {};
|
|
1260
|
+
const excluded = excludedProviders(opts.env);
|
|
1261
|
+
const anthropicKey = opts.env["ANTHROPIC_API_KEY"];
|
|
1262
|
+
if (anthropicKey && !excluded.has("anthropic")) {
|
|
1263
|
+
const model = opts.env["ANTHROPIC_MODEL"] ?? DEFAULT_MODELS.anthropic;
|
|
1264
|
+
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts, fallbackModelsFor(opts.env, "anthropic"));
|
|
1265
|
+
}
|
|
1266
|
+
const openAiCompatSpec = [
|
|
1267
|
+
{ name: "openai", keyEnv: "OPENAI_API_KEY", modelEnv: "OPENAI_MODEL", defaultModel: DEFAULT_MODELS["openai"] },
|
|
1268
|
+
{ name: "xai", keyEnv: "XAI_API_KEY", modelEnv: "XAI_MODEL", defaultModel: DEFAULT_MODELS["xai"] },
|
|
1269
|
+
{ name: "mistral", keyEnv: "MISTRAL_API_KEY", modelEnv: "MISTRAL_MODEL", defaultModel: DEFAULT_MODELS["mistral"] },
|
|
1270
|
+
{ name: "groq", keyEnv: "GROQ_API_KEY", modelEnv: "GROQ_MODEL", defaultModel: DEFAULT_MODELS["groq"] },
|
|
1271
|
+
{ name: "deepseek", keyEnv: "DEEPSEEK_API_KEY", modelEnv: "DEEPSEEK_MODEL", defaultModel: DEFAULT_MODELS["deepseek"] },
|
|
1272
|
+
{ name: "kimi", keyEnv: "KIMI_API_KEY", modelEnv: "KIMI_MODEL", defaultModel: DEFAULT_MODELS["kimi"] },
|
|
1273
|
+
{ name: "qwen", keyEnv: "QWEN_API_KEY", modelEnv: "QWEN_MODEL", defaultModel: DEFAULT_MODELS["qwen"] }
|
|
1274
|
+
];
|
|
1275
|
+
for (const s of openAiCompatSpec) {
|
|
1276
|
+
const apiKey = opts.env[s.keyEnv];
|
|
1277
|
+
if (!apiKey) continue;
|
|
1278
|
+
if (excluded.has(s.name)) continue;
|
|
1279
|
+
const model = opts.env[s.modelEnv] ?? s.defaultModel;
|
|
1280
|
+
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts, fallbackModelsFor(opts.env, s.name));
|
|
1281
|
+
}
|
|
1282
|
+
const geminiKey = opts.env["GEMINI_API_KEY"];
|
|
1283
|
+
if (geminiKey && !excluded.has("gemini")) {
|
|
1284
|
+
const model = opts.env["GEMINI_MODEL"] ?? DEFAULT_MODELS.gemini;
|
|
1285
|
+
out["gemini"] = makeGeminiProvider(model, geminiKey, opts, fallbackModelsFor(opts.env, "gemini"));
|
|
1286
|
+
}
|
|
1287
|
+
return out;
|
|
1288
|
+
}
|
|
1289
|
+
function makeAnthropicProvider(model, apiKey, opts, fallbackModels = []) {
|
|
1290
|
+
return {
|
|
1291
|
+
name: "anthropic",
|
|
1292
|
+
model,
|
|
1293
|
+
fallbackModels,
|
|
1294
|
+
send: async (args) => {
|
|
1295
|
+
const sendOpts = {
|
|
1296
|
+
...args,
|
|
1297
|
+
apiKey,
|
|
1298
|
+
model: args.modelOverride ?? model,
|
|
1299
|
+
pricing: opts.pricing
|
|
1300
|
+
};
|
|
1301
|
+
if (opts.fetchImpl) sendOpts.fetchImpl = opts.fetchImpl;
|
|
1302
|
+
return sendAnthropic(sendOpts);
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
function makeOpenAICompatibleProvider(name, model, apiKey, opts, fallbackModels = []) {
|
|
1307
|
+
const url = OPENAI_COMPAT_DEFAULT_URLS[name];
|
|
1308
|
+
return {
|
|
1309
|
+
name,
|
|
1310
|
+
model,
|
|
1311
|
+
fallbackModels,
|
|
1312
|
+
send: async (args) => {
|
|
1313
|
+
const sendOpts = {
|
|
1314
|
+
...args,
|
|
1315
|
+
provider: name,
|
|
1316
|
+
apiKey,
|
|
1317
|
+
model: args.modelOverride ?? model,
|
|
1318
|
+
url,
|
|
1319
|
+
pricing: opts.pricing
|
|
1320
|
+
};
|
|
1321
|
+
if (opts.fetchImpl) sendOpts.fetchImpl = opts.fetchImpl;
|
|
1322
|
+
return sendOpenAICompatible(sendOpts);
|
|
1323
|
+
}
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
|
|
1327
|
+
return {
|
|
1328
|
+
name: "gemini",
|
|
1329
|
+
model,
|
|
1330
|
+
fallbackModels,
|
|
1331
|
+
send: async (args) => {
|
|
1332
|
+
const sendOpts = {
|
|
1333
|
+
...args,
|
|
1334
|
+
apiKey,
|
|
1335
|
+
model: args.modelOverride ?? model,
|
|
1336
|
+
pricing: opts.pricing
|
|
1337
|
+
};
|
|
1338
|
+
if (opts.fetchImpl) sendOpts.fetchImpl = opts.fetchImpl;
|
|
1339
|
+
return sendGemini(sendOpts);
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
// src/core/reasoning-upgrade.ts
|
|
1345
|
+
var UPGRADE_MODEL = "claude-fable-5";
|
|
1346
|
+
var UPGRADE_LABEL = "Fable 5";
|
|
1347
|
+
function isUpgradeRequested(args) {
|
|
1348
|
+
if (args["upgrade"] === true) return true;
|
|
1349
|
+
const reasoning = args["reasoning"];
|
|
1350
|
+
if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
|
|
1351
|
+
return true;
|
|
1352
|
+
}
|
|
1353
|
+
const model = args["model"];
|
|
1354
|
+
if (typeof model === "string") {
|
|
1355
|
+
const m = model.trim().toLowerCase();
|
|
1356
|
+
if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
|
|
1357
|
+
return true;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return false;
|
|
1361
|
+
}
|
|
1362
|
+
var HEAVY_TERMS = [
|
|
1363
|
+
/\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
|
|
1364
|
+
/\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
|
|
1365
|
+
/\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
|
|
1366
|
+
/\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
|
|
1367
|
+
/\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
|
|
1368
|
+
/\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
|
|
1369
|
+
/\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
|
|
1370
|
+
/\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
|
|
1371
|
+
/\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
|
|
1372
|
+
/\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
|
|
1373
|
+
/\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
|
|
1374
|
+
/\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
|
|
1375
|
+
];
|
|
1376
|
+
var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
|
|
1377
|
+
function assessReasoning(text) {
|
|
1378
|
+
const s = typeof text === "string" ? text : "";
|
|
1379
|
+
const signals = [];
|
|
1380
|
+
let score = 0;
|
|
1381
|
+
let termHits = 0;
|
|
1382
|
+
for (const re of HEAVY_TERMS) {
|
|
1383
|
+
if (re.test(s)) termHits += 1;
|
|
1384
|
+
}
|
|
1385
|
+
if (termHits > 0) {
|
|
1386
|
+
const add = Math.min(termHits, 4);
|
|
1387
|
+
score += add;
|
|
1388
|
+
signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
|
|
1389
|
+
}
|
|
1390
|
+
if (DIFFICULTY_TERMS.test(s)) {
|
|
1391
|
+
score += 1;
|
|
1392
|
+
signals.push("explicit difficulty marker");
|
|
1393
|
+
}
|
|
1394
|
+
const words = s.split(/\s+/).filter(Boolean).length;
|
|
1395
|
+
if (words >= 120) {
|
|
1396
|
+
score += 2;
|
|
1397
|
+
signals.push("long, detailed prompt");
|
|
1398
|
+
} else if (words >= 50) {
|
|
1399
|
+
score += 1;
|
|
1400
|
+
signals.push("substantial prompt");
|
|
1401
|
+
}
|
|
1402
|
+
const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
|
|
1403
|
+
if (constraints >= 3) {
|
|
1404
|
+
score += 1;
|
|
1405
|
+
signals.push(`${constraints} constraints/steps`);
|
|
1406
|
+
}
|
|
1407
|
+
const heavy = score >= 3;
|
|
1408
|
+
const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
|
|
1409
|
+
return { heavy, score, reason, signals };
|
|
1410
|
+
}
|
|
1411
|
+
function buildSuggestedUpgrade(toolName, assessment) {
|
|
1412
|
+
return {
|
|
1413
|
+
model: UPGRADE_MODEL,
|
|
1414
|
+
label: UPGRADE_LABEL,
|
|
1415
|
+
reason: assessment.reason,
|
|
1416
|
+
how: `re-run \`${toolName}\` with reasoning:"max" (or upgrade:true) to route the lead reasoning seat to ${UPGRADE_LABEL}, and \u2014 when an OpenAI key is configured \u2014 add an OpenAI second-opinion pass that ${UPGRADE_LABEL} weighs before finalizing (the result names which model ran). Other panel seats stay on their normal models.`
|
|
1378
1417
|
};
|
|
1379
|
-
await acquireRateLimit("gemini", args.signal ? { signal: args.signal } : void 0);
|
|
1380
|
-
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1381
1418
|
}
|
|
1382
1419
|
|
|
1383
|
-
// src/
|
|
1384
|
-
var
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
//
|
|
1391
|
-
|
|
1392
|
-
//
|
|
1393
|
-
|
|
1394
|
-
//
|
|
1395
|
-
//
|
|
1396
|
-
|
|
1420
|
+
// src/core/super-mode.ts
|
|
1421
|
+
var SUPER_MODELS = {
|
|
1422
|
+
// Fable 5.1 verified present on the account; 5 as the fallback for accounts
|
|
1423
|
+
// that haven't been granted 5.1 yet.
|
|
1424
|
+
anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
|
|
1425
|
+
// Astra is live (verified against the account's model list 2026-09-23).
|
|
1426
|
+
// The fallback is named explicitly rather than reusing CO_REASON_MODEL:
|
|
1427
|
+
// that constant means "OpenAI's top reasoning model" for the max-reasoning
|
|
1428
|
+
// co-reasoner, which is a different question from "what should super fall
|
|
1429
|
+
// back to". Sharing the symbol silently coupled two unrelated decisions.
|
|
1430
|
+
openai: ["gpt-6-astra", "gpt-6-sol"],
|
|
1431
|
+
// Explicitly left as-is per product decision. Note grok-4-latest is not in
|
|
1432
|
+
// xAI's published model list, but it resolves in practice — real panel calls
|
|
1433
|
+
// on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
|
|
1434
|
+
// rather than a dead id.
|
|
1435
|
+
xai: [DEFAULT_MODELS["xai"]],
|
|
1436
|
+
// 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
|
|
1437
|
+
// so this can no longer borrow DEFAULT_MODELS as its fallback — that would
|
|
1438
|
+
// repeat the same model and retry one that just failed. Named explicitly.
|
|
1439
|
+
gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
|
|
1440
|
+
kimi: [DEFAULT_MODELS["kimi"]],
|
|
1441
|
+
qwen: [DEFAULT_MODELS["qwen"]]
|
|
1397
1442
|
};
|
|
1398
|
-
var
|
|
1399
|
-
"
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1443
|
+
var SUPER_ENV_VARS = {
|
|
1444
|
+
anthropic: "ANTHROPIC_SUPER_MODEL",
|
|
1445
|
+
openai: "OPENAI_SUPER_MODEL",
|
|
1446
|
+
xai: "XAI_SUPER_MODEL",
|
|
1447
|
+
gemini: "GEMINI_SUPER_MODEL",
|
|
1448
|
+
kimi: "KIMI_SUPER_MODEL",
|
|
1449
|
+
qwen: "QWEN_SUPER_MODEL",
|
|
1450
|
+
mistral: "MISTRAL_SUPER_MODEL",
|
|
1451
|
+
groq: "GROQ_SUPER_MODEL",
|
|
1452
|
+
deepseek: "DEEPSEEK_SUPER_MODEL"
|
|
1453
|
+
};
|
|
1454
|
+
function parseChain(v) {
|
|
1455
|
+
if (!v) return [];
|
|
1456
|
+
const out = [];
|
|
1457
|
+
for (const raw of v.split(",")) {
|
|
1458
|
+
const m = raw.trim();
|
|
1459
|
+
if (m && !out.includes(m)) out.push(m);
|
|
1404
1460
|
}
|
|
1405
|
-
|
|
1406
|
-
if (/^o1(?:-|$)/.test(m)) return false;
|
|
1407
|
-
return true;
|
|
1461
|
+
return out;
|
|
1408
1462
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1463
|
+
var superEnvStore = new import_node_async_hooks.AsyncLocalStorage();
|
|
1464
|
+
var currentEnv = () => superEnvStore.getStore() ?? process.env;
|
|
1465
|
+
function superChainFor(provider, env = currentEnv()) {
|
|
1466
|
+
const varName = SUPER_ENV_VARS[provider];
|
|
1467
|
+
const user = varName ? parseChain(env[varName]) : [];
|
|
1468
|
+
if (user.length > 0) {
|
|
1469
|
+
const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
|
|
1470
|
+
return [...user, ...fallbacks.filter((m) => !user.includes(m))];
|
|
1471
|
+
}
|
|
1472
|
+
return SUPER_MODELS[provider] ?? [];
|
|
1473
|
+
}
|
|
1474
|
+
function superPrimary(provider, env = currentEnv()) {
|
|
1475
|
+
return superChainFor(provider, env)[0];
|
|
1476
|
+
}
|
|
1477
|
+
var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
|
|
1478
|
+
function isSuperRequested(args) {
|
|
1479
|
+
return args["super"] === true;
|
|
1480
|
+
}
|
|
1481
|
+
function retargetForSuper(p, env = currentEnv()) {
|
|
1482
|
+
const chain = superChainFor(p.name, env);
|
|
1483
|
+
if (chain.length === 0) return p;
|
|
1484
|
+
const [primary, ...fallbacks] = chain;
|
|
1485
|
+
return retargetProvider(p, primary, fallbacks);
|
|
1486
|
+
}
|
|
1487
|
+
var SUPER_BUDGET_MULTIPLIER = 3;
|
|
1488
|
+
var SUPER_BUDGET_CEILING = 16e3;
|
|
1489
|
+
function superBudget(base) {
|
|
1490
|
+
return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// src/core/moderator.ts
|
|
1494
|
+
var DEFAULT_MODERATOR = "anthropic";
|
|
1495
|
+
var PROVIDER_ALIASES = {
|
|
1496
|
+
grok: "xai",
|
|
1497
|
+
claude: "anthropic",
|
|
1498
|
+
google: "gemini",
|
|
1499
|
+
chatgpt: "openai",
|
|
1500
|
+
gpt: "openai",
|
|
1501
|
+
moonshot: "kimi"
|
|
1502
|
+
};
|
|
1503
|
+
function parseModeratorSpec(raw) {
|
|
1504
|
+
if (typeof raw !== "string") return null;
|
|
1505
|
+
const s = raw.trim();
|
|
1506
|
+
if (!s) return null;
|
|
1507
|
+
const i = s.indexOf(":");
|
|
1508
|
+
const providerPart = (i < 0 ? s : s.slice(0, i)).trim().toLowerCase();
|
|
1509
|
+
const modelPart = i < 0 ? "" : s.slice(i + 1).trim();
|
|
1510
|
+
if (!providerPart) return null;
|
|
1511
|
+
return {
|
|
1512
|
+
provider: PROVIDER_ALIASES[providerPart] ?? providerPart,
|
|
1513
|
+
model: modelPart || null
|
|
1417
1514
|
};
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1515
|
+
}
|
|
1516
|
+
function pickModeratorSpec(...candidates) {
|
|
1517
|
+
for (const c of candidates) {
|
|
1518
|
+
const s = parseModeratorSpec(c);
|
|
1519
|
+
if (s) return s;
|
|
1520
|
+
}
|
|
1521
|
+
return { provider: DEFAULT_MODERATOR, model: null };
|
|
1522
|
+
}
|
|
1523
|
+
function resolveModerator(providers, ...candidates) {
|
|
1524
|
+
const spec = pickModeratorSpec(...candidates);
|
|
1525
|
+
const base = providers[spec.provider];
|
|
1526
|
+
if (!base) return { provider: void 0, spec, pinned: false };
|
|
1527
|
+
if (!spec.model) return { provider: base, spec, pinned: false };
|
|
1528
|
+
return { provider: retargetProvider(base, spec.model), spec, pinned: true };
|
|
1529
|
+
}
|
|
1530
|
+
function synthesisSeat(o) {
|
|
1531
|
+
const { moderator, pinned, superRequested, upgradeRequested } = o;
|
|
1532
|
+
const wantsMore = superRequested || upgradeRequested;
|
|
1533
|
+
if (!wantsMore) return { provider: moderator, upgraded: false, label: null, skipReason: null };
|
|
1534
|
+
if (pinned) {
|
|
1535
|
+
return {
|
|
1536
|
+
provider: moderator,
|
|
1537
|
+
upgraded: false,
|
|
1538
|
+
label: null,
|
|
1539
|
+
skipReason: `the moderator is pinned to ${moderator.name}:${moderator.model}; a pinned model is used as-is.`
|
|
1540
|
+
};
|
|
1423
1541
|
}
|
|
1424
|
-
if (
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
// (planner, judges, synthesis) was a 400, unnoticed until the canary
|
|
1434
|
-
// grew a structured check (2026-10-11). Non-strict still shapes the
|
|
1435
|
-
// reply to the schema, and structured.ts validates it against the
|
|
1436
|
-
// full schema regardless, so strictness added a failure mode and no
|
|
1437
|
-
// safety.
|
|
1438
|
-
strict: false
|
|
1439
|
-
}
|
|
1542
|
+
if (moderator.name === "anthropic" && !superRequested) {
|
|
1543
|
+
return { provider: retargetProvider(moderator, UPGRADE_MODEL), upgraded: true, label: UPGRADE_LABEL, skipReason: null };
|
|
1544
|
+
}
|
|
1545
|
+
if (superChainFor(moderator.name).length === 0) {
|
|
1546
|
+
return {
|
|
1547
|
+
provider: moderator,
|
|
1548
|
+
upgraded: false,
|
|
1549
|
+
label: null,
|
|
1550
|
+
skipReason: `no flagship model is known for ${moderator.name}; the moderator ran at ${moderator.model}.`
|
|
1440
1551
|
};
|
|
1441
1552
|
}
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
};
|
|
1446
|
-
return { url: opts.url, headers, body };
|
|
1553
|
+
const seat = retargetForSuper(moderator);
|
|
1554
|
+
const label = moderator.name === "anthropic" ? UPGRADE_LABEL : seat.model;
|
|
1555
|
+
return { provider: seat, upgraded: true, label, skipReason: null };
|
|
1447
1556
|
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1557
|
+
|
|
1558
|
+
// ../../config/pricing.json
|
|
1559
|
+
var pricing_default = {
|
|
1560
|
+
_meta: {
|
|
1561
|
+
currency: "USD",
|
|
1562
|
+
unit: "per_1k_tokens",
|
|
1563
|
+
updated_at: "2026-09-06",
|
|
1564
|
+
notes: "Prices reflect public list pricing per provider. cached_per_1k applies to prompt tokens served from prompt-cache (Anthropic) or input-cache (OpenAI) when supported. Update when providers change rates. Missing models fall back to cost=0 with estimated=true. 2026-06-07: removed decommissioned gemini-2.0-flash / gemini-2.0-flash-lite (Google returns 404) and replaced the low-tier lite model with gemini-2.5-flash-lite. 2026-06-08: bumped the default Anthropic model to claude-opus-4-8. Kept gemini-2.5-pro as the Gemini default (gemini-3.1-pro-preview measured ~2.2x the cost at $2/$12 per M \u2014 output-token dominated by ~1.5k thinking tokens \u2014 so not worth it for a 1-of-4 panel voice). 2026-06-09: corrected rates verified against live provider pricing pages \u2014 gemini-2.5-pro output $5->$10/M; gemini-2.5-flash $0.15/$0.60 -> $0.30/$2.50; gpt-5 $5/$15 -> $1.25/$10 (cached $0.125); grok-4-latest input $5->$3/M (legacy grok-4 tier). Anthropic opus/sonnet/haiku rates confirmed unchanged. Older/unused models (gpt-4o, o1, o3, grok-3) not all re-verified. 2026-07-02: added claude-fable-5 as the optional reasoning-upgrade model (used only for the lead reasoning seat when the operator bumps a heavy task). 2026-07-04: set claude-fable-5 to its real pay-as-you-go API price effective 2026-07-07 \u2014 $10/M input ($0.01/1k), $50/M output ($0.05/1k), cached at 10% of input ($0.001/1k). Dropped the estimated flag. Note: Fable 5 is cheaper than opus-4-8 on both input and output. 2026-07-09: Google retired gemini-2.5-pro AND gemini-3-pro-preview (both now 404 'no longer available'). Moved the Gemini default to gemini-3.1-pro-preview ($2/M in, $12/M out, cache $0.20/M \u2014 the newest working pro; note it's a preview and may need another bump) and added gemini-3.5-flash ($1.50/M in, $9/M out, cache $0.15/M \u2014 GA) to the low/cheap tier. Removed the dead gemini-2.5-pro rate. IMPORTANT: gemini-3.x are heavy thinking models \u2014 thoughtsTokenCount (billed as output) is now folded into completion_tokens in the adapter, so cost reflects reasoning tokens. 2026-07-13: bumped the default OpenAI model to gpt-5.5 ($5/M in, $30/M out, cached $0.50/M \u2014 verified live). Note gpt-5.5 is markedly pricier than gpt-5 ($1.25/$10): ~4x input, 3x output. Kept gpt-5 in the table for cost lookups on pinned installs. 2026-07-16: added gpt-5.6 at the Sol (flagship) tier \u2014 $5/M in, $30/M out, cached $0.50/M (cache-write $6.25/M not modeled). gpt-5.6 is the second premium voice for the max-reasoning co-reasoner (runs alongside claude-fable-5 when an OpenAI key is set). Terra/Luna tiers not added (unused). 2026-07-17: added Moonshot AI (Kimi) provider with kimi-k3, base URL https://api.moonshot.ai/v1. Confirmed real rates \u2014 $3.00/M input (cache miss, $0.003/1k), $15.00/M output ($0.015/1k), $0.30/M cache-hit input ($0.0003/1k); 1M-token context. Kimi K3 is reasoning-class (temperature must be 1; uses max_completion_tokens). 2026-07-24: added claude-opus-5 ($5/M in, $25/M out, cached $0.50/M -- 10% of input, matching this table's existing cache-discount ratio) and made it the default anthropic model (was claude-opus-4-8). Fable 5 remains the reasoning-upgrade/super-mode target, unchanged. 2026-08-04: added Alibaba Cloud Qwen provider (OpenAI-compatible, DashScope international endpoint https://dashscope-intl.aliyuncs.com/compatible-mode/v1) with default model qwen3.8-max -- confirmed business pricing $2.00/M input ($0.002/1k), $6.00/M output ($0.006/1k), cache estimated at 10% of input ($0.0002/1k, not independently confirmed). Also added qwen3.7-max as a fallback tier at $1.25/M in ($0.00125/1k) / $3.75/M out ($0.00375/1k). Note: the model string initially configured (qwen-v3.8 / qwen-v3.7) does not exist on DashScope and 404s -- live-probed the real key against qwen3.8-max, qwen3.7-max, qwen3-max, qwen-max, and qwen-plus, all of which work; corrected the default and pricing keys to qwen3.8-max/qwen3.7-max and fixed QWEN_MODEL in both .env files to match. Qwen's Max tier is a standard instruct model, not reasoning-class -- normal temperature/max_tokens handling. 2026-09-06: added gpt-6-astra at published list rates ($10/$50 per Mtok, $1/Mtok cache read; the $12.50/Mtok cache-WRITE rate has no field in this schema and is not modelled). gemini-3.8-flash now carries Google's published paid-tier rates ($0.75/$3.75/$0.075 per Mtok). THESE DOUBLE ON 2027-01-01 ($1.50/$7.50/$0.15) and this schema has no effective-date support, so they must be updated by hand on that date or every Gemini cost will read half of actual. claude-fable-5-1 now carries published rates ($10/$50/$0.25 per Mtok). Note its cache-read rate is 4x cheaper than claude-fable-5's, so the two must not be assumed to track each other. No inferred rows remain. All three were previously absent, which silently recorded their cost as $0."
|
|
1565
|
+
},
|
|
1566
|
+
anthropic: {
|
|
1567
|
+
_source: {
|
|
1568
|
+
url: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
1569
|
+
verified_at: "2026-09-27"
|
|
1570
|
+
},
|
|
1571
|
+
"claude-opus-4-5": {
|
|
1572
|
+
prompt_per_1k: 5e-3,
|
|
1573
|
+
completion_per_1k: 0.025,
|
|
1574
|
+
cached_per_1k: 5e-4
|
|
1575
|
+
},
|
|
1576
|
+
"claude-opus-4-7": {
|
|
1577
|
+
prompt_per_1k: 5e-3,
|
|
1578
|
+
completion_per_1k: 0.025,
|
|
1579
|
+
cached_per_1k: 5e-4
|
|
1580
|
+
},
|
|
1581
|
+
"claude-opus-4-8": {
|
|
1582
|
+
prompt_per_1k: 5e-3,
|
|
1583
|
+
completion_per_1k: 0.025,
|
|
1584
|
+
cached_per_1k: 5e-4
|
|
1585
|
+
},
|
|
1586
|
+
"claude-opus-5-5": {
|
|
1587
|
+
prompt_per_1k: 4e-3,
|
|
1588
|
+
completion_per_1k: 0.02,
|
|
1589
|
+
cached_per_1k: 2e-4
|
|
1590
|
+
},
|
|
1591
|
+
"claude-opus-5": {
|
|
1592
|
+
prompt_per_1k: 5e-3,
|
|
1593
|
+
completion_per_1k: 0.025,
|
|
1594
|
+
cached_per_1k: 5e-4
|
|
1595
|
+
},
|
|
1596
|
+
"claude-fable-5": {
|
|
1597
|
+
prompt_per_1k: 0.01,
|
|
1598
|
+
completion_per_1k: 0.05,
|
|
1599
|
+
cached_per_1k: 1e-3
|
|
1600
|
+
},
|
|
1601
|
+
"claude-sonnet-4-5": {
|
|
1602
|
+
prompt_per_1k: 3e-3,
|
|
1603
|
+
completion_per_1k: 0.015,
|
|
1604
|
+
cached_per_1k: 3e-4
|
|
1605
|
+
},
|
|
1606
|
+
"claude-sonnet-4-6": {
|
|
1607
|
+
prompt_per_1k: 3e-3,
|
|
1608
|
+
completion_per_1k: 0.015,
|
|
1609
|
+
cached_per_1k: 3e-4
|
|
1610
|
+
},
|
|
1611
|
+
"claude-haiku-4-5": {
|
|
1612
|
+
prompt_per_1k: 1e-3,
|
|
1613
|
+
completion_per_1k: 5e-3,
|
|
1614
|
+
cached_per_1k: 1e-4
|
|
1615
|
+
},
|
|
1616
|
+
"claude-3-5-sonnet": {
|
|
1617
|
+
prompt_per_1k: 3e-3,
|
|
1618
|
+
completion_per_1k: 0.015,
|
|
1619
|
+
cached_per_1k: 3e-4
|
|
1620
|
+
},
|
|
1621
|
+
"claude-3-5-haiku": {
|
|
1622
|
+
prompt_per_1k: 8e-4,
|
|
1623
|
+
completion_per_1k: 4e-3,
|
|
1624
|
+
cached_per_1k: 8e-5
|
|
1625
|
+
},
|
|
1626
|
+
"claude-fable-5-1": {
|
|
1627
|
+
prompt_per_1k: 0.01,
|
|
1628
|
+
completion_per_1k: 0.05,
|
|
1629
|
+
cached_per_1k: 25e-5
|
|
1630
|
+
}
|
|
1631
|
+
},
|
|
1632
|
+
openai: {
|
|
1633
|
+
_source: {
|
|
1634
|
+
url: "https://developers.openai.com/api/docs/pricing",
|
|
1635
|
+
verified_at: "2026-09-27"
|
|
1636
|
+
},
|
|
1637
|
+
"gpt-6-astra": {
|
|
1638
|
+
prompt_per_1k: 0.01,
|
|
1639
|
+
completion_per_1k: 0.05,
|
|
1640
|
+
cached_per_1k: 1e-3
|
|
1641
|
+
},
|
|
1642
|
+
"gpt-6-sol": {
|
|
1643
|
+
prompt_per_1k: 2e-3,
|
|
1644
|
+
completion_per_1k: 0.01,
|
|
1645
|
+
cached_per_1k: 2e-4
|
|
1646
|
+
},
|
|
1647
|
+
"gpt-6-luna": {
|
|
1648
|
+
prompt_per_1k: 1e-4,
|
|
1649
|
+
completion_per_1k: 5e-4,
|
|
1650
|
+
cached_per_1k: 1e-5
|
|
1651
|
+
},
|
|
1652
|
+
"gpt-5.6-sol": {
|
|
1653
|
+
prompt_per_1k: 4e-3,
|
|
1654
|
+
completion_per_1k: 0.02,
|
|
1655
|
+
cached_per_1k: 4e-4
|
|
1656
|
+
},
|
|
1657
|
+
"gpt-5.6-terra": {
|
|
1658
|
+
prompt_per_1k: 2e-3,
|
|
1659
|
+
completion_per_1k: 0.012,
|
|
1660
|
+
cached_per_1k: 2e-4
|
|
1661
|
+
},
|
|
1662
|
+
"gpt-5.6-luna": {
|
|
1663
|
+
prompt_per_1k: 2e-4,
|
|
1664
|
+
completion_per_1k: 12e-4,
|
|
1665
|
+
cached_per_1k: 2e-5
|
|
1666
|
+
},
|
|
1667
|
+
"gpt-5.6": {
|
|
1668
|
+
prompt_per_1k: 4e-3,
|
|
1669
|
+
completion_per_1k: 0.02,
|
|
1670
|
+
cached_per_1k: 4e-4
|
|
1671
|
+
},
|
|
1672
|
+
"gpt-5.5": {
|
|
1673
|
+
prompt_per_1k: 5e-3,
|
|
1674
|
+
completion_per_1k: 0.03,
|
|
1675
|
+
cached_per_1k: 5e-4
|
|
1676
|
+
},
|
|
1677
|
+
"gpt-5": {
|
|
1678
|
+
prompt_per_1k: 125e-5,
|
|
1679
|
+
completion_per_1k: 0.01,
|
|
1680
|
+
cached_per_1k: 125e-6
|
|
1681
|
+
},
|
|
1682
|
+
"gpt-5-mini": {
|
|
1683
|
+
prompt_per_1k: 25e-5,
|
|
1684
|
+
completion_per_1k: 2e-3,
|
|
1685
|
+
cached_per_1k: 25e-6
|
|
1686
|
+
},
|
|
1687
|
+
"gpt-5-nano": {
|
|
1688
|
+
prompt_per_1k: 5e-5,
|
|
1689
|
+
completion_per_1k: 4e-4,
|
|
1690
|
+
cached_per_1k: 5e-6
|
|
1691
|
+
},
|
|
1692
|
+
"gpt-4o": {
|
|
1693
|
+
prompt_per_1k: 25e-4,
|
|
1694
|
+
completion_per_1k: 0.01,
|
|
1695
|
+
cached_per_1k: 125e-5
|
|
1696
|
+
},
|
|
1697
|
+
"gpt-4o-mini": {
|
|
1698
|
+
prompt_per_1k: 15e-5,
|
|
1699
|
+
completion_per_1k: 6e-4,
|
|
1700
|
+
cached_per_1k: 75e-6
|
|
1701
|
+
},
|
|
1702
|
+
o1: {
|
|
1703
|
+
prompt_per_1k: 0.015,
|
|
1704
|
+
completion_per_1k: 0.06,
|
|
1705
|
+
cached_per_1k: 75e-4
|
|
1706
|
+
},
|
|
1707
|
+
"o1-mini": {
|
|
1708
|
+
prompt_per_1k: 3e-3,
|
|
1709
|
+
completion_per_1k: 0.012,
|
|
1710
|
+
cached_per_1k: 15e-4
|
|
1711
|
+
},
|
|
1712
|
+
o3: {
|
|
1713
|
+
prompt_per_1k: 2e-3,
|
|
1714
|
+
completion_per_1k: 8e-3,
|
|
1715
|
+
cached_per_1k: 5e-4
|
|
1716
|
+
},
|
|
1717
|
+
"o3-mini": {
|
|
1718
|
+
prompt_per_1k: 11e-4,
|
|
1719
|
+
completion_per_1k: 44e-4,
|
|
1720
|
+
cached_per_1k: 55e-5
|
|
1721
|
+
},
|
|
1722
|
+
"gpt-5-pro": {
|
|
1723
|
+
prompt_per_1k: 0.015,
|
|
1724
|
+
completion_per_1k: 0.12,
|
|
1725
|
+
cached_per_1k: 0
|
|
1726
|
+
},
|
|
1727
|
+
"gpt-5.2-pro": {
|
|
1728
|
+
prompt_per_1k: 0.021,
|
|
1729
|
+
completion_per_1k: 0.168,
|
|
1730
|
+
cached_per_1k: 0
|
|
1731
|
+
},
|
|
1732
|
+
"gpt-5.4-pro": {
|
|
1733
|
+
prompt_per_1k: 0.03,
|
|
1734
|
+
completion_per_1k: 0.18,
|
|
1735
|
+
cached_per_1k: 0
|
|
1736
|
+
},
|
|
1737
|
+
"gpt-5.5-pro": {
|
|
1738
|
+
prompt_per_1k: 0.03,
|
|
1739
|
+
completion_per_1k: 0.18,
|
|
1740
|
+
cached_per_1k: 0
|
|
1741
|
+
},
|
|
1742
|
+
"o1-pro": {
|
|
1743
|
+
prompt_per_1k: 0.15,
|
|
1744
|
+
completion_per_1k: 0.6,
|
|
1745
|
+
cached_per_1k: 0
|
|
1746
|
+
},
|
|
1747
|
+
"o3-pro": {
|
|
1748
|
+
prompt_per_1k: 0.02,
|
|
1749
|
+
completion_per_1k: 0.08,
|
|
1750
|
+
cached_per_1k: 0
|
|
1455
1751
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1752
|
+
},
|
|
1753
|
+
xai: {
|
|
1754
|
+
"grok-4-latest": {
|
|
1755
|
+
prompt_per_1k: 3e-3,
|
|
1756
|
+
completion_per_1k: 0.015,
|
|
1757
|
+
cached_per_1k: 75e-5
|
|
1758
|
+
},
|
|
1759
|
+
"grok-3": {
|
|
1760
|
+
prompt_per_1k: 3e-3,
|
|
1761
|
+
completion_per_1k: 0.015,
|
|
1762
|
+
cached_per_1k: 15e-4
|
|
1763
|
+
},
|
|
1764
|
+
"grok-3-mini": {
|
|
1765
|
+
prompt_per_1k: 3e-4,
|
|
1766
|
+
completion_per_1k: 5e-4,
|
|
1767
|
+
cached_per_1k: 15e-5
|
|
1460
1768
|
}
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
estimated: Object.keys(u).length === 0,
|
|
1488
|
-
purpose: opts.purpose
|
|
1489
|
-
};
|
|
1490
|
-
if (usage.total_tokens === 0) {
|
|
1491
|
-
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
|
|
1492
|
-
}
|
|
1493
|
-
return { text, usage };
|
|
1494
|
-
}
|
|
1495
|
-
function applyPricing3(usage, pricing) {
|
|
1496
|
-
const { cost_usd, estimated } = calculateCost(
|
|
1497
|
-
pricing,
|
|
1498
|
-
usage.provider,
|
|
1499
|
-
usage.model,
|
|
1500
|
-
usage.prompt_tokens,
|
|
1501
|
-
usage.completion_tokens,
|
|
1502
|
-
usage.cached_tokens
|
|
1503
|
-
);
|
|
1504
|
-
return {
|
|
1505
|
-
...usage,
|
|
1506
|
-
cost_usd,
|
|
1507
|
-
estimated: usage.estimated || estimated
|
|
1508
|
-
};
|
|
1509
|
-
}
|
|
1510
|
-
async function sendOpenAICompatible(args) {
|
|
1511
|
-
const { url, headers, body } = buildOpenAICompatibleRequest({
|
|
1512
|
-
provider: args.provider,
|
|
1513
|
-
model: args.model,
|
|
1514
|
-
apiKey: args.apiKey,
|
|
1515
|
-
url: args.url,
|
|
1516
|
-
messages: args.messages,
|
|
1517
|
-
maxTokens: args.maxTokens,
|
|
1518
|
-
temperature: args.temperature,
|
|
1519
|
-
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
1520
|
-
});
|
|
1521
|
-
if (args.provider === "openai" && isReasoningModel("openai", args.model) && !/^o1(?:-|$)/i.test(args.model)) {
|
|
1522
|
-
const effort = (process.env["CROSSCHECK_OPENAI_REASONING_EFFORT"] ?? "low").toLowerCase();
|
|
1523
|
-
if (effort === "minimal" || effort === "low" || effort === "medium" || effort === "high") {
|
|
1524
|
-
body.reasoning_effort = effort;
|
|
1769
|
+
},
|
|
1770
|
+
gemini: {
|
|
1771
|
+
"gemini-3.1-pro-preview": {
|
|
1772
|
+
prompt_per_1k: 2e-3,
|
|
1773
|
+
completion_per_1k: 0.012,
|
|
1774
|
+
cached_per_1k: 2e-4
|
|
1775
|
+
},
|
|
1776
|
+
"gemini-3.5-flash": {
|
|
1777
|
+
prompt_per_1k: 15e-4,
|
|
1778
|
+
completion_per_1k: 9e-3,
|
|
1779
|
+
cached_per_1k: 15e-5
|
|
1780
|
+
},
|
|
1781
|
+
"gemini-2.5-flash": {
|
|
1782
|
+
prompt_per_1k: 3e-4,
|
|
1783
|
+
completion_per_1k: 25e-4,
|
|
1784
|
+
cached_per_1k: 75e-6
|
|
1785
|
+
},
|
|
1786
|
+
"gemini-2.5-flash-lite": {
|
|
1787
|
+
prompt_per_1k: 1e-4,
|
|
1788
|
+
completion_per_1k: 4e-4,
|
|
1789
|
+
cached_per_1k: 25e-6
|
|
1790
|
+
},
|
|
1791
|
+
"gemini-3.8-flash": {
|
|
1792
|
+
prompt_per_1k: 75e-5,
|
|
1793
|
+
completion_per_1k: 375e-5,
|
|
1794
|
+
cached_per_1k: 75e-6
|
|
1525
1795
|
}
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
const init = {
|
|
1538
|
-
method: "POST",
|
|
1539
|
-
headers,
|
|
1540
|
-
body: JSON.stringify(body)
|
|
1541
|
-
};
|
|
1542
|
-
if (args.signal) init.signal = args.signal;
|
|
1543
|
-
const attemptOnce = async () => {
|
|
1544
|
-
let respLike;
|
|
1545
|
-
try {
|
|
1546
|
-
respLike = await doFetch(url, init);
|
|
1547
|
-
} catch (e) {
|
|
1548
|
-
const d = diagnoseNetworkError(e);
|
|
1549
|
-
if (!looksLikeNetworkFailure(d)) throw e;
|
|
1550
|
-
throw new ProviderError(d.kind, networkErrorMessage(args.provider, d), {
|
|
1551
|
-
transient: d.transient
|
|
1552
|
-
});
|
|
1796
|
+
},
|
|
1797
|
+
mistral: {
|
|
1798
|
+
"mistral-large-latest": {
|
|
1799
|
+
prompt_per_1k: 2e-3,
|
|
1800
|
+
completion_per_1k: 6e-3,
|
|
1801
|
+
cached_per_1k: 2e-4
|
|
1802
|
+
},
|
|
1803
|
+
"mistral-small-latest": {
|
|
1804
|
+
prompt_per_1k: 2e-4,
|
|
1805
|
+
completion_per_1k: 6e-4,
|
|
1806
|
+
cached_per_1k: 2e-5
|
|
1553
1807
|
}
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
model: args.model,
|
|
1566
|
-
purpose: args.purpose ?? "worker"
|
|
1567
|
-
});
|
|
1568
|
-
return { text, attempts: 1, usage: applyPricing3(usage, args.pricing) };
|
|
1808
|
+
},
|
|
1809
|
+
groq: {
|
|
1810
|
+
"llama-3.3-70b-versatile": {
|
|
1811
|
+
prompt_per_1k: 59e-5,
|
|
1812
|
+
completion_per_1k: 79e-5,
|
|
1813
|
+
cached_per_1k: 295e-6
|
|
1814
|
+
},
|
|
1815
|
+
"llama-3.1-8b-instant": {
|
|
1816
|
+
prompt_per_1k: 5e-5,
|
|
1817
|
+
completion_per_1k: 8e-5,
|
|
1818
|
+
cached_per_1k: 25e-6
|
|
1569
1819
|
}
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
xai: "grok-4-latest",
|
|
1582
|
-
mistral: "mistral-large-latest",
|
|
1583
|
-
groq: "llama-3.3-70b-versatile",
|
|
1584
|
-
deepseek: "deepseek-chat",
|
|
1585
|
-
gemini: "gemini-3.8-flash",
|
|
1586
|
-
kimi: "kimi-k3",
|
|
1587
|
-
qwen: "qwen3.8-max"
|
|
1588
|
-
};
|
|
1589
|
-
var DEFAULT_FALLBACKS = {
|
|
1590
|
-
// gpt-5.6 requires a billing-enabled account and is excluded from the free
|
|
1591
|
-
// tier, so gpt-5 remains the broadly-available floor. One hop, not a
|
|
1592
|
-
// ladder — an intermediate 5.5 step would just be another model to reason
|
|
1593
|
-
// about.
|
|
1594
|
-
openai: ["gpt-5"],
|
|
1595
|
-
// 3.8 Flash is recent enough that some projects won't have it yet.
|
|
1596
|
-
gemini: ["gemini-3.1-pro-preview"],
|
|
1597
|
-
// Opus 5.5 is new enough that an account could still be waiting on it.
|
|
1598
|
-
// Opus 5 is the same family one step back — and this chain is consulted
|
|
1599
|
-
// ONLY for a model-access failure, never for a bad key or a rate limit,
|
|
1600
|
-
// so it can't quietly downgrade anyone who simply hit a 429.
|
|
1601
|
-
anthropic: ["claude-opus-5"]
|
|
1602
|
-
};
|
|
1603
|
-
var MODEL_ENV_VARS = {
|
|
1604
|
-
anthropic: "ANTHROPIC_MODEL",
|
|
1605
|
-
openai: "OPENAI_MODEL",
|
|
1606
|
-
xai: "XAI_MODEL",
|
|
1607
|
-
mistral: "MISTRAL_MODEL",
|
|
1608
|
-
groq: "GROQ_MODEL",
|
|
1609
|
-
deepseek: "DEEPSEEK_MODEL",
|
|
1610
|
-
gemini: "GEMINI_MODEL",
|
|
1611
|
-
kimi: "KIMI_MODEL",
|
|
1612
|
-
qwen: "QWEN_MODEL"
|
|
1613
|
-
};
|
|
1614
|
-
function parseFallbackModels(raw) {
|
|
1615
|
-
if (!raw) return [];
|
|
1616
|
-
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1617
|
-
}
|
|
1618
|
-
function fallbackModelsFor(env, provider) {
|
|
1619
|
-
const envVar = MODEL_ENV_VARS[provider];
|
|
1620
|
-
const fromEnv = envVar ? parseFallbackModels(env[`${envVar}_FALLBACKS`]) : [];
|
|
1621
|
-
if (fromEnv.length > 0) return fromEnv;
|
|
1622
|
-
const pinned = envVar ? env[envVar] : void 0;
|
|
1623
|
-
if (pinned && pinned !== DEFAULT_MODELS[provider]) return [];
|
|
1624
|
-
return DEFAULT_FALLBACKS[provider] ?? [];
|
|
1625
|
-
}
|
|
1626
|
-
var DEFAULT_EXCLUDED_PROVIDERS = ["mistral", "groq"];
|
|
1627
|
-
function excludedProviders(env) {
|
|
1628
|
-
const parse = (v) => (v ?? "").split(",").map((x) => x.trim().toLowerCase()).filter((x) => x !== "");
|
|
1629
|
-
const excluded = new Set(
|
|
1630
|
-
parse(env["CROSSCHECK_EXCLUDE_PROVIDERS"]).length > 0 ? parse(env["CROSSCHECK_EXCLUDE_PROVIDERS"]) : DEFAULT_EXCLUDED_PROVIDERS
|
|
1631
|
-
);
|
|
1632
|
-
for (const name of parse(env["CROSSCHECK_ENABLE_PROVIDERS"])) excluded.delete(name);
|
|
1633
|
-
return excluded;
|
|
1634
|
-
}
|
|
1635
|
-
function buildProviders(opts) {
|
|
1636
|
-
const out = {};
|
|
1637
|
-
const excluded = excludedProviders(opts.env);
|
|
1638
|
-
const anthropicKey = opts.env["ANTHROPIC_API_KEY"];
|
|
1639
|
-
if (anthropicKey && !excluded.has("anthropic")) {
|
|
1640
|
-
const model = opts.env["ANTHROPIC_MODEL"] ?? DEFAULT_MODELS.anthropic;
|
|
1641
|
-
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts, fallbackModelsFor(opts.env, "anthropic"));
|
|
1642
|
-
}
|
|
1643
|
-
const openAiCompatSpec = [
|
|
1644
|
-
{ name: "openai", keyEnv: "OPENAI_API_KEY", modelEnv: "OPENAI_MODEL", defaultModel: DEFAULT_MODELS["openai"] },
|
|
1645
|
-
{ name: "xai", keyEnv: "XAI_API_KEY", modelEnv: "XAI_MODEL", defaultModel: DEFAULT_MODELS["xai"] },
|
|
1646
|
-
{ name: "mistral", keyEnv: "MISTRAL_API_KEY", modelEnv: "MISTRAL_MODEL", defaultModel: DEFAULT_MODELS["mistral"] },
|
|
1647
|
-
{ name: "groq", keyEnv: "GROQ_API_KEY", modelEnv: "GROQ_MODEL", defaultModel: DEFAULT_MODELS["groq"] },
|
|
1648
|
-
{ name: "deepseek", keyEnv: "DEEPSEEK_API_KEY", modelEnv: "DEEPSEEK_MODEL", defaultModel: DEFAULT_MODELS["deepseek"] },
|
|
1649
|
-
{ name: "kimi", keyEnv: "KIMI_API_KEY", modelEnv: "KIMI_MODEL", defaultModel: DEFAULT_MODELS["kimi"] },
|
|
1650
|
-
{ name: "qwen", keyEnv: "QWEN_API_KEY", modelEnv: "QWEN_MODEL", defaultModel: DEFAULT_MODELS["qwen"] }
|
|
1651
|
-
];
|
|
1652
|
-
for (const s of openAiCompatSpec) {
|
|
1653
|
-
const apiKey = opts.env[s.keyEnv];
|
|
1654
|
-
if (!apiKey) continue;
|
|
1655
|
-
if (excluded.has(s.name)) continue;
|
|
1656
|
-
const model = opts.env[s.modelEnv] ?? s.defaultModel;
|
|
1657
|
-
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts, fallbackModelsFor(opts.env, s.name));
|
|
1658
|
-
}
|
|
1659
|
-
const geminiKey = opts.env["GEMINI_API_KEY"];
|
|
1660
|
-
if (geminiKey && !excluded.has("gemini")) {
|
|
1661
|
-
const model = opts.env["GEMINI_MODEL"] ?? DEFAULT_MODELS.gemini;
|
|
1662
|
-
out["gemini"] = makeGeminiProvider(model, geminiKey, opts, fallbackModelsFor(opts.env, "gemini"));
|
|
1663
|
-
}
|
|
1664
|
-
return out;
|
|
1665
|
-
}
|
|
1666
|
-
function makeAnthropicProvider(model, apiKey, opts, fallbackModels = []) {
|
|
1667
|
-
return {
|
|
1668
|
-
name: "anthropic",
|
|
1669
|
-
model,
|
|
1670
|
-
fallbackModels,
|
|
1671
|
-
send: async (args) => {
|
|
1672
|
-
const sendOpts = {
|
|
1673
|
-
...args,
|
|
1674
|
-
apiKey,
|
|
1675
|
-
model: args.modelOverride ?? model,
|
|
1676
|
-
pricing: opts.pricing
|
|
1677
|
-
};
|
|
1678
|
-
if (opts.fetchImpl) sendOpts.fetchImpl = opts.fetchImpl;
|
|
1679
|
-
return sendAnthropic(sendOpts);
|
|
1820
|
+
},
|
|
1821
|
+
deepseek: {
|
|
1822
|
+
"deepseek-chat": {
|
|
1823
|
+
prompt_per_1k: 27e-5,
|
|
1824
|
+
completion_per_1k: 11e-4,
|
|
1825
|
+
cached_per_1k: 7e-5
|
|
1826
|
+
},
|
|
1827
|
+
"deepseek-reasoner": {
|
|
1828
|
+
prompt_per_1k: 55e-5,
|
|
1829
|
+
completion_per_1k: 22e-4,
|
|
1830
|
+
cached_per_1k: 14e-5
|
|
1680
1831
|
}
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
model,
|
|
1688
|
-
fallbackModels,
|
|
1689
|
-
send: async (args) => {
|
|
1690
|
-
const sendOpts = {
|
|
1691
|
-
...args,
|
|
1692
|
-
provider: name,
|
|
1693
|
-
apiKey,
|
|
1694
|
-
model: args.modelOverride ?? model,
|
|
1695
|
-
url,
|
|
1696
|
-
pricing: opts.pricing
|
|
1697
|
-
};
|
|
1698
|
-
if (opts.fetchImpl) sendOpts.fetchImpl = opts.fetchImpl;
|
|
1699
|
-
return sendOpenAICompatible(sendOpts);
|
|
1832
|
+
},
|
|
1833
|
+
kimi: {
|
|
1834
|
+
"kimi-k3": {
|
|
1835
|
+
prompt_per_1k: 3e-3,
|
|
1836
|
+
completion_per_1k: 0.015,
|
|
1837
|
+
cached_per_1k: 3e-4
|
|
1700
1838
|
}
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1839
|
+
},
|
|
1840
|
+
qwen: {
|
|
1841
|
+
"qwen3.8-max": {
|
|
1842
|
+
prompt_per_1k: 2e-3,
|
|
1843
|
+
completion_per_1k: 6e-3,
|
|
1844
|
+
cached_per_1k: 2e-4
|
|
1845
|
+
},
|
|
1846
|
+
"qwen3.7-max": {
|
|
1847
|
+
prompt_per_1k: 125e-5,
|
|
1848
|
+
completion_per_1k: 375e-5,
|
|
1849
|
+
cached_per_1k: 125e-6
|
|
1850
|
+
}
|
|
1851
|
+
},
|
|
1852
|
+
_tiers: {
|
|
1853
|
+
low: {
|
|
1854
|
+
description: "Cheap, fast models for simple subtasks (extraction, formatting, short summaries).",
|
|
1855
|
+
models: [
|
|
1856
|
+
{
|
|
1857
|
+
provider: "openai",
|
|
1858
|
+
model: "gpt-4o-mini"
|
|
1859
|
+
},
|
|
1860
|
+
{
|
|
1861
|
+
provider: "openai",
|
|
1862
|
+
model: "gpt-5-mini"
|
|
1863
|
+
},
|
|
1864
|
+
{
|
|
1865
|
+
provider: "anthropic",
|
|
1866
|
+
model: "claude-haiku-4-5"
|
|
1867
|
+
},
|
|
1868
|
+
{
|
|
1869
|
+
provider: "anthropic",
|
|
1870
|
+
model: "claude-3-5-haiku"
|
|
1871
|
+
},
|
|
1872
|
+
{
|
|
1873
|
+
provider: "gemini",
|
|
1874
|
+
model: "gemini-3.5-flash"
|
|
1875
|
+
},
|
|
1876
|
+
{
|
|
1877
|
+
provider: "gemini",
|
|
1878
|
+
model: "gemini-2.5-flash"
|
|
1879
|
+
},
|
|
1880
|
+
{
|
|
1881
|
+
provider: "gemini",
|
|
1882
|
+
model: "gemini-2.5-flash-lite"
|
|
1883
|
+
},
|
|
1884
|
+
{
|
|
1885
|
+
provider: "xai",
|
|
1886
|
+
model: "grok-3-mini"
|
|
1887
|
+
},
|
|
1888
|
+
{
|
|
1889
|
+
provider: "groq",
|
|
1890
|
+
model: "llama-3.1-8b-instant"
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
provider: "deepseek",
|
|
1894
|
+
model: "deepseek-chat"
|
|
1895
|
+
}
|
|
1896
|
+
]
|
|
1897
|
+
},
|
|
1898
|
+
med: {
|
|
1899
|
+
description: "Mid-tier models for reasoning, code, multi-step thinking.",
|
|
1900
|
+
models: [
|
|
1901
|
+
{
|
|
1902
|
+
provider: "anthropic",
|
|
1903
|
+
model: "claude-sonnet-4-5"
|
|
1904
|
+
},
|
|
1905
|
+
{
|
|
1906
|
+
provider: "anthropic",
|
|
1907
|
+
model: "claude-sonnet-4-6"
|
|
1908
|
+
},
|
|
1909
|
+
{
|
|
1910
|
+
provider: "openai",
|
|
1911
|
+
model: "gpt-4o"
|
|
1912
|
+
},
|
|
1913
|
+
{
|
|
1914
|
+
provider: "openai",
|
|
1915
|
+
model: "o3-mini"
|
|
1916
|
+
},
|
|
1917
|
+
{
|
|
1918
|
+
provider: "gemini",
|
|
1919
|
+
model: "gemini-3.1-pro-preview"
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
provider: "xai",
|
|
1923
|
+
model: "grok-3"
|
|
1924
|
+
},
|
|
1925
|
+
{
|
|
1926
|
+
provider: "mistral",
|
|
1927
|
+
model: "mistral-large-latest"
|
|
1928
|
+
},
|
|
1929
|
+
{
|
|
1930
|
+
provider: "deepseek",
|
|
1931
|
+
model: "deepseek-reasoner"
|
|
1932
|
+
},
|
|
1933
|
+
{
|
|
1934
|
+
provider: "groq",
|
|
1935
|
+
model: "llama-3.3-70b-versatile"
|
|
1936
|
+
}
|
|
1937
|
+
]
|
|
1938
|
+
},
|
|
1939
|
+
high: {
|
|
1940
|
+
description: "Most capable models for hard reasoning, planning, audits.",
|
|
1941
|
+
models: [
|
|
1942
|
+
{
|
|
1943
|
+
provider: "anthropic",
|
|
1944
|
+
model: "claude-opus-4-5"
|
|
1945
|
+
},
|
|
1946
|
+
{
|
|
1947
|
+
provider: "anthropic",
|
|
1948
|
+
model: "claude-opus-4-7"
|
|
1949
|
+
},
|
|
1950
|
+
{
|
|
1951
|
+
provider: "anthropic",
|
|
1952
|
+
model: "claude-fable-5"
|
|
1953
|
+
},
|
|
1954
|
+
{
|
|
1955
|
+
provider: "openai",
|
|
1956
|
+
model: "gpt-5.5"
|
|
1957
|
+
},
|
|
1958
|
+
{
|
|
1959
|
+
provider: "openai",
|
|
1960
|
+
model: "gpt-5"
|
|
1961
|
+
},
|
|
1962
|
+
{
|
|
1963
|
+
provider: "openai",
|
|
1964
|
+
model: "o1"
|
|
1965
|
+
},
|
|
1966
|
+
{
|
|
1967
|
+
provider: "openai",
|
|
1968
|
+
model: "o3"
|
|
1969
|
+
},
|
|
1970
|
+
{
|
|
1971
|
+
provider: "xai",
|
|
1972
|
+
model: "grok-4-latest"
|
|
1973
|
+
}
|
|
1974
|
+
]
|
|
1717
1975
|
}
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1976
|
+
}
|
|
1977
|
+
};
|
|
1720
1978
|
|
|
1721
1979
|
// src/tools/index.ts
|
|
1722
1980
|
var import_zod = require("zod");
|
|
1723
1981
|
|
|
1724
1982
|
// src/server-meta.ts
|
|
1725
1983
|
var SERVER_NAME = "crosscheck-agent";
|
|
1726
|
-
var SERVER_VERSION = true ? "0.2.
|
|
1984
|
+
var SERVER_VERSION = true ? "0.2.31" : "0.0.0-dev";
|
|
1727
1985
|
|
|
1728
1986
|
// src/tools/audit.ts
|
|
1729
1987
|
var import_node_fs4 = require("fs");
|
|
@@ -2406,33 +2664,6 @@ function operatorCeiling(purpose, provider) {
|
|
|
2406
2664
|
return null;
|
|
2407
2665
|
}
|
|
2408
2666
|
|
|
2409
|
-
// src/core/retarget.ts
|
|
2410
|
-
function retargetProvider(p, newModel, fallbackModels) {
|
|
2411
|
-
const chain = fallbackModels && fallbackModels.length > 0 ? fallbackModels : void 0;
|
|
2412
|
-
if (p.model === newModel && chain === void 0) return p;
|
|
2413
|
-
return {
|
|
2414
|
-
name: p.name,
|
|
2415
|
-
model: newModel,
|
|
2416
|
-
...chain ? { fallbackModels: chain } : {},
|
|
2417
|
-
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
2418
|
-
};
|
|
2419
|
-
}
|
|
2420
|
-
async function loadProviderWeights(storage, names) {
|
|
2421
|
-
const out = {};
|
|
2422
|
-
for (const raw of names) {
|
|
2423
|
-
const name = raw.toLowerCase();
|
|
2424
|
-
if (name in out) continue;
|
|
2425
|
-
const row = await storage.getProviderStats(name);
|
|
2426
|
-
if (!row) {
|
|
2427
|
-
out[name] = 0.5;
|
|
2428
|
-
continue;
|
|
2429
|
-
}
|
|
2430
|
-
const total = row.wins + row.losses + row.abstains;
|
|
2431
|
-
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
2432
|
-
}
|
|
2433
|
-
return out;
|
|
2434
|
-
}
|
|
2435
|
-
|
|
2436
2667
|
// src/core/model-fallback.ts
|
|
2437
2668
|
async function sendWithModelFallback(provider, args) {
|
|
2438
2669
|
const chain = [provider.model, ...provider.fallbackModels ?? []];
|
|
@@ -3499,82 +3730,6 @@ function selectForDifficulty(args) {
|
|
|
3499
3730
|
};
|
|
3500
3731
|
}
|
|
3501
3732
|
|
|
3502
|
-
// src/core/reasoning-upgrade.ts
|
|
3503
|
-
var UPGRADE_MODEL = "claude-fable-5";
|
|
3504
|
-
var UPGRADE_LABEL = "Fable 5";
|
|
3505
|
-
function isUpgradeRequested(args) {
|
|
3506
|
-
if (args["upgrade"] === true) return true;
|
|
3507
|
-
const reasoning = args["reasoning"];
|
|
3508
|
-
if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
|
|
3509
|
-
return true;
|
|
3510
|
-
}
|
|
3511
|
-
const model = args["model"];
|
|
3512
|
-
if (typeof model === "string") {
|
|
3513
|
-
const m = model.trim().toLowerCase();
|
|
3514
|
-
if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
|
|
3515
|
-
return true;
|
|
3516
|
-
}
|
|
3517
|
-
}
|
|
3518
|
-
return false;
|
|
3519
|
-
}
|
|
3520
|
-
var HEAVY_TERMS = [
|
|
3521
|
-
/\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
|
|
3522
|
-
/\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
|
|
3523
|
-
/\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
|
|
3524
|
-
/\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
|
|
3525
|
-
/\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
|
|
3526
|
-
/\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
|
|
3527
|
-
/\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
|
|
3528
|
-
/\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
|
|
3529
|
-
/\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
|
|
3530
|
-
/\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
|
|
3531
|
-
/\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
|
|
3532
|
-
/\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
|
|
3533
|
-
];
|
|
3534
|
-
var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
|
|
3535
|
-
function assessReasoning(text) {
|
|
3536
|
-
const s = typeof text === "string" ? text : "";
|
|
3537
|
-
const signals = [];
|
|
3538
|
-
let score = 0;
|
|
3539
|
-
let termHits = 0;
|
|
3540
|
-
for (const re of HEAVY_TERMS) {
|
|
3541
|
-
if (re.test(s)) termHits += 1;
|
|
3542
|
-
}
|
|
3543
|
-
if (termHits > 0) {
|
|
3544
|
-
const add = Math.min(termHits, 4);
|
|
3545
|
-
score += add;
|
|
3546
|
-
signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
|
|
3547
|
-
}
|
|
3548
|
-
if (DIFFICULTY_TERMS.test(s)) {
|
|
3549
|
-
score += 1;
|
|
3550
|
-
signals.push("explicit difficulty marker");
|
|
3551
|
-
}
|
|
3552
|
-
const words = s.split(/\s+/).filter(Boolean).length;
|
|
3553
|
-
if (words >= 120) {
|
|
3554
|
-
score += 2;
|
|
3555
|
-
signals.push("long, detailed prompt");
|
|
3556
|
-
} else if (words >= 50) {
|
|
3557
|
-
score += 1;
|
|
3558
|
-
signals.push("substantial prompt");
|
|
3559
|
-
}
|
|
3560
|
-
const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
|
|
3561
|
-
if (constraints >= 3) {
|
|
3562
|
-
score += 1;
|
|
3563
|
-
signals.push(`${constraints} constraints/steps`);
|
|
3564
|
-
}
|
|
3565
|
-
const heavy = score >= 3;
|
|
3566
|
-
const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
|
|
3567
|
-
return { heavy, score, reason, signals };
|
|
3568
|
-
}
|
|
3569
|
-
function buildSuggestedUpgrade(toolName, assessment) {
|
|
3570
|
-
return {
|
|
3571
|
-
model: UPGRADE_MODEL,
|
|
3572
|
-
label: UPGRADE_LABEL,
|
|
3573
|
-
reason: assessment.reason,
|
|
3574
|
-
how: `re-run \`${toolName}\` with reasoning:"max" (or upgrade:true) to route the lead reasoning seat to ${UPGRADE_LABEL}, and \u2014 when an OpenAI key is configured \u2014 add an OpenAI second-opinion pass that ${UPGRADE_LABEL} weighs before finalizing (the result names which model ran). Other panel seats stay on their normal models.`
|
|
3575
|
-
};
|
|
3576
|
-
}
|
|
3577
|
-
|
|
3578
3733
|
// src/tools/audit.ts
|
|
3579
3734
|
var AUDIT_OBVIOUS_FAILURE_HIGH = 0.3;
|
|
3580
3735
|
var AUDIT_OBVIOUS_FAILURE_MED = 0.2;
|
|
@@ -3872,9 +4027,14 @@ ${rubricText}`;
|
|
|
3872
4027
|
);
|
|
3873
4028
|
}
|
|
3874
4029
|
const upgradeRequested = isUpgradeRequested(args);
|
|
3875
|
-
const
|
|
3876
|
-
const
|
|
3877
|
-
const
|
|
4030
|
+
const seat = synthesisSeat({ moderator: auditor, pinned: false, superRequested: false, upgradeRequested });
|
|
4031
|
+
const upgraded = seat.upgraded;
|
|
4032
|
+
const auditorForCall = seat.provider;
|
|
4033
|
+
const coProvider = pickCoReasoner(
|
|
4034
|
+
opts.providers,
|
|
4035
|
+
upgradeRequested,
|
|
4036
|
+
auditorForCall.name === "openai" ? auditorForCall.model : void 0
|
|
4037
|
+
);
|
|
3878
4038
|
const coReason = await coReasonThenInject({
|
|
3879
4039
|
provider: coProvider,
|
|
3880
4040
|
messages: msgs,
|
|
@@ -3957,15 +4117,16 @@ ${rubricText}`;
|
|
|
3957
4117
|
if (upgraded) {
|
|
3958
4118
|
result["reasoning_upgrade"] = {
|
|
3959
4119
|
applied: true,
|
|
3960
|
-
|
|
3961
|
-
|
|
4120
|
+
provider: auditorForCall.name,
|
|
4121
|
+
model: auditorForCall.model,
|
|
4122
|
+
label: seat.label ?? auditorForCall.model,
|
|
3962
4123
|
seat: "auditor",
|
|
3963
4124
|
...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
|
|
3964
4125
|
};
|
|
3965
4126
|
} else if (upgradeRequested) {
|
|
3966
4127
|
result["reasoning_upgrade"] = {
|
|
3967
4128
|
applied: false,
|
|
3968
|
-
reason:
|
|
4129
|
+
reason: seat.skipReason ?? "the auditor couldn't be upgraded."
|
|
3969
4130
|
};
|
|
3970
4131
|
} else {
|
|
3971
4132
|
const assessment = assessReasoning(
|
|
@@ -4048,7 +4209,7 @@ function pickAuditor(providers, exclude, explicit, moderatorName, allowlist, che
|
|
|
4048
4209
|
}
|
|
4049
4210
|
}
|
|
4050
4211
|
}
|
|
4051
|
-
const mod = providers
|
|
4212
|
+
const mod = resolveModerator(providers, moderatorName).provider;
|
|
4052
4213
|
if (mod !== void 0 && !exclude.has(mod.name) && (allowlist === null || allowlist.includes(mod.name))) {
|
|
4053
4214
|
return { auditor: mod, reason: null };
|
|
4054
4215
|
}
|
|
@@ -4367,9 +4528,7 @@ function pickPanelJudge(providers, moderatorName, pricing) {
|
|
|
4367
4528
|
return entry.model && entry.model !== base.model ? retargetProvider(base, entry.model) : base;
|
|
4368
4529
|
}
|
|
4369
4530
|
}
|
|
4370
|
-
|
|
4371
|
-
if (mod !== void 0) return mod;
|
|
4372
|
-
return null;
|
|
4531
|
+
return resolveModerator(providers, moderatorName).provider ?? null;
|
|
4373
4532
|
}
|
|
4374
4533
|
var AGREEMENT_CHECK_SCHEMA = {
|
|
4375
4534
|
type: "object",
|
|
@@ -4686,77 +4845,6 @@ async function pickAutoPanel(storage, purpose, n, providers, allowlist) {
|
|
|
4686
4845
|
return { providers: ranked.slice(0, Math.max(1, n)), coldStart: true };
|
|
4687
4846
|
}
|
|
4688
4847
|
|
|
4689
|
-
// src/core/super-mode.ts
|
|
4690
|
-
var SUPER_MODELS = {
|
|
4691
|
-
// Fable 5.1 verified present on the account; 5 as the fallback for accounts
|
|
4692
|
-
// that haven't been granted 5.1 yet.
|
|
4693
|
-
anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
|
|
4694
|
-
// Astra is live (verified against the account's model list 2026-09-23).
|
|
4695
|
-
// The fallback is named explicitly rather than reusing CO_REASON_MODEL:
|
|
4696
|
-
// that constant means "OpenAI's top reasoning model" for the max-reasoning
|
|
4697
|
-
// co-reasoner, which is a different question from "what should super fall
|
|
4698
|
-
// back to". Sharing the symbol silently coupled two unrelated decisions.
|
|
4699
|
-
openai: ["gpt-6-astra", "gpt-6-sol"],
|
|
4700
|
-
// Explicitly left as-is per product decision. Note grok-4-latest is not in
|
|
4701
|
-
// xAI's published model list, but it resolves in practice — real panel calls
|
|
4702
|
-
// on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
|
|
4703
|
-
// rather than a dead id.
|
|
4704
|
-
xai: [DEFAULT_MODELS["xai"]],
|
|
4705
|
-
// 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
|
|
4706
|
-
// so this can no longer borrow DEFAULT_MODELS as its fallback — that would
|
|
4707
|
-
// repeat the same model and retry one that just failed. Named explicitly.
|
|
4708
|
-
gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
|
|
4709
|
-
kimi: [DEFAULT_MODELS["kimi"]],
|
|
4710
|
-
qwen: [DEFAULT_MODELS["qwen"]]
|
|
4711
|
-
};
|
|
4712
|
-
var SUPER_ENV_VARS = {
|
|
4713
|
-
anthropic: "ANTHROPIC_SUPER_MODEL",
|
|
4714
|
-
openai: "OPENAI_SUPER_MODEL",
|
|
4715
|
-
xai: "XAI_SUPER_MODEL",
|
|
4716
|
-
gemini: "GEMINI_SUPER_MODEL",
|
|
4717
|
-
kimi: "KIMI_SUPER_MODEL",
|
|
4718
|
-
qwen: "QWEN_SUPER_MODEL",
|
|
4719
|
-
mistral: "MISTRAL_SUPER_MODEL",
|
|
4720
|
-
groq: "GROQ_SUPER_MODEL",
|
|
4721
|
-
deepseek: "DEEPSEEK_SUPER_MODEL"
|
|
4722
|
-
};
|
|
4723
|
-
function parseChain(v) {
|
|
4724
|
-
if (!v) return [];
|
|
4725
|
-
const out = [];
|
|
4726
|
-
for (const raw of v.split(",")) {
|
|
4727
|
-
const m = raw.trim();
|
|
4728
|
-
if (m && !out.includes(m)) out.push(m);
|
|
4729
|
-
}
|
|
4730
|
-
return out;
|
|
4731
|
-
}
|
|
4732
|
-
function superChainFor(provider, env = process.env) {
|
|
4733
|
-
const varName = SUPER_ENV_VARS[provider];
|
|
4734
|
-
const user = varName ? parseChain(env[varName]) : [];
|
|
4735
|
-
if (user.length > 0) {
|
|
4736
|
-
const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
|
|
4737
|
-
return [...user, ...fallbacks.filter((m) => !user.includes(m))];
|
|
4738
|
-
}
|
|
4739
|
-
return SUPER_MODELS[provider] ?? [];
|
|
4740
|
-
}
|
|
4741
|
-
function superPrimary(provider, env = process.env) {
|
|
4742
|
-
return superChainFor(provider, env)[0];
|
|
4743
|
-
}
|
|
4744
|
-
var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
|
|
4745
|
-
function isSuperRequested(args) {
|
|
4746
|
-
return args["super"] === true;
|
|
4747
|
-
}
|
|
4748
|
-
function retargetForSuper(p, env = process.env) {
|
|
4749
|
-
const chain = superChainFor(p.name, env);
|
|
4750
|
-
if (chain.length === 0) return p;
|
|
4751
|
-
const [primary, ...fallbacks] = chain;
|
|
4752
|
-
return retargetProvider(p, primary, fallbacks);
|
|
4753
|
-
}
|
|
4754
|
-
var SUPER_BUDGET_MULTIPLIER = 3;
|
|
4755
|
-
var SUPER_BUDGET_CEILING = 16e3;
|
|
4756
|
-
function superBudget(base) {
|
|
4757
|
-
return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
|
|
4758
|
-
}
|
|
4759
|
-
|
|
4760
4848
|
// src/core/session-memory.ts
|
|
4761
4849
|
var SESSION_MEMORY_DEFAULT_LIMIT = 50;
|
|
4762
4850
|
var SESSION_MEMORY_INJECT_BUDGET_CHARS = 4e3;
|
|
@@ -6816,9 +6904,9 @@ var import_node_path9 = __toESM(require("path"), 1);
|
|
|
6816
6904
|
var import_node_perf_hooks6 = require("perf_hooks");
|
|
6817
6905
|
|
|
6818
6906
|
// src/core/progress.ts
|
|
6819
|
-
var
|
|
6907
|
+
var import_node_async_hooks2 = require("async_hooks");
|
|
6820
6908
|
var import_node_perf_hooks5 = require("perf_hooks");
|
|
6821
|
-
var progressStorage = new
|
|
6909
|
+
var progressStorage = new import_node_async_hooks2.AsyncLocalStorage();
|
|
6822
6910
|
async function emitProgress(message, opts) {
|
|
6823
6911
|
const ctx = progressStorage.getStore();
|
|
6824
6912
|
const wallStart = ctx ? ctx.wallStart : import_node_perf_hooks5.performance.now();
|
|
@@ -7107,8 +7195,9 @@ async function runOrchestrate(args, opts) {
|
|
|
7107
7195
|
blocked
|
|
7108
7196
|
};
|
|
7109
7197
|
}
|
|
7110
|
-
const
|
|
7111
|
-
const moderator =
|
|
7198
|
+
const resolvedModerator = resolveModerator(opts.providers, args["moderator"], opts.moderator);
|
|
7199
|
+
const moderator = resolvedModerator.provider ?? selected[0];
|
|
7200
|
+
const moderatorChosen = resolvedModerator.provider !== void 0 && (resolvedModerator.pinned || parseModeratorSpec(args["moderator"]) !== null);
|
|
7112
7201
|
const failFast = boolArg2(args["fail_fast"], false);
|
|
7113
7202
|
const planOnly = boolArg2(args["plan_only"], false);
|
|
7114
7203
|
const cheapMode = boolArg2(args["cheap_mode"], opts.ctx?.cheap_mode ?? false);
|
|
@@ -7592,11 +7681,17 @@ ${recombineSys}` : recombineSys },
|
|
|
7592
7681
|
let synthProvider = moderator;
|
|
7593
7682
|
let synthModel = null;
|
|
7594
7683
|
const upgradeRequested = isUpgradeRequested(args);
|
|
7595
|
-
const
|
|
7684
|
+
const seat = synthesisSeat({
|
|
7685
|
+
moderator,
|
|
7686
|
+
pinned: resolvedModerator.provider !== void 0 && resolvedModerator.pinned,
|
|
7687
|
+
superRequested: false,
|
|
7688
|
+
upgradeRequested
|
|
7689
|
+
});
|
|
7690
|
+
const upgraded = seat.upgraded;
|
|
7596
7691
|
if (upgraded) {
|
|
7597
|
-
synthProvider =
|
|
7598
|
-
synthModel =
|
|
7599
|
-
} else if (cheapMode && opts.pricing) {
|
|
7692
|
+
synthProvider = seat.provider;
|
|
7693
|
+
synthModel = `${seat.provider.name}/${seat.provider.model}`;
|
|
7694
|
+
} else if (cheapMode && opts.pricing && !moderatorChosen) {
|
|
7600
7695
|
const pick = selectForDifficulty({
|
|
7601
7696
|
pricing: opts.pricing,
|
|
7602
7697
|
tier: "med",
|
|
@@ -7622,7 +7717,11 @@ ${recombineSys}` : recombineSys },
|
|
|
7622
7717
|
provider: synthProvider.name
|
|
7623
7718
|
} }
|
|
7624
7719
|
);
|
|
7625
|
-
const coProvider = pickCoReasoner(
|
|
7720
|
+
const coProvider = pickCoReasoner(
|
|
7721
|
+
opts.providers,
|
|
7722
|
+
upgradeRequested,
|
|
7723
|
+
synthProvider.name === "openai" ? synthProvider.model : void 0
|
|
7724
|
+
);
|
|
7626
7725
|
const coReason = await coReasonThenInject({
|
|
7627
7726
|
provider: coProvider,
|
|
7628
7727
|
messages: recMsgs,
|
|
@@ -7672,7 +7771,8 @@ ${recombineSys}` : recombineSys },
|
|
|
7672
7771
|
missing,
|
|
7673
7772
|
partial,
|
|
7674
7773
|
fail_fast: failFast,
|
|
7675
|
-
cheap_mode: cheapMode
|
|
7774
|
+
cheap_mode: cheapMode,
|
|
7775
|
+
moderator: { provider: moderator.name, model: moderator.model }
|
|
7676
7776
|
};
|
|
7677
7777
|
if (personaMeta.used !== null) result["persona"] = personaMeta;
|
|
7678
7778
|
if (synthModel) result["synth_model"] = synthModel;
|
|
@@ -7680,15 +7780,16 @@ ${recombineSys}` : recombineSys },
|
|
|
7680
7780
|
if (upgraded) {
|
|
7681
7781
|
result["reasoning_upgrade"] = {
|
|
7682
7782
|
applied: true,
|
|
7683
|
-
|
|
7684
|
-
|
|
7783
|
+
provider: seat.provider.name,
|
|
7784
|
+
model: seat.provider.model,
|
|
7785
|
+
label: seat.label ?? seat.provider.model,
|
|
7685
7786
|
seat: "recombine/synthesis",
|
|
7686
7787
|
...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
|
|
7687
7788
|
};
|
|
7688
7789
|
} else if (upgradeRequested) {
|
|
7689
7790
|
result["reasoning_upgrade"] = {
|
|
7690
7791
|
applied: false,
|
|
7691
|
-
reason:
|
|
7792
|
+
reason: seat.skipReason ?? "the moderator couldn't be upgraded."
|
|
7692
7793
|
};
|
|
7693
7794
|
} else if (goal) {
|
|
7694
7795
|
const assessment = assessReasoning(goal);
|
|
@@ -8870,13 +8971,24 @@ async function runCoordinate(args, opts) {
|
|
|
8870
8971
|
};
|
|
8871
8972
|
}
|
|
8872
8973
|
const proposerName = typeof args["proposer"] === "string" && args["proposer"] ? args["proposer"] : selected[0].name;
|
|
8873
|
-
const
|
|
8974
|
+
const resolvedSynth = resolveModerator(
|
|
8975
|
+
opts.providers,
|
|
8976
|
+
args["synthesizer"],
|
|
8977
|
+
args["moderator"],
|
|
8978
|
+
opts.moderator
|
|
8979
|
+
);
|
|
8874
8980
|
const proposer = opts.providers[proposerName.toLowerCase()] ?? selected[0];
|
|
8875
|
-
const synth =
|
|
8981
|
+
const synth = resolvedSynth.provider ?? proposer;
|
|
8876
8982
|
const proposerForCall = superRequested ? retargetForSuper(proposer) : proposer;
|
|
8877
8983
|
const upgradeRequested = isUpgradeRequested(args) || superRequested;
|
|
8878
|
-
const
|
|
8879
|
-
|
|
8984
|
+
const seat = synthesisSeat({
|
|
8985
|
+
moderator: synth,
|
|
8986
|
+
pinned: resolvedSynth.provider !== void 0 && resolvedSynth.pinned,
|
|
8987
|
+
superRequested,
|
|
8988
|
+
upgradeRequested
|
|
8989
|
+
});
|
|
8990
|
+
const upgraded = seat.upgraded;
|
|
8991
|
+
const synthForCall = seat.provider;
|
|
8880
8992
|
let critics = [];
|
|
8881
8993
|
if (Array.isArray(args["critics"])) {
|
|
8882
8994
|
for (const n of args["critics"]) {
|
|
@@ -9027,7 +9139,11 @@ CRITIQUES:
|
|
|
9027
9139
|
${critiqueBlock}`
|
|
9028
9140
|
}
|
|
9029
9141
|
];
|
|
9030
|
-
const coProvider = pickCoReasoner(
|
|
9142
|
+
const coProvider = pickCoReasoner(
|
|
9143
|
+
opts.providers,
|
|
9144
|
+
upgradeRequested,
|
|
9145
|
+
synthForCall.name === "openai" ? synthForCall.model : void 0
|
|
9146
|
+
);
|
|
9031
9147
|
const coReason = await coReasonThenInject({
|
|
9032
9148
|
provider: coProvider,
|
|
9033
9149
|
messages: synthMessages,
|
|
@@ -9072,7 +9188,8 @@ ${critiqueBlock}`
|
|
|
9072
9188
|
critique_answers: scannedCritiques,
|
|
9073
9189
|
critique_structured: critiqueStructured,
|
|
9074
9190
|
synthesis_answer: synthAns,
|
|
9075
|
-
synthesis_structured: synthObj
|
|
9191
|
+
synthesis_structured: synthObj,
|
|
9192
|
+
moderator: { provider: synthForCall.name, model: synthForCall.model }
|
|
9076
9193
|
};
|
|
9077
9194
|
if (synthErrs.length > 0) result["synthesis_errors"] = synthErrs;
|
|
9078
9195
|
if (canaryLeaks.length > 0) result["canary_leaks"] = canaryLeaks;
|
|
@@ -9090,15 +9207,16 @@ ${critiqueBlock}`
|
|
|
9090
9207
|
result["reasoning_upgrade"] = {
|
|
9091
9208
|
applied: true,
|
|
9092
9209
|
// The model that actually ran the seat, not the one we'd have picked.
|
|
9093
|
-
|
|
9094
|
-
|
|
9210
|
+
provider: synthForCall.name,
|
|
9211
|
+
model: synthForCall.model,
|
|
9212
|
+
label: seat.label ?? synthForCall.model,
|
|
9095
9213
|
seat: "synthesizer",
|
|
9096
9214
|
...coReasonAns ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
|
|
9097
9215
|
};
|
|
9098
9216
|
} else if (upgradeRequested) {
|
|
9099
9217
|
result["reasoning_upgrade"] = {
|
|
9100
9218
|
applied: false,
|
|
9101
|
-
reason:
|
|
9219
|
+
reason: seat.skipReason ?? "the synthesizer couldn't be upgraded."
|
|
9102
9220
|
};
|
|
9103
9221
|
} else {
|
|
9104
9222
|
const assessment = assessReasoning(topic);
|
|
@@ -9704,10 +9822,12 @@ async function runDebate(args, opts) {
|
|
|
9704
9822
|
const earlyStopThreshold = Number.isFinite(earlyStopThresholdRaw) ? earlyStopThresholdRaw : 0.7;
|
|
9705
9823
|
let earlyStoppedRound = null;
|
|
9706
9824
|
let agreementBlock = null;
|
|
9707
|
-
const
|
|
9708
|
-
const
|
|
9825
|
+
const resolvedModerator = resolveModerator(opts.providers, args["moderator"], opts.moderator);
|
|
9826
|
+
const moderatorName = resolvedModerator.spec.provider;
|
|
9827
|
+
const moderator = resolvedModerator.provider ?? selected[0];
|
|
9709
9828
|
const upgradeRequested = isUpgradeRequested(args) || superRequested;
|
|
9710
|
-
const
|
|
9829
|
+
const seat = moderator ? synthesisSeat({ moderator, pinned: resolvedModerator.pinned, superRequested, upgradeRequested }) : null;
|
|
9830
|
+
const upgraded = seat?.upgraded ?? false;
|
|
9711
9831
|
for (let rnd = 1; rnd <= maxRounds; rnd++) {
|
|
9712
9832
|
const roundMessages = [
|
|
9713
9833
|
{
|
|
@@ -9792,8 +9912,10 @@ ${prior.body}` });
|
|
|
9792
9912
|
let personaMeta = null;
|
|
9793
9913
|
let coReasoned = false;
|
|
9794
9914
|
let coReasonerModel = null;
|
|
9915
|
+
let moderatorUsed = null;
|
|
9795
9916
|
if (moderator) {
|
|
9796
|
-
const synthProvider =
|
|
9917
|
+
const synthProvider = seat?.provider ?? moderator;
|
|
9918
|
+
moderatorUsed = { provider: synthProvider.name, model: synthProvider.model };
|
|
9797
9919
|
const condensed = buildModeratorTranscript(transcript);
|
|
9798
9920
|
const persona = buildPersonaInjection({
|
|
9799
9921
|
toolName: "debate",
|
|
@@ -9819,7 +9941,7 @@ TRANSCRIPT:
|
|
|
9819
9941
|
${condensed}`
|
|
9820
9942
|
}
|
|
9821
9943
|
];
|
|
9822
|
-
const openAiSeat = selected.find((p) => p.name === "openai")?.model;
|
|
9944
|
+
const openAiSeat = synthProvider.name === "openai" ? synthProvider.model : selected.find((p) => p.name === "openai")?.model;
|
|
9823
9945
|
const coProvider = pickCoReasoner(opts.providers, upgradeRequested, openAiSeat);
|
|
9824
9946
|
const coReason = await coReasonThenInject({
|
|
9825
9947
|
provider: coProvider,
|
|
@@ -9879,20 +10001,22 @@ ${condensed}`
|
|
|
9879
10001
|
transcript,
|
|
9880
10002
|
synthesis
|
|
9881
10003
|
};
|
|
10004
|
+
if (moderatorUsed) result["moderator"] = moderatorUsed;
|
|
9882
10005
|
if (personaMeta && personaMeta.used !== null) result["persona"] = personaMeta;
|
|
9883
10006
|
if (compactionMeta) result["round_compaction"] = compactionMeta;
|
|
9884
10007
|
if (upgraded) {
|
|
9885
10008
|
result["reasoning_upgrade"] = {
|
|
9886
10009
|
applied: true,
|
|
9887
|
-
|
|
9888
|
-
|
|
10010
|
+
provider: seat.provider.name,
|
|
10011
|
+
model: seat.provider.model,
|
|
10012
|
+
label: seat.label ?? seat.provider.model,
|
|
9889
10013
|
seat: "moderator synthesis",
|
|
9890
10014
|
...coReasoned && coReasonerModel ? { co_reasoner: { model: coReasonerModel, label: coReasonLabel(coReasonerModel) } } : {}
|
|
9891
10015
|
};
|
|
9892
10016
|
} else if (upgradeRequested) {
|
|
9893
10017
|
result["reasoning_upgrade"] = {
|
|
9894
10018
|
applied: false,
|
|
9895
|
-
reason:
|
|
10019
|
+
reason: seat?.skipReason ?? "no moderator was available for the upgrade."
|
|
9896
10020
|
};
|
|
9897
10021
|
} else {
|
|
9898
10022
|
const assessment = assessReasoning(topic);
|
|
@@ -10521,12 +10645,25 @@ function runListProviders(args, opts) {
|
|
|
10521
10645
|
}
|
|
10522
10646
|
const result = {
|
|
10523
10647
|
providers,
|
|
10524
|
-
moderator_default: opts.moderatorDefault ??
|
|
10648
|
+
moderator_default: opts.moderatorDefault ?? DEFAULT_MODERATOR,
|
|
10649
|
+
// Who will actually moderate right now: the configured spec if that
|
|
10650
|
+
// provider has a key, else the first available (what the tools fall to).
|
|
10651
|
+
moderator: describeModerator(opts.providers ?? {}, opts.moderatorDefault),
|
|
10525
10652
|
usage_hint: USAGE_HINT
|
|
10526
10653
|
};
|
|
10527
10654
|
if (superRequested) result["super_mode"] = true;
|
|
10528
10655
|
return result;
|
|
10529
10656
|
}
|
|
10657
|
+
function describeModerator(providers, spec) {
|
|
10658
|
+
const r = resolveModerator(providers, spec);
|
|
10659
|
+
const p = r.provider ?? Object.values(providers)[0];
|
|
10660
|
+
return {
|
|
10661
|
+
spec: spec ?? DEFAULT_MODERATOR,
|
|
10662
|
+
provider: p?.name ?? null,
|
|
10663
|
+
model: p?.model ?? null,
|
|
10664
|
+
available: r.provider !== void 0
|
|
10665
|
+
};
|
|
10666
|
+
}
|
|
10530
10667
|
|
|
10531
10668
|
// src/tools/models.ts
|
|
10532
10669
|
var KNOWN_PROVIDERS7 = [
|
|
@@ -12076,7 +12213,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
|
|
|
12076
12213
|
var DEFAULT_PACKAGE = "crosscheck-cli";
|
|
12077
12214
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
12078
12215
|
function engineVersion() {
|
|
12079
|
-
return true ? "0.2.
|
|
12216
|
+
return true ? "0.2.31" : "0.0.0-dev";
|
|
12080
12217
|
}
|
|
12081
12218
|
function defaultUpdateCachePath() {
|
|
12082
12219
|
const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path12.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");
|
|
@@ -12521,7 +12658,7 @@ var HELP_CATALOG = [
|
|
|
12521
12658
|
primaryArg: "topic",
|
|
12522
12659
|
summary: "Bounded multi-round debate between models, ending in a synthesized recommendation.",
|
|
12523
12660
|
example: "xc debate should we adopt a monorepo?",
|
|
12524
|
-
detail: `The panel argues across a few rounds \u2014 each model sees the others' prior turns, concedes or pushes back \u2014 then a moderator synthesizes a single grounded recommendation.
|
|
12661
|
+
detail: `The panel argues across a few rounds \u2014 each model sees the others' prior turns, concedes or pushes back \u2014 then a moderator synthesizes a single grounded recommendation. Any model can moderate \u2014 pass moderator:"openai" or an exact model (moderator:"openai:gpt-6-sol"), or set a default with \`crosscheck moderator set\`. reasoning:"max" bumps the moderator's synthesis to its provider's flagship (Fable 5 for Anthropic). Great for contentious or trade-off-heavy calls.`
|
|
12525
12662
|
},
|
|
12526
12663
|
{
|
|
12527
12664
|
name: "coordinate",
|
|
@@ -12764,6 +12901,10 @@ function renderHelpCommand(entry) {
|
|
|
12764
12901
|
}
|
|
12765
12902
|
|
|
12766
12903
|
// src/tools/index.ts
|
|
12904
|
+
var MODERATOR_PARAM = {
|
|
12905
|
+
type: "string",
|
|
12906
|
+
description: 'Who moderates (synthesises) this run: a provider ("openai") or an exact model ("openai:gpt-6-sol"). Any configured provider works. Defaults to the configured moderator (CROSSCHECK_MODERATOR / config `moderator`).'
|
|
12907
|
+
};
|
|
12767
12908
|
function defineTool(opts) {
|
|
12768
12909
|
return {
|
|
12769
12910
|
name: opts.name,
|
|
@@ -12998,7 +13139,7 @@ function createTool(o, toolName, cheapDefault) {
|
|
|
12998
13139
|
instruction: { type: "string" },
|
|
12999
13140
|
documents: { type: "array", items: { type: "string" } },
|
|
13000
13141
|
providers: { type: "array", items: { type: "string" } },
|
|
13001
|
-
moderator:
|
|
13142
|
+
moderator: MODERATOR_PARAM,
|
|
13002
13143
|
constraints: { type: "string" },
|
|
13003
13144
|
target_path: { type: "string" },
|
|
13004
13145
|
session_id: { type: "string" },
|
|
@@ -13039,7 +13180,7 @@ function orchestrateTool(providers, allowlist, moderator, storage, pricing, brea
|
|
|
13039
13180
|
dag: { type: "object", additionalProperties: true },
|
|
13040
13181
|
context: { type: "string" },
|
|
13041
13182
|
providers: { type: "array", items: { type: "string" } },
|
|
13042
|
-
moderator:
|
|
13183
|
+
moderator: MODERATOR_PARAM,
|
|
13043
13184
|
fail_fast: { type: "boolean" },
|
|
13044
13185
|
plan_only: { type: "boolean" },
|
|
13045
13186
|
cheap_mode: { type: "boolean" },
|
|
@@ -13418,7 +13559,7 @@ function planTool(providers, allowlist, storage, breakers, transcriptsDir, repoR
|
|
|
13418
13559
|
constraints: { type: "string" },
|
|
13419
13560
|
context: { type: "string" },
|
|
13420
13561
|
providers: { type: "array", items: { type: "string" } },
|
|
13421
|
-
moderator:
|
|
13562
|
+
moderator: MODERATOR_PARAM,
|
|
13422
13563
|
session_id: { type: "string" },
|
|
13423
13564
|
structured: { type: "boolean" },
|
|
13424
13565
|
mode: { type: "string", enum: ["fast", "thorough"] },
|
|
@@ -13480,7 +13621,7 @@ function coordinateTool(providers, allowlist, innerCallers, storage, breakers, t
|
|
|
13480
13621
|
providers: { type: "array", items: { type: "string" } },
|
|
13481
13622
|
proposer: { type: "string" },
|
|
13482
13623
|
synthesizer: { type: "string" },
|
|
13483
|
-
moderator:
|
|
13624
|
+
moderator: MODERATOR_PARAM,
|
|
13484
13625
|
critics: { type: "array", items: { type: "string" } },
|
|
13485
13626
|
session_id: { type: "string" },
|
|
13486
13627
|
untrusted_input: { type: "boolean" },
|
|
@@ -13513,7 +13654,7 @@ function debateTool(providers, allowlist, innerCallers, storage, pricing, breake
|
|
|
13513
13654
|
context: { type: "string" },
|
|
13514
13655
|
max_rounds: { type: "integer", minimum: 1 },
|
|
13515
13656
|
providers: { type: "array", items: { type: "string" } },
|
|
13516
|
-
moderator:
|
|
13657
|
+
moderator: MODERATOR_PARAM,
|
|
13517
13658
|
session_id: { type: "string" },
|
|
13518
13659
|
reasoning: { type: "string", enum: ["default", "max"], description: '"max" bumps the moderator-synthesis seat to Fable 5 (premium thinking model); the debaters stay on their normal models. Omit for the Opus 4.8 default.' },
|
|
13519
13660
|
structured: { type: "boolean" },
|
|
@@ -13730,6 +13871,7 @@ var KEY_ENV = {
|
|
|
13730
13871
|
qwen: "QWEN_API_KEY"
|
|
13731
13872
|
};
|
|
13732
13873
|
function createCrosscheck(opts) {
|
|
13874
|
+
const moderator = opts.moderator ?? DEFAULT_MODERATOR;
|
|
13733
13875
|
const env = {};
|
|
13734
13876
|
for (const [provider, key] of Object.entries(opts.keys)) {
|
|
13735
13877
|
const varName = KEY_ENV[provider.trim().toLowerCase()];
|
|
@@ -13737,7 +13879,7 @@ function createCrosscheck(opts) {
|
|
|
13737
13879
|
}
|
|
13738
13880
|
const pricingDoc = pricing_default;
|
|
13739
13881
|
const providers = buildProviders({ env, pricing: pricingDoc });
|
|
13740
|
-
const tools = registerCoreTools({ providers, pricing: pricingDoc });
|
|
13882
|
+
const tools = registerCoreTools({ providers, pricing: pricingDoc, moderatorDefault: moderator });
|
|
13741
13883
|
return {
|
|
13742
13884
|
listTools: () => [...tools.keys()],
|
|
13743
13885
|
call: (name, args) => {
|
|
@@ -13750,11 +13892,11 @@ function createCrosscheck(opts) {
|
|
|
13750
13892
|
// same opts the registry wires for confer in a browser build: providers
|
|
13751
13893
|
// + pricing + default moderator; storage/innerCallers aren't
|
|
13752
13894
|
// available here, matching the non-streaming path.
|
|
13753
|
-
conferStream: (args, onAnswer) => runConfer(args, { providers, pricing: pricingDoc, moderator
|
|
13895
|
+
conferStream: (args, onAnswer) => runConfer(args, { providers, pricing: pricingDoc, moderator, onAnswer }),
|
|
13754
13896
|
debateStream: (args, { onTurn, onSynthesis } = {}) => runDebate(args, {
|
|
13755
13897
|
providers,
|
|
13756
13898
|
pricing: pricingDoc,
|
|
13757
|
-
moderator
|
|
13899
|
+
moderator,
|
|
13758
13900
|
...onTurn ? { onTurn: (entry, round, provider) => onTurn(entry, round, provider) } : {},
|
|
13759
13901
|
...onSynthesis ? { onSynthesis: (s) => onSynthesis(s) } : {}
|
|
13760
13902
|
})
|