@warmdrift/kgauto-compiler 2.0.0-alpha.13 → 2.0.0-alpha.15

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.mjs CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  getProfile,
18
18
  profilesByProvider,
19
19
  tryGetProfile
20
- } from "./chunk-DICCTQLG.mjs";
20
+ } from "./chunk-SFF5EVTL.mjs";
21
21
 
22
22
  // src/tokenizer.ts
23
23
  var tokenizerImpl = defaultCharBasedCounter;
@@ -139,33 +139,69 @@ function passCompressHistory(ir, opts = {}) {
139
139
  const summarizeAboveTokens = opts.summarizeAboveTokens;
140
140
  const historyTokensTotal = totalHistoryTokens(history);
141
141
  const countThresholdHit = history.length > summarizeOlderThan;
142
- const tokenThresholdHit = summarizeAboveTokens !== void 0 && historyTokensTotal > summarizeAboveTokens && history.length > keepRecent;
142
+ const tokenThresholdHit = summarizeAboveTokens !== void 0 && historyTokensTotal > summarizeAboveTokens;
143
143
  if (!countThresholdHit && !tokenThresholdHit) {
144
144
  return { value: ir, mutations: [], historyTokensTotal };
145
145
  }
146
- const cutIndex = history.length - keepRecent;
147
- const old = history.slice(0, cutIndex);
148
- const recent = history.slice(cutIndex);
149
- const userTurns = old.filter((m) => m.role === "user");
150
- const firstUserLine = userTurns[0]?.content.split("\n")[0]?.slice(0, 200) ?? "";
151
- const oldTokens = totalHistoryTokens(old);
152
- const trigger = tokenThresholdHit && !countThresholdHit ? "tokens" : "count";
153
- const summary = {
154
- role: "system",
155
- content: `[Earlier conversation: ${old.length} turns omitted (~${oldTokens} tokens). First user message: "${firstUserLine}"]`
156
- };
157
- return {
158
- value: { ...ir, history: [summary, ...recent] },
159
- mutations: [
160
- {
161
- id: `compress-history-${old.length}`,
162
- source: "static_pass",
163
- passName: "compress_history",
164
- description: trigger === "tokens" ? `Compressed ${old.length} old turns (~${oldTokens} tokens) into 1 summary \u2014 token threshold ${summarizeAboveTokens} exceeded (kept ${keepRecent} recent)` : `Compressed ${old.length} old turns into 1 summary (kept ${keepRecent} recent)`
146
+ if (history.length > keepRecent) {
147
+ const cutIndex = history.length - keepRecent;
148
+ const old = history.slice(0, cutIndex);
149
+ const recent = history.slice(cutIndex);
150
+ const userTurns = old.filter((m) => m.role === "user");
151
+ const firstUserLine = userTurns[0]?.content.split("\n")[0]?.slice(0, 200) ?? "";
152
+ const oldTokens = totalHistoryTokens(old);
153
+ const trigger = tokenThresholdHit && !countThresholdHit ? "tokens" : "count";
154
+ const summary = {
155
+ role: "system",
156
+ content: `[Earlier conversation: ${old.length} turns omitted (~${oldTokens} tokens). First user message: "${firstUserLine}"]`
157
+ };
158
+ return {
159
+ value: { ...ir, history: [summary, ...recent] },
160
+ mutations: [
161
+ {
162
+ id: `compress-history-${old.length}`,
163
+ source: "static_pass",
164
+ passName: "compress_history",
165
+ description: trigger === "tokens" ? `Compressed ${old.length} old turns (~${oldTokens} tokens) into 1 summary \u2014 token threshold ${summarizeAboveTokens} exceeded (kept ${keepRecent} recent)` : `Compressed ${old.length} old turns into 1 summary (kept ${keepRecent} recent)`
166
+ }
167
+ ],
168
+ historyTokensTotal
169
+ };
170
+ }
171
+ if (tokenThresholdHit) {
172
+ let fattestIdx = -1;
173
+ let fattestTokens = 0;
174
+ for (let i = 0; i < history.length; i++) {
175
+ const m = history[i];
176
+ if (!m || typeof m.content !== "string") continue;
177
+ const t = countTokens(m.content);
178
+ if (t > fattestTokens) {
179
+ fattestTokens = t;
180
+ fattestIdx = i;
165
181
  }
166
- ],
167
- historyTokensTotal
168
- };
182
+ }
183
+ const FAT_DOMINANCE_FLOOR = 0.3;
184
+ const fattest = fattestIdx >= 0 ? history[fattestIdx] : void 0;
185
+ if (fattest && historyTokensTotal > 0 && fattestTokens / historyTokensTotal >= FAT_DOMINANCE_FLOOR) {
186
+ const firstLine = fattest.content.split("\n")[0]?.slice(0, 200) ?? "";
187
+ const newContent = `[Earlier ${fattest.role} message content omitted: ~${fattestTokens} tokens. Preview: "${firstLine}"]`;
188
+ const newHistory = history.slice();
189
+ newHistory[fattestIdx] = { ...fattest, content: newContent };
190
+ return {
191
+ value: { ...ir, history: newHistory },
192
+ mutations: [
193
+ {
194
+ id: `compress-fat-message-${fattestIdx}`,
195
+ source: "static_pass",
196
+ passName: "compress_history",
197
+ description: `Replaced fat ${fattest.role} message #${fattestIdx} content (~${fattestTokens} of ${historyTokensTotal} tokens, ${Math.round(fattestTokens / historyTokensTotal * 100)}% of history) with summary stub \u2014 token threshold ${summarizeAboveTokens} exceeded (history.length ${history.length} <= keepRecent ${keepRecent}, slice not possible)`
198
+ }
199
+ ],
200
+ historyTokensTotal
201
+ };
202
+ }
203
+ }
204
+ return { value: ir, mutations: [], historyTokensTotal };
169
205
  }
170
206
  function passApplyCliffs(ir, profile, estimatedInputTokens) {
171
207
  const mutations = [];
@@ -1498,6 +1534,185 @@ function tryParseJson(s) {
1498
1534
  }
1499
1535
  }
1500
1536
 
1537
+ // src/chains-brain.ts
1538
+ function isChainsRow(x) {
1539
+ if (!x || typeof x !== "object") return false;
1540
+ const r = x;
1541
+ return typeof r.archetype === "string" && typeof r.tier === "number" && typeof r.model_id === "string";
1542
+ }
1543
+ function mapRowsToChains(rows) {
1544
+ const grouped = /* @__PURE__ */ new Map();
1545
+ for (const row of rows) {
1546
+ if (!isChainsRow(row)) continue;
1547
+ const list = grouped.get(row.archetype) ?? [];
1548
+ list.push(row);
1549
+ grouped.set(row.archetype, list);
1550
+ }
1551
+ const out = {};
1552
+ for (const [archetype, group] of grouped.entries()) {
1553
+ group.sort((a, b) => a.tier - b.tier);
1554
+ out[archetype] = group.map((r) => r.model_id);
1555
+ }
1556
+ const bundled = getAllStarterChains();
1557
+ for (const archetype of Object.keys(bundled)) {
1558
+ if (!out[archetype]) out[archetype] = bundled[archetype];
1559
+ }
1560
+ return out;
1561
+ }
1562
+ var loadChainsFromBrain = createBrainQueryCache({
1563
+ table: "kgauto_chains",
1564
+ mapRows: mapRowsToChains,
1565
+ bundledFallback: getAllStarterChains
1566
+ });
1567
+
1568
+ // src/fallback.ts
1569
+ var STARTER_CHAINS = {
1570
+ // Reasoning floor — never degrade. Walk UP on 429 to Opus → cross-provider.
1571
+ critique: [
1572
+ "claude-opus-4-7",
1573
+ "claude-sonnet-4-6",
1574
+ "gemini-2.5-pro"
1575
+ ],
1576
+ // Reasoning matters — Sonnet primary; walk UP to Opus on 429 (rare exception
1577
+ // to "always cheaper"); cross-provider via Pro; DeepSeek Pro as tier 3 floor.
1578
+ plan: [
1579
+ "claude-sonnet-4-6",
1580
+ "claude-opus-4-7",
1581
+ "gemini-2.5-pro",
1582
+ "deepseek-v4-pro"
1583
+ ],
1584
+ // Quality + cost match. Walk Sonnet → Haiku same-provider, Pro cross,
1585
+ // Flash floor for the open-posture chain.
1586
+ generate: [
1587
+ "claude-sonnet-4-6",
1588
+ "claude-haiku-4-5",
1589
+ "gemini-2.5-pro",
1590
+ "gemini-2.5-flash"
1591
+ ],
1592
+ ask: [
1593
+ "claude-sonnet-4-6",
1594
+ "claude-haiku-4-5",
1595
+ "gemini-2.5-pro",
1596
+ "gemini-2.5-flash"
1597
+ ],
1598
+ // Structured-output archetype — Flash skipped (alpha.8 MAX_TOKENS cliff),
1599
+ // DeepSeek skipped (no brain evidence). Floor at Haiku.
1600
+ extract: [
1601
+ "claude-sonnet-4-6",
1602
+ "claude-haiku-4-5",
1603
+ "gemini-2.5-pro"
1604
+ ],
1605
+ // Forgiving archetype — Sonnet primary but Flash safely floors it.
1606
+ transform: [
1607
+ "claude-sonnet-4-6",
1608
+ "claude-haiku-4-5",
1609
+ "gemini-2.5-pro",
1610
+ "gemini-2.5-flash"
1611
+ ],
1612
+ // Parallel-tool throughput champion (Flash, L-040). Tier 1 cross-provider
1613
+ // Pro; tier 2 Sonnet (quality safety net for blocked-Flash case); tier 3
1614
+ // Haiku (reduced tool budget — cliff at 16 fires).
1615
+ hunt: [
1616
+ "gemini-2.5-flash",
1617
+ "gemini-2.5-pro",
1618
+ "claude-sonnet-4-6",
1619
+ "claude-haiku-4-5"
1620
+ ],
1621
+ // Cost-sensitive + tolerant. DeepSeek brain-evidence tier 1; Haiku tier 2
1622
+ // for quality safety; Flash-Lite emergency floor (onboarded s22).
1623
+ summarize: [
1624
+ "gemini-2.5-flash",
1625
+ "deepseek-v4-flash",
1626
+ "claude-haiku-4-5",
1627
+ "gemini-2.5-flash-lite"
1628
+ ],
1629
+ // Brain-validated DeepSeek tier 1 (169 rows, 0% empty); Haiku tier 2;
1630
+ // Flash-Lite floor for repeat-prompt workloads (cache-discount 10×).
1631
+ classify: [
1632
+ "gemini-2.5-flash",
1633
+ "deepseek-v4-flash",
1634
+ "claude-haiku-4-5",
1635
+ "gemini-2.5-flash-lite"
1636
+ ]
1637
+ };
1638
+ function getDefaultFallbackChain(opts) {
1639
+ const { archetype, primary, maxDepth = 3, policy, reachability } = opts;
1640
+ if (maxDepth < 1) {
1641
+ throw new Error(
1642
+ `getDefaultFallbackChain: maxDepth must be >= 1, got ${maxDepth}`
1643
+ );
1644
+ }
1645
+ const allChains = loadChainsFromBrain();
1646
+ const starter = allChains[archetype];
1647
+ if (!starter) {
1648
+ throw new Error(
1649
+ `getDefaultFallbackChain: unknown archetype "${archetype}". Known: ${Object.keys(allChains).join(", ")}`
1650
+ );
1651
+ }
1652
+ let chain;
1653
+ if (primary) {
1654
+ chain = [primary, ...starter.filter((id) => id !== primary)];
1655
+ } else {
1656
+ chain = [...starter];
1657
+ }
1658
+ if (policy?.blockedModels && policy.blockedModels.length > 0) {
1659
+ const blocked = new Set(policy.blockedModels);
1660
+ chain = chain.filter((id) => !blocked.has(id));
1661
+ }
1662
+ const seen = /* @__PURE__ */ new Set();
1663
+ const deduped = [];
1664
+ for (const id of chain) {
1665
+ if (!seen.has(id)) {
1666
+ seen.add(id);
1667
+ deduped.push(id);
1668
+ }
1669
+ }
1670
+ let filtered = deduped;
1671
+ if (reachability) {
1672
+ filtered = deduped.filter((id) => isModelReachable(id, reachability));
1673
+ }
1674
+ return filtered.slice(0, maxDepth);
1675
+ }
1676
+ function getStarterChain(archetype) {
1677
+ const chain = STARTER_CHAINS[archetype];
1678
+ if (!chain) {
1679
+ throw new Error(
1680
+ `getStarterChain: unknown archetype "${archetype}"`
1681
+ );
1682
+ }
1683
+ return [...chain];
1684
+ }
1685
+ function getAllStarterChains() {
1686
+ const out = {};
1687
+ for (const [archetype, chain] of Object.entries(STARTER_CHAINS)) {
1688
+ out[archetype] = [...chain];
1689
+ }
1690
+ return out;
1691
+ }
1692
+ function ensureCrossProviderTail(opts) {
1693
+ const { chain, archetype, apiKeys, envSource } = opts;
1694
+ if (chain.length < 1) return { chain };
1695
+ const providers = /* @__PURE__ */ new Set();
1696
+ for (const t of chain) {
1697
+ const p = tryGetProfile(t);
1698
+ if (p) providers.add(p.provider);
1699
+ }
1700
+ if (providers.size >= 2) return { chain };
1701
+ const existingProvider = providers.values().next().value;
1702
+ if (!existingProvider) return { chain };
1703
+ const allChains = loadChainsFromBrain();
1704
+ const fullChain = allChains[archetype];
1705
+ if (!fullChain) return { chain };
1706
+ for (const candidate of fullChain) {
1707
+ if (chain.includes(candidate)) continue;
1708
+ const cp = tryGetProfile(candidate);
1709
+ if (!cp || cp.provider === existingProvider) continue;
1710
+ if (!isModelReachable(candidate, { apiKeys, envSource })) continue;
1711
+ return { chain: [...chain, candidate], appended: candidate };
1712
+ }
1713
+ return { chain };
1714
+ }
1715
+
1501
1716
  // src/call.ts
1502
1717
  async function call(ir, opts = {}) {
1503
1718
  const initial = compileAndRegister(ir, opts);
@@ -1543,11 +1758,33 @@ async function call(ir, opts = {}) {
1543
1758
  "no_reachable_models"
1544
1759
  );
1545
1760
  }
1761
+ const archetypeName = ir.intent?.archetype;
1762
+ if (archetypeName) {
1763
+ const ensured = ensureCrossProviderTail({
1764
+ chain: targetsToTry,
1765
+ archetype: archetypeName,
1766
+ apiKeys: opts.apiKeys
1767
+ });
1768
+ if (ensured.appended) {
1769
+ targetsToTry = ensured.chain;
1770
+ }
1771
+ }
1546
1772
  }
