@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +33 -32
  2. package/CONTRIBUTING.md +60 -12
  3. package/MANIFESTO.md +23 -0
  4. package/README.md +276 -144
  5. package/capabilities/public-surface.json +668 -0
  6. package/compatibility/manifest.json +17 -0
  7. package/compatibility/migration.md +10 -0
  8. package/compatibility/report.json +23 -0
  9. package/compatibility/report.md +22 -0
  10. package/compatibility/rollback.md +8 -0
  11. package/dist/cli.js +958 -239
  12. package/dist/cli.js.map +1 -1
  13. package/dist/index.d.ts +1338 -122
  14. package/dist/index.js +2273 -353
  15. package/dist/index.js.map +1 -1
  16. package/docs/ADR-0025-portable-orchestration-controls.md +27 -0
  17. package/docs/ADR-0026-kernel-adapters-boundary.md +82 -0
  18. package/docs/GETTING-STARTED.md +18 -0
  19. package/docs/MODULE-BOUNDARIES.md +143 -0
  20. package/docs/ORGANIZATION.md +46 -0
  21. package/docs/TROUBLESHOOTING.md +24 -0
  22. package/examples/minimum-profile.mjs +27 -0
  23. package/package.json +52 -34
  24. package/release/manifest.json +14 -0
  25. package/release/notes.md +10 -0
  26. package/release/qualification.json +14 -0
  27. package/docs/ADR-0025-ci-dogfood.md +0 -22
  28. package/docs/ADR-0026-ci-evidence-artifact.md +0 -22
  29. package/docs/ADR-0027-portable-evidence.md +0 -19
  30. package/docs/ADR-0028-effective-metrics.md +0 -20
  31. package/docs/ADR-0029-honest-ci-preparation.md +0 -20
  32. package/docs/ADR-0030-agentskit-os-benchmark-bridge.md +0 -20
  33. package/docs/ADR-0031-real-provider-baseline.md +0 -18
  34. package/docs/ADR-0032-harness-equivalent-benchmark.md +0 -25
  35. package/docs/ADR-0033-portable-agent-gate.md +0 -25
  36. package/docs/ADR-0034-measurement-quality-gates.md +0 -25
  37. package/docs/ADR-0035-reproducible-benchmark-samples.md +0 -20
  38. package/docs/ADR-0036-comparable-baseline-samples.md +0 -20
  39. package/docs/ADR-0037-replicated-baseline-collection.md +0 -27
  40. package/docs/ADR-0038-end-to-end-benchmark-boundary.md +0 -28
  41. package/docs/ADR-0039-artifact-and-protocol-metrics.md +0 -39
  42. package/docs/ADR-0040-benchmark-corpus-surfaces.md +0 -32
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import { resolve, dirname, join, relative, sep } from 'path';
2
- import { randomUUID, createPrivateKey, createPublicKey, sign, verify, createHash } from 'crypto';
3
- import { existsSync, mkdirSync, openSync, writeSync, appendFileSync, closeSync, unlinkSync, readFileSync, writeFileSync, mkdtempSync, renameSync, rmSync, lstatSync, readdirSync } from 'fs';
1
+ import { resolve, dirname, join, relative, basename, extname, sep } from 'path';
2
+ import { createHash, randomUUID, createPrivateKey, createPublicKey, sign, verify } from 'crypto';
3
+ import { existsSync, mkdirSync, openSync, writeSync, appendFileSync, closeSync, unlinkSync, readFileSync, writeFileSync, readdirSync, mkdtempSync, renameSync, rmSync } from 'fs';
4
4
  import { execFile, spawn } from 'child_process';
5
5
  import { promisify } from 'util';
6
- import { tmpdir } from 'os';
6
+ import { cpus, loadavg, freemem, totalmem, tmpdir } from 'os';
7
7
 
