メインコンテンツへスキップ
フックはエージェントイベントに応答してコードを実行するコールバック関数です。ツールが呼び出されたり、セッションが開始したり、実行が停止したりするなどのイベントに対応します。フックを使用すると、以下のことができます。
  • 危険な操作をブロックする:破壊的なシェルコマンドや不正なファイルアクセスなど、実行前に危険な操作をブロックします
  • ログと監査:コンプライアンス、デバッグ、分析のためにすべてのツール呼び出しをログして監査します
  • 入力と出力を変換する:データをサニタイズしたり、認証情報を注入したり、ファイルパスをリダイレクトしたりします
  • 人間の承認を要求する:データベース書き込みや API 呼び出しなどの機密アクションに対して
  • セッションライフサイクルを追跡する:状態を管理したり、リソースをクリーンアップしたり、通知を送信したりします
このガイドでは、フックの仕組み、フックの設定方法、およびツールのブロック、入力の変更、通知の転送などの一般的なパターンの例を説明します。

フックの仕組み

1

イベントが発火する

エージェント実行中に何かが起こり、SDK がイベントを発火します。ツールが呼び出されようとしている(PreToolUse)、ツールが結果を返した(PostToolUse)、サブエージェントが開始または停止した、エージェントがアイドル状態である、または実行が完了したなどです。イベントの完全なリストを参照してください。
2

SDK が登録されたフックを収集する

SDK は、そのイベントタイプに登録されたフックをチェックします。これには、options.hooks に渡すコールバックフックと、対応する settingSources または setting_sources エントリが有効になっているときの設定ファイルからのシェルコマンドフックが含まれます。これはデフォルトの query() オプションで有効になっています。
3

マッチャーがどのフックを実行するかをフィルタリングする

フックに matcher パターン("Write|Edit" など)がある場合、SDK はそれをイベントのターゲット(たとえば、ツール名)に対してテストします。マッチャーのないフックは、そのタイプのすべてのイベントに対して実行されます。
4

コールバック関数が実行される

各マッチングフックのコールバック関数は、何が起こっているかについての入力を受け取ります。ツール名、その引数、セッション ID、およびその他のイベント固有の詳細です。
5

コールバックが決定を返す

任意の操作(ログ、API 呼び出し、検証)を実行した後、コールバックは出力オブジェクトを返します。これはエージェントに何をするかを指示します。操作を許可する、ブロックする、入力を変更する、または会話にコンテキストを注入するなどです。
次の例は、これらのステップをまとめたものです。PreToolUse フック(ステップ 1)を "Write|Edit" マッチャー(ステップ 3)で登録して、コールバックがファイル書き込みツールに対してのみ発火するようにします。トリガーされると、コールバックはツールの入力(ステップ 4)を受け取り、ファイルパスが .env ファイルをターゲットにしているかどうかをチェックし、permissionDecision: "deny" を返して操作をブロックします(ステップ 5)。
import asyncio
from claude_agent_sdk import (
    AssistantMessage,
    ClaudeSDKClient,
    ClaudeAgentOptions,
    HookMatcher,
    ResultMessage,
)


# ツール呼び出しの詳細を受け取るフックコールバックを定義する
async def protect_env_files(input_data, tool_use_id, context):
    # ツールの入力引数からファイルパスを抽出する
    file_path = input_data["tool_input"].get("file_path", "")
    file_name = file_path.split("/")[-1]

    # .env ファイルをターゲットにしている場合は操作をブロックする
    if file_name == ".env":
        return {
            "hookSpecificOutput": {
                "hookEventName": input_data["hook_event_name"],
                "permissionDecision": "deny",
                "permissionDecisionReason": "Cannot modify .env files",
            }
        }

    # 空のオブジェクトを返して操作を許可する
    return {}


async def main():
    options = ClaudeAgentOptions(
        hooks={
            # PreToolUse イベントのフックを登録する
            # マッチャーは Write と Edit ツール呼び出しのみにフィルタリングする
            "PreToolUse": [HookMatcher(matcher="Write|Edit", hooks=[protect_env_files])]
        }
    )

    async with ClaudeSDKClient(options=options) as client:
        await client.query("Update the database configuration")
        async for message in client.receive_response():
            # アシスタントとリザルトメッセージをフィルタリングする
            if isinstance(message, (AssistantMessage, ResultMessage)):
                print(message)


asyncio.run(main())

利用可能なフック

