Files
rea/scripts/lib/catalog-builders.mjs
c1fd496e6a feat(kuna): serve procedures, pseudocode and assembly from Kuna exports (#3854)
* feat(kuna): serve procedures, pseudocode and assembly from Kuna exports

Register Kuna as a bring-your-own deep-analysis provider. It answers
list_documents, list_procedures, search_procedures, procedure_address,
address_name, procedure_pseudo_code, procedure_assembly and
analyze_function (and batch_decompile through composition) from the
verified project export. While a cold export builds in the background,
pseudocode comes from a single-function `kuna decompile` run on a private
copy and says so; assembly and data names wait for the export.

Each answer records its source and file span in raw_result, limitations
are scoped per operation, and the session-owned export is stopped on
close unless another session still waits for it. The literal/regex
procedure search predicate moves from the IDA runner into a shared
domain helper. `npm run verify:kuna` adds a real-provider lane with an
optional Ghidra comparison.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): refuse a Ghidra comparison without caller-built targets

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): retry owned cleanup and keep answers bound to the opened Kuna

- Re-verify the Kuna executable's digest before every launch (memoized by
  file identity), so no answer names one build while another produced it.
- Keep the process supervisor of an unproven stop, and retry it on every
  later close in both the session and the cache; a failed close keeps
  failing until the stop is proven instead of reporting success.
- Report an abandoned export's retained cleanup from session close.
- Forget and rebuild an adopted export whose files another REA process
  evicted, instead of failing every later read.
- Close a private copy whose setup failed, and retry failed removals.
- Let the session own the shared quick inventory; each caller only stops
  waiting when it cancels.
- Report a failed export as `failed` with its reason.
- Lane: release the MCP client when open_binary rejects a target, and accept
  either valid source for the first answer.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): drain session requests before releasing workspaces

Combine caller cancellation with session shutdown and wait for active reads before releasing their process supervisors or private inputs. Retain unproven cleanup for later close attempts. Measure UTF-8 reply bytes without implying model token usage.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* perf(kuna): keep document metadata reads free of target analysis

Listing the selected path no longer initializes the project cache or starts an export that the request never consumes.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(kuna): reject verification matrices with missing functions

Validate requested matrix coverage before building the fixture or opening providers. Empty matrices, unmatched names and partially matched function sets must fail instead of reporting an unperformed comparison as successful.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(kuna): retain component provenance in function dossiers

Record each source facet as one file span and preserve both pseudocode and assembly digests/ranges in combined reports. Keep direct text source fields compatible and verify every combined span through the real MCP lane.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(kuna): keep retired file cleanup bound to its export

Retain the export key with each failed file removal so a cancelled session reports and retries its own stopped resources. Preserve that association across sweeping and process-cleanup recovery without attributing unrelated cache failures to the session.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* docs(kuna): document the Kuna provider and list it in the catalog (#3855)

* docs(kuna): document the Kuna provider and list it in the catalog

Add the Kuna provider guide (configuration, answered tools, cold and warm
paths, provenance, cache ownership, verification lane), link it from the
README and its translations, the CLI, installation and index pages and the
site navigation, describe `npm run verify:kuna` in the testing guide, credit
Kuna in THIRD_PARTY_NOTICES, teach the agent skill when to select Kuna, and
register its identity and operations in the generated product catalog.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(kuna): state the Linux cache fallback, raw_result scope and comparison inputs

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(kuna): note that cache pins hold within one REA process

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(kuna): describe retired staging and retried cleanup

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): supervise regex and bind reads to one export

Share a cancellable ECMAScript matching worker with Hopper while preserving its error contract. Bind C and assembly indexes to immutable project readers, retry whole dossiers after eviction, and enforce the existing buffered-text capacity before allocating cached spans. Document the verified Linux scope and keep complete exports accessible.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 00:53:26 -07:00

467 lines
15 KiB
JavaScript

