/** * Where a skill's BODY actually lands — and the one combination where the honest * answer is "nowhere" (8.5.0). * * `surfaceMode` names a delivery CHANNEL, and `'tool-only'` names the channel that * only exists when the model calls `read_skill`: the body is returned as that tool's * result, and the system slot suppresses it by design (`buildSystemPromptSlot`, * mirrored in `routeActiveInjections`). That is exactly right for a skill `read_skill` * activates, which is what the mode was designed against and what every shipped * Block-C test covers. * * It is a hole for a skill the GRAPH activates. A route target, a flat entry and a * decision-tree leaf all activate off the cursor, without any `read_skill` call — so * the tool result never happens, the system slot suppresses the body anyway, and the * body reaches the model through no channel at all. The skill's TOOLS still arrive, * which makes it worse than the skill not loading: the model is handed the tools of a * procedure it was never told. * * The rule that closes it is the one 8.4.0 already uses for the `read_skill` gate: * `trigger.kind === 'llm-activated'` is precisely "read_skill can really activate * this". A skill may claim the read_skill delivery channel exactly when read_skill is * what activates it. * * Refusal rather than a quiet fall back to the system slot, because the author wrote * `'tool-only'` to keep the body OUT of the system prompt (token cost, attention * placement). Silently putting it back would honour the activation and break the * declaration — a different lie, not a fix. `'both'` already means "deliver it either * way", so the refusal has a real, one-word answer to name. */ import type { Injection } from './types.js'; import { type SurfaceMode } from './factories/defineSkill.js'; /** * The surface mode a skill will be TREATED as at runtime. * * Today the runtime compares the literal string, so `'auto'` is its own mode and * lands in the system slot — pass no provider and that is what you get back, which * is the truth about the current engine. * * Pass a provider (and model) to ask the OTHER question: what would `'auto'` become * if the `resolveSurfaceMode` cascade were wired into the runtime? It resolves to * `'tool-only'` for every non-Claude provider, so wiring it in without this guard * would silently open the delivered-nowhere hole for every OpenAI / Bedrock / Ollama * user at once. Routing both questions through ONE function is what keeps that from * being a future accident: the refusal below is written against this, so the day the * cascade is wired in, the guard already covers it. */ export declare function resolvedSurfaceModeOf(skill: Injection, provider?: string, model?: string): SurfaceMode; /** * Can this skill's body be delivered through the `read_skill` tool result? * * Only if `read_skill` is what activates it. `'llm-activated'` is the one trigger * kind that reads `activatedInjectionIds`, which is the only thing a `read_skill` * call writes — the same clause the gate's open-skill rule turns on (8.4.0). */ export declare function activatesByRead(skill: Injection): boolean; /** * Refuse every skill that claims the `read_skill` delivery channel without being * activated by `read_skill`. Returns the message, or `undefined` when all is well. * * Runs over the FINAL injection list — the agent is the only place that sees every * skill's compiled trigger, whichever call registered it — and names every offender, * because `skillsFromDir({ surfaceMode: 'tool-only' })` under a graph refuses a whole * directory at once and a list is debuggable where one sample is not. */ export declare function toolOnlyDeliveryRefusal(injections: readonly Injection[]): string | undefined;