SDK はエージェント実行のさまざまなステージのフックを提供します。一部のフックは両方の SDK で利用可能ですが、その他は TypeScript のみです。
フックイベントPython SDKTypeScript SDKトリガーされる条件使用例
PreToolUseはいはいツール呼び出しリクエスト(ブロックまたは変更可能)危険なシェルコマンドをブロックする
PostToolUseはいはいツール実行結果すべてのファイル変更を監査証跡にログする
PostToolUseFailureはいはいツール実行失敗ツールエラーを処理またはログする
PostToolBatchいいえはいツール呼び出しの完全なバッチが解決される。次のモデル呼び出しの前に 1 回バッチ全体に対して規約を 1 回注入する
UserPromptSubmitはいはいユーザープロンプト送信プロンプトに追加のコンテキストを注入する
MessageDisplayいいえはいテキスト付きのアシスタントメッセージが完了する。メッセージごとに 1 回、完全なメッセージテキスト付き表示されたテキストを編集または再フォーマットする(トランスクリプトは変更しない)
Stopはいはいエージェント実行停止終了前にセッション状態を保存する
SubagentStartはいはいサブエージェント初期化並列タスク生成を追跡する
SubagentStopはいはいサブエージェント完了並列タスクから結果を集約する
PreCompactはいはい会話圧縮リクエスト要約する前に完全なトランスクリプトをアーカイブする
PermissionRequestはいはいパーミッションダイアログが表示されるカスタムパーミッション処理
SessionStartいいえはいセッション初期化ログとテレメトリを初期化する
SessionEndいいえはいセッション終了一時的なリソースをクリーンアップする
Notificationはいはいエージェントステータスメッセージエージェントステータス更新を Slack または PagerDuty に送信する
Setupいいえはいセッション設定/メンテナンス初期化タスクを実行する
TeammateIdleいいえはいチームメイトがアイドル状態になる作業を再割り当てするか通知する
TaskCompletedいいえはいバックグラウンドタスク完了並列タスクから結果を集約する
ConfigChangeいいえはい設定ファイル変更設定を動的に再ロードする
WorktreeCreateいいえはいGit ワークツリー作成分離されたワークスペースを追跡する
WorktreeRemoveいいえはいGit ワークツリー削除ワークスペースリソースをクリーンアップする

フックを設定する

フックを設定するには、エージェントオプション(Python では ClaudeAgentOptions、TypeScript では options オブジェクト)の hooks フィールドに渡します。
options = ClaudeAgentOptions(
    hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[my_callback])]}
)

async with ClaudeSDKClient(options=options) as client:
    await client.query("Your prompt")
    async for message in client.receive_response():
        print(message)
hooks オプションは辞書(Python)またはオブジェクト(TypeScript)です。ここで:

マッチャー

マッチャーを使用して、コールバックがいつ発火するかをフィルタリングします。matcher フィールドは、フックイベントタイプに応じて異なる値に対してマッチングされます。たとえば、ツールベースのフックはツール名に対してマッチングされ、Notification フックは通知タイプに対してマッチングされます。各イベントタイプのマッチャー値の完全なリストについては、Claude Code フックリファレンスを参照してください。 SDK マッチャーは設定ファイルのマッチャーと同じルールに従います。文字、数字、_、および | のみを含むマッチャーは正確な文字列として比較され、| は代替案を区切るため、Write|Edit はこれら 2 つのツールと正確にマッチします。* のマッチャー、空の文字列、またはマッチャーを完全に省略すると、イベントのすべての発生にマッチします。他の文字を含むマッチャーは正規表現として評価されるため、^mcp__ はすべての MCP ツールにマッチします。mcp__memory のようなマッチャーは文字とアンダースコアのみを含むため、正確な文字列として比較され、ツールにマッチしません。そのサーバーからすべてのツールにマッチするには、mcp__memory__.* を使用します。
オプションデフォルト説明
matcherstringundefinedイベントのフィルタフィールドに対してマッチングされるパターン。上記の比較ルールに従います。ツールフックの場合、これはツール名です。組み込みツールには BashReadWriteEditGlobGrepWebFetchAgent などが含まれます(完全なリストについてはツール入力型を参照)。MCP ツールはパターン mcp__<server>__<action> を使用します。
hooksHookCallback[]-必須。パターンがマッチしたときに実行するコールバック関数の配列
timeoutnumber60タイムアウト(秒単位)
可能な限り matcher パターンを使用して特定のツールをターゲットにします。'Bash' のマッチャーは Bash コマンドに対してのみ実行されますが、パターンを省略するとコールバックはそのイベントのすべての発生に対して実行されます。ツールベースのフックの場合、マッチャーはツール名でのみフィルタリングされ、ファイルパスやその他の引数ではフィルタリングされません。ファイルパスでフィルタリングするには、コールバック内で tool_input.file_path をチェックします。
ツール名の発見: 組み込みツール名の完全なリストについてはツール入力型を参照するか、マッチャーなしでフックを追加して、セッションが行うすべてのツール呼び出しをログします。MCP ツール命名: MCP ツールは常に mcp__ で始まり、その後にサーバー名とアクション mcp__<server>__<action> が続きます。たとえば、playwright という名前のサーバーを設定した場合、そのツールは mcp__playwright__browser_screenshotmcp__playwright__browser_click などという名前になります。サーバー名は mcpServers 設定で使用するキーから取得されます。

