import type { AgentId, PermissionSet, InstalledPermission, ClaudePermissions, CursorPermissions, OpenCodePermissions, CodexPermissions } from './types.js';
import { CODEX_RULES_FILENAME } from './permissions-registry.js';
export { CODEX_RULES_FILENAME };
export type ParsedRules = PermissionSet;
export declare const COMPUTER_PERMISSION_RULE_PREFIX = "Computer";
export declare const COMPUTER_APP_GATED_VERBS: readonly ["screenshot", "describe", "get-text", "launch", "raise", "click", "right-click", "type", "type-text", "key", "drag", "scroll", "ax-action", "focus", "wait"];
export declare const COMPUTER_INPUT_GATED_VERBS: readonly ["raise", "click", "right-click", "type", "type-text", "key", "drag", "scroll", "ax-action", "focus"];
export declare function formatComputerPermissionGrantHint(bundleId?: string): string;
export declare function containsBroadGrants(rules: ParsedRules): {
    broad: string[];
    reason: string;
} | null;
/**
 * Convert canonical deny rules to Codex Starlark .rules format.
 * E.g. "Bash(git reset:*)" -> prefix_rule(pattern=["git", "reset"], decision="forbidden")
 *
 * Inverse: `toCanonical` for `PERMISSION_TARGETS.codex` reads
 * `.codex/rules/agents-deny.rules` back into `Bash(<parts>:*)`.
 */
export declare function convertDenyToCodexRules(deny: string[]): string | null;
/**
 * Parse a permission set from a YAML file.
 */
export declare function parsePermissionSet(filePath: string): PermissionSet | null;
/**
 * Discover permission sets from a repository.
 */
export declare function discoverPermissionsFromRepo(repoPath: string): Array<{
    name: string;
    path: string;
    set: PermissionSet;
}>;
/**
 * Permission group info with rule count.
 */
export interface PermissionGroupInfo {
    name: string;
    ruleCount: number;
    path: string;
}
/**
 * Discover permission groups from ~/.agents/permissions/groups/.
 * Returns groups with their rule counts.
 */
export declare function discoverPermissionGroups(): PermissionGroupInfo[];
/**
 * Get total rule count across all permission groups.
 */
export declare function getTotalPermissionRuleCount(): number;
/**
 * A permission preset recipe — names a preset and lists which groups it composes.
 * Lives at ~/.agents/permissions/presets/<name>.yaml.
 */
export interface PermissionPresetRecipe {
    name: string;
    description?: string;
    includes: string[];
}
/** Env var that selects which set recipe to apply at sync time. */
export declare const PERMISSION_PRESET_ENV_VAR = "AGENTS_PERMISSION_PRESET";
/**
 * Read a permission preset recipe by name from ~/.agents/permissions/presets/.
 * Returns null if the recipe file is missing or malformed.
 */
export declare function readPermissionPresetRecipe(name: string): PermissionPresetRecipe | null;
/**
 * Return the active permission preset name from AGENTS_PERMISSION_PRESET env var,
 * or null if unset. Caller decides the default behavior when null.
 */
export declare function getActivePermissionPresetName(): string | null;
/**
 * Build a PermissionSet from selected groups.
 * Concatenates allow/deny rules from each group.
 *
 * Uses line-by-line regex extraction instead of YAML parsing because
 * permission files often contain unescaped nested quotes that break YAML.
 */
export declare function buildPermissionsFromGroups(groupNames: string[]): PermissionSet;
/**
 * List installed permission sets from central storage.
 * User dir takes precedence; system entries are surfaced when user has no
 * same-named override.
 */
export declare function listInstalledPermissions(): InstalledPermission[];
/**
 * Install a permission set to user-level central storage.
 */
export declare function installPermissionSet(sourcePath: string, name: string): {
    success: boolean;
    error?: string;
};
/**
 * Remove a permission set from user-level central storage. System-shipped
 * sets are intentionally not deletable from user commands.
 */
