pi-watchdog-supervisor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.tmp/2026-07-08_pi_watchdog_extension_requirement.md +907 -0
  2. package/.tmp/2026-07-08_pi_watchdog_high_level_spec.md +192 -0
  3. package/.tmp/2026-07-08_pi_watchdog_task01_low_level_spec.md +234 -0
  4. package/.tmp/2026-07-08_pi_watchdog_task02_low_level_spec.md +158 -0
  5. package/.tmp/2026-07-08_pi_watchdog_task03_low_level_spec.md +175 -0
  6. package/.tmp/2026-07-08_pi_watchdog_task04_low_level_spec.md +151 -0
  7. package/.tmp/2026-07-08_pi_watchdog_task05_low_level_spec.md +116 -0
  8. package/.tmp/2026-07-08_pi_watchdog_task06_low_level_spec.md +69 -0
  9. package/.tmp/detect_approach.patch +253 -0
  10. package/README.md +176 -0
  11. package/examples/AGENTS.md +27 -0
  12. package/examples/watchdog-supervisor.json +12 -0
  13. package/package.json +39 -0
  14. package/prompts/watchdog-agent.md +36 -0
  15. package/skills/watchdog-supervisor/SKILL.md +35 -0
  16. package/src/checker.ts +54 -0
  17. package/src/collector.ts +223 -0
  18. package/src/commands.ts +159 -0
  19. package/src/config.ts +80 -0
  20. package/src/detector.ts +150 -0
  21. package/src/index.ts +94 -0
  22. package/src/integrations/gotgenes-subagents.ts +45 -0
  23. package/src/lmdebug.ts +128 -0
  24. package/src/normalize.ts +40 -0
  25. package/src/registry.ts +121 -0
  26. package/src/store.ts +137 -0
  27. package/src/tools.ts +289 -0
  28. package/src/types.ts +80 -0
  29. package/test/Screenshot_20260708_220337.png +0 -0
  30. package/test/checker.test.ts +118 -0
  31. package/test/collector.test.ts +201 -0
  32. package/test/config.test.ts +131 -0
  33. package/test/detector.test.ts +224 -0
  34. package/test/integration.test.ts +57 -0
  35. package/test/lmdebug.test.ts +114 -0
  36. package/test/normalize.test.ts +78 -0
  37. package/test/registry.test.ts +149 -0
  38. package/test/store.test.ts +176 -0
  39. package/test/tools.test.ts +315 -0
  40. package/tsconfig.json +13 -0
  41. package/vitest.config.ts +7 -0
