@warmdrift/kgauto-compiler 2.0.0-alpha.9 → 2.0.0-alpha.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +176 -46
  2. package/dist/brain-proxy.d.mts +113 -0
  3. package/dist/brain-proxy.d.ts +113 -0
  4. package/dist/brain-proxy.js +193 -0
  5. package/dist/brain-proxy.mjs +6 -0
  6. package/dist/chunk-54IXD5BT.mjs +858 -0
  7. package/dist/chunk-65ZMX5OT.mjs +169 -0
  8. package/dist/chunk-AUZTO6Q5.mjs +219 -0
  9. package/dist/{chunk-5TI6PNSK.mjs → chunk-BVEXV5KC.mjs} +11 -0
  10. package/dist/chunk-NBO4R5PC.mjs +313 -0
  11. package/dist/chunk-P3TOAEG4.mjs +56 -0
  12. package/dist/chunk-RO22VFIF.mjs +29 -0
  13. package/dist/chunk-T53ISC2F.mjs +2008 -0
  14. package/dist/dialect.d.mts +41 -3
  15. package/dist/dialect.d.ts +41 -3
  16. package/dist/dialect.js +14 -2
  17. package/dist/dialect.mjs +5 -3
  18. package/dist/glassbox/index.d.mts +59 -0
  19. package/dist/glassbox/index.d.ts +59 -0
  20. package/dist/glassbox/index.js +312 -0
  21. package/dist/glassbox/index.mjs +12 -0
  22. package/dist/glassbox-routes/format.d.mts +24 -0
  23. package/dist/glassbox-routes/format.d.ts +24 -0
  24. package/dist/glassbox-routes/format.js +86 -0
  25. package/dist/glassbox-routes/format.mjs +18 -0
  26. package/dist/glassbox-routes/index.d.mts +191 -0
  27. package/dist/glassbox-routes/index.d.ts +191 -0
  28. package/dist/glassbox-routes/index.js +3326 -0
  29. package/dist/glassbox-routes/index.mjs +668 -0
  30. package/dist/glassbox-routes/react/index.d.mts +74 -0
  31. package/dist/glassbox-routes/react/index.d.ts +74 -0
  32. package/dist/glassbox-routes/react/index.js +819 -0
  33. package/dist/glassbox-routes/react/index.mjs +754 -0
  34. package/dist/index.d.mts +3782 -99
  35. package/dist/index.d.ts +3782 -99
  36. package/dist/index.js +10970 -2043
  37. package/dist/index.mjs +6279 -276
  38. package/dist/ir-CTx026t0.d.ts +1887 -0
  39. package/dist/ir-DeYMLWge.d.mts +1887 -0
  40. package/dist/key-health.d.mts +166 -0
  41. package/dist/key-health.d.ts +166 -0
  42. package/dist/key-health.js +247 -0
  43. package/dist/key-health.mjs +12 -0
  44. package/dist/profiles.d.mts +352 -2
  45. package/dist/profiles.d.ts +352 -2
  46. package/dist/profiles.js +1412 -52
  47. package/dist/profiles.mjs +19 -1
  48. package/dist/types-BKbRtmUb.d.ts +131 -0
  49. package/dist/types-Cp9ot1HV.d.ts +142 -0
  50. package/dist/types-DD36cCbZ.d.mts +142 -0
  51. package/dist/types-cBzinzUR.d.mts +131 -0
  52. package/package.json +62 -9
  53. package/dist/chunk-3KVKELZN.mjs +0 -657
  54. package/dist/profiles-BYVOc1eW.d.ts +0 -700
  55. package/dist/profiles-NUZOIzGr.d.mts +0 -700
