@gavin-hu/my-pi-agent 0.20.0 → 0.22.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 (298) hide show
  1. package/README.md +46 -30
  2. package/README.zh-CN.md +40 -25
  3. package/assets/screens/board.png +0 -0
  4. package/assets/screens/chrome.png +0 -0
  5. package/assets/screens/file-browse.png +0 -0
  6. package/assets/screens/plan-review.png +0 -0
  7. package/assets/screens/plan.png +0 -0
  8. package/docs/extensions/README.md +22 -23
  9. package/docs/extensions/board.md +397 -168
  10. package/docs/extensions/bytefly.md +234 -197
  11. package/docs/extensions/check.md +15 -17
  12. package/docs/extensions/context.md +35 -15
  13. package/docs/extensions/file-browse.md +57 -63
  14. package/docs/extensions/file-open.md +9 -11
  15. package/docs/extensions/file-read.md +12 -14
  16. package/docs/extensions/goal.md +15 -17
  17. package/docs/extensions/job.md +54 -42
  18. package/docs/extensions/memory.md +17 -19
  19. package/docs/extensions/plan.md +197 -140
  20. package/docs/extensions/question.md +16 -18
  21. package/docs/extensions/rewind.md +17 -19
  22. package/docs/extensions/serve.md +145 -0
  23. package/docs/extensions/session-bus.md +38 -16
  24. package/docs/extensions/subagent.md +23 -25
  25. package/docs/extensions/team.md +83 -44
  26. package/docs/extensions/todo.md +18 -18
  27. package/docs/extensions/watch.md +31 -31
  28. package/docs/extensions/web-access.md +2 -4
  29. package/docs/extensions/wechat.md +34 -35
  30. package/docs/extensions/worktree.md +56 -50
  31. package/extensions/board/commands.ts +79 -25
  32. package/extensions/board/format.ts +43 -24
  33. package/extensions/board/index.ts +17 -4
  34. package/extensions/board/input.ts +4 -9
  35. package/extensions/board/join.ts +5 -20
  36. package/extensions/board/lazy-page.ts +40 -0
  37. package/extensions/board/page.ts +65 -93
  38. package/extensions/board/result.ts +30 -39
  39. package/extensions/board/rows.ts +62 -25
  40. package/extensions/board/schema.ts +49 -11
  41. package/extensions/board/snapshot.ts +8 -6
  42. package/extensions/board/tone.ts +22 -0
  43. package/extensions/board/tools.ts +154 -55
  44. package/extensions/board/transport/router.ts +3 -5
  45. package/extensions/board/transport/server.ts +59 -28
  46. package/extensions/board/types.ts +36 -42
  47. package/extensions/board/view/card.ts +229 -0
  48. package/extensions/board/view/client.ts +35 -16
  49. package/extensions/board/view/dialog.ts +169 -6
  50. package/extensions/board/view/empty.ts +47 -0
  51. package/extensions/board/view/keys.ts +13 -2
  52. package/extensions/board/view/live.ts +90 -28
  53. package/extensions/board/view/names.ts +13 -4
  54. package/extensions/board/view/page.ts +38 -13
  55. package/extensions/board/view/render.ts +219 -277
  56. package/extensions/board/view/styles.ts +130 -32
  57. package/extensions/board/view/team.ts +297 -76
  58. package/extensions/bytefly/block/fallback.ts +1 -1
  59. package/extensions/bytefly/doctor/runtime.ts +5 -2
  60. package/extensions/bytefly/footer/collect/label.ts +60 -0
  61. package/extensions/bytefly/footer/collect/snapshot.ts +8 -1
  62. package/extensions/bytefly/footer/config.ts +7 -8
  63. package/extensions/bytefly/footer/register.ts +33 -4
  64. package/extensions/bytefly/footer/render/footer.ts +18 -4
  65. package/extensions/bytefly/footer/render/key.ts +46 -0
  66. package/extensions/bytefly/footer/render/layout.ts +13 -3
  67. package/extensions/bytefly/footer/render/lines.ts +10 -7
  68. package/extensions/bytefly/footer/render/segments.ts +41 -38
  69. package/extensions/bytefly/footer/render/status.ts +0 -11
  70. package/extensions/bytefly/footer/types.ts +7 -0
  71. package/extensions/bytefly/header/component.ts +4 -20
  72. package/extensions/bytefly/header/config.ts +1 -1
  73. package/extensions/bytefly/index.ts +32 -6
  74. package/extensions/bytefly/mode/editor.ts +115 -0
  75. package/extensions/bytefly/panel/commands.ts +210 -120
  76. package/extensions/bytefly/panel/format.ts +4 -4
  77. package/extensions/bytefly/panel/settings.ts +73 -6
  78. package/extensions/bytefly/panel/tui/keys.ts +2 -6
  79. package/extensions/bytefly/update/cache.ts +1 -1
  80. package/extensions/bytefly/update/register.ts +4 -4
  81. package/extensions/bytefly/working/config.ts +5 -7
  82. package/extensions/bytefly/working/format.ts +10 -13
  83. package/extensions/bytefly/working/register.ts +22 -27
  84. package/extensions/check/commands.ts +3 -2
  85. package/extensions/check/render.ts +10 -1
  86. package/extensions/check/{tui.ts → view/check-screens.ts} +14 -19
  87. package/extensions/context/commands.ts +2 -2
  88. package/extensions/context/{tui.ts → view/context-report.ts} +51 -19
  89. package/extensions/file-browse/config/config.ts +4 -19
  90. package/extensions/file-browse/index.ts +89 -79
  91. package/extensions/file-browse/live/live.ts +1 -1
  92. package/extensions/file-browse/transport/router.ts +12 -4
  93. package/extensions/file-browse/transport/server.ts +61 -28
  94. package/extensions/file-browse/view/chrome.ts +4 -4
  95. package/extensions/file-browse/view/client.ts +3 -1
  96. package/extensions/file-browse/view/html.ts +73 -40
  97. package/extensions/file-browse/view/live-client.ts +2 -2
  98. package/extensions/file-browse/view/markdown.ts +13 -14
  99. package/extensions/file-browse/view/palette.ts +2 -2
  100. package/extensions/file-browse/view/raw-html.ts +9 -9
  101. package/extensions/file-browse/view/tree.ts +1 -1
  102. package/extensions/file-browse/view/urls.ts +20 -6
  103. package/extensions/file-read/render.ts +4 -4
  104. package/extensions/goal/plan-route.ts +2 -2
  105. package/extensions/goal/tui.ts +12 -2
  106. package/extensions/job/child.ts +527 -0
  107. package/extensions/job/clock.ts +142 -0
  108. package/extensions/job/commands.ts +4 -2
  109. package/extensions/job/config.ts +8 -1
  110. package/extensions/job/format.ts +22 -1
  111. package/extensions/job/log-writer.ts +13 -2
  112. package/extensions/job/registry.ts +63 -7
  113. package/extensions/job/render.ts +34 -0
  114. package/extensions/job/runtime.ts +296 -655
  115. package/extensions/job/session.ts +132 -19
  116. package/extensions/job/store.ts +7 -7
  117. package/extensions/job/tools.ts +21 -9
  118. package/extensions/job/types.ts +47 -0
  119. package/extensions/job/{tui.ts → view/jobs-screen.ts} +19 -99
  120. package/extensions/job/waiters.ts +44 -1
  121. package/extensions/memory/commands.ts +4 -2
  122. package/extensions/memory/{tui.ts → view/memory-picker.ts} +12 -12
  123. package/extensions/plan/commands.ts +12 -9
  124. package/extensions/plan/feedback.ts +62 -16
  125. package/extensions/plan/format.ts +8 -2
  126. package/extensions/plan/git-bash.ts +115 -16
  127. package/extensions/plan/index.ts +58 -51
  128. package/extensions/plan/plan-index.ts +4 -2
  129. package/extensions/plan/plan-text.ts +114 -0
  130. package/extensions/plan/plans.ts +5 -54
  131. package/extensions/plan/policy.ts +8 -10
  132. package/extensions/plan/review-lazy.ts +85 -0
  133. package/extensions/plan/review.ts +2 -2
  134. package/extensions/plan/runtime.ts +61 -41
  135. package/extensions/plan/sections.ts +1 -1
  136. package/extensions/plan/steps.ts +3 -3
  137. package/extensions/plan/todos.ts +1 -1
  138. package/extensions/plan/tools/cancel.ts +27 -62
  139. package/extensions/plan/tools/enter.ts +55 -81
  140. package/extensions/plan/tools/exit.ts +221 -238
  141. package/extensions/plan/tools/refusal.ts +18 -0
  142. package/extensions/plan/tools/steps.ts +75 -92
  143. package/extensions/plan/tools/write.ts +77 -120
  144. package/extensions/plan/tools.ts +188 -30
  145. package/extensions/plan/tui.ts +5 -5
  146. package/extensions/plan/types.ts +48 -5
  147. package/extensions/plan/web-blocks.ts +76 -5
  148. package/extensions/plan/web-chrome.ts +4 -0
  149. package/extensions/plan/web-client.ts +102 -2
  150. package/extensions/plan/web-collab.ts +4 -4
  151. package/extensions/plan/web-comments.ts +39 -8
  152. package/extensions/plan/web-composer.ts +14 -3
  153. package/extensions/plan/web-keys.ts +149 -25
  154. package/extensions/plan/web-lazy.ts +53 -0
  155. package/extensions/plan/web-markdown.ts +10 -68
  156. package/extensions/plan/web-outline.ts +22 -2
  157. package/extensions/plan/web-page.ts +56 -86
  158. package/extensions/plan/web-payload.ts +188 -0
  159. package/extensions/plan/web-plan.ts +23 -13
  160. package/extensions/plan/web-router.ts +18 -9
  161. package/extensions/plan/web-settle.ts +14 -7
  162. package/extensions/plan/web-share-session.ts +24 -55
  163. package/extensions/plan/web-share.ts +19 -37
  164. package/extensions/plan/web-write.ts +112 -0
  165. package/extensions/plan/web.ts +67 -131
  166. package/extensions/question/tools.ts +6 -2
  167. package/extensions/question/{tui.ts → view/question-screen.ts} +11 -11
  168. package/extensions/rewind/commands/commands.ts +6 -3
  169. package/extensions/rewind/state/runtime.ts +1 -1
  170. package/extensions/rewind/{view/status.ts → status.ts} +4 -4
  171. package/extensions/rewind/view/review-tui.ts +4 -4
  172. package/extensions/serve/config.ts +39 -0
  173. package/extensions/serve/index.ts +85 -0
  174. package/extensions/serve/registry.ts +260 -0
  175. package/extensions/session-bus/commands.ts +11 -6
  176. package/extensions/session-bus/join-request.ts +17 -9
  177. package/extensions/session-bus/mail-watch.ts +26 -17
  178. package/extensions/session-bus/protocol.ts +15 -3
  179. package/extensions/session-bus/resolve.ts +5 -7
  180. package/extensions/session-bus/result.ts +1 -27
  181. package/extensions/session-bus/runtime.ts +40 -12
  182. package/extensions/session-bus/store.ts +101 -19
  183. package/extensions/session-bus/tools.ts +5 -4
  184. package/extensions/session-bus/types.ts +19 -0
  185. package/extensions/session-bus/{tui.ts → view/session-list.ts} +16 -16
  186. package/extensions/subagent/agents.ts +1 -1
  187. package/extensions/subagent/commands.ts +4 -2
  188. package/extensions/subagent/format.ts +15 -15
  189. package/extensions/subagent/index.ts +1 -1
  190. package/extensions/subagent/render.ts +3 -3
  191. package/extensions/subagent/{background-tui.ts → view/agents-board.ts} +11 -11
  192. package/extensions/team/commands.ts +24 -5
  193. package/extensions/team/dispatch.ts +18 -4
  194. package/extensions/team/format.ts +17 -2
  195. package/extensions/team/index.ts +9 -8
  196. package/extensions/team/schema.ts +6 -5
  197. package/extensions/team/tools.ts +107 -45
  198. package/extensions/team/types.ts +3 -2
  199. package/extensions/todo/commands.ts +4 -2
  200. package/extensions/todo/format.ts +11 -0
  201. package/extensions/todo/render.ts +57 -0
  202. package/extensions/todo/runtime.ts +1 -1
  203. package/extensions/todo/tools.ts +1 -1
  204. package/extensions/todo/{tui.ts → view/todo-screen.ts} +18 -90
  205. package/extensions/todo/widget.ts +47 -0
  206. package/extensions/watch/commands.ts +4 -2
  207. package/extensions/watch/format.ts +36 -7
  208. package/extensions/watch/render.ts +57 -0
  209. package/extensions/watch/runtime.ts +27 -22
  210. package/extensions/watch/tools.ts +4 -4
  211. package/extensions/watch/{tui.ts → view/watch-screen.ts} +18 -80
  212. package/extensions/web-access/fetch/tool.ts +12 -4
  213. package/extensions/wechat/commands/commands.ts +18 -14
  214. package/extensions/wechat/index.ts +72 -37
  215. package/extensions/wechat/state/credentials.ts +1 -1
  216. package/extensions/wechat/state/state.ts +1 -1
  217. package/extensions/wechat/text/status.ts +112 -0
  218. package/extensions/wechat/types.ts +2 -2
  219. package/extensions/wechat/view/status-screen.ts +4 -103
  220. package/extensions/worktree/commands.ts +11 -9
  221. package/extensions/worktree/enter.ts +2 -2
  222. package/extensions/worktree/exit.ts +2 -2
  223. package/extensions/worktree/index.ts +5 -5
  224. package/extensions/worktree/screen.ts +3 -3
  225. package/extensions/worktree/status.ts +2 -2
  226. package/extensions/worktree/tools.ts +113 -116
  227. package/lib/README.md +28 -13
  228. package/lib/board/actions.ts +111 -0
  229. package/lib/board/artifact.ts +149 -0
  230. package/lib/board/card.ts +163 -52
  231. package/lib/board/config.ts +58 -0
  232. package/lib/board/contract.ts +113 -0
  233. package/lib/board/join.ts +38 -0
  234. package/lib/board/lanes.ts +182 -0
  235. package/lib/board/lead.ts +130 -0
  236. package/lib/board/limits.ts +78 -0
  237. package/lib/board/links.ts +31 -10
  238. package/lib/board/page-pointer.ts +30 -5
  239. package/lib/board/read.ts +83 -0
  240. package/lib/board/reason.ts +102 -0
  241. package/lib/board/round-text.ts +68 -6
  242. package/lib/board/round.ts +31 -15
  243. package/lib/board/rules.ts +151 -307
  244. package/lib/board/seat.ts +35 -0
  245. package/lib/board/session.ts +199 -51
  246. package/lib/board/skipped.ts +22 -0
  247. package/lib/board/steps.ts +487 -0
  248. package/lib/board/store.ts +119 -36
  249. package/lib/board/tone.ts +92 -0
  250. package/lib/board/view.ts +81 -63
  251. package/lib/bus-request.ts +83 -0
  252. package/{extensions/watch → lib}/child.ts +40 -21
  253. package/lib/env.ts +65 -7
  254. package/lib/format.ts +13 -0
  255. package/lib/http/index.ts +17 -11
  256. package/lib/http/mount.ts +142 -0
  257. package/lib/http/port.ts +30 -0
  258. package/lib/http/serve.ts +170 -46
  259. package/lib/http/server.ts +3 -3
  260. package/lib/lazy.ts +45 -0
  261. package/lib/plan-entry.ts +46 -21
  262. package/lib/plans/paths.ts +82 -0
  263. package/lib/policy.ts +9 -6
  264. package/lib/report.ts +3 -2
  265. package/lib/requirements.ts +10 -10
  266. package/lib/session-label.ts +129 -7
  267. package/lib/session-roster.ts +43 -1
  268. package/lib/session-tool.ts +39 -22
  269. package/lib/stray-fields.ts +7 -2
  270. package/lib/tool-names.ts +5 -10
  271. package/lib/tui/README.md +29 -11
  272. package/lib/tui/chrome.ts +16 -15
  273. package/lib/tui/cursor.ts +18 -3
  274. package/lib/tui/glyphs.ts +11 -11
  275. package/lib/tui/render-cache.ts +29 -0
  276. package/lib/tui/tokens.ts +33 -2
  277. package/lib/tui/tool.ts +33 -4
  278. package/lib/web/README.md +21 -0
  279. package/lib/web/client-runtime.ts +27 -1
  280. package/lib/web/components.ts +5 -1
  281. package/lib/web/index.ts +1 -0
  282. package/lib/web/markdown.ts +83 -0
  283. package/lib/web/markup.ts +20 -2
  284. package/lib/web/runtime.ts +9 -10
  285. package/package.json +8 -2
  286. package/skills/init/SKILL.md +5 -5
  287. package/skills/next/SKILL.md +3 -5
  288. package/extensions/board/config.ts +0 -36
  289. package/extensions/board/seat.ts +0 -28
  290. package/lib/tui/index.ts +0 -34
  291. package/skills/project-docs/SKILL.md +0 -133
  292. package/skills/project-docs/references/audit.md +0 -131
  293. package/skills/project-docs/references/changelog.md +0 -105
  294. package/skills/project-docs/references/discovery.md +0 -127
  295. package/skills/project-docs/references/readme.md +0 -80
  296. package/skills/project-docs/references/release.md +0 -84
  297. package/skills/project-docs/references/roadmap.md +0 -93
  298. package/skills/project-docs/scripts/state.ts +0 -316
