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
@@ -3,7 +3,8 @@ import canonicalize from "canonicalize";
3
3
  import { z } from "zod";
4
4
  import { webPageInspectionSchema, } from "./browserObservation.js";
5
5
  import { webMcpDiscoverySchema, } from "./webMcpDiscovery.js";
6
- const captureSnapshotSchema = z.object({
6
+ /** One normalized passive page snapshot accepted by capture comparison. */
7
+ export const captureSnapshotSchema = z.object({
7
8
  inspection: webPageInspectionSchema,
8
9
  webmcp: webMcpDiscoverySchema.nullable().default(null),
9
10
  });
@@ -24,6 +25,13 @@ const dimensionSchema = z.object({
24
25
  omitted_changes: z.number().int().min(0),
25
26
  reason: z.string().nullable(),
26
27
  });
28
+ const legacyUnknownDimension = {
29
+ status: "unknown",
30
+ total_changes: 0,
31
+ changes: [],
32
+ omitted_changes: 0,
33
+ reason: "Dimension was not recorded by this version 1 capture diff.",
34
+ };
27
35
  /** Completeness-aware changes across stable browser evidence dimensions. */
28
36
  export const webCaptureDiffSchema = z.object({
29
37
  schema_version: z.literal(1),
@@ -37,39 +45,17 @@ export const webCaptureDiffSchema = z.object({
37
45
  network: dimensionSchema,
38
46
  metadata: dimensionSchema,
39
47
  webmcp: dimensionSchema,
48
+ accessibility: dimensionSchema.default(legacyUnknownDimension),
49
+ storage: dimensionSchema.default(legacyUnknownDimension),
40
50
  }),
41
51
  limitations: z.array(z.string()),
42
52
  });
43
53
  /** Compare normalized observations without treating incomplete absence as proof. */
44
54
  export const compareWebCaptures = (input) => {
45
- let remaining = input.max_changes;
46
- const dimension = (before, after, complete, reason) => {
47
- const all = compareIdentities(before, after);
48
- const retained = all.slice(0, remaining);
49
- remaining -= retained.length;
50
- return {
51
- status: all.length > 0 ? "changed" : complete ? "unchanged" : "unknown",
52
- total_changes: all.length,
53
- changes: retained,
54
- omitted_changes: all.length - retained.length,
55
- reason: all.length === 0 && !complete ? reason : null,
56
- };
57
- };
55
+ const remaining = { value: input.max_changes };
58
56
  const before = input.before.inspection;
59
57
  const after = input.after.inspection;
60
- const dimensions = {
61
- dom_structure: dimension(singleton("document", digest(domProjection(before))), singleton("document", digest(domProjection(after))), sectionsComplete(before, ["frames", "dom"]) &&
62
- sectionsComplete(after, ["frames", "dom"]), "DOM or frame capture was incomplete in at least one observation."),
63
- scripts: dimension(keyed(before.scripts.items.map(scriptProjection), (item) => item.script_key), keyed(after.scripts.items.map(scriptProjection), (item) => item.script_key), sectionsComplete(before, ["scripts"]) &&
64
- sectionsComplete(after, ["scripts"]), "Script inventory was incomplete in at least one observation."),
65
- resources: dimension(keyed(before.resources, (item) => item.resource_key), keyed(after.resources, (item) => item.resource_key), sectionsComplete(before, ["resources"]) &&
66
- sectionsComplete(after, ["resources"]), "Resource inventory was incomplete in at least one observation."),
67
- network: dimension(networkMap(before), networkMap(after), sectionsComplete(before, ["network_requests"]) &&
68
- sectionsComplete(after, ["network_requests"]), "Network capture is attach-window limited or incomplete."),
69
- metadata: dimension(singleton("metadata", digest(metadataProjection(before))), singleton("metadata", digest(metadataProjection(after))), sectionsComplete(before, ["metadata"]) &&
70
- sectionsComplete(after, ["metadata"]), "Safe metadata capture was incomplete in at least one observation."),
71
- webmcp: dimension(webMcpMap(input.before.webmcp), webMcpMap(input.after.webmcp), webMcpComplete(input.before.webmcp) && webMcpComplete(input.after.webmcp), "WebMCP discovery was unavailable or incomplete in at least one capture."),
72
- };
58
+ const dimensions = compareWebCaptureDimensions(input, remaining);
73
59
  const statuses = Object.values(dimensions).map(({ status }) => status);
74
60
  return webCaptureDiffSchema.parse({
75
61
  schema_version: 1,
@@ -90,6 +76,8 @@ export const compareWebCaptures = (input) => {
90
76
  limitations: [
91
77
  "A changed status proves an observed difference; an unknown status means absence could not be established from capture completeness.",
92
78
  "Network comparison covers only activity observed after each CDP attachment.",
79
+ "Accessibility roles, ignored state, text, and hierarchy are compared only when the accessibility tree was fully captured and text capture was approved and not truncated.",
80
+ "Storage key inventories are compared only when approved and complete; usage and quota are compared only when reported. Redacted content is compared through complete SHA-256 fingerprints.",
93
81
  ],
94
82
  });
95
83
  };
@@ -108,6 +96,54 @@ const compareIdentities = (before, after) => {
108
96
  return changes.sort((left, right) => left.identity.localeCompare(right.identity) ||
109
97
  left.change.localeCompare(right.change));
110
98
  };
99
+ const compareDimension = (remaining) => (before, after, complete, reason) => {
100
+ const all = compareIdentities(before, after);
101
+ const retained = all.slice(0, remaining.value);
102
+ remaining.value -= retained.length;
103
+ return {
104
+ status: all.length > 0 ? "changed" : complete ? "unchanged" : "unknown",
105
+ total_changes: all.length,
106
+ changes: retained,
107
+ omitted_changes: all.length - retained.length,
108
+ reason: all.length === 0 && !complete ? reason : null,
109
+ };
110
+ };
111
+ const accessibilityDimension = (before, after, remaining) => {
112
+ const beforeAccess = accessibilityComparable(before);
113
+ const afterAccess = accessibilityComparable(after);
114
+ const textComparable = beforeAccess.text && afterAccess.text;
115
+ const nodesComparable = beforeAccess.nodes && afterAccess.nodes;
116
+ return compareDimension(remaining)(singleton("accessibility_tree", digest(accessibilityProjection(before.accessibility, textComparable, nodesComparable))), singleton("accessibility_tree", digest(accessibilityProjection(after.accessibility, textComparable, nodesComparable))), beforeAccess.complete && afterAccess.complete, "Accessibility tree or text capture was incomplete in at least one observation.");
117
+ };
118
+ const storageDimension = (before, after, remaining) => {
119
+ const keysComplete = storageKeysComparable(before) && storageKeysComparable(after);
120
+ const usageComplete = before.storage.usage_bytes !== null &&
121
+ after.storage.usage_bytes !== null &&
122
+ before.storage.quota_bytes !== null &&
123
+ after.storage.quota_bytes !== null;
124
+ const comparableFingerprints = storageFingerprintIdentities(before.storage, after.storage);
125
+ return compareDimension(remaining)(storageMap(before.storage, keysComplete, usageComplete, comparableFingerprints), storageMap(after.storage, keysComplete, usageComplete, comparableFingerprints), storageComparable(before, after, keysComplete, usageComplete), "Storage fingerprints, inventories, usage, or quota were unavailable or truncated in at least one observation.");
126
+ };
127
+ const compareWebCaptureDimensions = (input, remaining) => {
128
+ const before = input.before.inspection;
129
+ const after = input.after.inspection;
130
+ const dimension = compareDimension(remaining);
131
+ return {
132
+ dom_structure: dimension(singleton("document", digest(domProjection(before))), singleton("document", digest(domProjection(after))), sectionsComplete(before, ["frames", "dom"]) &&
133
+ sectionsComplete(after, ["frames", "dom"]), "DOM or frame capture was incomplete in at least one observation."),
134
+ scripts: dimension(keyed(before.scripts.items.map(scriptProjection), (item) => item.script_key), keyed(after.scripts.items.map(scriptProjection), (item) => item.script_key), sectionsComplete(before, ["scripts"]) &&
135
+ sectionsComplete(after, ["scripts"]), "Script inventory was incomplete in at least one observation."),
136
+ resources: dimension(keyed(before.resources, (item) => item.resource_key), keyed(after.resources, (item) => item.resource_key), sectionsComplete(before, ["resources"]) &&
137
+ sectionsComplete(after, ["resources"]), "Resource inventory was incomplete in at least one observation."),
138
+ network: dimension(networkMap(before), networkMap(after), sectionsComplete(before, ["network_requests"]) &&
139
+ sectionsComplete(after, ["network_requests"]), "Network capture is attach-window limited or incomplete."),
140
+ metadata: dimension(singleton("metadata", digest(metadataProjection(before))), singleton("metadata", digest(metadataProjection(after))), sectionsComplete(before, ["metadata"]) &&
141
+ sectionsComplete(after, ["metadata"]), "Safe metadata capture was incomplete in at least one observation."),
142
+ webmcp: dimension(webMcpMap(input.before.webmcp), webMcpMap(input.after.webmcp), webMcpComplete(input.before.webmcp) && webMcpComplete(input.after.webmcp), "WebMCP discovery was unavailable or incomplete in at least one capture."),
143
+ accessibility: accessibilityDimension(before, after, remaining),
144
+ storage: storageDimension(before, after, remaining),
145
+ };
146
+ };
111
147
  const keyed = (values, identity) => new Map(values.map((value) => [identity(value), digest(value)]));
112
148
  const networkMap = (inspection) => {
113
149
  const grouped = new Map();
@@ -182,3 +218,81 @@ const digest = (value) => {
182
218
  throw new TypeError("Expected canonical JSON");
183
219
  return createHash("sha256").update(encoded).digest("hex");
184
220
  };
221
+ const accessibilityComparable = (inspection) => {
222
+ const text = inspection.accessibility.text_capture.status === "included" &&
223
+ inspection.accessibility.text_capture.excluded_fields === 0 &&
224
+ inspection.accessibility.text_capture.truncated_fields === 0;
225
+ const nodes = inspection.accessibility.total_nodes ===
226
+ inspection.accessibility.nodes.length;
227
+ const complete = text && nodes && sectionsComplete(inspection, ["accessibility"]);
228
+ return { text, nodes, complete };
229
+ };
230
+ const accessibilityProjection = (accessibility, includeText, includeNodes) => {
231
+ const result = {
232
+ total_nodes: accessibility.total_nodes,
233
+ };
234
+ if (!includeNodes)
235
+ return result;
236
+ const nodeIndexes = new Map(accessibility.nodes.map((node, index) => [node.node_id, index]));
237
+ result.nodes = accessibility.nodes.map((node) => ({
238
+ parent_index: node.parent_id === null ? null : (nodeIndexes.get(node.parent_id) ?? -1),
239
+ role: node.role,
240
+ ignored: node.ignored,
241
+ states: node.states,
242
+ ...(includeText ? { name: node.name, description: node.description } : {}),
243
+ }));
244
+ return result;
245
+ };
246
+ const storageKeysComparable = (inspection) => sectionsComplete(inspection, ["storage_keys"]);
247
+ const storageComparable = (before, after, keysComplete, usageComplete) => keysComplete &&
248
+ usageComplete &&
249
+ before.storage.fingerprints_complete &&
250
+ after.storage.fingerprints_complete;
251
+ const storageMap = (storage, includeKeys, includeUsage, fingerprintIdentities) => {
252
+ const map = new Map([
253
+ [
254
+ "storage:summary",
255
+ digest({
256
+ origin: storage.origin,
257
+ values_redacted: storage.values_redacted,
258
+ ...(includeUsage
259
+ ? {
260
+ usage_bytes: storage.usage_bytes,
261
+ quota_bytes: storage.quota_bytes,
262
+ }
263
+ : {}),
264
+ }),
265
+ ],
266
+ ]);
267
+ if (!includeKeys)
268
+ return map;
269
+ const add = (kind, keys) => {
270
+ for (const key of keys) {
271
+ map.set(`storage:${kind}:${key}`, digest(key));
272
+ }
273
+ };
274
+ add("local_storage", storage.local_storage_keys);
275
+ add("session_storage", storage.session_storage_keys);
276
+ add("indexed_db", storage.indexed_db_names);
277
+ add("cache", storage.cache_names);
278
+ for (const fingerprint of storage.content_fingerprints) {
279
+ const identity = `${fingerprint.scope}:${fingerprint.identity_sha256}`;
280
+ if (!fingerprintIdentities.has(identity))
281
+ continue;
282
+ map.set(`storage:content:${identity}`, digest(fingerprint.value_sha256));
283
+ }
284
+ return map;
285
+ };
286
+ const storageFingerprintIdentities = (before, after) => {
287
+ const beforeComplete = completeStorageFingerprints(before);
288
+ const afterComplete = completeStorageFingerprints(after);
289
+ if (before.fingerprints_complete && after.fingerprints_complete)
290
+ return new Set([...beforeComplete.keys(), ...afterComplete.keys()]);
291
+ return new Set([...beforeComplete.keys()].filter((identity) => afterComplete.has(identity)));
292
+ };
293
+ const completeStorageFingerprints = (storage) => new Map(storage.content_fingerprints
294
+ .filter(({ complete }) => complete)
295
+ .map(({ scope, identity_sha256, value_sha256 }) => [
296
+ `${scope}:${identity_sha256}`,
297
+ value_sha256,
298
+ ]));
@@ -4,7 +4,7 @@ import { browserCompletenessSchema } from "./browserCompleteness.js";
4
4
  import { browserAllowedOriginsSchema, browserEndpointSchema, } from "./browserObservation.js";
5
5
  const MAX_SCREENSHOT_BYTES = 8 * 1_024 * 1_024;
6
6
  /** Self-verifying inline PNG artifact for CLI/MCP parity. */
7
- const webScreenshotArtifactSchema = z
7
+ export const webScreenshotArtifactSchema = z
8
8
  .object({
9
9
  uri: z.string().regex(/^rea:\/\/web-screenshot\/sha256\/[a-f0-9]{64}$/u),
10
10
  sha256: z.string().regex(/^[a-f0-9]{64}$/u),
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { createReadStream } from "node:fs";
2
3
  import { readFile, stat } from "node:fs/promises";
3
4
  import { createAnalysisExecution, } from "../application/AnalysisProvider.js";
4
5
  import { MANAGED_STATIC_PROVIDER } from "../application/InvestigationProviders.js";
@@ -48,6 +49,7 @@ export class ManagedStaticProvider {
48
49
  }
49
50
  class ManagedStaticClient {
50
51
  target;
52
+ #snapshotBytes;
51
53
  constructor(target) {
52
54
  this.target = target;
53
55
  }
@@ -65,12 +67,18 @@ class ManagedStaticClient {
65
67
  const metadata = await stat(this.target.path);
66
68
  if (metadata.size > maxFileBytes)
67
69
  return err(new AnalysisCapabilityUnavailableError(IDENTITY.id, operation, `Artifact size ${String(metadata.size)} exceeds max_file_bytes ${String(maxFileBytes)}.`));
68
- const bytes = await readFile(this.target.path, options?.signal === undefined ? undefined : { signal: options.signal });
69
- if (bytes.length > maxFileBytes)
70
+ const snapshot = this.#snapshotBytes;
71
+ const observed = snapshot === undefined
72
+ ? await readManagedSnapshot(this.target.path, options?.signal)
73
+ : await hashManagedSource(this.target.path, options?.signal);
74
+ if (observed.byteLength > maxFileBytes)
70
75
  return err(new AnalysisCapabilityUnavailableError(IDENTITY.id, operation, `Artifact grew beyond max_file_bytes ${String(maxFileBytes)} while it was read.`));
71
- const observedDigest = createHash("sha256").update(bytes).digest("hex");
72
- if (observedDigest !== this.target.sha256)
73
- return err(new EvidenceIntegrityError(`Managed artifact digest changed after open: expected ${this.target.sha256}, observed ${observedDigest} at ${this.target.path}`));
76
+ if (observed.sha256 !== this.target.sha256)
77
+ return err(new EvidenceIntegrityError(`Managed artifact digest changed after open: expected ${this.target.sha256}, observed ${observed.sha256} at ${this.target.path}`));
78
+ const bytes = snapshot ?? observed.bytes;
79
+ if (bytes === undefined)
80
+ throw new TypeError("Managed snapshot bytes are unavailable");
81
+ this.#snapshotBytes = bytes;
74
82
  const result = inspectManagedOperation(operation, parameters, bytes, this.target);
75
83
  return ok(createAnalysisExecution(result, IDENTITY, {
76
84
  rawResult: null,
@@ -86,9 +94,30 @@ class ManagedStaticClient {
86
94
  }
87
95
  }
88
96
  close() {
97
+ this.#snapshotBytes = undefined;
89
98
  return Promise.resolve();
90
99
  }
91
100
  }
101
+ const readManagedSnapshot = async (path, signal) => {
102
+ const bytes = await readFile(path, signal === undefined ? undefined : { signal });
103
+ return {
104
+ byteLength: bytes.length,
105
+ sha256: createHash("sha256").update(bytes).digest("hex"),
106
+ bytes,
107
+ };
108
+ };
109
+ const hashManagedSource = async (path, signal) => {
110
+ const digest = createHash("sha256");
111
+ let byteLength = 0;
112
+ const stream = createReadStream(path, signal === undefined ? undefined : { signal });
113
+ for await (const chunk of stream) {
114
+ if (!Buffer.isBuffer(chunk))
115
+ throw new TypeError("Managed source stream returned non-buffer data");
116
+ byteLength += chunk.length;
117
+ digest.update(chunk);
118
+ }
119
+ return { byteLength, sha256: digest.digest("hex") };
120
+ };
92
121
  const isManagedOperation = (operation) => MANAGED_TOOL_CONTRACTS.some(({ name }) => name === operation);
93
122
  const limitationsFor = (operation) => Object.freeze(operation === "inspect_managed_artifact"
94
123
  ? [