コールバック関数

入力

すべてのフックコールバックは 3 つの引数を受け取ります。
  • 入力データ: イベント詳細を含む型付きオブジェクト。各フック型には独自の入力形状があります(たとえば、PreToolUseHookInput には tool_nametool_input が含まれ、NotificationHookInput には message が含まれます)。TypeScript および Python SDK リファレンスで完全な型定義を参照してください。
    • すべてのフック入力は session_idcwd、および hook_event_name を共有します。
    • agent_idagent_type は、フックがサブエージェント内で発火するときに入力されます。TypeScript では、これらはベースフック入力にあり、すべてのフック型で利用可能です。Python では、PreToolUsePostToolUse、および PostToolUseFailure のみにあります。
  • ツール使用 IDstr | None / string | undefined):同じツール呼び出しの PreToolUsePostToolUse イベントを相関させます。
  • コンテキスト: TypeScript では、キャンセル用の signal プロパティ(AbortSignal)を含みます。Python では、この引数は将来の使用のために予約されています。

出力

コールバックは 2 つのカテゴリのフィールドを持つオブジェクトを返します。
  • トップレベルフィールドはすべてのイベントで同じように機能します。systemMessage はユーザーにメッセージを表示し、continue(Python では continue_)はこのフック後にエージェントが実行を続けるかどうかを決定します。
  • hookSpecificOutput は現在の操作を制御します。内部のフィールドはフックイベントタイプに依存します。PreToolUse フックの場合、ここで permissionDecision"allow""deny""ask"、または "defer")、permissionDecisionReason、および updatedInput を設定します。"defer" を返すとクエリが終了し、後で再開できます。PostToolUse フックの場合、additionalContext を設定してツール結果に情報を追加できます。Claude がそれを見る前にツールの出力を置き換えるには、updatedToolOutput を設定します。これは両方の SDK のすべてのツールで機能します。古い updatedMCPToolOutput フィールドは MCP ツール出力のみを置き換え、非推奨です。
変更なしで操作を許可するには {} を返します。SDK コールバックフックは、Claude Code シェルコマンドフックと同じ JSON 出力形式を使用します。これはすべてのフィールドとイベント固有のオプションを文書化しています。SDK 型定義については、TypeScript および Python SDK リファレンスを参照してください。
複数のフックまたはパーミッションルールが適用される場合、denydefer より優先され、deferask より優先され、askallow より優先されます。いずれかのフックが deny を返す場合、他のフックに関係なく操作はブロックされます。

非同期出力

デフォルトでは、エージェントはコールバックが返されるのを待ってから続行します。フックが副作用(ログ、ウェブフック送信)を実行し、エージェントの動作に影響を与える必要がない場合、代わりに非同期出力を返すことができます。これはエージェントに、フックが完了するのを待たずに即座に続行するよう指示します。
async def async_hook(input_data, tool_use_id, context):
    # バックグラウンドタスクを開始してから即座に返す
    asyncio.create_task(send_to_logging_service(input_data))
    return {"async_": True, "asyncTimeout": 30000}
フィールド説明
asynctrue非同期モードを通知します。エージェントは待たずに続行します。Python では、予約キーワードを避けるために async_ を使用します。
asyncTimeoutnumberバックグラウンド操作のオプションのタイムアウト(ミリ秒単位)
非同期出力はエージェントが既に先に進んでいるため、ブロック、変更、またはコンテキストを注入することはできません。ログ、メトリクス、または通知などの副作用にのみ使用します。

