rea-agents 2.5.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 (253) hide show
  1. package/README.md +20 -12
  2. package/bridge/ghidra/ReaGhidraBridge.java +477 -4
  3. package/bridge/hopper_bridge.py +54 -4
  4. package/dist/application/AnalysisContextQueries.js +142 -0
  5. package/dist/application/AnalysisSnapshotCache.js +4 -2
  6. package/dist/application/ApplicationWorkflowEvidenceResolver.js +19 -11
  7. package/dist/application/ArtifactInventory/policy.js +17 -0
  8. package/dist/application/ArtifactInventory/reader.js +1 -4
  9. package/dist/application/ArtifactInventory/scanCanonical.js +0 -3
  10. package/dist/application/ArtifactInventory/scanReader.js +7 -7
  11. package/dist/application/ArtifactInventory/types.js +1 -5
  12. package/dist/application/BinarySession.js +19 -20
  13. package/dist/application/BinarySessionComposition.js +21 -0
  14. package/dist/application/BinarySessionRecords.js +106 -10
  15. package/dist/application/BinarySessionStatus.js +11 -5
  16. package/dist/application/BrowserEvidence.js +3 -5
  17. package/dist/application/CapabilityClientRequirements.js +26 -0
  18. package/dist/application/CapabilityInventory.js +140 -55
  19. package/dist/application/ClientRegistrationStatus.js +4 -1
  20. package/dist/application/CrossVersionInventory.js +7 -6
  21. package/dist/application/DirectAnalysis.js +6 -2
  22. package/dist/application/ElectronActiveEvidence.js +55 -0
  23. package/dist/application/ElectronActiveObservationPort.js +1 -0
  24. package/dist/application/ElectronActiveObservationService.js +81 -0
  25. package/dist/application/EnhancedTools.js +49 -17
  26. package/dist/application/EvidenceBundleCommands.js +5 -1
  27. package/dist/application/EvidenceLedger.js +53 -17
  28. package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
  29. package/dist/application/JavaScriptReplayPermission.js +1 -1
  30. package/dist/application/JavaScriptReplayService.js +23 -15
  31. package/dist/application/LazyAnalysisProvider.js +77 -0
  32. package/dist/application/LoopbackReplay.js +1 -1
  33. package/dist/application/ManagedRuntimeCorrelationService.js +9 -5
  34. package/dist/application/NativeApiInspection.js +80 -0
  35. package/dist/application/NodeRuntimeCharacterizationService.js +8 -9
  36. package/dist/application/ProcessCaptureLifecycle.js +5 -0
  37. package/dist/application/ProcessCli.js +2 -1
  38. package/dist/application/ProcessHarness.js +8 -3
  39. package/dist/application/ProcessNormalization.js +2 -1
  40. package/dist/application/ProjectPermissionStore.js +1 -0
  41. package/dist/application/ReferenceSourceImport.js +2 -2
  42. package/dist/application/SessionProviderRouter.js +6 -6
  43. package/dist/application/SetupClientConfiguration.js +10 -3
  44. package/dist/application/WindowsCapabilities.js +5 -4
  45. package/dist/application/runtime.js +26 -9
  46. package/dist/artifacts/ArtifactProvider.js +8 -10
  47. package/dist/artifacts/SafeOutputTree.js +7 -8
  48. package/dist/browser/CdpCaptureStorageFingerprints.js +10 -7
  49. package/dist/browser/CdpElectronInspection.js +3 -1
  50. package/dist/browser/JavaScriptRuntimeScope.js +5 -0
  51. package/dist/browser/PlaywrightBrowserScenarioProvider.js +8 -2
  52. package/dist/browser/PlaywrightElectronActiveActions.js +324 -0
  53. package/dist/browser/PlaywrightElectronActiveProvider.js +294 -0
  54. package/dist/browser/PlaywrightScenarioBrowser.js +4 -4
  55. package/dist/browser/PlaywrightScenarioSession.js +4 -4
  56. package/dist/browser/SensitiveTextCapture.js +1 -1
  57. package/dist/browser/V8InspectorCaptureProjection.js +1 -0
  58. package/dist/browser/V8InspectorProvider.js +3 -3
  59. package/dist/catalogIdentity.js +15 -9
  60. package/dist/cli/artifactCommands.js +5 -3
  61. package/dist/cli/coreAnalysisCommands.js +41 -0
  62. package/dist/cli/managedCommands.js +1 -1
  63. package/dist/cliApplicationCommands.js +2 -2
  64. package/dist/cliBrowserAdvancedCommands.js +11 -5
  65. package/dist/cliBrowserCommands.js +14 -7
  66. package/dist/cliCommandNames.js +2 -0
  67. package/dist/cliElectronCommands.js +46 -4
  68. package/dist/cliJavaScriptRuntimeCommands.js +14 -6
  69. package/dist/cliObservationOptions.js +4 -0
  70. package/dist/cliPolicyCommands.js +1 -0
  71. package/dist/config/browserScenario.js +77 -0
  72. package/dist/config/electronAutomation.js +35 -0
  73. package/dist/config/environment.js +6 -0
  74. package/dist/config/javascriptReplay.js +41 -0
  75. package/dist/config/managedRuntime.js +25 -0
  76. package/dist/config/parseConfig.js +56 -200
  77. package/dist/config/parsers.js +10 -1
  78. package/dist/config/passiveObservation.js +157 -0
  79. package/dist/config/processCapture.js +47 -0
  80. package/dist/contracts/applicationToolContracts.js +2 -5
  81. package/dist/contracts/applicationWorkflowInputContracts.js +86 -96
  82. package/dist/contracts/artifactToolContracts.js +35 -37
  83. package/dist/contracts/browserToolContracts.js +2 -2
  84. package/dist/contracts/electronToolContracts.js +30 -1
  85. package/dist/contracts/enhancedInputs.js +21 -27
  86. package/dist/contracts/functionWorkflowToolContracts.js +29 -0
  87. package/dist/contracts/managedToolContracts.js +4 -9
  88. package/dist/contracts/managedWorkflowExamples.js +12 -9
  89. package/dist/contracts/managedWorkflowToolContracts.js +30 -27
  90. package/dist/contracts/nativeToolContracts.js +2 -3
  91. package/dist/contracts/promptContracts.js +14 -15
  92. package/dist/contracts/sessionToolSchemas.js +61 -0
  93. package/dist/contracts/toolContractExamples.js +4 -0
  94. package/dist/contracts/toolContractHelpers.js +23 -0
  95. package/dist/contracts/toolContracts.js +12 -67
  96. package/dist/contracts/toolEffects.js +19 -0
  97. package/dist/contracts/toolOutputSchemaGroups.js +66 -11
  98. package/dist/contracts/toolOutputSchemaPrimitives.js +76 -26
  99. package/dist/contracts/toolSchemaMetadata.js +128 -0
  100. package/dist/doctorRuntime.js +1 -1
  101. package/dist/domain/analysisErrorPresentation.js +8 -2
  102. package/dist/domain/analysisErrorProjection.js +1 -0
  103. package/dist/domain/androidApplication.js +2 -0
  104. package/dist/domain/appleApplication.js +2 -0
  105. package/dist/domain/binaryTarget.js +7 -5
  106. package/dist/domain/binaryTargetTypes.js +1 -0
  107. package/dist/domain/browserCaptureComparison.js +21 -69
  108. package/dist/domain/browserObservation.js +22 -9
  109. package/dist/domain/bytecodeProvider.js +177 -0
  110. package/dist/domain/callPath.js +3 -2
  111. package/dist/domain/conformancePackage.js +169 -0
  112. package/dist/domain/conformanceReplay.js +104 -0
  113. package/dist/domain/conformanceTrustGate.js +328 -0
  114. package/dist/domain/customProtocolCapture.js +137 -0
  115. package/dist/domain/electronActiveObservation.js +277 -0
  116. package/dist/domain/electronObservation.js +15 -11
  117. package/dist/domain/electronStaticAnalysisValues.js +4 -1
  118. package/dist/domain/eventProcessTree.js +217 -0
  119. package/dist/domain/evidenceBundle.js +47 -0
  120. package/dist/domain/functionComparisonNormalization.js +15 -8
  121. package/dist/domain/functionDossierEvidence.js +9 -4
  122. package/dist/domain/hopperStartupFailure.js +1 -1
  123. package/dist/domain/hopperValues.js +68 -68
  124. package/dist/domain/inputIssueProjection.js +11 -2
  125. package/dist/domain/investigationWorkspace.js +16 -15
  126. package/dist/domain/javascriptExportShapeComparisonSchemas.js +2 -2
  127. package/dist/domain/javascriptReplay.js +79 -63
  128. package/dist/domain/javascriptRuntimeObservation.js +1 -0
  129. package/dist/domain/javascriptRuntimeReconciliationActive.js +89 -0
  130. package/dist/domain/javascriptRuntimeReconciliationGraph.js +11 -10
  131. package/dist/domain/javascriptRuntimeReconciliationMatching.js +57 -49
  132. package/dist/domain/javascriptRuntimeReconciliationParsing.js +5 -1
  133. package/dist/domain/javascriptRuntimeReconciliationResult.js +21 -14
  134. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  135. package/dist/domain/javascriptSemanticAnalysis.js +8 -12
  136. package/dist/domain/javascriptSourceParser.js +15 -0
  137. package/dist/domain/javascriptStaticAnalysis.js +8 -12
  138. package/dist/domain/knownPageCoverage.js +15 -7
  139. package/dist/domain/mobileApplicationInvestigation.js +159 -0
  140. package/dist/domain/nativeApiBoundary.js +138 -0
  141. package/dist/domain/nodeRuntimeCharacterization.js +14 -13
  142. package/dist/domain/objcSwiftMetadata.js +150 -0
  143. package/dist/domain/packageAnalysis.js +123 -0
  144. package/dist/domain/peInspection.js +196 -0
  145. package/dist/domain/processCaptureReactiveValidation.js +8 -3
  146. package/dist/domain/processCaptureValidation.js +7 -3
  147. package/dist/domain/processComparison.js +12 -7
  148. package/dist/domain/processObservation.js +5 -6
  149. package/dist/domain/processReactiveMatching.js +14 -7
  150. package/dist/domain/processReactiveTransition.js +9 -3
  151. package/dist/domain/processScenario.js +6 -3
  152. package/dist/domain/processTraceComparison.js +3 -2
  153. package/dist/domain/protocolCapture.js +245 -0
  154. package/dist/domain/reconstructionReadinessSchemas.js +30 -2
  155. package/dist/domain/referenceSourceImportParsing.js +2 -2
  156. package/dist/domain/replayMachineRuntime.js +5 -3
  157. package/dist/domain/sourceToBundleComparison.js +2 -2
  158. package/dist/domain/sourceToBundleComparisonSchemas.js +8 -8
  159. package/dist/domain/sourceToBundleSignals.js +7 -1
  160. package/dist/domain/staticRuntimeCorrelation.js +7 -5
  161. package/dist/domain/webBundleAnalysis.js +14 -14
  162. package/dist/domain/webCaptureDiff.js +15 -12
  163. package/dist/dotnet/ManagedNativeBoundaryInspector.js +2 -3
  164. package/dist/dotnet/ManagedStaticProvider.js +34 -5
  165. package/dist/generatedMcpToolCatalog.js +37 -0
  166. package/dist/generatedPackageMetadata.js +4 -4
  167. package/dist/ghidra/GhidraClient.js +1 -1
  168. package/dist/ghidra/GhidraClientStartup.js +1 -1
  169. package/dist/ghidra/GhidraDefaults.js +1 -1
  170. package/dist/ghidra/GhidraFunctionValues.js +44 -16
  171. package/dist/ghidra/GhidraInventoryValues.js +57 -47
  172. package/dist/ghidra/GhidraProvider.js +18 -10
  173. package/dist/ghidra/GhidraProviderCapabilities.js +45 -39
  174. package/dist/ghidra/GhidraSocketConnection.js +1 -1
  175. package/dist/ghidra/protocol.js +19 -42
  176. package/dist/hopper/BridgeLauncher.js +3 -1
  177. package/dist/hopper/HopperAnalysisProfile.js +2 -4
  178. package/dist/hopper/HopperCleanup.js +5 -6
  179. package/dist/hopper/HopperClient.js +27 -3
  180. package/dist/hopper/HopperProvider.js +42 -44
  181. package/dist/hopper/HopperRequestQueue.js +32 -4
  182. package/dist/hopper/HopperResponseStream.js +2 -2
  183. package/dist/hopper/LinuxPrivateDisplayProbe.js +16 -10
  184. package/dist/hopper/protocol.js +63 -21
  185. package/dist/main/reload.js +1 -6
  186. package/dist/main/state.js +0 -14
  187. package/dist/main/transport.js +31 -41
  188. package/dist/main.js +17 -13
  189. package/dist/mcpDoctor.js +4 -26
  190. package/dist/mcpStartupPolicy.js +14 -0
  191. package/dist/process/PrivateRuntimeRoot.js +17 -0
  192. package/dist/process/ProcessOwnership.js +64 -128
  193. package/dist/process/ProcessOwnershipIdentity.js +13 -0
  194. package/dist/process/ProcessOwnershipObservation.js +88 -0
  195. package/dist/process/ProcessOwnershipProcessTree.js +22 -0
  196. package/dist/process/ProviderDeadline.js +8 -12
  197. package/dist/process/WindowsAuthority.js +32 -0
  198. package/dist/reference/ReferenceSourceReader.js +3 -3
  199. package/dist/reference/ReferenceSourceReaderEntries.js +2 -2
  200. package/dist/replay/JavaScriptReplayWorker.js +95 -11
  201. package/dist/replay/JavaScriptReplayWorkerTypes.js +1 -0
  202. package/dist/replay/LinuxJavaScriptReplayRunner.js +10 -15
  203. package/dist/replay/ReplayOutcome.js +26 -15
  204. package/dist/replay/ReplayWorkerProtocol.js +34 -29
  205. package/dist/server/createServer.js +101 -144
  206. package/dist/server/mcpClientMetadata.js +32 -0
  207. package/dist/server/registerApplicationTools/characterization.js +6 -13
  208. package/dist/server/registerApplicationTools/compareExportShapes.js +2 -7
  209. package/dist/server/registerApplicationTools/compareSourceToBundle.js +2 -7
  210. package/dist/server/registerApplicationTools/compareVersions.js +3 -8
  211. package/dist/server/registerApplicationTools/controlledReplay.js +6 -11
  212. package/dist/server/registerApplicationTools/coverage.js +13 -19
  213. package/dist/server/registerApplicationTools/obligationLedger.js +1 -6
  214. package/dist/server/registerApplicationTools/readiness.js +1 -6
  215. package/dist/server/registerApplicationTools/traceFeature.js +3 -8
  216. package/dist/server/registerApplicationTools/traceSemantics.js +1 -6
  217. package/dist/server/registerArtifactComparisonTool.js +12 -16
  218. package/dist/server/registerBrowserScenarioTool.js +1 -6
  219. package/dist/server/registerBrowserTools.js +37 -79
  220. package/dist/server/registerBundleComparisonTool.js +10 -14
  221. package/dist/server/registerCloseLifecycleTool.js +6 -14
  222. package/dist/server/registerElectronTools.js +30 -47
  223. package/dist/server/registerEnhancedTools.js +124 -122
  224. package/dist/server/registerEvidenceResources.js +50 -101
  225. package/dist/server/registerEvidenceTools.js +5 -9
  226. package/dist/server/registerFunctionComparisonTool.js +11 -15
  227. package/dist/server/registerInvestigationTools.js +42 -59
  228. package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
  229. package/dist/server/registerJavaScriptRuntimeObservationTools.js +13 -27
  230. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +4 -9
  231. package/dist/server/registerManagedWorkflowTools/contract.js +2 -3
  232. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +3 -8
  233. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +3 -8
  234. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +8 -13
  235. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +4 -9
  236. package/dist/server/registerOfficialTools.js +8 -9
  237. package/dist/server/registerProcessComparisonTool.js +19 -24
  238. package/dist/server/registerReconstructionObligationLedgerResource.js +0 -7
  239. package/dist/server/registerReconstructionReadinessResource.js +0 -7
  240. package/dist/server/registerReplayMachineTool.js +4 -10
  241. package/dist/server/registerSessionRecordTools.js +146 -47
  242. package/dist/server/registerSessionStatusTool.js +14 -29
  243. package/dist/server/registerSessionTools.js +45 -41
  244. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  245. package/dist/server/sessionToolPolicies.js +1 -5
  246. package/dist/server/toolRegistrationOptions.js +4 -177
  247. package/dist/server/toolResult.js +14 -18
  248. package/package.json +35 -16
  249. package/scripts/electron-active-hook-boundaries.cjs +394 -0
  250. package/scripts/electron-active-hook.cjs +501 -0
  251. package/skills/reverse-engineer-anything/SKILL.md +10 -3
  252. package/dist/server/DynamicToolAvailability.js +0 -70
  253. package/dist/server/toolInputValidation.js +0 -77
