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

# Hooks

Hooks を使用すると、AI セッションの主要なライフサイクルポイントにカスタムロジックを注入でき、監査ログ、セキュリティ制御、コンテキスト注入、動的な動作変更が可能になります。

<div id="イベント概要" />

## イベント概要

| イベント                 | トリガー           | 制御可能な動作                |
| -------------------- | -------------- | ---------------------- |
| `PreToolUse`         | ツール呼び出し前       | インターセプト / 許可 / 入力の変更   |
| `PostToolUse`        | ツール成功後         | 監査 / コンテキスト注入 / 出力の上書き |
| `PostToolUseFailure` | ツール失敗後         | エラーハンドリング / ロギング       |
| `UserPromptSubmit`   | ユーザープロンプト送信前   | コンテキスト注入 / インターセプト     |
| `SessionStart`       | セッション開始時       | 初期化 / コンテキスト注入         |
| `SessionEnd`         | セッション終了時       | クリーンアップ / ロギング         |
| `Stop`               | AI が生成を停止した時   | 停止の防止、続行の強制            |
| `SubagentStart`      | サブエージェント開始時    | 監視 / ロギング              |
| `SubagentStop`       | サブエージェント停止時    | 監視 / ロギング              |
| `PreCompact`         | コンテキスト圧縮前      | 監視 / ロギング              |
| `PostCompact`        | コンテキスト圧縮後      | 監視 / ロギング              |
| `CwdChanged`         | 作業ディレクトリ変更時    | 監視 / ロギング              |
| `InstructionsLoaded` | 命令ファイルロード時     | 監視 / ロギング              |
| `FileChanged`        | ファイルの作成/変更/削除時 | 監視 / ロギング              |
| `PermissionRequest`  | パーミッション要求時     | パーミッション要求を自動承認 / 拒否    |

