rea-agents 2.0.0 → 2.2.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 (273) hide show
  1. package/README.md +103 -38
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/AndroidApplicationService.js +17 -0
  4. package/dist/application/AppleApplicationService.js +17 -0
  5. package/dist/application/ArtifactExtraction.js +1 -1
  6. package/dist/application/ArtifactInventory/classify.js +42 -0
  7. package/dist/application/ArtifactInventory/hash.js +43 -0
  8. package/dist/application/ArtifactInventory/reader.js +41 -0
  9. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  10. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  11. package/dist/application/ArtifactInventory/types.js +10 -0
  12. package/dist/application/ArtifactInventory.js +9 -351
  13. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  14. package/dist/application/BinarySession.js +47 -270
  15. package/dist/application/BinarySessionExecution.js +28 -0
  16. package/dist/application/BinarySessionOpen.js +39 -0
  17. package/dist/application/BinarySessionRecords.js +149 -0
  18. package/dist/application/BinarySessionStatus.js +75 -0
  19. package/dist/application/CapabilityInventory.js +24 -2
  20. package/dist/application/ClientRegistrationStatus.js +11 -4
  21. package/dist/application/CrossVersionInventory.js +8 -2
  22. package/dist/application/CrossVersionInvestigation.js +1 -23
  23. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  24. package/dist/application/DirectAnalysis.js +47 -44
  25. package/dist/application/Doctor.js +156 -238
  26. package/dist/application/DoctorDiagnostics.js +180 -0
  27. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  28. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  29. package/dist/application/EnhancedToolPagination.js +22 -0
  30. package/dist/application/EnhancedToolTypes.js +4 -0
  31. package/dist/application/EnhancedTools.js +20 -37
  32. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  33. package/dist/application/InventoryProjectionEvidence.js +41 -0
  34. package/dist/application/InvestigationProviders.js +12 -0
  35. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  36. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  37. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  38. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  39. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  40. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  41. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  42. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  43. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  44. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  45. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  46. package/dist/application/JavaScriptJsonModules.js +39 -0
  47. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  48. package/dist/application/JavaScriptReplayPermission.js +16 -0
  49. package/dist/application/JavaScriptReplayService.js +182 -156
  50. package/dist/application/LinuxHopper.js +10 -0
  51. package/dist/application/MacHopper.js +22 -11
  52. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  53. package/dist/application/PermissionAuthority.js +106 -63
  54. package/dist/application/ProcessCaptureCapability.js +1 -1
  55. package/dist/application/ProcessCapturePermission.js +13 -0
  56. package/dist/application/ProcessCli.js +2 -12
  57. package/dist/application/ProcessHarness.js +1 -1
  58. package/dist/application/ProcessPairedExperiment.js +72 -0
  59. package/dist/application/ProjectPermissionStore.js +48 -9
  60. package/dist/application/RealHopperAssertions.js +76 -0
  61. package/dist/application/ReconstructionCoverageService.js +69 -0
  62. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  63. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  64. package/dist/application/Setup.js +148 -260
  65. package/dist/application/SetupClientConfiguration.js +224 -0
  66. package/dist/application/SetupClients.js +24 -2
  67. package/dist/application/SetupPlan.js +124 -12
  68. package/dist/application/SetupSkill.js +54 -36
  69. package/dist/application/SupportedClients.js +1 -0
  70. package/dist/application/Uninstall.js +12 -3
  71. package/dist/application/Upgrade.js +16 -1
  72. package/dist/application/WindowsCapabilities.js +86 -0
  73. package/dist/artifacts/AsarArtifactReader.js +11 -0
  74. package/dist/browser/BrowserProcessStartup.js +88 -0
  75. package/dist/browser/CdpCaptureDocuments.js +12 -8
  76. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  77. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  78. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  79. package/dist/browser/CdpCaptureEventState.js +1 -0
  80. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  81. package/dist/browser/CdpCaptureEvents.js +29 -496
  82. package/dist/browser/CdpObservationSession.js +51 -8
  83. package/dist/browser/CdpPageCapture.js +12 -133
  84. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  85. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  86. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  87. package/dist/browser/PngVisualDiff.js +9 -2
  88. package/dist/catalogIdentity.js +4 -0
  89. package/dist/cli/artifactCommands.js +82 -0
  90. package/dist/cli/coreAnalysisCommands.js +232 -0
  91. package/dist/cli/managedCommands.js +397 -0
  92. package/dist/cli/managedProjectGraphCommand.js +24 -0
  93. package/dist/cli/options.js +9 -0
  94. package/dist/cli/setupCommands.js +113 -0
  95. package/dist/cli/types.js +1 -0
  96. package/dist/cli/utilityCommands.js +122 -0
  97. package/dist/cli.js +10 -768
  98. package/dist/cliApplicationCommands.js +118 -4
  99. package/dist/cliBrowserAdvancedCommands.js +73 -16
  100. package/dist/cliBrowserCommands.js +39 -115
  101. package/dist/cliCommandNames.js +4 -0
  102. package/dist/cliElectronCommands.js +33 -62
  103. package/dist/cliEvidenceCommands.js +13 -2
  104. package/dist/cliInvestigationCommands.js +53 -10
  105. package/dist/cliObservationOptions.js +137 -0
  106. package/dist/cliPolicyCommands.js +80 -46
  107. package/dist/cliSetup.js +306 -0
  108. package/dist/config/environment.js +107 -0
  109. package/dist/config/parseConfig.js +228 -0
  110. package/dist/config/parsers.js +68 -0
  111. package/dist/config/permissions.js +30 -0
  112. package/dist/config/types.js +1 -0
  113. package/dist/config.js +1 -375
  114. package/dist/contracts/applicationToolContracts.js +134 -0
  115. package/dist/contracts/electronToolContracts.js +51 -3
  116. package/dist/contracts/managedToolContracts.js +8 -3
  117. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  118. package/dist/contracts/providerSelection.js +2 -0
  119. package/dist/contracts/sessionStatusContract.js +26 -0
  120. package/dist/contracts/toolContractTypes.js +12 -1
  121. package/dist/contracts/toolContracts.js +5 -12
  122. package/dist/contracts/toolEffects.js +14 -0
  123. package/dist/contracts/toolOutputSchemaGroups.js +274 -0
  124. package/dist/contracts/toolOutputSchemaPrimitives.js +328 -0
  125. package/dist/contracts/toolOutputSchemas.js +2 -550
  126. package/dist/domain/analysisErrorPresentation.js +234 -0
  127. package/dist/domain/analysisErrorProjection.js +284 -0
  128. package/dist/domain/androidApplication.js +206 -0
  129. package/dist/domain/appleApplication.js +221 -0
  130. package/dist/domain/artifactComparison.js +34 -15
  131. package/dist/domain/binaryTarget.js +87 -37
  132. package/dist/domain/boundedCartesianProjection.js +15 -0
  133. package/dist/domain/browserObservation.js +1 -285
  134. package/dist/domain/browserObservationSchemas.js +286 -0
  135. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  136. package/dist/domain/errors.js +4 -486
  137. package/dist/domain/functionComparison.js +4 -165
  138. package/dist/domain/functionComparisonDimensions.js +164 -0
  139. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  140. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  141. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  142. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  143. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  144. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  145. package/dist/domain/javascriptSemanticIr.js +30 -0
  146. package/dist/domain/javascriptSemanticProjection.js +366 -0
  147. package/dist/domain/javascriptSemanticState.js +40 -0
  148. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  149. package/dist/domain/javascriptSemanticValues.js +352 -0
  150. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  151. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  152. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  153. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  154. package/dist/domain/knownPageCoverage.js +21 -0
  155. package/dist/domain/managedApplicationGraph.js +14 -364
  156. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  157. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  158. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  159. package/dist/domain/managedArtifact.js +16 -8
  160. package/dist/domain/managedMemberComparison.js +37 -391
  161. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  162. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  163. package/dist/domain/managedNativeVerification.js +27 -389
  164. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  165. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  166. package/dist/domain/managedReconstruction.js +82 -69
  167. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  168. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  169. package/dist/domain/permissionPolicy.js +2 -0
  170. package/dist/domain/processComparison.js +19 -0
  171. package/dist/domain/processPairedExperiment.js +128 -0
  172. package/dist/domain/reconstructionCoverage.js +387 -0
  173. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  174. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  175. package/dist/domain/replayMachine.js +316 -0
  176. package/dist/domain/replayMachineRuntime.js +215 -0
  177. package/dist/domain/runtimeCharacterization.js +85 -0
  178. package/dist/domain/runtimeIdentification.js +210 -0
  179. package/dist/domain/webBundleAnalyzer.js +3 -409
  180. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  181. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  182. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  183. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  184. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  185. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  186. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  187. package/dist/dotnet/ManagedMemberRows.js +120 -0
  188. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  189. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  190. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  191. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  192. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  193. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  194. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  195. package/dist/dotnet/ManagedPeReader.js +59 -28
  196. package/dist/evaluation/CodexAgentEval.js +132 -0
  197. package/dist/generatedPackageMetadata.js +2 -2
  198. package/dist/ghidra/GhidraClient.js +76 -91
  199. package/dist/ghidra/GhidraClientStartup.js +29 -0
  200. package/dist/ghidra/GhidraClientWire.js +34 -0
  201. package/dist/ghidra/GhidraDefaults.js +1 -1
  202. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  203. package/dist/ghidra/GhidraInstallation.js +20 -15
  204. package/dist/ghidra/GhidraLauncher.js +93 -29
  205. package/dist/ghidra/GhidraProvider.js +61 -161
  206. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  207. package/dist/ghidra/GhidraSessionValues.js +2 -0
  208. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  209. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  210. package/dist/ghidra/GhidraTransport.js +71 -0
  211. package/dist/identity.js +4 -2
  212. package/dist/main/messages.js +5 -0
  213. package/dist/main/reload.js +49 -0
  214. package/dist/main/shutdown.js +25 -0
  215. package/dist/main/startup.js +14 -0
  216. package/dist/main/state.js +26 -0
  217. package/dist/main/transport.js +61 -0
  218. package/dist/main/types.js +1 -0
  219. package/dist/main.js +35 -146
  220. package/dist/process/PrivateRuntimeRoot.js +3 -2
  221. package/dist/process/ProcessOwnership.js +35 -0
  222. package/dist/process/ProviderProcess.js +2 -0
  223. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  224. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  225. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  226. package/dist/replay/ReplayOutcome.js +95 -0
  227. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  228. package/dist/server/ProcessCaptureElicitation.js +157 -0
  229. package/dist/server/createServer.js +124 -56
  230. package/dist/server/javascriptApplicationResult.js +85 -0
  231. package/dist/server/registerApplicationTools/characterization.js +57 -0
  232. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  233. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  234. package/dist/server/registerApplicationTools/coverage.js +84 -0
  235. package/dist/server/registerApplicationTools/helpers.js +42 -0
  236. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  237. package/dist/server/registerApplicationTools/types.js +1 -0
  238. package/dist/server/registerApplicationTools.js +10 -112
  239. package/dist/server/registerBrowserTools.js +59 -83
  240. package/dist/server/registerElectronTools.js +48 -49
  241. package/dist/server/registerEnhancedTools.js +34 -42
  242. package/dist/server/registerEvidenceResources.js +56 -0
  243. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  244. package/dist/server/registerInvestigationTools/types.js +1 -0
  245. package/dist/server/registerInvestigationTools.js +83 -116
  246. package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
  247. package/dist/server/registerManagedTools.js +18 -1
  248. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  249. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  250. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  251. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  252. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  253. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  254. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  255. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  256. package/dist/server/registerManagedWorkflowTools.js +10 -274
  257. package/dist/server/registerOfficialTools.js +73 -59
  258. package/dist/server/registerSessionRecordTools.js +137 -0
  259. package/dist/server/registerSessionStatusTool.js +97 -26
  260. package/dist/server/registerSessionTools.js +60 -164
  261. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  262. package/dist/server/toolRegistrationOptions.js +101 -3
  263. package/dist/server/toolResult.js +15 -4
  264. package/package.json +11 -11
  265. package/skills/reverse-engineer-anything/SKILL.md +76 -0
  266. package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
  267. package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
  268. package/skills/reverse-engineer-anything/references/javascript-applications.md +25 -0
  269. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
  270. package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
  271. package/scripts/prepare-node-pty.mjs +0 -35
  272. package/scripts/rebuild-native.mjs +0 -22
  273. package/skills/rea-analysis/SKILL.md +0 -222
