@warmdrift/kgauto-compiler 2.0.0-alpha.21 → 2.0.0-alpha.22

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.d.mts CHANGED
@@ -326,14 +326,47 @@ declare function countTokens(text: string): number;
326
326
 
327
327
  /** Subset of CompileResult fields the advisor needs. */
328
328
  type AdvisorContext = Pick<CompileResult, 'target' | 'provider' | 'tokensIn' | 'diagnostics'>;
329
+ /**
330
+ * Optional Phase 2 (alpha.22) context — fallback chain + a profile resolver
331
+ * for cross-model comparison. Three new rules
332
+ * (`cost-mismatched-archetype`, `model-stale-evidence`, `tier-down`) consume
333
+ * this to surface measurement-substrate signals (alpha.20 clean-attribution +
334
+ * alpha.21 grounding labels) as actionable consumer guidance.
335
+ *
336
+ * When `fallbackChain` is empty, rules 1 + 3 stay silent (nothing to
337
+ * compare against). When `profileResolver` is omitted, the rules degrade
338
+ * gracefully — they can still inspect the chosen profile but not chain
339
+ * alternatives. Rule 2 (`model-stale-evidence`) is independent of chain
340
+ * shape and works on the chosen model alone.
341
+ */
342
+ interface RunAdvisorPhase2Context {
343
+ fallbackChain: string[];
344
+ profileResolver?: (id: string) => ModelProfile | undefined;
345
+ }
329
346
  /**
330
347
  * Run all phased rules and return collected advisories. Order is fixed so
331
348
  * output is stable across runs. The `policy` argument is alpha.9 — the
332
349
  * `single-model-array` rule needs to know whether the consumer explicitly
333
350
  * declared `posture: 'locked'` (in which case single-model is intentional
334
351
  * and shouldn't warn).
352
+ *
353
+ * `phase2` is alpha.22 — gives the advisor access to the fallback chain +
354
+ * a profile resolver so the three new compile-time recommendation rules
355
+ * (`cost-mismatched-archetype`, `model-stale-evidence`, `tier-down`) can
356
+ * compare the chosen model against in-chain alternatives. Optional for
357
+ * backward compatibility with consumers calling `runAdvisor()` directly.
358
+ */
359
+ declare function runAdvisor(ir: PromptIR, result: AdvisorContext, profile: ModelProfile, policy?: CompilePolicy, phase2?: RunAdvisorPhase2Context): BestPracticeAdvisory[];
360
+
361
+ /**
362
+ * alpha.22 — sync introspection: is brain-query mode active for a given
363
+ * table? Used by the advisor (`model-stale-evidence` rule) to decide
364
+ * whether a `judgment`-grounded chosen model is a measurement gap worth
365
+ * surfacing. Returns false on cold start, when configureBrain() was never
366
+ * called, or when the consumer explicitly opted the table out via
367
+ * `BrainConfig.brainQuery.<table> = false`.
335
368
  */
336
- declare function runAdvisor(ir: PromptIR, result: AdvisorContext, profile: ModelProfile, policy?: CompilePolicy): BestPracticeAdvisory[];
369
+ declare function isBrainQueryActiveFor(table: string): boolean;
337
370
 
338
371
  /**
339
372
  * env.ts — provider env-key resolution + reachability predicates.
@@ -870,4 +903,4 @@ declare const loadAliasesFromBrain: () => Record<string, string>;
870
903
  */
871
904
  declare function compile(ir: PromptIR, opts?: CompileOptions): CompileResult;
872
905
 
