> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Permission request

> Control how Laravel Copilot SDK handles permission requests with defaults, built-in handlers, and custom approval callbacks.

## Permission request

## Default behavior (`deny-all`)

If `permission_approve` is set to `"deny-all"` (default) in `config/copilot.php`, all permission requests are automatically **denied** when you use `Copilot::run()` or `Copilot::start()`.

This is a safe default when your main use case is text generation, where permissions are usually unnecessary.

```php theme={null}
// config/copilot.php
'permission_approve' => env('COPILOT_PERMISSION_APPROVE', 'deny-all'),
```

## Configurable values

| Value              | Behavior                                                              |
| ------------------ | --------------------------------------------------------------------- |
| `"deny-all"`       | Auto-deny everything (**default**)                                    |
| `"approve-safety"` | Deny only `shell` and `write`; auto-approve others                    |
| `"approve-all"`    | Auto-approve everything                                               |
| `false`            | No handler → `onPermissionRequest` is required (same as official SDK) |

```php theme={null}
// .env
COPILOT_PERMISSION_APPROVE="approve-safety"
```

<Warning>
  If you allow user prompt input, `"approve-safety"` and `"approve-all"` are dangerous.
  Always use `false` or `"deny-all"`.
  Even read-only access can be risky because it can expose your Laravel project code.
</Warning>

## PermissionHandler::approveAll()

Use `PermissionHandler::approveAll()` to auto-approve all requests.

```php theme={null}
use Revolution\Copilot\Facades\Copilot;
use Revolution\Copilot\Support\PermissionHandler;
use Revolution\Copilot\Types\SessionConfig;

$config = new SessionConfig(
    onPermissionRequest: PermissionHandler::approveAll(),
);

$response = Copilot::run(prompt: 'Hello', config: $config);
```

## PermissionHandler::approveSafety()

Use `PermissionHandler::approveSafety()` to deny only high-risk permissions (`shell`, `write`) and auto-approve others.

```php theme={null}
use Revolution\Copilot\Facades\Copilot;
use Revolution\Copilot\Support\PermissionHandler;
use Revolution\Copilot\Types\SessionConfig;

$config = new SessionConfig(
    onPermissionRequest: PermissionHandler::approveSafety(),
);

$response = Copilot::run(prompt: 'Hello', config: $config);
```

This still may not be fully safe.
If you need precise control, write a custom handler and decide based on `$request['kind']`.

## PermissionHandler::denyAll()

Use `PermissionHandler::denyAll()` to deny everything.

```php theme={null}
use Revolution\Copilot\Support\PermissionHandler;
use Revolution\Copilot\Types\SessionConfig;

$config = new SessionConfig(
    onPermissionRequest: PermissionHandler::denyAll(),
);
```

## Direct client usage

When you use `CopilotClient` directly, specifying `onPermissionRequest` is **required**, just like the official SDK.

```php theme={null}
use Revolution\Copilot\Client;
use Revolution\Copilot\Support\PermissionHandler;

$client = new Client([
    'cli_path' => 'copilot',
    'cli_args' => [],
    'cwd' => base_path(),
    'log_level' => 'info',
    'env' => null,
]);
$client->start();

// onPermissionRequest is required
$session = $client->createSession([
    'onPermissionRequest' => PermissionHandler::approveSafety(),
]);

// If omitted, InvalidArgumentException is thrown
// $session = $client->createSession([]); // Error!
```

## Custom handler

To control approval or rejection per request type, pass a closure.
`$request` and `$invocation` are arrays like the examples below.

```php theme={null}
use Illuminate\Support\Facades\Artisan;
use Revolution\Copilot\Contracts\CopilotSession;
use Revolution\Copilot\Facades\Copilot;
use Revolution\Copilot\Support\PermissionRequestResultKind;
use Revolution\Copilot\Types\SessionConfig;

use function Laravel\Prompts\{confirm, note, spin, text};

Artisan::command('copilot:chat', function () {
    $config = new SessionConfig(
        onPermissionRequest: function (array $request, array $invocation) {
            $confirm = confirm(
                label: 'Do you accept the requested permissions?',
            );
            if ($confirm) {
                return PermissionRequestResultKind::approveOnce();
            } else {
                return PermissionRequestResultKind::reject();
            }
        },
    );

    Copilot::start(function (CopilotSession $session) use ($config) {
        while (true) {
            $prompt = text(
                label: 'Enter your prompt',
                placeholder: 'Ask me anything...',
                required: true,
                hint: 'Ctrl+C to exit',
            );

            $response = spin(
                callback: fn () => $session->sendAndWait($prompt),
                message: 'Waiting for Copilot response...',
            );

            note($response->content());
        }
    }, config: $config);
});
```

