Loading...
    • Panduan Pengembang
    • Referensi API
    • MCP
    • Sumber daya
    • Catatan rilis
    Search...
    ⌘K
    Langkah pertama
    Pengenalan ClaudeMulai cepat
    Model & harga
    Ikhtisar modelMemilih modelYang baru di Claude 4.5Migrasi ke Claude 4.5Penghentian modelHarga
    Bangun dengan Claude
    Ikhtisar fiturMenggunakan Messages APIJendela konteksPraktik terbaik prompting
    Kemampuan
    Prompt cachingPengeditan konteksPemikiran diperpanjangUpayaStreaming pesanPemrosesan batchKutipanDukungan multibahasaPenghitungan tokenEmbeddingsVisiDukungan PDFFiles APIHasil pencarianOutput terstruktur
    Alat
    IkhtisarCara mengimplementasikan penggunaan alatStreaming alat berbutir halusAlat BashAlat eksekusi kodePemanggilan alat terprogramAlat penggunaan komputerAlat editor teksAlat pengambilan webAlat pencarian webAlat memoriAlat pencarian alat
    Keterampilan Agen
    IkhtisarMulai cepatPraktik terbaikMenggunakan Keterampilan dengan API
    Agent SDK
    IkhtisarMulai cepatTypeScript SDKTypeScript V2 (pratinjau)Python SDKPanduan migrasi
    Input streamingMenangani izinKontrol eksekusi dengan hookManajemen sesiOutput terstruktur di SDKHosting Agent SDKPenerapan agen AI dengan amanMemodifikasi prompt sistemMCP di SDKAlat khususSubagen di SDKPerintah garis miring di SDKKeterampilan agen di SDKPelacakan biaya dan penggunaanDaftar tugasPlugin di SDK
    MCP di API
    Konektor MCPServer MCP jarak jauh
    Claude di platform pihak ketiga
    Amazon BedrockMicrosoft FoundryVertex AI
    Rekayasa prompt
    IkhtisarPembuat promptGunakan template promptPenyempurna promptJadilah jelas dan langsungGunakan contoh (prompting multishot)Biarkan Claude berpikir (CoT)Gunakan tag XMLBerikan Claude peran (prompt sistem)Isi respons Claude sebelumnyaRantai prompt kompleksTips konteks panjangTips pemikiran diperpanjang
    Uji & evaluasi
    Tentukan kriteria kesuksesanKembangkan kasus ujiMenggunakan alat evaluasiMengurangi latensi
    Perkuat penjaga
    Kurangi halusinasiTingkatkan konsistensi outputMitigasi jailbreakStreaming penolakanKurangi kebocoran promptJaga Claude tetap dalam karakter
    Administrasi dan pemantauan
    Ikhtisar Admin APIAPI penggunaan dan biayaClaude Code Analytics API
    Console
    Log in
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...

    Solutions

    • AI agents
    • Code modernization
    • Coding
    • Customer support
    • Education
    • Financial services
    • Government
    • Life sciences

    Partners

    • Amazon Bedrock
    • Google Cloud's Vertex AI

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Company

    • Anthropic
    • Careers
    • Economic Futures
    • Research
    • News
    • Responsible Scaling Policy
    • Security and compliance
    • Transparency

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Help and security

    • Availability
    • Status
    • Support
    • Discord

    Terms and policies

    • Privacy policy
    • Responsible disclosure policy
    • Terms of service: Commercial
    • Terms of service: Consumer
    • Usage policy
    Panduan

    Intercept dan kontrol perilaku agen dengan hooks

    Intercept dan sesuaikan perilaku agen pada titik eksekusi kunci dengan hooks

    Hooks memungkinkan Anda untuk menginterceptor eksekusi agen pada titik-titik kunci untuk menambahkan validasi, logging, kontrol keamanan, atau logika kustom. Dengan hooks, Anda dapat:

    • Memblokir operasi berbahaya sebelum dieksekusi, seperti perintah shell yang merusak atau akses file yang tidak sah
    • Log dan audit setiap pemanggilan tool untuk kepatuhan, debugging, atau analitik
    • Transformasi input dan output untuk membersihkan data, menyuntikkan kredensial, atau mengalihkan jalur file
    • Memerlukan persetujuan manusia untuk tindakan sensitif seperti penulisan database atau panggilan API
    • Lacak siklus hidup sesi untuk mengelola status, membersihkan sumber daya, atau mengirim notifikasi

    Sebuah hook memiliki dua bagian:

    1. Fungsi callback: logika yang berjalan ketika hook dipicu
    2. Konfigurasi hook: memberitahu SDK acara mana yang akan di-hook (seperti PreToolUse) dan tool mana yang cocok

    Contoh berikut memblokir agen dari memodifikasi file .env. Pertama, tentukan callback yang memeriksa jalur file, kemudian teruskan ke query() untuk dijalankan sebelum panggilan tool Write atau Edit apa pun:

    Ini adalah hook PreToolUse. Hook ini berjalan sebelum tool dieksekusi dan dapat memblokir atau mengizinkan operasi berdasarkan logika Anda. Sisa panduan ini mencakup semua hook yang tersedia, opsi konfigurasi mereka, dan pola untuk kasus penggunaan umum.

    Hook yang tersedia

    SDK menyediakan hooks untuk tahap berbeda dari eksekusi agen. Beberapa hooks tersedia di kedua SDK, sementara yang lain hanya TypeScript karena Python SDK tidak mendukungnya.

    Hook EventPython SDKTypeScript SDKApa yang memicunyaContoh kasus penggunaan
    PreToolUseYaYaPermintaan pemanggilan tool (dapat memblokir atau memodifikasi)Blokir perintah shell berbahaya
    PostToolUseYaYaHasil eksekusi toolLog semua perubahan file ke jejak audit
    PostToolUseFailureTidakYaKegagalan eksekusi toolTangani atau log kesalahan tool
    UserPromptSubmitYaYaPengajuan prompt penggunaSuntikkan konteks tambahan ke dalam prompt

    Kasus penggunaan umum

    Hooks cukup fleksibel untuk menangani banyak skenario berbeda. Berikut adalah beberapa pola paling umum yang diorganisir berdasarkan kategori.

    Konfigurasi hooks

    Untuk mengonfigurasi hook untuk agen Anda, teruskan hook dalam parameter options.hooks saat memanggil query():

    async for message in query(
        prompt="Your prompt",
        options=ClaudeAgentOptions(
            hooks={
                'PreToolUse': [HookMatcher(matcher='Bash', hooks=[my_callback])]
            }
        )
    ):
        print(message)

    Opsi hooks adalah kamus (Python) atau objek (TypeScript) di mana:

    • Kunci adalah nama acara hook (misalnya, 'PreToolUse', 'PostToolUse', 'Stop')
    • Nilai adalah array dari matcher, masing-masing berisi pola filter opsional dan fungsi callback Anda

    Fungsi callback hook Anda menerima data input tentang acara dan mengembalikan respons sehingga agen tahu untuk mengizinkan, memblokir, atau memodifikasi operasi.

    Matchers

    Gunakan matcher untuk memfilter tool mana yang memicu callback Anda:

    OpsiTipeDefaultDeskripsi
    matcherstringundefinedPola regex untuk mencocokkan nama tool. Tool bawaan termasuk Bash, Read, Write, Edit, Glob, Grep, WebFetch, Task, dan lainnya. Tool MCP menggunakan pola mcp__<server>__<action>.
    hooksHookCallback[]-

    Gunakan pola matcher untuk menargetkan tool tertentu kapan pun memungkinkan. Matcher dengan 'Bash' hanya berjalan untuk perintah Bash, sementara menghilangkan pola menjalankan callback Anda untuk setiap pemanggilan tool. Perhatikan bahwa matcher hanya memfilter berdasarkan nama tool, bukan jalur file atau argumen lainnya—untuk memfilter berdasarkan jalur file, periksa tool_input.file_path di dalam callback Anda.

    Matcher hanya berlaku untuk hook berbasis tool (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest). Untuk hook siklus hidup seperti Stop, SessionStart, dan Notification, matcher diabaikan dan hook dipicu untuk semua acara dari tipe itu.

    Menemukan nama tool: Periksa array tools dalam pesan sistem awal ketika sesi Anda dimulai, atau tambahkan hook tanpa matcher untuk mencatat semua pemanggilan tool.

    Penamaan tool MCP: Tool MCP selalu dimulai dengan mcp__ diikuti oleh nama server dan tindakan: mcp__<server>__<action>. Misalnya, jika Anda mengonfigurasi server bernama playwright, toolnya akan dinamai mcp__playwright__browser_screenshot, mcp__playwright__browser_click, dll. Nama server berasal dari kunci yang Anda gunakan dalam konfigurasi mcpServers.

    Contoh ini menggunakan matcher untuk menjalankan hook hanya untuk tool yang memodifikasi file ketika acara PreToolUse dipicu:

    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                HookMatcher(matcher='Write|Edit', hooks=[validate_file_path])
            ]
        }
    )

    Input fungsi callback

    Setiap callback hook menerima tiga argumen:

    1. Data input (dict / HookInput): Detail acara. Lihat data input untuk bidang
    2. ID penggunaan tool (str | None / string | null): Korelasikan acara PreToolUse dan PostToolUse
    3. Konteks (HookContext): Di TypeScript, berisi properti signal (AbortSignal) untuk pembatalan. Teruskan ini ke operasi async seperti fetch() sehingga mereka secara otomatis membatalkan jika hook kedaluwarsa. Di Python, argumen ini dicadangkan untuk penggunaan di masa depan.

    Data input

    Argumen pertama untuk callback hook Anda berisi informasi tentang acara. Nama bidang identik di seluruh SDK (keduanya menggunakan snake_case).

    Bidang umum yang ada di semua tipe hook:

    BidangTipeDeskripsi
    hook_event_namestringTipe hook (PreToolUse, PostToolUse, dll.)
    session_idstringPengidentifikasi sesi saat ini
    transcript_pathstringJalur ke transkrip percakapan
    cwdstringDirektori kerja saat ini

    Bidang spesifik hook bervariasi menurut tipe hook. Item yang ditandai TS hanya tersedia di TypeScript SDK:

    BidangTipeDeskripsiHooks
    tool_namestringNama tool yang dipanggilPreToolUse, PostToolUse, PostToolUseFailureTS, PermissionRequestTS
    tool_inputobjectArgumen yang dilewatkan ke toolPreToolUse, PostToolUse, PostToolUseFailureTS, PermissionRequestTS
    tool_responseanyHasil yang dikembalikan dari eksekusi toolPostToolUse
    errorstring

    Kode di bawah mendefinisikan callback hook yang menggunakan tool_name dan tool_input untuk mencatat detail tentang setiap pemanggilan tool:

    Output callback

    Fungsi callback Anda mengembalikan objek yang memberitahu SDK cara melanjutkan. Kembalikan objek kosong {} untuk mengizinkan operasi tanpa perubahan. Untuk memblokir, memodifikasi, atau menambahkan konteks ke operasi, kembalikan objek dengan bidang hookSpecificOutput yang berisi keputusan Anda.

    Bidang tingkat atas (di luar hookSpecificOutput):

    BidangTipeDeskripsi
    continuebooleanApakah agen harus melanjutkan setelah hook ini (default: true)
    stopReasonstringPesan yang ditampilkan ketika continue adalah false
    suppressOutputbooleanSembunyikan stdout dari transkrip (default: false)
    systemMessagestring

    Bidang di dalam hookSpecificOutput:

    BidangTipeHooksDeskripsi
    hookEventNamestringSemuaDiperlukan. Gunakan input.hook_event_name untuk mencocokkan acara saat ini
    permissionDecision'allow' | 'deny' | 'ask'PreToolUseMengontrol apakah tool dieksekusi
    permissionDecisionReasonstringPreToolUsePenjelasan yang ditampilkan kepada Claude untuk keputusan
    updatedInput

    Contoh ini memblokir operasi penulisan ke direktori /etc sambil menyuntikkan pesan sistem untuk mengingatkan Claude tentang praktik file yang aman:

    Alur keputusan izin

    Ketika beberapa hook atau aturan izin berlaku, SDK mengevaluasinya dalam urutan ini:

    1. Aturan Deny diperiksa terlebih dahulu (kecocokan apa pun = penolakan segera).
    2. Aturan Ask diperiksa kedua.
    3. Aturan Allow diperiksa ketiga.
    4. Default ke Ask jika tidak ada yang cocok.

    Jika hook apa pun mengembalikan deny, operasi diblokir—hook lain yang mengembalikan allow tidak akan menimpanya.

    Blokir tool

    Kembalikan keputusan deny untuk mencegah eksekusi tool:

    Modifikasi input tool

    Kembalikan input yang diperbarui untuk mengubah apa yang diterima tool:

    Saat menggunakan updatedInput, Anda juga harus menyertakan permissionDecision. Selalu kembalikan objek baru daripada mutasi tool_input asli.

    Tambahkan pesan sistem

    Suntikkan konteks ke dalam percakapan:

    async def add_security_reminder(input_data, tool_use_id, context):
        return {
            'systemMessage': 'Remember to follow security best practices.'
        }

    Persetujuan otomatis untuk tool tertentu

    Lewati prompt izin untuk tool terpercaya. Ini berguna ketika Anda ingin operasi tertentu berjalan tanpa konfirmasi pengguna:

    Bidang permissionDecision menerima tiga nilai: 'allow' (persetujuan otomatis), 'deny' (blokir), atau 'ask' (minta konfirmasi).

    Tangani skenario lanjutan

    Pola-pola ini membantu Anda membangun sistem hook yang lebih canggih untuk kasus penggunaan yang kompleks.

    Chaining multiple hooks

    Hooks dieksekusi dalam urutan kemunculannya di array. Jaga setiap hook tetap fokus pada tanggung jawab tunggal dan rantai beberapa hook untuk logika kompleks. Contoh ini menjalankan keempat hook untuk setiap pemanggilan tool (tidak ada matcher yang ditentukan):

    Matcher spesifik tool dengan regex

    Gunakan pola regex untuk mencocokkan beberapa tool:

    Matcher hanya mencocokkan nama tool, bukan jalur file atau argumen lainnya. Untuk memfilter berdasarkan jalur file, periksa tool_input.file_path di dalam callback hook Anda.

    Melacak aktivitas subagen

    Gunakan hook SubagentStop untuk memantau penyelesaian subagen. tool_use_id membantu menghubungkan panggilan agen induk dengan subagen mereka:

    Operasi async dalam hooks

    Hooks dapat melakukan operasi async seperti permintaan HTTP. Tangani kesalahan dengan baik dengan menangkap pengecualian daripada melemparnya. Di TypeScript, teruskan signal ke fetch() sehingga permintaan dibatalkan jika hook kedaluwarsa:

    Mengirim notifikasi (hanya TypeScript)

    Gunakan hook Notification untuk menerima pembaruan status dari agen dan meneruskannya ke layanan eksternal seperti Slack atau dashboard pemantauan:

    TypeScript
    import { query, HookCallback, NotificationHookInput } from "@anthropic-ai/claude-agent-sdk";
    
    const notificationHandler: HookCallback = async (input, toolUseID, { signal }) => {
      const notification = input as NotificationHookInput;
    
      await fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', {
        method: 'POST',
        body: JSON.stringify({
          text: `Agent status: ${notification.message}`
        }),
        signal
      });
    
      return {};
    };
    
    for await (const message of query({
      prompt: "Analyze this codebase",
      options: {
        hooks: {
          Notification: [{ hooks: [notificationHandler] }]
        }
      }
    })) {
      console.log(message);
    }

    Perbaiki masalah umum

    Bagian ini mencakup masalah umum dan cara menyelesaikannya.

    Hook tidak dipicu

    • Verifikasi nama acara hook benar dan peka huruf besar-kecil (PreToolUse, bukan preToolUse)
    • Periksa bahwa pola matcher Anda cocok dengan nama tool dengan tepat
    • Pastikan hook berada di bawah tipe acara yang benar dalam options.hooks
    • Untuk hook SubagentStop, Stop, SessionStart, SessionEnd, dan Notification, matcher diabaikan. Hook ini dipicu untuk semua acara dari tipe itu.
    • Hook mungkin tidak dipicu ketika agen mencapai batas max_turns karena sesi berakhir sebelum hook dapat dieksekusi

    Matcher tidak memfilter seperti yang diharapkan

    Matcher hanya mencocokkan nama tool, bukan jalur file atau argumen lainnya. Untuk memfilter berdasarkan jalur file, periksa tool_input.file_path di dalam hook Anda:

    const myHook: HookCallback = async (input, toolUseID, { signal }) => {
      const preInput = input as PreToolUseHookInput;
      const filePath = preInput.tool_input?.file_path as string;
      if (!filePath?.endsWith('.md')) return {};  // Skip non-markdown files
      // Process markdown files...
    };

    Hook timeout

    • Tingkatkan nilai timeout dalam konfigurasi HookMatcher
    • Gunakan AbortSignal dari argumen callback ketiga untuk menangani pembatalan dengan baik di TypeScript

    Tool diblokir secara tidak terduga

    • Periksa semua hook PreToolUse untuk pengembalian permissionDecision: 'deny'
    • Tambahkan logging ke hook Anda untuk melihat apa permissionDecisionReason yang mereka kembalikan
    • Verifikasi pola matcher tidak terlalu luas (matcher kosong cocok dengan semua tool)

    Input yang dimodifikasi tidak diterapkan

    • Pastikan updatedInput berada di dalam hookSpecificOutput, bukan di tingkat atas:

      return {
        hookSpecificOutput: {
          hookEventName: input.hook_event_name,
          permissionDecision: 'allow',
          updatedInput: { command: 'new command' }
        }
      };
    • Anda juga harus mengembalikan permissionDecision: 'allow' agar modifikasi input berlaku

    Hook sesi tidak tersedia

    Hook SessionStart, SessionEnd, dan Notification hanya tersedia di TypeScript SDK. Python SDK tidak mendukung acara ini karena keterbatasan setup.

    Prompt izin subagen berlipat ganda

    Saat memijahkan beberapa subagen, masing-masing mungkin meminta izin secara terpisah. Subagen tidak secara otomatis mewarisi izin agen induk. Untuk menghindari prompt berulang, gunakan hook PreToolUse untuk persetujuan otomatis tool tertentu, atau konfigurasi aturan izin yang berlaku untuk sesi subagen.

    Loop hook rekursif dengan subagen

    Hook UserPromptSubmit yang memijahkan subagen dapat membuat loop tak terbatas jika subagen tersebut memicu hook yang sama. Untuk mencegah ini:

    • Periksa indikator subagen dalam input hook sebelum memijahkan
    • Gunakan bidang parent_tool_use_id untuk mendeteksi jika Anda sudah dalam konteks subagen
    • Batasi hook hanya berjalan untuk sesi agen tingkat atas

    systemMessage tidak muncul dalam output

    Bidang systemMessage menambahkan konteks ke percakapan yang model lihat, tetapi mungkin tidak muncul di semua mode output SDK. Jika Anda perlu menampilkan keputusan hook ke aplikasi Anda, log mereka secara terpisah atau gunakan saluran output khusus.

    Pelajari lebih lanjut

    • Permissions: kontrol apa yang dapat dilakukan agen Anda
    • Custom Tools: bangun tool untuk memperluas kemampuan agen
    • TypeScript SDK Reference
    • Python SDK Reference
    • Hook yang tersedia
    • Kasus penggunaan umum
    • Konfigurasi hooks
    • Matchers
    • Input fungsi callback
    • Data input
    • Output callback
    • Tangani skenario lanjutan
    • Chaining multiple hooks
    • Matcher spesifik tool dengan regex
    • Melacak aktivitas subagen
    • Operasi async dalam hooks
    • Mengirim notifikasi (hanya TypeScript)
    • Perbaiki masalah umum
    • Hook tidak dipicu
    • Matcher tidak memfilter seperti yang diharapkan
    • Hook timeout
    • Tool diblokir secara tidak terduga
    • Input yang dimodifikasi tidak diterapkan
    • Hook sesi tidak tersedia
    • Prompt izin subagen berlipat ganda
    • Loop hook rekursif dengan subagen
    • systemMessage tidak muncul dalam output
    • Pelajari lebih lanjut
    import asyncio
    from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher
    
    # Define a hook callback that receives tool call details
    async def protect_env_files(input_data, tool_use_id, context):
        # Extract the file path from the tool's input arguments
        file_path = input_data['tool_input'].get('file_path', '')
        file_name = file_path.split('/')[-1]
    
        # Block the operation if targeting a .env file
        if file_name == '.env':
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Cannot modify .env files'
                }
            }
    
        # Return empty object to allow the operation
        return {}
    
    async def main():
        async for message in query(
            prompt="Update the database configuration",
            options=ClaudeAgentOptions(
                hooks={
                    # Register the hook for PreToolUse events
                    # The matcher filters to only Write and Edit tool calls
                    'PreToolUse': [HookMatcher(matcher='Write|Edit', hooks=[protect_env_files])]
                }
            )
        ):
            print(message)
    
    asyncio.run(main())
    StopYaYaPenghentian eksekusi agenSimpan status sesi sebelum keluar
    SubagentStartTidakYaInisialisasi subagenLacak pemijahan tugas paralel
    SubagentStopYaYaPenyelesaian subagenAgregasi hasil dari tugas paralel
    PreCompactYaYaPermintaan pemadatan percakapanArsipkan transkrip lengkap sebelum merangkum
    PermissionRequestTidakYaDialog izin akan ditampilkanPenanganan izin kustom
    SessionStartTidakYaInisialisasi sesiInisialisasi logging dan telemetri
    SessionEndTidakYaPenghentian sesiBersihkan sumber daya sementara
    NotificationTidakYaPesan status agenKirim pembaruan status agen ke Slack atau PagerDuty
    Diperlukan. Array fungsi callback untuk dieksekusi ketika pola cocok
    timeoutnumber60Timeout dalam detik; tingkatkan untuk hooks yang melakukan panggilan API eksternal
    Pesan kesalahan dari kegagalan eksekusi tool
    PostToolUseFailureTS
    is_interruptbooleanApakah kegagalan disebabkan oleh gangguanPostToolUseFailureTS
    promptstringTeks prompt penggunaUserPromptSubmit
    stop_hook_activebooleanApakah hook stop sedang diprosesStop, SubagentStop
    agent_idstringPengidentifikasi unik untuk subagenSubagentStartTS, SubagentStopTS
    agent_typestringTipe/peran subagenSubagentStartTS
    agent_transcript_pathstringJalur ke transkrip percakapan subagenSubagentStopTS
    triggerstringApa yang memicu pemadatan: manual atau autoPreCompact
    custom_instructionsstringInstruksi kustom yang disediakan untuk pemadatanPreCompact
    permission_suggestionsarrayPembaruan izin yang disarankan untuk toolPermissionRequestTS
    sourcestringBagaimana sesi dimulai: startup, resume, clear, atau compactSessionStartTS
    reasonstringMengapa sesi berakhir: clear, logout, prompt_input_exit, bypass_permissions_disabled, atau otherSessionEndTS
    messagestringPesan status dari agenNotificationTS
    notification_typestringTipe notifikasi: permission_prompt, idle_prompt, auth_success, atau elicitation_dialogNotificationTS
    titlestringJudul opsional yang ditetapkan oleh agenNotificationTS
    async def log_tool_calls(input_data, tool_use_id, context):
        if input_data['hook_event_name'] == 'PreToolUse':
            print(f"Tool: {input_data['tool_name']}")
            print(f"Input: {input_data['tool_input']}")
        return {}
    Pesan yang disuntikkan ke dalam percakapan untuk Claude lihat
    object
    PreToolUse
    Input tool yang dimodifikasi (memerlukan permissionDecision: 'allow')
    additionalContextstringPostToolUse, UserPromptSubmit, SessionStartTS, SubagentStartTSKonteks yang ditambahkan ke percakapan
    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 {
                # Top-level field: inject guidance into the conversation
                'systemMessage': 'Remember: system directories like /etc are protected.',
                # hookSpecificOutput: block the operation
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Writing to /etc is not allowed'
                }
            }
        return {}
    async def block_dangerous_commands(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PreToolUse':
            return {}
    
        command = input_data['tool_input'].get('command', '')
    
        if 'rm -rf /' in command:
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Dangerous command blocked: rm -rf /'
                }
            }
        return {}
    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 {}
    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', 'LS']
        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 {}
    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                HookMatcher(hooks=[rate_limiter]),        # First: check rate limits
                HookMatcher(hooks=[authorization_check]), # Second: verify permissions
                HookMatcher(hooks=[input_sanitizer]),     # Third: sanitize inputs
                HookMatcher(hooks=[audit_logger])         # Last: log the action
            ]
        }
    )
    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                # Match file modification tools
                HookMatcher(matcher='Write|Edit|Delete', hooks=[file_security_hook]),
    
                # Match all MCP tools
                HookMatcher(matcher='^mcp__', hooks=[mcp_audit_hook]),
    
                # Match everything (no matcher)
                HookMatcher(hooks=[global_logger])
            ]
        }
    )
    async def subagent_tracker(input_data, tool_use_id, context):
        if input_data['hook_event_name'] == 'SubagentStop':
            print(f"[SUBAGENT] Completed")
            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])]
        }
    )
    import aiohttp
    from datetime import datetime
    
    async def webhook_notifier(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PostToolUse':
            return {}
    
        try:
            async with aiohttp.ClientSession() as session:
                await session.post(
                    'https://api.example.com/webhook',
                    json={
                        'tool': input_data['tool_name'],
                        'timestamp': datetime.now().isoformat()
                    }
                )
        except Exception as e:
            print(f'Webhook request failed: {e}')
    
        return {}

    Sertakan hookEventName dalam hookSpecificOutput untuk mengidentifikasi tipe hook mana output tersebut