rea-agents 2.3.0 → 2.5.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 (209) hide show
  1. package/README.md +80 -18
  2. package/bridge/ghidra/ReaGhidraBridge.java +48 -1
  3. package/bridge/hopper_bridge.py +259 -24
  4. package/dist/application/AnalysisClientCleanup.js +14 -0
  5. package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
  6. package/dist/application/ArtifactExtraction.js +5 -1
  7. package/dist/application/ArtifactGraphConstruction.js +3 -15
  8. package/dist/application/ArtifactInventory/classify.js +5 -1
  9. package/dist/application/ArtifactInventory/reader.js +2 -0
  10. package/dist/application/BinarySession.js +15 -4
  11. package/dist/application/BinarySessionStatus.js +32 -1
  12. package/dist/application/BrowserEvidence.js +14 -0
  13. package/dist/application/BrowserScenarioCapturePort.js +1 -0
  14. package/dist/application/BrowserScenarioCaptureService.js +53 -0
  15. package/dist/application/BrowserScenarioEvidence.js +40 -0
  16. package/dist/application/CallPathTracing.js +160 -0
  17. package/dist/application/CapabilityInventory.js +79 -15
  18. package/dist/application/CompositeProvider.js +10 -1
  19. package/dist/application/DirectAnalysis.js +7 -2
  20. package/dist/application/EnhancedLiteralTracing.js +118 -0
  21. package/dist/application/EnhancedTools.js +35 -121
  22. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
  23. package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
  24. package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
  25. package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
  26. package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
  27. package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
  28. package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
  29. package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
  30. package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
  31. package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
  32. package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
  33. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
  34. package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
  35. package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
  36. package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
  37. package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
  38. package/dist/application/ProcessCaptureEnvironment.js +26 -0
  39. package/dist/application/ProcessCaptureLifecycle.js +1 -0
  40. package/dist/application/ProcessCheckpoints.js +5 -0
  41. package/dist/application/ProcessHarness.js +45 -34
  42. package/dist/application/ProcessNormalization.js +6 -0
  43. package/dist/application/ProcessReactiveCoordinator.js +10 -3
  44. package/dist/application/ProcessReactiveEffects.js +15 -1
  45. package/dist/application/ProcessReactiveHarness.js +100 -0
  46. package/dist/application/ProcessReactiveObservations.js +101 -16
  47. package/dist/application/ProjectPermissionStore.js +1 -0
  48. package/dist/application/ReconstructionObligationAuthority.js +72 -0
  49. package/dist/application/ReconstructionObligationCandidates.js +265 -0
  50. package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
  51. package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
  52. package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
  53. package/dist/application/ReconstructionObligationLedgerService.js +72 -0
  54. package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
  55. package/dist/application/ReconstructionObligationPolicies.js +132 -0
  56. package/dist/application/ReconstructionReadinessService.js +48 -0
  57. package/dist/artifacts/ArtifactProvider.js +87 -22
  58. package/dist/browser/BrowserScenarioSecrets.js +53 -0
  59. package/dist/browser/BrowserScenarioSessionPort.js +1 -0
  60. package/dist/browser/CdpBrowserProvider.js +2 -2
  61. package/dist/browser/CdpCaptureDocuments.js +57 -0
  62. package/dist/browser/CdpCaptureStorage.js +71 -29
  63. package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
  64. package/dist/browser/CdpEndpoint.js +8 -6
  65. package/dist/browser/CdpPageCapture.js +2 -0
  66. package/dist/browser/JavaScriptRuntimeScope.js +38 -0
  67. package/dist/browser/PlaywrightBrowserScenarioProvider.js +253 -0
  68. package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
  69. package/dist/browser/PlaywrightScenarioActions.js +62 -0
  70. package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
  71. package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
  72. package/dist/browser/PlaywrightScenarioEvents.js +301 -0
  73. package/dist/browser/PlaywrightScenarioSession.js +300 -0
  74. package/dist/browser/V8InspectorCaptureProjection.js +110 -0
  75. package/dist/browser/V8InspectorEndpoint.js +72 -0
  76. package/dist/browser/V8InspectorProvider.js +267 -0
  77. package/dist/catalogIdentity.js +8 -0
  78. package/dist/cli/artifactCommands.js +39 -0
  79. package/dist/cli/coreAnalysisCommands.js +37 -0
  80. package/dist/cli.js +4 -0
  81. package/dist/cliApplicationCommands.js +30 -2
  82. package/dist/cliBrowserAdvancedCommands.js +17 -5
  83. package/dist/cliBrowserCommands.js +1 -0
  84. package/dist/cliBrowserScenarioCommands.js +43 -0
  85. package/dist/cliCommandNames.js +8 -0
  86. package/dist/cliJavaScriptRuntimeCommands.js +164 -0
  87. package/dist/cliObservationOptions.js +4 -0
  88. package/dist/cliPolicyCommands.js +1 -0
  89. package/dist/config/environment.js +12 -0
  90. package/dist/config/parseConfig.js +88 -17
  91. package/dist/contracts/analysisSearchContract.js +14 -0
  92. package/dist/contracts/applicationToolContracts.js +86 -2
  93. package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
  94. package/dist/contracts/artifactToolContracts.js +22 -0
  95. package/dist/contracts/browserProviderToolContracts.js +7 -0
  96. package/dist/contracts/browserScenarioToolContracts.js +81 -0
  97. package/dist/contracts/browserToolContracts.js +88 -98
  98. package/dist/contracts/enhancedInputs.js +25 -6
  99. package/dist/contracts/functionInstructionContract.js +8 -0
  100. package/dist/contracts/hopperMemoryContracts.js +22 -0
  101. package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
  102. package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
  103. package/dist/contracts/reconstructionReadinessExample.js +436 -0
  104. package/dist/contracts/toolContractExamples.js +5 -0
  105. package/dist/contracts/toolContractTypes.js +1 -0
  106. package/dist/contracts/toolContracts.js +13 -21
  107. package/dist/contracts/toolEffects.js +21 -0
  108. package/dist/contracts/toolOutputSchemaGroups.js +70 -20
  109. package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
  110. package/dist/domain/analysisErrorProjection.js +8 -0
  111. package/dist/domain/analysisSnapshot.js +2 -0
  112. package/dist/domain/artifactGraph.js +2 -0
  113. package/dist/domain/artifactInspection.js +347 -0
  114. package/dist/domain/binaryTarget.js +3 -6
  115. package/dist/domain/browserCaptureComparison.js +82 -0
  116. package/dist/domain/browserObservation.js +8 -1
  117. package/dist/domain/browserObservationErrors.js +1 -0
  118. package/dist/domain/browserObservationSchemas.js +26 -0
  119. package/dist/domain/browserScenario.js +165 -0
  120. package/dist/domain/browserScenarioCapture.js +57 -0
  121. package/dist/domain/browserScenarioCaptureValues.js +204 -0
  122. package/dist/domain/browserScenarioDiff.js +324 -0
  123. package/dist/domain/browserScenarioDiffValues.js +157 -0
  124. package/dist/domain/browserScenarioNormalization.js +45 -0
  125. package/dist/domain/browserScenarioValues.js +372 -0
  126. package/dist/domain/evidence.js +2 -0
  127. package/dist/domain/hopperValues.js +10 -0
  128. package/dist/domain/javascriptRuntimeObservation.js +170 -0
  129. package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
  130. package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
  131. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
  132. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  133. package/dist/domain/javascriptSemanticAnalysis.js +5 -4
  134. package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
  135. package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
  136. package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
  137. package/dist/domain/javascriptSemanticDataEffects.js +284 -0
  138. package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
  139. package/dist/domain/javascriptSemanticFingerprints.js +174 -0
  140. package/dist/domain/javascriptSemanticIr.js +21 -1
  141. package/dist/domain/javascriptSemanticObjects.js +115 -0
  142. package/dist/domain/javascriptSemanticPromises.js +290 -0
  143. package/dist/domain/javascriptSemanticResources.js +136 -0
  144. package/dist/domain/javascriptStaticAnalysis.js +4 -3
  145. package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
  146. package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
  147. package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
  148. package/dist/domain/processCapture.js +71 -1
  149. package/dist/domain/processCaptureReactiveValidation.js +175 -0
  150. package/dist/domain/processCaptureValidation.js +2 -0
  151. package/dist/domain/processObservation.js +15 -6
  152. package/dist/domain/processReactiveRuntime.js +21 -10
  153. package/dist/domain/processReactiveScenario.js +1 -0
  154. package/dist/domain/processReactiveTransition.js +17 -10
  155. package/dist/domain/processScenario.js +21 -0
  156. package/dist/domain/providerCleanupError.js +11 -0
  157. package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
  158. package/dist/domain/reconstructionReadiness.js +121 -0
  159. package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
  160. package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
  161. package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
  162. package/dist/domain/reconstructionReadinessSchemas.js +240 -0
  163. package/dist/domain/sourceToBundleComparison.js +335 -0
  164. package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
  165. package/dist/domain/sourceToBundleSignals.js +202 -0
  166. package/dist/domain/webCaptureDiff.js +141 -27
  167. package/dist/domain/webScreenshot.js +1 -1
  168. package/dist/domain/zipPackageFormat.js +20 -0
  169. package/dist/generatedPackageMetadata.js +1 -1
  170. package/dist/ghidra/GhidraDefaults.js +1 -1
  171. package/dist/ghidra/GhidraFunctionValues.js +13 -1
  172. package/dist/ghidra/GhidraProviderCapabilities.js +7 -0
  173. package/dist/hopper/HopperCleanup.js +158 -0
  174. package/dist/hopper/HopperClient.js +69 -99
  175. package/dist/hopper/HopperProvider.js +20 -1
  176. package/dist/hopper/HopperRequestQueue.js +187 -0
  177. package/dist/hopper/HopperResponseStream.js +46 -0
  178. package/dist/hopper/protocol.js +1 -0
  179. package/dist/main/transport.js +24 -24
  180. package/dist/main.js +6 -0
  181. package/dist/mcpDoctor.js +25 -2
  182. package/dist/native/NativeMacOSProvider.js +1 -0
  183. package/dist/process/ProcessCommandIdentity.js +19 -0
  184. package/dist/process/ProcessOwnership.js +11 -8
  185. package/dist/server/DynamicToolAvailability.js +70 -0
  186. package/dist/server/createServer.js +46 -1
  187. package/dist/server/lifecycleProgress.js +10 -0
  188. package/dist/server/permissionFailure.js +12 -0
  189. package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
  190. package/dist/server/registerApplicationTools/helpers.js +11 -7
  191. package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
  192. package/dist/server/registerApplicationTools/readiness.js +55 -0
  193. package/dist/server/registerApplicationTools.js +6 -0
  194. package/dist/server/registerArtifactTools.js +9 -1
  195. package/dist/server/registerBrowserScenarioTool.js +25 -0
  196. package/dist/server/registerBrowserTools.js +2 -2
  197. package/dist/server/registerCloseLifecycleTool.js +57 -0
  198. package/dist/server/registerEnhancedTools.js +16 -5
  199. package/dist/server/registerEvidenceResources.js +4 -0
  200. package/dist/server/registerEvidenceTools.js +9 -2
  201. package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
  202. package/dist/server/registerReconstructionObligationLedgerResource.js +39 -0
  203. package/dist/server/registerReconstructionReadinessResource.js +39 -0
  204. package/dist/server/registerSessionStatusTool.js +11 -8
  205. package/dist/server/registerSessionTools.js +11 -57
  206. package/dist/server/sessionAvailabilityPolicy.js +10 -8
  207. package/package.json +12 -6
  208. package/skills/reverse-engineer-anything/SKILL.md +4 -3
  209. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +2 -2
