rea-agents 2.5.0 → 3.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 +14 -4
- package/bridge/ghidra/ReaGhidraBridge.java +477 -4
- package/bridge/hopper_bridge.py +54 -4
- package/dist/application/AnalysisContextQueries.js +130 -0
- package/dist/application/AnalysisSnapshotCache.js +4 -2
- package/dist/application/ArtifactInventory/scanCanonical.js +1 -1
- package/dist/application/BinarySession.js +5 -4
- package/dist/application/BinarySessionComposition.js +21 -0
- package/dist/application/BinarySessionRecords.js +79 -9
- package/dist/application/CapabilityInventory.js +16 -2
- package/dist/application/ClientRegistrationStatus.js +4 -1
- package/dist/application/DirectAnalysis.js +6 -2
- package/dist/application/EnhancedTools.js +37 -7
- package/dist/application/EvidenceLedger.js +45 -16
- package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
- package/dist/application/LazyAnalysisProvider.js +77 -0
- package/dist/application/LoopbackReplay.js +1 -1
- package/dist/application/NativeApiInspection.js +80 -0
- package/dist/application/SessionProviderRouter.js +6 -6
- package/dist/application/SetupClientConfiguration.js +4 -2
- package/dist/application/runtime.js +26 -9
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +8 -2
- package/dist/catalogIdentity.js +3 -3
- package/dist/cli/artifactCommands.js +5 -3
- package/dist/cli/coreAnalysisCommands.js +41 -0
- package/dist/cliCommandNames.js +1 -0
- package/dist/contracts/artifactToolContracts.js +8 -3
- package/dist/contracts/enhancedInputs.js +10 -0
- package/dist/contracts/functionWorkflowToolContracts.js +30 -0
- package/dist/contracts/promptContracts.js +14 -15
- package/dist/contracts/toolContractExamples.js +3 -0
- package/dist/contracts/toolContracts.js +21 -4
- package/dist/contracts/toolEffects.js +4 -0
- package/dist/contracts/toolOutputSchemaGroups.js +50 -10
- package/dist/domain/analysisErrorPresentation.js +8 -2
- package/dist/domain/analysisErrorProjection.js +1 -0
- package/dist/domain/bytecodeProvider.js +179 -0
- package/dist/domain/conformancePackage.js +169 -0
- package/dist/domain/conformanceReplay.js +104 -0
- package/dist/domain/conformanceTrustGate.js +193 -0
- package/dist/domain/customProtocolCapture.js +137 -0
- package/dist/domain/eventProcessTree.js +214 -0
- package/dist/domain/evidenceBundle.js +47 -0
- package/dist/domain/hopperStartupFailure.js +1 -1
- package/dist/domain/hopperValues.js +4 -1
- package/dist/domain/inputIssueProjection.js +7 -1
- package/dist/domain/javascriptSemanticAnalysis.js +8 -12
- package/dist/domain/javascriptSourceParser.js +15 -0
- package/dist/domain/javascriptStaticAnalysis.js +8 -12
- package/dist/domain/mobileApplicationInvestigation.js +159 -0
- package/dist/domain/nativeApiBoundary.js +130 -0
- package/dist/domain/objcSwiftMetadata.js +150 -0
- package/dist/domain/packageAnalysis.js +123 -0
- package/dist/domain/peInspection.js +196 -0
- package/dist/domain/protocolCapture.js +243 -0
- package/dist/dotnet/ManagedStaticProvider.js +34 -5
- package/dist/generatedMcpToolCatalog.js +37 -0
- package/dist/generatedPackageMetadata.js +4 -4
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +27 -1
- package/dist/ghidra/GhidraProviderCapabilities.js +40 -38
- package/dist/hopper/HopperClient.js +26 -2
- package/dist/hopper/HopperProvider.js +42 -30
- package/dist/hopper/HopperRequestQueue.js +32 -4
- package/dist/hopper/HopperResponseStream.js +2 -2
- package/dist/hopper/protocol.js +63 -21
- package/dist/main/transport.js +2 -5
- package/dist/main.js +14 -12
- package/dist/mcpDoctor.js +2 -2
- package/dist/mcpStartupPolicy.js +14 -0
- package/dist/server/LazyToolCatalog.js +134 -0
- package/dist/server/createServer.js +64 -171
- package/dist/server/hydrateServerTools.js +191 -0
- package/dist/server/registerEnhancedTools.js +13 -3
- package/dist/server/registerEvidenceResources.js +50 -101
- package/dist/server/registerInvestigationTools.js +1 -2
- package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
- package/dist/server/registerReconstructionObligationLedgerResource.js +0 -7
- package/dist/server/registerReconstructionReadinessResource.js +0 -7
- package/dist/server/registerSessionRecordTools.js +82 -12
- package/dist/server/registerSessionStatusTool.js +1 -1
- package/dist/server/registerSessionTools.js +21 -1
- package/dist/server/toolRegistrationOptions.js +21 -6
- package/dist/server/toolResult.js +12 -0
- package/package.json +21 -10
- package/scripts/package-runner-bootstrap.mjs +72 -0
- package/scripts/rea.mjs +11 -1
- package/skills/reverse-engineer-anything/SKILL.md +10 -3
|
@@ -4,13 +4,12 @@ import canonicalize from "canonicalize";
|
|
|
4
4
|
import { registerJavaScriptApplicationGraphResource } from "./registerJavaScriptApplicationGraphResource.js";
|
|
5
5
|
import { registerReconstructionObligationLedgerResource } from "./registerReconstructionObligationLedgerResource.js";
|
|
6
6
|
import { registerReconstructionReadinessResource } from "./registerReconstructionReadinessResource.js";
|
|
7
|
-
const evidenceUri = (evidenceId) => `rea://evidence/${evidenceId}`;
|
|
8
7
|
/** Expose immutable session-owned Evidence v2 records as MCP resources. */
|
|
9
8
|
export const registerEvidenceResources = (server, session) => {
|
|
10
9
|
registerReconstructionCoverageResource(server, session);
|
|
11
10
|
registerInvestigationWorkspaceResource(server, session);
|
|
12
11
|
registerSessionEvidenceResource(server, session);
|
|
13
|
-
|
|
12
|
+
registerEvidenceBundleResource(server, session);
|
|
14
13
|
registerAnalysisSnapshotResource(server, session);
|
|
15
14
|
registerArtifactPageResource(server, session);
|
|
16
15
|
registerFunctionDossierResource(server, session);
|
|
@@ -19,20 +18,15 @@ export const registerEvidenceResources = (server, session) => {
|
|
|
19
18
|
registerReconstructionReadinessResource(server, session);
|
|
20
19
|
registerJavaScriptApplicationGraphResource(server, session);
|
|
21
20
|
registerResidualUnknownResource(server, session);
|
|
21
|
+
session.onAnalysisSnapshotChanged?.(() => {
|
|
22
|
+
void server.server
|
|
23
|
+
.sendResourceUpdated({ uri: "rea://snapshot/current" })
|
|
24
|
+
.catch(() => undefined);
|
|
25
|
+
});
|
|
22
26
|
};
|
|
23
27
|
const registerReconstructionCoverageResource = (server, session) => {
|
|
24
28
|
server.registerResource("reconstruction-coverage-revision", new ResourceTemplate("rea://reconstruction-coverage/{workspaceId}/revision/{revision}", {
|
|
25
|
-
list:
|
|
26
|
-
resources: session
|
|
27
|
-
.reconstructionCoverageWorkspaces()
|
|
28
|
-
.map((workspace) => ({
|
|
29
|
-
uri: coverageWorkspaceUri(workspace.workspace_id, workspace.revision),
|
|
30
|
-
name: `${workspace.workspace_id} revision ${String(workspace.revision)}`,
|
|
31
|
-
title: `${workspace.name} coverage revision ${String(workspace.revision)}`,
|
|
32
|
-
description: "Immutable evidence-backed reconstruction coverage workspace revision.",
|
|
33
|
-
mimeType: "application/json",
|
|
34
|
-
})),
|
|
35
|
-
}),
|
|
29
|
+
list: undefined,
|
|
36
30
|
}), {
|
|
37
31
|
title: "Reconstruction coverage revision",
|
|
38
32
|
description: "Canonical CAS-linked surfaces, owners, claims, verifiers, evidence, and completion boundaries.",
|
|
@@ -50,27 +44,7 @@ const registerReconstructionCoverageResource = (server, session) => {
|
|
|
50
44
|
};
|
|
51
45
|
const registerInvestigationWorkspaceResource = (server, session) => {
|
|
52
46
|
server.registerResource("investigation-workspace-revision", new ResourceTemplate("rea://workspace/{workspaceId}/revision/{revision}", {
|
|
53
|
-
list:
|
|
54
|
-
resources: session.investigationWorkspaces().map((workspace) => ({
|
|
55
|
-
uri: workspaceUri(workspace.workspace_id, workspace.revision),
|
|
56
|
-
name: `${workspace.workspace_id} revision ${String(workspace.revision)}`,
|
|
57
|
-
title: `${workspace.name} revision ${String(workspace.revision)}`,
|
|
58
|
-
description: "Immutable CAS-linked investigation workspace revision.",
|
|
59
|
-
mimeType: "application/json",
|
|
60
|
-
})),
|
|
61
|
-
}),
|
|
62
|
-
complete: {
|
|
63
|
-
workspaceId: (prefix) => [
|
|
64
|
-
...new Set(session
|
|
65
|
-
.investigationWorkspaces()
|
|
66
|
-
.map(({ workspace_id }) => workspace_id)),
|
|
67
|
-
].filter((workspaceId) => workspaceId.startsWith(prefix)),
|
|
68
|
-
revision: (prefix, context) => session
|
|
69
|
-
.investigationWorkspaces()
|
|
70
|
-
.filter(({ workspace_id }) => context?.arguments?.workspaceId === workspace_id)
|
|
71
|
-
.map(({ revision }) => String(revision))
|
|
72
|
-
.filter((revision) => revision.startsWith(prefix)),
|
|
73
|
-
},
|
|
47
|
+
list: undefined,
|
|
74
48
|
}), {
|
|
75
49
|
title: "Investigation workspace revision",
|
|
76
50
|
description: "Immutable session-retained workspace with CAS linkage.",
|
|
@@ -88,21 +62,7 @@ const registerInvestigationWorkspaceResource = (server, session) => {
|
|
|
88
62
|
};
|
|
89
63
|
const registerSessionEvidenceResource = (server, session) => {
|
|
90
64
|
server.registerResource("session-evidence", new ResourceTemplate("rea://evidence/{evidenceId}", {
|
|
91
|
-
list:
|
|
92
|
-
resources: session.exportEvidenceBundle().records.map((evidence) => ({
|
|
93
|
-
uri: evidenceUri(evidence.evidence_id),
|
|
94
|
-
name: evidence.evidence_id,
|
|
95
|
-
title: `${evidence.operation} evidence`,
|
|
96
|
-
description: `Session-owned Evidence v2 record for ${evidence.operation}.`,
|
|
97
|
-
mimeType: "application/json",
|
|
98
|
-
})),
|
|
99
|
-
}),
|
|
100
|
-
complete: {
|
|
101
|
-
evidenceId: (prefix) => session
|
|
102
|
-
.exportEvidenceBundle()
|
|
103
|
-
.records.map(({ evidence_id }) => evidence_id)
|
|
104
|
-
.filter((evidenceId) => evidenceId.startsWith(prefix)),
|
|
105
|
-
},
|
|
65
|
+
list: undefined,
|
|
106
66
|
}), {
|
|
107
67
|
title: "Session evidence",
|
|
108
68
|
description: "Immutable Evidence v2 records owned by this REA session.",
|
|
@@ -125,42 +85,52 @@ const registerSessionEvidenceResource = (server, session) => {
|
|
|
125
85
|
};
|
|
126
86
|
});
|
|
127
87
|
};
|
|
128
|
-
const
|
|
129
|
-
server.registerResource("
|
|
130
|
-
|
|
131
|
-
|
|
88
|
+
const registerEvidenceBundleResource = (server, session) => {
|
|
89
|
+
server.registerResource("evidence-bundle", new ResourceTemplate("rea://evidence-bundle/{bundleDigest}", {
|
|
90
|
+
list: undefined,
|
|
91
|
+
}), {
|
|
92
|
+
title: "Evidence bundle",
|
|
93
|
+
description: "Immutable session-retained canonical Evidence v2 bundle.",
|
|
132
94
|
mimeType: "application/json",
|
|
133
|
-
}, (uri
|
|
95
|
+
}, (uri, variables) => {
|
|
96
|
+
const digest = stringVariable(variables.bundleDigest, uri.href);
|
|
97
|
+
if (!/^[a-f0-9]{64}$/u.test(digest))
|
|
98
|
+
throw new ResourceNotFoundError(uri.href);
|
|
99
|
+
const text = session.retainedEvidenceBundle(digest);
|
|
100
|
+
if (text === undefined)
|
|
101
|
+
throw new ResourceNotFoundError(uri.href);
|
|
102
|
+
return {
|
|
103
|
+
contents: [
|
|
104
|
+
{
|
|
105
|
+
uri: uri.href,
|
|
106
|
+
mimeType: "application/json",
|
|
107
|
+
text,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
});
|
|
134
112
|
};
|
|
135
113
|
const registerAnalysisSnapshotResource = (server, session) => {
|
|
136
|
-
server.registerResource("analysis-snapshot",
|
|
137
|
-
list: () => {
|
|
138
|
-
const snapshot = session.exportAnalysisSnapshot();
|
|
139
|
-
if (!snapshot.ok)
|
|
140
|
-
return { resources: [] };
|
|
141
|
-
const digest = contentDigest(snapshot.value);
|
|
142
|
-
return {
|
|
143
|
-
resources: [
|
|
144
|
-
{
|
|
145
|
-
uri: `rea://snapshot/${digest}`,
|
|
146
|
-
name: digest,
|
|
147
|
-
title: "Current analysis snapshot",
|
|
148
|
-
description: "Immutable projection of the current target cache.",
|
|
149
|
-
mimeType: "application/json",
|
|
150
|
-
},
|
|
151
|
-
],
|
|
152
|
-
};
|
|
153
|
-
},
|
|
154
|
-
}), {
|
|
114
|
+
server.registerResource("analysis-snapshot", "rea://snapshot/current", {
|
|
155
115
|
title: "Analysis snapshot",
|
|
156
|
-
description: "
|
|
116
|
+
description: "Mutable provider-neutral analysis snapshot for the current native target.",
|
|
157
117
|
mimeType: "application/json",
|
|
158
|
-
}, (uri
|
|
159
|
-
const requested = stringVariable(variables.snapshotDigest, uri.href);
|
|
118
|
+
}, (uri) => {
|
|
160
119
|
const snapshot = session.exportAnalysisSnapshot();
|
|
161
|
-
if (!snapshot.ok
|
|
162
|
-
|
|
163
|
-
|
|
120
|
+
if (!snapshot.ok)
|
|
121
|
+
return jsonResource(uri.href, {
|
|
122
|
+
state: "unavailable",
|
|
123
|
+
reason: snapshot.error.message,
|
|
124
|
+
remediation: "Open a native target with a concrete analysis profile. After metadata mutation, call open_binary again for the current target to begin a fresh cache.",
|
|
125
|
+
});
|
|
126
|
+
return jsonResource(uri.href, {
|
|
127
|
+
state: "available",
|
|
128
|
+
target: snapshot.value.target,
|
|
129
|
+
profile: snapshot.value.binding,
|
|
130
|
+
content_digest: contentDigest(snapshot.value),
|
|
131
|
+
entry_count: snapshot.value.entries.length,
|
|
132
|
+
snapshot: snapshot.value,
|
|
133
|
+
});
|
|
164
134
|
});
|
|
165
135
|
};
|
|
166
136
|
const registerArtifactPageResource = (server, session) => {
|
|
@@ -221,7 +191,6 @@ const registerEvidenceSectionResource = (server, session) => {
|
|
|
221
191
|
server.registerResource("evidence-section", new ResourceTemplate("rea://evidence/{evidenceId}/section/{section}", {
|
|
222
192
|
list: undefined,
|
|
223
193
|
complete: {
|
|
224
|
-
evidenceId: (prefix) => evidenceIds(session, prefix),
|
|
225
194
|
section: (prefix) => EVIDENCE_SECTIONS.filter((section) => section.startsWith(prefix)),
|
|
226
195
|
},
|
|
227
196
|
}), {
|
|
@@ -251,21 +220,7 @@ const registerEvidenceSectionResource = (server, session) => {
|
|
|
251
220
|
};
|
|
252
221
|
const registerResidualUnknownResource = (server, session) => {
|
|
253
222
|
server.registerResource("residual-unknown", new ResourceTemplate("rea://unknown/{unknownId}", {
|
|
254
|
-
list:
|
|
255
|
-
resources: session.listUnknowns().map((unknown) => ({
|
|
256
|
-
uri: `rea://unknown/${unknown.unknown_id}`,
|
|
257
|
-
name: unknown.unknown_id,
|
|
258
|
-
title: unknown.question,
|
|
259
|
-
description: `Current ${unknown.status} residual unknown revision.`,
|
|
260
|
-
mimeType: "application/json",
|
|
261
|
-
})),
|
|
262
|
-
}),
|
|
263
|
-
complete: {
|
|
264
|
-
unknownId: (prefix) => session
|
|
265
|
-
.listUnknowns()
|
|
266
|
-
.map(({ unknown_id }) => unknown_id)
|
|
267
|
-
.filter((unknownId) => unknownId.startsWith(prefix)),
|
|
268
|
-
},
|
|
223
|
+
list: undefined,
|
|
269
224
|
}), {
|
|
270
225
|
title: "Residual unknown",
|
|
271
226
|
description: "Current session-owned residual unknown head.",
|
|
@@ -283,8 +238,6 @@ const registerResidualUnknownResource = (server, session) => {
|
|
|
283
238
|
return jsonResource(uri.href, { current: unknown, history });
|
|
284
239
|
});
|
|
285
240
|
};
|
|
286
|
-
const coverageWorkspaceUri = (workspaceId, revision) => `rea://reconstruction-coverage/${workspaceId}/revision/${String(revision)}`;
|
|
287
|
-
const workspaceUri = (workspaceId, revision) => `rea://workspace/${workspaceId}/revision/${String(revision)}`;
|
|
288
241
|
const EVIDENCE_SECTIONS = [
|
|
289
242
|
"result",
|
|
290
243
|
"terminal",
|
|
@@ -295,10 +248,6 @@ const EVIDENCE_SECTIONS = [
|
|
|
295
248
|
"occurrences",
|
|
296
249
|
"edges",
|
|
297
250
|
];
|
|
298
|
-
const evidenceIds = (session, prefix) => session
|
|
299
|
-
.exportEvidenceBundle()
|
|
300
|
-
.records.map(({ evidence_id }) => evidence_id)
|
|
301
|
-
.filter((evidenceId) => evidenceId.startsWith(prefix));
|
|
302
251
|
const stringVariable = (value, uri) => {
|
|
303
252
|
if (typeof value !== "string")
|
|
304
253
|
throw new ResourceNotFoundError(uri);
|
|
@@ -114,8 +114,7 @@ const runAutomaticInvestigation = async (input) => {
|
|
|
114
114
|
if (!investigated.ok)
|
|
115
115
|
return toCallToolResult(investigated, contract);
|
|
116
116
|
const workspace = investigated.value.workspace;
|
|
117
|
-
|
|
118
|
-
input.server.sendResourceListChanged();
|
|
117
|
+
session.retainInvestigationWorkspace(workspace);
|
|
119
118
|
const result = changedBehaviorResultSchema.parse(investigated.value.evidence.normalized_result);
|
|
120
119
|
const toolResult = toCallToolResult(recordWorkflowEvidence(session, investigated.value.evidence, input.unknownRegistryApproved, isIncomplete(result.behavior_status), {
|
|
121
120
|
question: `Automatic changed behavior run ${result.investigation_run?.run_id ?? "unknown"} remains ${result.behavior_status}`,
|
|
@@ -5,10 +5,6 @@ export const registerJavaScriptApplicationGraphResource = (server, session) => {
|
|
|
5
5
|
server.registerResource("javascript-application-graph-page", new ResourceTemplate("rea://evidence/{evidenceId}/application-graph/{collection}/offset/{offset}/limit/{limit}", {
|
|
6
6
|
list: undefined,
|
|
7
7
|
complete: {
|
|
8
|
-
evidenceId: (prefix) => session
|
|
9
|
-
.exportEvidenceBundle()
|
|
10
|
-
.records.map(({ evidence_id }) => evidence_id)
|
|
11
|
-
.filter((evidenceId) => evidenceId.startsWith(prefix)),
|
|
12
8
|
collection: (prefix) => ["nodes", "edges"].filter((name) => name.startsWith(prefix)),
|
|
13
9
|
},
|
|
14
10
|
}), {
|
|
@@ -4,13 +4,6 @@ import { reconstructionObligationLedgerPageSchema } from "../domain/reconstructi
|
|
|
4
4
|
export const registerReconstructionObligationLedgerResource = (server, session) => {
|
|
5
5
|
server.registerResource("reconstruction-obligation-ledger-page", new ResourceTemplate("rea://evidence/{evidenceId}/reconstruction-obligation-ledger", {
|
|
6
6
|
list: undefined,
|
|
7
|
-
complete: {
|
|
8
|
-
evidenceId: (prefix) => session
|
|
9
|
-
.exportEvidenceBundle()
|
|
10
|
-
.records.filter(({ predicate_type: predicateType }) => predicateType === "rea.reconstruction-obligation-ledger/v1")
|
|
11
|
-
.map(({ evidence_id: evidenceId }) => evidenceId)
|
|
12
|
-
.filter((evidenceId) => evidenceId.startsWith(prefix)),
|
|
13
|
-
},
|
|
14
7
|
}), {
|
|
15
8
|
title: "Reconstruction obligation ledger page",
|
|
16
9
|
description: "One deterministic Evidence v2-backed obligation page with full-ledger closure digest and summaries.",
|
|
@@ -4,13 +4,6 @@ import { reconstructionReadinessReportSchema } from "../domain/reconstructionRea
|
|
|
4
4
|
export const registerReconstructionReadinessResource = (server, session) => {
|
|
5
5
|
server.registerResource("reconstruction-readiness-report", new ResourceTemplate("rea://evidence/{evidenceId}/reconstruction-readiness-report", {
|
|
6
6
|
list: undefined,
|
|
7
|
-
complete: {
|
|
8
|
-
evidenceId: (prefix) => session
|
|
9
|
-
.exportEvidenceBundle()
|
|
10
|
-
.records.filter(({ predicate_type: predicateType }) => predicateType === "rea.reconstruction-readiness-report/v1")
|
|
11
|
-
.map(({ evidence_id: evidenceId }) => evidenceId)
|
|
12
|
-
.filter((evidenceId) => evidenceId.startsWith(prefix)),
|
|
13
|
-
},
|
|
14
7
|
}), {
|
|
15
8
|
title: "Reconstruction readiness report",
|
|
16
9
|
description: "Full deterministic nine-stage conformance report and replay identity.",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readEvidenceBundle, writeEvidenceBundle, } from "../application/EvidenceBundleFiles.js";
|
|
2
|
-
import { exportEvidenceBundleInputSchema, importEvidenceBundleInputSchema, listUnknownsInputSchema, SESSION_TOOL_CONTRACTS, verifyUnknownResolutionInputSchema, } from "../contracts/toolContracts.js";
|
|
2
|
+
import { exportEvidenceBundleInputSchema, importEvidenceBundleInputSchema, listUnknownsInputSchema, snapshotEvidenceBundleInputSchema, SESSION_TOOL_CONTRACTS, verifyUnknownResolutionInputSchema, } from "../contracts/toolContracts.js";
|
|
3
3
|
import { AnalysisProtocolError, PermissionRequiredError, } from "../domain/errors.js";
|
|
4
4
|
import { recordUnknownInputSchema, updateUnknownInputSchema, } from "../domain/residualUnknown.js";
|
|
5
5
|
import { err, ok } from "../domain/result.js";
|
|
@@ -10,6 +10,7 @@ import { toCallToolResult } from "./toolResult.js";
|
|
|
10
10
|
export const registerEvidenceTools = (registration) => {
|
|
11
11
|
registerExportEvidenceTool(registration);
|
|
12
12
|
registerImportEvidenceTool(registration);
|
|
13
|
+
registerSnapshotEvidenceTool(registration);
|
|
13
14
|
};
|
|
14
15
|
const registerExportEvidenceTool = ({ server, session, exportContract, filePolicy, permissionAuthority, }) => {
|
|
15
16
|
server.registerTool(exportContract.name, toolRegistrationOptions(exportContract), async (input) => {
|
|
@@ -18,8 +19,6 @@ const registerExportEvidenceTool = ({ server, session, exportContract, filePolic
|
|
|
18
19
|
return toCallToolResult(parsedInput, exportContract);
|
|
19
20
|
const parsed = parsedInput.value;
|
|
20
21
|
const bundle = session.exportEvidenceBundle();
|
|
21
|
-
if (parsed.path === undefined)
|
|
22
|
-
return toCallToolResult(ok(bundle), exportContract);
|
|
23
22
|
const denied = await authorizeEvidencePath({
|
|
24
23
|
authority: permissionAuthority,
|
|
25
24
|
capability: "evidence_write",
|
|
@@ -35,10 +34,38 @@ const registerExportEvidenceTool = ({ server, session, exportContract, filePolic
|
|
|
35
34
|
path: written.value.path,
|
|
36
35
|
bytes: written.value.bytes,
|
|
37
36
|
records: bundle.records.length,
|
|
37
|
+
unknowns: bundle.unknowns.length,
|
|
38
38
|
}), exportContract)
|
|
39
39
|
: toCallToolResult(written, exportContract);
|
|
40
40
|
});
|
|
41
41
|
};
|
|
42
|
+
const registerSnapshotEvidenceTool = ({ server, session, snapshotContract, }) => {
|
|
43
|
+
server.registerTool(snapshotContract.name, toolRegistrationOptions(snapshotContract), (input) => {
|
|
44
|
+
const parsed = safeParseToolInput(snapshotEvidenceBundleInputSchema, input, snapshotContract.name);
|
|
45
|
+
if (!parsed.ok)
|
|
46
|
+
return toCallToolResult(parsed, snapshotContract);
|
|
47
|
+
const snapshot = session.snapshotEvidenceBundle();
|
|
48
|
+
if (!snapshot.ok)
|
|
49
|
+
return toCallToolResult(snapshot, snapshotContract);
|
|
50
|
+
const value = {
|
|
51
|
+
bundle_digest: snapshot.value.bundleDigest,
|
|
52
|
+
bundle_version: snapshot.value.bundleVersion,
|
|
53
|
+
bytes: snapshot.value.bytes,
|
|
54
|
+
records: snapshot.value.records,
|
|
55
|
+
unknowns: snapshot.value.unknowns,
|
|
56
|
+
bundle_uri: snapshot.value.uri,
|
|
57
|
+
};
|
|
58
|
+
return toCallToolResult(ok(value), snapshotContract, {
|
|
59
|
+
resourceLinks: [
|
|
60
|
+
{
|
|
61
|
+
uri: snapshot.value.uri,
|
|
62
|
+
name: snapshot.value.bundleDigest,
|
|
63
|
+
description: "Immutable session-retained Evidence v2 bundle",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
};
|
|
42
69
|
const registerImportEvidenceTool = ({ server, session, importContract, filePolicy, permissionAuthority, }) => {
|
|
43
70
|
server.registerTool(importContract.name, toolRegistrationOptions(importContract), async (input) => {
|
|
44
71
|
const parsedInput = safeParseToolInput(importEvidenceBundleInputSchema, input, importContract.name);
|
|
@@ -58,8 +85,6 @@ const registerImportEvidenceTool = ({ server, session, importContract, filePolic
|
|
|
58
85
|
if (!loaded.ok)
|
|
59
86
|
return toCallToolResult(loaded, importContract);
|
|
60
87
|
const imported = session.importEvidenceBundle(loaded.value);
|
|
61
|
-
if (imported.ok && imported.value > 0)
|
|
62
|
-
server.sendResourceListChanged();
|
|
63
88
|
return imported.ok
|
|
64
89
|
? toCallToolResult(ok({
|
|
65
90
|
imported: imported.value,
|
|
@@ -93,22 +118,53 @@ export const registerUnknownTools = ({ server, session, contracts, }) => {
|
|
|
93
118
|
if (!parsed.ok)
|
|
94
119
|
return toCallToolResult(parsed, listContract);
|
|
95
120
|
const filters = parsed.value;
|
|
96
|
-
|
|
121
|
+
const all = session.listUnknowns({
|
|
97
122
|
...(filters.status === undefined ? {} : { status: filters.status }),
|
|
98
123
|
...(filters.severity === undefined
|
|
99
124
|
? {}
|
|
100
125
|
: { severity: filters.severity }),
|
|
101
126
|
...(filters.domain === undefined ? {} : { domain: filters.domain }),
|
|
102
|
-
})
|
|
127
|
+
});
|
|
128
|
+
const items = all
|
|
129
|
+
.slice(filters.offset, filters.offset + filters.limit)
|
|
130
|
+
.map((unknown) => ({
|
|
131
|
+
unknown,
|
|
132
|
+
uri: `rea://unknown/${unknown.unknown_id}`,
|
|
133
|
+
}));
|
|
134
|
+
const nextOffset = filters.offset + items.length < all.length
|
|
135
|
+
? filters.offset + items.length
|
|
136
|
+
: null;
|
|
137
|
+
return toCallToolResult(ok({
|
|
138
|
+
items,
|
|
139
|
+
offset: filters.offset,
|
|
140
|
+
limit: filters.limit,
|
|
141
|
+
total: all.length,
|
|
142
|
+
next_offset: nextOffset,
|
|
143
|
+
has_more: nextOffset !== null,
|
|
144
|
+
}), listContract, {
|
|
145
|
+
resourceLinks: items.map((unknown) => ({
|
|
146
|
+
uri: unknown.uri,
|
|
147
|
+
name: unknown.unknown.unknown_id,
|
|
148
|
+
description: `Current ${unknown.unknown.status} residual unknown revision`,
|
|
149
|
+
})),
|
|
150
|
+
});
|
|
103
151
|
});
|
|
104
152
|
server.registerTool(recordContract.name, toolRegistrationOptions(recordContract), (input) => {
|
|
105
153
|
const parsed = safeParseToolInput(recordUnknownInputSchema, input, recordContract.name);
|
|
106
154
|
if (!parsed.ok)
|
|
107
155
|
return toCallToolResult(parsed, recordContract);
|
|
108
156
|
const result = session.recordUnknown(parsed.value);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
157
|
+
return result.ok
|
|
158
|
+
? toCallToolResult(result, recordContract, {
|
|
159
|
+
resourceLinks: [
|
|
160
|
+
{
|
|
161
|
+
uri: `rea://unknown/${result.value.unknown_id}`,
|
|
162
|
+
name: result.value.unknown_id,
|
|
163
|
+
description: "Current residual unknown head",
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
})
|
|
167
|
+
: toCallToolResult(result, recordContract);
|
|
112
168
|
});
|
|
113
169
|
server.registerTool(updateContract.name, toolRegistrationOptions(updateContract), (input) => {
|
|
114
170
|
const parsed = safeParseToolInput(updateUnknownInputSchema, input, updateContract.name);
|
|
@@ -116,8 +172,22 @@ export const registerUnknownTools = ({ server, session, contracts, }) => {
|
|
|
116
172
|
return toCallToolResult(parsed, updateContract);
|
|
117
173
|
const result = session.updateUnknown(parsed.value);
|
|
118
174
|
if (result.ok)
|
|
119
|
-
server.
|
|
120
|
-
|
|
175
|
+
void server.server
|
|
176
|
+
.sendResourceUpdated({
|
|
177
|
+
uri: `rea://unknown/${result.value.unknown_id}`,
|
|
178
|
+
})
|
|
179
|
+
.catch(() => undefined);
|
|
180
|
+
return result.ok
|
|
181
|
+
? toCallToolResult(result, updateContract, {
|
|
182
|
+
resourceLinks: [
|
|
183
|
+
{
|
|
184
|
+
uri: `rea://unknown/${result.value.unknown_id}`,
|
|
185
|
+
name: result.value.unknown_id,
|
|
186
|
+
description: "Updated residual unknown head",
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
})
|
|
190
|
+
: toCallToolResult(result, updateContract);
|
|
121
191
|
});
|
|
122
192
|
server.registerTool(verifyContract.name, toolRegistrationOptions(verifyContract), (input) => {
|
|
123
193
|
const parsed = safeParseToolInput(verifyUnknownResolutionInputSchema, input, verifyContract.name);
|
|
@@ -118,7 +118,7 @@ const recommendedActions = (open, alignment) => [
|
|
|
118
118
|
: []),
|
|
119
119
|
...(!open
|
|
120
120
|
? [
|
|
121
|
-
"
|
|
121
|
+
"For a supplied target, route by format: ASAR/JavaScript to analyze_javascript_application; archive/package to open_binary(path), then inspect_artifact or inventory_artifact on the active target; managed PE/CLI to inspect_managed_artifact; browser/Electron runtimes to their list-target tools; native binaries to open_binary.",
|
|
122
122
|
]
|
|
123
123
|
: [
|
|
124
124
|
"Continue from the active target with binary_overview or a bounded target-specific query.",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { SESSION_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
|
|
2
|
+
import { addressContextInputSchema, navigationContextInputSchema, SESSION_TOOL_CONTRACTS, } from "../contracts/toolContracts.js";
|
|
3
3
|
import { toCallToolResult } from "./toolResult.js";
|
|
4
4
|
import { logToolExecution } from "./toolLogging.js";
|
|
5
5
|
import { ok } from "../domain/result.js";
|
|
@@ -27,6 +27,7 @@ import { registerReplayMachineTool } from "./registerReplayMachineTool.js";
|
|
|
27
27
|
import { sessionAvailabilityPolicy, } from "./sessionAvailabilityPolicy.js";
|
|
28
28
|
import { permissionFailure } from "./permissionFailure.js";
|
|
29
29
|
import { registerCloseLifecycleTool } from "./registerCloseLifecycleTool.js";
|
|
30
|
+
import { getNavigationContext, inspectAddressContext, } from "../application/AnalysisContextQueries.js";
|
|
30
31
|
const recordProcessResidualUnknowns = (session, scenario, evidence, residuals) => {
|
|
31
32
|
if (scenario.unknown_registry_approved !== true)
|
|
32
33
|
return ok(null);
|
|
@@ -171,11 +172,28 @@ const registerRecordAndReplayTools = (server, session, logger) => {
|
|
|
171
172
|
});
|
|
172
173
|
registerReplayMachineTool(server, logger);
|
|
173
174
|
};
|
|
175
|
+
const registerContextTools = (server, session) => {
|
|
176
|
+
const navigationContract = SESSION_TOOL_CONTRACTS[20];
|
|
177
|
+
const addressContract = SESSION_TOOL_CONTRACTS[21];
|
|
178
|
+
server.registerTool(navigationContract.name, toolRegistrationOptions(navigationContract), async (input, context) => {
|
|
179
|
+
const parsed = safeParseToolInput(navigationContextInputSchema, input, navigationContract.name);
|
|
180
|
+
if (!parsed.ok)
|
|
181
|
+
return toCallToolResult(parsed, navigationContract);
|
|
182
|
+
return toCallToolResult(await getNavigationContext(session, parsed.value, context.mcpReq.signal), navigationContract);
|
|
183
|
+
});
|
|
184
|
+
server.registerTool(addressContract.name, toolRegistrationOptions(addressContract), async (input, context) => {
|
|
185
|
+
const parsed = safeParseToolInput(addressContextInputSchema, input, addressContract.name);
|
|
186
|
+
if (!parsed.ok)
|
|
187
|
+
return toCallToolResult(parsed, addressContract);
|
|
188
|
+
return toCallToolResult(await inspectAddressContext(session, parsed.value, context.mcpReq.signal), addressContract);
|
|
189
|
+
});
|
|
190
|
+
};
|
|
174
191
|
export const registerSessionTools = (server, session, logger, options = {}) => {
|
|
175
192
|
const processPolicy = options.processPolicy ?? DENY_PROCESS_POLICY;
|
|
176
193
|
const evidenceFilePolicy = options.evidenceFilePolicy ?? DENY_EVIDENCE_FILE_POLICY;
|
|
177
194
|
const analysisSnapshotFilePolicy = options.analysisSnapshotFilePolicy ?? DENY_EVIDENCE_FILE_POLICY;
|
|
178
195
|
const [openContract, closeContract, statusContract, exportContract, importContract, captureContract, compareContract, compareArtifactsContract, compareFunctionsContract, compareBundlesContract, changedBehaviorContract, callPathContract, staticRuntimeContract, reconstructionContract,] = SESSION_TOOL_CONTRACTS;
|
|
196
|
+
const snapshotContract = SESSION_TOOL_CONTRACTS[19];
|
|
179
197
|
registerLifecycleTools({
|
|
180
198
|
server,
|
|
181
199
|
session,
|
|
@@ -197,6 +215,7 @@ export const registerSessionTools = (server, session, logger, options = {}) => {
|
|
|
197
215
|
session,
|
|
198
216
|
exportContract,
|
|
199
217
|
importContract,
|
|
218
|
+
snapshotContract,
|
|
200
219
|
filePolicy: evidenceFilePolicy,
|
|
201
220
|
...(options.permissionAuthority === undefined
|
|
202
221
|
? {}
|
|
@@ -238,4 +257,5 @@ export const registerSessionTools = (server, session, logger, options = {}) => {
|
|
|
238
257
|
: { permissionAuthority: options.permissionAuthority }),
|
|
239
258
|
});
|
|
240
259
|
registerRecordAndReplayTools(server, session, logger);
|
|
260
|
+
registerContextTools(server, session);
|
|
241
261
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
const ADVERTISED_INPUT_SCHEMAS = new WeakMap();
|
|
3
|
+
const ADVERTISED_OUTPUT_SCHEMAS = new WeakMap();
|
|
2
4
|
/** Project the public contract fields accepted by MCP tool registration. */
|
|
3
5
|
export const toolRegistrationOptions = (contract) => ({
|
|
4
6
|
title: contract.title,
|
|
5
7
|
description: contract.description,
|
|
6
|
-
inputSchema: advertisedInputSchema(contract
|
|
8
|
+
inputSchema: advertisedInputSchema(contract),
|
|
7
9
|
outputSchema: advertisedOutputSchema(contract.outputSchema),
|
|
8
10
|
annotations: contract.annotations,
|
|
9
11
|
});
|
|
@@ -12,14 +14,14 @@ export const toolRegistrationOptions = (contract) => ({
|
|
|
12
14
|
* The SDK otherwise converts validation failures to text-only JSON-RPC errors
|
|
13
15
|
* before REA can return its stable structured error envelope.
|
|
14
16
|
*/
|
|
15
|
-
const advertisedInputSchema = (
|
|
17
|
+
const advertisedInputSchema = (contract) => ({
|
|
16
18
|
"~standard": {
|
|
17
19
|
version: 1,
|
|
18
20
|
vendor: "rea",
|
|
19
21
|
validate: (input) => ({ value: input }),
|
|
20
22
|
jsonSchema: {
|
|
21
|
-
input: () =>
|
|
22
|
-
output: () =>
|
|
23
|
+
input: () => advertisedInputJsonSchema(contract),
|
|
24
|
+
output: () => advertisedInputJsonSchema(contract),
|
|
23
25
|
},
|
|
24
26
|
},
|
|
25
27
|
});
|
|
@@ -32,12 +34,25 @@ const advertisedOutputSchema = (schema) => {
|
|
|
32
34
|
"~standard": {
|
|
33
35
|
...standard,
|
|
34
36
|
jsonSchema: {
|
|
35
|
-
input: () =>
|
|
36
|
-
output: () =>
|
|
37
|
+
input: () => advertisedOutputJsonSchema(schema, converter),
|
|
38
|
+
output: () => advertisedOutputJsonSchema(schema, converter),
|
|
37
39
|
},
|
|
38
40
|
},
|
|
39
41
|
};
|
|
40
42
|
};
|
|
43
|
+
const advertisedInputJsonSchema = (contract) => cachedJsonSchema(ADVERTISED_INPUT_SCHEMAS, contract.inputSchema, () => ({
|
|
44
|
+
...describeProperties(closeObjectSchemas(inputJsonSchema(contract.inputSchema))),
|
|
45
|
+
examples: contract.examples.map(({ input }) => input),
|
|
46
|
+
}));
|
|
47
|
+
const advertisedOutputJsonSchema = (schema, converter) => cachedJsonSchema(ADVERTISED_OUTPUT_SCHEMAS, schema, () => describeRootProperties(closeObjectSchemas(converter({ target: "draft-2020-12" }))));
|
|
48
|
+
const cachedJsonSchema = (cache, schema, create) => {
|
|
49
|
+
const cached = cache.get(schema);
|
|
50
|
+
if (cached !== undefined)
|
|
51
|
+
return cached;
|
|
52
|
+
const created = create();
|
|
53
|
+
cache.set(schema, created);
|
|
54
|
+
return created;
|
|
55
|
+
};
|
|
41
56
|
const inputJsonSchema = (schema) => {
|
|
42
57
|
const converter = schema["~standard"].jsonSchema?.input;
|
|
43
58
|
if (converter === undefined)
|
|
@@ -49,6 +49,14 @@ const successResult = (value, contract, options) => {
|
|
|
49
49
|
: (compactEvidence(value, options.evidenceTextProjection) ??
|
|
50
50
|
options.evidenceTextProjection)),
|
|
51
51
|
},
|
|
52
|
+
...(hasResourceLinks(value, contract, options)
|
|
53
|
+
? [
|
|
54
|
+
{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: "Full detail is available through MCP resources/read. Copy the opaque URI exactly. In Codex, call read_mcp_resource.",
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
: []),
|
|
52
60
|
...evidenceResourceLinks(value, contract.kind === "session" ||
|
|
53
61
|
options.evidenceResourcesAvailable === true),
|
|
54
62
|
...(options.resourceLinks ?? []).map((resource) => ({
|
|
@@ -60,6 +68,10 @@ const successResult = (value, contract, options) => {
|
|
|
60
68
|
structuredContent: candidate,
|
|
61
69
|
};
|
|
62
70
|
};
|
|
71
|
+
const hasResourceLinks = (value, contract, options) => options.resourceLinks !== undefined && options.resourceLinks.length > 0
|
|
72
|
+
? true
|
|
73
|
+
: evidenceResourceLinks(value, contract.kind === "session" ||
|
|
74
|
+
options.evidenceResourcesAvailable === true).length > 0;
|
|
63
75
|
const compactEvidence = (value, resultProjection) => {
|
|
64
76
|
if (typeof value !== "object" ||
|
|
65
77
|
value === null ||
|