rea-agents 3.0.0 → 3.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 (219) hide show
  1. package/README.md +14 -16
  2. package/dist/application/AnalysisContextQueries.js +21 -9
  3. package/dist/application/ApplicationWorkflowEvidenceResolver.js +19 -11
  4. package/dist/application/ArtifactInventory/policy.js +17 -0
  5. package/dist/application/ArtifactInventory/reader.js +1 -4
  6. package/dist/application/ArtifactInventory/scanCanonical.js +0 -3
  7. package/dist/application/ArtifactInventory/scanReader.js +7 -7
  8. package/dist/application/ArtifactInventory/types.js +1 -5
  9. package/dist/application/BinarySession.js +14 -16
  10. package/dist/application/BinarySessionRecords.js +31 -5
  11. package/dist/application/BinarySessionStatus.js +11 -5
  12. package/dist/application/BrowserEvidence.js +3 -5
  13. package/dist/application/CapabilityClientRequirements.js +26 -0
  14. package/dist/application/CapabilityInventory.js +134 -63
  15. package/dist/application/CrossVersionInventory.js +7 -6
  16. package/dist/application/ElectronActiveEvidence.js +55 -0
  17. package/dist/application/ElectronActiveObservationPort.js +1 -0
  18. package/dist/application/ElectronActiveObservationService.js +81 -0
  19. package/dist/application/EnhancedTools.js +22 -20
  20. package/dist/application/EvidenceBundleCommands.js +5 -1
  21. package/dist/application/EvidenceLedger.js +8 -1
  22. package/dist/application/JavaScriptReplayPermission.js +1 -1
  23. package/dist/application/JavaScriptReplayService.js +23 -15
  24. package/dist/application/ManagedRuntimeCorrelationService.js +9 -5
  25. package/dist/application/NodeRuntimeCharacterizationService.js +8 -9
  26. package/dist/application/ProcessCaptureLifecycle.js +5 -0
  27. package/dist/application/ProcessCli.js +2 -1
  28. package/dist/application/ProcessHarness.js +8 -3
  29. package/dist/application/ProcessNormalization.js +2 -1
  30. package/dist/application/ProjectPermissionStore.js +1 -0
  31. package/dist/application/ReferenceSourceImport.js +2 -2
  32. package/dist/application/SetupClientConfiguration.js +6 -1
  33. package/dist/application/WindowsCapabilities.js +5 -4
  34. package/dist/artifacts/ArtifactProvider.js +8 -10
  35. package/dist/artifacts/SafeOutputTree.js +7 -8
  36. package/dist/browser/CdpCaptureStorageFingerprints.js +10 -7
  37. package/dist/browser/CdpElectronInspection.js +3 -1
  38. package/dist/browser/JavaScriptRuntimeScope.js +5 -0
  39. package/dist/browser/PlaywrightElectronActiveActions.js +324 -0
  40. package/dist/browser/PlaywrightElectronActiveProvider.js +294 -0
  41. package/dist/browser/PlaywrightScenarioBrowser.js +4 -4
  42. package/dist/browser/PlaywrightScenarioSession.js +4 -4
  43. package/dist/browser/SensitiveTextCapture.js +1 -1
  44. package/dist/browser/V8InspectorCaptureProjection.js +1 -0
  45. package/dist/browser/V8InspectorProvider.js +3 -3
  46. package/dist/catalogIdentity.js +12 -6
  47. package/dist/cli/managedCommands.js +1 -1
  48. package/dist/cliApplicationCommands.js +2 -2
  49. package/dist/cliBrowserAdvancedCommands.js +11 -5
  50. package/dist/cliBrowserCommands.js +14 -7
  51. package/dist/cliCommandNames.js +1 -0
  52. package/dist/cliElectronCommands.js +46 -4
  53. package/dist/cliJavaScriptRuntimeCommands.js +14 -6
  54. package/dist/cliObservationOptions.js +4 -0
  55. package/dist/cliPolicyCommands.js +1 -0
  56. package/dist/config/browserScenario.js +77 -0
  57. package/dist/config/electronAutomation.js +35 -0
  58. package/dist/config/environment.js +6 -0
  59. package/dist/config/javascriptReplay.js +41 -0
  60. package/dist/config/managedRuntime.js +25 -0
  61. package/dist/config/parseConfig.js +56 -200
  62. package/dist/config/parsers.js +10 -1
  63. package/dist/config/passiveObservation.js +157 -0
  64. package/dist/config/processCapture.js +47 -0
  65. package/dist/contracts/applicationToolContracts.js +2 -5
  66. package/dist/contracts/applicationWorkflowInputContracts.js +86 -96
  67. package/dist/contracts/artifactToolContracts.js +27 -34
  68. package/dist/contracts/browserToolContracts.js +2 -2
  69. package/dist/contracts/electronToolContracts.js +30 -1
  70. package/dist/contracts/enhancedInputs.js +13 -29
  71. package/dist/contracts/functionWorkflowToolContracts.js +2 -3
  72. package/dist/contracts/managedToolContracts.js +4 -9
  73. package/dist/contracts/managedWorkflowExamples.js +12 -9
  74. package/dist/contracts/managedWorkflowToolContracts.js +30 -27
  75. package/dist/contracts/nativeToolContracts.js +2 -3
  76. package/dist/contracts/sessionToolSchemas.js +61 -0
  77. package/dist/contracts/toolContractExamples.js +1 -0
  78. package/dist/contracts/toolContractHelpers.js +23 -0
  79. package/dist/contracts/toolContracts.js +5 -77
  80. package/dist/contracts/toolEffects.js +15 -0
  81. package/dist/contracts/toolOutputSchemaGroups.js +17 -2
  82. package/dist/contracts/toolOutputSchemaPrimitives.js +76 -26
  83. package/dist/contracts/toolSchemaMetadata.js +128 -0
  84. package/dist/doctorRuntime.js +1 -1
  85. package/dist/domain/androidApplication.js +2 -0
  86. package/dist/domain/appleApplication.js +2 -0
  87. package/dist/domain/binaryTarget.js +7 -5
  88. package/dist/domain/binaryTargetTypes.js +1 -0
  89. package/dist/domain/browserCaptureComparison.js +21 -69
  90. package/dist/domain/browserObservation.js +22 -9
  91. package/dist/domain/bytecodeProvider.js +33 -35
  92. package/dist/domain/callPath.js +3 -2
  93. package/dist/domain/conformanceReplay.js +3 -3
  94. package/dist/domain/conformanceTrustGate.js +151 -16
  95. package/dist/domain/customProtocolCapture.js +3 -3
  96. package/dist/domain/electronActiveObservation.js +277 -0
  97. package/dist/domain/electronObservation.js +15 -11
  98. package/dist/domain/electronStaticAnalysisValues.js +4 -1
  99. package/dist/domain/eventProcessTree.js +82 -79
  100. package/dist/domain/functionComparisonNormalization.js +15 -8
  101. package/dist/domain/functionDossierEvidence.js +9 -4
  102. package/dist/domain/hopperValues.js +68 -71
  103. package/dist/domain/inputIssueProjection.js +4 -1
  104. package/dist/domain/investigationWorkspace.js +16 -15
  105. package/dist/domain/javascriptExportShapeComparisonSchemas.js +2 -2
  106. package/dist/domain/javascriptReplay.js +79 -63
  107. package/dist/domain/javascriptRuntimeObservation.js +1 -0
  108. package/dist/domain/javascriptRuntimeReconciliationActive.js +89 -0
  109. package/dist/domain/javascriptRuntimeReconciliationGraph.js +11 -10
  110. package/dist/domain/javascriptRuntimeReconciliationMatching.js +57 -49
  111. package/dist/domain/javascriptRuntimeReconciliationParsing.js +5 -1
  112. package/dist/domain/javascriptRuntimeReconciliationResult.js +21 -14
  113. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  114. package/dist/domain/knownPageCoverage.js +15 -7
  115. package/dist/domain/nativeApiBoundary.js +15 -7
  116. package/dist/domain/nodeRuntimeCharacterization.js +14 -13
  117. package/dist/domain/processCaptureReactiveValidation.js +8 -3
  118. package/dist/domain/processCaptureValidation.js +7 -3
  119. package/dist/domain/processComparison.js +12 -7
  120. package/dist/domain/processObservation.js +5 -6
  121. package/dist/domain/processReactiveMatching.js +14 -7
  122. package/dist/domain/processReactiveTransition.js +9 -3
  123. package/dist/domain/processScenario.js +6 -3
  124. package/dist/domain/processTraceComparison.js +3 -2
  125. package/dist/domain/protocolCapture.js +8 -6
  126. package/dist/domain/reconstructionReadinessSchemas.js +30 -2
  127. package/dist/domain/referenceSourceImportParsing.js +2 -2
  128. package/dist/domain/replayMachineRuntime.js +5 -3
  129. package/dist/domain/sourceToBundleComparison.js +2 -2
  130. package/dist/domain/sourceToBundleComparisonSchemas.js +8 -8
  131. package/dist/domain/sourceToBundleSignals.js +7 -1
  132. package/dist/domain/staticRuntimeCorrelation.js +7 -5
  133. package/dist/domain/webBundleAnalysis.js +14 -14
  134. package/dist/domain/webCaptureDiff.js +15 -12
  135. package/dist/dotnet/ManagedNativeBoundaryInspector.js +2 -3
  136. package/dist/generatedMcpToolCatalog.js +1 -1
  137. package/dist/generatedPackageMetadata.js +1 -1
  138. package/dist/ghidra/GhidraClient.js +1 -1
  139. package/dist/ghidra/GhidraClientStartup.js +1 -1
  140. package/dist/ghidra/GhidraFunctionValues.js +17 -15
  141. package/dist/ghidra/GhidraInventoryValues.js +57 -47
  142. package/dist/ghidra/GhidraProvider.js +18 -10
  143. package/dist/ghidra/GhidraProviderCapabilities.js +5 -1
  144. package/dist/ghidra/GhidraSocketConnection.js +1 -1
  145. package/dist/ghidra/protocol.js +19 -42
  146. package/dist/hopper/BridgeLauncher.js +3 -1
  147. package/dist/hopper/HopperAnalysisProfile.js +2 -4
  148. package/dist/hopper/HopperCleanup.js +5 -6
  149. package/dist/hopper/HopperClient.js +1 -1
  150. package/dist/hopper/HopperProvider.js +0 -14
  151. package/dist/hopper/LinuxPrivateDisplayProbe.js +16 -10
  152. package/dist/main/reload.js +1 -6
  153. package/dist/main/state.js +0 -14
  154. package/dist/main/transport.js +30 -37
  155. package/dist/main.js +17 -15
  156. package/dist/mcpDoctor.js +2 -24
  157. package/dist/process/PrivateRuntimeRoot.js +17 -0
  158. package/dist/process/ProcessOwnership.js +64 -128
  159. package/dist/process/ProcessOwnershipIdentity.js +13 -0
  160. package/dist/process/ProcessOwnershipObservation.js +88 -0
  161. package/dist/process/ProcessOwnershipProcessTree.js +22 -0
  162. package/dist/process/ProviderDeadline.js +8 -12
  163. package/dist/process/WindowsAuthority.js +32 -0
  164. package/dist/reference/ReferenceSourceReader.js +3 -3
  165. package/dist/reference/ReferenceSourceReaderEntries.js +2 -2
  166. package/dist/replay/JavaScriptReplayWorker.js +95 -11
  167. package/dist/replay/JavaScriptReplayWorkerTypes.js +1 -0
  168. package/dist/replay/LinuxJavaScriptReplayRunner.js +10 -15
  169. package/dist/replay/ReplayOutcome.js +26 -15
  170. package/dist/replay/ReplayWorkerProtocol.js +34 -29
  171. package/dist/server/createServer.js +158 -94
  172. package/dist/server/mcpClientMetadata.js +32 -0
  173. package/dist/server/registerApplicationTools/characterization.js +6 -13
  174. package/dist/server/registerApplicationTools/compareExportShapes.js +2 -7
  175. package/dist/server/registerApplicationTools/compareSourceToBundle.js +2 -7
  176. package/dist/server/registerApplicationTools/compareVersions.js +3 -8
  177. package/dist/server/registerApplicationTools/controlledReplay.js +6 -11
  178. package/dist/server/registerApplicationTools/coverage.js +13 -19
  179. package/dist/server/registerApplicationTools/obligationLedger.js +1 -6
  180. package/dist/server/registerApplicationTools/readiness.js +1 -6
  181. package/dist/server/registerApplicationTools/traceFeature.js +3 -8
  182. package/dist/server/registerApplicationTools/traceSemantics.js +1 -6
  183. package/dist/server/registerArtifactComparisonTool.js +12 -16
  184. package/dist/server/registerBrowserScenarioTool.js +1 -6
  185. package/dist/server/registerBrowserTools.js +37 -79
  186. package/dist/server/registerBundleComparisonTool.js +10 -14
  187. package/dist/server/registerCloseLifecycleTool.js +6 -14
  188. package/dist/server/registerElectronTools.js +30 -47
  189. package/dist/server/registerEnhancedTools.js +114 -122
  190. package/dist/server/registerEvidenceTools.js +5 -9
  191. package/dist/server/registerFunctionComparisonTool.js +11 -15
  192. package/dist/server/registerInvestigationTools.js +41 -57
  193. package/dist/server/registerJavaScriptRuntimeObservationTools.js +13 -27
  194. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +4 -9
  195. package/dist/server/registerManagedWorkflowTools/contract.js +2 -3
  196. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +3 -8
  197. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +3 -8
  198. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +8 -13
  199. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +4 -9
  200. package/dist/server/registerOfficialTools.js +8 -9
  201. package/dist/server/registerProcessComparisonTool.js +19 -24
  202. package/dist/server/registerReplayMachineTool.js +4 -10
  203. package/dist/server/registerSessionRecordTools.js +69 -40
  204. package/dist/server/registerSessionStatusTool.js +13 -28
  205. package/dist/server/registerSessionTools.js +36 -52
  206. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  207. package/dist/server/sessionToolPolicies.js +1 -5
  208. package/dist/server/toolRegistrationOptions.js +4 -192
  209. package/dist/server/toolResult.js +2 -18
  210. package/package.json +22 -14
  211. package/scripts/electron-active-hook-boundaries.cjs +394 -0
  212. package/scripts/electron-active-hook.cjs +501 -0
  213. package/scripts/rea.mjs +1 -11
  214. package/skills/reverse-engineer-anything/SKILL.md +2 -2
  215. package/dist/server/DynamicToolAvailability.js +0 -70
  216. package/dist/server/LazyToolCatalog.js +0 -134
  217. package/dist/server/hydrateServerTools.js +0 -191
  218. package/dist/server/toolInputValidation.js +0 -77
  219. package/scripts/package-runner-bootstrap.mjs +0 -72