export declare function removePermissionSet(name: string): {
    success: boolean;
    error?: string;
};
/**
 * Convert canonical permission set to Claude format.
 * Claude uses: { permissions: { allow: ["Bash(*)", "Read(**)"], deny: [] } }
 */
export declare function convertToClaudeFormat(set: PermissionSet): ClaudePermissions;
/**
 * Convert canonical permission set to Cursor CLI format
 * (`~/.cursor/cli-config.json` permissions.allow/deny).
 */
export declare function convertToCursorFormat(set: PermissionSet): CursorPermissions;
type CopilotToolApproval = {
    kind: 'commands';
    commandIdentifiers: string[];
} | {
    kind: 'read' | 'write';
} | {
    kind: 'mcp';
    serverName: string;
    toolName: string | null;
};
export interface CopilotPermissionsConfig {
    locations: Record<string, {
        tool_approvals?: CopilotToolApproval[];
        allowed_directories?: string[];
    }>;
}
export declare function convertToCopilotFormat(set: PermissionSet, location: string): CopilotPermissionsConfig;
/** Convert canonical Bash rules into Droid's command arrays. */
export declare function convertToDroidFormat(set: PermissionSet): {
    commandAllowlist: string[];
    commandDenylist: string[];
};
/**
 * Convert canonical permission set to OpenClaw's `tools.alsoAllow`/`tools.deny`.
 *
 * OpenClaw's allowlist is tool-level only, so ONLY blanket (whole-tool) rules
 * map — a rule is blanket iff it's a bare tool with no parens (`Bash`), it's in
 * BLANKET_BASH_FORMS, or its pattern is `*`/`**` (`Read(**)`, `Write(*)`).
 * Sub-command/path/domain rules (`Bash(git:*)`, `Write(secrets/**)`,
 * `WebFetch(domain:x)`) are SKIPPED — coarse-mapping a specific deny to a whole
 * tool would wrongly gate every use of that tool. Output arrays are deduped and
 * sorted for deterministic writes/tests.
 */
export declare function convertToOpenClawFormat(set: PermissionSet): {
    alsoAllow: string[];
    deny: string[];
};
export declare function convertToHermesFormat(set: PermissionSet): {
    command_allowlist: string[];
    approvals: {
        deny: string[];
    };
};
/**
 * Convert canonical permission set to Antigravity format.
 * Antigravity reads ~/.gemini/antigravity-cli/settings.json with
 *   { permissions: { allow: [...], deny: [...] } }
 * where each entry is action-namespaced: command(...), read_file(...),
 * write_file(...), read_url(...), mcp(...).
 * Bash maps to `command`, Read to `read_file`, Write to `write_file`,
 * WebFetch to `read_url`. Other canonical tools are skipped.
 * Note: Antigravity matches `command(npm install)` as an exact string,
 * not a prefix — `Bash(npm:*)` becomes `command(npm *)` which is a glob
 * but Antigravity upstream has a known exact-match bug for some forms.
 */
export declare function convertToAntigravityFormat(set: PermissionSet): {
    permissions: {
        allow: string[];
        deny?: string[];
    };
};
/**
 * Convert canonical permission set to Grok format.
 * Grok reads ~/.grok/config.toml with
 *   [permission]
 *   rules = [ { action = "allow", tool = "bash", pattern = "git *" }, ... ]
 * Tool names are lowercase: bash, read, edit, grep, mcptool, webfetch.
 * Canonical Write maps to Grok's `edit` tool.
 */
