import type { ActionEffectSource, ActionSpec, ToolDef } from "./types.js"; import type { ActionClass } from "./action-class.js"; /** A readable schema summary, not a substitute for runtime validation. */ export interface ValueSchema { /** Wire type, unwrapped through optional/default/nullable. */ type: string; required: boolean; description?: string; /** Allowed values, when the parameter is an enum or a union of literals. */ enumValues?: string[]; /** Default applied by the schema when the caller omits the parameter. */ default?: unknown; properties?: Record; items?: ValueSchema; variants?: ValueSchema[]; /** Deeper fields were omitted to bound discovery output. */ truncated?: boolean; } export interface ParamSchema extends ValueSchema { name: string; /** * Where this name was found. A parameter missing `declared` is stripped * before the handler sees it, whatever the description promises. */ sources: Array<"documented" | "forwards" | "declared">; /** * Index into the action's `alternatives` when this parameter is one of a * choice. `required` is false for every member of a choice, because the * handler takes either one; what has to be satisfied is the group. */ alternativeGroup?: number; } export interface ActionSchema { tool: string; action: string; description: string; /** The C++ bridge method this dispatches to, when it dispatches to one. */ bridge?: string; /** True when the action runs in the server process with no editor call. */ local: boolean; /** Longer wait this action declares for itself, in milliseconds. */ timeoutMs?: number; /** * Whether this observes the editor or changes it, as the action DECLARES it. * * MCP's own readOnlyHint is per TOOL, and every tool here is a category * holding both reads and mutations, so the manifest cannot carry this. A * harness that wants to auto-approve reads and prompt on writes reads it * from here instead of maintaining its own list. * * read observes; landing it in the wrong editor changes nothing * mutate may change the editor, its project on disk, or its process * unknown decided by a parameter (an arbitrary python string, a wrapped * tool name), and therefore gated exactly like mutate * * Read straight off the ActionSpec. It used to be recomputed here from the * action's name, which meant this field could disagree with the gate that * actually stops the call. */ class: ActionClass; /** * Where that answer came from. `declared` is a person's, written at the * declaration. `inferred` is the verb lexicon's, and appears only on the * actions this package does not declare: Epic's wrapped engine tools and a * plugin action whose manifest did not say. A caller building its own * approval policy should treat `inferred` reads with more suspicion than * declared ones. */ classSource: ActionEffectSource; params: ParamSchema[]; /** * Choices the action offers, when it offers any. * * `level.delete_actor` takes `actorLabel OR actorPath`, and reporting both * as required would have a caller send two ways of naming one actor, while * reporting both as optional would have it send neither. Neither is * individually required and the group is, so the group is what is published: * each branch is a set of names that go together, and `required` says * whether one of the branches has to be supplied. */ alternatives?: AlternativeGroup[]; /** * Names promised by the description or read by `mapParams` that the category * does not declare. Passing one of these has no effect. */ drift: string[]; } /** * A parameter named by an action's `Params:` clause. * * `group` is set when the clause offered the name as one of a choice * (`actorLabel OR actorPath`). A member of a choice is never individually * required, however the clause marks it, because the handler takes either. */ export interface DocumentedParam { name: string; optional: boolean; /** Index into the parse's `alternatives`, when this name is one of a choice. */ group?: number; } /** * A choice the clause offered. * * Each branch is the set of names that go together, so `name + packagePath? OR * materialPath` has branches `[["name", "packagePath"], ["materialPath"]]`: * what the caller supplies is one branch, not one name. */ export interface AlternativeGroup { branches: string[][]; /** True when the action needs one of the branches. */ required: boolean; } export interface DocumentedParams { params: DocumentedParam[]; alternatives: AlternativeGroup[]; } /** * Pull the parameters out of the `Params:` clause every action carries. * * `known`, when given, is the category's declared keys. It settles the two * places where the clause alone is ambiguous - a name behind a head connective * and a name inside a bracket - by asking whether the wire would accept it. * It only ever admits a name, never refuses one that stands in plain parameter * position, so a name the description invents is still reported and still * shows up as drift. */ export declare function parseParams(description: string, known?: ReadonlySet): DocumentedParams; /** The parameters the `Params:` clause names, without the choice structure. */ export declare function parseParamsClause(description: string, known?: ReadonlySet): DocumentedParam[]; /** * The parameter keys an action's `mapParams` closure reads. * * Reading the compiled source is the only way to see this: `mapParams` is an * opaque function by the time the registry is built. It is a best-effort * signal - a closure that spreads its argument reads everything and shows * nothing here - so it only ever adds names, never removes them. */ export declare function forwardedParams(spec: ActionSpec): string[]; /** Build the full schema for one action of one tool. */ export declare function actionSchema(tool: ToolDef, action: string): ActionSchema; /** * Resolve an action reference to the tools that provide it. * * Accepts `tool.action`, `tool:action`, `tool action`, or a bare action name, * which may be provided by more than one category (`list`, `save`, `create`). * All matches come back so the caller can disambiguate rather than being * handed whichever one sorted first. */ export declare function resolveActionRef(ref: string, tools: ToolDef[]): Array<{ tool: ToolDef; action: string; }>; /** Close spellings for an action name that did not resolve. */ export declare function suggestActions(ref: string, tools: ToolDef[], limit?: number): string[]; /** * How close two action names are, on 0..1. * * Substring containment dominates, because the realistic miss is a caller who * remembers part of the name (`bones` for `list_skeleton_bones`) rather than * one who transposes two letters. Edit distance catches the typo case below * that, and anything under a third of the name matching scores zero so the * suggestion list stays short enough to read. */ export declare function similarity(a: string, b: string): number; /** * The closest spellings to a missed action name, out of a plain name list. * * Separate from `suggestActions` because the dispatcher has only its own * category's keys at the point it fails, and importing the whole graph there * would tie a per-call error path to the session registry. */ export declare function nearestActions(ref: string, available: string[], limit?: number): string[]; /** * Every action on the surface, with the drift each one carries. Used by the * schema-drift unit test and by `project(describe_action)` when it is asked * for a whole category rather than one action. */ export declare function allActionSchemas(tools: ToolDef[]): ActionSchema[];