@tangle-network/agent-runtime 0.197.1 → 0.198.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 (101) hide show
  1. package/README.md +1 -2
  2. package/dist/{activation-BxMZybuo.js → activation-CwdO0ml2.js} +2 -2
  3. package/dist/{activation-BxMZybuo.js.map → activation-CwdO0ml2.js.map} +1 -1
  4. package/dist/{improve-DZs0KXhK.d.ts → activation-DFRTurvU.d.ts} +99 -5
  5. package/dist/agent.d.ts +1 -1
  6. package/dist/agent.js +2 -2
  7. package/dist/candidate-execution/index.d.ts +370 -2
  8. package/dist/candidate-execution/index.js +828 -4
  9. package/dist/candidate-execution/index.js.map +1 -0
  10. package/dist/{coordination-driver-zkCrfEbS.js → coordination-driver-Cf638-6q.js} +2 -5
  11. package/dist/{coordination-driver-zkCrfEbS.js.map → coordination-driver-Cf638-6q.js.map} +1 -1
  12. package/dist/{delegate-DHeUYU5E.js → delegate-Bez07Dlh.js} +2 -2
  13. package/dist/{delegate-DHeUYU5E.js.map → delegate-Bez07Dlh.js.map} +1 -1
  14. package/dist/durable.d.ts +2 -2
  15. package/dist/durable.js +4 -5
  16. package/dist/durable.js.map +1 -1
  17. package/dist/{graph-51SJOEcw.js → graph-DBk3Unto.js} +3 -3
  18. package/dist/{graph-51SJOEcw.js.map → graph-DBk3Unto.js.map} +1 -1
  19. package/dist/{improvement-cycle-Tw5nYT5R.js → improvement-cycle-Bma8TJs-.js} +8 -10
  20. package/dist/{improvement-cycle-Tw5nYT5R.js.map → improvement-cycle-Bma8TJs-.js.map} +1 -1
  21. package/dist/{index-CYOJsxSg.d.ts → index-D8LWK3Ha.d.ts} +6 -7
  22. package/dist/index.d.ts +694 -16
  23. package/dist/index.js +1780 -17
  24. package/dist/index.js.map +1 -1
  25. package/dist/intelligence.d.ts +3 -7
  26. package/dist/intelligence.js +5 -6
  27. package/dist/intelligence.js.map +1 -1
  28. package/dist/kernel.d.ts +2 -5
  29. package/dist/kernel.js +8 -10
  30. package/dist/{loop-runner-bin-2LQSjRTB.js → loop-runner-bin-Bd3dHQuw.js} +3 -3
  31. package/dist/{loop-runner-bin-2LQSjRTB.js.map → loop-runner-bin-Bd3dHQuw.js.map} +1 -1
  32. package/dist/{loop-runner-bin-DwrSB04m.d.ts → loop-runner-bin-Bn-0o_4k.d.ts} +3 -3
  33. package/dist/loop-runner-bin.d.ts +1 -1
  34. package/dist/loop-runner-bin.js +1 -1
  35. package/dist/mcp/bin.js +3 -3
  36. package/dist/mcp/index.d.ts +2 -3
  37. package/dist/mcp/index.js +4 -5
  38. package/dist/mcp/index.js.map +1 -1
  39. package/dist/{prepare-DDGp0-rW.js → prepare-CAO1yXov.js} +2407 -2407
  40. package/dist/prepare-CAO1yXov.js.map +1 -0
  41. package/dist/{protected-model-port-DxFN8DLS.js → protected-model-port-B5avcRiQ.js} +2 -2
  42. package/dist/{protected-model-port-DxFN8DLS.js.map → protected-model-port-B5avcRiQ.js.map} +1 -1
  43. package/dist/{provision-supervisor-CpMMShE_.js → provision-supervisor-C97q5NN_.js} +3 -6
  44. package/dist/{provision-supervisor-CpMMShE_.js.map → provision-supervisor-C97q5NN_.js.map} +1 -1
  45. package/dist/{redact-Cbl2O-4N.js → redact-CVyxp2AU.js} +5232 -2525
  46. package/dist/redact-CVyxp2AU.js.map +1 -0
  47. package/dist/{runtime-BSFz2z7h.js → runtime-BrTBBSfC.js} +427 -23
  48. package/dist/runtime-BrTBBSfC.js.map +1 -0
  49. package/dist/{server-COXa19sp.js → server-CO4bvAwe.js} +4 -4
  50. package/dist/{server-COXa19sp.js.map → server-CO4bvAwe.js.map} +1 -1
  51. package/dist/{types-DFLZMaeh.d.ts → stream-agent-turn-CLOQr497.d.ts} +1986 -6
  52. package/dist/{structural-rollout-DPbZWgEm.js → structural-rollout-D__lmWXe.js} +1101 -8
  53. package/dist/structural-rollout-D__lmWXe.js.map +1 -0
  54. package/dist/{supervise-b93YUaka.js → supervise-KSe4JEcg.js} +3 -5
  55. package/dist/{supervise-b93YUaka.js.map → supervise-KSe4JEcg.js.map} +1 -1
  56. package/dist/testing.d.ts +2 -2
  57. package/dist/testing.js +13 -13
  58. package/dist/tui/index.d.ts +1 -1
  59. package/dist/tui/index.js +1 -1
  60. package/dist/{workspace-archive-Ybomp7AN.js → workspace-archive-BMOnloFf.js} +3 -3
  61. package/dist/{workspace-archive-Ybomp7AN.js.map → workspace-archive-BMOnloFf.js.map} +1 -1
  62. package/package.json +1 -28
  63. package/dist/activation-DyWB0K6E.d.ts +0 -98
  64. package/dist/authored-code-URmkdgjv.js +0 -37
  65. package/dist/authored-code-URmkdgjv.js.map +0 -1
  66. package/dist/candidate-execution-nvqVIMyS.js +0 -829
  67. package/dist/candidate-execution-nvqVIMyS.js.map +0 -1
  68. package/dist/conversation-BxJ0SIBM.js +0 -1363
  69. package/dist/conversation-BxJ0SIBM.js.map +0 -1
  70. package/dist/conversation.d.ts +0 -2
  71. package/dist/conversation.js +0 -2
  72. package/dist/environment-provider-1fKZh2zl.js +0 -2281
  73. package/dist/environment-provider-1fKZh2zl.js.map +0 -1
  74. package/dist/environment-provider-B-I2jlQy.d.ts +0 -143
  75. package/dist/environment-provider.d.ts +0 -2
  76. package/dist/environment-provider.js +0 -2
  77. package/dist/graph.d.ts +0 -753
  78. package/dist/graph.js +0 -2111
  79. package/dist/graph.js.map +0 -1
  80. package/dist/index-CUosKU4N.d.ts +0 -372
  81. package/dist/index-D9mb6fn2.d.ts +0 -691
  82. package/dist/index-ZnxSe6iK.d.ts +0 -138
  83. package/dist/jsonl-file-BEpaEYjT.js +0 -141
  84. package/dist/jsonl-file-BEpaEYjT.js.map +0 -1
  85. package/dist/knowledge-B_MsOtDG.js +0 -428
  86. package/dist/knowledge-B_MsOtDG.js.map +0 -1
  87. package/dist/knowledge.d.ts +0 -2
  88. package/dist/knowledge.js +0 -2
  89. package/dist/materialization-Cy0oM8tb.js +0 -672
  90. package/dist/materialization-Cy0oM8tb.js.map +0 -1
  91. package/dist/prepare-DDGp0-rW.js.map +0 -1
  92. package/dist/primeintellect/index.d.ts +0 -218
  93. package/dist/primeintellect/index.js +0 -739
  94. package/dist/primeintellect/index.js.map +0 -1
  95. package/dist/redact-Cbl2O-4N.js.map +0 -1
  96. package/dist/runtime-0xNaV6TJ.d.ts +0 -1699
  97. package/dist/runtime-BSFz2z7h.js.map +0 -1
  98. package/dist/stream-agent-turn-Dt5mZpc3.js +0 -1103
  99. package/dist/stream-agent-turn-Dt5mZpc3.js.map +0 -1
  100. package/dist/stream-agent-turn-urHpmO_Z.d.ts +0 -160
  101. package/dist/structural-rollout-DPbZWgEm.js.map +0 -1
