rea-agents 2.2.0 → 2.4.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 (179) hide show
  1. package/README.md +57 -10
  2. package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
  3. package/dist/application/ArtifactExtraction.js +5 -1
  4. package/dist/application/ArtifactGraphConstruction.js +3 -15
  5. package/dist/application/ArtifactInventory/classify.js +5 -1
  6. package/dist/application/ArtifactInventory/reader.js +2 -0
  7. package/dist/application/BinarySession.js +9 -2
  8. package/dist/application/BinarySessionStatus.js +40 -1
  9. package/dist/application/CapabilityInventory.js +26 -3
  10. package/dist/application/CommandShimReplay.js +6 -3
  11. package/dist/application/CompositeProvider.js +5 -2
  12. package/dist/application/DirectAnalysis.js +1 -20
  13. package/dist/application/DirectAnalysisStatus.js +83 -0
  14. package/dist/application/Doctor.js +34 -25
  15. package/dist/application/DoctorDiagnostics.js +1 -4
  16. package/dist/application/DoctorProjection.js +43 -0
  17. package/dist/application/DoctorScope.js +102 -0
  18. package/dist/application/EvidenceReferenceResolver.js +26 -0
  19. package/dist/application/JavaScriptApplicationEvidence.js +1 -1
  20. package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
  21. package/dist/application/JavaScriptApplicationService.js +3 -2
  22. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
  23. package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
  24. package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
  25. package/dist/application/JavaScriptModuleRelationships.js +25 -1
  26. package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
  27. package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
  28. package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
  29. package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
  30. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
  31. package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
  32. package/dist/application/JavaScriptSemanticTraceService.js +45 -0
  33. package/dist/application/LoopbackReplay.js +277 -133
  34. package/dist/application/LoopbackReplayRecorder.js +101 -0
  35. package/dist/application/PermissionAuthority.js +7 -3
  36. package/dist/application/ProcessCaptureEnvironment.js +26 -0
  37. package/dist/application/ProcessCaptureJournal.js +95 -0
  38. package/dist/application/ProcessCaptureLifecycle.js +25 -13
  39. package/dist/application/ProcessCheckpoints.js +35 -4
  40. package/dist/application/ProcessCli.js +26 -4
  41. package/dist/application/ProcessHarness.js +64 -83
  42. package/dist/application/ProcessNormalization.js +9 -2
  43. package/dist/application/ProcessReactiveCoordinator.js +225 -0
  44. package/dist/application/ProcessReactiveEffects.js +141 -0
  45. package/dist/application/ProcessReactiveHarness.js +85 -0
  46. package/dist/application/ProcessReactiveObservations.js +105 -0
  47. package/dist/application/ProcessSampling.js +2 -0
  48. package/dist/application/SessionProviderRouter.js +7 -6
  49. package/dist/application/Setup.js +11 -10
  50. package/dist/application/SetupPlan.js +1 -1
  51. package/dist/application/SetupRegistrationEnvironment.js +12 -0
  52. package/dist/application/TerminalRenderer.js +3 -1
  53. package/dist/browser/CdpEndpoint.js +37 -4
  54. package/dist/catalogIdentity.js +16 -7
  55. package/dist/cli/coreAnalysisCommands.js +34 -2
  56. package/dist/cli/javascriptApplicationAnalysis.js +19 -0
  57. package/dist/cli/setupCommands.js +81 -32
  58. package/dist/cli/utilityCommands.js +10 -2
  59. package/dist/cliApplicationCommands.js +79 -16
  60. package/dist/cliCommandNames.js +3 -0
  61. package/dist/cliElectronCommands.js +23 -33
  62. package/dist/cliObservationOptions.js +1 -1
  63. package/dist/cliOutput.js +56 -0
  64. package/dist/cliProcessCommands.js +32 -1
  65. package/dist/cliSetup.js +30 -21
  66. package/dist/contracts/applicationToolContracts.js +54 -7
  67. package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
  68. package/dist/contracts/electronToolContracts.js +53 -32
  69. package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
  70. package/dist/contracts/processCaptureExample.js +1 -0
  71. package/dist/contracts/replayMachineExample.js +37 -0
  72. package/dist/contracts/toolContractExamples.js +2 -0
  73. package/dist/contracts/toolContracts.js +5 -1
  74. package/dist/contracts/toolEffects.js +3 -0
  75. package/dist/contracts/toolOutputSchemaGroups.js +2 -0
  76. package/dist/contracts/toolOutputSchemaPrimitives.js +43 -0
  77. package/dist/doctorRuntime.js +41 -0
  78. package/dist/domain/analysisErrorPresentation.js +5 -3
  79. package/dist/domain/analysisErrorProjection.js +7 -1
  80. package/dist/domain/analysisSnapshot.js +2 -0
  81. package/dist/domain/artifactGraph.js +2 -0
  82. package/dist/domain/binaryTarget.js +3 -6
  83. package/dist/domain/changedBehavior.js +5 -4
  84. package/dist/domain/completionLedgerGeneration.js +190 -0
  85. package/dist/domain/errors.js +4 -2
  86. package/dist/domain/evidence.js +2 -0
  87. package/dist/domain/evidenceCompletionLedger.js +113 -0
  88. package/dist/domain/hopperStartupFailure.js +9 -0
  89. package/dist/domain/javascriptApplicationAnalysis.js +44 -2
  90. package/dist/domain/javascriptExportShapeComparison.js +145 -0
  91. package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
  92. package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
  93. package/dist/domain/javascriptExportShapeSelection.js +132 -0
  94. package/dist/domain/javascriptExportShapeVariants.js +272 -0
  95. package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
  96. package/dist/domain/javascriptSemanticAnalysis.js +17 -36
  97. package/dist/domain/javascriptSemanticCallResolution.js +129 -0
  98. package/dist/domain/javascriptSemanticCalls.js +269 -0
  99. package/dist/domain/javascriptSemanticGraph.js +356 -0
  100. package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
  101. package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
  102. package/dist/domain/javascriptSemanticIr.js +14 -1
  103. package/dist/domain/javascriptSemanticPrimitives.js +36 -0
  104. package/dist/domain/javascriptSemanticProjection.js +36 -4
  105. package/dist/domain/javascriptSemanticProvenance.js +29 -0
  106. package/dist/domain/javascriptSemanticQuery.js +294 -0
  107. package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
  108. package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
  109. package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
  110. package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
  111. package/dist/domain/javascriptSemanticReturns.js +187 -0
  112. package/dist/domain/javascriptSemanticState.js +32 -0
  113. package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
  114. package/dist/domain/javascriptSemanticValues.js +54 -70
  115. package/dist/domain/processCapture.js +100 -0
  116. package/dist/domain/processCaptureReactiveValidation.js +175 -0
  117. package/dist/domain/processCaptureValidation.js +103 -12
  118. package/dist/domain/processComparison.js +78 -25
  119. package/dist/domain/processObservation.js +162 -0
  120. package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
  121. package/dist/domain/processReactiveMatching.js +210 -0
  122. package/dist/domain/processReactiveRuntime.js +120 -0
  123. package/dist/domain/processReactiveScenario.js +361 -0
  124. package/dist/domain/processReactiveScenarioPreflight.js +120 -0
  125. package/dist/domain/processReactiveTransition.js +161 -0
  126. package/dist/domain/processScenario.js +33 -0
  127. package/dist/domain/processTraceComparison.js +43 -0
  128. package/dist/domain/processTraceDimensionProjection.js +50 -0
  129. package/dist/domain/processTraceEvaluation.js +312 -0
  130. package/dist/domain/processTraceSpecification.js +373 -0
  131. package/dist/domain/reconstructionVerification.js +5 -4
  132. package/dist/domain/replayMachine.js +18 -2
  133. package/dist/domain/replayMachineRun.js +216 -0
  134. package/dist/domain/replayMachineRuntime.js +88 -9
  135. package/dist/domain/staticRuntimeCorrelation.js +4 -2
  136. package/dist/domain/zipPackageFormat.js +20 -0
  137. package/dist/evaluation/CodexAgentEval.js +24 -0
  138. package/dist/generatedPackageMetadata.js +1 -1
  139. package/dist/ghidra/GhidraClient.js +13 -2
  140. package/dist/ghidra/GhidraDiagnostics.js +1 -0
  141. package/dist/ghidra/GhidraLauncher.js +7 -3
  142. package/dist/ghidra/GhidraProvider.js +8 -1
  143. package/dist/hopper/BridgeLauncher.js +56 -14
  144. package/dist/hopper/HopperClient.js +23 -10
  145. package/dist/hopper/HopperDiagnostics.js +14 -0
  146. package/dist/hopper/HopperProcessDiagnostic.js +14 -0
  147. package/dist/hopper/HopperProvider.js +8 -1
  148. package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
  149. package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
  150. package/dist/main/transport.js +2 -0
  151. package/dist/mcpDoctor.js +310 -0
  152. package/dist/native/NativeMacOSProvider.js +1 -0
  153. package/dist/process/ProcessOwnership.js +200 -53
  154. package/dist/process/ProviderRunLineage.js +21 -0
  155. package/dist/server/createServer.js +4 -1
  156. package/dist/server/javascriptApplicationResult.js +22 -1
  157. package/dist/server/registerApplicationTools/characterization.js +3 -3
  158. package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
  159. package/dist/server/registerApplicationTools/compareVersions.js +12 -6
  160. package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
  161. package/dist/server/registerApplicationTools/coverage.js +3 -3
  162. package/dist/server/registerApplicationTools/helpers.js +37 -25
  163. package/dist/server/registerApplicationTools/traceFeature.js +9 -5
  164. package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
  165. package/dist/server/registerApplicationTools.js +4 -0
  166. package/dist/server/registerEvidenceResources.js +5 -1
  167. package/dist/server/registerProcessComparisonTool.js +17 -7
  168. package/dist/server/registerReplayMachineTool.js +20 -0
  169. package/dist/server/registerSessionTools.js +12 -7
  170. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  171. package/dist/serverIdentity.js +19 -0
  172. package/package.json +62 -30
  173. package/scripts/hopper-demo-x11.py +459 -61
  174. package/scripts/prepack.mjs +55 -0
  175. package/scripts/prepare.mjs +28 -0
  176. package/scripts/rea.mjs +26 -5
  177. package/skills/reverse-engineer-anything/SKILL.md +4 -3
  178. package/skills/reverse-engineer-anything/references/javascript-applications.md +10 -0
  179. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +2 -2