@@ -1,28 +1,30 @@
1
1
  import { createServer, } from "../server/createServer.js";
2
2
  import { MCP_CONNECTION_LOST, MCP_CONNECTION_START_FAILED, } from "./messages.js";
3
3
  export const startMcpTransport = async (dependencies, session, serverContext) => {
4
- const liveServers = new Set();
5
4
  const { serverLogger } = serverContext;
5
+ let optionalProviders = {};
6
+ try {
7
+ optionalProviders = await serverContext.loadOptionalProviders();
8
+ }
9
+ catch {
10
+ serverLogger.warn("Optional MCP providers could not load; affected tools remain unavailable");
11
+ }
6
12
  let handle;
7
13
  try {
8
- handle = dependencies.serve(() => {
9
- const server = (dependencies.createServer ?? createServer)(session, session, {
10
- logger: serverContext.logger,
11
- processPolicy: serverContext.runtimeState.processPolicy,
12
- evidenceFilePolicy: serverContext.runtimeState.evidencePolicy,
13
- investigationInputRoots: serverContext.runtimeState.investigationRoots,
14
- analysisSnapshotFilePolicy: serverContext.runtimeState.snapshotPolicy,
15
- permissionAuthority: serverContext.permissionAuthority,
16
- loadOptionalProviders: serverContext.loadOptionalProviders,
17
- artifactIntegrityContinueEnabled: () => serverContext.runtimeState.currentConfig
18
- .artifactIntegrityContinueEnabled,
19
- javascriptReplayPolicy: serverContext.runtimeState.javascriptReplayPolicy,
20
- managedRuntimePolicy: serverContext.runtimeState.managedRuntimePolicy,
21
- availabilityPolicy: () => runtimeAvailability(serverContext.runtimeState),
22
- });
23
- liveServers.add(server);
24
- return server;
25
- }, {
14
+ handle = dependencies.serve(() => (dependencies.createServer ?? createServer)(session, session, {
15
+ logger: serverContext.logger,
16
+ processPolicy: () => serverContext.runtimeState.currentConfig.processExecutionPolicy,
17
+ evidenceFilePolicy: serverContext.runtimeState.evidencePolicy,
18
+ investigationInputRoots: serverContext.runtimeState.investigationRoots,
19
+ analysisSnapshotFilePolicy: serverContext.runtimeState.snapshotPolicy,
20
+ permissionAuthority: serverContext.permissionAuthority,
21
+ ...optionalProviders,
22
+ artifactIntegrityContinueEnabled: () => serverContext.runtimeState.currentConfig
23
+ .artifactIntegrityContinueEnabled,
24
+ javascriptReplayPolicy: () => serverContext.runtimeState.currentConfig.javascriptReplayPolicy,
25
+ managedRuntimePolicy: () => serverContext.runtimeState.currentConfig.managedRuntimePolicy,
26
+ availabilityPolicy: () => runtimeAvailability(serverContext.runtimeState),
27
+ }), {
26
28
  onerror: () => {
27
29
  serverLogger.error(MCP_CONNECTION_LOST);
28
30
  dependencies.writeStderr(`${MCP_CONNECTION_LOST}\n`);
@@ -35,26 +37,17 @@ export const startMcpTransport = async (dependencies, session, serverContext) =>
35
37
  dependencies.writeStderr(`${MCP_CONNECTION_START_FAILED}\n`);
36
38
  return { ok: false };
37
39
  }
38
- return { ok: true, handle, liveServers };
40
+ return { ok: true, handle };
39
41
  };
40
42
  const runtimeAvailability = (state) => ({
41
- processCaptureEnabled: state.currentConfig.processExecutionPolicy.enabled,
43
+ processCaptureEnabled: state.currentConfig.processExecutionPolicy.status === "enabled",
42
44
  evidenceFileRoots: state.currentConfig.evidenceFilePolicy.roots.length,
43
45
  investigationInputRoots: state.currentConfig.investigationInputRoots.length,
44
- browserObservationEnabled: state.currentConfig.browserObservationEnabled &&
45
- state.currentConfig.browserCdpEndpoints.length > 0 &&
46
- state.currentConfig.browserAllowedOrigins.length > 0,
47
- browserScenarioEnabled: state.currentConfig.browserScenarioPolicy.enabled &&
48
- state.currentConfig.browserScenarioPolicy.allowedOrigins.length > 0 &&
49
- (state.currentConfig.browserScenarioPolicy.executableRoots.length > 0 ||
50
- state.currentConfig.browserScenarioPolicy.cdpEndpoints.length > 0),
51
- electronObservationEnabled: state.currentConfig.electronObservationEnabled &&
52
- state.currentConfig.electronCdpEndpoints.length > 0 &&
53
- state.currentConfig.electronFileRoots.length > 0,
54
- v8InspectorObservationEnabled: state.currentConfig.v8InspectorObservationEnabled &&
55
- state.currentConfig.v8InspectorEndpoints.length > 0 &&
56
- (state.currentConfig.v8InspectorFileRoots.length > 0 ||
57
- state.currentConfig.v8InspectorAllowedOrigins.length > 0),
58
- javascriptReplayEnabled: state.currentConfig.javascriptReplayPolicy.enabled,
59
- managedRuntimeEnabled: state.currentConfig.managedRuntimePolicy.enabled,
46
+ browserObservationEnabled: state.currentConfig.browserObservationPolicy.status === "enabled",
47
+ browserScenarioEnabled: state.currentConfig.browserScenarioPolicy.status === "enabled",
48
+ electronObservationEnabled: state.currentConfig.electronObservationPolicy.status === "enabled",
49
+ electronAutomationEnabled: state.currentConfig.electronAutomationPolicy.status === "enabled",
50
+ v8InspectorObservationEnabled: state.currentConfig.v8InspectorObservationPolicy.status === "enabled",
51
+ javascriptReplayEnabled: state.currentConfig.javascriptReplayPolicy.status === "enabled",
52
+ managedRuntimeEnabled: state.currentConfig.managedRuntimePolicy.status === "enabled",
60
53
  });
package/dist/main.js CHANGED
@@ -63,20 +63,23 @@ export const run = async (dependencies = runtimeDependencies()) => {
63
63
  const transport = await startMcpTransport(dependencies, session, {
64
64
  logger,
65
65
  serverLogger,
66
- loadOptionalProviders: async () => {
67
- const [{ CdpBrowserProvider }, { PlaywrightBrowserScenarioProvider }, { CdpElectronProvider }, { V8InspectorProvider },] = await Promise.all([
68
- import("./browser/CdpBrowserProvider.js"),
69
- import("./browser/PlaywrightBrowserScenarioProvider.js"),
70
- import("./browser/CdpElectronProvider.js"),
71
- import("./browser/V8InspectorProvider.js"),
72
- ]);
73
- return {
74
- browserObservation: new CdpBrowserProvider(),
75
- browserScenarioCapture: new PlaywrightBrowserScenarioProvider(),
76
- electronObservation: new CdpElectronProvider(),
77
- javascriptRuntimeObservation: new V8InspectorProvider(),
78
- };
79
- },
66
+ loadOptionalProviders: dependencies.loadOptionalProviders ??
67
+ (async () => {
68
+ const [{ CdpBrowserProvider }, { PlaywrightBrowserScenarioProvider }, { CdpElectronProvider }, { PlaywrightElectronActiveProvider }, { V8InspectorProvider },] = await Promise.all([
69
+ import("./browser/CdpBrowserProvider.js"),
70
+ import("./browser/PlaywrightBrowserScenarioProvider.js"),
71
+ import("./browser/CdpElectronProvider.js"),
72
+ import("./browser/PlaywrightElectronActiveProvider.js"),
73
+ import("./browser/V8InspectorProvider.js"),
74
+ ]);
75
+ return {
76
+ browserObservation: new CdpBrowserProvider(),
77
+ browserScenarioCapture: new PlaywrightBrowserScenarioProvider(),
78
+ electronObservation: new CdpElectronProvider(),
79
+ electronActiveObservation: new PlaywrightElectronActiveProvider(),
80
+ javascriptRuntimeObservation: new V8InspectorProvider(),
81
+ };
82
+ }),
80
83
  permissionAuthority: permissionAuthority.value,
81
84
  runtimeState,
82
85
  });
@@ -86,7 +89,6 @@ export const run = async (dependencies = runtimeDependencies()) => {
86
89
  dependencies,
87
90
  permissionAuthority: permissionAuthority.value,
88
91
  runtimeState,
89
- liveServers: transport.liveServers,
90
92
  serverLogger,
91
93
  });
92
94
  createShutdown({
package/dist/mcpDoctor.js CHANGED
@@ -4,6 +4,7 @@ import { Formatter } from "incur";
4
4
  import { resolve } from "node:path";
5
5
  import { CATALOG_IDENTITY, MCP_RESOURCE_CATALOG, MCP_RESOURCE_TEMPLATE_CATALOG, } from "./catalogIdentity.js";
6
6
  import { PROMPT_CONTRACTS } from "./contracts/promptContracts.js";
7
+ import { TOOL_CONTRACTS } from "./contracts/toolContracts.js";
7
8
  import { PRODUCT_IDENTITY } from "./identity.js";
8
9
  import { MCP_STARTUP_POLICY } from "./mcpStartupPolicy.js";
9
10
  const STDERR_LIMIT_BYTES = 64 * 1_024;
@@ -63,9 +64,8 @@ const inspectProductionMcpSession = async (client, deadline, signal) => {
63
64
  client.readResource({ uri: "rea://server/identity" }, request),
64
65
  client.callTool({ name: "binary_session", arguments: { detail: "full" } }, request),
65
66
  ]);
66
- const expectedToolNames = availableToolNames(requestFlow.structuredContent) ?? [];
67
67
  const inventory = {
68
- tools: compareInventory(expectedToolNames, tools.tools.map(({ name }) => name)),
68
+ tools: compareInventory(TOOL_CONTRACTS.map(({ name }) => name), tools.tools.map(({ name }) => name)),
69
69
  prompts: compareInventory(PROMPT_CONTRACTS.map(({ name }) => name), prompts.prompts.map(({ name }) => name)),
70
70
  resources: compareInventory(MCP_RESOURCE_CATALOG.map(({ name }) => name), resources.resources.map(({ name }) => name)),
71
71
  resource_templates: compareInventory(MCP_RESOURCE_TEMPLATE_CATALOG.map(({ name }) => name), templates.resourceTemplates.map(({ name }) => name)),
@@ -117,28 +117,6 @@ const inspectProductionMcpSession = async (client, deadline, signal) => {
117
117
  checks,
118
118
  };
119
119
  };
120
- const availableToolNames = (structuredContent) => {
121
- if (typeof structuredContent !== "object" || structuredContent === null)
122
- return undefined;
123
- const result = Reflect.get(structuredContent, "result");
124
- if (typeof result !== "object" || result === null)
125
- return undefined;
126
- const availability = Reflect.get(result, "tool_availability");
127
- if (!Array.isArray(availability))
128
- return undefined;
129
- const names = [];
130
- for (const item of availability) {
131
- if (typeof item !== "object" || item === null)
132
- return undefined;
133
- const name = Reflect.get(item, "name");
134
- const available = Reflect.get(item, "available");
135
- if (typeof name !== "string" || typeof available !== "boolean")
136
- return undefined;
137
- if (available)
138
- names.push(name);
139
- }
140
- return names;
141
- };
142
120
  /**
143
121
  * Dispatcher entry for `rea mcp doctor`, kept separate from Incur MCP setup.
144
122
  * @public
@@ -1,6 +1,19 @@
1
1
  import { chmod, mkdtemp, rm } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
+ import { windowsNativeCapabilities, } from "./WindowsAuthority.js";
5
+ /** Report whether this process can establish a private runtime root. */
6
+ export const privateRuntimeRootCapability = (platform = process.platform) => platform === "win32"
7
+ ? windowsNativeCapabilities(platform).private_runtime_dacl
8
+ : { available: true, reason: null, proof: "posix-mode-0700" };
9
+ /** Expected failure when a runtime root's privacy boundary is unavailable. */
10
+ export class PrivateRuntimeRootUnavailableError extends Error {
11
+ code = "private-runtime-root-authority-unavailable";
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = "PrivateRuntimeRootUnavailableError";
15
+ }
16
+ }
4
17
  /**
5
18
  * Owns one POSIX mode-0700 temporary runtime root and removes it idempotently.
6
19
  *
@@ -16,6 +29,10 @@ export class PrivateRuntimeRoot {
16
29
  }
17
30
  /** Allocate a new private runtime root without performing protocol work. */
18
31
  static async create(options = {}) {
32
+ const platform = options.platform ?? process.platform;
33
+ const capability = privateRuntimeRootCapability(platform);
34
+ if (!capability.available)
35
+ throw new PrivateRuntimeRootUnavailableError(capability.reason);
19
36
  const path = await mkdtemp(join(options.parent ?? tmpdir(), options.prefix ?? "rea-provider-"));
20
37
  try {
21
38
  await chmod(path, 0o700);
@@ -1,13 +1,16 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { promisify } from "node:util";
4
- import { matchesOwnedProcessCommand } from "./ProcessCommandIdentity.js";
4
+ import { observeOwnedProcessGroupWithHost, observeOwnedProcessLineageWithHost, } from "./ProcessOwnershipObservation.js";
5
+ import { launcherIdentityFailure } from "./ProcessOwnershipIdentity.js";
6
+ import { descendantsOf, liveProcesses } from "./ProcessOwnershipProcessTree.js";
5
7
  const execFileAsync = promisify(execFile);
6
8
  /**
7
9
  * Terminate one Windows process tree through the platform utility.
8
10
  *
9
11
  * This is a bounded P0 cleanup mechanism, not Job Object ownership proof. The
10
- * caller-visible Windows limitations must preserve that distinction.
12
+ * caller-visible Windows capability report remains unavailable until a native
13
+ * authority verifies Job Object creation, membership, and cleanup semantics.
11
14
  */
12
15
  export const cleanupWindowsProcessTree = async (rootPid, host = systemWindowsProcessTreeHost) => {
13
16
  if (!Number.isSafeInteger(rootPid) || rootPid <= 0)
@@ -70,8 +73,11 @@ const systemHost = {
70
73
  }
71
74
  const { stdout } = await execFileAsync("ps", ["eww", "-p", String(pid)]);
72
75
  const environment = {};
73
- for (const match of stdout.matchAll(/(?:^|\s)([A-Za-z_][A-Za-z0-9_]*)=([^\s]*)/gu))
74
- environment[match[1]] = match[2] ?? "";
76
+ for (const match of stdout.matchAll(/(?:^|\s)([A-Za-z_][A-Za-z0-9_]*)=([^\s]*)/gu)) {
77
+ const name = match[1];
78
+ if (name !== undefined)
79
+ environment[name] = match[2] ?? "";
80
+ }
75
81
  return environment;
76
82
  },
77
83
  signalGroup(processGroupId, signal) {
@@ -97,13 +103,9 @@ const systemWindowsProcessTreeHost = {
97
103
  export const readProcessRunId = async (pid, host = systemHost) => (await host.environment(pid)).REA_PROCESS_RUN_ID;
98
104
  /** Token-validate every rooted POSIX process group before signaling any. */
99
105
  export const cleanupOwnedProcessGroup = async (ownership, host = systemHost) => {
100
- let processes;
101
- try {
102
- processes = liveProcesses(await host.listProcesses());
103
- }
104
- catch {
106
+ const processes = await readLiveProcessTable(host);
107
+ if (processes === undefined)
105
108
  return { cleaned: false, reason: "process table could not be inspected" };
106
- }
107
109
  const plan = await createOwnedCleanupPlan(ownership, processes, host);
108
110
  if ("cleaned" in plan)
109
111
  return plan;
@@ -126,10 +128,38 @@ export const cleanupOwnedProcessGroup = async (ownership, host = systemHost) =>
126
128
  }
127
129
  return { cleaned: true, signaled };
128
130
  };
131
+ /** Verify that no live process still carries an owned capture run token. */
132
+ export const verifyNoTokenOwnedProcesses = async (runId, host = systemHost) => {
133
+ const processes = await readLiveProcessTable(host);
134
+ if (processes === undefined)
135
+ return { cleaned: false, reason: "process table could not be inspected" };
136
+ const scan = await scanTokenOwnedProcesses(processes, runId, host);
137
+ if (scan.failures.length > 0)
138
+ return cleanupValidationFailure(scan.failures);
139
+ return scan.owned.length === 0
140
+ ? { cleaned: true, signaled: false }
141
+ : { cleaned: false, reason: "token-owned process remained after cleanup" };
142
+ };
143
+ const readLiveProcessTable = async (host) => {
144
+ try {
145
+ return liveProcesses(await host.listProcesses());
146
+ }
147
+ catch {
148
+ return undefined;
149
+ }
150
+ };
129
151
  const createOwnedCleanupPlan = async (ownership, processes, host) => {
152
+ const tokenOwned = ownership.sweepTokenOwnedProcesses === true
153
+ ? await scanTokenOwnedProcesses(processes, ownership.runId, host)
154
+ : { owned: [], failures: [] };
155
+ if (tokenOwned.failures.length > 0)
156
+ return cleanupValidationFailure(tokenOwned.failures);
157
+ const tokenOwnedGroupIds = new Set(tokenOwned.owned.map(({ processGroupId }) => processGroupId));
130
158
  const launcher = processes.find(({ pid }) => pid === ownership.leaderPid);
131
159
  const rootMembers = processes.filter(({ processGroupId }) => processGroupId === ownership.processGroupId);
132
- if (launcher === undefined && rootMembers.length === 0)
160
+ if (launcher === undefined &&
161
+ rootMembers.length === 0 &&
162
+ tokenOwned.owned.length === 0)
133
163
  return { cleaned: true, signaled: false };
134
164
  let descendants = [];
135
165
  if (launcher !== undefined) {
@@ -142,11 +172,14 @@ const createOwnedCleanupPlan = async (ownership, processes, host) => {
142
172
  const processGroupIds = new Set([ownership.processGroupId]);
143
173
  for (const descendant of descendants)
144
174
  processGroupIds.add(descendant.processGroupId);
175
+ for (const process of tokenOwned.owned)
176
+ processGroupIds.add(process.processGroupId);
145
177
  for (const processGroupId of processGroupIds) {
146
178
  if (processGroupId === ownership.processGroupId)
147
179
  continue;
148
180
  const groupLeader = processes.find(({ pid, processGroupId: observedGroupId }) => pid === processGroupId && observedGroupId === processGroupId);
149
- if (groupLeader === undefined || !descendantPids.has(groupLeader.pid))
181
+ if ((groupLeader === undefined || !descendantPids.has(groupLeader.pid)) &&
182
+ !tokenOwnedGroupIds.has(processGroupId))
150
183
  return {
151
184
  cleaned: false,
152
185
  reason: "descendant process-group leader identity could not be verified",
@@ -162,17 +195,6 @@ const createOwnedCleanupPlan = async (ownership, processes, host) => {
162
195
  .sort((left, right) => left - right)),
163
196
  };
164
197
  };
165
- const launcherIdentityFailure = (launcher, ownership) => {
166
- if (launcher.processGroupId !== ownership.processGroupId)
167
- return "owned launcher process-group identity did not match";
168
- if (ownership.expectedParentPid !== undefined &&
169
- launcher.parentPid !== ownership.expectedParentPid)
170
- return "owned launcher parent identity did not match";
171
- if (ownership.expectedCommand !== undefined &&
172
- !matchesOwnedProcessCommand(launcher.command, ownership.expectedCommand))
173
- return `owned launcher command identity did not match (observed=${launcher.command}; expected=${ownership.expectedCommand})`;
174
- return null;
175
- };
176
198
  const revalidateOwnedProcessGroup = async (processGroupId, ownership, host) => {
177
199
  let members;
178
200
  try {
@@ -197,7 +219,8 @@ const revalidateOwnedProcessGroup = async (processGroupId, ownership, host) => {
197
219
  }
198
220
  if (processGroupId !== ownership.processGroupId &&
199
221
  members.length > 0 &&
200
- !members.some(({ pid }) => pid === processGroupId))
222
+ !members.some(({ pid }) => pid === processGroupId) &&
223
+ ownership.sweepTokenOwnedProcesses !== true)
201
224
  return {
202
225
  cleaned: false,
203
226
  reason: "descendant process-group leader identity could not be revalidated",
@@ -230,39 +253,28 @@ const processOwnershipFailures = async (members, runId, host) => {
230
253
  }
231
254
  return failures;
232
255
  };
233
- /** Observe one group without signaling it, failing closed on identity doubt. */
234
- export const observeOwnedProcessGroup = async (ownership, host = systemHost) => {
235
- let members;
236
- try {
237
- members = (await host.listProcesses()).filter(({ processGroupId }) => processGroupId === ownership.processGroupId);
238
- }
239
- catch {
240
- return {
241
- state: "unverifiable",
242
- reason: "process group could not be inspected",
243
- };
244
- }
245
- const liveMembers = liveProcesses(members);
246
- if (liveMembers.length === 0)
247
- return { state: "empty" };
248
- for (const member of liveMembers) {
256
+ const scanTokenOwnedProcesses = async (processes, runId, host) => {
257
+ const owned = [];
258
+ const failures = [];
259
+ for (const process of processes) {
249
260
  try {
250
- if ((await host.environment(member.pid)).REA_PROCESS_RUN_ID !==
251
- ownership.runId)
252
- return {
253
- state: "unverifiable",
254
- reason: "process ownership did not match",
255
- };
261
+ if ((await host.environment(process.pid)).REA_PROCESS_RUN_ID === runId)
262
+ owned.push(process);
256
263
  }
257
264
  catch {
258
- return {
259
- state: "unverifiable",
260
- reason: "process ownership could not be revalidated",
261
- };
265
+ try {
266
+ const live = liveProcesses(await host.listProcesses());
267
+ if (!live.some(({ pid }) => pid === process.pid))
268
+ continue;
269
+ }
270
+ catch { }
271
+ failures.push({ pid: process.pid, reason: "environment-unreadable" });
262
272
  }
263
273
  }
264
- return { state: "alive" };
274
+ return { owned, failures };
265
275
  };
276
+ /** Observe one group without signaling it, failing closed on identity doubt. */
277
+ export const observeOwnedProcessGroup = async (ownership, host = systemHost) => observeOwnedProcessGroupWithHost(ownership, host);
266
278
  /**
267
279
  * Record the live launcher and descendant lineage after run-token validation.
268
280
  *
@@ -270,80 +282,4 @@ export const observeOwnedProcessGroup = async (ownership, host = systemHost) =>
270
282
  * list means no descendants were live during this observation, not that the
271
283
  * run never created a short-lived child.
272
284
  */
273
- export const observeOwnedProcessLineage = async (ownership, host = systemHost) => {
274
- let processes;
275
- try {
276
- processes = liveProcesses(await host.listProcesses());
277
- }
278
- catch {
279
- return unavailableLineage(ownership, "process table could not be inspected");
280
- }
281
- const launcher = processes.find(({ pid }) => pid === ownership.leaderPid);
282
- if (launcher === undefined)
283
- return unavailableLineage(ownership, "owned launcher is not live");
284
- if (launcher.processGroupId !== ownership.processGroupId)
285
- return unavailableLineage(ownership, "owned launcher process-group identity did not match");
286
- if (ownership.expectedParentPid !== undefined &&
287
- launcher.parentPid !== ownership.expectedParentPid)
288
- return unavailableLineage(ownership, "owned launcher parent identity did not match");
289
- if (ownership.expectedCommand !== undefined &&
290
- !matchesOwnedProcessCommand(launcher.command, ownership.expectedCommand))
291
- return unavailableLineage(ownership, `owned launcher command identity did not match (observed=${launcher.command}; expected=${ownership.expectedCommand})`);
292
- const descendants = descendantsOf(launcher.pid, processes);
293
- for (const member of [launcher, ...descendants]) {
294
- try {
295
- if ((await host.environment(member.pid)).REA_PROCESS_RUN_ID !==
296
- ownership.runId)
297
- return unavailableLineage(ownership, "process lineage contains an unowned or PID-reused process");
298
- }
299
- catch {
300
- return unavailableLineage(ownership, "process ownership could not be revalidated");
301
- }
302
- }
303
- return {
304
- status: "verified",
305
- observedAt: new Date().toISOString(),
306
- lineage: {
307
- schemaVersion: 1,
308
- runId: ownership.runId,
309
- launcherPid: launcher.pid,
310
- launcherParentPid: launcher.parentPid,
311
- processGroupId: launcher.processGroupId,
312
- descendants: descendants
313
- .sort((left, right) => left.pid - right.pid)
314
- .map(({ pid, parentPid, processGroupId }) => ({
315
- pid,
316
- parentPid,
317
- processGroupId,
318
- })),
319
- },
320
- };
321
- };
322
- const descendantsOf = (launcherPid, processes) => {
323
- const childrenByParent = new Map();
324
- for (const process of processes) {
325
- const siblings = childrenByParent.get(process.parentPid) ?? [];
326
- siblings.push(process);
327
- childrenByParent.set(process.parentPid, siblings);
328
- }
329
- const descendants = [];
330
- const pending = [...(childrenByParent.get(launcherPid) ?? [])];
331
- const visited = new Set([launcherPid]);
332
- for (const process of pending) {
333
- if (visited.has(process.pid))
334
- continue;
335
- visited.add(process.pid);
336
- descendants.push(process);
337
- pending.push(...(childrenByParent.get(process.pid) ?? []));
338
- }
339
- return descendants;
340
- };
341
- const unavailableLineage = (ownership, reason) => ({
342
- status: "unavailable",
343
- observedAt: new Date().toISOString(),
344
- runId: ownership.runId,
345
- launcherPid: ownership.leaderPid,
346
- processGroupId: ownership.processGroupId,
347
- reason,
348
- });
349
- const liveProcesses = (members) => members.filter(({ state }) => !state.startsWith("Z"));
285
+ export const observeOwnedProcessLineage = async (ownership, host = systemHost) => observeOwnedProcessLineageWithHost(ownership, host);
@@ -0,0 +1,13 @@
1
+ import { matchesOwnedProcessCommand } from "./ProcessCommandIdentity.js";
2
+ /** Compare a live launcher against the identity committed at process launch. */
3
+ export const launcherIdentityFailure = (launcher, ownership) => {
4
+ if (launcher.processGroupId !== ownership.processGroupId)
5
+ return "owned launcher process-group identity did not match";
6
+ if (ownership.expectedParentPid !== undefined &&
7
+ launcher.parentPid !== ownership.expectedParentPid)
8
+ return "owned launcher parent identity did not match";
9
+ if (ownership.expectedCommand !== undefined &&
10
+ !matchesOwnedProcessCommand(launcher.command, ownership.expectedCommand))
11
+ return `owned launcher command identity did not match (observed=${launcher.command}; expected=${ownership.expectedCommand})`;
12
+ return null;
13
+ };
@@ -0,0 +1,88 @@
1
+ import { launcherIdentityFailure } from "./ProcessOwnershipIdentity.js";
2
+ import { descendantsOf, liveProcesses } from "./ProcessOwnershipProcessTree.js";
3
+ /** Observe one group without signaling it, failing closed on identity doubt. */
4
+ export const observeOwnedProcessGroupWithHost = async (ownership, host) => {
5
+ let members;
6
+ try {
7
+ members = (await host.listProcesses()).filter(({ processGroupId }) => processGroupId === ownership.processGroupId);
8
+ }
9
+ catch {
10
+ return {
11
+ state: "unverifiable",
12
+ reason: "process group could not be inspected",
13
+ };
14
+ }
15
+ const liveMembers = liveProcesses(members);
16
+ if (liveMembers.length === 0)
17
+ return { state: "empty" };
18
+ for (const member of liveMembers) {
19
+ try {
20
+ if ((await host.environment(member.pid)).REA_PROCESS_RUN_ID !==
21
+ ownership.runId)
22
+ return {
23
+ state: "unverifiable",
24
+ reason: "process ownership did not match",
25
+ };
26
+ }
27
+ catch {
28
+ return {
29
+ state: "unverifiable",
30
+ reason: "process ownership could not be revalidated",
31
+ };
32
+ }
33
+ }
34
+ return { state: "alive" };
35
+ };
36
+ /** Record the live launcher and descendant lineage after run-token validation. */
37
+ export const observeOwnedProcessLineageWithHost = async (ownership, host) => {
38
+ let processes;
39
+ try {
40
+ processes = liveProcesses(await host.listProcesses());
41
+ }
42
+ catch {
43
+ return unavailableLineage(ownership, "process table could not be inspected");
44
+ }
45
+ const launcher = processes.find(({ pid }) => pid === ownership.leaderPid);
46
+ if (launcher === undefined)
47
+ return unavailableLineage(ownership, "owned launcher is not live");
48
+ const identityFailure = launcherIdentityFailure(launcher, ownership);
49
+ if (identityFailure !== null)
50
+ return unavailableLineage(ownership, identityFailure);
51
+ const descendants = descendantsOf(launcher.pid, processes);
52
+ for (const member of [launcher, ...descendants]) {
53
+ try {
54
+ if ((await host.environment(member.pid)).REA_PROCESS_RUN_ID !==
55
+ ownership.runId)
56
+ return unavailableLineage(ownership, "process lineage contains an unowned or PID-reused process");
57
+ }
58
+ catch {
59
+ return unavailableLineage(ownership, "process ownership could not be revalidated");
60
+ }
61
+ }
62
+ return {
63
+ status: "verified",
64
+ observedAt: new Date().toISOString(),
65
+ lineage: {
66
+ schemaVersion: 1,
67
+ runId: ownership.runId,
68
+ launcherPid: launcher.pid,
69
+ launcherParentPid: launcher.parentPid,
70
+ processGroupId: launcher.processGroupId,
71
+ descendants: descendants
72
+ .sort((left, right) => left.pid - right.pid)
73
+ .map(({ pid, parentPid, processGroupId }) => ({
74
+ pid,
75
+ parentPid,
76
+ processGroupId,
77
+ })),
78
+ },
79
+ };
80
+ };
81
+ const unavailableLineage = (ownership, reason) => ({
82
+ status: "unavailable",
83
+ observedAt: new Date().toISOString(),
84
+ runId: ownership.runId,
85
+ launcherPid: ownership.leaderPid,
86
+ processGroupId: ownership.processGroupId,
87
+ reason,
88
+ });
@@ -0,0 +1,22 @@
1
+ /** Exclude process-table entries that the operating system has already reaped. */
2
+ export const liveProcesses = (members) => members.filter(({ state }) => !state.startsWith("Z"));
3
+ /** Return every live-table descendant of a launcher in breadth-first order. */
4
+ export const descendantsOf = (launcherPid, processes) => {
5
+ const childrenByParent = new Map();
6
+ for (const process of processes) {
7
+ const siblings = childrenByParent.get(process.parentPid) ?? [];
8
+ siblings.push(process);
9
+ childrenByParent.set(process.parentPid, siblings);
10
+ }
11
+ const descendants = [];
12
+ const pending = [...(childrenByParent.get(launcherPid) ?? [])];
13
+ const visited = new Set([launcherPid]);
14
+ for (const process of pending) {
15
+ if (visited.has(process.pid))
16
+ continue;
17
+ visited.add(process.pid);
18
+ descendants.push(process);
19
+ pending.push(...(childrenByParent.get(process.pid) ?? []));
20
+ }
21
+ return descendants;
22
+ };
@@ -26,8 +26,7 @@ export class ProviderStartupDeadline {
26
26
  #externalSignal;
27
27
  #onExternalAbort;
28
28
  #timer;
29
- #timedOut = false;
30
- #cancelled = false;
29
+ #interruption;
31
30
  constructor(timeoutMs, signal) {
32
31
  this.timeoutMs = timeoutMs;
33
32
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
@@ -38,11 +37,11 @@ export class ProviderStartupDeadline {
38
37
  if (this.#controller.signal.aborted)
39
38
  return;
40
39
  if (Date.now() >= this.#deadlineAt) {
41
- this.#timedOut = true;
40
+ this.#interruption = "timeout";
42
41
  this.#abort(new DOMException("Provider startup deadline elapsed", "TimeoutError"));
43
42
  return;
44
43
  }
45
- this.#cancelled = true;
44
+ this.#interruption = "cancelled";
46
45
  this.#abort(signal?.reason);
47
46
  };
48
47
  if (signal?.aborted === true) {
@@ -53,7 +52,7 @@ export class ProviderStartupDeadline {
53
52
  this.#timer = setTimeout(() => {
54
53
  if (this.#controller.signal.aborted)
55
54
  return;
56
- this.#timedOut = true;
55
+ this.#interruption = "timeout";
57
56
  this.#abort(new DOMException("Provider startup deadline elapsed", "TimeoutError"));
58
57
  }, timeoutMs);
59
58
  }
@@ -61,13 +60,10 @@ export class ProviderStartupDeadline {
61
60
  get signal() {
62
61
  return this.#controller.signal;
63
62
  }
64
- /** True only when the external caller signal initiated cancellation. */
65
- get cancelled() {
66
- return this.#cancelled;
67
- }
68
- /** True when the absolute startup deadline has elapsed. */
69
- get timedOut() {
70
- return (this.#timedOut || (!this.#cancelled && Date.now() >= this.#deadlineAt));
63
+ /** First control path that interrupted startup, if any. */
64
+ get interruption() {
65
+ return (this.#interruption ??
66
+ (Date.now() >= this.#deadlineAt ? "timeout" : undefined));
71
67
  }
72
68
  /** Milliseconds still available to the next startup phase. */
73
69
  remainingMs() {