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
@@ -1,6 +1,6 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
- import { createSystemProcessOwnershipHost, errorMessage, } from "./ProcessOwnershipObservation.js";
3
+ import { errorMessage, systemProcessOwnershipHost as systemHost, } from "./ProcessOwnershipObservation.js";
4
4
  import { launcherIdentityFailure } from "./ProcessOwnershipIdentity.js";
5
5
  import { descendantsOf, liveProcesses } from "./ProcessOwnershipProcessTree.js";
6
6
  const execFileAsync = promisify(execFile);
@@ -28,7 +28,6 @@ export const cleanupWindowsProcessTree = async (rootPid, host = systemWindowsPro
28
28
  };
29
29
  }
30
30
  };
31
- const systemHost = createSystemProcessOwnershipHost();
32
31
  const systemWindowsProcessTreeHost = {
33
32
  async terminateTree(rootPid) {
34
33
  try {
@@ -59,7 +58,7 @@ export const cleanupOwnedProcessGroup = async (ownership, host = systemHost) =>
59
58
  return plan;
60
59
  let signaled = false;
61
60
  for (const processGroupId of plan.signalOrder) {
62
- const revalidation = await revalidateOwnedProcessGroup(processGroupId, ownership, host);
61
+ const revalidation = await revalidateOwnedProcessGroup(processGroupId, ownership, host, plan.tokenOwnedIdentities);
63
62
  if ("cleaned" in revalidation)
64
63
  return revalidation;
65
64
  if (revalidation.empty)
@@ -77,17 +76,19 @@ export const cleanupOwnedProcessGroup = async (ownership, host = systemHost) =>
77
76
  };
78
77
  }
79
78
  }
79
+ if (plan.unresolved.length > 0)
80
+ return cleanupValidationFailure(plan.unresolved);
80
81
  return { cleaned: true, signaled };
81
82
  };
82
83
  /** Verify that no live process still carries an owned capture run token. */