完全なイベント型定義は [Hooks リファレンス](/ja/cli/sdk/python/references#hooks-reference) を参照してください。

<div id="設定" />

## 設定

`QoderAgentOptions.hooks` で hooks を設定します：

```python theme={null}
from qoder_agent_sdk import query, QoderAgentOptions, HookMatcher

async for msg in query(
    prompt="perform task",
    options=QoderAgentOptions(
        hooks={
            "PreToolUse": [HookMatcher(matcher="Bash", hooks=[my_hook])],
            "PostToolUse": [HookMatcher(hooks=[audit_hook])],
            "SessionEnd": [HookMatcher(hooks=[log_hook])],
        },
    ),
):
    ...  # process messages
```

<div id="matcher" />

### Matcher

`matcher` フィールドは正規表現パターンです。ツール名がマッチした場合のみ hook が実行されます：

```python theme={null}
hooks={
    "PreToolUse": [
        HookMatcher(matcher="Bash", hooks=[bash_audit]),                # Bash のみ
        HookMatcher(matcher="File.*|Write|Edit", hooks=[file_audit]),   # ファイル操作
        HookMatcher(hooks=[general_log]),                               # すべてのツール（matcher なし）
    ],
}
```

<div id="コールバック関数" />

### コールバック関数

各 hook コールバックはイベント入力、ツール使用 ID、コンテキストを受け取ります：

```python theme={null}
HookCallback = Callable[
    [HookInput, str | None, HookContext],
    Awaitable[HookJSONOutput],
]
```

<div id="入力" />

#### 入力

すべてのイベントは共通フィールドを共有します：`hook_event_name`（イベントタイプ）、`session_id`（セッション ID）、`transcript_path`（記録ファイルパス）、`cwd`（作業ディレクトリ）。各イベントにはさらに固有のフィールドがあります（例：`PreToolUse` の `tool_name` と `tool_input`）。

完全な入力型定義は [Hooks リファレンス](/ja/cli/sdk/python/references#basehookinput) を参照してください。

<div id="出力" />

#### 出力

コールバックは以下のフィールドで動作を制御する dict を返します：

* `continue_: False` — セッションを終了（JSON `"continue"` にシリアライズされます）
* `decision: "block"` + `reason` — ツール実行を阻止、または AI の停止を防止
* `hookSpecificOutput` — イベント固有の出力。ツール入力の変更（`updatedInput`）、ツール出力の上書き（`updatedToolOutput`）、コンテキスト注入（`additionalContext`）など

完全な出力型定義は [Hooks リファレンス](/ja/cli/sdk/python/references#hookjsonoutput) を参照してください。

<div id="サンプル" />

## サンプル

<div id="セキュリティインターセプトpretooluse" />

### セキュリティインターセプト（PreToolUse）

危険なシェルコマンドをブロック：

```python theme={null}
async def security_hook(inp: HookInput, tid: str | None, ctx: HookContext) -> HookJSONOutput:
    if inp.get("tool_name") == "Bash":
        cmd = (inp.get("tool_input") or {}).get("command", "")
        if "rm -rf" in cmd:
            return {
                "hookSpecificOutput": {
                    "hookEventName": "PreToolUse",
                    "permissionDecision": "deny",
                    "permissionDecisionReason": "Destructive delete operations are not allowed",
                },
            }
    return {}
```

<div id="機密情報のマスキングposttooluse" />

### 機密情報のマスキング（PostToolUse）

ツール出力を上書きし、AK/Token などの機密情報を置換：

```python theme={null}
import re

async def secret_redact_hook(inp: HookInput, tid: str | None, ctx: HookContext) -> HookJSONOutput:
    if inp.get("hook_event_name") != "PostToolUse":
        return {}

    response = inp.get("tool_response", "")
    content = response if isinstance(response, str) else json.dumps(response)

    redacted = re.sub(r"(?:LTAI|AKID)[A-Za-z0-9]{16,}", "<REDACTED_AK>", content)
    redacted = re.sub(r"Bearer\s+[A-Za-z0-9\-._~+/]+=*", "Bearer <REDACTED>", redacted)

    if redacted == content:
        return {}

    return {
        "hookSpecificOutput": {
            "hookEventName": "PostToolUse",
            "updatedToolOutput": redacted,
        },
    }
```

<div id="長い出力のトリミングposttooluse" />

### 長い出力のトリミング（PostToolUse）

長すぎる Bash 出力を先頭と末尾を残して切り詰め：

```python theme={null}
async def bash_summarize_hook(inp: HookInput, tid: str | None, ctx: HookContext) -> HookJSONOutput:
    if inp.get("hook_event_name") != "PostToolUse":
        return {}
    if inp.get("tool_name") != "Bash":
        return {}

    content = str(inp.get("tool_response") or "")
    THRESHOLD = 50 * 1024
    if len(content) <= THRESHOLD:
        return {}

    head = content[:8 * 1024]
    tail = content[-4 * 1024:]
    omitted = len(content) - len(head) - len(tail)
    return {
        "hookSpecificOutput": {
            "hookEventName": "PostToolUse",
            "updatedToolOutput": f"{head}\n\n[... OMITTED {omitted} chars ...]\n\n{tail}",
        },
    }
```

<div id="強制続行stop" />

### 強制続行（Stop）

タスクが未完了の場合、AI の停止を防止：

```python theme={null}
async def keep_going(inp: HookInput, tid: str | None, ctx: HookContext) -> HookJSONOutput:
    if inp.get("hook_event_name") != "Stop":
        return {}

    if not is_task_complete():
        return {"decision": "block", "reason": "Please continue completing the remaining tasks"}
    return {}
```

<div id="パーミッションの自動承認permissionrequest" />

### パーミッションの自動承認（PermissionRequest）

Read ツールのパーミッション要求を自動承認：

```python theme={null}
async def auto_approve_read(inp: HookInput, tid: str | None, ctx: HookContext) -> HookJSONOutput:
    if inp.get("hook_event_name") != "PermissionRequest":
        return {}

    if inp.get("tool_name") == "Read":
        return {
            "hookSpecificOutput": {
                "hookEventName": "PermissionRequest",
                "decision": {"behavior": "allow"},
            },
        }
    return {}
```

> 完全なパーミッションモデルは[パーミッションドキュメント](/ja/cli/sdk/python/permissions)を参照してください。

<div id="監査とセキュリティ制御総合" />

### 監査とセキュリティ制御（総合）

監査ログとセキュリティインターセプトの組み合わせ：

```python theme={null}
import json
import logging
import re
from qoder_agent_sdk import query, QoderAgentOptions, HookMatcher
from qoder_agent_sdk.types import HookInput, HookContext, HookJSONOutput

async def security_hook(inp: HookInput, tid: str | None, ctx: HookContext) -> HookJSONOutput:
    if inp.get("hook_event_name") != "PreToolUse":
        return {}

    # Audit log
    logging.info(json.dumps({
        "event": "tool_call",
        "tool": inp.get("tool_name"),
        "input": inp.get("tool_input"),
    }))

    # Security check: block curl to external domains
    if inp.get("tool_name") == "Bash":
        cmd = str((inp.get("tool_input") or {}).get("command", ""))
        if re.search(r"curl\s+https?://(?!localhost)", cmd):
            return {
                "hookSpecificOutput": {
                    "hookEventName": "PreToolUse",
                    "permissionDecision": "deny",
                    "permissionDecisionReason": "HTTP requests to external domains are not allowed",
                },
            }

    return {}


async def main():
    async for msg in query(
        prompt="run deployment",
        options=QoderAgentOptions(
            hooks={
                "PreToolUse": [HookMatcher(hooks=[security_hook])],
            },
        ),
    ):
        pass  # process messages
```

<div id="注意事項" />

## 注意事項

* Hook コールバックは AI の実行をブロックしないよう、迅速に返すべきです。
* `matcher` は Python の正規表現構文（`re` モジュール）を使用し、`tool_name` フィールドにマッチします。
* `continue_: False` はセッションを終了します——`PreToolUse`、`PostToolUse`、`PostToolUseFailure`、`UserPromptSubmit`、`Stop`、`SubagentStop` イベントでのみ有効です。観察系イベント（`SessionEnd`、`CwdChanged` など）はこのフィールドを無視します。
* 複数の hook が競合する `decision` 値を返した場合、`"deny"` / `"block"` が優先されます（最も厳格なルールが適用）。
* 複数の hook が `updatedToolOutput` を設定した場合、**最後の非空値**が有効になります。チェーン変換（例：マスキング後にトリミング）が必要な場合は、単一のコールバック内で順次実行してください。
* Python SDK は Python の予約語との衝突を避けるため、末尾アンダースコア付きフィールド名（`continue_`）を使用します。SDK はシリアライズ時に自動的にワイヤープロトコル名（`continue`）に変換します。
