@tangle-network/agent-runtime 0.94.13 → 0.96.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.
- package/README.md +64 -15
- package/dist/activation-B0ZD7nfX.d.ts +63 -0
- package/dist/agent.d.ts +6 -193
- package/dist/agent.js +10 -234
- package/dist/agent.js.map +1 -1
- package/dist/analyst-loop.d.ts +7 -10
- package/dist/analyst-loop.js +1 -2
- package/dist/candidate-execution/index.d.ts +43 -16
- package/dist/candidate-execution/index.js +17 -8
- package/dist/{chunk-VYA2YEKA.js → chunk-2KGAN2HM.js} +83 -14
- package/dist/chunk-2KGAN2HM.js.map +1 -0
- package/dist/chunk-3XKSBI2U.js +474 -0
- package/dist/chunk-3XKSBI2U.js.map +1 -0
- package/dist/{chunk-TVJQAYQM.js → chunk-6XKPVJAZ.js} +107 -716
- package/dist/chunk-6XKPVJAZ.js.map +1 -0
- package/dist/chunk-BLQIYRVR.js +699 -0
- package/dist/chunk-BLQIYRVR.js.map +1 -0
- package/dist/{chunk-QDSOD7RC.js → chunk-FD2MBMOH.js} +13 -101
- package/dist/chunk-FD2MBMOH.js.map +1 -0
- package/dist/{chunk-U33YZ7B2.js → chunk-FXF2OL34.js} +8 -8
- package/dist/{chunk-EP6RVHMX.js → chunk-HZDEXTSL.js} +848 -2
- package/dist/chunk-HZDEXTSL.js.map +1 -0
- package/dist/{chunk-WIPGQ4GT.js → chunk-IMSNJSXH.js} +1 -1
- package/dist/{chunk-WIPGQ4GT.js.map → chunk-IMSNJSXH.js.map} +1 -1
- package/dist/{chunk-VSWBYWFK.js → chunk-M6MD6JBS.js} +20 -27
- package/dist/chunk-M6MD6JBS.js.map +1 -0
- package/dist/chunk-PSOCBNM3.js +2069 -0
- package/dist/chunk-PSOCBNM3.js.map +1 -0
- package/dist/{chunk-AEG3NGJ2.js → chunk-Q2JSAVQ3.js} +34 -2
- package/dist/chunk-Q2JSAVQ3.js.map +1 -0
- package/dist/{chunk-XP5KDM3R.js → chunk-SGQ4YIQW.js} +4 -4
- package/dist/{chunk-C3UKLQ54.js → chunk-UQ6PNNXM.js} +18 -10
- package/dist/chunk-UQ6PNNXM.js.map +1 -0
- package/dist/{chunk-33OG2NN3.js → chunk-WYC2XJF2.js} +2 -2
- package/dist/{chunk-ZEYAT33L.js → chunk-Y3SRWZMP.js} +2 -2
- package/dist/{chunk-CNH7DF7Z.js → chunk-YOLKCWRV.js} +1116 -591
- package/dist/chunk-YOLKCWRV.js.map +1 -0
- package/dist/{completion-gate-D1gX1-hg.d.ts → completion-gate-C80jiRfN.d.ts} +1 -1
- package/dist/conversation.d.ts +12 -1
- package/dist/conversation.js +2 -3
- package/dist/{coordination-Dr_axlAf.d.ts → coordination-BFE3Den7.d.ts} +10 -11
- package/dist/environment-provider.d.ts +2 -2
- package/dist/environment-provider.js +1 -2
- package/dist/{agentic-generator-DDMM45kZ.d.ts → improve-g75IE2Cx.d.ts} +152 -4
- package/dist/{improvement-adapter-BieWeK5J.d.ts → improvement-adapter-HAZz-7vK.d.ts} +8 -31
- package/dist/index.d.ts +55 -28
- package/dist/index.js +206 -82
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +185 -120
- package/dist/intelligence.js +509 -345
- package/dist/intelligence.js.map +1 -1
- package/dist/knowledge.d.ts +40 -12
- package/dist/knowledge.js +13 -7
- package/dist/{loop-runner-bin-BRQSQdHa.d.ts → loop-runner-bin-Cn1N2rRo.d.ts} +3 -3
- package/dist/loop-runner-bin.d.ts +6 -6
- package/dist/loop-runner-bin.js +8 -10
- package/dist/loops.d.ts +13 -13
- package/dist/loops.js +6 -8
- package/dist/mcp/bin.js +5 -7
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +6 -6
- package/dist/mcp/index.js +12 -14
- package/dist/mcp/index.js.map +1 -1
- package/dist/platform.js +0 -2
- package/dist/platform.js.map +1 -1
- package/dist/primeintellect/index.js +1 -2
- package/dist/primeintellect/index.js.map +1 -1
- package/dist/profile-DbfaMTdk.d.ts +233 -0
- package/dist/profiles.d.ts +1 -1
- package/dist/profiles.js +0 -1
- package/dist/profiles.js.map +1 -1
- package/dist/{supervise-DmYOug5f.d.ts → supervise-BLPI50-w.d.ts} +3 -3
- package/dist/{types-ByAYqlVb.d.ts → types-B3vAW0Oq.d.ts} +1 -1
- package/dist/{prepare-CtdtsFNG.d.ts → types-CWqfCO8s.d.ts} +67 -298
- package/dist/{types-BC3bZpH0.d.ts → types-CmYCMbFT.d.ts} +12 -54
- package/dist/{types-1d5QGK3t.d.ts → types-CmnA2iL3.d.ts} +3 -3
- package/dist/{worktree-fanout-CPprU-qI.d.ts → worktree-fanout-DCA3G4bO.d.ts} +3 -3
- package/package.json +14 -16
- package/skills/build-with-agent-runtime/SKILL.md +122 -213
- package/dist/chunk-6O73TRHW.js +0 -142
- package/dist/chunk-6O73TRHW.js.map +0 -1
- package/dist/chunk-AEG3NGJ2.js.map +0 -1
- package/dist/chunk-C3UKLQ54.js.map +0 -1
- package/dist/chunk-CNH7DF7Z.js.map +0 -1
- package/dist/chunk-D3H7F6L2.js +0 -626
- package/dist/chunk-D3H7F6L2.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-EP6RVHMX.js.map +0 -1
- package/dist/chunk-HGRW27YY.js +0 -214
- package/dist/chunk-HGRW27YY.js.map +0 -1
- package/dist/chunk-ISTDY47H.js +0 -849
- package/dist/chunk-ISTDY47H.js.map +0 -1
- package/dist/chunk-PCURO3DL.js +0 -661
- package/dist/chunk-PCURO3DL.js.map +0 -1
- package/dist/chunk-QDSOD7RC.js.map +0 -1
- package/dist/chunk-TVJQAYQM.js.map +0 -1
- package/dist/chunk-VSWBYWFK.js.map +0 -1
- package/dist/chunk-VYA2YEKA.js.map +0 -1
- package/dist/generator-YkAQrOoD.d.ts +0 -382
- package/dist/improve-BN3HyXIO.d.ts +0 -172
- package/dist/lifecycle.d.ts +0 -870
- package/dist/lifecycle.js +0 -981
- package/dist/lifecycle.js.map +0 -1
- package/dist/mcp-serve-verifier-DQQDbuyz.d.ts +0 -34
- package/skills/agent-runtime-adoption/SKILL.md +0 -246
- /package/dist/{chunk-U33YZ7B2.js.map → chunk-FXF2OL34.js.map} +0 -0
- /package/dist/{chunk-XP5KDM3R.js.map → chunk-SGQ4YIQW.js.map} +0 -0
- /package/dist/{chunk-33OG2NN3.js.map → chunk-WYC2XJF2.js.map} +0 -0
- /package/dist/{chunk-ZEYAT33L.js.map → chunk-Y3SRWZMP.js.map} +0 -0
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
sealAgentCandidateBundle
|
|
3
|
+
} from "./chunk-BLQIYRVR.js";
|
|
4
|
+
import {
|
|
5
5
|
RecoveryAgentCandidateTraceStore,
|
|
6
|
-
TERMINAL_FORMAT_VERSION,
|
|
7
6
|
applyExactAgentProfileDiff,
|
|
8
7
|
assertExactObjectKeys,
|
|
9
|
-
assertNoGitIndirection,
|
|
10
8
|
assertPreparedCandidateIntegrity,
|
|
11
9
|
assertRecoveryMatchesStaged,
|
|
12
10
|
assertTerminalAllowedInPhase,
|
|
@@ -16,12 +14,9 @@ import {
|
|
|
16
14
|
candidateClaimFileInternals,
|
|
17
15
|
candidateCleanupDeadline,
|
|
18
16
|
candidateCleanupTimeout,
|
|
19
|
-
candidateWorkspaceManifest,
|
|
20
17
|
canonicalCandidateBytes,
|
|
21
18
|
canonicalCandidateDigest,
|
|
22
|
-
captureMaterializedWorkspace,
|
|
23
19
|
consumePreparedCandidateExecution,
|
|
24
|
-
deepFreezeCandidate,
|
|
25
20
|
embeddedCandidateArtifact,
|
|
26
21
|
freezeGenericAgentCandidateProfile,
|
|
27
22
|
immutableCandidateValue,
|
|
@@ -30,57 +25,39 @@ import {
|
|
|
30
25
|
parseExactCandidateProfile,
|
|
31
26
|
persistCandidateModelSettlementEvidence,
|
|
32
27
|
persistCandidateOutputArtifact,
|
|
33
|
-
|
|
28
|
+
readVerifiedArtifact,
|
|
34
29
|
recoveredTerminalRecord,
|
|
35
30
|
rejectedFinish,
|
|
36
31
|
rejectedStage,
|
|
37
32
|
requireStagedTerminal,
|
|
38
|
-
|
|
39
|
-
sealAgentCandidateBundle,
|
|
40
|
-
sealAgentCandidateExecutorFinalCapture,
|
|
33
|
+
sealAgentCandidateExecutorStopAcknowledgement,
|
|
41
34
|
sealAgentCandidateModelSettlement,
|
|
42
35
|
sealTerminalDigest,
|
|
43
|
-
sha256Bytes,
|
|
44
36
|
terminalRecord,
|
|
45
37
|
usdToNanos,
|
|
46
|
-
verifyBytes,
|
|
47
|
-
verifyMaterializedWorkspace,
|
|
48
38
|
withinCandidateCleanupDeadline
|
|
49
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-YOLKCWRV.js";
|
|
50
40
|
|
|
51
41
|
// src/candidate-execution/builder.ts
|
|
52
42
|
import { verifyCodeSurface } from "@tangle-network/agent-eval/campaign";
|
|
53
43
|
function buildAgentCandidateBundle(input) {
|
|
54
|
-
if (Object.hasOwn(input.lineage, "profileDiffIds")) {
|
|
55
|
-
throw new Error("profileDiffIds is derived from the profile source and cannot be supplied");
|
|
56
|
-
}
|
|
57
|
-
const compiledProfile = compileCandidateProfile(input.profile);
|
|
58
|
-
const profileDiffIds = compiledProfile.profileDiffIds;
|
|
59
44
|
const bundle = {
|
|
60
|
-
schemaVersion: 2,
|
|
61
45
|
kind: "agent-candidate-bundle",
|
|
62
46
|
digestAlgorithm: "rfc8785-sha256",
|
|
63
|
-
profile:
|
|
47
|
+
profile: compileCandidateProfile(input.profile),
|
|
64
48
|
code: compileCandidateCode(input.code),
|
|
65
49
|
execution: input.execution,
|
|
66
|
-
...input.knowledge ? { knowledge: input.knowledge } : {},
|
|
67
|
-
memory: input.memory
|
|
68
|
-
lineage: {
|
|
69
|
-
...input.lineage,
|
|
70
|
-
...profileDiffIds.length > 0 ? { profileDiffIds } : {}
|
|
71
|
-
}
|
|
50
|
+
...input.knowledge !== void 0 ? { knowledge: input.knowledge } : {},
|
|
51
|
+
memory: input.memory
|
|
72
52
|
};
|
|
73
53
|
return sealAgentCandidateBundle(bundle);
|
|
74
54
|
}
|
|
75
55
|
function compileCandidateProfile(source) {
|
|
76
56
|
if (source.kind === "candidate-profile") {
|
|
77
|
-
return
|
|
78
|
-
profile: parseExactCandidateProfile(source.profile),
|
|
79
|
-
profileDiffIds: []
|
|
80
|
-
};
|
|
57
|
+
return parseExactCandidateProfile(source.profile);
|
|
81
58
|
}
|
|
82
59
|
if (source.kind === "profile") {
|
|
83
|
-
return
|
|
60
|
+
return freezeGenericAgentCandidateProfile(source.profile);
|
|
84
61
|
}
|
|
85
62
|
if (source.kind !== "profile-diffs") {
|
|
86
63
|
throw new Error(
|
|
@@ -91,13 +68,11 @@ function compileCandidateProfile(source) {
|
|
|
91
68
|
throw new Error("profile-diffs source requires at least one AgentProfileDiff");
|
|
92
69
|
}
|
|
93
70
|
let profile = parseExactAgentProfile(source.base, "base profile");
|
|
94
|
-
const profileDiffIds = [];
|
|
95
71
|
for (const [index, inputDiff] of source.diffs.entries()) {
|
|
96
72
|
const diff = parseExactAgentProfileDiff(inputDiff, `profile diff ${index}`);
|
|
97
73
|
profile = applyExactAgentProfileDiff(profile, diff, `profile diff ${index}`);
|
|
98
|
-
profileDiffIds.push(canonicalCandidateDigest(diff));
|
|
99
74
|
}
|
|
100
|
-
return
|
|
75
|
+
return freezeGenericAgentCandidateProfile(profile);
|
|
101
76
|
}
|
|
102
77
|
function compileCandidateCode(source) {
|
|
103
78
|
if (source.kind === "disabled" || source.kind === "no-op") return source;
|
|
@@ -171,7 +146,6 @@ var FileAgentCandidateExecutionClaimStore = class {
|
|
|
171
146
|
if (retryFailure) return rejectedRetry(claim, retryFailure);
|
|
172
147
|
const lease = newLease(claim);
|
|
173
148
|
const acquired = await writeRecordIfAbsent(this.directory, claimPath, {
|
|
174
|
-
version: CLAIM_FORMAT_VERSION,
|
|
175
149
|
...claim,
|
|
176
150
|
phase: "claimed",
|
|
177
151
|
leaseDigest: leaseDigest(lease)
|
|
@@ -202,7 +176,6 @@ var FileAgentCandidateExecutionClaimStore = class {
|
|
|
202
176
|
this.directory,
|
|
203
177
|
this.transitionPath(stored.claim, 1),
|
|
204
178
|
{
|
|
205
|
-
version: PHASE_FORMAT_VERSION,
|
|
206
179
|
kind: "candidate-execution-phase",
|
|
207
180
|
executionId: stored.claim.executionId,
|
|
208
181
|
attempt: stored.claim.attempt,
|
|
@@ -234,7 +207,6 @@ var FileAgentCandidateExecutionClaimStore = class {
|
|
|
234
207
|
this.directory,
|
|
235
208
|
this.transitionPath(stored.claim, transition),
|
|
236
209
|
{
|
|
237
|
-
version: PENDING_FORMAT_VERSION,
|
|
238
210
|
kind: "candidate-execution-pending-terminal",
|
|
239
211
|
terminal
|
|
240
212
|
},
|
|
@@ -268,7 +240,6 @@ var FileAgentCandidateExecutionClaimStore = class {
|
|
|
268
240
|
this.directory,
|
|
269
241
|
terminalPath,
|
|
270
242
|
{
|
|
271
|
-
version: TERMINAL_FORMAT_VERSION,
|
|
272
243
|
terminal: staged
|
|
273
244
|
},
|
|
274
245
|
this.ownerPublication(stored.claim)
|
|
@@ -296,7 +267,6 @@ var FileAgentCandidateExecutionClaimStore = class {
|
|
|
296
267
|
this.directory,
|
|
297
268
|
this.transitionPath(record2.claim, transition),
|
|
298
269
|
{
|
|
299
|
-
version: PENDING_FORMAT_VERSION,
|
|
300
270
|
kind: "candidate-execution-pending-terminal",
|
|
301
271
|
terminal: recovered
|
|
302
272
|
}
|
|
@@ -316,7 +286,6 @@ var FileAgentCandidateExecutionClaimStore = class {
|
|
|
316
286
|
}
|
|
317
287
|
const terminalPath = this.terminalPath(attempt);
|
|
318
288
|
const didFinish = await writeRecordIfAbsent(this.directory, terminalPath, {
|
|
319
|
-
version: TERMINAL_FORMAT_VERSION,
|
|
320
289
|
terminal: staged
|
|
321
290
|
});
|
|
322
291
|
if (didFinish) return Object.freeze({ finished: true, terminal: staged });
|
|
@@ -605,7 +574,7 @@ function createProtectedAgentCandidateModelPort(options) {
|
|
|
605
574
|
grantDigest: request.grantDigest,
|
|
606
575
|
model: request.resolved.model
|
|
607
576
|
});
|
|
608
|
-
if (state) assertWithinReservedLimits(sealed.
|
|
577
|
+
if (state) assertWithinReservedLimits(sealed.usage, state);
|
|
609
578
|
const settlementDigest = canonicalCandidateDigest(sealed.value);
|
|
610
579
|
if (remembered?.settlementDigest && remembered.settlementDigest !== settlementDigest) {
|
|
611
580
|
throw new Error("protected model settlement retry returned a different final ledger");
|
|
@@ -819,6 +788,10 @@ function record(value, label) {
|
|
|
819
788
|
}
|
|
820
789
|
|
|
821
790
|
// src/candidate-execution/recover.ts
|
|
791
|
+
import {
|
|
792
|
+
agentCandidateExecutionPlanMaterialSchema,
|
|
793
|
+
agentCandidateMaterializationReceiptSchema
|
|
794
|
+
} from "@tangle-network/agent-interface";
|
|
822
795
|
async function recoverExpiredAgentCandidateExecution(options) {
|
|
823
796
|
const record2 = await options.claimStore.getAttempt(options.attempt);
|
|
824
797
|
if (!record2) throw new Error("candidate execution recovery attempt is missing");
|
|
@@ -836,12 +809,15 @@ async function recoverExpiredAgentCandidateExecution(options) {
|
|
|
836
809
|
throw new Error("candidate execution lease has not expired");
|
|
837
810
|
}
|
|
838
811
|
const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs);
|
|
839
|
-
const controller = new AbortController();
|
|
840
|
-
controller.abort(new Error("recovering an expired candidate execution"));
|
|
841
812
|
const recoveryTraceStore = new RecoveryAgentCandidateTraceStore(options.traceStore);
|
|
813
|
+
const preparation = withinCandidateCleanupDeadline(
|
|
814
|
+
() => readRecoveryPreparation(record2.claim, options.outputArtifacts),
|
|
815
|
+
cleanupDeadlineAtMs,
|
|
816
|
+
"expired candidate preparation evidence read"
|
|
817
|
+
);
|
|
842
818
|
const processClosure = withinCandidateCleanupDeadline(
|
|
843
|
-
async () => {
|
|
844
|
-
const stopped = await options.executor.
|
|
819
|
+
async (cleanupSignal) => {
|
|
820
|
+
const stopped = await options.executor.stop(
|
|
845
821
|
{
|
|
846
822
|
executionId: record2.claim.executionId,
|
|
847
823
|
executionPlanDigest: record2.claim.executionPlanDigest
|
|
@@ -849,11 +825,23 @@ async function recoverExpiredAgentCandidateExecution(options) {
|
|
|
849
825
|
{
|
|
850
826
|
traceStore: recoveryTraceStore,
|
|
851
827
|
reason: "failed",
|
|
852
|
-
signal:
|
|
853
|
-
deadlineAtMs:
|
|
828
|
+
signal: cleanupSignal,
|
|
829
|
+
deadlineAtMs: cleanupDeadlineAtMs
|
|
854
830
|
}
|
|
855
831
|
);
|
|
856
|
-
|
|
832
|
+
sealAgentCandidateExecutorStopAcknowledgement(stopped);
|
|
833
|
+
if (options.executor.dispose) {
|
|
834
|
+
const disposed = await options.executor.dispose(
|
|
835
|
+
{
|
|
836
|
+
executionId: record2.claim.executionId,
|
|
837
|
+
executionPlanDigest: record2.claim.executionPlanDigest
|
|
838
|
+
},
|
|
839
|
+
{ signal: cleanupSignal }
|
|
840
|
+
);
|
|
841
|
+
if (disposed.disposed !== true || Object.keys(disposed).some((key) => key !== "disposed")) {
|
|
842
|
+
throw new Error("expired candidate executor did not acknowledge resource disposal");
|
|
843
|
+
}
|
|
844
|
+
}
|
|
857
845
|
return { stopped: true };
|
|
858
846
|
},
|
|
859
847
|
cleanupDeadlineAtMs,
|
|
@@ -896,13 +884,26 @@ async function recoverExpiredAgentCandidateExecution(options) {
|
|
|
896
884
|
cleanupDeadlineAtMs,
|
|
897
885
|
"expired candidate memory closure"
|
|
898
886
|
) : void 0;
|
|
899
|
-
const
|
|
900
|
-
|
|
901
|
-
|
|
887
|
+
const closureOutcomes = Promise.allSettled([
|
|
888
|
+
modelClosure,
|
|
889
|
+
memoryClosure ?? Promise.resolve(void 0)
|
|
890
|
+
]);
|
|
891
|
+
const [preparationOutcome, processOutcome] = await Promise.allSettled([
|
|
892
|
+
preparation,
|
|
893
|
+
processClosure
|
|
894
|
+
]);
|
|
895
|
+
const failures = [preparationOutcome, processOutcome].filter((outcome) => outcome.status === "rejected").map((outcome) => outcome.reason);
|
|
896
|
+
const [modelOutcome, memoryOutcome] = await closureOutcomes;
|
|
897
|
+
for (const outcome of [modelOutcome, memoryOutcome]) {
|
|
898
|
+
if (outcome.status === "rejected") failures.push(outcome.reason);
|
|
899
|
+
}
|
|
902
900
|
if (failures.length > 0) {
|
|
903
901
|
throw new AggregateError(failures, "expired candidate cleanup could not be proven");
|
|
904
902
|
}
|
|
905
|
-
|
|
903
|
+
if (modelOutcome.status !== "fulfilled") {
|
|
904
|
+
throw new Error("expired candidate model settlement was not recovered");
|
|
905
|
+
}
|
|
906
|
+
const model = modelOutcome.value;
|
|
906
907
|
const modelSettlement = await withinCandidateCleanupDeadline(
|
|
907
908
|
() => persistCandidateModelSettlementEvidence(
|
|
908
909
|
{
|
|
@@ -916,11 +917,33 @@ async function recoverExpiredAgentCandidateExecution(options) {
|
|
|
916
917
|
cleanupDeadlineAtMs,
|
|
917
918
|
"expired candidate model-settlement persistence"
|
|
918
919
|
);
|
|
920
|
+
const failureClass = record2.phase === "claimed" && model.usage.modelCalls === 0 ? "pre-model-infrastructure" : "unknown";
|
|
921
|
+
const failureEvidence = await withinCandidateCleanupDeadline(
|
|
922
|
+
() => persistCandidateOutputArtifact(options.outputArtifacts, {
|
|
923
|
+
executionId: record2.claim.executionId,
|
|
924
|
+
purpose: "failure-evidence",
|
|
925
|
+
bytes: canonicalCandidateBytes({
|
|
926
|
+
kind: "agent-candidate-recovery-failure",
|
|
927
|
+
executionId: record2.claim.executionId,
|
|
928
|
+
attempt: record2.claim.attempt,
|
|
929
|
+
bundleDigest: record2.claim.bundleDigest,
|
|
930
|
+
executionPlanDigest: record2.claim.executionPlanDigest,
|
|
931
|
+
materializationReceiptDigest: record2.claim.preparationEvidence.materializationReceipt.sha256,
|
|
932
|
+
failureClass,
|
|
933
|
+
processStopped: true,
|
|
934
|
+
modelSettlementDigest: modelSettlement.digest,
|
|
935
|
+
memoryClosed: record2.claim.cleanup.memory !== void 0
|
|
936
|
+
})
|
|
937
|
+
}),
|
|
938
|
+
cleanupDeadlineAtMs,
|
|
939
|
+
"expired candidate failure-evidence persistence"
|
|
940
|
+
);
|
|
919
941
|
const memory = record2.claim.cleanup.memory;
|
|
920
942
|
return await options.claimStore.recoverExpired(options.attempt, {
|
|
921
|
-
failureClass
|
|
922
|
-
usage: model.
|
|
943
|
+
failureClass,
|
|
944
|
+
usage: model.usage,
|
|
923
945
|
modelSettlement: modelSettlement.artifact,
|
|
946
|
+
failureEvidence,
|
|
924
947
|
process: {
|
|
925
948
|
stopped: true,
|
|
926
949
|
executionPlanDigest: record2.claim.executionPlanDigest
|
|
@@ -940,680 +963,48 @@ async function recoverExpiredAgentCandidateExecution(options) {
|
|
|
940
963
|
} : {}
|
|
941
964
|
});
|
|
942
965
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
mkdtemp,
|
|
951
|
-
open as open2,
|
|
952
|
-
readdir,
|
|
953
|
-
readFile,
|
|
954
|
-
realpath,
|
|
955
|
-
rename,
|
|
956
|
-
rm,
|
|
957
|
-
writeFile
|
|
958
|
-
} from "fs/promises";
|
|
959
|
-
import { tmpdir } from "os";
|
|
960
|
-
import { dirname, isAbsolute, join as join2, resolve, sep, win32 } from "path";
|
|
961
|
-
import { isAnyArrayBuffer, isSharedArrayBuffer } from "util/types";
|
|
962
|
-
import { extract, pack } from "tar-stream";
|
|
963
|
-
var archiveKind = "agent-candidate-workspace-archive";
|
|
964
|
-
var workspaceEntryPrefix = "workspace/";
|
|
965
|
-
var repositoryMetadataEntry = "metadata/repository.json";
|
|
966
|
-
var repositoryBundleEntry = "metadata/repository.bundle";
|
|
967
|
-
var fixedTarTime = /* @__PURE__ */ new Date(0);
|
|
968
|
-
var defaultLimits = Object.freeze({
|
|
969
|
-
maxArchiveBytes: 512 * 1024 * 1024,
|
|
970
|
-
maxEmbeddedArtifactBytes: 1024 * 1024,
|
|
971
|
-
maxFiles: 5e4,
|
|
972
|
-
maxFileBytes: 128 * 1024 * 1024,
|
|
973
|
-
maxTotalFileBytes: 256 * 1024 * 1024,
|
|
974
|
-
maxPathBytes: 4096,
|
|
975
|
-
maxRepositoryBundleBytes: 128 * 1024 * 1024
|
|
976
|
-
});
|
|
977
|
-
async function captureAgentCandidateWorkspace(rootInput, options = {}) {
|
|
978
|
-
const root = resolve(rootInput);
|
|
979
|
-
const limits = workspaceLimits(options.limits);
|
|
980
|
-
const captured = options.includeRepository ? await captureRepository(root, limits) : await captureMaterializedWorkspace(root, {
|
|
981
|
-
limits: {
|
|
982
|
-
maxFiles: limits.maxFiles,
|
|
983
|
-
maxFileBytes: limits.maxFileBytes,
|
|
984
|
-
maxTotalFileBytes: limits.maxTotalFileBytes
|
|
985
|
-
}
|
|
986
|
-
});
|
|
987
|
-
assertWorkspaceFilesWithinLimits(captured.files, limits);
|
|
988
|
-
const repository = "repository" in captured ? captured.repository : void 0;
|
|
989
|
-
return captureWorkspaceFiles(captured.files, repository, limits, options.artifactPersistence);
|
|
990
|
-
}
|
|
991
|
-
async function captureAgentCandidateWorkspaceFiles(input, options = {}) {
|
|
992
|
-
const limits = workspaceLimits(options.limits);
|
|
993
|
-
return captureWorkspaceFiles(
|
|
994
|
-
normalizeWorkspaceFiles(input, limits),
|
|
995
|
-
void 0,
|
|
996
|
-
limits,
|
|
997
|
-
options.artifactPersistence
|
|
966
|
+
async function readRecoveryPreparation(claim, artifacts) {
|
|
967
|
+
const [planBytes, receiptBytes] = await Promise.all([
|
|
968
|
+
readVerifiedArtifact(claim.preparationEvidence.executionPlan, artifacts),
|
|
969
|
+
readVerifiedArtifact(claim.preparationEvidence.materializationReceipt, artifacts)
|
|
970
|
+
]);
|
|
971
|
+
const plan = agentCandidateExecutionPlanMaterialSchema.parse(
|
|
972
|
+
parseCanonicalBytes(planBytes, "plan")
|
|
998
973
|
);
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
const material = candidateWorkspaceManifest(files);
|
|
1002
|
-
const manifest = canonicalCandidateBytes(material);
|
|
1003
|
-
const archive = await encodeWorkspaceArchive(files, repository, limits.maxArchiveBytes);
|
|
1004
|
-
if (!artifactPersistence && (manifest.byteLength > limits.maxEmbeddedArtifactBytes || archive.byteLength > limits.maxEmbeddedArtifactBytes)) {
|
|
1005
|
-
throw new Error(
|
|
1006
|
-
"candidate workspace artifacts exceed maxEmbeddedArtifactBytes; artifactPersistence is required"
|
|
1007
|
-
);
|
|
974
|
+
if (claim.preparationEvidence.executionPlan.sha256 !== claim.executionPlanDigest) {
|
|
975
|
+
throw new Error("recovery execution plan artifact does not match the claimed digest");
|
|
1008
976
|
}
|
|
1009
|
-
const
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
kind: "agent-candidate-workspace-snapshot",
|
|
1014
|
-
digest: canonicalCandidateDigest(material),
|
|
1015
|
-
material,
|
|
1016
|
-
manifest: manifestArtifact,
|
|
1017
|
-
archive: archiveArtifact
|
|
1018
|
-
});
|
|
1019
|
-
return Object.freeze({ snapshot, archive });
|
|
1020
|
-
}
|
|
1021
|
-
async function captureWorkspaceArtifact(kind, bytes, persistence) {
|
|
1022
|
-
if (!persistence) return embeddedCandidateArtifact(bytes);
|
|
1023
|
-
if (!persistence.executionId.trim()) {
|
|
1024
|
-
throw new Error("candidate workspace artifact persistence requires an executionId");
|
|
1025
|
-
}
|
|
1026
|
-
return persistCandidateOutputArtifact(persistence.outputArtifacts, {
|
|
1027
|
-
executionId: persistence.executionId,
|
|
1028
|
-
purpose: kind === "manifest" ? "candidate-workspace-manifest" : "candidate-workspace-archive",
|
|
1029
|
-
bytes,
|
|
1030
|
-
...persistence.signal ? { signal: persistence.signal } : {}
|
|
977
|
+
const receiptMaterial = parseCanonicalBytes(receiptBytes, "materialization receipt");
|
|
978
|
+
const receipt = agentCandidateMaterializationReceiptSchema.parse({
|
|
979
|
+
...receiptMaterial,
|
|
980
|
+
digest: claim.preparationEvidence.materializationReceipt.sha256
|
|
1031
981
|
});
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
const limits = workspaceLimits(options.limits);
|
|
1035
|
-
const port = {
|
|
1036
|
-
materialize: async ({ role, snapshot, archive, destination }) => {
|
|
1037
|
-
await materializeAgentCandidateWorkspace({
|
|
1038
|
-
role,
|
|
1039
|
-
snapshot,
|
|
1040
|
-
archive,
|
|
1041
|
-
destination,
|
|
1042
|
-
limits
|
|
1043
|
-
});
|
|
1044
|
-
}
|
|
1045
|
-
};
|
|
1046
|
-
return Object.freeze(port);
|
|
1047
|
-
}
|
|
1048
|
-
async function materializeAgentCandidateWorkspace(input) {
|
|
1049
|
-
verifyBytes(
|
|
1050
|
-
input.archive,
|
|
1051
|
-
input.snapshot.archive.sha256,
|
|
1052
|
-
input.snapshot.archive.byteLength,
|
|
1053
|
-
"candidate workspace archive"
|
|
1054
|
-
);
|
|
1055
|
-
const decoded = await parseWorkspaceArchive(input.archive, input.limits);
|
|
1056
|
-
if (decoded.repository && input.role !== "task") {
|
|
1057
|
-
throw new Error("only task workspaces may carry a Git repository");
|
|
1058
|
-
}
|
|
1059
|
-
assertArchiveMatchesSnapshot(decoded.files, input.snapshot);
|
|
1060
|
-
const destination = resolve(input.destination);
|
|
1061
|
-
await prepareEmptyDestination(destination);
|
|
1062
|
-
const staging = await mkdtemp(`${destination}.materializing-`);
|
|
1063
|
-
try {
|
|
1064
|
-
await writeWorkspaceFiles(staging, decoded.files);
|
|
1065
|
-
if (decoded.repository) {
|
|
1066
|
-
await materializeRepository(decoded.repository, staging);
|
|
1067
|
-
}
|
|
1068
|
-
await verifyMaterializedWorkspace(staging, input.snapshot.material, {
|
|
1069
|
-
ignoredProtectedRootEntries: decoded.repository ? [".git"] : []
|
|
1070
|
-
});
|
|
1071
|
-
await rename(staging, destination);
|
|
1072
|
-
} catch (error) {
|
|
1073
|
-
await rm(staging, { recursive: true, force: true });
|
|
1074
|
-
await rm(destination, { recursive: true, force: true });
|
|
1075
|
-
throw error;
|
|
982
|
+
if (receipt.bundleDigest !== claim.bundleDigest || receipt.executionPlan.digest !== claim.executionPlanDigest || plan.runCell.bundleDigest !== claim.bundleDigest || plan.executionId !== claim.executionId) {
|
|
983
|
+
throw new Error("recovery preparation evidence does not match the durable claim");
|
|
1076
984
|
}
|
|
985
|
+
return { plan, receipt };
|
|
1077
986
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
if (!stats.isDirectory() || stats.isSymbolicLink() || await realpath(root) !== root) {
|
|
1081
|
-
throw new Error("candidate repository root must be a real directory");
|
|
1082
|
-
}
|
|
1083
|
-
const topLevel = (await runCandidateGit(root, ["rev-parse", "--show-toplevel"])).stdout.toString("utf8").trim();
|
|
1084
|
-
if (resolve(topLevel) !== root) {
|
|
1085
|
-
throw new Error("candidate repository capture must start at the Git worktree root");
|
|
1086
|
-
}
|
|
1087
|
-
const gitDir = resolve(
|
|
1088
|
-
(await runCandidateGit(root, ["rev-parse", "--absolute-git-dir"])).stdout.toString("utf8").trim()
|
|
1089
|
-
);
|
|
1090
|
-
if (await realpath(gitDir) !== gitDir || gitDir.includes(":")) {
|
|
1091
|
-
throw new Error("candidate repository Git object store has an unsupported path");
|
|
1092
|
-
}
|
|
1093
|
-
await assertNoGitIndirection(root, gitDir, "candidate repository");
|
|
1094
|
-
const headCommit = (await runCandidateGit(root, ["rev-parse", "HEAD"])).stdout.toString("utf8").trim();
|
|
1095
|
-
const headTree = (await runCandidateGit(root, ["rev-parse", "HEAD^{tree}"])).stdout.toString("utf8").trim();
|
|
1096
|
-
assertGitObjectId(headCommit, "HEAD");
|
|
1097
|
-
assertGitObjectId(headTree, "HEAD tree");
|
|
1098
|
-
if (headCommit.length !== headTree.length) {
|
|
1099
|
-
throw new Error("candidate repository HEAD and tree use different object formats");
|
|
1100
|
-
}
|
|
1101
|
-
const files = await readCandidateGitTreeFiles(root, headTree, {}, limits);
|
|
1102
|
-
assertWorkspaceFilesWithinLimits(files, limits);
|
|
1103
|
-
const reachableBytesText = (await runCandidateGit(root, ["rev-list", "--disk-usage", "--objects", "HEAD"])).stdout.toString("utf8").trim();
|
|
1104
|
-
const reachableBytes = Number(reachableBytesText);
|
|
1105
|
-
if (!Number.isSafeInteger(reachableBytes) || reachableBytes < 0) {
|
|
1106
|
-
throw new Error("candidate repository reachable size is invalid");
|
|
1107
|
-
}
|
|
1108
|
-
if (reachableBytes > limits.maxRepositoryBundleBytes) {
|
|
1109
|
-
throw new Error("candidate repository exceeds maxRepositoryBundleBytes");
|
|
1110
|
-
}
|
|
1111
|
-
const temporary = await mkdtemp(join2(tmpdir(), "agent-candidate-workspace-bundle-"));
|
|
1112
|
-
const bundlePath = join2(temporary, `${randomUUID2()}.bundle`);
|
|
1113
|
-
try {
|
|
1114
|
-
await runCandidateGit(root, ["bundle", "create", bundlePath, "HEAD"]);
|
|
1115
|
-
const bundle = Uint8Array.from(await readFile(bundlePath));
|
|
1116
|
-
if (bundle.byteLength > limits.maxRepositoryBundleBytes) {
|
|
1117
|
-
throw new Error("candidate repository bundle exceeds maxRepositoryBundleBytes");
|
|
1118
|
-
}
|
|
1119
|
-
return {
|
|
1120
|
-
files,
|
|
1121
|
-
repository: {
|
|
1122
|
-
headCommit,
|
|
1123
|
-
headTree,
|
|
1124
|
-
bundle: {
|
|
1125
|
-
sha256: sha256Bytes(bundle),
|
|
1126
|
-
byteLength: bundle.byteLength,
|
|
1127
|
-
bytes: bundle
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
};
|
|
1131
|
-
} finally {
|
|
1132
|
-
await rm(temporary, { recursive: true, force: true });
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
async function encodeWorkspaceArchive(files, repository, maxArchiveBytes) {
|
|
1136
|
-
const archive = pack();
|
|
1137
|
-
const output = collectStream(archive, maxArchiveBytes, "candidate workspace archive");
|
|
987
|
+
function parseCanonicalBytes(bytes, label) {
|
|
988
|
+
let parsed;
|
|
1138
989
|
try {
|
|
1139
|
-
|
|
1140
|
-
archive.finalize();
|
|
1141
|
-
return await output;
|
|
990
|
+
parsed = JSON.parse(Buffer.from(bytes).toString("utf8"));
|
|
1142
991
|
} catch (error) {
|
|
1143
|
-
|
|
1144
|
-
await output.catch(() => void 0);
|
|
1145
|
-
throw error;
|
|
992
|
+
throw new Error(`candidate recovery ${label} is not JSON`, { cause: error });
|
|
1146
993
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
const archive = pack();
|
|
1150
|
-
const comparison = streamEqualsBytes(
|
|
1151
|
-
archive,
|
|
1152
|
-
expected,
|
|
1153
|
-
maxArchiveBytes,
|
|
1154
|
-
"candidate workspace archive"
|
|
1155
|
-
);
|
|
1156
|
-
try {
|
|
1157
|
-
await writeWorkspaceArchiveEntries(archive, files, repository);
|
|
1158
|
-
archive.finalize();
|
|
1159
|
-
if (!await comparison) throw new Error("candidate workspace tar is not canonical");
|
|
1160
|
-
} catch (error) {
|
|
1161
|
-
archive.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
1162
|
-
await comparison.catch(() => void 0);
|
|
1163
|
-
throw error;
|
|
994
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
995
|
+
throw new Error(`candidate recovery ${label} must be an object`);
|
|
1164
996
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
for (const file of files) {
|
|
1168
|
-
await writeTarEntry(archive, `${workspaceEntryPrefix}${file.path}`, file.mode, file.bytes);
|
|
1169
|
-
}
|
|
1170
|
-
if (!repository) return;
|
|
1171
|
-
const metadata = canonicalCandidateBytes({
|
|
1172
|
-
schemaVersion: 1,
|
|
1173
|
-
kind: archiveKind,
|
|
1174
|
-
headCommit: repository.headCommit,
|
|
1175
|
-
headTree: repository.headTree,
|
|
1176
|
-
bundle: {
|
|
1177
|
-
sha256: repository.bundle.sha256,
|
|
1178
|
-
byteLength: repository.bundle.byteLength
|
|
1179
|
-
}
|
|
1180
|
-
});
|
|
1181
|
-
await writeTarEntry(archive, repositoryMetadataEntry, 384, metadata);
|
|
1182
|
-
await writeTarEntry(archive, repositoryBundleEntry, 384, repository.bundle.bytes);
|
|
1183
|
-
}
|
|
1184
|
-
async function parseWorkspaceArchive(bytes, limits) {
|
|
1185
|
-
if (bytes.byteLength > limits.maxArchiveBytes) {
|
|
1186
|
-
throw new Error("candidate workspace archive exceeds maxArchiveBytes");
|
|
1187
|
-
}
|
|
1188
|
-
const parser = extract();
|
|
1189
|
-
const files = [];
|
|
1190
|
-
const observedEntryNames = /* @__PURE__ */ new Set();
|
|
1191
|
-
const observedPaths = /* @__PURE__ */ new Set();
|
|
1192
|
-
let totalBytes = 0;
|
|
1193
|
-
let retainedEntryBytes = 0;
|
|
1194
|
-
let previousPath;
|
|
1195
|
-
let repositoryMetadata;
|
|
1196
|
-
let repositoryBundle;
|
|
1197
|
-
const decoded = (async () => {
|
|
1198
|
-
for await (const entry of parser) {
|
|
1199
|
-
const name = entry.header.name;
|
|
1200
|
-
if (entry.header.type !== "file" || typeof name !== "string" || observedEntryNames.has(name)) {
|
|
1201
|
-
throw new Error("candidate workspace tar contains an invalid entry");
|
|
1202
|
-
}
|
|
1203
|
-
observedEntryNames.add(name);
|
|
1204
|
-
if (name.startsWith(workspaceEntryPrefix)) {
|
|
1205
|
-
if (files.length >= limits.maxFiles) {
|
|
1206
|
-
throw new Error("candidate workspace archive has an invalid file count");
|
|
1207
|
-
}
|
|
1208
|
-
const path = safeArchivePath(name.slice(workspaceEntryPrefix.length), limits.maxPathBytes);
|
|
1209
|
-
if (!isRegularFileMode(entry.header.mode)) {
|
|
1210
|
-
throw new Error(`candidate workspace archive has an unsupported mode: ${path}`);
|
|
1211
|
-
}
|
|
1212
|
-
assertRetainedArchiveSize(bytes.byteLength, retainedEntryBytes, entry.header.size, limits);
|
|
1213
|
-
const fileBytes = await readTarEntry(entry, limits.maxFileBytes, `workspace file ${path}`);
|
|
1214
|
-
retainedEntryBytes += fileBytes.byteLength;
|
|
1215
|
-
assertWorkspacePathOrder(
|
|
1216
|
-
path,
|
|
1217
|
-
previousPath,
|
|
1218
|
-
observedPaths,
|
|
1219
|
-
"candidate workspace archive paths must be unique and sorted"
|
|
1220
|
-
);
|
|
1221
|
-
previousPath = path;
|
|
1222
|
-
if (fileBytes.byteLength > limits.maxTotalFileBytes - totalBytes) {
|
|
1223
|
-
throw new Error("candidate workspace archive exceeds maxTotalFileBytes");
|
|
1224
|
-
}
|
|
1225
|
-
totalBytes += fileBytes.byteLength;
|
|
1226
|
-
files.push({ path, mode: entry.header.mode, bytes: fileBytes });
|
|
1227
|
-
} else if (name === repositoryMetadataEntry) {
|
|
1228
|
-
if (entry.header.mode !== 384 || repositoryMetadata) {
|
|
1229
|
-
throw new Error("candidate workspace archive repository metadata is invalid");
|
|
1230
|
-
}
|
|
1231
|
-
assertRetainedArchiveSize(bytes.byteLength, retainedEntryBytes, entry.header.size, limits);
|
|
1232
|
-
const metadataBytes = await readTarEntry(
|
|
1233
|
-
entry,
|
|
1234
|
-
limits.maxPathBytes,
|
|
1235
|
-
"candidate repository metadata"
|
|
1236
|
-
);
|
|
1237
|
-
retainedEntryBytes += metadataBytes.byteLength;
|
|
1238
|
-
repositoryMetadata = parseRepositoryMetadata(metadataBytes, limits.maxRepositoryBundleBytes);
|
|
1239
|
-
} else if (name === repositoryBundleEntry) {
|
|
1240
|
-
if (entry.header.mode !== 384 || repositoryBundle) {
|
|
1241
|
-
throw new Error("candidate workspace archive repository bundle is invalid");
|
|
1242
|
-
}
|
|
1243
|
-
assertRetainedArchiveSize(bytes.byteLength, retainedEntryBytes, entry.header.size, limits);
|
|
1244
|
-
repositoryBundle = await readTarEntry(
|
|
1245
|
-
entry,
|
|
1246
|
-
limits.maxRepositoryBundleBytes,
|
|
1247
|
-
"candidate Git bundle"
|
|
1248
|
-
);
|
|
1249
|
-
retainedEntryBytes += repositoryBundle.byteLength;
|
|
1250
|
-
} else {
|
|
1251
|
-
throw new Error(`candidate workspace tar contains an unsupported entry: ${name}`);
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
if (Boolean(repositoryMetadata) !== Boolean(repositoryBundle)) {
|
|
1255
|
-
throw new Error("candidate workspace archive repository evidence is incomplete");
|
|
1256
|
-
}
|
|
1257
|
-
const repository = repositoryMetadata && repositoryBundle ? repositoryFromTar(repositoryMetadata, repositoryBundle) : void 0;
|
|
1258
|
-
return { files, ...repository ? { repository } : {} };
|
|
1259
|
-
})();
|
|
1260
|
-
parser.end(Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength));
|
|
1261
|
-
const result = await decoded;
|
|
1262
|
-
await verifyCanonicalWorkspaceArchive(
|
|
1263
|
-
result.files,
|
|
1264
|
-
result.repository,
|
|
1265
|
-
bytes,
|
|
1266
|
-
limits.maxArchiveBytes
|
|
1267
|
-
);
|
|
1268
|
-
return result;
|
|
1269
|
-
}
|
|
1270
|
-
function assertRetainedArchiveSize(archiveBytes, retainedEntryBytes, entryBytes, limits) {
|
|
1271
|
-
if (!Number.isSafeInteger(entryBytes) || entryBytes === void 0 || entryBytes < 0 || entryBytes > limits.maxArchiveBytes - archiveBytes - retainedEntryBytes) {
|
|
1272
|
-
throw new Error("candidate workspace archive exceeds maxArchiveBytes while decoding");
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
function parseRepositoryMetadata(bytes, maxBundleBytes) {
|
|
1276
|
-
let value;
|
|
1277
|
-
try {
|
|
1278
|
-
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
1279
|
-
} catch (error) {
|
|
1280
|
-
throw new Error("candidate workspace archive repository metadata is invalid", { cause: error });
|
|
997
|
+
if (!Buffer.from(canonicalCandidateBytes(parsed)).equals(Buffer.from(bytes))) {
|
|
998
|
+
throw new Error(`candidate recovery ${label} bytes are not canonical`);
|
|
1281
999
|
}
|
|
1282
|
-
|
|
1283
|
-
throw new Error("candidate workspace archive repository is invalid");
|
|
1284
|
-
}
|
|
1285
|
-
if (!Buffer.from(canonicalCandidateBytes(value)).equals(Buffer.from(bytes))) {
|
|
1286
|
-
throw new Error("candidate workspace archive repository metadata is not canonical");
|
|
1287
|
-
}
|
|
1288
|
-
assertGitObjectId(value.headCommit, "repository HEAD");
|
|
1289
|
-
assertGitObjectId(value.headTree, "repository HEAD tree");
|
|
1290
|
-
if (value.headCommit.length !== value.headTree.length) {
|
|
1291
|
-
throw new Error("candidate repository HEAD and tree use different object formats");
|
|
1292
|
-
}
|
|
1293
|
-
return {
|
|
1294
|
-
schemaVersion: 1,
|
|
1295
|
-
kind: archiveKind,
|
|
1296
|
-
headCommit: value.headCommit,
|
|
1297
|
-
headTree: value.headTree,
|
|
1298
|
-
bundle: {
|
|
1299
|
-
sha256: value.bundle.sha256,
|
|
1300
|
-
byteLength: value.bundle.byteLength
|
|
1301
|
-
}
|
|
1302
|
-
};
|
|
1303
|
-
}
|
|
1304
|
-
function repositoryFromTar(metadata, bundle) {
|
|
1305
|
-
verifyBytes(bundle, metadata.bundle.sha256, metadata.bundle.byteLength, "candidate Git bundle");
|
|
1306
|
-
return {
|
|
1307
|
-
headCommit: metadata.headCommit,
|
|
1308
|
-
headTree: metadata.headTree,
|
|
1309
|
-
bundle: { ...metadata.bundle, bytes: bundle }
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
async function writeTarEntry(archive, name, mode, bytes) {
|
|
1313
|
-
await new Promise((resolveEntry, rejectEntry) => {
|
|
1314
|
-
archive.entry(
|
|
1315
|
-
{
|
|
1316
|
-
name,
|
|
1317
|
-
mode,
|
|
1318
|
-
uid: 0,
|
|
1319
|
-
gid: 0,
|
|
1320
|
-
size: bytes.byteLength,
|
|
1321
|
-
mtime: fixedTarTime,
|
|
1322
|
-
type: "file",
|
|
1323
|
-
uname: "",
|
|
1324
|
-
gname: ""
|
|
1325
|
-
},
|
|
1326
|
-
Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength),
|
|
1327
|
-
(error) => error ? rejectEntry(error) : resolveEntry()
|
|
1328
|
-
);
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1331
|
-
async function readTarEntry(entry, maxBytes, label) {
|
|
1332
|
-
const size = entry.header.size;
|
|
1333
|
-
if (!Number.isSafeInteger(size) || size === void 0 || size < 0 || size > maxBytes) {
|
|
1334
|
-
throw new Error(`${label} has an invalid tar size`);
|
|
1335
|
-
}
|
|
1336
|
-
const bytes = await collectStream(entry, maxBytes, label);
|
|
1337
|
-
if (bytes.byteLength !== size) throw new Error(`${label} differs from its tar size`);
|
|
1338
|
-
return bytes;
|
|
1339
|
-
}
|
|
1340
|
-
async function collectStream(stream, maxBytes, label) {
|
|
1341
|
-
const chunks = [];
|
|
1342
|
-
let totalBytes = 0;
|
|
1343
|
-
for await (const chunk of stream) {
|
|
1344
|
-
if (chunk.byteLength > maxBytes - totalBytes) {
|
|
1345
|
-
const error = new Error(`${label} exceeds its size limit`);
|
|
1346
|
-
if ("destroy" in stream && typeof stream.destroy === "function") stream.destroy(error);
|
|
1347
|
-
throw error;
|
|
1348
|
-
}
|
|
1349
|
-
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1350
|
-
chunks.push(bytes);
|
|
1351
|
-
totalBytes += bytes.byteLength;
|
|
1352
|
-
}
|
|
1353
|
-
return Buffer.concat(chunks, totalBytes);
|
|
1354
|
-
}
|
|
1355
|
-
async function streamEqualsBytes(stream, expected, maxBytes, label) {
|
|
1356
|
-
const expectedBuffer = Buffer.from(expected.buffer, expected.byteOffset, expected.byteLength);
|
|
1357
|
-
let equal = true;
|
|
1358
|
-
let offset = 0;
|
|
1359
|
-
for await (const chunk of stream) {
|
|
1360
|
-
if (chunk.byteLength > maxBytes - offset) {
|
|
1361
|
-
const error = new Error(`${label} exceeds its size limit`);
|
|
1362
|
-
if ("destroy" in stream && typeof stream.destroy === "function") stream.destroy(error);
|
|
1363
|
-
throw error;
|
|
1364
|
-
}
|
|
1365
|
-
const observed = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
1366
|
-
if (offset + observed.byteLength > expectedBuffer.byteLength || !observed.equals(expectedBuffer.subarray(offset, offset + observed.byteLength))) {
|
|
1367
|
-
equal = false;
|
|
1368
|
-
}
|
|
1369
|
-
offset += observed.byteLength;
|
|
1370
|
-
}
|
|
1371
|
-
return equal && offset === expectedBuffer.byteLength;
|
|
1372
|
-
}
|
|
1373
|
-
async function prepareEmptyDestination(destination) {
|
|
1374
|
-
await mkdir2(destination, { recursive: true, mode: 448 });
|
|
1375
|
-
const stats = await lstat(destination);
|
|
1376
|
-
if (!stats.isDirectory() || stats.isSymbolicLink() || await realpath(destination) !== destination) {
|
|
1377
|
-
throw new Error("candidate workspace destination must be a real directory");
|
|
1378
|
-
}
|
|
1379
|
-
if ((await readdir(destination)).length > 0) {
|
|
1380
|
-
throw new Error("candidate workspace destination must be empty");
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
async function writeWorkspaceFiles(destination, files) {
|
|
1384
|
-
for (const file of files) {
|
|
1385
|
-
const path = workspacePath(destination, file.path);
|
|
1386
|
-
await mkdir2(dirname(path), { recursive: true, mode: 448 });
|
|
1387
|
-
const descriptor = await open2(
|
|
1388
|
-
path,
|
|
1389
|
-
fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | (typeof fsConstants.O_NOFOLLOW === "number" ? fsConstants.O_NOFOLLOW : 0),
|
|
1390
|
-
384
|
|
1391
|
-
);
|
|
1392
|
-
try {
|
|
1393
|
-
await descriptor.writeFile(file.bytes);
|
|
1394
|
-
const stats = await descriptor.stat();
|
|
1395
|
-
if (!stats.isFile() || stats.nlink !== 1) {
|
|
1396
|
-
throw new Error(`candidate workspace file identity changed while writing: ${file.path}`);
|
|
1397
|
-
}
|
|
1398
|
-
await descriptor.chmod(file.mode);
|
|
1399
|
-
} finally {
|
|
1400
|
-
await descriptor.close();
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
async function materializeRepository(repository, destination) {
|
|
1405
|
-
const temporary = await mkdtemp(join2(tmpdir(), "agent-candidate-workspace-bundle-"));
|
|
1406
|
-
const bundlePath = join2(temporary, `${randomUUID2()}.bundle`);
|
|
1407
|
-
try {
|
|
1408
|
-
await writeFile(bundlePath, repository.bundle.bytes, { flag: "wx", mode: 384 });
|
|
1409
|
-
await runCandidateGit(destination, [
|
|
1410
|
-
"init",
|
|
1411
|
-
"--quiet",
|
|
1412
|
-
"--initial-branch=candidate",
|
|
1413
|
-
`--object-format=${repository.headCommit.length === 64 ? "sha256" : "sha1"}`
|
|
1414
|
-
]);
|
|
1415
|
-
await runCandidateGit(destination, ["bundle", "verify", bundlePath]);
|
|
1416
|
-
await runCandidateGit(destination, ["bundle", "unbundle", bundlePath]);
|
|
1417
|
-
const objectType = (await runCandidateGit(destination, ["cat-file", "-t", repository.headCommit])).stdout.toString("utf8").trim();
|
|
1418
|
-
if (objectType !== "commit") throw new Error("candidate repository HEAD is not a commit");
|
|
1419
|
-
const observedTree = (await runCandidateGit(destination, ["rev-parse", `${repository.headCommit}^{tree}`])).stdout.toString("utf8").trim();
|
|
1420
|
-
if (observedTree !== repository.headTree) {
|
|
1421
|
-
throw new Error("candidate repository bundle HEAD tree changed");
|
|
1422
|
-
}
|
|
1423
|
-
await runCandidateGit(destination, ["update-ref", "--no-deref", "HEAD", repository.headCommit]);
|
|
1424
|
-
await runCandidateGit(destination, ["read-tree", repository.headTree]);
|
|
1425
|
-
const status = (await runCandidateGit(destination, ["status", "--porcelain=v1", "--untracked-files=all"])).stdout.toString("utf8").trim();
|
|
1426
|
-
if (status) throw new Error("candidate repository files do not exactly match HEAD");
|
|
1427
|
-
} finally {
|
|
1428
|
-
await rm(temporary, { recursive: true, force: true });
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
function assertArchiveMatchesSnapshot(files, snapshot) {
|
|
1432
|
-
const material = candidateWorkspaceManifest(files);
|
|
1433
|
-
const materialBytes = canonicalCandidateBytes(material);
|
|
1434
|
-
verifyBytes(
|
|
1435
|
-
materialBytes,
|
|
1436
|
-
snapshot.manifest.sha256,
|
|
1437
|
-
snapshot.manifest.byteLength,
|
|
1438
|
-
"candidate workspace manifest"
|
|
1439
|
-
);
|
|
1440
|
-
if (canonicalCandidateDigest(material) !== snapshot.digest || !Buffer.from(materialBytes).equals(Buffer.from(canonicalCandidateBytes(snapshot.material)))) {
|
|
1441
|
-
throw new Error("candidate workspace archive does not match its snapshot manifest");
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
function assertWorkspaceFilesWithinLimits(files, limits) {
|
|
1445
|
-
if (files.length > limits.maxFiles) {
|
|
1446
|
-
throw new Error("candidate workspace exceeds maxFiles");
|
|
1447
|
-
}
|
|
1448
|
-
let totalBytes = 0;
|
|
1449
|
-
let previousPath;
|
|
1450
|
-
const observedPaths = /* @__PURE__ */ new Set();
|
|
1451
|
-
for (const file of files) {
|
|
1452
|
-
const path = safeArchivePath(file.path, limits.maxPathBytes);
|
|
1453
|
-
assertWorkspacePathOrder(
|
|
1454
|
-
path,
|
|
1455
|
-
previousPath,
|
|
1456
|
-
observedPaths,
|
|
1457
|
-
"candidate workspace paths must be unique and sorted"
|
|
1458
|
-
);
|
|
1459
|
-
previousPath = path;
|
|
1460
|
-
if (!isRegularFileMode(file.mode)) {
|
|
1461
|
-
throw new Error(`candidate workspace file has unsupported mode: ${path}`);
|
|
1462
|
-
}
|
|
1463
|
-
if (file.bytes.byteLength > limits.maxFileBytes) {
|
|
1464
|
-
throw new Error(`candidate workspace file exceeds maxFileBytes: ${path}`);
|
|
1465
|
-
}
|
|
1466
|
-
totalBytes += file.bytes.byteLength;
|
|
1467
|
-
if (totalBytes > limits.maxTotalFileBytes) {
|
|
1468
|
-
throw new Error("candidate workspace exceeds maxTotalFileBytes");
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
function normalizeWorkspaceFiles(input, limits) {
|
|
1473
|
-
if (!Array.isArray(input)) {
|
|
1474
|
-
throw new Error("candidate workspace files must be an array");
|
|
1475
|
-
}
|
|
1476
|
-
const fileCount = input.length;
|
|
1477
|
-
if (fileCount > limits.maxFiles) {
|
|
1478
|
-
throw new Error("candidate workspace exceeds maxFiles");
|
|
1479
|
-
}
|
|
1480
|
-
let totalBytes = 0;
|
|
1481
|
-
const files = [];
|
|
1482
|
-
for (let index = 0; index < fileCount; index++) {
|
|
1483
|
-
const inputFile = input[index];
|
|
1484
|
-
if (!isRecord(inputFile) || Object.keys(inputFile).sort().join(",") !== "bytes,mode,path") {
|
|
1485
|
-
throw new Error(`candidate workspace file ${index} is invalid`);
|
|
1486
|
-
}
|
|
1487
|
-
const descriptors = Object.getOwnPropertyDescriptors(inputFile);
|
|
1488
|
-
if (!("value" in (descriptors.path ?? {})) || !("value" in (descriptors.mode ?? {})) || !("value" in (descriptors.bytes ?? {}))) {
|
|
1489
|
-
throw new Error(`candidate workspace file ${index} must use fixed values`);
|
|
1490
|
-
}
|
|
1491
|
-
const path = safeArchivePath(descriptors.path?.value, limits.maxPathBytes);
|
|
1492
|
-
const mode = descriptors.mode?.value;
|
|
1493
|
-
const inputBytes = descriptors.bytes?.value;
|
|
1494
|
-
if (!isRegularFileMode(mode)) {
|
|
1495
|
-
throw new Error(`candidate workspace file has unsupported mode: ${path}`);
|
|
1496
|
-
}
|
|
1497
|
-
const view = inspectWorkspaceBytes(inputBytes, path);
|
|
1498
|
-
if (view.byteLength > limits.maxFileBytes) {
|
|
1499
|
-
throw new Error(`candidate workspace file exceeds maxFileBytes: ${path}`);
|
|
1500
|
-
}
|
|
1501
|
-
if (view.byteLength > limits.maxTotalFileBytes - totalBytes) {
|
|
1502
|
-
throw new Error("candidate workspace exceeds maxTotalFileBytes");
|
|
1503
|
-
}
|
|
1504
|
-
if (view.byteLength > Math.max(0, limits.maxArchiveBytes - 1024)) {
|
|
1505
|
-
throw new Error("candidate workspace archive exceeds maxArchiveBytes");
|
|
1506
|
-
}
|
|
1507
|
-
totalBytes += view.byteLength;
|
|
1508
|
-
files.push({ path, mode, bytes: detachWorkspaceBytes(view) });
|
|
1509
|
-
}
|
|
1510
|
-
files.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
1511
|
-
assertWorkspaceFilesWithinLimits(files, limits);
|
|
1512
|
-
return files;
|
|
1513
|
-
}
|
|
1514
|
-
function isRegularFileMode(value) {
|
|
1515
|
-
return Number.isInteger(value) && Number(value) >= 0 && Number(value) <= 511;
|
|
1516
|
-
}
|
|
1517
|
-
function workspaceLimits(overrides) {
|
|
1518
|
-
const limits = { ...defaultLimits, ...overrides };
|
|
1519
|
-
for (const [name, value] of Object.entries(limits)) {
|
|
1520
|
-
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
1521
|
-
throw new Error(`candidate workspace ${name} must be a positive safe integer`);
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
return Object.freeze(limits);
|
|
1525
|
-
}
|
|
1526
|
-
function safeArchivePath(value, maxPathBytes) {
|
|
1527
|
-
if (typeof value !== "string" || !value || !isWellFormedUnicode(value) || Buffer.byteLength(value, "utf8") > maxPathBytes || isAbsolute(value) || win32.isAbsolute(value) || value.includes("\\") || value.includes("\0") || hasControlCharacter(value) || value.split("/").some((part) => !part || part === "." || part === "..") || [".git", ".sidecar"].includes(value.split("/")[0]?.toLowerCase() ?? "")) {
|
|
1528
|
-
throw new Error(`candidate workspace archive contains an unsafe path: ${String(value)}`);
|
|
1529
|
-
}
|
|
1530
|
-
return value;
|
|
1531
|
-
}
|
|
1532
|
-
function assertWorkspacePathOrder(path, previousPath, observedPaths, errorMessage) {
|
|
1533
|
-
if (previousPath !== void 0 && previousPath >= path) throw new Error(errorMessage);
|
|
1534
|
-
for (let slash = path.indexOf("/"); slash !== -1; slash = path.indexOf("/", slash + 1)) {
|
|
1535
|
-
if (observedPaths.has(path.slice(0, slash))) throw new Error(errorMessage);
|
|
1536
|
-
}
|
|
1537
|
-
observedPaths.add(path);
|
|
1538
|
-
}
|
|
1539
|
-
function inspectWorkspaceBytes(input, path) {
|
|
1540
|
-
let buffer;
|
|
1541
|
-
let byteOffset;
|
|
1542
|
-
let byteLength;
|
|
1543
|
-
try {
|
|
1544
|
-
buffer = typedArrayBufferGetter.call(input);
|
|
1545
|
-
byteOffset = typedArrayByteOffsetGetter.call(input);
|
|
1546
|
-
byteLength = typedArrayByteLengthGetter.call(input);
|
|
1547
|
-
} catch {
|
|
1548
|
-
throw new Error(`candidate workspace file bytes are invalid: ${path}`);
|
|
1549
|
-
}
|
|
1550
|
-
if (isSharedArrayBuffer(buffer)) {
|
|
1551
|
-
throw new Error(`candidate workspace file uses shared bytes: ${path}`);
|
|
1552
|
-
}
|
|
1553
|
-
if (!isAnyArrayBuffer(buffer)) {
|
|
1554
|
-
throw new Error(`candidate workspace file bytes are invalid: ${path}`);
|
|
1555
|
-
}
|
|
1556
|
-
return { buffer, byteOffset, byteLength };
|
|
1557
|
-
}
|
|
1558
|
-
function detachWorkspaceBytes(input) {
|
|
1559
|
-
const { buffer, byteOffset, byteLength } = input;
|
|
1560
|
-
const copy = new Uint8Array(byteLength);
|
|
1561
|
-
Uint8Array.prototype.set.call(copy, new Uint8Array(buffer, byteOffset, byteLength));
|
|
1562
|
-
return copy;
|
|
1563
|
-
}
|
|
1564
|
-
function isWellFormedUnicode(value) {
|
|
1565
|
-
for (let index = 0; index < value.length; index++) {
|
|
1566
|
-
const code = value.charCodeAt(index);
|
|
1567
|
-
if (code >= 55296 && code <= 56319) {
|
|
1568
|
-
const next = value.charCodeAt(index + 1);
|
|
1569
|
-
if (!(next >= 56320 && next <= 57343)) return false;
|
|
1570
|
-
index++;
|
|
1571
|
-
} else if (code >= 56320 && code <= 57343) {
|
|
1572
|
-
return false;
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
return true;
|
|
1576
|
-
}
|
|
1577
|
-
function workspacePath(root, relativePath) {
|
|
1578
|
-
const path = resolve(root, relativePath);
|
|
1579
|
-
if (!path.startsWith(`${root}${sep}`)) {
|
|
1580
|
-
throw new Error(`candidate workspace archive path escapes its destination: ${relativePath}`);
|
|
1581
|
-
}
|
|
1582
|
-
return path;
|
|
1583
|
-
}
|
|
1584
|
-
function assertGitObjectId(value, label) {
|
|
1585
|
-
if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(value)) {
|
|
1586
|
-
throw new Error(`candidate repository ${label} is not a Git object id`);
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
function hasControlCharacter(value) {
|
|
1590
|
-
for (let index = 0; index < value.length; index++) {
|
|
1591
|
-
const code = value.charCodeAt(index);
|
|
1592
|
-
if (code < 32 || code === 127) return true;
|
|
1593
|
-
}
|
|
1594
|
-
return false;
|
|
1595
|
-
}
|
|
1596
|
-
function isRecord(value) {
|
|
1597
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1598
|
-
}
|
|
1599
|
-
function requireTypedArrayGetter(name) {
|
|
1600
|
-
const typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
|
|
1601
|
-
const getter = Object.getOwnPropertyDescriptor(typedArrayPrototype, name)?.get;
|
|
1602
|
-
if (!getter) throw new Error(`typed array intrinsic ${name} accessor is unavailable`);
|
|
1603
|
-
return getter;
|
|
1000
|
+
return parsed;
|
|
1604
1001
|
}
|
|
1605
|
-
var typedArrayBufferGetter = requireTypedArrayGetter("buffer");
|
|
1606
|
-
var typedArrayByteOffsetGetter = requireTypedArrayGetter("byteOffset");
|
|
1607
|
-
var typedArrayByteLengthGetter = requireTypedArrayGetter("byteLength");
|
|
1608
1002
|
|
|
1609
1003
|
export {
|
|
1610
1004
|
buildAgentCandidateBundle,
|
|
1611
1005
|
FileAgentCandidateExecutionClaimStore,
|
|
1612
1006
|
disposePreparedAgentCandidateExecution,
|
|
1613
1007
|
createProtectedAgentCandidateModelPort,
|
|
1614
|
-
recoverExpiredAgentCandidateExecution
|
|
1615
|
-
captureAgentCandidateWorkspace,
|
|
1616
|
-
captureAgentCandidateWorkspaceFiles,
|
|
1617
|
-
createAgentCandidateWorkspacePort
|
|
1008
|
+
recoverExpiredAgentCandidateExecution
|
|
1618
1009
|
};
|
|
1619
|
-
//# sourceMappingURL=chunk-
|
|
1010
|
+
//# sourceMappingURL=chunk-6XKPVJAZ.js.map
|