rea-agents 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +78 -32
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/ArtifactExtraction.js +1 -1
  4. package/dist/application/ArtifactInventory/classify.js +42 -0
  5. package/dist/application/ArtifactInventory/hash.js +43 -0
  6. package/dist/application/ArtifactInventory/reader.js +41 -0
  7. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  8. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  9. package/dist/application/ArtifactInventory/types.js +10 -0
  10. package/dist/application/ArtifactInventory.js +9 -351
  11. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  12. package/dist/application/BinarySession.js +47 -270
  13. package/dist/application/BinarySessionExecution.js +28 -0
  14. package/dist/application/BinarySessionOpen.js +39 -0
  15. package/dist/application/BinarySessionRecords.js +149 -0
  16. package/dist/application/BinarySessionStatus.js +75 -0
  17. package/dist/application/CapabilityInventory.js +24 -2
  18. package/dist/application/ClientRegistrationStatus.js +1 -1
  19. package/dist/application/CrossVersionInventory.js +8 -2
  20. package/dist/application/CrossVersionInvestigation.js +1 -23
  21. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  22. package/dist/application/DirectAnalysis.js +47 -44
  23. package/dist/application/Doctor.js +156 -238
  24. package/dist/application/DoctorDiagnostics.js +180 -0
  25. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  26. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  27. package/dist/application/EnhancedToolPagination.js +22 -0
  28. package/dist/application/EnhancedToolTypes.js +4 -0
  29. package/dist/application/EnhancedTools.js +20 -37
  30. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  31. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  32. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  33. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  34. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  35. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  36. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  37. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  38. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  39. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  40. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  41. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  42. package/dist/application/JavaScriptJsonModules.js +39 -0
  43. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  44. package/dist/application/JavaScriptReplayPermission.js +16 -0
  45. package/dist/application/JavaScriptReplayService.js +182 -156
  46. package/dist/application/LinuxHopper.js +10 -0
  47. package/dist/application/MacHopper.js +22 -11
  48. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  49. package/dist/application/PermissionAuthority.js +106 -63
  50. package/dist/application/ProcessCaptureCapability.js +1 -1
  51. package/dist/application/ProcessCapturePermission.js +13 -0
  52. package/dist/application/ProcessCli.js +2 -12
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProjectPermissionStore.js +48 -9
  55. package/dist/application/RealHopperAssertions.js +76 -0
  56. package/dist/application/ReconstructionCoverageService.js +69 -0
  57. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  58. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  59. package/dist/application/Setup.js +147 -259
  60. package/dist/application/SetupClientConfiguration.js +220 -0
  61. package/dist/application/SetupClients.js +24 -2
  62. package/dist/application/SetupPlan.js +123 -12
  63. package/dist/application/SetupSkill.js +26 -12
  64. package/dist/application/SupportedClients.js +1 -0
  65. package/dist/application/Uninstall.js +6 -3
  66. package/dist/application/WindowsCapabilities.js +86 -0
  67. package/dist/artifacts/AsarArtifactReader.js +11 -0
  68. package/dist/browser/BrowserProcessStartup.js +88 -0
  69. package/dist/browser/CdpCaptureDocuments.js +12 -8
  70. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  71. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  72. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  73. package/dist/browser/CdpCaptureEventState.js +1 -0
  74. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  75. package/dist/browser/CdpCaptureEvents.js +29 -496
  76. package/dist/browser/CdpObservationSession.js +51 -8
  77. package/dist/browser/CdpPageCapture.js +12 -133
  78. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  79. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  80. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  81. package/dist/browser/PngVisualDiff.js +9 -2
  82. package/dist/catalogIdentity.js +4 -0
  83. package/dist/cli/artifactCommands.js +82 -0
  84. package/dist/cli/coreAnalysisCommands.js +232 -0
  85. package/dist/cli/managedCommands.js +397 -0
  86. package/dist/cli/managedProjectGraphCommand.js +24 -0
  87. package/dist/cli/options.js +9 -0
  88. package/dist/cli/setupCommands.js +108 -0
  89. package/dist/cli/types.js +1 -0
  90. package/dist/cli/utilityCommands.js +122 -0
  91. package/dist/cli.js +10 -768
  92. package/dist/cliApplicationCommands.js +118 -4
  93. package/dist/cliBrowserAdvancedCommands.js +73 -16
  94. package/dist/cliBrowserCommands.js +39 -115
  95. package/dist/cliCommandNames.js +4 -0
  96. package/dist/cliElectronCommands.js +33 -62
  97. package/dist/cliEvidenceCommands.js +13 -2
  98. package/dist/cliInvestigationCommands.js +53 -10
  99. package/dist/cliObservationOptions.js +137 -0
  100. package/dist/cliPolicyCommands.js +80 -46
  101. package/dist/cliSetup.js +289 -0
  102. package/dist/config/environment.js +107 -0
  103. package/dist/config/parseConfig.js +228 -0
  104. package/dist/config/parsers.js +68 -0
  105. package/dist/config/permissions.js +30 -0
  106. package/dist/config/types.js +1 -0
  107. package/dist/config.js +1 -375
  108. package/dist/contracts/applicationToolContracts.js +134 -0
  109. package/dist/contracts/managedToolContracts.js +1 -1
  110. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  111. package/dist/contracts/providerSelection.js +2 -0
  112. package/dist/contracts/toolContracts.js +2 -2
  113. package/dist/contracts/toolEffects.js +14 -0
  114. package/dist/contracts/toolOutputSchemaGroups.js +232 -0
  115. package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
  116. package/dist/contracts/toolOutputSchemas.js +2 -550
  117. package/dist/domain/analysisErrorPresentation.js +234 -0
  118. package/dist/domain/analysisErrorProjection.js +284 -0
  119. package/dist/domain/artifactComparison.js +34 -15
  120. package/dist/domain/binaryTarget.js +87 -37
  121. package/dist/domain/browserObservation.js +1 -285
  122. package/dist/domain/browserObservationSchemas.js +286 -0
  123. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  124. package/dist/domain/errors.js +4 -486
  125. package/dist/domain/functionComparison.js +4 -165
  126. package/dist/domain/functionComparisonDimensions.js +164 -0
  127. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  128. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  129. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  130. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  131. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  132. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  133. package/dist/domain/javascriptSemanticIr.js +30 -0
  134. package/dist/domain/javascriptSemanticProjection.js +366 -0
  135. package/dist/domain/javascriptSemanticState.js +40 -0
  136. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  137. package/dist/domain/javascriptSemanticValues.js +352 -0
  138. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  139. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  140. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  141. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  142. package/dist/domain/knownPageCoverage.js +21 -0
  143. package/dist/domain/managedApplicationGraph.js +14 -364
  144. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  145. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  146. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  147. package/dist/domain/managedArtifact.js +16 -8
  148. package/dist/domain/managedMemberComparison.js +37 -391
  149. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  150. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  151. package/dist/domain/managedNativeVerification.js +27 -389
  152. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  153. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  154. package/dist/domain/managedReconstruction.js +82 -69
  155. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  156. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  157. package/dist/domain/permissionPolicy.js +2 -0
  158. package/dist/domain/reconstructionCoverage.js +387 -0
  159. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  160. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  161. package/dist/domain/runtimeCharacterization.js +85 -0
  162. package/dist/domain/webBundleAnalyzer.js +3 -409
  163. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  164. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  165. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  166. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  167. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  168. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  169. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  170. package/dist/dotnet/ManagedMemberRows.js +120 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  172. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  173. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  174. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  175. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  176. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  177. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  178. package/dist/dotnet/ManagedPeReader.js +59 -28
  179. package/dist/generatedPackageMetadata.js +2 -2
  180. package/dist/ghidra/GhidraClient.js +76 -91
  181. package/dist/ghidra/GhidraClientStartup.js +29 -0
  182. package/dist/ghidra/GhidraClientWire.js +34 -0
  183. package/dist/ghidra/GhidraDefaults.js +1 -1
  184. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  185. package/dist/ghidra/GhidraInstallation.js +20 -15
  186. package/dist/ghidra/GhidraLauncher.js +93 -29
  187. package/dist/ghidra/GhidraProvider.js +61 -161
  188. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  189. package/dist/ghidra/GhidraSessionValues.js +2 -0
  190. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  191. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  192. package/dist/ghidra/GhidraTransport.js +71 -0
  193. package/dist/identity.js +3 -2
  194. package/dist/main/messages.js +5 -0
  195. package/dist/main/reload.js +49 -0
  196. package/dist/main/shutdown.js +25 -0
  197. package/dist/main/startup.js +14 -0
  198. package/dist/main/state.js +26 -0
  199. package/dist/main/transport.js +61 -0
  200. package/dist/main/types.js +1 -0
  201. package/dist/main.js +35 -146
  202. package/dist/process/PrivateRuntimeRoot.js +3 -2
  203. package/dist/process/ProcessOwnership.js +35 -0
  204. package/dist/process/ProviderProcess.js +2 -0
  205. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  206. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  207. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  208. package/dist/replay/ReplayOutcome.js +95 -0
  209. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  210. package/dist/server/ProcessCaptureElicitation.js +157 -0
  211. package/dist/server/createServer.js +119 -54
  212. package/dist/server/registerApplicationTools/characterization.js +57 -0
  213. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  214. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  215. package/dist/server/registerApplicationTools/coverage.js +84 -0
  216. package/dist/server/registerApplicationTools/helpers.js +42 -0
  217. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  218. package/dist/server/registerApplicationTools/types.js +1 -0
  219. package/dist/server/registerApplicationTools.js +10 -112
  220. package/dist/server/registerBrowserTools.js +59 -83
  221. package/dist/server/registerElectronTools.js +31 -46
  222. package/dist/server/registerEnhancedTools.js +34 -42
  223. package/dist/server/registerEvidenceResources.js +54 -0
  224. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  225. package/dist/server/registerInvestigationTools/types.js +1 -0
  226. package/dist/server/registerInvestigationTools.js +83 -116
  227. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  228. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  229. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  230. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  231. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  232. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  233. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  234. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  235. package/dist/server/registerManagedWorkflowTools.js +10 -274
  236. package/dist/server/registerOfficialTools.js +73 -59
  237. package/dist/server/registerSessionRecordTools.js +137 -0
  238. package/dist/server/registerSessionStatusTool.js +2 -1
  239. package/dist/server/registerSessionTools.js +60 -164
  240. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  241. package/dist/server/toolRegistrationOptions.js +101 -3
  242. package/dist/server/toolResult.js +5 -0
  243. package/package.json +10 -11
  244. package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +20 -7
  245. package/scripts/prepare-node-pty.mjs +0 -35
  246. package/scripts/rebuild-native.mjs +0 -22