8
- // src/constants.ts
8
+ // src/kernel/constants.ts
9
9
  var STATES = [
10
10
  "CLARIFYING",
11
11
  "PLANNED",
@@ -23,7 +23,7 @@ var LEGAL_TRANSITIONS = {
23
23
  CLARIFYING: ["PLANNED", "BLOCKED", "CANCELLED"],
24
24
  PLANNED: ["IMPLEMENTING", "CLARIFYING", "STALE", "CANCELLED"],
25
25
  IMPLEMENTING: ["VERIFYING", "CLARIFYING", "STALE", "CANCELLED"],
26
- VERIFYING: ["AWAITING_HUMAN_APPROVAL", "BLOCKED", "STALE", "CANCELLED"],
26
+ VERIFYING: ["AWAITING_HUMAN_APPROVAL", "COMPLETE", "BLOCKED", "STALE", "CANCELLED"],
27
27
  AWAITING_HUMAN_APPROVAL: ["AWAITING_AUTHORIZATION", "COMPLETE", "BLOCKED", "IMPLEMENTING", "STALE", "CANCELLED"],
28
28
  AWAITING_AUTHORIZATION: ["COMPLETE", "BLOCKED", "IMPLEMENTING", "STALE", "CANCELLED"],
29
29
  COMPLETE: ["STALE", "SUPERSEDED"],
@@ -35,7 +35,8 @@ var LEGAL_TRANSITIONS = {
35
35
  var REAL_CATEGORIES = /* @__PURE__ */ new Set(["endpoint", "database", "cli", "mcp", "ui"]);
36
36
  var DECISIONS = /* @__PURE__ */ new Set(["approved", "approve", "yes", "ok", "rejected", "reject", "no"]);
37
37
 
38
- // src/errors.ts
38
+ // src/kernel/errors.ts
39
+ var HARNESS_ERROR_CODES = ["HARNESS_ERROR", "INVALID_CONFIG", "INVALID_INPUT", "INVALID_STATE", "POLICY_BLOCKED", "CLARIFYING", "STALE", "WORKTREE_DIRTY", "ACTIVE_RUN", "NO_RUN", "HUMAN_APPROVAL_REQUIRED", "GIT_REQUIRED"];
39
40
  var HarnessError = class extends Error {
40
41
  code;
41
42
  constructor(message, code = "HARNESS_ERROR") {
@@ -48,7 +49,41 @@ var fail = (message, code = "HARNESS_ERROR") => {
48
49
  throw new HarnessError(message, code);
49
50
  };
50
51
 
51
- // src/profiles.ts
52
+ // src/kernel/error-policy.ts
53
+ var HARNESS_ERROR_CATALOG = {
54
+ HARNESS_ERROR: { disposition: "escalate", retryable: false },
55
+ INVALID_CONFIG: { disposition: "block", retryable: false },
56
+ INVALID_INPUT: { disposition: "block", retryable: false },
57
+ INVALID_STATE: { disposition: "block", retryable: false },
58
+ POLICY_BLOCKED: { disposition: "block", retryable: false },
59
+ CLARIFYING: { disposition: "block", retryable: false },
60
+ STALE: { disposition: "block", retryable: false },
61
+ WORKTREE_DIRTY: { disposition: "block", retryable: false },
62
+ ACTIVE_RUN: { disposition: "retry", retryable: true },
63
+ NO_RUN: { disposition: "block", retryable: false },
64
+ HUMAN_APPROVAL_REQUIRED: { disposition: "block", retryable: false },
65
+ GIT_REQUIRED: { disposition: "block", retryable: false }
66
+ };
67
+ var nonEmpty = (value, label) => {
68
+ if (typeof value !== "string" || !value.trim()) throw new HarnessError(`${label} is required.`, "INVALID_INPUT");
69
+ return value.trim();
70
+ };
71
+ var classifyHarnessError = (error) => {
72
+ const code = error instanceof HarnessError ? error.code : "HARNESS_ERROR";
73
+ const descriptor2 = HARNESS_ERROR_CATALOG[code];
74
+ return { code, ...descriptor2, message: error instanceof Error ? error.message : String(error) };
75
+ };
76
+ var validateHarnessErrorClassification = (value) => {
77
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new HarnessError("Error classification must be an object.", "INVALID_INPUT");
78
+ const candidate = value;
79
+ const code = candidate["code"];
80
+ if (typeof code !== "string" || !HARNESS_ERROR_CODES.includes(code)) throw new HarnessError("Error classification code is invalid.", "INVALID_INPUT");
81
+ const expected = HARNESS_ERROR_CATALOG[code];
82
+ if (candidate["disposition"] !== expected.disposition || candidate["retryable"] !== expected.retryable) throw new HarnessError(`Error classification for ${code} is inconsistent.`, "INVALID_INPUT");
83
+ return { code, disposition: expected.disposition, retryable: expected.retryable, message: nonEmpty(candidate["message"], "Error classification message") };
84
+ };
85
+
86
+ // src/profiles/index.ts
52
87
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
53
88
  var record = (value, label) => {
54
89
  if (!isRecord(value)) fail(`${label} must be an object.`, "INVALID_CONFIG");
@@ -61,9 +96,11 @@ var id = (value, label) => {
61
96
  var parents = (value, label) => value === void 0 ? [] : Array.isArray(value) ? value.map((item, index2) => id(item, `${label}[${index2}]`)) : [id(value, label)];
62
97
  var merge = (base, overlay) => {
63
98
  const result = { ...base };
64
- for (const key of ["surfaces", "budget", "cleanup"]) {
99
+ for (const key of ["surfaces", "budget", "cleanup", "runtime", "verification"]) {
65
100
  if (overlay[key] !== void 0) result[key] = { ...isRecord(result[key]) ? result[key] : {}, ...record(overlay[key], `profile.${key}`) };
66
101
  }
102
+ if (overlay["autonomy"] !== void 0) result["autonomy"] = overlay["autonomy"];
103
+ if (Array.isArray(overlay["checks"])) result["checks"] = overlay["checks"];
67
104
  if (overlay["checkOverrides"] !== void 0) {
68
105
  if (!Array.isArray(overlay["checkOverrides"])) fail("profile.checkOverrides must be an array.", "INVALID_CONFIG");
69
106
  const checks = Array.isArray(result["checks"]) ? [...result["checks"]] : [];
@@ -136,7 +173,7 @@ var cleanConfiguredArtifacts = (loaded) => {
136
173
  };
137
174
  var fileContents = (path) => readFileSync(path, "utf8");
138
175
 
139
- // src/types.ts
176
+ // src/kernel/types.ts
140
177
  var SURFACE_NAMES = ["logic", "endpoint", "database", "cli", "mcp", "ui", "docs"];
141
178
  var CHECK_CATEGORIES = ["build", "test", "lint", ...SURFACE_NAMES, "custom"];
142
179
  var RUN_STATES = [
@@ -153,7 +190,7 @@ var RUN_STATES = [
153
190
  "SUPERSEDED"
154
191
  ];
155
192
 
156
- // src/config.ts
193
+ // src/execution/config.ts
157
194
  var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
158
195
  var stringValue = (value, label) => {
159
196
  if (typeof value !== "string") return fail(`${label} is required.`, "INVALID_CONFIG");
@@ -181,18 +218,20 @@ var surface = (value, name) => {
181
218
  var parseCheck = (value, index2) => {
182
219
  const record3 = asRecord(value, `checks[${index2}]`);
183
220
  const id2 = stringValue(record3["id"], `checks[${index2}].id`);
184
- const category = stringValue(record3["category"], `checks[${index2}].category`);
185
- if (!CHECK_CATEGORIES.includes(category)) fail(`checks[${index2}].category is invalid.`, "INVALID_CONFIG");
221
+ const category2 = stringValue(record3["category"], `checks[${index2}].category`);
222
+ if (!CHECK_CATEGORIES.includes(category2)) fail(`checks[${index2}].category is invalid.`, "INVALID_CONFIG");
186
223
  const command = stringValue(record3["command"], `checks[${index2}].command`);
187
- if (REAL_CATEGORIES.has(category) && record3["execution"] !== "real") fail(`checks[${index2}] must declare execution: real.`, "INVALID_CONFIG");
224
+ if (REAL_CATEGORIES.has(category2) && record3["execution"] !== "real") fail(`checks[${index2}] must declare execution: real.`, "INVALID_CONFIG");
188
225
  if (record3["evidence"] !== "structured") fail(`checks[${index2}] must declare evidence: structured.`, "INVALID_CONFIG");
189
226
  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");
190
227
  const capabilities = Array.isArray(record3["capabilities"]) ? record3["capabilities"].filter((item) => typeof item === "string") : void 0;
191
- if (category === "ui" && !capabilities?.includes("real-browser")) fail(`checks[${index2}] must declare real-browser.`, "INVALID_CONFIG");
192
- if (category === "ui" && !capabilities?.includes("screenshot")) fail(`checks[${index2}] must declare screenshot.`, "INVALID_CONFIG");
228
+ if (category2 === "ui" && !capabilities?.includes("real-browser")) fail(`checks[${index2}] must declare real-browser.`, "INVALID_CONFIG");
229
+ if (category2 === "ui" && !capabilities?.includes("screenshot")) fail(`checks[${index2}] must declare screenshot.`, "INVALID_CONFIG");
193
230
  if (record3["required"] !== void 0 && typeof record3["required"] !== "boolean") fail(`checks[${index2}].required must be boolean.`, "INVALID_CONFIG");
231
+ if (record3["required"] === false && typeof record3["reason"] !== "string") fail(`checks[${index2}].reason is required when required is false.`, "INVALID_CONFIG");
194
232
  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");
195
- return { id: id2, category, command, required: record3["required"] !== false, timeoutMs: typeof record3["timeoutMs"] === "number" ? record3["timeoutMs"] : 12e4, ...record3["execution"] === "real" ? { execution: "real" } : {}, ...capabilities ? { capabilities } : {}, evidence: "structured" };
233
+ const dependsOn = record3["dependsOn"] === void 0 ? void 0 : stringArray(record3["dependsOn"], `checks[${index2}].dependsOn`);
234
+ 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" };
196
235
  };
197
236
  var parseOutcome = (value, index2, checks) => {
198
237
  const record3 = asRecord(value, `contract.outcomes[${index2}]`);
@@ -206,10 +245,28 @@ var validateConfig = (rawValue) => {
206
245
  const raw = resolveProfile(asRecord(rawValue, "verification config"));
207
246
  if (raw["schemaVersion"] !== 1) fail("verification config schemaVersion must be 1.", "INVALID_CONFIG");
208
247
  const project = stringValue(raw["project"], "verification config project");
248
+ const runtimeRaw = asRecord(raw["runtime"] ?? { kind: "process" }, "runtime");
249
+ if (runtimeRaw["kind"] !== "process" && runtimeRaw["kind"] !== "docker") fail("runtime.kind must be process or docker.", "INVALID_CONFIG");
250
+ const runtime = { kind: runtimeRaw["kind"] };
251
+ if (raw["autonomy"] !== void 0 && raw["autonomy"] !== "controlled" && raw["autonomy"] !== "yolo") fail("autonomy must be controlled or yolo.", "INVALID_CONFIG");
252
+ const autonomy = raw["autonomy"] ?? "controlled";
209
253
  const contractRaw = asRecord(raw["contract"], "contract");
210
254
  const rawChecks = raw["checks"];
211
255
  const checks = Array.isArray(rawChecks) ? rawChecks.map(parseCheck) : fail("checks must be a non-empty array.", "INVALID_CONFIG");
212
256
  if (!checks.length || new Set(checks.map((check) => check.id)).size !== checks.length) fail("check ids must be unique.", "INVALID_CONFIG");
257
+ const checkIds = new Set(checks.map((check) => check.id));
258
+ 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");
259
+ const visiting = /* @__PURE__ */ new Set();
260
+ const visited = /* @__PURE__ */ new Set();
261
+ const visit = (id2) => {
262
+ if (visiting.has(id2)) fail(`check dependency cycle includes ${id2}.`, "INVALID_CONFIG");
263
+ if (visited.has(id2)) return;
264
+ visiting.add(id2);
265
+ for (const dependency of checks.find((check) => check.id === id2)?.dependsOn ?? []) visit(dependency);
266
+ visiting.delete(id2);
267
+ visited.add(id2);
268
+ };
269
+ for (const check of checks) visit(check.id);
213
270
  const scopeRaw = asRecord(contractRaw["scope"], "contract.scope");
214
271
  const scope = { inScope: stringArray(scopeRaw["inScope"], "contract.scope.inScope"), outOfScope: stringArray(scopeRaw["outOfScope"], "contract.scope.outOfScope") };
215
272
  const ambiguities = stringArray(contractRaw["ambiguities"], "contract.ambiguities");
@@ -226,13 +283,15 @@ var validateConfig = (rawValue) => {
226
283
  if (trackingRaw["required"] !== true && typeof trackingRaw["reason"] !== "string") fail("tracking.reason is required when tracking is disabled.", "INVALID_CONFIG");
227
284
  const budgetRaw = raw["budget"] === void 0 ? void 0 : asRecord(raw["budget"], "budget");
228
285
  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");
286
+ const verificationRaw = raw["verification"] === void 0 ? void 0 : asRecord(raw["verification"], "verification");
287
+ 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");
229
288
  const cleanupRaw = raw["cleanup"] === void 0 ? void 0 : asRecord(raw["cleanup"], "cleanup");
230
289
  const cleanup = cleanupRaw ? { roots: cleanupRaw["roots"] === void 0 ? void 0 : stringArray(cleanupRaw["roots"], "cleanup.roots") } : void 0;
231
290
  const benchmarkRaw = raw["benchmark"] === void 0 ? void 0 : asRecord(raw["benchmark"], "benchmark");
232
291
  const benchmark = 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;
233
292
  const contract = { intent: stringValue(contractRaw["intent"], "contract.intent"), scope, ambiguities, outcomes };
234
293
  const tracking = { required: trackingRaw["required"] === true, ...typeof trackingRaw["target"] === "string" ? { target: trackingRaw["target"] } : {}, ...typeof trackingRaw["reason"] === "string" ? { reason: trackingRaw["reason"] } : {} };
235
- 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 } : {}, ...benchmark ? { benchmark } : {} };
294
+ 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 } : {}, ...benchmark ? { benchmark } : {} };
236
295
  };
237
296
  var loadConfig = (configPath = ".codex/verification.json") => {
238
297
  const absolute = resolve(configPath);
@@ -240,12 +299,12 @@ var loadConfig = (configPath = ".codex/verification.json") => {
240
299
  const rawRecord = asRecord(raw, "verification config");
241
300
  const root = resolve(dirname(absolute), typeof rawRecord["root"] === "string" ? rawRecord["root"] : ".");
242
301
  const stateDir = resolve(root, typeof rawRecord["stateDir"] === "string" ? rawRecord["stateDir"] : ".codex/verification");
243
- if (!pathInside(root, stateDir)) fail("stateDir must be inside the project root.", "INVALID_CONFIG");
302
+ if (stateDir === root) fail("stateDir must be separate from the project root.", "INVALID_CONFIG");
244
303
  const config = validateConfig(raw);
245
304
  return { absolute, root, stateDir, config, configHash: hashJson(config) };
246
305
  };
247
306
 
248
- // src/state-machine.ts
307
+ // src/kernel/state-machine.ts
249
308
  var transition = (run, to, reason, actor = "harness") => {
250
309
  if (!STATES.includes(to)) fail(`Unknown state ${to}.`, "INVALID_STATE");
251
310
  if (run.state !== to && !LEGAL_TRANSITIONS[run.state].some((state) => state === to)) fail(`Illegal transition ${run.state} -> ${to}.`, "INVALID_STATE");
@@ -260,8 +319,63 @@ var approvedDecision = (decision) => {
260
319
  return ["approved", "approve", "yes", "ok"].includes(decision);
261
320
  };
262
321
  var HARNESS_EVENT_SCHEMA_VERSION = 1;
322
+ var HARNESS_EVENT_ENVELOPE_SCHEMA_VERSION = 2;
263
323
  var EVENT_LOG_GENESIS = "GENESIS";
264
- 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"];
324
+ 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"];
325
+ var envelopeId = (value, label) => {
326
+ if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value)) fail(`${label} is invalid.`, "INVALID_INPUT");
327
+ return value;
328
+ };
329
+ var envelopeText = (value, label) => {
330
+ if (typeof value !== "string") fail(`${label} is required.`, "INVALID_INPUT");
331
+ const result = value.trim();
332
+ if (!result) fail(`${label} is required.`, "INVALID_INPUT");
333
+ return result;
334
+ };
335
+ var envelopeDigest = (value, label) => {
336
+ const result = envelopeText(value, label);
337
+ if (!/^[a-f0-9]{64}$/.test(result)) fail(`${label} must be a lowercase SHA-256 digest.`, "INVALID_INPUT");
338
+ return result;
339
+ };
340
+ var envelopeProvenance = (value) => {
341
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("Event provenance must be an object.", "INVALID_INPUT");
342
+ const candidate = value;
343
+ return {
344
+ source: envelopeText(candidate["source"], "Event provenance source"),
345
+ component: envelopeText(candidate["component"], "Event provenance component"),
346
+ version: envelopeText(candidate["version"], "Event provenance version"),
347
+ ...candidate["actor"] === void 0 ? {} : { actor: envelopeText(candidate["actor"], "Event provenance actor") }
348
+ };
349
+ };
350
+ var validateHarnessEventEnvelope = (value) => {
351
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("Event envelope must be an object.", "INVALID_INPUT");
352
+ const candidate = value;
353
+ if (candidate["schemaVersion"] !== HARNESS_EVENT_ENVELOPE_SCHEMA_VERSION) fail("Event envelope schemaVersion is invalid.", "INVALID_INPUT");
354
+ const payload = candidate["payload"];
355
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) fail("Event envelope payload must be an object.", "INVALID_INPUT");
356
+ const issueRef = candidate["issueRef"] === void 0 ? void 0 : envelopeText(candidate["issueRef"], "Event issueRef");
357
+ const occurredAt = envelopeText(candidate["occurredAt"], "Event occurredAt");
358
+ if (!Number.isFinite(Date.parse(occurredAt))) fail("Event occurredAt must be a valid timestamp.", "INVALID_INPUT");
359
+ return {
360
+ eventId: envelopeId(candidate["eventId"], "Event eventId"),
361
+ eventType: envelopeId(candidate["eventType"], "Event eventType"),
362
+ schemaVersion: HARNESS_EVENT_ENVELOPE_SCHEMA_VERSION,
363
+ occurredAt,
364
+ runId: envelopeText(candidate["runId"], "Event runId"),
365
+ ...issueRef === void 0 ? {} : { issueRef },
366
+ sourceRevision: envelopeText(candidate["sourceRevision"], "Event sourceRevision"),
367
+ correlationId: envelopeId(candidate["correlationId"], "Event correlationId"),
368
+ payload,
369
+ idempotencyKey: envelopeDigest(candidate["idempotencyKey"], "Event idempotencyKey"),
370
+ provenance: envelopeProvenance(candidate["provenance"])
371
+ };
372
+ };
373
+ var createHarnessEventEnvelope = (input) => {
374
+ if (typeof input !== "object" || input === null || Array.isArray(input)) fail("Event envelope input must be an object.", "INVALID_INPUT");
375
+ const identity = { eventType: input.eventType, runId: input.runId, ...input.issueRef === void 0 ? {} : { issueRef: input.issueRef }, sourceRevision: input.sourceRevision, correlationId: input.correlationId, payload: input.payload, provenance: input.provenance };
376
+ const candidate = { ...input, schemaVersion: HARNESS_EVENT_ENVELOPE_SCHEMA_VERSION, idempotencyKey: input.idempotencyKey ?? hashJson(identity) };
377
+ return validateHarnessEventEnvelope(candidate);
378
+ };
265
379
  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"]);
266
380
  var eventPath = (stateDir, runId) => join(stateDir, "runs", runId, "events.ndjson");
267
381
  var lockPath = (stateDir, runId) => `${eventPath(stateDir, runId)}.lock`;
@@ -282,14 +396,16 @@ var readLock = (stateDir, runId) => {
282
396
  var isEventType = (value) => typeof value === "string" && HARNESS_EVENT_TYPES.includes(value);
283
397
  var digest = (value) => /^[a-f0-9]{64}$/.test(value);
284
398
  var eventBody = (event) => {
285
- const { eventHash: _eventHash, ...body2 } = event;
286
- return body2;
399
+ const { eventHash: _eventHash, ...body3 } = event;
400
+ return body3;
287
401
  };
288
402
  var eventDigest = (event) => sha256(JSON.stringify(eventBody(event)));
289
403
  var parseEvent = (value, expectedSequence) => {
290
404
  if (typeof value !== "object" || value === null || Array.isArray(value)) fail("Event log contains a non-object record.", "HARNESS_ERROR");
291
405
  const record3 = value;
292
- 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") fail("Event log is invalid or out of order.", "HARNESS_ERROR");
406
+ const context = record3["correlation"];
407
+ const validContext = context === void 0 || typeof context === "object" && context !== null && !Array.isArray(context) && Object.entries(context).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 context["operationId"] === "string";
408
+ 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");
293
409
  const hasPreviousHash = record3["previousHash"] !== void 0;
294
410
  const hasEventHash = record3["eventHash"] !== void 0;
295
411
  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");
@@ -318,6 +434,7 @@ var FileEventStore = class {
318
434
  if (!isEventType(event.type)) fail("Event type is invalid.", "INVALID_INPUT");
319
435
  if (SESSION_EVENT_TYPES.has(event.type) && (!event.sessionId || !event.sessionId.trim())) fail("Session events require a sessionId.", "INVALID_INPUT");
320
436
  if (event.sessionId !== void 0 && !event.sessionId.trim()) fail("Event sessionId cannot be empty.", "INVALID_INPUT");
437
+ if (event.correlation !== void 0 && (!event.correlation.operationId || !event.correlation.operationId.trim())) fail("Event correlation operationId is required.", "INVALID_INPUT");
321
438
  const path = eventPath(this.stateDir, event.runId);
322
439
  const lock = lockPath(this.stateDir, event.runId);
323
440
  mkdirSync(join(this.stateDir, "runs", event.runId), { recursive: true });
@@ -332,8 +449,8 @@ var FileEventStore = class {
332
449
  try {
333
450
  const events = this.readUnlocked(event.runId);
334
451
  const previous = events.at(-1);
335
- const body2 = { schemaVersion: HARNESS_EVENT_SCHEMA_VERSION, sequence: events.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 } : events.length ? {} : { previousHash: EVENT_LOG_GENESIS }, type: event.type, payload: event.payload };
336
- const record3 = events.length && !previous?.eventHash ? body2 : { ...body2, eventHash: eventDigest(body2) };
452
+ const body3 = { schemaVersion: HARNESS_EVENT_SCHEMA_VERSION, sequence: events.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 } : events.length ? {} : { previousHash: EVENT_LOG_GENESIS }, type: event.type, payload: event.payload };
453
+ const record3 = events.length && !previous?.eventHash ? body3 : { ...body3, eventHash: eventDigest(body3) };
337
454
  appendFileSync(path, `${JSON.stringify(record3)}
338
455
  `, "utf8");
339
456
  return record3;
@@ -387,16 +504,27 @@ var recoverEventLogLock = ({ stateDir, runId, actor, maxAgeMs = 3e5 }) => {
387
504
  return fail("Event log lock owner is still alive.", "HARNESS_ERROR");
388
505
  };
389
506
 
390
- // src/plugins.ts
507
+ // src/kernel/plugins.ts
391
508
  var HARNESS_PLUGIN_API_VERSION = 1;
392
509
  var createPluginSlot = (id2) => {
393
- if (!id2.trim()) fail("Plugin slot id is required.", "INVALID_INPUT");
394
- return { id: id2 };
510
+ if (typeof id2 !== "string" || !id2.trim()) fail("Plugin slot id is required.", "INVALID_INPUT");
511
+ return { id: id2.trim() };
395
512
  };
396
513
  var validId = (value, label) => {
397
- if (!value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
514
+ if (typeof value !== "string") fail(`${label} is required.`, "INVALID_INPUT");
515
+ const result = value.trim();
516
+ if (!result) fail(`${label} is required.`, "INVALID_INPUT");
517
+ return result;
518
+ };
519
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
520
+ var validEventType = (value) => {
521
+ if (typeof value !== "string" || !HARNESS_EVENT_TYPES.includes(value)) fail("Plugin event type is invalid.", "INVALID_INPUT");
398
522
  return value;
399
523
  };
524
+ var validSlot = (value) => {
525
+ if (!isRecord3(value)) fail("Plugin slot must be an object.", "INVALID_INPUT");
526
+ return { id: validId(value["id"], "Plugin slot id") };
527
+ };
400
528
  var createPluginRegistry = () => {
401
529
  const plugins = /* @__PURE__ */ new Map();
402
530
  const contributions = /* @__PURE__ */ new Map();
@@ -443,11 +571,20 @@ var createPluginRegistry = () => {
443
571
  register(plugin) {
444
572
  ensureOpen();
445
573
  if (mounted) fail("Plugins cannot be registered after mount.", "HARNESS_ERROR");
446
- validId(plugin.id, "Plugin id");
447
- validId(plugin.version, "Plugin version");
448
- if (plugin.apiVersion !== HARNESS_PLUGIN_API_VERSION) fail(`Unsupported plugin API version: ${String(plugin.apiVersion)}.`, "INVALID_INPUT");
449
- if (plugins.has(plugin.id)) fail(`Plugin already registered: ${plugin.id}.`, "INVALID_INPUT");
450
- plugins.set(plugin.id, plugin);
574
+ if (!isRecord3(plugin)) fail("Plugin must be an object.", "INVALID_INPUT");
575
+ const candidate = plugin;
576
+ const id2 = validId(candidate.id, "Plugin id");
577
+ validId(candidate.version, "Plugin version");
578
+ if (candidate.apiVersion !== HARNESS_PLUGIN_API_VERSION) fail(`Unsupported plugin API version: ${String(candidate.apiVersion)}.`, "INVALID_INPUT");
579
+ if (typeof candidate.apply !== "function") fail("Plugin apply must be a function.", "INVALID_INPUT");
580
+ let requires;
581
+ if (candidate.requires !== void 0) {
582
+ if (!Array.isArray(candidate.requires)) fail("Plugin requires must be an array.", "INVALID_INPUT");
583
+ requires = candidate.requires.map((dependency, index2) => validId(dependency, `Plugin dependency[${index2}]`));
584
+ if (new Set(requires).size !== requires.length) fail("Plugin dependencies must be unique.", "INVALID_INPUT");
585
+ }
586
+ if (plugins.has(id2)) fail(`Plugin already registered: ${id2}.`, "INVALID_INPUT");
587
+ plugins.set(id2, { ...candidate, id: id2, ...requires === void 0 ? {} : { requires } });
451
588
  },
452
589
  mount() {
453
590
  ensureOpen();
@@ -456,14 +593,17 @@ var createPluginRegistry = () => {
456
593
  for (const plugin of order()) {
457
594
  const context = {
458
595
  apiVersion: HARNESS_PLUGIN_API_VERSION,
459
- register: (slot, id2, value) => registerContribution(plugin.id, slot, validId(id2, "Plugin contribution id"), value),
596
+ register: (slot, id2, value) => registerContribution(plugin.id, validSlot(slot), validId(id2, "Plugin contribution id"), value),
460
597
  effect: (disposer) => {
598
+ if (typeof disposer !== "function") fail("Plugin disposer must be a function.", "INVALID_INPUT");
461
599
  cleanups.push(disposer);
462
600
  },
463
601
  on: (type, listener) => {
464
- const handlers = listeners.get(type) ?? /* @__PURE__ */ new Set();
602
+ const eventType = validEventType(type);
603
+ if (typeof listener !== "function") fail("Plugin event listener must be a function.", "INVALID_INPUT");
604
+ const handlers = listeners.get(eventType) ?? /* @__PURE__ */ new Set();
465
605
  handlers.add(listener);
466
- listeners.set(type, handlers);
606
+ listeners.set(eventType, handlers);
467
607
  const disposer = () => {
468
608
  handlers.delete(listener);
469
609
  };
@@ -486,14 +626,16 @@ var createPluginRegistry = () => {
486
626
  },
487
627
  on(type, listener) {
488
628
  ensureOpen();
489
- const handlers = listeners.get(type) ?? /* @__PURE__ */ new Set();
629
+ const eventType = validEventType(type);
630
+ if (typeof listener !== "function") fail("Plugin event listener must be a function.", "INVALID_INPUT");
631
+ const handlers = listeners.get(eventType) ?? /* @__PURE__ */ new Set();
490
632
  handlers.add(listener);
491
- listeners.set(type, handlers);
633
+ listeners.set(eventType, handlers);
492
634
  return () => {
493
635
  handlers.delete(listener);
494
636
  };
495
637
  },
496
- contributions: (slot) => [...contributions.get(slot.id)?.values() ?? []],
638
+ contributions: (slot) => [...contributions.get(validSlot(slot).id)?.values() ?? []],
497
639
  dispose() {
498
640
  if (disposed) return;
499
641
  let firstError;
@@ -514,7 +656,25 @@ var createPluginRegistry = () => {
514
656
  return registry;
515
657
  };
516
658
 
517
- // src/context.ts
659
+ // src/kernel/adapter-contract.ts
660
+ var ASSURANCE_LEVELS = ["unverified", "contract-tested", "runtime-attested"];
661
+ var nonNegative = (value, label) => {
662
+ if (!Number.isFinite(value) || value < 0) return fail(`${label} must be a non-negative number.`, "INVALID_INPUT");
663
+ return value;
664
+ };
665
+ var validateAdapterMetadata = (value) => {
666
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return fail("Adapter metadata must be an object.", "INVALID_INPUT");
667
+ const candidate = value;
668
+ if (!ASSURANCE_LEVELS.includes(candidate["assurance"])) return fail("Adapter assurance is invalid.", "INVALID_INPUT");
669
+ if (typeof candidate["telemetry"] !== "object" || candidate["telemetry"] === null || Array.isArray(candidate["telemetry"])) return fail("Adapter telemetry must be an object.", "INVALID_INPUT");
670
+ const telemetry = candidate["telemetry"];
671
+ if (telemetry["status"] !== "measured" && telemetry["status"] !== "unknown") return fail("Adapter telemetry status is invalid.", "INVALID_INPUT");
672
+ for (const key of ["durationMs", "inputTokens", "outputTokens", "totalTokens", "cacheHits", "cacheMisses", "memoryReads", "memoryWrites", "memoryRelevantHits", "memoryStaleHits", "contextReferences", "contextCostTokens", "externalMutations"]) if (telemetry[key] !== void 0) nonNegative(telemetry[key], `Adapter telemetry ${key}`);
673
+ return value;
674
+ };
675
+ var unknownTelemetry = () => ({ status: "unknown" });
676
+
677
+ // src/context/index.ts
518
678
  var hashContextSnapshot = ({ providerId, query, references, sourceHash: sourceHash2 }) => hashJson({ providerId, query, references, sourceHash: sourceHash2 });
519
679
  var hashContextSnapshots = (snapshots) => hashJson(snapshots.map(({ providerId, query, references, sourceHash: sourceHash2, snapshotHash }) => ({ providerId, query, references, sourceHash: sourceHash2, snapshotHash })));
520
680
  var record2 = (value, label) => {
@@ -537,17 +697,23 @@ var validateContextSnapshot = (value, index2 = 0) => {
537
697
  uri: requiredString(rawReference["uri"], `context snapshot ${index2}.references[${referenceIndex}].uri`),
538
698
  ...typeof rawReference["title"] === "string" ? { title: rawReference["title"] } : {},
539
699
  ...typeof rawReference["version"] === "string" ? { version: rawReference["version"] } : {},
540
- ...typeof rawReference["contentHash"] === "string" ? { contentHash: rawReference["contentHash"] } : {}
700
+ ...typeof rawReference["contentHash"] === "string" ? { contentHash: rawReference["contentHash"] } : {},
701
+ ...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")
541
702
  };
542
703
  });
543
704
  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");
705
+ const assurance = raw["assurance"] === void 0 ? void 0 : ASSURANCE_LEVELS.includes(raw["assurance"]) ? raw["assurance"] : fail(`context snapshot ${index2}.assurance is invalid.`, "INVALID_INPUT");
706
+ const telemetry = raw["telemetry"] === void 0 ? void 0 : record2(raw["telemetry"], `context snapshot ${index2}.telemetry`);
707
+ if (telemetry && telemetry["status"] !== "measured" && telemetry["status"] !== "unknown") fail(`context snapshot ${index2}.telemetry.status is invalid.`, "INVALID_INPUT");
544
708
  const snapshot = {
545
709
  providerId: requiredString(raw["providerId"], `context snapshot ${index2}.providerId`),
546
710
  query: { query: requiredString(rawQuery["query"], `context snapshot ${index2}.query.query`), ...scope ? { scope } : {}, ...typeof rawQuery["sourceRevision"] === "string" ? { sourceRevision: rawQuery["sourceRevision"] } : {} },
547
711
  references,
548
712
  sourceHash: requiredString(raw["sourceHash"], `context snapshot ${index2}.sourceHash`),
549
713
  snapshotHash: requiredString(raw["snapshotHash"], `context snapshot ${index2}.snapshotHash`),
550
- resolvedAt: requiredString(raw["resolvedAt"], `context snapshot ${index2}.resolvedAt`)
714
+ resolvedAt: requiredString(raw["resolvedAt"], `context snapshot ${index2}.resolvedAt`),
715
+ ...assurance === void 0 ? {} : { assurance },
716
+ ...telemetry === void 0 ? {} : { telemetry }
551
717
  };
552
718
  if (snapshot.snapshotHash !== hashContextSnapshot(snapshot)) fail(`context snapshot ${index2}.snapshotHash does not match its contents.`, "INVALID_INPUT");
553
719
  return snapshot;
@@ -559,7 +725,7 @@ var readContextSnapshots = (path) => {
559
725
  var validateContextSnapshots = (snapshots) => snapshots.map((snapshot, index2) => validateContextSnapshot(snapshot, index2));
560
726
  var CONTEXT_PROVIDER_SLOT = createPluginSlot("context.provider");
561
727
 
562
- // src/runs.ts
728
+ // src/execution/runs.ts
563
729
  var now = () => (/* @__PURE__ */ new Date()).toISOString();
564
730
  var newRunId = () => `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
565
731
  var saveRun2 = (stateDir, run) => {
@@ -578,8 +744,7 @@ var saveRun2 = (stateDir, run) => {
578
744
  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 } });
579
745
  }
580
746
  };
581
- var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized, contextSnapshots = [], planner = "human" }) => {
582
- const contractHash = hashJson(loaded.config.contract);
747
+ var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized, contextSnapshots = [] }) => {
583
748
  const run = {
584
749
  type: "agentskit-harness-run",
585
750
  schemaVersion: 1,
@@ -587,17 +752,18 @@ var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized,
587
752
  project: loaded.config.project,
588
753
  state: "PLANNED",
589
754
  configHash: loaded.configHash,
590
- contractHash,
755
+ contractHash: hashJson(loaded.config.contract),
591
756
  sourceRevision: baseline.revision,
592
757
  sourceStatusHash: baseline.statusHash,
593
758
  baseline,
594
- ...planner === "human" ? { contractApproval: { actor: "human", at: now(), contractHash } } : { contractPreparation: { actor: "ci", at: now(), contractHash } },
595
- checks: loaded.config.checks.map(({ id: id2, category }) => ({ id: id2, category, status: "pending" })),
759
+ autonomy: loaded.config.autonomy,
760
+ contractApproval: { actor: "human", at: now(), contractHash: hashJson(loaded.config.contract) },
761
+ checks: loaded.config.checks.map(({ id: id2, category: category2 }) => ({ id: id2, category: category2, status: "pending" })),
596
762
  contextSnapshots,
597
763
  ...contextSnapshots.length ? { contextHash: hashContextSnapshots(contextSnapshots) } : {},
598
764
  ...loaded.config.benchmark ? { benchmark: loaded.config.benchmark } : {},
599
765
  outcomes: loaded.config.contract.outcomes.map(({ id: id2, statement, checks }) => ({ id: id2, statement, checks, status: "pending" })),
600
- transitions: [{ from: null, to: "PLANNED", at: now(), actor: planner }],
766
+ transitions: [{ from: null, to: "PLANNED", at: now(), actor: "human" }],
601
767
  evidenceReferences: [],
602
768
  ...supersedes ? { supersedes } : {},
603
769
  ...dirtyBaselineAuthorized ? { dirtyBaselineAuthorized: true } : {}
@@ -616,8 +782,8 @@ var parseStructuredEvidence = (stdout) => {
616
782
  }
617
783
  return null;
618
784
  };
619
- var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
620
- var viewportValid = (viewport) => typeof viewport === "string" || isRecord3(viewport) && typeof viewport["width"] === "number" && viewport["width"] > 0 && typeof viewport["height"] === "number" && viewport["height"] > 0;
785
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
786
+ var viewportValid = (viewport) => typeof viewport === "string" || isRecord4(viewport) && typeof viewport["width"] === "number" && viewport["width"] > 0 && typeof viewport["height"] === "number" && viewport["height"] > 0;
621
787
  var validateEvidence = (root, check, evidence, outcomeIds) => {
622
788
  if (!evidence || evidence.status !== "passed") return ["structured evidence did not pass"];
623
789
  if (!Array.isArray(evidence.criteria) || !evidence.criteria.every((id2) => typeof id2 === "string") || outcomeIds.some((id2) => !evidence.criteria.includes(id2))) return [`evidence must map criteria: ${outcomeIds.join(", ")}`];
@@ -628,7 +794,7 @@ var validateEvidence = (root, check, evidence, outcomeIds) => {
628
794
  }
629
795
  const artifacts = Array.isArray(evidence.artifacts) ? evidence.artifacts : [];
630
796
  for (const artifactValue of artifacts) {
631
- if (!isRecord3(artifactValue) || typeof artifactValue["path"] !== "string" || typeof artifactValue["sha256"] !== "string") {
797
+ if (!isRecord4(artifactValue) || typeof artifactValue["path"] !== "string" || typeof artifactValue["sha256"] !== "string") {
632
798
  failures.push("artifact requires string path and sha256");
633
799
  continue;
634
800
  }
@@ -650,25 +816,100 @@ var git = async (root, args) => {
650
816
  };
651
817
  var sourceSnapshot = async (root, stateDir) => {
652
818
  const revision = await git(root, ["rev-parse", "HEAD"]);
819
+ if (!revision) fail("Current-source evidence requires a Git repository with a committed HEAD.", "GIT_REQUIRED");
653
820
  const stateRelative = relative(root, stateDir).replaceAll("\\", "/");
654
821
  const pathspec = ["--", "."];
655
822
  if (stateRelative && stateRelative !== ".." && !stateRelative.startsWith("../")) pathspec.push(`:(exclude)${stateRelative}`);
656
823
  const status = await git(root, ["status", "--porcelain=v1", "--untracked-files=all", ...pathspec]);
657
824
  const diff = await git(root, ["diff", "--no-ext-diff", "--binary", "HEAD", ...pathspec]);
658
825
  const untrackedPaths = (await git(root, ["ls-files", "--others", "--exclude-standard", "-z"])).split("\0").filter(Boolean).filter((path) => !stateRelative || path !== stateRelative && !path.startsWith(`${stateRelative}/`));
659
- const untracked = untrackedPaths.flatMap((path) => {
660
- const absolute = resolve(root, path);
661
- try {
662
- return lstatSync(absolute).isFile() ? [{ path, hash: sha256(readFileSync(absolute)) }] : [];
663
- } catch {
664
- return [];
665
- }
666
- });
826
+ const untracked = untrackedPaths.map((path) => ({ path, hash: sha256(readFileSync(resolve(root, path))) }));
667
827
  const fingerprint = { revision, status, diff, untracked };
668
- return { revision: revision || `content:${hashJson(fingerprint)}`, status, statusHash: hashJson(fingerprint) };
828
+ return { revision, status, statusHash: hashJson(fingerprint) };
829
+ };
830
+ var thresholds = (value = {}) => {
831
+ const result = { warningPercent: value.warningPercent ?? 75, criticalPercent: value.criticalPercent ?? 90 };
832
+ 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");
833
+ return result;
834
+ };
835
+ var linuxSwap = () => {
836
+ if (process.platform !== "linux" || !existsSync("/proc/meminfo")) return void 0;
837
+ const values = Object.fromEntries(readFileSync("/proc/meminfo", "utf8").split(/\r?\n/).flatMap((line) => {
838
+ const match = line.match(/^(SwapTotal|SwapFree):\s+(\d+)\s+kB$/);
839
+ return match ? [[match[1], Number(match[2])]] : [];
840
+ }));
841
+ if (!values["SwapTotal"]) return void 0;
842
+ return Number(((1 - (values["SwapFree"] ?? 0) / values["SwapTotal"]) * 100).toFixed(2));
843
+ };
844
+ var sampleMachine = () => {
845
+ const cpus$1 = Math.max(1, cpus().length);
846
+ const load1 = Math.max(0, loadavg()[0] ?? 0);
847
+ const memory = Math.max(0, Math.min(100, (1 - freemem() / Math.max(1, totalmem())) * 100));
848
+ const swapUsedPercent = linuxSwap();
849
+ return {
850
+ at: (/* @__PURE__ */ new Date()).toISOString(),
851
+ cpus: cpus$1,
852
+ load1: Number(load1.toFixed(4)),
853
+ load1PerCpuPercent: Number(Math.min(100, load1 / cpus$1 * 100).toFixed(2)),
854
+ memoryUsedPercent: Number(memory.toFixed(2)),
855
+ rssBytes: process.memoryUsage().rss,
856
+ ...swapUsedPercent === void 0 ? {} : { swapUsedPercent }
857
+ };
858
+ };
859
+ var summarizeMachine = (samples, sampleIntervalMs = 5e3, limits = {}) => {
860
+ const limit = thresholds(limits);
861
+ const load = samples.map((sample) => sample.load1PerCpuPercent);
862
+ const memory = samples.map((sample) => sample.memoryUsedPercent);
863
+ const rss = samples.map((sample) => sample.rssBytes);
864
+ return {
865
+ sampleIntervalMs,
866
+ samples,
867
+ peakLoad1PerCpuPercent: Number(Math.max(...load, 0).toFixed(2)),
868
+ peakMemoryUsedPercent: Number(Math.max(...memory, 0).toFixed(2)),
869
+ peakRssBytes: Math.max(...rss, 0),
870
+ pressureEvents: samples.filter((sample) => sample.load1PerCpuPercent >= limit.criticalPercent || sample.memoryUsedPercent >= limit.criticalPercent || (sample.swapUsedPercent ?? 0) >= limit.criticalPercent || sample.memoryPressure === "critical").length,
871
+ throttleEvents: 0,
872
+ minimumEffectiveConcurrency: 0
873
+ };
874
+ };
875
+ var adaptiveConcurrency = (configured, sample, limits = {}) => {
876
+ if (!Number.isInteger(configured) || configured < 1) throw new Error("configured concurrency must be a positive integer.");
877
+ const limit = thresholds(limits);
878
+ const critical = sample.load1PerCpuPercent >= limit.criticalPercent || sample.memoryUsedPercent >= limit.criticalPercent || (sample.swapUsedPercent ?? 0) >= limit.criticalPercent || sample.memoryPressure === "critical";
879
+ const warning = sample.load1PerCpuPercent >= limit.warningPercent || sample.memoryUsedPercent >= limit.warningPercent || (sample.swapUsedPercent ?? 0) >= limit.warningPercent || sample.memoryPressure === "warning";
880
+ if (critical) return 1;
881
+ if (warning) return Math.min(configured, 2);
882
+ return configured;
883
+ };
884
+ var createMachineMonitor = (sampleIntervalMs = 5e3, options = {}) => {
885
+ const sampler = options.sample ?? sampleMachine;
886
+ const limits = thresholds(options.thresholds);
887
+ const samples = [sampler()];
888
+ let throttleEvents = 0;
889
+ const effectiveConcurrency = [];
890
+ const record3 = () => {
891
+ const sample = sampler();
892
+ samples.push(sample);
893
+ return sample;
894
+ };
895
+ const timer = setInterval(record3, sampleIntervalMs);
896
+ timer.unref();
897
+ return {
898
+ sample: record3,
899
+ observeConcurrency: (value) => effectiveConcurrency.push(value),
900
+ markThrottle: () => {
901
+ throttleEvents += 1;
902
+ },
903
+ stop: () => {
904
+ clearInterval(timer);
905
+ record3();
906
+ const summary = summarizeMachine(samples, sampleIntervalMs, limits);
907
+ return { ...summary, throttleEvents, minimumEffectiveConcurrency: effectiveConcurrency.length ? Math.min(...effectiveConcurrency) : 0 };
908
+ }
909
+ };
669
910
  };
670
911
 
671
- // src/verification.ts
912
+ // src/execution/verification.ts
672
913
  var now2 = () => (/* @__PURE__ */ new Date()).toISOString();
673
914
  var requireRun = (run) => run ?? fail("No verification run exists.", "NO_RUN");
674
915
  var verificationProjection = (run) => ({ checks: run.checks, outcomes: run.outcomes, metrics: run.metrics });
@@ -694,6 +935,12 @@ var runCommand = (check, cwd) => new Promise((resolveResult) => {
694
935
  resolveResult({ exitCode: exitCode ?? 1, timedOut, stdout, stderr, durationMs: Date.now() - started });
695
936
  });
696
937
  });
938
+ var executeCheck = async (check, cwd, checkDir, outcomes) => {
939
+ const result = await runCommand(check, cwd);
940
+ const evidence = parseStructuredEvidence(result.stdout);
941
+ 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"];
942
+ 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 };
943
+ };
697
944
  var currentBinding = async (loaded) => ({ source: await sourceSnapshot(loaded.root, loaded.stateDir), configHash: loaded.configHash });
698
945
  var staleRun = (loaded, run, reason) => {
699
946
  const stale = transition(run, "STALE", reason);
@@ -706,11 +953,8 @@ var isFresh = async (loaded, run) => {
706
953
  return current.configHash === run.configHash && current.source.revision === run.sourceRevision && current.source.statusHash === run.sourceStatusHash;
707
954
  };
708
955
  var planRun = async ({ configPath, decision, actor = "human", allowDirty = false, contextSnapshots = [] }) => {
709
- const automatedPreparation = actor === "ci" && decision === "prepared";
710
- if (!automatedPreparation) {
711
- assertHuman(actor);
712
- if (!approvedDecision(decision)) fail("Contract was not approved.", "CLARIFYING");
713
- }
956
+ assertHuman(actor);
957
+ if (!approvedDecision(decision)) fail("Contract was not approved.", "CLARIFYING");
714
958
  const loaded = loadConfig(configPath);
715
959
  if (loaded.config.contract.ambiguities.length) fail(`Unresolved ambiguities remain: ${loaded.config.contract.ambiguities.join(" | ")}`, "CLARIFYING");
716
960
  const validatedContextSnapshots = validateContextSnapshots(contextSnapshots);
@@ -722,7 +966,7 @@ ${meaningful.join("\n")}
722
966
  Use --allow-dirty only with explicit human authorization.`, "WORKTREE_DIRTY");
723
967
  const previous = loadLatestRun(loaded.stateDir);
724
968
  if (previous && !["STALE", "SUPERSEDED"].includes(previous.state)) fail(`An active run already exists: ${previous.runId} (${previous.state}).`, "ACTIVE_RUN");
725
- return createRun({ loaded, baseline, supersedes: previous?.runId, dirtyBaselineAuthorized: allowDirty, contextSnapshots: validatedContextSnapshots, planner: automatedPreparation ? "ci" : "human" });
969
+ return createRun({ loaded, baseline, supersedes: previous?.runId, dirtyBaselineAuthorized: allowDirty, contextSnapshots: validatedContextSnapshots });
726
970
  };
727
971
  var startRun = (loaded) => {
728
972
  const run = requireRun(loadLatestRun(loaded.stateDir));
@@ -742,42 +986,81 @@ var cancelRun = async ({ configPath, runId, reason = "Run cancelled by a human."
742
986
  };
743
987
  var verifyRun = async ({ configPath }) => {
744
988
  const loaded = loadConfig(configPath);
989
+ const machineMonitor = createMachineMonitor();
745
990
  const run = requireRun(loadLatestRun(loaded.stateDir));
746
991
  if (!["IMPLEMENTING", "VERIFYING"].includes(run.state)) {
747
992
  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.");
748
993
  fail(`Cannot verify from ${run.state}.`, "INVALID_STATE");
749
994
  }
750
995
  if (run.configHash !== loaded.configHash) staleRun(loaded, run, "Run is stale because the verification contract changed.");
751
- const binding = await currentBinding(loaded);
752
- let current = { ...transition(run, "VERIFYING", "Verification started.", "agent"), sourceRevision: binding.source.revision, sourceStatusHash: binding.source.statusHash };
996
+ const binding2 = await currentBinding(loaded);
997
+ let current = { ...transition(run, "VERIFYING", "Verification started.", "agent"), sourceRevision: binding2.source.revision, sourceStatusHash: binding2.source.statusHash };
753
998
  saveRun2(loaded.stateDir, current);
754
999
  const checkDir = join(loaded.stateDir, "runs", current.runId, "checks");
755
1000
  mkdirSync(checkDir, { recursive: true });
756
1001
  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)]));
757
1002
  let totalDurationMs = 0;
758
- for (const check of loaded.config.checks) {
759
- const result = await runCommand(check, loaded.root);
760
- totalDurationMs += result.durationMs;
761
- const stdoutPath = join(checkDir, `${check.id}.stdout`);
762
- const stderrPath = join(checkDir, `${check.id}.stderr`);
763
- writeFileSync(stdoutPath, result.stdout, "utf8");
764
- writeFileSync(stderrPath, result.stderr, "utf8");
765
- const evidence = parseStructuredEvidence(result.stdout);
766
- const failures = result.exitCode === 0 && !result.timedOut && evidence ? validateEvidence(loaded.root, check, evidence, outcomesByCheck.get(check.id) ?? []) : [result.timedOut ? "check timed out" : result.exitCode !== 0 ? `exit code ${result.exitCode}` : "missing final structured evidence"];
767
- const nextCheck = { id: check.id, category: check.category, status: failures.length ? "failed" : "passed", exitCode: result.exitCode, durationMs: result.durationMs, ...evidence ? { evidence } : {}, ...failures.length ? { failures } : {} };
768
- current = { ...current, evidenceReferences: [...current.evidenceReferences, { checkId: check.id, stdout: relative(loaded.stateDir, stdoutPath), stderr: relative(loaded.stateDir, stderrPath) }], checks: current.checks.map((item) => item.id === check.id ? nextCheck : item) };
769
- saveRun2(loaded.stateDir, current);
1003
+ let activeChecks = 0;
1004
+ let observedPeakConcurrency = 0;
1005
+ const verificationStarted = Date.now();
1006
+ const maxConcurrency = loaded.config.verification?.maxConcurrency ?? 1;
1007
+ const requiredChecks = loaded.config.checks.filter((check) => check.required);
1008
+ for (const check of loaded.config.checks.filter((item) => !item.required)) {
1009
+ const nextCheck = { id: check.id, category: check.category, status: "not-applicable", failures: [check.reason ?? "Not required by the selected profile."] };
1010
+ current = { ...current, checks: current.checks.map((item) => item.id === check.id ? nextCheck : item) };
1011
+ }
1012
+ const remaining = new Set(requiredChecks.map((check) => check.id));
1013
+ const completed = new Set(loaded.config.checks.filter((check) => !check.required).map((check) => check.id));
1014
+ while (remaining.size) {
1015
+ const ready = requiredChecks.filter((check) => remaining.has(check.id) && (check.dependsOn ?? []).every((dependency) => completed.has(dependency)));
1016
+ if (!ready.length) fail("Check dependency graph contains an unknown dependency or cycle.", "INVALID_CONFIG");
1017
+ const buildChecks = ready.filter((check) => check.category === "build");
1018
+ const queue = buildChecks.length ? buildChecks : ready;
1019
+ let offset = 0;
1020
+ while (offset < queue.length) {
1021
+ const effectiveConcurrency = buildChecks.length ? 1 : adaptiveConcurrency(maxConcurrency, machineMonitor.sample());
1022
+ machineMonitor.observeConcurrency(effectiveConcurrency);
1023
+ if (effectiveConcurrency < maxConcurrency) machineMonitor.markThrottle();
1024
+ const batch = queue.slice(offset, offset + effectiveConcurrency);
1025
+ const executed = await Promise.all(batch.map(async (check) => {
1026
+ activeChecks += 1;
1027
+ observedPeakConcurrency = Math.max(observedPeakConcurrency, activeChecks);
1028
+ try {
1029
+ return await executeCheck(check, loaded.root, checkDir, outcomesByCheck.get(check.id) ?? []);
1030
+ } finally {
1031
+ activeChecks -= 1;
1032
+ }
1033
+ }));
1034
+ for (const item of executed) {
1035
+ totalDurationMs += item.durationMs;
1036
+ const stdoutPath = join(checkDir, `${item.check.id}.stdout`);
1037
+ const stderrPath = join(checkDir, `${item.check.id}.stderr`);
1038
+ writeFileSync(stdoutPath, item.stdout, "utf8");
1039
+ writeFileSync(stderrPath, item.stderr, "utf8");
1040
+ 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) };
1041
+ }
1042
+ saveRun2(loaded.stateDir, current);
1043
+ for (const check of batch) {
1044
+ remaining.delete(check.id);
1045
+ completed.add(check.id);
1046
+ }
1047
+ offset += batch.length;
1048
+ }
770
1049
  }
771
1050
  const statuses = new Map(current.checks.map((check) => [check.id, check.status]));
772
1051
  const budgetExceeded = loaded.config.budget?.maxDurationMs !== void 0 && totalDurationMs > loaded.config.budget.maxDurationMs;
773
- const allPassed = loaded.config.checks.every((check) => statuses.get(check.id) === "passed") && !budgetExceeded;
774
- current = { ...current, outcomes: current.outcomes.map((outcome) => ({ ...outcome, status: outcome.checks.every((id2) => statuses.get(id2) === "passed") ? "passed" : "failed" })), metrics: { totalDurationMs, budgetExceeded } };
775
- const digest2 = verificationDigest(current);
776
- current = { ...current, verificationDigest: digest2 };
1052
+ const allPassed = loaded.config.checks.every((check) => !check.required || statuses.get(check.id) === "passed") && !budgetExceeded;
1053
+ current = { ...current, outcomes: current.outcomes.map((outcome) => {
1054
+ const required16 = outcome.checks.filter((id2) => loaded.config.checks.find((check) => check.id === id2)?.required);
1055
+ return { ...outcome, status: required16.length === 0 ? "not-applicable" : required16.every((id2) => statuses.get(id2) === "passed") ? "passed" : "failed" };
1056
+ }), metrics: { totalDurationMs, wallDurationMs: Date.now() - verificationStarted, peakConcurrency: observedPeakConcurrency, budgetExceeded, machine: machineMonitor.stop() } };
1057
+ const digest6 = verificationDigest(current);
1058
+ current = { ...current, verificationDigest: digest6 };
777
1059
  saveRun2(loaded.stateDir, current);
778
- new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest: digest2, checkCount: current.checks.length, outcomeCount: current.outcomes.length, totalDurationMs, budgetExceeded } });
779
- const nextState = allPassed ? "AWAITING_HUMAN_APPROVAL" : "BLOCKED";
780
- current = { ...transition(current, nextState, allPassed ? "All configured checks passed; human approval is required." : budgetExceeded ? "Verification budget was exceeded." : "A configured check failed or lacked structured evidence.", "harness") };
1060
+ new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest: digest6, checkCount: current.checks.length, outcomeCount: current.outcomes.length, totalDurationMs, budgetExceeded } });
1061
+ const automatic = allPassed && current.autonomy === "yolo" && !loaded.config.tracking.required && loaded.config.contract.ambiguities.length === 0;
1062
+ const nextState = allPassed ? automatic ? "COMPLETE" : "AWAITING_HUMAN_APPROVAL" : "BLOCKED";
1063
+ 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") };
781
1064
  saveRun2(loaded.stateDir, current);
782
1065
  setLatest(loaded.stateDir, current);
783
1066
  return current;
@@ -791,11 +1074,6 @@ var assertVerificationAttestation = (loaded, run) => {
791
1074
  const event = new FileEventStore(loaded.stateDir).read(run.runId).filter((item) => item.type === "verification.completed").at(-1);
792
1075
  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");
793
1076
  };
794
- var plannerForRetry = (loaded, run) => {
795
- if (run.contractPreparation) return "ci";
796
- if (!run.supersedes) return "human";
797
- return plannerForRetry(loaded, readRun(loaded.stateDir, run.supersedes));
798
- };
799
1077
  var recordDecision = (loaded, run, type, payload) => {
800
1078
  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 });
801
1079
  };
@@ -815,7 +1093,7 @@ var reconcileRun = async ({ configPath, runId }) => {
815
1093
  if (eventLog.status !== "verified") fail("Terminal run requires a verified event log.", "HARNESS_ERROR");
816
1094
  assertVerificationAttestation(loaded, run);
817
1095
  }
818
- if (run.state === "AWAITING_AUTHORIZATION" || run.state === "COMPLETE") {
1096
+ if ((run.state === "AWAITING_AUTHORIZATION" || run.state === "COMPLETE") && run.autonomy !== "yolo") {
819
1097
  const approval = events.filter((event) => event.type === "approval.recorded").at(-1) ?? fail("Terminal run is missing its human approval event.", "HARNESS_ERROR");
820
1098
  assertDecisionProjection(run, approval.payload, run.state === "COMPLETE" && !loaded.config.tracking.required ? "COMPLETE" : "AWAITING_AUTHORIZATION");
821
1099
  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");
@@ -877,13 +1155,77 @@ var retryRun = async ({ configPath }) => {
877
1155
  const baseline = await sourceSnapshot(loaded.root, loaded.stateDir);
878
1156
  const superseded = transition(previousRun, "SUPERSEDED", "Retry superseded the previous run.", "harness");
879
1157
  saveRun2(loaded.stateDir, superseded);
880
- const run = await createRun({ loaded, baseline, supersedes: previousRun.runId, dirtyBaselineAuthorized: previousRun.dirtyBaselineAuthorized, planner: plannerForRetry(loaded, previousRun) });
1158
+ const run = await createRun({ loaded, baseline, supersedes: previousRun.runId, dirtyBaselineAuthorized: previousRun.dirtyBaselineAuthorized });
881
1159
  const next = transition(run, "IMPLEMENTING", "Retry started after a previous attempt.", "agent");
882
1160
  saveRun2(loaded.stateDir, next);
883
1161
  setLatest(loaded.stateDir, next);
884
1162
  return next;
885
1163
  };
886
1164
  var cleanTaskArtifacts = (configPath) => cleanConfiguredArtifacts(loadConfig(configPath));
1165
+
1166
+ // src/kernel/capabilities.ts
1167
+ var CAPABILITY_MANIFEST_SCHEMA_VERSION = 1;
1168
+ var CAPABILITY_KINDS = ["kernel", "execution", "adapter", "composition"];
1169
+ var nonEmpty2 = (value, label) => {
1170
+ if (typeof value !== "string") fail(`${label} is required.`, "INVALID_INPUT");
1171
+ const result = value.trim();
1172
+ if (!result) fail(`${label} is required.`, "INVALID_INPUT");
1173
+ return result;
1174
+ };
1175
+ var digest2 = (value, label) => {
1176
+ const result = nonEmpty2(value, label);
1177
+ if (!/^[a-f0-9]{64}$/.test(result)) fail(`${label} must be a lowercase SHA-256 digest.`, "INVALID_INPUT");
1178
+ return result;
1179
+ };
1180
+ var stringList = (value, label) => {
1181
+ if (!Array.isArray(value)) fail(`${label} must be a non-empty string array.`, "INVALID_INPUT");
1182
+ if (!value.length) fail(`${label} must be a non-empty string array.`, "INVALID_INPUT");
1183
+ const items = value;
1184
+ const result = items.map((item, index2) => nonEmpty2(item, `${label}[${index2}]`));
1185
+ if (new Set(result).size !== result.length) fail(`${label} must not contain duplicates.`, "INVALID_INPUT");
1186
+ return result;
1187
+ };
1188
+ var descriptor = (value, index2) => {
1189
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail(`capabilities[${index2}] must be an object.`, "INVALID_INPUT");
1190
+ const candidate = value;
1191
+ const kind = candidate["kind"];
1192
+ if (typeof kind !== "string" || !CAPABILITY_KINDS.includes(kind)) fail(`capabilities[${index2}].kind is invalid.`, "INVALID_INPUT");
1193
+ const dependencies = candidate["dependencies"] === void 0 ? void 0 : stringList(candidate["dependencies"], `capabilities[${index2}].dependencies`);
1194
+ return {
1195
+ id: nonEmpty2(candidate["id"], `capabilities[${index2}].id`),
1196
+ version: nonEmpty2(candidate["version"], `capabilities[${index2}].version`),
1197
+ kind,
1198
+ entryPoint: nonEmpty2(candidate["entryPoint"], `capabilities[${index2}].entryPoint`),
1199
+ exports: stringList(candidate["exports"], `capabilities[${index2}].exports`),
1200
+ ...dependencies === void 0 ? {} : { dependencies }
1201
+ };
1202
+ };
1203
+ var manifestBody = (input) => ({
1204
+ type: "agentskit-harness-capability-manifest",
1205
+ schemaVersion: CAPABILITY_MANIFEST_SCHEMA_VERSION,
1206
+ package: nonEmpty2(input.package, "package"),
1207
+ packageVersion: nonEmpty2(input.packageVersion, "packageVersion"),
1208
+ entryPoint: nonEmpty2(input.entryPoint, "entryPoint"),
1209
+ sourceDigest: digest2(input.sourceDigest, "sourceDigest"),
1210
+ capabilities: (Array.isArray(input.capabilities) ? input.capabilities : fail("capabilities must be an array.", "INVALID_INPUT")).map(descriptor)
1211
+ });
1212
+ var createCapabilityManifest = (input) => {
1213
+ const body3 = manifestBody(input);
1214
+ if (!body3.capabilities.length) fail("capabilities must be non-empty.", "INVALID_INPUT");
1215
+ if (new Set(body3.capabilities.map((item) => item.id)).size !== body3.capabilities.length) fail("capability ids must be unique.", "INVALID_INPUT");
1216
+ return { ...body3, digest: hashJson(body3) };
1217
+ };
1218
+ var validateCapabilityManifest = (value) => {
1219
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("Capability manifest must be an object.", "INVALID_INPUT");
1220
+ const candidate = value;
1221
+ const capabilities = Array.isArray(candidate["capabilities"]) ? candidate["capabilities"] : fail("capabilities must be an array.", "INVALID_INPUT");
1222
+ const body3 = manifestBody({ package: nonEmpty2(candidate["package"], "package"), packageVersion: nonEmpty2(candidate["packageVersion"], "packageVersion"), entryPoint: nonEmpty2(candidate["entryPoint"], "entryPoint"), sourceDigest: digest2(candidate["sourceDigest"], "sourceDigest"), capabilities });
1223
+ if (new Set(body3.capabilities.map((item) => item.id)).size !== body3.capabilities.length) fail("capability ids must be unique.", "INVALID_INPUT");
1224
+ if (candidate["type"] !== body3.type || candidate["schemaVersion"] !== body3.schemaVersion) fail("Capability manifest type or schemaVersion is invalid.", "INVALID_INPUT");
1225
+ const manifestDigest = digest2(candidate["digest"], "digest");
1226
+ if (manifestDigest !== hashJson(body3)) fail("Capability manifest digest is invalid.", "INVALID_INPUT");
1227
+ return { ...body3, digest: manifestDigest };
1228
+ };
887
1229
  var index = (root, indexPath) => JSON.parse(readFileSync(resolve(root, indexPath), "utf8"));
888
1230
  var text = (entry) => [entry.id, entry.type, entry.title, entry.path, entry.description, entry.body, ...Array.isArray(entry.tags) ? entry.tags : []].filter((value) => typeof value === "string").join(" ").toLowerCase();
889
1231
  var sourceHash = (document) => typeof document.contentHash === "string" && document.contentHash.length > 0 ? document.contentHash : hashJson(document);
@@ -897,23 +1239,1425 @@ var createDocBridgeContextProvider = ({ root, indexPath = ".doc-bridge/index.jso
897
1239
  id: "doc-bridge",
898
1240
  version: "1.0.0",
899
1241
  resolve: async (query) => {
1242
+ const started = Date.now();
900
1243
  const document = index(root, indexPath);
901
1244
  const contentHash = sourceHash(document);
902
1245
  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) : [];
903
- 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 }] : []);
904
- return { providerId: "doc-bridge", query, references, sourceHash: contentHash, snapshotHash: hashContextSnapshot({ providerId: "doc-bridge", query, references, sourceHash: contentHash }), resolvedAt: (/* @__PURE__ */ new Date()).toISOString() };
1246
+ 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 }] : []);
1247
+ const telemetry = { status: "measured", durationMs: Date.now() - started, contextReferences: references.length, contextCostTokens: Math.max(1, Math.ceil(JSON.stringify(references).length / 4)) };
1248
+ 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 };
1249
+ }
1250
+ });
1251
+
1252
+ // src/kernel/discovery.ts
1253
+ var required = (value, label) => {
1254
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1255
+ return value.trim();
1256
+ };
1257
+ var unique = (values, label) => {
1258
+ if (new Set(values).size !== values.length) fail(`${label} must be unique.`, "INVALID_INPUT");
1259
+ };
1260
+ var validate = (input) => {
1261
+ required(input.issueId, "issueId");
1262
+ required(input.sourceRevision, "sourceRevision");
1263
+ required(input.contractHash, "contractHash");
1264
+ if (!Array.isArray(input.ambiguities)) fail("ambiguities must be an array.", "INVALID_INPUT");
1265
+ unique(input.ambiguities.map((item) => required(item.id, "ambiguity.id")), "ambiguity ids");
1266
+ const assumptions = /* @__PURE__ */ new Map();
1267
+ for (const assumption of input.approvedAssumptions ?? []) {
1268
+ const id2 = required(assumption.id, "assumption.id");
1269
+ if (assumptions.has(id2)) fail("assumption ids must be unique.", "INVALID_INPUT");
1270
+ assumptions.set(id2, { id: id2, policyId: required(assumption.policyId, "assumption.policyId"), resolution: required(assumption.resolution, "assumption.resolution") });
1271
+ }
1272
+ for (const ambiguity of input.ambiguities) {
1273
+ required(ambiguity.question, "ambiguity.question");
1274
+ if (typeof ambiguity.material !== "boolean") fail("ambiguity.material must be boolean.", "INVALID_INPUT");
1275
+ if (!Array.isArray(ambiguity.options) || ambiguity.options.length < 2 || ambiguity.options.length > 4) fail("ambiguity.options must contain 2 to 4 options.", "INVALID_INPUT");
1276
+ unique(ambiguity.options.map((option) => required(option.id, "option.id")), "option ids");
1277
+ for (const option of ambiguity.options) {
1278
+ required(option.summary, "option.summary");
1279
+ required(option.impact, "option.impact");
1280
+ }
1281
+ if (!ambiguity.options.some((option) => option.id === ambiguity.recommendedOptionId)) fail("recommendedOptionId must identify an option.", "INVALID_INPUT");
1282
+ if (!ambiguity.material && (!ambiguity.assumptionId || !assumptions.has(ambiguity.assumptionId))) fail("non-material ambiguity requires an approved assumption.", "INVALID_INPUT");
1283
+ }
1284
+ return { assumptions };
1285
+ };
1286
+ var digest3 = (result) => hashJson(result);
1287
+ var assessDiscovery = (input) => {
1288
+ const { assumptions } = validate(input);
1289
+ const human = input.ambiguities.filter((ambiguity) => ambiguity.material);
1290
+ const decisionLog = input.ambiguities.map((ambiguity) => {
1291
+ if (ambiguity.material) return { ambiguityId: ambiguity.id, kind: "human-decision-required", detail: `Recommendation: ${ambiguity.recommendedOptionId}.` };
1292
+ const assumption = assumptions.get(ambiguity.assumptionId);
1293
+ return { ambiguityId: ambiguity.id, kind: "approved-assumption", detail: assumption.resolution, policyId: assumption.policyId };
1294
+ });
1295
+ const base = {
1296
+ version: 1,
1297
+ issueId: input.issueId,
1298
+ sourceRevision: input.sourceRevision,
1299
+ contractHash: input.contractHash,
1300
+ ...input.contextHash ? { contextHash: input.contextHash } : {},
1301
+ status: human.length ? "awaiting-decision" : "ready",
1302
+ ...human.length ? { packet: {
1303
+ issueId: input.issueId,
1304
+ contractHash: input.contractHash,
1305
+ sourceRevision: input.sourceRevision,
1306
+ ...input.contextHash ? { contextHash: input.contextHash } : {},
1307
+ decisions: human.map((ambiguity) => ({ id: ambiguity.id, question: ambiguity.question, options: ambiguity.options, recommendedOptionId: ambiguity.recommendedOptionId }))
1308
+ } } : {},
1309
+ decisionLog
1310
+ };
1311
+ return { ...base, digest: digest3(base) };
1312
+ };
1313
+ var isDiscoveryCurrent = (result, current) => {
1314
+ const reasons = [];
1315
+ if (result.sourceRevision !== current.sourceRevision) reasons.push("source");
1316
+ if (result.contractHash !== current.contractHash) reasons.push("contract");
1317
+ if ((result.contextHash ?? "") !== (current.contextHash ?? "")) reasons.push("context");
1318
+ return { current: reasons.length === 0, reasons };
1319
+ };
1320
+
1321
+ // src/kernel/wip.ts
1322
+ var WIP_STATES = ["ready", "implementing", "blocked", "awaiting-decision", "awaiting-acceptance", "done", "cancelled"];
1323
+ var terminal = /* @__PURE__ */ new Set(["done", "cancelled"]);
1324
+ var required2 = (value, label) => {
1325
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1326
+ return value.trim();
1327
+ };
1328
+ var assessWip = ({ entries, candidate, maxInFlight = 3 }) => {
1329
+ if (!Array.isArray(entries)) fail("entries must be an array.", "INVALID_INPUT");
1330
+ if (!Number.isInteger(maxInFlight) || maxInFlight < 1) fail("maxInFlight must be a positive integer.", "INVALID_INPUT");
1331
+ const candidateId = required2(candidate.issueId, "candidate.issueId");
1332
+ if (candidate.kind !== "new" && candidate.kind !== "resume") fail("candidate.kind must be new or resume.", "INVALID_INPUT");
1333
+ const ids = /* @__PURE__ */ new Set();
1334
+ const counts = Object.fromEntries(WIP_STATES.map((state) => [state, 0]));
1335
+ for (const entry of entries) {
1336
+ const id2 = required2(entry.issueId, "entry.issueId");
1337
+ if (ids.has(id2)) fail("entry issueIds must be unique.", "INVALID_INPUT");
1338
+ ids.add(id2);
1339
+ if (!WIP_STATES.includes(entry.state)) fail(`Unknown WIP state: ${entry.state}.`, "INVALID_INPUT");
1340
+ counts[entry.state] += 1;
1341
+ }
1342
+ const inFlight = entries.filter((entry) => !terminal.has(entry.state));
1343
+ const existing = entries.find((entry) => entry.issueId === candidateId);
1344
+ if (candidate.kind === "resume") {
1345
+ if (!existing || terminal.has(existing.state)) return { decision: "hold", inFlight, counts, reason: "A resume requires an existing non-terminal issue." };
1346
+ return { decision: "admit", inFlight, counts, reason: "A resume keeps its existing WIP reservation and takes priority over new work." };
1347
+ }
1348
+ if (existing) return { decision: "hold", inFlight, counts, reason: "A new admission cannot reuse an existing issue id." };
1349
+ if (inFlight.length >= maxInFlight) return { decision: "hold", inFlight, counts, reason: `WIP limit ${maxInFlight} reached; blocked and awaiting-human work still count.` };
1350
+ return { decision: "admit", inFlight, counts, reason: `WIP slot available (${inFlight.length}/${maxInFlight}).` };
1351
+ };
1352
+
1353
+ // src/kernel/experiment.ts
1354
+ var required3 = (value, label) => {
1355
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1356
+ return value.trim();
1357
+ };
1358
+ var comparable = (candidate, baseline) => {
1359
+ for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) {
1360
+ if (candidate[key] !== baseline[key]) fail(`Candidates must share ${key}.`, "INVALID_INPUT");
1361
+ }
1362
+ };
1363
+ var selectRuntime = (candidates) => {
1364
+ if (!Array.isArray(candidates) || candidates.length < 2) fail("At least two runtime candidates are required.", "INVALID_INPUT");
1365
+ const names2 = /* @__PURE__ */ new Set();
1366
+ for (const candidate of candidates) {
1367
+ const runtime = required3(candidate.runtime, "candidate.runtime");
1368
+ if (names2.has(runtime)) fail("candidate.runtime values must be unique.", "INVALID_INPUT");
1369
+ names2.add(runtime);
1370
+ for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) required3(candidate[key], `candidate.${key}`);
1371
+ 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");
1372
+ comparable(candidate, candidates[0]);
1373
+ }
1374
+ const eligible = candidates.filter((candidate) => candidate.hardGatesPassed);
1375
+ if (!eligible.length) return { decision: "blocked", eligible, reason: "No runtime passed every hard gate." };
1376
+ 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];
1377
+ return { decision: "selected", selected, eligible, reason: "Selected by human minutes, duration, cost, then Orca tie-break." };
1378
+ };
1379
+
1380
+ // src/kernel/workflow.ts
1381
+ var validId2 = (id2) => {
1382
+ if (typeof id2 !== "string" || !id2.trim()) fail("Workflow node id must be non-empty.", "INVALID_INPUT");
1383
+ return id2.trim();
1384
+ };
1385
+ var levels = (nodes) => {
1386
+ const byId = new Map(nodes.map((node) => [validId2(node.id), node]));
1387
+ if (byId.size !== nodes.length) fail("Workflow node ids must be unique.", "INVALID_INPUT");
1388
+ const remaining = new Set(byId.keys());
1389
+ const completed = /* @__PURE__ */ new Set();
1390
+ const result = [];
1391
+ while (remaining.size) {
1392
+ const ready = [...remaining].sort().map((id2) => byId.get(id2)).filter((node) => (node.dependsOn ?? []).every((dependency) => completed.has(dependency)));
1393
+ if (!ready.length) fail("Workflow contains an unknown dependency or cycle.", "INVALID_INPUT");
1394
+ result.push(ready);
1395
+ for (const node of ready) {
1396
+ remaining.delete(node.id);
1397
+ completed.add(node.id);
1398
+ }
1399
+ }
1400
+ return result;
1401
+ };
1402
+ var runWorkflow = async (nodes, options) => {
1403
+ if (!Number.isInteger(options.maxConcurrency) || options.maxConcurrency < 1) fail("maxConcurrency must be a positive integer.", "INVALID_INPUT");
1404
+ const started = Date.now();
1405
+ const results = {};
1406
+ const order = [];
1407
+ let peakConcurrency = 0;
1408
+ for (const level of levels(nodes)) {
1409
+ const remaining = [...level];
1410
+ while (remaining.length) {
1411
+ const batch = [];
1412
+ const keys = /* @__PURE__ */ new Set();
1413
+ const limit = options.currentConcurrency ? options.currentConcurrency() : options.maxConcurrency;
1414
+ if (!Number.isInteger(limit) || limit < 1) fail("currentConcurrency must return a positive integer.", "INVALID_INPUT");
1415
+ for (const node of remaining) {
1416
+ const key = node.mutationKey?.trim();
1417
+ if (batch.length >= limit || key && keys.has(key)) continue;
1418
+ batch.push(node);
1419
+ if (key) keys.add(key);
1420
+ }
1421
+ if (!batch.length) fail("Workflow could not schedule a mutation batch.", "INVALID_INPUT");
1422
+ peakConcurrency = Math.max(peakConcurrency, batch.length);
1423
+ const values = await Promise.all(batch.map((node) => node.run()));
1424
+ batch.forEach((node, index2) => {
1425
+ results[node.id] = values[index2];
1426
+ order.push(node.id);
1427
+ });
1428
+ for (const node of batch) remaining.splice(remaining.indexOf(node), 1);
1429
+ }
1430
+ }
1431
+ return { results, order, peakConcurrency, criticalPathMs: Date.now() - started };
1432
+ };
1433
+
1434
+ // src/delivery/review.ts
1435
+ var runAdversarialReview = async ({ lenses, reviewer, binding: binding2, maxConcurrency = 3 }) => {
1436
+ if (!Array.isArray(lenses) || lenses.length === 0) fail("At least one review lens is required.", "INVALID_INPUT");
1437
+ if (!Number.isInteger(maxConcurrency) || maxConcurrency < 1) fail("maxConcurrency must be a positive integer.", "INVALID_INPUT");
1438
+ const normalized = lenses.map((lens) => {
1439
+ if (typeof lens !== "object" || lens === null || Array.isArray(lens) || typeof lens.id !== "string" || !lens.id.trim()) fail("Review lens id is required.", "INVALID_INPUT");
1440
+ const maxAttempts = lens.maxAttempts ?? 1;
1441
+ if (!Number.isInteger(maxAttempts) || maxAttempts < 1 || maxAttempts > 3) fail("Review lens maxAttempts must be between 1 and 3.", "INVALID_INPUT");
1442
+ return { id: lens.id.trim(), maxAttempts };
1443
+ });
1444
+ if (new Set(normalized.map((lens) => lens.id)).size !== normalized.length) fail("Review lens ids must be unique.", "INVALID_INPUT");
1445
+ const workflow = await runWorkflow(normalized.map((lens) => ({ id: lens.id, run: async () => {
1446
+ let last = { status: "unverified", reason: "Reviewer returned no verdict." };
1447
+ for (let attempt = 1; attempt <= lens.maxAttempts; attempt += 1) {
1448
+ try {
1449
+ const verdict = await reviewer(lens, attempt);
1450
+ if (!verdict || !["pass", "finding", "unverified"].includes(verdict.status)) return { status: "unverified", reason: "Reviewer returned an invalid verdict." };
1451
+ last = verdict;
1452
+ if (verdict.status !== "unverified" || verdict.retryable !== true) return verdict;
1453
+ } catch (error) {
1454
+ last = { status: "unverified", reason: error instanceof Error ? error.message : String(error) };
1455
+ }
1456
+ }
1457
+ return last;
1458
+ } })), { maxConcurrency });
1459
+ const verdicts = Object.fromEntries(Object.entries(workflow.results).sort(([left], [right]) => left.localeCompare(right)));
1460
+ const reasons = Object.entries(verdicts).flatMap(([id2, verdict]) => verdict.status === "pass" ? [] : verdict.status === "finding" && (verdict.evidence?.trim() || verdict.reproduction?.trim()) ? [`${id2} found an issue: ${verdict.reason ?? "evidence recorded"}.`] : [`${id2} is unverified or lacks reproducible evidence.`]);
1461
+ const base = { verdicts, reasons, binding: binding2, peakConcurrency: workflow.peakConcurrency };
1462
+ return { decision: reasons.length ? "blocked" : "approved", ...base, digest: hashJson(base) };
1463
+ };
1464
+
1465
+ // src/delivery/index.ts
1466
+ var required4 = (value, label) => {
1467
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1468
+ return value.trim();
1469
+ };
1470
+ var criteriaFor = (criteria, gate) => {
1471
+ if (!Array.isArray(criteria)) fail("criteria must be an array.", "INVALID_INPUT");
1472
+ const ids = /* @__PURE__ */ new Set();
1473
+ for (const criterion of criteria) {
1474
+ const id2 = required4(criterion.id, "criterion.id");
1475
+ if (ids.has(id2)) fail("criterion ids must be unique.", "INVALID_INPUT");
1476
+ ids.add(id2);
1477
+ if (!["G2", "G3", "G4", "G5"].includes(criterion.gate)) fail("criterion.gate is invalid.", "INVALID_INPUT");
1478
+ if (!["passed", "failed", "pending", "not-applicable"].includes(criterion.status)) fail("criterion.status is invalid.", "INVALID_INPUT");
1479
+ if (criterion.status === "not-applicable" && !criterion.reason?.trim()) fail("not-applicable criteria require a reason.", "INVALID_INPUT");
1480
+ }
1481
+ return criteria.filter((criterion) => criterion.gate === gate);
1482
+ };
1483
+ var binding = (value) => ({ candidateRevision: required4(value.candidateRevision, "binding.candidateRevision"), contractHash: required4(value.contractHash, "binding.contractHash"), configHash: required4(value.configHash, "binding.configHash") });
1484
+ var assessed = (gate, decision, reasons, current) => {
1485
+ const base = { gate, decision, reasons, binding: binding(current) };
1486
+ return { ...base, digest: hashJson(base) };
1487
+ };
1488
+ var assessPreflight = ({ criteria, repairAttempts = 0, implementerId, reviewerId, reviewKind, reviewApproved, binding: current }) => {
1489
+ required4(implementerId, "implementerId");
1490
+ if (!Number.isInteger(repairAttempts) || repairAttempts < 0) fail("repairAttempts must be a non-negative integer.", "INVALID_INPUT");
1491
+ const g2 = criteriaFor(criteria, "G2");
1492
+ const reasons = [
1493
+ ...g2.length ? [] : ["No G2 criteria are defined."],
1494
+ ...g2.filter((criterion) => criterion.status === "failed" || criterion.status === "pending").map((criterion) => `${criterion.id} is ${criterion.status}.`),
1495
+ ...reviewApproved && reviewerId && reviewerId !== implementerId && reviewKind === "adversarial" ? [] : ["An approved adversarial review by a reviewer different from the implementer is required."],
1496
+ ...repairAttempts <= 2 ? [] : ["The two-repair limit was exceeded; preserve diagnostics and return blocked."]
1497
+ ];
1498
+ return assessed("G2", reasons.length ? "blocked" : "approved", reasons, current);
1499
+ };
1500
+ var composePullRequest = ({ draft, g2, remote }) => {
1501
+ 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}`);
1502
+ 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) };
1503
+ const idempotencyKey = hashJson({ issueId: draft.issueId, contractHash: draft.contractHash, action: "pull-request", revision: draft.candidateRevision });
1504
+ if (remote?.state === "uncertain") return { decision: "blocked", reason: "Remote PR state is uncertain; reconcile before retrying.", idempotencyKey };
1505
+ if (remote?.state === "confirmed") {
1506
+ if (remote.candidateRevision !== draft.candidateRevision || !remote.url) return { decision: "blocked", reason: "Confirmed remote PR does not match the candidate revision.", idempotencyKey };
1507
+ return { decision: "reuse", reason: "The idempotent remote PR already exists for this candidate revision.", idempotencyKey };
1508
+ }
1509
+ const body3 = [`## 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");
1510
+ return { decision: "create", body: body3, reason: "G2 is current and the remote PR is absent.", idempotencyKey };
1511
+ };
1512
+ var createPullRequestApproval = ({ body: body3, metadata, approvedBy, candidateRevision, contractHash, configHash }) => {
1513
+ if (approvedBy !== "human") fail("Pull request approval requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
1514
+ const normalizedBody = required4(body3, "PR body");
1515
+ const binding2 = { approvedBy, candidateRevision: required4(candidateRevision, "candidateRevision"), contractHash: required4(contractHash, "contractHash"), configHash: required4(configHash, "configHash"), bodyHash: hashJson(normalizedBody), metadataHash: hashJson(metadata) };
1516
+ return { ...binding2, digest: hashJson(binding2) };
1517
+ };
1518
+ var verifyPullRequestApproval = ({ approval, body: body3, metadata, candidateRevision, contractHash, configHash }) => {
1519
+ const expected = createPullRequestApproval({ body: body3, metadata, approvedBy: "human", candidateRevision, contractHash, configHash });
1520
+ if (approval.digest !== expected.digest || approval.bodyHash !== expected.bodyHash || approval.metadataHash !== expected.metadataHash) fail("Approved pull request content or metadata changed.", "STALE");
1521
+ return approval;
1522
+ };
1523
+ var assessQaTransition = ({ featureValidated, g5, qaPassed, issue }) => {
1524
+ required4(issue, "issue");
1525
+ const base = { issue, featureValidated, g5: g5.digest, qaPassed };
1526
+ if (!featureValidated || g5.gate !== "G5" || g5.decision !== "approved") return { decision: "blocked", target: "verification", invalidatesDownstream: false, reason: "Feature validation and approved G5 acceptance are required before moving the issue to QA.", idempotencyKey: hashJson(base) };
1527
+ if (!qaPassed) return { decision: "return-to-verification", target: "verification", invalidatesDownstream: true, reason: "QA failed; downstream evidence is invalidated and verification must be repeated.", idempotencyKey: hashJson(base) };
1528
+ return { decision: "move-to-qa", target: "qa", invalidatesDownstream: false, reason: "Feature validation and G5 acceptance are current.", idempotencyKey: hashJson(base) };
1529
+ };
1530
+ var assessIntegration = ({ g2, candidateRevision, evidenceRevision, contractHash, configHash, ci }) => {
1531
+ required4(candidateRevision, "candidateRevision");
1532
+ required4(evidenceRevision, "evidenceRevision");
1533
+ if (!["passed", "failed", "pending", "not-applicable"].includes(ci)) fail("ci is invalid.", "INVALID_INPUT");
1534
+ const reasons = [
1535
+ ...g2.gate === "G2" && g2.decision === "approved" ? [] : ["G2 is not approved."],
1536
+ ...g2.binding.candidateRevision === candidateRevision && g2.binding.contractHash === contractHash && g2.binding.configHash === configHash ? [] : ["G2 is not bound to the current candidate, contract, and configuration."],
1537
+ ...candidateRevision === evidenceRevision ? [] : ["Candidate revision changed; G3 evidence must be revalidated."],
1538
+ ...ci === "passed" ? [] : [`Integration CI is ${ci}.`]
1539
+ ];
1540
+ return assessed("G3", reasons.length ? "blocked" : "approved", reasons, { candidateRevision, contractHash, configHash });
1541
+ };
1542
+ var assessWorktreeCleanup = ({ branch, candidateRevision, contractHash, configHash, remoteBranchRevision, remotePr, integration }) => {
1543
+ required4(branch, "branch");
1544
+ required4(candidateRevision, "candidateRevision");
1545
+ if (remotePr === "uncertain") return { decision: "preserve", reason: "Remote PR state is uncertain; preserve the worktree for reconciliation." };
1546
+ if (remotePr !== "confirmed") return { decision: "preserve", reason: "No confirmed remote PR exists; preserve the worktree." };
1547
+ if (remoteBranchRevision !== candidateRevision) return { decision: "preserve", reason: "Remote branch SHA does not match the candidate revision." };
1548
+ if (integration.gate !== "G3" || integration.decision !== "approved") return { decision: "preserve", reason: "G3 is not approved." };
1549
+ 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." };
1550
+ return { decision: "clean", reason: "Remote branch, PR, and G3 evidence are confirmed for the candidate revision." };
1551
+ };
1552
+ 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.`]);
1553
+ var assessProduction = ({ profile, integration, artifact, isolated, acceptanceArtifact, lowRisk = true, observationMinutes, technicalPassed, evidence, containmentPreauthorized, containmentAction, linkedDefect }) => {
1554
+ required4(artifact, "artifact");
1555
+ if (!Number.isFinite(observationMinutes) || observationMinutes < 0) fail("observationMinutes must be non-negative.", "INVALID_INPUT");
1556
+ 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"));
1557
+ const reasons = [
1558
+ ...profileReasons(profile),
1559
+ ...integration.gate === "G3" && integration.decision === "approved" ? [] : ["G3 is not approved."],
1560
+ ...evidenceReasons,
1561
+ ...isolated || acceptanceArtifact === artifact ? [] : ["Exposure requires isolation or acceptance linked to this artifact version."],
1562
+ ...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."],
1563
+ ...lowRisk && observationMinutes < 15 ? ["Low-risk production validation requires a 15-minute observation window."] : []
1564
+ ];
1565
+ return assessed("G4", reasons.length ? "blocked" : "approved", reasons, integration.binding);
1566
+ };
1567
+ var assessAcceptance = ({ production, acceptanceRequired, accepted, notApplicableReason, materialChange }) => {
1568
+ const reasons = [
1569
+ ...production.gate === "G4" && production.decision === "approved" ? [] : ["G4 is not approved."],
1570
+ ...materialChange ? ["A material change invalidated acceptance; return to the affected gate."] : []
1571
+ ];
1572
+ if (reasons.length) return assessed("G5", "blocked", reasons, production.binding);
1573
+ if (acceptanceRequired && !accepted) return assessed("G5", "awaiting-acceptance", ["Business or UX acceptance is still required."], production.binding);
1574
+ if (!acceptanceRequired && !notApplicableReason?.trim()) return assessed("G5", "blocked", ["Acceptance marked not applicable requires a contractual reason."], production.binding);
1575
+ return assessed("G5", "approved", acceptanceRequired ? [] : [`Acceptance is not applicable: ${notApplicableReason}.`], production.binding);
1576
+ };
1577
+
1578
+ // src/kernel/pilot.ts
1579
+ var required5 = (value, label) => {
1580
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1581
+ return value.trim();
1582
+ };
1583
+ var assessPilot = (manifest) => {
1584
+ required5(manifest.policyHash, "policyHash");
1585
+ required5(manifest.baselineReference, "baselineReference");
1586
+ if (!Array.isArray(manifest.entries)) fail("entries must be an array.", "INVALID_INPUT");
1587
+ const ids = /* @__PURE__ */ new Set();
1588
+ const reasons = [];
1589
+ const included = [];
1590
+ for (const entry of manifest.entries) {
1591
+ const issueId = required5(entry.issueId, "entry.issueId");
1592
+ if (ids.has(issueId)) fail("entry issueIds must be unique; an issue cannot be substituted in the same pilot.", "INVALID_INPUT");
1593
+ ids.add(issueId);
1594
+ if (!["normal", "incident", "sensitive"].includes(entry.classification)) fail("entry.classification is invalid.", "INVALID_INPUT");
1595
+ if (!["included", "excluded", "aborted"].includes(entry.status)) fail("entry.status is invalid.", "INVALID_INPUT");
1596
+ if (entry.status !== "included" && !entry.reason?.trim()) reasons.push(`${issueId} is ${entry.status} without an auditable reason.`);
1597
+ if (entry.status === "included") {
1598
+ included.push(issueId);
1599
+ if (entry.classification !== "normal") reasons.push(`${issueId} is ${entry.classification}; only normal issues can enter the pilot.`);
1600
+ }
1601
+ }
1602
+ if (included.length !== 10) reasons.push(`Pilot requires exactly 10 included issues; found ${included.length}.`);
1603
+ const base = { decision: reasons.length ? "blocked" : "ready", included, reasons };
1604
+ return { ...base, digest: hashJson({ ...manifest, ...base }) };
1605
+ };
1606
+ var IMPROVEMENT_CYCLE_STEPS = ["adversarial-review", "g2-preflight", "baseline-record", "pilot-execution", "comparison"];
1607
+ var nonEmpty3 = (value, label) => {
1608
+ if (typeof value !== "string" || !value.trim()) return fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1609
+ return value.trim();
1610
+ };
1611
+ var validateMetrics = (metrics, index2) => {
1612
+ if (metrics === void 0) return void 0;
1613
+ for (const [key, value] of Object.entries(metrics)) {
1614
+ 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");
1615
+ }
1616
+ return metrics;
1617
+ };
1618
+ var validateIteration = (iteration, index2) => {
1619
+ if (typeof iteration !== "object" || iteration === null || Array.isArray(iteration)) return fail(`iterations[${index2}] must be an object.`, "INVALID_INPUT");
1620
+ if (!Number.isInteger(iteration.iteration) || iteration.iteration < 1) return fail(`iterations[${index2}].iteration must be a positive integer.`, "INVALID_INPUT");
1621
+ 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");
1622
+ iteration.steps.forEach((result, stepIndex) => {
1623
+ if (typeof result !== "object" || result === null || Array.isArray(result)) return fail(`iterations[${index2}].steps[${stepIndex}] must be an object.`, "INVALID_INPUT");
1624
+ if (result.step !== IMPROVEMENT_CYCLE_STEPS[stepIndex]) return fail(`iterations[${index2}].steps[${stepIndex}] must be ${IMPROVEMENT_CYCLE_STEPS[stepIndex]}.`, "INVALID_INPUT");
1625
+ if (!["passed", "failed", "blocked", "pending"].includes(result.status)) return fail(`iterations[${index2}].steps[${stepIndex}].status is invalid.`, "INVALID_INPUT");
1626
+ if (result.status !== "passed" && !nonEmpty3(result.reason, `iterations[${index2}].steps[${stepIndex}].reason`)) return fail(`iterations[${index2}].steps[${stepIndex}].reason is required when the step does not pass.`, "INVALID_INPUT");
1627
+ });
1628
+ if (iteration.adjustment !== void 0) nonEmpty3(iteration.adjustment, `iterations[${index2}].adjustment`);
1629
+ return { ...iteration, metrics: validateMetrics(iteration.metrics, index2) };
1630
+ };
1631
+ var assessImprovementCycle = (input) => {
1632
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return fail("cycle input must be an object.", "INVALID_INPUT");
1633
+ const cycleId = nonEmpty3(input.cycleId, "cycleId");
1634
+ if (!Number.isInteger(input.maxIterations) || input.maxIterations < 1) return fail("maxIterations must be a positive integer.", "INVALID_INPUT");
1635
+ if (!Array.isArray(input.iterations) || input.iterations.length < 1) return fail("iterations must be non-empty.", "INVALID_INPUT");
1636
+ if (input.iterations.length > input.maxIterations) return fail("iterations cannot exceed maxIterations.", "INVALID_INPUT");
1637
+ const iterations = input.iterations.map(validateIteration);
1638
+ iterations.forEach((iteration, index2) => {
1639
+ if (iteration.iteration !== index2 + 1) return fail("iterations must be sequential and start at 1.", "INVALID_INPUT");
1640
+ if (index2 > 0 && iterations[index2 - 1]?.steps.every((step) => step.status === "passed")) return fail("a completed cycle cannot have later iterations.", "INVALID_INPUT");
1641
+ if (index2 < iterations.length - 1 && !iteration.adjustment) return fail(`iterations[${index2}].adjustment is required before repeating.`, "INVALID_INPUT");
1642
+ });
1643
+ const matrix = iterations.map((iteration) => {
1644
+ const statuses = Object.fromEntries(iteration.steps.map((step) => [step.step, step.status]));
1645
+ const passedSteps = iteration.steps.filter((step) => step.status === "passed").length;
1646
+ 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 } : {} };
1647
+ });
1648
+ const latest = iterations[iterations.length - 1];
1649
+ const complete = latest.steps.every((step) => step.status === "passed");
1650
+ 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."];
1651
+ const decision = complete ? "complete" : iterations.length >= input.maxIterations || !latest.adjustment ? "blocked" : "repeat";
1652
+ const result = { type: "agentskit-harness-improvement-cycle", cycleId, decision, ...decision === "repeat" ? { nextIteration: latest.iteration + 1 } : {}, reasons, matrix };
1653
+ const digest6 = createHash("sha256").update(JSON.stringify(result)).digest("hex");
1654
+ return { ...result, digest: digest6 };
1655
+ };
1656
+
1657
+ // src/kernel/eval.ts
1658
+ var EVAL_MANIFEST_SCHEMA_VERSION = 1;
1659
+ var EVAL_LAYERS = ["contract", "deterministic", "integration", "quality", "regression", "resource"];
1660
+ var EVAL_COMPONENTS = ["core", "workflow", "memory", "cache", "doc-bridge", "agent-model", "orca-worktree", "runtime", "code-review", "github-linear", "eval-metrics"];
1661
+ var nonEmpty4 = (value, label) => {
1662
+ const text7 = typeof value === "string" ? value.trim() : "";
1663
+ if (!text7) fail(`${label} is required.`, "INVALID_INPUT");
1664
+ return text7;
1665
+ };
1666
+ var digest4 = (value, label) => {
1667
+ const result = nonEmpty4(value, label);
1668
+ if (!/^[a-f0-9]{64}$/.test(result)) fail(`${label} must be a lowercase SHA-256 digest.`, "INVALID_INPUT");
1669
+ return result;
1670
+ };
1671
+ var score = (value, label) => {
1672
+ const numeric = typeof value === "number" ? value : Number.NaN;
1673
+ if (!Number.isFinite(numeric) || numeric < 0 || numeric > 100) fail(`${label} must be a number between 0 and 100.`, "INVALID_INPUT");
1674
+ return numeric;
1675
+ };
1676
+ var validateCase = (value, index2) => {
1677
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail(`cases[${index2}] must be an object.`, "INVALID_INPUT");
1678
+ const candidate = value;
1679
+ const layer = nonEmpty4(candidate["layer"], `cases[${index2}].layer`);
1680
+ if (!EVAL_LAYERS.includes(layer)) fail(`cases[${index2}].layer is invalid.`, "INVALID_INPUT");
1681
+ const components = candidate["components"];
1682
+ if (!Array.isArray(components) || !components.length) fail(`cases[${index2}].components must be a non-empty array.`, "INVALID_INPUT");
1683
+ const normalizedComponents = components.map((item, componentIndex) => {
1684
+ const component2 = nonEmpty4(item, `cases[${index2}].components[${componentIndex}]`);
1685
+ if (!EVAL_COMPONENTS.includes(component2)) fail(`cases[${index2}].components[${componentIndex}] is invalid.`, "INVALID_INPUT");
1686
+ return component2;
1687
+ });
1688
+ if (new Set(normalizedComponents).size !== normalizedComponents.length) fail(`cases[${index2}].components must not contain duplicates.`, "INVALID_INPUT");
1689
+ const baselineScore = candidate["baselineScore"] === void 0 ? void 0 : score(candidate["baselineScore"], `cases[${index2}].baselineScore`);
1690
+ return {
1691
+ id: nonEmpty4(candidate["id"], `cases[${index2}].id`),
1692
+ layer,
1693
+ components: normalizedComponents,
1694
+ grader: nonEmpty4(candidate["grader"], `cases[${index2}].grader`),
1695
+ input: nonEmpty4(candidate["input"], `cases[${index2}].input`),
1696
+ ...candidate["critical"] === void 0 ? {} : { critical: candidate["critical"] === true },
1697
+ ...candidate["subjective"] === void 0 ? {} : { subjective: candidate["subjective"] === true },
1698
+ ...baselineScore === void 0 ? {} : { baselineScore }
1699
+ };
1700
+ };
1701
+ var manifestBody2 = (value) => {
1702
+ const casesValue = value["cases"];
1703
+ if (!Array.isArray(casesValue) || !casesValue.length) fail("cases must be a non-empty array.", "INVALID_INPUT");
1704
+ const cases = casesValue.map(validateCase);
1705
+ if (new Set(cases.map((item) => item.id)).size !== cases.length) fail("case ids must be unique.", "INVALID_INPUT");
1706
+ const layers = new Set(cases.map((item) => item.layer));
1707
+ const missingLayers = EVAL_LAYERS.filter((layer) => !layers.has(layer));
1708
+ if (missingLayers.length) fail(`cases must cover layers: ${missingLayers.join(", ")}.`, "INVALID_INPUT");
1709
+ const coveredComponents = new Set(cases.flatMap((item) => item.components));
1710
+ const missingComponents = EVAL_COMPONENTS.filter((component2) => !coveredComponents.has(component2));
1711
+ if (missingComponents.length) fail(`cases must cover components: ${missingComponents.join(", ")}.`, "INVALID_INPUT");
1712
+ const gradersValue = value["graders"];
1713
+ if (!Array.isArray(gradersValue) || !gradersValue.length) fail("graders must be a non-empty array.", "INVALID_INPUT");
1714
+ const graders = gradersValue.map((item, index2) => nonEmpty4(item, `graders[${index2}]`));
1715
+ const thresholdsValue = value["thresholds"];
1716
+ if (typeof thresholdsValue !== "object" || thresholdsValue === null || Array.isArray(thresholdsValue)) fail("thresholds must be an object.", "INVALID_INPUT");
1717
+ const thresholds2 = thresholdsValue;
1718
+ const repetitions = value["repetitions"];
1719
+ if (!Number.isInteger(repetitions) || repetitions < 1) fail("repetitions must be a positive integer.", "INVALID_INPUT");
1720
+ return {
1721
+ type: "agentskit-harness-eval-manifest",
1722
+ schemaVersion: EVAL_MANIFEST_SCHEMA_VERSION,
1723
+ suiteId: nonEmpty4(value["suiteId"], "suiteId"),
1724
+ name: nonEmpty4(value["name"], "name"),
1725
+ cases,
1726
+ graders,
1727
+ thresholds: { subjectiveQuality: score(thresholds2["subjectiveQuality"] ?? 80, "thresholds.subjectiveQuality"), maxRegression: score(thresholds2["maxRegression"] ?? 5, "thresholds.maxRegression") },
1728
+ repetitions,
1729
+ provider: nonEmpty4(value["provider"], "provider"),
1730
+ model: nonEmpty4(value["model"], "model"),
1731
+ promptHash: digest4(value["promptHash"], "promptHash"),
1732
+ toolHash: digest4(value["toolHash"], "toolHash"),
1733
+ evidenceOutputs: Array.isArray(value["evidenceOutputs"]) && value["evidenceOutputs"].length ? value["evidenceOutputs"].map((item, index2) => nonEmpty4(item, `evidenceOutputs[${index2}]`)) : fail("evidenceOutputs must be a non-empty array.", "INVALID_INPUT")
1734
+ };
1735
+ };
1736
+ var createEvalManifest = (input) => {
1737
+ const body3 = manifestBody2(input);
1738
+ return { ...body3, digest: hashJson(body3) };
1739
+ };
1740
+ var validateEvalManifest = (value) => {
1741
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("Eval manifest must be an object.", "INVALID_INPUT");
1742
+ const candidate = value;
1743
+ const body3 = manifestBody2(candidate);
1744
+ if (candidate["type"] !== body3.type || candidate["schemaVersion"] !== body3.schemaVersion) fail("Eval manifest type or schemaVersion is invalid.", "INVALID_INPUT");
1745
+ const manifestDigest = digest4(candidate["digest"], "digest");
1746
+ if (manifestDigest !== hashJson(body3)) fail("Eval manifest digest is invalid.", "INVALID_INPUT");
1747
+ return { ...body3, digest: manifestDigest };
1748
+ };
1749
+ var median = (values) => {
1750
+ if (!values.length) return null;
1751
+ const ordered = [...values].sort((left, right) => left - right);
1752
+ const middle = Math.floor(ordered.length / 2);
1753
+ return ordered.length % 2 ? ordered[middle] : (ordered[middle - 1] + ordered[middle]) / 2;
1754
+ };
1755
+ var runEvalBattery = async ({ manifest, evaluate }) => {
1756
+ const validated = validateEvalManifest(manifest);
1757
+ const reports = [];
1758
+ for (const testCase of validated.cases) {
1759
+ const observations = [];
1760
+ for (let repetition = 1; repetition <= validated.repetitions; repetition += 1) observations.push(await evaluate(testCase, repetition));
1761
+ const blockers2 = [];
1762
+ const statuses = observations.map((observation) => observation.status);
1763
+ if (statuses.some((status) => status === "unknown" || status === "stale" || status === "unverified")) blockers2.push("unknown, stale, or unverified evidence");
1764
+ if (statuses.some((status) => status === "failed")) blockers2.push("failed observation");
1765
+ const values = observations.map((observation) => observation.score).filter((value) => typeof value === "number");
1766
+ const minimum = values.length ? Math.min(...values) : null;
1767
+ const baseline = testCase.baselineScore;
1768
+ if (testCase.critical && (minimum === null || minimum < 100)) blockers2.push("critical case requires 100/100");
1769
+ if (testCase.subjective && (median(values) ?? 0) < validated.thresholds.subjectiveQuality) blockers2.push(`subjective score below ${validated.thresholds.subjectiveQuality}/100`);
1770
+ if (baseline !== void 0 && minimum !== null && minimum < baseline - validated.thresholds.maxRegression && !observations.every((observation) => observation.decision)) blockers2.push(`regression exceeds ${validated.thresholds.maxRegression} points without a decision`);
1771
+ reports.push({ id: testCase.id, repetitions: observations.length, min: minimum, median: median(values), max: values.length ? Math.max(...values) : null, statuses, blockers: blockers2 });
1772
+ }
1773
+ const blockers = reports.flatMap((report) => report.blockers.map((reason) => `${report.id}: ${reason}`));
1774
+ return { suiteId: validated.suiteId, repetitions: validated.repetitions, cases: reports, status: blockers.length ? "blocked" : "passed", blockers };
1775
+ };
1776
+ var pass = (expected, output) => typeof expected === "string" ? output === expected : expected(output);
1777
+ var runAgentEval = async ({ suite, agent, concurrency = 1 }) => {
1778
+ if (!suite.name.trim() || !suite.cases.length) fail("Eval suite must have a name and at least one case.", "INVALID_INPUT");
1779
+ if (!Number.isInteger(concurrency) || concurrency < 1) fail("Eval concurrency must be a positive integer.", "INVALID_INPUT");
1780
+ const failures = [];
1781
+ let passed = 0;
1782
+ for (let offset = 0; offset < suite.cases.length; offset += concurrency) {
1783
+ const batch = suite.cases.slice(offset, offset + concurrency);
1784
+ const outputs = await Promise.all(batch.map((testCase) => agent(testCase.input)));
1785
+ batch.forEach((testCase, index2) => {
1786
+ if (pass(testCase.expected, outputs[index2])) passed += 1;
1787
+ else failures.push(testCase.id);
1788
+ });
1789
+ }
1790
+ return { suite: suite.name, total: suite.cases.length, passed, failed: suite.cases.length - passed, accuracy: passed / suite.cases.length, failures };
1791
+ };
1792
+ var assessAgentEval = (report, minimumAccuracy) => {
1793
+ if (!Number.isFinite(minimumAccuracy) || minimumAccuracy < 0 || minimumAccuracy > 1) fail("minimumAccuracy must be between 0 and 1.", "INVALID_INPUT");
1794
+ if (report.total < 1 || report.passed + report.failed !== report.total || report.accuracy !== report.passed / report.total) fail("Eval report is inconsistent.", "INVALID_INPUT");
1795
+ return report.accuracy >= minimumAccuracy ? { status: "passed", reason: `Accuracy ${report.accuracy.toFixed(4)} meets ${minimumAccuracy.toFixed(4)}.`, report } : { status: "blocked", reason: `Accuracy ${report.accuracy.toFixed(4)} is below ${minimumAccuracy.toFixed(4)}.`, report };
1796
+ };
1797
+
1798
+ // src/kernel/cache.ts
1799
+ var validateCacheableOperation = (operation) => {
1800
+ if (operation !== "context" && operation !== "read-only") fail("Only context and read-only operations may use the LLM cache.", "POLICY_BLOCKED");
1801
+ return operation;
1802
+ };
1803
+ var createLlmCacheKey = (input) => {
1804
+ validateCacheableOperation(input.operation);
1805
+ return hashJson(input);
1806
+ };
1807
+ var createLlmCache = () => {
1808
+ const values = /* @__PURE__ */ new Map();
1809
+ let hits = 0;
1810
+ let misses = 0;
1811
+ let invalidations = 0;
1812
+ return {
1813
+ assurance: "contract-tested",
1814
+ telemetry: () => ({ status: "measured", cacheHits: hits, cacheMisses: misses }),
1815
+ async getOrCompute(key, compute) {
1816
+ const cached = values.get(key);
1817
+ if (cached !== void 0) {
1818
+ hits += 1;
1819
+ return cached;
1820
+ }
1821
+ misses += 1;
1822
+ const value = await compute();
1823
+ values.set(key, value);
1824
+ return value;
1825
+ },
1826
+ invalidate(key) {
1827
+ if (key === void 0) {
1828
+ invalidations += values.size;
1829
+ values.clear();
1830
+ return;
1831
+ }
1832
+ if (values.delete(key)) invalidations += 1;
1833
+ },
1834
+ stats: () => ({ hits, misses, invalidations })
1835
+ };
1836
+ };
1837
+
1838
+ // src/kernel/optimization.ts
1839
+ var nonNegative2 = (value, label) => {
1840
+ if (!Number.isFinite(value) || value < 0) fail(`${label} must be a non-negative number.`, "INVALID_INPUT");
1841
+ return value;
1842
+ };
1843
+ var nonNegativeInteger = (value, label) => {
1844
+ nonNegative2(value, label);
1845
+ if (!Number.isInteger(value)) fail(`${label} must be an integer.`, "INVALID_INPUT");
1846
+ return value;
1847
+ };
1848
+ var required6 = (value, label) => {
1849
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1850
+ return value.trim();
1851
+ };
1852
+ var validateOptimizationObservation = (observation) => {
1853
+ required6(observation.sourceRevision, "sourceRevision");
1854
+ required6(observation.contractHash, "contractHash");
1855
+ required6(observation.configHash, "configHash");
1856
+ required6(observation.provider, "provider");
1857
+ required6(observation.model, "model");
1858
+ nonNegative2(observation.durationMs, "durationMs");
1859
+ if (observation.accuracy !== void 0 && (!Number.isFinite(observation.accuracy) || observation.accuracy < 0 || observation.accuracy > 1)) fail("accuracy must be between 0 and 1.", "INVALID_INPUT");
1860
+ if (observation.tokens) {
1861
+ const tokens = observation.tokens;
1862
+ for (const key of ["inputTokens", "outputTokens", "totalTokens"]) nonNegativeInteger(tokens[key], `tokens.${key}`);
1863
+ if (tokens.totalTokens !== tokens.inputTokens + tokens.outputTokens) fail("tokens.totalTokens must equal inputTokens + outputTokens.", "INVALID_INPUT");
1864
+ for (const key of ["cacheReadTokens", "cacheWriteTokens"]) if (tokens[key] !== void 0) nonNegativeInteger(tokens[key], `tokens.${key}`);
1865
+ }
1866
+ if (observation.memory) for (const key of ["reads", "writes", "relevantHits", "staleHits"]) nonNegativeInteger(observation.memory[key], `memory.${key}`);
1867
+ if (observation.cache) {
1868
+ for (const key of ["hits", "misses", "invalidations"]) nonNegativeInteger(observation.cache[key], `cache.${key}`);
1869
+ if (observation.cache.tokensSaved !== void 0) nonNegativeInteger(observation.cache.tokensSaved, "cache.tokensSaved");
1870
+ }
1871
+ if (observation.parallelism) {
1872
+ for (const key of ["tasks", "peakConcurrency"]) nonNegativeInteger(observation.parallelism[key], `parallelism.${key}`);
1873
+ nonNegative2(observation.parallelism.criticalPathMs, "parallelism.criticalPathMs");
1874
+ if (observation.parallelism.queueWaitMs !== void 0) nonNegative2(observation.parallelism.queueWaitMs, "parallelism.queueWaitMs");
1875
+ if (observation.parallelism.tasks > 0 && observation.parallelism.peakConcurrency < 1) fail("parallelism.peakConcurrency must be positive when tasks exist.", "INVALID_INPUT");
1876
+ }
1877
+ return observation;
1878
+ };
1879
+ var rate = (hits, total) => total ? Number((hits / total).toFixed(4)) : void 0;
1880
+ var compareOptimization = (baseline, candidate) => {
1881
+ validateOptimizationObservation(baseline);
1882
+ validateOptimizationObservation(candidate);
1883
+ for (const key of ["sourceRevision", "contractHash", "configHash", "provider", "model"]) if (baseline[key] !== candidate[key]) return { comparable: false, reason: `Bindings differ: ${key}.`, digest: hashJson({ baseline, candidate }) };
1884
+ const result = {
1885
+ comparable: true,
1886
+ reason: "Observations share source, contract, configuration, provider, and model bindings.",
1887
+ digest: hashJson({ baseline, candidate }),
1888
+ durationDeltaMs: candidate.durationMs - baseline.durationMs,
1889
+ ...baseline.accuracy !== void 0 && candidate.accuracy !== void 0 ? { accuracyDelta: Number((candidate.accuracy - baseline.accuracy).toFixed(4)) } : {},
1890
+ ...baseline.tokens && candidate.tokens ? { tokenDelta: candidate.tokens.totalTokens - baseline.tokens.totalTokens } : {},
1891
+ ...baseline.cache && candidate.cache ? { cacheHitRateDelta: (rate(candidate.cache.hits, candidate.cache.hits + candidate.cache.misses) ?? 0) - (rate(baseline.cache.hits, baseline.cache.hits + baseline.cache.misses) ?? 0) } : {},
1892
+ ...baseline.memory && candidate.memory ? { memoryRelevantHitRateDelta: (rate(candidate.memory.relevantHits, candidate.memory.reads) ?? 0) - (rate(baseline.memory.relevantHits, baseline.memory.reads) ?? 0) } : {},
1893
+ ...baseline.parallelism && candidate.parallelism ? { peakConcurrencyDelta: candidate.parallelism.peakConcurrency - baseline.parallelism.peakConcurrency } : {}
1894
+ };
1895
+ return result;
1896
+ };
1897
+
1898
+ // src/kernel/memory.ts
1899
+ var MEMORY_SCOPES = ["issue", "project", "global"];
1900
+ var text2 = (value, label) => {
1901
+ if (typeof value !== "string" || !value.trim()) fail(label + " must be a non-empty string.", "INVALID_INPUT");
1902
+ return value.trim();
1903
+ };
1904
+ var validateMemoryRecord = (record3) => {
1905
+ text2(record3.id, "memory.id");
1906
+ if (!MEMORY_SCOPES.includes(record3.scope)) fail("memory.scope is invalid.", "INVALID_INPUT");
1907
+ text2(record3.summary, "memory.summary");
1908
+ text2(record3.source, "memory.source");
1909
+ text2(record3.sourceRevision, "memory.sourceRevision");
1910
+ text2(record3.contentHash, "memory.contentHash");
1911
+ if (record3.approved !== true) fail("Only approved memory may enter the shared store.", "POLICY_BLOCKED");
1912
+ return record3;
1913
+ };
1914
+ var createInMemoryMemoryAdapter = (options = {}) => {
1915
+ const records = /* @__PURE__ */ new Map();
1916
+ let reads = 0;
1917
+ let writes = 0;
1918
+ let relevantHits = 0;
1919
+ let staleHits = 0;
1920
+ return {
1921
+ id: options.id ?? "in-memory",
1922
+ version: options.version ?? "1",
1923
+ assurance: "contract-tested",
1924
+ telemetry: () => ({ status: "measured", memoryReads: reads, memoryWrites: writes, memoryRelevantHits: relevantHits, memoryStaleHits: staleHits }),
1925
+ async remember(record3) {
1926
+ records.set(validateMemoryRecord(record3).id, record3);
1927
+ writes += 1;
1928
+ },
1929
+ async recall({ query, issueId, project, sourceRevision }) {
1930
+ reads += 1;
1931
+ const needle = query.trim().toLowerCase();
1932
+ const hits = [...records.values()].filter((record3) => {
1933
+ const scopeMatch = record3.scope === "global" || (record3.scope === "issue" ? Boolean(issueId && record3.source.includes(issueId)) : Boolean(project && record3.source.includes(project)));
1934
+ return scopeMatch && (!needle || `${record3.summary} ${record3.source}`.toLowerCase().includes(needle));
1935
+ }).map((record3) => ({ record: record3, relevant: true, stale: sourceRevision !== void 0 && record3.sourceRevision !== sourceRevision }));
1936
+ relevantHits += hits.length;
1937
+ staleHits += hits.filter((hit) => hit.stale).length;
1938
+ return hits;
1939
+ }
1940
+ };
1941
+ };
1942
+ var createKvMemoryAdapter = (store, options = {}) => {
1943
+ const indexKey = "agentskit-harness:memory:index";
1944
+ let reads = 0;
1945
+ let writes = 0;
1946
+ let relevantHits = 0;
1947
+ let staleHits = 0;
1948
+ const matches2 = (record3, query, issueId, project) => {
1949
+ const scopeMatch = record3.scope === "global" || (record3.scope === "issue" ? Boolean(issueId && record3.source.includes(issueId)) : Boolean(project && record3.source.includes(project)));
1950
+ return scopeMatch && (!query || `${record3.summary} ${record3.source}`.toLowerCase().includes(query));
1951
+ };
1952
+ return {
1953
+ id: options.id ?? "agentskit-kv",
1954
+ version: options.version ?? "1",
1955
+ assurance: "contract-tested",
1956
+ telemetry: () => ({ status: "measured", memoryReads: reads, memoryWrites: writes, memoryRelevantHits: relevantHits, memoryStaleHits: staleHits }),
1957
+ async remember(record3) {
1958
+ const valid = validateMemoryRecord(record3);
1959
+ const ids = await store.get(indexKey);
1960
+ const index2 = Array.isArray(ids) ? ids.filter((id2) => typeof id2 === "string") : [];
1961
+ if (!index2.includes(valid.id)) await store.set(indexKey, [...index2, valid.id].sort());
1962
+ await store.set(`agentskit-harness:memory:${valid.id}`, valid);
1963
+ writes += 1;
1964
+ },
1965
+ async recall({ query, issueId, project, sourceRevision }) {
1966
+ reads += 1;
1967
+ const ids = await store.get(indexKey);
1968
+ const records = Array.isArray(ids) ? await Promise.all(ids.filter((id2) => typeof id2 === "string").map((id2) => store.get(`agentskit-harness:memory:${id2}`))) : [];
1969
+ const hits = records.filter((record3) => Boolean(record3 && typeof record3 === "object" && record3.approved === true)).filter((record3) => matches2(record3, query.trim().toLowerCase(), issueId, project)).map((record3) => ({ record: record3, relevant: true, stale: sourceRevision !== void 0 && record3.sourceRevision !== sourceRevision }));
1970
+ relevantHits += hits.length;
1971
+ staleHits += hits.filter((hit) => hit.stale).length;
1972
+ return hits;
1973
+ }
1974
+ };
1975
+ };
1976
+
1977
+ // src/kernel/phase-executor.ts
1978
+ var PHASE_MODES = ["safe", "yolo", "dry-run"];
1979
+ var PHASE_EFFECTS = ["read", "write", "external"];
1980
+ var PHASE_EFFECT_ACTIONS = ["allow", "preview", "block", "escalate"];
1981
+ var PHASE_DECISIONS = ["pass", "block", "escalate", "retry", "cancel", "resume"];
1982
+ var requiredId = (value, label) => {
1983
+ if (typeof value !== "string") return fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1984
+ if (!value.trim()) return fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1985
+ return value.trim();
1986
+ };
1987
+ var names = (values, label) => {
1988
+ if (values === void 0) return [];
1989
+ if (!Array.isArray(values)) fail(`${label} must be an array.`, "INVALID_INPUT");
1990
+ const normalized = values.map((value, index2) => requiredId(value, `${label}[${index2}]`));
1991
+ if (new Set(normalized).size !== normalized.length) fail(`${label} must contain unique names.`, "INVALID_INPUT");
1992
+ return normalized;
1993
+ };
1994
+ var boundedPositive = (value, label, fallback) => {
1995
+ const result = value ?? fallback;
1996
+ if (!Number.isInteger(result) || result < 1 || result > 100) fail(`${label} must be a bounded positive integer (1-100).`, "INVALID_INPUT");
1997
+ return result;
1998
+ };
1999
+ var duration = (value, label) => {
2000
+ if (value === void 0) return void 0;
2001
+ if (!Number.isInteger(value) || value < 1) fail(`${label} must be a positive integer.`, "INVALID_INPUT");
2002
+ return value;
2003
+ };
2004
+ var defaultEffects = (mode) => mode === "dry-run" ? { read: "allow", write: "preview", external: "preview" } : mode === "safe" ? { read: "allow", write: "allow", external: "escalate" } : { read: "allow", write: "allow", external: "allow" };
2005
+ var normalize = (profile) => {
2006
+ if (typeof profile !== "object" || profile === null || Array.isArray(profile)) return fail("profile must be an object.", "INVALID_INPUT");
2007
+ const id2 = requiredId(profile.id, "profile.id");
2008
+ if (!PHASE_MODES.includes(profile.mode)) fail("profile.mode is invalid.", "INVALID_INPUT");
2009
+ if (!Array.isArray(profile.phases) || !profile.phases.length) fail("profile.phases must be non-empty.", "INVALID_INPUT");
2010
+ profile.phases.forEach((phase2, index2) => {
2011
+ if (typeof phase2 !== "object" || phase2 === null || Array.isArray(phase2)) fail(`phases[${index2}] must be an object.`, "INVALID_INPUT");
2012
+ });
2013
+ const ids = profile.phases.map((phase2, index2) => requiredId(phase2.id, `phases[${index2}].id`));
2014
+ if (new Set(ids).size !== ids.length) fail("Phase ids must be unique.", "INVALID_INPUT");
2015
+ const known = new Set(ids);
2016
+ const outputOwners = /* @__PURE__ */ new Map();
2017
+ const phases = profile.phases.map((phase2, index2) => {
2018
+ if (typeof phase2 !== "object" || phase2 === null || Array.isArray(phase2)) return fail(`phases[${index2}] must be an object.`, "INVALID_INPUT");
2019
+ if (!PHASE_EFFECTS.includes(phase2.effect)) fail(`phases[${index2}].effect is invalid.`, "INVALID_INPUT");
2020
+ const dependsOn = names(phase2.dependsOn, `phases[${index2}].dependsOn`);
2021
+ if (dependsOn.includes(ids[index2])) fail(`phases[${index2}] cannot depend on itself.`, "INVALID_INPUT");
2022
+ if (dependsOn.some((dependency) => !known.has(dependency))) fail(`phases[${index2}] has an unknown dependency.`, "INVALID_INPUT");
2023
+ const inputs = names(phase2.inputs, `phases[${index2}].inputs`);
2024
+ const outputs = names(phase2.outputs, `phases[${index2}].outputs`);
2025
+ for (const output of outputs) {
2026
+ const owner = outputOwners.get(output);
2027
+ if (owner) fail(`Output ${output} is declared by both ${owner} and ${ids[index2]}.`, "INVALID_INPUT");
2028
+ outputOwners.set(output, ids[index2]);
2029
+ }
2030
+ const gates = names(phase2.gates, `phases[${index2}].gates`);
2031
+ const maxAttempts = boundedPositive(phase2.retries?.maxAttempts, `phases[${index2}].retries.maxAttempts`, 1);
2032
+ return { id: ids[index2], effect: phase2.effect, inputs, outputs, dependsOn, gates, ...maxAttempts > 1 ? { retries: { maxAttempts } } : {}, ...duration(phase2.budgetMs, `phases[${index2}].budgetMs`) ? { budgetMs: phase2.budgetMs } : {} };
2033
+ });
2034
+ const defaults = defaultEffects(profile.mode);
2035
+ const effectPolicy = { ...defaults, ...profile.effectPolicy ?? {} };
2036
+ for (const effect of PHASE_EFFECTS) if (!PHASE_EFFECT_ACTIONS.includes(effectPolicy[effect])) fail(`effectPolicy.${effect} is invalid.`, "INVALID_INPUT");
2037
+ const maxConcurrency = boundedPositive(profile.maxConcurrency, "profile.maxConcurrency", 1);
2038
+ const budgetMs = duration(profile.budgetMs, "profile.budgetMs");
2039
+ calculateLevels(phases);
2040
+ return { id: id2, mode: profile.mode, phases, effectPolicy, maxConcurrency, ...budgetMs ? { budgetMs } : {} };
2041
+ };
2042
+ var calculateLevels = (phases) => {
2043
+ const byId = new Map(phases.map((phase2) => [phase2.id, phase2]));
2044
+ const remaining = new Set(byId.keys());
2045
+ const completed = /* @__PURE__ */ new Set();
2046
+ const levels2 = [];
2047
+ while (remaining.size) {
2048
+ const ready = [...remaining].sort().filter((id2) => (byId.get(id2)?.dependsOn ?? []).every((dependency) => completed.has(dependency)));
2049
+ if (!ready.length) fail("Phase profile contains an unknown dependency or cycle.", "INVALID_INPUT");
2050
+ levels2.push(ready);
2051
+ ready.forEach((id2) => {
2052
+ remaining.delete(id2);
2053
+ completed.add(id2);
2054
+ });
2055
+ }
2056
+ return levels2;
2057
+ };
2058
+ var createPhaseProfile = (profile) => normalize(profile);
2059
+ var planPhaseProfile = (profile) => {
2060
+ const normalized = normalize(profile);
2061
+ return { profileId: normalized.id, mode: normalized.mode, levels: calculateLevels(normalized.phases), phases: normalized.phases, effectPolicy: normalized.effectPolicy, maxConcurrency: normalized.maxConcurrency, ...normalized.budgetMs ? { budgetMs: normalized.budgetMs } : {} };
2062
+ };
2063
+ var resultDecision = (value) => {
2064
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("Phase handler must return a decision object.", "INVALID_INPUT");
2065
+ const result = value;
2066
+ if (!PHASE_DECISIONS.includes(result.decision)) fail("Phase handler returned an invalid decision.", "INVALID_INPUT");
2067
+ if (result.outputs !== void 0 && (typeof result.outputs !== "object" || result.outputs === null || Array.isArray(result.outputs))) fail("Phase outputs must be an object.", "INVALID_INPUT");
2068
+ return result;
2069
+ };
2070
+ var gateDecision = (value) => typeof value === "boolean" ? { decision: value ? "pass" : "block" } : value;
2071
+ var packet = (phaseIds, ambiguities) => ambiguities.length ? { id: "phase-preflight", phaseIds: [...phaseIds].sort(), ambiguities } : void 0;
2072
+ var timeout = async (operation, budgetMs) => {
2073
+ if (budgetMs === void 0) return operation;
2074
+ let timer;
2075
+ const limit = new Promise((_, reject) => {
2076
+ timer = setTimeout(() => reject(new Error(`phase budget exceeded after ${budgetMs}ms`)), budgetMs);
2077
+ });
2078
+ try {
2079
+ return await Promise.race([operation, limit]);
2080
+ } finally {
2081
+ if (timer) clearTimeout(timer);
905
2082
  }
2083
+ };
2084
+ var executePhaseProfile = async (profile, options = {}) => {
2085
+ const plan = planPhaseProfile(profile);
2086
+ const started = (options.now ?? Date.now)();
2087
+ const inputValues = { ...options.inputs ?? {} };
2088
+ const outputValues = { ...options.resume?.outputs ?? {} };
2089
+ const completed = options.resume?.completed ?? {};
2090
+ const phasesById = new Map(plan.phases.map((phase2) => [phase2.id, phase2]));
2091
+ const mutating = plan.phases.filter((phase2) => phase2.effect !== "read");
2092
+ const preflightAmbiguities = [];
2093
+ const preflightAmbiguityPhaseIds = /* @__PURE__ */ new Set();
2094
+ const preflightBlocked = [];
2095
+ const preflightEscalated = [];
2096
+ for (const phase2 of mutating) {
2097
+ const action = plan.effectPolicy[phase2.effect];
2098
+ if (action === "block") {
2099
+ preflightBlocked.push({ id: phase2.id, effect: phase2.effect, decision: "block", attempts: 0, skipped: true, reason: `Effect ${phase2.effect} is blocked by profile policy.` });
2100
+ continue;
2101
+ }
2102
+ if (action === "escalate") {
2103
+ preflightEscalated.push({ id: phase2.id, effect: phase2.effect, decision: "escalate", attempts: 0, skipped: true, reason: `Effect ${phase2.effect} requires escalation in ${plan.mode} mode.` });
2104
+ continue;
2105
+ }
2106
+ if (!options.preflight && action === "allow") {
2107
+ preflightBlocked.push({ id: phase2.id, effect: phase2.effect, decision: "block", attempts: 0, skipped: true, reason: `Preflight is required before ${phase2.effect} effects.` });
2108
+ continue;
2109
+ }
2110
+ if (!options.preflight) continue;
2111
+ const context = { phase: phase2, attempt: 0, mode: plan.mode, inputs: inputValues, outputs: outputValues, dryRun: action === "preview" };
2112
+ const check = await options.preflight(context);
2113
+ if (check.ambiguities?.length) {
2114
+ preflightAmbiguityPhaseIds.add(phase2.id);
2115
+ preflightAmbiguities.push(...check.ambiguities);
2116
+ }
2117
+ if (check.decision === "block") preflightBlocked.push({ id: phase2.id, effect: phase2.effect, decision: "block", attempts: 0, skipped: true, ...check.reason ? { reason: check.reason } : {} });
2118
+ if (check.decision === "escalate") preflightEscalated.push({ id: phase2.id, effect: phase2.effect, decision: "escalate", attempts: 0, skipped: true, ...check.reason ? { reason: check.reason } : {} });
2119
+ }
2120
+ const decisionPacket = packet([...preflightAmbiguityPhaseIds, ...preflightEscalated.map((phase2) => phase2.id)], preflightAmbiguities);
2121
+ if (decisionPacket || preflightEscalated.length) return { status: "escalated", plan, phases: [...preflightBlocked, ...preflightEscalated], order: [], outputs: outputValues, resumed: false, ...decisionPacket ? { decisionPacket } : {}, durationMs: (options.now ?? Date.now)() - started };
2122
+ if (preflightBlocked.length) return { status: "blocked", plan, phases: preflightBlocked, order: [], outputs: outputValues, resumed: false, durationMs: (options.now ?? Date.now)() - started };
2123
+ const executions = [];
2124
+ let resumed = false;
2125
+ let dryRun = false;
2126
+ const statusOf = (phase2, decision, attempts, skipped, reason, outputs) => ({ id: phase2.id, effect: phase2.effect, decision, attempts, skipped, ...reason ? { reason } : {}, ...outputs ? { outputs } : {} });
2127
+ const runPhase = async (phase2, levelOutputs) => {
2128
+ const prior = completed[phase2.id];
2129
+ if (prior && (prior.decision === "pass" || prior.decision === "resume")) {
2130
+ resumed = true;
2131
+ const restored = prior.outputs ?? {};
2132
+ return { execution: statusOf(phase2, "resume", 0, true, "Resumed from a completed phase.", restored), outputs: restored };
2133
+ }
2134
+ const action = plan.effectPolicy[phase2.effect];
2135
+ if (action === "block") return { execution: statusOf(phase2, "block", 0, true, `Effect ${phase2.effect} is blocked by profile policy.`), outputs: {} };
2136
+ if (action === "escalate") return { execution: statusOf(phase2, "escalate", 0, true, `Effect ${phase2.effect} requires escalation in ${plan.mode} mode.`), outputs: {} };
2137
+ if (action === "preview") {
2138
+ dryRun = true;
2139
+ return { execution: statusOf(phase2, "pass", 0, true, "Effect previewed; handler was not invoked."), outputs: {} };
2140
+ }
2141
+ const values = { ...inputValues, ...levelOutputs };
2142
+ const inputs = {};
2143
+ for (const name of phase2.inputs ?? []) {
2144
+ if (!(name in values)) return { execution: statusOf(phase2, "block", 0, true, `Missing phase input: ${name}.`), outputs: {} };
2145
+ inputs[name] = values[name];
2146
+ }
2147
+ const handler = options.handlers?.[phase2.id];
2148
+ if (!handler) return { execution: statusOf(phase2, "block", 0, true, `No handler registered for phase ${phase2.id}.`), outputs: {} };
2149
+ const gateContext = (attempt) => ({ phase: phase2, attempt, mode: plan.mode, inputs, outputs: levelOutputs, dryRun: false });
2150
+ for (const gateId of phase2.gates ?? []) {
2151
+ const evaluator = options.gates?.[gateId];
2152
+ if (!evaluator) return { execution: statusOf(phase2, "block", 0, true, `No evaluator registered for gate ${gateId}.`), outputs: {} };
2153
+ const gate = gateDecision(await evaluator(gateContext(0)));
2154
+ if (gate.decision !== "pass") return { execution: statusOf(phase2, gate.decision, 0, true, gate.reason ?? `Gate ${gateId} did not pass.`), outputs: {} };
2155
+ }
2156
+ const maxAttempts = phase2.retries?.maxAttempts ?? 1;
2157
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
2158
+ let result;
2159
+ try {
2160
+ result = resultDecision(await timeout(Promise.resolve(handler(gateContext(attempt))), phase2.budgetMs));
2161
+ } catch (error) {
2162
+ return { execution: statusOf(phase2, "block", attempt, false, error instanceof Error ? error.message : String(error)), outputs: {} };
2163
+ }
2164
+ if (result.decision === "retry") {
2165
+ if (attempt < maxAttempts) continue;
2166
+ return { execution: statusOf(phase2, "block", attempt, false, result.reason ?? "Phase retry budget exhausted."), outputs: {} };
2167
+ }
2168
+ if (result.decision === "pass" || result.decision === "resume") {
2169
+ const produced = { ...result.outputs ?? {} };
2170
+ const declared = new Set(phase2.outputs ?? []);
2171
+ if ([...Object.keys(produced)].some((name) => !declared.has(name))) return { execution: statusOf(phase2, "block", attempt, false, "Phase returned an undeclared output."), outputs: {} };
2172
+ if ([...phase2.outputs ?? []].some((name) => !(name in produced))) return { execution: statusOf(phase2, "block", attempt, false, "Phase did not produce every declared output."), outputs: {} };
2173
+ return { execution: statusOf(phase2, result.decision, attempt, false, result.reason, produced), outputs: produced };
2174
+ }
2175
+ return { execution: statusOf(phase2, result.decision, attempt, false, result.reason), outputs: {} };
2176
+ }
2177
+ return { execution: statusOf(phase2, "block", maxAttempts, false, "Phase did not resolve."), outputs: {} };
2178
+ };
2179
+ for (const level of plan.levels) {
2180
+ if (plan.budgetMs !== void 0 && (options.now ?? Date.now)() - started > plan.budgetMs) {
2181
+ const phase2 = phasesById.get(level[0]);
2182
+ executions.push(statusOf(phase2, "block", 0, true, `Profile budget exceeded after ${plan.budgetMs}ms.`));
2183
+ break;
2184
+ }
2185
+ const levelSnapshot = { ...outputValues };
2186
+ const workflow = await runWorkflow(level.map((id2) => ({ id: id2, run: () => runPhase(phasesById.get(id2), levelSnapshot) })), { maxConcurrency: plan.maxConcurrency });
2187
+ let stop = false;
2188
+ for (const id2 of level) {
2189
+ const step = workflow.results[id2];
2190
+ executions.push(step.execution);
2191
+ if (step.execution.decision === "pass" || step.execution.decision === "resume") Object.assign(outputValues, step.outputs);
2192
+ else stop = true;
2193
+ }
2194
+ if (plan.budgetMs !== void 0 && (options.now ?? Date.now)() - started > plan.budgetMs && executions.length) {
2195
+ const last = executions.length - 1;
2196
+ executions[last] = { ...executions[last], decision: "block", reason: `Profile budget exceeded after ${plan.budgetMs}ms.` };
2197
+ stop = true;
2198
+ }
2199
+ if (stop) break;
2200
+ }
2201
+ const failed = executions.find((execution) => execution.decision === "block" || execution.decision === "escalate" || execution.decision === "cancel");
2202
+ const status = failed?.decision === "cancel" ? "cancelled" : failed?.decision === "escalate" ? "escalated" : failed ? "blocked" : dryRun ? "dry-run" : "passed";
2203
+ return { status, plan, phases: executions, order: executions.map((execution) => execution.id), outputs: outputValues, resumed, durationMs: (options.now ?? Date.now)() - started };
2204
+ };
2205
+ var ARTIFACT_SCHEMA_VERSION = 1;
2206
+ var ARTIFACT_TYPES = ["plan", "finding", "decision", "repair", "blocker", "approval", "phase"];
2207
+ var text3 = (value, label) => {
2208
+ if (typeof value !== "string" || !value.trim()) return fail(`${label} is required.`, "INVALID_INPUT");
2209
+ return value.trim();
2210
+ };
2211
+ var digest5 = (value, label) => {
2212
+ const result = text3(value, label);
2213
+ if (!/^[a-f0-9]{64}$/.test(result)) fail(`${label} must be a lowercase SHA-256 digest.`, "INVALID_INPUT");
2214
+ return result;
2215
+ };
2216
+ var artifactId = (value) => {
2217
+ const result = text3(value, "Artifact artifactId");
2218
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(result)) fail("Artifact artifactId is invalid.", "INVALID_INPUT");
2219
+ return result;
2220
+ };
2221
+ var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2222
+ var artifactBody = (artifact) => ({
2223
+ type: artifact.type,
2224
+ schemaVersion: artifact.schemaVersion,
2225
+ artifactId: artifact.artifactId,
2226
+ artifactType: artifact.artifactType,
2227
+ artifactVersion: artifact.artifactVersion,
2228
+ runId: artifact.runId,
2229
+ issueRef: artifact.issueRef,
2230
+ sourceRevision: artifact.sourceRevision,
2231
+ contractHash: artifact.contractHash,
2232
+ configHash: artifact.configHash,
2233
+ contextHash: artifact.contextHash,
2234
+ phase: artifact.phase,
2235
+ payload: artifact.payload,
2236
+ payloadHash: artifact.payloadHash
906
2237
  });
2238
+ var expectedArtifactHash = (artifact) => hashJson(artifactBody(artifact));
2239
+ var validateArtifactEnvelope = (value) => {
2240
+ if (!isRecord5(value)) return fail("Artifact envelope must be an object.", "INVALID_INPUT");
2241
+ if (value["type"] !== "agentskit-harness-artifact" || value["schemaVersion"] !== ARTIFACT_SCHEMA_VERSION) fail("Artifact envelope type or schemaVersion is invalid.", "INVALID_INPUT");
2242
+ if (!ARTIFACT_TYPES.includes(value["artifactType"])) fail("Artifact artifactType is invalid.", "INVALID_INPUT");
2243
+ if (!Number.isInteger(value["artifactVersion"]) || value["artifactVersion"] < 1) fail("Artifact artifactVersion must be a positive integer.", "INVALID_INPUT");
2244
+ const createdAt = text3(value["createdAt"], "Artifact createdAt");
2245
+ if (!Number.isFinite(Date.parse(createdAt))) fail("Artifact createdAt must be a valid timestamp.", "INVALID_INPUT");
2246
+ const payloadHash = digest5(value["payloadHash"], "Artifact payloadHash");
2247
+ if (hashJson(value["payload"]) !== payloadHash) fail("Artifact payloadHash does not match payload.", "INVALID_INPUT");
2248
+ const artifact = {
2249
+ type: "agentskit-harness-artifact",
2250
+ schemaVersion: ARTIFACT_SCHEMA_VERSION,
2251
+ artifactId: artifactId(value["artifactId"]),
2252
+ artifactType: value["artifactType"],
2253
+ artifactVersion: value["artifactVersion"],
2254
+ runId: text3(value["runId"], "Artifact runId"),
2255
+ issueRef: text3(value["issueRef"], "Artifact issueRef"),
2256
+ sourceRevision: text3(value["sourceRevision"], "Artifact sourceRevision"),
2257
+ contractHash: digest5(value["contractHash"], "Artifact contractHash"),
2258
+ configHash: digest5(value["configHash"], "Artifact configHash"),
2259
+ contextHash: digest5(value["contextHash"], "Artifact contextHash"),
2260
+ phase: text3(value["phase"], "Artifact phase"),
2261
+ createdAt,
2262
+ payload: value["payload"],
2263
+ payloadHash
2264
+ };
2265
+ if (digest5(value["artifactHash"], "Artifact artifactHash") !== expectedArtifactHash(artifact)) fail("Artifact artifactHash does not match envelope.", "INVALID_INPUT");
2266
+ return { ...artifact, artifactHash: value["artifactHash"] };
2267
+ };
2268
+ var createArtifactEnvelope = (input) => {
2269
+ if (!ARTIFACT_TYPES.includes(input.artifactType)) fail("Artifact artifactType is invalid.", "INVALID_INPUT");
2270
+ const payloadHash = input.payloadHash ?? hashJson(input.payload);
2271
+ if (payloadHash !== hashJson(input.payload)) fail("Artifact payloadHash does not match payload.", "INVALID_INPUT");
2272
+ const identity = {
2273
+ type: "agentskit-harness-artifact",
2274
+ schemaVersion: ARTIFACT_SCHEMA_VERSION,
2275
+ artifactType: input.artifactType,
2276
+ artifactVersion: input.artifactVersion,
2277
+ runId: input.runId,
2278
+ issueRef: input.issueRef,
2279
+ sourceRevision: input.sourceRevision,
2280
+ contractHash: input.contractHash,
2281
+ configHash: input.configHash,
2282
+ contextHash: input.contextHash,
2283
+ phase: input.phase,
2284
+ payload: input.payload,
2285
+ payloadHash
2286
+ };
2287
+ const id2 = input.artifactId ?? hashJson(identity);
2288
+ const artifact = {
2289
+ ...identity,
2290
+ artifactId: id2,
2291
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
2292
+ };
2293
+ const artifactHash = input.artifactHash ?? expectedArtifactHash(artifact);
2294
+ return validateArtifactEnvelope({ ...artifact, artifactHash });
2295
+ };
2296
+ var renderArtifactMarkdown = (artifact) => [
2297
+ `# ${artifact.artifactType} artifact ${artifact.artifactId}`,
2298
+ "",
2299
+ `- Schema: ${artifact.schemaVersion}`,
2300
+ `- Version: ${artifact.artifactVersion}`,
2301
+ `- Run: ${artifact.runId}`,
2302
+ `- Issue: ${artifact.issueRef}`,
2303
+ `- Phase: ${artifact.phase}`,
2304
+ `- Source revision: ${artifact.sourceRevision}`,
2305
+ `- Contract hash: ${artifact.contractHash}`,
2306
+ `- Configuration hash: ${artifact.configHash}`,
2307
+ `- Context hash: ${artifact.contextHash}`,
2308
+ `- Artifact hash: ${artifact.artifactHash}`,
2309
+ "",
2310
+ "## Payload",
2311
+ "",
2312
+ "```json",
2313
+ JSON.stringify(artifact.payload, null, 2),
2314
+ "```",
2315
+ ""
2316
+ ].join("\n");
2317
+ var artifactFilePath = (stateDir, runId, id2) => join(stateDir, "runs", runId, "artifacts", `${id2}.json`);
2318
+ var artifactMarkdownPath = (stateDir, runId, id2) => join(stateDir, "runs", runId, "artifacts", `${id2}.md`);
2319
+ var FileArtifactStore = class {
2320
+ constructor(stateDir) {
2321
+ this.stateDir = stateDir;
2322
+ }
2323
+ stateDir;
2324
+ write(input) {
2325
+ const artifact = validateArtifactEnvelope(input);
2326
+ const path = artifactFilePath(this.stateDir, artifact.runId, artifact.artifactId);
2327
+ mkdirSync(join(this.stateDir, "runs", artifact.runId, "artifacts"), { recursive: true });
2328
+ if (existsSync(path)) {
2329
+ const existing = validateArtifactEnvelope(JSON.parse(readFileSync(path, "utf8")));
2330
+ if (existing.artifactHash !== artifact.artifactHash) fail(`Artifact ${artifact.artifactId} already exists with different content.`, "HARNESS_ERROR");
2331
+ return existing;
2332
+ }
2333
+ writeFileSync(path, `${JSON.stringify(artifact, null, 2)}
2334
+ `, "utf8");
2335
+ writeFileSync(artifactMarkdownPath(this.stateDir, artifact.runId, artifact.artifactId), renderArtifactMarkdown(artifact), "utf8");
2336
+ new FileEventStore(this.stateDir).append({
2337
+ runId: artifact.runId,
2338
+ sourceRevision: artifact.sourceRevision,
2339
+ configHash: artifact.configHash,
2340
+ type: "artifact.recorded",
2341
+ payload: { artifactId: artifact.artifactId, artifactType: artifact.artifactType, artifactVersion: artifact.artifactVersion, artifactHash: artifact.artifactHash, phase: artifact.phase, representation: "json+markdown" }
2342
+ });
2343
+ return artifact;
2344
+ }
2345
+ read(runId, id2) {
2346
+ return validateArtifactEnvelope(JSON.parse(readFileSync(artifactFilePath(this.stateDir, runId, artifactId(id2)), "utf8")));
2347
+ }
2348
+ list(runId) {
2349
+ const directory = join(this.stateDir, "runs", runId, "artifacts");
2350
+ if (!existsSync(directory)) return [];
2351
+ return readdirSync(directory).filter((name) => name.endsWith(".json")).sort().map((name) => validateArtifactEnvelope(JSON.parse(readFileSync(join(directory, name), "utf8"))));
2352
+ }
2353
+ };
2354
+ var artifactIsFresh = (artifact, binding2) => artifact.runId === binding2.runId && artifact.issueRef === binding2.issueRef && artifact.sourceRevision === binding2.sourceRevision && artifact.contractHash === binding2.contractHash && artifact.configHash === binding2.configHash && artifact.contextHash === binding2.contextHash && (binding2.phase === void 0 || artifact.phase === binding2.phase);
2355
+ var resumeStateFromArtifacts = (artifacts) => {
2356
+ const completed = {};
2357
+ const outputs = {};
2358
+ for (const artifact of artifacts.filter((item) => item.artifactType === "phase").sort((left, right) => left.phase.localeCompare(right.phase) || left.artifactVersion - right.artifactVersion)) {
2359
+ if (!isRecord5(artifact.payload) || artifact.payload["decision"] !== "pass") continue;
2360
+ const phaseOutputs = isRecord5(artifact.payload["outputs"]) ? artifact.payload["outputs"] : {};
2361
+ completed[artifact.phase] = { decision: "pass", outputs: phaseOutputs };
2362
+ Object.assign(outputs, phaseOutputs);
2363
+ }
2364
+ return { completed, outputs };
2365
+ };
2366
+ var createPhaseArtifact = (base, execution) => createArtifactEnvelope({ ...base, artifactType: "phase", phase: execution.id, payload: { decision: execution.decision, outputs: execution.outputs ?? {} } });
2367
+ var readArtifactFile = (path) => validateArtifactEnvelope(JSON.parse(readFileSync(path, "utf8")));
2368
+ var artifactDigest = (artifact) => sha256(JSON.stringify(artifact));
2369
+
2370
+ // src/kernel/quality.ts
2371
+ var QUALITY_DIMENSIONS = ["correctness", "completeness", "speed", "cost", "resource", "reliability"];
2372
+ var finite = (value, label, max) => {
2373
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || max !== void 0 && value > max) return fail(`${label} is invalid.`, "INVALID_INPUT");
2374
+ return value;
2375
+ };
2376
+ var integer = (value, label) => {
2377
+ const result = finite(value, label);
2378
+ if (!Number.isInteger(result)) return fail(`${label} must be an integer.`, "INVALID_INPUT");
2379
+ return result;
2380
+ };
2381
+ var phase = (value) => {
2382
+ if (typeof value !== "string" || !value.trim()) return fail("phaseId is required.", "INVALID_INPUT");
2383
+ return value.trim();
2384
+ };
2385
+ var validatePhaseTelemetry = (value) => {
2386
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return fail("Phase telemetry must be an object.", "INVALID_INPUT");
2387
+ const raw = value;
2388
+ const outcome = raw["outcome"];
2389
+ if (!["pass", "block", "escalate", "cancel", "unknown"].includes(outcome)) return fail("Phase telemetry outcome is invalid.", "INVALID_INPUT");
2390
+ const tokens = raw["tokens"] === void 0 ? void 0 : raw["tokens"];
2391
+ if (tokens) {
2392
+ for (const key of ["inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens", "costUsd"]) if (tokens[key] !== void 0) finite(tokens[key], `tokens.${key}`);
2393
+ }
2394
+ const machine = raw["machine"] === void 0 ? void 0 : raw["machine"];
2395
+ if (machine) {
2396
+ for (const key of ["cpuPercent", "memoryUsedPercent", "peakConcurrency", "queueWaitMs", "contentionMs", "saturationPercent"]) if (machine[key] !== void 0) finite(machine[key], `machine.${key}`, ["cpuPercent", "memoryUsedPercent", "saturationPercent"].includes(key) ? 100 : void 0);
2397
+ }
2398
+ return {
2399
+ phaseId: phase(raw["phaseId"]),
2400
+ ...raw["durationMs"] === void 0 ? {} : { durationMs: finite(raw["durationMs"], "durationMs") },
2401
+ ...raw["attempts"] === void 0 ? {} : { attempts: integer(raw["attempts"], "attempts") },
2402
+ outcome,
2403
+ ...raw["failureClass"] === void 0 ? {} : { failureClass: phase(raw["failureClass"]) },
2404
+ ...raw["evidenceCoverage"] === void 0 ? {} : { evidenceCoverage: finite(raw["evidenceCoverage"], "evidenceCoverage", 1) },
2405
+ ...tokens ? { tokens } : {},
2406
+ ...machine ? { machine } : {}
2407
+ };
2408
+ };
2409
+ var average = (values) => values.length ? Number((values.reduce((sum, value) => sum + value, 0) / values.length).toFixed(2)) : null;
2410
+ var score2 = (value, source, baseline = null) => ({ score: value === null ? null : Math.max(0, Math.min(100, Number(value.toFixed(2)))), status: value === null ? "unknown" : "measured", baselineDelta: value === null || baseline === null ? null : Number((value - baseline).toFixed(2)), source });
2411
+ var evaluateWatchdog = ({ phases, budget }) => {
2412
+ const blockers = [];
2413
+ const duration5 = phases.every((phase2) => phase2.durationMs !== void 0) ? phases.reduce((sum, phase2) => sum + (phase2.durationMs ?? 0), 0) : void 0;
2414
+ const totalTokens = phases.every((phase2) => phase2.tokens?.inputTokens !== void 0 && phase2.tokens.outputTokens !== void 0) ? phases.reduce((sum, phase2) => sum + (phase2.tokens?.inputTokens ?? 0) + (phase2.tokens?.outputTokens ?? 0), 0) : void 0;
2415
+ if (budget.maxDurationMs !== void 0 && duration5 !== void 0 && duration5 > budget.maxDurationMs) blockers.push({ class: "budget", reason: `Duration budget exceeded: ${duration5}ms > ${budget.maxDurationMs}ms.` });
2416
+ if (budget.maxTotalTokens !== void 0 && totalTokens !== void 0 && totalTokens > budget.maxTotalTokens) blockers.push({ class: "budget", reason: `Token budget exceeded: ${totalTokens} > ${budget.maxTotalTokens}.` });
2417
+ for (const phase2 of phases) {
2418
+ if (budget.maxMemoryUsedPercent !== void 0 && phase2.machine?.memoryUsedPercent !== void 0 && phase2.machine.memoryUsedPercent > budget.maxMemoryUsedPercent) blockers.push({ class: "resource", phaseId: phase2.phaseId, reason: `Memory saturation exceeded: ${phase2.machine.memoryUsedPercent}% > ${budget.maxMemoryUsedPercent}%.` });
2419
+ if (budget.maxSaturationPercent !== void 0 && phase2.machine?.saturationPercent !== void 0 && phase2.machine.saturationPercent > budget.maxSaturationPercent) blockers.push({ class: "contention", phaseId: phase2.phaseId, reason: `Saturation exceeded: ${phase2.machine.saturationPercent}% > ${budget.maxSaturationPercent}%.` });
2420
+ }
2421
+ return { status: blockers.length ? "blocked" : "ok", blockers };
2422
+ };
2423
+ var createQualityMatrix = ({ phases, baseline, budget = {} }) => {
2424
+ const current = phases.map(validatePhaseTelemetry);
2425
+ const prior = baseline?.map(validatePhaseTelemetry) ?? [];
2426
+ const currentDurations = current.flatMap((phase2) => phase2.durationMs === void 0 ? [] : [phase2.durationMs]);
2427
+ const priorDurations = prior.flatMap((phase2) => phase2.durationMs === void 0 ? [] : [phase2.durationMs]);
2428
+ const currentTokens = current.flatMap((phase2) => phase2.tokens?.inputTokens !== void 0 && phase2.tokens.outputTokens !== void 0 ? [phase2.tokens.inputTokens + phase2.tokens.outputTokens] : []);
2429
+ const priorTokens = prior.flatMap((phase2) => phase2.tokens?.inputTokens !== void 0 && phase2.tokens.outputTokens !== void 0 ? [phase2.tokens.inputTokens + phase2.tokens.outputTokens] : []);
2430
+ const correctness = score2(average(current.map((phase2) => phase2.evidenceCoverage === void 0 ? 0 : phase2.evidenceCoverage * 100)), "mean evidence coverage");
2431
+ const completeness = score2(current.length ? current.filter((phase2) => phase2.outcome === "pass").length / current.length * 100 : null, "passed phases / total phases");
2432
+ const speed = score2(currentDurations.length && priorDurations.length ? average(priorDurations) / Math.max(1, average(currentDurations)) * 100 : null, "baseline duration / current duration", 100);
2433
+ const cost = score2(currentTokens.length && priorTokens.length ? average(priorTokens) / Math.max(1, average(currentTokens)) * 100 : null, "baseline tokens / current tokens", 100);
2434
+ const resourceValues = current.flatMap((phase2) => phase2.machine?.cpuPercent !== void 0 && phase2.machine.memoryUsedPercent !== void 0 ? [100 - Math.max(phase2.machine.cpuPercent, phase2.machine.memoryUsedPercent)] : []);
2435
+ const resource = score2(average(resourceValues), "100 - max(cpu%, memory%)");
2436
+ const reliability = score2(current.length ? current.filter((phase2) => phase2.outcome === "pass").length / current.length * 100 : null, "passed phases / total phases");
2437
+ const dimensions = { correctness, completeness, speed, cost, resource, reliability };
2438
+ const measured = Object.values(dimensions).filter((item) => item.score !== null).map((item) => item.score);
2439
+ const overall = score2(average(measured), "mean of measured dimensions");
2440
+ const unknownMetricCount = Object.values(dimensions).filter((item) => item.status === "unknown").length + current.filter((phase2) => phase2.durationMs === void 0 || phase2.tokens === void 0 || phase2.machine === void 0).length;
2441
+ const blockers = evaluateWatchdog({ phases: current, budget }).blockers;
2442
+ const body3 = { type: "agentskit-harness-quality-matrix", schemaVersion: 1, dimensions, overall, phaseCount: current.length, unknownMetricCount, blockers };
2443
+ return { ...body3, digest: hashJson(body3) };
2444
+ };
2445
+
2446
+ // src/kernel/compatibility.ts
2447
+ var COMPATIBILITY_SCHEMA_VERSION = 1;
2448
+ var COMPATIBILITY_COMPONENTS = ["core", "memory", "eval", "doc-bridge", "code-review", "adapter-boundary", "runtime"];
2449
+ var text4 = (value, label) => {
2450
+ const result = typeof value === "string" ? value.trim() : "";
2451
+ if (!result) fail(`${label} is required.`, "INVALID_INPUT");
2452
+ return result;
2453
+ };
2454
+ var sha = (value, label) => {
2455
+ const result = text4(value, label);
2456
+ if (!/^[a-f0-9]{40,64}$/.test(result)) fail(`${label} must be a pinned git revision.`, "INVALID_INPUT");
2457
+ return result;
2458
+ };
2459
+ var component = (value, index2) => {
2460
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail(`components[${index2}] must be an object.`, "INVALID_INPUT");
2461
+ const candidate = value;
2462
+ const id2 = text4(candidate["id"], `components[${index2}].id`);
2463
+ if (!COMPATIBILITY_COMPONENTS.includes(id2)) fail(`components[${index2}].id is invalid.`, "INVALID_INPUT");
2464
+ if (candidate["adapterBoundary"] !== "real-adapter") fail(`components[${index2}] must use the real-adapter boundary.`, "INVALID_INPUT");
2465
+ return {
2466
+ id: id2,
2467
+ package: text4(candidate["package"], `components[${index2}].package`),
2468
+ version: text4(candidate["version"], `components[${index2}].version`),
2469
+ revision: sha(candidate["revision"], `components[${index2}].revision`),
2470
+ repository: text4(candidate["repository"], `components[${index2}].repository`),
2471
+ adapterBoundary: "real-adapter",
2472
+ testCommand: text4(candidate["testCommand"], `components[${index2}].testCommand`),
2473
+ evalCommand: text4(candidate["evalCommand"], `components[${index2}].evalCommand`),
2474
+ previousVersion: text4(candidate["previousVersion"], `components[${index2}].previousVersion`),
2475
+ noHarnessBaseline: text4(candidate["noHarnessBaseline"], `components[${index2}].noHarnessBaseline`),
2476
+ migrationEvidence: text4(candidate["migrationEvidence"], `components[${index2}].migrationEvidence`),
2477
+ rollbackEvidence: text4(candidate["rollbackEvidence"], `components[${index2}].rollbackEvidence`)
2478
+ };
2479
+ };
2480
+ var body = (value) => {
2481
+ const componentsValue = value["components"];
2482
+ if (!Array.isArray(componentsValue) || !componentsValue.length) fail("components must be a non-empty array.", "INVALID_INPUT");
2483
+ const components = componentsValue.map(component);
2484
+ if (new Set(components.map((item) => item.id)).size !== components.length) fail("component ids must be unique.", "INVALID_INPUT");
2485
+ const missing = COMPATIBILITY_COMPONENTS.filter((id2) => !components.some((item) => item.id === id2));
2486
+ if (missing.length) fail(`components must cover: ${missing.join(", ")}.`, "INVALID_INPUT");
2487
+ const outputs = value["evidenceOutputs"];
2488
+ if (!Array.isArray(outputs) || !outputs.length) fail("evidenceOutputs must be a non-empty array.", "INVALID_INPUT");
2489
+ return {
2490
+ type: "agentskit-harness-compatibility-manifest",
2491
+ schemaVersion: COMPATIBILITY_SCHEMA_VERSION,
2492
+ harnessVersion: text4(value["harnessVersion"], "harnessVersion"),
2493
+ sourceRevision: sha(value["sourceRevision"], "sourceRevision"),
2494
+ components,
2495
+ evidenceOutputs: outputs.map((item, index2) => text4(item, `evidenceOutputs[${index2}]`))
2496
+ };
2497
+ };
2498
+ var createCompatibilityManifest = (input) => {
2499
+ const value = body(input);
2500
+ return { ...value, digest: hashJson(value) };
2501
+ };
2502
+ var validateCompatibilityManifest = (value) => {
2503
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("Compatibility manifest must be an object.", "INVALID_INPUT");
2504
+ const candidate = value;
2505
+ const valueBody = body(candidate);
2506
+ if (candidate["type"] !== valueBody.type || candidate["schemaVersion"] !== valueBody.schemaVersion) fail("Compatibility manifest type or schemaVersion is invalid.", "INVALID_INPUT");
2507
+ const digest6 = text4(candidate["digest"], "digest");
2508
+ if (digest6 !== hashJson(valueBody)) fail("Compatibility manifest digest is invalid.", "INVALID_INPUT");
2509
+ return { ...valueBody, digest: digest6 };
2510
+ };
2511
+ var assessCompatibility = ({ manifest, observations }) => {
2512
+ const validated = validateCompatibilityManifest(manifest);
2513
+ const expected = new Set(validated.components.map((item) => item.id));
2514
+ const seen = /* @__PURE__ */ new Set();
2515
+ const blockers = [];
2516
+ observations.forEach((observation) => {
2517
+ if (!expected.has(observation.componentId) || seen.has(observation.componentId)) blockers.push(`${observation.componentId}: unexpected or duplicate observation`);
2518
+ seen.add(observation.componentId);
2519
+ if (observation.status !== "passed") blockers.push(`${observation.componentId}: ${observation.status} compatibility evidence`);
2520
+ if (!observation.evidence) blockers.push(`${observation.componentId}: missing evidence`);
2521
+ });
2522
+ validated.components.forEach((item) => {
2523
+ if (!seen.has(item.id)) blockers.push(`${item.id}: missing observation`);
2524
+ const observation = observations.find((candidate) => candidate.componentId === item.id);
2525
+ if (observation && observation.previousVersion !== item.previousVersion) blockers.push(`${item.id}: previous version binding mismatch`);
2526
+ if (observation && observation.noHarnessBaseline !== item.noHarnessBaseline) blockers.push(`${item.id}: no-Harness baseline binding mismatch`);
2527
+ });
2528
+ return { status: blockers.length ? "blocked" : "passed", componentCount: validated.components.length, observations, blockers };
2529
+ };
2530
+
2531
+ // src/kernel/resilience.ts
2532
+ var positiveInteger = (value, label) => {
2533
+ if (!Number.isInteger(value) || value < 1) fail(`${label} must be a positive integer.`, "INVALID_INPUT");
2534
+ return value;
2535
+ };
2536
+ var nonNegativeInteger2 = (value, label) => {
2537
+ if (!Number.isInteger(value) || value < 0) fail(`${label} must be a non-negative integer.`, "INVALID_INPUT");
2538
+ return value;
2539
+ };
2540
+ var classifyFailure = (error) => {
2541
+ const value = error;
2542
+ const code = typeof value?.code === "string" ? value.code.toUpperCase() : "";
2543
+ const message = typeof value?.message === "string" ? value.message : String(error);
2544
+ const text7 = `${code} ${message}`.toLowerCase();
2545
+ if (/quota|rate.?limit|too many requests|429/.test(text7)) return { class: "quota", retryable: true, reason: message };
2546
+ if (/timeout|timed out|deadline/.test(text7)) return { class: "timeout", retryable: true, reason: message };
2547
+ if (/policy|forbidden|permission|approval/.test(text7)) return { class: "policy", retryable: false, reason: message };
2548
+ if (/invalid|schema|argument|config|validation/.test(text7)) return { class: "validation", retryable: false, reason: message };
2549
+ if (/network|connection|econn|503|502|external/.test(text7)) return { class: "external", retryable: true, reason: message };
2550
+ return { class: "unknown", retryable: false, reason: message };
2551
+ };
2552
+ var recoveryDelayMs = (attempt, policy) => {
2553
+ positiveInteger(attempt, "attempt");
2554
+ nonNegativeInteger2(policy.baseDelayMs, "baseDelayMs");
2555
+ nonNegativeInteger2(policy.maxDelayMs, "maxDelayMs");
2556
+ if (policy.maxDelayMs < policy.baseDelayMs) fail("maxDelayMs must be greater than or equal to baseDelayMs.", "INVALID_INPUT");
2557
+ return Math.min(policy.maxDelayMs, policy.baseDelayMs * 2 ** Math.max(0, attempt - 1));
2558
+ };
2559
+ var wait = (delayMs, sleep) => delayMs > 0 ? sleep(delayMs) : Promise.resolve();
2560
+ var runWithRecovery = async (operation, options) => {
2561
+ const maxAttempts = positiveInteger(options.maxAttempts, "maxAttempts");
2562
+ const baseDelayMs = nonNegativeInteger2(options.baseDelayMs, "baseDelayMs");
2563
+ const maxDelayMs = nonNegativeInteger2(options.maxDelayMs, "maxDelayMs");
2564
+ if (maxDelayMs < baseDelayMs) fail("maxDelayMs must be greater than or equal to baseDelayMs.", "INVALID_INPUT");
2565
+ if (options.timeoutMs !== void 0) positiveInteger(options.timeoutMs, "timeoutMs");
2566
+ const sleep = options.sleep ?? ((delayMs) => new Promise((resolve6) => setTimeout(resolve6, delayMs)));
2567
+ const observations = [];
2568
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
2569
+ const controller = new AbortController();
2570
+ let timer;
2571
+ try {
2572
+ const operationPromise = operation(controller.signal, attempt);
2573
+ const value = options.timeoutMs === void 0 ? await operationPromise : await Promise.race([
2574
+ operationPromise,
2575
+ new Promise((_, reject) => {
2576
+ timer = setTimeout(() => {
2577
+ controller.abort();
2578
+ reject(new Error("operation timed out"));
2579
+ }, options.timeoutMs);
2580
+ })
2581
+ ]);
2582
+ return { status: "completed", attempts: attempt, observations, value };
2583
+ } catch (error) {
2584
+ const failure = classifyFailure(error);
2585
+ const delayMs = failure.retryable && attempt < maxAttempts ? recoveryDelayMs(attempt, { baseDelayMs, maxDelayMs }) : 0;
2586
+ const observation = { attempt, failure, delayMs };
2587
+ observations.push(observation);
2588
+ options.onObservation?.(observation);
2589
+ if (!failure.retryable || attempt >= maxAttempts) return { status: "failed", attempts: attempt, observations, failure };
2590
+ await wait(delayMs, sleep);
2591
+ } finally {
2592
+ if (timer) clearTimeout(timer);
2593
+ }
2594
+ }
2595
+ return fail("Recovery loop exhausted unexpectedly.", "HARNESS_ERROR");
2596
+ };
2597
+
2598
+ // src/adapters/agent.ts
2599
+ var required7 = (value, label) => {
2600
+ if (typeof value !== "string" || !value.trim()) return fail(`${label} is required.`, "INVALID_INPUT");
2601
+ return value.trim();
2602
+ };
2603
+ var duration2 = (value) => Number.isFinite(value) && value >= 0 ? value : fail("Agent durationMs must be non-negative.", "INVALID_INPUT");
2604
+ var usage = (value) => {
2605
+ if (value === void 0) return { status: "unknown" };
2606
+ if (value.status !== "measured" && value.status !== "unknown") return fail("Agent usage status is invalid.", "INVALID_INPUT");
2607
+ for (const key of ["inputTokens", "outputTokens", "totalTokens"]) if (value[key] !== void 0 && (!Number.isFinite(value[key]) || value[key] < 0)) return fail(`Agent usage ${key} must be non-negative.`, "INVALID_INPUT");
2608
+ return value;
2609
+ };
2610
+ var createCodingAgentAdapter = ({ id: id2, version, assurance = "contract-tested", timeoutMs = 12e4, execute }) => {
2611
+ const adapterId = required7(id2, "agent.id");
2612
+ const adapterVersion = required7(version, "agent.version");
2613
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 1) return fail("agent.timeoutMs must be a positive integer.", "INVALID_INPUT");
2614
+ return {
2615
+ id: adapterId,
2616
+ version: adapterVersion,
2617
+ assurance,
2618
+ execute: async (request) => {
2619
+ const issueRef = required7(request.issueRef, "agent.issueRef");
2620
+ const prompt = required7(request.prompt, "agent.prompt");
2621
+ const sourceRevision = required7(request.sourceRevision, "agent.sourceRevision");
2622
+ const controller = new AbortController();
2623
+ const signal = request.signal;
2624
+ if (signal?.aborted) return { status: "cancelled", output: {}, diff: "", usage: { status: "unknown" }, durationMs: 0, failure: { class: "policy", retryable: false, reason: "Agent execution was cancelled before start." }, metadata: { assurance, telemetry: { status: "measured", durationMs: 0 } } };
2625
+ const onAbort = () => controller.abort();
2626
+ signal?.addEventListener("abort", onAbort, { once: true });
2627
+ const started = Date.now();
2628
+ let timer;
2629
+ let timedOut = false;
2630
+ try {
2631
+ const operation = Promise.resolve(execute({ issueRef, prompt, sourceRevision, ...request.contextHash ? { contextHash: request.contextHash } : {}, signal: controller.signal }));
2632
+ const timeout2 = new Promise((_, reject) => {
2633
+ timer = setTimeout(() => {
2634
+ timedOut = true;
2635
+ controller.abort();
2636
+ reject(new Error("agent execution timed out"));
2637
+ }, timeoutMs);
2638
+ });
2639
+ const result = await Promise.race([operation, timeout2]);
2640
+ if (!result || typeof result !== "object" || Array.isArray(result) || typeof result.output !== "object" || result.output === null || Array.isArray(result.output) || typeof result.diff !== "string") return fail("Agent result must contain structured output and diff.", "INVALID_INPUT");
2641
+ const measuredUsage = usage(result.usage);
2642
+ const durationMs = duration2(Date.now() - started);
2643
+ return { status: "completed", output: result.output, diff: result.diff, usage: measuredUsage, durationMs, metadata: { assurance, telemetry: { status: measuredUsage.status, durationMs, ...measuredUsage.inputTokens === void 0 ? {} : { inputTokens: measuredUsage.inputTokens }, ...measuredUsage.outputTokens === void 0 ? {} : { outputTokens: measuredUsage.outputTokens }, ...measuredUsage.totalTokens === void 0 ? {} : { totalTokens: measuredUsage.totalTokens } } } };
2644
+ } catch (error) {
2645
+ const failure = timedOut ? { class: "timeout", retryable: true, reason: "Agent execution timed out." } : classifyFailure(error);
2646
+ const status = timedOut ? "timeout" : controller.signal.aborted ? "cancelled" : "failed";
2647
+ return { status, output: {}, diff: "", usage: { status: "unknown" }, durationMs: duration2(Date.now() - started), failure, metadata: { assurance, telemetry: { status: "unknown", durationMs: duration2(Date.now() - started) } } };
2648
+ } finally {
2649
+ if (timer) clearTimeout(timer);
2650
+ signal?.removeEventListener("abort", onAbort);
2651
+ }
2652
+ }
2653
+ };
2654
+ };
907
2655
  var BENCHMARK_SCHEMA_VERSION = 1;
