rea-agents 1.3.0 → 1.4.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 (72) hide show
  1. package/README.md +21 -0
  2. package/bridge/hopper_bridge.py +12 -3
  3. package/dist/application/ArtifactGraphConstruction.js +1 -1
  4. package/dist/application/ArtifactInventory.js +98 -26
  5. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  6. package/dist/application/BinarySession.js +83 -0
  7. package/dist/application/CapabilityInventory.js +127 -0
  8. package/dist/application/ClientRegistrationStatus.js +69 -0
  9. package/dist/application/CrossVersionInventory.js +19 -8
  10. package/dist/application/CrossVersionInvestigation.js +68 -5
  11. package/dist/application/DirectAnalysis.js +97 -9
  12. package/dist/application/Doctor.js +74 -2
  13. package/dist/application/PermissionAuthority.js +183 -0
  14. package/dist/application/PermissionConfiguration.js +26 -0
  15. package/dist/application/ProcessCaptureError.js +13 -0
  16. package/dist/application/ProcessCaptureLifecycle.js +3 -1
  17. package/dist/application/ProcessCli.js +23 -10
  18. package/dist/application/ProcessHarness.js +4 -1
  19. package/dist/application/ProgressReporter.js +30 -0
  20. package/dist/application/ProjectPermissionStore.js +110 -0
  21. package/dist/application/Setup.js +2 -41
  22. package/dist/application/SupportedClients.js +41 -0
  23. package/dist/application/Uninstall.js +1 -1
  24. package/dist/application/Upgrade.js +2 -0
  25. package/dist/application/runtime.js +1 -1
  26. package/dist/artifacts/ArtifactProvider.js +13 -3
  27. package/dist/catalogIdentity.js +121 -0
  28. package/dist/cli.js +46 -3
  29. package/dist/cliEvidenceCommands.js +42 -1
  30. package/dist/cliInvestigationCommands.js +57 -6
  31. package/dist/cliLogging.js +20 -3
  32. package/dist/cliPolicyCommands.js +115 -0
  33. package/dist/cliProcessCommands.js +4 -14
  34. package/dist/config.js +63 -3
  35. package/dist/contracts/artifactToolContracts.js +14 -1
  36. package/dist/contracts/errorSchemas.js +98 -0
  37. package/dist/contracts/toolContracts.js +10 -1
  38. package/dist/contracts/toolOutputSchemas.js +62 -30
  39. package/dist/domain/artifactComparison.js +38 -11
  40. package/dist/domain/artifactGraph.js +25 -1
  41. package/dist/domain/artifactInventoryEvidence.js +18 -0
  42. package/dist/domain/changedBehavior.js +5 -3
  43. package/dist/domain/errors.js +216 -3
  44. package/dist/domain/investigationWorkspace.js +131 -4
  45. package/dist/domain/permissionPolicy.js +157 -0
  46. package/dist/domain/processComparison.js +1 -0
  47. package/dist/domain/reconstructionVerification.js +1 -1
  48. package/dist/domain/reconstructionVerificationSchemas.js +1 -0
  49. package/dist/domain/staticRuntimeCorrelation.js +5 -1
  50. package/dist/generatedPackageMetadata.js +9 -0
  51. package/dist/hopper/BridgeLauncher.js +7 -3
  52. package/dist/hopper/HopperClient.js +32 -5
  53. package/dist/identity.js +10 -3
  54. package/dist/logger.js +2 -2
  55. package/dist/main.js +95 -7
  56. package/dist/server/createServer.js +52 -5
  57. package/dist/server/mcpProgress.js +23 -0
  58. package/dist/server/registerArtifactComparisonTool.js +6 -2
  59. package/dist/server/registerBundleComparisonTool.js +6 -2
  60. package/dist/server/registerEnhancedTools.js +18 -1
  61. package/dist/server/registerEvidenceResources.js +302 -0
  62. package/dist/server/registerEvidenceTools.js +58 -1
  63. package/dist/server/registerFunctionComparisonTool.js +6 -2
  64. package/dist/server/registerInvestigationTools.js +83 -10
  65. package/dist/server/registerOfficialTools.js +19 -1
  66. package/dist/server/registerProcessComparisonTool.js +7 -3
  67. package/dist/server/registerSessionStatusTool.js +44 -3
  68. package/dist/server/registerSessionTools.js +129 -7
  69. package/dist/server/runDerivedOperation.js +35 -0
  70. package/dist/server/toolResult.js +27 -3
  71. package/dist/serverIdentity.js +70 -0
  72. package/package.json +6 -4
