@agentskit/harness 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +77 -2
- package/capabilities/public-surface.json +668 -0
- package/compatibility/manifest.json +17 -0
- package/compatibility/migration.md +10 -0
- package/compatibility/report.json +23 -0
- package/compatibility/report.md +22 -0
- package/compatibility/rollback.md +8 -0
- package/dist/cli.js +185 -41
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +633 -35
- package/dist/index.js +1344 -300
- package/dist/index.js.map +1 -1
- package/docs/ADR-0026-kernel-adapters-boundary.md +82 -0
- package/docs/GETTING-STARTED.md +18 -0
- package/docs/MODULE-BOUNDARIES.md +143 -0
- package/docs/ORGANIZATION.md +13 -4
- package/docs/TROUBLESHOOTING.md +24 -0
- package/examples/minimum-profile.mjs +27 -0
- package/package.json +13 -2
- package/release/manifest.json +14 -0
- package/release/notes.md +10 -0
- package/release/qualification.json +14 -0
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { resolve, dirname, join, relative, basename, extname, sep } from 'path';
|
|
2
2
|
import { createHash, randomUUID, createPrivateKey, createPublicKey, sign, verify } from 'crypto';
|
|
3
|
-
import { existsSync, mkdirSync, openSync, writeSync, appendFileSync, closeSync, unlinkSync, readFileSync, writeFileSync, mkdtempSync, renameSync, rmSync
|
|
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
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",
|
|
@@ -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/
|
|
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");
|
|
@@ -138,7 +173,7 @@ var cleanConfiguredArtifacts = (loaded) => {
|
|
|
138
173
|
};
|
|
139
174
|
var fileContents = (path) => readFileSync(path, "utf8");
|
|
140
175
|
|
|
141
|
-
// src/types.ts
|
|
176
|
+
// src/kernel/types.ts
|
|
142
177
|
var SURFACE_NAMES = ["logic", "endpoint", "database", "cli", "mcp", "ui", "docs"];
|
|
143
178
|
var CHECK_CATEGORIES = ["build", "test", "lint", ...SURFACE_NAMES, "custom"];
|
|
144
179
|
var RUN_STATES = [
|
|
@@ -155,7 +190,7 @@ var RUN_STATES = [
|
|
|
155
190
|
"SUPERSEDED"
|
|
156
191
|
];
|
|
157
192
|
|
|
158
|
-
// src/config.ts
|
|
193
|
+
// src/execution/config.ts
|
|
159
194
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
160
195
|
var stringValue = (value, label) => {
|
|
161
196
|
if (typeof value !== "string") return fail(`${label} is required.`, "INVALID_CONFIG");
|
|
@@ -269,7 +304,7 @@ var loadConfig = (configPath = ".codex/verification.json") => {
|
|
|
269
304
|
return { absolute, root, stateDir, config, configHash: hashJson(config) };
|
|
270
305
|
};
|
|
271
306
|
|
|
272
|
-
// src/state-machine.ts
|
|
307
|
+
// src/kernel/state-machine.ts
|
|
273
308
|
var transition = (run, to, reason, actor = "harness") => {
|
|
274
309
|
if (!STATES.includes(to)) fail(`Unknown state ${to}.`, "INVALID_STATE");
|
|
275
310
|
if (run.state !== to && !LEGAL_TRANSITIONS[run.state].some((state) => state === to)) fail(`Illegal transition ${run.state} -> ${to}.`, "INVALID_STATE");
|
|
@@ -284,8 +319,63 @@ var approvedDecision = (decision) => {
|
|
|
284
319
|
return ["approved", "approve", "yes", "ok"].includes(decision);
|
|
285
320
|
};
|
|
286
321
|
var HARNESS_EVENT_SCHEMA_VERSION = 1;
|
|
322
|
+
var HARNESS_EVENT_ENVELOPE_SCHEMA_VERSION = 2;
|
|
287
323
|
var EVENT_LOG_GENESIS = "GENESIS";
|
|
288
|
-
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
|
+
};
|
|
289
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"]);
|
|
290
380
|
var eventPath = (stateDir, runId) => join(stateDir, "runs", runId, "events.ndjson");
|
|
291
381
|
var lockPath = (stateDir, runId) => `${eventPath(stateDir, runId)}.lock`;
|
|
@@ -306,8 +396,8 @@ var readLock = (stateDir, runId) => {
|
|
|
306
396
|
var isEventType = (value) => typeof value === "string" && HARNESS_EVENT_TYPES.includes(value);
|
|
307
397
|
var digest = (value) => /^[a-f0-9]{64}$/.test(value);
|
|
308
398
|
var eventBody = (event) => {
|
|
309
|
-
const { eventHash: _eventHash, ...
|
|
310
|
-
return
|
|
399
|
+
const { eventHash: _eventHash, ...body3 } = event;
|
|
400
|
+
return body3;
|
|
311
401
|
};
|
|
312
402
|
var eventDigest = (event) => sha256(JSON.stringify(eventBody(event)));
|
|
313
403
|
var parseEvent = (value, expectedSequence) => {
|
|
@@ -359,8 +449,8 @@ var FileEventStore = class {
|
|
|
359
449
|
try {
|
|
360
450
|
const events = this.readUnlocked(event.runId);
|
|
361
451
|
const previous = events.at(-1);
|
|
362
|
-
const
|
|
363
|
-
const record3 = events.length && !previous?.eventHash ?
|
|
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) };
|
|
364
454
|
appendFileSync(path, `${JSON.stringify(record3)}
|
|
365
455
|
`, "utf8");
|
|
366
456
|
return record3;
|
|
@@ -414,16 +504,27 @@ var recoverEventLogLock = ({ stateDir, runId, actor, maxAgeMs = 3e5 }) => {
|
|
|
414
504
|
return fail("Event log lock owner is still alive.", "HARNESS_ERROR");
|
|
415
505
|
};
|
|
416
506
|
|
|
417
|
-
// src/plugins.ts
|
|
507
|
+
// src/kernel/plugins.ts
|
|
418
508
|
var HARNESS_PLUGIN_API_VERSION = 1;
|
|
419
509
|
var createPluginSlot = (id2) => {
|
|
420
|
-
if (!id2.trim()) fail("Plugin slot id is required.", "INVALID_INPUT");
|
|
421
|
-
return { id: id2 };
|
|
510
|
+
if (typeof id2 !== "string" || !id2.trim()) fail("Plugin slot id is required.", "INVALID_INPUT");
|
|
511
|
+
return { id: id2.trim() };
|
|
422
512
|
};
|
|
423
513
|
var validId = (value, label) => {
|
|
424
|
-
if (
|
|
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");
|
|
425
522
|
return value;
|
|
426
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
|
+
};
|
|
427
528
|
var createPluginRegistry = () => {
|
|
428
529
|
const plugins = /* @__PURE__ */ new Map();
|
|
429
530
|
const contributions = /* @__PURE__ */ new Map();
|
|
@@ -470,11 +571,20 @@ var createPluginRegistry = () => {
|
|
|
470
571
|
register(plugin) {
|
|
471
572
|
ensureOpen();
|
|
472
573
|
if (mounted) fail("Plugins cannot be registered after mount.", "HARNESS_ERROR");
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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 } });
|
|
478
588
|
},
|
|
479
589
|
mount() {
|
|
480
590
|
ensureOpen();
|
|
@@ -483,14 +593,17 @@ var createPluginRegistry = () => {
|
|
|
483
593
|
for (const plugin of order()) {
|
|
484
594
|
const context = {
|
|
485
595
|
apiVersion: HARNESS_PLUGIN_API_VERSION,
|
|
486
|
-
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),
|
|
487
597
|
effect: (disposer) => {
|
|
598
|
+
if (typeof disposer !== "function") fail("Plugin disposer must be a function.", "INVALID_INPUT");
|
|
488
599
|
cleanups.push(disposer);
|
|
489
600
|
},
|
|
490
601
|
on: (type, listener) => {
|
|
491
|
-
const
|
|
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();
|
|
492
605
|
handlers.add(listener);
|
|
493
|
-
listeners.set(
|
|
606
|
+
listeners.set(eventType, handlers);
|
|
494
607
|
const disposer = () => {
|
|
495
608
|
handlers.delete(listener);
|
|
496
609
|
};
|
|
@@ -513,14 +626,16 @@ var createPluginRegistry = () => {
|
|
|
513
626
|
},
|
|
514
627
|
on(type, listener) {
|
|
515
628
|
ensureOpen();
|
|
516
|
-
const
|
|
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();
|
|
517
632
|
handlers.add(listener);
|
|
518
|
-
listeners.set(
|
|
633
|
+
listeners.set(eventType, handlers);
|
|
519
634
|
return () => {
|
|
520
635
|
handlers.delete(listener);
|
|
521
636
|
};
|
|
522
637
|
},
|
|
523
|
-
contributions: (slot) => [...contributions.get(slot.id)?.values() ?? []],
|
|
638
|
+
contributions: (slot) => [...contributions.get(validSlot(slot).id)?.values() ?? []],
|
|
524
639
|
dispose() {
|
|
525
640
|
if (disposed) return;
|
|
526
641
|
let firstError;
|
|
@@ -541,7 +656,25 @@ var createPluginRegistry = () => {
|
|
|
541
656
|
return registry;
|
|
542
657
|
};
|
|
543
658
|
|
|
544
|
-
// src/
|
|
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
|
|
545
678
|
var hashContextSnapshot = ({ providerId, query, references, sourceHash: sourceHash2 }) => hashJson({ providerId, query, references, sourceHash: sourceHash2 });
|
|
546
679
|
var hashContextSnapshots = (snapshots) => hashJson(snapshots.map(({ providerId, query, references, sourceHash: sourceHash2, snapshotHash }) => ({ providerId, query, references, sourceHash: sourceHash2, snapshotHash })));
|
|
547
680
|
var record2 = (value, label) => {
|
|
@@ -564,17 +697,23 @@ var validateContextSnapshot = (value, index2 = 0) => {
|
|
|
564
697
|
uri: requiredString(rawReference["uri"], `context snapshot ${index2}.references[${referenceIndex}].uri`),
|
|
565
698
|
...typeof rawReference["title"] === "string" ? { title: rawReference["title"] } : {},
|
|
566
699
|
...typeof rawReference["version"] === "string" ? { version: rawReference["version"] } : {},
|
|
567
|
-
...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")
|
|
568
702
|
};
|
|
569
703
|
});
|
|
570
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");
|
|
571
708
|
const snapshot = {
|
|
572
709
|
providerId: requiredString(raw["providerId"], `context snapshot ${index2}.providerId`),
|
|
573
710
|
query: { query: requiredString(rawQuery["query"], `context snapshot ${index2}.query.query`), ...scope ? { scope } : {}, ...typeof rawQuery["sourceRevision"] === "string" ? { sourceRevision: rawQuery["sourceRevision"] } : {} },
|
|
574
711
|
references,
|
|
575
712
|
sourceHash: requiredString(raw["sourceHash"], `context snapshot ${index2}.sourceHash`),
|
|
576
713
|
snapshotHash: requiredString(raw["snapshotHash"], `context snapshot ${index2}.snapshotHash`),
|
|
577
|
-
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 }
|
|
578
717
|
};
|
|
579
718
|
if (snapshot.snapshotHash !== hashContextSnapshot(snapshot)) fail(`context snapshot ${index2}.snapshotHash does not match its contents.`, "INVALID_INPUT");
|
|
580
719
|
return snapshot;
|
|
@@ -586,7 +725,7 @@ var readContextSnapshots = (path) => {
|
|
|
586
725
|
var validateContextSnapshots = (snapshots) => snapshots.map((snapshot, index2) => validateContextSnapshot(snapshot, index2));
|
|
587
726
|
var CONTEXT_PROVIDER_SLOT = createPluginSlot("context.provider");
|
|
588
727
|
|
|
589
|
-
// src/runs.ts
|
|
728
|
+
// src/execution/runs.ts
|
|
590
729
|
var now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
591
730
|
var newRunId = () => `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
592
731
|
var saveRun2 = (stateDir, run) => {
|
|
@@ -643,8 +782,8 @@ var parseStructuredEvidence = (stdout) => {
|
|
|
643
782
|
}
|
|
644
783
|
return null;
|
|
645
784
|
};
|
|
646
|
-
var
|
|
647
|
-
var viewportValid = (viewport) => typeof viewport === "string" ||
|
|
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;
|
|
648
787
|
var validateEvidence = (root, check, evidence, outcomeIds) => {
|
|
649
788
|
if (!evidence || evidence.status !== "passed") return ["structured evidence did not pass"];
|
|
650
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(", ")}`];
|
|
@@ -655,7 +794,7 @@ var validateEvidence = (root, check, evidence, outcomeIds) => {
|
|
|
655
794
|
}
|
|
656
795
|
const artifacts = Array.isArray(evidence.artifacts) ? evidence.artifacts : [];
|
|
657
796
|
for (const artifactValue of artifacts) {
|
|
658
|
-
if (!
|
|
797
|
+
if (!isRecord4(artifactValue) || typeof artifactValue["path"] !== "string" || typeof artifactValue["sha256"] !== "string") {
|
|
659
798
|
failures.push("artifact requires string path and sha256");
|
|
660
799
|
continue;
|
|
661
800
|
}
|
|
@@ -770,7 +909,7 @@ var createMachineMonitor = (sampleIntervalMs = 5e3, options = {}) => {
|
|
|
770
909
|
};
|
|
771
910
|
};
|
|
772
911
|
|
|
773
|
-
// src/verification.ts
|
|
912
|
+
// src/execution/verification.ts
|
|
774
913
|
var now2 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
775
914
|
var requireRun = (run) => run ?? fail("No verification run exists.", "NO_RUN");
|
|
776
915
|
var verificationProjection = (run) => ({ checks: run.checks, outcomes: run.outcomes, metrics: run.metrics });
|
|
@@ -912,13 +1051,13 @@ var verifyRun = async ({ configPath }) => {
|
|
|
912
1051
|
const budgetExceeded = loaded.config.budget?.maxDurationMs !== void 0 && totalDurationMs > loaded.config.budget.maxDurationMs;
|
|
913
1052
|
const allPassed = loaded.config.checks.every((check) => !check.required || statuses.get(check.id) === "passed") && !budgetExceeded;
|
|
914
1053
|
current = { ...current, outcomes: current.outcomes.map((outcome) => {
|
|
915
|
-
const
|
|
916
|
-
return { ...outcome, status:
|
|
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" };
|
|
917
1056
|
}), metrics: { totalDurationMs, wallDurationMs: Date.now() - verificationStarted, peakConcurrency: observedPeakConcurrency, budgetExceeded, machine: machineMonitor.stop() } };
|
|
918
|
-
const
|
|
919
|
-
current = { ...current, verificationDigest:
|
|
1057
|
+
const digest6 = verificationDigest(current);
|
|
1058
|
+
current = { ...current, verificationDigest: digest6 };
|
|
920
1059
|
saveRun2(loaded.stateDir, current);
|
|
921
|
-
new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest:
|
|
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 } });
|
|
922
1061
|
const automatic = allPassed && current.autonomy === "yolo" && !loaded.config.tracking.required && loaded.config.contract.ambiguities.length === 0;
|
|
923
1062
|
const nextState = allPassed ? automatic ? "COMPLETE" : "AWAITING_HUMAN_APPROVAL" : "BLOCKED";
|
|
924
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") };
|
|
@@ -1023,6 +1162,70 @@ var retryRun = async ({ configPath }) => {
|
|
|
1023
1162
|
return next;
|
|
1024
1163
|
};
|
|
1025
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
|
+
};
|
|
1026
1229
|
var index = (root, indexPath) => JSON.parse(readFileSync(resolve(root, indexPath), "utf8"));
|
|
1027
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();
|
|
1028
1231
|
var sourceHash = (document) => typeof document.contentHash === "string" && document.contentHash.length > 0 ? document.contentHash : hashJson(document);
|
|
@@ -1036,15 +1239,17 @@ var createDocBridgeContextProvider = ({ root, indexPath = ".doc-bridge/index.jso
|
|
|
1036
1239
|
id: "doc-bridge",
|
|
1037
1240
|
version: "1.0.0",
|
|
1038
1241
|
resolve: async (query) => {
|
|
1242
|
+
const started = Date.now();
|
|
1039
1243
|
const document = index(root, indexPath);
|
|
1040
1244
|
const contentHash = sourceHash(document);
|
|
1041
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) : [];
|
|
1042
|
-
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 }] : []);
|
|
1043
|
-
|
|
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 };
|
|
1044
1249
|
}
|
|
1045
1250
|
});
|
|
1046
1251
|
|
|
1047
|
-
// src/discovery.ts
|
|
1252
|
+
// src/kernel/discovery.ts
|
|
1048
1253
|
var required = (value, label) => {
|
|
1049
1254
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1050
1255
|
return value.trim();
|
|
@@ -1078,7 +1283,7 @@ var validate = (input) => {
|
|
|
1078
1283
|
}
|
|
1079
1284
|
return { assumptions };
|
|
1080
1285
|
};
|
|
1081
|
-
var
|
|
1286
|
+
var digest3 = (result) => hashJson(result);
|
|
1082
1287
|
var assessDiscovery = (input) => {
|
|
1083
1288
|
const { assumptions } = validate(input);
|
|
1084
1289
|
const human = input.ambiguities.filter((ambiguity) => ambiguity.material);
|
|
@@ -1103,7 +1308,7 @@ var assessDiscovery = (input) => {
|
|
|
1103
1308
|
} } : {},
|
|
1104
1309
|
decisionLog
|
|
1105
1310
|
};
|
|
1106
|
-
return { ...base, digest:
|
|
1311
|
+
return { ...base, digest: digest3(base) };
|
|
1107
1312
|
};
|
|
1108
1313
|
var isDiscoveryCurrent = (result, current) => {
|
|
1109
1314
|
const reasons = [];
|
|
@@ -1113,7 +1318,7 @@ var isDiscoveryCurrent = (result, current) => {
|
|
|
1113
1318
|
return { current: reasons.length === 0, reasons };
|
|
1114
1319
|
};
|
|
1115
1320
|
|
|
1116
|
-
// src/wip.ts
|
|
1321
|
+
// src/kernel/wip.ts
|
|
1117
1322
|
var WIP_STATES = ["ready", "implementing", "blocked", "awaiting-decision", "awaiting-acceptance", "done", "cancelled"];
|
|
1118
1323
|
var terminal = /* @__PURE__ */ new Set(["done", "cancelled"]);
|
|
1119
1324
|
var required2 = (value, label) => {
|
|
@@ -1145,7 +1350,7 @@ var assessWip = ({ entries, candidate, maxInFlight = 3 }) => {
|
|
|
1145
1350
|
return { decision: "admit", inFlight, counts, reason: `WIP slot available (${inFlight.length}/${maxInFlight}).` };
|
|
1146
1351
|
};
|
|
1147
1352
|
|
|
1148
|
-
// src/experiment.ts
|
|
1353
|
+
// src/kernel/experiment.ts
|
|
1149
1354
|
var required3 = (value, label) => {
|
|
1150
1355
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1151
1356
|
return value.trim();
|
|
@@ -1157,11 +1362,11 @@ var comparable = (candidate, baseline) => {
|
|
|
1157
1362
|
};
|
|
1158
1363
|
var selectRuntime = (candidates) => {
|
|
1159
1364
|
if (!Array.isArray(candidates) || candidates.length < 2) fail("At least two runtime candidates are required.", "INVALID_INPUT");
|
|
1160
|
-
const
|
|
1365
|
+
const names2 = /* @__PURE__ */ new Set();
|
|
1161
1366
|
for (const candidate of candidates) {
|
|
1162
1367
|
const runtime = required3(candidate.runtime, "candidate.runtime");
|
|
1163
|
-
if (
|
|
1164
|
-
|
|
1368
|
+
if (names2.has(runtime)) fail("candidate.runtime values must be unique.", "INVALID_INPUT");
|
|
1369
|
+
names2.add(runtime);
|
|
1165
1370
|
for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) required3(candidate[key], `candidate.${key}`);
|
|
1166
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");
|
|
1167
1372
|
comparable(candidate, candidates[0]);
|
|
@@ -1172,7 +1377,92 @@ var selectRuntime = (candidates) => {
|
|
|
1172
1377
|
return { decision: "selected", selected, eligible, reason: "Selected by human minutes, duration, cost, then Orca tie-break." };
|
|
1173
1378
|
};
|
|
1174
1379
|
|
|
1175
|
-
// src/
|
|
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
|
|
1176
1466
|
var required4 = (value, label) => {
|
|
1177
1467
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1178
1468
|
return value.trim();
|
|
@@ -1216,8 +1506,26 @@ var composePullRequest = ({ draft, g2, remote }) => {
|
|
|
1216
1506
|
if (remote.candidateRevision !== draft.candidateRevision || !remote.url) return { decision: "blocked", reason: "Confirmed remote PR does not match the candidate revision.", idempotencyKey };
|
|
1217
1507
|
return { decision: "reuse", reason: "The idempotent remote PR already exists for this candidate revision.", idempotencyKey };
|
|
1218
1508
|
}
|
|
1219
|
-
const
|
|
1220
|
-
return { decision: "create", body:
|
|
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) };
|
|
1221
1529
|
};
|
|
1222
1530
|
var assessIntegration = ({ g2, candidateRevision, evidenceRevision, contractHash, configHash, ci }) => {
|
|
1223
1531
|
required4(candidateRevision, "candidateRevision");
|
|
@@ -1267,7 +1575,7 @@ var assessAcceptance = ({ production, acceptanceRequired, accepted, notApplicabl
|
|
|
1267
1575
|
return assessed("G5", "approved", acceptanceRequired ? [] : [`Acceptance is not applicable: ${notApplicableReason}.`], production.binding);
|
|
1268
1576
|
};
|
|
1269
1577
|
|
|
1270
|
-
// src/pilot.ts
|
|
1578
|
+
// src/kernel/pilot.ts
|
|
1271
1579
|
var required5 = (value, label) => {
|
|
1272
1580
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1273
1581
|
return value.trim();
|
|
@@ -1296,7 +1604,7 @@ var assessPilot = (manifest) => {
|
|
|
1296
1604
|
return { ...base, digest: hashJson({ ...manifest, ...base }) };
|
|
1297
1605
|
};
|
|
1298
1606
|
var IMPROVEMENT_CYCLE_STEPS = ["adversarial-review", "g2-preflight", "baseline-record", "pilot-execution", "comparison"];
|
|
1299
|
-
var
|
|
1607
|
+
var nonEmpty3 = (value, label) => {
|
|
1300
1608
|
if (typeof value !== "string" || !value.trim()) return fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1301
1609
|
return value.trim();
|
|
1302
1610
|
};
|
|
@@ -1315,14 +1623,14 @@ var validateIteration = (iteration, index2) => {
|
|
|
1315
1623
|
if (typeof result !== "object" || result === null || Array.isArray(result)) return fail(`iterations[${index2}].steps[${stepIndex}] must be an object.`, "INVALID_INPUT");
|
|
1316
1624
|
if (result.step !== IMPROVEMENT_CYCLE_STEPS[stepIndex]) return fail(`iterations[${index2}].steps[${stepIndex}] must be ${IMPROVEMENT_CYCLE_STEPS[stepIndex]}.`, "INVALID_INPUT");
|
|
1317
1625
|
if (!["passed", "failed", "blocked", "pending"].includes(result.status)) return fail(`iterations[${index2}].steps[${stepIndex}].status is invalid.`, "INVALID_INPUT");
|
|
1318
|
-
if (result.status !== "passed" && !
|
|
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");
|
|
1319
1627
|
});
|
|
1320
|
-
if (iteration.adjustment !== void 0)
|
|
1628
|
+
if (iteration.adjustment !== void 0) nonEmpty3(iteration.adjustment, `iterations[${index2}].adjustment`);
|
|
1321
1629
|
return { ...iteration, metrics: validateMetrics(iteration.metrics, index2) };
|
|
1322
1630
|
};
|
|
1323
1631
|
var assessImprovementCycle = (input) => {
|
|
1324
1632
|
if (typeof input !== "object" || input === null || Array.isArray(input)) return fail("cycle input must be an object.", "INVALID_INPUT");
|
|
1325
|
-
const cycleId =
|
|
1633
|
+
const cycleId = nonEmpty3(input.cycleId, "cycleId");
|
|
1326
1634
|
if (!Number.isInteger(input.maxIterations) || input.maxIterations < 1) return fail("maxIterations must be a positive integer.", "INVALID_INPUT");
|
|
1327
1635
|
if (!Array.isArray(input.iterations) || input.iterations.length < 1) return fail("iterations must be non-empty.", "INVALID_INPUT");
|
|
1328
1636
|
if (input.iterations.length > input.maxIterations) return fail("iterations cannot exceed maxIterations.", "INVALID_INPUT");
|
|
@@ -1342,11 +1650,129 @@ var assessImprovementCycle = (input) => {
|
|
|
1342
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."];
|
|
1343
1651
|
const decision = complete ? "complete" : iterations.length >= input.maxIterations || !latest.adjustment ? "blocked" : "repeat";
|
|
1344
1652
|
const result = { type: "agentskit-harness-improvement-cycle", cycleId, decision, ...decision === "repeat" ? { nextIteration: latest.iteration + 1 } : {}, reasons, matrix };
|
|
1345
|
-
const
|
|
1346
|
-
return { ...result, digest:
|
|
1653
|
+
const digest6 = createHash("sha256").update(JSON.stringify(result)).digest("hex");
|
|
1654
|
+
return { ...result, digest: digest6 };
|
|
1347
1655
|
};
|
|
1348
1656
|
|
|
1349
|
-
// src/eval.ts
|
|
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
|
+
};
|
|
1350
1776
|
var pass = (expected, output) => typeof expected === "string" ? output === expected : expected(output);
|
|
1351
1777
|
var runAgentEval = async ({ suite, agent, concurrency = 1 }) => {
|
|
1352
1778
|
if (!suite.name.trim() || !suite.cases.length) fail("Eval suite must have a name and at least one case.", "INVALID_INPUT");
|
|
@@ -1369,7 +1795,7 @@ var assessAgentEval = (report, minimumAccuracy) => {
|
|
|
1369
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 };
|
|
1370
1796
|
};
|
|
1371
1797
|
|
|
1372
|
-
// src/cache.ts
|
|
1798
|
+
// src/kernel/cache.ts
|
|
1373
1799
|
var validateCacheableOperation = (operation) => {
|
|
1374
1800
|
if (operation !== "context" && operation !== "read-only") fail("Only context and read-only operations may use the LLM cache.", "POLICY_BLOCKED");
|
|
1375
1801
|
return operation;
|
|
@@ -1384,6 +1810,8 @@ var createLlmCache = () => {
|
|
|
1384
1810
|
let misses = 0;
|
|
1385
1811
|
let invalidations = 0;
|
|
1386
1812
|
return {
|
|
1813
|
+
assurance: "contract-tested",
|
|
1814
|
+
telemetry: () => ({ status: "measured", cacheHits: hits, cacheMisses: misses }),
|
|
1387
1815
|
async getOrCompute(key, compute) {
|
|
1388
1816
|
const cached = values.get(key);
|
|
1389
1817
|
if (cached !== void 0) {
|
|
@@ -1407,13 +1835,13 @@ var createLlmCache = () => {
|
|
|
1407
1835
|
};
|
|
1408
1836
|
};
|
|
1409
1837
|
|
|
1410
|
-
// src/optimization.ts
|
|
1411
|
-
var
|
|
1838
|
+
// src/kernel/optimization.ts
|
|
1839
|
+
var nonNegative2 = (value, label) => {
|
|
1412
1840
|
if (!Number.isFinite(value) || value < 0) fail(`${label} must be a non-negative number.`, "INVALID_INPUT");
|
|
1413
1841
|
return value;
|
|
1414
1842
|
};
|
|
1415
1843
|
var nonNegativeInteger = (value, label) => {
|
|
1416
|
-
|
|
1844
|
+
nonNegative2(value, label);
|
|
1417
1845
|
if (!Number.isInteger(value)) fail(`${label} must be an integer.`, "INVALID_INPUT");
|
|
1418
1846
|
return value;
|
|
1419
1847
|
};
|
|
@@ -1427,7 +1855,7 @@ var validateOptimizationObservation = (observation) => {
|
|
|
1427
1855
|
required6(observation.configHash, "configHash");
|
|
1428
1856
|
required6(observation.provider, "provider");
|
|
1429
1857
|
required6(observation.model, "model");
|
|
1430
|
-
|
|
1858
|
+
nonNegative2(observation.durationMs, "durationMs");
|
|
1431
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");
|
|
1432
1860
|
if (observation.tokens) {
|
|
1433
1861
|
const tokens = observation.tokens;
|
|
@@ -1442,8 +1870,8 @@ var validateOptimizationObservation = (observation) => {
|
|
|
1442
1870
|
}
|
|
1443
1871
|
if (observation.parallelism) {
|
|
1444
1872
|
for (const key of ["tasks", "peakConcurrency"]) nonNegativeInteger(observation.parallelism[key], `parallelism.${key}`);
|
|
1445
|
-
|
|
1446
|
-
if (observation.parallelism.queueWaitMs !== void 0)
|
|
1873
|
+
nonNegative2(observation.parallelism.criticalPathMs, "parallelism.criticalPathMs");
|
|
1874
|
+
if (observation.parallelism.queueWaitMs !== void 0) nonNegative2(observation.parallelism.queueWaitMs, "parallelism.queueWaitMs");
|
|
1447
1875
|
if (observation.parallelism.tasks > 0 && observation.parallelism.peakConcurrency < 1) fail("parallelism.peakConcurrency must be positive when tasks exist.", "INVALID_INPUT");
|
|
1448
1876
|
}
|
|
1449
1877
|
return observation;
|
|
@@ -1467,7 +1895,7 @@ var compareOptimization = (baseline, candidate) => {
|
|
|
1467
1895
|
return result;
|
|
1468
1896
|
};
|
|
1469
1897
|
|
|
1470
|
-
// src/memory.ts
|
|
1898
|
+
// src/kernel/memory.ts
|
|
1471
1899
|
var MEMORY_SCOPES = ["issue", "project", "global"];
|
|
1472
1900
|
var text2 = (value, label) => {
|
|
1473
1901
|
if (typeof value !== "string" || !value.trim()) fail(label + " must be a non-empty string.", "INVALID_INPUT");
|
|
@@ -1485,23 +1913,38 @@ var validateMemoryRecord = (record3) => {
|
|
|
1485
1913
|
};
|
|
1486
1914
|
var createInMemoryMemoryAdapter = (options = {}) => {
|
|
1487
1915
|
const records = /* @__PURE__ */ new Map();
|
|
1916
|
+
let reads = 0;
|
|
1917
|
+
let writes = 0;
|
|
1918
|
+
let relevantHits = 0;
|
|
1919
|
+
let staleHits = 0;
|
|
1488
1920
|
return {
|
|
1489
1921
|
id: options.id ?? "in-memory",
|
|
1490
1922
|
version: options.version ?? "1",
|
|
1923
|
+
assurance: "contract-tested",
|
|
1924
|
+
telemetry: () => ({ status: "measured", memoryReads: reads, memoryWrites: writes, memoryRelevantHits: relevantHits, memoryStaleHits: staleHits }),
|
|
1491
1925
|
async remember(record3) {
|
|
1492
1926
|
records.set(validateMemoryRecord(record3).id, record3);
|
|
1927
|
+
writes += 1;
|
|
1493
1928
|
},
|
|
1494
1929
|
async recall({ query, issueId, project, sourceRevision }) {
|
|
1930
|
+
reads += 1;
|
|
1495
1931
|
const needle = query.trim().toLowerCase();
|
|
1496
|
-
|
|
1932
|
+
const hits = [...records.values()].filter((record3) => {
|
|
1497
1933
|
const scopeMatch = record3.scope === "global" || (record3.scope === "issue" ? Boolean(issueId && record3.source.includes(issueId)) : Boolean(project && record3.source.includes(project)));
|
|
1498
1934
|
return scopeMatch && (!needle || `${record3.summary} ${record3.source}`.toLowerCase().includes(needle));
|
|
1499
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;
|
|
1500
1939
|
}
|
|
1501
1940
|
};
|
|
1502
1941
|
};
|
|
1503
1942
|
var createKvMemoryAdapter = (store, options = {}) => {
|
|
1504
1943
|
const indexKey = "agentskit-harness:memory:index";
|
|
1944
|
+
let reads = 0;
|
|
1945
|
+
let writes = 0;
|
|
1946
|
+
let relevantHits = 0;
|
|
1947
|
+
let staleHits = 0;
|
|
1505
1948
|
const matches2 = (record3, query, issueId, project) => {
|
|
1506
1949
|
const scopeMatch = record3.scope === "global" || (record3.scope === "issue" ? Boolean(issueId && record3.source.includes(issueId)) : Boolean(project && record3.source.includes(project)));
|
|
1507
1950
|
return scopeMatch && (!query || `${record3.summary} ${record3.source}`.toLowerCase().includes(query));
|
|
@@ -1509,80 +1952,712 @@ var createKvMemoryAdapter = (store, options = {}) => {
|
|
|
1509
1952
|
return {
|
|
1510
1953
|
id: options.id ?? "agentskit-kv",
|
|
1511
1954
|
version: options.version ?? "1",
|
|
1955
|
+
assurance: "contract-tested",
|
|
1956
|
+
telemetry: () => ({ status: "measured", memoryReads: reads, memoryWrites: writes, memoryRelevantHits: relevantHits, memoryStaleHits: staleHits }),
|
|
1512
1957
|
async remember(record3) {
|
|
1513
1958
|
const valid = validateMemoryRecord(record3);
|
|
1514
1959
|
const ids = await store.get(indexKey);
|
|
1515
1960
|
const index2 = Array.isArray(ids) ? ids.filter((id2) => typeof id2 === "string") : [];
|
|
1516
1961
|
if (!index2.includes(valid.id)) await store.set(indexKey, [...index2, valid.id].sort());
|
|
1517
1962
|
await store.set(`agentskit-harness:memory:${valid.id}`, valid);
|
|
1963
|
+
writes += 1;
|
|
1518
1964
|
},
|
|
1519
1965
|
async recall({ query, issueId, project, sourceRevision }) {
|
|
1966
|
+
reads += 1;
|
|
1520
1967
|
const ids = await store.get(indexKey);
|
|
1521
1968
|
const records = Array.isArray(ids) ? await Promise.all(ids.filter((id2) => typeof id2 === "string").map((id2) => store.get(`agentskit-harness:memory:${id2}`))) : [];
|
|
1522
|
-
|
|
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;
|
|
1523
1973
|
}
|
|
1524
1974
|
};
|
|
1525
1975
|
};
|
|
1526
1976
|
|
|
1527
|
-
// src/
|
|
1528
|
-
var
|
|
1529
|
-
|
|
1530
|
-
|
|
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();
|
|
1531
1986
|
};
|
|
1532
|
-
var
|
|
1533
|
-
|
|
1534
|
-
if (
|
|
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]));
|
|
1535
2044
|
const remaining = new Set(byId.keys());
|
|
1536
2045
|
const completed = /* @__PURE__ */ new Set();
|
|
1537
|
-
const
|
|
2046
|
+
const levels2 = [];
|
|
1538
2047
|
while (remaining.size) {
|
|
1539
|
-
const ready = [...remaining].sort().
|
|
1540
|
-
if (!ready.length) fail("
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
remaining.delete(
|
|
1544
|
-
completed.add(
|
|
1545
|
-
}
|
|
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
|
+
});
|
|
1546
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");
|
|
1547
2068
|
return result;
|
|
1548
2069
|
};
|
|
1549
|
-
var
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
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);
|
|
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: {} };
|
|
1567
2163
|
}
|
|
1568
|
-
if (
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
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: {} };
|
|
1576
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;
|
|
1577
2200
|
}
|
|
1578
|
-
|
|
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
|
|
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
|
+
};
|
|
1579
2654
|
};
|
|
1580
2655
|
var BENCHMARK_SCHEMA_VERSION = 1;
|
|
1581
2656
|
var percentage = (part, total) => total ? Number((part / total).toFixed(4)) : null;
|
|
1582
2657
|
var improvementRate = (baseline, current) => baseline === void 0 || current === void 0 || baseline === 0 ? null : Number(((baseline - current) / baseline).toFixed(4));
|
|
1583
2658
|
var improvementDirection = (rate2) => rate2 === null ? "unavailable" : rate2 > 0 ? "improved" : rate2 < 0 ? "regressed" : "unchanged";
|
|
1584
2659
|
var count = (items, predicate) => items.filter(predicate).length;
|
|
1585
|
-
var
|
|
2660
|
+
var median2 = (values) => {
|
|
1586
2661
|
if (!values.length) return null;
|
|
1587
2662
|
const sorted = [...values].sort((left, right) => left - right);
|
|
1588
2663
|
const middle = Math.floor(sorted.length / 2);
|
|
@@ -1628,8 +2703,8 @@ var summarize = (runs) => {
|
|
|
1628
2703
|
firstAttemptApprovalRate: percentage(count(firstAttempts, (run) => run.humanApproved), firstAttempts.length),
|
|
1629
2704
|
retryRate: percentage(count(runs, (run) => run.supersedes !== void 0), runs.length),
|
|
1630
2705
|
staleRate: percentage(stateCounts.STALE, runs.length),
|
|
1631
|
-
averageDurationMs: durations.length ? Math.round(durations.reduce((total,
|
|
1632
|
-
medianDurationMs:
|
|
2706
|
+
averageDurationMs: durations.length ? Math.round(durations.reduce((total, duration5) => total + duration5, 0) / durations.length) : null,
|
|
2707
|
+
medianDurationMs: median2(durations)
|
|
1633
2708
|
};
|
|
1634
2709
|
};
|
|
1635
2710
|
var readRuns = (stateDir) => {
|
|
@@ -1656,7 +2731,7 @@ var sha2562 = (value, label) => {
|
|
|
1656
2731
|
if (!/^[a-f0-9]{64}$/.test(result)) return fail(`${label} must be a lowercase SHA-256 digest.`, "INVALID_CONFIG");
|
|
1657
2732
|
return result;
|
|
1658
2733
|
};
|
|
1659
|
-
var
|
|
2734
|
+
var stringList2 = (value, label) => {
|
|
1660
2735
|
if (!Array.isArray(value)) return fail(`${label} must be a non-empty string array.`, "INVALID_CONFIG");
|
|
1661
2736
|
const items = value;
|
|
1662
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");
|
|
@@ -1669,7 +2744,7 @@ var nonNegativeNumber = (value, label) => {
|
|
|
1669
2744
|
const result = value;
|
|
1670
2745
|
return result;
|
|
1671
2746
|
};
|
|
1672
|
-
var
|
|
2747
|
+
var nonNegativeInteger3 = (value, label) => {
|
|
1673
2748
|
const result = nonNegativeNumber(value, label);
|
|
1674
2749
|
if (result !== void 0 && !Number.isInteger(result)) return fail(`${label} must be an integer.`, "INVALID_CONFIG");
|
|
1675
2750
|
return result;
|
|
@@ -1687,7 +2762,7 @@ var validateBenchmarkManifest = (value) => {
|
|
|
1687
2762
|
const tasks = rawTasks.map((item, index2) => {
|
|
1688
2763
|
if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`benchmark.tasks[${index2}] must be an object.`, "INVALID_CONFIG");
|
|
1689
2764
|
const task = item;
|
|
1690
|
-
return { id: nonEmptyString(task["id"], `benchmark.tasks[${index2}].id`), title: nonEmptyString(task["title"], `benchmark.tasks[${index2}].title`), acceptanceCriteria:
|
|
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`) };
|
|
1691
2766
|
});
|
|
1692
2767
|
if (new Set(tasks.map((task) => task.id)).size !== tasks.length) fail("benchmark task ids must be unique.", "INVALID_CONFIG");
|
|
1693
2768
|
const taskIds = new Set(tasks.map((task) => task.id));
|
|
@@ -1700,10 +2775,10 @@ var validateBenchmarkManifest = (value) => {
|
|
|
1700
2775
|
const taskId = nonEmptyString(observation["taskId"], `benchmark.observations[${index2}].taskId`);
|
|
1701
2776
|
if (!taskIds.has(taskId)) fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
|
|
1702
2777
|
const task = tasks.find((candidate) => candidate.id === taskId) ?? fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
|
|
1703
|
-
const attempts =
|
|
2778
|
+
const attempts = nonNegativeInteger3(observation["attempts"], `benchmark.observations[${index2}].attempts`);
|
|
1704
2779
|
const durationMs = nonNegativeNumber(observation["durationMs"], `benchmark.observations[${index2}].durationMs`);
|
|
1705
2780
|
const reviewMinutes2 = nonNegativeNumber(observation["reviewMinutes"], `benchmark.observations[${index2}].reviewMinutes`);
|
|
1706
|
-
const escapedIncomplete =
|
|
2781
|
+
const escapedIncomplete = nonNegativeInteger3(observation["escapedIncomplete"], `benchmark.observations[${index2}].escapedIncomplete`);
|
|
1707
2782
|
const evidenceDigest = sha2562(observation["evidenceDigest"], `benchmark.observations[${index2}].evidenceDigest`);
|
|
1708
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");
|
|
1709
2784
|
const evidence = rawEvidence?.map((item2, evidenceIndex) => {
|
|
@@ -1783,19 +2858,19 @@ var benchmarkRuns = (stateDir, manifest) => {
|
|
|
1783
2858
|
const reportComparisons = manifest ? comparisons(runs, manifest) : [];
|
|
1784
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 } } : {} };
|
|
1785
2860
|
};
|
|
1786
|
-
var
|
|
2861
|
+
var required8 = (value, label) => {
|
|
1787
2862
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1788
2863
|
return value.trim();
|
|
1789
2864
|
};
|
|
1790
|
-
var
|
|
2865
|
+
var duration3 = (value) => {
|
|
1791
2866
|
if (!Number.isFinite(value) || value < 0) fail("Tool durationMs must be a non-negative number.", "INVALID_INPUT");
|
|
1792
2867
|
return value;
|
|
1793
2868
|
};
|
|
1794
2869
|
var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionId = randomUUID(), resume = false }) => {
|
|
1795
2870
|
if (run.state !== "IMPLEMENTING") fail(`Agent sessions can only start during IMPLEMENTING, not ${run.state}.`, "INVALID_STATE");
|
|
1796
|
-
const id2 =
|
|
1797
|
-
const adapterId =
|
|
1798
|
-
const adapterVersion =
|
|
2871
|
+
const id2 = required8(sessionId, "sessionId");
|
|
2872
|
+
const adapterId = required8(adapter.id, "adapter.id");
|
|
2873
|
+
const adapterVersion = required8(adapter.version, "adapter.version");
|
|
1799
2874
|
if (!policy || typeof policy.evaluate !== "function") fail("policy.evaluate is required.", "INVALID_INPUT");
|
|
1800
2875
|
if (!runtime || typeof runtime.execute !== "function") fail("runtime.execute is required.", "INVALID_INPUT");
|
|
1801
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");
|
|
@@ -1846,18 +2921,18 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
|
|
|
1846
2921
|
};
|
|
1847
2922
|
const complete = (input) => {
|
|
1848
2923
|
open();
|
|
1849
|
-
const actionId =
|
|
2924
|
+
const actionId = required8(input.actionId, "actionId");
|
|
1850
2925
|
if (!pending.has(actionId)) fail(`Tool action is not pending: ${actionId}.`, "INVALID_STATE");
|
|
1851
|
-
const event = append("tool.completed", { actionId, resultHash:
|
|
2926
|
+
const event = append("tool.completed", { actionId, resultHash: required8(input.resultHash, "resultHash"), durationMs: duration3(input.durationMs), ...input.runtimeEvidence ? { runtimeEvidence: input.runtimeEvidence } : {} });
|
|
1852
2927
|
pending.delete(actionId);
|
|
1853
2928
|
return event;
|
|
1854
2929
|
};
|
|
1855
2930
|
const failAction = (input) => {
|
|
1856
2931
|
open();
|
|
1857
|
-
const actionId =
|
|
2932
|
+
const actionId = required8(input.actionId, "actionId");
|
|
1858
2933
|
if (!pending.has(actionId)) fail(`Tool action is not pending: ${actionId}.`, "INVALID_STATE");
|
|
1859
2934
|
if (typeof input.retryable !== "boolean") fail("retryable must be boolean.", "INVALID_INPUT");
|
|
1860
|
-
const event = append("tool.failed", { actionId, errorCode:
|
|
2935
|
+
const event = append("tool.failed", { actionId, errorCode: required8(input.errorCode, "errorCode"), retryable: input.retryable, durationMs: duration3(input.durationMs), ...input.runtimeEvidence ? { runtimeEvidence: input.runtimeEvidence } : {} });
|
|
1861
2936
|
pending.delete(actionId);
|
|
1862
2937
|
return event;
|
|
1863
2938
|
};
|
|
@@ -1865,24 +2940,24 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
|
|
|
1865
2940
|
sessionId: id2,
|
|
1866
2941
|
startTurn: (inputHash, turnId = randomUUID()) => {
|
|
1867
2942
|
open();
|
|
1868
|
-
const turn =
|
|
2943
|
+
const turn = required8(turnId, "turnId");
|
|
1869
2944
|
if (turns.has(turn)) fail(`Turn already exists: ${turn}.`, "INVALID_STATE");
|
|
1870
|
-
const event = append("agent.turn.started", { turnId: turn, inputHash:
|
|
2945
|
+
const event = append("agent.turn.started", { turnId: turn, inputHash: required8(inputHash, "inputHash") });
|
|
1871
2946
|
turns.add(turn);
|
|
1872
2947
|
return event;
|
|
1873
2948
|
},
|
|
1874
2949
|
requestTool: (input) => {
|
|
1875
2950
|
open();
|
|
1876
|
-
const turnId =
|
|
2951
|
+
const turnId = required8(input.turnId, "turnId");
|
|
1877
2952
|
if (!turns.has(turnId)) fail(`Turn does not exist: ${turnId}.`, "INVALID_STATE");
|
|
1878
|
-
const actionId =
|
|
2953
|
+
const actionId = required8(input.actionId ?? randomUUID(), "actionId");
|
|
1879
2954
|
if (actions.has(actionId)) fail(`Tool action already exists: ${actionId}.`, "INVALID_STATE");
|
|
1880
|
-
const toolId =
|
|
1881
|
-
const argumentsHash =
|
|
2955
|
+
const toolId = required8(input.toolId, "toolId");
|
|
2956
|
+
const argumentsHash = required8(input.argumentsHash, "argumentsHash");
|
|
1882
2957
|
const decision = policy.evaluate({ actionId, turnId, toolId, argumentsHash });
|
|
1883
2958
|
if (!decision || decision.decision !== "allow" && decision.decision !== "block" && decision.decision !== "approve") fail("Policy decision is invalid.", "HARNESS_ERROR");
|
|
1884
|
-
const policyId =
|
|
1885
|
-
const reason =
|
|
2959
|
+
const policyId = required8(decision.policyId, "policyId");
|
|
2960
|
+
const reason = required8(decision.reason, "policy reason");
|
|
1886
2961
|
append("policy.evaluated", { actionId, turnId, toolId, decision: decision.decision, policyId, reason });
|
|
1887
2962
|
actions.add(actionId);
|
|
1888
2963
|
if (decision.decision === "block") {
|
|
@@ -1900,7 +2975,7 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
|
|
|
1900
2975
|
},
|
|
1901
2976
|
approveTool: (input) => {
|
|
1902
2977
|
open();
|
|
1903
|
-
const actionId =
|
|
2978
|
+
const actionId = required8(input.actionId, "actionId");
|
|
1904
2979
|
const approval = approvals.get(actionId) ?? fail(`Tool action is not awaiting human approval: ${actionId}.`, "INVALID_STATE");
|
|
1905
2980
|
if (input.actor !== void 0 && input.actor !== "human") fail("Tool approval requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
|
|
1906
2981
|
const decision = input.decision;
|
|
@@ -1914,7 +2989,7 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
|
|
|
1914
2989
|
},
|
|
1915
2990
|
recoverTool: (input) => {
|
|
1916
2991
|
open();
|
|
1917
|
-
const actionId =
|
|
2992
|
+
const actionId = required8(input.actionId, "actionId");
|
|
1918
2993
|
const action = pending.get(actionId) ?? fail(`Tool action is not pending: ${actionId}.`, "INVALID_STATE");
|
|
1919
2994
|
if (!action.executionStarted) fail(`Tool action does not require recovery: ${actionId}.`, "INVALID_STATE");
|
|
1920
2995
|
if (input.actor !== void 0 && input.actor !== "human") fail("Tool recovery requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
|
|
@@ -1932,7 +3007,7 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
|
|
|
1932
3007
|
failTool: failAction,
|
|
1933
3008
|
executeTool: async (input) => {
|
|
1934
3009
|
open();
|
|
1935
|
-
const actionId =
|
|
3010
|
+
const actionId = required8(input.actionId, "actionId");
|
|
1936
3011
|
const action = pending.get(actionId) ?? fail(`Tool action is not pending: ${actionId}.`, "INVALID_STATE");
|
|
1937
3012
|
if (action.executionStarted) fail(`Tool action requires human recovery decision: ${actionId}.`, "HUMAN_APPROVAL_REQUIRED");
|
|
1938
3013
|
if (executing.has(actionId)) fail(`Tool action is already executing: ${actionId}.`, "INVALID_STATE");
|
|
@@ -1975,8 +3050,8 @@ var createSessionRecorder = ({ stateDir, run, adapter, policy, runtime, sessionI
|
|
|
1975
3050
|
return recorder;
|
|
1976
3051
|
};
|
|
1977
3052
|
|
|
1978
|
-
// src/policy.ts
|
|
1979
|
-
var
|
|
3053
|
+
// src/kernel/policy.ts
|
|
3054
|
+
var required9 = (value, label) => {
|
|
1980
3055
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1981
3056
|
return value.trim();
|
|
1982
3057
|
};
|
|
@@ -1984,30 +3059,30 @@ var createPolicyGate = ({ rules }) => {
|
|
|
1984
3059
|
if (!Array.isArray(rules)) fail("Policy rules must be an array.", "INVALID_INPUT");
|
|
1985
3060
|
const normalized = rules.map((rule, index2) => {
|
|
1986
3061
|
if (typeof rule !== "object" || rule === null || Array.isArray(rule)) fail(`rules[${index2}] must be an object.`, "INVALID_INPUT");
|
|
1987
|
-
const id2 =
|
|
3062
|
+
const id2 = required9(rule.id, `rules[${index2}].id`);
|
|
1988
3063
|
if (rule.effect !== "allow" && rule.effect !== "block" && rule.effect !== "approve") fail(`rules[${index2}].effect is invalid.`, "INVALID_INPUT");
|
|
1989
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");
|
|
1990
|
-
return { id: id2, effect: rule.effect, toolIds: rule.toolIds.map((toolId) =>
|
|
3065
|
+
return { id: id2, effect: rule.effect, toolIds: rule.toolIds.map((toolId) => required9(toolId, `rules[${index2}].toolIds`)), reason: required9(rule.reason, `rules[${index2}].reason`) };
|
|
1991
3066
|
});
|
|
1992
3067
|
if (new Set(normalized.map((rule) => rule.id)).size !== normalized.length) fail("Policy rules must have unique ids.", "INVALID_INPUT");
|
|
1993
3068
|
return {
|
|
1994
3069
|
evaluate: (request) => {
|
|
1995
3070
|
if (typeof request !== "object" || request === null || Array.isArray(request)) fail("Policy request must be an object.", "INVALID_INPUT");
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
const toolId =
|
|
1999
|
-
|
|
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");
|
|
2000
3075
|
const rule = normalized.find((candidate) => candidate.toolIds.includes(toolId));
|
|
2001
3076
|
return rule ? { decision: rule.effect, policyId: rule.id, reason: rule.reason } : { decision: "block", policyId: "default-deny", reason: `No policy rule allows tool: ${toolId}.` };
|
|
2002
3077
|
}
|
|
2003
3078
|
};
|
|
2004
3079
|
};
|
|
2005
3080
|
var createConfiguredToolRuntime = ({ runtime, process: process2, docker }) => runtime.kind === "docker" ? createDockerToolRuntime(docker) : createProcessToolRuntime(process2);
|
|
2006
|
-
var
|
|
3081
|
+
var required10 = (value, label) => {
|
|
2007
3082
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
2008
3083
|
return value.trim();
|
|
2009
3084
|
};
|
|
2010
|
-
var
|
|
3085
|
+
var duration4 = (value) => {
|
|
2011
3086
|
if (!Number.isFinite(value) || value < 0) fail("Tool durationMs must be a non-negative number.", "INVALID_INPUT");
|
|
2012
3087
|
return value;
|
|
2013
3088
|
};
|
|
@@ -2017,7 +3092,7 @@ var positiveNumber = (value, label) => {
|
|
|
2017
3092
|
return normalized;
|
|
2018
3093
|
};
|
|
2019
3094
|
var absolutePath = (value, label) => {
|
|
2020
|
-
const normalized =
|
|
3095
|
+
const normalized = required10(value, label);
|
|
2021
3096
|
if (!normalized.startsWith("/") || normalized.includes(",")) fail(`${label} must be an absolute path without commas.`, "INVALID_INPUT");
|
|
2022
3097
|
return normalized;
|
|
2023
3098
|
};
|
|
@@ -2035,35 +3110,38 @@ var createToolRuntime = ({ tools, timeoutMs = 3e4 }) => {
|
|
|
2035
3110
|
if (!Number.isInteger(timeoutMs) || timeoutMs < 1) fail("Runtime timeoutMs must be a positive integer.", "INVALID_INPUT");
|
|
2036
3111
|
const normalized = tools.map((tool, index2) => {
|
|
2037
3112
|
if (typeof tool !== "object" || tool === null || Array.isArray(tool)) fail(`tools[${index2}] must be an object.`, "INVALID_INPUT");
|
|
2038
|
-
const toolId =
|
|
3113
|
+
const toolId = required10(tool.toolId, `tools[${index2}].toolId`);
|
|
2039
3114
|
if (typeof tool.execute !== "function") fail(`tools[${index2}].execute is required.`, "INVALID_INPUT");
|
|
2040
3115
|
return { toolId, execute: tool.execute };
|
|
2041
3116
|
});
|
|
2042
3117
|
if (new Set(normalized.map((tool) => tool.toolId)).size !== normalized.length) fail("Runtime tools must have unique ids.", "INVALID_INPUT");
|
|
2043
3118
|
return {
|
|
3119
|
+
assurance: "contract-tested",
|
|
3120
|
+
isolation: "none",
|
|
3121
|
+
telemetry: () => ({ status: "unknown" }),
|
|
2044
3122
|
execute: async (request) => {
|
|
2045
3123
|
const started = Date.now();
|
|
2046
|
-
const actionId =
|
|
2047
|
-
const turnId =
|
|
2048
|
-
const toolId =
|
|
2049
|
-
const 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");
|
|
2050
3128
|
const tool = normalized.find((candidate) => candidate.toolId === toolId);
|
|
2051
|
-
if (!tool) return { status: "failed", errorCode: "TOOL_NOT_FOUND", retryable: false, durationMs:
|
|
3129
|
+
if (!tool) return { status: "failed", errorCode: "TOOL_NOT_FOUND", retryable: false, durationMs: duration4(Date.now() - started) };
|
|
2052
3130
|
const controller = new AbortController();
|
|
2053
3131
|
let timedOut = false;
|
|
2054
3132
|
let timer;
|
|
2055
3133
|
try {
|
|
2056
|
-
const
|
|
3134
|
+
const timeout2 = new Promise((_, reject) => {
|
|
2057
3135
|
timer = setTimeout(() => {
|
|
2058
3136
|
timedOut = true;
|
|
2059
3137
|
controller.abort();
|
|
2060
3138
|
reject(new Error("Tool execution timed out."));
|
|
2061
3139
|
}, timeoutMs);
|
|
2062
3140
|
});
|
|
2063
|
-
const result = await Promise.race([Promise.resolve(tool.execute({ actionId, turnId, toolId, argumentsHash, arguments: request.arguments, signal: controller.signal })),
|
|
2064
|
-
return { status: "completed", resultHash: hashJson(result === void 0 ? null : result), durationMs:
|
|
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) };
|
|
2065
3143
|
} catch {
|
|
2066
|
-
return { status: "failed", errorCode: timedOut ? "TIMEOUT" : "RUNTIME_ERROR", retryable: true, durationMs:
|
|
3144
|
+
return { status: "failed", errorCode: timedOut ? "TIMEOUT" : "RUNTIME_ERROR", retryable: true, durationMs: duration4(Date.now() - started) };
|
|
2067
3145
|
} finally {
|
|
2068
3146
|
if (timer) clearTimeout(timer);
|
|
2069
3147
|
}
|
|
@@ -2076,20 +3154,23 @@ var createProcessToolRuntime = ({ tools, timeoutMs = 3e4, maxOutputBytes = 10485
|
|
|
2076
3154
|
if (!Number.isInteger(maxOutputBytes) || maxOutputBytes < 1) fail("Process runtime maxOutputBytes must be a positive integer.", "INVALID_INPUT");
|
|
2077
3155
|
const normalized = tools.map((tool, index2) => {
|
|
2078
3156
|
if (typeof tool !== "object" || tool === null || Array.isArray(tool)) fail(`tools[${index2}] must be an object.`, "INVALID_INPUT");
|
|
2079
|
-
const toolId =
|
|
2080
|
-
const command =
|
|
3157
|
+
const toolId = required10(tool.toolId, `tools[${index2}].toolId`);
|
|
3158
|
+
const command = required10(tool.command, `tools[${index2}].command`);
|
|
2081
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");
|
|
2082
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");
|
|
2083
3161
|
return { toolId, command, args: tool.args ? [...tool.args] : [], ...tool.cwd ? { cwd: tool.cwd } : {}, env: tool.env ? { ...tool.env } : { PATH: process.env["PATH"] ?? "" } };
|
|
2084
3162
|
});
|
|
2085
3163
|
if (new Set(normalized.map((tool) => tool.toolId)).size !== normalized.length) fail("Process runtime tools must have unique ids.", "INVALID_INPUT");
|
|
2086
3164
|
return {
|
|
3165
|
+
assurance: "contract-tested",
|
|
3166
|
+
isolation: "none",
|
|
3167
|
+
telemetry: () => ({ status: "unknown" }),
|
|
2087
3168
|
execute: async (request) => {
|
|
2088
3169
|
const started = Date.now();
|
|
2089
|
-
const actionId =
|
|
2090
|
-
const turnId =
|
|
2091
|
-
const toolId =
|
|
2092
|
-
const 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");
|
|
2093
3174
|
const tool = normalized.find((candidate) => candidate.toolId === toolId);
|
|
2094
3175
|
if (!tool) return { status: "failed", errorCode: "TOOL_NOT_FOUND", retryable: false, durationMs: Date.now() - started };
|
|
2095
3176
|
let input;
|
|
@@ -2159,17 +3240,17 @@ var createDockerToolRuntime = ({
|
|
|
2159
3240
|
pull = "never"
|
|
2160
3241
|
}) => {
|
|
2161
3242
|
if (!Array.isArray(tools)) fail("Docker runtime tools must be an array.", "INVALID_INPUT");
|
|
2162
|
-
const command =
|
|
2163
|
-
const memory =
|
|
3243
|
+
const command = required10(dockerCommand, "dockerCommand");
|
|
3244
|
+
const memory = required10(memoryLimit, "memoryLimit");
|
|
2164
3245
|
const cpu = positiveNumber(cpus, "cpus");
|
|
2165
3246
|
if (!Number.isInteger(pidsLimit) || pidsLimit < 1) fail("pidsLimit must be a positive integer.", "INVALID_INPUT");
|
|
2166
|
-
const normalizedUser =
|
|
3247
|
+
const normalizedUser = required10(user, "user");
|
|
2167
3248
|
if (normalizedUser.includes(" ")) fail("user must not contain spaces.", "INVALID_INPUT");
|
|
2168
3249
|
if (pull !== "never" && pull !== "missing" && pull !== "always") fail("pull must be never, missing, or always.", "INVALID_INPUT");
|
|
2169
3250
|
const normalized = tools.map((tool, index2) => {
|
|
2170
3251
|
if (typeof tool !== "object" || tool === null || Array.isArray(tool)) fail(`tools[${index2}] must be an object.`, "INVALID_INPUT");
|
|
2171
|
-
const toolId =
|
|
2172
|
-
const image =
|
|
3252
|
+
const toolId = required10(tool.toolId, `tools[${index2}].toolId`);
|
|
3253
|
+
const image = required10(tool.image, `tools[${index2}].image`);
|
|
2173
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");
|
|
2174
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");
|
|
2175
3256
|
const env = dockerEnvironment(tool.env, `tools[${index2}].env`);
|
|
@@ -2221,6 +3302,9 @@ var createDockerToolRuntime = ({
|
|
|
2221
3302
|
if (new Set(normalized.map((tool) => tool.toolId)).size !== normalized.length) fail("Docker runtime tools must have unique ids.", "INVALID_INPUT");
|
|
2222
3303
|
const processRuntime = createProcessToolRuntime({ tools: normalized, timeoutMs, maxOutputBytes });
|
|
2223
3304
|
return {
|
|
3305
|
+
assurance: "runtime-attested",
|
|
3306
|
+
isolation: "sandboxed",
|
|
3307
|
+
telemetry: () => ({ status: "unknown" }),
|
|
2224
3308
|
execute: async (request) => {
|
|
2225
3309
|
const tool = normalized.find((candidate) => candidate.toolId === request.toolId);
|
|
2226
3310
|
if (!tool) return processRuntime.execute(request);
|
|
@@ -2231,7 +3315,7 @@ var createDockerToolRuntime = ({
|
|
|
2231
3315
|
imageDigest = inspected.stdout.trim();
|
|
2232
3316
|
if (!/^sha256:[a-f0-9]{64}$/.test(imageDigest)) throw new Error("Docker image inspection did not return a digest.");
|
|
2233
3317
|
} catch {
|
|
2234
|
-
return { status: "failed", errorCode: "IMAGE_UNAVAILABLE", retryable: true, durationMs:
|
|
3318
|
+
return { status: "failed", errorCode: "IMAGE_UNAVAILABLE", retryable: true, durationMs: duration4(Date.now() - started), runtimeEvidence: tool.evidence };
|
|
2235
3319
|
}
|
|
2236
3320
|
const runtimeEvidence = { ...tool.evidence, imageDigest, profileHash: hashJson({ ...tool.evidence, imageDigest }) };
|
|
2237
3321
|
const result = await processRuntime.execute(request);
|
|
@@ -2239,7 +3323,7 @@ var createDockerToolRuntime = ({
|
|
|
2239
3323
|
}
|
|
2240
3324
|
};
|
|
2241
3325
|
};
|
|
2242
|
-
var
|
|
3326
|
+
var required11 = (value, label) => {
|
|
2243
3327
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
2244
3328
|
return value.trim();
|
|
2245
3329
|
};
|
|
@@ -2249,20 +3333,20 @@ var parse = (value, label) => {
|
|
|
2249
3333
|
try {
|
|
2250
3334
|
const raw = JSON.parse(value);
|
|
2251
3335
|
const identity = {
|
|
2252
|
-
tracker:
|
|
2253
|
-
repository:
|
|
2254
|
-
issue:
|
|
2255
|
-
worktree:
|
|
2256
|
-
branch:
|
|
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`)
|
|
2257
3341
|
};
|
|
2258
|
-
return { ...identity, key:
|
|
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`) };
|
|
2259
3343
|
} catch (error) {
|
|
2260
3344
|
if (error instanceof SyntaxError) fail(`${label} contains invalid JSON.`, "HARNESS_ERROR");
|
|
2261
3345
|
throw error;
|
|
2262
3346
|
}
|
|
2263
3347
|
};
|
|
2264
3348
|
var createDispatchLedger = (stateDir) => {
|
|
2265
|
-
const root =
|
|
3349
|
+
const root = required11(stateDir, "stateDir");
|
|
2266
3350
|
const claimsDir = join(root, "coordination", "claims");
|
|
2267
3351
|
const ledgerPath = join(root, "coordination", "dispatch-ledger.ndjson");
|
|
2268
3352
|
mkdirSync(claimsDir, { recursive: true });
|
|
@@ -2290,13 +3374,13 @@ var createDispatchLedger = (stateDir) => {
|
|
|
2290
3374
|
return {
|
|
2291
3375
|
claim: (input) => {
|
|
2292
3376
|
const identity = {
|
|
2293
|
-
tracker:
|
|
2294
|
-
repository:
|
|
2295
|
-
issue:
|
|
2296
|
-
worktree:
|
|
2297
|
-
branch:
|
|
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")
|
|
2298
3382
|
};
|
|
2299
|
-
const owner =
|
|
3383
|
+
const owner = required11(input.owner, "owner");
|
|
2300
3384
|
const key = safeKey(identity);
|
|
2301
3385
|
const path = claimPath(key);
|
|
2302
3386
|
if (existsSync(path)) return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
|
|
@@ -2317,27 +3401,27 @@ var createDispatchLedger = (stateDir) => {
|
|
|
2317
3401
|
return { decision: "claimed", lease };
|
|
2318
3402
|
},
|
|
2319
3403
|
recordDispatch: ({ lease, idempotencyKey, commandDigest }) => {
|
|
2320
|
-
const id2 =
|
|
2321
|
-
const
|
|
3404
|
+
const id2 = required11(idempotencyKey, "idempotencyKey");
|
|
3405
|
+
const digest6 = required11(commandDigest, "commandDigest");
|
|
2322
3406
|
const existing = records().find((record4) => record4.action === "dispatch" && record4.idempotencyKey === id2);
|
|
2323
3407
|
if (existing) return { decision: "duplicate", record: existing };
|
|
2324
|
-
const record3 = { ...lease, action: "dispatch", at: now3(), idempotencyKey: id2, commandDigest:
|
|
3408
|
+
const record3 = { ...lease, action: "dispatch", at: now3(), idempotencyKey: id2, commandDigest: digest6 };
|
|
2325
3409
|
append(record3);
|
|
2326
3410
|
return { decision: "recorded", record: record3 };
|
|
2327
3411
|
},
|
|
2328
3412
|
release: (lease, reason = "lease released") => {
|
|
2329
|
-
const path = claimPath(
|
|
3413
|
+
const path = claimPath(required11(lease.key, "lease.key"));
|
|
2330
3414
|
if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
|
|
2331
3415
|
const current = parse(readFileSync(path, "utf8"), "claim");
|
|
2332
3416
|
if (current.leaseId !== lease.leaseId) fail("Dispatch lease owner does not match.", "INVALID_STATE");
|
|
2333
3417
|
unlinkSync(path);
|
|
2334
|
-
const record3 = { ...current, action: "release", at: now3(), reason:
|
|
3418
|
+
const record3 = { ...current, action: "release", at: now3(), reason: required11(reason, "reason") };
|
|
2335
3419
|
append(record3);
|
|
2336
3420
|
return record3;
|
|
2337
3421
|
},
|
|
2338
3422
|
recover: (key, input) => {
|
|
2339
3423
|
if (input.actor !== "human") fail("Dispatch lease recovery requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
|
|
2340
|
-
const normalizedKey =
|
|
3424
|
+
const normalizedKey = required11(key, "key");
|
|
2341
3425
|
const maxAgeMs = input.maxAgeMs ?? 3e5;
|
|
2342
3426
|
if (!Number.isInteger(maxAgeMs) || maxAgeMs < 0) fail("maxAgeMs must be a non-negative integer.", "INVALID_INPUT");
|
|
2343
3427
|
const path = claimPath(normalizedKey);
|
|
@@ -2345,7 +3429,7 @@ var createDispatchLedger = (stateDir) => {
|
|
|
2345
3429
|
const current = parse(readFileSync(path, "utf8"), "claim");
|
|
2346
3430
|
if (Date.now() - Date.parse(current.claimedAt) < maxAgeMs) fail("Dispatch lease is not old enough to recover.", "HARNESS_ERROR");
|
|
2347
3431
|
unlinkSync(path);
|
|
2348
|
-
const record3 = { ...current, action: "recover", at: now3(), reason:
|
|
3432
|
+
const record3 = { ...current, action: "recover", at: now3(), reason: required11(input.reason, "reason") };
|
|
2349
3433
|
append(record3);
|
|
2350
3434
|
return record3;
|
|
2351
3435
|
},
|
|
@@ -2353,73 +3437,6 @@ var createDispatchLedger = (stateDir) => {
|
|
|
2353
3437
|
records
|
|
2354
3438
|
};
|
|
2355
3439
|
};
|
|
2356
|
-
|
|
2357
|
-
// src/resilience.ts
|
|
2358
|
-
var positiveInteger = (value, label) => {
|
|
2359
|
-
if (!Number.isInteger(value) || value < 1) fail(`${label} must be a positive integer.`, "INVALID_INPUT");
|
|
2360
|
-
return value;
|
|
2361
|
-
};
|
|
2362
|
-
var nonNegativeInteger3 = (value, label) => {
|
|
2363
|
-
if (!Number.isInteger(value) || value < 0) fail(`${label} must be a non-negative integer.`, "INVALID_INPUT");
|
|
2364
|
-
return value;
|
|
2365
|
-
};
|
|
2366
|
-
var classifyFailure = (error) => {
|
|
2367
|
-
const value = error;
|
|
2368
|
-
const code = typeof value?.code === "string" ? value.code.toUpperCase() : "";
|
|
2369
|
-
const message = typeof value?.message === "string" ? value.message : String(error);
|
|
2370
|
-
const text5 = `${code} ${message}`.toLowerCase();
|
|
2371
|
-
if (/quota|rate.?limit|too many requests|429/.test(text5)) return { class: "quota", retryable: true, reason: message };
|
|
2372
|
-
if (/timeout|timed out|deadline/.test(text5)) return { class: "timeout", retryable: true, reason: message };
|
|
2373
|
-
if (/policy|forbidden|permission|approval/.test(text5)) return { class: "policy", retryable: false, reason: message };
|
|
2374
|
-
if (/invalid|schema|argument|config|validation/.test(text5)) return { class: "validation", retryable: false, reason: message };
|
|
2375
|
-
if (/network|connection|econn|503|502|external/.test(text5)) return { class: "external", retryable: true, reason: message };
|
|
2376
|
-
return { class: "unknown", retryable: false, reason: message };
|
|
2377
|
-
};
|
|
2378
|
-
var recoveryDelayMs = (attempt, policy) => {
|
|
2379
|
-
positiveInteger(attempt, "attempt");
|
|
2380
|
-
nonNegativeInteger3(policy.baseDelayMs, "baseDelayMs");
|
|
2381
|
-
nonNegativeInteger3(policy.maxDelayMs, "maxDelayMs");
|
|
2382
|
-
if (policy.maxDelayMs < policy.baseDelayMs) fail("maxDelayMs must be greater than or equal to baseDelayMs.", "INVALID_INPUT");
|
|
2383
|
-
return Math.min(policy.maxDelayMs, policy.baseDelayMs * 2 ** Math.max(0, attempt - 1));
|
|
2384
|
-
};
|
|
2385
|
-
var wait = (delayMs, sleep) => delayMs > 0 ? sleep(delayMs) : Promise.resolve();
|
|
2386
|
-
var runWithRecovery = async (operation, options) => {
|
|
2387
|
-
const maxAttempts = positiveInteger(options.maxAttempts, "maxAttempts");
|
|
2388
|
-
const baseDelayMs = nonNegativeInteger3(options.baseDelayMs, "baseDelayMs");
|
|
2389
|
-
const maxDelayMs = nonNegativeInteger3(options.maxDelayMs, "maxDelayMs");
|
|
2390
|
-
if (maxDelayMs < baseDelayMs) fail("maxDelayMs must be greater than or equal to baseDelayMs.", "INVALID_INPUT");
|
|
2391
|
-
if (options.timeoutMs !== void 0) positiveInteger(options.timeoutMs, "timeoutMs");
|
|
2392
|
-
const sleep = options.sleep ?? ((delayMs) => new Promise((resolve6) => setTimeout(resolve6, delayMs)));
|
|
2393
|
-
const observations = [];
|
|
2394
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
2395
|
-
const controller = new AbortController();
|
|
2396
|
-
let timer;
|
|
2397
|
-
try {
|
|
2398
|
-
const operationPromise = operation(controller.signal, attempt);
|
|
2399
|
-
const value = options.timeoutMs === void 0 ? await operationPromise : await Promise.race([
|
|
2400
|
-
operationPromise,
|
|
2401
|
-
new Promise((_, reject) => {
|
|
2402
|
-
timer = setTimeout(() => {
|
|
2403
|
-
controller.abort();
|
|
2404
|
-
reject(new Error("operation timed out"));
|
|
2405
|
-
}, options.timeoutMs);
|
|
2406
|
-
})
|
|
2407
|
-
]);
|
|
2408
|
-
return { status: "completed", attempts: attempt, observations, value };
|
|
2409
|
-
} catch (error) {
|
|
2410
|
-
const failure = classifyFailure(error);
|
|
2411
|
-
const delayMs = failure.retryable && attempt < maxAttempts ? recoveryDelayMs(attempt, { baseDelayMs, maxDelayMs }) : 0;
|
|
2412
|
-
const observation = { attempt, failure, delayMs };
|
|
2413
|
-
observations.push(observation);
|
|
2414
|
-
options.onObservation?.(observation);
|
|
2415
|
-
if (!failure.retryable || attempt >= maxAttempts) return { status: "failed", attempts: attempt, observations, failure };
|
|
2416
|
-
await wait(delayMs, sleep);
|
|
2417
|
-
} finally {
|
|
2418
|
-
if (timer) clearTimeout(timer);
|
|
2419
|
-
}
|
|
2420
|
-
}
|
|
2421
|
-
return fail("Recovery loop exhausted unexpectedly.", "HARNESS_ERROR");
|
|
2422
|
-
};
|
|
2423
3440
|
var DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx", ".txt", ".adoc", ".rst"]);
|
|
2424
3441
|
var TEST_SUFFIXES = [".test.", ".spec.", "__tests__"];
|
|
2425
3442
|
var SHELL_META = /[;&|`$()<>\n\r]/;
|
|
@@ -2454,9 +3471,9 @@ var planFilePreflight = (files, options = {}) => {
|
|
|
2454
3471
|
return { files: unique2, codeFiles, testFiles, docsOnly, checks: docsOnly ? [] : ["lint", "typecheck", ...testFiles.length ? ["test"] : []] };
|
|
2455
3472
|
};
|
|
2456
3473
|
|
|
2457
|
-
// src/block.ts
|
|
3474
|
+
// src/kernel/block.ts
|
|
2458
3475
|
var BLOCK_STATUSES = ["todo", "picked", "development", "validation", "pr-open", "merged", "post-merge", "done", "blocked", "scope-cut"];
|
|
2459
|
-
var
|
|
3476
|
+
var text5 = (value, label) => {
|
|
2460
3477
|
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
2461
3478
|
};
|
|
2462
3479
|
var list = (value, label) => {
|
|
@@ -2483,17 +3500,17 @@ var validateBlockManifest = (value) => {
|
|
|
2483
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");
|
|
2484
3501
|
budget = { ...candidate["maxMinutes"] === void 0 ? {} : { maxMinutes: candidate["maxMinutes"] }, ...candidate["maxAttempts"] === void 0 ? {} : { maxAttempts: candidate["maxAttempts"] } };
|
|
2485
3502
|
}
|
|
2486
|
-
return { schemaVersion: 1, id:
|
|
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") } };
|
|
2487
3504
|
};
|
|
2488
3505
|
var assessBlock = (manifest, completedDependencies = []) => {
|
|
2489
3506
|
const value = validateBlockManifest(manifest);
|
|
2490
|
-
const completed = new Set(completedDependencies.map((item) =>
|
|
3507
|
+
const completed = new Set(completedDependencies.map((item) => text5(item, "completedDependencies[]")));
|
|
2491
3508
|
const blockers = value.dependencies.filter((dependency) => !completed.has(dependency));
|
|
2492
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."];
|
|
2493
3510
|
return { status: blockers.length || value.status === "blocked" ? "blocked" : "ready", manifestHash: hashJson(value), blockers, next };
|
|
2494
3511
|
};
|
|
2495
3512
|
var LEARNING_STATUSES = ["proposed", "promoted", "rejected"];
|
|
2496
|
-
var
|
|
3513
|
+
var text6 = (value, label) => {
|
|
2497
3514
|
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
2498
3515
|
};
|
|
2499
3516
|
var category = (heading) => {
|
|
@@ -2504,8 +3521,8 @@ var category = (heading) => {
|
|
|
2504
3521
|
return "other";
|
|
2505
3522
|
};
|
|
2506
3523
|
var parseRetro = (markdown, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) => {
|
|
2507
|
-
const input =
|
|
2508
|
-
const origin =
|
|
3524
|
+
const input = text6(markdown, "markdown");
|
|
3525
|
+
const origin = text6(source, "source");
|
|
2509
3526
|
if (!Number.isFinite(Date.parse(recordedAt))) fail("recordedAt must be a valid timestamp.", "INVALID_INPUT");
|
|
2510
3527
|
const records = [];
|
|
2511
3528
|
let current = "other";
|
|
@@ -2525,7 +3542,7 @@ var parseRetro = (markdown, source, recordedAt = (/* @__PURE__ */ new Date()).to
|
|
|
2525
3542
|
};
|
|
2526
3543
|
var promoteLearnings = (records, input) => {
|
|
2527
3544
|
if (input.actor !== "human") fail("Learning promotion requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
|
|
2528
|
-
const ids = new Set(input.ids.map((id2) =>
|
|
3545
|
+
const ids = new Set(input.ids.map((id2) => text6(id2, "ids[]")));
|
|
2529
3546
|
const status = input.status ?? "promoted";
|
|
2530
3547
|
const result = records.map((record3) => ids.has(record3.id) ? { ...record3, status } : record3);
|
|
2531
3548
|
const unknown = [...ids].filter((id2) => !records.some((record3) => record3.id === id2));
|
|
@@ -2533,12 +3550,12 @@ var promoteLearnings = (records, input) => {
|
|
|
2533
3550
|
return result;
|
|
2534
3551
|
};
|
|
2535
3552
|
|
|
2536
|
-
// src/status.ts
|
|
2537
|
-
var
|
|
3553
|
+
// src/kernel/status.ts
|
|
3554
|
+
var required12 = (value, label) => {
|
|
2538
3555
|
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
2539
3556
|
};
|
|
2540
3557
|
var createStatusSnapshot = (input) => {
|
|
2541
|
-
const sourceRevision =
|
|
3558
|
+
const sourceRevision = required12(input.sourceRevision, "sourceRevision");
|
|
2542
3559
|
if (!Number.isFinite(Date.parse(input.generatedAt))) fail("generatedAt must be a valid timestamp.", "INVALID_INPUT");
|
|
2543
3560
|
if (!Array.isArray(input.blocks)) fail("blocks must be an array.", "INVALID_INPUT");
|
|
2544
3561
|
const blocks = input.blocks.map((block, index2) => {
|
|
@@ -2549,20 +3566,20 @@ var createStatusSnapshot = (input) => {
|
|
|
2549
3566
|
return { ...value, id: value.id.trim() };
|
|
2550
3567
|
}).sort((left, right) => left.id.localeCompare(right.id));
|
|
2551
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");
|
|
2552
|
-
const
|
|
2553
|
-
return { ...
|
|
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) };
|
|
2554
3571
|
};
|
|
2555
3572
|
var validateStatusSnapshot = (value) => {
|
|
2556
3573
|
if (typeof value !== "object" || value === null || Array.isArray(value)) fail("status snapshot must be an object.", "INVALID_INPUT");
|
|
2557
3574
|
const raw = value;
|
|
2558
|
-
const snapshot = createStatusSnapshot({ generatedAt:
|
|
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 } : {} });
|
|
2559
3576
|
if (raw.schemaVersion !== 1 || raw.digest !== snapshot.digest) fail("status snapshot digest or schemaVersion is invalid.", "HARNESS_ERROR");
|
|
2560
3577
|
return snapshot;
|
|
2561
3578
|
};
|
|
2562
3579
|
|
|
2563
|
-
// src/model-policy.ts
|
|
3580
|
+
// src/kernel/model-policy.ts
|
|
2564
3581
|
var MODEL_ROLES = ["orchestrator", "reviewer", "builder", "watcher"];
|
|
2565
|
-
var
|
|
3582
|
+
var required13 = (value, label) => {
|
|
2566
3583
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
2567
3584
|
return value.trim();
|
|
2568
3585
|
};
|
|
@@ -2572,7 +3589,7 @@ var createModelPolicy = (bindings) => {
|
|
|
2572
3589
|
if (typeof binding2 !== "object" || binding2 === null || Array.isArray(binding2)) fail(`bindings[${index2}] must be an object.`, "INVALID_INPUT");
|
|
2573
3590
|
if (!MODEL_ROLES.includes(binding2.role)) fail(`bindings[${index2}].role is invalid.`, "INVALID_INPUT");
|
|
2574
3591
|
if (binding2.maxTokens !== void 0 && (!Number.isInteger(binding2.maxTokens) || binding2.maxTokens < 1)) fail(`bindings[${index2}].maxTokens must be a positive integer.`, "INVALID_INPUT");
|
|
2575
|
-
return { role: binding2.role, provider:
|
|
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 } };
|
|
2576
3593
|
});
|
|
2577
3594
|
if (new Set(normalized.map((binding2) => binding2.role)).size !== normalized.length) fail("Each model role may be bound only once.", "INVALID_INPUT");
|
|
2578
3595
|
return { bindings: normalized, digest: hashJson(normalized) };
|
|
@@ -2580,42 +3597,69 @@ var createModelPolicy = (bindings) => {
|
|
|
2580
3597
|
var modelFor = (policy, role) => policy.bindings.find((binding2) => binding2.role === role) ?? fail(`No model binding exists for role: ${role}.`, "INVALID_STATE");
|
|
2581
3598
|
|
|
2582
3599
|
// src/adapters/orca.ts
|
|
2583
|
-
var
|
|
3600
|
+
var required14 = (value, label) => {
|
|
2584
3601
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
2585
3602
|
return value.trim();
|
|
2586
3603
|
};
|
|
2587
3604
|
var createOrcaDispatchPlan = (input) => {
|
|
2588
|
-
const repository =
|
|
2589
|
-
const worktree =
|
|
2590
|
-
const branch =
|
|
2591
|
-
const baseBranch =
|
|
2592
|
-
const goalFile =
|
|
2593
|
-
const agent =
|
|
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");
|
|
2594
3611
|
const argv = ["orca", "worktree", "create", "--repo", repository, "--name", worktree, "--base-branch", baseBranch, "--agent", agent, "--prompt-file", goalFile];
|
|
2595
3612
|
validateSafeCommand(argv.join(" "));
|
|
2596
3613
|
const identity = { repository, worktree, branch, baseBranch, goalFile, agent };
|
|
2597
3614
|
return { argv, commandDigest: hashJson(argv), idempotencyKey: hashJson(identity) };
|
|
2598
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
|
+
};
|
|
2599
3630
|
|
|
2600
3631
|
// src/adapters/tracking.ts
|
|
2601
|
-
var
|
|
3632
|
+
var required15 = (value, label) => {
|
|
2602
3633
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
2603
3634
|
return value.trim();
|
|
2604
3635
|
};
|
|
2605
3636
|
var createTrackingTransition = (input) => {
|
|
2606
|
-
const transition2 = { tracker:
|
|
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") };
|
|
2607
3638
|
return { ...transition2, idempotencyKey: hashJson(transition2) };
|
|
2608
3639
|
};
|
|
2609
|
-
var createTrackingAdapter = (id2, handler) => {
|
|
2610
|
-
const adapterId =
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
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
|
+
};
|
|
2616
3660
|
};
|
|
2617
3661
|
var EVIDENCE_BUNDLE_SCHEMA_VERSION = 1;
|
|
2618
|
-
var
|
|
3662
|
+
var body2 = (bundle) => {
|
|
2619
3663
|
const { payloadHash: _payloadHash, signature: _signature, ...unsigned } = bundle;
|
|
2620
3664
|
return unsigned;
|
|
2621
3665
|
};
|
|
@@ -2640,7 +3684,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
|
|
|
2640
3684
|
const loaded = loadConfig(configPath);
|
|
2641
3685
|
const run = requireRun2(runId ? readJson(join(loaded.stateDir, "runs", runId, "run.json")) : loadLatestRun(loaded.stateDir));
|
|
2642
3686
|
const reconciliation = await reconcileRun({ configPath, runId: run.runId });
|
|
2643
|
-
const
|
|
3687
|
+
const digest6 = run.verificationDigest ?? fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
|
|
2644
3688
|
if (reconciliation.state !== "COMPLETE") fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
|
|
2645
3689
|
const eventLog = new FileEventStore(loaded.stateDir);
|
|
2646
3690
|
eventLog.read(run.runId);
|
|
@@ -2655,7 +3699,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
|
|
|
2655
3699
|
return bundleFile(loaded.stateDir, path);
|
|
2656
3700
|
});
|
|
2657
3701
|
const privateKey = createPrivateKey(readFileSync(privateKeyPath));
|
|
2658
|
-
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:
|
|
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 };
|
|
2659
3703
|
const payloadHash = sha256(JSON.stringify(unsigned));
|
|
2660
3704
|
const publicKeyPem = createPublicKey(privateKey).export({ type: "spki", format: "pem" }).toString();
|
|
2661
3705
|
const bundle = { ...unsigned, payloadHash, signature: { algorithm: "ed25519", keyId, publicKeyPem, signatureBase64: sign(null, Buffer.from(payloadHash), privateKey).toString("base64") } };
|
|
@@ -2680,7 +3724,7 @@ var verifyEvidenceBundle = (path, { trustedKeys = [] } = {}) => {
|
|
|
2680
3724
|
if (sha256(content) !== file.sha256) fail(`Evidence bundle file hash mismatch: ${file.path}`, "HARNESS_ERROR");
|
|
2681
3725
|
}
|
|
2682
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");
|
|
2683
|
-
if (sha256(JSON.stringify(
|
|
3727
|
+
if (sha256(JSON.stringify(body2(bundle))) !== bundle.payloadHash) fail("Evidence bundle payload hash mismatch.", "HARNESS_ERROR");
|
|
2684
3728
|
let valid = false;
|
|
2685
3729
|
try {
|
|
2686
3730
|
valid = verify(null, Buffer.from(bundle.payloadHash), createPublicKey(bundle.signature.publicKeyPem), Buffer.from(bundle.signature.signatureBase64, "base64"));
|
|
@@ -2699,6 +3743,6 @@ var readEvidenceTrustStore = (path) => {
|
|
|
2699
3743
|
});
|
|
2700
3744
|
};
|
|
2701
3745
|
|
|
2702
|
-
export { BENCHMARK_SCHEMA_VERSION, BLOCK_STATUSES, CONTEXT_PROVIDER_SLOT, EVENT_LOG_GENESIS, EVIDENCE_BUNDLE_SCHEMA_VERSION, FileEventStore, HARNESS_EVENT_SCHEMA_VERSION, HARNESS_EVENT_TYPES, HARNESS_PLUGIN_API_VERSION, HarnessError, IMPROVEMENT_CYCLE_STEPS, LEARNING_STATUSES, LEGAL_TRANSITIONS, MEMORY_SCOPES, MODEL_ROLES, STATES, WIP_STATES, adaptiveConcurrency, approveRun, approvedDecision, assertHuman, assessAcceptance, assessAgentEval, assessBlock, assessDiscovery, assessImprovementCycle, assessIntegration, assessPilot, assessPreflight, assessProduction, assessWip, assessWorktreeCleanup, authorizeRun, benchmarkRuns, cancelRun, classifyFailure, cleanTaskArtifacts, compareOptimization, composePullRequest, createConfiguredToolRuntime, createDispatchLedger, createDocBridgeContextProvider, createDockerToolRuntime, createInMemoryMemoryAdapter, createKvMemoryAdapter, createLlmCache, createLlmCacheKey, createMachineMonitor, createModelPolicy, createOrcaDispatchPlan, createPluginRegistry, createPluginSlot, createPolicyGate, createProcessToolRuntime, createSessionRecorder, createStatusSnapshot, createToolRuntime, createTrackingAdapter, createTrackingTransition, exportEvidenceBundle, hashContextSnapshot, hashContextSnapshots, inspectEventLogLock, isDiscoveryCurrent, loadBenchmarkManifest, loadConfig, loadLatestRun, modelFor, parseRetro, planFilePreflight, planRun, promoteLearnings, readContextSnapshots, readEvidenceTrustStore, reconcileRun, recordBenchmarkObservation, recoverEventLogLock, recoveryDelayMs, retryRun, runAgentEval, runWithRecovery, runWorkflow, sampleMachine, selectRuntime, startRun, summarizeMachine, transition, validateBenchmarkManifest, validateBlockManifest, validateCacheableOperation, validateConfig, validateContextSnapshot, validateContextSnapshots, validateMemoryRecord, validateOptimizationObservation, validateSafeCommand, validateStatusSnapshot, 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 };
|
|
2703
3747
|
//# sourceMappingURL=index.js.map
|
|
2704
3748
|
//# sourceMappingURL=index.js.map
|