@@ -0,0 +1,132 @@
1
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2
+ const record = (value) => isRecord(value) ? value : undefined;
3
+ const textValue = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
4
+ const numberValue = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
5
+ const canonicalValue = (value) => {
6
+ if (Array.isArray(value))
7
+ return value.map(canonicalValue);
8
+ const object = record(value);
9
+ if (object === undefined)
10
+ return value;
11
+ return Object.fromEntries(Object.entries(object)
12
+ .sort(([left], [right]) => left.localeCompare(right))
13
+ .map(([key, child]) => [key, canonicalValue(child)]));
14
+ };
15
+ const evidenceIdsFrom = (value) => {
16
+ const encoded = JSON.stringify(value);
17
+ if (encoded === undefined)
18
+ return [];
19
+ return [...new Set(encoded.match(/ev_[a-f0-9]{64}/gu) ?? [])];
20
+ };
21
+ const toolResultFailed = (item) => {
22
+ if (item.status === "failed" ||
23
+ (item.error !== undefined && item.error !== null))
24
+ return true;
25
+ const result = record(item.result ?? item.output);
26
+ const structured = record(result?.structured_content ?? result?.structuredContent);
27
+ return result?.isError === true || structured?.error !== undefined;
28
+ };
29
+ const callFromItem = (value) => {
30
+ const item = record(value);
31
+ if (item?.type !== "mcp_tool_call")
32
+ return undefined;
33
+ const tool = textValue(item.tool) ?? textValue(item.name);
34
+ if (tool === undefined)
35
+ return undefined;
36
+ return {
37
+ id: textValue(item.id) ?? null,
38
+ server: textValue(item.server) ?? textValue(item.server_name) ?? null,
39
+ tool,
40
+ arguments: item.arguments ?? item.input ?? {},
41
+ evidenceIds: evidenceIdsFrom(item.result ?? item.output),
42
+ error: toolResultFailed(item),
43
+ };
44
+ };
45
+ const agentTextFromItem = (value) => {
46
+ const item = record(value);
47
+ if (item?.type !== "agent_message")
48
+ return undefined;
49
+ const direct = textValue(item.text) ?? textValue(item.message);
50
+ if (direct !== undefined)
51
+ return direct;
52
+ if (!Array.isArray(item.content))
53
+ return undefined;
54
+ const parts = item.content.flatMap((part) => {
55
+ const object = record(part);
56
+ const text = object === undefined ? undefined : textValue(object.text);
57
+ return text === undefined ? [] : [text];
58
+ });
59
+ return parts.length === 0 ? undefined : parts.join("\n");
60
+ };
61
+ const consumeEvent = (state, value) => {
62
+ const event = record(value);
63
+ if (event === undefined)
64
+ return;
65
+ const completedItem = event.type === "item.completed" ? event.item : undefined;
66
+ const call = callFromItem(completedItem) ??
67
+ (event.type === "mcp_tool_call" ? callFromItem(event) : undefined);
68
+ if (call !== undefined) {
69
+ const identity = call.id ??
70
+ `${call.server ?? ""}:${call.tool}:${JSON.stringify(canonicalValue(call.arguments))}`;
71
+ if (!state.seenCallIds.has(identity)) {
72
+ state.seenCallIds.add(identity);
73
+ state.calls.push(call);
74
+ }
75
+ }
76
+ const message = agentTextFromItem(completedItem);
77
+ if (message !== undefined)
78
+ state.finalMessages.push(message);
79
+ if (event.type !== "turn.completed")
80
+ return;
81
+ const usage = record(event.usage);
82
+ state.inputTokens = Math.max(state.inputTokens, numberValue(usage?.input_tokens));
83
+ state.cachedInputTokens = Math.max(state.cachedInputTokens, numberValue(usage?.cached_input_tokens));
84
+ state.outputTokens = Math.max(state.outputTokens, numberValue(usage?.output_tokens));
85
+ };
86
+ /** Score agent routing, repetition, model usage, and epistemic completion. */
87
+ export const evaluateCodexEvents = (events, expectedFirstTool, options = {}) => {
88
+ const state = {
89
+ calls: [],
90
+ seenCallIds: new Set(),
91
+ finalMessages: [],
92
+ inputTokens: 0,
93
+ cachedInputTokens: 0,
94
+ outputTokens: 0,
95
+ };
96
+ for (const event of events)
97
+ consumeEvent(state, event);
98
+ const reaCalls = state.calls.filter(({ server }) => server === null || server.toLowerCase() === "rea");
99
+ const signatures = new Map();
100
+ for (const call of reaCalls) {
101
+ const signature = `${call.tool}:${JSON.stringify(canonicalValue(call.arguments))}`;
102
+ signatures.set(signature, (signatures.get(signature) ?? 0) + 1);
103
+ }
104
+ const repeatedCallCount = [...signatures.values()].reduce((total, count) => total + Math.max(0, count - 1), 0);
105
+ const finalMessage = state.finalMessages.at(-1) ?? "";
106
+ const evidenceIds = [
107
+ ...new Set(reaCalls.flatMap(({ evidenceIds: ids }) => ids)),
108
+ ];
109
+ const finalCitesEvidence = evidenceIds.some((evidenceId) => finalMessage.includes(evidenceId));
110
+ const normalizedFinalMessage = finalMessage.toLocaleLowerCase("en-US");
111
+ const contentCriteriaMet = (options.requiredAnswerTermGroups ?? []).every((terms) => terms.some((term) => normalizedFinalMessage.includes(term.toLocaleLowerCase("en-US"))));
112
+ const authorityHonesty = /\b(evidence|observed|inferred|unknown|unavailable|limitation|authority|not configured|could not|requires approval)\b/iu.test(finalMessage);
113
+ return {
114
+ naturalUse: reaCalls.length > 0,
115
+ correctFirstTool: reaCalls[0]?.tool === expectedFirstTool,
116
+ firstTool: reaCalls[0]?.tool ?? null,
117
+ reaCalls,
118
+ repeatedCallCount,
119
+ inputTokens: state.inputTokens,
120
+ cachedInputTokens: state.cachedInputTokens,
121
+ outputTokens: state.outputTokens,
122
+ finalMessage,
123
+ evidenceIds,
124
+ finalCitesEvidence,
125
+ contentCriteriaMet,
126
+ completionQuality: finalMessage.trim().length >= 80 &&
127
+ authorityHonesty &&
128
+ contentCriteriaMet &&
129
+ (options.requireEvidence !== true || finalCitesEvidence),
130
+ authorityHonesty,
131
+ };
132
+ };
@@ -1,9 +1,9 @@
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.0.0",
4
+ version: "2.2.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",
8
- skillVersion: "20",
8
+ skillVersion: "23",
9
9
  };
