@chllming/wave-orchestration 0.6.3 → 0.7.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 +57 -1
- package/README.md +39 -7
- package/docs/agents/wave-orchestrator-role.md +50 -0
- package/docs/agents/wave-planner-role.md +39 -0
- package/docs/context7/bundles.json +9 -0
- package/docs/context7/planner-agent/README.md +25 -0
- package/docs/context7/planner-agent/manifest.json +83 -0
- package/docs/context7/planner-agent/papers/cooperbench-why-coding-agents-cannot-be-your-teammates-yet.md +3283 -0
- package/docs/context7/planner-agent/papers/dova-deliberation-first-multi-agent-orchestration-for-autonomous-research-automation.md +1699 -0
- package/docs/context7/planner-agent/papers/dpbench-large-language-models-struggle-with-simultaneous-coordination.md +2251 -0
- package/docs/context7/planner-agent/papers/incremental-planning-to-control-a-blackboard-based-problem-solver.md +1729 -0
- package/docs/context7/planner-agent/papers/silo-bench-a-scalable-environment-for-evaluating-distributed-coordination-in-multi-agent-llm-systems.md +3747 -0
- package/docs/context7/planner-agent/papers/todoevolve-learning-to-architect-agent-planning-systems.md +1675 -0
- package/docs/context7/planner-agent/papers/verified-multi-agent-orchestration-a-plan-execute-verify-replan-framework-for-complex-query-resolution.md +1173 -0
- package/docs/context7/planner-agent/papers/why-do-multi-agent-llm-systems-fail.md +5211 -0
- package/docs/context7/planner-agent/topics/planning-and-orchestration.md +24 -0
- package/docs/evals/README.md +96 -1
- package/docs/evals/arm-templates/README.md +13 -0
- package/docs/evals/arm-templates/full-wave.json +15 -0
- package/docs/evals/arm-templates/single-agent.json +15 -0
- package/docs/evals/benchmark-catalog.json +7 -0
- package/docs/evals/cases/README.md +47 -0
- package/docs/evals/cases/wave-blackboard-inbox-targeting.json +73 -0
- package/docs/evals/cases/wave-contradiction-conflict.json +104 -0
- package/docs/evals/cases/wave-expert-routing-preservation.json +69 -0
- package/docs/evals/cases/wave-hidden-profile-private-evidence.json +81 -0
- package/docs/evals/cases/wave-premature-closure-guard.json +71 -0
- package/docs/evals/cases/wave-silo-cross-agent-state.json +77 -0
- package/docs/evals/cases/wave-simultaneous-lockstep.json +92 -0
- package/docs/evals/cooperbench/real-world-mitigation.md +341 -0
- package/docs/evals/external-benchmarks.json +85 -0
- package/docs/evals/external-command-config.sample.json +9 -0
- package/docs/evals/external-command-config.swe-bench-pro.json +8 -0
- package/docs/evals/pilots/README.md +47 -0
- package/docs/evals/pilots/swe-bench-pro-public-full-wave-review-10.json +64 -0
- package/docs/evals/pilots/swe-bench-pro-public-pilot.json +111 -0
- package/docs/evals/wave-benchmark-program.md +302 -0
- package/docs/guides/planner.md +48 -11
- package/docs/plans/context7-wave-orchestrator.md +20 -0
- package/docs/plans/current-state.md +8 -1
- package/docs/plans/examples/wave-benchmark-improvement.md +108 -0
- package/docs/plans/examples/wave-example-live-proof.md +1 -1
- package/docs/plans/examples/wave-example-rollout-fidelity.md +340 -0
- package/docs/plans/wave-orchestrator.md +62 -11
- package/docs/plans/waves/reviews/wave-1-benchmark-operator.md +118 -0
- package/docs/reference/coordination-and-closure.md +436 -0
- package/docs/reference/live-proof-waves.md +25 -3
- package/docs/reference/npmjs-trusted-publishing.md +3 -3
- package/docs/reference/proof-metrics.md +90 -0
- package/docs/reference/runtime-config/README.md +61 -0
- package/docs/reference/sample-waves.md +29 -18
- package/docs/reference/wave-control.md +164 -0
- package/docs/reference/wave-planning-lessons.md +131 -0
- package/package.json +5 -4
- package/releases/manifest.json +18 -0
- package/scripts/research/agent-context-archive.mjs +18 -0
- package/scripts/research/manifests/agent-context-expanded-2026-03-22.mjs +17 -0
- package/scripts/research/sync-planner-context7-bundle.mjs +133 -0
- package/scripts/wave-orchestrator/artifact-schemas.mjs +232 -0
- package/scripts/wave-orchestrator/autonomous.mjs +7 -0
- package/scripts/wave-orchestrator/benchmark-cases.mjs +374 -0
- package/scripts/wave-orchestrator/benchmark-external.mjs +1384 -0
- package/scripts/wave-orchestrator/benchmark.mjs +972 -0
- package/scripts/wave-orchestrator/clarification-triage.mjs +78 -12
- package/scripts/wave-orchestrator/config.mjs +175 -0
- package/scripts/wave-orchestrator/control-cli.mjs +1123 -0
- package/scripts/wave-orchestrator/control-plane.mjs +697 -0
- package/scripts/wave-orchestrator/coord-cli.mjs +360 -2
- package/scripts/wave-orchestrator/coordination-store.mjs +211 -9
- package/scripts/wave-orchestrator/coordination.mjs +84 -0
- package/scripts/wave-orchestrator/dashboard-renderer.mjs +38 -3
- package/scripts/wave-orchestrator/dashboard-state.mjs +22 -0
- package/scripts/wave-orchestrator/evals.mjs +23 -0
- package/scripts/wave-orchestrator/executors.mjs +3 -2
- package/scripts/wave-orchestrator/feedback.mjs +55 -0
- package/scripts/wave-orchestrator/install.mjs +55 -1
- package/scripts/wave-orchestrator/launcher-closure.mjs +4 -1
- package/scripts/wave-orchestrator/launcher-runtime.mjs +24 -21
- package/scripts/wave-orchestrator/launcher.mjs +796 -35
- package/scripts/wave-orchestrator/planner-context.mjs +75 -0
- package/scripts/wave-orchestrator/planner.mjs +2270 -136
- package/scripts/wave-orchestrator/proof-cli.mjs +195 -0
- package/scripts/wave-orchestrator/proof-registry.mjs +317 -0
- package/scripts/wave-orchestrator/replay.mjs +10 -4
- package/scripts/wave-orchestrator/retry-cli.mjs +184 -0
- package/scripts/wave-orchestrator/retry-control.mjs +225 -0
- package/scripts/wave-orchestrator/shared.mjs +26 -0
- package/scripts/wave-orchestrator/swe-bench-pro-task.mjs +1004 -0
- package/scripts/wave-orchestrator/traces.mjs +157 -2
- package/scripts/wave-orchestrator/wave-control-client.mjs +532 -0
- package/scripts/wave-orchestrator/wave-control-schema.mjs +309 -0
- package/scripts/wave-orchestrator/wave-files.mjs +17 -5
- package/scripts/wave.mjs +27 -0
- package/skills/repo-coding-rules/SKILL.md +1 -0
- package/skills/role-cont-eval/SKILL.md +1 -0
- package/skills/role-cont-qa/SKILL.md +13 -6
- package/skills/role-deploy/SKILL.md +1 -0
- package/skills/role-documentation/SKILL.md +4 -0
- package/skills/role-implementation/SKILL.md +4 -0
- package/skills/role-infra/SKILL.md +2 -1
- package/skills/role-integration/SKILL.md +15 -8
- package/skills/role-planner/SKILL.md +39 -0
- package/skills/role-planner/skill.json +21 -0
- package/skills/role-research/SKILL.md +1 -0
- package/skills/role-security/SKILL.md +2 -2
- package/skills/runtime-claude/SKILL.md +2 -1
- package/skills/runtime-codex/SKILL.md +1 -0
- package/skills/runtime-local/SKILL.md +2 -0
- package/skills/runtime-opencode/SKILL.md +1 -0
- package/skills/wave-core/SKILL.md +25 -6
- package/skills/wave-core/references/marker-syntax.md +16 -8
- package/wave.config.json +45 -0
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
import { readJsonOrNull, toIsoTimestamp, writeJsonAtomic } from "./shared.mjs";
|
|
2
|
+
import {
|
|
3
|
+
normalizeWaveControlReportMode,
|
|
4
|
+
normalizeWaveControlRunKind,
|
|
5
|
+
} from "./wave-control-schema.mjs";
|
|
2
6
|
|
|
3
7
|
export const MANIFEST_SCHEMA_VERSION = 1;
|
|
4
8
|
export const GLOBAL_DASHBOARD_SCHEMA_VERSION = 1;
|
|
5
9
|
export const WAVE_DASHBOARD_SCHEMA_VERSION = 1;
|
|
6
10
|
export const RELAUNCH_PLAN_SCHEMA_VERSION = 1;
|
|
11
|
+
export const RETRY_OVERRIDE_SCHEMA_VERSION = 1;
|
|
7
12
|
export const ASSIGNMENT_SNAPSHOT_SCHEMA_VERSION = 1;
|
|
8
13
|
export const DEPENDENCY_SNAPSHOT_SCHEMA_VERSION = 1;
|
|
14
|
+
export const PROOF_REGISTRY_SCHEMA_VERSION = 1;
|
|
9
15
|
export const RUN_STATE_SCHEMA_VERSION = 2;
|
|
16
|
+
export const WAVE_CONTROL_DELIVERY_STATE_SCHEMA_VERSION = 1;
|
|
10
17
|
|
|
11
18
|
export const MANIFEST_KIND = "wave-manifest";
|
|
12
19
|
export const GLOBAL_DASHBOARD_KIND = "global-dashboard";
|
|
13
20
|
export const WAVE_DASHBOARD_KIND = "wave-dashboard";
|
|
14
21
|
export const RELAUNCH_PLAN_KIND = "wave-relaunch-plan";
|
|
22
|
+
export const RETRY_OVERRIDE_KIND = "wave-retry-override";
|
|
15
23
|
export const ASSIGNMENT_SNAPSHOT_KIND = "wave-assignment-snapshot";
|
|
16
24
|
export const DEPENDENCY_SNAPSHOT_KIND = "wave-dependency-snapshot";
|
|
25
|
+
export const PROOF_REGISTRY_KIND = "wave-proof-registry";
|
|
17
26
|
export const RUN_STATE_KIND = "wave-run-state";
|
|
27
|
+
export const WAVE_CONTROL_DELIVERY_STATE_KIND = "wave-control-delivery-state";
|
|
18
28
|
|
|
19
29
|
function isPlainObject(value) {
|
|
20
30
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -25,6 +35,11 @@ function normalizeInteger(value, fallback = null) {
|
|
|
25
35
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
26
36
|
}
|
|
27
37
|
|
|
38
|
+
function normalizeNonNegativeInteger(value, fallback = 0) {
|
|
39
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
40
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
41
|
+
}
|
|
42
|
+
|
|
28
43
|
function normalizeText(value, fallback = null) {
|
|
29
44
|
const normalized = String(value ?? "").trim();
|
|
30
45
|
return normalized || fallback;
|
|
@@ -34,6 +49,19 @@ function cloneJson(value) {
|
|
|
34
49
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
35
50
|
}
|
|
36
51
|
|
|
52
|
+
function normalizeStringArray(values) {
|
|
53
|
+
if (!Array.isArray(values)) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
return Array.from(
|
|
57
|
+
new Set(
|
|
58
|
+
values
|
|
59
|
+
.map((value) => normalizeText(value, null))
|
|
60
|
+
.filter(Boolean),
|
|
61
|
+
),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
37
65
|
export function normalizeManifest(payload) {
|
|
38
66
|
const source = isPlainObject(payload) ? payload : {};
|
|
39
67
|
return {
|
|
@@ -94,6 +122,42 @@ export function writeRelaunchPlan(filePath, payload, defaults = {}) {
|
|
|
94
122
|
return normalized;
|
|
95
123
|
}
|
|
96
124
|
|
|
125
|
+
export function normalizeRetryOverride(payload, defaults = {}) {
|
|
126
|
+
const source = isPlainObject(payload) ? payload : {};
|
|
127
|
+
return {
|
|
128
|
+
schemaVersion: RETRY_OVERRIDE_SCHEMA_VERSION,
|
|
129
|
+
kind: RETRY_OVERRIDE_KIND,
|
|
130
|
+
lane: normalizeText(source.lane, normalizeText(defaults.lane, null)),
|
|
131
|
+
wave: normalizeInteger(source.wave, normalizeInteger(defaults.wave, null)),
|
|
132
|
+
selectedAgentIds: normalizeStringArray(source.selectedAgentIds),
|
|
133
|
+
reuseAttemptIds: normalizeStringArray(source.reuseAttemptIds),
|
|
134
|
+
reuseProofBundleIds: normalizeStringArray(source.reuseProofBundleIds),
|
|
135
|
+
reuseDerivedSummaries: source.reuseDerivedSummaries !== false,
|
|
136
|
+
invalidateComponentIds: normalizeStringArray(source.invalidateComponentIds),
|
|
137
|
+
clearReusableAgentIds: normalizeStringArray(source.clearReusableAgentIds),
|
|
138
|
+
preserveReusableAgentIds: normalizeStringArray(source.preserveReusableAgentIds),
|
|
139
|
+
resumePhase: normalizeText(source.resumePhase, null),
|
|
140
|
+
requestedBy: normalizeText(source.requestedBy, normalizeText(defaults.requestedBy, null)),
|
|
141
|
+
reason: normalizeText(source.reason, null),
|
|
142
|
+
applyOnce: source.applyOnce !== false,
|
|
143
|
+
createdAt: normalizeText(source.createdAt, toIsoTimestamp()),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function readRetryOverride(filePath, defaults = {}) {
|
|
148
|
+
const payload = readJsonOrNull(filePath);
|
|
149
|
+
if (!payload) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
return normalizeRetryOverride(payload, defaults);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function writeRetryOverride(filePath, payload, defaults = {}) {
|
|
156
|
+
const normalized = normalizeRetryOverride(payload, defaults);
|
|
157
|
+
writeJsonAtomic(filePath, normalized);
|
|
158
|
+
return normalized;
|
|
159
|
+
}
|
|
160
|
+
|
|
97
161
|
export function normalizeAssignmentSnapshot(payload, defaults = {}) {
|
|
98
162
|
if (Array.isArray(payload)) {
|
|
99
163
|
return {
|
|
@@ -168,6 +232,174 @@ export function writeDependencySnapshot(filePath, payload, defaults = {}) {
|
|
|
168
232
|
return normalized;
|
|
169
233
|
}
|
|
170
234
|
|
|
235
|
+
function normalizeProofArtifactEntry(entry) {
|
|
236
|
+
const source = isPlainObject(entry) ? entry : {};
|
|
237
|
+
return {
|
|
238
|
+
path: normalizeText(source.path, null),
|
|
239
|
+
kind: normalizeText(source.kind, null),
|
|
240
|
+
requiredFor: normalizeStringArray(source.requiredFor),
|
|
241
|
+
exists: source.exists === true,
|
|
242
|
+
sha256: normalizeText(source.sha256, null),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function normalizeProofComponentEntry(entry) {
|
|
247
|
+
const source = isPlainObject(entry) ? entry : {};
|
|
248
|
+
return {
|
|
249
|
+
componentId: normalizeText(source.componentId, null),
|
|
250
|
+
level: normalizeText(source.level, null),
|
|
251
|
+
state: normalizeText(source.state, null),
|
|
252
|
+
detail: normalizeText(source.detail, null),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function normalizeProofSummaryEntry(entry) {
|
|
257
|
+
const source = isPlainObject(entry) ? entry : {};
|
|
258
|
+
const state = normalizeText(source.state, null);
|
|
259
|
+
const completion = normalizeText(source.completion, null);
|
|
260
|
+
const durability = normalizeText(source.durability, null);
|
|
261
|
+
const proof = normalizeText(source.proof, null);
|
|
262
|
+
if (!state && !completion && !durability && !proof) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
state,
|
|
267
|
+
completion,
|
|
268
|
+
durability,
|
|
269
|
+
proof,
|
|
270
|
+
detail: normalizeText(source.detail, null),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function normalizeDocDeltaEntry(entry) {
|
|
275
|
+
const source = isPlainObject(entry) ? entry : {};
|
|
276
|
+
const state = normalizeText(source.state, null);
|
|
277
|
+
if (!state) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
state,
|
|
282
|
+
detail: normalizeText(source.detail, null),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function normalizeProofRegistryEntry(entry) {
|
|
287
|
+
const source = isPlainObject(entry) ? entry : {};
|
|
288
|
+
return {
|
|
289
|
+
id: normalizeText(source.id, null),
|
|
290
|
+
agentId: normalizeText(source.agentId, null),
|
|
291
|
+
state: normalizeText(source.state, null),
|
|
292
|
+
authoritative: source.authoritative === true,
|
|
293
|
+
recordedAt: normalizeText(source.recordedAt, toIsoTimestamp()),
|
|
294
|
+
recordedBy: normalizeText(source.recordedBy, null),
|
|
295
|
+
detail: normalizeText(source.detail, null),
|
|
296
|
+
summary: normalizeText(source.summary, null),
|
|
297
|
+
satisfyOwnedComponents: source.satisfyOwnedComponents === true,
|
|
298
|
+
proof: normalizeProofSummaryEntry(source.proof),
|
|
299
|
+
docDelta: normalizeDocDeltaEntry(source.docDelta),
|
|
300
|
+
components: (Array.isArray(source.components) ? source.components : [])
|
|
301
|
+
.map((item) => normalizeProofComponentEntry(item))
|
|
302
|
+
.filter((item) => item.componentId),
|
|
303
|
+
artifacts: (Array.isArray(source.artifacts) ? source.artifacts : [])
|
|
304
|
+
.map((item) => normalizeProofArtifactEntry(item))
|
|
305
|
+
.filter((item) => item.path),
|
|
306
|
+
scope: normalizeText(source.scope, null),
|
|
307
|
+
attestation: isPlainObject(source.attestation) ? cloneJson(source.attestation) : null,
|
|
308
|
+
satisfies: normalizeStringArray(source.satisfies),
|
|
309
|
+
supersedes: normalizeText(source.supersedes, null),
|
|
310
|
+
supersededBy: normalizeText(source.supersededBy, null),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function normalizeProofRegistry(payload, defaults = {}) {
|
|
315
|
+
const source = isPlainObject(payload) ? payload : {};
|
|
316
|
+
return {
|
|
317
|
+
schemaVersion: PROOF_REGISTRY_SCHEMA_VERSION,
|
|
318
|
+
kind: PROOF_REGISTRY_KIND,
|
|
319
|
+
lane: normalizeText(source.lane, normalizeText(defaults.lane, null)),
|
|
320
|
+
wave: normalizeInteger(source.wave, normalizeInteger(defaults.wave, null)),
|
|
321
|
+
updatedAt: normalizeText(source.updatedAt, toIsoTimestamp()),
|
|
322
|
+
entries: (Array.isArray(source.entries) ? source.entries : [])
|
|
323
|
+
.map((entry) => normalizeProofRegistryEntry(entry))
|
|
324
|
+
.filter((entry) => entry.id && entry.agentId),
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function readProofRegistry(filePath, defaults = {}) {
|
|
329
|
+
const payload = readJsonOrNull(filePath);
|
|
330
|
+
if (!payload) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
return normalizeProofRegistry(payload, defaults);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function writeProofRegistry(filePath, payload, defaults = {}) {
|
|
337
|
+
const normalized = normalizeProofRegistry(payload, defaults);
|
|
338
|
+
writeJsonAtomic(filePath, normalized);
|
|
339
|
+
return normalized;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export function normalizeWaveControlDeliveryState(payload, defaults = {}) {
|
|
343
|
+
const source = isPlainObject(payload) ? payload : {};
|
|
344
|
+
return {
|
|
345
|
+
schemaVersion: WAVE_CONTROL_DELIVERY_STATE_SCHEMA_VERSION,
|
|
346
|
+
kind: WAVE_CONTROL_DELIVERY_STATE_KIND,
|
|
347
|
+
workspaceId: normalizeText(source.workspaceId, normalizeText(defaults.workspaceId, null)),
|
|
348
|
+
lane: normalizeText(source.lane, normalizeText(defaults.lane, null)),
|
|
349
|
+
runId: normalizeText(source.runId, normalizeText(defaults.runId, null)),
|
|
350
|
+
runKind: normalizeWaveControlRunKind(
|
|
351
|
+
source.runKind,
|
|
352
|
+
"waveControlDeliveryState.runKind",
|
|
353
|
+
normalizeText(defaults.runKind, "unknown"),
|
|
354
|
+
),
|
|
355
|
+
reportMode: normalizeWaveControlReportMode(
|
|
356
|
+
source.reportMode,
|
|
357
|
+
"waveControlDeliveryState.reportMode",
|
|
358
|
+
normalizeText(defaults.reportMode, "metadata-plus-selected"),
|
|
359
|
+
),
|
|
360
|
+
endpoint: normalizeText(source.endpoint, normalizeText(defaults.endpoint, null)),
|
|
361
|
+
queuePath: normalizeText(source.queuePath, normalizeText(defaults.queuePath, null)),
|
|
362
|
+
eventsPath: normalizeText(source.eventsPath, normalizeText(defaults.eventsPath, null)),
|
|
363
|
+
pendingCount: normalizeNonNegativeInteger(
|
|
364
|
+
source.pendingCount,
|
|
365
|
+
normalizeNonNegativeInteger(defaults.pendingCount, 0),
|
|
366
|
+
),
|
|
367
|
+
sentCount: normalizeNonNegativeInteger(
|
|
368
|
+
source.sentCount,
|
|
369
|
+
normalizeNonNegativeInteger(defaults.sentCount, 0),
|
|
370
|
+
),
|
|
371
|
+
failedCount: normalizeNonNegativeInteger(
|
|
372
|
+
source.failedCount,
|
|
373
|
+
normalizeNonNegativeInteger(defaults.failedCount, 0),
|
|
374
|
+
),
|
|
375
|
+
lastEnqueuedAt: normalizeText(source.lastEnqueuedAt, normalizeText(defaults.lastEnqueuedAt, null)),
|
|
376
|
+
lastFlushAt: normalizeText(source.lastFlushAt, normalizeText(defaults.lastFlushAt, null)),
|
|
377
|
+
lastSuccessAt: normalizeText(source.lastSuccessAt, normalizeText(defaults.lastSuccessAt, null)),
|
|
378
|
+
lastError:
|
|
379
|
+
source.lastError === undefined
|
|
380
|
+
? defaults.lastError ?? null
|
|
381
|
+
: isPlainObject(source.lastError)
|
|
382
|
+
? source.lastError
|
|
383
|
+
: normalizeText(source.lastError, null),
|
|
384
|
+
recentEventIds: normalizeStringArray(source.recentEventIds ?? defaults.recentEventIds),
|
|
385
|
+
updatedAt: normalizeText(source.updatedAt, normalizeText(defaults.updatedAt, toIsoTimestamp())),
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function readWaveControlDeliveryState(filePath, defaults = {}) {
|
|
390
|
+
const payload = readJsonOrNull(filePath);
|
|
391
|
+
if (!payload) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return normalizeWaveControlDeliveryState(payload, defaults);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function writeWaveControlDeliveryState(filePath, payload, defaults = {}) {
|
|
398
|
+
const normalized = normalizeWaveControlDeliveryState(payload, defaults);
|
|
399
|
+
writeJsonAtomic(filePath, normalized);
|
|
400
|
+
return normalized;
|
|
401
|
+
}
|
|
402
|
+
|
|
171
403
|
export function cloneArtifactPayload(value) {
|
|
172
404
|
return cloneJson(value);
|
|
173
405
|
}
|
|
@@ -45,6 +45,7 @@ Options:
|
|
|
45
45
|
Max backoff delay for 429 retries (default: ${DEFAULT_AGENT_RATE_LIMIT_MAX_DELAY_SECONDS})
|
|
46
46
|
--agent-launch-stagger-ms <n> Delay between agent launches (default: ${DEFAULT_AGENT_LAUNCH_STAGGER_MS})
|
|
47
47
|
--orchestrator-id <id> Orchestrator ID for coordination board
|
|
48
|
+
--resident-orchestrator Launch a resident orchestrator session for each live wave
|
|
48
49
|
--executor <mode> Default executor passed to launcher: ${SUPPORTED_EXECUTOR_MODES.join(" | ")} (default: lane config)
|
|
49
50
|
--codex-sandbox <mode> Default Codex sandbox mode passed to launcher (default: ${DEFAULT_CODEX_SANDBOX_MODE})
|
|
50
51
|
--dashboard Enable dashboards (default: disabled)
|
|
@@ -64,6 +65,7 @@ export function parseArgs(argv) {
|
|
|
64
65
|
agentRateLimitMaxDelaySeconds: DEFAULT_AGENT_RATE_LIMIT_MAX_DELAY_SECONDS,
|
|
65
66
|
agentLaunchStaggerMs: DEFAULT_AGENT_LAUNCH_STAGGER_MS,
|
|
66
67
|
orchestratorId: null,
|
|
68
|
+
residentOrchestrator: false,
|
|
67
69
|
executorMode: DEFAULT_EXECUTOR_MODE,
|
|
68
70
|
codexSandboxMode: DEFAULT_CODEX_SANDBOX_MODE,
|
|
69
71
|
noDashboard: true,
|
|
@@ -103,6 +105,8 @@ export function parseArgs(argv) {
|
|
|
103
105
|
options.agentLaunchStaggerMs = parseNonNegativeInt(argv[++i], "--agent-launch-stagger-ms");
|
|
104
106
|
} else if (arg === "--orchestrator-id") {
|
|
105
107
|
options.orchestratorId = String(argv[++i] || "").trim();
|
|
108
|
+
} else if (arg === "--resident-orchestrator") {
|
|
109
|
+
options.residentOrchestrator = true;
|
|
106
110
|
} else if (arg === "--executor") {
|
|
107
111
|
options.executorMode = normalizeExecutorMode(argv[++i], "--executor");
|
|
108
112
|
executorProvided = true;
|
|
@@ -228,6 +232,9 @@ function launchSingleWave(params) {
|
|
|
228
232
|
if (params.keepTerminals) {
|
|
229
233
|
args.push("--keep-terminals");
|
|
230
234
|
}
|
|
235
|
+
if (params.residentOrchestrator) {
|
|
236
|
+
args.push("--resident-orchestrator");
|
|
237
|
+
}
|
|
231
238
|
return runCommand(args, { [WAVE_SUPPRESS_UPDATE_NOTICE_ENV]: "1" });
|
|
232
239
|
}
|
|
233
240
|
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
materializeCoordinationState,
|
|
5
|
+
normalizeCoordinationRecord,
|
|
6
|
+
} from "./coordination-store.mjs";
|
|
7
|
+
import { loadBenchmarkCatalog } from "./evals.mjs";
|
|
8
|
+
import { REPO_ROOT, readJsonOrNull } from "./shared.mjs";
|
|
9
|
+
|
|
10
|
+
const DEFAULT_BENCHMARK_CASES_DIR = "docs/evals/cases";
|
|
11
|
+
const DEFAULT_EXTERNAL_BENCHMARKS_PATH = "docs/evals/external-benchmarks.json";
|
|
12
|
+
const SUPPORTED_CASE_KINDS = new Set(["projection"]);
|
|
13
|
+
const SUPPORTED_CASE_ARMS = new Set([
|
|
14
|
+
"single-agent",
|
|
15
|
+
"multi-agent-minimal",
|
|
16
|
+
"full-wave",
|
|
17
|
+
"full-wave-plus-improvement",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
function cleanText(value) {
|
|
21
|
+
return String(value ?? "").trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizeRepoRelativePath(value, label) {
|
|
25
|
+
const normalized = cleanText(value)
|
|
26
|
+
.replaceAll("\\", "/")
|
|
27
|
+
.replace(/^\.\/+/, "")
|
|
28
|
+
.replace(/\/+/g, "/")
|
|
29
|
+
.replace(/\/$/, "");
|
|
30
|
+
if (!normalized) {
|
|
31
|
+
throw new Error(`${label} is required`);
|
|
32
|
+
}
|
|
33
|
+
if (normalized.startsWith("/") || normalized.startsWith("../") || normalized.includes("/../")) {
|
|
34
|
+
throw new Error(`${label} must stay within the repository`);
|
|
35
|
+
}
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeId(value, label) {
|
|
40
|
+
const normalized = cleanText(value).toLowerCase();
|
|
41
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(normalized)) {
|
|
42
|
+
throw new Error(`${label} must match /^[a-z0-9][a-z0-9._-]*$/`);
|
|
43
|
+
}
|
|
44
|
+
return normalized;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalizeStringArray(value, label) {
|
|
48
|
+
if (value == null) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
if (!Array.isArray(value)) {
|
|
52
|
+
throw new Error(`${label} must be an array`);
|
|
53
|
+
}
|
|
54
|
+
return value.map((entry, index) => cleanText(entry)).map((entry, index) => {
|
|
55
|
+
if (!entry) {
|
|
56
|
+
throw new Error(`${label}[${index}] must be a non-empty string`);
|
|
57
|
+
}
|
|
58
|
+
return entry;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeIdArray(value, label, { allowEmpty = true } = {}) {
|
|
63
|
+
const ids = normalizeStringArray(value, label).map((entry, index) =>
|
|
64
|
+
normalizeId(entry, `${label}[${index}]`),
|
|
65
|
+
);
|
|
66
|
+
if (!allowEmpty && ids.length === 0) {
|
|
67
|
+
throw new Error(`${label} must not be empty`);
|
|
68
|
+
}
|
|
69
|
+
return Array.from(new Set(ids));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeAgent(rawAgent, index) {
|
|
73
|
+
if (!rawAgent || typeof rawAgent !== "object" || Array.isArray(rawAgent)) {
|
|
74
|
+
throw new Error(`fixture.agents[${index}] must be an object`);
|
|
75
|
+
}
|
|
76
|
+
const agentId = normalizeId(rawAgent.agentId, `fixture.agents[${index}].agentId`);
|
|
77
|
+
return {
|
|
78
|
+
agentId,
|
|
79
|
+
title: cleanText(rawAgent.title) || agentId,
|
|
80
|
+
ownedPaths: normalizeStringArray(rawAgent.ownedPaths, `fixture.agents[${index}].ownedPaths`),
|
|
81
|
+
capabilities: normalizeIdArray(
|
|
82
|
+
rawAgent.capabilities,
|
|
83
|
+
`fixture.agents[${index}].capabilities`,
|
|
84
|
+
),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function normalizeAssignments(value, label) {
|
|
89
|
+
if (value == null) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(value)) {
|
|
93
|
+
throw new Error(`${label} must be an array`);
|
|
94
|
+
}
|
|
95
|
+
return value.map((entry, index) => {
|
|
96
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
97
|
+
throw new Error(`${label}[${index}] must be an object`);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
requestId: normalizeId(entry.requestId, `${label}[${index}].requestId`),
|
|
101
|
+
assignedAgentId: normalizeId(entry.assignedAgentId, `${label}[${index}].assignedAgentId`),
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function normalizeTargetedInboxes(value, label) {
|
|
107
|
+
if (value == null) {
|
|
108
|
+
return {};
|
|
109
|
+
}
|
|
110
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
111
|
+
throw new Error(`${label} must be an object`);
|
|
112
|
+
}
|
|
113
|
+
return Object.fromEntries(
|
|
114
|
+
Object.entries(value).map(([agentId, facts]) => [
|
|
115
|
+
normalizeId(agentId, `${label}.${agentId}`),
|
|
116
|
+
normalizeStringArray(facts, `${label}.${agentId}`),
|
|
117
|
+
]),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function normalizeExpectations(rawExpectations = {}) {
|
|
122
|
+
if (!rawExpectations || typeof rawExpectations !== "object" || Array.isArray(rawExpectations)) {
|
|
123
|
+
throw new Error("expectations must be an object");
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
globalFacts: normalizeStringArray(rawExpectations.globalFacts, "expectations.globalFacts"),
|
|
127
|
+
summaryFacts: normalizeStringArray(rawExpectations.summaryFacts, "expectations.summaryFacts"),
|
|
128
|
+
targetedInboxes: normalizeTargetedInboxes(
|
|
129
|
+
rawExpectations.targetedInboxes,
|
|
130
|
+
"expectations.targetedInboxes",
|
|
131
|
+
),
|
|
132
|
+
requiredAssignments: normalizeAssignments(
|
|
133
|
+
rawExpectations.requiredAssignments,
|
|
134
|
+
"expectations.requiredAssignments",
|
|
135
|
+
),
|
|
136
|
+
clarificationRequestIds: normalizeIdArray(
|
|
137
|
+
rawExpectations.clarificationRequestIds,
|
|
138
|
+
"expectations.clarificationRequestIds",
|
|
139
|
+
),
|
|
140
|
+
minimumDistinctAssignedAgents:
|
|
141
|
+
rawExpectations.minimumDistinctAssignedAgents == null
|
|
142
|
+
? null
|
|
143
|
+
: Number.parseInt(String(rawExpectations.minimumDistinctAssignedAgents), 10),
|
|
144
|
+
requireBlockingGuard:
|
|
145
|
+
rawExpectations.requireBlockingGuard === undefined
|
|
146
|
+
? false
|
|
147
|
+
: Boolean(rawExpectations.requireBlockingGuard),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function normalizeThresholds(rawThresholds = {}) {
|
|
152
|
+
if (!rawThresholds || typeof rawThresholds !== "object" || Array.isArray(rawThresholds)) {
|
|
153
|
+
throw new Error("scoring.thresholds must be an object");
|
|
154
|
+
}
|
|
155
|
+
const normalized = {};
|
|
156
|
+
for (const [key, value] of Object.entries(rawThresholds)) {
|
|
157
|
+
const parsed = Number(value);
|
|
158
|
+
if (!Number.isFinite(parsed)) {
|
|
159
|
+
throw new Error(`scoring.thresholds.${key} must be numeric`);
|
|
160
|
+
}
|
|
161
|
+
normalized[key] = parsed;
|
|
162
|
+
}
|
|
163
|
+
return normalized;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function normalizeScoring(rawScoring = {}) {
|
|
167
|
+
if (!rawScoring || typeof rawScoring !== "object" || Array.isArray(rawScoring)) {
|
|
168
|
+
throw new Error("scoring must be an object");
|
|
169
|
+
}
|
|
170
|
+
const kind = normalizeId(rawScoring.kind, "scoring.kind");
|
|
171
|
+
return {
|
|
172
|
+
kind,
|
|
173
|
+
primaryMetric: normalizeId(rawScoring.primaryMetric, "scoring.primaryMetric"),
|
|
174
|
+
thresholds: normalizeThresholds(rawScoring.thresholds || {}),
|
|
175
|
+
practicalWinThreshold:
|
|
176
|
+
rawScoring.practicalWinThreshold == null
|
|
177
|
+
? 5
|
|
178
|
+
: Number.parseFloat(String(rawScoring.practicalWinThreshold)),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function normalizeCapabilityRouting(value) {
|
|
183
|
+
if (value == null) {
|
|
184
|
+
return { preferredAgents: {} };
|
|
185
|
+
}
|
|
186
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
187
|
+
throw new Error("fixture.capabilityRouting must be an object");
|
|
188
|
+
}
|
|
189
|
+
const preferredAgents =
|
|
190
|
+
value.preferredAgents && typeof value.preferredAgents === "object" && !Array.isArray(value.preferredAgents)
|
|
191
|
+
? value.preferredAgents
|
|
192
|
+
: {};
|
|
193
|
+
return {
|
|
194
|
+
preferredAgents: Object.fromEntries(
|
|
195
|
+
Object.entries(preferredAgents).map(([capability, agents]) => [
|
|
196
|
+
normalizeId(capability, `fixture.capabilityRouting.preferredAgents.${capability}`),
|
|
197
|
+
normalizeIdArray(
|
|
198
|
+
agents,
|
|
199
|
+
`fixture.capabilityRouting.preferredAgents.${capability}`,
|
|
200
|
+
),
|
|
201
|
+
]),
|
|
202
|
+
),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function normalizeBenchmarkCase(rawCase, filePath, catalog) {
|
|
207
|
+
if (!rawCase || typeof rawCase !== "object" || Array.isArray(rawCase)) {
|
|
208
|
+
throw new Error(`Benchmark case must be an object: ${filePath}`);
|
|
209
|
+
}
|
|
210
|
+
const id = normalizeId(rawCase.id, `${filePath}: id`);
|
|
211
|
+
const familyId = normalizeId(rawCase.familyId, `${filePath}: familyId`);
|
|
212
|
+
const benchmarkId = normalizeId(rawCase.benchmarkId, `${filePath}: benchmarkId`);
|
|
213
|
+
const family = catalog.families[familyId];
|
|
214
|
+
if (!family) {
|
|
215
|
+
throw new Error(`${filePath}: unknown benchmark family "${familyId}"`);
|
|
216
|
+
}
|
|
217
|
+
const benchmark = family.benchmarks[benchmarkId];
|
|
218
|
+
if (!benchmark) {
|
|
219
|
+
throw new Error(`${filePath}: unknown benchmark "${benchmarkId}" for family "${familyId}"`);
|
|
220
|
+
}
|
|
221
|
+
const catalogLocalCases = Array.from(
|
|
222
|
+
new Set([...(family.localCases || []), ...(benchmark.localCases || [])]),
|
|
223
|
+
);
|
|
224
|
+
if (catalogLocalCases.length > 0 && !catalogLocalCases.includes(id)) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`${filePath}: case "${id}" is not registered in ${catalog.path} for ${familyId}/${benchmarkId}`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const kind = normalizeId(rawCase.kind || "projection", `${filePath}: kind`);
|
|
230
|
+
if (!SUPPORTED_CASE_KINDS.has(kind)) {
|
|
231
|
+
throw new Error(`${filePath}: unsupported case kind "${kind}"`);
|
|
232
|
+
}
|
|
233
|
+
const fixture = rawCase.fixture;
|
|
234
|
+
if (!fixture || typeof fixture !== "object" || Array.isArray(fixture)) {
|
|
235
|
+
throw new Error(`${filePath}: fixture must be an object`);
|
|
236
|
+
}
|
|
237
|
+
const agents = Array.isArray(fixture.agents)
|
|
238
|
+
? fixture.agents.map((agent, index) => normalizeAgent(agent, index))
|
|
239
|
+
: [];
|
|
240
|
+
if (agents.length === 0) {
|
|
241
|
+
throw new Error(`${filePath}: fixture.agents must define at least one agent`);
|
|
242
|
+
}
|
|
243
|
+
const primaryAgentId = normalizeId(
|
|
244
|
+
fixture.primaryAgentId || agents[0].agentId,
|
|
245
|
+
`${filePath}: fixture.primaryAgentId`,
|
|
246
|
+
);
|
|
247
|
+
if (!agents.some((agent) => agent.agentId === primaryAgentId)) {
|
|
248
|
+
throw new Error(`${filePath}: fixture.primaryAgentId must match one of fixture.agents`);
|
|
249
|
+
}
|
|
250
|
+
const supportedArms = normalizeIdArray(
|
|
251
|
+
rawCase.supportedArms || ["single-agent", "multi-agent-minimal", "full-wave"],
|
|
252
|
+
`${filePath}: supportedArms`,
|
|
253
|
+
{ allowEmpty: false },
|
|
254
|
+
);
|
|
255
|
+
for (const arm of supportedArms) {
|
|
256
|
+
if (!SUPPORTED_CASE_ARMS.has(arm)) {
|
|
257
|
+
throw new Error(`${filePath}: unsupported benchmark arm "${arm}"`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const records = Array.isArray(fixture.records)
|
|
261
|
+
? fixture.records.map((record, index) =>
|
|
262
|
+
normalizeCoordinationRecord(record, {
|
|
263
|
+
lane: cleanText(fixture.lane) || "main",
|
|
264
|
+
wave:
|
|
265
|
+
fixture.waveNumber == null ? 0 : Number.parseInt(String(fixture.waveNumber), 10),
|
|
266
|
+
source: "benchmark-case",
|
|
267
|
+
}),
|
|
268
|
+
)
|
|
269
|
+
: [];
|
|
270
|
+
if (records.length === 0) {
|
|
271
|
+
throw new Error(`${filePath}: fixture.records must define at least one coordination record`);
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
id,
|
|
275
|
+
version: Number.parseInt(String(rawCase.version ?? "1"), 10) || 1,
|
|
276
|
+
path: path.relative(REPO_ROOT, filePath).replaceAll(path.sep, "/"),
|
|
277
|
+
title: cleanText(rawCase.title) || id,
|
|
278
|
+
summary: cleanText(rawCase.summary) || null,
|
|
279
|
+
tags: normalizeIdArray(rawCase.tags, `${filePath}: tags`),
|
|
280
|
+
kind,
|
|
281
|
+
familyId,
|
|
282
|
+
benchmarkId,
|
|
283
|
+
familyTitle: family.title,
|
|
284
|
+
benchmarkTitle: benchmark.title,
|
|
285
|
+
supportedArms,
|
|
286
|
+
scoring: normalizeScoring(rawCase.scoring),
|
|
287
|
+
expectations: normalizeExpectations(rawCase.expectations),
|
|
288
|
+
fixture: {
|
|
289
|
+
lane: cleanText(fixture.lane) || "main",
|
|
290
|
+
waveNumber:
|
|
291
|
+
fixture.waveNumber == null ? 0 : Number.parseInt(String(fixture.waveNumber), 10),
|
|
292
|
+
primaryAgentId,
|
|
293
|
+
capabilityRouting: normalizeCapabilityRouting(fixture.capabilityRouting),
|
|
294
|
+
agents,
|
|
295
|
+
records,
|
|
296
|
+
state: materializeCoordinationState(records),
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function readJsonFile(filePath) {
|
|
302
|
+
const payload = readJsonOrNull(filePath);
|
|
303
|
+
if (!payload) {
|
|
304
|
+
throw new Error(`Invalid JSON file: ${path.relative(REPO_ROOT, filePath)}`);
|
|
305
|
+
}
|
|
306
|
+
return payload;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function loadBenchmarkCases(options = {}) {
|
|
310
|
+
const casesDir = path.resolve(
|
|
311
|
+
REPO_ROOT,
|
|
312
|
+
normalizeRepoRelativePath(options.casesDir || DEFAULT_BENCHMARK_CASES_DIR, "casesDir"),
|
|
313
|
+
);
|
|
314
|
+
if (!fs.existsSync(casesDir)) {
|
|
315
|
+
throw new Error(`Benchmark cases directory does not exist: ${path.relative(REPO_ROOT, casesDir)}`);
|
|
316
|
+
}
|
|
317
|
+
const catalog = loadBenchmarkCatalog(options);
|
|
318
|
+
const files = fs
|
|
319
|
+
.readdirSync(casesDir, { withFileTypes: true })
|
|
320
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
|
321
|
+
.map((entry) => path.join(casesDir, entry.name))
|
|
322
|
+
.toSorted();
|
|
323
|
+
const seen = new Set();
|
|
324
|
+
const cases = files.map((filePath) => {
|
|
325
|
+
const benchmarkCase = normalizeBenchmarkCase(readJsonFile(filePath), filePath, catalog);
|
|
326
|
+
if (seen.has(benchmarkCase.id)) {
|
|
327
|
+
throw new Error(`Duplicate benchmark case id "${benchmarkCase.id}"`);
|
|
328
|
+
}
|
|
329
|
+
seen.add(benchmarkCase.id);
|
|
330
|
+
return benchmarkCase;
|
|
331
|
+
});
|
|
332
|
+
return {
|
|
333
|
+
casesDir: path.relative(REPO_ROOT, casesDir).replaceAll(path.sep, "/"),
|
|
334
|
+
absoluteCasesDir: casesDir,
|
|
335
|
+
catalog,
|
|
336
|
+
cases,
|
|
337
|
+
byId: new Map(cases.map((benchmarkCase) => [benchmarkCase.id, benchmarkCase])),
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function loadExternalBenchmarkAdapters(options = {}) {
|
|
342
|
+
const registryPath = path.resolve(
|
|
343
|
+
REPO_ROOT,
|
|
344
|
+
normalizeRepoRelativePath(
|
|
345
|
+
options.externalBenchmarksPath || DEFAULT_EXTERNAL_BENCHMARKS_PATH,
|
|
346
|
+
"externalBenchmarksPath",
|
|
347
|
+
),
|
|
348
|
+
);
|
|
349
|
+
const payload = readJsonFile(registryPath);
|
|
350
|
+
const adapters = Array.isArray(payload.adapters) ? payload.adapters : [];
|
|
351
|
+
return {
|
|
352
|
+
path: path.relative(REPO_ROOT, registryPath).replaceAll(path.sep, "/"),
|
|
353
|
+
version: Number.parseInt(String(payload.version ?? "1"), 10) || 1,
|
|
354
|
+
adapters: adapters.map((adapter, index) => {
|
|
355
|
+
if (!adapter || typeof adapter !== "object" || Array.isArray(adapter)) {
|
|
356
|
+
throw new Error(`adapters[${index}] in ${registryPath} must be an object`);
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
id: normalizeId(adapter.id, `adapters[${index}].id`),
|
|
360
|
+
title: cleanText(adapter.title) || normalizeId(adapter.id, `adapters[${index}].id`),
|
|
361
|
+
mode: cleanText(adapter.mode) || "adapted",
|
|
362
|
+
sourceBenchmark: cleanText(adapter.sourceBenchmark) || null,
|
|
363
|
+
split: cleanText(adapter.split) || null,
|
|
364
|
+
pilotManifestPath: cleanText(adapter.pilotManifestPath) || null,
|
|
365
|
+
officialDocsUrl: cleanText(adapter.officialDocsUrl) || null,
|
|
366
|
+
officialCodeUrl: cleanText(adapter.officialCodeUrl) || null,
|
|
367
|
+
summary: cleanText(adapter.summary) || null,
|
|
368
|
+
commandTemplate: cleanText(adapter.commandTemplate) || null,
|
|
369
|
+
metrics: normalizeStringArray(adapter.metrics, `adapters[${index}].metrics`),
|
|
370
|
+
notes: normalizeStringArray(adapter.notes, `adapters[${index}].notes`),
|
|
371
|
+
};
|
|
372
|
+
}),
|
|
373
|
+
};
|
|
374
|
+
}
|