908
2656
  var percentage = (part, total) => total ? Number((part / total).toFixed(4)) : null;
909
- var DEFAULT_POLICY = { minComparableTasks: 3, maxDurationRegressionRate: 0.2, minCompletedRunsPerTask: 3, minBaselineSamplesPerTask: 3, requireZeroEscapedIncomplete: true };
910
2657
  var improvementRate = (baseline, current) => baseline === void 0 || current === void 0 || baseline === 0 ? null : Number(((baseline - current) / baseline).toFixed(4));
911
- var increaseRate = (baseline, current) => baseline === void 0 || current === void 0 || baseline === 0 ? null : Number(((current - baseline) / baseline).toFixed(4));
912
- var increaseDelta = (baseline, current) => baseline === void 0 || current === null ? null : Number((current - baseline).toFixed(4));
913
- var increaseDirection = (rate2, delta) => delta === null ? improvementDirection(rate2) : delta > 0 ? "improved" : delta < 0 ? "regressed" : "unchanged";
914
2658
  var improvementDirection = (rate2) => rate2 === null ? "unavailable" : rate2 > 0 ? "improved" : rate2 < 0 ? "regressed" : "unchanged";
915
2659
  var count = (items, predicate) => items.filter(predicate).length;
916
- var median = (values) => {
2660
+ var median2 = (values) => {
917
2661
  if (!values.length) return null;
918
2662
  const sorted = [...values].sort((left, right) => left - right);
919
2663
  const middle = Math.floor(sorted.length / 2);
@@ -926,25 +2670,13 @@ var reviewMinutes = (run) => {
926
2670
  const elapsed = Date.parse(run.humanApproval.at) - Date.parse(reviewStart);
927
2671
  return Number.isFinite(elapsed) && elapsed >= 0 ? Number((elapsed / 6e4).toFixed(2)) : void 0;
928
2672
  };
929
- var confidence = (comparable, completedRuns, policy) => !comparable ? "insufficient" : completedRuns >= policy.minCompletedRunsPerTask ? "reliable" : "directional";
930
- var artifactAcceptanceRate = (run) => {
931
- const rates = run.checks.flatMap((check) => {
932
- const evidence = check.evidence;
933
- if (!evidence) return [];
934
- const direct = typeof evidence["artifactAcceptanceRate"] === "number" ? [evidence["artifactAcceptanceRate"]] : [];
935
- const reports = Array.isArray(evidence["reports"]) ? evidence["reports"].flatMap((report) => typeof report === "object" && report !== null && typeof report["artifactAcceptanceRate"] === "number" ? [report["artifactAcceptanceRate"]] : []) : [];
936
- return [...direct, ...reports].filter((rate2) => Number.isFinite(rate2) && rate2 >= 0 && rate2 <= 1);
937
- });
938
- return rates.length ? Number((rates.reduce((total, rate2) => total + rate2, 0) / rates.length).toFixed(4)) : void 0;
939
- };
940
2673
  var projectRun = (run) => {
941
2674
  const checks = { total: run.checks.length, passed: count(run.checks, (check) => check.status === "passed"), failed: count(run.checks, (check) => check.status === "failed") };
942
2675
  const outcomes = { total: run.outcomes.length, passed: count(run.outcomes, (outcome) => outcome.status === "passed"), failed: count(run.outcomes, (outcome) => outcome.status === "failed") };
943
2676
  const evidence = { total: run.checks.length, attached: count(run.checks, (check) => check.evidence !== void 0) };
944
- const acceptanceRate = artifactAcceptanceRate(run);
945
2677
  const humanReviewMinutes = reviewMinutes(run);
946
2678
  const escapedIncomplete = run.state === "COMPLETE" && checks.failed === 0 && outcomes.failed === 0 && evidence.attached === evidence.total ? 0 : void 0;
947
- 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, ...acceptanceRate === void 0 ? {} : { artifactAcceptanceRate: acceptanceRate }, ...escapedIncomplete === void 0 ? {} : { escapedIncomplete }, humanApproved: run.humanApproval !== void 0, ...humanReviewMinutes === void 0 ? {} : { humanReviewMinutes }, authorized: run.authorization !== void 0, ...run.benchmark ? { benchmark: run.benchmark } : {} };
2679
+ 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 } : {} };
948
2680
  };
