@warmdrift/kgauto-compiler 2.0.0-alpha.90 → 2.0.0-alpha.92

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 (35) hide show
  1. package/dist/{chunk-ENELVMJI.mjs → chunk-4G73BYES.mjs} +10 -5
  2. package/dist/{chunk-BVEXV5KC.mjs → chunk-FEMPY6EC.mjs} +5 -1
  3. package/dist/{chunk-4LYNDEHJ.mjs → chunk-QOQV66PU.mjs} +1 -1
  4. package/dist/{chunk-OK2TMFRR.mjs → chunk-T53ISC2F.mjs} +133 -4
  5. package/dist/dialect.d.mts +11 -1
  6. package/dist/dialect.d.ts +11 -1
  7. package/dist/dialect.js +7 -2
  8. package/dist/dialect.mjs +5 -3
  9. package/dist/glassbox/index.d.mts +3 -3
  10. package/dist/glassbox/index.d.ts +3 -3
  11. package/dist/glassbox-routes/format.d.mts +2 -2
  12. package/dist/glassbox-routes/format.d.ts +2 -2
  13. package/dist/glassbox-routes/index.d.mts +4 -4
  14. package/dist/glassbox-routes/index.d.ts +4 -4
  15. package/dist/glassbox-routes/index.js +142 -8
  16. package/dist/glassbox-routes/index.mjs +2 -2
  17. package/dist/glassbox-routes/react/index.d.mts +2 -2
  18. package/dist/glassbox-routes/react/index.d.ts +2 -2
  19. package/dist/index.d.mts +15 -4
  20. package/dist/index.d.ts +15 -4
  21. package/dist/index.js +233 -17
  22. package/dist/index.mjs +93 -13
  23. package/dist/{ir-DeYMLWge.d.mts → ir-BPYh68mv.d.mts} +95 -1
  24. package/dist/{ir-CTx026t0.d.ts → ir-BWnE6LaB.d.ts} +95 -1
  25. package/dist/key-health.js +1 -1
  26. package/dist/key-health.mjs +1 -1
  27. package/dist/profiles.d.mts +1 -1
  28. package/dist/profiles.d.ts +1 -1
  29. package/dist/profiles.js +133 -4
  30. package/dist/profiles.mjs +1 -1
  31. package/dist/{types-Cp9ot1HV.d.ts → types-BCHv34P7.d.ts} +1 -1
  32. package/dist/{types-cBzinzUR.d.mts → types-BgvLmT3s.d.mts} +1 -1
  33. package/dist/{types-BKbRtmUb.d.ts → types-BhxC4hdx.d.ts} +1 -1
  34. package/dist/{types-DD36cCbZ.d.mts → types-eLelJBj-.d.mts} +1 -1
  35. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -11,15 +11,16 @@ import {
11
11
  hashShape,
12
12
  isArchetype,
13
13
  learningKey,
14
- resolveOutputMode
15
- } from "./chunk-BVEXV5KC.mjs";
14
+ resolveOutputMode,
15
+ routeScopedModel
16
+ } from "./chunk-FEMPY6EC.mjs";
16
17
  import {
17
18
  KEY_FINGERPRINT_DOMAIN,
18
19
  KEY_FINGERPRINT_LENGTH,
19
20
  LIBRARY_VERSION,
20
21
  createKeyHealthRoute,
21
22
  keyFingerprint
22
- } from "./chunk-4LYNDEHJ.mjs";
23
+ } from "./chunk-QOQV66PU.mjs";
23
24
  import {
24
25
  ABSOLUTE_FLOOR,
25
26
  ARCHETYPE_FLOOR_DEFAULT,
@@ -57,7 +58,7 @@ import {
57
58
  policySetHas,
58
59
  readBrainReadEnv,
59
60
  resolveProviderKey
60
- } from "./chunk-ENELVMJI.mjs";
61
+ } from "./chunk-4G73BYES.mjs";
61
62
  import {
62
63
  ALIASES,
63
64
  LATENCY_TIER_MS,
@@ -73,7 +74,7 @@ import {
73
74
  registerProfiles,
74
75
  resolveModelAlias,
75
76
  tryGetProfile
76
- } from "./chunk-OK2TMFRR.mjs";
77
+ } from "./chunk-T53ISC2F.mjs";
77
78
  import {
78
79
  emitAdvisoryFired,
79
80
  emitCompileDone,
@@ -1169,7 +1170,26 @@ function passApplyConventions(ir, profile) {
1169
1170
  }
1170
1171
 
1171
1172
  // src/lower.ts
1173
+ var OPENROUTER_VENDOR_SLUG = {
1174
+ anthropic: "anthropic",
1175
+ google: "google",
1176
+ openai: "openai",
1177
+ deepseek: "deepseek",
1178
+ zai: "z-ai",
1179
+ moonshot: "moonshotai"
1180
+ };
1181
+ var OPENROUTER_ID_OVERRIDES = Object.freeze({});
1182
+ function openRouterModelId(profile) {
1183
+ const override = OPENROUTER_ID_OVERRIDES[profile.id];
1184
+ if (override) return override;
1185
+ const slug = OPENROUTER_VENDOR_SLUG[profile.provider];
1186
+ if (!slug) throw new Error(`No OpenRouter vendor slug for provider ${profile.provider} (model ${profile.id})`);
1187
+ return `${slug}/${profile.id}`;
1188
+ }
1172
1189
  function lower(ir, profile, hints = {}) {
1190
+ if (hints.route === "openrouter") {
1191
+ return lowerOpenRouter(ir, profile);
1192
+ }
1173
1193
  switch (profile.provider) {
1174
1194
  case "anthropic":
1175
1195
  return lowerAnthropic(ir, profile, hints);
@@ -1522,6 +1542,23 @@ function lowerZai(ir, profile, hints) {
1522
1542
  }
1523
1543
  };
1524
1544
  }
1545
+ function lowerOpenRouter(ir, profile) {
1546
+ const parts = buildOpenAICompatibleParts(ir);
1547
+ return {
1548
+ request: {
1549
+ provider: "openrouter",
1550
+ model: openRouterModelId(profile),
1551
+ messages: parts.messages,
1552
+ tools: parts.tools,
1553
+ response_format: parts.response_format
1554
+ },
1555
+ diagnostics: {
1556
+ cacheableTokens: 0,
1557
+ historyCacheableTokens: parts.historyCacheableTokens,
1558
+ estimatedCacheSavingsUsd: 0
1559
+ }
1560
+ };
1561
+ }
1525
1562
  function lowerMoonshot(ir, profile) {
1526
1563
  const parts = buildOpenAICompatibleParts(ir);
1527
1564
  return {
@@ -3173,12 +3210,13 @@ function compile(ir, opts = {}) {
3173
3210
  const lowered = lower(workingIR, profile, {
3174
3211
  forceThinkingZero: cliffs.value.loweringHints.forceThinkingZero,
3175
3212
  forceTerseOutput: cliffs.value.loweringHints.forceTerseOutput,
3176
- wireOverrides
3213
+ wireOverrides,
3214
+ route: opts.route
3177
3215
  });
3178
3216
  validateFinalFit(workingIR, profile, inputTokens);
3179
3217
  const handle = makeHandle();
3180
3218
  const finalShape = computeShape(workingIR, inputTokens);
3181
- const _learningKey = learningKey(ir.intent.archetype, profile.id, finalShape);
3219
+ const _learningKey = learningKey(ir.intent.archetype, routeScopedModel(profile.id, opts.route), finalShape);
3182
3220
  const historyCacheMarkIndex = computeHistoryCacheMarkIndex(workingIR);
3183
3221
  const systemMessages = buildSystemMessages(workingIR, profile.provider);
3184
3222
  const systemCacheMarkIndex = lastCacheableSystemIndex(systemMessages);
@@ -3677,7 +3715,7 @@ async function flushBrainDeadLetter() {
3677
3715
  }
3678
3716
  var compileRegistry = /* @__PURE__ */ new Map();
3679
3717
  var REGISTRY_MAX_ENTRIES = 1e4;
3680
- function registerCompile(appId, archetype, ir, result, parentHandle) {
3718
+ function registerCompile(appId, archetype, ir, result, parentHandle, route) {
3681
3719
  if (compileRegistry.size >= REGISTRY_MAX_ENTRIES) {
3682
3720
  const cutoff = Math.floor(REGISTRY_MAX_ENTRIES * 0.25);
3683
3721
  let evicted = 0;
@@ -3709,7 +3747,10 @@ function registerCompile(appId, archetype, ir, result, parentHandle) {
3709
3747
  model: result.target,
3710
3748
  provider: result.provider,
3711
3749
  shapeKey,
3712
- learningKey: learningKey(archetype, result.target, shape),
3750
+ route: route ?? "direct",
3751
+ // alpha.92 — route-scoped for non-direct routes: the same model id on a
3752
+ // gateway is a different serving stack (Venice/StreamLake, 2026-08-26).
3753
+ learningKey: learningKey(archetype, routeScopedModel(result.target, route), shape),
3713
3754
  estimatedTokensIn: tokens,
3714
3755
  mutationsApplied: result.mutationsApplied.map((m) => m.id),
3715
3756
  // alpha.30: cache the in-memory advisories so record() can auto-persist
@@ -3903,6 +3944,11 @@ function buildPayload(input, reg) {
3903
3944
  provider: reg?.provider,
3904
3945
  shape_key: reg?.shapeKey,
3905
3946
  learning_key: reg?.learningKey,
3947
+ // alpha.92 (migration 067) — route identity on every row. Input wins
3948
+ // (a manual record() replaying through a gateway states its own route);
3949
+ // registry fills the call() path; column default covers legacy writers.
3950
+ route: input.route ?? reg?.route ?? "direct",
3951
+ route_host: input.routeHost,
3906
3952
  mutations_applied: mutationsApplied,
3907
3953
  tokens_in: input.tokensIn,
3908
3954
  tokens_out: input.tokensOut,
@@ -4697,6 +4743,7 @@ var OPENAI_URL = "https://api.openai.com/v1/chat/completions";
4697
4743
  var DEEPSEEK_URL = "https://api.deepseek.com/chat/completions";
4698
4744
  var ZAI_URL = "https://api.z.ai/api/paas/v4/chat/completions";
4699
4745
  var MOONSHOT_URL = "https://api.moonshot.ai/v1/chat/completions";
4746
+ var OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions";
4700
4747
  async function execute(request, opts = {}) {
4701
4748
  const merged = applyOverrides(request, opts.providerOverrides);
4702
4749
  switch (merged.provider) {
@@ -4720,6 +4767,12 @@ async function execute(request, opts = {}) {
4720
4767
  opts,
4721
4768
  { provider: "moonshot", url: MOONSHOT_URL, missingKeyMessage: "MOONSHOT_API_KEY missing" }
4722
4769
  );
4770
+ case "openrouter":
4771
+ return executeOpenAICompatible(
4772
+ merged,
4773
+ opts,
4774
+ { provider: "openrouter", url: OPENROUTER_URL, missingKeyMessage: "OPENROUTER_API_KEY missing" }
4775
+ );
4723
4776
  default: {
4724
4777
  const _exhaustive = merged;
4725
4778
  throw new Error(`execute(): no executor for provider: ${JSON.stringify(_exhaustive)}`);
@@ -4919,6 +4972,7 @@ function normalizeOpenAILike(raw) {
4919
4972
  const r = raw;
4920
4973
  const choice = r.choices?.[0];
4921
4974
  const text = choice?.message?.content ?? "";
4975
+ const routeHost = typeof raw?.provider === "string" ? raw.provider : void 0;
4922
4976
  const toolCalls = (choice?.message?.tool_calls ?? []).filter((tc) => tc.function?.name).map((tc, i) => ({
4923
4977
  id: tc.id ?? `tc-${i}`,
4924
4978
  name: tc.function.name,
@@ -4931,7 +4985,7 @@ function normalizeOpenAILike(raw) {
4931
4985
  total: u.total_tokens ?? (u.prompt_tokens ?? 0) + (u.completion_tokens ?? 0),
4932
4986
  cached: u.prompt_tokens_details?.cached_tokens
4933
4987
  };
4934
- return { text, structuredOutput: null, toolCalls, tokens, finishReason: choice?.finish_reason, raw };
4988
+ return { text, structuredOutput: null, toolCalls, tokens, finishReason: choice?.finish_reason, routeHost, raw };
4935
4989
  }
4936
4990
  function applyOverrides(request, overrides) {
4937
4991
  if (!overrides) return request;
@@ -4956,6 +5010,9 @@ function classifyHttpError2(status, body) {
4956
5010
  if (status === 404) {
4957
5011
  return { ok: false, status, errorType: "retryable", errorCode: "model_not_found", message, raw: body };
4958
5012
  }
5013
+ if (status >= 400 && status < 500 && isModelUnavailableMessage(message)) {
5014
+ return { ok: false, status, errorType: "retryable", errorCode: "model_not_found", message, raw: body };
5015
+ }
4959
5016
  if (status === 401 || status === 403) {
4960
5017
  return { ok: false, status, errorType: "terminal", errorCode: "auth", message, raw: body };
4961
5018
  }
@@ -4967,6 +5024,12 @@ function classifyHttpError2(status, body) {
4967
5024
  }
4968
5025
  return { ok: false, status, errorType: "terminal", errorCode: "unknown", message, raw: body };
4969
5026
  }
5027
+ function isModelUnavailableMessage(message) {
5028
+ if (!message) return false;
5029
+ return /no longer (available|supported)|not available to new users|has been (deprecated|retired|discontinued|shut ?down)/i.test(
5030
+ message
5031
+ );
5032
+ }
4970
5033
  function extractErrorMessage2(body) {
4971
5034
  if (!body || typeof body !== "object") return void 0;
4972
5035
  const b = body;
@@ -5060,6 +5123,7 @@ async function call(ir, opts = {}) {
5060
5123
  handle: initial.handle,
5061
5124
  source: opts.source,
5062
5125
  endUserId: opts.endUserId,
5126
+ route: opts.route,
5063
5127
  tokensIn: 0,
5064
5128
  tokensOut: 0,
5065
5129
  latencyMs: latencyMs2,
@@ -5114,6 +5178,7 @@ async function call(ir, opts = {}) {
5114
5178
  handle: initial.handle,
5115
5179
  source: opts.source,
5116
5180
  endUserId: opts.endUserId,
5181
+ route: opts.route,
5117
5182
  tokensIn: 0,
5118
5183
  tokensOut: 0,
5119
5184
  latencyMs: latencyMs2,
@@ -5257,6 +5322,11 @@ async function call(ir, opts = {}) {
5257
5322
  handle: initial.handle,
5258
5323
  source: opts.source,
5259
5324
  endUserId: opts.endUserId,
5325
+ // alpha.92 — route identity on the row (registry also carries it;
5326
+ // input wins in buildPayload, so state it explicitly here with the
5327
+ // host the gateway reported for the WINNING attempt).
5328
+ route: opts.route,
5329
+ routeHost: validated.response.routeHost,
5260
5330
  tokensIn: validated.response.tokens.input,
5261
5331
  tokensOut: validated.response.tokens.output,
5262
5332
  latencyMs: latencyMs2,
@@ -5337,6 +5407,12 @@ async function call(ir, opts = {}) {
5337
5407
  provider: activeCompile.provider,
5338
5408
  response: validated.response,
5339
5409
  latencyMs: latencyMs2,
5410
+ // alpha.92 — route identity + the served profile's cliffs flagged
5411
+ // route-unverified on a non-direct route (guards still fired; their
5412
+ // evidence is vendor-route evidence — see the execute-leg contract).
5413
+ route: opts.route ?? "direct",
5414
+ routeHost: validated.response.routeHost,
5415
+ routeUnverifiedCliffs: opts.route === "openrouter" ? (tryGetProfile(targetModel)?.cliffs ?? []).map((c) => c.metric) : void 0,
5340
5416
  mutationsApplied: activeCompile.mutationsApplied,
5341
5417
  attempts,
5342
5418
  servedBy: targetModel,
@@ -5381,6 +5457,7 @@ async function call(ir, opts = {}) {
5381
5457
  handle: initial.handle,
5382
5458
  source: opts.source,
5383
5459
  endUserId: opts.endUserId,
5460
+ route: opts.route,
5384
5461
  tokensIn: lastErr?.tokens?.input ?? 0,
5385
5462
  tokensOut: lastErr?.tokens?.output ?? 0,
5386
5463
  latencyMs,
@@ -5424,9 +5501,12 @@ function compileAndRegister(ir, opts) {
5424
5501
  toolRelevanceThreshold: opts.toolRelevanceThreshold,
5425
5502
  compressHistoryAfter: opts.compressHistoryAfter,
5426
5503
  // alpha.68 / Release A — carry the fan-out parent handle through to compile.
5427
- parentHandle: opts.parentHandle
5504
+ parentHandle: opts.parentHandle,
5505
+ // alpha.92 — gateway route (execute-leg contract): changes the wire +
5506
+ // learning-key encoding, never the pass pipeline.
5507
+ route: opts.route
5428
5508
  });
5429
- registerCompile(ir.appId, ir.intent.archetype, ir, result, opts.parentHandle);
5509
+ registerCompile(ir.appId, ir.intent.archetype, ir, result, opts.parentHandle, opts.route);
5430
5510
  return result;
5431
5511
  }
5432
5512
  function extractPromptPreview(ir) {
@@ -7490,7 +7570,7 @@ function planDecomposition(args) {
7490
7570
  // src/index.ts
7491
7571
  function compile2(ir, opts) {
7492
7572
  const result = compile(ir, opts);
7493
- registerCompile(ir.appId, ir.intent.archetype, ir, result, opts?.parentHandle);
7573
+ registerCompile(ir.appId, ir.intent.archetype, ir, result, opts?.parentHandle, opts?.route);
7494
7574
  return result;
7495
7575
  }
7496
7576
  export {
@@ -440,7 +440,21 @@ interface PromptIR {
440
440
  */
441
441
  historyCachePolicy?: HistoryCachePolicy;
442
442
  }
443
- type Provider = 'anthropic' | 'google' | 'openai' | 'deepseek' | 'zai' | 'moonshot' | 'mistral' | 'xai';
443
+ type Provider = 'anthropic' | 'google' | 'openai' | 'deepseek' | 'zai' | 'moonshot' | 'mistral' | 'xai' | 'openrouter';
444
+ /**
445
+ * alpha.92 — the route a call travels, part of a measurement's identity
446
+ * (execute-leg design contract, 2026-08-26). `'direct'` = the vendor's own
447
+ * API (every measurement before this release; the default, and the only
448
+ * route consumer production traffic uses unless a consumer explicitly opts
449
+ * in later). `'openrouter'` = the OpenRouter gateway — the STARTER tier for
450
+ * eval and candidate admission. The route matters because the model behind
451
+ * a name is a different serving stack per gateway host: the same
452
+ * `deepseek-v4-flash` id was served by "Venice" and by "StreamLake"
453
+ * emitting PARALLEL tool calls — behavior opposite to the vendor-API
454
+ * evidence our cliff rests on. Vendor-keyed evidence does not transfer
455
+ * through a gateway, so verdicts never silently transfer across routes.
456
+ */
457
+ type CallRoute = 'direct' | 'openrouter';
444
458
  /**
445
459
  * Mutation IDs that fired during compile. Empty in v1 (no mutation engine
446
460
  * yet). Populated when the brain is online and pushing mutations.
@@ -592,6 +606,22 @@ type CompiledRequest = {
592
606
  }>;
593
607
  tools?: unknown[];
594
608
  response_format?: unknown;
609
+ } | {
610
+ /**
611
+ * alpha.92 — the OpenRouter gateway route. OpenAI-compatible wire;
612
+ * `model` is the OpenRouter id (`vendor/model`, e.g.
613
+ * `deepseek/deepseek-v4-flash`), mapped from the CANONICAL profile id
614
+ * by `openRouterModelId()` — the canonical id stays the measurement
615
+ * identity everywhere else.
616
+ */
617
+ provider: 'openrouter';
618
+ model: string;
619
+ messages: Array<{
620
+ role: string;
621
+ content: unknown;
622
+ }>;
623
+ tools?: unknown[];
624
+ response_format?: unknown;
595
625
  };
596
626
  /**
597
627
  * Best-practice advisory emitted by the compiler at compile time. Non-fatal —
@@ -1065,6 +1095,14 @@ interface NormalizedResponse {
1065
1095
  tokens: NormalizedTokens;
1066
1096
  /** Provider-specific finish reason, passed through unchanged. */
1067
1097
  finishReason?: string;
1098
+ /**
1099
+ * alpha.92 — the serving host a gateway reported (OpenRouter's response
1100
+ * `provider` field, captured verbatim: "Venice", "StreamLake", …).
1101
+ * Undefined on direct vendor calls and on gateways that don't report it.
1102
+ * Discarding this would be the L-073 silent-drop at birth — the gateway
1103
+ * itself tells us which stack served the call.
1104
+ */
1105
+ routeHost?: string;
1068
1106
  /** Untouched provider response — escape hatch for consumers needing fields not yet normalized. */
1069
1107
  raw: unknown;
1070
1108
  /** Set when structuredOutput parsing was attempted and failed. */
@@ -1077,6 +1115,8 @@ interface ApiKeys {
1077
1115
  deepseek?: string;
1078
1116
  zai?: string;
1079
1117
  moonshot?: string;
1118
+ /** alpha.92 — the OpenRouter gateway route's key (a route key, not a vendor key). */
1119
+ openrouter?: string;
1080
1120
  }
1081
1121
  /**
1082
1122
  * Per-provider override fields shallow-merged into the lowered request before
@@ -1090,6 +1130,8 @@ interface ProviderOverrides {
1090
1130
  deepseek?: Record<string, unknown>;
1091
1131
  zai?: Record<string, unknown>;
1092
1132
  moonshot?: Record<string, unknown>;
1133
+ /** alpha.92 — overrides for requests traveling the OpenRouter route. */
1134
+ openrouter?: Record<string, unknown>;
1093
1135
  }
1094
1136
  /**
1095
1137
  * Full-IR inline shadow-probe config (Shape B, Phase 1 — 2026-05-29 s51).
@@ -1180,6 +1222,24 @@ interface CallOptions {
1180
1222
  * money attributable to the customer). See {@link RecordInput.endUserId}.
1181
1223
  */
1182
1224
  endUserId?: string;
1225
+ /**
1226
+ * alpha.92 — route this call through a gateway instead of the vendor's
1227
+ * own API. Omit (or `'direct'`) for normal calls — this is the STARTER
1228
+ * tier for eval and candidate admission, NOT a consumer routing surface:
1229
+ * production traffic stays direct-vendor-only by default per the
1230
+ * execute-leg design contract. When `'openrouter'`:
1231
+ * - lowering emits the OpenAI-compatible wire with the OpenRouter id
1232
+ * (canonical id stays the measurement identity),
1233
+ * - the outcome row records `route='openrouter'` + `route_host` (the
1234
+ * serving host OpenRouter reports),
1235
+ * - the learning key gains the route dimension (`…::model@openrouter`),
1236
+ * so gateway evidence never pollutes direct-vendor evidence,
1237
+ * - cliff guards still apply but are route-unverified (see
1238
+ * {@link CallResult.routeUnverifiedCliffs}) — never assume a host
1239
+ * fixed a vendor cliff.
1240
+ * Requires `OPENROUTER_API_KEY`.
1241
+ */
1242
+ route?: CallRoute;
1183
1243
  /**
1184
1244
  * alpha.84 — fires once per FAILED attempt during the fallback walk, before
1185
1245
  * the walk continues. IC-Cairn's filing, 2026-07-29.
@@ -1429,6 +1489,28 @@ interface CallResult {
1429
1489
  provider: Provider;
1430
1490
  response: NormalizedResponse;
1431
1491
  latencyMs: number;
1492
+ /**
1493
+ * alpha.92 — the route this call traveled. `'direct'` unless the caller
1494
+ * passed `CallOptions.route`. Part of the measurement's identity: the
1495
+ * outcome row and learning key are route-scoped, and verdicts never
1496
+ * silently transfer across routes.
1497
+ */
1498
+ route: CallRoute;
1499
+ /**
1500
+ * alpha.92 — the serving host the gateway reported for the winning
1501
+ * attempt (OpenRouter's `provider` field: "Venice", "StreamLake", …).
1502
+ * Undefined on direct calls.
1503
+ */
1504
+ routeHost?: string;
1505
+ /**
1506
+ * alpha.92 — cliff metrics of the served profile that applied on a
1507
+ * non-direct route. The guards STILL FIRED (conservative default: never
1508
+ * assume a host fixed a vendor cliff) but their evidence is vendor-route
1509
+ * evidence — a different serving stack may not share the cliff. The
1510
+ * probe-suite can promote/demote per (cliff × route). Undefined on
1511
+ * direct calls or when the served profile has no cliffs.
1512
+ */
1513
+ routeUnverifiedCliffs?: string[];
1432
1514
  /** Mutations that fired during compile (informational, mirrors CompileResult.mutationsApplied). */
1433
1515
  mutationsApplied: MutationApplied[];
1434
1516
  /** One entry per provider attempt — observability for retry/fallback walks. */
@@ -1592,6 +1674,18 @@ interface RecordInput {
1592
1674
  * Omit for traffic that has no end user.
1593
1675
  */
1594
1676
  endUserId?: string;
1677
+ /**
1678
+ * alpha.92 — the route the call traveled (migration 067). `call()` sets
1679
+ * this automatically from `CallOptions.route`; manual `record()` callers
1680
+ * replaying through a gateway set it themselves. Omit for direct calls —
1681
+ * the column defaults to `'direct'`.
1682
+ */
1683
+ route?: CallRoute;
1684
+ /**
1685
+ * alpha.92 — the gateway-reported serving host ("Venice", "StreamLake").
1686
+ * Only meaningful with a non-direct `route`.
1687
+ */
1688
+ routeHost?: string;
1595
1689
  /** Actual tokens consumed (post-call). */
1596
1690
  tokensIn: number;
1597
1691
  tokensOut: number;
@@ -440,7 +440,21 @@ interface PromptIR {
440
440
  */
441
441
  historyCachePolicy?: HistoryCachePolicy;
442
442
  }
443
- type Provider = 'anthropic' | 'google' | 'openai' | 'deepseek' | 'zai' | 'moonshot' | 'mistral' | 'xai';
443
+ type Provider = 'anthropic' | 'google' | 'openai' | 'deepseek' | 'zai' | 'moonshot' | 'mistral' | 'xai' | 'openrouter';
444
+ /**
445
+ * alpha.92 — the route a call travels, part of a measurement's identity
446
+ * (execute-leg design contract, 2026-08-26). `'direct'` = the vendor's own
447
+ * API (every measurement before this release; the default, and the only
448
+ * route consumer production traffic uses unless a consumer explicitly opts
449
+ * in later). `'openrouter'` = the OpenRouter gateway — the STARTER tier for
450
+ * eval and candidate admission. The route matters because the model behind
451
+ * a name is a different serving stack per gateway host: the same
452
+ * `deepseek-v4-flash` id was served by "Venice" and by "StreamLake"
453
+ * emitting PARALLEL tool calls — behavior opposite to the vendor-API
454
+ * evidence our cliff rests on. Vendor-keyed evidence does not transfer
455
+ * through a gateway, so verdicts never silently transfer across routes.
456
+ */
457
+ type CallRoute = 'direct' | 'openrouter';
444
458
  /**
445
459
  * Mutation IDs that fired during compile. Empty in v1 (no mutation engine
446
460
  * yet). Populated when the brain is online and pushing mutations.
@@ -592,6 +606,22 @@ type CompiledRequest = {
592
606
  }>;
593
607
  tools?: unknown[];
594
608
  response_format?: unknown;
609
+ } | {
610
+ /**
611
+ * alpha.92 — the OpenRouter gateway route. OpenAI-compatible wire;
612
+ * `model` is the OpenRouter id (`vendor/model`, e.g.
613
+ * `deepseek/deepseek-v4-flash`), mapped from the CANONICAL profile id
614
+ * by `openRouterModelId()` — the canonical id stays the measurement
615
+ * identity everywhere else.
616
+ */
617
+ provider: 'openrouter';
618
+ model: string;
619
+ messages: Array<{
620
+ role: string;
621
+ content: unknown;
622
+ }>;
623
+ tools?: unknown[];
624
+ response_format?: unknown;
595
625
  };
596
626
  /**
597
627
  * Best-practice advisory emitted by the compiler at compile time. Non-fatal —
@@ -1065,6 +1095,14 @@ interface NormalizedResponse {
1065
1095
  tokens: NormalizedTokens;
1066
1096
  /** Provider-specific finish reason, passed through unchanged. */
1067
1097
  finishReason?: string;
1098
+ /**
1099
+ * alpha.92 — the serving host a gateway reported (OpenRouter's response
1100
+ * `provider` field, captured verbatim: "Venice", "StreamLake", …).
1101
+ * Undefined on direct vendor calls and on gateways that don't report it.
1102
+ * Discarding this would be the L-073 silent-drop at birth — the gateway
1103
+ * itself tells us which stack served the call.
1104
+ */
1105
+ routeHost?: string;
1068
1106
  /** Untouched provider response — escape hatch for consumers needing fields not yet normalized. */
1069
1107
  raw: unknown;
1070
1108
  /** Set when structuredOutput parsing was attempted and failed. */
@@ -1077,6 +1115,8 @@ interface ApiKeys {
1077
1115
  deepseek?: string;
1078
1116
  zai?: string;
1079
1117
  moonshot?: string;
1118
+ /** alpha.92 — the OpenRouter gateway route's key (a route key, not a vendor key). */
1119
+ openrouter?: string;
1080
1120
  }
1081
1121
  /**
1082
1122
  * Per-provider override fields shallow-merged into the lowered request before
@@ -1090,6 +1130,8 @@ interface ProviderOverrides {
1090
1130
  deepseek?: Record<string, unknown>;
1091
1131
  zai?: Record<string, unknown>;
1092
1132
  moonshot?: Record<string, unknown>;
1133
+ /** alpha.92 — overrides for requests traveling the OpenRouter route. */
1134
+ openrouter?: Record<string, unknown>;
1093
1135
  }
1094
1136
  /**
1095
1137
  * Full-IR inline shadow-probe config (Shape B, Phase 1 — 2026-05-29 s51).
@@ -1180,6 +1222,24 @@ interface CallOptions {
1180
1222
  * money attributable to the customer). See {@link RecordInput.endUserId}.
1181
1223
  */
1182
1224
  endUserId?: string;
1225
+ /**
1226
+ * alpha.92 — route this call through a gateway instead of the vendor's
1227
+ * own API. Omit (or `'direct'`) for normal calls — this is the STARTER
1228
+ * tier for eval and candidate admission, NOT a consumer routing surface:
1229
+ * production traffic stays direct-vendor-only by default per the
1230
+ * execute-leg design contract. When `'openrouter'`:
1231
+ * - lowering emits the OpenAI-compatible wire with the OpenRouter id
1232
+ * (canonical id stays the measurement identity),
1233
+ * - the outcome row records `route='openrouter'` + `route_host` (the
1234
+ * serving host OpenRouter reports),
1235
+ * - the learning key gains the route dimension (`…::model@openrouter`),
1236
+ * so gateway evidence never pollutes direct-vendor evidence,
1237
+ * - cliff guards still apply but are route-unverified (see
1238
+ * {@link CallResult.routeUnverifiedCliffs}) — never assume a host
1239
+ * fixed a vendor cliff.
1240
+ * Requires `OPENROUTER_API_KEY`.
1241
+ */
1242
+ route?: CallRoute;
1183
1243
  /**
1184
1244
  * alpha.84 — fires once per FAILED attempt during the fallback walk, before
1185
1245
  * the walk continues. IC-Cairn's filing, 2026-07-29.
@@ -1429,6 +1489,28 @@ interface CallResult {
1429
1489
  provider: Provider;
1430
1490
  response: NormalizedResponse;
1431
1491
  latencyMs: number;
1492
+ /**
1493
+ * alpha.92 — the route this call traveled. `'direct'` unless the caller
1494
+ * passed `CallOptions.route`. Part of the measurement's identity: the
1495
+ * outcome row and learning key are route-scoped, and verdicts never
1496
+ * silently transfer across routes.
1497
+ */
1498
+ route: CallRoute;
1499
+ /**
1500
+ * alpha.92 — the serving host the gateway reported for the winning
1501
+ * attempt (OpenRouter's `provider` field: "Venice", "StreamLake", …).
1502
+ * Undefined on direct calls.
1503
+ */
1504
+ routeHost?: string;
1505
+ /**
1506
+ * alpha.92 — cliff metrics of the served profile that applied on a
1507
+ * non-direct route. The guards STILL FIRED (conservative default: never
1508
+ * assume a host fixed a vendor cliff) but their evidence is vendor-route
1509
+ * evidence — a different serving stack may not share the cliff. The
1510
+ * probe-suite can promote/demote per (cliff × route). Undefined on
1511
+ * direct calls or when the served profile has no cliffs.
1512
+ */
1513
+ routeUnverifiedCliffs?: string[];
1432
1514
  /** Mutations that fired during compile (informational, mirrors CompileResult.mutationsApplied). */
1433
1515
  mutationsApplied: MutationApplied[];
1434
1516
  /** One entry per provider attempt — observability for retry/fallback walks. */
@@ -1592,6 +1674,18 @@ interface RecordInput {
1592
1674
  * Omit for traffic that has no end user.
1593
1675
  */
1594
1676
  endUserId?: string;
1677
+ /**
1678
+ * alpha.92 — the route the call traveled (migration 067). `call()` sets
1679
+ * this automatically from `CallOptions.route`; manual `record()` callers
1680
+ * replaying through a gateway set it themselves. Omit for direct calls —
1681
+ * the column defaults to `'direct'`.
1682
+ */
1683
+ route?: CallRoute;
1684
+ /**
1685
+ * alpha.92 — the gateway-reported serving host ("Venice", "StreamLake").
1686
+ * Only meaningful with a non-direct `route`.
1687
+ */
1688
+ routeHost?: string;
1595
1689
  /** Actual tokens consumed (post-call). */
1596
1690
  tokensIn: number;
1597
1691
  tokensOut: number;
@@ -28,7 +28,7 @@ __export(key_health_exports, {
28
28
  module.exports = __toCommonJS(key_health_exports);
29
29
 
30
30
  // src/version.ts
31
- var LIBRARY_VERSION = "2.0.0-alpha.90";
31
+ var LIBRARY_VERSION = "2.0.0-alpha.92";
32
32
 
33
33
  // src/key-health.ts
34
34
  var JSON_HEADERS = { "Content-Type": "application/json" };
@@ -3,7 +3,7 @@ import {
3
3
  KEY_FINGERPRINT_LENGTH,
4
4
  createKeyHealthRoute,
5
5
  keyFingerprint
6
- } from "./chunk-4LYNDEHJ.mjs";
6
+ } from "./chunk-QOQV66PU.mjs";
7
7
  export {
8
8
  KEY_FINGERPRINT_DOMAIN,
9
9
  KEY_FINGERPRINT_LENGTH,
@@ -1,4 +1,4 @@
1
- import { l as Provider } from './ir-DeYMLWge.mjs';
1
+ import { l as Provider } from './ir-BPYh68mv.mjs';
2
2
  import { IntentArchetypeName } from './dialect.mjs';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { l as Provider } from './ir-CTx026t0.js';
1
+ import { l as Provider } from './ir-BWnE6LaB.js';
2
2
  import { IntentArchetypeName } from './dialect.js';
3
3
 
4
4
  /**