export declare function convertToGrokFormat(set: PermissionSet): {
    permission: {
        rules: GrokRule[];
    };
};
export type GrokRule = {
    action: 'allow' | 'deny';
    tool: string;
    pattern?: string;
};
export type KimiRule = {
    decision: 'allow' | 'deny';
    pattern: string;
};
/**
 * Convert a canonical permission set to Kimi Code's `[permission].rules` format.
 * Kimi (`~/.kimi-code/config.toml`) reads rules of the form
 *   [[permission.rules]]
 *   decision = "allow"
 *   pattern  = "Bash(git status*)"
 * Tool names are capitalized and the Bash arg-glob uses a trailing `*` (no
 * Claude `:*` separator). Without this conversion the canonical strings match
 * nothing in Kimi's engine and every tool call falls through to a prompt.
 *
 * Each `:*` Bash rule expands to TWO patterns (`cmd*` and `cmd*​/**`) so the
 * command auto-approves whether or not its arguments contain a slash — see
 * `kimiBashPatterns` for why Kimi's picomatch matcher needs both.
 */
export declare function convertToKimiFormat(set: PermissionSet): {
    permission: {
        rules: KimiRule[];
    };
};
/**
 * Convert canonical permission set to OpenCode format.
 * OpenCode uses: { permission: { bash: { "git *": "allow", "rm *": "deny" } } }
 */
export declare function convertToOpenCodeFormat(set: PermissionSet): OpenCodePermissions;
/**
 * Default extra writable roots for Codex's `workspace-write` sandbox: the
 * regenerable package/toolchain caches that build/test/install write OUTSIDE the
 * workspace (cargo registry, npm/bun/pnpm caches, GOPATH/GOCACHE, the OS cache
 * root, …). Without these, a sandboxed `cargo build` / `go build` / `npm install`
 * fails on its cache write, which is what pushes users to `--mode full` (YOLO).
 *
 * Credential dirs (`~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.config`, `~/.netrc`) are
 * deliberately EXCLUDED so the sandbox stays meaningful — this is far from
 * danger-full-access. Resolved per platform + home; each box regenerates its own
 * Codex config on sync, so the paths always match the box Codex runs on.
 */
export declare function codexDefaultWritableRoots(home?: string, platform?: NodeJS.Platform): string[];
/**
 * Merge Codex `[sandbox_workspace_write]` config, UNIONing `writable_roots` so a
 * baseline cache root never clobbers a root the user configured directly (a
 * plain object spread would overwrite the whole array), while merging scalar
 * keys (`network_access`) normally. Shared by the user- and version-scoped Codex
 * config writers so the two can't drift.
 */
export declare function mergeCodexSandboxWrite(existing: Record<string, unknown> | undefined, incoming: NonNullable<CodexPermissions['sandbox_workspace_write']>): Record<string, unknown>;
/**
 * Convert canonical permission set to Codex format.
 * Codex uses coarse-grained modes, so we infer the best fit.
 */
export declare function convertToCodexFormat(set: PermissionSet, cwd?: string): CodexPermissions;
/**
 * Read an agent's currently installed permissions.
 *
 * claude/opencode/codex return their NATIVE shape, because `agents permissions
 * list` renders those three specially — Codex especially, whose config records a
 * sandbox mode rather than a rule list, so its own fields say more than the
 * blanket grants that mode widens into.
 *
 * Every other allowlist-capable harness returns the canonical `PermissionSet`
 * that `PERMISSION_TARGETS` reads back. Before RUSH-2676 they returned `null`,
 * so permissions written for cursor, antigravity, grok, kimi, droid,
 * copilot, openclaw and hermes were reported as absent.
 */
export declare function readAgentPermissions(agentId: AgentId, scope?: 'user' | 'project', cwd?: string, options?: {
    home?: string;
}): ClaudePermissions | OpenCodePermissions | CodexPermissions | PermissionSet | null;
/**
 * Apply a permission set to Claude's settings.json.
 */
export declare function applyClaudePermissions(set: PermissionSet, scope?: 'user' | 'project', cwd?: string, merge?: boolean): {
    success: boolean;
    error?: string;
};
/**
 * Path OpenCode actually loads for global config:
 *   ~/.config/opencode/opencode.jsonc  (or .json)
 * Project: <cwd>/opencode.jsonc (or .json) at project root — not .opencode/.
 * See https://opencode.ai/docs/config/
 */
