@ikon85/agent-workflow-kit 0.44.2 → 0.45.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/.agents/skills/audit-skills/SKILL.md +7 -4
- package/.agents/skills/code-review/SKILL.md +7 -4
- package/.agents/skills/codebase-design/DESIGN-IT-TWICE.md +7 -4
- package/.agents/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +7 -4
- package/.agents/skills/improve-codebase-architecture/SKILL.md +7 -4
- package/.agents/skills/orchestrate-wave/SKILL.md +1 -1
- package/.agents/skills/orchestrate-wave/references/dispatch-subagents.md +9 -0
- package/.agents/skills/orchestrate-wave/references/dispatch-workflow.md +9 -0
- package/.agents/skills/research/SKILL.md +7 -4
- package/.agents/skills/to-issues/SKILL.md +25 -4
- package/.claude/skills/audit-skills/SKILL.md +7 -4
- package/.claude/skills/code-review/SKILL.md +7 -4
- package/.claude/skills/codebase-design/DESIGN-IT-TWICE.md +7 -4
- package/.claude/skills/codex-build/SKILL.md +13 -0
- package/.claude/skills/codex-review/SKILL.md +13 -0
- package/.claude/skills/grill-me-codex/SKILL.md +14 -0
- package/.claude/skills/grill-with-docs-codex/SKILL.md +14 -0
- package/.claude/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +7 -4
- package/.claude/skills/improve-codebase-architecture/SKILL.md +7 -4
- package/.claude/skills/orchestrate-wave/SKILL.md +1 -1
- package/.claude/skills/orchestrate-wave/references/dispatch-subagents.md +9 -0
- package/.claude/skills/orchestrate-wave/references/dispatch-workflow.md +9 -0
- package/.claude/skills/research/SKILL.md +7 -4
- package/.claude/skills/skill-manifest.json +34 -23
- package/.claude/skills/to-issues/SKILL.md +25 -4
- package/README.md +64 -0
- package/agent-workflow-kit.package.json +149 -45
- package/package.json +1 -1
- package/scripts/doctrine-migration/index.mjs +296 -0
- package/scripts/kit-release.mjs +41 -9
- package/src/cli.mjs +515 -79
- package/src/commands/routing-status.mjs +288 -0
- package/src/lib/bundle.mjs +158 -2
- package/src/lib/dispatchJournal.mjs +300 -0
- package/src/lib/dispatchPlan.mjs +286 -0
- package/src/lib/dispatchReceipt.mjs +226 -89
- package/src/lib/frontendWorkloads.mjs +35 -33
- package/src/lib/routeDispatcher.mjs +367 -70
- package/src/lib/routingAccessGraph.mjs +265 -20
- package/src/lib/routingAccessGraphStore.mjs +300 -0
- package/src/lib/routingAdapters/claude.mjs +104 -4
- package/src/lib/routingAdapters/codex.mjs +132 -7
- package/src/lib/routingAdapters/hostBridge.mjs +291 -0
- package/src/lib/routingCatalog.mjs +201 -24
- package/src/lib/routingDispatchLease.mjs +253 -0
- package/src/lib/routingEvidenceCache.mjs +78 -0
- package/src/lib/routingIntent.mjs +176 -10
- package/src/lib/routingIntentClassifier.mjs +137 -0
- package/src/lib/routingInventory/snapshots/claude.json +49 -0
- package/src/lib/routingInventory/snapshots/codex.json +109 -0
- package/src/lib/routingInventory.mjs +182 -0
- package/src/lib/routingPolicy.mjs +193 -6
- package/src/lib/routingProfile.mjs +1251 -123
- package/src/lib/routingProfilePolicy.mjs +156 -0
- package/src/lib/routingProfileStorage.mjs +299 -0
- package/src/lib/routingResolver.mjs +369 -86
- package/src/lib/routingSources/artificialAnalysis.mjs +19 -7
- package/src/lib/routingSources/benchlm.mjs +5 -0
- package/src/lib/routingSources/codeArena.mjs +13 -3
- package/src/lib/routingSources/deepswe.mjs +19 -5
- package/src/lib/routingSources/openhands.mjs +17 -4
- package/src/lib/routingSources/openhandsFrontend.mjs +13 -3
- package/src/lib/safeText.mjs +26 -0
- package/src/lib/updateCandidate.mjs +36 -3
|
@@ -1,44 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Route decision — the dispatch-time resolution of a Routing intent against
|
|
3
|
+
* the current Evidence catalog, Access graph and Routing policy.
|
|
4
|
+
*
|
|
5
|
+
* v2 separates provenance from execution state, because one tagged union cannot
|
|
6
|
+
* hold both facts: an evidence-backed candidate carries observation, source,
|
|
7
|
+
* harness, score, freshness and cost, which a Standard route has none of, and an
|
|
8
|
+
* approval-required candidate is neither executable nor blocked. The two axes
|
|
9
|
+
* are therefore orthogonal — `origin: evidence | standard` and `state: ready |
|
|
10
|
+
* approval-required | verification-required | blocked` — with a selected
|
|
11
|
+
* candidate whose required fields follow the origin. `status` stays the
|
|
12
|
+
* dispatch-facing axis and keeps the `inherit` and `handoff` fallbacks the
|
|
13
|
+
* Routing policy owns, which `state` deliberately cannot express.
|
|
14
|
+
*
|
|
15
|
+
* The Model roster is authorization, not evidence. An Access path whose
|
|
16
|
+
* model-and-effort pair the policy never authorized is refused with
|
|
17
|
+
* `pair-not-authorized` before any executable ranking, whatever the evidence
|
|
18
|
+
* says; the Evidence catalog itself is never filtered by it, so `bestOverall`
|
|
19
|
+
* stays the evidence view.
|
|
20
|
+
*
|
|
21
|
+
* Ranking is cohort-bound and terminates. Only observations that share an axis
|
|
22
|
+
* identity, a harness, an effort and a cost currency and unit are comparable;
|
|
23
|
+
* inside one cohort a score difference smaller than the combined uncertainty is
|
|
24
|
+
* no difference, so cost breaks that tie and never drives the ranking. Cohorts
|
|
25
|
+
* are never compared with each other: when their winners disagree the outcome is
|
|
26
|
+
* `ambiguous-evidence`, and the Standard route decides and says so.
|
|
27
|
+
*/
|
|
1
28
|
import {
|
|
2
29
|
evidenceSelectionMatchesObservation,
|
|
3
30
|
validateRoutingIntent,
|
|
4
31
|
} from './routingIntent.mjs';
|
|
5
32
|
import { validateEvidenceCatalog } from './routingCatalog.mjs';
|
|
6
|
-
import { validateAccessGraph } from './routingAccessGraph.mjs';
|
|
33
|
+
import { accessPairKey, validateAccessGraph } from './routingAccessGraph.mjs';
|
|
7
34
|
import { validateRoutingPolicy } from './routingPolicy.mjs';
|
|
35
|
+
import { normalizeRosterModelId } from './routingProfile.mjs';
|
|
8
36
|
|
|
9
|
-
export const ROUTE_DECISION_VERSION =
|
|
37
|
+
export const ROUTE_DECISION_VERSION = 2;
|
|
38
|
+
|
|
39
|
+
export const ROUTE_DECISION_ORIGINS = Object.freeze(['evidence', 'standard']);
|
|
40
|
+
/** What the selected candidate may do next. Pending states are not blocked. */
|
|
41
|
+
export const ROUTE_DECISION_STATES = Object.freeze([
|
|
42
|
+
'ready', 'approval-required', 'verification-required', 'blocked',
|
|
43
|
+
]);
|
|
44
|
+
export const BEST_OVERALL_STATES = Object.freeze(['resolved', 'ambiguous', 'unavailable']);
|
|
45
|
+
export const ROUTE_APPROVAL_DECISIONS = Object.freeze(['granted', 'declined']);
|
|
10
46
|
|
|
11
47
|
const REQUIRED_INFRASTRUCTURE = ['catalog', 'accessGraph', 'policy'];
|
|
48
|
+
/** Every state that still carries a candidate, best runnable tier first. */
|
|
49
|
+
const CANDIDATE_TIERS = Object.freeze(
|
|
50
|
+
ROUTE_DECISION_STATES.filter((state) => state !== 'blocked'),
|
|
51
|
+
);
|
|
52
|
+
const APPROVAL_FIELDS = new Set(['decision', 'authorizationId']);
|
|
53
|
+
const NO_EVIDENCE = Object.freeze({
|
|
54
|
+
status: 'unavailable', route: null, cohorts: Object.freeze([]),
|
|
55
|
+
});
|
|
12
56
|
|
|
13
57
|
function missingDecision(missing, explicitFallback) {
|
|
14
58
|
return Object.freeze({
|
|
15
59
|
schemaVersion: ROUTE_DECISION_VERSION,
|
|
16
60
|
status: explicitFallback === 'inherit' ? 'inherit' : 'blocked',
|
|
17
61
|
reason: 'routing-infrastructure-missing',
|
|
62
|
+
origin: null,
|
|
63
|
+
state: 'blocked',
|
|
18
64
|
missing: Object.freeze(missing),
|
|
19
|
-
|
|
65
|
+
selected: null,
|
|
66
|
+
approval: null,
|
|
67
|
+
bestOverall: NO_EVIDENCE,
|
|
20
68
|
bestExecutable: null,
|
|
21
69
|
blockers: Object.freeze(missing.map((field) => `missing:${field}`)),
|
|
22
70
|
revisions: Object.freeze({ catalog: null, accessGraph: null, policy: null }),
|
|
23
71
|
});
|
|
24
72
|
}
|
|
25
73
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
74
|
+
/** A surface-switch authorization, recorded so a receipt can name it. */
|
|
75
|
+
function validateApproval(input) {
|
|
76
|
+
if (input == null) return null;
|
|
77
|
+
if (typeof input !== 'object' || Array.isArray(input)) {
|
|
78
|
+
throw new TypeError('approval must be an object');
|
|
79
|
+
}
|
|
80
|
+
for (const key of Object.keys(input)) {
|
|
81
|
+
if (!APPROVAL_FIELDS.has(key)) throw new TypeError(`unknown approval field: ${key}`);
|
|
82
|
+
}
|
|
83
|
+
if (!ROUTE_APPROVAL_DECISIONS.includes(input.decision)) {
|
|
84
|
+
throw new TypeError(`approval decision must be one of: ${ROUTE_APPROVAL_DECISIONS.join(', ')}`);
|
|
85
|
+
}
|
|
86
|
+
const authorizationId = input.authorizationId ?? null;
|
|
87
|
+
if (authorizationId !== null
|
|
88
|
+
&& (typeof authorizationId !== 'string' || authorizationId.trim() === '')) {
|
|
89
|
+
throw new TypeError('approval authorizationId must be a non-empty string');
|
|
90
|
+
}
|
|
91
|
+
return Object.freeze({ decision: input.decision, authorizationId });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The declared cohort: axis identity, harness, effort, cost currency and unit. */
|
|
95
|
+
function cohortKey(observation) {
|
|
96
|
+
return JSON.stringify([
|
|
97
|
+
observation.workload,
|
|
98
|
+
observation.harness.id, observation.harness.version,
|
|
99
|
+
observation.effort,
|
|
100
|
+
observation.cost.currency, observation.cost.unit,
|
|
101
|
+
]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function compareTie(left, right) {
|
|
105
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
106
|
+
const delta = typeof left[index] === 'number'
|
|
107
|
+
? left[index] - right[index]
|
|
108
|
+
: String(left[index]).localeCompare(String(right[index]));
|
|
109
|
+
if (delta !== 0) return delta;
|
|
110
|
+
}
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const byScore = (left, right) =>
|
|
115
|
+
right.score - left.score || left.cost - right.cost || compareTie(left.tie, right.tie);
|
|
116
|
+
const byCost = (left, right) => left.cost - right.cost || compareTie(left.tie, right.tie);
|
|
117
|
+
|
|
118
|
+
function rankedEntry(observation, { route, tie, value }) {
|
|
119
|
+
return {
|
|
120
|
+
cohort: cohortKey(observation),
|
|
121
|
+
score: observation.score,
|
|
122
|
+
spread: Math.max(0, observation.uncertainty.value),
|
|
123
|
+
cost: observation.cost.amount,
|
|
124
|
+
route,
|
|
125
|
+
tie,
|
|
126
|
+
value,
|
|
37
127
|
};
|
|
38
128
|
}
|
|
39
129
|
|
|
130
|
+
/**
|
|
131
|
+
* One cohort's winner. Every candidate whose score sits inside the combined
|
|
132
|
+
* uncertainty of itself and the leader is indistinguishable from it, so the
|
|
133
|
+
* cohort's identical cost unit breaks that tie before the deterministic key.
|
|
134
|
+
*/
|
|
135
|
+
function cohortWinner(entries) {
|
|
136
|
+
const leader = [...entries].sort(byScore)[0];
|
|
137
|
+
const band = entries.filter((entry) => leader.score - entry.score <= leader.spread + entry.spread);
|
|
138
|
+
return [...band].sort(byCost)[0];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Rank inside cohorts only. Several cohorts resolve when their winners nominate
|
|
143
|
+
* the same route; otherwise nothing dominates and the evidence is ambiguous.
|
|
144
|
+
*/
|
|
145
|
+
function rankCohorts(entries) {
|
|
146
|
+
const cohorts = new Map();
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
const bucket = cohorts.get(entry.cohort);
|
|
149
|
+
if (bucket) bucket.push(entry);
|
|
150
|
+
else cohorts.set(entry.cohort, [entry]);
|
|
151
|
+
}
|
|
152
|
+
const winners = [...cohorts.keys()].sort().map((key) => cohortWinner(cohorts.get(key)));
|
|
153
|
+
if (winners.length === 0) return { status: 'unavailable', winner: null, winners };
|
|
154
|
+
const routes = new Set(winners.map((entry) => entry.route));
|
|
155
|
+
return routes.size === 1
|
|
156
|
+
? { status: 'resolved', winner: winners[0], winners }
|
|
157
|
+
: { status: 'ambiguous', winner: null, winners };
|
|
158
|
+
}
|
|
159
|
+
|
|
40
160
|
function observationRoute(observation) {
|
|
41
|
-
return
|
|
161
|
+
return {
|
|
42
162
|
observationId: observation.id,
|
|
43
163
|
providerId: observation.providerId,
|
|
44
164
|
modelId: observation.modelId,
|
|
@@ -51,52 +171,180 @@ function observationRoute(observation) {
|
|
|
51
171
|
freshness: observation.freshness,
|
|
52
172
|
cost: observation.cost,
|
|
53
173
|
reason: `${observation.workload} supported by ${observation.source.id}`,
|
|
54
|
-
}
|
|
174
|
+
};
|
|
55
175
|
}
|
|
56
176
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
177
|
+
/** The dispatched identity always comes from the Access path, never the evidence. */
|
|
178
|
+
function pathIdentity(path) {
|
|
179
|
+
return {
|
|
180
|
+
providerId: path.providerId,
|
|
181
|
+
modelId: path.modelId,
|
|
182
|
+
effort: path.effort,
|
|
61
183
|
surfaceId: path.surfaceId,
|
|
62
184
|
transportId: path.transportId,
|
|
185
|
+
accessPathId: path.id,
|
|
63
186
|
enforcement: path.enforcement,
|
|
64
187
|
capabilityEvidence: path.capabilityEvidence,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const NO_PATH_IDENTITY = Object.freeze({
|
|
192
|
+
providerId: null, surfaceId: null, transportId: null,
|
|
193
|
+
accessPathId: null, enforcement: null, capabilityEvidence: null,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
function evidenceCandidate({ observation, path, state }) {
|
|
197
|
+
return Object.freeze({
|
|
198
|
+
origin: 'evidence',
|
|
199
|
+
state,
|
|
200
|
+
...observationRoute(observation),
|
|
201
|
+
...pathIdentity(path),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function standardCandidate(route, workloadClass, path, state) {
|
|
206
|
+
return Object.freeze({
|
|
207
|
+
origin: 'standard',
|
|
208
|
+
state,
|
|
209
|
+
workloadClass,
|
|
210
|
+
...NO_PATH_IDENTITY,
|
|
211
|
+
modelId: route.model,
|
|
212
|
+
effort: route.effort,
|
|
213
|
+
...(path ? pathIdentity(path) : {}),
|
|
214
|
+
reason: `standard-route:${workloadClass}`,
|
|
65
215
|
});
|
|
66
216
|
}
|
|
67
217
|
|
|
68
|
-
|
|
218
|
+
const pairLabel = (path) => `${normalizeRosterModelId(path.modelId)}+${path.effort ?? 'none'}`;
|
|
219
|
+
const rosterKey = ({ model, effort }) => JSON.stringify([model, effort ?? null]);
|
|
220
|
+
|
|
221
|
+
function rosterAuthorizes(policy, path) {
|
|
222
|
+
const key = rosterKey({ model: normalizeRosterModelId(path.modelId), effort: path.effort });
|
|
223
|
+
return policy.roster.some((pair) => rosterKey(pair) === key);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const denied = (blocker) => ({ state: 'blocked', blocker });
|
|
227
|
+
|
|
228
|
+
function refusedPath(path, { policy, knownTransports, now }) {
|
|
69
229
|
if (!knownTransports.has(path.transportId)) {
|
|
70
|
-
|
|
71
|
-
return false;
|
|
230
|
+
return denied(`unknown-transport:${path.transportId}`);
|
|
72
231
|
}
|
|
73
232
|
if (Date.parse(path.capabilityEvidence.expiresAt) <= now) {
|
|
74
|
-
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
if (path.availability !== 'available') {
|
|
78
|
-
blockers.add(`route-${path.availability}:${path.id}`);
|
|
79
|
-
return false;
|
|
233
|
+
return denied(`stale-capability-evidence:${path.id}`);
|
|
80
234
|
}
|
|
81
235
|
if (!policy.allowedSurfaces.includes(path.surfaceId)) {
|
|
82
|
-
|
|
83
|
-
return false;
|
|
236
|
+
return denied(`surface-not-allowed:${path.surfaceId}`);
|
|
84
237
|
}
|
|
85
238
|
if (!policy.allowedTransports.includes(path.transportId)) {
|
|
86
|
-
|
|
87
|
-
return false;
|
|
239
|
+
return denied(`transport-not-allowed:${path.transportId}`);
|
|
88
240
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
241
|
+
// The roster is a positive list: an unauthorized pair never reaches a ranking.
|
|
242
|
+
if (!rosterAuthorizes(policy, path)) return denied(`pair-not-authorized:${pairLabel(path)}`);
|
|
243
|
+
if (path.availability === 'unavailable') return denied(`route-unavailable:${path.id}`);
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Untested access verifies under supervision and stays blocked for an AFK run. */
|
|
248
|
+
function attestationState(path, { afk }) {
|
|
249
|
+
if (path.availability === 'available') return null;
|
|
250
|
+
if (afk) return denied(`afk-requires-attested-access:${path.id}`);
|
|
251
|
+
return { state: 'verification-required', blocker: `access-unknown:${path.id}` };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Cross-surface autonomy: `ask` carries the candidate into an approval decision. */
|
|
255
|
+
function switchState(path, { policy, activeSurface, approval }) {
|
|
256
|
+
if (path.surfaceId === activeSurface || policy.switching === 'automatic') return null;
|
|
257
|
+
if (policy.switching === 'current-surface-only') {
|
|
258
|
+
return denied(`surface-switch-disabled:${path.surfaceId}`);
|
|
259
|
+
}
|
|
260
|
+
if (approval?.decision === 'declined') return denied(`approval-declined:${path.surfaceId}`);
|
|
261
|
+
if (approval?.decision === 'granted') return null;
|
|
262
|
+
return {
|
|
263
|
+
state: 'approval-required',
|
|
264
|
+
blocker: `surface-switch-approval-required:${path.surfaceId}`,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* One path's executable state. A refusal always wins; when both a surface
|
|
270
|
+
* approval and an attestation are pending the approval is reported, because
|
|
271
|
+
* approving an attested route costs less than probing an unattested one.
|
|
272
|
+
*/
|
|
273
|
+
function classifyPath(path, context) {
|
|
274
|
+
const refused = refusedPath(path, context);
|
|
275
|
+
if (refused) return refused;
|
|
276
|
+
const attested = attestationState(path, context);
|
|
277
|
+
if (attested?.state === 'blocked') return attested;
|
|
278
|
+
const approval = switchState(path, context);
|
|
279
|
+
if (approval?.state === 'blocked') return approval;
|
|
280
|
+
return approval ?? attested ?? { state: 'ready', blocker: null };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function classifiedPath(path, context) {
|
|
284
|
+
const classified = classifyPath(path, context);
|
|
285
|
+
if (classified.blocker) context.blockers.add(classified.blocker);
|
|
286
|
+
return classified;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const surfaceTie = (path, activeSurface) => [path.surfaceId === activeSurface ? 0 : 1, path.id];
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The candidates one observation and one Access path form. An observation ranks
|
|
293
|
+
* a model, so it pairs with every path to that model; which effort is dispatched
|
|
294
|
+
* stays the path's own attested pair, and the roster authorizes that pair.
|
|
295
|
+
*/
|
|
296
|
+
function candidateEntries(evidence, paths, context) {
|
|
297
|
+
const entries = [];
|
|
298
|
+
for (const observation of evidence) {
|
|
299
|
+
for (const path of paths) {
|
|
300
|
+
if (path.providerId !== observation.providerId || path.modelId !== observation.modelId) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const { state } = classifiedPath(path, context);
|
|
304
|
+
if (state === 'blocked') continue;
|
|
305
|
+
entries.push(rankedEntry(observation, {
|
|
306
|
+
route: accessPairKey(path),
|
|
307
|
+
tie: [...surfaceTie(path, context.activeSurface), observation.id],
|
|
308
|
+
value: { observation, path, state },
|
|
309
|
+
}));
|
|
97
310
|
}
|
|
98
311
|
}
|
|
99
|
-
return
|
|
312
|
+
return entries;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** The best runnable tier decides; a pending candidate never outranks a ready one. */
|
|
316
|
+
function tieredRanking(entries) {
|
|
317
|
+
for (const state of CANDIDATE_TIERS) {
|
|
318
|
+
const tier = entries.filter((entry) => entry.value.state === state);
|
|
319
|
+
if (tier.length > 0) return rankCohorts(tier);
|
|
320
|
+
}
|
|
321
|
+
return rankCohorts([]);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* The Standard route for the resolved workload class. It authorizes nothing
|
|
326
|
+
* while unresolved, and it is named even when no path can currently take it.
|
|
327
|
+
*/
|
|
328
|
+
function standardSelection(workloadClass, paths, context) {
|
|
329
|
+
const route = context.policy.standardRoutes[workloadClass];
|
|
330
|
+
if (!route || route.state !== 'configured') {
|
|
331
|
+
context.blockers.add(`standard-route-unresolved:${workloadClass}`);
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
const candidates = paths
|
|
335
|
+
.filter((path) => normalizeRosterModelId(path.modelId) === route.model
|
|
336
|
+
&& (path.effort ?? null) === (route.effort ?? null))
|
|
337
|
+
.map((path) => ({ path, ...classifiedPath(path, context) }))
|
|
338
|
+
.sort((left, right) => compareTie(
|
|
339
|
+
surfaceTie(left.path, context.activeSurface),
|
|
340
|
+
surfaceTie(right.path, context.activeSurface),
|
|
341
|
+
));
|
|
342
|
+
for (const state of CANDIDATE_TIERS) {
|
|
343
|
+
const usable = candidates.find((entry) => entry.state === state);
|
|
344
|
+
if (usable) return standardCandidate(route, workloadClass, usable.path, state);
|
|
345
|
+
}
|
|
346
|
+
context.blockers.add(`standard-route-unreachable:${route.model}+${route.effort ?? 'none'}`);
|
|
347
|
+
return standardCandidate(route, workloadClass, null, 'blocked');
|
|
100
348
|
}
|
|
101
349
|
|
|
102
350
|
function noExecutableStatus(policy) {
|
|
@@ -105,29 +353,21 @@ function noExecutableStatus(policy) {
|
|
|
105
353
|
return 'blocked';
|
|
106
354
|
}
|
|
107
355
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
356
|
+
/** A pending decision is owed to a human; it never inherits or hands off silently. */
|
|
357
|
+
function decisionStatus(state, policy) {
|
|
358
|
+
if (state === 'ready') return 'ready';
|
|
359
|
+
if (state === 'blocked') return noExecutableStatus(policy);
|
|
360
|
+
return 'blocked';
|
|
361
|
+
}
|
|
114
362
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
throw new TypeError('activeSurface must be a non-empty string');
|
|
121
|
-
}
|
|
122
|
-
if (!Array.isArray(input.knownTransports)
|
|
123
|
-
|| !input.knownTransports.every((entry) => typeof entry === 'string' && entry !== '')) {
|
|
124
|
-
throw new TypeError('knownTransports must be an array of non-empty strings');
|
|
125
|
-
}
|
|
126
|
-
const now = Date.parse(input.now);
|
|
127
|
-
if (!Number.isFinite(now)) throw new TypeError('now must be an ISO timestamp');
|
|
363
|
+
function decisionReason(selected, fallbackReason) {
|
|
364
|
+
if (!selected) return 'no-executable-route';
|
|
365
|
+
if (selected.origin === 'standard') return fallbackReason;
|
|
366
|
+
return selected.state === 'ready' ? 'route-resolved' : selected.state;
|
|
367
|
+
}
|
|
128
368
|
|
|
129
|
-
|
|
130
|
-
|
|
369
|
+
function currentEvidenceFor(intent, catalog, now, blockers) {
|
|
370
|
+
return catalog.observations.filter((entry) => {
|
|
131
371
|
const matchesIntent = intent.evidenceSelection
|
|
132
372
|
? evidenceSelectionMatchesObservation(intent.evidenceSelection, entry.workload)
|
|
133
373
|
: entry.workload === intent.workload;
|
|
@@ -138,34 +378,77 @@ export function resolveRoute(input) {
|
|
|
138
378
|
}
|
|
139
379
|
return true;
|
|
140
380
|
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function evidenceView(evidence) {
|
|
384
|
+
const ranking = rankCohorts(evidence.map((observation) => rankedEntry(observation, {
|
|
385
|
+
route: JSON.stringify([observation.providerId, observation.modelId, observation.effort]),
|
|
386
|
+
tie: [observation.id],
|
|
387
|
+
value: observation,
|
|
388
|
+
})));
|
|
389
|
+
return Object.freeze({
|
|
390
|
+
status: ranking.status,
|
|
391
|
+
route: ranking.winner ? Object.freeze(observationRoute(ranking.winner.value)) : null,
|
|
392
|
+
cohorts: Object.freeze(ranking.winners.map((entry) =>
|
|
393
|
+
Object.freeze(observationRoute(entry.value)))),
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function validatedContext(input, { policy, intent, blockers }) {
|
|
398
|
+
if (typeof input.activeSurface !== 'string' || input.activeSurface === '') {
|
|
399
|
+
throw new TypeError('activeSurface must be a non-empty string');
|
|
400
|
+
}
|
|
401
|
+
if (!Array.isArray(input.knownTransports)
|
|
402
|
+
|| !input.knownTransports.every((entry) => typeof entry === 'string' && entry !== '')) {
|
|
403
|
+
throw new TypeError('knownTransports must be an array of non-empty strings');
|
|
404
|
+
}
|
|
405
|
+
const now = Date.parse(input.now);
|
|
406
|
+
if (!Number.isFinite(now)) throw new TypeError('now must be an ISO timestamp');
|
|
407
|
+
return {
|
|
408
|
+
policy,
|
|
409
|
+
activeSurface: input.activeSurface,
|
|
410
|
+
knownTransports: new Set(input.knownTransports),
|
|
411
|
+
now,
|
|
412
|
+
afk: intent.autonomyRequirement === 'afk',
|
|
413
|
+
approval: validateApproval(input.approval),
|
|
414
|
+
blockers,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function resolveRoute(input) {
|
|
419
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
420
|
+
throw new TypeError('route resolution input must be an object');
|
|
152
421
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const
|
|
422
|
+
const missing = REQUIRED_INFRASTRUCTURE.filter((field) => input[field] == null);
|
|
423
|
+
if (missing.length > 0) return missingDecision(missing, input.missingInfrastructure);
|
|
424
|
+
|
|
425
|
+
const intent = validateRoutingIntent(input.intent);
|
|
426
|
+
const catalog = validateEvidenceCatalog(input.catalog);
|
|
427
|
+
const accessGraph = validateAccessGraph(input.accessGraph);
|
|
428
|
+
const policy = validateRoutingPolicy(input.policy);
|
|
429
|
+
const blockers = new Set();
|
|
430
|
+
const context = validatedContext(input, { policy, intent, blockers });
|
|
431
|
+
|
|
432
|
+
const evidence = currentEvidenceFor(intent, catalog, context.now, blockers);
|
|
433
|
+
const bestOverall = evidenceView(evidence);
|
|
434
|
+
const ranking = tieredRanking(candidateEntries(evidence, accessGraph.paths, context));
|
|
435
|
+
const fallbackReason = ranking.status === 'ambiguous' ? 'ambiguous-evidence' : 'no-evidence-route';
|
|
436
|
+
const selected = ranking.winner
|
|
437
|
+
? evidenceCandidate(ranking.winner.value)
|
|
438
|
+
: standardSelection(intent.workload, accessGraph.paths, context);
|
|
439
|
+
const state = selected?.state ?? 'blocked';
|
|
440
|
+
|
|
162
441
|
return Object.freeze({
|
|
163
442
|
schemaVersion: ROUTE_DECISION_VERSION,
|
|
164
|
-
status,
|
|
165
|
-
reason:
|
|
443
|
+
status: decisionStatus(state, policy),
|
|
444
|
+
reason: decisionReason(selected, fallbackReason),
|
|
445
|
+
origin: selected?.origin ?? null,
|
|
446
|
+
state,
|
|
166
447
|
intent,
|
|
448
|
+
selected,
|
|
449
|
+
approval: context.approval,
|
|
167
450
|
bestOverall,
|
|
168
|
-
bestExecutable,
|
|
451
|
+
bestExecutable: state === 'ready' ? selected : null,
|
|
169
452
|
blockers: Object.freeze([...blockers].sort()),
|
|
170
453
|
revisions: Object.freeze({
|
|
171
454
|
catalog: catalog.revision,
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertPublishedEffort,
|
|
3
|
+
evidenceFreshness,
|
|
4
|
+
evidenceIdentity,
|
|
5
|
+
evidenceSourceClaim,
|
|
6
|
+
} from '../routingCatalog.mjs';
|
|
7
|
+
|
|
1
8
|
const SOURCE_ID = 'artificial-analysis-coding-agents';
|
|
2
9
|
const OWNER = 'Artificial Analysis';
|
|
3
10
|
const ARTIFACT_URL = 'https://artificialanalysis.ai/agents/coding-agents';
|
|
11
|
+
const WORKLOAD = evidenceIdentity({ workload: 'repository-repair', axis: 'functional' });
|
|
4
12
|
|
|
5
13
|
function object(value, field) {
|
|
6
14
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
@@ -60,10 +68,13 @@ function ingest({ payload, snapshotHash, observedAt, expiresAt }) {
|
|
|
60
68
|
row.modelId,
|
|
61
69
|
`Artificial Analysis configurations[${index}].modelId`,
|
|
62
70
|
);
|
|
63
|
-
const effort =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
const effort = assertPublishedEffort({
|
|
72
|
+
sourceId: SOURCE_ID,
|
|
73
|
+
effort: string(
|
|
74
|
+
row.reasoningEffort,
|
|
75
|
+
`Artificial Analysis configurations[${index}].effort`,
|
|
76
|
+
),
|
|
77
|
+
});
|
|
67
78
|
const id = `${SOURCE_ID}:${benchmarkVersion}:${providerId}:${modelId}:${effort}`;
|
|
68
79
|
if (observationIds.has(id)) {
|
|
69
80
|
throw new TypeError(`duplicate Artificial Analysis observation: ${id}`);
|
|
@@ -79,7 +90,7 @@ function ingest({ payload, snapshotHash, observedAt, expiresAt }) {
|
|
|
79
90
|
providerId,
|
|
80
91
|
modelId,
|
|
81
92
|
effort,
|
|
82
|
-
workload:
|
|
93
|
+
workload: WORKLOAD,
|
|
83
94
|
harness: { id: harnessId, version: harnessVersion },
|
|
84
95
|
score: number(
|
|
85
96
|
row.indexScore,
|
|
@@ -103,14 +114,14 @@ function ingest({ payload, snapshotHash, observedAt, expiresAt }) {
|
|
|
103
114
|
`Artificial Analysis configurations[${index}].uncertainty.value`,
|
|
104
115
|
),
|
|
105
116
|
},
|
|
106
|
-
freshness: {
|
|
117
|
+
freshness: evidenceFreshness({ sourceId: SOURCE_ID, observedAt }),
|
|
107
118
|
cost: {
|
|
108
119
|
amount: number(
|
|
109
120
|
row.costPerTaskUsd,
|
|
110
121
|
`Artificial Analysis configurations[${index}].costPerTaskUsd`,
|
|
111
122
|
),
|
|
112
123
|
currency: 'USD',
|
|
113
|
-
unit: '
|
|
124
|
+
unit: 'attempt',
|
|
114
125
|
},
|
|
115
126
|
});
|
|
116
127
|
});
|
|
@@ -125,5 +136,6 @@ export const artificialAnalysisSource = Object.freeze({
|
|
|
125
136
|
sourceId: SOURCE_ID,
|
|
126
137
|
owner: OWNER,
|
|
127
138
|
artifactUrl: ARTIFACT_URL,
|
|
139
|
+
claim: evidenceSourceClaim(SOURCE_ID),
|
|
128
140
|
ingest,
|
|
129
141
|
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { evidenceFreshness, evidenceSourceClaim } from '../routingCatalog.mjs';
|
|
2
|
+
|
|
1
3
|
const SOURCE_ID = 'benchlm';
|
|
2
4
|
const ARTIFACT_URL = 'https://benchlm.ai/data';
|
|
3
5
|
const OWNER_URLS = Object.freeze({
|
|
@@ -128,6 +130,8 @@ function ingest({ payload, snapshotHash, observedAt, expiresAt }) {
|
|
|
128
130
|
generatedAt,
|
|
129
131
|
sourceLastUpdated,
|
|
130
132
|
snapshotHash,
|
|
133
|
+
// The owner publishes no cadence, so the Kit dates its own expiry.
|
|
134
|
+
...evidenceFreshness({ sourceId: SOURCE_ID, observedAt }),
|
|
131
135
|
});
|
|
132
136
|
signals.push({
|
|
133
137
|
kind: 'corroboration-candidate',
|
|
@@ -147,5 +151,6 @@ export const benchLmSource = Object.freeze({
|
|
|
147
151
|
sourceId: SOURCE_ID,
|
|
148
152
|
owner: 'BenchLM',
|
|
149
153
|
artifactUrl: ARTIFACT_URL,
|
|
154
|
+
claim: evidenceSourceClaim(SOURCE_ID),
|
|
150
155
|
ingest,
|
|
151
156
|
});
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { frontendEvidenceWorkload } from '../frontendWorkloads.mjs';
|
|
2
|
+
import {
|
|
3
|
+
assertCostUnit,
|
|
4
|
+
assertPublishedEffort,
|
|
5
|
+
evidenceFreshness,
|
|
6
|
+
evidenceSourceClaim,
|
|
7
|
+
} from '../routingCatalog.mjs';
|
|
2
8
|
|
|
3
9
|
const OWNER_URL = 'https://arena.ai/leaderboard/code/webdev';
|
|
4
10
|
const SOURCE_ID = 'code-arena-webdev';
|
|
@@ -80,7 +86,10 @@ function ingest({ payload, snapshotHash, observedAt, expiresAt }) {
|
|
|
80
86
|
object(row, `Code Arena rows[${index}]`);
|
|
81
87
|
const providerId = string(row.providerId, `Code Arena rows[${index}].providerId`);
|
|
82
88
|
const modelId = string(row.modelId, `Code Arena rows[${index}].modelId`);
|
|
83
|
-
const effort =
|
|
89
|
+
const effort = assertPublishedEffort({
|
|
90
|
+
sourceId: SOURCE_ID,
|
|
91
|
+
effort: string(row.effort, `Code Arena rows[${index}].effort`),
|
|
92
|
+
});
|
|
84
93
|
const domain = string(row.domain, `Code Arena rows[${index}].domain`);
|
|
85
94
|
const score = finite(row.score, `Code Arena rows[${index}].score`);
|
|
86
95
|
const interval = finite(
|
|
@@ -132,11 +141,11 @@ function ingest({ payload, snapshotHash, observedAt, expiresAt }) {
|
|
|
132
141
|
status: row.status,
|
|
133
142
|
sampleSize,
|
|
134
143
|
},
|
|
135
|
-
freshness: {
|
|
144
|
+
freshness: evidenceFreshness({ sourceId: SOURCE_ID, observedAt }),
|
|
136
145
|
cost: {
|
|
137
146
|
amount: finite(row.cost.amount, `Code Arena rows[${index}].cost.amount`),
|
|
138
147
|
currency: string(row.cost.currency, `Code Arena rows[${index}].cost.currency`),
|
|
139
|
-
unit:
|
|
148
|
+
unit: assertCostUnit(row.cost.unit, `Code Arena rows[${index}].cost.unit`),
|
|
140
149
|
},
|
|
141
150
|
});
|
|
142
151
|
}
|
|
@@ -158,5 +167,6 @@ export const codeArenaSource = Object.freeze({
|
|
|
158
167
|
sourceId: SOURCE_ID,
|
|
159
168
|
owner: 'Arena',
|
|
160
169
|
artifactUrl: OWNER_URL,
|
|
170
|
+
claim: evidenceSourceClaim(SOURCE_ID),
|
|
161
171
|
ingest,
|
|
162
172
|
});
|