Files
reverse-engineering-anything/src/application/oneShotOperationInput.ts
T
morlutoandCursor Agent 5c2917266d fix(inputs): validate command admission and preserve valid MCP requests (#9901)
* 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>
2026-11-11 00:43:30 -05:00

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 });
};