rea-agents 2.4.0 → 3.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 (231) hide show
  1. package/README.md +91 -19
  2. package/bridge/ghidra/ReaGhidraBridge.java +524 -4
  3. package/bridge/hopper_bridge.py +313 -28
  4. package/dist/application/AnalysisClientCleanup.js +14 -0
  5. package/dist/application/AnalysisContextQueries.js +130 -0
  6. package/dist/application/AnalysisSnapshotCache.js +4 -2
  7. package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
  8. package/dist/application/ArtifactInventory/scanCanonical.js +1 -1
  9. package/dist/application/BinarySession.js +20 -8
  10. package/dist/application/BinarySessionComposition.js +21 -0
  11. package/dist/application/BinarySessionRecords.js +79 -9
  12. package/dist/application/BinarySessionStatus.js +31 -1
  13. package/dist/application/BrowserEvidence.js +14 -0
  14. package/dist/application/BrowserScenarioCapturePort.js +1 -0
  15. package/dist/application/BrowserScenarioCaptureService.js +53 -0
  16. package/dist/application/BrowserScenarioEvidence.js +40 -0
  17. package/dist/application/CallPathTracing.js +160 -0
  18. package/dist/application/CapabilityInventory.js +84 -14
  19. package/dist/application/ClientRegistrationStatus.js +4 -1
  20. package/dist/application/CompositeProvider.js +10 -1
  21. package/dist/application/DirectAnalysis.js +13 -4
  22. package/dist/application/EnhancedLiteralTracing.js +118 -0
  23. package/dist/application/EnhancedTools.js +71 -127
  24. package/dist/application/EvidenceLedger.js +45 -16
  25. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
  26. package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
  27. package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
  28. package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
  29. package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
  30. package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
  31. package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
  32. package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
  33. package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
  34. package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
  35. package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
  36. package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
  37. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
  38. package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
  39. package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
  40. package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
  41. package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
  42. package/dist/application/LazyAnalysisProvider.js +77 -0
  43. package/dist/application/LoopbackReplay.js +1 -1
  44. package/dist/application/NativeApiInspection.js +80 -0
  45. package/dist/application/ProcessReactiveEffects.js +6 -0
  46. package/dist/application/ProcessReactiveHarness.js +16 -1
  47. package/dist/application/ProjectPermissionStore.js +1 -0
  48. package/dist/application/ReconstructionObligationAuthority.js +72 -0
  49. package/dist/application/ReconstructionObligationCandidates.js +265 -0
  50. package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
  51. package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
  52. package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
  53. package/dist/application/ReconstructionObligationLedgerService.js +72 -0
  54. package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
  55. package/dist/application/ReconstructionObligationPolicies.js +132 -0
  56. package/dist/application/ReconstructionReadinessService.js +48 -0
  57. package/dist/application/SessionProviderRouter.js +6 -6
  58. package/dist/application/SetupClientConfiguration.js +4 -2
  59. package/dist/application/runtime.js +26 -9
  60. package/dist/artifacts/ArtifactProvider.js +87 -22
  61. package/dist/browser/BrowserScenarioSecrets.js +53 -0
  62. package/dist/browser/BrowserScenarioSessionPort.js +1 -0
  63. package/dist/browser/CdpBrowserProvider.js +2 -2
  64. package/dist/browser/CdpCaptureDocuments.js +57 -0
  65. package/dist/browser/CdpCaptureStorage.js +71 -29
  66. package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
  67. package/dist/browser/CdpEndpoint.js +8 -6
  68. package/dist/browser/CdpPageCapture.js +2 -0
  69. package/dist/browser/JavaScriptRuntimeScope.js +38 -0
  70. package/dist/browser/PlaywrightBrowserScenarioProvider.js +259 -0
  71. package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
  72. package/dist/browser/PlaywrightScenarioActions.js +62 -0
  73. package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
  74. package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
  75. package/dist/browser/PlaywrightScenarioEvents.js +301 -0
  76. package/dist/browser/PlaywrightScenarioSession.js +300 -0
  77. package/dist/browser/V8InspectorCaptureProjection.js +110 -0
  78. package/dist/browser/V8InspectorEndpoint.js +72 -0
  79. package/dist/browser/V8InspectorProvider.js +267 -0
  80. package/dist/catalogIdentity.js +11 -3
  81. package/dist/cli/artifactCommands.js +44 -3
  82. package/dist/cli/coreAnalysisCommands.js +78 -0
  83. package/dist/cli.js +4 -0
  84. package/dist/cliApplicationCommands.js +30 -2
  85. package/dist/cliBrowserAdvancedCommands.js +17 -5
  86. package/dist/cliBrowserCommands.js +1 -0
  87. package/dist/cliBrowserScenarioCommands.js +43 -0
  88. package/dist/cliCommandNames.js +9 -0
  89. package/dist/cliJavaScriptRuntimeCommands.js +164 -0
  90. package/dist/cliObservationOptions.js +4 -0
  91. package/dist/cliPolicyCommands.js +1 -0
  92. package/dist/config/environment.js +12 -0
  93. package/dist/config/parseConfig.js +88 -17
  94. package/dist/contracts/analysisSearchContract.js +14 -0
  95. package/dist/contracts/applicationToolContracts.js +86 -2
  96. package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
  97. package/dist/contracts/artifactToolContracts.js +29 -2
  98. package/dist/contracts/browserProviderToolContracts.js +7 -0
  99. package/dist/contracts/browserScenarioToolContracts.js +81 -0
  100. package/dist/contracts/browserToolContracts.js +88 -98
  101. package/dist/contracts/enhancedInputs.js +35 -6
  102. package/dist/contracts/functionInstructionContract.js +8 -0
  103. package/dist/contracts/functionWorkflowToolContracts.js +30 -0
  104. package/dist/contracts/hopperMemoryContracts.js +22 -0
  105. package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
  106. package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
  107. package/dist/contracts/promptContracts.js +14 -15
  108. package/dist/contracts/reconstructionReadinessExample.js +436 -0
  109. package/dist/contracts/toolContractExamples.js +8 -0
  110. package/dist/contracts/toolContractTypes.js +1 -0
  111. package/dist/contracts/toolContracts.js +34 -25
  112. package/dist/contracts/toolEffects.js +25 -0
  113. package/dist/contracts/toolOutputSchemaGroups.js +120 -30
  114. package/dist/contracts/toolOutputSchemaPrimitives.js +37 -0
  115. package/dist/domain/analysisErrorPresentation.js +8 -2
  116. package/dist/domain/analysisErrorProjection.js +9 -0
  117. package/dist/domain/artifactInspection.js +347 -0
  118. package/dist/domain/browserCaptureComparison.js +82 -0
  119. package/dist/domain/browserObservation.js +8 -1
  120. package/dist/domain/browserObservationErrors.js +1 -0
  121. package/dist/domain/browserObservationSchemas.js +26 -0
  122. package/dist/domain/browserScenario.js +165 -0
  123. package/dist/domain/browserScenarioCapture.js +57 -0
  124. package/dist/domain/browserScenarioCaptureValues.js +204 -0
  125. package/dist/domain/browserScenarioDiff.js +324 -0
  126. package/dist/domain/browserScenarioDiffValues.js +157 -0
  127. package/dist/domain/browserScenarioNormalization.js +45 -0
  128. package/dist/domain/browserScenarioValues.js +372 -0
  129. package/dist/domain/bytecodeProvider.js +179 -0
  130. package/dist/domain/conformancePackage.js +169 -0
  131. package/dist/domain/conformanceReplay.js +104 -0
  132. package/dist/domain/conformanceTrustGate.js +193 -0
  133. package/dist/domain/customProtocolCapture.js +137 -0
  134. package/dist/domain/eventProcessTree.js +214 -0
  135. package/dist/domain/evidenceBundle.js +47 -0
  136. package/dist/domain/hopperStartupFailure.js +1 -1
  137. package/dist/domain/hopperValues.js +14 -1
  138. package/dist/domain/inputIssueProjection.js +7 -1
  139. package/dist/domain/javascriptRuntimeObservation.js +170 -0
  140. package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
  141. package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
  142. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
  143. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  144. package/dist/domain/javascriptSemanticAnalysis.js +13 -16
  145. package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
  146. package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
  147. package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
  148. package/dist/domain/javascriptSemanticDataEffects.js +284 -0
  149. package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
  150. package/dist/domain/javascriptSemanticFingerprints.js +174 -0
  151. package/dist/domain/javascriptSemanticIr.js +21 -1
  152. package/dist/domain/javascriptSemanticObjects.js +115 -0
  153. package/dist/domain/javascriptSemanticPromises.js +290 -0
  154. package/dist/domain/javascriptSemanticResources.js +136 -0
  155. package/dist/domain/javascriptSourceParser.js +15 -0
  156. package/dist/domain/javascriptStaticAnalysis.js +12 -15
  157. package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
  158. package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
  159. package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
  160. package/dist/domain/mobileApplicationInvestigation.js +159 -0
  161. package/dist/domain/nativeApiBoundary.js +130 -0
  162. package/dist/domain/objcSwiftMetadata.js +150 -0
  163. package/dist/domain/packageAnalysis.js +123 -0
  164. package/dist/domain/peInspection.js +196 -0
  165. package/dist/domain/processCapture.js +8 -2
  166. package/dist/domain/processReactiveScenario.js +1 -0
  167. package/dist/domain/processReactiveTransition.js +9 -4
  168. package/dist/domain/protocolCapture.js +243 -0
  169. package/dist/domain/providerCleanupError.js +11 -0
  170. package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
  171. package/dist/domain/reconstructionReadiness.js +121 -0
  172. package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
  173. package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
  174. package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
  175. package/dist/domain/reconstructionReadinessSchemas.js +240 -0
  176. package/dist/domain/sourceToBundleComparison.js +335 -0
  177. package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
  178. package/dist/domain/sourceToBundleSignals.js +202 -0
  179. package/dist/domain/webCaptureDiff.js +141 -27
  180. package/dist/domain/webScreenshot.js +1 -1
  181. package/dist/dotnet/ManagedStaticProvider.js +34 -5
  182. package/dist/generatedMcpToolCatalog.js +37 -0
  183. package/dist/generatedPackageMetadata.js +4 -4
  184. package/dist/ghidra/GhidraDefaults.js +1 -1
  185. package/dist/ghidra/GhidraFunctionValues.js +40 -2
  186. package/dist/ghidra/GhidraProviderCapabilities.js +47 -38
  187. package/dist/hopper/HopperCleanup.js +158 -0
  188. package/dist/hopper/HopperClient.js +93 -99
  189. package/dist/hopper/HopperProvider.js +61 -30
  190. package/dist/hopper/HopperRequestQueue.js +215 -0
  191. package/dist/hopper/HopperResponseStream.js +46 -0
  192. package/dist/hopper/protocol.js +63 -20
  193. package/dist/main/transport.js +24 -27
  194. package/dist/main.js +14 -6
  195. package/dist/mcpDoctor.js +27 -4
  196. package/dist/mcpStartupPolicy.js +14 -0
  197. package/dist/process/ProcessCommandIdentity.js +19 -0
  198. package/dist/process/ProcessOwnership.js +11 -8
  199. package/dist/server/DynamicToolAvailability.js +70 -0
  200. package/dist/server/LazyToolCatalog.js +134 -0
  201. package/dist/server/createServer.js +92 -154
  202. package/dist/server/hydrateServerTools.js +191 -0
  203. package/dist/server/lifecycleProgress.js +10 -0
  204. package/dist/server/permissionFailure.js +12 -0
  205. package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
  206. package/dist/server/registerApplicationTools/helpers.js +11 -7
  207. package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
  208. package/dist/server/registerApplicationTools/readiness.js +55 -0
  209. package/dist/server/registerApplicationTools.js +6 -0
  210. package/dist/server/registerArtifactTools.js +9 -1
  211. package/dist/server/registerBrowserScenarioTool.js +25 -0
  212. package/dist/server/registerBrowserTools.js +2 -2
  213. package/dist/server/registerCloseLifecycleTool.js +57 -0
  214. package/dist/server/registerEnhancedTools.js +28 -7
  215. package/dist/server/registerEvidenceResources.js +54 -101
  216. package/dist/server/registerEvidenceTools.js +9 -2
  217. package/dist/server/registerInvestigationTools.js +1 -2
  218. package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
  219. package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
  220. package/dist/server/registerReconstructionObligationLedgerResource.js +32 -0
  221. package/dist/server/registerReconstructionReadinessResource.js +32 -0
  222. package/dist/server/registerSessionRecordTools.js +82 -12
  223. package/dist/server/registerSessionStatusTool.js +11 -8
  224. package/dist/server/registerSessionTools.js +32 -58
  225. package/dist/server/sessionAvailabilityPolicy.js +10 -8
  226. package/dist/server/toolRegistrationOptions.js +21 -6
  227. package/dist/server/toolResult.js +12 -0
  228. package/package.json +28 -11
  229. package/scripts/package-runner-bootstrap.mjs +72 -0
  230. package/scripts/rea.mjs +11 -1
  231. package/skills/reverse-engineer-anything/SKILL.md +10 -3