@@ -1,5 +1,5 @@
1
1
  import { randomBytes, randomUUID } from "node:crypto";
2
- import { access } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { err, ok } from "../domain/result.js";
5
5
  import { silentLogger } from "../logger.js";
@@ -14,19 +14,23 @@ import { GhidraResponseRouter } from "./GhidraResponseRouter.js";
14
14
  import { GhidraRequestQueue } from "./GhidraRequestQueue.js";
15
15
  import { bindGhidraSessionFailure, GhidraSessionError, } from "./GhidraSessionError.js";
16
16
  import { isGhidraShutdownAcknowledgement, parseGhidraSessionInfo, } from "./GhidraSessionValues.js";
17
- import { attachGhidraSocket, connectGhidraSocketOnce, } from "./GhidraSocketConnection.js";
17
+ import { createGhidraTargetSnapshot } from "./GhidraTargetSnapshot.js";
18
+ import { connectGhidraSocket } from "./GhidraSocketConnection.js";
19
+ import { GhidraWire } from "./GhidraClientWire.js";
20
+ import { completeGhidraStartupHandshake } from "./GhidraClientStartup.js";
18
21
  const SHUTDOWN_TIMEOUT_MS = 10_000;
19
- const SESSION_ROOT = "/tmp";
22
+ const SESSION_ROOT = tmpdir();
20
23
  /** Owns one authenticated, private, read-only Ghidra headless session. */