873
- export { ApiKeys, type AppOracle, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, CallOptions, CallResult, ChainEntry, type CompileOptions, CompilePolicy, CompileResult, CompiledRequest, type ExecuteErr, type ExecuteOk, type ExecuteOptions, type ExecuteResult, type FallbackPosture, type GetDefaultFallbackChainOpts, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type ModelBrainRow, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PROVIDER_ENV_KEYS, type PricingRow, type ProfileToRowOptions, PromptIR, Provider, ProviderOverrides, type ProviderReachability, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type SupportedProvider, buildLLMJudge, call, clearBrain, compile, configureBrain, countTokens, execute, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getReachabilityDiagnostic, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStarterChain, getStarterChainWithGrounding, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, profileToRow, record, recordOutcome, resetTokenizer, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
906
+ export { ApiKeys, type AppOracle, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, CallOptions, CallResult, ChainEntry, type CompileOptions, CompilePolicy, CompileResult, CompiledRequest, type ExecuteErr, type ExecuteOk, type ExecuteOptions, type ExecuteResult, type FallbackPosture, type GetDefaultFallbackChainOpts, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type ModelBrainRow, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PROVIDER_ENV_KEYS, type PricingRow, type ProfileToRowOptions, PromptIR, Provider, ProviderOverrides, type ProviderReachability, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type RunAdvisorPhase2Context, type SupportedProvider, buildLLMJudge, call, clearBrain, compile, configureBrain, countTokens, execute, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getReachabilityDiagnostic, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStarterChain, getStarterChainWithGrounding, isBrainQueryActiveFor, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, profileToRow, record, recordOutcome, resetTokenizer, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
package/dist/index.d.ts CHANGED
@@ -326,14 +326,47 @@ declare function countTokens(text: string): number;
326
326
 
327
327
  /** Subset of CompileResult fields the advisor needs. */
328
328
  type AdvisorContext = Pick<CompileResult, 'target' | 'provider' | 'tokensIn' | 'diagnostics'>;
329
+ /**
330
+ * Optional Phase 2 (alpha.22) context — fallback chain + a profile resolver
331
+ * for cross-model comparison. Three new rules
332
+ * (`cost-mismatched-archetype`, `model-stale-evidence`, `tier-down`) consume
333
+ * this to surface measurement-substrate signals (alpha.20 clean-attribution +
334
+ * alpha.21 grounding labels) as actionable consumer guidance.
335
+ *
336
+ * When `fallbackChain` is empty, rules 1 + 3 stay silent (nothing to
337
+ * compare against). When `profileResolver` is omitted, the rules degrade
338
+ * gracefully — they can still inspect the chosen profile but not chain
339
+ * alternatives. Rule 2 (`model-stale-evidence`) is independent of chain
340
+ * shape and works on the chosen model alone.
341
+ */
342
+ interface RunAdvisorPhase2Context {
343
+ fallbackChain: string[];
344
+ profileResolver?: (id: string) => ModelProfile | undefined;
345
+ }
329
346
  /**
330
347
  * Run all phased rules and return collected advisories. Order is fixed so
331
348
  * output is stable across runs. The `policy` argument is alpha.9 — the
332
349
  * `single-model-array` rule needs to know whether the consumer explicitly
333
350
  * declared `posture: 'locked'` (in which case single-model is intentional
334
351
  * and shouldn't warn).
352
+ *
353
+ * `phase2` is alpha.22 — gives the advisor access to the fallback chain +
354
+ * a profile resolver so the three new compile-time recommendation rules
355
+ * (`cost-mismatched-archetype`, `model-stale-evidence`, `tier-down`) can
356
+ * compare the chosen model against in-chain alternatives. Optional for
357
+ * backward compatibility with consumers calling `runAdvisor()` directly.
358
+ */
359
+ declare function runAdvisor(ir: PromptIR, result: AdvisorContext, profile: ModelProfile, policy?: CompilePolicy, phase2?: RunAdvisorPhase2Context): BestPracticeAdvisory[];
360
+
361
+ /**
362
+ * alpha.22 — sync introspection: is brain-query mode active for a given
363
+ * table? Used by the advisor (`model-stale-evidence` rule) to decide
364
+ * whether a `judgment`-grounded chosen model is a measurement gap worth
365
+ * surfacing. Returns false on cold start, when configureBrain() was never
366
+ * called, or when the consumer explicitly opted the table out via
367
+ * `BrainConfig.brainQuery.<table> = false`.
335
368
  */
336
- declare function runAdvisor(ir: PromptIR, result: AdvisorContext, profile: ModelProfile, policy?: CompilePolicy): BestPracticeAdvisory[];
369
+ declare function isBrainQueryActiveFor(table: string): boolean;
337
370
 
338
371
  /**
339
372
  * env.ts — provider env-key resolution + reachability predicates.
@@ -870,4 +903,4 @@ declare const loadAliasesFromBrain: () => Record<string, string>;
870
903
  */
871
904
  declare function compile(ir: PromptIR, opts?: CompileOptions): CompileResult;
872
905
 
873
- export { ApiKeys, type AppOracle, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, CallOptions, CallResult, ChainEntry, type CompileOptions, CompilePolicy, CompileResult, CompiledRequest, type ExecuteErr, type ExecuteOk, type ExecuteOptions, type ExecuteResult, type FallbackPosture, type GetDefaultFallbackChainOpts, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type ModelBrainRow, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PROVIDER_ENV_KEYS, type PricingRow, type ProfileToRowOptions, PromptIR, Provider, ProviderOverrides, type ProviderReachability, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type SupportedProvider, buildLLMJudge, call, clearBrain, compile, configureBrain, countTokens, execute, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getReachabilityDiagnostic, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStarterChain, getStarterChainWithGrounding, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, profileToRow, record, recordOutcome, resetTokenizer, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
906
+ export { ApiKeys, type AppOracle, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, CallOptions, CallResult, ChainEntry, type CompileOptions, CompilePolicy, CompileResult, CompiledRequest, type ExecuteErr, type ExecuteOk, type ExecuteOptions, type ExecuteResult, type FallbackPosture, type GetDefaultFallbackChainOpts, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type ModelBrainRow, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PROVIDER_ENV_KEYS, type PricingRow, type ProfileToRowOptions, PromptIR, Provider, ProviderOverrides, type ProviderReachability, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type RunAdvisorPhase2Context, type SupportedProvider, buildLLMJudge, call, clearBrain, compile, configureBrain, countTokens, execute, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getReachabilityDiagnostic, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStarterChain, getStarterChainWithGrounding, isBrainQueryActiveFor, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, profileToRow, record, recordOutcome, resetTokenizer, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
package/dist/index.js CHANGED
@@ -51,6 +51,7 @@ __export(index_exports, {
51
51
  getStarterChainWithGrounding: () => getStarterChainWithGrounding,
52
52
  hashShape: () => hashShape,
53
53
  isArchetype: () => isArchetype,
54
+ isBrainQueryActiveFor: () => isBrainQueryActiveFor,
54
55
  isModelReachable: () => isModelReachable,
55
56
  isProviderReachable: () => isProviderReachable,
56
57
  learningKey: () => learningKey,
@@ -1944,14 +1945,160 @@ function profilesByProvider(provider) {
1944
1945
  return PROFILES_RAW.filter((p) => p.provider === provider);
1945
1946
  }
1946
1947
 
1948
+ // src/brain-query.ts
1949
+ var FRESH_SNAPSHOT = {
1950
+ data: null,
1951
+ expiresAt: 0,
1952
+ refreshing: false,
1953
+ warned: false
1954
+ };
1955
+ var snapshot = { ...FRESH_SNAPSHOT };
1956
+ var runtime;
1957
+ function configureBrainQuery(rt) {
1958
+ runtime = rt;
1959
+ snapshot = { ...FRESH_SNAPSHOT };
1960
+ }
1961
+ function createBrainQueryCache(opts) {
1962
+ return () => {
1963
+ const rt = runtime;
1964
+ if (!rt || !rt.enabledTables.has(opts.table)) {
1965
+ return opts.bundledFallback();
1966
+ }
1967
+ const now = Date.now();
1968
+ const stale = snapshot.expiresAt <= now;
1969
+ if (stale && !snapshot.refreshing) {
1970
+ snapshot.refreshing = true;
1971
+ void asyncRefresh(rt);
1972
+ }
1973
+ if (snapshot.data) {
1974
+ const rows = snapshot.data[opts.table];
1975
+ if (Array.isArray(rows) && rows.length > 0) {
1976
+ try {
1977
+ return opts.mapRows(rows);
1978
+ } catch {
1979
+ return opts.bundledFallback();
1980
+ }
1981
+ }
1982
+ }
1983
+ return opts.bundledFallback();
1984
+ };
1985
+ }
1986
+ var pendingRefresh;
1987
+ async function asyncRefresh(rt) {
1988
+ const promise = doRefresh(rt);
1989
+ pendingRefresh = promise;
1990
+ try {
1991
+ await promise;
1992
+ } finally {
1993
+ if (pendingRefresh === promise) pendingRefresh = void 0;
1994
+ }
1995
+ }
1996
+ var DEFAULT_CONFIG_URL = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/config";
1997
+ async function doRefresh(rt) {
1998
+ const url = rt.configEndpoint ?? DEFAULT_CONFIG_URL;
1999
+ try {
2000
+ const res = await rt.fetchImpl(url, { method: "GET" });
2001
+ if (!res.ok) {
2002
+ throw new Error(`brain-query ${res.status}: ${res.statusText}`);
2003
+ }
2004
+ const body = await res.json();
2005
+ if (runtime !== rt) return;
2006
+ snapshot = {
2007
+ data: body,
2008
+ expiresAt: Date.now() + rt.ttlMs,
2009
+ refreshing: false,
2010
+ warned: snapshot.warned
2011
+ };
2012
+ } catch (err) {
2013
+ if (runtime !== rt) return;
2014
+ snapshot.refreshing = false;
2015
+ snapshot.expiresAt = Date.now() + rt.ttlMs;
2016
+ if (!snapshot.warned) {
2017
+ snapshot.warned = true;
2018
+ (rt.onError ?? defaultOnError)(err);
2019
+ }
2020
+ }
2021
+ }
2022
+ function defaultOnError(err) {
2023
+ console.warn("[kgauto] brain-query failed (using bundled fallback):", err);
2024
+ }
2025
+ function isBrainQueryActiveFor(table) {
2026
+ return runtime !== void 0 && runtime.enabledTables.has(table);
2027
+ }
2028
+
2029
+ // src/archetype-perf-brain.ts
2030
+ function isPerfRow(x) {
2031
+ if (!x || typeof x !== "object") return false;
2032
+ const r = x;
2033
+ return typeof r.model_id === "string" && typeof r.archetype === "string" && typeof r.perf_score === "number";
2034
+ }
2035
+ function mapRowsToPerfMap(rows) {
2036
+ const out = /* @__PURE__ */ new Map();
2037
+ for (const row of rows) {
2038
+ if (!isPerfRow(row)) continue;
2039
+ const existing = out.get(row.model_id) ?? {};
2040
+ existing[row.archetype] = row.perf_score;
2041
+ out.set(row.model_id, existing);
2042
+ }
2043
+ return out;
2044
+ }
2045
+ function mapRowsToNMap(rows) {
2046
+ const out = /* @__PURE__ */ new Map();
2047
+ for (const row of rows) {
2048
+ if (!isPerfRow(row)) continue;
2049
+ if (typeof row.n !== "number") continue;
2050
+ const existing = out.get(row.model_id) ?? {};
2051
+ existing[row.archetype] = row.n;
2052
+ out.set(row.model_id, existing);
2053
+ }
2054
+ return out;
2055
+ }
2056
+ function bundledArchetypePerf() {
2057
+ const out = /* @__PURE__ */ new Map();
2058
+ for (const profile of allProfiles()) {
2059
+ if (profile.archetypePerf) out.set(profile.id, profile.archetypePerf);
2060
+ }
2061
+ return out;
2062
+ }
2063
+ function bundledArchetypePerfN() {
2064
+ return /* @__PURE__ */ new Map();
2065
+ }
2066
+ var loadArchetypePerfFromBrain = createBrainQueryCache({
2067
+ table: "kgauto_archetype_perf",
2068
+ mapRows: mapRowsToPerfMap,
2069
+ bundledFallback: bundledArchetypePerf
2070
+ });
2071
+ var loadArchetypePerfNFromBrain = createBrainQueryCache(
2072
+ {
2073
+ table: "kgauto_archetype_perf",
2074
+ mapRows: mapRowsToNMap,
2075
+ bundledFallback: bundledArchetypePerfN
2076
+ }
2077
+ );
2078
+ var MEASURED_GROUNDING_MIN_N = 10;
2079
+ function getArchetypePerfScore(modelId, archetype) {
2080
+ const score = loadArchetypePerfFromBrain().get(modelId)?.[archetype] ?? 5;
2081
+ const n = loadArchetypePerfNFromBrain().get(modelId)?.[archetype] ?? 0;
2082
+ const grounding = n >= MEASURED_GROUNDING_MIN_N ? "measured" : "judgment";
2083
+ return { score, n, grounding };
2084
+ }
2085
+
1947
2086
  // src/advisor.ts
1948
- function runAdvisor(ir, result, profile, policy) {
2087
+ var QUALITY_FLOOR_FOR_RECOMMENDATION = 6;
2088
+ var TIER_DOWN_COST_RATIO = 0.5;
2089
+ var COST_MISMATCHED_CHOSEN_SCORE_CEILING = 7;
2090
+ function runAdvisor(ir, result, profile, policy, phase2) {
1949
2091
  const out = [];
1950
2092
  out.push(...detectCachingOff(ir, profile));
1951
2093
  out.push(...detectSingleChunkSystem(ir, profile));
1952
2094
  out.push(...detectToolBloat(ir, result));
1953
2095
  out.push(...detectHistoryUncached(ir, profile));
1954
2096
  out.push(...detectSingleModelArray(ir, policy));
2097
+ if (policy?.posture !== "locked") {
2098
+ out.push(...detectCostMismatchedArchetype(ir, profile, phase2));
2099
+ out.push(...detectModelStaleEvidence(ir, profile));
2100
+ out.push(...detectTierDown(ir, profile, phase2));
2101
+ }
1955
2102
  return out;
1956
2103
  }
1957
2104
  function detectCachingOff(ir, profile) {
@@ -2037,6 +2184,91 @@ function detectSingleModelArray(ir, policy) {
2037
2184
  }
2038
2185
  ];
2039
2186
  }
2187
+ function detectCostMismatchedArchetype(ir, profile, phase2) {
2188
+ if (!phase2 || phase2.fallbackChain.length === 0) return [];
2189
+ if (!phase2.profileResolver) return [];
2190
+ const archetype = ir.intent.archetype;
2191
+ const chosenScore = getArchetypePerfScore(profile.id, archetype);
2192
+ const chosenHasRoomToGrow = chosenScore.grounding === "judgment" || chosenScore.score < COST_MISMATCHED_CHOSEN_SCORE_CEILING;
2193
+ if (!chosenHasRoomToGrow) return [];
2194
+ let bestAlt = null;
2195
+ for (const altId of phase2.fallbackChain) {
2196
+ const altProfile = phase2.profileResolver(altId);
2197
+ if (!altProfile) continue;
2198
+ if (altProfile.id === profile.id) continue;
2199
+ const altScore = getArchetypePerfScore(altProfile.id, archetype);
2200
+ if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
2201
+ if (altScore.score < chosenScore.score) continue;
2202
+ if (altProfile.costInputPer1m >= profile.costInputPer1m) continue;
2203
+ if (!bestAlt || altScore.score > bestAlt.score.score || altScore.score === bestAlt.score.score && altProfile.costInputPer1m < bestAlt.profile.costInputPer1m) {
2204
+ bestAlt = { id: altId, profile: altProfile, score: altScore };
2205
+ }
2206
+ }
2207
+ if (!bestAlt) return [];
2208
+ const tierDownWouldFire = bestAlt.score.grounding === "measured" && bestAlt.profile.costInputPer1m <= profile.costInputPer1m * TIER_DOWN_COST_RATIO;
2209
+ if (tierDownWouldFire) return [];
2210
+ const chosenGrounding = chosenScore.grounding === "judgment" ? `archetypePerf.${archetype}=judgment` : `archetypePerf.${archetype}=${chosenScore.score}`;
2211
+ const altGrounding = bestAlt.score.grounding === "measured" ? `archetypePerf.${archetype}=${bestAlt.score.score}, measured, n=${bestAlt.score.n}` : `archetypePerf.${archetype}=${bestAlt.score.score}, judgment`;
2212
+ return [
2213
+ {
2214
+ level: "warn",
2215
+ code: "cost-mismatched-archetype",
2216
+ message: `Cost-mismatched-archetype: target=${profile.id} (${chosenGrounding}) selected for ${archetype}. Alternative ${bestAlt.id} (${altGrounding}) is cheaper ($${bestAlt.profile.costInputPer1m}/$${bestAlt.profile.costOutputPer1m} vs $${profile.costInputPer1m}/$${profile.costOutputPer1m} per 1M) at equal-or-better quality.`,
2217
+ suggestion: `Consider declaring \`${bestAlt.id}\` as the primary model for this archetype, or relax to posture='open' to let kgauto select among the chain. If the chosen model is required for compliance/brand reasons, set \`policy.posture = 'locked'\` to silence this rule.`,
2218
+ recommendationType: profile.provider === bestAlt.profile.provider ? "tier-down" : "model-swap",
2219
+ docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
2220
+ }
2221
+ ];
2222
+ }
2223
+ function detectModelStaleEvidence(ir, profile) {
2224
+ if (!isBrainQueryActiveFor("kgauto_archetype_perf")) return [];
2225
+ const archetype = ir.intent.archetype;
2226
+ const chosen = getArchetypePerfScore(profile.id, archetype);
2227
+ if (chosen.grounding !== "judgment") return [];
2228
+ return [
2229
+ {
2230
+ level: "info",
2231
+ code: "model-stale-evidence",
2232
+ message: `Model-stale-evidence: target=${profile.id} archetype=${archetype} is judgment-grounded (n=${chosen.n}) despite brain-query mode being active. Measurement substrate is wired but the brain hasn't accumulated >=10 outcomes for this (model, archetype) tuple yet \u2014 routing decisions remain pre-measured for this slot.`,
2233
+ suggestion: "Verify that `record()` is being called on every call() outcome with the appropriate `actualModel` and `mutationsApplied` fields. Once the brain accumulates n>=10 rows on this tuple, the score promotes from judgment to measured automatically (5-min SWR cache). No code change required from your side \u2014 this is the substrate signaling the gap.",
2234
+ recommendationType: "prompt-fix",
2235
+ docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
2236
+ }
2237
+ ];
2238
+ }
2239
+ function detectTierDown(ir, profile, phase2) {
2240
+ if (!phase2 || phase2.fallbackChain.length === 0) return [];
2241
+ if (!phase2.profileResolver) return [];
2242
+ const archetype = ir.intent.archetype;
2243
+ const chosenScore = getArchetypePerfScore(profile.id, archetype);
2244
+ const chosenCost = profile.costInputPer1m;
2245
+ let bestAlt = null;
2246
+ for (const altId of phase2.fallbackChain) {
2247
+ const altProfile = phase2.profileResolver(altId);
2248
+ if (!altProfile) continue;
2249
+ if (altProfile.id === profile.id) continue;
2250
+ const altScore = getArchetypePerfScore(altProfile.id, archetype);
2251
+ if (altScore.grounding !== "measured") continue;
2252
+ if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
2253
+ if (altScore.score < chosenScore.score) continue;
2254
+ if (altProfile.costInputPer1m > chosenCost * TIER_DOWN_COST_RATIO) continue;
2255
+ if (!bestAlt || altProfile.costInputPer1m < bestAlt.profile.costInputPer1m || altProfile.costInputPer1m === bestAlt.profile.costInputPer1m && altScore.score > bestAlt.score.score) {
2256
+ bestAlt = { id: altId, profile: altProfile, score: altScore };
2257
+ }
2258
+ }
2259
+ if (!bestAlt) return [];
2260
+ const chosenDesc = chosenScore.grounding === "measured" ? `archetypePerf.${archetype}=${chosenScore.score} (measured, n=${chosenScore.n})` : `archetypePerf.${archetype}=${chosenScore.score} (${chosenScore.grounding})`;
2261
+ return [
2262
+ {
2263
+ level: "warn",
2264
+ code: "tier-down",
2265
+ message: `Tier-down: target=${profile.id} (${chosenDesc}) selected for ${archetype}. Brain shows ${bestAlt.id} delivers equal-or-better quality (archetypePerf.${archetype}=${bestAlt.score.score}, measured, n=${bestAlt.score.n}) at $${bestAlt.profile.costInputPer1m}/$${bestAlt.profile.costOutputPer1m} per 1M vs $${profile.costInputPer1m}/$${profile.costOutputPer1m} \u2014 a measured tier-down opportunity.`,
2266
+ suggestion: `Move \`${bestAlt.id}\` to primary for this archetype. The brain has n=${bestAlt.score.n} measured outcomes backing the recommendation; this is data, not opinion. If posture='locked' is required (compliance/brand promise), set it explicitly to silence this rule.`,
2267
+ recommendationType: "tier-down",
2268
+ docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
2269
+ }
2270
+ ];
2271
+ }
2040
2272
 
2041
2273
  // src/compile.ts
2042
2274
  var counter = 0;
@@ -2112,6 +2344,13 @@ function compile(ir, opts = {}) {
2112
2344
  description: "ir.constraints.toolOrchestration='sequential' selected the DeepSeek-tier-0 hunt chain overlay (L-040 parallel-tool cliff doesn't apply at single-step granularity)."
2113
2345
  });
2114
2346
  }
2347
+ const phase2ProfileResolver = opts.profileResolver ? (id) => {
2348
+ try {
2349
+ return opts.profileResolver(id);
2350
+ } catch {
2351
+ return void 0;
2352
+ }
2353
+ } : tryGetProfile;
2115
2354
  const advisories = runAdvisor(
2116
2355
  ir,
2117
2356
  {
@@ -2121,7 +2360,11 @@ function compile(ir, opts = {}) {
2121
2360
  diagnostics
2122
2361
  },
2123
2362
  profile,
2124
- opts.policy
2363
+ opts.policy,
2364
+ {
2365
+ fallbackChain,
2366
+ profileResolver: phase2ProfileResolver
2367
+ }
2125
2368
  );
2126
2369
  return {
2127
2370
  handle,
@@ -2174,84 +2417,6 @@ function validateFinalFit(ir, profile, tokens) {
2174
2417
  }
2175
2418
  }
2176
2419
 
2177
- // src/brain-query.ts
2178
- var FRESH_SNAPSHOT = {
2179
- data: null,
2180
- expiresAt: 0,
2181
- refreshing: false,
2182
- warned: false
2183
- };
2184
- var snapshot = { ...FRESH_SNAPSHOT };
2185
- var runtime;
2186
- function configureBrainQuery(rt) {
2187
- runtime = rt;
2188
- snapshot = { ...FRESH_SNAPSHOT };
2189
- }
2190
- function createBrainQueryCache(opts) {
2191
- return () => {
2192
- const rt = runtime;
2193
- if (!rt || !rt.enabledTables.has(opts.table)) {
2194
- return opts.bundledFallback();
2195
- }
2196
- const now = Date.now();
2197
- const stale = snapshot.expiresAt <= now;
2198
- if (stale && !snapshot.refreshing) {
2199
- snapshot.refreshing = true;
2200
- void asyncRefresh(rt);
2201
- }
2202
- if (snapshot.data) {
2203
- const rows = snapshot.data[opts.table];
2204
- if (Array.isArray(rows) && rows.length > 0) {
2205
- try {
2206
- return opts.mapRows(rows);
2207
- } catch {
2208
- return opts.bundledFallback();
2209
- }
2210
- }
2211
- }
2212
- return opts.bundledFallback();
2213
- };
2214
- }
2215
- var pendingRefresh;
2216
- async function asyncRefresh(rt) {
2217
- const promise = doRefresh(rt);
2218
- pendingRefresh = promise;
2219
- try {
2220
- await promise;
2221
- } finally {
2222
- if (pendingRefresh === promise) pendingRefresh = void 0;
2223
- }
2224
- }
2225
- var DEFAULT_CONFIG_URL = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/config";
2226
- async function doRefresh(rt) {
2227
- const url = rt.configEndpoint ?? DEFAULT_CONFIG_URL;
2228
- try {
2229
- const res = await rt.fetchImpl(url, { method: "GET" });
2230
- if (!res.ok) {
2231
- throw new Error(`brain-query ${res.status}: ${res.statusText}`);
2232
- }
2233
- const body = await res.json();
2234
- if (runtime !== rt) return;
2235
- snapshot = {
2236
- data: body,
2237
- expiresAt: Date.now() + rt.ttlMs,
2238
- refreshing: false,
2239
- warned: snapshot.warned
2240
- };
2241
- } catch (err) {
2242
- if (runtime !== rt) return;
2243
- snapshot.refreshing = false;
2244
- snapshot.expiresAt = Date.now() + rt.ttlMs;
2245
- if (!snapshot.warned) {
2246
- snapshot.warned = true;
2247
- (rt.onError ?? defaultOnError)(err);
2248
- }
2249
- }
2250
- }
2251
- function defaultOnError(err) {
2252
- console.warn("[kgauto] brain-query failed (using bundled fallback):", err);
2253
- }
2254
-
2255
2420
  // src/pricing-brain.ts
2256
2421
  function isPricingRow(x) {
2257
2422
  if (!x || typeof x !== "object") return false;
@@ -3923,63 +4088,6 @@ function clamp(n) {
3923
4088
  return Math.max(0, Math.min(1, n));
3924
4089
  }
3925
4090
 
3926
- // src/archetype-perf-brain.ts
3927
- function isPerfRow(x) {
3928
- if (!x || typeof x !== "object") return false;
3929
- const r = x;
3930
- return typeof r.model_id === "string" && typeof r.archetype === "string" && typeof r.perf_score === "number";
3931
- }
3932
- function mapRowsToPerfMap(rows) {
3933
- const out = /* @__PURE__ */ new Map();
3934
- for (const row of rows) {
3935
- if (!isPerfRow(row)) continue;
3936
- const existing = out.get(row.model_id) ?? {};
3937
- existing[row.archetype] = row.perf_score;
3938
- out.set(row.model_id, existing);
3939
- }
3940
- return out;
3941
- }
3942
- function mapRowsToNMap(rows) {
3943
- const out = /* @__PURE__ */ new Map();
3944
- for (const row of rows) {
3945
- if (!isPerfRow(row)) continue;
3946
- if (typeof row.n !== "number") continue;
3947
- const existing = out.get(row.model_id) ?? {};
3948
- existing[row.archetype] = row.n;
3949
- out.set(row.model_id, existing);
3950
- }
3951
- return out;
3952
- }
3953
- function bundledArchetypePerf() {
3954
- const out = /* @__PURE__ */ new Map();
3955
- for (const profile of allProfiles()) {
3956
- if (profile.archetypePerf) out.set(profile.id, profile.archetypePerf);
3957
- }
3958
- return out;
3959
- }
3960
- function bundledArchetypePerfN() {
3961
- return /* @__PURE__ */ new Map();
3962
- }
3963
- var loadArchetypePerfFromBrain = createBrainQueryCache({
3964
- table: "kgauto_archetype_perf",
3965
- mapRows: mapRowsToPerfMap,
3966
- bundledFallback: bundledArchetypePerf
3967
- });
3968
- var loadArchetypePerfNFromBrain = createBrainQueryCache(
3969
- {
3970
- table: "kgauto_archetype_perf",
3971
- mapRows: mapRowsToNMap,
3972
- bundledFallback: bundledArchetypePerfN
3973
- }
3974
- );
3975
- var MEASURED_GROUNDING_MIN_N = 10;
3976
- function getArchetypePerfScore(modelId, archetype) {
3977
- const score = loadArchetypePerfFromBrain().get(modelId)?.[archetype] ?? 5;
3978
- const n = loadArchetypePerfNFromBrain().get(modelId)?.[archetype] ?? 0;
3979
- const grounding = n >= MEASURED_GROUNDING_MIN_N ? "measured" : "judgment";
3980
- return { score, n, grounding };
3981
- }
3982
-
3983
4091
  // src/models-brain.ts
3984
4092
  function isModelRow(x) {
3985
4093
  if (!x || typeof x !== "object") return false;
@@ -4138,6 +4246,7 @@ function compile2(ir, opts) {
4138
4246
  getStarterChainWithGrounding,
4139
4247
  hashShape,
4140
4248
  isArchetype,
4249
+ isBrainQueryActiveFor,
4141
4250
  isModelReachable,
4142
4251
  isProviderReachable,
4143
4252
  learningKey,
package/dist/index.mjs CHANGED
@@ -740,14 +740,160 @@ function setNestedField(obj, path, value) {
740
740
  cursor[parts[parts.length - 1]] = value;
741
741
  }
742
742
 
743
+ // src/brain-query.ts
744
+ var FRESH_SNAPSHOT = {
745
+ data: null,
746
+ expiresAt: 0,
747
+ refreshing: false,
748
+ warned: false
749
+ };
750
+ var snapshot = { ...FRESH_SNAPSHOT };
751
+ var runtime;
752
+ function configureBrainQuery(rt) {
753
+ runtime = rt;
754
+ snapshot = { ...FRESH_SNAPSHOT };
755
+ }
756
+ function createBrainQueryCache(opts) {
757
+ return () => {
758
+ const rt = runtime;
759
+ if (!rt || !rt.enabledTables.has(opts.table)) {
760
+ return opts.bundledFallback();
761
+ }
762
+ const now = Date.now();
763
+ const stale = snapshot.expiresAt <= now;
764
+ if (stale && !snapshot.refreshing) {
765
+ snapshot.refreshing = true;
766
+ void asyncRefresh(rt);
767
+ }
768
+ if (snapshot.data) {
769
+ const rows = snapshot.data[opts.table];
770
+ if (Array.isArray(rows) && rows.length > 0) {
771
+ try {
772
+ return opts.mapRows(rows);
773
+ } catch {
774
+ return opts.bundledFallback();
775
+ }
776
+ }
777
+ }
778
+ return opts.bundledFallback();
779
+ };
780
+ }
781
+ var pendingRefresh;
782
+ async function asyncRefresh(rt) {
783
+ const promise = doRefresh(rt);
784
+ pendingRefresh = promise;
785
+ try {
786
+ await promise;
787
+ } finally {
788
+ if (pendingRefresh === promise) pendingRefresh = void 0;
789
+ }
790
+ }
791
+ var DEFAULT_CONFIG_URL = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/config";
792
+ async function doRefresh(rt) {
793
+ const url = rt.configEndpoint ?? DEFAULT_CONFIG_URL;
794
+ try {
795
+ const res = await rt.fetchImpl(url, { method: "GET" });
796
+ if (!res.ok) {
797
+ throw new Error(`brain-query ${res.status}: ${res.statusText}`);
798
+ }
799
+ const body = await res.json();
800
+ if (runtime !== rt) return;
801
+ snapshot = {
802
+ data: body,
803
+ expiresAt: Date.now() + rt.ttlMs,
804
+ refreshing: false,
805
+ warned: snapshot.warned
806
+ };
807
+ } catch (err) {
808
+ if (runtime !== rt) return;
809
+ snapshot.refreshing = false;
810
+ snapshot.expiresAt = Date.now() + rt.ttlMs;
811
+ if (!snapshot.warned) {
812
+ snapshot.warned = true;
813
+ (rt.onError ?? defaultOnError)(err);
814
+ }
815
+ }
816
+ }
817
+ function defaultOnError(err) {
818
+ console.warn("[kgauto] brain-query failed (using bundled fallback):", err);
819
+ }
820
+ function isBrainQueryActiveFor(table) {
821
+ return runtime !== void 0 && runtime.enabledTables.has(table);
822
+ }
823
+
824
+ // src/archetype-perf-brain.ts
825
+ function isPerfRow(x) {
826
+ if (!x || typeof x !== "object") return false;
827
+ const r = x;
828
+ return typeof r.model_id === "string" && typeof r.archetype === "string" && typeof r.perf_score === "number";
829
+ }
830
+ function mapRowsToPerfMap(rows) {
831
+ const out = /* @__PURE__ */ new Map();
832
+ for (const row of rows) {
833
+ if (!isPerfRow(row)) continue;
834
+ const existing = out.get(row.model_id) ?? {};
835
+ existing[row.archetype] = row.perf_score;
836
+ out.set(row.model_id, existing);
837
+ }
838
+ return out;
839
+ }
840
+ function mapRowsToNMap(rows) {
841
+ const out = /* @__PURE__ */ new Map();
842
+ for (const row of rows) {
843
+ if (!isPerfRow(row)) continue;
844
+ if (typeof row.n !== "number") continue;
845
+ const existing = out.get(row.model_id) ?? {};
846
+ existing[row.archetype] = row.n;
847
+ out.set(row.model_id, existing);
848
+ }
849
+ return out;
850
+ }
851
+ function bundledArchetypePerf() {
852
+ const out = /* @__PURE__ */ new Map();
853
+ for (const profile of allProfiles()) {
854
+ if (profile.archetypePerf) out.set(profile.id, profile.archetypePerf);
855
+ }
856
+ return out;
857
+ }
858
+ function bundledArchetypePerfN() {
859
+ return /* @__PURE__ */ new Map();
860
+ }
861
+ var loadArchetypePerfFromBrain = createBrainQueryCache({
862
+ table: "kgauto_archetype_perf",
863
+ mapRows: mapRowsToPerfMap,
864
+ bundledFallback: bundledArchetypePerf
865
+ });
866
+ var loadArchetypePerfNFromBrain = createBrainQueryCache(
867
+ {
868
+ table: "kgauto_archetype_perf",
869
+ mapRows: mapRowsToNMap,
870
+ bundledFallback: bundledArchetypePerfN
871
+ }
872
+ );
873
+ var MEASURED_GROUNDING_MIN_N = 10;
874
+ function getArchetypePerfScore(modelId, archetype) {
875
+ const score = loadArchetypePerfFromBrain().get(modelId)?.[archetype] ?? 5;
876
+ const n = loadArchetypePerfNFromBrain().get(modelId)?.[archetype] ?? 0;
877
+ const grounding = n >= MEASURED_GROUNDING_MIN_N ? "measured" : "judgment";
878
+ return { score, n, grounding };
879
+ }
880
+
743
881
  // src/advisor.ts
744
- function runAdvisor(ir, result, profile, policy) {
882
+ var QUALITY_FLOOR_FOR_RECOMMENDATION = 6;
883
+ var TIER_DOWN_COST_RATIO = 0.5;
884
+ var COST_MISMATCHED_CHOSEN_SCORE_CEILING = 7;
885
+ function runAdvisor(ir, result, profile, policy, phase2) {
745
886
  const out = [];
746
887
  out.push(...detectCachingOff(ir, profile));
747
888
  out.push(...detectSingleChunkSystem(ir, profile));
748
889
  out.push(...detectToolBloat(ir, result));
749
890
  out.push(...detectHistoryUncached(ir, profile));
750
891
  out.push(...detectSingleModelArray(ir, policy));
892
+ if (policy?.posture !== "locked") {
893
+ out.push(...detectCostMismatchedArchetype(ir, profile, phase2));
894
+ out.push(...detectModelStaleEvidence(ir, profile));
895
+ out.push(...detectTierDown(ir, profile, phase2));
896
+ }
751
897
  return out;
752
898
  }
753
899
  function detectCachingOff(ir, profile) {
@@ -833,6 +979,91 @@ function detectSingleModelArray(ir, policy) {
833
979
  }
834
980
  ];
835
981
  }
982
+ function detectCostMismatchedArchetype(ir, profile, phase2) {
983
+ if (!phase2 || phase2.fallbackChain.length === 0) return [];
984
+ if (!phase2.profileResolver) return [];
985
+ const archetype = ir.intent.archetype;
986
+ const chosenScore = getArchetypePerfScore(profile.id, archetype);
987
+ const chosenHasRoomToGrow = chosenScore.grounding === "judgment" || chosenScore.score < COST_MISMATCHED_CHOSEN_SCORE_CEILING;
988
+ if (!chosenHasRoomToGrow) return [];
989
+ let bestAlt = null;
990
+ for (const altId of phase2.fallbackChain) {
991
+ const altProfile = phase2.profileResolver(altId);
992
+ if (!altProfile) continue;
993
+ if (altProfile.id === profile.id) continue;
994
+ const altScore = getArchetypePerfScore(altProfile.id, archetype);
995
+ if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
996
+ if (altScore.score < chosenScore.score) continue;
997
+ if (altProfile.costInputPer1m >= profile.costInputPer1m) continue;
998
+ if (!bestAlt || altScore.score > bestAlt.score.score || altScore.score === bestAlt.score.score && altProfile.costInputPer1m < bestAlt.profile.costInputPer1m) {
999
+ bestAlt = { id: altId, profile: altProfile, score: altScore };
1000
+ }
1001
+ }
1002
+ if (!bestAlt) return [];
1003
+ const tierDownWouldFire = bestAlt.score.grounding === "measured" && bestAlt.profile.costInputPer1m <= profile.costInputPer1m * TIER_DOWN_COST_RATIO;
1004
+ if (tierDownWouldFire) return [];
1005
+ const chosenGrounding = chosenScore.grounding === "judgment" ? `archetypePerf.${archetype}=judgment` : `archetypePerf.${archetype}=${chosenScore.score}`;
1006
+ const altGrounding = bestAlt.score.grounding === "measured" ? `archetypePerf.${archetype}=${bestAlt.score.score}, measured, n=${bestAlt.score.n}` : `archetypePerf.${archetype}=${bestAlt.score.score}, judgment`;
1007
+ return [
1008
+ {
1009
+ level: "warn",
1010
+ code: "cost-mismatched-archetype",
1011
+ message: `Cost-mismatched-archetype: target=${profile.id} (${chosenGrounding}) selected for ${archetype}. Alternative ${bestAlt.id} (${altGrounding}) is cheaper ($${bestAlt.profile.costInputPer1m}/$${bestAlt.profile.costOutputPer1m} vs $${profile.costInputPer1m}/$${profile.costOutputPer1m} per 1M) at equal-or-better quality.`,
1012
+ suggestion: `Consider declaring \`${bestAlt.id}\` as the primary model for this archetype, or relax to posture='open' to let kgauto select among the chain. If the chosen model is required for compliance/brand reasons, set \`policy.posture = 'locked'\` to silence this rule.`,
1013
+ recommendationType: profile.provider === bestAlt.profile.provider ? "tier-down" : "model-swap",
1014
+ docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
1015
+ }
1016
+ ];
1017
+ }
1018
+ function detectModelStaleEvidence(ir, profile) {
1019
+ if (!isBrainQueryActiveFor("kgauto_archetype_perf")) return [];
1020
+ const archetype = ir.intent.archetype;
1021
+ const chosen = getArchetypePerfScore(profile.id, archetype);
1022
+ if (chosen.grounding !== "judgment") return [];
1023
+ return [
1024
+ {
1025
+ level: "info",
1026
+ code: "model-stale-evidence",
1027
+ message: `Model-stale-evidence: target=${profile.id} archetype=${archetype} is judgment-grounded (n=${chosen.n}) despite brain-query mode being active. Measurement substrate is wired but the brain hasn't accumulated >=10 outcomes for this (model, archetype) tuple yet \u2014 routing decisions remain pre-measured for this slot.`,
1028
+ suggestion: "Verify that `record()` is being called on every call() outcome with the appropriate `actualModel` and `mutationsApplied` fields. Once the brain accumulates n>=10 rows on this tuple, the score promotes from judgment to measured automatically (5-min SWR cache). No code change required from your side \u2014 this is the substrate signaling the gap.",
1029
+ recommendationType: "prompt-fix",
1030
+ docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
1031
+ }
1032
+ ];
1033
+ }
1034
+ function detectTierDown(ir, profile, phase2) {
1035
+ if (!phase2 || phase2.fallbackChain.length === 0) return [];
1036
+ if (!phase2.profileResolver) return [];
1037
+ const archetype = ir.intent.archetype;
1038
+ const chosenScore = getArchetypePerfScore(profile.id, archetype);
1039
+ const chosenCost = profile.costInputPer1m;
1040
+ let bestAlt = null;
1041
+ for (const altId of phase2.fallbackChain) {
1042
+ const altProfile = phase2.profileResolver(altId);
1043
+ if (!altProfile) continue;
1044
+ if (altProfile.id === profile.id) continue;
1045
+ const altScore = getArchetypePerfScore(altProfile.id, archetype);
1046
+ if (altScore.grounding !== "measured") continue;
1047
+ if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
1048
+ if (altScore.score < chosenScore.score) continue;
1049
+ if (altProfile.costInputPer1m > chosenCost * TIER_DOWN_COST_RATIO) continue;
1050
+ if (!bestAlt || altProfile.costInputPer1m < bestAlt.profile.costInputPer1m || altProfile.costInputPer1m === bestAlt.profile.costInputPer1m && altScore.score > bestAlt.score.score) {
1051
+ bestAlt = { id: altId, profile: altProfile, score: altScore };
1052
+ }
1053
+ }
1054
+ if (!bestAlt) return [];
1055
+ const chosenDesc = chosenScore.grounding === "measured" ? `archetypePerf.${archetype}=${chosenScore.score} (measured, n=${chosenScore.n})` : `archetypePerf.${archetype}=${chosenScore.score} (${chosenScore.grounding})`;
1056
+ return [
1057
+ {
1058
+ level: "warn",
1059
+ code: "tier-down",
1060
+ message: `Tier-down: target=${profile.id} (${chosenDesc}) selected for ${archetype}. Brain shows ${bestAlt.id} delivers equal-or-better quality (archetypePerf.${archetype}=${bestAlt.score.score}, measured, n=${bestAlt.score.n}) at $${bestAlt.profile.costInputPer1m}/$${bestAlt.profile.costOutputPer1m} per 1M vs $${profile.costInputPer1m}/$${profile.costOutputPer1m} \u2014 a measured tier-down opportunity.`,
1061
+ suggestion: `Move \`${bestAlt.id}\` to primary for this archetype. The brain has n=${bestAlt.score.n} measured outcomes backing the recommendation; this is data, not opinion. If posture='locked' is required (compliance/brand promise), set it explicitly to silence this rule.`,
1062
+ recommendationType: "tier-down",
1063
+ docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
1064
+ }
1065
+ ];
1066
+ }
836
1067
 
837
1068
  // src/compile.ts
838
1069
  var counter = 0;
@@ -908,6 +1139,13 @@ function compile(ir, opts = {}) {
908
1139
  description: "ir.constraints.toolOrchestration='sequential' selected the DeepSeek-tier-0 hunt chain overlay (L-040 parallel-tool cliff doesn't apply at single-step granularity)."
909
1140
  });
910
1141
  }
1142
+ const phase2ProfileResolver = opts.profileResolver ? (id) => {
1143
+ try {
1144
+ return opts.profileResolver(id);
1145
+ } catch {
1146
+ return void 0;
1147
+ }
1148
+ } : tryGetProfile;
911
1149
  const advisories = runAdvisor(
912
1150
  ir,
913
1151
  {
@@ -917,7 +1155,11 @@ function compile(ir, opts = {}) {
917
1155
  diagnostics
918
1156
  },
919
1157
  profile,
920
- opts.policy
1158
+ opts.policy,
1159
+ {
1160
+ fallbackChain,
1161
+ profileResolver: phase2ProfileResolver
1162
+ }
921
1163
  );
922
1164
  return {
923
1165
  handle,
@@ -970,84 +1212,6 @@ function validateFinalFit(ir, profile, tokens) {
970
1212
  }
971
1213
  }
972
1214
 
973
- // src/brain-query.ts
974
- var FRESH_SNAPSHOT = {
975
- data: null,
976
- expiresAt: 0,
977
- refreshing: false,
978
- warned: false
979
- };
980
- var snapshot = { ...FRESH_SNAPSHOT };
981
- var runtime;
982
- function configureBrainQuery(rt) {
983
- runtime = rt;
984
- snapshot = { ...FRESH_SNAPSHOT };
985
- }
986
- function createBrainQueryCache(opts) {
987
- return () => {
988
- const rt = runtime;
989
- if (!rt || !rt.enabledTables.has(opts.table)) {
990
- return opts.bundledFallback();
991
- }
992
- const now = Date.now();
993
- const stale = snapshot.expiresAt <= now;
994
- if (stale && !snapshot.refreshing) {
995
- snapshot.refreshing = true;
996
- void asyncRefresh(rt);
997
- }
998
- if (snapshot.data) {
999
- const rows = snapshot.data[opts.table];
1000
- if (Array.isArray(rows) && rows.length > 0) {
1001
- try {
1002
- return opts.mapRows(rows);
1003
- } catch {
1004
- return opts.bundledFallback();
1005
- }
1006
- }
1007
- }
1008
- return opts.bundledFallback();
1009
- };
1010
- }
1011
- var pendingRefresh;
1012
- async function asyncRefresh(rt) {
1013
- const promise = doRefresh(rt);
1014
- pendingRefresh = promise;
1015
- try {
1016
- await promise;
1017
- } finally {
1018
- if (pendingRefresh === promise) pendingRefresh = void 0;
1019
- }
1020
- }
1021
- var DEFAULT_CONFIG_URL = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/config";
1022
- async function doRefresh(rt) {
1023
- const url = rt.configEndpoint ?? DEFAULT_CONFIG_URL;
1024
- try {
1025
- const res = await rt.fetchImpl(url, { method: "GET" });
1026
- if (!res.ok) {
1027
- throw new Error(`brain-query ${res.status}: ${res.statusText}`);
1028
- }
1029
- const body = await res.json();
1030
- if (runtime !== rt) return;
1031
- snapshot = {
1032
- data: body,
1033
- expiresAt: Date.now() + rt.ttlMs,
1034
- refreshing: false,
1035
- warned: snapshot.warned
1036
- };
1037
- } catch (err) {
1038
- if (runtime !== rt) return;
1039
- snapshot.refreshing = false;
1040
- snapshot.expiresAt = Date.now() + rt.ttlMs;
1041
- if (!snapshot.warned) {
1042
- snapshot.warned = true;
1043
- (rt.onError ?? defaultOnError)(err);
1044
- }
1045
- }
1046
- }
1047
- function defaultOnError(err) {
1048
- console.warn("[kgauto] brain-query failed (using bundled fallback):", err);
1049
- }
1050
-
1051
1215
  // src/pricing-brain.ts
1052
1216
  function isPricingRow(x) {
1053
1217
  if (!x || typeof x !== "object") return false;
@@ -2431,63 +2595,6 @@ function clamp(n) {
2431
2595
  return Math.max(0, Math.min(1, n));
2432
2596
  }
2433
2597
 
2434
- // src/archetype-perf-brain.ts
2435
- function isPerfRow(x) {
2436
- if (!x || typeof x !== "object") return false;
2437
- const r = x;
2438
- return typeof r.model_id === "string" && typeof r.archetype === "string" && typeof r.perf_score === "number";
2439
- }
2440
- function mapRowsToPerfMap(rows) {
2441
- const out = /* @__PURE__ */ new Map();
2442
- for (const row of rows) {
2443
- if (!isPerfRow(row)) continue;
2444
- const existing = out.get(row.model_id) ?? {};
2445
- existing[row.archetype] = row.perf_score;
2446
- out.set(row.model_id, existing);
2447
- }
2448
- return out;
2449
- }
2450
- function mapRowsToNMap(rows) {
2451
- const out = /* @__PURE__ */ new Map();
2452
- for (const row of rows) {
2453
- if (!isPerfRow(row)) continue;
2454
- if (typeof row.n !== "number") continue;
2455
- const existing = out.get(row.model_id) ?? {};
2456
- existing[row.archetype] = row.n;
2457
- out.set(row.model_id, existing);
2458
- }
2459
- return out;
2460
- }
2461
- function bundledArchetypePerf() {
2462
- const out = /* @__PURE__ */ new Map();
2463
- for (const profile of allProfiles()) {
2464
- if (profile.archetypePerf) out.set(profile.id, profile.archetypePerf);
2465
- }
2466
- return out;
2467
- }
2468
- function bundledArchetypePerfN() {
2469
- return /* @__PURE__ */ new Map();
2470
- }
2471
- var loadArchetypePerfFromBrain = createBrainQueryCache({
2472
- table: "kgauto_archetype_perf",
2473
- mapRows: mapRowsToPerfMap,
2474
- bundledFallback: bundledArchetypePerf
2475
- });
2476
- var loadArchetypePerfNFromBrain = createBrainQueryCache(
2477
- {
2478
- table: "kgauto_archetype_perf",
2479
- mapRows: mapRowsToNMap,
2480
- bundledFallback: bundledArchetypePerfN
2481
- }
2482
- );
2483
- var MEASURED_GROUNDING_MIN_N = 10;
2484
- function getArchetypePerfScore(modelId, archetype) {
2485
- const score = loadArchetypePerfFromBrain().get(modelId)?.[archetype] ?? 5;
2486
- const n = loadArchetypePerfNFromBrain().get(modelId)?.[archetype] ?? 0;
2487
- const grounding = n >= MEASURED_GROUNDING_MIN_N ? "measured" : "judgment";
2488
- return { score, n, grounding };
2489
- }
2490
-
2491
2598
  // src/models-brain.ts
2492
2599
  function isModelRow(x) {
2493
2600
  if (!x || typeof x !== "object") return false;
@@ -2645,6 +2752,7 @@ export {
2645
2752
  getStarterChainWithGrounding,
2646
2753
  hashShape,
2647
2754
  isArchetype,
2755
+ isBrainQueryActiveFor,
2648
2756
  isModelReachable,
2649
2757
  isProviderReachable,
2650
2758
  learningKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warmdrift/kgauto-compiler",
3
- "version": "2.0.0-alpha.21",
3
+ "version": "2.0.0-alpha.22",
4
4
  "description": "Prompt compiler + central learning brain for multi-model AI apps. Swap models without rewriting prompts.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",