@warmdrift/kgauto-compiler 2.0.0-alpha.96 → 2.0.0-alpha.98

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.
@@ -2,7 +2,7 @@ import {
2
2
  allProfiles,
3
3
  resolveModelAlias,
4
4
  tryGetProfile
5
- } from "./chunk-AMV3M2KH.mjs";
5
+ } from "./chunk-HHIXHL4O.mjs";
6
6
 
7
7
  // src/brain-query.ts
8
8
  var FRESH_SNAPSHOT = {
@@ -182,7 +182,7 @@ function mapPerAxisMetrics(raw, fallbackAppId, fallbackArchetype, fallbackModel,
182
182
  var ARCHETYPE_FLOOR_DEFAULT = 6;
183
183
  var ABSOLUTE_FLOOR = 4;
184
184
  function rawArchetypePerf(profile, archetype) {
185
- return profile.archetypePerf?.[archetype] ?? 5;
185
+ return profile.archetypePerf?.[archetype] ?? ARCHETYPE_FLOOR_DEFAULT;
186
186
  }
187
187
  function hasSequentialToolCliffForHunt(profile) {
188
188
  if (profile.parallelToolCalls !== false) return false;
@@ -1914,6 +1914,19 @@ var PROFILES_RAW = [
1914
1914
  id: "glm-5.2",
1915
1915
  verifiedAgainstDocs: "2026-07-18",
1916
1916
  provider: "zai",
1917
+ probedLimits: [
1918
+ {
1919
+ metric: "tool_count",
1920
+ testedAt: 6,
1921
+ demanded: 3,
1922
+ served: [3, 3, 3],
1923
+ outcome: "held",
1924
+ trials: 3,
1925
+ lastProbed: "2026-08-29",
1926
+ route: "openrouter:baidu",
1927
+ claim: "Measured 2026-08-29 via OpenRouter (served-by Baidu): 6 tools offered, 3 of 3 demanded served in parallel, 3/3 trials. Route-scoped \u2014 direct z.ai is unmeasured (no key held)."
1928
+ }
1929
+ ],
1917
1930
  status: "preview",
1918
1931
  maxContextTokens: 1048576,
1919
1932
  maxOutputTokens: 131072,
@@ -1924,7 +1937,8 @@ var PROFILES_RAW = [
1924
1937
  // NOTHING about multiple tool_calls per response. Conservative false so
1925
1938
  // hunt-shaped ranking doesn't over-promise; flip on doc or probe
1926
1939
  // evidence (L-081: undocumented ≠ true).
1927
- parallelToolCalls: false,
1940
+ parallelToolCalls: true,
1941
+ // alpha.96 — see weaknesses note; was an unevidenced blanket false
1928
1942
  structuredOutput: "native",
1929
1943
  systemPromptMode: "inline",
1930
1944
  streaming: true,
@@ -1947,7 +1961,17 @@ var PROFILES_RAW = [
1947
1961
  { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
1948
1962
  ],
1949
1963
  strengths: ["cost", "1m_context", "code", "tool_use", "json_output", "reasoning"],
1950
- weaknesses: ["parallel_tools"],
1964
+ // alpha.96 — 'parallel_tools' REMOVED. Measured 2026-08-29 via OpenRouter
1965
+ // (served-by Baidu), 6 tools offered, 3 of 3 demanded served in PARALLEL,
1966
+ // 3/3 trials. ROUTE-SCOPED: per the alpha.92 execute-leg contract there is
1967
+ // no cross-route verdict transfer, and direct z.ai is UNMEASURED (we hold
1968
+ // no zai key). But `parallelToolCalls: false` was a blanket claim with no
1969
+ // route qualification and no evidence at all, and it is capability-
1970
+ // REDUCING — it drives requires-adapter/reject. An unsubstantiated reducer
1971
+ // must not reduce. Contradicted on the one route we can reach is enough to
1972
+ // stop enforcing it; it is not enough to claim the opposite, which is why
1973
+ // probedLimits below carries the route.
1974
+ weaknesses: [],
1951
1975
  notes: "GLM-5.2 (Z.ai, 2026-06): agentic-coding flagship. 1,048,576 ctx / 131,072 max out. $1.40/$4.40 per 1M; cached input $0.26/M (automatic caching, no marker; storage limited-time free as of 2026-07-18). Thinking on by default (`thinking.type`), `reasoning_effort` max..none. Parallel tool calls UNDOCUMENTED on the hosted API \u2014 profile says false until doc or probe evidence. status:preview \u2014 no brain evidence yet; earns placement via the machinery.",
1952
1976
  // Starter hypotheses — verify with telemetry/probes; NO brain evidence
1953
1977
  // yet. Anchored on the vendor's agentic-coding positioning at a
@@ -1965,8 +1989,11 @@ var PROFILES_RAW = [
1965
1989
  extract: 6,
1966
1990
  critique: 6,
1967
1991
  classify: 6,
1968
- hunt: 5
1969
- // tools work; parallel unproven discounted
1992
+ // alpha.96 — was 5, discounted for "parallel unproven". Parallel is no
1993
+ // longer unproven on the reachable route (3/3, 2026-08-29). The discount
1994
+ // had no other stated basis, so it is withdrawn to the neutral floor.
1995
+ // Judgment, not measurement — same standard as the DeepSeek correction.
1996
+ hunt: 6
1970
1997
  }
1971
1998
  },
1972
1999
  // ── Moonshot (Kimi) ──
@@ -2099,7 +2126,24 @@ function bestEffortProfile(id) {
2099
2126
  maxContextTokens: 128e3,
2100
2127
  maxOutputTokens: 4096,
2101
2128
  maxTools: donor.maxTools,
2102
- parallelToolCalls: false,
2129
+ // alpha.97 — was `false`. That was the SAME MISTAKE as the three profiles
2130
+ // corrected in alpha.96, in the one place it does the most damage: applied
2131
+ // to models nobody has measured at all.
2132
+ //
2133
+ // `false` here is capability-REDUCING and unsubstantiated by construction —
2134
+ // a synthesized profile exists precisely because we know nothing. It made
2135
+ // `getModelCompatibility(<any unprofiled model>, {archetype:'hunt'})`
2136
+ // return `requires-adapter` (verified: raw score 5 vs floor 6, adapter
2137
+ // offered), so every consumer on `onUnprofiledModel:'best-effort'` was told
2138
+ // to serialize tool calls on a model we had never touched.
2139
+ //
2140
+ // The never-worse-than-raw rule decides it: for an unknown model kgauto
2141
+ // must behave exactly as a direct call would, and a direct call passes
2142
+ // every tool and lets the provider default apply. `true` is not a claim
2143
+ // that the model parallelises — it is the absence of a claim, expressed as
2144
+ // non-interference. The honest long-term fix is a third state (unknown)
2145
+ // rather than a boolean; that is P4 in the rebuild contract.
2146
+ parallelToolCalls: true,
2103
2147
  structuredOutput: "none",
2104
2148
  systemPromptMode: donor.systemPromptMode,
2105
2149
  streaming: donor.streaming,
@@ -2109,7 +2153,7 @@ function bestEffortProfile(id) {
2109
2153
  lowering: donor.lowering,
2110
2154
  recovery: [],
2111
2155
  strengths: [],
2112
- weaknesses: ["best-effort synthesized profile \u2014 no verified capability data"],
2156
+ weaknesses: ["best-effort synthesized profile \u2014 no verified capability data; kgauto does not reduce capability for a model it has never measured (alpha.97)"],
2113
2157
  notes: `Synthesized by onUnprofiledModel:'best-effort' (provider ${provider} inferred from id; wire mechanics borrowed from ${donor.id}). No cliffs, no measured knowledge, cost UNKNOWN (recorded as 0). Replace via registerProfiles() for real guards.`
2114
2158
  };
2115
2159
  CONSUMER_INDEX.set(canonical, synth);
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var LIBRARY_VERSION = "2.0.0-alpha.96";
2
+ var LIBRARY_VERSION = "2.0.0-alpha.98";
3
3
 
4
4
  // src/key-health.ts
5
5
  var JSON_HEADERS = { "Content-Type": "application/json" };
@@ -9,7 +9,7 @@ import {
9
9
  getModelCompatibility,
10
10
  isBrainQueryActiveFor,
11
11
  policySetHas
12
- } from "./chunk-2D4H7HGZ.mjs";
12
+ } from "./chunk-77AHSX6Y.mjs";
13
13
  import {
14
14
  bucketContext,
15
15
  bucketHistory,
@@ -30,7 +30,7 @@ import {
30
30
  latencyTierOf,
31
31
  resolveModelAlias,
32
32
  tryGetProfile
33
- } from "./chunk-AMV3M2KH.mjs";
33
+ } from "./chunk-HHIXHL4O.mjs";
34
34
 
35
35
  // src/models-brain.ts
36
36
  function isModelRow(x) {
@@ -1146,12 +1146,19 @@ function mapRows(rows) {
1146
1146
  if (!isRawFailureRow(row)) continue;
1147
1147
  const n = coerceCount(row.n);
1148
1148
  const nFail = coerceCount(row.n_fail) ?? 0;
1149
- if (n === null || n <= 0) continue;
1149
+ const nAttempted = coerceCount(row.n_attempted);
1150
+ const nUnavailable = coerceCount(row.n_unavailable) ?? 0;
1151
+ const nUnclassified = coerceCount(row.n_unclassified) ?? 0;
1152
+ if (n === null) continue;
1153
+ if (n <= 0 && nUnavailable <= 0) continue;
1150
1154
  out.push({
1151
1155
  archetype: row.intent_archetype,
1152
1156
  model: row.model,
1153
1157
  n,
1154
- nFail
1158
+ nFail,
1159
+ nAttempted: nAttempted ?? n,
1160
+ nUnavailable,
1161
+ nUnclassified
1155
1162
  });
1156
1163
  }
1157
1164
  return out;
@@ -1175,7 +1182,24 @@ var MEASURED_FAILURE_CFG = {
1175
1182
  /** 95% one-sided-ish confidence (standard two-sided z at α=0.05). */
1176
1183
  z: 1.96,
1177
1184
  /** Must match the view's window. Documented here for the advisory text. */
1178
- windowDays: 28
1185
+ windowDays: 28,
1186
+ /**
1187
+ * alpha.98 — the AVAILABILITY axis. Same statistic, separate vocabulary,
1188
+ * separate verdict, because a 429 and a parse-failure send an operator in
1189
+ * opposite directions (fund or throttle the account vs. distrust the
1190
+ * model's declared structuredOutput). Merging them into one "failure rate"
1191
+ * is the s75 normalization defect.
1192
+ *
1193
+ * Thresholds match the quality axis deliberately: "we are 95% confident
1194
+ * this model is unreachable more often than not" is the same unarguable
1195
+ * bar, and a de-rank is the same remedy. Kept as a distinct config block
1196
+ * so the two can be tuned apart when evidence says they should be.
1197
+ */
1198
+ availability: {
1199
+ minSample: 5,
1200
+ lowerBoundThreshold: 0.5,
1201
+ z: 1.96
1202
+ }
1179
1203
  };
1180
1204
  function wilsonLowerBound(failures, n, z = MEASURED_FAILURE_CFG.z) {
1181
1205
  if (n <= 0) return 0;
@@ -1192,15 +1216,53 @@ function mapMeasuredFailureRows(rows) {
1192
1216
  }
1193
1217
  function judgeMeasuredFailure(row, cfg = MEASURED_FAILURE_CFG) {
1194
1218
  if (!row) return void 0;
1195
- const normalized = "nFail" in row && typeof row.n === "number" ? row : mapRows([row])[0];
1196
- if (!normalized || normalized.n < cfg.minSample) return void 0;
1197
- const lowerBound = wilsonLowerBound(normalized.nFail, normalized.n, cfg.z);
1219
+ const raw = "nFail" in row && typeof row.n === "number" ? row : mapRows([row])[0];
1220
+ if (!raw) return void 0;
1221
+ const normalized = {
1222
+ ...raw,
1223
+ nAttempted: typeof raw.nAttempted === "number" ? raw.nAttempted : raw.n,
1224
+ nUnavailable: typeof raw.nUnavailable === "number" ? raw.nUnavailable : 0,
1225
+ nUnclassified: typeof raw.nUnclassified === "number" ? raw.nUnclassified : 0
1226
+ };
1227
+ const quality = judgeAxis(
1228
+ normalized.nFail,
1229
+ normalized.n,
1230
+ cfg.minSample,
1231
+ cfg.lowerBoundThreshold,
1232
+ cfg.z
1233
+ );
1234
+ const availability = judgeAxis(
1235
+ normalized.nUnavailable,
1236
+ normalized.nAttempted,
1237
+ cfg.availability.minSample,
1238
+ cfg.availability.lowerBoundThreshold,
1239
+ cfg.availability.z
1240
+ );
1241
+ if (normalized.n < cfg.minSample && normalized.nAttempted < cfg.availability.minSample) {
1242
+ return void 0;
1243
+ }
1244
+ const axis = quality.gated ? "quality" : availability.gated ? "availability" : void 0;
1245
+ const lead = axis === "availability" ? availability : quality;
1198
1246
  return {
1199
- gated: lowerBound > cfg.lowerBoundThreshold,
1200
- rate: normalized.nFail / normalized.n,
1247
+ gated: quality.gated || availability.gated,
1248
+ axis,
1249
+ rate: lead.rate,
1250
+ lowerBound: lead.lowerBound,
1251
+ n: lead.n,
1252
+ nFail: lead.nFail,
1253
+ quality,
1254
+ availability,
1255
+ nUnclassified: normalized.nUnclassified
1256
+ };
1257
+ }
1258
+ function judgeAxis(failures, n, minSample, threshold, z) {
1259
+ const lowerBound = wilsonLowerBound(failures, n, z);
1260
+ return {
1261
+ rate: n > 0 ? failures / n : 0,
1201
1262
  lowerBound,
1202
- n: normalized.n,
1203
- nFail: normalized.nFail
1263
+ n,
1264
+ nFail: failures,
1265
+ gated: n >= minSample && lowerBound > threshold
1204
1266
  };
1205
1267
  }
1206
1268
  var snapshots2 = /* @__PURE__ */ new Map();
@@ -2242,9 +2304,13 @@ function detectSingleModelArray(ir, policy) {
2242
2304
  ];
2243
2305
  }
2244
2306
  function suppressedRecommendationReason(ir, archetype, altProfile) {
2245
- if (getMeasuredFailureVerdict({ appId: ir.appId, archetype, model: altProfile.id })?.gated === true) {
2246
- return "measured-failure-gate";
2247
- }
2307
+ const measured = getMeasuredFailureVerdict({
2308
+ appId: ir.appId,
2309
+ archetype,
2310
+ model: altProfile.id
2311
+ });
2312
+ if (measured?.quality.gated === true) return "measured-failure-gate";
2313
+ if (measured?.availability.gated === true) return "measured-unavailable-gate";
2248
2314
  if (ir.constraints?.structuredOutput && effectiveConventions(altProfile).some(
2249
2315
  (c) => c.archetype === archetype && c.structuredOutputHint === "avoid"
2250
2316
  )) {
@@ -3057,7 +3123,8 @@ function compile(ir, opts = {}) {
3057
3123
  rate: verdict.rate,
3058
3124
  lowerBound: verdict.lowerBound,
3059
3125
  n: verdict.n,
3060
- nFail: verdict.nFail
3126
+ nFail: verdict.nFail,
3127
+ axis: verdict.axis ?? "quality"
3061
3128
  });
3062
3129
  }
3063
3130
  }
@@ -1959,6 +1959,19 @@ var PROFILES_RAW = [
1959
1959
  id: "glm-5.2",
1960
1960
  verifiedAgainstDocs: "2026-07-18",
1961
1961
  provider: "zai",
1962
+ probedLimits: [
1963
+ {
1964
+ metric: "tool_count",
1965
+ testedAt: 6,
1966
+ demanded: 3,
1967
+ served: [3, 3, 3],
1968
+ outcome: "held",
1969
+ trials: 3,
1970
+ lastProbed: "2026-08-29",
1971
+ route: "openrouter:baidu",
1972
+ claim: "Measured 2026-08-29 via OpenRouter (served-by Baidu): 6 tools offered, 3 of 3 demanded served in parallel, 3/3 trials. Route-scoped \u2014 direct z.ai is unmeasured (no key held)."
1973
+ }
1974
+ ],
1962
1975
  status: "preview",
1963
1976
  maxContextTokens: 1048576,
1964
1977
  maxOutputTokens: 131072,
@@ -1969,7 +1982,8 @@ var PROFILES_RAW = [
1969
1982
  // NOTHING about multiple tool_calls per response. Conservative false so
1970
1983
  // hunt-shaped ranking doesn't over-promise; flip on doc or probe
1971
1984
  // evidence (L-081: undocumented ≠ true).
1972
- parallelToolCalls: false,
1985
+ parallelToolCalls: true,
1986
+ // alpha.96 — see weaknesses note; was an unevidenced blanket false
1973
1987
  structuredOutput: "native",
1974
1988
  systemPromptMode: "inline",
1975
1989
  streaming: true,
@@ -1992,7 +2006,17 @@ var PROFILES_RAW = [
1992
2006
  { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
1993
2007
  ],
1994
2008
  strengths: ["cost", "1m_context", "code", "tool_use", "json_output", "reasoning"],
1995
- weaknesses: ["parallel_tools"],
2009
+ // alpha.96 — 'parallel_tools' REMOVED. Measured 2026-08-29 via OpenRouter
2010
+ // (served-by Baidu), 6 tools offered, 3 of 3 demanded served in PARALLEL,
2011
+ // 3/3 trials. ROUTE-SCOPED: per the alpha.92 execute-leg contract there is
2012
+ // no cross-route verdict transfer, and direct z.ai is UNMEASURED (we hold
2013
+ // no zai key). But `parallelToolCalls: false` was a blanket claim with no
2014
+ // route qualification and no evidence at all, and it is capability-
2015
+ // REDUCING — it drives requires-adapter/reject. An unsubstantiated reducer
2016
+ // must not reduce. Contradicted on the one route we can reach is enough to
2017
+ // stop enforcing it; it is not enough to claim the opposite, which is why
2018
+ // probedLimits below carries the route.
2019
+ weaknesses: [],
1996
2020
  notes: "GLM-5.2 (Z.ai, 2026-06): agentic-coding flagship. 1,048,576 ctx / 131,072 max out. $1.40/$4.40 per 1M; cached input $0.26/M (automatic caching, no marker; storage limited-time free as of 2026-07-18). Thinking on by default (`thinking.type`), `reasoning_effort` max..none. Parallel tool calls UNDOCUMENTED on the hosted API \u2014 profile says false until doc or probe evidence. status:preview \u2014 no brain evidence yet; earns placement via the machinery.",
1997
2021
  // Starter hypotheses — verify with telemetry/probes; NO brain evidence
1998
2022
  // yet. Anchored on the vendor's agentic-coding positioning at a
@@ -2010,8 +2034,11 @@ var PROFILES_RAW = [
2010
2034
  extract: 6,
2011
2035
  critique: 6,
2012
2036
  classify: 6,
2013
- hunt: 5
2014
- // tools work; parallel unproven discounted
2037
+ // alpha.96 — was 5, discounted for "parallel unproven". Parallel is no
2038
+ // longer unproven on the reachable route (3/3, 2026-08-29). The discount
2039
+ // had no other stated basis, so it is withdrawn to the neutral floor.
2040
+ // Judgment, not measurement — same standard as the DeepSeek correction.
2041
+ hunt: 6
2015
2042
  }
2016
2043
  },
2017
2044
  // ── Moonshot (Kimi) ──
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ARCHETYPE_FLOOR_DEFAULT,
3
3
  getDefaultFallbackChain
4
- } from "../chunk-2D4H7HGZ.mjs";
4
+ } from "../chunk-77AHSX6Y.mjs";
5
5
  import {
6
6
  tryGetProfile
7
- } from "../chunk-AMV3M2KH.mjs";
7
+ } from "../chunk-HHIXHL4O.mjs";
8
8
  import {
9
9
  subscribe,
10
10
  subscribeApp
package/dist/index.d.mts CHANGED
@@ -1241,7 +1241,7 @@ declare function runStrategyEvalWithAttribution(opts: Omit<GoldenEvalOptions, 'a
1241
1241
  * guard in `tests/version.test.ts` fails the suite (and therefore
1242
1242
  * `prepublishOnly`) when they diverge — a stale constant cannot reach npm.
1243
1243
  */
1244
- declare const LIBRARY_VERSION = "2.0.0-alpha.96";
1244
+ declare const LIBRARY_VERSION = "2.0.0-alpha.98";
1245
1245
 
1246
1246
  /**
1247
1247
  * Oracle contract — how an app tells the brain whether a response was good.
@@ -3735,10 +3735,26 @@ declare function _testWaitForPromotionsRefresh(): Promise<void>;
3735
3735
  interface SurfaceFailureRow {
3736
3736
  archetype: string;
3737
3737
  model: string;
3738
- /** Total attempts in-window (served + quality walkaways). */
3738
+ /**
3739
+ * QUALITY-axis denominator: attempts that produced something judgeable
3740
+ * (served + quality walkaways). Excludes calls that never reached an
3741
+ * answer — a 429 is not a trial of the model's quality. See migration 071.
3742
+ */
3739
3743
  n: number;
3740
3744
  /** Attempts that failed on the quality axis. */
3741
3745
  nFail: number;
3746
+ /**
3747
+ * alpha.98 — AVAILABILITY-axis denominator: every served attempt is a
3748
+ * trial of whether the model can be reached at all.
3749
+ */
3750
+ nAttempted: number;
3751
+ /** Attempts that never reached an answer — 429, auth, timeout, transport. */
3752
+ nUnavailable: number;
3753
+ /**
3754
+ * Failures in NEITHER vocabulary. Carried so the taxonomy's blind spot is
3755
+ * a number rather than silence; not gated on.
3756
+ */
3757
+ nUnclassified: number;
3742
3758
  }
3743
3759
  declare const MEASURED_FAILURE_CFG: {
3744
3760
  /**
@@ -3760,6 +3776,23 @@ declare const MEASURED_FAILURE_CFG: {
3760
3776
  readonly z: 1.96;
3761
3777
  /** Must match the view's window. Documented here for the advisory text. */
3762
3778
  readonly windowDays: 28;
3779
+ /**
3780
+ * alpha.98 — the AVAILABILITY axis. Same statistic, separate vocabulary,
3781
+ * separate verdict, because a 429 and a parse-failure send an operator in
3782
+ * opposite directions (fund or throttle the account vs. distrust the
3783
+ * model's declared structuredOutput). Merging them into one "failure rate"
3784
+ * is the s75 normalization defect.
3785
+ *
3786
+ * Thresholds match the quality axis deliberately: "we are 95% confident
3787
+ * this model is unreachable more often than not" is the same unarguable
3788
+ * bar, and a de-rank is the same remedy. Kept as a distinct config block
3789
+ * so the two can be tuned apart when evidence says they should be.
3790
+ */
3791
+ readonly availability: {
3792
+ readonly minSample: 5;
3793
+ readonly lowerBoundThreshold: 0.5;
3794
+ readonly z: 1.96;
3795
+ };
3763
3796
  };
3764
3797
  /**
3765
3798
  * Wilson score interval, lower bound. Preferred over the normal
@@ -3771,16 +3804,45 @@ declare const MEASURED_FAILURE_CFG: {
3771
3804
  * Returns 0 for n <= 0.
3772
3805
  */
3773
3806
  declare function wilsonLowerBound(failures: number, n: number, z?: number): number;
3807
+ /** One axis's numbers. Same statistic, different vocabulary. */
3808
+ interface FailureAxisVerdict {
3809
+ /** Observed failure rate in-window on this axis. */
3810
+ rate: number;
3811
+ /** 95% lower confidence bound on that rate — what the gate tests. */
3812
+ lowerBound: number;
3813
+ /** Attempts backing this axis. */
3814
+ n: number;
3815
+ /** Failures on this axis. */
3816
+ nFail: number;
3817
+ /** Whether this axis on its own clears the bar. */
3818
+ gated: boolean;
3819
+ }
3774
3820
  interface MeasuredFailureVerdict {
3775
- /** Whether the measured-failure quality gate fires for this tuple. */
3821
+ /** Whether a measured-failure gate fires for this tuple, on EITHER axis. */
3776
3822
  gated: boolean;
3777
- /** Observed failure rate in-window. */
3823
+ /**
3824
+ * alpha.98 — which axis fired. Undefined when `gated` is false. Read this
3825
+ * before rendering `rate`/`n`: the same number means "answered badly" on
3826
+ * one axis and "could not be reached" on the other, and the fixes differ.
3827
+ * When both axes fire, `'quality'` wins the label — the model is reachable
3828
+ * enough to have been judged, so its answers are the actionable half.
3829
+ */
3830
+ axis?: 'quality' | 'availability';
3831
+ /**
3832
+ * The numbers that JUSTIFY the gate — i.e. drawn from `axis` when gated,
3833
+ * and from the quality axis otherwise. Deliberately not always-quality:
3834
+ * a consumer printing "gated: 0% over 16" for a model that 429'd 42 times
3835
+ * would be reporting a true number as an explanation of the wrong thing.
3836
+ */
3778
3837
  rate: number;
3779
- /** 95% lower confidence bound on that rate — what the gate tests. */
3780
3838
  lowerBound: number;
3781
- /** Attempts backing the verdict. */
3782
3839
  n: number;
3783
3840
  nFail: number;
3841
+ /** Both axes, always present, so a caller never has to infer one. */
3842
+ quality: FailureAxisVerdict;
3843
+ availability: FailureAxisVerdict;
3844
+ /** Failures in neither vocabulary. Not gated on; surfaced for visibility. */
3845
+ nUnclassified: number;
3784
3846
  }
3785
3847
  /**
3786
3848
  * alpha.78 — public row normalizer (PB 2026-07-25 trap): the counts
@@ -3805,6 +3867,9 @@ declare function judgeMeasuredFailure(row: SurfaceFailureRow | {
3805
3867
  model: string;
3806
3868
  n: number | string;
3807
3869
  n_fail: number | string;
3870
+ n_attempted?: number | string;
3871
+ n_unavailable?: number | string;
3872
+ n_unclassified?: number | string;
3808
3873
  } | undefined, cfg?: typeof MEASURED_FAILURE_CFG): MeasuredFailureVerdict | undefined;
3809
3874
  interface MeasuredFailureRuntime {
3810
3875
  /** Endpoint base URL. The library appends `?app_id=<id>`. */
package/dist/index.d.ts CHANGED
@@ -1241,7 +1241,7 @@ declare function runStrategyEvalWithAttribution(opts: Omit<GoldenEvalOptions, 'a
1241
1241
  * guard in `tests/version.test.ts` fails the suite (and therefore
1242
1242
  * `prepublishOnly`) when they diverge — a stale constant cannot reach npm.
1243
1243
  */
1244
- declare const LIBRARY_VERSION = "2.0.0-alpha.96";
1244
+ declare const LIBRARY_VERSION = "2.0.0-alpha.98";
1245
1245
 
1246
1246
  /**
1247
1247
  * Oracle contract — how an app tells the brain whether a response was good.
@@ -3735,10 +3735,26 @@ declare function _testWaitForPromotionsRefresh(): Promise<void>;
3735
3735
  interface SurfaceFailureRow {
3736
3736
  archetype: string;
3737
3737
  model: string;
3738
- /** Total attempts in-window (served + quality walkaways). */
3738
+ /**
3739
+ * QUALITY-axis denominator: attempts that produced something judgeable
3740
+ * (served + quality walkaways). Excludes calls that never reached an
3741
+ * answer — a 429 is not a trial of the model's quality. See migration 071.
3742
+ */
3739
3743
  n: number;
3740
3744
  /** Attempts that failed on the quality axis. */
3741
3745
  nFail: number;
3746
+ /**
3747
+ * alpha.98 — AVAILABILITY-axis denominator: every served attempt is a
3748
+ * trial of whether the model can be reached at all.
3749
+ */
3750
+ nAttempted: number;
3751
+ /** Attempts that never reached an answer — 429, auth, timeout, transport. */
3752
+ nUnavailable: number;
3753
+ /**
3754
+ * Failures in NEITHER vocabulary. Carried so the taxonomy's blind spot is
3755
+ * a number rather than silence; not gated on.
3756
+ */
3757
+ nUnclassified: number;
3742
3758
  }
3743
3759
  declare const MEASURED_FAILURE_CFG: {
3744
3760
  /**
@@ -3760,6 +3776,23 @@ declare const MEASURED_FAILURE_CFG: {
3760
3776
  readonly z: 1.96;
3761
3777
  /** Must match the view's window. Documented here for the advisory text. */
3762
3778
  readonly windowDays: 28;
3779
+ /**
3780
+ * alpha.98 — the AVAILABILITY axis. Same statistic, separate vocabulary,
3781
+ * separate verdict, because a 429 and a parse-failure send an operator in
3782
+ * opposite directions (fund or throttle the account vs. distrust the
3783
+ * model's declared structuredOutput). Merging them into one "failure rate"
3784
+ * is the s75 normalization defect.
3785
+ *
3786
+ * Thresholds match the quality axis deliberately: "we are 95% confident
3787
+ * this model is unreachable more often than not" is the same unarguable
3788
+ * bar, and a de-rank is the same remedy. Kept as a distinct config block
3789
+ * so the two can be tuned apart when evidence says they should be.
3790
+ */
3791
+ readonly availability: {
3792
+ readonly minSample: 5;
3793
+ readonly lowerBoundThreshold: 0.5;
3794
+ readonly z: 1.96;
3795
+ };
3763
3796
  };
3764
3797
  /**
3765
3798
  * Wilson score interval, lower bound. Preferred over the normal
@@ -3771,16 +3804,45 @@ declare const MEASURED_FAILURE_CFG: {
3771
3804
  * Returns 0 for n <= 0.
3772
3805
  */
3773
3806
  declare function wilsonLowerBound(failures: number, n: number, z?: number): number;
3807
+ /** One axis's numbers. Same statistic, different vocabulary. */
3808
+ interface FailureAxisVerdict {
3809
+ /** Observed failure rate in-window on this axis. */
3810
+ rate: number;
3811
+ /** 95% lower confidence bound on that rate — what the gate tests. */
3812
+ lowerBound: number;
3813
+ /** Attempts backing this axis. */
3814
+ n: number;
3815
+ /** Failures on this axis. */
3816
+ nFail: number;
3817
+ /** Whether this axis on its own clears the bar. */
3818
+ gated: boolean;
3819
+ }
3774
3820
  interface MeasuredFailureVerdict {
3775
- /** Whether the measured-failure quality gate fires for this tuple. */
3821
+ /** Whether a measured-failure gate fires for this tuple, on EITHER axis. */
3776
3822
  gated: boolean;
3777
- /** Observed failure rate in-window. */
3823
+ /**
3824
+ * alpha.98 — which axis fired. Undefined when `gated` is false. Read this
3825
+ * before rendering `rate`/`n`: the same number means "answered badly" on
3826
+ * one axis and "could not be reached" on the other, and the fixes differ.
3827
+ * When both axes fire, `'quality'` wins the label — the model is reachable
3828
+ * enough to have been judged, so its answers are the actionable half.
3829
+ */
3830
+ axis?: 'quality' | 'availability';
3831
+ /**
3832
+ * The numbers that JUSTIFY the gate — i.e. drawn from `axis` when gated,
3833
+ * and from the quality axis otherwise. Deliberately not always-quality:
3834
+ * a consumer printing "gated: 0% over 16" for a model that 429'd 42 times
3835
+ * would be reporting a true number as an explanation of the wrong thing.
3836
+ */
3778
3837
  rate: number;
3779
- /** 95% lower confidence bound on that rate — what the gate tests. */
3780
3838
  lowerBound: number;
3781
- /** Attempts backing the verdict. */
3782
3839
  n: number;
3783
3840
  nFail: number;
3841
+ /** Both axes, always present, so a caller never has to infer one. */
3842
+ quality: FailureAxisVerdict;
3843
+ availability: FailureAxisVerdict;
3844
+ /** Failures in neither vocabulary. Not gated on; surfaced for visibility. */
3845
+ nUnclassified: number;
3784
3846
  }
3785
3847
  /**
3786
3848
  * alpha.78 — public row normalizer (PB 2026-07-25 trap): the counts
@@ -3805,6 +3867,9 @@ declare function judgeMeasuredFailure(row: SurfaceFailureRow | {
3805
3867
  model: string;
3806
3868
  n: number | string;
3807
3869
  n_fail: number | string;
3870
+ n_attempted?: number | string;
3871
+ n_unavailable?: number | string;
3872
+ n_unclassified?: number | string;
3808
3873
  } | undefined, cfg?: typeof MEASURED_FAILURE_CFG): MeasuredFailureVerdict | undefined;
3809
3874
  interface MeasuredFailureRuntime {
3810
3875
  /** Endpoint base URL. The library appends `?app_id=<id>`. */
package/dist/index.js CHANGED
@@ -2279,6 +2279,19 @@ var PROFILES_RAW = [
2279
2279
  id: "glm-5.2",
2280
2280
  verifiedAgainstDocs: "2026-07-18",
2281
2281
  provider: "zai",
2282
+ probedLimits: [
2283
+ {
2284
+ metric: "tool_count",
2285
+ testedAt: 6,
2286
+ demanded: 3,
2287
+ served: [3, 3, 3],
2288
+ outcome: "held",
2289
+ trials: 3,
2290
+ lastProbed: "2026-08-29",
2291
+ route: "openrouter:baidu",
2292
+ claim: "Measured 2026-08-29 via OpenRouter (served-by Baidu): 6 tools offered, 3 of 3 demanded served in parallel, 3/3 trials. Route-scoped \u2014 direct z.ai is unmeasured (no key held)."
2293
+ }
2294
+ ],
2282
2295
  status: "preview",
2283
2296
  maxContextTokens: 1048576,
2284
2297
  maxOutputTokens: 131072,
@@ -2289,7 +2302,8 @@ var PROFILES_RAW = [
2289
2302
  // NOTHING about multiple tool_calls per response. Conservative false so
2290
2303
  // hunt-shaped ranking doesn't over-promise; flip on doc or probe
2291
2304
  // evidence (L-081: undocumented ≠ true).
2292
- parallelToolCalls: false,
2305
+ parallelToolCalls: true,
2306
+ // alpha.96 — see weaknesses note; was an unevidenced blanket false
2293
2307
  structuredOutput: "native",
2294
2308
  systemPromptMode: "inline",
2295
2309
  streaming: true,
@@ -2312,7 +2326,17 @@ var PROFILES_RAW = [
2312
2326
  { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
2313
2327
  ],
2314
2328
  strengths: ["cost", "1m_context", "code", "tool_use", "json_output", "reasoning"],
2315
- weaknesses: ["parallel_tools"],
2329
+ // alpha.96 — 'parallel_tools' REMOVED. Measured 2026-08-29 via OpenRouter
2330
+ // (served-by Baidu), 6 tools offered, 3 of 3 demanded served in PARALLEL,
2331
+ // 3/3 trials. ROUTE-SCOPED: per the alpha.92 execute-leg contract there is
2332
+ // no cross-route verdict transfer, and direct z.ai is UNMEASURED (we hold
2333
+ // no zai key). But `parallelToolCalls: false` was a blanket claim with no
2334
+ // route qualification and no evidence at all, and it is capability-
2335
+ // REDUCING — it drives requires-adapter/reject. An unsubstantiated reducer
2336
+ // must not reduce. Contradicted on the one route we can reach is enough to
2337
+ // stop enforcing it; it is not enough to claim the opposite, which is why
2338
+ // probedLimits below carries the route.
2339
+ weaknesses: [],
2316
2340
  notes: "GLM-5.2 (Z.ai, 2026-06): agentic-coding flagship. 1,048,576 ctx / 131,072 max out. $1.40/$4.40 per 1M; cached input $0.26/M (automatic caching, no marker; storage limited-time free as of 2026-07-18). Thinking on by default (`thinking.type`), `reasoning_effort` max..none. Parallel tool calls UNDOCUMENTED on the hosted API \u2014 profile says false until doc or probe evidence. status:preview \u2014 no brain evidence yet; earns placement via the machinery.",
2317
2341
  // Starter hypotheses — verify with telemetry/probes; NO brain evidence
2318
2342
  // yet. Anchored on the vendor's agentic-coding positioning at a
@@ -2330,8 +2354,11 @@ var PROFILES_RAW = [
2330
2354
  extract: 6,
2331
2355
  critique: 6,
2332
2356
  classify: 6,
2333
- hunt: 5
2334
- // tools work; parallel unproven discounted
2357
+ // alpha.96 — was 5, discounted for "parallel unproven". Parallel is no
2358
+ // longer unproven on the reachable route (3/3, 2026-08-29). The discount
2359
+ // had no other stated basis, so it is withdrawn to the neutral floor.
2360
+ // Judgment, not measurement — same standard as the DeepSeek correction.
2361
+ hunt: 6
2335
2362
  }
2336
2363
  },
2337
2364
  // ── Moonshot (Kimi) ──
@@ -2464,7 +2491,24 @@ function bestEffortProfile(id) {
2464
2491
  maxContextTokens: 128e3,
2465
2492
  maxOutputTokens: 4096,
2466
2493
  maxTools: donor.maxTools,
2467
- parallelToolCalls: false,
2494
+ // alpha.97 — was `false`. That was the SAME MISTAKE as the three profiles
2495
+ // corrected in alpha.96, in the one place it does the most damage: applied
2496
+ // to models nobody has measured at all.
2497
+ //
2498
+ // `false` here is capability-REDUCING and unsubstantiated by construction —
2499
+ // a synthesized profile exists precisely because we know nothing. It made
2500
+ // `getModelCompatibility(<any unprofiled model>, {archetype:'hunt'})`
2501
+ // return `requires-adapter` (verified: raw score 5 vs floor 6, adapter
2502
+ // offered), so every consumer on `onUnprofiledModel:'best-effort'` was told
2503
+ // to serialize tool calls on a model we had never touched.
2504
+ //
2505
+ // The never-worse-than-raw rule decides it: for an unknown model kgauto
2506
+ // must behave exactly as a direct call would, and a direct call passes
2507
+ // every tool and lets the provider default apply. `true` is not a claim
2508
+ // that the model parallelises — it is the absence of a claim, expressed as
2509
+ // non-interference. The honest long-term fix is a third state (unknown)
2510
+ // rather than a boolean; that is P4 in the rebuild contract.
2511
+ parallelToolCalls: true,
2468
2512
  structuredOutput: "none",
2469
2513
  systemPromptMode: donor.systemPromptMode,
2470
2514
  streaming: donor.streaming,
@@ -2474,7 +2518,7 @@ function bestEffortProfile(id) {
2474
2518
  lowering: donor.lowering,
2475
2519
  recovery: [],
2476
2520
  strengths: [],
2477
- weaknesses: ["best-effort synthesized profile \u2014 no verified capability data"],
2521
+ weaknesses: ["best-effort synthesized profile \u2014 no verified capability data; kgauto does not reduce capability for a model it has never measured (alpha.97)"],
2478
2522
  notes: `Synthesized by onUnprofiledModel:'best-effort' (provider ${provider} inferred from id; wire mechanics borrowed from ${donor.id}). No cliffs, no measured knowledge, cost UNKNOWN (recorded as 0). Replace via registerProfiles() for real guards.`
2479
2523
  };
2480
2524
  CONSUMER_INDEX.set(canonical, synth);
@@ -2675,7 +2719,7 @@ _setProfileBrainHook({
2675
2719
  var ARCHETYPE_FLOOR_DEFAULT = 6;
2676
2720
  var ABSOLUTE_FLOOR = 4;
2677
2721
  function rawArchetypePerf(profile, archetype) {
2678
- return profile.archetypePerf?.[archetype] ?? 5;
2722
+ return profile.archetypePerf?.[archetype] ?? ARCHETYPE_FLOOR_DEFAULT;
2679
2723
  }
2680
2724
  function hasSequentialToolCliffForHunt(profile) {
2681
2725
  if (profile.parallelToolCalls !== false) return false;
@@ -4799,12 +4843,19 @@ function mapRows(rows) {
4799
4843
  if (!isRawFailureRow(row)) continue;
4800
4844
  const n = coerceCount(row.n);
4801
4845
  const nFail = coerceCount(row.n_fail) ?? 0;
4802
- if (n === null || n <= 0) continue;
4846
+ const nAttempted = coerceCount(row.n_attempted);
4847
+ const nUnavailable = coerceCount(row.n_unavailable) ?? 0;
4848
+ const nUnclassified = coerceCount(row.n_unclassified) ?? 0;
4849
+ if (n === null) continue;
4850
+ if (n <= 0 && nUnavailable <= 0) continue;
4803
4851
  out.push({
4804
4852
  archetype: row.intent_archetype,
4805
4853
  model: row.model,
4806
4854
  n,
4807
- nFail
4855
+ nFail,
4856
+ nAttempted: nAttempted ?? n,
4857
+ nUnavailable,
4858
+ nUnclassified
4808
4859
  });
4809
4860
  }
4810
4861
  return out;
@@ -4828,7 +4879,24 @@ var MEASURED_FAILURE_CFG = {
4828
4879
  /** 95% one-sided-ish confidence (standard two-sided z at α=0.05). */
4829
4880
  z: 1.96,
4830
4881
  /** Must match the view's window. Documented here for the advisory text. */
4831
- windowDays: 28
4882
+ windowDays: 28,
4883
+ /**
4884
+ * alpha.98 — the AVAILABILITY axis. Same statistic, separate vocabulary,
4885
+ * separate verdict, because a 429 and a parse-failure send an operator in
4886
+ * opposite directions (fund or throttle the account vs. distrust the
4887
+ * model's declared structuredOutput). Merging them into one "failure rate"
4888
+ * is the s75 normalization defect.
4889
+ *
4890
+ * Thresholds match the quality axis deliberately: "we are 95% confident
4891
+ * this model is unreachable more often than not" is the same unarguable
4892
+ * bar, and a de-rank is the same remedy. Kept as a distinct config block
4893
+ * so the two can be tuned apart when evidence says they should be.
4894
+ */
4895
+ availability: {
4896
+ minSample: 5,
4897
+ lowerBoundThreshold: 0.5,
4898
+ z: 1.96
4899
+ }
4832
4900
  };
4833
4901
  function wilsonLowerBound(failures, n, z = MEASURED_FAILURE_CFG.z) {
4834
4902
  if (n <= 0) return 0;
@@ -4845,15 +4913,53 @@ function mapMeasuredFailureRows(rows) {
4845
4913
  }
4846
4914
  function judgeMeasuredFailure(row, cfg = MEASURED_FAILURE_CFG) {
4847
4915
  if (!row) return void 0;
4848
- const normalized = "nFail" in row && typeof row.n === "number" ? row : mapRows([row])[0];
4849
- if (!normalized || normalized.n < cfg.minSample) return void 0;
4850
- const lowerBound = wilsonLowerBound(normalized.nFail, normalized.n, cfg.z);
4916
+ const raw = "nFail" in row && typeof row.n === "number" ? row : mapRows([row])[0];
4917
+ if (!raw) return void 0;
4918
+ const normalized = {
4919
+ ...raw,
4920
+ nAttempted: typeof raw.nAttempted === "number" ? raw.nAttempted : raw.n,
4921
+ nUnavailable: typeof raw.nUnavailable === "number" ? raw.nUnavailable : 0,
4922
+ nUnclassified: typeof raw.nUnclassified === "number" ? raw.nUnclassified : 0
4923
+ };
4924
+ const quality = judgeAxis(
4925
+ normalized.nFail,
4926
+ normalized.n,
4927
+ cfg.minSample,
4928
+ cfg.lowerBoundThreshold,
4929
+ cfg.z
4930
+ );
4931
+ const availability = judgeAxis(
4932
+ normalized.nUnavailable,
4933
+ normalized.nAttempted,
4934
+ cfg.availability.minSample,
4935
+ cfg.availability.lowerBoundThreshold,
4936
+ cfg.availability.z
4937
+ );
4938
+ if (normalized.n < cfg.minSample && normalized.nAttempted < cfg.availability.minSample) {
4939
+ return void 0;
4940
+ }
4941
+ const axis = quality.gated ? "quality" : availability.gated ? "availability" : void 0;
4942
+ const lead = axis === "availability" ? availability : quality;
4851
4943
  return {
4852
- gated: lowerBound > cfg.lowerBoundThreshold,
4853
- rate: normalized.nFail / normalized.n,
4944
+ gated: quality.gated || availability.gated,
4945
+ axis,
4946
+ rate: lead.rate,
4947
+ lowerBound: lead.lowerBound,
4948
+ n: lead.n,
4949
+ nFail: lead.nFail,
4950
+ quality,
4951
+ availability,
4952
+ nUnclassified: normalized.nUnclassified
4953
+ };
4954
+ }
4955
+ function judgeAxis(failures, n, minSample, threshold, z) {
4956
+ const lowerBound = wilsonLowerBound(failures, n, z);
4957
+ return {
4958
+ rate: n > 0 ? failures / n : 0,
4854
4959
  lowerBound,
4855
- n: normalized.n,
4856
- nFail: normalized.nFail
4960
+ n,
4961
+ nFail: failures,
4962
+ gated: n >= minSample && lowerBound > threshold
4857
4963
  };
4858
4964
  }
4859
4965
  var snapshots2 = /* @__PURE__ */ new Map();
@@ -5895,9 +6001,13 @@ function detectSingleModelArray(ir, policy) {
5895
6001
  ];
5896
6002
  }
5897
6003
  function suppressedRecommendationReason(ir, archetype, altProfile) {
5898
- if (getMeasuredFailureVerdict({ appId: ir.appId, archetype, model: altProfile.id })?.gated === true) {
5899
- return "measured-failure-gate";
5900
- }
6004
+ const measured = getMeasuredFailureVerdict({
6005
+ appId: ir.appId,
6006
+ archetype,
6007
+ model: altProfile.id
6008
+ });
6009
+ if (measured?.quality.gated === true) return "measured-failure-gate";
6010
+ if (measured?.availability.gated === true) return "measured-unavailable-gate";
5901
6011
  if (ir.constraints?.structuredOutput && effectiveConventions(altProfile).some(
5902
6012
  (c) => c.archetype === archetype && c.structuredOutputHint === "avoid"
5903
6013
  )) {
@@ -6282,7 +6392,8 @@ function compile(ir, opts = {}) {
6282
6392
  rate: verdict.rate,
6283
6393
  lowerBound: verdict.lowerBound,
6284
6394
  n: verdict.n,
6285
- nFail: verdict.nFail
6395
+ nFail: verdict.nFail,
6396
+ axis: verdict.axis ?? "quality"
6286
6397
  });
6287
6398
  }
6288
6399
  }
@@ -6626,7 +6737,7 @@ function validateFinalFit(ir, profile, tokens) {
6626
6737
  }
6627
6738
 
6628
6739
  // src/version.ts
6629
- var LIBRARY_VERSION = "2.0.0-alpha.96";
6740
+ var LIBRARY_VERSION = "2.0.0-alpha.98";
6630
6741
 
6631
6742
  // src/pricing-brain.ts
6632
6743
  function isPricingRow(x) {
@@ -8772,8 +8883,8 @@ async function call(ir, opts = {}) {
8772
8883
  appId: ir.appId,
8773
8884
  archetype: ir.intent.archetype,
8774
8885
  model: targetModel
8775
- })?.gated === true) {
8776
- retrySuppressionNote = " [sameModelRetry suppressed: this model carries an active measured-failure gate for this archetype \u2014 retrying it would near-certainly bill a second doomed inference; walking the chain instead]";
8886
+ })?.quality.gated === true) {
8887
+ retrySuppressionNote = " [sameModelRetry suppressed: this model carries an active measured-failure gate (quality axis) for this archetype \u2014 retrying it would near-certainly bill a second doomed inference; walking the chain instead]";
8777
8888
  }
8778
8889
  if (!validated.ok && isStructuredContractViolation(validated.errorCode) && sameModelRetryEnabled && !retriedSameModel && !retrySuppressionNote) {
8779
8890
  retriedSameModel = true;
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  LIBRARY_VERSION,
8
8
  createKeyHealthRoute,
9
9
  keyFingerprint
10
- } from "./chunk-26IS3Y6Z.mjs";
10
+ } from "./chunk-KENTOSJO.mjs";
11
11
  import {
12
12
  ARCHETYPE_FAMILY_FITS,
13
13
  BLOCKED_MODEL_FAMILY_SIBLING_SERVED_CODE,
@@ -63,7 +63,7 @@ import {
63
63
  runAdvisor,
64
64
  setTokenizer,
65
65
  wilsonLowerBound
66
- } from "./chunk-SRPRNTDT.mjs";
66
+ } from "./chunk-KZNTCMGM.mjs";
67
67
  import {
68
68
  ABSOLUTE_FLOOR,
69
69
  ARCHETYPE_FLOOR_DEFAULT,
@@ -100,7 +100,7 @@ import {
100
100
  policySetHas,
101
101
  readBrainReadEnv,
102
102
  resolveProviderKey
103
- } from "./chunk-2D4H7HGZ.mjs";
103
+ } from "./chunk-77AHSX6Y.mjs";
104
104
  import {
105
105
  ALL_ARCHETYPES,
106
106
  DIALECT_VERSION,
@@ -127,7 +127,7 @@ import {
127
127
  registerProfiles,
128
128
  resolveModelAlias,
129
129
  tryGetProfile
130
- } from "./chunk-AMV3M2KH.mjs";
130
+ } from "./chunk-HHIXHL4O.mjs";
131
131
  import {
132
132
  emitAdvisoryFired,
133
133
  emitCompileDone,
@@ -1980,8 +1980,8 @@ async function call(ir, opts = {}) {
1980
1980
  appId: ir.appId,
1981
1981
  archetype: ir.intent.archetype,
1982
1982
  model: targetModel
1983
- })?.gated === true) {
1984
- retrySuppressionNote = " [sameModelRetry suppressed: this model carries an active measured-failure gate for this archetype \u2014 retrying it would near-certainly bill a second doomed inference; walking the chain instead]";
1983
+ })?.quality.gated === true) {
1984
+ retrySuppressionNote = " [sameModelRetry suppressed: this model carries an active measured-failure gate (quality axis) for this archetype \u2014 retrying it would near-certainly bill a second doomed inference; walking the chain instead]";
1985
1985
  }
1986
1986
  if (!validated.ok && isStructuredContractViolation(validated.errorCode) && sameModelRetryEnabled && !retriedSameModel && !retrySuppressionNote) {
1987
1987
  retriedSameModel = true;
@@ -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.96";
31
+ var LIBRARY_VERSION = "2.0.0-alpha.98";
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-26IS3Y6Z.mjs";
6
+ } from "./chunk-KENTOSJO.mjs";
7
7
  export {
8
8
  KEY_FINGERPRINT_DOMAIN,
9
9
  KEY_FINGERPRINT_LENGTH,
package/dist/probe.js CHANGED
@@ -2021,6 +2021,19 @@ var PROFILES_RAW = [
2021
2021
  id: "glm-5.2",
2022
2022
  verifiedAgainstDocs: "2026-07-18",
2023
2023
  provider: "zai",
2024
+ probedLimits: [
2025
+ {
2026
+ metric: "tool_count",
2027
+ testedAt: 6,
2028
+ demanded: 3,
2029
+ served: [3, 3, 3],
2030
+ outcome: "held",
2031
+ trials: 3,
2032
+ lastProbed: "2026-08-29",
2033
+ route: "openrouter:baidu",
2034
+ claim: "Measured 2026-08-29 via OpenRouter (served-by Baidu): 6 tools offered, 3 of 3 demanded served in parallel, 3/3 trials. Route-scoped \u2014 direct z.ai is unmeasured (no key held)."
2035
+ }
2036
+ ],
2024
2037
  status: "preview",
2025
2038
  maxContextTokens: 1048576,
2026
2039
  maxOutputTokens: 131072,
@@ -2031,7 +2044,8 @@ var PROFILES_RAW = [
2031
2044
  // NOTHING about multiple tool_calls per response. Conservative false so
2032
2045
  // hunt-shaped ranking doesn't over-promise; flip on doc or probe
2033
2046
  // evidence (L-081: undocumented ≠ true).
2034
- parallelToolCalls: false,
2047
+ parallelToolCalls: true,
2048
+ // alpha.96 — see weaknesses note; was an unevidenced blanket false
2035
2049
  structuredOutput: "native",
2036
2050
  systemPromptMode: "inline",
2037
2051
  streaming: true,
@@ -2054,7 +2068,17 @@ var PROFILES_RAW = [
2054
2068
  { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
2055
2069
  ],
2056
2070
  strengths: ["cost", "1m_context", "code", "tool_use", "json_output", "reasoning"],
2057
- weaknesses: ["parallel_tools"],
2071
+ // alpha.96 — 'parallel_tools' REMOVED. Measured 2026-08-29 via OpenRouter
2072
+ // (served-by Baidu), 6 tools offered, 3 of 3 demanded served in PARALLEL,
2073
+ // 3/3 trials. ROUTE-SCOPED: per the alpha.92 execute-leg contract there is
2074
+ // no cross-route verdict transfer, and direct z.ai is UNMEASURED (we hold
2075
+ // no zai key). But `parallelToolCalls: false` was a blanket claim with no
2076
+ // route qualification and no evidence at all, and it is capability-
2077
+ // REDUCING — it drives requires-adapter/reject. An unsubstantiated reducer
2078
+ // must not reduce. Contradicted on the one route we can reach is enough to
2079
+ // stop enforcing it; it is not enough to claim the opposite, which is why
2080
+ // probedLimits below carries the route.
2081
+ weaknesses: [],
2058
2082
  notes: "GLM-5.2 (Z.ai, 2026-06): agentic-coding flagship. 1,048,576 ctx / 131,072 max out. $1.40/$4.40 per 1M; cached input $0.26/M (automatic caching, no marker; storage limited-time free as of 2026-07-18). Thinking on by default (`thinking.type`), `reasoning_effort` max..none. Parallel tool calls UNDOCUMENTED on the hosted API \u2014 profile says false until doc or probe evidence. status:preview \u2014 no brain evidence yet; earns placement via the machinery.",
2059
2083
  // Starter hypotheses — verify with telemetry/probes; NO brain evidence
2060
2084
  // yet. Anchored on the vendor's agentic-coding positioning at a
@@ -2072,8 +2096,11 @@ var PROFILES_RAW = [
2072
2096
  extract: 6,
2073
2097
  critique: 6,
2074
2098
  classify: 6,
2075
- hunt: 5
2076
- // tools work; parallel unproven discounted
2099
+ // alpha.96 — was 5, discounted for "parallel unproven". Parallel is no
2100
+ // longer unproven on the reachable route (3/3, 2026-08-29). The discount
2101
+ // had no other stated basis, so it is withdrawn to the neutral floor.
2102
+ // Judgment, not measurement — same standard as the DeepSeek correction.
2103
+ hunt: 6
2077
2104
  }
2078
2105
  },
2079
2106
  // ── Moonshot (Kimi) ──
@@ -2200,7 +2227,24 @@ function bestEffortProfile(id) {
2200
2227
  maxContextTokens: 128e3,
2201
2228
  maxOutputTokens: 4096,
2202
2229
  maxTools: donor.maxTools,
2203
- parallelToolCalls: false,
2230
+ // alpha.97 — was `false`. That was the SAME MISTAKE as the three profiles
2231
+ // corrected in alpha.96, in the one place it does the most damage: applied
2232
+ // to models nobody has measured at all.
2233
+ //
2234
+ // `false` here is capability-REDUCING and unsubstantiated by construction —
2235
+ // a synthesized profile exists precisely because we know nothing. It made
2236
+ // `getModelCompatibility(<any unprofiled model>, {archetype:'hunt'})`
2237
+ // return `requires-adapter` (verified: raw score 5 vs floor 6, adapter
2238
+ // offered), so every consumer on `onUnprofiledModel:'best-effort'` was told
2239
+ // to serialize tool calls on a model we had never touched.
2240
+ //
2241
+ // The never-worse-than-raw rule decides it: for an unknown model kgauto
2242
+ // must behave exactly as a direct call would, and a direct call passes
2243
+ // every tool and lets the provider default apply. `true` is not a claim
2244
+ // that the model parallelises — it is the absence of a claim, expressed as
2245
+ // non-interference. The honest long-term fix is a third state (unknown)
2246
+ // rather than a boolean; that is P4 in the rebuild contract.
2247
+ parallelToolCalls: true,
2204
2248
  structuredOutput: "none",
2205
2249
  systemPromptMode: donor.systemPromptMode,
2206
2250
  streaming: donor.streaming,
@@ -2210,7 +2254,7 @@ function bestEffortProfile(id) {
2210
2254
  lowering: donor.lowering,
2211
2255
  recovery: [],
2212
2256
  strengths: [],
2213
- weaknesses: ["best-effort synthesized profile \u2014 no verified capability data"],
2257
+ weaknesses: ["best-effort synthesized profile \u2014 no verified capability data; kgauto does not reduce capability for a model it has never measured (alpha.97)"],
2214
2258
  notes: `Synthesized by onUnprofiledModel:'best-effort' (provider ${provider} inferred from id; wire mechanics borrowed from ${donor.id}). No cliffs, no measured knowledge, cost UNKNOWN (recorded as 0). Replace via registerProfiles() for real guards.`
2215
2259
  };
2216
2260
  CONSUMER_INDEX.set(canonical, synth);
@@ -2359,7 +2403,7 @@ _setProfileBrainHook({
2359
2403
  // src/compatibility.ts
2360
2404
  var ARCHETYPE_FLOOR_DEFAULT = 6;
2361
2405
  function rawArchetypePerf(profile, archetype) {
2362
- return profile.archetypePerf?.[archetype] ?? 5;
2406
+ return profile.archetypePerf?.[archetype] ?? ARCHETYPE_FLOOR_DEFAULT;
2363
2407
  }
2364
2408
  function hasSequentialToolCliffForHunt(profile) {
2365
2409
  if (profile.parallelToolCalls !== false) return false;
@@ -4240,12 +4284,19 @@ function mapRows(rows) {
4240
4284
  if (!isRawFailureRow(row)) continue;
4241
4285
  const n = coerceCount(row.n);
4242
4286
  const nFail = coerceCount(row.n_fail) ?? 0;
4243
- if (n === null || n <= 0) continue;
4287
+ const nAttempted = coerceCount(row.n_attempted);
4288
+ const nUnavailable = coerceCount(row.n_unavailable) ?? 0;
4289
+ const nUnclassified = coerceCount(row.n_unclassified) ?? 0;
4290
+ if (n === null) continue;
4291
+ if (n <= 0 && nUnavailable <= 0) continue;
4244
4292
  out.push({
4245
4293
  archetype: row.intent_archetype,
4246
4294
  model: row.model,
4247
4295
  n,
4248
- nFail
4296
+ nFail,
4297
+ nAttempted: nAttempted ?? n,
4298
+ nUnavailable,
4299
+ nUnclassified
4249
4300
  });
4250
4301
  }
4251
4302
  return out;
@@ -4269,7 +4320,24 @@ var MEASURED_FAILURE_CFG = {
4269
4320
  /** 95% one-sided-ish confidence (standard two-sided z at α=0.05). */
4270
4321
  z: 1.96,
4271
4322
  /** Must match the view's window. Documented here for the advisory text. */
4272
- windowDays: 28
4323
+ windowDays: 28,
4324
+ /**
4325
+ * alpha.98 — the AVAILABILITY axis. Same statistic, separate vocabulary,
4326
+ * separate verdict, because a 429 and a parse-failure send an operator in
4327
+ * opposite directions (fund or throttle the account vs. distrust the
4328
+ * model's declared structuredOutput). Merging them into one "failure rate"
4329
+ * is the s75 normalization defect.
4330
+ *
4331
+ * Thresholds match the quality axis deliberately: "we are 95% confident
4332
+ * this model is unreachable more often than not" is the same unarguable
4333
+ * bar, and a de-rank is the same remedy. Kept as a distinct config block
4334
+ * so the two can be tuned apart when evidence says they should be.
4335
+ */
4336
+ availability: {
4337
+ minSample: 5,
4338
+ lowerBoundThreshold: 0.5,
4339
+ z: 1.96
4340
+ }
4273
4341
  };
4274
4342
  function wilsonLowerBound(failures, n, z = MEASURED_FAILURE_CFG.z) {
4275
4343
  if (n <= 0) return 0;
@@ -4283,15 +4351,53 @@ function wilsonLowerBound(failures, n, z = MEASURED_FAILURE_CFG.z) {
4283
4351
  }
4284
4352
  function judgeMeasuredFailure(row, cfg = MEASURED_FAILURE_CFG) {
4285
4353
  if (!row) return void 0;
4286
- const normalized = "nFail" in row && typeof row.n === "number" ? row : mapRows([row])[0];
4287
- if (!normalized || normalized.n < cfg.minSample) return void 0;
4288
- const lowerBound = wilsonLowerBound(normalized.nFail, normalized.n, cfg.z);
4354
+ const raw = "nFail" in row && typeof row.n === "number" ? row : mapRows([row])[0];
4355
+ if (!raw) return void 0;
4356
+ const normalized = {
4357
+ ...raw,
4358
+ nAttempted: typeof raw.nAttempted === "number" ? raw.nAttempted : raw.n,
4359
+ nUnavailable: typeof raw.nUnavailable === "number" ? raw.nUnavailable : 0,
4360
+ nUnclassified: typeof raw.nUnclassified === "number" ? raw.nUnclassified : 0
4361
+ };
4362
+ const quality = judgeAxis(
4363
+ normalized.nFail,
4364
+ normalized.n,
4365
+ cfg.minSample,
4366
+ cfg.lowerBoundThreshold,
4367
+ cfg.z
4368
+ );
4369
+ const availability = judgeAxis(
4370
+ normalized.nUnavailable,
4371
+ normalized.nAttempted,
4372
+ cfg.availability.minSample,
4373
+ cfg.availability.lowerBoundThreshold,
4374
+ cfg.availability.z
4375
+ );
4376
+ if (normalized.n < cfg.minSample && normalized.nAttempted < cfg.availability.minSample) {
4377
+ return void 0;
4378
+ }
4379
+ const axis = quality.gated ? "quality" : availability.gated ? "availability" : void 0;
4380
+ const lead = axis === "availability" ? availability : quality;
4289
4381
  return {
4290
- gated: lowerBound > cfg.lowerBoundThreshold,
4291
- rate: normalized.nFail / normalized.n,
4382
+ gated: quality.gated || availability.gated,
4383
+ axis,
4384
+ rate: lead.rate,
4385
+ lowerBound: lead.lowerBound,
4386
+ n: lead.n,
4387
+ nFail: lead.nFail,
4388
+ quality,
4389
+ availability,
4390
+ nUnclassified: normalized.nUnclassified
4391
+ };
4392
+ }
4393
+ function judgeAxis(failures, n, minSample, threshold, z) {
4394
+ const lowerBound = wilsonLowerBound(failures, n, z);
4395
+ return {
4396
+ rate: n > 0 ? failures / n : 0,
4292
4397
  lowerBound,
4293
- n: normalized.n,
4294
- nFail: normalized.nFail
4398
+ n,
4399
+ nFail: failures,
4400
+ gated: n >= minSample && lowerBound > threshold
4295
4401
  };
4296
4402
  }
4297
4403
  var snapshots2 = /* @__PURE__ */ new Map();
@@ -5155,9 +5261,13 @@ function detectSingleModelArray(ir, policy) {
5155
5261
  ];
5156
5262
  }
5157
5263
  function suppressedRecommendationReason(ir, archetype, altProfile) {
5158
- if (getMeasuredFailureVerdict({ appId: ir.appId, archetype, model: altProfile.id })?.gated === true) {
5159
- return "measured-failure-gate";
5160
- }
5264
+ const measured = getMeasuredFailureVerdict({
5265
+ appId: ir.appId,
5266
+ archetype,
5267
+ model: altProfile.id
5268
+ });
5269
+ if (measured?.quality.gated === true) return "measured-failure-gate";
5270
+ if (measured?.availability.gated === true) return "measured-unavailable-gate";
5161
5271
  if (ir.constraints?.structuredOutput && effectiveConventions(altProfile).some(
5162
5272
  (c) => c.archetype === archetype && c.structuredOutputHint === "avoid"
5163
5273
  )) {
@@ -5542,7 +5652,8 @@ function compile(ir, opts = {}) {
5542
5652
  rate: verdict.rate,
5543
5653
  lowerBound: verdict.lowerBound,
5544
5654
  n: verdict.n,
5545
- nFail: verdict.nFail
5655
+ nFail: verdict.nFail,
5656
+ axis: verdict.axis ?? "quality"
5546
5657
  });
5547
5658
  }
5548
5659
  }
package/dist/probe.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  compile
3
- } from "./chunk-SRPRNTDT.mjs";
4
- import "./chunk-2D4H7HGZ.mjs";
3
+ } from "./chunk-KZNTCMGM.mjs";
4
+ import "./chunk-77AHSX6Y.mjs";
5
5
  import "./chunk-FEMPY6EC.mjs";
6
6
  import {
7
7
  deriveCliffEvidenceClass,
8
8
  getProfile
9
- } from "./chunk-AMV3M2KH.mjs";
9
+ } from "./chunk-HHIXHL4O.mjs";
10
10
 
11
11
  // src/probe.ts
12
12
  function compileSuppressing(ir, opts) {
package/dist/profiles.js CHANGED
@@ -1952,6 +1952,19 @@ var PROFILES_RAW = [
1952
1952
  id: "glm-5.2",
1953
1953
  verifiedAgainstDocs: "2026-07-18",
1954
1954
  provider: "zai",
1955
+ probedLimits: [
1956
+ {
1957
+ metric: "tool_count",
1958
+ testedAt: 6,
1959
+ demanded: 3,
1960
+ served: [3, 3, 3],
1961
+ outcome: "held",
1962
+ trials: 3,
1963
+ lastProbed: "2026-08-29",
1964
+ route: "openrouter:baidu",
1965
+ claim: "Measured 2026-08-29 via OpenRouter (served-by Baidu): 6 tools offered, 3 of 3 demanded served in parallel, 3/3 trials. Route-scoped \u2014 direct z.ai is unmeasured (no key held)."
1966
+ }
1967
+ ],
1955
1968
  status: "preview",
1956
1969
  maxContextTokens: 1048576,
1957
1970
  maxOutputTokens: 131072,
@@ -1962,7 +1975,8 @@ var PROFILES_RAW = [
1962
1975
  // NOTHING about multiple tool_calls per response. Conservative false so
1963
1976
  // hunt-shaped ranking doesn't over-promise; flip on doc or probe
1964
1977
  // evidence (L-081: undocumented ≠ true).
1965
- parallelToolCalls: false,
1978
+ parallelToolCalls: true,
1979
+ // alpha.96 — see weaknesses note; was an unevidenced blanket false
1966
1980
  structuredOutput: "native",
1967
1981
  systemPromptMode: "inline",
1968
1982
  streaming: true,
@@ -1985,7 +1999,17 @@ var PROFILES_RAW = [
1985
1999
  { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
1986
2000
  ],
1987
2001
  strengths: ["cost", "1m_context", "code", "tool_use", "json_output", "reasoning"],
1988
- weaknesses: ["parallel_tools"],
2002
+ // alpha.96 — 'parallel_tools' REMOVED. Measured 2026-08-29 via OpenRouter
2003
+ // (served-by Baidu), 6 tools offered, 3 of 3 demanded served in PARALLEL,
2004
+ // 3/3 trials. ROUTE-SCOPED: per the alpha.92 execute-leg contract there is
2005
+ // no cross-route verdict transfer, and direct z.ai is UNMEASURED (we hold
2006
+ // no zai key). But `parallelToolCalls: false` was a blanket claim with no
2007
+ // route qualification and no evidence at all, and it is capability-
2008
+ // REDUCING — it drives requires-adapter/reject. An unsubstantiated reducer
2009
+ // must not reduce. Contradicted on the one route we can reach is enough to
2010
+ // stop enforcing it; it is not enough to claim the opposite, which is why
2011
+ // probedLimits below carries the route.
2012
+ weaknesses: [],
1989
2013
  notes: "GLM-5.2 (Z.ai, 2026-06): agentic-coding flagship. 1,048,576 ctx / 131,072 max out. $1.40/$4.40 per 1M; cached input $0.26/M (automatic caching, no marker; storage limited-time free as of 2026-07-18). Thinking on by default (`thinking.type`), `reasoning_effort` max..none. Parallel tool calls UNDOCUMENTED on the hosted API \u2014 profile says false until doc or probe evidence. status:preview \u2014 no brain evidence yet; earns placement via the machinery.",
1990
2014
  // Starter hypotheses — verify with telemetry/probes; NO brain evidence
1991
2015
  // yet. Anchored on the vendor's agentic-coding positioning at a
@@ -2003,8 +2027,11 @@ var PROFILES_RAW = [
2003
2027
  extract: 6,
2004
2028
  critique: 6,
2005
2029
  classify: 6,
2006
- hunt: 5
2007
- // tools work; parallel unproven discounted
2030
+ // alpha.96 — was 5, discounted for "parallel unproven". Parallel is no
2031
+ // longer unproven on the reachable route (3/3, 2026-08-29). The discount
2032
+ // had no other stated basis, so it is withdrawn to the neutral floor.
2033
+ // Judgment, not measurement — same standard as the DeepSeek correction.
2034
+ hunt: 6
2008
2035
  }
2009
2036
  },
2010
2037
  // ── Moonshot (Kimi) ──
@@ -2137,7 +2164,24 @@ function bestEffortProfile(id) {
2137
2164
  maxContextTokens: 128e3,
2138
2165
  maxOutputTokens: 4096,
2139
2166
  maxTools: donor.maxTools,
2140
- parallelToolCalls: false,
2167
+ // alpha.97 — was `false`. That was the SAME MISTAKE as the three profiles
2168
+ // corrected in alpha.96, in the one place it does the most damage: applied
2169
+ // to models nobody has measured at all.
2170
+ //
2171
+ // `false` here is capability-REDUCING and unsubstantiated by construction —
2172
+ // a synthesized profile exists precisely because we know nothing. It made
2173
+ // `getModelCompatibility(<any unprofiled model>, {archetype:'hunt'})`
2174
+ // return `requires-adapter` (verified: raw score 5 vs floor 6, adapter
2175
+ // offered), so every consumer on `onUnprofiledModel:'best-effort'` was told
2176
+ // to serialize tool calls on a model we had never touched.
2177
+ //
2178
+ // The never-worse-than-raw rule decides it: for an unknown model kgauto
2179
+ // must behave exactly as a direct call would, and a direct call passes
2180
+ // every tool and lets the provider default apply. `true` is not a claim
2181
+ // that the model parallelises — it is the absence of a claim, expressed as
2182
+ // non-interference. The honest long-term fix is a third state (unknown)
2183
+ // rather than a boolean; that is P4 in the rebuild contract.
2184
+ parallelToolCalls: true,
2141
2185
  structuredOutput: "none",
2142
2186
  systemPromptMode: donor.systemPromptMode,
2143
2187
  streaming: donor.streaming,
@@ -2147,7 +2191,7 @@ function bestEffortProfile(id) {
2147
2191
  lowering: donor.lowering,
2148
2192
  recovery: [],
2149
2193
  strengths: [],
2150
- weaknesses: ["best-effort synthesized profile \u2014 no verified capability data"],
2194
+ weaknesses: ["best-effort synthesized profile \u2014 no verified capability data; kgauto does not reduce capability for a model it has never measured (alpha.97)"],
2151
2195
  notes: `Synthesized by onUnprofiledModel:'best-effort' (provider ${provider} inferred from id; wire mechanics borrowed from ${donor.id}). No cliffs, no measured knowledge, cost UNKNOWN (recorded as 0). Replace via registerProfiles() for real guards.`
2152
2196
  };
2153
2197
  CONSUMER_INDEX.set(canonical, synth);
package/dist/profiles.mjs CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  registerProfiles,
15
15
  resolveModelAlias,
16
16
  tryGetProfile
17
- } from "./chunk-AMV3M2KH.mjs";
17
+ } from "./chunk-HHIXHL4O.mjs";
18
18
  export {
19
19
  ALIASES,
20
20
  LATENCY_TIER_MS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warmdrift/kgauto-compiler",
3
- "version": "2.0.0-alpha.96",
3
+ "version": "2.0.0-alpha.98",
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",