권한 요청
기본 동작(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"
사용자 입력 프롬프트를 받는 경우, "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']