rea-agents 1.6.0 → 2.0.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 (238) hide show
  1. package/README.md +157 -66
  2. package/bridge/ghidra/ReaGhidraBridge.java +2075 -0
  3. package/bridge/hopper_bridge.py +13 -3
  4. package/dist/application/AbortablePromise.js +25 -0
  5. package/dist/application/AnalysisProvider.js +3 -0
  6. package/dist/application/AnalysisProviderEvaluation.js +60 -0
  7. package/dist/application/AnalysisProviderRegistry.js +228 -0
  8. package/dist/application/AnalysisSnapshotCache.js +41 -21
  9. package/dist/application/AnalysisSnapshotFiles.js +5 -6
  10. package/dist/application/BinarySession.js +218 -101
  11. package/dist/application/CapabilityInventory.js +18 -0
  12. package/dist/application/CompositeProvider.js +20 -8
  13. package/dist/application/CrossVersionInvestigation.js +10 -6
  14. package/dist/application/DirectAnalysis.js +49 -35
  15. package/dist/application/Doctor.js +110 -8
  16. package/dist/application/ElectronBoundaryAnalysis.js +96 -0
  17. package/dist/application/ElectronBoundaryGraph.js +9 -0
  18. package/dist/application/ElectronBoundaryGraphContext.js +62 -0
  19. package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
  20. package/dist/application/ElectronBoundaryGraphNative.js +86 -0
  21. package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
  22. package/dist/application/EnhancedTools.js +39 -14
  23. package/dist/application/InvestigationProviders.js +41 -0
  24. package/dist/application/JavaScriptApplicationEvidence.js +26 -0
  25. package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
  26. package/dist/application/JavaScriptApplicationService.js +74 -0
  27. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
  28. package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
  29. package/dist/application/JavaScriptArtifactAnalysis.js +305 -0
  30. package/dist/application/JavaScriptArtifactAnalysisTypes.js +1 -0
  31. package/dist/application/JavaScriptArtifactFiles.js +231 -0
  32. package/dist/application/JavaScriptArtifactGraphAccumulator.js +54 -0
  33. package/dist/application/JavaScriptArtifactGraphBuilder.js +144 -0
  34. package/dist/application/JavaScriptArtifactGraphContext.js +205 -0
  35. package/dist/application/JavaScriptArtifactGraphDocuments.js +108 -0
  36. package/dist/application/JavaScriptArtifactGraphEvidence.js +99 -0
  37. package/dist/application/JavaScriptArtifactGraphFindings.js +271 -0
  38. package/dist/application/JavaScriptArtifactGraphStructure.js +409 -0
  39. package/dist/application/JavaScriptArtifactReconstruction.js +77 -0
  40. package/dist/application/JavaScriptArtifactReconstructionInput.js +18 -0
  41. package/dist/application/JavaScriptReplayPlanning.js +201 -0
  42. package/dist/application/JavaScriptReplayService.js +266 -0
  43. package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
  44. package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
  45. package/dist/application/MacHopper.js +20 -4
  46. package/dist/application/ManagedApplicationGraphService.js +55 -0
  47. package/dist/application/ManagedMemberComparisonService.js +122 -0
  48. package/dist/application/ManagedNativeVerificationService.js +39 -0
  49. package/dist/application/ManagedReconstructionService.js +58 -0
  50. package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
  51. package/dist/application/PermissionAuthority.js +1 -1
  52. package/dist/application/ProcessCaptureLifecycle.js +1 -1
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProcessSampling.js +16 -12
  55. package/dist/application/ProjectPermissionStore.js +1 -0
  56. package/dist/application/SessionProviderRouter.js +219 -0
  57. package/dist/application/Setup.js +84 -60
  58. package/dist/application/SetupClients.js +1 -1
  59. package/dist/application/SetupPlan.js +9 -2
  60. package/dist/application/SupportedClients.js +40 -16
  61. package/dist/application/Upgrade.js +1 -1
  62. package/dist/application/runtime.js +11 -6
  63. package/dist/artifacts/AsarArtifactReader.js +23 -3
  64. package/dist/browser/CdpBrowserProvider.js +4 -2
  65. package/dist/browser/CdpCaptureEvents.js +49 -0
  66. package/dist/browser/CdpElectronInspection.js +106 -162
  67. package/dist/browser/CdpElectronProvider.js +9 -3
  68. package/dist/browser/CdpElectronScriptEvents.js +69 -0
  69. package/dist/browser/CdpElectronScripts.js +94 -0
  70. package/dist/browser/CdpElectronWorkers.js +56 -0
  71. package/dist/browser/CdpEndpoint.js +14 -1
  72. package/dist/browser/CdpPageCapture.js +8 -4
  73. package/dist/catalogIdentity.js +6 -42
  74. package/dist/cli.js +446 -34
  75. package/dist/cliApplicationCommands.js +62 -0
  76. package/dist/cliBrowserAdvancedCommands.js +5 -4
  77. package/dist/cliBrowserCommands.js +5 -4
  78. package/dist/cliCommandNames.js +56 -0
  79. package/dist/cliElectronCommands.js +106 -3
  80. package/dist/cliEvidenceCommands.js +4 -3
  81. package/dist/cliInvestigationCommands.js +2 -1
  82. package/dist/cliJsonInput.js +54 -0
  83. package/dist/cliPolicyCommands.js +2 -1
  84. package/dist/cliProcessCommands.js +3 -2
  85. package/dist/config.js +101 -2
  86. package/dist/contracts/applicationToolContracts.js +91 -0
  87. package/dist/contracts/artifactToolContracts.js +5 -9
  88. package/dist/contracts/browserToolContracts.js +14 -62
  89. package/dist/contracts/electronToolContracts.js +45 -19
  90. package/dist/contracts/enhancedInputs.js +2 -2
  91. package/dist/contracts/errorSchemas.js +1 -0
  92. package/dist/contracts/investigationExamples.js +0 -6
  93. package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
  94. package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
  95. package/dist/contracts/managedToolContracts.js +153 -0
  96. package/dist/contracts/managedWorkflowExamples.js +346 -0
  97. package/dist/contracts/managedWorkflowToolContracts.js +203 -0
  98. package/dist/contracts/nativeToolContracts.js +2 -6
  99. package/dist/contracts/promptContracts.js +18 -2
  100. package/dist/contracts/providerSelection.js +28 -0
  101. package/dist/contracts/sessionLifecycleInputs.js +2 -0
  102. package/dist/contracts/toolContractExamples.js +10 -25
  103. package/dist/contracts/toolContracts.js +69 -65
  104. package/dist/contracts/toolEffects.js +160 -0
  105. package/dist/contracts/toolOutputSchemas.js +152 -42
  106. package/dist/doctorRuntime.js +47 -0
  107. package/dist/domain/analysisProfile.js +78 -0
  108. package/dist/domain/analysisSnapshot.js +92 -46
  109. package/dist/domain/artifactComparison.js +3 -4
  110. package/dist/domain/artifactGraph.js +1 -0
  111. package/dist/domain/binaryTarget.js +4 -29
  112. package/dist/domain/browserObservation.js +3 -0
  113. package/dist/domain/bundleComparison.js +4 -4
  114. package/dist/domain/electronObservation.js +13 -0
  115. package/dist/domain/electronStaticAnalysis.js +9 -0
  116. package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
  117. package/dist/domain/electronStaticAnalysisIpc.js +171 -0
  118. package/dist/domain/electronStaticAnalysisNative.js +164 -0
  119. package/dist/domain/electronStaticAnalysisTypes.js +1 -0
  120. package/dist/domain/electronStaticAnalysisValues.js +103 -0
  121. package/dist/domain/errors.js +148 -7
  122. package/dist/domain/evidence.js +44 -5
  123. package/dist/domain/evidenceBundle.js +9 -7
  124. package/dist/domain/functionComparison.js +9 -3
  125. package/dist/domain/functionComparisonNormalization.js +37 -0
  126. package/dist/domain/functionComparisonSchemas.js +3 -8
  127. package/dist/domain/functionDossierEvidence.js +4 -1
  128. package/dist/domain/hopperValues.js +66 -9
  129. package/dist/domain/inputIssueProjection.js +52 -0
  130. package/dist/domain/investigationWorkspace.js +2 -1
  131. package/dist/domain/javascriptApplicationAnalysis.js +155 -0
  132. package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
  133. package/dist/domain/javascriptApplicationEvidenceSchemas.js +379 -0
  134. package/dist/domain/javascriptApplicationGraph.js +282 -0
  135. package/dist/domain/javascriptApplicationGraphSchemas.js +139 -0
  136. package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
  137. package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
  138. package/dist/domain/javascriptApplicationVersionItems.js +221 -0
  139. package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
  140. package/dist/domain/javascriptAstFingerprint.js +143 -0
  141. package/dist/domain/javascriptFeatureSeed.js +140 -0
  142. package/dist/domain/javascriptFeatureTrace.js +232 -0
  143. package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
  144. package/dist/domain/javascriptFeatureTraversal.js +88 -0
  145. package/dist/domain/javascriptNativeHandoff.js +85 -0
  146. package/dist/domain/javascriptReplay.js +361 -0
  147. package/dist/domain/javascriptRuntimeLoadState.js +106 -0
  148. package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
  149. package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
  150. package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
  151. package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
  152. package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
  153. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
  154. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
  155. package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
  156. package/dist/domain/javascriptStaticAnalysis.js +346 -0
  157. package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
  158. package/dist/domain/javascriptStaticAnalysisHelpers.js +274 -0
  159. package/dist/domain/javascriptStaticAnalysisState.js +23 -0
  160. package/dist/domain/javascriptStaticAnalysisTypes.js +1 -0
  161. package/dist/domain/managedApplicationGraph.js +556 -0
  162. package/dist/domain/managedArtifact.js +444 -0
  163. package/dist/domain/managedMemberComparison.js +565 -0
  164. package/dist/domain/managedNativeVerification.js +408 -0
  165. package/dist/domain/managedReconstruction.js +183 -0
  166. package/dist/domain/managedRuntimeCorrelation.js +175 -0
  167. package/dist/domain/reconstructionVerificationSchemas.js +1 -3
  168. package/dist/dotnet/ManagedArtifactInspector.js +318 -0
  169. package/dist/dotnet/ManagedMemberInspector.js +887 -0
  170. package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +368 -0
  172. package/dist/dotnet/ManagedMetadataLayout.js +322 -0
  173. package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
  174. package/dist/dotnet/ManagedPeReader.js +157 -0
  175. package/dist/dotnet/ManagedReaderFailure.js +10 -0
  176. package/dist/dotnet/ManagedStaticProvider.js +203 -0
  177. package/dist/generatedPackageMetadata.js +2 -2
  178. package/dist/ghidra/GhidraAnalysisProfile.js +37 -0
  179. package/dist/ghidra/GhidraClient.js +400 -0
  180. package/dist/ghidra/GhidraClientTypes.js +1 -0
  181. package/dist/ghidra/GhidraDefaults.js +20 -0
  182. package/dist/ghidra/GhidraDiagnostics.js +43 -0
  183. package/dist/ghidra/GhidraDoctor.js +59 -0
  184. package/dist/ghidra/GhidraFunctionValues.js +264 -0
  185. package/dist/ghidra/GhidraInstallation.js +239 -0
  186. package/dist/ghidra/GhidraInventoryValues.js +217 -0
  187. package/dist/ghidra/GhidraLauncher.js +139 -0
  188. package/dist/ghidra/GhidraProvider.js +366 -0
  189. package/dist/ghidra/GhidraRequestQueue.js +98 -0
  190. package/dist/ghidra/GhidraResponseBuffer.js +31 -0
  191. package/dist/ghidra/GhidraResponseRouter.js +24 -0
  192. package/dist/ghidra/GhidraSessionError.js +39 -0
  193. package/dist/ghidra/GhidraSessionValues.js +62 -0
  194. package/dist/ghidra/GhidraSocketConnection.js +46 -0
  195. package/dist/ghidra/protocol.js +69 -0
  196. package/dist/hopper/BridgeLauncher.js +19 -54
  197. package/dist/hopper/HopperAnalysisProfile.js +106 -0
  198. package/dist/hopper/HopperClient.js +154 -162
  199. package/dist/hopper/HopperProvider.js +99 -7
  200. package/dist/hopper/HopperSessionValues.js +27 -0
  201. package/dist/hopper/HopperSocketConnection.js +31 -0
  202. package/dist/main.js +14 -0
  203. package/dist/native/NativeMacOSProvider.js +3 -1
  204. package/dist/process/PendingOperations.js +59 -0
  205. package/dist/process/PrivateRuntimeRoot.js +33 -0
  206. package/dist/process/ProviderDeadline.js +95 -0
  207. package/dist/process/ProviderProcess.js +275 -0
  208. package/dist/replay/JavaScriptReplayWorker.js +286 -0
  209. package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
  210. package/dist/replay/LinuxRuntimeClosure.js +46 -0
  211. package/dist/replay/LinuxSeccompPolicy.js +40 -0
  212. package/dist/replay/ReplayWorkerProtocol.js +55 -0
  213. package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
  214. package/dist/server/createServer.js +53 -0
  215. package/dist/server/promptCompletion.js +9 -3
  216. package/dist/server/registerApplicationTools.js +115 -0
  217. package/dist/server/registerArtifactComparisonTool.js +16 -9
  218. package/dist/server/registerBrowserTools.js +33 -8
  219. package/dist/server/registerBundleComparisonTool.js +17 -20
  220. package/dist/server/registerElectronTools.js +45 -3
  221. package/dist/server/registerEnhancedTools.js +67 -19
  222. package/dist/server/registerEvidenceTools.js +5 -1
  223. package/dist/server/registerFunctionComparisonTool.js +14 -7
  224. package/dist/server/registerInvestigationTools.js +21 -17
  225. package/dist/server/registerManagedTools.js +6 -0
  226. package/dist/server/registerManagedWorkflowTools.js +277 -0
  227. package/dist/server/registerOfficialTools.js +11 -8
  228. package/dist/server/registerProcessComparisonTool.js +25 -47
  229. package/dist/server/registerSessionStatusTool.js +7 -7
  230. package/dist/server/registerSessionTools.js +57 -87
  231. package/dist/server/sessionEvidence.js +14 -19
  232. package/dist/server/toolInputValidation.js +77 -0
  233. package/dist/server/toolRegistrationOptions.js +79 -2
  234. package/dist/server/toolResult.js +25 -17
  235. package/package.json +17 -7
  236. package/scripts/rea.mjs +36 -2
  237. package/skills/rea-analysis/SKILL.md +59 -3
  238. /package/dist/{application → process}/ProcessOwnership.js +0 -0
