权限请求
默认行为(deny-all)
当 config/copilot.php 的 permission_approve 为 "deny-all"(默认)时,Copilot::run() 和 Copilot::start() 中的权限请求会被自动 拒绝。
由于以文本生成为主的用途通常不需要权限,因此这是一个安全的默认值。
// config/copilot.php
'permission_approve' => env('COPILOT_PERMISSION_APPROVE', 'deny-all'),
可配置的值
| 值 | 行为 |
|---|
"deny-all" | 全部自动拒绝(默认) |
"approve-safety" | 只拒绝 shell 和 write,其他自动许可 |
"approve-all" | 全部自动许可 |
false | 无处理器。onPermissionRequest 是必需的(与官方 SDK 相同) |
// .env
COPILOT_PERMISSION_APPROVE="approve-safety"
如果接收用户输入的 prompt,"approve-safety" 与 "approve-all" 是危险的。
请务必使用 false 或 "deny-all"。
即使是 read-only 权限,Laravel 项目的代码也可能被读取。
PermissionHandler::approveAll()
PermissionHandler::approveAll() 会自动许可所有请求。
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()
PermissionHandler::approveSafety() 只拒绝高风险的权限(shell、write),其他自动许可。
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);
即便如此也未必完全安全。
若要严格控制,请实现自定义处理器,通过查看 $request['kind'] 进行判断。
PermissionHandler::denyAll()
PermissionHandler::denyAll() 会拒绝所有请求。
use Revolution\Copilot\Support\PermissionHandler;
use Revolution\Copilot\Types\SessionConfig;
$config = new SessionConfig(
onPermissionRequest: PermissionHandler::denyAll(),
);
直接使用 Client 的情况
直接使用 CopilotClient 时,与官方 SDK 一样,onPermissionRequest 是 必需的。
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 是必需的
$session = $client->createSession([
'onPermissionRequest' => PermissionHandler::approveSafety(),
]);
// 省略会抛出 InvalidArgumentException
// $session = $client->createSession([]); // Error!
自定义处理器
要按请求类型控制许可 / 拒绝,请传入闭包。
$request 和 $invocation 是如下的数组。
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
kind 和 toolCallId 以外的字段会因 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
权限判断结果以数组形式返回。
使用 PermissionRequestResultKind 类可以更易读。
return PermissionRequestResultKind::approveOnce();
return PermissionRequestResultKind::reject();
协议详情
在 Protocol v3(当前默认)中,权限请求不再作为 JSON-RPC 请求,而是作为会话事件(permission.requested)投递。
SDK 内部会处理它,并通过 session.permissions.handlePendingPermissionRequest RPC 进行响应。
SessionConfig 的使用方式不变。
只要向 onPermissionRequest 传入处理器,协议差异会由 SDK 吸收。
PermissionRequestResultKind
你也可以直接以 ['kind' => 'approve-once'] 的形式返回,但使用 PermissionRequestResultKind 会更直观。
use Revolution\Copilot\Support\PermissionRequestResultKind;
$confirm = confirm(
label: 'Do you accept the requested permissions?',
);
if ($confirm) {
return PermissionRequestResultKind::approveOnce();
} else {
return PermissionRequestResultKind::reject();
}
可用的方法
| 方法 | 值 | 说明 |
|---|
approveOnce() | approve-once | 仅本次请求许可 |
approveForSession() | approve-for-session | 在本会话内全部许可同类请求 |
approveForLocation() | approve-for-location | 从该位置(文件路径等)发起的所有同类请求都许可 |
reject() | reject | 拒绝请求 |
userNotAvailable() | user-not-available | 用户无法响应的状态(如非交互环境) |
noResult() | no-result | 当处理器无法返回结果时(跳过 RPC 调用) |
如果想使用 Laravel\Prompts\select,可以通过 PermissionRequestResultKind::select() 获取选项。
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
当处理器无法返回结果时(例如非交互环境),可以返回 no-result。
返回 no-result 会跳过 RPC 调用,采用 Copilot CLI 侧的默认拒绝。
return PermissionRequestResultKind::noResult();
// 或 ['kind' => 'no-result']