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.js
CHANGED
|
@@ -6,426 +6,35 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
6
6
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
completion_per_1k: 0.025,
|
|
30
|
-
cached_per_1k: 5e-4
|
|
31
|
-
},
|
|
32
|
-
"claude-opus-4-8": {
|
|
33
|
-
prompt_per_1k: 5e-3,
|
|
34
|
-
completion_per_1k: 0.025,
|
|
35
|
-
cached_per_1k: 5e-4
|
|
36
|
-
},
|
|
37
|
-
"claude-opus-5-5": {
|
|
38
|
-
prompt_per_1k: 4e-3,
|
|
39
|
-
completion_per_1k: 0.02,
|
|
40
|
-
cached_per_1k: 2e-4
|
|
41
|
-
},
|
|
42
|
-
"claude-opus-5": {
|
|
43
|
-
prompt_per_1k: 5e-3,
|
|
44
|
-
completion_per_1k: 0.025,
|
|
45
|
-
cached_per_1k: 5e-4
|
|
46
|
-
},
|
|
47
|
-
"claude-fable-5": {
|
|
48
|
-
prompt_per_1k: 0.01,
|
|
49
|
-
completion_per_1k: 0.05,
|
|
50
|
-
cached_per_1k: 1e-3
|
|
51
|
-
},
|
|
52
|
-
"claude-sonnet-4-5": {
|
|
53
|
-
prompt_per_1k: 3e-3,
|
|
54
|
-
completion_per_1k: 0.015,
|
|
55
|
-
cached_per_1k: 3e-4
|
|
56
|
-
},
|
|
57
|
-
"claude-sonnet-4-6": {
|
|
58
|
-
prompt_per_1k: 3e-3,
|
|
59
|
-
completion_per_1k: 0.015,
|
|
60
|
-
cached_per_1k: 3e-4
|
|
61
|
-
},
|
|
62
|
-
"claude-haiku-4-5": {
|
|
63
|
-
prompt_per_1k: 1e-3,
|
|
64
|
-
completion_per_1k: 5e-3,
|
|
65
|
-
cached_per_1k: 1e-4
|
|
66
|
-
},
|
|
67
|
-
"claude-3-5-sonnet": {
|
|
68
|
-
prompt_per_1k: 3e-3,
|
|
69
|
-
completion_per_1k: 0.015,
|
|
70
|
-
cached_per_1k: 3e-4
|
|
71
|
-
},
|
|
72
|
-
"claude-3-5-haiku": {
|
|
73
|
-
prompt_per_1k: 8e-4,
|
|
74
|
-
completion_per_1k: 4e-3,
|
|
75
|
-
cached_per_1k: 8e-5
|
|
76
|
-
},
|
|
77
|
-
"claude-fable-5-1": {
|
|
78
|
-
prompt_per_1k: 0.01,
|
|
79
|
-
completion_per_1k: 0.05,
|
|
80
|
-
cached_per_1k: 25e-5
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
openai: {
|
|
84
|
-
_source: {
|
|
85
|
-
url: "https://developers.openai.com/api/docs/pricing",
|
|
86
|
-
verified_at: "2026-09-27"
|
|
87
|
-
},
|
|
88
|
-
"gpt-6-astra": {
|
|
89
|
-
prompt_per_1k: 0.01,
|
|
90
|
-
completion_per_1k: 0.05,
|
|
91
|
-
cached_per_1k: 1e-3
|
|
92
|
-
},
|
|
93
|
-
"gpt-6-sol": {
|
|
94
|
-
prompt_per_1k: 2e-3,
|
|
95
|
-
completion_per_1k: 0.01,
|
|
96
|
-
cached_per_1k: 2e-4
|
|
97
|
-
},
|
|
98
|
-
"gpt-6-luna": {
|
|
99
|
-
prompt_per_1k: 1e-4,
|
|
100
|
-
completion_per_1k: 5e-4,
|
|
101
|
-
cached_per_1k: 1e-5
|
|
102
|
-
},
|
|
103
|
-
"gpt-5.6-sol": {
|
|
104
|
-
prompt_per_1k: 4e-3,
|
|
105
|
-
completion_per_1k: 0.02,
|
|
106
|
-
cached_per_1k: 4e-4
|
|
107
|
-
},
|
|
108
|
-
"gpt-5.6-terra": {
|
|
109
|
-
prompt_per_1k: 2e-3,
|
|
110
|
-
completion_per_1k: 0.012,
|
|
111
|
-
cached_per_1k: 2e-4
|
|
112
|
-
},
|
|
113
|
-
"gpt-5.6-luna": {
|
|
114
|
-
prompt_per_1k: 2e-4,
|
|
115
|
-
completion_per_1k: 12e-4,
|
|
116
|
-
cached_per_1k: 2e-5
|
|
117
|
-
},
|
|
118
|
-
"gpt-5.6": {
|
|
119
|
-
prompt_per_1k: 4e-3,
|
|
120
|
-
completion_per_1k: 0.02,
|
|
121
|
-
cached_per_1k: 4e-4
|
|
122
|
-
},
|
|
123
|
-
"gpt-5.5": {
|
|
124
|
-
prompt_per_1k: 5e-3,
|
|
125
|
-
completion_per_1k: 0.03,
|
|
126
|
-
cached_per_1k: 5e-4
|
|
127
|
-
},
|
|
128
|
-
"gpt-5": {
|
|
129
|
-
prompt_per_1k: 125e-5,
|
|
130
|
-
completion_per_1k: 0.01,
|
|
131
|
-
cached_per_1k: 125e-6
|
|
132
|
-
},
|
|
133
|
-
"gpt-5-mini": {
|
|
134
|
-
prompt_per_1k: 25e-5,
|
|
135
|
-
completion_per_1k: 2e-3,
|
|
136
|
-
cached_per_1k: 25e-6
|
|
137
|
-
},
|
|
138
|
-
"gpt-5-nano": {
|
|
139
|
-
prompt_per_1k: 5e-5,
|
|
140
|
-
completion_per_1k: 4e-4,
|
|
141
|
-
cached_per_1k: 5e-6
|
|
142
|
-
},
|
|
143
|
-
"gpt-4o": {
|
|
144
|
-
prompt_per_1k: 25e-4,
|
|
145
|
-
completion_per_1k: 0.01,
|
|
146
|
-
cached_per_1k: 125e-5
|
|
147
|
-
},
|
|
148
|
-
"gpt-4o-mini": {
|
|
149
|
-
prompt_per_1k: 15e-5,
|
|
150
|
-
completion_per_1k: 6e-4,
|
|
151
|
-
cached_per_1k: 75e-6
|
|
152
|
-
},
|
|
153
|
-
o1: {
|
|
154
|
-
prompt_per_1k: 0.015,
|
|
155
|
-
completion_per_1k: 0.06,
|
|
156
|
-
cached_per_1k: 75e-4
|
|
157
|
-
},
|
|
158
|
-
"o1-mini": {
|
|
159
|
-
prompt_per_1k: 3e-3,
|
|
160
|
-
completion_per_1k: 0.012,
|
|
161
|
-
cached_per_1k: 15e-4
|
|
162
|
-
},
|
|
163
|
-
o3: {
|
|
164
|
-
prompt_per_1k: 2e-3,
|
|
165
|
-
completion_per_1k: 8e-3,
|
|
166
|
-
cached_per_1k: 5e-4
|
|
167
|
-
},
|
|
168
|
-
"o3-mini": {
|
|
169
|
-
prompt_per_1k: 11e-4,
|
|
170
|
-
completion_per_1k: 44e-4,
|
|
171
|
-
cached_per_1k: 55e-5
|
|
172
|
-
},
|
|
173
|
-
"gpt-5-pro": {
|
|
174
|
-
prompt_per_1k: 0.015,
|
|
175
|
-
completion_per_1k: 0.12,
|
|
176
|
-
cached_per_1k: 0
|
|
177
|
-
},
|
|
178
|
-
"gpt-5.2-pro": {
|
|
179
|
-
prompt_per_1k: 0.021,
|
|
180
|
-
completion_per_1k: 0.168,
|
|
181
|
-
cached_per_1k: 0
|
|
182
|
-
},
|
|
183
|
-
"gpt-5.4-pro": {
|
|
184
|
-
prompt_per_1k: 0.03,
|
|
185
|
-
completion_per_1k: 0.18,
|
|
186
|
-
cached_per_1k: 0
|
|
187
|
-
},
|
|
188
|
-
"gpt-5.5-pro": {
|
|
189
|
-
prompt_per_1k: 0.03,
|
|
190
|
-
completion_per_1k: 0.18,
|
|
191
|
-
cached_per_1k: 0
|
|
192
|
-
},
|
|
193
|
-
"o1-pro": {
|
|
194
|
-
prompt_per_1k: 0.15,
|
|
195
|
-
completion_per_1k: 0.6,
|
|
196
|
-
cached_per_1k: 0
|
|
197
|
-
},
|
|
198
|
-
"o3-pro": {
|
|
199
|
-
prompt_per_1k: 0.02,
|
|
200
|
-
completion_per_1k: 0.08,
|
|
201
|
-
cached_per_1k: 0
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
xai: {
|
|
205
|
-
"grok-4-latest": {
|
|
206
|
-
prompt_per_1k: 3e-3,
|
|
207
|
-
completion_per_1k: 0.015,
|
|
208
|
-
cached_per_1k: 75e-5
|
|
209
|
-
},
|
|
210
|
-
"grok-3": {
|
|
211
|
-
prompt_per_1k: 3e-3,
|
|
212
|
-
completion_per_1k: 0.015,
|
|
213
|
-
cached_per_1k: 15e-4
|
|
214
|
-
},
|
|
215
|
-
"grok-3-mini": {
|
|
216
|
-
prompt_per_1k: 3e-4,
|
|
217
|
-
completion_per_1k: 5e-4,
|
|
218
|
-
cached_per_1k: 15e-5
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
gemini: {
|
|
222
|
-
"gemini-3.1-pro-preview": {
|
|
223
|
-
prompt_per_1k: 2e-3,
|
|
224
|
-
completion_per_1k: 0.012,
|
|
225
|
-
cached_per_1k: 2e-4
|
|
226
|
-
},
|
|
227
|
-
"gemini-3.5-flash": {
|
|
228
|
-
prompt_per_1k: 15e-4,
|
|
229
|
-
completion_per_1k: 9e-3,
|
|
230
|
-
cached_per_1k: 15e-5
|
|
231
|
-
},
|
|
232
|
-
"gemini-2.5-flash": {
|
|
233
|
-
prompt_per_1k: 3e-4,
|
|
234
|
-
completion_per_1k: 25e-4,
|
|
235
|
-
cached_per_1k: 75e-6
|
|
236
|
-
},
|
|
237
|
-
"gemini-2.5-flash-lite": {
|
|
238
|
-
prompt_per_1k: 1e-4,
|
|
239
|
-
completion_per_1k: 4e-4,
|
|
240
|
-
cached_per_1k: 25e-6
|
|
241
|
-
},
|
|
242
|
-
"gemini-3.8-flash": {
|
|
243
|
-
prompt_per_1k: 75e-5,
|
|
244
|
-
completion_per_1k: 375e-5,
|
|
245
|
-
cached_per_1k: 75e-6
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
mistral: {
|
|
249
|
-
"mistral-large-latest": {
|
|
250
|
-
prompt_per_1k: 2e-3,
|
|
251
|
-
completion_per_1k: 6e-3,
|
|
252
|
-
cached_per_1k: 2e-4
|
|
253
|
-
},
|
|
254
|
-
"mistral-small-latest": {
|
|
255
|
-
prompt_per_1k: 2e-4,
|
|
256
|
-
completion_per_1k: 6e-4,
|
|
257
|
-
cached_per_1k: 2e-5
|
|
258
|
-
}
|
|
259
|
-
},
|
|
260
|
-
groq: {
|
|
261
|
-
"llama-3.3-70b-versatile": {
|
|
262
|
-
prompt_per_1k: 59e-5,
|
|
263
|
-
completion_per_1k: 79e-5,
|
|
264
|
-
cached_per_1k: 295e-6
|
|
265
|
-
},
|
|
266
|
-
"llama-3.1-8b-instant": {
|
|
267
|
-
prompt_per_1k: 5e-5,
|
|
268
|
-
completion_per_1k: 8e-5,
|
|
269
|
-
cached_per_1k: 25e-6
|
|
270
|
-
}
|
|
271
|
-
},
|
|
272
|
-
deepseek: {
|
|
273
|
-
"deepseek-chat": {
|
|
274
|
-
prompt_per_1k: 27e-5,
|
|
275
|
-
completion_per_1k: 11e-4,
|
|
276
|
-
cached_per_1k: 7e-5
|
|
277
|
-
},
|
|
278
|
-
"deepseek-reasoner": {
|
|
279
|
-
prompt_per_1k: 55e-5,
|
|
280
|
-
completion_per_1k: 22e-4,
|
|
281
|
-
cached_per_1k: 14e-5
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
kimi: {
|
|
285
|
-
"kimi-k3": {
|
|
286
|
-
prompt_per_1k: 3e-3,
|
|
287
|
-
completion_per_1k: 0.015,
|
|
288
|
-
cached_per_1k: 3e-4
|
|
289
|
-
}
|
|
290
|
-
},
|
|
291
|
-
qwen: {
|
|
292
|
-
"qwen3.8-max": {
|
|
293
|
-
prompt_per_1k: 2e-3,
|
|
294
|
-
completion_per_1k: 6e-3,
|
|
295
|
-
cached_per_1k: 2e-4
|
|
296
|
-
},
|
|
297
|
-
"qwen3.7-max": {
|
|
298
|
-
prompt_per_1k: 125e-5,
|
|
299
|
-
completion_per_1k: 375e-5,
|
|
300
|
-
cached_per_1k: 125e-6
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
_tiers: {
|
|
304
|
-
low: {
|
|
305
|
-
description: "Cheap, fast models for simple subtasks (extraction, formatting, short summaries).",
|
|
306
|
-
models: [
|
|
307
|
-
{
|
|
308
|
-
provider: "openai",
|
|
309
|
-
model: "gpt-4o-mini"
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
provider: "openai",
|
|
313
|
-
model: "gpt-5-mini"
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
provider: "anthropic",
|
|
317
|
-
model: "claude-haiku-4-5"
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
provider: "anthropic",
|
|
321
|
-
model: "claude-3-5-haiku"
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
provider: "gemini",
|
|
325
|
-
model: "gemini-3.5-flash"
|
|
326
|
-
},
|
|
327
|
-
{
|
|
328
|
-
provider: "gemini",
|
|
329
|
-
model: "gemini-2.5-flash"
|
|
330
|
-
},
|
|
331
|
-
{
|
|
332
|
-
provider: "gemini",
|
|
333
|
-
model: "gemini-2.5-flash-lite"
|
|
334
|
-
},
|
|
335
|
-
{
|
|
336
|
-
provider: "xai",
|
|
337
|
-
model: "grok-3-mini"
|
|
338
|
-
},
|
|
339
|
-
{
|
|
340
|
-
provider: "groq",
|
|
341
|
-
model: "llama-3.1-8b-instant"
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
provider: "deepseek",
|
|
345
|
-
model: "deepseek-chat"
|
|
346
|
-
}
|
|
347
|
-
]
|
|
348
|
-
},
|
|
349
|
-
med: {
|
|
350
|
-
description: "Mid-tier models for reasoning, code, multi-step thinking.",
|
|
351
|
-
models: [
|
|
352
|
-
{
|
|
353
|
-
provider: "anthropic",
|
|
354
|
-
model: "claude-sonnet-4-5"
|
|
355
|
-
},
|
|
356
|
-
{
|
|
357
|
-
provider: "anthropic",
|
|
358
|
-
model: "claude-sonnet-4-6"
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
provider: "openai",
|
|
362
|
-
model: "gpt-4o"
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
provider: "openai",
|
|
366
|
-
model: "o3-mini"
|
|
367
|
-
},
|
|
368
|
-
{
|
|
369
|
-
provider: "gemini",
|
|
370
|
-
model: "gemini-3.1-pro-preview"
|
|
371
|
-
},
|
|
372
|
-
{
|
|
373
|
-
provider: "xai",
|
|
374
|
-
model: "grok-3"
|
|
375
|
-
},
|
|
376
|
-
{
|
|
377
|
-
provider: "mistral",
|
|
378
|
-
model: "mistral-large-latest"
|
|
379
|
-
},
|
|
380
|
-
{
|
|
381
|
-
provider: "deepseek",
|
|
382
|
-
model: "deepseek-reasoner"
|
|
383
|
-
},
|
|
384
|
-
{
|
|
385
|
-
provider: "groq",
|
|
386
|
-
model: "llama-3.3-70b-versatile"
|
|
387
|
-
}
|
|
388
|
-
]
|
|
389
|
-
},
|
|
390
|
-
high: {
|
|
391
|
-
description: "Most capable models for hard reasoning, planning, audits.",
|
|
392
|
-
models: [
|
|
393
|
-
{
|
|
394
|
-
provider: "anthropic",
|
|
395
|
-
model: "claude-opus-4-5"
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
provider: "anthropic",
|
|
399
|
-
model: "claude-opus-4-7"
|
|
400
|
-
},
|
|
401
|
-
{
|
|
402
|
-
provider: "anthropic",
|
|
403
|
-
model: "claude-fable-5"
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
provider: "openai",
|
|
407
|
-
model: "gpt-5.5"
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
provider: "openai",
|
|
411
|
-
model: "gpt-5"
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
provider: "openai",
|
|
415
|
-
model: "o1"
|
|
416
|
-
},
|
|
417
|
-
{
|
|
418
|
-
provider: "openai",
|
|
419
|
-
model: "o3"
|
|
420
|
-
},
|
|
421
|
-
{
|
|
422
|
-
provider: "xai",
|
|
423
|
-
model: "grok-4-latest"
|
|
424
|
-
}
|
|
425
|
-
]
|
|
9
|
+
// src/core/retarget.ts
|
|
10
|
+
function retargetProvider(p, newModel, fallbackModels) {
|
|
11
|
+
const chain = fallbackModels && fallbackModels.length > 0 ? fallbackModels : void 0;
|
|
12
|
+
if (p.model === newModel && chain === void 0) return p;
|
|
13
|
+
return {
|
|
14
|
+
name: p.name,
|
|
15
|
+
model: newModel,
|
|
16
|
+
...chain ? { fallbackModels: chain } : {},
|
|
17
|
+
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
async function loadProviderWeights(storage, names) {
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const raw of names) {
|
|
23
|
+
const name = raw.toLowerCase();
|
|
24
|
+
if (name in out) continue;
|
|
25
|
+
const row = await storage.getProviderStats(name);
|
|
26
|
+
if (!row) {
|
|
27
|
+
out[name] = 0.5;
|
|
28
|
+
continue;
|
|
426
29
|
}
|
|
30
|
+
const total = row.wins + row.losses + row.abstains;
|
|
31
|
+
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
427
32
|
}
|
|
428
|
-
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/core/super-mode.ts
|
|
37
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
429
38
|
|
|
430
39
|
// src/core/pricing.ts
|
|
431
40
|
import { readFileSync } from "fs";
|
|
@@ -578,6 +187,11 @@ function reasoningSharesOutputBudget(provider) {
|
|
|
578
187
|
}
|
|
579
188
|
|
|
580
189
|
// src/providers/types.ts
|
|
190
|
+
var IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
|
|
191
|
+
function imagesOf(m) {
|
|
192
|
+
if (m.role !== "user" || !Array.isArray(m.images)) return [];
|
|
193
|
+
return m.images.filter((i) => Boolean(i) && typeof i.data === "string" && i.data.length > 0 && IMAGE_MEDIA_TYPES.includes(i.mediaType));
|
|
194
|
+
}
|
|
581
195
|
var ProviderError = class extends Error {
|
|
582
196
|
name = "ProviderError";
|
|
583
197
|
kind;
|
|
@@ -966,7 +580,12 @@ function buildAnthropicRequest(opts) {
|
|
|
966
580
|
}
|
|
967
581
|
continue;
|
|
968
582
|
}
|
|
969
|
-
|
|
583
|
+
const text = typeof m.content === "string" ? m.content : "";
|
|
584
|
+
const images = imagesOf(m);
|
|
585
|
+
convo.push(images.length ? { role: m.role, content: [
|
|
586
|
+
...images.map((i) => ({ type: "image", source: { type: "base64", media_type: i.mediaType, data: i.data } })),
|
|
587
|
+
{ type: "text", text }
|
|
588
|
+
] } : { role: m.role, content: text });
|
|
970
589
|
}
|
|
971
590
|
const body = {
|
|
972
591
|
model: opts.model,
|
|
@@ -1206,7 +825,10 @@ function buildGeminiRequest(opts) {
|
|
|
1206
825
|
const role = m.role === "user" ? "user" : "model";
|
|
1207
826
|
contents.push({
|
|
1208
827
|
role,
|
|
1209
|
-
parts: [
|
|
828
|
+
parts: [
|
|
829
|
+
...imagesOf(m).map((i) => ({ inline_data: { mime_type: i.mediaType, data: i.data } })),
|
|
830
|
+
{ text: typeof m.content === "string" ? m.content : "" }
|
|
831
|
+
]
|
|
1210
832
|
});
|
|
1211
833
|
}
|
|
1212
834
|
const body = {
|
|
@@ -1275,7 +897,192 @@ function parseGeminiResponse(opts) {
|
|
|
1275
897
|
);
|
|
1276
898
|
}
|
|
1277
899
|
}
|
|
1278
|
-
function applyPricing2(usage, pricing) {
|
|
900
|
+
function applyPricing2(usage, pricing) {
|
|
901
|
+
const { cost_usd, estimated } = calculateCost(
|
|
902
|
+
pricing,
|
|
903
|
+
usage.provider,
|
|
904
|
+
usage.model,
|
|
905
|
+
usage.prompt_tokens,
|
|
906
|
+
usage.completion_tokens,
|
|
907
|
+
usage.cached_tokens
|
|
908
|
+
);
|
|
909
|
+
return {
|
|
910
|
+
...usage,
|
|
911
|
+
cost_usd,
|
|
912
|
+
estimated: usage.estimated || estimated
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
async function sendGemini(args) {
|
|
916
|
+
const { url, headers, body } = buildGeminiRequest({
|
|
917
|
+
model: args.model,
|
|
918
|
+
apiKey: args.apiKey,
|
|
919
|
+
messages: args.messages,
|
|
920
|
+
maxTokens: args.maxTokens,
|
|
921
|
+
// args.temperature is deliberately NOT forwarded: Gemini ignores it and
|
|
922
|
+
// upcoming models reject it. See the header note.
|
|
923
|
+
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
924
|
+
});
|
|
925
|
+
const doFetch = args.fetchImpl ?? globalThis.fetch;
|
|
926
|
+
const init = {
|
|
927
|
+
method: "POST",
|
|
928
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
929
|
+
body: JSON.stringify(body)
|
|
930
|
+
};
|
|
931
|
+
if (args.signal) init.signal = args.signal;
|
|
932
|
+
const attemptOnce = async () => {
|
|
933
|
+
let respLike;
|
|
934
|
+
try {
|
|
935
|
+
respLike = await doFetch(url, init);
|
|
936
|
+
} catch (e) {
|
|
937
|
+
const d = diagnoseNetworkError(e);
|
|
938
|
+
if (!looksLikeNetworkFailure(d)) throw e;
|
|
939
|
+
throw new ProviderError(d.kind, networkErrorMessage("gemini", d), {
|
|
940
|
+
transient: d.transient
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
const status = respLike.status;
|
|
944
|
+
if (status >= 200 && status < 300) {
|
|
945
|
+
let parsed;
|
|
946
|
+
try {
|
|
947
|
+
parsed = await respLike.json();
|
|
948
|
+
} catch (e) {
|
|
949
|
+
throw new ProviderError("parse", `gemini: response body not JSON: ${e.message}`);
|
|
950
|
+
}
|
|
951
|
+
const { text, usage, finishReason } = parseGeminiResponse({
|
|
952
|
+
resp: parsed,
|
|
953
|
+
model: args.model,
|
|
954
|
+
purpose: args.purpose ?? "worker"
|
|
955
|
+
});
|
|
956
|
+
if (text.trim() === "" && finishReason === "MAX_TOKENS") {
|
|
957
|
+
throw new ProviderError(
|
|
958
|
+
"parse",
|
|
959
|
+
`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.`,
|
|
960
|
+
{ transient: false }
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
return { text, attempts: 1, usage: applyPricing2(usage, args.pricing) };
|
|
964
|
+
}
|
|
965
|
+
const bodyText = await respLike.text().catch(() => "");
|
|
966
|
+
throw httpFailureToProviderError("gemini", status, bodyText, parseRetryAfter(respLike), args.model);
|
|
967
|
+
};
|
|
968
|
+
await acquireRateLimit("gemini", args.signal ? { signal: args.signal } : void 0);
|
|
969
|
+
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// src/providers/openai-compatible.ts
|
|
973
|
+
var OPENAI_COMPAT_DEFAULT_URLS = {
|
|
974
|
+
openai: "https://api.openai.com/v1/chat/completions",
|
|
975
|
+
xai: "https://api.x.ai/v1/chat/completions",
|
|
976
|
+
mistral: "https://api.mistral.ai/v1/chat/completions",
|
|
977
|
+
groq: "https://api.groq.com/openai/v1/chat/completions",
|
|
978
|
+
deepseek: "https://api.deepseek.com/v1/chat/completions",
|
|
979
|
+
// Moonshot AI (Kimi). OpenAI-compatible chat completions endpoint.
|
|
980
|
+
kimi: "https://api.moonshot.ai/v1/chat/completions",
|
|
981
|
+
// Alibaba Cloud DashScope (Qwen). International endpoint — the mainland
|
|
982
|
+
// China endpoint (dashscope.aliyuncs.com) 403s/404s from outside China in
|
|
983
|
+
// a way that can look like a bad key; this is the deliberate default for
|
|
984
|
+
// a US-based deployment.
|
|
985
|
+
qwen: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions"
|
|
986
|
+
};
|
|
987
|
+
var OPENAI_COMPAT_NATIVE_STRUCTURED = /* @__PURE__ */ new Set([
|
|
988
|
+
"openai"
|
|
989
|
+
]);
|
|
990
|
+
function supportsNativeJsonSchema(provider, model) {
|
|
991
|
+
if (!OPENAI_COMPAT_NATIVE_STRUCTURED.has(provider.toLowerCase())) {
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
const m = model.toLowerCase();
|
|
995
|
+
if (/^o1(?:-|$)/.test(m)) return false;
|
|
996
|
+
return true;
|
|
997
|
+
}
|
|
998
|
+
function buildOpenAICompatibleRequest(opts) {
|
|
999
|
+
const msgs = opts.messages.map((m) => {
|
|
1000
|
+
const text = typeof m.content === "string" ? m.content : "";
|
|
1001
|
+
const images = imagesOf(m);
|
|
1002
|
+
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 };
|
|
1003
|
+
});
|
|
1004
|
+
const body = {
|
|
1005
|
+
model: opts.model,
|
|
1006
|
+
messages: msgs
|
|
1007
|
+
};
|
|
1008
|
+
if (supportsTemperature(opts.provider, opts.model)) {
|
|
1009
|
+
body.max_tokens = opts.maxTokens;
|
|
1010
|
+
body.temperature = opts.temperature;
|
|
1011
|
+
} else {
|
|
1012
|
+
body.max_completion_tokens = opts.maxTokens;
|
|
1013
|
+
}
|
|
1014
|
+
if (opts.jsonSchema && supportsNativeJsonSchema(opts.provider, opts.model)) {
|
|
1015
|
+
body.response_format = {
|
|
1016
|
+
type: "json_schema",
|
|
1017
|
+
json_schema: {
|
|
1018
|
+
name: "structured_output",
|
|
1019
|
+
schema: opts.jsonSchema,
|
|
1020
|
+
// NOT strict. Strict mode requires `additionalProperties: false` on
|
|
1021
|
+
// every object and every property listed in `required` — the
|
|
1022
|
+
// engine's schemas don't meet that, so every structured OpenAI call
|
|
1023
|
+
// (planner, judges, synthesis) was a 400, unnoticed until the canary
|
|
1024
|
+
// grew a structured check (2026-10-11). Non-strict still shapes the
|
|
1025
|
+
// reply to the schema, and structured.ts validates it against the
|
|
1026
|
+
// full schema regardless, so strictness added a failure mode and no
|
|
1027
|
+
// safety.
|
|
1028
|
+
strict: false
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
const headers = {
|
|
1033
|
+
"content-type": "application/json",
|
|
1034
|
+
Authorization: `Bearer ${opts.apiKey}`
|
|
1035
|
+
};
|
|
1036
|
+
return { url: opts.url, headers, body };
|
|
1037
|
+
}
|
|
1038
|
+
function parseOpenAICompatibleResponse(opts) {
|
|
1039
|
+
const r = opts.resp ?? {};
|
|
1040
|
+
let text;
|
|
1041
|
+
try {
|
|
1042
|
+
const choices = r["choices"];
|
|
1043
|
+
if (!Array.isArray(choices) || choices.length === 0) {
|
|
1044
|
+
throw new Error("choices missing or empty");
|
|
1045
|
+
}
|
|
1046
|
+
const first = choices[0];
|
|
1047
|
+
const message = first["message"];
|
|
1048
|
+
if (!message || typeof message["content"] !== "string") {
|
|
1049
|
+
throw new Error("message.content missing or not string");
|
|
1050
|
+
}
|
|
1051
|
+
text = message["content"];
|
|
1052
|
+
} catch (e) {
|
|
1053
|
+
throw new ProviderError(
|
|
1054
|
+
"parse",
|
|
1055
|
+
`${opts.provider}: unexpected response shape: ${JSON.stringify(r).slice(0, 200)}`
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
const u = r["usage"] ?? {};
|
|
1059
|
+
const details = u["prompt_tokens_details"] ?? {};
|
|
1060
|
+
const cached = Math.trunc(Number(details["cached_tokens"] ?? 0)) || 0;
|
|
1061
|
+
const promptTokens = Math.trunc(Number(u["prompt_tokens"] ?? 0)) || 0;
|
|
1062
|
+
const reportedCompletion = Math.trunc(Number(u["completion_tokens"] ?? 0)) || 0;
|
|
1063
|
+
const reportedTotal = Math.trunc(Number(u["total_tokens"] ?? 0)) || 0;
|
|
1064
|
+
const derivedCompletion = reportedTotal > promptTokens ? reportedTotal - promptTokens : reportedCompletion;
|
|
1065
|
+
const completionTokens = Math.max(reportedCompletion, derivedCompletion);
|
|
1066
|
+
const usage = {
|
|
1067
|
+
provider: opts.provider,
|
|
1068
|
+
model: opts.model,
|
|
1069
|
+
prompt_tokens: promptTokens,
|
|
1070
|
+
completion_tokens: completionTokens,
|
|
1071
|
+
cached_tokens: cached,
|
|
1072
|
+
// Use the response's reported total_tokens directly. Python's
|
|
1073
|
+
// `Usage.to_dict()` falls back to prompt+completion only when
|
|
1074
|
+
// total_tokens is 0/missing; mirror that.
|
|
1075
|
+
total_tokens: reportedTotal,
|
|
1076
|
+
cost_usd: 0,
|
|
1077
|
+
estimated: Object.keys(u).length === 0,
|
|
1078
|
+
purpose: opts.purpose
|
|
1079
|
+
};
|
|
1080
|
+
if (usage.total_tokens === 0) {
|
|
1081
|
+
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
|
|
1082
|
+
}
|
|
1083
|
+
return { text, usage };
|
|
1084
|
+
}
|
|
1085
|
+
function applyPricing3(usage, pricing) {
|
|
1279
1086
|
const { cost_usd, estimated } = calculateCost(
|
|
1280
1087
|
pricing,
|
|
1281
1088
|
usage.provider,
|
|
@@ -1290,20 +1097,36 @@ function applyPricing2(usage, pricing) {
|
|
|
1290
1097
|
estimated: usage.estimated || estimated
|
|
1291
1098
|
};
|
|
1292
1099
|
}
|
|
1293
|
-
async function
|
|
1294
|
-
const { url, headers, body } =
|
|
1100
|
+
async function sendOpenAICompatible(args) {
|
|
1101
|
+
const { url, headers, body } = buildOpenAICompatibleRequest({
|
|
1102
|
+
provider: args.provider,
|
|
1295
1103
|
model: args.model,
|
|
1296
1104
|
apiKey: args.apiKey,
|
|
1105
|
+
url: args.url,
|
|
1297
1106
|
messages: args.messages,
|
|
1298
1107
|
maxTokens: args.maxTokens,
|
|
1299
|
-
|
|
1300
|
-
// upcoming models reject it. See the header note.
|
|
1108
|
+
temperature: args.temperature,
|
|
1301
1109
|
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
1302
1110
|
});
|
|
1111
|
+
if (args.provider === "openai" && isReasoningModel("openai", args.model) && !/^o1(?:-|$)/i.test(args.model)) {
|
|
1112
|
+
const effort = (process.env["CROSSCHECK_OPENAI_REASONING_EFFORT"] ?? "low").toLowerCase();
|
|
1113
|
+
if (effort === "minimal" || effort === "low" || effort === "medium" || effort === "high") {
|
|
1114
|
+
body.reasoning_effort = effort;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
if (isReasoningModel(args.provider, args.model) && // Only where the cap is a SHARED budget. On a provider that bounds visible
|
|
1118
|
+
// output only (xAI), headroom cannot protect the answer from being crowded
|
|
1119
|
+
// out — nothing is crowding it — so it would merely authorise a
|
|
1120
|
+
// 25,000-token reply and the bill that comes with it.
|
|
1121
|
+
reasoningSharesOutputBudget(args.provider) && typeof body.max_completion_tokens === "number") {
|
|
1122
|
+
const raw = Number(process.env["CROSSCHECK_OPENAI_REASONING_HEADROOM_TOKENS"]);
|
|
1123
|
+
const headroom = Number.isFinite(raw) && raw >= 0 ? Math.trunc(raw) : 25e3;
|
|
1124
|
+
body.max_completion_tokens += headroom;
|
|
1125
|
+
}
|
|
1303
1126
|
const doFetch = args.fetchImpl ?? globalThis.fetch;
|
|
1304
1127
|
const init = {
|
|
1305
1128
|
method: "POST",
|
|
1306
|
-
headers
|
|
1129
|
+
headers,
|
|
1307
1130
|
body: JSON.stringify(body)
|
|
1308
1131
|
};
|
|
1309
1132
|
if (args.signal) init.signal = args.signal;
|
|
@@ -1314,7 +1137,7 @@ async function sendGemini(args) {
|
|
|
1314
1137
|
} catch (e) {
|
|
1315
1138
|
const d = diagnoseNetworkError(e);
|
|
1316
1139
|
if (!looksLikeNetworkFailure(d)) throw e;
|
|
1317
|
-
throw new ProviderError(d.kind, networkErrorMessage(
|
|
1140
|
+
throw new ProviderError(d.kind, networkErrorMessage(args.provider, d), {
|
|
1318
1141
|
transient: d.transient
|
|
1319
1142
|
});
|
|
1320
1143
|
}
|
|
@@ -1324,373 +1147,808 @@ async function sendGemini(args) {
|
|
|
1324
1147
|
try {
|
|
1325
1148
|
parsed = await respLike.json();
|
|
1326
1149
|
} catch (e) {
|
|
1327
|
-
throw new ProviderError("parse",
|
|
1150
|
+
throw new ProviderError("parse", `${args.provider}: response body not JSON: ${e.message}`);
|
|
1328
1151
|
}
|
|
1329
|
-
const { text, usage
|
|
1152
|
+
const { text, usage } = parseOpenAICompatibleResponse({
|
|
1330
1153
|
resp: parsed,
|
|
1154
|
+
provider: args.provider,
|
|
1331
1155
|
model: args.model,
|
|
1332
1156
|
purpose: args.purpose ?? "worker"
|
|
1333
1157
|
});
|
|
1334
|
-
|
|
1335
|
-
throw new ProviderError(
|
|
1336
|
-
"parse",
|
|
1337
|
-
`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.`,
|
|
1338
|
-
{ transient: false }
|
|
1339
|
-
);
|
|
1340
|
-
}
|
|
1341
|
-
return { text, attempts: 1, usage: applyPricing2(usage, args.pricing) };
|
|
1158
|
+
return { text, attempts: 1, usage: applyPricing3(usage, args.pricing) };
|
|
1342
1159
|
}
|
|
1343
1160
|
const bodyText = await respLike.text().catch(() => "");
|
|
1344
|
-
throw httpFailureToProviderError(
|
|
1161
|
+
throw httpFailureToProviderError(args.provider, status, bodyText, parseRetryAfter(respLike), args.model);
|
|
1162
|
+
};
|
|
1163
|
+
await acquireRateLimit(args.provider, args.signal ? { signal: args.signal } : void 0);
|
|
1164
|
+
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/providers/registry.ts
|
|
1168
|
+
var DEFAULT_MODELS = {
|
|
1169
|
+
anthropic: "claude-opus-5-5",
|
|
1170
|
+
openai: "gpt-6-sol",
|
|
1171
|
+
xai: "grok-4-latest",
|
|
1172
|
+
mistral: "mistral-large-latest",
|
|
1173
|
+
groq: "llama-3.3-70b-versatile",
|
|
1174
|
+
deepseek: "deepseek-chat",
|
|
1175
|
+
gemini: "gemini-3.8-flash",
|
|
1176
|
+
kimi: "kimi-k3",
|
|
1177
|
+
qwen: "qwen3.8-max"
|
|
1178
|
+
};
|
|
1179
|
+
var DEFAULT_FALLBACKS = {
|
|
1180
|
+
// gpt-5.6 requires a billing-enabled account and is excluded from the free
|
|
1181
|
+
// tier, so gpt-5 remains the broadly-available floor. One hop, not a
|
|
1182
|
+
// ladder — an intermediate 5.5 step would just be another model to reason
|
|
1183
|
+
// about.
|
|
1184
|
+
openai: ["gpt-5"],
|
|
1185
|
+
// 3.8 Flash is recent enough that some projects won't have it yet.
|
|
1186
|
+
gemini: ["gemini-3.1-pro-preview"],
|
|
1187
|
+
// Opus 5.5 is new enough that an account could still be waiting on it.
|
|
1188
|
+
// Opus 5 is the same family one step back — and this chain is consulted
|
|
1189
|
+
// ONLY for a model-access failure, never for a bad key or a rate limit,
|
|
1190
|
+
// so it can't quietly downgrade anyone who simply hit a 429.
|
|
1191
|
+
anthropic: ["claude-opus-5"]
|
|
1192
|
+
};
|
|
1193
|
+
var MODEL_ENV_VARS = {
|
|
1194
|
+
anthropic: "ANTHROPIC_MODEL",
|
|
1195
|
+
openai: "OPENAI_MODEL",
|
|
1196
|
+
xai: "XAI_MODEL",
|
|
1197
|
+
mistral: "MISTRAL_MODEL",
|
|
1198
|
+
groq: "GROQ_MODEL",
|
|
1199
|
+
deepseek: "DEEPSEEK_MODEL",
|
|
1200
|
+
gemini: "GEMINI_MODEL",
|
|
1201
|
+
kimi: "KIMI_MODEL",
|
|
1202
|
+
qwen: "QWEN_MODEL"
|
|
1203
|
+
};
|
|
1204
|
+
function parseFallbackModels(raw) {
|
|
1205
|
+
if (!raw) return [];
|
|
1206
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1207
|
+
}
|
|
1208
|
+
function fallbackModelsFor(env, provider) {
|
|
1209
|
+
const envVar = MODEL_ENV_VARS[provider];
|
|
1210
|
+
const fromEnv = envVar ? parseFallbackModels(env[`${envVar}_FALLBACKS`]) : [];
|
|
1211
|
+
if (fromEnv.length > 0) return fromEnv;
|
|
1212
|
+
const pinned = envVar ? env[envVar] : void 0;
|
|
1213
|
+
if (pinned && pinned !== DEFAULT_MODELS[provider]) return [];
|
|
1214
|
+
return DEFAULT_FALLBACKS[provider] ?? [];
|
|
1215
|
+
}
|
|
1216
|
+
var DEFAULT_EXCLUDED_PROVIDERS = ["mistral", "groq"];
|
|
1217
|
+
function excludedProviders(env) {
|
|
1218
|
+
const parse = (v) => (v ?? "").split(",").map((x) => x.trim().toLowerCase()).filter((x) => x !== "");
|
|
1219
|
+
const excluded = new Set(
|
|
1220
|
+
parse(env["CROSSCHECK_EXCLUDE_PROVIDERS"]).length > 0 ? parse(env["CROSSCHECK_EXCLUDE_PROVIDERS"]) : DEFAULT_EXCLUDED_PROVIDERS
|
|
1221
|
+
);
|
|
1222
|
+
for (const name of parse(env["CROSSCHECK_ENABLE_PROVIDERS"])) excluded.delete(name);
|
|
1223
|
+
return excluded;
|
|
1224
|
+
}
|
|
1225
|
+
function buildProviders(opts) {
|
|
1226
|
+
const out = {};
|
|
1227
|
+
const excluded = excludedProviders(opts.env);
|
|
1228
|
+
const anthropicKey = opts.env["ANTHROPIC_API_KEY"];
|
|
1229
|
+
if (anthropicKey && !excluded.has("anthropic")) {
|
|
1230
|
+
const model = opts.env["ANTHROPIC_MODEL"] ?? DEFAULT_MODELS.anthropic;
|
|
1231
|
+
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts, fallbackModelsFor(opts.env, "anthropic"));
|
|
1232
|
+
}
|
|
1233
|
+
const openAiCompatSpec = [
|
|
1234
|
+
{ name: "openai", keyEnv: "OPENAI_API_KEY", modelEnv: "OPENAI_MODEL", defaultModel: DEFAULT_MODELS["openai"] },
|
|
1235
|
+
{ name: "xai", keyEnv: "XAI_API_KEY", modelEnv: "XAI_MODEL", defaultModel: DEFAULT_MODELS["xai"] },
|
|
1236
|
+
{ name: "mistral", keyEnv: "MISTRAL_API_KEY", modelEnv: "MISTRAL_MODEL", defaultModel: DEFAULT_MODELS["mistral"] },
|
|
1237
|
+
{ name: "groq", keyEnv: "GROQ_API_KEY", modelEnv: "GROQ_MODEL", defaultModel: DEFAULT_MODELS["groq"] },
|
|
1238
|
+
{ name: "deepseek", keyEnv: "DEEPSEEK_API_KEY", modelEnv: "DEEPSEEK_MODEL", defaultModel: DEFAULT_MODELS["deepseek"] },
|
|
1239
|
+
{ name: "kimi", keyEnv: "KIMI_API_KEY", modelEnv: "KIMI_MODEL", defaultModel: DEFAULT_MODELS["kimi"] },
|
|
1240
|
+
{ name: "qwen", keyEnv: "QWEN_API_KEY", modelEnv: "QWEN_MODEL", defaultModel: DEFAULT_MODELS["qwen"] }
|
|
1241
|
+
];
|
|
1242
|
+
for (const s of openAiCompatSpec) {
|
|
1243
|
+
const apiKey = opts.env[s.keyEnv];
|
|
1244
|
+
if (!apiKey) continue;
|
|
1245
|
+
if (excluded.has(s.name)) continue;
|
|
1246
|
+
const model = opts.env[s.modelEnv] ?? s.defaultModel;
|
|
1247
|
+
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts, fallbackModelsFor(opts.env, s.name));
|
|
1248
|
+
}
|
|
1249
|
+
const geminiKey = opts.env["GEMINI_API_KEY"];
|
|
1250
|
+
if (geminiKey && !excluded.has("gemini")) {
|
|
1251
|
+
const model = opts.env["GEMINI_MODEL"] ?? DEFAULT_MODELS.gemini;
|
|
1252
|
+
out["gemini"] = makeGeminiProvider(model, geminiKey, opts, fallbackModelsFor(opts.env, "gemini"));
|
|
1253
|
+
}
|
|
1254
|
+
return out;
|
|
1255
|
+
}
|
|
1256
|
+
function makeAnthropicProvider(model, apiKey, opts, fallbackModels = []) {
|
|
1257
|
+
return {
|
|
1258
|
+
name: "anthropic",
|
|
1259
|
+
model,
|
|
1260
|
+
fallbackModels,
|
|
1261
|
+
send: async (args) => {
|
|
1262
|
+
const sendOpts = {
|
|
1263
|
+
...args,
|
|
1264
|
+
apiKey,
|
|
1265
|
+
model: args.modelOverride ?? model,
|
|
1266
|
+
pricing: opts.pricing
|
|
1267
|
+
};
|
|
1268
|
+
if (opts.fetchImpl) sendOpts.fetchImpl = opts.fetchImpl;
|
|
1269
|
+
return sendAnthropic(sendOpts);
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
function makeOpenAICompatibleProvider(name, model, apiKey, opts, fallbackModels = []) {
|
|
1274
|
+
const url = OPENAI_COMPAT_DEFAULT_URLS[name];
|
|
1275
|
+
return {
|
|
1276
|
+
name,
|
|
1277
|
+
model,
|
|
1278
|
+
fallbackModels,
|
|
1279
|
+
send: async (args) => {
|
|
1280
|
+
const sendOpts = {
|
|
1281
|
+
...args,
|
|
1282
|
+
provider: name,
|
|
1283
|
+
apiKey,
|
|
1284
|
+
model: args.modelOverride ?? model,
|
|
1285
|
+
url,
|
|
1286
|
+
pricing: opts.pricing
|
|
1287
|
+
};
|
|
1288
|
+
if (opts.fetchImpl) sendOpts.fetchImpl = opts.fetchImpl;
|
|
1289
|
+
return sendOpenAICompatible(sendOpts);
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
|
|
1294
|
+
return {
|
|
1295
|
+
name: "gemini",
|
|
1296
|
+
model,
|
|
1297
|
+
fallbackModels,
|
|
1298
|
+
send: async (args) => {
|
|
1299
|
+
const sendOpts = {
|
|
1300
|
+
...args,
|
|
1301
|
+
apiKey,
|
|
1302
|
+
model: args.modelOverride ?? model,
|
|
1303
|
+
pricing: opts.pricing
|
|
1304
|
+
};
|
|
1305
|
+
if (opts.fetchImpl) sendOpts.fetchImpl = opts.fetchImpl;
|
|
1306
|
+
return sendGemini(sendOpts);
|
|
1307
|
+
}
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/core/reasoning-upgrade.ts
|
|
1312
|
+
var UPGRADE_MODEL = "claude-fable-5";
|
|
1313
|
+
var UPGRADE_LABEL = "Fable 5";
|
|
1314
|
+
function isUpgradeRequested(args) {
|
|
1315
|
+
if (args["upgrade"] === true) return true;
|
|
1316
|
+
const reasoning = args["reasoning"];
|
|
1317
|
+
if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
|
|
1318
|
+
return true;
|
|
1319
|
+
}
|
|
1320
|
+
const model = args["model"];
|
|
1321
|
+
if (typeof model === "string") {
|
|
1322
|
+
const m = model.trim().toLowerCase();
|
|
1323
|
+
if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
|
|
1324
|
+
return true;
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
var HEAVY_TERMS = [
|
|
1330
|
+
/\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
|
|
1331
|
+
/\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
|
|
1332
|
+
/\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
|
|
1333
|
+
/\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
|
|
1334
|
+
/\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
|
|
1335
|
+
/\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
|
|
1336
|
+
/\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
|
|
1337
|
+
/\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
|
|
1338
|
+
/\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
|
|
1339
|
+
/\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
|
|
1340
|
+
/\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
|
|
1341
|
+
/\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
|
|
1342
|
+
];
|
|
1343
|
+
var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
|
|
1344
|
+
function assessReasoning(text) {
|
|
1345
|
+
const s = typeof text === "string" ? text : "";
|
|
1346
|
+
const signals = [];
|
|
1347
|
+
let score = 0;
|
|
1348
|
+
let termHits = 0;
|
|
1349
|
+
for (const re of HEAVY_TERMS) {
|
|
1350
|
+
if (re.test(s)) termHits += 1;
|
|
1351
|
+
}
|
|
1352
|
+
if (termHits > 0) {
|
|
1353
|
+
const add = Math.min(termHits, 4);
|
|
1354
|
+
score += add;
|
|
1355
|
+
signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
|
|
1356
|
+
}
|
|
1357
|
+
if (DIFFICULTY_TERMS.test(s)) {
|
|
1358
|
+
score += 1;
|
|
1359
|
+
signals.push("explicit difficulty marker");
|
|
1360
|
+
}
|
|
1361
|
+
const words = s.split(/\s+/).filter(Boolean).length;
|
|
1362
|
+
if (words >= 120) {
|
|
1363
|
+
score += 2;
|
|
1364
|
+
signals.push("long, detailed prompt");
|
|
1365
|
+
} else if (words >= 50) {
|
|
1366
|
+
score += 1;
|
|
1367
|
+
signals.push("substantial prompt");
|
|
1368
|
+
}
|
|
1369
|
+
const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
|
|
1370
|
+
if (constraints >= 3) {
|
|
1371
|
+
score += 1;
|
|
1372
|
+
signals.push(`${constraints} constraints/steps`);
|
|
1373
|
+
}
|
|
1374
|
+
const heavy = score >= 3;
|
|
1375
|
+
const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
|
|
1376
|
+
return { heavy, score, reason, signals };
|
|
1377
|
+
}
|
|
1378
|
+
function buildSuggestedUpgrade(toolName, assessment) {
|
|
1379
|
+
return {
|
|
1380
|
+
model: UPGRADE_MODEL,
|
|
1381
|
+
label: UPGRADE_LABEL,
|
|
1382
|
+
reason: assessment.reason,
|
|
1383
|
+
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.`
|
|
1345
1384
|
};
|
|
1346
|
-
await acquireRateLimit("gemini", args.signal ? { signal: args.signal } : void 0);
|
|
1347
|
-
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1348
1385
|
}
|
|
1349
1386
|
|
|
1350
|
-
// src/
|
|
1351
|
-
var
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
//
|
|
1358
|
-
|
|
1359
|
-
//
|
|
1360
|
-
|
|
1361
|
-
//
|
|
1362
|
-
//
|
|
1363
|
-
|
|
1387
|
+
// src/core/super-mode.ts
|
|
1388
|
+
var SUPER_MODELS = {
|
|
1389
|
+
// Fable 5.1 verified present on the account; 5 as the fallback for accounts
|
|
1390
|
+
// that haven't been granted 5.1 yet.
|
|
1391
|
+
anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
|
|
1392
|
+
// Astra is live (verified against the account's model list 2026-09-23).
|
|
1393
|
+
// The fallback is named explicitly rather than reusing CO_REASON_MODEL:
|
|
1394
|
+
// that constant means "OpenAI's top reasoning model" for the max-reasoning
|
|
1395
|
+
// co-reasoner, which is a different question from "what should super fall
|
|
1396
|
+
// back to". Sharing the symbol silently coupled two unrelated decisions.
|
|
1397
|
+
openai: ["gpt-6-astra", "gpt-6-sol"],
|
|
1398
|
+
// Explicitly left as-is per product decision. Note grok-4-latest is not in
|
|
1399
|
+
// xAI's published model list, but it resolves in practice — real panel calls
|
|
1400
|
+
// on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
|
|
1401
|
+
// rather than a dead id.
|
|
1402
|
+
xai: [DEFAULT_MODELS["xai"]],
|
|
1403
|
+
// 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
|
|
1404
|
+
// so this can no longer borrow DEFAULT_MODELS as its fallback — that would
|
|
1405
|
+
// repeat the same model and retry one that just failed. Named explicitly.
|
|
1406
|
+
gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
|
|
1407
|
+
kimi: [DEFAULT_MODELS["kimi"]],
|
|
1408
|
+
qwen: [DEFAULT_MODELS["qwen"]]
|
|
1364
1409
|
};
|
|
1365
|
-
var
|
|
1366
|
-
"
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1410
|
+
var SUPER_ENV_VARS = {
|
|
1411
|
+
anthropic: "ANTHROPIC_SUPER_MODEL",
|
|
1412
|
+
openai: "OPENAI_SUPER_MODEL",
|
|
1413
|
+
xai: "XAI_SUPER_MODEL",
|
|
1414
|
+
gemini: "GEMINI_SUPER_MODEL",
|
|
1415
|
+
kimi: "KIMI_SUPER_MODEL",
|
|
1416
|
+
qwen: "QWEN_SUPER_MODEL",
|
|
1417
|
+
mistral: "MISTRAL_SUPER_MODEL",
|
|
1418
|
+
groq: "GROQ_SUPER_MODEL",
|
|
1419
|
+
deepseek: "DEEPSEEK_SUPER_MODEL"
|
|
1420
|
+
};
|
|
1421
|
+
function parseChain(v) {
|
|
1422
|
+
if (!v) return [];
|
|
1423
|
+
const out = [];
|
|
1424
|
+
for (const raw of v.split(",")) {
|
|
1425
|
+
const m = raw.trim();
|
|
1426
|
+
if (m && !out.includes(m)) out.push(m);
|
|
1371
1427
|
}
|
|
1372
|
-
|
|
1373
|
-
if (/^o1(?:-|$)/.test(m)) return false;
|
|
1374
|
-
return true;
|
|
1428
|
+
return out;
|
|
1375
1429
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1430
|
+
var superEnvStore = new AsyncLocalStorage();
|
|
1431
|
+
var currentEnv = () => superEnvStore.getStore() ?? process.env;
|
|
1432
|
+
function superChainFor(provider, env = currentEnv()) {
|
|
1433
|
+
const varName = SUPER_ENV_VARS[provider];
|
|
1434
|
+
const user = varName ? parseChain(env[varName]) : [];
|
|
1435
|
+
if (user.length > 0) {
|
|
1436
|
+
const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
|
|
1437
|
+
return [...user, ...fallbacks.filter((m) => !user.includes(m))];
|
|
1438
|
+
}
|
|
1439
|
+
return SUPER_MODELS[provider] ?? [];
|
|
1440
|
+
}
|
|
1441
|
+
function superPrimary(provider, env = currentEnv()) {
|
|
1442
|
+
return superChainFor(provider, env)[0];
|
|
1443
|
+
}
|
|
1444
|
+
var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
|
|
1445
|
+
function isSuperRequested(args) {
|
|
1446
|
+
return args["super"] === true;
|
|
1447
|
+
}
|
|
1448
|
+
function retargetForSuper(p, env = currentEnv()) {
|
|
1449
|
+
const chain = superChainFor(p.name, env);
|
|
1450
|
+
if (chain.length === 0) return p;
|
|
1451
|
+
const [primary, ...fallbacks] = chain;
|
|
1452
|
+
return retargetProvider(p, primary, fallbacks);
|
|
1453
|
+
}
|
|
1454
|
+
var SUPER_BUDGET_MULTIPLIER = 3;
|
|
1455
|
+
var SUPER_BUDGET_CEILING = 16e3;
|
|
1456
|
+
function superBudget(base) {
|
|
1457
|
+
return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
// src/core/moderator.ts
|
|
1461
|
+
var DEFAULT_MODERATOR = "anthropic";
|
|
1462
|
+
var PROVIDER_ALIASES = {
|
|
1463
|
+
grok: "xai",
|
|
1464
|
+
claude: "anthropic",
|
|
1465
|
+
google: "gemini",
|
|
1466
|
+
chatgpt: "openai",
|
|
1467
|
+
gpt: "openai",
|
|
1468
|
+
moonshot: "kimi"
|
|
1469
|
+
};
|
|
1470
|
+
function parseModeratorSpec(raw) {
|
|
1471
|
+
if (typeof raw !== "string") return null;
|
|
1472
|
+
const s = raw.trim();
|
|
1473
|
+
if (!s) return null;
|
|
1474
|
+
const i = s.indexOf(":");
|
|
1475
|
+
const providerPart = (i < 0 ? s : s.slice(0, i)).trim().toLowerCase();
|
|
1476
|
+
const modelPart = i < 0 ? "" : s.slice(i + 1).trim();
|
|
1477
|
+
if (!providerPart) return null;
|
|
1478
|
+
return {
|
|
1479
|
+
provider: PROVIDER_ALIASES[providerPart] ?? providerPart,
|
|
1480
|
+
model: modelPart || null
|
|
1384
1481
|
};
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1482
|
+
}
|
|
1483
|
+
function pickModeratorSpec(...candidates) {
|
|
1484
|
+
for (const c of candidates) {
|
|
1485
|
+
const s = parseModeratorSpec(c);
|
|
1486
|
+
if (s) return s;
|
|
1487
|
+
}
|
|
1488
|
+
return { provider: DEFAULT_MODERATOR, model: null };
|
|
1489
|
+
}
|
|
1490
|
+
function resolveModerator(providers, ...candidates) {
|
|
1491
|
+
const spec = pickModeratorSpec(...candidates);
|
|
1492
|
+
const base = providers[spec.provider];
|
|
1493
|
+
if (!base) return { provider: void 0, spec, pinned: false };
|
|
1494
|
+
if (!spec.model) return { provider: base, spec, pinned: false };
|
|
1495
|
+
return { provider: retargetProvider(base, spec.model), spec, pinned: true };
|
|
1496
|
+
}
|
|
1497
|
+
function synthesisSeat(o) {
|
|
1498
|
+
const { moderator, pinned, superRequested, upgradeRequested } = o;
|
|
1499
|
+
const wantsMore = superRequested || upgradeRequested;
|
|
1500
|
+
if (!wantsMore) return { provider: moderator, upgraded: false, label: null, skipReason: null };
|
|
1501
|
+
if (pinned) {
|
|
1502
|
+
return {
|
|
1503
|
+
provider: moderator,
|
|
1504
|
+
upgraded: false,
|
|
1505
|
+
label: null,
|
|
1506
|
+
skipReason: `the moderator is pinned to ${moderator.name}:${moderator.model}; a pinned model is used as-is.`
|
|
1507
|
+
};
|
|
1390
1508
|
}
|
|
1391
|
-
if (
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
// (planner, judges, synthesis) was a 400, unnoticed until the canary
|
|
1401
|
-
// grew a structured check (2026-10-11). Non-strict still shapes the
|
|
1402
|
-
// reply to the schema, and structured.ts validates it against the
|
|
1403
|
-
// full schema regardless, so strictness added a failure mode and no
|
|
1404
|
-
// safety.
|
|
1405
|
-
strict: false
|
|
1406
|
-
}
|
|
1509
|
+
if (moderator.name === "anthropic" && !superRequested) {
|
|
1510
|
+
return { provider: retargetProvider(moderator, UPGRADE_MODEL), upgraded: true, label: UPGRADE_LABEL, skipReason: null };
|
|
1511
|
+
}
|
|
1512
|
+
if (superChainFor(moderator.name).length === 0) {
|
|
1513
|
+
return {
|
|
1514
|
+
provider: moderator,
|
|
1515
|
+
upgraded: false,
|
|
1516
|
+
label: null,
|
|
1517
|
+
skipReason: `no flagship model is known for ${moderator.name}; the moderator ran at ${moderator.model}.`
|
|
1407
1518
|
};
|
|
1408
1519
|
}
|
|
1409
|
-
const
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
};
|
|
1413
|
-
return { url: opts.url, headers, body };
|
|
1520
|
+
const seat = retargetForSuper(moderator);
|
|
1521
|
+
const label = moderator.name === "anthropic" ? UPGRADE_LABEL : seat.model;
|
|
1522
|
+
return { provider: seat, upgraded: true, label, skipReason: null };
|
|
1414
1523
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1524
|
+
|
|
1525
|
+
// ../../config/pricing.json
|
|
1526
|
+
var pricing_default = {
|
|
1527
|
+
_meta: {
|
|
1528
|
+
currency: "USD",
|
|
1529
|
+
unit: "per_1k_tokens",
|
|
1530
|
+
updated_at: "2026-09-06",
|
|
1531
|
+
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."
|
|
1532
|
+
},
|
|
1533
|
+
anthropic: {
|
|
1534
|
+
_source: {
|
|
1535
|
+
url: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
1536
|
+
verified_at: "2026-09-27"
|
|
1537
|
+
},
|
|
1538
|
+
"claude-opus-4-5": {
|
|
1539
|
+
prompt_per_1k: 5e-3,
|
|
1540
|
+
completion_per_1k: 0.025,
|
|
1541
|
+
cached_per_1k: 5e-4
|
|
1542
|
+
},
|
|
1543
|
+
"claude-opus-4-7": {
|
|
1544
|
+
prompt_per_1k: 5e-3,
|
|
1545
|
+
completion_per_1k: 0.025,
|
|
1546
|
+
cached_per_1k: 5e-4
|
|
1547
|
+
},
|
|
1548
|
+
"claude-opus-4-8": {
|
|
1549
|
+
prompt_per_1k: 5e-3,
|
|
1550
|
+
completion_per_1k: 0.025,
|
|
1551
|
+
cached_per_1k: 5e-4
|
|
1552
|
+
},
|
|
1553
|
+
"claude-opus-5-5": {
|
|
1554
|
+
prompt_per_1k: 4e-3,
|
|
1555
|
+
completion_per_1k: 0.02,
|
|
1556
|
+
cached_per_1k: 2e-4
|
|
1557
|
+
},
|
|
1558
|
+
"claude-opus-5": {
|
|
1559
|
+
prompt_per_1k: 5e-3,
|
|
1560
|
+
completion_per_1k: 0.025,
|
|
1561
|
+
cached_per_1k: 5e-4
|
|
1562
|
+
},
|
|
1563
|
+
"claude-fable-5": {
|
|
1564
|
+
prompt_per_1k: 0.01,
|
|
1565
|
+
completion_per_1k: 0.05,
|
|
1566
|
+
cached_per_1k: 1e-3
|
|
1567
|
+
},
|
|
1568
|
+
"claude-sonnet-4-5": {
|
|
1569
|
+
prompt_per_1k: 3e-3,
|
|
1570
|
+
completion_per_1k: 0.015,
|
|
1571
|
+
cached_per_1k: 3e-4
|
|
1572
|
+
},
|
|
1573
|
+
"claude-sonnet-4-6": {
|
|
1574
|
+
prompt_per_1k: 3e-3,
|
|
1575
|
+
completion_per_1k: 0.015,
|
|
1576
|
+
cached_per_1k: 3e-4
|
|
1577
|
+
},
|
|
1578
|
+
"claude-haiku-4-5": {
|
|
1579
|
+
prompt_per_1k: 1e-3,
|
|
1580
|
+
completion_per_1k: 5e-3,
|
|
1581
|
+
cached_per_1k: 1e-4
|
|
1582
|
+
},
|
|
1583
|
+
"claude-3-5-sonnet": {
|
|
1584
|
+
prompt_per_1k: 3e-3,
|
|
1585
|
+
completion_per_1k: 0.015,
|
|
1586
|
+
cached_per_1k: 3e-4
|
|
1587
|
+
},
|
|
1588
|
+
"claude-3-5-haiku": {
|
|
1589
|
+
prompt_per_1k: 8e-4,
|
|
1590
|
+
completion_per_1k: 4e-3,
|
|
1591
|
+
cached_per_1k: 8e-5
|
|
1592
|
+
},
|
|
1593
|
+
"claude-fable-5-1": {
|
|
1594
|
+
prompt_per_1k: 0.01,
|
|
1595
|
+
completion_per_1k: 0.05,
|
|
1596
|
+
cached_per_1k: 25e-5
|
|
1597
|
+
}
|
|
1598
|
+
},
|
|
1599
|
+
openai: {
|
|
1600
|
+
_source: {
|
|
1601
|
+
url: "https://developers.openai.com/api/docs/pricing",
|
|
1602
|
+
verified_at: "2026-09-27"
|
|
1603
|
+
},
|
|
1604
|
+
"gpt-6-astra": {
|
|
1605
|
+
prompt_per_1k: 0.01,
|
|
1606
|
+
completion_per_1k: 0.05,
|
|
1607
|
+
cached_per_1k: 1e-3
|
|
1608
|
+
},
|
|
1609
|
+
"gpt-6-sol": {
|
|
1610
|
+
prompt_per_1k: 2e-3,
|
|
1611
|
+
completion_per_1k: 0.01,
|
|
1612
|
+
cached_per_1k: 2e-4
|
|
1613
|
+
},
|
|
1614
|
+
"gpt-6-luna": {
|
|
1615
|
+
prompt_per_1k: 1e-4,
|
|
1616
|
+
completion_per_1k: 5e-4,
|
|
1617
|
+
cached_per_1k: 1e-5
|
|
1618
|
+
},
|
|
1619
|
+
"gpt-5.6-sol": {
|
|
1620
|
+
prompt_per_1k: 4e-3,
|
|
1621
|
+
completion_per_1k: 0.02,
|
|
1622
|
+
cached_per_1k: 4e-4
|
|
1623
|
+
},
|
|
1624
|
+
"gpt-5.6-terra": {
|
|
1625
|
+
prompt_per_1k: 2e-3,
|
|
1626
|
+
completion_per_1k: 0.012,
|
|
1627
|
+
cached_per_1k: 2e-4
|
|
1628
|
+
},
|
|
1629
|
+
"gpt-5.6-luna": {
|
|
1630
|
+
prompt_per_1k: 2e-4,
|
|
1631
|
+
completion_per_1k: 12e-4,
|
|
1632
|
+
cached_per_1k: 2e-5
|
|
1633
|
+
},
|
|
1634
|
+
"gpt-5.6": {
|
|
1635
|
+
prompt_per_1k: 4e-3,
|
|
1636
|
+
completion_per_1k: 0.02,
|
|
1637
|
+
cached_per_1k: 4e-4
|
|
1638
|
+
},
|
|
1639
|
+
"gpt-5.5": {
|
|
1640
|
+
prompt_per_1k: 5e-3,
|
|
1641
|
+
completion_per_1k: 0.03,
|
|
1642
|
+
cached_per_1k: 5e-4
|
|
1643
|
+
},
|
|
1644
|
+
"gpt-5": {
|
|
1645
|
+
prompt_per_1k: 125e-5,
|
|
1646
|
+
completion_per_1k: 0.01,
|
|
1647
|
+
cached_per_1k: 125e-6
|
|
1648
|
+
},
|
|
1649
|
+
"gpt-5-mini": {
|
|
1650
|
+
prompt_per_1k: 25e-5,
|
|
1651
|
+
completion_per_1k: 2e-3,
|
|
1652
|
+
cached_per_1k: 25e-6
|
|
1653
|
+
},
|
|
1654
|
+
"gpt-5-nano": {
|
|
1655
|
+
prompt_per_1k: 5e-5,
|
|
1656
|
+
completion_per_1k: 4e-4,
|
|
1657
|
+
cached_per_1k: 5e-6
|
|
1658
|
+
},
|
|
1659
|
+
"gpt-4o": {
|
|
1660
|
+
prompt_per_1k: 25e-4,
|
|
1661
|
+
completion_per_1k: 0.01,
|
|
1662
|
+
cached_per_1k: 125e-5
|
|
1663
|
+
},
|
|
1664
|
+
"gpt-4o-mini": {
|
|
1665
|
+
prompt_per_1k: 15e-5,
|
|
1666
|
+
completion_per_1k: 6e-4,
|
|
1667
|
+
cached_per_1k: 75e-6
|
|
1668
|
+
},
|
|
1669
|
+
o1: {
|
|
1670
|
+
prompt_per_1k: 0.015,
|
|
1671
|
+
completion_per_1k: 0.06,
|
|
1672
|
+
cached_per_1k: 75e-4
|
|
1673
|
+
},
|
|
1674
|
+
"o1-mini": {
|
|
1675
|
+
prompt_per_1k: 3e-3,
|
|
1676
|
+
completion_per_1k: 0.012,
|
|
1677
|
+
cached_per_1k: 15e-4
|
|
1678
|
+
},
|
|
1679
|
+
o3: {
|
|
1680
|
+
prompt_per_1k: 2e-3,
|
|
1681
|
+
completion_per_1k: 8e-3,
|
|
1682
|
+
cached_per_1k: 5e-4
|
|
1683
|
+
},
|
|
1684
|
+
"o3-mini": {
|
|
1685
|
+
prompt_per_1k: 11e-4,
|
|
1686
|
+
completion_per_1k: 44e-4,
|
|
1687
|
+
cached_per_1k: 55e-5
|
|
1688
|
+
},
|
|
1689
|
+
"gpt-5-pro": {
|
|
1690
|
+
prompt_per_1k: 0.015,
|
|
1691
|
+
completion_per_1k: 0.12,
|
|
1692
|
+
cached_per_1k: 0
|
|
1693
|
+
},
|
|
1694
|
+
"gpt-5.2-pro": {
|
|
1695
|
+
prompt_per_1k: 0.021,
|
|
1696
|
+
completion_per_1k: 0.168,
|
|
1697
|
+
cached_per_1k: 0
|
|
1698
|
+
},
|
|
1699
|
+
"gpt-5.4-pro": {
|
|
1700
|
+
prompt_per_1k: 0.03,
|
|
1701
|
+
completion_per_1k: 0.18,
|
|
1702
|
+
cached_per_1k: 0
|
|
1703
|
+
},
|
|
1704
|
+
"gpt-5.5-pro": {
|
|
1705
|
+
prompt_per_1k: 0.03,
|
|
1706
|
+
completion_per_1k: 0.18,
|
|
1707
|
+
cached_per_1k: 0
|
|
1708
|
+
},
|
|
1709
|
+
"o1-pro": {
|
|
1710
|
+
prompt_per_1k: 0.15,
|
|
1711
|
+
completion_per_1k: 0.6,
|
|
1712
|
+
cached_per_1k: 0
|
|
1713
|
+
},
|
|
1714
|
+
"o3-pro": {
|
|
1715
|
+
prompt_per_1k: 0.02,
|
|
1716
|
+
completion_per_1k: 0.08,
|
|
1717
|
+
cached_per_1k: 0
|
|
1422
1718
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1719
|
+
},
|
|
1720
|
+
xai: {
|
|
1721
|
+
"grok-4-latest": {
|
|
1722
|
+
prompt_per_1k: 3e-3,
|
|
1723
|
+
completion_per_1k: 0.015,
|
|
1724
|
+
cached_per_1k: 75e-5
|
|
1725
|
+
},
|
|
1726
|
+
"grok-3": {
|
|
1727
|
+
prompt_per_1k: 3e-3,
|
|
1728
|
+
completion_per_1k: 0.015,
|
|
1729
|
+
cached_per_1k: 15e-4
|
|
1730
|
+
},
|
|
1731
|
+
"grok-3-mini": {
|
|
1732
|
+
prompt_per_1k: 3e-4,
|
|
1733
|
+
completion_per_1k: 5e-4,
|
|
1734
|
+
cached_per_1k: 15e-5
|
|
1427
1735
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
estimated: Object.keys(u).length === 0,
|
|
1455
|
-
purpose: opts.purpose
|
|
1456
|
-
};
|
|
1457
|
-
if (usage.total_tokens === 0) {
|
|
1458
|
-
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
|
|
1459
|
-
}
|
|
1460
|
-
return { text, usage };
|
|
1461
|
-
}
|
|
1462
|
-
function applyPricing3(usage, pricing) {
|
|
1463
|
-
const { cost_usd, estimated } = calculateCost(
|
|
1464
|
-
pricing,
|
|
1465
|
-
usage.provider,
|
|
1466
|
-
usage.model,
|
|
1467
|
-
usage.prompt_tokens,
|
|
1468
|
-
usage.completion_tokens,
|
|
1469
|
-
usage.cached_tokens
|
|
1470
|
-
);
|
|
1471
|
-
return {
|
|
1472
|
-
...usage,
|
|
1473
|
-
cost_usd,
|
|
1474
|
-
estimated: usage.estimated || estimated
|
|
1475
|
-
};
|
|
1476
|
-
}
|
|
1477
|
-
async function sendOpenAICompatible(args) {
|
|
1478
|
-
const { url, headers, body } = buildOpenAICompatibleRequest({
|
|
1479
|
-
provider: args.provider,
|
|
1480
|
-
model: args.model,
|
|
1481
|
-
apiKey: args.apiKey,
|
|
1482
|
-
url: args.url,
|
|
1483
|
-
messages: args.messages,
|
|
1484
|
-
maxTokens: args.maxTokens,
|
|
1485
|
-
temperature: args.temperature,
|
|
1486
|
-
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
1487
|
-
});
|
|
1488
|
-
if (args.provider === "openai" && isReasoningModel("openai", args.model) && !/^o1(?:-|$)/i.test(args.model)) {
|
|
1489
|
-
const effort = (process.env["CROSSCHECK_OPENAI_REASONING_EFFORT"] ?? "low").toLowerCase();
|
|
1490
|
-
if (effort === "minimal" || effort === "low" || effort === "medium" || effort === "high") {
|
|
1491
|
-
body.reasoning_effort = effort;
|
|
1736
|
+
},
|
|
1737
|
+
gemini: {
|
|
1738
|
+
"gemini-3.1-pro-preview": {
|
|
1739
|
+
prompt_per_1k: 2e-3,
|
|
1740
|
+
completion_per_1k: 0.012,
|
|
1741
|
+
cached_per_1k: 2e-4
|
|
1742
|
+
},
|
|
1743
|
+
"gemini-3.5-flash": {
|
|
1744
|
+
prompt_per_1k: 15e-4,
|
|
1745
|
+
completion_per_1k: 9e-3,
|
|
1746
|
+
cached_per_1k: 15e-5
|
|
1747
|
+
},
|
|
1748
|
+
"gemini-2.5-flash": {
|
|
1749
|
+
prompt_per_1k: 3e-4,
|
|
1750
|
+
completion_per_1k: 25e-4,
|
|
1751
|
+
cached_per_1k: 75e-6
|
|
1752
|
+
},
|
|
1753
|
+
"gemini-2.5-flash-lite": {
|
|
1754
|
+
prompt_per_1k: 1e-4,
|
|
1755
|
+
completion_per_1k: 4e-4,
|
|
1756
|
+
cached_per_1k: 25e-6
|
|
1757
|
+
},
|
|
1758
|
+
"gemini-3.8-flash": {
|
|
1759
|
+
prompt_per_1k: 75e-5,
|
|
1760
|
+
completion_per_1k: 375e-5,
|
|
1761
|
+
cached_per_1k: 75e-6
|
|
1492
1762
|
}
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
const init = {
|
|
1505
|
-
method: "POST",
|
|
1506
|
-
headers,
|
|
1507
|
-
body: JSON.stringify(body)
|
|
1508
|
-
};
|
|
1509
|
-
if (args.signal) init.signal = args.signal;
|
|
1510
|
-
const attemptOnce = async () => {
|
|
1511
|
-
let respLike;
|
|
1512
|
-
try {
|
|
1513
|
-
respLike = await doFetch(url, init);
|
|
1514
|
-
} catch (e) {
|
|
1515
|
-
const d = diagnoseNetworkError(e);
|
|
1516
|
-
if (!looksLikeNetworkFailure(d)) throw e;
|
|
1517
|
-
throw new ProviderError(d.kind, networkErrorMessage(args.provider, d), {
|
|
1518
|
-
transient: d.transient
|
|
1519
|
-
});
|
|
1763
|
+
},
|
|
1764
|
+
mistral: {
|
|
1765
|
+
"mistral-large-latest": {
|
|
1766
|
+
prompt_per_1k: 2e-3,
|
|
1767
|
+
completion_per_1k: 6e-3,
|
|
1768
|
+
cached_per_1k: 2e-4
|
|
1769
|
+
},
|
|
1770
|
+
"mistral-small-latest": {
|
|
1771
|
+
prompt_per_1k: 2e-4,
|
|
1772
|
+
completion_per_1k: 6e-4,
|
|
1773
|
+
cached_per_1k: 2e-5
|
|
1520
1774
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
model: args.model,
|
|
1533
|
-
purpose: args.purpose ?? "worker"
|
|
1534
|
-
});
|
|
1535
|
-
return { text, attempts: 1, usage: applyPricing3(usage, args.pricing) };
|
|
1775
|
+
},
|
|
1776
|
+
groq: {
|
|
1777
|
+
"llama-3.3-70b-versatile": {
|
|
1778
|
+
prompt_per_1k: 59e-5,
|
|
1779
|
+
completion_per_1k: 79e-5,
|
|
1780
|
+
cached_per_1k: 295e-6
|
|
1781
|
+
},
|
|
1782
|
+
"llama-3.1-8b-instant": {
|
|
1783
|
+
prompt_per_1k: 5e-5,
|
|
1784
|
+
completion_per_1k: 8e-5,
|
|
1785
|
+
cached_per_1k: 25e-6
|
|
1536
1786
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
xai: "grok-4-latest",
|
|
1549
|
-
mistral: "mistral-large-latest",
|
|
1550
|
-
groq: "llama-3.3-70b-versatile",
|
|
1551
|
-
deepseek: "deepseek-chat",
|
|
1552
|
-
gemini: "gemini-3.8-flash",
|
|
1553
|
-
kimi: "kimi-k3",
|
|
1554
|
-
qwen: "qwen3.8-max"
|
|
1555
|
-
};
|
|
1556
|
-
var DEFAULT_FALLBACKS = {
|
|
1557
|
-
// gpt-5.6 requires a billing-enabled account and is excluded from the free
|
|
1558
|
-
// tier, so gpt-5 remains the broadly-available floor. One hop, not a
|
|
1559
|
-
// ladder — an intermediate 5.5 step would just be another model to reason
|
|
1560
|
-
// about.
|
|
1561
|
-
openai: ["gpt-5"],
|
|
1562
|
-
// 3.8 Flash is recent enough that some projects won't have it yet.
|
|
1563
|
-
gemini: ["gemini-3.1-pro-preview"],
|
|
1564
|
-
// Opus 5.5 is new enough that an account could still be waiting on it.
|
|
1565
|
-
// Opus 5 is the same family one step back — and this chain is consulted
|
|
1566
|
-
// ONLY for a model-access failure, never for a bad key or a rate limit,
|
|
1567
|
-
// so it can't quietly downgrade anyone who simply hit a 429.
|
|
1568
|
-
anthropic: ["claude-opus-5"]
|
|
1569
|
-
};
|
|
1570
|
-
var MODEL_ENV_VARS = {
|
|
1571
|
-
anthropic: "ANTHROPIC_MODEL",
|
|
1572
|
-
openai: "OPENAI_MODEL",
|
|
1573
|
-
xai: "XAI_MODEL",
|
|
1574
|
-
mistral: "MISTRAL_MODEL",
|
|
1575
|
-
groq: "GROQ_MODEL",
|
|
1576
|
-
deepseek: "DEEPSEEK_MODEL",
|
|
1577
|
-
gemini: "GEMINI_MODEL",
|
|
1578
|
-
kimi: "KIMI_MODEL",
|
|
1579
|
-
qwen: "QWEN_MODEL"
|
|
1580
|
-
};
|
|
1581
|
-
function parseFallbackModels(raw) {
|
|
1582
|
-
if (!raw) return [];
|
|
1583
|
-
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1584
|
-
}
|
|
1585
|
-
function fallbackModelsFor(env, provider) {
|
|
1586
|
-
const envVar = MODEL_ENV_VARS[provider];
|
|
1587
|
-
const fromEnv = envVar ? parseFallbackModels(env[`${envVar}_FALLBACKS`]) : [];
|
|
1588
|
-
if (fromEnv.length > 0) return fromEnv;
|
|
1589
|
-
const pinned = envVar ? env[envVar] : void 0;
|
|
1590
|
-
if (pinned && pinned !== DEFAULT_MODELS[provider]) return [];
|
|
1591
|
-
return DEFAULT_FALLBACKS[provider] ?? [];
|
|
1592
|
-
}
|
|
1593
|
-
var DEFAULT_EXCLUDED_PROVIDERS = ["mistral", "groq"];
|
|
1594
|
-
function excludedProviders(env) {
|
|
1595
|
-
const parse = (v) => (v ?? "").split(",").map((x) => x.trim().toLowerCase()).filter((x) => x !== "");
|
|
1596
|
-
const excluded = new Set(
|
|
1597
|
-
parse(env["CROSSCHECK_EXCLUDE_PROVIDERS"]).length > 0 ? parse(env["CROSSCHECK_EXCLUDE_PROVIDERS"]) : DEFAULT_EXCLUDED_PROVIDERS
|
|
1598
|
-
);
|
|
1599
|
-
for (const name of parse(env["CROSSCHECK_ENABLE_PROVIDERS"])) excluded.delete(name);
|
|
1600
|
-
return excluded;
|
|
1601
|
-
}
|
|
1602
|
-
function buildProviders(opts) {
|
|
1603
|
-
const out = {};
|
|
1604
|
-
const excluded = excludedProviders(opts.env);
|
|
1605
|
-
const anthropicKey = opts.env["ANTHROPIC_API_KEY"];
|
|
1606
|
-
if (anthropicKey && !excluded.has("anthropic")) {
|
|
1607
|
-
const model = opts.env["ANTHROPIC_MODEL"] ?? DEFAULT_MODELS.anthropic;
|
|
1608
|
-
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts, fallbackModelsFor(opts.env, "anthropic"));
|
|
1609
|
-
}
|
|
1610
|
-
const openAiCompatSpec = [
|
|
1611
|
-
{ name: "openai", keyEnv: "OPENAI_API_KEY", modelEnv: "OPENAI_MODEL", defaultModel: DEFAULT_MODELS["openai"] },
|
|
1612
|
-
{ name: "xai", keyEnv: "XAI_API_KEY", modelEnv: "XAI_MODEL", defaultModel: DEFAULT_MODELS["xai"] },
|
|
1613
|
-
{ name: "mistral", keyEnv: "MISTRAL_API_KEY", modelEnv: "MISTRAL_MODEL", defaultModel: DEFAULT_MODELS["mistral"] },
|
|
1614
|
-
{ name: "groq", keyEnv: "GROQ_API_KEY", modelEnv: "GROQ_MODEL", defaultModel: DEFAULT_MODELS["groq"] },
|
|
1615
|
-
{ name: "deepseek", keyEnv: "DEEPSEEK_API_KEY", modelEnv: "DEEPSEEK_MODEL", defaultModel: DEFAULT_MODELS["deepseek"] },
|
|
1616
|
-
{ name: "kimi", keyEnv: "KIMI_API_KEY", modelEnv: "KIMI_MODEL", defaultModel: DEFAULT_MODELS["kimi"] },
|
|
1617
|
-
{ name: "qwen", keyEnv: "QWEN_API_KEY", modelEnv: "QWEN_MODEL", defaultModel: DEFAULT_MODELS["qwen"] }
|
|
1618
|
-
];
|
|
1619
|
-
for (const s of openAiCompatSpec) {
|
|
1620
|
-
const apiKey = opts.env[s.keyEnv];
|
|
1621
|
-
if (!apiKey) continue;
|
|
1622
|
-
if (excluded.has(s.name)) continue;
|
|
1623
|
-
const model = opts.env[s.modelEnv] ?? s.defaultModel;
|
|
1624
|
-
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts, fallbackModelsFor(opts.env, s.name));
|
|
1625
|
-
}
|
|
1626
|
-
const geminiKey = opts.env["GEMINI_API_KEY"];
|
|
1627
|
-
if (geminiKey && !excluded.has("gemini")) {
|
|
1628
|
-
const model = opts.env["GEMINI_MODEL"] ?? DEFAULT_MODELS.gemini;
|
|
1629
|
-
out["gemini"] = makeGeminiProvider(model, geminiKey, opts, fallbackModelsFor(opts.env, "gemini"));
|
|
1630
|
-
}
|
|
1631
|
-
return out;
|
|
1632
|
-
}
|
|
1633
|
-
function makeAnthropicProvider(model, apiKey, opts, fallbackModels = []) {
|
|
1634
|
-
return {
|
|
1635
|
-
name: "anthropic",
|
|
1636
|
-
model,
|
|
1637
|
-
fallbackModels,
|
|
1638
|
-
send: async (args) => {
|
|
1639
|
-
const sendOpts = {
|
|
1640
|
-
...args,
|
|
1641
|
-
apiKey,
|
|
1642
|
-
model: args.modelOverride ?? model,
|
|
1643
|
-
pricing: opts.pricing
|
|
1644
|
-
};
|
|
1645
|
-
if (opts.fetchImpl) sendOpts.fetchImpl = opts.fetchImpl;
|
|
1646
|
-
return sendAnthropic(sendOpts);
|
|
1787
|
+
},
|
|
1788
|
+
deepseek: {
|
|
1789
|
+
"deepseek-chat": {
|
|
1790
|
+
prompt_per_1k: 27e-5,
|
|
1791
|
+
completion_per_1k: 11e-4,
|
|
1792
|
+
cached_per_1k: 7e-5
|
|
1793
|
+
},
|
|
1794
|
+
"deepseek-reasoner": {
|
|
1795
|
+
prompt_per_1k: 55e-5,
|
|
1796
|
+
completion_per_1k: 22e-4,
|
|
1797
|
+
cached_per_1k: 14e-5
|
|
1647
1798
|
}
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
model,
|
|
1655
|
-
fallbackModels,
|
|
1656
|
-
send: async (args) => {
|
|
1657
|
-
const sendOpts = {
|
|
1658
|
-
...args,
|
|
1659
|
-
provider: name,
|
|
1660
|
-
apiKey,
|
|
1661
|
-
model: args.modelOverride ?? model,
|
|
1662
|
-
url,
|
|
1663
|
-
pricing: opts.pricing
|
|
1664
|
-
};
|
|
1665
|
-
if (opts.fetchImpl) sendOpts.fetchImpl = opts.fetchImpl;
|
|
1666
|
-
return sendOpenAICompatible(sendOpts);
|
|
1799
|
+
},
|
|
1800
|
+
kimi: {
|
|
1801
|
+
"kimi-k3": {
|
|
1802
|
+
prompt_per_1k: 3e-3,
|
|
1803
|
+
completion_per_1k: 0.015,
|
|
1804
|
+
cached_per_1k: 3e-4
|
|
1667
1805
|
}
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1806
|
+
},
|
|
1807
|
+
qwen: {
|
|
1808
|
+
"qwen3.8-max": {
|
|
1809
|
+
prompt_per_1k: 2e-3,
|
|
1810
|
+
completion_per_1k: 6e-3,
|
|
1811
|
+
cached_per_1k: 2e-4
|
|
1812
|
+
},
|
|
1813
|
+
"qwen3.7-max": {
|
|
1814
|
+
prompt_per_1k: 125e-5,
|
|
1815
|
+
completion_per_1k: 375e-5,
|
|
1816
|
+
cached_per_1k: 125e-6
|
|
1817
|
+
}
|
|
1818
|
+
},
|
|
1819
|
+
_tiers: {
|
|
1820
|
+
low: {
|
|
1821
|
+
description: "Cheap, fast models for simple subtasks (extraction, formatting, short summaries).",
|
|
1822
|
+
models: [
|
|
1823
|
+
{
|
|
1824
|
+
provider: "openai",
|
|
1825
|
+
model: "gpt-4o-mini"
|
|
1826
|
+
},
|
|
1827
|
+
{
|
|
1828
|
+
provider: "openai",
|
|
1829
|
+
model: "gpt-5-mini"
|
|
1830
|
+
},
|
|
1831
|
+
{
|
|
1832
|
+
provider: "anthropic",
|
|
1833
|
+
model: "claude-haiku-4-5"
|
|
1834
|
+
},
|
|
1835
|
+
{
|
|
1836
|
+
provider: "anthropic",
|
|
1837
|
+
model: "claude-3-5-haiku"
|
|
1838
|
+
},
|
|
1839
|
+
{
|
|
1840
|
+
provider: "gemini",
|
|
1841
|
+
model: "gemini-3.5-flash"
|
|
1842
|
+
},
|
|
1843
|
+
{
|
|
1844
|
+
provider: "gemini",
|
|
1845
|
+
model: "gemini-2.5-flash"
|
|
1846
|
+
},
|
|
1847
|
+
{
|
|
1848
|
+
provider: "gemini",
|
|
1849
|
+
model: "gemini-2.5-flash-lite"
|
|
1850
|
+
},
|
|
1851
|
+
{
|
|
1852
|
+
provider: "xai",
|
|
1853
|
+
model: "grok-3-mini"
|
|
1854
|
+
},
|
|
1855
|
+
{
|
|
1856
|
+
provider: "groq",
|
|
1857
|
+
model: "llama-3.1-8b-instant"
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
provider: "deepseek",
|
|
1861
|
+
model: "deepseek-chat"
|
|
1862
|
+
}
|
|
1863
|
+
]
|
|
1864
|
+
},
|
|
1865
|
+
med: {
|
|
1866
|
+
description: "Mid-tier models for reasoning, code, multi-step thinking.",
|
|
1867
|
+
models: [
|
|
1868
|
+
{
|
|
1869
|
+
provider: "anthropic",
|
|
1870
|
+
model: "claude-sonnet-4-5"
|
|
1871
|
+
},
|
|
1872
|
+
{
|
|
1873
|
+
provider: "anthropic",
|
|
1874
|
+
model: "claude-sonnet-4-6"
|
|
1875
|
+
},
|
|
1876
|
+
{
|
|
1877
|
+
provider: "openai",
|
|
1878
|
+
model: "gpt-4o"
|
|
1879
|
+
},
|
|
1880
|
+
{
|
|
1881
|
+
provider: "openai",
|
|
1882
|
+
model: "o3-mini"
|
|
1883
|
+
},
|
|
1884
|
+
{
|
|
1885
|
+
provider: "gemini",
|
|
1886
|
+
model: "gemini-3.1-pro-preview"
|
|
1887
|
+
},
|
|
1888
|
+
{
|
|
1889
|
+
provider: "xai",
|
|
1890
|
+
model: "grok-3"
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
provider: "mistral",
|
|
1894
|
+
model: "mistral-large-latest"
|
|
1895
|
+
},
|
|
1896
|
+
{
|
|
1897
|
+
provider: "deepseek",
|
|
1898
|
+
model: "deepseek-reasoner"
|
|
1899
|
+
},
|
|
1900
|
+
{
|
|
1901
|
+
provider: "groq",
|
|
1902
|
+
model: "llama-3.3-70b-versatile"
|
|
1903
|
+
}
|
|
1904
|
+
]
|
|
1905
|
+
},
|
|
1906
|
+
high: {
|
|
1907
|
+
description: "Most capable models for hard reasoning, planning, audits.",
|
|
1908
|
+
models: [
|
|
1909
|
+
{
|
|
1910
|
+
provider: "anthropic",
|
|
1911
|
+
model: "claude-opus-4-5"
|
|
1912
|
+
},
|
|
1913
|
+
{
|
|
1914
|
+
provider: "anthropic",
|
|
1915
|
+
model: "claude-opus-4-7"
|
|
1916
|
+
},
|
|
1917
|
+
{
|
|
1918
|
+
provider: "anthropic",
|
|
1919
|
+
model: "claude-fable-5"
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
provider: "openai",
|
|
1923
|
+
model: "gpt-5.5"
|
|
1924
|
+
},
|
|
1925
|
+
{
|
|
1926
|
+
provider: "openai",
|
|
1927
|
+
model: "gpt-5"
|
|
1928
|
+
},
|
|
1929
|
+
{
|
|
1930
|
+
provider: "openai",
|
|
1931
|
+
model: "o1"
|
|
1932
|
+
},
|
|
1933
|
+
{
|
|
1934
|
+
provider: "openai",
|
|
1935
|
+
model: "o3"
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
provider: "xai",
|
|
1939
|
+
model: "grok-4-latest"
|
|
1940
|
+
}
|
|
1941
|
+
]
|
|
1684
1942
|
}
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1943
|
+
}
|
|
1944
|
+
};
|
|
1687
1945
|
|
|
1688
1946
|
// src/tools/index.ts
|
|
1689
1947
|
import { z } from "zod";
|
|
1690
1948
|
|
|
1691
1949
|
// src/server-meta.ts
|
|
1692
1950
|
var SERVER_NAME = "crosscheck-agent";
|
|
1693
|
-
var SERVER_VERSION = true ? "0.2.
|
|
1951
|
+
var SERVER_VERSION = true ? "0.2.31" : "0.0.0-dev";
|
|
1694
1952
|
|
|
1695
1953
|
// src/tools/audit.ts
|
|
1696
1954
|
import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
@@ -2373,33 +2631,6 @@ function operatorCeiling(purpose, provider) {
|
|
|
2373
2631
|
return null;
|
|
2374
2632
|
}
|
|
2375
2633
|
|
|
2376
|
-
// src/core/retarget.ts
|
|
2377
|
-
function retargetProvider(p, newModel, fallbackModels) {
|
|
2378
|
-
const chain = fallbackModels && fallbackModels.length > 0 ? fallbackModels : void 0;
|
|
2379
|
-
if (p.model === newModel && chain === void 0) return p;
|
|
2380
|
-
return {
|
|
2381
|
-
name: p.name,
|
|
2382
|
-
model: newModel,
|
|
2383
|
-
...chain ? { fallbackModels: chain } : {},
|
|
2384
|
-
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
2385
|
-
};
|
|
2386
|
-
}
|
|
2387
|
-
async function loadProviderWeights(storage, names) {
|
|
2388
|
-
const out = {};
|
|
2389
|
-
for (const raw of names) {
|
|
2390
|
-
const name = raw.toLowerCase();
|
|
2391
|
-
if (name in out) continue;
|
|
2392
|
-
const row = await storage.getProviderStats(name);
|
|
2393
|
-
if (!row) {
|
|
2394
|
-
out[name] = 0.5;
|
|
2395
|
-
continue;
|
|
2396
|
-
}
|
|
2397
|
-
const total = row.wins + row.losses + row.abstains;
|
|
2398
|
-
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
2399
|
-
}
|
|
2400
|
-
return out;
|
|
2401
|
-
}
|
|
2402
|
-
|
|
2403
2634
|
// src/core/model-fallback.ts
|
|
2404
2635
|
async function sendWithModelFallback(provider, args) {
|
|
2405
2636
|
const chain = [provider.model, ...provider.fallbackModels ?? []];
|
|
@@ -3466,82 +3697,6 @@ function selectForDifficulty(args) {
|
|
|
3466
3697
|
};
|
|
3467
3698
|
}
|
|
3468
3699
|
|
|
3469
|
-
// src/core/reasoning-upgrade.ts
|
|
3470
|
-
var UPGRADE_MODEL = "claude-fable-5";
|
|
3471
|
-
var UPGRADE_LABEL = "Fable 5";
|
|
3472
|
-
function isUpgradeRequested(args) {
|
|
3473
|
-
if (args["upgrade"] === true) return true;
|
|
3474
|
-
const reasoning = args["reasoning"];
|
|
3475
|
-
if (typeof reasoning === "string" && reasoning.trim().toLowerCase() === "max") {
|
|
3476
|
-
return true;
|
|
3477
|
-
}
|
|
3478
|
-
const model = args["model"];
|
|
3479
|
-
if (typeof model === "string") {
|
|
3480
|
-
const m = model.trim().toLowerCase();
|
|
3481
|
-
if (m === "fable5" || m === "fable-5" || m === "fable" || m === UPGRADE_MODEL) {
|
|
3482
|
-
return true;
|
|
3483
|
-
}
|
|
3484
|
-
}
|
|
3485
|
-
return false;
|
|
3486
|
-
}
|
|
3487
|
-
var HEAVY_TERMS = [
|
|
3488
|
-
/\bprov(e|ing|en)\b|\bproof\b|\btheorem\b|\bderiv(e|ation)\b/i,
|
|
3489
|
-
/\barchitect(ure|ing)?\b|\bdesign\s+(a|the|an)\b|\bhigh[-\s]?level\s+design\b/i,
|
|
3490
|
-
/\btrade[-\s]?offs?\b|\bpros?\s+and\s+cons?\b|\bweigh\b/i,
|
|
3491
|
-
/\boptimi[sz]e\b|\bcomplexity\b|\bO\(|\balgorithm(ic)?\b/i,
|
|
3492
|
-
/\brace\s+condition\b|\bconcurren(t|cy)\b|\bdeadlock\b|\bdistributed\b|\bconsensus\b/i,
|
|
3493
|
-
/\broot[-\s]?cause\b|\bdebug(ging)?\b|\bwhy\s+(does|is|would|might)\b/i,
|
|
3494
|
-
/\bsecurity\b|\bthreat\s+model\b|\bexploit\b|\bvulnerabilit(y|ies)\b/i,
|
|
3495
|
-
/\bmigrat(e|ion)\b|\brefactor\b|\bre[-\s]?architect\b/i,
|
|
3496
|
-
/\binvariant\b|\bcorrectness\b|\bedge\s+cases?\b|\bformal(ly)?\b/i,
|
|
3497
|
-
/\bstrateg(y|ic|ies)\b|\bplan\s+(the|a|an|out)\b|\broadmap\b/i,
|
|
3498
|
-
/\breason\s+(through|about)\b|\bstep[-\s]?by[-\s]?step\b|\bthink\s+deeply\b/i,
|
|
3499
|
-
/\bnp[-\s]?(hard|complete)\b|\bgraph\b|\bstate\s+machine\b|\bconstraint\b/i
|
|
3500
|
-
];
|
|
3501
|
-
var DIFFICULTY_TERMS = /\b(hard|tricky|subtle|complex|complicated|non[-\s]?trivial|thorny|gnarly|difficult|deep)\b/i;
|
|
3502
|
-
function assessReasoning(text) {
|
|
3503
|
-
const s = typeof text === "string" ? text : "";
|
|
3504
|
-
const signals = [];
|
|
3505
|
-
let score = 0;
|
|
3506
|
-
let termHits = 0;
|
|
3507
|
-
for (const re of HEAVY_TERMS) {
|
|
3508
|
-
if (re.test(s)) termHits += 1;
|
|
3509
|
-
}
|
|
3510
|
-
if (termHits > 0) {
|
|
3511
|
-
const add = Math.min(termHits, 4);
|
|
3512
|
-
score += add;
|
|
3513
|
-
signals.push(`${termHits} reasoning cue${termHits === 1 ? "" : "s"}`);
|
|
3514
|
-
}
|
|
3515
|
-
if (DIFFICULTY_TERMS.test(s)) {
|
|
3516
|
-
score += 1;
|
|
3517
|
-
signals.push("explicit difficulty marker");
|
|
3518
|
-
}
|
|
3519
|
-
const words = s.split(/\s+/).filter(Boolean).length;
|
|
3520
|
-
if (words >= 120) {
|
|
3521
|
-
score += 2;
|
|
3522
|
-
signals.push("long, detailed prompt");
|
|
3523
|
-
} else if (words >= 50) {
|
|
3524
|
-
score += 1;
|
|
3525
|
-
signals.push("substantial prompt");
|
|
3526
|
-
}
|
|
3527
|
-
const constraints = (s.match(/\b(must|should|need to|ensure|require[ds]?|constraint)\b/gi) ?? []).length + (s.match(/(^|\n)\s*(?:[-*]|\d+[.)])\s+/g) ?? []).length;
|
|
3528
|
-
if (constraints >= 3) {
|
|
3529
|
-
score += 1;
|
|
3530
|
-
signals.push(`${constraints} constraints/steps`);
|
|
3531
|
-
}
|
|
3532
|
-
const heavy = score >= 3;
|
|
3533
|
-
const reason = heavy ? `Task looks reasoning-heavy (${signals.join(", ") || "score " + score}).` : `Task looks routine (score ${score}).`;
|
|
3534
|
-
return { heavy, score, reason, signals };
|
|
3535
|
-
}
|
|
3536
|
-
function buildSuggestedUpgrade(toolName, assessment) {
|
|
3537
|
-
return {
|
|
3538
|
-
model: UPGRADE_MODEL,
|
|
3539
|
-
label: UPGRADE_LABEL,
|
|
3540
|
-
reason: assessment.reason,
|
|
3541
|
-
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.`
|
|
3542
|
-
};
|
|
3543
|
-
}
|
|
3544
|
-
|
|
3545
3700
|
// src/tools/audit.ts
|
|
3546
3701
|
var AUDIT_OBVIOUS_FAILURE_HIGH = 0.3;
|
|
3547
3702
|
var AUDIT_OBVIOUS_FAILURE_MED = 0.2;
|
|
@@ -3839,9 +3994,14 @@ ${rubricText}`;
|
|
|
3839
3994
|
);
|
|
3840
3995
|
}
|
|
3841
3996
|
const upgradeRequested = isUpgradeRequested(args);
|
|
3842
|
-
const
|
|
3843
|
-
const
|
|
3844
|
-
const
|
|
3997
|
+
const seat = synthesisSeat({ moderator: auditor, pinned: false, superRequested: false, upgradeRequested });
|
|
3998
|
+
const upgraded = seat.upgraded;
|
|
3999
|
+
const auditorForCall = seat.provider;
|
|
4000
|
+
const coProvider = pickCoReasoner(
|
|
4001
|
+
opts.providers,
|
|
4002
|
+
upgradeRequested,
|
|
4003
|
+
auditorForCall.name === "openai" ? auditorForCall.model : void 0
|
|
4004
|
+
);
|
|
3845
4005
|
const coReason = await coReasonThenInject({
|
|
3846
4006
|
provider: coProvider,
|
|
3847
4007
|
messages: msgs,
|
|
@@ -3924,15 +4084,16 @@ ${rubricText}`;
|
|
|
3924
4084
|
if (upgraded) {
|
|
3925
4085
|
result["reasoning_upgrade"] = {
|
|
3926
4086
|
applied: true,
|
|
3927
|
-
|
|
3928
|
-
|
|
4087
|
+
provider: auditorForCall.name,
|
|
4088
|
+
model: auditorForCall.model,
|
|
4089
|
+
label: seat.label ?? auditorForCall.model,
|
|
3929
4090
|
seat: "auditor",
|
|
3930
4091
|
...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
|
|
3931
4092
|
};
|
|
3932
4093
|
} else if (upgradeRequested) {
|
|
3933
4094
|
result["reasoning_upgrade"] = {
|
|
3934
4095
|
applied: false,
|
|
3935
|
-
reason:
|
|
4096
|
+
reason: seat.skipReason ?? "the auditor couldn't be upgraded."
|
|
3936
4097
|
};
|
|
3937
4098
|
} else {
|
|
3938
4099
|
const assessment = assessReasoning(
|
|
@@ -4015,7 +4176,7 @@ function pickAuditor(providers, exclude, explicit, moderatorName, allowlist, che
|
|
|
4015
4176
|
}
|
|
4016
4177
|
}
|
|
4017
4178
|
}
|
|
4018
|
-
const mod = providers
|
|
4179
|
+
const mod = resolveModerator(providers, moderatorName).provider;
|
|
4019
4180
|
if (mod !== void 0 && !exclude.has(mod.name) && (allowlist === null || allowlist.includes(mod.name))) {
|
|
4020
4181
|
return { auditor: mod, reason: null };
|
|
4021
4182
|
}
|
|
@@ -4334,9 +4495,7 @@ function pickPanelJudge(providers, moderatorName, pricing) {
|
|
|
4334
4495
|
return entry.model && entry.model !== base.model ? retargetProvider(base, entry.model) : base;
|
|
4335
4496
|
}
|
|
4336
4497
|
}
|
|
4337
|
-
|
|
4338
|
-
if (mod !== void 0) return mod;
|
|
4339
|
-
return null;
|
|
4498
|
+
return resolveModerator(providers, moderatorName).provider ?? null;
|
|
4340
4499
|
}
|
|
4341
4500
|
var AGREEMENT_CHECK_SCHEMA = {
|
|
4342
4501
|
type: "object",
|
|
@@ -4653,77 +4812,6 @@ async function pickAutoPanel(storage, purpose, n, providers, allowlist) {
|
|
|
4653
4812
|
return { providers: ranked.slice(0, Math.max(1, n)), coldStart: true };
|
|
4654
4813
|
}
|
|
4655
4814
|
|
|
4656
|
-
// src/core/super-mode.ts
|
|
4657
|
-
var SUPER_MODELS = {
|
|
4658
|
-
// Fable 5.1 verified present on the account; 5 as the fallback for accounts
|
|
4659
|
-
// that haven't been granted 5.1 yet.
|
|
4660
|
-
anthropic: ["claude-fable-5-1", UPGRADE_MODEL],
|
|
4661
|
-
// Astra is live (verified against the account's model list 2026-09-23).
|
|
4662
|
-
// The fallback is named explicitly rather than reusing CO_REASON_MODEL:
|
|
4663
|
-
// that constant means "OpenAI's top reasoning model" for the max-reasoning
|
|
4664
|
-
// co-reasoner, which is a different question from "what should super fall
|
|
4665
|
-
// back to". Sharing the symbol silently coupled two unrelated decisions.
|
|
4666
|
-
openai: ["gpt-6-astra", "gpt-6-sol"],
|
|
4667
|
-
// Explicitly left as-is per product decision. Note grok-4-latest is not in
|
|
4668
|
-
// xAI's published model list, but it resolves in practice — real panel calls
|
|
4669
|
-
// on it succeeded as recently as 2026-08-30 — so it is an unlisted alias
|
|
4670
|
-
// rather than a dead id.
|
|
4671
|
-
xai: [DEFAULT_MODELS["xai"]],
|
|
4672
|
-
// 3.8 Flash is now BOTH the everyday default and the super pick for Gemini,
|
|
4673
|
-
// so this can no longer borrow DEFAULT_MODELS as its fallback — that would
|
|
4674
|
-
// repeat the same model and retry one that just failed. Named explicitly.
|
|
4675
|
-
gemini: ["gemini-3.8-flash", "gemini-3.1-pro-preview"],
|
|
4676
|
-
kimi: [DEFAULT_MODELS["kimi"]],
|
|
4677
|
-
qwen: [DEFAULT_MODELS["qwen"]]
|
|
4678
|
-
};
|
|
4679
|
-
var SUPER_ENV_VARS = {
|
|
4680
|
-
anthropic: "ANTHROPIC_SUPER_MODEL",
|
|
4681
|
-
openai: "OPENAI_SUPER_MODEL",
|
|
4682
|
-
xai: "XAI_SUPER_MODEL",
|
|
4683
|
-
gemini: "GEMINI_SUPER_MODEL",
|
|
4684
|
-
kimi: "KIMI_SUPER_MODEL",
|
|
4685
|
-
qwen: "QWEN_SUPER_MODEL",
|
|
4686
|
-
mistral: "MISTRAL_SUPER_MODEL",
|
|
4687
|
-
groq: "GROQ_SUPER_MODEL",
|
|
4688
|
-
deepseek: "DEEPSEEK_SUPER_MODEL"
|
|
4689
|
-
};
|
|
4690
|
-
function parseChain(v) {
|
|
4691
|
-
if (!v) return [];
|
|
4692
|
-
const out = [];
|
|
4693
|
-
for (const raw of v.split(",")) {
|
|
4694
|
-
const m = raw.trim();
|
|
4695
|
-
if (m && !out.includes(m)) out.push(m);
|
|
4696
|
-
}
|
|
4697
|
-
return out;
|
|
4698
|
-
}
|
|
4699
|
-
function superChainFor(provider, env = process.env) {
|
|
4700
|
-
const varName = SUPER_ENV_VARS[provider];
|
|
4701
|
-
const user = varName ? parseChain(env[varName]) : [];
|
|
4702
|
-
if (user.length > 0) {
|
|
4703
|
-
const fallbacks = varName ? parseChain(env[`${varName}_FALLBACKS`]) : [];
|
|
4704
|
-
return [...user, ...fallbacks.filter((m) => !user.includes(m))];
|
|
4705
|
-
}
|
|
4706
|
-
return SUPER_MODELS[provider] ?? [];
|
|
4707
|
-
}
|
|
4708
|
-
function superPrimary(provider, env = process.env) {
|
|
4709
|
-
return superChainFor(provider, env)[0];
|
|
4710
|
-
}
|
|
4711
|
-
var SUPER_PROVIDER_NAMES = Object.keys(SUPER_MODELS);
|
|
4712
|
-
function isSuperRequested(args) {
|
|
4713
|
-
return args["super"] === true;
|
|
4714
|
-
}
|
|
4715
|
-
function retargetForSuper(p, env = process.env) {
|
|
4716
|
-
const chain = superChainFor(p.name, env);
|
|
4717
|
-
if (chain.length === 0) return p;
|
|
4718
|
-
const [primary, ...fallbacks] = chain;
|
|
4719
|
-
return retargetProvider(p, primary, fallbacks);
|
|
4720
|
-
}
|
|
4721
|
-
var SUPER_BUDGET_MULTIPLIER = 3;
|
|
4722
|
-
var SUPER_BUDGET_CEILING = 16e3;
|
|
4723
|
-
function superBudget(base) {
|
|
4724
|
-
return Math.min(base * SUPER_BUDGET_MULTIPLIER, SUPER_BUDGET_CEILING);
|
|
4725
|
-
}
|
|
4726
|
-
|
|
4727
4815
|
// src/core/session-memory.ts
|
|
4728
4816
|
var SESSION_MEMORY_DEFAULT_LIMIT = 50;
|
|
4729
4817
|
var SESSION_MEMORY_INJECT_BUDGET_CHARS = 4e3;
|
|
@@ -6790,9 +6878,9 @@ import path6 from "path";
|
|
|
6790
6878
|
import { performance as performance7 } from "perf_hooks";
|
|
6791
6879
|
|
|
6792
6880
|
// src/core/progress.ts
|
|
6793
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
6881
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
6794
6882
|
import { performance as performance6 } from "perf_hooks";
|
|
6795
|
-
var progressStorage = new
|
|
6883
|
+
var progressStorage = new AsyncLocalStorage2();
|
|
6796
6884
|
async function emitProgress(message, opts) {
|
|
6797
6885
|
const ctx = progressStorage.getStore();
|
|
6798
6886
|
const wallStart = ctx ? ctx.wallStart : performance6.now();
|
|
@@ -7091,8 +7179,9 @@ async function runOrchestrate(args, opts) {
|
|
|
7091
7179
|
blocked
|
|
7092
7180
|
};
|
|
7093
7181
|
}
|
|
7094
|
-
const
|
|
7095
|
-
const moderator =
|
|
7182
|
+
const resolvedModerator = resolveModerator(opts.providers, args["moderator"], opts.moderator);
|
|
7183
|
+
const moderator = resolvedModerator.provider ?? selected[0];
|
|
7184
|
+
const moderatorChosen = resolvedModerator.provider !== void 0 && (resolvedModerator.pinned || parseModeratorSpec(args["moderator"]) !== null);
|
|
7096
7185
|
const failFast = boolArg2(args["fail_fast"], false);
|
|
7097
7186
|
const planOnly = boolArg2(args["plan_only"], false);
|
|
7098
7187
|
const cheapMode = boolArg2(args["cheap_mode"], opts.ctx?.cheap_mode ?? false);
|
|
@@ -7576,11 +7665,17 @@ ${recombineSys}` : recombineSys },
|
|
|
7576
7665
|
let synthProvider = moderator;
|
|
7577
7666
|
let synthModel = null;
|
|
7578
7667
|
const upgradeRequested = isUpgradeRequested(args);
|
|
7579
|
-
const
|
|
7668
|
+
const seat = synthesisSeat({
|
|
7669
|
+
moderator,
|
|
7670
|
+
pinned: resolvedModerator.provider !== void 0 && resolvedModerator.pinned,
|
|
7671
|
+
superRequested: false,
|
|
7672
|
+
upgradeRequested
|
|
7673
|
+
});
|
|
7674
|
+
const upgraded = seat.upgraded;
|
|
7580
7675
|
if (upgraded) {
|
|
7581
|
-
synthProvider =
|
|
7582
|
-
synthModel =
|
|
7583
|
-
} else if (cheapMode && opts.pricing) {
|
|
7676
|
+
synthProvider = seat.provider;
|
|
7677
|
+
synthModel = `${seat.provider.name}/${seat.provider.model}`;
|
|
7678
|
+
} else if (cheapMode && opts.pricing && !moderatorChosen) {
|
|
7584
7679
|
const pick = selectForDifficulty({
|
|
7585
7680
|
pricing: opts.pricing,
|
|
7586
7681
|
tier: "med",
|
|
@@ -7606,7 +7701,11 @@ ${recombineSys}` : recombineSys },
|
|
|
7606
7701
|
provider: synthProvider.name
|
|
7607
7702
|
} }
|
|
7608
7703
|
);
|
|
7609
|
-
const coProvider = pickCoReasoner(
|
|
7704
|
+
const coProvider = pickCoReasoner(
|
|
7705
|
+
opts.providers,
|
|
7706
|
+
upgradeRequested,
|
|
7707
|
+
synthProvider.name === "openai" ? synthProvider.model : void 0
|
|
7708
|
+
);
|
|
7610
7709
|
const coReason = await coReasonThenInject({
|
|
7611
7710
|
provider: coProvider,
|
|
7612
7711
|
messages: recMsgs,
|
|
@@ -7656,7 +7755,8 @@ ${recombineSys}` : recombineSys },
|
|
|
7656
7755
|
missing,
|
|
7657
7756
|
partial,
|
|
7658
7757
|
fail_fast: failFast,
|
|
7659
|
-
cheap_mode: cheapMode
|
|
7758
|
+
cheap_mode: cheapMode,
|
|
7759
|
+
moderator: { provider: moderator.name, model: moderator.model }
|
|
7660
7760
|
};
|
|
7661
7761
|
if (personaMeta.used !== null) result["persona"] = personaMeta;
|
|
7662
7762
|
if (synthModel) result["synth_model"] = synthModel;
|
|
@@ -7664,15 +7764,16 @@ ${recombineSys}` : recombineSys },
|
|
|
7664
7764
|
if (upgraded) {
|
|
7665
7765
|
result["reasoning_upgrade"] = {
|
|
7666
7766
|
applied: true,
|
|
7667
|
-
|
|
7668
|
-
|
|
7767
|
+
provider: seat.provider.name,
|
|
7768
|
+
model: seat.provider.model,
|
|
7769
|
+
label: seat.label ?? seat.provider.model,
|
|
7669
7770
|
seat: "recombine/synthesis",
|
|
7670
7771
|
...coReasoned ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
|
|
7671
7772
|
};
|
|
7672
7773
|
} else if (upgradeRequested) {
|
|
7673
7774
|
result["reasoning_upgrade"] = {
|
|
7674
7775
|
applied: false,
|
|
7675
|
-
reason:
|
|
7776
|
+
reason: seat.skipReason ?? "the moderator couldn't be upgraded."
|
|
7676
7777
|
};
|
|
7677
7778
|
} else if (goal) {
|
|
7678
7779
|
const assessment = assessReasoning(goal);
|
|
@@ -8854,13 +8955,24 @@ async function runCoordinate(args, opts) {
|
|
|
8854
8955
|
};
|
|
8855
8956
|
}
|
|
8856
8957
|
const proposerName = typeof args["proposer"] === "string" && args["proposer"] ? args["proposer"] : selected[0].name;
|
|
8857
|
-
const
|
|
8958
|
+
const resolvedSynth = resolveModerator(
|
|
8959
|
+
opts.providers,
|
|
8960
|
+
args["synthesizer"],
|
|
8961
|
+
args["moderator"],
|
|
8962
|
+
opts.moderator
|
|
8963
|
+
);
|
|
8858
8964
|
const proposer = opts.providers[proposerName.toLowerCase()] ?? selected[0];
|
|
8859
|
-
const synth =
|
|
8965
|
+
const synth = resolvedSynth.provider ?? proposer;
|
|
8860
8966
|
const proposerForCall = superRequested ? retargetForSuper(proposer) : proposer;
|
|
8861
8967
|
const upgradeRequested = isUpgradeRequested(args) || superRequested;
|
|
8862
|
-
const
|
|
8863
|
-
|
|
8968
|
+
const seat = synthesisSeat({
|
|
8969
|
+
moderator: synth,
|
|
8970
|
+
pinned: resolvedSynth.provider !== void 0 && resolvedSynth.pinned,
|
|
8971
|
+
superRequested,
|
|
8972
|
+
upgradeRequested
|
|
8973
|
+
});
|
|
8974
|
+
const upgraded = seat.upgraded;
|
|
8975
|
+
const synthForCall = seat.provider;
|
|
8864
8976
|
let critics = [];
|
|
8865
8977
|
if (Array.isArray(args["critics"])) {
|
|
8866
8978
|
for (const n of args["critics"]) {
|
|
@@ -9011,7 +9123,11 @@ CRITIQUES:
|
|
|
9011
9123
|
${critiqueBlock}`
|
|
9012
9124
|
}
|
|
9013
9125
|
];
|
|
9014
|
-
const coProvider = pickCoReasoner(
|
|
9126
|
+
const coProvider = pickCoReasoner(
|
|
9127
|
+
opts.providers,
|
|
9128
|
+
upgradeRequested,
|
|
9129
|
+
synthForCall.name === "openai" ? synthForCall.model : void 0
|
|
9130
|
+
);
|
|
9015
9131
|
const coReason = await coReasonThenInject({
|
|
9016
9132
|
provider: coProvider,
|
|
9017
9133
|
messages: synthMessages,
|
|
@@ -9056,7 +9172,8 @@ ${critiqueBlock}`
|
|
|
9056
9172
|
critique_answers: scannedCritiques,
|
|
9057
9173
|
critique_structured: critiqueStructured,
|
|
9058
9174
|
synthesis_answer: synthAns,
|
|
9059
|
-
synthesis_structured: synthObj
|
|
9175
|
+
synthesis_structured: synthObj,
|
|
9176
|
+
moderator: { provider: synthForCall.name, model: synthForCall.model }
|
|
9060
9177
|
};
|
|
9061
9178
|
if (synthErrs.length > 0) result["synthesis_errors"] = synthErrs;
|
|
9062
9179
|
if (canaryLeaks.length > 0) result["canary_leaks"] = canaryLeaks;
|
|
@@ -9074,15 +9191,16 @@ ${critiqueBlock}`
|
|
|
9074
9191
|
result["reasoning_upgrade"] = {
|
|
9075
9192
|
applied: true,
|
|
9076
9193
|
// The model that actually ran the seat, not the one we'd have picked.
|
|
9077
|
-
|
|
9078
|
-
|
|
9194
|
+
provider: synthForCall.name,
|
|
9195
|
+
model: synthForCall.model,
|
|
9196
|
+
label: seat.label ?? synthForCall.model,
|
|
9079
9197
|
seat: "synthesizer",
|
|
9080
9198
|
...coReasonAns ? { co_reasoner: { model: coProvider.model, label: coReasonLabel(coProvider.model) } } : {}
|
|
9081
9199
|
};
|
|
9082
9200
|
} else if (upgradeRequested) {
|
|
9083
9201
|
result["reasoning_upgrade"] = {
|
|
9084
9202
|
applied: false,
|
|
9085
|
-
reason:
|
|
9203
|
+
reason: seat.skipReason ?? "the synthesizer couldn't be upgraded."
|
|
9086
9204
|
};
|
|
9087
9205
|
} else {
|
|
9088
9206
|
const assessment = assessReasoning(topic);
|
|
@@ -9688,10 +9806,12 @@ async function runDebate(args, opts) {
|
|
|
9688
9806
|
const earlyStopThreshold = Number.isFinite(earlyStopThresholdRaw) ? earlyStopThresholdRaw : 0.7;
|
|
9689
9807
|
let earlyStoppedRound = null;
|
|
9690
9808
|
let agreementBlock = null;
|
|
9691
|
-
const
|
|
9692
|
-
const
|
|
9809
|
+
const resolvedModerator = resolveModerator(opts.providers, args["moderator"], opts.moderator);
|
|
9810
|
+
const moderatorName = resolvedModerator.spec.provider;
|
|
9811
|
+
const moderator = resolvedModerator.provider ?? selected[0];
|
|
9693
9812
|
const upgradeRequested = isUpgradeRequested(args) || superRequested;
|
|
9694
|
-
const
|
|
9813
|
+
const seat = moderator ? synthesisSeat({ moderator, pinned: resolvedModerator.pinned, superRequested, upgradeRequested }) : null;
|
|
9814
|
+
const upgraded = seat?.upgraded ?? false;
|
|
9695
9815
|
for (let rnd = 1; rnd <= maxRounds; rnd++) {
|
|
9696
9816
|
const roundMessages = [
|
|
9697
9817
|
{
|
|
@@ -9776,8 +9896,10 @@ ${prior.body}` });
|
|
|
9776
9896
|
let personaMeta = null;
|
|
9777
9897
|
let coReasoned = false;
|
|
9778
9898
|
let coReasonerModel = null;
|
|
9899
|
+
let moderatorUsed = null;
|
|
9779
9900
|
if (moderator) {
|
|
9780
|
-
const synthProvider =
|
|
9901
|
+
const synthProvider = seat?.provider ?? moderator;
|
|
9902
|
+
moderatorUsed = { provider: synthProvider.name, model: synthProvider.model };
|
|
9781
9903
|
const condensed = buildModeratorTranscript(transcript);
|
|
9782
9904
|
const persona = buildPersonaInjection({
|
|
9783
9905
|
toolName: "debate",
|
|
@@ -9803,7 +9925,7 @@ TRANSCRIPT:
|
|
|
9803
9925
|
${condensed}`
|
|
9804
9926
|
}
|
|
9805
9927
|
];
|
|
9806
|
-
const openAiSeat = selected.find((p) => p.name === "openai")?.model;
|
|
9928
|
+
const openAiSeat = synthProvider.name === "openai" ? synthProvider.model : selected.find((p) => p.name === "openai")?.model;
|
|
9807
9929
|
const coProvider = pickCoReasoner(opts.providers, upgradeRequested, openAiSeat);
|
|
9808
9930
|
const coReason = await coReasonThenInject({
|
|
9809
9931
|
provider: coProvider,
|
|
@@ -9863,20 +9985,22 @@ ${condensed}`
|
|
|
9863
9985
|
transcript,
|
|
9864
9986
|
synthesis
|
|
9865
9987
|
};
|
|
9988
|
+
if (moderatorUsed) result["moderator"] = moderatorUsed;
|
|
9866
9989
|
if (personaMeta && personaMeta.used !== null) result["persona"] = personaMeta;
|
|
9867
9990
|
if (compactionMeta) result["round_compaction"] = compactionMeta;
|
|
9868
9991
|
if (upgraded) {
|
|
9869
9992
|
result["reasoning_upgrade"] = {
|
|
9870
9993
|
applied: true,
|
|
9871
|
-
|
|
9872
|
-
|
|
9994
|
+
provider: seat.provider.name,
|
|
9995
|
+
model: seat.provider.model,
|
|
9996
|
+
label: seat.label ?? seat.provider.model,
|
|
9873
9997
|
seat: "moderator synthesis",
|
|
9874
9998
|
...coReasoned && coReasonerModel ? { co_reasoner: { model: coReasonerModel, label: coReasonLabel(coReasonerModel) } } : {}
|
|
9875
9999
|
};
|
|
9876
10000
|
} else if (upgradeRequested) {
|
|
9877
10001
|
result["reasoning_upgrade"] = {
|
|
9878
10002
|
applied: false,
|
|
9879
|
-
reason:
|
|
10003
|
+
reason: seat?.skipReason ?? "no moderator was available for the upgrade."
|
|
9880
10004
|
};
|
|
9881
10005
|
} else {
|
|
9882
10006
|
const assessment = assessReasoning(topic);
|
|
@@ -10505,12 +10629,25 @@ function runListProviders(args, opts) {
|
|
|
10505
10629
|
}
|
|
10506
10630
|
const result = {
|
|
10507
10631
|
providers,
|
|
10508
|
-
moderator_default: opts.moderatorDefault ??
|
|
10632
|
+
moderator_default: opts.moderatorDefault ?? DEFAULT_MODERATOR,
|
|
10633
|
+
// Who will actually moderate right now: the configured spec if that
|
|
10634
|
+
// provider has a key, else the first available (what the tools fall to).
|
|
10635
|
+
moderator: describeModerator(opts.providers ?? {}, opts.moderatorDefault),
|
|
10509
10636
|
usage_hint: USAGE_HINT
|
|
10510
10637
|
};
|
|
10511
10638
|
if (superRequested) result["super_mode"] = true;
|
|
10512
10639
|
return result;
|
|
10513
10640
|
}
|
|
10641
|
+
function describeModerator(providers, spec) {
|
|
10642
|
+
const r = resolveModerator(providers, spec);
|
|
10643
|
+
const p = r.provider ?? Object.values(providers)[0];
|
|
10644
|
+
return {
|
|
10645
|
+
spec: spec ?? DEFAULT_MODERATOR,
|
|
10646
|
+
provider: p?.name ?? null,
|
|
10647
|
+
model: p?.model ?? null,
|
|
10648
|
+
available: r.provider !== void 0
|
|
10649
|
+
};
|
|
10650
|
+
}
|
|
10514
10651
|
|
|
10515
10652
|
// src/tools/models.ts
|
|
10516
10653
|
var KNOWN_PROVIDERS7 = [
|
|
@@ -12060,7 +12197,7 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
|
|
|
12060
12197
|
var DEFAULT_PACKAGE = "crosscheck-cli";
|
|
12061
12198
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
12062
12199
|
function engineVersion() {
|
|
12063
|
-
return true ? "0.2.
|
|
12200
|
+
return true ? "0.2.31" : "0.0.0-dev";
|
|
12064
12201
|
}
|
|
12065
12202
|
function defaultUpdateCachePath() {
|
|
12066
12203
|
const base = process.env["CROSSCHECK_DATA_DIR"] || path9.join(os.homedir() || os.tmpdir(), ".crosscheck");
|
|
@@ -12505,7 +12642,7 @@ var HELP_CATALOG = [
|
|
|
12505
12642
|
primaryArg: "topic",
|
|
12506
12643
|
summary: "Bounded multi-round debate between models, ending in a synthesized recommendation.",
|
|
12507
12644
|
example: "xc debate should we adopt a monorepo?",
|
|
12508
|
-
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.
|
|
12645
|
+
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.`
|
|
12509
12646
|
},
|
|
12510
12647
|
{
|
|
12511
12648
|
name: "coordinate",
|
|
@@ -12748,6 +12885,10 @@ function renderHelpCommand(entry) {
|
|
|
12748
12885
|
}
|
|
12749
12886
|
|
|
12750
12887
|
// src/tools/index.ts
|
|
12888
|
+
var MODERATOR_PARAM = {
|
|
12889
|
+
type: "string",
|
|
12890
|
+
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`).'
|
|
12891
|
+
};
|
|
12751
12892
|
function defineTool(opts) {
|
|
12752
12893
|
return {
|
|
12753
12894
|
name: opts.name,
|
|
@@ -12982,7 +13123,7 @@ function createTool(o, toolName, cheapDefault) {
|
|
|
12982
13123
|
instruction: { type: "string" },
|
|
12983
13124
|
documents: { type: "array", items: { type: "string" } },
|
|
12984
13125
|
providers: { type: "array", items: { type: "string" } },
|
|
12985
|
-
moderator:
|
|
13126
|
+
moderator: MODERATOR_PARAM,
|
|
12986
13127
|
constraints: { type: "string" },
|
|
12987
13128
|
target_path: { type: "string" },
|
|
12988
13129
|
session_id: { type: "string" },
|
|
@@ -13023,7 +13164,7 @@ function orchestrateTool(providers, allowlist, moderator, storage, pricing, brea
|
|
|
13023
13164
|
dag: { type: "object", additionalProperties: true },
|
|
13024
13165
|
context: { type: "string" },
|
|
13025
13166
|
providers: { type: "array", items: { type: "string" } },
|
|
13026
|
-
moderator:
|
|
13167
|
+
moderator: MODERATOR_PARAM,
|
|
13027
13168
|
fail_fast: { type: "boolean" },
|
|
13028
13169
|
plan_only: { type: "boolean" },
|
|
13029
13170
|
cheap_mode: { type: "boolean" },
|
|
@@ -13402,7 +13543,7 @@ function planTool(providers, allowlist, storage, breakers, transcriptsDir, repoR
|
|
|
13402
13543
|
constraints: { type: "string" },
|
|
13403
13544
|
context: { type: "string" },
|
|
13404
13545
|
providers: { type: "array", items: { type: "string" } },
|
|
13405
|
-
moderator:
|
|
13546
|
+
moderator: MODERATOR_PARAM,
|
|
13406
13547
|
session_id: { type: "string" },
|
|
13407
13548
|
structured: { type: "boolean" },
|
|
13408
13549
|
mode: { type: "string", enum: ["fast", "thorough"] },
|
|
@@ -13464,7 +13605,7 @@ function coordinateTool(providers, allowlist, innerCallers, storage, breakers, t
|
|
|
13464
13605
|
providers: { type: "array", items: { type: "string" } },
|
|
13465
13606
|
proposer: { type: "string" },
|
|
13466
13607
|
synthesizer: { type: "string" },
|
|
13467
|
-
moderator:
|
|
13608
|
+
moderator: MODERATOR_PARAM,
|
|
13468
13609
|
critics: { type: "array", items: { type: "string" } },
|
|
13469
13610
|
session_id: { type: "string" },
|
|
13470
13611
|
untrusted_input: { type: "boolean" },
|
|
@@ -13497,7 +13638,7 @@ function debateTool(providers, allowlist, innerCallers, storage, pricing, breake
|
|
|
13497
13638
|
context: { type: "string" },
|
|
13498
13639
|
max_rounds: { type: "integer", minimum: 1 },
|
|
13499
13640
|
providers: { type: "array", items: { type: "string" } },
|
|
13500
|
-
moderator:
|
|
13641
|
+
moderator: MODERATOR_PARAM,
|
|
13501
13642
|
session_id: { type: "string" },
|
|
13502
13643
|
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.' },
|
|
13503
13644
|
structured: { type: "boolean" },
|
|
@@ -13714,6 +13855,7 @@ var KEY_ENV = {
|
|
|
13714
13855
|
qwen: "QWEN_API_KEY"
|
|
13715
13856
|
};
|
|
13716
13857
|
function createCrosscheck(opts) {
|
|
13858
|
+
const moderator = opts.moderator ?? DEFAULT_MODERATOR;
|
|
13717
13859
|
const env = {};
|
|
13718
13860
|
for (const [provider, key] of Object.entries(opts.keys)) {
|
|
13719
13861
|
const varName = KEY_ENV[provider.trim().toLowerCase()];
|
|
@@ -13721,7 +13863,7 @@ function createCrosscheck(opts) {
|
|
|
13721
13863
|
}
|
|
13722
13864
|
const pricingDoc = pricing_default;
|
|
13723
13865
|
const providers = buildProviders({ env, pricing: pricingDoc });
|
|
13724
|
-
const tools = registerCoreTools({ providers, pricing: pricingDoc });
|
|
13866
|
+
const tools = registerCoreTools({ providers, pricing: pricingDoc, moderatorDefault: moderator });
|
|
13725
13867
|
return {
|
|
13726
13868
|
listTools: () => [...tools.keys()],
|
|
13727
13869
|
call: (name, args) => {
|
|
@@ -13734,11 +13876,11 @@ function createCrosscheck(opts) {
|
|
|
13734
13876
|
// same opts the registry wires for confer in a browser build: providers
|
|
13735
13877
|
// + pricing + default moderator; storage/innerCallers aren't
|
|
13736
13878
|
// available here, matching the non-streaming path.
|
|
13737
|
-
conferStream: (args, onAnswer) => runConfer(args, { providers, pricing: pricingDoc, moderator
|
|
13879
|
+
conferStream: (args, onAnswer) => runConfer(args, { providers, pricing: pricingDoc, moderator, onAnswer }),
|
|
13738
13880
|
debateStream: (args, { onTurn, onSynthesis } = {}) => runDebate(args, {
|
|
13739
13881
|
providers,
|
|
13740
13882
|
pricing: pricingDoc,
|
|
13741
|
-
moderator
|
|
13883
|
+
moderator,
|
|
13742
13884
|
...onTurn ? { onTurn: (entry, round, provider) => onTurn(entry, round, provider) } : {},
|
|
13743
13885
|
...onSynthesis ? { onSynthesis: (s) => onSynthesis(s) } : {}
|
|
13744
13886
|
})
|