@@ -1,829 +0,0 @@
1
- import { a as canonicalCandidateBytes, l as embeddedCandidateArtifact } from "./protected-redaction-wGo44k2K.js";
2
- import { $ as parseExactAgentProfile, A as assertRecoveryMatchesStaged, D as candidateCleanupDeadline, F as rejectedFinish, G as applyExactAgentProfileDiff, I as rejectedStage, L as requireStagedTerminal, M as assertTerminalMatchesClaim, N as assertTerminalMatchesStaged, O as candidateCleanupTimeout, P as recoveredTerminalRecord, R as sealTerminalDigest, Y as freezeGenericAgentCandidateProfile, a as persistCandidateOutputArtifact, b as consumePreparedCandidateExecution, et as parseExactAgentProfileDiff, g as candidateClaimFileInternals, i as persistCandidateModelSettlementEvidence, it as readVerifiedArtifact, j as assertTerminalAllowedInPhase, k as withinCandidateCleanupDeadline, o as sealAgentCandidateExecutorStopAcknowledgement, p as sealAgentCandidateModelSettlement, r as RecoveryAgentCandidateTraceStore, tt as parseExactCandidateProfile, v as assertPreparedCandidateIntegrity, y as beginPreparedCandidateDisposal, z as terminalRecord } from "./prepare-DDGp0-rW.js";
3
- import { i as sealAgentCandidateBundle } from "./workspace-archive-Ybomp7AN.js";
4
- import "./protected-model-port-DxFN8DLS.js";
5
- import { agentCandidateExecutionPlanMaterialSchema, agentCandidateMaterializationReceiptSchema } from "@tangle-network/agent-interface";
6
- import { createHash, randomUUID } from "node:crypto";
7
- import { constants, linkSync } from "node:fs";
8
- import { cp, lstat, mkdir, open, readdir, readlink, realpath, unlink } from "node:fs/promises";
9
- import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
10
- import { verifyCodeSurface } from "@tangle-network/agent-eval/campaign";
11
- //#region src/candidate-execution/builder.ts
12
- /**
13
- * Compile one measured profile/code candidate into the immutable execution
14
- * contract. Code bytes are re-read and verified by agent-eval before they are
15
- * embedded. The returned bundle is schema-validated, canonically digested, and
16
- * deeply immutable; call `verifyAgentCandidateBundle` at the execution boundary
17
- * to re-read external memory, repository, and workspace artifacts.
18
- */
19
- function buildAgentCandidateBundle(input) {
20
- return sealAgentCandidateBundle({
21
- kind: "agent-candidate-bundle",
22
- digestAlgorithm: "rfc8785-sha256",
23
- profile: compileCandidateProfile(input.profile),
24
- code: compileCandidateCode(input.code),
25
- execution: input.execution,
26
- ...input.knowledge !== void 0 ? { knowledge: input.knowledge } : {},
27
- memory: input.memory
28
- });
29
- }
30
- function compileCandidateProfile(source) {
31
- if (source.kind === "candidate-profile") return parseExactCandidateProfile(source.profile);
32
- if (source.kind === "profile") return freezeGenericAgentCandidateProfile(source.profile);
33
- if (source.kind !== "profile-diffs") throw new Error(`unsupported candidate profile source: ${String(source.kind)}`);
34
- if (source.diffs.length === 0) throw new Error("profile-diffs source requires at least one AgentProfileDiff");
35
- let profile = parseExactAgentProfile(source.base, "base profile");
36
- for (const [index, inputDiff] of source.diffs.entries()) {
37
- const diff = parseExactAgentProfileDiff(inputDiff, `profile diff ${index}`);
38
- profile = applyExactAgentProfileDiff(profile, diff, `profile diff ${index}`);
39
- }
40
- return freezeGenericAgentCandidateProfile(profile);
41
- }
42
- function compileCandidateCode(source) {
43
- if (source.kind === "disabled" || source.kind === "no-op") return source;
44
- if (source.kind !== "code-surface") throw new Error(`unsupported candidate code source: ${String(source.kind)}`);
45
- const patch = embeddedCandidateArtifact(verifyCodeSurface(source.surface, source.worktreeDir).patchBytes);
46
- if (patch.sha256 !== source.surface.patch.sha256 || patch.byteLength !== source.surface.patch.byteLength) throw new Error("verified CodeSurface patch bytes do not match its content identity");
47
- return {
48
- kind: "git-patch",
49
- repository: source.repository,
50
- baseCommit: source.surface.baseCommit,
51
- baseTree: source.surface.baseTree,
52
- candidateTree: source.surface.candidateTree,
53
- patch: {
54
- format: "git-diff-binary",
55
- artifact: patch
56
- }
57
- };
58
- }
59
- //#endregion
60
- //#region src/candidate-execution/claim-file-store.ts
61
- const { assertExpiredLease, assertLease, assertSameSlot, assertUnexpiredLease, attemptRecord, claimSlot, leaseDigest, newLease, readClaim, readClaimIfPresent, readTerminal, readTerminalIfPresent, readTransitionIfPresent, rejectedExistingClaim, rejectedRetry, retryRejection, sealAttemptRef, sealClaim, sealLease } = candidateClaimFileInternals;
62
- /** Cross-process lifecycle implemented as fsynced, create-if-absent records. */
63
- var FileAgentCandidateExecutionClaimStore = class {
64
- directory;
65
- now;
66
- constructor(options) {
67
- if (options.directory.length === 0) throw new Error("candidate execution claim directory must not be empty");
68
- this.directory = options.directory;
69
- this.now = options.now ?? Date.now;
70
- }
71
- async tryClaim(requested) {
72
- const claim = sealClaim(requested);
73
- await mkdir(this.directory, { recursive: true });
74
- const claimPath = this.claimPath(claim);
75
- const existing = await readClaimIfPresent(claimPath);
76
- if (existing) {
77
- assertSameSlot(existing.claim, claim, claimPath);
78
- return rejectedExistingClaim(existing.claim, claim);
79
- }
80
- assertUnexpiredLease(claim.leaseExpiresAtMs, this.now());
81
- const retryFailure = await this.retryFailure(claim);
82
- if (retryFailure) return rejectedRetry(claim, retryFailure);
83
- const lease = newLease(claim);
84
- if (await writeRecordIfAbsent(this.directory, claimPath, {
85
- ...claim,
86
- phase: "claimed",
87
- leaseDigest: leaseDigest(lease)
88
- })) return Object.freeze({
89
- acquired: true,
90
- claim,
91
- lease
92
- });
93
- const winner = await readClaim(claimPath);
94
- assertSameSlot(winner.claim, claim, claimPath);
95
- return rejectedExistingClaim(winner.claim, claim);
96
- }
97
- async getAttempt(requestedAttempt) {
98
- return await this.storedAttempt(sealAttemptRef(requestedAttempt));
99
- }
100
- async markCandidateMayRun(requestedLease) {
101
- const lease = sealLease(requestedLease);
102
- const claimPath = this.claimPath(lease);
103
- const stored = await readClaim(claimPath);
104
- assertSameSlot(stored.claim, lease, claimPath);
105
- assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease);
106
- const state = await this.transitionState(stored.claim);
107
- if (state.phase === "candidate-may-run") return Object.freeze({
108
- marked: false,
109
- phase: "candidate-may-run"
110
- });
111
- if (state.staged) throw new Error("candidate execution terminal was staged before candidate-may-run phase");
112
- assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now());
113
- if (await writeRecordIfAbsent(this.directory, this.transitionPath(stored.claim, 1), {
114
- kind: "candidate-execution-phase",
115
- executionId: stored.claim.executionId,
116
- attempt: stored.claim.attempt,
117
- executionPlanDigest: stored.claim.executionPlanDigest,
118
- phase: "candidate-may-run"
119
- }, this.ownerPublication(stored.claim))) return Object.freeze({
120
- marked: true,
121
- phase: "candidate-may-run"
122
- });
123
- if ((await this.transitionState(stored.claim)).phase === "candidate-may-run") return Object.freeze({
124
- marked: false,
125
- phase: "candidate-may-run"
126
- });
127
- throw new Error("candidate execution terminal was staged before candidate-may-run phase");
128
- }
129
- async stageTerminal(requestedLease, result) {
130
- const lease = sealLease(requestedLease);
131
- const claimPath = this.claimPath(lease);
132
- const stored = await readClaim(claimPath);
133
- assertSameSlot(stored.claim, lease, claimPath);
134
- assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease);
135
- const terminal = terminalRecord(stored.claim, result);
136
- const state = await this.transitionState(stored.claim);
137
- if (state.staged) return rejectedStage(state.staged, terminal);
138
- assertTerminalAllowedInPhase(state.phase, terminal);
139
- assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now());
140
- const transition = state.phase === "claimed" ? 1 : 2;
141
- if (await writeRecordIfAbsent(this.directory, this.transitionPath(stored.claim, transition), {
142
- kind: "candidate-execution-pending-terminal",
143
- terminal
144
- }, this.ownerPublication(stored.claim))) return Object.freeze({
145
- staged: true,
146
- terminal
147
- });
148
- const winner = await this.transitionState(stored.claim);
149
- if (!winner.staged) throw new Error("candidate execution phase transition won while staging terminal");
150
- return rejectedStage(winner.staged, terminal);
151
- }
152
- async finish(requestedLease, requestedTerminalDigest) {
153
- const lease = sealLease(requestedLease);
154
- const terminalDigest = sealTerminalDigest(requestedTerminalDigest);
155
- const claimPath = this.claimPath(lease);
156
- const stored = await readClaim(claimPath);
157
- assertSameSlot(stored.claim, lease, claimPath);
158
- assertLease(stored.leaseDigest, stored.claim.leaseExpiresAtMs, lease);
159
- const staged = requireStagedTerminal((await this.transitionState(stored.claim)).staged, terminalDigest);
160
- const terminalPath = this.terminalPath(lease);
161
- const existing = await readTerminalIfPresent(terminalPath);
162
- if (existing) {
163
- assertTerminalMatchesClaim(existing, stored.claim, terminalPath);
164
- assertTerminalMatchesStaged(existing, staged, terminalPath);
165
- return rejectedFinish(existing, terminalDigest);
166
- }
167
- assertUnexpiredLease(stored.claim.leaseExpiresAtMs, this.now());
168
- if (await writeRecordIfAbsent(this.directory, terminalPath, { terminal: staged }, this.ownerPublication(stored.claim))) return Object.freeze({
169
- finished: true,
170
- terminal: staged
171
- });
172
- const winner = await readTerminal(terminalPath);
173
- assertSameSlot(winner, lease, terminalPath);
174
- assertTerminalMatchesClaim(winner, stored.claim, terminalPath);
175
- assertTerminalMatchesStaged(winner, staged, terminalPath);
176
- return rejectedFinish(winner, terminalDigest);
177
- }
178
- async recoverExpired(requestedAttempt, evidence) {
179
- const attempt = sealAttemptRef(requestedAttempt);
180
- const record = await this.storedAttempt(attempt);
181
- if (!record) throw new Error("candidate execution recovery does not name an acquired attempt");
182
- const recovered = recoveredTerminalRecord(record.claim, record.phase, evidence);
183
- if (record.staged) assertRecoveryMatchesStaged(record.staged, recovered);
184
- const requestedDigest = record.staged?.terminalDigest ?? recovered.terminalDigest;
185
- if (record.terminal) return rejectedFinish(record.terminal, requestedDigest);
186
- assertExpiredLease(record.claim.leaseExpiresAtMs, this.now());
187
- let staged = record.staged;
188
- if (!staged) {
189
- const transition = record.phase === "claimed" ? 1 : 2;
190
- if (await writeRecordIfAbsent(this.directory, this.transitionPath(record.claim, transition), {
191
- kind: "candidate-execution-pending-terminal",
192
- terminal: recovered
193
- })) staged = recovered;
194
- else {
195
- const winner = await this.transitionState(record.claim);
196
- if (!winner.staged) throw new Error("candidate execution recovery lost terminal staging to a phase transition");
197
- assertRecoveryMatchesStaged(winner.staged, recovered);
198
- staged = winner.staged;
199
- }
200
- }
201
- const terminalPath = this.terminalPath(attempt);
202
- if (await writeRecordIfAbsent(this.directory, terminalPath, { terminal: staged })) return Object.freeze({
203
- finished: true,
204
- terminal: staged
205
- });
206
- const winner = await readTerminal(terminalPath);
207
- assertSameSlot(winner, attempt, terminalPath);
208
- assertTerminalMatchesClaim(winner, record.claim, terminalPath);
209
- assertTerminalMatchesStaged(winner, staged, terminalPath);
210
- return rejectedFinish(winner, staged.terminalDigest);
211
- }
212
- async storedAttempt(claim) {
213
- const claimPath = this.claimPath(claim);
214
- const stored = await readClaimIfPresent(claimPath);
215
- if (!stored) return void 0;
216
- assertSameSlot(stored.claim, claim, claimPath);
217
- const state = await this.transitionState(stored.claim);
218
- const terminalPath = this.terminalPath(claim);
219
- const terminal = await readTerminalIfPresent(terminalPath);
220
- if (terminal) {
221
- assertTerminalMatchesClaim(terminal, stored.claim, terminalPath);
222
- if (!state.staged) throw new Error(`candidate execution terminal record at ${terminalPath} has no staged outbox`);
223
- assertTerminalMatchesStaged(terminal, state.staged, terminalPath);
224
- }
225
- return attemptRecord(stored.claim, state.phase, state.staged, terminal);
226
- }
227
- async transitionState(claim) {
228
- const firstPath = this.transitionPath(claim, 1);
229
- const first = await readTransitionIfPresent(firstPath, claim);
230
- if (!first) return { phase: "claimed" };
231
- if (first.kind === "pending") return {
232
- phase: "claimed",
233
- staged: first.terminal
234
- };
235
- const secondPath = this.transitionPath(claim, 2);
236
- const second = await readTransitionIfPresent(secondPath, claim);
237
- if (!second) return { phase: "candidate-may-run" };
238
- if (second.kind !== "pending") throw new Error(`candidate execution transition at ${secondPath} repeats the phase marker`);
239
- return {
240
- phase: "candidate-may-run",
241
- staged: second.terminal
242
- };
243
- }
244
- async retryFailure(claim) {
245
- if (claim.attempt === 1) return void 0;
246
- return retryRejection(claim, await this.storedAttempt({
247
- executionId: claim.executionId,
248
- attempt: claim.attempt - 1
249
- }));
250
- }
251
- ownerPublication(claim) {
252
- return { authorizePublish: () => {
253
- assertUnexpiredLease(claim.leaseExpiresAtMs, this.now());
254
- return true;
255
- } };
256
- }
257
- claimPath(claim) {
258
- return join(this.directory, `${claimSlot(claim)}.claim.json`);
259
- }
260
- terminalPath(claim) {
261
- return join(this.directory, `${claimSlot(claim)}.terminal.json`);
262
- }
263
- transitionPath(claim, ordinal) {
264
- return join(this.directory, `${claimSlot(claim)}.transition-${ordinal}.json`);
265
- }
266
- };
267
- /**
268
- * Candidate records keep this async writer because lease authorization must run after the
269
- * asynchronous temp-file fsync and immediately before synchronous link publication. The
270
- * supervisor durability helper is synchronous and has no lease hook, so combining them would
271
- * either move authorization earlier or add an async race to the claim boundary.
272
- */
273
- async function writeRecordIfAbsent(directory, destination, record, options = {}) {
274
- const temporaryPath = join(directory, `.candidate-execution-${process.pid}-${randomUUID()}.tmp`);
275
- const handle = await open(temporaryPath, "wx", 384);
276
- try {
277
- await handle.writeFile(Buffer.concat([Buffer.from(canonicalCandidateBytes(record)), Buffer.from("\n")]));
278
- await handle.sync();
279
- } finally {
280
- await handle.close();
281
- }
282
- let written = false;
283
- try {
284
- if (options.authorizePublish && options.authorizePublish() !== true) throw new Error("candidate execution record publication was not authorized");
285
- linkSync(temporaryPath, destination);
286
- written = true;
287
- await syncDirectory(directory);
288
- } catch (error) {
289
- if (!isNodeError(error, "EEXIST")) throw error;
290
- } finally {
291
- await unlink(temporaryPath).catch((error) => {
292
- if (!isNodeError(error, "ENOENT")) throw error;
293
- });
294
- }
295
- return written;
296
- }
297
- async function syncDirectory(directory) {
298
- const handle = await open(directory, "r");
299
- try {
300
- await handle.sync();
301
- } finally {
302
- await handle.close();
303
- }
304
- }
305
- function isNodeError(error, code) {
306
- return error !== null && typeof error === "object" && "code" in error && error.code === code;
307
- }
308
- //#endregion
309
- //#region src/candidate-execution/dispose.ts
310
- /** Revoke reservations held by a prepared candidate that will not be executed. */
311
- async function disposePreparedAgentCandidateExecution(prepared, options = {}) {
312
- const initialState = assertPreparedCandidateIntegrity(prepared);
313
- const cleanupTimeoutMs = options.cleanupTimeoutMs ?? initialState.cleanupTimeoutMs;
314
- if (cleanupTimeoutMs > initialState.cleanupTimeoutMs) throw new Error("disposal cleanup timeout exceeds the frozen preparation bound");
315
- const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs);
316
- const state = beginPreparedCandidateDisposal(prepared);
317
- const cleanup = [];
318
- if (state.memory.mode === "isolated") {
319
- const reservation = state.memoryReservation;
320
- if (!reservation) throw new Error("isolated memory reservation is missing");
321
- cleanup.push(withinCandidateCleanupDeadline(async () => {
322
- const closed = await state.ports.memory.close({
323
- executionId: state.executionId,
324
- preparationId: reservation.preparationId,
325
- accessDigest: reservation.accessDigest,
326
- effectiveNamespace: reservation.effectiveNamespace,
327
- reason: "abandoned"
328
- });
329
- if (closed.closed !== true || Object.keys(closed).some((key) => key !== "closed")) throw new Error("abandoned isolated memory access did not acknowledge closure");
330
- }, cleanupDeadlineAtMs, "isolated memory disposal"));
331
- }
332
- cleanup.push(withinCandidateCleanupDeadline(async () => {
333
- if (sealAgentCandidateModelSettlement(await state.ports.models.settleGrant({
334
- executionId: state.executionId,
335
- preparationId: state.preparationId,
336
- grantDigest: state.modelReservation.digest,
337
- resolved: state.resolvedModel,
338
- reason: "abandoned"
339
- }), {
340
- preparationId: state.preparationId,
341
- grantDigest: state.modelReservation.digest,
342
- model: state.resolvedModel.model
343
- }).usage.modelCalls !== 0) throw new Error("unexecuted model reservation unexpectedly contains calls");
344
- }, cleanupDeadlineAtMs, "model reservation disposal"));
345
- const failures = (await Promise.allSettled(cleanup)).filter((result) => result.status === "rejected").map((result) => result.reason);
346
- if (failures.length > 0) {
347
- consumePreparedCandidateExecution(prepared, "disposal-failed");
348
- throw new AggregateError(failures, "candidate preparation disposal failed");
349
- }
350
- consumePreparedCandidateExecution(prepared, "disposed");
351
- return Object.freeze({ disposed: true });
352
- }
353
- //#endregion
354
- //#region src/candidate-execution/protected-model-grant.ts
355
- const SETTLEMENT_RETRY_INITIAL_DELAY_MS = 25;
356
- const SETTLEMENT_RETRY_MAX_DELAY_MS = 1e3;
357
- /**
358
- * Run one bounded unit under a protected model grant.
359
- *
360
- * Runtime owns the grant lifecycle; callers own the unit boundary and any
361
- * durable scheduling or accounting around it. A reserved grant is settled
362
- * after activation failure or callback failure, and the callback error is
363
- * preserved when settlement also fails.
364
- */
365
- async function runProtectedAgentCandidateModelGrant(options) {
366
- if (typeof options.execute !== "function") throw new TypeError("protected model grant execute callback is required");
367
- const resolved = await options.port.resolve(options.resolve);
368
- const reservation = await options.port.reserveGrant({
369
- ...options.reserve,
370
- resolved
371
- });
372
- let value;
373
- let executionError;
374
- let executionFailed = false;
375
- let activated = false;
376
- let reason = "preparation-failed";
377
- try {
378
- const activation = await options.port.activateGrant({
379
- executionId: options.reserve.executionId,
380
- preparationId: options.reserve.preparationId,
381
- grantDigest: reservation.digest,
382
- resolved,
383
- deadlineAtMs: options.deadlineAtMs
384
- });
385
- activated = true;
386
- value = await options.execute({
387
- activation,
388
- reservation,
389
- resolved
390
- });
391
- reason = "completed";
392
- } catch (error) {
393
- executionError = error;
394
- executionFailed = true;
395
- if (activated) reason = "failed";
396
- }
397
- let settlement;
398
- try {
399
- settlement = await settleProtectedModelGrant(options, {
400
- executionId: options.reserve.executionId,
401
- preparationId: options.reserve.preparationId,
402
- grantDigest: reservation.digest,
403
- resolved,
404
- reason
405
- });
406
- } catch (settlementError) {
407
- if (executionFailed) throw new AggregateError([executionError, settlementError], "protected model execution failed and its grant did not settle");
408
- throw settlementError;
409
- }
410
- if (executionFailed) throw executionError;
411
- return {
412
- value,
413
- resolved,
414
- reservation,
415
- settlement
416
- };
417
- }
418
- async function settleProtectedModelGrant(options, input) {
419
- let delayMs = SETTLEMENT_RETRY_INITIAL_DELAY_MS;
420
- for (;;) try {
421
- return await options.port.settleGrant(input);
422
- } catch (error) {
423
- if (!isCandidateGrantDrainingError(error)) throw error;
424
- const remainingMs = options.deadlineAtMs - Date.now();
425
- if (remainingMs <= 0) throw error;
426
- await waitForSettlementRetry(Math.min(delayMs, remainingMs));
427
- if (Date.now() >= options.deadlineAtMs) throw error;
428
- delayMs = Math.min(delayMs * 2, SETTLEMENT_RETRY_MAX_DELAY_MS);
429
- }
430
- }
431
- function waitForSettlementRetry(delayMs) {
432
- return new Promise((resolve) => setTimeout(resolve, delayMs));
433
- }
434
- /** Only the gateway's explicit draining state is retryable; auth and ledger errors fail closed. */
435
- function isCandidateGrantDrainingError(error) {
436
- if (typeof error !== "object" || error === null) return false;
437
- const candidate = error;
438
- if (candidate.code === "candidate_grant_draining") return true;
439
- return typeof candidate.message === "string" && /\bcandidate_grant_draining\b/u.test(candidate.message);
440
- }
441
- //#endregion
442
- //#region src/candidate-execution/recover.ts
443
- /** Close an expired crashed attempt from persisted non-secret handles, then record failure. */
444
- async function recoverExpiredAgentCandidateExecution(options) {
445
- const record = await options.claimStore.getAttempt(options.attempt);
446
- if (!record) throw new Error("candidate execution recovery attempt is missing");
447
- if (record.terminal) return Object.freeze({
448
- finished: false,
449
- terminal: record.terminal,
450
- exactReplay: true
451
- });
452
- const cleanupTimeoutMs = candidateCleanupTimeout(options.cleanupTimeoutMs ?? record.claim.cleanup.cleanupTimeoutMs);
453
- if (cleanupTimeoutMs > record.claim.cleanup.cleanupTimeoutMs) throw new Error("recovery cleanup timeout exceeds the frozen preparation bound");
454
- if ((options.now ?? Date.now)() < record.claim.leaseExpiresAtMs) throw new Error("candidate execution lease has not expired");
455
- const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs);
456
- const recoveryTraceStore = new RecoveryAgentCandidateTraceStore(options.traceStore);
457
- const preparation = withinCandidateCleanupDeadline(() => readRecoveryPreparation(record.claim, options.outputArtifacts), cleanupDeadlineAtMs, "expired candidate preparation evidence read");
458
- const processClosure = withinCandidateCleanupDeadline(async (cleanupSignal) => {
459
- sealAgentCandidateExecutorStopAcknowledgement(await options.executor.stop({
460
- executionId: record.claim.executionId,
461
- executionPlanDigest: record.claim.executionPlanDigest
462
- }, {
463
- traceStore: recoveryTraceStore,
464
- reason: "failed",
465
- signal: cleanupSignal,
466
- deadlineAtMs: cleanupDeadlineAtMs
467
- }));
468
- if (options.executor.dispose) {
469
- const disposed = await options.executor.dispose({
470
- executionId: record.claim.executionId,
471
- executionPlanDigest: record.claim.executionPlanDigest
472
- }, { signal: cleanupSignal });
473
- if (disposed.disposed !== true || Object.keys(disposed).some((key) => key !== "disposed")) throw new Error("expired candidate executor did not acknowledge resource disposal");
474
- }
475
- return { stopped: true };
476
- }, cleanupDeadlineAtMs, "expired candidate process termination");
477
- const modelClosure = withinCandidateCleanupDeadline(async () => sealAgentCandidateModelSettlement(await options.ports.models.settleGrant({
478
- executionId: record.claim.executionId,
479
- preparationId: record.claim.cleanup.preparationId,
480
- grantDigest: record.claim.cleanup.modelGrantDigest,
481
- resolved: record.claim.cleanup.resolvedModel,
482
- reason: "failed"
483
- }), {
484
- preparationId: record.claim.cleanup.preparationId,
485
- grantDigest: record.claim.cleanup.modelGrantDigest,
486
- model: record.claim.cleanup.resolvedModel.model
487
- }), cleanupDeadlineAtMs, "expired candidate model settlement");
488
- const memoryClosure = record.claim.cleanup.memory ? withinCandidateCleanupDeadline(async () => {
489
- const memory = record.claim.cleanup.memory;
490
- if (!memory) throw new Error("expired candidate memory handle is missing");
491
- const closed = await options.ports.memory.close({
492
- executionId: record.claim.executionId,
493
- preparationId: record.claim.cleanup.preparationId,
494
- accessDigest: memory.accessDigest,
495
- effectiveNamespace: memory.effectiveNamespace,
496
- reason: "failed"
497
- });
498
- if (closed.closed !== true || Object.keys(closed).some((key) => key !== "closed")) throw new Error("expired candidate memory did not acknowledge closure");
499
- return { closed: true };
500
- }, cleanupDeadlineAtMs, "expired candidate memory closure") : void 0;
501
- const closureOutcomes = Promise.allSettled([modelClosure, memoryClosure ?? Promise.resolve(void 0)]);
502
- const [preparationOutcome, processOutcome] = await Promise.allSettled([preparation, processClosure]);
503
- const failures = [preparationOutcome, processOutcome].filter((outcome) => outcome.status === "rejected").map((outcome) => outcome.reason);
504
- const [modelOutcome, memoryOutcome] = await closureOutcomes;
505
- for (const outcome of [modelOutcome, memoryOutcome]) if (outcome.status === "rejected") failures.push(outcome.reason);
506
- if (failures.length > 0) throw new AggregateError(failures, "expired candidate cleanup could not be proven");
507
- if (modelOutcome.status !== "fulfilled") throw new Error("expired candidate model settlement was not recovered");
508
- const model = modelOutcome.value;
509
- const modelSettlement = await withinCandidateCleanupDeadline(() => persistCandidateModelSettlementEvidence({
510
- executionId: record.claim.executionId,
511
- executionPlanDigest: record.claim.executionPlanDigest,
512
- resolvedModel: record.claim.cleanup.resolvedModel
513
- }, model, options.outputArtifacts), cleanupDeadlineAtMs, "expired candidate model-settlement persistence");
514
- const failureClass = record.phase === "claimed" && model.usage.modelCalls === 0 ? "pre-model-infrastructure" : "unknown";
515
- const failureEvidence = await withinCandidateCleanupDeadline(() => persistCandidateOutputArtifact(options.outputArtifacts, {
516
- executionId: record.claim.executionId,
517
- purpose: "failure-evidence",
518
- bytes: canonicalCandidateBytes({
519
- kind: "agent-candidate-recovery-failure",
520
- executionId: record.claim.executionId,
521
- attempt: record.claim.attempt,
522
- bundleDigest: record.claim.bundleDigest,
523
- executionPlanDigest: record.claim.executionPlanDigest,
524
- materializationReceiptDigest: record.claim.preparationEvidence.materializationReceipt.sha256,
525
- failureClass,
526
- processStopped: true,
527
- modelSettlementDigest: modelSettlement.digest,
528
- memoryClosed: record.claim.cleanup.memory !== void 0
529
- })
530
- }), cleanupDeadlineAtMs, "expired candidate failure-evidence persistence");
531
- const memory = record.claim.cleanup.memory;
532
- return await options.claimStore.recoverExpired(options.attempt, {
533
- failureClass,
534
- usage: model.usage,
535
- modelSettlement: modelSettlement.artifact,
536
- failureEvidence,
537
- process: {
538
- stopped: true,
539
- executionPlanDigest: record.claim.executionPlanDigest
540
- },
541
- model: {
542
- closed: true,
543
- preparationId: record.claim.cleanup.preparationId,
544
- grantDigest: record.claim.cleanup.modelGrantDigest
545
- },
546
- ...memory ? { memory: {
547
- closed: true,
548
- preparationId: record.claim.cleanup.preparationId,
549
- accessDigest: memory.accessDigest,
550
- effectiveNamespace: memory.effectiveNamespace
551
- } } : {}
552
- });
553
- }
554
- async function readRecoveryPreparation(claim, artifacts) {
555
- const [planBytes, receiptBytes] = await Promise.all([readVerifiedArtifact(claim.preparationEvidence.executionPlan, artifacts), readVerifiedArtifact(claim.preparationEvidence.materializationReceipt, artifacts)]);
556
- const plan = agentCandidateExecutionPlanMaterialSchema.parse(parseCanonicalBytes(planBytes, "plan"));
557
- if (claim.preparationEvidence.executionPlan.sha256 !== claim.executionPlanDigest) throw new Error("recovery execution plan artifact does not match the claimed digest");
558
- const receiptMaterial = parseCanonicalBytes(receiptBytes, "materialization receipt");
559
- const receipt = agentCandidateMaterializationReceiptSchema.parse({
560
- ...receiptMaterial,
561
- digest: claim.preparationEvidence.materializationReceipt.sha256
562
- });
563
- if (receipt.bundleDigest !== claim.bundleDigest || receipt.executionPlan.digest !== claim.executionPlanDigest || plan.runCell.bundleDigest !== claim.bundleDigest || plan.executionId !== claim.executionId) throw new Error("recovery preparation evidence does not match the durable claim");
564
- return {
565
- plan,
566
- receipt
567
- };
568
- }
569
- function parseCanonicalBytes(bytes, label) {
570
- let parsed;
571
- try {
572
- parsed = JSON.parse(Buffer.from(bytes).toString("utf8"));
573
- } catch (error) {
574
- throw new Error(`candidate recovery ${label} is not JSON`, { cause: error });
575
- }
576
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`candidate recovery ${label} must be an object`);
577
- if (!Buffer.from(canonicalCandidateBytes(parsed)).equals(Buffer.from(bytes))) throw new Error(`candidate recovery ${label} bytes are not canonical`);
578
- return parsed;
579
- }
580
- //#endregion
581
- //#region src/candidate-execution/workspace-tree.ts
582
- /**
583
- * A content digest over a DIRECTORY TREE, and the per-file seed that fills one.
584
- *
585
- * The question this answers is "is this workspace the same workspace" — the question every launch
586
- * and every close of a branchable run has to answer, about a tree that a live process is still
587
- * writing to. That is a different question from the one
588
- * {@link scanMaterializedWorkspaceManifest} answers, and the difference decides the policies here:
589
- *
590
- * - A candidate manifest describes ONE EXACT regular-file workspace. It refuses a symbolic link
591
- * outright, because `AgentCandidateWorkspaceManifestMaterial` has no representation for one, and
592
- * it refuses an entry that disappears, because a signed manifest describes a settled tree.
593
- * - A tree descriptor describes what a RUN PRODUCED. A python venv writes
594
- * `bin/python -> /usr/bin/python3` as a matter of course, and a work directory can lose an entry
595
- * between two directory reads. Refusing on either aborts the close, and a run that never wrote
596
- * its digest can never be branched from.
597
- *
598
- * So the two policies are the caller's, one for links and one for vanishing entries, and both
599
- * default to `'refuse'` — the strict reading, correct for an INPUT seed, where a link that leaves
600
- * the tree means the seed describes bytes it does not contain. `'exclude'` is the reading a close
601
- * needs: the entry is never followed, it is recorded, and the exclusion itself is hashed, so two
602
- * trees that differ only in an excluded entry cannot share a digest.
603
- *
604
- * NOTHING is held in memory. A file reaches the tree digest as its own streamed sha-256 plus its
605
- * length, so the largest file in the tree does not decide whether the tree can be described:
606
- * `readFile` refuses anything above 2 GiB with `ERR_FS_FILE_TOO_LARGE`, and a single multi-gigabyte
607
- * artifact is exactly what a run leaves behind.
608
- *
609
- * @module
610
- * @experimental
611
- */
612
- const TREE_ALGORITHMS = /* @__PURE__ */ new Set(["tree-v1", "portable-tree-v1"]);
613
- const ENTRY_POLICIES = /* @__PURE__ */ new Set(["refuse", "exclude"]);
614
- /**
615
- * Describe one directory tree by content, streaming every file.
616
- *
617
- * The digest covers, for every entry in sorted order: its kind, its tree-relative path, its mode
618
- * under the selected algorithm, and then — for a file, its length and its own sha-256; for a kept
619
- * link, its target; for an excluded entry, the reason and the target. A file's content therefore
620
- * never enters the tree hash directly, which is what removes any in-memory size ceiling: the tree
621
- * hash consumes 32 bytes per file however large the file is.
622
- *
623
- * A hard-linked regular file is described like any other regular file. Its content is what the
624
- * digest is about, and a package manager that links a store into `node_modules` is ordinary
625
- * content, not a reason to refuse a workspace.
626
- */
627
- async function describeWorkspaceTree(directory, options = {}) {
628
- const algorithm = options.algorithm ?? "tree-v1";
629
- const onEscapingLink = options.onEscapingLink ?? "refuse";
630
- const onMissingEntry = options.onMissingEntry ?? "refuse";
631
- if (!TREE_ALGORITHMS.has(algorithm)) throw new Error(`unsupported workspace tree algorithm: ${algorithm}`);
632
- for (const [name, policy] of [["onEscapingLink", onEscapingLink], ["onMissingEntry", onMissingEntry]]) if (!ENTRY_POLICIES.has(policy)) throw new Error(`unsupported workspace tree ${name} policy: ${policy}`);
633
- const root = resolve(directory);
634
- const rootStats = await lstatIfPresent(root);
635
- if (rootStats === null || !rootStats.isDirectory() || rootStats.isSymbolicLink()) throw new Error(`workspace tree root must be a real directory: ${directory}`);
636
- const physicalRoot = await realpath(root);
637
- const hash = createHash("sha256");
638
- hash.update(`workspace-tree\0${algorithm}\0`);
639
- const counts = {
640
- files: 0,
641
- directories: 0,
642
- symlinks: 0,
643
- bytes: 0
644
- };
645
- const excluded = [];
646
- const exclude = (path, reason, target) => {
647
- const name = treePath(root, path);
648
- excluded.push(Object.freeze(target === void 0 ? {
649
- path: name,
650
- reason
651
- } : {
652
- path: name,
653
- reason,
654
- target
655
- }));
656
- hash.update(`excluded\0${name}\0${reason}\0${target ?? ""}\0`);
657
- };
658
- /** True when the caller asked to continue past a vanished entry, and it was not the root. */
659
- const recordMissing = (path) => {
660
- if (onMissingEntry === "refuse" || path === root) return false;
661
- exclude(path, "entry-disappeared");
662
- return true;
663
- };
664
- const visit = async (path) => {
665
- const info = await lstatIfPresent(path);
666
- if (info === null) {
667
- if (recordMissing(path)) return;
668
- throw new Error(`workspace tree entry disappeared during the walk: ${treePath(root, path)}`);
669
- }
670
- const name = treePath(root, path);
671
- const mode = entryMode(info.mode, algorithm, info.isDirectory(), info.isSymbolicLink());
672
- if (info.isDirectory()) {
673
- let entries;
674
- try {
675
- entries = await readdir(path);
676
- } catch (error) {
677
- if (isMissing(error) && recordMissing(path)) return;
678
- throw error;
679
- }
680
- counts.directories += 1;
681
- hash.update(`directory\0${name}\0${mode}\0`);
682
- for (const entry of entries.sort()) await visit(join(path, entry));
683
- return;
684
- }
685
- if (info.isSymbolicLink()) {
686
- await visitSymbolicLink(path, name, mode);
687
- return;
688
- }
689
- if (!info.isFile()) throw new Error(`workspace tree contains an unsupported entry: ${name}`);
690
- const read = await digestFile(path);
691
- if (read === null) {
692
- if (recordMissing(path)) return;
693
- throw new Error(`workspace tree entry disappeared during the walk: ${name}`);
694
- }
695
- counts.files += 1;
696
- counts.bytes += read.byteLength;
697
- hash.update(`file\0${name}\0${mode}\0${read.byteLength}\0${read.sha256}\0`);
698
- };
699
- const visitSymbolicLink = async (path, name, mode) => {
700
- let target;
701
- try {
702
- target = await readlink(path);
703
- } catch (error) {
704
- if (isMissing(error) && recordMissing(path)) return;
705
- throw error;
706
- }
707
- const refuse = (reason) => {
708
- if (onEscapingLink === "refuse") throw new Error(`workspace tree contains a link that ${reasonText(reason)}: ${name} -> ${target}`);
709
- exclude(path, reason, target);
710
- };
711
- if (isAbsolute(target)) return refuse("absolute-symlink");
712
- if (!isWithin(root, resolve(dirname(path), target))) return refuse("symlink-escapes-tree");
713
- let physicalTarget;
714
- try {
715
- physicalTarget = await realpath(path);
716
- } catch {
717
- return refuse("unresolved-symlink");
718
- }
719
- if (!isWithin(physicalRoot, physicalTarget)) return refuse("symlink-resolves-outside-tree");
720
- counts.symlinks += 1;
721
- hash.update(`symlink\0${name}\0${mode}\0${target}\0`);
722
- };
723
- await visit(root);
724
- return Object.freeze({
725
- algorithm,
726
- digest: `sha256:${hash.digest("hex")}`,
727
- files: counts.files,
728
- directories: counts.directories,
729
- symlinks: counts.symlinks,
730
- bytes: counts.bytes,
731
- excluded: Object.freeze(excluded)
732
- });
733
- }
734
- /**
735
- * Seed a workspace from a directory, one entry at a time, and return the digest of what was
736
- * seeded.
737
- *
738
- * Nothing is packed: `cp` walks and copies file by file, so a multi-gigabyte seed costs one file
739
- * handle rather than one archive in memory. Existing destination entries are never overwritten —
740
- * a seed that could replace a file the workspace already holds would make the resulting tree
741
- * depend on the order two seeds ran in.
742
- *
743
- * Links are copied verbatim, exactly as they were written. Following them would copy bytes from
744
- * outside the seed into the workspace, which is the same rule {@link describeWorkspaceTree}
745
- * applies to the digest.
746
- */
747
- async function seedWorkspaceTree(input) {
748
- const source = resolve(input.source);
749
- const destination = resolve(input.destination);
750
- if (source === destination || isWithin(source, destination)) throw new Error("workspace seed must not contain its destination");
751
- const destinationStats = await lstatIfPresent(destination);
752
- if (destinationStats === null || !destinationStats.isDirectory()) throw new Error(`workspace seed destination must be an existing directory: ${input.destination}`);
753
- const descriptor = await describeWorkspaceTree(source, { ...input.algorithm === void 0 ? {} : { algorithm: input.algorithm } });
754
- for (const name of (await readdir(source)).sort()) {
755
- if (await lstatIfPresent(join(destination, name)) !== null) throw new Error(`workspace seed would overwrite an existing entry: ${name}`);
756
- await cp(join(source, name), join(destination, name), {
757
- recursive: true,
758
- force: false,
759
- errorOnExist: true,
760
- verbatimSymlinks: true
761
- });
762
- }
763
- return descriptor;
764
- }
765
- /** Stream one regular file into its own digest, or `null` when it vanished mid-read. */
766
- async function digestFile(path) {
767
- let descriptor;
768
- try {
769
- descriptor = await open(path, constants.O_RDONLY | (typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0));
770
- } catch (error) {
771
- if (isMissing(error)) return null;
772
- throw error;
773
- }
774
- try {
775
- const hash = createHash("sha256");
776
- const buffer = Buffer.allocUnsafe(1024 * 1024);
777
- let total = 0;
778
- while (true) {
779
- const { bytesRead } = await descriptor.read(buffer, 0, buffer.byteLength, null);
780
- if (bytesRead === 0) break;
781
- total += bytesRead;
782
- hash.update(buffer.subarray(0, bytesRead));
783
- }
784
- return {
785
- sha256: `sha256:${hash.digest("hex")}`,
786
- byteLength: total
787
- };
788
- } finally {
789
- await descriptor.close();
790
- }
791
- }
792
- /** Git records exactly two file modes; a portable tree records the same two, plus 755 for a
793
- * directory and 777 for a link, which is what every filesystem reports for one. */
794
- function entryMode(mode, algorithm, isDirectory, isSymbolicLink) {
795
- const bits = Number(mode);
796
- if (algorithm !== "portable-tree-v1") return String(bits & 511);
797
- if (isDirectory) return "755";
798
- if (isSymbolicLink) return "777";
799
- return (bits & 73) !== 0 ? "755" : "644";
800
- }
801
- function reasonText(reason) {
802
- switch (reason) {
803
- case "absolute-symlink": return "is absolute";
804
- case "symlink-escapes-tree": return "escapes its tree";
805
- case "symlink-resolves-outside-tree": return "resolves outside its tree";
806
- default: return "does not resolve";
807
- }
808
- }
809
- function treePath(root, path) {
810
- return relative(root, path).split(sep).join("/") || ".";
811
- }
812
- function isWithin(root, path) {
813
- return path === root || path.startsWith(`${root}${sep}`);
814
- }
815
- function isMissing(error) {
816
- return error?.code === "ENOENT";
817
- }
818
- async function lstatIfPresent(path) {
819
- try {
820
- return await lstat(path);
821
- } catch (error) {
822
- if (isMissing(error)) return null;
823
- throw error;
824
- }
825
- }
826
- //#endregion
827
- export { disposePreparedAgentCandidateExecution as a, runProtectedAgentCandidateModelGrant as i, seedWorkspaceTree as n, FileAgentCandidateExecutionClaimStore as o, recoverExpiredAgentCandidateExecution as r, buildAgentCandidateBundle as s, describeWorkspaceTree as t };
828
-
829
- //# sourceMappingURL=candidate-execution-nvqVIMyS.js.map