rea-agents 3.0.0 → 3.1.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 (219) hide show
  1. package/README.md +14 -16
  2. package/dist/application/AnalysisContextQueries.js +21 -9
  3. package/dist/application/ApplicationWorkflowEvidenceResolver.js +19 -11
  4. package/dist/application/ArtifactInventory/policy.js +17 -0
  5. package/dist/application/ArtifactInventory/reader.js +1 -4
  6. package/dist/application/ArtifactInventory/scanCanonical.js +0 -3
  7. package/dist/application/ArtifactInventory/scanReader.js +7 -7
  8. package/dist/application/ArtifactInventory/types.js +1 -5
  9. package/dist/application/BinarySession.js +14 -16
  10. package/dist/application/BinarySessionRecords.js +31 -5
  11. package/dist/application/BinarySessionStatus.js +11 -5
  12. package/dist/application/BrowserEvidence.js +3 -5
  13. package/dist/application/CapabilityClientRequirements.js +26 -0
  14. package/dist/application/CapabilityInventory.js +134 -63
  15. package/dist/application/CrossVersionInventory.js +7 -6
  16. package/dist/application/ElectronActiveEvidence.js +55 -0
  17. package/dist/application/ElectronActiveObservationPort.js +1 -0
  18. package/dist/application/ElectronActiveObservationService.js +81 -0
  19. package/dist/application/EnhancedTools.js +22 -20
  20. package/dist/application/EvidenceBundleCommands.js +5 -1
  21. package/dist/application/EvidenceLedger.js +8 -1
  22. package/dist/application/JavaScriptReplayPermission.js +1 -1
  23. package/dist/application/JavaScriptReplayService.js +23 -15
  24. package/dist/application/ManagedRuntimeCorrelationService.js +9 -5
  25. package/dist/application/NodeRuntimeCharacterizationService.js +8 -9
  26. package/dist/application/ProcessCaptureLifecycle.js +5 -0
  27. package/dist/application/ProcessCli.js +2 -1
  28. package/dist/application/ProcessHarness.js +8 -3
  29. package/dist/application/ProcessNormalization.js +2 -1
  30. package/dist/application/ProjectPermissionStore.js +1 -0
  31. package/dist/application/ReferenceSourceImport.js +2 -2
  32. package/dist/application/SetupClientConfiguration.js +6 -1
  33. package/dist/application/WindowsCapabilities.js +5 -4
  34. package/dist/artifacts/ArtifactProvider.js +8 -10
  35. package/dist/artifacts/SafeOutputTree.js +7 -8
  36. package/dist/browser/CdpCaptureStorageFingerprints.js +10 -7
  37. package/dist/browser/CdpElectronInspection.js +3 -1
  38. package/dist/browser/JavaScriptRuntimeScope.js +5 -0
  39. package/dist/browser/PlaywrightElectronActiveActions.js +324 -0
  40. package/dist/browser/PlaywrightElectronActiveProvider.js +294 -0
  41. package/dist/browser/PlaywrightScenarioBrowser.js +4 -4
  42. package/dist/browser/PlaywrightScenarioSession.js +4 -4
  43. package/dist/browser/SensitiveTextCapture.js +1 -1
  44. package/dist/browser/V8InspectorCaptureProjection.js +1 -0
  45. package/dist/browser/V8InspectorProvider.js +3 -3
  46. package/dist/catalogIdentity.js +12 -6
  47. package/dist/cli/managedCommands.js +1 -1
  48. package/dist/cliApplicationCommands.js +2 -2
  49. package/dist/cliBrowserAdvancedCommands.js +11 -5
  50. package/dist/cliBrowserCommands.js +14 -7
  51. package/dist/cliCommandNames.js +1 -0
  52. package/dist/cliElectronCommands.js +46 -4
  53. package/dist/cliJavaScriptRuntimeCommands.js +14 -6
  54. package/dist/cliObservationOptions.js +4 -0
  55. package/dist/cliPolicyCommands.js +1 -0
  56. package/dist/config/browserScenario.js +77 -0
  57. package/dist/config/electronAutomation.js +35 -0
  58. package/dist/config/environment.js +6 -0
  59. package/dist/config/javascriptReplay.js +41 -0
  60. package/dist/config/managedRuntime.js +25 -0
  61. package/dist/config/parseConfig.js +56 -200
  62. package/dist/config/parsers.js +10 -1
  63. package/dist/config/passiveObservation.js +157 -0
  64. package/dist/config/processCapture.js +47 -0
  65. package/dist/contracts/applicationToolContracts.js +2 -5
  66. package/dist/contracts/applicationWorkflowInputContracts.js +86 -96
  67. package/dist/contracts/artifactToolContracts.js +27 -34
  68. package/dist/contracts/browserToolContracts.js +2 -2
  69. package/dist/contracts/electronToolContracts.js +30 -1
  70. package/dist/contracts/enhancedInputs.js +13 -29
  71. package/dist/contracts/functionWorkflowToolContracts.js +2 -3
  72. package/dist/contracts/managedToolContracts.js +4 -9
  73. package/dist/contracts/managedWorkflowExamples.js +12 -9
  74. package/dist/contracts/managedWorkflowToolContracts.js +30 -27
  75. package/dist/contracts/nativeToolContracts.js +2 -3
  76. package/dist/contracts/sessionToolSchemas.js +61 -0
  77. package/dist/contracts/toolContractExamples.js +1 -0
  78. package/dist/contracts/toolContractHelpers.js +23 -0
  79. package/dist/contracts/toolContracts.js +5 -77
  80. package/dist/contracts/toolEffects.js +15 -0
  81. package/dist/contracts/toolOutputSchemaGroups.js +17 -2
  82. package/dist/contracts/toolOutputSchemaPrimitives.js +76 -26
  83. package/dist/contracts/toolSchemaMetadata.js +128 -0
  84. package/dist/doctorRuntime.js +1 -1
  85. package/dist/domain/androidApplication.js +2 -0
  86. package/dist/domain/appleApplication.js +2 -0
  87. package/dist/domain/binaryTarget.js +7 -5
  88. package/dist/domain/binaryTargetTypes.js +1 -0
  89. package/dist/domain/browserCaptureComparison.js +21 -69
  90. package/dist/domain/browserObservation.js +22 -9
  91. package/dist/domain/bytecodeProvider.js +33 -35
  92. package/dist/domain/callPath.js +3 -2
  93. package/dist/domain/conformanceReplay.js +3 -3
  94. package/dist/domain/conformanceTrustGate.js +151 -16
  95. package/dist/domain/customProtocolCapture.js +3 -3
  96. package/dist/domain/electronActiveObservation.js +277 -0
  97. package/dist/domain/electronObservation.js +15 -11
  98. package/dist/domain/electronStaticAnalysisValues.js +4 -1
  99. package/dist/domain/eventProcessTree.js +82 -79
  100. package/dist/domain/functionComparisonNormalization.js +15 -8
  101. package/dist/domain/functionDossierEvidence.js +9 -4
  102. package/dist/domain/hopperValues.js +68 -71
  103. package/dist/domain/inputIssueProjection.js +4 -1
  104. package/dist/domain/investigationWorkspace.js +16 -15
  105. package/dist/domain/javascriptExportShapeComparisonSchemas.js +2 -2
  106. package/dist/domain/javascriptReplay.js +79 -63
  107. package/dist/domain/javascriptRuntimeObservation.js +1 -0
  108. package/dist/domain/javascriptRuntimeReconciliationActive.js +89 -0
  109. package/dist/domain/javascriptRuntimeReconciliationGraph.js +11 -10
  110. package/dist/domain/javascriptRuntimeReconciliationMatching.js +57 -49
  111. package/dist/domain/javascriptRuntimeReconciliationParsing.js +5 -1
  112. package/dist/domain/javascriptRuntimeReconciliationResult.js +21 -14
  113. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  114. package/dist/domain/knownPageCoverage.js +15 -7
  115. package/dist/domain/nativeApiBoundary.js +15 -7
  116. package/dist/domain/nodeRuntimeCharacterization.js +14 -13
  117. package/dist/domain/processCaptureReactiveValidation.js +8 -3
  118. package/dist/domain/processCaptureValidation.js +7 -3
  119. package/dist/domain/processComparison.js +12 -7
  120. package/dist/domain/processObservation.js +5 -6
  121. package/dist/domain/processReactiveMatching.js +14 -7
  122. package/dist/domain/processReactiveTransition.js +9 -3
  123. package/dist/domain/processScenario.js +6 -3
  124. package/dist/domain/processTraceComparison.js +3 -2
  125. package/dist/domain/protocolCapture.js +8 -6
  126. package/dist/domain/reconstructionReadinessSchemas.js +30 -2
  127. package/dist/domain/referenceSourceImportParsing.js +2 -2
  128. package/dist/domain/replayMachineRuntime.js +5 -3
  129. package/dist/domain/sourceToBundleComparison.js +2 -2
  130. package/dist/domain/sourceToBundleComparisonSchemas.js +8 -8
  131. package/dist/domain/sourceToBundleSignals.js +7 -1
  132. package/dist/domain/staticRuntimeCorrelation.js +7 -5
  133. package/dist/domain/webBundleAnalysis.js +14 -14
  134. package/dist/domain/webCaptureDiff.js +15 -12
  135. package/dist/dotnet/ManagedNativeBoundaryInspector.js +2 -3
  136. package/dist/generatedMcpToolCatalog.js +1 -1
  137. package/dist/generatedPackageMetadata.js +1 -1
  138. package/dist/ghidra/GhidraClient.js +1 -1
  139. package/dist/ghidra/GhidraClientStartup.js +1 -1
  140. package/dist/ghidra/GhidraFunctionValues.js +17 -15
  141. package/dist/ghidra/GhidraInventoryValues.js +57 -47
  142. package/dist/ghidra/GhidraProvider.js +18 -10
  143. package/dist/ghidra/GhidraProviderCapabilities.js +5 -1
  144. package/dist/ghidra/GhidraSocketConnection.js +1 -1
  145. package/dist/ghidra/protocol.js +19 -42
  146. package/dist/hopper/BridgeLauncher.js +3 -1
  147. package/dist/hopper/HopperAnalysisProfile.js +2 -4
  148. package/dist/hopper/HopperCleanup.js +5 -6
  149. package/dist/hopper/HopperClient.js +1 -1
  150. package/dist/hopper/HopperProvider.js +0 -14
  151. package/dist/hopper/LinuxPrivateDisplayProbe.js +16 -10
  152. package/dist/main/reload.js +1 -6
  153. package/dist/main/state.js +0 -14
  154. package/dist/main/transport.js +30 -37
  155. package/dist/main.js +17 -15
  156. package/dist/mcpDoctor.js +2 -24
  157. package/dist/process/PrivateRuntimeRoot.js +17 -0
  158. package/dist/process/ProcessOwnership.js +64 -128
  159. package/dist/process/ProcessOwnershipIdentity.js +13 -0
  160. package/dist/process/ProcessOwnershipObservation.js +88 -0
  161. package/dist/process/ProcessOwnershipProcessTree.js +22 -0
  162. package/dist/process/ProviderDeadline.js +8 -12
  163. package/dist/process/WindowsAuthority.js +32 -0
  164. package/dist/reference/ReferenceSourceReader.js +3 -3
  165. package/dist/reference/ReferenceSourceReaderEntries.js +2 -2
  166. package/dist/replay/JavaScriptReplayWorker.js +95 -11
  167. package/dist/replay/JavaScriptReplayWorkerTypes.js +1 -0
  168. package/dist/replay/LinuxJavaScriptReplayRunner.js +10 -15
  169. package/dist/replay/ReplayOutcome.js +26 -15
  170. package/dist/replay/ReplayWorkerProtocol.js +34 -29
  171. package/dist/server/createServer.js +158 -94
  172. package/dist/server/mcpClientMetadata.js +32 -0
  173. package/dist/server/registerApplicationTools/characterization.js +6 -13
  174. package/dist/server/registerApplicationTools/compareExportShapes.js +2 -7
  175. package/dist/server/registerApplicationTools/compareSourceToBundle.js +2 -7
  176. package/dist/server/registerApplicationTools/compareVersions.js +3 -8
  177. package/dist/server/registerApplicationTools/controlledReplay.js +6 -11
  178. package/dist/server/registerApplicationTools/coverage.js +13 -19
  179. package/dist/server/registerApplicationTools/obligationLedger.js +1 -6
  180. package/dist/server/registerApplicationTools/readiness.js +1 -6
  181. package/dist/server/registerApplicationTools/traceFeature.js +3 -8
  182. package/dist/server/registerApplicationTools/traceSemantics.js +1 -6
  183. package/dist/server/registerArtifactComparisonTool.js +12 -16
  184. package/dist/server/registerBrowserScenarioTool.js +1 -6
  185. package/dist/server/registerBrowserTools.js +37 -79
  186. package/dist/server/registerBundleComparisonTool.js +10 -14
  187. package/dist/server/registerCloseLifecycleTool.js +6 -14
  188. package/dist/server/registerElectronTools.js +30 -47
  189. package/dist/server/registerEnhancedTools.js +114 -122
  190. package/dist/server/registerEvidenceTools.js +5 -9
  191. package/dist/server/registerFunctionComparisonTool.js +11 -15
  192. package/dist/server/registerInvestigationTools.js +41 -57
  193. package/dist/server/registerJavaScriptRuntimeObservationTools.js +13 -27
  194. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +4 -9
  195. package/dist/server/registerManagedWorkflowTools/contract.js +2 -3
  196. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +3 -8
  197. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +3 -8
  198. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +8 -13
  199. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +4 -9
  200. package/dist/server/registerOfficialTools.js +8 -9
  201. package/dist/server/registerProcessComparisonTool.js +19 -24
  202. package/dist/server/registerReplayMachineTool.js +4 -10
  203. package/dist/server/registerSessionRecordTools.js +69 -40
  204. package/dist/server/registerSessionStatusTool.js +13 -28
  205. package/dist/server/registerSessionTools.js +36 -52
  206. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  207. package/dist/server/sessionToolPolicies.js +1 -5
  208. package/dist/server/toolRegistrationOptions.js +4 -192
  209. package/dist/server/toolResult.js +2 -18
  210. package/package.json +22 -14
  211. package/scripts/electron-active-hook-boundaries.cjs +394 -0
  212. package/scripts/electron-active-hook.cjs +501 -0
  213. package/scripts/rea.mjs +1 -11
  214. package/skills/reverse-engineer-anything/SKILL.md +2 -2
  215. package/dist/server/DynamicToolAvailability.js +0 -70
  216. package/dist/server/LazyToolCatalog.js +0 -134
  217. package/dist/server/hydrateServerTools.js +0 -191
  218. package/dist/server/toolInputValidation.js +0 -77
  219. package/scripts/package-runner-bootstrap.mjs +0 -72
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # REA: Reverse Engineer Anything
6
6
 
