engineering-behavior-observatory 0.2.4 → 0.2.6
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/dist/src/agent-sdk-normalizer.js +8 -4
- package/dist/src/agent-sdk-run.js +9 -2
- package/dist/src/codex-run.js +9 -2
- package/dist/src/cursor-sdk.js +9 -2
- package/dist/src/openhands-run.js +5 -3
- package/dist/src/pi.js +9 -2
- package/dist/src/run-bundles.js +78 -15
- package/dist/src/verifiers.js +27 -7
- package/docs/guides/evidence-and-sharing.md +45 -0
- package/package.json +3 -1
- package/release/0.2.5/KNOWN_LIMITATIONS.md +12 -0
- package/release/0.2.5/README.md +25 -0
- package/release/0.2.5/reproducibility.json +74 -0
- package/release/0.2.6/KNOWN_LIMITATIONS.md +15 -0
- package/release/0.2.6/README.md +26 -0
- package/release/0.2.6/reproducibility.json +74 -0
- package/release/README.md +2 -0
- package/schemas/run-bundles/v1.json +0 -1
|
@@ -116,8 +116,8 @@ export async function readQualifiedRunCapture(bundleRoot) {
|
|
|
116
116
|
const manifest = readManifest(root);
|
|
117
117
|
await assertPersistedStructuralQualification(root, manifest);
|
|
118
118
|
const qualification = await qualifyRunBundle(root);
|
|
119
|
-
if (!qualification.semanticAnalysisUsable
|
|
120
|
-
|
|
119
|
+
if (!qualification.semanticAnalysisUsable || Object.entries(qualification.dimensions)
|
|
120
|
+
.some(([dimension, result]) => dimension !== "workspace" && result.status === "unqualified")) {
|
|
121
121
|
const reasons = qualification.reasons.map(({ code }) => code).join(", ") || "unknown qualification failure";
|
|
122
122
|
throw new Error(`Agent SDK normalization requires capture-qualified evidence: ${reasons}.`);
|
|
123
123
|
}
|
|
@@ -677,11 +677,14 @@ function assertQualifiedInput(input) {
|
|
|
677
677
|
throw new Error("Agent SDK normalization input identity is invalid.");
|
|
678
678
|
}
|
|
679
679
|
const kinds = new Set(input.records.map(({ record }) => record.kind));
|
|
680
|
-
for (const required of ["session", "hook", "
|
|
680
|
+
for (const required of ["session", "hook", "manifest"]) {
|
|
681
681
|
if (!kinds.has(required))
|
|
682
682
|
throw new Error(`Capture-qualified Agent SDK input is missing required ${required} evidence.`);
|
|
683
683
|
}
|
|
684
684
|
const assessmentMode = input.records.map(assessmentModeOf).find((value) => value !== undefined);
|
|
685
|
+
if (assessmentMode !== "observational" && !kinds.has("workspace")) {
|
|
686
|
+
throw new Error("Capture-qualified verified input is missing required workspace evidence.");
|
|
687
|
+
}
|
|
685
688
|
if (assessmentMode !== "observational" && !input.records.some(isVerifierRecord)) {
|
|
686
689
|
throw new Error("Capture-qualified verified input is missing required verifier evidence.");
|
|
687
690
|
}
|
|
@@ -720,7 +723,8 @@ async function assertPersistedStructuralQualification(root, manifest) {
|
|
|
720
723
|
return;
|
|
721
724
|
const report = asRecord(parseJson(await readVerifiedArtifact(root, descriptor.relativePath, digestFrom(descriptor.digest), MAX_EVIDENCE_BYTES)));
|
|
722
725
|
const structural = asRecord(report?.structuralQualification);
|
|
723
|
-
if (structural?.
|
|
726
|
+
if (structural?.semanticAnalysisUsable === false
|
|
727
|
+
|| structural?.status === "unqualified" && structural?.semanticAnalysisUsable !== true) {
|
|
724
728
|
throw new Error("Agent SDK normalization rejects the persisted unqualified structural capture report.");
|
|
725
729
|
}
|
|
726
730
|
}
|
|
@@ -102,8 +102,15 @@ export async function captureClaudeAgentSdkRun(options) {
|
|
|
102
102
|
return workspace;
|
|
103
103
|
},
|
|
104
104
|
cleanup: async (context) => {
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
// Retain the source on packaging failure; the assembler records the outcome gap.
|
|
106
|
+
if (workspace?.status === "ready") {
|
|
107
|
+
try {
|
|
108
|
+
await captureWorkspace();
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
107
114
|
await options.workspace.cleanup?.(context);
|
|
108
115
|
},
|
|
109
116
|
};
|
package/dist/src/codex-run.js
CHANGED
|
@@ -89,8 +89,15 @@ export async function captureCodexAppServerRun(options) {
|
|
|
89
89
|
return workspace;
|
|
90
90
|
},
|
|
91
91
|
cleanup: async (context) => {
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
// Packaging gaps must not rewrite native completion or delete recoverable work.
|
|
93
|
+
if (workspace?.status === "ready") {
|
|
94
|
+
try {
|
|
95
|
+
await captureWorkspace();
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
94
101
|
await options.workspace.cleanup?.(context);
|
|
95
102
|
},
|
|
96
103
|
};
|
package/dist/src/cursor-sdk.js
CHANGED
|
@@ -119,8 +119,15 @@ export async function captureCursorSdkRun(options) {
|
|
|
119
119
|
return workspace;
|
|
120
120
|
},
|
|
121
121
|
cleanup: async (context) => {
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
// Retain the source on packaging failure; the assembler records the outcome gap.
|
|
123
|
+
if (workspace?.status === "ready") {
|
|
124
|
+
try {
|
|
125
|
+
await captureWorkspace();
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
124
131
|
await options.workspace.cleanup?.(context);
|
|
125
132
|
},
|
|
126
133
|
};
|
|
@@ -130,7 +130,7 @@ export async function captureOpenHandsAgentServerRun(options) {
|
|
|
130
130
|
catch (error) {
|
|
131
131
|
if (workspaceOutcome === undefined) {
|
|
132
132
|
workspaceCaptureError = error instanceof Error ? error.message : String(error);
|
|
133
|
-
|
|
133
|
+
return; // Preserve native completion and the source for later capture recovery.
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
await options.workspace.cleanup?.(context);
|
|
@@ -318,7 +318,8 @@ export async function captureOpenHandsAgentServerRun(options) {
|
|
|
318
318
|
const retainedWorkspacePath = workspace?.status === "ready" && workspaceOutcome === undefined
|
|
319
319
|
? workspace.path
|
|
320
320
|
: undefined;
|
|
321
|
-
if (!
|
|
321
|
+
if (!qualification.semanticAnalysisUsable || Object.entries(qualification.dimensions)
|
|
322
|
+
.some(([dimension, result]) => dimension !== "workspace" && result.status === "unqualified")) {
|
|
322
323
|
return {
|
|
323
324
|
attempt,
|
|
324
325
|
manifest,
|
|
@@ -332,7 +333,8 @@ export async function captureOpenHandsAgentServerRun(options) {
|
|
|
332
333
|
await validateUniformEvents(normalized.events, {
|
|
333
334
|
resolve: (reference) => resolvesCaptureReference(capture, reference),
|
|
334
335
|
});
|
|
335
|
-
return { attempt, manifest, qualification, capture, normalized
|
|
336
|
+
return { attempt, manifest, qualification, capture, normalized,
|
|
337
|
+
...(retainedWorkspacePath === undefined ? {} : { retainedWorkspacePath }) };
|
|
336
338
|
}
|
|
337
339
|
catch (error) {
|
|
338
340
|
return {
|
package/dist/src/pi.js
CHANGED
|
@@ -150,8 +150,15 @@ export async function capturePiSdkRun(options) {
|
|
|
150
150
|
return workspace;
|
|
151
151
|
},
|
|
152
152
|
cleanup: async (context) => {
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
// Retain the source on packaging failure; the assembler records the outcome gap.
|
|
154
|
+
if (workspace?.status === "ready") {
|
|
155
|
+
try {
|
|
156
|
+
await captureWorkspace();
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
155
162
|
await options.workspace.cleanup?.(context);
|
|
156
163
|
},
|
|
157
164
|
};
|
package/dist/src/run-bundles.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { execFile, spawn } from "node:child_process";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { createReadStream, createWriteStream } from "node:fs";
|
|
4
|
-
import { cp, lstat, mkdir, mkdtemp, readdir, rm, rmdir, utimes, writeFile } from "node:fs/promises";
|
|
4
|
+
import { cp, lstat, mkdir, mkdtemp, readdir, readlink, realpath, rm, rmdir, symlink, utimes, writeFile } from "node:fs/promises";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
|
-
import { join, resolve, sep } from "node:path";
|
|
6
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
8
|
import { Transform } from "node:stream";
|
|
9
9
|
import { pipeline } from "node:stream/promises";
|
|
@@ -259,7 +259,33 @@ async function withWorkspaceOutcomeProjection(options, use) {
|
|
|
259
259
|
throw new Error(`Workspace outcome exclusion "${name}" is invalid.`);
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
|
-
|
|
262
|
+
const absoluteLinks = [];
|
|
263
|
+
const sourceRoot = await realpath(finalPath);
|
|
264
|
+
const contained = (path) => {
|
|
265
|
+
const rel = relative(sourceRoot, path);
|
|
266
|
+
return !isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
|
|
267
|
+
};
|
|
268
|
+
const collectLinks = async (directory) => {
|
|
269
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
270
|
+
const path = join(directory, entry.name);
|
|
271
|
+
if (entry.isDirectory()) {
|
|
272
|
+
if (!exclusions.includes(entry.name))
|
|
273
|
+
await collectLinks(path);
|
|
274
|
+
}
|
|
275
|
+
else if (entry.isSymbolicLink()) {
|
|
276
|
+
const target = await readlink(path);
|
|
277
|
+
if (!isAbsolute(target))
|
|
278
|
+
continue;
|
|
279
|
+
const resolvedTarget = await realpath(path);
|
|
280
|
+
if (!contained(resolvedTarget)) {
|
|
281
|
+
throw new Error(`Workspace symbolic link escapes its root at "${relative(sourceRoot, path)}".`);
|
|
282
|
+
}
|
|
283
|
+
absoluteLinks.push({ path: relative(sourceRoot, path), target: relative(dirname(path), resolvedTarget) || "." });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
await collectLinks(sourceRoot);
|
|
288
|
+
if (absoluteLinks.length === 0 && exclusions.length === 0 && options.respectGitignore !== true && options.omitEmptyDirectories !== true) {
|
|
263
289
|
return use(startPath, finalPath);
|
|
264
290
|
}
|
|
265
291
|
await assertNoWorkspaceHardLinks(finalPath, new Set(exclusions));
|
|
@@ -268,11 +294,18 @@ async function withWorkspaceOutcomeProjection(options, use) {
|
|
|
268
294
|
try {
|
|
269
295
|
await cp(finalPath, projectedPath, {
|
|
270
296
|
recursive: true,
|
|
297
|
+
verbatimSymlinks: true,
|
|
271
298
|
preserveTimestamps: true,
|
|
272
299
|
force: false,
|
|
273
300
|
filter: async (source) => source === finalPath
|
|
274
301
|
|| !await isExcludedWorkspaceDirectory(finalPath, source, exclusions),
|
|
275
302
|
});
|
|
303
|
+
// Only the derived capture is relocated; native workspace link text is untouched.
|
|
304
|
+
for (const link of absoluteLinks) {
|
|
305
|
+
const path = join(projectedPath, link.path);
|
|
306
|
+
await rm(path);
|
|
307
|
+
await symlink(link.target, path);
|
|
308
|
+
}
|
|
276
309
|
if (options.respectGitignore === true)
|
|
277
310
|
await removeIgnoredWorkspaceEntries(startPath, projectedPath);
|
|
278
311
|
if (options.omitEmptyDirectories === true)
|
|
@@ -648,7 +681,7 @@ async function workspacePatchApplies(startingWorkspacePath, patch) {
|
|
|
648
681
|
const applied = join(temporaryRoot, "workspace");
|
|
649
682
|
const patchPath = join(temporaryRoot, "workspace.patch");
|
|
650
683
|
try {
|
|
651
|
-
await cp(resolve(startingWorkspacePath), applied, { recursive: true, preserveTimestamps: true, force: false });
|
|
684
|
+
await cp(resolve(startingWorkspacePath), applied, { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
652
685
|
if (patch.length > 0) {
|
|
653
686
|
await writeFile(patchPath, patch, { flag: "wx", mode: 0o600 });
|
|
654
687
|
await execFileAsync("git", ["apply", "--check", "--binary", patchPath], { cwd: applied });
|
|
@@ -842,7 +875,7 @@ async function removeIgnoredWorkspaceEntries(startPath, finalPath) {
|
|
|
842
875
|
const globalExcludes = join(temporaryRoot, "global-excludes");
|
|
843
876
|
try {
|
|
844
877
|
await writeFile(globalExcludes, "");
|
|
845
|
-
await cp(startPath, baseline, { recursive: true, preserveTimestamps: true, force: false });
|
|
878
|
+
await cp(startPath, baseline, { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
846
879
|
await execFileAsync("git", ["init", "--quiet"], { cwd: baseline });
|
|
847
880
|
await execFileAsync("git", ["add", "--force", "--all"], { cwd: baseline });
|
|
848
881
|
for (const relativePath of await ignoredWorkspacePaths(baseline, finalPath, globalExcludes)) {
|
|
@@ -910,12 +943,31 @@ async function isExcludedWorkspaceDirectory(root, source, exclusions) {
|
|
|
910
943
|
}
|
|
911
944
|
return false;
|
|
912
945
|
}
|
|
913
|
-
async function removeEmptyDirectories(directory, root = directory) {
|
|
946
|
+
async function removeEmptyDirectories(directory, root = directory, targets) {
|
|
947
|
+
if (targets === undefined) {
|
|
948
|
+
targets = new Set();
|
|
949
|
+
async function collect(path) {
|
|
950
|
+
for (const entry of await readdir(path, { withFileTypes: true })) {
|
|
951
|
+
const child = join(path, entry.name);
|
|
952
|
+
if (entry.isDirectory())
|
|
953
|
+
await collect(child);
|
|
954
|
+
else if (entry.isSymbolicLink()) {
|
|
955
|
+
// Omission must not turn a valid link into a dangling one. Invalid
|
|
956
|
+
// links are rejected later by workspace hashing; no content is read.
|
|
957
|
+
try {
|
|
958
|
+
targets.add(await realpath(child));
|
|
959
|
+
}
|
|
960
|
+
catch { /* validated by hashing */ }
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
await collect(directory);
|
|
965
|
+
}
|
|
914
966
|
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
915
967
|
if (entry.isDirectory())
|
|
916
|
-
await removeEmptyDirectories(join(directory, entry.name), root);
|
|
968
|
+
await removeEmptyDirectories(join(directory, entry.name), root, targets);
|
|
917
969
|
}
|
|
918
|
-
if (directory !== root && (await readdir(directory)).length === 0)
|
|
970
|
+
if (directory !== root && !targets.has(await realpath(directory)) && (await readdir(directory)).length === 0)
|
|
919
971
|
await rmdir(directory);
|
|
920
972
|
}
|
|
921
973
|
async function restoreProjectedDirectoryTimestamps(source, projected) {
|
|
@@ -938,7 +990,7 @@ async function workspacePatch(startPath, finalPath, finalTreeDigest) {
|
|
|
938
990
|
const applied = join(temporaryRoot, "applied");
|
|
939
991
|
const patchPath = join(temporaryRoot, "workspace.patch");
|
|
940
992
|
try {
|
|
941
|
-
await cp(startPath, worktree, { recursive: true, preserveTimestamps: true, force: false });
|
|
993
|
+
await cp(startPath, worktree, { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
942
994
|
await execFileAsync("git", ["init", "--quiet"], { cwd: worktree });
|
|
943
995
|
await execFileAsync("git", ["add", "--force", "--all"], { cwd: worktree });
|
|
944
996
|
// A tree is enough for the diff. A commit can spawn detached maintenance
|
|
@@ -951,7 +1003,7 @@ async function workspacePatch(startPath, finalPath, finalTreeDigest) {
|
|
|
951
1003
|
for (const entry of await readdir(finalPath)) {
|
|
952
1004
|
if (entry === ".git")
|
|
953
1005
|
throw new Error("Workspace patches cannot retain Git administrative state.");
|
|
954
|
-
await cp(join(finalPath, entry), join(worktree, entry), { recursive: true, preserveTimestamps: true, force: false });
|
|
1006
|
+
await cp(join(finalPath, entry), join(worktree, entry), { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
955
1007
|
}
|
|
956
1008
|
await execFileAsync("git", ["add", "--force", "--intent-to-add", "--all"], { cwd: worktree });
|
|
957
1009
|
let stdout;
|
|
@@ -966,12 +1018,14 @@ async function workspacePatch(startPath, finalPath, finalTreeDigest) {
|
|
|
966
1018
|
throw error;
|
|
967
1019
|
}
|
|
968
1020
|
const patch = Buffer.from(stdout);
|
|
969
|
-
await cp(startPath, applied, { recursive: true, preserveTimestamps: true, force: false });
|
|
1021
|
+
await cp(startPath, applied, { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
970
1022
|
if (patch.length > 0) {
|
|
971
1023
|
await writeFile(patchPath, patch, { flag: "wx", mode: 0o600 });
|
|
972
1024
|
await execFileAsync("git", ["apply", "--binary", patchPath], { cwd: applied });
|
|
973
1025
|
}
|
|
974
|
-
|
|
1026
|
+
// Git omits empty directories, including legitimate link targets. An
|
|
1027
|
+
// invalid reconstructed tree is a patch limitation, so use the snapshot.
|
|
1028
|
+
const appliedTreeDigest = await digestWorkspaceTree(applied).catch(() => undefined);
|
|
975
1029
|
if (appliedTreeDigest !== finalTreeDigest) {
|
|
976
1030
|
return undefined;
|
|
977
1031
|
}
|
|
@@ -989,8 +1043,8 @@ async function workspaceSnapshot(finalPath, finalTreeDigest, use) {
|
|
|
989
1043
|
const extracted = join(temporaryRoot, "extracted");
|
|
990
1044
|
try {
|
|
991
1045
|
await mkdir(snapshotParent);
|
|
992
|
-
await cp(finalPath, snapshotRoot, { recursive: true, preserveTimestamps: true, force: false });
|
|
993
|
-
const child = spawn(TAR_COMMAND, ["-czf", "-", "-C", snapshotParent, "workspace"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
1046
|
+
await cp(finalPath, snapshotRoot, { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
1047
|
+
const child = spawn(TAR_COMMAND, [...(process.platform === "darwin" ? ["--no-mac-metadata"] : []), "-czf", "-", "-C", snapshotParent, "workspace"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
994
1048
|
let stderr = "";
|
|
995
1049
|
child.stderr.on("data", (chunk) => { stderr = (stderr + chunk.toString("utf8")).slice(-8192); });
|
|
996
1050
|
const exited = new Promise((resolveExit, reject) => {
|
|
@@ -1012,7 +1066,16 @@ async function workspaceSnapshot(finalPath, finalTreeDigest, use) {
|
|
|
1012
1066
|
await Promise.allSettled([exited, streamed]);
|
|
1013
1067
|
}
|
|
1014
1068
|
await mkdir(extracted, { mode: 0o700 });
|
|
1015
|
-
|
|
1069
|
+
// macOS libarchive consumes genuine ._* AppleDouble files even when its
|
|
1070
|
+
// metadata flags are disabled. Native pax preserves them with copyfile off.
|
|
1071
|
+
if (process.platform === "darwin") {
|
|
1072
|
+
await execFileAsync("/bin/pax", ["-rz", "-p", "p", "-f", snapshotPath], {
|
|
1073
|
+
cwd: extracted, env: { ...process.env, COPYFILE_DISABLE: "1" },
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
else {
|
|
1077
|
+
await execFileAsync(TAR_COMMAND, ["-xzpf", snapshotPath, "-C", extracted]);
|
|
1078
|
+
}
|
|
1016
1079
|
if (await digestWorkspaceTree(join(extracted, "workspace")) !== finalTreeDigest) {
|
|
1017
1080
|
throw new Error("Bounded workspace snapshot cannot reproduce the final workspace tree.");
|
|
1018
1081
|
}
|
package/dist/src/verifiers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { createReadStream, createWriteStream } from "node:fs";
|
|
4
|
-
import { chmod, lstat, mkdir, mkdtemp, open, readdir, readFile, realpath, rm, utimes } from "node:fs/promises";
|
|
4
|
+
import { chmod, lstat, mkdir, mkdtemp, open, readdir, readFile, readlink, realpath, rm, utimes } from "node:fs/promises";
|
|
5
5
|
import { basename, dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { performance } from "node:perf_hooks";
|
|
@@ -72,10 +72,10 @@ async function digestWorkspaceEntries(workspacePath, includeTimestamps, signal)
|
|
|
72
72
|
if (!metadata.isDirectory() || metadata.isSymbolicLink())
|
|
73
73
|
throw new Error("Workspace root is not a directory.");
|
|
74
74
|
hash.update(`root\0${metadata.mode & 4095n}\0${includeTimestamps ? `${workspaceTimestamp(metadata)}\0` : ""}`);
|
|
75
|
-
await hashWorkspaceDirectory(root, "", hash, includeTimestamps, signal);
|
|
75
|
+
await hashWorkspaceDirectory(root, "", hash, includeTimestamps, signal, root);
|
|
76
76
|
return `sha256:${hash.digest("hex")}`;
|
|
77
77
|
}
|
|
78
|
-
async function hashWorkspaceDirectory(directory, relativeDirectory, hash, includeTimestamps, signal) {
|
|
78
|
+
async function hashWorkspaceDirectory(directory, relativeDirectory, hash, includeTimestamps, signal, root = directory) {
|
|
79
79
|
assertNotAborted(signal);
|
|
80
80
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
81
81
|
entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
@@ -83,11 +83,31 @@ async function hashWorkspaceDirectory(directory, relativeDirectory, hash, includ
|
|
|
83
83
|
const path = join(directory, entry.name);
|
|
84
84
|
const relativePath = posix.join(relativeDirectory, entry.name);
|
|
85
85
|
const metadata = await lstat(path, { bigint: true });
|
|
86
|
-
if (metadata.isSymbolicLink())
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
if (metadata.isSymbolicLink()) {
|
|
87
|
+
// Preserve link text, never hash the referent through the link. Both its
|
|
88
|
+
// lexical target and resolved chain must stay in this workspace.
|
|
89
|
+
const target = await readlink(path);
|
|
90
|
+
const contained = (candidate) => {
|
|
91
|
+
const rel = relative(root, candidate);
|
|
92
|
+
return !isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
|
|
93
|
+
};
|
|
94
|
+
if (!isAbsolute(target) && !contained(resolve(dirname(path), target))) {
|
|
95
|
+
throw new Error(`Workspace symbolic link escapes its root at "${relativePath}".`);
|
|
96
|
+
}
|
|
97
|
+
let resolved;
|
|
98
|
+
try {
|
|
99
|
+
resolved = await realpath(path);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
throw new Error(`Workspace symbolic link is dangling or cyclic at "${relativePath}".`);
|
|
103
|
+
}
|
|
104
|
+
if (!contained(resolved))
|
|
105
|
+
throw new Error(`Workspace symbolic link escapes its root at "${relativePath}".`);
|
|
106
|
+
hash.update(`symlink\0${relativePath}\0${metadata.mode & 4095n}\0${includeTimestamps ? `${workspaceTimestamp(metadata)}\0` : ""}${Buffer.byteLength(target)}\0${target}`);
|
|
107
|
+
}
|
|
108
|
+
else if (metadata.isDirectory()) {
|
|
89
109
|
hash.update(`directory\0${relativePath}\0${metadata.mode & 4095n}\0${includeTimestamps ? `${workspaceTimestamp(metadata)}\0` : ""}`);
|
|
90
|
-
await hashWorkspaceDirectory(path, relativePath, hash, includeTimestamps, signal);
|
|
110
|
+
await hashWorkspaceDirectory(path, relativePath, hash, includeTimestamps, signal, root);
|
|
91
111
|
}
|
|
92
112
|
else if (metadata.isFile()) {
|
|
93
113
|
if (metadata.nlink > 1n)
|
|
@@ -41,11 +41,56 @@ events can still explain a stop. Keep terminal state, infrastructure failure,
|
|
|
41
41
|
and capture quality separate. Preserve `retainedWorkspacePath` when outcome
|
|
42
42
|
packaging failed; that path is a recovery location, not qualified evidence.
|
|
43
43
|
|
|
44
|
+
A workspace packaging error does not change a completed observational execution
|
|
45
|
+
into a model or infrastructure failure. EBO omits the unpublishable workspace
|
|
46
|
+
artifact, retains the source for recovery, and records the outcome gap. Native
|
|
47
|
+
events, hooks, and telemetry remain available. Normalization and behavior analysis
|
|
48
|
+
can use qualified semantic evidence even when the workspace dimension is
|
|
49
|
+
unqualified; claims about missing work-product evidence must abstain. Verified
|
|
50
|
+
tasks still require their verifier and workspace binding.
|
|
51
|
+
|
|
52
|
+
Overall bundle qualification can remain `unqualified` because an outcome is
|
|
53
|
+
missing. Consumers should inspect `semanticAnalysisUsable` and the individual
|
|
54
|
+
qualification dimensions instead of treating that aggregate as a pipeline-wide
|
|
55
|
+
fatal error. Export independently enforces its sanitization and sharing policy;
|
|
56
|
+
successful capture is not permission to share.
|
|
57
|
+
|
|
44
58
|
Capture reports retain packaging exceptions as `workspace-capture-error`
|
|
45
59
|
missing-evidence entries. Inspect the detail before retrying capture against a
|
|
46
60
|
retained workspace. A later successful capture is recovery evidence; it does
|
|
47
61
|
not change the original attempt's recorded failure.
|
|
48
62
|
|
|
63
|
+
When the native run completed but capture failed, recover capture without
|
|
64
|
+
executing the task again. Materialize the same frozen starting workspace, use
|
|
65
|
+
the same capture exclusions, and assemble a separate bundle with byte-identical
|
|
66
|
+
native evidence plus the recovered workspace outcome. Record the original
|
|
67
|
+
manifest digest, recovery time, capture-code identity and qualification beside
|
|
68
|
+
the recovered bundle. Preserve the original failed bundle. A later workspace
|
|
69
|
+
capture cannot prove the retained directory was unchanged since execution ended
|
|
70
|
+
unless a fingerprint was recorded then. Do not count the recovery as another
|
|
71
|
+
model trial or as an independent sample.
|
|
72
|
+
|
|
73
|
+
Workspace outcomes preserve relative symbolic links whose lexical targets and
|
|
74
|
+
resolved referents stay inside the workspace. Contained absolute links are valid:
|
|
75
|
+
the derived capture rewrites them as relative links to their resolved in-workspace
|
|
76
|
+
targets, so the result survives relocation. The source workspace is not changed.
|
|
77
|
+
Relative link text is preserved, not replaced by the referent's bytes.
|
|
78
|
+
Empty-directory omission keeps directories referenced by links. External,
|
|
79
|
+
dangling and cyclic links prevent workspace artifact packaging, not native event
|
|
80
|
+
capture. Patch application or snapshot extraction must still
|
|
81
|
+
reproduce the resulting tree digest. This does not relax task-archive admission
|
|
82
|
+
or verifier sandbox rules.
|
|
83
|
+
|
|
84
|
+
On macOS, snapshot creation disables generated AppleDouble metadata. Snapshot
|
|
85
|
+
verification uses the built-in `/bin/pax` with `COPYFILE_DISABLE=1` so genuine
|
|
86
|
+
`._*` files remain files. macOS tar may otherwise consume them as metadata,
|
|
87
|
+
changing the reconstructed tree. To extract a snapshot for inspection on macOS:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
mkdir restored
|
|
91
|
+
(cd restored && COPYFILE_DISABLE=1 /bin/pax -rz -p p -f /absolute/path/workspace.tar.gz)
|
|
92
|
+
```
|
|
93
|
+
|
|
49
94
|
Workspace capture tries a patch up to 64 MiB, then falls back to a snapshot
|
|
50
95
|
when the patch is too large or cannot reproduce the tree. Snapshots stream to
|
|
51
96
|
disk with a 1 GiB compressed limit and are extracted to verify the tree digest
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engineering-behavior-observatory",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Capture engineering-agent trajectories, evaluate behavior, and inspect cited evidence across harnesses.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"release/0.2.2/",
|
|
49
49
|
"release/0.2.3/",
|
|
50
50
|
"release/0.2.4/",
|
|
51
|
+
"release/0.2.5/",
|
|
52
|
+
"release/0.2.6/",
|
|
51
53
|
"schemas/",
|
|
52
54
|
"scripts/atlas-grafana.sh"
|
|
53
55
|
],
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# v0.2.5 known limitations
|
|
2
|
+
|
|
3
|
+
The [v0.2.4 limitations](../0.2.4/KNOWN_LIMITATIONS.md) continue to apply.
|
|
4
|
+
|
|
5
|
+
- Symlink support applies to workspace outcome capture, not task-archive
|
|
6
|
+
admission or permission to read outside the workspace.
|
|
7
|
+
- On macOS, use the documented native `pax` command when inspecting snapshots
|
|
8
|
+
containing genuine AppleDouble files. Default `tar` extraction can consume them.
|
|
9
|
+
- Capture-only recovery can use retained evidence and workspace files without
|
|
10
|
+
rerunning a model, but cannot reconstruct missing files or prove an earlier
|
|
11
|
+
workspace state without a fingerprint captured at that time.
|
|
12
|
+
- Workspace snapshots remain restricted evidence, not sanitized partner exports.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# v0.2.5: Preserve framework files in workspace capture
|
|
2
|
+
|
|
3
|
+
Workspace capture now preserves contained relative symbolic links, including
|
|
4
|
+
framework link chains and links to empty directories. Absolute, escaping,
|
|
5
|
+
dangling, and cyclic links remain rejected. Copies retain the original link
|
|
6
|
+
text, and patches that cannot reproduce the tree fall back to a snapshot.
|
|
7
|
+
|
|
8
|
+
On macOS, snapshot verification uses native `pax` to preserve genuine AppleDouble
|
|
9
|
+
files that macOS `tar` otherwise consumes as metadata. Snapshots remain tar.gz
|
|
10
|
+
archives. No runtime dependencies, pins, or serialized artifact formats change.
|
|
11
|
+
|
|
12
|
+
## Verification
|
|
13
|
+
|
|
14
|
+
Regression tests cover link preservation, invalid targets, empty target
|
|
15
|
+
directories, AppleDouble bytes, and long paths. A completed model attempt's
|
|
16
|
+
retained workspace was recovered as a 695,459,840-byte snapshot with a verified
|
|
17
|
+
extracted tree digest, without another model call or rewriting original evidence.
|
|
18
|
+
The recovery qualified with an explicit missing-telemetry-receipt gap; it records
|
|
19
|
+
the later capture time rather than claiming an exact end-of-turn snapshot.
|
|
20
|
+
|
|
21
|
+
Run `npm ci` and `npm run acceptance` on Node 24.19.0 for tests, documentation
|
|
22
|
+
and package checks, and two byte-identical package builds. See
|
|
23
|
+
[known limitations](KNOWN_LIMITATIONS.md), the
|
|
24
|
+
[reproducibility manifest](reproducibility.json), and the
|
|
25
|
+
[workspace inspection guide](../../docs/guides/evidence-and-sharing.md).
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "ebo.release-reproducibility/v1",
|
|
3
|
+
"release": {
|
|
4
|
+
"name": "engineering-behavior-observatory",
|
|
5
|
+
"version": "0.2.5"
|
|
6
|
+
},
|
|
7
|
+
"runtime": {
|
|
8
|
+
"node": "24.19.0",
|
|
9
|
+
"claudeAgentSdk": "0.3.258",
|
|
10
|
+
"openhandsAgentServer": "1.46.0",
|
|
11
|
+
"deepseekClient": "0.1.1-rc.2",
|
|
12
|
+
"deepseekProtocol": "0.1.1-rc.2",
|
|
13
|
+
"deepseekRuntime": "0.1.1-rc.2",
|
|
14
|
+
"codexAppServer": "0.153.4",
|
|
15
|
+
"grafana": "13.2.0",
|
|
16
|
+
"grafanaInfinity": "4.0.0",
|
|
17
|
+
"piSdk": "0.85.1",
|
|
18
|
+
"cursorSdk": "1.0.31"
|
|
19
|
+
},
|
|
20
|
+
"commands": [
|
|
21
|
+
"npm ci",
|
|
22
|
+
"npm run acceptance"
|
|
23
|
+
],
|
|
24
|
+
"fixtureCoverage": {
|
|
25
|
+
"agent-sdk": "frozen queue entry through qualified capture, approved export, normalization, configurable judging, review, aggregation, and Atlas",
|
|
26
|
+
"openhands-agent-server": "pinned REST/WebSocket stream-final reconciliation, workspace/outcome evidence, partial capture, and retained evaluation",
|
|
27
|
+
"deepseek-harness": "official-client JSON-RPC composition, receipt-to-idle completion, stderr, interruption, shutdown, swaps, and retained evaluation",
|
|
28
|
+
"codex-app-server": "owned stdio lifecycle, full history, OTLP, interruption/failure, export, and retained evaluation; configurable workspace-write networking with explicit offline queue propagation",
|
|
29
|
+
"acceptance-cases": "seeded ordering, secret scanning, partial attempts, native references, abstention, disputes, denominators, and unsupported comparisons",
|
|
30
|
+
"pi-sdk": "frozen queue, passive hooks, durable session, partial cleanup, export, retained normalization and evaluation",
|
|
31
|
+
"cursor-sdk": "frozen queue, official store, callbacks/history, bounded cancellation, export, retained normalization and evaluation",
|
|
32
|
+
"workspace-capture": "Contained relative symlinks, empty link targets, AppleDouble files and long paths; streamed snapshots with verified tree digests and unchanged 1 GiB bound"
|
|
33
|
+
},
|
|
34
|
+
"determinism": {
|
|
35
|
+
"stable": [
|
|
36
|
+
"task, run, attempt, event, assertion, aggregate, and Atlas case identities",
|
|
37
|
+
"fixture, native-reference, normalized-dataset, source/cohort, archive, and package digests",
|
|
38
|
+
"seeded queue and review-sample ordering"
|
|
39
|
+
],
|
|
40
|
+
"excluded": [
|
|
41
|
+
"native and EBO observation timestamps",
|
|
42
|
+
"lifecycle start/finish timestamps",
|
|
43
|
+
"provider timing and usage",
|
|
44
|
+
"temporary workspace and output paths"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"fixtures": {
|
|
48
|
+
"tests/fixtures/task-packet.valid.v1.json": "ac2ef1043c0cc16bf4c21d05c1dc880ca1b4b8cc2f9ea7653328e91f0b0e2681",
|
|
49
|
+
"test/fixtures/agent-sdk-normalizer/complete.input.json": "7285dee088517e5de38ceda84fc7c58d9303949c7448ab3b5e10a5a2d2ebdfe3",
|
|
50
|
+
"test/fixtures/agent-sdk-normalizer/complete.expected.jsonl": "d32fa3a013bc179f1673f7e1a89b8a721bb8ddaf63eba5bc8a3f14f1cc148ed8",
|
|
51
|
+
"contracts/openhands-agent-server-v1.44.1.json": "e7a07977688a0703b15751a8a1ab17f29be45f9fdf438017e0dbcbc49cca37b0",
|
|
52
|
+
"contracts/openhands-agent-server-v1.46.0.json": "455cdddd2c206cb2f20245c2189b92e773657ec14d651236baa532b2dbadeb28",
|
|
53
|
+
"test/fixtures/openhands/v1.44.1/streamed-events.json": "d751083ba2ef94c9865117db481cc2f4933772bbbbb6b9fcf60571563715c1ea",
|
|
54
|
+
"test/fixtures/openhands/v1.44.1/final-events.json": "551db9904e860004d604c923692b5c5d3aaee966220492214c60003d2df415b6",
|
|
55
|
+
"test/fixtures/deepseek/golden-success.json": "f3867d38169bf0e28f64aba1dd60553f21cf07f41f81b4bad48261e49fa3ca88",
|
|
56
|
+
"test/fixtures/deepseek/golden-interrupted.json": "79e1f8404df1ee3b562d0bdbc0b2ee712ffc6d5774992b92970814ea815677b7",
|
|
57
|
+
"test/fixtures/deepseek/compositions/minimal/composition.json": "72fe4368e105f1bad82b7fdf8380e92c9fa689d8985f6ab91f534f0baf3eda02",
|
|
58
|
+
"contracts/codex-app-server-0.153.4/manifest.json": "e62281ff5e5d1cc71d07763b85e997ccc9ddbad9aa82a469da4181ee4b1890a8",
|
|
59
|
+
"test/fixtures/codex/legacy-0.150.1.dataset.json": "f71906b009e546afbf3b4e79396f223181382f3b3d8795e9cfcd032398a9d6bf",
|
|
60
|
+
"test/fixtures/behavior-assertions/abstained.json": "7f1962864f664c794925a67d93074e7b150af0f57c58041a81e062990d641197",
|
|
61
|
+
"test/fixtures/behavior-assertions/disputed.review.json": "450d3d9efe7feefaf7500b3ad20ab2803b412c7e53b91763125e15a83be87981",
|
|
62
|
+
"test/fixtures/comparison/exact.json": "cafa68e94b8d50dc308ddcfc2e73aa139c828b4ecd1e1c762bbc51bf1b44501e",
|
|
63
|
+
"test/fixtures/structural-observations/golden.json": "bad71a3a93524b76c0a2fdbb58e98e1e65be46cae14acf18ac89d9ba76e03e8a",
|
|
64
|
+
"test/atlas-fixture.ts": "9f03ce9cbcd0e5d3e3a5916866554ceb1c21f1c8712e58529499c1ea499f048d",
|
|
65
|
+
"test/pi.test.ts": "a00997c5617ca178e19b25b0d483cbdce8ce7c306d4c871557945b31a2e657cf",
|
|
66
|
+
"test/cursor-sdk.test.ts": "753094f127b11dc392ec2244c9edddabec27aa94347bb6159a292129ed2a2822",
|
|
67
|
+
"examples/cursor-sdk/README.md": "a2cf481123e7f3deb1c5a682ba208a775c3f91c35e1a6c1a428c517c79692618",
|
|
68
|
+
"examples/cursor-sdk/capture-profile.json": "d1a5787875daf5523b3783f259b891c256a904cbb10279d5be214af846305a2d",
|
|
69
|
+
"examples/cursor-sdk/harness.json": "2f1da6f2b17258d0ee59431f687696901ce3692589175d182f3a4bbe8dd4d689",
|
|
70
|
+
"examples/cursor-sdk/model.json": "ad8f31ff5d2387c02d79226679022358afafc61ba5dda73040e3f754530a230a",
|
|
71
|
+
"examples/cursor-sdk/native-limits.json": "40fe451f56b0863e79b42f5c85defbce678e1dc90dd248d15c430db176d71511",
|
|
72
|
+
"examples/cursor-sdk/native-tool-policy.json": "0fef98b36cd39de82914c5b068022c1335dadda24b7d6e7df398b2430e6a16e0"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# v0.2.6 known limitations
|
|
2
|
+
|
|
3
|
+
The [v0.2.5 limitations](../0.2.5/KNOWN_LIMITATIONS.md) continue to apply, except
|
|
4
|
+
that contained absolute workspace links are now supported.
|
|
5
|
+
|
|
6
|
+
- Link relocation describes a derived workspace artifact, not byte-identical
|
|
7
|
+
original link text. Source directories are not modified.
|
|
8
|
+
- Missing workspace evidence remains unqualified for work-product claims.
|
|
9
|
+
Semantic analysis may proceed only when its own evidence qualifies.
|
|
10
|
+
- A verified task whose verifier or workspace binding fails is not reported as
|
|
11
|
+
successful. Genuine harness, setup, persistence, and cleanup errors remain
|
|
12
|
+
distinct from workspace packaging gaps.
|
|
13
|
+
- Native telemetry receipt limits and provider evidence gaps are unchanged.
|
|
14
|
+
- No models are rerun to recover capture. A later recovered workspace cannot
|
|
15
|
+
prove its exact earlier state without a fingerprint recorded at that time.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# v0.2.6: Contain workspace capture failures
|
|
2
|
+
|
|
3
|
+
Workspace outcome capture accepts absolute symbolic links that resolve inside
|
|
4
|
+
the source workspace. The derived patch or snapshot uses portable relative
|
|
5
|
+
links; the source workspace is unchanged. External, dangling, and cyclic links
|
|
6
|
+
remain rejected for workspace packaging.
|
|
7
|
+
|
|
8
|
+
A workspace packaging failure no longer rewrites native completion as an
|
|
9
|
+
infrastructure failure. Capture records the missing outcome and retains the
|
|
10
|
+
source for recovery. Native semantic evidence can be normalized and analyzed
|
|
11
|
+
independently of workspace qualification. Verified task outcomes still require
|
|
12
|
+
a verifier bound to the captured workspace. Export safety checks are unchanged.
|
|
13
|
+
|
|
14
|
+
The run-manifest v1 schema now permits completed observational runs without a
|
|
15
|
+
workspace artifact. Consumers must use qualification dimensions when deciding
|
|
16
|
+
which evidence supports their analysis, rather than infer completeness from
|
|
17
|
+
terminal state.
|
|
18
|
+
|
|
19
|
+
Regression coverage includes absolute-link patch/snapshot relocation, preserved
|
|
20
|
+
source links, invalid-target rejection, cleanup containment, and normalization
|
|
21
|
+
with missing workspace evidence. A real retained xhigh trajectory generated 21
|
|
22
|
+
structural observations without its workspace artifact and without model calls.
|
|
23
|
+
|
|
24
|
+
See [known limitations](KNOWN_LIMITATIONS.md), the
|
|
25
|
+
[reproducibility manifest](reproducibility.json), and the
|
|
26
|
+
[evidence guide](../../docs/guides/evidence-and-sharing.md).
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "ebo.release-reproducibility/v1",
|
|
3
|
+
"release": {
|
|
4
|
+
"name": "engineering-behavior-observatory",
|
|
5
|
+
"version": "0.2.6"
|
|
6
|
+
},
|
|
7
|
+
"runtime": {
|
|
8
|
+
"node": "24.19.0",
|
|
9
|
+
"claudeAgentSdk": "0.3.258",
|
|
10
|
+
"openhandsAgentServer": "1.46.0",
|
|
11
|
+
"deepseekClient": "0.1.1-rc.2",
|
|
12
|
+
"deepseekProtocol": "0.1.1-rc.2",
|
|
13
|
+
"deepseekRuntime": "0.1.1-rc.2",
|
|
14
|
+
"codexAppServer": "0.153.4",
|
|
15
|
+
"grafana": "13.2.0",
|
|
16
|
+
"grafanaInfinity": "4.0.0",
|
|
17
|
+
"piSdk": "0.85.1",
|
|
18
|
+
"cursorSdk": "1.0.31"
|
|
19
|
+
},
|
|
20
|
+
"commands": [
|
|
21
|
+
"npm ci",
|
|
22
|
+
"npm run acceptance"
|
|
23
|
+
],
|
|
24
|
+
"fixtureCoverage": {
|
|
25
|
+
"agent-sdk": "frozen queue entry through qualified capture, approved export, normalization, configurable judging, review, aggregation, and Atlas",
|
|
26
|
+
"openhands-agent-server": "pinned REST/WebSocket stream-final reconciliation, workspace/outcome evidence, partial capture, and retained evaluation",
|
|
27
|
+
"deepseek-harness": "official-client JSON-RPC composition, receipt-to-idle completion, stderr, interruption, shutdown, swaps, and retained evaluation",
|
|
28
|
+
"codex-app-server": "owned stdio lifecycle, full history, OTLP, interruption/failure, export, and retained evaluation; configurable workspace-write networking with explicit offline queue propagation",
|
|
29
|
+
"acceptance-cases": "seeded ordering, secret scanning, partial attempts, native references, abstention, disputes, denominators, and unsupported comparisons",
|
|
30
|
+
"pi-sdk": "frozen queue, passive hooks, durable session, partial cleanup, export, retained normalization and evaluation",
|
|
31
|
+
"cursor-sdk": "frozen queue, official store, callbacks/history, bounded cancellation, export, retained normalization and evaluation",
|
|
32
|
+
"workspace-capture": "Contained absolute links relocated in derived captures; workspace packaging gaps preserve native completion and allow semantic analysis independently"
|
|
33
|
+
},
|
|
34
|
+
"determinism": {
|
|
35
|
+
"stable": [
|
|
36
|
+
"task, run, attempt, event, assertion, aggregate, and Atlas case identities",
|
|
37
|
+
"fixture, native-reference, normalized-dataset, source/cohort, archive, and package digests",
|
|
38
|
+
"seeded queue and review-sample ordering"
|
|
39
|
+
],
|
|
40
|
+
"excluded": [
|
|
41
|
+
"native and EBO observation timestamps",
|
|
42
|
+
"lifecycle start/finish timestamps",
|
|
43
|
+
"provider timing and usage",
|
|
44
|
+
"temporary workspace and output paths"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"fixtures": {
|
|
48
|
+
"tests/fixtures/task-packet.valid.v1.json": "ac2ef1043c0cc16bf4c21d05c1dc880ca1b4b8cc2f9ea7653328e91f0b0e2681",
|
|
49
|
+
"test/fixtures/agent-sdk-normalizer/complete.input.json": "7285dee088517e5de38ceda84fc7c58d9303949c7448ab3b5e10a5a2d2ebdfe3",
|
|
50
|
+
"test/fixtures/agent-sdk-normalizer/complete.expected.jsonl": "d32fa3a013bc179f1673f7e1a89b8a721bb8ddaf63eba5bc8a3f14f1cc148ed8",
|
|
51
|
+
"contracts/openhands-agent-server-v1.44.1.json": "e7a07977688a0703b15751a8a1ab17f29be45f9fdf438017e0dbcbc49cca37b0",
|
|
52
|
+
"contracts/openhands-agent-server-v1.46.0.json": "455cdddd2c206cb2f20245c2189b92e773657ec14d651236baa532b2dbadeb28",
|
|
53
|
+
"test/fixtures/openhands/v1.44.1/streamed-events.json": "d751083ba2ef94c9865117db481cc2f4933772bbbbb6b9fcf60571563715c1ea",
|
|
54
|
+
"test/fixtures/openhands/v1.44.1/final-events.json": "551db9904e860004d604c923692b5c5d3aaee966220492214c60003d2df415b6",
|
|
55
|
+
"test/fixtures/deepseek/golden-success.json": "f3867d38169bf0e28f64aba1dd60553f21cf07f41f81b4bad48261e49fa3ca88",
|
|
56
|
+
"test/fixtures/deepseek/golden-interrupted.json": "79e1f8404df1ee3b562d0bdbc0b2ee712ffc6d5774992b92970814ea815677b7",
|
|
57
|
+
"test/fixtures/deepseek/compositions/minimal/composition.json": "72fe4368e105f1bad82b7fdf8380e92c9fa689d8985f6ab91f534f0baf3eda02",
|
|
58
|
+
"contracts/codex-app-server-0.153.4/manifest.json": "e62281ff5e5d1cc71d07763b85e997ccc9ddbad9aa82a469da4181ee4b1890a8",
|
|
59
|
+
"test/fixtures/codex/legacy-0.150.1.dataset.json": "f71906b009e546afbf3b4e79396f223181382f3b3d8795e9cfcd032398a9d6bf",
|
|
60
|
+
"test/fixtures/behavior-assertions/abstained.json": "7f1962864f664c794925a67d93074e7b150af0f57c58041a81e062990d641197",
|
|
61
|
+
"test/fixtures/behavior-assertions/disputed.review.json": "450d3d9efe7feefaf7500b3ad20ab2803b412c7e53b91763125e15a83be87981",
|
|
62
|
+
"test/fixtures/comparison/exact.json": "cafa68e94b8d50dc308ddcfc2e73aa139c828b4ecd1e1c762bbc51bf1b44501e",
|
|
63
|
+
"test/fixtures/structural-observations/golden.json": "bad71a3a93524b76c0a2fdbb58e98e1e65be46cae14acf18ac89d9ba76e03e8a",
|
|
64
|
+
"test/atlas-fixture.ts": "9f03ce9cbcd0e5d3e3a5916866554ceb1c21f1c8712e58529499c1ea499f048d",
|
|
65
|
+
"test/pi.test.ts": "a00997c5617ca178e19b25b0d483cbdce8ce7c306d4c871557945b31a2e657cf",
|
|
66
|
+
"test/cursor-sdk.test.ts": "753094f127b11dc392ec2244c9edddabec27aa94347bb6159a292129ed2a2822",
|
|
67
|
+
"examples/cursor-sdk/README.md": "a2cf481123e7f3deb1c5a682ba208a775c3f91c35e1a6c1a428c517c79692618",
|
|
68
|
+
"examples/cursor-sdk/capture-profile.json": "d1a5787875daf5523b3783f259b891c256a904cbb10279d5be214af846305a2d",
|
|
69
|
+
"examples/cursor-sdk/harness.json": "2f1da6f2b17258d0ee59431f687696901ce3692589175d182f3a4bbe8dd4d689",
|
|
70
|
+
"examples/cursor-sdk/model.json": "ad8f31ff5d2387c02d79226679022358afafc61ba5dda73040e3f754530a230a",
|
|
71
|
+
"examples/cursor-sdk/native-limits.json": "40fe451f56b0863e79b42f5c85defbce678e1dc90dd248d15c430db176d71511",
|
|
72
|
+
"examples/cursor-sdk/native-tool-policy.json": "0fef98b36cd39de82914c5b068022c1335dadda24b7d6e7df398b2430e6a16e0"
|
|
73
|
+
}
|
|
74
|
+
}
|
package/release/README.md
CHANGED
|
@@ -5,6 +5,8 @@ For downloads and published artifacts, see
|
|
|
5
5
|
|
|
6
6
|
| Version | Changes and verification | Support boundary |
|
|
7
7
|
| :--- | :--- | :--- |
|
|
8
|
+
| [0.2.6](0.2.6/README.md) | Portable contained absolute links; isolate workspace packaging failures from native completion and semantic analysis | [Known limitations](0.2.6/KNOWN_LIMITATIONS.md) |
|
|
9
|
+
| [0.2.5](0.2.5/README.md) | Preserve contained workspace symlinks and macOS AppleDouble files | [Known limitations](0.2.5/KNOWN_LIMITATIONS.md) |
|
|
8
10
|
| [0.2.4](0.2.4/README.md) | Stream workspace snapshots and integrity checks; support archives up to 1 GiB | [Known limitations](0.2.4/KNOWN_LIMITATIONS.md) |
|
|
9
11
|
| [0.2.3](0.2.3/README.md) | Retain workspace capture errors in partial-bundle reports | [Known limitations](0.2.3/KNOWN_LIMITATIONS.md) |
|
|
10
12
|
| [0.2.2](0.2.2/README.md) | Configurable Codex tool networking, enabled by default for workspace-write | [Known limitations](0.2.2/KNOWN_LIMITATIONS.md) |
|