rea-agents 0.3.0 → 0.5.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 +139 -21
- package/bridge/hopper_bridge.py +156 -14
- 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 +174 -14
- package/dist/application/CompositeProvider.js +73 -0
- package/dist/application/DirectAnalysis.js +44 -3
- package/dist/application/Doctor.js +35 -6
- package/dist/application/EnhancedTools.js +10 -7
- package/dist/application/EvidenceBundleCommands.js +51 -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 +105 -0
- package/dist/application/ProcessSampling.js +284 -0
- package/dist/application/RealHopperAssertions.js +116 -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 +229 -21
- package/dist/cliEvidenceCommands.js +68 -0
- package/dist/cliLogging.js +21 -0
- 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 +156 -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 +199 -5
- 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 +35 -8
- 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/referenceSourcePolicy.js +1 -0
- package/dist/domain/residualUnknown.js +239 -0
- package/dist/domain/staticRuntimeCorrelation.js +375 -0
- package/dist/hopper/BridgeLauncher.js +39 -3
- package/dist/hopper/HopperClient.js +14 -5
- package/dist/hopper/HopperProvider.js +57 -22
- package/dist/hopper/protocol.js +13 -2
- 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 +18 -1
- package/scripts/rea.mjs +5 -1
- package/skills/rea-analysis/SKILL.md +77 -2
|
@@ -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
|
];
|
|
@@ -1,21 +1,92 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { evidenceSchema } from "../domain/evidence.js";
|
|
3
|
-
import { processCaptureSchema } from "../domain/processCapture.js";
|
|
3
|
+
import { processCaptureComparisonSchema, processCaptureSchema, } from "../domain/processCapture.js";
|
|
4
4
|
import { evidenceBundleSchema } from "../domain/evidenceBundle.js";
|
|
5
|
-
|
|
5
|
+
import { residualUnknownSchema } from "../domain/residualUnknown.js";
|
|
6
|
+
import { functionDossierSchema } from "../domain/hopperValues.js";
|
|
7
|
+
import { demangleSwiftSchema, inspectMachoSchema, inspectPlistSchema, inspectSignatureSchema, listArchitecturesSchema, } from "../domain/nativeInspection.js";
|
|
8
|
+
import { artifactExtractionResultSchema, artifactInventoryResultSchema, } from "../domain/artifactGraph.js";
|
|
9
|
+
import { artifactComparisonResultSchema } from "../domain/artifactComparison.js";
|
|
10
|
+
import { functionComparisonResultSchema } from "../domain/functionComparison.js";
|
|
11
|
+
import { bundleComparisonResultSchema } from "../domain/bundleComparison.js";
|
|
12
|
+
import { changedBehaviorResultSchema } from "../domain/changedBehavior.js";
|
|
13
|
+
import { callPathResultSchema } from "../domain/callPath.js";
|
|
14
|
+
import { staticRuntimeCorrelationResultSchema } from "../domain/staticRuntimeCorrelation.js";
|
|
15
|
+
import { reconstructionVerificationResultSchema } from "../domain/reconstructionVerification.js";
|
|
16
|
+
const resultOf = (schema) => evidenceSchema
|
|
17
|
+
.omit({ normalized_result: true })
|
|
18
|
+
.extend({ normalized_result: schema });
|
|
6
19
|
const lifecycleResultOf = (schema) => z.object({ result: schema });
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
/** Resolve a required named output schema or reject contract drift. */
|
|
21
|
+
export const requireOutputSchema = (schemas, name) => {
|
|
22
|
+
const schema = schemas[name];
|
|
23
|
+
if (schema === undefined)
|
|
24
|
+
throw new Error(`Missing output schema for ${name}`);
|
|
25
|
+
return schema;
|
|
26
|
+
};
|
|
27
|
+
const targetFormatSchema = z.enum([
|
|
28
|
+
"hopper",
|
|
29
|
+
"mach-o",
|
|
30
|
+
"elf",
|
|
31
|
+
"pe",
|
|
32
|
+
"zip",
|
|
33
|
+
"ipa",
|
|
34
|
+
"apk",
|
|
35
|
+
"asar",
|
|
36
|
+
"dmg",
|
|
37
|
+
"pkg",
|
|
38
|
+
"plist",
|
|
39
|
+
"javascript",
|
|
40
|
+
"source-map",
|
|
41
|
+
]);
|
|
42
|
+
const targetKindSchema = z.enum([
|
|
43
|
+
"executable",
|
|
44
|
+
"database",
|
|
45
|
+
"archive",
|
|
46
|
+
"artifact",
|
|
14
47
|
]);
|
|
48
|
+
const providerCapability = z.object({
|
|
49
|
+
operation: z.string(),
|
|
50
|
+
available: z.boolean(),
|
|
51
|
+
reason: z.string().nullable(),
|
|
52
|
+
input_contract_version: z.number().int().min(1),
|
|
53
|
+
output_contract_version: z.number().int().min(1),
|
|
54
|
+
pagination: z.enum(["none", "offset", "cursor"]),
|
|
55
|
+
exhaustive: z.boolean(),
|
|
56
|
+
effects: z.object({
|
|
57
|
+
mutates_artifact: z.boolean(),
|
|
58
|
+
launches_process: z.boolean(),
|
|
59
|
+
may_show_ui: z.boolean(),
|
|
60
|
+
may_access_network: z.boolean(),
|
|
61
|
+
may_write_filesystem: z.boolean(),
|
|
62
|
+
changes_permissions: z.boolean(),
|
|
63
|
+
requires_root: z.boolean(),
|
|
64
|
+
}),
|
|
65
|
+
limits: z.object({
|
|
66
|
+
max_results: z.number().int().min(0).nullable(),
|
|
67
|
+
max_payload_bytes: z.number().int().min(0).nullable(),
|
|
68
|
+
timeout_ms: z.number().int().min(0).nullable(),
|
|
69
|
+
}),
|
|
70
|
+
limitations: z.array(z.string()),
|
|
71
|
+
});
|
|
72
|
+
const providerIdentity = z.object({
|
|
73
|
+
id: z.string(),
|
|
74
|
+
name: z.string(),
|
|
75
|
+
version: z.string().nullable(),
|
|
76
|
+
});
|
|
77
|
+
const sessionProvider = z.object({
|
|
78
|
+
provider: providerIdentity,
|
|
79
|
+
providers: z.array(providerIdentity),
|
|
80
|
+
capabilities: z.array(providerCapability),
|
|
81
|
+
});
|
|
15
82
|
const nullableText = z.string().nullable();
|
|
16
83
|
const addressList = z.array(z.string());
|
|
17
84
|
const addressedEntry = z.object({ address: z.string(), name: z.string() });
|
|
18
85
|
const procedureIdentity = z.object({ address: z.string(), name: z.string() });
|
|
86
|
+
const localVariable = z.object({
|
|
87
|
+
description: z.string(),
|
|
88
|
+
provenance: z.literal("hopper-public-python-api"),
|
|
89
|
+
});
|
|
19
90
|
const containingProcedureResolution = z.discriminatedUnion("found", [
|
|
20
91
|
z.object({
|
|
21
92
|
query_address: z.string(),
|
|
@@ -48,22 +119,31 @@ const pageOutput = z.object({
|
|
|
48
119
|
next_offset: z.number().int().min(0).nullable(),
|
|
49
120
|
has_more: z.boolean(),
|
|
50
121
|
});
|
|
51
|
-
const
|
|
122
|
+
const searchPageOutput = pageOutput.extend({
|
|
123
|
+
items: z.array(z.object({
|
|
124
|
+
address: z.string(),
|
|
125
|
+
value: z.string(),
|
|
126
|
+
value_truncated: z.boolean(),
|
|
127
|
+
})),
|
|
128
|
+
});
|
|
129
|
+
const memoryRegionOutput = z.object({
|
|
52
130
|
name: z.string(),
|
|
53
131
|
start: z.string(),
|
|
54
132
|
end: z.string(),
|
|
133
|
+
readable: z.boolean().nullable(),
|
|
55
134
|
writable: z.boolean().nullable(),
|
|
56
135
|
executable: z.boolean().nullable(),
|
|
57
136
|
permissions: unavailable,
|
|
58
|
-
|
|
59
|
-
})
|
|
137
|
+
provenance: z.literal("hopper-public-python-api"),
|
|
138
|
+
});
|
|
139
|
+
const segmentOutput = resultOf(z.array(memoryRegionOutput.extend({ sections: z.array(memoryRegionOutput) })));
|
|
60
140
|
const procedureInfoOutput = resultOf(z.object({
|
|
61
141
|
name: z.string(),
|
|
62
142
|
entrypoint: z.string(),
|
|
63
143
|
basicblock_count: z.number().int().min(0),
|
|
64
144
|
length: z.number().min(0),
|
|
65
145
|
signature: nullableText,
|
|
66
|
-
locals: z.array(
|
|
146
|
+
locals: z.array(localVariable),
|
|
67
147
|
}));
|
|
68
148
|
const symbolDiscoveryOutput = (property) => resultOf(z.object({
|
|
69
149
|
count: z.number().int().min(0),
|
|
@@ -73,54 +153,7 @@ const graphNode = z.union([
|
|
|
73
153
|
z.object({ address: z.string(), calls: z.array(z.string()) }),
|
|
74
154
|
z.object({ address: z.string(), error: z.string() }),
|
|
75
155
|
]);
|
|
76
|
-
const
|
|
77
|
-
source_address: z.string(),
|
|
78
|
-
target_address: z.string(),
|
|
79
|
-
source_procedure: procedureIdentity.nullable(),
|
|
80
|
-
target_procedure: procedureIdentity.nullable(),
|
|
81
|
-
kind: unavailable,
|
|
82
|
-
});
|
|
83
|
-
const referencedValue = z.object({
|
|
84
|
-
address: z.string(),
|
|
85
|
-
value: z.string(),
|
|
86
|
-
source_address: z.string(),
|
|
87
|
-
});
|
|
88
|
-
const functionDossierOutput = resultOf(z.object({
|
|
89
|
-
procedure: z.object({
|
|
90
|
-
address: z.string(),
|
|
91
|
-
name: z.string(),
|
|
92
|
-
signature: nullableText,
|
|
93
|
-
locals: z.array(z.json()),
|
|
94
|
-
}),
|
|
95
|
-
pseudocode: z.object({
|
|
96
|
-
text: z.string(),
|
|
97
|
-
total_chars: z.number().int().min(0),
|
|
98
|
-
returned_chars: z.number().int().min(0),
|
|
99
|
-
truncated: z.boolean(),
|
|
100
|
-
next_offset: z.number().int().min(0).nullable(),
|
|
101
|
-
}),
|
|
102
|
-
assembly: bounded(z.string()),
|
|
103
|
-
comments: bounded(z.object({
|
|
104
|
-
address: z.string(),
|
|
105
|
-
kind: z.enum(["comment", "inline"]),
|
|
106
|
-
text: z.string(),
|
|
107
|
-
})),
|
|
108
|
-
callers: bounded(procedureIdentity),
|
|
109
|
-
callees: bounded(procedureIdentity),
|
|
110
|
-
incoming_references: bounded(referenceEdge),
|
|
111
|
-
outgoing_references: bounded(referenceEdge),
|
|
112
|
-
referenced_strings: bounded(referencedValue),
|
|
113
|
-
referenced_names: bounded(referencedValue),
|
|
114
|
-
basic_blocks: bounded(z.object({
|
|
115
|
-
start: z.string(),
|
|
116
|
-
end: z.string(),
|
|
117
|
-
successors: z.array(z.string()),
|
|
118
|
-
})),
|
|
119
|
-
instruction_scan: z.object({
|
|
120
|
-
scanned: z.number().int().min(0),
|
|
121
|
-
truncated: z.boolean(),
|
|
122
|
-
}),
|
|
123
|
-
}));
|
|
156
|
+
const functionDossierOutput = resultOf(functionDossierSchema);
|
|
124
157
|
/** Exact structured-content schemas for the direct Hopper operations. */
|
|
125
158
|
export const officialOutputSchemas = {
|
|
126
159
|
address_name: resultOf(nullableText),
|
|
@@ -158,8 +191,8 @@ export const officialOutputSchemas = {
|
|
|
158
191
|
})),
|
|
159
192
|
procedure_pseudo_code: resultOf(nullableText),
|
|
160
193
|
resolve_containing_procedure: resultOf(containingProcedureResolution),
|
|
161
|
-
search_procedures: resultOf(
|
|
162
|
-
search_strings: resultOf(
|
|
194
|
+
search_procedures: resultOf(searchPageOutput),
|
|
195
|
+
search_strings: resultOf(searchPageOutput),
|
|
163
196
|
set_address_name: resultOf(z.boolean()),
|
|
164
197
|
set_addresses_names: resultOf(z.record(z.string(), z.boolean())),
|
|
165
198
|
set_bookmark: resultOf(z.boolean()),
|
|
@@ -223,45 +256,86 @@ export const enhancedOutputSchemas = {
|
|
|
223
256
|
residual_unknowns: z.array(z.string()),
|
|
224
257
|
})),
|
|
225
258
|
};
|
|
259
|
+
/** Exact Evidence v2 schemas for provider-neutral native inspection. */
|
|
260
|
+
export const nativeOutputSchemas = {
|
|
261
|
+
inspect_macho: resultOf(inspectMachoSchema),
|
|
262
|
+
inspect_signature: resultOf(inspectSignatureSchema),
|
|
263
|
+
inspect_plist: resultOf(inspectPlistSchema),
|
|
264
|
+
list_architectures: resultOf(listArchitecturesSchema),
|
|
265
|
+
demangle_swift: resultOf(demangleSwiftSchema),
|
|
266
|
+
};
|
|
267
|
+
/** Exact Evidence v2 schemas for provider-neutral artifact graph operations. */
|
|
268
|
+
export const artifactOutputSchemas = {
|
|
269
|
+
inventory_artifact: resultOf(artifactInventoryResultSchema),
|
|
270
|
+
extract_artifact: resultOf(artifactExtractionResultSchema),
|
|
271
|
+
};
|
|
226
272
|
/** Exact structured-content schemas for target lifecycle operations. */
|
|
227
273
|
export const sessionOutputSchemas = {
|
|
228
274
|
open_binary: lifecycleResultOf(z.object({
|
|
229
275
|
path: z.string(),
|
|
230
|
-
format:
|
|
231
|
-
kind:
|
|
276
|
+
format: targetFormatSchema,
|
|
277
|
+
kind: targetKindSchema,
|
|
232
278
|
loaderArgs: z.array(z.string()),
|
|
233
279
|
sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
234
280
|
architecture: z.enum(["x86", "x86_64", "arm", "arm64"]).nullable(),
|
|
235
281
|
})),
|
|
236
282
|
close_binary: lifecycleResultOf(z.null()),
|
|
237
283
|
binary_session: lifecycleResultOf(z.union([
|
|
238
|
-
|
|
239
|
-
|
|
284
|
+
sessionProvider.extend({ open: z.literal(false) }),
|
|
285
|
+
sessionProvider.extend({
|
|
240
286
|
open: z.literal(true),
|
|
241
287
|
path: z.string(),
|
|
242
|
-
format:
|
|
243
|
-
kind:
|
|
288
|
+
format: targetFormatSchema,
|
|
289
|
+
kind: targetKindSchema,
|
|
244
290
|
sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
245
291
|
architecture: z.enum(["x86", "x86_64", "arm", "arm64"]).nullable(),
|
|
246
292
|
}),
|
|
247
293
|
])),
|
|
248
|
-
export_evidence_bundle: lifecycleResultOf(
|
|
294
|
+
export_evidence_bundle: lifecycleResultOf(z.union([
|
|
295
|
+
evidenceBundleSchema,
|
|
296
|
+
z.object({
|
|
297
|
+
path: z.string(),
|
|
298
|
+
bytes: z.number().int().min(0),
|
|
299
|
+
records: z.number().int().min(0),
|
|
300
|
+
}),
|
|
301
|
+
])),
|
|
249
302
|
import_evidence_bundle: lifecycleResultOf(z.object({
|
|
250
303
|
imported: z.number().int().min(0),
|
|
251
304
|
total: z.number().int().min(0),
|
|
252
305
|
})),
|
|
253
306
|
capture_process_scenario: lifecycleResultOf(evidenceSchema
|
|
254
|
-
.omit({
|
|
255
|
-
.extend({
|
|
256
|
-
compare_process_captures: lifecycleResultOf(evidenceSchema.omit({
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
307
|
+
.omit({ normalized_result: true })
|
|
308
|
+
.extend({ normalized_result: processCaptureSchema })),
|
|
309
|
+
compare_process_captures: lifecycleResultOf(evidenceSchema.omit({ normalized_result: true }).extend({
|
|
310
|
+
normalized_result: processCaptureComparisonSchema,
|
|
311
|
+
})),
|
|
312
|
+
compare_artifacts: lifecycleResultOf(evidenceSchema.omit({ normalized_result: true }).extend({
|
|
313
|
+
normalized_result: artifactComparisonResultSchema,
|
|
314
|
+
})),
|
|
315
|
+
compare_functions: lifecycleResultOf(evidenceSchema.omit({ normalized_result: true }).extend({
|
|
316
|
+
normalized_result: functionComparisonResultSchema,
|
|
317
|
+
})),
|
|
318
|
+
compare_bundles: lifecycleResultOf(evidenceSchema.omit({ normalized_result: true }).extend({
|
|
319
|
+
normalized_result: bundleComparisonResultSchema,
|
|
320
|
+
})),
|
|
321
|
+
find_changed_behavior: lifecycleResultOf(evidenceSchema.omit({ normalized_result: true }).extend({
|
|
322
|
+
normalized_result: changedBehaviorResultSchema,
|
|
323
|
+
})),
|
|
324
|
+
build_call_path: lifecycleResultOf(evidenceSchema.omit({ normalized_result: true }).extend({
|
|
325
|
+
normalized_result: callPathResultSchema,
|
|
326
|
+
})),
|
|
327
|
+
correlate_static_and_runtime: lifecycleResultOf(evidenceSchema.omit({ normalized_result: true }).extend({
|
|
328
|
+
normalized_result: staticRuntimeCorrelationResultSchema,
|
|
329
|
+
})),
|
|
330
|
+
verify_reconstruction: lifecycleResultOf(evidenceSchema.omit({ normalized_result: true }).extend({
|
|
331
|
+
normalized_result: reconstructionVerificationResultSchema,
|
|
332
|
+
})),
|
|
333
|
+
list_unknowns: lifecycleResultOf(z.array(residualUnknownSchema)),
|
|
334
|
+
record_unknown: lifecycleResultOf(residualUnknownSchema),
|
|
335
|
+
update_unknown: lifecycleResultOf(residualUnknownSchema),
|
|
336
|
+
verify_unknown_resolution: lifecycleResultOf(z.object({
|
|
337
|
+
valid: z.boolean(),
|
|
338
|
+
truthVerified: z.boolean(),
|
|
339
|
+
unknown: residualUnknownSchema,
|
|
266
340
|
})),
|
|
267
341
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Exact example inputs for residual-unknown lifecycle contracts. */
|
|
2
|
+
export const UNKNOWN_CONTRACT_EXAMPLES = {
|
|
3
|
+
list_unknowns: {},
|
|
4
|
+
record_unknown: {
|
|
5
|
+
approved: true,
|
|
6
|
+
question: "Does this branch require an unavailable external service?",
|
|
7
|
+
severity: "medium",
|
|
8
|
+
domain: "protocol",
|
|
9
|
+
required_authority: "controlled-replay",
|
|
10
|
+
required_confidence: "observed",
|
|
11
|
+
required_environment: null,
|
|
12
|
+
recommended_probes: [
|
|
13
|
+
{ operation: "capture_process_scenario", rationale: "Replay branch." },
|
|
14
|
+
],
|
|
15
|
+
relationships: [],
|
|
16
|
+
},
|
|
17
|
+
update_unknown: {
|
|
18
|
+
approved: true,
|
|
19
|
+
unknown_id: `unk_${"0".repeat(64)}`,
|
|
20
|
+
expected_revision: 1,
|
|
21
|
+
status: "investigating",
|
|
22
|
+
severity: "medium",
|
|
23
|
+
supporting_evidence_ids: [],
|
|
24
|
+
contradicting_evidence_ids: [],
|
|
25
|
+
required_authority: "controlled-replay",
|
|
26
|
+
required_confidence: "observed",
|
|
27
|
+
required_environment: null,
|
|
28
|
+
recommended_probes: [],
|
|
29
|
+
relationships: [],
|
|
30
|
+
resolution: null,
|
|
31
|
+
},
|
|
32
|
+
verify_unknown_resolution: { unknown_id: `unk_${"0".repeat(64)}` },
|
|
33
|
+
};
|