@dailephd/my-dev-kit-lab 0.9.0 → 0.9.2

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 (48) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +10 -3
  3. package/dist/src/agents/adapters/claudeAdapter.js +6 -3
  4. package/dist/src/agents/adapters/codexAdapter.js +51 -10
  5. package/dist/src/agents/index.js +1 -0
  6. package/dist/src/agents/normalizeAgentTelemetry.js +50 -0
  7. package/dist/src/agents/parseAgentTokenUsage.js +71 -49
  8. package/dist/src/agents/providerBuildObservation.js +97 -0
  9. package/dist/src/audits/core/projectInventory.js +1 -0
  10. package/dist/src/cli/help.js +8 -2
  11. package/dist/src/commands/runExperimentDescribeCommand.js +31 -0
  12. package/dist/src/commands/runExperimentRunCommand.js +193 -5
  13. package/dist/src/core/runMeasuredCommand.js +4 -0
  14. package/dist/src/evaluation/benchmarkSandbox/gitExecutor.js +3 -1
  15. package/dist/src/experiments/plugins/agentSuccessRate/campaignCheckpoint.js +216 -0
  16. package/dist/src/experiments/plugins/agentSuccessRate/campaignCheckpointStore.js +588 -0
  17. package/dist/src/experiments/plugins/agentSuccessRate/campaignEvidenceQuality.js +77 -0
  18. package/dist/src/experiments/plugins/agentSuccessRate/campaignExecution.js +314 -0
  19. package/dist/src/experiments/plugins/agentSuccessRate/campaignIdentity.js +111 -0
  20. package/dist/src/experiments/plugins/agentSuccessRate/campaignProgress.js +175 -0
  21. package/dist/src/experiments/plugins/agentSuccessRate/campaignQueue.js +11 -0
  22. package/dist/src/experiments/plugins/agentSuccessRate/campaignRecovery.js +256 -0
  23. package/dist/src/experiments/plugins/agentSuccessRate/campaignResumableExecution.js +207 -0
  24. package/dist/src/experiments/plugins/agentSuccessRate/index.js +6 -0
  25. package/dist/src/experiments/plugins/agentSuccessRate/plugin.js +32 -22
  26. package/dist/src/experiments/plugins/agentSuccessRate/realAgentExecution.js +29 -7
  27. package/dist/src/experiments/plugins/agentSuccessRate/timeoutPresets.js +9 -0
  28. package/dist/src/report/experiments/agentSuccessCampaignProgressReportModel.js +1 -0
  29. package/dist/src/report/experiments/buildAgentSuccessCampaignProgressReport.js +6 -0
  30. package/dist/src/report/experiments/buildAgentSuccessRateReport.js +11 -0
  31. package/dist/src/report/experiments/buildPluginExperimentReport.js +1 -1
  32. package/dist/src/report/experiments/renderAgentSuccessCampaignProgressHtml.js +6 -0
  33. package/dist/src/report/experiments/renderAgentSuccessCampaignProgressText.js +25 -0
  34. package/dist/src/report/experiments/renderAgentSuccessRateHtml.js +3 -1
  35. package/dist/src/report/experiments/renderAgentSuccessRateText.js +7 -0
  36. package/dist/src/report/experiments/writeAgentSuccessCampaignProgressReport.js +139 -0
  37. package/dist/src/report/experiments/writePluginExperimentReports.js +4 -3
  38. package/dist/src/securityValidation/attackScenarios/payloadCorpus.js +0 -0
  39. package/dist/src/securityValidation/packageChecks/parseNpmPackDryRun.js +34 -4
  40. package/dist/src/securityValidation/packageChecks/requiredPackageContents.js +106 -2
  41. package/dist/src/securityValidation/packageChecks/runPackageChecks.js +31 -16
  42. package/dist/src/securityValidation/staticScans/semgrep.js +68 -64
  43. package/dist/src/securityValidation/types.js +0 -4
  44. package/dist/src/securityValidation/validate/runSecurityValidation.js +1 -1
  45. package/docs/METRICS.md +9 -0
  46. package/examples/tutorial-browser/prepare.mjs +13 -13
  47. package/examples/tutorial-browser/server.mjs +13 -13
  48. package/package.json +1 -1
