rea-agents 1.6.0 → 2.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 (238) hide show
  1. package/README.md +157 -66
  2. package/bridge/ghidra/ReaGhidraBridge.java +2075 -0
  3. package/bridge/hopper_bridge.py +13 -3
  4. package/dist/application/AbortablePromise.js +25 -0
  5. package/dist/application/AnalysisProvider.js +3 -0
  6. package/dist/application/AnalysisProviderEvaluation.js +60 -0
  7. package/dist/application/AnalysisProviderRegistry.js +228 -0
  8. package/dist/application/AnalysisSnapshotCache.js +41 -21
  9. package/dist/application/AnalysisSnapshotFiles.js +5 -6
  10. package/dist/application/BinarySession.js +218 -101
  11. package/dist/application/CapabilityInventory.js +18 -0
  12. package/dist/application/CompositeProvider.js +20 -8
  13. package/dist/application/CrossVersionInvestigation.js +10 -6
  14. package/dist/application/DirectAnalysis.js +49 -35
  15. package/dist/application/Doctor.js +110 -8
  16. package/dist/application/ElectronBoundaryAnalysis.js +96 -0
  17. package/dist/application/ElectronBoundaryGraph.js +9 -0
  18. package/dist/application/ElectronBoundaryGraphContext.js +62 -0
  19. package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
  20. package/dist/application/ElectronBoundaryGraphNative.js +86 -0
  21. package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
  22. package/dist/application/EnhancedTools.js +39 -14
  23. package/dist/application/InvestigationProviders.js +41 -0
  24. package/dist/application/JavaScriptApplicationEvidence.js +26 -0
  25. package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
  26. package/dist/application/JavaScriptApplicationService.js +74 -0
  27. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
  28. package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
  29. package/dist/application/JavaScriptArtifactAnalysis.js +305 -0
  30. package/dist/application/JavaScriptArtifactAnalysisTypes.js +1 -0
  31. package/dist/application/JavaScriptArtifactFiles.js +231 -0
  32. package/dist/application/JavaScriptArtifactGraphAccumulator.js +54 -0
  33. package/dist/application/JavaScriptArtifactGraphBuilder.js +144 -0
  34. package/dist/application/JavaScriptArtifactGraphContext.js +205 -0
  35. package/dist/application/JavaScriptArtifactGraphDocuments.js +108 -0
  36. package/dist/application/JavaScriptArtifactGraphEvidence.js +99 -0
  37. package/dist/application/JavaScriptArtifactGraphFindings.js +271 -0
  38. package/dist/application/JavaScriptArtifactGraphStructure.js +409 -0
  39. package/dist/application/JavaScriptArtifactReconstruction.js +77 -0
  40. package/dist/application/JavaScriptArtifactReconstructionInput.js +18 -0
  41. package/dist/application/JavaScriptReplayPlanning.js +201 -0
  42. package/dist/application/JavaScriptReplayService.js +266 -0
  43. package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
  44. package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
  45. package/dist/application/MacHopper.js +20 -4
  46. package/dist/application/ManagedApplicationGraphService.js +55 -0
  47. package/dist/application/ManagedMemberComparisonService.js +122 -0
  48. package/dist/application/ManagedNativeVerificationService.js +39 -0
  49. package/dist/application/ManagedReconstructionService.js +58 -0
  50. package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
  51. package/dist/application/PermissionAuthority.js +1 -1
  52. package/dist/application/ProcessCaptureLifecycle.js +1 -1
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProcessSampling.js +16 -12
  55. package/dist/application/ProjectPermissionStore.js +1 -0
  56. package/dist/application/SessionProviderRouter.js +219 -0
  57. package/dist/application/Setup.js +84 -60
  58. package/dist/application/SetupClients.js +1 -1
  59. package/dist/application/SetupPlan.js +9 -2
  60. package/dist/application/SupportedClients.js +40 -16
  61. package/dist/application/Upgrade.js +1 -1
  62. package/dist/application/runtime.js +11 -6
  63. package/dist/artifacts/AsarArtifactReader.js +23 -3
  64. package/dist/browser/CdpBrowserProvider.js +4 -2
  65. package/dist/browser/CdpCaptureEvents.js +49 -0
  66. package/dist/browser/CdpElectronInspection.js +106 -162
  67. package/dist/browser/CdpElectronProvider.js +9 -3
  68. package/dist/browser/CdpElectronScriptEvents.js +69 -0
  69. package/dist/browser/CdpElectronScripts.js +94 -0
  70. package/dist/browser/CdpElectronWorkers.js +56 -0
  71. package/dist/browser/CdpEndpoint.js +14 -1
  72. package/dist/browser/CdpPageCapture.js +8 -4
  73. package/dist/catalogIdentity.js +6 -42
  74. package/dist/cli.js +446 -34
  75. package/dist/cliApplicationCommands.js +62 -0
  76. package/dist/cliBrowserAdvancedCommands.js +5 -4
  77. package/dist/cliBrowserCommands.js +5 -4
  78. package/dist/cliCommandNames.js +56 -0
  79. package/dist/cliElectronCommands.js +106 -3
  80. package/dist/cliEvidenceCommands.js +4 -3
  81. package/dist/cliInvestigationCommands.js +2 -1
  82. package/dist/cliJsonInput.js +54 -0
  83. package/dist/cliPolicyCommands.js +2 -1
  84. package/dist/cliProcessCommands.js +3 -2
  85. package/dist/config.js +101 -2
  86. package/dist/contracts/applicationToolContracts.js +91 -0
  87. package/dist/contracts/artifactToolContracts.js +5 -9
  88. package/dist/contracts/browserToolContracts.js +14 -62
  89. package/dist/contracts/electronToolContracts.js +45 -19
  90. package/dist/contracts/enhancedInputs.js +2 -2
  91. package/dist/contracts/errorSchemas.js +1 -0
  92. package/dist/contracts/investigationExamples.js +0 -6
  93. package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
  94. package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
  95. package/dist/contracts/managedToolContracts.js +153 -0
  96. package/dist/contracts/managedWorkflowExamples.js +346 -0
  97. package/dist/contracts/managedWorkflowToolContracts.js +203 -0
  98. package/dist/contracts/nativeToolContracts.js +2 -6
  99. package/dist/contracts/promptContracts.js +18 -2
  100. package/dist/contracts/providerSelection.js +28 -0
  101. package/dist/contracts/sessionLifecycleInputs.js +2 -0
  102. package/dist/contracts/toolContractExamples.js +10 -25
  103. package/dist/contracts/toolContracts.js +69 -65
  104. package/dist/contracts/toolEffects.js +160 -0
  105. package/dist/contracts/toolOutputSchemas.js +152 -42
  106. package/dist/doctorRuntime.js +47 -0
  107. package/dist/domain/analysisProfile.js +78 -0
  108. package/dist/domain/analysisSnapshot.js +92 -46
  109. package/dist/domain/artifactComparison.js +3 -4
  110. package/dist/domain/artifactGraph.js +1 -0
  111. package/dist/domain/binaryTarget.js +4 -29
  112. package/dist/domain/browserObservation.js +3 -0
  113. package/dist/domain/bundleComparison.js +4 -4
  114. package/dist/domain/electronObservation.js +13 -0
  115. package/dist/domain/electronStaticAnalysis.js +9 -0
  116. package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
  117. package/dist/domain/electronStaticAnalysisIpc.js +171 -0
  118. package/dist/domain/electronStaticAnalysisNative.js +164 -0
  119. package/dist/domain/electronStaticAnalysisTypes.js +1 -0
  120. package/dist/domain/electronStaticAnalysisValues.js +103 -0
  121. package/dist/domain/errors.js +148 -7
  122. package/dist/domain/evidence.js +44 -5
  123. package/dist/domain/evidenceBundle.js +9 -7
  124. package/dist/domain/functionComparison.js +9 -3
  125. package/dist/domain/functionComparisonNormalization.js +37 -0
  126. package/dist/domain/functionComparisonSchemas.js +3 -8
  127. package/dist/domain/functionDossierEvidence.js +4 -1
  128. package/dist/domain/hopperValues.js +66 -9
  129. package/dist/domain/inputIssueProjection.js +52 -0
  130. package/dist/domain/investigationWorkspace.js +2 -1
  131. package/dist/domain/javascriptApplicationAnalysis.js +155 -0
  132. package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
  133. package/dist/domain/javascriptApplicationEvidenceSchemas.js +379 -0
  134. package/dist/domain/javascriptApplicationGraph.js +282 -0
  135. package/dist/domain/javascriptApplicationGraphSchemas.js +139 -0
  136. package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
  137. package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
  138. package/dist/domain/javascriptApplicationVersionItems.js +221 -0
  139. package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
  140. package/dist/domain/javascriptAstFingerprint.js +143 -0
  141. package/dist/domain/javascriptFeatureSeed.js +140 -0
  142. package/dist/domain/javascriptFeatureTrace.js +232 -0
  143. package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
  144. package/dist/domain/javascriptFeatureTraversal.js +88 -0
  145. package/dist/domain/javascriptNativeHandoff.js +85 -0
  146. package/dist/domain/javascriptReplay.js +361 -0
  147. package/dist/domain/javascriptRuntimeLoadState.js +106 -0
  148. package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
  149. package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
  150. package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
  151. package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
  152. package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
  153. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
  154. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
  155. package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
  156. package/dist/domain/javascriptStaticAnalysis.js +346 -0
  157. package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
  158. package/dist/domain/javascriptStaticAnalysisHelpers.js +274 -0
  159. package/dist/domain/javascriptStaticAnalysisState.js +23 -0
  160. package/dist/domain/javascriptStaticAnalysisTypes.js +1 -0
  161. package/dist/domain/managedApplicationGraph.js +556 -0
  162. package/dist/domain/managedArtifact.js +444 -0
  163. package/dist/domain/managedMemberComparison.js +565 -0
  164. package/dist/domain/managedNativeVerification.js +408 -0
  165. package/dist/domain/managedReconstruction.js +183 -0
  166. package/dist/domain/managedRuntimeCorrelation.js +175 -0
  167. package/dist/domain/reconstructionVerificationSchemas.js +1 -3
  168. package/dist/dotnet/ManagedArtifactInspector.js +318 -0
  169. package/dist/dotnet/ManagedMemberInspector.js +887 -0
  170. package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +368 -0
  172. package/dist/dotnet/ManagedMetadataLayout.js +322 -0
  173. package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
  174. package/dist/dotnet/ManagedPeReader.js +157 -0
  175. package/dist/dotnet/ManagedReaderFailure.js +10 -0
  176. package/dist/dotnet/ManagedStaticProvider.js +203 -0
  177. package/dist/generatedPackageMetadata.js +2 -2
  178. package/dist/ghidra/GhidraAnalysisProfile.js +37 -0
  179. package/dist/ghidra/GhidraClient.js +400 -0
  180. package/dist/ghidra/GhidraClientTypes.js +1 -0
  181. package/dist/ghidra/GhidraDefaults.js +20 -0
  182. package/dist/ghidra/GhidraDiagnostics.js +43 -0
  183. package/dist/ghidra/GhidraDoctor.js +59 -0
  184. package/dist/ghidra/GhidraFunctionValues.js +264 -0
  185. package/dist/ghidra/GhidraInstallation.js +239 -0
  186. package/dist/ghidra/GhidraInventoryValues.js +217 -0
  187. package/dist/ghidra/GhidraLauncher.js +139 -0
  188. package/dist/ghidra/GhidraProvider.js +366 -0
  189. package/dist/ghidra/GhidraRequestQueue.js +98 -0
  190. package/dist/ghidra/GhidraResponseBuffer.js +31 -0
  191. package/dist/ghidra/GhidraResponseRouter.js +24 -0
  192. package/dist/ghidra/GhidraSessionError.js +39 -0
  193. package/dist/ghidra/GhidraSessionValues.js +62 -0
  194. package/dist/ghidra/GhidraSocketConnection.js +46 -0
  195. package/dist/ghidra/protocol.js +69 -0
  196. package/dist/hopper/BridgeLauncher.js +19 -54
  197. package/dist/hopper/HopperAnalysisProfile.js +106 -0
  198. package/dist/hopper/HopperClient.js +154 -162
  199. package/dist/hopper/HopperProvider.js +99 -7
  200. package/dist/hopper/HopperSessionValues.js +27 -0
  201. package/dist/hopper/HopperSocketConnection.js +31 -0
  202. package/dist/main.js +14 -0
  203. package/dist/native/NativeMacOSProvider.js +3 -1
  204. package/dist/process/PendingOperations.js +59 -0
  205. package/dist/process/PrivateRuntimeRoot.js +33 -0
  206. package/dist/process/ProviderDeadline.js +95 -0
  207. package/dist/process/ProviderProcess.js +275 -0
  208. package/dist/replay/JavaScriptReplayWorker.js +286 -0
  209. package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
  210. package/dist/replay/LinuxRuntimeClosure.js +46 -0
  211. package/dist/replay/LinuxSeccompPolicy.js +40 -0
  212. package/dist/replay/ReplayWorkerProtocol.js +55 -0
  213. package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
  214. package/dist/server/createServer.js +53 -0
  215. package/dist/server/promptCompletion.js +9 -3
  216. package/dist/server/registerApplicationTools.js +115 -0
  217. package/dist/server/registerArtifactComparisonTool.js +16 -9
  218. package/dist/server/registerBrowserTools.js +33 -8
  219. package/dist/server/registerBundleComparisonTool.js +17 -20
  220. package/dist/server/registerElectronTools.js +45 -3
  221. package/dist/server/registerEnhancedTools.js +67 -19
  222. package/dist/server/registerEvidenceTools.js +5 -1
  223. package/dist/server/registerFunctionComparisonTool.js +14 -7
  224. package/dist/server/registerInvestigationTools.js +21 -17
  225. package/dist/server/registerManagedTools.js +6 -0
  226. package/dist/server/registerManagedWorkflowTools.js +277 -0
  227. package/dist/server/registerOfficialTools.js +11 -8
  228. package/dist/server/registerProcessComparisonTool.js +25 -47
  229. package/dist/server/registerSessionStatusTool.js +7 -7
  230. package/dist/server/registerSessionTools.js +57 -87
  231. package/dist/server/sessionEvidence.js +14 -19
  232. package/dist/server/toolInputValidation.js +77 -0
  233. package/dist/server/toolRegistrationOptions.js +79 -2
  234. package/dist/server/toolResult.js +25 -17
  235. package/package.json +17 -7
  236. package/scripts/rea.mjs +36 -2
  237. package/skills/rea-analysis/SKILL.md +59 -3
  238. /package/dist/{application → process}/ProcessOwnership.js +0 -0
