rea-agents 2.3.0 → 2.5.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 (209) hide show
  1. package/README.md +80 -18
  2. package/bridge/ghidra/ReaGhidraBridge.java +48 -1
  3. package/bridge/hopper_bridge.py +259 -24
  4. package/dist/application/AnalysisClientCleanup.js +14 -0
  5. package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
  6. package/dist/application/ArtifactExtraction.js +5 -1
  7. package/dist/application/ArtifactGraphConstruction.js +3 -15
  8. package/dist/application/ArtifactInventory/classify.js +5 -1
  9. package/dist/application/ArtifactInventory/reader.js +2 -0
  10. package/dist/application/BinarySession.js +15 -4
  11. package/dist/application/BinarySessionStatus.js +32 -1
  12. package/dist/application/BrowserEvidence.js +14 -0
  13. package/dist/application/BrowserScenarioCapturePort.js +1 -0
  14. package/dist/application/BrowserScenarioCaptureService.js +53 -0
  15. package/dist/application/BrowserScenarioEvidence.js +40 -0
  16. package/dist/application/CallPathTracing.js +160 -0
  17. package/dist/application/CapabilityInventory.js +79 -15
  18. package/dist/application/CompositeProvider.js +10 -1
  19. package/dist/application/DirectAnalysis.js +7 -2
  20. package/dist/application/EnhancedLiteralTracing.js +118 -0
  21. package/dist/application/EnhancedTools.js +35 -121
  22. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
  23. package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
  24. package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
  25. package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
  26. package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
  27. package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
  28. package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
  29. package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
  30. package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
  31. package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
  32. package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
  33. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
  34. package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
  35. package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
  36. package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
  37. package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
  38. package/dist/application/ProcessCaptureEnvironment.js +26 -0
  39. package/dist/application/ProcessCaptureLifecycle.js +1 -0
  40. package/dist/application/ProcessCheckpoints.js +5 -0
  41. package/dist/application/ProcessHarness.js +45 -34
  42. package/dist/application/ProcessNormalization.js +6 -0
  43. package/dist/application/ProcessReactiveCoordinator.js +10 -3
  44. package/dist/application/ProcessReactiveEffects.js +15 -1
  45. package/dist/application/ProcessReactiveHarness.js +100 -0
  46. package/dist/application/ProcessReactiveObservations.js +101 -16
  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/artifacts/ArtifactProvider.js +87 -22
  58. package/dist/browser/BrowserScenarioSecrets.js +53 -0
  59. package/dist/browser/BrowserScenarioSessionPort.js +1 -0
  60. package/dist/browser/CdpBrowserProvider.js +2 -2
  61. package/dist/browser/CdpCaptureDocuments.js +57 -0
  62. package/dist/browser/CdpCaptureStorage.js +71 -29
  63. package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
  64. package/dist/browser/CdpEndpoint.js +8 -6
  65. package/dist/browser/CdpPageCapture.js +2 -0
  66. package/dist/browser/JavaScriptRuntimeScope.js +38 -0
  67. package/dist/browser/PlaywrightBrowserScenarioProvider.js +253 -0
  68. package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
  69. package/dist/browser/PlaywrightScenarioActions.js +62 -0
  70. package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
  71. package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
  72. package/dist/browser/PlaywrightScenarioEvents.js +301 -0
  73. package/dist/browser/PlaywrightScenarioSession.js +300 -0
  74. package/dist/browser/V8InspectorCaptureProjection.js +110 -0
  75. package/dist/browser/V8InspectorEndpoint.js +72 -0
  76. package/dist/browser/V8InspectorProvider.js +267 -0
  77. package/dist/catalogIdentity.js +8 -0
  78. package/dist/cli/artifactCommands.js +39 -0
  79. package/dist/cli/coreAnalysisCommands.js +37 -0
  80. package/dist/cli.js +4 -0
  81. package/dist/cliApplicationCommands.js +30 -2
  82. package/dist/cliBrowserAdvancedCommands.js +17 -5
  83. package/dist/cliBrowserCommands.js +1 -0
  84. package/dist/cliBrowserScenarioCommands.js +43 -0
  85. package/dist/cliCommandNames.js +8 -0
  86. package/dist/cliJavaScriptRuntimeCommands.js +164 -0
  87. package/dist/cliObservationOptions.js +4 -0
  88. package/dist/cliPolicyCommands.js +1 -0
  89. package/dist/config/environment.js +12 -0
  90. package/dist/config/parseConfig.js +88 -17
  91. package/dist/contracts/analysisSearchContract.js +14 -0
  92. package/dist/contracts/applicationToolContracts.js +86 -2
  93. package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
  94. package/dist/contracts/artifactToolContracts.js +22 -0
  95. package/dist/contracts/browserProviderToolContracts.js +7 -0
  96. package/dist/contracts/browserScenarioToolContracts.js +81 -0
  97. package/dist/contracts/browserToolContracts.js +88 -98
  98. package/dist/contracts/enhancedInputs.js +25 -6
  99. package/dist/contracts/functionInstructionContract.js +8 -0
  100. package/dist/contracts/hopperMemoryContracts.js +22 -0
  101. package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
  102. package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
  103. package/dist/contracts/reconstructionReadinessExample.js +436 -0
  104. package/dist/contracts/toolContractExamples.js +5 -0
  105. package/dist/contracts/toolContractTypes.js +1 -0
  106. package/dist/contracts/toolContracts.js +13 -21
  107. package/dist/contracts/toolEffects.js +21 -0
  108. package/dist/contracts/toolOutputSchemaGroups.js +70 -20
  109. package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
  110. package/dist/domain/analysisErrorProjection.js +8 -0
  111. package/dist/domain/analysisSnapshot.js +2 -0
  112. package/dist/domain/artifactGraph.js +2 -0
  113. package/dist/domain/artifactInspection.js +347 -0
  114. package/dist/domain/binaryTarget.js +3 -6
  115. package/dist/domain/browserCaptureComparison.js +82 -0
  116. package/dist/domain/browserObservation.js +8 -1
  117. package/dist/domain/browserObservationErrors.js +1 -0
  118. package/dist/domain/browserObservationSchemas.js +26 -0
  119. package/dist/domain/browserScenario.js +165 -0
  120. package/dist/domain/browserScenarioCapture.js +57 -0
  121. package/dist/domain/browserScenarioCaptureValues.js +204 -0
  122. package/dist/domain/browserScenarioDiff.js +324 -0
  123. package/dist/domain/browserScenarioDiffValues.js +157 -0
  124. package/dist/domain/browserScenarioNormalization.js +45 -0
  125. package/dist/domain/browserScenarioValues.js +372 -0
  126. package/dist/domain/evidence.js +2 -0
  127. package/dist/domain/hopperValues.js +10 -0
  128. package/dist/domain/javascriptRuntimeObservation.js +170 -0
  129. package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
  130. package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
  131. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
  132. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  133. package/dist/domain/javascriptSemanticAnalysis.js +5 -4
  134. package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
  135. package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
  136. package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
  137. package/dist/domain/javascriptSemanticDataEffects.js +284 -0
  138. package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
  139. package/dist/domain/javascriptSemanticFingerprints.js +174 -0
  140. package/dist/domain/javascriptSemanticIr.js +21 -1
  141. package/dist/domain/javascriptSemanticObjects.js +115 -0
  142. package/dist/domain/javascriptSemanticPromises.js +290 -0
  143. package/dist/domain/javascriptSemanticResources.js +136 -0
  144. package/dist/domain/javascriptStaticAnalysis.js +4 -3
  145. package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
  146. package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
  147. package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
  148. package/dist/domain/processCapture.js +71 -1
  149. package/dist/domain/processCaptureReactiveValidation.js +175 -0
  150. package/dist/domain/processCaptureValidation.js +2 -0
  151. package/dist/domain/processObservation.js +15 -6
  152. package/dist/domain/processReactiveRuntime.js +21 -10
  153. package/dist/domain/processReactiveScenario.js +1 -0
  154. package/dist/domain/processReactiveTransition.js +17 -10
  155. package/dist/domain/processScenario.js +21 -0
  156. package/dist/domain/providerCleanupError.js +11 -0
  157. package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
  158. package/dist/domain/reconstructionReadiness.js +121 -0
  159. package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
  160. package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
  161. package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
  162. package/dist/domain/reconstructionReadinessSchemas.js +240 -0
  163. package/dist/domain/sourceToBundleComparison.js +335 -0
  164. package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
  165. package/dist/domain/sourceToBundleSignals.js +202 -0
  166. package/dist/domain/webCaptureDiff.js +141 -27
  167. package/dist/domain/webScreenshot.js +1 -1
  168. package/dist/domain/zipPackageFormat.js +20 -0
  169. package/dist/generatedPackageMetadata.js +1 -1
  170. package/dist/ghidra/GhidraDefaults.js +1 -1
  171. package/dist/ghidra/GhidraFunctionValues.js +13 -1
  172. package/dist/ghidra/GhidraProviderCapabilities.js +7 -0
  173. package/dist/hopper/HopperCleanup.js +158 -0
  174. package/dist/hopper/HopperClient.js +69 -99
  175. package/dist/hopper/HopperProvider.js +20 -1
  176. package/dist/hopper/HopperRequestQueue.js +187 -0
  177. package/dist/hopper/HopperResponseStream.js +46 -0
  178. package/dist/hopper/protocol.js +1 -0
  179. package/dist/main/transport.js +24 -24
  180. package/dist/main.js +6 -0
  181. package/dist/mcpDoctor.js +25 -2
  182. package/dist/native/NativeMacOSProvider.js +1 -0
  183. package/dist/process/ProcessCommandIdentity.js +19 -0
  184. package/dist/process/ProcessOwnership.js +11 -8
  185. package/dist/server/DynamicToolAvailability.js +70 -0
  186. package/dist/server/createServer.js +46 -1
  187. package/dist/server/lifecycleProgress.js +10 -0
  188. package/dist/server/permissionFailure.js +12 -0
  189. package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
  190. package/dist/server/registerApplicationTools/helpers.js +11 -7
  191. package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
  192. package/dist/server/registerApplicationTools/readiness.js +55 -0
  193. package/dist/server/registerApplicationTools.js +6 -0
  194. package/dist/server/registerArtifactTools.js +9 -1
  195. package/dist/server/registerBrowserScenarioTool.js +25 -0
  196. package/dist/server/registerBrowserTools.js +2 -2
  197. package/dist/server/registerCloseLifecycleTool.js +57 -0
  198. package/dist/server/registerEnhancedTools.js +16 -5
  199. package/dist/server/registerEvidenceResources.js +4 -0
  200. package/dist/server/registerEvidenceTools.js +9 -2
  201. package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
  202. package/dist/server/registerReconstructionObligationLedgerResource.js +39 -0
  203. package/dist/server/registerReconstructionReadinessResource.js +39 -0
  204. package/dist/server/registerSessionStatusTool.js +11 -8
  205. package/dist/server/registerSessionTools.js +11 -57
  206. package/dist/server/sessionAvailabilityPolicy.js +10 -8
  207. package/package.json +12 -6
  208. package/skills/reverse-engineer-anything/SKILL.md +4 -3
  209. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +2 -2
