• English
  • Permissions

    Every tool call an agent makes passes a permission gate before it runs. policy() matches calls against allow / ask / deny rule sets — by tool name glob, or by the call's actual input — so you decide which actions run silently, which need a human, and which never happen. Precedence is always deny > ask > allow: a mis-ordered allow can never shadow a deny. This is how you keep an autonomous agent inside the lines you drew, with an audit trail to prove it.

    Enable it

    Pass a policy (and optionally an approval handler) to createLiteAgent:

    import { createLiteAgent, policy, bashCommand, filePath } from "@lite-agent/sdk";
    
    const agent = createLiteAgent({
      model: anthropic(),
      modelName: "claude-sonnet-4-6",
      workdir: process.cwd(),
      permission: policy({
        allow: ["read_file", "Task*"],
        ask: ["write_file", "edit_file"],
        deny: ["bash"],
      }),
      onApproval: {
        // human-in-the-loop: decide each "ask" call
        request: async (call) => (confirm(`Allow ${call.name}?`) ? "allow" : "deny"),
      },
      permissionAudit: true, // persist redacted decisions in the session event log
    });

    Name matching uses globs (Task* matches TaskCreate, TaskUpdate, …). A call that matches no rule falls through to default ("allow" unless you set it). The SDK defaults to asking before bash, write_file, edit_file, and delete_file; without an approval handler those calls are denied. Explicitly supplied custom tools are host-trusted. Supplying permission replaces this default. The low-level core remains opt-in.

    Content-level rules

    Beyond tool names, policy({ rules }) matches on call input via a when spec — conditions over dot-paths into the input like command or path. The SDK ships ready-made specifiers for its own tools:

    permission: policy({
      rules: [
        bashCommand("rm -rf*", "deny"),        // block destructive shell commands
        bashCommand("git status*", "allow"),   // `:*` desugars to a prefix match
        filePath("src/**", "allow"),           // allow file tools under ./src
        filePath("**/.env*", "deny"),          // …but never touch env files
      ],
      default: "ask",
    }),

    A rule is a PermissionRule:

    FieldDescription
    toolTool name glob (string or list).
    whenMatchSpec: dot-path → condition, ANDed across keys. Conditions: glob, regex, equals, in, startsWith, contains, not.
    whereArbitrary predicate (call, ctx) => boolean for cases a when spec can't express.
    effect"allow" | "ask" | "deny".
    id / descriptionProvenance surfaced in verdicts and audit events.
    Tip

    Bash command matching is best-effort — shell quoting and chaining can bypass prefix rules. The permission gate is defense-in-depth; the sandbox is the real containment.

    Auditing and dry-run

    • permissionAudit: true appends a redacted permission_decision event to the session log for every decision, including who made it (policy / user / auto). Secrets in tool input are masked by defaultRedactor (override with redact).
    • permissionMode: "dry-run" computes and records verdicts without blocking anything — point a candidate policy at real traffic to see what it would deny before enforcing it.

    Composing policies

    • composePolicies(...) merges policies deny-wins — a managed layer (e.g. your org's baseline) downstream users cannot loosen.
    • strictPolicy({ allow }) gives a deny-by-default posture: only what you list is permitted.
    import { composePolicies, strictPolicy, policy } from "@lite-agent/sdk";
    
    const permission = composePolicies(
      policy({ deny: ["bash"] }),                // org baseline: bash is off-limits
      strictPolicy({ allow: ["read_file", "bash"] }), // user layer tries to re-allow it…
    );                                             // …but deny wins: bash stays denied

    Children inherit the parent policy, sandbox, and tool restrictions. subagentPermission can only tighten access. All approvals share the root serial queue.

    Options

    OptionDefaultDescription
    permission—PermissionPolicy gating every tool call (policy() / strictPolicy() / composePolicies()).
    onApproval—Human-in-the-loop handler; its request(call) decides each "ask" verdict.
    permissionMode"enforce""dry-run" records decisions without blocking.
    permissionAuditfalsePersist redacted permission decisions in the session log.
    redactdefaultRedactorRedactor for audit payloads.
    subagentPermission—Permission policy applied to subagent runs.

    Related exports: policy, strictPolicy, composePolicies, bashCommand, filePath, permissionFilePolicy, defaultRedactor.

    See also

    • Sandbox — OS-level containment that composes with the gate.
    • Observability — reading permission_decision events out of the event stream.
    • Subagents — how subagentPermission gates child agents.
    • Core strategies — the PermissionPolicy strategy interface.

    Path and configuration boundaries

    Within the SDK, built-in file policies receive canonical workspace-relative paths, including absolute and symlink aliases. Write symlinks are rejected. Standalone core policies do not normalize paths for arbitrary custom tools. cwd alone does not sandbox Bash.

    permissionFilePolicy treats project rules as restrictions on host grants: a project cannot turn a default denial into permission. Grant access in managed/user files or inline rules. File tools cannot modify loaded policy paths or delete their parent directories. The local runtime additionally denies shell writes to configuration paths. Arbitrary custom tools remain trusted host code.