@@ -1,11 +1,12 @@
1
1
  import { z } from "zod";
2
- import { compareApplicationVersionsRequestSchema, compareJavaScriptExportShapesRequestSchema, traceApplicationFeatureRequestSchema, traceJavaScriptSemanticsRequestSchema, } from "../contracts/applicationWorkflowInputContracts.js";
2
+ import { compareApplicationVersionsRequestSchema, compareJavaScriptExportShapesRequestSchema, compareSourceToBundleRequestSchema, traceApplicationFeatureRequestSchema, traceJavaScriptSemanticsRequestSchema, } from "../contracts/applicationWorkflowInputContracts.js";
3
3
  import { AnalysisInputError } from "../domain/errors.js";
4
4
  import { projectInputIssues } from "../domain/inputIssueProjection.js";
5
5
  import { compareApplicationVersionsInputSchema } from "../domain/javascriptApplicationVersionComparisonSchemas.js";
6
6
  import { compareJavaScriptExportShapesInputSchema } from "../domain/javascriptExportShapeComparisonSchemas.js";
7
7
  import { traceApplicationFeatureInputSchema } from "../domain/javascriptFeatureTraceSchemas.js";
8
8
  import { traceJavaScriptSemanticsInputSchema } from "../domain/javascriptSemanticTraceSchemas.js";