@@ -5,6 +5,8 @@ import { logToolExecution } from "./toolLogging.js";
5
5
  import { createEvidence } from "../domain/evidence.js";
6
6
  import { AnalysisCapabilityUnavailableError, UnknownRegistryError, } from "../domain/errors.js";
7
7
  import { mcpProgressReporter } from "./mcpProgress.js";
8
+ import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
9
+ import { safeParseToolInput } from "./toolInputValidation.js";
8
10
  /** Register direct bridge proxies, preserving MCP cancellation and typed errors. */
9
11
  export const registerOfficialTools = (server, analysis, options) => {
10
12
  for (const contract of OFFICIAL_TOOL_CONTRACTS) {
@@ -17,13 +19,11 @@ export const registerOfficialTools = (server, analysis, options) => {
17
19
  }
18
20
  };
19
21
  const registerOfficialTool = (server, analysis, contract, registration) => {
20
- server.registerTool(contract.name, {
21
- description: contract.description,
22
- inputSchema: contract.inputSchema,
23
- outputSchema: contract.outputSchema,
24
- annotations: contract.annotations,
25
- }, async (input, context) => {
26
- const arguments_ = projectOfficialArguments(contract, input);
22
+ server.registerTool(contract.name, toolRegistrationOptions(contract), async (input, context) => {
23
+ const parsedInput = safeParseToolInput(contract.inputSchema, input, contract.name);
24
+ if (!parsedInput.ok)
25
+ return toCallToolResult(parsedInput, contract);
26
+ const arguments_ = projectOfficialArguments(contract, parsedInput.value);
27
27
  const progress = mcpProgressReporter(context);
28
28
  await progress.report({
29
29
  phase: contract.name,
@@ -47,6 +47,9 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
47
47
  operation: contract.name,
48
48
  parameters: arguments_,
49
49
  result: result.value.result,
50
+ ...(result.value.analysisProfile === undefined
51
+ ? {}
52
+ : { analysisProfile: result.value.analysisProfile }),
50
53
  rawResult: result.value.rawResult,
51
54
  limitations: result.value.limitations,
52
55
  locations: result.value.locations,
@@ -88,7 +91,7 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
88
91
  });
89
92
  };
90
93
  const projectOfficialArguments = (contract, input) => {
91
- const parsed = jsonObjectSchema.parse(contract.inputSchema.parse(input));
94
+ const parsed = jsonObjectSchema.parse(input);
92
95
  const projected = {};
93
96
  for (const key of Object.keys(contract.inputSchema.shape)) {
94
97
  if (key === "unknown_registry_approved")
@@ -1,35 +1,40 @@
1
- import canonicalize from "canonicalize";
2
- import { z } from "zod";
3
- import { SESSION_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
1
+ import { processComparisonInputSchema, SESSION_TOOL_CONTRACTS, } from "../contracts/toolContracts.js";
4
2
  import { createEvidence } from "../domain/evidence.js";
5
3
  import { EvidenceIntegrityError } from "../domain/errors.js";
6
4
  import { jsonValueSchema } from "../domain/jsonValue.js";
7
- import { compareProcessCaptures, processCaptureSchema, parseProcessCapture, } from "../domain/processCapture.js";
5
+ import { compareProcessCaptures, parseProcessCapture, } from "../domain/processCapture.js";
8
6
  import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
9
7
  import { err } from "../domain/result.js";
10
8
  import { PROCESS_PROVIDER } from "./sessionToolPolicies.js";
11
9
  import { toCallToolResult } from "./toolResult.js";
12
10
  import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
13
11
  import { runDerivedOperation } from "./runDerivedOperation.js";
12
+ import { safeParseToolInput } from "./toolInputValidation.js";
13
+ import { resolveSessionEvidenceIds } from "./sessionEvidence.js";
14
14
  /** Register deterministic process-capture comparison and contradiction tracking. */
15
15
  export const registerProcessComparisonTool = (server, session, contract, now = Date.now) => {
16
16
  server.registerTool(contract.name, toolRegistrationOptions(contract), async (input, context) => {
17
- const parsed = z
18
- .object({
19
- left_evidence_id: z.string(),
20
- left: processCaptureSchema,
21
- right_evidence_id: z.string(),
22
- right: processCaptureSchema,
23
- max_capture_age_ms: z.number().int().nonnegative().optional(),
24
- unknown_registry_approved: z.literal(true).optional(),
25
- })
26
- .parse(input);
27
- const validated = validateCaptureSources(session, parsed);
28
- if (!validated.ok)
29
- return toCallToolResult(validated, contract);
17
+ const parsedInput = safeParseToolInput(processComparisonInputSchema, input, contract.name);
18
+ if (!parsedInput.ok)
19
+ return toCallToolResult(parsedInput, contract);
20
+ const parsed = parsedInput.value;
21
+ const expected = {
22
+ operation: "capture_process_scenario",
23
+ predicate: "rea.process-capture/v4",
24
+ };
25
+ const leftRecord = resolveSessionEvidenceIds(session, [parsed.left_evidence_id], expected);
26
+ if (!leftRecord.ok)
27
+ return toCallToolResult(leftRecord, contract);
28
+ const rightRecord = resolveSessionEvidenceIds(session, [parsed.right_evidence_id], expected);
29
+ if (!rightRecord.ok)
30
+ return toCallToolResult(rightRecord, contract);
30
31
  let comparison;
32
+ let leftCapture;
33
+ let rightCapture;
31
34
  try {
32
- const computed = await runDerivedOperation(context, contract.name, () => compareProcessCaptures(parsed.left, parsed.right, {
35
+ leftCapture = parseProcessCapture(leftRecord.value[0]?.normalized_result);
36
+ rightCapture = parseProcessCapture(rightRecord.value[0]?.normalized_result);
37
+ const computed = await runDerivedOperation(context, contract.name, () => compareProcessCaptures(leftCapture, rightCapture, {
33
38
  ...(parsed.max_capture_age_ms === undefined
34
39
  ? {}
35
40
  : { maxCaptureAgeMs: parsed.max_capture_age_ms }),
@@ -50,8 +55,8 @@ export const registerProcessComparisonTool = (server, session, contract, now = D
50
55
  parameters: {
51
56
  left_evidence_id: parsed.left_evidence_id,
52
57
  right_evidence_id: parsed.right_evidence_id,
53
- left_normalization: parsed.left.normalization,
54
- right_normalization: parsed.right.normalization,
58
+ left_normalization: leftCapture.normalization,
59
+ right_normalization: rightCapture.normalization,
55
60
  },
56
61
  result: jsonValueSchema.parse(comparison),
57
62
  confidence: "derived",
@@ -62,33 +67,6 @@ export const registerProcessComparisonTool = (server, session, contract, now = D
62
67
  return toCallToolResult(recordDerivedEvidence(session, evidence, comparisonUnknownInput(parsed, comparison)), contract);
63
68
  });
64
69
  };
65
- const validateCaptureSources = (session, parsed) => {
66
- for (const [evidenceId, capture] of [
67
- [parsed.left_evidence_id, parsed.left],
68
- [parsed.right_evidence_id, parsed.right],
69
- ]) {
70
- const evidence = session.evidenceById(evidenceId);
71
- let evidenceCapture;
72
- try {
73
- evidenceCapture = parseProcessCapture(evidence?.normalized_result);
74
- }
75
- catch {
76
- evidenceCapture = undefined;
77
- }
78
- if (evidence === undefined ||
79
- evidence.operation !== "capture_process_scenario" ||
80
- evidence.predicate_type !== "rea.process-capture/v4" ||
81
- evidence.provider.id !== PROCESS_PROVIDER.id ||
82
- evidence.provider.name !== PROCESS_PROVIDER.name ||
83
- evidence.provider.version !== PROCESS_PROVIDER.version ||
84
- evidence.confidence !== "observed" ||
85
- evidence.authority !== "controlled-replay" ||
86
- evidenceCapture === undefined ||
87
- canonicalize(evidenceCapture) !== canonicalize(capture))
88
- return err(new EvidenceIntegrityError("Process comparison payload does not match its source Evidence"));
89
- }
90
- return { ok: true, value: null };
91
- };
92
70
  const comparisonUnknownInput = (parsed, comparison) => {
93
71
  if (parsed.unknown_registry_approved !== true ||
94
72
  comparison.status === "unchanged")
@@ -3,15 +3,15 @@ import { toCallToolResult } from "./toolResult.js";
3
3
  import { jsonObjectSchema } from "../domain/jsonValue.js";
4
4
  import { createServerIdentity } from "../serverIdentity.js";
5
5
  import { buildCapabilityInventory } from "../application/CapabilityInventory.js";
6
+ import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
7
+ import { safeParseToolInput } from "./toolInputValidation.js";
6
8
  /** Register the read-only provider and target status operation. */
7
9
  export const registerSessionStatusTool = (server, session, contract, startedAt, availabilityPolicy) => {
8
- server.registerTool(contract.name, {
9
- description: contract.description,
10
- inputSchema: contract.inputSchema,
11
- outputSchema: contract.outputSchema,
12
- annotations: contract.annotations,
13
- }, (input) => {
14
- const parsed = binarySessionInputSchema.parse(input);
10
+ server.registerTool(contract.name, toolRegistrationOptions(contract), (input) => {
11
+ const parsedInput = safeParseToolInput(binarySessionInputSchema, input, contract.name);
12
+ if (!parsedInput.ok)
13
+ return toCallToolResult(parsedInput, contract);
14
+ const parsed = parsedInput.value;
15
15
  const client = server.server.getClientVersion();
16
16
  const clientCapabilities = server.server.getClientCapabilities();
17
17
  const status = session.status();
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { SESSION_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
2
+ import { exportEvidenceBundleInputSchema, importEvidenceBundleInputSchema, listUnknownsInputSchema, SESSION_TOOL_CONTRACTS, verifyUnknownResolutionInputSchema, } from "../contracts/toolContracts.js";
3
3
  import { toCallToolResult } from "./toolResult.js";
4
4
  import { logToolExecution } from "./toolLogging.js";
5
5
  import { err, ok } from "../domain/result.js";
@@ -14,6 +14,8 @@ import { createProcessCaptureEvidence } from "../application/ProcessEvidence.js"
14
14
  import { registerProcessComparisonTool } from "./registerProcessComparisonTool.js";
15
15
  import { registerArtifactComparisonTool } from "./registerArtifactComparisonTool.js";
16
16
  import { registerFunctionComparisonTool } from "./registerFunctionComparisonTool.js";
17
+ import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
18
+ import { safeParseToolInput } from "./toolInputValidation.js";
17
19
  import { registerBundleComparisonTool } from "./registerBundleComparisonTool.js";
18
20
  import { registerInvestigationTools } from "./registerInvestigationTools.js";
19
21
  import { closeBinaryInputSchema, openBinaryInputSchema, } from "../contracts/sessionLifecycleInputs.js";
@@ -58,13 +60,11 @@ const recordProcessResidualUnknowns = (session, scenario, evidence, residuals) =
58
60
  return ok(null);
59
61
  };
60
62
  const registerProcessTools = ({ server, session, logger, processPolicy, captureContract, permissionAuthority, }) => {
61
- server.registerTool(captureContract.name, {
62
- description: captureContract.description,
63
- inputSchema: captureContract.inputSchema,
64
- outputSchema: captureContract.outputSchema,
65
- annotations: captureContract.annotations,
66
- }, async (input, context) => {
67
- const scenario = processScenarioSchema.parse(input);
63
+ server.registerTool(captureContract.name, toolRegistrationOptions(captureContract), async (input, context) => {
64
+ const parsedInput = safeParseToolInput(processScenarioSchema, input, captureContract.name);
65
+ if (!parsedInput.ok)
66
+ return toCallToolResult(parsedInput, captureContract);
67
+ const scenario = parsedInput.value;
68
68
  if (permissionAuthority !== undefined) {
69
69
  const authorized = await permissionAuthority.authorize({
70
70
  capability: "process_capture",
@@ -109,18 +109,11 @@ const registerProcessTools = ({ server, session, logger, processPolicy, captureC
109
109
  });
110
110
  };
111
111
  const registerEvidenceTools = ({ server, session, exportContract, importContract, filePolicy, permissionAuthority, }) => {
112
- server.registerTool(exportContract.name, {
113
- description: exportContract.description,
114
- inputSchema: exportContract.inputSchema,
115
- outputSchema: exportContract.outputSchema,
116
- annotations: exportContract.annotations,
117
- }, async (input) => {
118
- const parsed = z
119
- .object({
120
- path: z.string().min(1).optional(),
121
- overwrite: z.boolean().default(false),
122
- })
123
- .parse(input);
112
+ server.registerTool(exportContract.name, toolRegistrationOptions(exportContract), async (input) => {
113
+ const parsedInput = safeParseToolInput(exportEvidenceBundleInputSchema, input, exportContract.name);
114
+ if (!parsedInput.ok)
115
+ return toCallToolResult(parsedInput, exportContract);
116
+ const parsed = parsedInput.value;
124
117
  const bundle = session.exportEvidenceBundle();
125
118
  if (parsed.path === undefined)
126
119
  return toCallToolResult(ok(bundle), exportContract);
@@ -146,13 +139,11 @@ const registerEvidenceTools = ({ server, session, exportContract, importContract
146
139
  }), exportContract)
147
140
  : toCallToolResult(written, exportContract);
148
141
  });
149
- server.registerTool(importContract.name, {
150
- description: importContract.description,
151
- inputSchema: importContract.inputSchema,
152
- outputSchema: importContract.outputSchema,
153
- annotations: importContract.annotations,
154
- }, async (input) => {
155
- const path = z.object({ path: z.string().min(1) }).parse(input).path;
142
+ server.registerTool(importContract.name, toolRegistrationOptions(importContract), async (input) => {
143
+ const parsedInput = safeParseToolInput(importEvidenceBundleInputSchema, input, importContract.name);
144
+ if (!parsedInput.ok)
145
+ return toCallToolResult(parsedInput, importContract);
146
+ const path = parsedInput.value.path;
156
147
  if (permissionAuthority !== undefined) {
157
148
  const authorized = await permissionAuthority.authorize({
158
149
  capability: "evidence_read",
@@ -181,27 +172,11 @@ const registerEvidenceTools = ({ server, session, exportContract, importContract
181
172
  });
182
173
  };
183
174
  const registerUnknownTools = ({ server, session, contracts: [listContract, recordContract, updateContract, verifyContract], }) => {
184
- server.registerTool(listContract.name, {
185
- description: listContract.description,
186
- inputSchema: listContract.inputSchema,
187
- outputSchema: listContract.outputSchema,
188
- annotations: listContract.annotations,
189
- }, (input) => {
190
- const filters = z
191
- .object({
192
- status: z
193
- .enum([
194
- "open",
195
- "investigating",
196
- "blocked",
197
- "contradicted",
198
- "resolved",
199
- ])
200
- .optional(),
201
- severity: z.enum(["low", "medium", "high", "critical"]).optional(),
202
- domain: z.string().trim().min(1).max(100).optional(),
203
- })
204
- .parse(input);
175
+ server.registerTool(listContract.name, toolRegistrationOptions(listContract), (input) => {
176
+ const parsedInput = safeParseToolInput(listUnknownsInputSchema, input, listContract.name);
177
+ if (!parsedInput.ok)
178
+ return toCallToolResult(parsedInput, listContract);
179
+ const filters = parsedInput.value;
205
180
  return toCallToolResult(ok(session.listUnknowns({
206
181
  ...(filters.status === undefined ? {} : { status: filters.status }),
207
182
  ...(filters.severity === undefined
@@ -210,49 +185,39 @@ const registerUnknownTools = ({ server, session, contracts: [listContract, recor
210
185
  ...(filters.domain === undefined ? {} : { domain: filters.domain }),
211
186
  })), listContract);
212
187
  });
213
- server.registerTool(recordContract.name, {
214
- description: recordContract.description,
215
- inputSchema: recordContract.inputSchema,
216
- outputSchema: recordContract.outputSchema,
217
- annotations: recordContract.annotations,
218
- }, (input) => {
219
- const recorded = session.recordUnknown(recordUnknownInputSchema.parse(input));
188
+ server.registerTool(recordContract.name, toolRegistrationOptions(recordContract), (input) => {
189
+ const parsedInput = safeParseToolInput(recordUnknownInputSchema, input, recordContract.name);
190
+ if (!parsedInput.ok)
191
+ return toCallToolResult(parsedInput, recordContract);
192
+ const recorded = session.recordUnknown(parsedInput.value);
220
193
  if (recorded.ok)
221
194
  server.sendResourceListChanged();
222
195
  return toCallToolResult(recorded, recordContract);
223
196
  });
224
- server.registerTool(updateContract.name, {
225
- description: updateContract.description,
226
- inputSchema: updateContract.inputSchema,
227
- outputSchema: updateContract.outputSchema,
228
- annotations: updateContract.annotations,
229
- }, (input) => {
230
- const updated = session.updateUnknown(updateUnknownInputSchema.parse(input));
197
+ server.registerTool(updateContract.name, toolRegistrationOptions(updateContract), (input) => {
198
+ const parsedInput = safeParseToolInput(updateUnknownInputSchema, input, updateContract.name);
199
+ if (!parsedInput.ok)
200
+ return toCallToolResult(parsedInput, updateContract);
201
+ const updated = session.updateUnknown(parsedInput.value);
231
202
  if (updated.ok)
232
203
  server.sendResourceListChanged();
233
204
  return toCallToolResult(updated, updateContract);
234
205
  });
235
- server.registerTool(verifyContract.name, {
236
- description: verifyContract.description,
237
- inputSchema: verifyContract.inputSchema,
238
- outputSchema: verifyContract.outputSchema,
239
- annotations: verifyContract.annotations,
240
- }, (input) => {
241
- const unknownId = z
242
- .object({ unknown_id: z.string().regex(/^unk_[a-f0-9]{64}$/u) })
243
- .parse(input).unknown_id;
206
+ server.registerTool(verifyContract.name, toolRegistrationOptions(verifyContract), (input) => {
207
+ const parsedInput = safeParseToolInput(verifyUnknownResolutionInputSchema, input, verifyContract.name);
208
+ if (!parsedInput.ok)
209
+ return toCallToolResult(parsedInput, verifyContract);
210
+ const unknownId = parsedInput.value.unknown_id;
244
211
  return toCallToolResult(session.verifyUnknownResolution(unknownId), verifyContract);
245
212
  });
246
213
  };
247
214
  const registerLifecycleTools = ({ server, session, logger, contracts, snapshotFilePolicy, startedAt, availabilityPolicy, permissionAuthority, }) => {
248
215
  const [openContract, closeContract, statusContract] = contracts;
249
- server.registerTool(openContract.name, {
250
- description: openContract.description,
251
- inputSchema: openContract.inputSchema,
252
- outputSchema: openContract.outputSchema,
253
- annotations: openContract.annotations,
254
- }, async (input, context) => {
255
- const parsed = openBinaryInputSchema.parse(input);
216
+ server.registerTool(openContract.name, toolRegistrationOptions(openContract), async (input, context) => {
217
+ const parsedInput = safeParseToolInput(openBinaryInputSchema, input, openContract.name);
218
+ if (!parsedInput.ok)
219
+ return toCallToolResult(parsedInput, openContract);
220
+ const parsed = parsedInput.value;
256
221
  let snapshot;
257
222
  if (parsed.snapshot_path !== undefined) {
258
223
  if (permissionAuthority !== undefined) {
@@ -275,6 +240,9 @@ const registerLifecycleTools = ({ server, session, logger, contracts, snapshotFi
275
240
  }
276
241
  const opened = await logToolExecution(logger, openContract.name, () => session.open(parsed.path, {
277
242
  signal: context.mcpReq.signal,
243
+ ...(parsed.provider_id === undefined
244
+ ? {}
245
+ : { providerId: parsed.provider_id }),
278
246
  ...(snapshot === undefined ? {} : { snapshot }),
279
247
  }));
280
248
  if (opened.ok)
@@ -286,20 +254,20 @@ const registerLifecycleTools = ({ server, session, logger, contracts, snapshotFi
286
254
  path: opened.value.path,
287
255
  format: opened.value.format,
288
256
  kind: opened.value.kind,
289
- loaderArgs: [...opened.value.loaderArgs],
257
+ loaderArgs: z
258
+ .array(z.string())
259
+ .parse(session.openCompatibility().loaderArgs ?? []),
290
260
  sha256: opened.value.sha256,
291
261
  architecture: opened.value.architecture ?? null,
292
262
  },
293
263
  }, openContract)
294
264
  : toCallToolResult(opened, openContract);
295
265
  });
296
- server.registerTool(closeContract.name, {
297
- description: closeContract.description,
298
- inputSchema: closeContract.inputSchema,
299
- outputSchema: closeContract.outputSchema,
300
- annotations: closeContract.annotations,
301
- }, async (input) => {
302
- const parsed = closeBinaryInputSchema.parse(input);
266
+ server.registerTool(closeContract.name, toolRegistrationOptions(closeContract), async (input) => {
267
+ const parsedInput = safeParseToolInput(closeBinaryInputSchema, input, closeContract.name);
268
+ if (!parsedInput.ok)
269
+ return toCallToolResult(parsedInput, closeContract);
270
+ const parsed = parsedInput.value;
303
271
  if (parsed.snapshot_path !== undefined) {
304
272
  if (permissionAuthority !== undefined) {
305
273
  const authorized = await permissionAuthority.authorize({
@@ -356,6 +324,8 @@ export const registerSessionTools = (server, session, logger, options = {}) => {
356
324
  evidenceFileRoots: evidenceFilePolicy.roots.length,
357
325
  browserObservationEnabled: false,
358
326
  electronObservationEnabled: false,
327
+ javascriptReplayEnabled: false,
328
+ managedRuntimeEnabled: false,
359
329
  })),
360
330
  ...(options.permissionAuthority === undefined
361
331
  ? {}
@@ -384,7 +354,7 @@ export const registerSessionTools = (server, session, logger, options = {}) => {
384
354
  registerProcessComparisonTool(server, session, compareContract);
385
355
  registerArtifactComparisonTool(server, session, compareArtifactsContract);
386
356
  registerFunctionComparisonTool(server, session, compareFunctionsContract);
387
- registerBundleComparisonTool(server, session, compareBundlesContract);
357
+ registerBundleComparisonTool(server, session, compareBundlesContract, evidenceFilePolicy);
388
358
  const investigationContracts = [
389
359
  changedBehaviorContract,
390
360
  callPathContract,
@@ -1,22 +1,17 @@
1
- import canonicalize from "canonicalize";
2
- import { EvidenceIntegrityError } from "../domain/errors.js";
3
- import { parseEvidence } from "../domain/evidence.js";
1
+ import { EvidenceIntegrityError, EvidenceReferenceError, } from "../domain/errors.js";
4
2
  import { err, ok } from "../domain/result.js";
5
- /** Resolve caller-supplied records to their immutable session-owned values. */
6
- export const resolveSessionEvidence = (session, input) => {
7
- const supplied = Array.isArray(input) ? input : [input];
8
- const authoritative = [];
9
- for (const item of supplied) {
10
- const parsed = parseEvidence(item);
11
- const owned = session.evidenceById(parsed.evidence_id);
12
- if (owned === undefined || canonicalize(owned) !== canonicalize(parsed))
13
- return err(new EvidenceIntegrityError("Comparison input does not match its session-owned Evidence"));
14
- authoritative.push(owned);
3
+ /** Resolve bounded IDs to authoritative records with exact semantic identity. */
4
+ export const resolveSessionEvidenceIds = (session, evidenceIds, expected) => {
5
+ const records = [];
6
+ for (const evidenceId of evidenceIds) {
7
+ const record = session.evidenceById(evidenceId);
8
+ if (record === undefined)
9
+ return err(new EvidenceReferenceError(evidenceId, "missing", expected.operation, null));
10
+ if (record.operation !== expected.operation)
11
+ return err(new EvidenceReferenceError(evidenceId, "wrong_operation", expected.operation, record.operation));
12
+ if (record.predicate_type !== expected.predicate)
13
+ return err(new EvidenceReferenceError(evidenceId, "wrong_predicate", expected.predicate, record.predicate_type));
14
+ records.push(record);
15
15
  }
16
- return ok(authoritative);
17
- };
18
- /** Check exact ownership for a single Evidence record. */
19
- export const isSessionEvidence = (session, input) => {
20
- const owned = session.evidenceById(input.evidence_id);
21
- return owned !== undefined && canonicalize(owned) === canonicalize(input);
16
+ return ok(records);
22
17
  };
@@ -0,0 +1,77 @@
1
+ import { z } from "zod";
2
+ import { AnalysisInputError, } from "../domain/errors.js";
3
+ import { projectInputIssues } from "../domain/inputIssueProjection.js";
4
+ import { err, ok } from "../domain/result.js";
5
+ import { closeObjectSchemas } from "./toolRegistrationOptions.js";
6
+ /** Parse one MCP request exactly once and retain only schema-owned correction data. */
7
+ export const safeParseToolInput = (schema, input, operation) => {
8
+ const unknownIssues = unknownArgumentIssues(schema, input);
9
+ if (unknownIssues.length > 0)
10
+ return err(new AnalysisInputError(operation, undefined, unknownIssues));
11
+ const parsed = schema.safeParse(input);
12
+ return parsed.success
13
+ ? ok(parsed.data)
14
+ : err(new AnalysisInputError(operation, undefined, projectInputIssues(parsed.error.issues, input)));
15
+ };
16
+ const schemaCache = new WeakMap();
17
+ const unknownArgumentIssues = (schema, input) => {
18
+ let jsonSchema = schemaCache.get(schema);
19
+ if (jsonSchema === undefined) {
20
+ const converter = schema["~standard"].jsonSchema?.input;
21
+ if (converter === undefined)
22
+ throw new TypeError("Tool input schema does not expose Standard JSON Schema");
23
+ jsonSchema = closeObjectSchemas(converter({ target: "draft-2020-12" }));
24
+ schemaCache.set(schema, jsonSchema);
25
+ }
26
+ return scanUnknownArguments(input, jsonSchema, jsonSchema, []);
27
+ };
28
+ const scanUnknownArguments = (value, schema, root, path) => {
29
+ const resolved = resolveSchema(schema, root);
30
+ if (Array.isArray(value)) {
31
+ const items = objectValue(resolved.items);
32
+ return items === undefined
33
+ ? []
34
+ : value.flatMap((item, index) => scanUnknownArguments(item, items, root, [...path, index]));
35
+ }
36
+ if (!isObject(value))
37
+ return [];
38
+ const variants = schemaVariants(resolved, root);
39
+ const properties = Object.fromEntries(variants.flatMap((variant) => Object.entries(objectValue(variant.properties) ?? {})));
40
+ const closed = variants.length > 0 &&
41
+ variants.every((variant) => variant.additionalProperties !== true &&
42
+ !isObject(variant.additionalProperties));
43
+ const issues = [];
44
+ for (const [key, child] of Object.entries(value)) {
45
+ const propertySchema = objectValue(properties[key]);
46
+ if (propertySchema === undefined) {
47
+ if (closed)
48
+ issues.push({ path: [...path, key], reason: "unknown_argument" });
49
+ continue;
50
+ }
51
+ issues.push(...scanUnknownArguments(child, propertySchema, root, [...path, key]));
52
+ }
53
+ return issues;
54
+ };
55
+ const schemaVariants = (schema, root) => {
56
+ const resolved = resolveSchema(schema, root);
57
+ const variants = [resolved.oneOf, resolved.anyOf, resolved.allOf]
58
+ .flatMap((value) => (Array.isArray(value) ? value : []))
59
+ .flatMap((value) => {
60
+ const object = objectValue(value);
61
+ return object === undefined ? [] : schemaVariants(object, root);
62
+ });
63
+ if (variants.length > 0)
64
+ return variants;
65
+ return resolved.type === "object" || isObject(resolved.properties)
66
+ ? [resolved]
67
+ : [];
68
+ };
69
+ const resolveSchema = (schema, root) => {
70
+ const reference = schema.$ref;
71
+ if (typeof reference !== "string" || !reference.startsWith("#/$defs/"))
72
+ return schema;
73
+ const definition = objectValue(objectValue(root.$defs)?.[reference.slice(8)]);
74
+ return definition === undefined ? schema : definition;
75
+ };
76
+ const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
77
+ const objectValue = (value) => isObject(value) ? value : undefined;
@@ -1,7 +1,84 @@
1
+ import { z } from "zod";
1
2
  /** Project the public contract fields accepted by MCP tool registration. */
2
3
  export const toolRegistrationOptions = (contract) => ({
4
+ title: contract.title,
3
5
  description: contract.description,
4
- inputSchema: contract.inputSchema,
5
- outputSchema: contract.outputSchema,
6
+ inputSchema: advertisedInputSchema(contract.inputSchema),
7
+ outputSchema: advertisedOutputSchema(contract.outputSchema),
6
8
  annotations: contract.annotations,
7
9
  });
10
+ /**
11
+ * Advertise the exact Zod contract while leaving validation to REA's adapter.
12
+ * The SDK otherwise converts validation failures to text-only JSON-RPC errors
13
+ * before REA can return its stable structured error envelope.
14
+ */
15
+ const advertisedInputSchema = (schema) => ({
16
+ "~standard": {
17
+ version: 1,
18
+ vendor: "rea",
19
+ validate: (input) => ({ value: input }),
20
+ jsonSchema: {
21
+ input: () => describeProperties(closeObjectSchemas(inputJsonSchema(schema))),
22
+ output: () => describeProperties(closeObjectSchemas(inputJsonSchema(schema))),
23
+ },
24
+ },
25
+ });
26
+ const advertisedOutputSchema = (schema) => {
27
+ const standard = schema["~standard"];
28
+ const converter = standard.jsonSchema?.output;
29
+ if (converter === undefined)
30
+ throw new TypeError("Tool output schema does not expose Standard JSON Schema");
31
+ return {
32
+ "~standard": {
33
+ ...standard,
34
+ jsonSchema: {
35
+ input: () => closeObjectSchemas(converter({ target: "draft-2020-12" })),
36
+ output: () => closeObjectSchemas(converter({ target: "draft-2020-12" })),
37
+ },
38
+ },
39
+ };
40
+ };
41
+ const inputJsonSchema = (schema) => {
42
+ const converter = schema["~standard"].jsonSchema?.input;
43
+ if (converter === undefined)
44
+ throw new TypeError("Tool input schema does not expose Standard JSON Schema");
45
+ return converter({ target: "draft-2020-12" });
46
+ };
47
+ /** Close fixed-shape JSON Schema objects while preserving explicit maps. */
48
+ export const closeObjectSchemas = (value) => {
49
+ const closed = Object.fromEntries(Object.entries(value).map(([key, child]) => [key, closeSchemaValue(child)]));
50
+ return value.type === "object" &&
51
+ isObject(value.properties) &&
52
+ value.additionalProperties === undefined
53
+ ? { ...closed, additionalProperties: false }
54
+ : closed;
55
+ };
56
+ const closeSchemaValue = (value) => {
57
+ if (Array.isArray(value))
58
+ return value.map(closeSchemaValue);
59
+ if (!isObject(value))
60
+ return value;
61
+ return closeObjectSchemas(value);
62
+ };
63
+ const describeProperties = (value) => Object.fromEntries(Object.entries(value).map(([key, child]) => {
64
+ if (key !== "properties" || typeof child !== "object" || child === null)
65
+ return [key, describeValue(child)];
66
+ return [
67
+ key,
68
+ Object.fromEntries(Object.entries(child).map(([property, schema]) => [
69
+ property,
70
+ isObject(schema) && typeof schema.description !== "string"
71
+ ? {
72
+ ...describeProperties(schema),
73
+ description: property.replaceAll("_", " "),
74
+ }
75
+ : describeValue(schema),
76
+ ])),
77
+ ];
78
+ }));
79
+ const describeValue = (value) => {
80
+ if (Array.isArray(value))
81
+ return value.map(describeValue);
82
+ return isObject(value) ? describeProperties(value) : value;
83
+ };
84
+ const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);