/** * Permission system for Franklin. * Controls which tools can execute automatically vs. require user approval. */ export type PermissionBehavior = 'allow' | 'deny' | 'ask'; export interface PermissionRules { allow: string[]; deny: string[]; ask: string[]; } export type PermissionMode = 'default' | 'trust' | 'deny-all' | 'plan'; export interface PermissionDecision { behavior: PermissionBehavior; reason?: string; } export declare class PermissionManager { private rules; private mode; private sessionAllowed; private promptFn?; constructor(mode?: PermissionMode, promptFn?: (toolName: string, description: string) => Promise<'yes' | 'no' | 'always'>); /** * Check if a tool can be used. Returns the decision. */ check(toolName: string, input: Record): Promise; /** * Prompt the user interactively for permission. * Uses injected promptFn (Ink UI) when available, falls back to readline. * pendingCount: how many more operations of this type are waiting (including this one). * Returns true if allowed, false if denied. */ promptUser(toolName: string, input: Record, pendingCount?: number): Promise; /** * Persist a tool name to the user's allow rules so future sessions * skip the prompt. Idempotent: appends to the existing * `allow: []` array only if not already present. * * Why this exists: pre-2026-05-12, "always" in the UI prompt was a * misnomer — it only added the tool to the in-memory `sessionAllowed` * Set, which evaporated on every `franklin start`. Users reported * being prompted repeatedly across sessions despite hitting [a] each * time. Persistence here makes "always" actually mean always. * * Best-effort writes (try/catch around fs) — a logging failure should * never block the paid call that just got approved. */ private persistAllowRule; private loadRules; private matchesRule; private getPrimaryInputValue; private globMatch; private sessionKey; private describeAction; }