@@ -8,22 +8,20 @@ Modelled on Claude Code's `AskUserQuestion`.
8
8
 
9
9
  ## Capabilities
10
10
 
11
- Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
12
-
13
- | Capability | How | What you get | Status |
14
- |---|---|---|---|
15
- | Mid-turn asking | `ask_user_question` | The model asks 1–4 questions and the turn waits until you answer or cancel. | Shipped |
16
- | Labelled options | `questions[].options` | Each question offers up to four choices, each with an optional second line describing it. | Shipped |
17
- | Free-form answers | the automatic `Other (type something)` row, `questions[].options` omitted | You can type an answer the options do not cover, and a question with no options asks for plain text. | Shipped |
18
- | Multiple selections | `questions[].multiSelect` | `Space` toggles options and `Enter` confirms the set. | Shipped |
19
- | Batched questions | `questions[]` | Up to four questions ride in one call and are answered in one pass. | Shipped |
20
- | Submit gating | the `Submit` tab | Submit refuses until every question is answered and names the missing ones; a single question submits on `Enter` without a Submit tab. | Shipped |
21
- | Tab headers | `questions[].header` | Each question gets a short tab label, defaulted to `Q{n}` and clipped to 12 columns. | Shipped |
22
- | Language-aware batch | `dialogUI.language` | A remote turn in Chinese gets the batch prompt, retry text, and format line in Chinese. | Shipped |
23
- | Dialog fallback | `select`, `input`, `editor` | RPC and remote turns answer the same questions without the TUI screen. | Shipped |
24
- | No-UI degradation | `print`, `json` | The tool returns `unavailable` with an error instead of hanging when no dialog channel exists. | Shipped |
25
- | Cancelling | `Esc` | The model is told you cancelled and can continue or ask in prose. | Shipped |
26
- | Branch-scoped answers | tool-result `details` | The answers stay with the transcript, so `/resume` and `/tree` show the same history. | Shipped |
11
+ | Capability | How | What you get |
12
+ |---|---|---|
13
+ | Mid-turn asking | `ask_user_question` | The model asks 1–4 questions and the turn waits until you answer or cancel. |
14
+ | Labelled options | `questions[].options` | Each question offers up to four choices, each with an optional second line describing it. |
15
+ | Free-form answers | the automatic `Other (type something)` row, `questions[].options` omitted | You can type an answer the options do not cover, and a question with no options asks for plain text. |
16
+ | Multiple selections | `questions[].multiSelect` | `Space` toggles options and `Enter` confirms the set. |
17
+ | Batched questions | `questions[]` | Up to four questions ride in one call and are answered in one pass. |
18
+ | Submit gating | the `Submit` tab | Submit refuses until every question is answered and names the missing ones; a single question submits on `Enter` without a Submit tab. |
19
+ | Tab headers | `questions[].header` | Each question gets a short tab label, defaulted to `Q{n}` and clipped to 12 columns. |
20
+ | Language-aware batch | `dialogUI.language` | A remote turn in Chinese gets the batch prompt, retry text, and format line in Chinese. |
21
+ | Dialog fallback | `select`, `input`, `editor` | RPC and remote turns answer the same questions without the TUI screen. |
22
+ | No-UI degradation | `print`, `json` | The tool returns `unavailable` with an error instead of hanging when no dialog channel exists. |
23
+ | Cancelling | `Esc` | The model is told you cancelled and can continue or ask in prose. |
24
+ | Branch-scoped answers | tool-result `details` | The answers stay with the transcript, so `/resume` and `/tree` show the same history. |
27
25
 