949
2681
  var summarize = (runs) => {
950
2682
  const stateCounts = Object.fromEntries(RUN_STATES.map((state) => [state, count(runs, (run) => run.state === state)]));
@@ -955,14 +2687,6 @@ var summarize = (runs) => {
955
2687
  const evidenceTotal = runs.reduce((total, run) => total + run.evidence.total, 0);
956
2688
  const evidenceAttached = runs.reduce((total, run) => total + run.evidence.attached, 0);
957
2689
  const firstAttempts = runs.filter((run) => !run.supersedes);
958
- const superseded = new Set(runs.flatMap((run) => run.supersedes ? [run.supersedes] : []));
959
- const effectiveRuns = runs.filter((run) => !superseded.has(run.runId));
960
- const effectiveChecksTotal = effectiveRuns.reduce((total, run) => total + run.checks.total, 0);
961
- const effectiveChecksPassed = effectiveRuns.reduce((total, run) => total + run.checks.passed, 0);
962
- const effectiveOutcomesTotal = effectiveRuns.reduce((total, run) => total + run.outcomes.total, 0);
963
- const effectiveOutcomesPassed = effectiveRuns.reduce((total, run) => total + run.outcomes.passed, 0);
964
- const effectiveEvidenceTotal = effectiveRuns.reduce((total, run) => total + run.evidence.total, 0);
965
- const effectiveEvidenceAttached = effectiveRuns.reduce((total, run) => total + run.evidence.attached, 0);
966
2690
  const durations = runs.flatMap((run) => run.durationMs === void 0 ? [] : [run.durationMs]);
967
2691
  return {
968
2692
  totalRuns: runs.length,
@@ -973,20 +2697,14 @@ var summarize = (runs) => {
973
2697
  firstAttemptRuns: firstAttempts.length,
974
2698
  humanApprovedRuns: count(runs, (run) => run.humanApproved),
975
2699
  authorizedRuns: count(runs, (run) => run.authorized),
976
- effectiveRunCount: effectiveRuns.length,
977
- effectiveCompleteRuns: count(effectiveRuns, (run) => run.state === "COMPLETE"),
978
- effectiveCompletionRate: percentage(count(effectiveRuns, (run) => run.state === "COMPLETE"), effectiveRuns.length),
979
- effectiveCheckPassRate: percentage(effectiveChecksPassed, effectiveChecksTotal),
980
- effectiveOutcomePassRate: percentage(effectiveOutcomesPassed, effectiveOutcomesTotal),
981
- effectiveEvidenceCoverageRate: percentage(effectiveEvidenceAttached, effectiveEvidenceTotal),
982
2700
  checkPassRate: percentage(checksPassed, checksTotal),
983
2701
  outcomePassRate: percentage(outcomesPassed, outcomesTotal),
984
2702
  evidenceCoverageRate: percentage(evidenceAttached, evidenceTotal),
985
2703
  firstAttemptApprovalRate: percentage(count(firstAttempts, (run) => run.humanApproved), firstAttempts.length),
986
2704
  retryRate: percentage(count(runs, (run) => run.supersedes !== void 0), runs.length),
987
2705
  staleRate: percentage(stateCounts.STALE, runs.length),
988
- averageDurationMs: durations.length ? Math.round(durations.reduce((total, duration3) => total + duration3, 0) / durations.length) : null,
989
- medianDurationMs: median(durations)
2706
+ averageDurationMs: durations.length ? Math.round(durations.reduce((total, duration5) => total + duration5, 0) / durations.length) : null,
2707
+ medianDurationMs: median2(durations)
990
2708
  };
991
2709
  };
992
2710
  var readRuns = (stateDir) => {
@@ -1013,7 +2731,7 @@ var sha2562 = (value, label) => {
1013
2731
  if (!/^[a-f0-9]{64}$/.test(result)) return fail(`${label} must be a lowercase SHA-256 digest.`, "INVALID_CONFIG");
1014
2732
  return result;
1015
2733
  };
1016
- var stringList = (value, label) => {
2734
+ var stringList2 = (value, label) => {
1017
2735
  if (!Array.isArray(value)) return fail(`${label} must be a non-empty string array.`, "INVALID_CONFIG");
1018
2736
  const items = value;
1019
2737
  if (!items.length || !items.every((item) => typeof item === "string" && Boolean(item.trim()))) return fail(`${label} must be a non-empty string array.`, "INVALID_CONFIG");
@@ -1026,73 +2744,16 @@ var nonNegativeNumber = (value, label) => {
1026
2744
  const result = value;
1027
2745
  return result;
1028
2746
  };
1029
- var nonNegativeInteger = (value, label) => {
2747
+ var nonNegativeInteger3 = (value, label) => {
1030
2748
  const result = nonNegativeNumber(value, label);
1031
2749
  if (result !== void 0 && !Number.isInteger(result)) return fail(`${label} must be an integer.`, "INVALID_CONFIG");
1032
2750
  return result;
1033
2751
  };
1034
- var rate = (value, label) => {
1035
- const result = nonNegativeNumber(value, label);
1036
- if (result !== void 0 && result > 1) return fail(`${label} must be between 0 and 1.`, "INVALID_CONFIG");
1037
- return result;
1038
- };
1039
2752
  var timestamp = (value, label) => {
1040
2753
  const result = nonEmptyString(value, label);
1041
2754
  if (!Number.isFinite(Date.parse(result))) return fail(`${label} must be a valid timestamp.`, "INVALID_CONFIG");
1042
2755
  return result;
1043
2756
  };
1044
- var relativePath = (value, label) => {
1045
- const result = nonEmptyString(value, label);
1046
- if (result.startsWith("/") || result.split("/").includes("..")) return fail(`${label} must be a repository-relative path.`, "INVALID_CONFIG");
1047
- return result;
1048
- };
1049
- var taskFile = (value, label) => {
1050
- if (value === void 0) return void 0;
1051
- if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
1052
- const raw = value;
1053
- return { path: relativePath(raw["path"], `${label}.path`), sha256: sha2562(raw["sha256"], `${label}.sha256`) ?? fail(`${label}.sha256 is required.`, "INVALID_CONFIG") };
1054
- };
1055
- var taskSource = (value, label) => {
1056
- if (value === void 0) return void 0;
1057
- if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
1058
- const raw = value;
1059
- return { repository: nonEmptyString(raw["repository"], `${label}.repository`), path: relativePath(raw["path"], `${label}.path`), revision: nonEmptyString(raw["revision"], `${label}.revision`) };
1060
- };
1061
- var suiteSource = (value, label) => {
1062
- if (value === void 0) return void 0;
1063
- if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
1064
- const raw = value;
1065
- return { repository: nonEmptyString(raw["repository"], `${label}.repository`), revision: nonEmptyString(raw["revision"], `${label}.revision`), taskDefinition: relativePath(raw["taskDefinition"], `${label}.taskDefinition`) };
1066
- };
1067
- var taskScope = (value, label) => {
1068
- if (value === void 0) return void 0;
1069
- if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
1070
- const raw = value;
1071
- return { read: stringList(raw["read"], `${label}.read`), write: stringList(raw["write"], `${label}.write`) };
1072
- };
1073
- var taskSurfaces = (value, label) => {
1074
- if (value === void 0) return void 0;
1075
- if (!Array.isArray(value) || !value.length) return fail(`${label} must be a non-empty array.`, "INVALID_CONFIG");
1076
- const surfaces = value.map((item, index2) => nonEmptyString(item, `${label}[${index2}]`));
1077
- if (surfaces.some((surface2) => !SURFACE_NAMES.includes(surface2))) fail(`${label} contains an unknown surface.`, "INVALID_CONFIG");
1078
- if (new Set(surfaces).size !== surfaces.length) fail(`${label} must contain unique surfaces.`, "INVALID_CONFIG");
1079
- return surfaces;
1080
- };
1081
- var benchmarkPolicy = (value) => {
1082
- if (value === void 0) return void 0;
1083
- if (typeof value !== "object" || value === null || Array.isArray(value)) return fail("benchmark.policy must be an object.", "INVALID_CONFIG");
1084
- const raw = value;
1085
- const minComparableTasks = nonNegativeInteger(raw["minComparableTasks"], "benchmark.policy.minComparableTasks");
1086
- const maxDurationRegressionRate = nonNegativeNumber(raw["maxDurationRegressionRate"], "benchmark.policy.maxDurationRegressionRate");
1087
- const minCompletedRunsPerTask = nonNegativeInteger(raw["minCompletedRunsPerTask"], "benchmark.policy.minCompletedRunsPerTask");
1088
- const minBaselineSamplesPerTask = nonNegativeInteger(raw["minBaselineSamplesPerTask"] ?? 1, "benchmark.policy.minBaselineSamplesPerTask");
1089
- if (minComparableTasks === void 0 || minComparableTasks < 1) return fail("benchmark.policy.minComparableTasks must be at least 1.", "INVALID_CONFIG");
1090
- if (maxDurationRegressionRate === void 0 || maxDurationRegressionRate > 1) return fail("benchmark.policy.maxDurationRegressionRate must be between 0 and 1.", "INVALID_CONFIG");
1091
- if (minCompletedRunsPerTask === void 0 || minCompletedRunsPerTask < 1) return fail("benchmark.policy.minCompletedRunsPerTask must be at least 1.", "INVALID_CONFIG");
1092
- if (minBaselineSamplesPerTask === void 0 || minBaselineSamplesPerTask < 1) return fail("benchmark.policy.minBaselineSamplesPerTask must be at least 1.", "INVALID_CONFIG");
1093
- if (typeof raw["requireZeroEscapedIncomplete"] !== "boolean") return fail("benchmark.policy.requireZeroEscapedIncomplete must be boolean.", "INVALID_CONFIG");
1094
- return { minComparableTasks, maxDurationRegressionRate, minCompletedRunsPerTask, minBaselineSamplesPerTask, requireZeroEscapedIncomplete: raw["requireZeroEscapedIncomplete"] };
1095
- };
1096
2757
  var validateBenchmarkManifest = (value) => {
1097
2758
  if (typeof value !== "object" || value === null || Array.isArray(value)) fail("benchmark manifest must be an object.", "INVALID_CONFIG");
1098
2759
  const raw = value;
@@ -1101,12 +2762,7 @@ var validateBenchmarkManifest = (value) => {
1101
2762
  const tasks = rawTasks.map((item, index2) => {
1102
2763
  if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`benchmark.tasks[${index2}] must be an object.`, "INVALID_CONFIG");
1103
2764
  const task = item;
1104
- const kind = task["kind"] === void 0 ? void 0 : nonEmptyString(task["kind"], `benchmark.tasks[${index2}].kind`);
1105
- const prompt = taskFile(task["prompt"], `benchmark.tasks[${index2}].prompt`);
1106
- const source = taskSource(task["source"], `benchmark.tasks[${index2}].source`);
1107
- const scope = taskScope(task["scope"], `benchmark.tasks[${index2}].scope`);
1108
- const surfaces = taskSurfaces(task["surfaces"], `benchmark.tasks[${index2}].surfaces`);
1109
- 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 } };
2765
+ return { id: nonEmptyString(task["id"], `benchmark.tasks[${index2}].id`), title: nonEmptyString(task["title"], `benchmark.tasks[${index2}].title`), acceptanceCriteria: stringList2(task["acceptanceCriteria"], `benchmark.tasks[${index2}].acceptanceCriteria`) };
1110
2766
  });
1111
2767
  if (new Set(tasks.map((task) => task.id)).size !== tasks.length) fail("benchmark task ids must be unique.", "INVALID_CONFIG");
1112
2768
  const taskIds = new Set(tasks.map((task) => task.id));
@@ -1119,14 +2775,10 @@ var validateBenchmarkManifest = (value) => {
1119
2775
  const taskId = nonEmptyString(observation["taskId"], `benchmark.observations[${index2}].taskId`);
1120
2776
  if (!taskIds.has(taskId)) fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
1121
2777
  const task = tasks.find((candidate) => candidate.id === taskId) ?? fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
1122
- const attempts = nonNegativeInteger(observation["attempts"], `benchmark.observations[${index2}].attempts`);
2778
+ const attempts = nonNegativeInteger3(observation["attempts"], `benchmark.observations[${index2}].attempts`);
1123
2779
  const durationMs = nonNegativeNumber(observation["durationMs"], `benchmark.observations[${index2}].durationMs`);
1124
- 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");
1125
- if (durationSamplesMs && !durationSamplesMs.length) fail(`benchmark.observations[${index2}].durationSamplesMs must not be empty.`, "INVALID_CONFIG");
1126
- const artifactAcceptanceRate2 = rate(observation["artifactAcceptanceRate"], `benchmark.observations[${index2}].artifactAcceptanceRate`);
1127
- const protocolCompletionRate = rate(observation["protocolCompletionRate"], `benchmark.observations[${index2}].protocolCompletionRate`);
1128
2780
  const reviewMinutes2 = nonNegativeNumber(observation["reviewMinutes"], `benchmark.observations[${index2}].reviewMinutes`);
1129
- const escapedIncomplete = nonNegativeInteger(observation["escapedIncomplete"], `benchmark.observations[${index2}].escapedIncomplete`);
2781
+ const escapedIncomplete = nonNegativeInteger3(observation["escapedIncomplete"], `benchmark.observations[${index2}].escapedIncomplete`);
1130
2782
  const evidenceDigest = sha2562(observation["evidenceDigest"], `benchmark.observations[${index2}].evidenceDigest`);
1131
2783
  const rawEvidence = observation["evidence"] === void 0 ? void 0 : Array.isArray(observation["evidence"]) ? observation["evidence"] : fail(`benchmark.observations[${index2}].evidence must be an array.`, "INVALID_CONFIG");
1132
2784
  const evidence = rawEvidence?.map((item2, evidenceIndex) => {
@@ -1139,12 +2791,10 @@ var validateBenchmarkManifest = (value) => {
1139
2791
  return { criterion, status: evidenceStatus, source: nonEmptyString(entry["source"], `benchmark.observations[${index2}].evidence[${evidenceIndex}].source`) };
1140
2792
  });
1141
2793
  if (evidence && new Set(evidence.map((entry) => entry.criterion)).size !== evidence.length) fail(`benchmark.observations[${index2}].evidence criteria must be unique.`, "INVALID_CONFIG");
1142
- 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 }, ...durationSamplesMs === void 0 ? {} : { durationSamplesMs }, ...artifactAcceptanceRate2 === void 0 ? {} : { artifactAcceptanceRate: artifactAcceptanceRate2 }, ...protocolCompletionRate === void 0 ? {} : { protocolCompletionRate }, ...reviewMinutes2 === void 0 ? {} : { reviewMinutes: reviewMinutes2 }, ...escapedIncomplete === void 0 ? {} : { escapedIncomplete }, ...evidence === void 0 ? {} : { evidence }, ...evidenceDigest === void 0 ? {} : { evidenceDigest } };
2794
+ 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 }, ...reviewMinutes2 === void 0 ? {} : { reviewMinutes: reviewMinutes2 }, ...escapedIncomplete === void 0 ? {} : { escapedIncomplete }, ...evidence === void 0 ? {} : { evidence }, ...evidenceDigest === void 0 ? {} : { evidenceDigest } };
1143
2795
  });
1144
2796
  if (new Set(observations.map((observation) => observation.taskId)).size !== observations.length) fail("benchmark allows at most one baseline observation per task.", "INVALID_CONFIG");
1145
- const provenance = suiteSource(raw["provenance"], "benchmark.provenance");
1146
- const policy = benchmarkPolicy(raw["policy"]);
1147
- 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 } };
2797
+ return { type: "agentskit-harness-benchmark-manifest", schemaVersion: BENCHMARK_SCHEMA_VERSION, suiteId: nonEmptyString(raw["suiteId"], "benchmark.suiteId"), name: nonEmptyString(raw["name"], "benchmark.name"), tasks, observations };
1148
2798
  };