@@ -1,11 +1,52 @@
1
- import { SESSION_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
1
+ import { binarySessionInputSchema, SESSION_TOOL_CONTRACTS, } from "../contracts/toolContracts.js";
2
2
  import { toCallToolResult } from "./toolResult.js";
3
+ import { jsonObjectSchema } from "../domain/jsonValue.js";
4
+ import { createServerIdentity } from "../serverIdentity.js";
5
+ import { buildCapabilityInventory } from "../application/CapabilityInventory.js";
3
6
  /** Register the read-only provider and target status operation. */
4
- export const registerSessionStatusTool = (server, session, contract) => {
7
+ export const registerSessionStatusTool = (server, session, contract, startedAt, availabilityPolicy) => {
5
8
  server.registerTool(contract.name, {
6
9
  description: contract.description,
7
10
  inputSchema: contract.inputSchema,
8
11
  outputSchema: contract.outputSchema,
9
12
  annotations: contract.annotations,
10
- }, () => toCallToolResult({ ok: true, value: session.status() }, contract));
13
+ }, (input) => {
14
+ const parsed = binarySessionInputSchema.parse(input);
15
+ const client = server.server.getClientVersion();
16
+ const clientCapabilities = server.server.getClientCapabilities();
17
+ const status = session.status();
18
+ return toCallToolResult({
19
+ ok: true,
20
+ value: {
21
+ ...jsonObjectSchema.parse(status),
22
+ tool_availability: buildCapabilityInventory(status, availabilityPolicy()),
23
+ client_features: {
24
+ elicitation_form: clientCapabilities?.elicitation?.form !== undefined,
25
+ elicitation_url: clientCapabilities?.elicitation?.url !== undefined,
26
+ roots: clientCapabilities?.roots !== undefined,
27
+ sampling: clientCapabilities?.sampling !== undefined,
28
+ },
29
+ server_identity: createServerIdentity({
30
+ startedAt,
31
+ expected: {
32
+ ...(parsed.expected_package_version === undefined
33
+ ? {}
34
+ : { package_version: parsed.expected_package_version }),
35
+ ...(parsed.expected_catalog_digest === undefined
36
+ ? {}
37
+ : { catalog_digest: parsed.expected_catalog_digest }),
38
+ ...(parsed.expected_server_path === undefined
39
+ ? {}
40
+ : { server_path: parsed.expected_server_path }),
41
+ },
42
+ ...(client === undefined ? {} : { client }),
43
+ ...(server.server.getNegotiatedProtocolVersion() === undefined
44
+ ? {}
45
+ : {
46
+ protocolVersion: server.server.getNegotiatedProtocolVersion(),
47
+ }),
48
+ }),
49
+ },
50
+ }, contract);
51
+ });
11
52
  };
@@ -2,13 +2,13 @@ import { z } from "zod";
2
2
  import { SESSION_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
3
3
  import { toCallToolResult } from "./toolResult.js";
4
4
  import { logToolExecution } from "./toolLogging.js";
5
- import { ok } from "../domain/result.js";
5
+ import { err, ok } from "../domain/result.js";
6
6
  import { processScenarioSchema } from "../domain/processCapture.js";
7
7
  import { captureProcessScenario } from "../application/ProcessHarness.js";
8
8
  import { recordUnknownInputSchema, updateUnknownInputSchema, } from "../domain/residualUnknown.js";
9
9
  import { readEvidenceBundle, writeEvidenceBundle, } from "../application/EvidenceBundleFiles.js";
10
10
  import { readAnalysisSnapshot, writeAnalysisSnapshot, } from "../application/AnalysisSnapshotFiles.js";
11
- import { UnknownRegistryError } from "../domain/errors.js";
11
+ import { AnalysisProtocolError, PermissionRequiredError, UnknownRegistryError, } from "../domain/errors.js";
12
12
  import { DENY_EVIDENCE_FILE_POLICY, DENY_PROCESS_POLICY, } from "./sessionToolPolicies.js";
13
13
  import { createProcessCaptureEvidence } from "../application/ProcessEvidence.js";
14
14
  import { registerProcessComparisonTool } from "./registerProcessComparisonTool.js";
@@ -18,6 +18,16 @@ import { registerBundleComparisonTool } from "./registerBundleComparisonTool.js"
18
18
  import { registerInvestigationTools } from "./registerInvestigationTools.js";
19
19
  import { closeBinaryInputSchema, openBinaryInputSchema, } from "../contracts/sessionLifecycleInputs.js";
20
20
  import { registerSessionStatusTool } from "./registerSessionStatusTool.js";
21
+ import { mcpProgressReporter } from "./mcpProgress.js";
22
+ const permissionFailure = (failure) => {
23
+ if (failure.ok)
24
+ return err(new AnalysisProtocolError("Expected a denied permission"));
25
+ return err(failure.error instanceof PermissionRequiredError
26
+ ? failure.error
27
+ : new AnalysisProtocolError(failure.error.message, {
28
+ cause: failure.error,
29
+ }));
30
+ };
21
31
  const recordProcessResidualUnknowns = (session, scenario, evidence, residuals) => {
22
32
  if (scenario.unknown_registry_approved !== true)
23
33
  return ok(null);
@@ -47,7 +57,7 @@ const recordProcessResidualUnknowns = (session, scenario, evidence, residuals) =
47
57
  }
48
58
  return ok(null);
49
59
  };
