/**
 * The answer a `read_skill` SELF-CALL gets back (9.84.0).
 *
 * A self-call is `read_skill("X")` from a cursor already standing on X. It used
 * to fall through the skill-graph gate's reachability arm — `makeReachableSkills`
 * filters the cursor out of its own successor set (a MOVE to where you already
 * are is not a move) and `openSkillIds()` excludes every graph-wired skill, so
 * the cursor's own id is in neither half of `hops ∪ open`. The model was told:
 *
 *     read_skill("X") is not reachable from here. Reachable skills: …
 *
 * while X's body was in its system prompt and X's tools were in that very call's
 * tool list. Read as a statement about AVAILABILITY — which is how a model reads
 * "not reachable" — that is a flat contradiction of the request it arrived in,
 * and the observed behaviour was to stop and answer that it could not help.
 *
 * ── THE LAW THIS FILE IS WRITTEN AGAINST ──────────────────────────────────
 *
 * A TOOL RESULT IS NOT READ ONCE. It is composed during iteration N, written
 * into `history`, and then read by the model on call N+1 AND ON EVERY CALL
 * AFTER IT for the rest of the turn — including the out-of-budget WRAP-UP,
 * which `callLLM` serves with an EMPTY tool list under a user message saying
 * that no tools were offered on it.
 *
 * The consequence is the whole design: NO COMPOSE-TIME CHECK CAN MAKE A
 * FORWARD-LOOKING SENTENCE SAFE. A budget test, a posture test, a cursor test
 * — each is true at the instant it runs and each is outlived by the sentence
 * it admitted. Three rounds of this file fixed one clause at a time and each
 * time a different clause was still false, because every one of them was a
 * PREDICTION and the fix was always a better prediction.
 *
 * So the notice makes no predictions at all. Every clause is a fact about ONE
 * already-finished event — the call the model made `read_skill` on — and every
 * clause is anchored to that call by name ("that call"), never by deixis ("the
 * call you just made", which denotes a different call each time it is re-read).
 * A past fact about a named call cannot be falsified by anything that happens
 * afterwards: not by the budget running out, not by a posture refusing a hop,
 * not by a sibling tool in the same batch moving the cursor.
 *
 * ── WHAT WAS DELETED, AND WHY THE NOTICE STILL DOES ITS JOB ───────────────
 *
 * Gone: the exhortation ("Go ahead and act on the request"), the move offer
 * ("read_skill MOVES you somewhere else — from here that would be: beta"), the
 * open-skill offer ("These activate without moving you: gamma"), and the
 * budget clause that was added to gate them. Each was false somewhere:
 *
 *   • the exhortation, on the wrap-up call, beside a frame reporting that no
 *     tools were offered on it;
 *   • the move offer, on the wrap-up call (a hop is a tool call), under
 *     `strictness: 'rails'` (every model hop refused) and under `'guard'` on a
 *     decisively-routed turn (every hop not on an outstanding menu refused) —
 *     where the sibling posture arm answers a taken-up offer with "read_skill
 *     here reaches only the open skills: gamma", contradicting the notice from
 *     the arm immediately above it, and spending escalation budget to do so;
 *   • the budget clause, because a budget read at compose time is a claim
 *     about a call that has not happened.
 *
 * The notice's JOB is to stop a model concluding its capability is gone. It
 * does that by stating what it held, not by promising what it may do: you were
 * standing in this skill, its instructions were in that call's prompt, its
 * tools were on that call's wire, and asking took nothing away. A model that
 * reads those four facts has no ground left for "I cannot help".
 *
 * The forward-looking half had a rightful owner all along, and it is not a
 * tool result: the `read_skill` DESCRIPTION is recomposed on every single call,
 * so it can safely speak in the present ("You are in 'beta'."), it is filtered
 * for per-role visibility at source, and it is absent from the wrap-up because
 * the whole tool list is. One owner for the present tense, one for the past,
 * and they can no longer disagree — which is the only property that survived
 * every round of this bug.
 */
