rea-agents 1.7.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 (156) hide show
  1. package/README.md +38 -31
  2. package/dist/application/CapabilityInventory.js +18 -0
  3. package/dist/application/CrossVersionInvestigation.js +10 -6
  4. package/dist/application/Doctor.js +56 -6
  5. package/dist/application/ElectronBoundaryAnalysis.js +96 -0
  6. package/dist/application/ElectronBoundaryGraph.js +9 -0
  7. package/dist/application/ElectronBoundaryGraphContext.js +62 -0
  8. package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
  9. package/dist/application/ElectronBoundaryGraphNative.js +86 -0
  10. package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
  11. package/dist/application/EnhancedTools.js +38 -13
  12. package/dist/application/InvestigationProviders.js +30 -0
  13. package/dist/application/JavaScriptApplicationEvidence.js +26 -0
  14. package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
  15. package/dist/application/JavaScriptApplicationService.js +74 -0
  16. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
  17. package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
  18. package/dist/application/JavaScriptArtifactAnalysis.js +7 -1
  19. package/dist/application/JavaScriptArtifactGraphBuilder.js +59 -16
  20. package/dist/application/JavaScriptArtifactGraphEvidence.js +24 -14
  21. package/dist/application/JavaScriptArtifactReconstruction.js +3 -0
  22. package/dist/application/JavaScriptArtifactReconstructionInput.js +2 -83
  23. package/dist/application/JavaScriptReplayPlanning.js +201 -0
  24. package/dist/application/JavaScriptReplayService.js +266 -0
  25. package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
  26. package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
  27. package/dist/application/ManagedApplicationGraphService.js +55 -0
  28. package/dist/application/ManagedMemberComparisonService.js +122 -0
  29. package/dist/application/ManagedNativeVerificationService.js +39 -0
  30. package/dist/application/ManagedReconstructionService.js +58 -0
  31. package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
  32. package/dist/application/PermissionAuthority.js +1 -1
  33. package/dist/application/ProcessSampling.js +15 -11
  34. package/dist/application/ProjectPermissionStore.js +1 -0
  35. package/dist/application/runtime.js +2 -0
  36. package/dist/browser/CdpCaptureEvents.js +49 -0
  37. package/dist/browser/CdpElectronInspection.js +106 -162
  38. package/dist/browser/CdpElectronProvider.js +5 -1
  39. package/dist/browser/CdpElectronScriptEvents.js +69 -0
  40. package/dist/browser/CdpElectronScripts.js +94 -0
  41. package/dist/browser/CdpElectronWorkers.js +56 -0
  42. package/dist/browser/CdpPageCapture.js +8 -4
  43. package/dist/catalogIdentity.js +4 -1
  44. package/dist/cli.js +388 -0
  45. package/dist/cliApplicationCommands.js +62 -0
  46. package/dist/cliCommandNames.js +13 -0
  47. package/dist/cliElectronCommands.js +103 -1
  48. package/dist/cliJsonInput.js +54 -0
  49. package/dist/config.js +83 -0
  50. package/dist/contracts/applicationToolContracts.js +91 -0
  51. package/dist/contracts/artifactToolContracts.js +5 -9
  52. package/dist/contracts/browserToolContracts.js +14 -62
  53. package/dist/contracts/electronToolContracts.js +45 -19
  54. package/dist/contracts/errorSchemas.js +1 -0
  55. package/dist/contracts/investigationExamples.js +0 -6
  56. package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
  57. package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
  58. package/dist/contracts/managedToolContracts.js +153 -0
  59. package/dist/contracts/managedWorkflowExamples.js +346 -0
  60. package/dist/contracts/managedWorkflowToolContracts.js +203 -0
  61. package/dist/contracts/nativeToolContracts.js +2 -6
  62. package/dist/contracts/promptContracts.js +18 -2
  63. package/dist/contracts/toolContractExamples.js +10 -25
  64. package/dist/contracts/toolContracts.js +47 -45
  65. package/dist/contracts/toolEffects.js +160 -0
  66. package/dist/contracts/toolOutputSchemas.js +46 -31
  67. package/dist/doctorRuntime.js +42 -1
  68. package/dist/domain/artifactComparison.js +3 -4
  69. package/dist/domain/browserObservation.js +3 -0
  70. package/dist/domain/bundleComparison.js +4 -4
  71. package/dist/domain/electronObservation.js +13 -0
  72. package/dist/domain/electronStaticAnalysis.js +9 -0
  73. package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
  74. package/dist/domain/electronStaticAnalysisIpc.js +171 -0
  75. package/dist/domain/electronStaticAnalysisNative.js +164 -0
  76. package/dist/domain/electronStaticAnalysisTypes.js +1 -0
  77. package/dist/domain/electronStaticAnalysisValues.js +103 -0
  78. package/dist/domain/errors.js +67 -1
  79. package/dist/domain/functionComparisonSchemas.js +3 -8
  80. package/dist/domain/inputIssueProjection.js +52 -0
  81. package/dist/domain/javascriptApplicationAnalysis.js +155 -0
  82. package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
  83. package/dist/domain/javascriptApplicationEvidenceSchemas.js +18 -0
  84. package/dist/domain/javascriptApplicationGraph.js +2 -1
  85. package/dist/domain/javascriptApplicationGraphSchemas.js +7 -0
  86. package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
  87. package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
  88. package/dist/domain/javascriptApplicationVersionItems.js +221 -0
  89. package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
  90. package/dist/domain/javascriptFeatureSeed.js +140 -0
  91. package/dist/domain/javascriptFeatureTrace.js +232 -0
  92. package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
  93. package/dist/domain/javascriptFeatureTraversal.js +88 -0
  94. package/dist/domain/javascriptNativeHandoff.js +85 -0
  95. package/dist/domain/javascriptReplay.js +361 -0
  96. package/dist/domain/javascriptRuntimeLoadState.js +106 -0
  97. package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
  98. package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
  99. package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
  100. package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
  101. package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
  102. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
  103. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
  104. package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
  105. package/dist/domain/javascriptStaticAnalysis.js +31 -43
  106. package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
  107. package/dist/domain/javascriptStaticAnalysisHelpers.js +10 -0
  108. package/dist/domain/javascriptStaticAnalysisState.js +6 -0
  109. package/dist/domain/managedApplicationGraph.js +556 -0
  110. package/dist/domain/managedArtifact.js +444 -0
  111. package/dist/domain/managedMemberComparison.js +565 -0
  112. package/dist/domain/managedNativeVerification.js +408 -0
  113. package/dist/domain/managedReconstruction.js +183 -0
  114. package/dist/domain/managedRuntimeCorrelation.js +175 -0
  115. package/dist/domain/reconstructionVerificationSchemas.js +1 -3
  116. package/dist/dotnet/ManagedArtifactInspector.js +318 -0
  117. package/dist/dotnet/ManagedMemberInspector.js +887 -0
  118. package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
  119. package/dist/dotnet/ManagedMetadataInventory.js +368 -0
  120. package/dist/dotnet/ManagedMetadataLayout.js +322 -0
  121. package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
  122. package/dist/dotnet/ManagedPeReader.js +157 -0
  123. package/dist/dotnet/ManagedReaderFailure.js +10 -0
  124. package/dist/dotnet/ManagedStaticProvider.js +203 -0
  125. package/dist/generatedPackageMetadata.js +2 -2
  126. package/dist/ghidra/GhidraProvider.js +6 -0
  127. package/dist/main.js +14 -0
  128. package/dist/replay/JavaScriptReplayWorker.js +286 -0
  129. package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
  130. package/dist/replay/LinuxRuntimeClosure.js +46 -0
  131. package/dist/replay/LinuxSeccompPolicy.js +40 -0
  132. package/dist/replay/ReplayWorkerProtocol.js +55 -0
  133. package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
  134. package/dist/server/createServer.js +52 -0
  135. package/dist/server/registerApplicationTools.js +115 -0
  136. package/dist/server/registerArtifactComparisonTool.js +16 -9
  137. package/dist/server/registerBrowserTools.js +33 -8
  138. package/dist/server/registerBundleComparisonTool.js +17 -20
  139. package/dist/server/registerElectronTools.js +45 -3
  140. package/dist/server/registerEnhancedTools.js +58 -13
  141. package/dist/server/registerEvidenceTools.js +5 -1
  142. package/dist/server/registerFunctionComparisonTool.js +14 -7
  143. package/dist/server/registerInvestigationTools.js +21 -17
  144. package/dist/server/registerManagedTools.js +6 -0
  145. package/dist/server/registerManagedWorkflowTools.js +277 -0
  146. package/dist/server/registerOfficialTools.js +8 -8
  147. package/dist/server/registerProcessComparisonTool.js +25 -47
  148. package/dist/server/registerSessionStatusTool.js +7 -7
  149. package/dist/server/registerSessionTools.js +51 -86
  150. package/dist/server/sessionEvidence.js +14 -19
  151. package/dist/server/toolInputValidation.js +77 -0
  152. package/dist/server/toolRegistrationOptions.js +79 -2
  153. package/dist/server/toolResult.js +25 -17
  154. package/package.json +12 -4
  155. package/scripts/rea.mjs +1 -1
  156. package/skills/rea-analysis/SKILL.md +59 -3
