Skip to main content

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

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.
// config/copilot.php
'permission_approve' => env('COPILOT_PERMISSION_APPROVE', 'deny-all'),

Configurable values

ValueBehavior
"deny-all"Auto-deny everything (default)
"approve-safety"Deny only shell and write; auto-approve others
"approve-all"Auto-approve everything
falseNo handler → onPermissionRequest is required (same as official SDK)
// .env
COPILOT_PERMISSION_APPROVE="approve-safety"
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.

PermissionHandler::approveAll()

Use PermissionHandler::approveAll() to auto-approve all requests.
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.
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.
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.
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.
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.
kind: "shell" | "write" | "mcp" | "read" | "url" | "custom-tool" | "memory" | "hook"
[
  "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

[
  "sessionId" => "...",
]

Response

Return an array for the permission decision. Using the PermissionRequestResultKind class is convenient.
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.
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

MethodValueDescription
approveOnce()approve-onceApprove this request only
approveForSession()approve-for-sessionApprove all requests of the same type for this session
approveForLocation()approve-for-locationApprove all requests of the same type from this location (file path, etc.)
reject()rejectReject the request
userNotAvailable()user-not-availableUser cannot respond (non-interactive environment, etc.)
noResult()no-resultHandler cannot return a result (skip RPC call)
If you prefer Laravel\Prompts\select over Laravel\Prompts\confirm, use PermissionRequestResultKind::select() for options.
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.
return PermissionRequestResultKind::noResult();
// or ['kind' => 'no-result']
For the latest updates, see the GitHub repository.
Last modified on April 29, 2026