mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 09:52:27 +00:00
* fix(cli): validate named inputs before target admission * fix(mcp): preserve valid inputs when advertised schema references widen * test(inputs): reach session behavior with admitted inputs One-shot admission now rejects incomplete requests before a session exists. Lifecycle tests supply the required fields, and unknown arguments are rejected before provider issues are retained. * fix(cli): preserve Electron fuse path spelling Anchor relative fuse paths without stripping dot segments, and let an empty path reach the operation so it still reports invalid_request. * fix(cli): reject outline-depth unless detail is summary The advertised schema already required detail summary whenever outline-depth was present. The parser still applied the full default and accepted that combination, so its output failed the same schema. Parsing now rejects it before the command runs. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
112 lines
3.7 KiB
TypeScript
112 lines
3.7 KiB
TypeScript
import type { z } from "zod";
|
|
import { toolContract } from "../contracts/toolContracts.js";
|
|
import { OFFICIAL_TOOL_CONTRACTS } from "../contracts/officialToolContracts.js";
|
|
import { NATIVE_TOOL_CONTRACTS } from "../contracts/native/nativeToolContracts.js";
|
|
import {
|
|
ARTIFACT_TOOL_CONTRACTS,
|
|
artifactInventoryInputSchema,
|
|
type ArtifactAnalysisOperation,
|
|
} from "../contracts/artifactToolContracts.js";
|
|
import {
|
|
MANAGED_TOOL_CONTRACTS,
|
|
managedArtifactInputSchema,
|
|
managedMembersInputSchema,
|
|
type ManagedToolName,
|
|
} from "../contracts/managed/managedToolContracts.js";
|
|
import { enhancedInputSchemas } from "../contracts/enhancedInputs.js";
|
|
import type { NativeToolName } from "../contracts/native/nativeToolContracts.js";
|
|
import type { JsonValue } from "../domain/jsonValue.js";
|
|
import { parseAnalysisInput } from "./AnalysisInput.js";
|
|
import { ok } from "../domain/result.js";
|
|
|
|
/** Operations the direct workflow can perform without a persistent session. */
|
|
export type DirectAnalysisTool =
|
|
| "annotate_native_function"
|
|
| "inspect_native_load_image"
|
|
| "read_bytes"
|
|
| "search_bytes"
|
|
| "address_to_file_offset"
|
|
| "binary_overview"
|
|
| "procedure_pseudo_code"
|
|
| "procedure_references"
|
|
| "read_function_instructions"
|
|
| "inspect_native_instruction"
|
|
| "inspect_native_data_type"
|
|
| "resolve_native_call_targets"
|
|
| "analyze_function"
|
|
| "inspect_native_api"
|
|
| "inspect_native_dispatch_metadata"
|
|
| "search_strings"
|
|
| "search_procedures"
|
|
| "xrefs"
|
|
| "trace_feature"
|
|
| "trace_native_ui_action"
|
|
| "trace_native_values"
|
|
| "decompile_all";
|
|
|
|
type OneShotOperation =
|
|
| DirectAnalysisTool
|
|
| NativeToolName
|
|
| ArtifactAnalysisOperation
|
|
| ManagedToolName;
|
|
type Schema = z.ZodType<Readonly<Record<string, unknown>>>;
|
|
|
|
type ContractSchemas<
|
|
Contracts extends readonly {
|
|
readonly name: string;
|
|
readonly inputSchema: Schema;
|
|
}[],
|
|
> = {
|
|
[Contract in Contracts[number] as Contract["name"]]: Contract["inputSchema"];
|
|
};
|
|
|
|
const schemasByName = <
|
|
const Contracts extends readonly {
|
|
readonly name: string;
|
|
readonly inputSchema: Schema;
|
|
}[],
|
|
>(
|
|
contracts: Contracts,
|
|
): ContractSchemas<Contracts> =>
|
|
Object.fromEntries(
|
|
contracts.map(({ name, inputSchema }) => [name, inputSchema]),
|
|
) as ContractSchemas<Contracts>;
|
|
|
|
const schemas = {
|
|
...schemasByName(OFFICIAL_TOOL_CONTRACTS),
|
|
...schemasByName(NATIVE_TOOL_CONTRACTS),
|
|
...schemasByName(ARTIFACT_TOOL_CONTRACTS),
|
|
...schemasByName(MANAGED_TOOL_CONTRACTS),
|
|
...enhancedInputSchemas,
|
|
inspect_native_load_image: toolContract(
|
|
"inspect_native_load_image",
|
|
).inputSchema.omit({ detail: true }),
|
|
inspect_managed_artifact: managedArtifactInputSchema.omit({ detail: true }),
|
|
inspect_managed_members: managedMembersInputSchema.omit({ detail: true }),
|
|
inventory_artifact: artifactInventoryInputSchema,
|
|
} satisfies Record<OneShotOperation, Schema>;
|
|
|
|
/** Parse the named operation before target selection or snapshot replay. */
|
|
export const admitOneShotOperation = <Name extends OneShotOperation>(
|
|
name: Name,
|
|
input: Readonly<Record<string, JsonValue>>,
|
|
signal?: AbortSignal,
|
|
) => {
|
|
type SelectedInput = Partial<z.output<(typeof schemas)[Name]>> &
|
|
Readonly<Record<string, JsonValue>>;
|
|
const parsed = parseAnalysisInput(
|
|
name,
|
|
input,
|
|
schemas[name] as Schema,
|
|
signal,
|
|
);
|
|
if (!parsed.ok) return parsed;
|
|
// Admission owns the selected request: preserve top-level omissions for
|
|
// Evidence and snapshot identity while carrying each selected parsed value.
|
|
// Nested defaults remain in the owning field's parsed value.
|
|
const selected = Object.fromEntries(
|
|
Object.entries(parsed.value).filter(([key]) => Object.hasOwn(input, key)),
|
|
) as SelectedInput;
|
|
return ok({ name, input: selected });
|
|
};
|