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
@@ -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),
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Classify a ZIP-backed package by its complete lower-cased path suffix.
3
+ *
4
+ * This is deliberately extension-only. Callers must separately verify ZIP
5
+ * magic before trusting the classification for a root input.
6
+ */
7
+ export const zipPackageFormatForPath = (path) => {
8
+ const lower = path.toLowerCase();
9
+ if (lower.endsWith(".zip"))
10
+ return "zip";
11
+ if (lower.endsWith(".ipa"))
12
+ return "ipa";
13
+ if (lower.endsWith(".apk") || lower.endsWith(".aab"))
14
+ return "apk";
15
+ if (lower.endsWith(".msix") || lower.endsWith(".msixbundle"))
16
+ return "msix";
17
+ if (lower.endsWith(".appx") || lower.endsWith(".appxbundle"))
18
+ return "appx";
19
+ return undefined;
20
+ };
@@ -1,7 +1,7 @@
1
1
  /** Generated from package.json and package-lock.json; do not edit. */
2
2
  export const PACKAGE_METADATA = {
3
3
  name: "rea-agents",
4
- version: "2.3.0", // x-release-please-version
4
+ version: "2.5.0", // x-release-please-version
5
5
  serverSdkVersion: "2.0.0-beta.4",
6
6
  clientSdkVersion: "2.0.0-beta.4",
7
7
  coreSdkVersion: "2.0.0-beta.4",
@@ -17,4 +17,4 @@ export const GHIDRA_MAX_QUEUED_REQUESTS = 32;
17
17
  /** Maximum encoded response line accepted from the Java bridge. */
18
18
  export const GHIDRA_MAX_LINE_BYTES = 1024 * 1024;
19
19
  /** Version of REA's Java bridge handshake and request protocol. */
20
- export const GHIDRA_BRIDGE_VERSION = 4;
20
+ export const GHIDRA_BRIDGE_VERSION = 5;
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { AnalysisInputError, AnalysisOutputError } from "../domain/errors.js";
3
- import { functionDossierSchema, } from "../domain/hopperValues.js";
3
+ import { functionInstructionWindowSchema, functionDossierSchema, } from "../domain/hopperValues.js";
4
4
  import { jsonObjectSchema, jsonValueSchema, } from "../domain/jsonValue.js";
5
5
  import { err, ok } from "../domain/result.js";
6
6
  import { ghidraBoundedIdentifierSchema, ghidraCanonicalAddressSchema, } from "./GhidraInventoryValues.js";
@@ -12,6 +12,7 @@ export const GHIDRA_FUNCTION_OPERATIONS = [
12
12
  "procedure_callers",
13
13
  "procedure_info",
14
14
  "procedure_pseudo_code",
15
+ "read_function_instructions",
15
16
  "procedure_references",
16
17
  "xrefs",
17
18
  ];
@@ -66,6 +67,13 @@ const inputSchemas = {
66
67
  procedure_callers: z.object(directProcedure).strict(),
67
68
  procedure_info: z.object(directProcedure).strict(),
68
69
  procedure_pseudo_code: z.object(directProcedure).strict(),
70
+ read_function_instructions: z
71
+ .object({
72
+ ...directProcedure,
73
+ offset: z.number().int().min(0).max(100_000).default(0),
74
+ limit: z.number().int().min(1).max(500).default(64),
75
+ })
76
+ .strict(),
69
77
  procedure_references: z
70
78
  .object({
71
79
  ...directProcedure,
@@ -235,6 +243,9 @@ const ghidraFunctionDossier = functionDossierSchema.superRefine((value, context)
235
243
  message: "Ghidra dossier omitted required uncertainty limitations",
236
244
  });
237
245
  });
246
+ const ghidraFunctionInstructionWindow = functionInstructionWindowSchema
247
+ .extend({ procedure: procedureIdentity })
248
+ .strict();
238
249
  const resultSchemas = {
239
250
  analyze_function: ghidraFunctionDossier,
240
251
  procedure_assembly: z.string(),
@@ -242,6 +253,7 @@ const resultSchemas = {
242
253
  procedure_callers: z.array(ghidraCanonicalAddressSchema),
243
254
  procedure_info: procedureInfo,
244
255
  procedure_pseudo_code: z.string().nullable(),
256
+ read_function_instructions: ghidraFunctionInstructionWindow,
245
257
  procedure_references: procedureReferences,
246
258
  xrefs: z.array(ghidraCanonicalAddressSchema),
247
259
  };
@@ -26,6 +26,7 @@ const PAGINATED_OPERATIONS = new Set([
26
26
  "search_procedures",
27
27
  "search_strings",
28
28
  "procedure_references",
29
+ "read_function_instructions",
29
30
  "analyze_function",
30
31
  ]);
31
32
  const SEARCH_OPERATIONS = new Set([
@@ -93,6 +94,12 @@ export const limitationsFor = (operation) => {
93
94
  "Pseudocode is Ghidra decompiler output, not original source and not text-equivalent to Hopper output; each decompile has a 30-second native deadline.",
94
95
  "External functions and functions without an analyzable body return null; other decompiler failures remain explicit.",
95
96
  ];
97
+ case "read_function_instructions":
98
+ return [
99
+ ...common,
100
+ "This bounded fast path reads only the requested function instruction window and does not invoke the decompiler or whole-program string/name inventories.",
101
+ "Instruction text is Ghidra-specific and does not claim textual equivalence with Hopper output.",
102
+ ];
96
103
  case "procedure_assembly":
97
104
  return [
98
105
  ...common,
@@ -0,0 +1,158 @@
1
+ import { HopperProcessError } from "../domain/errors.js";
2
+ import { ProviderCleanupError } from "../domain/providerCleanupError.js";
3
+ import { err, ok } from "../domain/result.js";
4
+ import { createOwnedHopperShutdownDiagnostic, providerCleanupFailure, } from "./HopperDiagnostics.js";
5
+ import { isHopperCleanupRequired, isHopperShutdownAcknowledgement, } from "./HopperSessionValues.js";
6
+ /** Close Hopper phases and retain every resource whose cleanup is unverified. */
7
+ export const cleanupHopperSession = async (input) => {
8
+ const state = {
9
+ issues: [],
10
+ resources: new Set(),
11
+ cleanupResult: undefined,
12
+ shutdownConfirmed: false,
13
+ ownedProcessStopped: false,
14
+ };
15
+ await report(input.progress, 0, "requesting Hopper document shutdown");
16
+ await requestShutdown(input, state);
17
+ await report(input.progress, 0.35, "releasing Hopper bridge transport");
18
+ input.releaseTransport(input.socket);
19
+ await stopProcess(input, state);
20
+ recordUnconfirmedDocument(input, state);
21
+ await report(input.progress, 0.75, "removing Hopper private runtime files");
22
+ await closeRuntimeRoot(input.runtimeRoot, state);
23
+ return cleanupOutcome(input, state);
24
+ };
25
+ const requestShutdown = async (input, state) => {
26
+ if (input.socket === undefined ||
27
+ input.socket.destroyed ||
28
+ input.activeRequest !== null) {
29
+ if (input.activeRequest !== null)
30
+ input.logger.warn({
31
+ method: input.activeRequest.operation,
32
+ callerState: input.activeRequest.callerState,
33
+ }, "Hopper shutdown skipped because a bridge operation remained active");
34
+ return;
35
+ }
36
+ const shutdown = await input
37
+ .request("shutdown")
38
+ .catch(() => err(new HopperProcessError(null)));
39
+ if (shutdown.ok &&
40
+ isHopperCleanupRequired(shutdown.value) &&
41
+ input.launch?.shutdownByCleanup === true &&
42
+ input.launch.cleanup !== undefined) {
43
+ state.cleanupResult = await input.launch
44
+ .cleanup()
45
+ .catch(providerCleanupFailure);
46
+ if (!state.cleanupResult.cleaned) {
47
+ state.shutdownConfirmed = await fallbackDocumentShutdown(input);
48
+ state.cleanupResult = await input.launch
49
+ .cleanup()
50
+ .catch(providerCleanupFailure);
51
+ }
52
+ }
53
+ else if (shutdown.ok) {
54
+ state.shutdownConfirmed = isHopperShutdownAcknowledgement(shutdown.value);
55
+ }
56
+ if (!shutdown.ok ||
57
+ (!state.shutdownConfirmed && state.cleanupResult === undefined))
58
+ input.logger.warn({ status: shutdown.ok ? "invalid-acknowledgement" : "failed" }, "Hopper document shutdown was not confirmed");
59
+ };
60
+ const fallbackDocumentShutdown = async (input) => {
61
+ const fallback = await input
62
+ .request("shutdown_document")
63
+ .catch(() => err(new HopperProcessError(null)));
64
+ const confirmed = fallback.ok && isHopperShutdownAcknowledgement(fallback.value);
65
+ if (!confirmed)
66
+ input.logger.warn({ status: fallback.ok ? "invalid-acknowledgement" : "failed" }, "Hopper document shutdown fallback was not confirmed");
67
+ return confirmed;
68
+ };
69
+ const stopProcess = async (input, state) => {
70
+ const supervisor = input.processSupervisor;
71
+ if (supervisor === undefined)
72
+ return;
73
+ const stopped = await supervisor.stop(state.cleanupResult === undefined
74
+ ? {}
75
+ : { cleanupResult: state.cleanupResult });
76
+ const diagnostic = createOwnedHopperShutdownDiagnostic(supervisor.launch, stopped, state.cleanupResult);
77
+ try {
78
+ input.onDiagnostic?.(diagnostic);
79
+ }
80
+ catch {
81
+ // Diagnostic consumers cannot change the already-observed cleanup result.
82
+ }
83
+ input.logger.info(diagnostic, "Owned Hopper launcher shutdown completed");
84
+ if (stopped.status !== "incomplete") {
85
+ state.ownedProcessStopped = stopped.status !== "not-owned";
86
+ return;
87
+ }
88
+ const processGroupId = supervisor.launch.ownership?.processGroupId;
89
+ const resource = processGroupId === undefined
90
+ ? "hopper-process"
91
+ : `process-group:${String(processGroupId)}`;
92
+ state.resources.add(resource);
93
+ state.issues.push({ resource, reason: stopped.reason });
94
+ input.logger.warn({ reason: stopped.reason }, "Owned launcher cleanup failed closed");
95
+ };
96
+ const recordUnconfirmedDocument = (input, state) => {
97
+ if ((input.socket === undefined && input.launch === undefined) ||
98
+ state.shutdownConfirmed ||
99
+ state.ownedProcessStopped)
100
+ return;
101
+ state.resources.add("hopper-document");
102
+ state.issues.push({
103
+ resource: "hopper-document",
104
+ reason: input.activeRequest === null
105
+ ? "authenticated shutdown acknowledgement was not observed"
106
+ : `bridge operation ${input.activeRequest.operation} remained active after caller ${input.activeRequest.callerState}`,
107
+ });
108
+ };
109
+ const closeRuntimeRoot = async (runtimeRoot, state) => {
110
+ try {
111
+ await runtimeRoot?.close();
112
+ }
113
+ catch (cause) {
114
+ const resource = runtimeRoot?.path ?? "hopper-runtime-root";
115
+ state.resources.add(resource);
116
+ state.issues.push({
117
+ resource,
118
+ reason: cause instanceof Error
119
+ ? cause.message
120
+ : "private runtime root cleanup failed",
121
+ });
122
+ }
123
+ };
124
+ const cleanupOutcome = (input, state) => {
125
+ if (state.issues.length === 0)
126
+ return ok(null);
127
+ return err(new ProviderCleanupError("hopper", [...state.resources], {
128
+ issues: cleanupIssueDetails(state.issues),
129
+ ...(input.runtimeRoot === undefined
130
+ ? {}
131
+ : { runtime_root: input.runtimeRoot.path }),
132
+ ...(input.activeRequest === null
133
+ ? {}
134
+ : { active_request: activityDetails(input.activeRequest) }),
135
+ }));
136
+ };
137
+ const cleanupIssueDetails = (issues) => issues.map((issue) => ({
138
+ resource: issue.resource,
139
+ reason: issue.reason,
140
+ }));
141
+ const activityDetails = (activity) => ({
142
+ request_id: activity.requestId,
143
+ operation: activity.operation,
144
+ elapsed_ms: activity.elapsedMs,
145
+ timeout_ms: activity.timeoutMs,
146
+ caller_state: activity.callerState,
147
+ queued_requests: activity.queuedRequests,
148
+ });
149
+ const report = async (progress, completed, message) => {
150
+ await progress
151
+ ?.report({
152
+ phase: "hopper_cleanup",
153
+ completed,
154
+ total: 1,
155
+ message,
156
+ })
157
+ .catch(() => undefined);
158
+ };