package/dist/mcpDoctor.js CHANGED
@@ -61,10 +61,11 @@ const inspectProductionMcpSession = async (client, deadline, signal) => {
61
61
  client.listResources(undefined, request),
62
62
  client.listResourceTemplates(undefined, request),
63
63
  client.readResource({ uri: "rea://server/identity" }, request),
64
- client.callTool({ name: "binary_session", arguments: {} }, request),
64
+ client.callTool({ name: "binary_session", arguments: { detail: "full" } }, request),
65
65
  ]);
66
+ const expectedToolNames = availableToolNames(requestFlow.structuredContent) ?? [];
66
67
  const inventory = {
67
- tools: compareInventory(CATALOG_IDENTITY.tools.map(({ name }) => name), tools.tools.map(({ name }) => name)),
68
+ tools: compareInventory(expectedToolNames, tools.tools.map(({ name }) => name)),
68
69
  prompts: compareInventory(PROMPT_CONTRACTS.map(({ name }) => name), prompts.prompts.map(({ name }) => name)),
69
70
  resources: compareInventory(MCP_RESOURCE_CATALOG.map(({ name }) => name), resources.resources.map(({ name }) => name)),
70
71
  resource_templates: compareInventory(MCP_RESOURCE_TEMPLATE_CATALOG.map(({ name }) => name), templates.resourceTemplates.map(({ name }) => name)),
@@ -116,6 +117,28 @@ const inspectProductionMcpSession = async (client, deadline, signal) => {
116
117
  checks,
117
118
  };
118
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
+ };
119
142
  /**
120
143
  * Dispatcher entry for `rea mcp doctor`, kept separate from Incur MCP setup.
121
144
  * @public
@@ -35,6 +35,7 @@ export class NativeMacOSProvider {
35
35
  ? { available: true, reason: null }
36
36
  : {
37
37
  available: false,
38
+ availabilityCode: "unsupported_host",
38
39
  reason: "Native macOS utilities require macOS.",
39
40
  };
40
41
  return Object.freeze({
@@ -0,0 +1,19 @@
1
+ import { basename } from "node:path";
2
+ /** Match process-table command text while handling macOS executable-name normalization. */
3
+ export const matchesOwnedProcessCommand = (actual, expected, platform = process.platform) => {
4
+ const actualCommand = actual.trim();
5
+ const expectedCommand = expected.trim();
6
+ if (actualCommand === expectedCommand ||
7
+ actualCommand.startsWith(`${expectedCommand} `))
8
+ return true;
9
+ if (platform !== "darwin")
10
+ return false;
11
+ const actualExecutable = actualCommand.split(/\s+/u, 1)[0];
12
+ const expectedExecutable = expectedCommand.split(/\s+/u, 1)[0];
13
+ const normalizedActualExecutable = actualExecutable?.startsWith("(") === true && actualExecutable.endsWith(")")
14
+ ? actualExecutable.slice(1, -1)
15
+ : actualExecutable;
16
+ return (normalizedActualExecutable !== undefined &&
17
+ expectedExecutable !== undefined &&
18
+ basename(normalizedActualExecutable) === basename(expectedExecutable));
19
+ };
@@ -1,6 +1,7 @@
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
5
  const execFileAsync = promisify(execFile);
