rea-agents 1.1.0 → 1.2.0
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/README.md +21 -2
- package/dist/application/ArtifactInventory.js +80 -44
- package/dist/application/CommandShimReplay.js +136 -0
- package/dist/application/EvidenceBundleFiles.js +9 -1
- package/dist/application/ProcessCaptureAuthority.js +25 -0
- package/dist/application/ProcessCaptureCapability.js +23 -0
- package/dist/application/ProcessCaptureError.js +5 -0
- package/dist/application/ProcessCaptureLifecycle.js +269 -0
- package/dist/application/ProcessCheckpoints.js +129 -0
- package/dist/application/ProcessCli.js +61 -0
- package/dist/application/ProcessEvidence.js +38 -0
- package/dist/application/ProcessHarness.js +252 -272
- package/dist/application/ProcessNormalization.js +10 -1
- package/dist/application/ProcessOwnership.js +39 -1
- package/dist/application/ProcessSampling.js +39 -23
- package/dist/application/Setup.js +51 -76
- package/dist/application/SetupSkill.js +44 -0
- package/dist/application/TerminalRenderer.js +92 -0
- package/dist/application/runtime.js +1 -1
- package/dist/artifacts/ArtifactProvider.js +18 -6
- package/dist/artifacts/ArtifactReader.js +3 -1
- package/dist/artifacts/AsarArtifactReader.js +8 -1
- package/dist/artifacts/DirectoryArtifactReader.js +1 -0
- package/dist/artifacts/MachOSliceArtifactReader.js +1 -0
- package/dist/artifacts/NativeDmgArtifactReader.js +151 -0
- package/dist/artifacts/ZipArtifactReader.js +1 -0
- package/dist/cli.js +29 -24
- package/dist/cliProcessCommands.js +19 -0
- package/dist/config.js +2 -0
- package/dist/contracts/artifactToolContracts.js +1 -0
- package/dist/contracts/investigationExamples.js +3 -3
- package/dist/contracts/processCaptureExample.js +50 -7
- package/dist/contracts/toolContracts.js +3 -2
- package/dist/domain/changedBehavior.js +2 -2
- package/dist/domain/errors.js +11 -3
- package/dist/domain/processCapture.js +99 -293
- package/dist/domain/processCaptureValidation.js +124 -0
- package/dist/domain/processComparison.js +176 -32
- package/dist/domain/processScenario.js +370 -0
- package/dist/domain/reconstructionVerification.js +3 -3
- package/dist/domain/staticRuntimeCorrelation.js +3 -2
- package/dist/identity.js +1 -0
- package/dist/server/registerProcessComparisonTool.js +28 -8
- package/dist/server/registerSessionTools.js +3 -25
- package/dist/server/sessionToolPolicies.js +1 -6
- package/package.json +5 -1
- package/scripts/prepare-node-pty.mjs +35 -0
- package/skills/rea-analysis/SKILL.md +19 -4
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { parse } from "plist";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { ArtifactReaderFailure, } from "./ArtifactReader.js";
|
|
9
|
+
import { DirectoryArtifactReader } from "./DirectoryArtifactReader.js";
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
|
+
const attachOutputSchema = z.object({
|
|
12
|
+
"system-entities": z.array(z.object({
|
|
13
|
+
"dev-entry": z.string().startsWith("/dev/"),
|
|
14
|
+
"mount-point": z.string().optional(),
|
|
15
|
+
})),
|
|
16
|
+
});
|
|
17
|
+
const systemHost = {
|
|
18
|
+
async run(arguments_, signal) {
|
|
19
|
+
try {
|
|
20
|
+
const { stdout } = await execFileAsync("/usr/bin/hdiutil", [...arguments_], {
|
|
21
|
+
encoding: "utf8",
|
|
22
|
+
timeout: 120_000,
|
|
23
|
+
...(signal === undefined ? {} : { signal }),
|
|
24
|
+
});
|
|
25
|
+
return { stdout, exitCode: 0 };
|
|
26
|
+
}
|
|
27
|
+
catch (cause) {
|
|
28
|
+
if (cause instanceof Error && cause.name === "AbortError")
|
|
29
|
+
throw new ArtifactReaderFailure("cancelled", "DMG operation cancelled");
|
|
30
|
+
throw new ArtifactReaderFailure("format", "hdiutil rejected the DMG", {
|
|
31
|
+
cause,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
/** Read-only macOS DMG adapter that owns attachment and reverse-order detach. */
|
|
37
|
+
export class NativeDmgArtifactReader {
|
|
38
|
+
path;
|
|
39
|
+
host;
|
|
40
|
+
format = "file";
|
|
41
|
+
#provenance = [];
|
|
42
|
+
#directory;
|
|
43
|
+
#devices = [];
|
|
44
|
+
#mountRoot;
|
|
45
|
+
constructor(path, host) {
|
|
46
|
+
this.path = path;
|
|
47
|
+
this.host = host;
|
|
48
|
+
}
|
|
49
|
+
/** Verify and attach one image beneath an exclusively owned temporary root. */
|
|
50
|
+
static async create(path, signal, host = systemHost) {
|
|
51
|
+
if (process.platform !== "darwin")
|
|
52
|
+
throw new ArtifactReaderFailure("unavailable", "Native DMG traversal is available only on macOS");
|
|
53
|
+
const reader = new NativeDmgArtifactReader(path, host);
|
|
54
|
+
await reader.attach(signal);
|
|
55
|
+
return reader;
|
|
56
|
+
}
|
|
57
|
+
async *entries(signal) {
|
|
58
|
+
if (this.#directory === undefined)
|
|
59
|
+
throw new ArtifactReaderFailure("unavailable", "DMG is not attached");
|
|
60
|
+
const prefix = basename(this.path);
|
|
61
|
+
for await (const entry of this.#directory.entries(signal))
|
|
62
|
+
yield { ...entry, path: `${prefix}/${entry.path}` };
|
|
63
|
+
}
|
|
64
|
+
open(entry, signal) {
|
|
65
|
+
if (this.#directory === undefined)
|
|
66
|
+
return Promise.reject(new ArtifactReaderFailure("unavailable", "DMG is not attached"));
|
|
67
|
+
return this.#directory.open(entry, signal);
|
|
68
|
+
}
|
|
69
|
+
provenance() {
|
|
70
|
+
return this.#provenance;
|
|
71
|
+
}
|
|
72
|
+
async close() {
|
|
73
|
+
let detachFailure;
|
|
74
|
+
for (const device of [...this.#devices].reverse()) {
|
|
75
|
+
try {
|
|
76
|
+
await runChecked(this.host, ["detach", device]);
|
|
77
|
+
this.#provenance.push(command(["detach", device], ["mount"]));
|
|
78
|
+
}
|
|
79
|
+
catch (cause) {
|
|
80
|
+
detachFailure ??= cause;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
this.#devices = [];
|
|
84
|
+
if (this.#mountRoot !== undefined)
|
|
85
|
+
await rm(this.#mountRoot, { recursive: true, force: true }).catch((cause) => {
|
|
86
|
+
detachFailure ??= cause;
|
|
87
|
+
});
|
|
88
|
+
if (detachFailure !== undefined)
|
|
89
|
+
throw new ArtifactReaderFailure("unavailable", "DMG detach or mount-root cleanup failed", { cause: detachFailure });
|
|
90
|
+
}
|
|
91
|
+
async attach(signal) {
|
|
92
|
+
await runChecked(this.host, ["verify", this.path], signal);
|
|
93
|
+
this.#provenance.push(command(["verify", this.path], ["read"]));
|
|
94
|
+
this.#mountRoot = await mkdtemp(join(tmpdir(), "rea-dmg-"));
|
|
95
|
+
try {
|
|
96
|
+
const attached = await runChecked(this.host, [
|
|
97
|
+
"attach",
|
|
98
|
+
"-readonly",
|
|
99
|
+
"-nobrowse",
|
|
100
|
+
"-plist",
|
|
101
|
+
"-mountroot",
|
|
102
|
+
this.#mountRoot,
|
|
103
|
+
this.path,
|
|
104
|
+
], signal);
|
|
105
|
+
const parsed = attachOutputSchema.parse(parse(attached.stdout));
|
|
106
|
+
this.#devices = parsed["system-entities"].map((entity) => entity["dev-entry"]);
|
|
107
|
+
if (this.#devices.length === 0)
|
|
108
|
+
throw new ArtifactReaderFailure("format", "hdiutil returned no attached devices");
|
|
109
|
+
for (const entity of parsed["system-entities"])
|
|
110
|
+
if (entity["mount-point"] !== undefined &&
|
|
111
|
+
!entity["mount-point"].startsWith(`${this.#mountRoot}/`))
|
|
112
|
+
throw new ArtifactReaderFailure("path", "hdiutil mounted outside the owned root");
|
|
113
|
+
this.#provenance.push(command([
|
|
114
|
+
"attach",
|
|
115
|
+
"-readonly",
|
|
116
|
+
"-nobrowse",
|
|
117
|
+
"-plist",
|
|
118
|
+
"-mountroot",
|
|
119
|
+
this.#mountRoot,
|
|
120
|
+
this.path,
|
|
121
|
+
], ["read", "mount"]));
|
|
122
|
+
this.#directory = new DirectoryArtifactReader(this.#mountRoot);
|
|
123
|
+
}
|
|
124
|
+
catch (cause) {
|
|
125
|
+
let cleanupFailure;
|
|
126
|
+
try {
|
|
127
|
+
await this.close();
|
|
128
|
+
}
|
|
129
|
+
catch (cleanupCause) {
|
|
130
|
+
cleanupFailure = cleanupCause;
|
|
131
|
+
}
|
|
132
|
+
if (cleanupFailure !== undefined)
|
|
133
|
+
throw new ArtifactReaderFailure("unavailable", "DMG attach failed and cleanup could not detach every device", { cause: new AggregateError([cause, cleanupFailure]) });
|
|
134
|
+
throw cause;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const runChecked = async (host, arguments_, signal) => {
|
|
139
|
+
const result = await host.run(arguments_, signal);
|
|
140
|
+
if (result.exitCode !== 0)
|
|
141
|
+
throw new ArtifactReaderFailure("unavailable", `hdiutil ${arguments_[0] ?? "operation"} failed with a non-zero exit code`);
|
|
142
|
+
return { stdout: result.stdout, exitCode: 0 };
|
|
143
|
+
};
|
|
144
|
+
const command = (arguments_, effects) => ({
|
|
145
|
+
tool: "/usr/bin/hdiutil",
|
|
146
|
+
arguments: [...arguments_],
|
|
147
|
+
tool_version: null,
|
|
148
|
+
executable_sha256: null,
|
|
149
|
+
exit_code: 0,
|
|
150
|
+
effects,
|
|
151
|
+
});
|
package/dist/cli.js
CHANGED
|
@@ -12,6 +12,7 @@ import { logCliCommand } from "./cliLogging.js";
|
|
|
12
12
|
import { parseConfig } from "./config.js";
|
|
13
13
|
import { importReferenceSource } from "./application/ReferenceSourceImport.js";
|
|
14
14
|
import { registerEvidenceCommands } from "./cliEvidenceCommands.js";
|
|
15
|
+
import { registerProcessCommands } from "./cliProcessCommands.js";
|
|
15
16
|
/**
|
|
16
17
|
* Build the one-shot Incur CLI without starting Hopper at import time.
|
|
17
18
|
* Analysis commands acquire and close their own sessions; bare `mcp` and
|
|
@@ -38,6 +39,7 @@ export const createCli = () => {
|
|
|
38
39
|
],
|
|
39
40
|
},
|
|
40
41
|
});
|
|
42
|
+
registerSetupCommands(cli, logger);
|
|
41
43
|
registerCoreCommands(cli, logger);
|
|
42
44
|
registerFunctionCommand(cli, logger);
|
|
43
45
|
registerSearchCommand(cli, logger);
|
|
@@ -48,6 +50,7 @@ export const createCli = () => {
|
|
|
48
50
|
registerArtifactCommands(cli, logger);
|
|
49
51
|
registerEvidenceCommands(cli, logger);
|
|
50
52
|
registerReferenceSourceCommand(cli, logger);
|
|
53
|
+
registerProcessCommands(cli, logger);
|
|
51
54
|
return cli;
|
|
52
55
|
};
|
|
53
56
|
const registerCoreCommands = (cli, logger) => {
|
|
@@ -55,6 +58,32 @@ const registerCoreCommands = (cli, logger) => {
|
|
|
55
58
|
detail: z.enum(["concise", "detailed"]).default("concise"),
|
|
56
59
|
limit: z.number().int().min(1).max(50).default(10),
|
|
57
60
|
});
|
|
61
|
+
cli.command("analyze", {
|
|
62
|
+
description: "Get an overview of an app",
|
|
63
|
+
args: z.object({
|
|
64
|
+
path: z.string().describe("App, program, or Hopper database path"),
|
|
65
|
+
}),
|
|
66
|
+
options: overviewOptions,
|
|
67
|
+
run: ({ args, options }) => logCliCommand(logger, "analyze", () => runDirectAnalysis(args.path, "binary_overview", { detail: options.detail, limit: options.limit }, logger)),
|
|
68
|
+
});
|
|
69
|
+
cli.command("inspect", {
|
|
70
|
+
description: "Inspect an app overview with evidence",
|
|
71
|
+
args: z.object({
|
|
72
|
+
path: z.string().describe("App, program, or Hopper database path"),
|
|
73
|
+
}),
|
|
74
|
+
options: overviewOptions,
|
|
75
|
+
run: ({ args, options }) => logCliCommand(logger, "inspect", () => runDirectAnalysis(args.path, "binary_overview", { detail: options.detail, limit: options.limit }, logger)),
|
|
76
|
+
});
|
|
77
|
+
cli.command("decompile", {
|
|
78
|
+
description: "Read one part of an app as code",
|
|
79
|
+
args: z.object({
|
|
80
|
+
path: z.string().describe("App or program path"),
|
|
81
|
+
address: z.string().describe("Procedure address"),
|
|
82
|
+
}),
|
|
83
|
+
run: ({ args }) => logCliCommand(logger, "decompile", () => runDirectAnalysis(args.path, "procedure_pseudo_code", { procedure: args.address }, logger)),
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
const registerSetupCommands = (cli, logger) => {
|
|
58
87
|
cli.command("setup", {
|
|
59
88
|
description: "Install requirements and configure coding agents",
|
|
60
89
|
options: z.object({
|
|
@@ -98,30 +127,6 @@ const registerCoreCommands = (cli, logger) => {
|
|
|
98
127
|
description: "Upgrade a global npm installation to the latest REA release",
|
|
99
128
|
run: ({ formatExplicit }) => logCliCommand(logger, "upgrade", () => runUpgrade(process.env.REA_PACKAGE_VERSION ?? "0.0.0-development", systemUpgradeHost(), formatExplicit ? "structured" : "human")),
|
|
100
129
|
});
|
|
101
|
-
cli.command("analyze", {
|
|
102
|
-
description: "Get an overview of an app",
|
|
103
|
-
args: z.object({
|
|
104
|
-
path: z.string().describe("App, program, or Hopper database path"),
|
|
105
|
-
}),
|
|
106
|
-
options: overviewOptions,
|
|
107
|
-
run: ({ args, options }) => logCliCommand(logger, "analyze", () => runDirectAnalysis(args.path, "binary_overview", { detail: options.detail, limit: options.limit }, logger)),
|
|
108
|
-
});
|
|
109
|
-
cli.command("inspect", {
|
|
110
|
-
description: "Inspect an app overview with evidence",
|
|
111
|
-
args: z.object({
|
|
112
|
-
path: z.string().describe("App, program, or Hopper database path"),
|
|
113
|
-
}),
|
|
114
|
-
options: overviewOptions,
|
|
115
|
-
run: ({ args, options }) => logCliCommand(logger, "inspect", () => runDirectAnalysis(args.path, "binary_overview", { detail: options.detail, limit: options.limit }, logger)),
|
|
116
|
-
});
|
|
117
|
-
cli.command("decompile", {
|
|
118
|
-
description: "Read one part of an app as code",
|
|
119
|
-
args: z.object({
|
|
120
|
-
path: z.string().describe("App or program path"),
|
|
121
|
-
address: z.string().describe("Procedure address"),
|
|
122
|
-
}),
|
|
123
|
-
run: ({ args }) => logCliCommand(logger, "decompile", () => runDirectAnalysis(args.path, "procedure_pseudo_code", { procedure: args.address }, logger)),
|
|
124
|
-
});
|
|
125
130
|
};
|
|
126
131
|
const confirmSetup = async (actions) => {
|
|
127
132
|
process.stdout.write("\nREA setup plan\n");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Cli, z } from "incur";
|
|
2
|
+
import { captureProcessScenarioFile, compareProcessEvidenceFiles, } from "./application/ProcessCli.js";
|
|
3
|
+
import { logCliCommand } from "./cliLogging.js";
|
|
4
|
+
/** Register Process Capture v4 one-shot commands through shared application services. */
|
|
5
|
+
export const registerProcessCommands = (cli, logger) => {
|
|
6
|
+
cli.command("capture-process", {
|
|
7
|
+
description: "Capture one approved Process Capture v4 JSON scenario",
|
|
8
|
+
args: z.object({ scenario: z.string().describe("Scenario JSON path") }),
|
|
9
|
+
run: ({ args }) => logCliCommand(logger, "capture-process", () => captureProcessScenarioFile(args.scenario)),
|
|
10
|
+
});
|
|
11
|
+
cli.command("compare-process-captures", {
|
|
12
|
+
description: "Compare two Process Capture v4 Evidence JSON files",
|
|
13
|
+
args: z.object({
|
|
14
|
+
left: z.string().describe("Left capture Evidence JSON path"),
|
|
15
|
+
right: z.string().describe("Right capture Evidence JSON path"),
|
|
16
|
+
}),
|
|
17
|
+
run: ({ args }) => logCliCommand(logger, "compare-process-captures", () => compareProcessEvidenceFiles(args.left, args.right)),
|
|
18
|
+
});
|
|
19
|
+
};
|
package/dist/config.js
CHANGED
|
@@ -14,6 +14,7 @@ const environmentSchema = z.object({
|
|
|
14
14
|
.enum(["trace", "debug", "info", "warn", "error", "fatal", "silent"])
|
|
15
15
|
.default("info"),
|
|
16
16
|
REA_PROCESS_CAPTURE_ENABLED: z.enum(["true", "false"]).default("false"),
|
|
17
|
+
REA_ARTIFACT_NATIVE_MOUNT_ENABLED: z.enum(["true", "false"]).default("false"),
|
|
17
18
|
REA_PROCESS_ALLOW_EXTERNAL_NETWORK: z
|
|
18
19
|
.enum(["true", "false"])
|
|
19
20
|
.default("false"),
|
|
@@ -101,6 +102,7 @@ export const parseConfig = (environment) => {
|
|
|
101
102
|
allowedEnvironment: allowedEnvironment.value,
|
|
102
103
|
allowExternalNetwork: parsedEnvironment.data.REA_PROCESS_ALLOW_EXTERNAL_NETWORK === "true",
|
|
103
104
|
},
|
|
105
|
+
artifactNativeMountEnabled: parsedEnvironment.data.REA_ARTIFACT_NATIVE_MOUNT_ENABLED === "true",
|
|
104
106
|
evidenceFilePolicy: {
|
|
105
107
|
roots: evidenceRoots.value,
|
|
106
108
|
maxBytes: 64 * 1024 * 1024,
|
|
@@ -29,6 +29,7 @@ const traversalLimitsInput = {
|
|
|
29
29
|
};
|
|
30
30
|
/** Exact caller boundary for deterministic artifact inventory. */
|
|
31
31
|
export const artifactInventoryInputSchema = z.object({
|
|
32
|
+
native_mount_approved: z.boolean().default(false),
|
|
32
33
|
...pageInput,
|
|
33
34
|
...traversalLimitsInput,
|
|
34
35
|
});
|
|
@@ -30,11 +30,11 @@ export const FUNCTION_COMPARISON_EVIDENCE = createEvidence(undefined, {
|
|
|
30
30
|
const PROCESS_PROVIDER = {
|
|
31
31
|
id: "rea-process",
|
|
32
32
|
name: "REA deterministic process harness",
|
|
33
|
-
version: "
|
|
33
|
+
version: "3",
|
|
34
34
|
};
|
|
35
35
|
const capture = processCaptureSchema.parse(EMPTY_PROCESS_CAPTURE_EXAMPLE);
|
|
36
36
|
const captureEvidence = (scenario) => createEvidence(undefined, PROCESS_PROVIDER, {
|
|
37
|
-
predicateType: "rea.process-capture/
|
|
37
|
+
predicateType: "rea.process-capture/v4",
|
|
38
38
|
operation: "capture_process_scenario",
|
|
39
39
|
parameters: { scenario },
|
|
40
40
|
result: jsonValueSchema.parse(capture),
|
|
@@ -50,7 +50,7 @@ const captureEvidence = (scenario) => createEvidence(undefined, PROCESS_PROVIDER
|
|
|
50
50
|
export const PROCESS_CAPTURE_REFERENCE = captureEvidence("reference");
|
|
51
51
|
export const PROCESS_CAPTURE_RECONSTRUCTION = captureEvidence("reconstruction");
|
|
52
52
|
export const PROCESS_COMPARISON_EVIDENCE = createEvidence(undefined, PROCESS_PROVIDER, {
|
|
53
|
-
predicateType: "rea.process-comparison/
|
|
53
|
+
predicateType: "rea.process-comparison/v3",
|
|
54
54
|
operation: "compare_process_captures",
|
|
55
55
|
parameters: {
|
|
56
56
|
left_evidence_id: PROCESS_CAPTURE_REFERENCE.evidence_id,
|
|
@@ -1,16 +1,59 @@
|
|
|
1
|
+
import { digestProcessCommitment } from "../domain/processCapture.js";
|
|
2
|
+
const normalization = {
|
|
3
|
+
paths: true,
|
|
4
|
+
pids: true,
|
|
5
|
+
ports: true,
|
|
6
|
+
time_bucket_ms: 10,
|
|
7
|
+
patterns: [],
|
|
8
|
+
};
|
|
9
|
+
const scenario = { executable_sha256: "0".repeat(64) };
|
|
10
|
+
const comparisonContract = {};
|
|
11
|
+
const shimPlan = [];
|
|
12
|
+
const replayPlan = {};
|
|
1
13
|
/** Minimal valid process capture used only in public contract examples. */
|
|
2
14
|
export const EMPTY_PROCESS_CAPTURE_EXAMPLE = {
|
|
3
|
-
schema_version:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
15
|
+
schema_version: 4,
|
|
16
|
+
manifest: {
|
|
17
|
+
rea_version: "1.1.0",
|
|
18
|
+
provider_version: "3",
|
|
19
|
+
platform: "fixture",
|
|
20
|
+
architecture: "fixture",
|
|
21
|
+
pty_backend: "node-pty",
|
|
22
|
+
started_at: "2026-01-01T00:00:00.000Z",
|
|
23
|
+
completed_at: "2026-01-01T00:00:00.001Z",
|
|
24
|
+
scenario,
|
|
25
|
+
comparison_contract: comparisonContract,
|
|
26
|
+
shim_plan: shimPlan,
|
|
27
|
+
replay_plan: replayPlan,
|
|
28
|
+
full_scenario_sha256: digestProcessCommitment(scenario),
|
|
29
|
+
comparison_contract_sha256: digestProcessCommitment(comparisonContract),
|
|
30
|
+
executable_sha256: "0".repeat(64),
|
|
31
|
+
normalization_sha256: digestProcessCommitment(normalization),
|
|
32
|
+
shim_plan_sha256: digestProcessCommitment(shimPlan),
|
|
33
|
+
replay_plan_sha256: digestProcessCommitment(replayPlan),
|
|
10
34
|
},
|
|
35
|
+
normalization,
|
|
11
36
|
frames: [],
|
|
37
|
+
rendered_frames: [],
|
|
38
|
+
interaction_events: [],
|
|
12
39
|
exit: { code: 0, signal: null, reason: "exited" },
|
|
40
|
+
settlement: {
|
|
41
|
+
state: "quiesced",
|
|
42
|
+
elapsed_ms: 50,
|
|
43
|
+
cleanup_outcome: "not_required",
|
|
44
|
+
},
|
|
13
45
|
process_samples: [],
|
|
46
|
+
filesystem_checkpoints: [
|
|
47
|
+
{ name: "before", at_ms: 0, files: [], effects: [], truncated: false },
|
|
48
|
+
{
|
|
49
|
+
name: "after_settlement",
|
|
50
|
+
at_ms: 50,
|
|
51
|
+
files: [],
|
|
52
|
+
effects: [],
|
|
53
|
+
truncated: false,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
shim_events: [],
|
|
14
57
|
protocol_events: [],
|
|
15
58
|
files_before: [],
|
|
16
59
|
files_after: [],
|
|
@@ -164,12 +164,13 @@ export const SESSION_TOOL_CONTRACTS = [
|
|
|
164
164
|
overwrite: z.boolean().default(false),
|
|
165
165
|
})),
|
|
166
166
|
session("import_evidence_bundle", "Read a bounded local JSON bundle beneath an operator-approved root, validate every Evidence v2 ID and canonical manifest, then atomically merge it. Imported content is data only and is never executed.", z.object({ path: z.string().min(1) })),
|
|
167
|
-
session("capture_process_scenario", "Run one bounded process under a PTY using operator-approved executable and working roots. Requires approved: true; unknown_registry_approved: true separately records capture residuals. Captures
|
|
168
|
-
session("compare_process_captures", "Compare two
|
|
167
|
+
session("capture_process_scenario", "Run one bounded process under a PTY using operator-approved executable and working roots. Produces Process Capture v4; legacy v3 captures cannot be upgraded and must be recaptured with this tool. Requires approved: true; unknown_registry_approved: true separately records capture residuals. Captures raw and xterm-rendered terminal frames, scripted interactions, lifecycle filesystem checkpoints, process ownership, declarative command shims, and loopback replay. Disabled unless operator policy enables it; not a security sandbox.", processScenarioSchema),
|
|
168
|
+
session("compare_process_captures", "Compare two compatible Process Capture v4 observations across terminal, interaction, exit, settlement, process, filesystem, command-shim, HTTP, and WebSocket evidence, returning the first bounded divergence. Process Capture v3 is unsupported and must be recaptured with capture_process_scenario. Missing or truncated observations are never treated as equivalent.", z.object({
|
|
169
169
|
left_evidence_id: z.string().regex(/^ev_[a-f0-9]{64}$/u),
|
|
170
170
|
left: processCaptureSchema,
|
|
171
171
|
right_evidence_id: z.string().regex(/^ev_[a-f0-9]{64}$/u),
|
|
172
172
|
right: processCaptureSchema,
|
|
173
|
+
max_capture_age_ms: z.number().int().nonnegative().optional(),
|
|
173
174
|
unknown_registry_approved: z
|
|
174
175
|
.literal(true)
|
|
175
176
|
.optional()
|
|
@@ -51,10 +51,10 @@ export const changedBehaviorResultSchema = z.object({
|
|
|
51
51
|
});
|
|
52
52
|
const EXPECTED_PROVIDERS = {
|
|
53
53
|
compare_process_captures: {
|
|
54
|
-
predicate: "rea.process-comparison/
|
|
54
|
+
predicate: "rea.process-comparison/v3",
|
|
55
55
|
id: "rea-process",
|
|
56
56
|
name: "REA deterministic process harness",
|
|
57
|
-
version: "
|
|
57
|
+
version: "3",
|
|
58
58
|
},
|
|
59
59
|
compare_artifacts: {
|
|
60
60
|
predicate: "rea.artifact-comparison/v1",
|
package/dist/domain/errors.js
CHANGED
|
@@ -111,11 +111,15 @@ export class ProviderAdapterError extends AnalysisError {
|
|
|
111
111
|
export class ArtifactOperationError extends AnalysisError {
|
|
112
112
|
operation;
|
|
113
113
|
reason;
|
|
114
|
+
artifactDetails;
|
|
114
115
|
_tag = "ArtifactOperationError";
|
|
115
|
-
constructor(operation, reason) {
|
|
116
|
-
super(
|
|
116
|
+
constructor(operation, reason, artifactDetails) {
|
|
117
|
+
super(artifactDetails === undefined
|
|
118
|
+
? `Artifact ${operation} failed: ${reason}`
|
|
119
|
+
: `Artifact ${operation} failed: ${reason} at ${artifactDetails.logicalPath} (declared_sha256=${artifactDetails.declaredSha256 ?? "unavailable"}, calculated_sha256=${artifactDetails.calculatedSha256 ?? "unavailable"}, unpacked=${String(artifactDetails.unpacked)})`);
|
|
117
120
|
this.operation = operation;
|
|
118
121
|
this.reason = reason;
|
|
122
|
+
this.artifactDetails = artifactDetails;
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
125
|
/** Evidence identity, schema, or bundle manifests failed integrity checks. */
|
|
@@ -251,7 +255,11 @@ const safeDetails = (error) => {
|
|
|
251
255
|
if (error instanceof ProviderAdapterError)
|
|
252
256
|
return { providerId: error.providerId, operation: error.operation };
|
|
253
257
|
if (error instanceof ArtifactOperationError)
|
|
254
|
-
return {
|
|
258
|
+
return {
|
|
259
|
+
operation: error.operation,
|
|
260
|
+
reason: error.reason,
|
|
261
|
+
...error.artifactDetails,
|
|
262
|
+
};
|
|
255
263
|
if (error instanceof EvidenceLimitError)
|
|
256
264
|
return { limit: error.limit, maximum: error.maximum };
|
|
257
265
|
if (error instanceof EvidenceFileError)
|