/**
 * skillToolDescriptors — the graph DESCRIBING the tools it needs (9.34.0).
 *
 * `read_skill` is not an ordinary tool. It is how the model asks to move the
 * skill-graph cursor: its enum is the catalog, its description is the offer
 * (what is reachable from here, what will be refused, what this turn's menu
 * proposes), and its result is what the model reads about the skill it just
 * activated. Every one of those sentences is the GRAPH's to write.
 *
 * What the graph must NOT do is construct a framework's tool object. That is
 * why this module produces {@link SkillToolDescriptor} POJOs — name,
 * description, input schema, a pure `execute` — and `skillTools.ts` (the host
 * adapter) is the one line that turns each into an agentfootprint `Tool` with
 * `defineTool`. A host on another framework calls the same functions and
 * wraps the result in its own tool type instead.
 *
 * ONE catalog, ONE grammar: the Agent's auto-attach path, `SkillRegistry`
 * and any foreign host all read the descriptions from here, so they cannot
 * drift the way a second hand-written `read_skill` would.
 *
 * Zone: PURE CORE. Pinned by
 * `test/injection-engine/skill-graph-fence.test.ts`.
 */
import type { SkillToolDescriptor } from './hostContract.js';
import type { Injection } from './types.js';
/**
 * What the gate will ACTUALLY grant right now — the ids `read_skill` should be
 * offering, as opposed to the ids it can dispatch (8.5.0).
 *
 * Without this the tool enumerated every registered skill while the skill-graph gate
 * admitted only `reachableSkills(cursor) ∪ open`, so a route target the cursor cannot
 * reach was advertised on every iteration and refused on every call. The model was
 * being asked to choose from a menu the library knew it would reject.
 */
export interface ReadSkillOffer {
    /**
     * Ids the gate will grant from the current cursor. Omit when there is no
     * graph — then the description is the plain catalog it has always been,
     * minus anything {@link ReadSkillOffer.hiddenIds} removes.
     */
    readonly grantable?: readonly string[];
    /** Say the rest are refusable rather than hiding them. Hiding a skill it can see
     *  in `list_skills` (and in the enum) would just move the confusion; naming the
     *  boundary lets the model route around it in one step instead of guessing. */
    readonly showRefusable?: boolean;
    /**
     * Ids removed from the description ENTIRELY — not listed as reachable, not
     * listed as refusable, not named at all (9.11.0).
     *
     * The opposite treatment from the graph's `shut` set above, and deliberately
     * so. A graph refusal is about WHERE THE CURSOR IS: the skill exists for this
     * caller, it is simply not reachable from here, so naming it lets the model
     * route to it in one step. A hidden skill is about WHO IS ASKING: no cursor
     * move makes it available, and naming it would tell a role about a capability
     * it will never be allowed to use — leaking the shape of somebody else's
     * permissions into this model's prompt.
     *
     * The ENUM keeps the full catalog either way (see below). Narrowing it would
     * turn a policy refusal into a generic schema error, and the model would
     * never read the policy's own message.
     */
    readonly hiddenIds?: readonly string[];
    /**
     * WHERE THE CURSOR STANDS — the skill the model is already IN (9.84.0).
     *
     * Passed on every call that has a cursor, menu or no menu. It does two jobs
     * the offer could not do before: it NAMES the current skill in the
     * description (previously only the menu's stay clause ever did, and a
     * decisively-routed turn has no menu), and it keeps that skill out of the
     * "Not reachable from here" list — where it appeared purely as an artefact of
     * being filtered out of its own successor set.
     *
     * {@link ReadSkillOffer.menu}'s `cursorId` is the same value and still
     * honoured; this one is simply not conditional on a menu being outstanding.
     *
     * It is NOT exempt from {@link ReadSkillOffer.hiddenIds}. A cursor the
     * caller's role may not see is not named — see the filter in `describeOffer`.
     */
    readonly cursorId?: string;
    /**
     * Turn-start MENU (SG-C): when set, the description LEADS with these
     * candidates (id + one-line description; supplied advisory relevance %s),
     * names the cursor, and states STAY as a first-class
     * option ("answer without calling read_skill to stay in '<cursor>'").
     *
     * Set only while the turn's menu verdict is outstanding (turn start, before
     * any accepted pick) — the tools slot reads it off the SAME verdict the
     * record carries, so the offer can never drift from `turn_routed.offered`.
     * The enum stays the full catalog (the 8.5.0 law below — narrowing it would
     * retire four honesty mechanisms), and {@link ReadSkillOffer.hiddenIds}
     * filters menu rows exactly as it filters every other list: a role-hidden
     * skill is never NAMED, menu or no menu.
     * Descriptions appear once per skill id in this offer. Later catalog or
     * reachability rows retain the id and refer to its description above.
     */
    readonly menu?: {
        readonly candidates: ReadonlyArray<{
            readonly id: string;
            readonly relevance?: number;
        }>;
        /** Names the cursor the STAY sentence refers to ("you are in billing"). */
        readonly cursorId?: string;
        /** STAY spelled out as a first-class option (mid-conversation menus). */
        readonly stay?: boolean;
    };
    /**
     * The mounted graph is a decision `tree()` (9.86.0).
     *
     * A tree routes by predicate on every iteration and keeps no cursor, so
     * `reachableSkills()` is empty from every position and EVERY routing pick is
     * refused by construction. Offering `read_skill` anyway asked the model to
     * choose from a menu the library knew it would reject — the same defect
     * 8.5.0 fixed for the flat graph's unreachable ids, left standing for the
     * one shape where it is total.
     *
     * With no open skill left after {@link ReadSkillOffer.hiddenIds}, the tool is
     * not offered at all ({@link readSkillDescriptor} returns `undefined`); with
     * open skills, the description says a tree has no cursor to move and names
     * exactly the skills a pick CAN open. Either way the name stays dispatchable
     * — position governs the offer, never dispatch — so a model that calls it
     * from a restored transcript still reads the gate's own refusal.
     */
    readonly treeRouted?: boolean;
}
/**
 * Describe the `list_skills` tool — a no-arg tool that returns the registered
 * skills as `{ id, description }[]`. Lets the LLM discover skills without
 * paying the prompt-token cost of embedding the catalog into every system
 * prompt.
 *
 * Pairs with {@link readSkillDescriptor} (which actually activates a skill by
 * id). Returns `undefined` when there are no skills — a tool offering an
 * empty catalog is noise.
 */