50
- const registerProcessTools = ({ server, session, logger, processPolicy, captureContract, }) => {
60
+ const registerProcessTools = ({ server, session, logger, processPolicy, captureContract, permissionAuthority, }) => {
51
61
  server.registerTool(captureContract.name, {
52
62
  description: captureContract.description,
53
63
  inputSchema: captureContract.inputSchema,
@@ -55,7 +65,37 @@ const registerProcessTools = ({ server, session, logger, processPolicy, captureC
55
65
  annotations: captureContract.annotations,
56
66
  }, async (input, context) => {
57
67
  const scenario = processScenarioSchema.parse(input);
68
+ if (permissionAuthority !== undefined) {
69
+ const authorized = await permissionAuthority.authorize({
70
+ capability: "process_capture",
71
+ roots: [scenario.working_directory, ...scenario.filesystem_roots],
72
+ executables: [scenario.executable],
73
+ environment_names: [
74
+ ...Object.keys(scenario.environment),
75
+ ...scenario.inherit_environment,
76
+ ],
77
+ network: scenario.network_access === "host" ? "external" : "none",
78
+ mount: false,
79
+ operation_identity: `capture_process_scenario:${scenario.executable}`,
80
+ }, "read");
81
+ if (!authorized.ok)
82
+ return toCallToolResult(permissionFailure(authorized), captureContract);
83
+ }
84
+ const progress = mcpProgressReporter(context);
85
+ await progress.report({
86
+ phase: captureContract.name,
87
+ completed: 0,
88
+ total: 1,
89
+ message: "started",
90
+ });
58
91
  const captured = await logToolExecution(logger, captureContract.name, () => captureProcessScenario(scenario, processPolicy, context.mcpReq.signal));
92
+ await progress.report({
93
+ phase: captureContract.name,
94
+ completed: 1,
95
+ total: 1,
96
+ message: captured.ok ? "completed" : "failed",
97
+ terminal: true,
98
+ });
59
99
  if (!captured.ok)
60
100
  return toCallToolResult(captured, captureContract);
61
101
  const evidence = createProcessCaptureEvidence(scenario, captured.value);
@@ -68,7 +108,7 @@ const registerProcessTools = ({ server, session, logger, processPolicy, captureC
68
108
  return toCallToolResult(ok(evidence), captureContract);
69
109
  });
70
110
  };
71
- const registerEvidenceTools = ({ server, session, exportContract, importContract, filePolicy, }) => {
111
+ const registerEvidenceTools = ({ server, session, exportContract, importContract, filePolicy, permissionAuthority, }) => {
72
112
  server.registerTool(exportContract.name, {
73
113
  description: exportContract.description,
74
114
  inputSchema: exportContract.inputSchema,
@@ -84,6 +124,19 @@ const registerEvidenceTools = ({ server, session, exportContract, importContract
84
124
  const bundle = session.exportEvidenceBundle();
85
125
  if (parsed.path === undefined)
86
126
  return toCallToolResult(ok(bundle), exportContract);
127
+ if (permissionAuthority !== undefined) {
128
+ const authorized = await permissionAuthority.authorize({
129
+ capability: "evidence_write",
130
+ roots: [parsed.path],
131
+ executables: [],
132
+ environment_names: [],
133
+ network: "none",
134
+ mount: false,
135
+ operation_identity: `export_evidence:${parsed.path}`,
136
+ }, "write");
137
+ if (!authorized.ok)
138
+ return toCallToolResult(permissionFailure(authorized), exportContract);
139
+ }
87
140
  const written = await writeEvidenceBundle(bundle, parsed.path, parsed.overwrite, filePolicy);
88
141
  return written.ok
89
142
  ? toCallToolResult(ok({
@@ -100,6 +153,19 @@ const registerEvidenceTools = ({ server, session, exportContract, importContract
100
153
  annotations: importContract.annotations,
101
154
  }, async (input) => {
102
155
  const path = z.object({ path: z.string().min(1) }).parse(input).path;
156
+ if (permissionAuthority !== undefined) {
157
+ const authorized = await permissionAuthority.authorize({
158
+ capability: "evidence_read",
159
+ roots: [path],
160
+ executables: [],
161
+ environment_names: [],
162
+ network: "none",
163
+ mount: false,
164
+ operation_identity: `import_evidence:${path}`,
165
+ }, "read");
166
+ if (!authorized.ok)
167
+ return toCallToolResult(permissionFailure(authorized), importContract);
168
+ }
103
169
  const loaded = await readEvidenceBundle(path, filePolicy);
104
170
  if (!loaded.ok)
105
171
  return toCallToolResult(loaded, importContract);
@@ -166,7 +232,7 @@ const registerUnknownTools = ({ server, session, contracts: [listContract, recor
166
232
  return toCallToolResult(session.verifyUnknownResolution(unknownId), verifyContract);
167
233
  });
168
234
  };
169
- const registerLifecycleTools = ({ server, session, logger, contracts, snapshotFilePolicy, }) => {
235
+ const registerLifecycleTools = ({ server, session, logger, contracts, snapshotFilePolicy, startedAt, availabilityPolicy, permissionAuthority, }) => {
170
236
  const [openContract, closeContract, statusContract] = contracts;
171
237
  server.registerTool(openContract.name, {
172
238
  description: openContract.description,
@@ -177,6 +243,19 @@ const registerLifecycleTools = ({ server, session, logger, contracts, snapshotFi
177
243
  const parsed = openBinaryInputSchema.parse(input);
178
244
  let snapshot;
179
245
  if (parsed.snapshot_path !== undefined) {
246
+ if (permissionAuthority !== undefined) {
247
+ const authorized = await permissionAuthority.authorize({
248
+ capability: "snapshot_read",
249
+ roots: [parsed.snapshot_path],
250
+ executables: [],
251
+ environment_names: [],
252
+ network: "none",
253
+ mount: false,
254
+ operation_identity: `open_binary:snapshot:${parsed.snapshot_path}`,
255
+ }, "read");
256
+ if (!authorized.ok)
257
+ return toCallToolResult(permissionFailure(authorized), openContract);
258
+ }
180
259
  const loaded = await readAnalysisSnapshot(parsed.snapshot_path, snapshotFilePolicy);
181
260
  if (!loaded.ok)
182
261
  return toCallToolResult(loaded, openContract);
@@ -186,6 +265,8 @@ const registerLifecycleTools = ({ server, session, logger, contracts, snapshotFi
186
265
  signal: context.mcpReq.signal,
187
266
  ...(snapshot === undefined ? {} : { snapshot }),
188
267
  }));
268
+ if (opened.ok)
269
+ server.sendToolListChanged();
189
270
  return opened.ok
190
271
  ? toCallToolResult({
191
272
  ok: true,
@@ -208,6 +289,19 @@ const registerLifecycleTools = ({ server, session, logger, contracts, snapshotFi
208
289
  }, async (input) => {
209
290
  const parsed = closeBinaryInputSchema.parse(input);
210
291
  if (parsed.snapshot_path !== undefined) {
292
+ if (permissionAuthority !== undefined) {
293
+ const authorized = await permissionAuthority.authorize({
294
+ capability: "snapshot_write",
295
+ roots: [parsed.snapshot_path],
296
+ executables: [],
297
+ environment_names: [],
298
+ network: "none",
299
+ mount: false,
300
+ operation_identity: `close_binary:snapshot:${parsed.snapshot_path}`,
301
+ }, "write");
302
+ if (!authorized.ok)
303
+ return toCallToolResult(permissionFailure(authorized), closeContract);
304
+ }
211
305
  const snapshot = session.exportAnalysisSnapshot();
212
306
  if (!snapshot.ok)
213
307
  return toCallToolResult(snapshot, closeContract);
@@ -215,6 +309,8 @@ const registerLifecycleTools = ({ server, session, logger, contracts, snapshotFi
215
309
  if (!written.ok)
216
310
  return toCallToolResult(written, closeContract);
217
311
  const closed = await logToolExecution(logger, closeContract.name, () => session.close());
312
+ if (closed.ok)
313
+ server.sendToolListChanged();
218
314
  return closed.ok
219
315
  ? toCallToolResult(ok({
220
316
  path: written.value.path,
@@ -223,9 +319,12 @@ const registerLifecycleTools = ({ server, session, logger, contracts, snapshotFi
223
319
  }), closeContract)
224
320
  : toCallToolResult(closed, closeContract);
225
321
  }
226
- return toCallToolResult(await logToolExecution(logger, closeContract.name, () => session.close()), closeContract);
322
+ const closed = await logToolExecution(logger, closeContract.name, () => session.close());
323
+ if (closed.ok)
324
+ server.sendToolListChanged();
325
+ return toCallToolResult(closed, closeContract);
227
326
  });
228
- registerSessionStatusTool(server, session, statusContract);
327
+ registerSessionStatusTool(server, session, statusContract, startedAt, availabilityPolicy);
229
328
  };
230
329
  export const registerSessionTools = (server, session, logger, options = {}) => {
231
330
  const processPolicy = options.processPolicy ?? DENY_PROCESS_POLICY;
@@ -238,6 +337,15 @@ export const registerSessionTools = (server, session, logger, options = {}) => {
238
337
  logger,
239
338
  contracts: [openContract, closeContract, statusContract],
240
339
  snapshotFilePolicy: analysisSnapshotFilePolicy,
340
+ startedAt: options.startedAt ?? new Date().toISOString(),
341
+ availabilityPolicy: options.availabilityPolicy ??
342
+ (() => ({
343
+ processCaptureEnabled: processPolicy.enabled,
344
+ evidenceFileRoots: evidenceFilePolicy.roots.length,
345
+ })),
346
+ ...(options.permissionAuthority === undefined
347
+ ? {}
348
+ : { permissionAuthority: options.permissionAuthority }),
241
349
  });
242
350
  registerEvidenceTools({
243
351
  server,
@@ -245,6 +353,9 @@ export const registerSessionTools = (server, session, logger, options = {}) => {
245
353
  exportContract,
246
354
  importContract,
247
355
  filePolicy: evidenceFilePolicy,
356
+ ...(options.permissionAuthority === undefined
357
+ ? {}
358
+ : { permissionAuthority: options.permissionAuthority }),
248
359
  });
249
360
  registerProcessTools({
250
361
  server,
@@ -252,6 +363,9 @@ export const registerSessionTools = (server, session, logger, options = {}) => {
252
363
  logger,
253
364
  processPolicy,
254
365
  captureContract,
366
+ ...(options.permissionAuthority === undefined
367
+ ? {}
368
+ : { permissionAuthority: options.permissionAuthority }),
255
369
  });
256
370
  registerProcessComparisonTool(server, session, compareContract);
257
371
  registerArtifactComparisonTool(server, session, compareArtifactsContract);
@@ -266,6 +380,14 @@ export const registerSessionTools = (server, session, logger, options = {}) => {
266
380
  registerInvestigationTools(server, session, investigationContracts, {
267
381
  evidenceFiles: evidenceFilePolicy,
268
382
  inputRoots: options.investigationInputRoots ?? [],
383
+ ...(options.artifactIntegrityContinueEnabled === undefined
384
+ ? {}
385
+ : {
386
+ integrityContinueEnabled: options.artifactIntegrityContinueEnabled,
387
+ }),
388
+ ...(options.permissionAuthority === undefined
389
+ ? {}
390
+ : { permissionAuthority: options.permissionAuthority }),
269
391
  });
270
392
  registerUnknownTools({
271
393
  server,
@@ -0,0 +1,35 @@
1
+ import { setImmediate } from "node:timers/promises";
2
+ import { AnalysisCancelledError } from "../domain/errors.js";
3
+ import { err, ok } from "../domain/result.js";
4
+ import { mcpProgressReporter } from "./mcpProgress.js";
5
+ /** Run synchronous derived work with transport yields and a publication guard. */
6
+ export const runDerivedOperation = async (context, operation, compute) => {
7
+ const progress = mcpProgressReporter(context);
8
+ await progress.report({
9
+ phase: "prepare",
10
+ completed: 0,
11
+ total: 2,
12
+ message: `Preparing ${operation}`,
13
+ });
14
+ await setImmediate();
15
+ if (context.mcpReq.signal.aborted)
16
+ return err(new AnalysisCancelledError(operation));
17
+ const value = compute();
18
+ await progress.report({
19
+ phase: "compute",
20
+ completed: 1,
21
+ total: 2,
22
+ message: `Computed ${operation}`,
23
+ });
24
+ await setImmediate();
25
+ if (context.mcpReq.signal.aborted)
26
+ return err(new AnalysisCancelledError(operation));
27
+ await progress.report({
28
+ phase: "complete",
29
+ completed: 2,
30
+ total: 2,
31
+ message: `Completed ${operation}`,
32
+ terminal: true,
33
+ });
34
+ return ok(value);
35
+ };
@@ -4,7 +4,9 @@ import { AnalysisOutputError, projectAnalysisError, } from "../domain/errors.js"
4
4
  * Error tags and safe messages remain visible while underlying causes, process
5
5
  * output, and other potentially sensitive details stay private.
6
6
  */
7
- export const toCallToolResult = (result, contract) => result.ok ? successResult(result.value, contract) : errorResult(result.error);
7
+ export const toCallToolResult = (result, contract, options = {}) => result.ok
8
+ ? successResult(result.value, contract, options)
9
+ : errorResult(result.error);
8
10
  const errorResult = (error) => {
9
11
  const projected = projectAnalysisError(error);
10
12
  return {
@@ -18,7 +20,7 @@ const errorResult = (error) => {
18
20
  isError: true,
19
21
  };
20
22
  };
21
- const successResult = (value, contract) => {
23
+ const successResult = (value, contract, options) => {
22
24
  const candidate = contract.kind === "session" ? { result: value } : value;
23
25
  const parsed = contract.outputSchema.safeParse(candidate);
24
26
  if (!parsed.success) {
@@ -36,10 +38,32 @@ const successResult = (value, contract) => {
36
38
  };
37
39
  }
38
40
  return {
39
- content: [{ type: "text", text: formatValue(value) }],
41
+ content: [
42
+ { type: "text", text: formatValue(value) },
43
+ ...evidenceResourceLinks(value, contract.kind === "session" ||
44
+ options.evidenceResourcesAvailable === true),
45
+ ],
40
46
  structuredContent: candidate,
41
47
  };
42
48
  };
49
+ const evidenceResourceLinks = (value, available) => {
50
+ if (!available ||
51
+ typeof value !== "object" ||
52
+ value === null ||
53
+ Array.isArray(value) ||
54
+ typeof value.evidence_id !== "string" ||
55
+ !/^ev_[a-f0-9]{64}$/u.test(value.evidence_id))
56
+ return [];
57
+ return [
58
+ {
59
+ type: "resource_link",
60
+ uri: `rea://evidence/${value.evidence_id}`,
61
+ name: value.evidence_id,
62
+ description: "Session-owned Evidence v2 record",
63
+ mimeType: "application/json",
64
+ },
65
+ ];
66
+ };
43
67
  const formatValue = (value) => {
44
68
  if (value === null)
45
69
  return "OK";
@@ -0,0 +1,70 @@
1
+ import { dirname, basename, resolve } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { CATALOG_IDENTITY } from "./catalogIdentity.js";
4
+ import { PRODUCT_IDENTITY, SDK_IDENTITY } from "./identity.js";
5
+ const packageRoot = () => {
6
+ const moduleDirectory = dirname(fileURLToPath(import.meta.url));
7
+ return basename(moduleDirectory) === "src" &&
8
+ basename(dirname(moduleDirectory)) === "dist"
9
+ ? resolve(moduleDirectory, "../..")
10
+ : resolve(moduleDirectory, "..");
11
+ };
12
+ /** Build one runtime identity without claiming alignment from absent observations. */
13
+ export const createServerIdentity = (input) => {
14
+ const serverPath = resolve(process.argv[1] ?? "unknown");
15
+ const reasons = [];
16
+ if (input.expected?.package_version !== undefined &&
17
+ input.expected.package_version !== PRODUCT_IDENTITY.packageVersion)
18
+ reasons.push("package_version_mismatch");
19
+ if (input.expected?.catalog_digest !== undefined &&
20
+ input.expected.catalog_digest !== CATALOG_IDENTITY.digests.combined_sha256)
21
+ reasons.push("catalog_digest_mismatch");
22
+ if (input.expected?.server_path !== undefined &&
23
+ resolve(input.expected.server_path) !== serverPath)
24
+ reasons.push("registration_path_mismatch");
25
+ const observedExpectation = input.expected !== undefined &&
26
+ Object.values(input.expected).some((value) => value !== undefined);
27
+ return {
28
+ package: {
29
+ name: PRODUCT_IDENTITY.packageName,
30
+ version: PRODUCT_IDENTITY.packageVersion,
31
+ root_path: packageRoot(),
32
+ build_commit: process.env.REA_BUILD_COMMIT ?? null,
33
+ },
34
+ server: {
35
+ name: PRODUCT_IDENTITY.mcpServerKey,
36
+ version: PRODUCT_IDENTITY.packageVersion,
37
+ started_at: input.startedAt,
38
+ command_path: serverPath,
39
+ },
40
+ sdk: SDK_IDENTITY,
41
+ negotiated_protocol_version: input.protocolVersion ?? null,
42
+ client: input.client === undefined
43
+ ? null
44
+ : { name: input.client.name, version: input.client.version },
45
+ skill: {
46
+ name: PRODUCT_IDENTITY.skillName,
47
+ expected_version: PRODUCT_IDENTITY.skillVersion,
48
+ },
49
+ catalog: CATALOG_IDENTITY,
50
+ protocol_features: {
51
+ progress: true,
52
+ cancellation: true,
53
+ evidence_resources: true,
54
+ elicitation: false,
55
+ },
56
+ alignment: {
57
+ state: reasons.length > 0
58
+ ? "mcp_server_restart_required"
59
+ : observedExpectation
60
+ ? "aligned"
61
+ : "unknown",
62
+ reasons,
63
+ remediation: reasons.length > 0
64
+ ? "Restart the registered MCP server/client, then compare identity again."
65
+ : observedExpectation
66
+ ? null
67
+ : "Supply expected package, catalog, or registration identity to compare the live server.",
68
+ },
69
+ };
70
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rea-agents",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Reverse engineer anything from your terminal or agent with one CLI and MCP server.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -49,6 +49,7 @@
49
49
  "LICENSE"
50
50
  ],
51
51
  "scripts": {
52
+ "prebuild": "node scripts/generate-package-metadata.mjs",
52
53
  "build": "tsc -p tsconfig.build.json",
53
54
  "check": "npm run typecheck && npm run lint && npm run format:check && npm test",
54
55
  "format": "prettier --write .",
@@ -76,7 +77,8 @@
76
77
  "verify:package": "npm run build && node scripts/verify-package.mjs",
77
78
  "verify:published": "node scripts/verify-published-package.mjs",
78
79
  "config:print": "node scripts/print-mcp-config.mjs",
79
- "docs:generate": "typedoc",
80
+ "docs:generate": "npm run build && typedoc && node scripts/generate-error-schema.mjs",
81
+ "docs:error-schema": "npm run build && node scripts/generate-error-schema.mjs",
80
82
  "prepack": "npm run build",
81
83
  "prepare": "husky",
82
84
  "rebuild:native": "node scripts/rebuild-native.mjs"
@@ -89,7 +91,7 @@
89
91
  "@babel/parser": "8.0.4",
90
92
  "@babel/types": "8.0.4",
91
93
  "@electron/asar": "4.2.0",
92
- "@modelcontextprotocol/server": "2.0.0-beta.3",
94
+ "@modelcontextprotocol/server": "2.0.0-beta.4",
93
95
  "@xterm/addon-serialize": "0.14.0",
94
96
  "@xterm/headless": "6.0.0",
95
97
  "@zip.js/zip.js": "2.8.26",
@@ -110,7 +112,7 @@
110
112
  },
111
113
  "devDependencies": {
112
114
  "@fast-check/vitest": "0.4.1",
113
- "@modelcontextprotocol/client": "2.0.0-beta.3",
115
+ "@modelcontextprotocol/client": "2.0.0-beta.4",
114
116
  "@types/node": "24.10.1",
115
117
  "@types/write-file-atomic": "4.0.3",
116
118
  "@types/ws": "8.18.1",