rea-agents 6.0.0 → 6.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 (343) hide show
  1. package/README.md +23 -17
  2. package/bridge/android/ReaJadxBridge.java +14 -5
  3. package/bridge/ghidra/ReaGhidraBridge.java +173 -47
  4. package/bridge/hopper_bridge.py +310 -128
  5. package/bridge/mitmproxy/capture.py +0 -2
  6. package/bridge/native/rea_lldb_tracer.py +630 -94
  7. package/bridge/process/ProcessRunTokenReader.swift +39 -5
  8. package/bridge/pwntools/recorded_crash_impl.py +4 -0
  9. package/dist/android/JadxConfiguration.js +196 -33
  10. package/dist/android/JadxMcpTransport.js +4 -0
  11. package/dist/android/JadxProtocol.js +6 -0
  12. package/dist/android/JadxProvider.js +19 -2
  13. package/dist/android/JadxSession.js +23 -4
  14. package/dist/application/AnalysisContextQueries.js +8 -35
  15. package/dist/application/AnalysisDocument.js +32 -0
  16. package/dist/application/AppBundleExecutable.js +67 -21
  17. package/dist/application/CallPathTracing.js +19 -3
  18. package/dist/application/CapabilityInventory.js +6 -3
  19. package/dist/application/ClientConfigurationDocument.js +871 -6
  20. package/dist/application/ClientRegistrationIdentity.js +26 -5
  21. package/dist/application/ClientRegistrationStatus.js +29 -14
  22. package/dist/application/DirectAnalysis.js +27 -37
  23. package/dist/application/Doctor.js +4 -2
  24. package/dist/application/DoctorScope.js +25 -13
  25. package/dist/application/EnhancedLiteralTracing.js +22 -25
  26. package/dist/application/EnhancedTools.js +13 -11
  27. package/dist/application/EvidenceBundleFiles.js +23 -11
  28. package/dist/application/FunctionAnalysisEvidence.js +2 -3
  29. package/dist/application/InventoryProjectionEvidence.js +4 -3
  30. package/dist/application/InvestigationProviders.js +3 -2
  31. package/dist/application/JsonFiles.js +50 -28
  32. package/dist/application/ProcedureAddressResolution.js +11 -0
  33. package/dist/application/ReconstructionCoverageService.js +7 -4
  34. package/dist/application/ReconstructionObligationLedgerService.js +4 -3
  35. package/dist/application/Setup.js +13 -1
  36. package/dist/application/SetupClientConfiguration.js +10 -15
  37. package/dist/application/SetupHost.js +5 -2
  38. package/dist/application/SetupPlan.js +5 -3
  39. package/dist/application/SupportedClients.js +34 -1
  40. package/dist/application/Uninstall.js +4 -3
  41. package/dist/application/Update.js +18 -3
  42. package/dist/application/UpdateRuntime.js +46 -25
  43. package/dist/application/WebModuleTraceService.js +4 -2
  44. package/dist/application/WebNetworkCaptureService.js +4 -2
  45. package/dist/application/WebRuntimeService.js +5 -3
  46. package/dist/application/WebScriptExportService.js +2 -2
  47. package/dist/application/WebSourceLocationService.js +4 -2
  48. package/dist/application/WorkflowEvidence.js +82 -0
  49. package/dist/application/binary/AnalysisProviderEvaluation.js +21 -5
  50. package/dist/application/binary/AnalysisProviderRegistry.js +2 -0
  51. package/dist/application/binary/AnalysisSnapshotCache.js +3 -6
  52. package/dist/application/binary/BinarySession.js +95 -68
  53. package/dist/application/binary/BinarySessionOpen.js +3 -3
  54. package/dist/application/binary/BinarySessionRecords.js +25 -3
  55. package/dist/application/binary/BinarySessionStatus.js +17 -1
  56. package/dist/application/binary/CompositeProvider.js +13 -0
  57. package/dist/application/binary/LazyAnalysisProvider.js +3 -0
  58. package/dist/application/binaryDiagnostics/BinaryLayoutService.js +4 -2
  59. package/dist/application/binaryDiagnostics/RecordedCrashService.js +4 -2
  60. package/dist/application/evm/EvmInterfaceService.js +4 -2
  61. package/dist/application/investigation/EvidenceLedger.js +21 -7
  62. package/dist/application/investigation/InvestigationRecords.js +8 -0
  63. package/dist/application/javascript/JavaScriptAnalysisControl.js +34 -0
  64. package/dist/application/javascript/JavaScriptApplicationEvidence.js +24 -3
  65. package/dist/application/javascript/JavaScriptApplicationEvidenceGraph.js +19 -2
  66. package/dist/application/javascript/JavaScriptApplicationService.js +22 -15
  67. package/dist/application/javascript/JavaScriptApplicationWorkflowService.js +5 -5
  68. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +5 -4
  69. package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +39 -7
  70. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +6 -3
  71. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +11 -3
  72. package/dist/application/javascript/JavaScriptModuleRelationships.js +37 -8
  73. package/dist/application/javascript/JavaScriptRecoveryService.js +4 -2
  74. package/dist/application/javascript/JavaScriptRuntimeObservationService.js +3 -1
  75. package/dist/application/javascript/JavaScriptRuntimeReconciliationService.js +3 -3
  76. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +63 -61
  77. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +23 -1
  78. package/dist/application/managed/ManagedApplicationGraphService.js +4 -3
  79. package/dist/application/managed/ManagedMemberComparisonService.js +7 -4
  80. package/dist/application/managed/ManagedNativeVerificationService.js +4 -3
  81. package/dist/application/managed/ManagedReconstructionService.js +4 -3
  82. package/dist/application/process/ProcessCli.js +14 -6
  83. package/dist/application/process/ProcessEvidence.js +4 -3
  84. package/dist/application/workflowInputError.js +18 -0
  85. package/dist/artifacts/ArtifactProvider.js +12 -2
  86. package/dist/artifacts/MachOSliceArtifactReader.js +98 -14
  87. package/dist/artifacts/NativeDmgArtifactReader.js +13 -6
  88. package/dist/artifacts/apple/AppleAssetCatalogAnalysis.js +22 -3
  89. package/dist/artifacts/apple/InterfaceBuilderAnalysis.js +176 -56
  90. package/dist/artifacts/apple/InterfaceBuilderDecodeBudget.js +195 -0
  91. package/dist/artifacts/apple/KeyedArchiveReader.js +36 -22
  92. package/dist/artifacts/apple/MachoLoadCommandReader.js +92 -31
  93. package/dist/artifacts/apple/NibArchive.js +81 -14
  94. package/dist/artifacts/apple/NibViewHierarchy.js +10 -3
  95. package/dist/artifacts/apple/PlistNumberLiterals.js +164 -0
  96. package/dist/artifacts/inventory/ArtifactGraphConstruction.js +13 -8
  97. package/dist/artifacts/inventory/scanCanonical.js +11 -14
  98. package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +6 -2
  99. package/dist/browser/CdpCaptureCompleteness.js +2 -0
  100. package/dist/browser/CdpCaptureEventHandlers.js +76 -11
  101. package/dist/browser/CdpCaptureEventTypes.js +5 -1
  102. package/dist/browser/CdpCaptureEvents.js +25 -0
  103. package/dist/browser/CdpConnection.js +30 -11
  104. package/dist/browser/CdpOptionalCommand.js +2 -3
  105. package/dist/browser/CdpPageCapture.js +23 -1
  106. package/dist/browser/PlaywrightElectronActiveActions.js +32 -23
  107. package/dist/browser/PlaywrightElectronActiveProjection.js +141 -0
  108. package/dist/browser/PlaywrightElectronActiveProvider.js +123 -124
  109. package/dist/browser/WebSourceMapFetcher.js +426 -72
  110. package/dist/browser/assets/LocalWebScriptArtifacts.js +2 -2
  111. package/dist/browser/history/CaptureFailures.js +37 -1
  112. package/dist/browser/history/CaptureRelease.js +5 -0
  113. package/dist/browser/history/HarCaptureAdapter.js +3 -3
  114. package/dist/browser/history/HarJson.js +3 -2
  115. package/dist/browser/history/HistoricalCaptureDecoder.js +4 -2
  116. package/dist/cli/analysisSelector.js +23 -0
  117. package/dist/cli/applicationCommands.js +2 -3
  118. package/dist/cli/coreBinaryCommands.js +70 -12
  119. package/dist/cli/electronCommands.js +7 -6
  120. package/dist/cli/javascriptApplicationAnalysis.js +7 -3
  121. package/dist/cli/jsonOutput.js +4 -99
  122. package/dist/cli/nativeCallCommands.js +2 -3
  123. package/dist/cli/webRuntimeCommands.js +8 -8
  124. package/dist/cli.js +2 -1
  125. package/dist/cliCommandNames.js +0 -1
  126. package/dist/cliJsonInput.js +9 -4
  127. package/dist/config/environment.js +4 -0
  128. package/dist/config/ghidraStartupTimeout.js +17 -0
  129. package/dist/config/mcpResponseBudget.js +22 -0
  130. package/dist/config/parseConfig.js +2 -1
  131. package/dist/contracts/enhancedInputs.js +5 -3
  132. package/dist/contracts/enhancedToolContracts.js +3 -3
  133. package/dist/contracts/hopperMemoryContracts.js +2 -5
  134. package/dist/contracts/javascript/electronToolContracts.js +1 -1
  135. package/dist/contracts/managed/managedToolContracts.js +2 -4
  136. package/dist/contracts/native/nativeToolContracts.js +4 -2
  137. package/dist/contracts/officialToolContracts.js +14 -15
  138. package/dist/contracts/promptContracts.js +3 -3
  139. package/dist/contracts/sessionLifecycleInputs.js +3 -7
  140. package/dist/contracts/sessionStatusContract.js +2 -4
  141. package/dist/contracts/sessionToolContracts.js +4 -6
  142. package/dist/contracts/sessionToolSchemas.js +2 -5
  143. package/dist/contracts/toolContractExamples.js +0 -1
  144. package/dist/contracts/toolContractHelpers.js +4 -1
  145. package/dist/contracts/toolEffects.js +0 -1
  146. package/dist/contracts/toolOutputSchemaGroups.js +28 -27
  147. package/dist/contracts/toolOutputSchemaPrimitives.js +18 -0
  148. package/dist/contracts/toolSchemaMetadata.js +69 -10
  149. package/dist/contracts/webNetworkCaptureToolContracts.js +1 -1
  150. package/dist/domain/analysisErrorBase.js +8 -3
  151. package/dist/domain/analysisErrorCore.js +3 -1
  152. package/dist/domain/analysisErrorPresentation.js +72 -17
  153. package/dist/domain/analysisErrorProjection.js +106 -27
  154. package/dist/domain/android/androidAnalysis.js +2 -4
  155. package/dist/domain/android/androidApplication.js +17 -9
  156. package/dist/domain/apple/appleApplication.js +41 -14
  157. package/dist/domain/apple/dyldEnvironment.js +73 -0
  158. package/dist/domain/apple/dylibResolution.js +120 -9
  159. package/dist/domain/apple/dylibResolutionFindings.js +9 -7
  160. package/dist/domain/apple/interfaceBuilderGraph.js +25 -13
  161. package/dist/domain/apple/interfaceBuilderKeyedArchive.js +13 -3
  162. package/dist/domain/apple/keyedArchive.js +9 -1
  163. package/dist/domain/apple/machoContainer.js +54 -0
  164. package/dist/domain/apple/plistNumbers.js +76 -0
  165. package/dist/domain/artifactIdentity.js +29 -0
  166. package/dist/domain/artifactInventoryEvidence.js +19 -24
  167. package/dist/domain/bridgeCandidateProjection.js +71 -0
  168. package/dist/domain/browserCompleteness.js +1 -0
  169. package/dist/domain/browserObservationError.js +24 -5
  170. package/dist/domain/browserScenarioValues.js +2 -6
  171. package/dist/domain/bundleComparison.js +3 -2
  172. package/dist/domain/canonicalJsonDigestSteps.js +133 -0
  173. package/dist/domain/canonicalOrdering.js +9 -5
  174. package/dist/domain/changedBehavior.js +6 -6
  175. package/dist/domain/comparisonSourceEvidence.js +40 -0
  176. package/dist/domain/configurationErrors.js +1 -1
  177. package/dist/domain/evidence.js +64 -9
  178. package/dist/domain/evidenceBundle.js +42 -1
  179. package/dist/domain/evidenceErrors.js +12 -1
  180. package/dist/domain/explicitSensitiveFailure.js +17 -1
  181. package/dist/domain/firmware/firmwareAnalysis.js +6 -7
  182. package/dist/domain/hopperErrors.js +23 -2
  183. package/dist/domain/hopperValues.js +51 -8
  184. package/dist/domain/immutableJson.js +114 -0
  185. package/dist/domain/inputIssueProjection.js +3 -0
  186. package/dist/domain/javascript/electronActiveObservation.js +30 -4
  187. package/dist/domain/javascript/electronStaticAnalysisValues.js +6 -12
  188. package/dist/domain/javascript/javascriptApplicationAnalysis.js +71 -23
  189. package/dist/domain/javascript/javascriptApplicationGraph.js +13 -0
  190. package/dist/domain/javascript/javascriptAstValues.js +20 -0
  191. package/dist/domain/javascript/javascriptRecovery.js +3 -8
  192. package/dist/domain/javascript/javascriptSemanticAnalysis.js +97 -2
  193. package/dist/domain/javascript/javascriptSemanticCoverage.js +6 -5
  194. package/dist/domain/javascript/javascriptSemanticDataEffectHelpers.js +9 -17
  195. package/dist/domain/javascript/javascriptSemanticDataEffects.js +22 -5
  196. package/dist/domain/javascript/javascriptSemanticGraph.js +13 -0
  197. package/dist/domain/javascript/javascriptSemanticGraphSchemas.js +1 -0
  198. package/dist/domain/javascript/javascriptSemanticPrimitives.js +25 -17
  199. package/dist/domain/javascript/javascriptSemanticQuerySchemas.js +8 -4
  200. package/dist/domain/javascript/javascriptSemanticResourceLimits.js +77 -0
  201. package/dist/domain/javascript/javascriptSemanticValues.js +83 -11
  202. package/dist/domain/javascript/javascriptSourceParser.js +6 -2
  203. package/dist/domain/javascript/javascriptStaticAnalysis.js +22 -17
  204. package/dist/domain/javascript/javascriptStaticAnalysisBundler.js +5 -3
  205. package/dist/domain/javascript/javascriptStaticAnalysisCalls.js +14 -4
  206. package/dist/domain/javascript/javascriptStaticAnalysisFindings.js +66 -7
  207. package/dist/domain/javascript/javascriptStaticAnalysisHelpers.js +28 -16
  208. package/dist/domain/javascript/javascriptStaticAnalysisState.js +3 -2
  209. package/dist/domain/jsonSerialization.js +109 -0
  210. package/dist/domain/jsonValue.js +90 -6
  211. package/dist/domain/localPath.js +6 -0
  212. package/dist/domain/managed/managedApplicationGraph.js +10 -10
  213. package/dist/domain/managed/managedInspectionEvidence.js +16 -0
  214. package/dist/domain/managed/managedMemberComparisonMatch.js +5 -9
  215. package/dist/domain/managed/managedNativeVerification.js +3 -9
  216. package/dist/domain/native/fileOffsetPartialObservation.js +1 -0
  217. package/dist/domain/native/nativeCallObservation.js +33 -11
  218. package/dist/domain/native/nativeCallPartialObservation.js +33 -0
  219. package/dist/domain/process/processCapture.js +8 -4
  220. package/dist/domain/process/processComparison.js +38 -4
  221. package/dist/domain/process/processScenario.js +11 -10
  222. package/dist/domain/propertyListKeys.js +1 -1
  223. package/dist/domain/propertyListXmlText.js +19 -0
  224. package/dist/domain/providerAdapterError.js +1 -1
  225. package/dist/domain/providerOperationHealth.js +54 -0
  226. package/dist/domain/reconstructionVerification.js +6 -43
  227. package/dist/domain/reconstructionVerificationSchemas.js +2 -2
  228. package/dist/domain/staticRuntimeCorrelation.js +6 -43
  229. package/dist/domain/webBundleAnalysis.js +20 -8
  230. package/dist/domain/webBundleAnalyzerInspection.js +13 -8
  231. package/dist/domain/webBundleAnalyzerResult.js +41 -2
  232. package/dist/domain/webRuntime.js +19 -5
  233. package/dist/domain/webSourceLocation.js +1 -0
  234. package/dist/firmware/FirmwareFiles.js +1 -1
  235. package/dist/generatedPackageMetadata.js +1 -1
  236. package/dist/ghidra/GhidraAnalysisProfile.js +5 -0
  237. package/dist/ghidra/GhidraClient.js +47 -14
  238. package/dist/ghidra/GhidraDiagnostics.js +7 -7
  239. package/dist/ghidra/GhidraFunctionValues.js +10 -7
  240. package/dist/ghidra/GhidraInstallation.js +18 -21
  241. package/dist/ghidra/GhidraInventoryValues.js +14 -4
  242. package/dist/ghidra/GhidraJavaLaunch.js +62 -0
  243. package/dist/ghidra/GhidraLauncher.js +33 -16
  244. package/dist/ghidra/GhidraProviderCapabilities.js +14 -2
  245. package/dist/ghidra/GhidraProviderClient.js +23 -12
  246. package/dist/ghidra/GhidraSessionError.js +11 -7
  247. package/dist/ghidra/GhidraTargetSnapshot.js +47 -12
  248. package/dist/ghidra/GhidraTransport.js +20 -0
  249. package/dist/hopper/BridgeLauncher.js +31 -11
  250. package/dist/hopper/HopperAnalysisProfile.js +62 -4
  251. package/dist/hopper/HopperCleanup.js +15 -3
  252. package/dist/hopper/HopperClient.js +222 -14
  253. package/dist/hopper/HopperFileOffset.js +237 -0
  254. package/dist/hopper/HopperMachOImage.js +196 -0
  255. package/dist/hopper/HopperOperationStage.js +6 -0
  256. package/dist/hopper/HopperProvider.js +63 -9
  257. package/dist/hopper/HopperProviderCapabilities.js +19 -1
  258. package/dist/hopper/HopperRegexSearch.js +137 -0
  259. package/dist/hopper/HopperRegexWorker.js +27 -0
  260. package/dist/hopper/HopperRequestQueue.js +68 -19
  261. package/dist/hopper/HopperTargetLease.js +3 -2
  262. package/dist/inspector/JavaScriptRuntimeScope.js +9 -1
  263. package/dist/inspector/V8InspectorCaptureProjection.js +8 -3
  264. package/dist/inspector/V8InspectorProvider.js +47 -11
  265. package/dist/javascript/sourceMaps/DecodedSourceMap.js +47 -0
  266. package/dist/javascript/sourceMaps/SourceMapCommand.js +16 -0
  267. package/dist/javascript/sourceMaps/SourceMapFormat.js +79 -31
  268. package/dist/javascript/sourceMaps/TraceSourceMap.js +14 -28
  269. package/dist/mcpDoctor.js +62 -2
  270. package/dist/native/AppleDispatchCoverage.js +47 -6
  271. package/dist/native/AppleDispatchDecodeFacts.js +6 -0
  272. package/dist/native/AppleDispatchMetadata.js +6 -3
  273. package/dist/native/AppleMachoFixups.js +48 -24
  274. package/dist/native/AppleMachoSelection.js +32 -16
  275. package/dist/native/AppleObjcCategories.js +6 -2
  276. package/dist/native/AppleObjcDispatchFacets.js +5 -2
  277. package/dist/native/AppleObjcProperties.js +2 -1
  278. package/dist/native/AppleSwiftDispatchFacets.js +27 -21
  279. package/dist/native/AppleSwiftVtables.js +21 -15
  280. package/dist/native/CommandRunner.js +81 -84
  281. package/dist/native/LldbCallTracer.js +365 -102
  282. package/dist/native/LldbRetainedObservations.js +178 -0
  283. package/dist/native/NativeCallObservation.js +62 -3
  284. package/dist/native/NativeMacOSProvider.js +98 -17
  285. package/dist/native/SignatureTargetBinding.js +155 -0
  286. package/dist/native/parsers/lipo.js +71 -1
  287. package/dist/native/pwndbg/PwndbgCoreContext.js +25 -5
  288. package/dist/native/pwntools/RecordedCrashStructureBindings.js +2 -0
  289. package/dist/process/DarwinProcessRunTokenReader.js +8 -9
  290. package/dist/process/ExecFileOutput.js +20 -0
  291. package/dist/process/OwnedCommand.js +7 -2
  292. package/dist/process/ProcessOwnership.js +203 -53
  293. package/dist/process/ProcessOwnershipObservation.js +49 -4
  294. package/dist/process/ProviderProcess.js +38 -7
  295. package/dist/process/capture/ProcessCaptureError.js +2 -2
  296. package/dist/process/capture/ProcessCaptureLifecycle.js +100 -34
  297. package/dist/server/EvidenceMcpServer.js +146 -0
  298. package/dist/server/createServer.js +15 -8
  299. package/dist/server/registerAndroidTools.js +2 -4
  300. package/dist/server/registerApplicationTools/helpers.js +2 -4
  301. package/dist/server/registerArtifactComparisonTool.js +11 -7
  302. package/dist/server/registerBinaryDiagnosticsTools.js +2 -4
  303. package/dist/server/registerBrowserScenarioTool.js +2 -4
  304. package/dist/server/registerBrowserTools.js +2 -4
  305. package/dist/server/registerBundleComparisonTool.js +2 -3
  306. package/dist/server/registerCloseLifecycleTool.js +8 -18
  307. package/dist/server/registerElectronTools.js +2 -4
  308. package/dist/server/registerEnhancedTools.js +18 -59
  309. package/dist/server/registerEvidenceTools.js +2 -4
  310. package/dist/server/registerEvmTools.js +2 -4
  311. package/dist/server/registerFirmwareTools.js +2 -4
  312. package/dist/server/registerFunctionComparisonTool.js +10 -6
  313. package/dist/server/registerInvestigationTools.js +7 -7
  314. package/dist/server/registerJavaScriptRecoveryTool.js +2 -4
  315. package/dist/server/registerJavaScriptRuntimeObservationTools.js +2 -4
  316. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +2 -4
  317. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +2 -4
  318. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +2 -4
  319. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +2 -4
  320. package/dist/server/registerOfficialTools.js +2 -4
  321. package/dist/server/registerProcessComparisonTool.js +71 -45
  322. package/dist/server/registerRecordedCrashTools.js +2 -4
  323. package/dist/server/registerSessionRecordTools.js +5 -6
  324. package/dist/server/registerSessionStatusTool.js +17 -3
  325. package/dist/server/registerSessionTools.js +11 -3
  326. package/dist/server/registerWebModuleTool.js +2 -4
  327. package/dist/server/registerWebNetworkCaptureTool.js +2 -4
  328. package/dist/server/registerWebRuntimeTools.js +3 -7
  329. package/dist/server/registerWebScriptTool.js +2 -4
  330. package/dist/server/registerWebSourceLocationTool.js +2 -4
  331. package/dist/server/runDerivedOperation.js +15 -3
  332. package/dist/server/sessionAvailabilityPolicy.js +0 -1
  333. package/dist/server/toolRegistrationOptions.js +2 -2
  334. package/dist/server/toolResult.js +62 -10
  335. package/dist/server/toolResultEncoding.js +33 -0
  336. package/install.sh +1 -1
  337. package/native/windows/build/manifest.json +1 -1
  338. package/package.json +8 -3
  339. package/scripts/electron-active-hook-boundaries.cjs +18 -20
  340. package/scripts/electron-active-hook.cjs +241 -37
  341. package/skills/reverse-engineer-anything/SKILL.md +11 -3
  342. package/skills/reverse-engineer-anything/references/javascript-applications.md +8 -1
  343. package/dist/ghidra/GhidraDefaults.js +0 -2