1149
2799
  var loadBenchmarkManifest = (path) => {
1150
2800
  try {
@@ -1170,9 +2820,6 @@ var recordBenchmarkObservation = (path, input) => {
1170
2820
  recordedAt: input.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1171
2821
  ...input.attempts === void 0 ? {} : { attempts: input.attempts },
1172
2822
  ...input.durationMs === void 0 ? {} : { durationMs: input.durationMs },
1173
- ...input.durationSamplesMs === void 0 ? {} : { durationSamplesMs: input.durationSamplesMs },
1174
- ...input.artifactAcceptanceRate === void 0 ? {} : { artifactAcceptanceRate: input.artifactAcceptanceRate },
1175
- ...input.protocolCompletionRate === void 0 ? {} : { protocolCompletionRate: input.protocolCompletionRate },
1176
2823
  ...input.reviewMinutes === void 0 ? {} : { reviewMinutes: input.reviewMinutes },
1177
2824
  ...input.escapedIncomplete === void 0 ? {} : { escapedIncomplete: input.escapedIncomplete },
1178
2825
  ...input.evidence === void 0 ? {} : { evidence: input.evidence },
@@ -1191,111 +2838,39 @@ var recordBenchmarkObservation = (path, input) => {
1191
2838
  }
1192
2839
  return observation;
1193
2840
  };
1194
- var comparisons = (runs, manifest, policy) => manifest.tasks.map((task) => {
2841
+ var comparisons = (runs, manifest) => manifest.tasks.map((task) => {
1195
2842
  const taskRuns = runs.filter((run) => run.benchmark?.suiteId === manifest.suiteId && run.benchmark.taskId === task.id);
1196
2843
  const latest = taskRuns.at(-1);
1197
2844
  const baseline = manifest.observations.find((observation) => observation.taskId === task.id);
1198
2845
  const coveredCriteria = new Set((baseline?.evidence ?? []).map((entry) => entry.criterion));
1199
2846
  const baselineEvidenceCoverageRate = baseline ? percentage(coveredCriteria.size, task.acceptanceCriteria.length) : null;
1200
2847
  const baselineEvidenceComplete = baselineEvidenceCoverageRate === 1;
1201
- const baselineEvidencePassed = baselineEvidenceComplete && (baseline?.evidence?.every((entry) => entry.status === "passed") ?? false);
1202
- const baselineDeliveryComplete = baseline?.status === "passed" && baselineEvidencePassed;
1203
- const baselineDurationSamples = baseline?.durationSamplesMs ?? (baseline?.durationMs === void 0 ? [] : [baseline.durationMs]);
1204
- const baselineMedianDurationMs = median(baselineDurationSamples);
1205
- const baselineSamplesSufficient = baselineDurationSamples.length >= policy.minBaselineSamplesPerTask;
1206
- const comparable = baseline !== void 0 && baseline.status !== "not-run" && baselineEvidenceComplete && baselineDeliveryComplete && baselineSamplesSufficient && latest?.state === "COMPLETE";
1207
- const comparability = comparable ? "comparable" : baseline === void 0 ? "missing-baseline" : baseline.status === "not-run" ? "baseline-not-run" : !baselineEvidenceComplete ? "baseline-evidence-missing" : latest === void 0 ? "harness-not-run" : latest.state !== "COMPLETE" ? "harness-not-complete" : !baselineDeliveryComplete ? "baseline-incomplete" : "baseline-samples-insufficient";
1208
- const completedTaskRuns = taskRuns.filter((run) => run.state === "COMPLETE");
1209
- const durationSamplesMs = completedTaskRuns.flatMap((run) => run.durationMs === void 0 ? [] : [run.durationMs]);
1210
- const medianDurationMs = median(durationSamplesMs);
1211
- const retryCount = count(taskRuns, (run) => run.supersedes !== void 0);
1212
- const attempts = retryCount + (taskRuns.length ? 1 : 0);
1213
- const durationRate = comparable ? improvementRate(baselineMedianDurationMs ?? void 0, medianDurationMs ?? void 0) : null;
1214
- const attemptsRate = comparable ? improvementRate(baseline?.attempts, attempts) : null;
1215
- const reviewRate = comparable ? improvementRate(baseline?.reviewMinutes, latest?.humanReviewMinutes) : null;
1216
- const acceptanceSamples = taskRuns.flatMap((run) => run.artifactAcceptanceRate === void 0 ? [] : [run.artifactAcceptanceRate]);
1217
- const harnessArtifactAcceptanceRate = acceptanceSamples.length ? Number((acceptanceSamples.reduce((total, rate2) => total + rate2, 0) / acceptanceSamples.length).toFixed(4)) : null;
1218
- const artifactAcceptanceImprovementRate = increaseRate(baseline?.artifactAcceptanceRate, harnessArtifactAcceptanceRate ?? void 0);
1219
- const artifactAcceptanceDelta = increaseDelta(baseline?.artifactAcceptanceRate, harnessArtifactAcceptanceRate);
1220
- const completedRuns = completedTaskRuns.length;
1221
- const harnessProtocolCompletionRate = taskRuns.length ? percentage(completedRuns, taskRuns.length) : null;
1222
- const protocolCompletionImprovementRate = increaseRate(baseline?.protocolCompletionRate, harnessProtocolCompletionRate ?? void 0);
1223
- const protocolCompletionDelta = increaseDelta(baseline?.protocolCompletionRate, harnessProtocolCompletionRate);
1224
- const escapedIncompleteRate = improvementRate(baseline?.escapedIncomplete, latest?.escapedIncomplete);
1225
- 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 } : {} };
2848
+ const comparable2 = baseline !== void 0 && baseline.status !== "not-run" && baselineEvidenceComplete && latest?.state === "COMPLETE";
2849
+ 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";
2850
+ const durationRate = comparable2 ? improvementRate(baseline?.durationMs, latest?.durationMs) : null;
2851
+ const attemptsRate = comparable2 ? improvementRate(baseline?.attempts, taskRuns.length) : null;
2852
+ const reviewRate = comparable2 ? improvementRate(baseline?.reviewMinutes, latest?.humanReviewMinutes) : null;
2853
+ const escapedIncompleteRate = comparable2 ? improvementRate(baseline?.escapedIncomplete, latest?.escapedIncomplete) : null;
2854
+ 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 } : {} };
1226
2855
  });
