@tangle-network/agent-runtime 0.91.0 → 0.92.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 (86) hide show
  1. package/README.md +4 -2
  2. package/dist/agent.d.ts +3 -3
  3. package/dist/agent.js +106 -25
  4. package/dist/agent.js.map +1 -1
  5. package/dist/{mcp-serve-verifier-XsX8rkB9.d.ts → agentic-generator-B8oeE2Yv.d.ts} +6 -33
  6. package/dist/analyst-loop.d.ts +1 -1
  7. package/dist/candidate-execution/index.d.ts +104 -0
  8. package/dist/candidate-execution/index.js +34 -0
  9. package/dist/candidate-execution/index.js.map +1 -0
  10. package/dist/chunk-3D2RHC4K.js +73 -0
  11. package/dist/chunk-3D2RHC4K.js.map +1 -0
  12. package/dist/chunk-3MDZX7YU.js +125 -0
  13. package/dist/chunk-3MDZX7YU.js.map +1 -0
  14. package/dist/chunk-4FPXIMSI.js +659 -0
  15. package/dist/chunk-4FPXIMSI.js.map +1 -0
  16. package/dist/chunk-6O73TRHW.js +142 -0
  17. package/dist/chunk-6O73TRHW.js.map +1 -0
  18. package/dist/chunk-A62TP7SK.js +4784 -0
  19. package/dist/chunk-A62TP7SK.js.map +1 -0
  20. package/dist/{chunk-AD7JW4QG.js → chunk-BVVRQ4YC.js} +23 -1231
  21. package/dist/chunk-BVVRQ4YC.js.map +1 -0
  22. package/dist/chunk-FDJ7AHXG.js +1229 -0
  23. package/dist/chunk-FDJ7AHXG.js.map +1 -0
  24. package/dist/{chunk-FF77IBQM.js → chunk-FRBHUNQ7.js} +2 -141
  25. package/dist/chunk-FRBHUNQ7.js.map +1 -0
  26. package/dist/chunk-J2K6WIG6.js +2172 -0
  27. package/dist/chunk-J2K6WIG6.js.map +1 -0
  28. package/dist/{chunk-JRS3YSRZ.js → chunk-LQQPGRKT.js} +2 -2
  29. package/dist/{chunk-IOUUITQA.js → chunk-P6B3Z7PR.js} +3 -3
  30. package/dist/chunk-PH65PR4F.js +860 -0
  31. package/dist/chunk-PH65PR4F.js.map +1 -0
  32. package/dist/{chunk-7ON74BQO.js → chunk-R5GWDTM3.js} +2 -2
  33. package/dist/{chunk-DWWII6N2.js → chunk-TKJ3Q6CQ.js} +2 -2
  34. package/dist/{chunk-NC66AM3S.js → chunk-UO2L5VTP.js} +32 -1413
  35. package/dist/chunk-UO2L5VTP.js.map +1 -0
  36. package/dist/{chunk-BZF3KQ6G.js → chunk-VSWBYWFK.js} +4 -122
  37. package/dist/chunk-VSWBYWFK.js.map +1 -0
  38. package/dist/{completion-gate-DkAnUmpb.d.ts → completion-gate-BLaiN0-X.d.ts} +1 -1
  39. package/dist/{coordination-rRj5hjJK.d.ts → coordination-DxJ83oZA.d.ts} +12 -5
  40. package/dist/environment-provider.d.ts +2 -2
  41. package/dist/environment-provider.js +2 -1
  42. package/dist/improve-CUVCq7xg.d.ts +152 -0
  43. package/dist/{improvement-adapter-CDR8QNVM.d.ts → improvement-adapter-BieWeK5J.d.ts} +16 -0
  44. package/dist/index.d.ts +27 -1092
  45. package/dist/index.js +108 -6491
  46. package/dist/index.js.map +1 -1
  47. package/dist/intelligence.d.ts +160 -13
  48. package/dist/intelligence.js +534 -59
  49. package/dist/intelligence.js.map +1 -1
  50. package/dist/knowledge.d.ts +6 -6
  51. package/dist/knowledge.js +6 -4
  52. package/dist/lifecycle.d.ts +2 -1
  53. package/dist/lifecycle.js +5 -3
  54. package/dist/lifecycle.js.map +1 -1
  55. package/dist/{loop-runner-bin-DTbZVGfM.d.ts → loop-runner-bin-kKUNGLyV.d.ts} +2 -2
  56. package/dist/loop-runner-bin.d.ts +5 -5
  57. package/dist/loop-runner-bin.js +8 -5
  58. package/dist/loops.d.ts +16 -16
  59. package/dist/loops.js +47 -41
  60. package/dist/mcp/bin.js +6 -4
  61. package/dist/mcp/bin.js.map +1 -1
  62. package/dist/mcp/index.d.ts +8 -8
  63. package/dist/mcp/index.js +9 -6
  64. package/dist/mcp/index.js.map +1 -1
  65. package/dist/mcp-serve-verifier-Bg4C3p5S.d.ts +34 -0
  66. package/dist/{openai-tools-C4ZfUD4L.d.ts → openai-tools-E3woykz9.d.ts} +1 -1
  67. package/dist/prepare-Z08a4heC.d.ts +713 -0
  68. package/dist/profiles.d.ts +1 -1
  69. package/dist/{router-client-DJImUDlm.d.ts → sanitize-C9go6tXj.d.ts} +113 -1
  70. package/dist/{structural-rollout-MwlpgQ-6.d.ts → structural-rollout-DHGDbhvR.d.ts} +3 -3
  71. package/dist/{supervise-DPmYPk0j.d.ts → supervise-T2pazU3G.d.ts} +4 -4
  72. package/dist/{types-SyuwunY_.d.ts → types-B00NtbCs.d.ts} +1 -1
  73. package/dist/{types-eMNgWgFi.d.ts → types-DAdIm4AC.d.ts} +1 -1
  74. package/dist/{worktree-fanout-BDFQIO-Y.d.ts → worktree-fanout-BUb2Ag02.d.ts} +3 -3
  75. package/package.json +11 -6
  76. package/skills/build-with-agent-runtime/SKILL.md +1 -1
  77. package/dist/chunk-AD7JW4QG.js.map +0 -1
  78. package/dist/chunk-BZF3KQ6G.js.map +0 -1
  79. package/dist/chunk-FF77IBQM.js.map +0 -1
  80. package/dist/chunk-IVGYLCFH.js +0 -381
  81. package/dist/chunk-IVGYLCFH.js.map +0 -1
  82. package/dist/chunk-NC66AM3S.js.map +0 -1
  83. /package/dist/{chunk-JRS3YSRZ.js.map → chunk-LQQPGRKT.js.map} +0 -0
  84. /package/dist/{chunk-IOUUITQA.js.map → chunk-P6B3Z7PR.js.map} +0 -0
  85. /package/dist/{chunk-7ON74BQO.js.map → chunk-R5GWDTM3.js.map} +0 -0
  86. /package/dist/{chunk-DWWII6N2.js.map → chunk-TKJ3Q6CQ.js.map} +0 -0
