@warmdrift/kgauto-compiler 2.0.0-alpha.14 → 2.0.0-alpha.16

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-7MTHFSNY.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,193 @@ 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
+ // alpha.16: gpt-5.5 appended as third-provider critique floor (frontier-tier,
1572
+ // archetypePerf=9). Cross-provider-tail invariant has somewhere to land when
1573
+ // both Anthropic + Google are unreachable (consumer adds only OpenAI key).
1574
+ critique: [
1575
+ "claude-opus-4-7",
1576
+ "claude-sonnet-4-6",
1577
+ "gemini-2.5-pro",
1578
+ "gpt-5.5"
1579
+ ],
1580
+ // Reasoning matters — Sonnet primary; walk UP to Opus on 429 (rare exception
1581
+ // to "always cheaper"); cross-provider via Pro; DeepSeek Pro as tier 3 floor.
1582
+ plan: [
1583
+ "claude-sonnet-4-6",
1584
+ "claude-opus-4-7",
1585
+ "gemini-2.5-pro",
1586
+ "deepseek-v4-pro"
1587
+ ],
1588
+ // Quality + cost match. Walk Sonnet → Haiku same-provider, Pro cross,
1589
+ // gpt-5.4-mini as third-provider tail (alpha.16 — closes the mono-Anthropic
1590
+ // gap when consumer has only ANTHROPIC + OPENAI keys; archetypePerf=7).
1591
+ generate: [
1592
+ "claude-sonnet-4-6",
1593
+ "claude-haiku-4-5",
1594
+ "gemini-2.5-pro",
1595
+ "gpt-5.4-mini"
1596
+ ],
1597
+ ask: [
1598
+ "claude-sonnet-4-6",
1599
+ "claude-haiku-4-5",
1600
+ "gemini-2.5-pro",
1601
+ "gpt-5.4-mini"
1602
+ ],
1603
+ // Structured-output archetype — Flash skipped (alpha.8 MAX_TOKENS cliff),
1604
+ // DeepSeek skipped (no brain evidence). Floor at Haiku. alpha.16: gpt-5.4
1605
+ // appended as third-provider extract floor (archetypePerf=8, native
1606
+ // structured-output support).
1607
+ extract: [
1608
+ "claude-sonnet-4-6",
1609
+ "claude-haiku-4-5",
1610
+ "gemini-2.5-pro",
1611
+ "gpt-5.4"
1612
+ ],
1613
+ // Forgiving archetype — Sonnet primary but Flash safely floors it.
1614
+ transform: [
1615
+ "claude-sonnet-4-6",
1616
+ "claude-haiku-4-5",
1617
+ "gemini-2.5-pro",
1618
+ "gemini-2.5-flash"
1619
+ ],
1620
+ // Parallel-tool throughput champion (Flash, L-040). Tier 1 cross-provider
1621
+ // Pro; tier 2 Sonnet (quality safety net for blocked-Flash case); tier 3
1622
+ // Haiku (reduced tool budget — cliff at 16 fires).
1623
+ hunt: [
1624
+ "gemini-2.5-flash",
1625
+ "gemini-2.5-pro",
1626
+ "claude-sonnet-4-6",
1627
+ "claude-haiku-4-5"
1628
+ ],
1629
+ // Cost-sensitive + tolerant. DeepSeek brain-evidence tier 1; Haiku tier 2
1630
+ // for quality safety; Flash-Lite emergency floor (onboarded s22).
1631
+ summarize: [
1632
+ "gemini-2.5-flash",
1633
+ "deepseek-v4-flash",
1634
+ "claude-haiku-4-5",
1635
+ "gemini-2.5-flash-lite"
1636
+ ],
1637
+ // Brain-validated DeepSeek tier 1 (169 rows, 0% empty); Haiku tier 2;
1638
+ // Flash-Lite floor for repeat-prompt workloads (cache-discount 10×).
1639
+ classify: [
1640
+ "gemini-2.5-flash",
1641
+ "deepseek-v4-flash",
1642
+ "claude-haiku-4-5",
1643
+ "gemini-2.5-flash-lite"
1644
+ ]
1645
+ };
1646
+ function getDefaultFallbackChain(opts) {
1647
+ const { archetype, primary, maxDepth = 3, policy, reachability } = opts;
1648
+ if (maxDepth < 1) {
1649
+ throw new Error(
1650
+ `getDefaultFallbackChain: maxDepth must be >= 1, got ${maxDepth}`
1651
+ );
1652
+ }
1653
+ const allChains = loadChainsFromBrain();
1654
+ const starter = allChains[archetype];
1655
+ if (!starter) {
1656
+ throw new Error(
1657
+ `getDefaultFallbackChain: unknown archetype "${archetype}". Known: ${Object.keys(allChains).join(", ")}`
1658
+ );
1659
+ }
1660
+ let chain;
1661
+ if (primary) {
1662
+ chain = [primary, ...starter.filter((id) => id !== primary)];
1663
+ } else {
1664
+ chain = [...starter];
1665
+ }
1666
+ if (policy?.blockedModels && policy.blockedModels.length > 0) {
1667
+ const blocked = new Set(policy.blockedModels);
1668
+ chain = chain.filter((id) => !blocked.has(id));
1669
+ }
1670
+ const seen = /* @__PURE__ */ new Set();
1671
+ const deduped = [];
1672
+ for (const id of chain) {
1673
+ if (!seen.has(id)) {
1674
+ seen.add(id);
1675
+ deduped.push(id);
1676
+ }
1677
+ }
1678
+ let filtered = deduped;
1679
+ if (reachability) {
1680
+ filtered = deduped.filter((id) => isModelReachable(id, reachability));
1681
+ }
1682
+ return filtered.slice(0, maxDepth);
1683
+ }
1684
+ function getStarterChain(archetype) {
1685
+ const chain = STARTER_CHAINS[archetype];
1686
+ if (!chain) {
1687
+ throw new Error(
1688
+ `getStarterChain: unknown archetype "${archetype}"`
1689
+ );
1690
+ }
1691
+ return [...chain];
1692
+ }
1693
+ function getAllStarterChains() {
1694
+ const out = {};
1695
+ for (const [archetype, chain] of Object.entries(STARTER_CHAINS)) {
1696
+ out[archetype] = [...chain];
1697
+ }
1698
+ return out;
1699
+ }
1700
+ function ensureCrossProviderTail(opts) {
1701
+ const { chain, archetype, apiKeys, envSource } = opts;
1702
+ if (chain.length < 1) return { chain };
1703
+ const providers = /* @__PURE__ */ new Set();
1704
+ for (const t of chain) {
1705
+ const p = tryGetProfile(t);
1706
+ if (p) providers.add(p.provider);
1707
+ }
1708
+ if (providers.size >= 2) return { chain };
1709
+ const existingProvider = providers.values().next().value;
1710
+ if (!existingProvider) return { chain };
1711
+ const allChains = loadChainsFromBrain();
1712
+ const fullChain = allChains[archetype];
1713
+ if (!fullChain) return { chain };
1714
+ for (const candidate of fullChain) {
1715
+ if (chain.includes(candidate)) continue;
1716
+ const cp = tryGetProfile(candidate);
1717
+ if (!cp || cp.provider === existingProvider) continue;
1718
+ if (!isModelReachable(candidate, { apiKeys, envSource })) continue;
1719
+ return { chain: [...chain, candidate], appended: candidate };
1720
+ }
1721
+ return { chain };
1722
+ }
1723
+
1501
1724
  // src/call.ts