@@ -28,6 +28,7 @@ const ANALYSIS_ERROR_TAGS = [
28
28
  "NoBinaryOpenError",
29
29
  "BinaryTargetError",
30
30
  "PermissionRequiredError",
31
+ "ReplayPlanStaleError",
31
32
  ];
32
33
  /** Base class for expected analysis, provider, and session failures. */
33
34
  export class AnalysisError extends Error {
@@ -46,10 +47,12 @@ export class AnalysisProtocolError extends AnalysisError {
46
47
  /** Caller input failed provider-neutral application parsing. */
47
48
  export class AnalysisInputError extends AnalysisError {
48
49
  operation;
50
+ issues;
49
51
  _tag = "AnalysisInputError";
50
- constructor(operation, options) {
52
+ constructor(operation, options, issues = []) {
51
53
  super(`Invalid analysis input for ${operation}`, options);
52
54
  this.operation = operation;
55
+ this.issues = issues;
53
56
  }
54
57
  }
55
58
  /** Provider output failed provider-neutral application parsing. */
@@ -96,24 +99,74 @@ export class AnalysisTimeoutError extends AnalysisError {
96
99
  this.timeoutMs = timeoutMs;
97
100
  }
98
101
  }
99
- /** No configured provider can satisfy a requested operation. */
102
+ /** No configured provider can satisfy a requested operation or target binding. */
100
103
  export class ProviderSelectionError extends AnalysisError {
101
- operation;
102
104
  _tag = "ProviderSelectionError";
103
- constructor(operation) {
104
- super(`No configured provider can execute ${operation}`);
105
+ operation;
106
+ reason;
107
+ requestedProviderId;
108
+ candidateIds;
109
+ rejections;
110
+ userMessage;
111
+ constructor(input) {
112
+ const legacy = typeof input === "string";
113
+ const options = legacy
114
+ ? {
115
+ operation: input,
116
+ reason: "provider_unavailable",
117
+ requestedProviderId: "auto",
118
+ candidateIds: [],
119
+ }
120
+ : input;
121
+ const operation = options.operation ?? "open_binary";
122
+ super(legacy
123
+ ? `No configured provider can execute ${operation}`
124
+ : providerSelectionDiagnostic(options));
105
125
  this.operation = operation;
126
+ this.reason = options.reason;
127
+ this.requestedProviderId = options.requestedProviderId;
128
+ this.candidateIds = [...options.candidateIds];
129
+ this.rejections = [...(options.rejections ?? [])].map((rejection) => ({
130
+ ...rejection,
131
+ diagnostics: structuredClone(rejection.diagnostics),
132
+ }));
133
+ this.userMessage = providerSelectionUserMessage(options);
106
134
  }
107
135
  }
136
+ const providerSelectionDiagnostic = (options) => {
137
+ const candidates = options.candidateIds.length === 0
138
+ ? "none"
139
+ : options.candidateIds.join(", ");
140
+ return `Analysis provider selection failed (${options.reason}) for ${options.requestedProviderId}; candidates: ${candidates}`;
141
+ };
142
+ const providerSelectionUserMessage = (options) => {
143
+ switch (options.reason) {
144
+ case "ambiguous":
145
+ return "Multiple analysis providers support this target. Choose one with provider_id, --provider, or REA_ANALYSIS_PROVIDER.";
146
+ case "unknown_provider":
147
+ return `The requested analysis provider is unknown. Choose one of: ${options.candidateIds.join(", ") || "no configured providers"}.`;
148
+ case "provider_unavailable":
149
+ return "The selected analysis provider is unavailable. Review the reported local diagnostics or run `rea doctor`, then retry.";
150
+ case "target_unsupported":
151
+ return "The selected analysis provider does not support this target. Choose another provider or open a supported extracted binary.";
152
+ case "invalid_options":
153
+ return "The analysis provider selection or open options are invalid. Correct them and try again.";
154
+ }
155
+ };
108
156
  /** A provider adapter failed outside its more precise typed variants. */
109
157
  export class ProviderAdapterError extends AnalysisError {
110
158
  providerId;
111
159
  operation;
112
160
  _tag = "ProviderAdapterError";
113
- constructor(providerId, operation, options) {
161
+ diagnostics;
162
+ constructor(providerId, operation, options = {}) {
114
163
  super(`Provider ${providerId} adapter failed during ${operation}`, options);
115
164
  this.providerId = providerId;
116
165
  this.operation = operation;
166
+ this.diagnostics =
167
+ options.diagnostics === undefined
168
+ ? undefined
169
+ : structuredClone(options.diagnostics);
117
170
  }
118
171
  }
119
172
  /** A bounded passive browser observation failed at its CDP boundary. */
@@ -146,6 +199,20 @@ export class ArtifactOperationError extends AnalysisError {
146
199
  export class EvidenceIntegrityError extends AnalysisError {
147
200
  _tag = "EvidenceIntegrityError";
148
201
  }
202
+ /** A session Evidence reference is missing or has the wrong semantic identity. */
203
+ export class EvidenceReferenceError extends EvidenceIntegrityError {
204
+ evidenceId;
205
+ reason;
206
+ expected;
207
+ actual;
208
+ constructor(evidenceId, reason, expected, actual) {
209
+ super(`Evidence reference ${reason}: ${evidenceId}`);
210
+ this.evidenceId = evidenceId;
211
+ this.reason = reason;
212
+ this.expected = expected;
213
+ this.actual = actual;
214
+ }
215
+ }
149
216
  /** A bounded evidence ledger cannot accept more records or serialized bytes. */
150
217
  export class EvidenceLimitError extends AnalysisError {
151
218
  limit;
@@ -279,6 +346,17 @@ export class PermissionRequiredError extends AnalysisError {
279
346
  this.restartRequired = restartRequired;
280
347
  }
281
348
  }
349
+ /** Approved replay commitment no longer matches the immediately rebuilt plan. */
350
+ export class ReplayPlanStaleError extends AnalysisError {
351
+ approvedDigest;
352
+ actualDigest;
353
+ _tag = "ReplayPlanStaleError";
354
+ constructor(approvedDigest, actualDigest) {
355
+ super("Controlled replay plan changed before execution");
356
+ this.approvedDigest = approvedDigest;
357
+ this.actualDigest = actualDigest;
358
+ }
359
+ }
282
360
  /** Project expected failures into exhaustive, secret-safe caller fields. */
283
361
  export const projectAnalysisError = (error) => {
284
362
  assertKnownTag(error._tag);
@@ -300,8 +378,13 @@ export const projectAnalysisError = (error) => {
300
378
  };
301
379
  };
302
380
  const errorCode = (error) => {
381
+ if (error instanceof ReplayPlanStaleError)
382
+ return "plan_stale";
303
383
  if (error instanceof PermissionRequiredError)
304
384
  return "permission_required";
385
+ if (error instanceof ProviderSelectionError &&
386
+ error.reason === "provider_unavailable")
387
+ return "provider_unavailable";
305
388
  if (error instanceof BrowserObservationError) {
306
389
  if (error.reason === "payload_limit")
307
390
  return "truncated";
@@ -400,9 +483,35 @@ const errorCode = (error) => {
400
483
  case "UnknownRegistryError":
401
484
  case "PermissionRequiredError":
402
485
  throw new TypeError("Unhandled specialized analysis error");
486
+ case "ReplayPlanStaleError":
487
+ throw new TypeError("Unhandled specialized replay error");
403
488
  }
404
489
  };
405
490
  const errorDetails = (error) => {
491
+ if (error instanceof AnalysisInputError && error.issues.length > 0)
492
+ return {
493
+ operation: error.operation,
494
+ issues: error.issues.map((issue) => ({
495
+ path: [...issue.path],
496
+ reason: issue.reason,
497
+ ...(issue.expected === undefined ? {} : { expected: issue.expected }),
498
+ ...(issue.minimum === undefined ? {} : { minimum: issue.minimum }),
499
+ ...(issue.maximum === undefined ? {} : { maximum: issue.maximum }),
500
+ })),
501
+ };
502
+ if (error instanceof EvidenceReferenceError)
503
+ return {
504
+ evidence_id: error.evidenceId,
505
+ reason: error.reason,
506
+ expected: error.expected,
507
+ actual: error.actual,
508
+ };
509
+ if (error instanceof ReplayPlanStaleError)
510
+ return {
511
+ approved_plan_digest: error.approvedDigest,
512
+ actual_plan_digest: error.actualDigest,
513
+ application_code_admitted: false,
514
+ };
406
515
  if (error instanceof PermissionRequiredError)
407
516
  return {
408
517
  capability: error.requested.capability,
@@ -441,8 +550,27 @@ const errorDetails = (error) => {
441
550
  return { operation: error.operation, reason: error.reason };
442
551
  if (error instanceof AnalysisCapabilityUnavailableError)
443
552
  return { provider_id: error.providerId, operation: error.operation };
553
+ if (error instanceof ProviderSelectionError)
554
+ return {
555
+ operation: error.operation,
556
+ selection_reason: error.reason,
557
+ requested_provider_id: error.requestedProviderId,
558
+ candidate_ids: [...error.candidateIds],
559
+ rejections: error.rejections.map((rejection) => ({
560
+ provider_id: rejection.providerId,
561
+ code: rejection.code,
562
+ reason: rejection.reason,
563
+ diagnostics: rejection.diagnostics,
564
+ })),
565
+ };
444
566
  if (error instanceof ProviderAdapterError)
445
- return { provider_id: error.providerId, operation: error.operation };
567
+ return {
568
+ provider_id: error.providerId,
569
+ operation: error.operation,
570
+ ...(error.diagnostics === undefined
571
+ ? {}
572
+ : { diagnostics: error.diagnostics }),
573
+ };
446
574
  if (error instanceof BrowserObservationError)
447
575
  return { operation: error.operation, reason: error.reason };
448
576
  if (error instanceof HopperRemoteError)
@@ -483,12 +611,17 @@ const missingScopeDetails = (scope) => ({
483
611
  ...(scope.mount === undefined ? {} : { mount: scope.mount }),
484
612
  });
485
613
  const RETRYABLE_CODES = new Set([
614
+ "invalid_request",
486
615
  "provider_timeout",
487
616
  "cancelled",
488
617
  "revision_conflict",
489
618
  "provider_unavailable",
490
619
  ]);
491
620
  const remediationAction = (error) => {
621
+ if (error instanceof AnalysisInputError)
622
+ return "Correct the listed arguments and retry.";
623
+ if (error instanceof ReplayPlanStaleError)
624
+ return "Review the rebuilt replay plan and explicitly approve its new digest.";
492
625
  if (error instanceof PermissionRequiredError)
493
626
  return error.elicitationSupported
494
627
  ? "Approve the exact missing scope, then retry the operation."
@@ -496,8 +629,13 @@ const remediationAction = (error) => {
496
629
  return userMessage(error);
497
630
  };
498
631
  const errorCategory = (error) => {
632
+ if (error instanceof ReplayPlanStaleError)
633
+ return "integrity_mismatch";
499
634
  if (error instanceof PermissionRequiredError)
500
635
  return "permission_required";
636
+ if (error instanceof ProviderSelectionError &&
637
+ error.reason === "provider_unavailable")
638
+ return "unavailable";
501
639
  if (error._tag === "ProcessCaptureError")
502
640
  return error.userCategory ?? "execution_failure";
503
641
  if (error instanceof BrowserObservationError) {
@@ -557,6 +695,8 @@ const STATIC_ERROR_CATEGORIES = {
557
695
  BinaryTargetError: "unavailable",
558
696
  };
559
697
  const userMessage = (error) => {
698
+ if (error instanceof ReplayPlanStaleError)
699
+ return "The controlled replay plan changed before execution. Refresh the current state and try again.";
560
700
  if (error instanceof PermissionRequiredError)
561
701
  return "This operation needs additional local permission. Review the requested scope and remediation.";
562
702
  if (error instanceof AnalysisInputError)
@@ -695,6 +835,7 @@ const KNOWN_ERROR_TAGS = {
695
835
  NoBinaryOpenError: true,
696
836
  BinaryTargetError: true,
697
837
  PermissionRequiredError: true,
838
+ ReplayPlanStaleError: true,
698
839
  };
699
840
  const assertKnownTag = (tag) => {
700
841
  if (KNOWN_ERROR_TAGS[tag] !== true)
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import canonicalize from "canonicalize";
3
3
  import { z } from "zod";
4
+ import { analysisProfileSchema, } from "./analysisProfile.js";
4
5
  import { jsonValueSchema } from "./jsonValue.js";
5
6
  const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
6
7
  /** Provider identity schema shared by evidence-bearing persistence formats. */
@@ -14,6 +15,7 @@ const subjectSchema = z.object({
14
15
  digest: z.object({ sha256: digestSchema }),
15
16
  format: z.enum([
16
17
  "hopper",
18
+ "analysis-database",
17
19
  "mach-o",
18
20
  "elf",
19
21
  "pe",
@@ -66,8 +68,8 @@ const executionEnvironmentSchema = z.object({
66
68
  architecture: z.string().min(1),
67
69
  isolation: z.enum(["none", "process", "container", "virtual-machine"]),
68
70
  });
69
- /** Strict, provider-neutral record for one successful public observation. */
70
- export const evidenceSchema = z.object({
71
+ const evidenceBaseSchema = z
72
+ .object({
71
73
  schema_version: z.literal(2),
72
74
  evidence_id: z.string().regex(/^ev_[a-f0-9]{64}$/u),
73
75
  subject: subjectSchema.nullable(),
@@ -83,7 +85,34 @@ export const evidenceSchema = z.object({
83
85
  limitations: z.array(z.string()),
84
86
  locations: z.array(evidenceLocationSchema),
85
87
  evidence_links: z.array(z.string().regex(/^ev_[a-f0-9]{64}$/u)),
88
+ })
89
+ .strict();
90
+ /** Object envelope used to specialize caller-visible Evidence result schemas. */
91
+ export const evidenceEnvelopeSchema = evidenceBaseSchema.extend({
92
+ analysis_profile: analysisProfileSchema.optional(),
86
93
  });
94
+ const validateAnalysisProfileProvider = (evidence, context) => {
95
+ const profile = evidence.analysis_profile;
96
+ if (profile !== undefined &&
97
+ (profile.provider.id !== evidence.provider.id ||
98
+ profile.provider.name !== evidence.provider.name ||
99
+ profile.provider.version !== evidence.provider.version))
100
+ context.addIssue({
101
+ code: "custom",
102
+ path: ["analysis_profile", "provider"],
103
+ message: "Analysis profile provider does not match Evidence provider",
104
+ });
105
+ };
106
+ /** Strict, provider-neutral record for one successful public observation. */
107
+ export const evidenceSchema = evidenceEnvelopeSchema.superRefine(validateAnalysisProfileProvider);
108
+ const profiledEvidenceSchema = evidenceBaseSchema
109
+ .extend({ analysis_profile: analysisProfileSchema })
110
+ .superRefine(validateAnalysisProfileProvider);
111
+ /** JSON-exact union used where Evidence participates in larger schemas. */
112
+ export const evidenceRecordSchema = z.union([
113
+ profiledEvidenceSchema,
114
+ evidenceBaseSchema,
115
+ ]);
87
116
  const sha256 = (value) => createHash("sha256").update(value).digest("hex");
88
117
  /** Serialize JSON according to RFC 8785 JSON Canonicalization Scheme. */
89
118
  const canonicalJson = (value) => {
@@ -102,6 +131,9 @@ const semanticProjection = (evidence) => ({
102
131
  architecture: evidence.subject.architecture,
103
132
  },
104
133
  provider: evidence.provider,
134
+ ...(!("analysis_profile" in evidence)
135
+ ? {}
136
+ : { analysis_profile: evidence.analysis_profile }),
105
137
  predicate_type: evidence.predicate_type,
106
138
  operation: evidence.operation,
107
139
  parameters: evidence.parameters,
@@ -123,7 +155,7 @@ export const parseEvidence = (input) => {
123
155
  "schema_version" in input &&
124
156
  input.schema_version !== 2)
125
157
  throw new TypeError(`Unsupported evidence schema_version ${String(input.schema_version)}; Evidence v1 is not accepted. Produce Evidence v2.`);
126
- const evidence = evidenceSchema.parse(input);
158
+ const evidence = evidenceRecordSchema.parse(input);
127
159
  const { evidence_id: evidenceId, ...withoutId } = evidence;
128
160
  if (computeEvidenceId(withoutId) !== evidenceId)
129
161
  throw new TypeError("Evidence semantic identifier does not match its record");
@@ -154,6 +186,9 @@ export const createEvidence = (target, provider, observation) => {
154
186
  name: provider.name,
155
187
  version: provider.version,
156
188
  },
189
+ ...(observation.analysisProfile === undefined
190
+ ? {}
191
+ : { analysis_profile: observation.analysisProfile }),
157
192
  predicate_type: observation.predicateType ?? "rea.analysis/v2",
158
193
  operation: observation.operation,
159
194
  parameters: observation.parameters,
@@ -171,9 +206,13 @@ export const createEvidence = (target, provider, observation) => {
171
206
  locations: [...(observation.locations ?? [])],
172
207
  evidence_links: [...(observation.evidenceLinks ?? [])],
173
208
  };
174
- return evidenceSchema.parse({
209
+ const normalized = evidenceRecordSchema.parse({
175
210
  ...semantic,
176
- evidence_id: `ev_${sha256(canonicalJson(semantic))}`,
211
+ evidence_id: `ev_${"0".repeat(64)}`,
177
212
  subject,
178
213
  });
214
+ return parseEvidence({
215
+ ...normalized,
216
+ evidence_id: computeEvidenceId(normalized),
217
+ });
179
218
  };
@@ -1,21 +1,23 @@
1
1
  import { z } from "zod";
2
2
  import canonicalize from "canonicalize";
3
- import { evidenceSchema, parseEvidence } from "./evidence.js";
3
+ import { evidenceEnvelopeSchema, evidenceRecordSchema, parseEvidence, providerSchema, } from "./evidence.js";
4
4
  import { residualUnknownSchema, } from "./residualUnknown.js";
5
- const artifactManifestSchema = evidenceSchema.shape.subject.unwrap().pick({
5
+ const artifactManifestSchema = evidenceEnvelopeSchema.shape.subject
6
+ .unwrap()
7
+ .pick({
6
8
  digest: true,
7
9
  format: true,
8
10
  architecture: true,
9
11
  });
10
- const providerManifestSchema = evidenceSchema.shape.provider;
11
- const environmentManifestSchema = evidenceSchema.shape.environment.unwrap();
12
+ const providerManifestSchema = providerSchema;
13
+ const environmentManifestSchema = evidenceEnvelopeSchema.shape.environment.unwrap();
12
14
  const scenarioManifestSchema = z.object({
13
- evidence_id: evidenceSchema.shape.evidence_id,
15
+ evidence_id: evidenceEnvelopeSchema.shape.evidence_id,
14
16
  operation: z.string().min(1),
15
17
  authority: z.literal("controlled-replay"),
16
18
  });
17
19
  const captureManifestSchema = z.object({
18
- evidence_id: evidenceSchema.shape.evidence_id,
20
+ evidence_id: evidenceEnvelopeSchema.shape.evidence_id,
19
21
  predicate_type: z.string().min(1),
20
22
  });
21
23
  export const evidenceBundleSchema = z.object({
@@ -26,7 +28,7 @@ export const evidenceBundleSchema = z.object({
26
28
  scenarios: z.array(scenarioManifestSchema),
27
29
  captures: z.array(captureManifestSchema),
28
30
  unknowns: z.array(residualUnknownSchema),
29
- records: z.array(evidenceSchema),
31
+ records: z.array(evidenceRecordSchema),
30
32
  });
31
33
  /** Project records into a deterministic bundle whose order has no semantics. */
32
34
  export const createEvidenceBundle = (records, unknowns = []) => {
@@ -1,6 +1,6 @@
1
1
  import { diffLines } from "diff";
2
2
  import { parseFunctionEvidence, } from "./functionDossierEvidence.js";
3
- import { commentProjection, combineCoverage, functionMatch, identityProjection, isAutoName, normalizeCfg, project, referenceProjection, sorted, stringAndNameProjection, } from "./functionComparisonNormalization.js";
3
+ import { commentProjection, combineCoverage, functionMatch, identityProjection, isAutoName, normalizeCfg, project, referenceKindProjection, referenceProjection, sorted, stringAndNameProjection, } from "./functionComparisonNormalization.js";
4
4
  import { functionComparisonResultSchema, } from "./functionComparisonSchemas.js";
5
5
  import { canonicalJson, dimensionResult, overallStatus, summarize, unresolvedDimension as buildUnresolvedDimension, } from "./functionComparisonResults.js";
6
6
  export { functionComparisonInputSchema, functionComparisonResultSchema, } from "./functionComparisonSchemas.js";
@@ -141,10 +141,16 @@ const compareReferences = (left, right, links, providersDiffer) => {
141
141
  rightCount: rightProjection.length,
142
142
  textDelta: null,
143
143
  limitations: [
144
- "Reference endpoints differ; exact reference kinds remain unavailable.",
144
+ "Reference endpoints differ independently of provider reference-kind metadata.",
145
145
  ],
146
146
  });
147
- return unresolvedDimension("references", "unknown", links, leftProjection.length, rightProjection.length, ["Reference kinds are unavailable in the current dossier contract."]);
147
+ const leftKinds = referenceKindProjection(left);
148
+ const rightKinds = referenceKindProjection(right);
149
+ if (leftKinds !== null && rightKinds !== null)
150
+ return compareValues("references", leftKinds, rightKinds, links, true, true, providersDiffer);
151
+ return unresolvedDimension("references", "unknown", links, leftProjection.length, rightProjection.length, [
152
+ "At least one provider did not expose reference kinds, so equal endpoints do not prove equal edge semantics.",
153
+ ]);
148
154
  };
149
155
  const compareIdentity = (left, right, links, providersDiffer) => {
150
156
  if (isAutoName(left.procedure.name) || isAutoName(right.procedure.name))
@@ -60,6 +60,43 @@ export const referenceProjection = (snapshot) => sorted([
60
60
  target: item.target_procedure?.name ?? null,
61
61
  })),
62
62
  ]);
63
+ export const referenceKindProjection = (snapshot) => {
64
+ const edges = [
65
+ ...snapshot.collections.incoming_references.items.map((edge) => ({
66
+ direction: "in",
67
+ edge,
68
+ })),
69
+ ...snapshot.collections.outgoing_references.items.map((edge) => ({
70
+ direction: "out",
71
+ edge,
72
+ })),
73
+ ];
74
+ if (edges.some(({ edge }) => !edge.kind.available))
75
+ return null;
76
+ return sorted(edges.map(({ direction, edge }) => {
77
+ if (!edge.kind.available)
78
+ throw new TypeError("Reference-kind availability changed during projection");
79
+ return {
80
+ direction,
81
+ source: edge.source_procedure?.name ?? null,
82
+ target: edge.target_procedure?.name ?? null,
83
+ type: edge.kind.type,
84
+ flow: edge.kind.flow,
85
+ call: edge.kind.call,
86
+ jump: edge.kind.jump,
87
+ data: edge.kind.data,
88
+ read: edge.kind.read,
89
+ write: edge.kind.write,
90
+ indirect: edge.kind.indirect,
91
+ computed: edge.kind.computed,
92
+ conditional: edge.kind.conditional,
93
+ terminal: edge.kind.terminal,
94
+ primary: edge.kind.primary,
95
+ operand_index: edge.kind.operand_index,
96
+ external: edge.kind.external,
97
+ };
98
+ }));
99
+ };
63
100
  export const commentProjection = (snapshot) => {
64
101
  const values = snapshot.collections.comments.items.map(({ address, kind, text }) => ({
65
102
  offset: relativeAddress(address, snapshot.procedure.address),
@@ -1,9 +1,4 @@
1
1
  import { z } from "zod";
2
- import { evidenceSchema } from "./evidence.js";
3
- const evidenceInput = z.union([
4
- evidenceSchema,
5
- z.array(evidenceSchema).min(1).max(100),
6
- ]);
7
2
  const comparisonStatusSchema = z.enum([
8
3
  "unchanged",
9
4
  "added",
@@ -25,9 +20,9 @@ const dimensionNameSchema = z.enum([
25
20
  const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
26
21
  const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
27
22
  /** Strict bounded input for explicit function-to-function comparison. */
28
- export const functionComparisonInputSchema = z.object({
29
- left: evidenceInput,
30
- right: evidenceInput,
23
+ export const functionComparisonInputSchema = z.strictObject({
24
+ left_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
25
+ right_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
31
26
  offset: z.number().int().min(0).default(0),
32
27
  limit: z.number().int().min(1).max(100).default(100),
33
28
  unknown_registry_approved: z.literal(true).optional(),
@@ -47,7 +47,10 @@ export const parseFunctionEvidence = (input) => {
47
47
  collections,
48
48
  instructionScan: first.dossier.instruction_scan,
49
49
  limitations: [
50
- ...new Set(pages.flatMap(({ evidence }) => evidence.limitations)),
50
+ ...new Set(pages.flatMap(({ evidence, dossier }) => [
51
+ ...evidence.limitations,
52
+ ...dossier.limitations,
53
+ ])),
51
54
  ].sort((left, right) => left.localeCompare(right)),
52
55
  };
53
56
  };
@@ -1,8 +1,27 @@
1
1
  import { z } from "zod";
2
2
  import { AnalysisOutputError, HopperProtocolError } from "./errors.js";
3
3
  import { err, ok } from "./result.js";
4
- /** Return the non-negative byte distance between hexadecimal addresses. */
5
- export const addressDistance = (start, end) => Math.max(0, Number.parseInt(end, 16) - Number.parseInt(start, 16));
4
+ /** Return a bounded byte distance between canonical addresses in one space. */
5
+ export const addressDistance = (start, end) => {
6
+ const left = addressCoordinate(start);
7
+ const right = addressCoordinate(end);
8
+ if (left === null || right === null || left.space !== right.space)
9
+ return 0;
10
+ const distance = right.offset - left.offset;
11
+ if (distance <= 0n)
12
+ return 0;
13
+ return Number(distance > BigInt(Number.MAX_SAFE_INTEGER)
14
+ ? BigInt(Number.MAX_SAFE_INTEGER)
15
+ : distance);
16
+ };
17
+ const addressCoordinate = (value) => {
18
+ const separator = value.lastIndexOf(":0x");
19
+ const space = separator < 0 ? "default" : value.slice(0, separator);
20
+ const offset = separator < 0 ? value : value.slice(separator + 1);
21
+ if (!/^0x[0-9a-f]+$/u.test(offset))
22
+ return null;
23
+ return { space, offset: BigInt(offset) };
24
+ };
6
25
  const procedureMapSchema = z.record(z.string(), z.string());
7
26
  const addressedNamesSchema = z.array(z.object({ address: z.string(), name: z.string() }));
8
27
  const addressedNameMapSchema = z.record(z.string(), z.string());
@@ -39,16 +58,28 @@ const addressedPageSchema = z
39
58
  });
40
59
  }
41
60
  });
42
- const unavailableSchema = z
61
+ const unavailableAnalysisFactSchema = z
43
62
  .object({ available: z.literal(false), reason: z.string() })
44
63
  .strict();
45
- const procedureIdentitySchema = z
46
- .object({ address: z.string(), name: z.string() })
64
+ export const procedureClassificationSchema = z
65
+ .object({
66
+ external: z.boolean(),
67
+ thunk: z.boolean(),
68
+ thunk_target: z.string().nullable(),
69
+ provenance: z.string().min(1),
70
+ })
71
+ .strict();
72
+ export const procedureIdentitySchema = z
73
+ .object({
74
+ address: z.string(),
75
+ name: z.string(),
76
+ classification: procedureClassificationSchema.nullable().default(null),
77
+ })
47
78
  .strict();
48
- const localVariableSchema = z
79
+ export const localVariableSchema = z
49
80
  .object({
50
81
  description: z.string(),
51
- provenance: z.literal("hopper-public-python-api"),
82
+ provenance: z.string().min(1),
52
83
  })
53
84
  .strict();
54
85
  const boundedSchema = (item) => z
@@ -83,13 +114,37 @@ const boundedSchema = (item) => z
83
114
  });
84
115
  }
85
116
  });
117
+ const availableReferenceKindSchema = z
118
+ .object({
119
+ available: z.literal(true),
120
+ provenance: z.string().min(1),
121
+ type: z.string(),
122
+ flow: z.boolean(),
123
+ call: z.boolean(),
124
+ jump: z.boolean(),
125
+ data: z.boolean(),
126
+ read: z.boolean(),
127
+ write: z.boolean(),
128
+ indirect: z.boolean(),
129
+ computed: z.boolean(),
130
+ conditional: z.boolean(),
131
+ terminal: z.boolean(),
132
+ primary: z.boolean(),
133
+ operand_index: z.number().int(),
134
+ external: z.boolean(),
135
+ })
136
+ .strict();
137
+ export const referenceKindSchema = z.discriminatedUnion("available", [
138
+ unavailableAnalysisFactSchema,
139
+ availableReferenceKindSchema,
140
+ ]);
86
141
  const referenceEdgeSchema = z
87
142
  .object({
88
143
  source_address: z.string(),
89
144
  target_address: z.string(),
90
145
  source_procedure: procedureIdentitySchema.nullable(),
91
146
  target_procedure: procedureIdentitySchema.nullable(),
92
- kind: unavailableSchema,
147
+ kind: referenceKindSchema,
93
148
  })
94
149
  .strict();
95
150
  export const functionDossierSchema = z
@@ -98,6 +153,7 @@ export const functionDossierSchema = z
98
153
  .object({
99
154
  address: z.string(),
100
155
  name: z.string(),
156
+ classification: procedureClassificationSchema.nullable().default(null),
101
157
  signature: z.string().nullable(),
102
158
  locals: z.array(localVariableSchema),
103
159
  })
@@ -170,9 +226,10 @@ export const functionDossierSchema = z
170
226
  instruction_scan: z
171
227
  .object({ scanned: z.number().int().min(0), truncated: z.boolean() })
172
228
  .strict(),
229
+ limitations: z.array(z.string()).default([]),
173
230
  })
174
231
  .strict();
175
- /** Strictly parse a complete Hopper function dossier at the provider boundary. */
232
+ /** Strictly parse a complete provider-neutral function dossier. */
176
233
  export const parseFunctionDossier = (value) => {
177
234
  const parsed = functionDossierSchema.safeParse(value);
178
235
  return parsed.success