export declare function openCodeConfigPath(scope: 'user' | 'project', cwd?: string, home?: string): string;
/**
 * Apply a permission set to a specific version's home directory.
 * This writes to {versionHome}/.{agent}/settings.json (or equivalent).
 */
export declare function applyPermissionsToVersion(agentId: AgentId, set: PermissionSet, versionHome: string, merge?: boolean, cwd?: string): {
    success: boolean;
    error?: string;
};
/**
 * Convert Claude permissions back to canonical format.
 */
export declare function claudeToCanonical(perms: ClaudePermissions): PermissionSet;
/**
 * Convert OpenCode permissions back to canonical format.
 */
export declare function openCodeToCanonical(perms: OpenCodePermissions): PermissionSet;
/**
 * Convert Codex permissions back to canonical format (approximation).
 */
export declare function codexToCanonical(perms: CodexPermissions): PermissionSet;
/**
 * Export permissions from a specific config file path to canonical format,
 * auto-detecting the harness from the path.
 *
 * Detection walks `PERMISSION_TARGETS` and matches each harness's own declared
 * filename + parent directory, so it covers all 13 rather than the three
 * hardcoded `.claude`/`.opencode`/`.codex` fragments it used to know about.
 */
export declare function exportPermissionsFromPath(filePath: string): PermissionSet | null;
/**
 * Which harness owns `filePath`, by comparing against the trailing path segments
 * each registry target declares. Longer (more specific) suffixes win, so
  */
export declare function detectPermissionAgentFromPath(filePath: string): AgentId | null;
/**
 * Get the default permission set from central storage.
 */
export declare function getDefaultPermissionSet(): PermissionSet;
/**
 * Compute diff between existing and new permissions.
 * Returns { added, existing, removed } for both allow and deny rules.
 */
export declare function computePermissionsDiff(existing: PermissionSet, incoming: PermissionSet): {
    allow: {
        added: string[];
        existing: string[];
    };
    deny: {
        added: string[];
        existing: string[];
    };
};
/**
 * Merge incoming permissions into existing, deduplicating.
 */
export declare function mergePermissionSets(existing: PermissionSet, incoming: PermissionSet): PermissionSet;
/**
 * Save the default permission set.
 */
export declare function saveDefaultPermissionSet(set: PermissionSet): {
    success: boolean;
    error?: string;
};
/**
 * Harnesses whose native permission file carries a per-rule allow/deny list the
 * writer emits verbatim, so `agents doctor` can verify a group's rules survived
 * into the version home rule-for-rule. The compare is done in the harness's OWN
 * native vocabulary (Cursor `Shell(...)`, Droid command arrays, …) — NOT
 * canonical — because every target's canonical round-trip is lossy
 * (`lossyBecause` in `permissions-registry.ts`), so a canonical subset check
 * would false-diff a correctly-synced home.
 *
 * Every other allowlist harness (codex/grok/kimi/antigravity/hermes/copilot)
 * stores a lossy projection — a sandbox flag, whole-tool gate, per-directory
 * approval, or a split/merged pattern — with no faithful per-group provenance, so
 * doctor stays presence-only there and says so (`detail: 'format cannot verify
 * content'`) rather than faking `ok`.
 */
export declare const PERMISSIONS_REPRESENTABLE: ReadonlySet<AgentId>;
/**
 * True when permission GROUP `groupName` is faithfully present in `agent`'s
 * version home — every rule the group renders into that harness's native format
 * is on disk. Only meaningful for {@link PERMISSIONS_REPRESENTABLE} agents (the
 * caller keeps the lossy harnesses presence-only); returns true for a lossy
 * harness or an empty/header group so the caller does not down-rank it.
 *
 * The expected rules are re-derived from the CURRENT source group every call
 * (never a stored hash), so a rule edited/added in the source group surfaces as
 * drift even though the group name is unchanged (PHNX-3504).
 */
export declare function permissionsGroupMatches(agent: AgentId, versionHome: string, groupName: string): boolean;
