@warmdrift/kgauto-compiler 2.0.0-alpha.83 → 2.0.0-alpha.85

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.
@@ -305,6 +305,17 @@ interface CompilePolicy {
305
305
  * Model IDs the consumer has gated. Compile() will never select these.
306
306
  * Use for: cost caps, account-level rate limits, "this model is broken
307
307
  * for our workload" decisions.
308
+ *
309
+ * Matching is by EXACT id after alias resolution (alpha.85): an alias
310
+ * and its canonical id are the same model, so blocking either blocks
311
+ * both (`deepseek-chat` ≡ `deepseek-v4-flash`). Matching does NOT widen
312
+ * to family siblings — blocking `deepseek-v4-flash` says nothing about
313
+ * `deepseek-v4-pro` — and a bare family key (`'deepseek'`,
314
+ * `'claude-sonnet'`) is INERT: it matches no model and no warning fires
315
+ * today. To gate a family, list its concrete ids. Beware roster
316
+ * retargets: when a family primary moves to a new id, a literal-id
317
+ * block does not follow it (tt-intel s113 built consumer-side family
318
+ * resolution for exactly this).
308
319
  */
309
320
  blockedModels?: string[];
310
321
  /**
@@ -1073,6 +1084,44 @@ interface ShadowProbeConfig {
1073
1084
  skipSlowTierInSync?: boolean;
1074
1085
  }
1075
1086
  interface CallOptions {
1087
+ /**
1088
+ * alpha.84 — fires once per FAILED attempt during the fallback walk, before
1089
+ * the walk continues. IC-Cairn's filing, 2026-07-29.
1090
+ *
1091
+ * ## The gap this closes
1092
+ *
1093
+ * `call()` records exactly ONE outcome row per call. An attempt that fails
1094
+ * and then walks to a successful fallback survives only as
1095
+ * `fellOverFrom`/`fallbackReason` metadata ON the success row — so a
1096
+ * `call()`-only consumer's corpus contains **no failure rows at all**, by
1097
+ * construction, no matter how carefully they wired `record()`.
1098
+ *
1099
+ * That is not hypothetical. kgauto's own `corpus-implausible-success`
1100
+ * detector fired on inspire-central and told them to "wire record() on the
1101
+ * FAILURE path" — which they had already done, in five places. Measured:
1102
+ * 89 rows, 0 failure rows, 2 fellover-bearing. **The detector demanded
1103
+ * output the API could not produce.** Streaming consumers have had
1104
+ * `onFailedAttempt` since alpha.48 via `streamWithFallover`; `call()`
1105
+ * consumers had nothing. This restores parity.
1106
+ *
1107
+ * ## Contract
1108
+ *
1109
+ * Fires for every attempt whose `status` is `'retryable'` or `'terminal'`,
1110
+ * including skipped ones (`*_inferred`, `aborted`) — a skip is a real
1111
+ * datum about the chain, not an absence. Never fires for `'success'`.
1112
+ *
1113
+ * Called synchronously and **its throw is swallowed**: a consumer's
1114
+ * telemetry bug must never take down the call it is observing. It is a
1115
+ * notification, not a hook that can veto or alter the walk.
1116
+ *
1117
+ * Typical use — emit a failure row per attempt so the corpus can contain
1118
+ * failures at all:
1119
+ *
1120
+ * call(ir, { onFailedAttempt: (a) => void recordOutcome({
1121
+ * ...base, success: false, errorType: a.errorCode, model: a.model,
1122
+ * }) })
1123
+ */
1124
+ onFailedAttempt?: (attempt: CallAttempt) => void;
1076
1125
  /** Forwarded to compile(). */
1077
1126
  policy?: CompilePolicy;
1078
1127
  /**
@@ -305,6 +305,17 @@ interface CompilePolicy {
305
305
  * Model IDs the consumer has gated. Compile() will never select these.
306
306
  * Use for: cost caps, account-level rate limits, "this model is broken
307
307
  * for our workload" decisions.
308
+ *
309
+ * Matching is by EXACT id after alias resolution (alpha.85): an alias
310
+ * and its canonical id are the same model, so blocking either blocks
311
+ * both (`deepseek-chat` ≡ `deepseek-v4-flash`). Matching does NOT widen
312
+ * to family siblings — blocking `deepseek-v4-flash` says nothing about
313
+ * `deepseek-v4-pro` — and a bare family key (`'deepseek'`,
314
+ * `'claude-sonnet'`) is INERT: it matches no model and no warning fires
315
+ * today. To gate a family, list its concrete ids. Beware roster
316
+ * retargets: when a family primary moves to a new id, a literal-id
317
+ * block does not follow it (tt-intel s113 built consumer-side family
318
+ * resolution for exactly this).
308
319
  */
309
320
  blockedModels?: string[];
310
321
  /**
@@ -1073,6 +1084,44 @@ interface ShadowProbeConfig {
1073
1084
  skipSlowTierInSync?: boolean;
1074
1085
  }
1075
1086
  interface CallOptions {
1087
+ /**
1088
+ * alpha.84 — fires once per FAILED attempt during the fallback walk, before
1089
+ * the walk continues. IC-Cairn's filing, 2026-07-29.
1090
+ *
1091
+ * ## The gap this closes
1092
+ *
1093
+ * `call()` records exactly ONE outcome row per call. An attempt that fails
1094
+ * and then walks to a successful fallback survives only as
1095
+ * `fellOverFrom`/`fallbackReason` metadata ON the success row — so a
1096
+ * `call()`-only consumer's corpus contains **no failure rows at all**, by
1097
+ * construction, no matter how carefully they wired `record()`.
1098
+ *
1099
+ * That is not hypothetical. kgauto's own `corpus-implausible-success`
1100
+ * detector fired on inspire-central and told them to "wire record() on the
1101
+ * FAILURE path" — which they had already done, in five places. Measured:
1102
+ * 89 rows, 0 failure rows, 2 fellover-bearing. **The detector demanded
1103
+ * output the API could not produce.** Streaming consumers have had
1104
+ * `onFailedAttempt` since alpha.48 via `streamWithFallover`; `call()`
1105
+ * consumers had nothing. This restores parity.
1106
+ *
1107
+ * ## Contract
1108
+ *
1109
+ * Fires for every attempt whose `status` is `'retryable'` or `'terminal'`,
1110
+ * including skipped ones (`*_inferred`, `aborted`) — a skip is a real
1111
+ * datum about the chain, not an absence. Never fires for `'success'`.
1112
+ *
1113
+ * Called synchronously and **its throw is swallowed**: a consumer's
1114
+ * telemetry bug must never take down the call it is observing. It is a
1115
+ * notification, not a hook that can veto or alter the walk.
1116
+ *
1117
+ * Typical use — emit a failure row per attempt so the corpus can contain
1118
+ * failures at all:
1119
+ *
1120
+ * call(ir, { onFailedAttempt: (a) => void recordOutcome({
1121
+ * ...base, success: false, errorType: a.errorCode, model: a.model,
1122
+ * }) })
1123
+ */
1124
+ onFailedAttempt?: (attempt: CallAttempt) => void;
1076
1125
  /** Forwarded to compile(). */
1077
1126
  policy?: CompilePolicy;
1078
1127
  /**
@@ -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.83";
31
+ var LIBRARY_VERSION = "2.0.0-alpha.85";
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-QEFIAAE7.mjs";
6
+ } from "./chunk-WXN7FNXP.mjs";
7
7
  export {
8
8
  KEY_FINGERPRINT_DOMAIN,
9
9
  KEY_FINGERPRINT_LENGTH,
@@ -1,4 +1,4 @@
1
- import { k as Provider } from './ir-D4S9R816.mjs';
1
+ import { k as Provider } from './ir-JUoybVM-.mjs';
2
2
  import { IntentArchetypeName } from './dialect.mjs';
3
3
 
4
4
  /**
@@ -291,6 +291,15 @@ interface ProfileBrainHook {
291
291
  }
292
292
  /** @internal — called by models-brain.ts at module load. */
293
293
  declare function _setProfileBrainHook(hook: ProfileBrainHook): void;
294
+ /**
295
+ * Resolve a model id to its canonical form (alias → canonical, unknown ids
296
+ * returned unchanged). This is the same resolution `getProfile` applies, so
297
+ * any code comparing model ids against a policy list (blockedModels,
298
+ * preferredModels) must compare canonical forms on BOTH sides — an alias and
299
+ * its canonical are the same model, and a raw-string comparison lets one
300
+ * evade a policy naming the other (alpha.85, tt-intel s119 spend-gate leak).
301
+ */
302
+ declare function resolveModelAlias(id: string): string;
294
303
  declare function getProfile(id: string): ModelProfile;
295
304
  declare function tryGetProfile(id: string): ModelProfile | undefined;
296
305
  declare function allProfiles(): readonly ModelProfile[];
@@ -299,4 +308,4 @@ declare function allProfiles(): readonly ModelProfile[];
299
308
  declare function allProfilesRaw(): readonly ModelProfile[];
300
309
  declare function profilesByProvider(provider: Provider): readonly ModelProfile[];
301
310
 
302
- export { ALIASES, type ArchetypeConvention, type CacheStrategy, type CliffRule, LATENCY_TIER_MS, type LatencyTier, type LoweringSpec, type ModelProfile, type RecoveryRule, type StructuredOutputCapability, type SystemPromptMode, _setProfileBrainHook, allProfiles, allProfilesRaw, getProfile, latencyTierOf, profilesByProvider, tryGetProfile };
311
+ export { ALIASES, type ArchetypeConvention, type CacheStrategy, type CliffRule, LATENCY_TIER_MS, type LatencyTier, type LoweringSpec, type ModelProfile, type RecoveryRule, type StructuredOutputCapability, type SystemPromptMode, _setProfileBrainHook, allProfiles, allProfilesRaw, getProfile, latencyTierOf, profilesByProvider, resolveModelAlias, tryGetProfile };
@@ -1,4 +1,4 @@
1
- import { k as Provider } from './ir-Cx9hJj0B.js';
1
+ import { k as Provider } from './ir-DfrM71pA.js';
2
2
  import { IntentArchetypeName } from './dialect.js';
3
3
 
4
4
  /**
@@ -291,6 +291,15 @@ interface ProfileBrainHook {
291
291
  }
292
292
  /** @internal — called by models-brain.ts at module load. */
293
293
  declare function _setProfileBrainHook(hook: ProfileBrainHook): void;
294
+ /**
295
+ * Resolve a model id to its canonical form (alias → canonical, unknown ids
296
+ * returned unchanged). This is the same resolution `getProfile` applies, so
297
+ * any code comparing model ids against a policy list (blockedModels,
298
+ * preferredModels) must compare canonical forms on BOTH sides — an alias and
299
+ * its canonical are the same model, and a raw-string comparison lets one
300
+ * evade a policy naming the other (alpha.85, tt-intel s119 spend-gate leak).
301
+ */
302
+ declare function resolveModelAlias(id: string): string;
294
303
  declare function getProfile(id: string): ModelProfile;
295
304
  declare function tryGetProfile(id: string): ModelProfile | undefined;
296
305
  declare function allProfiles(): readonly ModelProfile[];
@@ -299,4 +308,4 @@ declare function allProfiles(): readonly ModelProfile[];
299
308
  declare function allProfilesRaw(): readonly ModelProfile[];
300
309
  declare function profilesByProvider(provider: Provider): readonly ModelProfile[];
301
310
 
302
- export { ALIASES, type ArchetypeConvention, type CacheStrategy, type CliffRule, LATENCY_TIER_MS, type LatencyTier, type LoweringSpec, type ModelProfile, type RecoveryRule, type StructuredOutputCapability, type SystemPromptMode, _setProfileBrainHook, allProfiles, allProfilesRaw, getProfile, latencyTierOf, profilesByProvider, tryGetProfile };
311
+ export { ALIASES, type ArchetypeConvention, type CacheStrategy, type CliffRule, LATENCY_TIER_MS, type LatencyTier, type LoweringSpec, type ModelProfile, type RecoveryRule, type StructuredOutputCapability, type SystemPromptMode, _setProfileBrainHook, allProfiles, allProfilesRaw, getProfile, latencyTierOf, profilesByProvider, resolveModelAlias, tryGetProfile };
package/dist/profiles.js CHANGED
@@ -28,6 +28,7 @@ __export(profiles_exports, {
28
28
  getProfile: () => getProfile,
29
29
  latencyTierOf: () => latencyTierOf,
30
30
  profilesByProvider: () => profilesByProvider,
31
+ resolveModelAlias: () => resolveModelAlias,
31
32
  tryGetProfile: () => tryGetProfile
32
33
  });
33
34
  module.exports = __toCommonJS(profiles_exports);
@@ -1698,6 +1699,9 @@ function _setProfileBrainHook(hook) {
1698
1699
  function canonicalId(id) {
1699
1700
  return brainHook.resolveAlias?.(id) ?? ALIASES[id] ?? id;
1700
1701
  }
1702
+ function resolveModelAlias(id) {
1703
+ return canonicalId(id);
1704
+ }
1701
1705
  var PROFILE_INDEX = new Map(
1702
1706
  PROFILES_RAW.map((p) => [p.id, p])
1703
1707
  );
@@ -1735,5 +1739,6 @@ function profilesByProvider(provider) {
1735
1739
  getProfile,
1736
1740
  latencyTierOf,
1737
1741
  profilesByProvider,
1742
+ resolveModelAlias,
1738
1743
  tryGetProfile
1739
1744
  });
package/dist/profiles.mjs CHANGED
@@ -7,8 +7,9 @@ import {
7
7
  getProfile,
8
8
  latencyTierOf,
9
9
  profilesByProvider,
10
+ resolveModelAlias,
10
11
  tryGetProfile
11
- } from "./chunk-VVRDFE6T.mjs";
12
+ } from "./chunk-FD3NFXDC.mjs";
12
13
  export {
13
14
  ALIASES,
14
15
  LATENCY_TIER_MS,
@@ -18,5 +19,6 @@ export {
18
19
  getProfile,
19
20
  latencyTierOf,
20
21
  profilesByProvider,
22
+ resolveModelAlias,
21
23
  tryGetProfile
22
24
  };
@@ -1,4 +1,4 @@
1
- import { i as Adapter, F as FallbackReason, x as SectionKind } from './ir-D4S9R816.mjs';
1
+ import { i as Adapter, F as FallbackReason, x as SectionKind } from './ir-JUoybVM-.mjs';
2
2
 
3
3
  /**
4
4
  * Wire contract for the Glass-Box Chrome extension's brain-poll endpoint.
@@ -1,4 +1,4 @@
1
- import { s as MutationApplied, B as BestPracticeAdvisory, F as FallbackReason, m as CallAttempt } from './ir-Cx9hJj0B.js';
1
+ import { s as MutationApplied, B as BestPracticeAdvisory, F as FallbackReason, m as CallAttempt } from './ir-DfrM71pA.js';
2
2
 
3
3
  /**
4
4
  * Glass-Box observability types (alpha.17).
@@ -1,4 +1,4 @@
1
- import { i as Adapter, F as FallbackReason, x as SectionKind } from './ir-Cx9hJj0B.js';
1
+ import { i as Adapter, F as FallbackReason, x as SectionKind } from './ir-DfrM71pA.js';
2
2
 
3
3
  /**
4
4
  * Wire contract for the Glass-Box Chrome extension's brain-poll endpoint.
@@ -1,4 +1,4 @@
1
- import { s as MutationApplied, B as BestPracticeAdvisory, F as FallbackReason, m as CallAttempt } from './ir-D4S9R816.mjs';
1
+ import { s as MutationApplied, B as BestPracticeAdvisory, F as FallbackReason, m as CallAttempt } from './ir-JUoybVM-.mjs';
2
2
 
3
3
  /**
4
4
  * Glass-Box observability types (alpha.17).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warmdrift/kgauto-compiler",
3
- "version": "2.0.0-alpha.83",
3
+ "version": "2.0.0-alpha.85",
4
4
  "description": "Prompt compiler with executable provider knowledge for multi-model AI apps: normalized multi-provider transport with fallback chains, compile-time cliff guards, a curated model registry, and a telemetry flight recorder. Swap models without rewriting prompts.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",