7
- ### One CLI and MCP server for agents to reverse engineer anything
7
+ ### Reverse engineer anything with agents, from app behavior down to native binaries.
8
8
 
9
9
  **See a feature you like. Understand how it works, down to the binary level.**
10
10
 
@@ -89,13 +89,13 @@ REA shows how it reached its conclusions. It does not claim to recover original
89
89
  ### Run setup — recommended
90
90
 
91
91
  ```bash
92
- npx rea-agents setup
92
+ npx --yes rea-agents@latest setup
93
93
  ```
94
94
 
95
95
  The npm package-runner prompt, when shown, approves downloading REA for this
96
96
  invocation; it does not approve any setup changes. The REA wizard separately
97
97
  shows its complete plan and asks before applying it. Setup does not update
98
- Homebrew, Node.js, or npm. `npx rea-agents setup` opens with the work it
98
+ Homebrew, Node.js, or npm. The setup command opens with the work it
99
99
  enables: investigate local apps from an agent, recover evidence through a
100
100
  deep-analysis provider, and reuse REA's guided workflow. It summarizes the
101
101
  detected agents, then asks which capabilities to set up: agent integration
@@ -103,12 +103,10 @@ detected agents, then asks which capabilities to set up: agent integration
103
103
  Nothing is preselected. Choosing agent integration opens a second empty
