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.
Files changed (165) hide show
  1. package/README.md +56 -9
  2. package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
  3. package/dist/application/BinarySession.js +9 -2
  4. package/dist/application/BinarySessionStatus.js +39 -1
  5. package/dist/application/CapabilityInventory.js +15 -0
  6. package/dist/application/CommandShimReplay.js +6 -3
  7. package/dist/application/CompositeProvider.js +5 -2
  8. package/dist/application/DirectAnalysis.js +1 -20
  9. package/dist/application/DirectAnalysisStatus.js +83 -0
  10. package/dist/application/Doctor.js +34 -25
  11. package/dist/application/DoctorDiagnostics.js +1 -4
  12. package/dist/application/DoctorProjection.js +43 -0
  13. package/dist/application/DoctorScope.js +102 -0
  14. package/dist/application/EvidenceReferenceResolver.js +26 -0
  15. package/dist/application/JavaScriptApplicationEvidence.js +1 -1
  16. package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
  17. package/dist/application/JavaScriptApplicationService.js +3 -2
  18. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
  19. package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
  20. package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
  21. package/dist/application/JavaScriptModuleRelationships.js +25 -1
  22. package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
  23. package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
  24. package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
  25. package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
  26. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
  27. package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
  28. package/dist/application/JavaScriptSemanticTraceService.js +45 -0
  29. package/dist/application/LoopbackReplay.js +277 -133
  30. package/dist/application/LoopbackReplayRecorder.js +101 -0
  31. package/dist/application/PermissionAuthority.js +7 -3
  32. package/dist/application/ProcessCaptureJournal.js +95 -0
  33. package/dist/application/ProcessCaptureLifecycle.js +24 -13
  34. package/dist/application/ProcessCheckpoints.js +30 -4
  35. package/dist/application/ProcessCli.js +26 -4
  36. package/dist/application/ProcessHarness.js +20 -50
  37. package/dist/application/ProcessNormalization.js +3 -2
  38. package/dist/application/ProcessReactiveCoordinator.js +218 -0
  39. package/dist/application/ProcessReactiveEffects.js +133 -0
  40. package/dist/application/ProcessReactiveObservations.js +20 -0
  41. package/dist/application/ProcessSampling.js +2 -0
  42. package/dist/application/SessionProviderRouter.js +7 -6
  43. package/dist/application/Setup.js +11 -10
  44. package/dist/application/SetupPlan.js +1 -1
  45. package/dist/application/SetupRegistrationEnvironment.js +12 -0
  46. package/dist/application/TerminalRenderer.js +3 -1
  47. package/dist/browser/CdpEndpoint.js +37 -4
  48. package/dist/catalogIdentity.js +16 -7
  49. package/dist/cli/coreAnalysisCommands.js +34 -2
  50. package/dist/cli/javascriptApplicationAnalysis.js +19 -0
  51. package/dist/cli/setupCommands.js +81 -32
  52. package/dist/cli/utilityCommands.js +10 -2
  53. package/dist/cliApplicationCommands.js +79 -16
  54. package/dist/cliCommandNames.js +3 -0
  55. package/dist/cliElectronCommands.js +23 -33
  56. package/dist/cliObservationOptions.js +1 -1
  57. package/dist/cliOutput.js +56 -0
  58. package/dist/cliProcessCommands.js +32 -1
  59. package/dist/cliSetup.js +30 -21
  60. package/dist/contracts/applicationToolContracts.js +54 -7
  61. package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
  62. package/dist/contracts/electronToolContracts.js +53 -32
  63. package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
  64. package/dist/contracts/processCaptureExample.js +1 -0
  65. package/dist/contracts/replayMachineExample.js +37 -0
  66. package/dist/contracts/toolContractExamples.js +2 -0
  67. package/dist/contracts/toolContracts.js +5 -1
  68. package/dist/contracts/toolEffects.js +3 -0
  69. package/dist/contracts/toolOutputSchemaGroups.js +2 -0
  70. package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
  71. package/dist/doctorRuntime.js +41 -0
  72. package/dist/domain/analysisErrorPresentation.js +5 -3
  73. package/dist/domain/analysisErrorProjection.js +7 -1
  74. package/dist/domain/changedBehavior.js +5 -4
  75. package/dist/domain/completionLedgerGeneration.js +190 -0
  76. package/dist/domain/errors.js +4 -2
  77. package/dist/domain/evidenceCompletionLedger.js +113 -0
  78. package/dist/domain/hopperStartupFailure.js +9 -0
  79. package/dist/domain/javascriptApplicationAnalysis.js +44 -2
  80. package/dist/domain/javascriptExportShapeComparison.js +145 -0
  81. package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
  82. package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
  83. package/dist/domain/javascriptExportShapeSelection.js +132 -0
  84. package/dist/domain/javascriptExportShapeVariants.js +272 -0
  85. package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
  86. package/dist/domain/javascriptSemanticAnalysis.js +17 -36
  87. package/dist/domain/javascriptSemanticCallResolution.js +129 -0
  88. package/dist/domain/javascriptSemanticCalls.js +269 -0
  89. package/dist/domain/javascriptSemanticGraph.js +356 -0
  90. package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
  91. package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
  92. package/dist/domain/javascriptSemanticIr.js +14 -1
  93. package/dist/domain/javascriptSemanticPrimitives.js +36 -0
  94. package/dist/domain/javascriptSemanticProjection.js +36 -4
  95. package/dist/domain/javascriptSemanticProvenance.js +29 -0
  96. package/dist/domain/javascriptSemanticQuery.js +294 -0
  97. package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
  98. package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
  99. package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
  100. package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
  101. package/dist/domain/javascriptSemanticReturns.js +187 -0
  102. package/dist/domain/javascriptSemanticState.js +32 -0
  103. package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
  104. package/dist/domain/javascriptSemanticValues.js +54 -70
  105. package/dist/domain/processCapture.js +36 -0
  106. package/dist/domain/processCaptureValidation.js +101 -12
  107. package/dist/domain/processComparison.js +78 -25
  108. package/dist/domain/processObservation.js +153 -0
  109. package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
  110. package/dist/domain/processReactiveMatching.js +210 -0
  111. package/dist/domain/processReactiveRuntime.js +109 -0
  112. package/dist/domain/processReactiveScenario.js +361 -0
  113. package/dist/domain/processReactiveScenarioPreflight.js +120 -0
  114. package/dist/domain/processReactiveTransition.js +159 -0
  115. package/dist/domain/processScenario.js +12 -0
  116. package/dist/domain/processTraceComparison.js +43 -0
  117. package/dist/domain/processTraceDimensionProjection.js +50 -0
  118. package/dist/domain/processTraceEvaluation.js +312 -0
  119. package/dist/domain/processTraceSpecification.js +373 -0
  120. package/dist/domain/reconstructionVerification.js +5 -4
  121. package/dist/domain/replayMachine.js +18 -2
  122. package/dist/domain/replayMachineRun.js +216 -0
  123. package/dist/domain/replayMachineRuntime.js +88 -9
  124. package/dist/domain/staticRuntimeCorrelation.js +4 -2
  125. package/dist/evaluation/CodexAgentEval.js +24 -0
  126. package/dist/generatedPackageMetadata.js +1 -1
  127. package/dist/ghidra/GhidraClient.js +13 -2
  128. package/dist/ghidra/GhidraDiagnostics.js +1 -0
  129. package/dist/ghidra/GhidraLauncher.js +7 -3
  130. package/dist/ghidra/GhidraProvider.js +8 -1
  131. package/dist/hopper/BridgeLauncher.js +56 -14
  132. package/dist/hopper/HopperClient.js +23 -10
  133. package/dist/hopper/HopperDiagnostics.js +14 -0
  134. package/dist/hopper/HopperProcessDiagnostic.js +14 -0
  135. package/dist/hopper/HopperProvider.js +8 -1
  136. package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
  137. package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
  138. package/dist/main/transport.js +2 -0
  139. package/dist/mcpDoctor.js +310 -0
  140. package/dist/process/ProcessOwnership.js +200 -53
  141. package/dist/process/ProviderRunLineage.js +21 -0
  142. package/dist/server/createServer.js +4 -1
  143. package/dist/server/javascriptApplicationResult.js +22 -1
  144. package/dist/server/registerApplicationTools/characterization.js +3 -3
  145. package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
  146. package/dist/server/registerApplicationTools/compareVersions.js +12 -6
  147. package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
  148. package/dist/server/registerApplicationTools/coverage.js +3 -3
  149. package/dist/server/registerApplicationTools/helpers.js +37 -25
  150. package/dist/server/registerApplicationTools/traceFeature.js +9 -5
  151. package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
  152. package/dist/server/registerApplicationTools.js +4 -0
  153. package/dist/server/registerEvidenceResources.js +5 -1
  154. package/dist/server/registerProcessComparisonTool.js +17 -7
  155. package/dist/server/registerReplayMachineTool.js +20 -0
  156. package/dist/server/registerSessionTools.js +12 -7
  157. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  158. package/dist/serverIdentity.js +19 -0
  159. package/package.json +62 -30
  160. package/scripts/hopper-demo-x11.py +459 -61
  161. package/scripts/prepack.mjs +55 -0
  162. package/scripts/prepare.mjs +28 -0
  163. package/scripts/rea.mjs +26 -5
  164. package/skills/reverse-engineer-anything/SKILL.md +2 -2
  165. 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,
@@ -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.elicitationSupported
9
- ? "Approve the exact missing scope, then retry the operation."
10
- : "Add the exact missing scope beneath the administrator ceiling, then retry.";
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 { failure_code: error.failureCode, exit_code: error.exitCode };
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
- predicate: "rea.process-comparison/v3",
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
- predicate: "rea.artifact-comparison/v1",
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
- predicate: "rea.function-comparison/v1",
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 !== expected.predicate ||
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);
@@ -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
- /** Strict high-level result returned inside Evidence v2. */
144
- export const javascriptApplicationAnalysisResultSchema = z.strictObject({
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
+ ]);