1227
2856
  var benchmarkRuns = (stateDir, manifest) => {
1228
2857
  const runs = readRuns(stateDir).map(projectRun).sort((left, right) => left.runId.localeCompare(right.runId));
1229
- const policy = manifest?.policy ?? DEFAULT_POLICY;
1230
- const reportComparisons = manifest ? comparisons(runs, manifest, policy) : [];
1231
- const comparable = reportComparisons.filter((comparison) => comparison.comparable);
1232
- const durationRegressionTaskIds = comparable.filter((comparison) => (comparison.improvement.durationRate ?? 0) < -policy.maxDurationRegressionRate).map((comparison) => comparison.taskId);
1233
- const escapedIncompleteTaskIds = comparable.filter((comparison) => comparison.harness.escapedIncomplete !== 0).map((comparison) => comparison.taskId);
1234
- const reasons = [];
1235
- if (comparable.length < policy.minComparableTasks) reasons.push(`requires at least ${policy.minComparableTasks} comparable tasks`);
1236
- const incompleteBaselineTaskIds = reportComparisons.filter((comparison) => comparison.comparability === "baseline-incomplete").map((comparison) => comparison.taskId);
1237
- if (incompleteBaselineTaskIds.length) reasons.push(`baseline delivery incomplete: ${incompleteBaselineTaskIds.join(", ")}`);
1238
- const baselineSampleGaps = reportComparisons.filter((comparison) => comparison.baselineSampleCount < policy.minBaselineSamplesPerTask).map((comparison) => `${comparison.taskId} (${comparison.baselineSampleCount}/${policy.minBaselineSamplesPerTask})`);
1239
- if (baselineSampleGaps.length) reasons.push(`requires ${policy.minBaselineSamplesPerTask} baseline samples per task: ${baselineSampleGaps.join(", ")}`);
1240
- if (durationRegressionTaskIds.length) reasons.push(`duration regression exceeds ${policy.maxDurationRegressionRate * 100}%: ${durationRegressionTaskIds.join(", ")}`);
1241
- if (policy.requireZeroEscapedIncomplete && escapedIncompleteTaskIds.length) reasons.push(`escaped incomplete delivery: ${escapedIncompleteTaskIds.join(", ")}`);
1242
- const confidenceLevel = comparable.length < policy.minComparableTasks ? "insufficient" : comparable.every((comparison) => comparison.confidence === "reliable") ? "reliable" : "directional";
1243
- const qualityGate = { status: comparable.length < policy.minComparableTasks ? "insufficient-data" : reasons.length ? "failed" : "passed", confidence: confidenceLevel, comparableTaskCount: comparable.length, policy, durationRegressionTaskIds, escapedIncompleteTaskIds, reasons };
1244
- return { type: "agentskit-harness-benchmark", schemaVersion: BENCHMARK_SCHEMA_VERSION, stateDir, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), runs, summary: summarize(runs), comparisons: reportComparisons, qualityGate, ...manifest ? { manifest: { suiteId: manifest.suiteId, taskCount: manifest.tasks.length, baselineCount: manifest.observations.length, comparableTaskCount: comparable.length } } : {} };
1245
- };
1246
-
1247
- // src/coding-benchmark.ts
1248
- var text2 = (value, label) => {
1249
- if (typeof value !== "string" || !value.trim()) fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1250
- return value.trim();
1251
- };
1252
- var numberValue = (value, label, integer = false) => {
1253
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || integer && !Number.isInteger(value)) fail(`${label} must be a non-negative ${integer ? "integer" : "number"}.`, "INVALID_INPUT");
1254
- return value;
2858
+ const reportComparisons = manifest ? comparisons(runs, manifest) : [];
2859
+ 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 } } : {} };
1255
2860
  };