ツール入力を変更する

この例は Write ツール呼び出しをインターセプトし、file_path 引数を書き直して /sandbox を先頭に追加し、すべてのファイル書き込みをサンドボックスディレクトリにリダイレクトします。コールバックは変更されたパスで updatedInput を返し、permissionDecision: 'allow' を返して書き直された操作を自動承認します。
async def redirect_to_sandbox(input_data, tool_use_id, context):
    if input_data["hook_event_name"] != "PreToolUse":
        return {}

    if input_data["tool_name"] == "Write":
        original_path = input_data["tool_input"].get("file_path", "")
        return {
            "hookSpecificOutput": {
                "hookEventName": input_data["hook_event_name"],
                "permissionDecision": "allow",
                "updatedInput": {
                    **input_data["tool_input"],
                    "file_path": f"/sandbox{original_path}",
                },
            }
        }
    return {}
updatedInput を使用する場合、permissionDecision: 'allow' を含めて変更された入力を自動承認するか、permissionDecision: 'ask' を含めてユーザーに表示する必要があります。'defer' の場合、updatedInput は無視されます。常に元の tool_input を変更するのではなく、新しいオブジェクトを返します。

コンテキストを追加してツールをブロックする

この例は /etc ディレクトリへの書き込みをブロックし、モデルとユーザーの両方に理由を説明します。
  • permissionDecision: 'deny' はツール呼び出しを停止します。
  • permissionDecisionReason はモデルに理由を伝えるため、再試行を避けます。
  • systemMessage はユーザーに何が起こったかを表示します。
async def block_etc_writes(input_data, tool_use_id, context):
    file_path = input_data["tool_input"].get("file_path", "")

    if file_path.startswith("/etc"):
        return {
            # トップレベルフィールド:ユーザーに表示されるメッセージ
            "systemMessage": "Remember: system directories like /etc are protected.",
            # hookSpecificOutput:操作をブロックする
            "hookSpecificOutput": {
                "hookEventName": input_data["hook_event_name"],
                "permissionDecision": "deny",
                "permissionDecisionReason": "Writing to /etc is not allowed",
            },
        }
    return {}

特定のツールを自動承認する

デフォルトでは、エージェントは特定のツールを使用する前にパーミッションを求めるプロンプトを表示する場合があります。この例は、permissionDecision: 'allow' を返すことで読み取り専用ファイルシステムツール(Read、Glob、Grep)を自動承認し、ユーザー確認なしで実行できるようにしながら、他のすべてのツールは通常のパーミッションチェックの対象のままにします。
async def auto_approve_read_only(input_data, tool_use_id, context):
    if input_data["hook_event_name"] != "PreToolUse":
        return {}

    read_only_tools = ["Read", "Glob", "Grep"]
    if input_data["tool_name"] in read_only_tools:
        return {
            "hookSpecificOutput": {
                "hookEventName": input_data["hook_event_name"],
                "permissionDecision": "allow",
                "permissionDecisionReason": "Read-only tool auto-approved",
            }
        }
    return {}

複数のフックを登録する

イベントが発火すると、すべてのマッチするフックが並列で実行されます。パーミッション決定の場合、最も制限的な結果が優先されます。単一の deny は、他のフックが何を返すかに関係なく、ツール呼び出しをブロックします。完了順序は非決定的であるため、別のフックが最初に実行されたことに依存するのではなく、各フックが独立して動作するように記述します。 以下の例は、すべてのツール呼び出しに対して 3 つの独立したチェックを登録します。
options = ClaudeAgentOptions(
    hooks={
        "PreToolUse": [
            HookMatcher(hooks=[authorization_check]),
            HookMatcher(hooks=[input_validator]),
            HookMatcher(hooks=[audit_logger]),
        ]
    }
)

マルチツールマッチャーでフィルタリングする

マルチツールマッチャーを使用して、関連するツール間で 1 つのコールバックを共有します。この例は、異なるスコープを持つ 3 つのマッチャーを登録します。
  • パイプで区切られた正確なリスト(Write|Edit|Delete)は、ファイル変更ツールに対してのみ file_security_hook をトリガーします。
  • 正規表現(^mcp__)は、名前が mcp__ で始まる任意の MCP ツールに対して mcp_audit_hook をトリガーします。
  • 省略されたマッチャーは、名前に関係なくすべてのツール呼び出しに対して global_logger をトリガーします。
