rea-agents 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +78 -32
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/ArtifactExtraction.js +1 -1
  4. package/dist/application/ArtifactInventory/classify.js +42 -0
  5. package/dist/application/ArtifactInventory/hash.js +43 -0
  6. package/dist/application/ArtifactInventory/reader.js +41 -0
  7. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  8. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  9. package/dist/application/ArtifactInventory/types.js +10 -0
  10. package/dist/application/ArtifactInventory.js +9 -351
  11. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  12. package/dist/application/BinarySession.js +47 -270
  13. package/dist/application/BinarySessionExecution.js +28 -0
  14. package/dist/application/BinarySessionOpen.js +39 -0
  15. package/dist/application/BinarySessionRecords.js +149 -0
  16. package/dist/application/BinarySessionStatus.js +75 -0
  17. package/dist/application/CapabilityInventory.js +24 -2
  18. package/dist/application/ClientRegistrationStatus.js +1 -1
  19. package/dist/application/CrossVersionInventory.js +8 -2
  20. package/dist/application/CrossVersionInvestigation.js +1 -23
  21. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  22. package/dist/application/DirectAnalysis.js +47 -44
  23. package/dist/application/Doctor.js +156 -238
  24. package/dist/application/DoctorDiagnostics.js +180 -0
  25. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  26. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  27. package/dist/application/EnhancedToolPagination.js +22 -0
  28. package/dist/application/EnhancedToolTypes.js +4 -0
  29. package/dist/application/EnhancedTools.js +20 -37
  30. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  31. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  32. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  33. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  34. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  35. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  36. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  37. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  38. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  39. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  40. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  41. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  42. package/dist/application/JavaScriptJsonModules.js +39 -0
  43. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  44. package/dist/application/JavaScriptReplayPermission.js +16 -0
  45. package/dist/application/JavaScriptReplayService.js +182 -156
  46. package/dist/application/LinuxHopper.js +10 -0
  47. package/dist/application/MacHopper.js +22 -11
  48. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  49. package/dist/application/PermissionAuthority.js +106 -63
  50. package/dist/application/ProcessCaptureCapability.js +1 -1
  51. package/dist/application/ProcessCapturePermission.js +13 -0
  52. package/dist/application/ProcessCli.js +2 -12
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProjectPermissionStore.js +48 -9
  55. package/dist/application/RealHopperAssertions.js +76 -0
  56. package/dist/application/ReconstructionCoverageService.js +69 -0
  57. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  58. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  59. package/dist/application/Setup.js +147 -259
  60. package/dist/application/SetupClientConfiguration.js +220 -0
  61. package/dist/application/SetupClients.js +24 -2
  62. package/dist/application/SetupPlan.js +123 -12
  63. package/dist/application/SetupSkill.js +26 -12
  64. package/dist/application/SupportedClients.js +1 -0
  65. package/dist/application/Uninstall.js +6 -3
  66. package/dist/application/WindowsCapabilities.js +86 -0
  67. package/dist/artifacts/AsarArtifactReader.js +11 -0
  68. package/dist/browser/BrowserProcessStartup.js +88 -0
  69. package/dist/browser/CdpCaptureDocuments.js +12 -8
  70. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  71. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  72. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  73. package/dist/browser/CdpCaptureEventState.js +1 -0
  74. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  75. package/dist/browser/CdpCaptureEvents.js +29 -496
  76. package/dist/browser/CdpObservationSession.js +51 -8
  77. package/dist/browser/CdpPageCapture.js +12 -133
  78. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  79. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  80. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  81. package/dist/browser/PngVisualDiff.js +9 -2
  82. package/dist/catalogIdentity.js +4 -0
  83. package/dist/cli/artifactCommands.js +82 -0
  84. package/dist/cli/coreAnalysisCommands.js +232 -0
  85. package/dist/cli/managedCommands.js +397 -0
  86. package/dist/cli/managedProjectGraphCommand.js +24 -0
  87. package/dist/cli/options.js +9 -0
  88. package/dist/cli/setupCommands.js +108 -0
  89. package/dist/cli/types.js +1 -0
  90. package/dist/cli/utilityCommands.js +122 -0
  91. package/dist/cli.js +10 -768
  92. package/dist/cliApplicationCommands.js +118 -4
  93. package/dist/cliBrowserAdvancedCommands.js +73 -16
  94. package/dist/cliBrowserCommands.js +39 -115
  95. package/dist/cliCommandNames.js +4 -0
  96. package/dist/cliElectronCommands.js +33 -62
  97. package/dist/cliEvidenceCommands.js +13 -2
  98. package/dist/cliInvestigationCommands.js +53 -10
  99. package/dist/cliObservationOptions.js +137 -0
  100. package/dist/cliPolicyCommands.js +80 -46
  101. package/dist/cliSetup.js +289 -0
  102. package/dist/config/environment.js +107 -0
  103. package/dist/config/parseConfig.js +228 -0
  104. package/dist/config/parsers.js +68 -0
  105. package/dist/config/permissions.js +30 -0
  106. package/dist/config/types.js +1 -0
  107. package/dist/config.js +1 -375
  108. package/dist/contracts/applicationToolContracts.js +134 -0
  109. package/dist/contracts/managedToolContracts.js +1 -1
  110. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  111. package/dist/contracts/providerSelection.js +2 -0
  112. package/dist/contracts/toolContracts.js +2 -2
  113. package/dist/contracts/toolEffects.js +14 -0
  114. package/dist/contracts/toolOutputSchemaGroups.js +232 -0
  115. package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
  116. package/dist/contracts/toolOutputSchemas.js +2 -550
  117. package/dist/domain/analysisErrorPresentation.js +234 -0
  118. package/dist/domain/analysisErrorProjection.js +284 -0
  119. package/dist/domain/artifactComparison.js +34 -15
  120. package/dist/domain/binaryTarget.js +87 -37
  121. package/dist/domain/browserObservation.js +1 -285
  122. package/dist/domain/browserObservationSchemas.js +286 -0
  123. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  124. package/dist/domain/errors.js +4 -486
  125. package/dist/domain/functionComparison.js +4 -165
  126. package/dist/domain/functionComparisonDimensions.js +164 -0
  127. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  128. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  129. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  130. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  131. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  132. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  133. package/dist/domain/javascriptSemanticIr.js +30 -0
  134. package/dist/domain/javascriptSemanticProjection.js +366 -0
  135. package/dist/domain/javascriptSemanticState.js +40 -0
  136. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  137. package/dist/domain/javascriptSemanticValues.js +352 -0
  138. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  139. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  140. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  141. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  142. package/dist/domain/knownPageCoverage.js +21 -0
  143. package/dist/domain/managedApplicationGraph.js +14 -364
  144. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  145. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  146. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  147. package/dist/domain/managedArtifact.js +16 -8
  148. package/dist/domain/managedMemberComparison.js +37 -391
  149. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  150. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  151. package/dist/domain/managedNativeVerification.js +27 -389
  152. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  153. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  154. package/dist/domain/managedReconstruction.js +82 -69
  155. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  156. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  157. package/dist/domain/permissionPolicy.js +2 -0
  158. package/dist/domain/reconstructionCoverage.js +387 -0
  159. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  160. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  161. package/dist/domain/runtimeCharacterization.js +85 -0
  162. package/dist/domain/webBundleAnalyzer.js +3 -409
  163. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  164. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  165. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  166. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  167. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  168. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  169. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  170. package/dist/dotnet/ManagedMemberRows.js +120 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  172. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  173. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  174. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  175. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  176. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  177. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  178. package/dist/dotnet/ManagedPeReader.js +59 -28
  179. package/dist/generatedPackageMetadata.js +2 -2
  180. package/dist/ghidra/GhidraClient.js +76 -91
  181. package/dist/ghidra/GhidraClientStartup.js +29 -0
  182. package/dist/ghidra/GhidraClientWire.js +34 -0
  183. package/dist/ghidra/GhidraDefaults.js +1 -1
  184. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  185. package/dist/ghidra/GhidraInstallation.js +20 -15
  186. package/dist/ghidra/GhidraLauncher.js +93 -29
  187. package/dist/ghidra/GhidraProvider.js +61 -161
  188. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  189. package/dist/ghidra/GhidraSessionValues.js +2 -0
  190. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  191. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  192. package/dist/ghidra/GhidraTransport.js +71 -0
  193. package/dist/identity.js +3 -2
  194. package/dist/main/messages.js +5 -0
  195. package/dist/main/reload.js +49 -0
  196. package/dist/main/shutdown.js +25 -0
  197. package/dist/main/startup.js +14 -0
  198. package/dist/main/state.js +26 -0
  199. package/dist/main/transport.js +61 -0
  200. package/dist/main/types.js +1 -0
  201. package/dist/main.js +35 -146
  202. package/dist/process/PrivateRuntimeRoot.js +3 -2
  203. package/dist/process/ProcessOwnership.js +35 -0
  204. package/dist/process/ProviderProcess.js +2 -0
  205. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  206. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  207. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  208. package/dist/replay/ReplayOutcome.js +95 -0
  209. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  210. package/dist/server/ProcessCaptureElicitation.js +157 -0
  211. package/dist/server/createServer.js +119 -54
  212. package/dist/server/registerApplicationTools/characterization.js +57 -0
  213. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  214. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  215. package/dist/server/registerApplicationTools/coverage.js +84 -0
  216. package/dist/server/registerApplicationTools/helpers.js +42 -0
  217. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  218. package/dist/server/registerApplicationTools/types.js +1 -0
  219. package/dist/server/registerApplicationTools.js +10 -112
  220. package/dist/server/registerBrowserTools.js +59 -83
  221. package/dist/server/registerElectronTools.js +31 -46
  222. package/dist/server/registerEnhancedTools.js +34 -42
  223. package/dist/server/registerEvidenceResources.js +54 -0
  224. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  225. package/dist/server/registerInvestigationTools/types.js +1 -0
  226. package/dist/server/registerInvestigationTools.js +83 -116
  227. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  228. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  229. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  230. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  231. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  232. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  233. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  234. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  235. package/dist/server/registerManagedWorkflowTools.js +10 -274
  236. package/dist/server/registerOfficialTools.js +73 -59
  237. package/dist/server/registerSessionRecordTools.js +137 -0
  238. package/dist/server/registerSessionStatusTool.js +2 -1
  239. package/dist/server/registerSessionTools.js +60 -164
  240. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  241. package/dist/server/toolRegistrationOptions.js +101 -3
  242. package/dist/server/toolResult.js +5 -0
  243. package/package.json +10 -11
  244. package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +20 -7
  245. package/scripts/prepare-node-pty.mjs +0 -35
  246. package/scripts/rebuild-native.mjs +0 -22