21
24
  export class GhidraClient {
22
25
  #options;
23
- #pending = new PendingOperations();
24
26
  #logger;
27
+ #pending = new PendingOperations();
25
28
  #socket;
26
29
  #detachSocket;
27
30
  #launch;
28
31
  #process;
29
32
  #runtimeRoot;
33
+ #snapshotPath;
30
34
  #token;
31
35
  #runId;
32
36
  #nextId = 1;
@@ -37,6 +41,7 @@ export class GhidraClient {
37
41
  #startPromise;
38
42
  #closePromise;
39
43
  #failure = bindGhidraSessionFailure(() => this.#diagnostics());
44
+ #wire;
40
45
  #requestQueue;
41
46
  #responseRouter = new GhidraResponseRouter({
42
47
  pending: this.#pending,
@@ -60,13 +65,23 @@ export class GhidraClient {
60
65
  this.#failAll(this.#failure("process", "Ghidra bridge socket closed"));
61
66
  };
62
67
  constructor(options) {
63
- this.#logger = options.logger ?? silentLogger;
64
68
  this.#options = {
65
69
  ...options,
66
70
  requestTimeoutMs: options.requestTimeoutMs ?? GHIDRA_REQUEST_TIMEOUT_MS,
67
71
  startupTimeoutMs: options.startupTimeoutMs ?? GHIDRA_STARTUP_TIMEOUT_MS,
72
+ transport: options.transport ?? "unix-socket",
68
73
  };
69
- this.#requestQueue = new GhidraRequestQueue(GHIDRA_MAX_QUEUED_REQUESTS, (method, parameters, requestOptions) => this.#request(method, parameters, requestOptions), (kind, message, timeoutMs) => this.#failure(kind, message, undefined, timeoutMs === undefined ? {} : { timeoutMs }));
74
+ this.#logger = options.logger ?? silentLogger;
75
+ this.#wire = new GhidraWire({
76
+ getSocket: () => this.#socket,
77
+ getToken: () => this.#token,
78
+ nextId: () => this.#nextId++,
79
+ pending: this.#pending,
80
+ requestTimeoutMs: this.#options.requestTimeoutMs,
81
+ logger: this.#logger,
82
+ failure: this.#failure,
83
+ });
84
+ this.#requestQueue = new GhidraRequestQueue(GHIDRA_MAX_QUEUED_REQUESTS, (method, parameters, requestOptions) => this.#wire.request(method, parameters, requestOptions), (kind, message, timeoutMs) => this.#failure(kind, message, undefined, timeoutMs === undefined ? {} : { timeoutMs }));
70
85
  }