options = ClaudeAgentOptions(
    hooks={
        "PreToolUse": [
            # ファイル変更ツールをマッチングする
            HookMatcher(matcher="Write|Edit|Delete", hooks=[file_security_hook]),
            # すべての MCP ツールをマッチングする
            HookMatcher(matcher="^mcp__", hooks=[mcp_audit_hook]),
            # すべてをマッチングする(マッチャーなし)
            HookMatcher(hooks=[global_logger]),
        ]
    }
)

サブエージェントアクティビティを追跡する

SubagentStop フックを使用して、サブエージェントが作業を完了するときを監視します。TypeScript および Python SDK リファレンスで完全な入力型を参照してください。この例は、サブエージェントが完了するたびに概要をログします。
async def subagent_tracker(input_data, tool_use_id, context):
    # サブエージェントが完了したときにサブエージェント詳細をログする
    print(f"[SUBAGENT] Completed: {input_data['agent_id']}")
    print(f"  Transcript: {input_data['agent_transcript_path']}")
    print(f"  Tool use ID: {tool_use_id}")
    print(f"  Stop hook active: {input_data.get('stop_hook_active')}")
    return {}


options = ClaudeAgentOptions(
    hooks={"SubagentStop": [HookMatcher(hooks=[subagent_tracker])]}
)

フックから HTTP リクエストを行う

フックは HTTP リクエストなどの非同期操作を実行できます。フック内でエラーをキャッチして、処理されない例外がエージェントを中断しないようにします。 この例は、各ツールが完了した後にウェブフックを送信し、どのツールが実行されたかと実行時刻をログします。フックはエラーをキャッチして、失敗したウェブフックがエージェントを中断しないようにします。
import asyncio
import json
import urllib.request
from datetime import datetime