@@ -1,24 +1,37 @@
1
1
  import { createEvidence } from "../domain/evidence.js";
2
- import { evidenceBundleForTarget } from "../domain/evidenceBundle.js";
3
- import { EvidenceIntegrityError, } from "../domain/errors.js";
2
+ import { evidenceBundleForTarget, serializeEvidenceBundle, } from "../domain/evidenceBundle.js";
3
+ import { EvidenceIntegrityError, EvidenceLimitError, } from "../domain/errors.js";
4
4
  import { parseInvestigationWorkspace, } from "../domain/investigationWorkspace.js";
5
5
  import { parseReconstructionCoverageWorkspace, } from "../domain/reconstructionCoverage.js";
6
- import { err } from "../domain/result.js";
6
+ import { err, ok } from "../domain/result.js";
7
7
  import { AnalysisSnapshotCache } from "./AnalysisSnapshotCache.js";
8
8
  import { EvidenceLedger } from "./EvidenceLedger.js";
9
9
  import { UNKNOWN_REGISTRY_PROVIDER, unknownEvidenceLinks, unknownMutationEvidence, } from "./UnknownEvidence.js";
10
10
  /** Owns session evidence, snapshots, workspaces, and residual unknowns. */
11
11
  export class BinarySessionRecords {
12
+ static MAX_RETAINED_BUNDLES = 16;
13
+ static MAX_RETAINED_BUNDLE_BYTES = 64 * 1024 * 1024;
12
14
  #evidence = new EvidenceLedger({
13
15
  maxRecords: 10_000,
14
16
  maxBytes: 64 * 1024 * 1024,
15
17
  });
