rea-agents 5.0.0 → 6.0.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 (275) hide show
  1. package/README.md +157 -650
  2. package/bridge/mitmproxy/capture.py +210 -0
  3. package/bridge/native/rea_lldb_tracer.py +303 -0
  4. package/bridge/process/ProcessRunTokenReader.swift +263 -0
  5. package/bridge/process/ProcessRunTokenReaderMain.swift +35 -0
  6. package/bridge/pwndbg/core_context.py +101 -0
  7. package/bridge/pwndbg/launch.py +39 -0
  8. package/bridge/pwndbg/launch_impl.py +34 -0
  9. package/bridge/pwntools/decoder_runtime.py +59 -0
  10. package/bridge/pwntools/layout.py +39 -0
  11. package/bridge/pwntools/layout_impl.py +309 -0
  12. package/bridge/pwntools/recorded_crash.py +39 -0
  13. package/bridge/pwntools/recorded_crash_impl.py +143 -0
  14. package/dist/android/JadxProvider.js +1 -0
  15. package/dist/application/CapabilityInventory.js +21 -0
  16. package/dist/application/DirectAnalysis.js +97 -23
  17. package/dist/application/EvidenceBundleFiles.js +1 -1
  18. package/dist/application/ReconstructionObligationCandidates.js +5 -1
  19. package/dist/application/WebNetworkCapturePort.js +1 -0
  20. package/dist/application/WebNetworkCaptureService.js +132 -0
  21. package/dist/application/WindowsCapabilities.js +1 -1
  22. package/dist/application/binary/AnalysisSnapshotCache.js +49 -6
  23. package/dist/application/binary/BinarySession.js +38 -10
  24. package/dist/application/binary/BinarySessionOpen.js +16 -3
  25. package/dist/application/binary/BinarySessionRecords.js +20 -0
  26. package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
  27. package/dist/application/binaryDiagnostics/BinaryLayoutService.js +57 -0
  28. package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
  29. package/dist/application/binaryDiagnostics/RecordedCrashService.js +60 -0
  30. package/dist/application/evm/EvmInterfacePort.js +1 -0
  31. package/dist/application/evm/EvmInterfaceService.js +62 -0
  32. package/dist/application/javascript/JavaScriptApplicationEvidence.js +1 -2
  33. package/dist/application/javascript/JavaScriptApplicationService.js +15 -2
  34. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +42 -23
  35. package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +26 -2
  36. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +74 -24
  37. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +23 -10
  38. package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +7 -7
  39. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +145 -70
  40. package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +14 -15
  41. package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +41 -1
  42. package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +7 -7
  43. package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -9
  44. package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +3 -3
  45. package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +118 -12
  46. package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +3 -3
  47. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +5 -5
  48. package/dist/application/{ProcessCli.js → process/ProcessCli.js} +10 -10
  49. package/dist/application/{ProcessEvidence.js → process/ProcessEvidence.js} +4 -4
  50. package/dist/artifacts/ArtifactProvider.js +30 -3
  51. package/dist/artifacts/ArtifactProviderMetadata.js +2 -1
  52. package/dist/artifacts/ZipArtifactReader.js +3 -0
  53. package/dist/artifacts/apple/DylibResolutionReader.js +259 -0
  54. package/dist/artifacts/apple/MachoLoadCommandReader.js +294 -0
  55. package/dist/{application → artifacts/extraction}/ArtifactExtraction.js +10 -10
  56. package/dist/{application → artifacts/inventory}/ArtifactGraphConstruction.js +3 -3
  57. package/dist/{application → artifacts/inventory}/ArtifactInventory.js +4 -4
  58. package/dist/{application/ArtifactInventory → artifacts/inventory}/classify.js +2 -2
  59. package/dist/{application/ArtifactInventory → artifacts/inventory}/reader.js +5 -5
  60. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanCanonical.js +3 -3
  61. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanReader.js +5 -5
  62. package/dist/artifacts/readStableArtifact.js +20 -4
  63. package/dist/browser/CdpCaptureEventHandlers.js +90 -2
  64. package/dist/browser/CdpCaptureEvents.js +9 -0
  65. package/dist/browser/PlaywrightElectronActiveProvider.js +12 -3
  66. package/dist/browser/history/CaptureFailures.js +101 -0
  67. package/dist/browser/history/CaptureFormatError.js +10 -0
  68. package/dist/browser/history/CaptureRedaction.js +65 -0
  69. package/dist/browser/history/CaptureRelease.js +12 -0
  70. package/dist/browser/history/HarCapture.js +165 -0
  71. package/dist/browser/history/HarCaptureAdapter.js +20 -0
  72. package/dist/browser/history/HarCaptureProcess.js +57 -0
  73. package/dist/browser/history/HarJson.js +91 -0
  74. package/dist/browser/history/HarSchema.js +72 -0
  75. package/dist/browser/history/HistoricalCaptureDecoder.js +186 -0
  76. package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
  77. package/dist/browser/history/MitmproxyCaptureAdapter.js +42 -0
  78. package/dist/cli/artifactCommands.js +26 -1
  79. package/dist/cli/binaryDiagnosticsCommands.js +39 -0
  80. package/dist/cli/coreAnalysisCommands.js +2 -2
  81. package/dist/cli/coreBinaryCommands.js +8 -8
  82. package/dist/cli/electronCommands.js +16 -9
  83. package/dist/cli/evmCommands.js +24 -0
  84. package/dist/cli/firmwareCommands.js +6 -3
  85. package/dist/cli/javascriptApplicationAnalysis.js +16 -3
  86. package/dist/cli/jsonOutput.js +133 -0
  87. package/dist/cli/nativeCallCommands.js +35 -0
  88. package/dist/cli/processCommands.js +1 -1
  89. package/dist/cli/streamedJsonOutput.js +56 -0
  90. package/dist/cli/utilityCommands.js +2 -0
  91. package/dist/cli/webNetworkCaptureCommands.js +40 -0
  92. package/dist/cli.js +9 -3
  93. package/dist/cliBrowserScenarioCommands.js +2 -2
  94. package/dist/cliCommandNames.js +6 -0
  95. package/dist/cliJsonInput.js +30 -1
  96. package/dist/cliOutput.js +21 -6
  97. package/dist/composition/binaryDiagnostics.js +8 -0
  98. package/dist/composition/evm.js +4 -0
  99. package/dist/composition/webNetworkCaptures.js +6 -0
  100. package/dist/contracts/artifactToolContracts.js +6 -0
  101. package/dist/contracts/browserCaptureToolInputSchema.js +43 -0
  102. package/dist/contracts/browserToolContracts.js +4 -3
  103. package/dist/contracts/errorSchemas.js +8 -1
  104. package/dist/contracts/evm/evmToolContracts.js +20 -0
  105. package/dist/contracts/investigationExamples.js +2 -2
  106. package/dist/contracts/managed/managedToolContracts.js +7 -3
  107. package/dist/contracts/native/binaryDiagnosticsToolContracts.js +42 -0
  108. package/dist/contracts/native/nativeToolContracts.js +17 -1
  109. package/dist/contracts/officialToolContracts.js +12 -9
  110. package/dist/contracts/process/processCaptureExample.js +2 -0
  111. package/dist/contracts/sessionLifecycleInputs.js +22 -5
  112. package/dist/contracts/sessionStatusContract.js +10 -2
  113. package/dist/contracts/sessionToolContracts.js +8 -1
  114. package/dist/contracts/sessionToolSchemas.js +10 -3
  115. package/dist/contracts/toolContractExamples.js +1 -1
  116. package/dist/contracts/toolContracts.js +6 -0
  117. package/dist/contracts/toolEffects.js +30 -0
  118. package/dist/contracts/toolOutputSchemaGroups.js +5 -1
  119. package/dist/contracts/toolOutputSchemaPrimitives.js +1 -1
  120. package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
  121. package/dist/domain/analysisErrorBase.js +15 -0
  122. package/dist/domain/analysisErrorCore.js +59 -5
  123. package/dist/domain/analysisErrorPresentation.js +29 -1
  124. package/dist/domain/analysisErrorProjection.js +52 -2
  125. package/dist/domain/analysisSnapshot.js +166 -2
  126. package/dist/domain/apple/dyldPaths.js +81 -0
  127. package/dist/domain/apple/dylibResolution.js +367 -0
  128. package/dist/domain/apple/dylibResolutionFindings.js +75 -0
  129. package/dist/domain/artifactPathSyntax.js +1 -1
  130. package/dist/domain/browserObservation.js +1 -1
  131. package/dist/domain/browserObservationSchemas.js +13 -0
  132. package/dist/domain/browserScenarioValues.js +9 -2
  133. package/dist/domain/callPathSchemas.js +1 -1
  134. package/dist/domain/canonicalDigest.js +13 -1
  135. package/dist/domain/changedBehavior.js +3 -3
  136. package/dist/domain/comparisonStatus.js +2 -30
  137. package/dist/domain/conformanceTrustGate.js +0 -16
  138. package/dist/domain/evidence.js +17 -5
  139. package/dist/domain/evm/evmInterface.js +63 -0
  140. package/dist/domain/explicitSensitiveFailure.js +92 -0
  141. package/dist/domain/explicitSensitiveValues.js +10 -0
  142. package/dist/domain/firmware/firmwareAnalysis.js +8 -1
  143. package/dist/domain/javascript/electronActiveObservation.js +9 -4
  144. package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
  145. package/dist/domain/javascript/javascriptExportShapeComparison.js +21 -5
  146. package/dist/domain/javascript/javascriptExportShapeVariants.js +0 -10
  147. package/dist/domain/javascript/javascriptSemanticGraph.js +21 -5
  148. package/dist/domain/jsonValue.js +4 -0
  149. package/dist/domain/localPath.js +2 -0
  150. package/dist/domain/native/binaryLayout.js +357 -0
  151. package/dist/domain/native/nativeCallObservation.js +164 -0
  152. package/dist/domain/native/objcSwiftMetadata.js +0 -39
  153. package/dist/domain/native/recordedCrash.js +266 -0
  154. package/dist/domain/native/recordedCrashNoteCoverage.js +73 -0
  155. package/dist/domain/{processCapture.js → process/processCapture.js} +139 -2
  156. package/dist/domain/{processCaptureExample.js → process/processCaptureExample.js} +5 -0
  157. package/dist/domain/process/processCaptureParsing.js +54 -0
  158. package/dist/domain/{processCaptureValidation.js → process/processCaptureValidation.js} +18 -2
  159. package/dist/domain/{processComparison.js → process/processComparison.js} +1 -1
  160. package/dist/domain/{processObservation.js → process/processObservation.js} +1 -1
  161. package/dist/domain/{processTraceSpecification.js → process/processTraceSpecification.js} +1 -1
  162. package/dist/domain/providerSelectionError.js +4 -2
  163. package/dist/domain/reconstructionVerification.js +3 -3
  164. package/dist/domain/reconstructionVerificationSchemas.js +1 -1
  165. package/dist/domain/residualUnknown.js +3 -2
  166. package/dist/domain/staticRuntimeCorrelation.js +3 -3
  167. package/dist/domain/webCaptureDiff.js +1 -0
  168. package/dist/domain/webNetworkCapture.js +118 -0
  169. package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
  170. package/dist/dotnet/ManagedExceptionRegionValidation.js +41 -0
  171. package/dist/dotnet/ManagedMemberInspector.js +4 -2
  172. package/dist/dotnet/ManagedMemberInspectorCore.js +38 -32
  173. package/dist/dotnet/ManagedMemberInstructionDecoder.js +23 -7
  174. package/dist/dotnet/ManagedMemberRows.js +14 -3
  175. package/dist/dotnet/ManagedMetadataHeaps.js +20 -2
  176. package/dist/dotnet/ManagedMetadataInventory.js +38 -28
  177. package/dist/dotnet/ManagedMetadataInventoryRows.js +24 -10
  178. package/dist/dotnet/ManagedMethodBodyReader.js +7 -1
  179. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +15 -3
  180. package/dist/dotnet/ManagedNativeBoundaryInspector.js +3 -1
  181. package/dist/dotnet/ManagedPeReader.js +11 -0
  182. package/dist/evm/EvmBytecodeCarrier.js +21 -0
  183. package/dist/evm/EvmWorkerLimits.js +40 -0
  184. package/dist/evm/EvmoleFailures.js +126 -0
  185. package/dist/evm/EvmoleInterfaceAdapter.js +53 -0
  186. package/dist/evm/EvmoleInterfaceProvider.js +273 -0
  187. package/dist/evm/EvmoleInterfaceWorker.js +87 -0
  188. package/dist/evm/EvmoleRelease.js +16 -0
  189. package/dist/firmware/FirmwareCommand.js +1 -0
  190. package/dist/generatedPackageMetadata.js +2 -2
  191. package/dist/ghidra/GhidraLauncher.js +1 -0
  192. package/dist/ghidra/GhidraProvider.js +2 -2
  193. package/dist/ghidra/GhidraProviderCapabilities.js +1 -1
  194. package/dist/hopper/BridgeLauncher.js +2 -0
  195. package/dist/hopper/LinuxPrivateDisplayProbe.js +4 -2
  196. package/dist/javascript/recovery/WakaruCommand.js +3 -0
  197. package/dist/javascript/sourceMaps/SourceMapCommand.js +1 -0
  198. package/dist/native/CommandRunner.js +2 -1
  199. package/dist/native/LldbCallTracer.js +278 -0
  200. package/dist/native/NativeCallObservation.js +135 -0
  201. package/dist/native/NativeMacOSProvider.js +19 -3
  202. package/dist/native/NativeMacOSProviderMetadata.js +8 -3
  203. package/dist/native/pwndbg/PwndbgCoreContext.js +160 -0
  204. package/dist/native/pwntools/PwntoolsDecoder.js +203 -0
  205. package/dist/native/pwntools/PwntoolsFailures.js +180 -0
  206. package/dist/native/pwntools/PwntoolsLayoutProvider.js +36 -0
  207. package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +75 -0
  208. package/dist/native/pwntools/PwntoolsRelease.js +19 -0
  209. package/dist/native/pwntools/PwntoolsResourceLimits.js +76 -0
  210. package/dist/native/pwntools/RecordedCrashSourceBindings.js +46 -0
  211. package/dist/native/pwntools/RecordedCrashStructureBindings.js +101 -0
  212. package/dist/process/DarwinProcessRunTokenReader.js +294 -0
  213. package/dist/process/OwnedCommand.js +113 -0
  214. package/dist/process/ProcessOwnership.js +263 -33
  215. package/dist/process/ProcessOwnershipObservation.js +161 -24
  216. package/dist/process/ProviderProcess.js +30 -3
  217. package/dist/{application → process/capture}/ProcessCaptureCapability.js +15 -0
  218. package/dist/{application → process/capture}/ProcessCaptureError.js +17 -1
  219. package/dist/process/capture/ProcessCaptureLifecycle.js +615 -0
  220. package/dist/{application → process/capture}/ProcessHarness.js +195 -29
  221. package/dist/{application → process/capture}/ProcessSampling.js +2 -2
  222. package/dist/server/createServer.js +22 -0
  223. package/dist/server/promptCompletion.js +10 -3
  224. package/dist/server/registerBinaryDiagnosticsTools.js +17 -0
  225. package/dist/server/registerEvidenceTools.js +1 -1
  226. package/dist/server/registerEvmTools.js +17 -0
  227. package/dist/server/registerProcessComparisonTool.js +1 -1
  228. package/dist/server/registerRecordedCrashTools.js +17 -0
  229. package/dist/server/registerSessionTools.js +2 -2
  230. package/dist/server/registerWebNetworkCaptureTool.js +28 -0
  231. package/dist/server/sessionAvailabilityPolicy.js +3 -0
  232. package/dist/server/sessionToolPolicies.js +1 -1
  233. package/dist/server/toolResult.js +1 -1
  234. package/native/windows/build/manifest.json +2 -2
  235. package/native/windows/build/rea-windows-x64.node +0 -0
  236. package/package.json +37 -9
  237. package/scripts/rea.mjs +11 -3
  238. package/skills/reverse-engineer-anything/SKILL.md +23 -3
  239. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +29 -0
  240. package/third_party/README.md +60 -0
  241. package/third_party/evmole/LICENSE +21 -0
  242. package/third_party/evmole/README.md +22 -0
  243. package/third_party/pwndbg/LICENSE.md +21 -0
  244. package/third_party/pwndbg/README.md +23 -0
  245. package/third_party/pwntools/README.md +40 -0
  246. package/dist/application/AuthorizedArtifactInventory.js +0 -7
  247. package/dist/application/ProcessCaptureLifecycle.js +0 -299
  248. package/dist/application/StoreFileLock.js +0 -124
  249. package/dist/contracts/processCaptureExample.js +0 -2
  250. package/dist/domain/bytecodeProvider.js +0 -178
  251. package/dist/domain/customProtocolCapture.js +0 -137
  252. package/dist/domain/eventProcessTree.js +0 -225
  253. package/dist/domain/mobileApplicationInvestigation.js +0 -159
  254. package/dist/domain/packageAnalysis.js +0 -123
  255. package/dist/domain/peInspection.js +0 -197
  256. package/dist/domain/processCaptureParsing.js +0 -21
  257. package/dist/domain/protocolCapture.js +0 -217
  258. package/dist/domain/runtimeIdentification.js +0 -212
  259. package/dist/generatedMcpToolCatalog.js +0 -37
  260. /package/dist/application/{ArtifactExtractionDestination.js → artifacts/ArtifactExtractionDestination.js} +0 -0
  261. /package/dist/{application/ArtifactInventory → artifacts/inventory}/policy.js +0 -0
  262. /package/dist/{application/ArtifactInventory → artifacts/inventory}/types.js +0 -0
  263. /package/dist/domain/{processEvidenceProvider.js → process/processEvidenceProvider.js} +0 -0
  264. /package/dist/domain/{processScenario.js → process/processScenario.js} +0 -0
  265. /package/dist/domain/{processTraceComparison.js → process/processTraceComparison.js} +0 -0
  266. /package/dist/domain/{processTraceDimensionProjection.js → process/processTraceDimensionProjection.js} +0 -0
  267. /package/dist/domain/{processTraceEvaluation.js → process/processTraceEvaluation.js} +0 -0
  268. /package/dist/{application → process/capture}/FilesystemSnapshot.js +0 -0
  269. /package/dist/{application → process/capture}/ProcessCaptureEnvironment.js +0 -0
  270. /package/dist/{application → process/capture}/ProcessCaptureJournal.js +0 -0
  271. /package/dist/{application → process/capture}/ProcessFilesystemEffects.js +0 -0
  272. /package/dist/{application → process/capture}/ProcessNormalization.js +0 -0
  273. /package/dist/{application → process/capture}/ProcessScenarioRuntimeValidation.js +0 -0
  274. /package/dist/{application → process/capture}/ProcessTimer.js +0 -0
  275. /package/dist/{application → process/capture}/TerminalRenderer.js +0 -0
