@agentskit/harness 0.1.0 → 0.4.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 +33 -32
- package/CONTRIBUTING.md +60 -12
- package/MANIFESTO.md +23 -0
- package/README.md +276 -144
- package/capabilities/public-surface.json +668 -0
- package/compatibility/manifest.json +17 -0
- package/compatibility/migration.md +10 -0
- package/compatibility/report.json +23 -0
- package/compatibility/report.md +22 -0
- package/compatibility/rollback.md +8 -0
- package/dist/cli.js +958 -239
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1338 -122
- package/dist/index.js +2273 -353
- package/dist/index.js.map +1 -1
- package/docs/ADR-0025-portable-orchestration-controls.md +27 -0
- package/docs/ADR-0026-kernel-adapters-boundary.md +82 -0
- package/docs/GETTING-STARTED.md +18 -0
- package/docs/MODULE-BOUNDARIES.md +143 -0
- package/docs/ORGANIZATION.md +46 -0
- package/docs/TROUBLESHOOTING.md +24 -0
- package/examples/minimum-profile.mjs +27 -0
- package/package.json +52 -34
- package/release/manifest.json +14 -0
- package/release/notes.md +10 -0
- package/release/qualification.json +14 -0
- 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,13 +1,13 @@
|
|
|
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, readdirSync, writeSync, appendFileSync, mkdtempSync, renameSync, rmSync } 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
|
-
// src/constants.ts
|
|
10
|
+
// src/kernel/constants.ts
|
|
11
11
|
var STATES = [
|
|
12
12
|
"CLARIFYING",
|
|
13
13
|
"PLANNED",
|
|
@@ -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"],
|
|
@@ -37,7 +37,7 @@ var LEGAL_TRANSITIONS = {
|
|
|
37
37
|
var REAL_CATEGORIES = /* @__PURE__ */ new Set(["endpoint", "database", "cli", "mcp", "ui"]);
|
|
38
38
|
var DECISIONS = /* @__PURE__ */ new Set(["approved", "approve", "yes", "ok", "rejected", "reject", "no"]);
|
|
39
39
|
|
|
40
|
-
// src/errors.ts
|
|
40
|
+
// src/kernel/errors.ts
|
|
41
41
|
var HarnessError = class extends Error {
|
|
42
42
|
code;
|
|
43
43
|
constructor(message, code = "HARNESS_ERROR") {
|
|
@@ -50,7 +50,7 @@ var fail = (message, code = "HARNESS_ERROR") => {
|
|
|
50
50
|
throw new HarnessError(message, code);
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
// src/profiles.ts
|
|
53
|
+
// src/profiles/index.ts
|
|
54
54
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
55
55
|
var record = (value, label) => {
|
|
56
56
|
if (!isRecord(value)) fail(`${label} must be an object.`, "INVALID_CONFIG");
|
|
@@ -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"]] : [];
|
|
@@ -138,7 +140,7 @@ var cleanConfiguredArtifacts = (loaded) => {
|
|
|
138
140
|
};
|
|
139
141
|
var fileContents = (path) => readFileSync(path, "utf8");
|
|
140
142
|
|
|
141
|
-
// src/types.ts
|
|
143
|
+
// src/kernel/types.ts
|
|
142
144
|
var SURFACE_NAMES = ["logic", "endpoint", "database", "cli", "mcp", "ui", "docs"];
|
|
143
145
|
var CHECK_CATEGORIES = ["build", "test", "lint", ...SURFACE_NAMES, "custom"];
|
|
144
146
|
var RUN_STATES = [
|
|
@@ -155,7 +157,7 @@ var RUN_STATES = [
|
|
|
155
157
|
"SUPERSEDED"
|
|
156
158
|
];
|
|
157
159
|
|
|
158
|
-
// src/config.ts
|
|
160
|
+
// src/execution/config.ts
|
|
159
161
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
160
162
|
var stringValue = (value, label) => {
|
|
161
163
|
if (typeof value !== "string") return fail(`${label} is required.`, "INVALID_CONFIG");
|
|
@@ -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,12 +266,12 @@ 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
|
};
|
|
249
273
|
|
|
250
|
-
// src/state-machine.ts
|
|
274
|
+
// src/kernel/state-machine.ts
|
|
251
275
|
var transition = (run, to, reason, actor = "harness") => {
|
|
252
276
|
if (!STATES.includes(to)) fail(`Unknown state ${to}.`, "INVALID_STATE");
|
|
253
277
|
if (run.state !== to && !LEGAL_TRANSITIONS[run.state].some((state) => state === to)) fail(`Illegal transition ${run.state} -> ${to}.`, "INVALID_STATE");
|
|
@@ -263,7 +287,7 @@ var approvedDecision = (decision) => {
|
|
|
263
287
|
};
|
|
264
288
|
var HARNESS_EVENT_SCHEMA_VERSION = 1;
|
|
265
289
|
var EVENT_LOG_GENESIS = "GENESIS";
|
|
266
|
-
var HARNESS_EVENT_TYPES = ["run.created", "state.transitioned", "context.attached", "verification.completed", "approval.recorded", "authorization.recorded", "session.started", "session.resumed", "agent.turn.started", "policy.evaluated", "tool.approval.requested", "tool.approval.recorded", "tool.requested", "tool.execution.started", "tool.recovery.recorded", "tool.blocked", "tool.completed", "tool.failed", "session.ended"];
|
|
290
|
+
var HARNESS_EVENT_TYPES = ["run.created", "state.transitioned", "context.attached", "verification.completed", "artifact.recorded", "approval.recorded", "authorization.recorded", "session.started", "session.resumed", "agent.turn.started", "policy.evaluated", "tool.approval.requested", "tool.approval.recorded", "tool.requested", "tool.execution.started", "tool.recovery.recorded", "tool.blocked", "tool.completed", "tool.failed", "session.ended"];
|
|
267
291
|
var SESSION_EVENT_TYPES = /* @__PURE__ */ new Set(["session.started", "session.resumed", "agent.turn.started", "policy.evaluated", "tool.approval.requested", "tool.approval.recorded", "tool.requested", "tool.execution.started", "tool.recovery.recorded", "tool.blocked", "tool.completed", "tool.failed", "session.ended"]);
|
|
268
292
|
var eventPath = (stateDir, runId) => join(stateDir, "runs", runId, "events.ndjson");
|
|
269
293
|
var lockPath = (stateDir, runId) => `${eventPath(stateDir, runId)}.lock`;
|
|
@@ -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");
|
|
@@ -389,13 +416,16 @@ var recoverEventLogLock = ({ stateDir, runId, actor, maxAgeMs = 3e5 }) => {
|
|
|
389
416
|
return fail("Event log lock owner is still alive.", "HARNESS_ERROR");
|
|
390
417
|
};
|
|
391
418
|
|
|
392
|
-
// src/plugins.ts
|
|
419
|
+
// src/kernel/plugins.ts
|
|
393
420
|
var createPluginSlot = (id2) => {
|
|
394
421
|
if (!id2.trim()) fail("Plugin slot id is required.", "INVALID_INPUT");
|
|
395
|
-
return { id: id2 };
|
|
422
|
+
return { id: id2.trim() };
|
|
396
423
|
};
|
|
397
424
|
|
|
398
|
-
// src/
|
|
425
|
+
// src/kernel/adapter-contract.ts
|
|
426
|
+
var ASSURANCE_LEVELS = ["unverified", "contract-tested", "runtime-attested"];
|
|
427
|
+
|
|
428
|
+
// src/context/index.ts
|
|
399
429
|
var hashContextSnapshot = ({ providerId, query, references, sourceHash: sourceHash2 }) => hashJson({ providerId, query, references, sourceHash: sourceHash2 });
|
|
400
430
|
var hashContextSnapshots = (snapshots) => hashJson(snapshots.map(({ providerId, query, references, sourceHash: sourceHash2, snapshotHash }) => ({ providerId, query, references, sourceHash: sourceHash2, snapshotHash })));
|
|
401
431
|
var record2 = (value, label) => {
|
|
@@ -418,17 +448,23 @@ var validateContextSnapshot = (value, index2 = 0) => {
|
|
|
418
448
|
uri: requiredString(rawReference["uri"], `context snapshot ${index2}.references[${referenceIndex}].uri`),
|
|
419
449
|
...typeof rawReference["title"] === "string" ? { title: rawReference["title"] } : {},
|
|
420
450
|
...typeof rawReference["version"] === "string" ? { version: rawReference["version"] } : {},
|
|
421
|
-
...typeof rawReference["contentHash"] === "string" ? { contentHash: rawReference["contentHash"] } : {}
|
|
451
|
+
...typeof rawReference["contentHash"] === "string" ? { contentHash: rawReference["contentHash"] } : {},
|
|
452
|
+
...rawReference["relevance"] === void 0 ? {} : typeof rawReference["relevance"] === "number" && rawReference["relevance"] >= 0 && rawReference["relevance"] <= 1 ? { relevance: rawReference["relevance"] } : fail(`context snapshot ${index2}.references[${referenceIndex}].relevance must be between 0 and 1.`, "INVALID_INPUT")
|
|
422
453
|
};
|
|
423
454
|
});
|
|
424
455
|
const scope = rawQuery["scope"] === void 0 ? void 0 : Array.isArray(rawQuery["scope"]) && rawQuery["scope"].every((item) => typeof item === "string") ? rawQuery["scope"] : fail(`context snapshot ${index2}.query.scope must be an array of strings.`, "INVALID_INPUT");
|
|
456
|
+
const assurance = raw["assurance"] === void 0 ? void 0 : ASSURANCE_LEVELS.includes(raw["assurance"]) ? raw["assurance"] : fail(`context snapshot ${index2}.assurance is invalid.`, "INVALID_INPUT");
|
|
457
|
+
const telemetry = raw["telemetry"] === void 0 ? void 0 : record2(raw["telemetry"], `context snapshot ${index2}.telemetry`);
|
|
458
|
+
if (telemetry && telemetry["status"] !== "measured" && telemetry["status"] !== "unknown") fail(`context snapshot ${index2}.telemetry.status is invalid.`, "INVALID_INPUT");
|
|
425
459
|
const snapshot = {
|
|
426
460
|
providerId: requiredString(raw["providerId"], `context snapshot ${index2}.providerId`),
|
|
427
461
|
query: { query: requiredString(rawQuery["query"], `context snapshot ${index2}.query.query`), ...scope ? { scope } : {}, ...typeof rawQuery["sourceRevision"] === "string" ? { sourceRevision: rawQuery["sourceRevision"] } : {} },
|
|
428
462
|
references,
|
|
429
463
|
sourceHash: requiredString(raw["sourceHash"], `context snapshot ${index2}.sourceHash`),
|
|
430
464
|
snapshotHash: requiredString(raw["snapshotHash"], `context snapshot ${index2}.snapshotHash`),
|
|
431
|
-
resolvedAt: requiredString(raw["resolvedAt"], `context snapshot ${index2}.resolvedAt`)
|
|
465
|
+
resolvedAt: requiredString(raw["resolvedAt"], `context snapshot ${index2}.resolvedAt`),
|
|
466
|
+
...assurance === void 0 ? {} : { assurance },
|
|
467
|
+
...telemetry === void 0 ? {} : { telemetry }
|
|
432
468
|
};
|
|
433
469
|
if (snapshot.snapshotHash !== hashContextSnapshot(snapshot)) fail(`context snapshot ${index2}.snapshotHash does not match its contents.`, "INVALID_INPUT");
|
|
434
470
|
return snapshot;
|
|
@@ -440,7 +476,7 @@ var readContextSnapshots = (path) => {
|
|
|
440
476
|
var validateContextSnapshots = (snapshots) => snapshots.map((snapshot, index2) => validateContextSnapshot(snapshot, index2));
|
|
441
477
|
createPluginSlot("context.provider");
|
|
442
478
|
|
|
443
|
-
// src/runs.ts
|
|
479
|
+
// src/execution/runs.ts
|
|
444
480
|
var now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
445
481
|
var newRunId = () => `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
446
482
|
var saveRun2 = (stateDir, run) => {
|
|
@@ -459,8 +495,7 @@ var saveRun2 = (stateDir, run) => {
|
|
|
459
495
|
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
496
|
}
|
|
461
497
|
};
|
|
462
|
-
var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized, contextSnapshots = []
|
|
463
|
-
const contractHash = hashJson(loaded.config.contract);
|
|
498
|
+
var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized, contextSnapshots = [] }) => {
|
|
464
499
|
const run = {
|
|
465
500
|
type: "agentskit-harness-run",
|
|
466
501
|
schemaVersion: 1,
|
|
@@ -468,17 +503,18 @@ var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized,
|
|
|
468
503
|
project: loaded.config.project,
|
|
469
504
|
state: "PLANNED",
|
|
470
505
|
configHash: loaded.configHash,
|
|
471
|
-
contractHash,
|
|
506
|
+
contractHash: hashJson(loaded.config.contract),
|
|
472
507
|
sourceRevision: baseline.revision,
|
|
473
508
|
sourceStatusHash: baseline.statusHash,
|
|
474
509
|
baseline,
|
|
475
|
-
|
|
476
|
-
|
|
510
|
+
autonomy: loaded.config.autonomy,
|
|
511
|
+
contractApproval: { actor: "human", at: now(), contractHash: hashJson(loaded.config.contract) },
|
|
512
|
+
checks: loaded.config.checks.map(({ id: id2, category: category2 }) => ({ id: id2, category: category2, status: "pending" })),
|
|
477
513
|
contextSnapshots,
|
|
478
514
|
...contextSnapshots.length ? { contextHash: hashContextSnapshots(contextSnapshots) } : {},
|
|
479
515
|
...loaded.config.benchmark ? { benchmark: loaded.config.benchmark } : {},
|
|
480
516
|
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:
|
|
517
|
+
transitions: [{ from: null, to: "PLANNED", at: now(), actor: "human" }],
|
|
482
518
|
evidenceReferences: [],
|
|
483
519
|
...supersedes ? { supersedes } : {},
|
|
484
520
|
...dirtyBaselineAuthorized ? { dirtyBaselineAuthorized: true } : {}
|
|
@@ -507,8 +543,8 @@ var validateEvidence = (root, check, evidence, outcomeIds) => {
|
|
|
507
543
|
if (evidence.capability !== "real-browser") failures.push("UI evidence must declare capability real-browser");
|
|
508
544
|
if (!Array.isArray(evidence.artifacts) || evidence.artifacts.length === 0) failures.push("UI evidence requires screenshot artifacts");
|
|
509
545
|
}
|
|
510
|
-
const
|
|
511
|
-
for (const artifactValue of
|
|
546
|
+
const artifacts2 = Array.isArray(evidence.artifacts) ? evidence.artifacts : [];
|
|
547
|
+
for (const artifactValue of artifacts2) {
|
|
512
548
|
if (!isRecord3(artifactValue) || typeof artifactValue["path"] !== "string" || typeof artifactValue["sha256"] !== "string") {
|
|
513
549
|
failures.push("artifact requires string path and sha256");
|
|
514
550
|
continue;
|
|
@@ -531,25 +567,100 @@ var git = async (root, args) => {
|
|
|
531
567
|
};
|
|
532
568
|
var sourceSnapshot = async (root, stateDir) => {
|
|
533
569
|
const revision = await git(root, ["rev-parse", "HEAD"]);
|
|
570
|
+
if (!revision) fail("Current-source evidence requires a Git repository with a committed HEAD.", "GIT_REQUIRED");
|
|
534
571
|
const stateRelative = relative(root, stateDir).replaceAll("\\", "/");
|
|
535
572
|
const pathspec = ["--", "."];
|
|
536
573
|
if (stateRelative && stateRelative !== ".." && !stateRelative.startsWith("../")) pathspec.push(`:(exclude)${stateRelative}`);
|
|
537
|
-
const
|
|
574
|
+
const status2 = await git(root, ["status", "--porcelain=v1", "--untracked-files=all", ...pathspec]);
|
|
538
575
|
const diff = await git(root, ["diff", "--no-ext-diff", "--binary", "HEAD", ...pathspec]);
|
|
539
576
|
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
|
-
|
|
577
|
+
const untracked = untrackedPaths.map((path) => ({ path, hash: sha256(readFileSync(resolve(root, path))) }));
|
|
578
|
+
const fingerprint = { revision, status: status2, diff, untracked };
|
|
579
|
+
return { revision, status: status2, statusHash: hashJson(fingerprint) };
|
|
580
|
+
};
|
|
581
|
+
var thresholds = (value = {}) => {
|
|
582
|
+
const result = { warningPercent: value.warningPercent ?? 75, criticalPercent: value.criticalPercent ?? 90 };
|
|
583
|
+
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");
|
|
584
|
+
return result;
|
|
585
|
+
};
|
|
586
|
+
var linuxSwap = () => {
|
|
587
|
+
if (process.platform !== "linux" || !existsSync("/proc/meminfo")) return void 0;
|
|
588
|
+
const values = Object.fromEntries(readFileSync("/proc/meminfo", "utf8").split(/\r?\n/).flatMap((line) => {
|
|
589
|
+
const match = line.match(/^(SwapTotal|SwapFree):\s+(\d+)\s+kB$/);
|
|
590
|
+
return match ? [[match[1], Number(match[2])]] : [];
|
|
591
|
+
}));
|
|
592
|
+
if (!values["SwapTotal"]) return void 0;
|
|
593
|
+
return Number(((1 - (values["SwapFree"] ?? 0) / values["SwapTotal"]) * 100).toFixed(2));
|
|
594
|
+
};
|
|
595
|
+
var sampleMachine = () => {
|
|
596
|
+
const cpus$1 = Math.max(1, cpus().length);
|
|
597
|
+
const load1 = Math.max(0, loadavg()[0] ?? 0);
|
|
598
|
+
const memory = Math.max(0, Math.min(100, (1 - freemem() / Math.max(1, totalmem())) * 100));
|
|
599
|
+
const swapUsedPercent = linuxSwap();
|
|
600
|
+
return {
|
|
601
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
602
|
+
cpus: cpus$1,
|
|
603
|
+
load1: Number(load1.toFixed(4)),
|
|
604
|
+
load1PerCpuPercent: Number(Math.min(100, load1 / cpus$1 * 100).toFixed(2)),
|
|
605
|
+
memoryUsedPercent: Number(memory.toFixed(2)),
|
|
606
|
+
rssBytes: process.memoryUsage().rss,
|
|
607
|
+
...swapUsedPercent === void 0 ? {} : { swapUsedPercent }
|
|
608
|
+
};
|
|
609
|
+
};
|
|
610
|
+
var summarizeMachine = (samples, sampleIntervalMs = 5e3, limits = {}) => {
|
|
611
|
+
const limit = thresholds(limits);
|
|
612
|
+
const load = samples.map((sample) => sample.load1PerCpuPercent);
|
|
613
|
+
const memory = samples.map((sample) => sample.memoryUsedPercent);
|
|
614
|
+
const rss = samples.map((sample) => sample.rssBytes);
|
|
615
|
+
return {
|
|
616
|
+
sampleIntervalMs,
|
|
617
|
+
samples,
|
|
618
|
+
peakLoad1PerCpuPercent: Number(Math.max(...load, 0).toFixed(2)),
|
|
619
|
+
peakMemoryUsedPercent: Number(Math.max(...memory, 0).toFixed(2)),
|
|
620
|
+
peakRssBytes: Math.max(...rss, 0),
|
|
621
|
+
pressureEvents: samples.filter((sample) => sample.load1PerCpuPercent >= limit.criticalPercent || sample.memoryUsedPercent >= limit.criticalPercent || (sample.swapUsedPercent ?? 0) >= limit.criticalPercent || sample.memoryPressure === "critical").length,
|
|
622
|
+
throttleEvents: 0,
|
|
623
|
+
minimumEffectiveConcurrency: 0
|
|
624
|
+
};
|
|
625
|
+
};
|
|
626
|
+
var adaptiveConcurrency = (configured, sample, limits = {}) => {
|
|
627
|
+
if (!Number.isInteger(configured) || configured < 1) throw new Error("configured concurrency must be a positive integer.");
|
|
628
|
+
const limit = thresholds(limits);
|
|
629
|
+
const critical = sample.load1PerCpuPercent >= limit.criticalPercent || sample.memoryUsedPercent >= limit.criticalPercent || (sample.swapUsedPercent ?? 0) >= limit.criticalPercent || sample.memoryPressure === "critical";
|
|
630
|
+
const warning = sample.load1PerCpuPercent >= limit.warningPercent || sample.memoryUsedPercent >= limit.warningPercent || (sample.swapUsedPercent ?? 0) >= limit.warningPercent || sample.memoryPressure === "warning";
|
|
631
|
+
if (critical) return 1;
|
|
632
|
+
if (warning) return Math.min(configured, 2);
|
|
633
|
+
return configured;
|
|
634
|
+
};
|
|
635
|
+
var createMachineMonitor = (sampleIntervalMs = 5e3, options2 = {}) => {
|
|
636
|
+
const sampler = options2.sample ?? sampleMachine;
|
|
637
|
+
const limits = thresholds(options2.thresholds);
|
|
638
|
+
const samples = [sampler()];
|
|
639
|
+
let throttleEvents = 0;
|
|
640
|
+
const effectiveConcurrency = [];
|
|
641
|
+
const record3 = () => {
|
|
642
|
+
const sample = sampler();
|
|
643
|
+
samples.push(sample);
|
|
644
|
+
return sample;
|
|
645
|
+
};
|
|
646
|
+
const timer = setInterval(record3, sampleIntervalMs);
|
|
647
|
+
timer.unref();
|
|
648
|
+
return {
|
|
649
|
+
sample: record3,
|
|
650
|
+
observeConcurrency: (value) => effectiveConcurrency.push(value),
|
|
651
|
+
markThrottle: () => {
|
|
652
|
+
throttleEvents += 1;
|
|
653
|
+
},
|
|
654
|
+
stop: () => {
|
|
655
|
+
clearInterval(timer);
|
|
656
|
+
record3();
|
|
657
|
+
const summary = summarizeMachine(samples, sampleIntervalMs, limits);
|
|
658
|
+
return { ...summary, throttleEvents, minimumEffectiveConcurrency: effectiveConcurrency.length ? Math.min(...effectiveConcurrency) : 0 };
|
|
546
659
|
}
|
|
547
|
-
}
|
|
548
|
-
const fingerprint = { revision, status, diff, untracked };
|
|
549
|
-
return { revision: revision || `content:${hashJson(fingerprint)}`, status, statusHash: hashJson(fingerprint) };
|
|
660
|
+
};
|
|
550
661
|
};
|
|
551
662
|
|
|
552
|
-
// src/verification.ts
|
|
663
|
+
// src/execution/verification.ts
|
|
553
664
|
var now2 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
554
665
|
var requireRun = (run) => run ?? fail("No verification run exists.", "NO_RUN");
|
|
555
666
|
var verificationProjection = (run) => ({ checks: run.checks, outcomes: run.outcomes, metrics: run.metrics });
|
|
@@ -575,6 +686,12 @@ var runCommand = (check, cwd) => new Promise((resolveResult) => {
|
|
|
575
686
|
resolveResult({ exitCode: exitCode ?? 1, timedOut, stdout, stderr, durationMs: Date.now() - started });
|
|
576
687
|
});
|
|
577
688
|
});
|
|
689
|
+
var executeCheck = async (check, cwd, checkDir, outcomes) => {
|
|
690
|
+
const result = await runCommand(check, cwd);
|
|
691
|
+
const evidence = parseStructuredEvidence(result.stdout);
|
|
692
|
+
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"];
|
|
693
|
+
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 };
|
|
694
|
+
};
|
|
578
695
|
var currentBinding = async (loaded) => ({ source: await sourceSnapshot(loaded.root, loaded.stateDir), configHash: loaded.configHash });
|
|
579
696
|
var staleRun = (loaded, run, reason) => {
|
|
580
697
|
const stale = transition(run, "STALE", reason);
|
|
@@ -587,11 +704,8 @@ var isFresh = async (loaded, run) => {
|
|
|
587
704
|
return current.configHash === run.configHash && current.source.revision === run.sourceRevision && current.source.statusHash === run.sourceStatusHash;
|
|
588
705
|
};
|
|
589
706
|
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
|
-
}
|
|
707
|
+
assertHuman(actor);
|
|
708
|
+
if (!approvedDecision(decision)) fail("Contract was not approved.", "CLARIFYING");
|
|
595
709
|
const loaded = loadConfig(configPath);
|
|
596
710
|
if (loaded.config.contract.ambiguities.length) fail(`Unresolved ambiguities remain: ${loaded.config.contract.ambiguities.join(" | ")}`, "CLARIFYING");
|
|
597
711
|
const validatedContextSnapshots = validateContextSnapshots(contextSnapshots);
|
|
@@ -603,7 +717,7 @@ ${meaningful.join("\n")}
|
|
|
603
717
|
Use --allow-dirty only with explicit human authorization.`, "WORKTREE_DIRTY");
|
|
604
718
|
const previous = loadLatestRun(loaded.stateDir);
|
|
605
719
|
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
|
|
720
|
+
return createRun({ loaded, baseline, supersedes: previous?.runId, dirtyBaselineAuthorized: allowDirty, contextSnapshots: validatedContextSnapshots });
|
|
607
721
|
};
|
|
608
722
|
var startRun = (loaded) => {
|
|
609
723
|
const run = requireRun(loadLatestRun(loaded.stateDir));
|
|
@@ -623,42 +737,81 @@ var cancelRun = async ({ configPath, runId, reason = "Run cancelled by a human."
|
|
|
623
737
|
};
|
|
624
738
|
var verifyRun = async ({ configPath }) => {
|
|
625
739
|
const loaded = loadConfig(configPath);
|
|
740
|
+
const machineMonitor = createMachineMonitor();
|
|
626
741
|
const run = requireRun(loadLatestRun(loaded.stateDir));
|
|
627
742
|
if (!["IMPLEMENTING", "VERIFYING"].includes(run.state)) {
|
|
628
743
|
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
744
|
fail(`Cannot verify from ${run.state}.`, "INVALID_STATE");
|
|
630
745
|
}
|
|
631
746
|
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:
|
|
747
|
+
const binding2 = await currentBinding(loaded);
|
|
748
|
+
let current = { ...transition(run, "VERIFYING", "Verification started.", "agent"), sourceRevision: binding2.source.revision, sourceStatusHash: binding2.source.statusHash };
|
|
634
749
|
saveRun2(loaded.stateDir, current);
|
|
635
750
|
const checkDir = join(loaded.stateDir, "runs", current.runId, "checks");
|
|
636
751
|
mkdirSync(checkDir, { recursive: true });
|
|
637
752
|
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
753
|
let totalDurationMs = 0;
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
754
|
+
let activeChecks = 0;
|
|
755
|
+
let observedPeakConcurrency = 0;
|
|
756
|
+
const verificationStarted = Date.now();
|
|
757
|
+
const maxConcurrency = loaded.config.verification?.maxConcurrency ?? 1;
|
|
758
|
+
const requiredChecks = loaded.config.checks.filter((check) => check.required);
|
|
759
|
+
for (const check of loaded.config.checks.filter((item) => !item.required)) {
|
|
760
|
+
const nextCheck = { id: check.id, category: check.category, status: "not-applicable", failures: [check.reason ?? "Not required by the selected profile."] };
|
|
761
|
+
current = { ...current, checks: current.checks.map((item) => item.id === check.id ? nextCheck : item) };
|
|
762
|
+
}
|
|
763
|
+
const remaining = new Set(requiredChecks.map((check) => check.id));
|
|
764
|
+
const completed = new Set(loaded.config.checks.filter((check) => !check.required).map((check) => check.id));
|
|
765
|
+
while (remaining.size) {
|
|
766
|
+
const ready = requiredChecks.filter((check) => remaining.has(check.id) && (check.dependsOn ?? []).every((dependency) => completed.has(dependency)));
|
|
767
|
+
if (!ready.length) fail("Check dependency graph contains an unknown dependency or cycle.", "INVALID_CONFIG");
|
|
768
|
+
const buildChecks = ready.filter((check) => check.category === "build");
|
|
769
|
+
const queue = buildChecks.length ? buildChecks : ready;
|
|
770
|
+
let offset = 0;
|
|
771
|
+
while (offset < queue.length) {
|
|
772
|
+
const effectiveConcurrency = buildChecks.length ? 1 : adaptiveConcurrency(maxConcurrency, machineMonitor.sample());
|
|
773
|
+
machineMonitor.observeConcurrency(effectiveConcurrency);
|
|
774
|
+
if (effectiveConcurrency < maxConcurrency) machineMonitor.markThrottle();
|
|
775
|
+
const batch = queue.slice(offset, offset + effectiveConcurrency);
|
|
776
|
+
const executed = await Promise.all(batch.map(async (check) => {
|
|
777
|
+
activeChecks += 1;
|
|
778
|
+
observedPeakConcurrency = Math.max(observedPeakConcurrency, activeChecks);
|
|
779
|
+
try {
|
|
780
|
+
return await executeCheck(check, loaded.root, checkDir, outcomesByCheck.get(check.id) ?? []);
|
|
781
|
+
} finally {
|
|
782
|
+
activeChecks -= 1;
|
|
783
|
+
}
|
|
784
|
+
}));
|
|
785
|
+
for (const item of executed) {
|
|
786
|
+
totalDurationMs += item.durationMs;
|
|
787
|
+
const stdoutPath = join(checkDir, `${item.check.id}.stdout`);
|
|
788
|
+
const stderrPath = join(checkDir, `${item.check.id}.stderr`);
|
|
789
|
+
writeFileSync(stdoutPath, item.stdout, "utf8");
|
|
790
|
+
writeFileSync(stderrPath, item.stderr, "utf8");
|
|
791
|
+
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) };
|
|
792
|
+
}
|
|
793
|
+
saveRun2(loaded.stateDir, current);
|
|
794
|
+
for (const check of batch) {
|
|
795
|
+
remaining.delete(check.id);
|
|
796
|
+
completed.add(check.id);
|
|
797
|
+
}
|
|
798
|
+
offset += batch.length;
|
|
799
|
+
}
|
|
651
800
|
}
|
|
652
801
|
const statuses = new Map(current.checks.map((check) => [check.id, check.status]));
|
|
653
802
|
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
|
-
|
|
803
|
+
const allPassed = loaded.config.checks.every((check) => !check.required || statuses.get(check.id) === "passed") && !budgetExceeded;
|
|
804
|
+
current = { ...current, outcomes: current.outcomes.map((outcome) => {
|
|
805
|
+
const required8 = outcome.checks.filter((id2) => loaded.config.checks.find((check) => check.id === id2)?.required);
|
|
806
|
+
return { ...outcome, status: required8.length === 0 ? "not-applicable" : required8.every((id2) => statuses.get(id2) === "passed") ? "passed" : "failed" };
|
|
807
|
+
}), metrics: { totalDurationMs, wallDurationMs: Date.now() - verificationStarted, peakConcurrency: observedPeakConcurrency, budgetExceeded, machine: machineMonitor.stop() } };
|
|
808
|
+
const digest4 = verificationDigest(current);
|
|
809
|
+
current = { ...current, verificationDigest: digest4 };
|
|
658
810
|
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
|
-
|
|
811
|
+
new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest: digest4, checkCount: current.checks.length, outcomeCount: current.outcomes.length, totalDurationMs, budgetExceeded } });
|
|
812
|
+
const automatic = allPassed && current.autonomy === "yolo" && !loaded.config.tracking.required && loaded.config.contract.ambiguities.length === 0;
|
|
813
|
+
const nextState = allPassed ? automatic ? "COMPLETE" : "AWAITING_HUMAN_APPROVAL" : "BLOCKED";
|
|
814
|
+
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
815
|
saveRun2(loaded.stateDir, current);
|
|
663
816
|
setLatest(loaded.stateDir, current);
|
|
664
817
|
return current;
|
|
@@ -672,11 +825,6 @@ var assertVerificationAttestation = (loaded, run) => {
|
|
|
672
825
|
const event = new FileEventStore(loaded.stateDir).read(run.runId).filter((item) => item.type === "verification.completed").at(-1);
|
|
673
826
|
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
827
|
};
|
|
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
828
|
var recordDecision = (loaded, run, type, payload) => {
|
|
681
829
|
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
830
|
};
|
|
@@ -696,7 +844,7 @@ var reconcileRun = async ({ configPath, runId }) => {
|
|
|
696
844
|
if (eventLog.status !== "verified") fail("Terminal run requires a verified event log.", "HARNESS_ERROR");
|
|
697
845
|
assertVerificationAttestation(loaded, run);
|
|
698
846
|
}
|
|
699
|
-
if (run.state === "AWAITING_AUTHORIZATION" || run.state === "COMPLETE") {
|
|
847
|
+
if ((run.state === "AWAITING_AUTHORIZATION" || run.state === "COMPLETE") && run.autonomy !== "yolo") {
|
|
700
848
|
const approval = events2.filter((event) => event.type === "approval.recorded").at(-1) ?? fail("Terminal run is missing its human approval event.", "HARNESS_ERROR");
|
|
701
849
|
assertDecisionProjection(run, approval.payload, run.state === "COMPLETE" && !loaded.config.tracking.required ? "COMPLETE" : "AWAITING_AUTHORIZATION");
|
|
702
850
|
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 +906,7 @@ var retryRun = async ({ configPath }) => {
|
|
|
758
906
|
const baseline = await sourceSnapshot(loaded.root, loaded.stateDir);
|
|
759
907
|
const superseded = transition(previousRun, "SUPERSEDED", "Retry superseded the previous run.", "harness");
|
|
760
908
|
saveRun2(loaded.stateDir, superseded);
|
|
761
|
-
const run = await createRun({ loaded, baseline, supersedes: previousRun.runId, dirtyBaselineAuthorized: previousRun.dirtyBaselineAuthorized
|
|
909
|
+
const run = await createRun({ loaded, baseline, supersedes: previousRun.runId, dirtyBaselineAuthorized: previousRun.dirtyBaselineAuthorized });
|
|
762
910
|
const next = transition(run, "IMPLEMENTING", "Retry started after a previous attempt.", "agent");
|
|
763
911
|
saveRun2(loaded.stateDir, next);
|
|
764
912
|
setLatest(loaded.stateDir, next);
|
|
@@ -778,21 +926,436 @@ var createDocBridgeContextProvider = ({ root, indexPath = ".doc-bridge/index.jso
|
|
|
778
926
|
id: "doc-bridge",
|
|
779
927
|
version: "1.0.0",
|
|
780
928
|
resolve: async (query) => {
|
|
929
|
+
const started = Date.now();
|
|
781
930
|
const document = index(root, indexPath);
|
|
782
931
|
const contentHash = sourceHash(document);
|
|
783
932
|
const entries = Array.isArray(document.knowledge) ? document.knowledge.filter((value) => typeof value === "object" && value !== null && !Array.isArray(value)).filter((entry) => matches(entry, query)).sort((left, right) => String(left.id ?? "").localeCompare(String(right.id ?? ""))).slice(0, 8) : [];
|
|
784
|
-
const references = entries.flatMap((entry) => typeof entry.id === "string" && typeof entry.path === "string" ? [{ id: entry.id, uri: `doc-bridge://${entry.path}`, ...typeof entry.title === "string" ? { title: entry.title } : {}, contentHash }] : []);
|
|
785
|
-
|
|
933
|
+
const references = entries.flatMap((entry) => typeof entry.id === "string" && typeof entry.path === "string" ? [{ id: entry.id, uri: `doc-bridge://${entry.path}`, ...typeof entry.title === "string" ? { title: entry.title } : {}, contentHash: typeof entry.contentHash === "string" ? entry.contentHash : contentHash, relevance: 1 }] : []);
|
|
934
|
+
const telemetry = { status: "measured", durationMs: Date.now() - started, contextReferences: references.length, contextCostTokens: Math.max(1, Math.ceil(JSON.stringify(references).length / 4)) };
|
|
935
|
+
return { providerId: "doc-bridge", query, references, sourceHash: contentHash, snapshotHash: hashContextSnapshot({ providerId: "doc-bridge", query, references, sourceHash: contentHash }), resolvedAt: (/* @__PURE__ */ new Date()).toISOString(), assurance: "contract-tested", telemetry };
|
|
786
936
|
}
|
|
787
937
|
});
|
|
938
|
+
|
|
939
|
+
// src/kernel/discovery.ts
|
|
940
|
+
var required = (value, label) => {
|
|
941
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
942
|
+
return value.trim();
|
|
943
|
+
};
|
|
944
|
+
var unique = (values, label) => {
|
|
945
|
+
if (new Set(values).size !== values.length) fail(`${label} must be unique.`, "INVALID_INPUT");
|
|
946
|
+
};
|
|
947
|
+
var validate = (input) => {
|
|
948
|
+
required(input.issueId, "issueId");
|
|
949
|
+
required(input.sourceRevision, "sourceRevision");
|
|
950
|
+
required(input.contractHash, "contractHash");
|
|
951
|
+
if (!Array.isArray(input.ambiguities)) fail("ambiguities must be an array.", "INVALID_INPUT");
|
|
952
|
+
unique(input.ambiguities.map((item) => required(item.id, "ambiguity.id")), "ambiguity ids");
|
|
953
|
+
const assumptions = /* @__PURE__ */ new Map();
|
|
954
|
+
for (const assumption of input.approvedAssumptions ?? []) {
|
|
955
|
+
const id2 = required(assumption.id, "assumption.id");
|
|
956
|
+
if (assumptions.has(id2)) fail("assumption ids must be unique.", "INVALID_INPUT");
|
|
957
|
+
assumptions.set(id2, { id: id2, policyId: required(assumption.policyId, "assumption.policyId"), resolution: required(assumption.resolution, "assumption.resolution") });
|
|
958
|
+
}
|
|
959
|
+
for (const ambiguity of input.ambiguities) {
|
|
960
|
+
required(ambiguity.question, "ambiguity.question");
|
|
961
|
+
if (typeof ambiguity.material !== "boolean") fail("ambiguity.material must be boolean.", "INVALID_INPUT");
|
|
962
|
+
if (!Array.isArray(ambiguity.options) || ambiguity.options.length < 2 || ambiguity.options.length > 4) fail("ambiguity.options must contain 2 to 4 options.", "INVALID_INPUT");
|
|
963
|
+
unique(ambiguity.options.map((option) => required(option.id, "option.id")), "option ids");
|
|
964
|
+
for (const option of ambiguity.options) {
|
|
965
|
+
required(option.summary, "option.summary");
|
|
966
|
+
required(option.impact, "option.impact");
|
|
967
|
+
}
|
|
968
|
+
if (!ambiguity.options.some((option) => option.id === ambiguity.recommendedOptionId)) fail("recommendedOptionId must identify an option.", "INVALID_INPUT");
|
|
969
|
+
if (!ambiguity.material && (!ambiguity.assumptionId || !assumptions.has(ambiguity.assumptionId))) fail("non-material ambiguity requires an approved assumption.", "INVALID_INPUT");
|
|
970
|
+
}
|
|
971
|
+
return { assumptions };
|
|
972
|
+
};
|
|
973
|
+
var digest2 = (result) => hashJson(result);
|
|
974
|
+
var assessDiscovery = (input) => {
|
|
975
|
+
const { assumptions } = validate(input);
|
|
976
|
+
const human = input.ambiguities.filter((ambiguity) => ambiguity.material);
|
|
977
|
+
const decisionLog = input.ambiguities.map((ambiguity) => {
|
|
978
|
+
if (ambiguity.material) return { ambiguityId: ambiguity.id, kind: "human-decision-required", detail: `Recommendation: ${ambiguity.recommendedOptionId}.` };
|
|
979
|
+
const assumption = assumptions.get(ambiguity.assumptionId);
|
|
980
|
+
return { ambiguityId: ambiguity.id, kind: "approved-assumption", detail: assumption.resolution, policyId: assumption.policyId };
|
|
981
|
+
});
|
|
982
|
+
const base = {
|
|
983
|
+
version: 1,
|
|
984
|
+
issueId: input.issueId,
|
|
985
|
+
sourceRevision: input.sourceRevision,
|
|
986
|
+
contractHash: input.contractHash,
|
|
987
|
+
...input.contextHash ? { contextHash: input.contextHash } : {},
|
|
988
|
+
status: human.length ? "awaiting-decision" : "ready",
|
|
989
|
+
...human.length ? { packet: {
|
|
990
|
+
issueId: input.issueId,
|
|
991
|
+
contractHash: input.contractHash,
|
|
992
|
+
sourceRevision: input.sourceRevision,
|
|
993
|
+
...input.contextHash ? { contextHash: input.contextHash } : {},
|
|
994
|
+
decisions: human.map((ambiguity) => ({ id: ambiguity.id, question: ambiguity.question, options: ambiguity.options, recommendedOptionId: ambiguity.recommendedOptionId }))
|
|
995
|
+
} } : {},
|
|
996
|
+
decisionLog
|
|
997
|
+
};
|
|
998
|
+
return { ...base, digest: digest2(base) };
|
|
999
|
+
};
|
|
1000
|
+
|
|
1001
|
+
// src/kernel/wip.ts
|
|
1002
|
+
var WIP_STATES = ["ready", "implementing", "blocked", "awaiting-decision", "awaiting-acceptance", "done", "cancelled"];
|
|
1003
|
+
var terminal = /* @__PURE__ */ new Set(["done", "cancelled"]);
|
|
1004
|
+
var required2 = (value, label) => {
|
|
1005
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1006
|
+
return value.trim();
|
|
1007
|
+
};
|
|
1008
|
+
var assessWip = ({ entries, candidate, maxInFlight = 3 }) => {
|
|
1009
|
+
if (!Array.isArray(entries)) fail("entries must be an array.", "INVALID_INPUT");
|
|
1010
|
+
if (!Number.isInteger(maxInFlight) || maxInFlight < 1) fail("maxInFlight must be a positive integer.", "INVALID_INPUT");
|
|
1011
|
+
const candidateId = required2(candidate.issueId, "candidate.issueId");
|
|
1012
|
+
if (candidate.kind !== "new" && candidate.kind !== "resume") fail("candidate.kind must be new or resume.", "INVALID_INPUT");
|
|
1013
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1014
|
+
const counts = Object.fromEntries(WIP_STATES.map((state) => [state, 0]));
|
|
1015
|
+
for (const entry of entries) {
|
|
1016
|
+
const id2 = required2(entry.issueId, "entry.issueId");
|
|
1017
|
+
if (ids.has(id2)) fail("entry issueIds must be unique.", "INVALID_INPUT");
|
|
1018
|
+
ids.add(id2);
|
|
1019
|
+
if (!WIP_STATES.includes(entry.state)) fail(`Unknown WIP state: ${entry.state}.`, "INVALID_INPUT");
|
|
1020
|
+
counts[entry.state] += 1;
|
|
1021
|
+
}
|
|
1022
|
+
const inFlight = entries.filter((entry) => !terminal.has(entry.state));
|
|
1023
|
+
const existing = entries.find((entry) => entry.issueId === candidateId);
|
|
1024
|
+
if (candidate.kind === "resume") {
|
|
1025
|
+
if (!existing || terminal.has(existing.state)) return { decision: "hold", inFlight, counts, reason: "A resume requires an existing non-terminal issue." };
|
|
1026
|
+
return { decision: "admit", inFlight, counts, reason: "A resume keeps its existing WIP reservation and takes priority over new work." };
|
|
1027
|
+
}
|
|
1028
|
+
if (existing) return { decision: "hold", inFlight, counts, reason: "A new admission cannot reuse an existing issue id." };
|
|
1029
|
+
if (inFlight.length >= maxInFlight) return { decision: "hold", inFlight, counts, reason: `WIP limit ${maxInFlight} reached; blocked and awaiting-human work still count.` };
|
|
1030
|
+
return { decision: "admit", inFlight, counts, reason: `WIP slot available (${inFlight.length}/${maxInFlight}).` };
|
|
1031
|
+
};
|
|
1032
|
+
|
|
1033
|
+
// src/kernel/experiment.ts
|
|
1034
|
+
var required3 = (value, label) => {
|
|
1035
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1036
|
+
return value.trim();
|
|
1037
|
+
};
|
|
1038
|
+
var comparable = (candidate, baseline) => {
|
|
1039
|
+
for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) {
|
|
1040
|
+
if (candidate[key] !== baseline[key]) fail(`Candidates must share ${key}.`, "INVALID_INPUT");
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
var selectRuntime = (candidates) => {
|
|
1044
|
+
if (!Array.isArray(candidates) || candidates.length < 2) fail("At least two runtime candidates are required.", "INVALID_INPUT");
|
|
1045
|
+
const names = /* @__PURE__ */ new Set();
|
|
1046
|
+
for (const candidate of candidates) {
|
|
1047
|
+
const runtime = required3(candidate.runtime, "candidate.runtime");
|
|
1048
|
+
if (names.has(runtime)) fail("candidate.runtime values must be unique.", "INVALID_INPUT");
|
|
1049
|
+
names.add(runtime);
|
|
1050
|
+
for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) required3(candidate[key], `candidate.${key}`);
|
|
1051
|
+
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");
|
|
1052
|
+
comparable(candidate, candidates[0]);
|
|
1053
|
+
}
|
|
1054
|
+
const eligible = candidates.filter((candidate) => candidate.hardGatesPassed);
|
|
1055
|
+
if (!eligible.length) return { decision: "blocked", eligible, reason: "No runtime passed every hard gate." };
|
|
1056
|
+
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];
|
|
1057
|
+
return { decision: "selected", selected, eligible, reason: "Selected by human minutes, duration, cost, then Orca tie-break." };
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
// src/delivery/index.ts
|
|
1061
|
+
var required4 = (value, label) => {
|
|
1062
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1063
|
+
return value.trim();
|
|
1064
|
+
};
|
|
1065
|
+
var criteriaFor = (criteria, gate) => {
|
|
1066
|
+
if (!Array.isArray(criteria)) fail("criteria must be an array.", "INVALID_INPUT");
|
|
1067
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1068
|
+
for (const criterion of criteria) {
|
|
1069
|
+
const id2 = required4(criterion.id, "criterion.id");
|
|
1070
|
+
if (ids.has(id2)) fail("criterion ids must be unique.", "INVALID_INPUT");
|
|
1071
|
+
ids.add(id2);
|
|
1072
|
+
if (!["G2", "G3", "G4", "G5"].includes(criterion.gate)) fail("criterion.gate is invalid.", "INVALID_INPUT");
|
|
1073
|
+
if (!["passed", "failed", "pending", "not-applicable"].includes(criterion.status)) fail("criterion.status is invalid.", "INVALID_INPUT");
|
|
1074
|
+
if (criterion.status === "not-applicable" && !criterion.reason?.trim()) fail("not-applicable criteria require a reason.", "INVALID_INPUT");
|
|
1075
|
+
}
|
|
1076
|
+
return criteria.filter((criterion) => criterion.gate === gate);
|
|
1077
|
+
};
|
|
1078
|
+
var binding = (value) => ({ candidateRevision: required4(value.candidateRevision, "binding.candidateRevision"), contractHash: required4(value.contractHash, "binding.contractHash"), configHash: required4(value.configHash, "binding.configHash") });
|
|
1079
|
+
var assessed = (gate, decision, reasons, current) => {
|
|
1080
|
+
const base = { gate, decision, reasons, binding: binding(current) };
|
|
1081
|
+
return { ...base, digest: hashJson(base) };
|
|
1082
|
+
};
|
|
1083
|
+
var assessPreflight = ({ criteria, repairAttempts = 0, implementerId, reviewerId, reviewKind, reviewApproved, binding: current }) => {
|
|
1084
|
+
required4(implementerId, "implementerId");
|
|
1085
|
+
if (!Number.isInteger(repairAttempts) || repairAttempts < 0) fail("repairAttempts must be a non-negative integer.", "INVALID_INPUT");
|
|
1086
|
+
const g2 = criteriaFor(criteria, "G2");
|
|
1087
|
+
const reasons = [
|
|
1088
|
+
...g2.length ? [] : ["No G2 criteria are defined."],
|
|
1089
|
+
...g2.filter((criterion) => criterion.status === "failed" || criterion.status === "pending").map((criterion) => `${criterion.id} is ${criterion.status}.`),
|
|
1090
|
+
...reviewApproved && reviewerId && reviewerId !== implementerId && reviewKind === "adversarial" ? [] : ["An approved adversarial review by a reviewer different from the implementer is required."],
|
|
1091
|
+
...repairAttempts <= 2 ? [] : ["The two-repair limit was exceeded; preserve diagnostics and return blocked."]
|
|
1092
|
+
];
|
|
1093
|
+
return assessed("G2", reasons.length ? "blocked" : "approved", reasons, current);
|
|
1094
|
+
};
|
|
1095
|
+
var composePullRequest = ({ draft, g2, remote }) => {
|
|
1096
|
+
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}`);
|
|
1097
|
+
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) };
|
|
1098
|
+
const idempotencyKey = hashJson({ issueId: draft.issueId, contractHash: draft.contractHash, action: "pull-request", revision: draft.candidateRevision });
|
|
1099
|
+
if (remote?.state === "uncertain") return { decision: "blocked", reason: "Remote PR state is uncertain; reconcile before retrying.", idempotencyKey };
|
|
1100
|
+
if (remote?.state === "confirmed") {
|
|
1101
|
+
if (remote.candidateRevision !== draft.candidateRevision || !remote.url) return { decision: "blocked", reason: "Confirmed remote PR does not match the candidate revision.", idempotencyKey };
|
|
1102
|
+
return { decision: "reuse", reason: "The idempotent remote PR already exists for this candidate revision.", idempotencyKey };
|
|
1103
|
+
}
|
|
1104
|
+
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");
|
|
1105
|
+
return { decision: "create", body: body2, reason: "G2 is current and the remote PR is absent.", idempotencyKey };
|
|
1106
|
+
};
|
|
1107
|
+
var assessIntegration = ({ g2, candidateRevision, evidenceRevision, contractHash, configHash, ci }) => {
|
|
1108
|
+
required4(candidateRevision, "candidateRevision");
|
|
1109
|
+
required4(evidenceRevision, "evidenceRevision");
|
|
1110
|
+
if (!["passed", "failed", "pending", "not-applicable"].includes(ci)) fail("ci is invalid.", "INVALID_INPUT");
|
|
1111
|
+
const reasons = [
|
|
1112
|
+
...g2.gate === "G2" && g2.decision === "approved" ? [] : ["G2 is not approved."],
|
|
1113
|
+
...g2.binding.candidateRevision === candidateRevision && g2.binding.contractHash === contractHash && g2.binding.configHash === configHash ? [] : ["G2 is not bound to the current candidate, contract, and configuration."],
|
|
1114
|
+
...candidateRevision === evidenceRevision ? [] : ["Candidate revision changed; G3 evidence must be revalidated."],
|
|
1115
|
+
...ci === "passed" ? [] : [`Integration CI is ${ci}.`]
|
|
1116
|
+
];
|
|
1117
|
+
return assessed("G3", reasons.length ? "blocked" : "approved", reasons, { candidateRevision, contractHash, configHash });
|
|
1118
|
+
};
|
|
1119
|
+
var assessWorktreeCleanup = ({ branch, candidateRevision, contractHash, configHash, remoteBranchRevision, remotePr, integration }) => {
|
|
1120
|
+
required4(branch, "branch");
|
|
1121
|
+
required4(candidateRevision, "candidateRevision");
|
|
1122
|
+
if (remotePr === "uncertain") return { decision: "preserve", reason: "Remote PR state is uncertain; preserve the worktree for reconciliation." };
|
|
1123
|
+
if (remotePr !== "confirmed") return { decision: "preserve", reason: "No confirmed remote PR exists; preserve the worktree." };
|
|
1124
|
+
if (remoteBranchRevision !== candidateRevision) return { decision: "preserve", reason: "Remote branch SHA does not match the candidate revision." };
|
|
1125
|
+
if (integration.gate !== "G3" || integration.decision !== "approved") return { decision: "preserve", reason: "G3 is not approved." };
|
|
1126
|
+
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." };
|
|
1127
|
+
return { decision: "clean", reason: "Remote branch, PR, and G3 evidence are confirmed for the candidate revision." };
|
|
1128
|
+
};
|
|
1129
|
+
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.`]);
|
|
1130
|
+
var assessProduction = ({ profile, integration, artifact, isolated, acceptanceArtifact, lowRisk = true, observationMinutes, technicalPassed, evidence, containmentPreauthorized, containmentAction, linkedDefect }) => {
|
|
1131
|
+
required4(artifact, "artifact");
|
|
1132
|
+
if (!Number.isFinite(observationMinutes) || observationMinutes < 0) fail("observationMinutes must be non-negative.", "INVALID_INPUT");
|
|
1133
|
+
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"));
|
|
1134
|
+
const reasons = [
|
|
1135
|
+
...profileReasons(profile),
|
|
1136
|
+
...integration.gate === "G3" && integration.decision === "approved" ? [] : ["G3 is not approved."],
|
|
1137
|
+
...evidenceReasons,
|
|
1138
|
+
...isolated || acceptanceArtifact === artifact ? [] : ["Exposure requires isolation or acceptance linked to this artifact version."],
|
|
1139
|
+
...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."],
|
|
1140
|
+
...lowRisk && observationMinutes < 15 ? ["Low-risk production validation requires a 15-minute observation window."] : []
|
|
1141
|
+
];
|
|
1142
|
+
return assessed("G4", reasons.length ? "blocked" : "approved", reasons, integration.binding);
|
|
1143
|
+
};
|
|
1144
|
+
var assessAcceptance = ({ production, acceptanceRequired, accepted, notApplicableReason, materialChange }) => {
|
|
1145
|
+
const reasons = [
|
|
1146
|
+
...production.gate === "G4" && production.decision === "approved" ? [] : ["G4 is not approved."],
|
|
1147
|
+
...materialChange ? ["A material change invalidated acceptance; return to the affected gate."] : []
|
|
1148
|
+
];
|
|
1149
|
+
if (reasons.length) return assessed("G5", "blocked", reasons, production.binding);
|
|
1150
|
+
if (acceptanceRequired && !accepted) return assessed("G5", "awaiting-acceptance", ["Business or UX acceptance is still required."], production.binding);
|
|
1151
|
+
if (!acceptanceRequired && !notApplicableReason?.trim()) return assessed("G5", "blocked", ["Acceptance marked not applicable requires a contractual reason."], production.binding);
|
|
1152
|
+
return assessed("G5", "approved", acceptanceRequired ? [] : [`Acceptance is not applicable: ${notApplicableReason}.`], production.binding);
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
// src/kernel/pilot.ts
|
|
1156
|
+
var required5 = (value, label) => {
|
|
1157
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1158
|
+
return value.trim();
|
|
1159
|
+
};
|
|
1160
|
+
var assessPilot = (manifest) => {
|
|
1161
|
+
required5(manifest.policyHash, "policyHash");
|
|
1162
|
+
required5(manifest.baselineReference, "baselineReference");
|
|
1163
|
+
if (!Array.isArray(manifest.entries)) fail("entries must be an array.", "INVALID_INPUT");
|
|
1164
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1165
|
+
const reasons = [];
|
|
1166
|
+
const included = [];
|
|
1167
|
+
for (const entry of manifest.entries) {
|
|
1168
|
+
const issueId = required5(entry.issueId, "entry.issueId");
|
|
1169
|
+
if (ids.has(issueId)) fail("entry issueIds must be unique; an issue cannot be substituted in the same pilot.", "INVALID_INPUT");
|
|
1170
|
+
ids.add(issueId);
|
|
1171
|
+
if (!["normal", "incident", "sensitive"].includes(entry.classification)) fail("entry.classification is invalid.", "INVALID_INPUT");
|
|
1172
|
+
if (!["included", "excluded", "aborted"].includes(entry.status)) fail("entry.status is invalid.", "INVALID_INPUT");
|
|
1173
|
+
if (entry.status !== "included" && !entry.reason?.trim()) reasons.push(`${issueId} is ${entry.status} without an auditable reason.`);
|
|
1174
|
+
if (entry.status === "included") {
|
|
1175
|
+
included.push(issueId);
|
|
1176
|
+
if (entry.classification !== "normal") reasons.push(`${issueId} is ${entry.classification}; only normal issues can enter the pilot.`);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
if (included.length !== 10) reasons.push(`Pilot requires exactly 10 included issues; found ${included.length}.`);
|
|
1180
|
+
const base = { decision: reasons.length ? "blocked" : "ready", included, reasons };
|
|
1181
|
+
return { ...base, digest: hashJson({ ...manifest, ...base }) };
|
|
1182
|
+
};
|
|
1183
|
+
var IMPROVEMENT_CYCLE_STEPS = ["adversarial-review", "g2-preflight", "baseline-record", "pilot-execution", "comparison"];
|
|
1184
|
+
var nonEmpty = (value, label) => {
|
|
1185
|
+
if (typeof value !== "string" || !value.trim()) return fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1186
|
+
return value.trim();
|
|
1187
|
+
};
|
|
1188
|
+
var validateMetrics = (metrics, index2) => {
|
|
1189
|
+
if (metrics === void 0) return void 0;
|
|
1190
|
+
for (const [key, value] of Object.entries(metrics)) {
|
|
1191
|
+
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");
|
|
1192
|
+
}
|
|
1193
|
+
return metrics;
|
|
1194
|
+
};
|
|
1195
|
+
var validateIteration = (iteration, index2) => {
|
|
1196
|
+
if (typeof iteration !== "object" || iteration === null || Array.isArray(iteration)) return fail(`iterations[${index2}] must be an object.`, "INVALID_INPUT");
|
|
1197
|
+
if (!Number.isInteger(iteration.iteration) || iteration.iteration < 1) return fail(`iterations[${index2}].iteration must be a positive integer.`, "INVALID_INPUT");
|
|
1198
|
+
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");
|
|
1199
|
+
iteration.steps.forEach((result, stepIndex) => {
|
|
1200
|
+
if (typeof result !== "object" || result === null || Array.isArray(result)) return fail(`iterations[${index2}].steps[${stepIndex}] must be an object.`, "INVALID_INPUT");
|
|
1201
|
+
if (result.step !== IMPROVEMENT_CYCLE_STEPS[stepIndex]) return fail(`iterations[${index2}].steps[${stepIndex}] must be ${IMPROVEMENT_CYCLE_STEPS[stepIndex]}.`, "INVALID_INPUT");
|
|
1202
|
+
if (!["passed", "failed", "blocked", "pending"].includes(result.status)) return fail(`iterations[${index2}].steps[${stepIndex}].status is invalid.`, "INVALID_INPUT");
|
|
1203
|
+
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");
|
|
1204
|
+
});
|
|
1205
|
+
if (iteration.adjustment !== void 0) nonEmpty(iteration.adjustment, `iterations[${index2}].adjustment`);
|
|
1206
|
+
return { ...iteration, metrics: validateMetrics(iteration.metrics, index2) };
|
|
1207
|
+
};
|
|
1208
|
+
var assessImprovementCycle = (input) => {
|
|
1209
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return fail("cycle input must be an object.", "INVALID_INPUT");
|
|
1210
|
+
const cycleId = nonEmpty(input.cycleId, "cycleId");
|
|
1211
|
+
if (!Number.isInteger(input.maxIterations) || input.maxIterations < 1) return fail("maxIterations must be a positive integer.", "INVALID_INPUT");
|
|
1212
|
+
if (!Array.isArray(input.iterations) || input.iterations.length < 1) return fail("iterations must be non-empty.", "INVALID_INPUT");
|
|
1213
|
+
if (input.iterations.length > input.maxIterations) return fail("iterations cannot exceed maxIterations.", "INVALID_INPUT");
|
|
1214
|
+
const iterations = input.iterations.map(validateIteration);
|
|
1215
|
+
iterations.forEach((iteration, index2) => {
|
|
1216
|
+
if (iteration.iteration !== index2 + 1) return fail("iterations must be sequential and start at 1.", "INVALID_INPUT");
|
|
1217
|
+
if (index2 > 0 && iterations[index2 - 1]?.steps.every((step) => step.status === "passed")) return fail("a completed cycle cannot have later iterations.", "INVALID_INPUT");
|
|
1218
|
+
if (index2 < iterations.length - 1 && !iteration.adjustment) return fail(`iterations[${index2}].adjustment is required before repeating.`, "INVALID_INPUT");
|
|
1219
|
+
});
|
|
1220
|
+
const matrix = iterations.map((iteration) => {
|
|
1221
|
+
const statuses = Object.fromEntries(iteration.steps.map((step) => [step.step, step.status]));
|
|
1222
|
+
const passedSteps = iteration.steps.filter((step) => step.status === "passed").length;
|
|
1223
|
+
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 } : {} };
|
|
1224
|
+
});
|
|
1225
|
+
const latest = iterations[iterations.length - 1];
|
|
1226
|
+
const complete = latest.steps.every((step) => step.status === "passed");
|
|
1227
|
+
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."];
|
|
1228
|
+
const decision = complete ? "complete" : iterations.length >= input.maxIterations || !latest.adjustment ? "blocked" : "repeat";
|
|
1229
|
+
const result = { type: "agentskit-harness-improvement-cycle", cycleId, decision, ...decision === "repeat" ? { nextIteration: latest.iteration + 1 } : {}, reasons, matrix };
|
|
1230
|
+
const digest4 = createHash("sha256").update(JSON.stringify(result)).digest("hex");
|
|
1231
|
+
return { ...result, digest: digest4 };
|
|
1232
|
+
};
|
|
1233
|
+
var ARTIFACT_SCHEMA_VERSION = 1;
|
|
1234
|
+
var ARTIFACT_TYPES = ["plan", "finding", "decision", "repair", "blocker", "approval", "phase"];
|
|
1235
|
+
var text2 = (value, label) => {
|
|
1236
|
+
if (typeof value !== "string" || !value.trim()) return fail(`${label} is required.`, "INVALID_INPUT");
|
|
1237
|
+
return value.trim();
|
|
1238
|
+
};
|
|
1239
|
+
var digest3 = (value, label) => {
|
|
1240
|
+
const result = text2(value, label);
|
|
1241
|
+
if (!/^[a-f0-9]{64}$/.test(result)) fail(`${label} must be a lowercase SHA-256 digest.`, "INVALID_INPUT");
|
|
1242
|
+
return result;
|
|
1243
|
+
};
|
|
1244
|
+
var artifactId = (value) => {
|
|
1245
|
+
const result = text2(value, "Artifact artifactId");
|
|
1246
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(result)) fail("Artifact artifactId is invalid.", "INVALID_INPUT");
|
|
1247
|
+
return result;
|
|
1248
|
+
};
|
|
1249
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1250
|
+
var artifactBody = (artifact) => ({
|
|
1251
|
+
type: artifact.type,
|
|
1252
|
+
schemaVersion: artifact.schemaVersion,
|
|
1253
|
+
artifactId: artifact.artifactId,
|
|
1254
|
+
artifactType: artifact.artifactType,
|
|
1255
|
+
artifactVersion: artifact.artifactVersion,
|
|
1256
|
+
runId: artifact.runId,
|
|
1257
|
+
issueRef: artifact.issueRef,
|
|
1258
|
+
sourceRevision: artifact.sourceRevision,
|
|
1259
|
+
contractHash: artifact.contractHash,
|
|
1260
|
+
configHash: artifact.configHash,
|
|
1261
|
+
contextHash: artifact.contextHash,
|
|
1262
|
+
phase: artifact.phase,
|
|
1263
|
+
payload: artifact.payload,
|
|
1264
|
+
payloadHash: artifact.payloadHash
|
|
1265
|
+
});
|
|
1266
|
+
var expectedArtifactHash = (artifact) => hashJson(artifactBody(artifact));
|
|
1267
|
+
var validateArtifactEnvelope = (value) => {
|
|
1268
|
+
if (!isRecord4(value)) return fail("Artifact envelope must be an object.", "INVALID_INPUT");
|
|
1269
|
+
if (value["type"] !== "agentskit-harness-artifact" || value["schemaVersion"] !== ARTIFACT_SCHEMA_VERSION) fail("Artifact envelope type or schemaVersion is invalid.", "INVALID_INPUT");
|
|
1270
|
+
if (!ARTIFACT_TYPES.includes(value["artifactType"])) fail("Artifact artifactType is invalid.", "INVALID_INPUT");
|
|
1271
|
+
if (!Number.isInteger(value["artifactVersion"]) || value["artifactVersion"] < 1) fail("Artifact artifactVersion must be a positive integer.", "INVALID_INPUT");
|
|
1272
|
+
const createdAt = text2(value["createdAt"], "Artifact createdAt");
|
|
1273
|
+
if (!Number.isFinite(Date.parse(createdAt))) fail("Artifact createdAt must be a valid timestamp.", "INVALID_INPUT");
|
|
1274
|
+
const payloadHash = digest3(value["payloadHash"], "Artifact payloadHash");
|
|
1275
|
+
if (hashJson(value["payload"]) !== payloadHash) fail("Artifact payloadHash does not match payload.", "INVALID_INPUT");
|
|
1276
|
+
const artifact = {
|
|
1277
|
+
type: "agentskit-harness-artifact",
|
|
1278
|
+
schemaVersion: ARTIFACT_SCHEMA_VERSION,
|
|
1279
|
+
artifactId: artifactId(value["artifactId"]),
|
|
1280
|
+
artifactType: value["artifactType"],
|
|
1281
|
+
artifactVersion: value["artifactVersion"],
|
|
1282
|
+
runId: text2(value["runId"], "Artifact runId"),
|
|
1283
|
+
issueRef: text2(value["issueRef"], "Artifact issueRef"),
|
|
1284
|
+
sourceRevision: text2(value["sourceRevision"], "Artifact sourceRevision"),
|
|
1285
|
+
contractHash: digest3(value["contractHash"], "Artifact contractHash"),
|
|
1286
|
+
configHash: digest3(value["configHash"], "Artifact configHash"),
|
|
1287
|
+
contextHash: digest3(value["contextHash"], "Artifact contextHash"),
|
|
1288
|
+
phase: text2(value["phase"], "Artifact phase"),
|
|
1289
|
+
createdAt,
|
|
1290
|
+
payload: value["payload"],
|
|
1291
|
+
payloadHash
|
|
1292
|
+
};
|
|
1293
|
+
if (digest3(value["artifactHash"], "Artifact artifactHash") !== expectedArtifactHash(artifact)) fail("Artifact artifactHash does not match envelope.", "INVALID_INPUT");
|
|
1294
|
+
return { ...artifact, artifactHash: value["artifactHash"] };
|
|
1295
|
+
};
|
|
1296
|
+
var renderArtifactMarkdown = (artifact) => [
|
|
1297
|
+
`# ${artifact.artifactType} artifact ${artifact.artifactId}`,
|
|
1298
|
+
"",
|
|
1299
|
+
`- Schema: ${artifact.schemaVersion}`,
|
|
1300
|
+
`- Version: ${artifact.artifactVersion}`,
|
|
1301
|
+
`- Run: ${artifact.runId}`,
|
|
1302
|
+
`- Issue: ${artifact.issueRef}`,
|
|
1303
|
+
`- Phase: ${artifact.phase}`,
|
|
1304
|
+
`- Source revision: ${artifact.sourceRevision}`,
|
|
1305
|
+
`- Contract hash: ${artifact.contractHash}`,
|
|
1306
|
+
`- Configuration hash: ${artifact.configHash}`,
|
|
1307
|
+
`- Context hash: ${artifact.contextHash}`,
|
|
1308
|
+
`- Artifact hash: ${artifact.artifactHash}`,
|
|
1309
|
+
"",
|
|
1310
|
+
"## Payload",
|
|
1311
|
+
"",
|
|
1312
|
+
"```json",
|
|
1313
|
+
JSON.stringify(artifact.payload, null, 2),
|
|
1314
|
+
"```",
|
|
1315
|
+
""
|
|
1316
|
+
].join("\n");
|
|
1317
|
+
var artifactFilePath = (stateDir, runId, id2) => join(stateDir, "runs", runId, "artifacts", `${id2}.json`);
|
|
1318
|
+
var artifactMarkdownPath = (stateDir, runId, id2) => join(stateDir, "runs", runId, "artifacts", `${id2}.md`);
|
|
1319
|
+
var FileArtifactStore = class {
|
|
1320
|
+
constructor(stateDir) {
|
|
1321
|
+
this.stateDir = stateDir;
|
|
1322
|
+
}
|
|
1323
|
+
stateDir;
|
|
1324
|
+
write(input) {
|
|
1325
|
+
const artifact = validateArtifactEnvelope(input);
|
|
1326
|
+
const path = artifactFilePath(this.stateDir, artifact.runId, artifact.artifactId);
|
|
1327
|
+
mkdirSync(join(this.stateDir, "runs", artifact.runId, "artifacts"), { recursive: true });
|
|
1328
|
+
if (existsSync(path)) {
|
|
1329
|
+
const existing = validateArtifactEnvelope(JSON.parse(readFileSync(path, "utf8")));
|
|
1330
|
+
if (existing.artifactHash !== artifact.artifactHash) fail(`Artifact ${artifact.artifactId} already exists with different content.`, "HARNESS_ERROR");
|
|
1331
|
+
return existing;
|
|
1332
|
+
}
|
|
1333
|
+
writeFileSync(path, `${JSON.stringify(artifact, null, 2)}
|
|
1334
|
+
`, "utf8");
|
|
1335
|
+
writeFileSync(artifactMarkdownPath(this.stateDir, artifact.runId, artifact.artifactId), renderArtifactMarkdown(artifact), "utf8");
|
|
1336
|
+
new FileEventStore(this.stateDir).append({
|
|
1337
|
+
runId: artifact.runId,
|
|
1338
|
+
sourceRevision: artifact.sourceRevision,
|
|
1339
|
+
configHash: artifact.configHash,
|
|
1340
|
+
type: "artifact.recorded",
|
|
1341
|
+
payload: { artifactId: artifact.artifactId, artifactType: artifact.artifactType, artifactVersion: artifact.artifactVersion, artifactHash: artifact.artifactHash, phase: artifact.phase, representation: "json+markdown" }
|
|
1342
|
+
});
|
|
1343
|
+
return artifact;
|
|
1344
|
+
}
|
|
1345
|
+
read(runId, id2) {
|
|
1346
|
+
return validateArtifactEnvelope(JSON.parse(readFileSync(artifactFilePath(this.stateDir, runId, artifactId(id2)), "utf8")));
|
|
1347
|
+
}
|
|
1348
|
+
list(runId) {
|
|
1349
|
+
const directory = join(this.stateDir, "runs", runId, "artifacts");
|
|
1350
|
+
if (!existsSync(directory)) return [];
|
|
1351
|
+
return readdirSync(directory).filter((name) => name.endsWith(".json")).sort().map((name) => validateArtifactEnvelope(JSON.parse(readFileSync(join(directory, name), "utf8"))));
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
var readArtifactFile = (path) => validateArtifactEnvelope(JSON.parse(readFileSync(path, "utf8")));
|
|
788
1355
|
var BENCHMARK_SCHEMA_VERSION = 1;
|
|
789
1356
|
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
1357
|
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";
|
|
1358
|
+
var improvementDirection = (rate) => rate === null ? "unavailable" : rate > 0 ? "improved" : rate < 0 ? "regressed" : "unchanged";
|
|
796
1359
|
var count = (items, predicate) => items.filter(predicate).length;
|
|
797
1360
|
var median = (values) => {
|
|
798
1361
|
if (!values.length) return null;
|
|
@@ -807,25 +1370,13 @@ var reviewMinutes = (run) => {
|
|
|
807
1370
|
const elapsed = Date.parse(run.humanApproval.at) - Date.parse(reviewStart);
|
|
808
1371
|
return Number.isFinite(elapsed) && elapsed >= 0 ? Number((elapsed / 6e4).toFixed(2)) : void 0;
|
|
809
1372
|
};
|
|
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
1373
|
var projectRun = (run) => {
|
|
822
1374
|
const checks = { total: run.checks.length, passed: count(run.checks, (check) => check.status === "passed"), failed: count(run.checks, (check) => check.status === "failed") };
|
|
823
1375
|
const outcomes = { total: run.outcomes.length, passed: count(run.outcomes, (outcome) => outcome.status === "passed"), failed: count(run.outcomes, (outcome) => outcome.status === "failed") };
|
|
824
1376
|
const evidence = { total: run.checks.length, attached: count(run.checks, (check) => check.evidence !== void 0) };
|
|
825
|
-
const acceptanceRate = artifactAcceptanceRate(run);
|
|
826
1377
|
const humanReviewMinutes = reviewMinutes(run);
|
|
827
1378
|
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, ...
|
|
1379
|
+
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
1380
|
};
|
|
830
1381
|
var summarize = (runs) => {
|
|
831
1382
|
const stateCounts = Object.fromEntries(RUN_STATES.map((state) => [state, count(runs, (run) => run.state === state)]));
|
|
@@ -836,14 +1387,6 @@ var summarize = (runs) => {
|
|
|
836
1387
|
const evidenceTotal = runs.reduce((total, run) => total + run.evidence.total, 0);
|
|
837
1388
|
const evidenceAttached = runs.reduce((total, run) => total + run.evidence.attached, 0);
|
|
838
1389
|
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
1390
|
const durations = runs.flatMap((run) => run.durationMs === void 0 ? [] : [run.durationMs]);
|
|
848
1391
|
return {
|
|
849
1392
|
totalRuns: runs.length,
|
|
@@ -854,12 +1397,6 @@ var summarize = (runs) => {
|
|
|
854
1397
|
firstAttemptRuns: firstAttempts.length,
|
|
855
1398
|
humanApprovedRuns: count(runs, (run) => run.humanApproved),
|
|
856
1399
|
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
1400
|
checkPassRate: percentage(checksPassed, checksTotal),
|
|
864
1401
|
outcomePassRate: percentage(outcomesPassed, outcomesTotal),
|
|
865
1402
|
evidenceCoverageRate: percentage(evidenceAttached, evidenceTotal),
|
|
@@ -912,68 +1449,11 @@ var nonNegativeInteger = (value, label) => {
|
|
|
912
1449
|
if (result !== void 0 && !Number.isInteger(result)) return fail(`${label} must be an integer.`, "INVALID_CONFIG");
|
|
913
1450
|
return result;
|
|
914
1451
|
};
|
|
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
1452
|
var timestamp = (value, label) => {
|
|
921
1453
|
const result = nonEmptyString(value, label);
|
|
922
1454
|
if (!Number.isFinite(Date.parse(result))) return fail(`${label} must be a valid timestamp.`, "INVALID_CONFIG");
|
|
923
1455
|
return result;
|
|
924
1456
|
};
|
|
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
1457
|
var validateBenchmarkManifest = (value) => {
|
|
978
1458
|
if (typeof value !== "object" || value === null || Array.isArray(value)) fail("benchmark manifest must be an object.", "INVALID_CONFIG");
|
|
979
1459
|
const raw = value;
|
|
@@ -982,12 +1462,7 @@ var validateBenchmarkManifest = (value) => {
|
|
|
982
1462
|
const tasks = rawTasks.map((item, index2) => {
|
|
983
1463
|
if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`benchmark.tasks[${index2}] must be an object.`, "INVALID_CONFIG");
|
|
984
1464
|
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 } };
|
|
1465
|
+
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
1466
|
});
|
|
992
1467
|
if (new Set(tasks.map((task) => task.id)).size !== tasks.length) fail("benchmark task ids must be unique.", "INVALID_CONFIG");
|
|
993
1468
|
const taskIds = new Set(tasks.map((task) => task.id));
|
|
@@ -995,17 +1470,13 @@ var validateBenchmarkManifest = (value) => {
|
|
|
995
1470
|
const observations = rawObservations.map((item, index2) => {
|
|
996
1471
|
if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`benchmark.observations[${index2}] must be an object.`, "INVALID_CONFIG");
|
|
997
1472
|
const observation = item;
|
|
998
|
-
const
|
|
999
|
-
if (!["passed", "failed", "blocked", "not-run"].includes(String(
|
|
1473
|
+
const status2 = observation["status"];
|
|
1474
|
+
if (!["passed", "failed", "blocked", "not-run"].includes(String(status2))) fail(`benchmark.observations[${index2}].status is invalid.`, "INVALID_CONFIG");
|
|
1000
1475
|
const taskId = nonEmptyString(observation["taskId"], `benchmark.observations[${index2}].taskId`);
|
|
1001
1476
|
if (!taskIds.has(taskId)) fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
|
|
1002
1477
|
const task = tasks.find((candidate) => candidate.id === taskId) ?? fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
|
|
1003
1478
|
const attempts = nonNegativeInteger(observation["attempts"], `benchmark.observations[${index2}].attempts`);
|
|
1004
1479
|
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
1480
|
const reviewMinutes2 = nonNegativeNumber(observation["reviewMinutes"], `benchmark.observations[${index2}].reviewMinutes`);
|
|
1010
1481
|
const escapedIncomplete = nonNegativeInteger(observation["escapedIncomplete"], `benchmark.observations[${index2}].escapedIncomplete`);
|
|
1011
1482
|
const evidenceDigest = sha2562(observation["evidenceDigest"], `benchmark.observations[${index2}].evidenceDigest`);
|
|
@@ -1020,12 +1491,10 @@ var validateBenchmarkManifest = (value) => {
|
|
|
1020
1491
|
return { criterion, status: evidenceStatus, source: nonEmptyString(entry["source"], `benchmark.observations[${index2}].evidence[${evidenceIndex}].source`) };
|
|
1021
1492
|
});
|
|
1022
1493
|
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 }, ...
|
|
1494
|
+
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
1495
|
});
|
|
1025
1496
|
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 } };
|
|
1497
|
+
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
1498
|
};
|
|
1030
1499
|
var loadBenchmarkManifest = (path) => {
|
|
1031
1500
|
try {
|
|
@@ -1051,9 +1520,6 @@ var recordBenchmarkObservation = (path, input) => {
|
|
|
1051
1520
|
recordedAt: input.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1052
1521
|
...input.attempts === void 0 ? {} : { attempts: input.attempts },
|
|
1053
1522
|
...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
1523
|
...input.reviewMinutes === void 0 ? {} : { reviewMinutes: input.reviewMinutes },
|
|
1058
1524
|
...input.escapedIncomplete === void 0 ? {} : { escapedIncomplete: input.escapedIncomplete },
|
|
1059
1525
|
...input.evidence === void 0 ? {} : { evidence: input.evidence },
|
|
@@ -1072,57 +1538,261 @@ var recordBenchmarkObservation = (path, input) => {
|
|
|
1072
1538
|
}
|
|
1073
1539
|
return observation;
|
|
1074
1540
|
};
|
|
1075
|
-
var comparisons = (runs, manifest
|
|
1541
|
+
var comparisons = (runs, manifest) => manifest.tasks.map((task) => {
|
|
1076
1542
|
const taskRuns = runs.filter((run) => run.benchmark?.suiteId === manifest.suiteId && run.benchmark.taskId === task.id);
|
|
1077
1543
|
const latest = taskRuns.at(-1);
|
|
1078
1544
|
const baseline = manifest.observations.find((observation) => observation.taskId === task.id);
|
|
1079
1545
|
const coveredCriteria = new Set((baseline?.evidence ?? []).map((entry) => entry.criterion));
|
|
1080
1546
|
const baselineEvidenceCoverageRate = baseline ? percentage(coveredCriteria.size, task.acceptanceCriteria.length) : null;
|
|
1081
1547
|
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 } : {} };
|
|
1548
|
+
const comparable2 = baseline !== void 0 && baseline.status !== "not-run" && baselineEvidenceComplete && latest?.state === "COMPLETE";
|
|
1549
|
+
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";
|
|
1550
|
+
const durationRate = comparable2 ? improvementRate(baseline?.durationMs, latest?.durationMs) : null;
|
|
1551
|
+
const attemptsRate = comparable2 ? improvementRate(baseline?.attempts, taskRuns.length) : null;
|
|
1552
|
+
const reviewRate = comparable2 ? improvementRate(baseline?.reviewMinutes, latest?.humanReviewMinutes) : null;
|
|
1553
|
+
const escapedIncompleteRate = comparable2 ? improvementRate(baseline?.escapedIncomplete, latest?.escapedIncomplete) : null;
|
|
1554
|
+
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
1555
|
});
|
|
1108
1556
|
var benchmarkRuns = (stateDir, manifest) => {
|
|
1109
1557
|
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
|
-
|
|
1558
|
+
const reportComparisons = manifest ? comparisons(runs, manifest) : [];
|
|
1559
|
+
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 } } : {} };
|
|
1560
|
+
};
|
|
1561
|
+
var required6 = (value, label) => {
|
|
1562
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1563
|
+
return value.trim();
|
|
1564
|
+
};
|
|
1565
|
+
var safeKey = (identity) => hashJson(identity);
|
|
1566
|
+
var now3 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
1567
|
+
var parse = (value, label) => {
|
|
1568
|
+
try {
|
|
1569
|
+
const raw = JSON.parse(value);
|
|
1570
|
+
const identity = {
|
|
1571
|
+
tracker: required6(raw["tracker"], `${label}.tracker`),
|
|
1572
|
+
repository: required6(raw["repository"], `${label}.repository`),
|
|
1573
|
+
issue: required6(raw["issue"], `${label}.issue`),
|
|
1574
|
+
worktree: required6(raw["worktree"], `${label}.worktree`),
|
|
1575
|
+
branch: required6(raw["branch"], `${label}.branch`)
|
|
1576
|
+
};
|
|
1577
|
+
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`) };
|
|
1578
|
+
} catch (error) {
|
|
1579
|
+
if (error instanceof SyntaxError) fail(`${label} contains invalid JSON.`, "HARNESS_ERROR");
|
|
1580
|
+
throw error;
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
var createDispatchLedger = (stateDir) => {
|
|
1584
|
+
const root = required6(stateDir, "stateDir");
|
|
1585
|
+
const claimsDir = join(root, "coordination", "claims");
|
|
1586
|
+
const ledgerPath = join(root, "coordination", "dispatch-ledger.ndjson");
|
|
1587
|
+
mkdirSync(claimsDir, { recursive: true });
|
|
1588
|
+
const claimPath = (key) => join(claimsDir, `${key}.json`);
|
|
1589
|
+
const append = (record3) => appendFileSync(ledgerPath, `${JSON.stringify(record3)}
|
|
1590
|
+
`, "utf8");
|
|
1591
|
+
const records = () => {
|
|
1592
|
+
if (!existsSync(ledgerPath)) return [];
|
|
1593
|
+
return readFileSync(ledgerPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line, index2) => {
|
|
1594
|
+
try {
|
|
1595
|
+
return JSON.parse(line);
|
|
1596
|
+
} catch {
|
|
1597
|
+
return fail(`Dispatch ledger record ${index2 + 1} is invalid JSON.`, "HARNESS_ERROR");
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
};
|
|
1601
|
+
const active = () => {
|
|
1602
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1603
|
+
for (const record3 of records()) {
|
|
1604
|
+
if (record3.action === "release" || record3.action === "recover") byKey.delete(record3.key);
|
|
1605
|
+
else if (record3.action === "dispatch") byKey.set(record3.key, record3);
|
|
1606
|
+
}
|
|
1607
|
+
return [...byKey.values()];
|
|
1608
|
+
};
|
|
1609
|
+
return {
|
|
1610
|
+
claim: (input) => {
|
|
1611
|
+
const identity = {
|
|
1612
|
+
tracker: required6(input.tracker, "tracker"),
|
|
1613
|
+
repository: required6(input.repository, "repository"),
|
|
1614
|
+
issue: required6(input.issue, "issue"),
|
|
1615
|
+
worktree: required6(input.worktree, "worktree"),
|
|
1616
|
+
branch: required6(input.branch, "branch")
|
|
1617
|
+
};
|
|
1618
|
+
const owner = required6(input.owner, "owner");
|
|
1619
|
+
const key = safeKey(identity);
|
|
1620
|
+
const path = claimPath(key);
|
|
1621
|
+
if (existsSync(path)) return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
|
|
1622
|
+
const lease = { ...identity, key, leaseId: randomUUID(), owner, claimedAt: now3() };
|
|
1623
|
+
let fd;
|
|
1624
|
+
try {
|
|
1625
|
+
fd = openSync(path, "wx");
|
|
1626
|
+
} catch (error) {
|
|
1627
|
+
if (error.code === "EEXIST") return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
|
|
1628
|
+
throw error;
|
|
1629
|
+
}
|
|
1630
|
+
try {
|
|
1631
|
+
writeFileSync(fd, JSON.stringify(lease), "utf8");
|
|
1632
|
+
} finally {
|
|
1633
|
+
closeSync(fd);
|
|
1634
|
+
}
|
|
1635
|
+
append({ ...lease, action: "dispatch", at: lease.claimedAt });
|
|
1636
|
+
return { decision: "claimed", lease };
|
|
1637
|
+
},
|
|
1638
|
+
recordDispatch: ({ lease, idempotencyKey, commandDigest }) => {
|
|
1639
|
+
const id2 = required6(idempotencyKey, "idempotencyKey");
|
|
1640
|
+
const digest4 = required6(commandDigest, "commandDigest");
|
|
1641
|
+
const existing = records().find((record4) => record4.action === "dispatch" && record4.idempotencyKey === id2);
|
|
1642
|
+
if (existing) return { decision: "duplicate", record: existing };
|
|
1643
|
+
const record3 = { ...lease, action: "dispatch", at: now3(), idempotencyKey: id2, commandDigest: digest4 };
|
|
1644
|
+
append(record3);
|
|
1645
|
+
return { decision: "recorded", record: record3 };
|
|
1646
|
+
},
|
|
1647
|
+
release: (lease, reason = "lease released") => {
|
|
1648
|
+
const path = claimPath(required6(lease.key, "lease.key"));
|
|
1649
|
+
if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
|
|
1650
|
+
const current = parse(readFileSync(path, "utf8"), "claim");
|
|
1651
|
+
if (current.leaseId !== lease.leaseId) fail("Dispatch lease owner does not match.", "INVALID_STATE");
|
|
1652
|
+
unlinkSync(path);
|
|
1653
|
+
const record3 = { ...current, action: "release", at: now3(), reason: required6(reason, "reason") };
|
|
1654
|
+
append(record3);
|
|
1655
|
+
return record3;
|
|
1656
|
+
},
|
|
1657
|
+
recover: (key, input) => {
|
|
1658
|
+
if (input.actor !== "human") fail("Dispatch lease recovery requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
|
|
1659
|
+
const normalizedKey = required6(key, "key");
|
|
1660
|
+
const maxAgeMs = input.maxAgeMs ?? 3e5;
|
|
1661
|
+
if (!Number.isInteger(maxAgeMs) || maxAgeMs < 0) fail("maxAgeMs must be a non-negative integer.", "INVALID_INPUT");
|
|
1662
|
+
const path = claimPath(normalizedKey);
|
|
1663
|
+
if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
|
|
1664
|
+
const current = parse(readFileSync(path, "utf8"), "claim");
|
|
1665
|
+
if (Date.now() - Date.parse(current.claimedAt) < maxAgeMs) fail("Dispatch lease is not old enough to recover.", "HARNESS_ERROR");
|
|
1666
|
+
unlinkSync(path);
|
|
1667
|
+
const record3 = { ...current, action: "recover", at: now3(), reason: required6(input.reason, "reason") };
|
|
1668
|
+
append(record3);
|
|
1669
|
+
return record3;
|
|
1670
|
+
},
|
|
1671
|
+
active,
|
|
1672
|
+
records
|
|
1673
|
+
};
|
|
1674
|
+
};
|
|
1675
|
+
var DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx", ".txt", ".adoc", ".rst"]);
|
|
1676
|
+
var TEST_SUFFIXES = [".test.", ".spec.", "__tests__"];
|
|
1677
|
+
var normalizedPath = (value, label) => {
|
|
1678
|
+
if (typeof value !== "string" || !value.trim()) fail(`${label} must be a non-empty path.`, "INVALID_INPUT");
|
|
1679
|
+
const path = value.trim().replaceAll("\\", "/");
|
|
1680
|
+
if (path.startsWith("/") || path.split("/").includes("..")) fail(`${label} must be repository-relative.`, "INVALID_INPUT");
|
|
1681
|
+
return path;
|
|
1682
|
+
};
|
|
1683
|
+
var isTest = (path) => TEST_SUFFIXES.some((suffix) => path.includes(suffix)) || /(^|\/)(test|tests|__tests__)\//.test(path);
|
|
1684
|
+
var isDoc = (path) => DOC_EXTENSIONS.has(extname(path).toLowerCase());
|
|
1685
|
+
var planFilePreflight = (files, options2 = {}) => {
|
|
1686
|
+
if (!Array.isArray(files)) fail("files must be an array.", "INVALID_INPUT");
|
|
1687
|
+
const unique2 = [...new Set(files.map((file, index2) => normalizedPath(file.path, `files[${index2}].path`)))].sort();
|
|
1688
|
+
const codeFiles = unique2.filter((path) => !isDoc(path) && !isTest(path));
|
|
1689
|
+
const existingTests = unique2.filter(isTest);
|
|
1690
|
+
const roots = (options2.testRoots ?? ["test", "tests", "__tests__"]).map((root, index2) => normalizedPath(root, `testRoots[${index2}]`));
|
|
1691
|
+
const colocated = options2.includeTests === false ? [] : codeFiles.flatMap((path) => {
|
|
1692
|
+
const file = basename(path);
|
|
1693
|
+
const directory = dirname(path);
|
|
1694
|
+
const stem = file.includes(".") ? file.slice(0, file.lastIndexOf(".")) : file;
|
|
1695
|
+
return [join(directory, `${stem}.test.ts`), join(directory, `${stem}.spec.ts`)].filter((candidate) => unique2.includes(candidate));
|
|
1696
|
+
});
|
|
1697
|
+
const testFiles = [...new Set([...existingTests, ...colocated, ...unique2.filter((path) => roots.some((root) => path === root || path.startsWith(`${root}/`)))].sort())];
|
|
1698
|
+
const docsOnly = unique2.length > 0 && codeFiles.length === 0 && existingTests.length === 0;
|
|
1699
|
+
return { files: unique2, codeFiles, testFiles, docsOnly, checks: docsOnly ? [] : ["lint", "typecheck", ...testFiles.length ? ["test"] : []] };
|
|
1700
|
+
};
|
|
1701
|
+
|
|
1702
|
+
// src/kernel/block.ts
|
|
1703
|
+
var BLOCK_STATUSES = ["todo", "picked", "development", "validation", "pr-open", "merged", "post-merge", "done", "blocked", "scope-cut"];
|
|
1704
|
+
var text3 = (value, label) => {
|
|
1705
|
+
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1706
|
+
};
|
|
1707
|
+
var list = (value, label) => {
|
|
1708
|
+
if (!Array.isArray(value)) return fail(`${label} must be an array of non-empty strings.`, "INVALID_INPUT");
|
|
1709
|
+
if (!value.every((item) => typeof item === "string" && Boolean(item.trim()))) return fail(`${label} must be an array of non-empty strings.`, "INVALID_INPUT");
|
|
1710
|
+
return [...new Set(value.map((item) => item.trim()))];
|
|
1711
|
+
};
|
|
1712
|
+
var validateBlockManifest = (value) => {
|
|
1713
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) fail("block manifest must be an object.", "INVALID_INPUT");
|
|
1714
|
+
const raw = value;
|
|
1715
|
+
if (raw["schemaVersion"] !== 1) fail("block manifest schemaVersion must be 1.", "INVALID_INPUT");
|
|
1716
|
+
const criteria = list(raw["acceptanceCriteria"], "acceptanceCriteria");
|
|
1717
|
+
if (!criteria.length) fail("acceptanceCriteria must not be empty.", "INVALID_INPUT");
|
|
1718
|
+
const dependencies = list(raw["dependencies"] ?? [], "dependencies");
|
|
1719
|
+
const wave = raw["wave"];
|
|
1720
|
+
if (!Number.isInteger(wave) || wave < 1) fail("wave must be a positive integer.", "INVALID_INPUT");
|
|
1721
|
+
const status2 = raw["status"];
|
|
1722
|
+
if (!BLOCK_STATUSES.includes(status2)) fail("status is invalid.", "INVALID_INPUT");
|
|
1723
|
+
const budgetRaw = raw["budget"];
|
|
1724
|
+
let budget;
|
|
1725
|
+
if (budgetRaw !== void 0) {
|
|
1726
|
+
if (typeof budgetRaw !== "object" || budgetRaw === null || Array.isArray(budgetRaw)) fail("budget must be an object.", "INVALID_INPUT");
|
|
1727
|
+
const candidate = budgetRaw;
|
|
1728
|
+
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");
|
|
1729
|
+
budget = { ...candidate["maxMinutes"] === void 0 ? {} : { maxMinutes: candidate["maxMinutes"] }, ...candidate["maxAttempts"] === void 0 ? {} : { maxAttempts: candidate["maxAttempts"] } };
|
|
1730
|
+
}
|
|
1731
|
+
return { schemaVersion: 1, id: text3(raw["id"], "id"), title: text3(raw["title"], "title"), tracker: text3(raw["tracker"], "tracker"), repository: text3(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: text3(raw["sourceHash"], "sourceHash") } };
|
|
1732
|
+
};
|
|
1733
|
+
var assessBlock = (manifest, completedDependencies = []) => {
|
|
1734
|
+
const value = validateBlockManifest(manifest);
|
|
1735
|
+
const completed = new Set(completedDependencies.map((item) => text3(item, "completedDependencies[]")));
|
|
1736
|
+
const blockers = value.dependencies.filter((dependency) => !completed.has(dependency));
|
|
1737
|
+
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."];
|
|
1738
|
+
return { status: blockers.length || value.status === "blocked" ? "blocked" : "ready", manifestHash: hashJson(value), blockers, next };
|
|
1739
|
+
};
|
|
1740
|
+
var text4 = (value, label) => {
|
|
1741
|
+
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1742
|
+
};
|
|
1743
|
+
var category = (heading) => {
|
|
1744
|
+
const value = heading.toLowerCase();
|
|
1745
|
+
if (/went well|success|worked/.test(value)) return "worked";
|
|
1746
|
+
if (/problem|failed|blocker|pain/.test(value)) return "problem";
|
|
1747
|
+
if (/adjust|action|next|improv/.test(value)) return "adjustment";
|
|
1748
|
+
return "other";
|
|
1749
|
+
};
|
|
1750
|
+
var parseRetro = (markdown, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) => {
|
|
1751
|
+
const input = text4(markdown, "markdown");
|
|
1752
|
+
const origin = text4(source, "source");
|
|
1753
|
+
if (!Number.isFinite(Date.parse(recordedAt))) fail("recordedAt must be a valid timestamp.", "INVALID_INPUT");
|
|
1754
|
+
const records = [];
|
|
1755
|
+
let current = "other";
|
|
1756
|
+
for (const line of input.split(/\r?\n/)) {
|
|
1757
|
+
const heading = line.match(/^#{1,6}\s+(.+)$/);
|
|
1758
|
+
if (heading) {
|
|
1759
|
+
current = category(heading[1] ?? "");
|
|
1760
|
+
continue;
|
|
1761
|
+
}
|
|
1762
|
+
const item = line.match(/^\s*[-*]\s+(?:\[[ xX]\]\s+)?(.+?)\s*$/);
|
|
1763
|
+
if (!item?.[1]?.trim()) continue;
|
|
1764
|
+
const value = item[1].trim();
|
|
1765
|
+
const id2 = `L-${createHash("sha256").update(`${origin}|${current}|${value}`).digest("hex").slice(0, 12)}`;
|
|
1766
|
+
if (!records.some((record3) => record3.id === id2)) records.push({ id: id2, source: origin, category: current, text: value, status: "proposed", recordedAt });
|
|
1767
|
+
}
|
|
1768
|
+
return records;
|
|
1769
|
+
};
|
|
1770
|
+
|
|
1771
|
+
// src/kernel/status.ts
|
|
1772
|
+
var required7 = (value, label) => {
|
|
1773
|
+
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1774
|
+
};
|
|
1775
|
+
var createStatusSnapshot = (input) => {
|
|
1776
|
+
const sourceRevision = required7(input.sourceRevision, "sourceRevision");
|
|
1777
|
+
if (!Number.isFinite(Date.parse(input.generatedAt))) fail("generatedAt must be a valid timestamp.", "INVALID_INPUT");
|
|
1778
|
+
if (!Array.isArray(input.blocks)) fail("blocks must be an array.", "INVALID_INPUT");
|
|
1779
|
+
const blocks = input.blocks.map((block2, index2) => {
|
|
1780
|
+
if (typeof block2 !== "object" || block2 === null || Array.isArray(block2)) fail(`blocks[${index2}] must be an object.`, "INVALID_INPUT");
|
|
1781
|
+
const value = block2;
|
|
1782
|
+
if (!(typeof value.id === "string" && value.id.trim())) fail(`blocks[${index2}].id is required.`, "INVALID_INPUT");
|
|
1783
|
+
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");
|
|
1784
|
+
return { ...value, id: value.id.trim() };
|
|
1785
|
+
}).sort((left, right) => left.id.localeCompare(right.id));
|
|
1786
|
+
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");
|
|
1787
|
+
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") } : {} };
|
|
1788
|
+
return { ...body2, digest: hashJson(body2) };
|
|
1789
|
+
};
|
|
1790
|
+
var validateStatusSnapshot = (value) => {
|
|
1791
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) fail("status snapshot must be an object.", "INVALID_INPUT");
|
|
1792
|
+
const raw = value;
|
|
1793
|
+
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 } : {} });
|
|
1794
|
+
if (raw.schemaVersion !== 1 || raw.digest !== snapshot.digest) fail("status snapshot digest or schemaVersion is invalid.", "HARNESS_ERROR");
|
|
1795
|
+
return snapshot;
|
|
1126
1796
|
};
|
|
1127
1797
|
var EVIDENCE_BUNDLE_SCHEMA_VERSION = 1;
|
|
1128
1798
|
var body = (bundle) => {
|
|
@@ -1150,7 +1820,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
|
|
|
1150
1820
|
const loaded = loadConfig(configPath);
|
|
1151
1821
|
const run = requireRun2(runId ? readJson(join(loaded.stateDir, "runs", runId, "run.json")) : loadLatestRun(loaded.stateDir));
|
|
1152
1822
|
const reconciliation = await reconcileRun({ configPath, runId: run.runId });
|
|
1153
|
-
const
|
|
1823
|
+
const digest4 = run.verificationDigest ?? fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
|
|
1154
1824
|
if (reconciliation.state !== "COMPLETE") fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
|
|
1155
1825
|
const eventLog = new FileEventStore(loaded.stateDir);
|
|
1156
1826
|
eventLog.read(run.runId);
|
|
@@ -1165,7 +1835,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
|
|
|
1165
1835
|
return bundleFile(loaded.stateDir, path);
|
|
1166
1836
|
});
|
|
1167
1837
|
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:
|
|
1838
|
+
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: digest4, eventLog: eventVerification, files };
|
|
1169
1839
|
const payloadHash = sha256(JSON.stringify(unsigned));
|
|
1170
1840
|
const publicKeyPem = createPublicKey(privateKey).export({ type: "spki", format: "pem" }).toString();
|
|
1171
1841
|
const bundle = { ...unsigned, payloadHash, signature: { algorithm: "ed25519", keyId, publicKeyPem, signatureBase64: sign(null, Buffer.from(payloadHash), privateKey).toString("base64") } };
|
|
@@ -1233,14 +1903,63 @@ var decisionArgs = (first, second) => {
|
|
|
1233
1903
|
const decisions = /* @__PURE__ */ new Set(["approved", "approve", "yes", "ok", "rejected", "reject", "no"]);
|
|
1234
1904
|
return decisions.has(first) ? { decision: first, ...second ? { runId: second } : {} } : { decision: second ?? "", runId: first };
|
|
1235
1905
|
};
|
|
1906
|
+
var readJsonInput = (path, label) => {
|
|
1907
|
+
try {
|
|
1908
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
1909
|
+
} catch (error) {
|
|
1910
|
+
return fail(`Invalid ${label} JSON: ${error instanceof Error ? error.message : String(error)}`, "INVALID_INPUT");
|
|
1911
|
+
}
|
|
1912
|
+
};
|
|
1236
1913
|
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("
|
|
1914
|
+
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
1915
|
var context = program.command("context").description("Resolve portable, provenance-bearing context snapshots.");
|
|
1239
1916
|
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
1917
|
if (command.provider !== "doc-bridge") fail(`Unsupported context provider: ${command.provider}`, "INVALID_INPUT");
|
|
1241
1918
|
const loaded = loadConfig(options().config);
|
|
1242
1919
|
print(await createDocBridgeContextProvider({ root: loaded.root, indexPath: command.index }).resolve({ query, ...command.scope?.length ? { scope: command.scope } : {} }));
|
|
1243
1920
|
});
|
|
1921
|
+
var discovery = program.command("discovery").description("Assess a structured discovery result before implementation.");
|
|
1922
|
+
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"))));
|
|
1923
|
+
var wip = program.command("wip").description("Assess deterministic WIP admission before starting work.");
|
|
1924
|
+
wip.command("assess <input>").description("Emit an admission decision from WIP ledger JSON.").action((input) => print(assessWip(readJsonInput(input, "WIP input"))));
|
|
1925
|
+
var experiment = program.command("experiment").description("Select a runtime only from a controlled, comparable experiment.");
|
|
1926
|
+
experiment.command("select <input>").description("Select the eligible runtime from experiment JSON.").action((input) => print(selectRuntime(readJsonInput(input, "experiment input"))));
|
|
1927
|
+
var delivery = program.command("delivery").description("Assess deterministic G2\u2013G5 gates and prepare idempotent PR handoff.");
|
|
1928
|
+
delivery.command("preflight <input>").action((input) => print(assessPreflight(readJsonInput(input, "preflight input"))));
|
|
1929
|
+
delivery.command("pr <input>").action((input) => print(composePullRequest(readJsonInput(input, "PR input"))));
|
|
1930
|
+
delivery.command("integration <input>").action((input) => print(assessIntegration(readJsonInput(input, "integration input"))));
|
|
1931
|
+
delivery.command("production <input>").action((input) => print(assessProduction(readJsonInput(input, "production input"))));
|
|
1932
|
+
delivery.command("acceptance <input>").action((input) => print(assessAcceptance(readJsonInput(input, "acceptance input"))));
|
|
1933
|
+
delivery.command("cleanup <input>").action((input) => print(assessWorktreeCleanup(readJsonInput(input, "cleanup input"))));
|
|
1934
|
+
program.command("pilot <input>").description("Freeze and assess a ten-issue pilot cohort.").action((input) => print(assessPilot(readJsonInput(input, "pilot input"))));
|
|
1935
|
+
var cycle = program.command("cycle").description("Run the five-step improvement cycle with explicit adjustment and bounded repetition.");
|
|
1936
|
+
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"))));
|
|
1937
|
+
var block = program.command("block").description("Validate and assess a portable execution block manifest.");
|
|
1938
|
+
block.command("validate <input>").action((input) => print(validateBlockManifest(readJsonInput(input, "block manifest"))));
|
|
1939
|
+
block.command("assess <input>").option("--completed <ids...>", "completed dependency IDs").action((input, command) => print(assessBlock(readJsonInput(input, "block manifest"), command.completed ?? [])));
|
|
1940
|
+
var preflight = program.command("preflight").description("Plan safe, file-scoped validation before commit.");
|
|
1941
|
+
preflight.command("files <input>").action((input) => print(planFilePreflight(readJsonInput(input, "changed files"))));
|
|
1942
|
+
var status = program.command("snapshot <input>").description("Create or validate a deterministic status snapshot.");
|
|
1943
|
+
status.action((input) => print(createStatusSnapshot(readJsonInput(input, "status input"))));
|
|
1944
|
+
status.command("validate <input>").action((input) => print(validateStatusSnapshot(readJsonInput(input, "status snapshot"))));
|
|
1945
|
+
var learning = program.command("learning").description("Parse retrospectives into proposed learnings.");
|
|
1946
|
+
learning.command("parse <input>").requiredOption("--source <source>").action((input, command) => print(parseRetro(readFileSync(input, "utf8"), command.source)));
|
|
1947
|
+
var coordination = program.command("coordination").description("Manage idempotent issue/worktree claims and dispatch records.");
|
|
1948
|
+
coordination.command("claim <input>").action((input) => {
|
|
1949
|
+
const loaded = loadConfig(options().config);
|
|
1950
|
+
print(createDispatchLedger(loaded.stateDir).claim(readJsonInput(input, "coordination identity")));
|
|
1951
|
+
});
|
|
1952
|
+
var artifacts = program.command("artifacts").description("Inspect versioned, provenance-bound run artifacts.");
|
|
1953
|
+
artifacts.command("inspect <path>").description("Validate and print one artifact as JSON or Markdown.").action((path) => {
|
|
1954
|
+
const artifact = readArtifactFile(path);
|
|
1955
|
+
print(options().json ? artifact : renderArtifactMarkdown(artifact));
|
|
1956
|
+
});
|
|
1957
|
+
artifacts.command("list [run-id]").description("List artifacts for the latest or selected run.").action((runId) => {
|
|
1958
|
+
const loaded = loadConfig(options().config);
|
|
1959
|
+
const run = runId ? { runId } : loadLatestRun(loaded.stateDir);
|
|
1960
|
+
print(new FileArtifactStore(loaded.stateDir).list(run?.runId ?? fail("No verification run exists.", "NO_RUN")));
|
|
1961
|
+
});
|
|
1962
|
+
artifacts.command("schema").description("Print the artifact schema version.").action(() => print({ schemaVersion: ARTIFACT_SCHEMA_VERSION, types: ["plan", "finding", "decision", "repair", "blocker", "approval", "phase"] }));
|
|
1244
1963
|
program.command("start").description("Move a planned run into implementation.").action(() => print(startRun(loadConfig(options().config))));
|
|
1245
1964
|
program.command("verify").description("Execute every configured check and record evidence.").action(async () => print(await verifyRun({ configPath: options().config })));
|
|
1246
1965
|
program.command("run").description("Alias for verify, compatible with the common protocol.").action(async () => print(await verifyRun({ configPath: options().config })));
|
|
@@ -1287,9 +2006,9 @@ var benchmark = program.command("benchmark").description("Aggregate reproducible
|
|
|
1287
2006
|
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
2007
|
const manifest = command.manifest ?? cliCommand.parent?.opts().manifest;
|
|
1289
2008
|
const manifestPath = manifest ?? fail("baseline requires --manifest <path>.", "INVALID_INPUT");
|
|
1290
|
-
const
|
|
2009
|
+
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
2010
|
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 } }));
|
|
2011
|
+
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
2012
|
});
|
|
1294
2013
|
program.command("clean").description("Remove only configured task-owned temporary artifacts.").action(() => print(cleanTaskArtifacts(options().config)));
|
|
1295
2014
|
process.on("SIGINT", () => {
|