@@ -0,0 +1,11 @@
1
+ import { validateAgentSuccessTask } from "../../../evaluation/agentSuccess/index.js";
2
+ import { campaignAssert, canonicalCampaignJson } from "./campaignIdentity.js";
3
+ import { AGENT_SUCCESS_RATE_TREATMENT_IDS } from "./metadata.js";
4
+ /** Selection is already established by selectAgentSuccessTasks. Never sort, interleave or drop a treatment. */
5
+ export function buildCampaignQueue(tasks, treatments, limit) {
6
+ campaignAssert(tasks.length > 0 && tasks.length <= 1000 && new Set(tasks.map((t) => t.id)).size === tasks.length, "INVALID_SELECTION");
7
+ tasks.forEach((task) => campaignAssert(validateAgentSuccessTask(task).ok, "INVALID_SELECTION"));
8
+ campaignAssert(canonicalCampaignJson(treatments) === canonicalCampaignJson(AGENT_SUCCESS_RATE_TREATMENT_IDS), "FIXED_TREATMENTS_REQUIRED");
9
+ campaignAssert(limit === "one-case" || limit === "all-selected-cases", "INVALID_EXECUTION_LIMIT");
10
+ return tasks.slice(0, limit === "one-case" ? 1 : tasks.length).flatMap((task, caseIndex) => AGENT_SUCCESS_RATE_TREATMENT_IDS.map((treatmentId, treatmentIndex) => ({ task, treatmentId, checkpointIndex: caseIndex * 2 + treatmentIndex })));
11
+ }
@@ -0,0 +1,256 @@
1
+ import { campaignAssert, campaignExact, campaignHash, campaignIdentifier, canonicalCampaignJson, isCampaignDigest, validateCampaignIdentity } from "./campaignIdentity.js";
2
+ import { CAMPAIGN_CHECKPOINT_MAX_BYTES, createCampaignCheckpoint, projectCampaignTreatmentEvidence, requiredCampaignArtifacts, validateCampaignCompletion, validateCampaignRelativePath } from "./campaignCheckpoint.js";
3
+ import { analyzeAgentSuccessTreatment } from "./analysis.js";
4
+ import { classifyAgentSuccessAttempt } from "./repairPolicy.js";
5
+ export const CAMPAIGN_CHECKPOINT_SCHEMA_V2 = "my-dev-kit-lab-agent-success-rate-campaign-checkpoint-v2";
6
+ export const CAMPAIGN_EPOCH_SCHEMA = "campaign-invocation-epoch-v1";
7
+ const equal = (a, b) => canonicalCampaignJson(a) === canonicalCampaignJson(b);
8
+ function timestamp(value) {
9
+ campaignAssert(typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value) && new Date(value).toISOString() === value, "INVALID_TIMESTAMP");
10
+ }
11
+ function generation(value) { campaignAssert(Number.isSafeInteger(value) && Number(value) >= 1 && Number(value) <= 1_000_000, "INVALID_GENERATION"); }
12
+ export function campaignEpochNamespace(caseId, treatmentId, ordinal, id) {
13
+ campaignAssert(campaignIdentifier(caseId) && ["raw-full-file", "context-pack"].includes(treatmentId) && [1, 2].includes(ordinal) && /^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/.test(id), "INVALID_EPOCH_ID");
14
+ // Compact, deterministic case/treatment labels leave room for Git's internal paths on Windows.
15
+ // The full epoch UUID is retained (without separators); the marker also binds the full case identity.
16
+ const relative = `campaign-epochs-v2/${campaignHash(caseId).slice(0, 12)}/${treatmentId === "raw-full-file" ? "raw" : "pack"}/${ordinal}-${id.replace(/-/g, "")}`;
17
+ validateCampaignRelativePath(relative);
18
+ return relative;
19
+ }
20
+ /** Only artifact references change. Source/check/protected paths and scientific evidence remain untouched. */
21
+ export function mapCampaignEpochEvidence(source, namespace, direction) {
22
+ validateCampaignRelativePath(namespace);
23
+ const evidence = structuredClone(source);
24
+ const map = (value) => {
25
+ if (value === null)
26
+ return null;
27
+ validateCampaignRelativePath(value);
28
+ if (direction === "prefix")
29
+ return `${namespace}/${value}`;
30
+ campaignAssert(value.startsWith(`${namespace}/`), "EPOCH_ARTIFACT_SCOPE");
31
+ const relative = value.slice(namespace.length + 1);
32
+ validateCampaignRelativePath(relative);
33
+ return relative;
34
+ };
35
+ const apply = (e) => {
36
+ e.proposedPatchPath = map(e.proposedPatchPath);
37
+ e.appliedPatchPath = map(e.appliedPatchPath);
38
+ if (e.realAgent) {
39
+ e.realAgent.context.contextArtifactPath = map(e.realAgent.context.contextArtifactPath);
40
+ e.realAgent.agentArtifactDirectory = map(e.realAgent.agentArtifactDirectory);
41
+ for (const key of Object.keys(e.realAgent.agentArtifacts))
42
+ e.realAgent.agentArtifacts[key] = map(e.realAgent.agentArtifacts[key]);
43
+ }
44
+ };
45
+ apply(evidence);
46
+ evidence.attempts?.forEach(a => apply(a.evidence));
47
+ return evidence;
48
+ }
49
+ export function validateCampaignEpochArtifacts(value, expected) {
50
+ campaignAssert(Array.isArray(value) && value.length <= 32, "ARTIFACT_REFERENCE_MISMATCH");
51
+ value.forEach(r => { campaignExact(r, ["path", "kind", "sha256", "bytes"]); validateCampaignRelativePath(r.path); campaignAssert(["patch", "context", "provider"].includes(String(r.kind)) && isCampaignDigest(r.sha256) && Number.isSafeInteger(r.bytes) && Number(r.bytes) >= 0 && Number(r.bytes) <= 32 * 1024 * 1024); });
52
+ campaignAssert(equal(value.map(({ path, kind }) => ({ path, kind })), expected), "ARTIFACT_REFERENCE_MISMATCH");
53
+ }
54
+ export function validateCampaignLimitAuthorization(value, cp, unit) {
55
+ campaignExact(value, ["identityDigest", "generation", "revision", "caseId", "treatmentId", "acknowledgeInvocation", "acknowledgeExternalLimit"]);
56
+ campaignAssert(value.identityDigest === cp.identity.digest && value.generation === cp.generation && value.revision === cp.integrity && value.caseId === unit.caseId && value.treatmentId === unit.treatmentId && value.acknowledgeInvocation === true && value.acknowledgeExternalLimit === true, "LIMIT_AUTHORIZATION_MISMATCH");
57
+ const previous = unit.epochs.at(-1);
58
+ campaignAssert(unit.state === "paused-limit" && unit.epochs.length === 1 && previous?.phase === "terminal" && previous.terminal?.reason === "provider-limit" && unit.completion === null, "LIMIT_CONTINUATION_NOT_ELIGIBLE");
59
+ }
60
+ export function sealCampaignCheckpointV2(body) { return { ...body, integrity: campaignHash(canonicalCampaignJson(body)) }; }
61
+ export function createCampaignCheckpointV2(identity, tasks, runId, at) {
62
+ const old = createCampaignCheckpoint(identity, tasks, at);
63
+ const cp = sealCampaignCheckpointV2({ schemaVersion: CAMPAIGN_CHECKPOINT_SCHEMA_V2, identity: structuredClone(identity), runId, generation: 1, parentIntegrity: null, createdAt: at, updatedAt: at, units: old.units.map(u => ({ caseId: u.caseId, treatmentId: u.treatmentId, state: "pending", epochs: [], completion: null })), finalization: null });
64
+ validateCampaignCheckpointV2(cp, tasks);
65
+ return cp;
66
+ }
67
+ export function validateCampaignCheckpointV2(value, tasks) {
68
+ campaignAssert(Buffer.byteLength(canonicalCampaignJson(value)) <= CAMPAIGN_CHECKPOINT_MAX_BYTES, "DATA_BOUND");
69
+ campaignExact(value, ["schemaVersion", "identity", "runId", "generation", "parentIntegrity", "createdAt", "updatedAt", "units", "finalization", "integrity"]);
70
+ campaignAssert(value.schemaVersion === CAMPAIGN_CHECKPOINT_SCHEMA_V2, "UNSUPPORTED_SCHEMA");
71
+ validateCampaignIdentity(value.identity);
72
+ generation(value.generation);
73
+ campaignAssert(campaignIdentifier(value.runId));
74
+ timestamp(value.createdAt);
75
+ timestamp(value.updatedAt);
76
+ campaignAssert(value.updatedAt >= value.createdAt && (value.parentIntegrity === null || isCampaignDigest(value.parentIntegrity)));
77
+ const identity = value.identity;
78
+ const createdAt = value.createdAt;
79
+ const updatedAt = value.updatedAt;
80
+ campaignAssert(tasks.length === identity.selected.length && tasks.every((t, i) => t.id === identity.selected[i].caseId && t.benchmarkProject === identity.selected[i].projectId && campaignHash(canonicalCampaignJson(t)) === identity.selected[i].taskDigest), "TASK_IDENTITY_MISMATCH");
81
+ campaignAssert(Array.isArray(value.units) && value.units.length === tasks.length * 2);
82
+ const ids = new Set();
83
+ value.units.forEach((u, index) => {
84
+ campaignExact(u, ["caseId", "treatmentId", "state", "epochs", "completion"]);
85
+ const task = tasks[Math.floor(index / 2)];
86
+ campaignAssert(u.caseId === task.id && u.treatmentId === identity.treatmentOrder[index % 2] && Array.isArray(u.epochs) && u.epochs.length <= 2, "UNIT_IDENTITY");
87
+ campaignAssert(["pending", "in-progress", "completed", "paused-limit", "incomplete", "uncertain"].includes(String(u.state)));
88
+ const epochs = u.epochs;
89
+ epochs.forEach((e, i) => {
90
+ campaignExact(e, ["schemaVersion", "caseId", "treatmentId", "ordinal", "id", "previousEpochId", "parentRevision", "phase", "configuration", "namespace", "authorization", "reservedAt", "authorizedAt", "terminalAt", "terminal"]);
91
+ campaignAssert(e.schemaVersion === CAMPAIGN_EPOCH_SCHEMA && e.caseId === u.caseId && e.treatmentId === u.treatmentId && e.ordinal === i + 1 && !ids.has(String(e.id)), "EPOCH_IDENTITY");
92
+ ids.add(String(e.id));
93
+ campaignAssert(e.namespace === campaignEpochNamespace(String(u.caseId), String(u.treatmentId), i + 1, String(e.id)) && isCampaignDigest(e.parentRevision) && equal(e.configuration, identity.config), "EPOCH_CONFIGURATION");
94
+ timestamp(e.reservedAt);
95
+ campaignAssert(e.reservedAt >= createdAt && e.reservedAt <= updatedAt);
96
+ campaignExact(e.authorization, ["intent", "scope"]);
97
+ if (i === 0)
98
+ campaignAssert(e.previousEpochId === null && e.authorization.intent === "initial" && e.authorization.scope === null);
99
+ else {
100
+ const prior = epochs[i - 1];
101
+ campaignAssert(e.previousEpochId === prior.id && prior.phase === "terminal" && prior.terminal?.reason === "provider-limit" && e.authorization.intent === "limit-continuation", "EPOCH_CONTINUATION");
102
+ const scope = e.authorization.scope;
103
+ campaignExact(scope, ["identityDigest", "generation", "revision", "caseId", "treatmentId", "acknowledgeInvocation", "acknowledgeExternalLimit"]);
104
+ generation(scope.generation);
105
+ campaignAssert(scope.identityDigest === identity.digest && scope.revision === e.parentRevision && scope.caseId === u.caseId && scope.treatmentId === u.treatmentId && scope.acknowledgeInvocation === true && scope.acknowledgeExternalLimit === true && Number(scope.generation) < Number(value.generation));
106
+ }
107
+ campaignAssert(["reserved", "invocation-authorized", "terminal", "uncertain"].includes(String(e.phase)));
108
+ if (e.phase === "reserved")
109
+ campaignAssert(e.authorizedAt === null && e.terminalAt === null && e.terminal === null);
110
+ else {
111
+ timestamp(e.authorizedAt);
112
+ campaignAssert(e.authorizedAt >= e.reservedAt && e.authorizedAt <= updatedAt);
113
+ }
114
+ if (e.phase !== "terminal")
115
+ campaignAssert(e.terminalAt === null && e.terminal === null);
116
+ else {
117
+ timestamp(e.terminalAt);
118
+ campaignAssert(e.terminalAt >= String(e.authorizedAt) && e.terminalAt <= updatedAt);
119
+ const t = e.terminal;
120
+ campaignExact(t, ["outcome", "reason", "evidence", "artifacts"]);
121
+ campaignAssert(["evaluated", "provider-limit", "incomplete"].includes(String(t.reason)));
122
+ const evidence = t.evidence;
123
+ campaignAssert(equal(evidence, projectCampaignTreatmentEvidence(evidence)), "PRIVATE_OR_INVALID_EVIDENCE");
124
+ const local = mapCampaignEpochEvidence(evidence, String(e.namespace), "strip");
125
+ campaignAssert(local.treatmentId === u.treatmentId && t.outcome === (local.realAgent?.providerStatus ?? "not-invoked"), "TERMINAL_OUTCOME");
126
+ const rows = local.attempts?.map(a => a.evidence) ?? [local];
127
+ campaignAssert(rows.length > 0 && rows.length <= 1 + identity.config.repairAttempts, "ATTEMPT_BOUND");
128
+ rows.forEach((a, j) => campaignAssert(a.treatmentId === u.treatmentId && (!a.realAgent || (a.realAgent.providerId === identity.config.providerId && a.realAgent.attempt === j + 1)) && (!local.attempts || local.attempts[j].attemptNumber === j + 1), "ATTEMPT_IDENTITY"));
129
+ if (local.attempts) {
130
+ const { attempts: _attempts, ...final } = local;
131
+ campaignAssert(equal(final, rows.at(-1)), "FINAL_ATTEMPT_MISMATCH");
132
+ local.attempts.forEach((a, j) => {
133
+ const row = rows[j];
134
+ const classification = classifyAgentSuccessAttempt(row, analyzeAgentSuccessTreatment(task, row).metrics);
135
+ campaignAssert(a.providerId === identity.config.providerId && a.providerStatus === row.realAgent?.providerStatus && a.failureCategory === classification.category && a.repairEligible === classification.repairEligible && a.taskSuccess === classification.taskSuccess && equal(a.cleanupResult, row.cleanup) && equal(a.tokenUsage, row.agentTokenUsage) && a.durationMs === row.timing.agentDurationMs && a.completedAt >= a.startedAt, "ATTEMPT_PROOF_MISMATCH");
136
+ if (j > 0)
137
+ campaignAssert(local.attempts[j - 1].repairEligible && local.attempts[j - 1].taskSuccess === false, "REPAIR_NOT_ELIGIBLE");
138
+ });
139
+ }
140
+ campaignAssert(t.reason === "provider-limit" || t.outcome !== "agent-limit-reached", "LIMIT_CLASSIFICATION_REQUIRED");
141
+ const expected = requiredCampaignArtifacts(local, task, t.reason !== "evaluated").map(r => ({ ...r, path: `${e.namespace}/${r.path}` }));
142
+ validateCampaignEpochArtifacts(t.artifacts, expected);
143
+ if (t.reason === "provider-limit")
144
+ campaignAssert(t.outcome === "agent-limit-reached" && local.realAgent?.providerInvoked === true && local.availability !== "complete" && !local.patch.attempted && local.cleanup.attempted && local.cleanup.removed && local.realAgent.cwdCleanup.attempted && local.realAgent.cwdCleanup.removed && local.protectedIntegrity.status === "intact", "LIMIT_TERMINAL_PROOF");
145
+ if (t.reason === "evaluated") {
146
+ const completion = { sourceSchema: "my-dev-kit-lab-agent-success-rate-execution-v1", evidence: local, taskSuccess: analyzeAgentSuccessTreatment(task, local).metrics.taskSuccess.value === true, artifacts: t.artifacts.map(r => ({ ...r, path: r.path.slice(String(e.namespace).length + 1) })) };
147
+ validateCampaignCompletion(completion, identity, task, String(u.treatmentId));
148
+ }
149
+ }
150
+ });
151
+ const last = u.epochs.at(-1);
152
+ if (u.state === "pending")
153
+ campaignAssert(last === undefined && u.completion === null);
154
+ else {
155
+ campaignAssert(last !== undefined);
156
+ if (u.state === "in-progress")
157
+ campaignAssert(["reserved", "invocation-authorized"].includes(last.phase));
158
+ if (u.state === "uncertain")
159
+ campaignAssert(last.phase === "uncertain");
160
+ if (["completed", "paused-limit", "incomplete"].includes(String(u.state)))
161
+ campaignAssert(last.phase === "terminal" && last.terminal?.reason === (u.state === "completed" ? "evaluated" : u.state === "paused-limit" ? "provider-limit" : "incomplete"));
162
+ if (u.state === "completed")
163
+ campaignAssert(equal(u.completion, { sourceSchema: "my-dev-kit-lab-agent-success-rate-execution-v1", evidence: last.terminal.evidence, taskSuccess: analyzeAgentSuccessTreatment(task, last.terminal.evidence).metrics.taskSuccess.value === true, artifacts: last.terminal.artifacts }));
164
+ else
165
+ campaignAssert(u.completion === null);
166
+ }
167
+ });
168
+ if (value.finalization !== null) {
169
+ campaignExact(value.finalization, ["completedAt", "artifacts"]);
170
+ timestamp(value.finalization.completedAt);
171
+ campaignAssert(value.units.every(u => u.state === "completed") && value.finalization.completedAt <= value.updatedAt);
172
+ validateCampaignEpochArtifacts(value.finalization.artifacts, [{ path: "agent-success-rate-analysis.json", kind: "provider" }, { path: "agent-success-rate-execution.json", kind: "provider" }]);
173
+ }
174
+ const { integrity, ...body } = value;
175
+ campaignAssert(isCampaignDigest(integrity) && integrity === campaignHash(canonicalCampaignJson(body)), "CHECKPOINT_INTEGRITY");
176
+ }
177
+ export function transitionCampaignCheckpointV2(previous, tasks, action, at) {
178
+ validateCampaignCheckpointV2(previous, tasks);
179
+ campaignAssert(previous.finalization === null, "COMPLETED_IMMUTABLE");
180
+ const { integrity, ...body } = structuredClone(previous);
181
+ body.generation++;
182
+ body.parentIntegrity = integrity;
183
+ body.updatedAt = at;
184
+ if (action.kind === "finalize") {
185
+ campaignAssert(body.units.every(u => u.state === "completed"), "INCOMPLETE_CAMPAIGN");
186
+ body.finalization = structuredClone(action.finalization);
187
+ }
188
+ else {
189
+ campaignAssert(Number.isSafeInteger(action.index) && action.index >= 0 && action.index < body.units.length);
190
+ campaignAssert(body.units.slice(0, action.index).every(u => u.state === "completed"), "QUEUE_ORDER");
191
+ const u = body.units[action.index];
192
+ const last = u.epochs.at(-1);
193
+ campaignAssert(u.state !== "completed", "COMPLETED_IMMUTABLE");
194
+ if (action.kind === "reserve") {
195
+ if (action.authorization)
196
+ validateCampaignLimitAuthorization(action.authorization, previous, previous.units[action.index]);
197
+ else
198
+ campaignAssert(u.state === "pending" && u.epochs.length === 0, "UNIT_NOT_PENDING");
199
+ const ordinal = (u.epochs.length + 1);
200
+ const e = { schemaVersion: CAMPAIGN_EPOCH_SCHEMA, caseId: u.caseId, treatmentId: u.treatmentId, ordinal, id: action.epochId, previousEpochId: last?.id ?? null, parentRevision: integrity, phase: "reserved", configuration: structuredClone(previous.identity.config), namespace: campaignEpochNamespace(u.caseId, u.treatmentId, ordinal, action.epochId), authorization: action.authorization ? { intent: "limit-continuation", scope: structuredClone(action.authorization) } : { intent: "initial", scope: null }, reservedAt: at, authorizedAt: null, terminalAt: null, terminal: null };
201
+ u.epochs.push(e);
202
+ u.state = "in-progress";
203
+ }
204
+ else {
205
+ campaignAssert(last && u.state === "in-progress", "INVALID_EPOCH_TRANSITION");
206
+ if (action.kind === "authorize") {
207
+ campaignAssert(last.phase === "reserved");
208
+ last.phase = "invocation-authorized";
209
+ last.authorizedAt = at;
210
+ }
211
+ else {
212
+ campaignAssert(last.phase === "invocation-authorized");
213
+ if (action.kind === "uncertain") {
214
+ last.phase = "uncertain";
215
+ u.state = "uncertain";
216
+ }
217
+ else {
218
+ last.phase = "terminal";
219
+ last.terminalAt = at;
220
+ last.terminal = structuredClone(action.terminal);
221
+ u.state = action.terminal.reason === "evaluated" ? "completed" : action.terminal.reason === "provider-limit" ? "paused-limit" : "incomplete";
222
+ if (u.state === "completed")
223
+ u.completion = { sourceSchema: "my-dev-kit-lab-agent-success-rate-execution-v1", evidence: structuredClone(action.terminal.evidence), taskSuccess: analyzeAgentSuccessTreatment(tasks[Math.floor(action.index / 2)], action.terminal.evidence).metrics.taskSuccess.value === true, artifacts: structuredClone(action.terminal.artifacts) };
224
+ }
225
+ }
226
+ }
227
+ }
228
+ const result = sealCampaignCheckpointV2(body);
229
+ validateCampaignCheckpointV2(result, tasks);
230
+ return result;
231
+ }
232
+ /** Reconstruct the exact permitted transition; no last-writer-wins or arbitrary unit replacement. */
233
+ export function validateCampaignHistoryV2(previous, next, tasks) {
234
+ let action;
235
+ if (previous.finalization === null && next.finalization !== null)
236
+ action = { kind: "finalize", finalization: next.finalization };
237
+ else {
238
+ const changes = next.units.map((u, i) => equal(u, previous.units[i]) ? -1 : i).filter(i => i >= 0);
239
+ campaignAssert(changes.length === 1, "INVALID_HISTORY");
240
+ const index = changes[0];
241
+ const a = previous.units[index], b = next.units[index];
242
+ const e = b.epochs.at(-1);
243
+ campaignAssert(e !== undefined, "INVALID_HISTORY");
244
+ if (b.epochs.length === a.epochs.length + 1)
245
+ action = { kind: "reserve", index, epochId: e.id, ...(e.authorization.intent === "limit-continuation" ? { authorization: e.authorization.scope } : {}) };
246
+ else if (e.phase === "invocation-authorized")
247
+ action = { kind: "authorize", index };
248
+ else if (e.phase === "uncertain")
249
+ action = { kind: "uncertain", index };
250
+ else {
251
+ campaignAssert(e.phase === "terminal" && e.terminal !== null, "INVALID_HISTORY");
252
+ action = { kind: "terminal", index, terminal: e.terminal };
253
+ }
254
+ }
255
+ campaignAssert(equal(transitionCampaignCheckpointV2(previous, tasks, action, next.updatedAt), next), "INVALID_HISTORY");
256
+ }
@@ -0,0 +1,207 @@
1
+ import { rmdir } from "node:fs/promises";
2
+ import { randomUUID } from "node:crypto";
3
+ import { runAgentPrompt } from "../../../agents/runAgentPrompt.js";
4
+ import { campaignAssert, canonicalCampaignJson, compareCampaignIdentity } from "./campaignIdentity.js";
5
+ import { buildCampaignQueue } from "./campaignQueue.js";
6
+ import { createCampaignCheckpointV2, transitionCampaignCheckpointV2, validateCampaignLimitAuthorization } from "./campaignRecovery.js";
7
+ import { acquireCampaignDispatch, allocateCampaignEpoch, assertCampaignDispatch, persistCampaignSupportingArtifacts, prepareCampaignEpochTerminal, prepareCampaignProviderOutput, publishCampaignFinalArtifacts, readCampaignCheckpoint, readCampaignCheckpointV2, releaseCampaignDispatch, writeCampaignCheckpointV2 } from "./campaignCheckpointStore.js";
8
+ import { validatePreparedCampaign } from "./campaignExecution.js";
9
+ import { boundMessage, executeAgentSuccessTreatment, resolveControlledBenchmarkProject, resolveSandboxRuntimeRoot } from "./execution.js";
10
+ import { executeAgentSuccessRepairTreatment } from "./repairExecution.js";
11
+ import { agentSuccessAgentArtifactDirectory, createRealAgentProposalSource } from "./realAgentExecution.js";
12
+ import { defaultContextDependencies, sealTask } from "./plugin.js";
13
+ import { projectAgentFacingTask } from "./agentTaskProjection.js";
14
+ import { AgentSuccessPackContextBuilder, buildContextTaskInput, buildRawFullFileContext, DEFAULT_AGENT_SUCCESS_KIT_COMMAND } from "./contextGeneration.js";
15
+ import { analyzeAgentSuccessRate } from "./analysis.js";
16
+ import { buildAgentSuccessRateExecutionArtifact, validateAgentSuccessRateExecutionArtifact, AGENT_SUCCESS_RATE_EXECUTION_ARTIFACT_FILE } from "./executionArtifact.js";
17
+ import { buildAgentSuccessRateAnalysisArtifact, validateAgentSuccessRateAnalysisArtifact, validateAgentSuccessRateArtifactFamily, AGENT_SUCCESS_RATE_ANALYSIS_ARTIFACT_FILE } from "./analysisArtifact.js";
18
+ function blocked(outcome, unit) {
19
+ const cp = outcome.checkpoint;
20
+ const epoch = unit.epochs.at(-1);
21
+ outcome.status = unit.state === "paused-limit" ? "provider-limit" : "recovery-required";
22
+ outcome.blockedUnit = { caseId: unit.caseId, treatmentId: unit.treatmentId, generation: cp.generation, revision: cp.integrity, phase: epoch?.phase ?? "pending", reason: unit.state === "paused-limit" ? "provider-limit" : epoch?.phase === "reserved" ? "reserved-not-replayed" : unit.state === "incomplete" ? "terminal-incomplete" : "potentially-invoked" };
23
+ }
24
+ export async function executeResumableCampaign(prepared, args) {
25
+ const outcome = { status: "failed", checkpoint: null, errors: [], scientificArtifactsWritten: false, blockedUnit: null };
26
+ let owner;
27
+ let verified;
28
+ let packBuilder;
29
+ let activeRuntime;
30
+ const at = () => new Date().toISOString();
31
+ try {
32
+ campaignAssert(args.intent && ["new-resumable", "resume", "authorize-limit-continuation"].includes(args.intent.kind), "EXPLICIT_INTENT_REQUIRED");
33
+ // The closed runtime intent also rejects force-replay or a generic resume boolean.
34
+ campaignAssert(Object.keys(args.intent).sort().join(",") === (args.intent.kind === "authorize-limit-continuation" ? "authorization,kind" : "kind"), "INVALID_INTENT");
35
+ verified = await validatePreparedCampaign(prepared);
36
+ const { input, identity } = verified;
37
+ const { boundary, tasks, provider } = input;
38
+ if (provider.kind === "real-agent")
39
+ campaignAssert(provider.includeRealAgents === true && provider.trustedExecutable && provider.trustedExecutable.providerId === provider.providerId, "REAL_PROVIDER_AUTHORIZATION_REQUIRED");
40
+ const queue = buildCampaignQueue(tasks, input.treatments, "all-selected-cases");
41
+ campaignAssert(["one-case", "all-selected-cases"].includes(args.limit), "INVALID_EXECUTION_LIMIT");
42
+ owner = await acquireCampaignDispatch(boundary, identity);
43
+ const fresh = async () => {
44
+ const current = await validatePreparedCampaign(prepared);
45
+ campaignAssert(compareCampaignIdentity(identity, current.identity) === "MATCH", "IDENTITY_CHANGED_BEFORE_DISPATCH");
46
+ await assertCampaignDispatch(boundary, identity, owner);
47
+ };
48
+ const read = async () => {
49
+ await fresh();
50
+ const stored = await readCampaignCheckpointV2(boundary, identity, tasks);
51
+ campaignAssert(stored.identityVerification === "MATCH", "IDENTITY_UNVERIFIABLE");
52
+ campaignAssert(stored.temporaryFiles.length === 0, "CHECKPOINT_PUBLICATION_UNRESOLVED");
53
+ return stored;
54
+ };
55
+ const publish = async (cp) => {
56
+ await fresh();
57
+ await writeCampaignCheckpointV2(boundary, cp, tasks, phase => args.hooks?.publication?.(cp.generation, phase), owner);
58
+ const stored = await read();
59
+ campaignAssert(stored.checkpoint?.integrity === cp.integrity, "CLAIM_READBACK_FAILED");
60
+ outcome.checkpoint = stored.checkpoint;
61
+ };
62
+ const stored = await read();
63
+ if (args.intent.kind === "new-resumable") {
64
+ campaignAssert(stored.checkpoint === null, "CAMPAIGN_ALREADY_EXISTS");
65
+ // No copy or migration, even when a historical chain happens to share the identity.
66
+ const legacy = await readCampaignCheckpoint(boundary, identity, tasks);
67
+ campaignAssert(legacy.checkpoint === null && legacy.temporaryFiles.length === 0, "LEGACY_CAMPAIGN_READ_ONLY");
68
+ await publish(createCampaignCheckpointV2(identity, tasks, args.runId, at()));
69
+ }
70
+ else {
71
+ campaignAssert(stored.reusable && stored.checkpoint !== null, "RESUMABLE_CAMPAIGN_REQUIRED_NO_V1_MIGRATION");
72
+ outcome.checkpoint = stored.checkpoint;
73
+ }
74
+ const alreadyComplete = outcome.checkpoint.units.every(u => u.state === "completed");
75
+ // Validate a continuation request before accepting any complete/blocked/no-work shortcut.
76
+ let authorizedIndex;
77
+ if (args.intent.kind === "authorize-limit-continuation") {
78
+ authorizedIndex = outcome.checkpoint.units.findIndex(u => u.state !== "completed");
79
+ campaignAssert(authorizedIndex >= 0, "COMPLETED_IMMUTABLE");
80
+ validateCampaignLimitAuthorization(args.intent.authorization, outcome.checkpoint, outcome.checkpoint.units[authorizedIndex]);
81
+ }
82
+ if (!alreadyComplete && provider.kind === "trusted-simulated-provider")
83
+ packBuilder = new AgentSuccessPackContextBuilder("trusted-simulated-kit", provider.contextDependencies);
84
+ if (!alreadyComplete && provider.kind === "real-agent")
85
+ packBuilder = new AgentSuccessPackContextBuilder(provider.kitCommand ?? DEFAULT_AGENT_SUCCESS_KIT_COMMAND, provider.contextDependencies ?? defaultContextDependencies());
86
+ let caseProcessed;
87
+ for (const unit of queue) {
88
+ let state = outcome.checkpoint.units[unit.checkpointIndex];
89
+ if (state.state === "completed")
90
+ continue;
91
+ if (args.limit === "one-case" && caseProcessed !== undefined && caseProcessed !== unit.task.id)
92
+ break;
93
+ if (state.state !== "pending" && authorizedIndex !== unit.checkpointIndex) {
94
+ blocked(outcome, state);
95
+ return outcome;
96
+ }
97
+ caseProcessed = unit.task.id;
98
+ // Read and verify the whole chain again before reserving. No local snapshot alone authorizes dispatch.
99
+ const latest = (await read()).checkpoint;
100
+ campaignAssert(latest.integrity === outcome.checkpoint.integrity, "STALE_REVISION");
101
+ const reserve = transitionCampaignCheckpointV2(latest, tasks, { kind: "reserve", index: unit.checkpointIndex, epochId: randomUUID(), ...(authorizedIndex === unit.checkpointIndex && args.intent.kind === "authorize-limit-continuation" ? { authorization: args.intent.authorization } : {}) }, at());
102
+ const epoch = reserve.units[unit.checkpointIndex].epochs.at(-1);
103
+ const epochRoot = await allocateCampaignEpoch(boundary, identity, owner, epoch);
104
+ await publish(reserve);
105
+ // Context preparation occurs before provider authorization; a crash here does not create an implicit replay policy.
106
+ const forbidden = new Map();
107
+ const executionTask = provider.kind === "deterministic-fixture" ? unit.task : sealTask(unit.task, forbidden);
108
+ const canonicalProjectRoot = await resolveControlledBenchmarkProject(boundary.toolRoot, unit.task.benchmarkProject);
109
+ const contextInput = buildContextTaskInput(executionTask);
110
+ const contexts = packBuilder ? { "raw-full-file": await buildRawFullFileContext(contextInput, canonicalProjectRoot), "context-pack": await packBuilder.build(contextInput, canonicalProjectRoot) } : null;
111
+ activeRuntime = await resolveSandboxRuntimeRoot(epochRoot, boundary.toolRoot);
112
+ await publish(transitionCampaignCheckpointV2(outcome.checkpoint, tasks, { kind: "authorize", index: unit.checkpointIndex }, at()));
113
+ await fresh();
114
+ const claimed = (await read()).checkpoint;
115
+ state = claimed.units[unit.checkpointIndex];
116
+ campaignAssert(claimed.integrity === outcome.checkpoint.integrity && state.epochs.at(-1).id === epoch.id && state.epochs.at(-1).phase === "invocation-authorized", "INVOCATION_NOT_AUTHORIZED");
117
+ const common = { task: executionTask, treatmentId: unit.treatmentId, runId: `${outcome.checkpoint.runId}-${epoch.id}`, canonicalProjectRoot, runtimeRoot: activeRuntime, privateRoots: [boundary.outputRoot, boundary.toolRoot], dependencies: input.dependencies };
118
+ // Explicit fixture branch only. Missing provider context never selects a fixture patch.
119
+ campaignAssert(provider.kind === "deterministic-fixture" || contexts !== null, "PROVIDER_CONTEXT_REQUIRED");
120
+ const result = provider.kind !== "deterministic-fixture" && contexts ? await executeAgentSuccessRepairTreatment({ ...common, repair: {
121
+ providerId: provider.providerId, repairAttempts: provider.repairAttempts,
122
+ createProposalSource: ({ attemptNumber, feedback }) => createRealAgentProposalSource({ settings: { providerId: provider.providerId, timeoutMs: provider.timeoutMs, ...(provider.kind === "real-agent" ? { trustedExecutable: provider.trustedExecutable, env: provider.env } : {}), outDir: epochRoot, privateRoots: common.privateRoots, protectedRoots: [boundary.toolRoot, boundary.outputRoot, activeRuntime, canonicalProjectRoot], runAgent: async (request) => {
123
+ await fresh();
124
+ campaignAssert((await read()).checkpoint?.integrity === claimed.integrity, "STALE_INVOCATION_CLAIM");
125
+ await prepareCampaignProviderOutput(boundary, identity, owner, `${epoch.namespace}/${agentSuccessAgentArtifactDirectory(unit.task.benchmarkProject, unit.task.id, unit.treatmentId, attemptNumber)}`);
126
+ return provider.kind === "real-agent" ? runAgentPrompt(request) : provider.runAgent(request);
127
+ } }, runId: common.runId, task: projectAgentFacingTask(executionTask), contexts, forbidden: forbidden.get(unit.task.id) ?? [], attemptNumber, ...(feedback ? { repair: { feedback, maxAttempts: 1 + provider.repairAttempts } } : {}) })
128
+ } }) : await executeAgentSuccessTreatment(common);
129
+ await rmdir(activeRuntime);
130
+ activeRuntime = undefined;
131
+ await fresh();
132
+ await persistCampaignSupportingArtifacts(boundary, identity, owner, unit.task, result, epoch);
133
+ const reason = result.evidence.realAgent?.providerStatus === "agent-limit-reached" ? "provider-limit" : result.evidence.status === "completed" && result.evidence.availability === "complete" ? "evaluated" : "incomplete";
134
+ const terminal = await prepareCampaignEpochTerminal(boundary, identity, unit.task, epoch, result.evidence, reason);
135
+ await publish(transitionCampaignCheckpointV2(outcome.checkpoint, tasks, { kind: "terminal", index: unit.checkpointIndex, terminal }, at()));
136
+ authorizedIndex = undefined;
137
+ if (reason !== "evaluated") {
138
+ blocked(outcome, outcome.checkpoint.units[unit.checkpointIndex]);
139
+ return outcome;
140
+ }
141
+ }
142
+ const complete = outcome.checkpoint.units.every(u => u.state === "completed");
143
+ if (!complete) {
144
+ outcome.status = "partial";
145
+ return outcome;
146
+ }
147
+ const cp = (await read()).checkpoint;
148
+ outcome.checkpoint = cp;
149
+ {
150
+ const cases = tasks.map((task, i) => ({ caseId: task.id, caseName: task.title, benchmarkProject: task.benchmarkProject, taskLocality: task.taskLocality, fixtureId: provider.kind === "deterministic-fixture" ? task.deterministicFixture.id : "", treatments: [cp.units[i * 2].completion.evidence, cp.units[i * 2 + 1].completion.evidence] }));
151
+ const analysis = analyzeAgentSuccessRate(tasks, cases, provider.kind === "deterministic-fixture" ? "deterministic-fixture" : "real-agent");
152
+ // Stable final metadata also permits safe recovery after a partial final artifact publication.
153
+ const completedAt = cp.finalization?.completedAt ?? cp.updatedAt;
154
+ const common = { runId: cp.runId, pluginId: "agent-success-rate", pluginSchemaVersion: "1.0.0", startedAt: cp.createdAt, completedAt };
155
+ const execution = buildAgentSuccessRateExecutionArtifact({ ...common, cases, ...(provider.kind === "deterministic-fixture" ? {} : { realAgent: { providerId: provider.providerId, timeoutMs: provider.timeoutMs, repairAttempts: provider.repairAttempts, attemptsPerTreatment: (1 + provider.repairAttempts), promptTransport: "stdin", contextEffectEvaluated: analysis.contextEffectEvaluated } }) });
156
+ const scientific = buildAgentSuccessRateAnalysisArtifact({ ...common, analysis });
157
+ campaignAssert([...validateAgentSuccessRateExecutionArtifact(execution), ...validateAgentSuccessRateAnalysisArtifact(scientific), ...validateAgentSuccessRateArtifactFamily(execution, scientific)].length === 0, "SCIENTIFIC_ARTIFACT_INVALID");
158
+ const finalization = await publishCampaignFinalArtifacts(boundary, identity, owner, [{ relativePath: AGENT_SUCCESS_RATE_EXECUTION_ARTIFACT_FILE, content: `${JSON.stringify(execution, null, 2)}\n` }, { relativePath: AGENT_SUCCESS_RATE_ANALYSIS_ARTIFACT_FILE, content: `${JSON.stringify(scientific, null, 2)}\n` }], cp.runId, completedAt);
159
+ if (cp.finalization === null)
160
+ await publish(transitionCampaignCheckpointV2(cp, tasks, { kind: "finalize", finalization }, at()));
161
+ else
162
+ campaignAssert(canonicalCampaignJson(finalization) === canonicalCampaignJson(cp.finalization), "FINAL_ARTIFACT_INCONSISTENT");
163
+ }
164
+ outcome.scientificArtifactsWritten = true;
165
+ outcome.status = alreadyComplete ? "already-complete" : "completed";
166
+ }
167
+ catch (error) {
168
+ outcome.status = "failed";
169
+ outcome.errors.push(boundMessage(error instanceof Error ? error.message : "CAMPAIGN_FAILED", verified ? [verified.input.boundary.outputRoot, verified.input.boundary.toolRoot, verified.input.sourceRoot ?? ""] : []));
170
+ if (owner && verified)
171
+ try {
172
+ outcome.checkpoint = (await readCampaignCheckpointV2(verified.input.boundary, verified.identity, verified.input.tasks)).checkpoint;
173
+ }
174
+ catch {
175
+ outcome.errors.push("CHECKPOINT_READ_FAILED");
176
+ }
177
+ }
178
+ finally {
179
+ if (packBuilder)
180
+ try {
181
+ campaignAssert((await packBuilder.dispose()) === null, "CONTEXT_CLEANUP_FAILED");
182
+ }
183
+ catch {
184
+ outcome.errors.push("CONTEXT_CLEANUP_FAILED");
185
+ outcome.status = "failed";
186
+ }
187
+ if (activeRuntime)
188
+ try {
189
+ await rmdir(activeRuntime);
190
+ }
191
+ catch (error) {
192
+ if (error.code !== "ENOENT") {
193
+ outcome.errors.push("RUNTIME_ROOT_NOT_REMOVED");
194
+ outcome.status = "failed";
195
+ }
196
+ }
197
+ if (owner && verified)
198
+ try {
199
+ await releaseCampaignDispatch(verified.input.boundary, verified.identity, owner);
200
+ }
201
+ catch {
202
+ outcome.errors.push("DISPATCH_RELEASE_FAILED");
203
+ outcome.status = "failed";
204
+ }
205
+ }
206
+ return outcome;
207
+ }
@@ -18,3 +18,9 @@ export * from "./repairPolicy.js";
18
18
  export * from "./repairFeedback.js";