5
6
  /**
6
7
  * Terminate one Windows process tree through the platform utility.
@@ -168,8 +169,8 @@ const launcherIdentityFailure = (launcher, ownership) => {
168
169
  launcher.parentPid !== ownership.expectedParentPid)
169
170
  return "owned launcher parent identity did not match";
170
171
  if (ownership.expectedCommand !== undefined &&
171
- !commandMatches(launcher.command, ownership.expectedCommand))
172
- return "owned launcher command identity did not match";
172
+ !matchesOwnedProcessCommand(launcher.command, ownership.expectedCommand))
173
+ return `owned launcher command identity did not match (observed=${launcher.command}; expected=${ownership.expectedCommand})`;
173
174
  return null;
174
175
  };
175
176
  const revalidateOwnedProcessGroup = async (processGroupId, ownership, host) => {
@@ -218,6 +219,12 @@ const processOwnershipFailures = async (members, runId, host) => {
218
219
  failures.push({ pid: member.pid, reason: "run-token-mismatch" });
219
220
  }
220
221
  catch {
222
+ try {
223
+ const live = liveProcesses(await host.listProcesses());
224
+ if (!live.some(({ pid }) => pid === member.pid))
225
+ continue;
226
+ }
227
+ catch { }
221
228
  failures.push({ pid: member.pid, reason: "environment-unreadable" });
222
229
  }
223
230
  }
@@ -280,8 +287,8 @@ export const observeOwnedProcessLineage = async (ownership, host = systemHost) =
280
287
  launcher.parentPid !== ownership.expectedParentPid)
281
288
  return unavailableLineage(ownership, "owned launcher parent identity did not match");
282
289
  if (ownership.expectedCommand !== undefined &&
283
- !commandMatches(launcher.command, ownership.expectedCommand))
284
- return unavailableLineage(ownership, "owned launcher command identity did not match");
290
+ !matchesOwnedProcessCommand(launcher.command, ownership.expectedCommand))
291
+ return unavailableLineage(ownership, `owned launcher command identity did not match (observed=${launcher.command}; expected=${ownership.expectedCommand})`);
285
292
  const descendants = descendantsOf(launcher.pid, processes);
286
293
  for (const member of [launcher, ...descendants]) {
287
294
  try {
@@ -340,7 +347,3 @@ const unavailableLineage = (ownership, reason) => ({
340
347
  reason,
341
348
  });
342
349
  const liveProcesses = (members) => members.filter(({ state }) => !state.startsWith("Z"));
343
- const commandMatches = (actual, expected) => {
344
- return (actual.trim() === expected.trim() ||
345
- actual.trim().startsWith(`${expected.trim()} `));
346
- };
@@ -0,0 +1,70 @@
1
+ import { buildCapabilityInventory, } from "../application/CapabilityInventory.js";
2
+ /**
3
+ * Track public SDK tool handles and keep their visible state synchronized with
4
+ * the same availability projection returned by `binary_session`.
5
+ */
6
+ export const installDynamicToolAvailability = (server, session, policy) => {
7
+ const tools = new Map();
8
+ const originalRegisterTool = server.registerTool.bind(server);
9
+ const originalSendToolListChanged = server.sendToolListChanged.bind(server);
10
+ let synchronizing = false;
11
+ const controller = {
12
+ synchronize: () => {
13
+ if (synchronizing)
14
+ return false;
15
+ synchronizing = true;
16
+ try {
17
+ const availability = new Map(buildCapabilityInventory(session.status(), policy(), clientFeatures(server)).map((item) => [item.name, item.available]));
18
+ let changed = false;
19
+ for (const [name, tool] of tools) {
20
+ const enabled = availability.get(name) === true;
21
+ if (tool.enabled === enabled)
22
+ continue;
23
+ changed = true;
24
+ if (enabled)
25
+ tool.enable();
26
+ else
27
+ tool.disable();
28
+ }
29
+ return changed;
30
+ }
31
+ finally {
32
+ synchronizing = false;
33
+ }
34
+ },
35
+ };
36
+ const wrappedRegisterTool = (...args) => {
37
+ const name = args[0];
38
+ if (typeof name !== "string")
39
+ throw new TypeError("MCP tool registration requires a string name");
40
+ const registered = Reflect.apply(originalRegisterTool, server, args);
41
+ if (!isRegisteredTool(registered))
42
+ throw new TypeError(`MCP SDK did not return a tool handle for ${name}`);
43
+ tools.set(name, registered);
44
+ return registered;
45
+ };
46
+ if (!Reflect.set(server, "registerTool", wrappedRegisterTool))
47
+ throw new TypeError("Unable to install dynamic MCP tool registration");
48
+ if (!Reflect.set(server, "sendToolListChanged", () => {
49
+ if (controller.synchronize())
50
+ originalSendToolListChanged();
51
+ }))
52
+ throw new TypeError("Unable to install dynamic MCP tool notifications");
53
+ return controller;
54
+ };
55
+ const clientFeatures = (server) => {
56
+ const capabilities = server.server.getClientCapabilities();
57
+ return {
58
+ elicitation_form: capabilities?.elicitation?.form !== undefined,
59
+ elicitation_url: capabilities?.elicitation?.url !== undefined,
60
+ roots: capabilities?.roots !== undefined,
61
+ sampling: capabilities?.sampling !== undefined,
62
+ };
63
+ };
64
+ const isRegisteredTool = (value) => {
65
+ if (typeof value !== "object" || value === null)
66
+ return false;
67
+ return (typeof Reflect.get(value, "enabled") === "boolean" &&
68
+ typeof Reflect.get(value, "enable") === "function" &&
69
+ typeof Reflect.get(value, "disable") === "function");
70
+ };
@@ -13,13 +13,39 @@ import { registerGuidedPrompts } from "./registerPrompts.js";
13
13
  import { registerEvidenceResources } from "./registerEvidenceResources.js";
