/**
 * topicProfileValidation — closed-enum clamps for every Topic Profile field.
 *
 * TOPIC-PROFILE-SPEC §10.2: EVERY profile field is clamped to its closed enum
 * server-side BEFORE persist and before it can reach a launch arg — not just
 * the model arm. The pin write is a NEW untrusted entry point that FABLE's
 * config-sourced resolver did not cover, and `thinkingMode` becomes a launch
 * arg (`-c model_reasoning_effort=…`), so it is an injection surface symmetric
 * to the model one.
 *
 * The clamp runs at the RESOLUTION boundary for every source (store pin,
 * config default, transferred entry) — `validateProfileFields` is pure and
 * callable from all three. Pure logic, no I/O.
 */
import type { IntelligenceFramework } from './intelligenceProviderFactory.js';
export declare const THINKING_MODES: readonly ["off", "low", "medium", "high", "max"];
export type ThinkingMode = (typeof THINKING_MODES)[number];
/**
 * Claude Code's `--effort` launch flag levels (the live CLI's verified set).
 * A DIRECT pin of the CLI flag value — distinct from `thinkingMode` (which is
 * a cross-framework reasoning-budget abstraction the launch builders MAP onto
 * `--effort`/`model_reasoning_effort`). `effort` passes through verbatim as
 * `--effort <level>` on claude-code spawns and is a strict no-op elsewhere.
 * NOTE: 'ultracode' / 'ultra' are NOT CLI values and are deliberately absent.
 */
export declare const EFFORT_LEVELS: readonly ["low", "medium", "high", "xhigh", "max"];
export type EffortLevel = (typeof EFFORT_LEVELS)[number];
export declare const ESCALATION_OVERRIDES: readonly ["inherit", "suppress"];
export type EscalationOverride = (typeof ESCALATION_OVERRIDES)[number];
export declare const MODEL_TIERS: readonly ["default", "escalated"];
export type ProfileModelTier = (typeof MODEL_TIERS)[number];
/** Failure classes for the §10.3 audit — never the raw value verbatim. */
export type ProfileFieldFailure = 'off-enum' | 'regex' | 'length' | 'cross-framework-id' | 'model-and-tier-both-set' | 'per-token-lane' | 'unknown-field';
/**
 * §10.2 billing lane — model ids whose lane would be PER-TOKEN (API-billed,
 * outside the subscription envelope), refused at validation. `knownModelIds`
 * membership proves an id is *recognized*, not that it is inside the
 * subscription envelope; a pin can never introduce a per-token API path
 * (§10.3 — the cost gate's write-time arm).
 *
 * The table is a closed allow/deny classification per framework. TODAY every
 * member of the closed enums rides the subscription envelope (the codex enum
 * mirrors CODEX_MODELS_SUBSCRIPTION by construction; the claude enum is the
 * CLI's subscription-login surface), so the deny sets ship empty — the seam
 * exists so the moment a per-token id joins a known-ids enum it is named here
 * and refused, instead of slipping into a launch arg as billable API spend.
 * If this table and a framework's enum ever disagree, the REFUSAL wins.
 */
export declare const PER_TOKEN_LANE_MODEL_IDS: Partial<Record<IntelligenceFramework, readonly string[]>>;
/**
 * §10.2 — refuse a model id whose billing lane would be per-token. Returns
 * null when the id rides the subscription envelope. `perTokenIds` is
 * injectable for tests (both sides of the boundary stay coverable while the
 * shipped deny sets are empty).
 */
export declare function billingLaneError(id: string, framework: IntelligenceFramework, perTokenIds?: readonly string[]): ProfileValidationError | null;
export interface ProfileValidationError {
    field: string;
    failure: ProfileFieldFailure;
    /** Human-readable named reason (operator-facing refusal text). */
    reason: string;
    /** Hard-truncated, charset-clamped prefix of the rejected value (§10.3). */
    rejectedPrefix?: string;
    /** Length of the rejected raw value (§10.3 audit field). */
    rejectedLength?: number;
}
export interface ProfilePatchInput {
    framework?: string | null;
    model?: string | null;
    modelTier?: string | null;
    escalationOverride?: string | null;
    thinkingMode?: string | null;
    effort?: string | null;
}
export interface ValidatedProfilePatch {
    framework?: IntelligenceFramework | null;
    model?: string | null;
    modelTier?: ProfileModelTier | null;
    escalationOverride?: EscalationOverride | null;
    thinkingMode?: ThinkingMode | null;
    effort?: EffortLevel | null;
}
/** §10.3 — a rejected value is arbitrary text; store only a clamped prefix. */
export declare function clampRejectedValue(value: unknown): {
    prefix: string;
    length: number;
};
/**
 * Validate a profile patch's fields against the closed enums.
 *
 * `effectiveFramework` is the framework the model id will be validated
 * against — the patch's own framework when it sets one, otherwise the
 * topic's currently-resolved framework (caller supplies it).
 *
 * Returns either the validated (narrowed) patch or the FIRST error — a
 * refusal leaves the profile unchanged (§4), so one named reason suffices.
 * `null` field values are CLEAR requests and always valid; `undefined`
 * fields are absent from the patch.
 */
export declare function validateProfileFields(patch: ProfilePatchInput, effectiveFramework: IntelligenceFramework): {
    ok: true;
    patch: ValidatedProfilePatch;
} | {
    ok: false;
    error: ProfileValidationError;
};
/**
 * §10.2 model-id clamp: regex shape + membership in the framework's closed
 * `knownModelIds` enumeration. Returns null when valid.
 */
export declare function validateModelId(id: string, framework: IntelligenceFramework): ProfileValidationError | null;
/**
 * §4 — model and modelTier are mutually exclusive; setting both (or a patch
 * whose merge RESULT would hold both) is a HARD refusal, never a silent
 * winner. Checked against the post-merge shape by the store.
 */
export declare function modelTierMutualExclusionError(merged: {
    model?: string | null;
    modelTier?: string | null;
}): ProfileValidationError | null;
//# sourceMappingURL=topicProfileValidation.d.ts.map