9
+ import { compareSourceToBundleInputSchema } from "../domain/sourceToBundleComparisonSchemas.js";
9
10
  import { err, ok } from "../domain/result.js";
10
11
  import { resolveEvidenceReferences, } from "./EvidenceReferenceResolver.js";
11
12
  const APPLICATION_GRAPH_IDENTITIES = [
@@ -35,18 +36,14 @@ export const resolveTraceApplicationFeatureRequest = (input, lookup) => {
35
36
  };
36
37
  /** Resolve a trace request already parsed by the caller-facing contract. */
37
38
  export const resolveTraceApplicationFeatureRequestValidated = (input, lookup) => {
38
- const application = input.application === undefined
39
- ? resolveEvidenceReferences(lookup, input.application_evidence_id === undefined
40
- ? []
41
- : [input.application_evidence_id], APPLICATION_GRAPH_IDENTITIES)
42
- : ok([input.application]);
39
+ const application = graphEvidence(input.application, input.application_evidence_id, lookup);
43
40
  if (!application.ok)
44
41
  return application;
45
42
  const native = resolveEvidenceReferences(lookup, input.native_observation_evidence_ids);
46
43
  if (!native.ok)
47
44
  return native;
48
45
  const raw = {
49
- application: application.value[0],
46
+ application: application.value,
50
47
  native_observations: [...input.native_observations, ...native.value],
51
48
  seed: input.seed,
52
49
  direction: input.direction,
@@ -66,14 +63,10 @@ export const resolveTraceJavaScriptSemanticsRequest = (input, lookup) => {
66
63
  };
67
64
  /** Resolve a semantic trace request already parsed by its public contract. */
68
65
  export const resolveTraceJavaScriptSemanticsRequestValidated = (input, lookup) => {
69
- const application = input.application === undefined
70
- ? resolveEvidenceReferences(lookup, input.application_evidence_id === undefined
71
- ? []
72
- : [input.application_evidence_id], APPLICATION_GRAPH_IDENTITIES)
73
- : ok([input.application]);
66
+ const application = graphEvidence(input.application, input.application_evidence_id, lookup);
74
67
  if (!application.ok)
75
68
  return application;
76
- const raw = { application: application.value[0], query: input.query };
69
+ const raw = { application: application.value, query: input.query };
77
70
  const parsed = traceJavaScriptSemanticsInputSchema.safeParse(raw);
78
71
  return parsed.success
79
72
  ? ok(parsed.data)
@@ -121,6 +114,31 @@ export const resolveCompareApplicationVersionsRequestValidated = (input, lookup)
121
114
  ? ok(parsed.data)
122
115
  : invalid("compare_application_versions", parsed.error, raw);
123
116
  };
117
+ /** Parse and resolve one historical-source comparison adapter request. */
118
+ export const resolveCompareSourceToBundleRequest = (input, lookup) => {
119
+ const parsed = compareSourceToBundleRequestSchema.safeParse(input);
120
+ return parsed.success
121
+ ? resolveCompareSourceToBundleRequestValidated(parsed.data, lookup)
122
+ : invalid("compare_source_to_bundle", parsed.error, input);
123
+ };
124
+ /** Resolve a historical-source comparison request parsed by its public contract. */
125
+ export const resolveCompareSourceToBundleRequestValidated = (input, lookup) => {
126
+ const application = graphEvidence(input.application, input.application_evidence_id, lookup);
127
+ if (!application.ok)
128
+ return application;
129
+ const raw = {
130
+ reference: input.reference,
131
+ application: application.value,
132
+ limits: input.limits,
133
+ ...(input.unknown_registry_approved === undefined
134
+ ? {}
135
+ : { unknown_registry_approved: input.unknown_registry_approved }),
136
+ };
137
+ const parsed = compareSourceToBundleInputSchema.safeParse(raw);
138
+ return parsed.success
139
+ ? ok(parsed.data)
140
+ : invalid("compare_source_to_bundle", parsed.error, raw);
141
+ };
124
142
  /** Parse and resolve an export-shape adapter request into canonical input. */
125
143
  export const resolveCompareJavaScriptExportShapesRequest = (input, lookup) => {
126
144
  const parsed = compareJavaScriptExportShapesRequestSchema.safeParse(input);
@@ -12,7 +12,7 @@ export const scanCanonicalArtifactInventory = async (path, limits, options = {})
12
12
  const integrity = options.integrity ?? STRICT_INTEGRITY_POLICY;
13
13
  if (integrity.mode === "record-and-continue" &&
14
14
  (!integrity.approved || !integrity.enabled))
15
- throw new ArtifactReaderFailure("unavailable", "Integrity continuation requires explicit approval and operator policy");
15
+ throw new ArtifactReaderFailure("policy", "Integrity continuation requires explicit approval and operator policy");
16
16
  const metadata = await lstat(path);
17
17
  const rootFormat = await classifyRoot(path, metadata.isDirectory());
18
18
  const rootDigest = metadata.isDirectory()
@@ -2,14 +2,15 @@ import { randomUUID } from "node:crypto";
2
2
  import { analysisProfilesEqual, } from "../domain/analysisProfile.js";
3
3
  import { AnalysisCancelledError, ProviderAdapterError, } from "../domain/errors.js";
4
4
  import { err, ok } from "../domain/result.js";
5
- import { enhancedToolNameSchema } from "../contracts/enhancedInputs.js";
6
- import { OFFICIAL_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
5
+ import { GENERATED_MCP_TOOL_CATALOG } from "../generatedMcpToolCatalog.js";
7
6
  import { SessionProviderRouter, } from "./SessionProviderRouter.js";
8
7
  import { BinarySessionRecords } from "./BinarySessionRecords.js";
9
8
  import { binarySessionStatus } from "./BinarySessionStatus.js";
10
9
  import { resolveSessionOpen, } from "./BinarySessionOpen.js";
11
10
  import { prepareSessionExecution } from "./BinarySessionExecution.js";
12
- const OFFICIAL_OPERATIONS = new Set(OFFICIAL_TOOL_CONTRACTS.map(({ name }) => name));
11
+ import { closeAnalysisClient } from "./AnalysisClientCleanup.js";
12
+ const OFFICIAL_OPERATIONS = new Set(GENERATED_MCP_TOOL_CATALOG.filter(({ kind }) => kind === "official-proxy").map(({ name }) => name));
13
+ const ENHANCED_OPERATIONS = new Set(GENERATED_MCP_TOOL_CATALOG.filter(({ kind }) => kind === "enhanced").map(({ name }) => name));
13
14
  /**
14
15
  * Owns the single active target shared by CLI and MCP adapters.
15
16
  *
@@ -41,7 +42,7 @@ export class BinarySession extends BinarySessionRecords {
41
42
  const exact = route.capabilities?.get(operation)?.provider;
42
43
  if (exact !== undefined)
43
44
  selected = exact;
44
- else if (enhancedToolNameSchema.safeParse(operation).success) {
45
+ else if (ENHANCED_OPERATIONS.has(operation)) {
45
46
  const providers = new Map();
46
47
  for (const descriptor of route.capabilities?.values() ?? [])
47
48
  if (descriptor.available &&
@@ -111,12 +112,20 @@ export class BinarySession extends BinarySessionRecords {
111
112
  if (!imported.ok)
112
113
  return imported;
113
114
  }
115
+ this.resetSnapshotInvalidation();
114
116
  return ok(target);
115
117
  }
116
118
  await this.#drainCalls();
117
119
  const previous = this.#active;
118
120
  this.#active = undefined;
119
- await previous?.client.close();
121
+ if (previous !== undefined) {
122
+ const closed = await closeAnalysisClient(previous.client, previous.route.identity.id);
123
+ if (!closed.ok) {
124
+ this.clearSessionRecords();
125
+ this.#clearRuntimeAvailability();
126
+ return closed;
127
+ }
128
+ }
120
129
  const runId = randomUUID();
121
130
  const client = route.createClient(target, { runId });
122
131
  const started = await client.execute("health", {}, options);
@@ -157,15 +166,17 @@ export class BinarySession extends BinarySessionRecords {
157
166
  });
158
167
  }
159
168
  /** Close the active target, if any. */
160
- close() {
169
+ close(options = {}) {
161
170
  return this.#serialize(async () => {
162
171
  const previous = this.#active;
163
172
  this.#active = undefined;
164
173
  await this.#drainCalls();
165
- await previous?.client.close();
174
+ const closed = previous === undefined
175
+ ? ok(null)
176
+ : await closeAnalysisClient(previous.client, previous.route.identity.id, options);
166
177
  this.clearSessionRecords();
167
178
  this.#clearRuntimeAvailability();
168
- return ok(null);
179
+ return closed;
169
180
  });
170
181
  }
171
182
  /** Describe the active binary session. */
@@ -177,6 +188,7 @@ export class BinarySession extends BinarySessionRecords {
177
188
  runtimeAvailability: this.#runtimeAvailability,
178
189
  runId: this.#active?.runId,
179
190
  runtimeLineageSnapshots: this.#active?.client.runtimeLineageSnapshots?.() ?? [],
191
+ requestActivitySnapshots: this.#active?.client.requestActivitySnapshots?.() ?? [],
180
192
  });
181
193
  }
182
194
  /** Return the immutable artifact identity captured before its provider started. */
@@ -0,0 +1,21 @@
1
+ import { AnalysisProviderRegistry } from "./AnalysisProviderRegistry.js";
2
+ import { BinarySession } from "./BinarySession.js";
3
+ import { SessionProviderRouter } from "./SessionProviderRouter.js";
4
+ const normalizeBinarySessionOptions = (optionsOrResolver) => typeof optionsOrResolver === "function"
5
+ ? { resolveAnalysisProfile: optionsOrResolver }
6
+ : (optionsOrResolver ?? {});
7
+ /**
8
+ * Compose one provider-neutral session from deep-provider selection and
9
+ * disjoint auxiliary operation families.
10
+ */
11
+ export const composeBinarySession = (registry, auxiliaryProviders = []) => new BinarySession(registry instanceof SessionProviderRouter
12
+ ? registry
13
+ : SessionProviderRouter.selectable(registry, auxiliaryProviders));
14
+ /**
15
+ * Compose a session through the same internal seam for focused adapter tests.
16
+ * The constructor remains private to this composition module, so callers test
17
+ * provider behavior without coupling to BinarySession construction.
18
+ */
19
+ export const composeBinarySessionFromFactory = (provider, optionsOrResolver) => new BinarySession(provider, normalizeBinarySessionOptions(optionsOrResolver));
20
+ /** Compose a focused session from any provider-shaped test seam. */
21
+ export const composeBinarySessionFromProvider = (provider, optionsOrResolver) => new BinarySession(provider, normalizeBinarySessionOptions(optionsOrResolver));
@@ -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,38 @@ 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
+ uri: `rea://evidence-bundle/${digest}`,
67
+ });
68
+ }
69
+ retainedEvidenceBundle(digest) {
70
+ return this.#retainedBundles.get(digest);
71
+ }
32
72
  importEvidenceBundle(bundle) {
33
- return this.#evidence.import(bundle);
73
+ const imported = this.#evidence.import(bundle);
74
+ if (imported.ok && imported.value > 0)
75
+ this.#emitSnapshotChanged();
76
+ return imported;
34
77
  }