export declare function listSkillsDescriptor(skills: readonly Injection[]): SkillToolDescriptor<Record<string, never>, string> | undefined;
/**
 * Describe the `read_skill` tool — THE cursor door. The LLM picks WHICH skill
 * via the `id` argument; the host's loop is what turns an accepted pick into a
 * cursor move (see `SkillGraphHost`, obligations 2 and 3).
 *
 * `execute` returns a confirmation string, or the skill body verbatim for the
 * `'tool-only'` / `'both'` surface modes (recency-first delivery). The actual
 * bookkeeping — appending the requested id to the activated set, gating it
 * against `reachableSkills`, publishing the accepted pick — is the HOST's,
 * because only the host sees the tool call.
 *
 * Pass `offer` to scope the DESCRIPTION to what the graph's gate will actually
 * grant from the current cursor (8.5.0); the enum stays the full catalog
 * either way. Omit it and the description is byte-identical to the plain
 * catalog it has always been.
 *
 * Returns `undefined` when there are no skills.
 */
export declare function readSkillDescriptor(skills: readonly Injection[], offer?: ReadSkillOffer): SkillToolDescriptor<{
    id: string;
}, string> | undefined;
/**
 * What `read_skill` answers when it activated a skill but did NOT carry its body
 * (surface modes `'system-prompt'` / `'auto'` — the body lands via the system
 * slot next iteration).
 *
 * Exported because the skill-graph gate has to tell this sentence apart from a
 * body: a SELF-CALL keeps the body (the model asked to re-read where it is, and
 * under `'tool-only'` the tool result is the only place that body ever appears)
 * but must not keep a promise of an activation that is not pending. Comparing
 * against the one owner of the string beats guessing at its shape.
 */
export declare function skillActivationConfirmation(id: string): string;
