@warmdrift/kgauto-compiler 2.0.0-alpha.41 → 2.0.0-alpha.43
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-XJZRDGHF.mjs → chunk-6MSJQCAP.mjs} +12 -1
- package/dist/{chunk-JQGRWJZO.mjs → chunk-ZHUD3I52.mjs} +60 -2
- 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 +60 -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 +191 -4
- package/dist/index.d.ts +191 -4
- package/dist/index.js +3303 -2950
- package/dist/index.mjs +1379 -1100
- package/dist/{ir-D_kgJcaV.d.mts → ir-CSgyol5D.d.mts} +46 -0
- package/dist/{ir-DYpvUSEw.d.ts → ir-MN9bGclm.d.ts} +46 -0
- package/dist/profiles.d.mts +92 -2
- package/dist/profiles.d.ts +92 -2
- package/dist/profiles.js +60 -2
- package/dist/profiles.mjs +1 -1
- package/dist/{types-CYY769Ls.d.mts → types-M48csxKK.d.mts} +1 -1
- package/dist/{types-DigtHnEt.d.ts → types-_1tcDEqa.d.ts} +1 -1
- package/dist/{types-MkiWUPM8.d.mts → types-acFM2-iZ.d.mts} +1 -1
- package/dist/{types-C8gOJ7hV.d.ts → types-uRIiYY_y.d.ts} +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -31,8 +31,9 @@ import {
|
|
|
31
31
|
isModelReachable,
|
|
32
32
|
isProviderReachable,
|
|
33
33
|
loadChainsFromBrain,
|
|
34
|
+
readBrainReadEnv,
|
|
34
35
|
resolveProviderKey
|
|
35
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-6MSJQCAP.mjs";
|
|
36
37
|
import {
|
|
37
38
|
ALIASES,
|
|
38
39
|
_setProfileBrainHook,
|
|
@@ -41,7 +42,7 @@ import {
|
|
|
41
42
|
getProfile,
|
|
42
43
|
profilesByProvider,
|
|
43
44
|
tryGetProfile
|
|
44
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-ZHUD3I52.mjs";
|
|
45
46
|
import {
|
|
46
47
|
emitAdvisoryFired,
|
|
47
48
|
emitCompileDone,
|
|
@@ -51,123 +52,497 @@ import {
|
|
|
51
52
|
emitFallbackWalked
|
|
52
53
|
} from "./chunk-NBO4R5PC.mjs";
|
|
53
54
|
|
|
54
|
-
// src/
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return
|
|
59
|
-
}
|
|
60
|
-
function setTokenizer(impl) {
|
|
61
|
-
tokenizerImpl = impl;
|
|
55
|
+
// src/models-brain.ts
|
|
56
|
+
function isModelRow(x) {
|
|
57
|
+
if (!x || typeof x !== "object") return false;
|
|
58
|
+
const r = x;
|
|
59
|
+
return typeof r.model_id === "string" && typeof r.provider === "string";
|
|
62
60
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
61
|
+
function isAliasRow(x) {
|
|
62
|
+
if (!x || typeof x !== "object") return false;
|
|
63
|
+
const r = x;
|
|
64
|
+
return typeof r.alias_id === "string" && typeof r.canonical_id === "string";
|
|
65
65
|
}
|
|
66
|
-
function
|
|
67
|
-
if (!text) return 0;
|
|
66
|
+
function rowToProfile(row) {
|
|
68
67
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
if (row.cliffs !== void 0 && row.cliffs !== null && !Array.isArray(row.cliffs)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (row.recovery !== void 0 && row.recovery !== null && !Array.isArray(row.recovery)) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (row.lowering !== void 0 && row.lowering !== null && (typeof row.lowering !== "object" || Array.isArray(row.lowering))) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
id: row.model_id,
|
|
79
|
+
provider: row.provider,
|
|
80
|
+
status: row.status ?? "current",
|
|
81
|
+
maxContextTokens: row.max_context_tokens ?? 0,
|
|
82
|
+
maxOutputTokens: row.max_output_tokens ?? 0,
|
|
83
|
+
maxTools: row.max_tools ?? 0,
|
|
84
|
+
parallelToolCalls: row.parallel_tool_calls ?? false,
|
|
85
|
+
structuredOutput: row.structured_output ?? "none",
|
|
86
|
+
systemPromptMode: row.system_prompt_mode ?? "inline",
|
|
87
|
+
streaming: row.streaming ?? true,
|
|
88
|
+
cliffs: row.cliffs ?? [],
|
|
89
|
+
costInputPer1m: row.cost_input_per_1m ?? 0,
|
|
90
|
+
costOutputPer1m: row.cost_output_per_1m ?? 0,
|
|
91
|
+
lowering: row.lowering ?? { system: { mode: "inline" }, cache: { strategy: "unsupported" } },
|
|
92
|
+
recovery: row.recovery ?? [],
|
|
93
|
+
strengths: row.strengths ?? [],
|
|
94
|
+
weaknesses: row.weaknesses ?? [],
|
|
95
|
+
notes: row.notes ?? void 0,
|
|
96
|
+
verifiedAgainstDocs: row.verified_against_docs ?? void 0,
|
|
97
|
+
archetypePerf: row.archetype_perf ?? void 0,
|
|
98
|
+
// alpha.41 — family-resolution fields. `family` may be null pre-
|
|
99
|
+
// migration 024; runtime falls back to deriveFamilyFromModelId at
|
|
100
|
+
// resolution time (see family-resolution.ts, G1).
|
|
101
|
+
family: row.family ?? void 0,
|
|
102
|
+
versionAdded: row.version_added ?? void 0,
|
|
103
|
+
active: row.active ?? void 0
|
|
104
|
+
};
|
|
71
105
|
} catch {
|
|
72
|
-
return
|
|
106
|
+
return null;
|
|
73
107
|
}
|
|
74
108
|
}
|
|
75
|
-
function
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
109
|
+
function profileToRow(profile, opts = {}) {
|
|
110
|
+
const row = {
|
|
111
|
+
model_id: profile.id,
|
|
112
|
+
provider: profile.provider,
|
|
113
|
+
status: profile.status,
|
|
114
|
+
max_context_tokens: profile.maxContextTokens,
|
|
115
|
+
max_output_tokens: profile.maxOutputTokens,
|
|
116
|
+
max_tools: profile.maxTools,
|
|
117
|
+
parallel_tool_calls: profile.parallelToolCalls,
|
|
118
|
+
structured_output: profile.structuredOutput,
|
|
119
|
+
system_prompt_mode: profile.systemPromptMode,
|
|
120
|
+
streaming: profile.streaming,
|
|
121
|
+
cliffs: profile.cliffs,
|
|
122
|
+
cost_input_per_1m: profile.costInputPer1m,
|
|
123
|
+
cost_output_per_1m: profile.costOutputPer1m,
|
|
124
|
+
lowering: profile.lowering,
|
|
125
|
+
recovery: profile.recovery,
|
|
126
|
+
strengths: profile.strengths,
|
|
127
|
+
weaknesses: profile.weaknesses,
|
|
128
|
+
notes: profile.notes ?? null,
|
|
129
|
+
archetype_perf: profile.archetypePerf ?? null,
|
|
130
|
+
active: opts.active ?? profile.active ?? true,
|
|
131
|
+
// alpha.41 — round-trip family + version_added when present on profile.
|
|
132
|
+
// version_added is operator-controlled via opts; profile-side value is
|
|
133
|
+
// used only when opts didn't override.
|
|
134
|
+
family: profile.family ?? null
|
|
135
|
+
};
|
|
136
|
+
if (opts.verifiedAgainstDocs !== void 0) {
|
|
137
|
+
row.verified_against_docs = opts.verifiedAgainstDocs;
|
|
138
|
+
} else if (profile.verifiedAgainstDocs !== void 0) {
|
|
139
|
+
const v = profile.verifiedAgainstDocs;
|
|
140
|
+
row.verified_against_docs = /^\d{4}-\d{2}-\d{2}/.test(v) ? v : null;
|
|
141
|
+
}
|
|
142
|
+
if (opts.versionAdded !== void 0) row.version_added = opts.versionAdded;
|
|
143
|
+
else if (profile.versionAdded !== void 0) row.version_added = profile.versionAdded;
|
|
144
|
+
if (opts.versionRemoved !== void 0) row.version_removed = opts.versionRemoved;
|
|
145
|
+
return row;
|
|
80
146
|
}
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
for (const
|
|
84
|
-
|
|
147
|
+
function mapRowsToModels(rows) {
|
|
148
|
+
const out = /* @__PURE__ */ new Map();
|
|
149
|
+
for (const row of rows) {
|
|
150
|
+
if (!isModelRow(row)) continue;
|
|
151
|
+
const profile = rowToProfile(row);
|
|
152
|
+
if (profile) out.set(profile.id, profile);
|
|
85
153
|
}
|
|
86
|
-
return
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
function mapRowsToAliases(rows) {
|
|
157
|
+
const out = {};
|
|
158
|
+
for (const row of rows) {
|
|
159
|
+
if (!isAliasRow(row)) continue;
|
|
160
|
+
out[row.alias_id] = row.canonical_id;
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
function bundledModels() {
|
|
165
|
+
return new Map(allProfilesRaw().map((p) => [p.id, p]));
|
|
87
166
|
}
|
|
167
|
+
function bundledAliases() {
|
|
168
|
+
return { ...ALIASES };
|
|
169
|
+
}
|
|
170
|
+
var loadModelsFromBrain = createBrainQueryCache({
|
|
171
|
+
table: "kgauto_models",
|
|
172
|
+
mapRows: mapRowsToModels,
|
|
173
|
+
bundledFallback: bundledModels
|
|
174
|
+
});
|
|
175
|
+
var loadAliasesFromBrain = createBrainQueryCache({
|
|
176
|
+
table: "kgauto_aliases",
|
|
177
|
+
mapRows: mapRowsToAliases,
|
|
178
|
+
bundledFallback: bundledAliases
|
|
179
|
+
});
|
|
180
|
+
_setProfileBrainHook({
|
|
181
|
+
getProfile: (canonical) => loadModelsFromBrain().get(canonical),
|
|
182
|
+
resolveAlias: (id) => loadAliasesFromBrain()[id]
|
|
183
|
+
});
|
|
88
184
|
|
|
89
|
-
// src/
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
const before = ir.sections.length;
|
|
93
|
-
const kept = ir.sections.filter((s) => !s.intents || s.intents.length === 0 || s.intents.includes(intent));
|
|
94
|
-
const dropped = before - kept.length;
|
|
95
|
-
if (dropped === 0) return { value: ir, mutations: [] };
|
|
96
|
-
return {
|
|
97
|
-
value: { ...ir, sections: kept },
|
|
98
|
-
mutations: [
|
|
99
|
-
{
|
|
100
|
-
id: `slice-${dropped}`,
|
|
101
|
-
source: "static_pass",
|
|
102
|
-
passName: "slice",
|
|
103
|
-
description: `Dropped ${dropped} of ${before} sections not tagged for intent=${intent}`
|
|
104
|
-
}
|
|
105
|
-
]
|
|
106
|
-
};
|
|
185
|
+
// src/exclusion-findings-brain.ts
|
|
186
|
+
function isValidVerdict(v) {
|
|
187
|
+
return v === "recommend-probe" || v === "unblock" || v === "stay-excluded" || v === "inconclusive";
|
|
107
188
|
}
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
const order = [];
|
|
111
|
-
for (const s of ir.sections) {
|
|
112
|
-
const key = simpleHash(s.text.trim());
|
|
113
|
-
if (!seen.has(key)) {
|
|
114
|
-
seen.set(key, s);
|
|
115
|
-
order.push(key);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const deduped = order.map((k) => seen.get(k));
|
|
119
|
-
const dropped = ir.sections.length - deduped.length;
|
|
120
|
-
if (dropped === 0) return { value: ir, mutations: [] };
|
|
121
|
-
return {
|
|
122
|
-
value: { ...ir, sections: deduped },
|
|
123
|
-
mutations: [
|
|
124
|
-
{
|
|
125
|
-
id: `dedupe-${dropped}`,
|
|
126
|
-
source: "static_pass",
|
|
127
|
-
passName: "dedupe",
|
|
128
|
-
description: `Removed ${dropped} duplicate section(s) (by text hash)`
|
|
129
|
-
}
|
|
130
|
-
]
|
|
131
|
-
};
|
|
189
|
+
function isValidConfidence(v) {
|
|
190
|
+
return v === "high" || v === "medium" || v === "low";
|
|
132
191
|
}
|
|
133
|
-
function
|
|
134
|
-
if (!
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
const scored = ir.tools.map((t) => {
|
|
138
|
-
const score = t.relevanceByIntent?.[intent] ?? 0.5;
|
|
139
|
-
return { tool: t, score };
|
|
140
|
-
});
|
|
141
|
-
const kept = scored.filter((s) => s.score >= threshold).sort((a, b) => b.score - a.score).map((s) => s.tool);
|
|
142
|
-
const limited = opts.maxKeep ? kept.slice(0, opts.maxKeep) : kept;
|
|
143
|
-
const dropped = ir.tools.length - limited.length;
|
|
144
|
-
if (dropped === 0) return { value: ir, mutations: [] };
|
|
145
|
-
return {
|
|
146
|
-
value: { ...ir, tools: limited },
|
|
147
|
-
mutations: [
|
|
148
|
-
{
|
|
149
|
-
id: `tool-relevance-${dropped}`,
|
|
150
|
-
source: "static_pass",
|
|
151
|
-
passName: "tool_relevance",
|
|
152
|
-
description: `Dropped ${dropped} of ${ir.tools.length} tools below relevance ${threshold} for intent=${intent}`
|
|
153
|
-
}
|
|
154
|
-
]
|
|
155
|
-
};
|
|
192
|
+
function isRawFindingRow(x) {
|
|
193
|
+
if (!x || typeof x !== "object") return false;
|
|
194
|
+
const r = x;
|
|
195
|
+
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";
|
|
156
196
|
}
|
|
157
|
-
function
|
|
158
|
-
|
|
159
|
-
for (const
|
|
160
|
-
if (
|
|
197
|
+
function mapRowsToFindings(rows) {
|
|
198
|
+
const out = [];
|
|
199
|
+
for (const row of rows) {
|
|
200
|
+
if (!isRawFindingRow(row)) continue;
|
|
201
|
+
if (!isValidVerdict(row.verdict)) continue;
|
|
202
|
+
if (!isValidConfidence(row.confidence)) continue;
|
|
203
|
+
let savings = null;
|
|
204
|
+
if (typeof row.estimated_savings_usd_30d === "number") {
|
|
205
|
+
savings = Number.isFinite(row.estimated_savings_usd_30d) ? row.estimated_savings_usd_30d : null;
|
|
206
|
+
} else if (typeof row.estimated_savings_usd_30d === "string") {
|
|
207
|
+
const n = Number(row.estimated_savings_usd_30d);
|
|
208
|
+
savings = Number.isFinite(n) ? n : null;
|
|
209
|
+
}
|
|
210
|
+
out.push({
|
|
211
|
+
archetype: row.intent_archetype,
|
|
212
|
+
excludedModel: row.excluded_model,
|
|
213
|
+
excludedProvider: row.excluded_provider,
|
|
214
|
+
verdict: row.verdict,
|
|
215
|
+
estimatedSavingsUsd30d: savings,
|
|
216
|
+
message: row.message,
|
|
217
|
+
suggestion: row.suggestion,
|
|
218
|
+
confidence: row.confidence,
|
|
219
|
+
evidence: row.evidence && typeof row.evidence === "object" ? row.evidence : void 0
|
|
220
|
+
});
|
|
161
221
|
}
|
|
162
|
-
return
|
|
222
|
+
return out;
|
|
163
223
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
224
|
+
var snapshots = /* @__PURE__ */ new Map();
|
|
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.expiresAt = Date.now() + rt.ttlMs;
|
|
286
|
+
snap.refreshing = false;
|
|
287
|
+
} catch (err) {
|
|
288
|
+
if (runtime !== rt) return;
|
|
289
|
+
snap.refreshing = false;
|
|
290
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
291
|
+
if (!warnedOnce) {
|
|
292
|
+
warnedOnce = true;
|
|
293
|
+
(rt.onError ?? defaultOnError)(err);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function defaultOnError(err) {
|
|
298
|
+
console.warn(
|
|
299
|
+
"[kgauto] exclusion-findings fetch failed (using empty fallback):",
|
|
300
|
+
err
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/family-resolution.ts
|
|
305
|
+
var FamilyResolutionError = class extends Error {
|
|
306
|
+
family;
|
|
307
|
+
cause;
|
|
308
|
+
constructor(family, cause) {
|
|
309
|
+
super(
|
|
310
|
+
`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.`
|
|
311
|
+
);
|
|
312
|
+
this.name = "FamilyResolutionError";
|
|
313
|
+
this.family = family;
|
|
314
|
+
this.cause = cause;
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
function deriveFamilyFromModelId(modelId) {
|
|
318
|
+
if (typeof modelId !== "string" || modelId.length === 0) return null;
|
|
319
|
+
if (modelId.startsWith("claude-opus-")) return "claude-opus";
|
|
320
|
+
if (modelId.startsWith("claude-sonnet-")) return "claude-sonnet";
|
|
321
|
+
if (modelId.startsWith("claude-haiku-")) return "claude-haiku";
|
|
322
|
+
if (modelId.startsWith("gemini-") && modelId.includes("flash-lite")) {
|
|
323
|
+
return "gemini-flash-lite";
|
|
324
|
+
}
|
|
325
|
+
if (modelId.startsWith("gemini-") && modelId.includes("flash")) {
|
|
326
|
+
return "gemini-flash";
|
|
327
|
+
}
|
|
328
|
+
if (modelId.startsWith("gemini-") && modelId.includes("pro")) {
|
|
329
|
+
return "gemini-pro";
|
|
330
|
+
}
|
|
331
|
+
if (modelId.startsWith("deepseek-") && modelId.includes("pro")) {
|
|
332
|
+
return "deepseek-reasoner";
|
|
333
|
+
}
|
|
334
|
+
if (modelId.startsWith("deepseek-")) return "deepseek-chat";
|
|
335
|
+
if (modelId.startsWith("gpt-")) return "openai-gpt";
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
function familyOf(profile) {
|
|
339
|
+
return profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
340
|
+
}
|
|
341
|
+
function archetypePerfFor(profile, archetype) {
|
|
342
|
+
if (!archetype) return 0;
|
|
343
|
+
const score = profile.archetypePerf?.[archetype];
|
|
344
|
+
return typeof score === "number" ? score : 5;
|
|
345
|
+
}
|
|
346
|
+
function selectCandidates(registry, family, archetype, appId) {
|
|
347
|
+
const out = [];
|
|
348
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
349
|
+
if (appId && archetype) {
|
|
350
|
+
const findings = getStaleExclusionFindings({ appId, archetype });
|
|
351
|
+
for (const f of findings) {
|
|
352
|
+
if (f.verdict === "stay-excluded") excluded.add(f.excludedModel);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
for (const profile of registry.values()) {
|
|
356
|
+
if (familyOf(profile) !== family) continue;
|
|
357
|
+
if (profile.status !== "current") continue;
|
|
358
|
+
if (profile.active === false) continue;
|
|
359
|
+
if (archetype) {
|
|
360
|
+
if (archetypePerfFor(profile, archetype) < ARCHETYPE_FLOOR_DEFAULT) continue;
|
|
361
|
+
}
|
|
362
|
+
if (excluded.has(profile.id)) continue;
|
|
363
|
+
out.push(profile);
|
|
364
|
+
}
|
|
365
|
+
return out;
|
|
366
|
+
}
|
|
367
|
+
function sortCandidates(candidates, archetype) {
|
|
368
|
+
const sorted = [...candidates];
|
|
369
|
+
sorted.sort((a, b) => {
|
|
370
|
+
const perfA = archetypePerfFor(a, archetype);
|
|
371
|
+
const perfB = archetypePerfFor(b, archetype);
|
|
372
|
+
if (perfA !== perfB) return perfB - perfA;
|
|
373
|
+
const vA = a.versionAdded ?? "";
|
|
374
|
+
const vB = b.versionAdded ?? "";
|
|
375
|
+
if (vA !== vB) return vA < vB ? 1 : -1;
|
|
376
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
377
|
+
});
|
|
378
|
+
return sorted;
|
|
379
|
+
}
|
|
380
|
+
function getRecommendedPrimary(opts) {
|
|
381
|
+
if (opts.posture === "locked") return opts.fallback;
|
|
382
|
+
const registry = loadModelsFromBrain();
|
|
383
|
+
const candidates = selectCandidates(
|
|
384
|
+
registry,
|
|
385
|
+
opts.family,
|
|
386
|
+
opts.archetype,
|
|
387
|
+
opts.appId
|
|
388
|
+
);
|
|
389
|
+
if (candidates.length === 0) return opts.fallback;
|
|
390
|
+
const sorted = sortCandidates(candidates, opts.archetype);
|
|
391
|
+
return sorted[0]?.id ?? opts.fallback;
|
|
392
|
+
}
|
|
393
|
+
function resolveFamilyEntry(family, ctx) {
|
|
394
|
+
if (typeof family !== "string" || family.length === 0) {
|
|
395
|
+
throw new FamilyResolutionError(
|
|
396
|
+
String(family),
|
|
397
|
+
"Family tag must be a non-empty string"
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
const registry = loadModelsFromBrain();
|
|
401
|
+
const candidates = selectCandidates(
|
|
402
|
+
registry,
|
|
403
|
+
family,
|
|
404
|
+
ctx.archetype,
|
|
405
|
+
ctx.appId
|
|
406
|
+
);
|
|
407
|
+
if (candidates.length === 0) {
|
|
408
|
+
let anyInFamily = false;
|
|
409
|
+
for (const profile of registry.values()) {
|
|
410
|
+
if (familyOf(profile) === family) {
|
|
411
|
+
anyInFamily = true;
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
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`;
|
|
416
|
+
throw new FamilyResolutionError(family, cause);
|
|
417
|
+
}
|
|
418
|
+
const sorted = sortCandidates(candidates, ctx.archetype);
|
|
419
|
+
const winner = sorted[0];
|
|
420
|
+
if (!winner) {
|
|
421
|
+
throw new FamilyResolutionError(
|
|
422
|
+
family,
|
|
423
|
+
"Candidates non-empty but sort returned undefined \u2014 internal invariant violation"
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
return winner.id;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/tokenizer.ts
|
|
430
|
+
var tokenizerImpl = defaultCharBasedCounter;
|
|
431
|
+
function defaultCharBasedCounter(text) {
|
|
432
|
+
if (!text) return 0;
|
|
433
|
+
return Math.max(1, Math.ceil(text.length / 4));
|
|
434
|
+
}
|
|
435
|
+
function setTokenizer(impl) {
|
|
436
|
+
tokenizerImpl = impl;
|
|
437
|
+
}
|
|
438
|
+
function resetTokenizer() {
|
|
439
|
+
tokenizerImpl = defaultCharBasedCounter;
|
|
440
|
+
}
|
|
441
|
+
function countTokens(text) {
|
|
442
|
+
if (!text) return 0;
|
|
443
|
+
try {
|
|
444
|
+
const n = tokenizerImpl(text);
|
|
445
|
+
return Number.isFinite(n) && n >= 0 ? n : defaultCharBasedCounter(text);
|
|
446
|
+
} catch {
|
|
447
|
+
return defaultCharBasedCounter(text);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function countToolTokens(tool) {
|
|
451
|
+
const namePart = countTokens(tool.name);
|
|
452
|
+
const descPart = tool.description ? countTokens(tool.description) : 0;
|
|
453
|
+
const paramPart = tool.parameters ? countTokens(JSON.stringify(tool.parameters)) : 0;
|
|
454
|
+
return namePart + descPart + paramPart + 8;
|
|
455
|
+
}
|
|
456
|
+
function countMessagesTokens(messages) {
|
|
457
|
+
let total = 0;
|
|
458
|
+
for (const m of messages) {
|
|
459
|
+
total += countTokens(m.content) + 4;
|
|
460
|
+
}
|
|
461
|
+
return total;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/passes.ts
|
|
465
|
+
function passSlice(ir) {
|
|
466
|
+
const intent = ir.intent.archetype;
|
|
467
|
+
const before = ir.sections.length;
|
|
468
|
+
const kept = ir.sections.filter((s) => !s.intents || s.intents.length === 0 || s.intents.includes(intent));
|
|
469
|
+
const dropped = before - kept.length;
|
|
470
|
+
if (dropped === 0) return { value: ir, mutations: [] };
|
|
471
|
+
return {
|
|
472
|
+
value: { ...ir, sections: kept },
|
|
473
|
+
mutations: [
|
|
474
|
+
{
|
|
475
|
+
id: `slice-${dropped}`,
|
|
476
|
+
source: "static_pass",
|
|
477
|
+
passName: "slice",
|
|
478
|
+
description: `Dropped ${dropped} of ${before} sections not tagged for intent=${intent}`
|
|
479
|
+
}
|
|
480
|
+
]
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function passDedupe(ir) {
|
|
484
|
+
const seen = /* @__PURE__ */ new Map();
|
|
485
|
+
const order = [];
|
|
486
|
+
for (const s of ir.sections) {
|
|
487
|
+
const key = simpleHash(s.text.trim());
|
|
488
|
+
if (!seen.has(key)) {
|
|
489
|
+
seen.set(key, s);
|
|
490
|
+
order.push(key);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
const deduped = order.map((k) => seen.get(k));
|
|
494
|
+
const dropped = ir.sections.length - deduped.length;
|
|
495
|
+
if (dropped === 0) return { value: ir, mutations: [] };
|
|
496
|
+
return {
|
|
497
|
+
value: { ...ir, sections: deduped },
|
|
498
|
+
mutations: [
|
|
499
|
+
{
|
|
500
|
+
id: `dedupe-${dropped}`,
|
|
501
|
+
source: "static_pass",
|
|
502
|
+
passName: "dedupe",
|
|
503
|
+
description: `Removed ${dropped} duplicate section(s) (by text hash)`
|
|
504
|
+
}
|
|
505
|
+
]
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function passToolRelevance(ir, opts = {}) {
|
|
509
|
+
if (!ir.tools || ir.tools.length === 0) return { value: ir, mutations: [] };
|
|
510
|
+
const threshold = opts.threshold ?? 0.2;
|
|
511
|
+
const intent = ir.intent.archetype;
|
|
512
|
+
const scored = ir.tools.map((t) => {
|
|
513
|
+
const score = t.relevanceByIntent?.[intent] ?? 0.5;
|
|
514
|
+
return { tool: t, score };
|
|
515
|
+
});
|
|
516
|
+
const kept = scored.filter((s) => s.score >= threshold).sort((a, b) => b.score - a.score).map((s) => s.tool);
|
|
517
|
+
const limited = opts.maxKeep ? kept.slice(0, opts.maxKeep) : kept;
|
|
518
|
+
const dropped = ir.tools.length - limited.length;
|
|
519
|
+
if (dropped === 0) return { value: ir, mutations: [] };
|
|
520
|
+
return {
|
|
521
|
+
value: { ...ir, tools: limited },
|
|
522
|
+
mutations: [
|
|
523
|
+
{
|
|
524
|
+
id: `tool-relevance-${dropped}`,
|
|
525
|
+
source: "static_pass",
|
|
526
|
+
passName: "tool_relevance",
|
|
527
|
+
description: `Dropped ${dropped} of ${ir.tools.length} tools below relevance ${threshold} for intent=${intent}`
|
|
528
|
+
}
|
|
529
|
+
]
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
function totalHistoryTokens(history) {
|
|
533
|
+
let total = 0;
|
|
534
|
+
for (const m of history) {
|
|
535
|
+
if (typeof m.content === "string") total += countTokens(m.content);
|
|
536
|
+
}
|
|
537
|
+
return total;
|
|
538
|
+
}
|
|
539
|
+
function passCompressHistory(ir, opts = {}) {
|
|
540
|
+
const history = ir.history;
|
|
541
|
+
if (!history || history.length === 0) {
|
|
542
|
+
return { value: ir, mutations: [], historyTokensTotal: 0 };
|
|
543
|
+
}
|
|
544
|
+
const keepRecent = opts.keepRecent ?? 4;
|
|
545
|
+
const summarizeOlderThan = opts.summarizeOlderThan ?? 8;
|
|
171
546
|
const summarizeAboveTokens = opts.summarizeAboveTokens;
|
|
172
547
|
const historyTokensTotal = totalHistoryTokens(history);
|
|
173
548
|
const countThresholdHit = history.length > summarizeOlderThan;
|
|
@@ -449,6 +824,175 @@ function simpleHash(s) {
|
|
|
449
824
|
}
|
|
450
825
|
return (h >>> 0).toString(36);
|
|
451
826
|
}
|
|
827
|
+
function resolveConventionsForProfile(profile) {
|
|
828
|
+
const own = profile.archetypeConventions ?? [];
|
|
829
|
+
const family = profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
830
|
+
if (!family) return own;
|
|
831
|
+
return own;
|
|
832
|
+
}
|
|
833
|
+
function applyArchetypeConvention(promptText, archetype, family) {
|
|
834
|
+
if (typeof promptText !== "string" || promptText.length === 0) return promptText;
|
|
835
|
+
const repId = familyRepId(family);
|
|
836
|
+
if (!repId) return promptText;
|
|
837
|
+
const profile = tryGetProfile(repId);
|
|
838
|
+
if (!profile || !profile.archetypeConventions) return promptText;
|
|
839
|
+
const conventions = profile.archetypeConventions.filter((c) => c.archetype === archetype);
|
|
840
|
+
if (conventions.length === 0) return promptText;
|
|
841
|
+
let next = promptText;
|
|
842
|
+
for (const c of conventions) {
|
|
843
|
+
if (c.promptPrefix) {
|
|
844
|
+
if (!next.startsWith(c.promptPrefix)) {
|
|
845
|
+
next = c.promptPrefix + next;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (c.promptSuffix) {
|
|
849
|
+
if (!next.endsWith(c.promptSuffix)) {
|
|
850
|
+
next = next + c.promptSuffix;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
return next;
|
|
855
|
+
}
|
|
856
|
+
function familyRepId(family) {
|
|
857
|
+
switch (family) {
|
|
858
|
+
case "deepseek-reasoner":
|
|
859
|
+
return "deepseek-v4-pro";
|
|
860
|
+
case "deepseek-chat":
|
|
861
|
+
return "deepseek-v4-flash";
|
|
862
|
+
default:
|
|
863
|
+
return void 0;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
function passApplyConventions(ir, profile) {
|
|
867
|
+
const mutations = [];
|
|
868
|
+
const cliffWarnings = [];
|
|
869
|
+
const structuredOutputHints = [];
|
|
870
|
+
const own = profile.archetypeConventions ?? [];
|
|
871
|
+
let effective = own;
|
|
872
|
+
if (effective.length === 0) {
|
|
873
|
+
const family2 = profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
874
|
+
if (family2) {
|
|
875
|
+
const repId = familyRepId(family2);
|
|
876
|
+
if (repId && repId !== profile.id) {
|
|
877
|
+
const repProfile = tryGetProfile(repId);
|
|
878
|
+
if (repProfile && repProfile.archetypeConventions) {
|
|
879
|
+
effective = repProfile.archetypeConventions;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (effective.length === 0) {
|
|
885
|
+
return {
|
|
886
|
+
value: { ir, cliffWarnings, structuredOutputHints },
|
|
887
|
+
mutations
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
const archetype = ir.intent.archetype;
|
|
891
|
+
const matching = effective.filter((c) => c.archetype === archetype);
|
|
892
|
+
if (matching.length === 0) {
|
|
893
|
+
return {
|
|
894
|
+
value: { ir, cliffWarnings, structuredOutputHints },
|
|
895
|
+
mutations
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
const family = profile.family ?? deriveFamilyFromModelId(profile.id) ?? "unknown";
|
|
899
|
+
let nextIR = ir;
|
|
900
|
+
for (const convention of matching) {
|
|
901
|
+
let touched = false;
|
|
902
|
+
if (convention.promptPrefix) {
|
|
903
|
+
const sections = nextIR.sections ?? [];
|
|
904
|
+
const alreadyPresent = sections.some(
|
|
905
|
+
(s) => s.text.includes(convention.promptPrefix)
|
|
906
|
+
);
|
|
907
|
+
if (!alreadyPresent) {
|
|
908
|
+
const prefixSection = {
|
|
909
|
+
id: `convention-prefix-${family}-${archetype}`,
|
|
910
|
+
text: convention.promptPrefix
|
|
911
|
+
};
|
|
912
|
+
nextIR = { ...nextIR, sections: [prefixSection, ...sections] };
|
|
913
|
+
touched = true;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
if (convention.promptSuffix) {
|
|
917
|
+
const history = nextIR.history ?? [];
|
|
918
|
+
let lastUserIdx = -1;
|
|
919
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
920
|
+
if (history[i]?.role === "user") {
|
|
921
|
+
lastUserIdx = i;
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
if (lastUserIdx >= 0) {
|
|
926
|
+
const target = history[lastUserIdx];
|
|
927
|
+
if (!target.content.endsWith(convention.promptSuffix)) {
|
|
928
|
+
const newHistory = history.slice();
|
|
929
|
+
newHistory[lastUserIdx] = {
|
|
930
|
+
...target,
|
|
931
|
+
content: target.content + convention.promptSuffix
|
|
932
|
+
};
|
|
933
|
+
nextIR = { ...nextIR, history: newHistory };
|
|
934
|
+
touched = true;
|
|
935
|
+
}
|
|
936
|
+
} else if (nextIR.currentTurn && nextIR.currentTurn.role === "user") {
|
|
937
|
+
const target = nextIR.currentTurn;
|
|
938
|
+
if (!target.content.endsWith(convention.promptSuffix)) {
|
|
939
|
+
nextIR = {
|
|
940
|
+
...nextIR,
|
|
941
|
+
currentTurn: {
|
|
942
|
+
...target,
|
|
943
|
+
content: target.content + convention.promptSuffix
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
touched = true;
|
|
947
|
+
}
|
|
948
|
+
} else {
|
|
949
|
+
nextIR = {
|
|
950
|
+
...nextIR,
|
|
951
|
+
currentTurn: {
|
|
952
|
+
role: "user",
|
|
953
|
+
content: convention.promptSuffix.trim()
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
touched = true;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
if (convention.structuredOutputHint) {
|
|
960
|
+
const wantsStructured = !!nextIR.constraints?.structuredOutput;
|
|
961
|
+
const shouldSurface = convention.structuredOutputHint === "avoid" && wantsStructured || convention.structuredOutputHint === "enforce" && !wantsStructured;
|
|
962
|
+
if (shouldSurface) {
|
|
963
|
+
structuredOutputHints.push({
|
|
964
|
+
archetype,
|
|
965
|
+
hint: convention.structuredOutputHint,
|
|
966
|
+
reason: convention.reason
|
|
967
|
+
});
|
|
968
|
+
if (convention.cliffWarning) {
|
|
969
|
+
cliffWarnings.push(`${profile.id}: ${convention.cliffWarning}`);
|
|
970
|
+
}
|
|
971
|
+
touched = true;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
if (convention.cliffWarning && !convention.structuredOutputHint) {
|
|
975
|
+
const toolCount = nextIR.tools?.length ?? 0;
|
|
976
|
+
const thresholdOk = convention.whenToolCountAtLeast === void 0 || toolCount >= convention.whenToolCountAtLeast;
|
|
977
|
+
if (thresholdOk) {
|
|
978
|
+
cliffWarnings.push(`${profile.id}: ${convention.cliffWarning}`);
|
|
979
|
+
touched = true;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
if (touched) {
|
|
983
|
+
mutations.push({
|
|
984
|
+
id: `apply-convention-${family}-${archetype}`,
|
|
985
|
+
source: "archetype_convention",
|
|
986
|
+
passName: "apply_conventions",
|
|
987
|
+
description: `${profile.id}: applied ${family}-family convention for archetype=${archetype} \u2014 ${convention.reason}`
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
return {
|
|
992
|
+
value: { ir: nextIR, cliffWarnings, structuredOutputHints },
|
|
993
|
+
mutations
|
|
994
|
+
};
|
|
995
|
+
}
|
|
452
996
|
|
|
453
997
|
// src/lower.ts
|
|
454
998
|
function lower(ir, profile, hints = {}) {
|
|
@@ -826,125 +1370,6 @@ function getArchetypePerfScore(modelId, archetype) {
|
|
|
826
1370
|
return { score, n, grounding };
|
|
827
1371
|
}
|
|
828
1372
|
|
|
829
|
-
// src/exclusion-findings-brain.ts
|
|
830
|
-
function isValidVerdict(v) {
|
|
831
|
-
return v === "recommend-probe" || v === "unblock" || v === "stay-excluded" || v === "inconclusive";
|
|
832
|
-
}
|
|
833
|
-
function isValidConfidence(v) {
|
|
834
|
-
return v === "high" || v === "medium" || v === "low";
|
|
835
|
-
}
|
|
836
|
-
function isRawFindingRow(x) {
|
|
837
|
-
if (!x || typeof x !== "object") return false;
|
|
838
|
-
const r = x;
|
|
839
|
-
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";
|
|
840
|
-
}
|
|
841
|
-
function mapRowsToFindings(rows) {
|
|
842
|
-
const out = [];
|
|
843
|
-
for (const row of rows) {
|
|
844
|
-
if (!isRawFindingRow(row)) continue;
|
|
845
|
-
if (!isValidVerdict(row.verdict)) continue;
|
|
846
|
-
if (!isValidConfidence(row.confidence)) continue;
|
|
847
|
-
let savings = null;
|
|
848
|
-
if (typeof row.estimated_savings_usd_30d === "number") {
|
|
849
|
-
savings = Number.isFinite(row.estimated_savings_usd_30d) ? row.estimated_savings_usd_30d : null;
|
|
850
|
-
} else if (typeof row.estimated_savings_usd_30d === "string") {
|
|
851
|
-
const n = Number(row.estimated_savings_usd_30d);
|
|
852
|
-
savings = Number.isFinite(n) ? n : null;
|
|
853
|
-
}
|
|
854
|
-
out.push({
|
|
855
|
-
archetype: row.intent_archetype,
|
|
856
|
-
excludedModel: row.excluded_model,
|
|
857
|
-
excludedProvider: row.excluded_provider,
|
|
858
|
-
verdict: row.verdict,
|
|
859
|
-
estimatedSavingsUsd30d: savings,
|
|
860
|
-
message: row.message,
|
|
861
|
-
suggestion: row.suggestion,
|
|
862
|
-
confidence: row.confidence,
|
|
863
|
-
evidence: row.evidence && typeof row.evidence === "object" ? row.evidence : void 0
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
return out;
|
|
867
|
-
}
|
|
868
|
-
var snapshots = /* @__PURE__ */ new Map();
|
|
869
|
-
var runtime;
|
|
870
|
-
var warnedOnce = false;
|
|
871
|
-
var DEFAULT_FINDINGS_ENDPOINT = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions";
|
|
872
|
-
function configureExclusionFindingsBrain(rt) {
|
|
873
|
-
runtime = rt;
|
|
874
|
-
snapshots.clear();
|
|
875
|
-
warnedOnce = false;
|
|
876
|
-
}
|
|
877
|
-
function isExclusionFindingsBrainActive() {
|
|
878
|
-
return runtime !== void 0;
|
|
879
|
-
}
|
|
880
|
-
function getStaleExclusionFindings(opts) {
|
|
881
|
-
const rt = runtime;
|
|
882
|
-
if (!rt) return [];
|
|
883
|
-
const appId = opts.appId;
|
|
884
|
-
if (!appId) return [];
|
|
885
|
-
let snap = snapshots.get(appId);
|
|
886
|
-
if (!snap) {
|
|
887
|
-
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
888
|
-
snapshots.set(appId, snap);
|
|
889
|
-
}
|
|
890
|
-
const now = Date.now();
|
|
891
|
-
const stale = snap.expiresAt <= now;
|
|
892
|
-
if (stale && !snap.refreshing) {
|
|
893
|
-
snap.refreshing = true;
|
|
894
|
-
void asyncRefresh(rt, appId);
|
|
895
|
-
}
|
|
896
|
-
if (opts.archetype) {
|
|
897
|
-
return snap.data.filter((f) => f.archetype === opts.archetype);
|
|
898
|
-
}
|
|
899
|
-
return snap.data;
|
|
900
|
-
}
|
|
901
|
-
var pendingRefreshes = /* @__PURE__ */ new Map();
|
|
902
|
-
async function asyncRefresh(rt, appId) {
|
|
903
|
-
const promise = doRefresh(rt, appId);
|
|
904
|
-
pendingRefreshes.set(appId, promise);
|
|
905
|
-
try {
|
|
906
|
-
await promise;
|
|
907
|
-
} finally {
|
|
908
|
-
if (pendingRefreshes.get(appId) === promise) {
|
|
909
|
-
pendingRefreshes.delete(appId);
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
async function doRefresh(rt, appId) {
|
|
914
|
-
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
915
|
-
let snap = snapshots.get(appId);
|
|
916
|
-
if (!snap) {
|
|
917
|
-
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
918
|
-
snapshots.set(appId, snap);
|
|
919
|
-
}
|
|
920
|
-
try {
|
|
921
|
-
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
922
|
-
if (!res.ok) {
|
|
923
|
-
throw new Error(`findings ${res.status}: ${res.statusText}`);
|
|
924
|
-
}
|
|
925
|
-
const body = await res.json();
|
|
926
|
-
if (runtime !== rt) return;
|
|
927
|
-
const rows = Array.isArray(body) ? mapRowsToFindings(body) : [];
|
|
928
|
-
snap.data = rows;
|
|
929
|
-
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
930
|
-
snap.refreshing = false;
|
|
931
|
-
} catch (err) {
|
|
932
|
-
if (runtime !== rt) return;
|
|
933
|
-
snap.refreshing = false;
|
|
934
|
-
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
935
|
-
if (!warnedOnce) {
|
|
936
|
-
warnedOnce = true;
|
|
937
|
-
(rt.onError ?? defaultOnError)(err);
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
function defaultOnError(err) {
|
|
942
|
-
console.warn(
|
|
943
|
-
"[kgauto] exclusion-findings fetch failed (using empty fallback):",
|
|
944
|
-
err
|
|
945
|
-
);
|
|
946
|
-
}
|
|
947
|
-
|
|
948
1373
|
// src/promote-ready-brain.ts
|
|
949
1374
|
function isRawPromoteReadyRow(x) {
|
|
950
1375
|
if (!x || typeof x !== "object") return false;
|
|
@@ -970,964 +1395,795 @@ function mapRowsToFindings2(rows) {
|
|
|
970
1395
|
out.push({
|
|
971
1396
|
archetype: row.intent_archetype,
|
|
972
1397
|
family: row.family,
|
|
973
|
-
candidateModel: row.candidate_model,
|
|
974
|
-
currentModel: row.current_model,
|
|
975
|
-
sampleN,
|
|
976
|
-
judgePassRate: passRate,
|
|
977
|
-
judgeAvgScore: avgScore,
|
|
978
|
-
costDeltaPct: coerceNumber(row.cost_delta_pct),
|
|
979
|
-
detectedAt: row.detected_at
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
return out;
|
|
983
|
-
}
|
|
984
|
-
var snapshots2 = /* @__PURE__ */ new Map();
|
|
985
|
-
var runtime2;
|
|
986
|
-
var warnedOnce2 = false;
|
|
987
|
-
function isPromoteReadyBrainActive() {
|
|
988
|
-
return runtime2 !== void 0;
|
|
989
|
-
}
|
|
990
|
-
function loadPromoteReadyFindings(opts) {
|
|
991
|
-
const rt = runtime2;
|
|
992
|
-
if (!rt) return [];
|
|
993
|
-
const appId = opts.appId;
|
|
994
|
-
if (!appId) return [];
|
|
995
|
-
let snap = snapshots2.get(appId);
|
|
996
|
-
if (!snap) {
|
|
997
|
-
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
998
|
-
snapshots2.set(appId, snap);
|
|
999
|
-
}
|
|
1000
|
-
const now = Date.now();
|
|
1001
|
-
const stale = snap.expiresAt <= now;
|
|
1002
|
-
if (stale && !snap.refreshing) {
|
|
1003
|
-
snap.refreshing = true;
|
|
1004
|
-
void asyncRefresh2(rt, appId);
|
|
1005
|
-
}
|
|
1006
|
-
let rows = snap.data;
|
|
1007
|
-
if (opts.archetype) {
|
|
1008
|
-
rows = rows.filter((f) => f.archetype === opts.archetype);
|
|
1009
|
-
}
|
|
1010
|
-
if (opts.family) {
|
|
1011
|
-
rows = rows.filter((f) => f.family === opts.family);
|
|
1012
|
-
}
|
|
1013
|
-
return rows;
|
|
1014
|
-
}
|
|
1015
|
-
var pendingRefreshes2 = /* @__PURE__ */ new Map();
|
|
1016
|
-
async function asyncRefresh2(rt, appId) {
|
|
1017
|
-
const promise = doRefresh2(rt, appId);
|
|
1018
|
-
pendingRefreshes2.set(appId, promise);
|
|
1019
|
-
try {
|
|
1020
|
-
await promise;
|
|
1021
|
-
} finally {
|
|
1022
|
-
if (pendingRefreshes2.get(appId) === promise) {
|
|
1023
|
-
pendingRefreshes2.delete(appId);
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
async function doRefresh2(rt, appId) {
|
|
1028
|
-
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
1029
|
-
let snap = snapshots2.get(appId);
|
|
1030
|
-
if (!snap) {
|
|
1031
|
-
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1032
|
-
snapshots2.set(appId, snap);
|
|
1033
|
-
}
|
|
1034
|
-
try {
|
|
1035
|
-
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
1036
|
-
if (!res.ok) {
|
|
1037
|
-
throw new Error(`promote-ready ${res.status}: ${res.statusText}`);
|
|
1038
|
-
}
|
|
1039
|
-
const body = await res.json();
|
|
1040
|
-
if (runtime2 !== rt) return;
|
|
1041
|
-
const rows = Array.isArray(body) ? mapRowsToFindings2(body) : [];
|
|
1042
|
-
snap.data = rows;
|
|
1043
|
-
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1044
|
-
snap.refreshing = false;
|
|
1045
|
-
} catch (err) {
|
|
1046
|
-
if (runtime2 !== rt) return;
|
|
1047
|
-
snap.refreshing = false;
|
|
1048
|
-
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1049
|
-
if (!warnedOnce2) {
|
|
1050
|
-
warnedOnce2 = true;
|
|
1051
|
-
(rt.onError ?? defaultOnError2)(err);
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
function defaultOnError2(err) {
|
|
1056
|
-
console.warn(
|
|
1057
|
-
"[kgauto] promote-ready fetch failed (using empty fallback):",
|
|
1058
|
-
err
|
|
1059
|
-
);
|
|
1060
|
-
}
|
|
1061
|
-
function resolveFetchImpl(injected) {
|
|
1062
|
-
return injected ?? ((...args) => globalThis.fetch(...args));
|
|
1063
|
-
}
|
|
1064
|
-
function normalizeEndpoint(endpoint) {
|
|
1065
|
-
return endpoint.replace(/\/+$/, "");
|
|
1066
|
-
}
|
|
1067
|
-
async function markPromoteReadyHandled(opts) {
|
|
1068
|
-
const {
|
|
1069
|
-
appId,
|
|
1070
|
-
archetype,
|
|
1071
|
-
family,
|
|
1072
|
-
resolution,
|
|
1073
|
-
resolutionNote,
|
|
1074
|
-
brainEndpoint,
|
|
1075
|
-
brainJwt,
|
|
1076
|
-
brainAnonKey,
|
|
1077
|
-
fetch: injectedFetch
|
|
1078
|
-
} = opts;
|
|
1079
|
-
if (!appId) return { ok: false, reason: "app_id_required" };
|
|
1080
|
-
if (!archetype) return { ok: false, reason: "archetype_required" };
|
|
1081
|
-
if (!family) return { ok: false, reason: "family_required" };
|
|
1082
|
-
if (resolution !== "promoted" && resolution !== "declined" && resolution !== "still-evaluating") {
|
|
1083
|
-
return { ok: false, reason: "resolution_invalid" };
|
|
1084
|
-
}
|
|
1085
|
-
const doFetch = resolveFetchImpl(injectedFetch);
|
|
1086
|
-
const base = normalizeEndpoint(brainEndpoint);
|
|
1087
|
-
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`;
|
|
1088
|
-
const patchBody = {
|
|
1089
|
-
resolved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1090
|
-
resolution
|
|
1091
|
-
};
|
|
1092
|
-
if (resolutionNote !== void 0) {
|
|
1093
|
-
patchBody.resolution_note = resolutionNote;
|
|
1094
|
-
}
|
|
1095
|
-
let res;
|
|
1096
|
-
try {
|
|
1097
|
-
res = await doFetch(url, {
|
|
1098
|
-
method: "PATCH",
|
|
1099
|
-
headers: {
|
|
1100
|
-
Authorization: `Bearer ${brainJwt}`,
|
|
1101
|
-
apikey: brainAnonKey,
|
|
1102
|
-
"Content-Type": "application/json",
|
|
1103
|
-
Accept: "application/json",
|
|
1104
|
-
Prefer: "return=minimal"
|
|
1105
|
-
},
|
|
1106
|
-
body: JSON.stringify(patchBody)
|
|
1107
|
-
});
|
|
1108
|
-
} catch (err) {
|
|
1109
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1110
|
-
return { ok: false, reason: `network_error:${msg}` };
|
|
1111
|
-
}
|
|
1112
|
-
if (res.status === 401 || res.status === 403) {
|
|
1113
|
-
return { ok: false, reason: "brain_auth_misconfig" };
|
|
1114
|
-
}
|
|
1115
|
-
if (res.status >= 500) {
|
|
1116
|
-
return { ok: false, reason: "brain_unavailable" };
|
|
1117
|
-
}
|
|
1118
|
-
if (!res.ok) {
|
|
1119
|
-
return { ok: false, reason: `patch_failed:${res.status}` };
|
|
1120
|
-
}
|
|
1121
|
-
return { ok: true };
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
// src/advisor-rules/promote-ready.ts
|
|
1125
|
-
var PROMOTE_READY_THRESHOLDS = {
|
|
1126
|
-
minPassRate: 0.8,
|
|
1127
|
-
minAvgScore: 4
|
|
1128
|
-
};
|
|
1129
|
-
function shouldFirePromoteReady(finding, resolvedPrimary) {
|
|
1130
|
-
if (finding.currentModel !== resolvedPrimary) return false;
|
|
1131
|
-
if (finding.judgePassRate < PROMOTE_READY_THRESHOLDS.minPassRate) return false;
|
|
1132
|
-
if (finding.judgeAvgScore < PROMOTE_READY_THRESHOLDS.minAvgScore) return false;
|
|
1133
|
-
return true;
|
|
1134
|
-
}
|
|
1135
|
-
function deriveFamilyLocal(modelId) {
|
|
1136
|
-
if (modelId.startsWith("claude-opus-")) return "claude-opus";
|
|
1137
|
-
if (modelId.startsWith("claude-sonnet-")) return "claude-sonnet";
|
|
1138
|
-
if (modelId.startsWith("claude-haiku-")) return "claude-haiku";
|
|
1139
|
-
if (/^gemini-.*-flash-lite/.test(modelId)) return "gemini-flash-lite";
|
|
1140
|
-
if (/^gemini-.*-flash/.test(modelId)) return "gemini-flash";
|
|
1141
|
-
if (/^gemini-.*-pro/.test(modelId)) return "gemini-pro";
|
|
1142
|
-
if (/^deepseek-.*-pro/.test(modelId)) return "deepseek-reasoner";
|
|
1143
|
-
if (modelId.startsWith("deepseek-")) return "deepseek-chat";
|
|
1144
|
-
if (modelId.startsWith("gpt-")) return "openai-gpt";
|
|
1145
|
-
return null;
|
|
1146
|
-
}
|
|
1147
|
-
function advisorRulePromoteReady(ctx) {
|
|
1148
|
-
if (!isPromoteReadyBrainActive()) return [];
|
|
1149
|
-
if (!ctx.appId) return [];
|
|
1150
|
-
if (!ctx.resolvedPrimary) return [];
|
|
1151
|
-
const family = deriveFamilyLocal(ctx.resolvedPrimary);
|
|
1152
|
-
if (!family) return [];
|
|
1153
|
-
const findings = loadPromoteReadyFindings({
|
|
1154
|
-
appId: ctx.appId,
|
|
1155
|
-
archetype: ctx.archetype,
|
|
1156
|
-
family
|
|
1157
|
-
});
|
|
1158
|
-
if (findings.length === 0) return [];
|
|
1159
|
-
const qualifying = findings.filter(
|
|
1160
|
-
(f) => shouldFirePromoteReady(f, ctx.resolvedPrimary)
|
|
1161
|
-
);
|
|
1162
|
-
if (qualifying.length === 0) return [];
|
|
1163
|
-
qualifying.sort((a, b) => {
|
|
1164
|
-
if (a.judgeAvgScore !== b.judgeAvgScore) {
|
|
1165
|
-
return b.judgeAvgScore - a.judgeAvgScore;
|
|
1166
|
-
}
|
|
1167
|
-
return b.judgePassRate - a.judgePassRate;
|
|
1168
|
-
});
|
|
1169
|
-
const top = qualifying[0];
|
|
1170
|
-
const pctPass = Math.round(top.judgePassRate * 100);
|
|
1171
|
-
const score = top.judgeAvgScore.toFixed(2);
|
|
1172
|
-
let costClause = "";
|
|
1173
|
-
if (top.costDeltaPct !== null) {
|
|
1174
|
-
const sign = top.costDeltaPct < 0 ? "cheaper" : "more expensive";
|
|
1175
|
-
const magnitude = Math.abs(top.costDeltaPct * 100).toFixed(1);
|
|
1176
|
-
costClause = `, cost ${magnitude}% ${sign}`;
|
|
1177
|
-
}
|
|
1178
|
-
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.`;
|
|
1179
|
-
return [
|
|
1180
|
-
{
|
|
1181
|
-
level: "info",
|
|
1182
|
-
code: "promote-ready",
|
|
1183
|
-
message,
|
|
1184
|
-
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.`,
|
|
1185
|
-
// alpha.36 architectural field — not a no-ai-needed case.
|
|
1186
|
-
recommendedArchitecture: void 0,
|
|
1187
|
-
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1188
|
-
}
|
|
1189
|
-
];
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
// src/advisor-rules/consumer-on-stale-model.ts
|
|
1193
|
-
function isStaleStatus(v) {
|
|
1194
|
-
return v === "legacy" || v === "deprecated";
|
|
1195
|
-
}
|
|
1196
|
-
function asString(v) {
|
|
1197
|
-
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
1198
|
-
}
|
|
1199
|
-
function mapRowsToFindings3(rows) {
|
|
1200
|
-
const out = [];
|
|
1201
|
-
for (const raw of rows) {
|
|
1202
|
-
if (!raw || typeof raw !== "object") continue;
|
|
1203
|
-
const r = raw;
|
|
1204
|
-
const archetype = asString(r.intent_archetype) ?? asString(r.applies_to_archetype);
|
|
1205
|
-
const staleModel = asString(r.stale_model) ?? asString(r.applies_to_model);
|
|
1206
|
-
const staleProvider = asString(r.stale_provider);
|
|
1207
|
-
const recommendedModel = asString(r.recommended_model);
|
|
1208
|
-
const family = asString(r.family);
|
|
1209
|
-
const message = asString(r.message);
|
|
1210
|
-
if (!archetype || !staleModel || !recommendedModel || !family || !message) {
|
|
1211
|
-
continue;
|
|
1212
|
-
}
|
|
1213
|
-
if (!isStaleStatus(r.stale_status)) continue;
|
|
1214
|
-
const row = {
|
|
1215
|
-
archetype,
|
|
1216
|
-
staleModel,
|
|
1217
|
-
staleProvider: staleProvider ?? "unknown",
|
|
1218
|
-
staleStatus: r.stale_status,
|
|
1219
|
-
recommendedModel,
|
|
1220
|
-
family,
|
|
1221
|
-
message
|
|
1222
|
-
};
|
|
1223
|
-
const suggestion = asString(r.suggestion);
|
|
1224
|
-
if (suggestion) row.suggestion = suggestion;
|
|
1225
|
-
if (typeof r.observation_count === "number" && Number.isFinite(r.observation_count)) {
|
|
1226
|
-
row.observationCount = r.observation_count;
|
|
1227
|
-
}
|
|
1228
|
-
out.push(row);
|
|
1398
|
+
candidateModel: row.candidate_model,
|
|
1399
|
+
currentModel: row.current_model,
|
|
1400
|
+
sampleN,
|
|
1401
|
+
judgePassRate: passRate,
|
|
1402
|
+
judgeAvgScore: avgScore,
|
|
1403
|
+
costDeltaPct: coerceNumber(row.cost_delta_pct),
|
|
1404
|
+
detectedAt: row.detected_at
|
|
1405
|
+
});
|
|
1229
1406
|
}
|
|
1230
1407
|
return out;
|
|
1231
1408
|
}
|
|
1232
|
-
var
|
|
1233
|
-
var
|
|
1234
|
-
var
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
return runtime3 !== void 0;
|
|
1409
|
+
var snapshots2 = /* @__PURE__ */ new Map();
|
|
1410
|
+
var runtime2;
|
|
1411
|
+
var warnedOnce2 = false;
|
|
1412
|
+
function isPromoteReadyBrainActive() {
|
|
1413
|
+
return runtime2 !== void 0;
|
|
1238
1414
|
}
|
|
1239
|
-
function
|
|
1240
|
-
const rt =
|
|
1415
|
+
function loadPromoteReadyFindings(opts) {
|
|
1416
|
+
const rt = runtime2;
|
|
1241
1417
|
if (!rt) return [];
|
|
1242
1418
|
const appId = opts.appId;
|
|
1243
1419
|
if (!appId) return [];
|
|
1244
|
-
let snap =
|
|
1420
|
+
let snap = snapshots2.get(appId);
|
|
1245
1421
|
if (!snap) {
|
|
1246
1422
|
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1247
|
-
|
|
1423
|
+
snapshots2.set(appId, snap);
|
|
1248
1424
|
}
|
|
1249
1425
|
const now = Date.now();
|
|
1250
1426
|
const stale = snap.expiresAt <= now;
|
|
1251
1427
|
if (stale && !snap.refreshing) {
|
|
1252
1428
|
snap.refreshing = true;
|
|
1253
|
-
void
|
|
1429
|
+
void asyncRefresh2(rt, appId);
|
|
1254
1430
|
}
|
|
1431
|
+
let rows = snap.data;
|
|
1255
1432
|
if (opts.archetype) {
|
|
1256
|
-
|
|
1433
|
+
rows = rows.filter((f) => f.archetype === opts.archetype);
|
|
1257
1434
|
}
|
|
1258
|
-
|
|
1435
|
+
if (opts.family) {
|
|
1436
|
+
rows = rows.filter((f) => f.family === opts.family);
|
|
1437
|
+
}
|
|
1438
|
+
return rows;
|
|
1259
1439
|
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1440
|
+
var pendingRefreshes2 = /* @__PURE__ */ new Map();
|
|
1441
|
+
async function asyncRefresh2(rt, appId) {
|
|
1442
|
+
const promise = doRefresh2(rt, appId);
|
|
1443
|
+
pendingRefreshes2.set(appId, promise);
|
|
1263
1444
|
try {
|
|
1264
1445
|
await promise;
|
|
1265
1446
|
} finally {
|
|
1266
|
-
if (
|
|
1267
|
-
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
async function doRefresh3(rt, appId) {
|
|
1272
|
-
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
1273
|
-
let snap = snapshots3.get(appId);
|
|
1274
|
-
if (!snap) {
|
|
1275
|
-
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1276
|
-
snapshots3.set(appId, snap);
|
|
1277
|
-
}
|
|
1278
|
-
try {
|
|
1279
|
-
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
1280
|
-
if (!res.ok) {
|
|
1281
|
-
throw new Error(`stale-model findings ${res.status}: ${res.statusText}`);
|
|
1282
|
-
}
|
|
1283
|
-
const body = await res.json();
|
|
1284
|
-
if (runtime3 !== rt) return;
|
|
1285
|
-
const rows = Array.isArray(body) ? mapRowsToFindings3(body) : [];
|
|
1286
|
-
snap.data = rows;
|
|
1287
|
-
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1288
|
-
snap.refreshing = false;
|
|
1289
|
-
} catch (err) {
|
|
1290
|
-
if (runtime3 !== rt) return;
|
|
1291
|
-
snap.refreshing = false;
|
|
1292
|
-
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1293
|
-
if (!warnedOnce3) {
|
|
1294
|
-
warnedOnce3 = true;
|
|
1295
|
-
(rt.onError ?? defaultOnError3)(err);
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
function defaultOnError3(err) {
|
|
1300
|
-
console.warn(
|
|
1301
|
-
"[kgauto] stale-model findings fetch failed (using empty fallback):",
|
|
1302
|
-
err
|
|
1303
|
-
);
|
|
1304
|
-
}
|
|
1305
|
-
var CONSUMER_ON_STALE_MODEL_RULE_CODE = "consumer-on-stale-model";
|
|
1306
|
-
function advisorRuleConsumerOnStaleModel(ir) {
|
|
1307
|
-
if (!isStaleModelFindingsBrainActive()) return [];
|
|
1308
|
-
if (!ir.appId) return [];
|
|
1309
|
-
const findings = getStaleModelFindings({
|
|
1310
|
-
appId: ir.appId,
|
|
1311
|
-
archetype: ir.intent.archetype
|
|
1312
|
-
});
|
|
1313
|
-
if (findings.length === 0) return [];
|
|
1314
|
-
const ranked = [...findings].sort((a, b) => {
|
|
1315
|
-
if (a.staleStatus !== b.staleStatus) {
|
|
1316
|
-
return a.staleStatus === "deprecated" ? -1 : 1;
|
|
1317
|
-
}
|
|
1318
|
-
return a.staleModel.localeCompare(b.staleModel);
|
|
1319
|
-
});
|
|
1320
|
-
const top = ranked[0];
|
|
1321
|
-
const extraCount = findings.length - 1;
|
|
1322
|
-
const extraNote = extraCount > 0 ? ` (+ ${extraCount} more stale model${extraCount === 1 ? "" : "s"} for this archetype)` : "";
|
|
1323
|
-
return [
|
|
1324
|
-
{
|
|
1325
|
-
level: "warn",
|
|
1326
|
-
code: CONSUMER_ON_STALE_MODEL_RULE_CODE,
|
|
1327
|
-
message: `${top.message}${extraNote}`,
|
|
1328
|
-
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}.`,
|
|
1329
|
-
recommendationType: "model-swap",
|
|
1330
|
-
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1331
|
-
}
|
|
1332
|
-
];
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
// src/advisor.ts
|
|
1336
|
-
var QUALITY_FLOOR_FOR_RECOMMENDATION = 6;
|
|
1337
|
-
var TIER_DOWN_COST_RATIO = 0.5;
|
|
1338
|
-
var COST_MISMATCHED_CHOSEN_SCORE_CEILING = 7;
|
|
1339
|
-
function runAdvisor(ir, result, profile, policy, phase2) {
|
|
1340
|
-
const out = [];
|
|
1341
|
-
out.push(...detectCachingOff(ir, profile));
|
|
1342
|
-
out.push(...detectSingleChunkSystem(ir, profile));
|
|
1343
|
-
out.push(...detectToolBloat(ir, result));
|
|
1344
|
-
out.push(...detectHistoryUncached(ir, profile));
|
|
1345
|
-
out.push(...detectSingleModelArray(ir, policy));
|
|
1346
|
-
if (policy?.posture !== "locked") {
|
|
1347
|
-
out.push(...detectCostMismatchedArchetype(ir, profile, phase2));
|
|
1348
|
-
out.push(...detectModelStaleEvidence(ir, profile));
|
|
1349
|
-
out.push(...detectTierDown(ir, profile, phase2));
|
|
1350
|
-
}
|
|
1351
|
-
if (!translatorClearedToolCallCliff(phase2)) {
|
|
1352
|
-
out.push(...detectArchetypePerfFloorBreach(ir, profile));
|
|
1353
|
-
}
|
|
1354
|
-
if (policy?.posture !== "locked") {
|
|
1355
|
-
out.push(...detectStaleExclusionCandidate(ir));
|
|
1356
|
-
}
|
|
1357
|
-
if (policy?.posture !== "locked" && ir.appId) {
|
|
1358
|
-
out.push(
|
|
1359
|
-
...advisorRulePromoteReady({
|
|
1360
|
-
appId: ir.appId,
|
|
1361
|
-
archetype: ir.intent.archetype,
|
|
1362
|
-
resolvedPrimary: profile.id
|
|
1363
|
-
})
|
|
1364
|
-
);
|
|
1365
|
-
out.push(...advisorRuleConsumerOnStaleModel(ir));
|
|
1366
|
-
}
|
|
1367
|
-
return out;
|
|
1368
|
-
}
|
|
1369
|
-
function translatorClearedToolCallCliff(phase2) {
|
|
1370
|
-
const rewrites = phase2?.sectionRewritesApplied;
|
|
1371
|
-
if (!rewrites || rewrites.length === 0) return false;
|
|
1372
|
-
for (const rw of rewrites) {
|
|
1373
|
-
if (rw.kind === "tool_call_contract") return true;
|
|
1374
|
-
}
|
|
1375
|
-
return false;
|
|
1376
|
-
}
|
|
1377
|
-
function detectCachingOff(ir, profile) {
|
|
1378
|
-
if (profile.provider !== "anthropic") return [];
|
|
1379
|
-
const totalChars = ir.sections.reduce((s, sec) => s + sec.text.length, 0);
|
|
1380
|
-
if (totalChars < 2e3) return [];
|
|
1381
|
-
const anyCacheable = ir.sections.some((s) => s.cacheable === true);
|
|
1382
|
-
if (anyCacheable) return [];
|
|
1383
|
-
return [
|
|
1384
|
-
{
|
|
1385
|
-
level: "warn",
|
|
1386
|
-
code: "caching-off-on-claude",
|
|
1387
|
-
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.`,
|
|
1388
|
-
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.",
|
|
1389
|
-
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1390
|
-
}
|
|
1391
|
-
];
|
|
1392
|
-
}
|
|
1393
|
-
function detectSingleChunkSystem(ir, profile) {
|
|
1394
|
-
if (profile.provider !== "anthropic") return [];
|
|
1395
|
-
if (ir.sections.length !== 1) return [];
|
|
1396
|
-
const only = ir.sections[0];
|
|
1397
|
-
if (!only || only.text.length <= 1e3) return [];
|
|
1398
|
-
return [
|
|
1399
|
-
{
|
|
1400
|
-
level: "info",
|
|
1401
|
-
code: "single-chunk-system",
|
|
1402
|
-
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.`,
|
|
1403
|
-
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.",
|
|
1404
|
-
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1405
|
-
}
|
|
1406
|
-
];
|
|
1407
|
-
}
|
|
1408
|
-
function detectToolBloat(ir, result) {
|
|
1409
|
-
const SHORT_OUTPUT = /* @__PURE__ */ new Set([
|
|
1410
|
-
"classify",
|
|
1411
|
-
"extract",
|
|
1412
|
-
"summarize",
|
|
1413
|
-
"transform",
|
|
1414
|
-
"critique"
|
|
1415
|
-
]);
|
|
1416
|
-
if (!ir.tools || ir.tools.length === 0) return [];
|
|
1417
|
-
const toolsKept = result.diagnostics.toolsKept;
|
|
1418
|
-
if (toolsKept <= 10) return [];
|
|
1419
|
-
if (!SHORT_OUTPUT.has(ir.intent.archetype)) return [];
|
|
1420
|
-
return [
|
|
1421
|
-
{
|
|
1422
|
-
level: "warn",
|
|
1423
|
-
code: "tool-bloat",
|
|
1424
|
-
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.`,
|
|
1425
|
-
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.",
|
|
1426
|
-
docsUrl: "https://github.com/stue/kgauto/blob/main/v2/README.md#tools"
|
|
1427
|
-
}
|
|
1428
|
-
];
|
|
1429
|
-
}
|
|
1430
|
-
function detectHistoryUncached(ir, profile) {
|
|
1431
|
-
if (profile.provider !== "anthropic") return [];
|
|
1432
|
-
if (!ir.history || ir.history.length < 2) return [];
|
|
1433
|
-
if (ir.historyCachePolicy && ir.historyCachePolicy.strategy !== "none") {
|
|
1434
|
-
return [];
|
|
1435
|
-
}
|
|
1436
|
-
return [
|
|
1437
|
-
{
|
|
1438
|
-
level: "warn",
|
|
1439
|
-
code: "history-uncached-on-claude",
|
|
1440
|
-
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.`,
|
|
1441
|
-
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.",
|
|
1442
|
-
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1443
|
-
}
|
|
1444
|
-
];
|
|
1445
|
-
}
|
|
1446
|
-
function detectSingleModelArray(ir, policy) {
|
|
1447
|
-
if (ir.models.length !== 1) return [];
|
|
1448
|
-
if (policy?.posture === "locked") return [];
|
|
1449
|
-
const only = ir.models[0];
|
|
1450
|
-
return [
|
|
1451
|
-
{
|
|
1452
|
-
level: "warn",
|
|
1453
|
-
code: "single-model-array",
|
|
1454
|
-
message: `\`ir.models\` has length 1 (only "${only}") and posture is not 'locked'. A single-model chain has no safety net \u2014 the first 429 / 5xx / cliff hits the user as a failure. Master plan \xA71.2 closes the reliability gap with a 2-step minimum.`,
|
|
1455
|
-
suggestion: "Use `getDefaultFallbackChain({ archetype: ir.intent.archetype, primary: '" + only + "', posture: 'preferred' })` for a user-anchored chain, or `getDefaultFallbackChain({ archetype, posture: 'open' })` for library-picked. If single-model is intentional (compliance/brand promise), set `policy.posture = 'locked'` to silence this rule.",
|
|
1456
|
-
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#single-model-array"
|
|
1457
|
-
}
|
|
1458
|
-
];
|
|
1459
|
-
}
|
|
1460
|
-
function detectCostMismatchedArchetype(ir, profile, phase2) {
|
|
1461
|
-
if (!phase2 || phase2.fallbackChain.length === 0) return [];
|
|
1462
|
-
if (!phase2.profileResolver) return [];
|
|
1463
|
-
const archetype = ir.intent.archetype;
|
|
1464
|
-
const chosenScore = getArchetypePerfScore(profile.id, archetype);
|
|
1465
|
-
const chosenHasRoomToGrow = chosenScore.grounding === "judgment" || chosenScore.score < COST_MISMATCHED_CHOSEN_SCORE_CEILING;
|
|
1466
|
-
if (!chosenHasRoomToGrow) return [];
|
|
1467
|
-
let bestAlt = null;
|
|
1468
|
-
for (const altId of phase2.fallbackChain) {
|
|
1469
|
-
const altProfile = phase2.profileResolver(altId);
|
|
1470
|
-
if (!altProfile) continue;
|
|
1471
|
-
if (altProfile.id === profile.id) continue;
|
|
1472
|
-
const altScore = getArchetypePerfScore(altProfile.id, archetype);
|
|
1473
|
-
if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
|
|
1474
|
-
if (altScore.score < chosenScore.score) continue;
|
|
1475
|
-
if (altProfile.costInputPer1m >= profile.costInputPer1m) continue;
|
|
1476
|
-
if (!bestAlt || altScore.score > bestAlt.score.score || altScore.score === bestAlt.score.score && altProfile.costInputPer1m < bestAlt.profile.costInputPer1m) {
|
|
1477
|
-
bestAlt = { id: altId, profile: altProfile, score: altScore };
|
|
1447
|
+
if (pendingRefreshes2.get(appId) === promise) {
|
|
1448
|
+
pendingRefreshes2.delete(appId);
|
|
1478
1449
|
}
|
|
1479
1450
|
}
|
|
1480
|
-
if (!bestAlt) return [];
|
|
1481
|
-
const tierDownWouldFire = bestAlt.score.grounding === "measured" && bestAlt.profile.costInputPer1m <= profile.costInputPer1m * TIER_DOWN_COST_RATIO;
|
|
1482
|
-
if (tierDownWouldFire) return [];
|
|
1483
|
-
const chosenGrounding = chosenScore.grounding === "judgment" ? `archetypePerf.${archetype}=judgment` : `archetypePerf.${archetype}=${chosenScore.score}`;
|
|
1484
|
-
const altGrounding = bestAlt.score.grounding === "measured" ? `archetypePerf.${archetype}=${bestAlt.score.score}, measured, n=${bestAlt.score.n}` : `archetypePerf.${archetype}=${bestAlt.score.score}, judgment`;
|
|
1485
|
-
return [
|
|
1486
|
-
{
|
|
1487
|
-
level: "warn",
|
|
1488
|
-
code: "cost-mismatched-archetype",
|
|
1489
|
-
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.`,
|
|
1490
|
-
suggestion: `Consider declaring \`${bestAlt.id}\` as the primary model for this archetype, or relax to posture='open' to let kgauto select among the chain. If the chosen model is required for compliance/brand reasons, set \`policy.posture = 'locked'\` to silence this rule.`,
|
|
1491
|
-
recommendationType: profile.provider === bestAlt.profile.provider ? "tier-down" : "model-swap",
|
|
1492
|
-
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1493
|
-
}
|
|
1494
|
-
];
|
|
1495
1451
|
}
|
|
1496
|
-
function
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
recommendationType: "prompt-fix",
|
|
1508
|
-
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1452
|
+
async function doRefresh2(rt, appId) {
|
|
1453
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
1454
|
+
let snap = snapshots2.get(appId);
|
|
1455
|
+
if (!snap) {
|
|
1456
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1457
|
+
snapshots2.set(appId, snap);
|
|
1458
|
+
}
|
|
1459
|
+
try {
|
|
1460
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
1461
|
+
if (!res.ok) {
|
|
1462
|
+
throw new Error(`promote-ready ${res.status}: ${res.statusText}`);
|
|
1509
1463
|
}
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
const altScore = getArchetypePerfScore(altProfile.id, archetype);
|
|
1524
|
-
if (altScore.grounding !== "measured") continue;
|
|
1525
|
-
if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
|
|
1526
|
-
if (altScore.score < chosenScore.score) continue;
|
|
1527
|
-
if (altProfile.costInputPer1m > chosenCost * TIER_DOWN_COST_RATIO) continue;
|
|
1528
|
-
if (!bestAlt || altProfile.costInputPer1m < bestAlt.profile.costInputPer1m || altProfile.costInputPer1m === bestAlt.profile.costInputPer1m && altScore.score > bestAlt.score.score) {
|
|
1529
|
-
bestAlt = { id: altId, profile: altProfile, score: altScore };
|
|
1464
|
+
const body = await res.json();
|
|
1465
|
+
if (runtime2 !== rt) return;
|
|
1466
|
+
const rows = Array.isArray(body) ? mapRowsToFindings2(body) : [];
|
|
1467
|
+
snap.data = rows;
|
|
1468
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1469
|
+
snap.refreshing = false;
|
|
1470
|
+
} catch (err) {
|
|
1471
|
+
if (runtime2 !== rt) return;
|
|
1472
|
+
snap.refreshing = false;
|
|
1473
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1474
|
+
if (!warnedOnce2) {
|
|
1475
|
+
warnedOnce2 = true;
|
|
1476
|
+
(rt.onError ?? defaultOnError2)(err);
|
|
1530
1477
|
}
|
|
1531
1478
|
}
|
|
1532
|
-
if (!bestAlt) return [];
|
|
1533
|
-
const chosenDesc = chosenScore.grounding === "measured" ? `archetypePerf.${archetype}=${chosenScore.score} (measured, n=${chosenScore.n})` : `archetypePerf.${archetype}=${chosenScore.score} (${chosenScore.grounding})`;
|
|
1534
|
-
return [
|
|
1535
|
-
{
|
|
1536
|
-
level: "warn",
|
|
1537
|
-
code: "tier-down",
|
|
1538
|
-
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.`,
|
|
1539
|
-
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.`,
|
|
1540
|
-
recommendationType: "tier-down",
|
|
1541
|
-
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1542
|
-
}
|
|
1543
|
-
];
|
|
1544
1479
|
}
|
|
1545
|
-
function
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1480
|
+
function defaultOnError2(err) {
|
|
1481
|
+
console.warn(
|
|
1482
|
+
"[kgauto] promote-ready fetch failed (using empty fallback):",
|
|
1483
|
+
err
|
|
1484
|
+
);
|
|
1485
|
+
}
|
|
1486
|
+
function resolveFetchImpl(injected) {
|
|
1487
|
+
return injected ?? ((...args) => globalThis.fetch(...args));
|
|
1488
|
+
}
|
|
1489
|
+
function normalizeEndpoint(endpoint) {
|
|
1490
|
+
return endpoint.replace(/\/+$/, "");
|
|
1491
|
+
}
|
|
1492
|
+
async function markPromoteReadyHandled(opts) {
|
|
1493
|
+
const {
|
|
1494
|
+
appId,
|
|
1495
|
+
archetype,
|
|
1496
|
+
family,
|
|
1497
|
+
resolution,
|
|
1498
|
+
resolutionNote,
|
|
1499
|
+
brainEndpoint,
|
|
1500
|
+
brainJwt,
|
|
1501
|
+
brainAnonKey,
|
|
1502
|
+
fetch: injectedFetch
|
|
1503
|
+
} = opts;
|
|
1504
|
+
if (!appId) return { ok: false, reason: "app_id_required" };
|
|
1505
|
+
if (!archetype) return { ok: false, reason: "archetype_required" };
|
|
1506
|
+
if (!family) return { ok: false, reason: "family_required" };
|
|
1507
|
+
if (resolution !== "promoted" && resolution !== "declined" && resolution !== "still-evaluating") {
|
|
1508
|
+
return { ok: false, reason: "resolution_invalid" };
|
|
1563
1509
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1510
|
+
const doFetch = resolveFetchImpl(injectedFetch);
|
|
1511
|
+
const base = normalizeEndpoint(brainEndpoint);
|
|
1512
|
+
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`;
|
|
1513
|
+
const patchBody = {
|
|
1514
|
+
resolved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1515
|
+
resolution
|
|
1516
|
+
};
|
|
1517
|
+
if (resolutionNote !== void 0) {
|
|
1518
|
+
patchBody.resolution_note = resolutionNote;
|
|
1519
|
+
}
|
|
1520
|
+
let res;
|
|
1521
|
+
try {
|
|
1522
|
+
res = await doFetch(url, {
|
|
1523
|
+
method: "PATCH",
|
|
1524
|
+
headers: {
|
|
1525
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
1526
|
+
apikey: brainAnonKey,
|
|
1527
|
+
"Content-Type": "application/json",
|
|
1528
|
+
Accept: "application/json",
|
|
1529
|
+
Prefer: "return=minimal"
|
|
1530
|
+
},
|
|
1531
|
+
body: JSON.stringify(patchBody)
|
|
1532
|
+
});
|
|
1533
|
+
} catch (err) {
|
|
1534
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1535
|
+
return { ok: false, reason: `network_error:${msg}` };
|
|
1536
|
+
}
|
|
1537
|
+
if (res.status === 401 || res.status === 403) {
|
|
1538
|
+
return { ok: false, reason: "brain_auth_misconfig" };
|
|
1539
|
+
}
|
|
1540
|
+
if (res.status >= 500) {
|
|
1541
|
+
return { ok: false, reason: "brain_unavailable" };
|
|
1542
|
+
}
|
|
1543
|
+
if (!res.ok) {
|
|
1544
|
+
return { ok: false, reason: `patch_failed:${res.status}` };
|
|
1545
|
+
}
|
|
1546
|
+
return { ok: true };
|
|
1574
1547
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1548
|
+
|
|
1549
|
+
// src/advisor-rules/promote-ready.ts
|
|
1550
|
+
var PROMOTE_READY_THRESHOLDS = {
|
|
1551
|
+
minPassRate: 0.8,
|
|
1552
|
+
minAvgScore: 4
|
|
1553
|
+
};
|
|
1554
|
+
function shouldFirePromoteReady(finding, resolvedPrimary) {
|
|
1555
|
+
if (finding.currentModel !== resolvedPrimary) return false;
|
|
1556
|
+
if (finding.judgePassRate < PROMOTE_READY_THRESHOLDS.minPassRate) return false;
|
|
1557
|
+
if (finding.judgeAvgScore < PROMOTE_READY_THRESHOLDS.minAvgScore) return false;
|
|
1558
|
+
return true;
|
|
1559
|
+
}
|
|
1560
|
+
function deriveFamilyLocal(modelId) {
|
|
1561
|
+
if (modelId.startsWith("claude-opus-")) return "claude-opus";
|
|
1562
|
+
if (modelId.startsWith("claude-sonnet-")) return "claude-sonnet";
|
|
1563
|
+
if (modelId.startsWith("claude-haiku-")) return "claude-haiku";
|
|
1564
|
+
if (/^gemini-.*-flash-lite/.test(modelId)) return "gemini-flash-lite";
|
|
1565
|
+
if (/^gemini-.*-flash/.test(modelId)) return "gemini-flash";
|
|
1566
|
+
if (/^gemini-.*-pro/.test(modelId)) return "gemini-pro";
|
|
1567
|
+
if (/^deepseek-.*-pro/.test(modelId)) return "deepseek-reasoner";
|
|
1568
|
+
if (modelId.startsWith("deepseek-")) return "deepseek-chat";
|
|
1569
|
+
if (modelId.startsWith("gpt-")) return "openai-gpt";
|
|
1570
|
+
return null;
|
|
1571
|
+
}
|
|
1572
|
+
function advisorRulePromoteReady(ctx) {
|
|
1573
|
+
if (!isPromoteReadyBrainActive()) return [];
|
|
1574
|
+
if (!ctx.appId) return [];
|
|
1575
|
+
if (!ctx.resolvedPrimary) return [];
|
|
1576
|
+
const family = deriveFamilyLocal(ctx.resolvedPrimary);
|
|
1577
|
+
if (!family) return [];
|
|
1578
|
+
const findings = loadPromoteReadyFindings({
|
|
1579
|
+
appId: ctx.appId,
|
|
1580
|
+
archetype: ctx.archetype,
|
|
1581
|
+
family
|
|
1581
1582
|
});
|
|
1582
1583
|
if (findings.length === 0) return [];
|
|
1583
|
-
const
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1584
|
+
const qualifying = findings.filter(
|
|
1585
|
+
(f) => shouldFirePromoteReady(f, ctx.resolvedPrimary)
|
|
1586
|
+
);
|
|
1587
|
+
if (qualifying.length === 0) return [];
|
|
1588
|
+
qualifying.sort((a, b) => {
|
|
1589
|
+
if (a.judgeAvgScore !== b.judgeAvgScore) {
|
|
1590
|
+
return b.judgeAvgScore - a.judgeAvgScore;
|
|
1591
|
+
}
|
|
1592
|
+
return b.judgePassRate - a.judgePassRate;
|
|
1588
1593
|
});
|
|
1589
|
-
const top =
|
|
1590
|
-
const
|
|
1591
|
-
const
|
|
1594
|
+
const top = qualifying[0];
|
|
1595
|
+
const pctPass = Math.round(top.judgePassRate * 100);
|
|
1596
|
+
const score = top.judgeAvgScore.toFixed(2);
|
|
1597
|
+
let costClause = "";
|
|
1598
|
+
if (top.costDeltaPct !== null) {
|
|
1599
|
+
const sign = top.costDeltaPct < 0 ? "cheaper" : "more expensive";
|
|
1600
|
+
const magnitude = Math.abs(top.costDeltaPct * 100).toFixed(1);
|
|
1601
|
+
costClause = `, cost ${magnitude}% ${sign}`;
|
|
1602
|
+
}
|
|
1603
|
+
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.`;
|
|
1592
1604
|
return [
|
|
1593
1605
|
{
|
|
1594
1606
|
level: "info",
|
|
1595
|
-
code: "
|
|
1596
|
-
message
|
|
1597
|
-
suggestion: top.
|
|
1598
|
-
|
|
1607
|
+
code: "promote-ready",
|
|
1608
|
+
message,
|
|
1609
|
+
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.`,
|
|
1610
|
+
// alpha.36 architectural field — not a no-ai-needed case.
|
|
1611
|
+
recommendedArchitecture: void 0,
|
|
1599
1612
|
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1600
1613
|
}
|
|
1601
1614
|
];
|
|
1602
1615
|
}
|
|
1603
|
-
function confidenceRank(c) {
|
|
1604
|
-
if (c === "high") return 3;
|
|
1605
|
-
if (c === "medium") return 2;
|
|
1606
|
-
return 1;
|
|
1607
|
-
}
|
|
1608
1616
|
|
|
1609
|
-
// src/
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
if (!
|
|
1620
|
-
const
|
|
1621
|
-
|
|
1622
|
-
|
|
1617
|
+
// src/advisor-rules/consumer-on-stale-model.ts
|
|
1618
|
+
function isStaleStatus(v) {
|
|
1619
|
+
return v === "legacy" || v === "deprecated";
|
|
1620
|
+
}
|
|
1621
|
+
function asString(v) {
|
|
1622
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
1623
|
+
}
|
|
1624
|
+
function mapRowsToFindings3(rows) {
|
|
1625
|
+
const out = [];
|
|
1626
|
+
for (const raw of rows) {
|
|
1627
|
+
if (!raw || typeof raw !== "object") continue;
|
|
1628
|
+
const r = raw;
|
|
1629
|
+
const archetype = asString(r.intent_archetype) ?? asString(r.applies_to_archetype);
|
|
1630
|
+
const staleModel = asString(r.stale_model) ?? asString(r.applies_to_model);
|
|
1631
|
+
const staleProvider = asString(r.stale_provider);
|
|
1632
|
+
const recommendedModel = asString(r.recommended_model);
|
|
1633
|
+
const family = asString(r.family);
|
|
1634
|
+
const message = asString(r.message);
|
|
1635
|
+
if (!archetype || !staleModel || !recommendedModel || !family || !message) {
|
|
1636
|
+
continue;
|
|
1623
1637
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1638
|
+
if (!isStaleStatus(r.stale_status)) continue;
|
|
1639
|
+
const row = {
|
|
1640
|
+
archetype,
|
|
1641
|
+
staleModel,
|
|
1642
|
+
staleProvider: staleProvider ?? "unknown",
|
|
1643
|
+
staleStatus: r.stale_status,
|
|
1644
|
+
recommendedModel,
|
|
1645
|
+
family,
|
|
1646
|
+
message
|
|
1628
1647
|
};
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
if (
|
|
1632
|
-
|
|
1633
|
-
id: RULE_NARRATION_DRIFT_ANTHROPIC,
|
|
1634
|
-
preamble: NARRATION_DRIFT_ANTHROPIC_PREAMBLE
|
|
1635
|
-
};
|
|
1636
|
-
}
|
|
1637
|
-
if (profile.provider === "deepseek") {
|
|
1638
|
-
return {
|
|
1639
|
-
id: RULE_NARRATION_THINKING_LEAK_DEEPSEEK,
|
|
1640
|
-
preamble: NARRATION_THINKING_LEAK_DEEPSEEK_PREAMBLE
|
|
1641
|
-
};
|
|
1648
|
+
const suggestion = asString(r.suggestion);
|
|
1649
|
+
if (suggestion) row.suggestion = suggestion;
|
|
1650
|
+
if (typeof r.observation_count === "number" && Number.isFinite(r.observation_count)) {
|
|
1651
|
+
row.observationCount = r.observation_count;
|
|
1642
1652
|
}
|
|
1643
|
-
|
|
1653
|
+
out.push(row);
|
|
1644
1654
|
}
|
|
1645
|
-
return
|
|
1655
|
+
return out;
|
|
1646
1656
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1657
|
+
var snapshots3 = /* @__PURE__ */ new Map();
|
|
1658
|
+
var runtime3;
|
|
1659
|
+
var warnedOnce3 = false;
|
|
1660
|
+
var pendingRefreshes3 = /* @__PURE__ */ new Map();
|
|
1661
|
+
function isStaleModelFindingsBrainActive() {
|
|
1662
|
+
return runtime3 !== void 0;
|
|
1663
|
+
}
|
|
1664
|
+
function getStaleModelFindings(opts) {
|
|
1665
|
+
const rt = runtime3;
|
|
1666
|
+
if (!rt) return [];
|
|
1667
|
+
const appId = opts.appId;
|
|
1668
|
+
if (!appId) return [];
|
|
1669
|
+
let snap = snapshots3.get(appId);
|
|
1670
|
+
if (!snap) {
|
|
1671
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1672
|
+
snapshots3.set(appId, snap);
|
|
1651
1673
|
}
|
|
1652
|
-
const
|
|
1653
|
-
const
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
const originalText = section.text;
|
|
1658
|
-
const transformedText = `${rule.preamble}
|
|
1659
|
-
|
|
1660
|
-
${originalText}`;
|
|
1661
|
-
rewrites.push({
|
|
1662
|
-
sectionId: section.id,
|
|
1663
|
-
kind: section.kind,
|
|
1664
|
-
rule: rule.id,
|
|
1665
|
-
originalText,
|
|
1666
|
-
transformedText,
|
|
1667
|
-
...rule.wireOverrides ? { wireOverrides: rule.wireOverrides } : {}
|
|
1668
|
-
});
|
|
1669
|
-
return { ...section, text: transformedText };
|
|
1670
|
-
});
|
|
1671
|
-
if (rewrites.length === 0) {
|
|
1672
|
-
return { rewrittenIR: ir, rewrites: [] };
|
|
1674
|
+
const now = Date.now();
|
|
1675
|
+
const stale = snap.expiresAt <= now;
|
|
1676
|
+
if (stale && !snap.refreshing) {
|
|
1677
|
+
snap.refreshing = true;
|
|
1678
|
+
void asyncRefresh3(rt, appId);
|
|
1673
1679
|
}
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
// src/models-brain.ts
|
|
1679
|
-
function isModelRow(x) {
|
|
1680
|
-
if (!x || typeof x !== "object") return false;
|
|
1681
|
-
const r = x;
|
|
1682
|
-
return typeof r.model_id === "string" && typeof r.provider === "string";
|
|
1683
|
-
}
|
|
1684
|
-
function isAliasRow(x) {
|
|
1685
|
-
if (!x || typeof x !== "object") return false;
|
|
1686
|
-
const r = x;
|
|
1687
|
-
return typeof r.alias_id === "string" && typeof r.canonical_id === "string";
|
|
1680
|
+
if (opts.archetype) {
|
|
1681
|
+
return snap.data.filter((f) => f.archetype === opts.archetype);
|
|
1682
|
+
}
|
|
1683
|
+
return snap.data;
|
|
1688
1684
|
}
|
|
1689
|
-
function
|
|
1685
|
+
async function asyncRefresh3(rt, appId) {
|
|
1686
|
+
const promise = doRefresh3(rt, appId);
|
|
1687
|
+
pendingRefreshes3.set(appId, promise);
|
|
1690
1688
|
try {
|
|
1691
|
-
|
|
1692
|
-
|
|
1689
|
+
await promise;
|
|
1690
|
+
} finally {
|
|
1691
|
+
if (pendingRefreshes3.get(appId) === promise) {
|
|
1692
|
+
pendingRefreshes3.delete(appId);
|
|
1693
1693
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
async function doRefresh3(rt, appId) {
|
|
1697
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
1698
|
+
let snap = snapshots3.get(appId);
|
|
1699
|
+
if (!snap) {
|
|
1700
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1701
|
+
snapshots3.set(appId, snap);
|
|
1702
|
+
}
|
|
1703
|
+
try {
|
|
1704
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
1705
|
+
if (!res.ok) {
|
|
1706
|
+
throw new Error(`stale-model findings ${res.status}: ${res.statusText}`);
|
|
1696
1707
|
}
|
|
1697
|
-
|
|
1698
|
-
|
|
1708
|
+
const body = await res.json();
|
|
1709
|
+
if (runtime3 !== rt) return;
|
|
1710
|
+
const rows = Array.isArray(body) ? mapRowsToFindings3(body) : [];
|
|
1711
|
+
snap.data = rows;
|
|
1712
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1713
|
+
snap.refreshing = false;
|
|
1714
|
+
} catch (err) {
|
|
1715
|
+
if (runtime3 !== rt) return;
|
|
1716
|
+
snap.refreshing = false;
|
|
1717
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1718
|
+
if (!warnedOnce3) {
|
|
1719
|
+
warnedOnce3 = true;
|
|
1720
|
+
(rt.onError ?? defaultOnError3)(err);
|
|
1699
1721
|
}
|
|
1700
|
-
return {
|
|
1701
|
-
id: row.model_id,
|
|
1702
|
-
provider: row.provider,
|
|
1703
|
-
status: row.status ?? "current",
|
|
1704
|
-
maxContextTokens: row.max_context_tokens ?? 0,
|
|
1705
|
-
maxOutputTokens: row.max_output_tokens ?? 0,
|
|
1706
|
-
maxTools: row.max_tools ?? 0,
|
|
1707
|
-
parallelToolCalls: row.parallel_tool_calls ?? false,
|
|
1708
|
-
structuredOutput: row.structured_output ?? "none",
|
|
1709
|
-
systemPromptMode: row.system_prompt_mode ?? "inline",
|
|
1710
|
-
streaming: row.streaming ?? true,
|
|
1711
|
-
cliffs: row.cliffs ?? [],
|
|
1712
|
-
costInputPer1m: row.cost_input_per_1m ?? 0,
|
|
1713
|
-
costOutputPer1m: row.cost_output_per_1m ?? 0,
|
|
1714
|
-
lowering: row.lowering ?? { system: { mode: "inline" }, cache: { strategy: "unsupported" } },
|
|
1715
|
-
recovery: row.recovery ?? [],
|
|
1716
|
-
strengths: row.strengths ?? [],
|
|
1717
|
-
weaknesses: row.weaknesses ?? [],
|
|
1718
|
-
notes: row.notes ?? void 0,
|
|
1719
|
-
verifiedAgainstDocs: row.verified_against_docs ?? void 0,
|
|
1720
|
-
archetypePerf: row.archetype_perf ?? void 0,
|
|
1721
|
-
// alpha.41 — family-resolution fields. `family` may be null pre-
|
|
1722
|
-
// migration 024; runtime falls back to deriveFamilyFromModelId at
|
|
1723
|
-
// resolution time (see family-resolution.ts, G1).
|
|
1724
|
-
family: row.family ?? void 0,
|
|
1725
|
-
versionAdded: row.version_added ?? void 0,
|
|
1726
|
-
active: row.active ?? void 0
|
|
1727
|
-
};
|
|
1728
|
-
} catch {
|
|
1729
|
-
return null;
|
|
1730
1722
|
}
|
|
1731
1723
|
}
|
|
1732
|
-
function
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
else if (profile.versionAdded !== void 0) row.version_added = profile.versionAdded;
|
|
1767
|
-
if (opts.versionRemoved !== void 0) row.version_removed = opts.versionRemoved;
|
|
1768
|
-
return row;
|
|
1724
|
+
function defaultOnError3(err) {
|
|
1725
|
+
console.warn(
|
|
1726
|
+
"[kgauto] stale-model findings fetch failed (using empty fallback):",
|
|
1727
|
+
err
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
var CONSUMER_ON_STALE_MODEL_RULE_CODE = "consumer-on-stale-model";
|
|
1731
|
+
function advisorRuleConsumerOnStaleModel(ir) {
|
|
1732
|
+
if (!isStaleModelFindingsBrainActive()) return [];
|
|
1733
|
+
if (!ir.appId) return [];
|
|
1734
|
+
const findings = getStaleModelFindings({
|
|
1735
|
+
appId: ir.appId,
|
|
1736
|
+
archetype: ir.intent.archetype
|
|
1737
|
+
});
|
|
1738
|
+
if (findings.length === 0) return [];
|
|
1739
|
+
const ranked = [...findings].sort((a, b) => {
|
|
1740
|
+
if (a.staleStatus !== b.staleStatus) {
|
|
1741
|
+
return a.staleStatus === "deprecated" ? -1 : 1;
|
|
1742
|
+
}
|
|
1743
|
+
return a.staleModel.localeCompare(b.staleModel);
|
|
1744
|
+
});
|
|
1745
|
+
const top = ranked[0];
|
|
1746
|
+
const extraCount = findings.length - 1;
|
|
1747
|
+
const extraNote = extraCount > 0 ? ` (+ ${extraCount} more stale model${extraCount === 1 ? "" : "s"} for this archetype)` : "";
|
|
1748
|
+
return [
|
|
1749
|
+
{
|
|
1750
|
+
level: "warn",
|
|
1751
|
+
code: CONSUMER_ON_STALE_MODEL_RULE_CODE,
|
|
1752
|
+
message: `${top.message}${extraNote}`,
|
|
1753
|
+
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}.`,
|
|
1754
|
+
recommendationType: "model-swap",
|
|
1755
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1756
|
+
}
|
|
1757
|
+
];
|
|
1769
1758
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1759
|
+
|
|
1760
|
+
// src/archetype-fits.ts
|
|
1761
|
+
var ARCHETYPE_FAMILY_FITS = Object.freeze([
|
|
1762
|
+
{
|
|
1763
|
+
archetype: "plan",
|
|
1764
|
+
betterFitFamily: "deepseek-reasoner",
|
|
1765
|
+
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).",
|
|
1766
|
+
costGuidance: "substantially cheaper at current pricing (deepseek-v4-pro promo: ~7-17x cheaper than sonnet)"
|
|
1767
|
+
},
|
|
1768
|
+
{
|
|
1769
|
+
archetype: "critique",
|
|
1770
|
+
betterFitFamily: "deepseek-reasoner",
|
|
1771
|
+
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.",
|
|
1772
|
+
costGuidance: "comparable or cheaper at current pricing"
|
|
1776
1773
|
}
|
|
1777
|
-
|
|
1774
|
+
]);
|
|
1775
|
+
function findBetterFit(archetype, currentFamily) {
|
|
1776
|
+
for (const fit of ARCHETYPE_FAMILY_FITS) {
|
|
1777
|
+
if (fit.archetype !== archetype) continue;
|
|
1778
|
+
if (fit.betterFitFamily === currentFamily) return null;
|
|
1779
|
+
return fit;
|
|
1780
|
+
}
|
|
1781
|
+
return null;
|
|
1778
1782
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1783
|
+
|
|
1784
|
+
// src/advisor-rules/cross-family-fit.ts
|
|
1785
|
+
function familyHasCurrentActiveModel(family) {
|
|
1786
|
+
for (const profile of allProfiles()) {
|
|
1787
|
+
const profileFamily = profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
1788
|
+
if (profileFamily !== family) continue;
|
|
1789
|
+
if (profile.status !== "current") continue;
|
|
1790
|
+
if (profile.active === false) continue;
|
|
1791
|
+
return true;
|
|
1784
1792
|
}
|
|
1785
|
-
return
|
|
1793
|
+
return false;
|
|
1786
1794
|
}
|
|
1787
|
-
function
|
|
1788
|
-
|
|
1795
|
+
function listCandidatesInFamily(family) {
|
|
1796
|
+
const candidates = [];
|
|
1797
|
+
for (const profile of allProfiles()) {
|
|
1798
|
+
const profileFamily = profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
1799
|
+
if (profileFamily !== family) continue;
|
|
1800
|
+
if (profile.status !== "current") continue;
|
|
1801
|
+
if (profile.active === false) continue;
|
|
1802
|
+
candidates.push(profile.id);
|
|
1803
|
+
if (candidates.length >= 3) break;
|
|
1804
|
+
}
|
|
1805
|
+
return candidates;
|
|
1789
1806
|
}
|
|
1790
|
-
function
|
|
1791
|
-
|
|
1807
|
+
function advisorRuleCrossFamilyFit(ctx) {
|
|
1808
|
+
if (!ctx.resolvedPrimary) return [];
|
|
1809
|
+
const currentFamily = deriveFamilyFromModelId(ctx.resolvedPrimary);
|
|
1810
|
+
if (!currentFamily) return [];
|
|
1811
|
+
const fit = findBetterFit(ctx.archetype, currentFamily);
|
|
1812
|
+
if (!fit) return [];
|
|
1813
|
+
if (!familyHasCurrentActiveModel(fit.betterFitFamily)) return [];
|
|
1814
|
+
const candidates = listCandidatesInFamily(fit.betterFitFamily);
|
|
1815
|
+
if (candidates.length === 0) return [];
|
|
1816
|
+
const candidateStr = candidates.join(", ");
|
|
1817
|
+
const message = `Your ${currentFamily} call on ${ctx.archetype} could shift to ${fit.betterFitFamily} \u2014 typically better quality + ${fit.costGuidance}. Suggested candidates: ${candidateStr}.`;
|
|
1818
|
+
return [
|
|
1819
|
+
{
|
|
1820
|
+
level: "info",
|
|
1821
|
+
code: "cross-family-fit-candidate",
|
|
1822
|
+
ownership: "consumer-actionable",
|
|
1823
|
+
message,
|
|
1824
|
+
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.`,
|
|
1825
|
+
recommendationType: "model-swap",
|
|
1826
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1827
|
+
}
|
|
1828
|
+
];
|
|
1792
1829
|
}
|
|
1793
|
-
var loadModelsFromBrain = createBrainQueryCache({
|
|
1794
|
-
table: "kgauto_models",
|
|
1795
|
-
mapRows: mapRowsToModels,
|
|
1796
|
-
bundledFallback: bundledModels
|
|
1797
|
-
});
|
|
1798
|
-
var loadAliasesFromBrain = createBrainQueryCache({
|
|
1799
|
-
table: "kgauto_aliases",
|
|
1800
|
-
mapRows: mapRowsToAliases,
|
|
1801
|
-
bundledFallback: bundledAliases
|
|
1802
|
-
});
|
|
1803
|
-
_setProfileBrainHook({
|
|
1804
|
-
getProfile: (canonical) => loadModelsFromBrain().get(canonical),
|
|
1805
|
-
resolveAlias: (id) => loadAliasesFromBrain()[id]
|
|
1806
|
-
});
|
|
1807
1830
|
|
|
1808
|
-
// src/
|
|
1809
|
-
var
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1831
|
+
// src/advisor.ts
|
|
1832
|
+
var QUALITY_FLOOR_FOR_RECOMMENDATION = 6;
|
|
1833
|
+
var TIER_DOWN_COST_RATIO = 0.5;
|
|
1834
|
+
var COST_MISMATCHED_CHOSEN_SCORE_CEILING = 7;
|
|
1835
|
+
var PRODUCER_OWNED_RULE_CODES = Object.freeze(
|
|
1836
|
+
/* @__PURE__ */ new Set(["model-stale-evidence", "promote-ready"])
|
|
1837
|
+
);
|
|
1838
|
+
function deriveOwnership(code, selfDeclared) {
|
|
1839
|
+
if (selfDeclared) return selfDeclared;
|
|
1840
|
+
return PRODUCER_OWNED_RULE_CODES.has(code) ? "producer-owned" : "consumer-actionable";
|
|
1841
|
+
}
|
|
1842
|
+
function runAdvisor(ir, result, profile, policy, phase2) {
|
|
1843
|
+
const out = [];
|
|
1844
|
+
out.push(...detectCachingOff(ir, profile));
|
|
1845
|
+
out.push(...detectSingleChunkSystem(ir, profile));
|
|
1846
|
+
out.push(...detectToolBloat(ir, result));
|
|
1847
|
+
out.push(...detectHistoryUncached(ir, profile));
|
|
1848
|
+
out.push(...detectSingleModelArray(ir, policy));
|
|
1849
|
+
if (policy?.posture !== "locked") {
|
|
1850
|
+
out.push(...detectCostMismatchedArchetype(ir, profile, phase2));
|
|
1851
|
+
out.push(...detectModelStaleEvidence(ir, profile));
|
|
1852
|
+
out.push(...detectTierDown(ir, profile, phase2));
|
|
1819
1853
|
}
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
if (typeof modelId !== "string" || modelId.length === 0) return null;
|
|
1823
|
-
if (modelId.startsWith("claude-opus-")) return "claude-opus";
|
|
1824
|
-
if (modelId.startsWith("claude-sonnet-")) return "claude-sonnet";
|
|
1825
|
-
if (modelId.startsWith("claude-haiku-")) return "claude-haiku";
|
|
1826
|
-
if (modelId.startsWith("gemini-") && modelId.includes("flash-lite")) {
|
|
1827
|
-
return "gemini-flash-lite";
|
|
1854
|
+
if (!translatorClearedToolCallCliff(phase2)) {
|
|
1855
|
+
out.push(...detectArchetypePerfFloorBreach(ir, profile));
|
|
1828
1856
|
}
|
|
1829
|
-
if (
|
|
1830
|
-
|
|
1857
|
+
if (policy?.posture !== "locked") {
|
|
1858
|
+
out.push(...detectStaleExclusionCandidate(ir));
|
|
1831
1859
|
}
|
|
1832
|
-
if (
|
|
1833
|
-
|
|
1860
|
+
if (policy?.posture !== "locked" && ir.appId) {
|
|
1861
|
+
out.push(
|
|
1862
|
+
...advisorRulePromoteReady({
|
|
1863
|
+
appId: ir.appId,
|
|
1864
|
+
archetype: ir.intent.archetype,
|
|
1865
|
+
resolvedPrimary: profile.id
|
|
1866
|
+
})
|
|
1867
|
+
);
|
|
1868
|
+
out.push(...advisorRuleConsumerOnStaleModel(ir));
|
|
1834
1869
|
}
|
|
1835
|
-
if (
|
|
1836
|
-
|
|
1870
|
+
if (policy?.posture !== "locked") {
|
|
1871
|
+
out.push(
|
|
1872
|
+
...advisorRuleCrossFamilyFit({
|
|
1873
|
+
archetype: ir.intent.archetype,
|
|
1874
|
+
resolvedPrimary: profile.id
|
|
1875
|
+
})
|
|
1876
|
+
);
|
|
1837
1877
|
}
|
|
1838
|
-
|
|
1839
|
-
if (modelId.startsWith("gpt-")) return "openai-gpt";
|
|
1840
|
-
return null;
|
|
1878
|
+
return out;
|
|
1841
1879
|
}
|
|
1842
|
-
function
|
|
1843
|
-
|
|
1880
|
+
function translatorClearedToolCallCliff(phase2) {
|
|
1881
|
+
const rewrites = phase2?.sectionRewritesApplied;
|
|
1882
|
+
if (!rewrites || rewrites.length === 0) return false;
|
|
1883
|
+
for (const rw of rewrites) {
|
|
1884
|
+
if (rw.kind === "tool_call_contract") return true;
|
|
1885
|
+
}
|
|
1886
|
+
return false;
|
|
1887
|
+
}
|
|
1888
|
+
function detectCachingOff(ir, profile) {
|
|
1889
|
+
if (profile.provider !== "anthropic") return [];
|
|
1890
|
+
const totalChars = ir.sections.reduce((s, sec) => s + sec.text.length, 0);
|
|
1891
|
+
if (totalChars < 2e3) return [];
|
|
1892
|
+
const anyCacheable = ir.sections.some((s) => s.cacheable === true);
|
|
1893
|
+
if (anyCacheable) return [];
|
|
1894
|
+
return [
|
|
1895
|
+
{
|
|
1896
|
+
level: "warn",
|
|
1897
|
+
code: "caching-off-on-claude",
|
|
1898
|
+
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.`,
|
|
1899
|
+
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.",
|
|
1900
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1901
|
+
}
|
|
1902
|
+
];
|
|
1903
|
+
}
|
|
1904
|
+
function detectSingleChunkSystem(ir, profile) {
|
|
1905
|
+
if (profile.provider !== "anthropic") return [];
|
|
1906
|
+
if (ir.sections.length !== 1) return [];
|
|
1907
|
+
const only = ir.sections[0];
|
|
1908
|
+
if (!only || only.text.length <= 1e3) return [];
|
|
1909
|
+
return [
|
|
1910
|
+
{
|
|
1911
|
+
level: "info",
|
|
1912
|
+
code: "single-chunk-system",
|
|
1913
|
+
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.`,
|
|
1914
|
+
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.",
|
|
1915
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1916
|
+
}
|
|
1917
|
+
];
|
|
1918
|
+
}
|
|
1919
|
+
function detectToolBloat(ir, result) {
|
|
1920
|
+
const SHORT_OUTPUT = /* @__PURE__ */ new Set([
|
|
1921
|
+
"classify",
|
|
1922
|
+
"extract",
|
|
1923
|
+
"summarize",
|
|
1924
|
+
"transform",
|
|
1925
|
+
"critique"
|
|
1926
|
+
]);
|
|
1927
|
+
if (!ir.tools || ir.tools.length === 0) return [];
|
|
1928
|
+
const toolsKept = result.diagnostics.toolsKept;
|
|
1929
|
+
if (toolsKept <= 10) return [];
|
|
1930
|
+
if (!SHORT_OUTPUT.has(ir.intent.archetype)) return [];
|
|
1931
|
+
return [
|
|
1932
|
+
{
|
|
1933
|
+
level: "warn",
|
|
1934
|
+
code: "tool-bloat",
|
|
1935
|
+
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.`,
|
|
1936
|
+
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.",
|
|
1937
|
+
docsUrl: "https://github.com/stue/kgauto/blob/main/v2/README.md#tools"
|
|
1938
|
+
}
|
|
1939
|
+
];
|
|
1940
|
+
}
|
|
1941
|
+
function detectHistoryUncached(ir, profile) {
|
|
1942
|
+
if (profile.provider !== "anthropic") return [];
|
|
1943
|
+
if (!ir.history || ir.history.length < 2) return [];
|
|
1944
|
+
if (ir.historyCachePolicy && ir.historyCachePolicy.strategy !== "none") {
|
|
1945
|
+
return [];
|
|
1946
|
+
}
|
|
1947
|
+
return [
|
|
1948
|
+
{
|
|
1949
|
+
level: "warn",
|
|
1950
|
+
code: "history-uncached-on-claude",
|
|
1951
|
+
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.`,
|
|
1952
|
+
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.",
|
|
1953
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1954
|
+
}
|
|
1955
|
+
];
|
|
1956
|
+
}
|
|
1957
|
+
function detectSingleModelArray(ir, policy) {
|
|
1958
|
+
if (ir.models.length !== 1) return [];
|
|
1959
|
+
if (policy?.posture === "locked") return [];
|
|
1960
|
+
const only = ir.models[0];
|
|
1961
|
+
return [
|
|
1962
|
+
{
|
|
1963
|
+
level: "warn",
|
|
1964
|
+
code: "single-model-array",
|
|
1965
|
+
message: `\`ir.models\` has length 1 (only "${only}") and posture is not 'locked'. A single-model chain has no safety net \u2014 the first 429 / 5xx / cliff hits the user as a failure. Master plan \xA71.2 closes the reliability gap with a 2-step minimum.`,
|
|
1966
|
+
suggestion: "Use `getDefaultFallbackChain({ archetype: ir.intent.archetype, primary: '" + only + "', posture: 'preferred' })` for a user-anchored chain, or `getDefaultFallbackChain({ archetype, posture: 'open' })` for library-picked. If single-model is intentional (compliance/brand promise), set `policy.posture = 'locked'` to silence this rule.",
|
|
1967
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#single-model-array"
|
|
1968
|
+
}
|
|
1969
|
+
];
|
|
1970
|
+
}
|
|
1971
|
+
function detectCostMismatchedArchetype(ir, profile, phase2) {
|
|
1972
|
+
if (!phase2 || phase2.fallbackChain.length === 0) return [];
|
|
1973
|
+
if (!phase2.profileResolver) return [];
|
|
1974
|
+
const archetype = ir.intent.archetype;
|
|
1975
|
+
const chosenScore = getArchetypePerfScore(profile.id, archetype);
|
|
1976
|
+
const chosenHasRoomToGrow = chosenScore.grounding === "judgment" || chosenScore.score < COST_MISMATCHED_CHOSEN_SCORE_CEILING;
|
|
1977
|
+
if (!chosenHasRoomToGrow) return [];
|
|
1978
|
+
let bestAlt = null;
|
|
1979
|
+
for (const altId of phase2.fallbackChain) {
|
|
1980
|
+
const altProfile = phase2.profileResolver(altId);
|
|
1981
|
+
if (!altProfile) continue;
|
|
1982
|
+
if (altProfile.id === profile.id) continue;
|
|
1983
|
+
const altScore = getArchetypePerfScore(altProfile.id, archetype);
|
|
1984
|
+
if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
|
|
1985
|
+
if (altScore.score < chosenScore.score) continue;
|
|
1986
|
+
if (altProfile.costInputPer1m >= profile.costInputPer1m) continue;
|
|
1987
|
+
if (!bestAlt || altScore.score > bestAlt.score.score || altScore.score === bestAlt.score.score && altProfile.costInputPer1m < bestAlt.profile.costInputPer1m) {
|
|
1988
|
+
bestAlt = { id: altId, profile: altProfile, score: altScore };
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
if (!bestAlt) return [];
|
|
1992
|
+
const tierDownWouldFire = bestAlt.score.grounding === "measured" && bestAlt.profile.costInputPer1m <= profile.costInputPer1m * TIER_DOWN_COST_RATIO;
|
|
1993
|
+
if (tierDownWouldFire) return [];
|
|
1994
|
+
const chosenGrounding = chosenScore.grounding === "judgment" ? `archetypePerf.${archetype}=judgment` : `archetypePerf.${archetype}=${chosenScore.score}`;
|
|
1995
|
+
const altGrounding = bestAlt.score.grounding === "measured" ? `archetypePerf.${archetype}=${bestAlt.score.score}, measured, n=${bestAlt.score.n}` : `archetypePerf.${archetype}=${bestAlt.score.score}, judgment`;
|
|
1996
|
+
return [
|
|
1997
|
+
{
|
|
1998
|
+
level: "warn",
|
|
1999
|
+
code: "cost-mismatched-archetype",
|
|
2000
|
+
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.`,
|
|
2001
|
+
suggestion: `Consider declaring \`${bestAlt.id}\` as the primary model for this archetype, or relax to posture='open' to let kgauto select among the chain. If the chosen model is required for compliance/brand reasons, set \`policy.posture = 'locked'\` to silence this rule.`,
|
|
2002
|
+
recommendationType: profile.provider === bestAlt.profile.provider ? "tier-down" : "model-swap",
|
|
2003
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2004
|
+
}
|
|
2005
|
+
];
|
|
1844
2006
|
}
|
|
1845
|
-
function
|
|
1846
|
-
if (!
|
|
1847
|
-
const
|
|
1848
|
-
|
|
2007
|
+
function detectModelStaleEvidence(ir, profile) {
|
|
2008
|
+
if (!isBrainQueryActiveFor("kgauto_archetype_perf")) return [];
|
|
2009
|
+
const archetype = ir.intent.archetype;
|
|
2010
|
+
const chosen = getArchetypePerfScore(profile.id, archetype);
|
|
2011
|
+
if (chosen.grounding !== "judgment") return [];
|
|
2012
|
+
return [
|
|
2013
|
+
{
|
|
2014
|
+
level: "info",
|
|
2015
|
+
code: "model-stale-evidence",
|
|
2016
|
+
message: `Model-stale-evidence: target=${profile.id} archetype=${archetype} is judgment-grounded (n=${chosen.n}) despite brain-query mode being active. Measurement substrate is wired but the brain hasn't accumulated >=10 outcomes for this (model, archetype) tuple yet \u2014 routing decisions remain pre-measured for this slot.`,
|
|
2017
|
+
suggestion: "Verify that `record()` is being called on every call() outcome with the appropriate `actualModel` and `mutationsApplied` fields. Once the brain accumulates n>=10 rows on this tuple, the score promotes from judgment to measured automatically (5-min SWR cache). No code change required from your side \u2014 this is the substrate signaling the gap.",
|
|
2018
|
+
recommendationType: "prompt-fix",
|
|
2019
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2020
|
+
}
|
|
2021
|
+
];
|
|
1849
2022
|
}
|
|
1850
|
-
function
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
2023
|
+
function detectTierDown(ir, profile, phase2) {
|
|
2024
|
+
if (!phase2 || phase2.fallbackChain.length === 0) return [];
|
|
2025
|
+
if (!phase2.profileResolver) return [];
|
|
2026
|
+
const archetype = ir.intent.archetype;
|
|
2027
|
+
const chosenScore = getArchetypePerfScore(profile.id, archetype);
|
|
2028
|
+
const chosenCost = profile.costInputPer1m;
|
|
2029
|
+
let bestAlt = null;
|
|
2030
|
+
for (const altId of phase2.fallbackChain) {
|
|
2031
|
+
const altProfile = phase2.profileResolver(altId);
|
|
2032
|
+
if (!altProfile) continue;
|
|
2033
|
+
if (altProfile.id === profile.id) continue;
|
|
2034
|
+
const altScore = getArchetypePerfScore(altProfile.id, archetype);
|
|
2035
|
+
if (altScore.grounding !== "measured") continue;
|
|
2036
|
+
if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
|
|
2037
|
+
if (altScore.score < chosenScore.score) continue;
|
|
2038
|
+
if (altProfile.costInputPer1m > chosenCost * TIER_DOWN_COST_RATIO) continue;
|
|
2039
|
+
if (!bestAlt || altProfile.costInputPer1m < bestAlt.profile.costInputPer1m || altProfile.costInputPer1m === bestAlt.profile.costInputPer1m && altScore.score > bestAlt.score.score) {
|
|
2040
|
+
bestAlt = { id: altId, profile: altProfile, score: altScore };
|
|
1857
2041
|
}
|
|
1858
2042
|
}
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
2043
|
+
if (!bestAlt) return [];
|
|
2044
|
+
const chosenDesc = chosenScore.grounding === "measured" ? `archetypePerf.${archetype}=${chosenScore.score} (measured, n=${chosenScore.n})` : `archetypePerf.${archetype}=${chosenScore.score} (${chosenScore.grounding})`;
|
|
2045
|
+
return [
|
|
2046
|
+
{
|
|
2047
|
+
level: "warn",
|
|
2048
|
+
code: "tier-down",
|
|
2049
|
+
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.`,
|
|
2050
|
+
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.`,
|
|
2051
|
+
recommendationType: "tier-down",
|
|
2052
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1865
2053
|
}
|
|
1866
|
-
|
|
1867
|
-
|
|
2054
|
+
];
|
|
2055
|
+
}
|
|
2056
|
+
function detectArchetypePerfFloorBreach(ir, profile) {
|
|
2057
|
+
const compat = getModelCompatibility(profile.id, {
|
|
2058
|
+
archetype: ir.intent.archetype,
|
|
2059
|
+
toolOrchestration: ir.constraints?.toolOrchestration
|
|
2060
|
+
});
|
|
2061
|
+
if (compat.status === "compatible") return [];
|
|
2062
|
+
if (compat.status === "requires-adapter") {
|
|
2063
|
+
return [
|
|
2064
|
+
{
|
|
2065
|
+
level: "warn",
|
|
2066
|
+
code: "archetype-perf-floor-breach",
|
|
2067
|
+
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}`,
|
|
2068
|
+
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.`,
|
|
2069
|
+
recommendationType: "prompt-fix",
|
|
2070
|
+
suggestedAdaptation: compat.adapter,
|
|
2071
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2072
|
+
}
|
|
2073
|
+
];
|
|
1868
2074
|
}
|
|
1869
|
-
return
|
|
2075
|
+
return [
|
|
2076
|
+
{
|
|
2077
|
+
level: "critical",
|
|
2078
|
+
code: "archetype-perf-floor-breach",
|
|
2079
|
+
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}`,
|
|
2080
|
+
suggestion: `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.`,
|
|
2081
|
+
recommendationType: "model-swap",
|
|
2082
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2083
|
+
}
|
|
2084
|
+
];
|
|
1870
2085
|
}
|
|
1871
|
-
function
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
const vA = a.versionAdded ?? "";
|
|
1878
|
-
const vB = b.versionAdded ?? "";
|
|
1879
|
-
if (vA !== vB) return vA < vB ? 1 : -1;
|
|
1880
|
-
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
2086
|
+
function detectStaleExclusionCandidate(ir) {
|
|
2087
|
+
if (!isExclusionFindingsBrainActive()) return [];
|
|
2088
|
+
if (!ir.appId) return [];
|
|
2089
|
+
const findings = getStaleExclusionFindings({
|
|
2090
|
+
appId: ir.appId,
|
|
2091
|
+
archetype: ir.intent.archetype
|
|
1881
2092
|
});
|
|
1882
|
-
return
|
|
2093
|
+
if (findings.length === 0) return [];
|
|
2094
|
+
const ranked = [...findings].sort((a, b) => {
|
|
2095
|
+
const sa = a.estimatedSavingsUsd30d ?? -Infinity;
|
|
2096
|
+
const sb = b.estimatedSavingsUsd30d ?? -Infinity;
|
|
2097
|
+
if (sa !== sb) return sb - sa;
|
|
2098
|
+
return confidenceRank(b.confidence) - confidenceRank(a.confidence);
|
|
2099
|
+
});
|
|
2100
|
+
const top = ranked[0];
|
|
2101
|
+
const extraCount = findings.length - 1;
|
|
2102
|
+
const extraNote = extraCount > 0 ? ` (+ ${extraCount} more excluded model${extraCount === 1 ? "" : "s"} for this archetype)` : "";
|
|
2103
|
+
return [
|
|
2104
|
+
{
|
|
2105
|
+
level: "info",
|
|
2106
|
+
code: "stale-exclusion-candidate",
|
|
2107
|
+
message: `${top.message}${extraNote}`,
|
|
2108
|
+
suggestion: top.suggestion,
|
|
2109
|
+
recommendationType: "tier-down",
|
|
2110
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2111
|
+
}
|
|
2112
|
+
];
|
|
1883
2113
|
}
|
|
1884
|
-
function
|
|
1885
|
-
if (
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
registry,
|
|
1889
|
-
opts.family,
|
|
1890
|
-
opts.archetype,
|
|
1891
|
-
opts.appId
|
|
1892
|
-
);
|
|
1893
|
-
if (candidates.length === 0) return opts.fallback;
|
|
1894
|
-
const sorted = sortCandidates(candidates, opts.archetype);
|
|
1895
|
-
return sorted[0]?.id ?? opts.fallback;
|
|
2114
|
+
function confidenceRank(c) {
|
|
2115
|
+
if (c === "high") return 3;
|
|
2116
|
+
if (c === "medium") return 2;
|
|
2117
|
+
return 1;
|
|
1896
2118
|
}
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2119
|
+
|
|
2120
|
+
// src/translator.ts
|
|
2121
|
+
var TRANSLATOR_FLOOR = ARCHETYPE_FLOOR_DEFAULT;
|
|
2122
|
+
var RULE_SEQUENTIAL_TOOL_CLIFF = "sequential-tool-cliff-below-floor";
|
|
2123
|
+
var RULE_NARRATION_DRIFT_ANTHROPIC = "narration-drift-anthropic";
|
|
2124
|
+
var RULE_NARRATION_THINKING_LEAK_DEEPSEEK = "narration-thinking-leak-deepseek";
|
|
2125
|
+
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.";
|
|
2126
|
+
var NARRATION_DRIFT_ANTHROPIC_PREAMBLE = "Output ONLY the requested content. Do not narrate your thought process. Each line \u2264 12 words.";
|
|
2127
|
+
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.";
|
|
2128
|
+
function matchRule(kind, profile, archetype) {
|
|
2129
|
+
if (kind === "tool_call_contract") {
|
|
2130
|
+
if (!profile.archetypePerf) return null;
|
|
2131
|
+
const archetypeScore = profile.archetypePerf[archetype];
|
|
2132
|
+
if (typeof archetypeScore !== "number" || archetypeScore >= TRANSLATOR_FLOOR) {
|
|
2133
|
+
return null;
|
|
2134
|
+
}
|
|
2135
|
+
return {
|
|
2136
|
+
id: RULE_SEQUENTIAL_TOOL_CLIFF,
|
|
2137
|
+
preamble: SEQUENTIAL_TOOL_PREAMBLE,
|
|
2138
|
+
wireOverrides: { parallelToolCalls: false }
|
|
2139
|
+
};
|
|
1903
2140
|
}
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
);
|
|
1911
|
-
if (candidates.length === 0) {
|
|
1912
|
-
let anyInFamily = false;
|
|
1913
|
-
for (const profile of registry.values()) {
|
|
1914
|
-
if (familyOf(profile) === family) {
|
|
1915
|
-
anyInFamily = true;
|
|
1916
|
-
break;
|
|
1917
|
-
}
|
|
2141
|
+
if (kind === "narration_contract") {
|
|
2142
|
+
if (profile.provider === "anthropic") {
|
|
2143
|
+
return {
|
|
2144
|
+
id: RULE_NARRATION_DRIFT_ANTHROPIC,
|
|
2145
|
+
preamble: NARRATION_DRIFT_ANTHROPIC_PREAMBLE
|
|
2146
|
+
};
|
|
1918
2147
|
}
|
|
1919
|
-
|
|
1920
|
-
|
|
2148
|
+
if (profile.provider === "deepseek") {
|
|
2149
|
+
return {
|
|
2150
|
+
id: RULE_NARRATION_THINKING_LEAK_DEEPSEEK,
|
|
2151
|
+
preamble: NARRATION_THINKING_LEAK_DEEPSEEK_PREAMBLE
|
|
2152
|
+
};
|
|
2153
|
+
}
|
|
2154
|
+
return null;
|
|
1921
2155
|
}
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
);
|
|
2156
|
+
return null;
|
|
2157
|
+
}
|
|
2158
|
+
function applySectionRewrites(args) {
|
|
2159
|
+
const { ir, profile, archetype } = args;
|
|
2160
|
+
if (!Array.isArray(ir.sections) || ir.sections.length === 0) {
|
|
2161
|
+
return { rewrittenIR: ir, rewrites: [] };
|
|
1929
2162
|
}
|
|
1930
|
-
|
|
2163
|
+
const rewrites = [];
|
|
2164
|
+
const newSections = ir.sections.map((section) => {
|
|
2165
|
+
if (!section.kind || section.kind === "arbitrary") return section;
|
|
2166
|
+
const rule = matchRule(section.kind, profile, archetype);
|
|
2167
|
+
if (!rule) return section;
|
|
2168
|
+
const originalText = section.text;
|
|
2169
|
+
const transformedText = `${rule.preamble}
|
|
2170
|
+
|
|
2171
|
+
${originalText}`;
|
|
2172
|
+
rewrites.push({
|
|
2173
|
+
sectionId: section.id,
|
|
2174
|
+
kind: section.kind,
|
|
2175
|
+
rule: rule.id,
|
|
2176
|
+
originalText,
|
|
2177
|
+
transformedText,
|
|
2178
|
+
...rule.wireOverrides ? { wireOverrides: rule.wireOverrides } : {}
|
|
2179
|
+
});
|
|
2180
|
+
return { ...section, text: transformedText };
|
|
2181
|
+
});
|
|
2182
|
+
if (rewrites.length === 0) {
|
|
2183
|
+
return { rewrittenIR: ir, rewrites: [] };
|
|
2184
|
+
}
|
|
2185
|
+
const rewrittenIR = { ...ir, sections: newSections };
|
|
2186
|
+
return { rewrittenIR, rewrites };
|
|
1931
2187
|
}
|
|
1932
2188
|
|
|
1933
2189
|
// src/compile.ts
|
|
@@ -1974,6 +2230,9 @@ function compile(ir, opts = {}) {
|
|
|
1974
2230
|
const cliffs = passApplyCliffs(workingIR, profile, inputTokens);
|
|
1975
2231
|
workingIR = cliffs.value.ir;
|
|
1976
2232
|
accumulatedMutations.push(...cliffs.mutations);
|
|
2233
|
+
const conventions = passApplyConventions(workingIR, profile);
|
|
2234
|
+
workingIR = conventions.value.ir;
|
|
2235
|
+
accumulatedMutations.push(...conventions.mutations);
|
|
1977
2236
|
const translated = applySectionRewrites({
|
|
1978
2237
|
ir: workingIR,
|
|
1979
2238
|
profile,
|
|
@@ -2025,7 +2284,15 @@ function compile(ir, opts = {}) {
|
|
|
2025
2284
|
toolOrchestration: ir.constraints?.toolOrchestration,
|
|
2026
2285
|
// alpha.33 — see top-of-block comment.
|
|
2027
2286
|
historyCacheMarkIndex,
|
|
2028
|
-
systemCacheMarkIndex
|
|
2287
|
+
systemCacheMarkIndex,
|
|
2288
|
+
// alpha.43 — cliff-style warnings emitted by passApplyConventions.
|
|
2289
|
+
// Merge convention-pass cliffWarnings with any cliff-guard quality
|
|
2290
|
+
// warnings the cliff pass surfaced (the same shape — informational
|
|
2291
|
+
// text the consumer can route on without changing behavior).
|
|
2292
|
+
cliffWarnings: [
|
|
2293
|
+
...cliffs.value.loweringHints.qualityWarning ?? [],
|
|
2294
|
+
...conventions.value.cliffWarnings
|
|
2295
|
+
]
|
|
2029
2296
|
};
|
|
2030
2297
|
if (ir.intent.archetype === "hunt" && ir.constraints?.toolOrchestration === "sequential") {
|
|
2031
2298
|
accumulatedMutations.push({
|
|
@@ -2042,7 +2309,7 @@ function compile(ir, opts = {}) {
|
|
|
2042
2309
|
return void 0;
|
|
2043
2310
|
}
|
|
2044
2311
|
} : tryGetProfile;
|
|
2045
|
-
const
|
|
2312
|
+
const rawAdvisories = runAdvisor(
|
|
2046
2313
|
ir,
|
|
2047
2314
|
{
|
|
2048
2315
|
target: profile.id,
|
|
@@ -2063,6 +2330,11 @@ function compile(ir, opts = {}) {
|
|
|
2063
2330
|
sectionRewritesApplied
|
|
2064
2331
|
}
|
|
2065
2332
|
);
|
|
2333
|
+
const advisories = rawAdvisories.map((a) => ({
|
|
2334
|
+
...a,
|
|
2335
|
+
kgautoRequestId: handle,
|
|
2336
|
+
ownership: deriveOwnership(a.code, a.ownership)
|
|
2337
|
+
}));
|
|
2066
2338
|
return {
|
|
2067
2339
|
handle,
|
|
2068
2340
|
target: profile.id,
|
|
@@ -3904,6 +4176,7 @@ export {
|
|
|
3904
4176
|
ABSOLUTE_FLOOR,
|
|
3905
4177
|
ALIASES,
|
|
3906
4178
|
ALL_ARCHETYPES,
|
|
4179
|
+
ARCHETYPE_FAMILY_FITS,
|
|
3907
4180
|
ARCHETYPE_FLOOR_DEFAULT,
|
|
3908
4181
|
CallError,
|
|
3909
4182
|
DEFAULT_FINDINGS_ENDPOINT,
|
|
@@ -3911,10 +4184,12 @@ export {
|
|
|
3911
4184
|
FamilyResolutionError,
|
|
3912
4185
|
INTENT_ARCHETYPES,
|
|
3913
4186
|
MEASURED_GROUNDING_MIN_N,
|
|
4187
|
+
PRODUCER_OWNED_RULE_CODES,
|
|
3914
4188
|
PROVIDER_ENV_KEYS,
|
|
3915
4189
|
RULE_SEQUENTIAL_TOOL_CLIFF,
|
|
3916
4190
|
TRANSLATOR_FLOOR,
|
|
3917
4191
|
allProfiles,
|
|
4192
|
+
applyArchetypeConvention,
|
|
3918
4193
|
applySectionRewrites,
|
|
3919
4194
|
attachCacheControlToStreamTextInput,
|
|
3920
4195
|
bucketContext,
|
|
@@ -3927,7 +4202,9 @@ export {
|
|
|
3927
4202
|
configureBrain,
|
|
3928
4203
|
countTokens,
|
|
3929
4204
|
deriveFamilyFromModelId,
|
|
4205
|
+
deriveOwnership,
|
|
3930
4206
|
execute,
|
|
4207
|
+
findBetterFit,
|
|
3931
4208
|
getActionableAdvisories,
|
|
3932
4209
|
getAllStarterChains,
|
|
3933
4210
|
getAllStarterChainsWithGrounding,
|
|
@@ -3962,9 +4239,11 @@ export {
|
|
|
3962
4239
|
markPromoteReadyHandled,
|
|
3963
4240
|
profileToRow,
|
|
3964
4241
|
profilesByProvider,
|
|
4242
|
+
readBrainReadEnv,
|
|
3965
4243
|
record,
|
|
3966
4244
|
recordOutcome,
|
|
3967
4245
|
resetTokenizer,
|
|
4246
|
+
resolveConventionsForProfile,
|
|
3968
4247
|
resolvePricingAt,
|
|
3969
4248
|
resolveProviderKey,
|
|
3970
4249
|
runAdvisor,
|