28
26
  ## Quickstart
29
27
 
@@ -183,7 +181,7 @@ UI.
183
181
  for free.
184
182
  - **A pure state machine behind the TUI.** `tui-state.ts` holds a reducer
185
183
  (`reduce(state, action) → { state, effect }`) so navigation, toggling, tab
186
- movement, and submit gating are tested without a terminal; `tui.ts` only maps
184
+ movement, and submit gating are tested without a terminal; `view/question-screen.ts` only maps
187
185
  state to lines and forwards input. Render output is cached and cleared from
188
186
  `invalidate()`, and themed strings are never stored across renders.
189
187
  - **Claude-Code-compatible parameter shape.** `{ label, description? }` options
@@ -212,7 +210,7 @@ UI.
212
210
  | [`types.ts`](../../extensions/question/types.ts) | Shared raw/normalized types |
213
211
  | [`answers.ts`](../../extensions/question/answers.ts) | Answer normalization and model-facing formatting (pure) |
214
212
  | [`tui-state.ts`](../../extensions/question/tui-state.ts) | Pure keyboard/navigation state machine |
215
- | [`tui.ts`](../../extensions/question/tui.ts) | Custom terminal component (render + input), drawn from the `lib/tui/` kit (`titleRule`/`plainRule`, `checkbox`, `flowCells`, `screenHint`, `editorTheme`). |
213
+ | [`view/question-screen.ts`](../../extensions/question/view/question-screen.ts) | Custom terminal component (render + input), loaded on first use, drawn from the `lib/tui/` kit (`titleRule`/`plainRule`, `checkbox`, `flowCells`, `screenHint`, `editorTheme`). |
216
214
  | [`dialogs.ts`](../../extensions/question/dialogs.ts) | `select`/`input`/`editor` fallback driver |