104
104
  checklist for the specific detected agents that should receive a registration.
105
105
 
106
- If bare `npx` finds a project-local REA package, its dispatcher restarts setup
107
- through `rea-agents@latest` before planning any changes. This prevents an older
108
- local dependency from silently writing stale integration state. An already
109
- published older release cannot contain that dispatcher protection; run
110
- `npx -y rea-agents@latest setup` once to repair registrations created by one.
111
- Intentional rollbacks remain available through an exact package request.
106
+ `@latest` makes the requested release explicit and asks npm for the release
107
+ currently published under that tag. REA does not silently replace the package
108
+ version npm selected. Intentional rollbacks therefore remain available through
109
+ an exact package request.
112
110
 
113
111
  REA keeps the journey inline so its history remains in the terminal. Selecting
114
112
  a capability does not select every detected target or authorize a change.
@@ -139,7 +137,7 @@ Pass installer options after `bash -s --`, for example `--dry-run`, `--no-setup`
139
137
  ### With an agent — recommended
140
138
 
141
139
  ```bash
142
- npx rea-agents setup
140
+ npx --yes rea-agents@latest setup
143
141
  ```
144
142
 
145
143
  Choose Agent Integration in the reviewed setup plan. REA installs the pinned MCP
@@ -151,7 +149,7 @@ Review the setup plan, approve it if appropriate, then describe the app or featu
151
149
  ### From Terminal — no installation
