Telegram + Hooks + Watchdog — 點樣 run 起

2026-04-23 — Steven 要 save 低,因為呢日一次過爆 3 個 bug:agent indicator 冇 show、mcp-watchdog 壞 4 日、修好後 spam 警告。借呢次做完整架構文檔。


Part 1 — 你 send TG 俾 CC 之後發生咩事

你 send msg
  ↓
UserPromptSubmit hook
  ↓
tg-thinking.sh → send「🤔 思考中...」→ 記低 msg_id
  ↓
Claude 開始用工具
  ↓
每個 tool call
  ↓
tg-tool-update.sh → edit 同一條 msg (加 #1 #2 #3... 計數 + tool icon)
  ↓
Claude 完成
  ↓
新 TG msg(final reply)→ 你手機 DING

設計哲學

  • 一條 msg 代表「Claude 思考中」—— edit 唔會 trigger 通知
  • 新 msg 先有聲 —— 只有最終答案先 ping 你

檔案

  • ~/.claude/hooks/tg-thinking.sh
  • ~/.claude/hooks/tg-tool-update.sh
  • State: /tmp/tg-thinking-${state_base}.json

Part 2 — Agent hook 點 work

正常流程(foreground)

Main Claude 派 subagent (例 Copy Agent)
  ↓
tg-agent-start.sh → 寫「/tmp/tg-agent/${state_base}-current.txt」= "Copy Agent"
  ↓
Subagent 跑 (可能幾分鐘)
  ↓
tg-agent-done.sh → 清走 current.txt

Background agent (2026-04-23 新加)

Main 派 subagent 用 run_in_background=true
  ↓
tg-agent-start.sh 寫「/tmp/tg-agent/${state_base}-bg-${tool_use_id}.txt」= "Research Agent"
  ↓
Main 唔等 subagent return,繼續做其他嘢
  ↓
tg-agent-done.sh 知道係 background → NOT clear state
  ↓
subagent 真正完成
  ↓
tg-subagent-stop.sh (SubagentStop hook) → 清 bg-*.txt

tg-tool-update.sh 每次 edit thinking msg 時讀 state files → 顯示:

🤔 思考中... #5
👤 Copy Agent (foreground)
🤖 BG: Research Agent, QA Agent (background,可以多於一個)

已知 bug(2026-04-23 未修)

Foreground agent 跑時,Main 會 block 住等 subagent return。呢段時間:

  • 冇 tool call in Main → tg-tool-update.sh 唔 fire
  • Subagent 內部 tool calls → 只 trigger subagent 自己嘅 hooks,唔影響 Main 嘅 thinking msg
  • 結果:3 分鐘 Research Agent run 期間,TG msg 凍結冇更新

Steven 批咗修,未做。要做嘅:

  • Step A:tg-agent-start.sh 自己主動 edit TG msg(唔淨係寫 state)
  • Step B:背景 watcher 每 15 秒 update 一次 elapsed time
  • Step C:改 hook 執行 order

Part 3 — 點解 TG 會死 + 保安巡查

TG 點會死

每個 CC session(cc / cc2 / cc3)背後有個 bun 程序,責任係 long-poll Telegram 嘅 getUpdates API,將 msg 送返去 Claude。

Bun 會靜靜死嘅原因:

  • 網絡問題
  • Plugin crash
  • RAM 壓力令 MCP RPC pipe 凍結
  • Telegram API 限流

結果:你 send msg → inbox 有但 bun 唔送 → 你冇回應,以為 Claude 死咗

保安 = mcp-watchdog(systemd timer,每 1 分鐘一次)

每 tick 對每個 session 做 4 個 check:

Check睇咩Failure mode
1bot.pid 檔案存在 + PID 仲 alivePLUGIN_DEAD
2Claude 主程序仲 aliveCLAUDE_DEAD
3Inbox 冇塞住STUCK_INBOX
4last_sid 檔案 mtime < thresholdMCP_STALE

last_sid 係 bun 每次 getUpdates 成功都會覆寫一次嘅檔案(~30 秒一次)。如果 mtime 太舊但 bun 仲 alive → pipe 斷咗。

Alert 邏輯(2026-04-23 改良)

  • Two-tick confirmation:第 1 個 bad tick 靜音(可能係瞬時),第 2 個 tick 同狀態先確認
  • 每個狀態只報 1 次:用 /tmp/mcp-watchdog-alerted-${session} marker 防重複
  • 狀態轉變或恢復 → 清 marker → 下次可以再 alert
  • 恢復會 send「✅ [session] MCP 恢復」

Mode

  • notify(當前):send 警告俾 Steven,要佢手動 bash ~/bin/start-tg.sh cc3
  • restart(未 enable):watchdog 自動執行 start-tg.sh

WATCHDOG_ACTION=restart 就自動化。Steven 未開因為要先信 watchdog 唔會誤判。

Threshold

  • MCP_STALE_THRESHOLD=1800(30 分鐘)—— 2026-04-23 由 600s 拉到 1800s
  • 原因:idle session 唔應該觸發(你冇 send msg 就唔會有新 update,係合理嘅)

Part 4 — 2026-04-23 嗰日發生咩事(post-mortem)

時間線

  • Apr 19:home 目錄重組,mcp-watchdog.sh 由 /home/claude/ 搬去 /home/claude/scripts/
  • Apr 19-23:systemd unit ExecStart 路徑冇更新 → watchdog 每分鐘 203/EXEC 失敗,冇警告
  • Apr 23 11:30:RAM 91% 警告彈出(watchdog 壞咗冇清 zombie sessions)
  • Apr 23 11:43:Claude 修返 systemd path
  • Apr 23 11:44-12:22:watchdog 醒返,見 cc2/cc3 last_sid 35 分鐘唔更新 → 當壞咗 → 每分鐘 spam 警告
  • Apr 23 12:25:Claude 加 dedup + 拉 threshold → spam 停

學到嘢

  1. Home reorg 要 grep /etc/systemd/ 搵用到嘅 script path
  2. Oneshot systemd service fail 時好靜,需要 journalctl 定期 check
  3. Watchdog 設計要有 rate limit + 兩級確認,唔好每 tick 報一次
  4. Idle session 唔更新 last_sid 係 normal,threshold 10 min 太短

檔案位置 cheatsheet

類別路徑
Hook scripts~/.claude/hooks/tg-*.sh
Watchdog script~/scripts/mcp-watchdog.sh
Systemd unit/etc/systemd/system/mcp-watchdog.{service,timer}
Hook config~/.claude/settings.json
Thinking state/tmp/tg-thinking-*.json
Agent state/tmp/tg-agent/*.txt
Watchdog state/tmp/mcp-watchdog-state.json
Alert markers/tmp/mcp-watchdog-alerted-*
Channel dirs~/.claude/channels/telegram{,-cc2,-cc3,-team-cc}/