35
78
  exportAnalysisSnapshot() {
36
79
  const active = this.activeAnalysisBinding();
@@ -48,36 +91,53 @@ export class BinarySessionRecords {
48
91
  const active = this.activeAnalysisBinding();
49
92
  if (active?.profile === null)
50
93
  return err(new EvidenceIntegrityError("Analysis snapshot profile_mismatch: the active target has no concrete analysis profile"));
51
- return this.#snapshot.import(snapshot, active === undefined
94
+ const imported = this.#snapshot.import(snapshot, active === undefined
52
95
  ? undefined
53
96
  : { target: active.target, profile: active.profile }, (bundle) => this.#evidence.import(bundle));
97
+ if (imported.ok)
98
+ this.#emitSnapshotChanged();
99
+ return imported;
54
100
  }
55
101
  matchesSnapshot(target, profile) {
56
102
  return this.#snapshot.matches(target, profile ?? undefined);
57
103
  }
58
104
  selectSnapshot(target, profile) {
59
105
  this.#snapshot.select(target, profile);
106
+ this.#emitSnapshotChanged();
60
107
  }
61
108
  lookupSnapshot(target, profile, operation, parameters) {
62
109
  return this.#snapshot.lookup(target, profile, operation, parameters);
63
110
  }
64
111
  recordSnapshot(input) {
65
112
  this.#snapshot.record(input);
113
+ this.#emitSnapshotChanged();
66
114
  }
67
115
  invalidateSnapshot() {
68
116
  this.#snapshot.clear();
69
117
  this.#snapshotInvalidated = true;
118
+ this.#emitSnapshotChanged();
70
119
  }
71
120
  resetSnapshotInvalidation() {
121
+ if (!this.#snapshotInvalidated)
122
+ return;
72
123
  this.#snapshotInvalidated = false;
124
+ this.#emitSnapshotChanged();
73
125
  }
74
126
  clearSnapshot() {
75
127
  this.#snapshot.clear();
128
+ this.#emitSnapshotChanged();
76
129
  }
77
130
  clearSessionRecords() {
78
131
  this.#evidence.clear();
79
132
  this.#snapshot.clear();
80
133
  this.#snapshotInvalidated = false;
134
+ this.#retainedBundles.clear();
135
+ this.#retainedBundleBytes = 0;
136
+ this.#emitSnapshotChanged();
137
+ }
138
+ #emitSnapshotChanged() {
139
+ for (const listener of this.#snapshotListeners)
140
+ void listener();
81
141
  }
82
142
  retainInvestigationWorkspace(workspace) {
83
143
  const parsed = parseInvestigationWorkspace(workspace);
@@ -111,10 +171,16 @@ export class BinarySessionRecords {
111
171
  }
112
172
  recordUnknown(input) {
113
173
  const target = this.activeAnalysisBinding()?.target;
114
- return this.#evidence.recordUnknown(input, unknownMutationEvidence(target, input));
174
+ const recorded = this.#evidence.recordUnknown(input, unknownMutationEvidence(target, input));
175
+ if (recorded.ok)
176
+ this.#emitSnapshotChanged();
177
+ return recorded;
115
178
  }
116
179
  recordEvidenceWithUnknown(evidence, input) {
117
- return this.#evidence.recordWithUnknown(evidence, input, unknownMutationEvidence(undefined, input));
180
+ const recorded = this.#evidence.recordWithUnknown(evidence, input, unknownMutationEvidence(undefined, input));
181
+ if (recorded.ok)
182
+ this.#emitSnapshotChanged();
183
+ return recorded;
118
184
  }
119
185
  updateUnknown(input) {
120
186
  const target = this.activeAnalysisBinding()?.target;
@@ -133,7 +199,10 @@ export class BinarySessionRecords {
133
199
  "Registry mutation evidence records analyst intent, not proof of the answer.",
134
200
  ],
135
201
  });
136
- return this.#evidence.updateUnknown(input, evidence);
202
+ const updated = this.#evidence.updateUnknown(input, evidence);
203
+ if (updated.ok)
204
+ this.#emitSnapshotChanged();
205
+ return updated;
137
206
  }
138
207
  listUnknowns(filters = {}) {
139
208
  return this.#evidence.listUnknowns(filters);
@@ -147,3 +216,4 @@ const sortedWorkspaces = (workspaces) => [...workspaces]
147
216
  .sort((left, right) => left.workspace_id.localeCompare(right.workspace_id) ||
148
217
  left.revision - right.revision)
149
218
  .map((workspace) => structuredClone(workspace));
219
+ import { createHash } from "node:crypto";
@@ -1,5 +1,5 @@
1
1
  /** Project internal provider routing state into the caller-visible session status. */
2
- export const binarySessionStatus = ({ target, route, router, runtimeAvailability, runId, runtimeLineageSnapshots, }) => {
2
+ export const binarySessionStatus = ({ target, route, router, runtimeAvailability, runId, runtimeLineageSnapshots, requestActivitySnapshots, }) => {
3
3
  const configuredProvider = router.configuredIdentity();
4
4
  const provider = providerSummary(configuredProvider);
5
5
  const providers = router.providerIdentities(route).map(providerSummary);
@@ -41,6 +41,7 @@ export const binarySessionStatus = ({ target, route, router, runtimeAvailability
41
41
  run_id: runId,
42
42
  process_lineage: processLineageStatus(runtimeLineageSnapshots),
43
43
  },
44
+ analysis_activity: requestActivityStatus(requestActivitySnapshots),
44
45
  };
45
46
  return target === undefined
46
47
  ? { open: false, ...common }
@@ -54,6 +55,35 @@ export const binarySessionStatus = ({ target, route, router, runtimeAvailability
54
55
  architecture: target.architecture ?? null,
55
56
  };
56
57
  };
58
+ const requestActivityStatus = (snapshots) => {
59
+ const providers = snapshots
60
+ .slice()
61
+ .sort((left, right) => left.provider.id.localeCompare(right.provider.id))
62
+ .map(({ provider, active, queuedRequests }) => ({
63
+ provider: providerSummary(provider),
64
+ active: active === null
65
+ ? null
66
+ : {
67
+ request_id: active.requestId,
68
+ operation: active.operation,
69
+ elapsed_ms: active.elapsedMs,
70
+ timeout_ms: active.timeoutMs,
71
+ caller_state: active.callerState,
72
+ },
73
+ queued_requests: queuedRequests,
74
+ }));
75
+ const active = snapshots.flatMap((snapshot) => snapshot.active === null ? [] : [snapshot.active]);
76
+ return {
77
+ status: snapshots.length === 0
78
+ ? "not_observed"
79
+ : active.some(({ callerState }) => callerState === "timed_out")
80
+ ? "timed_out_busy"
81
+ : active.length > 0
82
+ ? "busy"
83
+ : "idle",
84
+ providers,
85
+ };
86
+ };
57
87
  const providerSummary = ({ id, name, version, }) => ({ id, name, version });
58
88
  const processLineageStatus = (snapshots) => snapshots.length === 0
59
89
  ? { status: "not_observed" }
@@ -1,5 +1,6 @@
1
1
  import { createEvidence, } from "../domain/evidence.js";
2
2
  import { jsonValueSchema } from "../domain/jsonValue.js";
3
+ import { commitBrowserScenarioNormalization } from "../domain/browserScenarioNormalization.js";
3
4
  /** Create Evidence v2 for a policy-scoped external browser observation. */
4
5
  export const createBrowserEvidence = (operation, input, result, provider) => createEvidence(undefined, provider, {
5
6
  predicateType: browserPredicate(operation),
@@ -20,6 +21,18 @@ export const createBrowserEvidence = (operation, input, result, provider) => cre
20
21
  });
21
22
  const browserParameters = (input) => {
22
23
  if (!("cdp_endpoint" in input)) {
24
+ if ("before_scenario" in input &&
25
+ input.before_scenario !== undefined &&
26
+ input.after_scenario !== undefined)
27
+ return {
28
+ comparison_kind: "browser_scenario",
29
+ before_browser: input.before_scenario.browser,
30
+ after_browser: input.after_scenario.browser,
31
+ before_start_origin: input.before_scenario.scenario.start_origin,
32
+ after_start_origin: input.after_scenario.scenario.start_origin,
33
+ max_changes: input.max_changes,
34
+ normalization_sha256: commitBrowserScenarioNormalization(input.normalization).sha256,
35
+ };
23
36
  if ("max_changes" in input)
24
37
  return {
25
38
  before_target_id: input.before.inspection.target.target_id,
@@ -74,6 +87,7 @@ const browserParameters = (input) => {
74
87
  include_websocket_shapes: input.include_websocket_shapes,
75
88
  include_script_sources: input.include_script_sources,
76
89
  include_storage_keys: input.include_storage_keys,
90
+ include_storage_fingerprints: input.include_storage_fingerprints,
77
91
  limits: input.limits,
78
92
  ...(input.include_script_sources && "source_capture_approved" in input
79
93
  ? {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
1
+ import { createBrowserScenarioEvidence } from "./BrowserScenarioEvidence.js";
2
+ import { digestJson } from "./JavaScriptReplayPlanning.js";
3
+ import { AnalysisCapabilityUnavailableError, AnalysisProtocolError, PermissionRequiredError, } from "../domain/errors.js";
4
+ import { isLiteralLoopbackHostname } from "../domain/browserObservation.js";
5
+ import { err } from "../domain/result.js";
6
+ const OPERATION = "capture_browser_scenario";
7
+ const PROVIDER_ID = "rea-playwright-browser-scenario";
8
+ const networkScope = (origins) => origins.every((origin) => isLiteralLoopbackHostname(new URL(origin).hostname))
9
+ ? "loopback"
10
+ : "external";
11
+ const authorizeScenario = async (authority, scenario) => {
12
+ if (authority === undefined)
13
+ return err(new AnalysisCapabilityUnavailableError(PROVIDER_ID, OPERATION, "browser scenario permission policy is not configured"));
14
+ const authorized = await authority.authorize({
15
+ capability: "browser_automate",
16
+ roots: [],
17
+ executables: scenario.browser.mode === "launch"
18
+ ? [scenario.browser.executable_path]
19
+ : [],
20
+ environment_names: scenario.secrets.map(({ environment_variable: name }) => name),
21
+ origins: [
22
+ ...(scenario.browser.mode === "connect"
23
+ ? [scenario.browser.cdp_endpoint]
24
+ : []),
25
+ ...scenario.allowed_origins,
26
+ ],
27
+ network: networkScope(scenario.allowed_origins),
28
+ mount: false,
29
+ operation_identity: `${OPERATION}:${digestJson(scenario)}`,
30
+ }, "write");
31
+ if (!authorized.ok)
32
+ return err(authorized.error instanceof PermissionRequiredError
33
+ ? authorized.error
34
+ : new AnalysisProtocolError(authorized.error.message, {
35
+ cause: authorized.error,
36
+ }));
37
+ return { ok: true, value: true };
38
+ };
39
+ /** Authorize and execute one controlled browser scenario. */
40
+ export const captureBrowserScenario = async (provider, authority, scenario, options = {}) => {
41
+ const authorized = await authorizeScenario(authority, scenario);
42
+ if (!authorized.ok)
43
+ return authorized;
44
+ if (provider === undefined)
45
+ return err(new AnalysisCapabilityUnavailableError(PROVIDER_ID, OPERATION, "browser scenario provider is not configured"));
46
+ const captured = await provider.captureScenario(scenario, options);
47
+ return captured.ok
48
+ ? {
49
+ ok: true,
50
+ value: createBrowserScenarioEvidence(scenario, captured.value, provider.identity()),
51
+ }
52
+ : captured;
53
+ };
@@ -0,0 +1,40 @@
1
+ import { sanitizeBrowserUrl } from "../domain/browserObservation.js";
2
+ import { createEvidence, } from "../domain/evidence.js";
3
+ import { jsonValueSchema } from "../domain/jsonValue.js";
4
+ import { digestJson } from "./JavaScriptReplayPlanning.js";
5
+ const browserScenarioParameters = (scenario) => ({
6
+ scenario_sha256: digestJson(scenario),
7
+ browser_mode: scenario.browser.mode,
8
+ start_url: sanitizeBrowserUrl(scenario.start_url.url),
9
+ allowed_origins: scenario.allowed_origins,
10
+ actions: scenario.actions.map(({ step_id, action }) => ({
11
+ step_id,
12
+ action,
13
+ })),
14
+ secret_declarations: scenario.secrets.map(({ secret_id, environment_variable, purpose, redaction }) => ({
15
+ secret_id,
16
+ environment_variable,
17
+ purpose,
18
+ redaction,
19
+ })),
20
+ capture: scenario.capture,
21
+ limits: scenario.limits,
22
+ });
23
+ /** Create Evidence v2 without retaining resolved scenario secret values. */
24
+ export const createBrowserScenarioEvidence = (scenario, capture, provider) => createEvidence(undefined, provider, {
25
+ predicateType: "rea.browser-scenario-capture/v1",
26
+ operation: "capture_browser_scenario",
27
+ parameters: browserScenarioParameters(scenario),
28
+ result: jsonValueSchema.parse(capture),
29
+ confidence: "observed",
30
+ authority: "controlled-replay",
31
+ environment: {
32
+ id: `${capture.browser.product}@${capture.browser.version}`,
33
+ platform: process.platform,
34
+ architecture: process.arch,
35
+ isolation: capture.browser.process_ownership === "provider-owned"
36
+ ? "process"
37
+ : "none",
38
+ },
39
+ limitations: capture.limitations,
40
+ });