217
215
  | [`tools.ts`](../../extensions/question/tools.ts) | Tool registration and transcript renderers |
218
216
 
@@ -12,24 +12,22 @@ file, a selection, or the whole turn.
12
12
 
13
13
  ## Capabilities
14
14
 
15
- Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
16
-
17
- | Capability | How | What you get | Status |
18
- |---|---|---|---|
19
- | Automatic snapshots | the `autoSnapshots` key | One snapshot per prompt, taken before its first mutating call, so every task has a restore point. | Shipped |
20
- | Durable snapshots | `refNamespace` | Snapshots survive the session and are listed from git rather than from session state. | Shipped |
21
- | Prompt timeline | `/rewind` | Every user prompt on the active branch, newest first, marked `⬥` where a code snapshot was captured. | Shipped |
22
- | Scoped restore | `/rewind` | Code, conversation, or both, from the prompt you pick. | Shipped |
23
- | Undoable restore | the `safetySnapshot` key | A code restore snapshots the state it replaces first, so it can itself be undone. | Shipped |
24
- | Partial restore | `/rewind` | The changed paths grouped by directory, chosen with a checklist, and previewed as the subset you selected. | Shipped |
25
- | Latest-turn review | `/review` | The changed files of the latest prompt, with `M`/`A`/`D`/`R` marks and line counts. | Shipped |
26
- | File, batch, and hunk revert | `/review` | Revert one file, a selection, the whole turn, or chosen hunks, each with its own safety snapshot. | Shipped |
27
- | Busy-git refusal | `/rewind`, `/review` | A merge, rebase, cherry-pick, revert, or bisect in progress refuses the restore instead of fighting it. | Shipped |
28
- | Root-scoped snapshots | `refs/pi/rewind` | Worktrees share the ref store, while another root's snapshot is filtered out and refused. | Shipped |
29
- | Untracked capture | `autoSnapshots` | Non-ignored untracked files are captured, and a restore removes the ones that appeared after the snapshot. | Shipped |
30
- | Ref cap and auto-prune | `max`, `autoPrune` | The snapshot store is bounded and pruned instead of growing without limit. | Shipped |
31
- | Review plan guard | `/review` in plan mode | Review refuses while plan mode is on, matching the read-only planning rule. | Shipped |
32
- | Prompt-count chip | `↺ N`, the `showStatus` key | The status bar counts the prompts on the active branch, whether or not each has a snapshot. | Shipped |
15
+ | Capability | How | What you get |
16
+ |---|---|---|
17
+ | Automatic snapshots | the `autoSnapshots` key | One snapshot per prompt, taken before its first mutating call, so every task has a restore point. |
18
+ | Durable snapshots | `refNamespace` | Snapshots survive the session and are listed from git rather than from session state. |
19
+ | Prompt timeline | `/rewind` | Every user prompt on the active branch, newest first, marked `⬥` where a code snapshot was captured. |
20
+ | Scoped restore | `/rewind` | Code, conversation, or both, from the prompt you pick. |
21
+ | Undoable restore | the `safetySnapshot` key | A code restore snapshots the state it replaces first, so it can itself be undone. |
22
+ | Partial restore | `/rewind` | The changed paths grouped by directory, chosen with a checklist, and previewed as the subset you selected. |
23
+ | Latest-turn review | `/review` | The changed files of the latest prompt, with `M`/`A`/`D`/`R` marks and line counts. |
24
+ | File, batch, and hunk revert | `/review` | Revert one file, a selection, the whole turn, or chosen hunks, each with its own safety snapshot. |
25
+ | Busy-git refusal | `/rewind`, `/review` | A merge, rebase, cherry-pick, revert, or bisect in progress refuses the restore instead of fighting it. |
26
+ | Root-scoped snapshots | `refs/pi/rewind` | Worktrees share the ref store, while another root's snapshot is filtered out and refused. |
27
+ | Untracked capture | `autoSnapshots` | Non-ignored untracked files are captured, and a restore removes the ones that appeared after the snapshot. |
28
+ | Ref cap and auto-prune | `max`, `autoPrune` | The snapshot store is bounded and pruned instead of growing without limit. |
29
+ | Review plan guard | `/review` in plan mode | Review refuses while plan mode is on, matching the read-only planning rule. |
30
+ | Prompt-count chip | `↺ N`, the `showStatus` key | The status bar counts the prompts on the active branch, whether or not each has a snapshot. |
33
31
 
34
32
  ## Quickstart
35
33
 
@@ -278,7 +276,7 @@ commit. These refs are not branches and are not touched by `git push --all`.
278
276
  | `transport/store.ts` | Versioned metadata encoding and ref-backed listing/pruning. |
279
277
  | `state/policy.ts` | Which tool calls are worth a snapshot. |
280
278
  | `transport/temp-index.ts` | Per-process `GIT_INDEX_FILE` paths: create, release, and sweep stale files. |
281
- | `view/status.ts` | The `↺ N` status chip. |
279
+ | `status.ts` | The `↺ N` status chip. |
282
280
  | `transport/queue.ts` | The serialized git operation queue for staging calls (`GIT_INDEX_FILE`). |
283
281
  | `state/runtime.ts` | Root/config caches and the snapshot/list/plan/patch/numstat/restore/hunk-revert operations. |
284
282
  | `state/timeline.ts` | Pure: prompts on the branch, paired with their snapshots. |
