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
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { mkdtemp, open, readFile, realpath, rm, stat, writeFile, } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { digestBytes } from "../application/JavaScriptReplayPlanning.js";
|
|
8
|
+
import { buildLinuxX64ReplaySeccomp, linuxX64ReplaySeccompDigest, } from "./LinuxSeccompPolicy.js";
|
|
9
|
+
import { resolveLinuxRuntimeClosure } from "./LinuxRuntimeClosure.js";
|
|
10
|
+
const VERSION_OUTPUT_LIMIT = 16 * 1024;
|
|
11
|
+
/** Production planning host for descriptor-backed reads and fail-closed probes. */
|
|
12
|
+
export class SystemJavaScriptReplayHost {
|
|
13
|
+
async readSource(path, maximumBytes) {
|
|
14
|
+
const canonicalPath = await realpath(path);
|
|
15
|
+
const handle = await open(canonicalPath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
16
|
+
try {
|
|
17
|
+
const metadata = await handle.stat();
|
|
18
|
+
if (!metadata.isFile() || metadata.size > maximumBytes)
|
|
19
|
+
throw new RangeError(`Replay source exceeds its declared limit: ${canonicalPath}`);
|
|
20
|
+
const bytes = await handle.readFile();
|
|
21
|
+
return { canonicalPath, bytes };
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
await handle.close();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async identifyExecutable(path, versionArguments) {
|
|
28
|
+
const canonicalPath = await realpath(path);
|
|
29
|
+
const metadata = await stat(canonicalPath);
|
|
30
|
+
if (!metadata.isFile())
|
|
31
|
+
throw new TypeError(`Replay executable is not a file: ${canonicalPath}`);
|
|
32
|
+
const [bytes, version] = await Promise.all([
|
|
33
|
+
readFile(canonicalPath),
|
|
34
|
+
capture(canonicalPath, versionArguments, VERSION_OUTPUT_LIMIT),
|
|
35
|
+
]);
|
|
36
|
+
return {
|
|
37
|
+
path: canonicalPath,
|
|
38
|
+
version: version.trim(),
|
|
39
|
+
sha256: digestBytes(bytes),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
identifyWorker() {
|
|
43
|
+
return this.identifyExecutable(fileURLToPath(new URL("./JavaScriptReplayWorker.js", import.meta.url)), []);
|
|
44
|
+
}
|
|
45
|
+
identifyRuntimeClosure(nodePath) {
|
|
46
|
+
return resolveLinuxRuntimeClosure(nodePath);
|
|
47
|
+
}
|
|
48
|
+
seccompDigest() {
|
|
49
|
+
return linuxX64ReplaySeccompDigest();
|
|
50
|
+
}
|
|
51
|
+
async probe(policy) {
|
|
52
|
+
if (process.platform !== "linux" || process.arch !== "x64")
|
|
53
|
+
throw new TypeError("Controlled replay requires Linux x86_64 in v1");
|
|
54
|
+
const bwrapMetadata = await stat(policy.bubblewrapPath);
|
|
55
|
+
if ((bwrapMetadata.mode & 0o4000) !== 0)
|
|
56
|
+
throw new TypeError("Setuid Bubblewrap is not admitted by replay policy");
|
|
57
|
+
if ((await readFile("/sys/fs/cgroup/cgroup.controllers", "utf8")).trim()
|
|
58
|
+
.length === 0)
|
|
59
|
+
throw new TypeError("Controlled replay requires delegated cgroup v2");
|
|
60
|
+
const directory = await mkdtemp(join(tmpdir(), "rea-replay-probe-"));
|
|
61
|
+
const filterPath = join(directory, "seccomp.bpf");
|
|
62
|
+
await writeFile(filterPath, buildLinuxX64ReplaySeccomp(), { mode: 0o600 });
|
|
63
|
+
const filter = await open(filterPath, "r");
|
|
64
|
+
try {
|
|
65
|
+
await run(policy.bubblewrapPath, [
|
|
66
|
+
"--unshare-all",
|
|
67
|
+
"--unshare-user",
|
|
68
|
+
"--disable-userns",
|
|
69
|
+
"--new-session",
|
|
70
|
+
"--die-with-parent",
|
|
71
|
+
"--cap-drop",
|
|
72
|
+
"ALL",
|
|
73
|
+
"--ro-bind",
|
|
74
|
+
"/usr",
|
|
75
|
+
"/usr",
|
|
76
|
+
"--ro-bind",
|
|
77
|
+
"/lib",
|
|
78
|
+
"/lib",
|
|
79
|
+
"--ro-bind-try",
|
|
80
|
+
"/lib64",
|
|
81
|
+
"/lib64",
|
|
82
|
+
"--proc",
|
|
83
|
+
"/proc",
|
|
84
|
+
"--dev",
|
|
85
|
+
"/dev",
|
|
86
|
+
"--seccomp",
|
|
87
|
+
"3",
|
|
88
|
+
"--",
|
|
89
|
+
"/usr/bin/true",
|
|
90
|
+
], ["ignore", "pipe", "pipe", filter.fd]);
|
|
91
|
+
await runExpectFailure(policy.bubblewrapPath, [
|
|
92
|
+
"--unshare-all",
|
|
93
|
+
"--unshare-user",
|
|
94
|
+
"--disable-userns",
|
|
95
|
+
"--new-session",
|
|
96
|
+
"--die-with-parent",
|
|
97
|
+
"--cap-drop",
|
|
98
|
+
"ALL",
|
|
99
|
+
"--ro-bind",
|
|
100
|
+
"/usr",
|
|
101
|
+
"/usr",
|
|
102
|
+
"--ro-bind",
|
|
103
|
+
"/lib",
|
|
104
|
+
"/lib",
|
|
105
|
+
"--ro-bind-try",
|
|
106
|
+
"/lib64",
|
|
107
|
+
"/lib64",
|
|
108
|
+
"--proc",
|
|
109
|
+
"/proc",
|
|
110
|
+
"--dev",
|
|
111
|
+
"/dev",
|
|
112
|
+
"--seccomp",
|
|
113
|
+
"3",
|
|
114
|
+
"--",
|
|
115
|
+
"/usr/bin/unshare",
|
|
116
|
+
"--mount",
|
|
117
|
+
"/usr/bin/true",
|
|
118
|
+
], ["ignore", "pipe", "pipe", filter.fd]);
|
|
119
|
+
await run(policy.systemdRunPath, [
|
|
120
|
+
"--user",
|
|
121
|
+
"--pipe",
|
|
122
|
+
"--wait",
|
|
123
|
+
"--service-type=exec",
|
|
124
|
+
"--quiet",
|
|
125
|
+
"--property=MemoryMax=16777216",
|
|
126
|
+
"--property=MemorySwapMax=0",
|
|
127
|
+
"--property=TasksMax=2",
|
|
128
|
+
"--",
|
|
129
|
+
"/usr/bin/true",
|
|
130
|
+
]);
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
await filter.close();
|
|
134
|
+
await rm(directory, { recursive: true, force: true });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const capture = async (executable, arguments_, maximumBytes) => {
|
|
139
|
+
if (arguments_.length === 0)
|
|
140
|
+
return "content-addressed-worker-v1";
|
|
141
|
+
const result = await collect(executable, arguments_, maximumBytes);
|
|
142
|
+
if (result.code !== 0)
|
|
143
|
+
throw new TypeError(`Replay executable probe failed: ${executable}`);
|
|
144
|
+
return result.stdout.length > 0 ? result.stdout : result.stderr;
|
|
145
|
+
};
|
|
146
|
+
const run = async (executable, arguments_, stdio) => {
|
|
147
|
+
const result = await collect(executable, arguments_, VERSION_OUTPUT_LIMIT, stdio);
|
|
148
|
+
if (result.code !== 0)
|
|
149
|
+
throw new TypeError(`Replay feature probe failed (${String(result.code)}): ${executable}: ${result.stderr.trim()}`);
|
|
150
|
+
};
|
|
151
|
+
const runExpectFailure = async (executable, arguments_, stdio) => {
|
|
152
|
+
const result = await collect(executable, arguments_, VERSION_OUTPUT_LIMIT, stdio);
|
|
153
|
+
if (result.code === 0)
|
|
154
|
+
throw new TypeError(`Replay denial probe unexpectedly succeeded: ${executable}`);
|
|
155
|
+
};
|
|
156
|
+
const collect = async (executable, arguments_, maximumBytes, stdio = ["ignore", "pipe", "pipe"]) => new Promise((resolve, reject) => {
|
|
157
|
+
const child = spawn(executable, [...arguments_], { stdio });
|
|
158
|
+
const timeout = setTimeout(() => child.kill("SIGKILL"), 5_000);
|
|
159
|
+
timeout.unref();
|
|
160
|
+
let stdout = "";
|
|
161
|
+
let stderr = "";
|
|
162
|
+
const retain = (current, chunk) => {
|
|
163
|
+
const remaining = Math.max(0, maximumBytes - Buffer.byteLength(current));
|
|
164
|
+
return current + chunk.subarray(0, remaining).toString("utf8");
|
|
165
|
+
};
|
|
166
|
+
if (child.stdout !== null)
|
|
167
|
+
child.stdout.on("data", (chunk) => {
|
|
168
|
+
stdout = retain(stdout, chunk);
|
|
169
|
+
});
|
|
170
|
+
if (child.stderr !== null)
|
|
171
|
+
child.stderr.on("data", (chunk) => {
|
|
172
|
+
stderr = retain(stderr, chunk);
|
|
173
|
+
});
|
|
174
|
+
child.once("error", (error) => {
|
|
175
|
+
clearTimeout(timeout);
|
|
176
|
+
reject(error);
|
|
177
|
+
});
|
|
178
|
+
child.once("close", (code) => {
|
|
179
|
+
clearTimeout(timeout);
|
|
180
|
+
resolve({ code, stdout, stderr });
|
|
181
|
+
});
|
|
182
|
+
});
|
|
@@ -5,12 +5,17 @@ import { registerOfficialTools } from "./registerOfficialTools.js";
|
|
|
5
5
|
import { registerSessionTools } from "./registerSessionTools.js";
|
|
6
6
|
import { registerNativeTools } from "./registerNativeTools.js";
|
|
7
7
|
import { registerArtifactTools } from "./registerArtifactTools.js";
|
|
8
|
+
import { registerManagedTools } from "./registerManagedTools.js";
|
|
9
|
+
import { registerManagedWorkflowTools } from "./registerManagedWorkflowTools.js";
|
|
8
10
|
import { silentLogger } from "../logger.js";
|
|
9
11
|
import { registerGuidedPrompts } from "./registerPrompts.js";
|
|
10
12
|
import { registerEvidenceResources } from "./registerEvidenceResources.js";
|
|
11
13
|
import { createServerIdentity } from "../serverIdentity.js";
|
|
12
14
|
import { registerBrowserTools } from "./registerBrowserTools.js";
|
|
13
15
|
import { registerElectronTools } from "./registerElectronTools.js";
|
|
16
|
+
import { registerApplicationTools } from "./registerApplicationTools.js";
|
|
17
|
+
import { LinuxJavaScriptReplayRunner } from "../replay/LinuxJavaScriptReplayRunner.js";
|
|
18
|
+
import { SystemJavaScriptReplayHost } from "../replay/SystemJavaScriptReplayHost.js";
|
|
14
19
|
/**
|
|
15
20
|
* Construct one MCP server without acquiring subprocess resources.
|
|
16
21
|
* Supplying a session adds target lifecycle tools; omitting it retains the
|
|
@@ -66,6 +71,14 @@ export const createServer = (analysis, session, options = {}) => {
|
|
|
66
71
|
server.sendResourceListChanged();
|
|
67
72
|
return recorded;
|
|
68
73
|
};
|
|
74
|
+
const recordEvidenceWithUnknown = session === undefined
|
|
75
|
+
? undefined
|
|
76
|
+
: (evidence, input) => {
|
|
77
|
+
const recorded = session.recordEvidenceWithUnknown(evidence, input);
|
|
78
|
+
if (recorded.ok)
|
|
79
|
+
server.sendResourceListChanged();
|
|
80
|
+
return recorded;
|
|
81
|
+
};
|
|
69
82
|
registerOfficialTools(server, analysis, {
|
|
70
83
|
logger: toolLogger,
|
|
71
84
|
activeTarget,
|
|
@@ -96,6 +109,26 @@ export const createServer = (analysis, session, options = {}) => {
|
|
|
96
109
|
? {}
|
|
97
110
|
: { permissionAuthority: options.permissionAuthority }),
|
|
98
111
|
});
|
|
112
|
+
registerManagedTools(server, analysis, {
|
|
113
|
+
logger: toolLogger,
|
|
114
|
+
activeTarget,
|
|
115
|
+
recordEvidence,
|
|
116
|
+
});
|
|
117
|
+
if (session !== undefined)
|
|
118
|
+
registerManagedWorkflowTools(server, {
|
|
119
|
+
logger: toolLogger,
|
|
120
|
+
recordEvidence,
|
|
121
|
+
recordEvidenceWithUnknown,
|
|
122
|
+
session,
|
|
123
|
+
runtime: {
|
|
124
|
+
policy: options.managedRuntimePolicy ?? {
|
|
125
|
+
enabled: false,
|
|
126
|
+
roots: [],
|
|
127
|
+
executablePath: "/usr/bin/dotnet",
|
|
128
|
+
},
|
|
129
|
+
authority: options.permissionAuthority,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
99
132
|
registerBrowserTools(server, {
|
|
100
133
|
logger: toolLogger,
|
|
101
134
|
browser: options.browserObservation,
|
|
@@ -108,6 +141,25 @@ export const createServer = (analysis, session, options = {}) => {
|
|
|
108
141
|
permissionAuthority: options.permissionAuthority,
|
|
109
142
|
recordEvidence,
|
|
110
143
|
});
|
|
144
|
+
registerApplicationTools(server, {
|
|
145
|
+
logger: toolLogger,
|
|
146
|
+
recordEvidence,
|
|
147
|
+
recordEvidenceWithUnknown,
|
|
148
|
+
replay: {
|
|
149
|
+
policy: options.javascriptReplayPolicy ?? {
|
|
150
|
+
enabled: false,
|
|
151
|
+
roots: [],
|
|
152
|
+
nodePath: process.execPath,
|
|
153
|
+
bubblewrapPath: "/usr/bin/bwrap",
|
|
154
|
+
systemdRunPath: "/usr/bin/systemd-run",
|
|
155
|
+
systemctlPath: "/usr/bin/systemctl",
|
|
156
|
+
shellPath: "/usr/bin/bash",
|
|
157
|
+
},
|
|
158
|
+
host: options.javascriptReplayHost ?? new SystemJavaScriptReplayHost(),
|
|
159
|
+
runner: options.javascriptReplayRunner ?? new LinuxJavaScriptReplayRunner(),
|
|
160
|
+
authority: options.permissionAuthority,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
111
163
|
registerGuidedPrompts(server, analysis, session);
|
|
112
164
|
if (session !== undefined) {
|
|
113
165
|
registerEvidenceResources(server, session);
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { compareApplicationVersionsEvidenceValidated, traceApplicationFeatureEvidenceValidated, } from "../application/JavaScriptApplicationWorkflowService.js";
|
|
2
|
+
import { runControlledReplayValidated, } from "../application/JavaScriptReplayService.js";
|
|
3
|
+
import { APPLICATION_TOOL_CONTRACTS } from "../contracts/applicationToolContracts.js";
|
|
4
|
+
import { compareApplicationVersionsInputSchema } from "../domain/javascriptApplicationVersionComparisonSchemas.js";
|
|
5
|
+
import { applicationVersionComparisonResultSchema } from "../domain/javascriptApplicationVersionComparisonSchemas.js";
|
|
6
|
+
import { traceApplicationFeatureInputSchema } from "../domain/javascriptFeatureTraceSchemas.js";
|
|
7
|
+
import { parseEvidence } from "../domain/evidence.js";
|
|
8
|
+
import { controlledReplayInputSchema, controlledReplayOutputSchema, } from "../domain/javascriptReplay.js";
|
|
9
|
+
import { logToolExecution } from "./toolLogging.js";
|
|
10
|
+
import { toCallToolResult } from "./toolResult.js";
|
|
11
|
+
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
|
|
12
|
+
import { safeParseToolInput } from "./toolInputValidation.js";
|
|
13
|
+
import { mcpProgressReporter } from "./mcpProgress.js";
|
|
14
|
+
/** Register provider-neutral JavaScript application graph workflows. */
|
|
15
|
+
export const registerApplicationTools = (server, options) => {
|
|
16
|
+
const [traceContract, compareContract, replayContract] = APPLICATION_TOOL_CONTRACTS;
|
|
17
|
+
server.registerTool(traceContract.name, toolRegistrationOptions(traceContract), async (input) => {
|
|
18
|
+
const parsedInput = safeParseToolInput(traceApplicationFeatureInputSchema, input, traceContract.name);
|
|
19
|
+
if (!parsedInput.ok)
|
|
20
|
+
return toCallToolResult(parsedInput, traceContract);
|
|
21
|
+
const parsed = parsedInput.value;
|
|
22
|
+
const result = await logToolExecution(options.logger, traceContract.name, () => Promise.resolve(traceApplicationFeatureEvidenceValidated(parsed)));
|
|
23
|
+
if (!result.ok)
|
|
24
|
+
return toCallToolResult(result, traceContract);
|
|
25
|
+
const sources = [parsed.application, ...parsed.native_observations];
|
|
26
|
+
const recorded = recordSources(options.recordEvidence, sources);
|
|
27
|
+
if (!recorded.ok)
|
|
28
|
+
return toCallToolResult(recorded, traceContract);
|
|
29
|
+
return recordResult(options, traceContract, result.value);
|
|
30
|
+
});
|
|
31
|
+
server.registerTool(compareContract.name, toolRegistrationOptions(compareContract), async (input) => {
|
|
32
|
+
const parsedInput = safeParseToolInput(compareApplicationVersionsInputSchema, input, compareContract.name);
|
|
33
|
+
if (!parsedInput.ok)
|
|
34
|
+
return toCallToolResult(parsedInput, compareContract);
|
|
35
|
+
const parsed = parsedInput.value;
|
|
36
|
+
const result = await logToolExecution(options.logger, compareContract.name, () => Promise.resolve(compareApplicationVersionsEvidenceValidated(parsed)));
|
|
37
|
+
if (!result.ok)
|
|
38
|
+
return toCallToolResult(result, compareContract);
|
|
39
|
+
const sources = [
|
|
40
|
+
parsed.left,
|
|
41
|
+
parsed.right,
|
|
42
|
+
...parsed.left_native_observations,
|
|
43
|
+
...parsed.right_native_observations,
|
|
44
|
+
];
|
|
45
|
+
const recorded = recordSources(options.recordEvidence, sources);
|
|
46
|
+
if (!recorded.ok)
|
|
47
|
+
return toCallToolResult(recorded, compareContract);
|
|
48
|
+
const comparison = applicationVersionComparisonResultSchema.parse(result.value.normalized_result);
|
|
49
|
+
const unknown = comparison.summary.unknown > 0;
|
|
50
|
+
return recordResult(options, compareContract, result.value, parsed.unknown_registry_approved === true && unknown);
|
|
51
|
+
});
|
|
52
|
+
server.registerTool(replayContract.name, toolRegistrationOptions(replayContract), async (input, context) => {
|
|
53
|
+
const parsedInput = safeParseToolInput(controlledReplayInputSchema, input, replayContract.name);
|
|
54
|
+
if (!parsedInput.ok)
|
|
55
|
+
return toCallToolResult(parsedInput, replayContract);
|
|
56
|
+
const parsed = parsedInput.value;
|
|
57
|
+
const result = await logToolExecution(options.logger, replayContract.name, () => runControlledReplayValidated(options.replay, parsed, {
|
|
58
|
+
signal: context.mcpReq.signal,
|
|
59
|
+
progress: mcpProgressReporter(context),
|
|
60
|
+
}));
|
|
61
|
+
if (!result.ok)
|
|
62
|
+
return toCallToolResult(result, replayContract);
|
|
63
|
+
const output = controlledReplayOutputSchema.parse(result.value);
|
|
64
|
+
if (output.evidence !== null) {
|
|
65
|
+
const sourcesRecorded = recordSources(options.recordEvidence, output.source_evidence.map((item) => parseEvidence(item)));
|
|
66
|
+
if (!sourcesRecorded.ok)
|
|
67
|
+
return toCallToolResult(sourcesRecorded, replayContract);
|
|
68
|
+
const recorded = options.recordEvidence?.(parseEvidence(output.evidence));
|
|
69
|
+
if (recorded !== undefined && !recorded.ok)
|
|
70
|
+
return toCallToolResult(recorded, replayContract);
|
|
71
|
+
}
|
|
72
|
+
return toCallToolResult(result, replayContract, {
|
|
73
|
+
evidenceResourcesAvailable: output.evidence !== null && options.recordEvidence !== undefined,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
const recordSources = (recordEvidence, sources) => {
|
|
78
|
+
for (const source of sources) {
|
|
79
|
+
const recorded = recordEvidence?.(source);
|
|
80
|
+
if (recorded !== undefined && !recorded.ok)
|
|
81
|
+
return recorded;
|
|
82
|
+
}
|
|
83
|
+
return { ok: true, value: null };
|
|
84
|
+
};
|
|
85
|
+
const recordResult = (options, contract, evidence, recordUnknown = false) => {
|
|
86
|
+
const recorded = recordUnknown
|
|
87
|
+
? options.recordEvidenceWithUnknown?.(evidence, {
|
|
88
|
+
approved: true,
|
|
89
|
+
question: "Which application entities remain unmatched or ambiguous across these versions?",
|
|
90
|
+
severity: "medium",
|
|
91
|
+
domain: "application-version-comparison",
|
|
92
|
+
supporting_evidence_ids: [evidence.evidence_id],
|
|
93
|
+
contradicting_evidence_ids: [],
|
|
94
|
+
required_authority: "shipped-artifact",
|
|
95
|
+
required_confidence: "observed",
|
|
96
|
+
required_environment: null,
|
|
97
|
+
recommended_probes: [
|
|
98
|
+
{
|
|
99
|
+
operation: "analyze_javascript_application",
|
|
100
|
+
rationale: "Repeat static reconstruction with complete artifacts and source maps when available.",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
operation: "reconcile_javascript_runtime",
|
|
104
|
+
rationale: "Add approved passive runtime Evidence without promoting it to static fact.",
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
relationships: [],
|
|
108
|
+
})
|
|
109
|
+
: options.recordEvidence?.(evidence);
|
|
110
|
+
if (recorded !== undefined && !recorded.ok)
|
|
111
|
+
return toCallToolResult(recorded, contract);
|
|
112
|
+
return toCallToolResult({ ok: true, value: evidence }, contract, {
|
|
113
|
+
evidenceResourcesAvailable: recorded !== undefined,
|
|
114
|
+
});
|
|
115
|
+
};
|
|
@@ -2,27 +2,35 @@ import { artifactComparisonInputSchema, compareArtifacts, } from "../domain/arti
|
|
|
2
2
|
import { createEvidence } from "../domain/evidence.js";
|
|
3
3
|
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
4
4
|
import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
|
|
5
|
-
import {
|
|
5
|
+
import { resolveSessionEvidenceIds } from "./sessionEvidence.js";
|
|
6
6
|
import { ARTIFACT_COMPARISON_PROVIDER } from "./sessionToolPolicies.js";
|
|
7
7
|
import { toCallToolResult } from "./toolResult.js";
|
|
8
8
|
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
|
|
9
9
|
import { runDerivedOperation } from "./runDerivedOperation.js";
|
|
10
|
+
import { safeParseToolInput } from "./toolInputValidation.js";
|
|
10
11
|
/** Register Evidence-backed deterministic artifact comparison. */
|
|
11
12
|
export const registerArtifactComparisonTool = (server, session, contract) => {
|
|
12
13
|
server.registerTool(contract.name, toolRegistrationOptions(contract), async (input, context) => {
|
|
13
|
-
const
|
|
14
|
-
|
|
14
|
+
const parsedInput = safeParseToolInput(artifactComparisonInputSchema, input, contract.name);
|
|
15
|
+
if (!parsedInput.ok)
|
|
16
|
+
return toCallToolResult(parsedInput, contract);
|
|
17
|
+
const parsed = parsedInput.value;
|
|
18
|
+
const expected = {
|
|
19
|
+
operation: "inventory_artifact",
|
|
20
|
+
predicate: "rea.analysis/v2",
|
|
21
|
+
};
|
|
22
|
+
const left = resolveSessionEvidenceIds(session, parsed.left_evidence_ids, expected);
|
|
15
23
|
if (!left.ok)
|
|
16
24
|
return toCallToolResult(left, contract);
|
|
17
|
-
const right =
|
|
25
|
+
const right = resolveSessionEvidenceIds(session, parsed.right_evidence_ids, expected);
|
|
18
26
|
if (!right.ok)
|
|
19
27
|
return toCallToolResult(right, contract);
|
|
20
28
|
const computed = await runDerivedOperation(context, contract.name, () => compareArtifacts(left.value, right.value, parsed.offset, parsed.limit));
|
|
21
29
|
if (!computed.ok)
|
|
22
30
|
return toCallToolResult(computed, contract);
|
|
23
31
|
const comparison = computed.value;
|
|
24
|
-
const leftEvidenceIds =
|
|
25
|
-
const rightEvidenceIds =
|
|
32
|
+
const leftEvidenceIds = parsed.left_evidence_ids;
|
|
33
|
+
const rightEvidenceIds = parsed.right_evidence_ids;
|
|
26
34
|
const evidence = createEvidence(undefined, ARTIFACT_COMPARISON_PROVIDER, {
|
|
27
35
|
predicateType: "rea.artifact-comparison/v1",
|
|
28
36
|
operation: contract.name,
|
|
@@ -49,8 +57,8 @@ const artifactUnknownInput = (input, status) => {
|
|
|
49
57
|
question: `Artifact comparison is ${status}`,
|
|
50
58
|
severity: status === "unknown" || status === "truncated" ? "high" : "medium",
|
|
51
59
|
domain: "artifact-comparison",
|
|
52
|
-
supporting_evidence_ids:
|
|
53
|
-
contradicting_evidence_ids:
|
|
60
|
+
supporting_evidence_ids: [...input.left_evidence_ids],
|
|
61
|
+
contradicting_evidence_ids: [...input.right_evidence_ids],
|
|
54
62
|
required_authority: "shipped-artifact",
|
|
55
63
|
required_confidence: "observed",
|
|
56
64
|
required_environment: null,
|
|
@@ -63,4 +71,3 @@ const artifactUnknownInput = (input, status) => {
|
|
|
63
71
|
relationships: [],
|
|
64
72
|
};
|
|
65
73
|
};
|
|
66
|
-
const evidenceIds = (input) => (Array.isArray(input) ? input : [input]).map(({ evidence_id: id }) => id);
|
|
@@ -9,19 +9,26 @@ import { captureWebScreenshotInputSchema, compareWebScreenshotsInputSchema, } fr
|
|
|
9
9
|
import { logToolExecution } from "./toolLogging.js";
|
|
10
10
|
import { toCallToolResult } from "./toolResult.js";
|
|
11
11
|
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
|
|
12
|
+
import { safeParseToolInput } from "./toolInputValidation.js";
|
|
12
13
|
import { mcpProgressReporter } from "./mcpProgress.js";
|
|
13
14
|
/** Register browser tools even when policy/provider availability denies execution. */
|
|
14
15
|
export const registerBrowserTools = (server, options) => {
|
|
15
16
|
const [listContract, inspectContract, analyzeContract, sessionContract, webMcpContract, captureDiffContract, screenshotContract, screenshotDiffContract,] = BROWSER_TOOL_CONTRACTS;
|
|
16
17
|
server.registerTool(listContract.name, toolRegistrationOptions(listContract), async (input, context) => {
|
|
17
|
-
const
|
|
18
|
+
const parsedInput = safeParseToolInput(listBrowserTargetsInputSchema, input, listContract.name);
|
|
19
|
+
if (!parsedInput.ok)
|
|
20
|
+
return toCallToolResult(parsedInput, listContract);
|
|
21
|
+
const parsed = parsedInput.value;
|
|
18
22
|
const result = await logToolExecution(options.logger, listContract.name, () => listBrowserTargets(options.browser, options.permissionAuthority, parsed, { signal: context.mcpReq.signal }));
|
|
19
23
|
if (!result.ok)
|
|
20
24
|
return toCallToolResult(result, listContract);
|
|
21
25
|
return evidenceResult(options, listContract, result.value);
|
|
22
26
|
});
|
|
23
27
|
server.registerTool(inspectContract.name, toolRegistrationOptions(inspectContract), async (input, context) => {
|
|
24
|
-
const
|
|
28
|
+
const parsedInput = safeParseToolInput(inspectWebPageInputSchema, input, inspectContract.name);
|
|
29
|
+
if (!parsedInput.ok)
|
|
30
|
+
return toCallToolResult(parsedInput, inspectContract);
|
|
31
|
+
const parsed = parsedInput.value;
|
|
25
32
|
const result = await logToolExecution(options.logger, inspectContract.name, () => inspectWebPage(options.browser, options.permissionAuthority, parsed, {
|
|
26
33
|
signal: context.mcpReq.signal,
|
|
27
34
|
progress: mcpProgressReporter(context),
|
|
@@ -31,7 +38,10 @@ export const registerBrowserTools = (server, options) => {
|
|
|
31
38
|
return evidenceResult(options, inspectContract, result.value);
|
|
32
39
|
});
|
|
33
40
|
server.registerTool(analyzeContract.name, toolRegistrationOptions(analyzeContract), async (input, context) => {
|
|
34
|
-
const
|
|
41
|
+
const parsedInput = safeParseToolInput(analyzeWebBundleInputSchema, input, analyzeContract.name);
|
|
42
|
+
if (!parsedInput.ok)
|
|
43
|
+
return toCallToolResult(parsedInput, analyzeContract);
|
|
44
|
+
const parsed = parsedInput.value;
|
|
35
45
|
const result = await logToolExecution(options.logger, analyzeContract.name, () => analyzeWebBundle(options.browser, options.permissionAuthority, parsed, {
|
|
36
46
|
signal: context.mcpReq.signal,
|
|
37
47
|
progress: mcpProgressReporter(context),
|
|
@@ -41,7 +51,10 @@ export const registerBrowserTools = (server, options) => {
|
|
|
41
51
|
return evidenceResult(options, analyzeContract, result.value);
|
|
42
52
|
});
|
|
43
53
|
server.registerTool(sessionContract.name, toolRegistrationOptions(sessionContract), async (input, context) => {
|
|
44
|
-
const
|
|
54
|
+
const parsedInput = safeParseToolInput(observeWebSessionInputSchema, input, sessionContract.name);
|
|
55
|
+
if (!parsedInput.ok)
|
|
56
|
+
return toCallToolResult(parsedInput, sessionContract);
|
|
57
|
+
const parsed = parsedInput.value;
|
|
45
58
|
const result = await logToolExecution(options.logger, sessionContract.name, () => observeWebSession(options.browser, options.permissionAuthority, parsed, {
|
|
46
59
|
signal: context.mcpReq.signal,
|
|
47
60
|
progress: mcpProgressReporter(context),
|
|
@@ -51,7 +64,10 @@ export const registerBrowserTools = (server, options) => {
|
|
|
51
64
|
return evidenceResult(options, sessionContract, result.value);
|
|
52
65
|
});
|
|
53
66
|
server.registerTool(webMcpContract.name, toolRegistrationOptions(webMcpContract), async (input, context) => {
|
|
54
|
-
const
|
|
67
|
+
const parsedInput = safeParseToolInput(discoverWebMcpToolsInputSchema, input, webMcpContract.name);
|
|
68
|
+
if (!parsedInput.ok)
|
|
69
|
+
return toCallToolResult(parsedInput, webMcpContract);
|
|
70
|
+
const parsed = parsedInput.value;
|
|
55
71
|
const result = await logToolExecution(options.logger, webMcpContract.name, () => discoverWebMcpTools(options.browser, options.permissionAuthority, parsed, {
|
|
56
72
|
signal: context.mcpReq.signal,
|
|
57
73
|
progress: mcpProgressReporter(context),
|
|
@@ -61,14 +77,20 @@ export const registerBrowserTools = (server, options) => {
|
|
|
61
77
|
return evidenceResult(options, webMcpContract, result.value);
|
|
62
78
|
});
|
|
63
79
|
server.registerTool(captureDiffContract.name, toolRegistrationOptions(captureDiffContract), async (input) => {
|
|
64
|
-
const
|
|
80
|
+
const parsedInput = safeParseToolInput(compareWebCapturesInputSchema, input, captureDiffContract.name);
|
|
81
|
+
if (!parsedInput.ok)
|
|
82
|
+
return toCallToolResult(parsedInput, captureDiffContract);
|
|
83
|
+
const parsed = parsedInput.value;
|
|
65
84
|
const result = await logToolExecution(options.logger, captureDiffContract.name, () => compareWebCaptureEvidence(options.browser, parsed));
|
|
66
85
|
if (!result.ok)
|
|
67
86
|
return toCallToolResult(result, captureDiffContract);
|
|
68
87
|
return evidenceResult(options, captureDiffContract, result.value);
|
|
69
88
|
});
|
|
70
89
|
server.registerTool(screenshotContract.name, toolRegistrationOptions(screenshotContract), async (input, context) => {
|
|
71
|
-
const
|
|
90
|
+
const parsedInput = safeParseToolInput(captureWebScreenshotInputSchema, input, screenshotContract.name);
|
|
91
|
+
if (!parsedInput.ok)
|
|
92
|
+
return toCallToolResult(parsedInput, screenshotContract);
|
|
93
|
+
const parsed = parsedInput.value;
|
|
72
94
|
const result = await logToolExecution(options.logger, screenshotContract.name, () => captureWebScreenshot(options.browser, options.permissionAuthority, parsed, {
|
|
73
95
|
signal: context.mcpReq.signal,
|
|
74
96
|
progress: mcpProgressReporter(context),
|
|
@@ -78,7 +100,10 @@ export const registerBrowserTools = (server, options) => {
|
|
|
78
100
|
return evidenceResult(options, screenshotContract, result.value);
|
|
79
101
|
});
|
|
80
102
|
server.registerTool(screenshotDiffContract.name, toolRegistrationOptions(screenshotDiffContract), async (input) => {
|
|
81
|
-
const
|
|
103
|
+
const parsedInput = safeParseToolInput(compareWebScreenshotsInputSchema, input, screenshotDiffContract.name);
|
|
104
|
+
if (!parsedInput.ok)
|
|
105
|
+
return toCallToolResult(parsedInput, screenshotDiffContract);
|
|
106
|
+
const parsed = parsedInput.value;
|
|
82
107
|
const result = await logToolExecution(options.logger, screenshotDiffContract.name, () => compareWebScreenshotEvidence(options.browser, parsed));
|
|
83
108
|
if (!result.ok)
|
|
84
109
|
return toCallToolResult(result, screenshotDiffContract);
|
|
@@ -1,31 +1,29 @@
|
|
|
1
|
+
import { readEvidenceBundle } from "../application/EvidenceBundleFiles.js";
|
|
1
2
|
import { bundleComparisonInputSchema, compareBundles, } from "../domain/bundleComparison.js";
|
|
2
3
|
import { createEvidence } from "../domain/evidence.js";
|
|
3
|
-
import { EvidenceIntegrityError } from "../domain/errors.js";
|
|
4
4
|
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
5
|
-
import {
|
|
5
|
+
import { ok } from "../domain/result.js";
|
|
6
6
|
import { BUNDLE_COMPARISON_PROVIDER } from "./sessionToolPolicies.js";
|
|
7
7
|
import { toCallToolResult } from "./toolResult.js";
|
|
8
|
-
import { isSessionEvidence } from "./sessionEvidence.js";
|
|
9
8
|
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
|
|
10
9
|
import { runDerivedOperation } from "./runDerivedOperation.js";
|
|
10
|
+
import { safeParseToolInput } from "./toolInputValidation.js";
|
|
11
11
|
/** Register canonical Evidence bundle comparison. */
|
|
12
|
-
export const registerBundleComparisonTool = (server, session, contract) => {
|
|
12
|
+
export const registerBundleComparisonTool = (server, session, contract, evidenceFilePolicy) => {
|
|
13
13
|
server.registerTool(contract.name, toolRegistrationOptions(contract), async (input, context) => {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return toCallToolResult(err(new EvidenceIntegrityError("Bundle comparison input unknown history is not present in this session")), contract);
|
|
28
|
-
const computed = await runDerivedOperation(context, contract.name, () => compareBundles(parsed.left, parsed.right, parsed.record_pairs, parsed.offset, parsed.limit));
|
|
14
|
+
const parsedInput = safeParseToolInput(bundleComparisonInputSchema, input, contract.name);
|
|
15
|
+
if (!parsedInput.ok)
|
|
16
|
+
return toCallToolResult(parsedInput, contract);
|
|
17
|
+
const parsed = parsedInput.value;
|
|
18
|
+
const [left, right] = await Promise.all([
|
|
19
|
+
readEvidenceBundle(parsed.left_bundle_path, evidenceFilePolicy),
|
|
20
|
+
readEvidenceBundle(parsed.right_bundle_path, evidenceFilePolicy),
|
|
21
|
+
]);
|
|
22
|
+
if (!left.ok)
|
|
23
|
+
return toCallToolResult(left, contract);
|
|
24
|
+
if (!right.ok)
|
|
25
|
+
return toCallToolResult(right, contract);
|
|
26
|
+
const computed = await runDerivedOperation(context, contract.name, () => compareBundles(left.value, right.value, parsed.record_pairs, parsed.offset, parsed.limit));
|
|
29
27
|
if (!computed.ok)
|
|
30
28
|
return toCallToolResult(computed, contract);
|
|
31
29
|
const comparison = computed.value;
|
|
@@ -43,7 +41,6 @@ export const registerBundleComparisonTool = (server, session, contract) => {
|
|
|
43
41
|
confidence: "derived",
|
|
44
42
|
authority: "analyst-inference",
|
|
45
43
|
limitations: comparison.limitations,
|
|
46
|
-
evidenceLinks: sourceIds,
|
|
47
44
|
});
|
|
48
45
|
const recorded = session.recordEvidence(evidence);
|
|
49
46
|
return toCallToolResult(recorded.ok ? ok(evidence) : recorded, contract);
|