engineering-behavior-observatory 0.2.4 → 0.2.5
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/run-bundles.js +44 -13
- package/dist/src/verifiers.js +27 -7
- package/docs/guides/evidence-and-sharing.md +28 -0
- package/package.json +2 -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/README.md +1 -0
package/dist/src/run-bundles.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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, realpath, rm, rmdir, utimes, writeFile } from "node:fs/promises";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { join, resolve, sep } from "node:path";
|
|
7
7
|
import { promisify } from "node:util";
|
|
@@ -268,6 +268,7 @@ async function withWorkspaceOutcomeProjection(options, use) {
|
|
|
268
268
|
try {
|
|
269
269
|
await cp(finalPath, projectedPath, {
|
|
270
270
|
recursive: true,
|
|
271
|
+
verbatimSymlinks: true,
|
|
271
272
|
preserveTimestamps: true,
|
|
272
273
|
force: false,
|
|
273
274
|
filter: async (source) => source === finalPath
|
|
@@ -648,7 +649,7 @@ async function workspacePatchApplies(startingWorkspacePath, patch) {
|
|
|
648
649
|
const applied = join(temporaryRoot, "workspace");
|
|
649
650
|
const patchPath = join(temporaryRoot, "workspace.patch");
|
|
650
651
|
try {
|
|
651
|
-
await cp(resolve(startingWorkspacePath), applied, { recursive: true, preserveTimestamps: true, force: false });
|
|
652
|
+
await cp(resolve(startingWorkspacePath), applied, { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
652
653
|
if (patch.length > 0) {
|
|
653
654
|
await writeFile(patchPath, patch, { flag: "wx", mode: 0o600 });
|
|
654
655
|
await execFileAsync("git", ["apply", "--check", "--binary", patchPath], { cwd: applied });
|
|
@@ -842,7 +843,7 @@ async function removeIgnoredWorkspaceEntries(startPath, finalPath) {
|
|
|
842
843
|
const globalExcludes = join(temporaryRoot, "global-excludes");
|
|
843
844
|
try {
|
|
844
845
|
await writeFile(globalExcludes, "");
|
|
845
|
-
await cp(startPath, baseline, { recursive: true, preserveTimestamps: true, force: false });
|
|
846
|
+
await cp(startPath, baseline, { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
846
847
|
await execFileAsync("git", ["init", "--quiet"], { cwd: baseline });
|
|
847
848
|
await execFileAsync("git", ["add", "--force", "--all"], { cwd: baseline });
|
|
848
849
|
for (const relativePath of await ignoredWorkspacePaths(baseline, finalPath, globalExcludes)) {
|
|
@@ -910,12 +911,31 @@ async function isExcludedWorkspaceDirectory(root, source, exclusions) {
|
|
|
910
911
|
}
|
|
911
912
|
return false;
|
|
912
913
|
}
|
|
913
|
-
async function removeEmptyDirectories(directory, root = directory) {
|
|
914
|
+
async function removeEmptyDirectories(directory, root = directory, targets) {
|
|
915
|
+
if (targets === undefined) {
|
|
916
|
+
targets = new Set();
|
|
917
|
+
async function collect(path) {
|
|
918
|
+
for (const entry of await readdir(path, { withFileTypes: true })) {
|
|
919
|
+
const child = join(path, entry.name);
|
|
920
|
+
if (entry.isDirectory())
|
|
921
|
+
await collect(child);
|
|
922
|
+
else if (entry.isSymbolicLink()) {
|
|
923
|
+
// Omission must not turn a valid link into a dangling one. Invalid
|
|
924
|
+
// links are rejected later by workspace hashing; no content is read.
|
|
925
|
+
try {
|
|
926
|
+
targets.add(await realpath(child));
|
|
927
|
+
}
|
|
928
|
+
catch { /* validated by hashing */ }
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
await collect(directory);
|
|
933
|
+
}
|
|
914
934
|
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
915
935
|
if (entry.isDirectory())
|
|
916
|
-
await removeEmptyDirectories(join(directory, entry.name), root);
|
|
936
|
+
await removeEmptyDirectories(join(directory, entry.name), root, targets);
|
|
917
937
|
}
|
|
918
|
-
if (directory !== root && (await readdir(directory)).length === 0)
|
|
938
|
+
if (directory !== root && !targets.has(await realpath(directory)) && (await readdir(directory)).length === 0)
|
|
919
939
|
await rmdir(directory);
|
|
920
940
|
}
|
|
921
941
|
async function restoreProjectedDirectoryTimestamps(source, projected) {
|
|
@@ -938,7 +958,7 @@ async function workspacePatch(startPath, finalPath, finalTreeDigest) {
|
|
|
938
958
|
const applied = join(temporaryRoot, "applied");
|
|
939
959
|
const patchPath = join(temporaryRoot, "workspace.patch");
|
|
940
960
|
try {
|
|
941
|
-
await cp(startPath, worktree, { recursive: true, preserveTimestamps: true, force: false });
|
|
961
|
+
await cp(startPath, worktree, { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
942
962
|
await execFileAsync("git", ["init", "--quiet"], { cwd: worktree });
|
|
943
963
|
await execFileAsync("git", ["add", "--force", "--all"], { cwd: worktree });
|
|
944
964
|
// A tree is enough for the diff. A commit can spawn detached maintenance
|
|
@@ -951,7 +971,7 @@ async function workspacePatch(startPath, finalPath, finalTreeDigest) {
|
|
|
951
971
|
for (const entry of await readdir(finalPath)) {
|
|
952
972
|
if (entry === ".git")
|
|
953
973
|
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 });
|
|
974
|
+
await cp(join(finalPath, entry), join(worktree, entry), { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
955
975
|
}
|
|
956
976
|
await execFileAsync("git", ["add", "--force", "--intent-to-add", "--all"], { cwd: worktree });
|
|
957
977
|
let stdout;
|
|
@@ -966,12 +986,14 @@ async function workspacePatch(startPath, finalPath, finalTreeDigest) {
|
|
|
966
986
|
throw error;
|
|
967
987
|
}
|
|
968
988
|
const patch = Buffer.from(stdout);
|
|
969
|
-
await cp(startPath, applied, { recursive: true, preserveTimestamps: true, force: false });
|
|
989
|
+
await cp(startPath, applied, { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
970
990
|
if (patch.length > 0) {
|
|
971
991
|
await writeFile(patchPath, patch, { flag: "wx", mode: 0o600 });
|
|
972
992
|
await execFileAsync("git", ["apply", "--binary", patchPath], { cwd: applied });
|
|
973
993
|
}
|
|
974
|
-
|
|
994
|
+
// Git omits empty directories, including legitimate link targets. An
|
|
995
|
+
// invalid reconstructed tree is a patch limitation, so use the snapshot.
|
|
996
|
+
const appliedTreeDigest = await digestWorkspaceTree(applied).catch(() => undefined);
|
|
975
997
|
if (appliedTreeDigest !== finalTreeDigest) {
|
|
976
998
|
return undefined;
|
|
977
999
|
}
|
|
@@ -989,8 +1011,8 @@ async function workspaceSnapshot(finalPath, finalTreeDigest, use) {
|
|
|
989
1011
|
const extracted = join(temporaryRoot, "extracted");
|
|
990
1012
|
try {
|
|
991
1013
|
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"] });
|
|
1014
|
+
await cp(finalPath, snapshotRoot, { recursive: true, verbatimSymlinks: true, preserveTimestamps: true, force: false });
|
|
1015
|
+
const child = spawn(TAR_COMMAND, [...(process.platform === "darwin" ? ["--no-mac-metadata"] : []), "-czf", "-", "-C", snapshotParent, "workspace"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
994
1016
|
let stderr = "";
|
|
995
1017
|
child.stderr.on("data", (chunk) => { stderr = (stderr + chunk.toString("utf8")).slice(-8192); });
|
|
996
1018
|
const exited = new Promise((resolveExit, reject) => {
|
|
@@ -1012,7 +1034,16 @@ async function workspaceSnapshot(finalPath, finalTreeDigest, use) {
|
|
|
1012
1034
|
await Promise.allSettled([exited, streamed]);
|
|
1013
1035
|
}
|
|
1014
1036
|
await mkdir(extracted, { mode: 0o700 });
|
|
1015
|
-
|
|
1037
|
+
// macOS libarchive consumes genuine ._* AppleDouble files even when its
|
|
1038
|
+
// metadata flags are disabled. Native pax preserves them with copyfile off.
|
|
1039
|
+
if (process.platform === "darwin") {
|
|
1040
|
+
await execFileAsync("/bin/pax", ["-rz", "-p", "p", "-f", snapshotPath], {
|
|
1041
|
+
cwd: extracted, env: { ...process.env, COPYFILE_DISABLE: "1" },
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
else {
|
|
1045
|
+
await execFileAsync(TAR_COMMAND, ["-xzpf", snapshotPath, "-C", extracted]);
|
|
1046
|
+
}
|
|
1016
1047
|
if (await digestWorkspaceTree(join(extracted, "workspace")) !== finalTreeDigest) {
|
|
1017
1048
|
throw new Error("Bounded workspace snapshot cannot reproduce the final workspace tree.");
|
|
1018
1049
|
}
|
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)
|
|
@@ -46,6 +46,34 @@ missing-evidence entries. Inspect the detail before retrying capture against a
|
|
|
46
46
|
retained workspace. A later successful capture is recovery evidence; it does
|
|
47
47
|
not change the original attempt's recorded failure.
|
|
48
48
|
|
|
49
|
+
When the native run completed but capture failed, recover capture without
|
|
50
|
+
executing the task again. Materialize the same frozen starting workspace, use
|
|
51
|
+
the same capture exclusions, and assemble a separate bundle with byte-identical
|
|
52
|
+
native evidence plus the recovered workspace outcome. Record the original
|
|
53
|
+
manifest digest, recovery time, capture-code identity and qualification beside
|
|
54
|
+
the recovered bundle. Preserve the original failed bundle. A later workspace
|
|
55
|
+
capture cannot prove the retained directory was unchanged since execution ended
|
|
56
|
+
unless a fingerprint was recorded then. Do not count the recovery as another
|
|
57
|
+
model trial or as an independent sample.
|
|
58
|
+
|
|
59
|
+
Workspace outcomes preserve relative symbolic links whose lexical targets and
|
|
60
|
+
resolved referents stay inside the workspace. Link text is hashed and copied
|
|
61
|
+
verbatim, not replaced by the referent's bytes. Empty-directory omission keeps
|
|
62
|
+
directories referenced by links. Absolute, escaping, dangling and
|
|
63
|
+
cyclic links fail capture. Patch application or snapshot extraction must still
|
|
64
|
+
reproduce the resulting tree digest. This does not relax task-archive admission
|
|
65
|
+
or verifier sandbox rules.
|
|
66
|
+
|
|
67
|
+
On macOS, snapshot creation disables generated AppleDouble metadata. Snapshot
|
|
68
|
+
verification uses the built-in `/bin/pax` with `COPYFILE_DISABLE=1` so genuine
|
|
69
|
+
`._*` files remain files. macOS tar may otherwise consume them as metadata,
|
|
70
|
+
changing the reconstructed tree. To extract a snapshot for inspection on macOS:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
mkdir restored
|
|
74
|
+
(cd restored && COPYFILE_DISABLE=1 /bin/pax -rz -p p -f /absolute/path/workspace.tar.gz)
|
|
75
|
+
```
|
|
76
|
+
|
|
49
77
|
Workspace capture tries a patch up to 64 MiB, then falls back to a snapshot
|
|
50
78
|
when the patch is too large or cannot reproduce the tree. Snapshots stream to
|
|
51
79
|
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.5",
|
|
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,7 @@
|
|
|
48
48
|
"release/0.2.2/",
|
|
49
49
|
"release/0.2.3/",
|
|
50
50
|
"release/0.2.4/",
|
|
51
|
+
"release/0.2.5/",
|
|
51
52
|
"schemas/",
|
|
52
53
|
"scripts/atlas-grafana.sh"
|
|
53
54
|
],
|
|
@@ -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
|
+
}
|
package/release/README.md
CHANGED
|
@@ -5,6 +5,7 @@ For downloads and published artifacts, see
|
|
|
5
5
|
|
|
6
6
|
| Version | Changes and verification | Support boundary |
|
|
7
7
|
| :--- | :--- | :--- |
|
|
8
|
+
| [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
9
|
| [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
10
|
| [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
11
|
| [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) |
|