19
19
  export * from "./repairAnalysis.js";
20
20
  export * from "./repairExecution.js";
21
+ export * from "./campaignIdentity.js";
22
+ export * from "./campaignCheckpoint.js";
23
+ export * from "./campaignCheckpointStore.js";
24
+ export * from "./campaignQueue.js";
25
+ export * from "./campaignExecution.js";
26
+ export * from "./campaignRecovery.js";
@@ -187,29 +187,10 @@ export const agentSuccessRatePlugin = {
187
187
  { filePath: resolveWithinRoot(outDir, AGENT_SUCCESS_RATE_EXECUTION_ARTIFACT_FILE), content: `${JSON.stringify(executionArtifact, null, 2)}\n` },
188
188
  { filePath: resolveWithinRoot(outDir, AGENT_SUCCESS_RATE_ANALYSIS_ARTIFACT_FILE), content: `${JSON.stringify(analysisArtifact, null, 2)}\n` }
189
189
  ];
190
- for (const target of targets) {
191
- if (await io.exists(target.filePath)) {
192
- throw new Error(AGENT_SUCCESS_RATE_PERSISTENCE_FAILURE_MESSAGE + (await removeAgentAttemptDirectories(caseEvidence, outDir, removeAttemptDirectory)));
193
- }
194
- }
195
- const created = [];
196
190
  try {
197
- for (const target of targets) {
198
- await io.ensureDirectory(path.dirname(target.filePath));
199
- await io.writeFile(target.filePath, target.content);
200
- created.push(target.filePath);
201
- }
191
+ await writeAgentSuccessRateArtifactTargets(targets, io);
202
192
  }