def _send_webhook(tool_name):
    """外部ウェブフックにツール使用データを POST する同期ヘルパー。"""
    data = json.dumps(
        {
            "tool": tool_name,
            "timestamp": datetime.now().isoformat(),
        }
    ).encode()
    req = urllib.request.Request(
        "https://api.example.com/webhook",
        data=data,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    urllib.request.urlopen(req)


async def webhook_notifier(input_data, tool_use_id, context):
    # ツールが完了した後(PostToolUse)に発火し、前ではない
    if input_data["hook_event_name"] != "PostToolUse":
        return {}

    try:
        # イベントループをブロックしないようにスレッドでブロッキング HTTP 呼び出しを実行する
        await asyncio.to_thread(_send_webhook, input_data["tool_name"])
    except Exception as e:
        # エラーをログするが、発生させない。失敗したウェブフックはエージェントを停止すべきではない
        print(f"Webhook request failed: {e}")

    return {}

通知を Slack に転送する

Notification フックを使用して、エージェントからのシステム通知を受け取り、外部サービスに転送します。通知は以下のようなイベントタイプに対して発火します。
  • permission_prompt(Claude がパーミッションを必要とする)
  • idle_prompt(Claude が入力を待機している)
  • auth_success(認証が完了した)
  • elicitation_dialogelicitation_complete、および elicitation_response(ユーザープロンプト引き出しフロー用)
各通知には、人間が読める説明を含む message フィールドと、オプションで title が含まれます。 この例は、すべての通知を Slack チャネルに転送します。Slack 受信ウェブフック URL が必要です。これは、Slack ワークスペースにアプリを追加し、受信ウェブフックを有効にすることで作成します。
import asyncio
import json
import urllib.request

from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, HookMatcher


def _send_slack_notification(message):
    """受信ウェブフック経由で Slack にメッセージを送信する同期ヘルパー。"""
    data = json.dumps({"text": f"Agent status: {message}"}).encode()
    req = urllib.request.Request(
        "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
        data=data,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    urllib.request.urlopen(req)


async def notification_handler(input_data, tool_use_id, context):
    try:
        # イベントループをブロックしないようにスレッドでブロッキング HTTP 呼び出しを実行する
        await asyncio.to_thread(_send_slack_notification, input_data.get("message", ""))
    except Exception as e:
        print(f"Failed to send notification: {e}")

    # 空のオブジェクトを返す。通知フックはエージェント動作を変更しない
    return {}


async def main():
    options = ClaudeAgentOptions(
        hooks={
            # 通知イベントのフックを登録する(マッチャーは不要)
            "Notification": [HookMatcher(hooks=[notification_handler])],
        },
    )

    async with ClaudeSDKClient(options=options) as client:
        await client.query("Analyze this codebase")
        async for message in client.receive_response():
            print(message)


asyncio.run(main())

一般的な問題を修正する

フックが発火しない

  • フックイベント名が正しく、大文字と小文字が区別されていることを確認します(preToolUse ではなく PreToolUse
  • マッチャーパターンがツール名と正確にマッチしていることを確認します
  • フックが options.hooks の正しいイベントタイプの下にあることを確認します
  • StopSubagentStop などの非ツールフックの場合、マッチャーは異なるフィールドに対してマッチングされます(マッチャーパターンを参照)
  • エージェントが max_turns 制限に達するとセッションが終了する前にフックが実行される可能性があるため、フックが発火しない場合があります

マッチャーが期待どおりにフィルタリングしない

マッチャーはツール名のみをマッチングし、ファイルパスやその他の引数はマッチングしません。ファイルパスでフィルタリングするには、フック内で tool_input.file_path をチェックします:
const myHook: HookCallback = async (input, toolUseID, { signal }) => {
  const preInput = input as PreToolUseHookInput;
  const toolInput = preInput.tool_input as Record<string, unknown>;
  const filePath = toolInput?.file_path as string;
  if (!filePath?.endsWith(".md")) return {}; // マークダウンファイル以外をスキップ
  // マークダウンファイルを処理...
  return {};
};

フックタイムアウト

  • HookMatcher 設定で timeout 値を増やします
  • TypeScript で 3 番目のコールバック引数から AbortSignal を使用して、キャンセルを適切に処理します

ツールが予期せずブロックされた

  • すべての PreToolUse フックで permissionDecision: 'deny' を返していないかチェックします
  • フックにログを追加して、返している permissionDecisionReason を確認します
  • マッチャーパターンが広すぎないことを確認します(空のマッチャーはすべてのツールにマッチングします)

変更された入力が適用されない

  • updatedInputhookSpecificOutput の内部にあり、トップレベルにないことを確認します:
    return {
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "allow",
        updatedInput: { command: "new command" }
      }
    };
    
  • 変更された入力を自動承認するには permissionDecision: 'allow' を返すか、ユーザーに承認を求めるには 'ask' を返します
  • hookSpecificOutputhookEventName を含めて、出力がどのフック型用かを識別します

Python でセッションフックが利用できない

SessionStartSessionEnd は TypeScript で SDK コールバックフックとして登録できますが、Python SDK では利用できません(HookEvent は除外されています)。Python では、設定ファイルで定義されたシェルコマンドフックとしてのみ利用可能です(たとえば、.claude/settings.json)。SDK アプリケーションからシェルコマンドフックをロードするには、setting_sources または settingSources で適切な設定ソースを含めます:
options = ClaudeAgentOptions(
    setting_sources=["project"],  # フックを含む .claude/settings.json をロード
)
Python SDK コールバックとして初期化ロジックを実行するには、client.receive_response() からの最初のメッセージをトリガーとして使用します。

サブエージェントパーミッションプロンプトが増加する

複数のサブエージェントを生成する場合、各サブエージェントは個別にパーミッションをリクエストする可能性があります。サブエージェントは親エージェントのパーミッションを自動的に継承しません。繰り返されるプロンプトを避けるには、PreToolUse フックを使用して特定のツールを自動承認するか、サブエージェントセッションに適用されるパーミッションルールを設定します。

サブエージェントを使用した再帰的フックループ

サブエージェントを生成する UserPromptSubmit フックは、それらのサブエージェントが同じフックをトリガーする場合、無限ループを作成できます。これを防ぐには:
  • サブエージェント指標をチェックしてからサブエージェントを生成する前にフック入力をチェックします
  • 共有変数またはセッション状態を使用して、既にサブエージェント内にいるかどうかを追跡します
  • フックをトップレベルエージェントセッションのみに実行するようにスコープします

systemMessage が出力に表示されない

systemMessage フィールドはユーザーにメッセージを表示します。デフォルトでは SDK はメッセージストリームにフック出力を表示しないため、includeHookEvents(Python では include_hook_events)を設定しない限り、メッセージが表示されない場合があります。代わりにモデルにコンテキストを渡すには、additionalContextを返します。 フック決定をアプリケーションに確実に表示する必要がある場合は、別途ログするか、専用の出力チャネルを使用します。

関連リソース