27%

1.5 Agent SDK Hooks

Agent SDK Hooks คืออะไร

Hooks คือ callback functions ที่ถูกเรียกในจุดต่างๆ ของ agent lifecycle — ก่อน/หลังเรียก tool, เมื่อเกิด error, ตอนเริ่ม/จบ turn เป็นต้น Hooks ให้เราแทรก custom logic เข้าไปในการทำงานของ agent โดยไม่ต้องแก้ core logic — ใช้สำหรับ logging, monitoring, security checks, หรือ modify behavior

ใน Claude Code ecosystem hooks ถูกกำหนดใน settings (~/.claude/settings.json) และทำงานเป็น shell commands ที่ execute ณ จุดที่กำหนด

Hook Types ใน Claude Code

Pre-tool Hooks

ทำงานก่อนที่ tool จะถูก execute:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "python3 ~/.claude/hooks/validate-bash.py"
      },
      {
        "matcher": "Edit",
        "command": "python3 ~/.claude/hooks/check-file-permissions.py"
      }
    ]
  }
}

Post-tool Hooks

ทำงานหลังจาก tool execute เสร็จ:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "command": "python3 ~/.claude/hooks/lint-written-file.py"
      }
    ]
  }
}

Notification Hooks

แจ้งเตือนเมื่อเกิด events:

{
  "hooks": {
    "Notification": [
      {
        "command": "terminal-notifier -message '$NOTIFICATION_MESSAGE'"
      }
    ]
  }
}

Hook Input/Output Protocol

Hooks รับ input เป็น JSON ผ่าน stdin และส่ง output ผ่าน stdout:

Input ที่ Hook ได้รับ

{
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf /important/data"
  },
  "session_id": "abc123",
  "agent_type": "main"
}

Output ที่ Hook ส่งกลับ

{
  "decision": "block",
  "reason": "Destructive command detected: rm -rf on important directory"
}

Decision values:

  • "allow" — อนุญาตให้ tool ทำงาน (default ถ้าไม่มี output)
  • "block" — ห้ามไม่ให้ tool ทำงาน พร้อมแจ้ง reason
  • "modify" — แก้ไข tool input ก่อนส่งต่อ (advanced)

ตัวอย่าง Hook Scripts

Security Hook — Block Dangerous Commands

#!/usr/bin/env python3
import sys, json

data = json.load(sys.stdin)
command = data.get("tool_input", {}).get("command", "")

DANGEROUS_PATTERNS = [
    "rm -rf /",
    "DROP TABLE",
    "chmod 777",
    "> /dev/sda",
]

for pattern in DANGEROUS_PATTERNS:
    if pattern in command:
        print(json.dumps({
            "decision": "block",
            "reason": f"Blocked: dangerous pattern '{pattern}' detected"
        }))
        sys.exit(0)

# Allow by default
print(json.dumps({"decision": "allow"}))

Logging Hook — Track All Tool Usage

#!/usr/bin/env python3
import sys, json, datetime

data = json.load(sys.stdin)

log_entry = {
    "timestamp": datetime.datetime.now().isoformat(),
    "tool": data.get("tool_name"),
    "input_summary": str(data.get("tool_input", ""))[:200],
    "session": data.get("session_id")
}

with open("/tmp/claude-tool-log.jsonl", "a") as f:
    f.write(json.dumps(log_entry) + "\n")

print(json.dumps({"decision": "allow"}))

Cost Tracking Hook

#!/usr/bin/env python3
import sys, json, os

data = json.load(sys.stdin)
BUDGET_FILE = os.path.expanduser("~/.claude/budget.json")

try:
    with open(BUDGET_FILE) as f:
        budget = json.load(f)
except FileNotFoundError:
    budget = {"spent": 0, "limit": 10.0}

if budget["spent"] >= budget["limit"]:
    print(json.dumps({
        "decision": "block",
        "reason": f"Budget exhausted: ${budget['spent']:.2f} / ${budget['limit']:.2f}"
    }))
else:
    print(json.dumps({"decision": "allow"}))

Hook Composition

Hooks ทำงานเป็น pipeline — ถ้ามีหลาย hooks สำหรับ event เดียว จะ execute ตามลำดับ ถ้าตัวใดตัวหนึ่ง block การ execute หยุดทันที:

{
  "hooks": {
    "PreToolUse": [
      {"matcher": "Bash", "command": "hook-security.py"},
      {"matcher": "Bash", "command": "hook-cost-check.py"},
      {"matcher": "Bash", "command": "hook-rate-limit.py"}
    ]
  }
}

Key Concepts

  • Matcher — กำหนดว่า hook จะ fire สำหรับ tool ไหน (เช่น "Bash", "Edit", "*" สำหรับทุก tool)
  • Non-blocking Default — ถ้า hook crash หรือ timeout agent ยังทำงานต่อได้ (fail-open)
  • Context Available — hooks ได้รับ session info, tool input, agent type ครบเพื่อ decision making
  • Stateless Preferred — hooks ควรเป็น stateless (อ่าน state จาก file/db) เพื่อ reliability

Exam Tips

  • จำ hook types: PreToolUse, PostToolUse, Notification — แต่ละตัวใช้ตอนไหน
  • Hook ที่ return "block" จะหยุด tool execution ทันที — agent จะเห็น error message จาก reason
  • Hooks execute as shell commands ไม่ใช่ in-process — มี overhead ของ process spawn
  • ข้อสอบอาจถามเรื่อง security hooks: ใช้ PreToolUse เพื่อ validate ก่อน execute
  • Hooks เหมาะสำหรับ cross-cutting concerns (logging, security, cost) ไม่ควรใส่ business logic