@warmdrift/kgauto-compiler 2.0.0-alpha.49 → 2.0.0-alpha.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -310,6 +310,115 @@ var GOOGLE_LOWERING_BASE = {
310
310
  };
311
311
  var PROFILES_RAW = [
312
312
  // ── Anthropic ──
313
+ {
314
+ // VERIFIED 2026-06-21 against the claude-api reference. Claude Fable 5 is
315
+ // Anthropic's most capable model — a NEW tier ABOVE the opus/sonnet/haiku
316
+ // line (the id has no MAJOR-MINOR version, just `-5`) at 2× Opus pricing
317
+ // ($10/$50 vs $5/$25). Hand-authored, not cloned: the onboarder regex can't
318
+ // match this shape (new family + single-segment version), AND a clone would
319
+ // carry the wrong (opus) pricing. status:'preview' — no brain evidence yet;
320
+ // it earns per-archetype placement through the machinery like any model, and
321
+ // at 2× Opus the quality-floor + cost gates do real work before it leads.
322
+ // API quirk (claude-api ref): an explicit `thinking:{type:"disabled"}` 400s
323
+ // on Fable 5 (omit instead) — MOOT here: ANTHROPIC_LOWERING_BASE carries no
324
+ // `thinking` field, so kgauto never sends the param. (L-049/L-081.)
325
+ id: "claude-fable-5",
326
+ verifiedAgainstDocs: "2026-06-21",
327
+ provider: "anthropic",
328
+ status: "preview",
329
+ maxContextTokens: 1e6,
330
+ maxOutputTokens: 128e3,
331
+ maxTools: 64,
332
+ parallelToolCalls: true,
333
+ structuredOutput: "grammar",
334
+ systemPromptMode: "inline",
335
+ streaming: true,
336
+ cliffs: [],
337
+ costInputPer1m: 10,
338
+ costOutputPer1m: 50,
339
+ lowering: ANTHROPIC_LOWERING_BASE,
340
+ recovery: [
341
+ {
342
+ signal: "rate_limit",
343
+ action: "escalate",
344
+ reason: "429 from Anthropic \u2014 escalate to fallback chain"
345
+ },
346
+ {
347
+ signal: "model_not_found",
348
+ action: "escalate",
349
+ reason: "Model deprecated/renamed \u2014 escalate (L-061)"
350
+ }
351
+ ],
352
+ strengths: ["reasoning", "agentic_coding", "long_context", "reliable_tool_use", "structured_output"],
353
+ weaknesses: ["cost", "latency"],
354
+ 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).",
355
+ // archetypePerf cloned from claude-opus-4-8 as a conservative estimate
356
+ // (Fable >= Opus in capability, but unmeasured — no fabricated superiority).
357
+ // Re-rank once brain rows exist.
358
+ archetypePerf: {
359
+ critique: 10,
360
+ plan: 10,
361
+ generate: 9,
362
+ ask: 9,
363
+ extract: 9,
364
+ transform: 9,
365
+ hunt: 8,
366
+ summarize: 8,
367
+ classify: 8
368
+ }
369
+ },
370
+ {
371
+ // Auto-onboarded 2026-06-04 from `claude-opus-4-7`; VERIFIED 2026-06-21
372
+ // against the claude-api reference. Opus 4.8 shares 4.7's surface exactly —
373
+ // 1M ctx, 128k out, $5/$25, no new breaking changes — so the clone's
374
+ // capability data was accurate (confirmed field-by-field). archetypePerf is
375
+ // still cloned from 4.7 (a sound estimate: 4.8 >= 4.7) — re-rank once brain
376
+ // rows exist. Promoted to status:'current' 2026-06-21 (Sacha: "include Claude
377
+ // Opus 4.8") — now live-selectable. Ties with the still-current claude-opus-4-7
378
+ // (identical $5/$25 + cloned perf) resolve to 4-8 by array order. (L-049/L-081.)
379
+ id: "claude-opus-4-8",
380
+ verifiedAgainstDocs: "2026-06-21",
381
+ provider: "anthropic",
382
+ status: "current",
383
+ maxContextTokens: 1e6,
384
+ maxOutputTokens: 128e3,
385
+ maxTools: 64,
386
+ parallelToolCalls: true,
387
+ structuredOutput: "grammar",
388
+ systemPromptMode: "inline",
389
+ streaming: true,
390
+ cliffs: [],
391
+ costInputPer1m: 5,
392
+ costOutputPer1m: 25,
393
+ lowering: ANTHROPIC_LOWERING_BASE,
394
+ recovery: [
395
+ {
396
+ signal: "rate_limit",
397
+ action: "escalate",
398
+ reason: "429 from Anthropic \u2014 escalate to fallback chain"
399
+ },
400
+ {
401
+ signal: "model_not_found",
402
+ action: "escalate",
403
+ reason: "Model deprecated/renamed \u2014 escalate (L-061)"
404
+ }
405
+ ],
406
+ strengths: ["reasoning", "agentic_coding", "long_context", "reliable_tool_use", "structured_output"],
407
+ weaknesses: ["cost", "latency"],
408
+ 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).",
409
+ // Cloned archetypePerf from claude-opus-4-7 — re-evaluate once brain rows exist.
410
+ archetypePerf: {
411
+ critique: 10,
412
+ plan: 10,
413
+ generate: 9,
414
+ ask: 9,
415
+ extract: 9,
416
+ transform: 9,
417
+ hunt: 8,
418
+ summarize: 8,
419
+ classify: 8
420
+ }
421
+ },
313
422
  {
314
423
  id: "claude-opus-4-7",
315
424
  verifiedAgainstDocs: "2026-05-08",
@@ -877,7 +986,7 @@ var PROFILES_RAW = [
877
986
  },
878
987
  {
879
988
  id: "deepseek-v4-pro",
880
- verifiedAgainstDocs: "2026-05-08",
989
+ verifiedAgainstDocs: "2026-06-22",
881
990
  provider: "deepseek",
882
991
  status: "current",
883
992
  maxContextTokens: 1e6,
@@ -895,10 +1004,14 @@ var PROFILES_RAW = [
895
1004
  reason: "Sequential tool calls only \u2014 L-040"
896
1005
  }
897
1006
  ],
898
- // Profile carries REGULAR pricing, not the 75%-off promo (ends 2026-05-31).
899
- // Under-estimating cost is worse than over-estimating for budget caps.
900
- costInputPer1m: 1.74,
901
- costOutputPer1m: 3.48,
1007
+ // Verified against the live DeepSeek pricing page 2026-06-22: the 75%-off
1008
+ // launch "promo" ($0.435/$0.87) did NOT revert on 2026-05-31 it became the
1009
+ // standing price (docs show $0.435 in / $0.87 out cache-miss, $0.003625
1010
+ // cache-hit in; no promo caveat). The old $1.74/$3.48 "regular post-promo"
1011
+ // was a projected revert that never happened (L-073/L-081) — it 4×-overstated
1012
+ // cost and suppressed V4-Pro in cost-aware ranking.
1013
+ costInputPer1m: 0.435,
1014
+ costOutputPer1m: 0.87,
902
1015
  lowering: {
903
1016
  system: { mode: "inline" },
904
1017
  cache: { strategy: "unsupported" },
@@ -914,7 +1027,7 @@ var PROFILES_RAW = [
914
1027
  // of the served set. Tag derivation would say 'medium'; the measurement says
915
1028
  // otherwise.
916
1029
  latencyTier: "slow",
917
- notes: "Pro tier. 1M context, 384k max output. Regular pricing $1.74/$3.48; 75% promo through 2026-05-31 ($0.435/$0.87). Default mode = thinking.",
1030
+ 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.",
918
1031
  // Master plan §3.3: tier 3 cross-provider for plan chain. Reasoning
919
1032
  // bumped one notch over V4-Flash; same parallel-tool ceiling.
920
1033
  archetypePerf: {
@@ -4589,7 +4702,10 @@ function buildShadowProbeRow(input) {
4589
4702
  // Full IR was replayed (not a truncated preview), so fidelity is 1.0 — the
4590
4703
  // prompt-fidelity guard never fires on these rows.
4591
4704
  prompt_fidelity: 1,
4592
- replay_source: "inline-full-ir"
4705
+ replay_source: "inline-full-ir",
4706
+ // alpha — migration 029. 'completed' is the default completed-probe shape;
4707
+ // diagnostic rows (aborted/skipped) pass the explicit class.
4708
+ outcome: input.outcome ?? "completed"
4593
4709
  };
4594
4710
  }
4595
4711
  async function recordShadowProbe(input) {
@@ -6201,15 +6317,54 @@ function hashForProbe(s) {
6201
6317
  for (let i = 0; i < s.length; i++) h = (h << 5) - h + s.charCodeAt(i) | 0;
6202
6318
  return `h${(h >>> 0).toString(16)}`;
6203
6319
  }
6320
+ var DEFAULT_PROBE_MAX_LATENCY_MS = 15e3;
6321
+ function isSlowTierCandidate(candidate) {
6322
+ const profile = tryGetProfile(candidate);
6323
+ if (!profile) return false;
6324
+ return latencyTierOf(profile) === "slow";
6325
+ }
6204
6326
  async function runProbeCandidates(args) {
6205
6327
  try {
6206
6328
  if (!shouldSampleProbe(args.cfg.sampleRate ?? 0.05)) return;
6207
6329
  const candidates = normalizeProbeCandidates(args.cfg.candidates);
6208
6330
  const promptHash = hashForProbe(args.ir.currentTurn?.content ?? "");
6331
+ const budgetMs = args.cfg.maxLatencyMs ?? DEFAULT_PROBE_MAX_LATENCY_MS;
6332
+ const deadline = args.sync ? Date.now() + budgetMs : Number.POSITIVE_INFINITY;
6333
+ const skipSlow = args.sync && (args.cfg.skipSlowTierInSync ?? true);
6334
+ const recordDiagnostic = async (candidate, outcome) => {
6335
+ await recordShadowProbe({
6336
+ appId: args.ir.appId,
6337
+ archetype: args.ir.intent.archetype,
6338
+ family: deriveFamilyFromModelId(candidate) ?? "unknown",
6339
+ candidateModel: candidate,
6340
+ currentModel: args.servedModel,
6341
+ promptHash,
6342
+ // Served leg only — the candidate never produced a comparable response.
6343
+ currentResponsePreview: args.servedResponse.text.slice(0, 2e3),
6344
+ tokensCurrentIn: args.servedResponse.tokens.input,
6345
+ tokensCurrentOut: args.servedResponse.tokens.output,
6346
+ latencyCurrentMs: args.servedLatencyMs,
6347
+ outcome
6348
+ });
6349
+ };
6209
6350
  for (const candidate of candidates) {
6210
6351
  if (candidate === args.servedModel) continue;
6211
6352
  if (deriveFamilyFromModelId(candidate) === "claude-opus") continue;
6212
6353
  if (!isModelReachable(candidate, { apiKeys: args.exec.apiKeys })) continue;
6354
+ if (skipSlow && isSlowTierCandidate(candidate)) {
6355
+ try {
6356
+ await recordDiagnostic(candidate, "skipped_slow_tier_sync");
6357
+ } catch {
6358
+ }
6359
+ continue;
6360
+ }
6361
+ if (args.sync && Date.now() >= deadline) {
6362
+ try {
6363
+ await recordDiagnostic(candidate, "aborted_latency_budget");
6364
+ } catch {
6365
+ }
6366
+ break;
6367
+ }
6213
6368
  try {
6214
6369
  const candCompile = compileAndRegister(
6215
6370
  {
@@ -6220,11 +6375,32 @@ async function runProbeCandidates(args) {
6220
6375
  args.exec
6221
6376
  );
6222
6377
  const candStart = Date.now();
6223
- const exec = await execute(candCompile.request, {
6378
+ let timer;
6379
+ const execPromise = execute(candCompile.request, {
6224
6380
  apiKeys: args.exec.apiKeys,
6225
6381
  fetchImpl: args.exec.fetchImpl,
6226
6382
  providerOverrides: args.exec.providerOverrides
6227
- });
6383
+ }).then((exec2) => ({ kind: "exec", exec: exec2 })).catch(() => ({ kind: "failed" }));
6384
+ let raced;
6385
+ if (args.sync) {
6386
+ const remaining = Math.max(0, deadline - Date.now());
6387
+ const timeoutPromise = new Promise((resolve) => {
6388
+ timer = setTimeout(() => resolve({ kind: "timeout" }), remaining);
6389
+ });
6390
+ raced = await Promise.race([execPromise, timeoutPromise]);
6391
+ if (timer) clearTimeout(timer);
6392
+ } else {
6393
+ raced = await execPromise;
6394
+ }
6395
+ if (raced.kind === "timeout") {
6396
+ try {
6397
+ await recordDiagnostic(candidate, "aborted_latency_budget");
6398
+ } catch {
6399
+ }
6400
+ break;
6401
+ }
6402
+ if (raced.kind === "failed") continue;
6403
+ const exec = raced.exec;
6228
6404
  const candidateLatencyMs = Date.now() - candStart;
6229
6405
  if (!exec.ok) continue;
6230
6406
  await recordShadowProbe({
@@ -6244,7 +6420,8 @@ async function runProbeCandidates(args) {
6244
6420
  // mirrors the user's actual wait; candidate latency is measured only
6245
6421
  // here and stored nowhere else (irrecoverable if not captured now).
6246
6422
  latencyCurrentMs: args.servedLatencyMs,
6247
- latencyCandidateMs: candidateLatencyMs
6423
+ latencyCandidateMs: candidateLatencyMs,
6424
+ outcome: "completed"
6248
6425
  });
6249
6426
  } catch {
6250
6427
  }
@@ -6262,7 +6439,11 @@ async function runShadowProbe(args) {
6262
6439
  servedResponse: args.servedResponse,
6263
6440
  servedLatencyMs: args.servedLatencyMs,
6264
6441
  // CallOptions is a structural superset of ProbeExecOpts.
6265
- exec: args.opts
6442
+ exec: args.opts,
6443
+ // alpha — the call()-inline path is on the user's critical path exactly when
6444
+ // BrainConfig.sync is set (call() awaits the probe before returning). That's
6445
+ // the only case where the latency budget + slow-tier skip must fire.
6446
+ sync: isBrainSync()
6266
6447
  });
6267
6448
  }
6268
6449
  async function probeShadow(ir, opts) {
@@ -6295,7 +6476,15 @@ async function probeShadow(ir, opts) {
6295
6476
  apiKeys: opts.apiKeys,
6296
6477
  fetchImpl: opts.fetchImpl,
6297
6478
  providerOverrides: opts.providerOverrides
6298
- }
6479
+ },
6480
+ // alpha — probeShadow() is ALWAYS off the user-facing critical path: the
6481
+ // consumer fires it from `onFinish` wrapped in `after()`/`waitUntil()`,
6482
+ // after the response is flushed. So the sync-mode latency budget +
6483
+ // slow-tier skip are no-ops here — slow candidates run to completion (this
6484
+ // is in fact the right place to evaluate a slow reasoner). The
6485
+ // maxLatencyMs/skipSlowTierInSync knobs aren't even exposed on
6486
+ // ProbeShadowOptions for that reason.
6487
+ sync: false
6299
6488
  });
6300
6489
  } catch {
6301
6490
  }
package/dist/index.mjs CHANGED
@@ -33,7 +33,7 @@ import {
33
33
  loadChainsFromBrain,
34
34
  readBrainReadEnv,
35
35
  resolveProviderKey
36
- } from "./chunk-SAWTKMD4.mjs";
36
+ } from "./chunk-HHWBB46W.mjs";
37
37
  import {
38
38
  ALIASES,
39
39
  LATENCY_TIER_MS,
@@ -44,7 +44,7 @@ import {
44
44
  latencyTierOf,
45
45
  profilesByProvider,
46
46
  tryGetProfile
47
- } from "./chunk-PKOFXEB3.mjs";
47
+ } from "./chunk-CXH7KC4D.mjs";
48
48
  import {
49
49
  emitAdvisoryFired,
50
50
  emitCompileDone,
@@ -2914,7 +2914,10 @@ function buildShadowProbeRow(input) {
2914
2914
  // Full IR was replayed (not a truncated preview), so fidelity is 1.0 — the
2915
2915
  // prompt-fidelity guard never fires on these rows.
2916
2916
  prompt_fidelity: 1,
2917
- replay_source: "inline-full-ir"
2917
+ replay_source: "inline-full-ir",
2918
+ // alpha — migration 029. 'completed' is the default completed-probe shape;
2919
+ // diagnostic rows (aborted/skipped) pass the explicit class.
2920
+ outcome: input.outcome ?? "completed"
2918
2921
  };
2919
2922
  }
2920
2923
  async function recordShadowProbe(input) {
@@ -3811,15 +3814,54 @@ function hashForProbe(s) {
3811
3814
  for (let i = 0; i < s.length; i++) h = (h << 5) - h + s.charCodeAt(i) | 0;
3812
3815
  return `h${(h >>> 0).toString(16)}`;
3813
3816
  }
3817
+ var DEFAULT_PROBE_MAX_LATENCY_MS = 15e3;
3818
+ function isSlowTierCandidate(candidate) {
3819
+ const profile = tryGetProfile(candidate);
3820
+ if (!profile) return false;
3821
+ return latencyTierOf(profile) === "slow";
3822
+ }
3814
3823
  async function runProbeCandidates(args) {
3815
3824
  try {
3816
3825
  if (!shouldSampleProbe(args.cfg.sampleRate ?? 0.05)) return;
3817
3826
  const candidates = normalizeProbeCandidates(args.cfg.candidates);
3818
3827
  const promptHash = hashForProbe(args.ir.currentTurn?.content ?? "");
3828
+ const budgetMs = args.cfg.maxLatencyMs ?? DEFAULT_PROBE_MAX_LATENCY_MS;
3829
+ const deadline = args.sync ? Date.now() + budgetMs : Number.POSITIVE_INFINITY;
3830
+ const skipSlow = args.sync && (args.cfg.skipSlowTierInSync ?? true);
3831
+ const recordDiagnostic = async (candidate, outcome) => {
3832
+ await recordShadowProbe({
3833
+ appId: args.ir.appId,
3834
+ archetype: args.ir.intent.archetype,
3835
+ family: deriveFamilyFromModelId(candidate) ?? "unknown",
3836
+ candidateModel: candidate,
3837
+ currentModel: args.servedModel,
3838
+ promptHash,
3839
+ // Served leg only — the candidate never produced a comparable response.
3840
+ currentResponsePreview: args.servedResponse.text.slice(0, 2e3),
3841
+ tokensCurrentIn: args.servedResponse.tokens.input,
3842
+ tokensCurrentOut: args.servedResponse.tokens.output,
3843
+ latencyCurrentMs: args.servedLatencyMs,
3844
+ outcome
3845
+ });
3846
+ };
3819
3847
  for (const candidate of candidates) {
3820
3848
  if (candidate === args.servedModel) continue;
3821
3849
  if (deriveFamilyFromModelId(candidate) === "claude-opus") continue;
3822
3850
  if (!isModelReachable(candidate, { apiKeys: args.exec.apiKeys })) continue;
3851
+ if (skipSlow && isSlowTierCandidate(candidate)) {
3852
+ try {
3853
+ await recordDiagnostic(candidate, "skipped_slow_tier_sync");
3854
+ } catch {
3855
+ }
3856
+ continue;
3857
+ }
3858
+ if (args.sync && Date.now() >= deadline) {
3859
+ try {
3860
+ await recordDiagnostic(candidate, "aborted_latency_budget");
3861
+ } catch {
3862
+ }
3863
+ break;
3864
+ }
3823
3865
  try {
3824
3866
  const candCompile = compileAndRegister(
3825
3867
  {
@@ -3830,11 +3872,32 @@ async function runProbeCandidates(args) {
3830
3872
  args.exec
3831
3873
  );
3832
3874
  const candStart = Date.now();
3833
- const exec = await execute(candCompile.request, {
3875
+ let timer;
3876
+ const execPromise = execute(candCompile.request, {
3834
3877
  apiKeys: args.exec.apiKeys,
3835
3878
  fetchImpl: args.exec.fetchImpl,
3836
3879
  providerOverrides: args.exec.providerOverrides
3837
- });
3880
+ }).then((exec2) => ({ kind: "exec", exec: exec2 })).catch(() => ({ kind: "failed" }));
3881
+ let raced;
3882
+ if (args.sync) {
3883
+ const remaining = Math.max(0, deadline - Date.now());
3884
+ const timeoutPromise = new Promise((resolve) => {
3885
+ timer = setTimeout(() => resolve({ kind: "timeout" }), remaining);
3886
+ });
3887
+ raced = await Promise.race([execPromise, timeoutPromise]);
3888
+ if (timer) clearTimeout(timer);
3889
+ } else {
3890
+ raced = await execPromise;
3891
+ }
3892
+ if (raced.kind === "timeout") {
3893
+ try {
3894
+ await recordDiagnostic(candidate, "aborted_latency_budget");
3895
+ } catch {
3896
+ }
3897
+ break;
3898
+ }
3899
+ if (raced.kind === "failed") continue;
3900
+ const exec = raced.exec;
3838
3901
  const candidateLatencyMs = Date.now() - candStart;
3839
3902
  if (!exec.ok) continue;
3840
3903
  await recordShadowProbe({
@@ -3854,7 +3917,8 @@ async function runProbeCandidates(args) {
3854
3917
  // mirrors the user's actual wait; candidate latency is measured only
3855
3918
  // here and stored nowhere else (irrecoverable if not captured now).
3856
3919
  latencyCurrentMs: args.servedLatencyMs,
3857
- latencyCandidateMs: candidateLatencyMs
3920
+ latencyCandidateMs: candidateLatencyMs,
3921
+ outcome: "completed"
3858
3922
  });
3859
3923
  } catch {
3860
3924
  }
@@ -3872,7 +3936,11 @@ async function runShadowProbe(args) {
3872
3936
  servedResponse: args.servedResponse,
3873
3937
  servedLatencyMs: args.servedLatencyMs,
3874
3938
  // CallOptions is a structural superset of ProbeExecOpts.
3875
- exec: args.opts
3939
+ exec: args.opts,
3940
+ // alpha — the call()-inline path is on the user's critical path exactly when
3941
+ // BrainConfig.sync is set (call() awaits the probe before returning). That's
3942
+ // the only case where the latency budget + slow-tier skip must fire.
3943
+ sync: isBrainSync()
3876
3944
  });
3877
3945
  }
3878
3946
  async function probeShadow(ir, opts) {
@@ -3905,7 +3973,15 @@ async function probeShadow(ir, opts) {
3905
3973
  apiKeys: opts.apiKeys,
3906
3974
  fetchImpl: opts.fetchImpl,
3907
3975
  providerOverrides: opts.providerOverrides
3908
- }
3976
+ },
3977
+ // alpha — probeShadow() is ALWAYS off the user-facing critical path: the
3978
+ // consumer fires it from `onFinish` wrapped in `after()`/`waitUntil()`,
3979
+ // after the response is flushed. So the sync-mode latency budget +
3980
+ // slow-tier skip are no-ops here — slow candidates run to completion (this
3981
+ // is in fact the right place to evaluate a slow reasoner). The
3982
+ // maxLatencyMs/skipSlowTierInSync knobs aren't even exposed on
3983
+ // ProbeShadowOptions for that reason.
3984
+ sync: false
3909
3985
  });
3910
3986
  } catch {
3911
3987
  }
@@ -830,6 +830,49 @@ interface ShadowProbeConfig {
830
830
  judge?: 'opus' | 'off';
831
831
  /** Per-(appId, archetype, candidate) daily cap. Phase 2 (needs brain-count read). Default 20. */
832
832
  maxPerDay?: number;
833
+ /**
834
+ * alpha — latency budget (ms) for the probe leg. **Only enforced in sync mode**
835
+ * (`BrainConfig.sync === true`), where `call()` awaits the probe before returning
836
+ * the served response (PB-class Edge consumers, L-086) and the candidate's
837
+ * latency therefore lands on the user-facing critical path.
838
+ *
839
+ * The probe path races against this budget: if the candidate(s) don't finish
840
+ * within `maxLatencyMs`, the probe aborts, `call()` returns the already-served
841
+ * response immediately, and an `outcome='aborted_latency_budget'` row is
842
+ * recorded (no verdict, no false quality data). **The user is never delayed
843
+ * past the budget.** Default `15000`.
844
+ *
845
+ * No-op for fire-and-forget consumers (`sync` unset/false, and every
846
+ * `probeShadow()` caller fired from `after()`/`waitUntil()`) — they don't block
847
+ * the user, so there's no latency to bound. The probe runs to completion there.
848
+ *
849
+ * Root cause this closes (PB s57 dogfood incident, 2026-06-05): PB arms the
850
+ * probe inline with `sync:true` on a `maxDuration:120` analyze route; arming a
851
+ * `latency_tier='slow'` candidate (deepseek-v4-pro, ~78–84s) pushed total
852
+ * handler time past the frontend's patience and the served analysis came back
853
+ * empty. A sync probe had no latency budget — this field is the budget.
854
+ */
855
+ maxLatencyMs?: number;
856
+ /**
857
+ * alpha — when true (the default) **in sync mode**, skip a candidate whose
858
+ * registry `latency_tier === 'slow'` *before starting it*: a slow-tier model
859
+ * (e.g. deepseek-v4-pro / deepseek-v4-flash, both `slow` = ~24s+ p50) cannot
860
+ * fit a sane inline budget, so starting it just to abort it wastes a provider
861
+ * call. An `outcome='skipped_slow_tier_sync'` row is recorded so the offline
862
+ * rollup can see the skip (not a silent drop, not a false verdict). Fail-safe.
863
+ *
864
+ * **The deeper rule (slow-reasoner → offline-mode):** inline shadow-probing is
865
+ * for FAST candidates only. To evaluate a slow reasoner you need the offline /
866
+ * async probe path (the Phase-2 batch direction) which runs off the response
867
+ * entirely — never the inline sync probe. Set this `false` only if you've moved
868
+ * the budget high enough that a slow candidate genuinely fits (rare), or you're
869
+ * in a non-sync consumer where this flag is a no-op anyway.
870
+ *
871
+ * No-op for fire-and-forget consumers (`sync` unset/false; `probeShadow()`):
872
+ * there's no user latency to protect, so slow candidates run normally. Default
873
+ * `true`.
874
+ */
875
+ skipSlowTierInSync?: boolean;
833
876
  }
834
877
  interface CallOptions {
835
878
  /** Forwarded to compile(). */
@@ -830,6 +830,49 @@ interface ShadowProbeConfig {
830
830
  judge?: 'opus' | 'off';
831
831
  /** Per-(appId, archetype, candidate) daily cap. Phase 2 (needs brain-count read). Default 20. */
832
832
  maxPerDay?: number;
833
+ /**
834
+ * alpha — latency budget (ms) for the probe leg. **Only enforced in sync mode**
835
+ * (`BrainConfig.sync === true`), where `call()` awaits the probe before returning
836
+ * the served response (PB-class Edge consumers, L-086) and the candidate's
837
+ * latency therefore lands on the user-facing critical path.
838
+ *
839
+ * The probe path races against this budget: if the candidate(s) don't finish
840
+ * within `maxLatencyMs`, the probe aborts, `call()` returns the already-served
841
+ * response immediately, and an `outcome='aborted_latency_budget'` row is
842
+ * recorded (no verdict, no false quality data). **The user is never delayed
843
+ * past the budget.** Default `15000`.
844
+ *
845
+ * No-op for fire-and-forget consumers (`sync` unset/false, and every
846
+ * `probeShadow()` caller fired from `after()`/`waitUntil()`) — they don't block
847
+ * the user, so there's no latency to bound. The probe runs to completion there.
848
+ *
849
+ * Root cause this closes (PB s57 dogfood incident, 2026-06-05): PB arms the
850
+ * probe inline with `sync:true` on a `maxDuration:120` analyze route; arming a
851
+ * `latency_tier='slow'` candidate (deepseek-v4-pro, ~78–84s) pushed total
852
+ * handler time past the frontend's patience and the served analysis came back
853
+ * empty. A sync probe had no latency budget — this field is the budget.
854
+ */
855
+ maxLatencyMs?: number;
856
+ /**
857
+ * alpha — when true (the default) **in sync mode**, skip a candidate whose
858
+ * registry `latency_tier === 'slow'` *before starting it*: a slow-tier model
859
+ * (e.g. deepseek-v4-pro / deepseek-v4-flash, both `slow` = ~24s+ p50) cannot
860
+ * fit a sane inline budget, so starting it just to abort it wastes a provider
861
+ * call. An `outcome='skipped_slow_tier_sync'` row is recorded so the offline
862
+ * rollup can see the skip (not a silent drop, not a false verdict). Fail-safe.
863
+ *
864
+ * **The deeper rule (slow-reasoner → offline-mode):** inline shadow-probing is
865
+ * for FAST candidates only. To evaluate a slow reasoner you need the offline /
866
+ * async probe path (the Phase-2 batch direction) which runs off the response
867
+ * entirely — never the inline sync probe. Set this `false` only if you've moved
868
+ * the budget high enough that a slow candidate genuinely fits (rare), or you're
869
+ * in a non-sync consumer where this flag is a no-op anyway.
870
+ *
871
+ * No-op for fire-and-forget consumers (`sync` unset/false; `probeShadow()`):
872
+ * there's no user latency to protect, so slow candidates run normally. Default
873
+ * `true`.
874
+ */
875
+ skipSlowTierInSync?: boolean;
833
876
  }
834
877
  interface CallOptions {
835
878
  /** Forwarded to compile(). */
@@ -1,4 +1,4 @@
1
- import { k as Provider } from './ir-BBKgsUX7.mjs';
1
+ import { k as Provider } from './ir-dDcG8Pvu.mjs';
2
2
  import { IntentArchetypeName } from './dialect.mjs';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { k as Provider } from './ir-D3n-pBYI.js';
1
+ import { k as Provider } from './ir-rUUojj0s.js';
2
2
  import { IntentArchetypeName } from './dialect.js';
3
3
 
4
4
  /**