152
150
 
153
151
  ```bash
154
- npx rea-agents setup
152
+ npx --yes rea-agents@latest setup
155
153
  npx -y rea-agents@latest doctor
156
154
  npx -y rea-agents@latest analyze /Applications/Notes.app
157
155
  ```
@@ -386,10 +384,10 @@ REA handles the app analysis in steps 1–5. The agent performs step 6 with its
386
384
  | Artifact graph | 3 | bounded provider-neutral inspection, deterministic directory/ZIP/APK/IPA/MSIX/AppX/ASAR inventory, and explicitly selected extraction into an absent owned tree |
387
385
  | 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 |
388
386
  | Browser observation | 9 | exact-origin passive CDP capture, bundle and source-map analysis, WebMCP discovery, session timelines, capture diff, visual evidence, and bounded Playwright scenarios |
389
- | Electron analysis | 4 | passive root-confined observation, bounded static application mapping, and evidence-backed static/runtime reconciliation |
387
+ | Electron analysis | 5 | passive root-confined observation, bounded static application mapping, evidence-backed static/runtime reconciliation, and separately approved provider-owned click/wait scenarios |
390
388
  | JavaScript runtime | 2 | approved attach-only Node/Electron Inspector target discovery plus bounded script and execution-context observation without evaluation or instrumentation |
391
389
  | Application workflows | 12 | bounded cross-layer traces, unique-only version matching, historical-source to bundle mapping, static export return-shape comparison, approved Linux-isolated extracted-module replay, managed-runtime characterization, reconstruction coverage closure, deterministic obligation ledgers, and end-to-end readiness conformance |
392
- | Workspace and observation | 22 | target lifecycle, Evidence v2 bundle snapshots, aggregate navigation/address context, direct finite replay-machine evaluation, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
390
+ | Workspace and observation | 23 | target lifecycle, Evidence v2 bundle snapshots, retained-bundle release, aggregate navigation/address context, direct finite replay-machine evaluation, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
393
391
 
394
392
  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.
395
393
 
@@ -509,14 +507,14 @@ REA is growing into a toolkit for understanding software across static artifacts
509
507
 
510
508
  ### Next
511
509
 
512
- 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.
510
+ 1. **Controlled replay conformance growth** — extend the shipped Linux extracted-module sandbox with more source-owned hostile fixtures and cross-kernel conformance; browser and Electron scenarios remain separately permissioned authorities.
513
511
  2. **Broader application graph evidence** — extend authenticated cross-layer traces with additional static extractors and separately approved runtime authorities.
514
512
  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).
515
513
  4. **Deterministic behavior harnesses** — extend process ownership, protocol fixtures, filesystem observation, reconnects, and cross-version behavioral comparison.
516
514
 
517
515
  ### Later
518
516
 
519
- 1. **Controlled application interaction** — evaluate separately authorized full-application driving without widening passive browser, Electron, or extracted-module replay authority.
517
+ 1. **Broader controlled application interaction** — extend the separately authorized browser and Electron scenario surfaces beyond the current bounded click/wait actions without widening passive observation or extracted-module replay authority.
520
518
  2. **Native runtime observation** — approval-gated LLDB, Frida, system logs, process/filesystem observers, and native API tracing.
521
519
  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.
522
520
 
@@ -537,7 +535,7 @@ Setup detects Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf,
537
535
  "mcpServers": {
538
536
  "rea": {
539
537
  "command": "npx",
540
- "args": ["-y", "rea-agents@3.0.0", "mcp"]
538
+ "args": ["-y", "rea-agents@3.1.0", "mcp"]
541
539
  }
542
540
  }
543
541
  }
