rea-agents 2.1.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 (193) hide show
  1. package/README.md +84 -18
  2. package/dist/application/AndroidApplicationService.js +17 -0
  3. package/dist/application/AppleApplicationService.js +17 -0
  4. package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
  5. package/dist/application/BinarySession.js +9 -2
  6. package/dist/application/BinarySessionStatus.js +39 -1
  7. package/dist/application/CapabilityInventory.js +15 -0
  8. package/dist/application/ClientRegistrationStatus.js +11 -4
  9. package/dist/application/CommandShimReplay.js +6 -3
  10. package/dist/application/CompositeProvider.js +5 -2
  11. package/dist/application/DirectAnalysis.js +1 -20
  12. package/dist/application/DirectAnalysisStatus.js +83 -0
  13. package/dist/application/Doctor.js +34 -25
  14. package/dist/application/DoctorDiagnostics.js +1 -4
  15. package/dist/application/DoctorProjection.js +43 -0
  16. package/dist/application/DoctorScope.js +102 -0
  17. package/dist/application/EvidenceReferenceResolver.js +26 -0
  18. package/dist/application/InventoryProjectionEvidence.js +41 -0
  19. package/dist/application/InvestigationProviders.js +12 -0
  20. package/dist/application/JavaScriptApplicationEvidence.js +1 -1
  21. package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
  22. package/dist/application/JavaScriptApplicationService.js +3 -2
  23. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
  24. package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
  25. package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
  26. package/dist/application/JavaScriptModuleRelationships.js +25 -1
  27. package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
  28. package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
  29. package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
  30. package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
  31. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
  32. package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
  33. package/dist/application/JavaScriptSemanticTraceService.js +45 -0
  34. package/dist/application/LoopbackReplay.js +277 -133
  35. package/dist/application/LoopbackReplayRecorder.js +101 -0
  36. package/dist/application/PermissionAuthority.js +7 -3
  37. package/dist/application/ProcessCaptureJournal.js +95 -0
  38. package/dist/application/ProcessCaptureLifecycle.js +24 -13
  39. package/dist/application/ProcessCheckpoints.js +30 -4
  40. package/dist/application/ProcessCli.js +26 -4
  41. package/dist/application/ProcessHarness.js +20 -50
  42. package/dist/application/ProcessNormalization.js +3 -2
  43. package/dist/application/ProcessPairedExperiment.js +72 -0
  44. package/dist/application/ProcessReactiveCoordinator.js +218 -0
  45. package/dist/application/ProcessReactiveEffects.js +133 -0
  46. package/dist/application/ProcessReactiveObservations.js +20 -0
  47. package/dist/application/ProcessSampling.js +2 -0
  48. package/dist/application/SessionProviderRouter.js +7 -6
  49. package/dist/application/Setup.js +14 -13
  50. package/dist/application/SetupClientConfiguration.js +10 -6
  51. package/dist/application/SetupPlan.js +4 -3
  52. package/dist/application/SetupRegistrationEnvironment.js +12 -0
  53. package/dist/application/SetupSkill.js +44 -40
  54. package/dist/application/TerminalRenderer.js +3 -1
  55. package/dist/application/Uninstall.js +7 -1
  56. package/dist/application/Upgrade.js +16 -1
  57. package/dist/browser/CdpEndpoint.js +37 -4
  58. package/dist/catalogIdentity.js +16 -7
  59. package/dist/cli/coreAnalysisCommands.js +34 -2
  60. package/dist/cli/javascriptApplicationAnalysis.js +19 -0
  61. package/dist/cli/setupCommands.js +84 -30
  62. package/dist/cli/utilityCommands.js +10 -2
  63. package/dist/cliApplicationCommands.js +79 -16
  64. package/dist/cliCommandNames.js +3 -0
  65. package/dist/cliElectronCommands.js +23 -33
  66. package/dist/cliObservationOptions.js +1 -1
  67. package/dist/cliOutput.js +56 -0
  68. package/dist/cliProcessCommands.js +32 -1
  69. package/dist/cliSetup.js +55 -29
  70. package/dist/contracts/applicationToolContracts.js +54 -7
  71. package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
  72. package/dist/contracts/electronToolContracts.js +73 -4
  73. package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
  74. package/dist/contracts/managedToolContracts.js +7 -2
  75. package/dist/contracts/processCaptureExample.js +1 -0
  76. package/dist/contracts/replayMachineExample.js +37 -0
  77. package/dist/contracts/sessionStatusContract.js +26 -0
  78. package/dist/contracts/toolContractExamples.js +2 -0
  79. package/dist/contracts/toolContractTypes.js +12 -1
  80. package/dist/contracts/toolContracts.js +8 -11
  81. package/dist/contracts/toolEffects.js +3 -0
  82. package/dist/contracts/toolOutputSchemaGroups.js +53 -9
  83. package/dist/contracts/toolOutputSchemaPrimitives.js +73 -32
  84. package/dist/doctorRuntime.js +41 -0
  85. package/dist/domain/analysisErrorPresentation.js +5 -3
  86. package/dist/domain/analysisErrorProjection.js +7 -1
  87. package/dist/domain/androidApplication.js +206 -0
  88. package/dist/domain/appleApplication.js +221 -0
  89. package/dist/domain/boundedCartesianProjection.js +15 -0
  90. package/dist/domain/changedBehavior.js +5 -4
  91. package/dist/domain/completionLedgerGeneration.js +190 -0
  92. package/dist/domain/errors.js +4 -2
  93. package/dist/domain/evidenceCompletionLedger.js +113 -0
  94. package/dist/domain/hopperStartupFailure.js +9 -0
  95. package/dist/domain/javascriptApplicationAnalysis.js +44 -2
  96. package/dist/domain/javascriptExportShapeComparison.js +145 -0
  97. package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
  98. package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
  99. package/dist/domain/javascriptExportShapeSelection.js +132 -0
  100. package/dist/domain/javascriptExportShapeVariants.js +272 -0
  101. package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
  102. package/dist/domain/javascriptSemanticAnalysis.js +17 -36
  103. package/dist/domain/javascriptSemanticCallResolution.js +129 -0
  104. package/dist/domain/javascriptSemanticCalls.js +269 -0
  105. package/dist/domain/javascriptSemanticGraph.js +356 -0
  106. package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
  107. package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
  108. package/dist/domain/javascriptSemanticIr.js +14 -1
  109. package/dist/domain/javascriptSemanticPrimitives.js +36 -0
  110. package/dist/domain/javascriptSemanticProjection.js +36 -4
  111. package/dist/domain/javascriptSemanticProvenance.js +29 -0
  112. package/dist/domain/javascriptSemanticQuery.js +294 -0
  113. package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
  114. package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
  115. package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
  116. package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
  117. package/dist/domain/javascriptSemanticReturns.js +187 -0
  118. package/dist/domain/javascriptSemanticState.js +32 -0
  119. package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
  120. package/dist/domain/javascriptSemanticValues.js +54 -70
  121. package/dist/domain/processCapture.js +36 -0
  122. package/dist/domain/processCaptureValidation.js +101 -12
  123. package/dist/domain/processComparison.js +96 -24
  124. package/dist/domain/processObservation.js +153 -0
  125. package/dist/domain/processPairedExperiment.js +128 -0
  126. package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
  127. package/dist/domain/processReactiveMatching.js +210 -0
  128. package/dist/domain/processReactiveRuntime.js +109 -0
  129. package/dist/domain/processReactiveScenario.js +361 -0
  130. package/dist/domain/processReactiveScenarioPreflight.js +120 -0
  131. package/dist/domain/processReactiveTransition.js +159 -0
  132. package/dist/domain/processScenario.js +12 -0
  133. package/dist/domain/processTraceComparison.js +43 -0
  134. package/dist/domain/processTraceDimensionProjection.js +50 -0
  135. package/dist/domain/processTraceEvaluation.js +312 -0
  136. package/dist/domain/processTraceSpecification.js +373 -0
  137. package/dist/domain/reconstructionVerification.js +5 -4
  138. package/dist/domain/replayMachine.js +332 -0
  139. package/dist/domain/replayMachineRun.js +216 -0
  140. package/dist/domain/replayMachineRuntime.js +294 -0
  141. package/dist/domain/runtimeIdentification.js +210 -0
  142. package/dist/domain/staticRuntimeCorrelation.js +4 -2
  143. package/dist/evaluation/CodexAgentEval.js +156 -0
  144. package/dist/generatedPackageMetadata.js +2 -2
  145. package/dist/ghidra/GhidraClient.js +13 -2
  146. package/dist/ghidra/GhidraDiagnostics.js +1 -0
  147. package/dist/ghidra/GhidraLauncher.js +7 -3
  148. package/dist/ghidra/GhidraProvider.js +8 -1
  149. package/dist/hopper/BridgeLauncher.js +56 -14
  150. package/dist/hopper/HopperClient.js +23 -10
  151. package/dist/hopper/HopperDiagnostics.js +14 -0
  152. package/dist/hopper/HopperProcessDiagnostic.js +14 -0
  153. package/dist/hopper/HopperProvider.js +8 -1
  154. package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
  155. package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
  156. package/dist/identity.js +2 -1
  157. package/dist/main/transport.js +2 -0
  158. package/dist/mcpDoctor.js +310 -0
  159. package/dist/process/ProcessOwnership.js +200 -53
  160. package/dist/process/ProviderRunLineage.js +21 -0
  161. package/dist/server/createServer.js +8 -2
  162. package/dist/server/javascriptApplicationResult.js +106 -0
  163. package/dist/server/registerApplicationTools/characterization.js +3 -3
  164. package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
  165. package/dist/server/registerApplicationTools/compareVersions.js +12 -6
  166. package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
  167. package/dist/server/registerApplicationTools/coverage.js +3 -3
  168. package/dist/server/registerApplicationTools/helpers.js +37 -25
  169. package/dist/server/registerApplicationTools/traceFeature.js +9 -5
  170. package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
  171. package/dist/server/registerApplicationTools.js +4 -0
  172. package/dist/server/registerElectronTools.js +19 -5
  173. package/dist/server/registerEvidenceResources.js +7 -1
  174. package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
  175. package/dist/server/registerManagedTools.js +18 -1
  176. package/dist/server/registerProcessComparisonTool.js +17 -7
  177. package/dist/server/registerReplayMachineTool.js +20 -0
  178. package/dist/server/registerSessionStatusTool.js +95 -25
  179. package/dist/server/registerSessionTools.js +12 -7
  180. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  181. package/dist/server/toolResult.js +10 -4
  182. package/dist/serverIdentity.js +19 -0
  183. package/package.json +62 -29
  184. package/scripts/hopper-demo-x11.py +459 -61
  185. package/scripts/prepack.mjs +55 -0
  186. package/scripts/prepare.mjs +28 -0
  187. package/scripts/rea.mjs +26 -5
  188. package/skills/reverse-engineer-anything/SKILL.md +69 -228
  189. package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
  190. package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
  191. package/skills/reverse-engineer-anything/references/javascript-applications.md +35 -0
  192. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
  193. package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
