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
@@ -61,6 +61,81 @@ export const requireFunctionDossier = (input, expectedAddress) => {
61
61
  validateInstructionScan(value.instruction_scan);
62
62
  return value;
63
63
  };
64
+ /** Reject a structurally valid dossier that omits fixture-proven semantics. */
65
+ export const requireFunctionDossierOracle = (input, oracle) => {
66
+ const value = requireFunctionDossier(input, oracle.procedure_address);
67
+ if (oracle.callee_address !== undefined) {
68
+ const callees = z
69
+ .array(identitySchema)
70
+ .parse(collectionSchema.parse(value.callees).items);
71
+ if (!callees.some(({ address }) => address === oracle.callee_address))
72
+ throw new TypeError("analyze_function omitted the expected fixture callee");
73
+ const references = z
74
+ .array(z.object({
75
+ source_address: addressSchema,
76
+ target_address: addressSchema,
77
+ }))
78
+ .parse(collectionSchema.parse(value.outgoing_references).items);
79
+ if (!references.some(({ target_address }) => target_address === oracle.callee_address))
80
+ throw new TypeError("analyze_function omitted the expected fixture reference");
81
+ }
82
+ if (oracle.caller_address !== undefined) {
83
+ const callers = z
84
+ .array(identitySchema)
85
+ .parse(collectionSchema.parse(value.callers).items);
86
+ if (!callers.some(({ address }) => address === oracle.caller_address))
87
+ throw new TypeError("analyze_function omitted the expected fixture caller");
88
+ }
89
+ if (oracle.referenced_string !== undefined)
90
+ requireReferencedValue(value, "referenced_strings", oracle.referenced_string, (candidate) => candidate);
91
+ if (oracle.referenced_name !== undefined)
92
+ requireReferencedValue(value, "referenced_names", oracle.referenced_name, (candidate) => candidate.replace(/^_+/u, ""));
93
+ if (oracle.comment !== undefined) {
94
+ const comments = z
95
+ .array(z.object({
96
+ address: addressSchema,
97
+ kind: z.enum(["comment", "inline"]),
98
+ text: z.string(),
99
+ }))
100
+ .parse(collectionSchema.parse(value.comments).items);
101
+ if (!comments.some(({ address, kind, text }) => address === oracle.procedure_address &&
102
+ kind === "comment" &&
103
+ text === oracle.comment))
104
+ throw new TypeError("analyze_function omitted the verifier comment");
105
+ }
106
+ if (oracle.require_assembly === true &&
107
+ collectionSchema.parse(value.assembly).items.length === 0)
108
+ throw new TypeError("analyze_function omitted fixture assembly");
109
+ if (oracle.require_cfg_successor) {
110
+ const blocks = z
111
+ .array(z.object({ successors: z.array(addressSchema) }))
112
+ .parse(collectionSchema.parse(value.basic_blocks).items);
113
+ if (!blocks.some(({ successors }) => successors.length > 0))
114
+ throw new TypeError("analyze_function omitted a real CFG successor");
115
+ }
116
+ return value;
117
+ };
118
+ const referencedValueSchema = z.object({
119
+ address: addressSchema,
120
+ value: z.string(),
121
+ source_address: addressSchema,
122
+ });
123
+ const referenceEdgeSchema = z.object({
124
+ source_address: addressSchema,
125
+ target_address: addressSchema,
126
+ });
127
+ const requireReferencedValue = (dossier, field, expected, normalize) => {
128
+ const values = z
129
+ .array(referencedValueSchema)
130
+ .parse(collectionSchema.parse(dossier[field]).items);
131
+ const references = z
132
+ .array(referenceEdgeSchema)
133
+ .parse(collectionSchema.parse(dossier.outgoing_references).items);
134
+ const found = values.some(({ address, value, source_address: sourceAddress }) => normalize(value) === expected &&
135
+ references.some(({ source_address, target_address }) => source_address === sourceAddress && target_address === address));
136
+ if (!found)
137
+ throw new TypeError(`analyze_function omitted the expected fixture ${field === "referenced_strings" ? "string" : "name"}`);
138
+ };
64
139
  /** Require every relationship to be an actual hexadecimal address. */