14
14
  import { createServerIdentity } from "../serverIdentity.js";
15
15
  import { registerBrowserTools } from "./registerBrowserTools.js";
16
+ import { registerBrowserScenarioTool } from "./registerBrowserScenarioTool.js";
16
17
  import { registerElectronTools } from "./registerElectronTools.js";
18
+ import { registerJavaScriptRuntimeObservationTools } from "./registerJavaScriptRuntimeObservationTools.js";
17
19
  import { registerApplicationTools } from "./registerApplicationTools.js";
18
- const TARGET_FREE_INSTRUCTIONS = "REA analyzes shipped artifacts. Route: ASAR/JavaScript -> analyze_javascript_application (configured root required); archive/package -> inventory_artifact; managed PE/CLI -> inspect_managed_artifact; browser/Electron runtime -> list_browser_targets/list_electron_targets; native binary/database -> open_binary, then binary_overview. Check binary_session for policy. Start with summaries and cite Evidence IDs. Never repeat identical analysis or read full Evidence without a specific need.";
20
+ import { sessionAvailabilityPolicy } from "./sessionAvailabilityPolicy.js";
21
+ import { DENY_EVIDENCE_FILE_POLICY, DENY_PROCESS_POLICY, } from "./sessionToolPolicies.js";
22
+ import { installDynamicToolAvailability } from "./DynamicToolAvailability.js";
23
+ const TARGET_FREE_INSTRUCTIONS = "REA analyzes shipped artifacts. Route: ASAR/JavaScript -> analyze_javascript_application (configured root); archive/package -> inspect_artifact; managed PE/CLI -> inspect_managed_artifact; passive browser/Electron -> list_browser_targets/list_electron_targets; approved browser interaction -> capture_browser_scenario; Node/Electron Inspector -> list_javascript_runtime_targets; native binary/database -> open_binary, then binary_overview. Check binary_session policy. Start with summaries and cite Evidence IDs. Never repeat identical analysis or read full Evidence without need.";
19
24
  const ACTIVE_TARGET_INSTRUCTIONS = "REA analyzes the active reverse-engineering target. Start native analysis with binary_overview, then narrow with analyze_function, literal search, callers, callees, and xrefs. Prefer summary views, never repeat an identical call, and read full Evidence only when the task requires it.";
20
25
  import { LinuxJavaScriptReplayRunner } from "../replay/LinuxJavaScriptReplayRunner.js";
