Files
rea/src/application/CapabilityInventory.ts
T
2026-07-26 15:02:58 +00:00

389 lines
12 KiB
TypeScript

import { z } from "zod";
import type { ClientFeatureAvailability } from "../contracts/toolOutputSchemaPrimitives.js";
import type { ToolKind } from "../contracts/toolContractTypes.js";
import type { JsonValue } from "../domain/jsonValue.js";
import { GENERATED_MCP_TOOL_CATALOG } from "../generatedMcpToolCatalog.js";
const statusSchema = z.object({
open: z.boolean(),
kind: z.enum(["executable", "database", "archive", "artifact"]).optional(),
format: z.string().optional(),
capabilities: z.array(
z.object({
operation: z.string(),
available: z.boolean(),
reason: z.string().nullable(),
availability_code: z.string().nullable().optional(),
}),
),
});
type ToolAvailabilityReason =
| "available"
| "client_capability_missing"
| "target_required"
| "provider_missing"
| "provider_unavailable"
| "target_unsupported"
| "unsupported_host"
| "policy_disabled";
type ProviderDescriptor = {
readonly available: boolean;
readonly reason: string | null;
readonly availability_code?: string | null;
};
export type AvailabilityPolicy = {
readonly processCaptureEnabled: boolean;
readonly evidenceFileRoots: number;
readonly investigationInputRoots: number;
readonly browserObservationEnabled?: boolean;
readonly browserScenarioEnabled?: boolean;
readonly electronObservationEnabled?: boolean;
readonly v8InspectorObservationEnabled?: boolean;
readonly javascriptReplayEnabled?: boolean;
readonly managedRuntimeEnabled?: boolean;
};
type ClientFeatureName = keyof ClientFeatureAvailability;
const NO_CLIENT_FEATURES: ClientFeatureAvailability = {
elicitation_form: false,
elicitation_url: false,
roots: false,
sampling: false,
};
const CLIENT_FEATURE_REQUIREMENTS: Readonly<
Record<
string,
{
readonly required: readonly ClientFeatureName[];
readonly optional: readonly ClientFeatureName[];
}
>
> = {
capture_process_scenario: {
required: [],
optional: ["elicitation_form"],
},
};
type Availability = {
readonly reason: ToolAvailabilityReason;
readonly remediation: string | null;
};
type AvailabilityContext = {
readonly name: string;
readonly kind: ToolKind;
readonly targetOpen: boolean;
readonly targetKind:
| "executable"
| "database"
| "archive"
| "artifact"
| undefined;
readonly descriptors: ReadonlyMap<string, ProviderDescriptor>;
readonly policy: AvailabilityPolicy;
};
const ENHANCED_REQUIREMENTS: Readonly<Record<string, readonly string[]>> = {
swift_classes: ["list_procedures"],
get_objc_classes: ["list_names"],
get_objc_protocols: ["list_names"],
batch_decompile: ["procedure_pseudo_code"],
get_call_graph: ["procedure_callees", "procedure_callers"],
analyze_swift_types: ["list_procedures"],
find_xrefs_to_name: ["list_names", "xrefs"],
binary_overview: [
"list_segments",
"list_documents",
"list_procedures",
"list_strings",
],
analyze_function: ["analyze_function"],
inspect_native_api: ["analyze_function"],
trace_feature: [
"list_strings",
"list_procedures",
"xrefs",
"resolve_containing_procedure",
],
};
/** Build stable per-operation availability for discovery and tool visibility. */
export const buildCapabilityInventory = (
sessionStatus: JsonValue,
policy: AvailabilityPolicy,
clientFeatures: ClientFeatureAvailability = NO_CLIENT_FEATURES,
) => {
const status = statusSchema.parse(sessionStatus);
const descriptors = new Map<string, ProviderDescriptor>(
status.capabilities.map((descriptor) => [
descriptor.operation,
{
available: descriptor.available,
reason: descriptor.reason,
availability_code: descriptor.availability_code ?? null,
},
]),
);
return GENERATED_MCP_TOOL_CATALOG.map((contract) => {
const availability = availabilityFor({
name: contract.name,
kind: contract.kind,
targetOpen: status.open,
targetKind: status.kind,
descriptors,
policy,
});
const clientRequirements = clientRequirementsFor(
contract.name,
clientFeatures,
);
const clientBlocked = clientRequirements.missing_required.length > 0;
return {
name: contract.name,
surface: contract.kind,
available: availability.reason === "available" && !clientBlocked,
reason: clientBlocked
? ("client_capability_missing" as const)
: availability.reason,
remediation: clientBlocked
? `Use an MCP client that supports: ${clientRequirements.missing_required.join(", ")}.`
: availability.remediation,
client_requirements: clientRequirements,
effects: { ...contract.effects },
annotations: {
read_only: contract.annotations.readOnlyHint ?? false,
destructive: contract.annotations.destructiveHint ?? false,
idempotent: contract.annotations.idempotentHint ?? false,
open_world: contract.annotations.openWorldHint ?? true,
},
};
}).sort((left, right) => left.name.localeCompare(right.name));
};
const clientRequirementsFor = (
name: string,
clientFeatures: ClientFeatureAvailability,
) => {
const requirements = CLIENT_FEATURE_REQUIREMENTS[name] ?? {
required: [],
optional: [],
};
return {
required: [...requirements.required],
optional: [...requirements.optional],
missing_required: requirements.required.filter(
(feature) => !clientFeatures[feature],
),
missing_optional: requirements.optional.filter(
(feature) => !clientFeatures[feature],
),
};
};
const availabilityFor = (context: AvailabilityContext): Availability => {
const javascriptApplication = javascriptApplicationAvailability(context);
if (javascriptApplication !== null) return javascriptApplication;
const policyDecision = policyAvailability(context);
if (policyDecision !== null) return policyDecision;
const targetDecision = targetAvailability(context);
if (targetDecision !== null) return targetDecision;
return providerAvailability(context);
};
const javascriptApplicationAvailability = ({
name,
policy,
}: AvailabilityContext): Availability | null => {
if (name === "analyze_javascript_application")
return policy.investigationInputRoots === 0
? {
reason: "policy_disabled",
remediation:
"Configure an exact REA_INVESTIGATION_INPUT_ROOTS_JSON root in the MCP registration, then restart the registered MCP server or client.",
}
: { reason: "available", remediation: null };
return name === "reconcile_javascript_runtime"
? { reason: "available", remediation: null }
: null;
};
const policyAvailability = ({
name,
kind,
policy,
}: AvailabilityContext): Availability | null => {
const browser = browserPolicyAvailability(name, kind, policy);
if (browser !== null) return browser;
if (name === "capture_process_scenario" && !policy.processCaptureEnabled)
return {
reason: "policy_disabled",
remediation:
"Enable or grant process_capture within the administrator ceiling.",
};
if (name === "run_controlled_replay" && !policy.javascriptReplayEnabled)
return {
reason: "policy_disabled",
remediation:
"Enable javascript_replay with exact source roots and sandbox executables.",
};
if (name === "run_controlled_replay")
return { reason: "available", remediation: null };
if (
name === "plan_managed_runtime_correlation" &&
!policy.managedRuntimeEnabled
)
return {
reason: "policy_disabled",
remediation:
"Enable managed_runtime with exact artifact roots and a runtime executable.",
};
if (name === "plan_managed_runtime_correlation")
return { reason: "available", remediation: null };
if (kind === "application") return { reason: "available", remediation: null };
if (name === "import_evidence_bundle" && policy.evidenceFileRoots === 0)
return {
reason: "policy_disabled",
remediation: "Configure or grant an evidence_read root.",
};
if (kind === "electron-provider")
return policy.electronObservationEnabled === true
? { reason: "available", remediation: null }
: {
reason: "policy_disabled",
remediation:
"Enable Electron observation and configure a loopback CDP endpoint and canonical file roots.",
};
if (kind === "runtime-provider")
return policy.v8InspectorObservationEnabled === true
? { reason: "available", remediation: null }
: {
reason: "policy_disabled",
remediation:
"Enable V8 Inspector observation and configure exact loopback endpoints plus canonical file roots or exact origins.",
};
if (kind === "session") return { reason: "available", remediation: null };
return null;
};
const browserPolicyAvailability = (
name: string,
kind: ToolKind,
policy: AvailabilityPolicy,
): Availability | null => {
if (name === "capture_browser_scenario")
return policy.browserScenarioEnabled === true
? { reason: "available", remediation: null }
: {
reason: "policy_disabled",
remediation:
"Enable browser scenarios and configure exact executable roots or loopback CDP endpoints, page origins, and environment secret names.",
};
if (kind !== "browser-provider") return null;
return policy.browserObservationEnabled === true
? { reason: "available", remediation: null }
: {
reason: "policy_disabled",
remediation:
"Enable browser observation and configure exact CDP endpoint and page origins.",
};
};
const targetAvailability = ({
kind,
targetKind,
targetOpen,
}: AvailabilityContext): Availability | null => {
if (!targetOpen && (kind === "official-proxy" || kind === "enhanced"))
return {
reason: "target_required",
remediation: "Call open_binary with a supported local target.",
};
if (
targetKind !== undefined &&
targetKind !== "executable" &&
targetKind !== "database" &&
(kind === "official-proxy" || kind === "enhanced")
)
return {
reason: "target_unsupported",
remediation:
"Inventory or extract a native executable, then call open_binary on that executable.",
};
return null;
};
const providerAvailability = ({
descriptors,
kind,
name,
}: AvailabilityContext): Availability => {
if (kind === "enhanced") return composedAvailability(name, descriptors);
const descriptor = descriptors.get(name);
if (
descriptor === undefined &&
(kind === "artifact-provider" ||
kind === "managed-provider" ||
kind === "native-provider")
)
return { reason: "available", remediation: null };
if (descriptor === undefined)
return {
reason: "provider_missing",
remediation:
"Install or configure a provider that declares this operation.",
};
if (
!descriptor.available &&
descriptor.availability_code === "unsupported_host"
)
return {
reason: "unsupported_host",
remediation: descriptor.reason,
};
return descriptor.available
? { reason: "available", remediation: null }
: {
reason: "provider_unavailable",
remediation:
descriptor.reason ?? "Choose another target or configured provider.",
};
};
const composedAvailability = (
name: string,
descriptors: ReadonlyMap<string, ProviderDescriptor>,
): {
readonly reason: ToolAvailabilityReason;
readonly remediation: string | null;
} => {
const requirements = ENHANCED_REQUIREMENTS[name];
if (requirements === undefined)
return {
reason: "provider_missing",
remediation: "No provider composition is declared for this operation.",
};
const missing = requirements.find((operation) => !descriptors.has(operation));
if (missing !== undefined)
return {
reason: "provider_missing",
remediation: `Configure a provider for required operation ${missing}.`,
};
const unavailable = requirements
.map((operation) => descriptors.get(operation))
.find((descriptor) => descriptor?.available === false);
if (unavailable !== undefined)
return {
reason:
unavailable.availability_code === "unsupported_host"
? "unsupported_host"
: "provider_unavailable",
remediation:
unavailable.reason ?? "Choose another target or configured provider.",
};
return { reason: "available", remediation: null };
};