@warmdrift/kgauto-compiler 2.0.0-alpha.83 → 2.0.0-alpha.86
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/{chunk-VVRDFE6T.mjs → chunk-2MQIVVEU.mjs} +19 -3
- package/dist/{chunk-XPD4I3Q5.mjs → chunk-QDWOMQYN.mjs} +39 -17
- package/dist/{chunk-QEFIAAE7.mjs → chunk-V4T2CBRI.mjs} +1 -1
- package/dist/glassbox/index.d.mts +3 -3
- package/dist/glassbox/index.d.ts +3 -3
- package/dist/glassbox-routes/format.d.mts +2 -2
- package/dist/glassbox-routes/format.d.ts +2 -2
- package/dist/glassbox-routes/index.d.mts +4 -4
- package/dist/glassbox-routes/index.d.ts +4 -4
- package/dist/glassbox-routes/index.js +51 -17
- package/dist/glassbox-routes/index.mjs +2 -2
- package/dist/glassbox-routes/react/index.d.mts +2 -2
- package/dist/glassbox-routes/react/index.d.ts +2 -2
- package/dist/index.d.mts +254 -131
- package/dist/index.d.ts +254 -131
- package/dist/index.js +266 -46
- package/dist/index.mjs +213 -29
- package/dist/{ir-D4S9R816.d.mts → ir-5TJLAYKR.d.mts} +81 -0
- package/dist/{ir-Cx9hJj0B.d.ts → ir-C1zmvAh6.d.ts} +81 -0
- package/dist/key-health.js +1 -1
- package/dist/key-health.mjs +1 -1
- package/dist/profiles.d.mts +11 -2
- package/dist/profiles.d.ts +11 -2
- package/dist/profiles.js +20 -3
- package/dist/profiles.mjs +3 -1
- package/dist/{types-Cvd6kRNv.d.ts → types-8Goj1cKf.d.ts} +1 -1
- package/dist/{types-DHi4FcUu.d.mts → types-BcYPDXgJ.d.mts} +1 -1
- package/dist/{types-CRXaR7nJ.d.mts → types-BiV-qN_x.d.mts} +1 -1
- package/dist/{types-CAs9-0S4.d.ts → types-C2H37XLR.d.ts} +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
LIBRARY_VERSION,
|
|
20
20
|
createKeyHealthRoute,
|
|
21
21
|
keyFingerprint
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-V4T2CBRI.mjs";
|
|
23
23
|
import {
|
|
24
24
|
ABSOLUTE_FLOOR,
|
|
25
25
|
ARCHETYPE_FLOOR_DEFAULT,
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
COST_RANKING_REFERENCE_SHAPE,
|
|
28
28
|
MEASURED_GROUNDING_MIN_N,
|
|
29
29
|
PROVIDER_ENV_KEYS,
|
|
30
|
+
canonicalPolicySet,
|
|
30
31
|
chainProviderSpread,
|
|
31
32
|
configureBrainQuery,
|
|
32
33
|
createBrainQueryCache,
|
|
@@ -52,9 +53,10 @@ import {
|
|
|
52
53
|
loadArchetypePerfFromBrain,
|
|
53
54
|
loadArchetypePerfNFromBrain,
|
|
54
55
|
loadChainsFromBrain,
|
|
56
|
+
policySetHas,
|
|
55
57
|
readBrainReadEnv,
|
|
56
58
|
resolveProviderKey
|
|
57
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-QDWOMQYN.mjs";
|
|
58
60
|
import {
|
|
59
61
|
ALIASES,
|
|
60
62
|
LATENCY_TIER_MS,
|
|
@@ -64,8 +66,9 @@ import {
|
|
|
64
66
|
getProfile,
|
|
65
67
|
latencyTierOf,
|
|
66
68
|
profilesByProvider,
|
|
69
|
+
resolveModelAlias,
|
|
67
70
|
tryGetProfile
|
|
68
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-2MQIVVEU.mjs";
|
|
69
72
|
import {
|
|
70
73
|
emitAdvisoryFired,
|
|
71
74
|
emitCompileDone,
|
|
@@ -680,9 +683,12 @@ function passApplyCliffs(ir, profile, estimatedInputTokens) {
|
|
|
680
683
|
if (!triggered) continue;
|
|
681
684
|
switch (cliff.action) {
|
|
682
685
|
case "drop_to_top_relevant": {
|
|
683
|
-
const targetCount = Math.
|
|
684
|
-
|
|
685
|
-
Math.
|
|
686
|
+
const targetCount = Math.max(
|
|
687
|
+
1,
|
|
688
|
+
Math.min(
|
|
689
|
+
Math.floor(cliff.threshold * 0.75),
|
|
690
|
+
Math.floor((nextIR.tools?.length ?? 0) / 2)
|
|
691
|
+
)
|
|
686
692
|
);
|
|
687
693
|
if (nextIR.tools && nextIR.tools.length > targetCount) {
|
|
688
694
|
const intent = nextIR.intent.archetype;
|
|
@@ -767,8 +773,8 @@ function effectiveConventions(profile) {
|
|
|
767
773
|
function passScoreTargets(ir, opts) {
|
|
768
774
|
const constraints = ir.constraints ?? {};
|
|
769
775
|
const policy = opts.policy ?? {};
|
|
770
|
-
const blockedSet =
|
|
771
|
-
const preferredSet =
|
|
776
|
+
const blockedSet = canonicalPolicySet(policy.blockedModels);
|
|
777
|
+
const preferredSet = canonicalPolicySet(policy.preferredModels);
|
|
772
778
|
const scores = [];
|
|
773
779
|
const policyMutations = [];
|
|
774
780
|
const rawPromotion = opts.promotion;
|
|
@@ -791,7 +797,7 @@ function passScoreTargets(ir, opts) {
|
|
|
791
797
|
continue;
|
|
792
798
|
}
|
|
793
799
|
const reasons = [];
|
|
794
|
-
if (blockedSet
|
|
800
|
+
if (policySetHas(blockedSet, modelId)) {
|
|
795
801
|
reasons.push(`blocked_by_policy (consumer gated this model \u2014 see CompilePolicy.blockedModels)`);
|
|
796
802
|
}
|
|
797
803
|
if (opts.estimatedInputTokens > profile.maxContextTokens * 0.9) {
|
|
@@ -821,7 +827,7 @@ function passScoreTargets(ir, opts) {
|
|
|
821
827
|
const qualityScore = Math.max(0, baseQuality - qualityPenalty);
|
|
822
828
|
const callerOrderBoost = (modelIds.length - modelIds.indexOf(modelId)) * 0.1;
|
|
823
829
|
const costPenalty = estimatedCostUsd * 5;
|
|
824
|
-
const preferredBoost = preferredSet
|
|
830
|
+
const preferredBoost = policySetHas(preferredSet, modelId) ? 0.5 : 0;
|
|
825
831
|
let latencyPenalty = 0;
|
|
826
832
|
const maxLatencyMs = constraints.maxLatencyMs;
|
|
827
833
|
if (typeof maxLatencyMs === "number" && maxLatencyMs > 0) {
|
|
@@ -878,7 +884,7 @@ function passScoreTargets(ir, opts) {
|
|
|
878
884
|
description: `Model ${modelId} excluded \u2014 estimated cost $${estimatedCostUsd.toFixed(4)} exceeds policy ceiling $${policy.maxCostPerCallUsd.toFixed(4)}`
|
|
879
885
|
});
|
|
880
886
|
}
|
|
881
|
-
if (preferredSet
|
|
887
|
+
if (policySetHas(preferredSet, modelId) && reasons.length === 0) {
|
|
882
888
|
policyMutations.push({
|
|
883
889
|
id: `policy-preferred-${modelId}`,
|
|
884
890
|
source: "compile_policy",
|
|
@@ -1892,6 +1898,40 @@ function defaultOnError3(err) {
|
|
|
1892
1898
|
err
|
|
1893
1899
|
);
|
|
1894
1900
|
}
|
|
1901
|
+
function prefetchPromotions(appId) {
|
|
1902
|
+
const rt = runtime3;
|
|
1903
|
+
if (!rt || !appId) return void 0;
|
|
1904
|
+
let snap = snapshots3.get(appId);
|
|
1905
|
+
if (!snap) {
|
|
1906
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1907
|
+
snapshots3.set(appId, snap);
|
|
1908
|
+
}
|
|
1909
|
+
if (snap.expiresAt > Date.now()) return void 0;
|
|
1910
|
+
const inflight = pendingRefreshes3.get(appId);
|
|
1911
|
+
if (inflight) return inflight;
|
|
1912
|
+
if (snap.refreshing) return void 0;
|
|
1913
|
+
snap.refreshing = true;
|
|
1914
|
+
void asyncRefresh3(rt, appId);
|
|
1915
|
+
return pendingRefreshes3.get(appId);
|
|
1916
|
+
}
|
|
1917
|
+
async function awaitPromotionsReady(appId, timeoutMs) {
|
|
1918
|
+
if (!runtime3 || !appId) return;
|
|
1919
|
+
const pending = prefetchPromotions(appId) ?? pendingRefreshes3.get(appId);
|
|
1920
|
+
if (!(timeoutMs > 0)) return;
|
|
1921
|
+
if (!pending) return;
|
|
1922
|
+
let timer;
|
|
1923
|
+
try {
|
|
1924
|
+
await Promise.race([
|
|
1925
|
+
pending,
|
|
1926
|
+
new Promise((resolve) => {
|
|
1927
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
1928
|
+
})
|
|
1929
|
+
]);
|
|
1930
|
+
} catch {
|
|
1931
|
+
} finally {
|
|
1932
|
+
if (timer) clearTimeout(timer);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1895
1935
|
function _testResetPromotions() {
|
|
1896
1936
|
runtime3 = void 0;
|
|
1897
1937
|
snapshots3.clear();
|
|
@@ -2361,6 +2401,92 @@ function advisorRuleCrossFamilyFit(ctx) {
|
|
|
2361
2401
|
];
|
|
2362
2402
|
}
|
|
2363
2403
|
|
|
2404
|
+
// src/advisor-rules/blocked-model-drift.ts
|
|
2405
|
+
var BLOCKED_MODEL_NOT_IN_ROSTER_CODE = "blocked-model-not-in-roster";
|
|
2406
|
+
var BLOCKED_MODEL_FAMILY_SIBLING_SERVED_CODE = "blocked-model-family-sibling-served";
|
|
2407
|
+
var DOCS_URL = "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories";
|
|
2408
|
+
function familyOf2(modelId, profile) {
|
|
2409
|
+
return profile?.family ?? deriveFamilyFromModelId(modelId);
|
|
2410
|
+
}
|
|
2411
|
+
function isOnTheWayOut(status) {
|
|
2412
|
+
return status === "legacy" || status === "deprecated";
|
|
2413
|
+
}
|
|
2414
|
+
function advisorRuleBlockedModelDrift(ctx) {
|
|
2415
|
+
const blocked = ctx.policy?.blockedModels;
|
|
2416
|
+
if (!blocked || blocked.length === 0) return [];
|
|
2417
|
+
const resolve = ctx.resolveProfile ?? tryGetProfile;
|
|
2418
|
+
const out = [];
|
|
2419
|
+
const entries = [...new Set(blocked)].sort();
|
|
2420
|
+
const selectedProfile = resolve(ctx.selectedModelId);
|
|
2421
|
+
const selectedFamily = familyOf2(ctx.selectedModelId, selectedProfile);
|
|
2422
|
+
const orphans = entries.filter((e) => resolve(e) === void 0);
|
|
2423
|
+
if (orphans.length > 0) {
|
|
2424
|
+
const list = orphans.map((o) => `\`${o}\``).join(", ");
|
|
2425
|
+
const plural = orphans.length === 1 ? "entry" : "entries";
|
|
2426
|
+
const verb = orphans.length === 1 ? "matches" : "match";
|
|
2427
|
+
out.push({
|
|
2428
|
+
level: "warn",
|
|
2429
|
+
code: BLOCKED_MODEL_NOT_IN_ROSTER_CODE,
|
|
2430
|
+
message: `CompilePolicy.blockedModels ${plural} ${list} ${verb} no model in the current roster, so ${orphans.length === 1 ? "it is" : "they are"} inert \u2014 \`blockedModels\` is matched by exact model id, and nothing kgauto can select carries ${orphans.length === 1 ? "that id" : "those ids"}. The block will never fire.`,
|
|
2431
|
+
suggestion: `Check for a typo, or for an id that was retired from the roster since the block was written. This is how a block goes quiet without an error: tt-intel (2026-07-29) carried \`KGAUTO_BLOCKED_MODELS="claude-sonnet-4-6"\` across a roster retarget and the block stopped covering the traffic they believed it covered. Resolve the intended model id against the live roster \u2014 \`getRecommendedPrimary({ family, fallback })\` returns the id the family currently resolves to \u2014 and block that id, or drop the entry if it is no longer needed.`,
|
|
2432
|
+
docsUrl: DOCS_URL
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
if (selectedFamily !== null) {
|
|
2436
|
+
const siblings = entries.filter((e) => {
|
|
2437
|
+
if (resolveModelAlias(e) === resolveModelAlias(ctx.selectedModelId)) return false;
|
|
2438
|
+
const p = resolve(e);
|
|
2439
|
+
if (familyOf2(e, p) !== selectedFamily) return false;
|
|
2440
|
+
if (p && selectedProfile && p.provider !== selectedProfile.provider) {
|
|
2441
|
+
return false;
|
|
2442
|
+
}
|
|
2443
|
+
return true;
|
|
2444
|
+
});
|
|
2445
|
+
if (siblings.length > 0) {
|
|
2446
|
+
const list = siblings.map((s) => `\`${s}\``).join(", ");
|
|
2447
|
+
const plural = siblings.length === 1 ? "" : "s";
|
|
2448
|
+
const retargetShaped = siblings.some((s) => {
|
|
2449
|
+
const p = resolve(s);
|
|
2450
|
+
return isOnTheWayOut(p?.status) && selectedProfile?.status === "current";
|
|
2451
|
+
});
|
|
2452
|
+
const retargetNote = retargetShaped ? ` The blocked id${plural} ${siblings.length === 1 ? "is" : "are"} legacy/deprecated while \`${ctx.selectedModelId}\` is current \u2014 that is the signature of a roster lifecycle move rather than a deliberate per-generation block, so this is more likely to be drift than intent.` : "";
|
|
2453
|
+
out.push({
|
|
2454
|
+
level: "warn",
|
|
2455
|
+
code: BLOCKED_MODEL_FAMILY_SIBLING_SERVED_CODE,
|
|
2456
|
+
message: `\`${ctx.selectedModelId}\` was selected for this call. It is in the same family (\`${selectedFamily}\`) as blocked entr${siblings.length === 1 ? "y" : "ies"} ${list}, but its exact id differs \u2014 and \`CompilePolicy.blockedModels\` matches by exact id, so the block does not cover it.${retargetNote}`,
|
|
2457
|
+
suggestion: `Two readings, and kgauto cannot tell them apart: (1) intentional \u2014 you meant to gate that specific id and \`${ctx.selectedModelId}\` is fine, in which case nothing needs doing and you can filter this code; (2) drift \u2014 you meant to gate the family, and a roster change moved traffic to a sibling your block never named. This happened to tt-intel: when the \`claude-sonnet\` family primary retargeted from \`claude-sonnet-4-6\` to \`claude-sonnet-5\`, their literal-id gate stopped matching the family's routed traffic (their fix was local family resolution). If you meant the family, add \`${ctx.selectedModelId}\` to \`blockedModels\` \u2014 kgauto deliberately does NOT widen exact-id blocks into family globs, because that would silently change what every existing block covers.`,
|
|
2458
|
+
docsUrl: DOCS_URL
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
return out;
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
// src/advisor-rules/preferred-blocked-overlap.ts
|
|
2466
|
+
var PREFERRED_MODEL_BLOCKED_CODE = "preferred-model-blocked";
|
|
2467
|
+
var DOCS_URL2 = "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories";
|
|
2468
|
+
function advisorRulePreferredBlockedOverlap(ctx) {
|
|
2469
|
+
const preferred = ctx.policy?.preferredModels;
|
|
2470
|
+
const blocked = ctx.policy?.blockedModels;
|
|
2471
|
+
if (!preferred?.length || !blocked?.length) return [];
|
|
2472
|
+
const blockedSet = canonicalPolicySet(blocked);
|
|
2473
|
+
const deadPins = [...new Set(preferred)].filter((p) => policySetHas(blockedSet, p)).sort();
|
|
2474
|
+
if (deadPins.length === 0) return [];
|
|
2475
|
+
const list = deadPins.map((p) => `\`${p}\``).join(", ");
|
|
2476
|
+
const one = deadPins.length === 1;
|
|
2477
|
+
const preferredSet = canonicalPolicySet(preferred);
|
|
2478
|
+
const servedIsPinned = policySetHas(preferredSet, ctx.selectedModelId);
|
|
2479
|
+
return [
|
|
2480
|
+
{
|
|
2481
|
+
level: "warn",
|
|
2482
|
+
code: PREFERRED_MODEL_BLOCKED_CODE,
|
|
2483
|
+
message: `CompilePolicy.preferredModels entr${one ? "y" : "ies"} ${list} ${one ? "is" : "are"} also in \`blockedModels\` (matched canonically, aliases included). A blocked model is hard-rejected before preference boosts apply, so ${one ? "this pin" : "these pins"} can never serve. ` + (servedIsPinned ? `This call was served by \`${ctx.selectedModelId}\`, which is itself a live pin \u2014 the dead entr${one ? "y is" : "ies are"} latent, not currently rerouting traffic.` : `Substitution is your steady state: this call landed on \`${ctx.selectedModelId}\`, which you did not pin.`),
|
|
2484
|
+
suggestion: `Two readings, and kgauto cannot tell them apart: (1) intentional \u2014 your spend gate deliberately outranks the pin, in which case nothing needs doing and you can filter this code; (2) misconfiguration \u2014 the pin and the block were written at different times and the overlap is an accident. tt-intel hit reading (2) on 2026-08-01: a summarize site pinned \`claude-sonnet\` while \`KGAUTO_BLOCKED_MODELS\` carried the same family, and every call silently substituted a reasoning model whose reasoning burn exceeded the site's \`maxOutputTokens\` \u2014 100% empty payloads under HTTP 200. If the block should win, remove the pin so the policy says what it does. If the pin should win, remove ${one ? "the blocking entry" : "the blocking entries"} or re-scope the block to the exact ids you mean. Check what the substitute costs at your input shape while you are here \u2014 a spend gate that lands traffic on a pricier model than the one it blocked is a cost inversion, not a saving.`,
|
|
2485
|
+
docsUrl: DOCS_URL2
|
|
2486
|
+
}
|
|
2487
|
+
];
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2364
2490
|
// src/advisor.ts
|
|
2365
2491
|
var QUALITY_FLOOR_FOR_RECOMMENDATION = 6;
|
|
2366
2492
|
var TIER_DOWN_COST_RATIO = 0.5;
|
|
@@ -2379,6 +2505,18 @@ function runAdvisor(ir, result, profile, policy, phase2) {
|
|
|
2379
2505
|
out.push(...detectToolBloat(ir, result));
|
|
2380
2506
|
out.push(...detectHistoryUncached(ir, profile));
|
|
2381
2507
|
out.push(...detectSingleModelArray(ir, policy));
|
|
2508
|
+
out.push(
|
|
2509
|
+
...advisorRuleBlockedModelDrift({
|
|
2510
|
+
policy,
|
|
2511
|
+
selectedModelId: profile.id
|
|
2512
|
+
})
|
|
2513
|
+
);
|
|
2514
|
+
out.push(
|
|
2515
|
+
...advisorRulePreferredBlockedOverlap({
|
|
2516
|
+
policy,
|
|
2517
|
+
selectedModelId: profile.id
|
|
2518
|
+
})
|
|
2519
|
+
);
|
|
2382
2520
|
if (policy?.posture !== "locked") {
|
|
2383
2521
|
out.push(...detectCostMismatchedArchetype(ir, profile, phase2));
|
|
2384
2522
|
out.push(...detectModelStaleEvidence(ir, profile));
|
|
@@ -2490,18 +2628,27 @@ function detectHistoryUncached(ir, profile) {
|
|
|
2490
2628
|
function detectSingleModelArray(ir, policy) {
|
|
2491
2629
|
if (ir.models.length !== 1) return [];
|
|
2492
2630
|
if (policy?.posture === "locked") return [];
|
|
2493
|
-
const
|
|
2631
|
+
const entry = ir.models[0];
|
|
2632
|
+
const only = typeof entry === "string" ? entry : `family:${entry.family}`;
|
|
2633
|
+
const blocked = canonicalPolicySet(policy?.blockedModels);
|
|
2634
|
+
let alternatives = [];
|
|
2635
|
+
try {
|
|
2636
|
+
alternatives = getDefaultFallbackChain({
|
|
2637
|
+
archetype: ir.intent.archetype,
|
|
2638
|
+
primary: only,
|
|
2639
|
+
posture: "preferred",
|
|
2640
|
+
policy
|
|
2641
|
+
}).filter((id) => resolveModelAlias(id) !== resolveModelAlias(only)).filter((id) => !policySetHas(blocked, id)).filter((id) => getModelCompatibility(id, { archetype: ir.intent.archetype }).status !== "reject");
|
|
2642
|
+
} catch {
|
|
2643
|
+
}
|
|
2644
|
+
const hasAlternative = alternatives.length > 0;
|
|
2645
|
+
const remedy = "Widen the chain AND pin your primary: `compile({ ...ir, models: getDefaultFallbackChain({ archetype: ir.intent.archetype, primary: '" + only + "', posture: 'preferred' }) }, { policy: { preferredModels: ['" + only + "'] } })`. The chain is a CANDIDATE SET \u2014 compile() scores it and does not honour its order, so without the `preferredModels` pin the extra entries can retarget your primary (and with it your cost profile). With the pin, `" + only + "` stays primary and the added entries serve only as the safety net. Note `posture` has no effect once `primary` is passed. If you gate models by spend, re-verify `policy.blockedModels` against the widened set before shipping. If single-model is intentional (compliance/brand promise), set `policy.posture = 'locked'` to silence this rule.";
|
|
2494
2646
|
return [
|
|
2495
2647
|
{
|
|
2496
|
-
level: "warn",
|
|
2648
|
+
level: hasAlternative ? "critical" : "warn",
|
|
2497
2649
|
code: "single-model-array",
|
|
2498
|
-
message: `\`ir.models\` has length 1 (only "${only}") and posture is not 'locked'. A single-model chain has no safety net \u2014 the first 429 / 5xx / cliff hits the user as a failure.
|
|
2499
|
-
|
|
2500
|
-
// position 0 and is therefore cost-neutral; `'open'` re-picks the
|
|
2501
|
-
// primary and is NOT. This rule is about reliability, not cost, so both
|
|
2502
|
-
// stay on offer — but the cost consequence of the second is now stated,
|
|
2503
|
-
// since a reliability fix should not silently become a repricing.
|
|
2504
|
-
suggestion: "Use `getDefaultFallbackChain({ archetype: ir.intent.archetype, primary: '" + only + "', posture: 'preferred' })` for a user-anchored chain \u2014 this keeps `" + only + "` as your primary and only adds fallbacks, so your cost profile is unchanged. `getDefaultFallbackChain({ archetype, posture: 'open' })` instead lets the library pick the PRIMARY, ordered by archetype performance rather than cost \u2014 check what it returns before adopting it. If single-model is intentional (compliance/brand promise), set `policy.posture = 'locked'` to silence this rule.",
|
|
2650
|
+
message: hasAlternative ? `\`ir.models\` has length 1 (only "${only}") for archetype "${ir.intent.archetype}" and posture is not 'locked'. A single-model chain has no safety net \u2014 the first 429 / 5xx / cliff hits the user as a failure \u2014 and a compatible, non-blocked alternative exists in the roster today (${alternatives[0]}), so this is critical: the missing safety net is adoptable now.` : `\`ir.models\` has length 1 (only "${only}") for archetype "${ir.intent.archetype}" and posture is not 'locked'. A single-model chain has no safety net \u2014 the first 429 / 5xx / cliff hits the user as a failure. No compatible non-blocked alternative is visible in the roster for this archetype, so this stays a warning.`,
|
|
2651
|
+
suggestion: remedy,
|
|
2505
2652
|
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#single-model-array"
|
|
2506
2653
|
}
|
|
2507
2654
|
];
|
|
@@ -3055,6 +3202,15 @@ function compile(ir, opts = {}) {
|
|
|
3055
3202
|
sectionRewritesApplied
|
|
3056
3203
|
}
|
|
3057
3204
|
);
|
|
3205
|
+
if (ir["policy"] !== void 0 && opts.policy === void 0) {
|
|
3206
|
+
rawAdvisories.push({
|
|
3207
|
+
level: "critical",
|
|
3208
|
+
code: "policy-in-ir-ignored",
|
|
3209
|
+
message: "The IR passed to compile() carries a `policy` field. `policy` belongs in the SECOND argument \u2014 `compile(ir, { policy })` \u2014 and inside the IR it is an unknown field that is completely ignored. If that policy names blockedModels, no block is being enforced on this call.",
|
|
3210
|
+
suggestion: "Move it: `compile(ir, { policy: { ... } })`. If you also configure policy correctly elsewhere on this path, remove the IR copy so the next reader is not misled.",
|
|
3211
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#public-api"
|
|
3212
|
+
});
|
|
3213
|
+
}
|
|
3058
3214
|
const advisories = rawAdvisories.map((a) => ({
|
|
3059
3215
|
...a,
|
|
3060
3216
|
kgautoRequestId: handle,
|
|
@@ -3281,6 +3437,12 @@ function configureBrain(config) {
|
|
|
3281
3437
|
fetchImpl: config.fetchImpl ?? fetch,
|
|
3282
3438
|
onError: config.onError
|
|
3283
3439
|
});
|
|
3440
|
+
if (config.appId) {
|
|
3441
|
+
try {
|
|
3442
|
+
void prefetchPromotions(config.appId);
|
|
3443
|
+
} catch {
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3284
3446
|
} else {
|
|
3285
3447
|
configurePromotionsBrain(void 0);
|
|
3286
3448
|
}
|
|
@@ -3652,6 +3814,7 @@ function buildPayload(input, reg) {
|
|
|
3652
3814
|
latency_ms: input.latencyMs,
|
|
3653
3815
|
success: input.success,
|
|
3654
3816
|
empty_response: input.emptyResponse ?? input.tokensOut === 0,
|
|
3817
|
+
source: input.source,
|
|
3655
3818
|
error_type: input.errorType,
|
|
3656
3819
|
tools_called: input.toolsCalled,
|
|
3657
3820
|
oracle_score: input.oracleScore?.score,
|
|
@@ -4750,7 +4913,11 @@ async function call(ir, opts = {}) {
|
|
|
4750
4913
|
)
|
|
4751
4914
|
})
|
|
4752
4915
|
);
|
|
4753
|
-
|
|
4916
|
+
const warmupMs = resolveGateWarmupMs(opts);
|
|
4917
|
+
await Promise.all([
|
|
4918
|
+
awaitMeasuredFailureReady(ir.appId, warmupMs),
|
|
4919
|
+
awaitPromotionsReady(ir.appId, warmupMs)
|
|
4920
|
+
]);
|
|
4754
4921
|
const initial = compileAndRegister(ir, opts);
|
|
4755
4922
|
safeEmit(
|
|
4756
4923
|
() => emitCompileDone(traceId, ir.appId, {
|
|
@@ -4790,6 +4957,7 @@ async function call(ir, opts = {}) {
|
|
|
4790
4957
|
const latencyMs2 = Date.now() - start;
|
|
4791
4958
|
await record({
|
|
4792
4959
|
handle: initial.handle,
|
|
4960
|
+
source: opts.source,
|
|
4793
4961
|
tokensIn: 0,
|
|
4794
4962
|
tokensOut: 0,
|
|
4795
4963
|
latencyMs: latencyMs2,
|
|
@@ -4824,11 +4992,11 @@ async function call(ir, opts = {}) {
|
|
|
4824
4992
|
}
|
|
4825
4993
|
let policyBlockedFiltered;
|
|
4826
4994
|
if (opts.policy?.blockedModels && opts.policy.blockedModels.length > 0) {
|
|
4827
|
-
const blocked =
|
|
4995
|
+
const blocked = canonicalPolicySet(opts.policy.blockedModels);
|
|
4828
4996
|
const filtered = [];
|
|
4829
4997
|
const dropped = [];
|
|
4830
4998
|
for (const t of targetsToTry) {
|
|
4831
|
-
if (blocked
|
|
4999
|
+
if (policySetHas(blocked, t)) {
|
|
4832
5000
|
dropped.push(t);
|
|
4833
5001
|
} else {
|
|
4834
5002
|
filtered.push(t);
|
|
@@ -4842,6 +5010,7 @@ async function call(ir, opts = {}) {
|
|
|
4842
5010
|
const latencyMs2 = Date.now() - start;
|
|
4843
5011
|
await record({
|
|
4844
5012
|
handle: initial.handle,
|
|
5013
|
+
source: opts.source,
|
|
4845
5014
|
tokensIn: 0,
|
|
4846
5015
|
tokensOut: 0,
|
|
4847
5016
|
latencyMs: latencyMs2,
|
|
@@ -4868,10 +5037,18 @@ async function call(ir, opts = {}) {
|
|
|
4868
5037
|
const failedProviders = /* @__PURE__ */ new Map();
|
|
4869
5038
|
const sameModelRetryEnabled = opts.sameModelRetry ?? isSameModelRetryEnabledFromEnv();
|
|
4870
5039
|
let retriedSameModel = false;
|
|
5040
|
+
const pushAttempt = (attempt) => {
|
|
5041
|
+
attempts.push(attempt);
|
|
5042
|
+
if (attempt.status === "success") return;
|
|
5043
|
+
try {
|
|
5044
|
+
opts.onFailedAttempt?.(attempt);
|
|
5045
|
+
} catch {
|
|
5046
|
+
}
|
|
5047
|
+
};
|
|
4871
5048
|
for (let i = 0; i < targetsToTry.length; i++) {
|
|
4872
5049
|
const targetModel = targetsToTry[i];
|
|
4873
5050
|
if (opts.abortSignal?.aborted) {
|
|
4874
|
-
|
|
5051
|
+
pushAttempt({
|
|
4875
5052
|
model: targetModel,
|
|
4876
5053
|
status: "terminal",
|
|
4877
5054
|
errorCode: "aborted",
|
|
@@ -4882,7 +5059,7 @@ async function call(ir, opts = {}) {
|
|
|
4882
5059
|
const targetProfile = tryGetProfile(targetModel);
|
|
4883
5060
|
const providerFailReason = targetProfile ? failedProviders.get(targetProfile.provider) : void 0;
|
|
4884
5061
|
if (targetProfile && providerFailReason && !opts.noFallback) {
|
|
4885
|
-
|
|
5062
|
+
pushAttempt({
|
|
4886
5063
|
model: targetModel,
|
|
4887
5064
|
status: "terminal",
|
|
4888
5065
|
errorCode: `${providerFailReason}_inferred`,
|
|
@@ -4901,7 +5078,7 @@ async function call(ir, opts = {}) {
|
|
|
4901
5078
|
opts
|
|
4902
5079
|
);
|
|
4903
5080
|
} catch (err) {
|
|
4904
|
-
|
|
5081
|
+
pushAttempt({
|
|
4905
5082
|
model: targetModel,
|
|
4906
5083
|
status: "terminal",
|
|
4907
5084
|
errorCode: "compile_error",
|
|
@@ -4939,7 +5116,7 @@ async function call(ir, opts = {}) {
|
|
|
4939
5116
|
}
|
|
4940
5117
|
if (!validated.ok && isStructuredContractViolation(validated.errorCode) && sameModelRetryEnabled && !retriedSameModel && !retrySuppressionNote) {
|
|
4941
5118
|
retriedSameModel = true;
|
|
4942
|
-
|
|
5119
|
+
pushAttempt({
|
|
4943
5120
|
model: targetModel,
|
|
4944
5121
|
status: validated.errorType,
|
|
4945
5122
|
errorCode: validated.errorCode,
|
|
@@ -4954,7 +5131,7 @@ async function call(ir, opts = {}) {
|
|
|
4954
5131
|
servedByRetry = true;
|
|
4955
5132
|
}
|
|
4956
5133
|
if (validated.ok) {
|
|
4957
|
-
|
|
5134
|
+
pushAttempt({
|
|
4958
5135
|
model: targetModel,
|
|
4959
5136
|
status: "success",
|
|
4960
5137
|
...servedByRetry ? { sameModelRetry: true } : {}
|
|
@@ -4975,6 +5152,7 @@ async function call(ir, opts = {}) {
|
|
|
4975
5152
|
const goldenShapeKey = goldenRate > 0 ? peekRegisteredShapeKey(initial.handle) : void 0;
|
|
4976
5153
|
await record({
|
|
4977
5154
|
handle: initial.handle,
|
|
5155
|
+
source: opts.source,
|
|
4978
5156
|
tokensIn: validated.response.tokens.input,
|
|
4979
5157
|
tokensOut: validated.response.tokens.output,
|
|
4980
5158
|
latencyMs: latencyMs2,
|
|
@@ -5072,7 +5250,7 @@ async function call(ir, opts = {}) {
|
|
|
5072
5250
|
advisories: activeCompile.advisories
|
|
5073
5251
|
};
|
|
5074
5252
|
}
|
|
5075
|
-
|
|
5253
|
+
pushAttempt({
|
|
5076
5254
|
model: targetModel,
|
|
5077
5255
|
status: validated.errorType,
|
|
5078
5256
|
errorCode: validated.errorCode,
|
|
@@ -5097,6 +5275,7 @@ async function call(ir, opts = {}) {
|
|
|
5097
5275
|
const latencyMs = Date.now() - start;
|
|
5098
5276
|
await record({
|
|
5099
5277
|
handle: initial.handle,
|
|
5278
|
+
source: opts.source,
|
|
5100
5279
|
tokensIn: lastErr?.tokens?.input ?? 0,
|
|
5101
5280
|
tokensOut: lastErr?.tokens?.output ?? 0,
|
|
5102
5281
|
latencyMs,
|
|
@@ -7216,6 +7395,8 @@ export {
|
|
|
7216
7395
|
ALT_STRATEGY_IDS,
|
|
7217
7396
|
ARCHETYPE_FAMILY_FITS,
|
|
7218
7397
|
ARCHETYPE_FLOOR_DEFAULT,
|
|
7398
|
+
BLOCKED_MODEL_FAMILY_SIBLING_SERVED_CODE,
|
|
7399
|
+
BLOCKED_MODEL_NOT_IN_ROSTER_CODE,
|
|
7219
7400
|
BRAIN_READ_ENV_NAMES,
|
|
7220
7401
|
BURST_SPAN_MS,
|
|
7221
7402
|
COACH_CFG,
|
|
@@ -7259,6 +7440,7 @@ export {
|
|
|
7259
7440
|
applySectionRewrites,
|
|
7260
7441
|
attachCacheControlToStreamTextInput,
|
|
7261
7442
|
awaitMeasuredFailureReady,
|
|
7443
|
+
awaitPromotionsReady,
|
|
7262
7444
|
brainHealth,
|
|
7263
7445
|
bucketContext,
|
|
7264
7446
|
bucketHistory,
|
|
@@ -7345,6 +7527,7 @@ export {
|
|
|
7345
7527
|
peekBrainDeadLetter,
|
|
7346
7528
|
planDecomposition,
|
|
7347
7529
|
prefetchMeasuredFailure,
|
|
7530
|
+
prefetchPromotions,
|
|
7348
7531
|
probeShadow,
|
|
7349
7532
|
profileToRow,
|
|
7350
7533
|
profilesByProvider,
|
|
@@ -7357,6 +7540,7 @@ export {
|
|
|
7357
7540
|
resetTokenizer,
|
|
7358
7541
|
resolveConventionsForProfile,
|
|
7359
7542
|
resolveGoldenCaptureRate,
|
|
7543
|
+
resolveModelAlias,
|
|
7360
7544
|
resolvePricingAt,
|
|
7361
7545
|
resolveProviderKey,
|
|
7362
7546
|
rowToAdvisory,
|
|
@@ -305,6 +305,17 @@ interface CompilePolicy {
|
|
|
305
305
|
* Model IDs the consumer has gated. Compile() will never select these.
|
|
306
306
|
* Use for: cost caps, account-level rate limits, "this model is broken
|
|
307
307
|
* for our workload" decisions.
|
|
308
|
+
*
|
|
309
|
+
* Matching is by EXACT id after alias resolution (alpha.85): an alias
|
|
310
|
+
* and its canonical id are the same model, so blocking either blocks
|
|
311
|
+
* both (`deepseek-chat` ≡ `deepseek-v4-flash`). Matching does NOT widen
|
|
312
|
+
* to family siblings — blocking `deepseek-v4-flash` says nothing about
|
|
313
|
+
* `deepseek-v4-pro` — and a bare family key (`'deepseek'`,
|
|
314
|
+
* `'claude-sonnet'`) is INERT: it matches no model and no warning fires
|
|
315
|
+
* today. To gate a family, list its concrete ids. Beware roster
|
|
316
|
+
* retargets: when a family primary moves to a new id, a literal-id
|
|
317
|
+
* block does not follow it (tt-intel s113 built consumer-side family
|
|
318
|
+
* resolution for exactly this).
|
|
308
319
|
*/
|
|
309
320
|
blockedModels?: string[];
|
|
310
321
|
/**
|
|
@@ -1073,6 +1084,51 @@ interface ShadowProbeConfig {
|
|
|
1073
1084
|
skipSlowTierInSync?: boolean;
|
|
1074
1085
|
}
|
|
1075
1086
|
interface CallOptions {
|
|
1087
|
+
/**
|
|
1088
|
+
* alpha.86 — self-mark for synthetic callers (canaries, smokes, eval
|
|
1089
|
+
* replays, probes). Passed through to every outcome row this call
|
|
1090
|
+
* records, including failure rows. OMIT for real traffic. See
|
|
1091
|
+
* {@link OutcomeSource}.
|
|
1092
|
+
*/
|
|
1093
|
+
source?: OutcomeSource;
|
|
1094
|
+
/**
|
|
1095
|
+
* alpha.84 — fires once per FAILED attempt during the fallback walk, before
|
|
1096
|
+
* the walk continues. IC-Cairn's filing, 2026-07-29.
|
|
1097
|
+
*
|
|
1098
|
+
* ## The gap this closes
|
|
1099
|
+
*
|
|
1100
|
+
* `call()` records exactly ONE outcome row per call. An attempt that fails
|
|
1101
|
+
* and then walks to a successful fallback survives only as
|
|
1102
|
+
* `fellOverFrom`/`fallbackReason` metadata ON the success row — so a
|
|
1103
|
+
* `call()`-only consumer's corpus contains **no failure rows at all**, by
|
|
1104
|
+
* construction, no matter how carefully they wired `record()`.
|
|
1105
|
+
*
|
|
1106
|
+
* That is not hypothetical. kgauto's own `corpus-implausible-success`
|
|
1107
|
+
* detector fired on inspire-central and told them to "wire record() on the
|
|
1108
|
+
* FAILURE path" — which they had already done, in five places. Measured:
|
|
1109
|
+
* 89 rows, 0 failure rows, 2 fellover-bearing. **The detector demanded
|
|
1110
|
+
* output the API could not produce.** Streaming consumers have had
|
|
1111
|
+
* `onFailedAttempt` since alpha.48 via `streamWithFallover`; `call()`
|
|
1112
|
+
* consumers had nothing. This restores parity.
|
|
1113
|
+
*
|
|
1114
|
+
* ## Contract
|
|
1115
|
+
*
|
|
1116
|
+
* Fires for every attempt whose `status` is `'retryable'` or `'terminal'`,
|
|
1117
|
+
* including skipped ones (`*_inferred`, `aborted`) — a skip is a real
|
|
1118
|
+
* datum about the chain, not an absence. Never fires for `'success'`.
|
|
1119
|
+
*
|
|
1120
|
+
* Called synchronously and **its throw is swallowed**: a consumer's
|
|
1121
|
+
* telemetry bug must never take down the call it is observing. It is a
|
|
1122
|
+
* notification, not a hook that can veto or alter the walk.
|
|
1123
|
+
*
|
|
1124
|
+
* Typical use — emit a failure row per attempt so the corpus can contain
|
|
1125
|
+
* failures at all:
|
|
1126
|
+
*
|
|
1127
|
+
* call(ir, { onFailedAttempt: (a) => void recordOutcome({
|
|
1128
|
+
* ...base, success: false, errorType: a.errorCode, model: a.model,
|
|
1129
|
+
* }) })
|
|
1130
|
+
*/
|
|
1131
|
+
onFailedAttempt?: (attempt: CallAttempt) => void;
|
|
1076
1132
|
/** Forwarded to compile(). */
|
|
1077
1133
|
policy?: CompilePolicy;
|
|
1078
1134
|
/**
|
|
@@ -1409,9 +1465,34 @@ interface OracleScore {
|
|
|
1409
1465
|
/** Free-form explanation for debugging. */
|
|
1410
1466
|
rationale?: string;
|
|
1411
1467
|
}
|
|
1468
|
+
/**
|
|
1469
|
+
* alpha.86 — who originated an outcome row (migration 056).
|
|
1470
|
+
*
|
|
1471
|
+
* Absent/undefined means ORGANIC: a real consumer call on behalf of a real
|
|
1472
|
+
* user. Consumers change nothing. Synthetic writers — canaries, smoke
|
|
1473
|
+
* gates, eval replays, probe harnesses — self-mark, so liveness and volume
|
|
1474
|
+
* rules can compute over consumer-originated rows only.
|
|
1475
|
+
*
|
|
1476
|
+
* The incident this closes (cost-watch 2026-08-06 → 08-08): playbacksam's
|
|
1477
|
+
* daily canary wrote exactly 8 rows/day for 13 straight days while PB's
|
|
1478
|
+
* organic traffic was near-zero, and the liveness rule scored PB the
|
|
1479
|
+
* healthiest consumer in the portfolio. On 08-08 EVERY row the brain
|
|
1480
|
+
* received in 24h was canary, and no rule could say so — distinguishing a
|
|
1481
|
+
* replay from real traffic took a hand-reconstructed time-window +
|
|
1482
|
+
* token-fingerprint argument, three mornings running. A consumer whose
|
|
1483
|
+
* only rows are written by kgauto's own machinery is dark, not healthy;
|
|
1484
|
+
* this column is what lets a rule print that sentence.
|
|
1485
|
+
*/
|
|
1486
|
+
type OutcomeSource = 'canary' | 'smoke' | 'eval' | 'probe' | 'synthetic';
|
|
1412
1487
|
interface RecordInput {
|
|
1413
1488
|
/** Handle from CompileResult. */
|
|
1414
1489
|
handle: string;
|
|
1490
|
+
/**
|
|
1491
|
+
* alpha.86 — self-mark for synthetic writers (migration 056). OMIT for
|
|
1492
|
+
* real traffic; never write an explicit "organic" value. See
|
|
1493
|
+
* {@link OutcomeSource}.
|
|
1494
|
+
*/
|
|
1495
|
+
source?: OutcomeSource;
|
|
1415
1496
|
/** Actual tokens consumed (post-call). */
|
|
1416
1497
|
tokensIn: number;
|
|
1417
1498
|
tokensOut: number;
|
|
@@ -305,6 +305,17 @@ interface CompilePolicy {
|
|
|
305
305
|
* Model IDs the consumer has gated. Compile() will never select these.
|
|
306
306
|
* Use for: cost caps, account-level rate limits, "this model is broken
|
|
307
307
|
* for our workload" decisions.
|
|
308
|
+
*
|
|
309
|
+
* Matching is by EXACT id after alias resolution (alpha.85): an alias
|
|
310
|
+
* and its canonical id are the same model, so blocking either blocks
|
|
311
|
+
* both (`deepseek-chat` ≡ `deepseek-v4-flash`). Matching does NOT widen
|
|
312
|
+
* to family siblings — blocking `deepseek-v4-flash` says nothing about
|
|
313
|
+
* `deepseek-v4-pro` — and a bare family key (`'deepseek'`,
|
|
314
|
+
* `'claude-sonnet'`) is INERT: it matches no model and no warning fires
|
|
315
|
+
* today. To gate a family, list its concrete ids. Beware roster
|
|
316
|
+
* retargets: when a family primary moves to a new id, a literal-id
|
|
317
|
+
* block does not follow it (tt-intel s113 built consumer-side family
|
|
318
|
+
* resolution for exactly this).
|
|
308
319
|
*/
|
|
309
320
|
blockedModels?: string[];
|
|
310
321
|
/**
|
|
@@ -1073,6 +1084,51 @@ interface ShadowProbeConfig {
|
|
|
1073
1084
|
skipSlowTierInSync?: boolean;
|
|
1074
1085
|
}
|
|
1075
1086
|
interface CallOptions {
|
|
1087
|
+
/**
|
|
1088
|
+
* alpha.86 — self-mark for synthetic callers (canaries, smokes, eval
|
|
1089
|
+
* replays, probes). Passed through to every outcome row this call
|
|
1090
|
+
* records, including failure rows. OMIT for real traffic. See
|
|
1091
|
+
* {@link OutcomeSource}.
|
|
1092
|
+
*/
|
|
1093
|
+
source?: OutcomeSource;
|
|
1094
|
+
/**
|
|
1095
|
+
* alpha.84 — fires once per FAILED attempt during the fallback walk, before
|
|
1096
|
+
* the walk continues. IC-Cairn's filing, 2026-07-29.
|
|
1097
|
+
*
|
|
1098
|
+
* ## The gap this closes
|
|
1099
|
+
*
|
|
1100
|
+
* `call()` records exactly ONE outcome row per call. An attempt that fails
|
|
1101
|
+
* and then walks to a successful fallback survives only as
|
|
1102
|
+
* `fellOverFrom`/`fallbackReason` metadata ON the success row — so a
|
|
1103
|
+
* `call()`-only consumer's corpus contains **no failure rows at all**, by
|
|
1104
|
+
* construction, no matter how carefully they wired `record()`.
|
|
1105
|
+
*
|
|
1106
|
+
* That is not hypothetical. kgauto's own `corpus-implausible-success`
|
|
1107
|
+
* detector fired on inspire-central and told them to "wire record() on the
|
|
1108
|
+
* FAILURE path" — which they had already done, in five places. Measured:
|
|
1109
|
+
* 89 rows, 0 failure rows, 2 fellover-bearing. **The detector demanded
|
|
1110
|
+
* output the API could not produce.** Streaming consumers have had
|
|
1111
|
+
* `onFailedAttempt` since alpha.48 via `streamWithFallover`; `call()`
|
|
1112
|
+
* consumers had nothing. This restores parity.
|
|
1113
|
+
*
|
|
1114
|
+
* ## Contract
|
|
1115
|
+
*
|
|
1116
|
+
* Fires for every attempt whose `status` is `'retryable'` or `'terminal'`,
|
|
1117
|
+
* including skipped ones (`*_inferred`, `aborted`) — a skip is a real
|
|
1118
|
+
* datum about the chain, not an absence. Never fires for `'success'`.
|
|
1119
|
+
*
|
|
1120
|
+
* Called synchronously and **its throw is swallowed**: a consumer's
|
|
1121
|
+
* telemetry bug must never take down the call it is observing. It is a
|
|
1122
|
+
* notification, not a hook that can veto or alter the walk.
|
|
1123
|
+
*
|
|
1124
|
+
* Typical use — emit a failure row per attempt so the corpus can contain
|
|
1125
|
+
* failures at all:
|
|
1126
|
+
*
|
|
1127
|
+
* call(ir, { onFailedAttempt: (a) => void recordOutcome({
|
|
1128
|
+
* ...base, success: false, errorType: a.errorCode, model: a.model,
|
|
1129
|
+
* }) })
|
|
1130
|
+
*/
|
|
1131
|
+
onFailedAttempt?: (attempt: CallAttempt) => void;
|
|
1076
1132
|
/** Forwarded to compile(). */
|
|
1077
1133
|
policy?: CompilePolicy;
|
|
1078
1134
|
/**
|
|
@@ -1409,9 +1465,34 @@ interface OracleScore {
|
|
|
1409
1465
|
/** Free-form explanation for debugging. */
|
|
1410
1466
|
rationale?: string;
|
|
1411
1467
|
}
|
|
1468
|
+
/**
|
|
1469
|
+
* alpha.86 — who originated an outcome row (migration 056).
|
|
1470
|
+
*
|
|
1471
|
+
* Absent/undefined means ORGANIC: a real consumer call on behalf of a real
|
|
1472
|
+
* user. Consumers change nothing. Synthetic writers — canaries, smoke
|
|
1473
|
+
* gates, eval replays, probe harnesses — self-mark, so liveness and volume
|
|
1474
|
+
* rules can compute over consumer-originated rows only.
|
|
1475
|
+
*
|
|
1476
|
+
* The incident this closes (cost-watch 2026-08-06 → 08-08): playbacksam's
|
|
1477
|
+
* daily canary wrote exactly 8 rows/day for 13 straight days while PB's
|
|
1478
|
+
* organic traffic was near-zero, and the liveness rule scored PB the
|
|
1479
|
+
* healthiest consumer in the portfolio. On 08-08 EVERY row the brain
|
|
1480
|
+
* received in 24h was canary, and no rule could say so — distinguishing a
|
|
1481
|
+
* replay from real traffic took a hand-reconstructed time-window +
|
|
1482
|
+
* token-fingerprint argument, three mornings running. A consumer whose
|
|
1483
|
+
* only rows are written by kgauto's own machinery is dark, not healthy;
|
|
1484
|
+
* this column is what lets a rule print that sentence.
|
|
1485
|
+
*/
|
|
1486
|
+
type OutcomeSource = 'canary' | 'smoke' | 'eval' | 'probe' | 'synthetic';
|
|
1412
1487
|
interface RecordInput {
|
|
1413
1488
|
/** Handle from CompileResult. */
|
|
1414
1489
|
handle: string;
|
|
1490
|
+
/**
|
|
1491
|
+
* alpha.86 — self-mark for synthetic writers (migration 056). OMIT for
|
|
1492
|
+
* real traffic; never write an explicit "organic" value. See
|
|
1493
|
+
* {@link OutcomeSource}.
|
|
1494
|
+
*/
|
|
1495
|
+
source?: OutcomeSource;
|
|
1415
1496
|
/** Actual tokens consumed (post-call). */
|
|
1416
1497
|
tokensIn: number;
|
|
1417
1498
|
tokensOut: number;
|