@@ -1,11 +1,13 @@
1
1
  import { createAnalysisExecution, } from "../application/AnalysisProvider.js";
2
2
  import { inventoryArtifact } from "../application/ArtifactInventory.js";
3
3
  import { extractArtifact } from "../application/ArtifactExtraction.js";
4
- import { ARTIFACT_TOOL_CONTRACTS, artifactInventoryInputSchema, artifactExtractionInputSchema, } from "../contracts/artifactToolContracts.js";
4
+ import { ARTIFACT_TOOL_CONTRACTS, artifactInspectionInputSchema, artifactInventoryInputSchema, artifactExtractionInputSchema, } from "../contracts/artifactToolContracts.js";
5
5
  import { AnalysisCapabilityUnavailableError, ArtifactOperationError, } from "../domain/errors.js";
6
6
  import { err, ok } from "../domain/result.js";
7
7
  import { ArtifactReaderFailure, } from "./ArtifactReader.js";
8
8
  import { ARTIFACT_GRAPH_PROVIDER } from "../application/InvestigationProviders.js";
9
+ import { createEvidence } from "../domain/evidence.js";
10
+ import { createArtifactInspection } from "../domain/artifactInspection.js";
9
11
  const IDENTITY = Object.freeze(ARTIFACT_GRAPH_PROVIDER);
