@kal-elsam/kairo-runtime 0.16.0 → 0.17.0
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/CHANGELOG.md +50 -0
- package/package.json +2 -1
- package/scripts/cockpit-smoke.mjs +1 -1
- package/scripts/ux-smoke-test.sh +3 -3
- package/src/cli.js +96 -11
- package/src/global/agent-capabilities/create-capability-adapter.js +2 -2
- package/src/global/architect/architect-cli.js +76 -0
- package/src/global/architect/architect-codex.js +146 -0
- package/src/global/architect/architect-manager.js +125 -0
- package/src/global/architect/architect-store.js +377 -0
- package/src/global/architect/architect-types.js +47 -0
- package/src/global/cli-help.js +10 -1
- package/src/global/cockpit/app.js +475 -0
- package/src/global/cockpit/card.js +111 -0
- package/src/global/cockpit/cli.js +33 -0
- package/src/global/cockpit/gauge.js +31 -0
- package/src/global/cockpit/project-overlay.js +683 -0
- package/src/global/cockpit/rows.js +148 -0
- package/src/global/cockpit/theme.js +118 -0
- package/src/global/cockpit/view.js +1263 -0
- package/src/global/conversation/bootstrap-analyzer-adapters.js +251 -0
- package/src/global/conversation/cli.js +53 -0
- package/src/global/conversation/codex-sandbox.js +230 -0
- package/src/global/conversation/cursor-sandbox.js +215 -0
- package/src/global/conversation/project-analysis.js +204 -0
- package/src/global/conversation/project-profile.js +178 -0
- package/src/global/conversation/project-router.js +149 -0
- package/src/global/conversation/project-strategy-store.js +64 -0
- package/src/global/conversation/project-strategy.js +514 -0
- package/src/global/conversation/sanitized-snapshot.js +169 -0
- package/src/global/conversation/secret-scanner.js +71 -0
- package/src/global/conversation/service.js +1063 -0
- package/src/global/conversation/session-store.js +75 -0
- package/src/global/conversation/transcript-store.js +79 -0
- package/src/global/conversation/ui.js +195 -0
- package/src/global/intelligence/capability-scoring.js +480 -0
- package/src/global/intelligence/execution-router.js +444 -0
- package/src/global/intelligence/kairo-telemetry-source.js +59 -0
- package/src/global/intelligence/kairobench-runner.js +85 -0
- package/src/global/intelligence/kairobench-source.js +34 -0
- package/src/global/intelligence/kairobench-tasks.js +47 -0
- package/src/global/intelligence/model-candidate-catalog.js +456 -0
- package/src/global/intelligence/model-capability-registry-sources.js +145 -0
- package/src/global/intelligence/model-capability-registry.js +125 -0
- package/src/global/intelligence/model-intelligence.js +1646 -0
- package/src/global/intelligence/official-benchmark-snapshots.js +162 -0
- package/src/global/intelligence/quick-ask.js +149 -0
- package/src/global/intelligence/role-profiles.js +251 -0
- package/src/global/intelligence/skill-catalog.js +67 -0
- package/src/global/intelligence/subscription-pressure-source.js +41 -0
- package/src/global/mcp/kairo-mcp.js +51 -18
- package/src/global/mcp/work-snapshot-rule.js +4 -2
- package/src/global/mcp/workspace-binding.js +88 -0
- package/src/global/mcp/workspace-mcp-entry.js +74 -0
- package/src/global/mcp-install.js +8 -1
- package/src/global/observability/artificial-analysis-models.js +118 -0
- package/src/global/observability/claude-models.js +31 -0
- package/src/global/observability/claude-usage.js +112 -0
- package/src/global/observability/codex-models.js +96 -0
- package/src/global/observability/codex-usage.js +160 -0
- package/src/global/observability/cursor-auth.js +88 -0
- package/src/global/observability/cursor-models.js +101 -0
- package/src/global/observability/huggingface-leaderboard.js +97 -0
- package/src/global/observability/opencode-models.js +101 -0
- package/src/global/observability/opencode-usage.js +162 -0
- package/src/global/paths.js +49 -2
- package/src/global/profile.js +23 -1
- package/src/global/runtime/execution-adapters/claude.js +63 -30
- package/src/global/runtime/execution-adapters/codex.js +9 -2
- package/src/global/runtime/execution-adapters/create-execution-adapter.js +6 -1
- package/src/global/runtime/execution-adapters/opencode.js +83 -18
- package/src/global/runtime/execution-worktree-manager.js +924 -0
- package/src/global/runtime/execution-worktree-orchestrator.js +194 -0
- package/src/global/runtime/execution-worktree-store.js +83 -0
- package/src/global/runtime/execution-worktree-types.js +45 -0
- package/src/global/runtime/run-events.js +38 -0
- package/src/global/runtime/run-manager.js +22 -6
- package/src/global/runtime/run-supervisor.js +41 -12
- package/src/global/runtime/usage-manager.js +96 -0
- package/src/global/runtime/usage-store.js +69 -0
- package/src/global/runtime/usage-types.js +62 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
// Robust multi-metric role scoring: percentile-normalized, confidence-aware,
|
|
2
|
+
// never a fabricated zero for missing evidence. Consumes the same raw
|
|
3
|
+
// CapabilityRegistry evidence (model-capability-registry.js) that AI TEAM
|
|
4
|
+
// always has — this module owns turning that evidence into a per-role
|
|
5
|
+
// score; it never collects evidence itself.
|
|
6
|
+
//
|
|
7
|
+
// Why percentiles, not the AA composite indices directly: AA's
|
|
8
|
+
// intelligenceIndex/codingIndex are already a blend Kairo doesn't control
|
|
9
|
+
// (which benchmarks, what weights) and mix 0-100/0-1 scales across
|
|
10
|
+
// sources. Ranking on percentile position within Kairo's own real,
|
|
11
|
+
// accessible candidate pool sidesteps the whole scale question — relative
|
|
12
|
+
// order is scale-invariant — and stops one blended vendor number from
|
|
13
|
+
// silently deciding a role Kairo could score on its own real component
|
|
14
|
+
// benchmarks instead.
|
|
15
|
+
//
|
|
16
|
+
// Percentile alone can't decide EVERYTHING, though: with Kairo's typical
|
|
17
|
+
// real candidate pool (2-3 accessible providers), a percentile rank is
|
|
18
|
+
// always exactly {0, 0.5, 1} — it encodes ORDER, never real magnitude, by
|
|
19
|
+
// construction. Two models 1% apart and two models 40% apart both just
|
|
20
|
+
// read "1 vs 0" with only two real candidates. So this module exports two
|
|
21
|
+
// parallel, deliberately different numbers per model: capabilityPercentile
|
|
22
|
+
// (robust rank position — decides WHO'S AHEAD) and a separate real,
|
|
23
|
+
// scale-normalized gap value (decides HOW CLOSE — near-equivalence bands
|
|
24
|
+
// and capability floors in model-intelligence.js compare THIS, never the
|
|
25
|
+
// percentile, so small pools keep real granularity instead of collapsing
|
|
26
|
+
// every non-winner to a floor-failing 0).
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {"reasoning"|"coding"|"terminalExecution"|"softwareExecution"|"instructionFollowing"} Capability
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* One real, distinct benchmark identity per row — `metricAliases` lists
|
|
34
|
+
* every metric NAME any connected source uses for that SAME real
|
|
35
|
+
* benchmark (e.g. AA's free API reports GPQA as "gpqa"; a manufacturer's
|
|
36
|
+
* own launch-page table reports the identical benchmark as
|
|
37
|
+
* "gpqa-diamond"). Deduplicated via bestEvidence-style pick (verified
|
|
38
|
+
* first, then most recent) BEFORE ranking, so the same real benchmark
|
|
39
|
+
* reported by two sources counts once, never twice in a capability's
|
|
40
|
+
* median. Distinct real benchmarks (GPQA vs HLE — Humanity's Last Exam is
|
|
41
|
+
* a different exam, not a rename) always stay separate rows.
|
|
42
|
+
*
|
|
43
|
+
* terminalBenchV2/terminalBenchHard/terminal-bench/terminal-bench-science
|
|
44
|
+
* are pooled as one identity here, inheriting the same grouping this
|
|
45
|
+
* codebase's CANONICAL_CAPABILITIES table already uses elsewhere — not a
|
|
46
|
+
* new decision introduced by this module.
|
|
47
|
+
*
|
|
48
|
+
* tauBanking is deliberately excluded from softwareExecution: it's kept
|
|
49
|
+
* as a separate agentic/tool-use signal, never counted as software
|
|
50
|
+
* engineering capability, per explicit product decision.
|
|
51
|
+
* Each alias also carries its own real `scale` ("unit": 0-1 fraction, as
|
|
52
|
+
* AA's free API and most independent evals report; "hundred": 0-100
|
|
53
|
+
* score, as manufacturer launch tables tend to report) — used ONLY by the
|
|
54
|
+
* real-gap calculation below, never by the percentile ranking (which is
|
|
55
|
+
* scale-invariant by construction).
|
|
56
|
+
* @type {Array<{benchmarkId: string, capability: Capability, metricAliases: Array<{metric: string, scale: "unit"|"hundred"}>}>}
|
|
57
|
+
*/
|
|
58
|
+
export const BENCHMARK_IDENTITIES = [
|
|
59
|
+
{ benchmarkId: "gpqa", capability: "reasoning", metricAliases: [{ metric: "gpqa", scale: "unit" }, { metric: "gpqa-diamond", scale: "hundred" }] },
|
|
60
|
+
{ benchmarkId: "hle", capability: "reasoning", metricAliases: [{ metric: "hle", scale: "unit" }] },
|
|
61
|
+
{ benchmarkId: "mmlu-pro", capability: "reasoning", metricAliases: [{ metric: "mmluPro", scale: "unit" }] },
|
|
62
|
+
{ benchmarkId: "livecodebench", capability: "coding", metricAliases: [{ metric: "liveCodeBench", scale: "unit" }] },
|
|
63
|
+
{ benchmarkId: "scicode", capability: "coding", metricAliases: [{ metric: "sciCode", scale: "unit" }] },
|
|
64
|
+
{ benchmarkId: "terminal-bench", capability: "terminalExecution", metricAliases: [
|
|
65
|
+
{ metric: "terminalBenchV2", scale: "unit" }, { metric: "terminalBenchHard", scale: "unit" },
|
|
66
|
+
{ metric: "terminal-bench", scale: "hundred" }, { metric: "terminal-bench-science", scale: "hundred" }
|
|
67
|
+
] },
|
|
68
|
+
{ benchmarkId: "cursorbench", capability: "softwareExecution", metricAliases: [{ metric: "cursorbench", scale: "hundred" }] },
|
|
69
|
+
{ benchmarkId: "kairo-success", capability: "softwareExecution", metricAliases: [{ metric: "kairo.success", scale: "unit" }] },
|
|
70
|
+
{ benchmarkId: "ifbench", capability: "instructionFollowing", metricAliases: [{ metric: "ifBench", scale: "unit" }] }
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
// A capability falls back to AA's own composite index ONLY when none of
|
|
74
|
+
// its component benchmarks (above) produced a real, comparable cohort
|
|
75
|
+
// anywhere in the current candidate pool — never alongside real component
|
|
76
|
+
// benchmarks (that would double-count the same underlying capability:
|
|
77
|
+
// once via its real components, once via the blended index that already
|
|
78
|
+
// includes them).
|
|
79
|
+
export const COMPOSITE_FALLBACKS = { reasoning: "intelligenceIndex", coding: "codingIndex" };
|
|
80
|
+
|
|
81
|
+
function activeBenchmarkIdentities(capability) {
|
|
82
|
+
return BENCHMARK_IDENTITIES.filter((identity) => identity.capability === capability);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** How many of a capability's real, distinct benchmark identities exist at all — reasoning: 3 (gpqa/hle/mmlu-pro), coding: 2, terminalExecution/instructionFollowing: 1 each. Static reference data (never per-model) — used by /models --evidence to render "X/Y benchmarks" alongside a real model's own count. */
|
|
86
|
+
export function activeBenchmarkCountForCapability(capability) {
|
|
87
|
+
return activeBenchmarkIdentities(capability).length;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* How many of this capability's real, distinct benchmark identities this
|
|
92
|
+
* EXACT model has real evidence for — counting identities (AA's "hle" and
|
|
93
|
+
* Hugging Face's "hle" are the SAME real benchmark, counted once), never
|
|
94
|
+
* raw evidence entries or sources. Deliberately independent of whether a
|
|
95
|
+
* comparable cohort formed for ranking (percentileForBenchmark's own,
|
|
96
|
+
* separate concern) — this measures how much of the capability THIS
|
|
97
|
+
* candidate's own evidence actually covers, regardless of who else is in
|
|
98
|
+
* the pool.
|
|
99
|
+
* @param {ReturnType<import("./model-capability-registry.js").createCapabilityRegistry>} registry
|
|
100
|
+
* @param {object} model
|
|
101
|
+
* @param {Capability} capability
|
|
102
|
+
* @returns {number}
|
|
103
|
+
*/
|
|
104
|
+
export function countModelBenchmarks(registry, model, capability) {
|
|
105
|
+
const id = registry.registerIdentity(model.adapterId, model.modelId, model.displayName ?? null);
|
|
106
|
+
let count = 0;
|
|
107
|
+
for (const identity of activeBenchmarkIdentities(capability)) {
|
|
108
|
+
if (bestAcrossAliases(registry, id, identity)) count += 1;
|
|
109
|
+
}
|
|
110
|
+
return count;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// A capability is "comparable" once real, distinct benchmark coverage
|
|
114
|
+
// reaches at least half of its real active benchmarks, rounded UP —
|
|
115
|
+
// reasoning (3 active) needs 2, coding (2 active) needs 1, and a
|
|
116
|
+
// capability with only ever one real known benchmark (terminalExecution,
|
|
117
|
+
// instructionFollowing) needs just that one — the 50% floor never
|
|
118
|
+
// demands evidence the real catalog structurally can't provide. Below
|
|
119
|
+
// that floor, a candidate is "provisional" for the capability: it can
|
|
120
|
+
// still be scored and ranked (never excluded outright — a real single
|
|
121
|
+
// data point is still real evidence), but never wins outright over a
|
|
122
|
+
// real, more broadly comparable candidate — see model-intelligence.js's
|
|
123
|
+
// own comparable-before-provisional selection order.
|
|
124
|
+
const COMPARABILITY_THRESHOLD_RATIO = 0.5;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {Capability} capability
|
|
128
|
+
* @param {number} benchmarkCount - this model's own real distinct benchmark count for the capability (see countModelBenchmarks)
|
|
129
|
+
* @returns {boolean}
|
|
130
|
+
*/
|
|
131
|
+
export function isCapabilityComparable(capability, benchmarkCount) {
|
|
132
|
+
const active = activeBenchmarkCountForCapability(capability);
|
|
133
|
+
if (active === 0) return true; // no known real benchmark for this capability at all — never gate on something unmeasurable
|
|
134
|
+
return benchmarkCount >= Math.ceil(active * COMPARABILITY_THRESHOLD_RATIO);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Metrics where a LOWER value is the better real result. Every metric in
|
|
138
|
+
// BENCHMARK_IDENTITIES today is higher-is-better; this stays a real,
|
|
139
|
+
// checked table (not a hardcoded assumption baked into the ranking math)
|
|
140
|
+
// so a future lower-is-better benchmark is handled correctly without
|
|
141
|
+
// silently ranking it backwards.
|
|
142
|
+
const LOWER_IS_BETTER_METRICS = new Set();
|
|
143
|
+
|
|
144
|
+
function modelKey(model) {
|
|
145
|
+
return `${model.adapterId}::${model.modelId}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function directionFor(metric) {
|
|
149
|
+
return LOWER_IS_BETTER_METRICS.has(metric) ? "lower" : "higher";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function convertByScale(scale, value) {
|
|
153
|
+
return scale === "hundred" ? value / 100 : value;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The single most trustworthy real entry for a model across every metric
|
|
158
|
+
* alias of one benchmark identity — verified first, then most recent,
|
|
159
|
+
* exactly bestEvidence()'s own rule, just applied across aliases instead
|
|
160
|
+
* of a single metric name. This is the dedup step: the same real
|
|
161
|
+
* benchmark reported under two different source-specific key names never
|
|
162
|
+
* produces two competing data points for the same model. Returns the
|
|
163
|
+
* alias's own `scale` alongside the raw entry, for callers that need a
|
|
164
|
+
* real, unit-converted value (the gap calculation) — percentile ranking
|
|
165
|
+
* itself never needs this, since relative order is scale-invariant.
|
|
166
|
+
* @param {ReturnType<import("./model-capability-registry.js").createCapabilityRegistry>} registry
|
|
167
|
+
* @param {string} id
|
|
168
|
+
* @param {{metricAliases: Array<{metric: string, scale: string}>}} identity
|
|
169
|
+
*/
|
|
170
|
+
function bestAcrossAliases(registry, id, identity) {
|
|
171
|
+
let best = null;
|
|
172
|
+
for (const alias of identity.metricAliases) {
|
|
173
|
+
for (const entry of registry.getEvidence(id, alias.metric)) {
|
|
174
|
+
const isBetter = !best
|
|
175
|
+
|| (entry.verified && !best.entry.verified)
|
|
176
|
+
|| (entry.verified === best.entry.verified && Date.parse(entry.date ?? "") > Date.parse(best.entry.date ?? ""));
|
|
177
|
+
// The evidence's OWN real scale (set by the ingestion source that
|
|
178
|
+
// actually knows it) always wins over the alias's metric-name-based
|
|
179
|
+
// guess — two sources can share one metric name ("hle") while
|
|
180
|
+
// genuinely reporting in different real scales (AA: 0-1 fraction;
|
|
181
|
+
// Hugging Face: 0-100, verified live — DeepSeek-V4.1-Flash's real
|
|
182
|
+
// HLE via HF is 63.9, not 0.639). The alias scale stays a fallback
|
|
183
|
+
// only for evidence that never declared its own.
|
|
184
|
+
if (isBetter) best = { entry, scale: entry.scale ?? alias.scale };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return best;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Percentile rank (0-1) of each real, comparable result for one benchmark
|
|
192
|
+
* identity, among the given candidate models — scale-invariant (only
|
|
193
|
+
* relative order matters, so 0-1 fractions and 0-100 scores from
|
|
194
|
+
* different sources never need unit conversion here). Requires at least
|
|
195
|
+
* two distinct models with a real result for this exact benchmark WHEN
|
|
196
|
+
* other real candidate models exist in the pool but simply lack evidence
|
|
197
|
+
* for this one benchmark (that reduces coverage honestly instead of
|
|
198
|
+
* inventing a comparison with nobody). The one exception: when the ENTIRE
|
|
199
|
+
* candidate pool is a single model (the only accessible real provider —
|
|
200
|
+
* a real, common case, not a data gap), that lone model gets percentile 1
|
|
201
|
+
* for any benchmark it has real evidence for — being the only real
|
|
202
|
+
* option available IS the real, correct result, not a fabricated one.
|
|
203
|
+
* @param {ReturnType<import("./model-capability-registry.js").createCapabilityRegistry>} registry
|
|
204
|
+
* @param {Array<object>} models
|
|
205
|
+
* @param {{metricAliases: Array<{metric: string, scale: string}>}} identity
|
|
206
|
+
* @returns {Map<string, {value: number, verified: boolean}>} modelKey -> percentile + whether the deciding real entry was independently verified
|
|
207
|
+
*/
|
|
208
|
+
function percentileForBenchmark(registry, models, identity) {
|
|
209
|
+
const direction = directionFor(identity.metricAliases[0].metric);
|
|
210
|
+
const results = [];
|
|
211
|
+
for (const model of models) {
|
|
212
|
+
const id = registry.registerIdentity(model.adapterId, model.modelId, model.displayName ?? null);
|
|
213
|
+
const best = bestAcrossAliases(registry, id, identity);
|
|
214
|
+
// Different models can win bestAcrossAliases via different metric
|
|
215
|
+
// aliases of the SAME real benchmark identity (e.g. one model's best
|
|
216
|
+
// evidence is a 0-1 "unit" alias, another's is a 0-100 "hundred"
|
|
217
|
+
// alias) — comparing their raw entry.value would rank 0.64 below 57.9
|
|
218
|
+
// even though 0.64 (64%) is actually ahead of 57.9/100 (57.9%).
|
|
219
|
+
// Percentile ranking is scale-invariant only WITHIN one common scale,
|
|
220
|
+
// so every result is normalized to the same 0-1 scale before sorting.
|
|
221
|
+
if (best) results.push({ model, entry: best.entry, normalizedValue: convertByScale(best.scale, best.entry.value) });
|
|
222
|
+
}
|
|
223
|
+
if (results.length < 2 && models.length > 1) return new Map();
|
|
224
|
+
if (!results.length) return new Map();
|
|
225
|
+
|
|
226
|
+
const sorted = [...results].sort((a, b) => (direction === "higher" ? a.normalizedValue - b.normalizedValue : b.normalizedValue - a.normalizedValue));
|
|
227
|
+
const percentiles = new Map();
|
|
228
|
+
const n = sorted.length;
|
|
229
|
+
sorted.forEach((entry, index) => {
|
|
230
|
+
// Ties share the same percentile (their shared rank position), so a
|
|
231
|
+
// real dead-heat is never arbitrarily broken by array order here —
|
|
232
|
+
// portfolio-level tiebreaks happen later, on the final score.
|
|
233
|
+
let rankIndex = index;
|
|
234
|
+
while (rankIndex > 0 && sorted[rankIndex - 1].normalizedValue === entry.normalizedValue) rankIndex -= 1;
|
|
235
|
+
const percentile = n > 1 ? rankIndex / (n - 1) : 1;
|
|
236
|
+
percentiles.set(modelKey(entry.model), { value: percentile, verified: entry.entry.verified });
|
|
237
|
+
});
|
|
238
|
+
return percentiles;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* @param {number[]} values
|
|
243
|
+
*/
|
|
244
|
+
function median(values) {
|
|
245
|
+
if (!values.length) return null;
|
|
246
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
247
|
+
const mid = Math.floor(sorted.length / 2);
|
|
248
|
+
return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* One capability's real percentile per model: the median across every
|
|
253
|
+
* real benchmark identity in that capability that produced a comparable
|
|
254
|
+
* cohort (see percentileForBenchmark) — median, not mean, so one real
|
|
255
|
+
* outlier benchmark can't swing the capability score on its own. Falls
|
|
256
|
+
* back to AA's own composite index (also percentile-ranked, same
|
|
257
|
+
* scale-invariance) ONLY when NOT ONE component benchmark produced any
|
|
258
|
+
* real cohort anywhere in the pool — never blended alongside real
|
|
259
|
+
* components.
|
|
260
|
+
* @param {ReturnType<import("./model-capability-registry.js").createCapabilityRegistry>} registry
|
|
261
|
+
* @param {Array<object>} models
|
|
262
|
+
* @param {Capability} capability
|
|
263
|
+
* @returns {Map<string, {percentile: number, benchmarkCount: number, verifiedCount: number}>}
|
|
264
|
+
*/
|
|
265
|
+
export function computeCapabilityPercentile(registry, models, capability) {
|
|
266
|
+
const identities = BENCHMARK_IDENTITIES.filter((identity) => identity.capability === capability);
|
|
267
|
+
const perModel = new Map(models.map((m) => [modelKey(m), { percentiles: [], verifiedCount: 0 }]));
|
|
268
|
+
let anyComponentCohort = false;
|
|
269
|
+
|
|
270
|
+
for (const identity of identities) {
|
|
271
|
+
const percentiles = percentileForBenchmark(registry, models, identity);
|
|
272
|
+
if (!percentiles.size) continue;
|
|
273
|
+
anyComponentCohort = true;
|
|
274
|
+
for (const [key, { value, verified }] of percentiles) {
|
|
275
|
+
const bucket = perModel.get(key);
|
|
276
|
+
bucket.percentiles.push(value);
|
|
277
|
+
if (verified) bucket.verifiedCount += 1;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (!anyComponentCohort && COMPOSITE_FALLBACKS[capability]) {
|
|
282
|
+
const fallbackIdentity = { metricAliases: [{ metric: COMPOSITE_FALLBACKS[capability], scale: "hundred" }] };
|
|
283
|
+
const percentiles = percentileForBenchmark(registry, models, fallbackIdentity);
|
|
284
|
+
for (const [key, { value, verified }] of percentiles) {
|
|
285
|
+
const bucket = perModel.get(key);
|
|
286
|
+
bucket.percentiles.push(value);
|
|
287
|
+
if (verified) bucket.verifiedCount += 1;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const result = new Map();
|
|
292
|
+
for (const [key, bucket] of perModel) {
|
|
293
|
+
if (!bucket.percentiles.length) continue; // no real, comparable evidence at all — absent, never zero
|
|
294
|
+
result.set(key, {
|
|
295
|
+
percentile: median(bucket.percentiles),
|
|
296
|
+
benchmarkCount: bucket.percentiles.length,
|
|
297
|
+
verifiedCount: bucket.verifiedCount
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* One capability's real, scale-normalized magnitude per model — the
|
|
305
|
+
* median of each real benchmark identity's own best real value, converted
|
|
306
|
+
* to a common 0-1 scale via that alias's real `scale`. Unlike
|
|
307
|
+
* computeCapabilityPercentile, this needs no 2-model cohort (it's each
|
|
308
|
+
* model's own real value, not a rank), so it keeps real granularity even
|
|
309
|
+
* for a lone runner-up — exactly what a near-equivalence band or
|
|
310
|
+
* capability floor needs to mean anything with only 2-3 real candidates.
|
|
311
|
+
* Falls back to AA's own composite index under the same real rule as the
|
|
312
|
+
* percentile side: only when NOT ONE component benchmark has any real
|
|
313
|
+
* value anywhere in the pool, never blended alongside real components.
|
|
314
|
+
* @param {ReturnType<import("./model-capability-registry.js").createCapabilityRegistry>} registry
|
|
315
|
+
* @param {Array<object>} models
|
|
316
|
+
* @param {Capability} capability
|
|
317
|
+
* @returns {Map<string, number>} modelKey -> real 0-1 magnitude
|
|
318
|
+
*/
|
|
319
|
+
export function computeCapabilityGapValue(registry, models, capability) {
|
|
320
|
+
const identities = BENCHMARK_IDENTITIES.filter((identity) => identity.capability === capability);
|
|
321
|
+
const perModel = new Map(models.map((m) => [modelKey(m), []]));
|
|
322
|
+
let anyComponentValue = false;
|
|
323
|
+
|
|
324
|
+
for (const identity of identities) {
|
|
325
|
+
for (const model of models) {
|
|
326
|
+
const id = registry.registerIdentity(model.adapterId, model.modelId, model.displayName ?? null);
|
|
327
|
+
const best = bestAcrossAliases(registry, id, identity);
|
|
328
|
+
if (!best) continue;
|
|
329
|
+
anyComponentValue = true;
|
|
330
|
+
perModel.get(modelKey(model)).push(convertByScale(best.scale, best.entry.value));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (!anyComponentValue && COMPOSITE_FALLBACKS[capability]) {
|
|
335
|
+
const fallbackIdentity = { metricAliases: [{ metric: COMPOSITE_FALLBACKS[capability], scale: "hundred" }] };
|
|
336
|
+
for (const model of models) {
|
|
337
|
+
const id = registry.registerIdentity(model.adapterId, model.modelId, model.displayName ?? null);
|
|
338
|
+
const best = bestAcrossAliases(registry, id, fallbackIdentity);
|
|
339
|
+
if (best) perModel.get(modelKey(model)).push(convertByScale(best.scale, best.entry.value));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const result = new Map();
|
|
344
|
+
for (const [key, values] of perModel) {
|
|
345
|
+
if (values.length) result.set(key, median(values));
|
|
346
|
+
}
|
|
347
|
+
return result;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* A role's real, scale-normalized magnitude per model — the median across
|
|
352
|
+
* its relevant capabilities' own real gap values (see
|
|
353
|
+
* computeCapabilityGapValue). This is what model-intelligence.js's
|
|
354
|
+
* NEAR_EQUIVALENCE_BAND and EFFICIENT_CAPABILITY_FLOOR actually compare —
|
|
355
|
+
* never RoleEvaluation.capabilityPercentile, which only encodes rank
|
|
356
|
+
* order and collapses to {0, 0.5, 1} with Kairo's typical small candidate
|
|
357
|
+
* pools.
|
|
358
|
+
* @param {ReturnType<import("./model-capability-registry.js").createCapabilityRegistry>} registry
|
|
359
|
+
* @param {Array<object>} models
|
|
360
|
+
* @param {Capability[]} relevantCapabilities
|
|
361
|
+
* @returns {Map<string, number>} modelKey -> real 0-1 magnitude
|
|
362
|
+
*/
|
|
363
|
+
export function computeRoleGapValue(registry, models, relevantCapabilities) {
|
|
364
|
+
const perCapability = relevantCapabilities.map((capability) => computeCapabilityGapValue(registry, models, capability));
|
|
365
|
+
const result = new Map();
|
|
366
|
+
for (const model of models) {
|
|
367
|
+
const key = modelKey(model);
|
|
368
|
+
const values = perCapability.map((m) => m.get(key)).filter((v) => v != null);
|
|
369
|
+
if (values.length) result.set(key, median(values));
|
|
370
|
+
}
|
|
371
|
+
return result;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* @typedef {object} RoleEvaluation
|
|
376
|
+
* @property {string} role
|
|
377
|
+
* @property {number} capabilityPercentile - median across the role's relevant capabilities, each already a median across that capability's real benchmarks
|
|
378
|
+
* @property {number} coverage - fraction of the role's relevant capabilities that produced a real score (0-1)
|
|
379
|
+
* @property {"high"|"medium"|"low"} confidence
|
|
380
|
+
* @property {number} benchmarkCount - total real benchmark identities that contributed, across all relevant capabilities
|
|
381
|
+
* @property {Record<string, number>} capabilities - per-capability percentile, only for capabilities with real evidence
|
|
382
|
+
* @property {Record<string, number>} benchmarkCountsByCapability - this model's own real, distinct benchmark-identity count per relevant capability (see countModelBenchmarks) — never a source count (AA HLE + Hugging Face HLE is one benchmark, not two).
|
|
383
|
+
* @property {Record<string, number>} benchmarkCoverage - benchmarkCountsByCapability[capability] / activeBenchmarkCountForCapability(capability), 0-1, per relevant capability.
|
|
384
|
+
* @property {string[]} provisionalCapabilities - relevant capabilities where this model's real coverage falls below the comparability floor (isCapabilityComparable) — empty when every relevant capability clears it.
|
|
385
|
+
* @property {boolean} isProvisional - true whenever provisionalCapabilities is non-empty.
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Confidence tiers, from real coverage and provenance only — never from
|
|
390
|
+
* the score's magnitude:
|
|
391
|
+
* high: >=70% of the role's relevant capabilities scored, AND at
|
|
392
|
+
* least one contributing benchmark was independently verified.
|
|
393
|
+
* medium: >=40% coverage, OR at least two distinct real benchmark
|
|
394
|
+
* identities contributed (even if coverage is thin).
|
|
395
|
+
* low: anything short of that.
|
|
396
|
+
* A provisional candidate (real benchmark-identity coverage below the
|
|
397
|
+
* comparability floor for at least one relevant capability — see
|
|
398
|
+
* isCapabilityComparable) can never reach "high", even with a real,
|
|
399
|
+
* independently-verified single data point: one verified benchmark is
|
|
400
|
+
* real evidence, but not YET broad enough evidence to be that confident.
|
|
401
|
+
*/
|
|
402
|
+
function confidenceFor(coverage, benchmarkCount, verifiedCount, isProvisional = false) {
|
|
403
|
+
if (coverage >= 0.7 && verifiedCount >= 1 && !isProvisional) return "high";
|
|
404
|
+
if (coverage >= 0.4 || benchmarkCount >= 2) return "medium";
|
|
405
|
+
return "low";
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Computes one RoleEvaluation per candidate model for a role defined by
|
|
410
|
+
* its relevant capabilities. A model with zero real evidence across every
|
|
411
|
+
* relevant capability gets no evaluation at all (absent from the
|
|
412
|
+
* returned map) — per explicit rule, it never competes for the role
|
|
413
|
+
* rather than being scored as if it were the weakest real option.
|
|
414
|
+
* @param {ReturnType<import("./model-capability-registry.js").createCapabilityRegistry>} registry
|
|
415
|
+
* @param {Array<object>} models
|
|
416
|
+
* @param {string} role
|
|
417
|
+
* @param {Capability[]} relevantCapabilities
|
|
418
|
+
* @returns {Map<string, RoleEvaluation>}
|
|
419
|
+
*/
|
|
420
|
+
export function computeRoleEvaluations(registry, models, role, relevantCapabilities) {
|
|
421
|
+
const perCapability = relevantCapabilities.map((capability) => ({
|
|
422
|
+
capability, scores: computeCapabilityPercentile(registry, models, capability)
|
|
423
|
+
}));
|
|
424
|
+
|
|
425
|
+
const evaluations = new Map();
|
|
426
|
+
for (const model of models) {
|
|
427
|
+
const key = modelKey(model);
|
|
428
|
+
const capabilities = {};
|
|
429
|
+
let benchmarkCount = 0;
|
|
430
|
+
let verifiedCount = 0;
|
|
431
|
+
for (const { capability, scores } of perCapability) {
|
|
432
|
+
const entry = scores.get(key);
|
|
433
|
+
if (!entry) continue;
|
|
434
|
+
capabilities[capability] = entry.percentile;
|
|
435
|
+
benchmarkCount += entry.benchmarkCount;
|
|
436
|
+
verifiedCount += entry.verifiedCount;
|
|
437
|
+
}
|
|
438
|
+
const scoredCapabilities = Object.keys(capabilities);
|
|
439
|
+
if (!scoredCapabilities.length) continue; // no real primary evidence anywhere — doesn't compete for this role
|
|
440
|
+
|
|
441
|
+
// Benchmark-LEVEL coverage per relevant capability — independent of
|
|
442
|
+
// whether a percentile cohort formed (that's a ranking concern; this
|
|
443
|
+
// is "how much of the capability does THIS model's own evidence
|
|
444
|
+
// cover"). Counts real, distinct benchmark identities, never sources.
|
|
445
|
+
const benchmarkCountsByCapability = {};
|
|
446
|
+
const benchmarkCoverage = {};
|
|
447
|
+
const provisionalCapabilities = [];
|
|
448
|
+
for (const capability of Object.keys(capabilities)) {
|
|
449
|
+
const count = countModelBenchmarks(registry, model, capability);
|
|
450
|
+
const active = activeBenchmarkCountForCapability(capability);
|
|
451
|
+
benchmarkCountsByCapability[capability] = count;
|
|
452
|
+
benchmarkCoverage[capability] = active ? count / active : 0;
|
|
453
|
+
// A score of 0 real component-benchmark identities can only mean
|
|
454
|
+
// this capability's real score came entirely from the composite-
|
|
455
|
+
// index fallback (computeCapabilityPercentile's own pool-wide
|
|
456
|
+
// switch) — the best real signal available in that mode, coarse
|
|
457
|
+
// but not partial, so it's never "provisional" for lacking
|
|
458
|
+
// component benchmarks it structurally couldn't have used anyway.
|
|
459
|
+
// Only real, PARTIAL component-benchmark coverage (count > 0, but
|
|
460
|
+
// still below the comparability floor) is provisional.
|
|
461
|
+
if (count > 0 && !isCapabilityComparable(capability, count)) provisionalCapabilities.push(capability);
|
|
462
|
+
}
|
|
463
|
+
const isProvisional = provisionalCapabilities.length > 0;
|
|
464
|
+
|
|
465
|
+
const coverage = scoredCapabilities.length / relevantCapabilities.length;
|
|
466
|
+
evaluations.set(key, {
|
|
467
|
+
role,
|
|
468
|
+
capabilityPercentile: median(scoredCapabilities.map((c) => capabilities[c])),
|
|
469
|
+
coverage,
|
|
470
|
+
confidence: confidenceFor(coverage, benchmarkCount, verifiedCount, isProvisional),
|
|
471
|
+
benchmarkCount,
|
|
472
|
+
capabilities,
|
|
473
|
+
benchmarkCountsByCapability, benchmarkCoverage, provisionalCapabilities, isProvisional
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
return evaluations;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/** Ordinal rank so confidence can be compared/sorted (higher is more confident). */
|
|
480
|
+
export const CONFIDENCE_RANK = { high: 2, medium: 1, low: 0 };
|