21
26
  import { SystemJavaScriptReplayHost } from "../replay/SystemJavaScriptReplayHost.js";
22
27
  import { PROCESS_CAPTURE_ELICITATION_POLICY, } from "./ProcessCaptureElicitation.js";
28
+ const installSessionToolAvailability = (server, session, options) => {
29
+ if (session === undefined)
30
+ return undefined;
31
+ const policy = sessionAvailabilityPolicy(options.availabilityPolicy, {
32
+ processPolicy: options.processPolicy ?? DENY_PROCESS_POLICY,
33
+ evidenceFilePolicy: options.evidenceFilePolicy ?? DENY_EVIDENCE_FILE_POLICY,
34
+ investigationInputRoots: options.investigationInputRoots ?? [],
35
+ optionalFeatures: {
36
+ browserObservationEnabled: options.browserObservation !== undefined,
37
+ browserScenarioEnabled: options.browserScenarioCapture !== undefined,
38
+ electronObservationEnabled: options.electronObservation !== undefined,
39
+ v8InspectorObservationEnabled: options.javascriptRuntimeObservation !== undefined,
40
+ javascriptReplayEnabled: options.javascriptReplayPolicy?.enabled ?? false,
41
+ managedRuntimeEnabled: options.managedRuntimePolicy?.enabled ?? false,
42
+ },
43
+ });
44
+ return {
45
+ policy,
46
+ controller: installDynamicToolAvailability(server, session, policy),
47
+ };
48
+ };
23
49
  /**
24
50
  * Construct one MCP server without acquiring subprocess resources.
25
51
  * Supplying a session adds target lifecycle tools; omitting it retains the
@@ -50,9 +76,12 @@ export const createServer = (analysis, session, options = {}) => {
50
76
  ? ACTIVE_TARGET_INSTRUCTIONS
51
77
  : TARGET_FREE_INSTRUCTIONS,
52
78
  });
79
+ const dynamicTools = installSessionToolAvailability(server, session, options);
53
80
  server.server.onclose = () => {
54
81
  permissionAuthority?.clearSessionGrants();
55
82
  };
83
+ if (dynamicTools !== undefined)
84
+ server.server.oninitialized = () => server.sendToolListChanged();
56
85
  session?.onAvailabilityChanged?.(() => server.sendToolListChanged());
57
86
  registerServerIdentityResource(server, startedAt);
58
87
  const toolLogger = logger.child({ layer: "server" });
@@ -75,6 +104,9 @@ export const createServer = (analysis, session, options = {}) => {
75
104
  registerEvidenceResources(server, session);
76
105
  registerSessionTools(server, session, toolLogger, {
77
106
  ...options,
107
+ ...(dynamicTools === undefined
108
+ ? {}
109
+ : { availabilityPolicy: dynamicTools.policy }),
78
110
  ...(permissionAuthority === undefined ? {} : { permissionAuthority }),
79
111
  startedAt,
80
112
  processCaptureElicitation: {
@@ -94,6 +126,7 @@ export const createServer = (analysis, session, options = {}) => {
94
126
  },
95
127
  });
96
128
  }
129
+ dynamicTools?.controller.synchronize();
97
130
  return server;
98
131
  };
99
132
  const createSessionRecorders = (server, session) => ({
@@ -195,12 +228,24 @@ const registerObservationTools = ({ server, session, options, logger, permission
195
228
  permissionAuthority,
196
229
  recordEvidence,
197
230
  });
231
+ registerBrowserScenarioTool(server, {
232
+ logger,
233
+ provider: options.browserScenarioCapture,
234
+ permissionAuthority,
235
+ recordEvidence,
236
+ });
198
237
  registerElectronTools(server, {
199
238
  logger,
200
239
  electron: options.electronObservation,
201
240
  permissionAuthority,
202
241
  recordEvidence,
203
242
  });
243
+ registerJavaScriptRuntimeObservationTools(server, {
244
+ logger,
245
+ runtime: options.javascriptRuntimeObservation,
246
+ permissionAuthority,
247
+ recordEvidence,
248
+ });
204
249
  registerApplicationTools(server, {
205
250
  logger,
206
251
  recordEvidence,
@@ -0,0 +1,10 @@
1
+ /** Report one lifecycle operation start through the shared monotonic boundary. */
2
+ export const reportLifecycleStart = (progress, phase, message = "started") => progress.report({ phase, completed: 0, total: 1, message });
3
+ /** Report one lifecycle operation's terminal disposition. */
4
+ export const reportLifecycleEnd = (progress, phase, succeeded) => progress.report({
5
+ phase,
6
+ completed: 1,
7
+ total: 1,
8
+ message: succeeded ? "completed" : "failed",
9
+ terminal: true,
10
+ });
@@ -0,0 +1,12 @@
1
+ import { AnalysisProtocolError, PermissionRequiredError, } from "../domain/errors.js";
2
+ import { err } from "../domain/result.js";
3
+ /** Preserve a typed permission denial or contain an unexpected authority error. */
4
+ export const permissionFailure = (failure) => {
5
+ if (failure.ok)
6
+ return err(new AnalysisProtocolError("Expected a denied permission"));
7
+ return err(failure.error instanceof PermissionRequiredError
8
+ ? failure.error
9
+ : new AnalysisProtocolError(failure.error.message, {
10
+ cause: failure.error,
11
+ }));
12
+ };
@@ -0,0 +1,35 @@
1
+ import { compareSourceToBundleEvidenceValidated } from "../../application/JavaScriptApplicationWorkflowService.js";
2
+ import { resolveCompareSourceToBundleRequestValidated } from "../../application/ApplicationWorkflowEvidenceResolver.js";
3
+ import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
4
+ import { compareSourceToBundleRequestSchema } from "../../contracts/applicationWorkflowInputContracts.js";
5
+ import { sourceToBundleComparisonResultSchema } from "../../domain/sourceToBundleComparisonSchemas.js";
6
+ import { logToolExecution } from "../toolLogging.js";
7
+ import { safeParseToolInput } from "../toolInputValidation.js";
8
+ import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
9
+ import { toCallToolResult } from "../toolResult.js";
10
+ import { recordResult, recordSources } from "./helpers.js";
11
+ const contract = applicationToolContract("compare_source_to_bundle");
12
+ /** Register conservative historical-source to bundle comparison. */
13
+ export const registerCompareSourceToBundleTool = (server, options) => {
14
+ server.registerTool(contract.name, toolRegistrationOptions(contract), async (input) => {
15
+ const parsedInput = safeParseToolInput(compareSourceToBundleRequestSchema, input, contract.name);
16
+ if (!parsedInput.ok)
17
+ return toCallToolResult(parsedInput, contract);
18
+ const resolved = resolveCompareSourceToBundleRequestValidated(parsedInput.value, options.evidenceLookup);
19
+ if (!resolved.ok)
20
+ return toCallToolResult(resolved, contract);
21
+ const result = await logToolExecution(options.logger, contract.name, () => Promise.resolve(compareSourceToBundleEvidenceValidated(resolved.value)));
22
+ if (!result.ok)
23
+ return toCallToolResult(result, contract);
24
+ const recorded = recordSources(options.recordEvidence, [
25
+ resolved.value.application,
26
+ ]);
27
+ if (!recorded.ok)
28
+ return toCallToolResult(recorded, contract);
29
+ const comparison = sourceToBundleComparisonResultSchema.parse(result.value.normalized_result);
30
+ return recordResult(options, contract, result.value, resolved.value.unknown_registry_approved === true &&
31
+ comparison.summary.unknown > 0
32
+ ? "source-to-bundle-comparison"
33
+ : undefined);
34
+ });
35
+ };
@@ -23,7 +23,9 @@ const unknownRegistration = (evidence, kind) => ({
23
23
  approved: true,
24
24
  question: kind === "application-version-comparison"
25
25
  ? "Which application entities remain unmatched or ambiguous across these versions?"
26
- : "Which selected JavaScript export return shapes remain dynamic, incomplete, or ambiguously paired?",
26
+ : kind === "source-to-bundle-comparison"
27
+ ? "Which historical source files remain unmatched or ambiguously mapped to the shipped bundle?"
28
+ : "Which selected JavaScript export return shapes remain dynamic, incomplete, or ambiguously paired?",
27
29
  severity: "medium",
28
30
  domain: kind,
29
31
  supporting_evidence_ids: [evidence.evidence_id],
@@ -43,12 +45,14 @@ const unknownRegistration = (evidence, kind) => ({
43
45
  rationale: "Add approved passive runtime Evidence without promoting it to static fact.",
44
46
  },
45
47
  ]
46
- : [
47
- {
48
- operation: "run_controlled_replay",
49
- rationale: "Validate exact approved module behavior separately when runtime semantics are required.",
50
- },
51
- ]),
48
+ : kind === "source-to-bundle-comparison"
49
+ ? []
50
+ : [
51
+ {
52
+ operation: "run_controlled_replay",
53
+ rationale: "Validate exact approved module behavior separately when runtime semantics are required.",
54
+ },
55
+ ]),
52
56
  ],