65
140
  export const requireAddressArray = (input, operation) => {
66
141
  const parsed = z.array(addressSchema).parse(input);
@@ -69,6 +144,7 @@ export const requireAddressArray = (input, operation) => {
69
144
  return parsed;
70
145
  };
71
146
  const COLLECTION_FIELDS = [
147
+ "assembly",
72
148
  "comments",
73
149
  "callers",
74
150
  "callees",
@@ -0,0 +1,69 @@
1
+ import { z } from "zod";
2
+ import { AnalysisCancelledError, AnalysisInputError, } from "../domain/errors.js";
3
+ import { jsonValueSchema } from "../domain/jsonValue.js";
4
+ import { evaluateReconstructionClosure, reconstructionCoverageWorkspaceSchema, } from "../domain/reconstructionCoverage.js";
5
+ import { err, ok } from "../domain/result.js";
6
+ import { readReconstructionCoverageWorkspace, writeReconstructionCoverageWorkspace, } from "./ReconstructionCoverageWorkspaceStore.js";
7
+ const COMMIT_OPERATION = "commit_reconstruction_coverage";
8
+ const QUERY_OPERATION = "query_reconstruction_coverage";
9
+ const isAborted = (signal) => signal?.aborted === true;
10
+ export const reconstructionCoverageCommitInputSchema = z.strictObject({
11
+ approved: z.literal(true),
12
+ workspace_path: z.string().min(1).max(4_096),
13
+ expected_revision: z.number().int().min(1).nullable(),
14
+ workspace: reconstructionCoverageWorkspaceSchema,
15
+ });
16
+ export const reconstructionCoverageQueryInputSchema = z.strictObject({
17
+ workspace_path: z.string().min(1).max(4_096),
18
+ boundary_id: z.string().min(1).max(200),
19
+ });
20
+ export const reconstructionCoverageCommitOutputSchema = z.strictObject({
21
+ workspace_path: z.string(),
22
+ bytes: z.number().int().min(0),
23
+ workspace_id: z.string().regex(/^rcw_[a-f0-9]{64}$/u),
24
+ revision: z.number().int().min(1),
25
+ revision_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
26
+ evidence_records: z.number().int().min(0),
27
+ });
28
+ /** Commit one caller-approved canonical reconstruction coverage CAS revision. */
29
+ export const commitReconstructionCoverage = async (rawInput, policy, options = {}) => {
30
+ const parsed = reconstructionCoverageCommitInputSchema.safeParse(rawInput);
31
+ if (!parsed.success)
32
+ return err(new AnalysisInputError(COMMIT_OPERATION, { cause: parsed.error }));
33
+ if (isAborted(options.signal))
34
+ return err(new AnalysisCancelledError(COMMIT_OPERATION));
35
+ const written = await writeReconstructionCoverageWorkspace(parsed.data.workspace, parsed.data.workspace_path, parsed.data.expected_revision, policy);
36
+ if (!written.ok)
37
+ return written;
38
+ return ok(jsonValueSchema.parse({
39
+ workspace_path: written.value.path,
40
+ bytes: written.value.bytes,
41
+ workspace_id: parsed.data.workspace.workspace_id,
42
+ revision: parsed.data.workspace.revision,
43
+ revision_sha256: parsed.data.workspace.revision_sha256,
44
+ evidence_records: parsed.data.workspace.evidence_bundle.records.length,
45
+ }));
46
+ };
47
+ /** Read one coverage revision and evaluate a named fail-closed boundary. */
48
+ export const queryReconstructionCoverage = async (rawInput, policy, nowEpochMs, options = {}) => {
49
+ const parsed = reconstructionCoverageQueryInputSchema.safeParse(rawInput);
50
+ if (!parsed.success)
51
+ return err(new AnalysisInputError(QUERY_OPERATION, { cause: parsed.error }));
52
+ if (isAborted(options.signal))
53
+ return err(new AnalysisCancelledError(QUERY_OPERATION));
54
+ const loaded = await readReconstructionCoverageWorkspace(parsed.data.workspace_path, policy);
55
+ if (!loaded.ok)
56
+ return loaded;
57
+ if (loaded.value === null)
58
+ return err(new AnalysisInputError(QUERY_OPERATION, {
59
+ cause: new TypeError("Reconstruction coverage workspace does not exist"),
60
+ }));
61
+ if (isAborted(options.signal))
62
+ return err(new AnalysisCancelledError(QUERY_OPERATION));
63
+ try {
64
+ return ok(jsonValueSchema.parse(evaluateReconstructionClosure(loaded.value, parsed.data.boundary_id, nowEpochMs)));
65
+ }
66
+ catch (cause) {
67
+ return err(new AnalysisInputError(QUERY_OPERATION, { cause }));
68
+ }
69
+ };
@@ -0,0 +1,31 @@
1
+ import { InvestigationWorkspaceError } from "../domain/errors.js";
2
+ import { parseReconstructionCoverageWorkspace, serializeReconstructionCoverageWorkspace, } from "../domain/reconstructionCoverage.js";
3
+ import { err, ok } from "../domain/result.js";
4
+ import { readRevisionedWorkspace, writeRevisionedWorkspace, } from "./InvestigationWorkspaceStore.js";
5
+ const codec = {
6
+ parse: parseReconstructionCoverageWorkspace,
7
+ serialize: serializeReconstructionCoverageWorkspace,
8
+ validateNext: (current, next, expectedRevision) => {
9
+ if ((current?.revision ?? null) !== expectedRevision)
10
+ return err(new InvestigationWorkspaceError("update", "revision-conflict"));
11
+ if (current === null)
12
+ return next.revision === 1 && next.previous_revision_sha256 === null
13
+ ? ok(null)
14
+ : err(new InvestigationWorkspaceError("update", "revision-conflict"));
15
+ if (current.name !== next.name ||
16
+ current.workspace_id !== next.workspace_id)
17
+ return err(new InvestigationWorkspaceError("update", "name-conflict"));
18
+ return next.revision === current.revision + 1 &&
19
+ next.previous_revision_sha256 === current.revision_sha256
20
+ ? ok(null)
21
+ : err(new InvestigationWorkspaceError("update", "revision-conflict"));
22
+ },
23
+ };
24
+ /** Read a validated reconstruction coverage workspace revision. */
25
+ export const readReconstructionCoverageWorkspace = (path, policy) => readRevisionedWorkspace(path, policy, codec);
26
+ /** Atomically append one reconstruction coverage workspace CAS revision. */
27
+ export const writeReconstructionCoverageWorkspace = (workspace, path, expectedRevision, policy) => writeRevisionedWorkspace(path, policy, {
28
+ document: workspace,
29
+ expectedRevision,
30
+ codec,
31
+ });
@@ -0,0 +1,172 @@
1
+ import { execFile } from "node:child_process";
2
+ import { constants } from "node:fs";
3
+ import { access, realpath } from "node:fs/promises";
4
+ import { delimiter, join } from "node:path";
5
+ const TOOL_NAMES = ["node", "npm", "npx"];
6
+ const MAX_CANDIDATES_PER_TOOL = 16;
7
+ const MAX_OUTPUT_BYTES = 65_536;
8
+ const MAX_DIAGNOSTIC_BYTES = 4_096;
9
+ const PROBE_CONCURRENCY = 4;
10
+ /** Inventory and bounded-probe Node toolchain candidates under one exact PATH. */
11
+ export const inspectRuntimeExecutables = async (options) => {
12
+ const pathEntries = unique(options.path.split(delimiter).filter(Boolean));
13
+ const candidates = [
14
+ {
15
+ tool: "node",
16
+ path: options.launcherNode,
17
+ pathIndex: null,
18
+ selection: "rea-launcher",
19
+ },
20
+ ];
21
+ for (const tool of TOOL_NAMES) {
22
+ const discovered = await discoverToolCandidates(tool, pathEntries, options.platform, options.pathExtensions);
23
+ for (const [index, candidate] of discovered.entries())
24
+ candidates.push({
25
+ tool,
26
+ path: candidate.path,
27
+ pathIndex: candidate.pathIndex,
28
+ selection: index === 0 ? "path-primary" : "path-shadowed",
29
+ });
30
+ }
31
+ const seen = new Set();
32
+ const uniqueCandidates = [];
33
+ for (const candidate of candidates) {
34
+ const key = `${candidate.tool}\0${candidate.path}`;
35
+ if (seen.has(key))
36
+ continue;
37
+ seen.add(key);
38
+ uniqueCandidates.push(candidate);
39
+ }
40
+ const diagnostics = await mapConcurrentBounded(uniqueCandidates, PROBE_CONCURRENCY, (candidate) => probeCandidate(candidate, options.timeoutMs ?? 5_000, options.path));
41
+ return {
42
+ launcher_node: await canonicalPath(options.launcherNode),
43
+ candidates: diagnostics,
44
+ };
45
+ };
46
+ const discoverToolCandidates = async (tool, pathEntries, platform, configuredExtensions) => {
47
+ const extensions = platform === "win32"
48
+ ? (configuredExtensions ?? [".COM", ".EXE", ".BAT", ".CMD"])
49
+ : [""];
50
+ const candidates = [];
51
+ for (const [pathIndex, directory] of pathEntries.entries()) {
52
+ for (const extension of extensions) {
53
+ const path = join(directory, `${tool}${extension}`);
54
+ if (await isExecutable(path, platform)) {
55
+ candidates.push({ path, pathIndex });
56
+ break;
57
+ }
58
+ }
59
+ if (candidates.length >= MAX_CANDIDATES_PER_TOOL)
60
+ break;
61
+ }
62
+ return candidates;
63
+ };
64
+ const probeCandidate = async (candidate, timeoutMs, effectivePath) => {
65
+ const canonical = await canonicalPath(candidate.path);
66
+ const result = await executeVersion(candidate.path, timeoutMs, effectivePath);
67
+ return {
68
+ tool: candidate.tool,
69
+ lexical_path: candidate.path,
70
+ canonical_path: canonical,
71
+ path_index: candidate.pathIndex,
72
+ selection: candidate.selection,
73
+ version: result.ok ? result.version : null,
74
+ healthy: result.ok,
75
+ failure: result.ok ? null : result.failure,
76
+ };
77
+ };
78
+ const executeVersion = (path, timeoutMs, effectivePath) => new Promise((resolve) => {
79
+ const child = execFile(path, ["--version"], {
80
+ encoding: "utf8",
81
+ timeout: timeoutMs,
82
+ maxBuffer: MAX_OUTPUT_BYTES,
83
+ windowsHide: true,
84
+ env: environmentWithPath(effectivePath),
85
+ }, (cause, stdout, stderr) => {
86
+ if (cause === null) {
87
+ const version = firstNonemptyLine(stdout) ?? firstNonemptyLine(stderr);
88
+ if (version !== undefined)
89
+ resolve({ ok: true, version });
90
+ else
91
+ resolve({
92
+ ok: false,
93
+ failure: failure("runtime_invalid_version_output", child, stderr),
94
+ });
95
+ return;
96
+ }
97
+ resolve({
98
+ ok: false,
99
+ failure: classifyProbeFailure(cause, child, stderr),
100
+ });
101
+ });
102
+ });
103
+ const classifyProbeFailure = (cause, child, stderr) => {
104
+ const dependency = missingDynamicLibrary(stderr);
105
+ if (dependency !== null)
106
+ return failure("runtime_dynamic_library_missing", child, stderr, dependency);
107
+ if ("killed" in cause && cause.killed === true)
108
+ return failure("runtime_timeout", child, stderr);
109
+ if (child.signalCode !== null)
110
+ return failure("runtime_signal", child, stderr);
111
+ if ("code" in cause && typeof cause.code === "string")
112
+ return failure("runtime_spawn_failed", child, stderr);
113
+ return failure("runtime_nonzero_exit", child, stderr);
114
+ };
115
+ const failure = (code, child, stderr, dependency = null) => ({
116
+ code,
117
+ exit_code: child.exitCode,
118
+ signal: child.signalCode,
119
+ dependency,
120
+ stderr: bounded(stderr.trim()),
121
+ });
122
+ const missingDynamicLibrary = (stderr) => /(?:dyld(?:\[[^\]]+\])?: )?Library not loaded:\s*([^\r\n]+)/u
123
+ .exec(stderr)?.[1]
124
+ ?.trim() ?? null;
125
+ const isExecutable = async (path, platform) => {
126
+ try {
127
+ await access(path, platform === "win32" ? constants.F_OK : constants.X_OK);
128
+ return true;
129
+ }
130
+ catch {
131
+ return false;
132
+ }
133
+ };
134
+ const canonicalPath = async (path) => {
135
+ try {
136
+ return await realpath(path);
137
+ }
138
+ catch {
139
+ return path;
140
+ }
141
+ };
142
+ const firstNonemptyLine = (value) => value
143
+ .split(/\r?\n/u)
144
+ .find((line) => line.trim().length > 0)
145
+ ?.trim();
146
+ const bounded = (value) => value.length <= MAX_DIAGNOSTIC_BYTES
147
+ ? value
148
+ : value.slice(-MAX_DIAGNOSTIC_BYTES);
149
+ const unique = (values) => [...new Set(values)];
150
+ const environmentWithPath = (path) => {
151
+ const environment = {};
152
+ for (const [name, value] of Object.entries(process.env))
153
+ if (name.toUpperCase() !== "PATH")
154
+ environment[name] = value;
155
+ environment.PATH = path;
156
+ return environment;
157
+ };
158
+ const mapConcurrentBounded = async (inputs, concurrency, operation) => {
159
+ const outputs = [];
160
+ let nextIndex = 0;
161
+ const workers = Array.from({ length: Math.min(concurrency, inputs.length) }, async () => {
162
+ while (nextIndex < inputs.length) {
163
+ const index = nextIndex;
164
+ nextIndex += 1;
165
+ const input = inputs[index];
166
+ if (input !== undefined)
167
+ outputs[index] = await operation(input);
168
+ }
169
+ });
170
+ await Promise.all(workers);
171
+ return outputs;
172
+ };