1256
- var validateExternalCodingBenchmarkReport = (value) => {
1257
- if (typeof value !== "object" || value === null || Array.isArray(value)) fail("coding benchmark report must be an object.", "INVALID_INPUT");
1258
- const raw = value;
1259
- if (!["edit", "fix-bug", "add-feature", "refactor", "add-test", "review-pr", "free-form"].includes(text2(raw["kind"], "report.kind"))) fail("report.kind is not a supported coding task kind.", "INVALID_INPUT");
1260
- if (typeof raw["dryRun"] !== "boolean" || typeof raw["isolateWorktrees"] !== "boolean") fail("report.dryRun and report.isolateWorktrees must be booleans.", "INVALID_INPUT");
1261
- if (!Array.isArray(raw["rows"]) || raw["rows"].length === 0) fail("report.rows must contain at least one provider result.", "INVALID_INPUT");
1262
- const rows = raw["rows"].map((item, index2) => {
1263
- if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`report.rows[${index2}] must be an object.`, "INVALID_INPUT");
1264
- const row = item;
1265
- const status = text2(row["status"], `report.rows[${index2}].status`);
1266
- if (!["ok", "partial", "fail", "timeout"].includes(status)) fail(`report.rows[${index2}].status is invalid.`, "INVALID_INPUT");
1267
- const completenessScore = numberValue(row["completenessScore"], `report.rows[${index2}].completenessScore`);
1268
- if (completenessScore > 100) fail(`report.rows[${index2}].completenessScore must be between 0 and 100.`, "INVALID_INPUT");
1269
- const optional = (key) => row[key] === void 0 ? void 0 : numberValue(row[key], `report.rows[${index2}].${key}`);
1270
- return {
1271
- providerId: text2(row["providerId"], `report.rows[${index2}].providerId`),
1272
- status,
1273
- completenessScore,
1274
- fileEditCount: numberValue(row["fileEditCount"], `report.rows[${index2}].fileEditCount`, true),
1275
- summary: text2(row["summary"], `report.rows[${index2}].summary`),
1276
- ...optional("durationMs") === void 0 ? {} : { durationMs: optional("durationMs") },
1277
- ...optional("inputTokens") === void 0 ? {} : { inputTokens: optional("inputTokens") },
1278
- ...optional("outputTokens") === void 0 ? {} : { outputTokens: optional("outputTokens") },
1279
- ...optional("costUsd") === void 0 ? {} : { costUsd: optional("costUsd") },
1280
- ...row["successPassed"] === void 0 ? {} : typeof row["successPassed"] !== "boolean" ? fail(`report.rows[${index2}].successPassed must be a boolean.`, "INVALID_INPUT") : { successPassed: row["successPassed"] }
1281
- };
1282
- });
1283
- if (new Set(rows.map((row) => row.providerId)).size !== rows.length) fail("coding benchmark provider ids must be unique.", "INVALID_INPUT");
1284
- return { kind: text2(raw["kind"], "report.kind"), prompt: text2(raw["prompt"], "report.prompt"), dryRun: raw["dryRun"], isolateWorktrees: raw["isolateWorktrees"], repoRoot: text2(raw["repoRoot"], "report.repoRoot"), rows };
1285
- };
1286
- var required = (value, label) => {
2861
+ var required8 = (value, label) => {
1287
2862
  if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1288
2863
  return value.trim();
1289
2864
  };
1290
- var duration = (value) => {
2865
+ var duration3 = (value) => {
1291
2866
  if (!Number.isFinite(value) || value < 0) fail("Tool durationMs must be a non-negative number.", "INVALID_INPUT");
1292
2867
  return value;
1293
2868
  };
1294
2869
  var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionId = randomUUID(), resume = false }) => {
1295
2870
  if (run.state !== "IMPLEMENTING") fail(`Agent sessions can only start during IMPLEMENTING, not ${run.state}.`, "INVALID_STATE");
1296
- const id2 = required(sessionId, "sessionId");
1297
- const adapterId = required(adapter.id, "adapter.id");
1298
- const adapterVersion = required(adapter.version, "adapter.version");
2871
+ const id2 = required8(sessionId, "sessionId");
2872
+ const adapterId = required8(adapter.id, "adapter.id");
2873
+ const adapterVersion = required8(adapter.version, "adapter.version");
1299
2874
  if (!policy || typeof policy.evaluate !== "function") fail("policy.evaluate is required.", "INVALID_INPUT");
1300
2875
  if (!runtime || typeof runtime.execute !== "function") fail("runtime.execute is required.", "INVALID_INPUT");
1301
2876
  if (!Array.isArray(adapter.capabilities) || adapter.capabilities.some((capability) => typeof capability !== "string" || !capability.trim())) fail("adapter.capabilities must contain non-empty strings.", "INVALID_INPUT");
@@ -1346,18 +2921,18 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
1346
2921
  };
1347
2922
  const complete = (input) => {
1348
2923
  open();
1349
- const actionId = required(input.actionId, "actionId");
2924
+ const actionId = required8(input.actionId, "actionId");
1350
2925
  if (!pending.has(actionId)) fail(`Tool action is not pending: ${actionId}.`, "INVALID_STATE");
1351
- const event = append("tool.completed", { actionId, resultHash: required(input.resultHash, "resultHash"), durationMs: duration(input.durationMs), ...input.runtimeEvidence ? { runtimeEvidence: input.runtimeEvidence } : {} });
2926
+ const event = append("tool.completed", { actionId, resultHash: required8(input.resultHash, "resultHash"), durationMs: duration3(input.durationMs), ...input.runtimeEvidence ? { runtimeEvidence: input.runtimeEvidence } : {} });
1352
2927
  pending.delete(actionId);
1353
2928
  return event;
1354
2929
  };
1355
2930
  const failAction = (input) => {
1356
2931
  open();
1357
- const actionId = required(input.actionId, "actionId");
2932
+ const actionId = required8(input.actionId, "actionId");
1358
2933
  if (!pending.has(actionId)) fail(`Tool action is not pending: ${actionId}.`, "INVALID_STATE");
1359
2934
  if (typeof input.retryable !== "boolean") fail("retryable must be boolean.", "INVALID_INPUT");
1360
- const event = append("tool.failed", { actionId, errorCode: required(input.errorCode, "errorCode"), retryable: input.retryable, durationMs: duration(input.durationMs), ...input.runtimeEvidence ? { runtimeEvidence: input.runtimeEvidence } : {} });
2935
+ const event = append("tool.failed", { actionId, errorCode: required8(input.errorCode, "errorCode"), retryable: input.retryable, durationMs: duration3(input.durationMs), ...input.runtimeEvidence ? { runtimeEvidence: input.runtimeEvidence } : {} });
1361
2936
  pending.delete(actionId);
1362
2937
  return event;
1363
2938
  };
@@ -1365,24 +2940,24 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
1365
2940
  sessionId: id2,
1366
2941
  startTurn: (inputHash, turnId = randomUUID()) => {
1367
2942
  open();
1368
- const turn = required(turnId, "turnId");
2943
+ const turn = required8(turnId, "turnId");
1369
2944
  if (turns.has(turn)) fail(`Turn already exists: ${turn}.`, "INVALID_STATE");
1370
- const event = append("agent.turn.started", { turnId: turn, inputHash: required(inputHash, "inputHash") });
2945
+ const event = append("agent.turn.started", { turnId: turn, inputHash: required8(inputHash, "inputHash") });
1371
2946
  turns.add(turn);
1372
2947
  return event;
1373
2948
  },