53
57
  relationships: [],
54
58
  });
@@ -0,0 +1,27 @@
1
+ import { buildReconstructionObligationLedgerEvidenceValidated, resolveReconstructionObligationLedgerRequest, } from "../../application/ReconstructionObligationLedgerService.js";
2
+ import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
3
+ import { reconstructionObligationLedgerInputSchema } from "../../domain/reconstructionObligationLedgerSchemas.js";
4
+ import { logToolExecution } from "../toolLogging.js";
5
+ import { safeParseToolInput } from "../toolInputValidation.js";
6
+ import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
7
+ import { toCallToolResult } from "../toolResult.js";
8
+ import { recordResult, recordSources } from "./helpers.js";
9
+ const contract = applicationToolContract("build_reconstruction_obligation_ledger");
10
+ /** Register conservative reconstruction-obligation generation and closure. */
11
+ export const registerReconstructionObligationLedgerTool = (server, options) => {
12
+ server.registerTool(contract.name, toolRegistrationOptions(contract), async (input) => {
13
+ const parsed = safeParseToolInput(reconstructionObligationLedgerInputSchema, input, contract.name);
14
+ if (!parsed.ok)
15
+ return toCallToolResult(parsed, contract);
16
+ const resolved = resolveReconstructionObligationLedgerRequest(parsed.value);
17
+ if (!resolved.ok)
18
+ return toCallToolResult(resolved, contract);
19
+ const result = await logToolExecution(options.logger, contract.name, () => Promise.resolve(buildReconstructionObligationLedgerEvidenceValidated(resolved.value)));
20
+ if (!result.ok)
21
+ return toCallToolResult(result, contract);
22
+ const recorded = recordSources(options.recordEvidence, resolved.value.evidence_bundle.records);
23
+ if (!recorded.ok)
24
+ return toCallToolResult(recorded, contract);
25
+ return recordResult(options, contract, result.value);
26
+ });
27
+ };
@@ -0,0 +1,55 @@
1
+ import { evaluateReconstructionReadinessValidated, resolveReconstructionReadinessRequest, } from "../../application/ReconstructionReadinessService.js";
2
+ import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
3
+ import { reconstructionReadinessInputSchema } from "../../domain/reconstructionReadinessSchemas.js";
4
+ import { reconstructionReadinessReportSchema } from "../../domain/reconstructionReadinessSchemas.js";
5
+ import { logToolExecution } from "../toolLogging.js";
6
+ import { safeParseToolInput } from "../toolInputValidation.js";
7
+ import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
8
+ import { toCallToolResult } from "../toolResult.js";
9
+ import { recordSources } from "./helpers.js";
10
+ const contract = applicationToolContract("evaluate_reconstruction_readiness");
11
+ /** Register deterministic public-contract reconstruction conformance. */
12
+ export const registerReconstructionReadinessTool = (server, options) => {
13
+ server.registerTool(contract.name, toolRegistrationOptions(contract), async (input) => {
14
+ const parsed = safeParseToolInput(reconstructionReadinessInputSchema, input, contract.name);
15
+ if (!parsed.ok)
16
+ return toCallToolResult(parsed, contract);
17
+ const resolved = resolveReconstructionReadinessRequest(parsed.value);
18
+ if (!resolved.ok)
19
+ return toCallToolResult(resolved, contract);
20
+ const result = await logToolExecution(options.logger, contract.name, () => Promise.resolve(evaluateReconstructionReadinessValidated(resolved.value)));
21
+ if (!result.ok)
22
+ return toCallToolResult(result, contract);
23
+ const recorded = recordSources(options.recordEvidence, resolved.value.evidence_bundle.records);
24
+ if (!recorded.ok)
25
+ return toCallToolResult(recorded, contract);
26
+ const recordedResult = options.recordEvidence?.(result.value);
27
+ if (recordedResult !== undefined && !recordedResult.ok)
28
+ return toCallToolResult(recordedResult, contract);
29
+ const report = reconstructionReadinessReportSchema.parse(result.value.normalized_result);
30
+ const reportUri = `rea://evidence/${result.value.evidence_id}/reconstruction-readiness-report`;
31
+ const projection = {
32
+ schema: report.schema,
33
+ schema_version: report.schema_version,
34
+ report_id: report.report_id,
35
+ source_digest: report.source_digest,
36
+ report_digest: report.report_digest,
37
+ status: report.status,
38
+ summary: report.summary,
39
+ metrics: report.metrics,
40
+ report_resource_uri: reportUri,
41
+ };
42
+ return toCallToolResult({ ok: true, value: result.value }, contract, {
43
+ evidenceResourcesAvailable: recordedResult !== undefined,
44
+ evidenceResultProjection: projection,
45
+ evidenceTextProjection: projection,
46
+ resourceLinks: [
47
+ {
48
+ uri: reportUri,
49
+ name: report.report_id,
50
+ description: "Full deterministic reconstruction readiness report",
51
+ },
52
+ ],
53
+ });
54
+ });
55
+ };
@@ -1,17 +1,23 @@
1
1
  import { registerTraceFeatureTool } from "./registerApplicationTools/traceFeature.js";
