@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.
Files changed (110) hide show
  1. package/README.md +64 -15
  2. package/dist/activation-B0ZD7nfX.d.ts +63 -0
  3. package/dist/agent.d.ts +6 -193
  4. package/dist/agent.js +10 -234
  5. package/dist/agent.js.map +1 -1
  6. package/dist/analyst-loop.d.ts +7 -10
  7. package/dist/analyst-loop.js +1 -2
  8. package/dist/candidate-execution/index.d.ts +43 -16
  9. package/dist/candidate-execution/index.js +17 -8
  10. package/dist/{chunk-VYA2YEKA.js → chunk-2KGAN2HM.js} +83 -14
  11. package/dist/chunk-2KGAN2HM.js.map +1 -0
  12. package/dist/chunk-3XKSBI2U.js +474 -0
  13. package/dist/chunk-3XKSBI2U.js.map +1 -0
  14. package/dist/{chunk-TVJQAYQM.js → chunk-6XKPVJAZ.js} +107 -716
  15. package/dist/chunk-6XKPVJAZ.js.map +1 -0
  16. package/dist/chunk-BLQIYRVR.js +699 -0
  17. package/dist/chunk-BLQIYRVR.js.map +1 -0
  18. package/dist/{chunk-QDSOD7RC.js → chunk-FD2MBMOH.js} +13 -101
  19. package/dist/chunk-FD2MBMOH.js.map +1 -0
  20. package/dist/{chunk-U33YZ7B2.js → chunk-FXF2OL34.js} +8 -8
  21. package/dist/{chunk-EP6RVHMX.js → chunk-HZDEXTSL.js} +848 -2
  22. package/dist/chunk-HZDEXTSL.js.map +1 -0
  23. package/dist/{chunk-WIPGQ4GT.js → chunk-IMSNJSXH.js} +1 -1
  24. package/dist/{chunk-WIPGQ4GT.js.map → chunk-IMSNJSXH.js.map} +1 -1
  25. package/dist/{chunk-VSWBYWFK.js → chunk-M6MD6JBS.js} +20 -27
  26. package/dist/chunk-M6MD6JBS.js.map +1 -0
  27. package/dist/chunk-PSOCBNM3.js +2069 -0
  28. package/dist/chunk-PSOCBNM3.js.map +1 -0
  29. package/dist/{chunk-AEG3NGJ2.js → chunk-Q2JSAVQ3.js} +34 -2
  30. package/dist/chunk-Q2JSAVQ3.js.map +1 -0
  31. package/dist/{chunk-XP5KDM3R.js → chunk-SGQ4YIQW.js} +4 -4
  32. package/dist/{chunk-C3UKLQ54.js → chunk-UQ6PNNXM.js} +18 -10
  33. package/dist/chunk-UQ6PNNXM.js.map +1 -0
  34. package/dist/{chunk-33OG2NN3.js → chunk-WYC2XJF2.js} +2 -2
  35. package/dist/{chunk-ZEYAT33L.js → chunk-Y3SRWZMP.js} +2 -2
  36. package/dist/{chunk-CNH7DF7Z.js → chunk-YOLKCWRV.js} +1116 -591
  37. package/dist/chunk-YOLKCWRV.js.map +1 -0
  38. package/dist/{completion-gate-D1gX1-hg.d.ts → completion-gate-C80jiRfN.d.ts} +1 -1
  39. package/dist/conversation.d.ts +12 -1
  40. package/dist/conversation.js +2 -3
  41. package/dist/{coordination-Dr_axlAf.d.ts → coordination-BFE3Den7.d.ts} +10 -11
  42. package/dist/environment-provider.d.ts +2 -2
  43. package/dist/environment-provider.js +1 -2
  44. package/dist/{agentic-generator-DDMM45kZ.d.ts → improve-g75IE2Cx.d.ts} +152 -4
  45. package/dist/{improvement-adapter-BieWeK5J.d.ts → improvement-adapter-HAZz-7vK.d.ts} +8 -31
  46. package/dist/index.d.ts +55 -28
  47. package/dist/index.js +206 -82
  48. package/dist/index.js.map +1 -1
  49. package/dist/intelligence.d.ts +185 -120
  50. package/dist/intelligence.js +509 -345
  51. package/dist/intelligence.js.map +1 -1
  52. package/dist/knowledge.d.ts +40 -12
  53. package/dist/knowledge.js +13 -7
  54. package/dist/{loop-runner-bin-BRQSQdHa.d.ts → loop-runner-bin-Cn1N2rRo.d.ts} +3 -3
  55. package/dist/loop-runner-bin.d.ts +6 -6
  56. package/dist/loop-runner-bin.js +8 -10
  57. package/dist/loops.d.ts +13 -13
  58. package/dist/loops.js +6 -8
  59. package/dist/mcp/bin.js +5 -7
  60. package/dist/mcp/bin.js.map +1 -1
  61. package/dist/mcp/index.d.ts +6 -6
  62. package/dist/mcp/index.js +12 -14
  63. package/dist/mcp/index.js.map +1 -1
  64. package/dist/platform.js +0 -2
  65. package/dist/platform.js.map +1 -1
  66. package/dist/primeintellect/index.js +1 -2
  67. package/dist/primeintellect/index.js.map +1 -1
  68. package/dist/profile-DbfaMTdk.d.ts +233 -0
  69. package/dist/profiles.d.ts +1 -1
  70. package/dist/profiles.js +0 -1
  71. package/dist/profiles.js.map +1 -1
  72. package/dist/{supervise-DmYOug5f.d.ts → supervise-BLPI50-w.d.ts} +3 -3
  73. package/dist/{types-ByAYqlVb.d.ts → types-B3vAW0Oq.d.ts} +1 -1
  74. package/dist/{prepare-CtdtsFNG.d.ts → types-CWqfCO8s.d.ts} +67 -298
  75. package/dist/{types-BC3bZpH0.d.ts → types-CmYCMbFT.d.ts} +12 -54
  76. package/dist/{types-1d5QGK3t.d.ts → types-CmnA2iL3.d.ts} +3 -3
  77. package/dist/{worktree-fanout-CPprU-qI.d.ts → worktree-fanout-DCA3G4bO.d.ts} +3 -3
  78. package/package.json +14 -16
  79. package/skills/build-with-agent-runtime/SKILL.md +122 -213
  80. package/dist/chunk-6O73TRHW.js +0 -142
  81. package/dist/chunk-6O73TRHW.js.map +0 -1
  82. package/dist/chunk-AEG3NGJ2.js.map +0 -1
  83. package/dist/chunk-C3UKLQ54.js.map +0 -1
  84. package/dist/chunk-CNH7DF7Z.js.map +0 -1
  85. package/dist/chunk-D3H7F6L2.js +0 -626
  86. package/dist/chunk-D3H7F6L2.js.map +0 -1
  87. package/dist/chunk-DGUM43GV.js +0 -11
  88. package/dist/chunk-DGUM43GV.js.map +0 -1
  89. package/dist/chunk-EP6RVHMX.js.map +0 -1
  90. package/dist/chunk-HGRW27YY.js +0 -214
  91. package/dist/chunk-HGRW27YY.js.map +0 -1
  92. package/dist/chunk-ISTDY47H.js +0 -849
  93. package/dist/chunk-ISTDY47H.js.map +0 -1
  94. package/dist/chunk-PCURO3DL.js +0 -661
  95. package/dist/chunk-PCURO3DL.js.map +0 -1
  96. package/dist/chunk-QDSOD7RC.js.map +0 -1
  97. package/dist/chunk-TVJQAYQM.js.map +0 -1
  98. package/dist/chunk-VSWBYWFK.js.map +0 -1
  99. package/dist/chunk-VYA2YEKA.js.map +0 -1
  100. package/dist/generator-YkAQrOoD.d.ts +0 -382
  101. package/dist/improve-BN3HyXIO.d.ts +0 -172
  102. package/dist/lifecycle.d.ts +0 -870
  103. package/dist/lifecycle.js +0 -981
  104. package/dist/lifecycle.js.map +0 -1
  105. package/dist/mcp-serve-verifier-DQQDbuyz.d.ts +0 -34
  106. package/skills/agent-runtime-adoption/SKILL.md +0 -246
  107. /package/dist/{chunk-U33YZ7B2.js.map → chunk-FXF2OL34.js.map} +0 -0
  108. /package/dist/{chunk-XP5KDM3R.js.map → chunk-SGQ4YIQW.js.map} +0 -0
  109. /package/dist/{chunk-33OG2NN3.js.map → chunk-WYC2XJF2.js.map} +0 -0
  110. /package/dist/{chunk-ZEYAT33L.js.map → chunk-Y3SRWZMP.js.map} +0 -0
@@ -1,9 +1,15 @@
1
1
  import {
2
2
  contentAddress
3
- } from "./chunk-WIPGQ4GT.js";
3
+ } from "./chunk-IMSNJSXH.js";
4
4
 
5
- // src/candidate-execution/bundle.ts
6
- import { agentCandidateBundleSchema } from "@tangle-network/agent-interface";
5
+ // src/candidate-execution/profile.ts
6
+ import {
7
+ agentCandidateProfileActivationSchema,
8
+ agentCandidateProfileSchema,
9
+ agentProfileDiffSchema,
10
+ agentProfileSchema,
11
+ applyAgentProfileDiff
12
+ } from "@tangle-network/agent-interface";
7
13
 
8
14
  // src/candidate-execution/digest.ts
9
15
  import { createHash } from "crypto";
@@ -38,6 +44,12 @@ function canonicalCandidateDocument(valueWithoutDigest) {
38
44
  digest
39
45
  });
40
46
  }