1502
1725
  async function call(ir, opts = {}) {
1503
1726
  const initial = compileAndRegister(ir, opts);
@@ -1543,6 +1766,58 @@ async function call(ir, opts = {}) {
1543
1766
  "no_reachable_models"
1544
1767
  );
1545
1768
  }
1769
+ const archetypeName = ir.intent?.archetype;
1770
+ if (archetypeName) {
1771
+ const ensured = ensureCrossProviderTail({
1772
+ chain: targetsToTry,
1773
+ archetype: archetypeName,
1774
+ apiKeys: opts.apiKeys
1775
+ });
1776
+ if (ensured.appended) {
1777
+ targetsToTry = ensured.chain;
1778
+ }
1779
+ }
1780
+ }
1781
+ let policyBlockedFiltered;
1782
+ if (opts.policy?.blockedModels && opts.policy.blockedModels.length > 0) {
1783
+ const blocked = new Set(opts.policy.blockedModels);
1784
+ const filtered = [];
1785
+ const dropped = [];
1786
+ for (const t of targetsToTry) {
1787
+ if (blocked.has(t)) {
1788
+ dropped.push(t);
1789
+ } else {
1790
+ filtered.push(t);
1791
+ }
1792
+ }
1793
+ if (dropped.length > 0) {
1794
+ policyBlockedFiltered = dropped;
1795
+ targetsToTry = filtered;
1796
+ }
1797
+ if (targetsToTry.length === 0) {
1798
+ const latencyMs2 = Date.now() - start;
1799
+ await record({
1800
+ handle: initial.handle,
1801
+ tokensIn: 0,
1802
+ tokensOut: 0,
1803
+ latencyMs: latencyMs2,
1804
+ success: false,
1805
+ errorType: "all_blocked_by_policy",
1806
+ promptPreview: extractPromptPreview(ir)
1807
+ });
1808
+ const blockedAttempts = dropped.map((m) => ({
1809
+ model: m,
1810
+ status: "terminal",
1811
+ errorCode: "blocked_by_policy",
1812
+ message: `Skipped \u2014 model ${m} is in CompilePolicy.blockedModels`
1813
+ }));
1814
+ throw new CallError(
1815
+ `call(): all chain targets blocked by CompilePolicy.blockedModels: [${dropped.join(", ")}]`,
1816
+ blockedAttempts,
1817
+ void 0,
1818
+ "all_blocked_by_policy"
1819
+ );
1820
+ }
1546
1821
  }