83
- export const verifyNoTokenOwnedProcesses = async (runId, host = systemHost) => {
84
+ export const verifyNoTokenOwnedProcesses = async (runId, host = systemHost, captureBaseline) => {
84
85
  const processTable = await readLiveProcessTable(host);
85
86
  if (!processTable.available)
86
87
  return {
87
88
  cleaned: false,
88
89
  reason: `process table could not be inspected: ${processTable.reason}`,
89
90
  };
90
- const scan = await scanTokenOwnedProcesses(processTable.processes, runId, host);
91
+ const scan = await scanTokenOwnedProcesses(processTable.processes, runId, host, captureBaseline);
91
92
  if (scan.failures.length > 0)
92
93
  return cleanupValidationFailure(scan.failures);
93
94
  return scan.owned.length === 0
@@ -106,18 +107,24 @@ const readLiveProcessTable = async (host) => {
106
107
  }
107
108
  };
108
109
  const createOwnedCleanupPlan = async (ownership, processes, host) => {
110
+ const sampledProcessGroupIds = new Set((ownership.sampledProcessGroupIds ?? []).filter((processGroupId) => Number.isSafeInteger(processGroupId) && processGroupId > 0));
109
111
  const tokenOwned = ownership.sweepTokenOwnedProcesses === true
110
- ? await scanTokenOwnedProcesses(processes, ownership.runId, host)
111
- : { owned: [], failures: [] };
112
- if (tokenOwned.failures.length > 0)
113
- return cleanupValidationFailure(tokenOwned.failures);
112
+ ? await scanTokenOwnedProcesses(processes, ownership.runId, host, ownership.captureBaseline)
113
+ : { owned: [], failures: [], identities: new Map() };
114
114
  const tokenOwnedGroupIds = new Set(tokenOwned.owned.map(({ processGroupId }) => processGroupId));
115
115
  const launcher = processes.find(({ pid }) => pid === ownership.leaderPid);
116
116
  const rootMembers = processes.filter(({ processGroupId }) => processGroupId === ownership.processGroupId);
117
117
  if (launcher === undefined &&
118
118
  rootMembers.length === 0 &&
119
- tokenOwned.owned.length === 0)
120
- return { cleaned: true, signaled: false };
119
+ tokenOwned.owned.length === 0 &&
120
+ !processes.some(({ processGroupId }) => sampledProcessGroupIds.has(processGroupId)))
121
+ return tokenOwned.failures.length > 0
122
+ ? {
123
+ signalOrder: [],
124
+ tokenOwnedIdentities: tokenOwned.identities,
125
+ unresolved: tokenOwned.failures,
126
+ }
127
+ : { cleaned: true, signaled: false };
121
128
  let descendants = [];
122
129
  if (launcher !== undefined) {
123
130
  const identityFailure = launcherIdentityFailure(launcher, ownership);
@@ -131,28 +138,61 @@ const createOwnedCleanupPlan = async (ownership, processes, host) => {
131
138
  processGroupIds.add(descendant.processGroupId);
132
139
  for (const process of tokenOwned.owned)
133
140
  processGroupIds.add(process.processGroupId);
141
+ for (const processGroupId of sampledProcessGroupIds)
142
+ processGroupIds.add(processGroupId);
143
+ const signalableGroups = new Set();
144
+ const unresolved = [...tokenOwned.failures];
145
+ const unverifiableSampledGroups = new Set();
134
146
  for (const processGroupId of processGroupIds) {
135
147
  if (processGroupId === ownership.processGroupId)
136
148
  continue;
137
149
  const groupLeader = processes.find(({ pid, processGroupId: observedGroupId }) => pid === processGroupId && observedGroupId === processGroupId);
138
150
  if ((groupLeader === undefined || !descendantPids.has(groupLeader.pid)) &&
139
- !tokenOwnedGroupIds.has(processGroupId))
151
+ !tokenOwnedGroupIds.has(processGroupId) &&
152
+ !sampledProcessGroupIds.has(processGroupId))
140
153
  return {
141
154
  cleaned: false,
142
155
  reason: "descendant process-group leader identity could not be verified",
143
156
  };
157
+ if ((groupLeader === undefined || !descendantPids.has(groupLeader.pid)) &&
158
+ !tokenOwnedGroupIds.has(processGroupId))
159
+ unverifiableSampledGroups.add(processGroupId);
144
160
  }
145
161
  const liveMembers = processes.filter(({ processGroupId }) => processGroupIds.has(processGroupId));
146
- const failures = await processOwnershipFailures(liveMembers, ownership.runId, host);
147
- if (failures.length > 0)
148
- return cleanupValidationFailure(failures);
162
+ for (const processGroupId of processGroupIds) {
163
+ const members = liveMembers.filter(({ processGroupId: observedGroupId }) => observedGroupId === processGroupId);
164
+ if (members.length === 0)
165
+ continue;
166
+ const failures = await processOwnershipFailures(members, ownership.runId, host, tokenOwned.identities);
167
+ if (failures.length === 0 && unverifiableSampledGroups.has(processGroupId))
168
+ unresolved.push(...members.map(({ pid }) => ({
169
+ pid,
170
+ reason: "process-identity-unavailable",
171
+ diagnostic: "sampled process-group leader identity could not be verified",
172
+ })));
173
+ const scanFailures = tokenOwned.failures.filter(({ pid }) => members.some((member) => member.pid === pid));
174
+ if (failures.length > 0)
175
+ unresolved.push(...failures);
176
+ if (failures.length === 0 &&
177
+ scanFailures.length === 0 &&
178
+ !unverifiableSampledGroups.has(processGroupId))
179
+ signalableGroups.add(processGroupId);
180
+ }
181
+ if (ownership.sweepTokenOwnedProcesses !== true && unresolved.length > 0)
182
+ return cleanupValidationFailure(unresolved);
149
183
  return {
150
- signalOrder: [ownership.processGroupId].concat([...processGroupIds]
151
- .filter((processGroupId) => processGroupId !== ownership.processGroupId)
152
- .sort((left, right) => left - right)),
184
+ signalOrder: [...signalableGroups]
185
+ .filter((groupId) => processGroupIds.has(groupId))
186
+ .sort((left, right) => left === ownership.processGroupId
187
+ ? -1
188
+ : right === ownership.processGroupId
189
+ ? 1
190
+ : left - right),
191
+ tokenOwnedIdentities: tokenOwned.identities,
192
+ unresolved,
153
193
  };
154
194
  };
155
- const revalidateOwnedProcessGroup = async (processGroupId, ownership, host) => {
195
+ const revalidateOwnedProcessGroup = async (processGroupId, ownership, host, expectedIdentities) => {
156
196
  let members;
157
197
  try {
158
198
  members = liveProcesses(await host.listProcesses()).filter(({ processGroupId: observedGroupId }) => observedGroupId === processGroupId);
@@ -163,7 +203,7 @@ const revalidateOwnedProcessGroup = async (processGroupId, ownership, host) => {
163
203
  reason: `process ownership could not be revalidated: ${errorMessage(cause)}`,
164
204
  };
165
205
  }
166
- const failures = await processOwnershipFailures(members, ownership.runId, host);
206
+ const failures = await processOwnershipFailures(members, ownership.runId, host, expectedIdentities);
167
207
  if (failures.length > 0)
168
208
  return cleanupValidationFailure(failures);
169
209
  if (processGroupId === ownership.processGroupId) {
@@ -184,15 +224,93 @@ const revalidateOwnedProcessGroup = async (processGroupId, ownership, host) => {
184
224
  };
185
225
  return { empty: members.length === 0 };
186
226
  };
187
- const cleanupValidationFailure = (failures) => ({
188
- cleaned: false,
189
- reason: failures.some(({ reason }) => reason === "run-token-mismatch")
190
- ? "process tree contains an unowned or PID-reused process"
191
- : "process ownership could not be revalidated",
192
- failures,
193
- });
194
- const processOwnershipFailures = async (members, runId, host) => {
227
+ const cleanupValidationFailure = (failures) => {
228
+ const unreadable = failures.filter(({ reason }) => reason === "environment-unreadable");
229
+ if (failures.some(({ reason }) => reason === "run-token-mismatch"))
230
+ return {
231
+ cleaned: false,
232
+ reason: "process tree contains an unowned or PID-reused process",
233
+ failures,
234
+ };
235
+ if (failures.some(({ reason }) => reason === "process-identity-unavailable"))
236
+ return {
237
+ cleaned: false,
238
+ reason: "process identity could not be revalidated",
239
+ failures,
240
+ };
241
+ if (unreadable.length > 0) {
242
+ const counts = new Map();
243
+ for (const { diagnostic } of unreadable) {
244
+ const category = sanitizedTokenReadFailure(diagnostic);
245
+ counts.set(category, (counts.get(category) ?? 0) + 1);
246
+ }
247
+ const breakdown = [...counts]
248
+ .sort(([left], [right]) => left.localeCompare(right))
249
+ .map(([category, count]) => `${category}=${String(count)}`)
250
+ .join(", ");
251
+ return {
252
+ cleaned: false,
253
+ reason: `process ownership token could not be read for ${String(unreadable.length)} live process(es): ${breakdown}`,
254
+ failures,
255
+ };
256
+ }
257
+ return {
258
+ cleaned: false,
259
+ reason: "process ownership could not be revalidated",
260
+ failures,
261
+ };
262
+ };
263
+ const sanitizedTokenReadFailure = (diagnostic) => {
264
+ if (diagnostic === "environment_unavailable")
265
+ return diagnostic;
266
+ const environmentErrno = /^(EACCES|EPERM|ENOENT|ESRCH):/u.exec(diagnostic ?? "");
267
+ if (environmentErrno?.[1] !== undefined)
268
+ return `environment_errno_${environmentErrno[1]}`;
269
+ if (diagnostic === "apple_vector_unavailable")
270
+ return diagnostic;
271
+ if (diagnostic === "ambiguous_environment_boundary")
272
+ return diagnostic;
273
+ if (diagnostic === "malformed_procargs")
274
+ return diagnostic;
275
+ if (diagnostic === "process_identity_changed_during_token_read")
276
+ return diagnostic;
277
+ if (diagnostic === "process_not_in_snapshot")
278
+ return diagnostic;
279
+ const processTableFailure = /^process_table_failed_(\d+)$/u.exec(diagnostic ?? "");
280
+ if (processTableFailure?.[1] !== undefined)
281
+ return `process_table_errno_${processTableFailure[1]}`;
282
+ const sysctlFailure = /^sysctl_failed_(\d+)$/u.exec(diagnostic ?? "");
283
+ if (sysctlFailure?.[1] !== undefined)
284
+ return `sysctl_errno_${sysctlFailure[1]}`;
285
+ if (diagnostic === "reader_failure")
286
+ return diagnostic;
287
+ if (diagnostic === "duplicate_run_token")
288
+ return diagnostic;
289
+ if (diagnostic === "invalid_run_token_encoding")
290
+ return diagnostic;
291
+ return "other_unavailable";
292
+ };
293
+ const processOwnershipFailures = async (members, runId, host, expectedIdentities = new Map()) => {
195
294
  const failures = [];
295
+ if (expectedIdentities.size > 0 && host.processIdentities !== undefined) {
296
+ const expectedMembers = members.filter(({ pid }) => expectedIdentities.has(pid));
297
+ const observed = await host.processIdentities(expectedMembers);
298
+ for (const member of expectedMembers) {
299
+ const expected = expectedIdentities.get(member.pid);
300
+ const current = observed.get(member.pid);
301
+ if (expected === null ||
302
+ current?.state !== "readable" ||
303
+ current.identity !== expected) {
304
+ if (await processIsGone(host, member.pid))
305
+ continue;
306
+ failures.push({
307
+ pid: member.pid,
308
+ reason: "process-identity-unavailable",
309
+ diagnostic: "process identity changed or became unavailable before signal",
310
+ });
311
+ }
312
+ }
313
+ }
196
314
  for (const member of members) {
197
315
  try {
198
316
  if ((await host.environment(member.pid)).REA_PROCESS_RUN_ID !== runId)
@@ -219,15 +337,94 @@ const processOwnershipFailures = async (members, runId, host) => {
219
337
  });
220
338
  }
221
339
  }
340
+ if (failures.length === 0 &&
341
+ expectedIdentities.size > 0 &&
342
+ host.processIdentities !== undefined) {
343
+ const expectedMembers = members.filter(({ pid }) => expectedIdentities.has(pid));
344
+ const observed = await host.processIdentities(expectedMembers);
345
+ for (const member of expectedMembers) {
346
+ const expected = expectedIdentities.get(member.pid);
347
+ const current = observed.get(member.pid);
348
+ if (expected !== null &&
349
+ current?.state === "readable" &&
350
+ current.identity === expected)
351
+ continue;
352
+ if (await processIsGone(host, member.pid))
353
+ continue;
354
+ failures.push({
355
+ pid: member.pid,
356
+ reason: "process-identity-unavailable",
357
+ diagnostic: "process identity changed or became unavailable during token validation",
358
+ });
359
+ }
360
+ }
222
361
  return failures;
223
362
  };
224
- const scanTokenOwnedProcesses = async (processes, runId, host) => {
225
- const owned = [];
363
+ const scanTokenOwnedProcesses = async (processes, runId, host, captureBaseline) => {
364
+ let candidates = processes;
365
+ let candidateIdentities = new Map();
366
+ if (host.processIdentities !== undefined) {
367
+ const current = await host.processIdentities(processes);
368
+ candidateIdentities = current;
369
+ if (captureBaseline !== undefined) {
370
+ const baseline = new Map(captureBaseline.map(({ pid, identity }) => [pid, identity]));
371
+ candidates = processes.filter((process) => {
372
+ const before = baseline.get(process.pid);
373
+ const now = current.get(process.pid);
374
+ return !(before !== undefined &&
375
+ before !== null &&
376
+ now?.state === "readable" &&
377
+ now.identity === before);
378
+ });
379
+ }
380
+ }
226
381
  const failures = [];
227
- for (const process of processes) {
382
+ if (captureBaseline !== undefined && host.processIdentities !== undefined) {
383
+ const stableCandidates = [];
384
+ for (const process of candidates) {
385
+ if (candidateIdentities.get(process.pid)?.state === "readable") {
386
+ stableCandidates.push(process);
387
+ continue;
388
+ }
389
+ if (!(await processIsGone(host, process.pid)))
390
+ failures.push({
391
+ pid: process.pid,
392
+ reason: "process-identity-unavailable",
393
+ diagnostic: "process identity was unavailable during token scan",
394
+ });
395
+ }
396
+ candidates = stableCandidates;
397
+ }
398
+ let bulkTokens;
399
+ if (host.runTokens !== undefined)
400
+ bulkTokens = await host.runTokens(candidates);
401
+ const owned = [];
402
+ const ownedIdentities = new Map();
403
+ for (const process of candidates) {
404
+ const bulkToken = bulkTokens?.get(process.pid);
405
+ if (bulkToken !== undefined) {
406
+ 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
+ }
412
+ continue;
413
+ }
414
+ if (!(await processIsGone(host, process.pid)))
415
+ failures.push({
416
+ pid: process.pid,
417
+ reason: "environment-unreadable",
418
+ diagnostic: bulkToken.reason,
419
+ });
420
+ continue;
421
+ }
228
422
  try {
229
- if ((await host.environment(process.pid)).REA_PROCESS_RUN_ID === runId)
423
+ if ((await host.environment(process.pid)).REA_PROCESS_RUN_ID === runId) {
230
424
  owned.push(process);
425
+ const identity = candidateIdentities.get(process.pid);
426
+ ownedIdentities.set(process.pid, identity?.state === "readable" ? identity.identity : null);
427
+ }
231
428
  }
232
429
  catch (cause) {
233
430
  try {
@@ -250,5 +447,38 @@ const scanTokenOwnedProcesses = async (processes, runId, host) => {
250
447
  });
251
448
  }
252
449
  }
253
- return { owned, failures };
450
+ if (candidates.length > 0 && host.processIdentities !== undefined) {
451
+ const afterRead = await host.processIdentities(candidates);
452
+ const stillOwned = [];
453
+ const ownedPids = new Set(owned.map(({ pid }) => pid));
454
+ for (const process of candidates) {
455
+ const before = candidateIdentities.get(process.pid);
456
+ const current = afterRead.get(process.pid);
457
+ if (before?.state === "readable" &&
458
+ current?.state === "readable" &&
459
+ current.identity === before.identity) {
460
+ if (ownedPids.has(process.pid))
461
+ stillOwned.push(process);
462
+ continue;
463
+ }
464
+ if (await processIsGone(host, process.pid))
465
+ continue;
466
+ failures.push({
467
+ pid: process.pid,
468
+ reason: "process-identity-unavailable",
469
+ diagnostic: "process identity changed or became unavailable during token validation",
470
+ });
471
+ }
472
+ return { owned: stillOwned, failures, identities: ownedIdentities };
473
+ }
474
+ return { owned, failures, identities: ownedIdentities };
475
+ };
476
+ const processIsGone = async (host, pid) => {
477
+ try {
478
+ return !liveProcesses(await host.listProcesses()).some((process) => process.pid === pid);
479
+ }
480
+ catch (cause) {
481
+ void cause;
482
+ return false;
483
+ }
254
484
  };
@@ -2,6 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import { launcherIdentityFailure } from "./ProcessOwnershipIdentity.js";
3
3
  import { descendantsOf, liveProcesses } from "./ProcessOwnershipProcessTree.js";
4
4
  import { execFileOutput } from "./ExecFileOutput.js";
5
+ import { createDarwinProcessRunTokenReader } from "./DarwinProcessRunTokenReader.js";
5
6
  /** Observe one group without signaling it, failing closed on identity doubt. */
6
7
  export const observeOwnedProcessGroupWithHost = async (ownership, host) => {
7
8
  let members;
@@ -105,13 +106,34 @@ export const parseProcessEnvironment = (value) => Object.fromEntries(value
105
106
  const separator = entry.indexOf("=");
106
107
  return [entry.slice(0, separator), entry.slice(separator + 1)];
107
108
  }));
109
+ /** Read Linux stat field 22, tolerating spaces and closing parentheses in comm. */
110
+ export const parseLinuxProcessStartTime = (value) => {
111
+ const commandEnd = value.lastIndexOf(")");
112
+ if (commandEnd < 0)
113
+ return undefined;
114
+ const fields = value
115
+ .slice(commandEnd + 1)
116
+ .trim()
117
+ .split(/\s+/u);
118
+ const startTime = fields[19];
119
+ return startTime !== undefined && /^\d+$/u.test(startTime)
120
+ ? startTime
121
+ : undefined;
122
+ };
123
+ const isExpectedAbort = (cause, signal) => signal?.aborted === true &&
124
+ (cause === signal.reason ||
125
+ (cause instanceof Error && cause.name === "AbortError"));
108
126
  /** Create the operating-system process inspector for an explicit host context. */
109
- export const createSystemProcessOwnershipHost = (platform = process.platform, hostEnvironment = process.env) => ({
110
- platform,
111
- async listProcesses() {
127
+ export const createSystemProcessOwnershipHost = (platform = process.platform, hostEnvironment = process.env, options = {}) => {
128
+ const darwinTokens = platform === "darwin"
129
+ ? createDarwinProcessRunTokenReader(options.darwinXcrun === undefined
130
+ ? {}
131
+ : { xcrun: options.darwinXcrun })
132
+ : undefined;
133
+ const listProcesses = async (signal) => {
112
134
  if (platform === "win32")
113
135
  return [];
114
- const { stdout } = await execFileOutput("ps", ["-axo", "pid=,ppid=,pgid=,stat=,command="], { env: hostEnvironment });
136
+ const { stdout } = await execFileOutput("ps", ["-axo", "pid=,ppid=,pgid=,stat=,command="], { env: hostEnvironment, ...(signal === undefined ? {} : { signal }) });
115
137
  return stdout
116
138
  .split("\n")
117
139
  .map((line) => /\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(.*)/u.exec(line))
@@ -123,28 +145,143 @@ export const createSystemProcessOwnershipHost = (platform = process.platform, ho
123
145
  state: match[4] ?? "",
124
146
  command: match[5] ?? "",
125
147
  }));
126
- },
127
- async environment(pid) {
148
+ };
149
+ const processIdentities = async (processes, signal) => {
150
+ if (processes.length === 0)
151
+ return new Map();
128
152
  if (platform === "linux")
129
- return parseProcessEnvironment(await readFile(`/proc/${pid}/environ`, "utf8"));
130
- const { stdout } = await execFileOutput("ps", ["eww", "-p", String(pid)], {
131
- env: hostEnvironment,
132
- });
133
- const observedEnvironment = {};
134
- for (const match of stdout.matchAll(/(?:^|\s)([A-Za-z_][A-Za-z0-9_]*)=([^\s]*)/gu)) {
135
- const name = match[1];
136
- if (name !== undefined)
137
- observedEnvironment[name] = match[2] ?? "";
153
+ return new Map(await Promise.all(processes.map(async ({ pid }) => {
154
+ try {
155
+ const stat = await readFile(`/proc/${String(pid)}/stat`, {
156
+ encoding: "utf8",
157
+ ...(signal === undefined ? {} : { signal }),
158
+ });
159
+ const identity = parseLinuxProcessStartTime(stat);
160
+ return [
161
+ pid,
162
+ identity === undefined
163
+ ? {
164
+ state: "unavailable",
165
+ reason: "malformed_proc_stat",
166
+ }
167
+ : {
168
+ state: "readable",
169
+ identity: `linux-starttime:${identity}`,
170
+ },
171
+ ];
172
+ }
173
+ catch (cause) {
174
+ if (isExpectedAbort(cause, signal))
175
+ throw cause;
176
+ return [
177
+ pid,
178
+ { state: "unavailable", reason: errorMessage(cause) },
179
+ ];
180
+ }
181
+ })));
182
+ if (darwinTokens === undefined)
183
+ return new Map();
184
+ try {
185
+ return await darwinTokens.identities(processes, signal);
138
186
  }
139
- return observedEnvironment;
140
- },
141
- signalGroup(processGroupId, signal) {
142
- process.kill(-processGroupId, signal);
143
- },
144
- });
145
- const systemHost = createSystemProcessOwnershipHost();
187
+ catch (cause) {
188
+ if (isExpectedAbort(cause, signal))
189
+ throw cause;
190
+ const reason = errorMessage(cause);
191
+ return new Map(processes.map(({ pid }) => [
192
+ pid,
193
+ { state: "unavailable", reason },
194
+ ]));
195
+ }
196
+ };
197
+ const captureBaseline = async (signal) => {
198
+ if (platform !== "linux" && darwinTokens === undefined)
199
+ return [];
200
+ const processes = liveProcesses(await listProcesses(signal));
201
+ let identities = await processIdentities(processes, signal);
202
+ const unreadable = processes.filter(({ pid }) => identities.get(pid)?.state !== "readable");
203
+ if (unreadable.length > 0) {
204
+ const stillLive = liveProcesses(await listProcesses(signal)).filter(({ pid }) => unreadable.some((entry) => entry.pid === pid));
205
+ const retried = await processIdentities(stillLive, signal);
206
+ identities = new Map([...identities, ...retried]);
207
+ const unresolved = stillLive.filter(({ pid }) => identities.get(pid)?.state !== "readable");
208
+ const liveAfterRetry = liveProcesses(await listProcesses(signal));
209
+ const stillUnresolved = unresolved.filter(({ pid }) => liveAfterRetry.some((entry) => entry.pid === pid));
210
+ if (stillUnresolved.length > 0)
211
+ throw new Error(`process identity snapshot is unavailable for ${String(stillUnresolved.length)} live processes`);
212
+ }
213
+ return processes.map(({ pid }) => {
214
+ const observation = identities.get(pid);
215
+ return {
216
+ pid,
217
+ identity: observation?.state === "readable" ? observation.identity : null,
218
+ };
219
+ });
220
+ };
221
+ return {
222
+ platform,
223
+ prepare: async (signal) => {
224
+ await darwinTokens?.prepare(signal);
225
+ },
226
+ listProcesses,
227
+ async environment(pid) {
228
+ if (platform === "linux")
229
+ return parseProcessEnvironment(await readFile(`/proc/${pid}/environ`, "utf8"));
230
+ if (platform === "darwin") {
231
+ const process = (await listProcesses()).find((entry) => entry.pid === pid);
232
+ if (process === undefined)
233
+ throw new Error(`process ${String(pid)} is not live`);
234
+ const observation = (await darwinTokens?.read([process]))?.get(pid);
235
+ if (observation === undefined || observation.state === "unavailable")
236
+ throw new Error(observation?.reason ?? "process run token could not be read");
237
+ return observation.runId === undefined
238
+ ? {}
239
+ : { REA_PROCESS_RUN_ID: observation.runId };
240
+ }
241
+ const { stdout } = await execFileOutput("ps", ["eww", "-p", String(pid)], {
242
+ env: hostEnvironment,
243
+ });
244
+ const observedEnvironment = {};
245
+ for (const match of stdout.matchAll(/(?:^|\s)([A-Za-z_][A-Za-z0-9_]*)=([^\s]*)/gu)) {
246
+ const name = match[1];
247
+ if (name !== undefined)
248
+ observedEnvironment[name] = match[2] ?? "";
249
+ }
250
+ return observedEnvironment;
251
+ },
252
+ async runTokens(processes) {
253
+ if (platform !== "darwin" || processes.length === 0)
254
+ return new Map();
255
+ try {
256
+ return (await darwinTokens?.read(processes)) ?? new Map();
257
+ }
258
+ catch (cause) {
259
+ void cause;
260
+ return new Map(processes.map(({ pid }) => [
261
+ pid,
262
+ { state: "unavailable", reason: "reader_failure" },
263
+ ]));
264
+ }
265
+ },
266
+ ...(platform !== "linux" && darwinTokens === undefined
267
+ ? {}
268
+ : {
269
+ processIdentities,
270
+ captureBaseline,
271
+ ...(darwinTokens === undefined ? {} : { close: darwinTokens.close }),
272
+ }),
273
+ signalGroup(processGroupId, signal) {
274
+ process.kill(-processGroupId, signal);
275
+ },
276
+ };
277
+ };
278
+ export const systemProcessOwnershipHost = createSystemProcessOwnershipHost();
279
+ /** Prepare native token inspection before REA launches a captured child. */
280
+ export const prepareProcessOwnershipInspection = async (signal) => {
281
+ await systemProcessOwnershipHost.prepare?.(signal);
282
+ };
146
283
  /** Observe one group without signaling it, failing closed on identity doubt. */
147
- export const observeOwnedProcessGroup = async (ownership, host = systemHost) => observeOwnedProcessGroupWithHost(ownership, host);
284
+ export const observeOwnedProcessGroup = async (ownership, host = systemProcessOwnershipHost) => observeOwnedProcessGroupWithHost(ownership, host);
148
285
  /**
149
286
  * Record the live launcher and descendant lineage after run-token validation.
150
287
  *
@@ -152,7 +289,7 @@ export const observeOwnedProcessGroup = async (ownership, host = systemHost) =>
152
289
  * list means no descendants were live during this observation, not that the
153
290
  * run never created a short-lived child.
154
291
  */
155
- export const observeOwnedProcessLineage = async (ownership, host = systemHost) => observeOwnedProcessLineageWithHost(ownership, host);
292
+ export const observeOwnedProcessLineage = async (ownership, host = systemProcessOwnershipHost) => observeOwnedProcessLineageWithHost(ownership, host);
156
293
  const processIsGone = async (host, pid) => {
157
294
  try {
158
295
  return !liveProcesses(await host.listProcesses()).some((process) => process.pid === pid);