engineering-behavior-observatory 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +151 -0
  3. package/contracts/codex-app-server-0.150.1/manifest.json +25 -0
  4. package/contracts/codex-app-server-0.150.1/schema/InitializeParams.json +84 -0
  5. package/contracts/codex-app-server-0.150.1/schema/JSONRPCMessage.json +137 -0
  6. package/contracts/codex-app-server-0.150.1/schema/v2/ThreadReadParams.json +17 -0
  7. package/contracts/codex-app-server-0.150.1/schema/v2/ThreadTokenUsageUpdatedNotification.json +82 -0
  8. package/contracts/codex-app-server-0.150.1/schema/v2/TurnInterruptParams.json +17 -0
  9. package/contracts/codex-app-server-0.150.1/types/AskForApproval.ts +5 -0
  10. package/contracts/codex-app-server-0.150.1/types/ClientNotification.ts +5 -0
  11. package/contracts/codex-app-server-0.150.1/types/SandboxMode.ts +5 -0
  12. package/contracts/codex-app-server-0.150.1/types/ThreadReadParams.ts +9 -0
  13. package/contracts/codex-app-server-0.150.1/types/TokenUsageBreakdown.ts +5 -0
  14. package/contracts/codex-app-server-0.150.1/types/TurnInterruptParams.ts +5 -0
  15. package/contracts/codex-app-server-0.153.4/manifest.json +27 -0
  16. package/contracts/codex-app-server-0.153.4/schema/JSONRPCMessage.json +137 -0
  17. package/contracts/codex-app-server-0.153.4/schema/v1/InitializeParams.json +84 -0
  18. package/contracts/codex-app-server-0.153.4/schema/v2/ThreadReadParams.json +17 -0
  19. package/contracts/codex-app-server-0.153.4/schema/v2/ThreadStartParams.json +515 -0
  20. package/contracts/codex-app-server-0.153.4/schema/v2/ThreadTokenUsageUpdatedNotification.json +82 -0
  21. package/contracts/codex-app-server-0.153.4/schema/v2/TurnInterruptParams.json +17 -0
  22. package/contracts/codex-app-server-0.153.4/schema/v2/TurnStartParams.json +921 -0
  23. package/contracts/codex-app-server-0.153.4/types/AskForApproval.ts +5 -0
  24. package/contracts/codex-app-server-0.153.4/types/ClientNotification.ts +5 -0
  25. package/contracts/codex-app-server-0.153.4/types/SandboxMode.ts +5 -0
  26. package/contracts/codex-app-server-0.153.4/types/ThreadReadParams.ts +12 -0
  27. package/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.ts +5 -0
  28. package/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.ts +5 -0
  29. package/contracts/openhands-agent-server-v1.44.1.json +65 -0
  30. package/contracts/openhands-agent-server-v1.46.0.json +46 -0
  31. package/dist/contracts/codex-app-server-0.153.4/types/AskForApproval.d.ts +9 -0
  32. package/dist/contracts/codex-app-server-0.153.4/types/AskForApproval.js +2 -0
  33. package/dist/contracts/codex-app-server-0.153.4/types/ClientNotification.d.ts +3 -0
  34. package/dist/contracts/codex-app-server-0.153.4/types/ClientNotification.js +2 -0
  35. package/dist/contracts/codex-app-server-0.153.4/types/SandboxMode.d.ts +1 -0
  36. package/dist/contracts/codex-app-server-0.153.4/types/SandboxMode.js +2 -0
  37. package/dist/contracts/codex-app-server-0.153.4/types/ThreadReadParams.d.ts +10 -0
  38. package/dist/contracts/codex-app-server-0.153.4/types/ThreadReadParams.js +2 -0
  39. package/dist/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.d.ts +8 -0
  40. package/dist/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.js +2 -0
  41. package/dist/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.d.ts +4 -0
  42. package/dist/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.js +2 -0
  43. package/dist/src/agent-sdk-normalizer.d.ts +22 -0
  44. package/dist/src/agent-sdk-normalizer.js +995 -0
  45. package/dist/src/agent-sdk-run.d.ts +31 -0
  46. package/dist/src/agent-sdk-run.js +242 -0
  47. package/dist/src/agent-sdk-runner.d.ts +96 -0
  48. package/dist/src/agent-sdk-runner.js +407 -0
  49. package/dist/src/agent-sdk.d.ts +294 -0
  50. package/dist/src/agent-sdk.js +644 -0
  51. package/dist/src/aggregation.d.ts +183 -0
  52. package/dist/src/aggregation.js +513 -0
  53. package/dist/src/artifacts.d.ts +36 -0
  54. package/dist/src/artifacts.js +1372 -0
  55. package/dist/src/atlas-grafana.d.ts +377 -0
  56. package/dist/src/atlas-grafana.js +47 -0
  57. package/dist/src/atlas-html.d.ts +2 -0
  58. package/dist/src/atlas-html.js +34 -0
  59. package/dist/src/atlas.d.ts +130 -0
  60. package/dist/src/atlas.js +235 -0
  61. package/dist/src/behavior-assertions.d.ts +95 -0
  62. package/dist/src/behavior-assertions.js +154 -0
  63. package/dist/src/cli.d.ts +5 -0
  64. package/dist/src/cli.js +957 -0
  65. package/dist/src/codex-judge.d.ts +44 -0
  66. package/dist/src/codex-judge.js +214 -0
  67. package/dist/src/codex-run.d.ts +114 -0
  68. package/dist/src/codex-run.js +519 -0
  69. package/dist/src/codex.d.ts +187 -0
  70. package/dist/src/codex.js +1028 -0
  71. package/dist/src/contracts.d.ts +155 -0
  72. package/dist/src/contracts.js +866 -0
  73. package/dist/src/corpus.d.ts +67 -0
  74. package/dist/src/corpus.js +540 -0
  75. package/dist/src/cursor-sdk-runner.d.ts +81 -0
  76. package/dist/src/cursor-sdk-runner.js +362 -0
  77. package/dist/src/cursor-sdk.d.ts +81 -0
  78. package/dist/src/cursor-sdk.js +1078 -0
  79. package/dist/src/deepseek-adapter.d.ts +152 -0
  80. package/dist/src/deepseek-adapter.js +777 -0
  81. package/dist/src/exports.d.ts +69 -0
  82. package/dist/src/exports.js +966 -0
  83. package/dist/src/human-calibration.d.ts +167 -0
  84. package/dist/src/human-calibration.js +618 -0
  85. package/dist/src/index.d.ts +71 -0
  86. package/dist/src/index.js +38 -0
  87. package/dist/src/lifecycle.d.ts +196 -0
  88. package/dist/src/lifecycle.js +1889 -0
  89. package/dist/src/normalization-integrity.d.ts +128 -0
  90. package/dist/src/normalization-integrity.js +429 -0
  91. package/dist/src/openhands-run.d.ts +36 -0
  92. package/dist/src/openhands-run.js +415 -0
  93. package/dist/src/openhands.d.ts +130 -0
  94. package/dist/src/openhands.js +859 -0
  95. package/dist/src/pi.d.ts +203 -0
  96. package/dist/src/pi.js +1345 -0
  97. package/dist/src/process-protocol.d.ts +245 -0
  98. package/dist/src/process-protocol.js +1261 -0
  99. package/dist/src/retained-evidence.d.ts +12 -0
  100. package/dist/src/retained-evidence.js +201 -0
  101. package/dist/src/run-bundles.d.ts +193 -0
  102. package/dist/src/run-bundles.js +993 -0
  103. package/dist/src/scheduler.d.ts +109 -0
  104. package/dist/src/scheduler.js +1080 -0
  105. package/dist/src/semantic-judge.d.ts +194 -0
  106. package/dist/src/semantic-judge.js +875 -0
  107. package/dist/src/structural-observations.d.ts +124 -0
  108. package/dist/src/structural-observations.js +671 -0
  109. package/dist/src/task-packets.d.ts +115 -0
  110. package/dist/src/task-packets.js +683 -0
  111. package/dist/src/uniform-events.d.ts +120 -0
  112. package/dist/src/uniform-events.js +158 -0
  113. package/dist/src/verifiers.d.ts +116 -0
  114. package/dist/src/verifiers.js +819 -0
  115. package/dist/src/workspaces.d.ts +56 -0
  116. package/dist/src/workspaces.js +1314 -0
  117. package/docs/README.md +60 -0
  118. package/docs/development/README.md +71 -0
  119. package/docs/development/documentation-sync.md +58 -0
  120. package/docs/development/extension-contracts.md +307 -0
  121. package/docs/evaluation/README.md +48 -0
  122. package/docs/evaluation/aggregation.md +112 -0
  123. package/docs/evaluation/behavior-assertions.md +45 -0
  124. package/docs/evaluation/human-calibration.md +170 -0
  125. package/docs/evaluation/normalization-integrity.md +80 -0
  126. package/docs/evaluation/semantic-judge.md +188 -0
  127. package/docs/evaluation/structural-observations.md +84 -0
  128. package/docs/evaluation/uniform-events.md +131 -0
  129. package/docs/guides/atlas.md +144 -0
  130. package/docs/guides/evidence-and-sharing.md +100 -0
  131. package/docs/guides/operator-guide.md +424 -0
  132. package/docs/guides/quickstart.md +152 -0
  133. package/docs/guides/telemetry.md +66 -0
  134. package/docs/harnesses/README.md +31 -0
  135. package/docs/harnesses/claude-agent-sdk.md +61 -0
  136. package/docs/harnesses/codex-harness.md +174 -0
  137. package/docs/harnesses/cursor-sdk.md +149 -0
  138. package/docs/harnesses/deepseek-harness.md +134 -0
  139. package/docs/harnesses/openhands-agent-server.md +153 -0
  140. package/docs/harnesses/pi-sdk.md +66 -0
  141. package/docs/reference/README.md +19 -0
  142. package/docs/reference/agent-sdk-operational-runner.md +118 -0
  143. package/docs/reference/cli.md +114 -0
  144. package/docs/reference/contracts.md +222 -0
  145. package/docs/reference/run-bundle-contract.md +354 -0
  146. package/docs/reference/run-lifecycle.md +54 -0
  147. package/examples/cursor-sdk/README.md +11 -0
  148. package/examples/cursor-sdk/capture-profile.json +1 -0
  149. package/examples/cursor-sdk/harness.json +1 -0
  150. package/examples/cursor-sdk/model.json +1 -0
  151. package/examples/cursor-sdk/native-limits.json +1 -0
  152. package/examples/cursor-sdk/native-tool-policy.json +1 -0
  153. package/ontology/behavior-categories.v1.json +46 -0
  154. package/package.json +66 -0
  155. package/release/0.1.0/KNOWN_LIMITATIONS.md +35 -0
  156. package/release/0.1.0/README.md +46 -0
  157. package/release/0.1.0/reproducibility.json +61 -0
  158. package/release/0.2.0/KNOWN_LIMITATIONS.md +40 -0
  159. package/release/0.2.0/README.md +42 -0
  160. package/release/0.2.0/reproducibility.json +73 -0
  161. package/release/0.2.1/KNOWN_LIMITATIONS.md +18 -0
  162. package/release/0.2.1/README.md +63 -0
  163. package/release/0.2.1/reproducibility.json +73 -0
  164. package/release/README.md +13 -0
  165. package/schemas/aggregation.v1.json +293 -0
  166. package/schemas/behavior-assertions.v1.json +220 -0
  167. package/schemas/deepseek-runtime-composition.v1.schema.json +122 -0
  168. package/schemas/experiment.v1.schema.json +188 -0
  169. package/schemas/human-calibration.v1.json +336 -0
  170. package/schemas/normalization-integrity.v1.json +302 -0
  171. package/schemas/run-bundles/v1.json +1040 -0
  172. package/schemas/run-queue.v1.schema.json +206 -0
  173. package/schemas/semantic-judge.v1.json +380 -0
  174. package/schemas/structural-observations.v1.json +131 -0
  175. package/schemas/task-packet-freeze.v1.schema.json +106 -0
  176. package/schemas/task-packet.v1.schema.json +234 -0
  177. package/schemas/uniform-events/v1.json +289 -0
  178. package/scripts/atlas-grafana.sh +18 -0
