@dailephd/my-dev-kit-lab 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +4 -3
- package/dist/src/agents/adapters/claudeAdapter.js +6 -3
- package/dist/src/agents/adapters/codexAdapter.js +51 -10
- package/dist/src/agents/index.js +1 -0
- package/dist/src/agents/normalizeAgentTelemetry.js +50 -0
- package/dist/src/agents/parseAgentTokenUsage.js +71 -49
- package/dist/src/agents/providerBuildObservation.js +97 -0
- package/dist/src/audits/core/projectInventory.js +1 -0
- package/dist/src/cli/help.js +8 -2
- package/dist/src/commands/runExperimentDescribeCommand.js +31 -0
- package/dist/src/commands/runExperimentRunCommand.js +192 -5
- package/dist/src/core/runMeasuredCommand.js +4 -0
- package/dist/src/evaluation/benchmarkSandbox/gitExecutor.js +3 -1
- package/dist/src/experiments/plugins/agentSuccessRate/campaignCheckpoint.js +216 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignCheckpointStore.js +588 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignEvidenceQuality.js +77 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignExecution.js +314 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignIdentity.js +111 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignProgress.js +173 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignQueue.js +11 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignRecovery.js +256 -0
- package/dist/src/experiments/plugins/agentSuccessRate/campaignResumableExecution.js +207 -0
- package/dist/src/experiments/plugins/agentSuccessRate/index.js +6 -0
- package/dist/src/experiments/plugins/agentSuccessRate/plugin.js +32 -22
- package/dist/src/experiments/plugins/agentSuccessRate/realAgentExecution.js +29 -7
- package/dist/src/experiments/plugins/agentSuccessRate/timeoutPresets.js +9 -0
- package/dist/src/report/experiments/agentSuccessCampaignProgressReportModel.js +1 -0
- package/dist/src/report/experiments/buildAgentSuccessCampaignProgressReport.js +6 -0
- package/dist/src/report/experiments/buildAgentSuccessRateReport.js +11 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +1 -1
- package/dist/src/report/experiments/renderAgentSuccessCampaignProgressHtml.js +6 -0
- package/dist/src/report/experiments/renderAgentSuccessCampaignProgressText.js +25 -0
- package/dist/src/report/experiments/renderAgentSuccessRateHtml.js +3 -1
- package/dist/src/report/experiments/renderAgentSuccessRateText.js +7 -0
- package/dist/src/report/experiments/writeAgentSuccessCampaignProgressReport.js +135 -0
- package/dist/src/report/experiments/writePluginExperimentReports.js +4 -3
- package/docs/METRICS.md +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
import { link, lstat, mkdir, open, readdir, realpath, unlink } from "node:fs/promises";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { resolveWithinRoot } from "../../../core/pathSafety.js";
|
|
6
|
+
import { isSameOrInside, resolvePhysicalPath } from "../../../evaluation/benchmarkSandbox/pathPolicy.js";
|
|
7
|
+
import { campaignAssert, campaignHash, canonicalCampaignJson, compareCampaignIdentity, validateCampaignIdentity } from "./campaignIdentity.js";
|
|
8
|
+
import { CAMPAIGN_CHECKPOINT_MAX_BYTES, requiredCampaignArtifacts, validateCampaignCheckpoint, validateCampaignCompletion, validateCampaignHistory, validateCampaignRelativePath } from "./campaignCheckpoint.js";
|
|
9
|
+
import { projectCampaignTreatmentEvidence } from "./campaignCheckpoint.js";
|
|
10
|
+
import { analyzeAgentSuccessTreatment } from "./analysis.js";
|
|
11
|
+
import { writeAgentSuccessRateArtifactTargets } from "./plugin.js";
|
|
12
|
+
import { CAMPAIGN_CHECKPOINT_SCHEMA_V2, mapCampaignEpochEvidence, validateCampaignCheckpointV2, validateCampaignHistoryV2 } from "./campaignRecovery.js";
|
|
13
|
+
/** Command preflight, including a not-yet-created output root. No filesystem mutation. */
|
|
14
|
+
export async function validateCampaignOutputPath(boundary, sourceRoot) {
|
|
15
|
+
const root = path.resolve(boundary.outputRoot);
|
|
16
|
+
const physicalRoot = await resolveCampaignRoot(root);
|
|
17
|
+
const roots = await Promise.all([boundary.toolRoot, moduleRoot, ...(path.basename(path.resolve(moduleRoot)) === "dist" ? [path.dirname(path.resolve(moduleRoot))] : []), ...(sourceRoot ? [sourceRoot] : [])].map(resolvePhysicalPath));
|
|
18
|
+
for (const protectedRoot of roots) {
|
|
19
|
+
campaignAssert(!samePhysicalPath(physicalRoot, protectedRoot), "PROTECTED_OUTPUT_ROOT");
|
|
20
|
+
for (const segment of ["src", "scripts", "dist", "node_modules", "benchmarks", "tests"]) {
|
|
21
|
+
const protectedPath = path.join(protectedRoot, segment);
|
|
22
|
+
campaignAssert(!isSameOrInside(protectedPath, physicalRoot) && !isSameOrInside(physicalRoot, protectedPath), "PROTECTED_OUTPUT_ROOT");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const GENERATION = /^generation-(\d{10})\.json$/;
|
|
27
|
+
const TEMPORARY = /^\.temporary-[a-f0-9-]{36}\.json$/;
|
|
28
|
+
const STORE_SEGMENT = "campaign-checkpoints";
|
|
29
|
+
const STORE_OWNER = "campaign-checkpoint-store-v1";
|
|
30
|
+
const moduleRoot = fileURLToPath(new URL("../../../../", import.meta.url));
|
|
31
|
+
const durableCheckpoints = new WeakMap();
|
|
32
|
+
function samePhysicalPath(left, right) { return isSameOrInside(left, right) && isSameOrInside(right, left); }
|
|
33
|
+
async function plain(candidate, kind) {
|
|
34
|
+
const stat = await lstat(candidate);
|
|
35
|
+
campaignAssert(!stat.isSymbolicLink() && (kind === "file" ? stat.isFile() : stat.isDirectory()), "UNSAFE_FILESYSTEM_TARGET");
|
|
36
|
+
}
|
|
37
|
+
/** Inspect every ancestor; refuse links/junctions and fail closed on inspection errors. */
|
|
38
|
+
async function plainAncestors(candidate) {
|
|
39
|
+
let current = path.resolve(candidate);
|
|
40
|
+
for (;;) {
|
|
41
|
+
await plain(current, "directory");
|
|
42
|
+
const parent = path.dirname(current);
|
|
43
|
+
if (parent === current)
|
|
44
|
+
return;
|
|
45
|
+
current = parent;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Resolve filesystem aliases before inspecting ancestors (macOS exposes /var as /private/var). */
|
|
49
|
+
async function resolveCampaignRoot(candidate) {
|
|
50
|
+
const resolved = path.resolve(candidate);
|
|
51
|
+
let existing = resolved;
|
|
52
|
+
const remainder = [];
|
|
53
|
+
for (;;) {
|
|
54
|
+
try {
|
|
55
|
+
await plain(existing, "directory");
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error.code !== "ENOENT")
|
|
60
|
+
throw error;
|
|
61
|
+
const parent = path.dirname(existing);
|
|
62
|
+
campaignAssert(parent !== existing, "UNSAFE_FILESYSTEM_TARGET");
|
|
63
|
+
remainder.unshift(path.basename(existing));
|
|
64
|
+
existing = parent;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const physicalExisting = await realpath(existing);
|
|
68
|
+
await plainAncestors(physicalExisting);
|
|
69
|
+
return path.join(physicalExisting, ...remainder);
|
|
70
|
+
}
|
|
71
|
+
async function validateRoot(boundary) {
|
|
72
|
+
const root = await resolveCampaignRoot(boundary.outputRoot);
|
|
73
|
+
const physicalRoot = root;
|
|
74
|
+
const packageRoots = await Promise.all([boundary.toolRoot, moduleRoot, ...(path.basename(path.resolve(moduleRoot)) === "dist" ? [path.dirname(path.resolve(moduleRoot))] : [])].map(resolvePhysicalPath));
|
|
75
|
+
for (const packageRoot of packageRoots) {
|
|
76
|
+
campaignAssert(!samePhysicalPath(physicalRoot, packageRoot), "PROTECTED_OUTPUT_ROOT");
|
|
77
|
+
for (const segment of ["src", "scripts", "dist", "node_modules", "benchmarks", "tests"]) {
|
|
78
|
+
const protectedRoot = path.join(packageRoot, segment);
|
|
79
|
+
campaignAssert(!isSameOrInside(protectedRoot, physicalRoot) && !isSameOrInside(physicalRoot, protectedRoot), "PROTECTED_OUTPUT_ROOT");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return root;
|
|
83
|
+
}
|
|
84
|
+
async function safeFile(root, relative) {
|
|
85
|
+
validateCampaignRelativePath(relative);
|
|
86
|
+
const target = resolveWithinRoot(root, relative);
|
|
87
|
+
await plainAncestors(path.dirname(target));
|
|
88
|
+
await plain(target, "file");
|
|
89
|
+
const [physicalRoot, physicalTarget] = await Promise.all([realpath(root), realpath(target)]);
|
|
90
|
+
campaignAssert(isSameOrInside(physicalRoot, physicalTarget) && !samePhysicalPath(physicalTarget, physicalRoot), "ARTIFACT_ESCAPE");
|
|
91
|
+
return target;
|
|
92
|
+
}
|
|
93
|
+
async function boundedRead(file, max) {
|
|
94
|
+
await plain(file, "file");
|
|
95
|
+
const handle = await open(file, "r");
|
|
96
|
+
try {
|
|
97
|
+
const before = await handle.stat();
|
|
98
|
+
campaignAssert(before.isFile() && before.size <= max, "DATA_BOUND");
|
|
99
|
+
const result = Buffer.alloc(before.size + 1);
|
|
100
|
+
let size = 0;
|
|
101
|
+
for (;;) {
|
|
102
|
+
const n = await handle.read(result, size, result.length - size, size);
|
|
103
|
+
size += n.bytesRead;
|
|
104
|
+
if (n.bytesRead === 0 || size === result.length)
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
campaignAssert(size === before.size && (await handle.stat()).size === before.size, "FILE_CHANGED_DURING_READ");
|
|
108
|
+
return result.subarray(0, size);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
await handle.close();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function ownedDirectory(parent, name, owner, create) {
|
|
115
|
+
const dir = resolveWithinRoot(parent, name);
|
|
116
|
+
let created = false;
|
|
117
|
+
try {
|
|
118
|
+
await lstat(dir);
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
if (e.code !== "ENOENT")
|
|
122
|
+
throw e;
|
|
123
|
+
if (!create)
|
|
124
|
+
return null;
|
|
125
|
+
await mkdir(dir);
|
|
126
|
+
created = true;
|
|
127
|
+
}
|
|
128
|
+
await plainAncestors(dir);
|
|
129
|
+
const marker = path.join(dir, "owner.json");
|
|
130
|
+
if (created) {
|
|
131
|
+
const h = await open(marker, "wx");
|
|
132
|
+
try {
|
|
133
|
+
await h.writeFile(owner, "utf8");
|
|
134
|
+
await h.sync();
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
await h.close();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
campaignAssert((await boundedRead(marker, 1024)).toString("utf8") === owner, "UNKNOWN_DIRECTORY_OWNER");
|
|
141
|
+
return dir;
|
|
142
|
+
}
|
|
143
|
+
async function directory(boundary, identity, create, v2 = false) {
|
|
144
|
+
validateCampaignIdentity(identity);
|
|
145
|
+
const root = await validateRoot(boundary);
|
|
146
|
+
const owner = v2 ? "campaign-checkpoint-store-v2" : STORE_OWNER;
|
|
147
|
+
const store = await ownedDirectory(root, v2 ? "campaign-checkpoints-v2" : STORE_SEGMENT, owner, create);
|
|
148
|
+
return store === null ? null : ownedDirectory(store, identity.digest, `${owner}:${identity.digest}`, create);
|
|
149
|
+
}
|
|
150
|
+
function generationName(n) { campaignAssert(Number.isSafeInteger(n) && n >= 1 && n <= 1_000_000); return `generation-${String(n).padStart(10, "0")}.json`; }
|
|
151
|
+
export async function prepareCampaignCompletion(boundary, identity, task, source) {
|
|
152
|
+
const root = await validateRoot(boundary);
|
|
153
|
+
const evidence = projectCampaignTreatmentEvidence(source);
|
|
154
|
+
const artifacts = [];
|
|
155
|
+
for (const reference of requiredCampaignArtifacts(evidence, task)) {
|
|
156
|
+
const bytes = await boundedRead(await safeFile(root, reference.path), 32 * 1024 * 1024);
|
|
157
|
+
artifacts.push({ ...reference, sha256: campaignHash(bytes), bytes: bytes.length });
|
|
158
|
+
}
|
|
159
|
+
const result = { sourceSchema: "my-dev-kit-lab-agent-success-rate-execution-v1", evidence, taskSuccess: analyzeAgentSuccessTreatment(task, evidence).metrics.taskSuccess.value === true, artifacts };
|
|
160
|
+
validateCampaignCompletion(result, identity, task, source.treatmentId);
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
async function verifyArtifacts(boundary, checkpoint) {
|
|
164
|
+
const root = await validateRoot(boundary);
|
|
165
|
+
for (const unit of checkpoint.units)
|
|
166
|
+
for (const reference of unit.completion?.artifacts ?? []) {
|
|
167
|
+
const bytes = await boundedRead(await safeFile(root, reference.path), 32 * 1024 * 1024);
|
|
168
|
+
campaignAssert(bytes.length === reference.bytes && campaignHash(bytes) === reference.sha256, "ARTIFACT_INTEGRITY");
|
|
169
|
+
}
|
|
170
|
+
if (checkpoint.schemaVersion === CAMPAIGN_CHECKPOINT_SCHEMA_V2) {
|
|
171
|
+
for (const unit of checkpoint.units)
|
|
172
|
+
for (const epoch of unit.epochs) {
|
|
173
|
+
await verifyEpochDirectory(boundary, checkpoint.identity, epoch);
|
|
174
|
+
for (const reference of epoch.terminal?.artifacts ?? []) {
|
|
175
|
+
const bytes = await boundedRead(await safeFile(root, reference.path), 32 * 1024 * 1024);
|
|
176
|
+
campaignAssert(bytes.length === reference.bytes && campaignHash(bytes) === reference.sha256, "ARTIFACT_INTEGRITY");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const reference of checkpoint.finalization?.artifacts ?? []) {
|
|
180
|
+
const bytes = await boundedRead(await safeFile(root, reference.path), 32 * 1024 * 1024);
|
|
181
|
+
campaignAssert(bytes.length === reference.bytes && campaignHash(bytes) === reference.sha256, "ARTIFACT_INTEGRITY");
|
|
182
|
+
}
|
|
183
|
+
if (checkpoint.finalization)
|
|
184
|
+
campaignAssert((await boundedRead(path.join(root, "campaign-final-owner-v2.json"), 16384)).toString("utf8") === canonicalCampaignJson({ schema: "campaign-final-owner-v2", identity: checkpoint.identity.digest, runId: checkpoint.runId, completedAt: checkpoint.finalization.completedAt, artifacts: checkpoint.finalization.artifacts }), "FINAL_OWNER_MISMATCH");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
export async function readCampaignCheckpoint(boundary, expected, tasks) {
|
|
188
|
+
return readStoredCheckpoint(boundary, expected, tasks, false);
|
|
189
|
+
}
|
|
190
|
+
export async function readCampaignCheckpointV2(boundary, expected, tasks) {
|
|
191
|
+
return readStoredCheckpoint(boundary, expected, tasks, true);
|
|
192
|
+
}
|
|
193
|
+
async function readStoredCheckpoint(boundary, expected, tasks, v2) {
|
|
194
|
+
const dir = await directory(boundary, expected, false, v2);
|
|
195
|
+
if (dir === null)
|
|
196
|
+
return { checkpoint: null, temporaryFiles: [], identityVerification: compareCampaignIdentity(expected, expected) === "MATCH" ? "MATCH" : "UNVERIFIABLE", reusable: false };
|
|
197
|
+
const names = await readdir(dir);
|
|
198
|
+
campaignAssert(names.length <= 10_000, "DATA_BOUND");
|
|
199
|
+
const generations = names.filter((n) => GENERATION.test(n)).sort();
|
|
200
|
+
campaignAssert(names.every((n) => GENERATION.test(n) || TEMPORARY.test(n) || ["owner.json", "writer.json"].includes(n)), "UNEXPECTED_STORE_ENTRY");
|
|
201
|
+
let previous = null;
|
|
202
|
+
let verification = "MATCH";
|
|
203
|
+
for (let i = 0; i < generations.length; i++) {
|
|
204
|
+
const name = generations[i];
|
|
205
|
+
campaignAssert(name === generationName(i + 1), "GENERATION_GAP_OR_AMBIGUITY");
|
|
206
|
+
const bytes = await boundedRead(path.join(dir, name), CAMPAIGN_CHECKPOINT_MAX_BYTES);
|
|
207
|
+
let loaded;
|
|
208
|
+
try {
|
|
209
|
+
loaded = JSON.parse(bytes.toString("utf8"));
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
throw new Error("CHECKPOINT_CORRUPTION");
|
|
213
|
+
}
|
|
214
|
+
if (v2)
|
|
215
|
+
validateCampaignCheckpointV2(loaded, tasks);
|
|
216
|
+
else
|
|
217
|
+
validateCampaignCheckpoint(loaded, tasks);
|
|
218
|
+
const committed = loaded;
|
|
219
|
+
campaignAssert(committed.generation === i + 1, "GENERATION_MISMATCH");
|
|
220
|
+
const comparison = compareCampaignIdentity(expected, committed.identity);
|
|
221
|
+
campaignAssert(comparison !== "MISMATCH" && committed.identity.digest === expected.digest, "IDENTITY_MISMATCH");
|
|
222
|
+
if (comparison === "UNVERIFIABLE")
|
|
223
|
+
verification = comparison;
|
|
224
|
+
if (previous) {
|
|
225
|
+
if (v2)
|
|
226
|
+
validateCampaignHistoryV2(previous, committed, tasks);
|
|
227
|
+
else
|
|
228
|
+
validateCampaignHistory(previous, committed);
|
|
229
|
+
}
|
|
230
|
+
else
|
|
231
|
+
campaignAssert(committed.parentIntegrity === null && committed.units.every((u) => u.state === "pending") && (!v2 || committed.finalization === null), "INVALID_INITIAL_HISTORY");
|
|
232
|
+
await verifyArtifacts(boundary, committed);
|
|
233
|
+
previous = committed;
|
|
234
|
+
}
|
|
235
|
+
if (previous)
|
|
236
|
+
durableCheckpoints.set(previous, await resolvePhysicalPath(boundary.outputRoot));
|
|
237
|
+
return { checkpoint: previous, temporaryFiles: names.filter((n) => TEMPORARY.test(n)).sort(), identityVerification: verification, reusable: previous !== null && verification === "MATCH" };
|
|
238
|
+
}
|
|
239
|
+
/** Hooks are failure-only seams; no filesystem operation or validation can be replaced. */
|
|
240
|
+
export async function writeCampaignCheckpoint(boundary, checkpoint, tasks, failureHook, dispatch) {
|
|
241
|
+
return writeStoredCheckpoint(boundary, checkpoint, tasks, failureHook, dispatch);
|
|
242
|
+
}
|
|
243
|
+
export async function writeCampaignCheckpointV2(boundary, checkpoint, tasks, failureHook, dispatch) {
|
|
244
|
+
return writeStoredCheckpoint(boundary, checkpoint, tasks, failureHook, dispatch);
|
|
245
|
+
}
|
|
246
|
+
async function writeStoredCheckpoint(boundary, checkpoint, tasks, failureHook, dispatch) {
|
|
247
|
+
const v2 = checkpoint.schemaVersion === CAMPAIGN_CHECKPOINT_SCHEMA_V2;
|
|
248
|
+
if (v2)
|
|
249
|
+
validateCampaignCheckpointV2(checkpoint, tasks);
|
|
250
|
+
else
|
|
251
|
+
validateCampaignCheckpoint(checkpoint, tasks);
|
|
252
|
+
await assertDispatchWriter(boundary, checkpoint.identity, dispatch);
|
|
253
|
+
const dir = await directory(boundary, checkpoint.identity, true, v2);
|
|
254
|
+
campaignAssert(dir !== null);
|
|
255
|
+
const writer = path.join(dir, "writer.json");
|
|
256
|
+
let lock;
|
|
257
|
+
try {
|
|
258
|
+
lock = await open(writer, "wx");
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
throw new Error("WRITER_OWNERSHIP_CONFLICT");
|
|
262
|
+
}
|
|
263
|
+
const token = randomUUID();
|
|
264
|
+
let published = false;
|
|
265
|
+
try {
|
|
266
|
+
await lock.writeFile(token);
|
|
267
|
+
await lock.sync();
|
|
268
|
+
await lock.close();
|
|
269
|
+
await assertDispatchWriter(boundary, checkpoint.identity, dispatch);
|
|
270
|
+
const current = await readStoredCheckpoint(boundary, checkpoint.identity, tasks, v2);
|
|
271
|
+
if (current.checkpoint) {
|
|
272
|
+
if (v2)
|
|
273
|
+
validateCampaignHistoryV2(current.checkpoint, checkpoint, tasks);
|
|
274
|
+
else
|
|
275
|
+
validateCampaignHistory(current.checkpoint, checkpoint);
|
|
276
|
+
}
|
|
277
|
+
else
|
|
278
|
+
campaignAssert(checkpoint.generation === 1 && checkpoint.parentIntegrity === null && checkpoint.units.every((u) => u.state === "pending") && (!v2 || checkpoint.finalization === null), "INVALID_INITIAL_HISTORY");
|
|
279
|
+
await verifyArtifacts(boundary, checkpoint);
|
|
280
|
+
const temp = path.join(dir, `.temporary-${randomUUID()}.json`);
|
|
281
|
+
const h = await open(temp, "wx");
|
|
282
|
+
try {
|
|
283
|
+
await failureHook?.("temporary-opened");
|
|
284
|
+
await h.writeFile(`${canonicalCampaignJson(checkpoint)}\n`);
|
|
285
|
+
await failureHook?.("temporary-written");
|
|
286
|
+
await h.sync();
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
await h.close();
|
|
290
|
+
}
|
|
291
|
+
await plainAncestors(dir);
|
|
292
|
+
await failureHook?.("before-publication");
|
|
293
|
+
await assertDispatchWriter(boundary, checkpoint.identity, dispatch);
|
|
294
|
+
await verifyArtifacts(boundary, checkpoint);
|
|
295
|
+
const target = path.join(dir, generationName(checkpoint.generation));
|
|
296
|
+
// Same-filesystem hard-link publication creates a previously absent name; it cannot replace an existing name.
|
|
297
|
+
await link(temp, target);
|
|
298
|
+
published = true;
|
|
299
|
+
await failureHook?.("published");
|
|
300
|
+
const observed = await boundedRead(target, CAMPAIGN_CHECKPOINT_MAX_BYTES);
|
|
301
|
+
campaignAssert(observed.toString("utf8") === `${canonicalCampaignJson(checkpoint)}\n`, "PUBLICATION_INTEGRITY");
|
|
302
|
+
await verifyArtifacts(boundary, checkpoint);
|
|
303
|
+
await unlink(temp);
|
|
304
|
+
campaignAssert((await boundedRead(writer, 100)).toString("utf8") === token, "WRITER_OWNERSHIP_CHANGED");
|
|
305
|
+
await unlink(writer);
|
|
306
|
+
durableCheckpoints.set(checkpoint, await resolvePhysicalPath(boundary.outputRoot));
|
|
307
|
+
}
|
|
308
|
+
catch (e) {
|
|
309
|
+
await lock.close().catch(() => undefined);
|
|
310
|
+
if (!published) {
|
|
311
|
+
campaignAssert((await boundedRead(writer, 100)).toString("utf8") === token, "WRITER_OWNERSHIP_CHANGED");
|
|
312
|
+
await unlink(writer);
|
|
313
|
+
}
|
|
314
|
+
// A post-publication failure leaves the ownership marker and committed generation for explicit recovery.
|
|
315
|
+
throw e;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const dispatches = new WeakMap();
|
|
319
|
+
const DISPATCH_MARKER = "campaign-dispatch.json";
|
|
320
|
+
export async function acquireCampaignDispatch(boundary, identity) {
|
|
321
|
+
validateCampaignIdentity(identity);
|
|
322
|
+
campaignAssert(compareCampaignIdentity(identity, identity) === "MATCH", "IDENTITY_UNVERIFIABLE");
|
|
323
|
+
const root = await validateRoot(boundary);
|
|
324
|
+
const marker = path.join(root, DISPATCH_MARKER);
|
|
325
|
+
const token = canonicalCampaignJson({ schema: "campaign-dispatch-v1", identity: identity.digest, token: randomUUID() });
|
|
326
|
+
let handle;
|
|
327
|
+
try {
|
|
328
|
+
handle = await open(marker, "wx");
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
throw new Error("DISPATCH_OWNERSHIP_CONFLICT");
|
|
332
|
+
}
|
|
333
|
+
// A failed acquisition leaves an uncertain marker; it is never silently stolen.
|
|
334
|
+
try {
|
|
335
|
+
await handle.writeFile(token);
|
|
336
|
+
await handle.sync();
|
|
337
|
+
const stat = await handle.stat();
|
|
338
|
+
const ownership = Object.freeze({ identityDigest: identity.digest });
|
|
339
|
+
dispatches.set(ownership, { root: await resolvePhysicalPath(root), token, dev: stat.dev, ino: stat.ino });
|
|
340
|
+
// An already active checkpoint publication cannot become a campaign dispatch.
|
|
341
|
+
for (const v2 of [false, true]) {
|
|
342
|
+
const dir = await directory(boundary, identity, false, v2);
|
|
343
|
+
if (dir) {
|
|
344
|
+
try {
|
|
345
|
+
await lstat(path.join(dir, "writer.json"));
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
if (error.code === "ENOENT")
|
|
349
|
+
continue;
|
|
350
|
+
throw error;
|
|
351
|
+
}
|
|
352
|
+
throw new Error("WRITER_OWNERSHIP_CONFLICT");
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return ownership;
|
|
356
|
+
}
|
|
357
|
+
finally {
|
|
358
|
+
await handle.close();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
export async function assertCampaignDispatch(boundary, identity, ownership) {
|
|
362
|
+
const owner = dispatches.get(ownership);
|
|
363
|
+
campaignAssert(owner && samePhysicalPath(owner.root, await resolvePhysicalPath(await validateRoot(boundary))) && ownership.identityDigest === identity.digest, "DISPATCH_OWNER_INVALID");
|
|
364
|
+
const marker = path.join(owner.root, DISPATCH_MARKER);
|
|
365
|
+
const stat = await lstat(marker);
|
|
366
|
+
campaignAssert(stat.dev === owner.dev && stat.ino === owner.ino && (await boundedRead(marker, 1024)).toString("utf8") === owner.token, "DISPATCH_OWNER_CHANGED");
|
|
367
|
+
}
|
|
368
|
+
async function assertDispatchWriter(boundary, identity, ownership) {
|
|
369
|
+
const root = await validateRoot(boundary);
|
|
370
|
+
if (ownership)
|
|
371
|
+
return assertCampaignDispatch(boundary, identity, ownership);
|
|
372
|
+
try {
|
|
373
|
+
await lstat(path.join(root, DISPATCH_MARKER));
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
if (error.code === "ENOENT")
|
|
377
|
+
return;
|
|
378
|
+
throw error;
|
|
379
|
+
}
|
|
380
|
+
throw new Error("DISPATCH_OWNERSHIP_REQUIRED");
|
|
381
|
+
}
|
|
382
|
+
export async function releaseCampaignDispatch(boundary, identity, ownership) {
|
|
383
|
+
await assertCampaignDispatch(boundary, identity, ownership);
|
|
384
|
+
await unlink(path.join(boundary.outputRoot, DISPATCH_MARKER));
|
|
385
|
+
dispatches.delete(ownership);
|
|
386
|
+
}
|
|
387
|
+
async function ensurePlainDirectory(root, relative) {
|
|
388
|
+
let current = root;
|
|
389
|
+
for (const segment of relative.split("/")) {
|
|
390
|
+
current = resolveWithinRoot(current, segment);
|
|
391
|
+
try {
|
|
392
|
+
await mkdir(current);
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
if (error.code !== "EEXIST")
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
await plainAncestors(current);
|
|
399
|
+
}
|
|
400
|
+
return current;
|
|
401
|
+
}
|
|
402
|
+
/** Verify safe, absent provider output before handing its path to the established provider runner. */
|
|
403
|
+
export async function prepareCampaignProviderOutput(boundary, identity, ownership, relative) {
|
|
404
|
+
await assertCampaignDispatch(boundary, identity, ownership);
|
|
405
|
+
validateCampaignRelativePath(relative);
|
|
406
|
+
const root = await validateRoot(boundary);
|
|
407
|
+
await ensurePlainDirectory(root, path.posix.dirname(relative));
|
|
408
|
+
try {
|
|
409
|
+
await lstat(resolveWithinRoot(root, relative));
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
if (error.code === "ENOENT")
|
|
413
|
+
return;
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
throw new Error("ARTIFACT_COLLISION");
|
|
417
|
+
}
|
|
418
|
+
/** New files only. Existing files require a verified committed reference and identical bytes. */
|
|
419
|
+
export async function persistCampaignArtifact(boundary, identity, ownership, relative, content, committed) {
|
|
420
|
+
await assertCampaignDispatch(boundary, identity, ownership);
|
|
421
|
+
validateCampaignRelativePath(relative);
|
|
422
|
+
const root = await validateRoot(boundary);
|
|
423
|
+
const bytes = Buffer.from(content, "utf8");
|
|
424
|
+
campaignAssert(bytes.length <= 32 * 1024 * 1024, "DATA_BOUND");
|
|
425
|
+
await ensurePlainDirectory(root, path.posix.dirname(relative));
|
|
426
|
+
const target = resolveWithinRoot(root, relative);
|
|
427
|
+
let handle;
|
|
428
|
+
try {
|
|
429
|
+
handle = await open(target, "wx");
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
if (error.code !== "EEXIST")
|
|
433
|
+
throw error;
|
|
434
|
+
campaignAssert(committed?.identity.digest === identity.digest, "ARTIFACT_COLLISION");
|
|
435
|
+
const durableRoot = durableCheckpoints.get(committed);
|
|
436
|
+
campaignAssert(durableRoot !== undefined && samePhysicalPath(durableRoot, await resolvePhysicalPath(root)), "ARTIFACT_OWNER_UNVERIFIED");
|
|
437
|
+
const dir = await directory(boundary, identity, false, committed.schemaVersion === CAMPAIGN_CHECKPOINT_SCHEMA_V2);
|
|
438
|
+
campaignAssert(dir !== null, "ARTIFACT_OWNER_UNVERIFIED");
|
|
439
|
+
const published = await boundedRead(path.join(dir, generationName(committed.generation)), CAMPAIGN_CHECKPOINT_MAX_BYTES);
|
|
440
|
+
campaignAssert(published.toString("utf8") === `${canonicalCampaignJson(committed)}\n`, "ARTIFACT_OWNER_UNVERIFIED");
|
|
441
|
+
const reference = [...committed.units.flatMap((u) => u.completion?.artifacts ?? []), ...(committed.schemaVersion === CAMPAIGN_CHECKPOINT_SCHEMA_V2 ? committed.finalization?.artifacts ?? [] : [])].find((a) => a.path === relative);
|
|
442
|
+
campaignAssert(reference && reference.bytes === bytes.length && reference.sha256 === campaignHash(bytes), "ARTIFACT_COLLISION");
|
|
443
|
+
const observed = await boundedRead(await safeFile(root, relative), 32 * 1024 * 1024);
|
|
444
|
+
campaignAssert(observed.equals(bytes), "ARTIFACT_COLLISION");
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
await handle.writeFile(bytes);
|
|
449
|
+
await handle.sync();
|
|
450
|
+
}
|
|
451
|
+
finally {
|
|
452
|
+
await handle.close();
|
|
453
|
+
}
|
|
454
|
+
campaignAssert((await boundedRead(await safeFile(root, relative), 32 * 1024 * 1024)).equals(bytes), "ARTIFACT_INTEGRITY");
|
|
455
|
+
}
|
|
456
|
+
export async function persistCampaignSupportingArtifacts(boundary, identity, ownership, task, result, epoch) {
|
|
457
|
+
const expected = requiredCampaignArtifacts(result.evidence, task, epoch !== undefined);
|
|
458
|
+
const files = [...result.contextFiles, ...result.patchFiles];
|
|
459
|
+
campaignAssert(new Set(files.map((f) => f.relativePath)).size === files.length, "ARTIFACT_REFERENCE_MISMATCH");
|
|
460
|
+
campaignAssert(canonicalCampaignJson(files.map((f) => f.relativePath).sort()) === canonicalCampaignJson(expected.filter((r) => r.kind !== "provider").map((r) => r.path).sort()), "ARTIFACT_REFERENCE_MISMATCH");
|
|
461
|
+
for (const attempt of result.evidence.attempts?.map((a) => a.evidence) ?? [result.evidence]) {
|
|
462
|
+
const proposed = files.find((f) => f.relativePath === attempt.proposedPatchPath);
|
|
463
|
+
if (epoch && attempt.proposedPatchPath === null)
|
|
464
|
+
campaignAssert(!attempt.patch.attempted && attempt.patch.proposedPatchBytes === 0, "ARTIFACT_SIZE_MISMATCH");
|
|
465
|
+
else
|
|
466
|
+
campaignAssert(proposed && Buffer.byteLength(proposed.content, "utf8") === attempt.patch.proposedPatchBytes, "ARTIFACT_SIZE_MISMATCH");
|
|
467
|
+
if (attempt.realAgent && attempt.realAgent.context.contextArtifactPath !== null) {
|
|
468
|
+
const context = files.find((f) => f.relativePath === attempt.realAgent.context.contextArtifactPath);
|
|
469
|
+
campaignAssert(context && context.content.length === attempt.realAgent.context.contextChars, "ARTIFACT_SIZE_MISMATCH");
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
if (epoch)
|
|
473
|
+
await verifyEpochDirectory(boundary, identity, epoch);
|
|
474
|
+
const scoped = (relative) => epoch ? `${epoch.namespace}/${relative}` : relative;
|
|
475
|
+
for (const file of files)
|
|
476
|
+
await persistCampaignArtifact(boundary, identity, ownership, scoped(file.relativePath), file.content);
|
|
477
|
+
// Provider artifacts were produced by the existing runner. Prove containment and flush before completion.
|
|
478
|
+
for (const reference of expected.filter((r) => r.kind === "provider")) {
|
|
479
|
+
await assertCampaignDispatch(boundary, identity, ownership);
|
|
480
|
+
const target = await safeFile(await validateRoot(boundary), scoped(reference.path));
|
|
481
|
+
await boundedRead(target, 32 * 1024 * 1024);
|
|
482
|
+
const handle = await open(target, "r+");
|
|
483
|
+
try {
|
|
484
|
+
await handle.sync();
|
|
485
|
+
}
|
|
486
|
+
finally {
|
|
487
|
+
await handle.close();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function epochOwner(identity, epoch) {
|
|
492
|
+
return canonicalCampaignJson({ schema: "campaign-epoch-owner-v1", identity: identity.digest, caseId: epoch.caseId, treatmentId: epoch.treatmentId, ordinal: epoch.ordinal, epoch: epoch.id, parentRevision: epoch.parentRevision, namespace: epoch.namespace });
|
|
493
|
+
}
|
|
494
|
+
async function verifyEpochDirectory(boundary, identity, epoch) {
|
|
495
|
+
const root = await validateRoot(boundary);
|
|
496
|
+
validateCampaignRelativePath(epoch.namespace);
|
|
497
|
+
const target = resolveWithinRoot(root, epoch.namespace);
|
|
498
|
+
await plainAncestors(target);
|
|
499
|
+
campaignAssert((await boundedRead(path.join(target, "owner.json"), 1024)).toString("utf8") === epochOwner(identity, epoch), "EPOCH_OWNER_UNVERIFIED");
|
|
500
|
+
return target;
|
|
501
|
+
}
|
|
502
|
+
/** Allocation is exclusive. A failed reservation leaves owned evidence, never a reusable invocation. */
|
|
503
|
+
export async function allocateCampaignEpoch(boundary, identity, ownership, epoch) {
|
|
504
|
+
await assertCampaignDispatch(boundary, identity, ownership);
|
|
505
|
+
validateCampaignRelativePath(epoch.namespace);
|
|
506
|
+
const root = await validateRoot(boundary);
|
|
507
|
+
const parent = await ensurePlainDirectory(root, path.posix.dirname(epoch.namespace));
|
|
508
|
+
const target = resolveWithinRoot(parent, path.posix.basename(epoch.namespace));
|
|
509
|
+
await mkdir(target);
|
|
510
|
+
await plainAncestors(target);
|
|
511
|
+
const handle = await open(path.join(target, "owner.json"), "wx");
|
|
512
|
+
try {
|
|
513
|
+
await handle.writeFile(epochOwner(identity, epoch));
|
|
514
|
+
await handle.sync();
|
|
515
|
+
}
|
|
516
|
+
finally {
|
|
517
|
+
await handle.close();
|
|
518
|
+
}
|
|
519
|
+
return verifyEpochDirectory(boundary, identity, epoch);
|
|
520
|
+
}
|
|
521
|
+
export async function prepareCampaignEpochTerminal(boundary, identity, task, epoch, source, reason) {
|
|
522
|
+
await verifyEpochDirectory(boundary, identity, epoch);
|
|
523
|
+
const local = projectCampaignTreatmentEvidence(source);
|
|
524
|
+
const root = await validateRoot(boundary);
|
|
525
|
+
const artifacts = [];
|
|
526
|
+
for (const reference of requiredCampaignArtifacts(local, task, reason !== "evaluated")) {
|
|
527
|
+
const relative = `${epoch.namespace}/${reference.path}`;
|
|
528
|
+
const bytes = await boundedRead(await safeFile(root, relative), 32 * 1024 * 1024);
|
|
529
|
+
artifacts.push({ ...reference, path: relative, sha256: campaignHash(bytes), bytes: bytes.length });
|
|
530
|
+
}
|
|
531
|
+
return { reason, outcome: local.realAgent?.providerStatus ?? "not-invoked", evidence: mapCampaignEpochEvidence(local, epoch.namespace, "prefix"), artifacts };
|
|
532
|
+
}
|
|
533
|
+
/** An immutable owned publication intent permits safe recovery of a partially written final family. */
|
|
534
|
+
export async function publishCampaignFinalArtifacts(boundary, identity, ownership, targets, runId, completedAt) {
|
|
535
|
+
await assertCampaignDispatch(boundary, identity, ownership);
|
|
536
|
+
const root = await validateRoot(boundary);
|
|
537
|
+
const artifacts = targets.map(t => ({ path: t.relativePath, kind: "provider", sha256: campaignHash(t.content), bytes: Buffer.byteLength(t.content) })).sort((a, b) => a.path < b.path ? -1 : 1);
|
|
538
|
+
const owner = canonicalCampaignJson({ schema: "campaign-final-owner-v2", identity: identity.digest, runId, completedAt, artifacts });
|
|
539
|
+
const marker = path.join(root, "campaign-final-owner-v2.json");
|
|
540
|
+
try {
|
|
541
|
+
await lstat(marker);
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
if (error.code !== "ENOENT")
|
|
545
|
+
throw error;
|
|
546
|
+
for (const target of targets) {
|
|
547
|
+
validateCampaignRelativePath(target.relativePath);
|
|
548
|
+
try {
|
|
549
|
+
await lstat(resolveWithinRoot(root, target.relativePath));
|
|
550
|
+
throw new Error("ARTIFACT_COLLISION");
|
|
551
|
+
}
|
|
552
|
+
catch (e) {
|
|
553
|
+
if (e.code !== "ENOENT")
|
|
554
|
+
throw e;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
const handle = await open(marker, "wx");
|
|
558
|
+
try {
|
|
559
|
+
await handle.writeFile(owner);
|
|
560
|
+
await handle.sync();
|
|
561
|
+
}
|
|
562
|
+
finally {
|
|
563
|
+
await handle.close();
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
campaignAssert((await boundedRead(marker, 16384)).toString("utf8") === owner, "FINAL_OWNER_MISMATCH");
|
|
567
|
+
await writeAgentSuccessRateArtifactTargets(targets.map(t => ({ filePath: resolveWithinRoot(root, t.relativePath), content: t.content })), {
|
|
568
|
+
ensureDirectory: async () => { },
|
|
569
|
+
exists: async (file) => { try {
|
|
570
|
+
await lstat(file);
|
|
571
|
+
return true;
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
if (error.code === "ENOENT")
|
|
575
|
+
return false;
|
|
576
|
+
throw error;
|
|
577
|
+
} },
|
|
578
|
+
writeFile: (file, content) => persistCampaignArtifact(boundary, identity, ownership, path.relative(root, file).replace(/\\/g, "/"), content),
|
|
579
|
+
removeFile: async () => { }
|
|
580
|
+
}, async (target) => {
|
|
581
|
+
await assertCampaignDispatch(boundary, identity, ownership);
|
|
582
|
+
campaignAssert((await boundedRead(marker, 16384)).toString("utf8") === owner, "FINAL_OWNER_MISMATCH");
|
|
583
|
+
const bytes = await boundedRead(await safeFile(root, path.relative(root, target.filePath).replace(/\\/g, "/")), 32 * 1024 * 1024);
|
|
584
|
+
campaignAssert(bytes.equals(Buffer.from(target.content)), "ARTIFACT_INTEGRITY");
|
|
585
|
+
return true;
|
|
586
|
+
});
|
|
587
|
+
return { completedAt, artifacts };
|
|
588
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { normalizeAgentDurationV091, normalizeAgentTokenUsageV091 } from "../../../agents/normalizeAgentTelemetry.js";
|
|
2
|
+
import { analyzeAgentSuccessTreatment } from "./analysis.js";
|
|
3
|
+
export const CAMPAIGN_QUANTITIES = ["finalTaskSuccess", "initialTaskSuccess", "firstDuration", "finalDuration", "totalDuration", "firstTokens", "finalTokens", "totalTokens", "taskCheckPassRate", "regressionCheckPassRate", "regressionFailureCount"];
|
|
4
|
+
export const QUALITY_REASONS = {
|
|
5
|
+
NO_MATCHED_OBSERVATIONS: "No validated matched observations are available; no mean or delta can be constructed.",
|
|
6
|
+
SELECTED_CASES_EXCLUDED: "Selected cases without usable measurements on both sides are excluded. Values describe only the matched subset.",
|
|
7
|
+
TOKEN_FIELD_PROVENANCE_UNPROVEN: "The recorded channel cannot prove direct measurement of every token field, including parser-derived totals.",
|
|
8
|
+
ESTIMATED_TOKENS: "Text-size estimates are not measured provider usage or billing evidence.",
|
|
9
|
+
UNATTRIBUTED_TOKENS: "The telemetry channel has no verified provider attribution.",
|
|
10
|
+
MISSING_ATTEMPT_MEASUREMENT: "At least one contributing attempt has no measurement; no complete total is fabricated.",
|
|
11
|
+
NOT_APPLICABLE: "This execution mode has no provider measurements."
|
|
12
|
+
};
|
|
13
|
+
export function campaignAttemptTelemetry(e) {
|
|
14
|
+
return (e.attempts?.map(a => a.evidence) ?? [e]).map((a, index) => {
|
|
15
|
+
const t = a.agentTokenUsage;
|
|
16
|
+
return { attemptNumber: index + 1, providerInvoked: a.realAgent?.providerInvoked === true,
|
|
17
|
+
duration: normalizeAgentDurationV091(a.realAgent?.providerInvoked ? a.timing.agentDurationMs : null),
|
|
18
|
+
tokens: normalizeAgentTokenUsageV091({ source: t?.source ?? "unavailable", ...(t?.totalTokens == null ? {} : { totalTokens: t.totalTokens }), ...(t?.inputTokens == null ? {} : { inputTokens: t.inputTokens }), ...(t?.outputTokens == null ? {} : { outputTokens: t.outputTokens }) }, t?.reliability ?? "unavailable"),
|
|
19
|
+
tokenInterpretation: t?.source === "estimated-from-text" ? "estimated" : t?.totalTokens == null ? "unavailable" : "recorded-with-channel-provenance" };
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Only established analysis metrics are selected. Pending and historical epochs never enter scientific analysis. */
|
|
23
|
+
export function classifyCampaignEvidence(cp, tasks) {
|
|
24
|
+
const entries = tasks.map((task, i) => cp.units.slice(i * 2, i * 2 + 2).map(u => u.state === "completed" && u.completion ? { analysis: analyzeAgentSuccessTreatment(task, u.completion.evidence), telemetry: campaignAttemptTelemetry(u.completion.evidence) } : null));
|
|
25
|
+
const numeric = (m) => m?.availability !== "available" ? null : typeof m.value === "boolean" ? Number(m.value) : typeof m.value === "number" && Number.isFinite(m.value) ? m.value : null;
|
|
26
|
+
return CAMPAIGN_QUANTITIES.map(quantity => {
|
|
27
|
+
const basis = quantity.startsWith("first") || quantity === "initialTaskSuccess" ? "first-attempt" : quantity.startsWith("total") ? "total-across-attempts" : "final-attempt";
|
|
28
|
+
const token = quantity.endsWith("Tokens"), duration = quantity.endsWith("Duration");
|
|
29
|
+
const measurementBasis = token ? "recorded-token-channel" : duration ? "lab-wall-clock" : quantity.endsWith("TaskSuccess") ? "trusted-task-evaluation" : "trusted-check-evaluation";
|
|
30
|
+
const reasons = new Set();
|
|
31
|
+
const matched = [];
|
|
32
|
+
entries.forEach((sides, i) => {
|
|
33
|
+
const values = sides.map(side => {
|
|
34
|
+
if (!side)
|
|
35
|
+
return null;
|
|
36
|
+
const { metrics: m, repair: r } = side.analysis;
|
|
37
|
+
if (quantity === "finalTaskSuccess")
|
|
38
|
+
return numeric(m.taskSuccess);
|
|
39
|
+
if (quantity === "initialTaskSuccess")
|
|
40
|
+
return numeric(r?.initialAttemptTaskSuccess ?? m.taskSuccess);
|
|
41
|
+
if (!token && !duration)
|
|
42
|
+
return numeric(m[quantity]);
|
|
43
|
+
const selected = basis === "first-attempt" ? side.telemetry.slice(0, 1) : basis === "final-attempt" ? side.telemetry.slice(-1) : side.telemetry.filter(a => a.providerInvoked);
|
|
44
|
+
if (selected.length === 0 || selected.some(a => !a.providerInvoked))
|
|
45
|
+
return null;
|
|
46
|
+
const metric = duration ? basis === "first-attempt" ? r?.firstAttemptProviderDurationMs ?? m.agentDurationMs : basis === "final-attempt" ? m.agentDurationMs : r?.totalProviderDurationMs ?? m.agentDurationMs : basis === "first-attempt" ? r?.firstAttemptProviderTokens ?? m.agentTotalTokens : basis === "final-attempt" ? m.agentTotalTokens : r?.totalProviderTokens ?? m.agentTotalTokens;
|
|
47
|
+
const value = numeric(metric);
|
|
48
|
+
if (value === null && basis === "total-across-attempts")
|
|
49
|
+
reasons.add("MISSING_ATTEMPT_MEASUREMENT");
|
|
50
|
+
return value;
|
|
51
|
+
});
|
|
52
|
+
if (values[0] !== null && values[1] !== null) {
|
|
53
|
+
matched.push({ id: tasks[i].id, raw: values[0], pack: values[1] });
|
|
54
|
+
if (token)
|
|
55
|
+
for (const side of sides) {
|
|
56
|
+
const attempts = basis === "first-attempt" ? side.telemetry.slice(0, 1) : basis === "final-attempt" ? side.telemetry.slice(-1) : side.telemetry.filter(a => a.providerInvoked);
|
|
57
|
+
reasons.add("TOKEN_FIELD_PROVENANCE_UNPROVEN");
|
|
58
|
+
for (const a of attempts) {
|
|
59
|
+
if (a.tokens.evidenceLevel === "estimated")
|
|
60
|
+
reasons.add("ESTIMATED_TOKENS");
|
|
61
|
+
if (a.tokens.classificationReason === "unattributed-source" || a.tokens.source === "unavailable")
|
|
62
|
+
reasons.add("UNATTRIBUTED_TOKENS");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const matchedCaseIds = matched.map(m => m.id), excludedCaseIds = tasks.map(t => t.id).filter(id => !matchedCaseIds.includes(id));
|
|
68
|
+
if (matched.length === 0)
|
|
69
|
+
reasons.add("NO_MATCHED_OBSERVATIONS");
|
|
70
|
+
if (excludedCaseIds.length)
|
|
71
|
+
reasons.add("SELECTED_CASES_EXCLUDED");
|
|
72
|
+
if ((token || duration) && cp.identity.config.providerId === "deterministic-fixture")
|
|
73
|
+
reasons.add("NOT_APPLICABLE");
|
|
74
|
+
const mean = (pick) => matched.length ? matched.reduce((n, m) => n + pick(m), 0) / matched.length : null;
|
|
75
|
+
return { quantity, basis, measurementBasis, unit: token ? "tokens" : duration ? "ms" : quantity === "regressionFailureCount" ? "count" : "ratio", quality: matched.length === 0 ? "unavailable" : excludedCaseIds.length || token ? "partial" : "strong", selectedCaseCount: tasks.length, matchedCaseIds, excludedCaseIds, rawFullFileMean: mean(m => m.raw), contextPackMean: mean(m => m.pack), meanDifference: mean(m => m.pack - m.raw), reasons: [...reasons], reasonExplanations: [...reasons].map(r => QUALITY_REASONS[r]) };
|
|
76
|
+
});
|
|
77
|
+
}
|