203
193
  catch {
204
- // Remove only files this attempt created; never touch anything else.
205
- for (const filePath of created.reverse()) {
206
- try {
207
- await io.removeFile(filePath);
208
- }
209
- catch {
210
- // best effort: the failure below is the outcome
211
- }
212
- }
213
194
  throw new Error(AGENT_SUCCESS_RATE_PERSISTENCE_FAILURE_MESSAGE + (await removeAgentAttemptDirectories(caseEvidence, outDir, removeAttemptDirectory)));
214
195
  }
215
196
  return mapAgentSuccessRateToRun({
@@ -230,6 +211,35 @@ export const agentSuccessRatePlugin = {
230
211
  return result.summary ?? summarizeExperimentRun(result);
231
212
  }
232
213
  };
214
+ /** Shared artifact publication owner. Campaign reuse must verify committed ownership and exact bytes. */
215
+ export async function writeAgentSuccessRateArtifactTargets(targets, io, verifiedReuse) {
216
+ const pending = [];
217
+ for (const target of targets) {
218
+ if (await io.exists(target.filePath)) {
219
+ if (!verifiedReuse || !(await verifiedReuse(target)))
220
+ throw new Error(AGENT_SUCCESS_RATE_PERSISTENCE_FAILURE_MESSAGE);
221
+ }
222
+ else
223
+ pending.push(target);
224
+ }
225
+ const created = [];
226
+ try {
227
+ for (const target of pending) {
228
+ await io.ensureDirectory(path.dirname(target.filePath));
229
+ await io.writeFile(target.filePath, target.content);
230
+ created.push(target.filePath);
231
+ }
232
+ }
233
+ catch (error) {
234
+ for (const file of created.reverse()) {
235
+ try {
236
+ await io.removeFile(file);
237
+ }
238
+ catch { /* Report persistence failure below. */ }
239
+ }
240
+ throw error;
241
+ }
242
+ }
233
243
  /** Every patch artifact must be promised by exactly one treatment's evidence, and vice versa. */
234
244
  function validatePatchFiles(caseEvidence, patchFiles) {
235
245
  const problems = [];
@@ -393,7 +403,7 @@ function readRealAgentConfig(config) {
393
403
  };
394
404
  }
395
405
  /** Captures hidden values for the leak guard, then returns a copy of the task with the reference fixture removed. */
396
- function sealTask(task, forbiddenByTask) {
406
+ export function sealTask(task, forbiddenByTask) {
397
407
  forbiddenByTask.set(task.id, collectForbiddenAgentValues(task));
398
408
  const sealed = { ...task };
399
409
  delete sealed.deterministicFixture;
@@ -403,7 +413,7 @@ function sealTask(task, forbiddenByTask) {
403
413
  function packageRoot() {
404
414
  return fileURLToPath(new URL("../../../../", import.meta.url));
405
415
  }
406
- function defaultContextDependencies() {
416
+ export function defaultContextDependencies() {
407
417
  return {
408
418
  buildIndex: buildMyDevKitIndex,
409
419
  runCommand: runMeasuredCommand,