1547
1822
  let activeCompile = initial;
1548
1823
  let lastErr;
@@ -1616,7 +1891,8 @@ async function call(ir, opts = {}) {
1616
1891
  servedBy: targetModel,
1617
1892
  fellOverFrom: fellOver ? initial.target : void 0,
1618
1893
  fallbackReason: fellOver ? normalizeFallbackReason(attempts) : void 0,
1619
- unreachableFiltered
1894
+ unreachableFiltered,
1895
+ policyBlockedFiltered
1620
1896
  };
1621
1897
  }
1622
1898
  attempts.push({
@@ -1645,8 +1921,9 @@ async function call(ir, opts = {}) {
1645
1921
  promptPreview: extractPromptPreview(ir)
1646
1922
  });
1647
1923
  const filteredNote = unreachableFiltered && unreachableFiltered.length > 0 ? ` (also auto-filtered: [${unreachableFiltered.join(", ")}] \u2014 no API key)` : "";
1924
+ const blockedNote = policyBlockedFiltered && policyBlockedFiltered.length > 0 ? ` (also policy-blocked: [${policyBlockedFiltered.join(", ")}])` : "";
1648
1925
  throw new CallError(
1649
- `call(): all attempts failed${lastErr ? ` \u2014 ${lastErr.errorCode}: ${lastErr.message}` : ""}${filteredNote}`,
1926
+ `call(): all attempts failed${lastErr ? ` \u2014 ${lastErr.errorCode}: ${lastErr.message}` : ""}${filteredNote}${blockedNote}`,
1650
1927
  attempts,
1651
1928
  lastErr?.status,
1652
1929
  lastErr?.errorCode
@@ -1801,162 +2078,6 @@ function clamp(n) {
1801
2078
  return Math.max(0, Math.min(1, n));
1802
2079
  }
1803
2080
 
1804
- // src/chains-brain.ts
1805
- function isChainsRow(x) {
1806
- if (!x || typeof x !== "object") return false;
1807
- const r = x;
1808
- return typeof r.archetype === "string" && typeof r.tier === "number" && typeof r.model_id === "string";
1809
- }
1810
- function mapRowsToChains(rows) {
1811
- const grouped = /* @__PURE__ */ new Map();
1812
- for (const row of rows) {
1813
- if (!isChainsRow(row)) continue;
1814
- const list = grouped.get(row.archetype) ?? [];
1815
- list.push(row);
1816
- grouped.set(row.archetype, list);
1817
- }
1818
- const out = {};
1819
- for (const [archetype, group] of grouped.entries()) {
1820
- group.sort((a, b) => a.tier - b.tier);
1821
- out[archetype] = group.map((r) => r.model_id);
1822
- }
1823
- const bundled = getAllStarterChains();
1824
- for (const archetype of Object.keys(bundled)) {
1825
- if (!out[archetype]) out[archetype] = bundled[archetype];
1826
- }
1827
- return out;
1828
- }
1829
- var loadChainsFromBrain = createBrainQueryCache({
1830
- table: "kgauto_chains",
1831
- mapRows: mapRowsToChains,
1832
- bundledFallback: getAllStarterChains
1833
- });
1834
-
1835
- // src/fallback.ts
1836
- var STARTER_CHAINS = {
1837
- // Reasoning floor — never degrade. Walk UP on 429 to Opus → cross-provider.
1838
- critique: [
1839
- "claude-opus-4-7",
1840
- "claude-sonnet-4-6",
1841
- "gemini-2.5-pro"
1842
- ],
1843
- // Reasoning matters — Sonnet primary; walk UP to Opus on 429 (rare exception
1844
- // to "always cheaper"); cross-provider via Pro; DeepSeek Pro as tier 3 floor.
1845
- plan: [
1846
- "claude-sonnet-4-6",
1847
- "claude-opus-4-7",
1848
- "gemini-2.5-pro",
1849
- "deepseek-v4-pro"
1850
- ],
1851
- // Quality + cost match. Walk Sonnet → Haiku same-provider, Pro cross,
1852
- // Flash floor for the open-posture chain.
1853
- generate: [
1854
- "claude-sonnet-4-6",
1855
- "claude-haiku-4-5",
1856
- "gemini-2.5-pro",
1857
- "gemini-2.5-flash"
1858
- ],
1859
- ask: [
1860
- "claude-sonnet-4-6",
1861
- "claude-haiku-4-5",
1862
- "gemini-2.5-pro",
1863
- "gemini-2.5-flash"
1864
- ],
1865
- // Structured-output archetype — Flash skipped (alpha.8 MAX_TOKENS cliff),
1866
- // DeepSeek skipped (no brain evidence). Floor at Haiku.
1867
- extract: [
1868
- "claude-sonnet-4-6",
1869
- "claude-haiku-4-5",
1870
- "gemini-2.5-pro"
1871
- ],
1872
- // Forgiving archetype — Sonnet primary but Flash safely floors it.
1873
- transform: [
1874
- "claude-sonnet-4-6",
1875
- "claude-haiku-4-5",
1876
- "gemini-2.5-pro",
1877
- "gemini-2.5-flash"
1878
- ],
1879
- // Parallel-tool throughput champion (Flash, L-040). Tier 1 cross-provider
1880
- // Pro; tier 2 Sonnet (quality safety net for blocked-Flash case); tier 3
1881
- // Haiku (reduced tool budget — cliff at 16 fires).
1882
- hunt: [
1883
- "gemini-2.5-flash",
1884
- "gemini-2.5-pro",
1885
- "claude-sonnet-4-6",
1886
- "claude-haiku-4-5"
1887
- ],
1888
- // Cost-sensitive + tolerant. DeepSeek brain-evidence tier 1; Haiku tier 2
1889
- // for quality safety; Flash-Lite emergency floor (onboarded s22).
1890
- summarize: [
1891
- "gemini-2.5-flash",
1892
- "deepseek-v4-flash",
1893
- "claude-haiku-4-5",
1894
- "gemini-2.5-flash-lite"
1895
- ],
1896
- // Brain-validated DeepSeek tier 1 (169 rows, 0% empty); Haiku tier 2;
1897
- // Flash-Lite floor for repeat-prompt workloads (cache-discount 10×).
1898
- classify: [
1899
- "gemini-2.5-flash",
1900
- "deepseek-v4-flash",
1901
- "claude-haiku-4-5",
1902
- "gemini-2.5-flash-lite"
1903
- ]
1904
- };
1905
- function getDefaultFallbackChain(opts) {
1906
- const { archetype, primary, maxDepth = 3, policy, reachability } = opts;
1907
- if (maxDepth < 1) {
1908
- throw new Error(
1909
- `getDefaultFallbackChain: maxDepth must be >= 1, got ${maxDepth}`
1910
- );
1911
- }
1912
- const allChains = loadChainsFromBrain();
1913
- const starter = allChains[archetype];
1914
- if (!starter) {
1915
- throw new Error(
1916
- `getDefaultFallbackChain: unknown archetype "${archetype}". Known: ${Object.keys(allChains).join(", ")}`
1917
- );
1918
- }
1919
- let chain;
1920
- if (primary) {
1921
- chain = [primary, ...starter.filter((id) => id !== primary)];
1922
- } else {
1923
- chain = [...starter];
1924
- }
1925
- if (policy?.blockedModels && policy.blockedModels.length > 0) {
1926
- const blocked = new Set(policy.blockedModels);
1927
- chain = chain.filter((id) => !blocked.has(id));
1928
- }
1929
- const seen = /* @__PURE__ */ new Set();
1930
- const deduped = [];
1931
- for (const id of chain) {
1932
- if (!seen.has(id)) {
1933
- seen.add(id);
1934
- deduped.push(id);
1935
- }
1936
- }
1937
- let filtered = deduped;
1938
- if (reachability) {
1939
- filtered = deduped.filter((id) => isModelReachable(id, reachability));
1940
- }
1941
- return filtered.slice(0, maxDepth);
1942
- }
1943
- function getStarterChain(archetype) {
1944
- const chain = STARTER_CHAINS[archetype];
1945
- if (!chain) {
1946
- throw new Error(
1947
- `getStarterChain: unknown archetype "${archetype}"`
1948
- );
1949
- }
1950
- return [...chain];
1951
- }
1952
- function getAllStarterChains() {
1953
- const out = {};
1954
- for (const [archetype, chain] of Object.entries(STARTER_CHAINS)) {
1955
- out[archetype] = [...chain];
1956
- }
1957
- return out;
1958
- }
1959
-
1960
2081
  // src/archetype-perf-brain.ts
1961
2082
  function isPerfRow(x) {
1962
2083
  if (!x || typeof x !== "object") return false;
@@ -521,6 +521,23 @@ interface CallResult {
521
521
  * when `noAutoFilter: true`).
522
522
  */
523
523
  unreachableFiltered?: string[];
524
+ /**
525
+ * alpha.16. Models that policy.blockedModels filtering dropped from the
526
+ * fallback walk. Defense-in-depth at the call() boundary — compile()'s
527
+ * passScoreTargets already excludes blocked entries from the initial
528
+ * target + fallbackChain, but if a consumer re-shapes the chain and
529
+ * threads policy through only partially, this filter catches the gap.
530
+ *
531
+ * Resolves TT-40 follow-on `policy-block-not-enforced-on-fallback-chain`
532
+ * (2026-05-15) where mutations_applied recorded the block intent but
533
+ * the call walker landed on the blocked model anyway.
534
+ *
535
+ * Undefined when no filter ran (no blockedModels set). Populated only
536
+ * when filter ran AND dropped at least one entry — empty drops are
537
+ * stored as `undefined` to keep brain telemetry quiet on the common
538
+ * case.
539
+ */
540
+ policyBlockedFiltered?: string[];
524
541
  }
525
542
  /**
526
543
  * Thrown when call() exhausts the fallback chain without success.
@@ -521,6 +521,23 @@ interface CallResult {
521
521
  * when `noAutoFilter: true`).
522
522
  */
523
523
  unreachableFiltered?: string[];
524
+ /**
525
+ * alpha.16. Models that policy.blockedModels filtering dropped from the
526
+ * fallback walk. Defense-in-depth at the call() boundary — compile()'s
527
+ * passScoreTargets already excludes blocked entries from the initial
528
+ * target + fallbackChain, but if a consumer re-shapes the chain and
529
+ * threads policy through only partially, this filter catches the gap.
530
+ *
531
+ * Resolves TT-40 follow-on `policy-block-not-enforced-on-fallback-chain`
532
+ * (2026-05-15) where mutations_applied recorded the block intent but
533
+ * the call walker landed on the blocked model anyway.
534
+ *
535
+ * Undefined when no filter ran (no blockedModels set). Populated only
536
+ * when filter ran AND dropped at least one entry — empty drops are
537
+ * stored as `undefined` to keep brain telemetry quiet on the common
538
+ * case.
539
+ */
540
+ policyBlockedFiltered?: string[];
524
541
  }
525
542
  /**
526
543
  * Thrown when call() exhausts the fallback chain without success.
@@ -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-DTnIzGsA.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-BoLYdl7F.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-D0y6aLk0.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-CVB2_5C8.js';
2
2
  import './dialect.js';