@@ -0,0 +1,145 @@
1
+ # serve — the session's one loopback server
2
+
3
+ ## Capabilities
4
+
5
+ | Capability | How | What you get |
6
+ |---|---|---|
7
+ | One port for every page of a session | automatic: the first page that opens binds it | the file browser at `/files/`, the board at `/board/`, and a review at `/plan/<id>/` answer on one origin, so a bookmark, a firewall rule, and a port to remember are one thing |
8
+ | One chip for that port | `◉ <port>` in the status bar | the session's port while anything is served on it, and nothing when no page is open |
9
+ | A port that survives a restart | automatic: the project's own port | a session that starts again lands where a bookmark already points, so a saved URL still opens the page it names; `serve.port` overrides it |
10
+ | Two projects on one machine | automatic: the port is the repository's, hashed into the band | neither needs a setting to avoid the other, and a worktree of one repository keeps the number the main checkout uses |
11
+ | A second session on its own port | automatic: servers are keyed by Pi session | a WeChat peer conversation never answers on this session's port |
12
+ | One page reachable on the LAN, on request | the asking page calls `mount.share()` | a teammate's link names exactly one address, and only that page answers there |
13
+
14
+ ## Quickstart
15
+
16
+ There is nothing to run: `serve` is infrastructure, and the pages that use it are
17
+ `/board`, `/browse`, and `/plan review`.
18
+
19
+ ```bash
20
+ /board # http://127.0.0.1:<port>/board/?token=…
21
+ /browse # http://127.0.0.1:<port>/files/
22
+ /plan review # http://127.0.0.1:<port>/plan/3f9c21aa/?token=…
23
+ ```
24
+
25
+ All three answer on that one port — the project's own, in `4780`–`5779` — and the
26
+ footer shows `◉ <port>` while any of them is open, dropping the chip when the last
27
+ one closes.
28
+
29
+ ## Routes
30
+
31
+ The server holds prefixes, not pages. Each one is claimed by the extension that
32
+ serves it, and a request reaches that extension with its prefix stripped:
33
+
34
+ | Prefix | Served by | Note |
35
+ |---|---|---|
36
+ | `/files/` | `file-browse` | the file browser: its tree, its file pages, its own script, and the links it rewrites out of Markdown, all built from this prefix; `/files/browse/<path>` is one directory |
37
+ | `/board/` | `board` | the read-only board page, with its token in the query |
38
+ | `/plan/<id>/` | `plan` | one review; the id is the review's, so two reviews never share a namespace |
39
+
40
+ Anything else is a `404` with a plain-text body, and a prefix named without its
41
+ trailing slash (`/board`) is redirected to the slashed form, because a page's own
42
+ links are relative to its directory. A prefix another page already holds is
43
+ refused by name rather than served twice.
44
+
45
+ ## Behaviour by mode
46
+
47
+ The server runs in every mode, but the chip needs a UI: RPC and JSON sessions serve
48
+ their pages and skip it. Nothing here reads or writes the transcript.
49
+
50
+ ## Configuration
51
+
52
+ The `serve` section of the shared `bytefly.json` (global, then project, project wins:
53
+
54
+ | Key | Default | What it does |
55
+ |---|---|---|
56
+ | `port` | `0` | `0` takes the project's own port — its repository key hashed into `4780`–`5779` — and falls back to an OS-chosen one when that port is taken; a positive value pins the session's server so a bookmarked address survives a restart, and a taken pin is reported by name instead of moved |
57
+
58
+ The default is the repository's, so two repositories take two numbers without a
59
+ setting and a restart lands where the bookmark was. A second session of one
60
+ repository — a WeChat peer, or another checkout — prefers the same number and takes
61
+ an OS-chosen port when the first one holds it.
62
+
63
+ The pin belongs to the first session in a process that binds: a second session takes
64
+ an OS-chosen port instead of failing on a port the host already holds. A second
65
+ *process* that finds the pinned port taken is told so rather than moved silently off
66
+ the address a bookmark names.
67
+
68
+ ## Security
69
+
70
+ The server binds `127.0.0.1`. The unit of exposure is the mount: a prefix answers
71
+ only what its page routes, and each page keeps its own `Host` allowlist, its own
72
+ token, and its own method list in `lib/http`.
73
+
74
+ A shared review is the one exception, and it is the reviewing page's own: it asks
75
+ for a listener on a single LAN address, and that listener carries only the prefixes
76
+ that asked for it. `file-browse` and `board` are never reachable that way, and a page
77
+ that stops sharing leaves the listener at once — the socket closes with the last page
78
+ that was still sharing it.
79
+
80
+ ## Limitations
81
+
82
+ - A page needs `serve` loaded. Without it every page refuses with a sentence naming
83
+ the extension, instead of binding a rival socket.
84
+ - Two projects can hash into the same port, and the second start takes an OS-chosen one
85
+ instead: its bookmark moves until that project sets `serve.port`.
86
+ - Two checkouts of one repository at once prefer one number, so the second takes an
87
+ OS-chosen port each session and only the first keeps the address a bookmark names.
88
+ - The default is a guess: a port another process holds moves the session to an
89
+ OS-chosen one without a word — the chip shows the number — while a pinned port that is
90
+ taken is refused by name.
91
+
92
+ ## Non-goals
93
+
94
+ - It serves no page of its own, and registers no tool and no command.
95
+ - It is not a general web server: loopback, a page's own routes, one optional LAN
96
+ address per shared page, and nothing else.
97
+ - It does not proxy or rewrite; a page's paths are its own, under its prefix.
98
+
99
+ ## Pi integration
100
+
101
+ - Registers `pi.events.on(HTTP_MOUNT_CHANNEL, …)` in the factory — registration only.
102
+ Binding happens on the first mount, never at load and never on `session_start`.
103
+ - `session_start` records the session's working directory and its context, so the
104
+ pin is read from that session's config and the chip is drawn in its UI.
105
+ - `session_shutdown` closes the session's socket, its LAN listener, and its chip.
106
+ It is idempotent.
107
+
108
+ ## Design notes
109
+
110
+ A session has one port, so the socket cannot belong to a page. The page asks for a
111
+ prefix over `pi.events` — the mechanism the repository already uses for a
112
+ cross-extension request that must be answered — and `lib/http` owns the socket, the
113
+ dispatch, the body cap per mount, and the idle grace per mount.
114
+
115
+ The handshake is *claimed*, not awaited: `server.listen` cannot answer synchronously,
116
+ so the owner responds with the pending bind and only a request nobody claimed is
117
+ refused. Without that distinction a bind that is still starting looks exactly like a
118
+ session with no server.
119
+
120
+ The default is the repository key hashed into the band rather than a fresh draw. The
121
+ key is the one identity this repository already has — `session-bus` files sessions by
122
+ it, `plan` files plans by it — and every worktree of a repository resolves the same
123
+ one, so a bookmark saved in the main checkout opens the worktree's page. A draw was
124
+ the reason a bookmark only worked until the next start.
125
+
126
+ ## Files
127
+
128
+ | Path | Responsibility |
129
+ |---|---|
130
+ | [`../../extensions/serve/index.ts`](../../extensions/serve/index.ts) | The Pi wiring: the bus listener, the per-session directory and context, the port chip, and the shutdown |
131
+ | [`../../extensions/serve/registry.ts`](../../extensions/serve/registry.ts) | The session's servers: binding on first mount, the pin's owner, the remembered port, prefix refusals, the LAN listener a shared page borrows, and closing with the last mount |
132
+ | [`../../extensions/serve/config.ts`](../../extensions/serve/config.ts) | The `serve` section: `port`, its clamp, and the two documents it resolves over |
133
+
134
+ ## Testing
135
+
136
+ `config.test.ts`, `registry.test.ts`, and `extension.test.ts`, declared under
137
+ `serve` in `test/suite-manifest.ts`.
138
+
139
+ The registry cases bind real sockets on `127.0.0.1`: one port for two pages of a
140
+ session, a second session on its own port, a refused duplicate prefix, the socket
141
+ closing with its last page and reopening where it was, the project's port taken by the
142
+ first bind and the OS-chosen fallback when another listener holds it, the pin belonging
143
+ to the first session that binds, and a LAN share reaching one page only. The extension
144
+ cases drive the shipped bus path through `requestMount`, including the project's port
145
+ surviving a restart, the chip, and the shutdown.
@@ -12,11 +12,9 @@ a running turn settles. There is no network service and no hidden context.
12
12
 
13
13
  ## Capabilities
14
14
 
15
- Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
16
-
17
15
  | Capability | How | What you get | Status |
18
16
  |---|---|---|---|
19
- | Membership | `session join [label]`, `/team join [<label>]`, `/board`, any `board` action that writes, the board's `w` | The join: this session is on the bus under a stable label peers address, and listens for mail unless told not to. A label given to `session join` or `/team join` is also Pi's session name, so the footer and `/resume` agree with the record; `/board` joins as the page's seat (`team-lead`) or, on a page it is only watching, under its own label; the first `board` action that writes joins under the name the session already has; `w` joins under the name the bus derives and writes nothing to Pi. | Shipped |
17
+ | Membership | `session join [label]`, `/team join [<label>]`, `/board`, the first `board` claim, the board's `w` | The join: this session is on the bus under a stable label peers address, and listens for mail unless told not to. That label is what the footer draws; a label given to `session join` or `/team join` is also Pi's session name, so `/resume` agrees with the record; `/board` joins as the board's lead (`board.lead`, `lead` by default) or, on a page it is only watching, under its own preset teammate name (`tom`, `lily`, … — `team-agent-<id>` once every preset name is held); the first `board` claim joins under that same name, and renames nothing; `w` joins under the name the bus derives and writes nothing to Pi. | Shipped |
20
18
  | Peer discovery | `session status` | Every active session with its unread count and claims, plus this session's identity, in one read — and a session whose process is gone says so, its paths free from that read on. | Unreleased |
21
19
  | Live board | `/sessions` | Sessions, this session's unread mail, and every claim held on the repository, on one screen. | Shipped |
22
20
  | Board actions | `/sessions` (`w`, `x`) | Start or stop listening from the board, or release an own claim behind a confirmation, without leaving it. | Shipped |
@@ -56,11 +54,11 @@ collapsed line that expands to the board's rows.
56
54
  | Action | Inputs | Result |
57
55
  |---|---|---|
58
56
  | `status` | — | Active sessions with unread counts, their state, claims, and this session's identity. A session whose process is gone is listed as `gone` until its heartbeat ages out, and its claims are not listed at all. Prunes stale records. A read: it never adds this session to the bus. |
59
- | `send` | `to`, `text`, optional `kind`, optional `paths` | Writes one message per recipient. `to: "*"` is only valid with `kind: "event"`. |
57
+ | `send` | `to`, `text`, optional `kind`, optional `paths` | Writes one message per recipient. `to: "*"` is only valid with `kind: "event"`. A session whose process is gone is not a recipient: naming it is refused, and a broadcast skips it. |
60
58
  | `inbox` | optional `limit` | Returns and deletes up to `limit` messages, oldest first (default 20, max 50). |
61
59
  | `claim` | `paths` | Claims repository-relative files or directory prefixes; returns conflicts with their owner. |
62
60
  | `release` | `paths` | Removes claims owned by this session; reports paths owned by someone else. |
63
- | `join` | optional `label`, `wake`, `listen` | Joins the bus, names this session on it and in Pi when a label is given — the same path and uniqueness rule as `/team join`, so the footer and `/resume` agree with the record peers address — and starts listening for mail. `wake: true` raises one turn per burst once the agent is idle; `wake: false` reports at the next turn; `listen: false` joins without listening. A label a live session holds is refused, naming the session that holds it — its id, since the label it holds is the one you asked for — and the refusal leaves both names and the watch alone. The result reports the label the bus settled on and whether mail will reach this session. |
61
+ | `join` | optional `label`, `wake`, `listen` | Joins the bus, names this session on it and in Pi when a label is given — the same path and uniqueness rule as `/team join`, so `/resume` agrees with the record peers address — and starts listening for mail. The footer draws the label either way, so a plain join changes the name on the bar without renaming the session. `wake: true` raises one turn per burst once the agent is idle; `wake: false` reports at the next turn; `listen: false` joins without listening. A label a live session holds is refused, naming the session that holds it — its id, since the label it holds is the one you asked for — and the refusal leaves both names and the watch alone. The result reports the label the bus settled on and whether mail will reach this session. |
64
62
 
65
63
  A claim is all-or-nothing: if one path conflicts, nothing is claimed. A claim on
66
64
  a directory conflicts with claims below it and above it, so `src/api` and
@@ -68,7 +66,10 @@ a directory conflicts with claims below it and above it, so `src/api` and
68
66
  takeover; wait for the owner, ask it to release, or let the session expire.
69
67
 
70
68
  Paths resolve against the caller's working directory and are stored relative to
71
- its repository root; a path outside the repository is refused.
69
+ its repository root; a path outside the repository is refused, and so is one that
70
+ resolves to the repository itself — `path "." is the repository, not a path in
71
+ it.` — because a claim on the root would own every file in it. Below the root,
72
+ the same `.` claims the directory the session is in.
72
73
 
73
74
  `wake` is the mode, not an arm-time switch: `join({ wake: true })` raises a turn
74
75
  for a session that was reporting at the next turn, and `join({ wake: false })` —
@@ -91,9 +92,12 @@ Labels are how another session addresses you. Name one by the outcome it owns
91
92
  (`billing-core`, `integrator`, `verify-billing`), not by a layer or a role;
92
93
  duplicate labels get a short id suffix automatically.
93
94
 
94
- A label is also Pi's session name, so the footer's `[label]` and `/resume` agree
95
- with the bus. Because the bus derives a joining session's label from that name, a
96
- named session comes back under the same label after a resume.
95
+ A label is what the footer's `[label]` draws: bytefly reads this session's own record
96
+ and follows the `session-label` event, so the bar names the session the way a card, a
97
+ handoff, and `/team` do. `/team join <label>` makes the label Pi's session name too, so
98
+ `/resume` agrees with the bus; any other join leaves Pi's name alone and only the bar
99
+ moves. Because the bus derives a joining session's label from that name, a named session
100
+ comes back under the same label after a resume.
97
101
 
98
102
  The tool's `join` action is the same join from the model's side, so a `board` or a
99
103
  `team` call can put a session on the bus without the user naming it, and
@@ -181,6 +185,11 @@ listening watch is what raises the turn.
181
185
  both sides to keep replying; the answer is to stop listening on one of them.
182
186
  - **No broadcast history.** A broadcast reaches the sessions active at send time;
183
187
  a session that joins later does not receive it.
188
+ - **Mail to a gone session is refused, not queued.** A record whose process has
189
+ exited is not a recipient: `send` naming it answers `no active session`, and a
190
+ broadcast skips it. Its inbox outlives a `/resume`, but a sender is never told a
191
+ message arrived at a process that has exited — the bus's rule for a claim, and
192
+ for `status`, is the same one.
184
193
  - **No forced claims.** There is no takeover while an owner can still answer: a
185
194
  claim is released by its owner, or by the next bus call once the owner's process
186
195
  is gone. A *hung* owner still blocks its paths until the session TTL expires,
@@ -203,9 +212,10 @@ listening watch is what raises the turn.
203
212
  pane's shutdown leaves the record and the claims to the newer one instead of
204
213
  releasing them. Listening is still per process, so the survivor's watch is the
205
214
  survivor's.
206
- - **A message file that does not parse is dropped.** The board's read never
207
- consumes mail, but it does delete a file it cannot validate, so a truncated
208
- message cannot linger on the bus.
215
+ - **A message file that does not parse is skipped, and left to `inbox` and the TTL.** The
216
+ board's read never consumes mail, so it deletes nothing — not even a file it cannot
217
+ validate, which may be mail this build cannot read and the next one can. Consuming it
218
+ or the 24-hour expiry is what clears the file.
209
219
  - **No network.** It is not a distributed bus and does not synchronize machines.
210
220
 
211
221
  ## Pi integration
@@ -214,7 +224,7 @@ listening watch is what raises the turn.
214
224
  |---|---|
215
225
  | Tools | `session` — one action-shaped tool; `readOnlyHint: false`, `destructiveHint: true`, `openWorldHint: false`, `executionMode: "sequential"`. |
216
226
  | Commands | `/sessions` — the board in `tui`, the listing elsewhere; no arguments and no sub-commands. |
217
- | UI | `tui` mounts `SessionListComponent` through `withPolledScreen` (rails suppressed, a 1 s read-only poll of `snapshot()` + `peek()`); every other mode prints through `ctx.ui.notify`. No status chip, so listening never changes the footer. |
227
+ | UI | `tui` mounts `SessionListComponent` through `withPolledScreen` (rails suppressed, a 1 s read-only poll of `snapshot()` + `peek()`); every other mode prints through `ctx.ui.notify`. No status chip: the watch state changes no footer segment, and the label is the bus's own `session-label` event rather than a chip. |
218
228
  | Events | `session_start` resolves the repository and arms the mail trigger, without joining; `session_tree` only repaints, since navigating the tree changes no pending record; `agent_start` marks the run busy; `before_agent_start` reports queued non-wake mail at the next turn; `agent_settled` retries a wake the run suppressed; `context` prunes stale notices; `session_shutdown` clears the heartbeat, the watch ticker, and the record. The seven hooks are `lib/report.ts`'s `registerPendingChannel`, not a local copy. |
219
229
  | State | Files under `<agent-dir>/session-bus/<repo-key>/`; nothing in the session branch. Listening is session-only. `/resume` re-registers the same session id. |
220
230
  | Lifecycle | The factory only registers. The runtime starts on `session_start`; heartbeat, watch ticker, and cleanup belong to it. Disabling `session-bus` through `PI_DISABLED_EXTENSIONS` registers nothing. |
@@ -233,7 +243,11 @@ listening watch is what raises the turn.
233
243
  started through a link named differently is named after the directory it is
234
244
  actually in. An absolute path argument that sits outside the repository is
235
245
  refused before it is re-keyed, and a relative one whose target leaves the
236
- repository is refused by the re-keying itself.
246
+ repository is refused by the re-keying itself. A path whose target is the
247
+ repository root is refused there too, and by that one rule: the spelling check
248
+ hands the root on rather than pre-empting it, so the sentence a caller reads
249
+ says what happened (`is the repository, not a path in it`) instead of claiming
250
+ a path inside the repository is outside it.
237
251
  - **One file per item.** Sessions never append to a shared registry: each session
238
252
  writes its own record, each message is its own file, and a claim is a temp file
239
253
  hard-linked into place — an exclusive create no reader can see half-written.
@@ -252,6 +266,14 @@ listening watch is what raises the turn.
252
266
  with one `readdir` every 2 s, the same trade `watch` makes for a file tail, so
253
267
  it does not depend on a platform's event API or a filename in the event. Only
254
268
  a listening session polls.
269
+ - **The poll reads the mail it has not announced, and its queue by what is on disk.** That one
270
+ read returns up to 50 messages from the front of the inbox whose ids the watch does not have,
271
+ plus the ids of everything still unread, so where a message's id sorts cannot hide it: a
272
+ backlog deeper than one page arrives a page at a time, and a peer whose clock lags writes an id
273
+ before every other and is announced just the same. The queue and the watch's memory are pruned
274
+ by what is still on disk rather than by a count of ids, so mail already queued keeps the place
275
+ it held and nothing is announced twice. `inbox` still consumes oldest first, and a limit never
276
+ starves.
255
277
  - **Read the board, prune on the next call.** The open board polls a read-only
256
278
  `snapshot()` and `peek()` that hide a session past the TTL, a claim held by one,
257
279
  and consume nothing. The physical prune, and the claim release that goes with
@@ -274,14 +296,14 @@ listening watch is what raises the turn.
274
296
  |---|---|
275
297
  | `index.ts` | Disable guard, registration, the pending-report channel, and the session hooks. |
276
298
  | `join-request.ts` | The `board`/`team` join request: one `bus.join` answer, and the label it settles becomes Pi's session name. |
277
- | `runtime.ts` | Lifecycle, repo key, joining, heartbeat, and the session's own reads and writes. |
299
+ | `runtime.ts` | Lifecycle, repo key (`<agent-dir>/session-bus/<repo-key>/`), joining, heartbeat, and the session's own reads and writes. |
278
300
  | `mail-watch.ts` | The armed inbox watch: the poll, the not-yet-reported queue, and its state. |
279
301
  | `store.ts` | Atomic JSON files, inbox, peek, claims, pruning, and the one release loop. |
280
302
  | `protocol.ts` | Pure labels, targets, paths, validation, conflict rules. |
281
303
  | `overlap.ts` | The one "do these paths collide?" predicate: `claimsConflict`, which normalizes both spellings of a claim path first. |
282
304
  | `resolve.ts` | `resolveClaimPaths` and `claimPathMessage`: a claim path made repository-relative, posix, and canonical through its real target, or the refusal naming why. |
283
305
  | `types.ts` | The stored shapes — session record, message, claim — and the tool's `details`. |
284
- | `tui.ts` | The `/sessions` board, its scopes, and the pane over one row; the row grammar is in `rows.ts`. |
306
+ | `view/session-list.ts` | The `/sessions` board, its scopes, and the pane over one row, loaded on first use; the row grammar is in `rows.ts`. |
285
307
  | `rows.ts` | The session/claim/message row builders and summaries shared by the board and the transcript. |
286
308
  | `result.ts` | The `session` tool's expanded result block and its collapsed line. |
287
309
  | `notice.ts` | The mail notice's model text and renderer details. |
@@ -8,27 +8,25 @@ can also start in the background and be inspected, collected, or cancelled with
8
8
 
9
9
  ## Capabilities
10
10
 
11
- Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
12
-
13
- | Capability | How | What you get | Status |
14
- |---|---|---|---|
15
- | Isolated context | `subagent` | Each task runs in its own process with its own transcript, and only its final reply returns to the caller. | Shipped |
16
- | Explicit tool allowlist | `--tools` for every child | An agent gets only its declared tools, not the parent's ambient extensions; `worker` cannot recurse into `subagent`. | Shipped |
17
- | Read-only delegation | `subagent` (`readOnly: true`) | Every spawned agent runs with a reader-only tool set, enforced at the process boundary. | Shipped |
18
- | Plan-mode guard | `subagent`, plan mode | Plan mode forces every call read-only and refuses `/agents` until the plan is approved. | Shipped |
19
- | Project-agent trust gate | `agentScope` with the trust gate | A `.pi/agents` file loads only when the project is trusted or confirmed, so a repository cannot inject a prompt by adding a file. | Shipped |
20
- | Three delegation modes | `subagent` (`agent` + `task`, `tasks[]`, `chain[]`) | One agent, an independent batch, or an ordered chain whose steps receive `{previous}`. | Shipped |
21
- | Parallel and chain bounds | `tasks[]`, `chain[]` | Parallel accepts up to 8 tasks with 4 running at once; a chain accepts up to 8 steps and stops at the first failure. | Shipped |
22
- | Per-step working directory | `cwd` | A single run, each parallel task, or each chain step can name its working directory; a relative path resolves against the active root. | Shipped |
23
- | Built-in agents | `subagent` (`agent`) | Recon, planning, review, implementation, research, testing, debugging, and docs work without writing an agent file. | Shipped |
24
- | External agents | `subagent` (`agentScope`) | Markdown agents from the user directory and the nearest `.pi/agents`, opt-in per scope and never replacing a built-in silently. | Shipped |
25
- | Background runs | `subagent` (`background: true`), `/agents` | A single run continues after the call returns and is inspected, collected, or cancelled from the board. | Shipped |
26
- | Background concurrency cap | `backgroundMax` | At most four background children run at once; finished runs release their slot. | Shipped |
27
- | Session-owned background run | `background: true` | `/tree` does not move the run and a restart cannot resume it; an unfinished start shows as `interrupted`. | Shipped |
28
- | Live progress | `subagent` | Tool calls and progress update in the transcript while the child runs, with a `↳ N` footer chip and a finish notice. | Shipped |
29
- | Bounded runs | `timeoutMs` | A child that hangs is killed at the deadline and reported as a failed run rather than rejecting the whole call. | Shipped |
30
- | Optional transcripts | `transcripts` | Per-run streams under `.pi/subagents/`, written by the parent and pruned by age and count. | Shipped |
31
- | Inherited model | `subagent` | Children use the dispatching session's model and thinking level unless an agent overrides them. | Shipped |
11
+ | Capability | How | What you get |
12
+ |---|---|---|
13
+ | Isolated context | `subagent` | Each task runs in its own process with its own transcript, and only its final reply returns to the caller. |
14
+ | Explicit tool allowlist | `--tools` for every child | An agent gets only its declared tools, not the parent's ambient extensions; `worker` cannot recurse into `subagent`. |
15
+ | Read-only delegation | `subagent` (`readOnly: true`) | Every spawned agent runs with a reader-only tool set, enforced at the process boundary. |
16
+ | Plan-mode guard | `subagent`, plan mode | Plan mode forces every call read-only and refuses `/agents` until the plan is approved. |
17
+ | Project-agent trust gate | `agentScope` with the trust gate | A `.pi/agents` file loads only when the project is trusted or confirmed, so a repository cannot inject a prompt by adding a file. |
18
+ | Three delegation modes | `subagent` (`agent` + `task`, `tasks[]`, `chain[]`) | One agent, an independent batch, or an ordered chain whose steps receive `{previous}`. |
19
+ | Parallel and chain bounds | `tasks[]`, `chain[]` | Parallel accepts up to 8 tasks with 4 running at once; a chain accepts up to 8 steps and stops at the first failure. |
20
+ | Per-step working directory | `cwd` | A single run, each parallel task, or each chain step can name its working directory; a relative path resolves against the active root. |
21
+ | Built-in agents | `subagent` (`agent`) | Recon, planning, review, implementation, research, testing, debugging, and docs work without writing an agent file. |
22
+ | External agents | `subagent` (`agentScope`) | Markdown agents from the user directory and the nearest `.pi/agents`, opt-in per scope and never replacing a built-in silently. |
23
+ | Background runs | `subagent` (`background: true`), `/agents` | A single run continues after the call returns and is inspected, collected, or cancelled from the board. |
24
+ | Background concurrency cap | `backgroundMax` | At most four background children run at once; finished runs release their slot. |
25
+ | Session-owned background run | `background: true` | `/tree` does not move the run and a restart cannot resume it; an unfinished start shows as `interrupted`. |
26
+ | Live progress | `subagent` | Tool calls and progress update in the transcript while the child runs, with a `↳ N` footer chip and a finish notice. |
27
+ | Bounded runs | `timeoutMs` | A child that hangs is killed at the deadline and reported as a failed run rather than rejecting the whole call. |
28
+ | Optional transcripts | `transcripts` | Per-run streams under `.pi/subagents/`, written by the parent and pruned by age and count. |
29
+ | Inherited model | `subagent` | Children use the dispatching session's model and thinking level unless an agent overrides them. |
32
30
 
33
31
  ## Quickstart
34
32
 
@@ -229,7 +227,7 @@ picks up ambient extensions and `worker` cannot recurse into `subagent`.
229
227
  for another agent (typically `worker`) to execute, and it works headlessly. It
230
228
  returns the plan as text and writes nothing. For **interactive** planning where
231
229
  the user reviews and approves a saved plan, use the plan-mode extension instead
232
- (`enter_plan_mode` / `write_plan` / `exit_plan_mode`), which operates on the
230
+ (`plan` actions `enter` / `write` / `exit`), which operates on the
233
231
  main session and persists to the agent directory. Plan mode keeps `subagent` available
234
232
  for exploration but forces it read-only, so delegated recon is safe mid-plan.
235
233
 
@@ -454,15 +452,15 @@ step's `Output`.
454
452
  |---|---|
455
453
  | `index.ts` | Registers the `subagent` tool and assembles the mode context. |
456
454
  | `orchestrate.ts` | The `single`/`parallel`/`chain` mode runners. |
457
- | `agents.ts` | Built-in agent definitions, external markdown discovery, and pool resolution. |
455
+ | `agents.ts` | Built-in agent definitions, external markdown discovery (`<agent-dir>/agents`, and the nearest `<repo>/.pi/agents`), and pool resolution. |
458
456
  | `schema.ts` | TypeBox params, limits, and `resolveMode`. |
459
457
  | `invocation.ts` | `pi` subprocess invocation and argument construction. |
460
458
  | `stream.ts` | JSON-event parsing, guarded usage accounting, output helpers (pure). |
461
- | `transcripts.ts` | Per-run transcript buffer, run ids, the store directory, and pruning. |
459
+ | `transcripts.ts` | Per-run transcript buffer, run ids, the store directory (`<agent-dir>/subagents/`), and pruning. |
462
460
  | `config.ts` | The `subagent` section of `bytefly.json`: deadline, transcripts, retention. |
463
461
  | `run.ts` | Runs one subagent process and captures its result (including the background abort-as-result path). |
464
462
  | `background.ts` | Background run entries, replay, capacity, cancel, collect, and shutdown. |
465
- | `background-tui.ts` | The `/agents` board: a `DockListScreen` with a live output pane and width-aware facts. |
463
+ | `view/agents-board.ts` | The `/agents` board: a `DockListScreen` with a live output pane and width-aware facts, loaded on first use. |
466
464
  | `commands.ts` | The `/agents` command. |
467
465
  | `ui.ts` | The running-count footer chip and finish notices. |
468
466
  | `accounting.ts` | The `subagent-finished` event payload built from a result (pure). |