package/README.md CHANGED
@@ -10,11 +10,11 @@
10
10
 
11
11
  [![npm version](https://img.shields.io/npm/v/rea-agents?style=flat-square&color=cb3837)](https://www.npmjs.com/package/rea-agents)
12
12
  [![CI](https://img.shields.io/github/actions/workflow/status/morluto/rea/ci.yml?branch=main&style=flat-square&label=CI)](https://github.com/morluto/rea/actions/workflows/ci.yml)
13
- [![78 MCP tools](https://img.shields.io/badge/MCP_tools-78-5c4ee5?style=flat-square)](#78-tools-for-investigation)
13
+ [![MCP tool catalog](https://img.shields.io/badge/MCP-tool_catalog-5c4ee5?style=flat-square)](#tool-catalog-for-investigation)
14
14
  [![Node.js 22+](https://img.shields.io/badge/Node.js-22.19%2B-339933?style=flat-square&logo=nodedotjs&logoColor=white)](https://nodejs.org/)
15
15
  [![MIT license](https://img.shields.io/badge/license-MIT-f4c430?style=flat-square)](LICENSE)
16
16
 
17
- [Quick start](#quick-start) · [Current status](#current-status) · [Investigation model](#the-investigation-model) · [78 tools](#78-tools-for-investigation) · [Roadmap](#roadmap) · [How it works](#how-it-works)
17
+ [Quick start](#quick-start) · [Current status](#current-status) · [Investigation model](#the-investigation-model) · [Tool catalog](#tool-catalog-for-investigation) · [Roadmap](#roadmap) · [How it works](#how-it-works)
18
18
 
19
19
  <br />
20
20
 
@@ -30,7 +30,7 @@
30
30
 
31
31
  See a feature in an app that you want in your own product? Give the app to your agent—even without its source code. With REA, the agent can investigate the feature, explain how it works, show its evidence, and build a version adapted to your stack and requirements.
32
32
 
33
- REA gives agents one consistent way to investigate software. Today that includes deep native analysis and function dossiers through Hopper or bring-your-own Ghidra on Linux, reproducible Evidence v2 records, controlled process capture, passive website and Electron observation, bounded JavaScript/source-map reconstruction, and a versioned domain graph for connecting JavaScript application layers without confusing static inference with runtime observation. The longer-term toolkit extends the same agent workflow to APIs, protocols, mobile artifacts, firmware, richer runtime behavior, and differences between versions.
33
+ REA gives agents one consistent way to investigate software. Today that includes deep native analysis and function dossiers through Hopper or bring-your-own Ghidra on Linux, execution-free managed PE/CLI triage, reproducible Evidence v2 records, controlled process capture, passive website and Electron observation, bounded JavaScript/source-map reconstruction, and a versioned domain graph for connecting JavaScript application layers without confusing static inference with runtime observation. The longer-term toolkit extends the same agent workflow to APIs, protocols, mobile artifacts, firmware, richer runtime behavior, and differences between versions.
34
34
 
35
35
  Reverse engineering normally makes the operator choose a tool, learn its API, move evidence between programs, and decide what to inspect next. REA gives that work to the agent through commands, skills, structured results, and repeatable investigation workflows.
36
36
 
@@ -219,15 +219,16 @@ Uninstall preserves Hopper, Node.js, evidence, captures, external evidence roots
219
219
 
220
220
  ### CLI or agent?
221
221
 
222
- | If you want to… | Use |
223
- | --------------------------------------------------------------- | ------------------------------------------------------------------------- |
224
- | Ask an agent to investigate an app and build a feature | Install the skill, then talk to your agent |
225
- | Inspect or decompile one part of an app from the Terminal | `rea analyze` or `rea decompile` |
226
- | Validate, canonicalize, or compare Evidence v2 bundles | `rea evidence-import`, `rea evidence-export`, or `rea compare` |
227
- | Run or resume a persistent two-version artifact analysis | `rea investigate-versions` |
228
- | Reuse immutable analysis results without relaunching a provider | Pass `--snapshot /approved/path/analysis.json` to a deep-analysis command |
229
- | Import source as historical reference | `rea import-reference-source` |
230
- | Capture or compare controlled process behavior | `rea capture-process` or `rea compare-process-captures` |
222
+ | If you want to… | Use |
223
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
224
+ | Ask an agent to investigate an app and build a feature | Install the skill, then talk to your agent |
225
+ | Inspect or decompile one part of an app from the Terminal | `rea analyze` or `rea decompile` |
226
+ | Validate, canonicalize, or compare Evidence v2 bundles | `rea evidence-import`, `rea evidence-export`, or `rea compare` |
227
+ | Run or resume a persistent two-version artifact analysis | `rea investigate-versions` |
228
+ | Map a local JavaScript/Electron application without executing it | `rea analyze-javascript-application` |
229
+ | Reuse immutable analysis results without relaunching a provider | Pass `--snapshot /approved/path/analysis.json` to a deep-analysis command |
230
+ | Import source as historical reference | `rea import-reference-source` |
231
+ | Capture or compare controlled process behavior | `rea capture-process` or `rea compare-process-captures` |
231
232
 
232
233
  Filesystem evidence commands and MCP file tools are disabled until the operator approves absolute roots:
233
234
 
@@ -238,6 +239,7 @@ rea evidence-import /absolute/path/to/evidence/bundle.json
238
239
  rea evidence-export /absolute/path/to/evidence/bundle.json /absolute/path/to/evidence/canonical.json
239
240
  rea compare /absolute/path/to/evidence/left.json /absolute/path/to/evidence/right.json
240
241
  rea investigate-versions /absolute/path/to/releases/v1 /absolute/path/to/releases/v2 /absolute/path/to/evidence/releases.json --yes --workspace-name releases
242
+ rea analyze-javascript-application /absolute/path/to/releases/app.asar --approved --json
241
243
  ```
242
244
 
243
245
  `investigate-versions` inventories both versions, checkpoints their observed
@@ -318,17 +320,19 @@ REA handles the app analysis in steps 1–5. The agent performs step 6 with its
318
320
  - Analyze Swift and Objective-C metadata without manually untangling every mangled symbol.
319
321
  - Leave names, comments, and bookmarks in Hopper so human and agent analysis reinforce each other.
320
322
 
321
- ## 78 tools for investigation
323
+ ## Tool catalog for investigation
322
324
 
323
- | Tool family | Count | Examples |
324
- | ------------------------- | ----: | -------------------------------------------------------------------------------------------------------------------------------- |
325
- | Native inspection | 33 | procedures, pseudocode, assembly, strings, names, segments, callers, callees, xrefs, annotations |
326
- | Investigation workflows | 10 | `binary_overview`, `analyze_function`, `batch_decompile`, `trace_feature`, call graphs, Swift and Objective-C discovery |
327
- | Native macOS utilities | 5 | Mach-O metadata, code signatures, plists, architectures, Swift demangling; Hopper-free and provenance-bearing |
328
- | Artifact graph | 2 | deterministic directory, ZIP/APK/IPA, and ASAR inventory; explicitly selected extraction into an absent owned tree |
329
- | Browser observation | 8 | exact-origin CDP capture, bundle and source-map analysis, WebMCP discovery, session timelines, capture diff, and visual evidence |
330
- | Electron observation | 2 | canonical-root-confined file-page discovery and passive DOM, resource, and optionally approved script-source inspection |
331
- | Workspace and observation | 18 | target lifecycle, Evidence v2 bundles, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
325
+ | Tool family | Count | Examples |
326
+ | ------------------------- | ----: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
327
+ | Native inspection | 33 | procedures, pseudocode, assembly, strings, names, segments, callers, callees, xrefs, annotations |
328
+ | Investigation workflows | 10 | `binary_overview`, `analyze_function`, `batch_decompile`, `trace_feature`, call graphs, Swift and Objective-C discovery |
329
+ | Native macOS utilities | 5 | Mach-O metadata, code signatures, plists, architectures, Swift demangling; Hopper-free and provenance-bearing |
330
+ | Artifact graph | 2 | deterministic directory, ZIP/APK/IPA, and ASAR inventory; explicitly selected extraction into an absent owned tree |
331
+ | Managed PE/CLI | 8 | PE/CLI identity, metadata members, CIL hashes, P/Invoke/native-boundary declarations and verification, application-graph projection, decompiler reconstruction import, token remapping, runtime-correlation plans, and version comparison |
332
+ | Browser observation | 8 | exact-origin CDP capture, bundle and source-map analysis, WebMCP discovery, session timelines, capture diff, and visual evidence |
333
+ | Electron analysis | 4 | passive root-confined observation, bounded static application mapping, and evidence-backed static/runtime reconciliation |
334
+ | Application workflows | 3 | bounded cross-layer traces, unique-only version matching, and approved Linux-isolated extracted-module replay |
335
+ | Workspace and observation | 18 | target lifecycle, Evidence v2 bundles, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
332
336
 
333
337
  The public interface describes what the agent is trying to learn. Providers decide how to answer. macOS utilities handle common semantic inspection without launching Hopper; Hopper handles deeper native analysis; the process harness implements controlled behavioral capture.
334
338
 
@@ -341,7 +345,10 @@ REA is already useful for native application, browser, and Electron investigatio
341
345
  - Attach to a user-owned Chrome-family browser over a configured loopback CDP endpoint; capture exact-origin web structure, safe metadata, approved value-free payload shapes, bundle/source-map evidence, WebMCP declarations, user-action timelines, capture diffs, and explicitly approved screenshots without navigation or JavaScript evaluation.
342
346
  - Inspect Electron `file://` renderer pages through a separate canonical-root permission boundary without invoking Electron APIs; script contents remain separately approved and byte bounded.
343
347
  - Validate and canonically serialize a provider-neutral [JavaScript Application Graph v1](docs/javascript-application-graph.md) spanning packages, ASAR entries, Electron roles, JavaScript/source-map entities, browser/runtime instances, IPC, endpoints, storage, and native add-ons. This shipped domain contract performs no extraction or I/O by itself.
344
- - Reconstruct bounded static package, entrypoint, Webpack/Rspack module, import, worker, endpoint, storage, source-map, and native-add-on structure from a local directory or ASAR through an AST-only [application service](docs/javascript-artifact-reconstruction.md). The service does not execute bootstrap code and does not add a standalone CLI/MCP tool yet.
348
+ - Reconstruct bounded static package, entrypoint, Webpack/Rspack module, import, worker, endpoint, storage, source-map, BrowserWindow, preload, contextBridge, IPC, utility-process, and native-add-on structure from an approved local directory or ASAR through `analyze_javascript_application` or `rea analyze-javascript-application`. The AST-only [application service](docs/javascript-artifact-reconstruction.md) never executes bootstrap code, pairs only unique exact literal IPC channels, and reports dynamic or ambiguous channels as unresolved.
349
+ - Reconcile that static graph with existing passive web or Electron Evidence through `reconcile_javascript_runtime` or `rea reconcile-javascript-runtime`. Exact captured bytes outrank caller-declared file/URL mappings; target, frame, script, worker, cache, and asset ambiguity stays explicit, source-map authority stays separate, and a module resident in an observed bundle is never reported as executed. See [JavaScript static/runtime reconciliation](docs/javascript-runtime-reconciliation.md).
350
+ - Trace a literal route, string, API, IPC channel, module, or native export through authenticated application Evidence with explicit traversal bounds, then hand exact native artifact digests and requested exports to retained Ghidra or Hopper Evidence without automatic provider switching. Compare application versions using unique-only digest, source-map, structural, and semantic tiers; duplicate, incomplete, and truncated matches stay unknown. See [cross-layer JavaScript application workflows](docs/javascript-application-workflows.md).
351
+ - Classify PE/CLI managed artifacts with `inspect_managed_artifact` / `rea inspect-managed-artifact`, inspect bounded metadata members, signatures, method-body CIL hashes, exception regions, call edges, and field-access anchors with `inspect_managed_members` / `rea inspect-managed-members`, inventory declared ModuleRef/ImplMap/PInvoke and non-IL method boundary indicators with `inspect_managed_native_boundaries` / `rea inspect-managed-native-boundaries`, then compare two authenticated member observations with `compare_managed_members` / `rea compare-managed-members`. `verify_managed_native_boundaries` / `rea verify-managed-native-boundaries` checks managed P/Invoke declarations against authenticated native export or function Evidence while keeping verified, inferred, contradicted, and unresolved states distinct. The comparison remaps build-local tokens using unique CIL/signature and structural method-shape tiers, never names alone. `project_managed_application_graph` / `rea project-managed-application-graph` projects authenticated managed artifact/member/native-boundary Evidence into the existing application graph for cross-layer feature tracing. `import_managed_reconstruction` / `rea import-managed-reconstruction` admits user-supplied decompiler C#/IL/pseudocode as analyst inference only after exact artifact SHA-256, MVID, signature, and normalized IL commitments match. Separately, `plan_managed_runtime_correlation` / `rea plan-managed-runtime-correlation` can admit a default-disabled, permission-gated runtime-correlation plan locked to the same build evidence. These paths never load the assembly, resolve CLR dependencies, execute target code, run a decompiler, or translate managed tokens into native addresses; native-body bridge mapping and an actual runtime executor remain future managed-code contracts.
345
352
  - Traverse content-addressed artifact graphs without extraction; on macOS, read-only DMG traversal additionally requires `native_mount_approved: true` and `REA_ARTIFACT_NATIVE_MOUNT_ENABLED=true`. Materialize only approved occurrences into absent output roots.
346
353
  - Build bounded function dossiers with pseudocode, assembly, CFG edges, comments, calls, references, strings, and names.
347
354
  - Search and trace features across symbols, strings, metadata, references, and call paths.
@@ -386,24 +393,24 @@ REA is growing into a toolkit for understanding software across static artifacts
386
393
  ### Now
387
394
 
388
395
  1. **Maintain truthful product metadata** — extend the shipped canonical catalog and drift checks whenever versions, tools, providers, schemas, setup clients, or CLI capabilities change.
389
- 2. **Electron boundary extraction and high-level surface** — extend the shipped static artifact projector with contextBridge, IPC, utility-process, and native-export boundaries, then expose one provider-neutral CLI/MCP workflow without widening authority.
390
- 3. **Cross-provider conformance growth** — add source-owned architectures and difficult indirect/thunk cases while preserving semantic comparison and provider-specific text boundaries.
396
+ 2. **Cross-provider conformance growth** — add source-owned architectures and difficult indirect/thunk cases while preserving semantic comparison and provider-specific text boundaries.
391
397
 
392
398
  ### Next
393
399
 
394
- 1. **Cross-layer tracing and version comparison** — trace renderer behavior through preload, IPC, main-process, storage, network, and native boundaries, then compare those paths across application versions.
395
- 2. **Deeper JavaScript and source recovery** — add historical-source matching, rechunked/minified cross-version matching, and stronger static/runtime reconciliation on top of the shipped AST-only Webpack/Rspack reconstruction.
396
- 3. **Deterministic behavior harnesses** — extend process ownership, protocol fixtures, filesystem observation, reconnects, and cross-version behavioral comparison.
400
+ 1. **Controlled replay conformance growth** — extend the shipped Linux extracted-module sandbox with more source-owned hostile fixtures and cross-kernel conformance; browser or Electron interaction remains a different future authority.
401
+ 2. **Broader application graph evidence** — extend authenticated cross-layer traces with additional static extractors and separately approved runtime authorities.
402
+ 3. **Professional managed-code analysis** — extend shipped PE/CLI triage, CIL evidence, managed/native declaration inventory, source-owned conformance, and obfuscation-resistant comparisons toward verified native-provider composition under the accepted [managed-code boundary](docs/managed-code-analysis.md).
403
+ 4. **Deterministic behavior harnesses** — extend process ownership, protocol fixtures, filesystem observation, reconnects, and cross-version behavioral comparison.
397
404
 
398
405
  ### Later
399
406
 
400
- 1. **Controlled interaction and replay** — keep approved JavaScript execution, browser interaction, Electron instrumentation, and fuzzing behind a separate authority from passive observation.
407
+ 1. **Controlled application interaction** — evaluate separately authorized full-application driving without widening passive browser, Electron, or extracted-module replay authority.
401
408
  2. **Native runtime observation** — approval-gated LLDB, Frida, system logs, process/filesystem observers, and native API tracing.
402
409
  3. **Additional providers and targets** — evaluate IDA/Hex-Rays, Binary Ninja, Rizin, LIEF, Windows-native providers, mobile artifacts, firmware, document formats, and other software-defined systems.
403
410
 
404
411
  New providers must produce the same evidence and safety metadata as existing capabilities before they become part of the public workflow. Once REA has multiple optional toolchains, setup can become capability-selective; the consent rules for that future work are recorded in the [installation roadmap](docs/roadmap.md).
405
412
 
406
- See the [static-analysis provider evaluation](docs/provider-evaluation.md) for the shipped Ghidra function-analysis boundary, remaining admission gates, and provider comparison matrix, and [ADR-0001](docs/adr/0001-provider-selection-and-analysis-profiles.md) for the binding, selection, profile, snapshot, and compatibility decisions.
413
+ See the [static-analysis provider evaluation](docs/provider-evaluation.md) for the shipped Ghidra function-analysis boundary, remaining admission gates, and provider comparison matrix; [ADR-0001](docs/adr/0001-provider-selection-and-analysis-profiles.md) for binding, selection, profile, snapshot, and compatibility decisions; the [controlled replay guide](docs/controlled-javascript-replay.md) plus [ADR-0002](docs/adr/0002-controlled-replay-authority-and-sandbox.md) for the shipped JavaScript replay boundary; and [ADR-0003](docs/adr/0003-managed-code-evidence-and-provider-boundary.md) for the managed-code evidence and provider design.
407
414
 
408
415
  ## Using REA with other agents
409
416
 
@@ -583,7 +590,7 @@ unpacked; REA does not silently accept the mismatched artifact.
583
590
 
584
591
  ## Security model
585
592
 
586
- REA does not provide a hosted analysis service. Hopper and Ghidra bridge communication uses authenticated private local sockets. Dynamic capabilities are disabled by default and require both operator policy and explicit per-call approval. REA is not a security sandbox: providers and launched targets run with the current user's permissions, and each capability reports its side effects and limitations. Report vulnerabilities through the private process in [SECURITY.md](SECURITY.md).
593
+ REA does not provide a hosted analysis service. Hopper and Ghidra bridge communication uses authenticated private local sockets. Dynamic capabilities are disabled by default and require both operator policy and explicit per-call approval. Shipped providers, passive observers, and Process Capture are not security sandboxes: providers and launched targets run with the current user's permissions. Extracted JavaScript replay is a distinct Linux-only capability that fails closed unless Bubblewrap namespaces, architecture-checked seccomp, private runtime mounts, and delegated cgroup limits are available; it never inherits browser, Electron, or Process Capture authority. See [ADR-0002](docs/adr/0002-controlled-replay-authority-and-sandbox.md). Report vulnerabilities through the private process in [SECURITY.md](SECURITY.md).
587
594
 
588
595
  ## FAQ
589
596
 
@@ -44,6 +44,7 @@ export const buildCapabilityInventory = (sessionStatus, policy) => {
44
44
  available: availability.reason === "available",
45
45
  reason: availability.reason,
46
46
  remediation: availability.remediation,
47
+ effects: { ...contract.effects },
47
48
  annotations: {
48
49
  read_only: contract.annotations.readOnlyHint ?? false,
49
50
  destructive: contract.annotations.destructiveHint ?? false,
@@ -59,6 +60,23 @@ const availabilityFor = (name, kind, targetOpen, targetKind, descriptors, policy
59
60
  reason: "policy_disabled",
60
61
  remediation: "Enable or grant process_capture within the administrator ceiling.",
61
62
  };
63
+ if (name === "run_controlled_replay" && !policy.javascriptReplayEnabled)
64
+ return {
65
+ reason: "policy_disabled",
66
+ remediation: "Enable javascript_replay with exact source roots and sandbox executables.",
67
+ };
68
+ if (name === "run_controlled_replay")
69
+ return { reason: "available", remediation: null };
70
+ if (name === "plan_managed_runtime_correlation" &&
71
+ !policy.managedRuntimeEnabled)
72
+ return {
73
+ reason: "policy_disabled",
74
+ remediation: "Enable managed_runtime with exact artifact roots and a runtime executable.",
75
+ };
76
+ if (name === "plan_managed_runtime_correlation")
77
+ return { reason: "available", remediation: null };
78
+ if (kind === "application")
79
+ return { reason: "available", remediation: null };
62
80
  if (name === "import_evidence_bundle" && policy.evidenceFileRoots === 0)
63
81
  return {
64
82
  reason: "policy_disabled",
@@ -14,16 +14,20 @@ export const runCrossVersionInvestigation = async (input, policy, execution) =>
14
14
  return err(new AnalysisInputError("find_changed_behavior", {
15
15
  cause: parsed.error,
16
16
  }));
17
- const initial = await readInvestigationWorkspace(parsed.data.workspace_path, policy);
17
+ return runCrossVersionInvestigationValidated(parsed.data, policy, execution);
18
+ };
19
+ /** Run input already parsed by a trusted adapter boundary. */
20
+ export const runCrossVersionInvestigationValidated = async (input, policy, execution) => {
21
+ const initial = await readInvestigationWorkspace(input.workspace_path, policy);
18
22
  if (!initial.ok)
19
23
  return initial;
20
- const preflight = validateWorkspaceRequest(initial.value, parsed.data);
24
+ const preflight = validateWorkspaceRequest(initial.value, input);
21
25
  if (!preflight.ok)
22
26
  return preflight;
23
27
  if (isCancelled(execution.signal))
24
28
  return err(new AnalysisCancelledError("find_changed_behavior"));
25
- if (parsed.data.replay_run_id !== undefined) {
26
- const replay = selectCompletedInvestigationReplay(initial.value, parsed.data);
29
+ if (input.replay_run_id !== undefined) {
30
+ const replay = selectCompletedInvestigationReplay(initial.value, input);
27
31
  if (!replay.ok)
28
32
  return replay;
29
33
  return completeOutcome(replay.value.workspace, replay.value.run, true, execution.session);
@@ -39,7 +43,7 @@ export const runCrossVersionInvestigation = async (input, policy, execution) =>
39
43
  total: 4,
40
44
  message: "Scanning both version inputs",
41
45
  });
42
- const snapshots = await scanVersions(parsed.data, execution.inputRoots, execution.signal, execution.integrityContinueEnabled);
46
+ const snapshots = await scanVersions(input, execution.inputRoots, execution.signal, execution.integrityContinueEnabled);
43
47
  if (!snapshots.ok)
44
48
  return snapshots;
45
49
  await execution.progress?.report({
@@ -49,7 +53,7 @@ export const runCrossVersionInvestigation = async (input, policy, execution) =>
49
53
  message: "Version inputs scanned",
50
54
  });
51
55
  return continueInvestigation({
52
- input: parsed.data,
56
+ input,
53
57
  initial: initial.value,
54
58
  snapshots: snapshots.value,
55
59
  policy,
@@ -66,6 +66,9 @@ export const runDoctor = async (target, host = systemDoctorHost()) => {
66
66
  classification: "config_drift",
67
67
  }));
68
68
  const providerInspections = await inspectDoctorProviders(host, checks);
69
+ const javascriptReplayCheck = await host.javascriptReplayCheck?.();
70
+ if (javascriptReplayCheck !== undefined)
71
+ checks.push(javascriptReplayCheck);
69
72
  if (host.platform === "linux" && hopperPath !== undefined)
70
73
  checks.push(check("hopper-demo-runtime", await host.linuxDemoRuntimeReady(), undefined, {
71
74
  remediation: "Rerun rea setup to install the Xvfb, xauth, Python, X11, and XTEST packages required for Linux demo sessions.",
@@ -77,7 +80,33 @@ export const runDoctor = async (target, host = systemDoctorHost()) => {
77
80
  classification: "config_drift",
78
81
  }));
79
82
  const installationPaths = (await host.installationPaths?.()) ?? [];
80
- const installedSkillVersion = await host.installedSkillVersion?.();
83
+ const observedSkillIdentity = await host.installedSkillIdentity?.();
84
+ const legacySkillVersion = observedSkillIdentity === undefined
85
+ ? await host.installedSkillVersion?.()
86
+ : undefined;
87
+ const installedSkillIdentity = observedSkillIdentity ??
88
+ (legacySkillVersion === undefined
89
+ ? undefined
90
+ : {
91
+ version: legacySkillVersion,
92
+ toolCount: null,
93
+ catalogDigest: null,
94
+ });
95
+ const installedSkillVersion = installedSkillIdentity?.version ?? undefined;
96
+ const skillAligned = installedSkillIdentity?.version === PRODUCT_IDENTITY.skillVersion &&
97
+ installedSkillIdentity.toolCount === CATALOG_IDENTITY.counts.mcp_tools &&
98
+ installedSkillIdentity.catalogDigest ===
99
+ CATALOG_IDENTITY.digests.combined_sha256;
100
+ if (!skillAligned)
101
+ checks.push({
102
+ name: "skill:identity",
103
+ ok: false,
104
+ classification: "config_drift",
105
+ detail: installedSkillIdentity === undefined
106
+ ? "Installed REA skill identity is missing."
107
+ : "Installed REA skill identity is stale.",
108
+ remediation: "Run rea setup to update the installed REA skill.",
109
+ });
81
110
  const registrations = (await host.clientRegistrations?.()) ?? [];
82
111
  for (const registration of registrations)
83
112
  if (registration.state !== "aligned")
@@ -120,13 +149,14 @@ export const runDoctor = async (target, host = systemDoctorHost()) => {
120
149
  },
121
150
  skill: {
122
151
  installed_version: installedSkillVersion ?? null,
123
- state: installedSkillVersion === undefined
124
- ? "unknown"
125
- : installedSkillVersion === PRODUCT_IDENTITY.skillVersion
152
+ installed_tool_count: installedSkillIdentity?.toolCount ?? null,
153
+ installed_catalog_digest: installedSkillIdentity?.catalogDigest ?? null,
154
+ state: installedSkillIdentity === undefined
155
+ ? "missing"
156
+ : skillAligned
126
157
  ? "aligned"
127
158
  : "stale",
128
- remediation: installedSkillVersion === undefined ||
129
- installedSkillVersion === PRODUCT_IDENTITY.skillVersion
159
+ remediation: skillAligned
130
160
  ? null
131
161
  : "Run rea setup to update the installed REA skill.",
132
162
  },
@@ -220,6 +250,9 @@ export const systemDoctorHost = (options = {}) => ({
220
250
  ...(options.providerInspections === undefined
221
251
  ? {}
222
252
  : { providerInspections: options.providerInspections }),
253
+ ...(options.javascriptReplayCheck === undefined
254
+ ? {}
255
+ : { javascriptReplayCheck: options.javascriptReplayCheck }),
223
256
  async installationPaths() {
224
257
  try {
225
258
  const command = process.platform === "win32" ? "where" : "which";
@@ -239,6 +272,23 @@ export const systemDoctorHost = (options = {}) => ({
239
272
  return undefined;
240
273
  }
241
274
  },
275
+ async installedSkillIdentity() {
276
+ try {
277
+ const content = await readFile(join(homedir(), ".agents/skills", PRODUCT_IDENTITY.skillName, "SKILL.md"), "utf8");
278
+ const version = /^\s{2}version:\s*"([^"]+)"\s*$/mu.exec(content)?.[1] ?? null;
279
+ const countText = /^\s{2}tool_count:\s*(\d+)\s*$/mu.exec(content)?.[1];
280
+ const catalogDigest = /^\s{2}catalog_digest:\s*"([a-f0-9]{64})"\s*$/mu.exec(content)?.[1] ??
281
+ null;
282
+ return {
283
+ version,
284
+ toolCount: countText === undefined ? null : Number.parseInt(countText, 10),
285
+ catalogDigest,
286
+ };
287
+ }
288
+ catch {
289
+ return undefined;
290
+ }
291
+ },
242
292
  clientRegistrations: () => readClientRegistrationStatuses(homedir()),
243
293
  });
244
294
  const parseMajor = (version) => Number.parseInt(version.split(".")[0] ?? "0", 10);
@@ -0,0 +1,96 @@
1
+ import { resolveArtifactPath } from "./JavaScriptArtifactGraphContext.js";
2
+ /** Flatten all per-file IPC facts into deterministic application records. */
3
+ export const collectElectronIpcRecords = (analysis) => analysis.files
4
+ .flatMap(({ file, javascript }) => (javascript?.electron.ipc ?? []).map((finding) => ({
5
+ key: ipcRecordKey(file.path, finding),
6
+ file,
7
+ finding,
8
+ })))
9
+ .sort((left, right) => compareCodePoints(left.key, right.key));
10
+ /** Match only exact literal channels and retain ambiguous matches separately. */
11
+ export const classifyElectronIpcPairings = (records) => {
12
+ const handlers = records.filter(isMainHandler);
13
+ return records.filter(isPairableRendererTransmission).map((transmission) => {
14
+ const candidates = transmission.finding.channel === null
15
+ ? []
16
+ : handlers.filter((handler) => handler.finding.channel === transmission.finding.channel &&
17
+ compatibleIpcModes(transmission.finding, handler.finding));
18
+ return {
19
+ transmission,
20
+ candidates,
21
+ status: candidates.length === 1
22
+ ? "paired"
23
+ : candidates.length > 1
24
+ ? "ambiguous"
25
+ : "unpaired",
26
+ };
27
+ });
28
+ };
29
+ /** Return only pairings supported by one unique literal main handler. */
30
+ export const unambiguousElectronIpcPairings = (records) => classifyElectronIpcPairings(records).flatMap((state) => {
31
+ const handler = state.candidates[0];
32
+ return state.status === "paired" && handler !== undefined
33
+ ? [{ transmission: state.transmission, handler }]
34
+ : [];
35
+ });
36
+ /** Summarize static Electron findings without treating gaps as absence. */
37
+ export const summarizeElectronBoundaries = (analysis) => {
38
+ const files = new Map(analysis.files.map(({ file }) => [file.path, file]));
39
+ const javascript = analysis.files.flatMap(({ javascript: value }) => value === null ? [] : [value]);
40
+ const windows = javascript.flatMap(({ electron }) => electron.browser_windows);
41
+ const bridges = javascript.flatMap(({ electron }) => electron.context_bridge_apis);
42
+ const validations = javascript.flatMap(({ electron }) => electron.sender_validations);
43
+ const utilities = analysis.files.flatMap(({ file, javascript: value }) => (value?.electron.utility_processes ?? []).map((finding) => ({
44
+ file,
45
+ finding,
46
+ })));
47
+ const nativeBindings = analysis.files.flatMap(({ file, javascript: value }) => (value?.electron.native_addon_bindings ?? []).map((finding) => ({
48
+ file,
49
+ finding,
50
+ })));
51
+ const ipc = collectElectronIpcRecords(analysis);
52
+ const pairing = classifyElectronIpcPairings(ipc);
53
+ const literalChannels = new Set(ipc.flatMap(({ finding }) => finding.channel === null ? [] : [finding.channel]));
54
+ return {
55
+ browser_windows: windows.length,
56
+ explicit_web_preferences: windows.reduce((count, window) => count + window.web_preferences.length, 0),
57
+ preload_entrypoints: windows.filter(({ preload_path: path }) => path !== null).length,
58
+ context_bridge_apis: bridges.length,
59
+ exposed_api_members: bridges.reduce((count, bridge) => count + bridge.members.length, 0),
60
+ ipc: {
61
+ operations: ipc.length,
62
+ literal_channels: literalChannels.size,
63
+ dynamic_channel_operations: ipc.filter(({ finding }) => finding.channel === null).length,
64
+ renderer_transmissions: ipc.filter(isRendererTransmission).length,
65
+ renderer_listeners: ipc.filter(({ finding }) => finding.side === "renderer" && finding.mode === "listen").length,
66
+ main_handlers: ipc.filter(isMainHandler).length,
67
+ paired_renderer_transmissions: pairing.filter(({ status }) => status === "paired").length,
68
+ ambiguous_renderer_transmissions: pairing.filter(({ status }) => status === "ambiguous").length,
69
+ unpaired_literal_renderer_transmissions: pairing.filter(({ status, transmission }) => status === "unpaired" && transmission.finding.channel !== null).length,
70
+ },
71
+ sender_validation_observations: validations.length,
72
+ utility_processes: utilities.length,
73
+ resolved_utility_entrypoints: utilities.filter(({ file, finding }) => resolvesToFile(file, finding.module_path, files)).length,
74
+ native_addon_bindings: nativeBindings.length,
75
+ resolved_native_addon_bindings: nativeBindings.filter(({ file, finding }) => resolvesToFile(file, finding.specifier, files, "native-addon")).length,
76
+ };
77
+ };
78
+ const compatibleIpcModes = (transmission, handler) => transmission.mode === "invoke"
79
+ ? handler.mode === "handle"
80
+ : transmission.operation === "send-to-host"
81
+ ? false
82
+ : transmission.mode === "send" && handler.mode === "listen";
83
+ const isRendererTransmission = ({ finding }) => finding.side === "renderer" &&
84
+ (finding.mode === "send" || finding.mode === "invoke");
85
+ const isPairableRendererTransmission = (record) => isRendererTransmission(record) && record.finding.operation !== "send-to-host";
86
+ const isMainHandler = ({ finding }) => finding.side === "main" &&
87
+ (finding.mode === "listen" || finding.mode === "handle");
88
+ const resolvesToFile = (file, specifier, files, kind) => {
89
+ if (specifier === null)
90
+ return false;
91
+ const resolved = resolveArtifactPath(specifier, file.path, files);
92
+ return (resolved !== null &&
93
+ (kind === undefined || files.get(resolved)?.kind === kind));
94
+ };
95
+ const ipcRecordKey = (path, finding) => `${path}\0${String(finding.location.start.line)}:${String(finding.location.start.column)}\0${finding.side}\0${finding.operation}`;
96
+ const compareCodePoints = (left, right) => left < right ? -1 : left > right ? 1 : 0;
@@ -0,0 +1,9 @@
1
+ import { addElectronIpcBoundaries } from "./ElectronBoundaryGraphIpc.js";
2
+ import { addElectronNativeBoundaries } from "./ElectronBoundaryGraphNative.js";
3
+ import { addElectronWindowBoundaries } from "./ElectronBoundaryGraphWindows.js";
4
+ /** Add every static Electron process and security-boundary fact to the JAG. */
5
+ export const addElectronBoundaries = (context) => {
6
+ addElectronWindowBoundaries(context);
7
+ addElectronIpcBoundaries(context);
8
+ addElectronNativeBoundaries(context);
9
+ };
@@ -0,0 +1,62 @@
1
+ import { sha256Text } from "../domain/javascriptStaticAnalysisHelpers.js";
2
+ import { astObservationEvidence, staticInferenceEvidence, } from "./JavaScriptArtifactGraphEvidence.js";
3
+ /** Resolve the file or recovered bundle module that owns one finding. */
4
+ export const electronFindingSourceNode = (context, file, moduleKey) => moduleKey === null
5
+ ? (context.assetNodes.get(file.path) ?? context.fileNodes.get(file.path))
6
+ : context.moduleNodes.get(`${file.path}\0${moduleKey}`);
7
+ /** Observation-scoped identity shared safely across different artifact files. */
8
+ export const electronObservationIdentity = (context, scope, key) => ({
9
+ strategy: "observation-fingerprint",
10
+ stability: "observation-only",
11
+ observation_sha256: sha256Text(`${context.snapshot.manifest.root_sha256}\0${scope}\0${key}`),
12
+ scope: scope.slice(0, 4_096),
13
+ });
14
+ /** Add a direct AST syntax relationship. */
15
+ export const addElectronAstEdge = (context, input) => {
16
+ context.accumulator.addEdge({
17
+ source_node_id: input.source.node_id,
18
+ target_node_id: input.target.node_id,
19
+ relation: input.relation,
20
+ properties: input.properties,
21
+ evidence: astObservationEvidence({
22
+ sha256: input.file.sha256,
23
+ path: input.file.path,
24
+ range: input.range,
25
+ operation: input.operation,
26
+ coverage: input.coverage,
27
+ ...(input.limitations === undefined
28
+ ? {}
29
+ : { limitations: input.limitations }),
30
+ }),
31
+ });
32
+ };
33
+ /** Add an Electron relationship that static syntax suggests but cannot prove. */
34
+ export const addElectronInferenceEdge = (context, input) => {
35
+ context.accumulator.addEdge({
36
+ source_node_id: input.source.node_id,
37
+ target_node_id: input.target.node_id,
38
+ relation: input.relation,
39
+ properties: input.properties,
40
+ evidence: staticInferenceEvidence({
41
+ sha256: input.file.sha256,
42
+ path: input.file.path,
43
+ range: input.range,
44
+ operation: input.operation,
45
+ coverage: input.coverage,
46
+ ...(input.confidence === undefined
47
+ ? {}
48
+ : { confidence: input.confidence }),
49
+ ...(input.limitations === undefined
50
+ ? {}
51
+ : { limitations: input.limitations }),
52
+ }),
53
+ });
54
+ };
55
+ /** Deterministic source-range key for artifact-local graph identities. */
56
+ export const electronRangeKey = (range) => `${String(range.start.line)}:${String(range.start.column)}-${String(range.end.line)}:${String(range.end.column)}`;
57
+ /** Test exact source-range containment without assuming runtime reachability. */
58
+ export const electronRangeContains = (outer, inner) => comparePoint(outer.start, inner.start) <= 0 &&
59
+ comparePoint(outer.end, inner.end) >= 0;
60
+ const comparePoint = (left, right) => left.line === right.line
61
+ ? left.column - right.column
62
+ : left.line - right.line;