1547
1773
  let activeCompile = initial;
1548
1774
  let lastErr;
1775
+ const failedProviders = /* @__PURE__ */ new Set();
1549
1776
  for (let i = 0; i < targetsToTry.length; i++) {
1550
1777
  const targetModel = targetsToTry[i];
1778
+ const targetProfile = tryGetProfile(targetModel);
1779
+ if (targetProfile && failedProviders.has(targetProfile.provider) && !opts.noFallback) {
1780
+ attempts.push({
1781
+ model: targetModel,
1782
+ status: "terminal",
1783
+ errorCode: "auth_inferred",
1784
+ message: `Skipped \u2014 provider ${targetProfile.provider} returned 401/403 earlier in this call; same key inferred to fail`
1785
+ });
1786
+ continue;
1787
+ }
1551
1788
  if (targetModel !== initial.target) {
1552
1789
  try {
1553
1790
  activeCompile = compileAndRegister(
@@ -1616,6 +1853,10 @@ async function call(ir, opts = {}) {
1616
1853
  });
1617
1854
  lastErr = validated;
1618
1855
  if (validated.errorType === "terminal" || opts.noFallback) {
1856
+ if (validated.errorCode === "auth" && !opts.noFallback && activeCompile.provider) {
1857
+ failedProviders.add(activeCompile.provider);
1858
+ continue;
1859
+ }
1619
1860
  break;
1620
1861
  }
1621
1862
  }
@@ -1694,6 +1935,7 @@ function normalizeFallbackReason(attempts) {
1694
1935
  return "cliff";
1695
1936
  }
1696
1937
  if (code === "cost_cap_exceeded") return "cost_cap";
1938
+ if (code === "auth" || code === "auth_inferred") return "provider_auth_failed";
1697
1939
  return "provider_error";
1698
1940
  }
1699
1941
 
@@ -1785,162 +2027,6 @@ function clamp(n) {
1785
2027
  return Math.max(0, Math.min(1, n));
1786
2028
  }
1787
2029
 
1788
- // src/chains-brain.ts
1789
- function isChainsRow(x) {
1790
- if (!x || typeof x !== "object") return false;
1791
- const r = x;
1792
- return typeof r.archetype === "string" && typeof r.tier === "number" && typeof r.model_id === "string";
1793
- }
1794
- function mapRowsToChains(rows) {
1795
- const grouped = /* @__PURE__ */ new Map();
1796
- for (const row of rows) {
1797
- if (!isChainsRow(row)) continue;
1798
- const list = grouped.get(row.archetype) ?? [];
1799
- list.push(row);
1800
- grouped.set(row.archetype, list);
1801
- }
1802
- const out = {};
1803
- for (const [archetype, group] of grouped.entries()) {
1804
- group.sort((a, b) => a.tier - b.tier);
1805
- out[archetype] = group.map((r) => r.model_id);
1806
- }
1807
- const bundled = getAllStarterChains();
1808
- for (const archetype of Object.keys(bundled)) {
1809
- if (!out[archetype]) out[archetype] = bundled[archetype];
1810
- }
1811
- return out;
1812
- }
1813
- var loadChainsFromBrain = createBrainQueryCache({
1814
- table: "kgauto_chains",
1815
- mapRows: mapRowsToChains,
1816
- bundledFallback: getAllStarterChains
1817
- });
1818
-
1819
- // src/fallback.ts
1820
- var STARTER_CHAINS = {
1821
- // Reasoning floor — never degrade. Walk UP on 429 to Opus → cross-provider.
1822
- critique: [
1823
- "claude-opus-4-7",
1824
- "claude-sonnet-4-6",
1825
- "gemini-2.5-pro"
1826
- ],
1827
- // Reasoning matters — Sonnet primary; walk UP to Opus on 429 (rare exception
1828
- // to "always cheaper"); cross-provider via Pro; DeepSeek Pro as tier 3 floor.
1829
- plan: [
1830
- "claude-sonnet-4-6",
1831
- "claude-opus-4-7",
1832
- "gemini-2.5-pro",
1833
- "deepseek-v4-pro"
1834
- ],
1835
- // Quality + cost match. Walk Sonnet → Haiku same-provider, Pro cross,
1836
- // Flash floor for the open-posture chain.
1837
- generate: [
1838
- "claude-sonnet-4-6",
1839
- "claude-haiku-4-5",
1840
- "gemini-2.5-pro",
1841
- "gemini-2.5-flash"
1842
- ],
1843
- ask: [
1844
- "claude-sonnet-4-6",
1845
- "claude-haiku-4-5",
1846
- "gemini-2.5-pro",
1847
- "gemini-2.5-flash"
1848
- ],
1849
- // Structured-output archetype — Flash skipped (alpha.8 MAX_TOKENS cliff),
1850
- // DeepSeek skipped (no brain evidence). Floor at Haiku.
1851
- extract: [
1852
- "claude-sonnet-4-6",
1853
- "claude-haiku-4-5",
1854
- "gemini-2.5-pro"
1855
- ],
1856
- // Forgiving archetype — Sonnet primary but Flash safely floors it.
1857
- transform: [
1858
- "claude-sonnet-4-6",
1859
- "claude-haiku-4-5",
1860
- "gemini-2.5-pro",
1861
- "gemini-2.5-flash"
1862
- ],
1863
- // Parallel-tool throughput champion (Flash, L-040). Tier 1 cross-provider
1864
- // Pro; tier 2 Sonnet (quality safety net for blocked-Flash case); tier 3
1865
- // Haiku (reduced tool budget — cliff at 16 fires).
1866
- hunt: [
1867
- "gemini-2.5-flash",
1868
- "gemini-2.5-pro",
1869
- "claude-sonnet-4-6",
1870
- "claude-haiku-4-5"
1871
- ],
1872
- // Cost-sensitive + tolerant. DeepSeek brain-evidence tier 1; Haiku tier 2
1873
- // for quality safety; Flash-Lite emergency floor (onboarded s22).
1874
- summarize: [
1875
- "gemini-2.5-flash",
1876
- "deepseek-v4-flash",
1877
- "claude-haiku-4-5",
1878
- "gemini-2.5-flash-lite"
1879
- ],
1880
- // Brain-validated DeepSeek tier 1 (169 rows, 0% empty); Haiku tier 2;
1881
- // Flash-Lite floor for repeat-prompt workloads (cache-discount 10×).
1882
- classify: [
1883
- "gemini-2.5-flash",
1884
- "deepseek-v4-flash",
1885
- "claude-haiku-4-5",
1886
- "gemini-2.5-flash-lite"
1887
- ]
1888
- };
1889
- function getDefaultFallbackChain(opts) {
1890
- const { archetype, primary, maxDepth = 3, policy, reachability } = opts;
1891
- if (maxDepth < 1) {
1892
- throw new Error(
1893
- `getDefaultFallbackChain: maxDepth must be >= 1, got ${maxDepth}`
1894
- );
1895
- }
1896
- const allChains = loadChainsFromBrain();
1897
- const starter = allChains[archetype];
1898
- if (!starter) {
1899
- throw new Error(
1900
- `getDefaultFallbackChain: unknown archetype "${archetype}". Known: ${Object.keys(allChains).join(", ")}`
1901
- );
1902
- }
1903
- let chain;
1904
- if (primary) {
1905
- chain = [primary, ...starter.filter((id) => id !== primary)];
1906
- } else {
1907
- chain = [...starter];
1908
- }
1909
- if (policy?.blockedModels && policy.blockedModels.length > 0) {
1910
- const blocked = new Set(policy.blockedModels);
1911
- chain = chain.filter((id) => !blocked.has(id));
1912
- }
1913
- const seen = /* @__PURE__ */ new Set();
1914
- const deduped = [];
1915
- for (const id of chain) {
1916
- if (!seen.has(id)) {
1917
- seen.add(id);
1918
- deduped.push(id);
1919
- }
1920
- }
1921
- let filtered = deduped;
1922
- if (reachability) {
1923
- filtered = deduped.filter((id) => isModelReachable(id, reachability));
1924
- }
1925
- return filtered.slice(0, maxDepth);
1926
- }
1927
- function getStarterChain(archetype) {
1928
- const chain = STARTER_CHAINS[archetype];
1929
- if (!chain) {
1930
- throw new Error(
1931
- `getStarterChain: unknown archetype "${archetype}"`
1932
- );
1933
- }
1934
- return [...chain];
1935
- }
1936
- function getAllStarterChains() {
1937
- const out = {};
1938
- for (const [archetype, chain] of Object.entries(STARTER_CHAINS)) {
1939
- out[archetype] = [...chain];
1940
- }
1941
- return out;
1942
- }
1943
-
1944
2030
  // src/archetype-perf-brain.ts
1945
2031
  function isPerfRow(x) {
1946
2032
  if (!x || typeof x !== "object") return false;
@@ -458,15 +458,21 @@ interface CallAttempt {
458
458
  /**
459
459
  * Why fallback fired. Normalized for `CallResult.fallbackReason` (alpha.9).
460
460
  *
461
- * - `rate_limit` provider returned 429
462
- * - `provider_error` 5xx, network, or other retryable upstream issue
463
- * - `cost_cap` preflight policy.maxCostPerCallUsd rejected target
464
- * - `cliff` alpha.8 contract violation (MAX_TOKENS on
465
- * structured output, parse-failed JSON)
466
- * - `contract_violation` other compile-time-contract failures (reserved
467
- * for alpha.10+ — e.g. mid-stream policy rejects)
461
+ * - `rate_limit` provider returned 429
462
+ * - `provider_error` 5xx, network, or other retryable upstream issue
463
+ * - `cost_cap` preflight policy.maxCostPerCallUsd rejected target
464
+ * - `cliff` alpha.8 contract violation (MAX_TOKENS on
465
+ * structured output, parse-failed JSON)
466
+ * - `contract_violation` other compile-time-contract failures (reserved
467
+ * for alpha.10+ — e.g. mid-stream policy rejects)
468
+ * - `provider_auth_failed` alpha.14 — initial provider returned 401/403
469
+ * (upstream key revocation, malformed-but-truthy
470
+ * key, billing lapse). The chain walks to the
471
+ * next non-same-provider target instead of
472
+ * short-circuiting; same-provider remaining
473
+ * entries skip with errorCode='auth_inferred'.
468
474
  */
469
- type FallbackReason = 'rate_limit' | 'provider_error' | 'cost_cap' | 'cliff' | 'contract_violation';
475
+ type FallbackReason = 'rate_limit' | 'provider_error' | 'cost_cap' | 'cliff' | 'contract_violation' | 'provider_auth_failed';
470
476
  interface CallResult {
471
477
  /** Compile handle (still valid for record() if consumer wants to add oracle scores later). */
472
478
  handle: string;
@@ -458,15 +458,21 @@ interface CallAttempt {
458
458
  /**
459
459
  * Why fallback fired. Normalized for `CallResult.fallbackReason` (alpha.9).
460
460
  *
461
- * - `rate_limit` provider returned 429
462
- * - `provider_error` 5xx, network, or other retryable upstream issue
463
- * - `cost_cap` preflight policy.maxCostPerCallUsd rejected target
464
- * - `cliff` alpha.8 contract violation (MAX_TOKENS on
465
- * structured output, parse-failed JSON)
466
- * - `contract_violation` other compile-time-contract failures (reserved
467
- * for alpha.10+ — e.g. mid-stream policy rejects)
461
+ * - `rate_limit` provider returned 429
462
+ * - `provider_error` 5xx, network, or other retryable upstream issue
463
+ * - `cost_cap` preflight policy.maxCostPerCallUsd rejected target
464
+ * - `cliff` alpha.8 contract violation (MAX_TOKENS on
465
+ * structured output, parse-failed JSON)
466
+ * - `contract_violation` other compile-time-contract failures (reserved
467
+ * for alpha.10+ — e.g. mid-stream policy rejects)
468
+ * - `provider_auth_failed` alpha.14 — initial provider returned 401/403
469
+ * (upstream key revocation, malformed-but-truthy
470
+ * key, billing lapse). The chain walks to the
471
+ * next non-same-provider target instead of
472
+ * short-circuiting; same-provider remaining
473
+ * entries skip with errorCode='auth_inferred'.
468
474
  */
469
- type FallbackReason = 'rate_limit' | 'provider_error' | 'cost_cap' | 'cliff' | 'contract_violation';
475
+ type FallbackReason = 'rate_limit' | 'provider_error' | 'cost_cap' | 'cliff' | 'contract_violation' | 'provider_auth_failed';
470
476
  interface CallResult {
471
477
  /** Compile handle (still valid for record() if consumer wants to add oracle scores later). */
472
478
  handle: string;
@@ -1,2 +1,2 @@
1
- export { g as ALIASES, h as CacheStrategy, k as CliffRule, L as LoweringSpec, M as ModelProfile, q as RecoveryRule, S as StructuredOutputCapability, r as SystemPromptMode, _ as _setProfileBrainHook, t as allProfiles, x as allProfilesRaw, u as getProfile, v as profilesByProvider, w as tryGetProfile } from './profiles-B5MCp_0L.mjs';
1
+ export { g as ALIASES, h as CacheStrategy, k as CliffRule, L as LoweringSpec, M as ModelProfile, q as RecoveryRule, S as StructuredOutputCapability, r as SystemPromptMode, _ as _setProfileBrainHook, t as allProfiles, x as allProfilesRaw, u as getProfile, v as profilesByProvider, w as tryGetProfile } from './profiles-DTnIzGsA.mjs';
2
2
  import './dialect.mjs';
@@ -1,2 +1,2 @@
1
- export { g as ALIASES, h as CacheStrategy, k as CliffRule, L as LoweringSpec, M as ModelProfile, q as RecoveryRule, S as StructuredOutputCapability, r as SystemPromptMode, _ as _setProfileBrainHook, t as allProfiles, x as allProfilesRaw, u as getProfile, v as profilesByProvider, w as tryGetProfile } from './profiles-B_sMA2eU.js';
1
+ export { g as ALIASES, h as CacheStrategy, k as CliffRule, L as LoweringSpec, M as ModelProfile, q as RecoveryRule, S as StructuredOutputCapability, r as SystemPromptMode, _ as _setProfileBrainHook, t as allProfiles, x as allProfilesRaw, u as getProfile, v as profilesByProvider, w as tryGetProfile } from './profiles-D0y6aLk0.js';
2
2
  import './dialect.js';