@@ -0,0 +1,1889 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { closeSync, fsyncSync, linkSync, openSync, readFileSync, renameSync, unlinkSync } from "node:fs";
3
+ import { lstat, mkdir, open, readFile, rm } from "node:fs/promises";
4
+ import { dirname, resolve } from "node:path";
5
+ export class InvalidLifecycleTransitionError extends Error {
6
+ constructor(from, to) {
7
+ super(`Invalid lifecycle transition from "${from}" to "${to}".`);
8
+ this.name = "InvalidLifecycleTransitionError";
9
+ }
10
+ }
11
+ const ALLOWED_TRANSITIONS = {
12
+ created: ["setup", "cleaning"],
13
+ setup: ["running", "cleaning"],
14
+ running: ["verifying", "cleaning"],
15
+ verifying: ["cleaning"],
16
+ cleaning: ["terminal"],
17
+ terminal: [],
18
+ };
19
+ const LIFECYCLE_PROGRESS = {
20
+ created: 0,
21
+ setup: 1,
22
+ running: 2,
23
+ verifying: 3,
24
+ cleaning: 4,
25
+ terminal: 5,
26
+ };
27
+ const MAX_TIMER_MS = 2_147_483_647;
28
+ class TerminalPublicationCancelled extends Error {
29
+ constructor() {
30
+ super("Terminal publication was cancelled before installation.");
31
+ this.name = "TerminalPublicationCancelled";
32
+ }
33
+ }
34
+ export function createRunIdentity(input) {
35
+ const run = {
36
+ id: input.id ?? randomUUID(),
37
+ taskId: input.taskId,
38
+ modelId: input.modelId,
39
+ harnessId: input.harnessId,
40
+ };
41
+ assertIdentity(run.id, "Run ID");
42
+ assertIdentity(run.taskId, "Task ID");
43
+ assertIdentity(run.modelId, "Model ID");
44
+ assertIdentity(run.harnessId, "Harness ID");
45
+ return run;
46
+ }
47
+ export function createAttemptIdentity(runId, number = 1, id = randomUUID(), retryOf) {
48
+ assertIdentity(runId, "Run ID");
49
+ assertIdentity(id, "Attempt ID");
50
+ if (!Number.isSafeInteger(number) || number < 1)
51
+ throw new Error("Attempt number must be a positive safe integer.");
52
+ if (retryOf !== undefined) {
53
+ assertIdentity(retryOf, "Retry attempt ID");
54
+ if (retryOf === id)
55
+ throw new Error("An attempt cannot retry itself.");
56
+ if (number < 2)
57
+ throw new Error("A retry attempt must have number 2 or greater.");
58
+ }
59
+ else if (number > 1) {
60
+ throw new Error("Attempt numbers greater than 1 require retryOf.");
61
+ }
62
+ return { id, number, ...(retryOf === undefined ? {} : { retryOf }) };
63
+ }
64
+ export function retryAttempt(previous, id = randomUUID()) {
65
+ const identity = "attempt" in previous ? previous.attempt : previous;
66
+ if (!Number.isSafeInteger(identity.number) || identity.number < 1) {
67
+ throw new Error("Previous attempt number must be a positive safe integer.");
68
+ }
69
+ return createAttemptIdentity("retry", identity.number + 1, id, identity.id);
70
+ }
71
+ export class LifecycleController {
72
+ now;
73
+ transitions = [];
74
+ timestamps;
75
+ current = "created";
76
+ createdAt;
77
+ endedAt;
78
+ constructor(now = () => new Date().toISOString()) {
79
+ this.now = now;
80
+ const createdAt = this.now();
81
+ assertTimestampValue(createdAt, "Lifecycle created timestamp");
82
+ this.createdAt = createdAt;
83
+ this.timestamps = { created: this.createdAt };
84
+ }
85
+ get state() {
86
+ return this.current;
87
+ }
88
+ transition(to, at = this.now()) {
89
+ assertTimestampValue(at, "Lifecycle transition timestamp");
90
+ if (!ALLOWED_TRANSITIONS[this.current].includes(to)) {
91
+ throw new InvalidLifecycleTransitionError(this.current, to);
92
+ }
93
+ this.transitions.push({ from: this.current, to, at });
94
+ this.current = to;
95
+ this.timestamps[to] = at;
96
+ if (to === "terminal")
97
+ this.endedAt = at;
98
+ return this.snapshot();
99
+ }
100
+ snapshot() {
101
+ return {
102
+ state: this.current,
103
+ createdAt: this.createdAt,
104
+ startedAt: this.timestamps.setup ?? this.createdAt,
105
+ ...(this.endedAt === undefined ? {} : { endedAt: this.endedAt }),
106
+ timestamps: { ...this.timestamps },
107
+ transitions: this.transitions.map((transition) => ({ ...transition })),
108
+ };
109
+ }
110
+ }
111
+ export async function executeRunAttempt(options) {
112
+ validateOptions(options);
113
+ const workspaceCoordinator = options.workspace;
114
+ const workspaceSetup = workspaceCoordinator.setup;
115
+ const workspaceCleanup = workspaceCoordinator.cleanup;
116
+ const harnessExecutor = options.harness;
117
+ const verifierExecutor = options.verifier;
118
+ const assessmentMode = options.assessmentMode ?? "verified";
119
+ const evidenceSink = options.evidence;
120
+ const evidenceFlush = evidenceSink?.flush;
121
+ const signal = options.signal;
122
+ const recordPath = options.recordPath;
123
+ const maxWallClockMs = options.maxWallClockMs;
124
+ const harnessBudgetMs = options.harnessBudgetMs;
125
+ const shutdownGraceMs = options.shutdownGraceMs ?? 250;
126
+ const now = options.now ?? (() => new Date().toISOString());
127
+ const attempt = options.attempt ?? createAttemptIdentity(options.run.id);
128
+ const runSnapshot = structuredClone(options.run);
129
+ const attemptSnapshot = structuredClone(attempt);
130
+ const lifecycle = new LifecycleController(now);
131
+ const controller = new AbortController();
132
+ const executionStartedAt = Date.now();
133
+ const record = {
134
+ schemaVersion: "ebo.attempt/v1",
135
+ assessmentMode,
136
+ run: structuredClone(runSnapshot),
137
+ attempt: structuredClone(attemptSnapshot),
138
+ lifecycle: lifecycle.snapshot(),
139
+ partial: true,
140
+ };
141
+ if (evidenceFlush === undefined) {
142
+ record.capture = { status: "incomplete", error: "Evidence flush boundary is unavailable." };
143
+ }
144
+ let persistenceError;
145
+ let initialCheckpointError;
146
+ let reservationPath;
147
+ let allowTerminalReplacement = false;
148
+ if (recordPath !== undefined) {
149
+ await assertAttemptRecordPathAvailable(recordPath, runSnapshot, attemptSnapshot);
150
+ reservationPath = await reserveAttemptRecordPath(recordPath);
151
+ }
152
+ const persist = async () => {
153
+ record.lifecycle = lifecycle.snapshot();
154
+ if (recordPath === undefined)
155
+ return;
156
+ try {
157
+ record.persistence = { status: "complete" };
158
+ const publicationGate = lifecycle.state === "terminal"
159
+ && classification !== undefined
160
+ && classificationUnderlyingKind(classification) === "completed"
161
+ ? () => {
162
+ markCoordinatorBudgetIfExpired();
163
+ if (controller.signal.aborted || budgetExpired !== undefined)
164
+ throw new TerminalPublicationCancelled();
165
+ }
166
+ : undefined;
167
+ await writeAttemptRecordInternal(recordPath, record, allowTerminalReplacement, publicationGate);
168
+ }
169
+ catch (error) {
170
+ if (error instanceof TerminalPublicationCancelled) {
171
+ record.persistence = { status: "complete" };
172
+ return;
173
+ }
174
+ persistenceError ??= errorMessage(error);
175
+ record.persistence = { status: "incomplete", error: persistenceError };
176
+ }
177
+ };
178
+ const flush = async () => {
179
+ try {
180
+ if (evidenceFlush !== undefined) {
181
+ const evidenceFlushPromise = Promise.resolve(evidenceFlush.call(evidenceSink));
182
+ const outcome = await Promise.race([
183
+ evidenceFlushPromise.then(() => "complete"),
184
+ abortPromise.then(() => "aborted"),
185
+ ]);
186
+ if (outcome === "aborted") {
187
+ const settledFlush = await settleWithTimeout(evidenceFlushPromise, shutdownGraceMs);
188
+ if (settledFlush.status === "failed")
189
+ throw settledFlush.error;
190
+ if (settledFlush.status === "timed-out")
191
+ throw new Error("Evidence flush interrupted.");
192
+ }
193
+ }
194
+ }
195
+ catch (error) {
196
+ if (record.capture?.status !== "incomplete") {
197
+ record.capture = { status: "incomplete", error: errorMessage(error) };
198
+ }
199
+ }
200
+ if (record.capture?.status !== "incomplete" && record.harness?.captureError !== undefined) {
201
+ record.capture = { status: "incomplete", error: record.harness.captureError };
202
+ }
203
+ await persist();
204
+ };
205
+ let abortCause;
206
+ const externalAbort = () => {
207
+ abortCause ??= "interrupted";
208
+ controller.abort("interrupted");
209
+ };
210
+ if (signal?.aborted)
211
+ externalAbort();
212
+ else
213
+ signal?.addEventListener("abort", externalAbort, { once: true });
214
+ let budgetTimer;
215
+ let budgetExpired;
216
+ let harnessDeadlineAt;
217
+ let harnessResult;
218
+ let workspace;
219
+ let classification;
220
+ let cleanupStatus;
221
+ let harnessShutdown;
222
+ let workspaceShutdown;
223
+ let workspaceTerminationConfirmed = true;
224
+ let verifierShutdown;
225
+ let setupStarted = false;
226
+ const abortPromise = new Promise((resolveAbort) => {
227
+ if (controller.signal.aborted)
228
+ resolveAbort("aborted");
229
+ else
230
+ controller.signal.addEventListener("abort", () => resolveAbort("aborted"), { once: true });
231
+ });
232
+ const noteBudgetStop = (requested) => {
233
+ if (abortCause === "interrupted")
234
+ return;
235
+ const nowMs = Date.now();
236
+ const coordinatorDeadlineAt = maxWallClockMs === undefined ? undefined : executionStartedAt + maxWallClockMs;
237
+ const source = coordinatorDeadlineAt !== undefined && harnessDeadlineAt !== undefined
238
+ && nowMs >= coordinatorDeadlineAt && nowMs >= harnessDeadlineAt
239
+ ? coordinatorDeadlineAt <= harnessDeadlineAt ? "coordinator" : "harness"
240
+ : requested;
241
+ abortCause ??= "budget";
242
+ budgetExpired = source;
243
+ controller.abort("budget");
244
+ };
245
+ if (maxWallClockMs !== undefined) {
246
+ budgetTimer = setTimeout(() => noteBudgetStop("coordinator"), maxWallClockMs);
247
+ }
248
+ const markCoordinatorBudgetIfExpired = () => {
249
+ if (maxWallClockMs !== undefined && Date.now() - executionStartedAt >= maxWallClockMs) {
250
+ noteBudgetStop("coordinator");
251
+ }
252
+ };
253
+ try {
254
+ await flush();
255
+ if (persistenceError !== undefined) {
256
+ initialCheckpointError = persistenceError;
257
+ throw new Error(`Initial attempt checkpoint could not be persisted: ${persistenceError}`);
258
+ }
259
+ lifecycle.transition("setup");
260
+ await persist();
261
+ if (persistenceError !== undefined) {
262
+ initialCheckpointError ??= persistenceError;
263
+ throw new Error(`Initial attempt checkpoint could not be persisted: ${persistenceError}`);
264
+ }
265
+ try {
266
+ markCoordinatorBudgetIfExpired();
267
+ if (controller.signal.aborted) {
268
+ classification = abortClassification(abortCause, budgetExpired, undefined, workspace);
269
+ }
270
+ else {
271
+ setupStarted = true;
272
+ const setupPromise = Promise.resolve(workspaceSetup.call(workspaceCoordinator, {
273
+ run: structuredClone(runSnapshot),
274
+ attempt: structuredClone(attemptSnapshot),
275
+ signal: controller.signal,
276
+ registerShutdown: (shutdown) => { workspaceShutdown = shutdown; },
277
+ }));
278
+ const setupOutcome = await Promise.race([
279
+ setupPromise.then((value) => ({ kind: "result", value })),
280
+ abortPromise.then((value) => ({ kind: value })),
281
+ ]);
282
+ if (setupOutcome.kind === "aborted" || controller.signal.aborted) {
283
+ const settledSetup = await settleWithTimeout(setupPromise, shutdownGraceMs);
284
+ if (settledSetup.status === "completed") {
285
+ workspace = snapshotWorkspace(settledSetup.value);
286
+ record.workspace = snapshotWorkspace(workspace);
287
+ workspaceTerminationConfirmed = workspace.shutdownResult === undefined || workspace.shutdownResult.status === "completed";
288
+ if (workspaceShutdown !== undefined) {
289
+ workspaceTerminationConfirmed = false;
290
+ const shutdownResult = await shutdownWorkspace(workspaceShutdown, shutdownGraceMs);
291
+ if (shutdownResult !== undefined) {
292
+ workspace = { ...workspace, shutdownResult };
293
+ record.workspace = snapshotWorkspace(workspace);
294
+ workspaceTerminationConfirmed = shutdownResult.status === "completed";
295
+ }
296
+ }
297
+ }
298
+ else if (settledSetup.status === "failed") {
299
+ record.workspace = { status: "failed", error: errorMessage(settledSetup.error) };
300
+ if (workspaceShutdown !== undefined) {
301
+ workspaceTerminationConfirmed = false;
302
+ const shutdownResult = await shutdownWorkspace(workspaceShutdown, shutdownGraceMs);
303
+ if (shutdownResult !== undefined) {
304
+ record.workspace.shutdownResult = shutdownResult;
305
+ workspaceTerminationConfirmed = shutdownResult.status === "completed";
306
+ }
307
+ }
308
+ }
309
+ else {
310
+ record.workspace = { status: "failed", error: settledSetup.error };
311
+ workspaceTerminationConfirmed = false;
312
+ const shutdownResult = await shutdownWorkspace(workspaceShutdown, shutdownGraceMs);
313
+ if (shutdownResult !== undefined) {
314
+ record.workspace.shutdownResult = shutdownResult;
315
+ workspaceTerminationConfirmed = shutdownResult.status === "completed";
316
+ }
317
+ }
318
+ classification = abortClassification(abortCause, budgetExpired, undefined, workspace);
319
+ }
320
+ else {
321
+ workspace = snapshotWorkspace(setupOutcome.value);
322
+ record.workspace = snapshotWorkspace(workspace);
323
+ workspaceTerminationConfirmed = workspace.shutdownResult === undefined || workspace.shutdownResult.status === "completed";
324
+ if (!workspaceTerminationConfirmed) {
325
+ classification = infrastructureClassification("Workspace shutdown did not complete.", "workspace", workspace);
326
+ }
327
+ if (workspace.status === "failed") {
328
+ classification ??= infrastructureClassification("Workspace setup failed.", "workspace", workspace);
329
+ if (workspaceShutdown !== undefined) {
330
+ workspaceTerminationConfirmed = false;
331
+ const shutdownResult = await shutdownWorkspace(workspaceShutdown, shutdownGraceMs);
332
+ if (shutdownResult !== undefined) {
333
+ workspace = { ...workspace, shutdownResult };
334
+ record.workspace = snapshotWorkspace(workspace);
335
+ workspaceTerminationConfirmed = shutdownResult.status === "completed";
336
+ }
337
+ }
338
+ }
339
+ }
340
+ }
341
+ }
342
+ catch (error) {
343
+ const setupError = errorMessage(error);
344
+ markCoordinatorBudgetIfExpired();
345
+ record.workspace = { status: "failed", error: setupError };
346
+ if (workspaceShutdown !== undefined) {
347
+ workspaceTerminationConfirmed = false;
348
+ const shutdownResult = await shutdownWorkspace(workspaceShutdown, shutdownGraceMs);
349
+ if (shutdownResult !== undefined)
350
+ record.workspace = { ...record.workspace, shutdownResult };
351
+ workspaceTerminationConfirmed = shutdownResult?.status === "completed";
352
+ }
353
+ classification = controller.signal.aborted
354
+ ? abortClassification(abortCause, budgetExpired, setupError, workspace)
355
+ : infrastructureClassification(setupError, "workspace", workspace);
356
+ }
357
+ markCoordinatorBudgetIfExpired();
358
+ if (classification === undefined && controller.signal.aborted) {
359
+ classification = abortClassification(abortCause, budgetExpired, undefined, workspace);
360
+ }
361
+ await flush();
362
+ markCoordinatorBudgetIfExpired();
363
+ if (classification === undefined && controller.signal.aborted) {
364
+ classification = abortClassification(abortCause, budgetExpired, undefined, workspace);
365
+ }
366
+ if (classification === undefined) {
367
+ lifecycle.transition("running");
368
+ await persist();
369
+ markCoordinatorBudgetIfExpired();
370
+ if (controller.signal.aborted)
371
+ classification = abortClassification(abortCause, budgetExpired, undefined, workspace);
372
+ }
373
+ if (classification === undefined) {
374
+ const timers = [];
375
+ const installBudget = (milliseconds) => {
376
+ if (milliseconds === undefined)
377
+ return;
378
+ const timer = setTimeout(() => noteBudgetStop("harness"), milliseconds);
379
+ timers.push(timer);
380
+ };
381
+ const harnessStartedAt = Date.now();
382
+ harnessDeadlineAt = harnessBudgetMs === undefined ? undefined : harnessStartedAt + harnessBudgetMs;
383
+ installBudget(harnessBudgetMs);
384
+ let harnessPromise;
385
+ let harnessError;
386
+ try {
387
+ harnessPromise = Promise.resolve(harnessExecutor.call(options, {
388
+ run: structuredClone(runSnapshot),
389
+ attempt: structuredClone(attemptSnapshot),
390
+ signal: controller.signal,
391
+ ...(harnessBudgetMs === undefined ? {} : { budgetMs: harnessBudgetMs }),
392
+ ...(workspace === undefined ? {} : { workspace: snapshotWorkspace(workspace) }),
393
+ registerShutdown: (shutdown) => { harnessShutdown = shutdown; },
394
+ }));
395
+ }
396
+ catch (error) {
397
+ harnessPromise = Promise.reject(error);
398
+ }
399
+ if (harnessBudgetMs !== undefined && Date.now() - harnessStartedAt >= harnessBudgetMs) {
400
+ noteBudgetStop("harness");
401
+ }
402
+ markCoordinatorBudgetIfExpired();
403
+ let outcome;
404
+ try {
405
+ outcome = await Promise.race([
406
+ harnessPromise.then((value) => ({ kind: "result", value })),
407
+ abortPromise.then((value) => ({ kind: value })),
408
+ ]);
409
+ }
410
+ catch (error) {
411
+ harnessError = errorMessage(error);
412
+ classification = controller.signal.aborted
413
+ ? abortClassification(abortCause, budgetExpired, harnessError, workspace)
414
+ : infrastructureClassification(harnessError, "harness", workspace);
415
+ }
416
+ finally {
417
+ for (const timer of timers)
418
+ clearTimeout(timer);
419
+ }
420
+ const postBudgetHarness = budgetExpired === "harness"
421
+ ? {
422
+ status: "stopped",
423
+ stopReason: "budget",
424
+ reason: "Harness result arrived after its budget deadline.",
425
+ ...(harnessError === undefined ? {} : { error: harnessError }),
426
+ }
427
+ : undefined;
428
+ const rejectedHarness = postBudgetHarness ?? {
429
+ status: "failed",
430
+ ...(harnessError === undefined && classification?.reason === undefined ? {} : { error: harnessError ?? classification?.reason }),
431
+ };
432
+ if (outcome === undefined)
433
+ record.harness = rejectedHarness;
434
+ if (outcome === undefined && harnessShutdown !== undefined) {
435
+ const shutdownResult = await shutdownHarness(undefined, harnessShutdown, shutdownGraceMs);
436
+ record.harness = {
437
+ ...rejectedHarness,
438
+ ...(shutdownResult === undefined ? {} : { shutdownResult }),
439
+ };
440
+ record.harnessTerminationConfirmed = shutdownResult?.status === "completed";
441
+ }
442
+ else if (outcome === undefined) {
443
+ record.harnessTerminationConfirmed = true;
444
+ }
445
+ if (outcome !== undefined && (outcome.kind === "aborted" || controller.signal.aborted)) {
446
+ let harnessTerminationConfirmed = false;
447
+ if (outcome.kind === "result") {
448
+ harnessResult = normalizeHarnessBudgetResult(outcome.value, budgetExpired);
449
+ record.harness = durableHarnessResult(harnessResult);
450
+ harnessTerminationConfirmed = harnessResult.shutdownResult === undefined || harnessResult.shutdownResult.status === "completed";
451
+ }
452
+ const settledHarness = await settleWithTimeout(harnessPromise, shutdownGraceMs);
453
+ if (settledHarness.status === "completed" && harnessResult === undefined) {
454
+ harnessResult = normalizeHarnessBudgetResult(settledHarness.value, budgetExpired);
455
+ record.harness = durableHarnessResult(harnessResult);
456
+ harnessTerminationConfirmed = harnessResult.shutdownResult === undefined || harnessResult.shutdownResult.status === "completed";
457
+ }
458
+ else if (settledHarness.status === "failed" && harnessResult === undefined) {
459
+ record.harness = { status: "interrupted", error: errorMessage(settledHarness.error) };
460
+ harnessTerminationConfirmed = true;
461
+ }
462
+ else if (settledHarness.status === "timed-out" && harnessResult === undefined) {
463
+ record.harness = { status: "interrupted", error: settledHarness.error };
464
+ }
465
+ classification = abortClassification(abortCause, budgetExpired, undefined, workspace);
466
+ const shutdownResult = await shutdownHarness(harnessResult, harnessShutdown, shutdownGraceMs);
467
+ if (shutdownResult !== undefined) {
468
+ if (harnessResult !== undefined) {
469
+ harnessResult = { ...harnessResult, shutdownResult };
470
+ record.harness = durableHarnessResult(harnessResult);
471
+ }
472
+ else {
473
+ record.harness = { status: "interrupted", shutdownResult };
474
+ }
475
+ harnessTerminationConfirmed = shutdownResult.status === "completed";
476
+ }
477
+ if (!harnessTerminationConfirmed)
478
+ record.harness = {
479
+ ...(record.harness ?? { status: "interrupted" }),
480
+ error: record.harness?.error ?? "Harness execution did not terminate before cleanup.",
481
+ };
482
+ record.harnessTerminationConfirmed = harnessTerminationConfirmed;
483
+ }
484
+ else if (outcome?.kind === "result") {
485
+ harnessResult = normalizeHarnessShutdownFailure(normalizeHarnessBudgetResult(outcome.value, budgetExpired));
486
+ record.harness = durableHarnessResult(harnessResult);
487
+ classification = classifyHarness(harnessResult, workspace);
488
+ if (classification !== undefined && harnessResult.status !== "completed") {
489
+ let harnessTerminationConfirmed = harnessResult.shutdownResult === undefined || harnessResult.shutdownResult.status === "completed";
490
+ if (harnessShutdown !== undefined || harnessResult.shutdown !== undefined) {
491
+ harnessTerminationConfirmed = false;
492
+ const shutdownResult = await shutdownHarness(harnessResult, harnessShutdown, shutdownGraceMs);
493
+ if (shutdownResult !== undefined) {
494
+ harnessResult = { ...harnessResult, shutdownResult };
495
+ record.harness = durableHarnessResult(harnessResult);
496
+ harnessTerminationConfirmed = shutdownResult.status === "completed";
497
+ }
498
+ }
499
+ record.harnessTerminationConfirmed = harnessTerminationConfirmed;
500
+ }
501
+ if (classification === undefined && harnessResult.status === "completed") {
502
+ if (assessmentMode === "observational") {
503
+ markCoordinatorBudgetIfExpired();
504
+ classification = controller.signal.aborted
505
+ ? abortClassification(abortCause, budgetExpired, undefined, workspace)
506
+ : completedClassification(workspace);
507
+ }
508
+ else {
509
+ lifecycle.transition("verifying");
510
+ await persist();
511
+ markCoordinatorBudgetIfExpired();
512
+ if (controller.signal.aborted) {
513
+ classification = abortClassification(abortCause, budgetExpired, undefined, workspace);
514
+ }
515
+ else if (verifierExecutor === undefined) {
516
+ record.verifier = { status: "not-run" };
517
+ classification = infrastructureClassification("Verifier did not run.", "verifier", workspace);
518
+ }
519
+ else {
520
+ try {
521
+ const verifierPromise = Promise.resolve(verifierExecutor.call(options, {
522
+ run: structuredClone(runSnapshot),
523
+ attempt: structuredClone(attemptSnapshot),
524
+ signal: controller.signal,
525
+ ...(workspace === undefined ? {} : { workspace: snapshotWorkspace(workspace) }),
526
+ registerShutdown: (shutdown) => { verifierShutdown = shutdown; },
527
+ }));
528
+ const verifierOutcome = await Promise.race([
529
+ verifierPromise.then((value) => ({ kind: "result", value })),
530
+ abortPromise.then((value) => ({ kind: value })),
531
+ ]);
532
+ if (verifierOutcome.kind === "aborted" || controller.signal.aborted) {
533
+ let verifierTerminationConfirmed = false;
534
+ if (verifierOutcome.kind === "result") {
535
+ record.verifier = snapshotVerifier(verifierOutcome.value);
536
+ verifierTerminationConfirmed = record.verifier.shutdownResult === undefined || record.verifier.shutdownResult.status === "completed";
537
+ }
538
+ const settledVerifier = await settleWithTimeout(verifierPromise, shutdownGraceMs);
539
+ if (settledVerifier.status === "completed" && record.verifier === undefined) {
540
+ record.verifier = snapshotVerifier(settledVerifier.value);
541
+ verifierTerminationConfirmed = record.verifier.shutdownResult === undefined || record.verifier.shutdownResult.status === "completed";
542
+ }
543
+ if (settledVerifier.status === "failed" && record.verifier === undefined) {
544
+ record.verifier = { status: "error", error: errorMessage(settledVerifier.error) };
545
+ verifierTerminationConfirmed = true;
546
+ }
547
+ if (settledVerifier.status === "timed-out" && record.verifier === undefined) {
548
+ record.verifier = { status: "error", error: settledVerifier.error };
549
+ }
550
+ const shutdownResult = await shutdownVerifier(verifierShutdown, shutdownGraceMs);
551
+ if (shutdownResult !== undefined) {
552
+ record.verifier = {
553
+ ...(record.verifier ?? { status: "error" }),
554
+ shutdownResult,
555
+ };
556
+ verifierTerminationConfirmed = shutdownResult.status === "completed";
557
+ }
558
+ record.verifierTerminationConfirmed = verifierTerminationConfirmed;
559
+ classification = abortClassification(abortCause, budgetExpired, undefined, workspace);
560
+ }
561
+ else {
562
+ let verifier = normalizeVerifierShutdownFailure(snapshotVerifier(verifierOutcome.value));
563
+ record.verifier = verifier;
564
+ if (verifier.status !== "passed") {
565
+ let verifierTerminationConfirmed = verifier.shutdownResult === undefined || verifier.shutdownResult.status === "completed";
566
+ if (verifierShutdown !== undefined) {
567
+ verifierTerminationConfirmed = false;
568
+ const shutdownResult = await shutdownVerifier(verifierShutdown, shutdownGraceMs);
569
+ if (shutdownResult !== undefined) {
570
+ verifier = normalizeVerifierShutdownFailure({ ...verifier, shutdownResult });
571
+ record.verifier = verifier;
572
+ verifierTerminationConfirmed = shutdownResult.status === "completed";
573
+ }
574
+ }
575
+ record.verifierTerminationConfirmed = verifierTerminationConfirmed;
576
+ }
577
+ markCoordinatorBudgetIfExpired();
578
+ classification = controller.signal.aborted
579
+ ? abortClassification(abortCause, budgetExpired, undefined, workspace)
580
+ : classifyVerifier(verifier, workspace);
581
+ }
582
+ }
583
+ catch (error) {
584
+ const verifierError = errorMessage(error);
585
+ record.verifier = { status: "error", error: verifierError };
586
+ record.verifierTerminationConfirmed = true;
587
+ if (verifierShutdown !== undefined) {
588
+ const shutdownResult = await shutdownVerifier(verifierShutdown, shutdownGraceMs);
589
+ if (shutdownResult !== undefined) {
590
+ record.verifier.shutdownResult = shutdownResult;
591
+ record.verifierTerminationConfirmed = shutdownResult.status === "completed";
592
+ }
593
+ }
594
+ markCoordinatorBudgetIfExpired();
595
+ classification = controller.signal.aborted
596
+ ? abortClassification(abortCause, budgetExpired, verifierError, workspace)
597
+ : verifierErrorClassification(verifierError, workspace);
598
+ }
599
+ }
600
+ }
601
+ }
602
+ }
603
+ await flush();
604
+ }
605
+ }
606
+ catch (error) {
607
+ classification ??= infrastructureClassification(errorMessage(error), "runner", workspace);
608
+ }
609
+ finally {
610
+ if (lifecycle.state !== "cleaning" && lifecycle.state !== "terminal") {
611
+ try {
612
+ lifecycle.transition("cleaning");
613
+ }
614
+ catch (error) {
615
+ classification ??= infrastructureClassification(errorMessage(error), "runner", workspace);
616
+ const snapshot = lifecycle.snapshot();
617
+ lifecycle.transition("cleaning", snapshot.timestamps[snapshot.state] ?? snapshot.createdAt);
618
+ }
619
+ await persist();
620
+ }
621
+ if (!setupStarted && workspace === undefined) {
622
+ cleanupStatus = { status: "completed" };
623
+ }
624
+ else if (record.harnessTerminationConfirmed === false) {
625
+ cleanupStatus = { status: "timed-out", error: "Harness execution did not terminate before cleanup." };
626
+ }
627
+ else if (record.verifierTerminationConfirmed === false) {
628
+ cleanupStatus = { status: "timed-out", error: "Verifier execution did not terminate before cleanup." };
629
+ }
630
+ else if (!workspaceTerminationConfirmed) {
631
+ cleanupStatus = { status: "timed-out", error: "Workspace setup did not terminate before cleanup." };
632
+ }
633
+ else
634
+ try {
635
+ const cleanupPromise = Promise.resolve(workspaceCleanup?.call(workspaceCoordinator, {
636
+ run: structuredClone(runSnapshot),
637
+ attempt: structuredClone(attemptSnapshot),
638
+ signal: controller.signal,
639
+ outcome: structuredClone(classification?.terminal ?? infrastructureClassification("Run did not produce a terminal outcome.", "runner").terminal),
640
+ ...(workspace === undefined ? {} : { workspace: snapshotWorkspace(workspace) }),
641
+ }));
642
+ const cleanupOutcome = await Promise.race([
643
+ cleanupPromise.then(() => "completed"),
644
+ abortPromise.then(() => "aborted"),
645
+ ]);
646
+ if (cleanupOutcome === "aborted") {
647
+ const settledCleanup = await settleWithTimeout(cleanupPromise, shutdownGraceMs);
648
+ if (settledCleanup.status === "timed-out") {
649
+ cleanupStatus = { status: "timed-out", error: settledCleanup.error };
650
+ }
651
+ else if (settledCleanup.status === "failed") {
652
+ cleanupStatus = { status: "failed", error: errorMessage(settledCleanup.error) };
653
+ }
654
+ else {
655
+ cleanupStatus = { status: "completed" };
656
+ }
657
+ }
658
+ else {
659
+ cleanupStatus = { status: "completed" };
660
+ }
661
+ }
662
+ catch (error) {
663
+ cleanupStatus = { status: "failed", error: errorMessage(error) };
664
+ markCoordinatorBudgetIfExpired();
665
+ if (abortCause === "budget" && classificationUnderlyingKind(classification ?? completedClassification(workspace)) === "completed") {
666
+ classification = abortClassification(abortCause, budgetExpired, errorMessage(error), workspace);
667
+ }
668
+ else if (classification === undefined || classificationUnderlyingKind(classification) === "completed") {
669
+ classification = infrastructureClassification(errorMessage(error), "cleanup", workspace);
670
+ }
671
+ }
672
+ markCoordinatorBudgetIfExpired();
673
+ if (cleanupStatus?.status === "timed-out" && classificationUnderlyingKind(classification ?? infrastructureClassification("Run did not produce a terminal outcome.", "runner")) === "completed"
674
+ && abortCause === "budget") {
675
+ classification = abortClassification(abortCause, budgetExpired, cleanupStatus.error, workspace);
676
+ }
677
+ else if (cleanupStatus?.status === "timed-out" && classificationUnderlyingKind(classification ?? infrastructureClassification("Run did not produce a terminal outcome.", "runner")) === "completed") {
678
+ classification = infrastructureClassification(cleanupStatus.error ?? "Workspace cleanup timed out.", "cleanup", workspace);
679
+ }
680
+ record.cleanup = cleanupStatus;
681
+ await flush();
682
+ markCoordinatorBudgetIfExpired();
683
+ if (record.capture?.status !== "incomplete" && evidenceFlush !== undefined) {
684
+ record.capture = { status: "complete" };
685
+ }
686
+ if (budgetExpired !== undefined && classification?.kind === "completed") {
687
+ classification = abortClassification(abortCause, budgetExpired, undefined, workspace);
688
+ }
689
+ if (controller.signal.aborted && classification?.kind === "completed") {
690
+ classification = abortClassification(abortCause, budgetExpired, undefined, workspace);
691
+ }
692
+ if (persistenceError !== undefined && classification !== undefined && classificationUnderlyingKind(classification) === "completed") {
693
+ classification = infrastructureClassification(persistenceError, "runner", workspace);
694
+ }
695
+ if (record.capture?.status === "incomplete" && classification !== undefined && classification.kind !== "capture-incomplete") {
696
+ classification = {
697
+ kind: "capture-incomplete",
698
+ terminal: classification.terminal,
699
+ reason: record.capture.error,
700
+ source: "capture",
701
+ underlying: classification.kind,
702
+ ...(classification.source === undefined ? {} : { underlyingSource: classification.source }),
703
+ };
704
+ }
705
+ markCoordinatorBudgetIfExpired();
706
+ try {
707
+ lifecycle.transition("terminal");
708
+ }
709
+ catch (error) {
710
+ const runnerFailure = infrastructureClassification(errorMessage(error), "runner", workspace);
711
+ classification = record.capture?.status === "incomplete"
712
+ ? {
713
+ kind: "capture-incomplete",
714
+ terminal: runnerFailure.terminal,
715
+ ...(record.capture.error === undefined ? {} : { reason: record.capture.error }),
716
+ source: "capture",
717
+ underlying: runnerFailure.kind,
718
+ ...(runnerFailure.source === undefined ? {} : { underlyingSource: runnerFailure.source }),
719
+ }
720
+ : runnerFailure;
721
+ const fallbackTimestamp = lifecycle.snapshot().timestamps.cleaning ?? lifecycle.snapshot().createdAt;
722
+ lifecycle.transition("terminal", fallbackTimestamp);
723
+ }
724
+ classification ??= infrastructureClassification("Run did not produce a terminal outcome.", "runner", workspace);
725
+ record.classification = classification;
726
+ record.terminal = classification.terminal;
727
+ record.partial = classificationUnderlyingKind(classification) !== "completed"
728
+ || record.capture?.status === "incomplete";
729
+ const persistenceBeforeTerminal = persistenceError;
730
+ await persist();
731
+ markCoordinatorBudgetIfExpired();
732
+ if (classificationUnderlyingKind(classification) === "completed"
733
+ && (budgetExpired !== undefined || abortCause === "interrupted")) {
734
+ const correctedClassification = abortClassification(abortCause, budgetExpired, undefined, workspace);
735
+ classification = record.capture?.status === "incomplete"
736
+ ? {
737
+ kind: "capture-incomplete",
738
+ terminal: correctedClassification.terminal,
739
+ ...(record.capture.error === undefined ? {} : { reason: record.capture.error }),
740
+ source: "capture",
741
+ underlying: correctedClassification.kind,
742
+ ...(correctedClassification.source === undefined ? {} : { underlyingSource: correctedClassification.source }),
743
+ }
744
+ : correctedClassification;
745
+ record.classification = classification;
746
+ record.terminal = classification.terminal;
747
+ record.partial = true;
748
+ allowTerminalReplacement = true;
749
+ await persist();
750
+ allowTerminalReplacement = false;
751
+ }
752
+ if (persistenceError !== undefined && persistenceBeforeTerminal === undefined
753
+ && classificationUnderlyingKind(classification) === "completed") {
754
+ const failedPersistence = infrastructureClassification(persistenceError, "runner", workspace);
755
+ classification = record.capture?.status === "incomplete"
756
+ ? {
757
+ ...failedPersistence,
758
+ kind: "capture-incomplete",
759
+ source: "capture",
760
+ underlying: failedPersistence.kind,
761
+ ...(failedPersistence.source === undefined ? {} : { underlyingSource: failedPersistence.source }),
762
+ }
763
+ : failedPersistence;
764
+ record.classification = classification;
765
+ record.terminal = classification.terminal;
766
+ record.partial = true;
767
+ // The second write may still fail, but the returned record retains the
768
+ // persistence failure rather than reporting an unverified completion.
769
+ allowTerminalReplacement = true;
770
+ await persist();
771
+ allowTerminalReplacement = false;
772
+ }
773
+ if (budgetTimer !== undefined)
774
+ clearTimeout(budgetTimer);
775
+ signal?.removeEventListener("abort", externalAbort);
776
+ if (reservationPath !== undefined)
777
+ await rm(reservationPath, { force: true });
778
+ if (initialCheckpointError !== undefined) {
779
+ throw new Error(`Initial attempt checkpoint could not be persisted: ${initialCheckpointError}`);
780
+ }
781
+ }
782
+ return {
783
+ run: structuredClone(record.run),
784
+ attempt: structuredClone(record.attempt),
785
+ record: structuredClone(record),
786
+ terminal: structuredClone(record.terminal),
787
+ classification: structuredClone(record.classification),
788
+ };
789
+ }
790
+ export async function writeAttemptRecord(path, record) {
791
+ if (path.trim() === "")
792
+ throw new Error("Attempt record path is required.");
793
+ assertRecord(record);
794
+ assertJsonValue(record, "attempt record", new Set());
795
+ const destination = resolve(path);
796
+ await mkdir(dirname(destination), { recursive: true, mode: 0o700 });
797
+ await withRecordReservationLock(destination, () => writeAttemptRecordInternal(path, record));
798
+ }
799
+ async function writeAttemptRecordInternal(path, record, allowTerminalReplacement = false, publicationGate) {
800
+ assertRecord(record);
801
+ const destination = resolve(path);
802
+ await withRecordPublicationLock(destination, async () => {
803
+ const temporary = `${destination}.${randomUUID()}.tmp`;
804
+ const bytes = Buffer.from(`${JSON.stringify(record)}\n`, "utf8");
805
+ const handle = await open(temporary, "wx", 0o600);
806
+ try {
807
+ await writeFileHandleFully(handle, bytes, "Attempt record");
808
+ await handle.sync();
809
+ }
810
+ finally {
811
+ await handle.close();
812
+ }
813
+ try {
814
+ publicationGate?.();
815
+ try {
816
+ linkSync(temporary, destination);
817
+ unlinkSync(temporary);
818
+ syncDirectory(dirname(destination));
819
+ }
820
+ catch (error) {
821
+ if (error.code !== "EEXIST")
822
+ throw error;
823
+ const existing = readAttemptRecordSync(destination);
824
+ if (existing.run.id !== record.run.id || existing.attempt.id !== record.attempt.id) {
825
+ throw new Error(`Attempt record "${path}" already belongs to another attempt.`);
826
+ }
827
+ if (!allowTerminalReplacement && (existing.terminal !== undefined || existing.classification !== undefined)) {
828
+ throw new Error(`Attempt record "${path}" is already terminal.`);
829
+ }
830
+ if (!allowTerminalReplacement)
831
+ assertCheckpointDoesNotRegress(existing, record);
832
+ publicationGate?.();
833
+ renameSync(temporary, destination);
834
+ syncDirectory(dirname(destination));
835
+ }
836
+ }
837
+ catch (error) {
838
+ await rm(temporary, { force: true });
839
+ throw error;
840
+ }
841
+ });
842
+ }
843
+ async function withRecordReservationLock(destination, operation) {
844
+ const lockPath = `${destination}.lock`;
845
+ let lock;
846
+ try {
847
+ lock = await open(lockPath, "wx", 0o600);
848
+ }
849
+ catch (error) {
850
+ throw new Error(`Attempt record "${destination}" is already reserved: ${errorMessage(error)}`);
851
+ }
852
+ try {
853
+ return await operation();
854
+ }
855
+ finally {
856
+ await lock.close();
857
+ await rm(lockPath, { force: true });
858
+ }
859
+ }
860
+ async function withRecordPublicationLock(destination, operation) {
861
+ const lockPath = `${destination}.write-lock`;
862
+ let lock;
863
+ try {
864
+ lock = await open(lockPath, "wx", 0o600);
865
+ }
866
+ catch (error) {
867
+ throw new Error(`Attempt record "${destination}" is already being published: ${errorMessage(error)}`);
868
+ }
869
+ try {
870
+ return await operation();
871
+ }
872
+ finally {
873
+ await lock.close();
874
+ await rm(lockPath, { force: true });
875
+ }
876
+ }
877
+ function syncDirectory(path) {
878
+ const directory = openSync(path, "r");
879
+ try {
880
+ fsyncSync(directory);
881
+ }
882
+ finally {
883
+ closeSync(directory);
884
+ }
885
+ }
886
+ async function writeFileHandleFully(handle, bytes, label) {
887
+ let offset = 0;
888
+ while (offset < bytes.length) {
889
+ const { bytesWritten } = await handle.write(bytes.subarray(offset));
890
+ if (bytesWritten <= 0)
891
+ throw new Error(`${label} write made no progress.`);
892
+ offset += bytesWritten;
893
+ }
894
+ }
895
+ export async function readAttemptRecord(path) {
896
+ const bytes = await readFile(resolve(path));
897
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
898
+ const value = JSON.parse(text);
899
+ assertRecord(value);
900
+ return value;
901
+ }
902
+ function readAttemptRecordSync(path) {
903
+ const bytes = readFileSync(resolve(path));
904
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
905
+ const value = JSON.parse(text);
906
+ assertRecord(value);
907
+ return value;
908
+ }
909
+ async function assertAttemptRecordPathAvailable(path, run, attempt) {
910
+ const destination = resolve(path);
911
+ let exists = false;
912
+ try {
913
+ await lstat(destination);
914
+ exists = true;
915
+ }
916
+ catch (error) {
917
+ if (error.code !== "ENOENT")
918
+ throw error;
919
+ }
920
+ if (!exists)
921
+ return;
922
+ const existing = await readAttemptRecord(destination);
923
+ if (existing.run.id !== run.id || existing.attempt.id !== attempt.id) {
924
+ throw new Error(`Attempt record "${path}" already belongs to another attempt.`);
925
+ }
926
+ throw new Error(`Attempt record "${path}" is already owned; use a new linked attempt.`);
927
+ }
928
+ async function reserveAttemptRecordPath(path) {
929
+ const reservation = `${resolve(path)}.lock`;
930
+ await mkdir(dirname(reservation), { recursive: true, mode: 0o700 });
931
+ let handle;
932
+ let created = false;
933
+ try {
934
+ handle = await open(reservation, "wx", 0o600);
935
+ created = true;
936
+ await writeFileHandleFully(handle, Buffer.from(`${process.pid}\n`, "utf8"), "Attempt record reservation");
937
+ await handle.sync();
938
+ return reservation;
939
+ }
940
+ catch (error) {
941
+ if (created)
942
+ await rm(reservation, { force: true });
943
+ throw new Error(`Attempt record "${path}" is already reserved: ${errorMessage(error)}`);
944
+ }
945
+ finally {
946
+ await handle?.close();
947
+ }
948
+ }
949
+ function validateOptions(options) {
950
+ createRunIdentity({
951
+ id: options.run.id,
952
+ taskId: options.run.taskId,
953
+ modelId: options.run.modelId,
954
+ harnessId: options.run.harnessId,
955
+ });
956
+ if (options.attempt !== undefined) {
957
+ createAttemptIdentity(options.run.id, options.attempt.number, options.attempt.id, options.attempt.retryOf);
958
+ }
959
+ if (options.assessmentMode !== undefined && options.assessmentMode !== "observational" && options.assessmentMode !== "verified") {
960
+ throw new Error("Assessment mode must be observational or verified.");
961
+ }
962
+ if (options.assessmentMode === "observational" && options.verifier !== undefined) {
963
+ throw new Error("Observational attempts cannot execute a verifier.");
964
+ }
965
+ if (!Number.isSafeInteger(options.maxWallClockMs) && options.maxWallClockMs !== undefined) {
966
+ throw new Error("Coordinator wall-clock budget must be a positive safe integer.");
967
+ }
968
+ if (options.maxWallClockMs !== undefined && options.maxWallClockMs < 1) {
969
+ throw new Error("Coordinator wall-clock budget must be a positive safe integer.");
970
+ }
971
+ if (!Number.isSafeInteger(options.harnessBudgetMs) && options.harnessBudgetMs !== undefined) {
972
+ throw new Error("Harness budget must be a positive safe integer.");
973
+ }
974
+ if (options.harnessBudgetMs !== undefined && options.harnessBudgetMs < 1) {
975
+ throw new Error("Harness budget must be a positive safe integer.");
976
+ }
977
+ if (!Number.isSafeInteger(options.shutdownGraceMs) && options.shutdownGraceMs !== undefined) {
978
+ throw new Error("Shutdown grace period must be a nonnegative safe integer.");
979
+ }
980
+ if (options.shutdownGraceMs !== undefined && options.shutdownGraceMs < 0) {
981
+ throw new Error("Shutdown grace period must be a nonnegative safe integer.");
982
+ }
983
+ for (const [label, milliseconds] of [
984
+ ["Coordinator wall-clock budget", options.maxWallClockMs],
985
+ ["Harness budget", options.harnessBudgetMs],
986
+ ["Shutdown grace period", options.shutdownGraceMs],
987
+ ]) {
988
+ if (milliseconds !== undefined && milliseconds > MAX_TIMER_MS) {
989
+ throw new Error(`${label} must not exceed the Node timer maximum.`);
990
+ }
991
+ }
992
+ }
993
+ function assertCheckpointDoesNotRegress(existing, incoming) {
994
+ const existingProgress = LIFECYCLE_PROGRESS[existing.lifecycle.state];
995
+ const incomingProgress = LIFECYCLE_PROGRESS[incoming.lifecycle.state];
996
+ if (incomingProgress < existingProgress || incoming.lifecycle.transitions.length < existing.lifecycle.transitions.length) {
997
+ throw new Error("Attempt record update regresses lifecycle progress.");
998
+ }
999
+ for (const [index, transition] of existing.lifecycle.transitions.entries()) {
1000
+ const incomingTransition = incoming.lifecycle.transitions[index];
1001
+ if (incomingTransition.from !== transition.from || incomingTransition.to !== transition.to || incomingTransition.at !== transition.at) {
1002
+ throw new Error("Attempt record update changes retained lifecycle transitions.");
1003
+ }
1004
+ }
1005
+ if (!sameJsonValue(existing.run, incoming.run) || !sameJsonValue(existing.attempt, incoming.attempt)
1006
+ || existing.assessmentMode !== incoming.assessmentMode
1007
+ || existing.lifecycle.createdAt !== incoming.lifecycle.createdAt) {
1008
+ throw new Error("Attempt record update changes retained creation metadata.");
1009
+ }
1010
+ for (const field of [
1011
+ "workspace",
1012
+ "harness",
1013
+ "verifier",
1014
+ "harnessTerminationConfirmed",
1015
+ "verifierTerminationConfirmed",
1016
+ "cleanup",
1017
+ "capture",
1018
+ ]) {
1019
+ if (existing[field] !== undefined && (incoming[field] === undefined || !sameJsonValue(existing[field], incoming[field]))) {
1020
+ throw new Error(`Attempt record update changes retained ${field} evidence.`);
1021
+ }
1022
+ }
1023
+ }
1024
+ function sameJsonValue(left, right) {
1025
+ if (Object.is(left, right))
1026
+ return true;
1027
+ if (Array.isArray(left) || Array.isArray(right)) {
1028
+ return Array.isArray(left) && Array.isArray(right)
1029
+ && left.length === right.length
1030
+ && left.every((value, index) => sameJsonValue(value, right[index]));
1031
+ }
1032
+ if (isRecord(left) || isRecord(right)) {
1033
+ if (!isRecord(left) || !isRecord(right))
1034
+ return false;
1035
+ const leftKeys = Object.keys(left).sort();
1036
+ const rightKeys = Object.keys(right).sort();
1037
+ return leftKeys.length === rightKeys.length
1038
+ && leftKeys.every((key, index) => key === rightKeys[index] && sameJsonValue(left[key], right[key]));
1039
+ }
1040
+ return false;
1041
+ }
1042
+ function classifyHarness(result, workspace) {
1043
+ switch (result.status) {
1044
+ case "completed":
1045
+ return undefined;
1046
+ case "failed":
1047
+ return infrastructureClassification(result.failureClass === "task"
1048
+ ? result.reason ?? "Harness reported task failure without verifier evidence."
1049
+ : result.reason ?? "Harness failed.", "harness", workspace);
1050
+ case "stopped":
1051
+ if (result.stopReason === "policy")
1052
+ return policyClassification(result.reason, workspace);
1053
+ if (result.stopReason === "budget")
1054
+ return budgetClassification("harness", result.reason, workspace);
1055
+ return infrastructureClassification("Harness stopped without an explicit stop reason.", "harness", workspace);
1056
+ case "interrupted":
1057
+ return interruptedClassification(result.reason, "harness", workspace);
1058
+ }
1059
+ }
1060
+ function classifyVerifier(result, workspace) {
1061
+ switch (result.status) {
1062
+ case "passed":
1063
+ return completedClassification(workspace);
1064
+ case "failed":
1065
+ return taskClassification(result.error ?? "Verifier reported task failure.", workspace, "verifier");
1066
+ case "error":
1067
+ return verifierErrorClassification(result.error ?? "Verifier failed to execute.", workspace);
1068
+ case "not-run":
1069
+ return infrastructureClassification("Verifier did not run.", "verifier", workspace);
1070
+ }
1071
+ }
1072
+ function completedClassification(workspace) {
1073
+ if (workspace === undefined || workspace.status !== "ready" || workspace.retained === false
1074
+ || workspace.artifactId === undefined) {
1075
+ return infrastructureClassification("Completed run requires a retained workspace artifact.", "workspace", workspace);
1076
+ }
1077
+ return {
1078
+ kind: "completed",
1079
+ terminal: { state: "completed", failureClass: "none", stopReason: "none", workspaceArtifactId: workspace.artifactId },
1080
+ source: "runner",
1081
+ };
1082
+ }
1083
+ function taskClassification(reason, workspace, source) {
1084
+ if (workspace === undefined || workspace.status !== "ready" || workspace.retained === false
1085
+ || workspace.artifactId === undefined) {
1086
+ return infrastructureClassification("Task failure requires a retained workspace artifact.", "workspace", workspace);
1087
+ }
1088
+ return {
1089
+ kind: "task-failure",
1090
+ terminal: { state: "failed", failureClass: "task", stopReason: "none", workspaceArtifactId: workspace.artifactId },
1091
+ reason,
1092
+ source,
1093
+ };
1094
+ }
1095
+ function infrastructureClassification(reason, source, workspace) {
1096
+ const workspaceArtifactId = retainedWorkspaceArtifactId(workspace);
1097
+ return {
1098
+ kind: source === "verifier" ? "verifier-error" : "infrastructure-failure",
1099
+ terminal: { state: "failed", failureClass: "infrastructure", stopReason: "none", ...(workspaceArtifactId === undefined ? {} : { workspaceArtifactId }) },
1100
+ reason,
1101
+ source,
1102
+ };
1103
+ }
1104
+ function verifierErrorClassification(reason, workspace) {
1105
+ const workspaceArtifactId = retainedWorkspaceArtifactId(workspace);
1106
+ return {
1107
+ kind: "verifier-error",
1108
+ terminal: { state: "failed", failureClass: "infrastructure", stopReason: "none", ...(workspaceArtifactId === undefined ? {} : { workspaceArtifactId }) },
1109
+ reason,
1110
+ source: "verifier",
1111
+ };
1112
+ }
1113
+ function retainedWorkspaceArtifactId(workspace) {
1114
+ return workspace !== undefined && workspace.retained !== false && workspace.artifactId !== undefined
1115
+ && (workspace.status === "ready" || workspace.retained === true)
1116
+ ? workspace.artifactId
1117
+ : undefined;
1118
+ }
1119
+ function budgetClassification(source, reason, workspace) {
1120
+ const workspaceArtifactId = retainedWorkspaceArtifactId(workspace);
1121
+ return {
1122
+ kind: "budget-stop",
1123
+ terminal: { state: "stopped", failureClass: "none", stopReason: "budget", ...(workspaceArtifactId === undefined ? {} : { workspaceArtifactId }) },
1124
+ ...(reason === undefined ? {} : { reason }),
1125
+ source: source === "harness" ? "harness" : "runner",
1126
+ };
1127
+ }
1128
+ function policyClassification(reason, workspace) {
1129
+ const workspaceArtifactId = retainedWorkspaceArtifactId(workspace);
1130
+ return {
1131
+ kind: "policy-stop",
1132
+ terminal: { state: "stopped", failureClass: "none", stopReason: "policy", ...(workspaceArtifactId === undefined ? {} : { workspaceArtifactId }) },
1133
+ ...(reason === undefined ? {} : { reason }),
1134
+ source: "harness",
1135
+ };
1136
+ }
1137
+ function interruptedClassification(reason, source = "runner", workspace) {
1138
+ const workspaceArtifactId = retainedWorkspaceArtifactId(workspace);
1139
+ return {
1140
+ kind: "interrupted",
1141
+ terminal: { state: "interrupted", failureClass: "infrastructure", stopReason: "none", ...(workspaceArtifactId === undefined ? {} : { workspaceArtifactId }) },
1142
+ ...(reason === undefined ? {} : { reason }),
1143
+ source,
1144
+ };
1145
+ }
1146
+ function abortClassification(cause, budget, reason, workspace) {
1147
+ return cause === "interrupted"
1148
+ ? interruptedClassification(reason, "runner", workspace)
1149
+ : budgetClassification(budget ?? "coordinator", reason, workspace);
1150
+ }
1151
+ function classificationUnderlyingKind(classification) {
1152
+ return classification.kind === "capture-incomplete"
1153
+ ? classification.underlying ?? classification.kind
1154
+ : classification.kind;
1155
+ }
1156
+ function snapshotWorkspace(workspace) {
1157
+ assertWorkspaceResult(workspace);
1158
+ assertJsonValue(workspace, "workspace", new Set());
1159
+ return structuredClone(workspace);
1160
+ }
1161
+ function snapshotVerifier(verifier) {
1162
+ assertVerifierResult(verifier);
1163
+ assertJsonValue(verifier, "verifier", new Set());
1164
+ return structuredClone(verifier);
1165
+ }
1166
+ async function shutdownHarness(result, independentShutdown, graceMs) {
1167
+ const shutdownCallback = independentShutdown ?? result?.shutdown;
1168
+ if (shutdownCallback === undefined)
1169
+ return undefined;
1170
+ let timer;
1171
+ try {
1172
+ const shutdown = Promise.resolve(shutdownCallback());
1173
+ const settled = await Promise.race([
1174
+ shutdown.then(() => ({ status: "completed" })),
1175
+ new Promise((resolvePromise) => {
1176
+ timer = setTimeout(() => resolvePromise({ status: "timed-out", error: "Harness shutdown exceeded its grace period." }), graceMs);
1177
+ }),
1178
+ ]);
1179
+ return settled;
1180
+ }
1181
+ catch (error) {
1182
+ return { status: "failed", error: errorMessage(error) };
1183
+ }
1184
+ finally {
1185
+ if (timer !== undefined)
1186
+ clearTimeout(timer);
1187
+ }
1188
+ }
1189
+ async function shutdownWorkspace(shutdownCallback, graceMs) {
1190
+ if (shutdownCallback === undefined)
1191
+ return undefined;
1192
+ let timer;
1193
+ try {
1194
+ const shutdown = Promise.resolve(shutdownCallback());
1195
+ return await Promise.race([
1196
+ shutdown.then(() => ({ status: "completed" }), (error) => ({ status: "failed", error: errorMessage(error) })),
1197
+ new Promise((resolvePromise) => {
1198
+ timer = setTimeout(() => resolvePromise({ status: "timed-out", error: "Workspace shutdown exceeded its grace period." }), graceMs);
1199
+ }),
1200
+ ]);
1201
+ }
1202
+ finally {
1203
+ if (timer !== undefined)
1204
+ clearTimeout(timer);
1205
+ }
1206
+ }
1207
+ async function shutdownVerifier(shutdownCallback, graceMs) {
1208
+ if (shutdownCallback === undefined)
1209
+ return undefined;
1210
+ let timer;
1211
+ try {
1212
+ const shutdown = Promise.resolve(shutdownCallback());
1213
+ return await Promise.race([
1214
+ shutdown.then(() => ({ status: "completed" }), (error) => ({ status: "failed", error: errorMessage(error) })),
1215
+ new Promise((resolvePromise) => {
1216
+ timer = setTimeout(() => resolvePromise({ status: "timed-out", error: "Verifier shutdown exceeded its grace period." }), graceMs);
1217
+ }),
1218
+ ]);
1219
+ }
1220
+ catch (error) {
1221
+ return { status: "failed", error: errorMessage(error) };
1222
+ }
1223
+ finally {
1224
+ if (timer !== undefined)
1225
+ clearTimeout(timer);
1226
+ }
1227
+ }
1228
+ function durableHarnessResult(result) {
1229
+ const { shutdown: _shutdown, ...durable } = result;
1230
+ assertHarnessResult(durable);
1231
+ assertJsonValue(durable, "harness", new Set());
1232
+ return structuredClone(durable);
1233
+ }
1234
+ function normalizeHarnessBudgetResult(result, budgetExpired) {
1235
+ if (budgetExpired !== "harness" || result.status === "stopped" && result.stopReason === "budget")
1236
+ return result;
1237
+ const { status: nativeStatus, failureClass: nativeFailureClass, stopReason: nativeStopReason, reason: nativeReason, error: nativeError, evidence: nativeEvidence, ...rest } = result;
1238
+ return {
1239
+ ...rest,
1240
+ status: "stopped",
1241
+ stopReason: "budget",
1242
+ reason: nativeReason ?? nativeError ?? "Harness result arrived after its budget deadline.",
1243
+ evidence: {
1244
+ nativeStatus,
1245
+ ...(nativeFailureClass === undefined ? {} : { nativeFailureClass }),
1246
+ ...(nativeStopReason === undefined ? {} : { nativeStopReason }),
1247
+ ...(nativeError === undefined ? {} : { nativeError }),
1248
+ ...(nativeEvidence === undefined ? {} : { nativeEvidence }),
1249
+ },
1250
+ };
1251
+ }
1252
+ function normalizeHarnessShutdownFailure(result) {
1253
+ if (result.status !== "completed" || result.shutdownResult === undefined || result.shutdownResult.status === "completed")
1254
+ return result;
1255
+ return {
1256
+ ...result,
1257
+ status: "failed",
1258
+ failureClass: "infrastructure",
1259
+ reason: result.reason ?? "Harness shutdown did not complete.",
1260
+ evidence: {
1261
+ nativeStatus: "completed",
1262
+ ...(result.evidence === undefined ? {} : { nativeEvidence: result.evidence }),
1263
+ },
1264
+ };
1265
+ }
1266
+ function normalizeVerifierShutdownFailure(result) {
1267
+ if ((result.status !== "passed" && result.status !== "failed")
1268
+ || result.shutdownResult === undefined || result.shutdownResult.status === "completed")
1269
+ return result;
1270
+ return {
1271
+ ...result,
1272
+ status: "error",
1273
+ error: result.error ?? "Verifier shutdown did not complete.",
1274
+ evidence: {
1275
+ nativeStatus: result.status,
1276
+ ...(result.evidence === undefined ? {} : { nativeEvidence: result.evidence }),
1277
+ },
1278
+ };
1279
+ }
1280
+ async function settleWithTimeout(promise, milliseconds) {
1281
+ let timer;
1282
+ try {
1283
+ return await Promise.race([
1284
+ promise.then((value) => ({ status: "completed", value }), (error) => ({ status: "failed", error })),
1285
+ new Promise((resolvePromise) => {
1286
+ timer = setTimeout(() => resolvePromise({ status: "timed-out", error: "Operation exceeded its cancellation grace period." }), milliseconds);
1287
+ }),
1288
+ ]);
1289
+ }
1290
+ finally {
1291
+ if (timer !== undefined)
1292
+ clearTimeout(timer);
1293
+ }
1294
+ }
1295
+ function assertIdentity(value, label) {
1296
+ if (typeof value !== "string" || value.trim() === "" || [...value].length > 256) {
1297
+ throw new Error(`${label} must be a non-empty identifier of at most 256 characters.`);
1298
+ }
1299
+ }
1300
+ function assertRecord(value) {
1301
+ if (!isRecord(value)) {
1302
+ throw new Error("Attempt record must be a JSON object.");
1303
+ }
1304
+ assertJsonValue(value, "attempt record", new Set());
1305
+ if (value.schemaVersion !== "ebo.attempt/v1")
1306
+ throw new Error("Attempt record schemaVersion is invalid.");
1307
+ if (value.assessmentMode === undefined)
1308
+ value.assessmentMode = "verified";
1309
+ if (value.assessmentMode !== "observational" && value.assessmentMode !== "verified") {
1310
+ throw new Error("Attempt record assessment mode is invalid.");
1311
+ }
1312
+ const run = value.run;
1313
+ if (!isRecord(run))
1314
+ throw new Error("Attempt record run is invalid.");
1315
+ assertIdentityValue(run.id, "Run ID");
1316
+ assertIdentityValue(run.taskId, "Task ID");
1317
+ assertIdentityValue(run.modelId, "Model ID");
1318
+ assertIdentityValue(run.harnessId, "Harness ID");
1319
+ const attempt = value.attempt;
1320
+ if (!isRecord(attempt))
1321
+ throw new Error("Attempt record attempt is invalid.");
1322
+ assertIdentityValue(attempt.id, "Attempt ID");
1323
+ const attemptNumber = typeof attempt.number === "number" ? attempt.number : NaN;
1324
+ if (!Number.isSafeInteger(attemptNumber) || attemptNumber < 1)
1325
+ throw new Error("Attempt number is invalid.");
1326
+ if (attempt.retryOf !== undefined)
1327
+ assertIdentityValue(attempt.retryOf, "Retry attempt ID");
1328
+ if ((attemptNumber > 1) !== (attempt.retryOf !== undefined))
1329
+ throw new Error("Attempt retry lineage is invalid.");
1330
+ if (attempt.retryOf === attempt.id)
1331
+ throw new Error("Attempt retry lineage is self-referential.");
1332
+ const lifecycle = value.lifecycle;
1333
+ if (!isRecord(lifecycle))
1334
+ throw new Error("Attempt record lifecycle is invalid.");
1335
+ if (!isLifecycleState(lifecycle.state))
1336
+ throw new Error("Attempt lifecycle state is invalid.");
1337
+ assertTimestampValue(lifecycle.createdAt, "Lifecycle createdAt");
1338
+ assertTimestampValue(lifecycle.startedAt, "Lifecycle startedAt");
1339
+ if (lifecycle.endedAt !== undefined)
1340
+ assertTimestampValue(lifecycle.endedAt, "Lifecycle endedAt");
1341
+ if (!isRecord(lifecycle.timestamps))
1342
+ throw new Error("Lifecycle timestamps are invalid.");
1343
+ if (!Array.isArray(lifecycle.transitions))
1344
+ throw new Error("Lifecycle transitions are invalid.");
1345
+ let state = "created";
1346
+ const visitedStates = new Set(["created"]);
1347
+ for (const transition of lifecycle.transitions) {
1348
+ if (!isRecord(transition) || !isLifecycleState(transition.from) || !isLifecycleState(transition.to)) {
1349
+ throw new Error("Lifecycle transition is invalid.");
1350
+ }
1351
+ assertTimestampValue(transition.at, "Lifecycle transition timestamp");
1352
+ if (transition.from !== state || !ALLOWED_TRANSITIONS[state].includes(transition.to)) {
1353
+ throw new Error("Lifecycle transition sequence is invalid.");
1354
+ }
1355
+ state = transition.to;
1356
+ visitedStates.add(state);
1357
+ }
1358
+ if (state !== lifecycle.state)
1359
+ throw new Error("Lifecycle state does not match its transitions.");
1360
+ for (const [timestampState, timestamp] of Object.entries(lifecycle.timestamps)) {
1361
+ if (!isLifecycleState(timestampState))
1362
+ throw new Error("Lifecycle timestamp state is invalid.");
1363
+ if (!visitedStates.has(timestampState))
1364
+ throw new Error("Lifecycle timestamp state is not present in its transitions.");
1365
+ assertTimestampValue(timestamp, `Lifecycle timestamp ${timestampState}`);
1366
+ }
1367
+ for (const visitedState of visitedStates) {
1368
+ if (lifecycle.timestamps[visitedState] === undefined)
1369
+ throw new Error(`Lifecycle timestamp ${visitedState} is missing.`);
1370
+ }
1371
+ for (const transition of lifecycle.transitions) {
1372
+ if (lifecycle.timestamps[transition.to] !== transition.at) {
1373
+ throw new Error("Lifecycle transition timestamp disagrees with its state timestamp.");
1374
+ }
1375
+ }
1376
+ if (lifecycle.timestamps.created !== lifecycle.createdAt)
1377
+ throw new Error("Lifecycle created timestamp is inconsistent.");
1378
+ if (lifecycle.startedAt !== (lifecycle.timestamps.setup ?? lifecycle.createdAt)) {
1379
+ throw new Error("Lifecycle started timestamp is inconsistent.");
1380
+ }
1381
+ if (visitedStates.has("terminal")) {
1382
+ if (lifecycle.endedAt === undefined || lifecycle.endedAt !== lifecycle.timestamps.terminal) {
1383
+ throw new Error("Lifecycle ended timestamp is inconsistent.");
1384
+ }
1385
+ }
1386
+ else if (lifecycle.endedAt !== undefined) {
1387
+ throw new Error("Lifecycle ended timestamp is present before terminal state.");
1388
+ }
1389
+ if (typeof value.partial !== "boolean")
1390
+ throw new Error("Attempt record partial state is invalid.");
1391
+ if (lifecycle.state !== "terminal" && value.partial !== true) {
1392
+ throw new Error("Nonterminal attempt records must remain partial.");
1393
+ }
1394
+ const workspaceShutdownFailed = isRecord(value.workspace)
1395
+ && isRecord(value.workspace.shutdownResult)
1396
+ && value.workspace.shutdownResult.status !== "completed";
1397
+ if (isRecord(value.workspace)
1398
+ && (value.workspace.status === "failed" || workspaceShutdownFailed)
1399
+ && (visitedStates.has("running") || visitedStates.has("verifying"))) {
1400
+ throw new Error("Workspace failure records cannot include execution lifecycle phases.");
1401
+ }
1402
+ if ((value.verifier !== undefined || value.verifierTerminationConfirmed !== undefined)
1403
+ && !visitedStates.has("verifying")) {
1404
+ throw new Error("Verifier evidence requires the verifying lifecycle phase.");
1405
+ }
1406
+ if (value.assessmentMode === "observational"
1407
+ && (visitedStates.has("verifying") || value.verifier !== undefined || value.verifierTerminationConfirmed !== undefined)) {
1408
+ throw new Error("Observational attempt records cannot contain verifier execution evidence.");
1409
+ }
1410
+ if (value.harnessTerminationConfirmed !== undefined && typeof value.harnessTerminationConfirmed !== "boolean") {
1411
+ throw new Error("Harness termination confirmation is invalid.");
1412
+ }
1413
+ if (value.verifierTerminationConfirmed !== undefined && typeof value.verifierTerminationConfirmed !== "boolean") {
1414
+ throw new Error("Verifier termination confirmation is invalid.");
1415
+ }
1416
+ if (value.workspace !== undefined)
1417
+ assertWorkspaceResult(value.workspace);
1418
+ if (value.harness !== undefined)
1419
+ assertHarnessResult(value.harness);
1420
+ if (value.verifier !== undefined)
1421
+ assertVerifierResult(value.verifier);
1422
+ assertTerminationConfirmation(value.harness, value.harnessTerminationConfirmed, "Harness");
1423
+ assertTerminationConfirmation(value.verifier, value.verifierTerminationConfirmed, "Verifier");
1424
+ if (value.terminal !== undefined)
1425
+ assertTerminalRecord(value.terminal);
1426
+ if ((value.terminal === undefined) !== (value.classification === undefined)) {
1427
+ throw new Error("Attempt terminal and classification records must be paired.");
1428
+ }
1429
+ if (lifecycle.state === "terminal" && value.terminal === undefined) {
1430
+ throw new Error("Terminal attempt records require terminal and classification outcomes.");
1431
+ }
1432
+ if (value.terminal !== undefined && lifecycle.state !== "terminal") {
1433
+ throw new Error("Terminal attempt records must have terminal lifecycle state.");
1434
+ }
1435
+ if (value.classification !== undefined) {
1436
+ if (!isRecord(value.classification) || !isClassificationKind(value.classification.kind)) {
1437
+ throw new Error("Attempt classification is invalid.");
1438
+ }
1439
+ if (value.classification.reason !== undefined)
1440
+ assertTimestampValue(value.classification.reason, "Classification reason");
1441
+ if (value.classification.source !== undefined && !["runner", "workspace", "harness", "verifier", "cleanup", "capture"].includes(String(value.classification.source))) {
1442
+ throw new Error("Classification source is invalid.");
1443
+ }
1444
+ if (value.classification.underlyingSource !== undefined
1445
+ && !["runner", "workspace", "harness", "verifier", "cleanup", "capture"].includes(String(value.classification.underlyingSource))) {
1446
+ throw new Error("Classification underlying source is invalid.");
1447
+ }
1448
+ if (value.classification.underlying !== undefined) {
1449
+ if (value.classification.kind !== "capture-incomplete" || !isClassificationKind(value.classification.underlying)
1450
+ || value.classification.underlying === "capture-incomplete") {
1451
+ throw new Error("Classification underlying outcome is invalid.");
1452
+ }
1453
+ }
1454
+ else if (value.classification.underlyingSource !== undefined) {
1455
+ throw new Error("Classification underlying source requires a wrapped outcome.");
1456
+ }
1457
+ if (value.classification.kind === "capture-incomplete" && value.classification.source !== "capture") {
1458
+ throw new Error("Capture-incomplete classifications require capture attribution.");
1459
+ }
1460
+ assertTerminalRecord(value.classification.terminal);
1461
+ if (!sameTerminalRecord(value.terminal, value.classification.terminal)) {
1462
+ throw new Error("Attempt terminal and classification records disagree.");
1463
+ }
1464
+ assertClassificationTerminal(value.classification.kind, value.classification.terminal, value.classification.underlying);
1465
+ const classificationKind = value.classification.kind === "capture-incomplete"
1466
+ ? value.classification.underlying
1467
+ : value.classification.kind;
1468
+ const classificationSource = value.classification.kind === "capture-incomplete"
1469
+ ? value.classification.underlyingSource
1470
+ : value.classification.source;
1471
+ if (classificationKind === "policy-stop") {
1472
+ if (classificationSource !== "harness") {
1473
+ throw new Error("Policy-stop terminal records require harness attribution.");
1474
+ }
1475
+ if (!visitedStates.has("running")) {
1476
+ throw new Error("Policy-stop terminal records require the running lifecycle phase.");
1477
+ }
1478
+ if (!isRecord(value.workspace) || value.workspace.status !== "ready") {
1479
+ throw new Error("Policy-stop terminal records require a ready workspace result.");
1480
+ }
1481
+ assertShutdownCompleted(value.workspace, "Workspace");
1482
+ if (!isRecord(value.harness) || value.harness.status !== "stopped" || value.harness.stopReason !== "policy") {
1483
+ throw new Error("Policy-stop terminal records require a matching stopped harness result.");
1484
+ }
1485
+ }
1486
+ if (classificationKind === "budget-stop") {
1487
+ if (classificationSource !== "runner" && classificationSource !== "harness") {
1488
+ throw new Error("Budget-stop terminal records require runner or harness attribution.");
1489
+ }
1490
+ if (classificationSource === "runner") {
1491
+ // Coordinator budgets do not require a harness result when the harness
1492
+ // was never started.
1493
+ }
1494
+ else {
1495
+ if (!visitedStates.has("running")) {
1496
+ throw new Error("Harness budget-stop terminal records require the running lifecycle phase.");
1497
+ }
1498
+ if (!isRecord(value.workspace) || value.workspace.status !== "ready") {
1499
+ throw new Error("Harness budget-stop terminal records require a ready workspace result.");
1500
+ }
1501
+ assertShutdownCompleted(value.workspace, "Workspace");
1502
+ if (!isRecord(value.harness)
1503
+ || !((value.harness.status === "stopped" && value.harness.stopReason === "budget")
1504
+ || value.harness.status === "interrupted")) {
1505
+ throw new Error("Harness budget-stop terminal records require compatible harness evidence.");
1506
+ }
1507
+ }
1508
+ }
1509
+ if (classificationKind === "interrupted" && classificationSource === "harness") {
1510
+ if (!visitedStates.has("running")) {
1511
+ throw new Error("Harness interruption terminal records require the running lifecycle phase.");
1512
+ }
1513
+ if (!isRecord(value.workspace) || value.workspace.status !== "ready") {
1514
+ throw new Error("Harness interruption terminal records require a ready workspace result.");
1515
+ }
1516
+ assertShutdownCompleted(value.workspace, "Workspace");
1517
+ if (!isRecord(value.harness) || value.harness.status !== "interrupted") {
1518
+ throw new Error("Harness interruption terminal records require an interrupted harness result.");
1519
+ }
1520
+ }
1521
+ if (classificationKind === "interrupted" && classificationSource !== "runner" && classificationSource !== "harness") {
1522
+ throw new Error("Interrupted terminal records require runner or harness attribution.");
1523
+ }
1524
+ if (classificationKind === "infrastructure-failure"
1525
+ && classificationSource !== "runner" && classificationSource !== "workspace"
1526
+ && classificationSource !== "harness" && classificationSource !== "cleanup") {
1527
+ throw new Error("Infrastructure-failure terminal records have an invalid attribution.");
1528
+ }
1529
+ if (classificationKind === "infrastructure-failure" && classificationSource === "harness") {
1530
+ if (!visitedStates.has("running")) {
1531
+ throw new Error("Harness infrastructure-failure records require the running lifecycle phase.");
1532
+ }
1533
+ if (!isRecord(value.workspace) || value.workspace.status !== "ready") {
1534
+ throw new Error("Harness infrastructure-failure records require a ready workspace result.");
1535
+ }
1536
+ assertShutdownCompleted(value.workspace, "Workspace");
1537
+ if (!isRecord(value.harness)
1538
+ || !(value.harness.status === "failed"
1539
+ || value.harness.status === "stopped" && value.harness.stopReason === undefined)) {
1540
+ throw new Error("Harness infrastructure-failure records require a failed or unreasoned stopped harness result.");
1541
+ }
1542
+ }
1543
+ if (classificationKind === "infrastructure-failure" && classificationSource === "workspace") {
1544
+ if (!visitedStates.has("setup")) {
1545
+ throw new Error("Workspace infrastructure-failure records require the setup lifecycle phase.");
1546
+ }
1547
+ if (!isRecord(value.workspace) || (value.workspace.status !== "ready" && value.workspace.status !== "failed")) {
1548
+ throw new Error("Workspace infrastructure-failure records require a native workspace result.");
1549
+ }
1550
+ if (value.workspace.status === "ready" && !workspaceShowsFailure(value.workspace)) {
1551
+ throw new Error("Workspace infrastructure-failure records require workspace failure evidence.");
1552
+ }
1553
+ }
1554
+ if (classificationKind === "infrastructure-failure" && classificationSource === "cleanup") {
1555
+ if (!isRecord(value.cleanup) || (value.cleanup.status !== "failed" && value.cleanup.status !== "timed-out")) {
1556
+ throw new Error("Cleanup infrastructure-failure records require failed or timed-out cleanup evidence.");
1557
+ }
1558
+ }
1559
+ if (classificationKind === "task-failure" && classificationSource !== "verifier") {
1560
+ throw new Error("Task-failure terminal records require verifier attribution.");
1561
+ }
1562
+ if (classificationKind === "completed" && classificationSource !== "runner") {
1563
+ throw new Error("Completed terminal records require runner attribution.");
1564
+ }
1565
+ if (classificationKind === "verifier-error") {
1566
+ if (classificationSource !== "verifier") {
1567
+ throw new Error("Verifier-error terminal records require verifier attribution.");
1568
+ }
1569
+ if (!visitedStates.has("running") || !visitedStates.has("verifying")) {
1570
+ throw new Error("Verifier-error terminal records require running and verifying lifecycle phases.");
1571
+ }
1572
+ if (!isRecord(value.harness) || value.harness.status !== "completed") {
1573
+ throw new Error("Verifier-error terminal records require a completed harness result.");
1574
+ }
1575
+ if (!isRecord(value.workspace) || value.workspace.status !== "ready") {
1576
+ throw new Error("Verifier-error terminal records require a ready workspace result.");
1577
+ }
1578
+ assertShutdownCompleted(value.workspace, "Workspace");
1579
+ if (!isRecord(value.verifier) || (value.verifier.status !== "error" && value.verifier.status !== "not-run")) {
1580
+ throw new Error("Verifier-error terminal records require an error or not-run verifier result.");
1581
+ }
1582
+ assertShutdownCompleted(value.harness, "Harness");
1583
+ if (value.harnessTerminationConfirmed === false) {
1584
+ throw new Error("Verifier-error terminal records require confirmed harness termination.");
1585
+ }
1586
+ }
1587
+ const expectedPartial = classificationUnderlyingKind(value.classification) !== "completed"
1588
+ || isRecord(value.capture) && value.capture.status === "incomplete";
1589
+ if (value.partial !== expectedPartial)
1590
+ throw new Error("Attempt partial state contradicts its terminal outcome.");
1591
+ }
1592
+ const retainedArtifactId = retainedRecordWorkspaceArtifactId(value.workspace);
1593
+ if (retainedArtifactId !== undefined && value.terminal !== undefined
1594
+ && value.terminal.workspaceArtifactId !== retainedArtifactId) {
1595
+ throw new Error("Retained workspace evidence must be linked from the terminal outcome.");
1596
+ }
1597
+ if (value.terminal?.workspaceArtifactId !== undefined) {
1598
+ assertTerminalWorkspaceEvidence(value.workspace, value.terminal.workspaceArtifactId, value.terminal.state === "failed" && value.terminal.failureClass === "infrastructure"
1599
+ || value.terminal.state === "stopped"
1600
+ || value.terminal.state === "interrupted");
1601
+ }
1602
+ if (value.terminal?.state === "completed") {
1603
+ if (!visitedStates.has("running") || value.assessmentMode === "verified" && !visitedStates.has("verifying")) {
1604
+ throw new Error("Completed terminal records require their assessment lifecycle phases.");
1605
+ }
1606
+ assertHarnessStatus(value.harness, "completed");
1607
+ assertCleanupStatus(value.cleanup, "completed");
1608
+ if (value.harnessTerminationConfirmed === false
1609
+ || value.assessmentMode === "verified" && value.verifierTerminationConfirmed === false) {
1610
+ throw new Error("Completed terminal records require confirmed execution termination.");
1611
+ }
1612
+ assertShutdownCompleted(value.workspace, "Workspace");
1613
+ assertShutdownCompleted(value.harness, "Harness");
1614
+ if (value.assessmentMode === "verified")
1615
+ assertShutdownCompleted(value.verifier, "Verifier");
1616
+ if (isRecord(value.persistence) && value.persistence.status !== "complete") {
1617
+ throw new Error("Completed terminal records cannot have incomplete persistence.");
1618
+ }
1619
+ assertTerminalWorkspaceEvidence(value.workspace, value.terminal.workspaceArtifactId);
1620
+ if (value.assessmentMode === "verified")
1621
+ assertVerifierStatus(value.verifier, "passed");
1622
+ }
1623
+ if (value.terminal?.state === "failed" && value.terminal.failureClass === "task") {
1624
+ if (!visitedStates.has("running") || !visitedStates.has("verifying")) {
1625
+ throw new Error("Task-failure terminal records require running and verifying lifecycle phases.");
1626
+ }
1627
+ assertHarnessStatus(value.harness, "completed");
1628
+ assertShutdownCompleted(value.workspace, "Workspace");
1629
+ assertShutdownCompleted(value.harness, "Harness");
1630
+ assertShutdownCompleted(value.verifier, "Verifier");
1631
+ if (value.harnessTerminationConfirmed === false || value.verifierTerminationConfirmed === false) {
1632
+ throw new Error("Task-failure terminal records require confirmed execution termination.");
1633
+ }
1634
+ assertTerminalWorkspaceEvidence(value.workspace, value.terminal.workspaceArtifactId);
1635
+ assertVerifierStatus(value.verifier, "failed");
1636
+ }
1637
+ if (isRecord(value.capture) && value.capture.status === "incomplete" && value.partial !== true) {
1638
+ throw new Error("Incomplete capture requires partial attempt state.");
1639
+ }
1640
+ for (const field of ["capture", "persistence"]) {
1641
+ const status = value[field];
1642
+ if (status !== undefined) {
1643
+ if (!isRecord(status) || !["complete", "incomplete"].includes(String(status.status))) {
1644
+ throw new Error(`Attempt ${field} status is invalid.`);
1645
+ }
1646
+ if (status.error !== undefined)
1647
+ assertTimestampValue(status.error, `Attempt ${field} error`);
1648
+ }
1649
+ }
1650
+ if (value.cleanup !== undefined) {
1651
+ if (!isRecord(value.cleanup) || !["completed", "failed", "timed-out"].includes(String(value.cleanup.status))) {
1652
+ throw new Error("Attempt cleanup status is invalid.");
1653
+ }
1654
+ if (value.cleanup.error !== undefined)
1655
+ assertTimestampValue(value.cleanup.error, "Attempt cleanup error");
1656
+ }
1657
+ if (lifecycle.state === "terminal" && (!isRecord(value.cleanup) || !["completed", "failed", "timed-out"].includes(String(value.cleanup.status)))) {
1658
+ throw new Error("Terminal attempt records require explicit cleanup status.");
1659
+ }
1660
+ if (lifecycle.state === "terminal" && isRecord(value.cleanup) && value.cleanup.status === "completed") {
1661
+ if (value.harnessTerminationConfirmed === false || value.verifierTerminationConfirmed === false) {
1662
+ throw new Error("Completed cleanup requires confirmed execution termination.");
1663
+ }
1664
+ assertShutdownCompleted(value.workspace, "Workspace");
1665
+ assertShutdownCompleted(value.harness, "Harness");
1666
+ assertShutdownCompleted(value.verifier, "Verifier");
1667
+ }
1668
+ if (lifecycle.state === "terminal"
1669
+ && (!isRecord(value.capture) || !["complete", "incomplete"].includes(String(value.capture.status)))) {
1670
+ throw new Error("Terminal attempt records require explicit capture status.");
1671
+ }
1672
+ if (value.classification?.kind === "capture-incomplete"
1673
+ && (!isRecord(value.capture) || value.capture.status !== "incomplete" || value.partial !== true)) {
1674
+ throw new Error("Capture-incomplete classifications require incomplete capture evidence and partial state.");
1675
+ }
1676
+ if (lifecycle.state === "terminal" && isRecord(value.capture) && value.capture.status === "incomplete"
1677
+ && value.classification?.kind !== "capture-incomplete") {
1678
+ throw new Error("Incomplete terminal capture requires a capture-incomplete classification.");
1679
+ }
1680
+ }
1681
+ function isRecord(value) {
1682
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1683
+ }
1684
+ function assertJsonValue(value, path, seen) {
1685
+ if (value === null || typeof value === "string" || typeof value === "boolean")
1686
+ return;
1687
+ if (typeof value === "number") {
1688
+ if (!Number.isFinite(value))
1689
+ throw new Error(`${path} must be a finite JSON number.`);
1690
+ return;
1691
+ }
1692
+ if (typeof value !== "object")
1693
+ throw new Error(`${path} contains a non-JSON value.`);
1694
+ if (seen.has(value))
1695
+ throw new Error(`${path} contains a cycle.`);
1696
+ seen.add(value);
1697
+ if (Array.isArray(value)) {
1698
+ value.forEach((item, index) => assertJsonValue(item, `${path}[${index}]`, seen));
1699
+ }
1700
+ else {
1701
+ const prototype = Object.getPrototypeOf(value);
1702
+ if (prototype !== Object.prototype && prototype !== null)
1703
+ throw new Error(`${path} must be a JSON object or array.`);
1704
+ const object = value;
1705
+ for (const key of Object.keys(object))
1706
+ assertJsonValue(object[key], `${path}.${key}`, seen);
1707
+ }
1708
+ seen.delete(value);
1709
+ }
1710
+ function assertIdentityValue(value, label) {
1711
+ if (typeof value !== "string")
1712
+ throw new Error(`${label} is invalid.`);
1713
+ assertIdentity(value, label);
1714
+ }
1715
+ function assertTimestampValue(value, label) {
1716
+ if (typeof value !== "string" || value.trim() === "")
1717
+ throw new Error(`${label} is invalid.`);
1718
+ }
1719
+ function isLifecycleState(value) {
1720
+ return typeof value === "string" && Object.hasOwn(ALLOWED_TRANSITIONS, value);
1721
+ }
1722
+ function isClassificationKind(value) {
1723
+ return ["completed", "task-failure", "infrastructure-failure", "verifier-error", "budget-stop", "policy-stop", "interrupted", "capture-incomplete"].includes(String(value));
1724
+ }
1725
+ function assertTerminalRecord(value) {
1726
+ if (!isRecord(value) || !["completed", "failed", "stopped", "interrupted"].includes(String(value.state))) {
1727
+ throw new Error("Attempt terminal record is invalid.");
1728
+ }
1729
+ if (!["none", "infrastructure", "task"].includes(String(value.failureClass))) {
1730
+ throw new Error("Attempt terminal failure class is invalid.");
1731
+ }
1732
+ if (!["none", "budget", "policy"].includes(String(value.stopReason))) {
1733
+ throw new Error("Attempt terminal stop reason is invalid.");
1734
+ }
1735
+ if (value.workspaceArtifactId !== undefined)
1736
+ assertIdentityValue(value.workspaceArtifactId, "Workspace artifact ID");
1737
+ if (value.state === "completed" && (value.failureClass !== "none" || value.stopReason !== "none" || value.workspaceArtifactId === undefined)) {
1738
+ throw new Error("Completed terminal record is incomplete.");
1739
+ }
1740
+ if (value.state === "failed" && (value.failureClass !== "infrastructure" && value.failureClass !== "task" || value.stopReason !== "none" || value.failureClass === "task" && value.workspaceArtifactId === undefined)) {
1741
+ throw new Error("Failed terminal record is invalid.");
1742
+ }
1743
+ if (value.state === "stopped" && (value.failureClass !== "none" || (value.stopReason !== "budget" && value.stopReason !== "policy"))) {
1744
+ throw new Error("Stopped terminal record is invalid.");
1745
+ }
1746
+ if (value.state === "interrupted" && (value.failureClass !== "infrastructure" || value.stopReason !== "none")) {
1747
+ throw new Error("Interrupted terminal record is invalid.");
1748
+ }
1749
+ }
1750
+ function sameTerminalRecord(left, right) {
1751
+ if (!isRecord(left) || !isRecord(right))
1752
+ return false;
1753
+ return left.state === right.state
1754
+ && left.failureClass === right.failureClass
1755
+ && left.stopReason === right.stopReason
1756
+ && left.workspaceArtifactId === right.workspaceArtifactId;
1757
+ }
1758
+ function assertTerminalWorkspaceEvidence(workspace, artifactId, allowFailed = false) {
1759
+ if (!isRecord(workspace) || (workspace.status !== "ready" && !(allowFailed && workspace.status === "failed" && workspace.retained === true)) || workspace.retained === false
1760
+ || typeof workspace.artifactId !== "string" || artifactId === undefined || workspace.artifactId !== artifactId) {
1761
+ throw new Error("Terminal outcome requires matching retained workspace evidence.");
1762
+ }
1763
+ }
1764
+ function retainedRecordWorkspaceArtifactId(workspace) {
1765
+ if (!isRecord(workspace) || workspace.retained === false || typeof workspace.artifactId !== "string")
1766
+ return undefined;
1767
+ return workspace.status === "ready" || workspace.retained === true ? workspace.artifactId : undefined;
1768
+ }
1769
+ function workspaceShowsFailure(workspace) {
1770
+ return workspace.error !== undefined
1771
+ || workspace.artifactId === undefined
1772
+ || workspace.retained === false
1773
+ || isRecord(workspace.shutdownResult) && workspace.shutdownResult.status !== "completed";
1774
+ }
1775
+ function assertWorkspaceResult(value) {
1776
+ if (!isRecord(value) || value.status !== "ready" && value.status !== "failed") {
1777
+ throw new Error("Attempt workspace evidence is invalid.");
1778
+ }
1779
+ for (const field of ["path", "artifactId", "digest", "error"]) {
1780
+ if (value[field] !== undefined)
1781
+ assertTimestampValue(value[field], `Workspace ${field}`);
1782
+ }
1783
+ if (value.retained !== undefined && typeof value.retained !== "boolean")
1784
+ throw new Error("Workspace retention is invalid.");
1785
+ if (value.shutdownResult !== undefined)
1786
+ assertShutdownResult(value.shutdownResult, "Workspace shutdown");
1787
+ }
1788
+ function assertHarnessResult(value) {
1789
+ if (!isRecord(value) || !["completed", "failed", "stopped", "interrupted"].includes(String(value.status))) {
1790
+ throw new Error("Attempt harness evidence is invalid.");
1791
+ }
1792
+ if (value.status === "completed" && (value.failureClass !== undefined || value.stopReason !== undefined)) {
1793
+ throw new Error("Completed harness evidence cannot include failure or stop fields.");
1794
+ }
1795
+ if (value.status === "failed" && value.stopReason !== undefined) {
1796
+ throw new Error("Failed harness evidence cannot include a stop field.");
1797
+ }
1798
+ if (value.status === "stopped" && value.failureClass !== undefined) {
1799
+ throw new Error("Stopped harness evidence cannot include a failure field.");
1800
+ }
1801
+ if (value.status === "interrupted" && (value.failureClass !== undefined || value.stopReason !== undefined)) {
1802
+ throw new Error("Interrupted harness evidence cannot include failure or stop fields.");
1803
+ }
1804
+ if (value.failureClass !== undefined && !["infrastructure", "task"].includes(String(value.failureClass))) {
1805
+ throw new Error("Harness failure class is invalid.");
1806
+ }
1807
+ if (value.stopReason !== undefined && !["budget", "policy"].includes(String(value.stopReason))) {
1808
+ throw new Error("Harness stop reason is invalid.");
1809
+ }
1810
+ for (const field of ["reason", "error", "captureError"]) {
1811
+ if (value[field] !== undefined)
1812
+ assertTimestampValue(value[field], `Harness ${field}`);
1813
+ }
1814
+ if (value.shutdownResult !== undefined)
1815
+ assertShutdownResult(value.shutdownResult, "Harness shutdown");
1816
+ }
1817
+ function assertVerifierResult(value) {
1818
+ if (!isRecord(value) || !["passed", "failed", "error", "not-run"].includes(String(value.status))) {
1819
+ throw new Error("Attempt verifier evidence is invalid.");
1820
+ }
1821
+ if (value.status === "passed" && value.error !== undefined) {
1822
+ throw new Error("Passed verifier evidence cannot include an error.");
1823
+ }
1824
+ if (value.error !== undefined)
1825
+ assertTimestampValue(value.error, "Verifier error");
1826
+ if (value.shutdownResult !== undefined)
1827
+ assertShutdownResult(value.shutdownResult, "Verifier shutdown");
1828
+ }
1829
+ function assertShutdownResult(value, label) {
1830
+ if (!isRecord(value) || !["completed", "failed", "timed-out"].includes(String(value.status))) {
1831
+ throw new Error(`${label} result is invalid.`);
1832
+ }
1833
+ if (value.error !== undefined)
1834
+ assertTimestampValue(value.error, `${label} error`);
1835
+ }
1836
+ function assertTerminationConfirmation(value, confirmation, label) {
1837
+ if (!isRecord(value) || typeof confirmation !== "boolean" || !isRecord(value.shutdownResult))
1838
+ return;
1839
+ const shutdownStatus = value.shutdownResult.status;
1840
+ if (confirmation && shutdownStatus !== "completed") {
1841
+ throw new Error(`${label} termination confirmation contradicts its shutdown result.`);
1842
+ }
1843
+ if (!confirmation && shutdownStatus === "completed") {
1844
+ throw new Error(`${label} termination confirmation contradicts its shutdown result.`);
1845
+ }
1846
+ }
1847
+ function assertVerifierStatus(verifier, status) {
1848
+ if (!isRecord(verifier) || verifier.status !== status) {
1849
+ throw new Error(`Terminal outcome requires a ${status} verifier result.`);
1850
+ }
1851
+ }
1852
+ function assertHarnessStatus(harness, status) {
1853
+ if (!isRecord(harness) || harness.status !== status) {
1854
+ throw new Error(`Terminal outcome requires a ${status} harness result.`);
1855
+ }
1856
+ }
1857
+ function assertCleanupStatus(cleanup, status) {
1858
+ if (!isRecord(cleanup) || cleanup.status !== status) {
1859
+ throw new Error(`Terminal outcome requires ${status} cleanup.`);
1860
+ }
1861
+ }
1862
+ function assertShutdownCompleted(value, label) {
1863
+ if (isRecord(value) && isRecord(value.shutdownResult) && value.shutdownResult.status !== "completed") {
1864
+ throw new Error(`Completed terminal records require confirmed ${label.toLowerCase()} shutdown.`);
1865
+ }
1866
+ }
1867
+ function assertClassificationTerminal(kind, terminal, underlying) {
1868
+ const baseKind = kind === "capture-incomplete" ? underlying : kind;
1869
+ if (!isClassificationKind(baseKind) || baseKind === "capture-incomplete") {
1870
+ throw new Error("Capture-incomplete classification must name a concrete underlying outcome.");
1871
+ }
1872
+ const expected = baseKind === "completed"
1873
+ ? { state: "completed", failureClass: "none", stopReason: "none" }
1874
+ : baseKind === "task-failure"
1875
+ ? { state: "failed", failureClass: "task", stopReason: "none" }
1876
+ : baseKind === "infrastructure-failure" || baseKind === "verifier-error"
1877
+ ? { state: "failed", failureClass: "infrastructure", stopReason: "none" }
1878
+ : baseKind === "budget-stop"
1879
+ ? { state: "stopped", failureClass: "none", stopReason: "budget" }
1880
+ : baseKind === "policy-stop"
1881
+ ? { state: "stopped", failureClass: "none", stopReason: "policy" }
1882
+ : { state: "interrupted", failureClass: "infrastructure", stopReason: "none" };
1883
+ if (terminal.state !== expected.state || terminal.failureClass !== expected.failureClass || terminal.stopReason !== expected.stopReason) {
1884
+ throw new Error("Attempt classification kind does not match its terminal outcome.");
1885
+ }
1886
+ }
1887
+ function errorMessage(error) {
1888
+ return error instanceof Error ? error.message : String(error);
1889
+ }