rea-agents 2.2.0 → 2.3.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 +56 -9
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
- package/dist/application/BinarySession.js +9 -2
- package/dist/application/BinarySessionStatus.js +39 -1
- package/dist/application/CapabilityInventory.js +15 -0
- package/dist/application/CommandShimReplay.js +6 -3
- package/dist/application/CompositeProvider.js +5 -2
- package/dist/application/DirectAnalysis.js +1 -20
- package/dist/application/DirectAnalysisStatus.js +83 -0
- package/dist/application/Doctor.js +34 -25
- package/dist/application/DoctorDiagnostics.js +1 -4
- package/dist/application/DoctorProjection.js +43 -0
- package/dist/application/DoctorScope.js +102 -0
- package/dist/application/EvidenceReferenceResolver.js +26 -0
- package/dist/application/JavaScriptApplicationEvidence.js +1 -1
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
- package/dist/application/JavaScriptApplicationService.js +3 -2
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
- package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
- package/dist/application/JavaScriptModuleRelationships.js +25 -1
- package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
- package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
- package/dist/application/JavaScriptSemanticTraceService.js +45 -0
- package/dist/application/LoopbackReplay.js +277 -133
- package/dist/application/LoopbackReplayRecorder.js +101 -0
- package/dist/application/PermissionAuthority.js +7 -3
- package/dist/application/ProcessCaptureJournal.js +95 -0
- package/dist/application/ProcessCaptureLifecycle.js +24 -13
- package/dist/application/ProcessCheckpoints.js +30 -4
- package/dist/application/ProcessCli.js +26 -4
- package/dist/application/ProcessHarness.js +20 -50
- package/dist/application/ProcessNormalization.js +3 -2
- package/dist/application/ProcessReactiveCoordinator.js +218 -0
- package/dist/application/ProcessReactiveEffects.js +133 -0
- package/dist/application/ProcessReactiveObservations.js +20 -0
- package/dist/application/ProcessSampling.js +2 -0
- package/dist/application/SessionProviderRouter.js +7 -6
- package/dist/application/Setup.js +11 -10
- package/dist/application/SetupPlan.js +1 -1
- package/dist/application/SetupRegistrationEnvironment.js +12 -0
- package/dist/application/TerminalRenderer.js +3 -1
- package/dist/browser/CdpEndpoint.js +37 -4
- package/dist/catalogIdentity.js +16 -7
- package/dist/cli/coreAnalysisCommands.js +34 -2
- package/dist/cli/javascriptApplicationAnalysis.js +19 -0
- package/dist/cli/setupCommands.js +81 -32
- package/dist/cli/utilityCommands.js +10 -2
- package/dist/cliApplicationCommands.js +79 -16
- package/dist/cliCommandNames.js +3 -0
- package/dist/cliElectronCommands.js +23 -33
- package/dist/cliObservationOptions.js +1 -1
- package/dist/cliOutput.js +56 -0
- package/dist/cliProcessCommands.js +32 -1
- package/dist/cliSetup.js +30 -21
- package/dist/contracts/applicationToolContracts.js +54 -7
- package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
- package/dist/contracts/electronToolContracts.js +53 -32
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
- package/dist/contracts/processCaptureExample.js +1 -0
- package/dist/contracts/replayMachineExample.js +37 -0
- package/dist/contracts/toolContractExamples.js +2 -0
- package/dist/contracts/toolContracts.js +5 -1
- package/dist/contracts/toolEffects.js +3 -0
- package/dist/contracts/toolOutputSchemaGroups.js +2 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
- package/dist/doctorRuntime.js +41 -0
- package/dist/domain/analysisErrorPresentation.js +5 -3
- package/dist/domain/analysisErrorProjection.js +7 -1
- package/dist/domain/changedBehavior.js +5 -4
- package/dist/domain/completionLedgerGeneration.js +190 -0
- package/dist/domain/errors.js +4 -2
- package/dist/domain/evidenceCompletionLedger.js +113 -0
- package/dist/domain/hopperStartupFailure.js +9 -0
- package/dist/domain/javascriptApplicationAnalysis.js +44 -2
- package/dist/domain/javascriptExportShapeComparison.js +145 -0
- package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
- package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
- package/dist/domain/javascriptExportShapeSelection.js +132 -0
- package/dist/domain/javascriptExportShapeVariants.js +272 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
- package/dist/domain/javascriptSemanticAnalysis.js +17 -36
- package/dist/domain/javascriptSemanticCallResolution.js +129 -0
- package/dist/domain/javascriptSemanticCalls.js +269 -0
- package/dist/domain/javascriptSemanticGraph.js +356 -0
- package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
- package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
- package/dist/domain/javascriptSemanticIr.js +14 -1
- package/dist/domain/javascriptSemanticPrimitives.js +36 -0
- package/dist/domain/javascriptSemanticProjection.js +36 -4
- package/dist/domain/javascriptSemanticProvenance.js +29 -0
- package/dist/domain/javascriptSemanticQuery.js +294 -0
- package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
- package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
- package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
- package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
- package/dist/domain/javascriptSemanticReturns.js +187 -0
- package/dist/domain/javascriptSemanticState.js +32 -0
- package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
- package/dist/domain/javascriptSemanticValues.js +54 -70
- package/dist/domain/processCapture.js +36 -0
- package/dist/domain/processCaptureValidation.js +101 -12
- package/dist/domain/processComparison.js +78 -25
- package/dist/domain/processObservation.js +153 -0
- package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
- package/dist/domain/processReactiveMatching.js +210 -0
- package/dist/domain/processReactiveRuntime.js +109 -0
- package/dist/domain/processReactiveScenario.js +361 -0
- package/dist/domain/processReactiveScenarioPreflight.js +120 -0
- package/dist/domain/processReactiveTransition.js +159 -0
- package/dist/domain/processScenario.js +12 -0
- package/dist/domain/processTraceComparison.js +43 -0
- package/dist/domain/processTraceDimensionProjection.js +50 -0
- package/dist/domain/processTraceEvaluation.js +312 -0
- package/dist/domain/processTraceSpecification.js +373 -0
- package/dist/domain/reconstructionVerification.js +5 -4
- package/dist/domain/replayMachine.js +18 -2
- package/dist/domain/replayMachineRun.js +216 -0
- package/dist/domain/replayMachineRuntime.js +88 -9
- package/dist/domain/staticRuntimeCorrelation.js +4 -2
- package/dist/evaluation/CodexAgentEval.js +24 -0
- package/dist/generatedPackageMetadata.js +1 -1
- package/dist/ghidra/GhidraClient.js +13 -2
- package/dist/ghidra/GhidraDiagnostics.js +1 -0
- package/dist/ghidra/GhidraLauncher.js +7 -3
- package/dist/ghidra/GhidraProvider.js +8 -1
- package/dist/hopper/BridgeLauncher.js +56 -14
- package/dist/hopper/HopperClient.js +23 -10
- package/dist/hopper/HopperDiagnostics.js +14 -0
- package/dist/hopper/HopperProcessDiagnostic.js +14 -0
- package/dist/hopper/HopperProvider.js +8 -1
- package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
- package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
- package/dist/main/transport.js +2 -0
- package/dist/mcpDoctor.js +310 -0
- package/dist/process/ProcessOwnership.js +200 -53
- package/dist/process/ProviderRunLineage.js +21 -0
- package/dist/server/createServer.js +4 -1
- package/dist/server/javascriptApplicationResult.js +22 -1
- package/dist/server/registerApplicationTools/characterization.js +3 -3
- package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
- package/dist/server/registerApplicationTools/compareVersions.js +12 -6
- package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
- package/dist/server/registerApplicationTools/coverage.js +3 -3
- package/dist/server/registerApplicationTools/helpers.js +37 -25
- package/dist/server/registerApplicationTools/traceFeature.js +9 -5
- package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
- package/dist/server/registerApplicationTools.js +4 -0
- package/dist/server/registerEvidenceResources.js +5 -1
- package/dist/server/registerProcessComparisonTool.js +17 -7
- package/dist/server/registerReplayMachineTool.js +20 -0
- package/dist/server/registerSessionTools.js +12 -7
- package/dist/server/sessionAvailabilityPolicy.js +2 -1
- package/dist/serverIdentity.js +19 -0
- package/package.json +62 -30
- package/scripts/hopper-demo-x11.py +459 -61
- package/scripts/prepack.mjs +55 -0
- package/scripts/prepare.mjs +28 -0
- package/scripts/rea.mjs +26 -5
- package/skills/reverse-engineer-anything/SKILL.md +2 -2
- package/skills/reverse-engineer-anything/references/javascript-applications.md +10 -0
|
@@ -18,6 +18,7 @@ import { changedBehaviorResultSchema } from "../domain/changedBehavior.js";
|
|
|
18
18
|
import { callPathResultSchema } from "../domain/callPath.js";
|
|
19
19
|
import { staticRuntimeCorrelationResultSchema } from "../domain/staticRuntimeCorrelation.js";
|
|
20
20
|
import { reconstructionVerificationResultSchema } from "../domain/reconstructionVerification.js";
|
|
21
|
+
import { replayMachineRunOutputSchema } from "../domain/replayMachineRun.js";
|
|
21
22
|
import { analysisErrorProjectionSchema } from "./errorSchemas.js";
|
|
22
23
|
import { addressList, addressedEntry, bounded, containingProcedureResolution, functionDossierOutput, graphNode, lifecycleResultOf, nullableText, pageOutput, procedureIdentity, procedureInfoOutput, evidenceResultOf as resultOf, searchPageOutput, segmentOutput, sessionProvider, providerIdentity, toolAvailability, symbolDiscoveryOutput, targetFormatSchema, targetKindSchema, } from "./toolOutputSchemaPrimitives.js";
|
|
23
24
|
/** Exact structured-content schemas shared by direct analysis providers. */
|
|
@@ -271,4 +272,5 @@ export const sessionOutputSchemas = {
|
|
|
271
272
|
truthVerified: z.boolean(),
|
|
272
273
|
unknown: residualUnknownSchema,
|
|
273
274
|
})),
|
|
275
|
+
run_replay_machine: lifecycleResultOf(replayMachineRunOutputSchema),
|
|
274
276
|
};
|
|
@@ -69,6 +69,45 @@ export const providerIdentity = z.object({
|
|
|
69
69
|
name: z.string(),
|
|
70
70
|
version: z.string().nullable(),
|
|
71
71
|
});
|
|
72
|
+
const processCoordinates = {
|
|
73
|
+
launcher_pid: z.number().int().min(1),
|
|
74
|
+
process_group_id: z.number().int().min(1),
|
|
75
|
+
};
|
|
76
|
+
const processLineageObservation = z.discriminatedUnion("status", [
|
|
77
|
+
z.object({
|
|
78
|
+
status: z.literal("unavailable"),
|
|
79
|
+
observed_at: z.iso.datetime(),
|
|
80
|
+
...processCoordinates,
|
|
81
|
+
reason: z.string().min(1),
|
|
82
|
+
}),
|
|
83
|
+
z.object({
|
|
84
|
+
status: z.literal("verified"),
|
|
85
|
+
observed_at: z.iso.datetime(),
|
|
86
|
+
schema_version: z.literal(1),
|
|
87
|
+
...processCoordinates,
|
|
88
|
+
launcher_parent_pid: z.number().int().min(0),
|
|
89
|
+
descendants: z.array(z.object({
|
|
90
|
+
pid: z.number().int().min(1),
|
|
91
|
+
parent_pid: z.number().int().min(0),
|
|
92
|
+
process_group_id: z.number().int().min(1),
|
|
93
|
+
})),
|
|
94
|
+
}),
|
|
95
|
+
]);
|
|
96
|
+
const analysisRun = z
|
|
97
|
+
.object({
|
|
98
|
+
run_id: z.string().uuid(),
|
|
99
|
+
process_lineage: z.discriminatedUnion("status", [
|
|
100
|
+
z.object({ status: z.literal("not_observed") }),
|
|
101
|
+
z.object({
|
|
102
|
+
status: z.literal("snapshots"),
|
|
103
|
+
snapshots: z.array(z.object({
|
|
104
|
+
provider: providerIdentity,
|
|
105
|
+
observation: processLineageObservation,
|
|
106
|
+
})),
|
|
107
|
+
}),
|
|
108
|
+
]),
|
|
109
|
+
})
|
|
110
|
+
.nullable();
|
|
72
111
|
const providerRejectionCode = z.enum(PROVIDER_REJECTION_CODES);
|
|
73
112
|
const providerDiagnostics = z.record(z.string(), z.json());
|
|
74
113
|
const providerAvailability = z.discriminatedUnion("status", [
|
|
@@ -146,6 +185,7 @@ export const sessionProvider = z.object({
|
|
|
146
185
|
provider: providerIdentity,
|
|
147
186
|
providers: z.array(providerIdentity),
|
|
148
187
|
capabilities: z.array(providerCapability),
|
|
188
|
+
analysis_run: analysisRun,
|
|
149
189
|
analysis_provider_binding: z
|
|
150
190
|
.object({
|
|
151
191
|
provider: providerIdentity,
|
package/dist/doctorRuntime.js
CHANGED
|
@@ -1,12 +1,53 @@
|
|
|
1
1
|
import { systemDoctorHost, } from "./application/Doctor.js";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import { inspectSystemGhidraProvider } from "./ghidra/GhidraDoctor.js";
|
|
4
|
+
import { selectLinuxPrivateDisplayStrategy } from "./hopper/LinuxPrivateDisplayProbe.js";
|
|
3
5
|
import { parseConfig } from "./config.js";
|
|
4
6
|
import { SystemJavaScriptReplayHost } from "./replay/SystemJavaScriptReplayHost.js";
|
|
5
7
|
/** Compose provider diagnostics at the outer CLI adapter boundary. */
|
|
6
8
|
export const createSystemDoctorHost = () => systemDoctorHost({
|
|
7
9
|
providerInspections: async () => [await inspectSystemGhidraProvider()],
|
|
8
10
|
javascriptReplayCheck: inspectJavaScriptReplay,
|
|
11
|
+
linuxDemoRuntimeCheck: inspectLinuxPrivateDisplay,
|
|
9
12
|
});
|
|
13
|
+
const HOPPER_DEMO_HELPER_PATH = fileURLToPath(new URL("../scripts/hopper-demo-x11.py", import.meta.url));
|
|
14
|
+
const inspectLinuxPrivateDisplay = async () => {
|
|
15
|
+
const selection = await selectLinuxPrivateDisplayStrategy({
|
|
16
|
+
helperPath: HOPPER_DEMO_HELPER_PATH,
|
|
17
|
+
});
|
|
18
|
+
const value = selection.diagnostic;
|
|
19
|
+
const detail = [
|
|
20
|
+
`strategy=${selection.strategy}`,
|
|
21
|
+
`socket_directory=${value.socket_directory}`,
|
|
22
|
+
`mode=${value.socket_directory_mode ?? "unknown"}`,
|
|
23
|
+
`mount_read_only=${String(value.mount_read_only)}`,
|
|
24
|
+
`effective_mode=${value.effective_socket_directory_mode ?? "unknown"}`,
|
|
25
|
+
`effective_mount_read_only=${String(value.effective_mount_read_only)}`,
|
|
26
|
+
`wsl=${String(value.wsl)}`,
|
|
27
|
+
].join("; ");
|
|
28
|
+
if (selection.ok)
|
|
29
|
+
return {
|
|
30
|
+
name: "hopper-demo-runtime",
|
|
31
|
+
ok: true,
|
|
32
|
+
classification: "healthy",
|
|
33
|
+
detail,
|
|
34
|
+
details: { private_display: { ...value } },
|
|
35
|
+
};
|
|
36
|
+
const missingDependency = value.failure_code === "runtime_dependency_unavailable" ||
|
|
37
|
+
value.failure_code === "x11_authorization_failed";
|
|
38
|
+
return {
|
|
39
|
+
name: "hopper-demo-runtime",
|
|
40
|
+
ok: false,
|
|
41
|
+
classification: missingDependency ? "missing_dependency" : "config_drift",
|
|
42
|
+
detail,
|
|
43
|
+
details: { private_display: { ...value } },
|
|
44
|
+
remediation: value.failure_code === "x11_socket_directory_unusable"
|
|
45
|
+
? "Enable unprivileged user and mount namespaces, or provide a writable mode-1777 /tmp/.X11-unix directory. Do not mutate the current host mount with sudo; then rerun rea doctor --provider hopper."
|
|
46
|
+
: missingDependency
|
|
47
|
+
? "Install Xvfb, xauth, Python 3, libX11, and libXtst, then rerun rea doctor --provider hopper."
|
|
48
|
+
: "Inspect the private-display diagnostic, correct the reported local condition, and rerun rea doctor --provider hopper.",
|
|
49
|
+
};
|
|
50
|
+
};
|
|
10
51
|
const inspectJavaScriptReplay = async () => {
|
|
11
52
|
const config = parseConfig(process.env);
|
|
12
53
|
if (!config.ok)
|
|
@@ -5,9 +5,11 @@ export const analysisErrorRemediationAction = (error) => {
|
|
|
5
5
|
if (error instanceof ReplayPlanStaleError)
|
|
6
6
|
return "Review the rebuilt replay plan and explicitly approve its new digest.";
|
|
7
7
|
if (error instanceof PermissionRequiredError)
|
|
8
|
-
return error.
|
|
9
|
-
? "
|
|
10
|
-
:
|
|
8
|
+
return error.restartRequired
|
|
9
|
+
? "Add the exact missing scope to the administrator configuration, then restart the registered MCP server or client."
|
|
10
|
+
: error.elicitationSupported
|
|
11
|
+
? "Approve the exact missing scope, then retry the operation."
|
|
12
|
+
: "Add the exact missing scope beneath the administrator ceiling, then retry.";
|
|
11
13
|
return analysisErrorUserMessage(error);
|
|
12
14
|
};
|
|
13
15
|
export const analysisErrorCategory = (error) => {
|
|
@@ -230,7 +230,13 @@ const providerErrorDetails = (error) => {
|
|
|
230
230
|
if (error instanceof HopperRemoteError)
|
|
231
231
|
return { provider_code: error.code, diagnostic_type: error.diagnosticType };
|
|
232
232
|
if (error instanceof HopperProcessError && error.failureCode !== undefined)
|
|
233
|
-
return {
|
|
233
|
+
return {
|
|
234
|
+
failure_code: error.failureCode,
|
|
235
|
+
exit_code: error.exitCode,
|
|
236
|
+
...(error.diagnostic === undefined
|
|
237
|
+
? {}
|
|
238
|
+
: { diagnostics: { ...error.diagnostic } }),
|
|
239
|
+
};
|
|
234
240
|
return undefined;
|
|
235
241
|
};
|
|
236
242
|
const lifecycleErrorDetails = (error) => {
|
|
@@ -63,24 +63,25 @@ export const changedBehaviorResultSchema = z.object({
|
|
|
63
63
|
});
|
|
64
64
|
const EXPECTED_PROVIDERS = {
|
|
65
65
|
compare_process_captures: {
|
|
66
|
-
|
|
66
|
+
predicates: ["rea.process-comparison/v3", "rea.process-comparison/v4"],
|
|
67
67
|
id: "rea-process",
|
|
68
68
|
name: "REA deterministic process harness",
|
|
69
69
|
version: "3",
|
|
70
70
|
},
|
|
71
71
|
compare_artifacts: {
|
|
72
|
-
|
|
72
|
+
predicates: ["rea.artifact-comparison/v1"],
|
|
73
73
|
id: "rea-artifact-comparison",
|
|
74
74
|
name: "REA artifact comparison",
|
|
75
75
|
version: "1",
|
|
76
76
|
},
|
|
77
77
|
compare_functions: {
|
|
78
|
-
|
|
78
|
+
predicates: ["rea.function-comparison/v1"],
|
|
79
79
|
id: "rea-function-comparison",
|
|
80
80
|
name: "REA function comparison",
|
|
81
81
|
version: "1",
|
|
82
82
|
},
|
|
83
83
|
};
|
|
84
|
+
const acceptsPredicate = (expected, actual) => expected.some((predicate) => predicate === actual);
|
|
84
85
|
/** Find observed runtime changes and separately report static change candidates. */
|
|
85
86
|
export const findChangedBehavior = (comparisonsInput, offset, limit) => {
|
|
86
87
|
const parsedInput = changedBehaviorInputSchema.parse({
|
|
@@ -141,7 +142,7 @@ const parseComparisonEvidence = (input) => {
|
|
|
141
142
|
])
|
|
142
143
|
.parse(evidence.operation);
|
|
143
144
|
const expected = EXPECTED_PROVIDERS[operation];
|
|
144
|
-
if (evidence.predicate_type
|
|
145
|
+
if (!acceptsPredicate(expected.predicates, evidence.predicate_type) ||
|
|
145
146
|
evidence.provider.id !== expected.id ||
|
|
146
147
|
evidence.provider.name !== expected.name ||
|
|
147
148
|
evidence.provider.version !== expected.version ||
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import canonicalize from "canonicalize";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { createEvidenceCompletionLedger, evidenceCompletionRecordSchema, } from "./evidenceCompletionLedger.js";
|
|
5
|
+
const identifierSchema = z.string().regex(/^[A-Za-z][A-Za-z0-9._-]{0,99}$/u);
|
|
6
|
+
const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
7
|
+
const identitySchema = z.strictObject({
|
|
8
|
+
id: identifierSchema,
|
|
9
|
+
version: z.string().min(1).max(100),
|
|
10
|
+
});
|
|
11
|
+
const environmentSchema = z.strictObject({
|
|
12
|
+
platform: identifierSchema,
|
|
13
|
+
architecture: identifierSchema,
|
|
14
|
+
runtime: identifierSchema,
|
|
15
|
+
runtime_version: z.string().min(1).max(100),
|
|
16
|
+
});
|
|
17
|
+
const scenarioSchema = z.strictObject({
|
|
18
|
+
id: identifierSchema,
|
|
19
|
+
version: z.number().int().positive(),
|
|
20
|
+
});
|
|
21
|
+
const skillDigestSchema = z.strictObject({
|
|
22
|
+
skill_id: identifierSchema,
|
|
23
|
+
sha256: digestSchema,
|
|
24
|
+
});
|
|
25
|
+
const verifierRunSchema = z
|
|
26
|
+
.strictObject({
|
|
27
|
+
schema_version: z.literal(1),
|
|
28
|
+
run_id: z.string().uuid(),
|
|
29
|
+
verifier_pid: z.number().int().positive(),
|
|
30
|
+
parent_pid: z.number().int().nonnegative(),
|
|
31
|
+
process_lineage: z.discriminatedUnion("status", [
|
|
32
|
+
z.strictObject({
|
|
33
|
+
status: z.literal("verified"),
|
|
34
|
+
schema_version: z.literal(1),
|
|
35
|
+
observed_at: z.iso.datetime(),
|
|
36
|
+
launcher_pid: z.number().int().positive(),
|
|
37
|
+
launcher_parent_pid: z.number().int().nonnegative(),
|
|
38
|
+
process_group_id: z.number().int().positive(),
|
|
39
|
+
descendants: z.array(z.strictObject({
|
|
40
|
+
pid: z.number().int().positive(),
|
|
41
|
+
parent_pid: z.number().int().nonnegative(),
|
|
42
|
+
process_group_id: z.number().int().positive(),
|
|
43
|
+
})),
|
|
44
|
+
}),
|
|
45
|
+
z.strictObject({
|
|
46
|
+
status: z.literal("unavailable"),
|
|
47
|
+
observed_at: z.iso.datetime(),
|
|
48
|
+
launcher_pid: z.number().int().positive(),
|
|
49
|
+
launcher_parent_pid: z.number().int().nonnegative(),
|
|
50
|
+
process_group_id: z.number().int().positive().nullable(),
|
|
51
|
+
reason: z.string().min(1).max(500),
|
|
52
|
+
}),
|
|
53
|
+
]),
|
|
54
|
+
})
|
|
55
|
+
.superRefine((run, context) => {
|
|
56
|
+
if (run.process_lineage.launcher_pid !== run.verifier_pid)
|
|
57
|
+
context.addIssue({
|
|
58
|
+
code: "custom",
|
|
59
|
+
path: ["process_lineage", "launcher_pid"],
|
|
60
|
+
message: "Lineage launcher PID must identify the verifier process",
|
|
61
|
+
});
|
|
62
|
+
if (run.process_lineage.launcher_parent_pid !== run.parent_pid)
|
|
63
|
+
context.addIssue({
|
|
64
|
+
code: "custom",
|
|
65
|
+
path: ["process_lineage", "launcher_parent_pid"],
|
|
66
|
+
message: "Lineage parent PID must identify the verifier parent",
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
const reportClaimSchema = z.strictObject({
|
|
70
|
+
claim_id: identifierSchema,
|
|
71
|
+
scenario: scenarioSchema,
|
|
72
|
+
artifact_sha256s: z.array(digestSchema).max(100),
|
|
73
|
+
provider: identitySchema,
|
|
74
|
+
result_schema_version: z.number().int().positive(),
|
|
75
|
+
status: evidenceCompletionRecordSchema.shape.status,
|
|
76
|
+
evidence_ids: evidenceCompletionRecordSchema.shape.evidence_ids,
|
|
77
|
+
});
|
|
78
|
+
const completionVerifierReportObjectSchema = z.strictObject({
|
|
79
|
+
schema_version: z.literal(1),
|
|
80
|
+
verifier: identitySchema,
|
|
81
|
+
verifier_run: verifierRunSchema,
|
|
82
|
+
environment: environmentSchema,
|
|
83
|
+
claims: z.array(reportClaimSchema).min(1).max(10_000),
|
|
84
|
+
});
|
|
85
|
+
/** Strict output contract implemented by completion-aware verifiers. */
|
|
86
|
+
export const completionVerifierReportSchema = completionVerifierReportObjectSchema.superRefine((report, context) => {
|
|
87
|
+
for (const [index, claim] of report.claims.entries())
|
|
88
|
+
if (claim.status === "pass" && claim.artifact_sha256s.length === 0)
|
|
89
|
+
context.addIssue({
|
|
90
|
+
code: "custom",
|
|
91
|
+
path: ["claims", index, "artifact_sha256s"],
|
|
92
|
+
message: "Passing claims must commit at least one artifact digest",
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
const manifestClaimSchema = reportClaimSchema.omit({
|
|
96
|
+
status: true,
|
|
97
|
+
evidence_ids: true,
|
|
98
|
+
});
|
|
99
|
+
const completionManifestObjectSchema = z.strictObject({
|
|
100
|
+
schema_version: z.literal(1),
|
|
101
|
+
manifest_id: z.string().regex(/^ecm_[a-f0-9]{64}$/u),
|
|
102
|
+
verifier: identitySchema,
|
|
103
|
+
environment: environmentSchema,
|
|
104
|
+
skill_digests: z.array(skillDigestSchema).min(1).max(100),
|
|
105
|
+
claims: z.array(manifestClaimSchema).min(1).max(10_000),
|
|
106
|
+
});
|
|
107
|
+
const compareIdentifiers = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
108
|
+
const isSortedUnique = (values) => values.every((value, index) => index === 0 || compareIdentifiers(values[index - 1] ?? "", value) < 0);
|
|
109
|
+
const canonicalJson = (value) => {
|
|
110
|
+
const serialized = canonicalize(value);
|
|
111
|
+
if (serialized === undefined)
|
|
112
|
+
throw new TypeError("RFC 8785 canonicalization rejected completion metadata");
|
|
113
|
+
return serialized;
|
|
114
|
+
};
|
|
115
|
+
const computeManifestId = (manifest) => `ecm_${createHash("sha256").update(canonicalJson(manifest)).digest("hex")}`;
|
|
116
|
+
const manifestIssues = (manifest, context) => {
|
|
117
|
+
if (!isSortedUnique(manifest.claims.map(({ claim_id }) => claim_id)))
|
|
118
|
+
context.addIssue({
|
|
119
|
+
code: "custom",
|
|
120
|
+
path: ["claims"],
|
|
121
|
+
message: "Manifest claims must be sorted by unique claim ID",
|
|
122
|
+
});
|
|
123
|
+
if (!isSortedUnique(manifest.skill_digests.map(({ skill_id }) => skill_id)))
|
|
124
|
+
context.addIssue({
|
|
125
|
+
code: "custom",
|
|
126
|
+
path: ["skill_digests"],
|
|
127
|
+
message: "Skill digests must be sorted by unique skill ID",
|
|
128
|
+
});
|
|
129
|
+
for (const [index, claim] of manifest.claims.entries())
|
|
130
|
+
if (!isSortedUnique(claim.artifact_sha256s))
|
|
131
|
+
context.addIssue({
|
|
132
|
+
code: "custom",
|
|
133
|
+
path: ["claims", index, "artifact_sha256s"],
|
|
134
|
+
message: "Artifact digests must be sorted and unique",
|
|
135
|
+
});
|
|
136
|
+
const { manifest_id: manifestId, ...projection } = manifest;
|
|
137
|
+
if (computeManifestId(projection) !== manifestId)
|
|
138
|
+
context.addIssue({
|
|
139
|
+
code: "custom",
|
|
140
|
+
path: ["manifest_id"],
|
|
141
|
+
message: "Completion manifest identifier does not match its content",
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
/** Canonical immutable commitments for one verifier run. */
|
|
145
|
+
export const completionManifestSchema = completionManifestObjectSchema.superRefine(manifestIssues);
|
|
146
|
+
const canonicalClaims = (claims) => claims
|
|
147
|
+
.map((claim) => ({
|
|
148
|
+
...claim,
|
|
149
|
+
artifact_sha256s: [...claim.artifact_sha256s].sort(compareIdentifiers),
|
|
150
|
+
evidence_ids: [...claim.evidence_ids].sort(compareIdentifiers),
|
|
151
|
+
}))
|
|
152
|
+
.sort((left, right) => compareIdentifiers(left.claim_id, right.claim_id));
|
|
153
|
+
/** Generate canonical manifest and ledger artifacts from live verifier output. */
|
|
154
|
+
export const createCompletionLedgerArtifacts = (input, skillDigests) => {
|
|
155
|
+
const report = completionVerifierReportSchema.parse(input);
|
|
156
|
+
const claims = canonicalClaims(report.claims);
|
|
157
|
+
if (!isSortedUnique(claims.map(({ claim_id }) => claim_id)))
|
|
158
|
+
throw new TypeError("Completion verifier claim IDs must be unique");
|
|
159
|
+
for (const claim of claims) {
|
|
160
|
+
if (!isSortedUnique(claim.artifact_sha256s))
|
|
161
|
+
throw new TypeError("Artifact digests must be unique per claim");
|
|
162
|
+
if (!isSortedUnique(claim.evidence_ids))
|
|
163
|
+
throw new TypeError("Evidence IDs must be unique per claim");
|
|
164
|
+
}
|
|
165
|
+
const skills = skillDigests
|
|
166
|
+
.map((skill) => skillDigestSchema.parse(skill))
|
|
167
|
+
.sort((left, right) => compareIdentifiers(left.skill_id, right.skill_id));
|
|
168
|
+
if (!isSortedUnique(skills.map(({ skill_id }) => skill_id)))
|
|
169
|
+
throw new TypeError("Completion skill IDs must be unique");
|
|
170
|
+
const projection = {
|
|
171
|
+
schema_version: 1,
|
|
172
|
+
verifier: report.verifier,
|
|
173
|
+
environment: report.environment,
|
|
174
|
+
skill_digests: skills,
|
|
175
|
+
claims: claims.map(({ status: _status, evidence_ids: _evidenceIds, ...claim }) => claim),
|
|
176
|
+
};
|
|
177
|
+
return {
|
|
178
|
+
manifest: completionManifestSchema.parse({
|
|
179
|
+
...projection,
|
|
180
|
+
manifest_id: computeManifestId(projection),
|
|
181
|
+
}),
|
|
182
|
+
ledger: createEvidenceCompletionLedger(claims.map(({ claim_id, status, evidence_ids }) => ({
|
|
183
|
+
claim_id,
|
|
184
|
+
status,
|
|
185
|
+
evidence_ids,
|
|
186
|
+
}))),
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
/** Parse generated completion commitments and reject manual tampering. */
|
|
190
|
+
export const parseCompletionManifest = (input) => completionManifestSchema.parse(input);
|
package/dist/domain/errors.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hopperStartupFailure, } from "./hopperStartupFailure.js";
|
|
2
|
-
export { hopperStartupFailure };
|
|
2
|
+
export { hopperStartupFailure, };
|
|
3
3
|
export { projectAnalysisError } from "./analysisErrorProjection.js";
|
|
4
4
|
/** Stable tags exposed by safe analysis-error projections. */
|
|
5
5
|
const ANALYSIS_ERROR_TAGS = [
|
|
@@ -292,12 +292,14 @@ export class HopperRemoteError extends HopperError {
|
|
|
292
292
|
/** The owned Hopper bridge stopped before its client was closed. */
|
|
293
293
|
export class HopperProcessError extends HopperError {
|
|
294
294
|
exitCode;
|
|
295
|
+
diagnostic;
|
|
295
296
|
_tag = "HopperProcessError";
|
|
296
297
|
failureCode;
|
|
297
298
|
userMessage;
|
|
298
|
-
constructor(exitCode) {
|
|
299
|
+
constructor(exitCode, diagnostic) {
|
|
299
300
|
super(`Hopper bridge stopped unexpectedly with code ${String(exitCode)}`);
|
|
300
301
|
this.exitCode = exitCode;
|
|
302
|
+
this.diagnostic = diagnostic;
|
|
301
303
|
const failure = hopperStartupFailure(exitCode);
|
|
302
304
|
this.failureCode = failure?.code;
|
|
303
305
|
this.userMessage = failure?.message;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import canonicalize from "canonicalize";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
|
|
5
|
+
const claimIdSchema = z.string().regex(/^[A-Za-z][A-Za-z0-9._-]{0,99}$/u);
|
|
6
|
+
/** Terminal outcome recorded for one verifier claim. */
|
|
7
|
+
export const evidenceCompletionStatusSchema = z.enum([
|
|
8
|
+
"pass",
|
|
9
|
+
"fail",
|
|
10
|
+
"unsupported",
|
|
11
|
+
"truncated",
|
|
12
|
+
"unknown",
|
|
13
|
+
]);
|
|
14
|
+
/** Strict evidence-linked result for one verifier claim. */
|
|
15
|
+
export const evidenceCompletionRecordSchema = z.strictObject({
|
|
16
|
+
claim_id: claimIdSchema,
|
|
17
|
+
status: evidenceCompletionStatusSchema,
|
|
18
|
+
evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
|
|
19
|
+
});
|
|
20
|
+
const completionSummarySchema = z.strictObject({
|
|
21
|
+
total: z.number().int().min(1).max(10_000),
|
|
22
|
+
pass: z.number().int().nonnegative(),
|
|
23
|
+
fail: z.number().int().nonnegative(),
|
|
24
|
+
unsupported: z.number().int().nonnegative(),
|
|
25
|
+
truncated: z.number().int().nonnegative(),
|
|
26
|
+
unknown: z.number().int().nonnegative(),
|
|
27
|
+
complete: z.boolean(),
|
|
28
|
+
});
|
|
29
|
+
const completionLedgerObjectSchema = z.strictObject({
|
|
30
|
+
schema_version: z.literal(1),
|
|
31
|
+
ledger_id: z.string().regex(/^ecl_[a-f0-9]{64}$/u),
|
|
32
|
+
records: z.array(evidenceCompletionRecordSchema).min(1).max(10_000),
|
|
33
|
+
summary: completionSummarySchema,
|
|
34
|
+
});
|
|
35
|
+
const compareIdentifiers = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
36
|
+
const isSortedUnique = (values) => values.every((value, index) => index === 0 || compareIdentifiers(values[index - 1] ?? "", value) < 0);
|
|
37
|
+
const summarize = (records) => {
|
|
38
|
+
const counts = {
|
|
39
|
+
pass: 0,
|
|
40
|
+
fail: 0,
|
|
41
|
+
unsupported: 0,
|
|
42
|
+
truncated: 0,
|
|
43
|
+
unknown: 0,
|
|
44
|
+
};
|
|
45
|
+
for (const record of records)
|
|
46
|
+
counts[record.status] += 1;
|
|
47
|
+
return {
|
|
48
|
+
total: records.length,
|
|
49
|
+
...counts,
|
|
50
|
+
complete: counts.pass === records.length,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const canonicalJson = (value) => {
|
|
54
|
+
const serialized = canonicalize(value);
|
|
55
|
+
if (serialized === undefined)
|
|
56
|
+
throw new TypeError("RFC 8785 canonicalization rejected the completion ledger");
|
|
57
|
+
return serialized;
|
|
58
|
+
};
|
|
59
|
+
const computeLedgerId = (records) => `ecl_${createHash("sha256")
|
|
60
|
+
.update(canonicalJson({ schema_version: 1, records }))
|
|
61
|
+
.digest("hex")}`;
|
|
62
|
+
const ledgerIssues = (ledger, context) => {
|
|
63
|
+
if (!isSortedUnique(ledger.records.map((record) => record.claim_id)))
|
|
64
|
+
context.addIssue({
|
|
65
|
+
code: "custom",
|
|
66
|
+
path: ["records"],
|
|
67
|
+
message: "Completion records must be sorted by unique claim ID",
|
|
68
|
+
});
|
|
69
|
+
for (const [index, record] of ledger.records.entries())
|
|
70
|
+
if (!isSortedUnique(record.evidence_ids))
|
|
71
|
+
context.addIssue({
|
|
72
|
+
code: "custom",
|
|
73
|
+
path: ["records", index, "evidence_ids"],
|
|
74
|
+
message: "Evidence IDs must be sorted and unique",
|
|
75
|
+
});
|
|
76
|
+
if (JSON.stringify(summarize(ledger.records)) !== JSON.stringify(ledger.summary))
|
|
77
|
+
context.addIssue({
|
|
78
|
+
code: "custom",
|
|
79
|
+
path: ["summary"],
|
|
80
|
+
message: "Completion summary does not match its records",
|
|
81
|
+
});
|
|
82
|
+
if (computeLedgerId(ledger.records) !== ledger.ledger_id)
|
|
83
|
+
context.addIssue({
|
|
84
|
+
code: "custom",
|
|
85
|
+
path: ["ledger_id"],
|
|
86
|
+
message: "Completion ledger identifier does not match its records",
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
/** Strict path-free Evidence v2 completion ledger. */
|
|
90
|
+
export const evidenceCompletionLedgerSchema = completionLedgerObjectSchema.superRefine(ledgerIssues);
|
|
91
|
+
/** Build a canonical path-independent ledger from verifier claim outcomes. */
|
|
92
|
+
export const createEvidenceCompletionLedger = (input) => {
|
|
93
|
+
const records = input
|
|
94
|
+
.map((record) => evidenceCompletionRecordSchema.parse(record))
|
|
95
|
+
.map((record) => ({
|
|
96
|
+
...record,
|
|
97
|
+
evidence_ids: [...record.evidence_ids].sort(compareIdentifiers),
|
|
98
|
+
}))
|
|
99
|
+
.sort((left, right) => compareIdentifiers(left.claim_id, right.claim_id));
|
|
100
|
+
if (!isSortedUnique(records.map((record) => record.claim_id)))
|
|
101
|
+
throw new TypeError("Completion claim IDs must be unique");
|
|
102
|
+
for (const record of records)
|
|
103
|
+
if (!isSortedUnique(record.evidence_ids))
|
|
104
|
+
throw new TypeError("Completion Evidence IDs must be unique per claim");
|
|
105
|
+
return evidenceCompletionLedgerSchema.parse({
|
|
106
|
+
schema_version: 1,
|
|
107
|
+
ledger_id: computeLedgerId(records),
|
|
108
|
+
records,
|
|
109
|
+
summary: summarize(records),
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
/** Parse a completion ledger and reject non-canonical or tampered content. */
|
|
113
|
+
export const parseEvidenceCompletionLedger = (input) => evidenceCompletionLedgerSchema.parse(input);
|
|
@@ -49,7 +49,16 @@ const failures = [
|
|
|
49
49
|
"runtime_dependency_unavailable",
|
|
50
50
|
"A Linux demo-session dependency is unavailable. Rerun rea setup --yes --install-hopper, then rea doctor.",
|
|
51
51
|
],
|
|
52
|
+
[
|
|
53
|
+
80,
|
|
54
|
+
"x11_socket_directory_unusable",
|
|
55
|
+
"REA cannot use the host X11 socket directory for a private display. Run rea doctor --provider hopper for exact mount and namespace diagnostics.",
|
|
56
|
+
],
|
|
52
57
|
];
|
|
53
58
|
const HOPPER_STARTUP_FAILURES = new Map(failures.map(([exitCode, code, message]) => [exitCode, { code, message }]));
|
|
54
59
|
/** Return the stable public failure associated with a Linux demo adapter exit. */
|
|
55
60
|
export const hopperStartupFailure = (exitCode) => exitCode === null ? undefined : HOPPER_STARTUP_FAILURES.get(exitCode);
|
|
61
|
+
/** Return the adapter exit associated with one stable startup failure code. */
|
|
62
|
+
export const hopperStartupExitCode = (code) => failures.find(([, candidate]) => candidate === code)?.[0];
|
|
63
|
+
/** Narrow an untrusted diagnostic field to the public startup code union. */
|
|
64
|
+
export const isHopperStartupFailureCode = (value) => failures.some(([, candidate]) => candidate === value);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isAbsolute } from "node:path";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { javascriptApplicationGraphSchema } from "./javascriptApplicationGraph.js";
|
|
4
|
+
import { javaScriptSemanticGraphSchema } from "./javascriptSemanticGraph.js";
|
|
4
5
|
const MAX_GRAPH_NODES = 100_000;
|
|
5
6
|
const MAX_GRAPH_EDGES = 200_000;
|
|
6
7
|
const countSchema = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
@@ -140,8 +141,8 @@ const reconstructionStatisticsSchema = z.strictObject({
|
|
|
140
141
|
parse_failures: countSchema,
|
|
141
142
|
truncated_scopes: countSchema,
|
|
142
143
|
});
|
|
143
|
-
/**
|
|
144
|
-
export const
|
|
144
|
+
/** Legacy structural JavaScript application analysis result. */
|
|
145
|
+
export const javaScriptApplicationAnalysisResultV1Schema = z.strictObject({
|
|
145
146
|
schema_version: z.literal(1),
|
|
146
147
|
input_path: z.string().min(1).max(16_384),
|
|
147
148
|
format: z.enum(["asar", "directory"]),
|
|
@@ -153,3 +154,44 @@ export const javascriptApplicationAnalysisResultSchema = z.strictObject({
|
|
|
153
154
|
statistics: reconstructionStatisticsSchema,
|
|
154
155
|
limitations: z.array(z.string().min(1).max(4_096)).max(1_000),
|
|
155
156
|
});
|
|
157
|
+
/** JavaScript application analysis with an authenticated semantic companion. */
|
|
158
|
+
export const javaScriptApplicationAnalysisResultV2Schema = javaScriptApplicationAnalysisResultV1Schema
|
|
159
|
+
.extend({
|
|
160
|
+
schema_version: z.literal(2),
|
|
161
|
+
semantic_graph: javaScriptSemanticGraphSchema,
|
|
162
|
+
})
|
|
163
|
+
.superRefine((result, context) => {
|
|
164
|
+
if (result.semantic_graph.application_graph_id !== result.graph.graph_id)
|
|
165
|
+
context.addIssue({
|
|
166
|
+
code: "custom",
|
|
167
|
+
path: ["semantic_graph", "application_graph_id"],
|
|
168
|
+
message: "Semantic graph must commit the containing application graph",
|
|
169
|
+
});
|
|
170
|
+
if (result.semantic_graph.root_artifact_sha256 !==
|
|
171
|
+
result.root_artifact_sha256)
|
|
172
|
+
context.addIssue({
|
|
173
|
+
code: "custom",
|
|
174
|
+
path: ["semantic_graph", "root_artifact_sha256"],
|
|
175
|
+
message: "Semantic graph must commit the containing root artifact",
|
|
176
|
+
});
|
|
177
|
+
const applicationNodeIds = new Set(result.graph.nodes.map(({ node_id }) => node_id));
|
|
178
|
+
for (const [nodeIndex, node] of result.semantic_graph.nodes.entries())
|
|
179
|
+
for (const [identifierIndex, identifier,] of node.application_node_ids.entries())
|
|
180
|
+
if (!applicationNodeIds.has(identifier))
|
|
181
|
+
context.addIssue({
|
|
182
|
+
code: "custom",
|
|
183
|
+
path: [
|
|
184
|
+
"semantic_graph",
|
|
185
|
+
"nodes",
|
|
186
|
+
nodeIndex,
|
|
187
|
+
"application_node_ids",
|
|
188
|
+
identifierIndex,
|
|
189
|
+
],
|
|
190
|
+
message: "Semantic node references an absent application node",
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
/** Strict versioned high-level result returned inside Evidence v2. */
|
|
194
|
+
export const javascriptApplicationAnalysisResultSchema = z.discriminatedUnion("schema_version", [
|
|
195
|
+
javaScriptApplicationAnalysisResultV1Schema,
|
|
196
|
+
javaScriptApplicationAnalysisResultV2Schema,
|
|
197
|
+
]);
|