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