@@ -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
+ ]);
@@ -0,0 +1,145 @@
1
+ import { compareCodePoints } from "./javascriptApplicationGraph.js";
2
+ import { digestExportShapeValue } from "./javascriptExportShapeComparisonIdentity.js";
3
+ import { javaScriptExportShapeComparisonResultSchema, } from "./javascriptExportShapeComparisonSchemas.js";
4
+ import { retainJavaScriptExportShapes, selectJavaScriptExport, } from "./javascriptExportShapeSelection.js";
5
+ import { buildJavaScriptExportShapeChanges, compareJavaScriptExportShapeChanges, hasPartialJavaScriptExportPropertyCoverage, pairJavaScriptExportShapeVariants, } from "./javascriptExportShapeVariants.js";
6
+ /** Compare exact selected export return shapes without executing JavaScript. */
7
+ export const compareJavaScriptExportShapes = (input) => {
8
+ const left = selectJavaScriptExport(input.left, input.limits.max_candidate_exports);
9
+ const right = selectJavaScriptExport(input.right, input.limits.max_candidate_exports);
10
+ const leftRetained = retainJavaScriptExportShapes(left, input.limits.max_return_variants);
11
+ const rightRetained = retainJavaScriptExportShapes(right, input.limits.max_return_variants);
12
+ const pairing = pairJavaScriptExportShapeVariants(leftRetained.shapes, rightRetained.shapes);
13
+ const evidenceLinks = evidencePair(input.left.evidenceId, input.right.evidenceId);
14
+ const allChanges = buildJavaScriptExportShapeChanges({
15
+ leftSelection: left,
16
+ rightSelection: right,
17
+ pairing,
18
+ leftShapes: leftRetained.shapes,
19
+ rightShapes: rightRetained.shapes,
20
+ evidenceLinks,
21
+ }).sort(compareJavaScriptExportShapeChanges);
22
+ const changes = allChanges.slice(0, input.limits.max_changes);
23
+ const omittedChanges = allChanges.length - changes.length;
24
+ const coverage = comparisonCoverage({
25
+ input,
26
+ left,
27
+ right,
28
+ leftRetained,
29
+ rightRetained,
30
+ pairing,
31
+ allChanges,
32
+ omittedChanges,
33
+ });
34
+ const limitations = comparisonLimitations(left, right, coverage.status);
35
+ const semantic = {
36
+ schema_version: 1,
37
+ left: left.selection,
38
+ right: right.selection,
39
+ summary: summarize(allChanges),
40
+ changes,
41
+ coverage,
42
+ evidence_links: evidenceLinks,
43
+ limitations,
44
+ runtime_validation: {
45
+ recommended_tool: "run_controlled_replay",
46
+ automatically_started: false,
47
+ required_for: "runtime-semantics",
48
+ },
49
+ };
50
+ return javaScriptExportShapeComparisonResultSchema.parse({
51
+ ...semantic,
52
+ comparison_id: `jesc_${digestExportShapeValue(semantic)}`,
53
+ });
54
+ };
55
+ const comparisonCoverage = (context) => {
56
+ const leftSource = context.left.projection?.return_shape_coverage;
57
+ const rightSource = context.right.projection?.return_shape_coverage;
58
+ const omittedCandidates = context.left.selection.omitted_candidates +
59
+ context.right.selection.omitted_candidates;
60
+ const truncated = comparisonTruncated(context, omittedCandidates);
61
+ const partial = comparisonPartial(context);
62
+ return {
63
+ status: truncated
64
+ ? "truncated"
65
+ : partial
66
+ ? "partial"
67
+ : "complete-within-inputs",
68
+ left_graph_status: context.input.left.graph.coverage.status,
69
+ right_graph_status: context.input.right.graph.coverage.status,
70
+ paired_variants: context.pairing.pairs.length,
71
+ unpaired_left_variants: context.pairing.unpairedLeft.length,
72
+ unpaired_right_variants: context.pairing.unpairedRight.length,
73
+ omitted_left_variants: context.leftRetained.omitted,
74
+ omitted_right_variants: context.rightRetained.omitted,
75
+ left_source_omitted_variants: leftSource?.omitted_return_sites ?? null,
76
+ right_source_omitted_variants: rightSource?.omitted_return_sites ?? null,
77
+ left_omitted_fields: leftSource?.omitted_fields ?? 0,
78
+ right_omitted_fields: rightSource?.omitted_fields ?? 0,
79
+ left_omitted_property_coverage: leftSource?.omitted_property_coverage ?? 0,
80
+ right_omitted_property_coverage: rightSource?.omitted_property_coverage ?? 0,
81
+ omitted_candidates: omittedCandidates,
82
+ omitted_changes: context.omittedChanges,
83
+ };
84
+ };
85
+ const comparisonTruncated = (context, omittedCandidates) => context.leftRetained.omitted > 0 ||
86
+ context.rightRetained.omitted > 0 ||
87
+ omittedCandidates > 0 ||
88
+ context.omittedChanges > 0 ||
89
+ sourceProjectionTruncated(context.left) ||
90
+ sourceProjectionTruncated(context.right);
91
+ const sourceProjectionTruncated = (selection) => {
92
+ const coverage = selection.projection?.return_shape_coverage;
93
+ if (coverage === undefined)
94
+ return false;
95
+ return (coverage.status === "truncated" ||
96
+ coverage.omitted_fields > 0 ||
97
+ coverage.omitted_property_coverage > 0 ||
98
+ coverage.projection_complete === false);
99
+ };
100
+ const comparisonPartial = (context) => selectionPartial(context.left) ||
101
+ selectionPartial(context.right) ||
102
+ context.input.left.graph.coverage.status !== "complete" ||
103
+ context.input.right.graph.coverage.status !== "complete" ||
104
+ hasPartialJavaScriptExportPropertyCoverage(context.leftRetained.shapes) ||
105
+ hasPartialJavaScriptExportPropertyCoverage(context.rightRetained.shapes) ||
106
+ context.pairing.unpairedLeft.length > 0 ||
107
+ context.pairing.unpairedRight.length > 0 ||
108
+ context.allChanges.some(({ status }) => status === "unknown");
109
+ const selectionPartial = (selection) => {
110
+ const coverage = selection.projection?.return_shape_coverage;
111
+ return (selection.selection.status !== "selected" ||
112
+ !selection.observationComplete ||
113
+ coverage?.status !== "complete" ||
114
+ coverage.projection_complete !== true);
115
+ };
116
+ const comparisonLimitations = (left, right, status) => uniqueSorted([
117
+ "Return shapes are inferred from inert syntax and do not prove runtime behavior.",
118
+ "Return variants are paired only by unique exact literal discriminants, never by source order.",
119
+ "Dynamic values, incomplete object coverage, and ambiguous variants remain unknown.",
120
+ "Controlled replay is recommended separately for runtime semantics and was not started.",
121
+ ...left.limitations,
122
+ ...right.limitations,
123
+ ...(status === "truncated"
124
+ ? [
125
+ "Comparison output or source return-shape recovery reached an explicit limit.",
126
+ ]
127
+ : []),
128
+ ...(status === "partial"
129
+ ? [
130
+ "At least one selector, graph, return shape, or property boundary is incomplete.",
131
+ ]
132
+ : []),
133
+ ]);
134
+ const summarize = (changes) => ({
135
+ added: countStatus(changes, "added"),
136
+ removed: countStatus(changes, "removed"),
137
+ changed: countStatus(changes, "changed"),
138
+ unknown: countStatus(changes, "unknown"),
139
+ });
140
+ const countStatus = (changes, status) => changes.filter((change) => change.status === status).length;
141
+ const evidencePair = (left, right) => [
142
+ left,
143
+ right,
144
+ ];
145
+ const uniqueSorted = (values) => [...new Set(values)].sort(compareCodePoints);
@@ -0,0 +1,11 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ /** Canonically encode one export-shape comparison value. */
4
+ export const canonicalExportShapeValue = (value) => {
5
+ const encoded = canonicalize(value);
6
+ if (encoded === undefined)
7
+ throw new TypeError("JavaScript export shape comparison could not canonicalize data");
8
+ return encoded;
9
+ };
10
+ /** Derive one deterministic SHA-256 comparison identifier component. */
11
+ export const digestExportShapeValue = (value) => createHash("sha256").update(canonicalExportShapeValue(value)).digest("hex");