@@ -69,10 +69,7 @@ export const inspectAddressContext = async (analysis, input, signal) => {
69
69
  comment,
70
70
  inline_comment: inlineComment,
71
71
  bookmarks: bookmarks.state === "available"
72
- ? {
73
- state: "available",
74
- value: matchingBookmarks(bookmarks.value, resolvedAddress(results[1], input.address)),
75
- }
72
+ ? matchingBookmarks(bookmarks.value, resolvedAddress(results[1], input.address))
76
73
  : bookmarks,
77
74
  });
78
75
  };
@@ -122,9 +119,24 @@ const resolvedAddress = (result, fallback) => {
122
119
  };
123
120
  const matchingBookmarks = (value, address) => {
124
121
  if (!Array.isArray(value))
125
- return [];
126
- return value.filter((bookmark) => typeof bookmark === "object" &&
127
- bookmark !== null &&
128
- !Array.isArray(bookmark) &&
129
- bookmark.address === address);
122
+ return {
123
+ state: "unavailable",
124
+ reason: "list_bookmarks returned malformed output; expected an array.",
125
+ remediation: "Retry list_bookmarks directly and report the provider output shape if it remains malformed.",
126
+ };
127
+ if (!value.every(isBookmark))
128
+ return {
129
+ state: "unavailable",
130
+ reason: "list_bookmarks returned malformed entries; expected address and name strings.",
131
+ remediation: "Retry list_bookmarks directly and report the provider output shape if it remains malformed.",
132
+ };
133
+ return {
134
+ state: "available",
135
+ value: value.filter((bookmark) => bookmark.address === address),
136
+ };
130
137
  };
138
+ const isBookmark = (value) => typeof value === "object" &&
139
+ value !== null &&
140
+ !Array.isArray(value) &&
141
+ typeof value.address === "string" &&
142
+ typeof value.name === "string";
@@ -36,7 +36,7 @@ export const resolveTraceApplicationFeatureRequest = (input, lookup) => {
36
36
  };
37
37
  /** Resolve a trace request already parsed by the caller-facing contract. */