71
86
  /** Launch Ghidra once and require its exact post-analysis handshake. */
72
87
  start(signal) {
@@ -103,7 +118,7 @@ export class GhidraClient {
103
118
  const started = await this.start(options.signal);
104
119
  if (!started.ok)
105
120
  return started;
106
- const result = await this.#request("ping", {}, options);
121
+ const result = await this.#wire.request("ping", {}, options);
107
122
  if (!result.ok)
108
123
  return result;
109
124
  return this.#parseSessionInfo(result.value);
@@ -155,16 +170,34 @@ export class GhidraClient {
155
170
  }
156
171
  if (deadline.signal.aborted)
157
172
  return this.#startupInterrupted(deadline);
158
- const socketPath = join(this.#runtimeRoot.path, "bridge.sock");
173
+ const endpoint = {
174
+ transport: this.#options.transport,
175
+ path: join(this.#runtimeRoot.path, this.#options.transport === "unix-socket"
176
+ ? "bridge.sock"
177
+ : "bridge-endpoint.json"),
178
+ };
179
+ try {
180
+ const snapshot = await createGhidraTargetSnapshot(this.#options.targetPath, this.#runtimeRoot.path, this.#options.targetSha256);
181
+ this.#snapshotPath = snapshot.path;
182
+ }
183
+ catch (cause) {
184
+ const failure = this.#failure("start", "Ghidra target snapshot failed admission", cause);
185
+ await this.#cleanup();
186
+ return err(failure);
187
+ }
188
+ if (deadline.signal.aborted)
189
+ return this.#startupInterrupted(deadline);
159
190
  this.#token = randomBytes(32).toString("hex");
160
191
  this.#runId = randomUUID();
161
192
  const launched = await this.#options.launcher
162
193
  .launch({
163
194
  runtimeRoot: this.#runtimeRoot.path,
164
- socketPath,
195
+ transport: endpoint.transport,
196
+ endpointPath: endpoint.path,
165
197
  token: this.#token,
166
198
  runId: this.#runId,
167
- targetPath: this.#options.targetPath,
199
+ targetPath: this.#snapshotPath,
200
+ targetSha256: this.#options.targetSha256,
168
201
  providerVersion: this.#options.providerVersion,
169
202
  profileDigest: this.#options.profileDigest,
170
203
  }, { signal: deadline.signal })
@@ -182,92 +215,40 @@ export class GhidraClient {
182
215
  this.#process = new ProviderProcessSupervisor(launched.value, {
183
216
  onDiagnostic: (event) => this.#onProcessDiagnostic(event),
184
217
  });
185
- const connected = await this.#connect(socketPath, deadline);
218
+ const connected = await this.#connect(endpoint, deadline);
186
219
  if (!connected.ok) {
187
220
  const failure = connected.error;
188
221
  await this.#cleanup();
189
222
  return err(failure);
190
223
  }
191
- const ping = await this.#request("ping", {}, {
192
- signal: deadline.signal,
193
- timeoutMs: deadline.remainingMs(),
224
+ return completeGhidraStartupHandshake({
225
+ deadline,
226
+ request: (method, params, requestOptions) => this.#wire.request(method, params, requestOptions),
227
+ parseSessionInfo: (value) => this.#parseSessionInfo(value),
228
+ cleanup: () => this.#cleanup(),
229
+ failure: this.#failure,
230
+ startupTimeoutMs: this.#options.startupTimeoutMs,
194
231
  });
195
- if (!ping.ok) {
196
- const failure = deadline.signal.aborted
197
- ? this.#interruptionFailure(deadline)
198
- : ping.error;
199
- await this.#cleanup();
200
- return err(failure);
201
- }
202
- const parsed = this.#parseSessionInfo(ping.value);
203
- if (!parsed.ok || parsed.value.analysis_timed_out) {
204
- const failure = parsed.ok
205
- ? this.#failure("analysis_timeout", "Ghidra auto-analysis reached its per-file deadline")
206
- : parsed.error;
207
- await this.#cleanup();
208
- return err(failure);
209
- }
210
- return parsed;
211
232
  }
212
- async #connect(socketPath, deadline) {
213
- while (deadline.remainingMs() > 0) {
214
- if (deadline.signal.aborted)
215
- return err(this.#interruptionFailure(deadline));
216
- if (this.#closing)
217
- return err(this.#failure("cancelled", "Ghidra startup was closed"));
218
- if (this.#processSnapshot?.exitCode !== undefined)
219
- return err(this.#failure("process", "Ghidra exited before bridge startup"));
220
- try {
221
- await access(socketPath);
222
- }
223
- catch {
224
- if ((await deadline.wait(50)) === "aborted")
225
- return err(this.#interruptionFailure(deadline));
226
- continue;
227
- }
228
- const connected = await connectGhidraSocketOnce(socketPath, deadline.signal);
229
- if (connected.ok) {
230
- this.#socket = connected.value;
231
- this.#detachSocket = attachGhidraSocket(connected.value, {
232
- data: this.#onSocketData,
233
- error: this.#onSocketError,
234
- close: this.#onSocketClose,
235
- });
236
- return ok(undefined);
237
- }
238
- if ((await deadline.wait(50)) === "aborted")
239
- return err(this.#interruptionFailure(deadline));
240
- }
241
- return err(this.#failure("timeout", "Ghidra startup deadline elapsed", undefined, {
242
- timeoutMs: this.#options.startupTimeoutMs,
243
- }));
244
- }
245
- async #request(method, params, options) {
246
- const socket = this.#socket;
247
- const token = this.#token;
248
- if (socket === undefined || socket.destroyed || token === undefined)
249
- return err(this.#failure("process", "Ghidra bridge is unavailable"));
250
- if (options.signal?.aborted === true)
251
- return err(this.#failure("cancelled", "Ghidra request was cancelled"));
252
- const id = this.#nextId++;
253
- const timeoutMs = options.timeoutMs ?? this.#options.requestTimeoutMs;
254
- if (timeoutMs <= 0)
255
- return err(this.#failure("timeout", "Ghidra request deadline elapsed", undefined, {
256
- timeoutMs,
257
- }));
258
- const response = this.#pending.wait(id, {
259
- timeoutMs,
260
- ...(options.signal === undefined ? {} : { signal: options.signal }),
261
- timeoutValue: () => err(this.#failure("timeout", "Ghidra bridge request timed out", undefined, { timeoutMs })),
262
- cancelledValue: () => err(this.#failure("cancelled", "Ghidra request was cancelled")),
263
- });
264
- socket.write(`${JSON.stringify({ id, token, method, params })}\n`, (cause) => {
265
- if (cause !== undefined && cause !== null)
266
- this.#pending.settle(id, err(this.#failure("process", "Ghidra bridge write failed", cause)));
233
+ async #connect(endpoint, deadline) {
234
+ const connected = await connectGhidraSocket({
235
+ endpoint,
236
+ deadline,
237
+ failure: this.#failure,
238
+ isClosed: () => this.#closing,
239
+ processExited: () => this.#processSnapshot?.exitCode !== undefined,
240
+ startupTimeoutMs: this.#options.startupTimeoutMs,
241
+ handlers: {
242
+ data: this.#onSocketData,
243
+ error: this.#onSocketError,
244
+ close: this.#onSocketClose,
245
+ },
267
246
  });
268
- const result = await response;
269
- this.#logger[result.ok ? "debug" : "warn"]({ method, status: result.ok ? "ok" : "error" }, "Ghidra bridge request completed");
270
- return result;
247
+ if (!connected.ok)
248
+ return connected;
249
+ this.#socket = connected.value.socket;
250
+ this.#detachSocket = connected.value.detach;
251
+ return ok(undefined);
271
252
  }
272
253
  #parseSessionInfo(value) {
273
254
  const runId = this.#runId;
@@ -277,6 +258,7 @@ export class GhidraClient {
277
258
  runId,
278
259
  providerVersion: this.#options.providerVersion,
279
260
  profileDigest: this.#options.profileDigest,
261
+ targetSha256: this.#options.targetSha256,
280
262
  });
281
263
  if (parsed.ok)
282
264
  return parsed;
@@ -301,9 +283,9 @@ export class GhidraClient {
301
283
  this.#requestQueue.failQueued(this.#failure("process", "Ghidra session closed"));
302
284
  const socket = this.#socket;
303
285
  if (socket !== undefined && !socket.destroyed) {
304
- const shutdown = await this.#request("shutdown", {}, {
305
- timeoutMs: SHUTDOWN_TIMEOUT_MS,
306
- }).catch(() => err(this.#failure("process", "Ghidra shutdown request failed")));
286
+ const shutdown = await this.#wire
287
+ .request("shutdown", {}, { timeoutMs: SHUTDOWN_TIMEOUT_MS })
288
+ .catch(() => err(this.#failure("process", "Ghidra shutdown request failed")));
307
289
  if (!shutdown.ok || !isGhidraShutdownAcknowledgement(shutdown.value))
308
290
  this.#logger.warn({ status: shutdown.ok ? "invalid-acknowledgement" : "failed" }, "Ghidra bridge shutdown was not confirmed");
309
291
  }
@@ -331,6 +313,7 @@ export class GhidraClient {
331
313
  const runtimeRoot = this.#runtimeRoot;
332
314
  this.#runtimeRoot = undefined;
333
315
  await runtimeRoot?.close();
316
+ this.#snapshotPath = undefined;
334
317
  this.#token = undefined;
335
318
  this.#runId = undefined;
336
319
  this.#responseBuffer.reset();
@@ -386,6 +369,8 @@ export class GhidraClient {
386
369
  const snapshot = this.#process?.snapshot() ?? this.#processSnapshot;
387
370
  return createGhidraDiagnostics({
388
371
  targetPath: this.#options.targetPath,
372
+ targetSha256: this.#options.targetSha256,
373
+ transport: this.#options.transport,
389
374
  providerVersion: this.#options.providerVersion,
390
375
  profileDigest: this.#options.profileDigest,
391
376
  ...(this.#runtimeRoot === undefined
@@ -0,0 +1,29 @@
1
+ import { err } from "../domain/result.js";
2
+ /** Ping and validate the bridge after its socket is attached. */
3
+ export async function completeGhidraStartupHandshake(options) {
4
+ const ping = await options.request("ping", {}, {
5
+ signal: options.deadline.signal,
6
+ timeoutMs: options.deadline.remainingMs(),
7
+ });
8
+ if (!ping.ok) {
9
+ const failure = options.deadline.signal.aborted
10
+ ? interruptionFailure(options)
11
+ : ping.error;
12
+ await options.cleanup();
13
+ return err(failure);
14
+ }
15
+ const parsed = options.parseSessionInfo(ping.value);
16
+ if (!parsed.ok || parsed.value.analysis_timed_out) {
17
+ const failure = parsed.ok
18
+ ? options.failure("analysis_timeout", "Ghidra auto-analysis reached its per-file deadline")
19
+ : parsed.error;
20
+ await options.cleanup();
21
+ return err(failure);
22
+ }
23
+ return parsed;
24
+ }
25
+ const interruptionFailure = (options) => options.deadline.cancelled
26
+ ? options.failure("cancelled", "Ghidra startup was cancelled")
27
+ : options.failure("timeout", "Ghidra startup deadline elapsed", undefined, {
28
+ timeoutMs: options.startupTimeoutMs,
29
+ });
@@ -0,0 +1,34 @@
1
+ import { err } from "../domain/result.js";
2
+ /** Owns the socket write and pending-reply lifecycle for one bridge session. */
3
+ export class GhidraWire {
4
+ #options;
5
+ constructor(options) {
6
+ this.#options = options;
7
+ }
8
+ /** Write one authenticated request and return its correlated reply. */
9
+ async request(method, params, options) {
10
+ const socket = this.#options.getSocket();
11
+ const token = this.#options.getToken();
12
+ if (socket === undefined || socket.destroyed || token === undefined)
13
+ return err(this.#options.failure("process", "Ghidra bridge is unavailable"));
14
+ if (options.signal?.aborted === true)
15
+ return err(this.#options.failure("cancelled", "Ghidra request was cancelled"));
16
+ const id = this.#options.nextId();
17
+ const timeoutMs = options.timeoutMs ?? this.#options.requestTimeoutMs;
18
+ if (timeoutMs <= 0)
19
+ return err(this.#options.failure("timeout", "Ghidra request deadline elapsed", undefined, { timeoutMs }));
20
+ const response = this.#options.pending.wait(id, {
21
+ timeoutMs,
22
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
23
+ timeoutValue: () => err(this.#options.failure("timeout", "Ghidra bridge request timed out", undefined, { timeoutMs })),
24
+ cancelledValue: () => err(this.#options.failure("cancelled", "Ghidra request was cancelled")),
25
+ });
26
+ socket.write(`${JSON.stringify({ id, token, method, params })}\n`, (cause) => {
27
+ if (cause !== undefined && cause !== null)
28
+ this.#options.pending.settle(id, err(this.#options.failure("process", "Ghidra bridge write failed", cause)));
29
+ });
30
+ const result = await response;
31
+ this.#options.logger[result.ok ? "debug" : "warn"]({ method, status: result.ok ? "ok" : "error" }, "Ghidra bridge request completed");
32
+ return result;
33
+ }
34
+ }
@@ -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 = 3;
20
+ export const GHIDRA_BRIDGE_VERSION = 4;
@@ -1,3 +1,4 @@
1
+ import { join } from "node:path";
1
2
  /** Preserve actionable runtime coordinates after cleanup and redact bearer data. */
2
3
  export const createGhidraDiagnostics = (options) => {
3
4
  if (options.runtimeRoot === undefined &&
@@ -9,13 +10,17 @@ export const createGhidraDiagnostics = (options) => {
9
10
  : value.replaceAll(options.token, "[REDACTED]");
10
11
  return {
11
12
  target_path: options.targetPath,
13
+ target_sha256: options.targetSha256,
14
+ transport: options.transport,
12
15
  provider_version: options.providerVersion,
13
16
  profile_digest: options.profileDigest,
14
17
  ...(options.runtimeRoot === undefined
15
18
  ? {}
16
19
  : {
17
20
  runtime_root: options.runtimeRoot,
18
- socket_path: `${options.runtimeRoot}/bridge.sock`,
21
+ endpoint_path: join(options.runtimeRoot, options.transport === "unix-socket"
22
+ ? "bridge.sock"
23
+ : "bridge-endpoint.json"),
19
24
  }),
20
25
  ...(options.launch === undefined
21
26
  ? {}
@@ -1,7 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { constants } from "node:fs";
3
3
  import { accessSync, readFileSync } from "node:fs";
4
- import { delimiter, join } from "node:path";
4
+ import { posix, win32 } from "node:path";
5
5
  /** Exact Ghidra build whose Java bridge contract this REA release supports. */
6
6
  export const SUPPORTED_GHIDRA_VERSION = "12.1.2";
7
7
  /** Exact JDK major documented by the supported Ghidra release. */
@@ -10,7 +10,7 @@ export const SUPPORTED_GHIDRA_JAVA_MAJOR = 21;
10
10
  export const inspectGhidraInstallation = (options, host = systemGhidraInstallationHost()) => {
11
11
  const platform = options.platform ?? process.platform;
12
12
  const architecture = options.architecture ?? process.arch;
13
- const coordinates = installationCoordinates(options, host);
13
+ const coordinates = installationCoordinates(options, platform, host);
14
14
  const checks = installationChecks({
15
15
  coordinates,
16
16
  platform,
@@ -35,18 +35,23 @@ export const inspectGhidraInstallation = (options, host = systemGhidraInstallati
35
35
  reason: failed?.remediation ?? null,
36
36
  };
37
37
  };
38
- const installationCoordinates = (options, host) => {
38
+ const installationCoordinates = (options, platform, host) => {
39
+ const path = platform === "win32" ? win32 : posix;
39
40
  const installDir = options.installDir ?? null;
40
41
  const applicationPropertiesPath = installDir === null
41
42
  ? null
42
- : join(installDir, "Ghidra", "application.properties");
43
- const analyzeHeadlessPath = installDir === null ? null : join(installDir, "support", "analyzeHeadless");
43
+ : path.join(installDir, "Ghidra", "application.properties");
44
+ const analyzeHeadlessPath = installDir === null
45
+ ? null
46
+ : path.join(installDir, "support", platform === "win32" ? "analyzeHeadless.bat" : "analyzeHeadless");
44
47
  const properties = applicationPropertiesPath === null
45
48
  ? undefined
46
49
  : host.readText(applicationPropertiesPath);
47
50
  const javaCommand = options.javaHome === undefined
48
- ? "java"
49
- : join(options.javaHome, "bin", "java");
51
+ ? platform === "win32"
52
+ ? "java.exe"
53
+ : "java"
54
+ : path.join(options.javaHome, "bin", platform === "win32" ? "java.exe" : "java");
50
55
  return {
51
56
  installDir,
52
57
  applicationPropertiesPath,
@@ -56,7 +61,7 @@ const installationCoordinates = (options, host) => {
56
61
  ? null
57
62
  : propertyValue(properties, "application.version"),
58
63
  javaCommand,
59
- java: host.probeJava(javaCommand, ghidraJavaEnvironment(options.javaHome)),
64
+ java: host.probeJava(javaCommand, ghidraJavaEnvironment(options.javaHome, process.env, platform)),
60
65
  };
61
66
  };
62
67
  const installationChecks = ({ coordinates, platform, architecture, javaHome, host, }) => [
@@ -69,17 +74,17 @@ const installationChecks = ({ coordinates, platform, architecture, javaHome, hos
69
74
  }),
70
75
  installationCheck({
71
76
  name: "platform",
72
- ok: platform === "linux",
77
+ ok: platform === "linux" || platform === "win32",
73
78
  code: "unsupported_host",
74
79
  detail: platform,
75
- remediation: "REA's Ghidra v1 adapter currently supports Linux only.",
80
+ remediation: "Use REA's Ghidra adapter on a supported Linux or Windows x64 host.",
76
81
  }),
77
82
  installationCheck({
78
83
  name: "architecture",
79
84
  ok: architecture === "x64",
80
85
  code: "unsupported_host",
81
86
  detail: architecture,
82
- remediation: "Use the official Linux x86-64 Ghidra distribution on an x64 host.",
87
+ remediation: "Use the official x86-64 Ghidra distribution on an x64 Linux or Windows host.",
83
88
  }),
84
89
  installationCheck({
85
90
  name: "installation",
@@ -104,7 +109,7 @@ const installationChecks = ({ coordinates, platform, architecture, javaHome, hos
104
109
  host.executable(coordinates.analyzeHeadlessPath),
105
110
  code: "executable_missing",
106
111
  detail: coordinates.analyzeHeadlessPath ?? "analyzeHeadless unavailable",
107
- remediation: "Restore the executable support/analyzeHeadless script from the official Ghidra release.",
112
+ remediation: "Restore support/analyzeHeadless or support/analyzeHeadless.bat from the official Ghidra release.",
108
113
  }),
109
114
  javaCheck(coordinates.java, coordinates.javaCommand, javaHome),
110
115
  ];
@@ -127,7 +132,7 @@ export const ghidraInstallationDiagnostics = (inspection) => ({
127
132
  })),
128
133
  });
129
134
  /** Environment that makes an explicitly selected JDK win over PATH discovery. */
130
- export const ghidraJavaEnvironment = (javaHome, environment = process.env) => {
135
+ export const ghidraJavaEnvironment = (javaHome, environment = process.env, platform = process.platform) => {
131
136
  const boundedEnvironment = {
132
137
  ...environment,
133
138
  _JAVA_OPTIONS: "",
@@ -140,7 +145,7 @@ export const ghidraJavaEnvironment = (javaHome, environment = process.env) => {
140
145
  : {
141
146
  ...boundedEnvironment,
142
147
  JAVA_HOME: javaHome,
143
- PATH: `${join(javaHome, "bin")}${delimiter}${environment.PATH ?? ""}`,
148
+ PATH: `${(platform === "win32" ? win32 : posix).join(javaHome, "bin")}${platform === "win32" ? win32.delimiter : posix.delimiter}${environment.PATH ?? ""}`,
144
149
  };
145
150
  };
146
151
  const installationCheck = (options) => ({
@@ -217,7 +222,7 @@ const systemGhidraInstallationHost = () => ({
217
222
  major: javaMajor(version),
218
223
  home,
219
224
  bits,
220
- jdk: executablePath(join(home, "bin", "javac")),
225
+ jdk: executablePath((process.platform === "win32" ? win32 : posix).join(home, "bin", process.platform === "win32" ? "javac.exe" : "javac")),
221
226
  };
222
227
  },
223
228
  });