rea-agents 0.2.1 → 0.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 +124 -20
- package/bridge/hopper_bridge.py +141 -13
- package/dist/application/AnalysisProvider.js +12 -1
- package/dist/application/ArtifactExtraction.js +166 -0
- package/dist/application/ArtifactGraphConstruction.js +257 -0
- package/dist/application/ArtifactInventory.js +253 -0
- package/dist/application/BinarySession.js +164 -14
- package/dist/application/CompositeProvider.js +73 -0
- package/dist/application/DirectAnalysis.js +27 -3
- package/dist/application/Doctor.js +35 -6
- package/dist/application/EnhancedTools.js +8 -7
- package/dist/application/EvidenceBundleCommands.js +29 -0
- package/dist/application/EvidenceBundleFiles.js +127 -0
- package/dist/application/EvidenceLedger.js +225 -18
- package/dist/application/FilesystemSnapshot.js +124 -0
- package/dist/application/LinuxHopper.js +186 -0
- package/dist/application/LoopbackReplay.js +195 -37
- package/dist/application/ProcessHarness.js +200 -191
- package/dist/application/ProcessNormalization.js +44 -0
- package/dist/application/ProcessOwnership.js +84 -0
- package/dist/application/ProcessSampling.js +284 -0
- package/dist/application/RealHopperAssertions.js +105 -0
- package/dist/application/ReferenceSourceImport.js +182 -0
- package/dist/application/ReferenceSourceImportEntries.js +122 -0
- package/dist/application/ReferenceSourceImportPolicy.js +73 -0
- package/dist/application/ReferenceSourceImportTypes.js +18 -0
- package/dist/application/ReferenceSourceVcsAdapter.js +34 -0
- package/dist/application/Setup.js +192 -41
- package/dist/application/Uninstall.js +130 -0
- package/dist/application/runtime.js +8 -1
- package/dist/artifacts/ArtifactPaths.js +51 -0
- package/dist/artifacts/ArtifactProvider.js +130 -0
- package/dist/artifacts/ArtifactReader.js +9 -0
- package/dist/artifacts/AsarArtifactReader.js +62 -0
- package/dist/artifacts/DirectoryArtifactReader.js +107 -0
- package/dist/artifacts/MachOSliceArtifactReader.js +66 -0
- package/dist/artifacts/SafeOutputTree.js +199 -0
- package/dist/artifacts/StreamBytes.js +10 -0
- package/dist/artifacts/ZipArtifactReader.js +109 -0
- package/dist/cli.js +153 -6
- package/dist/config.js +35 -1
- package/dist/contracts/artifactComparisonExample.js +95 -0
- package/dist/contracts/artifactToolContracts.js +84 -0
- package/dist/contracts/enhancedInputs.js +4 -0
- package/dist/contracts/functionComparisonExample.js +57 -0
- package/dist/contracts/investigationExamples.js +118 -0
- package/dist/contracts/nativeToolContracts.js +52 -0
- package/dist/contracts/processCaptureExample.js +25 -0
- package/dist/contracts/toolContractExamples.js +69 -0
- package/dist/contracts/toolContracts.js +99 -36
- package/dist/contracts/toolOutputSchemas.js +154 -82
- package/dist/contracts/unknownContractExamples.js +33 -0
- package/dist/domain/artifactComparison.js +273 -0
- package/dist/domain/artifactGraph.js +194 -0
- package/dist/domain/artifactInventoryEvidence.js +150 -0
- package/dist/domain/binaryTarget.js +55 -0
- package/dist/domain/bundleComparison.js +266 -0
- package/dist/domain/callPath.js +346 -0
- package/dist/domain/changedBehavior.js +294 -0
- package/dist/domain/errors.js +192 -4
- package/dist/domain/evidence.js +41 -10
- package/dist/domain/evidenceBundle.js +187 -6
- package/dist/domain/functionComparison.js +201 -0
- package/dist/domain/functionComparisonNormalization.js +112 -0
- package/dist/domain/functionComparisonResults.js +54 -0
- package/dist/domain/functionComparisonSchemas.js +82 -0
- package/dist/domain/functionDossierEvidence.js +171 -0
- package/dist/domain/hopperValues.js +14 -7
- package/dist/domain/nativeInspection.js +142 -0
- package/dist/domain/processCapture.js +152 -54
- package/dist/domain/processComparison.js +106 -0
- package/dist/domain/reconstructionUnknowns.js +90 -0
- package/dist/domain/reconstructionVerification.js +285 -0
- package/dist/domain/reconstructionVerificationSchemas.js +126 -0
- package/dist/domain/referenceSourceClassification.js +496 -0
- package/dist/domain/referenceSourceGraph.js +376 -0
- package/dist/domain/referenceSourceImportParsing.js +235 -0
- package/dist/domain/residualUnknown.js +239 -0
- package/dist/domain/staticRuntimeCorrelation.js +375 -0
- package/dist/hopper/BridgeLauncher.js +40 -3
- package/dist/hopper/HopperClient.js +14 -5
- package/dist/hopper/HopperProvider.js +57 -22
- package/dist/identity.js +1 -0
- package/dist/main.js +5 -1
- package/dist/native/CommandRunner.js +156 -0
- package/dist/native/NativeMacOSProvider.js +306 -0
- package/dist/native/NativeMachoInspection.js +135 -0
- package/dist/native/parsers/codesign.js +55 -0
- package/dist/native/parsers/demangle.js +26 -0
- package/dist/native/parsers/dyldInfo.js +25 -0
- package/dist/native/parsers/lipo.js +67 -0
- package/dist/native/parsers/otool.js +193 -0
- package/dist/native/parsers/plist.js +23 -0
- package/dist/reference/ReferenceSourceReader.js +73 -0
- package/dist/reference/ReferenceSourceReaderEntries.js +206 -0
- package/dist/reference/ReferenceSourceReaderErrors.js +19 -0
- package/dist/reference/ReferenceSourceReaderFile.js +119 -0
- package/dist/reference/ReferenceSourceReaderPaths.js +23 -0
- package/dist/reference/ReferenceSourceReaderTypes.js +2 -0
- package/dist/reference/ReferenceSourceReaderValidate.js +71 -0
- package/dist/server/createServer.js +31 -5
- package/dist/server/recordDerivedEvidence.js +10 -0
- package/dist/server/registerArtifactComparisonTool.js +62 -0
- package/dist/server/registerArtifactTools.js +6 -0
- package/dist/server/registerBundleComparisonTool.js +47 -0
- package/dist/server/registerEnhancedTools.js +64 -12
- package/dist/server/registerEvidenceTools.js +36 -0
- package/dist/server/registerFunctionComparisonTool.js +68 -0
- package/dist/server/registerInvestigationTools.js +224 -0
- package/dist/server/registerNativeTools.js +6 -0
- package/dist/server/registerOfficialTools.js +47 -14
- package/dist/server/registerProcessComparisonTool.js +106 -0
- package/dist/server/registerSessionTools.js +179 -70
- package/dist/server/sessionEvidence.js +28 -0
- package/dist/server/sessionToolPolicies.js +64 -0
- package/dist/server/toolRegistrationOptions.js +7 -0
- package/dist/server/toolResult.js +8 -5
- package/install.sh +198 -0
- package/package.json +19 -2
- package/scripts/rea.mjs +5 -1
- package/skills/rea-analysis/SKILL.md +77 -2
- /package/dist/{application/HopperToolPort.js → domain/referenceSourcePolicy.js} +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { compareFunctions } from "../domain/functionComparison.js";
|
|
2
|
+
import { createEvidence } from "../domain/evidence.js";
|
|
3
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
4
|
+
import { createEvidenceBundle } from "../domain/evidenceBundle.js";
|
|
5
|
+
import { compareProcessCaptures, processCaptureSchema, } from "../domain/processCapture.js";
|
|
6
|
+
import { FUNCTION_COMPARISON_EXAMPLE } from "./functionComparisonExample.js";
|
|
7
|
+
import { EMPTY_PROCESS_CAPTURE_EXAMPLE } from "./processCaptureExample.js";
|
|
8
|
+
const comparison = compareFunctions(FUNCTION_COMPARISON_EXAMPLE.left, FUNCTION_COMPARISON_EXAMPLE.right, 0, 100);
|
|
9
|
+
export const FUNCTION_COMPARISON_EVIDENCE = createEvidence(undefined, {
|
|
10
|
+
id: "rea-function-comparison",
|
|
11
|
+
name: "REA function comparison",
|
|
12
|
+
version: "1",
|
|
13
|
+
}, {
|
|
14
|
+
predicateType: "rea.function-comparison/v1",
|
|
15
|
+
operation: "compare_functions",
|
|
16
|
+
parameters: {
|
|
17
|
+
left_evidence_ids: [FUNCTION_COMPARISON_EXAMPLE.left.evidence_id],
|
|
18
|
+
right_evidence_ids: [FUNCTION_COMPARISON_EXAMPLE.right.evidence_id],
|
|
19
|
+
offset: 0,
|
|
20
|
+
limit: 100,
|
|
21
|
+
},
|
|
22
|
+
result: jsonValueSchema.parse(comparison),
|
|
23
|
+
confidence: "derived",
|
|
24
|
+
authority: "analyst-inference",
|
|
25
|
+
evidenceLinks: [
|
|
26
|
+
FUNCTION_COMPARISON_EXAMPLE.left.evidence_id,
|
|
27
|
+
FUNCTION_COMPARISON_EXAMPLE.right.evidence_id,
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
const PROCESS_PROVIDER = {
|
|
31
|
+
id: "rea-process",
|
|
32
|
+
name: "REA deterministic process harness",
|
|
33
|
+
version: "1",
|
|
34
|
+
};
|
|
35
|
+
const capture = processCaptureSchema.parse(EMPTY_PROCESS_CAPTURE_EXAMPLE);
|
|
36
|
+
const captureEvidence = (scenario) => createEvidence(undefined, PROCESS_PROVIDER, {
|
|
37
|
+
predicateType: "rea.process-capture/v2",
|
|
38
|
+
operation: "capture_process_scenario",
|
|
39
|
+
parameters: { scenario },
|
|
40
|
+
result: jsonValueSchema.parse(capture),
|
|
41
|
+
confidence: "observed",
|
|
42
|
+
authority: "controlled-replay",
|
|
43
|
+
environment: {
|
|
44
|
+
id: "fixture-linux",
|
|
45
|
+
platform: "linux",
|
|
46
|
+
architecture: "x86_64",
|
|
47
|
+
isolation: "container",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
export const PROCESS_CAPTURE_REFERENCE = captureEvidence("reference");
|
|
51
|
+
export const PROCESS_CAPTURE_RECONSTRUCTION = captureEvidence("reconstruction");
|
|
52
|
+
export const PROCESS_COMPARISON_EVIDENCE = createEvidence(undefined, PROCESS_PROVIDER, {
|
|
53
|
+
predicateType: "rea.process-comparison/v1",
|
|
54
|
+
operation: "compare_process_captures",
|
|
55
|
+
parameters: {
|
|
56
|
+
left_evidence_id: PROCESS_CAPTURE_REFERENCE.evidence_id,
|
|
57
|
+
right_evidence_id: PROCESS_CAPTURE_RECONSTRUCTION.evidence_id,
|
|
58
|
+
left_normalization: capture.normalization,
|
|
59
|
+
right_normalization: capture.normalization,
|
|
60
|
+
},
|
|
61
|
+
result: jsonValueSchema.parse(compareProcessCaptures(capture, capture)),
|
|
62
|
+
confidence: "derived",
|
|
63
|
+
authority: "analyst-inference",
|
|
64
|
+
evidenceLinks: [
|
|
65
|
+
PROCESS_CAPTURE_REFERENCE.evidence_id,
|
|
66
|
+
PROCESS_CAPTURE_RECONSTRUCTION.evidence_id,
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
/** Canonical inputs for comparison-composed investigation contracts. */
|
|
70
|
+
export const INVESTIGATION_EXAMPLES = {
|
|
71
|
+
find_changed_behavior: { comparisons: [FUNCTION_COMPARISON_EVIDENCE] },
|
|
72
|
+
build_call_path: {
|
|
73
|
+
functions: [FUNCTION_COMPARISON_EXAMPLE.left],
|
|
74
|
+
start: { address: "0x1000" },
|
|
75
|
+
goal: { address: "0x1000" },
|
|
76
|
+
},
|
|
77
|
+
correlate_static_and_runtime: {
|
|
78
|
+
static_comparisons: [FUNCTION_COMPARISON_EVIDENCE],
|
|
79
|
+
runtime_comparisons: [PROCESS_COMPARISON_EVIDENCE],
|
|
80
|
+
mappings: [
|
|
81
|
+
{
|
|
82
|
+
static: {
|
|
83
|
+
comparison_evidence_id: FUNCTION_COMPARISON_EVIDENCE.evidence_id,
|
|
84
|
+
selector: { kind: "function", dimension: "pseudocode" },
|
|
85
|
+
},
|
|
86
|
+
runtime: {
|
|
87
|
+
comparison_evidence_id: PROCESS_COMPARISON_EVIDENCE.evidence_id,
|
|
88
|
+
dimension: "terminal",
|
|
89
|
+
},
|
|
90
|
+
side_alignment: "left_to_left",
|
|
91
|
+
hypothesis: {
|
|
92
|
+
statement: "Static implementation changed without terminal change.",
|
|
93
|
+
expected_pattern: "static_only",
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
verify_reconstruction: {
|
|
99
|
+
specification: {
|
|
100
|
+
schema_version: 1,
|
|
101
|
+
name: "Terminal compatibility",
|
|
102
|
+
claims: [
|
|
103
|
+
{
|
|
104
|
+
kind: "behavioral",
|
|
105
|
+
claim_id: "terminal-output",
|
|
106
|
+
title: "Terminal output remains equivalent",
|
|
107
|
+
comparison_evidence_id: PROCESS_COMPARISON_EVIDENCE.evidence_id,
|
|
108
|
+
dimension: "terminal",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
evidence_bundle: createEvidenceBundle([
|
|
113
|
+
PROCESS_CAPTURE_REFERENCE,
|
|
114
|
+
PROCESS_CAPTURE_RECONSTRUCTION,
|
|
115
|
+
PROCESS_COMPARISON_EVIDENCE,
|
|
116
|
+
]),
|
|
117
|
+
},
|
|
118
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { nativeOutputSchemas } from "./toolOutputSchemas.js";
|
|
3
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
4
|
+
const examples = {
|
|
5
|
+
inspect_macho: {},
|
|
6
|
+
inspect_signature: {},
|
|
7
|
+
inspect_plist: { relative_path: "Contents/Info.plist" },
|
|
8
|
+
list_architectures: {},
|
|
9
|
+
demangle_swift: { symbols: ["$s4Test3fooyyF"] },
|
|
10
|
+
};
|
|
11
|
+
const native = (name, description, inputSchema) => {
|
|
12
|
+
const outputSchema = nativeOutputSchemas[name];
|
|
13
|
+
if (outputSchema === undefined)
|
|
14
|
+
throw new Error(`Missing native output schema for ${name}`);
|
|
15
|
+
return {
|
|
16
|
+
name,
|
|
17
|
+
description,
|
|
18
|
+
kind: "native-provider",
|
|
19
|
+
inputSchema,
|
|
20
|
+
outputSchema,
|
|
21
|
+
annotations: {
|
|
22
|
+
readOnlyHint: true,
|
|
23
|
+
destructiveHint: false,
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
openWorldHint: false,
|
|
26
|
+
},
|
|
27
|
+
examples: [
|
|
28
|
+
{
|
|
29
|
+
title: `Example ${name.replaceAll("_", " ")} request`,
|
|
30
|
+
input: z
|
|
31
|
+
.record(z.string(), jsonValueSchema)
|
|
32
|
+
.parse(examples[name] ?? {}),
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/** Provider-neutral semantic operations backed initially by macOS utilities. */
|
|
38
|
+
export const NATIVE_TOOL_CONTRACTS = [
|
|
39
|
+
native("inspect_macho", "Inspect Mach-O slices, load commands, imports, exports, dependencies, build metadata, segments, sections, permissions, and exact command provenance without launching Hopper.", z.object({})),
|
|
40
|
+
native("inspect_signature", "Inspect the active artifact's code-signing identity, hashes, authorities, requirements, entitlements, hardened-runtime state, and bounded command provenance.", z.object({})),
|
|
41
|
+
native("inspect_plist", "Parse a plist at a bounded relative path beneath the active artifact container. Symlink and traversal escapes are rejected; output is normalized JSON rather than plutil text.", z.object({
|
|
42
|
+
relative_path: z
|
|
43
|
+
.string()
|
|
44
|
+
.min(1)
|
|
45
|
+
.max(1_024)
|
|
46
|
+
.default("Contents/Info.plist"),
|
|
47
|
+
})),
|
|
48
|
+
native("list_architectures", "List thin or universal Mach-O slices with offsets, sizes, alignment, explicit coverage, and bounded native-tool provenance.", z.object({})),
|
|
49
|
+
native("demangle_swift", "Demangle an ordered bounded batch of Swift symbols without requiring Hopper. Each input returns demangled, unchanged, or invalid status.", z.object({
|
|
50
|
+
symbols: z.array(z.string().min(1).max(4_096)).min(1).max(500),
|
|
51
|
+
})),
|
|
52
|
+
];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Minimal valid process capture used only in public contract examples. */
|
|
2
|
+
export const EMPTY_PROCESS_CAPTURE_EXAMPLE = {
|
|
3
|
+
schema_version: 2,
|
|
4
|
+
normalization: {
|
|
5
|
+
paths: true,
|
|
6
|
+
pids: true,
|
|
7
|
+
ports: true,
|
|
8
|
+
time_bucket_ms: 10,
|
|
9
|
+
patterns: [],
|
|
10
|
+
},
|
|
11
|
+
frames: [],
|
|
12
|
+
exit: { code: 0, signal: null, reason: "exited" },
|
|
13
|
+
process_samples: [],
|
|
14
|
+
protocol_events: [],
|
|
15
|
+
files_before: [],
|
|
16
|
+
files_after: [],
|
|
17
|
+
filesystem_effects: [],
|
|
18
|
+
truncated: false,
|
|
19
|
+
limitations: [],
|
|
20
|
+
residual_unknowns: [],
|
|
21
|
+
cleanup: {
|
|
22
|
+
owned_process_group: "verified",
|
|
23
|
+
temporary_root: "removed",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { EMPTY_PROCESS_CAPTURE_EXAMPLE } from "./processCaptureExample.js";
|
|
2
|
+
import { UNKNOWN_CONTRACT_EXAMPLES } from "./unknownContractExamples.js";
|
|
3
|
+
import { ARTIFACT_COMPARISON_EXAMPLE } from "./artifactComparisonExample.js";
|
|
4
|
+
import { FUNCTION_COMPARISON_EXAMPLE } from "./functionComparisonExample.js";
|
|
5
|
+
import { INVESTIGATION_EXAMPLES } from "./investigationExamples.js";
|
|
6
|
+
/** Canonical examples for contracts whose required inputs have no defaults. */
|
|
7
|
+
export const TOOL_EXAMPLE_OVERRIDES = {
|
|
8
|
+
...UNKNOWN_CONTRACT_EXAMPLES,
|
|
9
|
+
...INVESTIGATION_EXAMPLES,
|
|
10
|
+
goto_address: { address: "0x1000" },
|
|
11
|
+
procedure_address: { procedure: "main" },
|
|
12
|
+
procedure_assembly: { procedure: "main" },
|
|
13
|
+
procedure_callees: { procedure: "main" },
|
|
14
|
+
procedure_callers: { procedure: "main" },
|
|
15
|
+
procedure_info: { procedure: "main" },
|
|
16
|
+
procedure_references: { procedure: "main" },
|
|
17
|
+
procedure_pseudo_code: { procedure: "main" },
|
|
18
|
+
resolve_containing_procedure: { address: "0x1000" },
|
|
19
|
+
search_procedures: { pattern: "main" },
|
|
20
|
+
search_strings: { pattern: "authorization failed" },
|
|
21
|
+
set_address_name: { address: "0x1000", name: "entry" },
|
|
22
|
+
set_addresses_names: { names: { "0x1000": "entry" } },
|
|
23
|
+
set_bookmark: { address: "0x1000" },
|
|
24
|
+
set_comment: { address: "0x1000", comment: "validated entry point" },
|
|
25
|
+
set_current_document: { document: "fixture" },
|
|
26
|
+
set_inline_comment: { address: "0x1000", comment: "calls parser" },
|
|
27
|
+
unset_bookmark: { address: "0x1000" },
|
|
28
|
+
get_call_graph: { address: "0x1000" },
|
|
29
|
+
find_xrefs_to_name: { name: "malloc" },
|
|
30
|
+
analyze_function: { procedure: "main" },
|
|
31
|
+
trace_feature: { query: "license" },
|
|
32
|
+
open_binary: { path: "/tmp/fixture" },
|
|
33
|
+
import_evidence_bundle: { path: "evidence.json" },
|
|
34
|
+
capture_process_scenario: {
|
|
35
|
+
approved: true,
|
|
36
|
+
executable: "/usr/bin/true",
|
|
37
|
+
working_directory: "/tmp",
|
|
38
|
+
},
|
|
39
|
+
compare_process_captures: {
|
|
40
|
+
left_evidence_id: `ev_${"0".repeat(64)}`,
|
|
41
|
+
left: EMPTY_PROCESS_CAPTURE_EXAMPLE,
|
|
42
|
+
right_evidence_id: `ev_${"1".repeat(64)}`,
|
|
43
|
+
right: EMPTY_PROCESS_CAPTURE_EXAMPLE,
|
|
44
|
+
},
|
|
45
|
+
compare_artifacts: ARTIFACT_COMPARISON_EXAMPLE,
|
|
46
|
+
compare_functions: FUNCTION_COMPARISON_EXAMPLE,
|
|
47
|
+
compare_bundles: {
|
|
48
|
+
left: {
|
|
49
|
+
bundle_version: 2,
|
|
50
|
+
artifacts: [],
|
|
51
|
+
providers: [],
|
|
52
|
+
environments: [],
|
|
53
|
+
scenarios: [],
|
|
54
|
+
captures: [],
|
|
55
|
+
unknowns: [],
|
|
56
|
+
records: [],
|
|
57
|
+
},
|
|
58
|
+
right: {
|
|
59
|
+
bundle_version: 2,
|
|
60
|
+
artifacts: [],
|
|
61
|
+
providers: [],
|
|
62
|
+
environments: [],
|
|
63
|
+
scenarios: [],
|
|
64
|
+
captures: [],
|
|
65
|
+
unknowns: [],
|
|
66
|
+
records: [],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
3
3
|
import { processCaptureSchema, processScenarioSchema, } from "../domain/processCapture.js";
|
|
4
|
+
import { recordUnknownInputSchema, updateUnknownInputSchema, } from "../domain/residualUnknown.js";
|
|
5
|
+
import { artifactComparisonInputSchema } from "../domain/artifactComparison.js";
|
|
6
|
+
import { functionComparisonInputSchema } from "../domain/functionComparison.js";
|
|
7
|
+
import { bundleComparisonInputSchema } from "../domain/bundleComparison.js";
|
|
8
|
+
import { changedBehaviorInputSchema } from "../domain/changedBehavior.js";
|
|
9
|
+
import { callPathInputSchema } from "../domain/callPath.js";
|
|
10
|
+
import { staticRuntimeCorrelationInputSchema } from "../domain/staticRuntimeCorrelation.js";
|
|
11
|
+
import { reconstructionVerificationInputSchema } from "../domain/reconstructionVerification.js";
|
|
4
12
|
import { enhancedInputSchemas } from "./enhancedInputs.js";
|
|
5
|
-
import { enhancedOutputSchemas, officialOutputSchemas, sessionOutputSchemas, } from "./toolOutputSchemas.js";
|
|
13
|
+
import { enhancedOutputSchemas, officialOutputSchemas, requireOutputSchema, sessionOutputSchemas, } from "./toolOutputSchemas.js";
|
|
14
|
+
import { TOOL_EXAMPLE_OVERRIDES } from "./toolContractExamples.js";
|
|
15
|
+
import { NATIVE_TOOL_CONTRACTS } from "./nativeToolContracts.js";
|
|
16
|
+
import { ARTIFACT_TOOL_CONTRACTS } from "./artifactToolContracts.js";
|
|
6
17
|
const document = z.string().optional().describe("The document name");
|
|
7
18
|
const address = z.string().describe("A Hopper address");
|
|
8
19
|
const optionalAddress = address.optional();
|
|
9
20
|
const procedure = z.string().describe("The procedure name or address");
|
|
10
|
-
const
|
|
21
|
+
const searchPattern = z
|
|
22
|
+
.string()
|
|
23
|
+
.min(1)
|
|
24
|
+
.max(256)
|
|
25
|
+
.describe("The literal text or bounded regex pattern to search for");
|
|
11
26
|
const caseSensitive = z
|
|
12
27
|
.boolean()
|
|
13
28
|
.default(false)
|
|
@@ -16,29 +31,55 @@ const pagination = {
|
|
|
16
31
|
offset: z.number().int().min(0).default(0),
|
|
17
32
|
limit: z.number().int().min(1).max(500).default(100),
|
|
18
33
|
};
|
|
34
|
+
const searchInput = {
|
|
35
|
+
pattern: searchPattern,
|
|
36
|
+
mode: z.enum(["literal", "regex"]).default("literal"),
|
|
37
|
+
case_sensitive: caseSensitive,
|
|
38
|
+
offset: z.number().int().min(0).default(0),
|
|
39
|
+
limit: z.number().int().min(1).max(100).default(100),
|
|
40
|
+
document,
|
|
41
|
+
};
|
|
42
|
+
const exampleInputSchema = z.record(z.string(), jsonValueSchema);
|
|
43
|
+
const examplesFor = (name, inputSchema) => {
|
|
44
|
+
const parsed = inputSchema.parse(TOOL_EXAMPLE_OVERRIDES[name] ?? {});
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
title: `Example ${name.replaceAll("_", " ")} request`,
|
|
48
|
+
input: exampleInputSchema.parse(parsed),
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
};
|
|
19
52
|
const annotations = (name, kind) => ({
|
|
20
|
-
readOnlyHint: kind === "enhanced" ||
|
|
21
|
-
name === "
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
destructiveHint: name === "unset_bookmark" ||
|
|
53
|
+
readOnlyHint: (kind === "enhanced" && name !== "trace_feature") ||
|
|
54
|
+
name === "binary_session" ||
|
|
55
|
+
name === "list_unknowns" ||
|
|
56
|
+
name === "verify_unknown_resolution",
|
|
57
|
+
destructiveHint: name === "export_evidence_bundle" ||
|
|
58
|
+
name === "unset_bookmark" ||
|
|
27
59
|
name === "set_address_name" ||
|
|
28
60
|
name === "set_addresses_names" ||
|
|
29
61
|
name === "set_comment" ||
|
|
30
62
|
name === "set_inline_comment",
|
|
31
|
-
idempotentHint:
|
|
32
|
-
openWorldHint:
|
|
33
|
-
});
|
|
34
|
-
const official = (name, description, inputSchema) => ({
|
|
35
|
-
name,
|
|
36
|
-
description,
|
|
37
|
-
kind: "official-proxy",
|
|
38
|
-
inputSchema,
|
|
39
|
-
outputSchema: requireOutputSchema(officialOutputSchemas, name),
|
|
40
|
-
annotations: annotations(name, "official-proxy"),
|
|
63
|
+
idempotentHint: name !== "record_unknown" && name !== "update_unknown",
|
|
64
|
+
openWorldHint: name === "capture_process_scenario",
|
|
41
65
|
});
|
|
66
|
+
const official = (name, description, inputSchema) => {
|
|
67
|
+
const trackedInputSchema = inputSchema.extend({
|
|
68
|
+
unknown_registry_approved: z
|
|
69
|
+
.literal(true)
|
|
70
|
+
.optional()
|
|
71
|
+
.describe("Explicit approval to record typed capability unavailability as a residual unknown"),
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
name,
|
|
75
|
+
description,
|
|
76
|
+
kind: "official-proxy",
|
|
77
|
+
inputSchema: trackedInputSchema,
|
|
78
|
+
outputSchema: requireOutputSchema(officialOutputSchemas, name),
|
|
79
|
+
annotations: annotations(name, "official-proxy"),
|
|
80
|
+
examples: examplesFor(name, trackedInputSchema),
|
|
81
|
+
};
|
|
82
|
+
};
|
|
42
83
|
const enhanced = (name, description, inputSchema) => ({
|
|
43
84
|
name,
|
|
44
85
|
description,
|
|
@@ -46,6 +87,7 @@ const enhanced = (name, description, inputSchema) => ({
|
|
|
46
87
|
inputSchema,
|
|
47
88
|
outputSchema: requireOutputSchema(enhancedOutputSchemas, name),
|
|
48
89
|
annotations: annotations(name, "enhanced"),
|
|
90
|
+
examples: examplesFor(name, inputSchema),
|
|
49
91
|
});
|
|
50
92
|
const session = (name, description, inputSchema) => ({
|
|
51
93
|
name,
|
|
@@ -54,13 +96,8 @@ const session = (name, description, inputSchema) => ({
|
|
|
54
96
|
inputSchema,
|
|
55
97
|
outputSchema: requireOutputSchema(sessionOutputSchemas, name),
|
|
56
98
|
annotations: annotations(name, "session"),
|
|
99
|
+
examples: examplesFor(name, inputSchema),
|
|
57
100
|
});
|
|
58
|
-
const requireOutputSchema = (schemas, name) => {
|
|
59
|
-
const schema = schemas[name];
|
|
60
|
-
if (schema === undefined)
|
|
61
|
-
throw new Error(`Missing output schema for ${name}`);
|
|
62
|
-
return schema;
|
|
63
|
-
};
|
|
64
101
|
/** Bridge operations exposed without additional application composition. */
|
|
65
102
|
export const OFFICIAL_TOOL_CONTRACTS = [
|
|
66
103
|
official("address_name", "Resolve the analyzed name at a code or data address, defaulting to Hopper's current cursor. Use before following a symbol into xrefs; null means Hopper has no name at that address.", z.object({ document, address: optionalAddress })),
|
|
@@ -93,8 +130,8 @@ export const OFFICIAL_TOOL_CONTRACTS = [
|
|
|
93
130
|
})),
|
|
94
131
|
official("procedure_pseudo_code", "Decompile one analyzed procedure by symbol name or hexadecimal address. Returns Hopper pseudocode, not original source, and may return null; request procedure_assembly when instruction precision matters.", z.object({ procedure, document })),
|
|
95
132
|
official("resolve_containing_procedure", "Resolve an arbitrary address, including an xref source or interior instruction, to its Hopper-analyzed containing procedure. A negative result includes an explicit reason and is not guessed from nearby symbols.", z.object({ address, document })),
|
|
96
|
-
official("search_procedures", "
|
|
97
|
-
official("search_strings", "
|
|
133
|
+
official("search_procedures", "Search analyzed procedure names using literal matching by default or a structurally bounded regex. Returns a deterministic, offset-paginated page; continue at next_offset while has_more is true.", z.object(searchInput)),
|
|
134
|
+
official("search_strings", "Search analyzed strings using literal matching by default or a structurally bounded regex. Returns a deterministic, offset-paginated page with explicit value truncation; follow matches with xrefs.", z.object(searchInput)),
|
|
98
135
|
official("set_address_name", "Assign an analyst name to one hexadecimal address and report Hopper's boolean result. This mutates analysis metadata; read it back with address_name before relying on it.", z.object({ address, name: z.string(), document })),
|
|
99
136
|
official("set_addresses_names", "Assign analyst names to an address/name map and return per-address success booleans. This mutates analysis metadata; use for bounded batches and verify failures individually.", z.object({ names: z.record(z.string(), z.string()), document })),
|
|
100
137
|
official("set_bookmark", "Create or replace a bookmark at a hexadecimal address. This mutates navigation metadata; verify with list_bookmarks and do not treat bookmarks as binary evidence.", z.object({ address, name: z.string().optional(), document })),
|
|
@@ -115,30 +152,56 @@ export const ENHANCED_TOOL_CONTRACTS = [
|
|
|
115
152
|
enhanced("find_xrefs_to_name", "Resolve a name through Hopper and return analyzed references to its address. Use when starting from a selector or symbol; resolution failure is returned explicitly and xrefs remain untyped.", enhancedInputSchemas.find_xrefs_to_name),
|
|
116
153
|
enhanced("binary_overview", "Use immediately after opening a target to summarize document, exhaustive procedure/string counts, and a bounded segment sample. detail controls segment fields and limit controls only the returned segment sample.", enhancedInputSchemas.binary_overview),
|
|
117
154
|
enhanced("analyze_function", "Preferred bounded analysis for one procedure symbol or address. Returns identity, pseudocode, optional assembly, comments, calls, incoming references, and blocks; unsupported outgoing references and CFG edges carry explicit unavailable metadata.", enhancedInputSchemas.analyze_function),
|
|
118
|
-
enhanced("trace_feature", "Trace a bounded literal feature query through matching strings and procedures, xrefs, and truthful containing-procedure resolution. Returns the operation budget, truncation, and residual unknowns;
|
|
155
|
+
enhanced("trace_feature", "Trace a bounded literal feature query through matching strings and procedures, xrefs, and truthful containing-procedure resolution. Returns the operation budget, truncation, and residual unknowns; unknown_registry_approved: true records them durably without inferring reference kinds.", enhancedInputSchemas.trace_feature),
|
|
119
156
|
];
|
|
120
157
|
/** Target lifecycle tools available only on the long-lived MCP adapter. */
|
|
121
158
|
export const SESSION_TOOL_CONTRACTS = [
|
|
122
|
-
session("open_binary", "Open a local executable, application bundle,
|
|
123
|
-
session("close_binary", "Close the active
|
|
124
|
-
session("binary_session", "Report whether a target is open
|
|
125
|
-
session("export_evidence_bundle", "Return the session's deterministic Evidence v2 bundle
|
|
126
|
-
|
|
127
|
-
|
|
159
|
+
session("open_binary", "Open a local executable, application bundle, archive, JavaScript, source map, plist, or Hopper database after validation. Providers start lazily: inventory_artifact does not launch Hopper; deep native operations may show Hopper UI.", z.object({ path: z.string().min(1) })),
|
|
160
|
+
session("close_binary", "Close the active target and every provider resource started for it. The operation is idempotent; call binary_session to verify the session is closed.", z.object({})),
|
|
161
|
+
session("binary_session", "Report provider identity, deterministic capability descriptors, and whether a target is open; open targets include canonical path, format, and kind. Use availability, effects, limits, and limitations before selecting analysis operations.", z.object({})),
|
|
162
|
+
session("export_evidence_bundle", "Return the session's deterministic Evidence v2 bundle, or atomically write it beneath an operator-approved root. Existing files require overwrite: true; records and manifests use canonical byte-stable ordering.", z.object({
|
|
163
|
+
path: z.string().min(1).optional(),
|
|
164
|
+
overwrite: z.boolean().default(false),
|
|
165
|
+
})),
|
|
166
|
+
session("import_evidence_bundle", "Read a bounded local JSON bundle beneath an operator-approved root, validate every Evidence v2 ID and canonical manifest, then atomically merge it. Imported content is data only and is never executed.", z.object({ path: z.string().min(1) })),
|
|
167
|
+
session("capture_process_scenario", "Run one bounded process under a PTY using operator-approved executable and working roots. Requires approved: true; unknown_registry_approved: true separately records capture residuals. Captures normalized terminal frames, descendants, filesystem snapshots, and loopback replay. Disabled unless operator policy enables it; not a security sandbox.", processScenarioSchema),
|
|
128
168
|
session("compare_process_captures", "Compare two bounded process captures across terminal, exit, sampled process, filesystem, HTTP, and WebSocket evidence. Missing or truncated observations are never treated as equivalent.", z.object({
|
|
129
169
|
left_evidence_id: z.string().regex(/^ev_[a-f0-9]{64}$/u),
|
|
130
170
|
left: processCaptureSchema,
|
|
131
171
|
right_evidence_id: z.string().regex(/^ev_[a-f0-9]{64}$/u),
|
|
132
172
|
right: processCaptureSchema,
|
|
173
|
+
unknown_registry_approved: z
|
|
174
|
+
.literal(true)
|
|
175
|
+
.optional()
|
|
176
|
+
.describe("Explicit approval to record capture disagreement durably"),
|
|
177
|
+
})),
|
|
178
|
+
session("compare_artifacts", "Compare two bounded sets of inventory_artifact Evidence pages by logical occurrence path, content identity, metadata, and graph relations. Pages must share and satisfy their graph commitment; every delta cites both sets, and gaps yield truncated or unknown, never equivalence.", artifactComparisonInputSchema),
|
|
179
|
+
session("compare_functions", "Compare two explicit bounded sets of analyze_function Evidence pages across identity, exact provider text, calls, references, strings, and address-normalized CFG topology. Missing or provider-incompatible facets remain truncated or unknown; every conclusion cites both Evidence sets.", functionComparisonInputSchema),
|
|
180
|
+
session("compare_bundles", "Compare two canonical Evidence v2 bundles by exact record membership, explicit one-to-one observation pairs, and complete residual-unknown revision histories. Missing bundle members describe omission only, never behavioral equivalence; output is digest-anchored and deterministically paginated.", bundleComparisonInputSchema),
|
|
181
|
+
session("find_changed_behavior", "Aggregate validated process, artifact, and function comparison Evidence into a deterministic change report. Runtime observations remain distinct from static behavior candidates; missing or incomplete comparisons produce unresolved findings, never causal claims.", changedBehaviorInputSchema),
|
|
182
|
+
session("build_call_path", "Build bounded shortest-first direct-callee paths from explicit analyze_function Evidence groups using exact canonical addresses. Missing dossiers, incomplete callee pages, provider mixing, and depth frontiers remain unknown; every node and edge cites source Evidence.", callPathInputSchema),
|
|
183
|
+
session("correlate_static_and_runtime", "Evaluate explicit caller-declared hypotheses between exact static comparison findings and runtime comparison dimensions. Similar names or paths are never auto-matched, consistent cochange never proves causality, and unknown or truncated inputs remain unresolved.", staticRuntimeCorrelationInputSchema),
|
|
184
|
+
session("verify_reconstruction", "Verify a finite typed behavioral and structural specification against a canonical Evidence bundle. Pass means every declared claim has complete comparable authority—not global source equivalence; changed claims fail and missing, limited, or unresolved evidence stays unknown.", reconstructionVerificationInputSchema),
|
|
185
|
+
session("list_unknowns", "List current residual-unknown heads in deterministic ID order, with optional exact status, severity, and domain filters. This is read-only; unresolved, contradicted, and non-truth dispositions remain distinct.", z.object({
|
|
186
|
+
status: z
|
|
187
|
+
.enum(["open", "investigating", "blocked", "contradicted", "resolved"])
|
|
188
|
+
.optional(),
|
|
189
|
+
severity: z.enum(["low", "medium", "high", "critical"]).optional(),
|
|
190
|
+
domain: z.string().trim().min(1).max(100).optional(),
|
|
133
191
|
})),
|
|
192
|
+
session("record_unknown", "Create one deterministic residual unknown and immutable mutation evidence. Requires approved: true, validates all evidence and relationship references, and rejects duplicate stable identity.", recordUnknownInputSchema),
|
|
193
|
+
session("update_unknown", "Append one immutable full-state revision and mutation evidence. Requires approved: true and exact expected_revision; stale concurrent writers fail instead of overwriting newer analysis.", updateUnknownInputSchema),
|
|
194
|
+
session("verify_unknown_resolution", "Revalidate the current residual-unknown head against live bundled evidence, exact authority/confidence/environment requirements, and revision integrity. Withdrawn and out-of-scope dispositions are not truth claims.", z.object({ unknown_id: z.string().regex(/^unk_[a-f0-9]{64}$/u) })),
|
|
134
195
|
];
|
|
135
196
|
/**
|
|
136
197
|
* Complete ordered public inventory used by registration and verification.
|
|
137
|
-
* Keep this collection at
|
|
198
|
+
* Keep this collection at 68 tools unless a deliberate contract change updates
|
|
138
199
|
* snapshots, package verification, and real-Hopper verification together.
|
|
139
200
|
*/
|
|
140
201
|
export const TOOL_CONTRACTS = [
|
|
141
202
|
...OFFICIAL_TOOL_CONTRACTS,
|
|
142
203
|
...ENHANCED_TOOL_CONTRACTS,
|
|
204
|
+
...NATIVE_TOOL_CONTRACTS,
|
|
205
|
+
...ARTIFACT_TOOL_CONTRACTS,
|
|
143
206
|
...SESSION_TOOL_CONTRACTS,
|
|
144
207
|
];
|