16
18
  #snapshot = new AnalysisSnapshotCache();
19
+ #retainedBundles = new Map();
20
+ #retainedBundleBytes = 0;
17
21
  #investigationWorkspaces = new Map();
18
22
  #coverageWorkspaces = new Map();
19
23
  #snapshotInvalidated = false;
24
+ #snapshotListeners = new Set();
25
+ /** Observe changes to the mutable current analysis snapshot resource. */
26
+ onAnalysisSnapshotChanged(listener) {
27
+ this.#snapshotListeners.add(listener);
28
+ return () => this.#snapshotListeners.delete(listener);
29
+ }
20
30
  recordEvidence(evidence) {
21
- return this.#evidence.record(evidence);
31
+ const recorded = this.#evidence.record(evidence);
32
+ if (recorded.ok && recorded.value === "added")
33
+ this.#emitSnapshotChanged();
34
+ return recorded;
22
35
  }
23
36
  hasEvidence(evidenceId) {
24
37
  return this.#evidence.has(evidenceId);
@@ -29,8 +42,52 @@ export class BinarySessionRecords {
29
42
  exportEvidenceBundle() {
30
43
  return this.#evidence.export();
31
44
  }
45
+ snapshotEvidenceBundle() {
46
+ const bundle = this.#evidence.export();
47
+ const encoded = serializeEvidenceBundle(bundle);
48
+ const digest = createHash("sha256").update(encoded).digest("hex");
49
+ const existing = this.#retainedBundles.get(digest);
50
+ if (existing === undefined) {
51
+ if (this.#retainedBundles.size >= BinarySessionRecords.MAX_RETAINED_BUNDLES)
52
+ return err(new EvidenceLimitError("records", BinarySessionRecords.MAX_RETAINED_BUNDLES));
53
+ const bytes = Buffer.byteLength(encoded);
54
+ if (this.#retainedBundleBytes + bytes >
55
+ BinarySessionRecords.MAX_RETAINED_BUNDLE_BYTES)
56
+ return err(new EvidenceLimitError("bytes", BinarySessionRecords.MAX_RETAINED_BUNDLE_BYTES));
57
+ this.#retainedBundles.set(digest, encoded);
58
+ this.#retainedBundleBytes += bytes;
59
+ }
60
+ return ok({
61
+ bundleDigest: digest,
62
+ bundleVersion: 2,
63
+ bytes: Buffer.byteLength(existing ?? encoded),
64
+ records: bundle.records.length,
65
+ unknowns: bundle.unknowns.length,
66
+ scope: "session",
67
+ survivesSession: false,
68
+ uri: `rea://evidence-bundle/${digest}`,
69
+ });
70
+ }
71
+ retainedEvidenceBundle(digest) {
72
+ return this.#retainedBundles.get(digest);
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
+ }
32
84
  importEvidenceBundle(bundle) {
33
- return this.#evidence.import(bundle);
85
+ const imported = this.#evidence.import(bundle);
86
+ if (!imported.ok)
87
+ return imported;
88
+ if (imported.value.changed)
89
+ this.#emitSnapshotChanged();
90
+ return ok(imported.value.recordsAdded);
34
91
  }
35
92
  exportAnalysisSnapshot() {
36
93
  const active = this.activeAnalysisBinding();
@@ -48,36 +105,65 @@ export class BinarySessionRecords {
48
105
  const active = this.activeAnalysisBinding();
49
106
  if (active?.profile === null)
50
107
  return err(new EvidenceIntegrityError("Analysis snapshot profile_mismatch: the active target has no concrete analysis profile"));
51
- return this.#snapshot.import(snapshot, active === undefined
108
+ const imported = this.#snapshot.import(snapshot, active === undefined
52
109
  ? undefined
53
- : { 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
+ });
114
+ if (imported.ok)
115
+ this.#emitSnapshotChanged();
116
+ return imported;
54
117
  }
55
118
  matchesSnapshot(target, profile) {
56
119
  return this.#snapshot.matches(target, profile ?? undefined);
57
120
  }
58
121
  selectSnapshot(target, profile) {
59
122
  this.#snapshot.select(target, profile);
123
+ this.#emitSnapshotChanged();
60
124
  }
61
125
  lookupSnapshot(target, profile, operation, parameters) {
62
126
  return this.#snapshot.lookup(target, profile, operation, parameters);
63
127
  }
64
128
  recordSnapshot(input) {
65
129
  this.#snapshot.record(input);
130
+ this.#emitSnapshotChanged();
66
131
  }
67
132
  invalidateSnapshot() {
68
133
  this.#snapshot.clear();
69
134
  this.#snapshotInvalidated = true;
135
+ this.#emitSnapshotChanged();
70
136
  }
71
137
  resetSnapshotInvalidation() {
138
+ if (!this.#snapshotInvalidated)
139
+ return;
72
140
  this.#snapshotInvalidated = false;
141
+ this.#emitSnapshotChanged();
73
142
  }
74
143
  clearSnapshot() {
75
144
  this.#snapshot.clear();
145
+ this.#emitSnapshotChanged();
76
146
  }
77
147
  clearSessionRecords() {
78
148
  this.#evidence.clear();
79
149
  this.#snapshot.clear();
80
150
  this.#snapshotInvalidated = false;
151
+ this.#retainedBundles.clear();
152
+ this.#retainedBundleBytes = 0;
153
+ this.#emitSnapshotChanged();
154
+ }
155
+ #emitSnapshotChanged() {
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
+ }
81
167
  }
82
168
  retainInvestigationWorkspace(workspace) {
83
169
  const parsed = parseInvestigationWorkspace(workspace);
@@ -111,10 +197,16 @@ export class BinarySessionRecords {
111
197
  }
112
198
  recordUnknown(input) {
113
199
  const target = this.activeAnalysisBinding()?.target;
114
- return this.#evidence.recordUnknown(input, unknownMutationEvidence(target, input));
200
+ const recorded = this.#evidence.recordUnknown(input, unknownMutationEvidence(target, input));
201
+ if (recorded.ok)
202
+ this.#emitSnapshotChanged();
203
+ return recorded;
115
204
  }
116
205
  recordEvidenceWithUnknown(evidence, input) {
117
- return this.#evidence.recordWithUnknown(evidence, input, unknownMutationEvidence(undefined, input));
206
+ const recorded = this.#evidence.recordWithUnknown(evidence, input, unknownMutationEvidence(undefined, input));
207
+ if (recorded.ok)
208
+ this.#emitSnapshotChanged();
209
+ return recorded;
118
210
  }
119
211
  updateUnknown(input) {
120
212
  const target = this.activeAnalysisBinding()?.target;
@@ -133,7 +225,10 @@ export class BinarySessionRecords {
133
225
  "Registry mutation evidence records analyst intent, not proof of the answer.",
134
226
  ],
135
227
  });
136
- return this.#evidence.updateUnknown(input, evidence);
228
+ const updated = this.#evidence.updateUnknown(input, evidence);
229
+ if (updated.ok)
230
+ this.#emitSnapshotChanged();
231
+ return updated;
137
232
  }
138
233
  listUnknowns(filters = {}) {
139
234
  return this.#evidence.listUnknowns(filters);
@@ -147,3 +242,4 @@ const sortedWorkspaces = (workspaces) => [...workspaces]
147
242
  .sort((left, right) => left.workspace_id.localeCompare(right.workspace_id) ||
148
243
  left.revision - right.revision)
149
244
  .map((workspace) => structuredClone(workspace));
245
+ import { createHash } from "node:crypto";
@@ -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
+ };
@@ -1,28 +1,13 @@
1
1
  import { z } from "zod";
2
- import { TOOL_CONTRACTS } from "../contracts/toolContracts.js";
2
+ import { providerCapability, } from "../contracts/toolOutputSchemaPrimitives.js";
3
+ import { GENERATED_MCP_TOOL_CATALOG } from "../generatedMcpToolCatalog.js";
4
+ import { clientRequirementsFor, NO_CLIENT_FEATURES, } from "./CapabilityClientRequirements.js";
3
5
  const statusSchema = z.object({
4
6
  open: z.boolean(),
5
7
  kind: z.enum(["executable", "database", "archive", "artifact"]).optional(),
6
8
  format: z.string().optional(),
7
- capabilities: z.array(z.object({
8
- operation: z.string(),
9
- available: z.boolean(),
10
- reason: z.string().nullable(),
11
- availability_code: z.string().nullable().optional(),
12
- })),
9
+ capabilities: z.array(providerCapability),
13
10
  });
14
- const NO_CLIENT_FEATURES = {
15
- elicitation_form: false,
16
- elicitation_url: false,
17
- roots: false,
18
- sampling: false,
19
- };
20
- const CLIENT_FEATURE_REQUIREMENTS = {
21
- capture_process_scenario: {
22
- required: [],
23
- optional: ["elicitation_form"],
24
- },
25
- };
26
11
  const ENHANCED_REQUIREMENTS = {
27
12
  swift_classes: ["list_procedures"],
28
13
  get_objc_classes: ["list_names"],
@@ -38,6 +23,7 @@ const ENHANCED_REQUIREMENTS = {
38
23
  "list_strings",
39
24
  ],
40
25
  analyze_function: ["analyze_function"],
26
+ inspect_native_api: ["analyze_function"],
41
27
  trace_feature: [
42
28
  "list_strings",
43
29
  "list_procedures",
@@ -45,18 +31,25 @@ const ENHANCED_REQUIREMENTS = {
45
31
  "resolve_containing_procedure",
46
32
  ],
47
33
  };
34
+ const NAVIGATION_CONTEXT_MODES = [
35
+ {
36
+ name: "current_selection",
37
+ requirements: [
38
+ "current_document",
39
+ "current_address",
40
+ "resolve_containing_procedure",
41
+ ],
42
+ },
43
+ {
44
+ name: "explicit_document",
45
+ requirements: ["current_address", "resolve_containing_procedure"],
46
+ },
47
+ ];
48
48
  /** Build stable per-operation availability for discovery and tool visibility. */
49
49
  export const buildCapabilityInventory = (sessionStatus, policy, clientFeatures = NO_CLIENT_FEATURES) => {
50
50
  const status = statusSchema.parse(sessionStatus);
51
- const descriptors = new Map(status.capabilities.map((descriptor) => [
52
- descriptor.operation,
53
- {
54
- available: descriptor.available,
55
- reason: descriptor.reason,
56
- availability_code: descriptor.availability_code ?? null,
57
- },
58
- ]));
59
- return TOOL_CONTRACTS.map((contract) => {
51
+ const descriptors = new Map(status.capabilities.map((descriptor) => [descriptor.operation, descriptor]));
52
+ return GENERATED_MCP_TOOL_CATALOG.map((contract) => {
60
53
  const availability = availabilityFor({
61
54
  name: contract.name,
62
55
  kind: contract.kind,
@@ -67,16 +60,15 @@ export const buildCapabilityInventory = (sessionStatus, policy, clientFeatures =
67
60
  });
68
61
  const clientRequirements = clientRequirementsFor(contract.name, clientFeatures);
69
62
  const clientBlocked = clientRequirements.missing_required.length > 0;
70
- return {
63
+ const facts = {
71
64
  name: contract.name,
72
65
  surface: contract.kind,
73
- available: availability.reason === "available" && !clientBlocked,
74
- reason: clientBlocked
75
- ? "client_capability_missing"
76
- : availability.reason,
77
- remediation: clientBlocked
78
- ? `Use an MCP client that supports: ${clientRequirements.missing_required.join(", ")}.`
79
- : availability.remediation,
66
+ ...(availability.defaultModeAvailable === undefined
67
+ ? {}
68
+ : { default_mode_available: availability.defaultModeAvailable }),
69
+ ...(availability.modes === undefined
70
+ ? {}
71
+ : { modes: [...availability.modes] }),
80
72
  client_requirements: clientRequirements,
81
73
  effects: { ...contract.effects },
82
74
  annotations: {
@@ -86,21 +78,31 @@ export const buildCapabilityInventory = (sessionStatus, policy, clientFeatures =
86
78
  open_world: contract.annotations.openWorldHint ?? true,
87
79
  },
88
80
  };
81
+ if (clientBlocked)
82
+ return {
83
+ ...facts,
84
+ available: false,
85
+ reason: "client_capability_missing",
86
+ remediation: `Use an MCP client that supports: ${clientRequirements.missing_required.join(", ")}.`,
87
+ };
88
+ return availability.reason === "available"
89
+ ? {
90
+ ...facts,
91
+ available: true,
92
+ reason: availability.reason,
93
+ remediation: null,
94
+ }
95
+ : {
96
+ ...facts,
97
+ available: false,
98
+ reason: availability.reason,
99
+ remediation: availability.remediation,
100
+ };
89
101
  }).sort((left, right) => left.name.localeCompare(right.name));
90
102
  };
91
- const clientRequirementsFor = (name, clientFeatures) => {
92
- const requirements = CLIENT_FEATURE_REQUIREMENTS[name] ?? {
93
- required: [],
94
- optional: [],
95
- };
96
- return {
97
- required: [...requirements.required],
98
- optional: [...requirements.optional],
99
- missing_required: requirements.required.filter((feature) => !clientFeatures[feature]),
100
- missing_optional: requirements.optional.filter((feature) => !clientFeatures[feature]),
101
- };
102
- };
103
103
  const availabilityFor = (context) => {
104
+ if (context.name === "get_navigation_context")
105
+ return navigationContextAvailability(context.descriptors);
104
106
  const javascriptApplication = javascriptApplicationAvailability(context);
105
107
  if (javascriptApplication !== null)
106
108
  return javascriptApplication;
@@ -155,13 +157,9 @@ const policyAvailability = ({ name, kind, policy, }) => {
155
157
  reason: "policy_disabled",
156
158
  remediation: "Configure or grant an evidence_read root.",
157
159
  };
158
- if (kind === "electron-provider")
159
- return policy.electronObservationEnabled === true
160
- ? { reason: "available", remediation: null }
161
- : {
162
- reason: "policy_disabled",
163
- remediation: "Enable Electron observation and configure a loopback CDP endpoint and canonical file roots.",
164
- };
160
+ const electron = electronPolicyAvailability(name, kind, policy);
161
+ if (electron !== null)
162
+ return electron;
165
163
  if (kind === "runtime-provider")
166
164
  return policy.v8InspectorObservationEnabled === true
167
165
  ? { reason: "available", remediation: null }
@@ -173,6 +171,23 @@ const policyAvailability = ({ name, kind, policy, }) => {
173
171
  return { reason: "available", remediation: null };
174
172
  return null;
175
173
  };
174
+ const electronPolicyAvailability = (name, kind, policy) => {
175
+ if (kind !== "electron-provider")
176
+ return null;
177
+ if (name === "capture_electron_scenario")
178
+ return policy.electronAutomationEnabled === true
179
+ ? { reason: "available", remediation: null }
180
+ : {
181
+ reason: "policy_disabled",
182
+ remediation: "Enable active Electron automation and configure exact executable and application roots.",
183
+ };
184
+ return policy.electronObservationEnabled === true
185
+ ? { reason: "available", remediation: null }
186
+ : {
187
+ reason: "policy_disabled",
188
+ remediation: "Enable Electron observation and configure a loopback CDP endpoint and canonical file roots.",
189
+ };
190
+ };
176
191
  const browserPolicyAvailability = (name, kind, policy) => {
177
192
  if (name === "capture_browser_scenario")
178
193
  return policy.browserScenarioEnabled === true
@@ -240,6 +255,9 @@ const composedAvailability = (name, descriptors) => {
240
255
  reason: "provider_missing",
241
256
  remediation: "No provider composition is declared for this operation.",
242
257
  };
258
+ return composedAvailabilityFor(requirements, descriptors);
259
+ };
260
+ const composedAvailabilityFor = (requirements, descriptors) => {
243
261
  const missing = requirements.find((operation) => !descriptors.has(operation));
244
262
  if (missing !== undefined)
245
263
  return {
@@ -258,3 +276,70 @@ const composedAvailability = (name, descriptors) => {
258
276
  };
259
277
  return { reason: "available", remediation: null };
260
278
  };
279
+ const navigationContextAvailability = (descriptors) => {
280
+ const modeResults = NAVIGATION_CONTEXT_MODES.map((mode) => {
281
+ const availability = composedAvailabilityFor(mode.requirements, descriptors);
282
+ if (availability.reason === "available")
283
+ return {
284
+ name: mode.name,
285
+ available: true,
286
+ reason: availability.reason,
287
+ missing_operations: [],
288
+ remediation: null,
289
+ };
290
+ return {
291
+ name: mode.name,
292
+ available: false,
293
+ reason: availability.reason,
294
+ missing_operations: mode.requirements.filter((operation) => {
295
+ const descriptor = descriptors.get(operation);
296
+ return descriptor === undefined || descriptor.available === false;
297
+ }),
298
+ remediation: availability.remediation,
299
+ };
300
+ });
301
+ const modes = modeResults.map((mode) => mode.available
302
+ ? {
303
+ name: mode.name,
304
+ available: true,
305
+ missing_operations: [],
306
+ remediation: null,
307
+ }
308
+ : {
309
+ name: mode.name,
310
+ available: false,
311
+ missing_operations: [...mode.missing_operations],
312
+ remediation: mode.remediation,
313
+ });
314
+ const availableMode = modeResults.find((mode) => mode.available);
315
+ const failures = modeResults.flatMap((mode) => mode.available ? [] : [mode]);
316
+ const failure = failures.sort((left, right) => navigationFailurePriority(left.reason) -
317
+ navigationFailurePriority(right.reason))[0];
318
+ const specificFailureRemediation = failure?.reason === "provider_missing" ? undefined : failure?.remediation;
319
+ const facts = {
320
+ defaultModeAvailable: modes[0]?.available ?? false,
321
+ modes,
322
+ };
323
+ if (availableMode !== undefined)
324
+ return { ...facts, reason: "available", remediation: null };
325
+ return {
326
+ ...facts,
327
+ reason: failure?.reason ?? "provider_missing",
328
+ remediation: specificFailureRemediation ??
329
+ `Configure a provider for one of: ${modes
330
+ .flatMap((mode) => mode.missing_operations)
331
+ .join(", ")}.`,
332
+ };
333
+ };
334
+ const navigationFailurePriority = (reason) => {
335
+ switch (reason) {
336
+ case "unsupported_host":
337
+ return 0;
338
+ case "provider_unavailable":
339
+ return 1;
340
+ case "provider_missing":
341
+ return 2;
342
+ default:
343
+ return 3;
344
+ }
345
+ };
@@ -3,6 +3,7 @@ import { resolve } from "node:path";
3
3
  import { parse as parseToml } from "smol-toml";
4
4
  import { z } from "zod";
5
5
  import { PRODUCT_IDENTITY } from "../identity.js";
6
+ import { MCP_STARTUP_POLICY } from "../mcpStartupPolicy.js";
6
7
  import { supportedClients } from "./SupportedClients.js";
7
8
  const objectSchema = z.record(z.string(), z.unknown());
8
9
  const registrationSchema = z
@@ -41,7 +42,9 @@ export const readClientRegistrationStatuses = async (home, currentCommandPath =
41
42
  };
42
43
  const registrationAligned = (registration, client, currentCommandPath) => {
43
44
  const command = [registration.command, ...registration.args];
44
- if (client === "codex" && registration.startup_timeout_sec !== 30)
45
+ if (client === "codex" &&
46
+ registration.startup_timeout_sec !==
47
+ MCP_STARTUP_POLICY.codexStartupTimeoutSeconds)
45
48
  return false;
46
49
  if (command.length === 4 &&
47
50
  command[0] === "npx" &&
@@ -6,6 +6,7 @@ import { err, ok } from "../domain/result.js";
6
6
  import { paginateArtifactInventory, } from "./ArtifactInventory.js";
7
7
  import { scanAuthorizedArtifactInventory } from "./AuthorizedArtifactInventory.js";
8
8
  import { ARTIFACT_GRAPH_PROVIDER } from "./InvestigationProviders.js";
9
+ import { resolveArtifactIntegrityPolicy } from "./ArtifactInventory/policy.js";
9
10
  export const AUTOMATIC_RUN_LIMITATION = "Automatic cross-version runs compare shipped artifact structure only; they do not execute either version.";
10
11
  /** Start both independent version scans while preserving left/right identity. */
11
12
  export const scanVersionInventories = async (options, scanner = scanAuthorizedArtifactInventory) => {
@@ -27,12 +28,12 @@ export const scanVersions = async (input, inputRoots, signal, integrityContinueE
27
28
  if (signal?.aborted === true)
28
29
  return err(new AnalysisCancelledError("find_changed_behavior"));
29
30
  const limits = artifactLimits(input.options);
30
- const integrity = {
31
- mode: input.integrity_policy,
32
- approved: input.integrity_continue_approved,
33
- enabled: integrityContinueEnabled,
34
- maxMismatches: input.max_integrity_mismatches,
35
- };
31
+ const integrity = resolveArtifactIntegrityPolicy(input.integrity_policy === "fail"
32
+ ? { mode: "fail" }
33
+ : {
34
+ mode: input.integrity_policy,
35
+ maxMismatches: input.max_integrity_mismatches,
36
+ }, integrityContinueEnabled);
36
37
  return ok(await scanVersionInventories({
37
38
  leftPath: input.left_path,
38
39
  rightPath: input.right_path,
@@ -182,6 +182,7 @@ const executeAnalysisTool = async (input) => {
182
182
  const { session, tool, signal, evidenceProfile } = input;
183
183
  if (tool === "binary_overview" ||
184
184
  tool === "analyze_function" ||
185
+ tool === "inspect_native_api" ||
185
186
  tool === "trace_feature") {
186
187
  const result = await new EnhancedTools(session).execute(tool, input.arguments, signal);
187
188
  if (!result.ok)
@@ -247,15 +248,18 @@ const prepareSnapshot = async (options) => {
247
248
  return err(new EvidenceIntegrityError("Analysis snapshot target does not match the requested binary"));
248
249
  return ok({ snapshot: loaded.value });
249
250
  };
250
- const replayProviderFor = (session, tool) => tool === "binary_overview" || tool === "trace_feature"
251
+ const replayProviderFor = (session, tool) => isWorkflowEvidenceTool(tool)
251
252
  ? REA_WORKFLOW_PROVIDER
252
253
  : session.providerIdentity(tool);
253
254
  const analysisProfileForEvidence = (session, tool) => {
254
- if (tool !== "binary_overview" && tool !== "trace_feature")
255
+ if (!isWorkflowEvidenceTool(tool))
255
256
  return session.analysisProfile(tool);
256
257
  const upstream = session.analysisProfile();
257
258
  return upstream === undefined ? undefined : workflowAnalysisProfile(upstream);
258
259
  };
260
+ const isWorkflowEvidenceTool = (tool) => tool === "binary_overview" ||
261
+ tool === "inspect_native_api" ||
262
+ tool === "trace_feature";
259
263
  const fileExists = async (path) => {
260
264
  try {
261
265
  await access(path);
@@ -0,0 +1,55 @@
1
+ import { createEvidence } from "../domain/evidence.js";
2
+ import { jsonValueSchema } from "../domain/jsonValue.js";
3
+ import { digestJson } from "./JavaScriptReplayPlanning.js";
4
+ /** Create Evidence v2 without retaining arbitrary runtime argument values. */
5
+ export const createElectronActiveEvidence = (input, result, provider) => createEvidence(undefined, provider, {
6
+ predicateType: "rea.electron-active-scenario/v1",
7
+ operation: "capture_electron_scenario",
8
+ parameters: parameters(input),
9
+ result: jsonValueSchema.parse(result),
10
+ confidence: "observed",
11
+ authority: "controlled-replay",
12
+ environment: {
13
+ id: `${result.application.electron_version}@${process.platform}`,
14
+ platform: process.platform,
15
+ architecture: process.arch,
16
+ isolation: "none",
17
+ },
18
+ limitations: result.limitations,
19
+ });
20
+ const scenarioProjection = (input) => ({
21
+ approved: input.approved,
22
+ schema_version: input.schema_version,
23
+ executable_path: input.executable_path,
24
+ application_path: input.application_path,
25
+ application_root: input.application_root,
26
+ args: redactArguments(input.args),
27
+ actions: input.actions.map(({ step_id, kind }) => ({ step_id, kind })),
28
+ limits: input.limits,
29
+ });
30
+ const parameters = (input) => ({
31
+ ...scenarioProjection(input),
32
+ scenario_sha256: digestJson(scenarioProjection(input)),
33
+ });
34
+ const redactArguments = (arguments_) => {
35
+ let redactNext = false;
36
+ return arguments_.map((argument) => {
37
+ if (redactNext) {
38
+ redactNext = false;
39
+ return "<redacted>";
40
+ }
41
+ if (sensitiveFlag.test(argument)) {
42
+ redactNext = true;
43
+ return argument;
44
+ }
45
+ return redactArgument(argument);
46
+ });
47
+ };
48
+ const sensitiveFlag = /^--?(?:password|token|secret|api[-_]?key|authorization|cookie)$/iu;
49
+ const redactArgument = (argument) => {
50
+ if (/^--?(?:password|token|secret|api[-_]?key|authorization|cookie)=/iu.test(argument))
51
+ return `${argument.slice(0, argument.indexOf("=") + 1)}<redacted>`;
52
+ return argument.length > 256
53
+ ? `${argument.slice(0, 256)}<redacted>`
54
+ : argument;
55
+ };
@@ -0,0 +1 @@
1
+ export {};