@agentskit/harness 0.1.0 → 0.3.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 +26 -32
- package/CONTRIBUTING.md +60 -12
- package/MANIFESTO.md +23 -0
- package/README.md +199 -142
- package/dist/cli.js +796 -221
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +739 -121
- package/dist/index.js +1179 -303
- package/dist/index.js.map +1 -1
- package/docs/ADR-0025-portable-orchestration-controls.md +27 -0
- package/docs/ORGANIZATION.md +37 -0
- package/package.json +41 -34
- package/docs/ADR-0025-ci-dogfood.md +0 -22
- package/docs/ADR-0026-ci-evidence-artifact.md +0 -22
- package/docs/ADR-0027-portable-evidence.md +0 -19
- package/docs/ADR-0028-effective-metrics.md +0 -20
- package/docs/ADR-0029-honest-ci-preparation.md +0 -20
- package/docs/ADR-0030-agentskit-os-benchmark-bridge.md +0 -20
- package/docs/ADR-0031-real-provider-baseline.md +0 -18
- package/docs/ADR-0032-harness-equivalent-benchmark.md +0 -25
- package/docs/ADR-0033-portable-agent-gate.md +0 -25
- package/docs/ADR-0034-measurement-quality-gates.md +0 -25
- package/docs/ADR-0035-reproducible-benchmark-samples.md +0 -20
- package/docs/ADR-0036-comparable-baseline-samples.md +0 -20
- package/docs/ADR-0037-replicated-baseline-collection.md +0 -27
- package/docs/ADR-0038-end-to-end-benchmark-boundary.md +0 -28
- package/docs/ADR-0039-artifact-and-protocol-metrics.md +0 -39
- package/docs/ADR-0040-benchmark-corpus-surfaces.md +0 -32
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createPrivateKey, createPublicKey, sign, verify
|
|
3
|
-
import { readFileSync, mkdirSync,
|
|
2
|
+
import { createHash, randomUUID, createPrivateKey, createPublicKey, sign, verify } from 'crypto';
|
|
3
|
+
import { readFileSync, mkdirSync, existsSync, unlinkSync, openSync, writeFileSync, closeSync, writeSync, appendFileSync, mkdtempSync, renameSync, rmSync, readdirSync } from 'fs';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
|
-
import { resolve, dirname, relative, join, sep } from 'path';
|
|
5
|
+
import { resolve, dirname, relative, basename, join, extname, sep } from 'path';
|
|
6
6
|
import { execFile, spawn } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
|
-
import { tmpdir } from 'os';
|
|
8
|
+
import { tmpdir, cpus, loadavg, freemem, totalmem } from 'os';
|
|
9
9
|
|
|
10
10
|
// src/constants.ts
|
|
11
11
|
var STATES = [
|
|
@@ -25,7 +25,7 @@ var LEGAL_TRANSITIONS = {
|
|
|
25
25
|
CLARIFYING: ["PLANNED", "BLOCKED", "CANCELLED"],
|
|
26
26
|
PLANNED: ["IMPLEMENTING", "CLARIFYING", "STALE", "CANCELLED"],
|
|
27
27
|
IMPLEMENTING: ["VERIFYING", "CLARIFYING", "STALE", "CANCELLED"],
|
|
28
|
-
VERIFYING: ["AWAITING_HUMAN_APPROVAL", "BLOCKED", "STALE", "CANCELLED"],
|
|
28
|
+
VERIFYING: ["AWAITING_HUMAN_APPROVAL", "COMPLETE", "BLOCKED", "STALE", "CANCELLED"],
|
|
29
29
|
AWAITING_HUMAN_APPROVAL: ["AWAITING_AUTHORIZATION", "COMPLETE", "BLOCKED", "IMPLEMENTING", "STALE", "CANCELLED"],
|
|
30
30
|
AWAITING_AUTHORIZATION: ["COMPLETE", "BLOCKED", "IMPLEMENTING", "STALE", "CANCELLED"],
|
|
31
31
|
COMPLETE: ["STALE", "SUPERSEDED"],
|
|
@@ -63,9 +63,11 @@ var id = (value, label) => {
|
|
|
63
63
|
var parents = (value, label) => value === void 0 ? [] : Array.isArray(value) ? value.map((item, index2) => id(item, `${label}[${index2}]`)) : [id(value, label)];
|
|
64
64
|
var merge = (base, overlay) => {
|
|
65
65
|
const result = { ...base };
|
|
66
|
-
for (const key of ["surfaces", "budget", "cleanup"]) {
|
|
66
|
+
for (const key of ["surfaces", "budget", "cleanup", "runtime", "verification"]) {
|
|
67
67
|
if (overlay[key] !== void 0) result[key] = { ...isRecord(result[key]) ? result[key] : {}, ...record(overlay[key], `profile.${key}`) };
|
|
68
68
|
}
|
|
69
|
+
if (overlay["autonomy"] !== void 0) result["autonomy"] = overlay["autonomy"];
|
|
70
|
+
if (Array.isArray(overlay["checks"])) result["checks"] = overlay["checks"];
|
|
69
71
|
if (overlay["checkOverrides"] !== void 0) {
|
|
70
72
|
if (!Array.isArray(overlay["checkOverrides"])) fail("profile.checkOverrides must be an array.", "INVALID_CONFIG");
|
|
71
73
|
const checks = Array.isArray(result["checks"]) ? [...result["checks"]] : [];
|
|
@@ -183,18 +185,20 @@ var surface = (value, name) => {
|
|
|
183
185
|
var parseCheck = (value, index2) => {
|
|
184
186
|
const record3 = asRecord(value, `checks[${index2}]`);
|
|
185
187
|
const id2 = stringValue(record3["id"], `checks[${index2}].id`);
|
|
186
|
-
const
|
|
187
|
-
if (!CHECK_CATEGORIES.includes(
|
|
188
|
+
const category2 = stringValue(record3["category"], `checks[${index2}].category`);
|
|
189
|
+
if (!CHECK_CATEGORIES.includes(category2)) fail(`checks[${index2}].category is invalid.`, "INVALID_CONFIG");
|
|
188
190
|
const command = stringValue(record3["command"], `checks[${index2}].command`);
|
|
189
|
-
if (REAL_CATEGORIES.has(
|
|
191
|
+
if (REAL_CATEGORIES.has(category2) && record3["execution"] !== "real") fail(`checks[${index2}] must declare execution: real.`, "INVALID_CONFIG");
|
|
190
192
|
if (record3["evidence"] !== "structured") fail(`checks[${index2}] must declare evidence: structured.`, "INVALID_CONFIG");
|
|
191
193
|
if (record3["capabilities"] !== void 0 && (!Array.isArray(record3["capabilities"]) || !record3["capabilities"].every((item) => typeof item === "string"))) fail(`checks[${index2}].capabilities must be strings.`, "INVALID_CONFIG");
|
|
192
194
|
const capabilities = Array.isArray(record3["capabilities"]) ? record3["capabilities"].filter((item) => typeof item === "string") : void 0;
|
|
193
|
-
if (
|
|
194
|
-
if (
|
|
195
|
+
if (category2 === "ui" && !capabilities?.includes("real-browser")) fail(`checks[${index2}] must declare real-browser.`, "INVALID_CONFIG");
|
|
196
|
+
if (category2 === "ui" && !capabilities?.includes("screenshot")) fail(`checks[${index2}] must declare screenshot.`, "INVALID_CONFIG");
|
|
195
197
|
if (record3["required"] !== void 0 && typeof record3["required"] !== "boolean") fail(`checks[${index2}].required must be boolean.`, "INVALID_CONFIG");
|
|
198
|
+
if (record3["required"] === false && typeof record3["reason"] !== "string") fail(`checks[${index2}].reason is required when required is false.`, "INVALID_CONFIG");
|
|
196
199
|
if (record3["timeoutMs"] !== void 0 && (!Number.isInteger(record3["timeoutMs"]) || typeof record3["timeoutMs"] !== "number" || record3["timeoutMs"] < 1)) fail(`checks[${index2}].timeoutMs must be positive.`, "INVALID_CONFIG");
|
|
197
|
-
|
|
200
|
+
const dependsOn = record3["dependsOn"] === void 0 ? void 0 : stringArray(record3["dependsOn"], `checks[${index2}].dependsOn`);
|
|
201
|
+
return { id: id2, category: category2, command, required: record3["required"] !== false, ...typeof record3["reason"] === "string" ? { reason: record3["reason"] } : {}, timeoutMs: typeof record3["timeoutMs"] === "number" ? record3["timeoutMs"] : 12e4, ...record3["execution"] === "real" ? { execution: "real" } : {}, ...capabilities ? { capabilities } : {}, ...dependsOn ? { dependsOn: [...new Set(dependsOn)] } : {}, evidence: "structured" };
|
|
198
202
|
};
|
|
199
203
|
var parseOutcome = (value, index2, checks) => {
|
|
200
204
|
const record3 = asRecord(value, `contract.outcomes[${index2}]`);
|
|
@@ -208,10 +212,28 @@ var validateConfig = (rawValue) => {
|
|
|
208
212
|
const raw = resolveProfile(asRecord(rawValue, "verification config"));
|
|
209
213
|
if (raw["schemaVersion"] !== 1) fail("verification config schemaVersion must be 1.", "INVALID_CONFIG");
|
|
210
214
|
const project = stringValue(raw["project"], "verification config project");
|
|
215
|
+
const runtimeRaw = asRecord(raw["runtime"] ?? { kind: "process" }, "runtime");
|
|
216
|
+
if (runtimeRaw["kind"] !== "process" && runtimeRaw["kind"] !== "docker") fail("runtime.kind must be process or docker.", "INVALID_CONFIG");
|
|
217
|
+
const runtime = { kind: runtimeRaw["kind"] };
|
|
218
|
+
if (raw["autonomy"] !== void 0 && raw["autonomy"] !== "controlled" && raw["autonomy"] !== "yolo") fail("autonomy must be controlled or yolo.", "INVALID_CONFIG");
|
|
219
|
+
const autonomy = raw["autonomy"] ?? "controlled";
|
|
211
220
|
const contractRaw = asRecord(raw["contract"], "contract");
|
|
212
221
|
const rawChecks = raw["checks"];
|
|
213
222
|
const checks = Array.isArray(rawChecks) ? rawChecks.map(parseCheck) : fail("checks must be a non-empty array.", "INVALID_CONFIG");
|
|
214
223
|
if (!checks.length || new Set(checks.map((check) => check.id)).size !== checks.length) fail("check ids must be unique.", "INVALID_CONFIG");
|
|
224
|
+
const checkIds = new Set(checks.map((check) => check.id));
|
|
225
|
+
for (const check of checks) for (const dependency of check.dependsOn ?? []) if (!checkIds.has(dependency) || dependency === check.id) fail(`check ${check.id} has an invalid dependency: ${dependency}.`, "INVALID_CONFIG");
|
|
226
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
227
|
+
const visited = /* @__PURE__ */ new Set();
|
|
228
|
+
const visit = (id2) => {
|
|
229
|
+
if (visiting.has(id2)) fail(`check dependency cycle includes ${id2}.`, "INVALID_CONFIG");
|
|
230
|
+
if (visited.has(id2)) return;
|
|
231
|
+
visiting.add(id2);
|
|
232
|
+
for (const dependency of checks.find((check) => check.id === id2)?.dependsOn ?? []) visit(dependency);
|
|
233
|
+
visiting.delete(id2);
|
|
234
|
+
visited.add(id2);
|
|
235
|
+
};
|
|
236
|
+
for (const check of checks) visit(check.id);
|
|
215
237
|
const scopeRaw = asRecord(contractRaw["scope"], "contract.scope");
|
|
216
238
|
const scope = { inScope: stringArray(scopeRaw["inScope"], "contract.scope.inScope"), outOfScope: stringArray(scopeRaw["outOfScope"], "contract.scope.outOfScope") };
|
|
217
239
|
const ambiguities = stringArray(contractRaw["ambiguities"], "contract.ambiguities");
|
|
@@ -228,13 +250,15 @@ var validateConfig = (rawValue) => {
|
|
|
228
250
|
if (trackingRaw["required"] !== true && typeof trackingRaw["reason"] !== "string") fail("tracking.reason is required when tracking is disabled.", "INVALID_CONFIG");
|
|
229
251
|
const budgetRaw = raw["budget"] === void 0 ? void 0 : asRecord(raw["budget"], "budget");
|
|
230
252
|
if (budgetRaw && budgetRaw["maxDurationMs"] !== void 0 && (!Number.isInteger(budgetRaw["maxDurationMs"]) || typeof budgetRaw["maxDurationMs"] !== "number" || budgetRaw["maxDurationMs"] < 1)) fail("budget.maxDurationMs must be positive.", "INVALID_CONFIG");
|
|
253
|
+
const verificationRaw = raw["verification"] === void 0 ? void 0 : asRecord(raw["verification"], "verification");
|
|
254
|
+
if (verificationRaw && verificationRaw["maxConcurrency"] !== void 0 && (!Number.isInteger(verificationRaw["maxConcurrency"]) || typeof verificationRaw["maxConcurrency"] !== "number" || verificationRaw["maxConcurrency"] < 1)) fail("verification.maxConcurrency must be a positive integer.", "INVALID_CONFIG");
|
|
231
255
|
const cleanupRaw = raw["cleanup"] === void 0 ? void 0 : asRecord(raw["cleanup"], "cleanup");
|
|
232
256
|
const cleanup = cleanupRaw ? { roots: cleanupRaw["roots"] === void 0 ? void 0 : stringArray(cleanupRaw["roots"], "cleanup.roots") } : void 0;
|
|
233
257
|
const benchmarkRaw = raw["benchmark"] === void 0 ? void 0 : asRecord(raw["benchmark"], "benchmark");
|
|
234
258
|
const benchmark2 = benchmarkRaw ? { suiteId: stringValue(benchmarkRaw["suiteId"], "benchmark.suiteId"), taskId: stringValue(benchmarkRaw["taskId"], "benchmark.taskId"), mode: benchmarkRaw["mode"] === "harness" ? "harness" : fail("benchmark.mode must be harness.", "INVALID_CONFIG") } : void 0;
|
|
235
259
|
const contract = { intent: stringValue(contractRaw["intent"], "contract.intent"), scope, ambiguities, outcomes };
|
|
236
260
|
const tracking = { required: trackingRaw["required"] === true, ...typeof trackingRaw["target"] === "string" ? { target: trackingRaw["target"] } : {}, ...typeof trackingRaw["reason"] === "string" ? { reason: trackingRaw["reason"] } : {} };
|
|
237
|
-
return { schemaVersion: 1, project, ...typeof raw["root"] === "string" ? { root: raw["root"] } : {}, ...typeof raw["stateDir"] === "string" ? { stateDir: raw["stateDir"] } : {}, profile: typeof raw["profile"] === "string" ? raw["profile"] : "strict", contract, surfaces, checks, tracking, ...budgetRaw ? { budget: { maxDurationMs: budgetRaw["maxDurationMs"] } } : {}, ...cleanup ? { cleanup } : {}, ...benchmark2 ? { benchmark: benchmark2 } : {} };
|
|
261
|
+
return { schemaVersion: 1, project, ...typeof raw["root"] === "string" ? { root: raw["root"] } : {}, ...typeof raw["stateDir"] === "string" ? { stateDir: raw["stateDir"] } : {}, profile: typeof raw["profile"] === "string" ? raw["profile"] : "strict", runtime, autonomy, contract, surfaces, checks, tracking, ...verificationRaw ? { verification: { maxConcurrency: verificationRaw["maxConcurrency"] } } : {}, ...budgetRaw ? { budget: { maxDurationMs: budgetRaw["maxDurationMs"] } } : {}, ...cleanup ? { cleanup } : {}, ...benchmark2 ? { benchmark: benchmark2 } : {} };
|
|
238
262
|
};
|
|
239
263
|
var loadConfig = (configPath = ".codex/verification.json") => {
|
|
240
264
|
const absolute = resolve(configPath);
|
|
@@ -242,7 +266,7 @@ var loadConfig = (configPath = ".codex/verification.json") => {
|
|
|
242
266
|
const rawRecord = asRecord(raw, "verification config");
|
|
243
267
|
const root = resolve(dirname(absolute), typeof rawRecord["root"] === "string" ? rawRecord["root"] : ".");
|
|
244
268
|
const stateDir = resolve(root, typeof rawRecord["stateDir"] === "string" ? rawRecord["stateDir"] : ".codex/verification");
|
|
245
|
-
if (
|
|
269
|
+
if (stateDir === root) fail("stateDir must be separate from the project root.", "INVALID_CONFIG");
|
|
246
270
|
const config = validateConfig(raw);
|
|
247
271
|
return { absolute, root, stateDir, config, configHash: hashJson(config) };
|
|
248
272
|
};
|
|
@@ -291,7 +315,9 @@ var eventDigest = (event) => sha256(JSON.stringify(eventBody(event)));
|
|
|
291
315
|
var parseEvent = (value, expectedSequence) => {
|
|
292
316
|
if (typeof value !== "object" || value === null || Array.isArray(value)) fail("Event log contains a non-object record.", "HARNESS_ERROR");
|
|
293
317
|
const record3 = value;
|
|
294
|
-
|
|
318
|
+
const context2 = record3["correlation"];
|
|
319
|
+
const validContext = context2 === void 0 || typeof context2 === "object" && context2 !== null && !Array.isArray(context2) && Object.entries(context2).every(([key, value2]) => ["operationId", "runId", "sessionId", "turnId", "actionId", "traceId"].includes(key) && typeof value2 === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value2)) && typeof context2["operationId"] === "string";
|
|
320
|
+
if (record3["schemaVersion"] !== HARNESS_EVENT_SCHEMA_VERSION || typeof record3["runId"] !== "string" || typeof record3["sequence"] !== "number" || record3["sequence"] !== expectedSequence || typeof record3["at"] !== "string" || typeof record3["sourceRevision"] !== "string" || typeof record3["configHash"] !== "string" || !isEventType(record3["type"]) || typeof record3["payload"] !== "object" || record3["payload"] === null || record3["sessionId"] !== void 0 && (typeof record3["sessionId"] !== "string" || !record3["sessionId"].trim()) || SESSION_EVENT_TYPES.has(record3["type"]) && typeof record3["sessionId"] !== "string" || !validContext) fail("Event log is invalid or out of order.", "HARNESS_ERROR");
|
|
295
321
|
const hasPreviousHash = record3["previousHash"] !== void 0;
|
|
296
322
|
const hasEventHash = record3["eventHash"] !== void 0;
|
|
297
323
|
if (hasPreviousHash !== hasEventHash || hasPreviousHash && (typeof record3["previousHash"] !== "string" || record3["previousHash"] !== EVENT_LOG_GENESIS && !digest(record3["previousHash"]) || typeof record3["eventHash"] !== "string" || !digest(record3["eventHash"]))) fail("Event log integrity metadata is invalid.", "HARNESS_ERROR");
|
|
@@ -320,6 +346,7 @@ var FileEventStore = class {
|
|
|
320
346
|
if (!isEventType(event.type)) fail("Event type is invalid.", "INVALID_INPUT");
|
|
321
347
|
if (SESSION_EVENT_TYPES.has(event.type) && (!event.sessionId || !event.sessionId.trim())) fail("Session events require a sessionId.", "INVALID_INPUT");
|
|
322
348
|
if (event.sessionId !== void 0 && !event.sessionId.trim()) fail("Event sessionId cannot be empty.", "INVALID_INPUT");
|
|
349
|
+
if (event.correlation !== void 0 && (!event.correlation.operationId || !event.correlation.operationId.trim())) fail("Event correlation operationId is required.", "INVALID_INPUT");
|
|
323
350
|
const path = eventPath(this.stateDir, event.runId);
|
|
324
351
|
const lock = lockPath(this.stateDir, event.runId);
|
|
325
352
|
mkdirSync(join(this.stateDir, "runs", event.runId), { recursive: true });
|
|
@@ -334,7 +361,7 @@ var FileEventStore = class {
|
|
|
334
361
|
try {
|
|
335
362
|
const events2 = this.readUnlocked(event.runId);
|
|
336
363
|
const previous = events2.at(-1);
|
|
337
|
-
const body2 = { schemaVersion: HARNESS_EVENT_SCHEMA_VERSION, sequence: events2.length + 1, at: (/* @__PURE__ */ new Date()).toISOString(), runId: event.runId, sourceRevision: event.sourceRevision, configHash: event.configHash, ...event.sessionId ? { sessionId: event.sessionId } : {}, ...previous?.eventHash ? { previousHash: previous.eventHash } : events2.length ? {} : { previousHash: EVENT_LOG_GENESIS }, type: event.type, payload: event.payload };
|
|
364
|
+
const body2 = { schemaVersion: HARNESS_EVENT_SCHEMA_VERSION, sequence: events2.length + 1, at: (/* @__PURE__ */ new Date()).toISOString(), runId: event.runId, sourceRevision: event.sourceRevision, configHash: event.configHash, ...event.correlation ? { correlation: event.correlation } : {}, ...event.sessionId ? { sessionId: event.sessionId } : {}, ...previous?.eventHash ? { previousHash: previous.eventHash } : events2.length ? {} : { previousHash: EVENT_LOG_GENESIS }, type: event.type, payload: event.payload };
|
|
338
365
|
const record3 = events2.length && !previous?.eventHash ? body2 : { ...body2, eventHash: eventDigest(body2) };
|
|
339
366
|
appendFileSync(path, `${JSON.stringify(record3)}
|
|
340
367
|
`, "utf8");
|
|
@@ -459,8 +486,7 @@ var saveRun2 = (stateDir, run) => {
|
|
|
459
486
|
store.append({ runId: run.runId, sourceRevision: run.sourceRevision, configHash: run.configHash, type: "context.attached", payload: { providerId: snapshot.providerId, sourceHash: snapshot.sourceHash, snapshotHash: snapshot.snapshotHash, query: snapshot.query } });
|
|
460
487
|
}
|
|
461
488
|
};
|
|
462
|
-
var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized, contextSnapshots = []
|
|
463
|
-
const contractHash = hashJson(loaded.config.contract);
|
|
489
|
+
var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized, contextSnapshots = [] }) => {
|
|
464
490
|
const run = {
|
|
465
491
|
type: "agentskit-harness-run",
|
|
466
492
|
schemaVersion: 1,
|
|
@@ -468,17 +494,18 @@ var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized,
|
|
|
468
494
|
project: loaded.config.project,
|
|
469
495
|
state: "PLANNED",
|
|
470
496
|
configHash: loaded.configHash,
|
|
471
|
-
contractHash,
|
|
497
|
+
contractHash: hashJson(loaded.config.contract),
|
|
472
498
|
sourceRevision: baseline.revision,
|
|
473
499
|
sourceStatusHash: baseline.statusHash,
|
|
474
500
|
baseline,
|
|
475
|
-
|
|
476
|
-
|
|
501
|
+
autonomy: loaded.config.autonomy,
|
|
502
|
+
contractApproval: { actor: "human", at: now(), contractHash: hashJson(loaded.config.contract) },
|
|
503
|
+
checks: loaded.config.checks.map(({ id: id2, category: category2 }) => ({ id: id2, category: category2, status: "pending" })),
|
|
477
504
|
contextSnapshots,
|
|
478
505
|
...contextSnapshots.length ? { contextHash: hashContextSnapshots(contextSnapshots) } : {},
|
|
479
506
|
...loaded.config.benchmark ? { benchmark: loaded.config.benchmark } : {},
|
|
480
507
|
outcomes: loaded.config.contract.outcomes.map(({ id: id2, statement, checks }) => ({ id: id2, statement, checks, status: "pending" })),
|
|
481
|
-
transitions: [{ from: null, to: "PLANNED", at: now(), actor:
|
|
508
|
+
transitions: [{ from: null, to: "PLANNED", at: now(), actor: "human" }],
|
|
482
509
|
evidenceReferences: [],
|
|
483
510
|
...supersedes ? { supersedes } : {},
|
|
484
511
|
...dirtyBaselineAuthorized ? { dirtyBaselineAuthorized: true } : {}
|
|
@@ -531,22 +558,97 @@ var git = async (root, args) => {
|
|
|
531
558
|
};
|
|
532
559
|
var sourceSnapshot = async (root, stateDir) => {
|
|
533
560
|
const revision = await git(root, ["rev-parse", "HEAD"]);
|
|
561
|
+
if (!revision) fail("Current-source evidence requires a Git repository with a committed HEAD.", "GIT_REQUIRED");
|
|
534
562
|
const stateRelative = relative(root, stateDir).replaceAll("\\", "/");
|
|
535
563
|
const pathspec = ["--", "."];
|
|
536
564
|
if (stateRelative && stateRelative !== ".." && !stateRelative.startsWith("../")) pathspec.push(`:(exclude)${stateRelative}`);
|
|
537
|
-
const
|
|
565
|
+
const status2 = await git(root, ["status", "--porcelain=v1", "--untracked-files=all", ...pathspec]);
|
|
538
566
|
const diff = await git(root, ["diff", "--no-ext-diff", "--binary", "HEAD", ...pathspec]);
|
|
539
567
|
const untrackedPaths = (await git(root, ["ls-files", "--others", "--exclude-standard", "-z"])).split("\0").filter(Boolean).filter((path) => !stateRelative || path !== stateRelative && !path.startsWith(`${stateRelative}/`));
|
|
540
|
-
const untracked = untrackedPaths.
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
568
|
+
const untracked = untrackedPaths.map((path) => ({ path, hash: sha256(readFileSync(resolve(root, path))) }));
|
|
569
|
+
const fingerprint = { revision, status: status2, diff, untracked };
|
|
570
|
+
return { revision, status: status2, statusHash: hashJson(fingerprint) };
|
|
571
|
+
};
|
|
572
|
+
var thresholds = (value = {}) => {
|
|
573
|
+
const result = { warningPercent: value.warningPercent ?? 75, criticalPercent: value.criticalPercent ?? 90 };
|
|
574
|
+
if (![result.warningPercent, result.criticalPercent].every((item) => Number.isFinite(item) && item >= 0 && item <= 100) || result.warningPercent > result.criticalPercent) fail("Machine thresholds must be between 0 and 100 and warning must not exceed critical.", "INVALID_INPUT");
|
|
575
|
+
return result;
|
|
576
|
+
};
|
|
577
|
+
var linuxSwap = () => {
|
|
578
|
+
if (process.platform !== "linux" || !existsSync("/proc/meminfo")) return void 0;
|
|
579
|
+
const values = Object.fromEntries(readFileSync("/proc/meminfo", "utf8").split(/\r?\n/).flatMap((line) => {
|
|
580
|
+
const match = line.match(/^(SwapTotal|SwapFree):\s+(\d+)\s+kB$/);
|
|
581
|
+
return match ? [[match[1], Number(match[2])]] : [];
|
|
582
|
+
}));
|
|
583
|
+
if (!values["SwapTotal"]) return void 0;
|
|
584
|
+
return Number(((1 - (values["SwapFree"] ?? 0) / values["SwapTotal"]) * 100).toFixed(2));
|
|
585
|
+
};
|
|
586
|
+
var sampleMachine = () => {
|
|
587
|
+
const cpus$1 = Math.max(1, cpus().length);
|
|
588
|
+
const load1 = Math.max(0, loadavg()[0] ?? 0);
|
|
589
|
+
const memory = Math.max(0, Math.min(100, (1 - freemem() / Math.max(1, totalmem())) * 100));
|
|
590
|
+
const swapUsedPercent = linuxSwap();
|
|
591
|
+
return {
|
|
592
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
593
|
+
cpus: cpus$1,
|
|
594
|
+
load1: Number(load1.toFixed(4)),
|
|
595
|
+
load1PerCpuPercent: Number(Math.min(100, load1 / cpus$1 * 100).toFixed(2)),
|
|
596
|
+
memoryUsedPercent: Number(memory.toFixed(2)),
|
|
597
|
+
rssBytes: process.memoryUsage().rss,
|
|
598
|
+
...swapUsedPercent === void 0 ? {} : { swapUsedPercent }
|
|
599
|
+
};
|
|
600
|
+
};
|
|
601
|
+
var summarizeMachine = (samples, sampleIntervalMs = 5e3, limits = {}) => {
|
|
602
|
+
const limit = thresholds(limits);
|
|
603
|
+
const load = samples.map((sample) => sample.load1PerCpuPercent);
|
|
604
|
+
const memory = samples.map((sample) => sample.memoryUsedPercent);
|
|
605
|
+
const rss = samples.map((sample) => sample.rssBytes);
|
|
606
|
+
return {
|
|
607
|
+
sampleIntervalMs,
|
|
608
|
+
samples,
|
|
609
|
+
peakLoad1PerCpuPercent: Number(Math.max(...load, 0).toFixed(2)),
|
|
610
|
+
peakMemoryUsedPercent: Number(Math.max(...memory, 0).toFixed(2)),
|
|
611
|
+
peakRssBytes: Math.max(...rss, 0),
|
|
612
|
+
pressureEvents: samples.filter((sample) => sample.load1PerCpuPercent >= limit.criticalPercent || sample.memoryUsedPercent >= limit.criticalPercent || (sample.swapUsedPercent ?? 0) >= limit.criticalPercent || sample.memoryPressure === "critical").length,
|
|
613
|
+
throttleEvents: 0,
|
|
614
|
+
minimumEffectiveConcurrency: 0
|
|
615
|
+
};
|
|
616
|
+
};
|
|
617
|
+
var adaptiveConcurrency = (configured, sample, limits = {}) => {
|
|
618
|
+
if (!Number.isInteger(configured) || configured < 1) throw new Error("configured concurrency must be a positive integer.");
|
|
619
|
+
const limit = thresholds(limits);
|
|
620
|
+
const critical = sample.load1PerCpuPercent >= limit.criticalPercent || sample.memoryUsedPercent >= limit.criticalPercent || (sample.swapUsedPercent ?? 0) >= limit.criticalPercent || sample.memoryPressure === "critical";
|
|
621
|
+
const warning = sample.load1PerCpuPercent >= limit.warningPercent || sample.memoryUsedPercent >= limit.warningPercent || (sample.swapUsedPercent ?? 0) >= limit.warningPercent || sample.memoryPressure === "warning";
|
|
622
|
+
if (critical) return 1;
|
|
623
|
+
if (warning) return Math.min(configured, 2);
|
|
624
|
+
return configured;
|
|
625
|
+
};
|
|
626
|
+
var createMachineMonitor = (sampleIntervalMs = 5e3, options2 = {}) => {
|
|
627
|
+
const sampler = options2.sample ?? sampleMachine;
|
|
628
|
+
const limits = thresholds(options2.thresholds);
|
|
629
|
+
const samples = [sampler()];
|
|
630
|
+
let throttleEvents = 0;
|
|
631
|
+
const effectiveConcurrency = [];
|
|
632
|
+
const record3 = () => {
|
|
633
|
+
const sample = sampler();
|
|
634
|
+
samples.push(sample);
|
|
635
|
+
return sample;
|
|
636
|
+
};
|
|
637
|
+
const timer = setInterval(record3, sampleIntervalMs);
|
|
638
|
+
timer.unref();
|
|
639
|
+
return {
|
|
640
|
+
sample: record3,
|
|
641
|
+
observeConcurrency: (value) => effectiveConcurrency.push(value),
|
|
642
|
+
markThrottle: () => {
|
|
643
|
+
throttleEvents += 1;
|
|
644
|
+
},
|
|
645
|
+
stop: () => {
|
|
646
|
+
clearInterval(timer);
|
|
647
|
+
record3();
|
|
648
|
+
const summary = summarizeMachine(samples, sampleIntervalMs, limits);
|
|
649
|
+
return { ...summary, throttleEvents, minimumEffectiveConcurrency: effectiveConcurrency.length ? Math.min(...effectiveConcurrency) : 0 };
|
|
546
650
|
}
|
|
547
|
-
}
|
|
548
|
-
const fingerprint = { revision, status, diff, untracked };
|
|
549
|
-
return { revision: revision || `content:${hashJson(fingerprint)}`, status, statusHash: hashJson(fingerprint) };
|
|
651
|
+
};
|
|
550
652
|
};
|
|
551
653
|
|
|
552
654
|
// src/verification.ts
|
|
@@ -575,6 +677,12 @@ var runCommand = (check, cwd) => new Promise((resolveResult) => {
|
|
|
575
677
|
resolveResult({ exitCode: exitCode ?? 1, timedOut, stdout, stderr, durationMs: Date.now() - started });
|
|
576
678
|
});
|
|
577
679
|
});
|
|
680
|
+
var executeCheck = async (check, cwd, checkDir, outcomes) => {
|
|
681
|
+
const result = await runCommand(check, cwd);
|
|
682
|
+
const evidence = parseStructuredEvidence(result.stdout);
|
|
683
|
+
const failures = result.exitCode === 0 && !result.timedOut && evidence ? validateEvidence(cwd, check, evidence, outcomes) : [result.timedOut ? "check timed out" : result.exitCode !== 0 ? `exit code ${result.exitCode}` : "missing final structured evidence"];
|
|
684
|
+
return { check: { id: check.id, category: check.category, status: failures.length ? "failed" : "passed", exitCode: result.exitCode, durationMs: result.durationMs, ...evidence ? { evidence } : {}, ...failures.length ? { failures } : {} }, stdout: result.stdout, stderr: result.stderr, durationMs: result.durationMs };
|
|
685
|
+
};
|
|
578
686
|
var currentBinding = async (loaded) => ({ source: await sourceSnapshot(loaded.root, loaded.stateDir), configHash: loaded.configHash });
|
|
579
687
|
var staleRun = (loaded, run, reason) => {
|
|
580
688
|
const stale = transition(run, "STALE", reason);
|
|
@@ -587,11 +695,8 @@ var isFresh = async (loaded, run) => {
|
|
|
587
695
|
return current.configHash === run.configHash && current.source.revision === run.sourceRevision && current.source.statusHash === run.sourceStatusHash;
|
|
588
696
|
};
|
|
589
697
|
var planRun = async ({ configPath, decision, actor = "human", allowDirty = false, contextSnapshots = [] }) => {
|
|
590
|
-
|
|
591
|
-
if (!
|
|
592
|
-
assertHuman(actor);
|
|
593
|
-
if (!approvedDecision(decision)) fail("Contract was not approved.", "CLARIFYING");
|
|
594
|
-
}
|
|
698
|
+
assertHuman(actor);
|
|
699
|
+
if (!approvedDecision(decision)) fail("Contract was not approved.", "CLARIFYING");
|
|
595
700
|
const loaded = loadConfig(configPath);
|
|
596
701
|
if (loaded.config.contract.ambiguities.length) fail(`Unresolved ambiguities remain: ${loaded.config.contract.ambiguities.join(" | ")}`, "CLARIFYING");
|
|
597
702
|
const validatedContextSnapshots = validateContextSnapshots(contextSnapshots);
|
|
@@ -603,7 +708,7 @@ ${meaningful.join("\n")}
|
|
|
603
708
|
Use --allow-dirty only with explicit human authorization.`, "WORKTREE_DIRTY");
|
|
604
709
|
const previous = loadLatestRun(loaded.stateDir);
|
|
605
710
|
if (previous && !["STALE", "SUPERSEDED"].includes(previous.state)) fail(`An active run already exists: ${previous.runId} (${previous.state}).`, "ACTIVE_RUN");
|
|
606
|
-
return createRun({ loaded, baseline, supersedes: previous?.runId, dirtyBaselineAuthorized: allowDirty, contextSnapshots: validatedContextSnapshots
|
|
711
|
+
return createRun({ loaded, baseline, supersedes: previous?.runId, dirtyBaselineAuthorized: allowDirty, contextSnapshots: validatedContextSnapshots });
|
|
607
712
|
};
|
|
608
713
|
var startRun = (loaded) => {
|
|
609
714
|
const run = requireRun(loadLatestRun(loaded.stateDir));
|
|
@@ -623,42 +728,81 @@ var cancelRun = async ({ configPath, runId, reason = "Run cancelled by a human."
|
|
|
623
728
|
};
|
|
624
729
|
var verifyRun = async ({ configPath }) => {
|
|
625
730
|
const loaded = loadConfig(configPath);
|
|
731
|
+
const machineMonitor = createMachineMonitor();
|
|
626
732
|
const run = requireRun(loadLatestRun(loaded.stateDir));
|
|
627
733
|
if (!["IMPLEMENTING", "VERIFYING"].includes(run.state)) {
|
|
628
734
|
if (["AWAITING_HUMAN_APPROVAL", "AWAITING_AUTHORIZATION", "COMPLETE"].includes(run.state) && !await isFresh(loaded, run)) staleRun(loaded, run, "Run is stale because source or contract changed.");
|
|
629
735
|
fail(`Cannot verify from ${run.state}.`, "INVALID_STATE");
|
|
630
736
|
}
|
|
631
737
|
if (run.configHash !== loaded.configHash) staleRun(loaded, run, "Run is stale because the verification contract changed.");
|
|
632
|
-
const
|
|
633
|
-
let current = { ...transition(run, "VERIFYING", "Verification started.", "agent"), sourceRevision:
|
|
738
|
+
const binding2 = await currentBinding(loaded);
|
|
739
|
+
let current = { ...transition(run, "VERIFYING", "Verification started.", "agent"), sourceRevision: binding2.source.revision, sourceStatusHash: binding2.source.statusHash };
|
|
634
740
|
saveRun2(loaded.stateDir, current);
|
|
635
741
|
const checkDir = join(loaded.stateDir, "runs", current.runId, "checks");
|
|
636
742
|
mkdirSync(checkDir, { recursive: true });
|
|
637
743
|
const outcomesByCheck = new Map(loaded.config.checks.map((check) => [check.id, loaded.config.contract.outcomes.filter((outcome) => outcome.checks.includes(check.id)).map((outcome) => outcome.id)]));
|
|
638
744
|
let totalDurationMs = 0;
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
745
|
+
let activeChecks = 0;
|
|
746
|
+
let observedPeakConcurrency = 0;
|
|
747
|
+
const verificationStarted = Date.now();
|
|
748
|
+
const maxConcurrency = loaded.config.verification?.maxConcurrency ?? 1;
|
|
749
|
+
const requiredChecks = loaded.config.checks.filter((check) => check.required);
|
|
750
|
+
for (const check of loaded.config.checks.filter((item) => !item.required)) {
|
|
751
|
+
const nextCheck = { id: check.id, category: check.category, status: "not-applicable", failures: [check.reason ?? "Not required by the selected profile."] };
|
|
752
|
+
current = { ...current, checks: current.checks.map((item) => item.id === check.id ? nextCheck : item) };
|
|
753
|
+
}
|
|
754
|
+
const remaining = new Set(requiredChecks.map((check) => check.id));
|
|
755
|
+
const completed = new Set(loaded.config.checks.filter((check) => !check.required).map((check) => check.id));
|
|
756
|
+
while (remaining.size) {
|
|
757
|
+
const ready = requiredChecks.filter((check) => remaining.has(check.id) && (check.dependsOn ?? []).every((dependency) => completed.has(dependency)));
|
|
758
|
+
if (!ready.length) fail("Check dependency graph contains an unknown dependency or cycle.", "INVALID_CONFIG");
|
|
759
|
+
const buildChecks = ready.filter((check) => check.category === "build");
|
|
760
|
+
const queue = buildChecks.length ? buildChecks : ready;
|
|
761
|
+
let offset = 0;
|
|
762
|
+
while (offset < queue.length) {
|
|
763
|
+
const effectiveConcurrency = buildChecks.length ? 1 : adaptiveConcurrency(maxConcurrency, machineMonitor.sample());
|
|
764
|
+
machineMonitor.observeConcurrency(effectiveConcurrency);
|
|
765
|
+
if (effectiveConcurrency < maxConcurrency) machineMonitor.markThrottle();
|
|
766
|
+
const batch = queue.slice(offset, offset + effectiveConcurrency);
|
|
767
|
+
const executed = await Promise.all(batch.map(async (check) => {
|
|
768
|
+
activeChecks += 1;
|
|
769
|
+
observedPeakConcurrency = Math.max(observedPeakConcurrency, activeChecks);
|
|
770
|
+
try {
|
|
771
|
+
return await executeCheck(check, loaded.root, checkDir, outcomesByCheck.get(check.id) ?? []);
|
|
772
|
+
} finally {
|
|
773
|
+
activeChecks -= 1;
|
|
774
|
+
}
|
|
775
|
+
}));
|
|
776
|
+
for (const item of executed) {
|
|
777
|
+
totalDurationMs += item.durationMs;
|
|
778
|
+
const stdoutPath = join(checkDir, `${item.check.id}.stdout`);
|
|
779
|
+
const stderrPath = join(checkDir, `${item.check.id}.stderr`);
|
|
780
|
+
writeFileSync(stdoutPath, item.stdout, "utf8");
|
|
781
|
+
writeFileSync(stderrPath, item.stderr, "utf8");
|
|
782
|
+
current = { ...current, evidenceReferences: [...current.evidenceReferences, { checkId: item.check.id, stdout: relative(loaded.stateDir, stdoutPath), stderr: relative(loaded.stateDir, stderrPath) }], checks: current.checks.map((check) => check.id === item.check.id ? item.check : check) };
|
|
783
|
+
}
|
|
784
|
+
saveRun2(loaded.stateDir, current);
|
|
785
|
+
for (const check of batch) {
|
|
786
|
+
remaining.delete(check.id);
|
|
787
|
+
completed.add(check.id);
|
|
788
|
+
}
|
|
789
|
+
offset += batch.length;
|
|
790
|
+
}
|
|
651
791
|
}
|
|
652
792
|
const statuses = new Map(current.checks.map((check) => [check.id, check.status]));
|
|
653
793
|
const budgetExceeded = loaded.config.budget?.maxDurationMs !== void 0 && totalDurationMs > loaded.config.budget.maxDurationMs;
|
|
654
|
-
const allPassed = loaded.config.checks.every((check) => statuses.get(check.id) === "passed") && !budgetExceeded;
|
|
655
|
-
current = { ...current, outcomes: current.outcomes.map((outcome) =>
|
|
656
|
-
|
|
657
|
-
|
|
794
|
+
const allPassed = loaded.config.checks.every((check) => !check.required || statuses.get(check.id) === "passed") && !budgetExceeded;
|
|
795
|
+
current = { ...current, outcomes: current.outcomes.map((outcome) => {
|
|
796
|
+
const required8 = outcome.checks.filter((id2) => loaded.config.checks.find((check) => check.id === id2)?.required);
|
|
797
|
+
return { ...outcome, status: required8.length === 0 ? "not-applicable" : required8.every((id2) => statuses.get(id2) === "passed") ? "passed" : "failed" };
|
|
798
|
+
}), metrics: { totalDurationMs, wallDurationMs: Date.now() - verificationStarted, peakConcurrency: observedPeakConcurrency, budgetExceeded, machine: machineMonitor.stop() } };
|
|
799
|
+
const digest3 = verificationDigest(current);
|
|
800
|
+
current = { ...current, verificationDigest: digest3 };
|
|
658
801
|
saveRun2(loaded.stateDir, current);
|
|
659
|
-
new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest:
|
|
660
|
-
const
|
|
661
|
-
|
|
802
|
+
new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest: digest3, checkCount: current.checks.length, outcomeCount: current.outcomes.length, totalDurationMs, budgetExceeded } });
|
|
803
|
+
const automatic = allPassed && current.autonomy === "yolo" && !loaded.config.tracking.required && loaded.config.contract.ambiguities.length === 0;
|
|
804
|
+
const nextState = allPassed ? automatic ? "COMPLETE" : "AWAITING_HUMAN_APPROVAL" : "BLOCKED";
|
|
805
|
+
current = { ...transition(current, nextState, automatic ? "All applicable checks passed; YOLO policy permits automatic completion." : allPassed ? "All configured checks passed; human approval is required." : budgetExceeded ? "Verification budget was exceeded." : "A configured check failed or lacked structured evidence.", "harness") };
|
|
662
806
|
saveRun2(loaded.stateDir, current);
|
|
663
807
|
setLatest(loaded.stateDir, current);
|
|
664
808
|
return current;
|
|
@@ -672,11 +816,6 @@ var assertVerificationAttestation = (loaded, run) => {
|
|
|
672
816
|
const event = new FileEventStore(loaded.stateDir).read(run.runId).filter((item) => item.type === "verification.completed").at(-1);
|
|
673
817
|
if (!event || event.payload.verificationDigest !== expected || event.sourceRevision !== run.sourceRevision || event.configHash !== run.configHash) fail("Verification projection attestation is missing from the event log.", "HARNESS_ERROR");
|
|
674
818
|
};
|
|
675
|
-
var plannerForRetry = (loaded, run) => {
|
|
676
|
-
if (run.contractPreparation) return "ci";
|
|
677
|
-
if (!run.supersedes) return "human";
|
|
678
|
-
return plannerForRetry(loaded, readRun(loaded.stateDir, run.supersedes));
|
|
679
|
-
};
|
|
680
819
|
var recordDecision = (loaded, run, type, payload) => {
|
|
681
820
|
new FileEventStore(loaded.stateDir).append({ runId: run.runId, sourceRevision: run.sourceRevision, configHash: run.configHash, type, payload: type === "authorization.recorded" ? { ...payload, target: payload.target ?? fail("tracking.target is required when authorizing.", "INVALID_CONFIG") } : payload });
|
|
682
821
|
};
|
|
@@ -696,7 +835,7 @@ var reconcileRun = async ({ configPath, runId }) => {
|
|
|
696
835
|
if (eventLog.status !== "verified") fail("Terminal run requires a verified event log.", "HARNESS_ERROR");
|
|
697
836
|
assertVerificationAttestation(loaded, run);
|
|
698
837
|
}
|
|
699
|
-
if (run.state === "AWAITING_AUTHORIZATION" || run.state === "COMPLETE") {
|
|
838
|
+
if ((run.state === "AWAITING_AUTHORIZATION" || run.state === "COMPLETE") && run.autonomy !== "yolo") {
|
|
700
839
|
const approval = events2.filter((event) => event.type === "approval.recorded").at(-1) ?? fail("Terminal run is missing its human approval event.", "HARNESS_ERROR");
|
|
701
840
|
assertDecisionProjection(run, approval.payload, run.state === "COMPLETE" && !loaded.config.tracking.required ? "COMPLETE" : "AWAITING_AUTHORIZATION");
|
|
702
841
|
if (!run.humanApproval || run.humanApproval.actor !== "human" || run.humanApproval.verificationDigest !== run.verificationDigest || run.humanApproval.sourceRevision !== run.sourceRevision || run.humanApproval.contractHash !== run.contractHash) fail("Human approval projection is inconsistent with its audit event.", "HARNESS_ERROR");
|
|
@@ -758,7 +897,7 @@ var retryRun = async ({ configPath }) => {
|
|
|
758
897
|
const baseline = await sourceSnapshot(loaded.root, loaded.stateDir);
|
|
759
898
|
const superseded = transition(previousRun, "SUPERSEDED", "Retry superseded the previous run.", "harness");
|
|
760
899
|
saveRun2(loaded.stateDir, superseded);
|
|
761
|
-
const run = await createRun({ loaded, baseline, supersedes: previousRun.runId, dirtyBaselineAuthorized: previousRun.dirtyBaselineAuthorized
|
|
900
|
+
const run = await createRun({ loaded, baseline, supersedes: previousRun.runId, dirtyBaselineAuthorized: previousRun.dirtyBaselineAuthorized });
|
|
762
901
|
const next = transition(run, "IMPLEMENTING", "Retry started after a previous attempt.", "agent");
|
|
763
902
|
saveRun2(loaded.stateDir, next);
|
|
764
903
|
setLatest(loaded.stateDir, next);
|
|
@@ -785,14 +924,305 @@ var createDocBridgeContextProvider = ({ root, indexPath = ".doc-bridge/index.jso
|
|
|
785
924
|
return { providerId: "doc-bridge", query, references, sourceHash: contentHash, snapshotHash: hashContextSnapshot({ providerId: "doc-bridge", query, references, sourceHash: contentHash }), resolvedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
786
925
|
}
|
|
787
926
|
});
|
|
927
|
+
|
|
928
|
+
// src/discovery.ts
|
|
929
|
+
var required = (value, label) => {
|
|
930
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
931
|
+
return value.trim();
|
|
932
|
+
};
|
|
933
|
+
var unique = (values, label) => {
|
|
934
|
+
if (new Set(values).size !== values.length) fail(`${label} must be unique.`, "INVALID_INPUT");
|
|
935
|
+
};
|
|
936
|
+
var validate = (input) => {
|
|
937
|
+
required(input.issueId, "issueId");
|
|
938
|
+
required(input.sourceRevision, "sourceRevision");
|
|
939
|
+
required(input.contractHash, "contractHash");
|
|
940
|
+
if (!Array.isArray(input.ambiguities)) fail("ambiguities must be an array.", "INVALID_INPUT");
|
|
941
|
+
unique(input.ambiguities.map((item) => required(item.id, "ambiguity.id")), "ambiguity ids");
|
|
942
|
+
const assumptions = /* @__PURE__ */ new Map();
|
|
943
|
+
for (const assumption of input.approvedAssumptions ?? []) {
|
|
944
|
+
const id2 = required(assumption.id, "assumption.id");
|
|
945
|
+
if (assumptions.has(id2)) fail("assumption ids must be unique.", "INVALID_INPUT");
|
|
946
|
+
assumptions.set(id2, { id: id2, policyId: required(assumption.policyId, "assumption.policyId"), resolution: required(assumption.resolution, "assumption.resolution") });
|
|
947
|
+
}
|
|
948
|
+
for (const ambiguity of input.ambiguities) {
|
|
949
|
+
required(ambiguity.question, "ambiguity.question");
|
|
950
|
+
if (typeof ambiguity.material !== "boolean") fail("ambiguity.material must be boolean.", "INVALID_INPUT");
|
|
951
|
+
if (!Array.isArray(ambiguity.options) || ambiguity.options.length < 2 || ambiguity.options.length > 4) fail("ambiguity.options must contain 2 to 4 options.", "INVALID_INPUT");
|
|
952
|
+
unique(ambiguity.options.map((option) => required(option.id, "option.id")), "option ids");
|
|
953
|
+
for (const option of ambiguity.options) {
|
|
954
|
+
required(option.summary, "option.summary");
|
|
955
|
+
required(option.impact, "option.impact");
|
|
956
|
+
}
|
|
957
|
+
if (!ambiguity.options.some((option) => option.id === ambiguity.recommendedOptionId)) fail("recommendedOptionId must identify an option.", "INVALID_INPUT");
|
|
958
|
+
if (!ambiguity.material && (!ambiguity.assumptionId || !assumptions.has(ambiguity.assumptionId))) fail("non-material ambiguity requires an approved assumption.", "INVALID_INPUT");
|
|
959
|
+
}
|
|
960
|
+
return { assumptions };
|
|
961
|
+
};
|
|
962
|
+
var digest2 = (result) => hashJson(result);
|
|
963
|
+
var assessDiscovery = (input) => {
|
|
964
|
+
const { assumptions } = validate(input);
|
|
965
|
+
const human = input.ambiguities.filter((ambiguity) => ambiguity.material);
|
|
966
|
+
const decisionLog = input.ambiguities.map((ambiguity) => {
|
|
967
|
+
if (ambiguity.material) return { ambiguityId: ambiguity.id, kind: "human-decision-required", detail: `Recommendation: ${ambiguity.recommendedOptionId}.` };
|
|
968
|
+
const assumption = assumptions.get(ambiguity.assumptionId);
|
|
969
|
+
return { ambiguityId: ambiguity.id, kind: "approved-assumption", detail: assumption.resolution, policyId: assumption.policyId };
|
|
970
|
+
});
|
|
971
|
+
const base = {
|
|
972
|
+
version: 1,
|
|
973
|
+
issueId: input.issueId,
|
|
974
|
+
sourceRevision: input.sourceRevision,
|
|
975
|
+
contractHash: input.contractHash,
|
|
976
|
+
...input.contextHash ? { contextHash: input.contextHash } : {},
|
|
977
|
+
status: human.length ? "awaiting-decision" : "ready",
|
|
978
|
+
...human.length ? { packet: {
|
|
979
|
+
issueId: input.issueId,
|
|
980
|
+
contractHash: input.contractHash,
|
|
981
|
+
sourceRevision: input.sourceRevision,
|
|
982
|
+
...input.contextHash ? { contextHash: input.contextHash } : {},
|
|
983
|
+
decisions: human.map((ambiguity) => ({ id: ambiguity.id, question: ambiguity.question, options: ambiguity.options, recommendedOptionId: ambiguity.recommendedOptionId }))
|
|
984
|
+
} } : {},
|
|
985
|
+
decisionLog
|
|
986
|
+
};
|
|
987
|
+
return { ...base, digest: digest2(base) };
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
// src/wip.ts
|
|
991
|
+
var WIP_STATES = ["ready", "implementing", "blocked", "awaiting-decision", "awaiting-acceptance", "done", "cancelled"];
|
|
992
|
+
var terminal = /* @__PURE__ */ new Set(["done", "cancelled"]);
|
|
993
|
+
var required2 = (value, label) => {
|
|
994
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
995
|
+
return value.trim();
|
|
996
|
+
};
|
|
997
|
+
var assessWip = ({ entries, candidate, maxInFlight = 3 }) => {
|
|
998
|
+
if (!Array.isArray(entries)) fail("entries must be an array.", "INVALID_INPUT");
|
|
999
|
+
if (!Number.isInteger(maxInFlight) || maxInFlight < 1) fail("maxInFlight must be a positive integer.", "INVALID_INPUT");
|
|
1000
|
+
const candidateId = required2(candidate.issueId, "candidate.issueId");
|
|
1001
|
+
if (candidate.kind !== "new" && candidate.kind !== "resume") fail("candidate.kind must be new or resume.", "INVALID_INPUT");
|
|
1002
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1003
|
+
const counts = Object.fromEntries(WIP_STATES.map((state) => [state, 0]));
|
|
1004
|
+
for (const entry of entries) {
|
|
1005
|
+
const id2 = required2(entry.issueId, "entry.issueId");
|
|
1006
|
+
if (ids.has(id2)) fail("entry issueIds must be unique.", "INVALID_INPUT");
|
|
1007
|
+
ids.add(id2);
|
|
1008
|
+
if (!WIP_STATES.includes(entry.state)) fail(`Unknown WIP state: ${entry.state}.`, "INVALID_INPUT");
|
|
1009
|
+
counts[entry.state] += 1;
|
|
1010
|
+
}
|
|
1011
|
+
const inFlight = entries.filter((entry) => !terminal.has(entry.state));
|
|
1012
|
+
const existing = entries.find((entry) => entry.issueId === candidateId);
|
|
1013
|
+
if (candidate.kind === "resume") {
|
|
1014
|
+
if (!existing || terminal.has(existing.state)) return { decision: "hold", inFlight, counts, reason: "A resume requires an existing non-terminal issue." };
|
|
1015
|
+
return { decision: "admit", inFlight, counts, reason: "A resume keeps its existing WIP reservation and takes priority over new work." };
|
|
1016
|
+
}
|
|
1017
|
+
if (existing) return { decision: "hold", inFlight, counts, reason: "A new admission cannot reuse an existing issue id." };
|
|
1018
|
+
if (inFlight.length >= maxInFlight) return { decision: "hold", inFlight, counts, reason: `WIP limit ${maxInFlight} reached; blocked and awaiting-human work still count.` };
|
|
1019
|
+
return { decision: "admit", inFlight, counts, reason: `WIP slot available (${inFlight.length}/${maxInFlight}).` };
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
// src/experiment.ts
|
|
1023
|
+
var required3 = (value, label) => {
|
|
1024
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1025
|
+
return value.trim();
|
|
1026
|
+
};
|
|
1027
|
+
var comparable = (candidate, baseline) => {
|
|
1028
|
+
for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) {
|
|
1029
|
+
if (candidate[key] !== baseline[key]) fail(`Candidates must share ${key}.`, "INVALID_INPUT");
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
var selectRuntime = (candidates) => {
|
|
1033
|
+
if (!Array.isArray(candidates) || candidates.length < 2) fail("At least two runtime candidates are required.", "INVALID_INPUT");
|
|
1034
|
+
const names = /* @__PURE__ */ new Set();
|
|
1035
|
+
for (const candidate of candidates) {
|
|
1036
|
+
const runtime = required3(candidate.runtime, "candidate.runtime");
|
|
1037
|
+
if (names.has(runtime)) fail("candidate.runtime values must be unique.", "INVALID_INPUT");
|
|
1038
|
+
names.add(runtime);
|
|
1039
|
+
for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) required3(candidate[key], `candidate.${key}`);
|
|
1040
|
+
for (const key of ["humanMinutes", "durationMs", "cost"]) if (!Number.isFinite(candidate[key]) || candidate[key] < 0) fail(`candidate.${key} must be a non-negative number.`, "INVALID_INPUT");
|
|
1041
|
+
comparable(candidate, candidates[0]);
|
|
1042
|
+
}
|
|
1043
|
+
const eligible = candidates.filter((candidate) => candidate.hardGatesPassed);
|
|
1044
|
+
if (!eligible.length) return { decision: "blocked", eligible, reason: "No runtime passed every hard gate." };
|
|
1045
|
+
const selected = [...eligible].sort((left, right) => left.humanMinutes - right.humanMinutes || left.durationMs - right.durationMs || left.cost - right.cost || (left.runtime === "orca" ? -1 : right.runtime === "orca" ? 1 : left.runtime.localeCompare(right.runtime)))[0];
|
|
1046
|
+
return { decision: "selected", selected, eligible, reason: "Selected by human minutes, duration, cost, then Orca tie-break." };
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
// src/delivery.ts
|
|
1050
|
+
var required4 = (value, label) => {
|
|
1051
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1052
|
+
return value.trim();
|
|
1053
|
+
};
|
|
1054
|
+
var criteriaFor = (criteria, gate) => {
|
|
1055
|
+
if (!Array.isArray(criteria)) fail("criteria must be an array.", "INVALID_INPUT");
|
|
1056
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1057
|
+
for (const criterion of criteria) {
|
|
1058
|
+
const id2 = required4(criterion.id, "criterion.id");
|
|
1059
|
+
if (ids.has(id2)) fail("criterion ids must be unique.", "INVALID_INPUT");
|
|
1060
|
+
ids.add(id2);
|
|
1061
|
+
if (!["G2", "G3", "G4", "G5"].includes(criterion.gate)) fail("criterion.gate is invalid.", "INVALID_INPUT");
|
|
1062
|
+
if (!["passed", "failed", "pending", "not-applicable"].includes(criterion.status)) fail("criterion.status is invalid.", "INVALID_INPUT");
|
|
1063
|
+
if (criterion.status === "not-applicable" && !criterion.reason?.trim()) fail("not-applicable criteria require a reason.", "INVALID_INPUT");
|
|
1064
|
+
}
|
|
1065
|
+
return criteria.filter((criterion) => criterion.gate === gate);
|
|
1066
|
+
};
|
|
1067
|
+
var binding = (value) => ({ candidateRevision: required4(value.candidateRevision, "binding.candidateRevision"), contractHash: required4(value.contractHash, "binding.contractHash"), configHash: required4(value.configHash, "binding.configHash") });
|
|
1068
|
+
var assessed = (gate, decision, reasons, current) => {
|
|
1069
|
+
const base = { gate, decision, reasons, binding: binding(current) };
|
|
1070
|
+
return { ...base, digest: hashJson(base) };
|
|
1071
|
+
};
|
|
1072
|
+
var assessPreflight = ({ criteria, repairAttempts = 0, implementerId, reviewerId, reviewKind, reviewApproved, binding: current }) => {
|
|
1073
|
+
required4(implementerId, "implementerId");
|
|
1074
|
+
if (!Number.isInteger(repairAttempts) || repairAttempts < 0) fail("repairAttempts must be a non-negative integer.", "INVALID_INPUT");
|
|
1075
|
+
const g2 = criteriaFor(criteria, "G2");
|
|
1076
|
+
const reasons = [
|
|
1077
|
+
...g2.length ? [] : ["No G2 criteria are defined."],
|
|
1078
|
+
...g2.filter((criterion) => criterion.status === "failed" || criterion.status === "pending").map((criterion) => `${criterion.id} is ${criterion.status}.`),
|
|
1079
|
+
...reviewApproved && reviewerId && reviewerId !== implementerId && reviewKind === "adversarial" ? [] : ["An approved adversarial review by a reviewer different from the implementer is required."],
|
|
1080
|
+
...repairAttempts <= 2 ? [] : ["The two-repair limit was exceeded; preserve diagnostics and return blocked."]
|
|
1081
|
+
];
|
|
1082
|
+
return assessed("G2", reasons.length ? "blocked" : "approved", reasons, current);
|
|
1083
|
+
};
|
|
1084
|
+
var composePullRequest = ({ draft, g2, remote }) => {
|
|
1085
|
+
for (const [label, value] of Object.entries({ issueId: draft.issueId, candidateRevision: draft.candidateRevision, contractHash: draft.contractHash, configHash: draft.configHash, g2Digest: draft.g2Digest, risk: draft.risk, rollback: draft.rollback })) required4(value, `draft.${label}`);
|
|
1086
|
+
if (g2.gate !== "G2" || g2.decision !== "approved" || g2.digest !== draft.g2Digest || g2.binding.candidateRevision !== draft.candidateRevision || g2.binding.contractHash !== draft.contractHash || g2.binding.configHash !== draft.configHash) return { decision: "blocked", reason: "A current approved G2 assessment is required before a PR can be created.", idempotencyKey: hashJson(draft) };
|
|
1087
|
+
const idempotencyKey = hashJson({ issueId: draft.issueId, contractHash: draft.contractHash, action: "pull-request", revision: draft.candidateRevision });
|
|
1088
|
+
if (remote?.state === "uncertain") return { decision: "blocked", reason: "Remote PR state is uncertain; reconcile before retrying.", idempotencyKey };
|
|
1089
|
+
if (remote?.state === "confirmed") {
|
|
1090
|
+
if (remote.candidateRevision !== draft.candidateRevision || !remote.url) return { decision: "blocked", reason: "Confirmed remote PR does not match the candidate revision.", idempotencyKey };
|
|
1091
|
+
return { decision: "reuse", reason: "The idempotent remote PR already exists for this candidate revision.", idempotencyKey };
|
|
1092
|
+
}
|
|
1093
|
+
const body2 = [`## Contract`, `- Issue: ${draft.issueId}`, `- Candidate: ${draft.candidateRevision}`, `- Contract: ${draft.contractHash}`, `- Configuration: ${draft.configHash}`, "", "## G2 evidence", ...draft.evidence.map((item) => `- ${item}`), "", "## Documentation", ...draft.documentation.map((item) => `- ${item}`), "", "## Risk and rollback", `- Risk: ${draft.risk}`, `- Rollback: ${draft.rollback}`, "", "## Later gates", ...draft.pendingCriteria.length ? draft.pendingCriteria.map((item) => `- Pending: ${item}`) : ["- None."]].join("\n");
|
|
1094
|
+
return { decision: "create", body: body2, reason: "G2 is current and the remote PR is absent.", idempotencyKey };
|
|
1095
|
+
};
|
|
1096
|
+
var assessIntegration = ({ g2, candidateRevision, evidenceRevision, contractHash, configHash, ci }) => {
|
|
1097
|
+
required4(candidateRevision, "candidateRevision");
|
|
1098
|
+
required4(evidenceRevision, "evidenceRevision");
|
|
1099
|
+
if (!["passed", "failed", "pending", "not-applicable"].includes(ci)) fail("ci is invalid.", "INVALID_INPUT");
|
|
1100
|
+
const reasons = [
|
|
1101
|
+
...g2.gate === "G2" && g2.decision === "approved" ? [] : ["G2 is not approved."],
|
|
1102
|
+
...g2.binding.candidateRevision === candidateRevision && g2.binding.contractHash === contractHash && g2.binding.configHash === configHash ? [] : ["G2 is not bound to the current candidate, contract, and configuration."],
|
|
1103
|
+
...candidateRevision === evidenceRevision ? [] : ["Candidate revision changed; G3 evidence must be revalidated."],
|
|
1104
|
+
...ci === "passed" ? [] : [`Integration CI is ${ci}.`]
|
|
1105
|
+
];
|
|
1106
|
+
return assessed("G3", reasons.length ? "blocked" : "approved", reasons, { candidateRevision, contractHash, configHash });
|
|
1107
|
+
};
|
|
1108
|
+
var assessWorktreeCleanup = ({ branch, candidateRevision, contractHash, configHash, remoteBranchRevision, remotePr, integration }) => {
|
|
1109
|
+
required4(branch, "branch");
|
|
1110
|
+
required4(candidateRevision, "candidateRevision");
|
|
1111
|
+
if (remotePr === "uncertain") return { decision: "preserve", reason: "Remote PR state is uncertain; preserve the worktree for reconciliation." };
|
|
1112
|
+
if (remotePr !== "confirmed") return { decision: "preserve", reason: "No confirmed remote PR exists; preserve the worktree." };
|
|
1113
|
+
if (remoteBranchRevision !== candidateRevision) return { decision: "preserve", reason: "Remote branch SHA does not match the candidate revision." };
|
|
1114
|
+
if (integration.gate !== "G3" || integration.decision !== "approved") return { decision: "preserve", reason: "G3 is not approved." };
|
|
1115
|
+
if (integration.binding.candidateRevision !== candidateRevision || integration.binding.contractHash !== contractHash || integration.binding.configHash !== configHash) return { decision: "preserve", reason: "G3 is not bound to the current candidate, contract, and configuration." };
|
|
1116
|
+
return { decision: "clean", reason: "Remote branch, PR, and G3 evidence are confirmed for the candidate revision." };
|
|
1117
|
+
};
|
|
1118
|
+
var profileReasons = (profile) => Object.entries(profile).flatMap(([key, value]) => Array.isArray(value) ? value.length ? [] : [`RepositoryProfile.${key} is required.`] : typeof value === "string" && value.trim() ? [] : [`RepositoryProfile.${key} is required.`]);
|
|
1119
|
+
var assessProduction = ({ profile, integration, artifact, isolated, acceptanceArtifact, lowRisk = true, observationMinutes, technicalPassed, evidence, containmentPreauthorized, containmentAction, linkedDefect }) => {
|
|
1120
|
+
required4(artifact, "artifact");
|
|
1121
|
+
if (!Number.isFinite(observationMinutes) || observationMinutes < 0) fail("observationMinutes must be non-negative.", "INVALID_INPUT");
|
|
1122
|
+
const evidenceReasons = [required4(evidence.tenant, "evidence.tenant"), required4(evidence.realFlow, "evidence.realFlow"), ...Array.isArray(evidence.logs) && evidence.logs.length ? [] : ["Production evidence requires logs."], ...Array.isArray(evidence.metrics) && evidence.metrics.length ? [] : ["Production evidence requires metrics."]].filter((item) => item.startsWith("Production evidence"));
|
|
1123
|
+
const reasons = [
|
|
1124
|
+
...profileReasons(profile),
|
|
1125
|
+
...integration.gate === "G3" && integration.decision === "approved" ? [] : ["G3 is not approved."],
|
|
1126
|
+
...evidenceReasons,
|
|
1127
|
+
...isolated || acceptanceArtifact === artifact ? [] : ["Exposure requires isolation or acceptance linked to this artifact version."],
|
|
1128
|
+
...technicalPassed ? [] : [containmentPreauthorized && containmentAction?.trim() && linkedDefect?.trim() ? "Technical validation failed; pre-authorized containment and linked defect are recorded." : containmentPreauthorized ? "Technical validation failed; containment action and linked defect are required." : "Technical validation failed."],
|
|
1129
|
+
...lowRisk && observationMinutes < 15 ? ["Low-risk production validation requires a 15-minute observation window."] : []
|
|
1130
|
+
];
|
|
1131
|
+
return assessed("G4", reasons.length ? "blocked" : "approved", reasons, integration.binding);
|
|
1132
|
+
};
|
|
1133
|
+
var assessAcceptance = ({ production, acceptanceRequired, accepted, notApplicableReason, materialChange }) => {
|
|
1134
|
+
const reasons = [
|
|
1135
|
+
...production.gate === "G4" && production.decision === "approved" ? [] : ["G4 is not approved."],
|
|
1136
|
+
...materialChange ? ["A material change invalidated acceptance; return to the affected gate."] : []
|
|
1137
|
+
];
|
|
1138
|
+
if (reasons.length) return assessed("G5", "blocked", reasons, production.binding);
|
|
1139
|
+
if (acceptanceRequired && !accepted) return assessed("G5", "awaiting-acceptance", ["Business or UX acceptance is still required."], production.binding);
|
|
1140
|
+
if (!acceptanceRequired && !notApplicableReason?.trim()) return assessed("G5", "blocked", ["Acceptance marked not applicable requires a contractual reason."], production.binding);
|
|
1141
|
+
return assessed("G5", "approved", acceptanceRequired ? [] : [`Acceptance is not applicable: ${notApplicableReason}.`], production.binding);
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1144
|
+
// src/pilot.ts
|
|
1145
|
+
var required5 = (value, label) => {
|
|
1146
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1147
|
+
return value.trim();
|
|
1148
|
+
};
|
|
1149
|
+
var assessPilot = (manifest) => {
|
|
1150
|
+
required5(manifest.policyHash, "policyHash");
|
|
1151
|
+
required5(manifest.baselineReference, "baselineReference");
|
|
1152
|
+
if (!Array.isArray(manifest.entries)) fail("entries must be an array.", "INVALID_INPUT");
|
|
1153
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1154
|
+
const reasons = [];
|
|
1155
|
+
const included = [];
|
|
1156
|
+
for (const entry of manifest.entries) {
|
|
1157
|
+
const issueId = required5(entry.issueId, "entry.issueId");
|
|
1158
|
+
if (ids.has(issueId)) fail("entry issueIds must be unique; an issue cannot be substituted in the same pilot.", "INVALID_INPUT");
|
|
1159
|
+
ids.add(issueId);
|
|
1160
|
+
if (!["normal", "incident", "sensitive"].includes(entry.classification)) fail("entry.classification is invalid.", "INVALID_INPUT");
|
|
1161
|
+
if (!["included", "excluded", "aborted"].includes(entry.status)) fail("entry.status is invalid.", "INVALID_INPUT");
|
|
1162
|
+
if (entry.status !== "included" && !entry.reason?.trim()) reasons.push(`${issueId} is ${entry.status} without an auditable reason.`);
|
|
1163
|
+
if (entry.status === "included") {
|
|
1164
|
+
included.push(issueId);
|
|
1165
|
+
if (entry.classification !== "normal") reasons.push(`${issueId} is ${entry.classification}; only normal issues can enter the pilot.`);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
if (included.length !== 10) reasons.push(`Pilot requires exactly 10 included issues; found ${included.length}.`);
|
|
1169
|
+
const base = { decision: reasons.length ? "blocked" : "ready", included, reasons };
|
|
1170
|
+
return { ...base, digest: hashJson({ ...manifest, ...base }) };
|
|
1171
|
+
};
|
|
1172
|
+
var IMPROVEMENT_CYCLE_STEPS = ["adversarial-review", "g2-preflight", "baseline-record", "pilot-execution", "comparison"];
|
|
1173
|
+
var nonEmpty = (value, label) => {
|
|
1174
|
+
if (typeof value !== "string" || !value.trim()) return fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1175
|
+
return value.trim();
|
|
1176
|
+
};
|
|
1177
|
+
var validateMetrics = (metrics, index2) => {
|
|
1178
|
+
if (metrics === void 0) return void 0;
|
|
1179
|
+
for (const [key, value] of Object.entries(metrics)) {
|
|
1180
|
+
if (value !== void 0 && (typeof value !== "number" || !Number.isFinite(value) || value < 0)) return fail(`iterations[${index2}].metrics.${key} must be a non-negative number.`, "INVALID_INPUT");
|
|
1181
|
+
}
|
|
1182
|
+
return metrics;
|
|
1183
|
+
};
|
|
1184
|
+
var validateIteration = (iteration, index2) => {
|
|
1185
|
+
if (typeof iteration !== "object" || iteration === null || Array.isArray(iteration)) return fail(`iterations[${index2}] must be an object.`, "INVALID_INPUT");
|
|
1186
|
+
if (!Number.isInteger(iteration.iteration) || iteration.iteration < 1) return fail(`iterations[${index2}].iteration must be a positive integer.`, "INVALID_INPUT");
|
|
1187
|
+
if (!Array.isArray(iteration.steps) || iteration.steps.length !== IMPROVEMENT_CYCLE_STEPS.length) return fail(`iterations[${index2}].steps must contain the five cycle steps exactly once, in order.`, "INVALID_INPUT");
|
|
1188
|
+
iteration.steps.forEach((result, stepIndex) => {
|
|
1189
|
+
if (typeof result !== "object" || result === null || Array.isArray(result)) return fail(`iterations[${index2}].steps[${stepIndex}] must be an object.`, "INVALID_INPUT");
|
|
1190
|
+
if (result.step !== IMPROVEMENT_CYCLE_STEPS[stepIndex]) return fail(`iterations[${index2}].steps[${stepIndex}] must be ${IMPROVEMENT_CYCLE_STEPS[stepIndex]}.`, "INVALID_INPUT");
|
|
1191
|
+
if (!["passed", "failed", "blocked", "pending"].includes(result.status)) return fail(`iterations[${index2}].steps[${stepIndex}].status is invalid.`, "INVALID_INPUT");
|
|
1192
|
+
if (result.status !== "passed" && !nonEmpty(result.reason, `iterations[${index2}].steps[${stepIndex}].reason`)) return fail(`iterations[${index2}].steps[${stepIndex}].reason is required when the step does not pass.`, "INVALID_INPUT");
|
|
1193
|
+
});
|
|
1194
|
+
if (iteration.adjustment !== void 0) nonEmpty(iteration.adjustment, `iterations[${index2}].adjustment`);
|
|
1195
|
+
return { ...iteration, metrics: validateMetrics(iteration.metrics, index2) };
|
|
1196
|
+
};
|
|
1197
|
+
var assessImprovementCycle = (input) => {
|
|
1198
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return fail("cycle input must be an object.", "INVALID_INPUT");
|
|
1199
|
+
const cycleId = nonEmpty(input.cycleId, "cycleId");
|
|
1200
|
+
if (!Number.isInteger(input.maxIterations) || input.maxIterations < 1) return fail("maxIterations must be a positive integer.", "INVALID_INPUT");
|
|
1201
|
+
if (!Array.isArray(input.iterations) || input.iterations.length < 1) return fail("iterations must be non-empty.", "INVALID_INPUT");
|
|
1202
|
+
if (input.iterations.length > input.maxIterations) return fail("iterations cannot exceed maxIterations.", "INVALID_INPUT");
|
|
1203
|
+
const iterations = input.iterations.map(validateIteration);
|
|
1204
|
+
iterations.forEach((iteration, index2) => {
|
|
1205
|
+
if (iteration.iteration !== index2 + 1) return fail("iterations must be sequential and start at 1.", "INVALID_INPUT");
|
|
1206
|
+
if (index2 > 0 && iterations[index2 - 1]?.steps.every((step) => step.status === "passed")) return fail("a completed cycle cannot have later iterations.", "INVALID_INPUT");
|
|
1207
|
+
if (index2 < iterations.length - 1 && !iteration.adjustment) return fail(`iterations[${index2}].adjustment is required before repeating.`, "INVALID_INPUT");
|
|
1208
|
+
});
|
|
1209
|
+
const matrix = iterations.map((iteration) => {
|
|
1210
|
+
const statuses = Object.fromEntries(iteration.steps.map((step) => [step.step, step.status]));
|
|
1211
|
+
const passedSteps = iteration.steps.filter((step) => step.status === "passed").length;
|
|
1212
|
+
return { iteration: iteration.iteration, passedSteps, totalSteps: IMPROVEMENT_CYCLE_STEPS.length, passRate: Number((passedSteps / IMPROVEMENT_CYCLE_STEPS.length).toFixed(4)), statuses, ...iteration.adjustment ? { adjustment: iteration.adjustment } : {}, ...iteration.metrics ? { metrics: iteration.metrics } : {} };
|
|
1213
|
+
});
|
|
1214
|
+
const latest = iterations[iterations.length - 1];
|
|
1215
|
+
const complete = latest.steps.every((step) => step.status === "passed");
|
|
1216
|
+
const reasons = complete ? ["All five cycle steps passed."] : iterations.length >= input.maxIterations ? ["Maximum cycle iterations reached; human adjustment is required."] : latest.adjustment ? ["A failed or blocked step remains; repeat with the recorded adjustment."] : ["A failed or blocked step remains; an explicit adjustment is required before repeating."];
|
|
1217
|
+
const decision = complete ? "complete" : iterations.length >= input.maxIterations || !latest.adjustment ? "blocked" : "repeat";
|
|
1218
|
+
const result = { type: "agentskit-harness-improvement-cycle", cycleId, decision, ...decision === "repeat" ? { nextIteration: latest.iteration + 1 } : {}, reasons, matrix };
|
|
1219
|
+
const digest3 = createHash("sha256").update(JSON.stringify(result)).digest("hex");
|
|
1220
|
+
return { ...result, digest: digest3 };
|
|
1221
|
+
};
|
|
788
1222
|
var BENCHMARK_SCHEMA_VERSION = 1;
|
|
789
1223
|
var percentage = (part, total) => total ? Number((part / total).toFixed(4)) : null;
|
|
790
|
-
var DEFAULT_POLICY = { minComparableTasks: 3, maxDurationRegressionRate: 0.2, minCompletedRunsPerTask: 3, minBaselineSamplesPerTask: 3, requireZeroEscapedIncomplete: true };
|
|
791
1224
|
var improvementRate = (baseline, current) => baseline === void 0 || current === void 0 || baseline === 0 ? null : Number(((baseline - current) / baseline).toFixed(4));
|
|
792
|
-
var
|
|
793
|
-
var increaseDelta = (baseline, current) => baseline === void 0 || current === null ? null : Number((current - baseline).toFixed(4));
|
|
794
|
-
var increaseDirection = (rate2, delta) => delta === null ? improvementDirection(rate2) : delta > 0 ? "improved" : delta < 0 ? "regressed" : "unchanged";
|
|
795
|
-
var improvementDirection = (rate2) => rate2 === null ? "unavailable" : rate2 > 0 ? "improved" : rate2 < 0 ? "regressed" : "unchanged";
|
|
1225
|
+
var improvementDirection = (rate) => rate === null ? "unavailable" : rate > 0 ? "improved" : rate < 0 ? "regressed" : "unchanged";
|
|
796
1226
|
var count = (items, predicate) => items.filter(predicate).length;
|
|
797
1227
|
var median = (values) => {
|
|
798
1228
|
if (!values.length) return null;
|
|
@@ -807,25 +1237,13 @@ var reviewMinutes = (run) => {
|
|
|
807
1237
|
const elapsed = Date.parse(run.humanApproval.at) - Date.parse(reviewStart);
|
|
808
1238
|
return Number.isFinite(elapsed) && elapsed >= 0 ? Number((elapsed / 6e4).toFixed(2)) : void 0;
|
|
809
1239
|
};
|
|
810
|
-
var confidence = (comparable, completedRuns, policy) => !comparable ? "insufficient" : completedRuns >= policy.minCompletedRunsPerTask ? "reliable" : "directional";
|
|
811
|
-
var artifactAcceptanceRate = (run) => {
|
|
812
|
-
const rates = run.checks.flatMap((check) => {
|
|
813
|
-
const evidence = check.evidence;
|
|
814
|
-
if (!evidence) return [];
|
|
815
|
-
const direct = typeof evidence["artifactAcceptanceRate"] === "number" ? [evidence["artifactAcceptanceRate"]] : [];
|
|
816
|
-
const reports = Array.isArray(evidence["reports"]) ? evidence["reports"].flatMap((report) => typeof report === "object" && report !== null && typeof report["artifactAcceptanceRate"] === "number" ? [report["artifactAcceptanceRate"]] : []) : [];
|
|
817
|
-
return [...direct, ...reports].filter((rate2) => Number.isFinite(rate2) && rate2 >= 0 && rate2 <= 1);
|
|
818
|
-
});
|
|
819
|
-
return rates.length ? Number((rates.reduce((total, rate2) => total + rate2, 0) / rates.length).toFixed(4)) : void 0;
|
|
820
|
-
};
|
|
821
1240
|
var projectRun = (run) => {
|
|
822
1241
|
const checks = { total: run.checks.length, passed: count(run.checks, (check) => check.status === "passed"), failed: count(run.checks, (check) => check.status === "failed") };
|
|
823
1242
|
const outcomes = { total: run.outcomes.length, passed: count(run.outcomes, (outcome) => outcome.status === "passed"), failed: count(run.outcomes, (outcome) => outcome.status === "failed") };
|
|
824
1243
|
const evidence = { total: run.checks.length, attached: count(run.checks, (check) => check.evidence !== void 0) };
|
|
825
|
-
const acceptanceRate = artifactAcceptanceRate(run);
|
|
826
1244
|
const humanReviewMinutes = reviewMinutes(run);
|
|
827
1245
|
const escapedIncomplete = run.state === "COMPLETE" && checks.failed === 0 && outcomes.failed === 0 && evidence.attached === evidence.total ? 0 : void 0;
|
|
828
|
-
return { runId: run.runId, state: run.state, sourceRevision: run.sourceRevision, configHash: run.configHash, contractHash: run.contractHash, ...run.supersedes ? { supersedes: run.supersedes } : {}, ...run.metrics ? { durationMs: run.metrics.totalDurationMs } : {}, checks, outcomes, evidence, ...
|
|
1246
|
+
return { runId: run.runId, state: run.state, sourceRevision: run.sourceRevision, configHash: run.configHash, contractHash: run.contractHash, ...run.supersedes ? { supersedes: run.supersedes } : {}, ...run.metrics ? { durationMs: run.metrics.totalDurationMs } : {}, checks, outcomes, evidence, ...escapedIncomplete === void 0 ? {} : { escapedIncomplete }, humanApproved: run.humanApproval !== void 0, ...humanReviewMinutes === void 0 ? {} : { humanReviewMinutes }, authorized: run.authorization !== void 0, ...run.metrics?.machine ? { machine: run.metrics.machine } : {}, ...run.benchmark ? { benchmark: run.benchmark } : {} };
|
|
829
1247
|
};
|
|
830
1248
|
var summarize = (runs) => {
|
|
831
1249
|
const stateCounts = Object.fromEntries(RUN_STATES.map((state) => [state, count(runs, (run) => run.state === state)]));
|
|
@@ -836,14 +1254,6 @@ var summarize = (runs) => {
|
|
|
836
1254
|
const evidenceTotal = runs.reduce((total, run) => total + run.evidence.total, 0);
|
|
837
1255
|
const evidenceAttached = runs.reduce((total, run) => total + run.evidence.attached, 0);
|
|
838
1256
|
const firstAttempts = runs.filter((run) => !run.supersedes);
|
|
839
|
-
const superseded = new Set(runs.flatMap((run) => run.supersedes ? [run.supersedes] : []));
|
|
840
|
-
const effectiveRuns = runs.filter((run) => !superseded.has(run.runId));
|
|
841
|
-
const effectiveChecksTotal = effectiveRuns.reduce((total, run) => total + run.checks.total, 0);
|
|
842
|
-
const effectiveChecksPassed = effectiveRuns.reduce((total, run) => total + run.checks.passed, 0);
|
|
843
|
-
const effectiveOutcomesTotal = effectiveRuns.reduce((total, run) => total + run.outcomes.total, 0);
|
|
844
|
-
const effectiveOutcomesPassed = effectiveRuns.reduce((total, run) => total + run.outcomes.passed, 0);
|
|
845
|
-
const effectiveEvidenceTotal = effectiveRuns.reduce((total, run) => total + run.evidence.total, 0);
|
|
846
|
-
const effectiveEvidenceAttached = effectiveRuns.reduce((total, run) => total + run.evidence.attached, 0);
|
|
847
1257
|
const durations = runs.flatMap((run) => run.durationMs === void 0 ? [] : [run.durationMs]);
|
|
848
1258
|
return {
|
|
849
1259
|
totalRuns: runs.length,
|
|
@@ -854,12 +1264,6 @@ var summarize = (runs) => {
|
|
|
854
1264
|
firstAttemptRuns: firstAttempts.length,
|
|
855
1265
|
humanApprovedRuns: count(runs, (run) => run.humanApproved),
|
|
856
1266
|
authorizedRuns: count(runs, (run) => run.authorized),
|
|
857
|
-
effectiveRunCount: effectiveRuns.length,
|
|
858
|
-
effectiveCompleteRuns: count(effectiveRuns, (run) => run.state === "COMPLETE"),
|
|
859
|
-
effectiveCompletionRate: percentage(count(effectiveRuns, (run) => run.state === "COMPLETE"), effectiveRuns.length),
|
|
860
|
-
effectiveCheckPassRate: percentage(effectiveChecksPassed, effectiveChecksTotal),
|
|
861
|
-
effectiveOutcomePassRate: percentage(effectiveOutcomesPassed, effectiveOutcomesTotal),
|
|
862
|
-
effectiveEvidenceCoverageRate: percentage(effectiveEvidenceAttached, effectiveEvidenceTotal),
|
|
863
1267
|
checkPassRate: percentage(checksPassed, checksTotal),
|
|
864
1268
|
outcomePassRate: percentage(outcomesPassed, outcomesTotal),
|
|
865
1269
|
evidenceCoverageRate: percentage(evidenceAttached, evidenceTotal),
|
|
@@ -912,68 +1316,11 @@ var nonNegativeInteger = (value, label) => {
|
|
|
912
1316
|
if (result !== void 0 && !Number.isInteger(result)) return fail(`${label} must be an integer.`, "INVALID_CONFIG");
|
|
913
1317
|
return result;
|
|
914
1318
|
};
|
|
915
|
-
var rate = (value, label) => {
|
|
916
|
-
const result = nonNegativeNumber(value, label);
|
|
917
|
-
if (result !== void 0 && result > 1) return fail(`${label} must be between 0 and 1.`, "INVALID_CONFIG");
|
|
918
|
-
return result;
|
|
919
|
-
};
|
|
920
1319
|
var timestamp = (value, label) => {
|
|
921
1320
|
const result = nonEmptyString(value, label);
|
|
922
1321
|
if (!Number.isFinite(Date.parse(result))) return fail(`${label} must be a valid timestamp.`, "INVALID_CONFIG");
|
|
923
1322
|
return result;
|
|
924
1323
|
};
|
|
925
|
-
var relativePath = (value, label) => {
|
|
926
|
-
const result = nonEmptyString(value, label);
|
|
927
|
-
if (result.startsWith("/") || result.split("/").includes("..")) return fail(`${label} must be a repository-relative path.`, "INVALID_CONFIG");
|
|
928
|
-
return result;
|
|
929
|
-
};
|
|
930
|
-
var taskFile = (value, label) => {
|
|
931
|
-
if (value === void 0) return void 0;
|
|
932
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
|
|
933
|
-
const raw = value;
|
|
934
|
-
return { path: relativePath(raw["path"], `${label}.path`), sha256: sha2562(raw["sha256"], `${label}.sha256`) ?? fail(`${label}.sha256 is required.`, "INVALID_CONFIG") };
|
|
935
|
-
};
|
|
936
|
-
var taskSource = (value, label) => {
|
|
937
|
-
if (value === void 0) return void 0;
|
|
938
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
|
|
939
|
-
const raw = value;
|
|
940
|
-
return { repository: nonEmptyString(raw["repository"], `${label}.repository`), path: relativePath(raw["path"], `${label}.path`), revision: nonEmptyString(raw["revision"], `${label}.revision`) };
|
|
941
|
-
};
|
|
942
|
-
var suiteSource = (value, label) => {
|
|
943
|
-
if (value === void 0) return void 0;
|
|
944
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
|
|
945
|
-
const raw = value;
|
|
946
|
-
return { repository: nonEmptyString(raw["repository"], `${label}.repository`), revision: nonEmptyString(raw["revision"], `${label}.revision`), taskDefinition: relativePath(raw["taskDefinition"], `${label}.taskDefinition`) };
|
|
947
|
-
};
|
|
948
|
-
var taskScope = (value, label) => {
|
|
949
|
-
if (value === void 0) return void 0;
|
|
950
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
|
|
951
|
-
const raw = value;
|
|
952
|
-
return { read: stringList(raw["read"], `${label}.read`), write: stringList(raw["write"], `${label}.write`) };
|
|
953
|
-
};
|
|
954
|
-
var taskSurfaces = (value, label) => {
|
|
955
|
-
if (value === void 0) return void 0;
|
|
956
|
-
if (!Array.isArray(value) || !value.length) return fail(`${label} must be a non-empty array.`, "INVALID_CONFIG");
|
|
957
|
-
const surfaces = value.map((item, index2) => nonEmptyString(item, `${label}[${index2}]`));
|
|
958
|
-
if (surfaces.some((surface2) => !SURFACE_NAMES.includes(surface2))) fail(`${label} contains an unknown surface.`, "INVALID_CONFIG");
|
|
959
|
-
if (new Set(surfaces).size !== surfaces.length) fail(`${label} must contain unique surfaces.`, "INVALID_CONFIG");
|
|
960
|
-
return surfaces;
|
|
961
|
-
};
|
|
962
|
-
var benchmarkPolicy = (value) => {
|
|
963
|
-
if (value === void 0) return void 0;
|
|
964
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return fail("benchmark.policy must be an object.", "INVALID_CONFIG");
|
|
965
|
-
const raw = value;
|
|
966
|
-
const minComparableTasks = nonNegativeInteger(raw["minComparableTasks"], "benchmark.policy.minComparableTasks");
|
|
967
|
-
const maxDurationRegressionRate = nonNegativeNumber(raw["maxDurationRegressionRate"], "benchmark.policy.maxDurationRegressionRate");
|
|
968
|
-
const minCompletedRunsPerTask = nonNegativeInteger(raw["minCompletedRunsPerTask"], "benchmark.policy.minCompletedRunsPerTask");
|
|
969
|
-
const minBaselineSamplesPerTask = nonNegativeInteger(raw["minBaselineSamplesPerTask"] ?? 1, "benchmark.policy.minBaselineSamplesPerTask");
|
|
970
|
-
if (minComparableTasks === void 0 || minComparableTasks < 1) return fail("benchmark.policy.minComparableTasks must be at least 1.", "INVALID_CONFIG");
|
|
971
|
-
if (maxDurationRegressionRate === void 0 || maxDurationRegressionRate > 1) return fail("benchmark.policy.maxDurationRegressionRate must be between 0 and 1.", "INVALID_CONFIG");
|
|
972
|
-
if (minCompletedRunsPerTask === void 0 || minCompletedRunsPerTask < 1) return fail("benchmark.policy.minCompletedRunsPerTask must be at least 1.", "INVALID_CONFIG");
|
|
973
|
-
if (minBaselineSamplesPerTask === void 0 || minBaselineSamplesPerTask < 1) return fail("benchmark.policy.minBaselineSamplesPerTask must be at least 1.", "INVALID_CONFIG");
|
|
974
|
-
if (typeof raw["requireZeroEscapedIncomplete"] !== "boolean") return fail("benchmark.policy.requireZeroEscapedIncomplete must be boolean.", "INVALID_CONFIG");
|
|
975
|
-
return { minComparableTasks, maxDurationRegressionRate, minCompletedRunsPerTask, minBaselineSamplesPerTask, requireZeroEscapedIncomplete: raw["requireZeroEscapedIncomplete"] };
|
|
976
|
-
};
|
|
977
1324
|
var validateBenchmarkManifest = (value) => {
|
|
978
1325
|
if (typeof value !== "object" || value === null || Array.isArray(value)) fail("benchmark manifest must be an object.", "INVALID_CONFIG");
|
|
979
1326
|
const raw = value;
|
|
@@ -982,12 +1329,7 @@ var validateBenchmarkManifest = (value) => {
|
|
|
982
1329
|
const tasks = rawTasks.map((item, index2) => {
|
|
983
1330
|
if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`benchmark.tasks[${index2}] must be an object.`, "INVALID_CONFIG");
|
|
984
1331
|
const task = item;
|
|
985
|
-
|
|
986
|
-
const prompt = taskFile(task["prompt"], `benchmark.tasks[${index2}].prompt`);
|
|
987
|
-
const source = taskSource(task["source"], `benchmark.tasks[${index2}].source`);
|
|
988
|
-
const scope = taskScope(task["scope"], `benchmark.tasks[${index2}].scope`);
|
|
989
|
-
const surfaces = taskSurfaces(task["surfaces"], `benchmark.tasks[${index2}].surfaces`);
|
|
990
|
-
return { id: nonEmptyString(task["id"], `benchmark.tasks[${index2}].id`), title: nonEmptyString(task["title"], `benchmark.tasks[${index2}].title`), acceptanceCriteria: stringList(task["acceptanceCriteria"], `benchmark.tasks[${index2}].acceptanceCriteria`), ...surfaces === void 0 ? {} : { surfaces }, ...kind === void 0 ? {} : { kind }, ...prompt === void 0 ? {} : { prompt }, ...source === void 0 ? {} : { source }, ...scope === void 0 ? {} : { scope } };
|
|
1332
|
+
return { id: nonEmptyString(task["id"], `benchmark.tasks[${index2}].id`), title: nonEmptyString(task["title"], `benchmark.tasks[${index2}].title`), acceptanceCriteria: stringList(task["acceptanceCriteria"], `benchmark.tasks[${index2}].acceptanceCriteria`) };
|
|
991
1333
|
});
|
|
992
1334
|
if (new Set(tasks.map((task) => task.id)).size !== tasks.length) fail("benchmark task ids must be unique.", "INVALID_CONFIG");
|
|
993
1335
|
const taskIds = new Set(tasks.map((task) => task.id));
|
|
@@ -995,17 +1337,13 @@ var validateBenchmarkManifest = (value) => {
|
|
|
995
1337
|
const observations = rawObservations.map((item, index2) => {
|
|
996
1338
|
if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`benchmark.observations[${index2}] must be an object.`, "INVALID_CONFIG");
|
|
997
1339
|
const observation = item;
|
|
998
|
-
const
|
|
999
|
-
if (!["passed", "failed", "blocked", "not-run"].includes(String(
|
|
1340
|
+
const status2 = observation["status"];
|
|
1341
|
+
if (!["passed", "failed", "blocked", "not-run"].includes(String(status2))) fail(`benchmark.observations[${index2}].status is invalid.`, "INVALID_CONFIG");
|
|
1000
1342
|
const taskId = nonEmptyString(observation["taskId"], `benchmark.observations[${index2}].taskId`);
|
|
1001
1343
|
if (!taskIds.has(taskId)) fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
|
|
1002
1344
|
const task = tasks.find((candidate) => candidate.id === taskId) ?? fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
|
|
1003
1345
|
const attempts = nonNegativeInteger(observation["attempts"], `benchmark.observations[${index2}].attempts`);
|
|
1004
1346
|
const durationMs = nonNegativeNumber(observation["durationMs"], `benchmark.observations[${index2}].durationMs`);
|
|
1005
|
-
const durationSamplesMs = observation["durationSamplesMs"] === void 0 ? void 0 : Array.isArray(observation["durationSamplesMs"]) ? observation["durationSamplesMs"].map((sample, sampleIndex) => nonNegativeNumber(sample, `benchmark.observations[${index2}].durationSamplesMs[${sampleIndex}]`) ?? fail(`benchmark.observations[${index2}].durationSamplesMs must contain numbers.`, "INVALID_CONFIG")) : fail(`benchmark.observations[${index2}].durationSamplesMs must be an array.`, "INVALID_CONFIG");
|
|
1006
|
-
if (durationSamplesMs && !durationSamplesMs.length) fail(`benchmark.observations[${index2}].durationSamplesMs must not be empty.`, "INVALID_CONFIG");
|
|
1007
|
-
const artifactAcceptanceRate2 = rate(observation["artifactAcceptanceRate"], `benchmark.observations[${index2}].artifactAcceptanceRate`);
|
|
1008
|
-
const protocolCompletionRate = rate(observation["protocolCompletionRate"], `benchmark.observations[${index2}].protocolCompletionRate`);
|
|
1009
1347
|
const reviewMinutes2 = nonNegativeNumber(observation["reviewMinutes"], `benchmark.observations[${index2}].reviewMinutes`);
|
|
1010
1348
|
const escapedIncomplete = nonNegativeInteger(observation["escapedIncomplete"], `benchmark.observations[${index2}].escapedIncomplete`);
|
|
1011
1349
|
const evidenceDigest = sha2562(observation["evidenceDigest"], `benchmark.observations[${index2}].evidenceDigest`);
|
|
@@ -1020,12 +1358,10 @@ var validateBenchmarkManifest = (value) => {
|
|
|
1020
1358
|
return { criterion, status: evidenceStatus, source: nonEmptyString(entry["source"], `benchmark.observations[${index2}].evidence[${evidenceIndex}].source`) };
|
|
1021
1359
|
});
|
|
1022
1360
|
if (evidence && new Set(evidence.map((entry) => entry.criterion)).size !== evidence.length) fail(`benchmark.observations[${index2}].evidence criteria must be unique.`, "INVALID_CONFIG");
|
|
1023
|
-
return { taskId, mode: "baseline", status, source: nonEmptyString(observation["source"], `benchmark.observations[${index2}].source`), recordedAt: timestamp(observation["recordedAt"], `benchmark.observations[${index2}].recordedAt`), ...attempts === void 0 ? {} : { attempts }, ...durationMs === void 0 ? {} : { durationMs }, ...
|
|
1361
|
+
return { taskId, mode: "baseline", status: status2, source: nonEmptyString(observation["source"], `benchmark.observations[${index2}].source`), recordedAt: timestamp(observation["recordedAt"], `benchmark.observations[${index2}].recordedAt`), ...attempts === void 0 ? {} : { attempts }, ...durationMs === void 0 ? {} : { durationMs }, ...reviewMinutes2 === void 0 ? {} : { reviewMinutes: reviewMinutes2 }, ...escapedIncomplete === void 0 ? {} : { escapedIncomplete }, ...evidence === void 0 ? {} : { evidence }, ...evidenceDigest === void 0 ? {} : { evidenceDigest } };
|
|
1024
1362
|
});
|
|
1025
1363
|
if (new Set(observations.map((observation) => observation.taskId)).size !== observations.length) fail("benchmark allows at most one baseline observation per task.", "INVALID_CONFIG");
|
|
1026
|
-
|
|
1027
|
-
const policy = benchmarkPolicy(raw["policy"]);
|
|
1028
|
-
return { type: "agentskit-harness-benchmark-manifest", schemaVersion: BENCHMARK_SCHEMA_VERSION, suiteId: nonEmptyString(raw["suiteId"], "benchmark.suiteId"), name: nonEmptyString(raw["name"], "benchmark.name"), ...provenance === void 0 ? {} : { provenance }, tasks, observations, ...policy === void 0 ? {} : { policy } };
|
|
1364
|
+
return { type: "agentskit-harness-benchmark-manifest", schemaVersion: BENCHMARK_SCHEMA_VERSION, suiteId: nonEmptyString(raw["suiteId"], "benchmark.suiteId"), name: nonEmptyString(raw["name"], "benchmark.name"), tasks, observations };
|
|
1029
1365
|
};
|
|
1030
1366
|
var loadBenchmarkManifest = (path) => {
|
|
1031
1367
|
try {
|
|
@@ -1051,9 +1387,6 @@ var recordBenchmarkObservation = (path, input) => {
|
|
|
1051
1387
|
recordedAt: input.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1052
1388
|
...input.attempts === void 0 ? {} : { attempts: input.attempts },
|
|
1053
1389
|
...input.durationMs === void 0 ? {} : { durationMs: input.durationMs },
|
|
1054
|
-
...input.durationSamplesMs === void 0 ? {} : { durationSamplesMs: input.durationSamplesMs },
|
|
1055
|
-
...input.artifactAcceptanceRate === void 0 ? {} : { artifactAcceptanceRate: input.artifactAcceptanceRate },
|
|
1056
|
-
...input.protocolCompletionRate === void 0 ? {} : { protocolCompletionRate: input.protocolCompletionRate },
|
|
1057
1390
|
...input.reviewMinutes === void 0 ? {} : { reviewMinutes: input.reviewMinutes },
|
|
1058
1391
|
...input.escapedIncomplete === void 0 ? {} : { escapedIncomplete: input.escapedIncomplete },
|
|
1059
1392
|
...input.evidence === void 0 ? {} : { evidence: input.evidence },
|
|
@@ -1072,57 +1405,261 @@ var recordBenchmarkObservation = (path, input) => {
|
|
|
1072
1405
|
}
|
|
1073
1406
|
return observation;
|
|
1074
1407
|
};
|
|
1075
|
-
var comparisons = (runs, manifest
|
|
1408
|
+
var comparisons = (runs, manifest) => manifest.tasks.map((task) => {
|
|
1076
1409
|
const taskRuns = runs.filter((run) => run.benchmark?.suiteId === manifest.suiteId && run.benchmark.taskId === task.id);
|
|
1077
1410
|
const latest = taskRuns.at(-1);
|
|
1078
1411
|
const baseline = manifest.observations.find((observation) => observation.taskId === task.id);
|
|
1079
1412
|
const coveredCriteria = new Set((baseline?.evidence ?? []).map((entry) => entry.criterion));
|
|
1080
1413
|
const baselineEvidenceCoverageRate = baseline ? percentage(coveredCriteria.size, task.acceptanceCriteria.length) : null;
|
|
1081
1414
|
const baselineEvidenceComplete = baselineEvidenceCoverageRate === 1;
|
|
1082
|
-
const
|
|
1083
|
-
const
|
|
1084
|
-
const
|
|
1085
|
-
const
|
|
1086
|
-
const
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
const completedTaskRuns = taskRuns.filter((run) => run.state === "COMPLETE");
|
|
1090
|
-
const durationSamplesMs = completedTaskRuns.flatMap((run) => run.durationMs === void 0 ? [] : [run.durationMs]);
|
|
1091
|
-
const medianDurationMs = median(durationSamplesMs);
|
|
1092
|
-
const retryCount = count(taskRuns, (run) => run.supersedes !== void 0);
|
|
1093
|
-
const attempts = retryCount + (taskRuns.length ? 1 : 0);
|
|
1094
|
-
const durationRate = comparable ? improvementRate(baselineMedianDurationMs ?? void 0, medianDurationMs ?? void 0) : null;
|
|
1095
|
-
const attemptsRate = comparable ? improvementRate(baseline?.attempts, attempts) : null;
|
|
1096
|
-
const reviewRate = comparable ? improvementRate(baseline?.reviewMinutes, latest?.humanReviewMinutes) : null;
|
|
1097
|
-
const acceptanceSamples = taskRuns.flatMap((run) => run.artifactAcceptanceRate === void 0 ? [] : [run.artifactAcceptanceRate]);
|
|
1098
|
-
const harnessArtifactAcceptanceRate = acceptanceSamples.length ? Number((acceptanceSamples.reduce((total, rate2) => total + rate2, 0) / acceptanceSamples.length).toFixed(4)) : null;
|
|
1099
|
-
const artifactAcceptanceImprovementRate = increaseRate(baseline?.artifactAcceptanceRate, harnessArtifactAcceptanceRate ?? void 0);
|
|
1100
|
-
const artifactAcceptanceDelta = increaseDelta(baseline?.artifactAcceptanceRate, harnessArtifactAcceptanceRate);
|
|
1101
|
-
const completedRuns = completedTaskRuns.length;
|
|
1102
|
-
const harnessProtocolCompletionRate = taskRuns.length ? percentage(completedRuns, taskRuns.length) : null;
|
|
1103
|
-
const protocolCompletionImprovementRate = increaseRate(baseline?.protocolCompletionRate, harnessProtocolCompletionRate ?? void 0);
|
|
1104
|
-
const protocolCompletionDelta = increaseDelta(baseline?.protocolCompletionRate, harnessProtocolCompletionRate);
|
|
1105
|
-
const escapedIncompleteRate = improvementRate(baseline?.escapedIncomplete, latest?.escapedIncomplete);
|
|
1106
|
-
return { taskId: task.id, title: task.title, comparability, comparable, baselineDeliveryComplete, baselineEvidenceCoverageRate, baselineSampleCount: baselineDurationSamples.length, baselineArtifactAcceptanceRate: baseline?.artifactAcceptanceRate ?? null, baselineProtocolCompletionRate: baseline?.protocolCompletionRate ?? null, ...baselineMedianDurationMs === null ? {} : { baselineMedianDurationMs }, improvement: { durationRate, duration: improvementDirection(durationRate), attemptsRate, attempts: improvementDirection(attemptsRate), reviewRate, review: improvementDirection(reviewRate), artifactAcceptanceRate: artifactAcceptanceImprovementRate, artifactAcceptance: increaseDirection(artifactAcceptanceImprovementRate, artifactAcceptanceDelta), artifactAcceptanceDelta, protocolCompletionRate: protocolCompletionImprovementRate, protocolCompletion: increaseDirection(protocolCompletionImprovementRate, protocolCompletionDelta), protocolCompletionDelta, escapedIncompleteRate, escapedIncomplete: improvementDirection(escapedIncompleteRate) }, ...baseline ? { baseline } : {}, harness: { attempts, retryCount, completedRuns, durationSamplesMs, ...medianDurationMs === null ? {} : { medianDurationMs }, ...harnessArtifactAcceptanceRate === null ? {} : { artifactAcceptanceRate: harnessArtifactAcceptanceRate }, artifactAcceptanceSampleCount: acceptanceSamples.length, protocolCompletionRate: harnessProtocolCompletionRate, protocolCompletionSampleCount: taskRuns.length, latestState: latest?.state ?? "NOT_RUN", ...latest ? { latestRunId: latest.runId } : {}, ...latest?.durationMs === void 0 ? {} : { latestDurationMs: latest.durationMs }, checkPassRate: latest ? percentage(latest.checks.passed, latest.checks.total) : null, outcomePassRate: latest ? percentage(latest.outcomes.passed, latest.outcomes.total) : null, evidenceCoverageRate: latest ? percentage(latest.evidence.attached, latest.evidence.total) : null, ...latest?.escapedIncomplete === void 0 ? {} : { escapedIncomplete: latest.escapedIncomplete }, ...latest?.humanReviewMinutes === void 0 ? {} : { humanReviewMinutes: latest.humanReviewMinutes }, humanApproved: latest?.humanApproved ?? false }, confidence: confidence(comparable, completedRuns, policy), ...comparable && baselineMedianDurationMs !== null && medianDurationMs !== null ? { durationDeltaMs: medianDurationMs - baselineMedianDurationMs } : {}, ...comparable && baseline?.attempts !== void 0 ? { attemptDelta: attempts - baseline.attempts } : {}, ...comparable && baseline?.reviewMinutes !== void 0 && latest?.humanReviewMinutes !== void 0 ? { reviewDeltaMinutes: latest.humanReviewMinutes - baseline.reviewMinutes } : {}, ...baseline?.escapedIncomplete !== void 0 && latest?.escapedIncomplete !== void 0 ? { escapedIncompleteDelta: latest.escapedIncomplete - baseline.escapedIncomplete } : {} };
|
|
1415
|
+
const comparable2 = baseline !== void 0 && baseline.status !== "not-run" && baselineEvidenceComplete && latest?.state === "COMPLETE";
|
|
1416
|
+
const comparability = comparable2 ? "comparable" : baseline === void 0 ? "missing-baseline" : baseline.status === "not-run" ? "baseline-not-run" : !baselineEvidenceComplete ? "baseline-evidence-missing" : latest === void 0 ? "harness-not-run" : "harness-not-complete";
|
|
1417
|
+
const durationRate = comparable2 ? improvementRate(baseline?.durationMs, latest?.durationMs) : null;
|
|
1418
|
+
const attemptsRate = comparable2 ? improvementRate(baseline?.attempts, taskRuns.length) : null;
|
|
1419
|
+
const reviewRate = comparable2 ? improvementRate(baseline?.reviewMinutes, latest?.humanReviewMinutes) : null;
|
|
1420
|
+
const escapedIncompleteRate = comparable2 ? improvementRate(baseline?.escapedIncomplete, latest?.escapedIncomplete) : null;
|
|
1421
|
+
return { taskId: task.id, title: task.title, comparability, comparable: comparable2, baselineEvidenceCoverageRate, improvement: { durationRate, duration: improvementDirection(durationRate), attemptsRate, attempts: improvementDirection(attemptsRate), reviewRate, review: improvementDirection(reviewRate), escapedIncompleteRate, escapedIncomplete: improvementDirection(escapedIncompleteRate) }, ...baseline ? { baseline } : {}, harness: { attempts: taskRuns.length, latestState: latest?.state ?? "NOT_RUN", ...latest ? { latestRunId: latest.runId } : {}, ...latest?.durationMs === void 0 ? {} : { latestDurationMs: latest.durationMs }, checkPassRate: latest ? percentage(latest.checks.passed, latest.checks.total) : null, outcomePassRate: latest ? percentage(latest.outcomes.passed, latest.outcomes.total) : null, evidenceCoverageRate: latest ? percentage(latest.evidence.attached, latest.evidence.total) : null, ...latest?.escapedIncomplete === void 0 ? {} : { escapedIncomplete: latest.escapedIncomplete }, ...latest?.humanReviewMinutes === void 0 ? {} : { humanReviewMinutes: latest.humanReviewMinutes }, humanApproved: latest?.humanApproved ?? false }, ...comparable2 && baseline?.durationMs !== void 0 && latest?.durationMs !== void 0 ? { durationDeltaMs: latest.durationMs - baseline.durationMs } : {}, ...comparable2 && baseline?.attempts !== void 0 ? { attemptDelta: taskRuns.length - baseline.attempts } : {}, ...comparable2 && baseline?.reviewMinutes !== void 0 && latest?.humanReviewMinutes !== void 0 ? { reviewDeltaMinutes: latest.humanReviewMinutes - baseline.reviewMinutes } : {}, ...comparable2 && baseline?.escapedIncomplete !== void 0 && latest?.escapedIncomplete !== void 0 ? { escapedIncompleteDelta: latest.escapedIncomplete - baseline.escapedIncomplete } : {} };
|
|
1107
1422
|
});
|
|
1108
1423
|
var benchmarkRuns = (stateDir, manifest) => {
|
|
1109
1424
|
const runs = readRuns(stateDir).map(projectRun).sort((left, right) => left.runId.localeCompare(right.runId));
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1425
|
+
const reportComparisons = manifest ? comparisons(runs, manifest) : [];
|
|
1426
|
+
return { type: "agentskit-harness-benchmark", schemaVersion: BENCHMARK_SCHEMA_VERSION, stateDir, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), runs, summary: summarize(runs), comparisons: reportComparisons, ...manifest ? { manifest: { suiteId: manifest.suiteId, taskCount: manifest.tasks.length, baselineCount: manifest.observations.length, comparableTaskCount: reportComparisons.filter((comparison) => comparison.comparable).length } } : {} };
|
|
1427
|
+
};
|
|
1428
|
+
var required6 = (value, label) => {
|
|
1429
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1430
|
+
return value.trim();
|
|
1431
|
+
};
|
|
1432
|
+
var safeKey = (identity) => hashJson(identity);
|
|
1433
|
+
var now3 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
1434
|
+
var parse = (value, label) => {
|
|
1435
|
+
try {
|
|
1436
|
+
const raw = JSON.parse(value);
|
|
1437
|
+
const identity = {
|
|
1438
|
+
tracker: required6(raw["tracker"], `${label}.tracker`),
|
|
1439
|
+
repository: required6(raw["repository"], `${label}.repository`),
|
|
1440
|
+
issue: required6(raw["issue"], `${label}.issue`),
|
|
1441
|
+
worktree: required6(raw["worktree"], `${label}.worktree`),
|
|
1442
|
+
branch: required6(raw["branch"], `${label}.branch`)
|
|
1443
|
+
};
|
|
1444
|
+
return { ...identity, key: required6(raw["key"], `${label}.key`), leaseId: required6(raw["leaseId"], `${label}.leaseId`), owner: required6(raw["owner"], `${label}.owner`), claimedAt: required6(raw["claimedAt"], `${label}.claimedAt`) };
|
|
1445
|
+
} catch (error) {
|
|
1446
|
+
if (error instanceof SyntaxError) fail(`${label} contains invalid JSON.`, "HARNESS_ERROR");
|
|
1447
|
+
throw error;
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
1450
|
+
var createDispatchLedger = (stateDir) => {
|
|
1451
|
+
const root = required6(stateDir, "stateDir");
|
|
1452
|
+
const claimsDir = join(root, "coordination", "claims");
|
|
1453
|
+
const ledgerPath = join(root, "coordination", "dispatch-ledger.ndjson");
|
|
1454
|
+
mkdirSync(claimsDir, { recursive: true });
|
|
1455
|
+
const claimPath = (key) => join(claimsDir, `${key}.json`);
|
|
1456
|
+
const append = (record3) => appendFileSync(ledgerPath, `${JSON.stringify(record3)}
|
|
1457
|
+
`, "utf8");
|
|
1458
|
+
const records = () => {
|
|
1459
|
+
if (!existsSync(ledgerPath)) return [];
|
|
1460
|
+
return readFileSync(ledgerPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line, index2) => {
|
|
1461
|
+
try {
|
|
1462
|
+
return JSON.parse(line);
|
|
1463
|
+
} catch {
|
|
1464
|
+
return fail(`Dispatch ledger record ${index2 + 1} is invalid JSON.`, "HARNESS_ERROR");
|
|
1465
|
+
}
|
|
1466
|
+
});
|
|
1467
|
+
};
|
|
1468
|
+
const active = () => {
|
|
1469
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1470
|
+
for (const record3 of records()) {
|
|
1471
|
+
if (record3.action === "release" || record3.action === "recover") byKey.delete(record3.key);
|
|
1472
|
+
else if (record3.action === "dispatch") byKey.set(record3.key, record3);
|
|
1473
|
+
}
|
|
1474
|
+
return [...byKey.values()];
|
|
1475
|
+
};
|
|
1476
|
+
return {
|
|
1477
|
+
claim: (input) => {
|
|
1478
|
+
const identity = {
|
|
1479
|
+
tracker: required6(input.tracker, "tracker"),
|
|
1480
|
+
repository: required6(input.repository, "repository"),
|
|
1481
|
+
issue: required6(input.issue, "issue"),
|
|
1482
|
+
worktree: required6(input.worktree, "worktree"),
|
|
1483
|
+
branch: required6(input.branch, "branch")
|
|
1484
|
+
};
|
|
1485
|
+
const owner = required6(input.owner, "owner");
|
|
1486
|
+
const key = safeKey(identity);
|
|
1487
|
+
const path = claimPath(key);
|
|
1488
|
+
if (existsSync(path)) return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
|
|
1489
|
+
const lease = { ...identity, key, leaseId: randomUUID(), owner, claimedAt: now3() };
|
|
1490
|
+
let fd;
|
|
1491
|
+
try {
|
|
1492
|
+
fd = openSync(path, "wx");
|
|
1493
|
+
} catch (error) {
|
|
1494
|
+
if (error.code === "EEXIST") return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
|
|
1495
|
+
throw error;
|
|
1496
|
+
}
|
|
1497
|
+
try {
|
|
1498
|
+
writeFileSync(fd, JSON.stringify(lease), "utf8");
|
|
1499
|
+
} finally {
|
|
1500
|
+
closeSync(fd);
|
|
1501
|
+
}
|
|
1502
|
+
append({ ...lease, action: "dispatch", at: lease.claimedAt });
|
|
1503
|
+
return { decision: "claimed", lease };
|
|
1504
|
+
},
|
|
1505
|
+
recordDispatch: ({ lease, idempotencyKey, commandDigest }) => {
|
|
1506
|
+
const id2 = required6(idempotencyKey, "idempotencyKey");
|
|
1507
|
+
const digest3 = required6(commandDigest, "commandDigest");
|
|
1508
|
+
const existing = records().find((record4) => record4.action === "dispatch" && record4.idempotencyKey === id2);
|
|
1509
|
+
if (existing) return { decision: "duplicate", record: existing };
|
|
1510
|
+
const record3 = { ...lease, action: "dispatch", at: now3(), idempotencyKey: id2, commandDigest: digest3 };
|
|
1511
|
+
append(record3);
|
|
1512
|
+
return { decision: "recorded", record: record3 };
|
|
1513
|
+
},
|
|
1514
|
+
release: (lease, reason = "lease released") => {
|
|
1515
|
+
const path = claimPath(required6(lease.key, "lease.key"));
|
|
1516
|
+
if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
|
|
1517
|
+
const current = parse(readFileSync(path, "utf8"), "claim");
|
|
1518
|
+
if (current.leaseId !== lease.leaseId) fail("Dispatch lease owner does not match.", "INVALID_STATE");
|
|
1519
|
+
unlinkSync(path);
|
|
1520
|
+
const record3 = { ...current, action: "release", at: now3(), reason: required6(reason, "reason") };
|
|
1521
|
+
append(record3);
|
|
1522
|
+
return record3;
|
|
1523
|
+
},
|
|
1524
|
+
recover: (key, input) => {
|
|
1525
|
+
if (input.actor !== "human") fail("Dispatch lease recovery requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
|
|
1526
|
+
const normalizedKey = required6(key, "key");
|
|
1527
|
+
const maxAgeMs = input.maxAgeMs ?? 3e5;
|
|
1528
|
+
if (!Number.isInteger(maxAgeMs) || maxAgeMs < 0) fail("maxAgeMs must be a non-negative integer.", "INVALID_INPUT");
|
|
1529
|
+
const path = claimPath(normalizedKey);
|
|
1530
|
+
if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
|
|
1531
|
+
const current = parse(readFileSync(path, "utf8"), "claim");
|
|
1532
|
+
if (Date.now() - Date.parse(current.claimedAt) < maxAgeMs) fail("Dispatch lease is not old enough to recover.", "HARNESS_ERROR");
|
|
1533
|
+
unlinkSync(path);
|
|
1534
|
+
const record3 = { ...current, action: "recover", at: now3(), reason: required6(input.reason, "reason") };
|
|
1535
|
+
append(record3);
|
|
1536
|
+
return record3;
|
|
1537
|
+
},
|
|
1538
|
+
active,
|
|
1539
|
+
records
|
|
1540
|
+
};
|
|
1541
|
+
};
|
|
1542
|
+
var DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx", ".txt", ".adoc", ".rst"]);
|
|
1543
|
+
var TEST_SUFFIXES = [".test.", ".spec.", "__tests__"];
|
|
1544
|
+
var normalizedPath = (value, label) => {
|
|
1545
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} must be a non-empty path.`, "INVALID_INPUT");
|
|
1546
|
+
const path = value.trim().replaceAll("\\", "/");
|
|
1547
|
+
if (path.startsWith("/") || path.split("/").includes("..")) fail(`${label} must be repository-relative.`, "INVALID_INPUT");
|
|
1548
|
+
return path;
|
|
1549
|
+
};
|
|
1550
|
+
var isTest = (path) => TEST_SUFFIXES.some((suffix) => path.includes(suffix)) || /(^|\/)(test|tests|__tests__)\//.test(path);
|
|
1551
|
+
var isDoc = (path) => DOC_EXTENSIONS.has(extname(path).toLowerCase());
|
|
1552
|
+
var planFilePreflight = (files, options2 = {}) => {
|
|
1553
|
+
if (!Array.isArray(files)) fail("files must be an array.", "INVALID_INPUT");
|
|
1554
|
+
const unique2 = [...new Set(files.map((file, index2) => normalizedPath(file.path, `files[${index2}].path`)))].sort();
|
|
1555
|
+
const codeFiles = unique2.filter((path) => !isDoc(path) && !isTest(path));
|
|
1556
|
+
const existingTests = unique2.filter(isTest);
|
|
1557
|
+
const roots = (options2.testRoots ?? ["test", "tests", "__tests__"]).map((root, index2) => normalizedPath(root, `testRoots[${index2}]`));
|
|
1558
|
+
const colocated = options2.includeTests === false ? [] : codeFiles.flatMap((path) => {
|
|
1559
|
+
const file = basename(path);
|
|
1560
|
+
const directory = dirname(path);
|
|
1561
|
+
const stem = file.includes(".") ? file.slice(0, file.lastIndexOf(".")) : file;
|
|
1562
|
+
return [join(directory, `${stem}.test.ts`), join(directory, `${stem}.spec.ts`)].filter((candidate) => unique2.includes(candidate));
|
|
1563
|
+
});
|
|
1564
|
+
const testFiles = [...new Set([...existingTests, ...colocated, ...unique2.filter((path) => roots.some((root) => path === root || path.startsWith(`${root}/`)))].sort())];
|
|
1565
|
+
const docsOnly = unique2.length > 0 && codeFiles.length === 0 && existingTests.length === 0;
|
|
1566
|
+
return { files: unique2, codeFiles, testFiles, docsOnly, checks: docsOnly ? [] : ["lint", "typecheck", ...testFiles.length ? ["test"] : []] };
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1569
|
+
// src/block.ts
|
|
1570
|
+
var BLOCK_STATUSES = ["todo", "picked", "development", "validation", "pr-open", "merged", "post-merge", "done", "blocked", "scope-cut"];
|
|
1571
|
+
var text2 = (value, label) => {
|
|
1572
|
+
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1573
|
+
};
|
|
1574
|
+
var list = (value, label) => {
|
|
1575
|
+
if (!Array.isArray(value)) return fail(`${label} must be an array of non-empty strings.`, "INVALID_INPUT");
|
|
1576
|
+
if (!value.every((item) => typeof item === "string" && Boolean(item.trim()))) return fail(`${label} must be an array of non-empty strings.`, "INVALID_INPUT");
|
|
1577
|
+
return [...new Set(value.map((item) => item.trim()))];
|
|
1578
|
+
};
|
|
1579
|
+
var validateBlockManifest = (value) => {
|
|
1580
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) fail("block manifest must be an object.", "INVALID_INPUT");
|
|
1581
|
+
const raw = value;
|
|
1582
|
+
if (raw["schemaVersion"] !== 1) fail("block manifest schemaVersion must be 1.", "INVALID_INPUT");
|
|
1583
|
+
const criteria = list(raw["acceptanceCriteria"], "acceptanceCriteria");
|
|
1584
|
+
if (!criteria.length) fail("acceptanceCriteria must not be empty.", "INVALID_INPUT");
|
|
1585
|
+
const dependencies = list(raw["dependencies"] ?? [], "dependencies");
|
|
1586
|
+
const wave = raw["wave"];
|
|
1587
|
+
if (!Number.isInteger(wave) || wave < 1) fail("wave must be a positive integer.", "INVALID_INPUT");
|
|
1588
|
+
const status2 = raw["status"];
|
|
1589
|
+
if (!BLOCK_STATUSES.includes(status2)) fail("status is invalid.", "INVALID_INPUT");
|
|
1590
|
+
const budgetRaw = raw["budget"];
|
|
1591
|
+
let budget;
|
|
1592
|
+
if (budgetRaw !== void 0) {
|
|
1593
|
+
if (typeof budgetRaw !== "object" || budgetRaw === null || Array.isArray(budgetRaw)) fail("budget must be an object.", "INVALID_INPUT");
|
|
1594
|
+
const candidate = budgetRaw;
|
|
1595
|
+
for (const key of ["maxMinutes", "maxAttempts"]) if (candidate[key] !== void 0 && (!Number.isInteger(candidate[key]) || candidate[key] < 1)) fail(`budget.${key} must be a positive integer.`, "INVALID_INPUT");
|
|
1596
|
+
budget = { ...candidate["maxMinutes"] === void 0 ? {} : { maxMinutes: candidate["maxMinutes"] }, ...candidate["maxAttempts"] === void 0 ? {} : { maxAttempts: candidate["maxAttempts"] } };
|
|
1597
|
+
}
|
|
1598
|
+
return { schemaVersion: 1, id: text2(raw["id"], "id"), title: text2(raw["title"], "title"), tracker: text2(raw["tracker"], "tracker"), repository: text2(raw["repository"], "repository"), acceptanceCriteria: criteria, dependencies, wave, status: status2, ...budget ? { budget } : {}, ...raw["humanGates"] === void 0 ? {} : { humanGates: list(raw["humanGates"], "humanGates") }, ...raw["sourceHash"] === void 0 ? {} : { sourceHash: text2(raw["sourceHash"], "sourceHash") } };
|
|
1599
|
+
};
|
|
1600
|
+
var assessBlock = (manifest, completedDependencies = []) => {
|
|
1601
|
+
const value = validateBlockManifest(manifest);
|
|
1602
|
+
const completed = new Set(completedDependencies.map((item) => text2(item, "completedDependencies[]")));
|
|
1603
|
+
const blockers = value.dependencies.filter((dependency) => !completed.has(dependency));
|
|
1604
|
+
const next = blockers.length ? [`Complete dependencies: ${blockers.join(", ")}`] : value.status === "blocked" ? ["Resolve the recorded blocker before dispatch."] : ["Dispatch the block with the frozen acceptance criteria."];
|
|
1605
|
+
return { status: blockers.length || value.status === "blocked" ? "blocked" : "ready", manifestHash: hashJson(value), blockers, next };
|
|
1606
|
+
};
|
|
1607
|
+
var text3 = (value, label) => {
|
|
1608
|
+
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1609
|
+
};
|
|
1610
|
+
var category = (heading) => {
|
|
1611
|
+
const value = heading.toLowerCase();
|
|
1612
|
+
if (/went well|success|worked/.test(value)) return "worked";
|
|
1613
|
+
if (/problem|failed|blocker|pain/.test(value)) return "problem";
|
|
1614
|
+
if (/adjust|action|next|improv/.test(value)) return "adjustment";
|
|
1615
|
+
return "other";
|
|
1616
|
+
};
|
|
1617
|
+
var parseRetro = (markdown, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) => {
|
|
1618
|
+
const input = text3(markdown, "markdown");
|
|
1619
|
+
const origin = text3(source, "source");
|
|
1620
|
+
if (!Number.isFinite(Date.parse(recordedAt))) fail("recordedAt must be a valid timestamp.", "INVALID_INPUT");
|
|
1621
|
+
const records = [];
|
|
1622
|
+
let current = "other";
|
|
1623
|
+
for (const line of input.split(/\r?\n/)) {
|
|
1624
|
+
const heading = line.match(/^#{1,6}\s+(.+)$/);
|
|
1625
|
+
if (heading) {
|
|
1626
|
+
current = category(heading[1] ?? "");
|
|
1627
|
+
continue;
|
|
1628
|
+
}
|
|
1629
|
+
const item = line.match(/^\s*[-*]\s+(?:\[[ xX]\]\s+)?(.+?)\s*$/);
|
|
1630
|
+
if (!item?.[1]?.trim()) continue;
|
|
1631
|
+
const value = item[1].trim();
|
|
1632
|
+
const id2 = `L-${createHash("sha256").update(`${origin}|${current}|${value}`).digest("hex").slice(0, 12)}`;
|
|
1633
|
+
if (!records.some((record3) => record3.id === id2)) records.push({ id: id2, source: origin, category: current, text: value, status: "proposed", recordedAt });
|
|
1634
|
+
}
|
|
1635
|
+
return records;
|
|
1636
|
+
};
|
|
1637
|
+
|
|
1638
|
+
// src/status.ts
|
|
1639
|
+
var required7 = (value, label) => {
|
|
1640
|
+
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1641
|
+
};
|
|
1642
|
+
var createStatusSnapshot = (input) => {
|
|
1643
|
+
const sourceRevision = required7(input.sourceRevision, "sourceRevision");
|
|
1644
|
+
if (!Number.isFinite(Date.parse(input.generatedAt))) fail("generatedAt must be a valid timestamp.", "INVALID_INPUT");
|
|
1645
|
+
if (!Array.isArray(input.blocks)) fail("blocks must be an array.", "INVALID_INPUT");
|
|
1646
|
+
const blocks = input.blocks.map((block2, index2) => {
|
|
1647
|
+
if (typeof block2 !== "object" || block2 === null || Array.isArray(block2)) fail(`blocks[${index2}] must be an object.`, "INVALID_INPUT");
|
|
1648
|
+
const value = block2;
|
|
1649
|
+
if (!(typeof value.id === "string" && value.id.trim())) fail(`blocks[${index2}].id is required.`, "INVALID_INPUT");
|
|
1650
|
+
if (!["todo", "picked", "development", "validation", "pr-open", "merged", "post-merge", "done", "blocked", "scope-cut"].includes(value.status)) fail(`blocks[${index2}].status is invalid.`, "INVALID_INPUT");
|
|
1651
|
+
return { ...value, id: value.id.trim() };
|
|
1652
|
+
}).sort((left, right) => left.id.localeCompare(right.id));
|
|
1653
|
+
if (input.metrics !== void 0 && Object.entries(input.metrics).some(([key, value]) => !key.trim() || typeof value !== "number" || !Number.isFinite(value) || value < 0)) fail("metrics must contain finite non-negative numbers.", "INVALID_INPUT");
|
|
1654
|
+
const body2 = { schemaVersion: 1, generatedAt: input.generatedAt, sourceRevision, blocks, ...input.machine ? { machine: input.machine } : {}, ...input.metrics ? { metrics: input.metrics } : {}, ...input.next ? { next: required7(input.next, "next") } : {} };
|
|
1655
|
+
return { ...body2, digest: hashJson(body2) };
|
|
1656
|
+
};
|
|
1657
|
+
var validateStatusSnapshot = (value) => {
|
|
1658
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) fail("status snapshot must be an object.", "INVALID_INPUT");
|
|
1659
|
+
const raw = value;
|
|
1660
|
+
const snapshot = createStatusSnapshot({ generatedAt: required7(raw.generatedAt, "generatedAt"), sourceRevision: required7(raw.sourceRevision, "sourceRevision"), blocks: raw.blocks, ...raw.machine ? { machine: raw.machine } : {}, ...raw.metrics ? { metrics: raw.metrics } : {}, ...raw.next ? { next: raw.next } : {} });
|
|
1661
|
+
if (raw.schemaVersion !== 1 || raw.digest !== snapshot.digest) fail("status snapshot digest or schemaVersion is invalid.", "HARNESS_ERROR");
|
|
1662
|
+
return snapshot;
|
|
1126
1663
|
};
|
|
1127
1664
|
var EVIDENCE_BUNDLE_SCHEMA_VERSION = 1;
|
|
1128
1665
|
var body = (bundle) => {
|
|
@@ -1150,7 +1687,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
|
|
|
1150
1687
|
const loaded = loadConfig(configPath);
|
|
1151
1688
|
const run = requireRun2(runId ? readJson(join(loaded.stateDir, "runs", runId, "run.json")) : loadLatestRun(loaded.stateDir));
|
|
1152
1689
|
const reconciliation = await reconcileRun({ configPath, runId: run.runId });
|
|
1153
|
-
const
|
|
1690
|
+
const digest3 = run.verificationDigest ?? fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
|
|
1154
1691
|
if (reconciliation.state !== "COMPLETE") fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
|
|
1155
1692
|
const eventLog = new FileEventStore(loaded.stateDir);
|
|
1156
1693
|
eventLog.read(run.runId);
|
|
@@ -1165,7 +1702,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
|
|
|
1165
1702
|
return bundleFile(loaded.stateDir, path);
|
|
1166
1703
|
});
|
|
1167
1704
|
const privateKey = createPrivateKey(readFileSync(privateKeyPath));
|
|
1168
|
-
const unsigned = { type: "agentskit-harness-evidence-bundle", schemaVersion: EVIDENCE_BUNDLE_SCHEMA_VERSION, runId: run.runId, signerKeyId: keyId, sourceRevision: run.sourceRevision, configHash: run.configHash, contractHash: run.contractHash, verificationDigest:
|
|
1705
|
+
const unsigned = { type: "agentskit-harness-evidence-bundle", schemaVersion: EVIDENCE_BUNDLE_SCHEMA_VERSION, runId: run.runId, signerKeyId: keyId, sourceRevision: run.sourceRevision, configHash: run.configHash, contractHash: run.contractHash, verificationDigest: digest3, eventLog: eventVerification, files };
|
|
1169
1706
|
const payloadHash = sha256(JSON.stringify(unsigned));
|
|
1170
1707
|
const publicKeyPem = createPublicKey(privateKey).export({ type: "spki", format: "pem" }).toString();
|
|
1171
1708
|
const bundle = { ...unsigned, payloadHash, signature: { algorithm: "ed25519", keyId, publicKeyPem, signatureBase64: sign(null, Buffer.from(payloadHash), privateKey).toString("base64") } };
|
|
@@ -1233,14 +1770,52 @@ var decisionArgs = (first, second) => {
|
|
|
1233
1770
|
const decisions = /* @__PURE__ */ new Set(["approved", "approve", "yes", "ok", "rejected", "reject", "no"]);
|
|
1234
1771
|
return decisions.has(first) ? { decision: first, ...second ? { runId: second } : {} } : { decision: second ?? "", runId: first };
|
|
1235
1772
|
};
|
|
1773
|
+
var readJsonInput = (path, label) => {
|
|
1774
|
+
try {
|
|
1775
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
1776
|
+
} catch (error) {
|
|
1777
|
+
return fail(`Invalid ${label} JSON: ${error instanceof Error ? error.message : String(error)}`, "INVALID_INPUT");
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1236
1780
|
program.command("doctor").description("Validate the contract without starting a run.").action(() => print({ status: "passed", criteria: ["package"], config: loadConfig(options().config).config }));
|
|
1237
|
-
program.command("plan <decision>").description("
|
|
1781
|
+
program.command("plan <decision>").description("Approve the frozen task contract and create a planned run.").option("--by <actor>", "approval actor", "human").option("--allow-dirty", "allow a human-authorized dirty worktree").option("--context-file <path>", "attach a context snapshot JSON file").action(async (decision, command) => print(await planRun({ configPath: options().config, decision, actor: command.by, allowDirty: command.allowDirty ?? false, contextSnapshots: command.contextFile ? readContextSnapshots(command.contextFile) : [] })));
|
|
1238
1782
|
var context = program.command("context").description("Resolve portable, provenance-bearing context snapshots.");
|
|
1239
1783
|
context.command("resolve <query>").description("Resolve a Doc Bridge snapshot from the local index.").option("--provider <provider>", "context provider", "doc-bridge").option("--scope <scope...>", "optional search scopes").option("--index <path>", "Doc Bridge index path", ".doc-bridge/index.json").action(async (query, command) => {
|
|
1240
1784
|
if (command.provider !== "doc-bridge") fail(`Unsupported context provider: ${command.provider}`, "INVALID_INPUT");
|
|
1241
1785
|
const loaded = loadConfig(options().config);
|
|
1242
1786
|
print(await createDocBridgeContextProvider({ root: loaded.root, indexPath: command.index }).resolve({ query, ...command.scope?.length ? { scope: command.scope } : {} }));
|
|
1243
1787
|
});
|
|
1788
|
+
var discovery = program.command("discovery").description("Assess a structured discovery result before implementation.");
|
|
1789
|
+
discovery.command("assess <input>").description("Emit Ready or a human decision packet from a discovery JSON file.").action((input) => print(assessDiscovery(readJsonInput(input, "discovery input"))));
|
|
1790
|
+
var wip = program.command("wip").description("Assess deterministic WIP admission before starting work.");
|
|
1791
|
+
wip.command("assess <input>").description("Emit an admission decision from WIP ledger JSON.").action((input) => print(assessWip(readJsonInput(input, "WIP input"))));
|
|
1792
|
+
var experiment = program.command("experiment").description("Select a runtime only from a controlled, comparable experiment.");
|
|
1793
|
+
experiment.command("select <input>").description("Select the eligible runtime from experiment JSON.").action((input) => print(selectRuntime(readJsonInput(input, "experiment input"))));
|
|
1794
|
+
var delivery = program.command("delivery").description("Assess deterministic G2\u2013G5 gates and prepare idempotent PR handoff.");
|
|
1795
|
+
delivery.command("preflight <input>").action((input) => print(assessPreflight(readJsonInput(input, "preflight input"))));
|
|
1796
|
+
delivery.command("pr <input>").action((input) => print(composePullRequest(readJsonInput(input, "PR input"))));
|
|
1797
|
+
delivery.command("integration <input>").action((input) => print(assessIntegration(readJsonInput(input, "integration input"))));
|
|
1798
|
+
delivery.command("production <input>").action((input) => print(assessProduction(readJsonInput(input, "production input"))));
|
|
1799
|
+
delivery.command("acceptance <input>").action((input) => print(assessAcceptance(readJsonInput(input, "acceptance input"))));
|
|
1800
|
+
delivery.command("cleanup <input>").action((input) => print(assessWorktreeCleanup(readJsonInput(input, "cleanup input"))));
|
|
1801
|
+
program.command("pilot <input>").description("Freeze and assess a ten-issue pilot cohort.").action((input) => print(assessPilot(readJsonInput(input, "pilot input"))));
|
|
1802
|
+
var cycle = program.command("cycle").description("Run the five-step improvement cycle with explicit adjustment and bounded repetition.");
|
|
1803
|
+
cycle.command("assess <input>").description("Assess run \u2192 verify \u2192 adjust \u2192 repeat from a cycle JSON file.").action((input) => print(assessImprovementCycle(readJsonInput(input, "cycle input"))));
|
|
1804
|
+
var block = program.command("block").description("Validate and assess a portable execution block manifest.");
|
|
1805
|
+
block.command("validate <input>").action((input) => print(validateBlockManifest(readJsonInput(input, "block manifest"))));
|
|
1806
|
+
block.command("assess <input>").option("--completed <ids...>", "completed dependency IDs").action((input, command) => print(assessBlock(readJsonInput(input, "block manifest"), command.completed ?? [])));
|
|
1807
|
+
var preflight = program.command("preflight").description("Plan safe, file-scoped validation before commit.");
|
|
1808
|
+
preflight.command("files <input>").action((input) => print(planFilePreflight(readJsonInput(input, "changed files"))));
|
|
1809
|
+
var status = program.command("snapshot <input>").description("Create or validate a deterministic status snapshot.");
|
|
1810
|
+
status.action((input) => print(createStatusSnapshot(readJsonInput(input, "status input"))));
|
|
1811
|
+
status.command("validate <input>").action((input) => print(validateStatusSnapshot(readJsonInput(input, "status snapshot"))));
|
|
1812
|
+
var learning = program.command("learning").description("Parse retrospectives into proposed learnings.");
|
|
1813
|
+
learning.command("parse <input>").requiredOption("--source <source>").action((input, command) => print(parseRetro(readFileSync(input, "utf8"), command.source)));
|
|
1814
|
+
var coordination = program.command("coordination").description("Manage idempotent issue/worktree claims and dispatch records.");
|
|
1815
|
+
coordination.command("claim <input>").action((input) => {
|
|
1816
|
+
const loaded = loadConfig(options().config);
|
|
1817
|
+
print(createDispatchLedger(loaded.stateDir).claim(readJsonInput(input, "coordination identity")));
|
|
1818
|
+
});
|
|
1244
1819
|
program.command("start").description("Move a planned run into implementation.").action(() => print(startRun(loadConfig(options().config))));
|
|
1245
1820
|
program.command("verify").description("Execute every configured check and record evidence.").action(async () => print(await verifyRun({ configPath: options().config })));
|
|
1246
1821
|
program.command("run").description("Alias for verify, compatible with the common protocol.").action(async () => print(await verifyRun({ configPath: options().config })));
|
|
@@ -1287,9 +1862,9 @@ var benchmark = program.command("benchmark").description("Aggregate reproducible
|
|
|
1287
1862
|
benchmark.command("baseline <taskId>").description("Record one controlled baseline observation in a benchmark manifest.").option("--manifest <path>", "benchmark manifest path").requiredOption("--status <status>", "passed, failed, blocked, or not-run").requiredOption("--source <source>", "baseline source or run reference").option("--evidence-file <path>", "JSON file with criterion-level baseline evidence").option("--recorded-at <timestamp>", "ISO-8601 timestamp").option("--attempts <count>", "attempt count", (value) => Number(value)).option("--duration-ms <milliseconds>", "duration in milliseconds", (value) => Number(value)).option("--review-minutes <minutes>", "human review time in minutes", (value) => Number(value)).option("--escaped-incomplete <count>", "incomplete deliveries discovered after handoff", (value) => Number(value)).action((taskId, command, cliCommand) => {
|
|
1288
1863
|
const manifest = command.manifest ?? cliCommand.parent?.opts().manifest;
|
|
1289
1864
|
const manifestPath = manifest ?? fail("baseline requires --manifest <path>.", "INVALID_INPUT");
|
|
1290
|
-
const
|
|
1865
|
+
const status2 = ["passed", "failed", "blocked", "not-run"].includes(command.status) ? command.status : fail("status must be passed, failed, blocked, or not-run.", "INVALID_INPUT");
|
|
1291
1866
|
const evidence = command.evidenceFile ? readBenchmarkEvidence(command.evidenceFile) : void 0;
|
|
1292
|
-
print(recordBenchmarkObservation(manifestPath, { taskId, status, source: command.source, ...evidence ? { evidence: evidence.evidence, evidenceDigest: evidence.digest } : {}, ...command.recordedAt ? { recordedAt: command.recordedAt } : {}, ...command.attempts === void 0 ? {} : { attempts: command.attempts }, ...command.durationMs === void 0 ? {} : { durationMs: command.durationMs }, ...command.reviewMinutes === void 0 ? {} : { reviewMinutes: command.reviewMinutes }, ...command.escapedIncomplete === void 0 ? {} : { escapedIncomplete: command.escapedIncomplete } }));
|
|
1867
|
+
print(recordBenchmarkObservation(manifestPath, { taskId, status: status2, source: command.source, ...evidence ? { evidence: evidence.evidence, evidenceDigest: evidence.digest } : {}, ...command.recordedAt ? { recordedAt: command.recordedAt } : {}, ...command.attempts === void 0 ? {} : { attempts: command.attempts }, ...command.durationMs === void 0 ? {} : { durationMs: command.durationMs }, ...command.reviewMinutes === void 0 ? {} : { reviewMinutes: command.reviewMinutes }, ...command.escapedIncomplete === void 0 ? {} : { escapedIncomplete: command.escapedIncomplete } }));
|
|
1293
1868
|
});
|
|
1294
1869
|
program.command("clean").description("Remove only configured task-owned temporary artifacts.").action(() => print(cleanTaskArtifacts(options().config)));
|
|
1295
1870
|
process.on("SIGINT", () => {
|