import { names } from "./catalog-core.mjs";
/** Build tool-family counts and sorted tool-name lists. */
export const toolFamilyCatalog = (sources) => {
const families = [
{
id: "direct",
surface: "official-proxy",
contracts: sources.officialContracts.OFFICIAL_TOOL_CONTRACTS,
},
{
id: "enhanced",
surface: "enhanced",
contracts: sources.enhancedContracts.ENHANCED_TOOL_CONTRACTS,
},
{
id: "native",
surface: "native-provider",
contracts: [
...sources.nativeContracts.NATIVE_TOOL_CONTRACTS,
...sources.binaryDiagnosticsContracts.BINARY_DIAGNOSTICS_TOOL_CONTRACTS,
...sources.cutterContracts.CUTTER_TOOL_CONTRACTS,
...sources.reverseEngineeringContracts
.REVERSE_ENGINEERING_TOOL_CONTRACTS,
...sources.gdbContracts.GDB_TOOL_CONTRACTS,
...sources.rizinDebugContracts.RIZIN_DEBUG_TOOL_CONTRACTS,
],
},
{
id: "artifact",
surface: "artifact-provider",
contracts: [
...sources.artifactContracts.ARTIFACT_TOOL_CONTRACTS,
...sources.evmContracts.EVM_TOOL_CONTRACTS,
...sources.goContracts.GO_TOOL_CONTRACTS,
...sources.rustContracts.RUST_TOOL_CONTRACTS,
...sources.wasmContracts.WASM_TOOL_CONTRACTS,
...sources.sqliteContracts.SQLITE_TOOL_CONTRACTS,
...sources.captureContracts.CAPTURE_TOOL_CONTRACTS,
...sources.signalContracts.SIGNAL_TOOL_CONTRACTS,
],
},
{
id: "managed",
surface: "managed",
contracts: [
...sources.managedContracts.MANAGED_TOOL_CONTRACTS,
...sources.managedWorkflowContracts.MANAGED_WORKFLOW_TOOL_CONTRACTS,
],
},
{
id: "firmware",
surface: "firmware-provider",
contracts: sources.firmwareContracts.FIRMWARE_TOOL_CONTRACTS,
},
{
id: "android",
surface: "android-provider",
contracts: sources.androidContracts.ANDROID_TOOL_CONTRACTS,
},
{
id: "pcap",
surface: "pcap-provider",
contracts: sources.pcapContracts.PCAP_TOOL_CONTRACTS,
},
{
id: "flutter",
surface: "flutter-provider",
contracts: sources.flutterContracts.FLUTTER_TOOL_CONTRACTS,
},
{
id: "frida",
surface: "instrumentation-provider",
contracts: sources.fridaContracts.FRIDA_TOOL_CONTRACTS,
},
{
id: "apktool",
surface: "apktool-provider",
contracts: sources.apktoolContracts.APKTOOL_TOOL_CONTRACTS,
},
{
id: "jeb",
surface: "jeb-provider",
contracts: sources.jebContracts.JEB_TOOL_CONTRACTS,
},
{
id: "adb",
surface: "adb-provider",
contracts: sources.adbContracts.ADB_TOOL_CONTRACTS,
},
{
id: "browser",
surface: "browser-provider",
contracts: [
...sources.browserContracts.BROWSER_TOOL_CONTRACTS,
...sources.webRuntimeContracts.WEB_RUNTIME_TOOL_CONTRACTS,
...sources.browserScenarioContracts.BROWSER_SCENARIO_TOOL_CONTRACTS,
...sources.webNetworkCaptureContracts
.WEB_NETWORK_CAPTURE_TOOL_CONTRACTS,
],
},
{
id: "electron",
surface: "electron-provider",
contracts: sources.electronContracts.ELECTRON_TOOL_CONTRACTS,
},
{
id: "javascript-runtime",
surface: "runtime-provider",
contracts:
sources.javascriptRuntimeObservationContracts
.JAVASCRIPT_RUNTIME_OBSERVATION_TOOL_CONTRACTS,
},
{
id: "application",
surface: "application-workflow",
contracts: [
...sources.applicationContracts.APPLICATION_TOOL_CONTRACTS,
...sources.analysisViewContracts.ANALYSIS_VIEW_TOOL_CONTRACTS,
...sources.webScriptContracts.WEB_SCRIPT_TOOL_CONTRACTS,
...sources.javascriptRecoveryContracts
.JAVASCRIPT_RECOVERY_TOOL_CONTRACTS,
],
},
{
id: "session",
surface: "session",
contracts: sources.sessionContracts.SESSION_TOOL_CONTRACTS,
},
].map(({ id, surface, contracts }) => ({
id,
surface,
count: contracts.length,
tools: names(contracts),
}));
const familyTotal = families.reduce(
(total, family) => total + family.count,
0,
);
if (
familyTotal !== sources.toolContracts.TOOL_CONTRACTS.length ||
familyTotal !== sources.catalogIdentity.CATALOG_IDENTITY.counts.mcp_tools
)
throw new Error("Tool family inventory does not match TOOL_CONTRACTS");
return { total: familyTotal, families };
};
/** Build provider identities with their sorted capability names. */
export const providerCatalog = (sources) => {
const contractsByName = new Map(
sources.toolContracts.TOOL_CONTRACTS.map((contract) => [
contract.name,
contract,
]),
);
const declaredContracts = (providerId, operations) =>
operations.map((operation) => {
const contract = contractsByName.get(operation);
if (contract === undefined)
throw new TypeError(
`Provider ${providerId} declares an unknown analyst operation: ${operation}`,
);
return contract;
});
const {
applicationContracts,
reconciliationContracts,
observationContracts,
activeContracts,
fuseContracts,
} = electronContractSlices(sources);
return [
{
identity: sources.cutterProvider.CUTTER_PROVIDER_IDENTITY,
contracts: sources.cutterContracts.CUTTER_TOOL_CONTRACTS,
},
{
identity: sources.objdumpProvider.OBJDUMP_PROVIDER_IDENTITY,
contracts:
sources.reverseEngineeringContracts.REVERSE_ENGINEERING_TOOL_CONTRACTS.filter(
({ name }) => name === "inspect_with_objdump",
),
},
{
identity: sources.rizinProvider.RIZIN_PROVIDER_IDENTITY,
contracts:
sources.reverseEngineeringContracts.REVERSE_ENGINEERING_TOOL_CONTRACTS.filter(
({ name }) => name === "execute_rizin_command",
),
},
{
identity: sources.gdbProvider.GDB_PROVIDER_IDENTITY,
contracts: sources.gdbContracts.GDB_TOOL_CONTRACTS,
},
{
identity: sources.rizinDebugProvider.RIZIN_DEBUGGER_PROVIDER_IDENTITY,
contracts: sources.rizinDebugContracts.RIZIN_DEBUG_TOOL_CONTRACTS,
},
{
identity: sources.fridaProvider.FRIDA_PROVIDER_IDENTITY,
contracts: sources.fridaContracts.FRIDA_TOOL_CONTRACTS,
},
{
identity: sources.hopperProvider.HOPPER_PROVIDER_IDENTITY,
contracts: declaredContracts(
"hopper",
sources.hopperProvider.HOPPER_OPERATIONS,
),
},
{
identity: sources.ghidraProvider.GHIDRA_PROVIDER_IDENTITY,
contracts: declaredContracts(
"ghidra",
sources.ghidraProvider.GHIDRA_OPERATIONS,
),
},
{
identity: sources.idaProvider.IDA_PROVIDER_IDENTITY,
contracts: declaredContracts("ida", sources.idaProvider.IDA_OPERATIONS),
},
{
identity: sources.kunaProvider.KUNA_PROVIDER_IDENTITY,
contracts: declaredContracts(
"kuna",
sources.kunaProvider.KUNA_OPERATIONS,
),
},
{
identity: sources.nativeProvider.NATIVE_MACOS_PROVIDER_IDENTITY,
contracts: sources.nativeContracts.NATIVE_TOOL_CONTRACTS,
},
{
identity: sources.pwntoolsProvider.PWNTOOLS_PROVIDER_IDENTITY,
contracts:
sources.binaryDiagnosticsContracts.BINARY_DIAGNOSTICS_TOOL_CONTRACTS.filter(
({ name }) => name !== "inspect_pe_resources",
),
},
{
identity: sources.artifactProviders.PE_RESOURCES_PROVIDER,
contracts:
sources.binaryDiagnosticsContracts.BINARY_DIAGNOSTICS_TOOL_CONTRACTS.filter(
({ name }) => name === "inspect_pe_resources",
),
},
{
identity: sources.captureProvider.TSHARK_PROVIDER_IDENTITY,
contracts: sources.captureContracts.CAPTURE_TOOL_CONTRACTS,
},
{
identity: sources.signalProvider.SIGROK_PROVIDER_IDENTITY,
contracts: sources.signalContracts.SIGNAL_TOOL_CONTRACTS,
},
{
identity: sources.wasmProvider.WABT_PROVIDER_IDENTITY,
contracts: sources.wasmContracts.WASM_TOOL_CONTRACTS.filter(
({ kind }) => kind === "artifact-provider",
),
},
{
identity: sources.evmProvider.EVMOLE_PROVIDER_IDENTITY,
contracts: sources.evmContracts.EVM_TOOL_CONTRACTS,
},
{
identity: sources.goProvider.GO_BINARY_PROVIDER_IDENTITY,
contracts: sources.goContracts.GO_TOOL_CONTRACTS,
},
{
identity: sources.rustProvider.RUST_BINARY_PROVIDER_IDENTITY,
contracts: sources.rustContracts.RUST_TOOL_CONTRACTS,
},
{
identity: sources.electronFuseProvider.ELECTRON_FUSE_PROVIDER_IDENTITY,
contracts: fuseContracts,
},
{
identity: sources.sqliteDomain.SQLITE_PROVIDER_IDENTITY,
contracts: sources.sqliteContracts.SQLITE_TOOL_CONTRACTS,
},
{
identity: sources.artifactProviders.ARTIFACT_GRAPH_PROVIDER,
contracts: sources.artifactContracts.ARTIFACT_TOOL_CONTRACTS,
},
{
identity: sources.artifactProviders.MANAGED_STATIC_PROVIDER,
contracts: sources.managedContracts.MANAGED_TOOL_CONTRACTS,
},
{
identity: sources.artifactProviders.MANAGED_WORKFLOW_PROVIDER,
contracts:
sources.managedWorkflowContracts.MANAGED_WORKFLOW_TOOL_CONTRACTS,
},
{
identity: sources.firmwareProvider.BINWALK_PROVIDER_IDENTITY,
contracts: sources.firmwareContracts.FIRMWARE_TOOL_CONTRACTS.filter(
({ name }) => name === "inspect_firmware_regions",
),
},
{
identity: sources.firmwareProvider.UNBLOB_PROVIDER_IDENTITY,
contracts: sources.firmwareContracts.FIRMWARE_TOOL_CONTRACTS.filter(
({ name }) => name === "extract_firmware",
),
},
{
identity: sources.androidProvider.JADX_PROVIDER_IDENTITY,
contracts: sources.androidContracts.ANDROID_TOOL_CONTRACTS,
},
{
identity: sources.pcapProvider.PCAP_PROVIDER_IDENTITY,
contracts: sources.pcapContracts.PCAP_TOOL_CONTRACTS,
},
{
identity: sources.flutterProvider.FLUTTER_PROVIDER_IDENTITY,
contracts: sources.flutterContracts.FLUTTER_TOOL_CONTRACTS,
},
{
identity: sources.apktoolProvider.APKTOOL_PROVIDER_IDENTITY,
contracts: sources.apktoolContracts.APKTOOL_TOOL_CONTRACTS,
},
{
identity: sources.jebProvider.JEB_PROVIDER_IDENTITY,
contracts: sources.jebContracts.JEB_TOOL_CONTRACTS,
},
{
identity: sources.adbProvider.ADB_PROVIDER_IDENTITY,
contracts: sources.adbContracts.ADB_TOOL_CONTRACTS,
},
{
identity: sources.webNetworkCaptureProvider.HAR_CAPTURE_PROVIDER_IDENTITY,
contracts:
sources.webNetworkCaptureContracts.WEB_NETWORK_CAPTURE_TOOL_CONTRACTS,
},
{
identity: sources.webNetworkCaptureProvider.SAZ_CAPTURE_PROVIDER_IDENTITY,
contracts:
sources.webNetworkCaptureContracts.WEB_NETWORK_CAPTURE_TOOL_CONTRACTS,
},
{
identity:
sources.webNetworkCaptureProvider.MITMPROXY_CAPTURE_PROVIDER_IDENTITY,
contracts:
sources.webNetworkCaptureContracts.WEB_NETWORK_CAPTURE_TOOL_CONTRACTS,
},
{
identity: sources.browserIdentities.CDP_BROWSER_PROVIDER_IDENTITY,
contracts: [
...sources.browserContracts.BROWSER_TOOL_CONTRACTS,
...sources.webRuntimeContracts.WEB_RUNTIME_TOOL_CONTRACTS,
],
},
{
identity:
sources.browserIdentities.PLAYWRIGHT_BROWSER_SCENARIO_PROVIDER_IDENTITY,
contracts:
sources.browserScenarioContracts.BROWSER_SCENARIO_TOOL_CONTRACTS,
},
{
identity: sources.browserIdentities.CDP_ELECTRON_PROVIDER_IDENTITY,
contracts: observationContracts,
},
{
identity:
sources.browserIdentities.PLAYWRIGHT_ELECTRON_ACTIVE_PROVIDER_IDENTITY,
contracts: activeContracts,
},
{
identity: sources.v8InspectorProvider.V8_INSPECTOR_PROVIDER_IDENTITY,
contracts:
sources.javascriptRuntimeObservationContracts
.JAVASCRIPT_RUNTIME_OBSERVATION_TOOL_CONTRACTS,
},
{
identity: sources.artifactProviders.JAVASCRIPT_APPLICATION_PROVIDER,
contracts: applicationContracts,
},
{
identity:
sources.artifactProviders.JAVASCRIPT_RUNTIME_RECONCILIATION_PROVIDER,
contracts: reconciliationContracts,
},
{
identity:
sources.artifactProviders.JAVASCRIPT_APPLICATION_WORKFLOW_PROVIDER,
contracts: sources.applicationContracts.APPLICATION_TOOL_CONTRACTS.filter(
({ name }) =>
name !== "project_android_application_graph" &&
name !== "project_apple_application_graph",
),
},
{
identity: sources.artifactProviders.ANALYSIS_VIEW_PROVIDER,
contracts: sources.analysisViewContracts.ANALYSIS_VIEW_TOOL_CONTRACTS,
},
{
identity: sources.artifactProviders.ANDROID_APPLICATION_PROVIDER,
contracts: sources.applicationContracts.APPLICATION_TOOL_CONTRACTS.filter(
({ name }) => name === "project_android_application_graph",
),
},
{
identity: sources.artifactProviders.APPLE_APPLICATION_PROVIDER,
contracts: sources.applicationContracts.APPLICATION_TOOL_CONTRACTS.filter(
({ name }) => name === "project_apple_application_graph",
),
},
{
identity: sources.artifactProviders.WEB_SCRIPT_EXPORT_PROVIDER,
contracts: sources.webScriptContracts.WEB_SCRIPT_TOOL_CONTRACTS,
},
{
identity: sources.javascriptRecoveryProvider.WAKARU_PROVIDER_IDENTITY,
contracts:
sources.javascriptRecoveryContracts.JAVASCRIPT_RECOVERY_TOOL_CONTRACTS,
},
]
.map(({ identity, contracts }) => ({
id: identity.id,
name: identity.name,
version: identity.version,
capabilities: names(contracts),
}))
.sort((left, right) => left.id.localeCompare(right.id));
};
const electronContractSlices = (sources) => {
const applicationContracts =
sources.electronContracts.ELECTRON_TOOL_CONTRACTS.filter(
({ name }) => name === "analyze_javascript_application",
);
const reconciliationContracts =
sources.electronContracts.ELECTRON_TOOL_CONTRACTS.filter(
({ name }) => name === "reconcile_javascript_runtime",
);
const observationContracts =
sources.electronContracts.ELECTRON_TOOL_CONTRACTS.filter(
({ name }) =>
name !== "analyze_javascript_application" &&
name !== "reconcile_javascript_runtime" &&
name !== "capture_electron_scenario" &&
name !== "inspect_electron_fuses",
);
const activeContracts =
sources.electronContracts.ELECTRON_TOOL_CONTRACTS.filter(
({ name }) => name === "capture_electron_scenario",
);
const fuseContracts =
sources.electronContracts.ELECTRON_TOOL_CONTRACTS.filter(
({ name }) => name === "inspect_electron_fuses",
);
if (
applicationContracts.length !== 1 ||
reconciliationContracts.length !== 1 ||
observationContracts.length !== 2 ||
activeContracts.length !== 1 ||
fuseContracts.length !== 1
)
throw new Error("Electron provider capability ownership drifted");
return {
applicationContracts,
reconciliationContracts,
observationContracts,
activeContracts,
fuseContracts,
};
};