@@ -77,23 +77,35 @@ export const cleanupOwnedProcessGroup = async (ownership, host = systemHost) =>
77
77
  }
78
78
  }
79
79
  if (plan.unresolved.length > 0)
80
- return cleanupValidationFailure(plan.unresolved);
81
- return { cleaned: true, signaled };
80
+ return cleanupValidationFailure(plan.unresolved, plan.unverified);
81
+ return {
82
+ cleaned: true,
83
+ signaled,
84
+ ...(plan.unverified.length === 0 ? {} : { unverified: plan.unverified }),
85
+ };
82
86
  };
83
87
  /** Verify that no live process still carries an owned capture run token. */
84
- export const verifyNoTokenOwnedProcesses = async (runId, host = systemHost, captureBaseline) => {
88
+ export const verifyNoTokenOwnedProcesses = async (runId, host = systemHost, captureBaseline, relation) => {
85
89
  const processTable = await readLiveProcessTable(host);
86
90
  if (!processTable.available)
87
91
  return {
88
92
  cleaned: false,
89
93
  reason: `process table could not be inspected: ${processTable.reason}`,
90
94
  };
91
- const scan = await scanTokenOwnedProcesses(processTable.processes, runId, host, captureBaseline);
95
+ const scan = await scanTokenOwnedProcesses(processTable.processes, runId, host, captureBaseline, relation);
92
96
  if (scan.failures.length > 0)
93
- return cleanupValidationFailure(scan.failures);
94
- return scan.owned.length === 0
95
- ? { cleaned: true, signaled: false }
96
- : { cleaned: false, reason: "token-owned process remained after cleanup" };
97
+ return cleanupValidationFailure(scan.failures, scan.unverified);
98
+ if (scan.owned.length > 0)
99
+ return {
100
+ cleaned: false,
101
+ reason: "token-owned process remained after cleanup",
102
+ ...(scan.unverified.length === 0 ? {} : { unverified: scan.unverified }),
103
+ };
104
+ return {
105
+ cleaned: true,
106
+ signaled: false,
107
+ ...(scan.unverified.length === 0 ? {} : { unverified: scan.unverified }),
108
+ };
97
109
  };
98
110
  const readLiveProcessTable = async (host) => {
99
111
  try {
@@ -109,8 +121,14 @@ const readLiveProcessTable = async (host) => {
109
121
  const createOwnedCleanupPlan = async (ownership, processes, host) => {
110
122
  const sampledProcessGroupIds = new Set((ownership.sampledProcessGroupIds ?? []).filter((processGroupId) => Number.isSafeInteger(processGroupId) && processGroupId > 0));
111
123
  const tokenOwned = ownership.sweepTokenOwnedProcesses === true
112
- ? await scanTokenOwnedProcesses(processes, ownership.runId, host, ownership.captureBaseline)
113
- : { owned: [], failures: [], identities: new Map() };
124
+ ? await scanTokenOwnedProcesses(processes, ownership.runId, host, ownership.captureBaseline, {
125
+ leaderPid: ownership.leaderPid,
126
+ processGroupId: ownership.processGroupId,
127
+ ...(sampledProcessGroupIds.size === 0
128
+ ? {}
129
+ : { sampledProcessGroupIds: [...sampledProcessGroupIds] }),
130
+ })
131
+ : { owned: [], failures: [], unverified: [], identities: new Map() };
114
132
  const tokenOwnedGroupIds = new Set(tokenOwned.owned.map(({ processGroupId }) => processGroupId));
115
133
  const launcher = processes.find(({ pid }) => pid === ownership.leaderPid);
116
134
  const rootMembers = processes.filter(({ processGroupId }) => processGroupId === ownership.processGroupId);
@@ -123,8 +141,15 @@ const createOwnedCleanupPlan = async (ownership, processes, host) => {
123
141
  signalOrder: [],
124
142
  tokenOwnedIdentities: tokenOwned.identities,
125
143
  unresolved: tokenOwned.failures,
144
+ unverified: tokenOwned.unverified,
126
145
  }
127
- : { cleaned: true, signaled: false };
146
+ : {
147
+ cleaned: true,
148
+ signaled: false,
149
+ ...(tokenOwned.unverified.length === 0
150
+ ? {}
151
+ : { unverified: tokenOwned.unverified }),
152
+ };
128
153
  let descendants = [];
129
154
  if (launcher !== undefined) {
130
155
  const identityFailure = launcherIdentityFailure(launcher, ownership);
@@ -179,7 +204,7 @@ const createOwnedCleanupPlan = async (ownership, processes, host) => {
179
204
  signalableGroups.add(processGroupId);
180
205
  }
181
206
  if (ownership.sweepTokenOwnedProcesses !== true && unresolved.length > 0)
182
- return cleanupValidationFailure(unresolved);
207
+ return cleanupValidationFailure(unresolved, tokenOwned.unverified);
183
208
  return {
184
209
  signalOrder: [...signalableGroups]
185
210
  .filter((groupId) => processGroupIds.has(groupId))
@@ -190,6 +215,7 @@ const createOwnedCleanupPlan = async (ownership, processes, host) => {
190
215
  : left - right),
191
216
  tokenOwnedIdentities: tokenOwned.identities,
192
217
  unresolved,
218
+ unverified: tokenOwned.unverified,
193
219
  };
194
220
  };
195
221
  const revalidateOwnedProcessGroup = async (processGroupId, ownership, host, expectedIdentities) => {
@@ -224,19 +250,22 @@ const revalidateOwnedProcessGroup = async (processGroupId, ownership, host, expe
224
250
  };
225
251
  return { empty: members.length === 0 };
226
252
  };
227
- const cleanupValidationFailure = (failures) => {
253
+ const cleanupValidationFailure = (failures, unverified = []) => {
254
+ const unverifiedEntry = unverified.length === 0 ? {} : { unverified };
228
255
  const unreadable = failures.filter(({ reason }) => reason === "environment-unreadable");
229
256
  if (failures.some(({ reason }) => reason === "run-token-mismatch"))
230
257
  return {
231
258
  cleaned: false,
232
259
  reason: "process tree contains an unowned or PID-reused process",
233
260
  failures,
261
+ ...unverifiedEntry,
234
262
  };
235
263
  if (failures.some(({ reason }) => reason === "process-identity-unavailable"))
236
264
  return {
237
265
  cleaned: false,
238
266
  reason: "process identity could not be revalidated",
239
267
  failures,
268
+ ...unverifiedEntry,
240
269
  };
241
270
  if (unreadable.length > 0) {
242
271
  const counts = new Map();
@@ -248,21 +277,28 @@ const cleanupValidationFailure = (failures) => {
248
277
  .sort(([left], [right]) => left.localeCompare(right))
249
278
  .map(([category, count]) => `${category}=${String(count)}`)
250
279
  .join(", ");
280
+ const candidates = unreadable
281
+ .map(({ pid, diagnostic }) => `${String(pid)}=${sanitizedTokenReadFailure(diagnostic)}`)
282
+ .sort((left, right) => left.localeCompare(right));
251
283
  return {
252
284
  cleaned: false,
253
- reason: `process ownership token could not be read for ${String(unreadable.length)} live process(es): ${breakdown}`,
285
+ reason: `process ownership token could not be read for ${String(unreadable.length)} live process(es): ${breakdown}; live candidates ${candidates.join(", ")}`,
254
286
  failures,
287
+ ...unverifiedEntry,
255
288
  };
256
289
  }
257
290
  return {
258
291
  cleaned: false,
259
292
  reason: "process ownership could not be revalidated",
260
293
  failures,
294
+ ...unverifiedEntry,
261
295
  };
262
296
  };
263
297
  const sanitizedTokenReadFailure = (diagnostic) => {
264
298
  if (diagnostic === "environment_unavailable")
265
299
  return diagnostic;
300
+ if (diagnostic === "platform_binary_environment_withheld")
301
+ return diagnostic;
266
302
  const environmentErrno = /^(EACCES|EPERM|ENOENT|ESRCH):/u.exec(diagnostic ?? "");
267
303
  if (environmentErrno?.[1] !== undefined)
268
304
  return `environment_errno_${environmentErrno[1]}`;
@@ -360,7 +396,26 @@ const processOwnershipFailures = async (members, runId, host, expectedIdentities
360
396
  }
361
397
  return failures;
362
398
  };
363
- const scanTokenOwnedProcesses = async (processes, runId, host, captureBaseline) => {
399
+ const TOKEN_READ_RETRY_ATTEMPTS = 3;
400
+ const TOKEN_READ_RETRY_DELAY_MS = 40;
401
+ /** Darwin reports this when the kernel withholds a platform binary's environment. */
402
+ const PLATFORM_BINARY_ENVIRONMENT_WITHHELD = "platform_binary_environment_withheld";
403
+ const currentProcessUid = () => typeof process.getuid === "function" ? process.getuid() : undefined;
404
+ /**
405
+ * Whether the operating system provably withheld this candidate's run token:
406
+ * current macOS omits the environment of Apple platform binaries from
407
+ * KERN_PROCARGS2 and fails with EINVAL for processes owned by another
408
+ * account, and neither class can be token-verified by any caller. Such a
409
+ * candidate is recorded as unverified instead of failing an otherwise clean
410
+ * capture; every other unreadable candidate stays fail-closed.
411
+ */
412
+ const environmentWithheldFromInspection = (entry, diagnostic) => {
413
+ const uid = currentProcessUid();
414
+ if (uid !== undefined && entry.uid !== undefined && entry.uid !== uid)
415
+ return true;
416
+ return diagnostic === PLATFORM_BINARY_ENVIRONMENT_WITHHELD;
417
+ };
418
+ const scanTokenOwnedProcesses = async (processes, runId, host, captureBaseline, relation) => {
364
419
  let candidates = processes;
365
420
  let candidateIdentities = new Map();
366
421
  if (host.processIdentities !== undefined) {
@@ -395,58 +450,89 @@ const scanTokenOwnedProcesses = async (processes, runId, host, captureBaseline)
395
450
  }
396
451
  candidates = stableCandidates;
397
452
  }
453
+ const owned = [];
454
+ const ownedIdentities = new Map();
455
+ const recordOwned = (process) => {
456
+ owned.push(process);
457
+ const identity = candidateIdentities.get(process.pid);
458
+ ownedIdentities.set(process.pid, identity?.state === "readable" ? identity.identity : null);
459
+ };
398
460
  let bulkTokens;
399
461
  if (host.runTokens !== undefined)
400
462
  bulkTokens = await host.runTokens(candidates);
401
- const owned = [];
402
- const ownedIdentities = new Map();
463
+ const unreadable = [];
403
464
  for (const process of candidates) {
404
465
  const bulkToken = bulkTokens?.get(process.pid);
405
466
  if (bulkToken !== undefined) {
406
467
  if (bulkToken.state === "readable") {
407
- if (bulkToken.runId === runId) {
408
- owned.push(process);
409
- const identity = candidateIdentities.get(process.pid);
410
- ownedIdentities.set(process.pid, identity?.state === "readable" ? identity.identity : null);
411
- }
468
+ if (bulkToken.runId === runId)
469
+ recordOwned(process);
412
470
  continue;
413
471
  }
414
- if (!(await processIsGone(host, process.pid)))
415
- failures.push({
416
- pid: process.pid,
417
- reason: "environment-unreadable",
418
- diagnostic: bulkToken.reason,
419
- });
472
+ unreadable.push({ process, diagnostic: bulkToken.reason });
420
473
  continue;
421
474
  }
422
475
  try {
423
- if ((await host.environment(process.pid)).REA_PROCESS_RUN_ID === runId) {
424
- owned.push(process);
425
- const identity = candidateIdentities.get(process.pid);
426
- ownedIdentities.set(process.pid, identity?.state === "readable" ? identity.identity : null);
427
- }
476
+ if ((await host.environment(process.pid)).REA_PROCESS_RUN_ID === runId)
477
+ recordOwned(process);
428
478
  }
429
479
  catch (cause) {
430
- try {
431
- const live = liveProcesses(await host.listProcesses());
432
- if (!live.some(({ pid }) => pid === process.pid))
433
- continue;
434
- }
435
- catch (recheckCause) {
436
- failures.push({
437
- pid: process.pid,
438
- reason: "environment-unreadable",
439
- diagnostic: `${errorMessage(cause)}; process liveness recheck failed: ${errorMessage(recheckCause)}`,
440
- });
441
- continue;
442
- }
443
- failures.push({
444
- pid: process.pid,
445
- reason: "environment-unreadable",
446
- diagnostic: errorMessage(cause),
447
- });
480
+ unreadable.push({ process, diagnostic: errorMessage(cause) });
448
481
  }
449
482
  }
483
+ const remaining = await settleUnreadableTokenCandidates(unreadable, runId, host, recordOwned);
484
+ const relatedPids = new Set();
485
+ const relatedGroupIds = new Set();
486
+ if (relation !== undefined) {
487
+ relatedPids.add(relation.leaderPid);
488
+ for (const descendant of descendantsOf(relation.leaderPid, processes))
489
+ relatedPids.add(descendant.pid);
490
+ relatedGroupIds.add(relation.processGroupId);
491
+ for (const processGroupId of relation.sampledProcessGroupIds ?? [])
492
+ if (Number.isSafeInteger(processGroupId) && processGroupId > 0)
493
+ relatedGroupIds.add(processGroupId);
494
+ for (const process of owned)
495
+ relatedGroupIds.add(process.processGroupId);
496
+ }
497
+ const unverified = [];
498
+ const strict = [];
499
+ const withheld = [];
500
+ for (const candidate of remaining) {
501
+ const isRelated = relation === undefined ||
502
+ relatedPids.has(candidate.process.pid) ||
503
+ relatedGroupIds.has(candidate.process.processGroupId);
504
+ if (!isRelated &&
505
+ environmentWithheldFromInspection(candidate.process, candidate.diagnostic)) {
506
+ withheld.push(candidate);
507
+ continue;
508
+ }
509
+ strict.push(candidate);
510
+ }
511
+ let finalLive;
512
+ try {
513
+ finalLive = new Set(liveProcesses(await host.listProcesses()).map(({ pid }) => pid));
514
+ }
515
+ catch (cause) {
516
+ void cause;
517
+ finalLive = undefined;
518
+ }
519
+ for (const candidate of strict) {
520
+ if (finalLive !== undefined && !finalLive.has(candidate.process.pid))
521
+ continue;
522
+ failures.push({
523
+ pid: candidate.process.pid,
524
+ reason: "environment-unreadable",
525
+ diagnostic: candidate.diagnostic,
526
+ });
527
+ }
528
+ for (const candidate of withheld) {
529
+ if (finalLive !== undefined && !finalLive.has(candidate.process.pid))
530
+ continue;
531
+ unverified.push({
532
+ pid: candidate.process.pid,
533
+ diagnostic: candidate.diagnostic,
534
+ });
535
+ }
450
536
  if (candidates.length > 0 && host.processIdentities !== undefined) {
451
537
  const afterRead = await host.processIdentities(candidates);
452
538
  const stillOwned = [];
@@ -469,9 +555,73 @@ const scanTokenOwnedProcesses = async (processes, runId, host, captureBaseline)
469
555
  diagnostic: "process identity changed or became unavailable during token validation",
470
556
  });
471
557
  }
472
- return { owned: stillOwned, failures, identities: ownedIdentities };
558
+ return {
559
+ owned: stillOwned,
560
+ failures,
561
+ unverified,
562
+ identities: ownedIdentities,
563
+ };
564
+ }
565
+ return { owned, failures, unverified, identities: ownedIdentities };
566
+ };
567
+ /**
568
+ * Re-read run tokens for unresolved sweep candidates so transient exec and
569
+ * exit states settle before classification. Each attempt revalidates
570
+ * liveness, refreshes the diagnostic, and is bounded by a fixed attempt
571
+ * budget; a still-unreadable candidate keeps its latest diagnostic.
572
+ */
573
+ const settleUnreadableTokenCandidates = async (pending, runId, host, recordOwned) => {
574
+ let remaining = pending;
575
+ for (let attempt = 0; attempt < TOKEN_READ_RETRY_ATTEMPTS && remaining.length > 0; attempt += 1) {
576
+ await new Promise((resolve) => setTimeout(resolve, TOKEN_READ_RETRY_DELAY_MS));
577
+ let live;
578
+ try {
579
+ live = new Set(liveProcesses(await host.listProcesses()).map(({ pid }) => pid));
580
+ }
581
+ catch (cause) {
582
+ void cause;
583
+ live = undefined;
584
+ }
585
+ let observations;
586
+ if (host.runTokens !== undefined) {
587
+ try {
588
+ observations = await host.runTokens(remaining.map(({ process }) => process));
589
+ }
590
+ catch (cause) {
591
+ void cause;
592
+ observations = undefined;
593
+ }
594
+ }
595
+ const stillUnreadable = [];
596
+ for (const candidate of remaining) {
597
+ const pid = candidate.process.pid;
598
+ if (live !== undefined && !live.has(pid))
599
+ continue;
600
+ const observation = observations?.get(pid) ?? (await readEnvironmentRunToken(host, pid));
601
+ if (observation.state === "readable") {
602
+ if (observation.runId === runId)
603
+ recordOwned(candidate.process);
604
+ continue;
605
+ }
606
+ stillUnreadable.push({
607
+ process: candidate.process,
608
+ diagnostic: observation.reason,
609
+ });
610
+ }
611
+ remaining = stillUnreadable;
612
+ }
613
+ return remaining;
614
+ };
615
+ const readEnvironmentRunToken = async (host, pid) => {
616
+ try {
617
+ return {
618
+ state: "readable",
619
+ runId: (await host.environment(pid)).REA_PROCESS_RUN_ID,
620
+ };
621
+ }
622
+ catch (cause) {
623
+ return { state: "unavailable", reason: errorMessage(cause) };
473
624
  }
474
- return { owned, failures, identities: ownedIdentities };
475
625
  };
476
626
  const processIsGone = async (host, pid) => {
477
627
  try {
@@ -1,4 +1,5 @@
1
1
  import { readFile } from "node:fs/promises";
2
+ import { AnalysisCapabilityUnavailableError } from "../domain/analysisErrorCore.js";
2
3
  import { launcherIdentityFailure } from "./ProcessOwnershipIdentity.js";
3
4
  import { descendantsOf, liveProcesses } from "./ProcessOwnershipProcessTree.js";
4
5
  import { execFileOutput } from "./ExecFileOutput.js";
@@ -133,17 +134,18 @@ export const createSystemProcessOwnershipHost = (platform = process.platform, ho
133
134
  const listProcesses = async (signal) => {
134
135
  if (platform === "win32")
135
136
  return [];
136
- const { stdout } = await execFileOutput("ps", ["-axo", "pid=,ppid=,pgid=,stat=,command="], { env: hostEnvironment, ...(signal === undefined ? {} : { signal }) });
137
+ const { stdout } = await execFileOutput("ps", ["-axo", "pid=,ppid=,pgid=,uid=,stat=,command="], { env: hostEnvironment, ...(signal === undefined ? {} : { signal }) });
137
138
  return stdout
138
139
  .split("\n")
139
- .map((line) => /\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(.*)/u.exec(line))
140
+ .map((line) => /\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(.*)/u.exec(line))
140
141
  .filter((match) => match !== null)
141
142
  .map((match) => ({
142
143
  pid: Number(match[1]),
143
144
  parentPid: Number(match[2]),
144
145
  processGroupId: Number(match[3]),
145
- state: match[4] ?? "",
146
- command: match[5] ?? "",
146
+ uid: Number(match[4]),
147
+ state: match[5] ?? "",
148
+ command: match[6] ?? "",
147
149
  }));
148
150
  };
149
151
  const processIdentities = async (processes, signal) => {
@@ -222,6 +224,19 @@ export const createSystemProcessOwnershipHost = (platform = process.platform, ho
222
224
  platform,
223
225
  prepare: async (signal) => {
224
226
  await darwinTokens?.prepare(signal);
227
+ if (platform === "linux") {
228
+ try {
229
+ const processes = await listProcesses(signal);
230
+ if (!processes.some(({ pid }) => pid === process.pid))
231
+ throw new Error("ps did not report REA's current process");
232
+ }
233
+ catch (cause) {
234
+ if (isExpectedAbort(cause, signal))
235
+ throw cause;
236
+ const reason = `Linux process ownership inspection requires a procps-compatible ps on REA's PATH before launching a child: ${errorMessage(cause)}`;
237
+ throw new AnalysisCapabilityUnavailableError("process-ownership", "prepare_owned_process", reason, { cause, userMessage: reason });
238
+ }
239
+ }
225
240
  },
226
241
  listProcesses,
227
242
  async environment(pid) {
@@ -276,6 +291,36 @@ export const createSystemProcessOwnershipHost = (platform = process.platform, ho
276
291
  };
277
292
  };
278
293
  export const systemProcessOwnershipHost = createSystemProcessOwnershipHost();
294
+ /** Observe the current OS start identity for one live PID, when supported. */
295
+ export const observeProcessStartIdentity = async (pid, host = systemProcessOwnershipHost) => {
296
+ const process = (await host.listProcesses()).find((entry) => entry.pid === pid);
297
+ if (process === undefined || liveProcesses([process]).length === 0)
298
+ return undefined;
299
+ if (host.processIdentities === undefined)
300
+ return { state: "unavailable", reason: "process identity is unsupported" };
301
+ return ((await host.processIdentities([process])).get(pid) ?? {
302
+ state: "unavailable",
303
+ reason: "process identity was not returned",
304
+ });
305
+ };
306
+ /** Revalidate a launch-time start identity immediately before signaling a PID. */
307
+ export const signalProcessWithStartIdentity = async (pid, expectedIdentity, signal, options = {}) => {
308
+ const host = options.host ?? systemProcessOwnershipHost;
309
+ try {
310
+ const observed = await observeProcessStartIdentity(pid, host);
311
+ if (observed === undefined)
312
+ return "gone";
313
+ if (observed.state !== "readable")
314
+ return "unverified";
315
+ if (observed.identity !== expectedIdentity)
316
+ return "identity-changed";
317
+ (options.sendSignal ?? process.kill)(pid, signal);
318
+ return "signaled";
319
+ }
320
+ catch {
321
+ return "unverified";
322
+ }
323
+ };
279
324
  /** Prepare native token inspection before REA launches a captured child. */
280
325
  export const prepareProcessOwnershipInspection = async (signal) => {
281
326
  await systemProcessOwnershipHost.prepare?.(signal);
@@ -83,6 +83,12 @@ export class ProviderProcessSupervisor {
83
83
  #onStderr = (chunk) => {
84
84
  this.#capture("stderr", this.#stderr, chunk);
85
85
  };
86
+ #onStdoutError = (cause) => {
87
+ this.#recordStreamError("stdout", cause);
88
+ };
89
+ #onStderrError = (cause) => {
90
+ this.#recordStreamError("stderr", cause);
91
+ };
86
92
  #onExit = (code, signal) => {
87
93
  this.#recordExit(code, signal);
88
94
  };
@@ -135,9 +141,13 @@ export class ProviderProcessSupervisor {
135
141
  async waitForOutputClose(timeoutMs) {
136
142
  return waitForProcessEvent(this.#outputClosed, this.#outputClose.promise, timeoutMs);
137
143
  }
138
- /** Stop an owned process once; concurrent callers share the same escalation. */
144
+ /** Share each stop attempt; incomplete cleanup retains ownership and permits retry. */
139
145
  stop(options = {}) {
140
- this.#stopPromise ??= this.#stop(options);
146
+ this.#stopPromise ??= this.#stop(options).then((result) => {
147
+ if (result.status === "incomplete")
148
+ this.#stopPromise = undefined;
149
+ return result;
150
+ });
141
151
  return this.#stopPromise;
142
152
  }
143
153
  /** Detach capture and process listeners after an adapter releases ownership. */
@@ -146,12 +156,26 @@ export class ProviderProcessSupervisor {
146
156
  return;
147
157
  this.#disposed = true;
148
158
  this.launch.process.stdout?.off("data", this.#onStdout);
159
+ this.launch.process.stdout?.off("error", this.#onStdoutError);
149
160
  this.launch.process.stderr?.off("data", this.#onStderr);
161
+ this.launch.process.stderr?.off("error", this.#onStderrError);
150
162
  this.launch.process.off("exit", this.#onExit);
151
163
  this.launch.process.off("close", this.#onClose);
152
164
  this.launch.process.off("error", this.#onError);
153
165
  }
154
166
  async #stop(options) {
167
+ let completed = false;
168
+ try {
169
+ const result = await this.#stopWithin(options);
170
+ completed = result.status !== "incomplete";
171
+ return result;
172
+ }
173
+ finally {
174
+ if (completed)
175
+ this.dispose();
176
+ }
177
+ }
178
+ async #stopWithin(options) {
155
179
  try {
156
180
  if (!this.launch.ownsProcessLifetime)
157
181
  return { status: "not-owned" };
@@ -193,9 +217,6 @@ export class ProviderProcessSupervisor {
193
217
  : "owned provider process cleanup failed",
194
218
  };
195
219
  }
196
- finally {
197
- this.dispose();
198
- }
199
220
  }
200
221
  #ownedCleanup(supplied) {
201
222
  if (supplied !== undefined)
@@ -214,10 +235,20 @@ export class ProviderProcessSupervisor {
214
235
  this.launch.process.kill(signal);
215
236
  }
216
237
  #attach(stream, name) {
217
- if (name === "stdout")
238
+ if (name === "stdout") {
218
239
  stream?.on("data", this.#onStdout);
219
- else
240
+ stream?.on("error", this.#onStdoutError);
241
+ }
242
+ else {
220
243
  stream?.on("data", this.#onStderr);
244
+ stream?.on("error", this.#onStderrError);
245
+ }
246
+ }
247
+ #recordStreamError(stream, cause) {
248
+ this.#options.onDiagnostic?.({
249
+ type: "error",
250
+ message: `${stream} stream failed: ${cause.message}`,
251
+ });
221
252
  }
222
253
  #capture(stream, capture, chunk) {
223
254
  const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
@@ -12,14 +12,14 @@ export class ProcessCaptureError extends AnalysisError {
12
12
  cleanupReport;
13
13
  constructor(message, options) {
14
14
  super(message, options);
15
- this.userMessage = options?.userMessage;
15
+ this.userMessage = options?.userMessage ?? message;
16
16
  this.userCategory = options?.userCategory;
17
17
  this.reason =
18
18
  options?.reason ??
19
19
  (options?.userCategory === "cancelled" ? "cancelled" : "capture_failed");
20
20
  this.cleanupIncomplete = this.reason === "cleanup_incomplete";
21
21
  this.cleanupResources = options?.cleanupResources ?? [];
22
- this.executionFailure = options?.executionFailure;
22
+ this.executionFailure = options?.executionFailure ?? message;
23
23
  this.partialObservation = options?.partialObservation;
24
24
  this.cleanupReport = options?.cleanupReport;
25
25
  }