38
38
  export const resolveTraceApplicationFeatureRequestValidated = (input, lookup) => {
39
- const application = graphEvidence(input.application, input.application_evidence_id, lookup);
39
+ const application = graphEvidence(input, lookup);
40
40
  if (!application.ok)
41
41
  return application;
42
42
  const native = resolveEvidenceReferences(lookup, input.native_observation_evidence_ids);
@@ -63,7 +63,7 @@ export const resolveTraceJavaScriptSemanticsRequest = (input, lookup) => {
63
63
  };
64
64
  /** Resolve a semantic trace request already parsed by its public contract. */
65
65
  export const resolveTraceJavaScriptSemanticsRequestValidated = (input, lookup) => {
66
- const application = graphEvidence(input.application, input.application_evidence_id, lookup);
66
+ const application = graphEvidence(input, lookup);
67
67
  if (!application.ok)
68
68
  return application;
69
69
  const raw = { application: application.value, query: input.query };
@@ -81,10 +81,14 @@ export const resolveCompareApplicationVersionsRequest = (input, lookup) => {
81
81
  };
82
82
  /** Resolve a comparison request parsed by the caller-facing contract. */
83
83
  export const resolveCompareApplicationVersionsRequestValidated = (input, lookup) => {
84
- const left = graphEvidence(input.left, input.left_evidence_id, lookup);
84
+ const left = graphEvidence("left" in input
85
+ ? { application: input.left }
86
+ : { application_evidence_id: input.left_evidence_id }, lookup);
85
87
  if (!left.ok)
86
88
  return left;
87
- const right = graphEvidence(input.right, input.right_evidence_id, lookup);
89
+ const right = graphEvidence("right" in input
90
+ ? { application: input.right }
91
+ : { application_evidence_id: input.right_evidence_id }, lookup);
88
92
  if (!right.ok)
89
93
  return right;
90
94
  const leftNative = resolveEvidenceReferences(lookup, input.left_native_observation_evidence_ids);
@@ -123,7 +127,7 @@ export const resolveCompareSourceToBundleRequest = (input, lookup) => {
123
127
  };
124
128
  /** Resolve a historical-source comparison request parsed by its public contract. */
125
129
  export const resolveCompareSourceToBundleRequestValidated = (input, lookup) => {
126
- const application = graphEvidence(input.application, input.application_evidence_id, lookup);
130
+ const application = graphEvidence(input, lookup);
127
131
  if (!application.ok)
128
132
  return application;
129
133
  const raw = {
@@ -148,10 +152,14 @@ export const resolveCompareJavaScriptExportShapesRequest = (input, lookup) => {
148
152
  };
149
153
  /** Resolve an export-shape request already parsed by the adapter contract. */
150
154
  export const resolveCompareJavaScriptExportShapesRequestValidated = (input, lookup) => {
151
- const left = graphEvidence(input.left, input.left_evidence_id, lookup);
155
+ const left = graphEvidence("left" in input
156
+ ? { application: input.left }
157
+ : { application_evidence_id: input.left_evidence_id }, lookup);
152
158
  if (!left.ok)
153
159
  return left;
154
- const right = graphEvidence(input.right, input.right_evidence_id, lookup);
160
+ const right = graphEvidence("right" in input
161
+ ? { application: input.right }
162
+ : { application_evidence_id: input.right_evidence_id }, lookup);
155
163
  if (!right.ok)
156
164
  return right;
157
165
  const raw = {
@@ -171,10 +179,10 @@ export const resolveCompareJavaScriptExportShapesRequestValidated = (input, look
171
179
  ? ok(parsed.data)
172
180
  : invalid("compare_javascript_export_shapes", parsed.error, raw);
173
181
  };
174
- const graphEvidence = (evidence, evidenceId, lookup) => {
175
- if (evidence !== undefined)
176
- return ok(evidence);
177
- const resolved = resolveEvidenceReferences(lookup, evidenceId === undefined ? [] : [evidenceId], APPLICATION_GRAPH_IDENTITIES);
182
+ const graphEvidence = (source, lookup) => {
183
+ if ("application" in source)
184
+ return ok(source.application);
185
+ const resolved = resolveEvidenceReferences(lookup, [source.application_evidence_id], APPLICATION_GRAPH_IDENTITIES);
178
186
  return resolved.ok ? ok(resolved.value[0]) : resolved;
179
187
  };
180
188
  const invalid = (operation, cause, input) => err(new AnalysisInputError(operation, undefined, projectInputIssues(cause.issues, input)));
@@ -0,0 +1,17 @@
1
+ import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
2
+ /** Resolve caller approval and operator policy before native reader selection. */
3
+ export const resolveNativeMountPolicy = (approved, enabled) => {
4
+ if (!approved)
5
+ return { status: "disabled" };
6
+ if (!enabled)
7
+ throw new ArtifactReaderFailure("unavailable", "Native DMG mounting is disabled by operator policy");
8
+ return { status: "approved" };
9
+ };
10
+ /** Resolve parsed caller intent and operator policy before artifact scanning. */
11
+ export const resolveArtifactIntegrityPolicy = (intent, enabled) => {
12
+ if (intent.mode === "fail")
13
+ return intent;
14
+ if (!enabled)
15
+ throw new ArtifactReaderFailure("policy", "Integrity continuation requires explicit approval and operator policy");
16
+ return intent;
17
+ };
@@ -1,5 +1,4 @@
1
1
  import { AsarArtifactReader } from "../../artifacts/AsarArtifactReader.js";
2
- import { ArtifactReaderFailure, } from "../../artifacts/ArtifactReader.js";
3
2
  import { DirectoryArtifactReader } from "../../artifacts/DirectoryArtifactReader.js";
4
3
  import { MachOSliceArtifactReader } from "../../artifacts/MachOSliceArtifactReader.js";
5
4
  import { NativeDmgArtifactReader } from "../../artifacts/NativeDmgArtifactReader.js";
@@ -21,10 +20,8 @@ export const createReader = async (path, format, nativeMount, signal) => {
21
20
  ? new MachOSliceArtifactReader(path)
22
21
  : undefined;
23
22
  case "dmg":
24
- if (!nativeMount.nativeMountApproved)
23
+ if (nativeMount.status === "disabled")
25
24
  return undefined;
26
- if (!nativeMount.nativeMountEnabled)
27
- throw new ArtifactReaderFailure("unavailable", "Native DMG mounting is disabled by operator policy");
28
25
  return NativeDmgArtifactReader.create(path, signal);
29
26
  default:
30
27
  return undefined;
@@ -10,9 +10,6 @@ import { scanReader, } from "./scanReader.js";
10
10
  import { NATIVE_MOUNT_DISABLED, STRICT_INTEGRITY_POLICY, } from "./types.js";
11
11
  export const scanCanonicalArtifactInventory = async (path, limits, options = {}) => {
12
12
  const integrity = options.integrity ?? STRICT_INTEGRITY_POLICY;
13
- if (integrity.mode === "record-and-continue" &&
14
- (!integrity.approved || !integrity.enabled))
15
- throw new ArtifactReaderFailure("policy", "Integrity continuation requires explicit approval and operator policy");
16
13
  const metadata = await lstat(path);
17
14
  const rootFormat = await classifyRoot(path, metadata.isDirectory());
18
15
  const rootDigest = metadata.isDirectory()
@@ -69,14 +69,14 @@ const digestArtifactEntry = async (context, currentReader, entry, logicalPath) =
69
69
  const digest = await hashReadable(await currentReader.open(entry, context.signal), Math.min(context.limits.maxEntryBytes, remainingBytes), context.signal);
70
70
  context.totalBytes += digest.bytes;
71
71
  const mismatched = entry.declaredSha256 !== null && entry.declaredSha256 !== digest.sha256;
72
- if (mismatched && context.integrity.mode === "fail")
73
- throw new ArtifactReaderFailure("integrity", `Artifact integrity metadata disagrees with content: ${logicalPath}`, undefined, {
74
- logicalPath,
75
- declaredSha256: entry.declaredSha256,
76
- calculatedSha256: digest.sha256,
77
- unpacked: entry.unpacked,
78
- });
79
72
  if (mismatched && entry.declaredSha256 !== null) {
73
+ if (context.integrity.mode === "fail")
74
+ throw new ArtifactReaderFailure("integrity", `Artifact integrity metadata disagrees with content: ${logicalPath}`, undefined, {
75
+ logicalPath,
76
+ declaredSha256: entry.declaredSha256,
77
+ calculatedSha256: digest.sha256,
78
+ unpacked: entry.unpacked,
79
+ });
80
80
  if (context.pendingContradictions.length >= context.integrity.maxMismatches)
81
81
  throw new ArtifactReaderFailure("limit", "Artifact integrity mismatch limit exceeded");
82
82
  context.pendingContradictions.push({
@@ -1,10 +1,6 @@
1
1
  export const NATIVE_MOUNT_DISABLED = {
2
- nativeMountApproved: false,
3
- nativeMountEnabled: false,
2
+ status: "disabled",
4
3
  };
5
4
  export const STRICT_INTEGRITY_POLICY = {
6
5
  mode: "fail",
7
- approved: false,
8
- enabled: false,
9
- maxMismatches: 1,
10
6
  };
@@ -24,7 +24,7 @@ export class BinarySession extends BinarySessionRecords {
24
24
  #transition = Promise.resolve();
25
25
  #calls = new Set();
26
26
  #providerRouter;
27
- #runtimeAvailability = new Map();
27
+ #runtimeUnavailability = new Map();
28
28
  #availabilityListeners = new Set();
29
29
  constructor(provider, options = {}) {
30
30
  super();
@@ -185,7 +185,7 @@ export class BinarySession extends BinarySessionRecords {
185
185
  target: this.#active?.target,
186
186
  route: this.#currentRoute(),
187
187
  router: this.#providerRouter,
188
- runtimeAvailability: this.#runtimeAvailability,
188
+ runtimeUnavailability: this.#runtimeUnavailability,
189
189
  runId: this.#active?.runId,
190
190
  runtimeLineageSnapshots: this.#active?.client.runtimeLineageSnapshots?.() ?? [],
191
191
  requestActivitySnapshots: this.#active?.client.requestActivitySnapshots?.() ?? [],
@@ -246,12 +246,12 @@ export class BinarySession extends BinarySessionRecords {
246
246
  }
247
247
  #observeRuntimeAvailability(operation, result) {
248
248
  if (result.ok) {
249
- if (this.#setRuntimeAvailability(operation, true, null))
249
+ if (this.#markRuntimeAvailable(operation))
250
250
  this.#emitAvailabilityChanged();
251
251
  return;
252
252
  }
253
253
  if (result.error._tag === "AnalysisCapabilityUnavailableError") {
254
- if (this.#setRuntimeAvailability(operation, false, result.error.message))
254
+ if (this.#markRuntimeUnavailable(operation, result.error.message))
255
255
  this.#emitAvailabilityChanged();
256
256
  return;
257
257
  }
@@ -267,27 +267,25 @@ export class BinarySession extends BinarySessionRecords {
267
267
  for (const descriptor of capabilities?.values() ?? [])
268
268
  if (providerId !== undefined && descriptor.provider.id === providerId)
269
269
  changed =
270
- this.#setRuntimeAvailability(descriptor.operation, false, "Provider became unavailable during this session.") || changed;
270
+ this.#markRuntimeUnavailable(descriptor.operation, "Provider became unavailable during this session.") || changed;
271
271
  if (changed)
272
272
  this.#emitAvailabilityChanged();
273
273
  }
274
274
  }
275
- #setRuntimeAvailability(operation, available, reason) {
276
- const current = this.#runtimeAvailability.get(operation);
277
- if (current?.available === available && current.reason === reason)
278
- return false;
279
- if (available && current === undefined)
275
+ #markRuntimeAvailable(operation) {
276
+ return this.#runtimeUnavailability.delete(operation);
277
+ }
278
+ #markRuntimeUnavailable(operation, reason) {
279
+ const current = this.#runtimeUnavailability.get(operation);
280
+ if (current?.reason === reason)
280
281
  return false;
281
- if (available)
282
- this.#runtimeAvailability.delete(operation);
283
- else
284
- this.#runtimeAvailability.set(operation, { available, reason });
282
+ this.#runtimeUnavailability.set(operation, { reason });
285
283
  return true;
286
284
  }
287
285
  #clearRuntimeAvailability() {
288
- if (this.#runtimeAvailability.size === 0)
286
+ if (this.#runtimeUnavailability.size === 0)
289
287
  return;
290
- this.#runtimeAvailability.clear();
288
+ this.#runtimeUnavailability.clear();
291
289
  this.#emitAvailabilityChanged();
292
290
  }
293
291
  #emitAvailabilityChanged() {
@@ -63,17 +63,31 @@ export class BinarySessionRecords {
63
63
  bytes: Buffer.byteLength(existing ?? encoded),
64
64
  records: bundle.records.length,
65
65
  unknowns: bundle.unknowns.length,
66
+ scope: "session",
67
+ survivesSession: false,
66
68
  uri: `rea://evidence-bundle/${digest}`,
67
69
  });
68
70
  }
69
71
  retainedEvidenceBundle(digest) {
70
72
  return this.#retainedBundles.get(digest);
71
73
  }
74
+ /** Release one immutable bundle so bounded retention has an explicit recovery path. */
75
+ releaseEvidenceBundle(bundleDigest) {
76
+ const encoded = this.#retainedBundles.get(bundleDigest);
77
+ if (encoded === undefined)
78
+ return false;
79
+ this.#retainedBundles.delete(bundleDigest);
80
+ this.#retainedBundleBytes -= Buffer.byteLength(encoded);
81
+ this.#emitSnapshotChanged();
82
+ return true;
83
+ }
72
84
  importEvidenceBundle(bundle) {
73
85
  const imported = this.#evidence.import(bundle);
74
- if (imported.ok && imported.value > 0)
86
+ if (!imported.ok)
87
+ return imported;
88
+ if (imported.value.changed)
75
89
  this.#emitSnapshotChanged();
76
- return imported;
90
+ return ok(imported.value.recordsAdded);
77
91
  }
78
92
  exportAnalysisSnapshot() {
79
93
  const active = this.activeAnalysisBinding();
@@ -93,7 +107,10 @@ export class BinarySessionRecords {
93
107
  return err(new EvidenceIntegrityError("Analysis snapshot profile_mismatch: the active target has no concrete analysis profile"));
94
108
  const imported = this.#snapshot.import(snapshot, active === undefined
95
109
  ? undefined
96
- : { target: active.target, profile: active.profile }, (bundle) => this.#evidence.import(bundle));
110
+ : { target: active.target, profile: active.profile }, (bundle) => {
111
+ const imported = this.#evidence.import(bundle);
112
+ return imported.ok ? ok(imported.value.recordsAdded) : imported;
113
+ });
97
114
  if (imported.ok)
98
115
  this.#emitSnapshotChanged();
99
116
  return imported;
@@ -136,8 +153,17 @@ export class BinarySessionRecords {
136
153
  this.#emitSnapshotChanged();
137
154
  }
138
155
  #emitSnapshotChanged() {
139
- for (const listener of this.#snapshotListeners)
140
- void listener();
156
+ for (const listener of this.#snapshotListeners) {
157
+ try {
158
+ const notification = listener();
159
+ if (notification !== undefined)
160
+ void notification.catch(() => undefined);
161
+ }
162
+ catch {
163
+ // External resource observers are best-effort; one callback must not
164
+ // make a committed evidence mutation appear to fail.
165
+ }
166
+ }
141
167
  }
142
168
  retainInvestigationWorkspace(workspace) {
143
169
  const parsed = parseInvestigationWorkspace(workspace);
@@ -1,14 +1,20 @@
1
1
  /** Project internal provider routing state into the caller-visible session status. */
2
- export const binarySessionStatus = ({ target, route, router, runtimeAvailability, runId, runtimeLineageSnapshots, requestActivitySnapshots, }) => {
2
+ export const binarySessionStatus = ({ target, route, router, runtimeUnavailability, runId, runtimeLineageSnapshots, requestActivitySnapshots, }) => {
3
3
  const configuredProvider = router.configuredIdentity();
4
4
  const provider = providerSummary(configuredProvider);
5
5
  const providers = router.providerIdentities(route).map(providerSummary);
6
6
  const capabilities = [...(route.capabilities?.values() ?? [])]
7
7
  .sort((left, right) => left.operation.localeCompare(right.operation))
8
- .map((descriptor) => ({
9
- ...descriptor,
10
- ...runtimeAvailability.get(descriptor.operation),
11
- }))
8
+ .map((descriptor) => {
9
+ const unavailable = runtimeUnavailability.get(descriptor.operation);
10
+ return unavailable === undefined
11
+ ? descriptor
12
+ : {
13
+ ...descriptor,
14
+ available: false,
15
+ reason: unavailable.reason,
16
+ };
17
+ })
12
18
  .map(capabilityStatus);
13
19
  const analysisProviderBinding = route.binding === null
14
20
  ? null
@@ -21,9 +21,7 @@ export const createBrowserEvidence = (operation, input, result, provider) => cre
21
21
  });
22
22
  const browserParameters = (input) => {
23
23
  if (!("cdp_endpoint" in input)) {
24
- if ("before_scenario" in input &&
25
- input.before_scenario !== undefined &&
26
- input.after_scenario !== undefined)
24
+ if ("before_scenario" in input)
27
25
  return {
28
26
  comparison_kind: "browser_scenario",
29
27
  before_browser: input.before_scenario.browser,
@@ -89,9 +87,9 @@ const browserParameters = (input) => {
89
87
  include_storage_keys: input.include_storage_keys,
90
88
  include_storage_fingerprints: input.include_storage_fingerprints,
91
89
  limits: input.limits,
92
- ...(input.include_script_sources && "source_capture_approved" in input
90
+ source_capture_approved: input.source_capture_approved,
91
+ ...(input.include_script_sources && "analysis_limits" in input
93
92
  ? {
94
- source_capture_approved: input.source_capture_approved,
95
93
  fetch_source_maps: input.fetch_source_maps,
96
94
  source_map_fetch_approved: input.source_map_fetch_approved,
97
95
  analysis_limits: input.analysis_limits,
@@ -0,0 +1,26 @@
1
+ /** Empty MCP client feature declaration used when the caller supplies none. */
2
+ export const NO_CLIENT_FEATURES = {
3
+ elicitation_form: false,
4
+ elicitation_url: false,
5
+ roots: false,
6
+ sampling: false,
7
+ };
8
+ const CLIENT_FEATURE_REQUIREMENTS = {
9
+ capture_process_scenario: {
10
+ required: [],
11
+ optional: ["elicitation_form"],
12
+ },
13
+ };
14
+ /** Project a tool's client requirements against the negotiated feature set. */
15
+ export const clientRequirementsFor = (name, clientFeatures) => {
16
+ const requirements = CLIENT_FEATURE_REQUIREMENTS[name] ?? {
17
+ required: [],
18
+ optional: [],
19
+ };
20
+ return {
21
+ required: [...requirements.required],
22
+ optional: [...requirements.optional],
23
+ missing_required: requirements.required.filter((feature) => !clientFeatures[feature]),
24
+ missing_optional: requirements.optional.filter((feature) => !clientFeatures[feature]),
25
+ };
26
+ };