2
2
  import { registerTraceJavaScriptSemanticsTool } from "./registerApplicationTools/traceSemantics.js";
3
3
  import { registerCompareApplicationVersionsTool } from "./registerApplicationTools/compareVersions.js";
4
+ import { registerCompareSourceToBundleTool } from "./registerApplicationTools/compareSourceToBundle.js";
4
5
  import { registerCompareJavaScriptExportShapesTool } from "./registerApplicationTools/compareExportShapes.js";
5
6
  import { registerControlledReplayTool } from "./registerApplicationTools/controlledReplay.js";
6
7
  import { registerCharacterizationTools } from "./registerApplicationTools/characterization.js";
7
8
  import { registerCoverageTools } from "./registerApplicationTools/coverage.js";
9
+ import { registerReconstructionObligationLedgerTool } from "./registerApplicationTools/obligationLedger.js";
10
+ import { registerReconstructionReadinessTool } from "./registerApplicationTools/readiness.js";
8
11
  /** Register provider-neutral JavaScript application graph workflows. */
9
12
  export const registerApplicationTools = (server, options) => {
10
13
  registerTraceFeatureTool(server, options);
11
14
  registerTraceJavaScriptSemanticsTool(server, options);
12
15
  registerCompareApplicationVersionsTool(server, options);
16
+ registerCompareSourceToBundleTool(server, options);
13
17
  registerCompareJavaScriptExportShapesTool(server, options);
14
18
  registerControlledReplayTool(server, options);
15
19
  registerCharacterizationTools(server, options);
20
+ registerReconstructionObligationLedgerTool(server, options);
21
+ registerReconstructionReadinessTool(server, options);
16
22
  registerCoverageTools(server, options);
17
23
  };
@@ -1,6 +1,14 @@
1
1
  import { ARTIFACT_TOOL_CONTRACTS } from "../contracts/artifactToolContracts.js";
2
2
  import { registerEvidenceTools } from "./registerEvidenceTools.js";
3
+ import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
3
4
  /** Register deterministic artifact inventory and safe extraction operations. */
4
5
  export const registerArtifactTools = (server, analysis, options) => {
5
- registerEvidenceTools(server, analysis, ARTIFACT_TOOL_CONTRACTS, options);
6
+ registerEvidenceTools(server, analysis, ARTIFACT_TOOL_CONTRACTS, {
7
+ ...options,
8
+ sourceEvidence: (operation, result) => operation === "inspect_artifact"
9
+ ? artifactInspectionResultSchema
10
+ .parse(result)
11
+ .substeps.map(({ evidence }) => evidence)
12
+ : [],
13
+ });
6
14
  };
