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.
- package/README.md +21 -0
- package/bridge/hopper_bridge.py +12 -3
- package/dist/application/ArtifactGraphConstruction.js +1 -1
- package/dist/application/ArtifactInventory.js +98 -26
- package/dist/application/AuthorizedArtifactInventory.js +2 -2
- package/dist/application/BinarySession.js +83 -0
- package/dist/application/CapabilityInventory.js +127 -0
- package/dist/application/ClientRegistrationStatus.js +69 -0
- package/dist/application/CrossVersionInventory.js +19 -8
- package/dist/application/CrossVersionInvestigation.js +68 -5
- package/dist/application/DirectAnalysis.js +97 -9
- package/dist/application/Doctor.js +74 -2
- package/dist/application/PermissionAuthority.js +183 -0
- package/dist/application/PermissionConfiguration.js +26 -0
- package/dist/application/ProcessCaptureError.js +13 -0
- package/dist/application/ProcessCaptureLifecycle.js +3 -1
- package/dist/application/ProcessCli.js +23 -10
- package/dist/application/ProcessHarness.js +4 -1
- package/dist/application/ProgressReporter.js +30 -0
- package/dist/application/ProjectPermissionStore.js +110 -0
- package/dist/application/Setup.js +2 -41
- package/dist/application/SupportedClients.js +41 -0
- package/dist/application/Uninstall.js +1 -1
- package/dist/application/Upgrade.js +2 -0
- package/dist/application/runtime.js +1 -1
- package/dist/artifacts/ArtifactProvider.js +13 -3
- package/dist/catalogIdentity.js +121 -0
- package/dist/cli.js +46 -3
- package/dist/cliEvidenceCommands.js +42 -1
- package/dist/cliInvestigationCommands.js +57 -6
- package/dist/cliLogging.js +20 -3
- package/dist/cliPolicyCommands.js +115 -0
- package/dist/cliProcessCommands.js +4 -14
- package/dist/config.js +63 -3
- package/dist/contracts/artifactToolContracts.js +14 -1
- package/dist/contracts/errorSchemas.js +98 -0
- package/dist/contracts/toolContracts.js +10 -1
- package/dist/contracts/toolOutputSchemas.js +62 -30
- package/dist/domain/artifactComparison.js +38 -11
- package/dist/domain/artifactGraph.js +25 -1
- package/dist/domain/artifactInventoryEvidence.js +18 -0
- package/dist/domain/changedBehavior.js +5 -3
- package/dist/domain/errors.js +216 -3
- package/dist/domain/investigationWorkspace.js +131 -4
- package/dist/domain/permissionPolicy.js +157 -0
- package/dist/domain/processComparison.js +1 -0
- package/dist/domain/reconstructionVerification.js +1 -1
- package/dist/domain/reconstructionVerificationSchemas.js +1 -0
- package/dist/domain/staticRuntimeCorrelation.js +5 -1
- package/dist/generatedPackageMetadata.js +9 -0
- package/dist/hopper/BridgeLauncher.js +7 -3
- package/dist/hopper/HopperClient.js +32 -5
- package/dist/identity.js +10 -3
- package/dist/logger.js +2 -2
- package/dist/main.js +95 -7
- package/dist/server/createServer.js +52 -5
- package/dist/server/mcpProgress.js +23 -0
- package/dist/server/registerArtifactComparisonTool.js +6 -2
- package/dist/server/registerBundleComparisonTool.js +6 -2
- package/dist/server/registerEnhancedTools.js +18 -1
- package/dist/server/registerEvidenceResources.js +302 -0
- package/dist/server/registerEvidenceTools.js +58 -1
- package/dist/server/registerFunctionComparisonTool.js +6 -2
- package/dist/server/registerInvestigationTools.js +83 -10
- package/dist/server/registerOfficialTools.js +19 -1
- package/dist/server/registerProcessComparisonTool.js +7 -3
- package/dist/server/registerSessionStatusTool.js +44 -3
- package/dist/server/registerSessionTools.js +129 -7
- package/dist/server/runDerivedOperation.js +35 -0
- package/dist/server/toolResult.js +27 -3
- package/dist/serverIdentity.js +70 -0
- 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
|
-
}, () =>
|
|
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
|
-
|
|
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
|
|
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: [
|
|
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
|
+
"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.
|
|
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.
|
|
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",
|