10
12
  /** Read-only inventory and exclusively owned extraction provider. */
11
13
  export class ArtifactProvider {
@@ -22,7 +24,9 @@ export class ArtifactProvider {
22
24
  outputContractVersion: 1,
23
25
  available: true,
24
26
  reason: null,
25
- pagination: "offset",
27
+ pagination: contract.name === "inspect_artifact"
28
+ ? "none"
29
+ : "offset",
26
30
  exhaustive: false,
27
31
  effects: Object.freeze({
28
32
  mutatesArtifact: false,
@@ -68,6 +72,10 @@ class ArtifactClient {
68
72
  if (!isArtifactOperation(operation))
69
73
  return err(new AnalysisCapabilityUnavailableError(IDENTITY.id, operation, "Operation is not implemented by artifact graph provider."));
70
74
  try {
75
+ if (operation === "inspect_artifact") {
76
+ const inspected = await this.inspectArtifact(parameters, options);
77
+ return inspected;
78
+ }
71
79
  if (operation === "extract_artifact") {
72
80
  const parsed = artifactExtractionInputSchema.parse(parameters);
73
81
  const result = await extractArtifact({
@@ -90,26 +98,7 @@ class ArtifactClient {
90
98
  }));
91
99
  }
92
100
  const parsed = artifactInventoryInputSchema.parse(parameters);
93
- const result = await inventoryArtifact(this.target.sourcePath ?? this.target.path, limitsFrom(parsed), {
94
- nodeOffset: parsed.node_offset,
95
- nodeLimit: parsed.node_limit,
96
- occurrenceOffset: parsed.occurrence_offset,
97
- occurrenceLimit: parsed.occurrence_limit,
98
- edgeOffset: parsed.edge_offset,
99
- edgeLimit: parsed.edge_limit,
100
- }, {
101
- ...(options?.signal === undefined ? {} : { signal: options.signal }),
102
- nativeMount: {
103
- nativeMountApproved: parsed.native_mount_approved === true,
104
- nativeMountEnabled: this.nativeMountEnabled,
105
- },
106
- integrity: {
107
- mode: parsed.integrity_policy,
108
- approved: parsed.integrity_continue_approved === true,
109
- enabled: this.integrityContinueEnabled,
110
- maxMismatches: parsed.max_integrity_mismatches,
111
- },
112
- });
101
+ const result = await this.inventory(parsed, options);
113
102
  return ok(createAnalysisExecution(result, IDENTITY, {
114
103
  rawResult: null,
115
104
  limitations: result.limitations,
@@ -127,6 +116,82 @@ class ArtifactClient {
127
116
  close() {
128
117
  return Promise.resolve();
129
118
  }
119
+ async inspectArtifact(parameters, options) {
120
+ const parsed = artifactInspectionInputSchema.parse(parameters);
121
+ const inventoryParameters = artifactInventoryInputSchema.parse({
122
+ node_offset: 0,
123
+ node_limit: parsed.max_observations,
124
+ occurrence_offset: 0,
125
+ occurrence_limit: parsed.max_observations,
126
+ edge_offset: 0,
127
+ edge_limit: parsed.max_relationships,
128
+ native_mount_approved: parsed.native_mount_approved,
129
+ integrity_policy: parsed.integrity_policy,
130
+ integrity_continue_approved: parsed.integrity_continue_approved,
131
+ max_integrity_mismatches: parsed.max_integrity_mismatches,
132
+ max_entries: parsed.max_entries,
133
+ max_total_bytes: parsed.max_total_bytes,
134
+ max_entry_bytes: parsed.max_entry_bytes,
135
+ max_compression_ratio: parsed.max_compression_ratio,
136
+ max_depth: parsed.max_depth,
137
+ max_path_bytes: parsed.max_path_bytes,
138
+ });
139
+ await options?.progress?.report({
140
+ phase: "inspect_artifact.inventory",
141
+ completed: 0,
142
+ total: 1,
143
+ message: "inventory substep started",
144
+ });
145
+ const inventory = await this.inventory(inventoryParameters, options);
146
+ const subject = subjectFor(this.target.sourcePath ?? this.target.path, inventory.manifest);
147
+ const locations = inventory.occurrences.items.map(({ logical_path: path }) => ({
148
+ kind: "artifact-path",
149
+ path,
150
+ }));
151
+ const inventoryEvidence = createEvidence(subject, IDENTITY, {
152
+ operation: "inventory_artifact",
153
+ parameters: inventoryParameters,
154
+ result: inventory,
155
+ rawResult: null,
156
+ limitations: inventory.limitations,
157
+ locations,
158
+ });
159
+ const result = createArtifactInspection(inventoryEvidence, parsed);
160
+ await options?.progress?.report({
161
+ phase: "inspect_artifact.inventory",
162
+ completed: 1,
163
+ total: 1,
164
+ message: "inventory substep completed",
165
+ });
166
+ return ok(createAnalysisExecution(result, IDENTITY, {
167
+ rawResult: null,
168
+ limitations: result.limitations,
169
+ subject,
170
+ locations,
171
+ }));
172
+ }
173
+ inventory(parsed, options) {
174
+ return inventoryArtifact(this.target.sourcePath ?? this.target.path, limitsFrom(parsed), {
175
+ nodeOffset: parsed.node_offset,
176
+ nodeLimit: parsed.node_limit,
177
+ occurrenceOffset: parsed.occurrence_offset,
178
+ occurrenceLimit: parsed.occurrence_limit,
179
+ edgeOffset: parsed.edge_offset,
180
+ edgeLimit: parsed.edge_limit,
181
+ }, {
182
+ ...(options?.signal === undefined ? {} : { signal: options.signal }),
183
+ nativeMount: {
184
+ nativeMountApproved: parsed.native_mount_approved === true,
185
+ nativeMountEnabled: this.nativeMountEnabled,
186
+ },
187
+ integrity: {
188
+ mode: parsed.integrity_policy,
189
+ approved: parsed.integrity_continue_approved === true,
190
+ enabled: this.integrityContinueEnabled,
191
+ maxMismatches: parsed.max_integrity_mismatches,
192
+ },
193
+ });
194
+ }
130
195
  }
131
196
  const limitsFrom = (input) => ({
132
197
  maxEntries: input.max_entries,
@@ -0,0 +1,53 @@
1
+ import { createHash } from "node:crypto";
2
+ const REDACTION_PREFIX = "[REDACTED:";
3
+ /** Resolved secret values kept only for one in-memory scenario session. */
4
+ export class BrowserScenarioSecrets {
5
+ values;
6
+ constructor(values) {
7
+ this.values = values;
8
+ }
9
+ static resolve(scenario, environment) {
10
+ const values = new Map();
11
+ for (const declaration of scenario.secrets) {
12
+ const value = environment[declaration.environment_variable];
13
+ if (value === undefined)
14
+ return undefined;
15
+ values.set(declaration.secret_id, value);
16
+ }
17
+ return new BrowserScenarioSecrets(values);
18
+ }
19
+ value(source) {
20
+ if (source.source === "literal")
21
+ return source.value;
22
+ const value = this.values.get(source.secret_id);
23
+ if (value === undefined)
24
+ throw new Error("Validated browser secret was not resolved");
25
+ return value;
26
+ }
27
+ url(destination) {
28
+ const url = new URL(destination.url);
29
+ for (const { name, value } of destination.query)
30
+ url.searchParams.append(name, this.value(value));
31
+ return url.href;
32
+ }
33
+ redact(value) {
34
+ let output = value;
35
+ const replacements = [...this.values].sort(([leftId, left], [rightId, right]) => right.length - left.length ||
36
+ (leftId < rightId ? -1 : leftId > rightId ? 1 : 0));
37
+ for (const [id, secret] of replacements)
38
+ if (secret !== "")
39
+ output = output.replaceAll(secret, `${REDACTION_PREFIX}${id}]`);
40
+ return output;
41
+ }
42
+ fingerprint(value) {
43
+ for (const secret of this.values.values())
44
+ if (secret !== "" && value.includes(secret))
45
+ return { value_state: "redacted-secret", value_sha256: null };
46
+ return {
47
+ value_state: "hashed",
48
+ value_sha256: createHash("sha256")
49
+ .update(this.redact(value))
50
+ .digest("hex"),
51
+ };
52
+ }
53
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -3,7 +3,7 @@ import { analyzeCapturedWebBundle } from "../domain/webBundleAnalyzer.js";
3
3
  import { webBundleAnalysisSchema, } from "../domain/webBundleAnalysis.js";
4
4
  import { webObservationSessionSchema, } from "../domain/browserSession.js";
5
5
  import { webMcpDiscoverySchema, } from "../domain/webMcpDiscovery.js";
6
- import { compareWebCaptures, webCaptureDiffSchema, } from "../domain/webCaptureDiff.js";
6
+ import { browserCaptureComparisonSchema, compareBrowserCaptures, } from "../domain/browserCaptureComparison.js";
7
7
  import { webScreenshotDiffSchema, webScreenshotSchema, } from "../domain/webScreenshot.js";
8
8
  import { AnalysisError, BrowserObservationError, ProviderAdapterError, } from "../domain/errors.js";
9
9
  import { err, ok } from "../domain/result.js";
@@ -136,7 +136,7 @@ export class CdpBrowserProvider {
136
136
  }
137
137
  async compareCaptures(input) {
138
138
  try {
139
- return ok(webCaptureDiffSchema.parse(compareWebCaptures(input)));
139
+ return ok(browserCaptureComparisonSchema.parse(compareBrowserCaptures(input)));
140
140
  }
141
141
  catch (cause) {
142
142
  return err(providerError(cause, "compare_web_captures"));
@@ -156,6 +156,10 @@ export const captureDom = (result, allowedOrigins, input, completeness) => {
156
156
  };
157
157
  export const captureAccessibility = (results, maximum, options) => {
158
158
  const all = results.flatMap((result) => recordsValue(requiredRecord(result).nodes));
159
+ const nodeIds = new Set(all.flatMap((node) => {
160
+ const nodeId = stringValue(node.nodeId);
161
+ return nodeId === undefined ? [] : [nodeId];
162
+ }));
159
163
  let retainedBytes = 0;
160
164
  let excludedFields = 0;
161
165
  let truncatedFields = 0;
@@ -187,6 +191,7 @@ export const captureAccessibility = (results, maximum, options) => {
187
191
  name: captureText(node.name),
188
192
  description: captureText(node.description),
189
193
  ignored: node.ignored === true,
194
+ states: accessibilityStates(node.properties),
190
195
  };
191
196
  });
192
197
  for (const node of all.slice(maximum)) {
@@ -201,6 +206,11 @@ export const captureAccessibility = (results, maximum, options) => {
201
206
  return {
202
207
  total: all.length,
203
208
  nodes,
209
+ treeIncomplete: all.some((node) => Array.isArray(node.childIds) &&
210
+ node.childIds.some((childId) => {
211
+ const id = stringValue(childId);
212
+ return id !== undefined && !nodeIds.has(id);
213
+ })),
204
214
  textCapture: {
205
215
  status: options.unavailable
206
216
  ? "unavailable"
@@ -215,6 +225,53 @@ export const captureAccessibility = (results, maximum, options) => {
215
225
  },
216
226
  };
217
227
  };
228
+ const ACCESSIBILITY_STATE_NAMES = new Set([
229
+ "atomic",
230
+ "autocomplete",
231
+ "busy",
232
+ "checked",
233
+ "disabled",
234
+ "editable",
235
+ "expanded",
236
+ "focusable",
237
+ "focused",
238
+ "hasPopup",
239
+ "hidden",
240
+ "invalid",
241
+ "level",
242
+ "live",
243
+ "modal",
244
+ "multiline",
245
+ "multiselectable",
246
+ "orientation",
247
+ "pressed",
248
+ "readonly",
249
+ "required",
250
+ "selected",
251
+ "valuemax",
252
+ "valuemin",
253
+ ]);
254
+ const accessibilityStates = (value) => {
255
+ const states = new Map();
256
+ for (const property of recordsValue(value)) {
257
+ const name = stringValue(property.name);
258
+ if (name === undefined || !ACCESSIBILITY_STATE_NAMES.has(name))
259
+ continue;
260
+ const raw = recordValue(property.value)?.value;
261
+ const state = typeof raw === "boolean"
262
+ ? raw
263
+ : typeof raw === "number"
264
+ ? numberValue(raw)
265
+ : typeof raw === "string"
266
+ ? raw.slice(0, 256)
267
+ : undefined;
268
+ if (state !== undefined)
269
+ states.set(name, state);
270
+ }
271
+ return [...states]
272
+ .sort(([left], [right]) => left.localeCompare(right))
273
+ .map(([name, state]) => ({ name, value: state }));
274
+ };
218
275
  const axText = (value) => boundedText(recordValue(value)?.value, 1_024);
219
276
  const indexedString = (strings, index) => {
220
277
  const integer = numberValue(index);
@@ -1,53 +1,95 @@
1
1
  import { CdpConnection } from "./CdpConnection.js";
2
2
  import { optionalCdpCommand } from "./CdpOptionalCommand.js";
3
+ import { captureStorageFingerprints, } from "./CdpCaptureStorageFingerprints.js";
3
4
  import { numberValue, recordValue, recordsValue, stringValue, } from "./CdpCaptureValues.js";
4
5
  /** Capture redacted, bounded storage metadata for one authorized origin. */
5
6
  export const captureStorage = async (context, origin, limitations) => {
6
7
  const quota = recordValue(await optionalCdpCommand(context, "Storage.getUsageAndQuota", { origin }, limitations));
7
8
  const local = context.input.include_storage_keys
8
- ? await storageKeys(context, origin, true, limitations)
9
- : [];
9
+ ? await storageItems(context, origin, true, limitations)
10
+ : emptyStorageItems;
10
11
  const session = context.input.include_storage_keys
11
- ? await storageKeys(context, origin, false, limitations)
12
- : [];
13
- const indexed = context.input.include_storage_keys
14
- ? stringArray(recordValue(await optionalCdpCommand(context, "IndexedDB.requestDatabaseNames", { securityOrigin: origin }, limitations))?.databaseNames)
15
- : [];
16
- const caches = context.input.include_storage_keys
17
- ? recordsValue(recordValue(await optionalCdpCommand(context, "CacheStorage.requestCacheNames", { securityOrigin: origin }, limitations))?.caches).flatMap((cache) => {
18
- const name = stringValue(cache.cacheName);
19
- return name === undefined ? [] : [name.slice(0, 1_024)];
20
- })
21
- : [];
12
+ ? await storageItems(context, origin, false, limitations)
13
+ : emptyStorageItems;
14
+ const indexedRaw = context.input.include_storage_keys
15
+ ? await optionalCdpCommand(context, "IndexedDB.requestDatabaseNames", { securityOrigin: origin }, limitations)
16
+ : undefined;
17
+ const indexedValues = recordValue(indexedRaw)?.databaseNames;
18
+ const indexed = stringArray(indexedValues);
19
+ const cacheRaw = context.input.include_storage_keys
20
+ ? await optionalCdpCommand(context, "CacheStorage.requestCacheNames", { securityOrigin: origin }, limitations)
21
+ : undefined;
22
+ const cacheValues = recordsValue(recordValue(cacheRaw)?.caches);
23
+ const caches = capturedCaches(cacheValues);
22
24
  const maximum = context.input.limits.max_storage_keys;
25
+ const fingerprints = context.input.include_storage_fingerprints
26
+ ? await captureStorageFingerprints({
27
+ context,
28
+ origin,
29
+ local,
30
+ session,
31
+ indexedDbNames: indexed.slice(0, maximum),
32
+ caches: caches.slice(0, maximum),
33
+ limitations,
34
+ })
35
+ : { items: [], complete: false, truncated: false };
36
+ const truncated = [local.items, session.items, indexed, caches].some((items) => items.length > maximum) || fingerprints.truncated;
37
+ const structuredStorageComplete = indexedRaw !== undefined &&
38
+ Array.isArray(indexedValues) &&
39
+ indexed.length === indexedValues.length &&
40
+ cacheRaw !== undefined &&
41
+ caches.length === cacheValues.length;
23
42
  return {
24
43
  value: {
25
44
  origin,
26
45
  usage_bytes: numberValue(quota?.usage) ?? null,
27
46
  quota_bytes: numberValue(quota?.quota) ?? null,
28
- local_storage_keys: local.slice(0, maximum),
29
- session_storage_keys: session.slice(0, maximum),
30
- indexed_db_names: indexed.slice(0, maximum),
31
- cache_names: caches.slice(0, maximum),
47
+ local_storage_keys: local.items
48
+ .map(({ key }) => key.slice(0, 1_024))
49
+ .slice(0, maximum),
50
+ session_storage_keys: session.items
51
+ .map(({ key }) => key.slice(0, 1_024))
52
+ .slice(0, maximum),
53
+ indexed_db_names: indexed
54
+ .map((name) => name.slice(0, 1_024))
55
+ .slice(0, maximum),
56
+ cache_names: caches
57
+ .map(({ name }) => name.slice(0, 1_024))
58
+ .slice(0, maximum),
59
+ content_fingerprints: [...fingerprints.items],
60
+ fingerprint_algorithm: "sha256",
61
+ fingerprints_complete: fingerprints.complete && structuredStorageComplete && !truncated,
32
62
  values_redacted: true,
33
63
  },
34
- truncated: [local, session, indexed, caches].some((items) => items.length > maximum),
64
+ truncated,
35
65
  };
36
66
  };
37
- const storageKeys = async (context, origin, isLocalStorage, limitations) => {
38
- const result = recordValue(await optionalCdpCommand(context, "DOMStorage.getDOMStorageItems", { storageId: { securityOrigin: origin, isLocalStorage } }, limitations));
39
- return Array.isArray(result?.entries)
40
- ? result.entries.flatMap((entry) => {
41
- if (!Array.isArray(entry))
42
- return [];
43
- const key = stringValue(entry[0]);
44
- return key === undefined ? [] : [key.slice(0, 1_024)];
45
- })
46
- : [];
67
+ const emptyStorageItems = {
68
+ items: [],
69
+ complete: false,
70
+ };
71
+ const capturedCaches = (values) => values.flatMap((cache) => {
72
+ const name = stringValue(cache.cacheName);
73
+ const id = stringValue(cache.cacheId);
74
+ return name === undefined || id === undefined ? [] : [{ name, id }];
75
+ });
76
+ const storageItems = async (context, origin, isLocalStorage, limitations) => {
77
+ const raw = await optionalCdpCommand(context, "DOMStorage.getDOMStorageItems", { storageId: { securityOrigin: origin, isLocalStorage } }, limitations);
78
+ const entries = recordValue(raw)?.entries;
79
+ if (raw === undefined || !Array.isArray(entries))
80
+ return { items: [], complete: false };
81
+ const items = entries.flatMap((entry) => {
82
+ if (!Array.isArray(entry))
83
+ return [];
84
+ const key = stringValue(entry[0]);
85
+ const value = stringValue(entry[1]);
86
+ return key === undefined || value === undefined ? [] : [{ key, value }];
87
+ });
88
+ return { items, complete: items.length === entries.length };
47
89
  };
48
90
  const stringArray = (value) => Array.isArray(value)
49
91
  ? value.flatMap((item) => {
50
92
  const text = stringValue(item);
51
- return text === undefined ? [] : [text.slice(0, 1_024)];
93
+ return text === undefined ? [] : [text];
52
94
  })
53
95
  : [];