@@ -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
  };
@@ -27,6 +27,8 @@ export const targetFormatSchema = z.enum([
27
27
  "zip",
28
28
  "ipa",
29
29
  "apk",
30
+ "msix",
31
+ "appx",
30
32
  "asar",
31
33
  "dmg",
32
34
  "pkg",
@@ -44,6 +46,7 @@ const providerCapability = z.object({
44
46
  operation: z.string(),
45
47
  available: z.boolean(),
46
48
  reason: z.string().nullable(),
49
+ availability_code: z.enum(PROVIDER_REJECTION_CODES).nullable(),
47
50
  input_contract_version: z.number().int().min(1),
48
51
  output_contract_version: z.number().int().min(1),
49
52
  pagination: z.enum(["none", "offset", "cursor"]),
@@ -69,6 +72,45 @@ export const providerIdentity = z.object({
69
72
  name: z.string(),
70
73
  version: z.string().nullable(),
71
74
  });
75
+ const processCoordinates = {
76
+ launcher_pid: z.number().int().min(1),
77
+ process_group_id: z.number().int().min(1),
78
+ };
79
+ const processLineageObservation = z.discriminatedUnion("status", [
80
+ z.object({
81
+ status: z.literal("unavailable"),
82
+ observed_at: z.iso.datetime(),
83
+ ...processCoordinates,
84
+ reason: z.string().min(1),
85
+ }),
86
+ z.object({
87
+ status: z.literal("verified"),
88
+ observed_at: z.iso.datetime(),
89
+ schema_version: z.literal(1),
90
+ ...processCoordinates,
91
+ launcher_parent_pid: z.number().int().min(0),
92
+ descendants: z.array(z.object({
93
+ pid: z.number().int().min(1),
94
+ parent_pid: z.number().int().min(0),
95
+ process_group_id: z.number().int().min(1),
96
+ })),
97
+ }),
98
+ ]);
99
+ const analysisRun = z
100
+ .object({
101
+ run_id: z.string().uuid(),
102
+ process_lineage: z.discriminatedUnion("status", [
103
+ z.object({ status: z.literal("not_observed") }),
104
+ z.object({
105
+ status: z.literal("snapshots"),
106
+ snapshots: z.array(z.object({
107
+ provider: providerIdentity,
108
+ observation: processLineageObservation,
109
+ })),
110
+ }),
111
+ ]),
112
+ })
113
+ .nullable();
72
114
  const providerRejectionCode = z.enum(PROVIDER_REJECTION_CODES);
73
115
  const providerDiagnostics = z.record(z.string(), z.json());
74
116
  const providerAvailability = z.discriminatedUnion("status", [
@@ -146,6 +188,7 @@ export const sessionProvider = z.object({
146
188
  provider: providerIdentity,
147
189
  providers: z.array(providerIdentity),
148
190
  capabilities: z.array(providerCapability),
191
+ analysis_run: analysisRun,
149
192
  analysis_provider_binding: z
150
193
  .object({
151
194
  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) => {
@@ -15,6 +15,8 @@ const formatSchema = z.enum([
15
15
  "zip",
16
16
  "ipa",
17
17
  "apk",
18
+ "msix",
19
+ "appx",
18
20
  "asar",
19
21
  "dmg",
20
22
  "pkg",
@@ -45,6 +45,8 @@ const artifactFormatSchema = z.enum([
45
45
  "analysis-database",
46
46
  "ipa",
47
47
  "apk",
48
+ "msix",
49
+ "appx",
48
50
  "plist",
49
51
  "entitlements",
50
52
  "directory",
@@ -6,6 +6,7 @@ import { extname, isAbsolute, join, relative, resolve, sep } from "node:path";
6
6
  import { promisify } from "node:util";
7
7
  import { BinaryTargetError } from "./errors.js";
8
8
  import { err, ok } from "./result.js";
9
+ import { zipPackageFormatForPath } from "./zipPackageFormat.js";
9
10
  const execFileAsync = promisify(execFile);
10
11
  /**
11
12
  * Resolve and classify a readable local target before a provider is selected.
@@ -71,11 +72,7 @@ const detectArtifactFormat = async (path, handle) => {
71
72
  magic[1] === 0x4b &&
72
73
  [0x03, 0x05, 0x07].includes(magic[2] ?? -1) &&
73
74
  [0x04, 0x06, 0x08].includes(magic[3] ?? -1)) {
74
- return lower.endsWith(".ipa")
75
- ? "ipa"
76
- : lower.endsWith(".apk")
77
- ? "apk"
78
- : "zip";
75
+ return zipPackageFormatForPath(lower) ?? "zip";
79
76
  }
80
77
  const named = namedArtifactFormat(lower);
81
78
  if (named !== undefined)
@@ -104,7 +101,7 @@ const namedArtifactFormat = (lowerPath) => {
104
101
  return "source-map";
105
102
  return /\.(?:m?js|cjs)$/u.test(lowerPath) ? "javascript" : undefined;
106
103
  };
107
- const isArchiveFormat = (format) => ["zip", "ipa", "apk", "asar", "dmg", "pkg"].includes(format);
104
+ const isArchiveFormat = (format) => ["zip", "ipa", "apk", "msix", "appx", "asar", "dmg", "pkg"].includes(format);
108
105
  const sha256Handle = async (handle) => {
109
106
  const hash = createHash("sha256");
110
107
  const chunk = Buffer.allocUnsafe(64 * 1024);
@@ -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;
@@ -22,6 +22,8 @@ const subjectSchema = z.object({
22
22
  "zip",
23
23
  "ipa",
24
24
  "apk",
25
+ "msix",
26
+ "appx",
25
27
  "asar",
26
28
  "dmg",
27
29
  "pkg",
@@ -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);