@@ -0,0 +1,140 @@
1
+ import { open } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ import { digestJson, } from "../application/JavaScriptReplayPlanning.js";
4
+ import { buildLinuxX64ReplaySeccomp } from "./LinuxSeccompPolicy.js";
5
+ import { resolveLinuxRuntimeClosure } from "./LinuxRuntimeClosure.js";
6
+ export const assertRuntimeCommitment = (prepared, closure) => {
7
+ const observed = closure.map((item) => ({
8
+ source_path: item.sourcePath,
9
+ destination_path: item.destinationPath,
10
+ sha256: item.sha256,
11
+ }));
12
+ if (digestJson(observed) !==
13
+ digestJson(prepared.publicPlan.runtime.read_only_files))
14
+ throw new TypeError("Replay runtime closure changed after approval");
15
+ };
16
+ export const temporaryFilterHandle = async () => {
17
+ const { mkdtemp, writeFile, rm } = await import("node:fs/promises");
18
+ const { tmpdir } = await import("node:os");
19
+ const { join } = await import("node:path");
20
+ const directory = await mkdtemp(join(tmpdir(), "rea-replay-filter-"));
21
+ const path = join(directory, "seccomp.bpf");
22
+ await writeFile(path, buildLinuxX64ReplaySeccomp(), { mode: 0o600 });
23
+ const handle = await open(path, "r");
24
+ return {
25
+ path,
26
+ handle,
27
+ close: async () => {
28
+ await handle.close();
29
+ await rm(directory, { recursive: true, force: true });
30
+ },
31
+ };
32
+ };
33
+ export const systemdArguments = (options) => {
34
+ const { unit, policy, prepared, workerPath, closure, filterPath } = options;
35
+ const limits = prepared.publicPlan.limits;
36
+ const descriptorPaths = [
37
+ filterPath,
38
+ ...closure.map(({ sourcePath }) => sourcePath),
39
+ workerPath,
40
+ ];
41
+ const workerFd = descriptorPaths.length + 2;
42
+ const wrapper = descriptorPaths
43
+ .map((_, index) => `exec ${String(index + 3)}<"$1"; shift`)
44
+ .concat('exec "$@"')
45
+ .join("\n");
46
+ return [
47
+ "--user",
48
+ "--pipe",
49
+ "--wait",
50
+ "--service-type=exec",
51
+ "--quiet",
52
+ `--unit=${unit}`,
53
+ `--property=MemoryMax=${String(limits.memory_bytes)}`,
54
+ "--property=MemorySwapMax=0",
55
+ `--property=TasksMax=${String(limits.tasks)}`,
56
+ `--property=CPUQuota=${String(limits.cpu_quota_percent)}%`,
57
+ "--property=KillMode=control-group",
58
+ "--property=SendSIGKILL=yes",
59
+ "--",
60
+ policy.shellPath,
61
+ "-c",
62
+ wrapper,
63
+ "rea-replay-wrapper",
64
+ ...descriptorPaths,
65
+ policy.bubblewrapPath,
66
+ "--unshare-all",
67
+ "--unshare-user",
68
+ "--disable-userns",
69
+ "--new-session",
70
+ "--die-with-parent",
71
+ "--cap-drop",
72
+ "ALL",
73
+ "--clearenv",
74
+ "--setenv",
75
+ "HOME",
76
+ "/work",
77
+ "--setenv",
78
+ "TMPDIR",
79
+ "/tmp",
80
+ "--setenv",
81
+ "TZ",
82
+ "UTC",
83
+ "--setenv",
84
+ "LANG",
85
+ "C",
86
+ "--setenv",
87
+ "LC_ALL",
88
+ "C",
89
+ "--tmpfs",
90
+ "/",
91
+ "--dir",
92
+ "/runtime",
93
+ "--dir",
94
+ "/work",
95
+ "--size",
96
+ String(limits.tmpfs_bytes),
97
+ "--tmpfs",
98
+ "/tmp",
99
+ ...bindArguments(closure),
100
+ "--ro-bind-fd",
101
+ String(workerFd),
102
+ "/runtime/worker.js",
103
+ "--proc",
104
+ "/proc",
105
+ "--dev",
106
+ "/dev",
107
+ "--remount-ro",
108
+ "/",
109
+ "--chdir",
110
+ "/work",
111
+ "--seccomp",
112
+ "3",
113
+ "--",
114
+ "/runtime/node",
115
+ `--max-old-space-size=${String(Math.max(8, Math.floor(limits.memory_bytes / 1024 / 1024 / 2)))}`,
116
+ "--experimental-vm-modules",
117
+ "--permission",
118
+ "--allow-fs-read=/runtime/worker.js",
119
+ "/runtime/worker.js",
120
+ ];
121
+ };
122
+ const bindArguments = (closure) => {
123
+ const arguments_ = [];
124
+ const directories = new Set();
125
+ for (const [index, item] of closure.entries()) {
126
+ const destination = item.destinationPath;
127
+ let parent = dirname(destination);
128
+ const chain = [];
129
+ while (parent !== "/" && !directories.has(parent)) {
130
+ chain.push(parent);
131
+ parent = dirname(parent);
132
+ }
133
+ for (const directory of chain.reverse()) {
134
+ directories.add(directory);
135
+ arguments_.push("--dir", directory);
136
+ }
137
+ arguments_.push("--ro-bind-fd", String(index + 4), destination);
138
+ }
139
+ return arguments_;
140
+ };
@@ -0,0 +1,95 @@
1
+ import { digestJson, } from "../application/JavaScriptReplayPlanning.js";
2
+ import { jsonValueSchema } from "../domain/jsonValue.js";
3
+ export const workerRequest = (prepared) => ({
4
+ schemaVersion: 1,
5
+ left: workerSide(prepared.publicPlan.left, prepared.leftSources),
6
+ ...(prepared.publicPlan.right === undefined ||
7
+ prepared.rightSources === undefined
8
+ ? {}
9
+ : { right: workerSide(prepared.publicPlan.right, prepared.rightSources) }),
10
+ cases: prepared.publicPlan.cases.map((item) => ({
11
+ caseId: item.case_id,
12
+ arguments: item.arguments,
13
+ inputSha256: item.sha256,
14
+ })),
15
+ determinism: {
16
+ clockIso: prepared.publicPlan.determinism.clock_iso,
17
+ randomSeed: prepared.publicPlan.determinism.random_seed,
18
+ },
19
+ limits: {
20
+ resultDepth: prepared.publicPlan.limits.result_depth,
21
+ resultNodes: prepared.publicPlan.limits.result_nodes,
22
+ exceptionBytes: Math.min(64 * 1024, prepared.publicPlan.limits.output_bytes),
23
+ },
24
+ });
25
+ const workerSide = (side, sources) => ({
26
+ modules: side.modules.map((module) => ({
27
+ alias: module.alias,
28
+ format: module.format,
29
+ dependencies: module.dependencies,
30
+ source: sources[module.alias] ?? "",
31
+ })),
32
+ entryAlias: side.entry_alias,
33
+ entryExport: side.entry_export,
34
+ });
35
+ export const commitOutcomes = (outcomes) => outcomes.map((outcome) => {
36
+ const semantic = outcome.value === undefined ? (outcome.exception ?? null) : outcome.value;
37
+ return {
38
+ case_id: outcome.case_id,
39
+ outcome: outcome.outcome,
40
+ ...(outcome.value === undefined
41
+ ? {}
42
+ : { value: jsonValueSchema.parse(outcome.value) }),
43
+ ...(outcome.exception === undefined
44
+ ? {}
45
+ : { exception: outcome.exception }),
46
+ input_sha256: outcome.input_sha256,
47
+ output_sha256: digestJson(semantic),
48
+ truncated: false,
49
+ };
50
+ });
51
+ export const compareOutcomes = (left, right) => left.map((item, index) => ({
52
+ case_id: item.case_id,
53
+ status: right[index] === undefined
54
+ ? "unknown"
55
+ : item.outcome === right[index].outcome &&
56
+ item.output_sha256 === right[index].output_sha256
57
+ ? "equal"
58
+ : "changed",
59
+ left_index: index,
60
+ right_index: index,
61
+ }));
62
+ export const terminationResult = (options) => {
63
+ const { prepared, stderr, termination, cleanup, limitation } = options;
64
+ const outcomes = prepared.publicPlan.cases.map((item) => ({
65
+ case_id: item.case_id,
66
+ outcome: termination,
67
+ input_sha256: item.sha256,
68
+ output_sha256: null,
69
+ truncated: termination === "protocol_error",
70
+ }));
71
+ const differential = prepared.publicPlan.right !== undefined;
72
+ return {
73
+ schema_version: 1,
74
+ plan_digest: prepared.publicPlan.plan_digest,
75
+ outcomes: [...outcomes, ...(differential ? outcomes : [])],
76
+ ...(differential
77
+ ? {
78
+ comparison: outcomes.map((item, index) => ({
79
+ case_id: item.case_id,
80
+ status: "unknown",
81
+ left_index: index,
82
+ right_index: index,
83
+ })),
84
+ }
85
+ : {}),
86
+ stderr,
87
+ termination,
88
+ cleanup,
89
+ limitations: [
90
+ limitation ??
91
+ `Replay terminated with ${termination}; the requested functional result remains unknown.`,
92
+ ],
93
+ reproducer: null,
94
+ };
95
+ };
@@ -0,0 +1,71 @@
1
+ import { spawn } from "node:child_process";
2
+ export const boundedCollector = (maximum) => {
3
+ const chunks = [];
4
+ let bytes = 0;
5
+ let overflow = false;
6
+ return {
7
+ append: (chunk) => {
8
+ const retained = chunk.subarray(0, Math.max(0, maximum - bytes));
9
+ if (retained.byteLength > 0)
10
+ chunks.push(retained);
11
+ bytes += retained.byteLength;
12
+ if (retained.byteLength < chunk.byteLength)
13
+ overflow = true;
14
+ },
15
+ value: () => Buffer.concat(chunks).toString("utf8"),
16
+ exceeded: () => overflow,
17
+ };
18
+ };
19
+ const collect = async (executable, arguments_, maximum) => new Promise((resolve, reject) => {
20
+ const child = spawn(executable, [...arguments_], {
21
+ stdio: ["ignore", "pipe", "pipe"],
22
+ });
23
+ const stdout = boundedCollector(maximum);
24
+ const stderr = boundedCollector(maximum);
25
+ const timeout = setTimeout(() => child.kill("SIGKILL"), 5_000);
26
+ timeout.unref();
27
+ child.stdout?.on("data", stdout.append);
28
+ child.stderr?.on("data", stderr.append);
29
+ child.once("error", (error) => {
30
+ clearTimeout(timeout);
31
+ reject(error);
32
+ });
33
+ child.once("close", (code) => {
34
+ clearTimeout(timeout);
35
+ resolve({ code, stdout: stdout.value(), stderr: stderr.value() });
36
+ });
37
+ });
38
+ export const killUnit = async (systemctl, unit) => {
39
+ try {
40
+ await collect(systemctl, ["--user", "kill", "--kill-whom=all", "--signal=SIGKILL", unit], 4096);
41
+ }
42
+ catch {
43
+ /* cleanup continues */
44
+ }
45
+ };
46
+ export const observeCleanup = async (systemctl, unit) => {
47
+ try {
48
+ const state = await collect(systemctl, ["--user", "show", "--property=ActiveState", "--value", unit], 4096);
49
+ const activeState = state.stdout.trim();
50
+ if (state.code !== 0 ||
51
+ activeState === "inactive" ||
52
+ activeState === "failed" ||
53
+ activeState.length === 0) {
54
+ await collect(systemctl, ["--user", "reset-failed", unit], 4096);
55
+ return { state: "complete", residual_resources: [] };
56
+ }
57
+ return { state: "incomplete", residual_resources: [unit] };
58
+ }
59
+ catch {
60
+ return { state: "incomplete", residual_resources: [unit] };
61
+ }
62
+ };
63
+ export const observeUnitResult = async (systemctl, unit) => {
64
+ try {
65
+ const result = await collect(systemctl, ["--user", "show", "--property=Result", "--value", unit], 4096);
66
+ return result.stdout.trim();
67
+ }
68
+ catch {
69
+ return "unknown";
70
+ }
71
+ };
@@ -0,0 +1,157 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { acceptedContent, inputRequired, inputResponse, } from "@modelcontextprotocol/server";
3
+ import { z } from "zod";
4
+ import { PermissionRequiredError } from "../domain/errors.js";
5
+ import { isPermissionRequestWithinCeiling, } from "../domain/permissionPolicy.js";
6
+ const RESPONSE_KEY = "process_capture_grant";
7
+ const STATE_VERSION = 1;
8
+ /** Shared continuation, replay, and round limits for process capture consent. */
9
+ export const PROCESS_CAPTURE_ELICITATION_POLICY = {
10
+ protocolVersions: ["2026-07-28"],
11
+ stateTtlSeconds: 600,
12
+ roundTimeoutMs: 600_000,
13
+ maxConsumedNonces: 4_096,
14
+ };
15
+ const grantResponseSchema = z.strictObject({
16
+ lifetime: z.enum(["once", "session"]).meta({ default: "session" }),
17
+ });
18
+ const grantResponseJsonSchema = { ...z.toJSONSchema(grantResponseSchema) };
19
+ // MCP elicitation accepts a reduced JSON Schema subset. These generator
20
+ // annotations do not change the schema's fields or validation semantics; the
21
+ // Standard Schema marker would make the SDK regenerate the removed keywords.
22
+ Reflect.deleteProperty(grantResponseJsonSchema, "$schema");
23
+ Reflect.deleteProperty(grantResponseJsonSchema, "additionalProperties");
24
+ const stateSchema = z.strictObject({
25
+ version: z.literal(STATE_VERSION),
26
+ nonce: z.string().uuid(),
27
+ request: z.strictObject({
28
+ capability: z.literal("process_capture"),
29
+ roots: z.array(z.string()),
30
+ executables: z.array(z.string()),
31
+ environment_names: z.array(z.string()),
32
+ origins: z.array(z.string()).optional(),
33
+ network: z.enum(["none", "loopback", "external"]),
34
+ mount: z.boolean(),
35
+ operation_identity: z.string(),
36
+ }),
37
+ });
38
+ /** Authorize capture or return a signed MCP input-required continuation. */
39
+ export const authorizeProcessCaptureWithElicitation = async (authority, request, context, elicitation) => {
40
+ const supported = elicitation.supported(context);
41
+ const initial = await authority.authorize(request, "read", {
42
+ elicitationSupported: supported,
43
+ });
44
+ if (initial.ok || !(initial.error instanceof PermissionRequiredError))
45
+ return initial;
46
+ const denied = initial.error;
47
+ if (!supported ||
48
+ denied.requested.capability !== "process_capture" ||
49
+ denied.ceiling === null ||
50
+ !isPermissionRequestWithinCeiling(denied.requested, denied.ceiling))
51
+ return initial;
52
+ const encodedState = context.mcpReq.requestState();
53
+ if (encodedState !== undefined) {
54
+ const state = stateSchema.safeParse(encodedState);
55
+ if (!state.success || !sameRequest(state.data.request, denied.requested))
56
+ return initial;
57
+ const response = inputResponse(context.mcpReq.inputResponses, RESPONSE_KEY);
58
+ if (response.kind === "elicit") {
59
+ if (!consumeNonce(elicitation.consumedNonces, state.data.nonce, elicitation.now()))
60
+ return initial;
61
+ }
62
+ if (response.kind === "elicit" && response.action === "accept") {
63
+ const content = acceptedContent(context.mcpReq.inputResponses, RESPONSE_KEY, grantResponseSchema);
64
+ if (content === undefined)
65
+ return initial;
66
+ return grantAndAuthorize(authority, state.data.request, content.lifetime);
67
+ }
68
+ if (response.kind === "elicit" &&
69
+ (response.action === "decline" || response.action === "cancel"))
70
+ return initial;
71
+ }
72
+ const state = await elicitation.stateCodec.mint({
73
+ version: STATE_VERSION,
74
+ nonce: randomUUID(),
75
+ request: {
76
+ capability: "process_capture",
77
+ roots: [...denied.requested.roots],
78
+ executables: [...denied.requested.executables],
79
+ environment_names: [...denied.requested.environment_names],
80
+ network: denied.requested.network,
81
+ mount: denied.requested.mount,
82
+ operation_identity: denied.requested.operation_identity,
83
+ ...(denied.requested.origins === undefined
84
+ ? {}
85
+ : { origins: [...denied.requested.origins] }),
86
+ },
87
+ });
88
+ return inputRequired({
89
+ inputRequests: {
90
+ [RESPONSE_KEY]: inputRequired.elicit({
91
+ message: permissionMessage(denied.requested),
92
+ requestedSchema: grantResponseJsonSchema,
93
+ }),
94
+ },
95
+ requestState: state,
96
+ });
97
+ };
98
+ const grantAndAuthorize = async (authority, request, lifetime) => {
99
+ const grant = {
100
+ ...request,
101
+ grant_id: `elicited:${randomUUID()}`,
102
+ lifetime,
103
+ operation_identity: lifetime === "once" ? request.operation_identity : null,
104
+ expires_at: null,
105
+ };
106
+ const granted = authority.grant(grant);
107
+ if (!granted.ok)
108
+ return authority.authorize(request, "read", {
109
+ elicitationSupported: true,
110
+ });
111
+ let authorized;
112
+ try {
113
+ authorized = await authority.authorize(request, "read", {
114
+ elicitationSupported: true,
115
+ });
116
+ }
117
+ catch (cause) {
118
+ authority.revoke(grant.grant_id);
119
+ throw cause;
120
+ }
121
+ if (!authorized.ok)
122
+ authority.revoke(grant.grant_id);
123
+ return authorized;
124
+ };
125
+ const sameRequest = (left, right) => left.capability === right.capability &&
126
+ left.network === right.network &&
127
+ left.mount === right.mount &&
128
+ left.operation_identity === right.operation_identity &&
129
+ sameOptionalStrings(left.origins, right.origins) &&
130
+ sameStrings(left.roots, right.roots) &&
131
+ sameStrings(left.executables, right.executables) &&
132
+ sameStrings(left.environment_names, right.environment_names);
133
+ const sameStrings = (left, right) => left.length === right.length &&
134
+ left.every((value, index) => value === right[index]);
135
+ const sameOptionalStrings = (left, right) => left === undefined
136
+ ? right === undefined
137
+ : right !== undefined && sameStrings(left, right);
138
+ const consumeNonce = (consumed, nonce, now) => {
139
+ for (const [candidate, expiresAt] of consumed)
140
+ if (expiresAt <= now)
141
+ consumed.delete(candidate);
142
+ if (consumed.has(nonce) ||
143
+ consumed.size >= PROCESS_CAPTURE_ELICITATION_POLICY.maxConsumedNonces)
144
+ return false;
145
+ consumed.set(nonce, now + PROCESS_CAPTURE_ELICITATION_POLICY.stateTtlSeconds * 1_000);
146
+ return true;
147
+ };
148
+ const permissionMessage = (request) => [
149
+ "Allow REA to run this exact process-capture scope?",
150
+ `Executable: ${request.executables.join(", ") || "none"}`,
151
+ `Filesystem roots: ${request.roots.join(", ") || "none"}`,
152
+ `Environment names: ${request.environment_names.join(", ") || "none"}`,
153
+ `Origins: ${request.origins?.join(", ") || "none"}`,
154
+ `Network: ${request.network}`,
155
+ `Mount access: ${request.mount ? "yes" : "no"}`,
156
+ "Choose whether the grant applies once or for this MCP session.",
157
+ ].join("\n");