@warmdrift/kgauto-compiler 2.0.0-alpha.91 → 2.0.0-alpha.93
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-BTP5WK3B.mjs +3448 -0
- package/dist/{chunk-BVEXV5KC.mjs → chunk-FEMPY6EC.mjs} +5 -1
- package/dist/{chunk-54IXD5BT.mjs → chunk-S4PA2BDB.mjs} +8 -3
- package/dist/{chunk-AUZTO6Q5.mjs → chunk-WFLDRRY7.mjs} +1 -1
- package/dist/{chunk-T53ISC2F.mjs → chunk-YO2ZLPSD.mjs} +10 -0
- package/dist/compile-BlMXv6QT.d.ts +53 -0
- package/dist/compile-C2IfAGV1.d.mts +53 -0
- package/dist/dialect.d.mts +11 -1
- package/dist/dialect.d.ts +11 -1
- package/dist/dialect.js +7 -2
- package/dist/dialect.mjs +5 -3
- 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 +7 -2
- 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 +13 -49
- package/dist/index.d.ts +13 -49
- package/dist/index.js +95 -11
- package/dist/index.mjs +197 -3425
- package/dist/{ir-CTx026t0.d.ts → ir-DbvOFKF-.d.ts} +96 -2
- package/dist/{ir-DeYMLWge.d.mts → ir-DyMJ84je.d.mts} +96 -2
- package/dist/key-health.js +1 -1
- package/dist/key-health.mjs +1 -1
- package/dist/probe.d.mts +46 -0
- package/dist/probe.d.ts +46 -0
- package/dist/probe.js +5767 -0
- package/dist/probe.mjs +45 -0
- package/dist/profiles.d.mts +30 -2
- package/dist/profiles.d.ts +30 -2
- package/dist/profiles.js +11 -0
- package/dist/profiles.mjs +3 -1
- package/dist/{types-Cp9ot1HV.d.ts → types-5TqjBeZD.d.ts} +1 -1
- package/dist/{types-BKbRtmUb.d.ts → types-BlrbNQfj.d.ts} +1 -1
- package/dist/{types-DD36cCbZ.d.mts → types-D441T-KC.d.mts} +1 -1
- package/dist/{types-cBzinzUR.d.mts → types-_myk4bxn.d.mts} +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,3448 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ARCHETYPE_FLOOR_DEFAULT,
|
|
3
|
+
canonicalPolicySet,
|
|
4
|
+
configFreshness,
|
|
5
|
+
createBrainQueryCache,
|
|
6
|
+
estimateChainCostUsd,
|
|
7
|
+
getArchetypePerfScore,
|
|
8
|
+
getDefaultFallbackChain,
|
|
9
|
+
getModelCompatibility,
|
|
10
|
+
isBrainQueryActiveFor,
|
|
11
|
+
policySetHas
|
|
12
|
+
} from "./chunk-S4PA2BDB.mjs";
|
|
13
|
+
import {
|
|
14
|
+
bucketContext,
|
|
15
|
+
bucketHistory,
|
|
16
|
+
bucketToolCount,
|
|
17
|
+
learningKey,
|
|
18
|
+
resolveOutputMode,
|
|
19
|
+
routeScopedModel
|
|
20
|
+
} from "./chunk-FEMPY6EC.mjs";
|
|
21
|
+
import {
|
|
22
|
+
ALIASES,
|
|
23
|
+
LATENCY_TIER_MS,
|
|
24
|
+
_setProfileBrainHook,
|
|
25
|
+
allProfiles,
|
|
26
|
+
allProfilesRaw,
|
|
27
|
+
bestEffortProfile,
|
|
28
|
+
getProfile,
|
|
29
|
+
inferProviderFromId,
|
|
30
|
+
latencyTierOf,
|
|
31
|
+
resolveModelAlias,
|
|
32
|
+
tryGetProfile
|
|
33
|
+
} from "./chunk-YO2ZLPSD.mjs";
|
|
34
|
+
|
|
35
|
+
// src/models-brain.ts
|
|
36
|
+
function isModelRow(x) {
|
|
37
|
+
if (!x || typeof x !== "object") return false;
|
|
38
|
+
const r = x;
|
|
39
|
+
return typeof r.model_id === "string" && typeof r.provider === "string";
|
|
40
|
+
}
|
|
41
|
+
function isAliasRow(x) {
|
|
42
|
+
if (!x || typeof x !== "object") return false;
|
|
43
|
+
const r = x;
|
|
44
|
+
return typeof r.alias_id === "string" && typeof r.canonical_id === "string";
|
|
45
|
+
}
|
|
46
|
+
function rowToProfile(row) {
|
|
47
|
+
try {
|
|
48
|
+
if (row.cliffs !== void 0 && row.cliffs !== null && !Array.isArray(row.cliffs)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (row.recovery !== void 0 && row.recovery !== null && !Array.isArray(row.recovery)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (row.lowering !== void 0 && row.lowering !== null && (typeof row.lowering !== "object" || Array.isArray(row.lowering))) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (row.archetype_conventions !== void 0 && row.archetype_conventions !== null && !Array.isArray(row.archetype_conventions)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
id: row.model_id,
|
|
62
|
+
provider: row.provider,
|
|
63
|
+
status: row.status ?? "current",
|
|
64
|
+
maxContextTokens: row.max_context_tokens ?? 0,
|
|
65
|
+
maxOutputTokens: row.max_output_tokens ?? 0,
|
|
66
|
+
maxTools: row.max_tools ?? 0,
|
|
67
|
+
parallelToolCalls: row.parallel_tool_calls ?? false,
|
|
68
|
+
structuredOutput: row.structured_output ?? "none",
|
|
69
|
+
systemPromptMode: row.system_prompt_mode ?? "inline",
|
|
70
|
+
streaming: row.streaming ?? true,
|
|
71
|
+
cliffs: row.cliffs ?? [],
|
|
72
|
+
costInputPer1m: row.cost_input_per_1m ?? 0,
|
|
73
|
+
costOutputPer1m: row.cost_output_per_1m ?? 0,
|
|
74
|
+
lowering: row.lowering ?? { system: { mode: "inline" }, cache: { strategy: "unsupported" } },
|
|
75
|
+
recovery: row.recovery ?? [],
|
|
76
|
+
strengths: row.strengths ?? [],
|
|
77
|
+
weaknesses: row.weaknesses ?? [],
|
|
78
|
+
notes: row.notes ?? void 0,
|
|
79
|
+
verifiedAgainstDocs: row.verified_against_docs ?? void 0,
|
|
80
|
+
archetypePerf: row.archetype_perf ?? void 0,
|
|
81
|
+
// alpha.41 — family-resolution fields. `family` may be null pre-
|
|
82
|
+
// migration 024; runtime falls back to deriveFamilyFromModelId at
|
|
83
|
+
// resolution time (see family-resolution.ts, G1).
|
|
84
|
+
family: row.family ?? void 0,
|
|
85
|
+
versionAdded: row.version_added ?? void 0,
|
|
86
|
+
active: row.active ?? void 0,
|
|
87
|
+
// alpha.49 — executable-knowledge fields (migration 028). Invalid
|
|
88
|
+
// latency_tier → undefined (latencyTierOf derives from tags; not
|
|
89
|
+
// safety-critical). archetype_conventions already array-validated above.
|
|
90
|
+
latencyTier: normalizeLatencyTier(row.latency_tier),
|
|
91
|
+
archetypeConventions: row.archetype_conventions ?? void 0
|
|
92
|
+
};
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
var VALID_LATENCY_TIERS = /* @__PURE__ */ new Set(["fast", "medium", "slow"]);
|
|
98
|
+
function normalizeLatencyTier(v) {
|
|
99
|
+
return typeof v === "string" && VALID_LATENCY_TIERS.has(v) ? v : void 0;
|
|
100
|
+
}
|
|
101
|
+
function profileToRow(profile, opts = {}) {
|
|
102
|
+
const row = {
|
|
103
|
+
model_id: profile.id,
|
|
104
|
+
provider: profile.provider,
|
|
105
|
+
status: profile.status,
|
|
106
|
+
max_context_tokens: profile.maxContextTokens,
|
|
107
|
+
max_output_tokens: profile.maxOutputTokens,
|
|
108
|
+
max_tools: profile.maxTools,
|
|
109
|
+
parallel_tool_calls: profile.parallelToolCalls,
|
|
110
|
+
structured_output: profile.structuredOutput,
|
|
111
|
+
system_prompt_mode: profile.systemPromptMode,
|
|
112
|
+
streaming: profile.streaming,
|
|
113
|
+
cliffs: profile.cliffs,
|
|
114
|
+
cost_input_per_1m: profile.costInputPer1m,
|
|
115
|
+
cost_output_per_1m: profile.costOutputPer1m,
|
|
116
|
+
lowering: profile.lowering,
|
|
117
|
+
recovery: profile.recovery,
|
|
118
|
+
strengths: profile.strengths,
|
|
119
|
+
weaknesses: profile.weaknesses,
|
|
120
|
+
notes: profile.notes ?? null,
|
|
121
|
+
archetype_perf: profile.archetypePerf ?? null,
|
|
122
|
+
active: opts.active ?? profile.active ?? true,
|
|
123
|
+
// alpha.41 — round-trip family + version_added when present on profile.
|
|
124
|
+
// version_added is operator-controlled via opts; profile-side value is
|
|
125
|
+
// used only when opts didn't override.
|
|
126
|
+
family: profile.family ?? null,
|
|
127
|
+
// alpha.49 — round-trip the executable-knowledge fields (migration 028)
|
|
128
|
+
// so a reseed from bundled profiles makes the latency lever + schema
|
|
129
|
+
// conventions live warm. Closes the silently-dropped-field gap.
|
|
130
|
+
latency_tier: profile.latencyTier ?? null,
|
|
131
|
+
archetype_conventions: profile.archetypeConventions ?? null
|
|
132
|
+
};
|
|
133
|
+
if (opts.verifiedAgainstDocs !== void 0) {
|
|
134
|
+
row.verified_against_docs = opts.verifiedAgainstDocs;
|
|
135
|
+
} else if (profile.verifiedAgainstDocs !== void 0) {
|
|
136
|
+
const v = profile.verifiedAgainstDocs;
|
|
137
|
+
row.verified_against_docs = /^\d{4}-\d{2}-\d{2}/.test(v) ? v : null;
|
|
138
|
+
}
|
|
139
|
+
if (opts.versionAdded !== void 0) row.version_added = opts.versionAdded;
|
|
140
|
+
else if (profile.versionAdded !== void 0) row.version_added = profile.versionAdded;
|
|
141
|
+
if (opts.versionRemoved !== void 0) row.version_removed = opts.versionRemoved;
|
|
142
|
+
return row;
|
|
143
|
+
}
|
|
144
|
+
function mapRowsToModels(rows) {
|
|
145
|
+
const out = /* @__PURE__ */ new Map();
|
|
146
|
+
for (const row of rows) {
|
|
147
|
+
if (!isModelRow(row)) continue;
|
|
148
|
+
const profile = rowToProfile(row);
|
|
149
|
+
if (profile) out.set(profile.id, profile);
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
function mapRowsToAliases(rows) {
|
|
154
|
+
const out = {};
|
|
155
|
+
for (const row of rows) {
|
|
156
|
+
if (!isAliasRow(row)) continue;
|
|
157
|
+
out[row.alias_id] = row.canonical_id;
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
function bundledModels() {
|
|
162
|
+
return new Map(allProfilesRaw().map((p) => [p.id, p]));
|
|
163
|
+
}
|
|
164
|
+
function bundledAliases() {
|
|
165
|
+
return { ...ALIASES };
|
|
166
|
+
}
|
|
167
|
+
var loadModelsFromBrain = createBrainQueryCache({
|
|
168
|
+
table: "kgauto_models",
|
|
169
|
+
mapRows: mapRowsToModels,
|
|
170
|
+
bundledFallback: bundledModels
|
|
171
|
+
});
|
|
172
|
+
var loadAliasesFromBrain = createBrainQueryCache({
|
|
173
|
+
table: "kgauto_aliases",
|
|
174
|
+
mapRows: mapRowsToAliases,
|
|
175
|
+
bundledFallback: bundledAliases
|
|
176
|
+
});
|
|
177
|
+
_setProfileBrainHook({
|
|
178
|
+
getProfile: (canonical) => loadModelsFromBrain().get(canonical),
|
|
179
|
+
resolveAlias: (id) => loadAliasesFromBrain()[id]
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// src/exclusion-findings-brain.ts
|
|
183
|
+
function isValidVerdict(v) {
|
|
184
|
+
return v === "recommend-probe" || v === "unblock" || v === "stay-excluded" || v === "inconclusive";
|
|
185
|
+
}
|
|
186
|
+
function isValidConfidence(v) {
|
|
187
|
+
return v === "high" || v === "medium" || v === "low";
|
|
188
|
+
}
|
|
189
|
+
function isRawFindingRow(x) {
|
|
190
|
+
if (!x || typeof x !== "object") return false;
|
|
191
|
+
const r = x;
|
|
192
|
+
return typeof r.intent_archetype === "string" && typeof r.excluded_model === "string" && typeof r.excluded_provider === "string" && typeof r.verdict === "string" && typeof r.message === "string" && typeof r.suggestion === "string" && typeof r.confidence === "string";
|
|
193
|
+
}
|
|
194
|
+
function mapRowsToFindings(rows) {
|
|
195
|
+
const out = [];
|
|
196
|
+
for (const row of rows) {
|
|
197
|
+
if (!isRawFindingRow(row)) continue;
|
|
198
|
+
if (!isValidVerdict(row.verdict)) continue;
|
|
199
|
+
if (!isValidConfidence(row.confidence)) continue;
|
|
200
|
+
let savings = null;
|
|
201
|
+
if (typeof row.estimated_savings_usd_30d === "number") {
|
|
202
|
+
savings = Number.isFinite(row.estimated_savings_usd_30d) ? row.estimated_savings_usd_30d : null;
|
|
203
|
+
} else if (typeof row.estimated_savings_usd_30d === "string") {
|
|
204
|
+
const n = Number(row.estimated_savings_usd_30d);
|
|
205
|
+
savings = Number.isFinite(n) ? n : null;
|
|
206
|
+
}
|
|
207
|
+
out.push({
|
|
208
|
+
archetype: row.intent_archetype,
|
|
209
|
+
excludedModel: row.excluded_model,
|
|
210
|
+
excludedProvider: row.excluded_provider,
|
|
211
|
+
verdict: row.verdict,
|
|
212
|
+
estimatedSavingsUsd30d: savings,
|
|
213
|
+
message: row.message,
|
|
214
|
+
suggestion: row.suggestion,
|
|
215
|
+
confidence: row.confidence,
|
|
216
|
+
evidence: row.evidence && typeof row.evidence === "object" ? row.evidence : void 0
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
var snapshots = /* @__PURE__ */ new Map();
|
|
222
|
+
function exclusionFindingsFreshness(appId) {
|
|
223
|
+
return snapshots.get(appId)?.loaded ? "warm" : "cold";
|
|
224
|
+
}
|
|
225
|
+
var runtime;
|
|
226
|
+
var warnedOnce = false;
|
|
227
|
+
var DEFAULT_FINDINGS_ENDPOINT = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions";
|
|
228
|
+
function configureExclusionFindingsBrain(rt) {
|
|
229
|
+
runtime = rt;
|
|
230
|
+
snapshots.clear();
|
|
231
|
+
warnedOnce = false;
|
|
232
|
+
}
|
|
233
|
+
function isExclusionFindingsBrainActive() {
|
|
234
|
+
return runtime !== void 0;
|
|
235
|
+
}
|
|
236
|
+
function getStaleExclusionFindings(opts) {
|
|
237
|
+
const rt = runtime;
|
|
238
|
+
if (!rt) return [];
|
|
239
|
+
const appId = opts.appId;
|
|
240
|
+
if (!appId) return [];
|
|
241
|
+
let snap = snapshots.get(appId);
|
|
242
|
+
if (!snap) {
|
|
243
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
244
|
+
snapshots.set(appId, snap);
|
|
245
|
+
}
|
|
246
|
+
const now = Date.now();
|
|
247
|
+
const stale = snap.expiresAt <= now;
|
|
248
|
+
if (stale && !snap.refreshing) {
|
|
249
|
+
snap.refreshing = true;
|
|
250
|
+
void asyncRefresh(rt, appId);
|
|
251
|
+
}
|
|
252
|
+
if (opts.archetype) {
|
|
253
|
+
return snap.data.filter((f) => f.archetype === opts.archetype);
|
|
254
|
+
}
|
|
255
|
+
return snap.data;
|
|
256
|
+
}
|
|
257
|
+
var pendingRefreshes = /* @__PURE__ */ new Map();
|
|
258
|
+
async function asyncRefresh(rt, appId) {
|
|
259
|
+
const promise = doRefresh(rt, appId);
|
|
260
|
+
pendingRefreshes.set(appId, promise);
|
|
261
|
+
try {
|
|
262
|
+
await promise;
|
|
263
|
+
} finally {
|
|
264
|
+
if (pendingRefreshes.get(appId) === promise) {
|
|
265
|
+
pendingRefreshes.delete(appId);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function doRefresh(rt, appId) {
|
|
270
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
271
|
+
let snap = snapshots.get(appId);
|
|
272
|
+
if (!snap) {
|
|
273
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
274
|
+
snapshots.set(appId, snap);
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
278
|
+
if (!res.ok) {
|
|
279
|
+
throw new Error(`findings ${res.status}: ${res.statusText}`);
|
|
280
|
+
}
|
|
281
|
+
const body = await res.json();
|
|
282
|
+
if (runtime !== rt) return;
|
|
283
|
+
const rows = Array.isArray(body) ? mapRowsToFindings(body) : [];
|
|
284
|
+
snap.data = rows;
|
|
285
|
+
snap.loaded = true;
|
|
286
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
287
|
+
snap.refreshing = false;
|
|
288
|
+
} catch (err) {
|
|
289
|
+
if (runtime !== rt) return;
|
|
290
|
+
snap.refreshing = false;
|
|
291
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
292
|
+
if (!warnedOnce) {
|
|
293
|
+
warnedOnce = true;
|
|
294
|
+
(rt.onError ?? defaultOnError)(err);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function defaultOnError(err) {
|
|
299
|
+
console.warn(
|
|
300
|
+
"[kgauto] exclusion-findings fetch failed (using empty fallback):",
|
|
301
|
+
err
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/family-resolution.ts
|
|
306
|
+
var FamilyResolutionError = class extends Error {
|
|
307
|
+
family;
|
|
308
|
+
cause;
|
|
309
|
+
constructor(family, cause) {
|
|
310
|
+
super(
|
|
311
|
+
`Family "${family}" did not resolve to any current+active model. ${cause}. Pass a literal model id in ir.models, or call getRecommendedPrimary({ family, fallback }) at IR-construction time.`
|
|
312
|
+
);
|
|
313
|
+
this.name = "FamilyResolutionError";
|
|
314
|
+
this.family = family;
|
|
315
|
+
this.cause = cause;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
function deriveFamilyFromModelId(modelId) {
|
|
319
|
+
if (typeof modelId !== "string" || modelId.length === 0) return null;
|
|
320
|
+
if (modelId.startsWith("claude-opus-")) return "claude-opus";
|
|
321
|
+
if (modelId.startsWith("claude-sonnet-")) return "claude-sonnet";
|
|
322
|
+
if (modelId.startsWith("claude-haiku-")) return "claude-haiku";
|
|
323
|
+
if (modelId.startsWith("gemini-") && modelId.includes("flash-lite")) {
|
|
324
|
+
return "gemini-flash-lite";
|
|
325
|
+
}
|
|
326
|
+
if (modelId.startsWith("gemini-") && modelId.includes("flash")) {
|
|
327
|
+
return "gemini-flash";
|
|
328
|
+
}
|
|
329
|
+
if (modelId.startsWith("gemini-") && modelId.includes("pro")) {
|
|
330
|
+
return "gemini-pro";
|
|
331
|
+
}
|
|
332
|
+
if (modelId.startsWith("deepseek-") && modelId.includes("pro")) {
|
|
333
|
+
return "deepseek-reasoner";
|
|
334
|
+
}
|
|
335
|
+
if (modelId.startsWith("deepseek-")) return "deepseek-chat";
|
|
336
|
+
if (modelId.startsWith("gpt-")) return "openai-gpt";
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
function familyOf(profile) {
|
|
340
|
+
return profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
341
|
+
}
|
|
342
|
+
function archetypePerfFor(profile, archetype) {
|
|
343
|
+
if (!archetype) return 0;
|
|
344
|
+
const score = profile.archetypePerf?.[archetype];
|
|
345
|
+
return typeof score === "number" ? score : 5;
|
|
346
|
+
}
|
|
347
|
+
function selectCandidates(registry, family, archetype, appId) {
|
|
348
|
+
const out = [];
|
|
349
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
350
|
+
if (appId && archetype) {
|
|
351
|
+
const findings = getStaleExclusionFindings({ appId, archetype });
|
|
352
|
+
for (const f of findings) {
|
|
353
|
+
if (f.verdict === "stay-excluded") excluded.add(f.excludedModel);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
for (const profile of registry.values()) {
|
|
357
|
+
if (familyOf(profile) !== family) continue;
|
|
358
|
+
if (profile.status !== "current") continue;
|
|
359
|
+
if (profile.active === false) continue;
|
|
360
|
+
if (archetype) {
|
|
361
|
+
if (archetypePerfFor(profile, archetype) < ARCHETYPE_FLOOR_DEFAULT) continue;
|
|
362
|
+
}
|
|
363
|
+
if (excluded.has(profile.id)) continue;
|
|
364
|
+
out.push(profile);
|
|
365
|
+
}
|
|
366
|
+
return out;
|
|
367
|
+
}
|
|
368
|
+
function sortCandidates(candidates, archetype) {
|
|
369
|
+
const sorted = [...candidates];
|
|
370
|
+
sorted.sort((a, b) => {
|
|
371
|
+
const perfA = archetypePerfFor(a, archetype);
|
|
372
|
+
const perfB = archetypePerfFor(b, archetype);
|
|
373
|
+
if (perfA !== perfB) return perfB - perfA;
|
|
374
|
+
const vA = a.versionAdded ?? "";
|
|
375
|
+
const vB = b.versionAdded ?? "";
|
|
376
|
+
if (vA !== vB) return vA < vB ? 1 : -1;
|
|
377
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
378
|
+
});
|
|
379
|
+
return sorted;
|
|
380
|
+
}
|
|
381
|
+
function getRecommendedPrimary(opts) {
|
|
382
|
+
if (opts.posture === "locked") return opts.fallback;
|
|
383
|
+
const registry = loadModelsFromBrain();
|
|
384
|
+
const candidates = selectCandidates(
|
|
385
|
+
registry,
|
|
386
|
+
opts.family,
|
|
387
|
+
opts.archetype,
|
|
388
|
+
opts.appId
|
|
389
|
+
);
|
|
390
|
+
if (candidates.length === 0) return opts.fallback;
|
|
391
|
+
const sorted = sortCandidates(candidates, opts.archetype);
|
|
392
|
+
return sorted[0]?.id ?? opts.fallback;
|
|
393
|
+
}
|
|
394
|
+
function resolveFamilyEntry(family, ctx) {
|
|
395
|
+
if (typeof family !== "string" || family.length === 0) {
|
|
396
|
+
throw new FamilyResolutionError(
|
|
397
|
+
String(family),
|
|
398
|
+
"Family tag must be a non-empty string"
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
const registry = loadModelsFromBrain();
|
|
402
|
+
const candidates = selectCandidates(
|
|
403
|
+
registry,
|
|
404
|
+
family,
|
|
405
|
+
ctx.archetype,
|
|
406
|
+
ctx.appId
|
|
407
|
+
);
|
|
408
|
+
if (candidates.length === 0) {
|
|
409
|
+
let anyInFamily = false;
|
|
410
|
+
for (const profile of registry.values()) {
|
|
411
|
+
if (familyOf(profile) === family) {
|
|
412
|
+
anyInFamily = true;
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const cause = anyInFamily ? `Family has registered models but none are current+active${ctx.archetype ? ` at archetype-perf >= ${ARCHETYPE_FLOOR_DEFAULT} for "${ctx.archetype}"` : ""}${ctx.appId ? ` and not excluded for app "${ctx.appId}"` : ""}` : `No models in brain registry carry family="${family}". Check the taxonomy table in family-resolution.ts or migration 024`;
|
|
417
|
+
throw new FamilyResolutionError(family, cause);
|
|
418
|
+
}
|
|
419
|
+
const sorted = sortCandidates(candidates, ctx.archetype);
|
|
420
|
+
const winner = sorted[0];
|
|
421
|
+
if (!winner) {
|
|
422
|
+
throw new FamilyResolutionError(
|
|
423
|
+
family,
|
|
424
|
+
"Candidates non-empty but sort returned undefined \u2014 internal invariant violation"
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
return winner.id;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/tokenizer.ts
|
|
431
|
+
var tokenizerImpl = defaultCharBasedCounter;
|
|
432
|
+
function defaultCharBasedCounter(text) {
|
|
433
|
+
if (!text) return 0;
|
|
434
|
+
return Math.max(1, Math.ceil(text.length / 4));
|
|
435
|
+
}
|
|
436
|
+
function setTokenizer(impl) {
|
|
437
|
+
tokenizerImpl = impl;
|
|
438
|
+
}
|
|
439
|
+
function resetTokenizer() {
|
|
440
|
+
tokenizerImpl = defaultCharBasedCounter;
|
|
441
|
+
}
|
|
442
|
+
function countTokens(text) {
|
|
443
|
+
if (!text) return 0;
|
|
444
|
+
try {
|
|
445
|
+
const n = tokenizerImpl(text);
|
|
446
|
+
return Number.isFinite(n) && n >= 0 ? n : defaultCharBasedCounter(text);
|
|
447
|
+
} catch {
|
|
448
|
+
return defaultCharBasedCounter(text);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function countToolTokens(tool) {
|
|
452
|
+
const namePart = countTokens(tool.name);
|
|
453
|
+
const descPart = tool.description ? countTokens(tool.description) : 0;
|
|
454
|
+
const paramPart = tool.parameters ? countTokens(JSON.stringify(tool.parameters)) : 0;
|
|
455
|
+
return namePart + descPart + paramPart + 8;
|
|
456
|
+
}
|
|
457
|
+
function countMessagesTokens(messages) {
|
|
458
|
+
let total = 0;
|
|
459
|
+
for (const m of messages) {
|
|
460
|
+
total += countTokens(m.content) + 4;
|
|
461
|
+
}
|
|
462
|
+
return total;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// src/passes.ts
|
|
466
|
+
function passSlice(ir) {
|
|
467
|
+
const intent = ir.intent.archetype;
|
|
468
|
+
const before = ir.sections.length;
|
|
469
|
+
const kept = ir.sections.filter((s) => !s.intents || s.intents.length === 0 || s.intents.includes(intent));
|
|
470
|
+
const dropped = before - kept.length;
|
|
471
|
+
if (dropped === 0) return { value: ir, mutations: [] };
|
|
472
|
+
return {
|
|
473
|
+
value: { ...ir, sections: kept },
|
|
474
|
+
mutations: [
|
|
475
|
+
{
|
|
476
|
+
id: `slice-${dropped}`,
|
|
477
|
+
source: "static_pass",
|
|
478
|
+
passName: "slice",
|
|
479
|
+
description: `Dropped ${dropped} of ${before} sections not tagged for intent=${intent}`
|
|
480
|
+
}
|
|
481
|
+
]
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function passDedupe(ir) {
|
|
485
|
+
const seen = /* @__PURE__ */ new Map();
|
|
486
|
+
const order = [];
|
|
487
|
+
for (const s of ir.sections) {
|
|
488
|
+
const key = simpleHash(s.text.trim());
|
|
489
|
+
if (!seen.has(key)) {
|
|
490
|
+
seen.set(key, s);
|
|
491
|
+
order.push(key);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
const deduped = order.map((k) => seen.get(k));
|
|
495
|
+
const dropped = ir.sections.length - deduped.length;
|
|
496
|
+
if (dropped === 0) return { value: ir, mutations: [] };
|
|
497
|
+
return {
|
|
498
|
+
value: { ...ir, sections: deduped },
|
|
499
|
+
mutations: [
|
|
500
|
+
{
|
|
501
|
+
id: `dedupe-${dropped}`,
|
|
502
|
+
source: "static_pass",
|
|
503
|
+
passName: "dedupe",
|
|
504
|
+
description: `Removed ${dropped} duplicate section(s) (by text hash)`
|
|
505
|
+
}
|
|
506
|
+
]
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function passToolRelevance(ir, opts = {}) {
|
|
510
|
+
if (!ir.tools || ir.tools.length === 0) return { value: ir, mutations: [] };
|
|
511
|
+
const threshold = opts.threshold ?? 0.2;
|
|
512
|
+
const intent = ir.intent.archetype;
|
|
513
|
+
const scored = ir.tools.map((t) => {
|
|
514
|
+
const score = t.relevanceByIntent?.[intent] ?? 0.5;
|
|
515
|
+
return { tool: t, score };
|
|
516
|
+
});
|
|
517
|
+
const kept = scored.filter((s) => s.score >= threshold).sort((a, b) => b.score - a.score).map((s) => s.tool);
|
|
518
|
+
const limited = opts.maxKeep ? kept.slice(0, opts.maxKeep) : kept;
|
|
519
|
+
const dropped = ir.tools.length - limited.length;
|
|
520
|
+
if (dropped === 0) return { value: ir, mutations: [] };
|
|
521
|
+
return {
|
|
522
|
+
value: { ...ir, tools: limited },
|
|
523
|
+
mutations: [
|
|
524
|
+
{
|
|
525
|
+
id: `tool-relevance-${dropped}`,
|
|
526
|
+
source: "static_pass",
|
|
527
|
+
passName: "tool_relevance",
|
|
528
|
+
description: `Dropped ${dropped} of ${ir.tools.length} tools below relevance ${threshold} for intent=${intent}`
|
|
529
|
+
}
|
|
530
|
+
]
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function totalHistoryTokens(history) {
|
|
534
|
+
let total = 0;
|
|
535
|
+
for (const m of history) {
|
|
536
|
+
if (typeof m.content === "string") total += countTokens(m.content);
|
|
537
|
+
}
|
|
538
|
+
return total;
|
|
539
|
+
}
|
|
540
|
+
function passCompressHistory(ir, opts = {}) {
|
|
541
|
+
const history = ir.history;
|
|
542
|
+
if (!history || history.length === 0) {
|
|
543
|
+
return { value: ir, mutations: [], historyTokensTotal: 0 };
|
|
544
|
+
}
|
|
545
|
+
const keepRecent = opts.keepRecent ?? 4;
|
|
546
|
+
const summarizeOlderThan = opts.summarizeOlderThan ?? 8;
|
|
547
|
+
const summarizeAboveTokens = opts.summarizeAboveTokens;
|
|
548
|
+
const historyTokensTotal = totalHistoryTokens(history);
|
|
549
|
+
const countThresholdHit = history.length > summarizeOlderThan;
|
|
550
|
+
const tokenThresholdHit = summarizeAboveTokens !== void 0 && historyTokensTotal > summarizeAboveTokens;
|
|
551
|
+
if (!countThresholdHit && !tokenThresholdHit) {
|
|
552
|
+
return { value: ir, mutations: [], historyTokensTotal };
|
|
553
|
+
}
|
|
554
|
+
if (history.length > keepRecent) {
|
|
555
|
+
const cutIndex = history.length - keepRecent;
|
|
556
|
+
const old = history.slice(0, cutIndex);
|
|
557
|
+
const recent = history.slice(cutIndex);
|
|
558
|
+
const userTurns = old.filter((m) => m.role === "user");
|
|
559
|
+
const firstUserLine = userTurns[0]?.content.split("\n")[0]?.slice(0, 200) ?? "";
|
|
560
|
+
const oldTokens = totalHistoryTokens(old);
|
|
561
|
+
const trigger = tokenThresholdHit && !countThresholdHit ? "tokens" : "count";
|
|
562
|
+
const summary = {
|
|
563
|
+
role: "system",
|
|
564
|
+
content: `[Earlier conversation: ${old.length} turns omitted (~${oldTokens} tokens). First user message: "${firstUserLine}"]`
|
|
565
|
+
};
|
|
566
|
+
return {
|
|
567
|
+
value: { ...ir, history: [summary, ...recent] },
|
|
568
|
+
mutations: [
|
|
569
|
+
{
|
|
570
|
+
id: `compress-history-${old.length}`,
|
|
571
|
+
source: "static_pass",
|
|
572
|
+
passName: "compress_history",
|
|
573
|
+
description: trigger === "tokens" ? `Compressed ${old.length} old turns (~${oldTokens} tokens) into 1 summary \u2014 token threshold ${summarizeAboveTokens} exceeded (kept ${keepRecent} recent)` : `Compressed ${old.length} old turns into 1 summary (kept ${keepRecent} recent)`
|
|
574
|
+
}
|
|
575
|
+
],
|
|
576
|
+
historyTokensTotal
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
if (tokenThresholdHit) {
|
|
580
|
+
let fattestIdx = -1;
|
|
581
|
+
let fattestTokens = 0;
|
|
582
|
+
for (let i = 0; i < history.length; i++) {
|
|
583
|
+
const m = history[i];
|
|
584
|
+
if (!m || typeof m.content !== "string") continue;
|
|
585
|
+
const t = countTokens(m.content);
|
|
586
|
+
if (t > fattestTokens) {
|
|
587
|
+
fattestTokens = t;
|
|
588
|
+
fattestIdx = i;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
const FAT_DOMINANCE_FLOOR = 0.3;
|
|
592
|
+
const fattest = fattestIdx >= 0 ? history[fattestIdx] : void 0;
|
|
593
|
+
if (fattest && historyTokensTotal > 0 && fattestTokens / historyTokensTotal >= FAT_DOMINANCE_FLOOR) {
|
|
594
|
+
const firstLine = fattest.content.split("\n")[0]?.slice(0, 200) ?? "";
|
|
595
|
+
const newContent = `[Earlier ${fattest.role} message content omitted: ~${fattestTokens} tokens. Preview: "${firstLine}"]`;
|
|
596
|
+
const newHistory = history.slice();
|
|
597
|
+
newHistory[fattestIdx] = { ...fattest, content: newContent };
|
|
598
|
+
return {
|
|
599
|
+
value: { ...ir, history: newHistory },
|
|
600
|
+
mutations: [
|
|
601
|
+
{
|
|
602
|
+
id: `compress-fat-message-${fattestIdx}`,
|
|
603
|
+
source: "static_pass",
|
|
604
|
+
passName: "compress_history",
|
|
605
|
+
description: `Replaced fat ${fattest.role} message #${fattestIdx} content (~${fattestTokens} of ${historyTokensTotal} tokens, ${Math.round(fattestTokens / historyTokensTotal * 100)}% of history) with summary stub \u2014 token threshold ${summarizeAboveTokens} exceeded (history.length ${history.length} <= keepRecent ${keepRecent}, slice not possible)`
|
|
606
|
+
}
|
|
607
|
+
],
|
|
608
|
+
historyTokensTotal
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return { value: ir, mutations: [], historyTokensTotal };
|
|
613
|
+
}
|
|
614
|
+
function passApplyCliffs(ir, profile, estimatedInputTokens) {
|
|
615
|
+
const mutations = [];
|
|
616
|
+
const hints = { qualityWarning: [] };
|
|
617
|
+
let nextIR = ir;
|
|
618
|
+
const sequentialMode = nextIR.constraints?.toolOrchestration === "sequential";
|
|
619
|
+
for (const cliff of profile.cliffs) {
|
|
620
|
+
if (sequentialMode && cliff.reason.includes("L-040")) {
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
let triggered = false;
|
|
624
|
+
switch (cliff.metric) {
|
|
625
|
+
case "input_tokens":
|
|
626
|
+
triggered = estimatedInputTokens >= cliff.threshold;
|
|
627
|
+
break;
|
|
628
|
+
case "tool_count":
|
|
629
|
+
triggered = (nextIR.tools?.length ?? 0) >= cliff.threshold;
|
|
630
|
+
break;
|
|
631
|
+
case "history_turns":
|
|
632
|
+
triggered = (nextIR.history?.length ?? 0) >= cliff.threshold;
|
|
633
|
+
break;
|
|
634
|
+
case "thinking_with_short_output":
|
|
635
|
+
triggered = !!nextIR.constraints?.expectedShortOutput;
|
|
636
|
+
break;
|
|
637
|
+
}
|
|
638
|
+
if (triggered && cliff.whenIntent && nextIR.intent.archetype !== cliff.whenIntent) {
|
|
639
|
+
triggered = false;
|
|
640
|
+
}
|
|
641
|
+
if (!triggered) continue;
|
|
642
|
+
switch (cliff.action) {
|
|
643
|
+
case "drop_to_top_relevant": {
|
|
644
|
+
const targetCount = Math.max(
|
|
645
|
+
1,
|
|
646
|
+
Math.min(
|
|
647
|
+
Math.floor(cliff.threshold * 0.75),
|
|
648
|
+
Math.floor((nextIR.tools?.length ?? 0) / 2)
|
|
649
|
+
)
|
|
650
|
+
);
|
|
651
|
+
if (nextIR.tools && nextIR.tools.length > targetCount) {
|
|
652
|
+
const intent = nextIR.intent.archetype;
|
|
653
|
+
const scored = nextIR.tools.map((t) => ({ tool: t, score: t.relevanceByIntent?.[intent] ?? 0.5 })).sort((a, b) => b.score - a.score).slice(0, targetCount).map((s) => s.tool);
|
|
654
|
+
nextIR = { ...nextIR, tools: scored };
|
|
655
|
+
mutations.push({
|
|
656
|
+
id: `cliff-${cliff.metric}`,
|
|
657
|
+
source: "cliff_guard",
|
|
658
|
+
passName: "apply_cliffs",
|
|
659
|
+
description: `${profile.id}: ${cliff.reason}; trimmed tools to ${targetCount}`
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
case "force_thinking_budget_zero":
|
|
665
|
+
hints.forceThinkingZero = true;
|
|
666
|
+
mutations.push({
|
|
667
|
+
id: `cliff-thinking-zero`,
|
|
668
|
+
source: "cliff_guard",
|
|
669
|
+
passName: "apply_cliffs",
|
|
670
|
+
description: `${profile.id}: ${cliff.reason}`
|
|
671
|
+
});
|
|
672
|
+
break;
|
|
673
|
+
case "force_terse_output":
|
|
674
|
+
hints.forceTerseOutput = true;
|
|
675
|
+
mutations.push({
|
|
676
|
+
id: `cliff-terse`,
|
|
677
|
+
source: "cliff_guard",
|
|
678
|
+
passName: "apply_cliffs",
|
|
679
|
+
description: `${profile.id}: ${cliff.reason}`
|
|
680
|
+
});
|
|
681
|
+
break;
|
|
682
|
+
case "downgrade_quality_warning":
|
|
683
|
+
hints.qualityWarning.push(cliff.reason);
|
|
684
|
+
mutations.push({
|
|
685
|
+
id: `cliff-quality-warning`,
|
|
686
|
+
source: "cliff_guard",
|
|
687
|
+
passName: "apply_cliffs",
|
|
688
|
+
description: `${profile.id}: ${cliff.reason}`
|
|
689
|
+
});
|
|
690
|
+
break;
|
|
691
|
+
case "escalate_target":
|
|
692
|
+
hints.escalateRequested = true;
|
|
693
|
+
mutations.push({
|
|
694
|
+
id: `cliff-escalate`,
|
|
695
|
+
source: "cliff_guard",
|
|
696
|
+
passName: "apply_cliffs",
|
|
697
|
+
description: `${profile.id}: ${cliff.reason}`
|
|
698
|
+
});
|
|
699
|
+
break;
|
|
700
|
+
case "strip_tools": {
|
|
701
|
+
const droppedCount = nextIR.tools?.length ?? 0;
|
|
702
|
+
if (droppedCount > 0) {
|
|
703
|
+
nextIR = { ...nextIR, tools: [] };
|
|
704
|
+
mutations.push({
|
|
705
|
+
id: `cliff-strip-tools${cliff.whenIntent ? `-${cliff.whenIntent}` : ""}`,
|
|
706
|
+
source: "cliff_guard",
|
|
707
|
+
passName: "apply_cliffs",
|
|
708
|
+
description: `${profile.id}: ${cliff.reason} \u2014 stripped ${droppedCount} tools`
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return { value: { ir: nextIR, loweringHints: hints }, mutations };
|
|
716
|
+
}
|
|
717
|
+
var LATENCY_OVERAGE_WEIGHT = 0.6;
|
|
718
|
+
var LATENCY_PENALTY_CAP = 1;
|
|
719
|
+
var QUALITY_GATE_PENALTY = 4;
|
|
720
|
+
var PROMOTION_BOOST = 5;
|
|
721
|
+
function effectiveConventions(profile) {
|
|
722
|
+
const own = profile.archetypeConventions ?? [];
|
|
723
|
+
if (own.length > 0) return own;
|
|
724
|
+
const family = profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
725
|
+
if (!family) return [];
|
|
726
|
+
const repId = familyRepId(family);
|
|
727
|
+
if (!repId || repId === profile.id) return [];
|
|
728
|
+
const rep = tryGetProfile(repId);
|
|
729
|
+
return rep?.archetypeConventions ?? [];
|
|
730
|
+
}
|
|
731
|
+
function passScoreTargets(ir, opts) {
|
|
732
|
+
const constraints = ir.constraints ?? {};
|
|
733
|
+
const policy = opts.policy ?? {};
|
|
734
|
+
const blockedSet = canonicalPolicySet(policy.blockedModels);
|
|
735
|
+
const preferredSet = canonicalPolicySet(policy.preferredModels);
|
|
736
|
+
const scores = [];
|
|
737
|
+
const policyMutations = [];
|
|
738
|
+
const rawPromotion = opts.promotion;
|
|
739
|
+
const promotionDeferred = rawPromotion !== void 0 && (policy.preferredModels?.length ?? 0) > 0;
|
|
740
|
+
const promotion = promotionDeferred ? void 0 : rawPromotion;
|
|
741
|
+
const modelIds = ir.models.filter((m) => typeof m === "string");
|
|
742
|
+
for (const modelId of modelIds) {
|
|
743
|
+
let profile;
|
|
744
|
+
try {
|
|
745
|
+
profile = opts.profilesById(modelId);
|
|
746
|
+
} catch {
|
|
747
|
+
scores.push({
|
|
748
|
+
modelId,
|
|
749
|
+
estimatedCostUsd: 0,
|
|
750
|
+
fits: false,
|
|
751
|
+
rejectReasons: ["unknown_model_id"],
|
|
752
|
+
qualityScore: 0,
|
|
753
|
+
rank: -Infinity
|
|
754
|
+
});
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
const reasons = [];
|
|
758
|
+
if (policySetHas(blockedSet, modelId)) {
|
|
759
|
+
reasons.push(`blocked_by_policy (consumer gated this model \u2014 see CompilePolicy.blockedModels)`);
|
|
760
|
+
}
|
|
761
|
+
if (opts.estimatedInputTokens > profile.maxContextTokens * 0.9) {
|
|
762
|
+
reasons.push(`exceeds context budget (${opts.estimatedInputTokens} > 0.9*${profile.maxContextTokens})`);
|
|
763
|
+
}
|
|
764
|
+
if ((ir.tools?.length ?? 0) > profile.maxTools) {
|
|
765
|
+
reasons.push(`exceeds maxTools (${ir.tools?.length} > ${profile.maxTools})`);
|
|
766
|
+
}
|
|
767
|
+
if (constraints.structuredOutput && profile.structuredOutput === "none") {
|
|
768
|
+
reasons.push(`structuredOutput requested but model has none`);
|
|
769
|
+
}
|
|
770
|
+
let qualityPenalty = 0;
|
|
771
|
+
for (const cliff of profile.cliffs) {
|
|
772
|
+
if (cliff.action !== "downgrade_quality_warning") continue;
|
|
773
|
+
let triggered = false;
|
|
774
|
+
if (cliff.metric === "input_tokens") triggered = opts.estimatedInputTokens >= cliff.threshold;
|
|
775
|
+
if (cliff.metric === "tool_count") triggered = (ir.tools?.length ?? 0) >= cliff.threshold;
|
|
776
|
+
if (triggered) qualityPenalty += 0.3;
|
|
777
|
+
}
|
|
778
|
+
const estimatedCostUsd = opts.estimatedInputTokens / 1e6 * profile.costInputPer1m;
|
|
779
|
+
if (policy.maxCostPerCallUsd !== void 0 && estimatedCostUsd > policy.maxCostPerCallUsd) {
|
|
780
|
+
reasons.push(
|
|
781
|
+
`exceeds_max_cost_per_call (estimated $${estimatedCostUsd.toFixed(4)} > policy ceiling $${policy.maxCostPerCallUsd.toFixed(4)})`
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
const baseQuality = profile.strengths.includes("reasoning") ? 0.85 : profile.strengths.includes("quality") ? 0.8 : 0.6;
|
|
785
|
+
const qualityScore = Math.max(0, baseQuality - qualityPenalty);
|
|
786
|
+
const callerOrderBoost = (modelIds.length - modelIds.indexOf(modelId)) * 0.1;
|
|
787
|
+
const costPenalty = estimatedCostUsd * 5;
|
|
788
|
+
const preferredBoost = policySetHas(preferredSet, modelId) ? 0.5 : 0;
|
|
789
|
+
let latencyPenalty = 0;
|
|
790
|
+
const maxLatencyMs = constraints.maxLatencyMs;
|
|
791
|
+
if (typeof maxLatencyMs === "number" && maxLatencyMs > 0) {
|
|
792
|
+
const tierMs = LATENCY_TIER_MS[latencyTierOf(profile)];
|
|
793
|
+
if (tierMs > maxLatencyMs) {
|
|
794
|
+
const overage = (tierMs - maxLatencyMs) / maxLatencyMs;
|
|
795
|
+
latencyPenalty = Math.min(LATENCY_OVERAGE_WEIGHT * overage, LATENCY_PENALTY_CAP);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
let qualityGatePenalty = 0;
|
|
799
|
+
let structuredCliffGate;
|
|
800
|
+
if (constraints.structuredOutput) {
|
|
801
|
+
const schemaWeak = effectiveConventions(profile).some(
|
|
802
|
+
(c) => c.archetype === ir.intent.archetype && c.structuredOutputHint === "avoid"
|
|
803
|
+
);
|
|
804
|
+
if (schemaWeak) qualityGatePenalty = QUALITY_GATE_PENALTY;
|
|
805
|
+
if (!schemaWeak) {
|
|
806
|
+
structuredCliffGate = profile.cliffs.find(
|
|
807
|
+
(c) => c.action === "quality_gate_structured" && (!c.whenIntent || c.whenIntent === ir.intent.archetype) && c.metric === "input_tokens" && opts.estimatedInputTokens >= c.threshold
|
|
808
|
+
);
|
|
809
|
+
if (structuredCliffGate) qualityGatePenalty = QUALITY_GATE_PENALTY;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
const measuredGate = opts.measuredFailureGates?.get(modelId);
|
|
813
|
+
if (measuredGate) qualityGatePenalty = QUALITY_GATE_PENALTY;
|
|
814
|
+
const isPromoted = promotion?.mode === "downswap" && promotion.promotedModel === modelId;
|
|
815
|
+
if (isPromoted && promotion.suppressQualityGate && !measuredGate) {
|
|
816
|
+
qualityGatePenalty = 0;
|
|
817
|
+
}
|
|
818
|
+
const promotionNeutralized = isPromoted && !!measuredGate;
|
|
819
|
+
const promotionBoost = isPromoted && !measuredGate ? PROMOTION_BOOST : 0;
|
|
820
|
+
const rank = qualityScore + callerOrderBoost - costPenalty - reasons.length * 10 + preferredBoost - latencyPenalty - qualityGatePenalty + promotionBoost;
|
|
821
|
+
scores.push({
|
|
822
|
+
modelId,
|
|
823
|
+
estimatedCostUsd,
|
|
824
|
+
fits: reasons.length === 0,
|
|
825
|
+
rejectReasons: reasons,
|
|
826
|
+
qualityScore,
|
|
827
|
+
rank
|
|
828
|
+
});
|
|
829
|
+
if (blockedSet.has(modelId)) {
|
|
830
|
+
policyMutations.push({
|
|
831
|
+
id: `policy-blocked-${modelId}`,
|
|
832
|
+
source: "compile_policy",
|
|
833
|
+
passName: "score_targets",
|
|
834
|
+
description: `Model ${modelId} excluded by CompilePolicy.blockedModels`
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
if (policy.maxCostPerCallUsd !== void 0 && estimatedCostUsd > policy.maxCostPerCallUsd && !blockedSet.has(modelId)) {
|
|
838
|
+
policyMutations.push({
|
|
839
|
+
id: `policy-over-cost-${modelId}`,
|
|
840
|
+
source: "compile_policy",
|
|
841
|
+
passName: "score_targets",
|
|
842
|
+
description: `Model ${modelId} excluded \u2014 estimated cost $${estimatedCostUsd.toFixed(4)} exceeds policy ceiling $${policy.maxCostPerCallUsd.toFixed(4)}`
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
if (policySetHas(preferredSet, modelId) && reasons.length === 0) {
|
|
846
|
+
policyMutations.push({
|
|
847
|
+
id: `policy-preferred-${modelId}`,
|
|
848
|
+
source: "compile_policy",
|
|
849
|
+
passName: "score_targets",
|
|
850
|
+
description: `Model ${modelId} rank boosted by CompilePolicy.preferredModels`
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
if (latencyPenalty > 0) {
|
|
854
|
+
const tier = latencyTierOf(profile);
|
|
855
|
+
policyMutations.push({
|
|
856
|
+
id: `latency-downrank-${modelId}`,
|
|
857
|
+
source: "latency_guard",
|
|
858
|
+
passName: "score_targets",
|
|
859
|
+
description: `Model ${modelId} rank down ${latencyPenalty.toFixed(2)} \u2014 latency tier '${tier}' (~${LATENCY_TIER_MS[tier]}ms) exceeds constraints.maxLatencyMs (${maxLatencyMs}ms)`
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
if (qualityGatePenalty > 0) {
|
|
863
|
+
const rankBefore = rank + qualityGatePenalty;
|
|
864
|
+
if (measuredGate) {
|
|
865
|
+
const pct = (x) => `${(x * 100).toFixed(0)}%`;
|
|
866
|
+
policyMutations.push({
|
|
867
|
+
id: `quality-gate-measured-${modelId}`,
|
|
868
|
+
source: "quality_gate",
|
|
869
|
+
passName: "score_targets",
|
|
870
|
+
rankDelta: -qualityGatePenalty,
|
|
871
|
+
rankBefore,
|
|
872
|
+
rankAfter: rank,
|
|
873
|
+
description: `Model ${modelId} gated below the quality floor for archetype '${ir.intent.archetype}' by MEASURED evidence from this app's own outcomes \u2014 ${measuredGate.nFail} of ${measuredGate.n} attempts failed on the quality axis in the trailing window (${pct(measuredGate.rate)}; 95% lower bound ${pct(measuredGate.lowerBound)} > 50%). Rank ${rankBefore.toFixed(2)} \u2192 ${rank.toFixed(2)} (\u2212${qualityGatePenalty.toFixed(2)}). Down-ranked out of leadership; retained as graceful fallback only. The gate is derived, not stored \u2014 it lifts on its own once the failures age out of the window.`
|
|
874
|
+
});
|
|
875
|
+
} else if (structuredCliffGate) {
|
|
876
|
+
policyMutations.push({
|
|
877
|
+
id: `quality-gate-structured-cliff-${modelId}`,
|
|
878
|
+
source: "quality_gate",
|
|
879
|
+
passName: "score_targets",
|
|
880
|
+
rankDelta: -qualityGatePenalty,
|
|
881
|
+
rankBefore,
|
|
882
|
+
rankAfter: rank,
|
|
883
|
+
description: `Model ${modelId} gated below the quality floor for archetype '${ir.intent.archetype}' \u2014 declared structuredOutput + input_tokens \u2265 ${structuredCliffGate.threshold} trips a measured cliff: ${structuredCliffGate.reason} Rank ${rankBefore.toFixed(2)} \u2192 ${rank.toFixed(2)} (\u2212${qualityGatePenalty.toFixed(2)}). Down-ranked out of leadership; retained as graceful fallback only. Bundled knowledge \u2014 active on cold isolates with no brain.`
|
|
884
|
+
});
|
|
885
|
+
} else {
|
|
886
|
+
policyMutations.push({
|
|
887
|
+
id: `quality-gate-structured-${modelId}`,
|
|
888
|
+
source: "quality_gate",
|
|
889
|
+
passName: "score_targets",
|
|
890
|
+
rankDelta: -qualityGatePenalty,
|
|
891
|
+
rankBefore,
|
|
892
|
+
rankAfter: rank,
|
|
893
|
+
description: `Model ${modelId} gated below the quality floor for archetype '${ir.intent.archetype}' \u2014 declared structuredOutput + kgauto convention flags it schema-weak for this contract (structuredOutputHint:'avoid'). Rank ${rankBefore.toFixed(2)} \u2192 ${rank.toFixed(2)} (\u2212${qualityGatePenalty.toFixed(2)}). Down-ranked out of leadership; retained as graceful fallback only.`
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (promotionNeutralized && reasons.length === 0) {
|
|
898
|
+
policyMutations.push({
|
|
899
|
+
id: `promotion-neutralized-${modelId}`,
|
|
900
|
+
source: "surface_promotion",
|
|
901
|
+
passName: "score_targets",
|
|
902
|
+
description: `Active promotion #${promotion.id} for ${modelId} NOT applied \u2014 the surface's own measured evidence contradicts it (${measuredGate.nFail}/${measuredGate.n} attempts failed in the trailing window). A promotion rests on an eval at promote time; this rests on what the surface is doing now, and the fresher evidence wins. The 7-day rollback guard is expected to retire this promotion on its next run.`
|
|
903
|
+
});
|
|
904
|
+
} else if (isPromoted && reasons.length === 0) {
|
|
905
|
+
policyMutations.push({
|
|
906
|
+
id: `promotion-applied-${modelId}`,
|
|
907
|
+
source: "surface_promotion",
|
|
908
|
+
passName: "score_targets",
|
|
909
|
+
description: `Model ${modelId} boosted to surface leadership by active promotion #${promotion.id} (golden-eval run ${promotion.evalRunId ?? "n/a"}, Stage 2 auto-promote, 7-day rollback guard)` + (promotion.suppressQualityGate ? " \u2014 schema-weak quality gate suppressed for this tuple (eval measured the floor holding on real workload)" : "")
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
if (promotionDeferred && rawPromotion && modelIds.includes(rawPromotion.promotedModel)) {
|
|
914
|
+
policyMutations.push({
|
|
915
|
+
id: `promotion-deferred-consumer-preference-${rawPromotion.promotedModel}`,
|
|
916
|
+
source: "surface_promotion",
|
|
917
|
+
passName: "score_targets",
|
|
918
|
+
description: `Active promotion #${rawPromotion.id} for ${rawPromotion.promotedModel} deferred \u2014 CompilePolicy.preferredModels is declared and explicit consumer preference outranks brain evidence.`
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
return { value: scores, mutations: policyMutations };
|
|
922
|
+
}
|
|
923
|
+
function computeShape(ir, estimatedInputTokens) {
|
|
924
|
+
return {
|
|
925
|
+
contextBucket: bucketContext(estimatedInputTokens),
|
|
926
|
+
toolCountBucket: bucketToolCount(ir.tools?.length ?? 0),
|
|
927
|
+
historyDepth: bucketHistory(ir.history?.length ?? 0),
|
|
928
|
+
outputMode: resolveOutputMode({
|
|
929
|
+
declared: ir.constraints?.outputMode,
|
|
930
|
+
structuredOutput: ir.constraints?.structuredOutput,
|
|
931
|
+
toolCount: ir.tools?.length ?? 0
|
|
932
|
+
}),
|
|
933
|
+
hasExamples: ir.sections.some((s) => /\bexample\b/i.test(s.id))
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
function estimateInputTokens(ir) {
|
|
937
|
+
const sectionTokens = ir.sections.reduce((sum, s) => sum + countTokens(s.text), 0);
|
|
938
|
+
const toolTokens = (ir.tools ?? []).reduce((sum, t) => sum + countToolTokens(t), 0);
|
|
939
|
+
const historyTokens = countMessagesTokens(ir.history ?? []);
|
|
940
|
+
const turnTokens = ir.currentTurn ? countTokens(ir.currentTurn.content) + 4 : 0;
|
|
941
|
+
return sectionTokens + toolTokens + historyTokens + turnTokens + 6;
|
|
942
|
+
}
|
|
943
|
+
function simpleHash(s) {
|
|
944
|
+
let h = 5381;
|
|
945
|
+
for (let i = 0; i < s.length; i++) {
|
|
946
|
+
h = (h << 5) + h + s.charCodeAt(i) | 0;
|
|
947
|
+
}
|
|
948
|
+
return (h >>> 0).toString(36);
|
|
949
|
+
}
|
|
950
|
+
function resolveConventionsForProfile(profile) {
|
|
951
|
+
const own = profile.archetypeConventions ?? [];
|
|
952
|
+
const family = profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
953
|
+
if (!family) return own;
|
|
954
|
+
return own;
|
|
955
|
+
}
|
|
956
|
+
function applyArchetypeConvention(promptText, archetype, family) {
|
|
957
|
+
if (typeof promptText !== "string" || promptText.length === 0) return promptText;
|
|
958
|
+
const repId = familyRepId(family);
|
|
959
|
+
if (!repId) return promptText;
|
|
960
|
+
const profile = tryGetProfile(repId);
|
|
961
|
+
if (!profile || !profile.archetypeConventions) return promptText;
|
|
962
|
+
const conventions = profile.archetypeConventions.filter((c) => c.archetype === archetype);
|
|
963
|
+
if (conventions.length === 0) return promptText;
|
|
964
|
+
let next = promptText;
|
|
965
|
+
for (const c of conventions) {
|
|
966
|
+
if (c.promptPrefix) {
|
|
967
|
+
if (!next.startsWith(c.promptPrefix)) {
|
|
968
|
+
next = c.promptPrefix + next;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
if (c.promptSuffix) {
|
|
972
|
+
if (!next.endsWith(c.promptSuffix)) {
|
|
973
|
+
next = next + c.promptSuffix;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return next;
|
|
978
|
+
}
|
|
979
|
+
function familyRepId(family) {
|
|
980
|
+
switch (family) {
|
|
981
|
+
case "deepseek-reasoner":
|
|
982
|
+
return "deepseek-v4-pro";
|
|
983
|
+
case "deepseek-chat":
|
|
984
|
+
return "deepseek-v4-flash";
|
|
985
|
+
default:
|
|
986
|
+
return void 0;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
function passApplyConventions(ir, profile) {
|
|
990
|
+
const mutations = [];
|
|
991
|
+
const cliffWarnings = [];
|
|
992
|
+
const structuredOutputHints = [];
|
|
993
|
+
const own = profile.archetypeConventions ?? [];
|
|
994
|
+
let effective = own;
|
|
995
|
+
if (effective.length === 0) {
|
|
996
|
+
const family2 = profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
997
|
+
if (family2) {
|
|
998
|
+
const repId = familyRepId(family2);
|
|
999
|
+
if (repId && repId !== profile.id) {
|
|
1000
|
+
const repProfile = tryGetProfile(repId);
|
|
1001
|
+
if (repProfile && repProfile.archetypeConventions) {
|
|
1002
|
+
effective = repProfile.archetypeConventions;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (effective.length === 0) {
|
|
1008
|
+
return {
|
|
1009
|
+
value: { ir, cliffWarnings, structuredOutputHints },
|
|
1010
|
+
mutations
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
const archetype = ir.intent.archetype;
|
|
1014
|
+
const matching = effective.filter((c) => c.archetype === archetype);
|
|
1015
|
+
if (matching.length === 0) {
|
|
1016
|
+
return {
|
|
1017
|
+
value: { ir, cliffWarnings, structuredOutputHints },
|
|
1018
|
+
mutations
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
const family = profile.family ?? deriveFamilyFromModelId(profile.id) ?? "unknown";
|
|
1022
|
+
let nextIR = ir;
|
|
1023
|
+
for (const convention of matching) {
|
|
1024
|
+
let touched = false;
|
|
1025
|
+
if (convention.promptPrefix) {
|
|
1026
|
+
const sections = nextIR.sections ?? [];
|
|
1027
|
+
const alreadyPresent = sections.some(
|
|
1028
|
+
(s) => s.text.includes(convention.promptPrefix)
|
|
1029
|
+
);
|
|
1030
|
+
if (!alreadyPresent) {
|
|
1031
|
+
const prefixSection = {
|
|
1032
|
+
id: `convention-prefix-${family}-${archetype}`,
|
|
1033
|
+
text: convention.promptPrefix
|
|
1034
|
+
};
|
|
1035
|
+
nextIR = { ...nextIR, sections: [prefixSection, ...sections] };
|
|
1036
|
+
touched = true;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
if (convention.promptSuffix) {
|
|
1040
|
+
const history = nextIR.history ?? [];
|
|
1041
|
+
let lastUserIdx = -1;
|
|
1042
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
1043
|
+
if (history[i]?.role === "user") {
|
|
1044
|
+
lastUserIdx = i;
|
|
1045
|
+
break;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
if (lastUserIdx >= 0) {
|
|
1049
|
+
const target = history[lastUserIdx];
|
|
1050
|
+
if (!target.content.endsWith(convention.promptSuffix)) {
|
|
1051
|
+
const newHistory = history.slice();
|
|
1052
|
+
newHistory[lastUserIdx] = {
|
|
1053
|
+
...target,
|
|
1054
|
+
content: target.content + convention.promptSuffix
|
|
1055
|
+
};
|
|
1056
|
+
nextIR = { ...nextIR, history: newHistory };
|
|
1057
|
+
touched = true;
|
|
1058
|
+
}
|
|
1059
|
+
} else if (nextIR.currentTurn && nextIR.currentTurn.role === "user") {
|
|
1060
|
+
const target = nextIR.currentTurn;
|
|
1061
|
+
if (!target.content.endsWith(convention.promptSuffix)) {
|
|
1062
|
+
nextIR = {
|
|
1063
|
+
...nextIR,
|
|
1064
|
+
currentTurn: {
|
|
1065
|
+
...target,
|
|
1066
|
+
content: target.content + convention.promptSuffix
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
touched = true;
|
|
1070
|
+
}
|
|
1071
|
+
} else {
|
|
1072
|
+
nextIR = {
|
|
1073
|
+
...nextIR,
|
|
1074
|
+
currentTurn: {
|
|
1075
|
+
role: "user",
|
|
1076
|
+
content: convention.promptSuffix.trim()
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
touched = true;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
if (convention.structuredOutputHint) {
|
|
1083
|
+
const wantsStructured = !!nextIR.constraints?.structuredOutput;
|
|
1084
|
+
const shouldSurface = convention.structuredOutputHint === "avoid" && wantsStructured || convention.structuredOutputHint === "enforce" && !wantsStructured;
|
|
1085
|
+
if (shouldSurface) {
|
|
1086
|
+
structuredOutputHints.push({
|
|
1087
|
+
archetype,
|
|
1088
|
+
hint: convention.structuredOutputHint,
|
|
1089
|
+
reason: convention.reason
|
|
1090
|
+
});
|
|
1091
|
+
if (convention.cliffWarning) {
|
|
1092
|
+
cliffWarnings.push(`${profile.id}: ${convention.cliffWarning}`);
|
|
1093
|
+
}
|
|
1094
|
+
touched = true;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
if (convention.cliffWarning && !convention.structuredOutputHint) {
|
|
1098
|
+
const toolCount = nextIR.tools?.length ?? 0;
|
|
1099
|
+
const thresholdOk = convention.whenToolCountAtLeast === void 0 || toolCount >= convention.whenToolCountAtLeast;
|
|
1100
|
+
if (thresholdOk) {
|
|
1101
|
+
cliffWarnings.push(`${profile.id}: ${convention.cliffWarning}`);
|
|
1102
|
+
touched = true;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
if (touched) {
|
|
1106
|
+
mutations.push({
|
|
1107
|
+
id: `apply-convention-${family}-${archetype}`,
|
|
1108
|
+
source: "archetype_convention",
|
|
1109
|
+
passName: "apply_conventions",
|
|
1110
|
+
description: `${profile.id}: applied ${family}-family convention for archetype=${archetype} \u2014 ${convention.reason}`
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
return {
|
|
1115
|
+
value: { ir: nextIR, cliffWarnings, structuredOutputHints },
|
|
1116
|
+
mutations
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// src/measured-failure-brain.ts
|
|
1121
|
+
function coerceCount(v) {
|
|
1122
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
1123
|
+
if (typeof v === "string") {
|
|
1124
|
+
const n = Number(v);
|
|
1125
|
+
return Number.isFinite(n) ? n : null;
|
|
1126
|
+
}
|
|
1127
|
+
return null;
|
|
1128
|
+
}
|
|
1129
|
+
function isRawFailureRow(x) {
|
|
1130
|
+
if (!x || typeof x !== "object") return false;
|
|
1131
|
+
const r = x;
|
|
1132
|
+
return typeof r.intent_archetype === "string" && typeof r.model === "string" && (typeof r.n === "number" || typeof r.n === "string");
|
|
1133
|
+
}
|
|
1134
|
+
function mapRows(rows) {
|
|
1135
|
+
const out = [];
|
|
1136
|
+
for (const row of rows) {
|
|
1137
|
+
if (!isRawFailureRow(row)) continue;
|
|
1138
|
+
const n = coerceCount(row.n);
|
|
1139
|
+
const nFail = coerceCount(row.n_fail) ?? 0;
|
|
1140
|
+
if (n === null || n <= 0) continue;
|
|
1141
|
+
out.push({
|
|
1142
|
+
archetype: row.intent_archetype,
|
|
1143
|
+
model: row.model,
|
|
1144
|
+
n,
|
|
1145
|
+
nFail
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
return out;
|
|
1149
|
+
}
|
|
1150
|
+
var MEASURED_FAILURE_CFG = {
|
|
1151
|
+
/**
|
|
1152
|
+
* Hard minimum attempts before ANY gate may be created. Guards against
|
|
1153
|
+
* pathological tiny samples that the confidence bound alone would let
|
|
1154
|
+
* through in edge cases. At 5-for-5 the bound clears the threshold; at
|
|
1155
|
+
* 3-for-3 it does not, which is the behaviour we want (three failures is
|
|
1156
|
+
* a bad day, five in a row is a pattern).
|
|
1157
|
+
*/
|
|
1158
|
+
minSample: 5,
|
|
1159
|
+
/**
|
|
1160
|
+
* Gate when we are 95% confident the model fails MORE OFTEN THAN IT
|
|
1161
|
+
* SUCCEEDS on this surface. Deliberately unarguable rather than tuned —
|
|
1162
|
+
* a model that probably fails the majority of the time has no business
|
|
1163
|
+
* leading a surface, whatever its declared scores say.
|
|
1164
|
+
*/
|
|
1165
|
+
lowerBoundThreshold: 0.5,
|
|
1166
|
+
/** 95% one-sided-ish confidence (standard two-sided z at α=0.05). */
|
|
1167
|
+
z: 1.96,
|
|
1168
|
+
/** Must match the view's window. Documented here for the advisory text. */
|
|
1169
|
+
windowDays: 28
|
|
1170
|
+
};
|
|
1171
|
+
function wilsonLowerBound(failures, n, z = MEASURED_FAILURE_CFG.z) {
|
|
1172
|
+
if (n <= 0) return 0;
|
|
1173
|
+
const p = failures / n;
|
|
1174
|
+
const z2 = z * z;
|
|
1175
|
+
const denom = 1 + z2 / n;
|
|
1176
|
+
const centre = p + z2 / (2 * n);
|
|
1177
|
+
const margin = z * Math.sqrt(p * (1 - p) / n + z2 / (4 * n * n));
|
|
1178
|
+
const lower2 = (centre - margin) / denom;
|
|
1179
|
+
return lower2 < 0 ? 0 : lower2;
|
|
1180
|
+
}
|
|
1181
|
+
function mapMeasuredFailureRows(rows) {
|
|
1182
|
+
return mapRows(rows);
|
|
1183
|
+
}
|
|
1184
|
+
function judgeMeasuredFailure(row, cfg = MEASURED_FAILURE_CFG) {
|
|
1185
|
+
if (!row) return void 0;
|
|
1186
|
+
const normalized = "nFail" in row && typeof row.n === "number" ? row : mapRows([row])[0];
|
|
1187
|
+
if (!normalized || normalized.n < cfg.minSample) return void 0;
|
|
1188
|
+
const lowerBound = wilsonLowerBound(normalized.nFail, normalized.n, cfg.z);
|
|
1189
|
+
return {
|
|
1190
|
+
gated: lowerBound > cfg.lowerBoundThreshold,
|
|
1191
|
+
rate: normalized.nFail / normalized.n,
|
|
1192
|
+
lowerBound,
|
|
1193
|
+
n: normalized.n,
|
|
1194
|
+
nFail: normalized.nFail
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
var snapshots2 = /* @__PURE__ */ new Map();
|
|
1198
|
+
function measuredFailureFreshness(appId) {
|
|
1199
|
+
return snapshots2.get(appId)?.loaded ? "warm" : "cold";
|
|
1200
|
+
}
|
|
1201
|
+
var runtime2;
|
|
1202
|
+
var warnedOnce2 = false;
|
|
1203
|
+
var DEFAULT_MEASURED_FAILURE_ENDPOINT = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/measured-failure";
|
|
1204
|
+
function isMeasuredFailureGateEnabledFromEnv(envSource) {
|
|
1205
|
+
const env = envSource ?? (typeof process !== "undefined" && process.env ? process.env : {});
|
|
1206
|
+
const raw = (env.KGAUTO_MEASURED_FAILURE_GATE ?? "").trim().toLowerCase();
|
|
1207
|
+
return !(raw === "0" || raw === "false");
|
|
1208
|
+
}
|
|
1209
|
+
function configureMeasuredFailureBrain(rt) {
|
|
1210
|
+
runtime2 = rt;
|
|
1211
|
+
snapshots2.clear();
|
|
1212
|
+
warnedOnce2 = false;
|
|
1213
|
+
}
|
|
1214
|
+
function isMeasuredFailureBrainActive() {
|
|
1215
|
+
return runtime2 !== void 0;
|
|
1216
|
+
}
|
|
1217
|
+
function prefetchMeasuredFailure(appId) {
|
|
1218
|
+
const rt = runtime2;
|
|
1219
|
+
if (!rt || !appId) return void 0;
|
|
1220
|
+
let snap = snapshots2.get(appId);
|
|
1221
|
+
if (!snap) {
|
|
1222
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1223
|
+
snapshots2.set(appId, snap);
|
|
1224
|
+
}
|
|
1225
|
+
if (snap.expiresAt > Date.now()) return void 0;
|
|
1226
|
+
const inflight = pendingRefreshes2.get(appId);
|
|
1227
|
+
if (inflight) return inflight;
|
|
1228
|
+
if (snap.refreshing) return void 0;
|
|
1229
|
+
snap.refreshing = true;
|
|
1230
|
+
void asyncRefresh2(rt, appId);
|
|
1231
|
+
return pendingRefreshes2.get(appId);
|
|
1232
|
+
}
|
|
1233
|
+
async function awaitMeasuredFailureReady(appId, timeoutMs) {
|
|
1234
|
+
if (!runtime2 || !appId) return;
|
|
1235
|
+
const pending = prefetchMeasuredFailure(appId) ?? pendingRefreshes2.get(appId);
|
|
1236
|
+
if (!(timeoutMs > 0)) return;
|
|
1237
|
+
if (!pending) return;
|
|
1238
|
+
let timer;
|
|
1239
|
+
try {
|
|
1240
|
+
await Promise.race([
|
|
1241
|
+
pending,
|
|
1242
|
+
new Promise((resolve) => {
|
|
1243
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
1244
|
+
})
|
|
1245
|
+
]);
|
|
1246
|
+
} catch {
|
|
1247
|
+
} finally {
|
|
1248
|
+
if (timer) clearTimeout(timer);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
function getMeasuredFailureVerdict(opts) {
|
|
1252
|
+
const rt = runtime2;
|
|
1253
|
+
if (!rt) return void 0;
|
|
1254
|
+
const { appId, archetype, model } = opts;
|
|
1255
|
+
if (!appId || !archetype || !model) return void 0;
|
|
1256
|
+
let snap = snapshots2.get(appId);
|
|
1257
|
+
if (!snap) {
|
|
1258
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1259
|
+
snapshots2.set(appId, snap);
|
|
1260
|
+
}
|
|
1261
|
+
const now = Date.now();
|
|
1262
|
+
if (snap.expiresAt <= now && !snap.refreshing) {
|
|
1263
|
+
snap.refreshing = true;
|
|
1264
|
+
void asyncRefresh2(rt, appId);
|
|
1265
|
+
}
|
|
1266
|
+
const row = snap.data.find(
|
|
1267
|
+
(r) => r.archetype === archetype && r.model === model
|
|
1268
|
+
);
|
|
1269
|
+
return judgeMeasuredFailure(row);
|
|
1270
|
+
}
|
|
1271
|
+
var pendingRefreshes2 = /* @__PURE__ */ new Map();
|
|
1272
|
+
async function asyncRefresh2(rt, appId) {
|
|
1273
|
+
const promise = doRefresh2(rt, appId);
|
|
1274
|
+
pendingRefreshes2.set(appId, promise);
|
|
1275
|
+
try {
|
|
1276
|
+
await promise;
|
|
1277
|
+
} finally {
|
|
1278
|
+
if (pendingRefreshes2.get(appId) === promise) {
|
|
1279
|
+
pendingRefreshes2.delete(appId);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
async function doRefresh2(rt, appId) {
|
|
1284
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
1285
|
+
let snap = snapshots2.get(appId);
|
|
1286
|
+
if (!snap) {
|
|
1287
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1288
|
+
snapshots2.set(appId, snap);
|
|
1289
|
+
}
|
|
1290
|
+
try {
|
|
1291
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
1292
|
+
if (!res.ok) {
|
|
1293
|
+
throw new Error(`measured-failure ${res.status}: ${res.statusText}`);
|
|
1294
|
+
}
|
|
1295
|
+
const body = await res.json();
|
|
1296
|
+
if (runtime2 !== rt) return;
|
|
1297
|
+
snap.data = Array.isArray(body) ? mapRows(body) : [];
|
|
1298
|
+
snap.loaded = true;
|
|
1299
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1300
|
+
snap.refreshing = false;
|
|
1301
|
+
} catch (err) {
|
|
1302
|
+
if (runtime2 !== rt) return;
|
|
1303
|
+
snap.refreshing = false;
|
|
1304
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1305
|
+
if (!warnedOnce2) {
|
|
1306
|
+
warnedOnce2 = true;
|
|
1307
|
+
(rt.onError ?? defaultOnError2)(err);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function defaultOnError2(err) {
|
|
1312
|
+
console.warn(
|
|
1313
|
+
"[kgauto] measured-failure fetch failed (gate inactive until next refresh):",
|
|
1314
|
+
err
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
function _testResetMeasuredFailure() {
|
|
1318
|
+
runtime2 = void 0;
|
|
1319
|
+
snapshots2.clear();
|
|
1320
|
+
pendingRefreshes2 = /* @__PURE__ */ new Map();
|
|
1321
|
+
warnedOnce2 = false;
|
|
1322
|
+
}
|
|
1323
|
+
async function _testWaitForMeasuredFailureRefresh() {
|
|
1324
|
+
const pending = Array.from(pendingRefreshes2.values());
|
|
1325
|
+
if (pending.length > 0) await Promise.all(pending);
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// src/promotions-brain.ts
|
|
1329
|
+
function isRawPromotionRow(x) {
|
|
1330
|
+
if (!x || typeof x !== "object") return false;
|
|
1331
|
+
const r = x;
|
|
1332
|
+
return (typeof r.id === "number" || typeof r.id === "string") && typeof r.intent_archetype === "string" && typeof r.promoted_model === "string" && typeof r.incumbent_model === "string";
|
|
1333
|
+
}
|
|
1334
|
+
function coerceId(v) {
|
|
1335
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
1336
|
+
if (typeof v === "string") {
|
|
1337
|
+
const n = Number(v);
|
|
1338
|
+
return Number.isFinite(n) ? n : null;
|
|
1339
|
+
}
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
function mapRowsToPromotions(rows) {
|
|
1343
|
+
const out = [];
|
|
1344
|
+
for (const row of rows) {
|
|
1345
|
+
if (!isRawPromotionRow(row)) continue;
|
|
1346
|
+
const id = coerceId(row.id);
|
|
1347
|
+
if (id === null) continue;
|
|
1348
|
+
const mode = row.mode === "strategy" ? "strategy" : row.mode === "downswap" || row.mode === void 0 ? "downswap" : null;
|
|
1349
|
+
if (mode === null) continue;
|
|
1350
|
+
out.push({
|
|
1351
|
+
id,
|
|
1352
|
+
archetype: row.intent_archetype,
|
|
1353
|
+
mode,
|
|
1354
|
+
strategy: typeof row.strategy === "string" ? row.strategy : null,
|
|
1355
|
+
promotedModel: row.promoted_model,
|
|
1356
|
+
incumbentModel: row.incumbent_model,
|
|
1357
|
+
evalRunId: coerceId(row.eval_run_id ?? null),
|
|
1358
|
+
suppressQualityGate: row.suppress_quality_gate === true,
|
|
1359
|
+
promotedAt: typeof row.promoted_at === "string" ? row.promoted_at : "",
|
|
1360
|
+
// Pre-.78 endpoints serve no status column and only active rows —
|
|
1361
|
+
// defaulting to 'active' is exact, not optimistic.
|
|
1362
|
+
status: row.status === "rolled_back" ? "rolled_back" : "active",
|
|
1363
|
+
...typeof row.rolled_back_at === "string" ? { rolledBackAt: row.rolled_back_at } : {},
|
|
1364
|
+
...typeof row.rollback_class === "string" ? { rollbackClass: row.rollback_class } : {}
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
return out;
|
|
1368
|
+
}
|
|
1369
|
+
var snapshots3 = /* @__PURE__ */ new Map();
|
|
1370
|
+
function promotionsFreshness(appId) {
|
|
1371
|
+
return snapshots3.get(appId)?.loaded ? "warm" : "cold";
|
|
1372
|
+
}
|
|
1373
|
+
var runtime3;
|
|
1374
|
+
var warnedOnce3 = false;
|
|
1375
|
+
var DEFAULT_PROMOTIONS_ENDPOINT = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/promotions";
|
|
1376
|
+
function isAutoPromoteEnabledFromEnv(envSource) {
|
|
1377
|
+
const env = envSource ?? (typeof process !== "undefined" && process.env ? process.env : {});
|
|
1378
|
+
const raw = (env.KGAUTO_AUTO_PROMOTE ?? "").trim().toLowerCase();
|
|
1379
|
+
return raw === "1" || raw === "true";
|
|
1380
|
+
}
|
|
1381
|
+
function configurePromotionsBrain(rt) {
|
|
1382
|
+
runtime3 = rt;
|
|
1383
|
+
snapshots3.clear();
|
|
1384
|
+
warnedOnce3 = false;
|
|
1385
|
+
}
|
|
1386
|
+
function isPromotionsBrainActive() {
|
|
1387
|
+
return runtime3 !== void 0;
|
|
1388
|
+
}
|
|
1389
|
+
function getApplicablePromotion(opts) {
|
|
1390
|
+
const rt = runtime3;
|
|
1391
|
+
if (!rt) return void 0;
|
|
1392
|
+
const appId = opts.appId;
|
|
1393
|
+
if (!appId || !opts.archetype || !opts.mode) return void 0;
|
|
1394
|
+
let snap = snapshots3.get(appId);
|
|
1395
|
+
if (!snap) {
|
|
1396
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1397
|
+
snapshots3.set(appId, snap);
|
|
1398
|
+
}
|
|
1399
|
+
const now = Date.now();
|
|
1400
|
+
const stale = snap.expiresAt <= now;
|
|
1401
|
+
if (stale && !snap.refreshing) {
|
|
1402
|
+
snap.refreshing = true;
|
|
1403
|
+
void asyncRefresh3(rt, appId);
|
|
1404
|
+
}
|
|
1405
|
+
return snap.data.find(
|
|
1406
|
+
(p) => p.status === "active" && p.archetype === opts.archetype && p.mode === opts.mode
|
|
1407
|
+
);
|
|
1408
|
+
}
|
|
1409
|
+
var ROLLBACK_SUPPRESSION_WINDOW_DAYS = 28;
|
|
1410
|
+
function getRecentRollback(opts) {
|
|
1411
|
+
const rt = runtime3;
|
|
1412
|
+
if (!rt) return void 0;
|
|
1413
|
+
if (!opts.appId || !opts.archetype || !opts.model) return void 0;
|
|
1414
|
+
let snap = snapshots3.get(opts.appId);
|
|
1415
|
+
if (!snap) {
|
|
1416
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1417
|
+
snapshots3.set(opts.appId, snap);
|
|
1418
|
+
}
|
|
1419
|
+
const now = opts.nowMs ?? Date.now();
|
|
1420
|
+
if (snap.expiresAt <= now && !snap.refreshing) {
|
|
1421
|
+
snap.refreshing = true;
|
|
1422
|
+
void asyncRefresh3(rt, opts.appId);
|
|
1423
|
+
}
|
|
1424
|
+
const windowMs = (opts.windowDays ?? ROLLBACK_SUPPRESSION_WINDOW_DAYS) * 864e5;
|
|
1425
|
+
return snap.data.find(
|
|
1426
|
+
(p) => p.status === "rolled_back" && p.archetype === opts.archetype && p.promotedModel === opts.model && typeof p.rolledBackAt === "string" && now - Date.parse(p.rolledBackAt) <= windowMs
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
var pendingRefreshes3 = /* @__PURE__ */ new Map();
|
|
1430
|
+
async function asyncRefresh3(rt, appId) {
|
|
1431
|
+
const promise = doRefresh3(rt, appId);
|
|
1432
|
+
pendingRefreshes3.set(appId, promise);
|
|
1433
|
+
try {
|
|
1434
|
+
await promise;
|
|
1435
|
+
} finally {
|
|
1436
|
+
if (pendingRefreshes3.get(appId) === promise) {
|
|
1437
|
+
pendingRefreshes3.delete(appId);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
async function doRefresh3(rt, appId) {
|
|
1442
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}&with_rollbacks=1`;
|
|
1443
|
+
let snap = snapshots3.get(appId);
|
|
1444
|
+
if (!snap) {
|
|
1445
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1446
|
+
snapshots3.set(appId, snap);
|
|
1447
|
+
}
|
|
1448
|
+
try {
|
|
1449
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
1450
|
+
if (!res.ok) {
|
|
1451
|
+
throw new Error(`promotions ${res.status}: ${res.statusText}`);
|
|
1452
|
+
}
|
|
1453
|
+
const body = await res.json();
|
|
1454
|
+
if (runtime3 !== rt) return;
|
|
1455
|
+
const rows = Array.isArray(body) ? mapRowsToPromotions(body) : [];
|
|
1456
|
+
snap.data = rows;
|
|
1457
|
+
snap.loaded = true;
|
|
1458
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1459
|
+
snap.refreshing = false;
|
|
1460
|
+
} catch (err) {
|
|
1461
|
+
if (runtime3 !== rt) return;
|
|
1462
|
+
snap.refreshing = false;
|
|
1463
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1464
|
+
if (!warnedOnce3) {
|
|
1465
|
+
warnedOnce3 = true;
|
|
1466
|
+
(rt.onError ?? defaultOnError3)(err);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
function defaultOnError3(err) {
|
|
1471
|
+
console.warn(
|
|
1472
|
+
"[kgauto] promotions fetch failed (promotion boost inactive until next refresh):",
|
|
1473
|
+
err
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1476
|
+
function prefetchPromotions(appId) {
|
|
1477
|
+
const rt = runtime3;
|
|
1478
|
+
if (!rt || !appId) return void 0;
|
|
1479
|
+
let snap = snapshots3.get(appId);
|
|
1480
|
+
if (!snap) {
|
|
1481
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1482
|
+
snapshots3.set(appId, snap);
|
|
1483
|
+
}
|
|
1484
|
+
if (snap.expiresAt > Date.now()) return void 0;
|
|
1485
|
+
const inflight = pendingRefreshes3.get(appId);
|
|
1486
|
+
if (inflight) return inflight;
|
|
1487
|
+
if (snap.refreshing) return void 0;
|
|
1488
|
+
snap.refreshing = true;
|
|
1489
|
+
void asyncRefresh3(rt, appId);
|
|
1490
|
+
return pendingRefreshes3.get(appId);
|
|
1491
|
+
}
|
|
1492
|
+
async function awaitPromotionsReady(appId, timeoutMs) {
|
|
1493
|
+
if (!runtime3 || !appId) return;
|
|
1494
|
+
const pending = prefetchPromotions(appId) ?? pendingRefreshes3.get(appId);
|
|
1495
|
+
if (!(timeoutMs > 0)) return;
|
|
1496
|
+
if (!pending) return;
|
|
1497
|
+
let timer;
|
|
1498
|
+
try {
|
|
1499
|
+
await Promise.race([
|
|
1500
|
+
pending,
|
|
1501
|
+
new Promise((resolve) => {
|
|
1502
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
1503
|
+
})
|
|
1504
|
+
]);
|
|
1505
|
+
} catch {
|
|
1506
|
+
} finally {
|
|
1507
|
+
if (timer) clearTimeout(timer);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
function _testResetPromotions() {
|
|
1511
|
+
runtime3 = void 0;
|
|
1512
|
+
snapshots3.clear();
|
|
1513
|
+
pendingRefreshes3 = /* @__PURE__ */ new Map();
|
|
1514
|
+
warnedOnce3 = false;
|
|
1515
|
+
}
|
|
1516
|
+
async function _testWaitForPromotionsRefresh() {
|
|
1517
|
+
const pending = Array.from(pendingRefreshes3.values());
|
|
1518
|
+
if (pending.length > 0) await Promise.all(pending);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// src/promote-ready-brain.ts
|
|
1522
|
+
function isRawPromoteReadyRow(x) {
|
|
1523
|
+
if (!x || typeof x !== "object") return false;
|
|
1524
|
+
const r = x;
|
|
1525
|
+
return typeof r.intent_archetype === "string" && typeof r.family === "string" && typeof r.candidate_model === "string" && typeof r.current_model === "string" && typeof r.detected_at === "string";
|
|
1526
|
+
}
|
|
1527
|
+
function coerceNumber(v) {
|
|
1528
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
1529
|
+
if (typeof v === "string") {
|
|
1530
|
+
const n = Number(v);
|
|
1531
|
+
return Number.isFinite(n) ? n : null;
|
|
1532
|
+
}
|
|
1533
|
+
return null;
|
|
1534
|
+
}
|
|
1535
|
+
function mapRowsToFindings2(rows) {
|
|
1536
|
+
const out = [];
|
|
1537
|
+
for (const row of rows) {
|
|
1538
|
+
if (!isRawPromoteReadyRow(row)) continue;
|
|
1539
|
+
const sampleN = coerceNumber(row.sample_n);
|
|
1540
|
+
const passRate = coerceNumber(row.judge_pass_rate);
|
|
1541
|
+
const avgScore = coerceNumber(row.judge_avg_score);
|
|
1542
|
+
if (sampleN === null || passRate === null || avgScore === null) continue;
|
|
1543
|
+
out.push({
|
|
1544
|
+
archetype: row.intent_archetype,
|
|
1545
|
+
family: row.family,
|
|
1546
|
+
candidateModel: row.candidate_model,
|
|
1547
|
+
currentModel: row.current_model,
|
|
1548
|
+
sampleN,
|
|
1549
|
+
judgePassRate: passRate,
|
|
1550
|
+
judgeAvgScore: avgScore,
|
|
1551
|
+
costDeltaPct: coerceNumber(row.cost_delta_pct),
|
|
1552
|
+
detectedAt: row.detected_at
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
return out;
|
|
1556
|
+
}
|
|
1557
|
+
var snapshots4 = /* @__PURE__ */ new Map();
|
|
1558
|
+
function promoteReadyFreshness(appId) {
|
|
1559
|
+
return snapshots4.get(appId)?.loaded ? "warm" : "cold";
|
|
1560
|
+
}
|
|
1561
|
+
var runtime4;
|
|
1562
|
+
var warnedOnce4 = false;
|
|
1563
|
+
function isPromoteReadyBrainActive() {
|
|
1564
|
+
return runtime4 !== void 0;
|
|
1565
|
+
}
|
|
1566
|
+
function loadPromoteReadyFindings(opts) {
|
|
1567
|
+
const rt = runtime4;
|
|
1568
|
+
if (!rt) return [];
|
|
1569
|
+
const appId = opts.appId;
|
|
1570
|
+
if (!appId) return [];
|
|
1571
|
+
let snap = snapshots4.get(appId);
|
|
1572
|
+
if (!snap) {
|
|
1573
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1574
|
+
snapshots4.set(appId, snap);
|
|
1575
|
+
}
|
|
1576
|
+
const now = Date.now();
|
|
1577
|
+
const stale = snap.expiresAt <= now;
|
|
1578
|
+
if (stale && !snap.refreshing) {
|
|
1579
|
+
snap.refreshing = true;
|
|
1580
|
+
void asyncRefresh4(rt, appId);
|
|
1581
|
+
}
|
|
1582
|
+
let rows = snap.data;
|
|
1583
|
+
if (opts.archetype) {
|
|
1584
|
+
rows = rows.filter((f) => f.archetype === opts.archetype);
|
|
1585
|
+
}
|
|
1586
|
+
if (opts.family) {
|
|
1587
|
+
rows = rows.filter((f) => f.family === opts.family);
|
|
1588
|
+
}
|
|
1589
|
+
return rows;
|
|
1590
|
+
}
|
|
1591
|
+
var pendingRefreshes4 = /* @__PURE__ */ new Map();
|
|
1592
|
+
async function asyncRefresh4(rt, appId) {
|
|
1593
|
+
const promise = doRefresh4(rt, appId);
|
|
1594
|
+
pendingRefreshes4.set(appId, promise);
|
|
1595
|
+
try {
|
|
1596
|
+
await promise;
|
|
1597
|
+
} finally {
|
|
1598
|
+
if (pendingRefreshes4.get(appId) === promise) {
|
|
1599
|
+
pendingRefreshes4.delete(appId);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
async function doRefresh4(rt, appId) {
|
|
1604
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
1605
|
+
let snap = snapshots4.get(appId);
|
|
1606
|
+
if (!snap) {
|
|
1607
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1608
|
+
snapshots4.set(appId, snap);
|
|
1609
|
+
}
|
|
1610
|
+
try {
|
|
1611
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
1612
|
+
if (!res.ok) {
|
|
1613
|
+
throw new Error(`promote-ready ${res.status}: ${res.statusText}`);
|
|
1614
|
+
}
|
|
1615
|
+
const body = await res.json();
|
|
1616
|
+
if (runtime4 !== rt) return;
|
|
1617
|
+
const rows = Array.isArray(body) ? mapRowsToFindings2(body) : [];
|
|
1618
|
+
snap.data = rows;
|
|
1619
|
+
snap.loaded = true;
|
|
1620
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1621
|
+
snap.refreshing = false;
|
|
1622
|
+
} catch (err) {
|
|
1623
|
+
if (runtime4 !== rt) return;
|
|
1624
|
+
snap.refreshing = false;
|
|
1625
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1626
|
+
if (!warnedOnce4) {
|
|
1627
|
+
warnedOnce4 = true;
|
|
1628
|
+
(rt.onError ?? defaultOnError4)(err);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
function defaultOnError4(err) {
|
|
1633
|
+
console.warn(
|
|
1634
|
+
"[kgauto] promote-ready fetch failed (using empty fallback):",
|
|
1635
|
+
err
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
function resolveFetchImpl(injected) {
|
|
1639
|
+
return injected ?? ((...args) => globalThis.fetch(...args));
|
|
1640
|
+
}
|
|
1641
|
+
function normalizeEndpoint(endpoint) {
|
|
1642
|
+
return endpoint.replace(/\/+$/, "");
|
|
1643
|
+
}
|
|
1644
|
+
async function markPromoteReadyHandled(opts) {
|
|
1645
|
+
const {
|
|
1646
|
+
appId,
|
|
1647
|
+
archetype,
|
|
1648
|
+
family,
|
|
1649
|
+
resolution,
|
|
1650
|
+
resolutionNote,
|
|
1651
|
+
brainEndpoint,
|
|
1652
|
+
brainJwt,
|
|
1653
|
+
brainAnonKey,
|
|
1654
|
+
fetch: injectedFetch
|
|
1655
|
+
} = opts;
|
|
1656
|
+
if (!appId) return { ok: false, reason: "app_id_required" };
|
|
1657
|
+
if (!archetype) return { ok: false, reason: "archetype_required" };
|
|
1658
|
+
if (!family) return { ok: false, reason: "family_required" };
|
|
1659
|
+
if (resolution !== "promoted" && resolution !== "declined" && resolution !== "still-evaluating") {
|
|
1660
|
+
return { ok: false, reason: "resolution_invalid" };
|
|
1661
|
+
}
|
|
1662
|
+
const doFetch = resolveFetchImpl(injectedFetch);
|
|
1663
|
+
const base = normalizeEndpoint(brainEndpoint);
|
|
1664
|
+
const url = `${base}/rest/v1/promote_ready_findings?app_id=eq.${encodeURIComponent(appId)}&intent_archetype=eq.${encodeURIComponent(archetype)}&family=eq.${encodeURIComponent(family)}&resolved_at=is.null`;
|
|
1665
|
+
const patchBody = {
|
|
1666
|
+
resolved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1667
|
+
resolution
|
|
1668
|
+
};
|
|
1669
|
+
if (resolutionNote !== void 0) {
|
|
1670
|
+
patchBody.resolution_note = resolutionNote;
|
|
1671
|
+
}
|
|
1672
|
+
let res;
|
|
1673
|
+
try {
|
|
1674
|
+
res = await doFetch(url, {
|
|
1675
|
+
method: "PATCH",
|
|
1676
|
+
headers: {
|
|
1677
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
1678
|
+
apikey: brainAnonKey,
|
|
1679
|
+
"Content-Type": "application/json",
|
|
1680
|
+
Accept: "application/json",
|
|
1681
|
+
Prefer: "return=minimal"
|
|
1682
|
+
},
|
|
1683
|
+
body: JSON.stringify(patchBody)
|
|
1684
|
+
});
|
|
1685
|
+
} catch (err) {
|
|
1686
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1687
|
+
return { ok: false, reason: `network_error:${msg}` };
|
|
1688
|
+
}
|
|
1689
|
+
if (res.status === 401 || res.status === 403) {
|
|
1690
|
+
return { ok: false, reason: "brain_auth_misconfig" };
|
|
1691
|
+
}
|
|
1692
|
+
if (res.status >= 500) {
|
|
1693
|
+
return { ok: false, reason: "brain_unavailable" };
|
|
1694
|
+
}
|
|
1695
|
+
if (!res.ok) {
|
|
1696
|
+
return { ok: false, reason: `patch_failed:${res.status}` };
|
|
1697
|
+
}
|
|
1698
|
+
return { ok: true };
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// src/archetype-fits.ts
|
|
1702
|
+
var ARCHETYPE_FAMILY_FITS = Object.freeze([
|
|
1703
|
+
{
|
|
1704
|
+
archetype: "plan",
|
|
1705
|
+
betterFitFamily: "deepseek-reasoner",
|
|
1706
|
+
reason: "Plan archetype is reasoning-shaped (multi-step chains, hypothesis-and-check, sub-goal decomposition) \u2014 exactly where reasoner-family models excel. Sonnet/Opus produce plans but at higher cost; reasoners produce equivalent-or-better plans at 7-17x lower cost at current promo pricing (deepseek-v4-pro $0.435/$0.87 per 1M promo through 2026-05-31 vs sonnet $3/$15).",
|
|
1707
|
+
costGuidance: "substantially cheaper at current pricing (deepseek-v4-pro promo: ~7-17x cheaper than sonnet)"
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
archetype: "critique",
|
|
1711
|
+
betterFitFamily: "deepseek-reasoner",
|
|
1712
|
+
reason: "Critique archetype rewards epistemic humility and explicit reasoning \u2014 reasoner-family default behavior. Sonnet/Opus over-confident on critique tasks; reasoners surface uncertainty productively.",
|
|
1713
|
+
costGuidance: "comparable or cheaper at current pricing"
|
|
1714
|
+
}
|
|
1715
|
+
]);
|
|
1716
|
+
function findBetterFit(archetype, currentFamily) {
|
|
1717
|
+
for (const fit of ARCHETYPE_FAMILY_FITS) {
|
|
1718
|
+
if (fit.archetype !== archetype) continue;
|
|
1719
|
+
if (fit.betterFitFamily === currentFamily) return null;
|
|
1720
|
+
return fit;
|
|
1721
|
+
}
|
|
1722
|
+
return null;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
// src/advisor-rules/blocked-model-drift.ts
|
|
1726
|
+
var BLOCKED_MODEL_NOT_IN_ROSTER_CODE = "blocked-model-not-in-roster";
|
|
1727
|
+
var BLOCKED_MODEL_FAMILY_SIBLING_SERVED_CODE = "blocked-model-family-sibling-served";
|
|
1728
|
+
var DOCS_URL = "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories";
|
|
1729
|
+
function familyOf2(modelId, profile) {
|
|
1730
|
+
return profile?.family ?? deriveFamilyFromModelId(modelId);
|
|
1731
|
+
}
|
|
1732
|
+
function isOnTheWayOut(status) {
|
|
1733
|
+
return status === "legacy" || status === "deprecated";
|
|
1734
|
+
}
|
|
1735
|
+
function advisorRuleBlockedModelDrift(ctx) {
|
|
1736
|
+
const blocked = ctx.policy?.blockedModels;
|
|
1737
|
+
if (!blocked || blocked.length === 0) return [];
|
|
1738
|
+
const resolve = ctx.resolveProfile ?? tryGetProfile;
|
|
1739
|
+
const out = [];
|
|
1740
|
+
const entries = [...new Set(blocked)].sort();
|
|
1741
|
+
const selectedProfile = resolve(ctx.selectedModelId);
|
|
1742
|
+
const selectedFamily = familyOf2(ctx.selectedModelId, selectedProfile);
|
|
1743
|
+
const orphans = entries.filter((e) => resolve(e) === void 0);
|
|
1744
|
+
if (orphans.length > 0) {
|
|
1745
|
+
const list = orphans.map((o) => `\`${o}\``).join(", ");
|
|
1746
|
+
const plural = orphans.length === 1 ? "entry" : "entries";
|
|
1747
|
+
const verb = orphans.length === 1 ? "matches" : "match";
|
|
1748
|
+
out.push({
|
|
1749
|
+
level: "warn",
|
|
1750
|
+
code: BLOCKED_MODEL_NOT_IN_ROSTER_CODE,
|
|
1751
|
+
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.`,
|
|
1752
|
+
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.`,
|
|
1753
|
+
docsUrl: DOCS_URL
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
if (selectedFamily !== null) {
|
|
1757
|
+
const siblings = entries.filter((e) => {
|
|
1758
|
+
if (resolveModelAlias(e) === resolveModelAlias(ctx.selectedModelId)) return false;
|
|
1759
|
+
const p = resolve(e);
|
|
1760
|
+
if (familyOf2(e, p) !== selectedFamily) return false;
|
|
1761
|
+
if (p && selectedProfile && p.provider !== selectedProfile.provider) {
|
|
1762
|
+
return false;
|
|
1763
|
+
}
|
|
1764
|
+
return true;
|
|
1765
|
+
});
|
|
1766
|
+
if (siblings.length > 0) {
|
|
1767
|
+
const list = siblings.map((s) => `\`${s}\``).join(", ");
|
|
1768
|
+
const plural = siblings.length === 1 ? "" : "s";
|
|
1769
|
+
const retargetShaped = siblings.some((s) => {
|
|
1770
|
+
const p = resolve(s);
|
|
1771
|
+
return isOnTheWayOut(p?.status) && selectedProfile?.status === "current";
|
|
1772
|
+
});
|
|
1773
|
+
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.` : "";
|
|
1774
|
+
out.push({
|
|
1775
|
+
level: "warn",
|
|
1776
|
+
code: BLOCKED_MODEL_FAMILY_SIBLING_SERVED_CODE,
|
|
1777
|
+
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}`,
|
|
1778
|
+
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.`,
|
|
1779
|
+
docsUrl: DOCS_URL
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
return out;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
// src/advisor-rules/promote-ready.ts
|
|
1787
|
+
var PROMOTE_READY_THRESHOLDS = {
|
|
1788
|
+
minPassRate: 0.8,
|
|
1789
|
+
minAvgScore: 4
|
|
1790
|
+
};
|
|
1791
|
+
function shouldFirePromoteReady(finding, resolvedPrimary) {
|
|
1792
|
+
if (finding.currentModel !== resolvedPrimary) return false;
|
|
1793
|
+
if (finding.judgePassRate < PROMOTE_READY_THRESHOLDS.minPassRate) return false;
|
|
1794
|
+
if (finding.judgeAvgScore < PROMOTE_READY_THRESHOLDS.minAvgScore) return false;
|
|
1795
|
+
return true;
|
|
1796
|
+
}
|
|
1797
|
+
function deriveFamilyLocal(modelId) {
|
|
1798
|
+
if (modelId.startsWith("claude-opus-")) return "claude-opus";
|
|
1799
|
+
if (modelId.startsWith("claude-sonnet-")) return "claude-sonnet";
|
|
1800
|
+
if (modelId.startsWith("claude-haiku-")) return "claude-haiku";
|
|
1801
|
+
if (/^gemini-.*-flash-lite/.test(modelId)) return "gemini-flash-lite";
|
|
1802
|
+
if (/^gemini-.*-flash/.test(modelId)) return "gemini-flash";
|
|
1803
|
+
if (/^gemini-.*-pro/.test(modelId)) return "gemini-pro";
|
|
1804
|
+
if (/^deepseek-.*-pro/.test(modelId)) return "deepseek-reasoner";
|
|
1805
|
+
if (modelId.startsWith("deepseek-")) return "deepseek-chat";
|
|
1806
|
+
if (modelId.startsWith("gpt-")) return "openai-gpt";
|
|
1807
|
+
return null;
|
|
1808
|
+
}
|
|
1809
|
+
function advisorRulePromoteReady(ctx) {
|
|
1810
|
+
if (!isPromoteReadyBrainActive()) return [];
|
|
1811
|
+
if (!ctx.appId) return [];
|
|
1812
|
+
if (!ctx.resolvedPrimary) return [];
|
|
1813
|
+
const family = deriveFamilyLocal(ctx.resolvedPrimary);
|
|
1814
|
+
if (!family) return [];
|
|
1815
|
+
const findings = loadPromoteReadyFindings({
|
|
1816
|
+
appId: ctx.appId,
|
|
1817
|
+
archetype: ctx.archetype,
|
|
1818
|
+
family
|
|
1819
|
+
});
|
|
1820
|
+
if (findings.length === 0) return [];
|
|
1821
|
+
const qualifying = findings.filter(
|
|
1822
|
+
(f) => shouldFirePromoteReady(f, ctx.resolvedPrimary)
|
|
1823
|
+
);
|
|
1824
|
+
if (qualifying.length === 0) return [];
|
|
1825
|
+
qualifying.sort((a, b) => {
|
|
1826
|
+
if (a.judgeAvgScore !== b.judgeAvgScore) {
|
|
1827
|
+
return b.judgeAvgScore - a.judgeAvgScore;
|
|
1828
|
+
}
|
|
1829
|
+
return b.judgePassRate - a.judgePassRate;
|
|
1830
|
+
});
|
|
1831
|
+
const top = qualifying[0];
|
|
1832
|
+
const pctPass = Math.round(top.judgePassRate * 100);
|
|
1833
|
+
const score = top.judgeAvgScore.toFixed(2);
|
|
1834
|
+
let costClause = "";
|
|
1835
|
+
if (top.costDeltaPct !== null) {
|
|
1836
|
+
const sign = top.costDeltaPct < 0 ? "cheaper" : "more expensive";
|
|
1837
|
+
const magnitude = Math.abs(top.costDeltaPct * 100).toFixed(1);
|
|
1838
|
+
costClause = `, cost ${magnitude}% ${sign}`;
|
|
1839
|
+
}
|
|
1840
|
+
const message = `Probe found ${top.candidateModel} produces equivalent-or-better outputs vs ${top.currentModel} on ${top.sampleN} recent ${top.archetype} prompts (pass rate ${pctPass}%, avg score ${score}/5${costClause}). Consider promoting via markPromoteReadyHandled.`;
|
|
1841
|
+
return [
|
|
1842
|
+
{
|
|
1843
|
+
level: "info",
|
|
1844
|
+
code: "promote-ready",
|
|
1845
|
+
message,
|
|
1846
|
+
suggestion: `Migrate ${top.archetype} traffic from ${top.currentModel} to ${top.candidateModel}, then call markPromoteReadyHandled({ appId, archetype: '${top.archetype}', family: '${top.family}', resolution: 'promoted' }) to silence this advisory.`,
|
|
1847
|
+
// alpha.36 architectural field — not a no-ai-needed case.
|
|
1848
|
+
recommendedArchitecture: void 0,
|
|
1849
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1850
|
+
}
|
|
1851
|
+
];
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
// src/advisor-rules/consumer-on-stale-model.ts
|
|
1855
|
+
function isStaleStatus(v) {
|
|
1856
|
+
return v === "legacy" || v === "deprecated";
|
|
1857
|
+
}
|
|
1858
|
+
function asString(v) {
|
|
1859
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
1860
|
+
}
|
|
1861
|
+
function mapRowsToFindings3(rows) {
|
|
1862
|
+
const out = [];
|
|
1863
|
+
for (const raw of rows) {
|
|
1864
|
+
if (!raw || typeof raw !== "object") continue;
|
|
1865
|
+
const r = raw;
|
|
1866
|
+
const archetype = asString(r.intent_archetype) ?? asString(r.applies_to_archetype);
|
|
1867
|
+
const staleModel = asString(r.stale_model) ?? asString(r.applies_to_model);
|
|
1868
|
+
const staleProvider = asString(r.stale_provider);
|
|
1869
|
+
const recommendedModel = asString(r.recommended_model);
|
|
1870
|
+
const family = asString(r.family);
|
|
1871
|
+
const message = asString(r.message);
|
|
1872
|
+
if (!archetype || !staleModel || !recommendedModel || !family || !message) {
|
|
1873
|
+
continue;
|
|
1874
|
+
}
|
|
1875
|
+
if (!isStaleStatus(r.stale_status)) continue;
|
|
1876
|
+
const row = {
|
|
1877
|
+
archetype,
|
|
1878
|
+
staleModel,
|
|
1879
|
+
staleProvider: staleProvider ?? "unknown",
|
|
1880
|
+
staleStatus: r.stale_status,
|
|
1881
|
+
recommendedModel,
|
|
1882
|
+
family,
|
|
1883
|
+
message
|
|
1884
|
+
};
|
|
1885
|
+
const suggestion = asString(r.suggestion);
|
|
1886
|
+
if (suggestion) row.suggestion = suggestion;
|
|
1887
|
+
if (typeof r.observation_count === "number" && Number.isFinite(r.observation_count)) {
|
|
1888
|
+
row.observationCount = r.observation_count;
|
|
1889
|
+
}
|
|
1890
|
+
out.push(row);
|
|
1891
|
+
}
|
|
1892
|
+
return out;
|
|
1893
|
+
}
|
|
1894
|
+
var snapshots5 = /* @__PURE__ */ new Map();
|
|
1895
|
+
var runtime5;
|
|
1896
|
+
var warnedOnce5 = false;
|
|
1897
|
+
var pendingRefreshes5 = /* @__PURE__ */ new Map();
|
|
1898
|
+
function isStaleModelFindingsBrainActive() {
|
|
1899
|
+
return runtime5 !== void 0;
|
|
1900
|
+
}
|
|
1901
|
+
function getStaleModelFindings(opts) {
|
|
1902
|
+
const rt = runtime5;
|
|
1903
|
+
if (!rt) return [];
|
|
1904
|
+
const appId = opts.appId;
|
|
1905
|
+
if (!appId) return [];
|
|
1906
|
+
let snap = snapshots5.get(appId);
|
|
1907
|
+
if (!snap) {
|
|
1908
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1909
|
+
snapshots5.set(appId, snap);
|
|
1910
|
+
}
|
|
1911
|
+
const now = Date.now();
|
|
1912
|
+
const stale = snap.expiresAt <= now;
|
|
1913
|
+
if (stale && !snap.refreshing) {
|
|
1914
|
+
snap.refreshing = true;
|
|
1915
|
+
void asyncRefresh5(rt, appId);
|
|
1916
|
+
}
|
|
1917
|
+
if (opts.archetype) {
|
|
1918
|
+
return snap.data.filter((f) => f.archetype === opts.archetype);
|
|
1919
|
+
}
|
|
1920
|
+
return snap.data;
|
|
1921
|
+
}
|
|
1922
|
+
async function asyncRefresh5(rt, appId) {
|
|
1923
|
+
const promise = doRefresh5(rt, appId);
|
|
1924
|
+
pendingRefreshes5.set(appId, promise);
|
|
1925
|
+
try {
|
|
1926
|
+
await promise;
|
|
1927
|
+
} finally {
|
|
1928
|
+
if (pendingRefreshes5.get(appId) === promise) {
|
|
1929
|
+
pendingRefreshes5.delete(appId);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
async function doRefresh5(rt, appId) {
|
|
1934
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
1935
|
+
let snap = snapshots5.get(appId);
|
|
1936
|
+
if (!snap) {
|
|
1937
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1938
|
+
snapshots5.set(appId, snap);
|
|
1939
|
+
}
|
|
1940
|
+
try {
|
|
1941
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
1942
|
+
if (!res.ok) {
|
|
1943
|
+
throw new Error(`stale-model findings ${res.status}: ${res.statusText}`);
|
|
1944
|
+
}
|
|
1945
|
+
const body = await res.json();
|
|
1946
|
+
if (runtime5 !== rt) return;
|
|
1947
|
+
const rows = Array.isArray(body) ? mapRowsToFindings3(body) : [];
|
|
1948
|
+
snap.data = rows;
|
|
1949
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1950
|
+
snap.refreshing = false;
|
|
1951
|
+
} catch (err) {
|
|
1952
|
+
if (runtime5 !== rt) return;
|
|
1953
|
+
snap.refreshing = false;
|
|
1954
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1955
|
+
if (!warnedOnce5) {
|
|
1956
|
+
warnedOnce5 = true;
|
|
1957
|
+
(rt.onError ?? defaultOnError5)(err);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
function defaultOnError5(err) {
|
|
1962
|
+
console.warn(
|
|
1963
|
+
"[kgauto] stale-model findings fetch failed (using empty fallback):",
|
|
1964
|
+
err
|
|
1965
|
+
);
|
|
1966
|
+
}
|
|
1967
|
+
var CONSUMER_ON_STALE_MODEL_RULE_CODE = "consumer-on-stale-model";
|
|
1968
|
+
function advisorRuleConsumerOnStaleModel(ir) {
|
|
1969
|
+
if (!isStaleModelFindingsBrainActive()) return [];
|
|
1970
|
+
if (!ir.appId) return [];
|
|
1971
|
+
const findings = getStaleModelFindings({
|
|
1972
|
+
appId: ir.appId,
|
|
1973
|
+
archetype: ir.intent.archetype
|
|
1974
|
+
});
|
|
1975
|
+
if (findings.length === 0) return [];
|
|
1976
|
+
const ranked = [...findings].sort((a, b) => {
|
|
1977
|
+
if (a.staleStatus !== b.staleStatus) {
|
|
1978
|
+
return a.staleStatus === "deprecated" ? -1 : 1;
|
|
1979
|
+
}
|
|
1980
|
+
return a.staleModel.localeCompare(b.staleModel);
|
|
1981
|
+
});
|
|
1982
|
+
const top = ranked[0];
|
|
1983
|
+
const extraCount = findings.length - 1;
|
|
1984
|
+
const extraNote = extraCount > 0 ? ` (+ ${extraCount} more stale model${extraCount === 1 ? "" : "s"} for this archetype)` : "";
|
|
1985
|
+
return [
|
|
1986
|
+
{
|
|
1987
|
+
level: "warn",
|
|
1988
|
+
code: CONSUMER_ON_STALE_MODEL_RULE_CODE,
|
|
1989
|
+
message: `${top.message}${extraNote}`,
|
|
1990
|
+
suggestion: top.suggestion ?? `Migrate ${top.staleModel} \u2192 ${top.recommendedModel} for archetype "${top.archetype}". The newer model is the current latest in the "${top.family}" family; the stale one is ${top.staleStatus}.`,
|
|
1991
|
+
recommendationType: "model-swap",
|
|
1992
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1993
|
+
}
|
|
1994
|
+
];
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// src/advisor-rules/cross-family-fit.ts
|
|
1998
|
+
function familyHasCurrentActiveModel(family) {
|
|
1999
|
+
for (const profile of allProfiles()) {
|
|
2000
|
+
const profileFamily = profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
2001
|
+
if (profileFamily !== family) continue;
|
|
2002
|
+
if (profile.status !== "current") continue;
|
|
2003
|
+
if (profile.active === false) continue;
|
|
2004
|
+
return true;
|
|
2005
|
+
}
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
function listCandidatesInFamily(family) {
|
|
2009
|
+
const candidates = [];
|
|
2010
|
+
for (const profile of allProfiles()) {
|
|
2011
|
+
const profileFamily = profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
2012
|
+
if (profileFamily !== family) continue;
|
|
2013
|
+
if (profile.status !== "current") continue;
|
|
2014
|
+
if (profile.active === false) continue;
|
|
2015
|
+
candidates.push(profile.id);
|
|
2016
|
+
if (candidates.length >= 3) break;
|
|
2017
|
+
}
|
|
2018
|
+
return candidates;
|
|
2019
|
+
}
|
|
2020
|
+
function advisorRuleCrossFamilyFit(ctx) {
|
|
2021
|
+
if (!ctx.resolvedPrimary) return [];
|
|
2022
|
+
const currentFamily = deriveFamilyFromModelId(ctx.resolvedPrimary);
|
|
2023
|
+
if (!currentFamily) return [];
|
|
2024
|
+
const fit = findBetterFit(ctx.archetype, currentFamily);
|
|
2025
|
+
if (!fit) return [];
|
|
2026
|
+
if (!familyHasCurrentActiveModel(fit.betterFitFamily)) return [];
|
|
2027
|
+
const candidates = listCandidatesInFamily(fit.betterFitFamily);
|
|
2028
|
+
if (candidates.length === 0) return [];
|
|
2029
|
+
const candidateStr = candidates.join(", ");
|
|
2030
|
+
const message = `Your ${currentFamily} call on ${ctx.archetype} could shift to ${fit.betterFitFamily} \u2014 typically better quality + ${fit.costGuidance}. Suggested candidates: ${candidateStr}.`;
|
|
2031
|
+
return [
|
|
2032
|
+
{
|
|
2033
|
+
level: "info",
|
|
2034
|
+
code: "cross-family-fit-candidate",
|
|
2035
|
+
ownership: "consumer-actionable",
|
|
2036
|
+
message,
|
|
2037
|
+
suggestion: `Swap the model literal in \`ir.models\` to one of: ${candidateStr}. Or call \`getRecommendedPrimary({ family: '${fit.betterFitFamily}', archetype: '${ctx.archetype}', fallback: { id: '${candidates[0]}', reason: 'cross-family-fit-recommendation' } })\` to let kgauto resolve to the current+active family member.`,
|
|
2038
|
+
recommendationType: "model-swap",
|
|
2039
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2040
|
+
}
|
|
2041
|
+
];
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
// src/advisor-rules/preferred-blocked-overlap.ts
|
|
2045
|
+
var PREFERRED_MODEL_BLOCKED_CODE = "preferred-model-blocked";
|
|
2046
|
+
var DOCS_URL2 = "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories";
|
|
2047
|
+
function advisorRulePreferredBlockedOverlap(ctx) {
|
|
2048
|
+
const preferred = ctx.policy?.preferredModels;
|
|
2049
|
+
const blocked = ctx.policy?.blockedModels;
|
|
2050
|
+
if (!preferred?.length || !blocked?.length) return [];
|
|
2051
|
+
const blockedSet = canonicalPolicySet(blocked);
|
|
2052
|
+
const deadPins = [...new Set(preferred)].filter((p) => policySetHas(blockedSet, p)).sort();
|
|
2053
|
+
if (deadPins.length === 0) return [];
|
|
2054
|
+
const list = deadPins.map((p) => `\`${p}\``).join(", ");
|
|
2055
|
+
const one = deadPins.length === 1;
|
|
2056
|
+
const preferredSet = canonicalPolicySet(preferred);
|
|
2057
|
+
const servedIsPinned = policySetHas(preferredSet, ctx.selectedModelId);
|
|
2058
|
+
return [
|
|
2059
|
+
{
|
|
2060
|
+
level: "warn",
|
|
2061
|
+
code: PREFERRED_MODEL_BLOCKED_CODE,
|
|
2062
|
+
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.`),
|
|
2063
|
+
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.`,
|
|
2064
|
+
docsUrl: DOCS_URL2
|
|
2065
|
+
}
|
|
2066
|
+
];
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
// src/advisor.ts
|
|
2070
|
+
var QUALITY_FLOOR_FOR_RECOMMENDATION = 6;
|
|
2071
|
+
var TIER_DOWN_COST_RATIO = 0.5;
|
|
2072
|
+
var COST_MISMATCHED_CHOSEN_SCORE_CEILING = 7;
|
|
2073
|
+
var PRODUCER_OWNED_RULE_CODES = Object.freeze(
|
|
2074
|
+
/* @__PURE__ */ new Set(["model-stale-evidence", "promote-ready"])
|
|
2075
|
+
);
|
|
2076
|
+
function deriveOwnership(code, selfDeclared) {
|
|
2077
|
+
if (selfDeclared) return selfDeclared;
|
|
2078
|
+
return PRODUCER_OWNED_RULE_CODES.has(code) ? "producer-owned" : "consumer-actionable";
|
|
2079
|
+
}
|
|
2080
|
+
function runAdvisor(ir, result, profile, policy, phase2) {
|
|
2081
|
+
const out = [];
|
|
2082
|
+
out.push(...detectCachingOff(ir, profile));
|
|
2083
|
+
out.push(...detectSingleChunkSystem(ir, profile));
|
|
2084
|
+
out.push(...detectToolBloat(ir, result));
|
|
2085
|
+
out.push(...detectHistoryUncached(ir, profile));
|
|
2086
|
+
out.push(...detectSingleModelArray(ir, policy));
|
|
2087
|
+
out.push(
|
|
2088
|
+
...advisorRuleBlockedModelDrift({
|
|
2089
|
+
policy,
|
|
2090
|
+
selectedModelId: profile.id
|
|
2091
|
+
})
|
|
2092
|
+
);
|
|
2093
|
+
out.push(
|
|
2094
|
+
...advisorRulePreferredBlockedOverlap({
|
|
2095
|
+
policy,
|
|
2096
|
+
selectedModelId: profile.id
|
|
2097
|
+
})
|
|
2098
|
+
);
|
|
2099
|
+
if (policy?.posture !== "locked") {
|
|
2100
|
+
out.push(...detectCostMismatchedArchetype(ir, profile, phase2));
|
|
2101
|
+
out.push(...detectModelStaleEvidence(ir, profile));
|
|
2102
|
+
out.push(...detectTierDown(ir, profile, phase2));
|
|
2103
|
+
}
|
|
2104
|
+
if (!translatorClearedToolCallCliff(phase2)) {
|
|
2105
|
+
out.push(...detectArchetypePerfFloorBreach(ir, profile));
|
|
2106
|
+
}
|
|
2107
|
+
if (policy?.posture !== "locked") {
|
|
2108
|
+
out.push(...detectStaleExclusionCandidate(ir));
|
|
2109
|
+
}
|
|
2110
|
+
if (policy?.posture !== "locked" && ir.appId) {
|
|
2111
|
+
out.push(
|
|
2112
|
+
...advisorRulePromoteReady({
|
|
2113
|
+
appId: ir.appId,
|
|
2114
|
+
archetype: ir.intent.archetype,
|
|
2115
|
+
resolvedPrimary: profile.id
|
|
2116
|
+
})
|
|
2117
|
+
);
|
|
2118
|
+
out.push(...advisorRuleConsumerOnStaleModel(ir));
|
|
2119
|
+
}
|
|
2120
|
+
if (policy?.posture !== "locked") {
|
|
2121
|
+
out.push(
|
|
2122
|
+
...advisorRuleCrossFamilyFit({
|
|
2123
|
+
archetype: ir.intent.archetype,
|
|
2124
|
+
resolvedPrimary: profile.id
|
|
2125
|
+
})
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
return out;
|
|
2129
|
+
}
|
|
2130
|
+
function translatorClearedToolCallCliff(phase2) {
|
|
2131
|
+
const rewrites = phase2?.sectionRewritesApplied;
|
|
2132
|
+
if (!rewrites || rewrites.length === 0) return false;
|
|
2133
|
+
for (const rw of rewrites) {
|
|
2134
|
+
if (rw.kind === "tool_call_contract") return true;
|
|
2135
|
+
}
|
|
2136
|
+
return false;
|
|
2137
|
+
}
|
|
2138
|
+
function detectCachingOff(ir, profile) {
|
|
2139
|
+
if (profile.provider !== "anthropic") return [];
|
|
2140
|
+
const totalChars = ir.sections.reduce((s, sec) => s + sec.text.length, 0);
|
|
2141
|
+
if (totalChars < 2e3) return [];
|
|
2142
|
+
const anyCacheable = ir.sections.some((s) => s.cacheable === true);
|
|
2143
|
+
if (anyCacheable) return [];
|
|
2144
|
+
return [
|
|
2145
|
+
{
|
|
2146
|
+
level: "warn",
|
|
2147
|
+
code: "caching-off-on-claude",
|
|
2148
|
+
message: `System prompt is ${totalChars} chars on Anthropic but no PromptSection has cacheable=true. Anthropic prompt caching cuts cached-prefix input cost by ~90% on subsequent calls; without it, every turn re-pays full price for the static system context.`,
|
|
2149
|
+
suggestion: "Mark stable system sections (role, persona, tool policy) with `cacheable: true`. The lowering pass concatenates cacheable sections into a single cache-controlled block before the dynamic ones.",
|
|
2150
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2151
|
+
}
|
|
2152
|
+
];
|
|
2153
|
+
}
|
|
2154
|
+
function detectSingleChunkSystem(ir, profile) {
|
|
2155
|
+
if (profile.provider !== "anthropic") return [];
|
|
2156
|
+
if (ir.sections.length !== 1) return [];
|
|
2157
|
+
const only = ir.sections[0];
|
|
2158
|
+
if (!only || only.text.length <= 1e3) return [];
|
|
2159
|
+
return [
|
|
2160
|
+
{
|
|
2161
|
+
level: "info",
|
|
2162
|
+
code: "single-chunk-system",
|
|
2163
|
+
message: `System prompt is a single ${only.text.length}-char chunk. Splitting into NamedChunks (static role/persona vs dynamic context) gives the lowering pass a finer cache-marker boundary \u2014 only the static portion needs to be byte-stable for the cache to hit.`,
|
|
2164
|
+
suggestion: "Refactor the system builder to return an array of `PromptSection` shaped { id, text, cacheable?: boolean }. Static chunks (role, persona, tool policy) get `cacheable: true`; dynamic ones (current context, today's date) don't. NOTE: the lowering pass HOISTS cacheable sections ahead of dynamic ones on the Anthropic wire (prefix caching requires it) \u2014 if your prompt has a protected ordering (e.g. a voice/persona block that must precede boilerplate), splitting will reorder the compiled output; declining this advisory is then correct. Also: a cacheable block under ~1024 tokens gets NO cache_control marker (provider minimum), so marking small sections is inert, not harmful.",
|
|
2165
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2166
|
+
}
|
|
2167
|
+
];
|
|
2168
|
+
}
|
|
2169
|
+
function detectToolBloat(ir, result) {
|
|
2170
|
+
const SHORT_OUTPUT = /* @__PURE__ */ new Set([
|
|
2171
|
+
"classify",
|
|
2172
|
+
"extract",
|
|
2173
|
+
"summarize",
|
|
2174
|
+
"transform",
|
|
2175
|
+
"critique"
|
|
2176
|
+
]);
|
|
2177
|
+
if (!ir.tools || ir.tools.length === 0) return [];
|
|
2178
|
+
const toolsKept = result.diagnostics.toolsKept;
|
|
2179
|
+
if (toolsKept <= 10) return [];
|
|
2180
|
+
if (!SHORT_OUTPUT.has(ir.intent.archetype)) return [];
|
|
2181
|
+
return [
|
|
2182
|
+
{
|
|
2183
|
+
level: "warn",
|
|
2184
|
+
code: "tool-bloat",
|
|
2185
|
+
message: `${toolsKept} tools kept after the relevance pass for archetype="${ir.intent.archetype}" (consumer declared ${ir.tools.length}). This archetype is short-output and rarely needs more than 3 tools; each tool definition eats ~350 tokens of context budget.`,
|
|
2186
|
+
suggestion: "Tighten `relevanceByIntent: { [archetype]: 0..1 }` per ToolDefinition. Tools below `toolRelevanceThreshold` (default 0.2) get dropped. Without `relevanceByIntent`, every tool defaults to neutral (0.5) and stays.",
|
|
2187
|
+
docsUrl: "https://github.com/stue/kgauto/blob/main/v2/README.md#tools"
|
|
2188
|
+
}
|
|
2189
|
+
];
|
|
2190
|
+
}
|
|
2191
|
+
function detectHistoryUncached(ir, profile) {
|
|
2192
|
+
if (profile.provider !== "anthropic") return [];
|
|
2193
|
+
if (!ir.history || ir.history.length < 2) return [];
|
|
2194
|
+
if (ir.historyCachePolicy && ir.historyCachePolicy.strategy !== "none") {
|
|
2195
|
+
return [];
|
|
2196
|
+
}
|
|
2197
|
+
return [
|
|
2198
|
+
{
|
|
2199
|
+
level: "warn",
|
|
2200
|
+
code: "history-uncached-on-claude",
|
|
2201
|
+
message: `${ir.history.length} history messages on Anthropic with no historyCachePolicy. Every turn re-pays for the full conversation context; with caching, subsequent turns hit the cache at ~10% the input cost.`,
|
|
2202
|
+
suggestion: "Set `historyCachePolicy: { strategy: 'all-but-latest' }` on this IR. The lowering pass marks the message immediately preceding currentTurn with cache_control; subsequent turns whose history prefix matches byte-for-byte hit the cache.",
|
|
2203
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2204
|
+
}
|
|
2205
|
+
];
|
|
2206
|
+
}
|
|
2207
|
+
function detectSingleModelArray(ir, policy) {
|
|
2208
|
+
if (ir.models.length !== 1) return [];
|
|
2209
|
+
if (policy?.posture === "locked") return [];
|
|
2210
|
+
const entry = ir.models[0];
|
|
2211
|
+
const only = typeof entry === "string" ? entry : `family:${entry.family}`;
|
|
2212
|
+
const blocked = canonicalPolicySet(policy?.blockedModels);
|
|
2213
|
+
let alternatives = [];
|
|
2214
|
+
try {
|
|
2215
|
+
alternatives = getDefaultFallbackChain({
|
|
2216
|
+
archetype: ir.intent.archetype,
|
|
2217
|
+
primary: only,
|
|
2218
|
+
posture: "preferred",
|
|
2219
|
+
policy
|
|
2220
|
+
}).filter((id) => resolveModelAlias(id) !== resolveModelAlias(only)).filter((id) => !policySetHas(blocked, id)).filter((id) => getModelCompatibility(id, { archetype: ir.intent.archetype }).status !== "reject");
|
|
2221
|
+
} catch {
|
|
2222
|
+
}
|
|
2223
|
+
const hasAlternative = alternatives.length > 0;
|
|
2224
|
+
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.";
|
|
2225
|
+
return [
|
|
2226
|
+
{
|
|
2227
|
+
level: hasAlternative ? "critical" : "warn",
|
|
2228
|
+
code: "single-model-array",
|
|
2229
|
+
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.`,
|
|
2230
|
+
suggestion: remedy,
|
|
2231
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#single-model-array"
|
|
2232
|
+
}
|
|
2233
|
+
];
|
|
2234
|
+
}
|
|
2235
|
+
function suppressedRecommendationReason(ir, archetype, altProfile) {
|
|
2236
|
+
if (getMeasuredFailureVerdict({ appId: ir.appId, archetype, model: altProfile.id })?.gated === true) {
|
|
2237
|
+
return "measured-failure-gate";
|
|
2238
|
+
}
|
|
2239
|
+
if (ir.constraints?.structuredOutput && effectiveConventions(altProfile).some(
|
|
2240
|
+
(c) => c.archetype === archetype && c.structuredOutputHint === "avoid"
|
|
2241
|
+
)) {
|
|
2242
|
+
return "schema-weakness-convention";
|
|
2243
|
+
}
|
|
2244
|
+
if (getRecentRollback({ appId: ir.appId, archetype, model: altProfile.id }) !== void 0) {
|
|
2245
|
+
return "recent-rollback";
|
|
2246
|
+
}
|
|
2247
|
+
return void 0;
|
|
2248
|
+
}
|
|
2249
|
+
function openPostureRemedyClause(archetype, recommended, resolveProfile) {
|
|
2250
|
+
const openChain = getDefaultFallbackChain({ archetype, posture: "open" });
|
|
2251
|
+
const openPrimaryId = openChain[0];
|
|
2252
|
+
if (!openPrimaryId) return void 0;
|
|
2253
|
+
const openPrimary = resolveProfile(openPrimaryId);
|
|
2254
|
+
if (!openPrimary) return void 0;
|
|
2255
|
+
const openCost = estimateChainCostUsd(openPrimary);
|
|
2256
|
+
const recommendedCost = estimateChainCostUsd(recommended);
|
|
2257
|
+
if (openCost >= recommendedCost) return void 0;
|
|
2258
|
+
const ratio = recommendedCost / openCost;
|
|
2259
|
+
return ` Relaxing to \`policy.posture = 'open'\` is also viable here \u2014 it selects \`${openPrimaryId}\`, which is ${ratio >= 1.1 ? `about ${ratio.toFixed(1)}x cheaper than` : "priced comparably to"} \`${recommended.id}\` at a reference call shape. Note the library chain is ordered by archetype performance, not cost; it happens to agree with the cost recommendation for this archetype.`;
|
|
2260
|
+
}
|
|
2261
|
+
function openPostureCostWarningClause(archetype, recommended, resolveProfile) {
|
|
2262
|
+
const openChain = getDefaultFallbackChain({ archetype, posture: "open" });
|
|
2263
|
+
const openPrimaryId = openChain[0];
|
|
2264
|
+
const openPrimary = openPrimaryId ? resolveProfile(openPrimaryId) : void 0;
|
|
2265
|
+
if (!openPrimary || !openPrimaryId) return "";
|
|
2266
|
+
const ratio = estimateChainCostUsd(openPrimary) / estimateChainCostUsd(recommended);
|
|
2267
|
+
if (ratio < 1) return "";
|
|
2268
|
+
return ` Do NOT reach for \`posture = 'open'\` as a cost fix here: the library chain is ordered by archetype performance, not cost, and would select \`${openPrimaryId}\` \u2014 roughly ${ratio.toFixed(1)}x the cost of \`${recommended.id}\`. Pass \`getDefaultFallbackChain({ archetype: '${archetype}', posture: 'open', optimizeFor: 'cost' })\` if you want a library-picked chain that is actually cost-ordered.`;
|
|
2269
|
+
}
|
|
2270
|
+
function detectCostMismatchedArchetype(ir, profile, phase2) {
|
|
2271
|
+
if (!phase2 || phase2.fallbackChain.length === 0) return [];
|
|
2272
|
+
if (!phase2.profileResolver) return [];
|
|
2273
|
+
const archetype = ir.intent.archetype;
|
|
2274
|
+
const chosenScore = getArchetypePerfScore(profile.id, archetype);
|
|
2275
|
+
const chosenHasRoomToGrow = chosenScore.grounding === "judgment" || chosenScore.score < COST_MISMATCHED_CHOSEN_SCORE_CEILING;
|
|
2276
|
+
if (!chosenHasRoomToGrow) return [];
|
|
2277
|
+
let bestAlt = null;
|
|
2278
|
+
for (const altId of phase2.fallbackChain) {
|
|
2279
|
+
const altProfile = phase2.profileResolver(altId);
|
|
2280
|
+
if (!altProfile) continue;
|
|
2281
|
+
if (altProfile.id === profile.id) continue;
|
|
2282
|
+
const altScore = getArchetypePerfScore(altProfile.id, archetype);
|
|
2283
|
+
if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
|
|
2284
|
+
if (altScore.score < chosenScore.score) continue;
|
|
2285
|
+
if (altProfile.costInputPer1m >= profile.costInputPer1m) continue;
|
|
2286
|
+
if (suppressedRecommendationReason(ir, archetype, altProfile)) continue;
|
|
2287
|
+
if (!bestAlt || altScore.score > bestAlt.score.score || altScore.score === bestAlt.score.score && altProfile.costInputPer1m < bestAlt.profile.costInputPer1m) {
|
|
2288
|
+
bestAlt = { id: altId, profile: altProfile, score: altScore };
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
if (!bestAlt) return [];
|
|
2292
|
+
const tierDownWouldFire = bestAlt.score.grounding === "measured" && bestAlt.profile.costInputPer1m <= profile.costInputPer1m * TIER_DOWN_COST_RATIO;
|
|
2293
|
+
if (tierDownWouldFire) return [];
|
|
2294
|
+
const chosenGrounding = chosenScore.grounding === "judgment" ? `archetypePerf.${archetype}=judgment` : `archetypePerf.${archetype}=${chosenScore.score}`;
|
|
2295
|
+
const altGrounding = bestAlt.score.grounding === "measured" ? `archetypePerf.${archetype}=${bestAlt.score.score}, measured, n=${bestAlt.score.n}` : `archetypePerf.${archetype}=${bestAlt.score.score}, judgment`;
|
|
2296
|
+
return [
|
|
2297
|
+
{
|
|
2298
|
+
level: "warn",
|
|
2299
|
+
code: "cost-mismatched-archetype",
|
|
2300
|
+
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.`,
|
|
2301
|
+
// alpha.80 (tt-intel's `chain-builder-and-cost-advisories-give-opposite-answers`):
|
|
2302
|
+
// this field used to offer `posture='open'` unconditionally, alongside
|
|
2303
|
+
// the cheap-model swap, as if the two were equivalent remedies. They
|
|
2304
|
+
// point in opposite directions. Both branches are now computed from
|
|
2305
|
+
// the chain builder rather than assumed.
|
|
2306
|
+
suggestion: `Consider declaring \`${bestAlt.id}\` as the primary model for this archetype.` + (openPostureRemedyClause(archetype, bestAlt.profile, phase2.profileResolver) ?? openPostureCostWarningClause(archetype, bestAlt.profile, phase2.profileResolver)) + ` If the chosen model is required for compliance/brand reasons, set \`policy.posture = 'locked'\` to silence this rule.`,
|
|
2307
|
+
recommendationType: profile.provider === bestAlt.profile.provider ? "tier-down" : "model-swap",
|
|
2308
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2309
|
+
}
|
|
2310
|
+
];
|
|
2311
|
+
}
|
|
2312
|
+
function detectModelStaleEvidence(ir, profile) {
|
|
2313
|
+
if (!isBrainQueryActiveFor("kgauto_archetype_perf")) return [];
|
|
2314
|
+
const archetype = ir.intent.archetype;
|
|
2315
|
+
const chosen = getArchetypePerfScore(profile.id, archetype);
|
|
2316
|
+
if (chosen.grounding !== "judgment") return [];
|
|
2317
|
+
return [
|
|
2318
|
+
{
|
|
2319
|
+
level: "info",
|
|
2320
|
+
code: "model-stale-evidence",
|
|
2321
|
+
message: `Model-stale-evidence: target=${profile.id} archetype=${archetype} is judgment-grounded (n=${chosen.n}, cross-app 90d window) despite brain-query mode being active. Fewer than 10 outcomes back this (model, archetype) tuple across ALL consumers \u2014 routing decisions remain pre-measured for this slot.`,
|
|
2322
|
+
suggestion: "Verify that `record()` is being called on every call() outcome. Counts are cross-app (migration 050 view): once ANY consumers accumulate n>=10 rows on this tuple, the score promotes from judgment to measured automatically within the 5-min SWR window. (Before alpha.78 this promotion was advertised but had no implementing mechanism \u2014 n was never populated; if this advisory has been firing for weeks at n=0 despite real traffic, bump to >=alpha.78 and it will clear on its own.)",
|
|
2323
|
+
recommendationType: "prompt-fix",
|
|
2324
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2325
|
+
}
|
|
2326
|
+
];
|
|
2327
|
+
}
|
|
2328
|
+
function detectTierDown(ir, profile, phase2) {
|
|
2329
|
+
if (!phase2 || phase2.fallbackChain.length === 0) return [];
|
|
2330
|
+
if (!phase2.profileResolver) return [];
|
|
2331
|
+
const archetype = ir.intent.archetype;
|
|
2332
|
+
const chosenScore = getArchetypePerfScore(profile.id, archetype);
|
|
2333
|
+
const chosenCost = profile.costInputPer1m;
|
|
2334
|
+
let bestAlt = null;
|
|
2335
|
+
for (const altId of phase2.fallbackChain) {
|
|
2336
|
+
const altProfile = phase2.profileResolver(altId);
|
|
2337
|
+
if (!altProfile) continue;
|
|
2338
|
+
if (altProfile.id === profile.id) continue;
|
|
2339
|
+
const altScore = getArchetypePerfScore(altProfile.id, archetype);
|
|
2340
|
+
if (altScore.grounding !== "measured") continue;
|
|
2341
|
+
if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
|
|
2342
|
+
if (altScore.score < chosenScore.score) continue;
|
|
2343
|
+
if (altProfile.costInputPer1m > chosenCost * TIER_DOWN_COST_RATIO) continue;
|
|
2344
|
+
if (suppressedRecommendationReason(ir, archetype, altProfile)) continue;
|
|
2345
|
+
if (!bestAlt || altProfile.costInputPer1m < bestAlt.profile.costInputPer1m || altProfile.costInputPer1m === bestAlt.profile.costInputPer1m && altScore.score > bestAlt.score.score) {
|
|
2346
|
+
bestAlt = { id: altId, profile: altProfile, score: altScore };
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
if (!bestAlt) return [];
|
|
2350
|
+
const chosenDesc = chosenScore.grounding === "measured" ? `archetypePerf.${archetype}=${chosenScore.score} (measured, n=${chosenScore.n})` : `archetypePerf.${archetype}=${chosenScore.score} (${chosenScore.grounding})`;
|
|
2351
|
+
return [
|
|
2352
|
+
{
|
|
2353
|
+
level: "warn",
|
|
2354
|
+
code: "tier-down",
|
|
2355
|
+
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.`,
|
|
2356
|
+
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.`,
|
|
2357
|
+
recommendationType: "tier-down",
|
|
2358
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2359
|
+
}
|
|
2360
|
+
];
|
|
2361
|
+
}
|
|
2362
|
+
function detectArchetypePerfFloorBreach(ir, profile) {
|
|
2363
|
+
const compat = getModelCompatibility(profile.id, {
|
|
2364
|
+
archetype: ir.intent.archetype,
|
|
2365
|
+
toolOrchestration: ir.constraints?.toolOrchestration
|
|
2366
|
+
});
|
|
2367
|
+
if (compat.status === "compatible") return [];
|
|
2368
|
+
if (compat.status === "requires-adapter") {
|
|
2369
|
+
return [
|
|
2370
|
+
{
|
|
2371
|
+
level: "warn",
|
|
2372
|
+
code: "archetype-perf-floor-breach",
|
|
2373
|
+
message: `${profile.id} sits below the archetype floor for ${ir.intent.archetype} (score ${compat.archetypePerf}/10, floor ${6}). A known adapter would lift it: ${compat.adapter.parameter}=${compat.adapter.value}. ${compat.adapter.consequence}`,
|
|
2374
|
+
suggestion: `Pass \`ir.constraints.${compat.adapter.parameter} = '${compat.adapter.value}'\` for this call, OR pick a model whose archetypePerf for ${ir.intent.archetype} already clears the floor (call \`getModelCompatibility(modelId, { archetype: '${ir.intent.archetype}' })\` to check). Estimated post-adapter score: ${compat.archetypePerfWithAdapter}/10.`,
|
|
2375
|
+
recommendationType: "prompt-fix",
|
|
2376
|
+
suggestedAdaptation: compat.adapter,
|
|
2377
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2378
|
+
}
|
|
2379
|
+
];
|
|
2380
|
+
}
|
|
2381
|
+
return [
|
|
2382
|
+
{
|
|
2383
|
+
level: "critical",
|
|
2384
|
+
code: "archetype-perf-floor-breach",
|
|
2385
|
+
message: `${profile.id} sits below the archetype floor for ${ir.intent.archetype} (score ${compat.archetypePerf}/10, floor ${6}) and no known adapter would lift it. ${compat.reason}`,
|
|
2386
|
+
// alpha.80: this rule's advice is correct on the QUALITY axis and was
|
|
2387
|
+
// silent on cost, so a consumer reading it as a general "let the
|
|
2388
|
+
// library pick" endorsement could land on a materially pricier
|
|
2389
|
+
// primary. The floor recommendation stands; the axis is now named,
|
|
2390
|
+
// and the cost-ordered variant is offered alongside it.
|
|
2391
|
+
//
|
|
2392
|
+
// alpha.82 (cc-Cairn 2026-07-29, follow-on 1): the suggestion read as
|
|
2393
|
+
// advice about the ARCHETYPE when it is a fact about ONE CALL. Model
|
|
2394
|
+
// selection is input-size dependent, so a rule that fires on the
|
|
2395
|
+
// small-payload tail of a path whose typical case clears the floor
|
|
2396
|
+
// reads as "your primary is wrong" when the accurate statement is
|
|
2397
|
+
// "this call landed here." Ground truth for the wording: tt-intel's
|
|
2398
|
+
// open critical is `gemini-2.5-flash-lite` (ask, 5/10) selected at
|
|
2399
|
+
// ~2.1K tokens_in, while `claude-opus-5` served the same archetype at
|
|
2400
|
+
// ~22K the same day — cc read the evidence set as self-contradictory
|
|
2401
|
+
// precisely because the text did not say which call it described.
|
|
2402
|
+
suggestion: `This fired for \`${profile.id}\`, the model selected for THIS call \u2014 selection is input-size dependent, so other calls on \`${ir.intent.archetype}\` may pick a different model that clears the floor. Swap to a model whose archetypePerf for ${ir.intent.archetype} clears the floor. Use \`getModelCompatibility(candidateId, { archetype: '${ir.intent.archetype}' })\` to vet candidates, or \`getDefaultFallbackChain({ archetype: '${ir.intent.archetype}', posture: 'open' })\` for a library-picked chain that respects the floor by construction \u2014 note that chain is ordered by archetype performance, NOT cost, so it may select a pricier primary than you run today. Add \`optimizeFor: 'cost'\` for a chain that clears the same floor cheapest-first. To stop this model being selected at all, pass \`policy.blockedModels: ['${profile.id}']\`.`,
|
|
2403
|
+
recommendationType: "model-swap",
|
|
2404
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2405
|
+
}
|
|
2406
|
+
];
|
|
2407
|
+
}
|
|
2408
|
+
function detectStaleExclusionCandidate(ir) {
|
|
2409
|
+
if (!isExclusionFindingsBrainActive()) return [];
|
|
2410
|
+
if (!ir.appId) return [];
|
|
2411
|
+
const findings = getStaleExclusionFindings({
|
|
2412
|
+
appId: ir.appId,
|
|
2413
|
+
archetype: ir.intent.archetype
|
|
2414
|
+
});
|
|
2415
|
+
if (findings.length === 0) return [];
|
|
2416
|
+
const ranked = [...findings].sort((a, b) => {
|
|
2417
|
+
const sa = a.estimatedSavingsUsd30d ?? -Infinity;
|
|
2418
|
+
const sb = b.estimatedSavingsUsd30d ?? -Infinity;
|
|
2419
|
+
if (sa !== sb) return sb - sa;
|
|
2420
|
+
return confidenceRank(b.confidence) - confidenceRank(a.confidence);
|
|
2421
|
+
});
|
|
2422
|
+
const top = ranked[0];
|
|
2423
|
+
const extraCount = findings.length - 1;
|
|
2424
|
+
const extraNote = extraCount > 0 ? ` (+ ${extraCount} more excluded model${extraCount === 1 ? "" : "s"} for this archetype)` : "";
|
|
2425
|
+
return [
|
|
2426
|
+
{
|
|
2427
|
+
level: "info",
|
|
2428
|
+
code: "stale-exclusion-candidate",
|
|
2429
|
+
message: `${top.message}${extraNote}`,
|
|
2430
|
+
suggestion: top.suggestion,
|
|
2431
|
+
recommendationType: "tier-down",
|
|
2432
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2433
|
+
}
|
|
2434
|
+
];
|
|
2435
|
+
}
|
|
2436
|
+
function confidenceRank(c) {
|
|
2437
|
+
if (c === "high") return 3;
|
|
2438
|
+
if (c === "medium") return 2;
|
|
2439
|
+
return 1;
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
// src/translator.ts
|
|
2443
|
+
var TRANSLATOR_FLOOR = ARCHETYPE_FLOOR_DEFAULT;
|
|
2444
|
+
var RULE_SEQUENTIAL_TOOL_CLIFF = "sequential-tool-cliff-below-floor";
|
|
2445
|
+
var RULE_NARRATION_DRIFT_ANTHROPIC = "narration-drift-anthropic";
|
|
2446
|
+
var RULE_NARRATION_THINKING_LEAK_DEEPSEEK = "narration-thinking-leak-deepseek";
|
|
2447
|
+
var SEQUENTIAL_TOOL_PREAMBLE = "IMPORTANT: Use one tool call per response. Wait for the tool result before deciding the next tool. Do NOT batch tool calls in parallel.";
|
|
2448
|
+
var NARRATION_DRIFT_ANTHROPIC_PREAMBLE = "Output ONLY the requested content. Do not narrate your thought process. Each line \u2264 12 words.";
|
|
2449
|
+
var NARRATION_THINKING_LEAK_DEEPSEEK_PREAMBLE = "Reasoning is internal. Output ONLY the requested content; do not emit <thinking> blocks or internal monologue as user-facing text.";
|
|
2450
|
+
var RULE_DISCIPLINE_GATES_V1 = "discipline-gates-v1";
|
|
2451
|
+
var DISCIPLINE_GATES_V1_WITH_TOOLS = `Work through these gates at every judgment point, explicitly:
|
|
2452
|
+
1. Evidence before reasoning: cite what you observed before concluding from it.
|
|
2453
|
+
2. One extra signal: when a finding feels conclusive, check one more adjacent signal before stating it.
|
|
2454
|
+
3. Expand, don't guess: resolve a compressed or referenced item by looking it up rather than inferring its contents.
|
|
2455
|
+
4. Innocent explanation first: state the most plausible benign reading before alleging the alarming one.
|
|
2456
|
+
5. Label each claim: mark it observed, inferred, or assumed.
|
|
2457
|
+
6. A surfaced gap beats a guessed answer: flag what you cannot determine rather than fabricating past it.`;
|
|
2458
|
+
var DISCIPLINE_GATES_V1_NO_TOOLS = `Work through these gates at every judgment point, explicitly:
|
|
2459
|
+
1. Evidence before reasoning: cite what you observed before concluding from it.
|
|
2460
|
+
2. One extra signal: when a finding feels conclusive, check one more adjacent signal before stating it.
|
|
2461
|
+
3. Innocent explanation first: state the most plausible benign reading before alleging the alarming one.
|
|
2462
|
+
4. Label each claim: mark it observed, inferred, or assumed.
|
|
2463
|
+
5. A surfaced gap beats a guessed answer: flag what you cannot determine rather than fabricating past it.`;
|
|
2464
|
+
var RULE_DISCIPLINE_GATES_V1_STRUCTURED = "discipline-gates-v1-structured";
|
|
2465
|
+
var DISCIPLINE_GATES_V1_STRUCTURED_WITH_TOOLS = `Work through these gates at every judgment point, explicitly:
|
|
2466
|
+
1. Evidence before reasoning: cite what you observed before concluding from it.
|
|
2467
|
+
2. One extra signal: when a finding feels conclusive, check one more adjacent signal before stating it.
|
|
2468
|
+
3. Expand, don't guess: resolve a compressed or referenced item by looking it up rather than inferring its contents.
|
|
2469
|
+
4. Innocent explanation first: state the most plausible benign reading before alleging the alarming one.`;
|
|
2470
|
+
var DISCIPLINE_GATES_V1_STRUCTURED_NO_TOOLS = `Work through these gates at every judgment point, explicitly:
|
|
2471
|
+
1. Evidence before reasoning: cite what you observed before concluding from it.
|
|
2472
|
+
2. One extra signal: when a finding feels conclusive, check one more adjacent signal before stating it.
|
|
2473
|
+
3. Innocent explanation first: state the most plausible benign reading before alleging the alarming one.`;
|
|
2474
|
+
var DISCIPLINE_ELIGIBLE_ARCHETYPES = /* @__PURE__ */ new Set([
|
|
2475
|
+
"hunt",
|
|
2476
|
+
"summarize",
|
|
2477
|
+
"plan",
|
|
2478
|
+
"critique",
|
|
2479
|
+
"judge"
|
|
2480
|
+
]);
|
|
2481
|
+
function matchRule(kind, profile, archetype, ctx) {
|
|
2482
|
+
if (kind === "discipline_contract") {
|
|
2483
|
+
if (!DISCIPLINE_ELIGIBLE_ARCHETYPES.has(archetype)) return null;
|
|
2484
|
+
if (ctx.outputMode !== "text") {
|
|
2485
|
+
return {
|
|
2486
|
+
id: RULE_DISCIPLINE_GATES_V1_STRUCTURED,
|
|
2487
|
+
preamble: ctx.hasTools ? DISCIPLINE_GATES_V1_STRUCTURED_WITH_TOOLS : DISCIPLINE_GATES_V1_STRUCTURED_NO_TOOLS
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
return {
|
|
2491
|
+
id: RULE_DISCIPLINE_GATES_V1,
|
|
2492
|
+
preamble: ctx.hasTools ? DISCIPLINE_GATES_V1_WITH_TOOLS : DISCIPLINE_GATES_V1_NO_TOOLS
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
if (kind === "tool_call_contract") {
|
|
2496
|
+
if (!profile.archetypePerf) return null;
|
|
2497
|
+
const archetypeScore = profile.archetypePerf[archetype];
|
|
2498
|
+
if (typeof archetypeScore !== "number" || archetypeScore >= TRANSLATOR_FLOOR) {
|
|
2499
|
+
return null;
|
|
2500
|
+
}
|
|
2501
|
+
return {
|
|
2502
|
+
id: RULE_SEQUENTIAL_TOOL_CLIFF,
|
|
2503
|
+
preamble: SEQUENTIAL_TOOL_PREAMBLE,
|
|
2504
|
+
wireOverrides: { parallelToolCalls: false }
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
if (kind === "narration_contract") {
|
|
2508
|
+
if (profile.provider === "anthropic") {
|
|
2509
|
+
return {
|
|
2510
|
+
id: RULE_NARRATION_DRIFT_ANTHROPIC,
|
|
2511
|
+
preamble: NARRATION_DRIFT_ANTHROPIC_PREAMBLE
|
|
2512
|
+
};
|
|
2513
|
+
}
|
|
2514
|
+
if (profile.provider === "deepseek") {
|
|
2515
|
+
return {
|
|
2516
|
+
id: RULE_NARRATION_THINKING_LEAK_DEEPSEEK,
|
|
2517
|
+
preamble: NARRATION_THINKING_LEAK_DEEPSEEK_PREAMBLE
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
return null;
|
|
2521
|
+
}
|
|
2522
|
+
return null;
|
|
2523
|
+
}
|
|
2524
|
+
function applySectionRewrites(args) {
|
|
2525
|
+
const { ir, profile, archetype } = args;
|
|
2526
|
+
if (!Array.isArray(ir.sections) || ir.sections.length === 0) {
|
|
2527
|
+
return { rewrittenIR: ir, rewrites: [] };
|
|
2528
|
+
}
|
|
2529
|
+
const outputMode = args.outputMode ?? resolveOutputMode({
|
|
2530
|
+
declared: ir.constraints?.outputMode,
|
|
2531
|
+
structuredOutput: ir.constraints?.structuredOutput,
|
|
2532
|
+
toolCount: ir.tools?.length ?? 0
|
|
2533
|
+
});
|
|
2534
|
+
const hasTools = (ir.tools?.length ?? 0) > 0;
|
|
2535
|
+
const ctx = { outputMode, hasTools };
|
|
2536
|
+
const rewrites = [];
|
|
2537
|
+
const newSections = ir.sections.map((section) => {
|
|
2538
|
+
if (!section.kind || section.kind === "arbitrary") return section;
|
|
2539
|
+
const rule = matchRule(section.kind, profile, archetype, ctx);
|
|
2540
|
+
if (!rule) return section;
|
|
2541
|
+
const originalText = section.text;
|
|
2542
|
+
const transformedText = `${rule.preamble}
|
|
2543
|
+
|
|
2544
|
+
${originalText}`;
|
|
2545
|
+
rewrites.push({
|
|
2546
|
+
sectionId: section.id,
|
|
2547
|
+
kind: section.kind,
|
|
2548
|
+
rule: rule.id,
|
|
2549
|
+
originalText,
|
|
2550
|
+
transformedText,
|
|
2551
|
+
...rule.wireOverrides ? { wireOverrides: rule.wireOverrides } : {}
|
|
2552
|
+
});
|
|
2553
|
+
return { ...section, text: transformedText };
|
|
2554
|
+
});
|
|
2555
|
+
if (rewrites.length === 0) {
|
|
2556
|
+
return { rewrittenIR: ir, rewrites: [] };
|
|
2557
|
+
}
|
|
2558
|
+
const rewrittenIR = { ...ir, sections: newSections };
|
|
2559
|
+
return { rewrittenIR, rewrites };
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
// src/lower.ts
|
|
2563
|
+
var OPENROUTER_VENDOR_SLUG = {
|
|
2564
|
+
anthropic: "anthropic",
|
|
2565
|
+
google: "google",
|
|
2566
|
+
openai: "openai",
|
|
2567
|
+
deepseek: "deepseek",
|
|
2568
|
+
zai: "z-ai",
|
|
2569
|
+
moonshot: "moonshotai"
|
|
2570
|
+
};
|
|
2571
|
+
var OPENROUTER_ID_OVERRIDES = Object.freeze({});
|
|
2572
|
+
function openRouterModelId(profile) {
|
|
2573
|
+
const override = OPENROUTER_ID_OVERRIDES[profile.id];
|
|
2574
|
+
if (override) return override;
|
|
2575
|
+
const slug = OPENROUTER_VENDOR_SLUG[profile.provider];
|
|
2576
|
+
if (!slug) throw new Error(`No OpenRouter vendor slug for provider ${profile.provider} (model ${profile.id})`);
|
|
2577
|
+
return `${slug}/${profile.id}`;
|
|
2578
|
+
}
|
|
2579
|
+
function lower(ir, profile, hints = {}) {
|
|
2580
|
+
if (hints.route === "openrouter") {
|
|
2581
|
+
return lowerOpenRouter(ir, profile);
|
|
2582
|
+
}
|
|
2583
|
+
switch (profile.provider) {
|
|
2584
|
+
case "anthropic":
|
|
2585
|
+
return lowerAnthropic(ir, profile, hints);
|
|
2586
|
+
case "google":
|
|
2587
|
+
return lowerGoogle(ir, profile, hints);
|
|
2588
|
+
case "openai":
|
|
2589
|
+
return lowerOpenAI(ir, profile, hints);
|
|
2590
|
+
case "deepseek":
|
|
2591
|
+
return lowerDeepSeek(ir, profile);
|
|
2592
|
+
case "zai":
|
|
2593
|
+
return lowerZai(ir, profile, hints);
|
|
2594
|
+
case "moonshot":
|
|
2595
|
+
return lowerMoonshot(ir, profile);
|
|
2596
|
+
default:
|
|
2597
|
+
throw new Error(`No lowering implementation for provider: ${profile.provider}`);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
function lowerAnthropic(ir, profile, hints) {
|
|
2601
|
+
const systemBlocks = buildAnthropicSystemBlocks(ir.sections, profile);
|
|
2602
|
+
if (ir.constraints?.structuredOutput) {
|
|
2603
|
+
systemBlocks.push({
|
|
2604
|
+
type: "text",
|
|
2605
|
+
text: "Respond with a single valid JSON value and nothing else. Do not wrap it in a markdown code fence, and do not add any prose before or after it."
|
|
2606
|
+
});
|
|
2607
|
+
}
|
|
2608
|
+
const history = (ir.history ?? []).filter((m) => m.role !== "system");
|
|
2609
|
+
const policy = ir.historyCachePolicy;
|
|
2610
|
+
const markIndex = resolveHistoryMarkIndex(history.length, policy);
|
|
2611
|
+
const messages = buildAnthropicMessages(history, ir.currentTurn, markIndex);
|
|
2612
|
+
const tools = ir.tools ? toAnthropicTools(ir.tools) : void 0;
|
|
2613
|
+
const cacheableTokens = computeCacheableTokens(systemBlocks);
|
|
2614
|
+
const historyCacheableTokens = markIndex >= 0 ? sumHistoryTokens(history, markIndex) : 0;
|
|
2615
|
+
const totalCacheableTokens = cacheableTokens + historyCacheableTokens;
|
|
2616
|
+
const cacheSavings = totalCacheableTokens / 1e6 * profile.costInputPer1m * (1 - (profile.lowering.cache.discount ?? 0.1));
|
|
2617
|
+
const toolChoice = hints.wireOverrides?.parallelToolCalls === false && tools && tools.length > 0 ? { type: "auto", disable_parallel_tool_use: true } : void 0;
|
|
2618
|
+
return {
|
|
2619
|
+
request: {
|
|
2620
|
+
provider: "anthropic",
|
|
2621
|
+
model: profile.id,
|
|
2622
|
+
system: systemBlocks,
|
|
2623
|
+
messages,
|
|
2624
|
+
tools,
|
|
2625
|
+
// alpha.8: trust profile.maxOutputTokens. The historical Math.min(_, 4096)
|
|
2626
|
+
// floor surprised every consumer once (PB-Cairn contract-gaps brief, Gap 3).
|
|
2627
|
+
// Profile is the single source of truth; consumers wanting a tighter
|
|
2628
|
+
// budget can pass providerOverrides.anthropic.max_tokens explicitly.
|
|
2629
|
+
max_tokens: hints.forceTerseOutput ? 200 : profile.maxOutputTokens,
|
|
2630
|
+
tool_choice: toolChoice
|
|
2631
|
+
},
|
|
2632
|
+
diagnostics: {
|
|
2633
|
+
cacheableTokens,
|
|
2634
|
+
historyCacheableTokens,
|
|
2635
|
+
estimatedCacheSavingsUsd: cacheSavings
|
|
2636
|
+
}
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
function buildAnthropicSystemBlocks(sections, profile) {
|
|
2640
|
+
if (sections.length === 0) return [];
|
|
2641
|
+
const ordered = sortSections(sections);
|
|
2642
|
+
const minTokens = profile.lowering.cache.minTokens ?? 1024;
|
|
2643
|
+
const cacheable = [];
|
|
2644
|
+
const dynamic = [];
|
|
2645
|
+
for (const s of ordered) {
|
|
2646
|
+
if (s.cacheable) cacheable.push(s);
|
|
2647
|
+
else dynamic.push(s);
|
|
2648
|
+
}
|
|
2649
|
+
const blocks = [];
|
|
2650
|
+
if (cacheable.length > 0) {
|
|
2651
|
+
const cacheText = cacheable.map((s) => s.text).join("\n\n");
|
|
2652
|
+
const cacheTextTokens = countTokens(cacheText);
|
|
2653
|
+
const block = {
|
|
2654
|
+
type: "text",
|
|
2655
|
+
text: cacheText
|
|
2656
|
+
};
|
|
2657
|
+
if (cacheTextTokens >= minTokens) {
|
|
2658
|
+
block.cache_control = { type: "ephemeral" };
|
|
2659
|
+
}
|
|
2660
|
+
blocks.push(block);
|
|
2661
|
+
}
|
|
2662
|
+
for (const s of dynamic) {
|
|
2663
|
+
blocks.push({ type: "text", text: s.text });
|
|
2664
|
+
}
|
|
2665
|
+
return blocks;
|
|
2666
|
+
}
|
|
2667
|
+
function buildAnthropicMessages(history, currentTurn, markIndex) {
|
|
2668
|
+
const out = [];
|
|
2669
|
+
for (let i = 0; i < history.length; i++) {
|
|
2670
|
+
const m = history[i];
|
|
2671
|
+
if (m.role === "system") continue;
|
|
2672
|
+
const shouldMark = i === markIndex;
|
|
2673
|
+
out.push({
|
|
2674
|
+
role: m.role,
|
|
2675
|
+
content: shouldMark ? attachAnthropicCacheControl(m) : m.parts ?? m.content
|
|
2676
|
+
});
|
|
2677
|
+
}
|
|
2678
|
+
if (currentTurn && currentTurn.role !== "system") {
|
|
2679
|
+
out.push({ role: currentTurn.role, content: currentTurn.parts ?? currentTurn.content });
|
|
2680
|
+
}
|
|
2681
|
+
return out;
|
|
2682
|
+
}
|
|
2683
|
+
function attachAnthropicCacheControl(m) {
|
|
2684
|
+
if (Array.isArray(m.parts) && m.parts.length > 0) {
|
|
2685
|
+
const blocks = m.parts;
|
|
2686
|
+
const last = blocks[blocks.length - 1];
|
|
2687
|
+
const withMarker = {
|
|
2688
|
+
...last,
|
|
2689
|
+
cache_control: { type: "ephemeral" }
|
|
2690
|
+
};
|
|
2691
|
+
return [...blocks.slice(0, -1), withMarker];
|
|
2692
|
+
}
|
|
2693
|
+
return [
|
|
2694
|
+
{
|
|
2695
|
+
type: "text",
|
|
2696
|
+
text: m.content,
|
|
2697
|
+
cache_control: { type: "ephemeral" }
|
|
2698
|
+
}
|
|
2699
|
+
];
|
|
2700
|
+
}
|
|
2701
|
+
function resolveHistoryMarkIndex(historyLen, policy) {
|
|
2702
|
+
if (!policy || policy.strategy === "none") return -1;
|
|
2703
|
+
if (historyLen === 0) return -1;
|
|
2704
|
+
if (policy.strategy === "all-but-latest") {
|
|
2705
|
+
return historyLen - 1;
|
|
2706
|
+
}
|
|
2707
|
+
const idx = historyLen - 1 - policy.suffix;
|
|
2708
|
+
return idx >= 0 ? idx : -1;
|
|
2709
|
+
}
|
|
2710
|
+
function sumHistoryTokens(history, throughIndex) {
|
|
2711
|
+
let total = 0;
|
|
2712
|
+
for (let i = 0; i <= throughIndex && i < history.length; i++) {
|
|
2713
|
+
const m = history[i];
|
|
2714
|
+
if (m.role === "system") continue;
|
|
2715
|
+
if (Array.isArray(m.parts)) {
|
|
2716
|
+
for (const p of m.parts) {
|
|
2717
|
+
if (typeof p.text === "string") total += countTokens(p.text);
|
|
2718
|
+
}
|
|
2719
|
+
} else if (typeof m.content === "string") {
|
|
2720
|
+
total += countTokens(m.content);
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
return total;
|
|
2724
|
+
}
|
|
2725
|
+
function toAnthropicTools(tools) {
|
|
2726
|
+
return tools.map((t) => ({
|
|
2727
|
+
name: t.name,
|
|
2728
|
+
description: t.description ?? "",
|
|
2729
|
+
input_schema: t.parameters ?? { type: "object", properties: {} }
|
|
2730
|
+
}));
|
|
2731
|
+
}
|
|
2732
|
+
function computeCacheableTokens(blocks) {
|
|
2733
|
+
let total = 0;
|
|
2734
|
+
for (const b of blocks) {
|
|
2735
|
+
if (b.cache_control) total += countTokens(b.text);
|
|
2736
|
+
}
|
|
2737
|
+
return total;
|
|
2738
|
+
}
|
|
2739
|
+
function lowerGoogle(ir, profile, hints) {
|
|
2740
|
+
const ordered = sortSections(ir.sections);
|
|
2741
|
+
const systemText = ordered.map((s) => s.text).join("\n\n");
|
|
2742
|
+
const generationConfig = {};
|
|
2743
|
+
if (hints.forceThinkingZero && profile.lowering.thinking) {
|
|
2744
|
+
setNestedField(generationConfig, profile.lowering.thinking.field.replace(/^generationConfig\./, ""), 0);
|
|
2745
|
+
}
|
|
2746
|
+
if (hints.forceTerseOutput) {
|
|
2747
|
+
generationConfig.maxOutputTokens = 200;
|
|
2748
|
+
}
|
|
2749
|
+
if (ir.constraints?.structuredOutput && profile.structuredOutput === "native") {
|
|
2750
|
+
generationConfig.responseMimeType = "application/json";
|
|
2751
|
+
}
|
|
2752
|
+
const contents = buildGoogleContents(ir.history ?? [], ir.currentTurn);
|
|
2753
|
+
const tools = ir.tools && ir.tools.length > 0 ? toGoogleTools(ir.tools) : void 0;
|
|
2754
|
+
const cacheable = ordered.filter((s) => s.cacheable);
|
|
2755
|
+
const cacheableTokens = cacheable.reduce((sum, s) => sum + countTokens(s.text), 0);
|
|
2756
|
+
const minTokens = profile.lowering.cache.minTokens ?? 4096;
|
|
2757
|
+
const meetsMin = cacheableTokens >= minTokens;
|
|
2758
|
+
const cacheSavings = meetsMin ? cacheableTokens / 1e6 * profile.costInputPer1m * (1 - (profile.lowering.cache.discount ?? 0.25)) : 0;
|
|
2759
|
+
const history = (ir.history ?? []).filter((m) => m.role !== "system");
|
|
2760
|
+
const histMarkIndex = resolveHistoryMarkIndex(history.length, ir.historyCachePolicy);
|
|
2761
|
+
const historyCacheableTokens = histMarkIndex >= 0 ? sumHistoryTokens(history, histMarkIndex) : 0;
|
|
2762
|
+
return {
|
|
2763
|
+
request: {
|
|
2764
|
+
provider: "google",
|
|
2765
|
+
model: profile.id,
|
|
2766
|
+
systemInstruction: systemText ? { role: "system", parts: [{ text: systemText }] } : void 0,
|
|
2767
|
+
contents,
|
|
2768
|
+
tools,
|
|
2769
|
+
generationConfig: Object.keys(generationConfig).length > 0 ? generationConfig : void 0
|
|
2770
|
+
},
|
|
2771
|
+
diagnostics: {
|
|
2772
|
+
cacheableTokens: meetsMin ? cacheableTokens : 0,
|
|
2773
|
+
historyCacheableTokens,
|
|
2774
|
+
estimatedCacheSavingsUsd: cacheSavings
|
|
2775
|
+
}
|
|
2776
|
+
};
|
|
2777
|
+
}
|
|
2778
|
+
function buildGoogleContents(history, currentTurn) {
|
|
2779
|
+
const out = [];
|
|
2780
|
+
for (const m of history) {
|
|
2781
|
+
if (m.role === "system") continue;
|
|
2782
|
+
out.push({
|
|
2783
|
+
role: m.role === "assistant" ? "model" : m.role,
|
|
2784
|
+
parts: m.parts ?? [{ text: m.content }]
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2787
|
+
if (currentTurn && currentTurn.role !== "system") {
|
|
2788
|
+
out.push({
|
|
2789
|
+
role: currentTurn.role === "assistant" ? "model" : currentTurn.role,
|
|
2790
|
+
parts: currentTurn.parts ?? [{ text: currentTurn.content }]
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
return out;
|
|
2794
|
+
}
|
|
2795
|
+
function toGoogleTools(tools) {
|
|
2796
|
+
return [
|
|
2797
|
+
{
|
|
2798
|
+
functionDeclarations: tools.map((t) => ({
|
|
2799
|
+
name: t.name,
|
|
2800
|
+
description: t.description ?? "",
|
|
2801
|
+
parameters: t.parameters ?? { type: "object", properties: {} }
|
|
2802
|
+
}))
|
|
2803
|
+
}
|
|
2804
|
+
];
|
|
2805
|
+
}
|
|
2806
|
+
function lowerOpenAI(ir, profile, hints) {
|
|
2807
|
+
const ordered = sortSections(ir.sections);
|
|
2808
|
+
const systemText = ordered.map((s) => s.text).join("\n\n");
|
|
2809
|
+
const systemRole = profile.systemPromptMode === "as_developer" ? "developer" : "system";
|
|
2810
|
+
const messages = systemText ? [{ role: systemRole, content: systemText }] : [];
|
|
2811
|
+
for (const m of ir.history ?? []) {
|
|
2812
|
+
if (m.role === "system") continue;
|
|
2813
|
+
messages.push({ role: m.role, content: m.parts ?? m.content });
|
|
2814
|
+
}
|
|
2815
|
+
if (ir.currentTurn && ir.currentTurn.role !== "system") {
|
|
2816
|
+
messages.push({
|
|
2817
|
+
role: ir.currentTurn.role,
|
|
2818
|
+
content: ir.currentTurn.parts ?? ir.currentTurn.content
|
|
2819
|
+
});
|
|
2820
|
+
}
|
|
2821
|
+
const history = (ir.history ?? []).filter((m) => m.role !== "system");
|
|
2822
|
+
const histMarkIndex = resolveHistoryMarkIndex(history.length, ir.historyCachePolicy);
|
|
2823
|
+
const historyCacheableTokens = histMarkIndex >= 0 ? sumHistoryTokens(history, histMarkIndex) : 0;
|
|
2824
|
+
const openaiParallelToolCalls = hints.wireOverrides?.parallelToolCalls === false && ir.tools && ir.tools.length > 0 ? false : void 0;
|
|
2825
|
+
return {
|
|
2826
|
+
request: {
|
|
2827
|
+
provider: "openai",
|
|
2828
|
+
model: profile.id,
|
|
2829
|
+
messages,
|
|
2830
|
+
tools: ir.tools && ir.tools.length > 0 ? toOpenAITools(ir.tools) : void 0,
|
|
2831
|
+
response_format: ir.constraints?.structuredOutput ? { type: "json_object" } : void 0,
|
|
2832
|
+
reasoning_effort: hints.forceTerseOutput ? "low" : void 0,
|
|
2833
|
+
parallel_tool_calls: openaiParallelToolCalls
|
|
2834
|
+
},
|
|
2835
|
+
diagnostics: {
|
|
2836
|
+
cacheableTokens: 0,
|
|
2837
|
+
historyCacheableTokens,
|
|
2838
|
+
estimatedCacheSavingsUsd: 0
|
|
2839
|
+
}
|
|
2840
|
+
};
|
|
2841
|
+
}
|
|
2842
|
+
function toOpenAITools(tools) {
|
|
2843
|
+
return tools.map((t) => ({
|
|
2844
|
+
type: "function",
|
|
2845
|
+
function: {
|
|
2846
|
+
name: t.name,
|
|
2847
|
+
description: t.description ?? "",
|
|
2848
|
+
parameters: t.parameters ?? { type: "object", properties: {} }
|
|
2849
|
+
}
|
|
2850
|
+
}));
|
|
2851
|
+
}
|
|
2852
|
+
function lowerDeepSeek(ir, profile) {
|
|
2853
|
+
const ordered = sortSections(ir.sections);
|
|
2854
|
+
const systemText = ordered.map((s) => s.text).join("\n\n");
|
|
2855
|
+
const messages = systemText ? [{ role: "system", content: systemText }] : [];
|
|
2856
|
+
for (const m of ir.history ?? []) {
|
|
2857
|
+
if (m.role === "system") continue;
|
|
2858
|
+
messages.push({ role: m.role, content: m.parts ?? m.content });
|
|
2859
|
+
}
|
|
2860
|
+
if (ir.currentTurn && ir.currentTurn.role !== "system") {
|
|
2861
|
+
messages.push({
|
|
2862
|
+
role: ir.currentTurn.role,
|
|
2863
|
+
content: ir.currentTurn.parts ?? ir.currentTurn.content
|
|
2864
|
+
});
|
|
2865
|
+
}
|
|
2866
|
+
const history = (ir.history ?? []).filter((m) => m.role !== "system");
|
|
2867
|
+
const histMarkIndex = resolveHistoryMarkIndex(history.length, ir.historyCachePolicy);
|
|
2868
|
+
const historyCacheableTokens = histMarkIndex >= 0 ? sumHistoryTokens(history, histMarkIndex) : 0;
|
|
2869
|
+
return {
|
|
2870
|
+
request: {
|
|
2871
|
+
provider: "deepseek",
|
|
2872
|
+
model: profile.id,
|
|
2873
|
+
messages,
|
|
2874
|
+
tools: ir.tools && ir.tools.length > 0 ? ir.tools.slice(0, 1).map((t) => ({
|
|
2875
|
+
type: "function",
|
|
2876
|
+
function: {
|
|
2877
|
+
name: t.name,
|
|
2878
|
+
description: t.description ?? "",
|
|
2879
|
+
parameters: t.parameters ?? { type: "object", properties: {} }
|
|
2880
|
+
}
|
|
2881
|
+
})) : void 0
|
|
2882
|
+
},
|
|
2883
|
+
diagnostics: {
|
|
2884
|
+
cacheableTokens: 0,
|
|
2885
|
+
historyCacheableTokens,
|
|
2886
|
+
estimatedCacheSavingsUsd: 0
|
|
2887
|
+
}
|
|
2888
|
+
};
|
|
2889
|
+
}
|
|
2890
|
+
function buildOpenAICompatibleParts(ir) {
|
|
2891
|
+
const ordered = sortSections(ir.sections);
|
|
2892
|
+
const systemText = ordered.map((s) => s.text).join("\n\n");
|
|
2893
|
+
const messages = systemText ? [{ role: "system", content: systemText }] : [];
|
|
2894
|
+
for (const m of ir.history ?? []) {
|
|
2895
|
+
if (m.role === "system") continue;
|
|
2896
|
+
messages.push({ role: m.role, content: m.parts ?? m.content });
|
|
2897
|
+
}
|
|
2898
|
+
if (ir.currentTurn && ir.currentTurn.role !== "system") {
|
|
2899
|
+
messages.push({
|
|
2900
|
+
role: ir.currentTurn.role,
|
|
2901
|
+
content: ir.currentTurn.parts ?? ir.currentTurn.content
|
|
2902
|
+
});
|
|
2903
|
+
}
|
|
2904
|
+
const history = (ir.history ?? []).filter((m) => m.role !== "system");
|
|
2905
|
+
const histMarkIndex = resolveHistoryMarkIndex(history.length, ir.historyCachePolicy);
|
|
2906
|
+
const historyCacheableTokens = histMarkIndex >= 0 ? sumHistoryTokens(history, histMarkIndex) : 0;
|
|
2907
|
+
return {
|
|
2908
|
+
messages,
|
|
2909
|
+
tools: ir.tools && ir.tools.length > 0 ? toOpenAITools(ir.tools) : void 0,
|
|
2910
|
+
response_format: ir.constraints?.structuredOutput ? { type: "json_object" } : void 0,
|
|
2911
|
+
historyCacheableTokens
|
|
2912
|
+
};
|
|
2913
|
+
}
|
|
2914
|
+
function lowerZai(ir, profile, hints) {
|
|
2915
|
+
const parts = buildOpenAICompatibleParts(ir);
|
|
2916
|
+
return {
|
|
2917
|
+
request: {
|
|
2918
|
+
provider: "zai",
|
|
2919
|
+
model: profile.id,
|
|
2920
|
+
messages: parts.messages,
|
|
2921
|
+
tools: parts.tools,
|
|
2922
|
+
response_format: parts.response_format,
|
|
2923
|
+
// Z.ai thinking defaults to enabled server-side; emit an explicit
|
|
2924
|
+
// disable only when a cliff forced thinking off (the GLM analogue of
|
|
2925
|
+
// Gemini's thinkingBudget=0).
|
|
2926
|
+
thinking: hints.forceThinkingZero ? { type: "disabled" } : void 0
|
|
2927
|
+
},
|
|
2928
|
+
diagnostics: {
|
|
2929
|
+
cacheableTokens: 0,
|
|
2930
|
+
historyCacheableTokens: parts.historyCacheableTokens,
|
|
2931
|
+
estimatedCacheSavingsUsd: 0
|
|
2932
|
+
}
|
|
2933
|
+
};
|
|
2934
|
+
}
|
|
2935
|
+
function lowerOpenRouter(ir, profile) {
|
|
2936
|
+
const parts = buildOpenAICompatibleParts(ir);
|
|
2937
|
+
return {
|
|
2938
|
+
request: {
|
|
2939
|
+
provider: "openrouter",
|
|
2940
|
+
model: openRouterModelId(profile),
|
|
2941
|
+
messages: parts.messages,
|
|
2942
|
+
tools: parts.tools,
|
|
2943
|
+
response_format: parts.response_format
|
|
2944
|
+
},
|
|
2945
|
+
diagnostics: {
|
|
2946
|
+
cacheableTokens: 0,
|
|
2947
|
+
historyCacheableTokens: parts.historyCacheableTokens,
|
|
2948
|
+
estimatedCacheSavingsUsd: 0
|
|
2949
|
+
}
|
|
2950
|
+
};
|
|
2951
|
+
}
|
|
2952
|
+
function lowerMoonshot(ir, profile) {
|
|
2953
|
+
const parts = buildOpenAICompatibleParts(ir);
|
|
2954
|
+
return {
|
|
2955
|
+
request: {
|
|
2956
|
+
provider: "moonshot",
|
|
2957
|
+
model: profile.id,
|
|
2958
|
+
messages: parts.messages,
|
|
2959
|
+
tools: parts.tools,
|
|
2960
|
+
response_format: parts.response_format
|
|
2961
|
+
},
|
|
2962
|
+
diagnostics: {
|
|
2963
|
+
cacheableTokens: 0,
|
|
2964
|
+
historyCacheableTokens: parts.historyCacheableTokens,
|
|
2965
|
+
estimatedCacheSavingsUsd: 0
|
|
2966
|
+
}
|
|
2967
|
+
};
|
|
2968
|
+
}
|
|
2969
|
+
function sortSections(sections) {
|
|
2970
|
+
return [...sections].sort((a, b) => {
|
|
2971
|
+
const wa = a.weight ?? 100;
|
|
2972
|
+
const wb = b.weight ?? 100;
|
|
2973
|
+
return wa - wb;
|
|
2974
|
+
});
|
|
2975
|
+
}
|
|
2976
|
+
function setNestedField(obj, path, value) {
|
|
2977
|
+
const parts = path.split(".");
|
|
2978
|
+
let cursor = obj;
|
|
2979
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
2980
|
+
const key = parts[i];
|
|
2981
|
+
if (!(key in cursor) || typeof cursor[key] !== "object" || cursor[key] === null) {
|
|
2982
|
+
cursor[key] = {};
|
|
2983
|
+
}
|
|
2984
|
+
cursor = cursor[key];
|
|
2985
|
+
}
|
|
2986
|
+
cursor[parts[parts.length - 1]] = value;
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
// src/compile.ts
|
|
2990
|
+
var counter = 0;
|
|
2991
|
+
function makeHandle() {
|
|
2992
|
+
counter = (counter + 1) % 1e6;
|
|
2993
|
+
return `c${Date.now().toString(36)}-${counter.toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
2994
|
+
}
|
|
2995
|
+
function compile(ir, opts = {}) {
|
|
2996
|
+
const baseResolver = opts.profileResolver ?? getProfile;
|
|
2997
|
+
const resolver = opts.policy?.onUnprofiledModel === "best-effort" ? (id) => {
|
|
2998
|
+
try {
|
|
2999
|
+
return baseResolver(id);
|
|
3000
|
+
} catch (err) {
|
|
3001
|
+
const synth = bestEffortProfile(id);
|
|
3002
|
+
if (!synth) throw err;
|
|
3003
|
+
return synth;
|
|
3004
|
+
}
|
|
3005
|
+
} : baseResolver;
|
|
3006
|
+
validateIR(ir);
|
|
3007
|
+
ir = resolveModelEntries(ir);
|
|
3008
|
+
const sliced = passSlice(ir);
|
|
3009
|
+
const deduped = passDedupe(sliced.value);
|
|
3010
|
+
const toolFiltered = passToolRelevance(deduped.value, {
|
|
3011
|
+
threshold: opts.toolRelevanceThreshold
|
|
3012
|
+
});
|
|
3013
|
+
const compressed = passCompressHistory(toolFiltered.value, {
|
|
3014
|
+
summarizeOlderThan: opts.compressHistoryAfter,
|
|
3015
|
+
summarizeAboveTokens: opts.compressHistoryAboveTokens
|
|
3016
|
+
});
|
|
3017
|
+
let workingIR = compressed.value;
|
|
3018
|
+
const accumulatedMutations = [
|
|
3019
|
+
...sliced.mutations,
|
|
3020
|
+
...deduped.mutations,
|
|
3021
|
+
...toolFiltered.mutations,
|
|
3022
|
+
...compressed.mutations
|
|
3023
|
+
];
|
|
3024
|
+
const activePromotion = getApplicablePromotion({
|
|
3025
|
+
appId: ir.appId,
|
|
3026
|
+
archetype: ir.intent.archetype,
|
|
3027
|
+
mode: "downswap"
|
|
3028
|
+
});
|
|
3029
|
+
const promotion = activePromotion ? {
|
|
3030
|
+
id: activePromotion.id,
|
|
3031
|
+
mode: activePromotion.mode,
|
|
3032
|
+
promotedModel: activePromotion.promotedModel,
|
|
3033
|
+
evalRunId: activePromotion.evalRunId,
|
|
3034
|
+
suppressQualityGate: activePromotion.suppressQualityGate
|
|
3035
|
+
} : void 0;
|
|
3036
|
+
const measuredFailureGates = /* @__PURE__ */ new Map();
|
|
3037
|
+
for (const entry of workingIR.models ?? []) {
|
|
3038
|
+
const modelId = typeof entry === "string" ? entry : void 0;
|
|
3039
|
+
if (!modelId) continue;
|
|
3040
|
+
const verdict = getMeasuredFailureVerdict({
|
|
3041
|
+
appId: ir.appId,
|
|
3042
|
+
archetype: ir.intent.archetype,
|
|
3043
|
+
model: modelId
|
|
3044
|
+
});
|
|
3045
|
+
if (verdict?.gated) {
|
|
3046
|
+
measuredFailureGates.set(modelId, {
|
|
3047
|
+
rate: verdict.rate,
|
|
3048
|
+
lowerBound: verdict.lowerBound,
|
|
3049
|
+
n: verdict.n,
|
|
3050
|
+
nFail: verdict.nFail
|
|
3051
|
+
});
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
const inputTokens = estimateInputTokens(workingIR);
|
|
3055
|
+
const scores = passScoreTargets(workingIR, {
|
|
3056
|
+
estimatedInputTokens: inputTokens,
|
|
3057
|
+
profilesById: resolver,
|
|
3058
|
+
policy: opts.policy,
|
|
3059
|
+
promotion,
|
|
3060
|
+
measuredFailureGates
|
|
3061
|
+
});
|
|
3062
|
+
accumulatedMutations.push(...scores.mutations);
|
|
3063
|
+
const target = pickTarget(workingIR, scores.value);
|
|
3064
|
+
if (!target) {
|
|
3065
|
+
const unknownIds = scores.value.filter((s) => s.rejectReasons?.includes("unknown_model_id")).map((s) => s.modelId);
|
|
3066
|
+
const uninferable = unknownIds.filter((id) => !inferProviderFromId(id));
|
|
3067
|
+
const unknownHint = unknownIds.length > 0 ? ` Unprofiled model id(s): ${unknownIds.join(", ")}. registerProfiles([...]) supplies a full profile; policy.onUnprofiledModel:'best-effort' compiles with guards skipped.` + (uninferable.length > 0 ? ` Note: provider is not inferable from ${uninferable.join(", ")}, so best-effort cannot lower it \u2014 registerProfiles is the only path for those.` : "") : "";
|
|
3068
|
+
throw new Error(
|
|
3069
|
+
`compile(): no allowed model fits the request.${unknownHint} Scores: ${JSON.stringify(scores.value, null, 2)}`
|
|
3070
|
+
);
|
|
3071
|
+
}
|
|
3072
|
+
const profile = resolver(target.modelId);
|
|
3073
|
+
const bestEffortWarnings = [];
|
|
3074
|
+
if (profile.bestEffort) {
|
|
3075
|
+
bestEffortWarnings.push(
|
|
3076
|
+
`best-effort profile in use for "${profile.id}" (provider ${profile.provider} inferred from the id; wire mechanics borrowed). SKIPPED for this model: cliff guards, recovery rules, measured archetype knowledge, structured-output capability (treated as 'none'), parallel tools (treated as sequential). Cost is UNKNOWN and recorded as $0 \u2014 cost gates and cost attribution are meaningless for this model. registerProfiles() restores real guards.`
|
|
3077
|
+
);
|
|
3078
|
+
accumulatedMutations.push({
|
|
3079
|
+
id: `best_effort_profile_${profile.id}`,
|
|
3080
|
+
source: "best_effort",
|
|
3081
|
+
passName: "resolve-profile",
|
|
3082
|
+
description: `Unprofiled model "${profile.id}" compiled under onUnprofiledModel:'best-effort' \u2014 guards skipped, cost unknown. See diagnostics.cliffWarnings for the full list.`
|
|
3083
|
+
});
|
|
3084
|
+
}
|
|
3085
|
+
const fallbackChain = scores.value.filter((s) => s.modelId !== target.modelId && s.fits).sort((a, b) => b.rank - a.rank).map((s) => s.modelId);
|
|
3086
|
+
const cliffs = passApplyCliffs(workingIR, profile, inputTokens);
|
|
3087
|
+
workingIR = cliffs.value.ir;
|
|
3088
|
+
accumulatedMutations.push(...cliffs.mutations);
|
|
3089
|
+
const conventions = passApplyConventions(workingIR, profile);
|
|
3090
|
+
workingIR = conventions.value.ir;
|
|
3091
|
+
accumulatedMutations.push(...conventions.mutations);
|
|
3092
|
+
const outputMode = resolveOutputMode({
|
|
3093
|
+
declared: ir.constraints?.outputMode,
|
|
3094
|
+
structuredOutput: ir.constraints?.structuredOutput,
|
|
3095
|
+
toolCount: ir.tools?.length ?? 0
|
|
3096
|
+
});
|
|
3097
|
+
const strategyPromotion = getApplicablePromotion({
|
|
3098
|
+
appId: ir.appId,
|
|
3099
|
+
archetype: ir.intent.archetype,
|
|
3100
|
+
mode: "strategy"
|
|
3101
|
+
});
|
|
3102
|
+
if (strategyPromotion?.strategy === "discipline-gates-v1" && !(workingIR.sections ?? []).some((s) => s.kind === "discipline_contract")) {
|
|
3103
|
+
workingIR = {
|
|
3104
|
+
...workingIR,
|
|
3105
|
+
sections: [
|
|
3106
|
+
...workingIR.sections ?? [],
|
|
3107
|
+
{
|
|
3108
|
+
id: `__kgauto_strategy_promotion_${strategyPromotion.id}__`,
|
|
3109
|
+
kind: "discipline_contract",
|
|
3110
|
+
text: ""
|
|
3111
|
+
}
|
|
3112
|
+
]
|
|
3113
|
+
};
|
|
3114
|
+
}
|
|
3115
|
+
const translated = applySectionRewrites({
|
|
3116
|
+
ir: workingIR,
|
|
3117
|
+
profile,
|
|
3118
|
+
archetype: ir.intent.archetype,
|
|
3119
|
+
outputMode
|
|
3120
|
+
});
|
|
3121
|
+
workingIR = translated.rewrittenIR;
|
|
3122
|
+
const sectionRewritesApplied = translated.rewrites;
|
|
3123
|
+
if (strategyPromotion && translated.rewrites.some(
|
|
3124
|
+
(rw) => rw.kind === "discipline_contract" && rw.sectionId === `__kgauto_strategy_promotion_${strategyPromotion.id}__`
|
|
3125
|
+
)) {
|
|
3126
|
+
accumulatedMutations.push({
|
|
3127
|
+
id: `strategy-promotion-applied-${strategyPromotion.id}`,
|
|
3128
|
+
source: "strategy_promotion",
|
|
3129
|
+
passName: "translator",
|
|
3130
|
+
description: `Strategy promotion #${strategyPromotion.id} (${strategyPromotion.strategy}) enabled the discipline gates on ${ir.appId}/${ir.intent.archetype} \u2014 measured gates-on verdict behind the 7-day rollback guard.`
|
|
3131
|
+
});
|
|
3132
|
+
}
|
|
3133
|
+
const disciplineRewrite = sectionRewritesApplied.find(
|
|
3134
|
+
(rw) => rw.kind === "discipline_contract"
|
|
3135
|
+
);
|
|
3136
|
+
const disciplineGateTokens = disciplineRewrite ? countTokens(
|
|
3137
|
+
disciplineRewrite.transformedText.slice(
|
|
3138
|
+
0,
|
|
3139
|
+
disciplineRewrite.transformedText.length - disciplineRewrite.originalText.length
|
|
3140
|
+
)
|
|
3141
|
+
) : 0;
|
|
3142
|
+
let wireOverrides;
|
|
3143
|
+
for (const rw of sectionRewritesApplied) {
|
|
3144
|
+
if (!rw.wireOverrides) continue;
|
|
3145
|
+
if (!wireOverrides) wireOverrides = {};
|
|
3146
|
+
if (rw.wireOverrides.parallelToolCalls !== void 0) {
|
|
3147
|
+
wireOverrides.parallelToolCalls = rw.wireOverrides.parallelToolCalls;
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
for (const rw of sectionRewritesApplied) {
|
|
3151
|
+
accumulatedMutations.push({
|
|
3152
|
+
id: `translator:${rw.rule}:${rw.sectionId}`,
|
|
3153
|
+
source: "translator",
|
|
3154
|
+
passName: "translator",
|
|
3155
|
+
description: `Rewrote section "${rw.sectionId}" (kind=${rw.kind}) via rule "${rw.rule}".`
|
|
3156
|
+
});
|
|
3157
|
+
}
|
|
3158
|
+
const lowered = lower(workingIR, profile, {
|
|
3159
|
+
forceThinkingZero: cliffs.value.loweringHints.forceThinkingZero,
|
|
3160
|
+
forceTerseOutput: cliffs.value.loweringHints.forceTerseOutput,
|
|
3161
|
+
wireOverrides,
|
|
3162
|
+
route: opts.route
|
|
3163
|
+
});
|
|
3164
|
+
validateFinalFit(workingIR, profile, inputTokens);
|
|
3165
|
+
const handle = makeHandle();
|
|
3166
|
+
const finalShape = computeShape(workingIR, inputTokens);
|
|
3167
|
+
const _learningKey = learningKey(ir.intent.archetype, routeScopedModel(profile.id, opts.route), finalShape);
|
|
3168
|
+
const historyCacheMarkIndex = computeHistoryCacheMarkIndex(workingIR);
|
|
3169
|
+
const systemMessages = buildSystemMessages(workingIR, profile.provider);
|
|
3170
|
+
const systemCacheMarkIndex = lastCacheableSystemIndex(systemMessages);
|
|
3171
|
+
const keptSectionIds = new Set(workingIR.sections.map((s) => s.id));
|
|
3172
|
+
const cachePrefixActive = lowered.diagnostics.cacheableTokens > 0;
|
|
3173
|
+
const cacheReadFraction = profile.lowering.cache.discount ?? 0.25;
|
|
3174
|
+
const sectionAttribution = ir.sections.map((s) => {
|
|
3175
|
+
const tokens = countTokens(s.text);
|
|
3176
|
+
const kept = keptSectionIds.has(s.id);
|
|
3177
|
+
const estCostUsdFirstCall = kept ? tokens / 1e6 * profile.costInputPer1m : 0;
|
|
3178
|
+
const inActiveCachePrefix = kept && s.cacheable === true && cachePrefixActive;
|
|
3179
|
+
return {
|
|
3180
|
+
id: s.id,
|
|
3181
|
+
tokens,
|
|
3182
|
+
kept,
|
|
3183
|
+
cacheable: s.cacheable === true,
|
|
3184
|
+
estCostUsdFirstCall,
|
|
3185
|
+
estCostUsdWarm: inActiveCachePrefix ? estCostUsdFirstCall * cacheReadFraction : estCostUsdFirstCall
|
|
3186
|
+
};
|
|
3187
|
+
});
|
|
3188
|
+
const diagnostics = {
|
|
3189
|
+
sectionsKept: workingIR.sections.length,
|
|
3190
|
+
sectionsDropped: ir.sections.length - workingIR.sections.length,
|
|
3191
|
+
toolsKept: workingIR.tools?.length ?? 0,
|
|
3192
|
+
toolsDropped: (ir.tools?.length ?? 0) - (workingIR.tools?.length ?? 0),
|
|
3193
|
+
historyKept: workingIR.history?.length ?? 0,
|
|
3194
|
+
historyDropped: (ir.history?.length ?? 0) - (workingIR.history?.length ?? 0),
|
|
3195
|
+
cacheableTokens: lowered.diagnostics.cacheableTokens,
|
|
3196
|
+
estimatedCacheSavingsUsd: lowered.diagnostics.estimatedCacheSavingsUsd,
|
|
3197
|
+
historyCacheableTokens: lowered.diagnostics.historyCacheableTokens,
|
|
3198
|
+
historyTokensTotal: compressed.historyTokensTotal,
|
|
3199
|
+
// alpha.20 E3: mirror the consumer's declared mode for Glass-Box +
|
|
3200
|
+
// brain observability. Undefined when not declared (pre-alpha.20).
|
|
3201
|
+
toolOrchestration: ir.constraints?.toolOrchestration,
|
|
3202
|
+
// alpha.33 — see top-of-block comment.
|
|
3203
|
+
historyCacheMarkIndex,
|
|
3204
|
+
systemCacheMarkIndex,
|
|
3205
|
+
// alpha.43 — cliff-style warnings emitted by passApplyConventions.
|
|
3206
|
+
// Merge convention-pass cliffWarnings with any cliff-guard quality
|
|
3207
|
+
// warnings the cliff pass surfaced (the same shape — informational
|
|
3208
|
+
// text the consumer can route on without changing behavior).
|
|
3209
|
+
cliffWarnings: [
|
|
3210
|
+
...cliffs.value.loweringHints.qualityWarning ?? [],
|
|
3211
|
+
...conventions.value.cliffWarnings,
|
|
3212
|
+
// alpha.87 — best-effort profile in use (loud on EVERY compile).
|
|
3213
|
+
...bestEffortWarnings
|
|
3214
|
+
],
|
|
3215
|
+
// alpha.68 / Release A — measured discipline gate-token tax (§5.D). 0 when
|
|
3216
|
+
// the gate didn't fire.
|
|
3217
|
+
disciplineGateTokens,
|
|
3218
|
+
// alpha.87 — per-source freshness stamped at the moment the compile's
|
|
3219
|
+
// reads have all happened, from state each source already tracks (the
|
|
3220
|
+
// "one-line stamp at the point where the source is already known and
|
|
3221
|
+
// discarded" this filing reduced to once alpha.73/.86 warmed the caches).
|
|
3222
|
+
freshness: {
|
|
3223
|
+
config: configFreshness(),
|
|
3224
|
+
measuredFailure: measuredFailureFreshness(ir.appId),
|
|
3225
|
+
promotions: promotionsFreshness(ir.appId),
|
|
3226
|
+
exclusions: exclusionFindingsFreshness(ir.appId),
|
|
3227
|
+
promoteReady: promoteReadyFreshness(ir.appId)
|
|
3228
|
+
},
|
|
3229
|
+
sections: sectionAttribution
|
|
3230
|
+
};
|
|
3231
|
+
if (ir.intent.archetype === "hunt" && ir.constraints?.toolOrchestration === "sequential") {
|
|
3232
|
+
accumulatedMutations.push({
|
|
3233
|
+
id: "sequential-mode-chain-selected",
|
|
3234
|
+
source: "tool_orchestration",
|
|
3235
|
+
passName: "compile",
|
|
3236
|
+
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)."
|
|
3237
|
+
});
|
|
3238
|
+
}
|
|
3239
|
+
const phase2ProfileResolver = opts.profileResolver ? (id) => {
|
|
3240
|
+
try {
|
|
3241
|
+
return opts.profileResolver(id);
|
|
3242
|
+
} catch {
|
|
3243
|
+
return void 0;
|
|
3244
|
+
}
|
|
3245
|
+
} : tryGetProfile;
|
|
3246
|
+
const rawAdvisories = runAdvisor(
|
|
3247
|
+
ir,
|
|
3248
|
+
{
|
|
3249
|
+
target: profile.id,
|
|
3250
|
+
provider: profile.provider,
|
|
3251
|
+
tokensIn: inputTokens,
|
|
3252
|
+
diagnostics
|
|
3253
|
+
},
|
|
3254
|
+
profile,
|
|
3255
|
+
opts.policy,
|
|
3256
|
+
{
|
|
3257
|
+
fallbackChain,
|
|
3258
|
+
profileResolver: phase2ProfileResolver,
|
|
3259
|
+
// alpha.29 — feed translator rewrites to the advisor so the
|
|
3260
|
+
// `archetype-perf-floor-breach` rule can suppress when the translator
|
|
3261
|
+
// already cleared the cliff for the same archetype. Without this,
|
|
3262
|
+
// both the rewrite AND the advisory fire — noisy, and the advisory
|
|
3263
|
+
// would mislead consumers into thinking the cliff is unaddressed.
|
|
3264
|
+
sectionRewritesApplied
|
|
3265
|
+
}
|
|
3266
|
+
);
|
|
3267
|
+
if (ir["policy"] !== void 0 && opts.policy === void 0) {
|
|
3268
|
+
rawAdvisories.push({
|
|
3269
|
+
level: "critical",
|
|
3270
|
+
code: "policy-in-ir-ignored",
|
|
3271
|
+
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.",
|
|
3272
|
+
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.",
|
|
3273
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#public-api"
|
|
3274
|
+
});
|
|
3275
|
+
}
|
|
3276
|
+
const advisories = rawAdvisories.map((a) => ({
|
|
3277
|
+
...a,
|
|
3278
|
+
kgautoRequestId: handle,
|
|
3279
|
+
ownership: deriveOwnership(a.code, a.ownership)
|
|
3280
|
+
}));
|
|
3281
|
+
return {
|
|
3282
|
+
handle,
|
|
3283
|
+
target: profile.id,
|
|
3284
|
+
provider: profile.provider,
|
|
3285
|
+
request: lowered.request,
|
|
3286
|
+
tokensIn: inputTokens,
|
|
3287
|
+
estimatedCostUsd: target.estimatedCostUsd,
|
|
3288
|
+
mutationsApplied: accumulatedMutations,
|
|
3289
|
+
fallbackChain,
|
|
3290
|
+
advisories,
|
|
3291
|
+
diagnostics,
|
|
3292
|
+
sectionRewritesApplied,
|
|
3293
|
+
wireOverrides,
|
|
3294
|
+
systemMessages
|
|
3295
|
+
};
|
|
3296
|
+
}
|
|
3297
|
+
function computeHistoryCacheMarkIndex(ir) {
|
|
3298
|
+
const policy = ir.historyCachePolicy;
|
|
3299
|
+
if (!policy || policy.strategy === "none") return void 0;
|
|
3300
|
+
const historyLen = ir.history?.length ?? 0;
|
|
3301
|
+
if (historyLen === 0) return void 0;
|
|
3302
|
+
if (policy.strategy === "all-but-latest") {
|
|
3303
|
+
return historyLen - 1;
|
|
3304
|
+
}
|
|
3305
|
+
if (policy.strategy === "fixed-suffix") {
|
|
3306
|
+
const idx = historyLen - 1 - policy.suffix;
|
|
3307
|
+
if (idx < 0) return void 0;
|
|
3308
|
+
return idx;
|
|
3309
|
+
}
|
|
3310
|
+
return void 0;
|
|
3311
|
+
}
|
|
3312
|
+
function buildSystemMessages(ir, provider) {
|
|
3313
|
+
const sections = ir.sections;
|
|
3314
|
+
if (!sections || sections.length === 0) return [];
|
|
3315
|
+
return sections.map((s) => {
|
|
3316
|
+
const base = { role: "system", content: s.text };
|
|
3317
|
+
if (provider === "anthropic" && s.cacheable) {
|
|
3318
|
+
base.providerOptions = { anthropic: { cacheControl: { type: "ephemeral" } } };
|
|
3319
|
+
}
|
|
3320
|
+
return base;
|
|
3321
|
+
});
|
|
3322
|
+
}
|
|
3323
|
+
function lastCacheableSystemIndex(systemMessages) {
|
|
3324
|
+
for (let i = systemMessages.length - 1; i >= 0; i--) {
|
|
3325
|
+
const entry = systemMessages[i];
|
|
3326
|
+
if (entry && entry.providerOptions?.anthropic?.cacheControl) {
|
|
3327
|
+
return i;
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
return void 0;
|
|
3331
|
+
}
|
|
3332
|
+
function validateIR(ir) {
|
|
3333
|
+
if (!ir.appId) throw new Error("compile(): ir.appId is required");
|
|
3334
|
+
if (!ir.intent || !ir.intent.archetype) {
|
|
3335
|
+
throw new Error("compile(): ir.intent.archetype is required (use a dialect-v1 archetype)");
|
|
3336
|
+
}
|
|
3337
|
+
if (!Array.isArray(ir.models) || ir.models.length === 0) {
|
|
3338
|
+
throw new Error("compile(): ir.models must be a non-empty array");
|
|
3339
|
+
}
|
|
3340
|
+
if (!Array.isArray(ir.sections)) {
|
|
3341
|
+
throw new Error("compile(): ir.sections must be an array");
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
function resolveModelEntries(ir) {
|
|
3345
|
+
const out = [];
|
|
3346
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3347
|
+
for (const entry of ir.models) {
|
|
3348
|
+
let id;
|
|
3349
|
+
if (typeof entry === "string") {
|
|
3350
|
+
id = entry;
|
|
3351
|
+
} else if (entry && typeof entry === "object" && "family" in entry) {
|
|
3352
|
+
id = resolveFamilyEntry(entry.family, {
|
|
3353
|
+
archetype: ir.intent.archetype,
|
|
3354
|
+
appId: ir.appId
|
|
3355
|
+
});
|
|
3356
|
+
} else {
|
|
3357
|
+
throw new Error(
|
|
3358
|
+
`compile(): ir.models entry must be a string or { family: string }; got ${JSON.stringify(entry)}`
|
|
3359
|
+
);
|
|
3360
|
+
}
|
|
3361
|
+
if (seen.has(id)) continue;
|
|
3362
|
+
seen.add(id);
|
|
3363
|
+
out.push(id);
|
|
3364
|
+
}
|
|
3365
|
+
return { ...ir, models: out };
|
|
3366
|
+
}
|
|
3367
|
+
function pickTarget(ir, scores) {
|
|
3368
|
+
if (ir.constraints?.forceModel) {
|
|
3369
|
+
const forced = scores.find((s) => s.modelId === ir.constraints.forceModel);
|
|
3370
|
+
if (forced && forced.fits) return forced;
|
|
3371
|
+
if (forced) {
|
|
3372
|
+
throw new Error(
|
|
3373
|
+
`compile(): forceModel="${ir.constraints.forceModel}" does not fit: ${forced.rejectReasons.join("; ")}`
|
|
3374
|
+
);
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
const fitting = scores.filter((s) => s.fits).sort((a, b) => b.rank - a.rank);
|
|
3378
|
+
return fitting[0];
|
|
3379
|
+
}
|
|
3380
|
+
function validateFinalFit(ir, profile, tokens) {
|
|
3381
|
+
if (tokens > profile.maxContextTokens) {
|
|
3382
|
+
throw new Error(
|
|
3383
|
+
`compile(): final IR is ${tokens} tokens, exceeds ${profile.id} context (${profile.maxContextTokens})`
|
|
3384
|
+
);
|
|
3385
|
+
}
|
|
3386
|
+
if ((ir.tools?.length ?? 0) > profile.maxTools) {
|
|
3387
|
+
throw new Error(
|
|
3388
|
+
`compile(): final IR has ${ir.tools?.length} tools, exceeds ${profile.id} maxTools (${profile.maxTools})`
|
|
3389
|
+
);
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
|
|
3393
|
+
export {
|
|
3394
|
+
profileToRow,
|
|
3395
|
+
loadModelsFromBrain,
|
|
3396
|
+
loadAliasesFromBrain,
|
|
3397
|
+
DEFAULT_FINDINGS_ENDPOINT,
|
|
3398
|
+
configureExclusionFindingsBrain,
|
|
3399
|
+
isExclusionFindingsBrainActive,
|
|
3400
|
+
getStaleExclusionFindings,
|
|
3401
|
+
FamilyResolutionError,
|
|
3402
|
+
deriveFamilyFromModelId,
|
|
3403
|
+
getRecommendedPrimary,
|
|
3404
|
+
setTokenizer,
|
|
3405
|
+
resetTokenizer,
|
|
3406
|
+
countTokens,
|
|
3407
|
+
computeShape,
|
|
3408
|
+
resolveConventionsForProfile,
|
|
3409
|
+
applyArchetypeConvention,
|
|
3410
|
+
MEASURED_FAILURE_CFG,
|
|
3411
|
+
wilsonLowerBound,
|
|
3412
|
+
mapMeasuredFailureRows,
|
|
3413
|
+
judgeMeasuredFailure,
|
|
3414
|
+
DEFAULT_MEASURED_FAILURE_ENDPOINT,
|
|
3415
|
+
isMeasuredFailureGateEnabledFromEnv,
|
|
3416
|
+
configureMeasuredFailureBrain,
|
|
3417
|
+
isMeasuredFailureBrainActive,
|
|
3418
|
+
prefetchMeasuredFailure,
|
|
3419
|
+
awaitMeasuredFailureReady,
|
|
3420
|
+
getMeasuredFailureVerdict,
|
|
3421
|
+
_testResetMeasuredFailure,
|
|
3422
|
+
_testWaitForMeasuredFailureRefresh,
|
|
3423
|
+
DEFAULT_PROMOTIONS_ENDPOINT,
|
|
3424
|
+
isAutoPromoteEnabledFromEnv,
|
|
3425
|
+
configurePromotionsBrain,
|
|
3426
|
+
isPromotionsBrainActive,
|
|
3427
|
+
getApplicablePromotion,
|
|
3428
|
+
ROLLBACK_SUPPRESSION_WINDOW_DAYS,
|
|
3429
|
+
getRecentRollback,
|
|
3430
|
+
prefetchPromotions,
|
|
3431
|
+
awaitPromotionsReady,
|
|
3432
|
+
_testResetPromotions,
|
|
3433
|
+
_testWaitForPromotionsRefresh,
|
|
3434
|
+
markPromoteReadyHandled,
|
|
3435
|
+
ARCHETYPE_FAMILY_FITS,
|
|
3436
|
+
findBetterFit,
|
|
3437
|
+
BLOCKED_MODEL_NOT_IN_ROSTER_CODE,
|
|
3438
|
+
BLOCKED_MODEL_FAMILY_SIBLING_SERVED_CODE,
|
|
3439
|
+
PRODUCER_OWNED_RULE_CODES,
|
|
3440
|
+
deriveOwnership,
|
|
3441
|
+
runAdvisor,
|
|
3442
|
+
TRANSLATOR_FLOOR,
|
|
3443
|
+
RULE_SEQUENTIAL_TOOL_CLIFF,
|
|
3444
|
+
RULE_DISCIPLINE_GATES_V1,
|
|
3445
|
+
RULE_DISCIPLINE_GATES_V1_STRUCTURED,
|
|
3446
|
+
applySectionRewrites,
|
|
3447
|
+
compile
|
|
3448
|
+
};
|