@@ -0,0 +1,25 @@
1
+ import { captureBrowserScenario } from "../application/BrowserScenarioCaptureService.js";
2
+ import { BROWSER_SCENARIO_TOOL_CONTRACTS } from "../contracts/browserScenarioToolContracts.js";
3
+ import { browserScenarioSchema } from "../domain/browserScenario.js";
4
+ import { logToolExecution } from "./toolLogging.js";
5
+ import { safeParseToolInput } from "./toolInputValidation.js";
6
+ import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
7
+ import { toCallToolResult } from "./toolResult.js";
8
+ /** Register the controlled browser scenario tool even when policy disables it. */
9
+ export const registerBrowserScenarioTool = (server, options) => {
10
+ const [contract] = BROWSER_SCENARIO_TOOL_CONTRACTS;
11
+ server.registerTool(contract.name, toolRegistrationOptions(contract), async (input, context) => {
12
+ const parsed = safeParseToolInput(browserScenarioSchema, input, contract.name);
13
+ if (!parsed.ok)
14
+ return toCallToolResult(parsed, contract);
15
+ const result = await logToolExecution(options.logger, contract.name, () => captureBrowserScenario(options.provider, options.permissionAuthority, parsed.value, { signal: context.mcpReq.signal }));
16
+ if (!result.ok)
17
+ return toCallToolResult(result, contract);
18
+ const recorded = options.recordEvidence?.(result.value);
19
+ return recorded !== undefined && !recorded.ok
20
+ ? toCallToolResult(recorded, contract)
21
+ : toCallToolResult({ ok: true, value: result.value }, contract, {
22
+ evidenceResourcesAvailable: recorded !== undefined,
23
+ });
24
+ });
25
+ };
@@ -4,7 +4,7 @@ import { inspectWebPageInputSchema, listBrowserTargetsInputSchema, } from "../do
4
4
  import { analyzeWebBundleInputSchema } from "../domain/webBundleAnalysis.js";
5
5
  import { observeWebSessionInputSchema } from "../domain/browserSession.js";
6
6
  import { discoverWebMcpToolsInputSchema } from "../domain/webMcpDiscovery.js";
7
- import { compareWebCapturesInputSchema } from "../domain/webCaptureDiff.js";
7
+ import { browserCaptureComparisonInputSchema } from "../domain/browserCaptureComparison.js";
8
8
  import { captureWebScreenshotInputSchema, compareWebScreenshotsInputSchema, } from "../domain/webScreenshot.js";
9
9
  import { logToolExecution } from "./toolLogging.js";
10
10
  import { toCallToolResult } from "./toolResult.js";
@@ -55,7 +55,7 @@ export const registerBrowserTools = (server, options) => {
55
55
  });
56
56
  registerBrowserTool(server, options, {
57
57
  contract: captureDiffContract,
58
- schema: compareWebCapturesInputSchema,
58
+ schema: browserCaptureComparisonInputSchema,
59
59
  execute: (parsed, _context) => compareWebCaptureEvidence(options.browser, parsed),
60
60
  });
61
61
  registerBrowserTool(server, options, {
@@ -0,0 +1,57 @@
1
+ import { writeAnalysisSnapshot } from "../application/AnalysisSnapshotFiles.js";
2
+ import { closeBinaryInputSchema } from "../contracts/sessionLifecycleInputs.js";
3
+ import { ok } from "../domain/result.js";
4
+ import { reportLifecycleEnd, reportLifecycleStart, } from "./lifecycleProgress.js";
5
+ import { logToolExecution } from "./toolLogging.js";
6
+ import { toCallToolResult } from "./toolResult.js";
7
+ import { mcpProgressReporter } from "./mcpProgress.js";
8
+ import { permissionFailure } from "./permissionFailure.js";
9
+ import { safeParseToolInput } from "./toolInputValidation.js";
10
+ import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
11
+ /** Register provider cleanup and optional pre-close snapshot persistence. */
12
+ export const registerCloseLifecycleTool = ({ server, session, logger, contracts: [, closeContract], snapshotFilePolicy, permissionAuthority, }) => {
13
+ server.registerTool(closeContract.name, toolRegistrationOptions(closeContract), async (input, context) => {
14
+ const parsedInput = safeParseToolInput(closeBinaryInputSchema, input, closeContract.name);
15
+ if (!parsedInput.ok)
16
+ return toCallToolResult(parsedInput, closeContract);
17
+ const parsed = parsedInput.value;
18
+ const progress = mcpProgressReporter(context);
19
+ if (parsed.snapshot_path === undefined) {
20
+ await reportLifecycleStart(progress, closeContract.name);
21
+ const closed = await logToolExecution(logger, closeContract.name, () => session.close({ progress }));
22
+ await reportLifecycleEnd(progress, closeContract.name, closed.ok);
23
+ server.sendToolListChanged();
24
+ return toCallToolResult(closed, closeContract);
25
+ }
26
+ if (permissionAuthority !== undefined) {
27
+ const authorized = await permissionAuthority.authorize({
28
+ capability: "snapshot_write",
29
+ roots: [parsed.snapshot_path],
30
+ executables: [],
31
+ environment_names: [],
32
+ network: "none",
33
+ mount: false,
34
+ operation_identity: `close_binary:snapshot:${parsed.snapshot_path}`,
35
+ }, "write");
36
+ if (!authorized.ok)
37
+ return toCallToolResult(permissionFailure(authorized), closeContract);
38
+ }
39
+ const snapshot = session.exportAnalysisSnapshot();
40
+ if (!snapshot.ok)
41
+ return toCallToolResult(snapshot, closeContract);
42
+ const written = await writeAnalysisSnapshot(snapshot.value, parsed.snapshot_path, parsed.overwrite, snapshotFilePolicy);
43
+ if (!written.ok)
44
+ return toCallToolResult(written, closeContract);
45
+ await reportLifecycleStart(progress, closeContract.name, "snapshot written; closing provider");
46
+ const closed = await logToolExecution(logger, closeContract.name, () => session.close({ progress }));
47
+ await reportLifecycleEnd(progress, closeContract.name, closed.ok);
48
+ server.sendToolListChanged();
49
+ return closed.ok
50
+ ? toCallToolResult(ok({
51
+ path: written.value.path,
52
+ bytes: written.value.bytes,
53
+ entries: snapshot.value.entries.length,
54
+ }), closeContract)
55
+ : toCallToolResult(closed, closeContract);
56
+ });
57
+ };