@@ -0,0 +1,615 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { constants, statSync } from "node:fs";
3
+ import { open, mkdtemp, rm, stat } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { digestProcessCommitment, parseProcessCapture, partialProcessCaptureObservationSchema, processComparisonContract, processScenarioCommitment, } from "../../domain/process/processCapture.js";
7
+ import { PRODUCT_IDENTITY } from "../../identity.js";
8
+ import { snapshotRoots } from "./FilesystemSnapshot.js";
9
+ import { classifyFilesystemEffects } from "./ProcessFilesystemEffects.js";
10
+ import { cleanupOwnedProcessGroup, verifyNoTokenOwnedProcesses, } from "../ProcessOwnership.js";
11
+ import { observeOwnedProcessGroup, prepareProcessOwnershipInspection, systemProcessOwnershipHost, } from "../ProcessOwnershipObservation.js";
12
+ import { describeProcessCaptureExecutionFailure, ProcessCaptureError, } from "./ProcessCaptureError.js";
13
+ import { assertNotCancelled } from "./ProcessScenarioRuntimeValidation.js";
14
+ import { normalizeProcessElapsedTime, normalizeProcessSamples, normalizeProcessText, } from "./ProcessNormalization.js";
15
+ import { PROCESS_PROVIDER } from "../../domain/process/processEvidenceProvider.js";
16
+ import { TerminalRenderer } from "./TerminalRenderer.js";
17
+ import { scheduleProcessInterval } from "./ProcessTimer.js";
18
+ /** Seed explicit observation availability before capture completion begins. */
19
+ export const createProcessCaptureObservationBuffer = (options) => ({
20
+ target_pid: {
21
+ state: "unavailable",
22
+ reason: "No PTY process was spawned before the run failed.",
23
+ },
24
+ frames: { state: "available", value: options.frames },
25
+ rendered_frames: {
26
+ state: "unavailable",
27
+ reason: "Rendered terminal frames were not collected before capture completion finished.",
28
+ },
29
+ interaction_events: { state: "available", value: options.interactions },
30
+ exit: {
31
+ state: "unavailable",
32
+ reason: "Terminal exit was not observed before the run failed.",
33
+ },
34
+ settlement: {
35
+ state: "unavailable",
36
+ reason: "Process settlement was not observed before capture completion finished.",
37
+ },
38
+ process_samples: { state: "available", value: options.samples },
39
+ filesystem_snapshots: {
40
+ before: {
41
+ state: "available",
42
+ value: {
43
+ files: options.before.files,
44
+ truncated: options.before.truncated,
45
+ },
46
+ },
47
+ after: {
48
+ state: "unavailable",
49
+ reason: "Final filesystem snapshot was not collected before capture completion finished.",
50
+ },
51
+ },
52
+ event_journal: { state: "available", value: options.eventJournal },
53
+ manifest: {
54
+ state: "unavailable",
55
+ reason: "Capture manifest was not produced before capture completion finished.",
56
+ },
57
+ });
58
+ const normalizePartialProcessObservations = (observations, context) => {
59
+ const targetPid = observations.target_pid;
60
+ const target_pid = targetPid.state === "available" && context.rootPid !== undefined
61
+ ? {
62
+ state: "available",
63
+ value: context.scenario.normalization.pids ? 1 : context.rootPid,
64
+ }
65
+ : targetPid;
66
+ const samples = observations.process_samples;
67
+ const process_samples = samples.state === "available" && context.rootPid !== undefined
68
+ ? {
69
+ state: "available",
70
+ value: normalizeProcessSamples(samples.value, context.scenario, context.rootPid),
71
+ }
72
+ : samples;
73
+ return { ...observations, target_pid, process_samples };
74
+ };
75
+ /** Wait for trailing PTY callbacks before the capture becomes immutable. */
76
+ export const settleProcessCaptureJournal = async (journal, quietMs = 25, maxWaitMs = 500) => {
77
+ const deadline = Date.now() + maxWaitMs;
78
+ let observed = journal.length;
79
+ let lastChangeAt = Date.now();
80
+ while (Date.now() < deadline) {
81
+ await new Promise((resolve) => setTimeout(resolve, 4));
82
+ if (journal.length !== observed) {
83
+ observed = journal.length;
84
+ lastChangeAt = Date.now();
85
+ }
86
+ else if (Date.now() - lastChangeAt >= quietMs)
87
+ return;
88
+ }
89
+ };
90
+ export const buildCaptureResult = (options) => {
91
+ const hasSensitiveScriptedInput = options.scenario.events.some((event) => event.type === "input" && event.sensitive);
92
+ const sensitiveInputUnknown = "Sensitive scripted input values are redacted from process capture Evidence.";
93
+ const filesystemObservationUnknown = "No filesystem observation paths were selected; filesystem effects remain unknown.";
94
+ const hasFilesystemObservations = options.scenario.filesystem_observation_paths.length > 0;
95
+ return {
96
+ manifest: options.manifest,
97
+ normalization: options.scenario.normalization,
98
+ frames: options.frames,
99
+ rendered_frames: options.renderedFrames,
100
+ interaction_events: options.interactions,
101
+ exit: {
102
+ code: options.exit.reason === "exited" && options.exit.exitCode >= 0
103
+ ? options.exit.exitCode
104
+ : null,
105
+ signal: options.exit.signal ?? null,
106
+ reason: options.exit.reason,
107
+ },
108
+ settlement: options.settlement,
109
+ process_samples: normalizeProcessSamples(options.samples, options.scenario, options.rootPid),
110
+ filesystem_checkpoints: options.checkpoints,
111
+ event_journal: options.eventJournal,
112
+ files_before: options.before.files,
113
+ files_after: options.after.files,
114
+ filesystem_effects: classifyFilesystemEffects(options.before.files, options.after.files),
115
+ truncated: options.truncated,
116
+ limitations: [
117
+ "The executable digest is a prelaunch file sample; matching path metadata immediately after spawn does not prove an atomic operating-system image binding.",
118
+ "Process trees are sampled and may omit short-lived descendants.",
119
+ ...(options.samplingPartial
120
+ ? ["Process-tree sampling ended with an incomplete observation."]
121
+ : []),
122
+ "Filesystem observations are before/after snapshots, not syscall traces.",
123
+ "Inherited host environment variables are not recorded and may affect results.",
124
+ ...(!hasFilesystemObservations ? [filesystemObservationUnknown] : []),
125
+ ...(hasSensitiveScriptedInput ? [sensitiveInputUnknown] : []),
126
+ ],
127
+ residual_unknowns: [
128
+ {
129
+ scope: "process",
130
+ reason: "Process trees are sampled and may omit short-lived descendants.",
131
+ },
132
+ {
133
+ scope: "process",
134
+ reason: "The executable digest is a prelaunch file sample; operating-system image binding is not atomically observed.",
135
+ },
136
+ {
137
+ scope: "environment",
138
+ reason: "Inherited host environment variables are not recorded.",
139
+ },
140
+ {
141
+ scope: "network",
142
+ reason: "Network activity is not observed by this capture.",
143
+ },
144
+ ...(!hasFilesystemObservations
145
+ ? [
146
+ {
147
+ scope: "filesystem",
148
+ reason: filesystemObservationUnknown,
149
+ },
150
+ ]
151
+ : []),
152
+ ...(hasSensitiveScriptedInput
153
+ ? [{ scope: "interaction", reason: sensitiveInputUnknown }]
154
+ : []),
155
+ ],
156
+ };
157
+ };
158
+ const identityFromStats = (stats) => ({
159
+ dev: stats.dev,
160
+ ino: stats.ino,
161
+ mode: stats.mode,
162
+ size: stats.size,
163
+ mtimeNs: stats.mtimeNs,
164
+ ctimeNs: stats.ctimeNs,
165
+ });
166
+ const sameExecutableIdentity = (left, right) => left.dev === right.dev &&
167
+ left.ino === right.ino &&
168
+ left.mode === right.mode &&
169
+ left.size === right.size &&
170
+ left.mtimeNs === right.mtimeNs &&
171
+ left.ctimeNs === right.ctimeNs;
172
+ const filesystemFailure = (cause) => {
173
+ if (!(cause instanceof Error) || !("code" in cause) || !("syscall" in cause))
174
+ return undefined;
175
+ return `Executable file observation failed: ${cause.message}`;
176
+ };
177
+ const selectedExecutableFileSystem = {
178
+ open,
179
+ stat: (path) => stat(path, { bigint: true }),
180
+ };
181
+ /** Sample the selected file without launching it; retain cancellation and file identity. */
182
+ export const observeSelectedExecutable = async (path, signal, fileSystem = selectedExecutableFileSystem) => {
183
+ assertNotCancelled(signal);
184
+ let handle;
185
+ try {
186
+ handle = await fileSystem.open(path, constants.O_RDONLY | constants.O_NONBLOCK);
187
+ const openedStats = await handle.stat({ bigint: true });
188
+ if (!openedStats.isFile())
189
+ return {
190
+ sha256: null,
191
+ identity: null,
192
+ reason: "Selected executable is not a regular file.",
193
+ };
194
+ const openedIdentity = identityFromStats(openedStats);
195
+ const selectedPathIdentity = identityFromStats(await fileSystem.stat(path));
196
+ if (!sameExecutableIdentity(openedIdentity, selectedPathIdentity))
197
+ return {
198
+ sha256: null,
199
+ identity: null,
200
+ reason: "Selected executable path changed while it was being opened.",
201
+ };
202
+ // IO above can settle after cancellation. Do not construct a stream with an
203
+ // already-aborted signal: Node can emit a second, unhandled AbortError.
204
+ assertNotCancelled(signal);
205
+ const hash = createHash("sha256");
206
+ for await (const chunk of handle.createReadStream({
207
+ autoClose: false,
208
+ signal,
209
+ })) {
210
+ assertNotCancelled(signal);
211
+ hash.update(chunk);
212
+ }
213
+ assertNotCancelled(signal);
214
+ const finalIdentity = identityFromStats(await handle.stat({ bigint: true }));
215
+ if (!sameExecutableIdentity(openedIdentity, finalIdentity))
216
+ return {
217
+ sha256: null,
218
+ identity: null,
219
+ reason: "Selected executable changed while its contents were sampled.",
220
+ };
221
+ return {
222
+ sha256: hash.digest("hex"),
223
+ identity: openedIdentity,
224
+ reason: null,
225
+ };
226
+ }
227
+ catch (cause) {
228
+ assertNotCancelled(signal);
229
+ const reason = filesystemFailure(cause);
230
+ if (reason !== undefined)
231
+ return { sha256: null, identity: null, reason };
232
+ throw cause;
233
+ }
234
+ finally {
235
+ await handle?.close();
236
+ }
237
+ };
238
+ /** Compare the selected file metadata with the path immediately after PTY spawn. */
239
+ export const observeLaunchedExecutable = (path, selected) => {
240
+ if (selected.identity === null || selected.sha256 === null)
241
+ return {
242
+ selectedSha256: selected.sha256,
243
+ sha256: null,
244
+ state: "unknown",
245
+ reason: selected.reason ?? "Selected executable identity is unavailable.",
246
+ };
247
+ try {
248
+ const currentStats = statSync(path, { bigint: true });
249
+ const currentIdentity = identityFromStats(currentStats);
250
+ if (currentStats.isFile() &&
251
+ sameExecutableIdentity(selected.identity, currentIdentity))
252
+ return {
253
+ selectedSha256: selected.sha256,
254
+ sha256: selected.sha256,
255
+ state: "path_metadata_unchanged",
256
+ reason: null,
257
+ };
258
+ return {
259
+ selectedSha256: selected.sha256,
260
+ sha256: null,
261
+ state: "unknown",
262
+ reason: "Executable path metadata changed between sampling and spawn.",
263
+ };
264
+ }
265
+ catch (cause) {
266
+ const reason = filesystemFailure(cause);
267
+ if (reason !== undefined)
268
+ return {
269
+ selectedSha256: selected.sha256,
270
+ sha256: null,
271
+ state: "unknown",
272
+ reason,
273
+ };
274
+ throw cause;
275
+ }
276
+ };
277
+ export const createRunManifest = async (scenario, startedAt, completedAt, executable, host = {
278
+ platform: process.platform,
279
+ architecture: process.arch,
280
+ }) => {
281
+ const scenarioCommitment = processScenarioCommitment(scenario, executable.selectedSha256 ?? undefined);
282
+ const comparisonContract = processComparisonContract(scenario);
283
+ return {
284
+ rea_version: PRODUCT_IDENTITY.packageVersion,
285
+ provider_version: PROCESS_PROVIDER.version,
286
+ platform: host.platform,
287
+ architecture: host.architecture,
288
+ pty_backend: "node-pty",
289
+ started_at: startedAt.toISOString(),
290
+ completed_at: completedAt.toISOString(),
291
+ scenario: scenarioCommitment,
292
+ comparison_contract: comparisonContract,
293
+ full_scenario_sha256: digestProcessCommitment(scenarioCommitment),
294
+ comparison_contract_sha256: digestProcessCommitment(comparisonContract),
295
+ selected_executable_sha256: executable.selectedSha256,
296
+ executable_sha256: executable.sha256,
297
+ executable_identity: {
298
+ state: executable.state,
299
+ reason: executable.reason,
300
+ },
301
+ normalization_sha256: digestProcessCommitment(scenario.normalization),
302
+ };
303
+ };
304
+ export const observeSettlement = async (runId, processGroupIds, settleMs, recordEvent = () => undefined, platform = process.platform) => {
305
+ if (platform === "win32") {
306
+ recordEvent("lifecycle", 1);
307
+ return { state: "unverifiable", elapsed_ms: 0 };
308
+ }
309
+ const started = Date.now();
310
+ let consecutiveEmpty = 0;
311
+ let deadlineReached = false;
312
+ while (!deadlineReached) {
313
+ const observations = await Promise.all([...new Set(processGroupIds)].map((processGroupId) => observeOwnedProcessGroup({
314
+ runId,
315
+ leaderPid: processGroupId,
316
+ processGroupId,
317
+ })));
318
+ if (observations.some(({ state }) => state === "unverifiable")) {
319
+ recordEvent("lifecycle", 1);
320
+ return { state: "unverifiable", elapsed_ms: Date.now() - started };
321
+ }
322
+ if (observations.every(({ state }) => state === "empty")) {
323
+ consecutiveEmpty += 1;
324
+ if (consecutiveEmpty >= 2) {
325
+ recordEvent("lifecycle", 1);
326
+ return { state: "quiesced", elapsed_ms: Date.now() - started };
327
+ }
328
+ }
329
+ else
330
+ consecutiveEmpty = 0;
331
+ deadlineReached = Date.now() - started >= settleMs;
332
+ if (deadlineReached)
333
+ break;
334
+ await new Promise((resolve) => setTimeout(resolve, 50));
335
+ }
336
+ recordEvent("lifecycle", 1);
337
+ return { state: "alive_at_deadline", elapsed_ms: Date.now() - started };
338
+ };
339
+ export const awaitTerminalExit = async ({ terminal, scenario, started, lastOutput, signal, timers, interactions, dispatchedEventIndexes, recordEvent, }) => new Promise((resolveExit) => {
340
+ // The kill caused by a deadline is observed later as an ordinary PTY exit.
341
+ // Keep the initiating lifecycle reason so comparisons distinguish a target
342
+ // exit from harness-owned timeout, idle-timeout, and cancellation cleanup.
343
+ let reason = "exited";
344
+ terminal.onExit((exit) => {
345
+ recordEvent("lifecycle", 0);
346
+ for (const [eventIndex, event] of scenario.events.entries()) {
347
+ if (dispatchedEventIndexes.has(eventIndex))
348
+ continue;
349
+ const sequence = interactions.length;
350
+ interactions.push({
351
+ sequence,
352
+ scheduled_at_ms: event.at_ms,
353
+ dispatched_at_ms: Math.max(0, Date.now() - started),
354
+ type: event.type,
355
+ data: event.type === "input"
356
+ ? "<not-dispatched>"
357
+ : event.type === "resize"
358
+ ? `${String(event.columns)}x${String(event.rows)}`
359
+ : event.signal,
360
+ outcome: "target_exited",
361
+ });
362
+ recordEvent("interaction_events", sequence);
363
+ }
364
+ for (const timer of timers) {
365
+ timer.cancel();
366
+ }
367
+ timers.clear();
368
+ resolveExit({ ...exit, reason });
369
+ });
370
+ const timeout = scheduleProcessInterval(() => {
371
+ if (signal?.aborted === true) {
372
+ reason = "cancelled";
373
+ terminal.kill("SIGKILL");
374
+ }
375
+ else if (Date.now() - started >= scenario.timeout_ms) {
376
+ reason = "timeout";
377
+ terminal.kill("SIGKILL");
378
+ }
379
+ else if (Date.now() - lastOutput() >= scenario.idle_timeout_ms) {
380
+ reason = "idle_timeout";
381
+ terminal.kill("SIGKILL");
382
+ }
383
+ }, 20);
384
+ timers.add(timeout);
385
+ });
386
+ const processCaptureCleanupHost = {
387
+ platform: process.platform,
388
+ cleanupProcessGroup: cleanupOwnedProcessGroup,
389
+ verifyTokenOwnedProcesses: verifyNoTokenOwnedProcesses,
390
+ removeTemporaryRoot: (path) => rm(path, { recursive: true, force: true }),
391
+ };
392
+ const PROCESS_CLEANUP_VERIFICATION_GRACE_MS = 1_000;
393
+ const PROCESS_CLEANUP_VERIFICATION_INTERVAL_MS = 25;
394
+ const verifyTokenOwnedProcessesUntilSettled = async (verify) => {
395
+ const deadline = Date.now() + PROCESS_CLEANUP_VERIFICATION_GRACE_MS;
396
+ let result = await verify();
397
+ while (!result.cleaned) {
398
+ const remaining = deadline - Date.now();
399
+ if (remaining <= 0)
400
+ return result;
401
+ await new Promise((resolve) => setTimeout(resolve, Math.min(PROCESS_CLEANUP_VERIFICATION_INTERVAL_MS, remaining)));
402
+ result = await verify();
403
+ }
404
+ return result;
405
+ };
406
+ export const releaseProcessResources = async (options) => {
407
+ const host = options.host ?? processCaptureCleanupHost;
408
+ for (const timer of options.timers)
409
+ timer.cancel();
410
+ let terminalRenderer = {
411
+ state: options.renderer === undefined ? "not_required" : "cleaned",
412
+ reason: null,
413
+ };
414
+ try {
415
+ await options.renderer?.dispose();
416
+ }
417
+ catch (cause) {
418
+ terminalRenderer = {
419
+ state: "failed",
420
+ reason: cause instanceof Error
421
+ ? cause.message || cause.name || "renderer dispose failed"
422
+ : "renderer dispose failed",
423
+ };
424
+ }
425
+ let ownedProcessGroup = {
426
+ state: options.terminal === undefined ? "not_required" : "cleaned",
427
+ reason: null,
428
+ };
429
+ if (options.terminal !== undefined) {
430
+ if (host.platform === "win32") {
431
+ ownedProcessGroup = {
432
+ state: "unverified",
433
+ reason: "owned process cleanup is unverifiable on Windows without process-job authority",
434
+ };
435
+ }
436
+ else {
437
+ const cleaned = await host.cleanupProcessGroup({
438
+ runId: options.runId,
439
+ leaderPid: options.terminal.pid,
440
+ processGroupId: options.terminal.pid,
441
+ sweepTokenOwnedProcesses: true,
442
+ ...(options.sampledProcessGroupIds === undefined
443
+ ? {}
444
+ : { sampledProcessGroupIds: options.sampledProcessGroupIds }),
445
+ ...(options.captureBaseline === undefined
446
+ ? {}
447
+ : { captureBaseline: options.captureBaseline }),
448
+ });
449
+ if (!cleaned.cleaned) {
450
+ ownedProcessGroup = { state: "unverified", reason: cleaned.reason };
451
+ }
452
+ else {
453
+ const verified = await verifyTokenOwnedProcessesUntilSettled(() => host.verifyTokenOwnedProcesses(options.runId, undefined, options.captureBaseline));
454
+ if (!verified.cleaned)
455
+ ownedProcessGroup = { state: "unverified", reason: verified.reason };
456
+ }
457
+ }
458
+ }
459
+ let temporaryRoot = {
460
+ state: "cleaned",
461
+ reason: null,
462
+ };
463
+ try {
464
+ await host.removeTemporaryRoot(options.temporaryRoot);
465
+ }
466
+ catch (cause) {
467
+ temporaryRoot = {
468
+ state: "failed",
469
+ reason: cause instanceof Error
470
+ ? cause.message || cause.name || "temporary root removal failed"
471
+ : "temporary root removal failed",
472
+ };
473
+ }
474
+ return {
475
+ owned_process_group: ownedProcessGroup,
476
+ terminal_renderer: terminalRenderer,
477
+ temporary_root: temporaryRoot,
478
+ };
479
+ };
480
+ export const cleanupReportFailure = (report) => {
481
+ const failures = Object.entries(report).filter(([, outcome]) => outcome.state === "failed" || outcome.state === "unverified");
482
+ return failures.length === 0
483
+ ? undefined
484
+ : failures
485
+ .map(([resource, outcome]) => `${resource}: ${outcome.reason ?? outcome.state}`)
486
+ .join("; ");
487
+ };
488
+ export const captureTerminalFrames = (options) => {
489
+ let outputBytes = 0;
490
+ let truncated = false;
491
+ options.terminal.onData((data) => {
492
+ options.onOutput();
493
+ const bytes = Buffer.byteLength(data);
494
+ if (outputBytes + bytes > options.scenario.limits.output_bytes) {
495
+ truncated = true;
496
+ return;
497
+ }
498
+ outputBytes += bytes;
499
+ const atMs = normalizeProcessElapsedTime(Date.now() - options.started, options.scenario.normalization.time_bucket_ms);
500
+ const normalized = normalizeProcessText(data, options.scenario, options.temporaryRoot, options.terminal.pid);
501
+ const sequence = options.frames.length;
502
+ options.frames.push({
503
+ sequence,
504
+ at_ms: atMs,
505
+ data: normalized,
506
+ });
507
+ options.renderer.write(data, atMs);
508
+ options.recordEvent("frames", sequence);
509
+ });
510
+ return () => truncated;
511
+ };
512
+ export const resolveProcessResult = (capture, executionFailure, cleanup, observations, partialContext) => {
513
+ const cleanupFailure = cleanupReportFailure(cleanup);
514
+ if (cleanupFailure !== undefined) {
515
+ const cleanupResources = Object.entries(cleanup)
516
+ .filter(([, outcome]) => outcome.state === "failed" || outcome.state === "unverified")
517
+ .map(([resource]) => resource);
518
+ const executionFailureReason = describeProcessCaptureExecutionFailure(executionFailure);
519
+ let partialObservation;
520
+ if (capture !== undefined || observations !== undefined) {
521
+ const normalizedObservations = observations === undefined || partialContext === undefined
522
+ ? observations
523
+ : normalizePartialProcessObservations(observations, partialContext);
524
+ const partialPayload = capture === undefined
525
+ ? { observations: normalizedObservations }
526
+ : { capture };
527
+ const pendingCapture = capture === undefined
528
+ ? undefined
529
+ : {
530
+ ...capture,
531
+ settlement: {
532
+ ...capture.settlement,
533
+ cleanup_outcome: "failed",
534
+ },
535
+ };
536
+ const partialResult = partialProcessCaptureObservationSchema.safeParse({
537
+ ...partialPayload,
538
+ ...(pendingCapture === undefined
539
+ ? {}
540
+ : {
541
+ capture: Object.fromEntries(Object.entries(pendingCapture).filter(([key]) => key !== "cleanup")),
542
+ }),
543
+ cleanup,
544
+ execution_failure: executionFailureReason ?? null,
545
+ });
546
+ if (partialResult.success)
547
+ partialObservation = partialResult.data;
548
+ }
549
+ throw new ProcessCaptureError(cleanupFailure, {
550
+ cause: executionFailure,
551
+ reason: "cleanup_incomplete",
552
+ cleanupResources,
553
+ ...(executionFailureReason === undefined
554
+ ? {}
555
+ : { executionFailure: executionFailureReason }),
556
+ ...(partialObservation === undefined ? {} : { partialObservation }),
557
+ cleanupReport: cleanup,
558
+ });
559
+ }
560
+ if (executionFailure instanceof ProcessCaptureError)
561
+ throw executionFailure;
562
+ if (executionFailure !== undefined) {
563
+ const executionFailureReason = describeProcessCaptureExecutionFailure(executionFailure);
564
+ throw new ProcessCaptureError("process capture failed", {
565
+ cause: executionFailure,
566
+ ...(executionFailureReason === undefined
567
+ ? {}
568
+ : { executionFailure: executionFailureReason }),
569
+ });
570
+ }
571
+ if (capture === undefined)
572
+ throw new ProcessCaptureError("process capture produced no result");
573
+ if ("cleanup" in capture)
574
+ return capture;
575
+ const settlement = capture.settlement.state === "quiesced"
576
+ ? { ...capture.settlement, cleanup_outcome: "not_required" }
577
+ : { ...capture.settlement, cleanup_outcome: "cleaned" };
578
+ try {
579
+ return parseProcessCapture({
580
+ ...capture,
581
+ settlement,
582
+ cleanup: {
583
+ owned_process_group: "verified",
584
+ temporary_root: "removed",
585
+ },
586
+ });
587
+ }
588
+ catch (cause) {
589
+ throw new ProcessCaptureError(`process capture validation failed${cause instanceof Error ? `: ${cause.message}` : ""}`, { cause });
590
+ }
591
+ };
592
+ export const prepareProcessCapture = async (scenario, signal, captureSnapshot = snapshotRoots, host = systemProcessPreparationHost) => {
593
+ assertNotCancelled(signal);
594
+ await host.prepareOwnershipInspector?.(signal);
595
+ assertNotCancelled(signal);
596
+ const before = await captureSnapshot(scenario, signal);
597
+ const temporaryRoot = await host.createTemporaryRoot();
598
+ try {
599
+ assertNotCancelled(signal);
600
+ const runId = randomUUID();
601
+ const ownershipBaseline = (await host.captureOwnershipBaseline?.(signal)) ?? [];
602
+ assertNotCancelled(signal);
603
+ return { temporaryRoot, runId, ownershipBaseline, before };
604
+ }
605
+ catch (cause) {
606
+ await host.cleanup(temporaryRoot);
607
+ throw cause;
608
+ }
609
+ };
610
+ const systemProcessPreparationHost = {
611
+ prepareOwnershipInspector: prepareProcessOwnershipInspection,
612
+ captureOwnershipBaseline: (signal) => systemProcessOwnershipHost.captureBaseline?.(signal) ?? Promise.resolve([]),
613
+ createTemporaryRoot: () => mkdtemp(join(tmpdir(), "rea-process-")),
614
+ cleanup: (path) => rm(path, { recursive: true, force: true }),
615
+ };