47
+ function verifyCanonicalCandidateDocument(value, label) {
48
+ if (canonicalCandidateDigest(omitTopLevelDigest(value)) !== value.digest) {
49
+ throw new Error(`${label} digest does not match`);
50
+ }
51
+ return immutableCandidateValue(value);
52
+ }
41
53
  function embeddedCandidateArtifact(bytes) {
42
54
  return {
43
55
  encoding: "base64",
@@ -61,24 +73,69 @@ function deepFreezeCandidate(value, seen = /* @__PURE__ */ new Set()) {
61
73
  return Object.freeze(value);
62
74
  }
63
75
 
64
- // src/candidate-execution/bundle.ts
65
- function sealAgentCandidateBundle(input) {
66
- const digest = canonicalCandidateDigest(input);
67
- const parsed = agentCandidateBundleSchema.parse({ ...input, digest });
68
- const parsedDigest = canonicalCandidateDigest(omitTopLevelDigest(parsed));
69
- if (parsedDigest !== digest) {
70
- throw new Error("candidate bundle changed while validating its canonical wire shape");
76
+ // src/candidate-execution/profile.ts
77
+ var MATERIALIZER_HARNESSES = /* @__PURE__ */ new Set([
78
+ "claude-code",
79
+ "claude",
80
+ "claudish",
81
+ "nanoclaw",
82
+ "codex",
83
+ "opencode",
84
+ "kimi-code",
85
+ "kimi",
86
+ "pi",
87
+ "gemini",
88
+ "hermes",
89
+ "openclaw"
90
+ ]);
91
+ function candidateMaterializerHarness(harness) {
92
+ if (!MATERIALIZER_HARNESSES.has(harness)) {
93
+ throw new Error(
94
+ `sealed candidate profile materialization is unsupported for harness ${harness}`
95
+ );
71
96
  }
72
- return immutableCandidateValue(parsed);
97
+ return harness;
98
+ }
99
+ function createAgentCandidateProfileActivation(plan, profilePlan) {
100
+ const sourceByPath = new Map(plan.files.map((file) => [file.relPath, file]));
101
+ if (sourceByPath.size !== plan.files.length) {
102
+ throw new Error("candidate profile activation contains duplicate native file paths");
103
+ }
104
+ const files = profilePlan.material.files.map((expected) => {
105
+ const source = sourceByPath.get(expected.relPath);
106
+ if (!source) {
107
+ throw new Error(`candidate profile activation is missing ${expected.relPath}`);
108
+ }
109
+ const mode = source.mode ?? 420;
110
+ if (!Number.isSafeInteger(mode) || mode < 0 || mode > 511) {
111
+ throw new Error(`candidate profile activation has invalid mode for ${expected.relPath}`);
112
+ }
113
+ return { path: expected.relPath, mode, content: source.content };
114
+ });
115
+ if (files.length !== plan.files.length) {
116
+ throw new Error("candidate profile activation contains unplanned native files");
117
+ }
118
+ return parseAgentCandidateProfileActivation(
119
+ canonicalCandidateDocument({
120
+ kind: "agent-candidate-profile-activation",
121
+ profilePlan,
122
+ files
123
+ }).value,
124
+ profilePlan.digest
125
+ );
126
+ }
127
+ function parseAgentCandidateProfileActivation(input, expectedProfilePlanDigest) {
128
+ const activation = agentCandidateProfileActivationSchema.parse(input);
129
+ const planBytes = canonicalCandidateBytes(activation.profilePlan.material);
130
+ const profilePlanArtifact = activation.profilePlan.artifact;
131
+ if (sha256Bytes(planBytes) !== activation.profilePlan.digest || profilePlanArtifact.sha256 !== activation.profilePlan.digest || profilePlanArtifact.byteLength !== planBytes.byteLength || "content" in profilePlanArtifact && !Buffer.from(profilePlanArtifact.content, "base64").equals(Buffer.from(planBytes)) || expectedProfilePlanDigest !== void 0 && activation.profilePlan.digest !== expectedProfilePlanDigest) {
132
+ throw new Error("candidate profile activation has an invalid canonical profile plan");
133
+ }
134
+ if (canonicalCandidateDigest(omitTopLevelDigest(activation)) !== activation.digest) {
135
+ throw new Error("candidate profile activation digest does not match");
136
+ }
137
+ return immutableCandidateValue(activation);
73
138
  }
74
-
75
- // src/candidate-execution/profile.ts
76
- import {
77
- agentCandidateProfileSchema,
78
- agentProfileDiffSchema,
79
- agentProfileSchema,
80
- applyAgentProfileDiff
81
- } from "@tangle-network/agent-interface";
82
139
  var CANDIDATE_PROFILE_DIRECT_FIELDS = [
83
140
  "name",
84
141
  "description",
@@ -137,18 +194,28 @@ function assertCandidateProfileBinding(measuredInput, bundled) {
137
194
  if (measured.connections || measured.metadata || measured.extensions) {
138
195
  throw new Error("proposal candidate profile contains fields unsupported by sealed candidates");
139
196
  }
140
- const normalized = candidateProfileAsAgentProfile(bundled);
197
+ const normalized = agentCandidateProfileAsAgentProfile(bundled);
141
198
  if (canonicalCandidateDigest(measured) !== canonicalCandidateDigest(normalized)) {
142
199
  throw new Error("proposal candidateProfile does not match candidateBundle.profile");
143
200
  }
144
201
  }
145
202
  function parseExactAgentProfile(input, label) {
146
- const parsed = agentProfileSchema.parse(input);
203
+ let parsed;
204
+ try {
205
+ parsed = agentProfileSchema.parse(input);
206
+ } catch (cause) {
207
+ throw new Error(`${label} contains unsupported or non-canonical fields`, { cause });
208
+ }
147
209
  assertCanonicalParse(input, parsed, label);
148
210
  return parsed;
149
211
  }
150
212
  function parseExactAgentProfileDiff(input, label) {
151
- const parsed = agentProfileDiffSchema.parse(input);
213
+ let parsed;
214
+ try {
215
+ parsed = agentProfileDiffSchema.parse(input);
216
+ } catch (cause) {
217
+ throw new Error(`${label} contains unsupported or non-canonical fields`, { cause });
218
+ }
152
219
  assertCanonicalParse(input, parsed, label);
153
220
  return parsed;
154
221
  }
@@ -172,7 +239,7 @@ function assertCandidateProfileExecutionSupport(profile) {
172
239
  );
173
240
  }
174
241
  }
175
- function candidateProfileAsAgentProfile(candidate) {
242
+ function agentCandidateProfileAsAgentProfile(candidate) {
176
243
  const output = {};
177
244
  copyDirectProfileFields(output, candidate);
178
245
  if (candidate.model) output.model = { ...candidate.model };
@@ -375,13 +442,9 @@ import {
375
442
  } from "@tangle-network/agent-interface";
376
443
 
377
444
  // src/candidate-execution/claim-file-formats.ts
378
- var CLAIM_FORMAT_VERSION = 7;
379
- var PENDING_FORMAT_VERSION = 1;
380
- var TERMINAL_FORMAT_VERSION = 3;
381
- var PHASE_FORMAT_VERSION = 1;
382
-
383
- // src/candidate-execution/claim-terminal.ts
384
- import { agentCandidateArtifactRefSchema } from "@tangle-network/agent-interface";
445
+ import {
446
+ agentCandidateArtifactRefSchema
447
+ } from "@tangle-network/agent-interface";
385
448
 
386
449
  // src/candidate-execution/exact-object.ts
387
450
  function assertExactObjectKeys(value, required, label, optional = []) {
@@ -400,7 +463,31 @@ function assertExactObjectKeys(value, required, label, optional = []) {
400
463
  }
401
464
  }
402
465
 
466
+ // src/candidate-execution/claim-file-formats.ts
467
+ function sealCandidatePreparationEvidence(value, executionPlanDigest) {
468
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
469
+ throw new Error("candidate execution preparation evidence must be an object");
470
+ }
471
+ const record = value;
472
+ assertExactObjectKeys(
473
+ record,
474
+ ["executionPlan", "materializationReceipt"],
475
+ "candidate execution preparation evidence"
476
+ );
477
+ const executionPlan = immutableCandidateValue(
478
+ agentCandidateArtifactRefSchema.parse(record.executionPlan)
479
+ );
480
+ const materializationReceipt = immutableCandidateValue(
481
+ agentCandidateArtifactRefSchema.parse(record.materializationReceipt)
482
+ );
483
+ if (executionPlan.sha256 !== executionPlanDigest) {
484
+ throw new Error("candidate execution plan artifact digest does not match its claim");
485
+ }
486
+ return Object.freeze({ executionPlan, materializationReceipt });
487
+ }
488
+
403
489
  // src/candidate-execution/claim-terminal.ts
490
+ import { agentCandidateArtifactRefSchema as agentCandidateArtifactRefSchema2 } from "@tangle-network/agent-interface";
404
491
  var SHA256_PATTERN = /^sha256:[a-f0-9]{64}$/;
405
492
  function terminalRecord(claim, result) {
406
493
  const terminal = sealTerminalResult(result);
@@ -409,6 +496,7 @@ function terminalRecord(claim, result) {
409
496
  attempt: claim.attempt,
410
497
  bundleDigest: claim.bundleDigest,
411
498
  executionPlanDigest: claim.executionPlanDigest,
499
+ preparationEvidence: claim.preparationEvidence,
412
500
  ...terminal
413
501
  };
414
502
  return immutableCandidateValue({
@@ -419,7 +507,6 @@ function terminalRecord(claim, result) {
419
507
  function recoveredTerminalRecord(claim, phase, evidence) {
420
508
  const recovered = sealRecoveryEvidence(evidence, claim);
421
509
  return terminalRecord(claim, {
422
- schemaVersion: 1,
423
510
  status: "failed",
424
511
  failureClass: recovered.failureClass === "pre-model-infrastructure" && phase !== "claimed" ? "unknown" : recovered.failureClass,
425
512
  usage: recovered.usage,
@@ -474,8 +561,8 @@ function sealTerminalRecordValue(value, label) {
474
561
  "attempt",
475
562
  "bundleDigest",
476
563
  "executionPlanDigest",
564
+ "preparationEvidence",
477
565
  "terminalDigest",
478
- "schemaVersion",
479
566
  "status",
480
567
  "usage",
481
568
  "modelSettlement",
@@ -487,8 +574,8 @@ function sealTerminalRecordValue(value, label) {
487
574
  "attempt",
488
575
  "bundleDigest",
489
576
  "executionPlanDigest",
577
+ "preparationEvidence",
490
578
  "terminalDigest",
491
- "schemaVersion",
492
579
  "status",
493
580
  "failureClass",
494
581
  "usage",
@@ -497,14 +584,19 @@ function sealTerminalRecordValue(value, label) {
497
584
  ],
498
585
  label
499
586
  );
587
+ const executionPlanDigest = requireString(
588
+ value.executionPlanDigest,
589
+ label,
590
+ "executionPlanDigest"
591
+ );
500
592
  const identity = {
501
593
  executionId: requireString(value.executionId, label, "executionId"),
502
594
  attempt: requireNumber(value.attempt, label, "attempt"),
503
595
  bundleDigest: requireString(value.bundleDigest, label, "bundleDigest"),
504
- executionPlanDigest: requireString(
505
- value.executionPlanDigest,
506
- label,
507
- "executionPlanDigest"
596
+ executionPlanDigest,
597
+ preparationEvidence: sealCandidatePreparationEvidence(
598
+ value.preparationEvidence,
599
+ executionPlanDigest
508
600
  )
509
601
  };
510
602
  assertExecutionId(identity.executionId);
@@ -513,28 +605,21 @@ function sealTerminalRecordValue(value, label) {
513
605
  }
514
606
  assertSha256Digest(identity.bundleDigest, "bundleDigest");
515
607
  assertSha256Digest(identity.executionPlanDigest, "executionPlanDigest");
608
+ if (identity.preparationEvidence.executionPlan.sha256 !== identity.executionPlanDigest) {
609
+ throw new Error(`${label} execution plan artifact does not match executionPlanDigest`);
610
+ }
516
611
  const result = sealTerminalResult(
517
612
  status === "succeeded" ? {
518
- schemaVersion: requireNumber(value.schemaVersion, label, "schemaVersion"),
519
613
  status,
520
- usage: requireObject(
521
- value.usage,
522
- label,
523
- "usage"
524
- ),
614
+ usage: requireObject(value.usage, label, "usage"),
525
615
  modelSettlement: requireArtifactRef(value.modelSettlement, label, "modelSettlement"),
526
616
  taskOutcome: requireArtifactRef(value.taskOutcome, label, "taskOutcome"),
527
617
  benchmarkResult: requireArtifactRef(value.benchmarkResult, label, "benchmarkResult"),
528
618
  runReceipt: requireArtifactRef(value.runReceipt, label, "runReceipt")
529
619
  } : {
530
- schemaVersion: requireNumber(value.schemaVersion, label, "schemaVersion"),
531
620
  status,
532
621
  failureClass: requireFailureClass(value.failureClass, label),
533
- usage: requireObject(
534
- value.usage,
535
- label,
536
- "usage"
537
- ),
622
+ usage: requireObject(value.usage, label, "usage"),
538
623
  modelSettlement: requireArtifactRef(value.modelSettlement, label, "modelSettlement"),
539
624
  ...value.failureEvidence ? {
540
625
  failureEvidence: requireArtifactRef(
@@ -561,7 +646,7 @@ function sealTerminalRecordValue(value, label) {
561
646
  });
562
647
  }
563
648
  function assertTerminalMatchesClaim(terminal, claim, path) {
564
- if (terminal.executionId !== claim.executionId || terminal.attempt !== claim.attempt || terminal.bundleDigest !== claim.bundleDigest || terminal.executionPlanDigest !== claim.executionPlanDigest) {
649
+ if (terminal.executionId !== claim.executionId || terminal.attempt !== claim.attempt || terminal.bundleDigest !== claim.bundleDigest || terminal.executionPlanDigest !== claim.executionPlanDigest || canonicalCandidateDigest(terminal.preparationEvidence) !== canonicalCandidateDigest(claim.preparationEvidence)) {
565
650
  throw new Error(`candidate execution terminal record at ${path} does not match its claim`);
566
651
  }
567
652
  }
@@ -640,16 +725,7 @@ function sealTerminalResult(result) {
640
725
  }
641
726
  assertExactObjectKeys(
642
727
  result,
643
- result.status === "succeeded" ? [
644
- "schemaVersion",
645
- "status",
646
- "usage",
647
- "modelSettlement",
648
- "taskOutcome",
649
- "benchmarkResult",
650
- "runReceipt"
651
- ] : [
652
- "schemaVersion",
728
+ result.status === "succeeded" ? ["status", "usage", "modelSettlement", "taskOutcome", "benchmarkResult", "runReceipt"] : [
653
729
  "status",
654
730
  "failureClass",
655
731
  "usage",
@@ -658,14 +734,10 @@ function sealTerminalResult(result) {
658
734
  ],
659
735
  "candidate execution terminal result"
660
736
  );
661
- if (result.schemaVersion !== 1) {
662
- throw new Error("candidate execution terminal schemaVersion must be 1");
663
- }
664
737
  const usage = sealUsage(result.usage);
665
738
  const modelSettlement = sealArtifactRef(result.modelSettlement, "modelSettlement");
666
739
  if (result.status === "succeeded") {
667
740
  return Object.freeze({
668
- schemaVersion: 1,
669
741
  status: "succeeded",
670
742
  usage,
671
743
  modelSettlement,
@@ -679,7 +751,6 @@ function sealTerminalResult(result) {
679
751
  throw new Error("pre-model infrastructure failure cannot contain model calls");
680
752
  }
681
753
  return Object.freeze({
682
- schemaVersion: 1,
683
754
  status: "failed",
684
755
  failureClass: result.failureClass,
685
756
  usage,
@@ -713,7 +784,7 @@ function sealUsage(usage) {
713
784
  });
714
785
  }
715
786
  function sealArtifactRef(ref, label) {
716
- const parsed = agentCandidateArtifactRefSchema.parse(ref);
787
+ const parsed = agentCandidateArtifactRefSchema2.parse(ref);
717
788
  if (!Number.isSafeInteger(parsed.byteLength)) {
718
789
  throw new Error(`candidate execution terminal ${label} byteLength exceeds safe integer range`);
719
790
  }
@@ -798,29 +869,15 @@ function candidateResultTimeout(timeoutMs, taskTimeoutMs) {
798
869
  return effective;
799
870
  }
800
871
  async function withinCandidateCleanupDeadline(operation, deadlineAtMs, label) {
801
- const remainingMs = deadlineAtMs - Date.now();
802
- if (remainingMs <= 0) throw new CandidateCleanupTimeoutError(label);
803
- const pending = Promise.resolve().then(operation);
804
- void pending.catch(() => void 0);
805
- let timer;
806
- try {
807
- const result = await Promise.race([
808
- pending,
809
- new Promise((_resolve, reject) => {
810
- timer = setTimeout(() => reject(new CandidateCleanupTimeoutError(label)), remainingMs);
811
- })
812
- ]);
813
- if (Date.now() >= deadlineAtMs) throw new CandidateCleanupTimeoutError(label);
814
- return result;
815
- } finally {
816
- if (timer) clearTimeout(timer);
817
- }
872
+ return withinCandidateDeadline(operation, deadlineAtMs, new CandidateCleanupTimeoutError(label));
818
873
  }
819
874
  async function withinCandidateResultDeadline(operation, deadlineAtMs, label) {
875
+ return withinCandidateDeadline(operation, deadlineAtMs, new CandidateResultTimeoutError(label));
876
+ }
877
+ async function withinCandidateDeadline(operation, deadlineAtMs, timeoutError) {
820
878
  const remainingMs = deadlineAtMs - Date.now();
821
- if (remainingMs <= 0) throw new CandidateResultTimeoutError(label);
879
+ if (remainingMs <= 0) throw timeoutError;
822
880
  const controller = new AbortController();
823
- const timeoutError = new CandidateResultTimeoutError(label);
824
881
  const pending = Promise.resolve().then(() => operation(controller.signal));
825
882
  void pending.catch(() => void 0);
826
883
  let timer;
@@ -949,8 +1006,8 @@ var InMemoryAgentCandidateExecutionClaimStore = class {
949
1006
  }
950
1007
  };
951
1008
  var SHA256_PATTERN2 = /^sha256:[a-f0-9]{64}$/;
952
- var LEASE_TOKEN_PATTERN = /^candidate-execution-lease-v1\.[A-Za-z0-9_-]{43}$/;
953
- var PREPARATION_ID_PATTERN = /^candidate-preparation-v1\.[A-Za-z0-9_-]{43}$/;
1009
+ var LEASE_TOKEN_PATTERN = /^candidate-execution-lease\.[A-Za-z0-9_-]{43}$/;
1010
+ var PREPARATION_ID_PATTERN = /^candidate-preparation\.[A-Za-z0-9_-]{43}$/;
954
1011
  function sealClaim(claim) {
955
1012
  assertExactObjectKeys(
956
1013
  claim,
@@ -961,6 +1018,7 @@ function sealClaim(claim) {
961
1018
  "retryPolicy",
962
1019
  "bundleDigest",
963
1020
  "executionPlanDigest",
1021
+ "preparationEvidence",
964
1022
  "retryLineageDigest",
965
1023
  "leaseExpiresAtMs",
966
1024
  "resultTimeoutMs",
@@ -986,6 +1044,10 @@ function sealClaim(claim) {
986
1044
  }
987
1045
  assertSha256Digest2(claim.bundleDigest, "bundleDigest");
988
1046
  assertSha256Digest2(claim.executionPlanDigest, "executionPlanDigest");
1047
+ const preparationEvidence = sealCandidatePreparationEvidence(
1048
+ claim.preparationEvidence,
1049
+ claim.executionPlanDigest
1050
+ );
989
1051
  assertSha256Digest2(claim.retryLineageDigest, "retryLineageDigest");
990
1052
  assertPositiveTimestamp(claim.leaseExpiresAtMs, "leaseExpiresAtMs");
991
1053
  candidateResultTimeout(claim.resultTimeoutMs, claim.resultTimeoutMs);
@@ -997,6 +1059,7 @@ function sealClaim(claim) {
997
1059
  retryPolicy: claim.retryPolicy,
998
1060
  bundleDigest: claim.bundleDigest,
999
1061
  executionPlanDigest: claim.executionPlanDigest,
1062
+ preparationEvidence,
1000
1063
  retryLineageDigest: claim.retryLineageDigest,
1001
1064
  leaseExpiresAtMs: claim.leaseExpiresAtMs,
1002
1065
  resultTimeoutMs: claim.resultTimeoutMs,
@@ -1080,7 +1143,7 @@ function newLease(claim) {
1080
1143
  return Object.freeze({
1081
1144
  executionId: claim.executionId,
1082
1145
  attempt: claim.attempt,
1083
- token: `candidate-execution-lease-v1.${randomBytes(32).toString("base64url")}`,
1146
+ token: `candidate-execution-lease.${randomBytes(32).toString("base64url")}`,
1084
1147
  expiresAtMs: claim.leaseExpiresAtMs
1085
1148
  });
1086
1149
  }
@@ -1131,19 +1194,16 @@ function rejectedRetry(claim, detail) {
1131
1194
  async function readClaim(path) {
1132
1195
  const parsed = await readJsonObject(path, "claim");
1133
1196
  const record = parsed;
1134
- if (record.version !== CLAIM_FORMAT_VERSION) {
1135
- throw new Error(`candidate execution claim at ${path} has unsupported version`);
1136
- }
1137
1197
  assertExactObjectKeys(
1138
1198
  parsed,
1139
1199
  [
1140
- "version",
1141
1200
  "executionId",
1142
1201
  "attempt",
1143
1202
  "maxAttempts",
1144
1203
  "retryPolicy",
1145
1204
  "bundleDigest",
1146
1205
  "executionPlanDigest",
1206
+ "preparationEvidence",
1147
1207
  "retryLineageDigest",
1148
1208
  "leaseExpiresAtMs",
1149
1209
  "resultTimeoutMs",
@@ -1164,6 +1224,11 @@ async function readClaim(path) {
1164
1224
  path,
1165
1225
  "executionPlanDigest"
1166
1226
  ),
1227
+ preparationEvidence: requireObject2(
1228
+ record.preparationEvidence,
1229
+ path,
1230
+ "preparationEvidence"
1231
+ ),
1167
1232
  retryLineageDigest: requireString2(
1168
1233
  record.retryLineageDigest,
1169
1234
  path,
@@ -1199,10 +1264,7 @@ async function readClaimIfPresent(path) {
1199
1264
  async function readTerminal(path) {
1200
1265
  const parsed = await readJsonObject(path, "terminal record");
1201
1266
  const record = parsed;
1202
- if (record.version !== TERMINAL_FORMAT_VERSION) {
1203
- throw new Error(`candidate execution terminal record at ${path} has unsupported version`);
1204
- }
1205
- assertExactObjectKeys(parsed, ["version", "terminal"], `candidate execution terminal record at ${path}`);
1267
+ assertExactObjectKeys(parsed, ["terminal"], `candidate execution terminal record at ${path}`);
1206
1268
  return sealTerminalRecordValue(
1207
1269
  requireObject2(record.terminal, path, "terminal"),
1208
1270
  `candidate execution terminal record at ${path}`
@@ -1226,12 +1288,9 @@ async function readTransitionIfPresent(path, claim) {
1226
1288
  }
1227
1289
  if (parsed.kind === "candidate-execution-phase") {
1228
1290
  const record = parsed;
1229
- if (record.version !== PHASE_FORMAT_VERSION) {
1230
- throw new Error(`candidate execution phase record at ${path} has unsupported version`);
1231
- }
1232
1291
  assertExactObjectKeys(
1233
1292
  parsed,
1234
- ["version", "kind", "executionId", "attempt", "executionPlanDigest", "phase"],
1293
+ ["kind", "executionId", "attempt", "executionPlanDigest", "phase"],
1235
1294
  `candidate execution phase record at ${path}`
1236
1295
  );
1237
1296
  if (record.executionId !== claim.executionId || record.attempt !== claim.attempt || record.executionPlanDigest !== claim.executionPlanDigest || record.phase !== "candidate-may-run") {
@@ -1241,14 +1300,7 @@ async function readTransitionIfPresent(path, claim) {
1241
1300
  }
1242
1301
  if (parsed.kind === "candidate-execution-pending-terminal") {
1243
1302
  const record = parsed;
1244
- if (record.version !== PENDING_FORMAT_VERSION) {
1245
- throw new Error(`candidate execution pending record at ${path} has unsupported version`);
1246
- }
1247
- assertExactObjectKeys(
1248
- parsed,
1249
- ["version", "kind", "terminal"],
1250
- `candidate execution pending record at ${path}`
1251
- );
1303
+ assertExactObjectKeys(parsed, ["kind", "terminal"], `candidate execution pending record at ${path}`);
1252
1304
  const terminal = sealTerminalRecordValue(
1253
1305
  requireObject2(record.terminal, path, "terminal"),
1254
1306
  `candidate execution pending record at ${path}`
@@ -1398,6 +1450,8 @@ function candidateTerminalWindowMs(cleanupTimeoutMs) {
1398
1450
 
1399
1451
  // src/candidate-execution/prepared-state.ts
1400
1452
  import {
1453
+ agentCandidateBenchmarkSuiteSchema,
1454
+ agentCandidateBenchmarkTaskSchema,
1401
1455
  agentCandidateExecutionPlanEvidenceSchema,
1402
1456
  agentCandidateMaterializationReceiptSchema,
1403
1457
  agentCandidateProfilePlanEvidenceSchema
@@ -1466,7 +1520,6 @@ async function readMaterializedWorkspaceFiles(root, expected, options = {}) {
1466
1520
  }
1467
1521
  function candidateWorkspaceManifest(files) {
1468
1522
  return {
1469
- schemaVersion: 2,
1470
1523
  kind: "agent-candidate-workspace-manifest",
1471
1524
  files: files.map((file) => ({
1472
1525
  path: file.path,
@@ -1936,14 +1989,6 @@ function isNoEntry(error) {
1936
1989
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
1937
1990
  }
1938
1991
 
1939
- // src/candidate-execution/workspace-task.ts
1940
- function workspaceTaskRepository(task) {
1941
- if (task.outcome.kind !== "workspace" || !task.repository) {
1942
- throw new Error("candidate runtime requires a workspace outcome with repository provenance");
1943
- }
1944
- return task.repository;
1945
- }
1946
-
1947
1992
  // src/candidate-execution/prepared-state.ts
1948
1993
  var stateByExecution = /* @__PURE__ */ new WeakMap();
1949
1994
  var lifecycleByExecution = /* @__PURE__ */ new WeakMap();
@@ -1952,9 +1997,11 @@ function createPreparedCandidateExecution(input) {
1952
1997
  assertPrivateCandidateIntegrity(state);
1953
1998
  const prepared = Object.freeze({
1954
1999
  bundle: state.bundle,
2000
+ benchmark: Object.freeze({ suite: state.benchmarkSuite, task: state.benchmarkTask }),
1955
2001
  executionId: state.executionId,
1956
2002
  roots: state.roots,
1957
2003
  profilePlan: evidenceView(state.profilePlan),
2004
+ profileActivation: state.profileActivation,
1958
2005
  executionPlan: evidenceView(state.executionPlan),
1959
2006
  materializationReceipt: state.materializationReceipt,
1960
2007
  launch: state.launch,
@@ -2007,8 +2054,9 @@ function beginPreparedCandidateRun(prepared, modelAccess, memoryAccess) {
2007
2054
  const material = state.executionPlan.value.material;
2008
2055
  const request = Object.freeze({
2009
2056
  executionId: state.executionId,
2057
+ benchmark: preparedBenchmark(state),
2010
2058
  inputs: Object.freeze({
2011
- task: workspaceInputView(material.task.workspace, state.executorInputs.taskFiles),
2059
+ task: workspaceInputView(state.benchmarkTask.workspace, state.executorInputs.taskFiles),
2012
2060
  ...material.candidateWorkspace && state.executorInputs.candidateFiles ? {
2013
2061
  candidate: workspaceInputView(
2014
2062
  material.candidateWorkspace,
@@ -2023,6 +2071,7 @@ function beginPreparedCandidateRun(prepared, modelAccess, memoryAccess) {
2023
2071
  }),
2024
2072
  roots: state.roots.execution,
2025
2073
  profilePlan: evidenceView(state.profilePlan),
2074
+ profileActivation: state.profileActivation,
2026
2075
  executionPlan: evidenceView(state.executionPlan),
2027
2076
  materializationReceipt: state.materializationReceipt,
2028
2077
  launch: immutableCandidateValue({ ...state.launch, env: completeEnvironment }),
@@ -2036,6 +2085,9 @@ function beginPreparedCandidateRun(prepared, modelAccess, memoryAccess) {
2036
2085
  });
2037
2086
  return { state, request };
2038
2087
  }
2088
+ function preparedBenchmark(state) {
2089
+ return Object.freeze({ suite: state.benchmarkSuite, task: state.benchmarkTask });
2090
+ }
2039
2091
  function consumePreparedCandidateExecution(prepared, outcome) {
2040
2092
  transitionLifecycle(
2041
2093
  prepared,
@@ -2048,10 +2100,16 @@ function beginPreparedCandidateSettlement(prepared) {
2048
2100
  }
2049
2101
  async function assertPreparedCandidateWorkspaces(state) {
2050
2102
  const plan = state.executionPlan.value.material;
2051
- await verifyMaterializedWorkspace(state.roots.staging.taskRoot, plan.task.workspace.material, {
2052
- ignoredProtectedRootEntries: [".git", ".sidecar"]
2053
- });
2054
- await verifyTaskCheckout(state.roots.staging.taskRoot, workspaceTaskRepository(plan.task));
2103
+ await verifyMaterializedWorkspace(
2104
+ state.roots.staging.taskRoot,
2105
+ state.benchmarkTask.workspace.material,
2106
+ {
2107
+ ignoredProtectedRootEntries: [".git", ".sidecar"]
2108
+ }
2109
+ );
2110
+ if (state.benchmarkTask.repository) {
2111
+ await verifyTaskCheckout(state.roots.staging.taskRoot, state.benchmarkTask.repository);
2112
+ }
2055
2113
  await verifyMaterializedProfileWorkspace(
2056
2114
  state.roots.staging.profileRoot,
2057
2115
  state.profilePlan.value.material
@@ -2065,9 +2123,19 @@ async function assertPreparedCandidateWorkspaces(state) {
2065
2123
  }
2066
2124
  }
2067
2125
  function detachPreparedCandidateState(input) {
2126
+ const benchmarkSuite = immutableCandidateValue(
2127
+ agentCandidateBenchmarkSuiteSchema.parse(input.benchmarkSuite)
2128
+ );
2129
+ const benchmarkTask = immutableCandidateValue(
2130
+ agentCandidateBenchmarkTaskSchema.parse(input.benchmarkTask)
2131
+ );
2068
2132
  const profilePlan = immutableCandidateValue(
2069
2133
  agentCandidateProfilePlanEvidenceSchema.parse(input.profilePlan.value)
2070
2134
  );
2135
+ const profileActivation = parseAgentCandidateProfileActivation(
2136
+ input.profileActivation,
2137
+ profilePlan.digest
2138
+ );
2071
2139
  const executionPlan = immutableCandidateValue(
2072
2140
  agentCandidateExecutionPlanEvidenceSchema.parse(input.executionPlan.value)
2073
2141
  );
@@ -2083,6 +2151,8 @@ function detachPreparedCandidateState(input) {
2083
2151
  return Object.freeze({
2084
2152
  ports: input.ports,
2085
2153
  bundle: input.bundle,
2154
+ benchmarkSuite,
2155
+ benchmarkTask,
2086
2156
  executionId: input.executionId,
2087
2157
  roots: immutableCandidateValue(input.roots),
2088
2158
  profilePlan: Object.freeze({
@@ -2090,6 +2160,7 @@ function detachPreparedCandidateState(input) {
2090
2160
  bytes: Uint8Array.from(input.profilePlan.bytes),
2091
2161
  written: Object.freeze([...input.profilePlan.written])
2092
2162
  }),
2163
+ profileActivation,
2093
2164
  executionPlan: Object.freeze({
2094
2165
  value: executionPlan,
2095
2166
  bytes: Uint8Array.from(input.executionPlan.bytes)
@@ -2109,11 +2180,7 @@ function detachPreparedCandidateState(input) {
2109
2180
  executorInputs: Object.freeze({
2110
2181
  taskFiles: immutableExecutorFiles(input.executorInputs.taskFiles),
2111
2182
  ...input.executorInputs.candidateFiles ? { candidateFiles: immutableExecutorFiles(input.executorInputs.candidateFiles) } : {},
2112
- profileFiles: Object.freeze(
2113
- input.executorInputs.profileFiles.map(
2114
- (file) => Object.freeze({ ...file, bytes: Uint8Array.from(file.bytes) })
2115
- )
2116
- )
2183
+ profileFiles: immutableExecutorFiles(input.executorInputs.profileFiles)
2117
2184
  }),
2118
2185
  ...input.memoryReservation ? { memoryReservation: immutableCandidateValue(input.memoryReservation) } : {},
2119
2186
  ...input.knowledge ? {
@@ -2134,6 +2201,7 @@ function assertPrivateCandidateIntegrity(state) {
2134
2201
  throw new Error("prepared candidate bundle no longer matches its digest");
2135
2202
  }
2136
2203
  assertPlanEvidence(state.profilePlan.value, state.profilePlan.bytes, "profile plan");
2204
+ parseAgentCandidateProfileActivation(state.profileActivation, state.profilePlan.value.digest);
2137
2205
  assertPlanEvidence(state.executionPlan.value, state.executionPlan.bytes, "execution plan");
2138
2206
  agentCandidateExecutionPlanEvidenceSchema.parse(state.executionPlan.value);
2139
2207
  const receipt = agentCandidateMaterializationReceiptSchema.parse(
@@ -2143,11 +2211,12 @@ function assertPrivateCandidateIntegrity(state) {
2143
2211
  if (canonicalCandidateDigest(omitTopLevelDigest(receipt)) !== state.materializationReceipt.digest || !Buffer.from(receiptBytes).equals(Buffer.from(state.materializationReceipt.bytes))) {
2144
2212
  throw new Error("prepared materialization receipt no longer matches its canonical bytes");
2145
2213
  }
2146
- const instruction = state.executionPlan.value.material.task.instruction;
2147
- if (sha256Bytes(state.instruction.bytes) !== instruction.sha256 || state.instruction.bytes.byteLength !== instruction.byteLength || JSON.stringify(state.instruction.delivery) !== JSON.stringify(instruction.delivery)) {
2214
+ assertSignedBenchmarkInput(state);
2215
+ const instruction = Buffer.from(state.benchmarkTask.instruction, "utf8");
2216
+ if (!Buffer.from(state.instruction.bytes).equals(instruction) || JSON.stringify(state.instruction.delivery) !== JSON.stringify(state.executionPlan.value.material.instructionDelivery)) {
2148
2217
  throw new Error("prepared instruction no longer matches the signed execution plan");
2149
2218
  }
2150
- if (!/^candidate-preparation-v1\.[A-Za-z0-9_-]{43}$/.test(state.preparationId) || !Number.isSafeInteger(state.reservationExpiresAtMs) || state.reservationExpiresAtMs <= 0 || !Number.isSafeInteger(state.cleanupTimeoutMs) || state.cleanupTimeoutMs <= 0 || !Number.isSafeInteger(state.resultTimeoutMs) || state.resultTimeoutMs <= 0 || JSON.stringify(state.resolvedModel) !== JSON.stringify(state.executionPlan.value.material.model.resolved) || state.modelReservation.digest !== state.executionPlan.value.material.model.access.grantDigest || state.modelReservation.preparationId !== state.preparationId || state.modelReservation.expiresAtMs !== state.reservationExpiresAtMs || canonicalCandidateDigest(state.modelReservation.network) !== canonicalCandidateDigest(state.executionPlan.value.material.model.access.network) || canonicalCandidateDigest(state.modelReservation.enforcedLimits) !== canonicalCandidateDigest(modelLimits(state.executionPlan.value.material.limits))) {
2219
+ if (!/^candidate-preparation\.[A-Za-z0-9_-]{43}$/.test(state.preparationId) || !Number.isSafeInteger(state.reservationExpiresAtMs) || state.reservationExpiresAtMs <= 0 || !Number.isSafeInteger(state.cleanupTimeoutMs) || state.cleanupTimeoutMs <= 0 || !Number.isSafeInteger(state.resultTimeoutMs) || state.resultTimeoutMs <= 0 || JSON.stringify(state.resolvedModel) !== JSON.stringify(state.executionPlan.value.material.model.resolved) || state.modelReservation.digest !== state.executionPlan.value.material.model.access.grantDigest || state.modelReservation.preparationId !== state.preparationId || state.modelReservation.expiresAtMs !== state.reservationExpiresAtMs || canonicalCandidateDigest(state.modelReservation.network) !== canonicalCandidateDigest(state.executionPlan.value.material.model.access.network) || canonicalCandidateDigest(state.modelReservation.enforcedLimits) !== canonicalCandidateDigest(modelLimits(state.executionPlan.value.material.limits))) {
2151
2220
  throw new Error("prepared model access no longer matches the signed execution plan");
2152
2221
  }
2153
2222
  assertExecutorInputs(state);
@@ -2175,7 +2244,7 @@ function assertPrivateCandidateIntegrity(state) {
2175
2244
  [CANDIDATE_TRACE_ENV.traceRunId]: state.trace.runId
2176
2245
  };
2177
2246
  if (!state.trace.runId.startsWith(
2178
- `${state.executionId}:attempt-${state.executionPlan.value.material.attempt.number}:`
2247
+ `${state.executionId}:attempt-${state.executionPlan.value.material.runCell.attempt}:`
2179
2248
  ) || canonicalCandidateDigest(state.trace.tags) !== canonicalCandidateDigest(expectedTags) || canonicalCandidateDigest(state.trace.env) !== canonicalCandidateDigest(expectedTraceEnvironment)) {
2180
2249
  throw new Error("prepared trace identity no longer matches the signed execution");
2181
2250
  }
@@ -2220,7 +2289,7 @@ function knowledgeView(knowledge) {
2220
2289
  function workspaceInputView(snapshot, sourceFiles) {
2221
2290
  return Object.freeze({
2222
2291
  snapshot,
2223
- files: Object.freeze(sourceFiles.map((file) => profileFileView(file)))
2292
+ files: Object.freeze(sourceFiles.map((file) => executorFileView(file)))
2224
2293
  });
2225
2294
  }
2226
2295
  function profileFileView(source) {
@@ -2233,9 +2302,22 @@ function profileFileView(source) {
2233
2302
  }
2234
2303
  });
2235
2304
  }
2305
+ function executorFileView(source) {
2306
+ const bytes = Uint8Array.from(source.bytes);
2307
+ return Object.freeze({
2308
+ path: source.path,
2309
+ mode: source.mode,
2310
+ get bytes() {
2311
+ return Uint8Array.from(bytes);
2312
+ }
2313
+ });
2314
+ }
2236
2315
  function assertExecutorInputs(state) {
2237
2316
  const material = state.executionPlan.value.material;
2238
- assertWorkspaceExecutorFiles(state.executorInputs.taskFiles, material.task.workspace.material);
2317
+ assertWorkspaceExecutorFiles(
2318
+ state.executorInputs.taskFiles,
2319
+ state.benchmarkTask.workspace.material
2320
+ );
2239
2321
  if (material.candidateWorkspace) {
2240
2322
  if (!state.executorInputs.candidateFiles) {
2241
2323
  throw new Error("prepared candidate executor files are missing");
@@ -2293,6 +2375,22 @@ function assertWorkspaceExecutorFiles(actualFiles, expected) {
2293
2375
  }
2294
2376
  }
2295
2377
  }
2378
+ function assertSignedBenchmarkInput(state) {
2379
+ const plan = state.executionPlan.value.material;
2380
+ const cell = plan.runCell;
2381
+ const suite = state.benchmarkSuite;
2382
+ const task = state.benchmarkTask;
2383
+ const receipt = state.materializationReceipt.value;
2384
+ const cellIndex = cell.taskIndex * suite.reps + cell.repetition;
2385
+ if (canonicalCandidateDigest(omitTopLevelDigest(suite)) !== suite.digest || canonicalCandidateDigest(omitTopLevelDigest(task)) !== task.digest || canonicalCandidateDigest(omitTopLevelDigest(cell)) !== cell.digest || cell.bundleDigest !== state.bundle.digest || cell.suiteDigest !== suite.digest || cell.taskDigest !== task.digest || suite.taskDigests[cell.taskIndex] !== task.digest || suite.seeds[cellIndex] !== cell.seed || cell.repetition >= suite.reps || cell.attempt < 1 || cell.attempt > task.attempt.maxAttempts) {
2386
+ throw new Error("prepared candidate no longer matches its signed experiment cell");
2387
+ }
2388
+ const suiteBytes = canonicalCandidateBytes(omitTopLevelDigest(suite));
2389
+ const taskBytes = canonicalCandidateBytes(omitTopLevelDigest(task));
2390
+ if (receipt.benchmark.suite.digest !== suite.digest || receipt.benchmark.suite.material.sha256 !== sha256Bytes(suiteBytes) || receipt.benchmark.suite.material.byteLength !== suiteBytes.byteLength || receipt.benchmark.task.digest !== task.digest || receipt.benchmark.task.material.sha256 !== sha256Bytes(taskBytes) || receipt.benchmark.task.material.byteLength !== taskBytes.byteLength) {
2391
+ throw new Error("materialization receipt no longer matches its signed benchmark input");
2392
+ }
2393
+ }
2296
2394
  function immutableExecutorFiles(files) {
2297
2395
  return Object.freeze(
2298
2396
  files.map((file) => Object.freeze({ ...file, bytes: Uint8Array.from(file.bytes) }))
@@ -2330,10 +2428,14 @@ function assertProtectedEnvironment(state, modelEnvironment, memoryEnvironment)
2330
2428
  }
2331
2429
 
2332
2430
  // src/candidate-execution/claim-plan.ts
2333
- function candidateExecutionClaim(prepared) {
2431
+ function candidateExecutionClaim(prepared, preparationEvidence) {
2334
2432
  const state = assertPreparedCandidateIntegrity(prepared);
2335
2433
  const material = prepared.executionPlan.value.material;
2336
- const attempt = material.attempt;
2434
+ const attempt = {
2435
+ number: material.runCell.attempt,
2436
+ maxAttempts: state.benchmarkTask.attempt.maxAttempts,
2437
+ retryPolicy: state.benchmarkTask.attempt.retryPolicy
2438
+ };
2337
2439
  const nowMs = Date.now();
2338
2440
  if (!Number.isSafeInteger(nowMs) || nowMs < 0) {
2339
2441
  throw new Error("candidate execution claim-store clock returned an invalid timestamp");
@@ -2351,6 +2453,14 @@ function candidateExecutionClaim(prepared) {
2351
2453
  "candidate preparation expires before its full execution and cleanup owner window"
2352
2454
  );
2353
2455
  }
2456
+ if (preparationEvidence.executionPlan.sha256 !== prepared.executionPlan.value.digest || preparationEvidence.executionPlan.byteLength !== state.executionPlan.bytes.byteLength) {
2457
+ throw new Error("persisted execution plan does not match its canonical preparation bytes");
2458
+ }
2459
+ if (preparationEvidence.materializationReceipt.sha256 !== state.materializationReceipt.digest || preparationEvidence.materializationReceipt.byteLength !== state.materializationReceipt.bytes.byteLength) {
2460
+ throw new Error(
2461
+ "persisted materialization receipt does not match its canonical preparation bytes"
2462
+ );
2463
+ }
2354
2464
  return candidateClaimFileInternals.sealClaim({
2355
2465
  executionId: prepared.executionId,
2356
2466
  attempt: attempt.number,
@@ -2358,6 +2468,7 @@ function candidateExecutionClaim(prepared) {
2358
2468
  retryPolicy: attempt.retryPolicy,
2359
2469
  bundleDigest: prepared.bundle.digest,
2360
2470
  executionPlanDigest: prepared.executionPlan.value.digest,
2471
+ preparationEvidence,
2361
2472
  retryLineageDigest: retryLineageDigest(prepared, state.resultTimeoutMs),
2362
2473
  leaseExpiresAtMs,
2363
2474
  resultTimeoutMs: state.resultTimeoutMs,
@@ -2382,7 +2493,11 @@ function retryLineageDigest(prepared, resultTimeoutMs) {
2382
2493
  resultTimeoutMs,
2383
2494
  executionPlan: {
2384
2495
  ...material,
2385
- attempt: { ...material.attempt, number: 0 },
2496
+ runCell: {
2497
+ ...material.runCell,
2498
+ attempt: 0,
2499
+ digest: `sha256:${"0".repeat(64)}`
2500
+ },
2386
2501
  model: {
2387
2502
  ...material.model,
2388
2503
  access: {
@@ -2418,13 +2533,13 @@ function retryLineageDigest(prepared, resultTimeoutMs) {
2418
2533
 
2419
2534
  // src/candidate-execution/output-artifacts.ts
2420
2535
  import {
2421
- agentCandidateArtifactRefSchema as agentCandidateArtifactRefSchema2
2536
+ agentCandidateArtifactRefSchema as agentCandidateArtifactRefSchema3
2422
2537
  } from "@tangle-network/agent-interface";
2423
2538
  async function persistCandidateOutputArtifact(port, input) {
2424
2539
  input.signal?.throwIfAborted();
2425
2540
  const bytes = Uint8Array.from(input.bytes);
2426
2541
  const expectedDigest = sha256Bytes(bytes);
2427
- const ref = agentCandidateArtifactRefSchema2.parse(
2542
+ const ref = agentCandidateArtifactRefSchema3.parse(
2428
2543
  await port.put({
2429
2544
  executionId: input.executionId,
2430
2545
  purpose: input.purpose,
@@ -2447,6 +2562,7 @@ import {
2447
2562
  agentCandidateTerminationSchema,
2448
2563
  agentCandidateWorkspaceManifestMaterialSchema
2449
2564
  } from "@tangle-network/agent-interface";
2565
+ var sealedFinalCaptureBrand = /* @__PURE__ */ Symbol("sealedAgentCandidateExecutorFinalCapture");
2450
2566
  function sealAgentCandidateProtectedRunCapture(value) {
2451
2567
  const capture = requireRecord(value, "candidate execution capture");
2452
2568
  assertExactObjectKeys(capture, ["executionId", "termination"], "candidate execution capture");
@@ -2458,31 +2574,32 @@ function sealAgentCandidateProtectedRunCapture(value) {
2458
2574
  termination: Object.freeze(agentCandidateTerminationSchema.parse(capture.termination))
2459
2575
  });
2460
2576
  }
2461
- function sealAgentCandidateExecutorFinalCapture(value) {
2577
+ function sealAgentCandidateExecutorFinalCapture(value, expectedOutcome) {
2462
2578
  const capture = requireRecord(value, "candidate final capture");
2463
- assertExactObjectKeys(capture, ["stopped"], "candidate final capture", ["taskOutcome", "memoryAfter"]);
2464
- if (capture.stopped !== true) {
2465
- throw new Error("candidate final capture does not prove process death");
2466
- }
2467
- const taskOutcome = capture.taskOutcome ? sealTaskOutcomeCapture(capture.taskOutcome) : void 0;
2579
+ assertExactObjectKeys(capture, [], "candidate final capture", [
2580
+ "taskOutcome",
2581
+ "memoryAfter",
2582
+ "evidence"
2583
+ ]);
2584
+ const taskOutcome = capture.taskOutcome ? sealTaskOutcomeCapture(capture.taskOutcome, expectedOutcome) : void 0;
2468
2585
  const memoryAfter = capture.memoryAfter ? sealMemoryCapture(capture.memoryAfter) : void 0;
2586
+ if (capture.evidence !== void 0 && !(capture.evidence instanceof Uint8Array)) {
2587
+ throw new Error("candidate executor evidence must be a byte array");
2588
+ }
2589
+ const evidence = capture.evidence ? Uint8Array.from(capture.evidence) : void 0;
2469
2590
  return Object.freeze({
2470
- stopped: true,
2471
2591
  ...taskOutcome ? { taskOutcome } : {},
2472
- ...memoryAfter ? { memoryAfter: Object.freeze(memoryAfter) } : {}
2592
+ ...memoryAfter ? { memoryAfter: Object.freeze(memoryAfter) } : {},
2593
+ ...evidence ? { evidence } : {},
2594
+ [sealedFinalCaptureBrand]: true
2473
2595
  });
2474
2596
  }
2475
- function encodeAgentCandidateExecutorCapture(input) {
2476
- return canonicalCandidateBytes({
2477
- schemaVersion: 1,
2478
- kind: "agent-candidate-executor-capture",
2479
- executionId: input.executionId,
2480
- executionPlanDigest: input.executionPlanDigest,
2481
- termination: input.termination,
2482
- stopped: true,
2483
- taskOutcome: input.taskOutcome,
2484
- ...input.memoryAfter ? { memoryAfter: input.memoryAfter } : {}
2485
- });
2597
+ function sealAgentCandidateExecutorStopAcknowledgement(value) {
2598
+ const capture = requireRecord(value, "candidate stop acknowledgement");
2599
+ assertExactObjectKeys(capture, ["stopped"], "candidate stop acknowledgement");
2600
+ if (capture.stopped !== true) {
2601
+ throw new Error("candidate stop acknowledgement does not prove process death");
2602
+ }
2486
2603
  }
2487
2604
  function sealMemoryCapture(value) {
2488
2605
  const capture = requireRecord(value, "candidate memory capture");
@@ -2497,13 +2614,32 @@ function sealMemoryCapture(value) {
2497
2614
  archive: Uint8Array.from(capture.archive)
2498
2615
  });
2499
2616
  }
2500
- function sealTaskOutcomeCapture(value) {
2617
+ function sealTaskOutcomeCapture(value, expected) {
2501
2618
  const capture = requireRecord(value, "candidate task capture");
2619
+ if (capture.kind === "output") {
2620
+ assertExactObjectKeys(capture, ["kind", "bytes"], "candidate task output capture");
2621
+ if (expected.kind !== "output") {
2622
+ throw new Error("candidate task output capture does not match the signed outcome kind");
2623
+ }
2624
+ if (!(capture.bytes instanceof Uint8Array)) {
2625
+ throw new Error("candidate task output capture must contain a byte array");
2626
+ }
2627
+ if (capture.bytes.byteLength > expected.maxBytes) {
2628
+ throw new Error(`candidate task output exceeds the frozen ${expected.maxBytes}-byte maximum`);
2629
+ }
2630
+ return Object.freeze({ kind: "output", bytes: Uint8Array.from(capture.bytes) });
2631
+ }
2502
2632
  assertExactObjectKeys(
2503
2633
  capture,
2504
- ["resultTree", "afterState", "archive", "gitDiff"],
2634
+ ["kind", "resultTree", "afterState", "archive", "gitDiff"],
2505
2635
  "candidate task capture"
2506
2636
  );
2637
+ if (capture.kind !== "workspace") {
2638
+ throw new Error("candidate task capture has an invalid outcome kind");
2639
+ }
2640
+ if (expected.kind !== "workspace") {
2641
+ throw new Error("candidate workspace capture does not match the signed outcome kind");
2642
+ }
2507
2643
  if (typeof capture.resultTree !== "string" || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(capture.resultTree)) {
2508
2644
  throw new Error("candidate task capture resultTree is not a Git object id");
2509
2645
  }
@@ -2512,6 +2648,7 @@ function sealTaskOutcomeCapture(value) {
2512
2648
  }
2513
2649
  const afterState = agentCandidateWorkspaceManifestMaterialSchema.parse(capture.afterState);
2514
2650
  return Object.freeze({
2651
+ kind: "workspace",
2515
2652
  resultTree: capture.resultTree,
2516
2653
  afterState: Object.freeze(afterState),
2517
2654
  archive: Uint8Array.from(capture.archive),
@@ -2526,11 +2663,11 @@ function requireRecord(value, label) {
2526
2663
  }
2527
2664
 
2528
2665
  // src/candidate-execution/finalize.ts
2529
- import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
2530
- import { tmpdir as tmpdir2 } from "os";
2531
- import { join as join2 } from "path";
2532
2666
  import { isLlmSpan as isLlmSpan2, REDACTION_VERSION as REDACTION_VERSION2 } from "@tangle-network/agent-eval";
2533
- import { agentCandidateRunReceiptSchema } from "@tangle-network/agent-interface";
2667
+ import {
2668
+ agentCandidateRunReceiptSchema,
2669
+ agentCandidateWorkspaceSnapshotEvidenceSchema
2670
+ } from "@tangle-network/agent-interface";
2534
2671
 
2535
2672
  // src/candidate-execution/model-settlement.ts
2536
2673
  import { isLlmSpan } from "@tangle-network/agent-eval";
@@ -2557,7 +2694,6 @@ function sealAgentCandidateModelSettlement(settlement, expected) {
2557
2694
  let outputTokens = 0;
2558
2695
  let cachedInputTokens = 0;
2559
2696
  let reasoningTokens = 0;
2560
- let hasCachedInput = false;
2561
2697
  let costUsdNanos = 0;
2562
2698
  const calls = settlement.calls.map((source, index) => {
2563
2699
  assertExactObjectKeys(
@@ -2610,7 +2746,6 @@ function sealAgentCandidateModelSettlement(settlement, expected) {
2610
2746
  source.cachedInputTokens,
2611
2747
  "cached input token total"
2612
2748
  );
2613
- hasCachedInput = true;
2614
2749
  assertCount2(source.reasoningTokens, `model settlement call ${index} reasoningTokens`);
2615
2750
  reasoningTokens = safeAdd(reasoningTokens, source.reasoningTokens, "reasoning token total");
2616
2751
  assertCount2(source.costUsdNanos, `model settlement call ${index} costUsdNanos`);
@@ -2620,13 +2755,6 @@ function sealAgentCandidateModelSettlement(settlement, expected) {
2620
2755
  return Object.freeze({ ...source });
2621
2756
  });
2622
2757
  const usage = Object.freeze({
2623
- costUsd: costUsdNanos / USD_NANOS,
2624
- inputTokens,
2625
- outputTokens,
2626
- ...hasCachedInput ? { cachedInputTokens } : {},
2627
- modelCalls: calls.length
2628
- });
2629
- const fixedUsage = Object.freeze({
2630
2758
  costUsdNanos,
2631
2759
  inputTokens,
2632
2760
  outputTokens,
@@ -2641,9 +2769,7 @@ function sealAgentCandidateModelSettlement(settlement, expected) {
2641
2769
  closed: true,
2642
2770
  calls: Object.freeze(calls)
2643
2771
  }),
2644
- usage,
2645
- fixedUsage,
2646
- costUsdNanos
2772
+ usage
2647
2773
  });
2648
2774
  }
2649
2775
  async function appendAuthoritativeModelSettlementSpans(traceStore, runId, settlement) {
@@ -2766,11 +2892,60 @@ function safeAdd(left, right, label) {
2766
2892
  }
2767
2893
 
2768
2894
  // src/candidate-execution/protected-redaction.ts
2895
+ import { createHmac, randomBytes as randomBytes2 } from "crypto";
2769
2896
  import {
2770
2897
  DEFAULT_REDACTION_RULES,
2771
2898
  REDACTION_VERSION,
2772
2899
  redactString
2773
2900
  } from "@tangle-network/agent-eval";
2901
+ function createProtectedRecordRedactor(protectedValues) {
2902
+ const identityKey = randomBytes2(32);
2903
+ const aggregate = {
2904
+ version: REDACTION_VERSION,
2905
+ redactionCount: 0,
2906
+ byRule: {}
2907
+ };
2908
+ const mergeReport = (report) => {
2909
+ aggregate.version = report.version;
2910
+ aggregate.redactionCount += report.redactionCount;
2911
+ for (const [rule, count] of Object.entries(report.byRule)) {
2912
+ aggregate.byRule[rule] = (aggregate.byRule[rule] ?? 0) + count;
2913
+ }
2914
+ };
2915
+ const redactScalar = (value, record) => {
2916
+ const redacted = redactProtectedValue(value, protectedValues);
2917
+ if (record) mergeReport(redacted.report);
2918
+ return redacted.value;
2919
+ };
2920
+ const identifier = (value, record) => {
2921
+ const redacted = redactScalar(value, record);
2922
+ if (redacted === value) return value;
2923
+ const digest = createHmac("sha256", identityKey).update(value).digest("hex");
2924
+ return `${redacted}:hmac-sha256:${digest}`;
2925
+ };
2926
+ const transform = (value, record) => {
2927
+ const transformed = transformProtectedRecord(
2928
+ value,
2929
+ void 0,
2930
+ record,
2931
+ redactScalar,
2932
+ identifier
2933
+ );
2934
+ assertNoProtectedEvidence(transformed, protectedValues);
2935
+ return transformed;
2936
+ };
2937
+ return Object.freeze({
2938
+ record: (value) => transform(value, true),
2939
+ query: (value) => transform(value, false),
2940
+ identifier: (value) => identifier(value, false),
2941
+ traceIdentity: (trace) => transform(trace, false),
2942
+ report: () => ({
2943
+ version: aggregate.version,
2944
+ redactionCount: aggregate.redactionCount,
2945
+ byRule: { ...aggregate.byRule }
2946
+ })
2947
+ });
2948
+ }
2774
2949
  function redactProtectedValue(value, protectedValues) {
2775
2950
  const report = {
2776
2951
  version: REDACTION_VERSION,
@@ -2858,6 +3033,65 @@ function redactNode(value, protectedValues, rules, report) {
2858
3033
  }
2859
3034
  return value;
2860
3035
  }
3036
+ function transformProtectedRecord(value, fieldName, record, redactScalar, redactIdentifier) {
3037
+ if (value instanceof Uint8Array) return redactScalar(value, record);
3038
+ if (typeof value === "string") {
3039
+ return fieldName && isIdentifierFieldName(fieldName) ? redactIdentifier(value, record) : redactScalar(value, record);
3040
+ }
3041
+ if (Array.isArray(value)) {
3042
+ return value.map(
3043
+ (entry) => transformProtectedRecord(entry, fieldName, record, redactScalar, redactIdentifier)
3044
+ );
3045
+ }
3046
+ if (!value || typeof value !== "object") return value;
3047
+ if (fieldName === "tags") {
3048
+ return transformTagMap(value, record, redactScalar, redactIdentifier);
3049
+ }
3050
+ if (fieldName === "tag") {
3051
+ return transformTagFilter(value, record, redactScalar, redactIdentifier);
3052
+ }
3053
+ const entries = [];
3054
+ const seenKeys = /* @__PURE__ */ new Set();
3055
+ for (const [key, entry] of Object.entries(value)) {
3056
+ const redactedKey = redactScalar(key, record);
3057
+ if (seenKeys.has(redactedKey)) {
3058
+ throw new Error("protected evidence redaction produced an ambiguous object key");
3059
+ }
3060
+ seenKeys.add(redactedKey);
3061
+ entries.push([
3062
+ redactedKey,
3063
+ transformProtectedRecord(entry, key, record, redactScalar, redactIdentifier)
3064
+ ]);
3065
+ }
3066
+ return Object.fromEntries(entries);
3067
+ }
3068
+ function transformTagMap(value, record, redactScalar, redactIdentifier) {
3069
+ const entries = [];
3070
+ const seenKeys = /* @__PURE__ */ new Set();
3071
+ for (const [key, entry] of Object.entries(value)) {
3072
+ const redactedKey = redactIdentifier(key, record);
3073
+ if (seenKeys.has(redactedKey)) {
3074
+ throw new Error("protected evidence redaction produced an ambiguous trace tag");
3075
+ }
3076
+ seenKeys.add(redactedKey);
3077
+ entries.push([
3078
+ redactedKey,
3079
+ typeof entry === "string" ? redactIdentifier(entry, record) : transformProtectedRecord(entry, key, record, redactScalar, redactIdentifier)
3080
+ ]);
3081
+ }
3082
+ return Object.fromEntries(entries);
3083
+ }
3084
+ function transformTagFilter(value, record, redactScalar, redactIdentifier) {
3085
+ const output = {};
3086
+ for (const [key, entry] of Object.entries(value)) {
3087
+ const redactedKey = redactScalar(key, record);
3088
+ output[redactedKey] = (key === "key" || key === "value") && typeof entry === "string" ? redactIdentifier(entry, record) : transformProtectedRecord(entry, key, record, redactScalar, redactIdentifier);
3089
+ }
3090
+ return output;
3091
+ }
3092
+ function isIdentifierFieldName(name) {
3093
+ return /ids?$/i.test(name);
3094
+ }
2861
3095
  function protectedRedactionRules(protectedValues) {
2862
3096
  const exactRules = protectedValueVariants(protectedValues).sort((left, right) => right.length - left.length || left.localeCompare(right)).map((value, index) => ({
2863
3097
  id: `candidate-access-${index}`,
@@ -2904,75 +3138,24 @@ function escapeRegularExpression(value) {
2904
3138
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2905
3139
  }
2906
3140
 
2907
- // src/candidate-execution/workspace-snapshot.ts
2908
- import {
2909
- agentCandidateWorkspaceManifestMaterialSchema as agentCandidateWorkspaceManifestMaterialSchema2,
2910
- agentCandidateWorkspaceSnapshotEvidenceSchema
2911
- } from "@tangle-network/agent-interface";
2912
- function provisionalCandidateWorkspaceSnapshot(materialInput, archive) {
2913
- const material = immutableCandidateValue(
2914
- agentCandidateWorkspaceManifestMaterialSchema2.parse(materialInput)
2915
- );
2916
- const manifestBytes = canonicalCandidateBytes(material);
2917
- const snapshot = Object.freeze({
2918
- schemaVersion: 2,
2919
- kind: "agent-candidate-workspace-snapshot",
2920
- digest: sha256Bytes(manifestBytes),
2921
- material,
2922
- manifest: Object.freeze(embeddedCandidateArtifact(manifestBytes)),
2923
- archive: Object.freeze(embeddedCandidateArtifact(archive))
2924
- });
2925
- return Object.freeze({ material, manifestBytes, snapshot });
2926
- }
2927
- async function persistCandidateWorkspaceSnapshot(port, input) {
2928
- input.signal?.throwIfAborted();
2929
- const material = immutableCandidateValue(input.material);
2930
- const manifestBytes = canonicalCandidateBytes(material);
2931
- const [manifest, archive] = await Promise.all([
2932
- persistCandidateOutputArtifact(port, {
2933
- executionId: input.executionId,
2934
- purpose: input.purpose === "task" ? "task-manifest" : "memory-after-manifest",
2935
- bytes: manifestBytes,
2936
- ...input.signal ? { signal: input.signal } : {}
2937
- }),
2938
- persistCandidateOutputArtifact(port, {
2939
- executionId: input.executionId,
2940
- purpose: input.purpose === "task" ? "task-archive" : "memory-after-archive",
2941
- bytes: input.archive,
2942
- ...input.signal ? { signal: input.signal } : {}
2943
- })
2944
- ]);
2945
- return immutableCandidateValue(
2946
- agentCandidateWorkspaceSnapshotEvidenceSchema.parse({
2947
- schemaVersion: 2,
2948
- kind: "agent-candidate-workspace-snapshot",
2949
- digest: sha256Bytes(manifestBytes),
2950
- material,
2951
- manifest,
2952
- archive
2953
- })
2954
- );
2955
- }
2956
-
2957
3141
  // src/candidate-execution/finalize.ts
2958
- async function finalizeAgentCandidateRun(state, capture, traceStore, settlement, evidence, protectedValues, writeRedactionReport, signal) {
3142
+ async function finalizeAgentCandidateRun(state, capture, traceStore, traceIdentity, settlement, evidence, protectedValues, writeRedactionReport, signal) {
2959
3143
  let termination;
2960
3144
  try {
2961
3145
  signal?.throwIfAborted();
2962
- const protectedCapture = sealAgentCandidateProtectedRunCapture(capture);
2963
- if (protectedCapture.executionId !== state.executionId) {
3146
+ if (capture.executionId !== state.executionId) {
2964
3147
  throw new Error("protected capture execution id does not match the prepared execution");
2965
3148
  }
2966
- termination = protectedCapture.termination;
3149
+ termination = capture.termination;
2967
3150
  if (termination.kind === "timeout" && termination.timeoutMs !== state.executionPlan.value.material.limits.timeoutMs) {
2968
3151
  throw new Error("timeout termination does not match the frozen execution limit");
2969
3152
  }
2970
- const run = await traceStore.getRun(state.trace.runId);
2971
- if (!run) throw new Error(`protected trace run is missing: ${state.trace.runId}`);
3153
+ const run = await traceStore.getRun(traceIdentity.runId);
3154
+ if (!run) throw new Error(`protected trace run is missing: ${traceIdentity.runId}`);
2972
3155
  if (run.status === "running" || run.endedAt === void 0) {
2973
3156
  throw new Error("protected trace run is not terminal");
2974
3157
  }
2975
- assertTraceBindings(run.tags, state);
3158
+ assertTraceBindings(run.tags, traceIdentity.tags);
2976
3159
  const [spans, events, budget, artifacts] = await Promise.all([
2977
3160
  traceStore.spans({ runId: run.runId }),
2978
3161
  traceStore.events({ runId: run.runId }),
@@ -2994,31 +3177,14 @@ async function finalizeAgentCandidateRun(state, capture, traceStore, settlement,
2994
3177
  const modelSpans = orderedSpans.filter(isLlmSpan2);
2995
3178
  assertTraceMatchesModelSettlement(modelSpans, settlement);
2996
3179
  enforceLimits(state, run.startedAt, run.endedAt, orderedSpans, settlement);
2997
- const finalCapture = sealAgentCandidateExecutorFinalCapture(evidence.finalCapture);
2998
3180
  const memory = await memoryReceipt(
2999
3181
  state,
3000
- finalCapture,
3001
- evidence.outputArtifacts,
3002
- protectedValues,
3182
+ evidence.finalCapture,
3183
+ evidence.persistedCapture.memoryAfter,
3003
3184
  signal
3004
3185
  );
3005
- const executorCaptureBytes = encodeAgentCandidateExecutorCapture({
3006
- executionId: protectedCapture.executionId,
3007
- executionPlanDigest: state.executionPlan.value.digest,
3008
- termination: protectedCapture.termination,
3009
- taskOutcome: evidence.taskOutcome.evidence,
3010
- ...memory.mode === "isolated" ? { memoryAfter: memory.afterState } : {}
3011
- });
3012
- assertNoProtectedBytes(executorCaptureBytes, protectedValues);
3013
- const executorCapture = await persistCandidateOutputArtifact(evidence.outputArtifacts, {
3014
- executionId: state.executionId,
3015
- purpose: "executor-capture",
3016
- bytes: executorCaptureBytes,
3017
- signal
3018
- });
3019
3186
  const redacted = redactProtectedValue(
3020
3187
  {
3021
- schemaVersion: 1,
3022
3188
  run: { ...run, redactionVersion: REDACTION_VERSION2 },
3023
3189
  spans: orderedSpans,
3024
3190
  events: orderedEvents,
@@ -3049,22 +3215,26 @@ async function finalizeAgentCandidateRun(state, capture, traceStore, settlement,
3049
3215
  signal
3050
3216
  });
3051
3217
  const trace = {
3052
- schemaVersion: 1,
3053
3218
  artifact: traceArtifact,
3054
3219
  eventCount: 1 + orderedSpans.length + orderedEvents.length + orderedBudget.length + orderedArtifacts.length,
3055
3220
  modelCallCount: modelSpans.length
3056
3221
  };
3057
3222
  const document = canonicalCandidateDocument({
3058
- schemaVersion: 3,
3059
3223
  kind: "agent-candidate-run",
3060
3224
  digestAlgorithm: "rfc8785-sha256",
3061
3225
  bundleDigest: state.bundle.digest,
3226
+ runCellDigest: state.executionPlan.value.material.runCell.digest,
3062
3227
  materializationReceiptDigest: state.materializationReceipt.digest,
3063
3228
  executionPlanDigest: state.executionPlan.value.digest,
3229
+ timing: {
3230
+ startedAtMs: run.startedAt,
3231
+ endedAtMs: run.endedAt,
3232
+ durationMs: run.endedAt - run.startedAt
3233
+ },
3064
3234
  memory,
3065
3235
  trace,
3066
3236
  termination,
3067
- executorCapture,
3237
+ executorCapture: evidence.persistedCapture.evidence,
3068
3238
  modelSettlement: evidence.modelSettlement,
3069
3239
  taskOutcome: evidence.taskOutcome.evidence,
3070
3240
  benchmarkResult: evidence.benchmarkResult
@@ -3080,8 +3250,8 @@ async function finalizeAgentCandidateRun(state, capture, traceStore, settlement,
3080
3250
  succeeded: true,
3081
3251
  receipt: document,
3082
3252
  artifacts: {
3083
- executorCapture,
3084
3253
  modelSettlement: evidence.modelSettlement.artifact,
3254
+ executorCapture: evidence.persistedCapture.evidence,
3085
3255
  taskOutcome: evidence.taskOutcome.evidence.artifact,
3086
3256
  benchmarkResult: evidence.benchmarkResult.artifact,
3087
3257
  runReceipt
@@ -3101,8 +3271,7 @@ async function finalizeAgentCandidateRun(state, capture, traceStore, settlement,
3101
3271
  };
3102
3272
  }
3103
3273
  }
3104
- function assertTraceBindings(tags, state) {
3105
- const expected = state.trace.tags;
3274
+ function assertTraceBindings(tags, expected) {
3106
3275
  for (const name of Object.values(CANDIDATE_TRACE_TAGS)) {
3107
3276
  if (tags?.[name] !== expected[name]) {
3108
3277
  throw new Error(`protected trace is not bound to prepared execution tag ${name}`);
@@ -3126,45 +3295,30 @@ function enforceLimits(state, startedAt, endedAt, spans, settlement) {
3126
3295
  for (const [actual, limit, label] of checks) {
3127
3296
  if (actual > limit) throw new Error(`protected ${label} ${actual} exceeds ${limit}`);
3128
3297
  }
3129
- if (settlement.costUsdNanos > usdToNanos(limits.maxCostUsd, "frozen maxCostUsd")) {
3130
- throw new Error(`protected cost USD ${usage.costUsd} exceeds ${limits.maxCostUsd}`);
3298
+ if (usage.costUsdNanos > usdToNanos(limits.maxCostUsd, "frozen maxCostUsd")) {
3299
+ throw new Error(
3300
+ `protected cost USD ${usage.costUsdNanos / 1e9} exceeds ${limits.maxCostUsd}`
3301
+ );
3131
3302
  }
3132
3303
  }
3133
- async function memoryReceipt(state, capture, outputArtifacts, protectedValues, signal) {
3304
+ async function memoryReceipt(state, capture, persisted, signal) {
3134
3305
  signal?.throwIfAborted();
3135
3306
  if (state.memory.mode === "disabled") {
3136
- if (capture.memoryAfter !== void 0) {
3307
+ if (capture.memoryAfter !== void 0 || persisted !== void 0) {
3137
3308
  throw new Error("disabled memory cannot return an after-state");
3138
3309
  }
3139
3310
  return { mode: "disabled" };
3140
3311
  }
3141
3312
  if (!capture.memoryAfter) throw new Error("isolated memory is missing its protected after-state");
3142
- const archive = Uint8Array.from(capture.memoryAfter.archive);
3143
- if (archive.byteLength === 0) throw new Error("isolated memory archive cannot be empty");
3144
- const provisional = provisionalCandidateWorkspaceSnapshot(capture.memoryAfter.afterState, archive);
3145
- const afterState = provisional.material;
3146
- assertNoProtectedBytes(provisional.manifestBytes, protectedValues);
3147
- assertNoProtectedBytes(archive, protectedValues);
3148
- const root = await mkdtemp2(join2(tmpdir2(), "agent-candidate-memory-after-"));
3149
- try {
3150
- await state.ports.workspaces.materialize({
3151
- role: "memory",
3152
- snapshot: provisional.snapshot,
3153
- archive: Uint8Array.from(archive),
3154
- destination: root
3155
- });
3156
- const files = await readMaterializedWorkspaceFiles(root, afterState);
3157
- for (const file of files) assertNoProtectedBytes(file.bytes, protectedValues);
3158
- signal?.throwIfAborted();
3159
- } finally {
3160
- await rm2(root, { recursive: true, force: true });
3161
- }
3162
- const snapshot = await persistCandidateWorkspaceSnapshot(outputArtifacts, {
3163
- executionId: state.executionId,
3164
- material: afterState,
3165
- archive,
3166
- purpose: "memory-after",
3167
- signal
3313
+ if (!persisted) throw new Error("isolated memory is missing persisted capture evidence");
3314
+ const afterState = capture.memoryAfter.afterState;
3315
+ const manifestBytes = canonicalCandidateBytes(afterState);
3316
+ const snapshot = agentCandidateWorkspaceSnapshotEvidenceSchema.parse({
3317
+ kind: "agent-candidate-workspace-snapshot",
3318
+ digest: sha256Bytes(manifestBytes),
3319
+ material: afterState,
3320
+ manifest: persisted.manifest,
3321
+ archive: persisted.archive
3168
3322
  });
3169
3323
  return {
3170
3324
  mode: "isolated",
@@ -3194,19 +3348,20 @@ function compareStrings(a, b) {
3194
3348
  }
3195
3349
 
3196
3350
  // src/candidate-execution/outcome-evidence.ts
3197
- import { mkdtemp as mkdtemp3, rm as rm3 } from "fs/promises";
3198
- import { tmpdir as tmpdir3 } from "os";
3199
- import { join as join3 } from "path";
3351
+ import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
3352
+ import { tmpdir as tmpdir2 } from "os";
3353
+ import { join as join2 } from "path";
3200
3354
  import {
3201
3355
  agentCandidateBenchmarkResultEvidenceSchema,
3202
3356
  agentCandidateModelSettlementEvidenceSchema,
3203
- agentCandidateTaskOutcomeEvidenceSchema
3357
+ agentCandidateTaskOutcomeEvidenceSchema,
3358
+ agentCandidateWorkspaceSnapshotEvidenceSchema as agentCandidateWorkspaceSnapshotEvidenceSchema2
3204
3359
  } from "@tangle-network/agent-interface";
3205
3360
 
3206
3361
  // src/candidate-execution/benchmark-grader.ts
3207
3362
  async function runBoundCandidateBenchmarkGrader(input) {
3208
3363
  input.signal?.throwIfAborted();
3209
- const descriptor = snapshotGraderDescriptor(input.grader);
3364
+ const descriptor = snapshotGraderDescriptor(input.grader, input.expectedGrader);
3210
3365
  const implementationBytes = await readVerifiedArtifact(descriptor.artifact, input.artifacts);
3211
3366
  if (implementationBytes.byteLength === 0) {
3212
3367
  throw new Error("candidate benchmark grader implementation cannot be empty");
@@ -3214,6 +3369,7 @@ async function runBoundCandidateBenchmarkGrader(input) {
3214
3369
  const expectedImplementationDigest = sha256Bytes(implementationBytes);
3215
3370
  const termination = immutableCandidateValue(input.termination);
3216
3371
  const run = input.grader.run;
3372
+ const startedAtMs = Date.now();
3217
3373
  const result = await run(
3218
3374
  Object.freeze({
3219
3375
  executionId: input.executionId,
@@ -3223,6 +3379,7 @@ async function runBoundCandidateBenchmarkGrader(input) {
3223
3379
  signal: input.signal ?? new AbortController().signal
3224
3380
  })
3225
3381
  );
3382
+ const endedAtMs = Math.max(startedAtMs, Date.now());
3226
3383
  input.signal?.throwIfAborted();
3227
3384
  assertExactRunnerResult(result);
3228
3385
  const evidence = Uint8Array.from(result.evidence);
@@ -3242,19 +3399,42 @@ async function runBoundCandidateBenchmarkGrader(input) {
3242
3399
  }
3243
3400
  return Object.freeze({
3244
3401
  grader: descriptor,
3402
+ grading: immutableCandidateValue({
3403
+ usage: emptyFixedSpend(),
3404
+ timing: {
3405
+ startedAtMs,
3406
+ endedAtMs,
3407
+ durationMs: endedAtMs - startedAtMs
3408
+ }
3409
+ }),
3245
3410
  evaluation: result.evaluation,
3246
3411
  evidence
3247
3412
  });
3248
3413
  }
3249
- function snapshotGraderDescriptor(grader) {
3414
+ function snapshotGraderDescriptor(grader, expected) {
3250
3415
  if (!grader.name || !grader.version) {
3251
3416
  throw new Error("candidate benchmark grader name and version must be non-empty");
3252
3417
  }
3253
- return immutableCandidateValue({
3418
+ const actual = {
3254
3419
  name: grader.name,
3255
3420
  version: grader.version,
3421
+ format: "tangle-grader",
3256
3422
  artifact: grader.artifact
3257
- });
3423
+ };
3424
+ if (canonicalCandidateDigest(actual) !== canonicalCandidateDigest(expected)) {
3425
+ throw new Error("candidate benchmark grader does not match the signed task grader");
3426
+ }
3427
+ return immutableCandidateValue(expected);
3428
+ }
3429
+ function emptyFixedSpend() {
3430
+ return {
3431
+ inputTokens: 0,
3432
+ outputTokens: 0,
3433
+ cachedInputTokens: 0,
3434
+ reasoningTokens: 0,
3435
+ modelCalls: 0,
3436
+ costUsdNanos: 0
3437
+ };
3258
3438
  }
3259
3439
  function detachedImplementation(bytes) {
3260
3440
  const stored = Uint8Array.from(bytes);
@@ -3286,6 +3466,104 @@ function assertExactRunnerResult(value) {
3286
3466
  }
3287
3467
  }
3288
3468
 
3469
+ // src/candidate-execution/executor-capture-evidence.ts
3470
+ async function persistAgentCandidateExecutorCapture(identity, expected, capture, outputArtifacts, signal) {
3471
+ const executorEvidence = capture.evidence ? await persistCandidateOutputArtifact(outputArtifacts, {
3472
+ executionId: identity.executionId,
3473
+ purpose: "executor-native-evidence",
3474
+ bytes: capture.evidence,
3475
+ signal
3476
+ }) : void 0;
3477
+ const taskOutcome = capture.taskOutcome ? await persistTaskCapture(
3478
+ identity.executionId,
3479
+ expected,
3480
+ capture.taskOutcome,
3481
+ outputArtifacts,
3482
+ signal
3483
+ ) : void 0;
3484
+ const memoryAfter = capture.memoryAfter ? await persistWorkspaceCapture(
3485
+ identity.executionId,
3486
+ "memory-after-manifest",
3487
+ "memory-after-archive",
3488
+ capture.memoryAfter.afterState,
3489
+ capture.memoryAfter.archive,
3490
+ outputArtifacts,
3491
+ signal
3492
+ ) : void 0;
3493
+ const bytes = canonicalCandidateBytes({
3494
+ kind: "agent-candidate-executor-capture",
3495
+ executionPlanDigest: identity.executionPlanDigest,
3496
+ ...executorEvidence ? { executorEvidence } : {},
3497
+ ...taskOutcome ? { taskOutcome } : {},
3498
+ ...memoryAfter ? { memoryAfter } : {}
3499
+ });
3500
+ const evidence = await persistCandidateOutputArtifact(outputArtifacts, {
3501
+ executionId: identity.executionId,
3502
+ purpose: "executor-capture",
3503
+ bytes,
3504
+ signal
3505
+ });
3506
+ return Object.freeze({
3507
+ evidence,
3508
+ ...taskOutcome ? { taskOutcome: Object.freeze(taskOutcome) } : {},
3509
+ ...memoryAfter ? { memoryAfter: Object.freeze(memoryAfter) } : {}
3510
+ });
3511
+ }
3512
+ async function persistTaskCapture(executionId, expected, capture, outputArtifacts, signal) {
3513
+ if (capture.kind === "output") {
3514
+ if (expected.kind !== "output")
3515
+ throw new Error("captured output does not match the signed task");
3516
+ const artifact = await persistCandidateOutputArtifact(outputArtifacts, {
3517
+ executionId,
3518
+ purpose: "task-output",
3519
+ bytes: capture.bytes,
3520
+ signal
3521
+ });
3522
+ return {
3523
+ kind: "output",
3524
+ spec: { mediaType: expected.mediaType, maxBytes: expected.maxBytes },
3525
+ artifact
3526
+ };
3527
+ }
3528
+ if (expected.kind !== "workspace") {
3529
+ throw new Error("captured workspace does not match the signed task");
3530
+ }
3531
+ const workspace = await persistWorkspaceCapture(
3532
+ executionId,
3533
+ "task-manifest",
3534
+ "task-archive",
3535
+ capture.afterState,
3536
+ capture.archive,
3537
+ outputArtifacts,
3538
+ signal
3539
+ );
3540
+ const gitDiff = await persistCandidateOutputArtifact(outputArtifacts, {
3541
+ executionId,
3542
+ purpose: "task-patch",
3543
+ bytes: capture.gitDiff,
3544
+ signal
3545
+ });
3546
+ return { kind: "workspace", resultTree: capture.resultTree, ...workspace, gitDiff };
3547
+ }
3548
+ async function persistWorkspaceCapture(executionId, manifestPurpose, archivePurpose, afterState, archiveBytes, outputArtifacts, signal) {
3549
+ const manifestBytes = canonicalCandidateBytes(afterState);
3550
+ const [manifest, archive] = await Promise.all([
3551
+ persistCandidateOutputArtifact(outputArtifacts, {
3552
+ executionId,
3553
+ purpose: manifestPurpose,
3554
+ bytes: manifestBytes,
3555
+ signal
3556
+ }),
3557
+ persistCandidateOutputArtifact(outputArtifacts, {
3558
+ executionId,
3559
+ purpose: archivePurpose,
3560
+ bytes: archiveBytes,
3561
+ signal
3562
+ })
3563
+ ]);
3564
+ return { afterState, manifest, archive };
3565
+ }
3566
+
3289
3567
  // src/candidate-execution/outcome-evidence.ts
3290
3568
  async function persistCandidateModelSettlement(state, settlement, outputArtifacts) {
3291
3569
  return await persistCandidateModelSettlementEvidence(
@@ -3300,7 +3578,6 @@ async function persistCandidateModelSettlement(state, settlement, outputArtifact
3300
3578
  }
3301
3579
  async function persistCandidateModelSettlementEvidence(identity, settlement, outputArtifacts) {
3302
3580
  const material = {
3303
- schemaVersion: 2,
3304
3581
  kind: "agent-candidate-model-settlement-material",
3305
3582
  executionPlanDigest: identity.executionPlanDigest,
3306
3583
  preparationId: settlement.value.preparationId,
@@ -3321,7 +3598,7 @@ async function persistCandidateModelSettlementEvidence(identity, settlement, out
3321
3598
  reasoningTokens: call.reasoningTokens ?? 0,
3322
3599
  costUsdNanos: call.costUsdNanos
3323
3600
  })),
3324
- usage: settlement.fixedUsage
3601
+ usage: settlement.usage
3325
3602
  };
3326
3603
  const bytes = canonicalCandidateBytes(material);
3327
3604
  const digest = sha256Bytes(bytes);
@@ -3332,7 +3609,6 @@ async function persistCandidateModelSettlementEvidence(identity, settlement, out
3332
3609
  });
3333
3610
  return immutableCandidateValue(
3334
3611
  agentCandidateModelSettlementEvidenceSchema.parse({
3335
- schemaVersion: 2,
3336
3612
  kind: "agent-candidate-model-settlement",
3337
3613
  digest,
3338
3614
  material,
@@ -3340,16 +3616,53 @@ async function persistCandidateModelSettlementEvidence(identity, settlement, out
3340
3616
  })
3341
3617
  );
3342
3618
  }
3343
- async function persistVerifiedCandidateTaskOutcome(state, capture, outputArtifacts, protectedValues, signal) {
3619
+ async function persistVerifiedAgentCandidateExecutorCapture(state, capture, outputArtifacts, protectedValues, signal) {
3344
3620
  signal?.throwIfAborted();
3621
+ const taskCapture = capture.taskOutcome;
3622
+ if (!taskCapture) throw new Error("candidate final capture is missing the task outcome");
3623
+ if (capture.evidence) assertNoProtectedBytes(capture.evidence, protectedValues);
3624
+ const expected = state.benchmarkTask.outcome;
3625
+ if (taskCapture.kind !== expected.kind) {
3626
+ throw new Error(
3627
+ `candidate captured ${taskCapture.kind} outcome does not match expected ${expected.kind} outcome`
3628
+ );
3629
+ }
3630
+ const verified = taskCapture.kind === "output" && expected.kind === "output" ? verifyOutputTaskCapture(taskCapture.bytes, expected, protectedValues) : taskCapture.kind === "workspace" && expected.kind === "workspace" ? await verifyWorkspaceTaskCapture(state, taskCapture, protectedValues, signal) : void 0;
3631
+ if (!verified) throw new Error("candidate task outcome kind could not be narrowed");
3632
+ await verifyMemoryCapture(state, capture, protectedValues, signal);
3633
+ const persistedCapture = await persistAgentCandidateExecutorCapture(
3634
+ {
3635
+ executionId: state.executionId,
3636
+ executionPlanDigest: state.executionPlan.value.digest
3637
+ },
3638
+ expected,
3639
+ capture,
3640
+ outputArtifacts,
3641
+ signal
3642
+ );
3643
+ const persisted = persistedCapture.taskOutcome;
3644
+ if (!persisted || persisted.kind !== verified.kind) {
3645
+ throw new Error("persisted task capture kind changed");
3646
+ }
3647
+ const taskOutcome = verified.kind === "output" && persisted.kind === "output" ? await persistVerifiedOutputTaskOutcome(state, verified, persisted, outputArtifacts, signal) : verified.kind === "workspace" && persisted.kind === "workspace" ? await persistVerifiedWorkspaceTaskOutcome(
3648
+ state,
3649
+ verified,
3650
+ persisted,
3651
+ outputArtifacts,
3652
+ signal
3653
+ ) : void 0;
3654
+ if (!taskOutcome) throw new Error("persisted task outcome kind could not be narrowed");
3655
+ return Object.freeze({ persistedCapture, taskOutcome });
3656
+ }
3657
+ async function verifyWorkspaceTaskCapture(state, capture, protectedValues, signal) {
3658
+ const repository = state.benchmarkTask.repository;
3659
+ if (!repository) throw new Error("workspace task outcome is missing repository identity");
3345
3660
  const patch = Uint8Array.from(capture.gitDiff);
3346
3661
  const archive = Uint8Array.from(capture.archive);
3347
3662
  if (archive.byteLength === 0) throw new Error("candidate task archive cannot be empty");
3348
3663
  assertNoProtectedBytes(patch, protectedValues);
3349
3664
  assertNoProtectedBytes(archive, protectedValues);
3350
- const provisional = provisionalCandidateWorkspaceSnapshot(capture.afterState, archive);
3351
- const afterState = provisional.material;
3352
- const repository = workspaceTaskRepository(state.executionPlan.value.material.task);
3665
+ const afterState = immutableCandidateValue(capture.afterState);
3353
3666
  const verified = await verifyTaskOutcomePatch({
3354
3667
  repositoryRoot: state.roots.staging.taskRoot,
3355
3668
  baseCommit: repository.baseCommit,
@@ -3359,24 +3672,38 @@ async function persistVerifiedCandidateTaskOutcome(state, capture, outputArtifac
3359
3672
  afterState
3360
3673
  });
3361
3674
  signal?.throwIfAborted();
3362
- assertNoProtectedBytes(provisional.manifestBytes, protectedValues);
3363
- await verifyTaskOutcomeArchive(state, provisional.snapshot, archive, protectedValues);
3364
- signal?.throwIfAborted();
3365
- const snapshot = await persistCandidateWorkspaceSnapshot(outputArtifacts, {
3366
- executionId: state.executionId,
3675
+ const manifestBytes = canonicalCandidateBytes(afterState);
3676
+ assertNoProtectedBytes(manifestBytes, protectedValues);
3677
+ const provisionalSnapshot = agentCandidateWorkspaceSnapshotEvidenceSchema2.parse({
3678
+ kind: "agent-candidate-workspace-snapshot",
3679
+ digest: sha256Bytes(manifestBytes),
3367
3680
  material: afterState,
3368
- archive,
3369
- purpose: "task",
3370
- signal
3681
+ manifest: embeddedCandidateArtifact(manifestBytes),
3682
+ archive: embeddedCandidateArtifact(archive)
3371
3683
  });
3372
- const gitDiff = await persistCandidateOutputArtifact(outputArtifacts, {
3373
- executionId: state.executionId,
3374
- purpose: "task-patch",
3375
- bytes: patch,
3376
- signal
3684
+ await verifyTaskOutcomeArchive(state, provisionalSnapshot, archive, protectedValues);
3685
+ signal?.throwIfAborted();
3686
+ return {
3687
+ kind: "workspace",
3688
+ patch,
3689
+ archive,
3690
+ afterState,
3691
+ manifestBytes,
3692
+ resultCommit: verified.resultCommit,
3693
+ resultTree: verified.resultTree,
3694
+ repository
3695
+ };
3696
+ }
3697
+ async function persistVerifiedWorkspaceTaskOutcome(state, verified, persisted, outputArtifacts, signal) {
3698
+ const { afterState, manifestBytes, patch, repository } = verified;
3699
+ const snapshot = agentCandidateWorkspaceSnapshotEvidenceSchema2.parse({
3700
+ kind: "agent-candidate-workspace-snapshot",
3701
+ digest: sha256Bytes(manifestBytes),
3702
+ material: afterState,
3703
+ manifest: persisted.manifest,
3704
+ archive: persisted.archive
3377
3705
  });
3378
3706
  const material = {
3379
- schemaVersion: 2,
3380
3707
  kind: "agent-candidate-task-outcome-material",
3381
3708
  executionPlanDigest: state.executionPlan.value.digest,
3382
3709
  outcome: {
@@ -3396,10 +3723,60 @@ async function persistVerifiedCandidateTaskOutcome(state, capture, outputArtifac
3396
3723
  afterState: snapshot,
3397
3724
  gitDiff: {
3398
3725
  format: "git-diff-binary",
3399
- artifact: gitDiff
3726
+ artifact: persisted.gitDiff
3400
3727
  }
3401
3728
  }
3402
3729
  };
3730
+ const evidence = await persistTaskOutcomeEvidence(state, material, outputArtifacts, signal);
3731
+ const storedPatch = Uint8Array.from(patch);
3732
+ return Object.freeze({
3733
+ kind: "workspace",
3734
+ evidence,
3735
+ get patch() {
3736
+ return Uint8Array.from(storedPatch);
3737
+ },
3738
+ [verifiedTaskOutcomeBrand]: true
3739
+ });
3740
+ }
3741
+ function verifyOutputTaskCapture(capturedBytes, expected, protectedValues) {
3742
+ if (capturedBytes.byteLength === 0) throw new Error("candidate task output cannot be empty");
3743
+ if (capturedBytes.byteLength > expected.maxBytes) {
3744
+ throw new Error(`candidate task output exceeds the frozen ${expected.maxBytes}-byte maximum`);
3745
+ }
3746
+ const output = Uint8Array.from(capturedBytes);
3747
+ assertNoProtectedBytes(output, protectedValues);
3748
+ return {
3749
+ kind: "output",
3750
+ bytes: output,
3751
+ spec: { mediaType: expected.mediaType, maxBytes: expected.maxBytes }
3752
+ };
3753
+ }
3754
+ async function persistVerifiedOutputTaskOutcome(state, verified, persisted, outputArtifacts, signal) {
3755
+ if (persisted.spec.mediaType !== verified.spec.mediaType || persisted.spec.maxBytes !== verified.spec.maxBytes) {
3756
+ throw new Error("persisted task output changed the signed media constraints");
3757
+ }
3758
+ const material = {
3759
+ kind: "agent-candidate-task-outcome-material",
3760
+ executionPlanDigest: state.executionPlan.value.digest,
3761
+ outcome: {
3762
+ kind: "output",
3763
+ spec: verified.spec,
3764
+ artifact: persisted.artifact
3765
+ }
3766
+ };
3767
+ const evidence = await persistTaskOutcomeEvidence(state, material, outputArtifacts, signal);
3768
+ const storedOutput = Uint8Array.from(verified.bytes);
3769
+ return Object.freeze({
3770
+ kind: "output",
3771
+ evidence,
3772
+ spec: immutableCandidateValue(verified.spec),
3773
+ get bytes() {
3774
+ return Uint8Array.from(storedOutput);
3775
+ },
3776
+ [verifiedTaskOutcomeBrand]: true
3777
+ });
3778
+ }
3779
+ async function persistTaskOutcomeEvidence(state, material, outputArtifacts, signal) {
3403
3780
  const bytes = canonicalCandidateBytes(material);
3404
3781
  const digest = sha256Bytes(bytes);
3405
3782
  const artifact = await persistCandidateOutputArtifact(outputArtifacts, {
@@ -3408,23 +3785,14 @@ async function persistVerifiedCandidateTaskOutcome(state, capture, outputArtifac
3408
3785
  bytes,
3409
3786
  signal
3410
3787
  });
3411
- const evidence = immutableCandidateValue(
3788
+ return immutableCandidateValue(
3412
3789
  agentCandidateTaskOutcomeEvidenceSchema.parse({
3413
- schemaVersion: 2,
3414
3790
  kind: "agent-candidate-task-outcome",
3415
3791
  digest,
3416
3792
  material,
3417
3793
  artifact
3418
3794
  })
3419
3795
  );
3420
- const storedPatch = Uint8Array.from(patch);
3421
- return Object.freeze({
3422
- evidence,
3423
- get patch() {
3424
- return Uint8Array.from(storedPatch);
3425
- },
3426
- [verifiedTaskOutcomeBrand]: true
3427
- });
3428
3796
  }
3429
3797
  async function persistCandidateBenchmarkResult(state, termination, outcome, grader, outputArtifacts, protectedValues, signal) {
3430
3798
  signal?.throwIfAborted();
@@ -3433,6 +3801,7 @@ async function persistCandidateBenchmarkResult(state, termination, outcome, grad
3433
3801
  executionId: state.executionId,
3434
3802
  termination: frozenTermination,
3435
3803
  outcome,
3804
+ expectedGrader: state.benchmarkTask.grader,
3436
3805
  grader,
3437
3806
  artifacts: outputArtifacts,
3438
3807
  signal
@@ -3448,24 +3817,13 @@ async function persistCandidateBenchmarkResult(state, termination, outcome, grad
3448
3817
  bytes: rawEvidence,
3449
3818
  signal
3450
3819
  });
3451
- const task = state.executionPlan.value.material.task;
3452
3820
  const material = {
3453
- schemaVersion: 1,
3454
3821
  kind: "agent-candidate-benchmark-result-material",
3455
3822
  executionPlanDigest: state.executionPlan.value.digest,
3456
3823
  taskOutcomeDigest: outcome.evidence.digest,
3457
- benchmark: {
3458
- name: task.benchmark,
3459
- version: task.benchmarkVersion,
3460
- taskId: task.taskId,
3461
- splitDigest: task.splitDigest
3462
- },
3463
- grader: {
3464
- name: graded.grader.name,
3465
- version: graded.grader.version,
3466
- artifact: graded.grader.artifact
3467
- },
3824
+ grader: graded.grader,
3468
3825
  evidence: evidenceRef,
3826
+ grading: graded.grading,
3469
3827
  score: evaluation.score,
3470
3828
  passed: evaluation.passed,
3471
3829
  dimensions: evaluation.dimensions
@@ -3480,7 +3838,6 @@ async function persistCandidateBenchmarkResult(state, termination, outcome, grad
3480
3838
  });
3481
3839
  return immutableCandidateValue(
3482
3840
  agentCandidateBenchmarkResultEvidenceSchema.parse({
3483
- schemaVersion: 1,
3484
3841
  kind: "agent-candidate-benchmark-result",
3485
3842
  digest,
3486
3843
  material,
@@ -3489,7 +3846,7 @@ async function persistCandidateBenchmarkResult(state, termination, outcome, grad
3489
3846
  );
3490
3847
  }
3491
3848
  async function verifyTaskOutcomeArchive(state, snapshot, archive, protectedValues) {
3492
- const root = await mkdtemp3(join3(tmpdir3(), "agent-candidate-task-archive-"));
3849
+ const root = await mkdtemp2(join2(tmpdir2(), "agent-candidate-task-archive-"));
3493
3850
  try {
3494
3851
  await state.ports.workspaces.materialize({
3495
3852
  role: "task",
@@ -3500,7 +3857,40 @@ async function verifyTaskOutcomeArchive(state, snapshot, archive, protectedValue
3500
3857
  const files = await readMaterializedWorkspaceFiles(root, snapshot.material);
3501
3858
  for (const file of files) assertNoProtectedBytes(file.bytes, protectedValues);
3502
3859
  } finally {
3503
- await rm3(root, { recursive: true, force: true });
3860
+ await rm2(root, { recursive: true, force: true });
3861
+ }
3862
+ }
3863
+ async function verifyMemoryCapture(state, capture, protectedValues, signal) {
3864
+ if (state.memory.mode === "disabled") {
3865
+ if (capture.memoryAfter) throw new Error("disabled memory cannot return an after-state");
3866
+ return;
3867
+ }
3868
+ const memory = capture.memoryAfter;
3869
+ if (!memory) throw new Error("isolated memory is missing its protected after-state");
3870
+ if (memory.archive.byteLength === 0) throw new Error("isolated memory archive cannot be empty");
3871
+ const manifestBytes = canonicalCandidateBytes(memory.afterState);
3872
+ assertNoProtectedBytes(manifestBytes, protectedValues);
3873
+ assertNoProtectedBytes(memory.archive, protectedValues);
3874
+ const snapshot = agentCandidateWorkspaceSnapshotEvidenceSchema2.parse({
3875
+ kind: "agent-candidate-workspace-snapshot",
3876
+ digest: sha256Bytes(manifestBytes),
3877
+ material: memory.afterState,
3878
+ manifest: embeddedCandidateArtifact(manifestBytes),
3879
+ archive: embeddedCandidateArtifact(memory.archive)
3880
+ });
3881
+ const root = await mkdtemp2(join2(tmpdir2(), "agent-candidate-memory-after-"));
3882
+ try {
3883
+ await state.ports.workspaces.materialize({
3884
+ role: "memory",
3885
+ snapshot,
3886
+ archive: Uint8Array.from(memory.archive),
3887
+ destination: root
3888
+ });
3889
+ const files = await readMaterializedWorkspaceFiles(root, memory.afterState);
3890
+ for (const file of files) assertNoProtectedBytes(file.bytes, protectedValues);
3891
+ signal?.throwIfAborted();
3892
+ } finally {
3893
+ await rm2(root, { recursive: true, force: true });
3504
3894
  }
3505
3895
  }
3506
3896
  function normalizeEvaluation(evaluation, termination) {
@@ -3532,81 +3922,60 @@ function assertUnitScore(value, label) {
3532
3922
  }
3533
3923
 
3534
3924
  // src/candidate-execution/protected-trace-store.ts
3535
- import { REDACTION_VERSION as REDACTION_VERSION3 } from "@tangle-network/agent-eval";
3536
- var ProtectedAgentCandidateTraceStore = class {
3537
- constructor(inner, protectedValues) {
3538
- this.inner = inner;
3539
- this.protectedValues = protectedValues;
3540
- }
3541
- inner;
3542
- protectedValues;
3543
- aggregate = {
3544
- version: REDACTION_VERSION3,
3545
- redactionCount: 0,
3546
- byRule: {}
3547
- };
3548
- report() {
3549
- return {
3550
- version: this.aggregate.version,
3551
- redactionCount: this.aggregate.redactionCount,
3552
- byRule: { ...this.aggregate.byRule }
3553
- };
3554
- }
3555
- async appendRun(run) {
3556
- await this.inner.appendRun(this.redact(run));
3557
- }
3558
- async updateRun(runId, patch) {
3559
- await this.inner.updateRun(this.redact(runId), this.redact(patch));
3560
- }
3561
- async appendSpan(span) {
3562
- if (span.kind === "llm") {
3563
- throw new Error("candidate executors cannot author protected model spans");
3925
+ function createProtectedAgentCandidateTraceAccess(inner, protectedValues) {
3926
+ const redactor = createProtectedRecordRedactor(protectedValues);
3927
+ const pendingWrites = /* @__PURE__ */ new Set();
3928
+ let acceptsWrites = true;
3929
+ const write = (operation) => {
3930
+ if (!acceptsWrites) {
3931
+ return Promise.reject(new Error("candidate trace writes are closed"));
3564
3932
  }
3565
- await this.inner.appendSpan(this.redact(span));
3566
- }
3567
- async updateSpan(spanId, patch) {
3568
- if (patch.kind === "llm") {
3569
- throw new Error("candidate executors cannot author protected model spans");
3570
- }
3571
- await this.inner.updateSpan(this.redact(spanId), this.redact(patch));
3572
- }
3573
- async appendEvent(event) {
3574
- await this.inner.appendEvent(this.redact(event));
3575
- }
3576
- async appendArtifact(artifact) {
3577
- await this.inner.appendArtifact(this.redact(artifact));
3578
- }
3579
- async appendBudgetEntry(entry) {
3580
- await this.inner.appendBudgetEntry(this.redact(entry));
3581
- }
3582
- getRun(...args) {
3583
- return this.inner.getRun(...args);
3584
- }
3585
- listRuns(...args) {
3586
- return this.inner.listRuns(...args);
3587
- }
3588
- spans(...args) {
3589
- return this.inner.spans(...args);
3590
- }
3591
- events(...args) {
3592
- return this.inner.events(...args);
3593
- }
3594
- budget(...args) {
3595
- return this.inner.budget(...args);
3596
- }
3597
- artifacts(...args) {
3598
- return this.inner.artifacts(...args);
3599
- }
3600
- redact(value) {
3601
- const redacted = redactProtectedValue(value, this.protectedValues);
3602
- this.aggregate.version = redacted.report.version;
3603
- this.aggregate.redactionCount += redacted.report.redactionCount;
3604
- for (const [rule, count] of Object.entries(redacted.report.byRule)) {
3605
- this.aggregate.byRule[rule] = (this.aggregate.byRule[rule] ?? 0) + count;
3933
+ let started;
3934
+ try {
3935
+ started = operation();
3936
+ } catch (error) {
3937
+ return Promise.reject(error);
3606
3938
  }
3607
- return redacted.value;
3608
- }
3609
- };
3939
+ let tracked;
3940
+ tracked = started.finally(() => pendingWrites.delete(tracked));
3941
+ pendingWrites.add(tracked);
3942
+ return tracked;
3943
+ };
3944
+ const store = Object.freeze({
3945
+ appendRun: (run) => write(() => inner.appendRun(redactor.record(run))),
3946
+ updateRun: (runId, patch) => write(() => inner.updateRun(redactor.identifier(runId), redactor.record(patch))),
3947
+ appendSpan: (span) => write(async () => {
3948
+ if (span.kind === "llm") {
3949
+ throw new Error("candidate executors cannot author protected model spans");
3950
+ }
3951
+ await inner.appendSpan(redactor.record(span));
3952
+ }),
3953
+ updateSpan: (spanId, patch) => write(async () => {
3954
+ if (patch.kind === "llm") {
3955
+ throw new Error("candidate executors cannot author protected model spans");
3956
+ }
3957
+ await inner.updateSpan(redactor.identifier(spanId), redactor.record(patch));
3958
+ }),
3959
+ appendEvent: (event) => write(() => inner.appendEvent(redactor.record(event))),
3960
+ appendArtifact: (artifact) => write(() => inner.appendArtifact(redactor.record(artifact))),
3961
+ appendBudgetEntry: (entry) => write(() => inner.appendBudgetEntry(redactor.record(entry))),
3962
+ getRun: async (runId) => inner.getRun(redactor.identifier(runId)),
3963
+ listRuns: async (filter) => inner.listRuns(filter ? redactor.query(filter) : void 0),
3964
+ spans: async (filter) => inner.spans(filter ? redactor.query(filter) : void 0),
3965
+ events: async (filter) => inner.events(filter ? redactor.query(filter) : void 0),
3966
+ budget: async (runId) => inner.budget(redactor.identifier(runId)),
3967
+ artifacts: async (runId) => inner.artifacts(redactor.identifier(runId))
3968
+ });
3969
+ return Object.freeze({
3970
+ store,
3971
+ closeWrites: async () => {
3972
+ acceptsWrites = false;
3973
+ await Promise.all([...pendingWrites]);
3974
+ },
3975
+ identity: (trace) => redactor.traceIdentity(trace),
3976
+ report: () => redactor.report()
3977
+ });
3978
+ }
3610
3979
  var RecoveryAgentCandidateTraceStore = class {
3611
3980
  constructor(inner) {
3612
3981
  this.inner = inner;
@@ -3683,9 +4052,29 @@ async function executePreparedAgentCandidate(prepared, options) {
3683
4052
  } catch (error) {
3684
4053
  return await failBeforeActivation(prepared, state, error, "failed", cleanupTimeoutMs);
3685
4054
  }
4055
+ let preparationEvidence;
4056
+ try {
4057
+ const [executionPlan, materializationReceipt] = await Promise.all([
4058
+ persistCandidateOutputArtifact(options.outputArtifacts, {
4059
+ executionId: state.executionId,
4060
+ purpose: "execution-plan",
4061
+ bytes: state.executionPlan.bytes
4062
+ }),
4063
+ persistCandidateOutputArtifact(options.outputArtifacts, {
4064
+ executionId: state.executionId,
4065
+ purpose: "materialization-receipt",
4066
+ bytes: state.materializationReceipt.bytes
4067
+ })
4068
+ ]);
4069
+ preparationEvidence = Object.freeze({ executionPlan, materializationReceipt });
4070
+ } catch (error) {
4071
+ return await failBeforeActivation(prepared, state, error, "failed", cleanupTimeoutMs);
4072
+ }
3686
4073
  let acquired;
3687
4074
  try {
3688
- acquired = await options.claimStore.tryClaim(candidateExecutionClaim(prepared));
4075
+ acquired = await options.claimStore.tryClaim(
4076
+ candidateExecutionClaim(prepared, preparationEvidence)
4077
+ );
3689
4078
  } catch (error) {
3690
4079
  return await failBeforeActivation(prepared, state, error, "failed", cleanupTimeoutMs);
3691
4080
  }
@@ -3830,25 +4219,36 @@ async function executePreparedAgentCandidate(prepared, options) {
3830
4219
  );
3831
4220
  }
3832
4221
  const protectedValues = protectedEnvironmentValues(activation, memoryActivation);
3833
- const protectedTraceStore = new ProtectedAgentCandidateTraceStore(
4222
+ const protectedTrace = createProtectedAgentCandidateTraceAccess(
3834
4223
  options.traceStore,
3835
4224
  protectedValues
3836
4225
  );
4226
+ const persistedTrace = protectedTrace.identity(state.trace);
3837
4227
  const execution = await runAndStopExecutor(
3838
4228
  options.executor,
3839
4229
  request,
3840
- protectedTraceStore,
4230
+ state.benchmarkTask.outcome,
4231
+ protectedTrace.store,
3841
4232
  deadlineAtMs,
3842
4233
  cleanupTimeoutMs
3843
4234
  );
3844
4235
  beginPreparedCandidateSettlement(prepared);
3845
4236
  const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs);
3846
4237
  const accessReason = execution.kind === "timeout" || execution.termination?.kind === "timeout" ? "timeout" : execution.kind === "capture" ? "completed" : "failed";
3847
- const [memoryClose, settlementResult] = await Promise.all([
4238
+ const traceClosure = withinCandidateCleanupDeadline(
4239
+ () => protectedTrace.closeWrites(),
4240
+ cleanupDeadlineAtMs,
4241
+ "candidate trace write closure"
4242
+ ).then(
4243
+ () => ({ closed: true, error: void 0 }),
4244
+ (error) => ({ closed: false, error })
4245
+ );
4246
+ const [traceClose, memoryClose, settlementResult] = await Promise.all([
4247
+ traceClosure,
3848
4248
  closeMemoryAccess(state, accessReason, cleanupDeadlineAtMs),
3849
4249
  settleModelGrant(state, accessReason, cleanupDeadlineAtMs)
3850
4250
  ]);
3851
- if (!execution.processStopped || !settlementResult.settlement || !memoryClose.closed) {
4251
+ if (!execution.processStopped || !traceClose.closed || !settlementResult.settlement || !memoryClose.closed) {
3852
4252
  consumePreparedCandidateExecution(prepared, "failed");
3853
4253
  return failedAgentCandidateRun(
3854
4254
  state,
@@ -3856,6 +4256,7 @@ async function executePreparedAgentCandidate(prepared, options) {
3856
4256
  joinErrors(
3857
4257
  execution.error,
3858
4258
  !execution.processStopped ? new Error("candidate process termination is not proven") : void 0,
4259
+ traceClose.error,
3859
4260
  memoryClose.error,
3860
4261
  settlementResult.error ?? (!settlementResult.settlement ? new Error("model settlement failed") : void 0),
3861
4262
  new Error("candidate claim remains recoverable until protected cleanup is proven")
@@ -3922,12 +4323,12 @@ async function executePreparedAgentCandidate(prepared, options) {
3922
4323
  async (signal) => {
3923
4324
  await appendAuthoritativeModelSettlementSpans(
3924
4325
  options.traceStore,
3925
- state.trace.runId,
4326
+ persistedTrace.runId,
3926
4327
  settlementResult.settlement
3927
4328
  );
3928
- const taskOutcome = await persistVerifiedCandidateTaskOutcome(
4329
+ const { persistedCapture, taskOutcome } = await persistVerifiedAgentCandidateExecutorCapture(
3929
4330
  state,
3930
- execution.finalCapture.taskOutcome,
4331
+ execution.finalCapture,
3931
4332
  options.outputArtifacts,
3932
4333
  protectedValues,
3933
4334
  signal
@@ -3945,16 +4346,18 @@ async function executePreparedAgentCandidate(prepared, options) {
3945
4346
  state,
3946
4347
  capture,
3947
4348
  options.traceStore,
4349
+ persistedTrace,
3948
4350
  settlementResult.settlement,
3949
4351
  {
3950
4352
  finalCapture: execution.finalCapture,
4353
+ persistedCapture,
3951
4354
  modelSettlement,
3952
4355
  taskOutcome,
3953
4356
  benchmarkResult,
3954
4357
  outputArtifacts: options.outputArtifacts
3955
4358
  },
3956
4359
  protectedValues,
3957
- protectedTraceStore.report(),
4360
+ protectedTrace.report(),
3958
4361
  signal
3959
4362
  );
3960
4363
  },
@@ -3973,18 +4376,16 @@ async function executePreparedAgentCandidate(prepared, options) {
3973
4376
  let terminal;
3974
4377
  try {
3975
4378
  terminal = result.succeeded ? {
3976
- schemaVersion: 1,
3977
4379
  status: "succeeded",
3978
- usage: settlementResult.settlement.fixedUsage,
4380
+ usage: settlementResult.settlement.usage,
3979
4381
  modelSettlement: result.artifacts.modelSettlement,
3980
4382
  taskOutcome: result.artifacts.taskOutcome,
3981
4383
  benchmarkResult: result.artifacts.benchmarkResult,
3982
4384
  runReceipt: result.artifacts.runReceipt
3983
4385
  } : {
3984
- schemaVersion: 1,
3985
4386
  status: "failed",
3986
4387
  failureClass,
3987
- usage: settlementResult.settlement.fixedUsage,
4388
+ usage: settlementResult.settlement.usage,
3988
4389
  modelSettlement: modelSettlement.artifact,
3989
4390
  failureEvidence: await withinCandidateCleanupDeadline(
3990
4391
  () => persistFailureEvidence(
@@ -4031,7 +4432,7 @@ async function executePreparedAgentCandidate(prepared, options) {
4031
4432
  settlementResult.settlement.usage
4032
4433
  );
4033
4434
  }
4034
- async function runAndStopExecutor(executor, request, traceStore, deadlineAtMs, cleanupTimeoutMs) {
4435
+ async function runAndStopExecutor(executor, request, expectedOutcome, traceStore, deadlineAtMs, cleanupTimeoutMs) {
4035
4436
  const timeoutMs = request.hardLimits.timeoutMs;
4036
4437
  const timeoutError = new CandidateExecutionDeadlineError(timeoutMs);
4037
4438
  if (Date.now() >= deadlineAtMs) {
@@ -4086,10 +4487,10 @@ async function runAndStopExecutor(executor, request, traceStore, deadlineAtMs, c
4086
4487
  }
4087
4488
  if (!capture || timedOut) controller.abort(executionError);
4088
4489
  const stopReason = timedOut ? "timeout" : capture ? "completed" : "failed";
4089
- let stopped;
4490
+ const cleanupDeadlineAtMs = Date.now() + cleanupTimeoutMs;
4090
4491
  try {
4091
- stopped = await withinCandidateCleanupDeadline(
4092
- () => executor.stopAndCapture(
4492
+ const stopped = await withinCandidateCleanupDeadline(
4493
+ (cleanupSignal) => executor.stop(
4093
4494
  {
4094
4495
  executionId: request.executionId,
4095
4496
  executionPlanDigest: request.executionPlan.value.digest
@@ -4097,35 +4498,65 @@ async function runAndStopExecutor(executor, request, traceStore, deadlineAtMs, c
4097
4498
  {
4098
4499
  traceStore,
4099
4500
  reason: stopReason,
4100
- signal: controller.signal,
4101
- deadlineAtMs
4501
+ signal: cleanupSignal,
4502
+ deadlineAtMs: cleanupDeadlineAtMs
4102
4503
  }
4103
4504
  ),
4104
- Date.now() + cleanupTimeoutMs,
4505
+ cleanupDeadlineAtMs,
4105
4506
  "candidate process termination"
4106
4507
  );
4107
- if (!stopped || typeof stopped !== "object" || stopped.stopped !== true) {
4108
- throw new Error("candidate executor did not acknowledge exact process termination");
4109
- }
4508
+ sealAgentCandidateExecutorStopAcknowledgement(stopped);
4110
4509
  } catch (stopError) {
4510
+ let disposalError2;
4511
+ try {
4512
+ await disposeExecutor(executor, request, cleanupDeadlineAtMs);
4513
+ } catch (error) {
4514
+ disposalError2 = error;
4515
+ }
4111
4516
  if (timer) clearTimeout(timer);
4112
4517
  controller.abort(stopError);
4113
4518
  return {
4114
4519
  kind: "error",
4115
- error: new Error(joinErrors(executionError, stopError)),
4520
+ error: new Error(joinErrors(executionError, stopError, disposalError2)),
4116
4521
  ...timedOut ? { termination: { kind: "timeout", timeoutMs } } : {},
4117
4522
  processStopped: false
4118
4523
  };
4119
4524
  }
4120
4525
  let finalCapture;
4526
+ let finalCaptureError;
4121
4527
  try {
4122
- finalCapture = sealAgentCandidateExecutorFinalCapture(stopped);
4528
+ finalCapture = sealAgentCandidateExecutorFinalCapture(
4529
+ await withinCandidateCleanupDeadline(
4530
+ (cleanupSignal) => executor.capture(
4531
+ {
4532
+ executionId: request.executionId,
4533
+ executionPlanDigest: request.executionPlan.value.digest
4534
+ },
4535
+ {
4536
+ traceStore,
4537
+ signal: cleanupSignal
4538
+ }
4539
+ ),
4540
+ cleanupDeadlineAtMs,
4541
+ "candidate final evidence capture"
4542
+ ),
4543
+ expectedOutcome
4544
+ );
4123
4545
  } catch (captureError) {
4546
+ finalCaptureError = captureError;
4547
+ }
4548
+ let disposalError;
4549
+ try {
4550
+ await disposeExecutor(executor, request, cleanupDeadlineAtMs);
4551
+ } catch (disposeError) {
4552
+ disposalError = disposeError;
4553
+ }
4554
+ if (finalCaptureError || disposalError || !finalCapture) {
4124
4555
  if (timer) clearTimeout(timer);
4125
- controller.abort(captureError);
4556
+ controller.abort(finalCaptureError ?? disposalError);
4126
4557
  return {
4127
4558
  kind: "error",
4128
- error: new Error(joinErrors(executionError, captureError)),
4559
+ error: new Error(joinErrors(executionError, finalCaptureError, disposalError)),
4129
4560
  ...timedOut ? { termination: { kind: "timeout", timeoutMs } } : {},
4130
4561
  processStopped: true
4131
4562
  };
@@ -4161,6 +4592,25 @@ async function runAndStopExecutor(executor, request, traceStore, deadlineAtMs, c
4161
4592
  processStopped: true
4162
4593
  };
4163
4594
  }
4595
+ async function disposeExecutor(executor, request, cleanupDeadlineAtMs) {
4596
+ const dispose = executor.dispose;
4597
+ if (!dispose) return;
4598
+ const disposed = await withinCandidateCleanupDeadline(
4599
+ (cleanupSignal) => dispose.call(
4600
+ executor,
4601
+ {
4602
+ executionId: request.executionId,
4603
+ executionPlanDigest: request.executionPlan.value.digest
4604
+ },
4605
+ { signal: cleanupSignal }
4606
+ ),
4607
+ cleanupDeadlineAtMs,
4608
+ "candidate execution resource disposal"
4609
+ );
4610
+ if (!disposed || disposed.disposed !== true || Object.keys(disposed).some((key) => key !== "disposed")) {
4611
+ throw new Error("candidate executor did not acknowledge resource disposal");
4612
+ }
4613
+ }
4164
4614
  async function failBeforeActivation(prepared, state, error, reason, cleanupTimeoutMs) {
4165
4615
  beginPreparedCandidateSettlement(prepared);
4166
4616
  const cleanupDeadlineAtMs = candidateCleanupDeadline(cleanupTimeoutMs);
@@ -4215,10 +4665,9 @@ async function failClaimedExecution(prepared, state, lease, claimStore, outputAr
4215
4665
  claimStore,
4216
4666
  lease,
4217
4667
  {
4218
- schemaVersion: 1,
4219
4668
  status: "failed",
4220
4669
  failureClass,
4221
- usage: settled.settlement.fixedUsage,
4670
+ usage: settled.settlement.usage,
4222
4671
  modelSettlement: modelSettlement.artifact,
4223
4672
  failureEvidence
4224
4673
  },
@@ -4315,7 +4764,6 @@ async function finishClaim(store, lease, terminal, deadlineAtMs) {
4315
4764
  }
4316
4765
  async function persistFailureEvidence(state, reason, failureClass, termination, outputArtifacts) {
4317
4766
  const bytes = canonicalCandidateBytes({
4318
- schemaVersion: 1,
4319
4767
  kind: "agent-candidate-execution-failure",
4320
4768
  executionId: state.executionId,
4321
4769
  bundleDigest: state.bundle.digest,
@@ -4347,10 +4795,24 @@ var CandidateExecutionDeadlineError = class extends Error {
4347
4795
  };
4348
4796
 
4349
4797
  // src/candidate-execution/verify.ts
4350
- import { agentCandidateBundleSchema as agentCandidateBundleSchema2 } from "@tangle-network/agent-interface";
4798
+ import { agentCandidateBundleSchema } from "@tangle-network/agent-interface";
4351
4799
  var verifiedCandidateState = /* @__PURE__ */ new WeakMap();
4800
+ var AGENT_CANDIDATE_EXECUTION_SUPPORT = Object.freeze({
4801
+ outcomes: Object.freeze(["workspace", "output"]),
4802
+ code: Object.freeze(["disabled", "no-op", "git-patch"]),
4803
+ memory: Object.freeze(["disabled", "isolated"]),
4804
+ knowledge: true,
4805
+ profile: Object.freeze({
4806
+ mcpTransports: Object.freeze(["stdio"]),
4807
+ remoteMcp: false,
4808
+ tools: false,
4809
+ permissions: false,
4810
+ modes: false,
4811
+ confidential: false
4812
+ })
4813
+ });
4352
4814
  async function verifyAgentCandidateBundle(input, ports) {
4353
- const parsed = agentCandidateBundleSchema2.parse(input);
4815
+ const parsed = agentCandidateBundleSchema.parse(input);
4354
4816
  const withoutDigest = omitTopLevelDigest(parsed);
4355
4817
  const actualDigest = canonicalCandidateDigest(withoutDigest);
4356
4818
  if (actualDigest !== parsed.digest) {
@@ -4358,6 +4820,7 @@ async function verifyAgentCandidateBundle(input, ports) {
4358
4820
  }
4359
4821
  const canonicalBytes = canonicalCandidateBytes(withoutDigest);
4360
4822
  verifyBytes(canonicalBytes, parsed.digest, canonicalBytes.byteLength, "candidate bundle");
4823
+ assertCandidateProfileExecutionSupport(parsed.profile);
4361
4824
  const artifactBytes = /* @__PURE__ */ new Map();
4362
4825
  const readArtifact = async (artifact) => {
4363
4826
  const key = artifactCacheKey(artifact);
@@ -4454,35 +4917,29 @@ function resourceKey(resource) {
4454
4917
  return canonicalCandidateDigest(resource);
4455
4918
  }
4456
4919
 
4457
- // src/candidate-execution/prepare.ts
4458
- import { randomBytes as randomBytes2 } from "crypto";
4459
- import { lstat as lstat2, readdir as readdir2 } from "fs/promises";
4460
- import { isAbsolute, posix, relative as relative2, resolve as resolveHostPath } from "path";
4461
- import {
4462
- agentCandidateContainerSchema,
4463
- agentCandidateExecutionLimitsSchema,
4464
- agentCandidateExecutionPlanEvidenceSchema as agentCandidateExecutionPlanEvidenceSchema2,
4465
- agentCandidateExecutionPlanMaterialSchema,
4466
- agentCandidateMaterializationReceiptSchema as agentCandidateMaterializationReceiptSchema2,
4467
- agentCandidateModelAccessNetworkSchema,
4468
- agentCandidateWorkspaceSnapshotEvidenceSchema as agentCandidateWorkspaceSnapshotEvidenceSchema2,
4469
- sha256DigestSchema
4470
- } from "@tangle-network/agent-interface";
4471
- import {
4472
- applyAgentCandidateWorkspacePlan,
4473
- materializeCandidateProfile
4474
- } from "@tangle-network/agent-profile-materialize";
4475
-
4476
4920
  // src/candidate-execution/knowledge.ts
4477
- import { mkdtemp as mkdtemp4, rm as rm4 } from "fs/promises";
4478
- import { tmpdir as tmpdir4 } from "os";
4479
- import { join as join4 } from "path";
4480
- async function prepareAgentCandidateKnowledge(candidate, ports) {
4921
+ import { mkdtemp as mkdtemp3, rm as rm3 } from "fs/promises";
4922
+ import { tmpdir as tmpdir3 } from "os";
4923
+ import { join as join3, posix } from "path";
4924
+ import { hashKnowledgeBase } from "@tangle-network/agent-knowledge";
4925
+ var CANDIDATE_KNOWLEDGE_ROOT_ENV = "TANGLE_CANDIDATE_KNOWLEDGE_ROOT";
4926
+ var CANDIDATE_KNOWLEDGE_RETRIEVAL_CONFIG_ENV = "TANGLE_CANDIDATE_KNOWLEDGE_RETRIEVAL_CONFIG";
4927
+ function candidateKnowledgeExecutionPaths(taskRoot, hasRetrievalConfig) {
4928
+ if (!posix.isAbsolute(taskRoot) || posix.normalize(taskRoot) !== taskRoot) {
4929
+ throw new Error("candidate knowledge requires a normalized absolute task root");
4930
+ }
4931
+ const parent = posix.join(taskRoot, ".tangle");
4932
+ return {
4933
+ root: posix.join(parent, "knowledge"),
4934
+ ...hasRetrievalConfig ? { retrievalConfig: posix.join(parent, "knowledge-retrieval-config.json") } : {}
4935
+ };
4936
+ }
4937
+ async function prepareAgentCandidateKnowledge(candidate, ports, arm) {
4481
4938
  const knowledge = candidate.bundle.knowledge;
4482
4939
  if (!knowledge) return void 0;
4483
4940
  const archive = await verifiedArtifactBytes(candidate, knowledge.snapshot.archive);
4484
4941
  const retrievalConfig = knowledge.retrievalConfig ? await verifiedArtifactBytes(candidate, knowledge.retrievalConfig) : void 0;
4485
- const root = await mkdtemp4(join4(tmpdir4(), "agent-candidate-knowledge-"));
4942
+ const root = await mkdtemp3(join3(tmpdir3(), "agent-candidate-knowledge-"));
4486
4943
  try {
4487
4944
  await ports.workspaces.materialize({
4488
4945
  role: "knowledge",
@@ -4490,6 +4947,13 @@ async function prepareAgentCandidateKnowledge(candidate, ports) {
4490
4947
  archive,
4491
4948
  destination: root
4492
4949
  });
4950
+ const expectedHash = arm === "baseline" ? knowledge.candidate.baseHash : knowledge.candidate.candidateHash;
4951
+ const actualHash = `sha256:${await hashKnowledgeBase(root)}`;
4952
+ if (actualHash !== expectedHash) {
4953
+ throw new Error(
4954
+ `materialized ${arm} knowledge does not match its measured content: expected ${expectedHash}, got ${actualHash}`
4955
+ );
4956
+ }
4493
4957
  const files = await readMaterializedWorkspaceFiles(root, knowledge.snapshot.material);
4494
4958
  return Object.freeze({
4495
4959
  candidate: knowledge.candidate,
@@ -4502,25 +4966,32 @@ async function prepareAgentCandidateKnowledge(candidate, ports) {
4502
4966
  ...retrievalConfig ? { retrievalConfig: Uint8Array.from(retrievalConfig) } : {}
4503
4967
  });
4504
4968
  } finally {
4505
- await rm4(root, { recursive: true, force: true });
4969
+ await rm3(root, { recursive: true, force: true });
4506
4970
  }
4507
4971
  }
4508
4972
 
4509
4973
  // src/candidate-execution/prepare.ts
4510
- var MATERIALIZER_HARNESSES = /* @__PURE__ */ new Set([
4511
- "claude-code",
4512
- "claude",
4513
- "claudish",
4514
- "nanoclaw",
4515
- "codex",
4516
- "opencode",
4517
- "kimi-code",
4518
- "kimi",
4519
- "pi",
4520
- "gemini",
4521
- "hermes",
4522
- "openclaw"
4523
- ]);
4974
+ import { randomBytes as randomBytes3 } from "crypto";
4975
+ import { lstat as lstat2, readdir as readdir2 } from "fs/promises";
4976
+ import { isAbsolute, posix as posix2, relative as relative2, resolve as resolveHostPath } from "path";
4977
+ import {
4978
+ agentCandidateBenchmarkSuiteSchema as agentCandidateBenchmarkSuiteSchema2,
4979
+ agentCandidateBenchmarkTaskSchema as agentCandidateBenchmarkTaskSchema2,
4980
+ agentCandidateContainerSchema,
4981
+ agentCandidateExecutionLimitsSchema,
4982
+ agentCandidateExecutionPlanEvidenceSchema as agentCandidateExecutionPlanEvidenceSchema2,
4983
+ agentCandidateExecutionPlanMaterialSchema,
4984
+ agentCandidateMaterializationReceiptSchema as agentCandidateMaterializationReceiptSchema2,
4985
+ agentCandidateModelAccessNetworkSchema,
4986
+ agentCandidateRunCellSchema,
4987
+ agentCandidateTaskOutcomeSpecSchema,
4988
+ agentCandidateWorkspaceSnapshotEvidenceSchema as agentCandidateWorkspaceSnapshotEvidenceSchema3,
4989
+ sha256DigestSchema
4990
+ } from "@tangle-network/agent-interface";
4991
+ import {
4992
+ applyAgentCandidateWorkspacePlan,
4993
+ materializeCandidateProfile
4994
+ } from "@tangle-network/agent-profile-materialize";
4524
4995
  var MIN_RESERVATION_TTL_MS = 15 * 6e4;
4525
4996
  var PREPARED_HOLD_MARGIN_MS = 5 * 6e4;
4526
4997
  async function prepareAgentCandidateExecution(candidate, task, ports, options = {}) {
@@ -4528,12 +4999,23 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4528
4999
  const verifiedState = getVerifiedCandidateState(candidate);
4529
5000
  assertSameVerificationPorts(verifiedState.ports, ports);
4530
5001
  const bundle = candidate.bundle;
4531
- assertCandidateProfileExecutionSupport(bundle.profile);
4532
- const harness = materializerHarness(bundle.execution.harness);
5002
+ if (task.runCell.bundleDigest !== bundle.digest) {
5003
+ throw new Error("candidate experiment cell does not bind the verified bundle");
5004
+ }
5005
+ const benchmarkTask = task.task;
5006
+ const attempt = {
5007
+ number: task.runCell.attempt,
5008
+ maxAttempts: benchmarkTask.attempt.maxAttempts,
5009
+ retryPolicy: benchmarkTask.attempt.retryPolicy
5010
+ };
5011
+ const harness = candidateMaterializerHarness(bundle.execution.harness);
4533
5012
  assertTaskInput(task, bundle.execution.instructionDelivery);
4534
- const resultTimeoutMs = candidateResultTimeout(options.resultTimeoutMs, task.limits.timeoutMs);
5013
+ const resultTimeoutMs = candidateResultTimeout(
5014
+ options.resultTimeoutMs,
5015
+ benchmarkTask.limits.timeoutMs
5016
+ );
4535
5017
  const ownerWindowMs = candidateExecutionOwnerWindowMs(
4536
- task.limits.timeoutMs,
5018
+ benchmarkTask.limits.timeoutMs,
4537
5019
  cleanupTimeoutMs,
4538
5020
  resultTimeoutMs
4539
5021
  );
@@ -4542,22 +5024,26 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4542
5024
  throw new Error("candidate reservation window exceeds the supported timer range");
4543
5025
  }
4544
5026
  assertDisjointHostStagingRoots(task);
4545
- const instructionBytes = Buffer.from(task.instruction, "utf8");
4546
- const instructionDigest = sha256Bytes(instructionBytes);
4547
- const taskArtifacts = await verifyWorkspaceSnapshotArtifacts(task.workspace, ports.artifacts);
5027
+ const instructionBytes = Buffer.from(benchmarkTask.instruction, "utf8");
5028
+ const taskArtifacts = await verifyWorkspaceSnapshotArtifacts(
5029
+ benchmarkTask.workspace,
5030
+ ports.artifacts
5031
+ );
4548
5032
  await ports.workspaces.materialize({
4549
5033
  role: "task",
4550
- snapshot: task.workspace,
5034
+ snapshot: benchmarkTask.workspace,
4551
5035
  archive: taskArtifacts.archive,
4552
5036
  destination: task.stagingRoots.taskRoot
4553
5037
  });
4554
- await verifyMaterializedWorkspace(task.stagingRoots.taskRoot, task.workspace.material, {
5038
+ await verifyMaterializedWorkspace(task.stagingRoots.taskRoot, benchmarkTask.workspace.material, {
4555
5039
  ignoredProtectedRootEntries: [".git", ".sidecar"]
4556
5040
  });
4557
- await verifyTaskCheckout(task.stagingRoots.taskRoot, task.repository);
5041
+ if (benchmarkTask.repository) {
5042
+ await verifyTaskCheckout(task.stagingRoots.taskRoot, benchmarkTask.repository);
5043
+ }
4558
5044
  const taskExecutorFiles = await readMaterializedWorkspaceFiles(
4559
5045
  task.stagingRoots.taskRoot,
4560
- task.workspace.material,
5046
+ benchmarkTask.workspace.material,
4561
5047
  { ignoredProtectedRootEntries: [".git", ".sidecar"] }
4562
5048
  );
4563
5049
  let candidateArchive;
@@ -4595,30 +5081,33 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4595
5081
  );
4596
5082
  await verifyMaterializedProfileWorkspace(
4597
5083
  task.stagingRoots.profileRoot,
4598
- profileApplication.profilePlan.material
5084
+ profileApplication.profileActivation.profilePlan.material
4599
5085
  );
4600
5086
  const profilePlanBytes = await readVerifiedArtifact(
4601
- profileApplication.profilePlan.artifact,
5087
+ profileApplication.profileActivation.profilePlan.artifact,
4602
5088
  ports.artifacts
4603
5089
  );
4604
5090
  if (!Buffer.from(profilePlanBytes).equals(
4605
- Buffer.from(canonicalCandidateBytes(profileApplication.profilePlan.material))
5091
+ Buffer.from(
5092
+ canonicalCandidateBytes(profileApplication.profileActivation.profilePlan.material)
5093
+ )
4606
5094
  )) {
4607
5095
  throw new Error("profile materializer did not capture exact canonical plan bytes");
4608
5096
  }
5097
+ const profileActivation = profileApplication.profileActivation;
4609
5098
  const container = await resolveContainer(candidate, task, ports);
4610
5099
  const resolvedModel = await resolveModel(candidate, task, ports);
4611
- const preparationId = `candidate-preparation-v1.${randomBytes2(32).toString("base64url")}`;
5100
+ const preparationId = `candidate-preparation.${randomBytes3(32).toString("base64url")}`;
4612
5101
  const reservationExpiresAtMs = Date.now() + Math.max(MIN_RESERVATION_TTL_MS, reservationWindowMs);
4613
5102
  const modelReservation = await withinCandidateCleanupDeadline(
4614
5103
  () => ports.models.reserveGrant({
4615
5104
  executionId: task.executionId,
4616
5105
  preparationId,
4617
5106
  expiresAtMs: reservationExpiresAtMs,
4618
- attempt: task.attempt,
5107
+ attempt,
4619
5108
  bundleDigest: bundle.digest,
4620
5109
  resolved: resolvedModel,
4621
- limits: modelLimits2(task.limits)
5110
+ limits: modelLimits2(benchmarkTask.limits)
4622
5111
  }),
4623
5112
  candidateCleanupDeadline(cleanupTimeoutMs),
4624
5113
  "protected model reservation"
@@ -4627,7 +5116,7 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4627
5116
  try {
4628
5117
  validateProtectedModelReservation(
4629
5118
  modelReservation,
4630
- task.limits,
5119
+ benchmarkTask.limits,
4631
5120
  preparationId,
4632
5121
  reservationExpiresAtMs
4633
5122
  );
@@ -4640,7 +5129,11 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4640
5129
  cleanupTimeoutMs
4641
5130
  );
4642
5131
  const memory = preparedMemory.value;
4643
- const knowledge = await prepareAgentCandidateKnowledge(candidate, ports);
5132
+ const knowledge = await prepareAgentCandidateKnowledge(candidate, ports, task.runCell.arm);
5133
+ const knowledgePaths = knowledge ? candidateKnowledgeExecutionPaths(
5134
+ task.executionRoots.taskRoot,
5135
+ knowledge.retrievalConfig !== void 0
5136
+ ) : void 0;
4644
5137
  const baseLaunch = buildLaunch(candidate, task, profileApplication.flags);
4645
5138
  const publicEnv = mergePublicEnvironment(
4646
5139
  bundle.execution.env ?? {},
@@ -4650,30 +5143,25 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4650
5143
  kind: "public",
4651
5144
  value: bundle.execution.instructionDelivery.path
4652
5145
  }
5146
+ } : {},
5147
+ knowledgePaths ? {
5148
+ [CANDIDATE_KNOWLEDGE_ROOT_ENV]: {
5149
+ kind: "public",
5150
+ value: knowledgePaths.root
5151
+ },
5152
+ ...knowledgePaths.retrievalConfig ? {
5153
+ [CANDIDATE_KNOWLEDGE_RETRIEVAL_CONFIG_ENV]: {
5154
+ kind: "public",
5155
+ value: knowledgePaths.retrievalConfig
5156
+ }
5157
+ } : {}
4653
5158
  } : {}
4654
5159
  );
4655
- const routes = modelRoutes(bundle.profile, task.model.requested);
5160
+ const routes = modelRoutes(bundle.profile, benchmarkTask.model.requested);
4656
5161
  const executionMaterial = {
4657
- schemaVersion: 2,
4658
5162
  kind: "agent-candidate-execution-plan-material",
4659
- bundleDigest: bundle.digest,
5163
+ runCell: task.runCell,
4660
5164
  executionId: task.executionId,
4661
- attempt: task.attempt,
4662
- task: {
4663
- benchmark: task.benchmark,
4664
- benchmarkVersion: task.benchmarkVersion,
4665
- taskId: task.taskId,
4666
- splitDigest: task.splitDigest,
4667
- instruction: {
4668
- encoding: "utf8",
4669
- sha256: instructionDigest,
4670
- byteLength: instructionBytes.byteLength,
4671
- delivery: bundle.execution.instructionDelivery
4672
- },
4673
- repository: task.repository,
4674
- outcome: { kind: "workspace" },
4675
- workspace: task.workspace
4676
- },
4677
5165
  workspaces: {
4678
5166
  taskRoot: task.executionRoots.taskRoot,
4679
5167
  ...task.executionRoots.candidateRoot ? { candidateRoot: task.executionRoots.candidateRoot } : {}
@@ -4683,6 +5171,8 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4683
5171
  profile: profileApplication.application,
4684
5172
  harness: bundle.execution.harness,
4685
5173
  harnessVersion: bundle.execution.harnessVersion,
5174
+ instructionDelivery: bundle.execution.instructionDelivery,
5175
+ limits: benchmarkTask.limits,
4686
5176
  container,
4687
5177
  model: {
4688
5178
  policy: "single",
@@ -4694,7 +5184,6 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4694
5184
  },
4695
5185
  routes
4696
5186
  },
4697
- grader: task.grader,
4698
5187
  launch: {
4699
5188
  executable: baseLaunch.executable,
4700
5189
  args: baseLaunch.args,
@@ -4703,7 +5192,6 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4703
5192
  },
4704
5193
  ...bundle.knowledge ? { knowledgeManifestDigest: bundle.knowledge.snapshot.digest } : {},
4705
5194
  memory,
4706
- limits: task.limits,
4707
5195
  network: { mode: "disabled" }
4708
5196
  };
4709
5197
  agentCandidateExecutionPlanMaterialSchema.parse(executionMaterial);
@@ -4713,7 +5201,6 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4713
5201
  throw new Error("execution plan canonical serializers disagree");
4714
5202
  }
4715
5203
  const executionPlan = agentCandidateExecutionPlanEvidenceSchema2.parse({
4716
- schemaVersion: 2,
4717
5204
  kind: "agent-candidate-execution-plan",
4718
5205
  digest: executionDigest,
4719
5206
  material: executionMaterial,
@@ -4722,11 +5209,24 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4722
5209
  const entrypoint = candidateEntrypointReceipt(candidate);
4723
5210
  const materializationReceipt = canonicalCandidateDocument(
4724
5211
  {
4725
- schemaVersion: 2,
4726
5212
  kind: "agent-candidate-materialization",
4727
5213
  digestAlgorithm: "rfc8785-sha256",
4728
5214
  bundleDigest: bundle.digest,
4729
- profilePlan: profileApplication.profilePlan,
5215
+ benchmark: {
5216
+ suite: {
5217
+ digest: task.benchmarkSuite.digest,
5218
+ material: embeddedCandidateArtifact(
5219
+ canonicalCandidateBytes(omitTopLevelDigest(task.benchmarkSuite))
5220
+ )
5221
+ },
5222
+ task: {
5223
+ digest: benchmarkTask.digest,
5224
+ material: embeddedCandidateArtifact(
5225
+ canonicalCandidateBytes(omitTopLevelDigest(benchmarkTask))
5226
+ )
5227
+ }
5228
+ },
5229
+ profileActivation,
4730
5230
  executionPlan,
4731
5231
  ...bundle.execution.workspace ? { candidateWorkspace: bundle.execution.workspace } : {},
4732
5232
  codeKind: bundle.code.kind,
@@ -4740,7 +5240,7 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4740
5240
  }
4741
5241
  );
4742
5242
  agentCandidateMaterializationReceiptSchema2.parse(materializationReceipt.value);
4743
- const traceRunId = `${task.executionId}:attempt-${task.attempt.number}:${canonicalCandidateDigest({ preparationId }).slice(7, 23)}`;
5243
+ const traceRunId = `${task.executionId}:attempt-${attempt.number}:${canonicalCandidateDigest({ preparationId }).slice(7, 23)}`;
4744
5244
  const traceTags = {
4745
5245
  [CANDIDATE_TRACE_TAGS.executionId]: task.executionId,
4746
5246
  [CANDIDATE_TRACE_TAGS.bundleDigest]: bundle.digest,
@@ -4758,16 +5258,19 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4758
5258
  return createPreparedCandidateExecution({
4759
5259
  ports,
4760
5260
  bundle,
5261
+ benchmarkSuite: task.benchmarkSuite,
5262
+ benchmarkTask,
4761
5263
  executionId: task.executionId,
4762
5264
  roots: {
4763
5265
  execution: { ...task.executionRoots },
4764
5266
  staging: { ...task.stagingRoots }
4765
5267
  },
4766
5268
  profilePlan: {
4767
- value: profileApplication.profilePlan,
5269
+ value: profileApplication.profileActivation.profilePlan,
4768
5270
  bytes: profilePlanBytes,
4769
5271
  written: [...profileApplication.application.mountPaths]
4770
5272
  },
5273
+ profileActivation,
4771
5274
  executionPlan: { value: executionPlan, bytes: executionBytes },
4772
5275
  materializationReceipt,
4773
5276
  launch: {
@@ -4798,7 +5301,7 @@ async function prepareAgentCandidateExecution(candidate, task, ports, options =
4798
5301
  ...candidateExecutorFiles ? { candidateFiles: candidateExecutorFiles } : {},
4799
5302
  profileFiles: exactProfileExecutorFiles(
4800
5303
  profileWorkspacePlan.files,
4801
- profileApplication.profilePlan.material.files
5304
+ profileApplication.profileActivation.profilePlan.material.files
4802
5305
  )
4803
5306
  },
4804
5307
  ...preparedMemory.accessDigest && preparedMemory.value.mode === "isolated" ? {
@@ -4883,34 +5386,54 @@ function assertSameVerificationPorts(verified, execution) {
4883
5386
  }
4884
5387
  }
4885
5388
  function assertTaskInput(task, delivery) {
5389
+ const benchmarkTask = agentCandidateBenchmarkTaskSchema2.parse(task.task);
5390
+ const benchmarkSuite = agentCandidateBenchmarkSuiteSchema2.parse(task.benchmarkSuite);
5391
+ const runCell = agentCandidateRunCellSchema.parse(task.runCell);
4886
5392
  const requiredStrings = [
4887
5393
  ["executionId", task.executionId],
4888
- ["benchmark", task.benchmark],
4889
- ["benchmarkVersion", task.benchmarkVersion],
4890
- ["taskId", task.taskId],
4891
- ["repository identity", task.repository.identity],
4892
- ["repository root identity", task.repository.rootIdentity]
5394
+ ["benchmark", benchmarkTask.benchmark.name],
5395
+ ["benchmarkVersion", benchmarkTask.benchmark.version],
5396
+ ["taskId", benchmarkTask.scenario.id]
4893
5397
  ];
5398
+ if (benchmarkTask.outcome.kind === "workspace" && !benchmarkTask.repository) {
5399
+ throw new Error("workspace task outcome requires repository identity");
5400
+ }
5401
+ if (benchmarkTask.repository) {
5402
+ requiredStrings.push(
5403
+ ["repository identity", benchmarkTask.repository.identity],
5404
+ ["repository root identity", benchmarkTask.repository.rootIdentity]
5405
+ );
5406
+ }
4894
5407
  for (const [name, value] of requiredStrings) {
4895
5408
  if (!value.trim()) throw new Error(`${name} must be non-empty`);
4896
5409
  }
4897
5410
  if (!/^[A-Za-z0-9._:-]{1,200}$/.test(task.executionId)) {
4898
5411
  throw new Error("executionId must be a stable filesystem-neutral identifier");
4899
5412
  }
4900
- if (!task.instruction || !isWellFormedUnicode(task.instruction)) {
5413
+ if (!benchmarkTask.instruction || !isWellFormedUnicode(benchmarkTask.instruction)) {
4901
5414
  throw new Error("task instruction must be non-empty well-formed Unicode");
4902
5415
  }
4903
- sha256DigestSchema.parse(task.splitDigest);
4904
- agentCandidateWorkspaceSnapshotEvidenceSchema2.parse(task.workspace);
4905
- agentCandidateExecutionLimitsSchema.parse(task.limits);
4906
- if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(task.repository.baseCommit)) {
4907
- throw new Error("task repository base commit is not a full Git object id");
5416
+ if (canonicalCandidateDigest(omitTopLevelDigest(benchmarkTask)) !== benchmarkTask.digest || canonicalCandidateDigest(omitTopLevelDigest(benchmarkSuite)) !== benchmarkSuite.digest || canonicalCandidateDigest(omitTopLevelDigest(runCell)) !== runCell.digest) {
5417
+ throw new Error("candidate experiment cell contains an invalid content digest");
4908
5418
  }
4909
- if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(task.repository.baseTree)) {
4910
- throw new Error("task repository base tree is not a full Git object id");
5419
+ const cellIndex = runCell.taskIndex * benchmarkSuite.reps + runCell.repetition;
5420
+ if (runCell.suiteDigest !== benchmarkSuite.digest || runCell.taskDigest !== benchmarkTask.digest || benchmarkSuite.taskDigests[runCell.taskIndex] !== benchmarkTask.digest || runCell.repetition >= benchmarkSuite.reps || benchmarkSuite.seeds[cellIndex] !== runCell.seed || runCell.attempt > benchmarkTask.attempt.maxAttempts) {
5421
+ throw new Error("candidate experiment cell does not match its signed suite and task");
4911
5422
  }
4912
- if (task.repository.baseCommit.length !== task.repository.baseTree.length) {
4913
- throw new Error("task repository Git object formats disagree");
5423
+ sha256DigestSchema.parse(benchmarkTask.benchmark.splitDigest);
5424
+ agentCandidateTaskOutcomeSpecSchema.parse(benchmarkTask.outcome);
5425
+ agentCandidateWorkspaceSnapshotEvidenceSchema3.parse(benchmarkTask.workspace);
5426
+ agentCandidateExecutionLimitsSchema.parse(benchmarkTask.limits);
5427
+ if (benchmarkTask.repository) {
5428
+ if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(benchmarkTask.repository.baseCommit)) {
5429
+ throw new Error("task repository base commit is not a full Git object id");
5430
+ }
5431
+ if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(benchmarkTask.repository.baseTree)) {
5432
+ throw new Error("task repository base tree is not a full Git object id");
5433
+ }
5434
+ if (benchmarkTask.repository.baseCommit.length !== benchmarkTask.repository.baseTree.length) {
5435
+ throw new Error("task repository Git object formats disagree");
5436
+ }
4914
5437
  }
4915
5438
  for (const [name, root] of [
4916
5439
  ["execution task root", task.executionRoots.taskRoot],
@@ -4920,34 +5443,36 @@ function assertTaskInput(task, delivery) {
4920
5443
  ["staging profile root", task.stagingRoots.profileRoot]
4921
5444
  ]) {
4922
5445
  if (root === void 0) continue;
4923
- const canonical = name.startsWith("execution") ? posix.isAbsolute(root) && posix.normalize(root) === root : isAbsolute(root) && resolveHostPath(root) === root;
5446
+ const canonical = name.startsWith("execution") ? posix2.isAbsolute(root) && posix2.normalize(root) === root : isAbsolute(root) && resolveHostPath(root) === root;
4924
5447
  if (!canonical) throw new Error(`${name} must be a canonical absolute path`);
4925
5448
  }
4926
- if (!Number.isInteger(task.attempt.number) || !Number.isInteger(task.attempt.maxAttempts) || task.attempt.number < 1 || task.attempt.number > task.attempt.maxAttempts || task.attempt.retryPolicy === "none" && task.attempt.maxAttempts !== 1) {
5449
+ if (!Number.isInteger(runCell.attempt) || !Number.isInteger(benchmarkTask.attempt.maxAttempts) || runCell.attempt < 1 || runCell.attempt > benchmarkTask.attempt.maxAttempts || benchmarkTask.attempt.retryPolicy === "none" && benchmarkTask.attempt.maxAttempts !== 1) {
4927
5450
  throw new Error("task attempt policy is invalid");
4928
5451
  }
4929
- const limits = task.limits;
5452
+ const limits = benchmarkTask.limits;
4930
5453
  if (!Number.isInteger(limits.timeoutMs) || limits.timeoutMs <= 0 || limits.timeoutMs > MAX_CANDIDATE_TIMER_INTERVAL_MS || !Number.isInteger(limits.maxSteps) || limits.maxSteps <= 0 || !Number.isInteger(limits.maxModelCalls) || limits.maxModelCalls < 0 || !Number.isInteger(limits.maxInputTokens) || limits.maxInputTokens < 0 || !Number.isInteger(limits.maxOutputTokens) || limits.maxOutputTokens < 0 || !Number.isFinite(limits.maxCostUsd) || limits.maxCostUsd < 0) {
4931
5454
  throw new Error("task execution limits are invalid");
4932
5455
  }
4933
5456
  usdToNanos(limits.maxCostUsd, "task maxCostUsd");
4934
- if (!task.model.requested.trim()) throw new Error("evaluator model request must be non-empty");
4935
- if (!task.grader.name.trim() || !task.grader.version.trim()) {
5457
+ if (!benchmarkTask.model.requested.trim()) {
5458
+ throw new Error("evaluator model request must be non-empty");
5459
+ }
5460
+ if (!benchmarkTask.grader.name.trim() || !benchmarkTask.grader.version.trim()) {
4936
5461
  throw new Error("evaluator benchmark grader identity must be non-empty");
4937
5462
  }
4938
- if (!Number.isInteger(task.grader.artifact.byteLength) || task.grader.artifact.byteLength <= 0) {
5463
+ if (!Number.isInteger(benchmarkTask.grader.artifact.byteLength) || benchmarkTask.grader.artifact.byteLength <= 0) {
4939
5464
  throw new Error("evaluator benchmark grader artifact must be non-empty");
4940
5465
  }
4941
- sha256DigestSchema.parse(task.grader.artifact.sha256);
4942
- if (task.evaluatorTaskContainer) {
4943
- if (task.evaluatorTaskContainer.source !== "evaluator-task-container" || !task.evaluatorTaskContainer.image.trim() || !task.evaluatorTaskContainer.platform.os.trim() || !task.evaluatorTaskContainer.platform.architecture.trim()) {
5466
+ sha256DigestSchema.parse(benchmarkTask.grader.artifact.sha256);
5467
+ if (benchmarkTask.evaluatorTaskContainer) {
5468
+ if (benchmarkTask.evaluatorTaskContainer.source !== "evaluator-task-container" || !benchmarkTask.evaluatorTaskContainer.image.trim() || !benchmarkTask.evaluatorTaskContainer.platform.os.trim() || !benchmarkTask.evaluatorTaskContainer.platform.architecture.trim()) {
4944
5469
  throw new Error("evaluator task container evidence is incomplete");
4945
5470
  }
4946
- sha256DigestSchema.parse(task.evaluatorTaskContainer.indexDigest);
4947
- sha256DigestSchema.parse(task.evaluatorTaskContainer.manifestDigest);
5471
+ sha256DigestSchema.parse(benchmarkTask.evaluatorTaskContainer.indexDigest);
5472
+ sha256DigestSchema.parse(benchmarkTask.evaluatorTaskContainer.manifestDigest);
4948
5473
  agentCandidateContainerSchema.parse({
4949
- image: task.evaluatorTaskContainer.image,
4950
- indexDigest: task.evaluatorTaskContainer.indexDigest
5474
+ image: benchmarkTask.evaluatorTaskContainer.image,
5475
+ indexDigest: benchmarkTask.evaluatorTaskContainer.indexDigest
4951
5476
  });
4952
5477
  }
4953
5478
  if (delivery.kind === "utf8-file") {
@@ -4957,8 +5482,8 @@ function assertTaskInput(task, delivery) {
4957
5482
  }
4958
5483
  }
4959
5484
  function executionPathsOverlap(left, right) {
4960
- const a = posix.normalize(left);
4961
- const b = posix.normalize(right);
5485
+ const a = posix2.normalize(left);
5486
+ const b = posix2.normalize(right);
4962
5487
  return a === b || b.startsWith(a === "/" ? "/" : `${a}/`) || a.startsWith(b === "/" ? "/" : `${b}/`);
4963
5488
  }
4964
5489
  function assertDisjointHostStagingRoots(task) {
@@ -4990,51 +5515,45 @@ async function assertEmptyDirectory(path) {
4990
5515
  throw new Error("profile staging root must be empty before materialization");
4991
5516
  }
4992
5517
  }
4993
- function materializerHarness(harness) {
4994
- if (!MATERIALIZER_HARNESSES.has(harness)) {
4995
- throw new Error(
4996
- `sealed candidate profile materialization is unsupported for harness ${harness}`
4997
- );
4998
- }
4999
- return harness;
5000
- }
5001
5518
  async function resolveContainer(candidate, task, ports) {
5002
5519
  const environment = candidate.bundle.execution.environment;
5520
+ const evaluatorTaskContainer = task.task.evaluatorTaskContainer;
5003
5521
  const pinned = environment.kind === "pinned-container" ? environment.container : void 0;
5004
- if (environment.kind === "evaluator-task-container" && !task.evaluatorTaskContainer) {
5522
+ if (environment.kind === "evaluator-task-container" && !evaluatorTaskContainer) {
5005
5523
  throw new Error("evaluator-task-container candidate requires an evaluator-owned task image");
5006
5524
  }
5007
- if (environment.kind === "pinned-container" && task.evaluatorTaskContainer) {
5525
+ if (environment.kind === "pinned-container" && evaluatorTaskContainer) {
5008
5526
  throw new Error("pinned candidate containers cannot be replaced by a task image");
5009
5527
  }
5010
5528
  const resolved = await ports.containers.resolve({
5011
5529
  candidate: pinned,
5012
- evaluatorTaskContainer: task.evaluatorTaskContainer
5530
+ evaluatorTaskContainer
5013
5531
  });
5014
5532
  if (resolved.source !== environment.kind) throw new Error("resolved container source drifted");
5015
5533
  if (pinned && (resolved.image !== pinned.image || resolved.indexDigest !== pinned.indexDigest)) {
5016
5534
  throw new Error("resolved pinned container does not match the candidate image index");
5017
5535
  }
5018
- if (task.evaluatorTaskContainer && JSON.stringify(resolved) !== JSON.stringify(task.evaluatorTaskContainer)) {
5536
+ if (evaluatorTaskContainer && canonicalCandidateDigest(resolved) !== canonicalCandidateDigest(evaluatorTaskContainer)) {
5019
5537
  throw new Error("resolved task container does not match evaluator-owned image evidence");
5020
5538
  }
5021
5539
  return resolved;
5022
5540
  }
5023
5541
  async function resolveModel(candidate, task, ports) {
5024
5542
  const hints = candidate.bundle.profile.model;
5025
- if (hints?.default !== void 0 && hints.default !== task.model.requested) {
5543
+ const expected = task.task.model;
5544
+ if (hints?.default !== void 0 && hints.default !== expected.requested) {
5026
5545
  throw new Error("candidate model preference conflicts with the evaluator-owned model");
5027
5546
  }
5028
- if (hints?.reasoningEffort !== void 0 && hints.reasoningEffort !== task.model.reasoningEffort) {
5547
+ if (hints?.reasoningEffort !== void 0 && hints.reasoningEffort !== expected.reasoningEffort) {
5029
5548
  throw new Error("candidate reasoning effort conflicts with the evaluator-owned effort");
5030
5549
  }
5031
5550
  const resolved = await ports.models.resolve({
5032
- requested: task.model.requested,
5551
+ requested: expected.requested,
5033
5552
  harness: candidate.bundle.execution.harness,
5034
- reasoningEffort: task.model.reasoningEffort
5553
+ reasoningEffort: expected.reasoningEffort
5035
5554
  });
5036
- if (resolved.requested !== task.model.requested || resolved.reasoningEffort !== task.model.reasoningEffort) {
5037
- throw new Error("model resolver drifted from the evaluator-owned request");
5555
+ if (canonicalCandidateDigest(resolved) !== canonicalCandidateDigest(expected)) {
5556
+ throw new Error("model resolver drifted from the signed benchmark model");
5038
5557
  }
5039
5558
  return resolved;
5040
5559
  }
@@ -5043,7 +5562,7 @@ async function prepareMemory(candidate, task, ports, preparationId, expiresAtMs,
5043
5562
  if (policy.mode === "disabled") return { value: { mode: "disabled" } };
5044
5563
  const seed = policy.seed ? await verifiedArtifactBytes(candidate, policy.seed) : void 0;
5045
5564
  const executionSegment = canonicalCandidateDigest({ executionId: task.executionId }).slice(7);
5046
- const taskSegment = canonicalCandidateDigest({ taskId: task.taskId }).slice(7);
5565
+ const taskSegment = canonicalCandidateDigest({ taskDigest: task.task.digest }).slice(7);
5047
5566
  const preparationSegment = canonicalCandidateDigest({ preparationId }).slice(7, 23);
5048
5567
  const effectiveNamespace = `candidate/${candidate.bundle.digest.slice(7, 23)}/${executionSegment}/${taskSegment}/${preparationSegment}`;
5049
5568
  const reset = await withinCandidateCleanupDeadline(
@@ -5110,7 +5629,7 @@ function buildLaunch(candidate, task, profileFlags) {
5110
5629
  }
5111
5630
  const candidateRoot = task.executionRoots.candidateRoot;
5112
5631
  if (!candidateRoot) throw new Error("candidate entrypoint requires a container candidate root");
5113
- const entrypoint = posix.join(candidateRoot, launch.entrypoint);
5632
+ const entrypoint = posix2.join(candidateRoot, launch.entrypoint);
5114
5633
  const candidateArgs = launch.args ?? [];
5115
5634
  if (launch.interpreter) {
5116
5635
  return {
@@ -5160,7 +5679,7 @@ function candidateEntrypointReceipt(candidate) {
5160
5679
  function absoluteExecutionCwd(cwd, roots) {
5161
5680
  const root = cwd.workspace === "task" ? roots.taskRoot : roots.candidateRoot;
5162
5681
  if (!root) throw new Error("candidate cwd is missing its execution workspace root");
5163
- const absolute = cwd.path === "." ? root : posix.join(root, cwd.path);
5682
+ const absolute = cwd.path === "." ? root : posix2.join(root, cwd.path);
5164
5683
  if (absolute !== root && !absolute.startsWith(`${root}/`)) {
5165
5684
  throw new Error("candidate cwd escapes its execution workspace");
5166
5685
  }
@@ -5236,20 +5755,20 @@ export {
5236
5755
  canonicalCandidateDigest,
5237
5756
  immutableCandidateValue,
5238
5757
  canonicalCandidateDocument,
5758
+ verifyCanonicalCandidateDocument,
5239
5759
  embeddedCandidateArtifact,
5240
5760
  omitTopLevelDigest,
5241
5761
  deepFreezeCandidate,
5242
- sealAgentCandidateBundle,
5762
+ candidateMaterializerHarness,
5763
+ createAgentCandidateProfileActivation,
5764
+ parseAgentCandidateProfileActivation,
5243
5765
  freezeGenericAgentCandidateProfile,
5244
5766
  assertCandidateProfileBinding,
5245
5767
  parseExactAgentProfile,
5246
5768
  parseExactAgentProfileDiff,
5247
5769
  applyExactAgentProfileDiff,
5248
5770
  parseExactCandidateProfile,
5249
- CLAIM_FORMAT_VERSION,
5250
- PENDING_FORMAT_VERSION,
5251
- TERMINAL_FORMAT_VERSION,
5252
- PHASE_FORMAT_VERSION,
5771
+ agentCandidateProfileAsAgentProfile,
5253
5772
  assertExactObjectKeys,
5254
5773
  terminalRecord,
5255
5774
  recoveredTerminalRecord,
@@ -5264,6 +5783,7 @@ export {
5264
5783
  candidateCleanupTimeout,
5265
5784
  candidateCleanupDeadline,
5266
5785
  withinCandidateCleanupDeadline,
5786
+ readVerifiedArtifact,
5267
5787
  verifyBytes,
5268
5788
  verifyMaterializedWorkspace,
5269
5789
  captureMaterializedWorkspace,
@@ -5281,12 +5801,17 @@ export {
5281
5801
  candidateClaimFileInternals,
5282
5802
  sealAgentCandidateModelSettlement,
5283
5803
  usdToNanos,
5284
- sealAgentCandidateExecutorFinalCapture,
5804
+ sealAgentCandidateExecutorStopAcknowledgement,
5285
5805
  persistCandidateOutputArtifact,
5286
5806
  persistCandidateModelSettlementEvidence,
5287
5807
  RecoveryAgentCandidateTraceStore,
5288
5808
  executePreparedAgentCandidate,
5809
+ AGENT_CANDIDATE_EXECUTION_SUPPORT,
5289
5810
  verifyAgentCandidateBundle,
5811
+ verifiedResourceTextByDigest,
5812
+ CANDIDATE_KNOWLEDGE_ROOT_ENV,
5813
+ CANDIDATE_KNOWLEDGE_RETRIEVAL_CONFIG_ENV,
5814
+ candidateKnowledgeExecutionPaths,
5290
5815
  prepareAgentCandidateExecution
5291
5816
  };
5292
- //# sourceMappingURL=chunk-CNH7DF7Z.js.map
5817
+ //# sourceMappingURL=chunk-YOLKCWRV.js.map