@@ -0,0 +1,175 @@
1
+ # Task03 — 事件收集(子 session 自我回報 + 共享 store):Low-Level Detail Spec
2
+
3
+ - Date: 2026-07-08
4
+ - Source: `.tmp/2026-07-08_pi_watchdog_high_level_spec.md`(Task03);requirement §7.1, §7.2, §10
5
+ - Status: v1 — 使用者已指示直接實作,本文件同步作為實作依據與追溯紀錄
6
+ - Perspective: TDD
7
+
8
+ ---
9
+
10
+ ## 1. Spike 結果(讀 `@gotgenes/pi-subagents@18.0.1` 原始碼,已完成)
11
+
12
+ `src/lifecycle/create-subagent-session.ts` 確認:
13
+
14
+ 1. **子 session 在同一個 process 內執行**(in-process),且「children always load the parent's extensions」— `await session.bindExtensions({})` 會重跑我們的 extension factory,`session_start` 會在子 session 內觸發。
15
+ 2. `subagents:child:session-created` `{ sessionId, parentSessionId }` 在 `bindExtensions()` **之前**同步發布於**父的 event bus** — 父端 instance 可在子端 extension 初始化前先登記該 child sessionId。
16
+ 3. Agent record id(`randomUUID`,來自 subagent-manager)**≠** child sessionId,事件與 record 均無兩者對應 — 需自建關聯。
17
+ 4. 每個 spawn 的 assembly 序列為 `spawning → session-created → bindExtensions → started`,同 process 同步發布。
18
+
19
+ ### 架構簡化決策(偏離 high-level spec,理由如下)
20
+
21
+ 共享 store 改用 **`globalThis` Symbol 註冊的 in-memory store**(仿 pi-subagents 自身的 `Symbol.for()` service 發布模式),**不用 JSONL 檔案**:
22
+
23
+ - Spike 證實父子 session 同 process → in-memory 天然共享、原子、零 I/O。
24
+ - 跨 process 場景(獨立 pi session 經 pi-intercom)本來就在 MVP 範圍外。
25
+ - 檔案格式、鎖、清理等複雜度全部消失。
26
+
27
+ ---
28
+
29
+ ## 2. 範圍
30
+
31
+ ### In scope
32
+
33
+ - `src/normalize.ts`:`normalizeCommand()`、`hashOutput()`(requirement §7.2)。
34
+ - `src/store.ts`:globalThis 共享 store — child 登記、per-target ring buffer(`maxEventsPerAgent`)、agentId↔sessionId 關聯。
35
+ - `src/collector.ts`:子 session 端事件收集 — 監聽自身 `tool_call` / `tool_result`,萃取 commandKey / outputHash / edit 判定,寫入 store。
36
+ - `src/index.ts`:角色分流 — 判斷自己是 parent 或 child instance,child 走 collector、parent 維持 Task02 行為並登記 child sessionId 與 id 關聯。
37
+ - `/watchdog inspect <targetId>` 實作(取代 Task01 stub):顯示該 target 最近的 compact 事件。
38
+
39
+ ### Out of scope
40
+
41
+ - stuck 偵測(Task04);`watchdog_read_events` 等 tools(Task05,本 Task 先以 `/watchdog inspect` 驗證資料流)。
42
+
43
+ ---
44
+
45
+ ## 3. 資料模型(requirement §10 子集)
46
+
47
+ ```ts
48
+ // src/types.ts 新增
49
+ type WatchdogEventType =
50
+ | 'subagent_created' | 'subagent_started' | 'subagent_completed' | 'subagent_failed'
51
+ | 'tool_call' | 'tool_result' | 'edit';
52
+
53
+ type WatchdogEvent = {
54
+ id: string; // `${sessionId}-${seq}`
55
+ targetId: string; // child sessionId(store 主鍵)
56
+ at: number;
57
+ type: WatchdogEventType;
58
+ summary: string; // e.g. `bash: rg "TOKEN" src`
59
+ commandKey?: string; // normalized command(bash 類)或 toolName+主要參數
60
+ outputHash?: string;
61
+ outputPreview?: string; // 前 maxPreviewLines 行
62
+ };
63
+ ```
64
+
65
+ ### 共享 store 契約
66
+
67
+ ```ts
68
+ // src/store.ts — 發布於 Symbol.for('pi-watchdog-supervisor:store')
69
+ type WatchdogStore = {
70
+ registerChild: (sessionId: string, parentSessionId?: string) => void;
71
+ getChild: (sessionId: string) => { parentSessionId?: string } | undefined;
72
+ linkAgent: (agentId: string, sessionId: string) => void;
73
+ resolveTargetKey: (idOrSessionId: string) => string | undefined; // agentId 或 sessionId → sessionId
74
+ appendEvent: (sessionId: string, event: Omit<WatchdogEvent, 'id' | 'targetId'>) => void;
75
+ getEvents: (sessionId: string, limit?: number) => WatchdogEvent[];
76
+ };
77
+
78
+ // 任一 instance 先載入者建立;之後的 instance 取得同一份
79
+ const getOrCreateStore = (maxEventsPerAgent: number): WatchdogStore => { ... }
80
+ ```
81
+
82
+ ### agentId ↔ sessionId 關聯(heuristic,需標註於程式註解)
83
+
84
+ 父端維護 FIFO:收到 `subagents:child:session-created` push sessionId;收到 `subagents:started { id }` 時 shift 最舊未關聯的 sessionId → `linkAgent(id, sessionId)`。同一 spawn 的兩事件在 assembly 序列中相鄰(spike 事實 4),佇列化併發時順序仍成立;此為 MVP heuristic,偏差時僅影響顯示關聯、不影響事件收集正確性。
85
+
86
+ ---
87
+
88
+ ## 4. 正規化規則(requirement §7.2)
89
+
90
+ ```ts
91
+ // normalizeCommand(raw: string): string
92
+ strip ANSI escape codes → trim → collapse 連續空白為單一空格
93
+
94
+ // hashOutput(raw: string, opts: { maxBytes?: number; previewLines: number })
95
+ // : { hash: string; preview: string }
96
+ strip ANSI → truncate 至 maxBytes(default 65536)→ sha256 hex
97
+ preview = 正規化後前 previewLines 行(maxPreviewLines config,default 20)
98
+ ```
99
+
100
+ commandKey 萃取:`tool_call` 事件 args 中 `command`(bash 類)優先;否則 `toolName + ' ' + 主要字串參數(path/pattern 等,取第一個 string 值)`。edit 判定:toolName ∈ {edit, write, patch, multi_edit}(不分大小寫比對)→ 事件 type `'edit'`。
101
+
102
+ ---
103
+
104
+ ## 5. 實作步驟(TDD)
105
+
106
+ ### Step 1 — `normalize.ts`(紅→綠)
107
+
108
+ `test/normalize.test.ts`:ANSI 移除、空白 collapse、trim;hash 對相同輸入穩定、不同輸入不同;truncate 生效;preview 行數上限;空字串輸入。
109
+
110
+ ### Step 2 — `store.ts`(紅→綠)
111
+
112
+ `test/store.test.ts`:
113
+
114
+ 1. `getOrCreateStore` 兩次呼叫回傳同一 instance(globalThis 共享);測試間需清除 Symbol key。
115
+ 2. `registerChild` / `getChild`。
116
+ 3. `appendEvent` 自動編 seq id 與 targetId;超過 `maxEventsPerAgent` 時丟最舊(ring buffer)。
117
+ 4. `getEvents(sessionId, limit)` 回傳最新 limit 筆(時序遞增)。
118
+ 5. `linkAgent` + `resolveTargetKey`:agentId 與 sessionId 皆可解析;未知 id → undefined。
119
+ 6. FIFO 關聯 helper(若獨立成函式):session-created ×2 + started ×2 → 依序配對。
120
+
121
+ ### Step 3 — `collector.ts`(紅→綠)
122
+
123
+ 以 fake `pi`(`on(event, handler)` 記錄 handler、可手動觸發)測試:
124
+
125
+ 1. `tool_call`(bash, `command: 'rg "X" src'`)→ store 出現 type `tool_call`、commandKey 正規化後的事件。
126
+ 2. `tool_result` → 事件含 outputHash 與 preview。
127
+ 3. `tool_call`(edit)→ type `'edit'`。
128
+ 4. `tool_result` payload 非預期結構 → 不 throw、略過。
129
+
130
+ 實作介面:`startCollector(pi, store, sessionId, config)` — 訂閱並寫入,回傳 unsubscribe。tool_call/tool_result 的實際 payload 欄位以 `@earendil-works/pi-coding-agent` 型別宣告為準(實作時查 `dist/*.d.ts`,於回報註明實際欄位名)。
131
+
132
+ ### Step 4 — `index.ts` 角色分流 + parent 接線
133
+
134
+ ```txt
135
+ factory 執行(父與子皆會跑)
136
+ ├─ getOrCreateStore(config.maxEventsPerAgent)
137
+ ├─ 訂閱 'subagents:child:session-created' → store.registerChild(...) // 父端才收得到
138
+ ├─ 訂閱 'subagents:started' → FIFO linkAgent // 父端
139
+ ├─ Task02 既有:subagents:* → registry.applyEvent // 父端
140
+ └─ session_start 時:sessionId = ctx.sessionManager.getSessionId()
141
+ └─ if store.getChild(sessionId) → 本 instance 是 child → startCollector(...)
142
+ (child instance 不需 registry/指令行為改變;registerCommand 在子 session 內無害)
143
+ ```
144
+
145
+ - `ctx.sessionManager.getSessionId()` 的實際 API 以型別宣告為準;若無此方法,改用可取得 session 識別的等價 API 並回報。
146
+
147
+ ### Step 5 — `/watchdog inspect <targetId>` + status 整合
148
+
149
+ - `inspect`:`store.resolveTargetKey(arg)` → `getEvents(key, 20)` → 每行 `HH:MM:SS type summary [hash 前 8 碼]`;未知 id → 提示。
150
+ - `status`:target 行尾追加 `events=<count>`(有 buffer 時)。
151
+
152
+ ### Step 6 — 驗證
153
+
154
+ - `npm run type-check`、`npm test` 全綠(既有 32 tests 不得回歸)。
155
+ - Smoke:`pi -e ./src/index.ts` 載入無錯誤。
156
+
157
+ ---
158
+
159
+ ## 6. 驗收標準
160
+
161
+ - [ ] normalize / store / collector / FIFO 關聯 unit tests 全過。
162
+ - [ ] 手動場景(需 TUI + API key + pi-subagents):spawn 一個 task sub-agent 執行數個 tool calls → 父 session `/watchdog inspect <id>` 顯示其 tool_call 事件(含 commandKey 與 output hash)。
163
+ - [ ] type-check + 全部測試通過,既有測試無回歸。
164
+
165
+ ## 7. Revision History
166
+
167
+ | Version | Change |
168
+ |---|---|
169
+ | v1 | 初版:含 spike 結果、globalThis store 簡化決策、FIFO 關聯 heuristic、TDD 步驟 |
170
+
171
+ ## 8. Reference
172
+
173
+ - reference01: `@gotgenes/pi-subagents@18.0.1` `src/lifecycle/create-subagent-session.ts`、`src/lifecycle/child-lifecycle.ts`、`src/lifecycle/subagent-manager.ts`(accessed 2026-07-08)
174
+ - reference02: `.tmp/2026-07-08_pi_watchdog_high_level_spec.md` — Task03 定義
175
+ - reference03: `.tmp/2026-07-08_pi_watchdog_extension_requirement.md` — §7.1, §7.2, §10
@@ -0,0 +1,151 @@
1
+ # Task04 — Stuck 偵測器:Low-Level Detail Spec
2
+
3
+ - Date: 2026-07-08
4
+ - Source: `.tmp/2026-07-08_pi_watchdog_high_level_spec.md`(Task04);requirement §7.3, §9.3, §15
5
+ - Status: v1 — 使用者已指示直接實作
6
+ - Perspective: TDD
7
+ - 前置:Task03 已完成(事件 buffer 以 child sessionId 為 key 存於共享 store)
8
+
9
+ ---
10
+
11
+ ## 1. 範圍
12
+
13
+ ### In scope
14
+
15
+ - `src/detector.ts`:純函式偵測器 — 吃 `WatchdogEvent[]` + config + now,回傳 `StuckAnalysis`。
16
+ - 四種 evidence(requirement §9.3):`repeated_command`、`repeated_output`、`typecheck_loop`、`idle_no_progress`。
17
+ - Cooldown + 同 evidence key 去重:狀態存於共享 store(Task05 的 tools 會從 watchdog 子 session 呼叫,必須跨 instance 共享)。
18
+ - `/watchdog status`:target 行追加 `STUCK?` 標記;`/watchdog inspect`:尾端附偵測分析摘要。
19
+
20
+ ### Out of scope
21
+
22
+ - alert 發送與 rescue message 組裝(Task05);watchdog_* tools(Task05)。
23
+
24
+ ---
25
+
26
+ ## 2. 資料模型
27
+
28
+ ```ts
29
+ // src/types.ts 新增
30
+ type StuckEvidenceType =
31
+ | 'repeated_command' | 'repeated_output' | 'idle_no_progress' | 'typecheck_loop';
32
+
33
+ type StuckEvidence = { type: StuckEvidenceType; summary: string };
34
+
35
+ type StuckAnalysis = {
36
+ likelyStuck: boolean;
37
+ confidence: 'low' | 'medium' | 'high';
38
+ reasons: string[]; // 人讀得懂的一句話 per rule
39
+ evidence: StuckEvidence[];
40
+ evidenceKey: string; // 去重 key:sorted evidence types + 觸發 hash/command 的 sha256 前 16 碼
41
+ };
42
+ ```
43
+
44
+ ### 共享 store 擴充(`src/store.ts`)
45
+
46
+ ```ts
47
+ type WatchdogStore = {
48
+ // ...Task03 既有...
49
+ getLastAlert: (targetId: string) => { at: number; evidenceKey: string } | undefined;
50
+ recordAlert: (targetId: string, evidenceKey: string, at: number) => void;
51
+ };
52
+ ```
53
+
54
+ ---
55
+
56
+ ## 3. 偵測規則(分析範圍 = buffer 內全部事件,buffer 本身已由 `maxEventsPerAgent` 限幅)
57
+
58
+ ### R1 `repeated_command` + R2 `repeated_output`
59
+
60
+ 以 `tool_result` 事件分組 `commandKey`:
61
+ - 某 commandKey 出現次數 ≥ `repeatThreshold`(3)→ `repeated_command`。
62
+ - 且其中相同 `outputHash` 次數 ≥ `repeatThreshold` → 追加 `repeated_output`。
63
+ - 只看**最近的連續尾段**不強制 — MVP 以整個 buffer 計數(buffer 已限幅 200 筆);若同 commandKey 之後出現過 `edit` 事件,該 commandKey 計數歸零重算(有進展就不算迴圈)。
64
+
65
+ ### R3 `typecheck_loop`
66
+
67
+ commandKey 含 `tsc` / `type-check` / `typecheck`(不分大小寫、字邊界不要求)的 `tool_result`,相同 `outputHash` 出現 ≥ `typecheckRepeatThreshold`(2)→ `typecheck_loop`。同樣受「後續 edit 歸零」規則約束。
68
+
69
+ ### R4 `idle_no_progress`
70
+
71
+ - buffer 內最後一筆事件距 `now` < 60s(tools 仍在動),且
72
+ - 最後一筆 `edit` 事件距 `now` ≥ `idleNoProgressSec`(300s)(或 buffer 內完全沒有 edit 且最早事件距 now ≥ idleNoProgressSec),且
73
+ - 期間內 `tool_call`/`tool_result` ≥ 5 筆(避免剛啟動誤報)。
74
+
75
+ ### Confidence 對映(確定性)
76
+
77
+ ```txt
78
+ evidence 種類數 0 → likelyStuck=false, confidence='low'
79
+ evidence 種類數 1 → likelyStuck=true, confidence='medium'
80
+ evidence 種類數 ≥2 → likelyStuck=true, confidence='high'
81
+ ```
82
+
83
+ ### Cooldown 判定(`shouldAlert`)
84
+
85
+ ```ts
86
+ // src/detector.ts
87
+ const shouldAlert = (
88
+ lastAlert: { at: number; evidenceKey: string } | undefined,
89
+ analysis: StuckAnalysis,
90
+ now: number,
91
+ cooldownSec: number,
92
+ ): boolean
93
+ ```
94
+
95
+ - `likelyStuck === false` → false。
96
+ - 無 lastAlert → true。
97
+ - `now - lastAlert.at < cooldownSec*1000` **且** `evidenceKey === lastAlert.evidenceKey` → false(同證據冷卻中)。
98
+ - evidenceKey 不同 → true(新證據不受冷卻限制);冷卻期滿 → true。
99
+
100
+ ---
101
+
102
+ ## 4. 實作步驟(TDD)
103
+
104
+ ### Step 1 — `detector.ts` 規則(紅→綠)
105
+
106
+ `test/detector.test.ts`(以 helper 建造事件序列):
107
+
108
+ 1. 同 commandKey 的 tool_result ×3(同 hash)→ `repeated_command` + `repeated_output`、confidence='high'、likelyStuck=true。
109
+ 2. 同 commandKey ×3 但 hash 各異 → 只有 `repeated_command`、confidence='medium'。
110
+ 3. 同 commandKey ×2 → 無 evidence、likelyStuck=false。
111
+ 4. 同 commandKey ×3 但中間穿插 `edit` → 計數歸零 → 無 evidence。
112
+ 5. `tsc` commandKey 同 hash ×2 → `typecheck_loop`。
113
+ 6. 最後事件在 30s 內、無 edit ≥ 300s、tool 事件 ≥ 5 → `idle_no_progress`;tool 事件只有 3 筆 → 不觸發;最後事件距 now 120s(agent 已停)→ 不觸發。
114
+ 7. `evidenceKey` 對相同輸入穩定、對不同 evidence 集合不同。
115
+ 8. 空 buffer → likelyStuck=false。
116
+
117
+ ### Step 2 — `shouldAlert`(紅→綠)
118
+
119
+ 測試 §3 cooldown 判定的四個分支。
120
+
121
+ ### Step 3 — store 擴充(紅→綠)
122
+
123
+ `getLastAlert` / `recordAlert` roundtrip;跨 `getOrCreateStore()` 呼叫共享。
124
+
125
+ ### Step 4 — 指令整合
126
+
127
+ - `status`:對每個可解析出事件 buffer 的 target 跑 `detectStuck`,`likelyStuck` 時行尾加 ` !! likely stuck (<confidence>)`。
128
+ - `inspect`:事件列表之後追加 `analysis: likelyStuck=<bool> confidence=<c> reasons=[...]`。
129
+
130
+ ### Step 5 — 驗證
131
+
132
+ `npm run type-check` + `npm test` 全綠(既有 56 tests 不得回歸);`pi -e` smoke test。
133
+
134
+ ---
135
+
136
+ ## 5. 驗收標準
137
+
138
+ - [ ] 高階 spec 驗收:模擬同一 `rg` 指令重複 3 次(同輸出)→ `likelyStuck === true`、evidence 含 `repeated_command`(unit test 覆蓋)。
139
+ - [ ] cooldown:同 evidenceKey 於 `cooldownSec` 內 `shouldAlert` 回 false;新 evidenceKey 回 true(unit test 覆蓋)。
140
+ - [ ] type-check + 全部測試通過,無回歸。
141
+
142
+ ## 6. Revision History
143
+
144
+ | Version | Change |
145
+ |---|---|
146
+ | v1 | 初版:四規則定義、edit 歸零原則、confidence 對映、cooldown 判定、TDD 步驟 |
147
+
148
+ ## 7. Reference
149
+
150
+ - reference01: `.tmp/2026-07-08_pi_watchdog_high_level_spec.md` — Task04 定義
151
+ - reference02: `.tmp/2026-07-08_pi_watchdog_extension_requirement.md` — §7.3, §9.3, §15
@@ -0,0 +1,116 @@
1
+ # Task05 — Watchdog Tools:Low-Level Detail Spec
2
+
3
+ - Date: 2026-07-08
4
+ - Source: `.tmp/2026-07-08_pi_watchdog_high_level_spec.md`(Task05);requirement §8.2, §9, §15
5
+ - Status: v1 — 使用者已指示直接實作
6
+ - Perspective: TDD
7
+ - 前置:Task01–04 已完成並 commit(`929c823`)
8
+
9
+ ---
10
+
11
+ ## 1. 核心設計約束
12
+
13
+ **Tools 是在 watchdog 子 session 的 instance 上執行的**(watchdog sub-agent 呼叫 tool → 該子 session 的 extension instance 的 `execute` 跑)。因此:
14
+
15
+ 1. **alert 通道**:子 instance 的 `pi.sendMessage()` 只會送進**自己的** session。解法:父 instance 於載入時把「alert sink」closure(包住父的 `pi.sendMessage`)註冊進共享 store;子 instance 的 `watchdog_alert_main` 透過 store 呼叫 sink → 訊息進 main session。
16
+ 2. **runtime 狀態共享**:`paused`、`rescueMessageOverride`、config 覆寫必須移入共享 store,否則父端 `/watchdog pause` 對子端 tool 無效。→ `src/state.ts` 廢除,職責併入 store(Task01 的 state 測試改寫為 store 測試)。
17
+ 3. **steer**:in-process 直接用 `getSubagentsService().steer(agentId, message)`(Task02 adapter 擴充)。
18
+
19
+ ### 簡化決策(記錄偏離)
20
+
21
+ - requirement §9.4 的 pi-intercom 備援**不實作**:MVP 架構下父子同 process,parent sink 必然存在;跨 process 場景在 MVP 範圍外(維持高階 spec 判斷)。sink 缺席時(理論上只在測試)回傳明確錯誤訊息。
22
+
23
+ ---
24
+
25
+ ## 2. 範圍
26
+
27
+ ### In scope
28
+
29
+ - `src/tools.ts`:以 `pi.registerTool` 註冊 6 個 tools(requirement §9 schema)。
30
+ - `src/store.ts` 擴充:`setPaused/isPaused`、`setRescueMessage/getRescueMessage`、`setConfigOverride/getConfigOverride`、`setAlertSink/alert`。
31
+ - `src/state.ts` 移除,`src/commands.ts` 的 pause/resume/set 改走 store;`test/state.test.ts` 併入 `test/store.test.ts`。
32
+ - alert 格式:requirement §8.2 `[Watchdog Alert]` 樣板。
33
+
34
+ ### Out of scope
35
+
36
+ - watchdog agent prompt / skill / 範例工作流(Task06);Option B tick。
37
+
38
+ ---
39
+
40
+ ## 3. Tools 定義(parameters 用 `Type.Object`;typebox 來源實作時查證 — pi 文件範例 `import { Type } from "typebox"`,若 pi-coding-agent 有 re-export 則優先)
41
+
42
+ ### 3.1 `watchdog_list_targets`
43
+
44
+ - Input:`{ includeCompleted?: boolean }`(default false → 過濾 status ∈ completed/aborted/stopped/error)。
45
+ - 行為:`getIntegration()` → `syncFromRecords` → registry.list() → 每個 target 併 `detectStuck` 的 `likelyStuck`。
46
+ - Output(text JSON):requirement §9.1 欄位(`repeatedCommandCount` 以 detector 的 repeated_command evidence 有無簡化為 boolean 對映數值 0/N — 從 evidence summary 取 count)。
47
+
48
+ ### 3.2 `watchdog_read_events`
49
+
50
+ - Input:`{ targetId: string; limit?: number }`(`sinceEventId` 不實作 — ring buffer 已限幅且 MVP 用不到,記錄為偏離)。
51
+ - 行為:`store.resolveTargetKey` → `getEvents(key, limit ?? 50)`。
52
+
53
+ ### 3.3 `watchdog_detect_stuck`
54
+
55
+ - Input:`{ targetId: string }`。
56
+ - 行為:resolve → `detectStuck(events, effectiveConfig, Date.now())`。
57
+ - Output:requirement §9.3 + `suggestedRescueMessage`(= store rescueMessage override ?? config.rescueMessage)。
58
+
59
+ ### 3.4 `watchdog_alert_main`
60
+
61
+ - Input:`{ targetId: string; message: string; severity?: 'info'|'warning'|'critical' }`。
62
+ - 行為:
63
+ 1. `store.isPaused()` → 回 `alert suppressed (watchdog paused)`。
64
+ 2. `detectStuck` 當前分析 → `shouldAlert(store.getLastAlert(targetId), analysis, now, cooldownSec)`;false → 回 `alert suppressed (cooldown)`。(likelyStuck=false 時仍允許發送 — watchdog agent 可能有 deterministic 規則外的觀察;此時跳過 cooldown 去重、不 recordAlert,記錄為設計決策)
65
+ 3. `store.alert(formatAlert(...))` → 父 sink `pi.sendMessage({ customType: 'watchdog-alert', content, display: true }, { deliverAs: 'nextTurn', triggerTurn: severity === 'critical' })`(實際 options 以 dist 型別為準)。
66
+ 4. likelyStuck 時 `store.recordAlert(targetId, evidenceKey, now)`。
67
+ - `formatAlert`:requirement §8.2 樣板(Target/Status/Confidence/Evidence/Last command/Suggested rescue)。
68
+
69
+ ### 3.5 `watchdog_steer_subagent`
70
+
71
+ - Input:`{ targetId: string; message?: string; dryRun?: boolean }`(default dryRun=**true**;message default = effective rescueMessage)。
72
+ - 行為:`alertMode === 'main_only'` 且 `dryRun !== false` 維持 dry-run;dry-run 回傳「would steer <id> with: <message>」;實際執行走 integration 的 `steer(agentId, message)`(targetId 若為 sessionId 需反查 agentId — store 增 `resolveAgentId`)。
73
+ - `alertMode === 'main_only'` 時即使 `dryRun: false` 也拒絕實際 steer(回覆要求先改 config)— requirement §15「Do not send direct rescue message unless alertMode allows it」。
74
+
75
+ ### 3.6 `watchdog_config`
76
+
77
+ - Input:`{ action: 'get' | 'set'; config?: Partial<WatchdogConfig> }`。
78
+ - get → effective config(base + store override)JSON;set → `store.setConfigOverride(merge)`(只允許 `WatchdogConfig` 已知欄位,沿用 `loadConfigFile` 的白名單邏輯抽出共用)。
79
+
80
+ ---
81
+
82
+ ## 4. 實作步驟(TDD)
83
+
84
+ 1. **store 擴充**(紅→綠):paused/rescue/configOverride/alertSink/alert/resolveAgentId roundtrip 測試;`state.ts` 刪除、commands 改接 store、`test/state.test.ts` 案例併入 store 測試(行為驗收不變:pause/resume 切換、rescue override 優先序)。
85
+ 2. **integration 擴充**(紅→綠):adapter 增 `steer(agentId, message)`(fake service 測試)。
86
+ 3. **`tools.ts`**(紅→綠):每個 tool 的 `execute` 抽成可測純邏輯(`executeListTargets(deps, input)` 形式,deps 注入 store/registry/integration/config/now),unit tests 覆蓋:
87
+ - list_targets 的 includeCompleted 過濾。
88
+ - read_events 未知 id → 錯誤訊息。
89
+ - detect_stuck 帶 suggestedRescueMessage。
90
+ - alert_main:paused 抑制、cooldown 抑制、成功路徑呼叫 sink 並 recordAlert、sink 缺席錯誤。
91
+ - steer:dry-run 預設、main_only 拒絕實際 steer、`alertMode: 'both'` + dryRun:false 實際呼叫 steer。
92
+ - config get/set 白名單。
93
+ - `formatAlert` 樣板快照測試。
94
+ 4. **註冊接線**:`index.ts` 呼叫 `registerWatchdogTools(pi, deps)`;父 instance 註冊 alert sink。`pi.registerTool` 簽名以 dist 型別為準。
95
+ 5. **驗證**:type-check + 全部測試綠(既有 74 不回歸,state 測試轉移後總數不減);`pi -e` smoke。
96
+
97
+ ---
98
+
99
+ ## 5. 驗收標準
100
+
101
+ - [ ] watchdog 子 session 可呼叫 `watchdog_detect_stuck` 取得 evidence + suggestedRescueMessage(unit test 以注入 deps 驗證;手動 E2E 留 Task06)。
102
+ - [ ] `watchdog_alert_main` 遵守 paused 與 cooldown;alert 文字符合 §8.2 樣板。
103
+ - [ ] `watchdog_steer_subagent` 預設 dry-run;`main_only` 模式拒絕實際 steer。
104
+ - [ ] type-check + 全部測試通過。
105
+
106
+ ## 6. Revision History
107
+
108
+ | Version | Change |
109
+ |---|---|
110
+ | v1 | 初版:tools 跨 session 設計(alert sink / store 化 runtime 狀態)、6 tools 定義、TDD 步驟 |
111
+
112
+ ## 7. Reference
113
+
114
+ - reference01: `.tmp/2026-07-08_pi_watchdog_high_level_spec.md` — Task05 定義
115
+ - reference02: `.tmp/2026-07-08_pi_watchdog_extension_requirement.md` — §8.2, §9, §15
116
+ - reference03: https://pi.dev/docs/latest/extensions — registerTool / sendMessage(accessed 2026-07-08)
@@ -0,0 +1,69 @@
1
+ # Task06 — Supervisor 工作流與文件:Low-Level Detail Spec
2
+
3
+ - Date: 2026-07-08
4
+ - Source: `.tmp/2026-07-08_pi_watchdog_high_level_spec.md`(Task06);requirement §8, §13, §14, §20, §21, §23
5
+ - Status: v1 — 使用者已指示直接實作
6
+ - 前置:Task01–05 已完成並 commit(`1069bf4`)
7
+ - 性質:以文件與 prompt 資產為主,程式變更僅 package.json manifest
8
+
9
+ ---
10
+
11
+ ## 1. 範圍
12
+
13
+ ### In scope
14
+
15
+ | 產出 | 內容依據 |
16
+ |---|---|
17
+ | `prompts/watchdog-agent.md` | requirement §8.1 watchdog prompt + §8.2 alert 格式;工具名對齊 Task05 實作 |
18
+ | `skills/watchdog-supervisor/SKILL.md` | requirement §21 六步驟;格式以 pi 文件 / 現有套件的 SKILL.md 慣例為準(實作時查證,回報格式出處) |
19
+ | `examples/AGENTS.md` | requirement §20 main agent 指引 + Option A 輪詢說明(Option B 標註 post-MVP) |
20
+ | `examples/watchdog-supervisor.json` | requirement §11 範例 project config |
21
+ | `README.md` | 架構圖(三層分工 + 資料流)、安裝(`pi install` / `pi -e`)、指令表、tools 表、config 說明、手動 E2E 場景(§2) |
22
+ | `package.json` | pi manifest 增 `skills`;補 `description`、`keywords: ["pi-package"]` |
23
+
24
+ ### Out of scope
25
+
26
+ - Option B(extension-driven tick)實作;pi-intercom 整合;npm 發布設定。
27
+
28
+ ---
29
+
30
+ ## 2. 手動 E2E 場景(README 收錄,依 requirement §23)
31
+
32
+ ```txt
33
+ 前置:已安裝 @gotgenes/pi-subagents;本 extension 以 pi -e ./src/index.ts 載入;有 API key。
34
+
35
+ 1. main session 啟動 watchdog sub-agent:
36
+ spawn 一個 subagent,prompt = prompts/watchdog-agent.md 內容 + 「supervise the task agents」
37
+ 2. main session 啟動 dummy loop task sub-agent:
38
+ prompt = 「Run `rg "SOME_TOKEN" src` three times, do not change the query or path,
39
+ then run it two more times.」
40
+ 3. 等 task agent 跑完幾次 rg 後,請 watchdog agent 檢查:
41
+ 對 watchdog steer/prompt:「Check task sub-agent status now.」
42
+ 4. 預期:
43
+ - watchdog 呼叫 watchdog_list_targets → 看到 task agent likelyStuck=true
44
+ - watchdog 呼叫 watchdog_detect_stuck → evidence 含 repeated_command/repeated_output
45
+ - watchdog 呼叫 watchdog_alert_main → main session 出現 [Watchdog Alert]
46
+ 5. main agent 決策:可要求 watchdog 執行 watchdog_steer_subagent(預設 dry-run);
47
+ 實際 steer 需先 watchdog_config set alertMode=both。
48
+ ```
49
+
50
+ ## 3. 內容要求
51
+
52
+ - prompt 與 skill 中的工具名、指令名必須與實作完全一致(`watchdog_list_targets` 等 6 個、`/watchdog` 6 個子指令)。
53
+ - README 架構圖用 ASCII(高階 spec §2.2 資料流圖可簡化沿用)。
54
+ - skill 步驟需含「ignore yourself and completed agents」「do not steer unless explicitly allowed」(requirement §21)。
55
+ - 文件用英文撰寫(開源 Pi package 慣例);rescueMessage 預設值保留中文原文並附英文說明。
56
+ - README 註明:alert 的 cooldown / pause 行為、alertMode 安全預設、Option B 為 post-MVP。
57
+
58
+ ## 4. 驗收標準
59
+
60
+ - [ ] 上表 6 項產出齊備;工具/指令名稱與程式碼一致(grep 對照)。
61
+ - [ ] `npm run type-check` + `npm test` 全綠(不應有程式邏輯變更)。
62
+ - [ ] `pi -e ./src/index.ts` smoke test 載入無錯誤(manifest 變更後)。
63
+ - [ ] SKILL.md 格式符合 pi 慣例(查證來源記錄於回報)。
64
+
65
+ ## 5. Reference
66
+
67
+ - reference01: `.tmp/2026-07-08_pi_watchdog_extension_requirement.md` §8, §13, §14, §20, §21, §23
68
+ - reference02: `.tmp/2026-07-08_pi_watchdog_high_level_spec.md` — Task06 定義
69
+ - reference03: https://pi.dev/docs/latest/packages — skills 慣例(accessed 2026-07-08)