rea-agents 1.2.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 +95 -27
- package/bridge/hopper_bridge.py +172 -14
- package/dist/application/AnalysisSnapshotCache.js +131 -0
- package/dist/application/AnalysisSnapshotFiles.js +31 -0
- package/dist/application/ArtifactGraphConstruction.js +1 -1
- package/dist/application/ArtifactInventory.js +100 -24
- package/dist/application/AuthorizedArtifactInventory.js +16 -0
- package/dist/application/BinarySession.js +149 -36
- package/dist/application/BinarySessionPort.js +1 -0
- package/dist/application/BoundedJsonFiles.js +109 -0
- package/dist/application/CapabilityInventory.js +127 -0
- package/dist/application/ClientRegistrationStatus.js +69 -0
- package/dist/application/CrossVersionInventory.js +109 -0
- package/dist/application/CrossVersionInvestigation.js +358 -0
- package/dist/application/DirectAnalysis.js +205 -24
- package/dist/application/Doctor.js +104 -3
- package/dist/application/EvidenceBundleFiles.js +15 -108
- package/dist/application/EvidenceLedger.js +3 -2
- package/dist/application/InvestigationProviders.js +48 -0
- package/dist/application/InvestigationWorkspaceStore.js +214 -0
- package/dist/application/LinuxHopper.js +72 -48
- package/dist/application/PermissionAuthority.js +183 -0
- package/dist/application/PermissionConfiguration.js +26 -0
- package/dist/application/ProcessCaptureAuthority.js +2 -2
- package/dist/application/ProcessCaptureError.js +25 -0
- package/dist/application/ProcessCaptureLifecycle.js +22 -2
- package/dist/application/ProcessCli.js +111 -31
- package/dist/application/ProcessHarness.js +6 -3
- package/dist/application/ProgressReporter.js +30 -0
- package/dist/application/ProjectPermissionStore.js +110 -0
- package/dist/application/ReferenceSourceImportEntries.js +18 -3
- package/dist/application/ReferenceSourceImportTypes.js +32 -0
- package/dist/application/Setup.js +35 -101
- package/dist/application/SetupClients.js +19 -0
- package/dist/application/SetupInstallFailure.js +53 -0
- package/dist/application/SetupPlan.js +31 -0
- package/dist/application/SupportedClients.js +41 -0
- package/dist/application/Uninstall.js +34 -24
- package/dist/application/UnknownEvidence.js +33 -0
- package/dist/application/Upgrade.js +3 -1
- package/dist/application/runtime.js +2 -2
- package/dist/artifacts/ArtifactProvider.js +15 -8
- package/dist/catalogIdentity.js +121 -0
- package/dist/cli.js +76 -13
- package/dist/cliEvidenceCommands.js +52 -12
- package/dist/cliInvestigationCommands.js +137 -0
- package/dist/cliLogging.js +20 -3
- package/dist/cliOutput.js +41 -0
- package/dist/cliPolicyCommands.js +115 -0
- package/dist/config.js +102 -27
- package/dist/contracts/artifactToolContracts.js +14 -1
- package/dist/contracts/errorSchemas.js +98 -0
- package/dist/contracts/promptContracts.js +256 -0
- package/dist/contracts/sessionLifecycleInputs.js +11 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +20 -7
- package/dist/contracts/toolOutputSchemas.js +70 -9
- package/dist/domain/analysisSnapshot.js +149 -0
- 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 +35 -10
- package/dist/domain/errors.js +384 -32
- package/dist/domain/evidence.js +4 -2
- package/dist/domain/evidenceBundle.js +28 -0
- package/dist/domain/hopperStartupFailure.js +55 -0
- package/dist/domain/investigationWorkspace.js +341 -0
- package/dist/domain/jsonValue.js +2 -0
- package/dist/domain/nativeInspection.js +3 -2
- package/dist/domain/permissionPolicy.js +157 -0
- package/dist/domain/processCapture.js +5 -4
- package/dist/domain/processComparison.js +6 -4
- 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 +51 -20
- package/dist/hopper/HopperClient.js +45 -3
- package/dist/hopper/HopperProvider.js +1 -0
- package/dist/identity.js +10 -3
- package/dist/logger.js +2 -2
- package/dist/main.js +139 -28
- package/dist/server/createServer.js +54 -5
- package/dist/server/mcpProgress.js +23 -0
- package/dist/server/promptCompletion.js +144 -0
- package/dist/server/registerArtifactComparisonTool.js +6 -2
- package/dist/server/registerBundleComparisonTool.js +6 -2
- package/dist/server/registerEnhancedTools.js +20 -7
- package/dist/server/registerEvidenceResources.js +302 -0
- package/dist/server/registerEvidenceTools.js +59 -8
- package/dist/server/registerFunctionComparisonTool.js +7 -3
- package/dist/server/registerInvestigationTools.js +111 -18
- package/dist/server/registerOfficialTools.js +25 -14
- package/dist/server/registerProcessComparisonTool.js +8 -10
- package/dist/server/registerPrompts.js +67 -0
- package/dist/server/registerSessionStatusTool.js +52 -0
- package/dist/server/registerSessionTools.js +178 -22
- package/dist/server/runDerivedOperation.js +35 -0
- package/dist/server/sessionEvidence.js +2 -8
- package/dist/server/sessionToolPolicies.js +1 -42
- package/dist/server/toolResult.js +34 -6
- package/dist/serverIdentity.js +70 -0
- package/install.sh +11 -11
- package/package.json +10 -6
- package/scripts/hopper-demo-x11.py +349 -0
- package/scripts/rea.mjs +6 -3
- package/skills/rea-analysis/SKILL.md +9 -2
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { runCrossVersionInvestigation } from "../application/CrossVersionInvestigation.js";
|
|
1
2
|
import { buildCallPath, callPathInputSchema } from "../domain/callPath.js";
|
|
2
3
|
import { correlateStaticAndRuntime, staticRuntimeCorrelationInputSchema, } from "../domain/staticRuntimeCorrelation.js";
|
|
3
4
|
import { reconstructionVerificationInputSchema, verifyReconstruction, } from "../domain/reconstructionVerification.js";
|
|
4
|
-
import { changedBehaviorInputSchema, findChangedBehavior, } from "../domain/changedBehavior.js";
|
|
5
|
+
import { changedBehaviorResultSchema, changedBehaviorInputSchema, findChangedBehavior, } from "../domain/changedBehavior.js";
|
|
5
6
|
import { createEvidence } from "../domain/evidence.js";
|
|
6
7
|
import { EvidenceIntegrityError } from "../domain/errors.js";
|
|
7
8
|
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
@@ -9,21 +10,105 @@ import { err, ok } from "../domain/result.js";
|
|
|
9
10
|
import { CALL_PATH_PROVIDER, CHANGED_BEHAVIOR_PROVIDER, RECONSTRUCTION_PROVIDER, STATIC_RUNTIME_PROVIDER, } from "./sessionToolPolicies.js";
|
|
10
11
|
import { toCallToolResult } from "./toolResult.js";
|
|
11
12
|
import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
|
|
13
|
+
import { AnalysisProtocolError, PermissionRequiredError, } from "../domain/errors.js";
|
|
14
|
+
import { mcpProgressReporter } from "./mcpProgress.js";
|
|
15
|
+
import { runDerivedOperation } from "./runDerivedOperation.js";
|
|
12
16
|
/** Register Evidence-composed differential investigation workflows. */
|
|
13
|
-
export const registerInvestigationTools = (server, session, contracts) => {
|
|
14
|
-
registerChangedBehavior(server, session, contracts[0]);
|
|
17
|
+
export const registerInvestigationTools = (server, session, contracts, policies) => {
|
|
18
|
+
registerChangedBehavior(server, session, contracts[0], policies);
|
|
15
19
|
registerCallPath(server, session, contracts[1]);
|
|
16
20
|
registerStaticRuntime(server, session, contracts[2]);
|
|
17
21
|
registerReconstruction(server, session, contracts[3]);
|
|
18
22
|
};
|
|
19
|
-
const
|
|
20
|
-
|
|
23
|
+
const investigationExecution = (session, signal, inputRoots, integrityContinueEnabled, progress) => ({
|
|
24
|
+
session,
|
|
25
|
+
signal,
|
|
26
|
+
inputRoots,
|
|
27
|
+
integrityContinueEnabled,
|
|
28
|
+
...(progress === undefined ? {} : { progress }),
|
|
29
|
+
});
|
|
30
|
+
const isIncomplete = (status) => status === "unknown" || status === "truncated";
|
|
31
|
+
const registerChangedBehavior = (server, session, contract, policies) => {
|
|
32
|
+
server.registerTool(contract.name, contractOptions(contract), async (input, context) => {
|
|
21
33
|
const parsed = changedBehaviorInputSchema.parse(input);
|
|
34
|
+
if (parsed.investigation_run !== undefined) {
|
|
35
|
+
const progress = mcpProgressReporter(context);
|
|
36
|
+
if (policies.permissionAuthority !== undefined) {
|
|
37
|
+
const scopes = [
|
|
38
|
+
{
|
|
39
|
+
capability: "investigation_workspace_read",
|
|
40
|
+
roots: [parsed.investigation_run.workspace_path],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
capability: "investigation_workspace_write",
|
|
44
|
+
roots: [parsed.investigation_run.workspace_path],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
capability: "investigation_input",
|
|
48
|
+
roots: [
|
|
49
|
+
parsed.investigation_run.left_path,
|
|
50
|
+
parsed.investigation_run.right_path,
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
for (const scope of scopes) {
|
|
55
|
+
const authorized = await policies.permissionAuthority.authorize({
|
|
56
|
+
...scope,
|
|
57
|
+
executables: [],
|
|
58
|
+
environment_names: [],
|
|
59
|
+
network: "none",
|
|
60
|
+
mount: false,
|
|
61
|
+
operation_identity: `${contract.name}:${scope.capability}:${scope.roots.join(":")}`,
|
|
62
|
+
}, scope.capability === "investigation_input" ? "read" : "write");
|
|
63
|
+
if (!authorized.ok)
|
|
64
|
+
return toCallToolResult(err(authorized.error instanceof PermissionRequiredError
|
|
65
|
+
? authorized.error
|
|
66
|
+
: new AnalysisProtocolError(authorized.error.message, {
|
|
67
|
+
cause: authorized.error,
|
|
68
|
+
})), contract);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const investigated = await runCrossVersionInvestigation(parsed.investigation_run, policies.evidenceFiles, investigationExecution(session, context.mcpReq.signal, policies.inputRoots, policies.integrityContinueEnabled?.() ?? false, progress));
|
|
72
|
+
if (!investigated.ok)
|
|
73
|
+
return toCallToolResult(investigated, contract);
|
|
74
|
+
const workspace = investigated.value.workspace;
|
|
75
|
+
if (session.retainInvestigationWorkspace(workspace) === "added")
|
|
76
|
+
server.sendResourceListChanged();
|
|
77
|
+
const result = changedBehaviorResultSchema.parse(investigated.value.evidence.normalized_result);
|
|
78
|
+
const toolResult = toCallToolResult(recordWorkflowEvidence(session, investigated.value.evidence, parsed.unknown_registry_approved, isIncomplete(result.behavior_status), {
|
|
79
|
+
question: `Automatic changed behavior run ${result.investigation_run?.run_id ?? "unknown"} remains ${result.behavior_status}`,
|
|
80
|
+
domain: "changed-behavior",
|
|
81
|
+
requiredAuthority: "controlled-replay",
|
|
82
|
+
requiredConfidence: "observed",
|
|
83
|
+
probes: [
|
|
84
|
+
{
|
|
85
|
+
operation: "capture_process_scenario",
|
|
86
|
+
rationale: "Capture both versions under the same bounded scenario and environment.",
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
}), contract);
|
|
90
|
+
return {
|
|
91
|
+
...toolResult,
|
|
92
|
+
content: [
|
|
93
|
+
...toolResult.content,
|
|
94
|
+
{
|
|
95
|
+
type: "resource_link",
|
|
96
|
+
uri: `rea://workspace/${workspace.workspace_id}/revision/${String(workspace.revision)}`,
|
|
97
|
+
name: `${workspace.workspace_id} revision ${String(workspace.revision)}`,
|
|
98
|
+
description: "Immutable CAS-linked investigation workspace revision",
|
|
99
|
+
mimeType: "application/json",
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
22
104
|
const closure = evidenceClosure(session, comparisonClosure(parsed.comparisons));
|
|
23
105
|
if (!closure.ok)
|
|
24
106
|
return toCallToolResult(closure, contract);
|
|
25
107
|
const links = closure.value;
|
|
26
|
-
const
|
|
108
|
+
const computed = await runDerivedOperation(context, contract.name, () => findChangedBehavior(parsed.comparisons, parsed.offset, parsed.limit));
|
|
109
|
+
if (!computed.ok)
|
|
110
|
+
return toCallToolResult(computed, contract);
|
|
111
|
+
const result = computed.value;
|
|
27
112
|
const evidence = createEvidence(undefined, CHANGED_BEHAVIOR_PROVIDER, {
|
|
28
113
|
predicateType: "rea.changed-behavior/v1",
|
|
29
114
|
operation: contract.name,
|
|
@@ -38,9 +123,8 @@ const registerChangedBehavior = (server, session, contract) => {
|
|
|
38
123
|
limitations: result.limitations,
|
|
39
124
|
evidenceLinks: links,
|
|
40
125
|
});
|
|
41
|
-
const recorded = recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, result.behavior_status
|
|
42
|
-
|
|
43
|
-
question: `Changed behavior result ${evidence.evidence_id} remains ${result.behavior_status}`,
|
|
126
|
+
const recorded = recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, isIncomplete(result.behavior_status), {
|
|
127
|
+
question: "Did both versions behave the same under a complete controlled replay?",
|
|
44
128
|
domain: "changed-behavior",
|
|
45
129
|
requiredAuthority: "controlled-replay",
|
|
46
130
|
requiredConfidence: "observed",
|
|
@@ -55,13 +139,16 @@ const registerChangedBehavior = (server, session, contract) => {
|
|
|
55
139
|
});
|
|
56
140
|
};
|
|
57
141
|
const registerCallPath = (server, session, contract) => {
|
|
58
|
-
server.registerTool(contract.name, contractOptions(contract), (input) => {
|
|
142
|
+
server.registerTool(contract.name, contractOptions(contract), async (input, context) => {
|
|
59
143
|
const parsed = callPathInputSchema.parse(input);
|
|
60
144
|
const closure = evidenceClosure(session, functionEvidenceIds(parsed.functions));
|
|
61
145
|
if (!closure.ok)
|
|
62
146
|
return toCallToolResult(closure, contract);
|
|
63
147
|
const links = closure.value;
|
|
64
|
-
const
|
|
148
|
+
const computed = await runDerivedOperation(context, contract.name, () => buildCallPath(parsed));
|
|
149
|
+
if (!computed.ok)
|
|
150
|
+
return toCallToolResult(computed, contract);
|
|
151
|
+
const result = computed.value;
|
|
65
152
|
const evidence = createEvidence(undefined, CALL_PATH_PROVIDER, {
|
|
66
153
|
predicateType: "rea.call-path/v1",
|
|
67
154
|
operation: contract.name,
|
|
@@ -80,7 +167,7 @@ const registerCallPath = (server, session, contract) => {
|
|
|
80
167
|
evidenceLinks: links,
|
|
81
168
|
});
|
|
82
169
|
const recorded = recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, result.status === "unknown" || result.status === "truncated", {
|
|
83
|
-
question:
|
|
170
|
+
question: "Can the requested call path be established from complete analysis?",
|
|
84
171
|
domain: "call-path",
|
|
85
172
|
requiredAuthority: "shipped-artifact",
|
|
86
173
|
requiredConfidence: "derived",
|
|
@@ -95,7 +182,7 @@ const registerCallPath = (server, session, contract) => {
|
|
|
95
182
|
});
|
|
96
183
|
};
|
|
97
184
|
const registerStaticRuntime = (server, session, contract) => {
|
|
98
|
-
server.registerTool(contract.name, contractOptions(contract), (input) => {
|
|
185
|
+
server.registerTool(contract.name, contractOptions(contract), async (input, context) => {
|
|
99
186
|
const parsed = staticRuntimeCorrelationInputSchema.parse(input);
|
|
100
187
|
const closure = evidenceClosure(session, comparisonClosure([
|
|
101
188
|
...parsed.static_comparisons,
|
|
@@ -104,7 +191,10 @@ const registerStaticRuntime = (server, session, contract) => {
|
|
|
104
191
|
if (!closure.ok)
|
|
105
192
|
return toCallToolResult(closure, contract);
|
|
106
193
|
const links = closure.value;
|
|
107
|
-
const
|
|
194
|
+
const computed = await runDerivedOperation(context, contract.name, () => correlateStaticAndRuntime(parsed));
|
|
195
|
+
if (!computed.ok)
|
|
196
|
+
return toCallToolResult(computed, contract);
|
|
197
|
+
const result = computed.value;
|
|
108
198
|
const evidence = createEvidence(undefined, STATIC_RUNTIME_PROVIDER, {
|
|
109
199
|
predicateType: "rea.static-runtime-correlation/v1",
|
|
110
200
|
operation: contract.name,
|
|
@@ -120,7 +210,7 @@ const registerStaticRuntime = (server, session, contract) => {
|
|
|
120
210
|
evidenceLinks: links,
|
|
121
211
|
});
|
|
122
212
|
return toCallToolResult(recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, result.status === "unknown" || result.status === "truncated", {
|
|
123
|
-
question:
|
|
213
|
+
question: "Does runtime behavior match the available static analysis?",
|
|
124
214
|
domain: "static-runtime-correlation",
|
|
125
215
|
requiredAuthority: null,
|
|
126
216
|
requiredConfidence: "derived",
|
|
@@ -134,7 +224,7 @@ const registerStaticRuntime = (server, session, contract) => {
|
|
|
134
224
|
});
|
|
135
225
|
};
|
|
136
226
|
const registerReconstruction = (server, session, contract) => {
|
|
137
|
-
server.registerTool(contract.name, contractOptions(contract), (input) => {
|
|
227
|
+
server.registerTool(contract.name, contractOptions(contract), async (input, context) => {
|
|
138
228
|
const parsed = reconstructionVerificationInputSchema.parse(input);
|
|
139
229
|
const owned = session.exportEvidenceBundle();
|
|
140
230
|
const ownedUnknowns = new Set(owned.unknowns.map(({ revision_digest: digest }) => digest));
|
|
@@ -142,7 +232,10 @@ const registerReconstruction = (server, session, contract) => {
|
|
|
142
232
|
if (parsed.evidence_bundle.records.some(({ evidence_id: id }) => !ownedEvidenceIds.has(id)) ||
|
|
143
233
|
parsed.evidence_bundle.unknowns.some(({ revision_digest: digest }) => !ownedUnknowns.has(digest)))
|
|
144
234
|
return toCallToolResult(err(new EvidenceIntegrityError("Reconstruction input unknown history is not present in this session")), contract);
|
|
145
|
-
const
|
|
235
|
+
const computed = await runDerivedOperation(context, contract.name, () => verifyReconstruction(parsed.specification, owned, parsed.offset, parsed.limit));
|
|
236
|
+
if (!computed.ok)
|
|
237
|
+
return toCallToolResult(computed, contract);
|
|
238
|
+
const result = computed.value;
|
|
146
239
|
const closure = evidenceClosure(session, result.evidence_links);
|
|
147
240
|
if (!closure.ok)
|
|
148
241
|
return toCallToolResult(closure, contract);
|
|
@@ -163,7 +256,7 @@ const registerReconstruction = (server, session, contract) => {
|
|
|
163
256
|
evidenceLinks: links,
|
|
164
257
|
});
|
|
165
258
|
return toCallToolResult(recordWorkflowEvidence(session, evidence, parsed.unknown_registry_approved, result.status === "unknown", {
|
|
166
|
-
question:
|
|
259
|
+
question: "Does the reconstruction satisfy every declared claim?",
|
|
167
260
|
domain: "reconstruction-verification",
|
|
168
261
|
requiredAuthority: null,
|
|
169
262
|
requiredConfidence: "derived",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { OFFICIAL_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
|
|
2
|
-
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
2
|
+
import { jsonObjectSchema, jsonValueSchema, } from "../domain/jsonValue.js";
|
|
3
3
|
import { toCallToolResult } from "./toolResult.js";
|
|
4
4
|
import { logToolExecution } from "./toolLogging.js";
|
|
5
5
|
import { createEvidence } from "../domain/evidence.js";
|
|
6
6
|
import { AnalysisCapabilityUnavailableError, UnknownRegistryError, } from "../domain/errors.js";
|
|
7
|
+
import { mcpProgressReporter } from "./mcpProgress.js";
|
|
7
8
|
/** Register direct bridge proxies, preserving MCP cancellation and typed errors. */
|
|
8
9
|
export const registerOfficialTools = (server, analysis, options) => {
|
|
9
10
|
for (const contract of OFFICIAL_TOOL_CONTRACTS) {
|
|
@@ -23,9 +24,24 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
|
|
|
23
24
|
annotations: contract.annotations,
|
|
24
25
|
}, async (input, context) => {
|
|
25
26
|
const arguments_ = projectOfficialArguments(contract, input);
|
|
27
|
+
const progress = mcpProgressReporter(context);
|
|
28
|
+
await progress.report({
|
|
29
|
+
phase: contract.name,
|
|
30
|
+
completed: 0,
|
|
31
|
+
total: 1,
|
|
32
|
+
message: "started",
|
|
33
|
+
});
|
|
26
34
|
const result = await logToolExecution(registration.logger, contract.name, () => analysis.execute(contract.name, arguments_, {
|
|
27
35
|
signal: context.mcpReq.signal,
|
|
36
|
+
progress,
|
|
28
37
|
}));
|
|
38
|
+
await progress.report({
|
|
39
|
+
phase: contract.name,
|
|
40
|
+
completed: 1,
|
|
41
|
+
total: 1,
|
|
42
|
+
message: result.ok ? "completed" : "failed",
|
|
43
|
+
terminal: true,
|
|
44
|
+
});
|
|
29
45
|
if (result.ok) {
|
|
30
46
|
const evidence = createEvidence(result.value.subject ?? registration.activeTarget?.(), result.value.provider, {
|
|
31
47
|
operation: contract.name,
|
|
@@ -38,16 +54,18 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
|
|
|
38
54
|
const recorded = registration.recordEvidence?.(evidence);
|
|
39
55
|
if (recorded !== undefined && !recorded.ok)
|
|
40
56
|
return toCallToolResult(recorded, contract);
|
|
41
|
-
return toCallToolResult({ ok: true, value: evidence }, contract
|
|
57
|
+
return toCallToolResult({ ok: true, value: evidence }, contract, {
|
|
58
|
+
evidenceResourcesAvailable: recorded !== undefined,
|
|
59
|
+
});
|
|
42
60
|
}
|
|
43
61
|
if (result.error instanceof AnalysisCapabilityUnavailableError &&
|
|
44
62
|
approvedUnknownTracking(input) &&
|
|
45
63
|
registration.recordUnknown !== undefined) {
|
|
46
64
|
const unknown = registration.recordUnknown({
|
|
47
65
|
approved: true,
|
|
48
|
-
question:
|
|
66
|
+
question: "The requested analysis is unavailable for the current target.",
|
|
49
67
|
severity: "medium",
|
|
50
|
-
domain: "
|
|
68
|
+
domain: "analysis-capability",
|
|
51
69
|
supporting_evidence_ids: [],
|
|
52
70
|
contradicting_evidence_ids: [],
|
|
53
71
|
required_authority: "shipped-artifact",
|
|
@@ -56,7 +74,7 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
|
|
|
56
74
|
recommended_probes: [
|
|
57
75
|
{
|
|
58
76
|
operation: contract.name,
|
|
59
|
-
rationale: "
|
|
77
|
+
rationale: "Choose another analysis or target that can answer this question.",
|
|
60
78
|
},
|
|
61
79
|
],
|
|
62
80
|
relationships: [],
|
|
@@ -70,19 +88,12 @@ const registerOfficialTool = (server, analysis, contract, registration) => {
|
|
|
70
88
|
});
|
|
71
89
|
};
|
|
72
90
|
const projectOfficialArguments = (contract, input) => {
|
|
73
|
-
const parsed =
|
|
74
|
-
if (!parsed.success ||
|
|
75
|
-
typeof parsed.data !== "object" ||
|
|
76
|
-
parsed.data === null ||
|
|
77
|
-
Array.isArray(parsed.data)) {
|
|
78
|
-
// The SDK validates this with the same schema before invoking the callback.
|
|
79
|
-
throw new Error("Validated MCP tool input was not a JSON object");
|
|
80
|
-
}
|
|
91
|
+
const parsed = jsonObjectSchema.parse(contract.inputSchema.parse(input));
|
|
81
92
|
const projected = {};
|
|
82
93
|
for (const key of Object.keys(contract.inputSchema.shape)) {
|
|
83
94
|
if (key === "unknown_registry_approved")
|
|
84
95
|
continue;
|
|
85
|
-
projected[key] = parsed
|
|
96
|
+
projected[key] = jsonValueSchema.parse(parsed[key] ?? null);
|
|
86
97
|
}
|
|
87
98
|
return projected;
|
|
88
99
|
};
|
|
@@ -10,9 +10,10 @@ import { err } from "../domain/result.js";
|
|
|
10
10
|
import { PROCESS_PROVIDER } from "./sessionToolPolicies.js";
|
|
11
11
|
import { toCallToolResult } from "./toolResult.js";
|
|
12
12
|
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
|
|
13
|
+
import { runDerivedOperation } from "./runDerivedOperation.js";
|
|
13
14
|
/** Register deterministic process-capture comparison and contradiction tracking. */
|
|
14
15
|
export const registerProcessComparisonTool = (server, session, contract, now = Date.now) => {
|
|
15
|
-
server.registerTool(contract.name, toolRegistrationOptions(contract), (input) => {
|
|
16
|
+
server.registerTool(contract.name, toolRegistrationOptions(contract), async (input, context) => {
|
|
16
17
|
const parsed = z
|
|
17
18
|
.object({
|
|
18
19
|
left_evidence_id: z.string(),
|
|
@@ -28,12 +29,15 @@ export const registerProcessComparisonTool = (server, session, contract, now = D
|
|
|
28
29
|
return toCallToolResult(validated, contract);
|
|
29
30
|
let comparison;
|
|
30
31
|
try {
|
|
31
|
-
|
|
32
|
+
const computed = await runDerivedOperation(context, contract.name, () => compareProcessCaptures(parsed.left, parsed.right, {
|
|
32
33
|
...(parsed.max_capture_age_ms === undefined
|
|
33
34
|
? {}
|
|
34
35
|
: { maxCaptureAgeMs: parsed.max_capture_age_ms }),
|
|
35
36
|
now,
|
|
36
|
-
});
|
|
37
|
+
}));
|
|
38
|
+
if (!computed.ok)
|
|
39
|
+
return toCallToolResult(computed, contract);
|
|
40
|
+
comparison = computed.value;
|
|
37
41
|
}
|
|
38
42
|
catch (cause) {
|
|
39
43
|
return toCallToolResult(err(new EvidenceIntegrityError(cause instanceof Error
|
|
@@ -80,17 +84,11 @@ const validateCaptureSources = (session, parsed) => {
|
|
|
80
84
|
evidence.confidence !== "observed" ||
|
|
81
85
|
evidence.authority !== "controlled-replay" ||
|
|
82
86
|
evidenceCapture === undefined ||
|
|
83
|
-
|
|
87
|
+
canonicalize(evidenceCapture) !== canonicalize(capture))
|
|
84
88
|
return err(new EvidenceIntegrityError("Process comparison payload does not match its source Evidence"));
|
|
85
89
|
}
|
|
86
90
|
return { ok: true, value: null };
|
|
87
91
|
};
|
|
88
|
-
const canonicalJson = (value) => {
|
|
89
|
-
const encoded = canonicalize(value);
|
|
90
|
-
if (encoded === undefined)
|
|
91
|
-
throw new TypeError("Process capture could not be canonicalized");
|
|
92
|
-
return encoded;
|
|
93
|
-
};
|
|
94
92
|
const comparisonUnknownInput = (parsed, comparison) => {
|
|
95
93
|
if (parsed.unknown_registry_approved !== true ||
|
|
96
94
|
comparison.status === "unchanged")
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { completable, } from "@modelcontextprotocol/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { PROMPT_CONTRACTS, renderGuidedPrompt, } from "../contracts/promptContracts.js";
|
|
4
|
+
import { createPromptCompletionSource, } from "./promptCompletion.js";
|
|
5
|
+
const promptValuesSchema = z.record(z.string(), z.string());
|
|
6
|
+
/** Register every guided workflow and its session-scoped argument completers. */
|
|
7
|
+
export const registerGuidedPrompts = (server, analysis, session) => {
|
|
8
|
+
const completion = createPromptCompletionSource(analysis, session);
|
|
9
|
+
const prompts = new Map();
|
|
10
|
+
for (const contract of PROMPT_CONTRACTS)
|
|
11
|
+
prompts.set(contract.name, registerPrompt(server, contract, completion));
|
|
12
|
+
return {
|
|
13
|
+
update(contract) {
|
|
14
|
+
const prompt = prompts.get(contract.name);
|
|
15
|
+
if (prompt === undefined)
|
|
16
|
+
throw new RangeError(`Unknown guided prompt ${contract.name}`);
|
|
17
|
+
const argsSchema = promptArgumentsSchema(contract, completion);
|
|
18
|
+
prompt.update({
|
|
19
|
+
title: contract.title,
|
|
20
|
+
description: contract.description,
|
|
21
|
+
argsSchema,
|
|
22
|
+
callback: (arguments_) => promptResult(contract, arguments_),
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
const registerPrompt = (server, contract, completion) => {
|
|
28
|
+
const argsSchema = promptArgumentsSchema(contract, completion);
|
|
29
|
+
return server.registerPrompt(contract.name, {
|
|
30
|
+
title: contract.title,
|
|
31
|
+
description: contract.description,
|
|
32
|
+
argsSchema,
|
|
33
|
+
}, (arguments_) => promptResult(contract, arguments_));
|
|
34
|
+
};
|
|
35
|
+
const promptArgumentsSchema = (contract, completion) => {
|
|
36
|
+
const shape = {};
|
|
37
|
+
for (const [name, argument] of Object.entries(contract.arguments))
|
|
38
|
+
shape[name] = argumentSchema(argument, completion);
|
|
39
|
+
return z.object(shape);
|
|
40
|
+
};
|
|
41
|
+
const argumentSchema = (argument, completion) => {
|
|
42
|
+
const value = z
|
|
43
|
+
.string()
|
|
44
|
+
.trim()
|
|
45
|
+
.min(1)
|
|
46
|
+
.max(4_096)
|
|
47
|
+
.describe(argument.description);
|
|
48
|
+
const kind = argument.completion;
|
|
49
|
+
const completed = kind === undefined
|
|
50
|
+
? value
|
|
51
|
+
: completable(value, async (partial, context) => [
|
|
52
|
+
...(await completion.complete(kind, partial, context)),
|
|
53
|
+
]);
|
|
54
|
+
return argument.required ? completed : completed.optional();
|
|
55
|
+
};
|
|
56
|
+
const promptResult = (contract, arguments_) => ({
|
|
57
|
+
description: contract.description,
|
|
58
|
+
messages: [
|
|
59
|
+
{
|
|
60
|
+
role: "user",
|
|
61
|
+
content: {
|
|
62
|
+
type: "text",
|
|
63
|
+
text: renderGuidedPrompt(contract, promptValuesSchema.parse(arguments_)),
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { binarySessionInputSchema, SESSION_TOOL_CONTRACTS, } from "../contracts/toolContracts.js";
|
|
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";
|
|
6
|
+
/** Register the read-only provider and target status operation. */
|
|
7
|
+
export const registerSessionStatusTool = (server, session, contract, startedAt, availabilityPolicy) => {
|
|
8
|
+
server.registerTool(contract.name, {
|
|
9
|
+
description: contract.description,
|
|
10
|
+
inputSchema: contract.inputSchema,
|
|
11
|
+
outputSchema: contract.outputSchema,
|
|
12
|
+
annotations: contract.annotations,
|
|
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
|
+
});
|
|
52
|
+
};
|