# Permission Handling

> Control which tools agents can run automatically and which require explicit approval.

- 网址：https://funcoding.ai/agents/cline/sdk/guides/permission-handling/
- 来源：Cline 官方文档原文（英文），Apache-2.0 许可，同步于 2026-10-11
- 官方原文：https://docs.cline.bot/sdk/guides/permission-handling

---
Tool policies control whether tools are enabled and whether they run without approval. Tool names not listed in `toolPolicies` default to enabled and auto-approved, so set policies explicitly for tools that need review.

## Tool Policies

The simplest way to manage permissions is through tool policies. Set them per-tool when creating an agent:

```typescript
import { Agent, createBuiltinTools } from "@cline/sdk"

const agent = new Agent({
  // ...config
  tools: createBuiltinTools({ cwd: process.cwd() }),
  toolPolicies: {
    read_files: { autoApprove: true },       // Always run without asking
    search_codebase: { autoApprove: true },  // Always run without asking
    editor: { autoApprove: false },          // Always ask before running
    run_commands: { autoApprove: false },    // Always ask before running
  },
})
```

### Policy Options

| Policy | Effect |
|--------|--------|
| `{ autoApprove: true }` | Tool executes immediately without approval |
| `{ autoApprove: false }` | Tool waits for approval before executing |
| `{ enabled: false }` | Tool is completely disabled (model won't see it) |
| No policy set | Defaults to enabled and auto-approved |

## Auto-Approve Everything

For trusted environments (CI pipelines, sandboxed containers, development scripts):

```typescript
const agent = new Agent({
  // ...config
  tools: allTools,
  toolPolicies: Object.fromEntries(
    allTools.map((t) => [t.name, { autoApprove: true }])
  ),
})
```

Or with ClineCore:

```typescript
const session = await cline.start({
  config: {
    enableTools: true,
  },
  toolPolicies: {
    run_commands: { autoApprove: true },
    editor: { autoApprove: true },
    read_files: { autoApprove: true },
    apply_patch: { autoApprove: true },
    search_codebase: { autoApprove: true },
    fetch_web_content: { autoApprove: true },
  },
  capabilities: {
    requestToolApproval: async () => ({ approved: true }),
  },
  // ...
})
```

<div class="callout callout-warning">

Auto-approving all tools means the agent can execute any shell command, modify any file, and make network requests without your review. Only use this in environments where the agent's actions are either sandboxed or fully trusted.

</div>

## Interactive Approval

For applications that need human oversight, implement a custom approval handler:

```typescript
import { ClineCore } from "@cline/sdk"
import * as readline from "readline"

const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
const ask = (q: string) => new Promise<string>((res) => rl.question(q, res))

const cline = await ClineCore.create({
  clientName: "interactive-app",
  capabilities: {
    requestToolApproval: async (request) => {
      console.log(`\nTool: ${request.toolName}`)
      console.log(`Input: ${JSON.stringify(request.input, null, 2)}`)

      const answer = await ask("Approve? (y/n): ")
      return { approved: answer.toLowerCase() === "y" }
    },
  },
})
```

## Tiered Permissions

A practical middle ground: auto-approve read-only operations, require approval for writes:

```typescript
const READ_TOOLS = new Set(["read_files", "search_codebase", "fetch_web_content"])
const WRITE_TOOLS = new Set(["run_commands", "editor", "apply_patch"])

const toolPolicies: Record<string, { autoApprove: boolean }> = {}

for (const tool of READ_TOOLS) {
  toolPolicies[tool] = { autoApprove: true }
}
for (const tool of WRITE_TOOLS) {
  toolPolicies[tool] = { autoApprove: false }
}
```

## Conditional Approval Logic

Approve based on what the tool is actually doing, not just which tool it is:

```typescript
const cline = await ClineCore.create({
  clientName: "smart-approval",
  capabilities: {
    requestToolApproval: async (request) => {
      // request.input is the model's raw tool input, so check its shape first.
      const input = request.input as { commands?: unknown; files?: unknown }

      // Auto-approve a fixed set of commands that only read: { commands: string[] }.
      // Match whole commands, because flags can change what a command does.
      // Git can also run programs named in a repository's config (for example
      // core.fsmonitor), so approve Git commands only in repositories you trust.
      if (request.toolName === "run_commands" && Array.isArray(input.commands)) {
        const safeCommands = new Set(["ls", "git status", "git log --oneline -20"])
        if (input.commands.every((cmd) => typeof cmd === "string" && safeCommands.has(cmd.trim()))) {
          return { approved: true }
        }
      }

      // Auto-approve reads in specific directories: { files: { path: string }[] }.
      if (request.toolName === "read_files" && Array.isArray(input.files)) {
        const isAllowed = (file: unknown) => {
          const path = (file as { path?: unknown })?.path
          return typeof path === "string" && !path.includes("..") &&
            (path.startsWith("/src/") || path.startsWith("/tests/"))
        }
        if (input.files.every(isAllowed)) {
          return { approved: true }
        }
      }

      // Everything else requires manual approval
      console.log(`Approval needed: ${request.toolName}`)
      console.log(`Input: ${JSON.stringify(request.input)}`)
      return { approved: false }
    },
  },
})
```

## What Happens When a Tool is Rejected

When approval is denied, the agent receives a rejection message and can adjust its approach. It might:
- Ask the user for clarification
- Try a different tool to accomplish the same goal
- Modify its approach and try again with different parameters
- Give up on that subtask and move on

The agent does not get stuck in a loop. The rejection counts as a response, and the agent proceeds with its next iteration.