@@ -0,0 +1,860 @@
1
+ import {
2
+ CLAIM_FORMAT_VERSION,
3
+ PENDING_FORMAT_VERSION,
4
+ PHASE_FORMAT_VERSION,
5
+ RecoveryAgentCandidateTraceStore,
6
+ TERMINAL_FORMAT_VERSION,
7
+ assertExactObjectKeys,
8
+ assertPreparedCandidateIntegrity,
9
+ assertRecoveryMatchesStaged,
10
+ assertTerminalAllowedInPhase,
11
+ assertTerminalMatchesClaim,
12
+ assertTerminalMatchesStaged,
13
+ beginPreparedCandidateDisposal,
14
+ candidateClaimFileInternals,
15
+ candidateCleanupDeadline,
16
+ candidateCleanupTimeout,
17
+ canonicalCandidateBytes,
18
+ canonicalCandidateDigest,
19
+ consumePreparedCandidateExecution,
20
+ immutableCandidateValue,
21
+ persistCandidateModelSettlementEvidence,
22
+ recoveredTerminalRecord,
23
+ rejectedFinish,
24
+ rejectedStage,
25
+ requireStagedTerminal,
26
+ sealAgentCandidateExecutorFinalCapture,
27
+ sealAgentCandidateModelSettlement,
28
+ sealTerminalDigest,
29
+ terminalRecord,
30
+ usdToNanos,
31
+ withinCandidateCleanupDeadline
32
+ } from "./chunk-A62TP7SK.js";
33
+
34
+ // src/candidate-execution/claim-file-store.ts
35
+ import { randomUUID } from "crypto";
36
+ import { linkSync } from "fs";
37
+ import { mkdir, open, unlink } from "fs/promises";
38
+ import { join } from "path";
39
+ var {
40
+ assertExpiredLease,
41
+ assertLease,
42
+ assertSameSlot,
43
+ assertUnexpiredLease,
44
+ attemptRecord,
45
+ claimSlot,
46
+ leaseDigest,
47
+ newLease,
48
+ readClaim,
49
+ readClaimIfPresent,
50
+ readTerminal,
51
+ readTerminalIfPresent,
52
+ readTransitionIfPresent,
53
+ rejectedExistingClaim,
54
+ rejectedRetry,
55
+ retryRejection,
56
+ sealAttemptRef,
57
+ sealClaim,
58
+ sealLease
59
+ } = candidateClaimFileInternals;
60
+ var FileAgentCandidateExecutionClaimStore = class {
61
+ directory;
62
+ now;
63
+ constructor(options) {
64
+ if (options.directory.length === 0) {
65
+ throw new Error("candidate execution claim directory must not be empty");
66
+ }
67
+ this.directory = options.directory;
68
+ this.now = options.now ?? Date.now;
69
+ }
70
+ async tryClaim(requested) {
71
+ const claim = sealClaim(requested);
72
+ await mkdir(this.directory, { recursive: true });
73
+ const claimPath = this.claimPath(claim);
74
+ const existing = await readClaimIfPresent(claimPath);
75
+ if (existing) {
76
+ assertSameSlot(existing.claim, claim, claimPath);
77
+ return rejectedExistingClaim(existing.claim, claim);
78
+ }
79
+ assertUnexpiredLease(claim.leaseExpiresAtMs, this.now());
80
+ const retryFailure = await this.retryFailure(claim);
81
+ if (retryFailure) return rejectedRetry(claim, retryFailure);
82
+ const lease = newLease(claim);
83
+ const acquired = await writeRecordIfAbsent(this.directory, claimPath, {
84
+ version: CLAIM_FORMAT_VERSION,
85
+ ...claim,
86
+ phase: "claimed",
87
+ leaseDigest: leaseDigest(lease)
88
+ });
89
+ if (acquired) return Object.freeze({ acquired: true, claim, lease });
90
+ const winner = await readClaim(claimPath);
91
+ assertSameSlot(winner.claim, claim, claimPath);
92
+ return rejectedExistingClaim(winner.claim, claim);
93
+ }
94
+ async getAttempt(requestedAttempt) {
95
+ return await this.storedAttempt(sealAttemptRef(requestedAttempt));
96
+ }
97
+ async markCandidateMayRun(requestedLease) {
98
+ const lease = sealLease(requestedLease);
99
+ const claimPath = this.claimPath(lease);
100
+ const stored = await readClaim(claimPath);
101
+ assertSameSlot(stored.claim, lease, claimPath);
102
+ assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease);
103
+ const state = await this.transitionState(stored.claim);
104
+ if (state.phase === "candidate-may-run") {
105
+ return Object.freeze({ marked: false, phase: "candidate-may-run" });
106
+ }
107
+ if (state.staged) {
108
+ throw new Error("candidate execution terminal was staged before candidate-may-run phase");
109
+ }
110
+ assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now());
111
+ const marked = await writeRecordIfAbsent(
112
+ this.directory,
113
+ this.transitionPath(stored.claim, 1),
114
+ {
115
+ version: PHASE_FORMAT_VERSION,
116
+ kind: "candidate-execution-phase",
117
+ executionId: stored.claim.executionId,
118
+ attempt: stored.claim.attempt,
119
+ executionPlanDigest: stored.claim.executionPlanDigest,
120
+ phase: "candidate-may-run"
121
+ },
122
+ this.ownerPublication(stored.claim)
123
+ );
124
+ if (marked) return Object.freeze({ marked: true, phase: "candidate-may-run" });
125
+ const winner = await this.transitionState(stored.claim);
126
+ if (winner.phase === "candidate-may-run") {
127
+ return Object.freeze({ marked: false, phase: "candidate-may-run" });
128
+ }
129
+ throw new Error("candidate execution terminal was staged before candidate-may-run phase");
130
+ }
131
+ async stageTerminal(requestedLease, result) {
132
+ const lease = sealLease(requestedLease);
133
+ const claimPath = this.claimPath(lease);
134
+ const stored = await readClaim(claimPath);
135
+ assertSameSlot(stored.claim, lease, claimPath);
136
+ assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease);
137
+ const terminal = terminalRecord(stored.claim, result);
138
+ const state = await this.transitionState(stored.claim);
139
+ if (state.staged) return rejectedStage(state.staged, terminal);
140
+ assertTerminalAllowedInPhase(state.phase, terminal);
141
+ assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now());
142
+ const transition = state.phase === "claimed" ? 1 : 2;
143
+ const staged = await writeRecordIfAbsent(
144
+ this.directory,
145
+ this.transitionPath(stored.claim, transition),
146
+ {
147
+ version: PENDING_FORMAT_VERSION,
148
+ kind: "candidate-execution-pending-terminal",
149
+ terminal
150
+ },
151
+ this.ownerPublication(stored.claim)
152
+ );
153
+ if (staged) return Object.freeze({ staged: true, terminal });
154
+ const winner = await this.transitionState(stored.claim);
155
+ if (!winner.staged) {
156
+ throw new Error("candidate execution phase transition won while staging terminal");
157
+ }
158
+ return rejectedStage(winner.staged, terminal);
159
+ }
160
+ async finish(requestedLease, requestedTerminalDigest) {
161
+ const lease = sealLease(requestedLease);
162
+ const terminalDigest = sealTerminalDigest(requestedTerminalDigest);
163
+ const claimPath = this.claimPath(lease);
164
+ const stored = await readClaim(claimPath);
165
+ assertSameSlot(stored.claim, lease, claimPath);
166
+ assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease);
167
+ const state = await this.transitionState(stored.claim);
168
+ const staged = requireStagedTerminal(state.staged, terminalDigest);
169
+ const terminalPath = this.terminalPath(lease);
170
+ const existing = await readTerminalIfPresent(terminalPath);
171
+ if (existing) {
172
+ assertTerminalMatchesClaim(existing, stored.claim, terminalPath);
173
+ assertTerminalMatchesStaged(existing, staged, terminalPath);
174
+ return rejectedFinish(existing, terminalDigest);
175
+ }
176
+ assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now());
177
+ const finished = await writeRecordIfAbsent(
178
+ this.directory,
179
+ terminalPath,
180
+ {
181
+ version: TERMINAL_FORMAT_VERSION,
182
+ terminal: staged
183
+ },
184
+ this.ownerPublication(stored.claim)
185
+ );
186
+ if (finished) return Object.freeze({ finished: true, terminal: staged });
187
+ const winner = await readTerminal(terminalPath);
188
+ assertSameSlot(winner, lease, terminalPath);
189
+ assertTerminalMatchesClaim(winner, stored.claim, terminalPath);
190
+ assertTerminalMatchesStaged(winner, staged, terminalPath);
191
+ return rejectedFinish(winner, terminalDigest);
192
+ }
193
+ async recoverExpired(requestedAttempt, evidence) {
194
+ const attempt = sealAttemptRef(requestedAttempt);
195
+ const record2 = await this.storedAttempt(attempt);
196
+ if (!record2) throw new Error("candidate execution recovery does not name an acquired attempt");
197
+ const recovered = recoveredTerminalRecord(record2.claim, record2.phase, evidence);
198
+ if (record2.staged) assertRecoveryMatchesStaged(record2.staged, recovered);
199
+ const requestedDigest = record2.staged?.terminalDigest ?? recovered.terminalDigest;
200
+ if (record2.terminal) return rejectedFinish(record2.terminal, requestedDigest);
201
+ assertExpiredLease(record2.claim.leaseExpiresAtMs, this.now());
202
+ let staged = record2.staged;
203
+ if (!staged) {
204
+ const transition = record2.phase === "claimed" ? 1 : 2;
205
+ const didStage = await writeRecordIfAbsent(
206
+ this.directory,
207
+ this.transitionPath(record2.claim, transition),
208
+ {
209
+ version: PENDING_FORMAT_VERSION,
210
+ kind: "candidate-execution-pending-terminal",
211
+ terminal: recovered
212
+ }
213
+ );
214
+ if (didStage) {
215
+ staged = recovered;
216
+ } else {
217
+ const winner2 = await this.transitionState(record2.claim);
218
+ if (!winner2.staged) {
219
+ throw new Error(
220
+ "candidate execution recovery lost terminal staging to a phase transition"
221
+ );
222
+ }
223
+ assertRecoveryMatchesStaged(winner2.staged, recovered);
224
+ staged = winner2.staged;
225
+ }
226
+ }
227
+ const terminalPath = this.terminalPath(attempt);
228
+ const didFinish = await writeRecordIfAbsent(this.directory, terminalPath, {
229
+ version: TERMINAL_FORMAT_VERSION,
230
+ terminal: staged
231
+ });
232
+ if (didFinish) return Object.freeze({ finished: true, terminal: staged });
233
+ const winner = await readTerminal(terminalPath);
234
+ assertSameSlot(winner, attempt, terminalPath);
235
+ assertTerminalMatchesClaim(winner, record2.claim, terminalPath);
236
+ assertTerminalMatchesStaged(winner, staged, terminalPath);
237
+ return rejectedFinish(winner, staged.terminalDigest);
238
+ }
239
+ async storedAttempt(claim) {
240
+ const claimPath = this.claimPath(claim);
241
+ const stored = await readClaimIfPresent(claimPath);
242
+ if (!stored) return void 0;
243
+ assertSameSlot(stored.claim, claim, claimPath);
244
+ const state = await this.transitionState(stored.claim);
245
+ const terminalPath = this.terminalPath(claim);
246
+ const terminal = await readTerminalIfPresent(terminalPath);
247
+ if (terminal) {
248
+ assertTerminalMatchesClaim(terminal, stored.claim, terminalPath);
249
+ if (!state.staged) {
250
+ throw new Error(
251
+ `candidate execution terminal record at ${terminalPath} has no staged outbox`
252
+ );
253
+ }
254
+ assertTerminalMatchesStaged(terminal, state.staged, terminalPath);
255
+ }
256
+ return attemptRecord(stored.claim, state.phase, state.staged, terminal);
257
+ }
258
+ async transitionState(claim) {
259
+ const firstPath = this.transitionPath(claim, 1);
260
+ const first = await readTransitionIfPresent(firstPath, claim);
261
+ if (!first) return { phase: "claimed" };
262
+ if (first.kind === "pending") return { phase: "claimed", staged: first.terminal };
263
+ const secondPath = this.transitionPath(claim, 2);
264
+ const second = await readTransitionIfPresent(secondPath, claim);
265
+ if (!second) return { phase: "candidate-may-run" };
266
+ if (second.kind !== "pending") {
267
+ throw new Error(`candidate execution transition at ${secondPath} repeats the phase marker`);
268
+ }
269
+ return { phase: "candidate-may-run", staged: second.terminal };
270
+ }
271
+ async retryFailure(claim) {
272
+ if (claim.attempt === 1) return void 0;
273
+ return retryRejection(
274
+ claim,
275
+ await this.storedAttempt({
276
+ executionId: claim.executionId,
277
+ attempt: claim.attempt - 1
278
+ })
279
+ );
280
+ }
281
+ ownerPublication(claim) {
282
+ return {
283
+ authorizePublish: () => {
284
+ assertUnexpiredLease(claim.leaseExpiresAtMs, this.now());
285
+ return true;
286
+ }
287
+ };
288
+ }
289
+ claimPath(claim) {
290
+ return join(this.directory, `${claimSlot(claim)}.claim.json`);
291
+ }
292
+ terminalPath(claim) {
293
+ return join(this.directory, `${claimSlot(claim)}.terminal.json`);
294
+ }
295
+ transitionPath(claim, ordinal) {
296
+ return join(this.directory, `${claimSlot(claim)}.transition-${ordinal}.json`);
297
+ }
298
+ };
299
+ async function writeRecordIfAbsent(directory, destination, record2, options = {}) {
300
+ const temporaryPath = join(directory, `.candidate-execution-${process.pid}-${randomUUID()}.tmp`);
301
+ const handle = await open(temporaryPath, "wx", 384);
302
+ try {
303
+ await handle.writeFile(
304
+ Buffer.concat([Buffer.from(canonicalCandidateBytes(record2)), Buffer.from("\n")])
305
+ );
306
+ await handle.sync();
307
+ } finally {
308
+ await handle.close();
309
+ }
310
+ let written = false;
311
+ try {
312
+ if (options.authorizePublish && options.authorizePublish() !== true) {
313
+ throw new Error("candidate execution record publication was not authorized");
314
+ }
315
+ linkSync(temporaryPath, destination);
316
+ written = true;
317
+ await syncDirectory(directory);
318
+ } catch (error) {
319
+ if (!isNodeError(error, "EEXIST")) throw error;
320
+ } finally {
321
+ await unlink(temporaryPath).catch((error) => {
322
+ if (!isNodeError(error, "ENOENT")) throw error;
323
+ });
324
+ }
325
+ return written;
326
+ }
327
+ async function syncDirectory(directory) {
328
+ const handle = await open(directory, "r");
329
+ try {
330
+ await handle.sync();
331
+ } finally {
332
+ await handle.close();
333
+ }
334
+ }
335
+ function isNodeError(error, code) {
336
+ return error !== null && typeof error === "object" && "code" in error && error.code === code;
337
+ }
338
+
339
+ // src/candidate-execution/dispose.ts
340
+ async function disposePreparedAgentCandidateExecution(prepared, options = {}) {
341
+ const initialState = assertPreparedCandidateIntegrity(prepared);
342
+ const cleanupTimeoutMs = options.cleanupTimeoutMs ?? initialState.cleanupTimeoutMs;
343
+ if (cleanupTimeoutMs > initialState.cleanupTimeoutMs) {
344
+ throw new Error("disposal cleanup timeout exceeds the frozen preparation bound");
345
+ }
346
+ const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs);
347
+ const state = beginPreparedCandidateDisposal(prepared);
348
+ const cleanup = [];
349
+ if (state.memory.mode === "isolated") {
350
+ const reservation = state.memoryReservation;
351
+ if (!reservation) throw new Error("isolated memory reservation is missing");
352
+ cleanup.push(
353
+ withinCandidateCleanupDeadline(
354
+ async () => {
355
+ const closed = await state.ports.memory.close({
356
+ executionId: state.executionId,
357
+ preparationId: reservation.preparationId,
358
+ accessDigest: reservation.accessDigest,
359
+ effectiveNamespace: reservation.effectiveNamespace,
360
+ reason: "abandoned"
361
+ });
362
+ if (closed.closed !== true || Object.keys(closed).some((key) => key !== "closed")) {
363
+ throw new Error("abandoned isolated memory access did not acknowledge closure");
364
+ }
365
+ },
366
+ cleanupDeadlineAtMs,
367
+ "isolated memory disposal"
368
+ )
369
+ );
370
+ }
371
+ cleanup.push(
372
+ withinCandidateCleanupDeadline(
373
+ async () => {
374
+ const settlement = sealAgentCandidateModelSettlement(
375
+ await state.ports.models.settleGrant({
376
+ executionId: state.executionId,
377
+ preparationId: state.preparationId,
378
+ grantDigest: state.modelReservation.digest,
379
+ resolved: state.resolvedModel,
380
+ reason: "abandoned"
381
+ }),
382
+ {
383
+ preparationId: state.preparationId,
384
+ grantDigest: state.modelReservation.digest,
385
+ model: state.resolvedModel.model
386
+ }
387
+ );
388
+ if (settlement.usage.modelCalls !== 0) {
389
+ throw new Error("unexecuted model reservation unexpectedly contains calls");
390
+ }
391
+ },
392
+ cleanupDeadlineAtMs,
393
+ "model reservation disposal"
394
+ )
395
+ );
396
+ const results = await Promise.allSettled(cleanup);
397
+ const failures = results.filter((result) => result.status === "rejected").map((result) => result.reason);
398
+ if (failures.length > 0) {
399
+ consumePreparedCandidateExecution(prepared, "disposal-failed");
400
+ throw new AggregateError(failures, "candidate preparation disposal failed");
401
+ }
402
+ consumePreparedCandidateExecution(prepared, "disposed");
403
+ return Object.freeze({ disposed: true });
404
+ }
405
+
406
+ // src/candidate-execution/protected-model-port.ts
407
+ var MAX_RECENT_SETTLEMENTS = 4096;
408
+ var RECOVERED_SETTLEMENT_RETENTION_MS = 15 * 60 * 1e3;
409
+ function createProtectedAgentCandidateModelPort(options) {
410
+ const gatewayDomain = assertGatewayDomain(options.gatewayDomain);
411
+ const activationEnvNames = exactEnvironmentNames(options.activationEnvNames);
412
+ const reservations = /* @__PURE__ */ new Map();
413
+ const recentSettlements = /* @__PURE__ */ new Map();
414
+ return {
415
+ resolve: async (input) => {
416
+ const request = immutableCandidateValue(input);
417
+ const resolved = validateResolvedModel(await options.resolveModel(request), request);
418
+ return immutableCandidateValue(resolved);
419
+ },
420
+ reserveGrant: async (input) => {
421
+ pruneExpiredState(reservations, recentSettlements, Date.now());
422
+ const request = immutableCandidateValue(input);
423
+ validateReserveInput(request);
424
+ const requestDigest = canonicalCandidateDigest(request);
425
+ const key = reservationKey(request.executionId, request.preparationId);
426
+ if (recentSettlements.has(key)) {
427
+ throw new Error("protected model reservation is already settled");
428
+ }
429
+ const previous = reservations.get(key);
430
+ if (previous && previous.requestDigest !== requestDigest) {
431
+ throw new Error("protected model reservation retry changed immutable input");
432
+ }
433
+ const reservation = validateReservation(
434
+ await options.client.reserve(request),
435
+ request,
436
+ gatewayDomain
437
+ );
438
+ const responseDigest = canonicalCandidateDigest(reservation);
439
+ if (recentSettlements.has(key)) {
440
+ throw new Error("protected model reservation completed after the grant was settled");
441
+ }
442
+ const recorded = previous ?? reservations.get(key);
443
+ if (recorded && recorded.requestDigest !== requestDigest) {
444
+ throw new Error("protected model reservation retry changed immutable input");
445
+ }
446
+ if (recorded && recorded.responseDigest !== responseDigest) {
447
+ throw new Error("protected model reservation retry returned different evidence");
448
+ }
449
+ if (!recorded) {
450
+ reservations.set(key, {
451
+ requestDigest,
452
+ responseDigest,
453
+ executionId: request.executionId,
454
+ preparationId: request.preparationId,
455
+ grantDigest: reservation.digest,
456
+ expiresAtMs: request.expiresAtMs,
457
+ resolved: immutableCandidateValue(request.resolved),
458
+ limits: immutableCandidateValue(request.limits),
459
+ maxCostUsdNanos: usdToNanos(request.limits.maxCostUsd, "reserved maxCostUsd"),
460
+ activation: "reserved"
461
+ });
462
+ }
463
+ return reservation;
464
+ },
465
+ activateGrant: async (input) => {
466
+ pruneExpiredState(reservations, recentSettlements, Date.now());
467
+ const request = immutableCandidateValue(input);
468
+ const key = reservationKey(request.executionId, request.preparationId);
469
+ if (recentSettlements.has(key)) throw new Error("protected model grant is already settled");
470
+ const state = reservations.get(key);
471
+ if (!state) throw new Error("protected model grant was not reserved by this port");
472
+ assertGrantIdentity(state, request);
473
+ if (state.settlementDigest) throw new Error("protected model grant is already settled");
474
+ if (state.activation !== "reserved") {
475
+ throw new Error("protected model grant activation is single-use");
476
+ }
477
+ if (!Number.isSafeInteger(request.deadlineAtMs) || request.deadlineAtMs <= 0) {
478
+ throw new Error("protected model activation deadline must be a positive safe integer");
479
+ }
480
+ if (request.deadlineAtMs <= Date.now()) {
481
+ throw new Error("protected model activation deadline must be in the future");
482
+ }
483
+ if (request.deadlineAtMs > state.expiresAtMs) {
484
+ throw new Error("protected model activation deadline exceeds the reserved expiry");
485
+ }
486
+ state.activation = "activating";
487
+ try {
488
+ const activation = validateActivation(
489
+ await options.client.activate(request),
490
+ state.limits.maxModelCalls === 0 ? [] : activationEnvNames
491
+ );
492
+ if (recentSettlements.has(key) || reservations.get(key) !== state) {
493
+ throw new Error("protected model grant settled while activation was in flight");
494
+ }
495
+ state.activation = "activated";
496
+ return activation;
497
+ } catch (error) {
498
+ state.activation = "reserved";
499
+ throw error;
500
+ }
501
+ },
502
+ settleGrant: async (input) => {
503
+ const now = Date.now();
504
+ pruneExpiredState(reservations, recentSettlements, now);
505
+ const request = immutableCandidateValue(input);
506
+ const key = reservationKey(request.executionId, request.preparationId);
507
+ const state = reservations.get(key);
508
+ if (state) assertGrantIdentity(state, request);
509
+ const remembered = state ?? recentSettlements.get(key);
510
+ if (remembered?.settlementReason && remembered.settlementReason !== request.reason) {
511
+ throw new Error("protected model settlement retry changed the termination reason");
512
+ }
513
+ const sealed = sealAgentCandidateModelSettlement(await options.client.settle(request), {
514
+ preparationId: request.preparationId,
515
+ grantDigest: request.grantDigest,
516
+ model: request.resolved.model
517
+ });
518
+ if (state) assertWithinReservedLimits(sealed.fixedUsage, state);
519
+ const settlementDigest = canonicalCandidateDigest(sealed.value);
520
+ if (remembered?.settlementDigest && remembered.settlementDigest !== settlementDigest) {
521
+ throw new Error("protected model settlement retry returned a different final ledger");
522
+ }
523
+ if (remembered?.settlementReason && remembered.settlementReason !== request.reason) {
524
+ throw new Error("protected model settlement retry changed the termination reason");
525
+ }
526
+ const expiresAtMs = state?.expiresAtMs ?? now + RECOVERED_SETTLEMENT_RETENTION_MS;
527
+ if (state) {
528
+ state.settlementDigest = settlementDigest;
529
+ state.settlementReason = request.reason;
530
+ }
531
+ reservations.delete(key);
532
+ rememberSettlement(recentSettlements, key, {
533
+ settlementDigest,
534
+ settlementReason: request.reason,
535
+ expiresAtMs
536
+ });
537
+ return sealed.value;
538
+ }
539
+ };
540
+ }
541
+ function validateReserveInput(input) {
542
+ if (!Number.isSafeInteger(input.expiresAtMs) || input.expiresAtMs <= 0) {
543
+ throw new Error("protected model reservation expiry must be a positive safe integer");
544
+ }
545
+ if (input.expiresAtMs <= Date.now()) {
546
+ throw new Error("protected model reservation expiry must be in the future");
547
+ }
548
+ for (const [name, value] of [
549
+ ["maxModelCalls", input.limits.maxModelCalls],
550
+ ["maxInputTokens", input.limits.maxInputTokens],
551
+ ["maxOutputTokens", input.limits.maxOutputTokens]
552
+ ]) {
553
+ if (!Number.isSafeInteger(value) || value < 0) {
554
+ throw new Error(`protected model reservation ${name} must be a nonnegative safe integer`);
555
+ }
556
+ }
557
+ usdToNanos(input.limits.maxCostUsd, "reserved maxCostUsd");
558
+ }
559
+ function validateReservation(value, expected, gatewayDomain) {
560
+ const source = exactRecord(
561
+ value,
562
+ ["preparationId", "digest", "expiresAtMs", "enforcedLimits", "network"],
563
+ "protected model reservation response"
564
+ );
565
+ if (source.preparationId !== expected.preparationId) {
566
+ throw new Error("protected model reservation response changed preparation identity");
567
+ }
568
+ if (!isSha256Digest(source.digest)) {
569
+ throw new Error("protected model reservation response has an invalid digest");
570
+ }
571
+ if (source.expiresAtMs !== expected.expiresAtMs) {
572
+ throw new Error("protected model reservation response changed expiry");
573
+ }
574
+ exactRecord(
575
+ source.enforcedLimits,
576
+ ["maxModelCalls", "maxInputTokens", "maxOutputTokens", "maxCostUsd"],
577
+ "protected model reservation enforced limits"
578
+ );
579
+ if (canonicalCandidateDigest(source.enforcedLimits) !== canonicalCandidateDigest(expected.limits)) {
580
+ throw new Error("protected model reservation response changed enforced limits");
581
+ }
582
+ const network = validateReservationNetwork(
583
+ source.network,
584
+ expected.limits.maxModelCalls,
585
+ gatewayDomain
586
+ );
587
+ return immutableCandidateValue({
588
+ preparationId: source.preparationId,
589
+ digest: source.digest,
590
+ expiresAtMs: source.expiresAtMs,
591
+ enforcedLimits: source.enforcedLimits,
592
+ network
593
+ });
594
+ }
595
+ function validateReservationNetwork(value, maxModelCalls, gatewayDomain) {
596
+ if (maxModelCalls === 0) {
597
+ const source2 = exactRecord(value, ["mode"], "protected model reservation network");
598
+ if (source2.mode !== "disabled") {
599
+ throw new Error("zero-call protected model reservation must disable gateway access");
600
+ }
601
+ return { mode: "disabled" };
602
+ }
603
+ const source = exactRecord(value, ["mode", "domains"], "protected model reservation network");
604
+ if (source.mode !== "gateway-only") {
605
+ throw new Error("protected model reservation must allow only its model gateway");
606
+ }
607
+ if (!Array.isArray(source.domains) || source.domains.length !== 1 || source.domains[0] !== gatewayDomain) {
608
+ throw new Error("protected model reservation returned an unexpected gateway domain");
609
+ }
610
+ return { mode: "gateway-only", domains: [gatewayDomain] };
611
+ }
612
+ function validateActivation(value, expectedNames) {
613
+ const source = exactRecord(value, ["env"], "protected model activation response");
614
+ const env = record(source.env, "protected model activation environment");
615
+ const actualNames = Object.keys(env).sort();
616
+ if (actualNames.length !== expectedNames.length || actualNames.some((name, index) => name !== expectedNames[index])) {
617
+ throw new Error("protected model activation returned unexpected environment names");
618
+ }
619
+ const detached = /* @__PURE__ */ Object.create(null);
620
+ for (const name of expectedNames) {
621
+ const value2 = env[name];
622
+ if (typeof value2 !== "string" || value2.length < 8 || value2.length > 65536) {
623
+ throw new Error(`protected model activation ${name} must be a non-empty protected value`);
624
+ }
625
+ detached[name] = value2;
626
+ }
627
+ return Object.freeze({ env: Object.freeze(detached) });
628
+ }
629
+ function validateResolvedModel(value, expected) {
630
+ const source = exactRecord(
631
+ value,
632
+ ["requested", "provider", "model", "snapshot", "reasoningEffort"],
633
+ "resolved candidate model"
634
+ );
635
+ if (source.requested !== expected.requested) {
636
+ throw new Error("model resolver changed the evaluator-owned request");
637
+ }
638
+ if (source.reasoningEffort !== expected.reasoningEffort) {
639
+ throw new Error("model resolver changed the evaluator-owned reasoning effort");
640
+ }
641
+ for (const name of ["requested", "provider", "model", "snapshot"]) {
642
+ if (typeof source[name] !== "string" || source[name].length === 0) {
643
+ throw new Error(`resolved candidate model ${name} must be non-empty`);
644
+ }
645
+ }
646
+ return source;
647
+ }
648
+ function assertGrantIdentity(expected, actual) {
649
+ if (actual.executionId !== expected.executionId || actual.preparationId !== expected.preparationId || actual.grantDigest !== expected.grantDigest || canonicalCandidateDigest(actual.resolved) !== canonicalCandidateDigest(expected.resolved)) {
650
+ throw new Error("protected model grant input does not match its immutable reservation");
651
+ }
652
+ }
653
+ function assertWithinReservedLimits(usage, reservation) {
654
+ for (const [name, actual, limit] of [
655
+ ["model calls", usage.modelCalls, reservation.limits.maxModelCalls],
656
+ ["input tokens", usage.inputTokens, reservation.limits.maxInputTokens],
657
+ ["output tokens", usage.outputTokens, reservation.limits.maxOutputTokens],
658
+ ["cost USD nanos", usage.costUsdNanos, reservation.maxCostUsdNanos]
659
+ ]) {
660
+ if (actual > limit) {
661
+ throw new Error(`protected model settlement ${name} ${actual} exceeds reserved ${limit}`);
662
+ }
663
+ }
664
+ }
665
+ function exactEnvironmentNames(values) {
666
+ if (values.length === 0) {
667
+ throw new Error("protected model activation environment names must not be empty");
668
+ }
669
+ const names = [...values].sort();
670
+ if (new Set(names).size !== names.length) {
671
+ throw new Error("protected model activation environment names must be unique");
672
+ }
673
+ for (const name of names) {
674
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || ["__proto__", "constructor", "prototype"].includes(name)) {
675
+ throw new Error(`protected model activation environment name is invalid: ${name}`);
676
+ }
677
+ }
678
+ return Object.freeze(names);
679
+ }
680
+ function assertGatewayDomain(value) {
681
+ if (value.length > 253 || value !== value.toLowerCase() || value.endsWith(".") || value.includes(":") || value === "localhost" || value.endsWith(".localhost") || value === "metadata.google" || value === "metadata.google.internal") {
682
+ throw new Error("protected model gateway must be an exact lowercase public DNS name");
683
+ }
684
+ const labels = value.split(".");
685
+ if (labels.length < 2 || !labels.every(
686
+ (label) => label.length >= 1 && label.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(label)
687
+ ) || !/[a-z]/.test(labels.at(-1) ?? "")) {
688
+ throw new Error("protected model gateway must be an exact lowercase public DNS name");
689
+ }
690
+ return value;
691
+ }
692
+ function reservationKey(executionId, preparationId) {
693
+ return canonicalCandidateDigest({ executionId, preparationId });
694
+ }
695
+ function pruneExpiredState(reservations, settlements, now) {
696
+ for (const [key, value] of reservations) {
697
+ if (value.expiresAtMs <= now) reservations.delete(key);
698
+ }
699
+ for (const [key, value] of settlements) {
700
+ if (value.expiresAtMs <= now) settlements.delete(key);
701
+ }
702
+ }
703
+ function rememberSettlement(settlements, key, value) {
704
+ settlements.delete(key);
705
+ settlements.set(key, value);
706
+ while (settlements.size > MAX_RECENT_SETTLEMENTS) {
707
+ const oldest = settlements.keys().next().value;
708
+ if (oldest === void 0) return;
709
+ settlements.delete(oldest);
710
+ }
711
+ }
712
+ function isSha256Digest(value) {
713
+ return typeof value === "string" && /^sha256:[a-f0-9]{64}$/.test(value);
714
+ }
715
+ function exactRecord(value, keys, label) {
716
+ const source = record(value, label);
717
+ assertExactObjectKeys(source, keys, label);
718
+ return source;
719
+ }
720
+ function record(value, label) {
721
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
722
+ throw new Error(`${label} must be an object`);
723
+ }
724
+ const prototype = Object.getPrototypeOf(value);
725
+ if (prototype !== Object.prototype && prototype !== null) {
726
+ throw new Error(`${label} must be a plain object`);
727
+ }
728
+ return value;
729
+ }
730
+
731
+ // src/candidate-execution/recover.ts
732
+ async function recoverExpiredAgentCandidateExecution(options) {
733
+ const record2 = await options.claimStore.getAttempt(options.attempt);
734
+ if (!record2) throw new Error("candidate execution recovery attempt is missing");
735
+ if (record2.terminal) {
736
+ return Object.freeze({ finished: false, terminal: record2.terminal, exactReplay: true });
737
+ }
738
+ const cleanupTimeoutMs = candidateCleanupTimeout(
739
+ options.cleanupTimeoutMs ?? record2.claim.cleanup.cleanupTimeoutMs
740
+ );
741
+ if (cleanupTimeoutMs > record2.claim.cleanup.cleanupTimeoutMs) {
742
+ throw new Error("recovery cleanup timeout exceeds the frozen preparation bound");
743
+ }
744
+ const now = options.now ?? Date.now;
745
+ if (now() < record2.claim.leaseExpiresAtMs) {
746
+ throw new Error("candidate execution lease has not expired");
747
+ }
748
+ const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs);
749
+ const controller = new AbortController();
750
+ controller.abort(new Error("recovering an expired candidate execution"));
751
+ const recoveryTraceStore = new RecoveryAgentCandidateTraceStore(options.traceStore);
752
+ const processClosure = withinCandidateCleanupDeadline(
753
+ async () => {
754
+ const stopped = await options.executor.stopAndCapture(
755
+ {
756
+ executionId: record2.claim.executionId,
757
+ executionPlanDigest: record2.claim.executionPlanDigest
758
+ },
759
+ {
760
+ traceStore: recoveryTraceStore,
761
+ reason: "failed",
762
+ signal: controller.signal,
763
+ deadlineAtMs: record2.claim.leaseExpiresAtMs
764
+ }
765
+ );
766
+ sealAgentCandidateExecutorFinalCapture(stopped);
767
+ return { stopped: true };
768
+ },
769
+ cleanupDeadlineAtMs,
770
+ "expired candidate process termination"
771
+ );
772
+ const modelClosure = withinCandidateCleanupDeadline(
773
+ async () => sealAgentCandidateModelSettlement(
774
+ await options.ports.models.settleGrant({
775
+ executionId: record2.claim.executionId,
776
+ preparationId: record2.claim.cleanup.preparationId,
777
+ grantDigest: record2.claim.cleanup.modelGrantDigest,
778
+ resolved: record2.claim.cleanup.resolvedModel,
779
+ reason: "failed"
780
+ }),
781
+ {
782
+ preparationId: record2.claim.cleanup.preparationId,
783
+ grantDigest: record2.claim.cleanup.modelGrantDigest,
784
+ model: record2.claim.cleanup.resolvedModel.model
785
+ }
786
+ ),
787
+ cleanupDeadlineAtMs,
788
+ "expired candidate model settlement"
789
+ );
790
+ const memoryClosure = record2.claim.cleanup.memory ? withinCandidateCleanupDeadline(
791
+ async () => {
792
+ const memory2 = record2.claim.cleanup.memory;
793
+ if (!memory2) throw new Error("expired candidate memory handle is missing");
794
+ const closed = await options.ports.memory.close({
795
+ executionId: record2.claim.executionId,
796
+ preparationId: record2.claim.cleanup.preparationId,
797
+ accessDigest: memory2.accessDigest,
798
+ effectiveNamespace: memory2.effectiveNamespace,
799
+ reason: "failed"
800
+ });
801
+ if (closed.closed !== true || Object.keys(closed).some((key) => key !== "closed")) {
802
+ throw new Error("expired candidate memory did not acknowledge closure");
803
+ }
804
+ return { closed: true };
805
+ },
806
+ cleanupDeadlineAtMs,
807
+ "expired candidate memory closure"
808
+ ) : void 0;
809
+ const operations = [processClosure, modelClosure, ...memoryClosure ? [memoryClosure] : []];
810
+ const outcomes = await Promise.allSettled(operations);
811
+ const failures = outcomes.filter((outcome) => outcome.status === "rejected").map((outcome) => outcome.reason);
812
+ if (failures.length > 0) {
813
+ throw new AggregateError(failures, "expired candidate cleanup could not be proven");
814
+ }
815
+ const model = await modelClosure;
816
+ const modelSettlement = await withinCandidateCleanupDeadline(
817
+ () => persistCandidateModelSettlementEvidence(
818
+ {
819
+ executionId: record2.claim.executionId,
820
+ executionPlanDigest: record2.claim.executionPlanDigest,
821
+ resolvedModel: record2.claim.cleanup.resolvedModel
822
+ },
823
+ model,
824
+ options.outputArtifacts
825
+ ),
826
+ cleanupDeadlineAtMs,
827
+ "expired candidate model-settlement persistence"
828
+ );
829
+ const memory = record2.claim.cleanup.memory;
830
+ return await options.claimStore.recoverExpired(options.attempt, {
831
+ failureClass: record2.phase === "claimed" && model.fixedUsage.modelCalls === 0 ? "pre-model-infrastructure" : "unknown",
832
+ usage: model.fixedUsage,
833
+ modelSettlement: modelSettlement.artifact,
834
+ process: {
835
+ stopped: true,
836
+ executionPlanDigest: record2.claim.executionPlanDigest
837
+ },
838
+ model: {
839
+ closed: true,
840
+ preparationId: record2.claim.cleanup.preparationId,
841
+ grantDigest: record2.claim.cleanup.modelGrantDigest
842
+ },
843
+ ...memory ? {
844
+ memory: {
845
+ closed: true,
846
+ preparationId: record2.claim.cleanup.preparationId,
847
+ accessDigest: memory.accessDigest,
848
+ effectiveNamespace: memory.effectiveNamespace
849
+ }
850
+ } : {}
851
+ });
852
+ }
853
+
854
+ export {
855
+ FileAgentCandidateExecutionClaimStore,
856
+ disposePreparedAgentCandidateExecution,
857
+ createProtectedAgentCandidateModelPort,
858
+ recoverExpiredAgentCandidateExecution
859
+ };
860
+ //# sourceMappingURL=chunk-PH65PR4F.js.map