1374
2949
  requestTool: (input) => {
1375
2950
  open();
1376
- const turnId = required(input.turnId, "turnId");
2951
+ const turnId = required8(input.turnId, "turnId");
1377
2952
  if (!turns.has(turnId)) fail(`Turn does not exist: ${turnId}.`, "INVALID_STATE");
1378
- const actionId = required(input.actionId ?? randomUUID(), "actionId");
2953
+ const actionId = required8(input.actionId ?? randomUUID(), "actionId");
1379
2954
  if (actions.has(actionId)) fail(`Tool action already exists: ${actionId}.`, "INVALID_STATE");
1380
- const toolId = required(input.toolId, "toolId");
1381
- const argumentsHash = required(input.argumentsHash, "argumentsHash");
2955
+ const toolId = required8(input.toolId, "toolId");
2956
+ const argumentsHash = required8(input.argumentsHash, "argumentsHash");
1382
2957
  const decision = policy.evaluate({ actionId, turnId, toolId, argumentsHash });
1383
2958
  if (!decision || decision.decision !== "allow" && decision.decision !== "block" && decision.decision !== "approve") fail("Policy decision is invalid.", "HARNESS_ERROR");
1384
- const policyId = required(decision.policyId, "policyId");
1385
- const reason = required(decision.reason, "policy reason");
2959
+ const policyId = required8(decision.policyId, "policyId");
2960
+ const reason = required8(decision.reason, "policy reason");
1386
2961
  append("policy.evaluated", { actionId, turnId, toolId, decision: decision.decision, policyId, reason });
1387
2962
  actions.add(actionId);
1388
2963
  if (decision.decision === "block") {
@@ -1400,7 +2975,7 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
1400
2975
  },
1401
2976
  approveTool: (input) => {
1402
2977
  open();
1403
- const actionId = required(input.actionId, "actionId");
2978
+ const actionId = required8(input.actionId, "actionId");
1404
2979
  const approval = approvals.get(actionId) ?? fail(`Tool action is not awaiting human approval: ${actionId}.`, "INVALID_STATE");
1405
2980
  if (input.actor !== void 0 && input.actor !== "human") fail("Tool approval requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
1406
2981
  const decision = input.decision;
@@ -1414,7 +2989,7 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
1414
2989
  },
1415
2990
  recoverTool: (input) => {
1416
2991
  open();
1417
- const actionId = required(input.actionId, "actionId");
2992
+ const actionId = required8(input.actionId, "actionId");
1418
2993
  const action = pending.get(actionId) ?? fail(`Tool action is not pending: ${actionId}.`, "INVALID_STATE");
1419
2994
  if (!action.executionStarted) fail(`Tool action does not require recovery: ${actionId}.`, "INVALID_STATE");
1420
2995
  if (input.actor !== void 0 && input.actor !== "human") fail("Tool recovery requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
@@ -1432,7 +3007,7 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
1432
3007
  failTool: failAction,
1433
3008
  executeTool: async (input) => {
1434
3009
  open();
1435
- const actionId = required(input.actionId, "actionId");
3010
+ const actionId = required8(input.actionId, "actionId");
1436
3011
  const action = pending.get(actionId) ?? fail(`Tool action is not pending: ${actionId}.`, "INVALID_STATE");
1437
3012
  if (action.executionStarted) fail(`Tool action requires human recovery decision: ${actionId}.`, "HUMAN_APPROVAL_REQUIRED");
1438
3013
  if (executing.has(actionId)) fail(`Tool action is already executing: ${actionId}.`, "INVALID_STATE");
@@ -1475,8 +3050,8 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
1475
3050
  return recorder;
1476
3051
  };
1477
3052
 
1478
- // src/policy.ts
1479
- var required2 = (value, label) => {
3053
+ // src/kernel/policy.ts
3054
+ var required9 = (value, label) => {
1480
3055
  if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1481
3056
  return value.trim();
1482
3057
  };
@@ -1484,29 +3059,30 @@ var createPolicyGate = ({ rules }) => {
1484
3059
  if (!Array.isArray(rules)) fail("Policy rules must be an array.", "INVALID_INPUT");
1485
3060
  const normalized = rules.map((rule, index2) => {
1486
3061
  if (typeof rule !== "object" || rule === null || Array.isArray(rule)) fail(`rules[${index2}] must be an object.`, "INVALID_INPUT");
1487
- const id2 = required2(rule.id, `rules[${index2}].id`);
3062
+ const id2 = required9(rule.id, `rules[${index2}].id`);
1488
3063
  if (rule.effect !== "allow" && rule.effect !== "block" && rule.effect !== "approve") fail(`rules[${index2}].effect is invalid.`, "INVALID_INPUT");
1489
3064
  if (!Array.isArray(rule.toolIds) || !rule.toolIds.length || rule.toolIds.some((toolId) => typeof toolId !== "string" || !toolId.trim())) fail(`rules[${index2}].toolIds must contain non-empty strings.`, "INVALID_INPUT");
1490
- return { id: id2, effect: rule.effect, toolIds: rule.toolIds.map((toolId) => required2(toolId, `rules[${index2}].toolIds`)), reason: required2(rule.reason, `rules[${index2}].reason`) };
3065
+ return { id: id2, effect: rule.effect, toolIds: rule.toolIds.map((toolId) => required9(toolId, `rules[${index2}].toolIds`)), reason: required9(rule.reason, `rules[${index2}].reason`) };
1491
3066
  });
1492
3067
  if (new Set(normalized.map((rule) => rule.id)).size !== normalized.length) fail("Policy rules must have unique ids.", "INVALID_INPUT");
1493
3068
  return {
1494
3069
  evaluate: (request) => {
1495
3070
  if (typeof request !== "object" || request === null || Array.isArray(request)) fail("Policy request must be an object.", "INVALID_INPUT");
1496
- required2(request.actionId, "request.actionId");
1497
- required2(request.turnId, "request.turnId");
1498
- const toolId = required2(request.toolId, "request.toolId");
1499
- required2(request.argumentsHash, "request.argumentsHash");
3071
+ required9(request.actionId, "request.actionId");
3072
+ required9(request.turnId, "request.turnId");
3073
+ const toolId = required9(request.toolId, "request.toolId");
3074
+ required9(request.argumentsHash, "request.argumentsHash");
1500
3075
  const rule = normalized.find((candidate) => candidate.toolIds.includes(toolId));
1501
3076
  return rule ? { decision: rule.effect, policyId: rule.id, reason: rule.reason } : { decision: "block", policyId: "default-deny", reason: `No policy rule allows tool: ${toolId}.` };
1502
3077
  }
1503
3078
  };
1504
3079
  };
1505
- var required3 = (value, label) => {
3080
+ var createConfiguredToolRuntime = ({ runtime, process: process2, docker }) => runtime.kind === "docker" ? createDockerToolRuntime(docker) : createProcessToolRuntime(process2);
3081
+ var required10 = (value, label) => {
1506
3082
  if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1507
3083
  return value.trim();
1508
3084
  };
1509
- var duration2 = (value) => {
3085
+ var duration4 = (value) => {
1510
3086
  if (!Number.isFinite(value) || value < 0) fail("Tool durationMs must be a non-negative number.", "INVALID_INPUT");
1511
3087
  return value;
1512
3088
  };
@@ -1516,7 +3092,7 @@ var positiveNumber = (value, label) => {
1516
3092
  return normalized;
1517
3093
  };
1518
3094
  var absolutePath = (value, label) => {
1519
- const normalized = required3(value, label);
3095
+ const normalized = required10(value, label);
1520
3096
  if (!normalized.startsWith("/") || normalized.includes(",")) fail(`${label} must be an absolute path without commas.`, "INVALID_INPUT");
1521
3097
  return normalized;
1522
3098
  };
@@ -1534,35 +3110,38 @@ var createToolRuntime = ({ tools, timeoutMs = 3e4 }) => {
1534
3110
  if (!Number.isInteger(timeoutMs) || timeoutMs < 1) fail("Runtime timeoutMs must be a positive integer.", "INVALID_INPUT");
1535
3111
  const normalized = tools.map((tool, index2) => {
1536
3112
  if (typeof tool !== "object" || tool === null || Array.isArray(tool)) fail(`tools[${index2}] must be an object.`, "INVALID_INPUT");
1537
- const toolId = required3(tool.toolId, `tools[${index2}].toolId`);
3113
+ const toolId = required10(tool.toolId, `tools[${index2}].toolId`);
1538
3114
  if (typeof tool.execute !== "function") fail(`tools[${index2}].execute is required.`, "INVALID_INPUT");
1539
3115
  return { toolId, execute: tool.execute };
1540
3116
  });
1541
3117
  if (new Set(normalized.map((tool) => tool.toolId)).size !== normalized.length) fail("Runtime tools must have unique ids.", "INVALID_INPUT");
1542
3118
  return {
3119
+ assurance: "contract-tested",
3120
+ isolation: "none",
3121
+ telemetry: () => ({ status: "unknown" }),
1543
3122
  execute: async (request) => {
1544
3123
  const started = Date.now();
1545
- const actionId = required3(request.actionId, "request.actionId");
1546
- const turnId = required3(request.turnId, "request.turnId");
1547
- const toolId = required3(request.toolId, "request.toolId");
1548
- const argumentsHash = required3(request.argumentsHash, "request.argumentsHash");
3124
+ const actionId = required10(request.actionId, "request.actionId");
3125
+ const turnId = required10(request.turnId, "request.turnId");
3126
+ const toolId = required10(request.toolId, "request.toolId");
3127
+ const argumentsHash = required10(request.argumentsHash, "request.argumentsHash");
1549
3128
  const tool = normalized.find((candidate) => candidate.toolId === toolId);
1550
- if (!tool) return { status: "failed", errorCode: "TOOL_NOT_FOUND", retryable: false, durationMs: duration2(Date.now() - started) };
3129
+ if (!tool) return { status: "failed", errorCode: "TOOL_NOT_FOUND", retryable: false, durationMs: duration4(Date.now() - started) };
1551
3130
  const controller = new AbortController();
1552
3131
  let timedOut = false;
1553
3132
  let timer;
1554
3133
  try {
1555
- const timeout = new Promise((_, reject) => {
3134
+ const timeout2 = new Promise((_, reject) => {
1556
3135
  timer = setTimeout(() => {
1557
3136
  timedOut = true;
1558
3137
  controller.abort();
1559
3138
  reject(new Error("Tool execution timed out."));
1560
3139
  }, timeoutMs);
1561
3140
  });
1562
- const result = await Promise.race([Promise.resolve(tool.execute({ actionId, turnId, toolId, argumentsHash, arguments: request.arguments, signal: controller.signal })), timeout]);
1563
- return { status: "completed", resultHash: hashJson(result === void 0 ? null : result), durationMs: duration2(Date.now() - started) };
3141
+ const result = await Promise.race([Promise.resolve(tool.execute({ actionId, turnId, toolId, argumentsHash, arguments: request.arguments, signal: controller.signal })), timeout2]);
3142
+ return { status: "completed", resultHash: hashJson(result === void 0 ? null : result), durationMs: duration4(Date.now() - started) };
1564
3143
  } catch {
1565
- return { status: "failed", errorCode: timedOut ? "TIMEOUT" : "RUNTIME_ERROR", retryable: true, durationMs: duration2(Date.now() - started) };
3144
+ return { status: "failed", errorCode: timedOut ? "TIMEOUT" : "RUNTIME_ERROR", retryable: true, durationMs: duration4(Date.now() - started) };
1566
3145
  } finally {
1567
3146
  if (timer) clearTimeout(timer);
1568
3147
  }
@@ -1575,20 +3154,23 @@ var createProcessToolRuntime = ({ tools, timeoutMs = 3e4, maxOutputBytes = 10485
1575
3154
  if (!Number.isInteger(maxOutputBytes) || maxOutputBytes < 1) fail("Process runtime maxOutputBytes must be a positive integer.", "INVALID_INPUT");
1576
3155
  const normalized = tools.map((tool, index2) => {
1577
3156
  if (typeof tool !== "object" || tool === null || Array.isArray(tool)) fail(`tools[${index2}] must be an object.`, "INVALID_INPUT");
1578
- const toolId = required3(tool.toolId, `tools[${index2}].toolId`);
1579
- const command = required3(tool.command, `tools[${index2}].command`);
3157
+ const toolId = required10(tool.toolId, `tools[${index2}].toolId`);
3158
+ const command = required10(tool.command, `tools[${index2}].command`);
1580
3159
  if (tool.args !== void 0 && (!Array.isArray(tool.args) || tool.args.some((arg) => typeof arg !== "string"))) fail(`tools[${index2}].args must contain strings.`, "INVALID_INPUT");
1581
3160
  if (tool.env !== void 0 && (typeof tool.env !== "object" || tool.env === null || Array.isArray(tool.env) || Object.values(tool.env).some((value) => typeof value !== "string"))) fail(`tools[${index2}].env must contain string values.`, "INVALID_INPUT");
1582
3161
  return { toolId, command, args: tool.args ? [...tool.args] : [], ...tool.cwd ? { cwd: tool.cwd } : {}, env: tool.env ? { ...tool.env } : { PATH: process.env["PATH"] ?? "" } };
1583
3162
  });
1584
3163
  if (new Set(normalized.map((tool) => tool.toolId)).size !== normalized.length) fail("Process runtime tools must have unique ids.", "INVALID_INPUT");
1585
3164
  return {
3165
+ assurance: "contract-tested",
3166
+ isolation: "none",
3167
+ telemetry: () => ({ status: "unknown" }),
1586
3168
  execute: async (request) => {
1587
3169
  const started = Date.now();
1588
- const actionId = required3(request.actionId, "request.actionId");
1589
- const turnId = required3(request.turnId, "request.turnId");
1590
- const toolId = required3(request.toolId, "request.toolId");
1591
- const argumentsHash = required3(request.argumentsHash, "request.argumentsHash");
3170
+ const actionId = required10(request.actionId, "request.actionId");
3171
+ const turnId = required10(request.turnId, "request.turnId");
3172
+ const toolId = required10(request.toolId, "request.toolId");
3173
+ const argumentsHash = required10(request.argumentsHash, "request.argumentsHash");
1592
3174
  const tool = normalized.find((candidate) => candidate.toolId === toolId);
1593
3175
  if (!tool) return { status: "failed", errorCode: "TOOL_NOT_FOUND", retryable: false, durationMs: Date.now() - started };
1594
3176
  let input;
@@ -1598,7 +3180,7 @@ var createProcessToolRuntime = ({ tools, timeoutMs = 3e4, maxOutputBytes = 10485
1598
3180
  return { status: "failed", errorCode: "SERIALIZATION_ERROR", retryable: false, durationMs: Date.now() - started };
1599
3181
  }
1600
3182
  return new Promise((resolve6) => {
1601
- const child = spawn(tool.command, tool.args, { cwd: tool.cwd, env: tool.env, shell: false, stdio: ["pipe", "pipe", "pipe"] });
3183
+ const child = spawn(tool.command, [...tool.args], { cwd: tool.cwd, env: tool.env, shell: false, stdio: ["pipe", "pipe", "pipe"] });
1602
3184
  let stdout = "";
1603
3185
  let timedOut = false;
1604
3186
  let outputLimit = false;
@@ -1658,17 +3240,17 @@ var createDockerToolRuntime = ({
1658
3240
  pull = "never"
1659
3241
  }) => {
1660
3242
  if (!Array.isArray(tools)) fail("Docker runtime tools must be an array.", "INVALID_INPUT");
1661
- const command = required3(dockerCommand, "dockerCommand");
1662
- const memory = required3(memoryLimit, "memoryLimit");
3243
+ const command = required10(dockerCommand, "dockerCommand");
3244
+ const memory = required10(memoryLimit, "memoryLimit");
1663
3245
  const cpu = positiveNumber(cpus, "cpus");
1664
3246
  if (!Number.isInteger(pidsLimit) || pidsLimit < 1) fail("pidsLimit must be a positive integer.", "INVALID_INPUT");
1665
- const normalizedUser = required3(user, "user");
3247
+ const normalizedUser = required10(user, "user");
1666
3248
  if (normalizedUser.includes(" ")) fail("user must not contain spaces.", "INVALID_INPUT");
1667
3249
  if (pull !== "never" && pull !== "missing" && pull !== "always") fail("pull must be never, missing, or always.", "INVALID_INPUT");
1668
3250
  const normalized = tools.map((tool, index2) => {
1669
3251
  if (typeof tool !== "object" || tool === null || Array.isArray(tool)) fail(`tools[${index2}] must be an object.`, "INVALID_INPUT");
1670
- const toolId = required3(tool.toolId, `tools[${index2}].toolId`);
1671
- const image = required3(tool.image, `tools[${index2}].image`);
3252
+ const toolId = required10(tool.toolId, `tools[${index2}].toolId`);
3253
+ const image = required10(tool.image, `tools[${index2}].image`);
1672
3254
  if (!Array.isArray(tool.command) || tool.command.length === 0 || tool.command.some((part) => typeof part !== "string" || !part.trim())) fail(`tools[${index2}].command must be a non-empty string array.`, "INVALID_INPUT");
1673
3255
  if (tool.args !== void 0 && (!Array.isArray(tool.args) || tool.args.some((arg) => typeof arg !== "string"))) fail(`tools[${index2}].args must contain strings.`, "INVALID_INPUT");
1674
3256
  const env = dockerEnvironment(tool.env, `tools[${index2}].env`);
@@ -1720,6 +3302,9 @@ var createDockerToolRuntime = ({
1720
3302
  if (new Set(normalized.map((tool) => tool.toolId)).size !== normalized.length) fail("Docker runtime tools must have unique ids.", "INVALID_INPUT");
1721
3303
  const processRuntime = createProcessToolRuntime({ tools: normalized, timeoutMs, maxOutputBytes });
1722
3304
  return {
3305
+ assurance: "runtime-attested",
3306
+ isolation: "sandboxed",
3307
+ telemetry: () => ({ status: "unknown" }),
1723
3308
  execute: async (request) => {
1724
3309
  const tool = normalized.find((candidate) => candidate.toolId === request.toolId);
1725
3310
  if (!tool) return processRuntime.execute(request);
@@ -1730,7 +3315,7 @@ var createDockerToolRuntime = ({
1730
3315
  imageDigest = inspected.stdout.trim();
1731
3316
  if (!/^sha256:[a-f0-9]{64}$/.test(imageDigest)) throw new Error("Docker image inspection did not return a digest.");
1732
3317
  } catch {
1733
- return { status: "failed", errorCode: "IMAGE_UNAVAILABLE", retryable: true, durationMs: duration2(Date.now() - started), runtimeEvidence: tool.evidence };
3318
+ return { status: "failed", errorCode: "IMAGE_UNAVAILABLE", retryable: true, durationMs: duration4(Date.now() - started), runtimeEvidence: tool.evidence };
1734
3319
  }
1735
3320
  const runtimeEvidence = { ...tool.evidence, imageDigest, profileHash: hashJson({ ...tool.evidence, imageDigest }) };
1736
3321
  const result = await processRuntime.execute(request);
@@ -1738,8 +3323,343 @@ var createDockerToolRuntime = ({
1738
3323
  }
1739
3324
  };
1740
3325
  };
3326
+ var required11 = (value, label) => {
3327
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
3328
+ return value.trim();
3329
+ };
3330
+ var safeKey = (identity) => hashJson(identity);
3331
+ var now3 = () => (/* @__PURE__ */ new Date()).toISOString();
3332
+ var parse = (value, label) => {
3333
+ try {
3334
+ const raw = JSON.parse(value);
3335
+ const identity = {
3336
+ tracker: required11(raw["tracker"], `${label}.tracker`),
3337
+ repository: required11(raw["repository"], `${label}.repository`),
3338
+ issue: required11(raw["issue"], `${label}.issue`),
3339
+ worktree: required11(raw["worktree"], `${label}.worktree`),
3340
+ branch: required11(raw["branch"], `${label}.branch`)
3341
+ };
3342
+ return { ...identity, key: required11(raw["key"], `${label}.key`), leaseId: required11(raw["leaseId"], `${label}.leaseId`), owner: required11(raw["owner"], `${label}.owner`), claimedAt: required11(raw["claimedAt"], `${label}.claimedAt`) };
3343
+ } catch (error) {
3344
+ if (error instanceof SyntaxError) fail(`${label} contains invalid JSON.`, "HARNESS_ERROR");
3345
+ throw error;
3346
+ }
3347
+ };
3348
+ var createDispatchLedger = (stateDir) => {
3349
+ const root = required11(stateDir, "stateDir");
3350
+ const claimsDir = join(root, "coordination", "claims");
3351
+ const ledgerPath = join(root, "coordination", "dispatch-ledger.ndjson");
3352
+ mkdirSync(claimsDir, { recursive: true });
3353
+ const claimPath = (key) => join(claimsDir, `${key}.json`);
3354
+ const append = (record3) => appendFileSync(ledgerPath, `${JSON.stringify(record3)}
3355
+ `, "utf8");
3356
+ const records = () => {
3357
+ if (!existsSync(ledgerPath)) return [];
3358
+ return readFileSync(ledgerPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line, index2) => {
3359
+ try {
3360
+ return JSON.parse(line);
3361
+ } catch {
3362
+ return fail(`Dispatch ledger record ${index2 + 1} is invalid JSON.`, "HARNESS_ERROR");
3363
+ }
3364
+ });
3365
+ };
3366
+ const active = () => {
3367
+ const byKey = /* @__PURE__ */ new Map();
3368
+ for (const record3 of records()) {
3369
+ if (record3.action === "release" || record3.action === "recover") byKey.delete(record3.key);
3370
+ else if (record3.action === "dispatch") byKey.set(record3.key, record3);
3371
+ }
3372
+ return [...byKey.values()];
3373
+ };
3374
+ return {
3375
+ claim: (input) => {
3376
+ const identity = {
3377
+ tracker: required11(input.tracker, "tracker"),
3378
+ repository: required11(input.repository, "repository"),
3379
+ issue: required11(input.issue, "issue"),
3380
+ worktree: required11(input.worktree, "worktree"),
3381
+ branch: required11(input.branch, "branch")
3382
+ };
3383
+ const owner = required11(input.owner, "owner");
3384
+ const key = safeKey(identity);
3385
+ const path = claimPath(key);
3386
+ if (existsSync(path)) return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
3387
+ const lease = { ...identity, key, leaseId: randomUUID(), owner, claimedAt: now3() };
3388
+ let fd;
3389
+ try {
3390
+ fd = openSync(path, "wx");
3391
+ } catch (error) {
3392
+ if (error.code === "EEXIST") return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
3393
+ throw error;
3394
+ }
3395
+ try {
3396
+ writeFileSync(fd, JSON.stringify(lease), "utf8");
3397
+ } finally {
3398
+ closeSync(fd);
3399
+ }
3400
+ append({ ...lease, action: "dispatch", at: lease.claimedAt });
3401
+ return { decision: "claimed", lease };
3402
+ },
3403
+ recordDispatch: ({ lease, idempotencyKey, commandDigest }) => {
3404
+ const id2 = required11(idempotencyKey, "idempotencyKey");
3405
+ const digest6 = required11(commandDigest, "commandDigest");
3406
+ const existing = records().find((record4) => record4.action === "dispatch" && record4.idempotencyKey === id2);
3407
+ if (existing) return { decision: "duplicate", record: existing };
3408
+ const record3 = { ...lease, action: "dispatch", at: now3(), idempotencyKey: id2, commandDigest: digest6 };
3409
+ append(record3);
3410
+ return { decision: "recorded", record: record3 };
3411
+ },
3412
+ release: (lease, reason = "lease released") => {
3413
+ const path = claimPath(required11(lease.key, "lease.key"));
3414
+ if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
3415
+ const current = parse(readFileSync(path, "utf8"), "claim");
3416
+ if (current.leaseId !== lease.leaseId) fail("Dispatch lease owner does not match.", "INVALID_STATE");
3417
+ unlinkSync(path);
3418
+ const record3 = { ...current, action: "release", at: now3(), reason: required11(reason, "reason") };
3419
+ append(record3);
3420
+ return record3;
3421
+ },
3422
+ recover: (key, input) => {
3423
+ if (input.actor !== "human") fail("Dispatch lease recovery requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
3424
+ const normalizedKey = required11(key, "key");
3425
+ const maxAgeMs = input.maxAgeMs ?? 3e5;
3426
+ if (!Number.isInteger(maxAgeMs) || maxAgeMs < 0) fail("maxAgeMs must be a non-negative integer.", "INVALID_INPUT");
3427
+ const path = claimPath(normalizedKey);
3428
+ if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
3429
+ const current = parse(readFileSync(path, "utf8"), "claim");
3430
+ if (Date.now() - Date.parse(current.claimedAt) < maxAgeMs) fail("Dispatch lease is not old enough to recover.", "HARNESS_ERROR");
3431
+ unlinkSync(path);
3432
+ const record3 = { ...current, action: "recover", at: now3(), reason: required11(input.reason, "reason") };
3433
+ append(record3);
3434
+ return record3;
3435
+ },
3436
+ active,
3437
+ records
3438
+ };
3439
+ };
3440
+ var DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx", ".txt", ".adoc", ".rst"]);
3441
+ var TEST_SUFFIXES = [".test.", ".spec.", "__tests__"];
3442
+ var SHELL_META = /[;&|`$()<>\n\r]/;
3443
+ var normalizedPath = (value, label) => {
3444
+ if (typeof value !== "string" || !value.trim()) fail(`${label} must be a non-empty path.`, "INVALID_INPUT");
3445
+ const path = value.trim().replaceAll("\\", "/");
3446
+ if (path.startsWith("/") || path.split("/").includes("..")) fail(`${label} must be repository-relative.`, "INVALID_INPUT");
3447
+ return path;
3448
+ };
3449
+ var validateSafeCommand = (command) => {
3450
+ if (typeof command !== "string" || !command.trim()) fail("command must be a non-empty string.", "INVALID_INPUT");
3451
+ const value = command.trim();
3452
+ if (SHELL_META.test(value)) fail("command contains shell metacharacters; use argv-based execution.", "POLICY_BLOCKED");
3453
+ return { valid: true, command: value };
3454
+ };
3455
+ var isTest = (path) => TEST_SUFFIXES.some((suffix) => path.includes(suffix)) || /(^|\/)(test|tests|__tests__)\//.test(path);
3456
+ var isDoc = (path) => DOC_EXTENSIONS.has(extname(path).toLowerCase());
3457
+ var planFilePreflight = (files, options = {}) => {
3458
+ if (!Array.isArray(files)) fail("files must be an array.", "INVALID_INPUT");
3459
+ const unique2 = [...new Set(files.map((file, index2) => normalizedPath(file.path, `files[${index2}].path`)))].sort();
3460
+ const codeFiles = unique2.filter((path) => !isDoc(path) && !isTest(path));
3461
+ const existingTests = unique2.filter(isTest);
3462
+ const roots = (options.testRoots ?? ["test", "tests", "__tests__"]).map((root, index2) => normalizedPath(root, `testRoots[${index2}]`));
3463
+ const colocated = options.includeTests === false ? [] : codeFiles.flatMap((path) => {
3464
+ const file = basename(path);
3465
+ const directory = dirname(path);
3466
+ const stem = file.includes(".") ? file.slice(0, file.lastIndexOf(".")) : file;
3467
+ return [join(directory, `${stem}.test.ts`), join(directory, `${stem}.spec.ts`)].filter((candidate) => unique2.includes(candidate));
3468
+ });
3469
+ const testFiles = [...new Set([...existingTests, ...colocated, ...unique2.filter((path) => roots.some((root) => path === root || path.startsWith(`${root}/`)))].sort())];
3470
+ const docsOnly = unique2.length > 0 && codeFiles.length === 0 && existingTests.length === 0;
3471
+ return { files: unique2, codeFiles, testFiles, docsOnly, checks: docsOnly ? [] : ["lint", "typecheck", ...testFiles.length ? ["test"] : []] };
3472
+ };
3473
+
3474
+ // src/kernel/block.ts
3475
+ var BLOCK_STATUSES = ["todo", "picked", "development", "validation", "pr-open", "merged", "post-merge", "done", "blocked", "scope-cut"];
3476
+ var text5 = (value, label) => {
3477
+ return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
3478
+ };
3479
+ var list = (value, label) => {
3480
+ if (!Array.isArray(value)) return fail(`${label} must be an array of non-empty strings.`, "INVALID_INPUT");
3481
+ if (!value.every((item) => typeof item === "string" && Boolean(item.trim()))) return fail(`${label} must be an array of non-empty strings.`, "INVALID_INPUT");
3482
+ return [...new Set(value.map((item) => item.trim()))];
3483
+ };
3484
+ var validateBlockManifest = (value) => {
3485
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("block manifest must be an object.", "INVALID_INPUT");
3486
+ const raw = value;
3487
+ if (raw["schemaVersion"] !== 1) fail("block manifest schemaVersion must be 1.", "INVALID_INPUT");
3488
+ const criteria = list(raw["acceptanceCriteria"], "acceptanceCriteria");
3489
+ if (!criteria.length) fail("acceptanceCriteria must not be empty.", "INVALID_INPUT");
3490
+ const dependencies = list(raw["dependencies"] ?? [], "dependencies");
3491
+ const wave = raw["wave"];
3492
+ if (!Number.isInteger(wave) || wave < 1) fail("wave must be a positive integer.", "INVALID_INPUT");
3493
+ const status = raw["status"];
3494
+ if (!BLOCK_STATUSES.includes(status)) fail("status is invalid.", "INVALID_INPUT");
3495
+ const budgetRaw = raw["budget"];
3496
+ let budget;
3497
+ if (budgetRaw !== void 0) {
3498
+ if (typeof budgetRaw !== "object" || budgetRaw === null || Array.isArray(budgetRaw)) fail("budget must be an object.", "INVALID_INPUT");
3499
+ const candidate = budgetRaw;
3500
+ 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");
3501
+ budget = { ...candidate["maxMinutes"] === void 0 ? {} : { maxMinutes: candidate["maxMinutes"] }, ...candidate["maxAttempts"] === void 0 ? {} : { maxAttempts: candidate["maxAttempts"] } };
3502
+ }
3503
+ return { schemaVersion: 1, id: text5(raw["id"], "id"), title: text5(raw["title"], "title"), tracker: text5(raw["tracker"], "tracker"), repository: text5(raw["repository"], "repository"), acceptanceCriteria: criteria, dependencies, wave, status, ...budget ? { budget } : {}, ...raw["humanGates"] === void 0 ? {} : { humanGates: list(raw["humanGates"], "humanGates") }, ...raw["sourceHash"] === void 0 ? {} : { sourceHash: text5(raw["sourceHash"], "sourceHash") } };
3504
+ };
3505
+ var assessBlock = (manifest, completedDependencies = []) => {
3506
+ const value = validateBlockManifest(manifest);
3507
+ const completed = new Set(completedDependencies.map((item) => text5(item, "completedDependencies[]")));
3508
+ const blockers = value.dependencies.filter((dependency) => !completed.has(dependency));
3509
+ 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."];
3510
+ return { status: blockers.length || value.status === "blocked" ? "blocked" : "ready", manifestHash: hashJson(value), blockers, next };
3511
+ };
3512
+ var LEARNING_STATUSES = ["proposed", "promoted", "rejected"];
3513
+ var text6 = (value, label) => {
3514
+ return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
3515
+ };
3516
+ var category = (heading) => {
3517
+ const value = heading.toLowerCase();
3518
+ if (/went well|success|worked/.test(value)) return "worked";
3519
+ if (/problem|failed|blocker|pain/.test(value)) return "problem";
3520
+ if (/adjust|action|next|improv/.test(value)) return "adjustment";
3521
+ return "other";
3522
+ };
3523
+ var parseRetro = (markdown, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) => {
3524
+ const input = text6(markdown, "markdown");
3525
+ const origin = text6(source, "source");
3526
+ if (!Number.isFinite(Date.parse(recordedAt))) fail("recordedAt must be a valid timestamp.", "INVALID_INPUT");
3527
+ const records = [];
3528
+ let current = "other";
3529
+ for (const line of input.split(/\r?\n/)) {
3530
+ const heading = line.match(/^#{1,6}\s+(.+)$/);
3531
+ if (heading) {
3532
+ current = category(heading[1] ?? "");
3533
+ continue;
3534
+ }
3535
+ const item = line.match(/^\s*[-*]\s+(?:\[[ xX]\]\s+)?(.+?)\s*$/);
3536
+ if (!item?.[1]?.trim()) continue;
3537
+ const value = item[1].trim();
3538
+ const id2 = `L-${createHash("sha256").update(`${origin}|${current}|${value}`).digest("hex").slice(0, 12)}`;
3539
+ if (!records.some((record3) => record3.id === id2)) records.push({ id: id2, source: origin, category: current, text: value, status: "proposed", recordedAt });
3540
+ }
3541
+ return records;
3542
+ };
3543
+ var promoteLearnings = (records, input) => {
3544
+ if (input.actor !== "human") fail("Learning promotion requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
3545
+ const ids = new Set(input.ids.map((id2) => text6(id2, "ids[]")));
3546
+ const status = input.status ?? "promoted";
3547
+ const result = records.map((record3) => ids.has(record3.id) ? { ...record3, status } : record3);
3548
+ const unknown = [...ids].filter((id2) => !records.some((record3) => record3.id === id2));
3549
+ if (unknown.length) fail(`Unknown learning IDs: ${unknown.join(", ")}`, "INVALID_INPUT");
3550
+ return result;
3551
+ };
3552
+
3553
+ // src/kernel/status.ts
3554
+ var required12 = (value, label) => {
3555
+ return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
3556
+ };
3557
+ var createStatusSnapshot = (input) => {
3558
+ const sourceRevision = required12(input.sourceRevision, "sourceRevision");
3559
+ if (!Number.isFinite(Date.parse(input.generatedAt))) fail("generatedAt must be a valid timestamp.", "INVALID_INPUT");
3560
+ if (!Array.isArray(input.blocks)) fail("blocks must be an array.", "INVALID_INPUT");
3561
+ const blocks = input.blocks.map((block, index2) => {
3562
+ if (typeof block !== "object" || block === null || Array.isArray(block)) fail(`blocks[${index2}] must be an object.`, "INVALID_INPUT");
3563
+ const value = block;
3564
+ if (!(typeof value.id === "string" && value.id.trim())) fail(`blocks[${index2}].id is required.`, "INVALID_INPUT");
3565
+ 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");
3566
+ return { ...value, id: value.id.trim() };
3567
+ }).sort((left, right) => left.id.localeCompare(right.id));
3568
+ 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");
3569
+ const body3 = { schemaVersion: 1, generatedAt: input.generatedAt, sourceRevision, blocks, ...input.machine ? { machine: input.machine } : {}, ...input.metrics ? { metrics: input.metrics } : {}, ...input.next ? { next: required12(input.next, "next") } : {} };
3570
+ return { ...body3, digest: hashJson(body3) };
3571
+ };
3572
+ var validateStatusSnapshot = (value) => {
3573
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("status snapshot must be an object.", "INVALID_INPUT");
3574
+ const raw = value;
3575
+ const snapshot = createStatusSnapshot({ generatedAt: required12(raw.generatedAt, "generatedAt"), sourceRevision: required12(raw.sourceRevision, "sourceRevision"), blocks: raw.blocks, ...raw.machine ? { machine: raw.machine } : {}, ...raw.metrics ? { metrics: raw.metrics } : {}, ...raw.next ? { next: raw.next } : {} });
3576
+ if (raw.schemaVersion !== 1 || raw.digest !== snapshot.digest) fail("status snapshot digest or schemaVersion is invalid.", "HARNESS_ERROR");
3577
+ return snapshot;
3578
+ };
3579
+
3580
+ // src/kernel/model-policy.ts
3581
+ var MODEL_ROLES = ["orchestrator", "reviewer", "builder", "watcher"];
3582
+ var required13 = (value, label) => {
3583
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
3584
+ return value.trim();
3585
+ };
3586
+ var createModelPolicy = (bindings) => {
3587
+ if (!Array.isArray(bindings) || !bindings.length) fail("bindings must be a non-empty array.", "INVALID_INPUT");
3588
+ const normalized = bindings.map((binding2, index2) => {
3589
+ if (typeof binding2 !== "object" || binding2 === null || Array.isArray(binding2)) fail(`bindings[${index2}] must be an object.`, "INVALID_INPUT");
3590
+ if (!MODEL_ROLES.includes(binding2.role)) fail(`bindings[${index2}].role is invalid.`, "INVALID_INPUT");
3591
+ if (binding2.maxTokens !== void 0 && (!Number.isInteger(binding2.maxTokens) || binding2.maxTokens < 1)) fail(`bindings[${index2}].maxTokens must be a positive integer.`, "INVALID_INPUT");
3592
+ return { role: binding2.role, provider: required13(binding2.provider, `bindings[${index2}].provider`), model: required13(binding2.model, `bindings[${index2}].model`), ...binding2.maxTokens === void 0 ? {} : { maxTokens: binding2.maxTokens } };
3593
+ });
3594
+ if (new Set(normalized.map((binding2) => binding2.role)).size !== normalized.length) fail("Each model role may be bound only once.", "INVALID_INPUT");
3595
+ return { bindings: normalized, digest: hashJson(normalized) };
3596
+ };
3597
+ var modelFor = (policy, role) => policy.bindings.find((binding2) => binding2.role === role) ?? fail(`No model binding exists for role: ${role}.`, "INVALID_STATE");
3598
+
3599
+ // src/adapters/orca.ts
3600
+ var required14 = (value, label) => {
3601
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
3602
+ return value.trim();
3603
+ };
3604
+ var createOrcaDispatchPlan = (input) => {
3605
+ const repository = required14(input.repository, "repository");
3606
+ const worktree = required14(input.worktree, "worktree");
3607
+ const branch = required14(input.branch, "branch");
3608
+ const baseBranch = required14(input.baseBranch, "baseBranch");
3609
+ const goalFile = required14(input.goalFile, "goalFile");
3610
+ const agent = required14(input.agent ?? "default", "agent");
3611
+ const argv = ["orca", "worktree", "create", "--repo", repository, "--name", worktree, "--base-branch", baseBranch, "--agent", agent, "--prompt-file", goalFile];
3612
+ validateSafeCommand(argv.join(" "));
3613
+ const identity = { repository, worktree, branch, baseBranch, goalFile, agent };
3614
+ return { argv, commandDigest: hashJson(argv), idempotencyKey: hashJson(identity) };
3615
+ };
3616
+ var createOrcaLifecycleProjection = (input) => {
3617
+ const issueRef = required14(input.issueRef, "issueRef");
3618
+ const repository = required14(input.repository, "repository");
3619
+ const worktree = required14(input.worktree, "worktree");
3620
+ const branch = required14(input.branch, "branch");
3621
+ if (!["acquired", "resumed", "conflict", "released"].includes(input.leaseState)) fail("leaseState is invalid.", "INVALID_INPUT");
3622
+ if (input.issueLock !== "held" && input.issueLock !== "missing") fail("issueLock is invalid.", "INVALID_INPUT");
3623
+ const expected = input.expectedRemoteSha?.trim();
3624
+ const observed = input.observedRemoteSha?.trim();
3625
+ const remoteShaConfirmed = Boolean(expected && observed && expected === observed);
3626
+ const cleanupAllowed = input.cleanupRequested === true && remoteShaConfirmed && input.leaseState === "released";
3627
+ const status = input.leaseState === "conflict" || input.issueLock === "missing" ? "blocked" : input.cleanupRequested === true && !remoteShaConfirmed ? "escalated" : input.leaseState === "resumed" ? "resume" : "ready";
3628
+ return { status, leaseState: input.leaseState, worktreeKey: hashJson({ issueRef, repository, worktree, branch }), issueLock: input.issueLock, remoteShaConfirmed, cleanupAllowed, assurance: "contract-tested", telemetry: { status: "measured", durationMs: 0 } };
3629
+ };
3630
+
3631
+ // src/adapters/tracking.ts
3632
+ var required15 = (value, label) => {
3633
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
3634
+ return value.trim();
3635
+ };
3636
+ var createTrackingTransition = (input) => {
3637
+ const transition2 = { tracker: required15(input.tracker, "tracker"), issue: required15(input.issue, "issue"), ...input.from ? { from: required15(input.from, "from") } : {}, to: required15(input.to, "to"), reason: required15(input.reason, "reason") };
3638
+ return { ...transition2, idempotencyKey: hashJson(transition2) };
3639
+ };
3640
+ var createTrackingAdapter = (id2, handler, options = {}) => {
3641
+ const adapterId = required15(id2, "id");
3642
+ const completed = /* @__PURE__ */ new Set();
3643
+ let writes = 0;
3644
+ return {
3645
+ id: adapterId,
3646
+ assurance: "contract-tested",
3647
+ telemetry: () => ({ status: "measured", externalMutations: writes }),
3648
+ transition: async (input) => {
3649
+ const transition2 = createTrackingTransition(input);
3650
+ if (!completed.has(transition2.idempotencyKey)) {
3651
+ if (!options.dryRun) {
3652
+ await handler(transition2);
3653
+ writes += 1;
3654
+ }
3655
+ completed.add(transition2.idempotencyKey);
3656
+ }
3657
+ return transition2;
3658
+ }
3659
+ };
3660
+ };
1741
3661
  var EVIDENCE_BUNDLE_SCHEMA_VERSION = 1;
1742
- var body = (bundle) => {
3662
+ var body2 = (bundle) => {
1743
3663
  const { payloadHash: _payloadHash, signature: _signature, ...unsigned } = bundle;
1744
3664
  return unsigned;
1745
3665
  };
@@ -1764,7 +3684,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
1764
3684
  const loaded = loadConfig(configPath);
1765
3685
  const run = requireRun2(runId ? readJson(join(loaded.stateDir, "runs", runId, "run.json")) : loadLatestRun(loaded.stateDir));
1766
3686
  const reconciliation = await reconcileRun({ configPath, runId: run.runId });
1767
- const digest2 = run.verificationDigest ?? fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
3687
+ const digest6 = run.verificationDigest ?? fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
1768
3688
  if (reconciliation.state !== "COMPLETE") fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
1769
3689
  const eventLog = new FileEventStore(loaded.stateDir);
1770
3690
  eventLog.read(run.runId);
@@ -1779,7 +3699,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
1779
3699
  return bundleFile(loaded.stateDir, path);
1780
3700
  });
1781
3701
  const privateKey = createPrivateKey(readFileSync(privateKeyPath));
1782
- 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: digest2, eventLog: eventVerification, files };
3702
+ 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: digest6, eventLog: eventVerification, files };
1783
3703
  const payloadHash = sha256(JSON.stringify(unsigned));
1784
3704
  const publicKeyPem = createPublicKey(privateKey).export({ type: "spki", format: "pem" }).toString();
1785
3705
  const bundle = { ...unsigned, payloadHash, signature: { algorithm: "ed25519", keyId, publicKeyPem, signatureBase64: sign(null, Buffer.from(payloadHash), privateKey).toString("base64") } };
@@ -1804,7 +3724,7 @@ var verifyEvidenceBundle = (path, { trustedKeys = [] } = {}) => {
1804
3724
  if (sha256(content) !== file.sha256) fail(`Evidence bundle file hash mismatch: ${file.path}`, "HARNESS_ERROR");
1805
3725
  }
1806
3726
  if (!paths.has(`runs/${bundle.runId}/run.json`) || !paths.has(`runs/${bundle.runId}/events.ndjson`)) fail("Evidence bundle is missing the run projection or event log.", "HARNESS_ERROR");
1807
- if (sha256(JSON.stringify(body(bundle))) !== bundle.payloadHash) fail("Evidence bundle payload hash mismatch.", "HARNESS_ERROR");
3727
+ if (sha256(JSON.stringify(body2(bundle))) !== bundle.payloadHash) fail("Evidence bundle payload hash mismatch.", "HARNESS_ERROR");
1808
3728
  let valid = false;
1809
3729
  try {
1810
3730
  valid = verify(null, Buffer.from(bundle.payloadHash), createPublicKey(bundle.signature.publicKeyPem), Buffer.from(bundle.signature.signatureBase64, "base64"));
@@ -1823,6 +3743,6 @@ var readEvidenceTrustStore = (path) => {
1823
3743
  });
1824
3744
  };
1825
3745
 
1826
- export { BENCHMARK_SCHEMA_VERSION, CONTEXT_PROVIDER_SLOT, EVENT_LOG_GENESIS, EVIDENCE_BUNDLE_SCHEMA_VERSION, FileEventStore, HARNESS_EVENT_SCHEMA_VERSION, HARNESS_EVENT_TYPES, HARNESS_PLUGIN_API_VERSION, HarnessError, LEGAL_TRANSITIONS, STATES, approveRun, approvedDecision, assertHuman, authorizeRun, benchmarkRuns, cancelRun, cleanTaskArtifacts, createDocBridgeContextProvider, createDockerToolRuntime, createPluginRegistry, createPluginSlot, createPolicyGate, createProcessToolRuntime, createSessionRecorder, createToolRuntime, exportEvidenceBundle, hashContextSnapshot, hashContextSnapshots, inspectEventLogLock, loadBenchmarkManifest, loadConfig, loadLatestRun, planRun, readContextSnapshots, readEvidenceTrustStore, reconcileRun, recordBenchmarkObservation, recoverEventLogLock, retryRun, startRun, transition, validateBenchmarkManifest, validateConfig, validateContextSnapshot, validateContextSnapshots, validateExternalCodingBenchmarkReport, verifyEvidenceBundle, verifyRun };
3746
+ export { ARTIFACT_SCHEMA_VERSION, ARTIFACT_TYPES, ASSURANCE_LEVELS, BENCHMARK_SCHEMA_VERSION, BLOCK_STATUSES, CAPABILITY_KINDS, CAPABILITY_MANIFEST_SCHEMA_VERSION, COMPATIBILITY_COMPONENTS, COMPATIBILITY_SCHEMA_VERSION, CONTEXT_PROVIDER_SLOT, EVAL_COMPONENTS, EVAL_LAYERS, EVAL_MANIFEST_SCHEMA_VERSION, EVENT_LOG_GENESIS, EVIDENCE_BUNDLE_SCHEMA_VERSION, FileArtifactStore, FileEventStore, HARNESS_ERROR_CATALOG, HARNESS_ERROR_CODES, HARNESS_EVENT_ENVELOPE_SCHEMA_VERSION, HARNESS_EVENT_SCHEMA_VERSION, HARNESS_EVENT_TYPES, HARNESS_PLUGIN_API_VERSION, HarnessError, IMPROVEMENT_CYCLE_STEPS, LEARNING_STATUSES, LEGAL_TRANSITIONS, MEMORY_SCOPES, MODEL_ROLES, PHASE_DECISIONS, PHASE_EFFECTS, PHASE_EFFECT_ACTIONS, PHASE_MODES, QUALITY_DIMENSIONS, STATES, WIP_STATES, adaptiveConcurrency, approveRun, approvedDecision, artifactDigest, artifactFilePath, artifactIsFresh, artifactMarkdownPath, assertHuman, assessAcceptance, assessAgentEval, assessBlock, assessCompatibility, assessDiscovery, assessImprovementCycle, assessIntegration, assessPilot, assessPreflight, assessProduction, assessQaTransition, assessWip, assessWorktreeCleanup, authorizeRun, benchmarkRuns, cancelRun, classifyFailure, classifyHarnessError, cleanTaskArtifacts, compareOptimization, composePullRequest, createArtifactEnvelope, createCapabilityManifest, createCodingAgentAdapter, createCompatibilityManifest, createConfiguredToolRuntime, createDispatchLedger, createDocBridgeContextProvider, createDockerToolRuntime, createEvalManifest, createHarnessEventEnvelope, createInMemoryMemoryAdapter, createKvMemoryAdapter, createLlmCache, createLlmCacheKey, createMachineMonitor, createModelPolicy, createOrcaDispatchPlan, createOrcaLifecycleProjection, createPhaseArtifact, createPhaseProfile, createPluginRegistry, createPluginSlot, createPolicyGate, createProcessToolRuntime, createPullRequestApproval, createQualityMatrix, createSessionRecorder, createStatusSnapshot, createToolRuntime, createTrackingAdapter, createTrackingTransition, evaluateWatchdog, executePhaseProfile, exportEvidenceBundle, hashContextSnapshot, hashContextSnapshots, inspectEventLogLock, isDiscoveryCurrent, loadBenchmarkManifest, loadConfig, loadLatestRun, modelFor, parseRetro, planFilePreflight, planPhaseProfile, planRun, promoteLearnings, readArtifactFile, readContextSnapshots, readEvidenceTrustStore, reconcileRun, recordBenchmarkObservation, recoverEventLogLock, recoveryDelayMs, renderArtifactMarkdown, resumeStateFromArtifacts, retryRun, runAdversarialReview, runAgentEval, runEvalBattery, runWithRecovery, runWorkflow, sampleMachine, selectRuntime, startRun, summarizeMachine, transition, unknownTelemetry, validateAdapterMetadata, validateArtifactEnvelope, validateBenchmarkManifest, validateBlockManifest, validateCacheableOperation, validateCapabilityManifest, validateCompatibilityManifest, validateConfig, validateContextSnapshot, validateContextSnapshots, validateEvalManifest, validateHarnessErrorClassification, validateHarnessEventEnvelope, validateMemoryRecord, validateOptimizationObservation, validatePhaseTelemetry, validateSafeCommand, validateStatusSnapshot, verifyEvidenceBundle, verifyPullRequestApproval, verifyRun };
1827
3747
  //# sourceMappingURL=index.js.map
1828
3748
  //# sourceMappingURL=index.js.map