@@ -1,4 +1,4 @@
1
- import { resolveArtifactPath } from "./JavaScriptArtifactGraphContext.js";
1
+ import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js";
2
2
  /** Flatten all per-file IPC facts into deterministic application records. */
3
3
  export const collectElectronIpcRecords = (analysis) => analysis.files
4
4
  .flatMap(({ file, javascript }) => (javascript?.electron.ipc ?? []).map((finding) => ({
@@ -70,9 +70,20 @@ export const summarizeElectronBoundaries = (analysis) => {
70
70
  },
71
71
  sender_validation_observations: validations.length,
72
72
  utility_processes: utilities.length,
73
- resolved_utility_entrypoints: utilities.filter(({ file, finding }) => resolvesToFile(file, finding.module_path, files)).length,
73
+ resolved_utility_entrypoints: utilities.filter(({ file, finding }) => resolvesToFile({
74
+ file,
75
+ specifier: finding.module_path,
76
+ context: finding.module_resolution_context ?? "module-specifier",
77
+ files,
78
+ })).length,
74
79
  native_addon_bindings: nativeBindings.length,
75
- resolved_native_addon_bindings: nativeBindings.filter(({ file, finding }) => resolvesToFile(file, finding.specifier, files, "native-addon")).length,
80
+ resolved_native_addon_bindings: nativeBindings.filter(({ file, finding }) => resolvesToFile({
81
+ file,
82
+ specifier: finding.specifier,
83
+ context: "module-specifier",
84
+ files,
85
+ kind: "native-addon",
86
+ })).length,
76
87
  };
77
88
  };
78
89
  const compatibleIpcModes = (transmission, handler) => transmission.mode === "invoke"
@@ -85,12 +96,17 @@ const isRendererTransmission = ({ finding }) => finding.side === "renderer" &&
85
96
  const isPairableRendererTransmission = (record) => isRendererTransmission(record) && record.finding.operation !== "send-to-host";
86
97
  const isMainHandler = ({ finding }) => finding.side === "main" &&
87
98
  (finding.mode === "listen" || finding.mode === "handle");
88
- const resolvesToFile = (file, specifier, files, kind) => {
89
- if (specifier === null)
99
+ const resolvesToFile = (input) => {
100
+ if (input.specifier === null)
90
101
  return false;
91
- const resolved = resolveArtifactPath(specifier, file.path, files);
102
+ const resolved = resolveArtifactPathByContext({
103
+ declaredPath: input.specifier,
104
+ sourcePath: input.file.path,
105
+ context: input.context,
106
+ files: input.files,
107
+ }).resolved_path;
92
108
  return (resolved !== null &&
93
- (kind === undefined || files.get(resolved)?.kind === kind));
109
+ (input.kind === undefined || input.files.get(resolved)?.kind === input.kind));
94
110
  };
95
111
  const ipcRecordKey = (path, finding) => `${path}\0${String(finding.location.start.line)}:${String(finding.location.start.column)}\0${finding.side}\0${finding.operation}`;
96
112
  const compareCodePoints = (left, right) => left < right ? -1 : left > right ? 1 : 0;
@@ -1,6 +1,7 @@
1
- import { artifactLocalIdentity, createElectronRoleNode, javascriptAnalysisCoverage, linkElectronRoleToAsset, resolveArtifactPath, } from "./JavaScriptArtifactGraphContext.js";
1
+ import { artifactLocalIdentity, createElectronRoleNode, javascriptAnalysisCoverage, linkElectronRoleToAsset, } from "./JavaScriptArtifactGraphContext.js";
2
2
  import { astObservationEvidence } from "./JavaScriptArtifactGraphEvidence.js";
3
3
  import { addElectronAstEdge, addElectronInferenceEdge, electronFindingSourceNode, electronRangeKey, } from "./ElectronBoundaryGraphContext.js";
4
+ import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js";
4
5
  /** Project BrowserWindow, contextBridge, preload, and utility-process facts. */
5
6
  export const addElectronWindowBoundaries = (context) => {
6
7
  for (const analyzed of context.analysis.files) {
@@ -40,6 +41,7 @@ const addBrowserWindow = (input) => {
40
41
  web_preferences: value.web_preferences,
41
42
  omitted_web_preferences: value.omitted_web_preferences,
42
43
  preload_path: value.preload_path,
44
+ preload_resolution_context: value.preload_resolution_context,
43
45
  absence_means_default: false,
44
46
  },
45
47
  evidence: astObservationEvidence({
@@ -71,12 +73,16 @@ const addWindowPreload = (input, window) => {
71
73
  const declared = input.value.preload_path;
72
74
  if (declared === null)
73
75
  return;
74
- const resolved = resolveArtifactPath(declared, input.file.path, input.context.filesByPath);
76
+ const resolution = resolveArtifactPathByContext({
77
+ declaredPath: declared,
78
+ sourcePath: input.file.path,
79
+ context: input.value.preload_resolution_context ?? "module-specifier",
80
+ files: input.context.filesByPath,
81
+ });
75
82
  const role = createElectronRoleNode(input.context, {
76
83
  kind: "electron-preload",
77
84
  anchor: input.file,
78
- declaredPath: declared,
79
- resolvedPath: resolved,
85
+ resolution,
80
86
  mechanism: "BrowserWindow:webPreferences.preload",
81
87
  range: input.value.location,
82
88
  coverage: input.coverage,
@@ -89,12 +95,18 @@ const addWindowPreload = (input, window) => {
89
95
  coverage: input.coverage,
90
96
  relation: "loads",
91
97
  operation: "associate-browser-window-preload",
92
- properties: { declared_path: declared, resolved_path: resolved },
98
+ properties: {
99
+ declared_path: declared,
100
+ resolution_context: resolution.resolution_context,
101
+ resolved_path: resolution.resolved_path,
102
+ resolution_status: resolution.resolution_status,
103
+ limitations: resolution.limitations,
104
+ },
93
105
  });
94
106
  linkElectronRoleToAsset(input.context, {
95
107
  role,
96
108
  anchor: input.file,
97
- path: resolved,
109
+ resolution,
98
110
  range: input.value.location,
99
111
  coverage: input.coverage,
100
112
  });
@@ -152,6 +164,7 @@ const addUtilityProcess = (input) => {
152
164
  label: value.service_name ?? value.module_path,
153
165
  properties: {
154
166
  module_path: value.module_path,
167
+ module_resolution_context: value.module_resolution_context,
155
168
  module_expression: value.module_expression,
156
169
  service_name: value.service_name,
157
170
  },
@@ -180,7 +193,13 @@ const addUtilityProcess = (input) => {
180
193
  });
181
194
  if (value.module_path === null)
182
195
  return;
183
- const resolved = resolveArtifactPath(value.module_path, file.path, context.filesByPath);
196
+ const resolution = resolveArtifactPathByContext({
197
+ declaredPath: value.module_path,
198
+ sourcePath: file.path,
199
+ context: value.module_resolution_context ?? "module-specifier",
200
+ files: context.filesByPath,
201
+ });
202
+ const resolved = resolution.resolved_path;
184
203
  const target = resolved === null
185
204
  ? undefined
186
205
  : (context.assetNodes.get(resolved) ?? context.fileNodes.get(resolved));
@@ -194,6 +213,11 @@ const addUtilityProcess = (input) => {
194
213
  coverage,
195
214
  relation: "maps_to",
196
215
  operation: "resolve-utility-process-entrypoint",
197
- properties: { resolved_path: resolved },
216
+ properties: {
217
+ declared_path: value.module_path,
218
+ resolution_context: resolution.resolution_context,
219
+ resolved_path: resolved,
220
+ resolution_status: resolution.resolution_status,
221
+ },
198
222
  });
199
223
  };
@@ -0,0 +1,22 @@
1
+ import { AnalysisOutputError } from "../domain/errors.js";
2
+ import { parseAddressedPage } from "../domain/hopperValues.js";
3
+ import { err, ok } from "../domain/result.js";
4
+ /** Read all addressed entries while enforcing advancing provider pagination. */
5
+ export const readAllAddressed = async (input) => {
6
+ const entries = [];
7
+ let offset = 0;
8
+ while (true) {
9
+ const result = await input.call(input.tool, { offset, limit: 500 }, input.signal);
10
+ if (!result.ok)
11
+ return result;
12
+ const page = parseAddressedPage(result.value);
13
+ if (!page.ok)
14
+ return page;
15
+ entries.push(...page.value.items);
16
+ if (!page.value.hasMore || page.value.nextOffset === null)
17
+ return ok(entries);
18
+ if (page.value.nextOffset <= offset)
19
+ return err(new AnalysisOutputError(input.tool, "provider returned a non-advancing pagination offset"));
20
+ offset = page.value.nextOffset;
21
+ }
22
+ };
@@ -0,0 +1,4 @@
1
+ import { AnalysisInputError } from "../domain/errors.js";
2
+ import { err } from "../domain/result.js";
3
+ import { enhancedInputSchemas, } from "../contracts/enhancedInputs.js";
4
+ export const invalidEnhancedInput = (name, cause) => Promise.resolve(err(new AnalysisInputError(name, { cause })));
@@ -1,8 +1,10 @@
1
1
  import { enhancedInputSchemas } from "../contracts/enhancedInputs.js";
2
- import { AnalysisInputError, AnalysisCancelledError, AnalysisOutputError, projectAnalysisError, } from "../domain/errors.js";
3
- import { addressDistance, parseDocuments, parseFunctionDossier, parseAddressedPage, parseListCount, parseRelatedAddresses, parseSegments, } from "../domain/hopperValues.js";
2
+ import { AnalysisCancelledError, AnalysisOutputError, projectAnalysisError, } from "../domain/errors.js";
3
+ import { addressDistance, parseAddressedPage, parseDocuments, parseFunctionDossier, parseListCount, parseRelatedAddresses, parseSegments, } from "../domain/hopperValues.js";
4
4
  import { err, ok } from "../domain/result.js";
5
5
  import { categorizeSwiftTypes, discoverObjcClasses, discoverObjcProtocols, discoverSwiftClasses, } from "../domain/symbolAnalysis.js";
6
+ import { invalidEnhancedInput, } from "./EnhancedToolTypes.js";
7
+ import { readAllAddressed } from "./EnhancedToolPagination.js";
6
8
  /**
7
9
  * Composes direct provider operations into bounded reverse-engineering tools.
8
10
  * Direct callers use `execute`; adapters that own validation use
@@ -20,13 +22,13 @@ export class EnhancedTools {
20
22
  const parsed = enhancedInputSchemas.swift_classes.safeParse(input);
21
23
  return parsed.success
22
24
  ? this.executeValidated({ name, input: parsed.data }, signal)
23
- : invalidInput(name, parsed.error);
25
+ : invalidEnhancedInput(name, parsed.error);
24
26
  }
25
27
  case "get_objc_classes": {
26
28
  const parsed = enhancedInputSchemas.get_objc_classes.safeParse(input);
27
29
  return parsed.success
28
30
  ? this.executeValidated({ name, input: parsed.data }, signal)
29
- : invalidInput(name, parsed.error);
31
+ : invalidEnhancedInput(name, parsed.error);
30
32
  }
31
33
  case "get_objc_protocols":
32
34
  return this.executeValidated({ name, input: {} }, signal);
@@ -34,13 +36,13 @@ export class EnhancedTools {
34
36
  const parsed = enhancedInputSchemas.batch_decompile.safeParse(input);
35
37
  return parsed.success
36
38
  ? this.executeValidated({ name, input: parsed.data }, signal)
37
- : invalidInput(name, parsed.error);
39
+ : invalidEnhancedInput(name, parsed.error);
38
40
  }
39
41
  case "get_call_graph": {
40
42
  const parsed = enhancedInputSchemas.get_call_graph.safeParse(input);
41
43
  return parsed.success
42
44
  ? this.executeValidated({ name, input: parsed.data }, signal)
43
- : invalidInput(name, parsed.error);
45
+ : invalidEnhancedInput(name, parsed.error);
44
46
  }
45
47
  case "analyze_swift_types":
46
48
  return this.executeValidated({ name, input: {} }, signal);
@@ -48,25 +50,25 @@ export class EnhancedTools {
48
50
  const parsed = enhancedInputSchemas.find_xrefs_to_name.safeParse(input);
49
51
  return parsed.success
50
52
  ? this.executeValidated({ name, input: parsed.data }, signal)
51
- : invalidInput(name, parsed.error);
53
+ : invalidEnhancedInput(name, parsed.error);
52
54
  }
53
55
  case "binary_overview": {
54
56
  const parsed = enhancedInputSchemas.binary_overview.safeParse(input);
55
57
  return parsed.success
56
58
  ? this.executeValidated({ name, input: parsed.data }, signal)
57
- : invalidInput(name, parsed.error);
59
+ : invalidEnhancedInput(name, parsed.error);
58
60
  }
59
61
  case "analyze_function": {
60
62
  const parsed = enhancedInputSchemas.analyze_function.safeParse(input);
61
63
  return parsed.success
62
64
  ? this.executeValidated({ name, input: parsed.data }, signal)
63
- : invalidInput(name, parsed.error);
65
+ : invalidEnhancedInput(name, parsed.error);
64
66
  }
65
67
  case "trace_feature": {
66
68
  const parsed = enhancedInputSchemas.trace_feature.safeParse(input);
67
69
  return parsed.success
68
70
  ? this.executeValidated({ name, input: parsed.data }, signal)
69
- : invalidInput(name, parsed.error);
71
+ : invalidEnhancedInput(name, parsed.error);
70
72
  }
71
73
  }
72
74
  }
@@ -100,7 +102,7 @@ export class EnhancedTools {
100
102
  return result.ok ? parseFunctionDossier(result.value) : result;
101
103
  }
102
104
  async #swiftClasses(pattern, signal) {
103
- const procedures = await this.#allProcedures(signal);
105
+ const procedures = await this.#allAddressed("list_procedures", signal);
104
106
  return procedures.ok
105
107
  ? ok(discoverSwiftClasses(procedures.value, pattern))
106
108
  : procedures;
@@ -189,7 +191,7 @@ export class EnhancedTools {
189
191
  return ok(graph);
190
192
  }
191
193
  async #analyzeSwiftTypes(signal) {
192
- const procedures = await this.#allProcedures(signal);
194
+ const procedures = await this.#allAddressed("list_procedures", signal);
193
195
  return procedures.ok
194
196
  ? ok(categorizeSwiftTypes(procedures.value))
195
197
  : procedures;
@@ -218,7 +220,7 @@ export class EnhancedTools {
218
220
  const [segmentsResult, documentsResult, procedures, stringsResult] = await Promise.all([
219
221
  this.#call("list_segments", {}, signal),
220
222
  this.#call("list_documents", {}, signal),
221
- this.#allProcedures(signal),
223
+ this.#allAddressed("list_procedures", signal),
222
224
  this.#call("list_strings", {}, signal),
223
225
  ]);
224
226
  if (!segmentsResult.ok)
@@ -358,28 +360,12 @@ export class EnhancedTools {
358
360
  residual: [...residual],
359
361
  });
360
362
  }
361
- async #allProcedures(signal) {
362
- return this.#allAddressed("list_procedures", signal);
363
- }
364
363
  async #allAddressed(tool, signal) {
365
- const entries = [];
366
- let offset = 0;
367
- while (true) {
368
- const result = await this.#call(tool, { offset, limit: 500 }, signal);
369
- if (!result.ok)
370
- return result;
371
- const page = parseAddressedPage(result.value);
372
- if (!page.ok)
373
- return page;
374
- entries.push(...page.value.items);
375
- if (!page.value.hasMore || page.value.nextOffset === null) {
376
- return ok(entries);
377
- }
378
- if (page.value.nextOffset <= offset) {
379
- return err(new AnalysisOutputError(tool, "provider returned a non-advancing pagination offset"));
380
- }
381
- offset = page.value.nextOffset;
382
- }
364
+ return readAllAddressed({
365
+ call: (name, arguments_, operationSignal) => this.#call(name, arguments_, operationSignal),
366
+ tool,
367
+ ...(signal === undefined ? {} : { signal }),
368
+ });
383
369
  }
384
370
  async #call(name, arguments_, signal) {
385
371
  if (signal?.aborted === true)
@@ -388,6 +374,3 @@ export class EnhancedTools {
388
374
  return execution.ok ? ok(execution.value.result) : execution;
389
375
  }
390
376
  }
391
- const invalidInput = (name, cause) => Promise.resolve(err(new AnalysisInputError(name, {
392
- cause,
393
- })));
@@ -0,0 +1,36 @@
1
+ import { instrumentJavaScriptExport, } from "../domain/javascriptExportInstrumentation.js";
2
+ /** Decorate one exact replay source with deterministic in-memory instrumentation. */
3
+ export class InstrumentedJavaScriptReplayHost {
4
+ delegate;
5
+ input;
6
+ instrumentation = null;
7
+ constructor(delegate, input) {
8
+ this.delegate = delegate;
9
+ this.input = input;
10
+ }
11
+ async readSource(path, maximumBytes) {
12
+ const source = await this.delegate.readSource(path, maximumBytes);
13
+ if (path !== this.input.artifact_path)
14
+ return source;
15
+ const instrumented = instrumentJavaScriptExport(source.bytes, this.input);
16
+ if (instrumented.bytes.byteLength > maximumBytes)
17
+ throw new RangeError("Instrumented JavaScript exceeds the replay module byte limit");
18
+ this.instrumentation = instrumented;
19
+ return { canonicalPath: source.canonicalPath, bytes: instrumented.bytes };
20
+ }
21
+ identifyExecutable(path, versionArguments) {
22
+ return this.delegate.identifyExecutable(path, versionArguments);
23
+ }
24
+ identifyWorker() {
25
+ return this.delegate.identifyWorker();
26
+ }
27
+ identifyRuntimeClosure(nodePath) {
28
+ return this.delegate.identifyRuntimeClosure(nodePath);
29
+ }
30
+ seccompDigest() {
31
+ return this.delegate.seccompDigest();
32
+ }
33
+ probe(policy) {
34
+ return this.delegate.probe(policy);
35
+ }
36
+ }
@@ -5,27 +5,41 @@ import { isJsonWithinLimits } from "../domain/jsonLimits.js";
5
5
  import { InvestigationWorkspaceError } from "../domain/errors.js";
6
6
  import { parseInvestigationWorkspace, serializeInvestigationWorkspace, } from "../domain/investigationWorkspace.js";
7
7
  import { err, ok } from "../domain/result.js";
8
+ const investigationWorkspaceCodec = {
9
+ parse: parseInvestigationWorkspace,
10
+ serialize: serializeInvestigationWorkspace,
11
+ validateNext: validateNextRevision,
12
+ };
8
13
  /** Read a validated workspace, returning null only when it does not exist. */
9
- export const readInvestigationWorkspace = async (path, policy) => {
14
+ export const readInvestigationWorkspace = async (path, policy) => readRevisionedWorkspace(path, policy, investigationWorkspaceCodec);
15
+ /** Read one root-confined, owner-only revisioned workspace document. */
16
+ export const readRevisionedWorkspace = async (path, policy, codec) => {
10
17
  if (policy.roots.length === 0)
11
18
  return err(new InvestigationWorkspaceError("read", "disabled"));
12
19
  try {
13
20
  const destination = await resolveDestination(path, policy.roots);
14
21
  if (destination === null)
15
22
  return err(new InvestigationWorkspaceError("read", "outside-root"));
16
- return await readWorkspaceFile(destination, policy);
23
+ return await readWorkspaceFile(destination, policy, codec);
17
24
  }
18
25
  catch (cause) {
19
26
  return err(new InvestigationWorkspaceError("read", "io", { cause }));
20
27
  }
21
28
  };
22
29
  /** Atomically append one CAS-linked workspace revision under an exclusive lock. */
23
- export const writeInvestigationWorkspace = async (workspace, path, expectedRevision, policy) => {
30
+ export const writeInvestigationWorkspace = async (workspace, path, expectedRevision, policy) => writeRevisionedWorkspace(path, policy, {
31
+ document: workspace,
32
+ expectedRevision,
33
+ codec: investigationWorkspaceCodec,
34
+ });
35
+ /** Atomically append one validated CAS-linked revision under an exclusive lock. */
36
+ export const writeRevisionedWorkspace = async (path, policy, input) => {
37
+ const { document, expectedRevision, codec } = input;
24
38
  if (policy.roots.length === 0)
25
39
  return err(new InvestigationWorkspaceError("update", "disabled"));
26
40
  let encoded;
27
41
  try {
28
- encoded = serializeInvestigationWorkspace(workspace);
42
+ encoded = codec.serialize(document);
29
43
  }
30
44
  catch (cause) {
31
45
  return err(new InvestigationWorkspaceError("update", "integrity", { cause }));
@@ -42,10 +56,10 @@ export const writeInvestigationWorkspace = async (workspace, path, expectedRevis
42
56
  if (!acquired.ok)
43
57
  return acquired;
44
58
  lock = acquired.value;
45
- const current = await readWorkspaceFile(destination, policy);
59
+ const current = await readWorkspaceFile(destination, policy, codec);
46
60
  if (!current.ok)
47
61
  return current;
48
- const checked = validateNextRevision(current.value, workspace, expectedRevision);
62
+ const checked = codec.validateNext(current.value, document, expectedRevision);
49
63
  if (!checked.ok)
50
64
  return checked;
51
65
  await writeFileAtomic(destination, encoded, {
@@ -76,7 +90,7 @@ const resolveDestination = async (path, roots) => {
76
90
  }
77
91
  return null;
78
92
  };
79
- const readWorkspaceFile = async (destination, policy) => {
93
+ const readWorkspaceFile = async (destination, policy, codec) => {
80
94
  const stats = await lstat(destination).catch((cause) => {
81
95
  if (isFileNotFound(cause))
82
96
  return undefined;
@@ -101,7 +115,7 @@ const readWorkspaceFile = async (destination, policy) => {
101
115
  if (!isJsonWithinLimits(decoded, policy))
102
116
  return err(new InvestigationWorkspaceError("read", "too-large"));
103
117
  try {
104
- return ok(parseInvestigationWorkspace(decoded));
118
+ return ok(codec.parse(decoded));
105
119
  }
106
120
  catch (cause) {
107
121
  return err(new InvestigationWorkspaceError("read", "integrity", { cause }));
@@ -168,7 +182,7 @@ const releaseLock = async (lock) => {
168
182
  await lock.handle.close().catch(() => undefined);
169
183
  await unlink(lock.path).catch(() => undefined);
170
184
  };
171
- const validateNextRevision = (current, next, expectedRevision) => {
185
+ function validateNextRevision(current, next, expectedRevision) {
172
186
  if ((current?.revision ?? null) !== expectedRevision)
173
187
  return err(new InvestigationWorkspaceError("update", "revision-conflict"));
174
188
  if (current === null) {
@@ -182,7 +196,7 @@ const validateNextRevision = (current, next, expectedRevision) => {
182
196
  next.previous_revision_digest === current.revision_digest
183
197
  ? ok(null)
184
198
  : err(new InvestigationWorkspaceError("update", "revision-conflict"));
185
- };
199
+ }
186
200
  const isFileNotFound = (cause) => errorCode(cause) === "ENOENT";
187
201
  const isAlreadyExists = (cause) => errorCode(cause) === "EEXIST";
188
202
  const errorCode = (cause) => typeof cause === "object" && cause !== null && "code" in cause
@@ -1,5 +1,8 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { analyzeJavaScriptStaticSource } from "../domain/javascriptStaticAnalysis.js";
3
+ import { analyzeJavaScriptSemantics } from "../domain/javascriptSemanticAnalysis.js";
4
+ import { DEFAULT_JAVASCRIPT_SEMANTIC_LIMITS, } from "../domain/javascriptSemanticIr.js";
5
+ import { analyzeJavaScriptJsonModule } from "./JavaScriptJsonModules.js";
3
6
  /** Analyze selected text files under one shared AST, finding, module, and time budget. */
4
7
  export const analyzeJavaScriptArtifactFiles = (fileSet, input, now) => {
5
8
  const state = emptyArtifactAnalysis();
@@ -23,6 +26,7 @@ const finalizeArtifactAnalysis = (state, input) => {
23
26
  return {
24
27
  files: state.files,
25
28
  packages: state.packages,
29
+ json_modules: state.jsonModules,
26
30
  html_scripts: state.htmlScripts.slice(0, input.limits.max_findings),
27
31
  source_maps: state.sourceMaps,
28
32
  visited_ast_nodes: state.visitedNodes,
@@ -46,14 +50,13 @@ const finalizeArtifactAnalysis = (state, input) => {
46
50
  };
47
51
  const analyzeArtifactFile = (file, context) => {
48
52
  const { state, input } = context;
49
- if (file.kind === "package-json")
50
- state.packages.push(parsePackage(file));
53
+ addStructuredObservations(file, state);
51
54
  if (file.kind === "html" && file.text.included)
52
55
  state.htmlScripts.push(...parseHtmlScripts(file.path, file.text.value, Math.max(0, input.limits.max_findings - state.findings)));
53
56
  if (file.kind === "source-map")
54
57
  addSourceMap(file, context);
55
58
  if (file.kind !== "javascript" || !file.text.included) {
56
- state.files.push({ file, javascript: null });
59
+ state.files.push({ file, javascript: null, semantic: null });
57
60
  return;
58
61
  }
59
62
  const remainingNodes = input.limits.max_ast_nodes - state.visitedNodes;
@@ -63,7 +66,7 @@ const analyzeArtifactFile = (file, context) => {
63
66
  remainingFindings <= 0 ||
64
67
  remainingModules <= 0 ||
65
68
  context.now() > context.deadline) {
66
- state.files.push({ file, javascript: null });
69
+ state.files.push({ file, javascript: null, semantic: null });
67
70
  state.truncatedScopes += 1;
68
71
  return;
69
72
  }
@@ -74,14 +77,33 @@ const analyzeArtifactFile = (file, context) => {
74
77
  deadline: context.deadline,
75
78
  now: context.now,
76
79
  });
77
- state.files.push({ file, javascript: analysis });
80
+ const staticFindings = findingCount(analysis);
81
+ const semanticFindingBudget = Math.max(0, input.limits.max_findings - state.findings - staticFindings);
82
+ const semanticLimits = {
83
+ ...DEFAULT_JAVASCRIPT_SEMANTIC_LIMITS,
84
+ maxScopes: remainingNodes,
85
+ maxBindings: remainingNodes,
86
+ maxCallables: remainingNodes,
87
+ maxReferences: remainingNodes,
88
+ maxModuleLinks: semanticFindingBudget,
89
+ };
90
+ const semantics = analysis.parse_status === "complete" || analysis.parse_status === "partial"
91
+ ? analyzeJavaScriptSemantics(file.text.value, semanticLimits)
92
+ : null;
93
+ state.files.push({
94
+ file,
95
+ javascript: analysis,
96
+ semantic: semantics === null ? null : { ir: semantics, limits: semanticLimits },
97
+ });
78
98
  state.visitedNodes += analysis.visited_ast_nodes;
79
- state.findings += findingCount(analysis);
99
+ state.findings += staticFindings + (semantics?.moduleLinks.length ?? 0);
80
100
  state.modules += analysis.bundler_registrations.reduce((count, registration) => count + registration.modules.length, 0);
81
101
  if (analysis.parse_status === "failed")
82
102
  state.parseFailures += 1;
83
103
  if (analysis.parse_status === "truncated")
84
104
  state.truncatedScopes += 1;
105
+ if (semantics?.coverage.status === "truncated")
106
+ state.truncatedScopes += 1;
85
107
  };
86
108
  const addSourceMap = (file, context) => {
87
109
  const sourceMap = parseSourceMap(file, {
@@ -96,6 +118,7 @@ const addSourceMap = (file, context) => {
96
118
  const emptyArtifactAnalysis = () => ({
97
119
  files: [],
98
120
  packages: [],
121
+ jsonModules: [],
99
122
  htmlScripts: [],
100
123
  sourceMaps: [],
101
124
  visitedNodes: 0,
@@ -105,6 +128,16 @@ const emptyArtifactAnalysis = () => ({
105
128
  truncatedScopes: 0,
106
129
  sourceMapSources: 0,
107
130
  });
131
+ const addStructuredObservations = (file, state) => {
132
+ if (file.kind === "package-json")
133
+ state.packages.push(parsePackage(file));
134
+ if (file.kind !== "json")
135
+ return;
136
+ const json = analyzeJavaScriptJsonModule(file);
137
+ state.jsonModules.push(json);
138
+ if (json.status === "invalid")
139
+ state.parseFailures += 1;
140
+ };
108
141
  const parsePackage = (file) => {
109
142
  if (!file.text.included)
110
143
  return unavailablePackage(file, "Package metadata text was unavailable.");
@@ -146,6 +179,7 @@ const unavailablePackage = (file, limitation) => ({
146
179
  });
147
180
  const parseHtmlScripts = (path, text, maximum) => {
148
181
  const scripts = [];
182
+ const baseHref = htmlBaseHref(text);
149
183
  const pattern = /<script\b[^>]*\bsrc\s*=\s*(["'])([^"']+)\1[^>]*>/giu;
150
184
  for (const match of text.matchAll(pattern)) {
151
185
  const script = match[2]?.slice(0, 4_096);
@@ -155,11 +189,16 @@ const parseHtmlScripts = (path, text, maximum) => {
155
189
  scripts.push({
156
190
  html_path: path,
157
191
  script_path: script,
192
+ base_href: baseHref,
158
193
  location: rangeForOffsets(text, start, start + match[0].length),
159
194
  });
160
195
  }
161
196
  return scripts;
162
197
  };
198
+ const htmlBaseHref = (text) => {
199
+ const match = /<base\b[^>]*\bhref\s*=\s*(["'])([^"']+)\1[^>]*>/iu.exec(text);
200
+ return match?.[2]?.slice(0, 4_096) ?? null;
201
+ };
163
202
  const parseSourceMap = (file, context) => {
164
203
  if (!file.text.included)
165
204
  return unavailableSourceMap(file);
@@ -197,6 +197,8 @@ const relevantKind = (path) => {
197
197
  const lower = path.toLowerCase();
198
198
  if (lower === "package.json" || lower.endsWith("/package.json"))
199
199
  return "package-json";
200
+ if (lower.endsWith(".json"))
201
+ return "json";
200
202
  if (/\.(?:cjs|mjs|js|jsx|ts|tsx)$/u.test(lower))
201
203
  return "javascript";
202
204
  if (/\.html?$/u.test(lower))
@@ -215,10 +217,12 @@ const filePriority = (kind) => {
215
217
  return 1;
216
218
  case "javascript":
217
219
  return 2;
218
- case "source-map":
220
+ case "json":
219
221
  return 3;
220
- case "native-addon":
222
+ case "source-map":
221
223
  return 4;
224
+ case "native-addon":
225
+ return 5;
222
226
  }
223
227
  };
224
228
  const isFilesystemAsar = (entry, path) => entry.kind === "file" &&