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

# 세션 훅

> SessionHooks로 Copilot 세션 라이프사이클에 처리를 끼워 넣어, 실행 제어, 감사, 복구 로직을 구현합니다.

## 세션 훅

`hooks`를 사용하면 Copilot 세션의 각 라이프사이클에 처리를 끼워 넣을 수 있습니다.
도구 실행 제어, 감사 로그, 프롬프트 보강, 에러 처리 등을 코어 구현을 변경하지 않고 추가할 수 있습니다.

## 훅의 흐름

```mermaid theme={null}
flowchart LR
    A[Session starts] -->|onSessionStart| B[User prompt]
    B -->|onUserPromptSubmitted| C[Pre tool]
    C -->|onPreToolUse| D[Tool execution]
    D -->|onPostToolUse| E{Continue?}
    E -->|yes| C
    E -->|no| F[Session ends]
    F -->|onSessionEnd| G((Done))
    C -. error .-> H[onErrorOccurred]
    D -. error .-> H
```

## 기본 사용법

```php theme={null}
use Revolution\Copilot\Contracts\CopilotSession;
use Revolution\Copilot\Facades\Copilot;
use Revolution\Copilot\Types\SessionHooks;
use Revolution\Copilot\Types\Hooks\ErrorOccurredHookInput;
use Revolution\Copilot\Types\Hooks\ErrorOccurredHookOutput;
use Revolution\Copilot\Types\Hooks\PostToolUseHookInput;
use Revolution\Copilot\Types\Hooks\PostToolUseHookOutput;
use Revolution\Copilot\Types\Hooks\PreToolUseHookInput;
use Revolution\Copilot\Types\Hooks\PreToolUseHookOutput;
use Revolution\Copilot\Types\Hooks\SessionEndHookInput;
use Revolution\Copilot\Types\Hooks\SessionEndHookOutput;
use Revolution\Copilot\Types\Hooks\SessionStartHookInput;
use Revolution\Copilot\Types\Hooks\SessionStartHookOutput;
use Revolution\Copilot\Types\Hooks\UserPromptSubmittedHookInput;
use Revolution\Copilot\Types\Hooks\UserPromptSubmittedHookOutput;

Copilot::start(function (CopilotSession $session) {
    $response = $session->sendAndWait(prompt: 'README의 요점을 정리해줘');
    dump($response->content());
}, config: [
    'model' => 'gpt-5',
    'hooks' => new SessionHooks(
        onSessionStart: function (SessionStartHookInput $input): ?SessionStartHookOutput {
            return new SessionStartHookOutput(
                additionalContext: "Project root: {$input->cwd}",
            );
        },

        onUserPromptSubmitted: function (UserPromptSubmittedHookInput $input): ?UserPromptSubmittedHookOutput {
            if (str_starts_with($input->prompt, '/fix')) {
                return new UserPromptSubmittedHookOutput(
                    modifiedPrompt: '현재 에러를 수정하고 변경점을 요약해줘.',
                );
            }

            return null;
        },

        onPreToolUse: function (PreToolUseHookInput $input): ?PreToolUseHookOutput {
            $blocked = ['bash', 'shell', 'delete_file'];

            if (in_array($input->toolName, $blocked, true)) {
                return new PreToolUseHookOutput(
                    permissionDecision: 'deny',
                    permissionDecisionReason: "{$input->toolName}은 이 환경에서 허용되지 않습니다",
                );
            }

            return new PreToolUseHookOutput(permissionDecision: 'allow');
        },

        onPostToolUse: function (PostToolUseHookInput $input): ?PostToolUseHookOutput {
            if ($input->toolName === 'read_file') {
                return new PostToolUseHookOutput(
                    additionalContext: '필요하다면 관련 파일도 탐색해서 비교해주세요.',
                );
            }

            return null;
        },

        onErrorOccurred: function (ErrorOccurredHookInput $input): ?ErrorOccurredHookOutput {
            if ($input->errorContext === 'model_call' && $input->recoverable) {
                return new ErrorOccurredHookOutput(
                    errorHandling: 'retry',
                    retryCount: 2,
                    userNotification: '일시적인 모델 에러로 재시도합니다.',
                );
            }

            return null;
        },

        onSessionEnd: function (SessionEndHookInput $input): ?SessionEndHookOutput {
            if ($input->reason !== 'complete') {
                return new SessionEndHookOutput(
                    sessionSummary: "Session ended with reason: {$input->reason}",
                );
            }

            return null;
        },
    ),
]);
```