import type { ActiveInjection } from '../../injection-engine/types.js';
import type { LLMToolSchema } from '../../provider-contract/types.js';
/** What the current skill's tools did on the call the model made `read_skill` on. */
export interface SelfSkillTools {
    /** Every tool the skill declares — `[]` for a skill that declares none. */
    readonly declared: readonly string[];
    /** Those actually on that call's wire. A subset of `declared`. */
    readonly served: readonly string[];
}
/**
 * The skill's tools, as of the call the model just made — or `undefined` when
 * that cannot be established, which is not the same answer and must not be
 * reported as one.
 *
 * `undefined` means the notice says nothing about tools at all, and there are
 * exactly two ways to get it (9.86.0 — the previous note named a third that
 * cannot happen). It happens when the cursor's skill is not in the ACTIVE set
 * for that call, and when the wire key is absent from scope altogether.
 *
 * `reactMode: 'classic'` is NOT one of them, which is worth stating because the
 * old note said it was: seed writes `scope.dynamicToolSchemas` from the
 * build-time schemas at turn start (`stages/seed.ts`), so under classic the key
 * is present and holds the list `callLLM` really sends — the notice would name
 * the build-time list, correctly. (No self-call can arise there anyway:
 * `.skillGraph()` is refused at build under classic, and only a graph gives the
 * gate a cursor to compare against.) The grouped chart used to be a real third
 * way — the Tools slot writes the key inside `sf-llm-call` and this gate reads
 * it outside — until that boundary began bubbling it out.
 *
 * Silence is the only honest output in the two remaining cases: the
 * alternative is a sentence naming tools the model may not have.
 *
 * The out-of-budget wrap-up is NOT one of these cases and must not be modelled
 * as one. Its empty tool list belongs to a LATER call; the wire read here is
 * always the one the model was really handed on the call being answered, and
 * the notice speaks of that call and only that call.
 */
export declare function selfSkillTools(skillId: string, activeInjections: readonly ActiveInjection[] | undefined, wire: readonly LLMToolSchema[] | undefined): SelfSkillTools | undefined;
/**
 * The self-call's tool result.
 *
 * NOT a refusal to the MODEL, and deliberately so. The gate's other two arms
 * refuse because the model asked for something it may not have; a self-call
 * asks for a body it already holds, and the useful reply is the fact — where
 * you stood, and what rode that call — not a decline. It is still counted as a
 * refusal by the ESCALATION BUDGET (`noteSkillRefusal`, beside the emit), and
 * those two facts do not fight: the budget is not measuring how the sentence
 * reads, it is measuring a model that keeps asking the graph where it is
 * instead of working, which is exactly the stuck run the budget escalates.
 *
 * It still OVERWRITES the tool's own result the way every other arm does,
 * because that overwrite is what puts the authoritative sentence in front of
 * the governance rules, the refusal cap and the after-tool moment, on both
 * channels, before the model reads anything.
 *
 * The one thing not overwritten is a BODY. The live case is `surfaceMode:
 * 'both'` — `'tool-only'` cannot arise here, because `skillBodyDelivery`
 * refuses it at BUILD time for anything but an `llm-activated` skill and only a
 * graph-wired skill can be the cursor. Under `'both'` the body is in the system
 * prompt AND rides the tool result, and the second copy is the whole point of
 * the mode: recency-first delivery, the body at the end of the window rather
 * than only at the top of the prompt. Dropping it here would quietly demote
 * `'both'` to `'system-prompt'` on the one call that asked to re-read.
 *
 * TENSE IS LOAD-BEARING, AND SO IS DEIXIS. Every clause below is past tense
 * about a single named call, and every reference to that call is "that call" —
 * bound by the opening clause, which says which call it was. "The call you just
 * made" was the earlier wording and it is not safe: re-read four calls later it
 * denotes call four, and the tools it names rode call one.
 *
 * There are exactly two present-tense phrases in the output and both are
 * timeless rather than temporal: "It declares no tools of its own" (a
 * declaration is fixed when the skill is defined and cannot vary within a run)
 * and "Its instructions are repeated below" (the body is appended to this very
 * string, so the sentence is true wherever the string is read).
 */
export declare function selfCallNotice(args: {
    readonly skillId: string;
    /** `undefined` when the wire could not be established — see `selfSkillTools`. */
    readonly tools: SelfSkillTools | undefined;
    readonly body?: string;
}): string;
