Files
reverse-engineering-anything/src/contracts/toolOutputSchemaPrimitives.ts
T
morluto b7ebccf8d3 fix(navigation): reject malformed containing-procedure results
Move the unchanged resolution contract into its domain owner and use it when
building navigation context. Preserve provider failures instead of guessing
absence from error text or malformed result shapes. Return null only for an
explicit valid negative resolution.

Remove the obsolete error-message matcher and partial name parser. Cover four
malformed result shapes, original error identity, valid positive/negative facts
and the typed MCP unreadable-output response. Original source failed six cases.

Validation: navigation source/MCP and advertised schema checks passed. Compact
and full generated catalogs are byte-identical to the parent. Static, docs,
discovery and duplication checks passed.
2026-10-24 04:18:07 +08:00

635 lines
18 KiB
TypeScript

import { procedureInfoSchema } from "../domain/native/procedureInfo.js";
import { z } from "zod";
import { functionDossierSchema } from "../domain/hopperValues.js";
import { analysisProfileSchema } from "../domain/analysisProfile.js";
import {
evidenceSchema,
validateAnalysisProfileProvider,
} from "../domain/evidence.js";
import { jsonValueSchema } from "../domain/jsonValue.js";
import {
PROVIDER_REJECTION_CODES,
type ProviderRejectionCode,
} from "./providerSelection.js";
import {
PROVIDER_FAILURE_STAGES,
PROVIDER_OPERATION_STATES,
PROVIDER_RETRY_ACTIONS,
} from "../domain/providerOperationHealth.js";
import { analysisErrorProjectionSchema } from "./errorSchemas.js";
import { relationshipCoverageSchema } from "../domain/relationshipCoverage.js";
import { ZIP_PACKAGE_FORMATS } from "../domain/zipPackageFormat.js";
import { objcClassImportIdentitySchema } from "../domain/native/objcSwiftMetadata.js";
/** Specialize the single normalized result inside a complete Evidence record. */
export const evidenceResultOf = <Schema extends z.ZodType>(schema: Schema) =>
z
.strictObject({ ...evidenceSchema.shape, normalized_result: schema })
.superRefine(validateAnalysisProfileProvider);
/** Wrap a lifecycle result while preserving its exact schema type. */
export const lifecycleResultOf = <Schema extends z.ZodType>(schema: Schema) =>
z.object({ result: schema });
/** Resolve a required named output schema or reject contract drift. */
export const requireOutputSchema = <
Schemas extends Readonly<Record<string, z.ZodObject>>,
Name extends keyof Schemas & string,
>(
schemas: Schemas,
name: Name,
): Schemas[Name] => {
const schema = schemas[name];
if (schema === undefined)
throw new Error(`Missing output schema for ${name}`);
return schema;
};
export const targetFormatSchema = z.enum([
"hopper",
"analysis-database",
"mach-o",
"mach-o-universal",
"elf",
"pe",
"dos-mz",
"dos-com",
"raw-binary",
...ZIP_PACKAGE_FORMATS,
"asar",
"godot-pck",
"dmg",
"pkg",
"plist",
"javascript",
"source-map",
]);
export const targetKindSchema = z.enum([
"executable",
"database",
"archive",
"artifact",
]);
const providerCapabilityFacts = {
operation: z.string(),
effects: z.object({
mutates_artifact: z.boolean(),
launches_process: z.boolean(),
may_show_ui: z.boolean(),
may_access_network: z.boolean(),
may_write_filesystem: z.boolean(),
changes_permissions: z.boolean(),
requires_root: z.boolean(),
}),
limitations: z.array(z.string()),
};
export const providerCapability = z.discriminatedUnion("available", [
z.object({
...providerCapabilityFacts,
available: z.literal(true),
reason: z.null(),
availability_code: z.null(),
}),
z.object({
...providerCapabilityFacts,
available: z.literal(false),
reason: z.string().nullable(),
availability_code: z.enum(PROVIDER_REJECTION_CODES).nullable(),
}),
]);
export type ProviderCapability = z.output<typeof providerCapability>;
/** Listed effects are true; all other members of this closed vocabulary are false. */
export const PROVIDER_EFFECT_NAMES = Object.keys(
providerCapabilityFacts.effects.shape,
) as (keyof z.output<typeof providerCapabilityFacts.effects>)[];
export const providerIdentity = z.object({
id: z.string(),
name: z.string(),
version: z.string().nullable(),
});
const providerCapabilityGroupFacts = {
operations: z.array(z.string().min(1)).min(1),
effects: z.array(providerCapabilityFacts.effects.keyof()),
limitations: z.array(z.string()),
};
const providerCapabilityGroup = z.discriminatedUnion("available", [
z.strictObject({
...providerCapabilityGroupFacts,
available: z.literal(true),
reason: z.null(),
availability_code: z.null(),
}),
z.strictObject({
...providerCapabilityGroupFacts,
available: z.literal(false),
reason: z.string().nullable(),
availability_code: z.enum(PROVIDER_REJECTION_CODES).nullable(),
}),
]);
/** Shared limitations precede each group's suffix, preserving exact source order. */
export const providerCapabilitySet = z.strictObject({
provider: providerIdentity,
shared_limitations: z.array(z.string()),
groups: z.array(providerCapabilityGroup),
});
export type ProviderCapabilitySet = z.output<typeof providerCapabilitySet>;
/** Decode one status scope without losing provider identity or limitation order. */
export const expandProviderCapabilities = (
sets: readonly ProviderCapabilitySet[],
): readonly (ProviderCapability & {
readonly provider: z.output<typeof providerIdentity>;
})[] =>
sets.flatMap((set) =>
set.groups.flatMap((group) =>
group.operations.map((operation) => ({
...providerCapability.parse({
operation,
available: group.available,
reason: group.reason,
availability_code: group.availability_code,
effects: Object.fromEntries(
PROVIDER_EFFECT_NAMES.map((name) => [
name,
group.effects.includes(name),
]),
),
limitations: [...set.shared_limitations, ...group.limitations],
}),
provider: set.provider,
})),
),
);
const processCoordinates = {
launcher_pid: z.int().min(1),
process_group_id: z.int().min(1),
};
const processLineageObservation = z.discriminatedUnion("status", [
z.object({
status: z.literal("unavailable"),
observed_at: z.iso.datetime(),
...processCoordinates,
reason: z.string().min(1),
}),
z.object({
status: z.literal("verified"),
observed_at: z.iso.datetime(),
...processCoordinates,
launcher_parent_pid: z.int().min(0),
descendants: z.array(
z.object({
pid: z.int().min(1),
parent_pid: z.int().min(0),
process_group_id: z.int().min(1),
}),
),
}),
]);
const analysisRun = z
.object({
run_id: z.string().uuid(),
process_lineage: z.discriminatedUnion("status", [
z.object({ status: z.literal("not_observed") }),
z.object({
status: z.literal("snapshots"),
snapshots: z.array(
z.object({
provider: providerIdentity,
observation: processLineageObservation,
}),
),
}),
]),
})
.nullable();
export const analysisActivity = z.object({
status: z.enum(["not_observed", "idle", "busy"]),
providers: z.array(
z.object({
provider: providerIdentity,
active: z
.object({
request_id: z.int().min(1),
operation: z.string().min(1),
elapsed_ms: z.int().min(0),
caller_state: z.enum(["waiting", "cancelled"]),
})
.nullable(),
queued_requests: z.int().min(0),
}),
),
});
const providerOperationRequest = z.object({
request_id: z.int().min(1),
operation: z.string().min(1),
stage: z.enum(PROVIDER_FAILURE_STAGES),
});
export const providerOperationHealth = z
.object({
state: z.enum(PROVIDER_OPERATION_STATES),
stage: z.enum(PROVIDER_FAILURE_STAGES).nullable(),
retry_action: z.enum(PROVIDER_RETRY_ACTIONS).nullable(),
exit_code: z.int().nullable(),
requests: z.array(providerOperationRequest),
})
.nullable();
const providerRejectionCode: z.ZodType<ProviderRejectionCode> = z.enum(
PROVIDER_REJECTION_CODES,
);
const providerDiagnostics = z.record(z.string(), jsonValueSchema);
const providerAvailability = z.discriminatedUnion("status", [
z.object({
status: z.literal("unknown"),
code: z.null(),
reason: z.string().min(1),
diagnostics: providerDiagnostics,
}),
z.object({
status: z.literal("available"),
code: z.null(),
reason: z.null(),
diagnostics: providerDiagnostics,
}),
z.object({
status: z.literal("unavailable"),
code: providerRejectionCode,
reason: z.string().min(1),
diagnostics: providerDiagnostics,
}),
]);
const providerTargetSupport = z.discriminatedUnion("status", [
z.object({
status: z.literal("unknown"),
code: z.null(),
reason: z.string().min(1),
diagnostics: providerDiagnostics,
}),
z.object({
status: z.literal("supported"),
code: z.null(),
reason: z.null(),
diagnostics: providerDiagnostics,
}),
z.object({
status: z.literal("unsupported"),
code: z.enum([
"target_kind_unsupported",
"target_format_unsupported",
"architecture_unsupported",
"target_role_unsupported",
"managed_target_unsupported",
]),
reason: z.string().min(1),
diagnostics: providerDiagnostics,
}),
]);
const clientFeatureNameSchema = z.enum([
"elicitation_form",
"elicitation_url",
"roots",
"sampling",
]);
/** Negotiated MCP client features that affect REA workflow availability. */
export const clientFeatureAvailabilitySchema = z.object({
elicitation_form: z.boolean(),
elicitation_url: z.boolean(),
roots: z.boolean(),
sampling: z.boolean(),
});
export type ClientFeatureAvailability = z.infer<
typeof clientFeatureAvailabilitySchema
>;
const toolAvailabilityMode = z.discriminatedUnion("available", [
z.object({
name: z.string(),
available: z.literal(true),
missing_operations: z.array(z.string()).length(0),
remediation: z.null(),
}),
z.object({
name: z.string(),
available: z.literal(false),
missing_operations: z.array(z.string()).min(1),
remediation: z.string().nullable(),
}),
]);
export type ToolAvailabilityMode = z.output<typeof toolAvailabilityMode>;
const toolAvailabilityFacts = {
name: z.string(),
surface: z.string(),
default_mode_available: z.boolean().optional(),
modes: z.array(toolAvailabilityMode).optional(),
client_requirements: z.object({
required: z.array(clientFeatureNameSchema),
optional: z.array(clientFeatureNameSchema),
missing_required: z.array(clientFeatureNameSchema),
missing_optional: z.array(clientFeatureNameSchema),
}),
effects: z.strictObject({
mutatesTarget: z.boolean(),
mutatesSession: z.boolean(),
writesFilesystem: z.boolean(),
launchesProcess: z.boolean(),
accessesNetwork: z.boolean(),
changesUiState: z.boolean(),
mayDiscardData: z.boolean(),
executesForeignCode: z.boolean(),
mutatesDevice: z.boolean(),
idempotent: z.boolean(),
}),
annotations: z.object({
read_only: z.boolean(),
destructive: z.boolean(),
idempotent: z.boolean(),
open_world: z.boolean(),
}),
};
export const toolUnavailabilityReason = z.enum([
"client_capability_missing",
"target_required",
"provider_missing",
"provider_unavailable",
"target_unsupported",
"unsupported_host",
]);
export const toolAvailability = z.discriminatedUnion("available", [
z.object({
...toolAvailabilityFacts,
available: z.literal(true),
reason: z.literal("available"),
remediation: z.null(),
}),
z.object({
...toolAvailabilityFacts,
available: z.literal(false),
reason: toolUnavailabilityReason,
remediation: z.string().nullable(),
}),
]);
export type ToolAvailability = z.output<typeof toolAvailability>;
export type ToolUnavailabilityReason = z.output<
typeof toolUnavailabilityReason
>;
export const TOOL_EFFECT_NAMES = Object.keys(
toolAvailabilityFacts.effects.shape,
) as (keyof z.output<typeof toolAvailabilityFacts.effects>)[];
export const TOOL_ANNOTATION_NAMES = Object.keys(
toolAvailabilityFacts.annotations.shape,
) as (keyof z.output<typeof toolAvailabilityFacts.annotations>)[];
const toolAvailabilityGroupFacts = {
names: z.array(z.string().min(1)).min(1),
surface: toolAvailabilityFacts.surface,
default_mode_available: toolAvailabilityFacts.default_mode_available,
modes: toolAvailabilityFacts.modes,
client_requirements: toolAvailabilityFacts.client_requirements.optional(),
effects: z.array(toolAvailabilityFacts.effects.keyof()),
annotations: z.array(toolAvailabilityFacts.annotations.keyof()),
};
const toolAvailabilityGroup = z.discriminatedUnion("available", [
z.strictObject({
...toolAvailabilityGroupFacts,
available: z.literal(true),
reason: z.literal("available"),
remediation: z.null(),
}),
z.strictObject({
...toolAvailabilityGroupFacts,
available: z.literal(false),
reason: toolUnavailabilityReason,
remediation: z.string().nullable(),
}),
]);
/** Complete inventory with explicit inherited client requirements and enabled flags. */
export const toolAvailabilityInventory = z.strictObject({
client_requirements: toolAvailabilityFacts.client_requirements,
groups: z.array(toolAvailabilityGroup),
});
export type ToolAvailabilityInventory = z.output<
typeof toolAvailabilityInventory
>;
/** Expand the wire representation once at the consuming boundary. */
export const expandToolAvailability = (
inventory: ToolAvailabilityInventory,
): readonly ToolAvailability[] =>
inventory.groups.flatMap((group) =>
group.names.map((name) =>
toolAvailability.parse({
...group,
name,
client_requirements:
group.client_requirements ?? inventory.client_requirements,
effects: Object.fromEntries(
TOOL_EFFECT_NAMES.map((effect) => [
effect,
group.effects.includes(effect),
]),
),
annotations: Object.fromEntries(
TOOL_ANNOTATION_NAMES.map((annotation) => [
annotation,
group.annotations.includes(annotation),
]),
),
}),
),
);
export const sessionProvider = z
.object({
cleanup_pending: z.boolean(),
capabilities: z.array(providerCapabilitySet),
analysis_run: analysisRun,
analysis_activity: analysisActivity,
provider_operation_health: providerOperationHealth,
analysis_provider_binding: z
.object({
provider: providerIdentity,
selection_source: z.enum([
"request",
"environment",
"auto-single-candidate",
]),
analysis_profile: analysisProfileSchema,
})
.nullable(),
analysis_provider_candidates: z.array(
z.object({
provider: providerIdentity,
availability: providerAvailability,
target_support: providerTargetSupport,
selected: z.boolean(),
capabilities: z.array(providerCapabilitySet),
}),
),
tool_availability: toolAvailabilityInventory,
client_features: z.object({
elicitation_form: z.boolean(),
elicitation_url: z.boolean(),
roots: z.boolean(),
sampling: z.boolean(),
}),
server_identity: z.object({
package: z.object({
name: z.string(),
version: z.string(),
root_path: z.string(),
build_commit: z.string().nullable(),
}),
server: z.object({
name: z.string(),
version: z.string(),
started_at: z.string(),
command_path: z.string().nullable(),
}),
sdk: z.object({
server: z.string(),
client_test: z.string(),
core: z.string(),
}),
negotiated_protocol_version: z.string().nullable(),
client: z.object({ name: z.string(), version: z.string() }).nullable(),
skill: z.object({ name: z.string(), expected_version: z.string() }),
catalog: z.strictObject({
counts: z.strictObject({
cli_commands: z.int().min(0),
mcp_tools: z.int().min(0),
mcp_prompts: z.int().min(0),
}),
digests: z.strictObject({
tools_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
prompts_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
combined_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
}),
}),
protocol_features: z.object({
progress: z.boolean(),
cancellation: z.boolean(),
elicitation: z.boolean(),
}),
alignment: z.object({
state: z.enum(["aligned", "mcp_server_restart_required", "unknown"]),
reasons: z.array(z.string()),
remediation: z.string().nullable(),
}),
}),
})
.passthrough();
export const nullableText = z.string().nullable();
export const addressList = z.array(z.string());
export const addressedEntry = z.object({
address: z.string(),
name: z.string(),
});
const unavailable = z.object({
available: z.literal(false),
reason: z.string(),
});
const availableMemoryPermissions = z.object({
available: z.literal(true),
source: z.literal("ghidra-memory-block"),
});
const memoryRegionOutput = z.object({
name: z.string(),
start: z.string(),
end: z.string(),
readable: z.boolean().nullable(),
writable: z.boolean().nullable(),
executable: z.boolean().nullable(),
permissions: z.union([unavailable, availableMemoryPermissions]),
provenance: z.enum(["hopper-public-python-api", "ghidra-memory-block"]),
address_space: z.string().optional(),
image_base: z.string().optional(),
initialized: z.boolean().optional(),
overlay: z.boolean().optional(),
});
export const segmentOutput = evidenceResultOf(
z.array(memoryRegionOutput.extend({ sections: z.array(memoryRegionOutput) })),
);
export const procedureInfoOutput = evidenceResultOf(procedureInfoSchema);
export const symbolDiscoveryOutput = (property: "classes" | "protocols") =>
evidenceResultOf(
z.object({
count: z.int().min(0),
[property]: z.array(addressedEntry),
}),
);
export const objcClassDiscoveryOutput = evidenceResultOf(
z.object({
count: z.int().min(0),
classes: z.array(
addressedEntry.extend({
binding_identity: objcClassImportIdentitySchema
.extend({
slot_addresses: z.array(z.string().min(1)).min(1),
})
.optional(),
}),
),
identity_coverage: z.strictObject({
status: z.enum([
"complete",
"partial",
"unsupported",
"not_requested",
"unknown",
"not_applicable",
]),
reason: z.string().nullable(),
}),
}),
);
export const graphNode = z.discriminatedUnion("status", [
z.object({
address: z.string(),
status: z.literal("ok"),
calls: z.array(z.string()),
relationship_coverage: relationshipCoverageSchema,
limitations: z.array(z.string()).optional(),
}),
z.object({
address: z.string(),
status: z.literal("error"),
error: analysisErrorProjectionSchema,
}),
]);
export const functionDossierOutput = evidenceResultOf(functionDossierSchema);