## 사용 가능한 훅

| Hook                    | 발화 타이밍                              | 주요 용도                     |
| ----------------------- | ----------------------------------- | ------------------------- |
| `onSessionStart`        | 세션 시작(`new` / `resume` / `startup`) | 초기 컨텍스트 주입, 설정 오버라이드      |
| `onUserPromptSubmitted` | 사용자 프롬프트 전송 시                       | 프롬프트 보강, 템플릿 확장, 입력 필터    |
| `onPreToolUse`          | 도구 실행 전                             | 허용/거부/확인 요청, 인자 수정, 출력 억제 |
| `onPostToolUse`         | 도구 실행 후                             | 결과 수정, 민감정보 마스킹, 감사 로그    |
| `onErrorOccurred`       | 세션 내 에러 발생 시                        | 재시도, 알림, 에러 분류            |
| `onSessionEnd`          | 세션 종료 시                             | 정리, 메트릭, 종료 요약            |

`null`을 반환하면 기본 동작이 계속됩니다.

## 대표적인 사용 사례

### 1) Permission control(실행 제어)

* `onPreToolUse`에서 허용 도구를 allow-list 방식으로 설정
* 파괴적 조작은 `permissionDecision: 'ask'`로 사람에게 승인 요청
* `permissionDecisionReason`으로 거부 이유를 명시
* `toolName`의 후보는 [Tools](/ko/packages/laravel-copilot-sdk/tools)의 목록에서 확인(예: `view`, `glob`, `bash`)

### 2) Auditing / compliance(감사)

* 라이프사이클 훅을 조합해 감사 이벤트를 수집
* 수집 데이터는 세션 ID 단위로 영속화

### 3) Prompt enrichment(입력 보강)

* `onSessionStart`에서 프로젝트 정보(언어, FW, 규약)를 `additionalContext`에 추가
* `onUserPromptSubmitted`에서 단축키(`/fix`, `/test`)를 확장

### 4) Result filtering(결과 정형화)

* `onPostToolUse`에서 API key / token / password 등을 마스킹
* 너무 긴 결과는 요약하고 필요 시에만 상세를 반환

### 5) Error recovery(장애 복구)

* `onErrorOccurred`에서 `model_call`이면서 `recoverable=true`일 때만 `retry`
* 비복구 계열은 `userNotification`으로 사용자에게 간결하게 알림

### 6) Session metrics(계측)

* `onSessionStart`에서 시작 시각을 기록
* `onPreToolUse` / `onUserPromptSubmitted`에서 카운터 업데이트
* `onSessionEnd`에서 소요 시간, 도구 횟수, 종료 이유를 출력

## Hook input / output 타입

### 공통 입력(`BaseHookInput`)

| 프로퍼티        | 타입       | 설명               |
| ----------- | -------- | ---------------- |
| `timestamp` | `int`    | 훅 발화 시각(Unix ms) |
| `cwd`       | `string` | 현재 작업 디렉터리       |

### `PreToolUseHookInput`

| 프로퍼티       | 타입       | 설명        |
| ---------- | -------- | --------- |
| `toolName` | `string` | 실행 예정 도구명 |
| `toolArgs` | `mixed`  | 실행 예정 인자  |

### `PreToolUseHookOutput`

| 프로퍼티                       | 타입        | 설명                       |
| -------------------------- | --------- | ------------------------ |
| `permissionDecision`       | `?string` | `allow` / `deny` / `ask` |
| `permissionDecisionReason` | `?string` | 거부/확인 시 이유               |
| `modifiedArgs`             | `mixed`   | 덮어쓴 인자                   |
| `additionalContext`        | `?string` | 추가 컨텍스트                  |
| `suppressOutput`           | `?bool`   | 도구 출력을 억제                |

### `PostToolUseHookInput`

| 프로퍼티         | 타입                        | 설명      |
| ------------ | ------------------------- | ------- |
| `toolName`   | `string`                  | 실행된 도구명 |
| `toolArgs`   | `mixed`                   | 실행 시 인자 |
| `toolResult` | `ToolResultObject\|array` | 도구 결과   |

### `PostToolUseHookOutput`

| 프로퍼티                | 타입                              | 설명           |
| ------------------- | ------------------------------- | ------------ |
| `modifiedResult`    | `ToolResultObject\|array\|null` | 수정된 결과       |
| `additionalContext` | `?string`                       | 추가 컨텍스트      |
| `suppressOutput`    | `?bool`                         | 도구 결과 표시를 억제 |