### `$request`

Other than `kind` and `toolCallId`, fields vary by `kind`.

```text theme={null}
kind: "shell" | "write" | "mcp" | "read" | "url" | "custom-tool" | "memory" | "hook"
```

```php theme={null}
[
  "kind" => "shell",
  "toolCallId" => "toolu_...",
  "fullCommandText" => "...",
  "intention" => "Run copilot:ping to test permission request",
  "commands" => [
    [
      "identifier" => "bash",
      "readOnly" => false,
    ]
  ]
  "possiblePaths" => [],
  "possibleUrls" => [],
  "hasWriteFileRedirection" => false,
  "canOfferSessionApproval" => false,
]
```

### `$invocation`

```php theme={null}
[
  "sessionId" => "...",
]
```

## Response

Return an array for the permission decision.
Using the `PermissionRequestResultKind` class is convenient.

```php theme={null}
return PermissionRequestResultKind::approveOnce();
return PermissionRequestResultKind::reject();
```

## Protocol details

In Protocol v3 (current default), permission requests are broadcast as session events (`permission.requested`) instead of JSON-RPC requests.
The SDK handles this internally and responds through the `session.permissions.handlePendingPermissionRequest` RPC.

**How you use `SessionConfig` does not change.**
You only need to pass a handler to `onPermissionRequest`, and the SDK absorbs protocol differences.

## PermissionRequestResultKind

You can return the `['kind' => 'approve-once']` format directly, but it is easier to read with `PermissionRequestResultKind`.

```php theme={null}
use Revolution\Copilot\Support\PermissionRequestResultKind;

$confirm = confirm(
    label: 'Do you accept the requested permissions?',
);

if ($confirm) {
    return PermissionRequestResultKind::approveOnce();
} else {
    return PermissionRequestResultKind::reject();
}
```

### Available methods

| Method                 | Value                  | Description                                                                |
| ---------------------- | ---------------------- | -------------------------------------------------------------------------- |
| `approveOnce()`        | `approve-once`         | Approve this request only                                                  |
| `approveForSession()`  | `approve-for-session`  | Approve all requests of the same type for this session                     |
| `approveForLocation()` | `approve-for-location` | Approve all requests of the same type from this location (file path, etc.) |
| `reject()`             | `reject`               | Reject the request                                                         |
| `userNotAvailable()`   | `user-not-available`   | User cannot respond (non-interactive environment, etc.)                    |
| `noResult()`           | `no-result`            | Handler cannot return a result (skip RPC call)                             |

If you prefer `Laravel\Prompts\select` over `Laravel\Prompts\confirm`, use `PermissionRequestResultKind::select()` for options.

```php theme={null}
use Revolution\Copilot\Support\PermissionRequestResultKind;
use function Laravel\Prompts\select;

$select = select(
    label: 'Do you accept the requested permissions?',
    options: PermissionRequestResultKind::select(),
);

return ['kind' => $select];
```

### no-result

If a handler cannot return a result (for example, in a non-interactive environment), you can return `no-result`.
When `no-result` is returned, the RPC call is skipped and Copilot CLI applies its default deny behavior.

```php theme={null}
return PermissionRequestResultKind::noResult();
// or ['kind' => 'no-result']
```

<Info>
  For the latest updates, see the [GitHub repository](https://github.com/invokable/laravel-copilot-sdk).
</Info>


## Related topics

- [Streaming events](/en/packages/laravel-copilot-sdk/streaming-events.md)
- [Cloud Sessions](/en/packages/laravel-copilot-sdk/cloud-sessions.md)
- [GitHub Copilot SDK for Laravel](/en/packages/laravel-copilot-sdk/index.md)
- [Socialite - Laravel Bluesky](/en/packages/laravel-bluesky/socialite.md)
- [GitHub Actions - GitHub Copilot SDK for Laravel](/en/packages/laravel-copilot-sdk/github-actions.md)
