/** * Planner conformance — runtime guard against the strongest claim of the * framework: "the LLM cannot see mutations" plus T4's tighter sibling: * "the LLM cannot propose intents the Pack does not declare." * * The CapabilityPlanner is named security-sensitive. Two failure modes: * * 1. **Mutating tool leak** (pre-T4): a misconfigured plan exposes a * tool from `MUTATING` in `visibleReadTools`. `assertPlanReadOnly` * throws. * 2. **Allowed-intent leak** (T4 #1): a plan advertises an intent kind * not present in the Pack's `intents` declaration. The LLM * effectively learns of a new mutation the Pack never claimed to * support — guards probably do not cover it, and `policy.default` * decides the outcome. `assertPlanSubsetOfPack` throws. * * `safePlan(planner, classification, pack?)` runs both assertions on * every `plan()` call when `pack` is supplied. Adopters wrap once at * Pack construction; misconfigurations fail-loud at runtime, before the * LLM sees the leaked surface. */ import type { CapabilityPlanner, Plan } from "./planner.js"; import { type ToolClassification } from "./tool-classifier.js"; /** * The minimal pack surface `assertPlanSubsetOfPack` / `safePlan` need: the * declared `intents` tuple. Accepting this (rather than a full `PackV0`) lets a * Pack break the planner↔pack construction cycle — a Pack can declare its * `intents` tuple in `capabilities.ts` and pass it to `safePlan` BEFORE the full * `PackV0` value (which references the planner) is built (024 T4). A full * `PackV0` is structurally assignable, so every existing full-pack caller is * unaffected. */ export interface PackIntentsSurface { readonly intents: readonly string[]; } export declare class PlanConformanceError extends Error { readonly mutatingToolsLeaked: ReadonlyArray; readonly intentsLeaked: ReadonlyArray; constructor(mutatingToolsLeaked: ReadonlyArray, intentsLeaked?: ReadonlyArray); } /** * Assert that no MUTATING tool name appears in `plan.visibleReadTools`. * Throws `PlanConformanceError` listing the offenders, otherwise returns * normally. * * Pure function — safe to call anywhere on the hot path. */ export declare function assertPlanReadOnly(plan: Plan, classification: ToolClassification): void; /** * T4 (#1, top-priority F): assert that every intent in `plan.allowedIntents` * is declared in `pack.intents`. Catches a planner that advertises a * mutation the Pack never claimed to handle — typically a refactoring * regression (intent renamed in the Pack's policy but the planner kept * the old name). * * Pure function — throws `PlanConformanceError` on first violation set. */ export declare function assertPlanSubsetOfPack(plan: Plan, pack: PackIntentsSurface): void; /** * Wrap a CapabilityPlanner so its output is checked against the * ToolClassification — and (T4) optionally against the Pack's `intents` * declaration — on every `plan()` call. Misconfigurations throw * `PlanConformanceError` synchronously — the LLM never sees a leaked * MUTATING tool or an intent absent from the Pack. * * Adopters typically wire this once at Pack construction: * * const planner = safePlan(rawPlanner, MY_TOOL_CLASSIFICATION, myPack); * * The original `staticPlanner` and bare planner authoring stay untouched * for tests that intentionally drive misconfigurations. */ export declare function safePlan(planner: CapabilityPlanner, classification: ToolClassification, pack?: PackIntentsSurface): CapabilityPlanner; //# sourceMappingURL=planner-conformance.d.ts.map