@@ -0,0 +1,3326 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/glassbox-routes/index.ts
21
+ var glassbox_routes_exports = {};
22
+ __export(glassbox_routes_exports, {
23
+ COUNTERFACTUAL_MAX_RESULTS: () => COUNTERFACTUAL_MAX_RESULTS,
24
+ COUNTERFACTUAL_MIN_SAVINGS_RATIO: () => COUNTERFACTUAL_MIN_SAVINGS_RATIO,
25
+ computeCounterfactuals: () => computeCounterfactuals,
26
+ createGlassboxRoutes: () => createGlassboxRoutes,
27
+ rowToDetail: () => rowToDetail,
28
+ rowToSummary: () => rowToSummary
29
+ });
30
+ module.exports = __toCommonJS(glassbox_routes_exports);
31
+
32
+ // src/glassbox-routes/auth.ts
33
+ var JSON_HEADERS = { "Content-Type": "application/json" };
34
+ function jsonError(status, code) {
35
+ return new Response(JSON.stringify({ error: code }), {
36
+ status,
37
+ headers: JSON_HEADERS
38
+ });
39
+ }
40
+ function tokensEqual(a, b) {
41
+ if (a.length !== b.length) return false;
42
+ let mismatch = 0;
43
+ for (let i = 0; i < a.length; i++) {
44
+ mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
45
+ }
46
+ return mismatch === 0;
47
+ }
48
+ function checkAuth(req, config) {
49
+ const authHeader = req.headers.get("Authorization") ?? "";
50
+ const match = /^Bearer\s+(.+)$/i.exec(authHeader);
51
+ const provided = match?.[1]?.trim() ?? "";
52
+ if (!provided || !tokensEqual(provided, config.installToken)) {
53
+ return jsonError(401, "unauthorized");
54
+ }
55
+ const origin = req.headers.get("Origin") ?? "";
56
+ const xExtId = req.headers.get("X-Glassbox-Extension-Id") ?? "";
57
+ const expectedOrigin = `chrome-extension://${config.extensionId}`;
58
+ const originOk = origin === expectedOrigin;
59
+ const xExtOk = xExtId.length > 0 && tokensEqual(xExtId, config.extensionId);
60
+ if (!originOk && !xExtOk) {
61
+ return jsonError(403, "forbidden_origin");
62
+ }
63
+ return null;
64
+ }
65
+
66
+ // src/profiles.ts
67
+ var ANTHROPIC_LOWERING_BASE = {
68
+ system: { mode: "inline" },
69
+ cache: {
70
+ strategy: "cache_control",
71
+ minTokens: 1024,
72
+ discount: 0.1,
73
+ ttlSeconds: 300
74
+ },
75
+ tools: { format: "anthropic" }
76
+ };
77
+ var GOOGLE_LOWERING_BASE = {
78
+ system: { mode: "separate", field: "systemInstruction" },
79
+ cache: {
80
+ strategy: "cachedContent",
81
+ minTokens: 4096,
82
+ discount: 0.25,
83
+ ttlSeconds: 3600
84
+ },
85
+ tools: { format: "google" }
86
+ };
87
+ var PROFILES_RAW = [
88
+ // ── Anthropic ──
89
+ {
90
+ // VERIFIED 2026-06-21 against the claude-api reference. Claude Fable 5 is
91
+ // Anthropic's most capable model — a NEW tier ABOVE the opus/sonnet/haiku
92
+ // line (the id has no MAJOR-MINOR version, just `-5`) at 2× Opus pricing
93
+ // ($10/$50 vs $5/$25). Hand-authored, not cloned: the onboarder regex can't
94
+ // match this shape (new family + single-segment version), AND a clone would
95
+ // carry the wrong (opus) pricing. status:'preview' — no brain evidence yet;
96
+ // it earns per-archetype placement through the machinery like any model, and
97
+ // at 2× Opus the quality-floor + cost gates do real work before it leads.
98
+ // API quirk (claude-api ref): an explicit `thinking:{type:"disabled"}` 400s
99
+ // on Fable 5 (omit instead) — MOOT here: ANTHROPIC_LOWERING_BASE carries no
100
+ // `thinking` field, so kgauto never sends the param. (L-049/L-081.)
101
+ id: "claude-fable-5",
102
+ verifiedAgainstDocs: "2026-06-21",
103
+ provider: "anthropic",
104
+ status: "current",
105
+ // s76 2026-07-25: GA since 2026-06-09 per docs; was preview.
106
+ maxContextTokens: 1e6,
107
+ maxOutputTokens: 128e3,
108
+ maxTools: 64,
109
+ parallelToolCalls: true,
110
+ structuredOutput: "grammar",
111
+ systemPromptMode: "inline",
112
+ streaming: true,
113
+ cliffs: [],
114
+ costInputPer1m: 10,
115
+ costOutputPer1m: 50,
116
+ lowering: ANTHROPIC_LOWERING_BASE,
117
+ recovery: [
118
+ {
119
+ signal: "rate_limit",
120
+ action: "escalate",
121
+ reason: "429 from Anthropic \u2014 escalate to fallback chain"
122
+ },
123
+ {
124
+ signal: "model_not_found",
125
+ action: "escalate",
126
+ reason: "Model deprecated/renamed \u2014 escalate (L-061)"
127
+ }
128
+ ],
129
+ strengths: ["reasoning", "agentic_coding", "long_context", "reliable_tool_use", "structured_output"],
130
+ weaknesses: ["cost", "latency"],
131
+ notes: "Most capable Anthropic model (2026-06); a new tier ABOVE Opus at 2\xD7 pricing ($10/$50). 1M ctx, 128k out. Verified 2026-06-21 against the claude-api reference. status:preview \u2014 no brain evidence yet; earns placement via the machinery (cost + quality-floor gates govern). latencyTier derives `slow` (weaknesses includes latency \u2014 minutes-long turns on hard tasks). The thinking-disabled-400 API quirk is moot for kgauto (Anthropic lowering emits no thinking param).",
132
+ // archetypePerf cloned from claude-opus-4-8 as a conservative estimate
133
+ // (Fable >= Opus in capability, but unmeasured — no fabricated superiority).
134
+ // Re-rank once brain rows exist.
135
+ archetypePerf: {
136
+ critique: 10,
137
+ plan: 10,
138
+ generate: 9,
139
+ ask: 9,
140
+ extract: 9,
141
+ transform: 9,
142
+ hunt: 8,
143
+ summarize: 8,
144
+ classify: 8
145
+ }
146
+ },
147
+ {
148
+ // Auto-onboarded 2026-06-04 from `claude-opus-4-7`; VERIFIED 2026-06-21
149
+ // against the claude-api reference. Opus 4.8 shares 4.7's surface exactly —
150
+ // 1M ctx, 128k out, $5/$25, no new breaking changes — so the clone's
151
+ // capability data was accurate (confirmed field-by-field). archetypePerf is
152
+ // still cloned from 4.7 (a sound estimate: 4.8 >= 4.7) — re-rank once brain
153
+ // rows exist. Promoted to status:'current' 2026-06-21 (Sacha: "include Claude
154
+ // Opus 4.8") — now live-selectable. Ties with the still-current claude-opus-4-7
155
+ // (identical $5/$25 + cloned perf) resolve to 4-8 by array order. (L-049/L-081.)
156
+ id: "claude-opus-4-8",
157
+ verifiedAgainstDocs: "2026-06-21",
158
+ provider: "anthropic",
159
+ status: "legacy",
160
+ // s76 2026-07-25: docs list as LEGACY (Opus 5 generation is current).
161
+ maxContextTokens: 1e6,
162
+ maxOutputTokens: 128e3,
163
+ maxTools: 64,
164
+ parallelToolCalls: true,
165
+ structuredOutput: "grammar",
166
+ systemPromptMode: "inline",
167
+ streaming: true,
168
+ cliffs: [],
169
+ costInputPer1m: 5,
170
+ costOutputPer1m: 25,
171
+ lowering: ANTHROPIC_LOWERING_BASE,
172
+ recovery: [
173
+ {
174
+ signal: "rate_limit",
175
+ action: "escalate",
176
+ reason: "429 from Anthropic \u2014 escalate to fallback chain"
177
+ },
178
+ {
179
+ signal: "model_not_found",
180
+ action: "escalate",
181
+ reason: "Model deprecated/renamed \u2014 escalate (L-061)"
182
+ }
183
+ ],
184
+ strengths: ["reasoning", "agentic_coding", "long_context", "reliable_tool_use", "structured_output"],
185
+ weaknesses: ["cost", "latency"],
186
+ notes: "Frontier Opus (2026-06), the current recommended Opus-tier default. Verified 2026-06-21 against the claude-api reference: 1M ctx, 128k out, $5/$25 \u2014 identical surface to 4.7, no new breaking changes. Promoted to status:current 2026-06-21 (live-selectable); coexists with claude-opus-4-7 (still current).",
187
+ // Cloned archetypePerf from claude-opus-4-7 — re-evaluate once brain rows exist.
188
+ archetypePerf: {
189
+ critique: 10,
190
+ plan: 10,
191
+ generate: 9,
192
+ ask: 9,
193
+ extract: 9,
194
+ transform: 9,
195
+ hunt: 8,
196
+ summarize: 8,
197
+ classify: 8
198
+ }
199
+ },
200
+ {
201
+ // ── Claude Opus 5 — added s76 (2026-07-25, alpha.72) ──────────────────
202
+ // The model every seat in the portfolio actually runs, absent from the
203
+ // roster until now. Found by a catalog QA, not by the release watcher:
204
+ // the watcher detects unknown IDs on a provider's /models endpoint, but
205
+ // detection was never wired to ingestion, so the gap sat open. (That is
206
+ // what `scripts/catalog-sync.mjs` exists to close.)
207
+ //
208
+ // PROVENANCE — doc-verified 2026-07-25 against
209
+ // platform.claude.com/docs/en/docs/about-claude/models/overview:
210
+ // 1M context · 128k max output · $5/$25 per MTok · adaptive thinking YES
211
+ // · extended thinking NO · `effort` defaults to 'high' on the Claude API
212
+ // · reliable knowledge cutoff May 2026.
213
+ //
214
+ // FAMILY-INHERITED (not doc-read): maxTools, parallelToolCalls,
215
+ // structuredOutput, systemPromptMode, streaming. These are identical
216
+ // across opus-4-6 / 4-7 / 4-8 / sonnet-5 / fable-5 — five consecutive
217
+ // models — so they are a structural provider constant rather than a
218
+ // per-model value. Deliberately distinguished from the gemini-3.1-flash-lite
219
+ // failure, where a template-clone of PRICE (the field that actually varies,
220
+ // there by 2.5-3.75x) went unverified for 65 days.
221
+ id: "claude-opus-5",
222
+ verifiedAgainstDocs: "2026-07-25",
223
+ provider: "anthropic",
224
+ status: "current",
225
+ maxContextTokens: 1e6,
226
+ maxOutputTokens: 128e3,
227
+ maxTools: 64,
228
+ parallelToolCalls: true,
229
+ structuredOutput: "grammar",
230
+ systemPromptMode: "inline",
231
+ streaming: true,
232
+ cliffs: [],
233
+ costInputPer1m: 5,
234
+ costOutputPer1m: 25,
235
+ lowering: ANTHROPIC_LOWERING_BASE,
236
+ recovery: [
237
+ {
238
+ signal: "rate_limit",
239
+ action: "escalate",
240
+ reason: "429 from Anthropic \u2014 escalate to fallback chain"
241
+ },
242
+ {
243
+ signal: "model_not_found",
244
+ action: "escalate",
245
+ reason: "Model deprecated/renamed \u2014 escalate (L-061)"
246
+ }
247
+ ],
248
+ strengths: ["reasoning", "agentic_coding", "long_context", "reliable_tool_use", "structured_output"],
249
+ weaknesses: ["cost", "latency"],
250
+ notes: "Anthropic frontier for complex agentic coding + enterprise work; the Opus-tier current default as of 2026-07-25, superseding 4.8/4.7/4.6 (all now legacy). Doc-verified 2026-07-25: 1M ctx, 128k out, $5/$25, adaptive thinking (extended thinking NOT supported \u2014 that is Haiku 4.5 only in this generation), `effort` defaults to high on the Claude API. Price is unchanged from 4.5 through 5 \u2014 the tier moved without a cost change.",
251
+ // COLD-START PRIOR cloned from claude-opus-4-8. Self-labelling: below
252
+ // MEASURED_GROUNDING_MIN_N (10) brain rows these resolve as
253
+ // grounding:'judgment', so nothing reads them as measured until they are.
254
+ archetypePerf: {
255
+ critique: 10,
256
+ plan: 10,
257
+ generate: 9,
258
+ ask: 9,
259
+ extract: 9,
260
+ transform: 9,
261
+ hunt: 8,
262
+ summarize: 8,
263
+ classify: 8
264
+ }
265
+ },
266
+ {
267
+ id: "claude-opus-4-7",
268
+ verifiedAgainstDocs: "2026-08-14",
269
+ provider: "anthropic",
270
+ status: "legacy",
271
+ // s76 2026-07-25: docs list as LEGACY (Opus 5 is current). Bundle said current while the brain already said legacy — the two disagreed.
272
+ maxContextTokens: 1e6,
273
+ maxOutputTokens: 128e3,
274
+ maxTools: 64,
275
+ parallelToolCalls: true,
276
+ structuredOutput: "grammar",
277
+ systemPromptMode: "inline",
278
+ streaming: true,
279
+ cliffs: [],
280
+ costInputPer1m: 5,
281
+ costOutputPer1m: 25,
282
+ lowering: ANTHROPIC_LOWERING_BASE,
283
+ recovery: [
284
+ {
285
+ signal: "rate_limit",
286
+ action: "escalate",
287
+ reason: "429 from Anthropic \u2014 escalate to fallback chain"
288
+ },
289
+ {
290
+ signal: "model_not_found",
291
+ action: "escalate",
292
+ reason: "Model deprecated/renamed \u2014 escalate (L-061)"
293
+ }
294
+ ],
295
+ strengths: ["reasoning", "agentic_coding", "long_context", "reliable_tool_use", "structured_output"],
296
+ weaknesses: ["cost", "latency"],
297
+ notes: "Frontier (2026-05). Step-change improvement over 4.6 in agentic coding. Adaptive thinking only \u2014 no extended-thinking toggle. 1M context, 128k max output.",
298
+ // Frontier perf. Drops on archetypes where parallel-tool throughput
299
+ // (hunt) or low-budget cost-sensitivity (classify/summarize) matters
300
+ // more than reasoning depth.
301
+ archetypePerf: {
302
+ critique: 10,
303
+ plan: 10,
304
+ generate: 9,
305
+ ask: 9,
306
+ extract: 9,
307
+ transform: 9,
308
+ hunt: 8,
309
+ // strong but Flash dominates parallel tool throughput
310
+ summarize: 8,
311
+ // overkill for tolerant archetype; cost-out of frontier
312
+ classify: 8
313
+ // overkill; brain-validated cheaper models cover this
314
+ }
315
+ },
316
+ {
317
+ id: "claude-opus-4-6",
318
+ verifiedAgainstDocs: "2026-08-14",
319
+ provider: "anthropic",
320
+ status: "legacy",
321
+ maxContextTokens: 1e6,
322
+ maxOutputTokens: 128e3,
323
+ maxTools: 64,
324
+ parallelToolCalls: true,
325
+ structuredOutput: "grammar",
326
+ systemPromptMode: "inline",
327
+ streaming: true,
328
+ cliffs: [],
329
+ costInputPer1m: 5,
330
+ costOutputPer1m: 25,
331
+ lowering: ANTHROPIC_LOWERING_BASE,
332
+ recovery: [
333
+ {
334
+ signal: "rate_limit",
335
+ action: "escalate",
336
+ reason: "429 from Anthropic \u2014 escalate to fallback chain"
337
+ },
338
+ {
339
+ signal: "model_not_found",
340
+ action: "escalate",
341
+ reason: "Model deprecated/renamed \u2014 escalate (L-061)"
342
+ }
343
+ ],
344
+ strengths: ["reasoning", "long_context", "reliable_tool_use", "structured_output", "extended_thinking"],
345
+ weaknesses: ["cost", "latency"],
346
+ notes: "Predecessor to 4.7. Still current in Anthropic legacy table. Same pricing as 4.7 \u2014 choose 4.7 unless you need extended-thinking budget control (4.7 is adaptive-only).",
347
+ // One notch below 4.7 across the board — extended-thinking edge does
348
+ // not flip any archetype ranking. Legacy: chains should prefer 4.7.
349
+ archetypePerf: {
350
+ critique: 9,
351
+ plan: 9,
352
+ generate: 9,
353
+ ask: 9,
354
+ extract: 9,
355
+ transform: 9,
356
+ hunt: 7,
357
+ summarize: 8,
358
+ classify: 8
359
+ }
360
+ },
361
+ {
362
+ id: "claude-sonnet-4-6",
363
+ verifiedAgainstDocs: "2026-08-14",
364
+ provider: "anthropic",
365
+ status: "legacy",
366
+ // s76 2026-07-25: docs list as LEGACY (Sonnet 5 is current).
367
+ maxContextTokens: 1e6,
368
+ maxOutputTokens: 64e3,
369
+ maxTools: 64,
370
+ parallelToolCalls: true,
371
+ structuredOutput: "grammar",
372
+ systemPromptMode: "inline",
373
+ streaming: true,
374
+ cliffs: [],
375
+ costInputPer1m: 3,
376
+ costOutputPer1m: 15,
377
+ lowering: ANTHROPIC_LOWERING_BASE,
378
+ recovery: [
379
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate" },
380
+ { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
381
+ ],
382
+ strengths: ["quality", "tool_use", "long_context", "cache_friendly", "extended_thinking"],
383
+ weaknesses: [],
384
+ notes: "Workhorse. Best price/quality for most multi-turn agentic work. 1M context, 64k max output.",
385
+ // Master plan §6.2 anchor. Tier 0 for plan/generate/ask/extract/transform
386
+ // in starter chains; tier 1 cross-provider for hunt/summarize/classify.
387
+ archetypePerf: {
388
+ ask: 9,
389
+ generate: 9,
390
+ plan: 9,
391
+ critique: 9,
392
+ extract: 9,
393
+ transform: 9,
394
+ hunt: 7,
395
+ // strong but Flash beats on parallel tool throughput
396
+ summarize: 8,
397
+ // overkill for tolerant archetype
398
+ classify: 8
399
+ // overkill
400
+ }
401
+ },
402
+ {
403
+ id: "claude-haiku-4-5",
404
+ verifiedAgainstDocs: "2026-08-14",
405
+ provider: "anthropic",
406
+ status: "current",
407
+ maxContextTokens: 2e5,
408
+ maxOutputTokens: 64e3,
409
+ maxTools: 32,
410
+ parallelToolCalls: true,
411
+ structuredOutput: "grammar",
412
+ systemPromptMode: "inline",
413
+ streaming: true,
414
+ cliffs: [
415
+ {
416
+ metric: "tool_count",
417
+ threshold: 16,
418
+ action: "drop_to_top_relevant",
419
+ reason: "Haiku reliability degrades above ~16 tools"
420
+ },
421
+ {
422
+ // alpha.78 — the declared `structuredOutput: 'grammar'` does NOT
423
+ // hold on long-input summarize. MEASURED (brain, playbacksam):
424
+ // 21 disambiguated `structured_output_parse_failed` fallover rows
425
+ // 2026-07-22..27, tokens_in 12,280–31,450; PB's gate counted 20/20
426
+ // in-window failures. Clean traffic p50 sits at ~9K tokens_in, so
427
+ // 12K gates the failing band without touching the working one.
428
+ // Short-input summarize carries no failure evidence and stays
429
+ // ungated — this is why it's a cliff, not an archetype-wide
430
+ // `structuredOutputHint: 'avoid'`.
431
+ metric: "input_tokens",
432
+ threshold: 12e3,
433
+ action: "quality_gate_structured",
434
+ whenIntent: "summarize",
435
+ reason: "Structured-output parse failures at 100% in-window on long-input summarize (measured on playbacksam, 2026-07-25..27; haiku only led when input size made price dominate, then failed every time)."
436
+ }
437
+ ],
438
+ costInputPer1m: 1,
439
+ costOutputPer1m: 5,
440
+ lowering: ANTHROPIC_LOWERING_BASE,
441
+ recovery: [
442
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate to Sonnet" }
443
+ ],
444
+ strengths: ["speed", "cost", "classification", "cache_friendly", "extended_thinking"],
445
+ weaknesses: ["complex_reasoning", "large_tool_sets"],
446
+ notes: "Cheapest Anthropic. Great for classify, summarize, ask shapes. 200k context, 64k max output. API alias `claude-haiku-4-5` resolves to dated snapshot `claude-haiku-4-5-20251001`.",
447
+ // Tier 1 cross-provider anchor for short-output chains (classify/
448
+ // summarize/extract/transform). Falls off on plan/critique where
449
+ // reasoning depth matters; competes with Pro on cost+latency.
450
+ archetypePerf: {
451
+ classify: 8,
452
+ summarize: 8,
453
+ ask: 7,
454
+ transform: 7,
455
+ extract: 7,
456
+ hunt: 6,
457
+ // tool reliability drops at 16 — cliff guard fires
458
+ generate: 6,
459
+ plan: 5,
460
+ critique: 4
461
+ // reasoning depth gap vs Sonnet/Opus
462
+ }
463
+ },
464
+ // ── Google ──
465
+ {
466
+ id: "gemini-2.5-flash",
467
+ verifiedAgainstDocs: "2026-08-14",
468
+ provider: "google",
469
+ status: "current",
470
+ maxContextTokens: 1048576,
471
+ maxOutputTokens: 65535,
472
+ maxTools: 128,
473
+ parallelToolCalls: true,
474
+ structuredOutput: "native",
475
+ systemPromptMode: "separate",
476
+ streaming: true,
477
+ cliffs: [
478
+ {
479
+ metric: "input_tokens",
480
+ threshold: 8e3,
481
+ action: "downgrade_quality_warning",
482
+ reason: "Quality degrades significantly above ~8K context tokens"
483
+ },
484
+ {
485
+ metric: "tool_count",
486
+ threshold: 20,
487
+ action: "drop_to_top_relevant",
488
+ reason: "Tool reliability drops above ~20 tools (despite 128 hard limit)"
489
+ },
490
+ {
491
+ metric: "thinking_with_short_output",
492
+ threshold: 1,
493
+ action: "force_thinking_budget_zero",
494
+ reason: "Thinking tokens consume maxOutputTokens \u2014 empty response if drained"
495
+ },
496
+ {
497
+ // s11 trust artifact (2026-05-02): brain showed 5/5 empty rate on
498
+ // tt-intelligence/summarize/gemini-2.5-flash with tools offered.
499
+ // v1's disable_thinking_for_short_output already fired and didn't
500
+ // help — disabling thinking is necessary but not sufficient. Tools
501
+ // present + summarize intent confuses Flash into a no-output state
502
+ // (likely tool-decision purgatory). Strip tools entirely for this
503
+ // archetype on this model.
504
+ metric: "tool_count",
505
+ threshold: 1,
506
+ whenIntent: "summarize",
507
+ action: "strip_tools",
508
+ reason: "Gemini Flash returns empty when summarize intent has tools offered (5/5 empty rate observed in v1 prod 2026-04-19, replayed into v2 brain 2026-04-29)"
509
+ }
510
+ ],
511
+ costInputPer1m: 0.3,
512
+ costOutputPer1m: 2.5,
513
+ lowering: {
514
+ ...GOOGLE_LOWERING_BASE,
515
+ thinking: { field: "generationConfig.thinkingConfig.thinkingBudget", default: "auto" }
516
+ },
517
+ recovery: [
518
+ {
519
+ signal: "empty_response_after_tool",
520
+ action: "retry_with_params",
521
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
522
+ maxRetries: 1,
523
+ reason: "Known: empty after tool result \u2014 retry with thinking off"
524
+ },
525
+ {
526
+ signal: "empty_response",
527
+ action: "retry_with_params",
528
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
529
+ maxRetries: 1,
530
+ reason: "Empty response \u2014 try with thinking off"
531
+ },
532
+ {
533
+ signal: "malformed_function_call",
534
+ action: "escalate",
535
+ reason: "MALFORMED_FUNCTION_CALL maps to stop \u2014 escalate to next target"
536
+ }
537
+ ],
538
+ strengths: ["speed", "volume", "classification", "1m_context", "cost"],
539
+ weaknesses: ["complex_schemas", "large_tool_sets", "high_context_quality"],
540
+ notes: "Fast and cheap with 1M context. Quality cliffs at 8K context and 20 tools \u2014 guard with cliffs.",
541
+ // Master plan §6.2 anchor. Tier 0 for hunt (parallel tool throughput
542
+ // 15-75 calls/step beats Sonnet — L-040), summarize, classify.
543
+ archetypePerf: {
544
+ hunt: 9,
545
+ // L-040: parallel tool throughput 15-75/step
546
+ classify: 7,
547
+ // brain-validated, 218 rows
548
+ summarize: 7,
549
+ // brain-validated; cliff strips tools when present
550
+ transform: 7,
551
+ ask: 7,
552
+ generate: 6,
553
+ plan: 5,
554
+ extract: 6,
555
+ // alpha.8 MAX_TOKENS history on structured output
556
+ critique: 4
557
+ // reasoning shallower than Sonnet/Opus
558
+ }
559
+ },
560
+ {
561
+ // ── Gemini 2.5 Flash-Lite ──
562
+ // Onboarded 2026-05-13 (s22) after the model-release watcher surfaced
563
+ // it as a UNREGISTERED + NEW candidate. Released by Google July 2025,
564
+ // stable. Positioned BELOW Flash on the cost/perf frontier:
565
+ // input $0.10/M (Flash $0.30/M) — 3× cheaper
566
+ // output $0.40/M (Flash $2.50/M) — 6× cheaper
567
+ // cache $0.01/M — 1/10 of input (vs Flash 0.25 discount)
568
+ // Cliffs are HYPOTHESIZED from Flash's known failure modes — Flash-Lite
569
+ // is a smaller sibling, so we inherit Flash's cliffs at equal-or-tighter
570
+ // thresholds. The brain will validate/relax these as evidence accumulates
571
+ // per (archetype, model) tuple. Currently ZERO brain rows for this model.
572
+ id: "gemini-2.5-flash-lite",
573
+ verifiedAgainstDocs: "2026-08-27",
574
+ provider: "google",
575
+ // alpha.91 — AVAILABILITY demotion, same class as gemini-2.5-pro below.
576
+ // Google refuses the model for newer account cohorts: "This model is no
577
+ // longer available to new users. Please update your code to use
578
+ // models/gemini-3.5-flash-lite" (tt-intel TT-49 runtime evidence,
579
+ // 2026-08-26, interfaces/kgauto.md ## Requested). It ranked FIRST in
580
+ // getFallbackChain('gemini-2.5-pro') — a dead-for-new-accounts model as
581
+ // the cheapest fallback pick. `legacy` removes it from chains and new
582
+ // selection; explicit-id consumers still resolve it. Successor is the
583
+ // one Google's own error names: gemini-3.5-flash-lite (onboarded this
584
+ // release, same price point).
585
+ status: "legacy",
586
+ maxContextTokens: 1048576,
587
+ maxOutputTokens: 65536,
588
+ maxTools: 128,
589
+ parallelToolCalls: true,
590
+ structuredOutput: "native",
591
+ systemPromptMode: "separate",
592
+ streaming: true,
593
+ cliffs: [
594
+ {
595
+ metric: "input_tokens",
596
+ threshold: 8e3,
597
+ action: "downgrade_quality_warning",
598
+ reason: "Inherited from Flash: quality degrades above ~8K. Smaller model \u2014 likely degrades faster. Re-tune from brain after n\u226520."
599
+ },
600
+ {
601
+ metric: "tool_count",
602
+ threshold: 10,
603
+ action: "drop_to_top_relevant",
604
+ reason: "Conservative: Flash drops at 20, Flash-Lite is smaller \u2014 assume tighter ceiling until brain proves otherwise."
605
+ },
606
+ {
607
+ metric: "thinking_with_short_output",
608
+ threshold: 1,
609
+ action: "force_thinking_budget_zero",
610
+ reason: "Thinking enabled per Google API (thinking: true). Same drain risk as Flash \u2014 thinking tokens consume maxOutputTokens."
611
+ },
612
+ {
613
+ // Strong prior: Flash hit 5/5 empty rate on summarize+tools (s11
614
+ // trust artifact, kgauto commit 3872832). Flash-Lite shares the
615
+ // same architectural family — almost certainly inherits this cliff.
616
+ // Ship the guard preemptively; brain telemetry confirms or relaxes.
617
+ metric: "tool_count",
618
+ threshold: 1,
619
+ whenIntent: "summarize",
620
+ action: "strip_tools",
621
+ reason: "Inherited from Flash s11 cliff: summarize+tools \u2192 empty response. Preemptive guard until brain evidence on Flash-Lite specifically."
622
+ }
623
+ ],
624
+ costInputPer1m: 0.1,
625
+ costOutputPer1m: 0.4,
626
+ lowering: {
627
+ ...GOOGLE_LOWERING_BASE,
628
+ // Cache discount 10× (vs Flash 4×) — Google's spec is $0.01/M cache vs
629
+ // $0.10/M input. Material for repeat-prompt workloads (classify shape).
630
+ cache: { ...GOOGLE_LOWERING_BASE.cache, discount: 0.1 },
631
+ thinking: { field: "generationConfig.thinkingConfig.thinkingBudget", default: "auto" }
632
+ },
633
+ recovery: [
634
+ {
635
+ signal: "empty_response_after_tool",
636
+ action: "retry_with_params",
637
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
638
+ maxRetries: 1,
639
+ reason: "Known on Flash family: empty after tool result \u2014 retry with thinking off."
640
+ },
641
+ {
642
+ signal: "empty_response",
643
+ action: "retry_with_params",
644
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
645
+ maxRetries: 1,
646
+ reason: "Empty response \u2014 try with thinking off."
647
+ },
648
+ {
649
+ signal: "malformed_function_call",
650
+ action: "escalate",
651
+ reason: "MALFORMED_FUNCTION_CALL maps to stop \u2014 escalate to next target."
652
+ }
653
+ ],
654
+ strengths: ["lowest_cost", "speed", "volume", "classification", "summarize", "1m_context", "cache_friendly"],
655
+ weaknesses: ["complex_reasoning", "large_tool_sets", "complex_schemas", "structured_output_unproven", "long_context_quality"],
656
+ notes: "Bottom-frontier anchor on cost: $0.10/$0.40 per 1M tokens, 1M context, 65K max output. Released July 2025 (stable). Positioned for classify / summarize / transform archetypes where quality bar is forgiving. Cliffs inherited from Flash at equal-or-tighter thresholds \u2014 re-tune per (archetype) once brain has n\u226520 rows. Alpha.8 contract layer handles MAX_TOKENS-on-structured-output via fallback chain, so structuredOutput=native is safe to declare even though Flash had alpha.8 history. Cache discount in spec: $0.01/M = 1/10 of input (richer than Flash 25%) \u2014 meaningful for repeat-prompt workloads.",
657
+ // Tier 3 emergency floor for summarize/classify chains. ZERO brain
658
+ // rows — all values are starter hypotheses anchored to "smaller
659
+ // sibling of Flash, at-or-below Flash perf on every archetype." The
660
+ // first 50 brain rows per archetype will validate or relax these.
661
+ archetypePerf: {
662
+ classify: 6,
663
+ // starter hypothesis — verify (Flash is 7, lite likely ≤)
664
+ summarize: 6,
665
+ // starter hypothesis — verify; cliff strips tools
666
+ transform: 6,
667
+ // starter hypothesis — verify
668
+ ask: 5,
669
+ hunt: 5,
670
+ generate: 4,
671
+ extract: 4,
672
+ plan: 3,
673
+ critique: 3
674
+ }
675
+ },
676
+ {
677
+ id: "gemini-2.5-pro",
678
+ verifiedAgainstDocs: "2026-08-08",
679
+ provider: "google",
680
+ // alpha.86 — AVAILABILITY demotion, not a provider-lifecycle mirror.
681
+ // Google's docs still list 2.5-pro as Stable, but the serving layer
682
+ // disagrees per account cohort: tt-intel measured 4/4 refusals
683
+ // ("no longer available to new users", 2026-08-02, their bench), and
684
+ // kgauto's own key 429s on a FreeTier per-day quota (probed
685
+ // 2026-08-08). A chain anchor that hard-fails for whole account
686
+ // cohorts is not an anchor; `legacy` removes it from cost-ordered
687
+ // chains and new selection while keeping the profile resolvable for
688
+ // consumers who pass it explicitly. Re-promotion is one status flip
689
+ // if Google restores availability. The registry cannot yet express
690
+ // account-cohort availability as data — see interfaces/kgauto.md
691
+ // ## Requested 2026-08-02.
692
+ status: "legacy",
693
+ maxContextTokens: 1048576,
694
+ maxOutputTokens: 65535,
695
+ maxTools: 128,
696
+ parallelToolCalls: true,
697
+ structuredOutput: "native",
698
+ systemPromptMode: "separate",
699
+ streaming: true,
700
+ cliffs: [
701
+ {
702
+ metric: "input_tokens",
703
+ threshold: 2e5,
704
+ action: "downgrade_quality_warning",
705
+ reason: "Pricing doubles above 200K: input $1.25\u2192$2.50/M, output $10\u2192$15/M"
706
+ }
707
+ ],
708
+ costInputPer1m: 1.25,
709
+ costOutputPer1m: 10,
710
+ lowering: {
711
+ ...GOOGLE_LOWERING_BASE,
712
+ thinking: { field: "generationConfig.thinkingConfig.thinkingBudget", default: "auto" }
713
+ },
714
+ recovery: [
715
+ {
716
+ signal: "malformed_function_call",
717
+ action: "escalate",
718
+ reason: "MALFORMED_FUNCTION_CALL \u2014 escalate"
719
+ }
720
+ ],
721
+ strengths: ["reasoning", "1m_context", "structured_output", "tool_use"],
722
+ weaknesses: ["pricing_above_200k", "availability_restricted_by_account_cohort"],
723
+ // Master plan §3.3 anchor: tier-2 cross-provider in almost every chain.
724
+ // Sits on the frontier at perf-9 — close to Sonnet but cheaper input.
725
+ archetypePerf: {
726
+ critique: 9,
727
+ plan: 9,
728
+ ask: 8,
729
+ generate: 8,
730
+ extract: 8,
731
+ transform: 8,
732
+ hunt: 8,
733
+ // tier 1 cross-provider for hunt chain
734
+ summarize: 7,
735
+ classify: 7
736
+ }
737
+ },
738
+ {
739
+ id: "gemini-3.1-pro-preview",
740
+ verifiedAgainstDocs: "2026-08-14",
741
+ provider: "google",
742
+ status: "preview",
743
+ maxContextTokens: 1048576,
744
+ maxOutputTokens: 65535,
745
+ maxTools: 128,
746
+ parallelToolCalls: true,
747
+ structuredOutput: "native",
748
+ systemPromptMode: "separate",
749
+ streaming: true,
750
+ cliffs: [
751
+ {
752
+ metric: "input_tokens",
753
+ threshold: 2e5,
754
+ action: "downgrade_quality_warning",
755
+ reason: "Pricing doubles above 200K: input $2\u2192$4/M, output $12\u2192$18/M"
756
+ }
757
+ ],
758
+ costInputPer1m: 2,
759
+ costOutputPer1m: 12,
760
+ lowering: {
761
+ ...GOOGLE_LOWERING_BASE,
762
+ cache: { ...GOOGLE_LOWERING_BASE.cache, discount: 0.1 },
763
+ thinking: { field: "generationConfig.thinkingConfig.thinkingBudget", default: "auto" }
764
+ },
765
+ recovery: [
766
+ {
767
+ signal: "malformed_function_call",
768
+ action: "escalate",
769
+ reason: "MALFORMED_FUNCTION_CALL \u2014 escalate"
770
+ }
771
+ ],
772
+ strengths: ["reasoning", "1m_context", "agentic_coding", "structured_output", "tool_use"],
773
+ weaknesses: ["cost", "preview_status", "pricing_above_200k"],
774
+ notes: "Frontier Gemini (preview, 2026-Q2). Step-change agentic coding per Google. Cache discount 10\xD7 (vs 4\xD7 for 2.5 Pro). Use status=preview to flag rollback path until GA.",
775
+ // Frontier-Gemini preview — bumped one notch over 2.5 Pro on agentic
776
+ // coding / reasoning per Google's release notes. Preview status:
777
+ // chains should stay on 2.5 Pro until GA. Starter hypothesis.
778
+ archetypePerf: {
779
+ critique: 10,
780
+ // Google claims step-change on reasoning
781
+ plan: 10,
782
+ ask: 9,
783
+ generate: 9,
784
+ extract: 9,
785
+ transform: 8,
786
+ hunt: 9,
787
+ // step-change agentic per Google
788
+ summarize: 8,
789
+ classify: 7
790
+ }
791
+ },
792
+ // ── DeepSeek ──
793
+ // 2026-05-08 audit (L-073): DeepSeek's `deepseek-chat` was silently aliased
794
+ // to `deepseek-v4-flash` non-thinking mode. Old kgauto profile claimed 64k
795
+ // context + $0.27/$1.10 — actual is 1M context + $0.14/$0.28. Now modeled
796
+ // as: V4-Flash + V4-Pro as canonical profiles; deepseek-chat and
797
+ // deepseek-reasoner registered as aliases (see ALIASES below).
798
+ {
799
+ id: "deepseek-v4-flash",
800
+ verifiedAgainstDocs: "2026-08-24",
801
+ provider: "deepseek",
802
+ status: "current",
803
+ maxContextTokens: 1e6,
804
+ maxOutputTokens: 384e3,
805
+ maxTools: 16,
806
+ parallelToolCalls: false,
807
+ structuredOutput: "native",
808
+ systemPromptMode: "inline",
809
+ streaming: true,
810
+ cliffs: [
811
+ {
812
+ metric: "tool_count",
813
+ threshold: 1,
814
+ action: "drop_to_top_relevant",
815
+ reason: "Sequential tool calls only \u2014 L-040"
816
+ }
817
+ ],
818
+ costInputPer1m: 0.44,
819
+ costOutputPer1m: 1.32,
820
+ lowering: {
821
+ system: { mode: "inline" },
822
+ cache: { strategy: "unsupported" },
823
+ tools: { format: "deepseek" }
824
+ },
825
+ recovery: [
826
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate" }
827
+ ],
828
+ strengths: ["cost", "1m_context", "json_output", "code", "reasoning"],
829
+ weaknesses: ["parallel_tools", "large_tool_sets"],
830
+ // alpha.47 — explicit slow override. Tag derivation would say 'medium'
831
+ // (no 'latency' weakness, no 'speed' strength), but the alpha.46 shadow
832
+ // probe MEASURED deepseek-v4-flash at 20485ms served (2026-06-03) — ~2.3×
833
+ // gemini-2.5-flash on PB's synchronous /api/analyze path. The 'flash' name
834
+ // is DeepSeek's, not a speed promise. This is the row that, scoring 0.85
835
+ // baseQuality (it carries 'reasoning') with no latency counterweight,
836
+ // leapfrogged gemini-2.5-flash as PB's summarize leader once reachable.
837
+ latencyTier: "slow",
838
+ notes: "Cheap workhorse. 1M context, 384k max output. Cache-hit input $0.0028/M (1/50\xD7 of miss). Aliased as `deepseek-chat` (non-thinking) and `deepseek-reasoner` (thinking) \u2014 see ALIASES. PEAK/OFF-PEAK since 2026-08-16 (page fetched 2026-08-24): peak = weekdays 01:00-04:00 + 06:00-10:00 UTC, off-peak = all other hours at HALF peak. Encoded prices ARE the peak rate (conservative: never understate cost); off-peak traffic costs half what the estimate says. alpha.87 was built + verified 2026-08-14 (prices then live), held at the publish gate 10 days, and published 2026-08-24 \u2014 by which time the 08-16 transition had killed them ($0.14/$0.28 flash, $0.435/$0.87 pro); corrected here (alpha.88). Build-then-hold is the mechanism: re-verify prices at publish when publish is not same-session as build. A single price field cannot represent a two-rate schedule; catalog-sync extractions oscillate between the rates day-to-day (peak read 08-21, off-peak read 08-22) \u2014 time-of-day pricing remains unmodeled, roadmap.",
839
+ // Master plan §6.2 anchor. Brain-validated tier 1 cross-provider for
840
+ // classify (169 rows, 0% empty). Tier 0 for summarize-with-no-tools.
841
+ // Falls off on hunt (sequential tools — L-040) and reasoning depth.
842
+ archetypePerf: {
843
+ classify: 7,
844
+ // brain-validated, 169 rows
845
+ summarize: 7,
846
+ // archetype-tolerant, no brain evidence yet
847
+ ask: 6,
848
+ transform: 6,
849
+ generate: 5,
850
+ plan: 5,
851
+ extract: 5,
852
+ critique: 4,
853
+ hunt: 4
854
+ // sequential tool calls only — L-040
855
+ },
856
+ // alpha.43 — family-level conventions for the `deepseek-chat` family
857
+ // (V4-Flash + future non-thinking-mode members). V4-Flash struggles
858
+ // with summarize-archetype + structured-output: 3 of 4 schema-attempted
859
+ // PB calls (2026-05-18 + 2026-05-22) hit `structured_output_parse_failed`.
860
+ // Surface the friction at compile time so consumers either drop the
861
+ // schema or route to a more schema-reliable family.
862
+ archetypeConventions: [
863
+ {
864
+ archetype: "summarize",
865
+ structuredOutputHint: "avoid",
866
+ cliffWarning: "deepseek-chat family schema compliance on summarize is weak (3 of 4 schema-attempted PB calls hit structured_output_parse_failed). Consider routing summarize+structured-output to a different family (claude-sonnet, gemini-pro, openai-gpt-5.4) or dropping the schema for free-text summaries.",
867
+ reason: "Brain evidence: PB compile_outcomes 2026-05-18 + 2026-05-22, error_type=structured_output_parse_failed on deepseek-v4-flash + summarize archetype."
868
+ }
869
+ ]
870
+ },
871
+ {
872
+ id: "deepseek-v4-pro",
873
+ verifiedAgainstDocs: "2026-08-24",
874
+ provider: "deepseek",
875
+ status: "current",
876
+ maxContextTokens: 1e6,
877
+ maxOutputTokens: 384e3,
878
+ maxTools: 16,
879
+ parallelToolCalls: false,
880
+ structuredOutput: "native",
881
+ systemPromptMode: "inline",
882
+ streaming: true,
883
+ cliffs: [
884
+ {
885
+ metric: "tool_count",
886
+ threshold: 1,
887
+ action: "drop_to_top_relevant",
888
+ reason: "Sequential tool calls only \u2014 L-040"
889
+ }
890
+ ],
891
+ // Verified against the live DeepSeek pricing page 2026-06-22: the 75%-off
892
+ // launch "promo" ($0.435/$0.87) did NOT revert on 2026-05-31 — it became the
893
+ // standing price (docs show $0.435 in / $0.87 out cache-miss, $0.003625
894
+ // cache-hit in; no promo caveat). The old $1.74/$3.48 "regular post-promo"
895
+ // was a projected revert that never happened (L-073/L-081) — it 4×-overstated
896
+ // cost and suppressed V4-Pro in cost-aware ranking.
897
+ costInputPer1m: 1.32,
898
+ costOutputPer1m: 3.96,
899
+ lowering: {
900
+ system: { mode: "inline" },
901
+ cache: { strategy: "unsupported" },
902
+ tools: { format: "deepseek" }
903
+ },
904
+ recovery: [
905
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate" }
906
+ ],
907
+ strengths: ["quality", "reasoning", "1m_context", "json_output", "code", "extended_thinking"],
908
+ weaknesses: ["parallel_tools", "large_tool_sets"],
909
+ // alpha.47 — explicit slow override. Measured 47722ms on the alpha.46
910
+ // shadow probe (2026-06-03) — it's an extended-thinking reasoner, slowest
911
+ // of the served set. Tag derivation would say 'medium'; the measurement says
912
+ // otherwise.
913
+ latencyTier: "slow",
914
+ notes: "Pro tier. 1M context, 384k max output. $0.435/$0.87 per 1M (cache-hit in $0.003625) \u2014 verified live 2026-06-22; the launch promo became the standing price (no revert). Default mode = thinking. PEAK/OFF-PEAK since 2026-08-16 (page fetched 2026-08-24): peak = weekdays 01:00-04:00 + 06:00-10:00 UTC, off-peak = all other hours at HALF peak. Encoded prices ARE the peak rate (conservative: never understate cost); off-peak traffic costs half what the estimate says. alpha.87 was built + verified 2026-08-14 (prices then live), held at the publish gate 10 days, and published 2026-08-24 \u2014 by which time the 08-16 transition had killed them ($0.14/$0.28 flash, $0.435/$0.87 pro); corrected here (alpha.88). Build-then-hold is the mechanism: re-verify prices at publish when publish is not same-session as build. A single price field cannot represent a two-rate schedule; catalog-sync extractions oscillate between the rates day-to-day (peak read 08-21, off-peak read 08-22) \u2014 time-of-day pricing remains unmodeled, roadmap.",
915
+ // Master plan §3.3: tier 3 cross-provider for plan chain. Reasoning
916
+ // bumped one notch over V4-Flash; same parallel-tool ceiling.
917
+ archetypePerf: {
918
+ plan: 7,
919
+ // §3.3 tier 3 for plan
920
+ critique: 6,
921
+ ask: 7,
922
+ generate: 6,
923
+ classify: 7,
924
+ summarize: 7,
925
+ extract: 6,
926
+ transform: 6,
927
+ hunt: 4
928
+ // sequential tools — same as V4-Flash
929
+ },
930
+ // alpha.43 — family-level conventions for the `deepseek-reasoner` family
931
+ // (V4-Pro is the family rep; future thinking-mode members inherit).
932
+ //
933
+ // The decisive-archetype suffix is the load-bearing piece: 2026-05-28
934
+ // first real V4-Pro probe on tt-intel/classify (exclusion-finding ID 20)
935
+ // showed 8/10 judge rationales citing "candidate hedges and fails to
936
+ // commit to a single classification" or "candidate refuses to pick
937
+ // among the labels." Without the forcing-function suffix, every
938
+ // reasoner probe on classify/extract/ask verdicts stay-excluded for
939
+ // model-behavior reasons (epistemic humility is reasoner-shaped),
940
+ // NOT quality reasons. The suffix re-shapes the contract so the model
941
+ // is asked for what it's actually being measured on.
942
+ //
943
+ // The hunt-archetype cliffWarning is the L-040 architectural reading
944
+ // surfaced at compile time. Reasoners produce sequential reasoning
945
+ // chains by design; hunt archetypes with 3+ parallel tools lose
946
+ // substantial throughput (L-040 + L-041). The L-040 cliff in the
947
+ // `cliffs` array already trims to top-relevant tools; this warning
948
+ // tells the consumer the family is wrong for the shape so they can
949
+ // route to gemini-flash (parallel-strong) instead of accepting trimmed
950
+ // throughput silently.
951
+ archetypeConventions: [
952
+ {
953
+ archetype: "classify",
954
+ promptSuffix: "\n\nOutput exactly one classification label. Do not hedge, do not add preamble, do not explain your reasoning. If uncertain, pick the most likely label and commit to it.",
955
+ reason: "Reasoners (deepseek-reasoner family) hedge on decisive-output archetypes by design. Evidence: 2026-05-28 V4-Pro probe on tt-intel/classify (exclusion-finding ID 20, brain probe_outcomes); 8 of 10 judge rationales cited hedging or refusal-to-commit. Forcing-function suffix converts reasoner output to decisive labels."
956
+ },
957
+ {
958
+ archetype: "extract",
959
+ promptSuffix: "\n\nOutput exactly the requested structured fields. Do not hedge, do not add preamble, do not explain your reasoning. If a field is unknown, emit the field with a null/empty value and commit to that \u2014 do not refuse to extract.",
960
+ reason: "Reasoners (deepseek-reasoner family) hedge on decisive-output archetypes by design. Evidence: same family failure shape as classify (exclusion-finding ID 20, 2026-05-28). Forcing-function suffix asks for commitment instead of epistemic-humility hedging."
961
+ },
962
+ {
963
+ archetype: "ask",
964
+ promptSuffix: "\n\nAnswer the question directly. Do not hedge, do not add preamble, do not explain your reasoning unless explicitly asked. If uncertain, give your best answer and commit to it.",
965
+ reason: "Reasoners (deepseek-reasoner family) hedge on decisive-output archetypes by design. Evidence: same family failure shape as classify (exclusion-finding ID 20, 2026-05-28). Forcing-function suffix asks for commitment instead of epistemic-humility hedging."
966
+ },
967
+ {
968
+ archetype: "hunt",
969
+ whenToolCountAtLeast: 3,
970
+ cliffWarning: "Reasoners (deepseek-reasoner family) produce sequential reasoning chains by design; hunt archetypes with 3+ parallel tools lose substantial throughput. Consider gemini-flash family (parallel-strong) instead \u2014 see L-040 / L-041.",
971
+ reason: "L-040 + L-041 architectural reading; 2026-05-28 V4-Pro probe evidence (exclusion-finding ID 20) confirms reasoner-family hunt fit is structurally weak."
972
+ }
973
+ ]
974
+ },
975
+ // ── OpenAI ──
976
+ // alpha.16 (2026-05-17): close the half-supported provider gap. env.ts
977
+ // already registered OPENAI_API_KEY + executeOpenAI + normalizeOpenAILike
978
+ // + lowerOpenAI all existed; profile entries were missing, so the
979
+ // alpha.10 auto-filter would mark openai-keyed models reachable but
980
+ // there were no profiles to filter IN. Half-supported is now fully
981
+ // supported. PB request `openai-provider-profiles` (2026-05-16).
982
+ //
983
+ // Profile data verified against developers.openai.com/api/docs/pricing
984
+ // + per-model pages 2026-05-17. L-049/L-081 step-zero: no AI-trained
985
+ // numbers — fetched live from OpenAI's docs. As of 2026-05, OpenAI's
986
+ // current flagship is gpt-5.5 (2025-12 cutoff); gpt-5.4-{base,mini,nano}
987
+ // are the workhorse family. gpt-4.1 + gpt-4o are legacy.
988
+ //
989
+ // Both 5.5 and 5.4 carry a 272K input-token pricing cliff (2x input,
990
+ // 1.5x output beyond that). Modeled as a `downgrade_quality_warning`
991
+ // cliff because it ranks the model down at large-context shapes — the
992
+ // semantics of "this model is now 2x more expensive" map onto the
993
+ // existing penalty mechanism. Cost-watcher will catch high-context
994
+ // spikes empirically; the cliff prevents naive routing into the doubled
995
+ // pricing zone.
996
+ {
997
+ id: "gpt-5.5",
998
+ verifiedAgainstDocs: "2026-05-17",
999
+ provider: "openai",
1000
+ status: "current",
1001
+ maxContextTokens: 105e4,
1002
+ maxOutputTokens: 128e3,
1003
+ maxTools: 64,
1004
+ parallelToolCalls: true,
1005
+ structuredOutput: "native",
1006
+ systemPromptMode: "inline",
1007
+ streaming: true,
1008
+ cliffs: [
1009
+ {
1010
+ metric: "input_tokens",
1011
+ threshold: 272e3,
1012
+ action: "downgrade_quality_warning",
1013
+ reason: "OpenAI pricing tier shift: >272K input tokens billed at 2x input + 1.5x output rates"
1014
+ }
1015
+ ],
1016
+ costInputPer1m: 5,
1017
+ costOutputPer1m: 30,
1018
+ lowering: {
1019
+ system: { mode: "inline" },
1020
+ // OpenAI caching is implicit (auto-applied to repeated prefixes
1021
+ // ≥1024 tokens for prompt_tokens_details.cached_tokens). No
1022
+ // wire-format marker. Discount: 10x for cached input ($0.50/$5.00).
1023
+ cache: { strategy: "unsupported", minTokens: 1024, discount: 0.1 },
1024
+ tools: { format: "openai" }
1025
+ },
1026
+ recovery: [
1027
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate to fallback chain" },
1028
+ { signal: "model_not_found", action: "escalate", reason: "Model deprecated/renamed \u2014 escalate (L-061)" }
1029
+ ],
1030
+ strengths: ["reasoning", "agentic_coding", "long_context", "structured_output", "reliable_tool_use", "reasoning_effort_knob"],
1031
+ weaknesses: ["cost", "pricing_cliff_at_272k"],
1032
+ notes: "OpenAI frontier (2026-05). 1M context (1.05M total), 128K max output, 2025-12 cutoff. Reasoning effort knob (none/low/medium/high/xhigh). Pricing cliff at 272K input.",
1033
+ // Frontier-tier perf hypothesis. Anchored to Opus 4.7 row (similar
1034
+ // price/positioning). Brain evidence will refine; no telemetry yet.
1035
+ archetypePerf: {
1036
+ critique: 9,
1037
+ plan: 9,
1038
+ generate: 9,
1039
+ ask: 9,
1040
+ extract: 9,
1041
+ transform: 9,
1042
+ hunt: 8,
1043
+ // parallel tool support good but cliff at 272K hurts deep multi-step
1044
+ summarize: 7,
1045
+ // overkill for tolerant archetype
1046
+ classify: 7
1047
+ // overkill; cheaper models cover this
1048
+ }
1049
+ },
1050
+ {
1051
+ id: "gpt-5.4",
1052
+ verifiedAgainstDocs: "2026-05-17",
1053
+ provider: "openai",
1054
+ status: "current",
1055
+ maxContextTokens: 105e4,
1056
+ maxOutputTokens: 128e3,
1057
+ maxTools: 64,
1058
+ parallelToolCalls: true,
1059
+ structuredOutput: "native",
1060
+ systemPromptMode: "inline",
1061
+ streaming: true,
1062
+ cliffs: [
1063
+ {
1064
+ metric: "input_tokens",
1065
+ threshold: 272e3,
1066
+ action: "downgrade_quality_warning",
1067
+ reason: "OpenAI pricing tier shift: >272K input tokens billed at 2x input + 1.5x output rates"
1068
+ }
1069
+ ],
1070
+ costInputPer1m: 2.5,
1071
+ costOutputPer1m: 15,
1072
+ lowering: {
1073
+ system: { mode: "inline" },
1074
+ cache: { strategy: "unsupported", minTokens: 1024, discount: 0.1 },
1075
+ tools: { format: "openai" }
1076
+ },
1077
+ recovery: [
1078
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate to fallback chain" },
1079
+ { signal: "model_not_found", action: "escalate", reason: "Model deprecated/renamed \u2014 escalate (L-061)" }
1080
+ ],
1081
+ strengths: ["reasoning", "long_context", "structured_output", "reliable_tool_use"],
1082
+ weaknesses: ["pricing_cliff_at_272k"],
1083
+ notes: "OpenAI workhorse (2026-05). 1M context (1.05M total), 128K max output, 2025-08 cutoff. Pricing cliff at 272K input. Pairs cleanly with Sonnet 4.6 on cost ($2.50/$15.00 vs $3.00/$15.00).",
1084
+ // Anchored to Sonnet 4.6 row (similar price/positioning). Slight
1085
+ // anthropic-side edge on agentic coding per master plan vibe.
1086
+ archetypePerf: {
1087
+ critique: 8,
1088
+ plan: 8,
1089
+ generate: 8,
1090
+ ask: 8,
1091
+ extract: 8,
1092
+ transform: 8,
1093
+ hunt: 7,
1094
+ summarize: 7,
1095
+ classify: 7
1096
+ }
1097
+ },
1098
+ {
1099
+ id: "gpt-5.4-mini",
1100
+ verifiedAgainstDocs: "2026-05-17",
1101
+ provider: "openai",
1102
+ status: "current",
1103
+ maxContextTokens: 4e5,
1104
+ maxOutputTokens: 128e3,
1105
+ maxTools: 64,
1106
+ parallelToolCalls: true,
1107
+ structuredOutput: "native",
1108
+ systemPromptMode: "inline",
1109
+ streaming: true,
1110
+ cliffs: [],
1111
+ costInputPer1m: 0.75,
1112
+ costOutputPer1m: 4.5,
1113
+ lowering: {
1114
+ system: { mode: "inline" },
1115
+ cache: { strategy: "unsupported", minTokens: 1024, discount: 0.1 },
1116
+ tools: { format: "openai" }
1117
+ },
1118
+ recovery: [
1119
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate to fallback chain" },
1120
+ { signal: "model_not_found", action: "escalate", reason: "Model deprecated/renamed \u2014 escalate (L-061)" }
1121
+ ],
1122
+ strengths: ["cost", "speed", "agentic_coding", "structured_output", "reliable_tool_use"],
1123
+ weaknesses: ["reasoning_depth"],
1124
+ notes: "OpenAI mini-tier (2026-05). 400K context, 128K max output, 2025-08 cutoff. OpenAI describes as 'strongest mini model for coding, computer use, subagents.' Cache discount 10x ($0.075 input).",
1125
+ // Mini-tier hypothesis. Anchored to Haiku 4.5 + Flash row pricing.
1126
+ // Cost is slightly higher than Haiku ($0.75 vs $0.50 input) but
1127
+ // OpenAI claims strong coding/subagent perf.
1128
+ archetypePerf: {
1129
+ ask: 7,
1130
+ generate: 7,
1131
+ extract: 7,
1132
+ transform: 7,
1133
+ classify: 7,
1134
+ summarize: 7,
1135
+ hunt: 7,
1136
+ plan: 6,
1137
+ critique: 5
1138
+ // reasoning depth gap — frontier models handle this
1139
+ }
1140
+ },
1141
+ {
1142
+ id: "gpt-5.4-nano",
1143
+ verifiedAgainstDocs: "2026-05-17",
1144
+ provider: "openai",
1145
+ status: "current",
1146
+ maxContextTokens: 4e5,
1147
+ maxOutputTokens: 128e3,
1148
+ maxTools: 64,
1149
+ parallelToolCalls: true,
1150
+ structuredOutput: "native",
1151
+ systemPromptMode: "inline",
1152
+ streaming: true,
1153
+ cliffs: [],
1154
+ costInputPer1m: 0.2,
1155
+ costOutputPer1m: 1.25,
1156
+ lowering: {
1157
+ system: { mode: "inline" },
1158
+ cache: { strategy: "unsupported", minTokens: 1024, discount: 0.1 },
1159
+ tools: { format: "openai" }
1160
+ },
1161
+ recovery: [
1162
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate to fallback chain" },
1163
+ { signal: "model_not_found", action: "escalate", reason: "Model deprecated/renamed \u2014 escalate (L-061)" }
1164
+ ],
1165
+ strengths: ["cost", "speed", "volume", "structured_output"],
1166
+ weaknesses: ["reasoning_depth", "no_computer_use"],
1167
+ notes: "OpenAI nano-tier (2026-05). 400K context, 128K max output, 2025-08 cutoff. 'Cheapest GPT-5.4-class for simple high-volume tasks.' No fine-tuning, no computer-use tools. Cache discount 10x.",
1168
+ // Nano-tier. Anchored to Flash-Lite row ($0.10/$0.40 vs nano's
1169
+ // $0.20/$1.25). Slightly more expensive than Flash-Lite but with
1170
+ // OpenAI brand reliability. Good fit for classify/summarize floor.
1171
+ archetypePerf: {
1172
+ classify: 7,
1173
+ summarize: 6,
1174
+ ask: 6,
1175
+ transform: 6,
1176
+ extract: 6,
1177
+ generate: 5,
1178
+ hunt: 5,
1179
+ plan: 4,
1180
+ critique: 3
1181
+ // not for reasoning archetypes
1182
+ }
1183
+ },
1184
+ // ── Auto-onboarded (UNVERIFIED) ──
1185
+ // Cloned by scripts/auto-onboard-models.mjs from a same-family template.
1186
+ // Each entry's pricing/context/cliffs/lowering reflects the template, NOT
1187
+ // provider docs. Verify before promoting status to 'current' (L-049/L-081).
1188
+ {
1189
+ // s37 (2026-05-21): UNVERIFIED-AUTO-ONBOARD → verified against
1190
+ // ai.google.dev/gemini-api/docs/models/gemini-3-flash-preview +
1191
+ // ai.google.dev/gemini-api/docs/pricing. L-081 catches:
1192
+ // maxOutputTokens 65_535 → 65_536 (off-by-one)
1193
+ // costInputPer1m 0.30 → 0.50 (template-cloned from 2.5-flash; actual is 1.67× more expensive)
1194
+ // costOutputPer1m 2.50 → 3.00 (template-cloned; actual 1.2× more expensive)
1195
+ // cache discount default 0.25 → 0.10 (10× discount, $0.05/$0.50 per docs)
1196
+ // Cliffs inherited from 2.5-flash conservatively. The 8K-context-quality
1197
+ // cliff was a 2.5-Flash observation — Google positions Gemini 3 as
1198
+ // sustained-frontier-on-long-context; brain evidence will validate/relax.
1199
+ // Kept as guard for now.
1200
+ id: "gemini-3-flash-preview",
1201
+ verifiedAgainstDocs: "2026-05-21",
1202
+ provider: "google",
1203
+ status: "preview",
1204
+ maxContextTokens: 1048576,
1205
+ maxOutputTokens: 65536,
1206
+ maxTools: 128,
1207
+ parallelToolCalls: true,
1208
+ structuredOutput: "native",
1209
+ systemPromptMode: "separate",
1210
+ streaming: true,
1211
+ cliffs: [
1212
+ {
1213
+ metric: "input_tokens",
1214
+ threshold: 8e3,
1215
+ action: "downgrade_quality_warning",
1216
+ reason: "Inherited from 2.5-flash guard; brain evidence on Gemini 3 long-context quality will validate/relax"
1217
+ },
1218
+ {
1219
+ metric: "tool_count",
1220
+ threshold: 20,
1221
+ action: "drop_to_top_relevant",
1222
+ reason: "Tool reliability drops above ~20 tools (despite 128 hard limit) \u2014 inherited from Flash family"
1223
+ },
1224
+ {
1225
+ metric: "thinking_with_short_output",
1226
+ threshold: 1,
1227
+ action: "force_thinking_budget_zero",
1228
+ reason: "Thinking tokens consume maxOutputTokens \u2014 empty response if drained"
1229
+ },
1230
+ {
1231
+ // Inherited from gemini-2.5-flash s11 trust artifact. Family-likely
1232
+ // failure mode for Flash architecture. Keep preemptively until brain
1233
+ // evidence on Gemini 3 specifically.
1234
+ metric: "tool_count",
1235
+ threshold: 1,
1236
+ whenIntent: "summarize",
1237
+ action: "strip_tools",
1238
+ reason: "Inherited from 2.5-flash s11 cliff: summarize+tools \u2192 empty response. Preemptive guard until brain evidence on 3-flash-preview specifically."
1239
+ }
1240
+ ],
1241
+ costInputPer1m: 0.5,
1242
+ costOutputPer1m: 3,
1243
+ lowering: {
1244
+ ...GOOGLE_LOWERING_BASE,
1245
+ // 10× cache discount per Google pricing: $0.05/M cached vs $0.50/M input.
1246
+ cache: { ...GOOGLE_LOWERING_BASE.cache, discount: 0.1 },
1247
+ thinking: { field: "generationConfig.thinkingConfig.thinkingBudget", default: "auto" }
1248
+ },
1249
+ recovery: [
1250
+ {
1251
+ signal: "empty_response_after_tool",
1252
+ action: "retry_with_params",
1253
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
1254
+ maxRetries: 1,
1255
+ reason: "Known: empty after tool result \u2014 retry with thinking off"
1256
+ },
1257
+ {
1258
+ signal: "empty_response",
1259
+ action: "retry_with_params",
1260
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
1261
+ maxRetries: 1,
1262
+ reason: "Empty response \u2014 try with thinking off"
1263
+ },
1264
+ {
1265
+ signal: "malformed_function_call",
1266
+ action: "escalate",
1267
+ reason: "MALFORMED_FUNCTION_CALL maps to stop \u2014 escalate to next target"
1268
+ }
1269
+ ],
1270
+ strengths: ["speed", "volume", "classification", "1m_context", "cost"],
1271
+ weaknesses: ["complex_schemas", "large_tool_sets", "high_context_quality"],
1272
+ notes: "Verified s37 (2026-05-21) against Google docs. Step-change positioning vs 2.5-flash on agentic loops per Google's release notes (Dec 2025). Pricing 1.67\xD7/1.2\xD7 higher than 2.5-flash; cache discount 10\xD7 (vs 4\xD7 for 2.5). Status=preview until brain evidence accumulates.",
1273
+ // Anchored to 2.5-flash archetypePerf as starter, with judgment adjustments
1274
+ // for Google's "step-change on agentic" positioning. Brain evidence (zero
1275
+ // rows today) will replace these starter values.
1276
+ archetypePerf: {
1277
+ hunt: 9,
1278
+ // Inherits 2.5-flash L-040 parallel-tool tier; Google positions 3 as agentic-loop upgrade
1279
+ classify: 7,
1280
+ // Inherits 2.5-flash brain-validated tier (218 rows on 2.5)
1281
+ summarize: 7,
1282
+ // Inherits 2.5-flash; cliff strips tools when present
1283
+ transform: 7,
1284
+ ask: 8,
1285
+ // +1 vs 2.5-flash — sustained-frontier positioning
1286
+ generate: 7,
1287
+ // +1 vs 2.5-flash — agentic coding upgrade per Google
1288
+ plan: 6,
1289
+ // +1 vs 2.5-flash — complex iterations per positioning
1290
+ extract: 6,
1291
+ critique: 5
1292
+ // +1 vs 2.5-flash — but still below Sonnet/Opus reasoning floor
1293
+ }
1294
+ },
1295
+ {
1296
+ // ── Gemini 3.1 Flash-Lite ──
1297
+ // Onboarded 2026-05-16 by auto-onboarder; s37 (2026-05-21) verified
1298
+ // against ai.google.dev/gemini-api/docs/pricing.
1299
+ //
1300
+ // L-081 CATCHES (template clone from 2.5-flash-lite was 2.5-3.75× too cheap):
1301
+ // costInputPer1m 0.10 → 0.25 (template clone undervalued by 2.5×)
1302
+ // costOutputPer1m 0.40 → 1.50 (template clone undervalued by 3.75×)
1303
+ //
1304
+ // Real 3.1-flash-lite is NOT a cost-equivalent successor to 2.5-flash-lite —
1305
+ // it sits between 2.5-flash-lite ($0.10/$0.40) and 2.5-flash ($0.30/$2.50).
1306
+ // Cache discount 10× verified ($0.025/M cached vs $0.25/M input).
1307
+ //
1308
+ // Cliffs are HYPOTHESIZED from 2.5-flash family; brain evidence pending.
1309
+ id: "gemini-3.1-flash-lite",
1310
+ verifiedAgainstDocs: "2026-08-27",
1311
+ provider: "google",
1312
+ // alpha.91 — docs page now labels it "Stable" and the brain row has been
1313
+ // `current` since catalog-sync moved it; the bundle lagged (the L-073
1314
+ // layer-parity shape, caught during the TT-49 delisting release).
1315
+ status: "current",
1316
+ maxContextTokens: 1048576,
1317
+ maxOutputTokens: 65536,
1318
+ maxTools: 128,
1319
+ parallelToolCalls: true,
1320
+ structuredOutput: "native",
1321
+ systemPromptMode: "separate",
1322
+ streaming: true,
1323
+ cliffs: [
1324
+ {
1325
+ metric: "input_tokens",
1326
+ threshold: 8e3,
1327
+ action: "downgrade_quality_warning",
1328
+ reason: "Inherited from Flash: quality degrades above ~8K. Smaller model \u2014 likely degrades faster. Re-tune from brain after n\u226520."
1329
+ },
1330
+ {
1331
+ metric: "tool_count",
1332
+ threshold: 10,
1333
+ action: "drop_to_top_relevant",
1334
+ reason: "Conservative: Flash drops at 20, Flash-Lite is smaller \u2014 assume tighter ceiling until brain proves otherwise."
1335
+ },
1336
+ {
1337
+ metric: "thinking_with_short_output",
1338
+ threshold: 1,
1339
+ action: "force_thinking_budget_zero",
1340
+ reason: "Thinking enabled per Google API (thinking: true). Same drain risk as Flash \u2014 thinking tokens consume maxOutputTokens."
1341
+ },
1342
+ {
1343
+ // Strong prior: Flash hit 5/5 empty rate on summarize+tools (s11
1344
+ // trust artifact, kgauto commit 3872832). Flash-Lite shares the
1345
+ // same architectural family — almost certainly inherits this cliff.
1346
+ // Ship the guard preemptively; brain telemetry confirms or relaxes.
1347
+ metric: "tool_count",
1348
+ threshold: 1,
1349
+ whenIntent: "summarize",
1350
+ action: "strip_tools",
1351
+ reason: "Inherited from Flash s11 cliff: summarize+tools \u2192 empty response. Preemptive guard until brain evidence on Flash-Lite specifically."
1352
+ }
1353
+ ],
1354
+ costInputPer1m: 0.25,
1355
+ costOutputPer1m: 1.5,
1356
+ lowering: {
1357
+ ...GOOGLE_LOWERING_BASE,
1358
+ // Cache discount 10× (vs Flash 4×) — Google docs s37: $0.025/M cached vs
1359
+ // $0.25/M input. Material for repeat-prompt workloads (classify shape).
1360
+ cache: { ...GOOGLE_LOWERING_BASE.cache, discount: 0.1 },
1361
+ thinking: { field: "generationConfig.thinkingConfig.thinkingBudget", default: "auto" }
1362
+ },
1363
+ recovery: [
1364
+ {
1365
+ signal: "empty_response_after_tool",
1366
+ action: "retry_with_params",
1367
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
1368
+ maxRetries: 1,
1369
+ reason: "Known on Flash family: empty after tool result \u2014 retry with thinking off."
1370
+ },
1371
+ {
1372
+ signal: "empty_response",
1373
+ action: "retry_with_params",
1374
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
1375
+ maxRetries: 1,
1376
+ reason: "Empty response \u2014 try with thinking off."
1377
+ },
1378
+ {
1379
+ signal: "malformed_function_call",
1380
+ action: "escalate",
1381
+ reason: "MALFORMED_FUNCTION_CALL maps to stop \u2014 escalate to next target."
1382
+ }
1383
+ ],
1384
+ strengths: ["low_cost", "speed", "volume", "classification", "summarize", "1m_context", "cache_friendly"],
1385
+ weaknesses: ["complex_reasoning", "large_tool_sets", "complex_schemas", "structured_output_unproven", "long_context_quality"],
1386
+ notes: "Verified s37 (2026-05-21) against Google docs. Sits between 2.5-flash-lite (cheaper) and 2.5-flash (more expensive) on cost frontier; 2.5\xD7 more expensive than initial template-clone. Cliffs hypothesized from Flash family \u2014 brain evidence pending.",
1387
+ // Tier 2-3 floor for summarize/classify chains at the new (verified) price
1388
+ // point. ZERO brain rows — values are starter hypotheses anchored to
1389
+ // "smaller sibling of Flash at higher cost than 2.5-flash-lite." The first
1390
+ // 50 brain rows per archetype will validate or relax these.
1391
+ archetypePerf: {
1392
+ classify: 6,
1393
+ // starter hypothesis — verify (Flash is 7, lite likely ≤)
1394
+ summarize: 6,
1395
+ // starter hypothesis — verify; cliff strips tools
1396
+ transform: 6,
1397
+ // starter hypothesis — verify
1398
+ ask: 5,
1399
+ hunt: 5,
1400
+ generate: 4,
1401
+ extract: 4,
1402
+ plan: 3,
1403
+ critique: 3
1404
+ }
1405
+ },
1406
+ // ─────────────────────────────────────────────────────────────────────────
1407
+ // Gemini 3.5 Flash-Lite — onboarded s91 (2026-08-27)
1408
+ //
1409
+ // The successor Google's own delisting error names: "This model
1410
+ // [gemini-2.5-flash-lite] is no longer available to new users. Please
1411
+ // update your code to use models/gemini-3.5-flash-lite" (tt-intel TT-49,
1412
+ // 2026-08-26). Same price point as the 2.5 Lite rung it replaces in the
1413
+ // summarize/classify starter chains ($0.30/$2.50 vs the old $0.10/$0.40
1414
+ // era pricing), same 1M/65K limits, same 10× cache discount ($0.03 vs
1415
+ // $0.30 input). Positioning: "fastest, most cost-effective 3.5 model for
1416
+ // high-throughput execution … high-volume agentic tasks, translation, and
1417
+ // simple data processing."
1418
+ //
1419
+ // Cliffs + archetypePerf inherited from the Flash-Lite family as starter
1420
+ // hypotheses — ZERO brain rows on this id yet; first 50 rows per archetype
1421
+ // validate or relax.
1422
+ //
1423
+ // Specs verified 2026-08-27 against:
1424
+ // ai.google.dev/gemini-api/docs/models/gemini-3.5-flash-lite
1425
+ // (Stable; input limit 1,048,576; output limit 65,536; function
1426
+ // calling / structured outputs / thinking / caching all Supported)
1427
+ // ai.google.dev/gemini-api/docs/pricing ($0.30 in / $2.50 out incl.
1428
+ // thinking tokens; context caching $0.03)
1429
+ // ─────────────────────────────────────────────────────────────────────────
1430
+ {
1431
+ id: "gemini-3.5-flash-lite",
1432
+ verifiedAgainstDocs: "2026-08-27",
1433
+ provider: "google",
1434
+ status: "current",
1435
+ maxContextTokens: 1048576,
1436
+ maxOutputTokens: 65536,
1437
+ maxTools: 128,
1438
+ parallelToolCalls: true,
1439
+ structuredOutput: "native",
1440
+ systemPromptMode: "separate",
1441
+ streaming: true,
1442
+ cliffs: [
1443
+ {
1444
+ metric: "input_tokens",
1445
+ threshold: 8e3,
1446
+ action: "downgrade_quality_warning",
1447
+ reason: "Inherited from Flash family: quality degrades above ~8K. Lite tier \u2014 assume it degrades at least as fast. Re-tune from brain after n\u226520."
1448
+ },
1449
+ {
1450
+ metric: "tool_count",
1451
+ threshold: 10,
1452
+ action: "drop_to_top_relevant",
1453
+ reason: "Conservative: Flash drops at 20, Lite tier is smaller \u2014 assume tighter ceiling until brain proves otherwise."
1454
+ },
1455
+ {
1456
+ metric: "thinking_with_short_output",
1457
+ threshold: 1,
1458
+ action: "force_thinking_budget_zero",
1459
+ reason: "Thinking Supported per model page \u2014 same drain risk as Flash; thinking tokens consume maxOutputTokens."
1460
+ },
1461
+ {
1462
+ // Strong prior: Flash hit 5/5 empty rate on summarize+tools (s11
1463
+ // trust artifact). Flash-Lite siblings carry the guard preemptively;
1464
+ // brain telemetry confirms or relaxes per id.
1465
+ metric: "tool_count",
1466
+ threshold: 1,
1467
+ whenIntent: "summarize",
1468
+ action: "strip_tools",
1469
+ reason: "Inherited from Flash s11 cliff: summarize+tools \u2192 empty response. Preemptive guard until brain evidence on 3.5 Flash-Lite specifically."
1470
+ }
1471
+ ],
1472
+ costInputPer1m: 0.3,
1473
+ costOutputPer1m: 2.5,
1474
+ lowering: {
1475
+ ...GOOGLE_LOWERING_BASE,
1476
+ // Cache discount 10× — pricing page: $0.03/M cached vs $0.30/M input.
1477
+ // Material for repeat-prompt workloads (classify shape).
1478
+ cache: { ...GOOGLE_LOWERING_BASE.cache, discount: 0.1 },
1479
+ thinking: { field: "generationConfig.thinkingConfig.thinkingBudget", default: "auto" }
1480
+ },
1481
+ recovery: [
1482
+ {
1483
+ signal: "empty_response_after_tool",
1484
+ action: "retry_with_params",
1485
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
1486
+ maxRetries: 1,
1487
+ reason: "Known on Flash family: empty after tool result \u2014 retry with thinking off."
1488
+ },
1489
+ {
1490
+ signal: "empty_response",
1491
+ action: "retry_with_params",
1492
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
1493
+ maxRetries: 1,
1494
+ reason: "Empty response \u2014 try with thinking off."
1495
+ },
1496
+ {
1497
+ signal: "malformed_function_call",
1498
+ action: "escalate",
1499
+ reason: "MALFORMED_FUNCTION_CALL maps to stop \u2014 escalate to next target."
1500
+ }
1501
+ ],
1502
+ strengths: ["low_cost", "speed", "volume", "classification", "summarize", "1m_context", "cache_friendly"],
1503
+ weaknesses: ["complex_reasoning", "large_tool_sets", "complex_schemas", "structured_output_unproven", "long_context_quality"],
1504
+ notes: 'Onboarded s91 (2026-08-27) as the Google-named successor to availability-restricted gemini-2.5-flash-lite. $0.30/$2.50 per 1M, 1M context, 65K max output, cache $0.03/M (10\xD7 discount). "Fastest, most cost-effective 3.5 model for high-throughput execution." Cliffs hypothesized from the Flash family \u2014 brain evidence pending.',
1505
+ // Lite-tier floor for summarize/classify chains. ZERO brain rows — all
1506
+ // values are starter hypotheses anchored to the Flash-Lite family shape.
1507
+ archetypePerf: {
1508
+ classify: 6,
1509
+ // starter hypothesis — verify (Flash is 7, lite likely ≤)
1510
+ summarize: 6,
1511
+ // starter hypothesis — verify; cliff strips tools
1512
+ transform: 6,
1513
+ // starter hypothesis — verify
1514
+ ask: 5,
1515
+ hunt: 5,
1516
+ generate: 4,
1517
+ extract: 4,
1518
+ plan: 3,
1519
+ critique: 3
1520
+ }
1521
+ },
1522
+ // ─────────────────────────────────────────────────────────────────────────
1523
+ // Gemini 3.5 Flash — hand-onboarded s37 (2026-05-21)
1524
+ //
1525
+ // Google positioning ("Most intelligent for sustained frontier performance
1526
+ // on agentic and coding tasks" / "particularly effective for rapid agentic
1527
+ // loops involving complex coding cycles and iterations") suggests this is
1528
+ // the Flash-family upgrade specifically aimed at hunt-shape workloads.
1529
+ // Pricing 5× input / 3.6× output vs 2.5-flash — material cost premium.
1530
+ // archetypePerf adjusted +1 vs 2.5-flash on ask/generate/plan/critique
1531
+ // (sustained-frontier positioning); hunt held at 9 inherited from L-040
1532
+ // family parallel-tool tier; brain evidence will validate within 50 rows.
1533
+ //
1534
+ // Cliffs inherited conservatively from 2.5-flash. Google's "sustained
1535
+ // frontier on long-context" positioning suggests the 8K cliff may not
1536
+ // apply to 3.5 — keep as guard until brain evidence shows otherwise.
1537
+ //
1538
+ // Specs verified against:
1539
+ // ai.google.dev/gemini-api/docs/models/gemini-3.5-flash
1540
+ // ai.google.dev/gemini-api/docs/pricing (Standard tier)
1541
+ // ─────────────────────────────────────────────────────────────────────────
1542
+ {
1543
+ id: "gemini-3.5-flash",
1544
+ verifiedAgainstDocs: "2026-05-21",
1545
+ provider: "google",
1546
+ status: "current",
1547
+ maxContextTokens: 1048576,
1548
+ maxOutputTokens: 65536,
1549
+ maxTools: 128,
1550
+ parallelToolCalls: true,
1551
+ structuredOutput: "native",
1552
+ systemPromptMode: "separate",
1553
+ streaming: true,
1554
+ cliffs: [
1555
+ {
1556
+ metric: "input_tokens",
1557
+ threshold: 8e3,
1558
+ action: "downgrade_quality_warning",
1559
+ reason: "Inherited from 2.5-flash guard; Google positions 3.5 as sustained-frontier-on-long-context but brain evidence pending"
1560
+ },
1561
+ {
1562
+ metric: "tool_count",
1563
+ threshold: 20,
1564
+ action: "drop_to_top_relevant",
1565
+ reason: "Inherited from Flash family: tool reliability drops above ~20 (despite 128 hard limit). Validate per (archetype, model) after n\u226520."
1566
+ },
1567
+ {
1568
+ metric: "thinking_with_short_output",
1569
+ threshold: 1,
1570
+ action: "force_thinking_budget_zero",
1571
+ reason: "Thinking mode supported per Google docs; same drain risk as 2.5-flash \u2014 thinking tokens consume maxOutputTokens"
1572
+ },
1573
+ {
1574
+ // Inherited from 2.5-flash s11 trust artifact (5/5 empty rate on
1575
+ // tt-intelligence/summarize/gemini-2.5-flash with tools offered).
1576
+ // Family-likely failure mode for Flash architecture across versions.
1577
+ // Keep preemptively until brain evidence on 3.5-flash specifically.
1578
+ metric: "tool_count",
1579
+ threshold: 1,
1580
+ whenIntent: "summarize",
1581
+ action: "strip_tools",
1582
+ reason: "Inherited from 2.5-flash s11 cliff (kgauto commit 3872832): summarize+tools \u2192 empty response. Preemptive guard until brain evidence on 3.5-flash specifically."
1583
+ }
1584
+ ],
1585
+ costInputPer1m: 1.5,
1586
+ costOutputPer1m: 9,
1587
+ lowering: {
1588
+ ...GOOGLE_LOWERING_BASE,
1589
+ // 10× cache discount per Google pricing: $0.15/M cached vs $1.50/M input.
1590
+ cache: { ...GOOGLE_LOWERING_BASE.cache, discount: 0.1 },
1591
+ thinking: { field: "generationConfig.thinkingConfig.thinkingBudget", default: "auto" }
1592
+ },
1593
+ recovery: [
1594
+ {
1595
+ signal: "empty_response_after_tool",
1596
+ action: "retry_with_params",
1597
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
1598
+ maxRetries: 1,
1599
+ reason: "Inherited Flash-family pattern: empty after tool result \u2014 retry with thinking off"
1600
+ },
1601
+ {
1602
+ signal: "empty_response",
1603
+ action: "retry_with_params",
1604
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
1605
+ maxRetries: 1,
1606
+ reason: "Empty response \u2014 try with thinking off"
1607
+ },
1608
+ {
1609
+ signal: "malformed_function_call",
1610
+ action: "escalate",
1611
+ reason: "MALFORMED_FUNCTION_CALL maps to stop \u2014 escalate to next target"
1612
+ }
1613
+ ],
1614
+ strengths: ["agentic_loops", "coding", "1m_context", "parallel_tools", "thinking_mode", "sustained_frontier"],
1615
+ weaknesses: ["cost_vs_2_5_flash", "no_brain_evidence_yet"],
1616
+ notes: "Hand-onboarded s37 (2026-05-21) verified against Google docs. Stable status; positioned as Flash-family upgrade for agentic loops and coding. 5\xD7/3.6\xD7 more expensive than 2.5-flash but Google claims step-change on sustained frontier work. archetypePerf adjustments are judgment-grounded starter hypotheses \u2014 brain evidence will validate within ~50 rows per archetype.",
1617
+ // Starter hypothesis: anchored to 2.5-flash archetypePerf with +1
1618
+ // adjustments where Google's positioning explicitly supports
1619
+ // (agentic/coding/sustained). Hunt held at 9 inherited from L-040 family
1620
+ // parallel-tool tier. Brain evidence will replace.
1621
+ archetypePerf: {
1622
+ hunt: 9,
1623
+ // Inherited from 2.5-flash L-040 parallel-tool tier; Google positions 3.5 as agentic-loop champion
1624
+ classify: 7,
1625
+ // Inherited from 2.5-flash brain-validated tier (218 rows on 2.5)
1626
+ summarize: 7,
1627
+ // Inherited from 2.5-flash; cliff strips tools when present
1628
+ transform: 7,
1629
+ ask: 8,
1630
+ // +1 vs 2.5-flash — sustained-frontier positioning
1631
+ generate: 8,
1632
+ // +1 vs 2.5-flash (6→8) — Google: "complex coding cycles and iterations"
1633
+ plan: 7,
1634
+ // +1 vs 2.5-flash (5→7) — "complex iterations" positioning
1635
+ extract: 7,
1636
+ // +1 vs 2.5-flash — sustained-frontier on structured tasks
1637
+ critique: 5
1638
+ // +1 vs 2.5-flash — but still below Sonnet/Opus reasoning floor
1639
+ }
1640
+ },
1641
+ // ─────────────────────────────────────────────────────────────────────────
1642
+ // gemini-3.6-flash — hand-onboarded s86 (2026-08-14), step-zero against
1643
+ // Google docs (models/gemini-3.6-flash page + pricing via catalog-sync's
1644
+ // two-agreeing-extraction the same day). Docs list it as Stable and call
1645
+ // it "previous-generation Flash" (3.5-flash is now "legacy" in Google's
1646
+ // wording) — but kgauto status is 'preview' per the standing entry rule:
1647
+ // a new model enters on capability data verified only at the docs level,
1648
+ // preview is unreachable by starter chains, and promotion to routing
1649
+ // happens through brain evidence, not through Google's marketing tier.
1650
+ // This also means NO family-primary retarget rides this release.
1651
+ // ─────────────────────────────────────────────────────────────────────────
1652
+ {
1653
+ id: "gemini-3.6-flash",
1654
+ verifiedAgainstDocs: "2026-08-24",
1655
+ versionAdded: "2.0.0-alpha.87",
1656
+ provider: "google",
1657
+ status: "preview",
1658
+ maxContextTokens: 1048576,
1659
+ maxOutputTokens: 65536,
1660
+ maxTools: 128,
1661
+ parallelToolCalls: true,
1662
+ structuredOutput: "native",
1663
+ systemPromptMode: "separate",
1664
+ streaming: true,
1665
+ cliffs: [
1666
+ {
1667
+ metric: "input_tokens",
1668
+ threshold: 8e3,
1669
+ action: "downgrade_quality_warning",
1670
+ reason: "Inherited Flash-family guard (2.5\u21923.5 lineage); no 3.6 brain evidence yet"
1671
+ },
1672
+ {
1673
+ metric: "tool_count",
1674
+ threshold: 20,
1675
+ action: "drop_to_top_relevant",
1676
+ reason: "Inherited from Flash family: tool reliability drops above ~20 (despite 128 hard limit). Validate per (archetype, model) after n\u226520."
1677
+ },
1678
+ {
1679
+ metric: "thinking_with_short_output",
1680
+ threshold: 1,
1681
+ action: "force_thinking_budget_zero",
1682
+ reason: "Thinking supported per Google docs; same drain risk as 2.5/3.5-flash \u2014 thinking tokens consume maxOutputTokens"
1683
+ },
1684
+ {
1685
+ metric: "tool_count",
1686
+ threshold: 1,
1687
+ whenIntent: "summarize",
1688
+ action: "strip_tools",
1689
+ reason: "Inherited from 2.5-flash s11 cliff: summarize+tools \u2192 empty response. Preemptive family guard until brain evidence on 3.6-flash specifically."
1690
+ }
1691
+ ],
1692
+ costInputPer1m: 0.75,
1693
+ // promo through 2026-12-31; $1.50 from 2027-01-01 (TIME_BOUNDED_PRICING_ROWS carries the transition)
1694
+ costOutputPer1m: 3.75,
1695
+ // promo through 2026-12-31; $7.50 from 2027-01-01
1696
+ lowering: {
1697
+ ...GOOGLE_LOWERING_BASE,
1698
+ cache: { ...GOOGLE_LOWERING_BASE.cache, discount: 0.1 },
1699
+ thinking: { field: "generationConfig.thinkingConfig.thinkingBudget", default: "auto" }
1700
+ },
1701
+ recovery: [
1702
+ {
1703
+ signal: "empty_response_after_tool",
1704
+ action: "retry_with_params",
1705
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
1706
+ maxRetries: 1,
1707
+ reason: "Inherited Flash-family pattern: empty after tool result \u2014 retry with thinking off"
1708
+ },
1709
+ {
1710
+ signal: "empty_response",
1711
+ action: "retry_with_params",
1712
+ retryParams: { "generationConfig.thinkingConfig.thinkingBudget": 0 },
1713
+ maxRetries: 1,
1714
+ reason: "Empty response \u2014 try with thinking off"
1715
+ },
1716
+ {
1717
+ signal: "malformed_function_call",
1718
+ action: "escalate",
1719
+ reason: "MALFORMED_FUNCTION_CALL maps to stop \u2014 escalate to next target"
1720
+ }
1721
+ ],
1722
+ strengths: ["agentic_loops", "coding", "1m_context", "parallel_tools", "thinking_mode"],
1723
+ weaknesses: ["no_brain_evidence_yet"],
1724
+ notes: "Hand-onboarded s86 (2026-08-14), step-zero verified: 1,048,576 ctx / 65,536 out (Google model page). PRICING re-verified 2026-08-24 at publish: $0.75/$3.75 promo through 2026-12-31, $1.50/$7.50 from 2027-01-01 \u2014 the alpha.87 profile carried the post-promo rate as if current; corrected alpha.88. Docs status Stable; enters kgauto as preview per standing rule (availability != lifecycle, promotion via brain evidence). Function calling + structured outputs + thinking per docs; parallel tools assumed per Flash family (docs silent). First flagged by the release watcher s73 (2026-07-24); ~21 days in the onboarding queue (onboarded 2026-08-14).",
1725
+ archetypePerf: {
1726
+ hunt: 9,
1727
+ // Flash-family parallel-tool tier (L-040); inherited from 3.5-flash
1728
+ classify: 7,
1729
+ summarize: 7,
1730
+ transform: 7,
1731
+ ask: 8,
1732
+ generate: 8,
1733
+ plan: 7,
1734
+ extract: 7,
1735
+ critique: 5
1736
+ }
1737
+ },
1738
+ {
1739
+ // Auto-onboarded 2026-07-01 from `claude-sonnet-4-6`; VERIFIED 2026-07-02
1740
+ // against the claude-api reference (cc-portfolio ratification pass). The
1741
+ // clone got context right (1M). PRICING RE-BASED 2026-08-14 (s86): the
1742
+ // introductory $2/$10 was made PERMANENT — Anthropic's pricing page now
1743
+ // states the scheduled Sept-1 increase to $3/$15 "will not occur", so
1744
+ // $2/$10 IS the sticker. (The $3/$15 encoded 07-02 was correct under the
1745
+ // time-bounded-pricing convention at the time; the bound was cancelled.) The clone got max output WRONG:
1746
+ // Sonnet 5 is 128k, not 4-6's 64k — corrected. New tokenizer (~30% more
1747
+ // tokens for the same text vs 4-6): byte-budget consumers should re-baseline.
1748
+ // API quirks (claude-api ref): (a) NON-DEFAULT temperature/top_p/top_k
1749
+ // return 400 — moot for kgauto's own call() path (ANTHROPIC_LOWERING_BASE
1750
+ // emits no sampling params) but a REAL hazard for compileForAISDKv6
1751
+ // consumers that pass temperature themselves (tt-intel scoring uses temp:0
1752
+ // for determinism — that 400s on this model; noted to consumers via the
1753
+ // contract). (b) Omitting `thinking` runs ADAPTIVE by default (4-6 ran
1754
+ // thinking-off) — output spend shifts. (c) Supports effort xhigh. status:
1755
+ // 'preview' per the Fable precedent — no brain evidence yet; promotion to
1756
+ // 'current' is an explicit call. (L-049/L-081.)
1757
+ id: "claude-sonnet-5",
1758
+ verifiedAgainstDocs: "2026-08-14",
1759
+ provider: "anthropic",
1760
+ status: "current",
1761
+ // s76 2026-07-25: GA per docs; was preview.
1762
+ maxContextTokens: 1e6,
1763
+ maxOutputTokens: 128e3,
1764
+ maxTools: 64,
1765
+ parallelToolCalls: true,
1766
+ structuredOutput: "grammar",
1767
+ systemPromptMode: "inline",
1768
+ streaming: true,
1769
+ cliffs: [],
1770
+ costInputPer1m: 2,
1771
+ // 2026-08-24: intro price made PERMANENT (docs: "the previously scheduled increase to $3/$15 on September 1, 2026 will not occur")
1772
+ costOutputPer1m: 10,
1773
+ lowering: ANTHROPIC_LOWERING_BASE,
1774
+ recovery: [
1775
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate" },
1776
+ { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
1777
+ ],
1778
+ strengths: ["quality", "tool_use", "long_context", "cache_friendly", "extended_thinking"],
1779
+ weaknesses: [],
1780
+ notes: "Sonnet 5 (2026-06): near-Opus quality on coding/agentic work at Sonnet cost. Verified 2026-07-02 against the claude-api reference: 1M ctx, 128k out (clone's 64k corrected), $3/$15 sticker (intro $2/$10 through 2026-08-31). New tokenizer ~30% more tokens vs sonnet-4-6. Consumer hazards: non-default temperature/top_p/top_k 400 (temp:0 rejected \u2014 deterministic-scoring consumers must omit); thinking defaults to adaptive when omitted. status:preview \u2014 no brain evidence yet; earns placement via the machinery.",
1781
+ // Master plan §6.2 anchor. Tier 0 for plan/generate/ask/extract/transform
1782
+ // in starter chains; tier 1 cross-provider for hunt/summarize/classify.
1783
+ archetypePerf: {
1784
+ ask: 9,
1785
+ generate: 9,
1786
+ plan: 9,
1787
+ critique: 9,
1788
+ extract: 9,
1789
+ transform: 9,
1790
+ hunt: 7,
1791
+ // strong but Flash beats on parallel tool throughput
1792
+ summarize: 8,
1793
+ // overkill for tolerant archetype
1794
+ classify: 8
1795
+ // overkill
1796
+ }
1797
+ },
1798
+ // ── Z.ai (GLM) ──
1799
+ // alpha.65 (2026-07-18): first Z.ai profile, provider `zai` added same
1800
+ // release. Step-zero sources (L-049/L-081, all fetched 2026-07-18):
1801
+ // docs.z.ai/guides/llm/glm-5.2 (capabilities), docs.z.ai api-reference
1802
+ // chat-completion (wire params: thinking.type enabled|disabled default
1803
+ // enabled, reasoning_effort max..none, tool_stream, max output 131072),
1804
+ // docs.z.ai/guides/overview/pricing ($1.4 in / $4.4 out, cached input
1805
+ // $0.26, cache storage limited-time free), vLLM recipe (native ctx
1806
+ // 1,048,576). NO AI-trained numbers.
1807
+ {
1808
+ id: "glm-5.2",
1809
+ verifiedAgainstDocs: "2026-07-18",
1810
+ provider: "zai",
1811
+ status: "preview",
1812
+ maxContextTokens: 1048576,
1813
+ maxOutputTokens: 131072,
1814
+ // No documented tool-count cap — conservative default pending measured
1815
+ // evidence; matches the deepseek posture, not a doc number.
1816
+ maxTools: 16,
1817
+ // Hosted-API docs document tools + tool_choice + tool_stream but say
1818
+ // NOTHING about multiple tool_calls per response. Conservative false so
1819
+ // hunt-shaped ranking doesn't over-promise; flip on doc or probe
1820
+ // evidence (L-081: undocumented ≠ true).
1821
+ parallelToolCalls: false,
1822
+ structuredOutput: "native",
1823
+ systemPromptMode: "inline",
1824
+ streaming: true,
1825
+ cliffs: [],
1826
+ costInputPer1m: 1.4,
1827
+ costOutputPer1m: 4.4,
1828
+ lowering: {
1829
+ system: { mode: "inline" },
1830
+ // Automatic prefix caching ("intelligent caching mechanism") — no wire
1831
+ // marker to emit, DeepSeek-style. Cache-hit input $0.26/M (~0.19× of
1832
+ // miss) tracked in notes; storage limited-time free as of 2026-07-18.
1833
+ cache: { strategy: "unsupported" },
1834
+ tools: { format: "openai" },
1835
+ // thinking.type: 'enabled' (server default) | 'disabled'. lowerZai
1836
+ // emits an explicit disable only on forceThinkingZero cliffs.
1837
+ thinking: { field: "thinking.type", default: "auto" }
1838
+ },
1839
+ recovery: [
1840
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate" },
1841
+ { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
1842
+ ],
1843
+ strengths: ["cost", "1m_context", "code", "tool_use", "json_output", "reasoning"],
1844
+ weaknesses: ["parallel_tools"],
1845
+ notes: "GLM-5.2 (Z.ai, 2026-06): agentic-coding flagship. 1,048,576 ctx / 131,072 max out. $1.40/$4.40 per 1M; cached input $0.26/M (automatic caching, no marker; storage limited-time free as of 2026-07-18). Thinking on by default (`thinking.type`), `reasoning_effort` max..none. Parallel tool calls UNDOCUMENTED on the hosted API \u2014 profile says false until doc or probe evidence. status:preview \u2014 no brain evidence yet; earns placement via the machinery.",
1846
+ // Starter hypotheses — verify with telemetry/probes; NO brain evidence
1847
+ // yet. Anchored on the vendor's agentic-coding positioning at a
1848
+ // mid-cost tier, discounted for the unproven parallel-tool story.
1849
+ archetypePerf: {
1850
+ generate: 7,
1851
+ // agentic-coding positioning — starter hypothesis
1852
+ transform: 7,
1853
+ // starter hypothesis
1854
+ plan: 7,
1855
+ // starter hypothesis
1856
+ summarize: 7,
1857
+ // tolerant archetype — starter hypothesis
1858
+ ask: 6,
1859
+ extract: 6,
1860
+ critique: 6,
1861
+ classify: 6,
1862
+ hunt: 5
1863
+ // tools work; parallel unproven → discounted
1864
+ }
1865
+ },
1866
+ // ── Moonshot (Kimi) ──
1867
+ // alpha.65 (2026-07-18): first Moonshot profile, provider `moonshot` added
1868
+ // same release. Step-zero sources (all fetched 2026-07-18):
1869
+ // platform.kimi.ai/docs/pricing/chat-k3 ($3.00 cache-miss in / $0.30
1870
+ // cache-hit in / $15.00 out; 1,048,576 ctx; ToolCalls + JSON Mode +
1871
+ // Partial Mode), kimi-k3-quickstart (max_completion_tokens default
1872
+ // 131072, up to 1,048,576; base https://api.moonshot.ai/v1; vision via
1873
+ // base64 or ms://file-id, NO public image URLs), models-overview
1874
+ // (temperature FIXED 1.0, top_p FIXED 0.95, n FIXED 1, always reasons,
1875
+ // reasoning_effort 'max' only), tool-calls guide ("can choose to call
1876
+ // multiple tools at once … will tend to call them in parallel").
1877
+ {
1878
+ id: "kimi-k3",
1879
+ verifiedAgainstDocs: "2026-07-18",
1880
+ provider: "moonshot",
1881
+ status: "preview",
1882
+ maxContextTokens: 1048576,
1883
+ // Documented ceiling; default is 131,072 when unset.
1884
+ maxOutputTokens: 1048576,
1885
+ // No hard doc cap; docs recommend dynamic tool loading at "dozens or
1886
+ // hundreds" — 64 matches the frontier-agentic posture (sonnet-5 tier).
1887
+ maxTools: 64,
1888
+ parallelToolCalls: true,
1889
+ structuredOutput: "native",
1890
+ systemPromptMode: "inline",
1891
+ streaming: true,
1892
+ cliffs: [],
1893
+ costInputPer1m: 3,
1894
+ costOutputPer1m: 15,
1895
+ lowering: {
1896
+ system: { mode: "inline" },
1897
+ // Automatic context caching — no wire marker. Cache-hit input $0.30/M
1898
+ // (0.1× of miss) tracked in notes.
1899
+ cache: { strategy: "unsupported" },
1900
+ tools: { format: "openai" }
1901
+ // No thinking knob: reasoning is always-on and reasoning_effort
1902
+ // accepts only 'max' (= the server default). Nothing to lower.
1903
+ },
1904
+ recovery: [
1905
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate" },
1906
+ { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
1907
+ ],
1908
+ strengths: ["quality", "reasoning", "1m_context", "parallel_tools", "tool_use", "code", "vision"],
1909
+ // 'latency' → latencyTierOf derives 'slow': always-on max-effort
1910
+ // reasoner with no off switch; no measured p50 yet, the tag is the
1911
+ // honest prior (reasoner family precedent: deepseek-v4-pro ~48s).
1912
+ weaknesses: ["cost", "latency"],
1913
+ notes: "Kimi K3 (Moonshot, API live 2026-07-16): 2.8T-param open-weight flagship, native vision. 1,048,576 ctx; max_completion_tokens default 131,072, ceiling 1,048,576. $3.00/$15.00 per 1M, cache-hit input $0.30/M (automatic caching). Reasoning ALWAYS ON (reasoning_effort max only, cannot disable; responses may carry reasoning_content). Wire quirks: temperature fixed 1.0 / top_p 0.95 / n 1 (kgauto never emits those); vision input must be base64 or ms://file-id \u2014 public image URLs rejected. status:preview \u2014 earns placement via the machinery.",
1914
+ // Starter hypotheses — verify with telemetry/probes; NO brain evidence
1915
+ // yet. Anchored on documented long-horizon-agentic + parallel-tool
1916
+ // strengths; discounted on terse archetypes where always-on reasoning
1917
+ // bills $15/M output for thinking the archetype doesn't need.
1918
+ archetypePerf: {
1919
+ hunt: 8,
1920
+ // parallel tools + long-horizon agentic — starter hypothesis
1921
+ plan: 8,
1922
+ // starter hypothesis
1923
+ generate: 8,
1924
+ // starter hypothesis
1925
+ critique: 7,
1926
+ ask: 7,
1927
+ extract: 7,
1928
+ transform: 7,
1929
+ summarize: 6,
1930
+ // works, but $15/M output reasoning tax on a tolerant archetype
1931
+ classify: 5
1932
+ // always-on reasoning cost + reasoner hedge risk
1933
+ },
1934
+ // Diagnostics-only (no prompt rewrite): reasoner-family hedge risk on
1935
+ // decisive archetypes, carried as a warning until K3-specific probe
1936
+ // evidence exists. The deepseek-reasoner family showed 8/10 judge
1937
+ // rationales citing hedging on classify (exclusion-finding ID 20,
1938
+ // 2026-05-28); K3 is an always-on reasoner and inherits the risk class,
1939
+ // not the conviction.
1940
+ archetypeConventions: [
1941
+ {
1942
+ archetype: "classify",
1943
+ cliffWarning: "kimi-k3 is an always-on reasoner: decisive archetypes (classify) may hedge, and reasoning tokens bill at $15/M output. Family-prior risk (deepseek-reasoner precedent), not K3-measured \u2014 probe before relying on it for classify.",
1944
+ reason: "Reasoner-family prior (exclusion-finding ID 20, 2026-05-28, deepseek-v4-pro on tt-intel/classify). No K3-specific evidence yet \u2014 warning only, no prompt rewrite."
1945
+ }
1946
+ ]
1947
+ }
1948
+ ];
1949
+ var ALIASES = {
1950
+ // DeepSeek's own model routing — both names served by V4-Flash.
1951
+ "deepseek-chat": "deepseek-v4-flash",
1952
+ "deepseek-reasoner": "deepseek-v4-flash",
1953
+ // Legacy kgauto typo — actual API alias is dash-form (alpha.1 had dot).
1954
+ "claude-haiku-4.5": "claude-haiku-4-5"
1955
+ };
1956
+ var brainHook = {};
1957
+ function canonicalId(id) {
1958
+ return brainHook.resolveAlias?.(id) ?? ALIASES[id] ?? id;
1959
+ }
1960
+ var CONSUMER_INDEX = /* @__PURE__ */ new Map();
1961
+ function resolveModelAlias(id) {
1962
+ return canonicalId(id);
1963
+ }
1964
+ var PROFILE_INDEX = new Map(
1965
+ PROFILES_RAW.map((p) => [p.id, p])
1966
+ );
1967
+ function tryGetProfile(id) {
1968
+ const canonical = canonicalId(id);
1969
+ return CONSUMER_INDEX.get(canonical) ?? brainHook.getProfile?.(canonical) ?? PROFILE_INDEX.get(canonical);
1970
+ }
1971
+ function allProfiles() {
1972
+ return PROFILES_RAW;
1973
+ }
1974
+
1975
+ // src/env.ts
1976
+ var SUPPORTED_PROVIDERS = Object.freeze([
1977
+ "anthropic",
1978
+ "google",
1979
+ "openai",
1980
+ "deepseek",
1981
+ "zai",
1982
+ "moonshot"
1983
+ ]);
1984
+ function isSupportedProvider(p) {
1985
+ return SUPPORTED_PROVIDERS.includes(p);
1986
+ }
1987
+ var PROVIDER_ENV_KEYS = Object.freeze({
1988
+ anthropic: Object.freeze(["ANTHROPIC_API_KEY"]),
1989
+ google: Object.freeze([
1990
+ "GOOGLE_API_KEY",
1991
+ "GEMINI_API_KEY",
1992
+ "GOOGLE_GENERATIVE_AI_API_KEY"
1993
+ ]),
1994
+ openai: Object.freeze(["OPENAI_API_KEY"]),
1995
+ deepseek: Object.freeze(["DEEPSEEK_API_KEY"]),
1996
+ // alpha.65 — Z.ai (GLM family). ZAI_API_KEY is the canonical name; the
1997
+ // Z_AI_API_KEY variant covers the underscore convention some tooling uses
1998
+ // for the brand's "Z.ai" spelling.
1999
+ zai: Object.freeze(["ZAI_API_KEY", "Z_AI_API_KEY"]),
2000
+ // alpha.65 — Moonshot AI (Kimi family). MOONSHOT_API_KEY is canonical
2001
+ // (api.moonshot.ai); KIMI_API_KEY covers the platform.kimi.ai rebrand
2002
+ // surface so consumers keyed under either name resolve.
2003
+ moonshot: Object.freeze(["MOONSHOT_API_KEY", "KIMI_API_KEY"])
2004
+ });
2005
+ function defaultEnv() {
2006
+ return typeof process !== "undefined" && process.env ? process.env : {};
2007
+ }
2008
+ function readKeyValue(raw) {
2009
+ if (raw === void 0) return void 0;
2010
+ const trimmed = raw.trim();
2011
+ return trimmed.length > 0 ? trimmed : void 0;
2012
+ }
2013
+ function resolveProviderKey(provider, opts = {}) {
2014
+ if (!isSupportedProvider(provider)) return void 0;
2015
+ const explicit = readKeyValue(opts.apiKeys?.[provider]);
2016
+ if (explicit) return explicit;
2017
+ const env = opts.envSource ?? defaultEnv();
2018
+ for (const name of PROVIDER_ENV_KEYS[provider]) {
2019
+ const v = readKeyValue(env[name]);
2020
+ if (v) return v;
2021
+ }
2022
+ return void 0;
2023
+ }
2024
+ function isProviderReachable(provider, opts = {}) {
2025
+ return resolveProviderKey(provider, opts) !== void 0;
2026
+ }
2027
+ function isModelReachable(modelId, opts = {}) {
2028
+ const profile = tryGetProfile(modelId);
2029
+ if (!profile) return false;
2030
+ return isProviderReachable(profile.provider, opts);
2031
+ }
2032
+
2033
+ // src/brain-query.ts
2034
+ var FRESH_SNAPSHOT = {
2035
+ data: null,
2036
+ expiresAt: 0,
2037
+ refreshing: false,
2038
+ warned: false
2039
+ };
2040
+ var snapshot = { ...FRESH_SNAPSHOT };
2041
+ var runtime;
2042
+ function createBrainQueryCache(opts) {
2043
+ return () => {
2044
+ const rt = runtime;
2045
+ if (!rt || !rt.enabledTables.has(opts.table)) {
2046
+ return opts.bundledFallback();
2047
+ }
2048
+ const now = Date.now();
2049
+ const stale = snapshot.expiresAt <= now;
2050
+ if (stale && !snapshot.refreshing) {
2051
+ snapshot.refreshing = true;
2052
+ void asyncRefresh(rt);
2053
+ }
2054
+ if (snapshot.data) {
2055
+ const rows = snapshot.data[opts.table];
2056
+ if (Array.isArray(rows) && rows.length > 0) {
2057
+ try {
2058
+ return opts.mapRows(rows);
2059
+ } catch {
2060
+ return opts.bundledFallback();
2061
+ }
2062
+ }
2063
+ }
2064
+ return opts.bundledFallback();
2065
+ };
2066
+ }
2067
+ var pendingRefresh;
2068
+ async function asyncRefresh(rt) {
2069
+ const promise = doRefresh(rt);
2070
+ pendingRefresh = promise;
2071
+ try {
2072
+ await promise;
2073
+ } finally {
2074
+ if (pendingRefresh === promise) pendingRefresh = void 0;
2075
+ }
2076
+ }
2077
+ var DEFAULT_CONFIG_URL = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/config";
2078
+ async function doRefresh(rt) {
2079
+ const url = rt.configEndpoint ?? DEFAULT_CONFIG_URL;
2080
+ try {
2081
+ const res = await rt.fetchImpl(url, { method: "GET" });
2082
+ if (!res.ok) {
2083
+ throw new Error(`brain-query ${res.status}: ${res.statusText}`);
2084
+ }
2085
+ const body = await res.json();
2086
+ if (runtime !== rt) return;
2087
+ snapshot = {
2088
+ data: body,
2089
+ expiresAt: Date.now() + rt.ttlMs,
2090
+ refreshing: false,
2091
+ warned: snapshot.warned
2092
+ };
2093
+ } catch (err) {
2094
+ if (runtime !== rt) return;
2095
+ snapshot.refreshing = false;
2096
+ snapshot.expiresAt = Date.now() + rt.ttlMs;
2097
+ if (!snapshot.warned) {
2098
+ snapshot.warned = true;
2099
+ (rt.onError ?? defaultOnError)(err);
2100
+ }
2101
+ }
2102
+ }
2103
+ function defaultOnError(err) {
2104
+ console.warn("[kgauto] brain-query failed (using bundled fallback):", err);
2105
+ }
2106
+
2107
+ // src/chains-brain.ts
2108
+ function isChainsRow(x) {
2109
+ if (!x || typeof x !== "object") return false;
2110
+ const r = x;
2111
+ return typeof r.archetype === "string" && typeof r.tier === "number" && typeof r.model_id === "string";
2112
+ }
2113
+ function mapRowsToChains(rows) {
2114
+ const grouped = /* @__PURE__ */ new Map();
2115
+ for (const row of rows) {
2116
+ if (!isChainsRow(row)) continue;
2117
+ const list = grouped.get(row.archetype) ?? [];
2118
+ list.push(row);
2119
+ grouped.set(row.archetype, list);
2120
+ }
2121
+ const out = {};
2122
+ for (const [archetype, group] of grouped.entries()) {
2123
+ group.sort((a, b) => a.tier - b.tier);
2124
+ out[archetype] = group.map((r) => r.model_id);
2125
+ }
2126
+ const bundled = getAllStarterChains();
2127
+ for (const archetype of Object.keys(bundled)) {
2128
+ if (!out[archetype]) out[archetype] = bundled[archetype];
2129
+ }
2130
+ return out;
2131
+ }
2132
+ var loadChainsFromBrain = createBrainQueryCache({
2133
+ table: "kgauto_chains",
2134
+ mapRows: mapRowsToChains,
2135
+ bundledFallback: getAllStarterChains
2136
+ });
2137
+
2138
+ // src/policy-match.ts
2139
+ function canonicalPolicySet(ids) {
2140
+ const set = /* @__PURE__ */ new Set();
2141
+ for (const id of ids ?? []) set.add(resolveModelAlias(id));
2142
+ return set;
2143
+ }
2144
+ function policySetHas(set, modelId) {
2145
+ if (set.size === 0) return false;
2146
+ return set.has(resolveModelAlias(modelId));
2147
+ }
2148
+
2149
+ // src/archetype-perf-brain.ts
2150
+ function isPerfRow(x) {
2151
+ if (!x || typeof x !== "object") return false;
2152
+ const r = x;
2153
+ return typeof r.model_id === "string" && typeof r.archetype === "string" && typeof r.perf_score === "number";
2154
+ }
2155
+ function mapRowsToPerfMap(rows) {
2156
+ const out = /* @__PURE__ */ new Map();
2157
+ for (const row of rows) {
2158
+ if (!isPerfRow(row)) continue;
2159
+ const existing = out.get(row.model_id) ?? {};
2160
+ existing[row.archetype] = row.perf_score;
2161
+ out.set(row.model_id, existing);
2162
+ }
2163
+ return out;
2164
+ }
2165
+ function mapRowsToNMap(rows) {
2166
+ const out = /* @__PURE__ */ new Map();
2167
+ for (const row of rows) {
2168
+ if (!isPerfRow(row)) continue;
2169
+ if (typeof row.n !== "number") continue;
2170
+ const existing = out.get(row.model_id) ?? {};
2171
+ existing[row.archetype] = row.n;
2172
+ out.set(row.model_id, existing);
2173
+ }
2174
+ return out;
2175
+ }
2176
+ function bundledArchetypePerf() {
2177
+ const out = /* @__PURE__ */ new Map();
2178
+ for (const profile of allProfiles()) {
2179
+ if (profile.archetypePerf) out.set(profile.id, profile.archetypePerf);
2180
+ }
2181
+ return out;
2182
+ }
2183
+ function bundledArchetypePerfN() {
2184
+ return /* @__PURE__ */ new Map();
2185
+ }
2186
+ var loadArchetypePerfFromBrain = createBrainQueryCache({
2187
+ table: "kgauto_archetype_perf",
2188
+ mapRows: mapRowsToPerfMap,
2189
+ bundledFallback: bundledArchetypePerf
2190
+ });
2191
+ var loadArchetypePerfNFromBrain = createBrainQueryCache(
2192
+ {
2193
+ table: "kgauto_archetype_perf",
2194
+ mapRows: mapRowsToNMap,
2195
+ bundledFallback: bundledArchetypePerfN
2196
+ }
2197
+ );
2198
+ var MEASURED_GROUNDING_MIN_N = 10;
2199
+ function getArchetypePerfScore(modelId, archetype) {
2200
+ const score = loadArchetypePerfFromBrain().get(modelId)?.[archetype] ?? 5;
2201
+ const n = loadArchetypePerfNFromBrain().get(modelId)?.[archetype] ?? 0;
2202
+ const grounding = n >= MEASURED_GROUNDING_MIN_N ? "measured" : "judgment";
2203
+ return { score, n, grounding };
2204
+ }
2205
+
2206
+ // src/compatibility.ts
2207
+ var ARCHETYPE_FLOOR_DEFAULT = 6;
2208
+
2209
+ // src/fallback.ts
2210
+ var COST_RANKING_REFERENCE_SHAPE = {
2211
+ inputTokens: 4e3,
2212
+ outputTokens: 250
2213
+ };
2214
+ function estimateModelCostUsd(profile, shape = COST_RANKING_REFERENCE_SHAPE) {
2215
+ if (!profile || typeof profile !== "object" || Array.isArray(profile) || typeof profile.costInputPer1m !== "number" || typeof profile.costOutputPer1m !== "number") {
2216
+ throw new TypeError(
2217
+ `estimateModelCostUsd: expected a single ModelProfile, got ${Array.isArray(profile) ? "an array (a chain? map over it)" : typeof profile}. For a chain: chain.map((id) => estimateModelCostUsd(getProfile(id))).`
2218
+ );
2219
+ }
2220
+ return shape.inputTokens / 1e6 * profile.costInputPer1m + shape.outputTokens / 1e6 * profile.costOutputPer1m;
2221
+ }
2222
+ var estimateChainCostUsd = estimateModelCostUsd;
2223
+ function buildCostOrderedChain(archetype) {
2224
+ return allProfiles().filter((p) => p.status === "current").filter(
2225
+ (p) => getArchetypePerfScore(p.id, archetype).score >= ARCHETYPE_FLOOR_DEFAULT
2226
+ ).map((p) => ({ p, cost: estimateChainCostUsd(p) })).sort((a, b) => a.cost - b.cost || a.p.id.localeCompare(b.p.id)).map((e) => e.p.id);
2227
+ }
2228
+ var STARTER_CHAINS_GROUNDED = {
2229
+ // Reasoning floor — never degrade. Walk UP on 429 to Opus → cross-provider.
2230
+ critique: [
2231
+ { id: "claude-opus-5", grounding: "judgment", reason: "Highest reasoning bar, no degradation tier \u2014 engineer pick, awaiting measured backing. alpha.90: retargeted from legacy claude-opus-4-7 (Opus 5 generation current since 2026-07-25; same drift class as the b3d919b judge default)" },
2232
+ { id: "claude-sonnet-5", grounding: "judgment", reason: "Same-provider walk-down from Opus on 429. alpha.90: retargeted from legacy claude-sonnet-4-6" },
2233
+ // alpha.86: gemini-2.5-pro dropped — availability-restricted (tt-intel
2234
+ // measured 4/4 cohort refusal 2026-08-02; kgauto key free-tier-quota
2235
+ // 429 2026-08-08). No current Google model clears the critique floor
2236
+ // (3.5-flash critique=5; 3.1-pro-preview scores 10 but is preview —
2237
+ // default chains carry promoted models only). deepseek-v4-pro takes the
2238
+ // third-provider tail instead.
2239
+ { id: "gpt-5.5", grounding: "judgment", reason: "alpha.16: third-provider frontier-tier floor (archetypePerf=9)" },
2240
+ { id: "deepseek-v4-pro", grounding: "judgment", reason: "alpha.86: third-provider tail replacing availability-restricted gemini-2.5-pro \u2014 critique=6 clears the floor exactly; last-resort when Anthropic AND OpenAI are down, not a quality peer of the tiers above" }
2241
+ ],
2242
+ // alpha.62 (eval spine) — pairwise output comparison. Same reasoning-floor
2243
+ // posture as critique (a verdict from a weak judge is worse than no
2244
+ // verdict); tier 0 anchored on the CURRENT opus (4-8, s64 roster). Strict
2245
+ // JSON output, so every tier declares native structured output.
2246
+ judge: [
2247
+ { id: "claude-opus-5", grounding: "judgment", reason: "Highest reasoning bar for pairwise verdicts \u2014 current opus. alpha.90: retargeted from legacy claude-opus-4-8, matching DEFAULT_JUDGE_MODEL (moved in b3d919b); verdict comparability preserved by judge_model on every run/case row" },
2248
+ { id: "claude-sonnet-5", grounding: "judgment", reason: "Same-provider walk-down on 429. alpha.90: retargeted from legacy claude-sonnet-4-6" },
2249
+ // alpha.86: gemini-2.5-pro dropped (availability-restricted; see critique
2250
+ // note). gpt-5.5 remains the cross-provider frontier floor for verdicts.
2251
+ { id: "gpt-5.5", grounding: "judgment", reason: "Third-provider frontier-tier floor" },
2252
+ { id: "deepseek-v4-pro", grounding: "judgment", reason: "alpha.86: third-provider tail replacing availability-restricted gemini-2.5-pro \u2014 native structured output per the judge contract; last-resort tier" }
2253
+ ],
2254
+ // Reasoning matters — Sonnet primary; walk UP to Opus on 429.
2255
+ plan: [
2256
+ { id: "claude-sonnet-5", grounding: "judgment", reason: "Reasoning + cost balance \u2014 engineer pick. alpha.90: retargeted from legacy claude-sonnet-4-6" },
2257
+ { id: "claude-opus-5", grounding: "judgment", reason: 'Same-provider walk-UP on 429 (rare exception to "always cheaper"). alpha.90: retargeted from legacy claude-opus-4-7' },
2258
+ { id: "gemini-3.5-flash", grounding: "judgment", reason: "alpha.86: cross-provider anchor \u2014 replaces availability-restricted gemini-2.5-pro (tt-intel 4/4 cohort refusal 2026-08-02; probed serving 200 on kgauto key 2026-08-08)" },
2259
+ { id: "deepseek-v4-pro", grounding: "judgment", reason: "Tier 3 cost floor \u2014 no brain evidence yet" },
2260
+ { id: "gpt-5.4", grounding: "judgment", reason: "alpha.33: third-provider tail (archetypePerf=7) \u2014 closes openai-in-default-fallback-chains" }
2261
+ ],
2262
+ // Quality + cost match.
2263
+ generate: [
2264
+ { id: "claude-sonnet-5", grounding: "judgment", reason: "Quality + cost match \u2014 engineer pick. alpha.90: retargeted from legacy claude-sonnet-4-6" },
2265
+ { id: "claude-haiku-4-5", grounding: "judgment", reason: "Same-provider step-down" },
2266
+ { id: "gemini-3.5-flash", grounding: "judgment", reason: "alpha.86: cross-provider anchor \u2014 replaces availability-restricted gemini-2.5-pro (tt-intel 4/4 cohort refusal 2026-08-02; probed serving 200 on kgauto key 2026-08-08)" },
2267
+ { id: "gpt-5.4-mini", grounding: "judgment", reason: "alpha.16: third-provider tail (archetypePerf=7) \u2014 closes mono-Anthropic gap" }
2268
+ ],
2269
+ // ask::sonnet — STARTER_CHAINS calls this "Quality + cost match" but
2270
+ // tt-intel s78 prod data showed 27% empty rate. Labeled 'judgment' until
2271
+ // evidence either validates or refutes the placement.
2272
+ ask: [
2273
+ { id: "claude-sonnet-5", grounding: "judgment", reason: "Quality + cost match \u2014 engineer pick. alpha.90: retargeted from legacy claude-sonnet-4-6 (whose tt-intel s78 27% empty rate was never validated; the empty-rate NOTE does not transfer to sonnet-5 \u2014 fresh model, fresh measurement)" },
2274
+ { id: "claude-haiku-4-5", grounding: "judgment", reason: "Same-provider step-down" },
2275
+ { id: "gemini-3.5-flash", grounding: "judgment", reason: "alpha.86: cross-provider anchor \u2014 replaces availability-restricted gemini-2.5-pro (tt-intel 4/4 cohort refusal 2026-08-02; probed serving 200 on kgauto key 2026-08-08)" },
2276
+ { id: "gpt-5.4-mini", grounding: "judgment", reason: "alpha.16: third-provider tail (archetypePerf=7)" }
2277
+ ],
2278
+ // Structured-output archetype — Flash skipped (alpha.8 MAX_TOKENS cliff,
2279
+ // capability-fact); DeepSeek skipped (no brain evidence).
2280
+ extract: [
2281
+ { id: "claude-sonnet-5", grounding: "judgment", reason: "Reliable structured-output anchor \u2014 engineer pick. alpha.90: retargeted from legacy claude-sonnet-4-6 (which served the believe-card endpoint's first real call 2026-08-26 while daily-ops had been advising consumers OFF it for weeks)" },
2282
+ { id: "claude-haiku-4-5", grounding: "judgment", reason: "Same-provider step-down with native structured output" },
2283
+ { id: "gemini-3.5-flash", grounding: "judgment", reason: "alpha.86: cross-provider anchor with native structured output \u2014 replaces availability-restricted gemini-2.5-pro" },
2284
+ { id: "gpt-5.4", grounding: "capability-fact", reason: "alpha.16: third-provider floor \u2014 native structured-output capability (archetypePerf=8)" }
2285
+ ],
2286
+ // Forgiving archetype — Sonnet primary but Flash safely floors it.
2287
+ transform: [
2288
+ { id: "claude-sonnet-5", grounding: "judgment", reason: "Quality anchor \u2014 engineer pick. alpha.90: retargeted from legacy claude-sonnet-4-6" },
2289
+ { id: "claude-haiku-4-5", grounding: "judgment", reason: "Same-provider step-down" },
2290
+ { id: "gemini-3.5-flash", grounding: "judgment", reason: "alpha.86: cross-provider anchor \u2014 replaces availability-restricted gemini-2.5-pro (tt-intel 4/4 cohort refusal 2026-08-02; probed serving 200 on kgauto key 2026-08-08)" },
2291
+ { id: "gemini-2.5-flash", grounding: "judgment", reason: "Cost floor \u2014 forgiving archetype tolerates Flash" },
2292
+ { id: "gpt-5.4-mini", grounding: "judgment", reason: "alpha.33: third-provider tail (archetypePerf=7) \u2014 closes openai-in-default-fallback-chains" }
2293
+ ],
2294
+ // Parallel-tool throughput champion — Flash leads on the L-040 cliff
2295
+ // (capability-fact: Flash 15-75 parallel calls/step vs DeepSeek 7-8).
2296
+ hunt: [
2297
+ { id: "gemini-2.5-flash", grounding: "capability-fact", reason: "L-040 parallel-tool throughput champion (15-75 calls/step)" },
2298
+ { id: "gemini-3.5-flash", grounding: "capability-fact", reason: "alpha.86: parallel-tool agentic tier (hunt archetypePerf 9, L-040 family) \u2014 replaces availability-restricted gemini-2.5-pro" },
2299
+ { id: "claude-sonnet-5", grounding: "judgment", reason: "Quality safety net for blocked-Flash case. alpha.90: retargeted from legacy claude-sonnet-4-6" },
2300
+ { id: "claude-haiku-4-5", grounding: "judgment", reason: "Reduced tool budget \u2014 cliff at 16 fires" },
2301
+ { id: "gpt-5.4-mini", grounding: "judgment", reason: "alpha.33: third-provider tail \u2014 OpenAI parallel-tool capable archetype" }
2302
+ ],
2303
+ // Cost-sensitive + tolerant. DeepSeek brain-evidence tier 1.
2304
+ summarize: [
2305
+ { id: "gemini-2.5-flash", grounding: "judgment", reason: "Cost-sensitive primary \u2014 engineer pick" },
2306
+ { id: "deepseek-v4-flash", grounding: "measured", reason: "Brain-validated tier 1 for cost-sensitive summarize workloads", n: 169 },
2307
+ { id: "claude-haiku-4-5", grounding: "judgment", reason: "Quality safety net" },
2308
+ { id: "gemini-3.5-flash-lite", grounding: "judgment", reason: "Emergency floor. alpha.91: retargeted from availability-restricted gemini-2.5-flash-lite (Google refuses it for new accounts, TT-49 2026-08-26); 3.5-flash-lite is the successor Google\u2019s own error names" },
2309
+ { id: "gpt-5.4-nano", grounding: "judgment", reason: "alpha.33: third-provider tail (archetypePerf=6) \u2014 cheapest OpenAI, matches summarize cost-band" }
2310
+ ],
2311
+ // Brain-validated DeepSeek tier 1 (169 rows, 0% empty rate).
2312
+ classify: [
2313
+ { id: "gemini-2.5-flash", grounding: "judgment", reason: "Cost-sensitive primary \u2014 engineer pick" },
2314
+ { id: "deepseek-v4-flash", grounding: "measured", reason: "Brain-validated tier 1 (169 rows, 0% empty rate)", n: 169 },
2315
+ { id: "claude-haiku-4-5", grounding: "judgment", reason: "Quality safety net" },
2316
+ { id: "gemini-3.5-flash-lite", grounding: "judgment", reason: "Cache-discount 10\xD7 floor for repeat-prompt workloads ($0.03/M cached). alpha.91: retargeted from availability-restricted gemini-2.5-flash-lite" },
2317
+ { id: "gpt-5.4-nano", grounding: "judgment", reason: "alpha.33: third-provider tail (archetypePerf=6) \u2014 cheapest OpenAI for classify" }
2318
+ ]
2319
+ };
2320
+ var STARTER_CHAINS = (() => {
2321
+ const out = {};
2322
+ for (const [archetype, entries] of Object.entries(STARTER_CHAINS_GROUNDED)) {
2323
+ out[archetype] = entries.map((e) => e.id);
2324
+ }
2325
+ return out;
2326
+ })();
2327
+ var STARTER_CHAINS_BY_MODE_GROUNDED = {
2328
+ hunt: {
2329
+ sequential: [
2330
+ {
2331
+ id: "deepseek-v4-pro",
2332
+ grounding: "judgment",
2333
+ reason: "alpha.20 E3: cheap + good reasoning at single-step granularity; L-040 cliff silenced when sequential \u2014 hypothesis not yet measured"
2334
+ },
2335
+ {
2336
+ id: "deepseek-v4-flash",
2337
+ grounding: "judgment",
2338
+ reason: "Cheapest viable; sibling-provider fallback"
2339
+ },
2340
+ {
2341
+ id: "claude-sonnet-5",
2342
+ grounding: "judgment",
2343
+ reason: "Cross-provider safety net \u2014 Sonnet handles sequential agentic loops cleanly. alpha.90: retargeted from legacy claude-sonnet-4-6"
2344
+ },
2345
+ {
2346
+ id: "gemini-3.5-flash",
2347
+ grounding: "judgment",
2348
+ reason: "alpha.86: third-provider tail when no DeepSeek key reachable \u2014 replaces availability-restricted gemini-2.5-pro"
2349
+ }
2350
+ ]
2351
+ }
2352
+ };
2353
+ var STARTER_CHAINS_BY_MODE = (() => {
2354
+ const out = {};
2355
+ for (const [archetype, modes] of Object.entries(STARTER_CHAINS_BY_MODE_GROUNDED)) {
2356
+ if (modes?.sequential) {
2357
+ out[archetype] = {
2358
+ sequential: modes.sequential.map((e) => e.id)
2359
+ };
2360
+ }
2361
+ }
2362
+ return out;
2363
+ })();
2364
+ function resolveStarterForMode(archetype, toolOrchestration, allChains) {
2365
+ if (toolOrchestration === "sequential") {
2366
+ const overlay = STARTER_CHAINS_BY_MODE[archetype]?.sequential;
2367
+ if (overlay) return [...overlay];
2368
+ }
2369
+ return allChains[archetype];
2370
+ }
2371
+ function getDefaultFallbackChain(opts) {
2372
+ const {
2373
+ archetype,
2374
+ primary,
2375
+ maxDepth = 4,
2376
+ policy,
2377
+ reachability,
2378
+ toolOrchestration,
2379
+ optimizeFor = "quality"
2380
+ } = opts;
2381
+ if (maxDepth < 1) {
2382
+ throw new Error(
2383
+ `getDefaultFallbackChain: maxDepth must be >= 1, got ${maxDepth}`
2384
+ );
2385
+ }
2386
+ const allChains = loadChainsFromBrain();
2387
+ const qualityStarter = resolveStarterForMode(archetype, toolOrchestration, allChains);
2388
+ if (!qualityStarter) {
2389
+ throw new Error(
2390
+ `getDefaultFallbackChain: unknown archetype "${archetype}". Known: ${Object.keys(allChains).join(", ")}`
2391
+ );
2392
+ }
2393
+ const starter = optimizeFor === "cost" ? buildCostOrderedChain(archetype) : qualityStarter;
2394
+ let chain;
2395
+ if (primary) {
2396
+ chain = [primary, ...starter.filter((id) => id !== primary)];
2397
+ } else {
2398
+ chain = [...starter];
2399
+ }
2400
+ if (policy?.blockedModels && policy.blockedModels.length > 0) {
2401
+ const blocked = canonicalPolicySet(policy.blockedModels);
2402
+ chain = chain.filter((id) => !policySetHas(blocked, id));
2403
+ }
2404
+ const seen = /* @__PURE__ */ new Set();
2405
+ const deduped = [];
2406
+ for (const id of chain) {
2407
+ if (!seen.has(id)) {
2408
+ seen.add(id);
2409
+ deduped.push(id);
2410
+ }
2411
+ }
2412
+ let filtered = deduped;
2413
+ if (reachability) {
2414
+ filtered = deduped.filter((id) => isModelReachable(id, reachability));
2415
+ }
2416
+ return filtered.slice(0, maxDepth);
2417
+ }
2418
+ function getAllStarterChains() {
2419
+ const out = {};
2420
+ for (const [archetype, chain] of Object.entries(STARTER_CHAINS)) {
2421
+ out[archetype] = [...chain];
2422
+ }
2423
+ return out;
2424
+ }
2425
+
2426
+ // src/glassbox-routes/counterfactuals.ts
2427
+ var COUNTERFACTUAL_MIN_SAVINGS_RATIO = 0.1;
2428
+ var COUNTERFACTUAL_MAX_RESULTS = 2;
2429
+ function computeCounterfactuals(args) {
2430
+ const {
2431
+ servedModel,
2432
+ servedCostUsd,
2433
+ archetype,
2434
+ tokensIn,
2435
+ tokensOut,
2436
+ cacheReadInputTokens = 0,
2437
+ toolOrchestration
2438
+ } = args;
2439
+ if (tokensIn <= 0) return [];
2440
+ if (servedCostUsd <= 0) return [];
2441
+ let chain;
2442
+ try {
2443
+ chain = getDefaultFallbackChain({
2444
+ archetype,
2445
+ posture: "open",
2446
+ maxDepth: 10,
2447
+ toolOrchestration
2448
+ });
2449
+ } catch {
2450
+ return [];
2451
+ }
2452
+ const candidates = [];
2453
+ const minSavings = servedCostUsd * COUNTERFACTUAL_MIN_SAVINGS_RATIO;
2454
+ for (const modelId of chain) {
2455
+ if (modelId === servedModel) continue;
2456
+ const profile = tryGetProfile(modelId);
2457
+ if (!profile) continue;
2458
+ const perf = profile.archetypePerf?.[archetype] ?? 5;
2459
+ if (perf < ARCHETYPE_FLOOR_DEFAULT) continue;
2460
+ const estimated = estimateCostUsd({
2461
+ profile,
2462
+ tokensIn,
2463
+ tokensOut,
2464
+ cacheReadInputTokens
2465
+ });
2466
+ if (estimated === void 0) continue;
2467
+ const savings = servedCostUsd - estimated;
2468
+ if (savings < minSavings) continue;
2469
+ const savingsPercent = Math.round(savings / servedCostUsd * 100);
2470
+ const reason = buildReason({
2471
+ modelId,
2472
+ archetype,
2473
+ perf,
2474
+ profile
2475
+ });
2476
+ candidates.push({
2477
+ modelId,
2478
+ estimatedCostUsd: round6(estimated),
2479
+ savingsUsd: round6(savings),
2480
+ savingsPercent,
2481
+ reason
2482
+ });
2483
+ }
2484
+ candidates.sort((a, b) => a.estimatedCostUsd - b.estimatedCostUsd);
2485
+ return candidates.slice(0, COUNTERFACTUAL_MAX_RESULTS);
2486
+ }
2487
+ function estimateCostUsd(args) {
2488
+ const { profile, tokensIn, tokensOut, cacheReadInputTokens } = args;
2489
+ const cacheableIn = Math.min(cacheReadInputTokens, tokensIn);
2490
+ const nonCachedIn = Math.max(tokensIn - cacheableIn, 0);
2491
+ const discount = profile.lowering.cache.discount ?? 1;
2492
+ const inUsd = nonCachedIn / 1e6 * profile.costInputPer1m + cacheableIn / 1e6 * profile.costInputPer1m * discount;
2493
+ const outUsd = tokensOut / 1e6 * profile.costOutputPer1m;
2494
+ const total = inUsd + outUsd;
2495
+ if (!Number.isFinite(total)) return void 0;
2496
+ if (total < 0) return void 0;
2497
+ return total;
2498
+ }
2499
+ function round6(n) {
2500
+ return Math.round(n * 1e6) / 1e6;
2501
+ }
2502
+ function buildReason(args) {
2503
+ const { modelId, archetype, perf, profile } = args;
2504
+ const hook = profile.strengths?.[0];
2505
+ const suffix = hook ? `, ${hook.replace(/_/g, " ")}` : "";
2506
+ return `${modelId} on ${archetype}: archetypePerf=${perf}${suffix}`;
2507
+ }
2508
+
2509
+ // src/glassbox-routes/projected-cost.ts
2510
+ var INSUFFICIENT_VOLUME_THRESHOLD = 5;
2511
+ var WINDOW_DAYS = 7;
2512
+ async function computeProjectedDailyCost(args) {
2513
+ const {
2514
+ appId,
2515
+ archetype,
2516
+ servedCostUsd,
2517
+ brainEndpoint,
2518
+ brainJwt,
2519
+ brainAnonKey,
2520
+ fetch: fetchImpl
2521
+ } = args;
2522
+ if (!appId || !archetype) return void 0;
2523
+ if (!Number.isFinite(servedCostUsd) || servedCostUsd <= 0) return void 0;
2524
+ const doFetch = fetchImpl ?? ((...a) => globalThis.fetch(...a));
2525
+ const base = brainEndpoint.replace(/\/+$/, "");
2526
+ const cutoffIso = new Date(
2527
+ Date.now() - WINDOW_DAYS * 24 * 60 * 60 * 1e3
2528
+ ).toISOString();
2529
+ const qs = new URLSearchParams();
2530
+ qs.set("app_id", `eq.${appId}`);
2531
+ qs.set("intent_archetype", `eq.${archetype}`);
2532
+ qs.set("created_at", `gte.${cutoffIso}`);
2533
+ qs.set("select", "handle");
2534
+ qs.set("limit", "0");
2535
+ const url = `${base}/rest/v1/compile_outcomes?${qs.toString()}`;
2536
+ let res;
2537
+ try {
2538
+ res = await doFetch(url, {
2539
+ method: "GET",
2540
+ headers: {
2541
+ Authorization: `Bearer ${brainJwt}`,
2542
+ apikey: brainAnonKey,
2543
+ Accept: "application/json",
2544
+ // Triggers PostgREST exact count in Content-Range header.
2545
+ Prefer: "count=exact"
2546
+ }
2547
+ });
2548
+ } catch {
2549
+ return void 0;
2550
+ }
2551
+ if (!res.ok) return void 0;
2552
+ const contentRange = res.headers.get("content-range");
2553
+ const count = parseContentRangeCount(contentRange);
2554
+ if (count === void 0) return void 0;
2555
+ const avgPerDay = count / WINDOW_DAYS;
2556
+ if (avgPerDay < INSUFFICIENT_VOLUME_THRESHOLD) return void 0;
2557
+ const projected = avgPerDay * servedCostUsd;
2558
+ return Math.round(projected * 1e6) / 1e6;
2559
+ }
2560
+ function parseContentRangeCount(header) {
2561
+ if (!header) return void 0;
2562
+ const slash = header.lastIndexOf("/");
2563
+ if (slash < 0) return void 0;
2564
+ const tail = header.slice(slash + 1).trim();
2565
+ if (tail === "*" || tail === "") return void 0;
2566
+ const n = Number.parseInt(tail, 10);
2567
+ if (!Number.isFinite(n) || n < 0) return void 0;
2568
+ return n;
2569
+ }
2570
+
2571
+ // src/glassbox-routes/proxy.ts
2572
+ var JSON_HEADERS2 = {
2573
+ "Content-Type": "application/json",
2574
+ "Cache-Control": "no-store"
2575
+ };
2576
+ var DEFAULT_LIMIT = 20;
2577
+ var MAX_LIMIT = 100;
2578
+ function jsonResponse(status, body) {
2579
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
2580
+ }
2581
+ function jsonError2(status, code) {
2582
+ return jsonResponse(status, { error: code });
2583
+ }
2584
+ function applyScrub(row, scrub) {
2585
+ if (!scrub || row == null || typeof row !== "object") return row;
2586
+ try {
2587
+ return scrub(row);
2588
+ } catch {
2589
+ return row;
2590
+ }
2591
+ }
2592
+ function parseLimit(raw) {
2593
+ if (!raw) return DEFAULT_LIMIT;
2594
+ const n = Number.parseInt(raw, 10);
2595
+ if (!Number.isFinite(n) || n <= 0) return DEFAULT_LIMIT;
2596
+ return Math.min(n, MAX_LIMIT);
2597
+ }
2598
+ function rowToSummary(row) {
2599
+ return {
2600
+ traceId: typeof row.handle === "string" ? row.handle : "",
2601
+ appId: typeof row.app_id === "string" ? row.app_id : "",
2602
+ archetype: typeof row.intent_archetype === "string" ? row.intent_archetype : "",
2603
+ target: typeof row.model === "string" ? row.model : "",
2604
+ createdAt: typeof row.created_at === "string" ? row.created_at : "",
2605
+ tokensIn: typeof row.tokens_in === "number" ? row.tokens_in : 0,
2606
+ tokensOut: typeof row.tokens_out === "number" ? row.tokens_out : 0,
2607
+ estimatedCostUsd: typeof row.cost_usd_actual === "number" ? row.cost_usd_actual : 0
2608
+ };
2609
+ }
2610
+ var INPUT_RATIO_YELLOW = 0.65;
2611
+ var INPUT_RATIO_RED = 0.85;
2612
+ var CACHE_HEALTH_MIN_TOKENS = 1e3;
2613
+ var CACHE_RATIO_GREEN = 0.5;
2614
+ var CACHE_RATIO_YELLOW = 0.1;
2615
+ var FALLBACK_REASONS = /* @__PURE__ */ new Set([
2616
+ "rate_limit",
2617
+ "provider_auth_failed",
2618
+ "provider_billing_exhausted",
2619
+ "provider_error",
2620
+ "cliff",
2621
+ "cost_cap",
2622
+ "contract_violation"
2623
+ ]);
2624
+ function asString(v) {
2625
+ return typeof v === "string" && v.length > 0 ? v : void 0;
2626
+ }
2627
+ function asNumber(v) {
2628
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
2629
+ }
2630
+ function asNumberOrZero(v) {
2631
+ return typeof v === "number" && Number.isFinite(v) ? v : 0;
2632
+ }
2633
+ function asStringArray(v) {
2634
+ if (!Array.isArray(v)) return [];
2635
+ const out = [];
2636
+ for (const e of v) {
2637
+ if (typeof e === "string") out.push(e);
2638
+ }
2639
+ return out;
2640
+ }
2641
+ function asFallbackReason(v) {
2642
+ if (typeof v !== "string") return void 0;
2643
+ const candidate = v;
2644
+ if (candidate && FALLBACK_REASONS.has(candidate)) return candidate;
2645
+ return "provider_error";
2646
+ }
2647
+ function rowToAdvisory(raw) {
2648
+ if (!raw || typeof raw !== "object") return void 0;
2649
+ const r = raw;
2650
+ const level = r.level;
2651
+ const code = r.code;
2652
+ const message = r.message;
2653
+ if (level !== "info" && level !== "warn" && level !== "critical" || typeof code !== "string" || typeof message !== "string") {
2654
+ return void 0;
2655
+ }
2656
+ const out = { level, code, message };
2657
+ const suggestion = asString(r.suggestion);
2658
+ if (suggestion) out.suggestion = suggestion;
2659
+ const docsUrl = asString(r.docs_url ?? r.docsUrl);
2660
+ if (docsUrl) out.docsUrl = docsUrl;
2661
+ const adapter = toAdapter(r.suggested_adaptation ?? r.suggestedAdaptation);
2662
+ if (adapter) out.suggestedAdaptation = adapter;
2663
+ return out;
2664
+ }
2665
+ var SECTION_KINDS = /* @__PURE__ */ new Set([
2666
+ "role_intro",
2667
+ "tool_call_contract",
2668
+ "narration_contract",
2669
+ "discipline_contract",
2670
+ "user_turn",
2671
+ "reference",
2672
+ "arbitrary"
2673
+ ]);
2674
+ function summarizeSectionRewrite(kind, rule) {
2675
+ if (kind === "tool_call_contract" && rule === "sequential-tool-cliff-below-floor") {
2676
+ return "Sequential tool pattern applied (model cliff cleared at compile time).";
2677
+ }
2678
+ if (kind === "narration_contract" && rule === "narration-drift-anthropic") {
2679
+ return "Narration tightened for Anthropic dialect (terse-log shape preserved).";
2680
+ }
2681
+ if (kind === "narration_contract" && rule === "narration-thinking-leak-deepseek") {
2682
+ return "Thinking-block suppression applied (DeepSeek V4 internal reasoning kept off-wire).";
2683
+ }
2684
+ if (kind === "discipline_contract" && rule === "discipline-gates-v1") {
2685
+ return "Discipline gates applied (surface-scaffolding: evidence-before-reasoning, one-extra-signal, source-labeling).";
2686
+ }
2687
+ return `Translator applied rule "${rule}" to ${kind} section.`;
2688
+ }
2689
+ function rowToSectionRewrite(raw) {
2690
+ if (!raw || typeof raw !== "object") return void 0;
2691
+ const r = raw;
2692
+ const sectionId = r.sectionId ?? r.section_id;
2693
+ if (typeof sectionId !== "string" || sectionId.length === 0) return void 0;
2694
+ const kind = r.kind;
2695
+ if (typeof kind !== "string" || !SECTION_KINDS.has(kind)) {
2696
+ return void 0;
2697
+ }
2698
+ const rule = r.rule;
2699
+ if (typeof rule !== "string" || rule.length === 0) return void 0;
2700
+ return {
2701
+ sectionId,
2702
+ kind,
2703
+ rule,
2704
+ summary: summarizeSectionRewrite(kind, rule)
2705
+ };
2706
+ }
2707
+ function toAdapter(raw) {
2708
+ if (!raw || typeof raw !== "object") return void 0;
2709
+ const a = raw;
2710
+ if (a.parameter === "toolOrchestration" && a.value === "sequential" && typeof a.consequence === "string") {
2711
+ return {
2712
+ parameter: "toolOrchestration",
2713
+ value: "sequential",
2714
+ consequence: a.consequence
2715
+ };
2716
+ }
2717
+ return void 0;
2718
+ }
2719
+ function computeHealth(args) {
2720
+ const {
2721
+ tokensIn,
2722
+ tokensOut,
2723
+ historyCacheableTokens,
2724
+ inputCacheHitRatio,
2725
+ fellOverFrom,
2726
+ target
2727
+ } = args;
2728
+ const total = tokensIn + tokensOut;
2729
+ const ratio = total > 0 ? tokensIn / total : 0;
2730
+ let inputRatioStatus;
2731
+ if (ratio > INPUT_RATIO_RED) inputRatioStatus = "red";
2732
+ else if (ratio > INPUT_RATIO_YELLOW) inputRatioStatus = "yellow";
2733
+ else inputRatioStatus = "green";
2734
+ let cacheStatus;
2735
+ if (historyCacheableTokens <= CACHE_HEALTH_MIN_TOKENS) {
2736
+ cacheStatus = "na";
2737
+ } else if (inputCacheHitRatio >= CACHE_RATIO_GREEN) {
2738
+ cacheStatus = "green";
2739
+ } else if (inputCacheHitRatio >= CACHE_RATIO_YELLOW) {
2740
+ cacheStatus = "yellow";
2741
+ } else {
2742
+ cacheStatus = "red";
2743
+ }
2744
+ const fallbackStatus = fellOverFrom !== void 0 && fellOverFrom !== target ? "red" : "green";
2745
+ return { inputRatioStatus, cacheStatus, fallbackStatus };
2746
+ }
2747
+ function rowToDetail(row) {
2748
+ const summary = rowToSummary(row);
2749
+ const tokensIn = summary.tokensIn;
2750
+ const tokensOut = summary.tokensOut;
2751
+ const cacheReadInputTokens = asNumberOrZero(row.cache_read_input_tokens);
2752
+ const cacheCreationInputTokens = asNumberOrZero(
2753
+ row.cache_creation_input_tokens
2754
+ );
2755
+ const historyCacheableTokens = asNumberOrZero(row.history_cacheable_tokens);
2756
+ const inputCacheHitRatio = tokensIn > 0 ? cacheReadInputTokens / tokensIn : 0;
2757
+ const fellOverFrom = asString(row.fell_over_from);
2758
+ const fallbackReasonRaw = row.fallback_reason;
2759
+ const fallbackReason = fellOverFrom ? asFallbackReason(fallbackReasonRaw) : void 0;
2760
+ const requestedModel = asString(row.requested_model) ?? fellOverFrom;
2761
+ const advisoriesRaw = Array.isArray(row.advisories) ? row.advisories : [];
2762
+ const advisories = [];
2763
+ for (const a of advisoriesRaw) {
2764
+ const rec = rowToAdvisory(a);
2765
+ if (rec) advisories.push(rec);
2766
+ }
2767
+ const health = computeHealth({
2768
+ tokensIn,
2769
+ tokensOut,
2770
+ cacheReadInputTokens,
2771
+ historyCacheableTokens,
2772
+ inputCacheHitRatio,
2773
+ fellOverFrom,
2774
+ target: summary.target
2775
+ });
2776
+ const sectionRewritesRaw = Array.isArray(row.section_rewrites_applied) ? row.section_rewrites_applied : [];
2777
+ const sectionRewritesApplied = [];
2778
+ for (const e of sectionRewritesRaw) {
2779
+ const rw = rowToSectionRewrite(e);
2780
+ if (rw) sectionRewritesApplied.push(rw);
2781
+ }
2782
+ const detail = {
2783
+ ...summary,
2784
+ mutationsApplied: asStringArray(row.mutations_applied),
2785
+ advisories,
2786
+ rawRequest: asString(row.prompt_preview),
2787
+ rawResponse: asString(row.response_preview),
2788
+ requestedModel,
2789
+ finishReason: asString(row.finish_reason),
2790
+ ttftMs: asNumber(row.ttft_ms),
2791
+ totalMs: asNumber(row.total_ms) ?? asNumber(row.latency_ms),
2792
+ toolsCount: asNumber(row.tools_count),
2793
+ historyDepth: asNumber(row.history_depth),
2794
+ systemPromptChars: asNumber(row.system_prompt_chars),
2795
+ cacheReadInputTokens,
2796
+ cacheCreationInputTokens,
2797
+ historyCacheableTokens,
2798
+ inputCacheHitRatio,
2799
+ fellOverFrom,
2800
+ fallbackReason,
2801
+ sectionRewritesApplied,
2802
+ health
2803
+ };
2804
+ return detail;
2805
+ }
2806
+ function createProxyHandler(config) {
2807
+ const {
2808
+ installToken,
2809
+ extensionId,
2810
+ brainEndpoint,
2811
+ brainJwt,
2812
+ brainAnonKey,
2813
+ appId,
2814
+ scrub,
2815
+ fetch: fetchImpl
2816
+ } = config;
2817
+ const doFetch = fetchImpl ?? ((...args) => globalThis.fetch(...args));
2818
+ const base = brainEndpoint.replace(/\/+$/, "");
2819
+ return async function proxy(req) {
2820
+ const authFail = checkAuth(req, { installToken, extensionId });
2821
+ if (authFail) return authFail;
2822
+ const url = new URL(req.url);
2823
+ const traceId = url.searchParams.get("traceId");
2824
+ const limit = parseLimit(url.searchParams.get("limit"));
2825
+ const qs = new URLSearchParams();
2826
+ qs.set("app_id", `eq.${appId}`);
2827
+ if (traceId) {
2828
+ qs.set("handle", `eq.${traceId}`);
2829
+ } else {
2830
+ qs.set("order", "created_at.desc");
2831
+ qs.set("limit", String(limit));
2832
+ }
2833
+ const brainUrl = `${base}/rest/v1/compile_outcomes?${qs.toString()}`;
2834
+ let brainRes;
2835
+ try {
2836
+ brainRes = await doFetch(brainUrl, {
2837
+ method: "GET",
2838
+ headers: {
2839
+ // Authorization carries the scoped JWT — drives RLS via app_id claim.
2840
+ Authorization: `Bearer ${brainJwt}`,
2841
+ // apikey MUST be one of the project's known keys (anon or
2842
+ // service_role). Supabase rejects any other JWT here, even when
2843
+ // HS256-signed with the same secret. Pre-alpha.24 this was set to
2844
+ // brainJwt and silently 401'd against real Supabase. See L-117.
2845
+ apikey: brainAnonKey,
2846
+ Accept: "application/json"
2847
+ }
2848
+ });
2849
+ } catch {
2850
+ return jsonError2(502, "brain_unavailable");
2851
+ }
2852
+ if (brainRes.status === 401 || brainRes.status === 403) {
2853
+ return jsonError2(500, "brain_auth_misconfig");
2854
+ }
2855
+ if (brainRes.status >= 500) {
2856
+ return jsonError2(502, "brain_unavailable");
2857
+ }
2858
+ if (!brainRes.ok) {
2859
+ return jsonError2(400, "bad_request");
2860
+ }
2861
+ let rows;
2862
+ try {
2863
+ rows = await brainRes.json();
2864
+ } catch {
2865
+ return jsonError2(502, "brain_unavailable");
2866
+ }
2867
+ if (!Array.isArray(rows)) {
2868
+ return jsonError2(502, "brain_unavailable");
2869
+ }
2870
+ const scrubbed = rows.map(
2871
+ (row) => applyScrub(row, scrub)
2872
+ );
2873
+ if (traceId) {
2874
+ const first = scrubbed[0];
2875
+ if (!first) return jsonError2(404, "not_found");
2876
+ const detail = rowToDetail(first);
2877
+ const counterfactuals = computeCounterfactuals({
2878
+ servedModel: detail.target,
2879
+ servedCostUsd: detail.estimatedCostUsd,
2880
+ archetype: detail.archetype,
2881
+ tokensIn: detail.tokensIn,
2882
+ tokensOut: detail.tokensOut,
2883
+ cacheReadInputTokens: detail.cacheReadInputTokens
2884
+ });
2885
+ detail.counterfactuals = counterfactuals;
2886
+ if (detail.estimatedCostUsd > 0) {
2887
+ const projected = await computeProjectedDailyCost({
2888
+ appId: detail.appId,
2889
+ archetype: detail.archetype,
2890
+ servedCostUsd: detail.estimatedCostUsd,
2891
+ brainEndpoint: base,
2892
+ brainJwt,
2893
+ brainAnonKey,
2894
+ fetch: doFetch
2895
+ });
2896
+ if (projected !== void 0) {
2897
+ detail.projectedDailyCostUsd = projected;
2898
+ }
2899
+ }
2900
+ return jsonResponse(200, detail);
2901
+ }
2902
+ return jsonResponse(200, { traces: scrubbed.map(rowToSummary) });
2903
+ };
2904
+ }
2905
+
2906
+ // src/glassbox-routes/stream.ts
2907
+ var SSE_HEADERS = {
2908
+ "Content-Type": "text/event-stream",
2909
+ "Cache-Control": "no-cache, no-transform",
2910
+ Connection: "keep-alive",
2911
+ "X-Accel-Buffering": "no"
2912
+ };
2913
+ function applyScrub2(event, scrub) {
2914
+ if (!scrub) return event;
2915
+ try {
2916
+ const out = scrub(event);
2917
+ if (out && typeof out === "object" && typeof out.kind === "string" && typeof out.at === "number") {
2918
+ return out;
2919
+ }
2920
+ return event;
2921
+ } catch {
2922
+ return event;
2923
+ }
2924
+ }
2925
+ function sseFrame(eventName, data) {
2926
+ const safeName = eventName.replace(/[\r\n]/g, "");
2927
+ return `event: ${safeName}
2928
+ data: ${JSON.stringify(data)}
2929
+
2930
+ `;
2931
+ }
2932
+ function createStreamHandler(config, subscribe2, subscribeApp2) {
2933
+ const { installToken, extensionId, appId, scrub } = config;
2934
+ return async function stream(req) {
2935
+ const authFail = checkAuth(req, { installToken, extensionId });
2936
+ if (authFail) return authFail;
2937
+ const url = new URL(req.url);
2938
+ const traceId = url.searchParams.get("traceId");
2939
+ const source = traceId ? subscribe2(traceId) : subscribeApp2({ appId });
2940
+ const encoder = new TextEncoder();
2941
+ let sourceReader;
2942
+ let cancelled = false;
2943
+ const body = new ReadableStream({
2944
+ async start(controller) {
2945
+ controller.enqueue(encoder.encode(sseFrame("ready", {})));
2946
+ sourceReader = source.getReader();
2947
+ const signal = req.signal;
2948
+ if (signal) {
2949
+ if (signal.aborted) {
2950
+ cancelled = true;
2951
+ await sourceReader.cancel();
2952
+ try {
2953
+ controller.close();
2954
+ } catch {
2955
+ }
2956
+ return;
2957
+ }
2958
+ signal.addEventListener(
2959
+ "abort",
2960
+ () => {
2961
+ cancelled = true;
2962
+ sourceReader?.cancel().catch(() => {
2963
+ });
2964
+ try {
2965
+ controller.close();
2966
+ } catch {
2967
+ }
2968
+ },
2969
+ { once: true }
2970
+ );
2971
+ }
2972
+ try {
2973
+ while (!cancelled) {
2974
+ const { value, done } = await sourceReader.read();
2975
+ if (done) break;
2976
+ const scrubbed = applyScrub2(value, scrub);
2977
+ controller.enqueue(
2978
+ encoder.encode(sseFrame(scrubbed.kind, scrubbed))
2979
+ );
2980
+ }
2981
+ } catch {
2982
+ } finally {
2983
+ try {
2984
+ controller.close();
2985
+ } catch {
2986
+ }
2987
+ try {
2988
+ sourceReader?.releaseLock();
2989
+ } catch {
2990
+ }
2991
+ }
2992
+ },
2993
+ cancel() {
2994
+ cancelled = true;
2995
+ sourceReader?.cancel().catch(() => {
2996
+ });
2997
+ }
2998
+ });
2999
+ return new Response(body, { status: 200, headers: SSE_HEADERS });
3000
+ };
3001
+ }
3002
+
3003
+ // src/glassbox/types.ts
3004
+ var GLASSBOX_STREAM_TTL_MS = 6e4;
3005
+
3006
+ // src/glassbox/pubsub-memory.ts
3007
+ var MemoryPubSub = class {
3008
+ subscribers = /* @__PURE__ */ new Map();
3009
+ async publish(channelKey, event) {
3010
+ const subs = this.subscribers.get(channelKey);
3011
+ if (!subs || subs.size === 0) return;
3012
+ for (const sub of subs) {
3013
+ if (sub.closed) continue;
3014
+ try {
3015
+ sub.controller.enqueue(event);
3016
+ } catch {
3017
+ sub.closed = true;
3018
+ continue;
3019
+ }
3020
+ this.refreshTtl(channelKey, sub);
3021
+ }
3022
+ }
3023
+ subscribe(channelKey) {
3024
+ const self = this;
3025
+ let sub;
3026
+ return new ReadableStream({
3027
+ start(controller) {
3028
+ sub = {
3029
+ controller,
3030
+ ttlTimer: setTimeout(() => {
3031
+ self.closeSubscriber(channelKey, sub);
3032
+ }, GLASSBOX_STREAM_TTL_MS),
3033
+ closed: false
3034
+ };
3035
+ let set = self.subscribers.get(channelKey);
3036
+ if (!set) {
3037
+ set = /* @__PURE__ */ new Set();
3038
+ self.subscribers.set(channelKey, set);
3039
+ }
3040
+ set.add(sub);
3041
+ },
3042
+ cancel() {
3043
+ if (sub) self.removeSubscriber(channelKey, sub);
3044
+ }
3045
+ });
3046
+ }
3047
+ /**
3048
+ * Refresh the rolling TTL for a subscriber after an event lands. Replaces
3049
+ * the existing timer with a fresh 60s one.
3050
+ */
3051
+ refreshTtl(channelKey, sub) {
3052
+ clearTimeout(sub.ttlTimer);
3053
+ sub.ttlTimer = setTimeout(() => {
3054
+ this.closeSubscriber(channelKey, sub);
3055
+ }, GLASSBOX_STREAM_TTL_MS);
3056
+ }
3057
+ /**
3058
+ * Close the subscriber's stream cleanly and remove from the fan-out set.
3059
+ * Idempotent — safe to call multiple times.
3060
+ */
3061
+ closeSubscriber(channelKey, sub) {
3062
+ if (sub.closed) return;
3063
+ sub.closed = true;
3064
+ clearTimeout(sub.ttlTimer);
3065
+ try {
3066
+ sub.controller.close();
3067
+ } catch {
3068
+ }
3069
+ this.removeSubscriber(channelKey, sub);
3070
+ }
3071
+ removeSubscriber(channelKey, sub) {
3072
+ clearTimeout(sub.ttlTimer);
3073
+ const set = this.subscribers.get(channelKey);
3074
+ if (!set) return;
3075
+ set.delete(sub);
3076
+ if (set.size === 0) this.subscribers.delete(channelKey);
3077
+ }
3078
+ /**
3079
+ * Test-only reset. Tears down all subscribers, clears all state. Calling
3080
+ * outside of tests is harmless but cancels every active stream.
3081
+ */
3082
+ _reset() {
3083
+ for (const [, set] of this.subscribers) {
3084
+ for (const sub of set) {
3085
+ this.closeSubscriber("", sub);
3086
+ }
3087
+ }
3088
+ this.subscribers.clear();
3089
+ }
3090
+ };
3091
+
3092
+ // src/glassbox/pubsub-upstash.ts
3093
+ var UpstashPubSub = class {
3094
+ url;
3095
+ token;
3096
+ fetchImpl;
3097
+ blockMs;
3098
+ maxLen;
3099
+ constructor(cfg) {
3100
+ this.url = cfg.url.replace(/\/$/, "");
3101
+ this.token = cfg.token;
3102
+ this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch.bind(globalThis);
3103
+ this.blockMs = cfg.blockMs ?? 100;
3104
+ this.maxLen = cfg.maxLen ?? 100;
3105
+ }
3106
+ async publish(channelKey, event) {
3107
+ const key = channelKey;
3108
+ const payload = JSON.stringify(event);
3109
+ await this.cmd([
3110
+ "XADD",
3111
+ key,
3112
+ "MAXLEN",
3113
+ "~",
3114
+ String(this.maxLen),
3115
+ "*",
3116
+ "event",
3117
+ payload
3118
+ ]);
3119
+ await this.cmd(["EXPIRE", key, String(Math.ceil(GLASSBOX_STREAM_TTL_MS / 1e3))]);
3120
+ }
3121
+ subscribe(channelKey) {
3122
+ const key = channelKey;
3123
+ const self = this;
3124
+ let cursor = "$";
3125
+ let cancelled = false;
3126
+ let ttlDeadline = Date.now() + GLASSBOX_STREAM_TTL_MS;
3127
+ return new ReadableStream({
3128
+ async start(controller) {
3129
+ try {
3130
+ while (!cancelled && Date.now() < ttlDeadline) {
3131
+ const resp = await self.cmd([
3132
+ "XREAD",
3133
+ "BLOCK",
3134
+ String(self.blockMs),
3135
+ "STREAMS",
3136
+ key,
3137
+ cursor
3138
+ ]);
3139
+ if (cancelled) break;
3140
+ const parsed = parseXReadResult(resp.result);
3141
+ if (parsed.entries.length === 0) {
3142
+ continue;
3143
+ }
3144
+ for (const entry of parsed.entries) {
3145
+ const evt = decodeEvent(entry.fields);
3146
+ if (evt) {
3147
+ try {
3148
+ controller.enqueue(evt);
3149
+ } catch {
3150
+ cancelled = true;
3151
+ break;
3152
+ }
3153
+ }
3154
+ cursor = entry.id;
3155
+ }
3156
+ ttlDeadline = Date.now() + GLASSBOX_STREAM_TTL_MS;
3157
+ }
3158
+ } catch (err) {
3159
+ if (!cancelled) {
3160
+ try {
3161
+ controller.error(err);
3162
+ } catch {
3163
+ }
3164
+ return;
3165
+ }
3166
+ }
3167
+ try {
3168
+ controller.close();
3169
+ } catch {
3170
+ }
3171
+ },
3172
+ cancel() {
3173
+ cancelled = true;
3174
+ }
3175
+ });
3176
+ }
3177
+ async cmd(args) {
3178
+ const res = await this.fetchImpl(this.url, {
3179
+ method: "POST",
3180
+ headers: {
3181
+ Authorization: `Bearer ${this.token}`,
3182
+ "Content-Type": "application/json"
3183
+ },
3184
+ body: JSON.stringify(args)
3185
+ });
3186
+ if (!res.ok) {
3187
+ throw new Error(`Upstash ${args[0]} failed: HTTP ${res.status}`);
3188
+ }
3189
+ const json = await res.json();
3190
+ if (json.error) {
3191
+ throw new Error(`Upstash ${args[0]} failed: ${json.error}`);
3192
+ }
3193
+ return json;
3194
+ }
3195
+ };
3196
+ function traceChannel(traceId) {
3197
+ return `glassbox:trace:${traceId}`;
3198
+ }
3199
+ function appChannel(appId) {
3200
+ return `glassbox:app:${appId}`;
3201
+ }
3202
+ function decodeEvent(fields) {
3203
+ const raw = fields["event"];
3204
+ if (!raw) return void 0;
3205
+ try {
3206
+ const parsed = JSON.parse(raw);
3207
+ if (typeof parsed.kind === "string" && typeof parsed.at === "number") {
3208
+ return parsed;
3209
+ }
3210
+ return void 0;
3211
+ } catch {
3212
+ return void 0;
3213
+ }
3214
+ }
3215
+ function parseXReadResult(raw) {
3216
+ if (!Array.isArray(raw)) return { entries: [] };
3217
+ const entries = [];
3218
+ for (const stream of raw) {
3219
+ if (!Array.isArray(stream) || stream.length < 2) continue;
3220
+ const streamEntries = stream[1];
3221
+ if (!Array.isArray(streamEntries)) continue;
3222
+ for (const entry of streamEntries) {
3223
+ if (!Array.isArray(entry) || entry.length < 2) continue;
3224
+ const id = String(entry[0]);
3225
+ const flat = entry[1];
3226
+ if (!Array.isArray(flat)) continue;
3227
+ const fields = {};
3228
+ for (let i = 0; i < flat.length; i += 2) {
3229
+ const k = flat[i];
3230
+ const v = flat[i + 1];
3231
+ if (typeof k === "string") fields[k] = String(v ?? "");
3232
+ }
3233
+ entries.push({ id, fields });
3234
+ }
3235
+ }
3236
+ return { entries };
3237
+ }
3238
+
3239
+ // src/glassbox/emit.ts
3240
+ var activePubSub;
3241
+ function getPubSub() {
3242
+ if (activePubSub) return activePubSub;
3243
+ const url = readEnv("UPSTASH_REDIS_URL");
3244
+ const token = readEnv("UPSTASH_REDIS_TOKEN");
3245
+ if (url && token) {
3246
+ activePubSub = new UpstashPubSub({ url, token });
3247
+ } else {
3248
+ activePubSub = new MemoryPubSub();
3249
+ }
3250
+ return activePubSub;
3251
+ }
3252
+ function readEnv(key) {
3253
+ try {
3254
+ if (typeof process !== "undefined" && process.env) {
3255
+ const v = process.env[key];
3256
+ return v && v.trim() !== "" ? v : void 0;
3257
+ }
3258
+ } catch {
3259
+ }
3260
+ return void 0;
3261
+ }
3262
+
3263
+ // src/glassbox/subscribe.ts
3264
+ function emptyStream() {
3265
+ return new ReadableStream({
3266
+ start(controller) {
3267
+ controller.close();
3268
+ }
3269
+ });
3270
+ }
3271
+ function subscribe(traceId) {
3272
+ if (!traceId) return emptyStream();
3273
+ return getPubSub().subscribe(traceChannel(traceId));
3274
+ }
3275
+ function subscribeApp({
3276
+ appId
3277
+ }) {
3278
+ if (!appId) return emptyStream();
3279
+ return getPubSub().subscribe(appChannel(appId));
3280
+ }
3281
+
3282
+ // src/glassbox-routes/index.ts
3283
+ function requireString(name, value) {
3284
+ if (typeof value !== "string" || value.length === 0) {
3285
+ throw new Error(`createGlassboxRoutes: ${name} is required`);
3286
+ }
3287
+ return value;
3288
+ }
3289
+ function createGlassboxRoutes(config) {
3290
+ const installToken = requireString("installToken", config.installToken);
3291
+ const extensionId = requireString("extensionId", config.extensionId);
3292
+ const brainEndpoint = requireString("brainEndpoint", config.brainEndpoint);
3293
+ const brainJwt = requireString("brainJwt", config.brainJwt);
3294
+ const brainAnonKey = requireString("brainAnonKey", config.brainAnonKey);
3295
+ const appId = requireString("appId", config.appId);
3296
+ const proxy = createProxyHandler({
3297
+ installToken,
3298
+ extensionId,
3299
+ brainEndpoint,
3300
+ brainJwt,
3301
+ brainAnonKey,
3302
+ appId,
3303
+ scrub: config.scrub,
3304
+ fetch: config.fetch
3305
+ });
3306
+ const stream = createStreamHandler(
3307
+ {
3308
+ installToken,
3309
+ extensionId,
3310
+ appId,
3311
+ scrub: config.scrub
3312
+ },
3313
+ config.subscribe ?? subscribe,
3314
+ config.subscribeApp ?? subscribeApp
3315
+ );
3316
+ return { proxy, stream };
3317
+ }
3318
+ // Annotate the CommonJS export names for ESM import in node:
3319
+ 0 && (module.exports = {
3320
+ COUNTERFACTUAL_MAX_RESULTS,
3321
+ COUNTERFACTUAL_MIN_SAVINGS_RATIO,
3322
+ computeCounterfactuals,
3323
+ createGlassboxRoutes,
3324
+ rowToDetail,
3325
+ rowToSummary
3326
+ });