/** * Safety layer for the desktop-control extension. * * - Session policy: by DEFAULT there is no confirmation prompt at all — * enabling the extension (enabledNativeExtensions contains * "desktop-control") is the explicit user consent, and every mutating * action runs immediately (including headless/serve/RPC sessions). * Opt-in SPECTRAL_DESKTOP_REQUIRE_CONFIRM=1|true restores the interactive * confirmation gate: the first mutating call asks once (ctx.hasUI); an * explicit user decision applies to the whole session (grant or lockout). * AUTO-deny causes — headless, non-TTY terminal, dialog timeout, * cancelled/unsupported client — deny only the current action with a * precise reason and never lock the session. * - Dry-run: SPECTRAL_DESKTOP_DRY_RUN=1|true returns action plans without * touching the OS (no injection, no screenshots). * - Allowlist: SPECTRAL_DESKTOP_ALLOWLIST="Safari, Code" restricts mutating * actions to sessions where the frontmost app (or the focus target's owner) * matches one of the comma-separated names (case-insensitive, substring). */ import type { DesktopAction } from "./types.js"; export declare const DESKTOP_CONTROL_TOOLS: readonly ["desktop_mouse_move", "desktop_mouse_click", "desktop_mouse_drag", "desktop_scroll", "desktop_type_text", "desktop_press_key", "desktop_hotkey", "desktop_focus_window"]; export type DesktopControlToolName = (typeof DESKTOP_CONTROL_TOOLS)[number]; export declare function isDesktopControlTool(name: string): boolean; export declare function isDryRun(env?: NodeJS.ProcessEnv): boolean; /** Opt-in confirmation gate: SPECTRAL_DESKTOP_REQUIRE_CONFIRM=1|true restores the per-session prompt. */ export declare function isRequireConfirm(env?: NodeJS.ProcessEnv): boolean; export declare function parseAllowlist(raw: string | undefined): string[]; /** Case-insensitive substring match in both directions ("Safari" matches "com.apple.Safari"). */ export declare function matchesAllowlist(app: string, allowlist: string[]): boolean; export interface PolicyDecision { allowed: boolean; reason?: string; } /** * Why a confirmation dialog ended without an explicit human decision. * These are AUTO-deny causes: they deny the current action only and must * never lock the whole session (unlike an explicit user denial). */ export type ConfirmFailureKind = "no_tty" | "timeout" | "cancelled"; export interface ConfirmFailure { kind: ConfirmFailureKind; /** For kind "timeout": how long the dialog waited before timing out (ms). */ timeoutMs?: number; } export interface ConfirmAllowed { allowed: true; } export interface ConfirmDenied { allowed: false; /** * Why the dialog did not end in an explicit human denial. Omit (or return * a plain `false`) when a real user answered "No" in an interactive dialog. */ failure?: ConfirmFailure; } /** * Result of the `confirm` callback: a plain boolean (legacy — always treated * as an explicit interactive user decision) or a rich result that can report * AUTO-deny causes (`failure`) so the session is not locked by them. */ export type ConfirmResult = boolean | ConfirmAllowed | ConfirmDenied; export interface AuthorizeInput { dryRun: boolean; hasUI: boolean; /** One-line description of the action for the confirmation dialog. */ actionSummary: string; confirm?: (title: string, message: string) => Promise; } /** Precise auto-deny message per cause (headless/no-UI are handled in authorize). */ export declare function autoDenyReason(failure: ConfirmFailure): string; /** * Session-scoped allow/deny state machine for mutating desktop actions. * Pure logic — the confirm callback is injected so tests can stub it. * Only used when SPECTRAL_DESKTOP_REQUIRE_CONFIRM is enabled (opt-in). * * Only an EXPLICIT user denial (the confirm callback reporting a human "No") * moves the session into the locked "denied" state. AUTO-deny causes — no UI, * non-TTY terminal, dialog timeout, cancelled/unsupported client — deny only * the current action (fail-closed) and leave the session undecided. */ export declare class DesktopActionPolicy { private state; get decided(): boolean; get isGranted(): boolean; authorize(input: AuthorizeInput): Promise; } /** One-line human-readable action summary (used in confirm dialogs, plans and result text). */ export declare function describeAction(action: DesktopAction): string; //# sourceMappingURL=safety.d.ts.map