/**
 * The skills-listing BUDGET SEAT as vocabulary: the two knobs a deployment may declare
 * (`TaskSpec.skillListingBudgetFraction` / `TaskSpec.skillListingMaxDescChars`), the ONE reader that
 * lowers them to the numbers the renderer measures against, and the disclosure announced when the
 * budget costs a skill its description. The reader is pure; the announcer's only effect is the notice.
 * `runner/synthetic-tools.ts` owns the renderer that consumes the numbers.
 *
 * The listing frame is an INTACT-class turn attachment (`runner/turn-attachments.ts`
 * `ATTACHMENT_DELIVERY_CLASS`): the delivery lane never clips it, so the number this reader produces is
 * the number the frame is rendered to — there is no lane ceiling above it.
 *
 * Sibling of `tool-search-setting.ts` in shape and in posture: a value outside its domain REFUSES
 * loudly rather than folding back to the default, because a knob that is silently dropped reads to the
 * deployment as a knob that took effect.
 */
import { type EngineNotice } from "./engine-notice.js";
/** Fraction of the model's context window the listing may spend when the deployment declares nothing. */
export declare const SKILL_LISTING_BUDGET_FRACTION_DEFAULT = 0.01;
/** Per-skill description cap (code units, ellipsis included) when the deployment declares nothing. */
export declare const SKILL_LISTING_DESC_MAX_CHARS_DEFAULT = 1536;
/** Context window assumed for a model that declares none. */
export declare const SKILL_LISTING_DEFAULT_CONTEXT_TOKENS = 200000;
/**
 * The token→byte exchange rate this budget allocates at.
 *
 * @contract skills.listing_budget_exchange_rate — deliberately NOT `DEFAULT_CHARS_PER_TOKEN` (3), and
 * the difference is directional, not an inconsistency. That constant serves the compaction/context
 * estimators, which convert bytes→tokens and must OVER-estimate the token count to stay inside a
 * window; a low rate is the conservative choice there. This seat converts tokens→a byte ALLOWANCE,
 * where a low rate silently SHRINKS what the deployment may spend. Two directions, two numbers, both
 * named. At 3 the default allowance would be 6 000 bytes rather than 8 000 — a quarter of every
 * default deployment's listing, removed by an estimator's safety margin.
 */
export declare const SKILL_LISTING_BYTES_PER_TOKEN = 4;
/** The typed refusal a budget seat outside its domain raises: a bad value never selects a smaller
 *  listing silently, and never folds back to the default budget. */
export declare const SKILL_LISTING_BUDGET_INVALID_CODE = "config.skill_listing_budget_invalid";
/** The notice code minted when the budget cost at least one skill its description or its line. */
export declare const SKILL_LISTING_TRUNCATED_CODE = "skills.listing_truncated";
/** The agent-type listing's twin: minted when the budget cost at least one agent type its detail or its line. */
export declare const AGENT_LISTING_TRUNCATED_CODE = "agents.listing_truncated";
/** Which listing frame a degradation report describes. */
export type ListingKind = "skills" | "agents";
/** The lowered seat: the two numbers the renderer measures against. */
export interface ResolvedSkillListingBudget {
    /** The budget the renderer enforces over the whole block (fences, headers, markers, joiners
     *  included), in UTF-8 bytes. */
    blockMaxBytes: number;
    /** Per-skill description cap in code units, ellipsis included. */
    descMaxChars: number;
}
/**
 * The ONE reader of the two listing-budget seats.
 *
 * @contract skills.listing_budget_enforced_as_declared — the resolved `blockMaxBytes` is
 * `floor(window × 4 × fraction)` with no ceiling above it: the `<skills>` frame is an intact-class
 * attachment the delivery lane never clips, so what the deployment declares is what the renderer
 * folds the listing into, on the initial block and on every drift frame alike. The fraction's own
 * domain `(0, 1]` is the only upper bound (a whole window at most), and a value outside it refuses.
 *
 * `skillListingBudgetFraction` must be a finite number in `(0, 1]` — zero is excluded deliberately: a
 * zero budget is not "spend nothing", it is a listing every skill loses its description to, which the
 * deployment expresses by declaring fewer skills. `skillListingMaxDescChars` must be a positive safe
 * integer. Both absent ⇒ the documented defaults, which reproduce the pre-seat render byte for byte
 * (`200 000 × 4 × 0.01` = 8 000 bytes).
 *
 * The window is `contextTokens ?? contextWindow`, the same physical coordinate the deferral gate
 * measures against. `contextTokens: 0` (or any other non-finite/non-positive declared value) does
 * NOT fall through `??` to `contextWindow` — `0` is not nullish — so it lands here as a
 * declared-but-unusable window, and this seat folds it to {@link SKILL_LISTING_DEFAULT_CONTEXT_TOKENS}
 * the same as a genuinely absent window: one rule, not a case per bad shape, because this seat's
 * job is to always hand a renderer a usable number.
 */
export declare function resolveSkillListingBudget(opts: {
    fraction?: unknown;
    maxDescChars?: unknown;
    model?: {
        contextTokens?: number;
        contextWindow?: number;
    } | undefined;
}): ResolvedSkillListingBudget;
/** The reader's answer to "no knobs declared, no window declared" — the budget a renderer measures
 *  against when its caller hands it none. One rule: only the reader turns knobs into numbers. */
export declare const SKILL_LISTING_DEFAULT_BUDGET: Readonly<ResolvedSkillListingBudget>;
/**
 * Announce a listing frame the budget degraded — delivered only when an entry actually lost its detail
 * (`nameOnly`) or its line (`dropped`), so a deployment whose roster fits hears nothing. One announcer
 * for both listing frames; the code names the frame (`skills.listing_truncated` / `agents.listing_truncated`).
 *
 * Called from the commit of the frame's receipt — the moment the carrying message is in the transcript —
 * never from the config door: the door cannot see the deployment's listing switches (resolved at run
 * start), it runs per prepared task rather than per delivered listing, and a resume leg reaches only
 * the boundary.
 *
 * Audience is `operator`: the person who can raise the fraction, shorten a description or remove an
 * entry runs the deployment, not the session.
 */
export declare function announceListingTruncation(onNotice: ((notice: EngineNotice) => void) | undefined, opts: {
    listing: ListingKind;
    /** The denominator: how many entries the DELIVERED frame set out to list — the whole roster on an
     *  initial frame, the newly announced set on a drift frame. Read off that frame's own render. */
    total: number;
    nameOnly: readonly string[];
    dropped: readonly string[];
    budget: ResolvedSkillListingBudget;
    sessionId?: string | undefined;
}): void;