### `UserPromptSubmittedHookInput`

| 프로퍼티     | 타입       | 설명          |
| -------- | -------- | ----------- |
| `prompt` | `string` | 사용자 입력 프롬프트 |

### `UserPromptSubmittedHookOutput`

| 프로퍼티                | 타입        | 설명       |
| ------------------- | --------- | -------- |
| `modifiedPrompt`    | `?string` | 수정된 프롬프트 |
| `additionalContext` | `?string` | 보완 컨텍스트  |
| `suppressOutput`    | `?bool`   | 응답 표시 억제 |

### `SessionStartHookInput`

| 프로퍼티            | 타입        | 설명                           |
| --------------- | --------- | ---------------------------- |
| `source`        | `string`  | `startup` / `resume` / `new` |
| `initialPrompt` | `?string` | 초기 프롬프트                      |

### `SessionStartHookOutput`

| 프로퍼티                | 타입        | 설명              |
| ------------------- | --------- | --------------- |
| `additionalContext` | `?string` | 세션 초기 컨텍스트      |
| `modifiedConfig`    | `?array`  | 세션 설정의 부분 오버라이드 |

### `SessionEndHookInput`

| 프로퍼티           | 타입        | 설명                                                       |
| -------------- | --------- | -------------------------------------------------------- |
| `reason`       | `string`  | `complete` / `error` / `abort` / `timeout` / `user_exit` |
| `finalMessage` | `?string` | 최종 메시지                                                   |
| `error`        | `?string` | 종료 시 에러                                                  |

### `SessionEndHookOutput`

| 프로퍼티             | 타입        | 설명        |
| ---------------- | --------- | --------- |
| `suppressOutput` | `?bool`   | 최종 출력 억제  |
| `cleanupActions` | `?array`  | 실행한 정리 정보 |
| `sessionSummary` | `?string` | 세션 요약     |

### `ErrorOccurredHookInput`

| 프로퍼티           | 타입        | 설명                                                             |
| -------------- | --------- | -------------------------------------------------------------- |
| `error`        | `string`  | 에러 메시지                                                         |
| `errorContext` | `?string` | `model_call` / `tool_execution` / `system` / `user_input` 중 하나 |
| `recoverable`  | `bool`    | 복구 가능한지                                                        |

### `ErrorOccurredHookOutput`

| 프로퍼티               | 타입        | 설명                         |
| ------------------ | --------- | -------------------------- |
| `suppressOutput`   | `?bool`   | 에러 표시 억제                   |
| `errorHandling`    | `?string` | `retry` / `skip` / `abort` |
| `retryCount`       | `?int`    | 재시도 횟수                     |
| `userNotification` | `?string` | 사용자에게 보내는 통지문              |

## ToolResultObject

도구 실행 결과의 표준 객체입니다.

| 프로퍼티                 | 타입        | 설명                                            |
| -------------------- | --------- | --------------------------------------------- |
| `textResultForLlm`   | `?string` | LLM에 전달할 텍스트 결과                               |
| `resultType`         | `?string` | `success` / `failure` / `rejected` / `denied` |
| `resultForAssistant` | `?array`  | 어시스턴트용 결과 데이터                                 |

## 베스트 프랙티스

1. 무거운 동기 처리를 훅 내에서 직접 너무 많이 실행하지 마세요. 필요하다면 비동기화합니다.
2. 변경이 불필요한 경우 `null`을 반환해 기본 동작에 맡깁니다.
3. `permissionDecision`은 가능한 한 명시합니다.
4. 크리티컬 에러는 너무 억제하지 말고 로그/통지 경로를 확보합니다.
5. 세션 단위의 상태는 session id 기반으로 관리하고 `onSessionEnd`에서 정리합니다.

<Info>
  최신 정보는 [GitHub 저장소](https://github.com/invokable/laravel-copilot-sdk)를 참고하세요.
</Info>


## Related topics

- [에이전트 루프](/ko/packages/laravel-copilot-sdk/agent-loop.md)
- [세션](/ko/session.md)
- [세션 재개](/ko/packages/laravel-copilot-sdk/resume.md)
- [시작하기 - GitHub Copilot SDK for Laravel](/ko/packages/laravel-copilot-sdk/getting-started.md)
- [세션 라이프사이클 이벤트](/ko/packages/laravel-copilot-sdk/session-lifecycle-event.md)
