@gavin-hu/my-pi-agent 0.18.0 → 0.20.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 (324) hide show
  1. package/README.md +66 -52
  2. package/README.zh-CN.md +56 -46
  3. package/docs/extensions/README.md +34 -0
  4. package/docs/extensions/board.md +507 -0
  5. package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +774 -574
  6. package/{extensions/check/README.md → docs/extensions/check.md} +83 -68
  7. package/{extensions/context/README.md → docs/extensions/context.md} +40 -23
  8. package/docs/extensions/file-browse.md +561 -0
  9. package/{extensions/file-open/README.md → docs/extensions/file-open.md} +31 -23
  10. package/{extensions/file-read/README.md → docs/extensions/file-read.md} +47 -55
  11. package/{extensions/goal/README.md → docs/extensions/goal.md} +115 -131
  12. package/{extensions/job/README.md → docs/extensions/job.md} +62 -52
  13. package/{extensions/memory/README.md → docs/extensions/memory.md} +71 -60
  14. package/{extensions/plan/README.md → docs/extensions/plan.md} +440 -322
  15. package/{extensions/question/README.md → docs/extensions/question.md} +54 -31
  16. package/{extensions/rewind/README.md → docs/extensions/rewind.md} +115 -122
  17. package/docs/extensions/session-bus.md +298 -0
  18. package/{extensions/subagent/README.md → docs/extensions/subagent.md} +165 -118
  19. package/docs/extensions/team.md +254 -0
  20. package/{extensions/todo/README.md → docs/extensions/todo.md} +85 -88
  21. package/{extensions/watch/README.md → docs/extensions/watch.md} +54 -36
  22. package/{extensions/web-access/README.md → docs/extensions/web-access.md} +93 -50
  23. package/{extensions/wechat/README.md → docs/extensions/wechat.md} +260 -163
  24. package/{extensions/worktree/README.md → docs/extensions/worktree.md} +137 -79
  25. package/extensions/board/commands.ts +105 -0
  26. package/extensions/board/config.ts +36 -0
  27. package/extensions/board/format.ts +170 -0
  28. package/extensions/board/index.ts +40 -0
  29. package/extensions/board/input.ts +54 -0
  30. package/extensions/board/join.ts +42 -0
  31. package/extensions/board/page.ts +388 -0
  32. package/extensions/board/result.ts +108 -0
  33. package/extensions/board/rows.ts +48 -0
  34. package/extensions/board/schema.ts +90 -0
  35. package/extensions/board/seat.ts +28 -0
  36. package/extensions/board/snapshot.ts +42 -0
  37. package/extensions/board/tools.ts +444 -0
  38. package/extensions/board/transport/router.ts +91 -0
  39. package/extensions/board/transport/server.ts +138 -0
  40. package/extensions/board/types.ts +121 -0
  41. package/extensions/board/view/client.ts +51 -0
  42. package/extensions/board/view/dialog.ts +43 -0
  43. package/extensions/board/view/keys.ts +125 -0
  44. package/extensions/board/view/live.ts +231 -0
  45. package/extensions/board/view/names.ts +91 -0
  46. package/extensions/board/view/page.ts +118 -0
  47. package/extensions/board/view/render.ts +519 -0
  48. package/extensions/board/view/styles.ts +228 -0
  49. package/extensions/board/view/team.ts +327 -0
  50. package/extensions/bytefly/footer/budget.ts +6 -5
  51. package/extensions/bytefly/footer/collect/entries.ts +12 -4
  52. package/extensions/bytefly/footer/collect/snapshot.ts +8 -17
  53. package/extensions/bytefly/footer/config.ts +19 -8
  54. package/extensions/bytefly/footer/price/price.ts +30 -8
  55. package/extensions/bytefly/footer/register.ts +5 -5
  56. package/extensions/bytefly/footer/render/format.ts +18 -0
  57. package/extensions/bytefly/footer/render/lines.ts +23 -7
  58. package/extensions/bytefly/footer/render/segments.ts +117 -31
  59. package/extensions/bytefly/footer/types.ts +29 -5
  60. package/extensions/bytefly/footer/unpriced.ts +42 -17
  61. package/extensions/bytefly/panel/commands.ts +67 -7
  62. package/extensions/bytefly/panel/filter.ts +68 -0
  63. package/extensions/bytefly/panel/format.ts +16 -15
  64. package/extensions/bytefly/panel/join.ts +56 -13
  65. package/extensions/bytefly/panel/prices.ts +43 -0
  66. package/extensions/bytefly/panel/settings-runtime.ts +0 -6
  67. package/extensions/bytefly/panel/settings.ts +75 -43
  68. package/extensions/bytefly/panel/tui/entries.ts +26 -8
  69. package/extensions/bytefly/panel/tui/frame.ts +130 -93
  70. package/extensions/bytefly/panel/tui/keys.ts +352 -0
  71. package/extensions/bytefly/panel/tui/list-view.ts +25 -46
  72. package/extensions/bytefly/panel/tui/panes.ts +76 -0
  73. package/extensions/bytefly/panel/tui/section-view.ts +126 -0
  74. package/extensions/bytefly/panel/tui/settings-section.ts +54 -125
  75. package/extensions/bytefly/panel/tui/settings-view.ts +30 -131
  76. package/extensions/bytefly/panel/tui/side-pane.ts +224 -0
  77. package/extensions/bytefly/panel/tui.ts +306 -259
  78. package/extensions/bytefly/picker/discovery.ts +0 -2
  79. package/extensions/bytefly/picker/types.ts +0 -2
  80. package/extensions/bytefly/update/check.ts +39 -0
  81. package/extensions/bytefly/update/format.ts +15 -0
  82. package/extensions/bytefly/update/register.ts +73 -34
  83. package/extensions/bytefly/update/state.ts +6 -3
  84. package/extensions/check/commands.ts +2 -1
  85. package/extensions/context/tui.ts +1 -1
  86. package/extensions/file-browse/{config.ts → config/config.ts} +1 -1
  87. package/extensions/file-browse/files/classify.ts +136 -0
  88. package/extensions/file-browse/files/files.ts +12 -0
  89. package/extensions/file-browse/files/index.ts +90 -0
  90. package/extensions/file-browse/files/list.ts +261 -0
  91. package/extensions/file-browse/{paths.ts → files/paths.ts} +1 -1
  92. package/extensions/file-browse/files/tree.ts +101 -0
  93. package/extensions/file-browse/{git.ts → git/git.ts} +10 -3
  94. package/extensions/file-browse/index.ts +4 -4
  95. package/extensions/file-browse/{live.ts → live/live.ts} +20 -69
  96. package/extensions/file-browse/{router.ts → transport/router.ts} +21 -17
  97. package/extensions/file-browse/transport/server.ts +110 -0
  98. package/extensions/file-browse/{chrome.ts → view/chrome.ts} +4 -4
  99. package/extensions/file-browse/{client.ts → view/client.ts} +1 -1
  100. package/extensions/file-browse/{diff-html.ts → view/diff-html.ts} +3 -3
  101. package/extensions/file-browse/view/emit.ts +51 -0
  102. package/extensions/file-browse/{html.ts → view/html.ts} +19 -18
  103. package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
  104. package/extensions/file-browse/{markdown.ts → view/markdown.ts} +18 -20
  105. package/extensions/file-browse/{raw-html.ts → view/raw-html.ts} +19 -14
  106. package/extensions/file-browse/{styles.ts → view/styles.ts} +29 -5
  107. package/extensions/file-browse/{tree.ts → view/tree.ts} +1 -1
  108. package/extensions/file-browse/{urls.ts → view/urls.ts} +1 -1
  109. package/extensions/file-read/schema.ts +2 -1
  110. package/extensions/job/runtime.ts +4 -4
  111. package/extensions/memory/store.ts +12 -5
  112. package/extensions/plan/blocks.ts +23 -2
  113. package/extensions/plan/commands.ts +21 -16
  114. package/extensions/plan/feedback.ts +185 -52
  115. package/extensions/plan/format.ts +10 -3
  116. package/extensions/plan/index.ts +23 -5
  117. package/extensions/plan/plan-files.ts +88 -0
  118. package/extensions/plan/plan-index.ts +43 -9
  119. package/extensions/plan/plan-text.ts +33 -23
  120. package/extensions/plan/plans.ts +27 -34
  121. package/extensions/plan/policy.ts +30 -17
  122. package/extensions/plan/review.ts +3 -27
  123. package/extensions/plan/runtime.ts +52 -11
  124. package/extensions/plan/sections.ts +112 -0
  125. package/extensions/plan/steps.ts +3 -22
  126. package/extensions/plan/todos.ts +51 -3
  127. package/extensions/plan/tools/exit.ts +20 -95
  128. package/extensions/plan/tools/todo-seed.ts +4 -2
  129. package/extensions/plan/tools/write.ts +12 -1
  130. package/extensions/plan/tui.ts +5 -34
  131. package/extensions/plan/types.ts +15 -8
  132. package/extensions/plan/web-blocks.ts +89 -0
  133. package/extensions/plan/web-chrome.ts +3 -5
  134. package/extensions/plan/web-client.ts +179 -330
  135. package/extensions/plan/web-collab.ts +3 -3
  136. package/extensions/plan/web-comments.ts +18 -8
  137. package/extensions/plan/web-composer.ts +80 -0
  138. package/extensions/plan/web-draft.ts +40 -0
  139. package/extensions/plan/web-keys.ts +134 -0
  140. package/extensions/plan/web-outline.ts +192 -0
  141. package/extensions/plan/web-page.ts +4 -8
  142. package/extensions/plan/web-plan.ts +150 -0
  143. package/extensions/plan/web-router.ts +3 -58
  144. package/extensions/plan/web-settle.ts +194 -0
  145. package/extensions/plan/web-share-session.ts +24 -30
  146. package/extensions/plan/web-streams.ts +14 -14
  147. package/extensions/plan/web-tokens.ts +2 -2
  148. package/extensions/plan/web.ts +66 -120
  149. package/extensions/rewind/{commands.ts → commands/commands.ts} +5 -5
  150. package/extensions/rewind/{review.ts → commands/review.ts} +15 -14
  151. package/extensions/rewind/{rewind.ts → commands/rewind.ts} +6 -6
  152. package/extensions/rewind/{config.ts → config/config.ts} +1 -1
  153. package/extensions/rewind/index.ts +3 -3
  154. package/extensions/rewind/{policy.ts → state/policy.ts} +4 -4
  155. package/extensions/rewind/{runtime.ts → state/runtime.ts} +11 -11
  156. package/extensions/rewind/{snapshot-run.ts → state/snapshot-run.ts} +1 -1
  157. package/extensions/rewind/{timeline.ts → state/timeline.ts} +2 -2
  158. package/extensions/rewind/{format.ts → text/format.ts} +3 -3
  159. package/extensions/rewind/{git.ts → transport/git.ts} +2 -2
  160. package/extensions/rewind/{restore.ts → transport/restore.ts} +2 -2
  161. package/extensions/rewind/{snapshot.ts → transport/snapshot.ts} +1 -1
  162. package/extensions/rewind/{store.ts → transport/store.ts} +1 -1
  163. package/extensions/rewind/{checklist.ts → view/checklist.ts} +6 -6
  164. package/extensions/rewind/{hunk-picker.ts → view/hunk-picker.ts} +8 -8
  165. package/extensions/rewind/{path-picker.ts → view/path-picker.ts} +7 -7
  166. package/extensions/rewind/{review-tui.ts → view/review-tui.ts} +11 -11
  167. package/extensions/rewind/{rewind-tui.ts → view/rewind-tui.ts} +17 -11
  168. package/extensions/rewind/{status.ts → view/status.ts} +4 -4
  169. package/extensions/session-bus/commands.ts +17 -69
  170. package/extensions/session-bus/format.ts +22 -45
  171. package/extensions/session-bus/index.ts +13 -7
  172. package/extensions/session-bus/join-request.ts +40 -0
  173. package/extensions/session-bus/mail-watch.ts +134 -0
  174. package/extensions/session-bus/notice.ts +32 -4
  175. package/{lib/claims → extensions/session-bus}/overlap.ts +11 -18
  176. package/extensions/session-bus/protocol.ts +19 -49
  177. package/extensions/session-bus/resolve.ts +67 -0
  178. package/extensions/session-bus/result.ts +16 -51
  179. package/extensions/session-bus/rows.ts +11 -5
  180. package/extensions/session-bus/runtime.ts +220 -267
  181. package/extensions/session-bus/store.ts +55 -7
  182. package/extensions/session-bus/tools.ts +31 -66
  183. package/extensions/session-bus/tui.ts +5 -5
  184. package/extensions/session-bus/types.ts +23 -14
  185. package/extensions/subagent/agents.ts +9 -8
  186. package/extensions/subagent/commands.ts +2 -1
  187. package/extensions/subagent/invocation.ts +4 -2
  188. package/extensions/team/commands.ts +145 -0
  189. package/extensions/team/dispatch.ts +133 -0
  190. package/extensions/team/format.ts +119 -0
  191. package/extensions/team/index.ts +34 -0
  192. package/extensions/team/schema.ts +29 -0
  193. package/extensions/team/tools.ts +267 -0
  194. package/extensions/team/types.ts +65 -0
  195. package/extensions/todo/index.ts +4 -1
  196. package/extensions/todo/nudge.ts +8 -2
  197. package/extensions/web-access/fetch/page.ts +4 -1
  198. package/extensions/web-access/fetch/structured.ts +447 -0
  199. package/extensions/web-access/fetch/tool.ts +2 -1
  200. package/extensions/web-access/search/tool.ts +2 -1
  201. package/extensions/wechat/bridge.ts +106 -251
  202. package/extensions/wechat/{commands.ts → commands/commands.ts} +26 -18
  203. package/extensions/wechat/{doctor.ts → commands/doctor.ts} +5 -5
  204. package/extensions/wechat/{history.ts → commands/history.ts} +1 -1
  205. package/extensions/wechat/{login.ts → commands/login.ts} +3 -3
  206. package/extensions/wechat/{slash.ts → commands/slash.ts} +38 -2
  207. package/extensions/wechat/dialog/channels.ts +136 -0
  208. package/extensions/wechat/{interaction.ts → dialog/interaction.ts} +37 -37
  209. package/extensions/wechat/{prompt.ts → dialog/prompt.ts} +2 -2
  210. package/extensions/wechat/{remote-ui.ts → dialog/remote-ui.ts} +1 -1
  211. package/extensions/wechat/index.ts +10 -10
  212. package/extensions/wechat/intake.ts +245 -0
  213. package/extensions/wechat/{admission.ts → peer/admission.ts} +2 -2
  214. package/extensions/wechat/{peer-session.ts → peer/peer-session.ts} +2 -2
  215. package/extensions/wechat/{peer-worker.ts → peer/peer-worker.ts} +100 -22
  216. package/extensions/wechat/{presence.ts → peer/presence.ts} +8 -7
  217. package/extensions/wechat/{reply-pipeline.ts → peer/reply-pipeline.ts} +1 -1
  218. package/extensions/wechat/{credentials.ts → state/credentials.ts} +2 -2
  219. package/extensions/wechat/{state.ts → state/state.ts} +14 -2
  220. package/extensions/wechat/{format.ts → text/format.ts} +3 -3
  221. package/extensions/wechat/{notices.ts → text/notices.ts} +8 -0
  222. package/extensions/wechat/tools.ts +29 -12
  223. package/extensions/wechat/{client.ts → transport/client.ts} +26 -2
  224. package/extensions/wechat/{deliver.ts → transport/deliver.ts} +72 -28
  225. package/extensions/wechat/{inbound.ts → transport/inbound.ts} +5 -5
  226. package/extensions/wechat/{media.ts → transport/media.ts} +11 -5
  227. package/extensions/wechat/transport/outbound.ts +142 -0
  228. package/extensions/wechat/{poll.ts → transport/poll.ts} +3 -3
  229. package/extensions/wechat/types.ts +10 -1
  230. package/extensions/wechat/{login-screen.ts → view/login-screen.ts} +4 -4
  231. package/extensions/wechat/{status-screen.ts → view/status-screen.ts} +31 -26
  232. package/extensions/worktree/commands.ts +24 -17
  233. package/extensions/worktree/exit.ts +12 -6
  234. package/extensions/worktree/git.ts +22 -14
  235. package/extensions/worktree/guard.ts +18 -290
  236. package/extensions/worktree/handoff.ts +10 -2
  237. package/extensions/worktree/index.ts +2 -1
  238. package/extensions/worktree/lifecycle.ts +2 -0
  239. package/extensions/worktree/registry.ts +20 -11
  240. package/extensions/worktree/root-tools.ts +13 -3
  241. package/extensions/worktree/runtime.ts +8 -2
  242. package/extensions/worktree/screen.ts +15 -8
  243. package/extensions/worktree/shell.ts +303 -0
  244. package/extensions/worktree/state.ts +1 -1
  245. package/extensions/worktree/status.ts +42 -9
  246. package/extensions/worktree/tools.ts +19 -13
  247. package/lib/README.md +11 -8
  248. package/lib/board/card.ts +479 -0
  249. package/lib/board/links.ts +141 -0
  250. package/lib/board/page-pointer.ts +310 -0
  251. package/lib/board/round-text.ts +81 -0
  252. package/lib/board/round.ts +94 -0
  253. package/lib/board/rules.ts +437 -0
  254. package/lib/board/session.ts +105 -0
  255. package/lib/board/store.ts +497 -0
  256. package/lib/board/view.ts +234 -0
  257. package/lib/errors.ts +12 -4
  258. package/lib/format.ts +35 -0
  259. package/lib/http/guard.ts +112 -0
  260. package/lib/http/index.ts +16 -5
  261. package/lib/http/serve.ts +237 -0
  262. package/lib/http/server.ts +12 -10
  263. package/lib/http/sink.ts +135 -0
  264. package/lib/http/token.ts +38 -0
  265. package/lib/plan-entry.ts +11 -0
  266. package/lib/policy.ts +25 -2
  267. package/lib/requirements.ts +6 -7
  268. package/lib/session-label.ts +64 -0
  269. package/lib/session-roster.ts +201 -0
  270. package/lib/session-tool.ts +184 -0
  271. package/lib/spawn.ts +37 -3
  272. package/lib/stray-fields.ts +45 -0
  273. package/lib/tool-names.ts +38 -2
  274. package/lib/tui/glyphs.ts +3 -2
  275. package/lib/web/README.md +57 -4
  276. package/lib/web/client-runtime.ts +123 -0
  277. package/lib/web/components.ts +5 -0
  278. package/lib/web/index.ts +4 -2
  279. package/lib/web/markup.ts +88 -1
  280. package/lib/web/names.ts +42 -0
  281. package/lib/web/runtime.ts +7 -1
  282. package/lib/web/tokens.ts +25 -2
  283. package/package.json +26 -14
  284. package/skills/next/SKILL.md +4 -3
  285. package/skills/project-docs/SKILL.md +133 -0
  286. package/skills/project-docs/references/audit.md +131 -0
  287. package/skills/project-docs/references/changelog.md +105 -0
  288. package/skills/project-docs/references/discovery.md +127 -0
  289. package/skills/project-docs/references/readme.md +80 -0
  290. package/skills/project-docs/references/release.md +84 -0
  291. package/skills/project-docs/references/roadmap.md +93 -0
  292. package/skills/project-docs/scripts/state.ts +316 -0
  293. package/skills/refactor/SKILL.md +100 -125
  294. package/skills/refactor/references/handoff.md +35 -0
  295. package/skills/refactor/references/signals.md +54 -0
  296. package/skills/refactor/scripts/inventory.ts +587 -0
  297. package/extensions/bytefly/panel/tui/extensions-view.ts +0 -40
  298. package/extensions/bytefly/panel/tui/skills-view.ts +0 -50
  299. package/extensions/file-browse/README.md +0 -506
  300. package/extensions/file-browse/files.ts +0 -564
  301. package/extensions/file-browse/server.ts +0 -141
  302. package/extensions/job/process.ts +0 -35
  303. package/extensions/plan/split.ts +0 -87
  304. package/extensions/session-bus/README.md +0 -247
  305. package/extensions/session-bus/team.ts +0 -102
  306. package/extensions/wechat/outbound.ts +0 -100
  307. package/lib/plans/sections.ts +0 -172
  308. package/lib/plans/setting.ts +0 -52
  309. package/lib/plans/split.ts +0 -215
  310. package/lib/plans/store.ts +0 -19
  311. package/lib/plans/team.ts +0 -151
  312. package/skills/changelog/SKILL.md +0 -148
  313. package/skills/parallel-dev/SKILL.md +0 -150
  314. /package/extensions/file-browse/{rev.ts → live/rev.ts} +0 -0
  315. /package/extensions/file-browse/{diff.ts → text/diff.ts} +0 -0
  316. /package/extensions/file-browse/{fuzzy.ts → text/fuzzy.ts} +0 -0
  317. /package/extensions/file-browse/{live-client.ts → view/live-client.ts} +0 -0
  318. /package/extensions/file-browse/{palette.ts → view/palette.ts} +0 -0
  319. /package/extensions/rewind/{patch.ts → text/patch.ts} +0 -0
  320. /package/extensions/rewind/{paths.ts → text/paths.ts} +0 -0
  321. /package/extensions/rewind/{queue.ts → transport/queue.ts} +0 -0
  322. /package/extensions/rewind/{temp-index.ts → transport/temp-index.ts} +0 -0
  323. /package/extensions/wechat/{lock.ts → state/lock.ts} +0 -0
  324. /package/extensions/wechat/{channel.ts → text/channel.ts} +0 -0
@@ -0,0 +1,298 @@
1
+ # session-bus — coordinate parallel Pi sessions
2
+
3
+ A local coordination bus for Pi sessions on the same machine and repository:
4
+ sessions discover each other, exchange short messages and events, consume an
5
+ inbox, and claim paths before editing. A session joins explicitly, so the
6
+ board opens empty and a peer never appears because the package loaded. Delivery is pull-based, and a session that
7
+ joined listens for new mail — a report at its next turn, or,
8
+ with `wake`, one raised turn as soon as the agent is idle, including right after
9
+ a running turn settles. There is no network service and no hidden context.
10
+
11
+ <img src="../../assets/screens/session.png" alt="The /sessions screen: a counts summary, session rows with unread and claim counts, and the focused session's cwd, claims, and identity" width="100%">
12
+
13
+ ## Capabilities
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
+ | 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 |
20
+ | 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
+ | Live board | `/sessions` | Sessions, this session's unread mail, and every claim held on the repository, on one screen. | Shipped |
22
+ | Board actions | `/sessions` (`w`, `x`) | Start or stop listening from the board, or release an own claim behind a confirmation, without leaving it. | Shipped |
23
+ | Short messages | `session send` | One message per recipient, addressed by label or id, with a broadcast for events. | Shipped |
24
+ | Message kinds | `session send` with `kind` | A `message`, `event`, `ask`, or `handoff` keeps its meaning; `ask` does not block the sender, and a `handoff` is named as the work it assigns. | Shipped |
25
+ | Message paths | `session send` with `paths` | A message can name the repository paths it concerns, echoed in the inbox row. | Shipped |
26
+ | Inbox on demand | `session inbox` | Up to `limit` messages, oldest first, consumed only when the model asks. | Shipped |
27
+ | Listening | `session join` | New mail reported at the next turn; `join({wake: true})` raises one turn once the agent is idle, and `join({listen: false})` stops it. The record carries whether this session listens, so a peer's status read answers it without asking. | Shipped |
28
+ | Path claims | `session claim`, `session release` | A file or directory prefix held by one session at a time. | Shipped |
29
+
30
+ ## Quickstart
31
+
32
+ ```bash
33
+ pi --extension ./extensions/session-bus # load just this extension
34
+ pi -e . # load the whole package
35
+ pi install ./ # install the package for a user
36
+ ```
37
+
38
+ ```text
39
+ /sessions # open the board: empty until this session joins
40
+ session({action:"join", label:"billing-core"}) # join, name this session for Pi, and listen for mail
41
+ ```
42
+
43
+ Nothing joins on `session_start`, in any mode: the board and `status` only read, so
44
+ a fresh session sees an empty board and a peer's session is not listed until it
45
+ joins. `join` is the join — it puts this session on the bus, names it when a label
46
+ is given, and starts listening — and so is the first action that needs you to be
47
+ addressable: `send`, `inbox`, `claim`, or `release`.
48
+
49
+ ## Tool
50
+
51
+ `session` — `exposure: "direct"` (the default), `executionMode: "sequential"`,
52
+ read-write and closed-world. One action-shaped tool keeps the prompt surface
53
+ small: each action takes only the parameters it needs, and the result is one
54
+ collapsed line that expands to the board's rows.
55
+
56
+ | Action | Inputs | Result |
57
+ |---|---|---|
58
+ | `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"`. |
60
+ | `inbox` | optional `limit` | Returns and deletes up to `limit` messages, oldest first (default 20, max 50). |
61
+ | `claim` | `paths` | Claims repository-relative files or directory prefixes; returns conflicts with their owner. |
62
+ | `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. |
64
+
65
+ A claim is all-or-nothing: if one path conflicts, nothing is claimed. A claim on
66
+ a directory conflicts with claims below it and above it, so `src/api` and
67
+ `src/api/routes.ts` cannot be held by different sessions. There is no `force`
68
+ takeover; wait for the owner, ask it to release, or let the session expire.
69
+
70
+ Paths resolve against the caller's working directory and are stored relative to
71
+ its repository root; a path outside the repository is refused.
72
+
73
+ `wake` is the mode, not an arm-time switch: `join({ wake: true })` raises a turn
74
+ for a session that was reporting at the next turn, and `join({ wake: false })` —
75
+ or a plain `join` — sets it back. The board's `w` starts and stops listening,
76
+ and it starts a plain watch; set `wake` with the tool. Nothing joins or listens on
77
+ `session_start`; a session that never asks stays pull-only and off the bus.
78
+
79
+ ## Commands
80
+
81
+ `/sessions` is the human surface: in `tui` mode it opens the live board described
82
+ in [the board](#the-sessions-board), and every other mode prints the compact
83
+ listing. It takes no argument, and it joins nothing — the board's `w` is the one
84
+ act there that puts this session on the bus.
85
+
86
+ | Command | Effect |
87
+ |---|---|
88
+ | `/sessions` | Open the board: `Tab` cycles Sessions, Inbox, and Claims. `Enter` opens the focused row, `w` starts or stops listening, and in Claims `x` releases a claim you hold after a y/N confirmation. A non-TUI caller prints the listing. |
89
+
90
+ Labels are how another session addresses you. Name one by the outcome it owns
91
+ (`billing-core`, `integrator`, `verify-billing`), not by a layer or a role;
92
+ duplicate labels get a short id suffix automatically.
93
+
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.
97
+
98
+ The tool's `join` action is the same join from the model's side, so a `board` or a
99
+ `team` call can put a session on the bus without the user naming it, and
100
+ `/team join [<label>]` is that join from the terminal.
101
+
102
+ ## Behaviour by mode
103
+
104
+ | Mode | Behaviour |
105
+ |---|---|
106
+ | `tui` | Does not join on start. `/sessions` opens the board: sessions, this session's unread mail, and every claim, with `Tab` between them, and lists nobody in this session until it joins. |
107
+ | `rpc`, `json`, `print` | Same rule: `/sessions` and `status` are reads, and the first addressable action joins. This is how a subagent or an RPC-driven session opts in. |
108
+ | WeChat peer | Same as `rpc`: the peer is a separate AgentSession in the bridge process and joins only on explicit use. Messages are read by that peer’s model; the bridge does not forward them to the phone. |
109
+
110
+ Every mode can join and listen; the notice is a visible transcript message, so
111
+ a `wake` raises that session’s own turn, not a phone notification.
112
+
113
+ ## Configuration
114
+
115
+ None in v1. The heartbeat (10 s), watch poll (2 s), session TTL (10 min), message
116
+ TTL (24 h), text cap, inbox limit, and preview cap are fixed, not configurable.
117
+ The bus lives under the agent directory, not the project, so it is never committed.
118
+
119
+ ## Parallel workflow
120
+
121
+ 1. The user joins each session (`/team join <label>`, or `w` on the board), and the
122
+ contract (OpenAPI, schema, or types) is written before parallel work starts.
123
+ 2. Each session claims its paths before touching them, then implements its side.
124
+ 3. A contract, build, or test change goes out as an event:
125
+ `session({action:"send", to:"*", kind:"event", text:"contract v2: ..."})`.
126
+ 4. Other sessions consume `inbox` at the start of their next turn, or join with
127
+ `session({action:"join", wake:true})` so a message raises their turn as soon
128
+ as they are idle; a wake that arrives mid-turn is raised once that run
129
+ settles, never pushed into the running turn.
130
+ 5. Whoever integrates merges the worktrees and runs the project's full check.
131
+
132
+ The bus carries coordination, not authority. The plan, todo list, contract, and
133
+ git branch remain the source of truth.
134
+
135
+ ## The /sessions board
136
+
137
+ The TUI opens the board; RPC, JSON, and print print the compact listing. `Tab`
138
+ cycles Sessions, Inbox, and Claims, `Enter` opens the focused row, and `Esc` goes
139
+ back from a row's pane. `↑`/`↓` and `PgUp`/`PgDn` scroll a list; a pane adds
140
+ `g`/`G` for its ends.
141
+
142
+ The board changes only this session's own state: `w` starts or stops listening and
143
+ `x` releases a claim it holds. Mail is previewed, not consumed — `session inbox`
144
+ consumes it — and no view shows another session's inbox or releases its claims.
145
+
146
+ ## Message kinds
147
+
148
+ Message kinds are `message` (default), `event`, `ask`, and `handoff`. An `ask` is
149
+ still a message: it does not block or wait for a reply. Text is capped at 4,000
150
+ characters.
151
+
152
+ A `handoff` is the one kind that assigns work, so it is the one kind the notice
153
+ treats as an instruction: the waking turn is headlined `1 handoff`, the preview
154
+ keeps 800 characters so the card, its paths, and the next step survive, and the
155
+ hint says the work is yours — read it with `session inbox`, record the plan, and
156
+ start. Nothing else changes: a handoff still waits in the inbox, and the recipient's
157
+ listening watch is what raises the turn.
158
+
159
+ ## Limitations
160
+
161
+ - **Same machine, same OS user.** Any process with that user’s file access can
162
+ read or write the bus; there is no authentication between sessions.
163
+ - **Pull-based by default.** A message does not reach a session that has not
164
+ joined and is not listening. Agents must call `inbox` at coordination points, and
165
+ joining listens unless `join({ listen: false })` says otherwise.
166
+ - **A reader is not a member.** `/sessions` and `session status` never add this
167
+ session to the bus, so an unjoined session reads a board it is not on until it
168
+ joins or acts.
169
+ - **Membership is per session and session-only.** It survives neither a resume nor a
170
+ new process: the record and every claim it held go when the session stops, and a
171
+ resumed pane is off the bus until it joins again. What does survive is everything
172
+ stored as a file — unread mail is previewed once more after `/resume`, and the
173
+ label a rejoining session takes is the same one, because it derives from Pi's
174
+ session name (`runtime.test.ts`, *a resume*).
175
+ - **The board acts on your own state only.** `w` starts or stops listening and `x`
176
+ releases a claim it holds. Mail is a preview, not a consume: the board never
177
+ deletes a message, never marks one reported, and never touches a peer's inbox or
178
+ claim.
179
+ - **Two listening peers can ping-pong.** Each reply wakes the other as soon as it is
180
+ idle. A wake raises one turn per burst, and `inbox` consumes, so the loop needs
181
+ both sides to keep replying; the answer is to stop listening on one of them.
182
+ - **No broadcast history.** A broadcast reaches the sessions active at send time;
183
+ a session that joins later does not receive it.
184
+ - **No forced claims.** There is no takeover while an owner can still answer: a
185
+ claim is released by its owner, or by the next bus call once the owner's process
186
+ is gone. A *hung* owner still blocks its paths until the session TTL expires,
187
+ because a running process is not evidence of a working one.
188
+ - **A dead process is named, not waited out.** A record carries its `pid` and, where
189
+ the host can name one, that process's start time. A pid that is no longer running,
190
+ or one whose start time is not the record's (a reused pid), reads `gone` at once:
191
+ its label is free, the paths it held are nobody's, and `session status` and
192
+ `/sessions` count it. It keeps its row until its heartbeat ages out, so a lead can
193
+ see what died and reclaim it. A record with no `pid` — and a platform without
194
+ `ps`, such as Windows — falls back to the heartbeat rather than being called dead
195
+ on no evidence.
196
+ - **Bounded by TTL.** An idle session with a stopped heartbeat is pruned after
197
+ ten minutes; its record, its claims, and an inbox directory left empty go with
198
+ it, and its unread mail expires after 24 hours. A claim belongs to a record
199
+ that was read from disk, not to the reader's idea of who is active, so a peer
200
+ that claimed in the middle of someone else's prune keeps it.
201
+ - **One session id, one record.** Resuming a session in a second pane gives two
202
+ processes the same id, and the record names the process that owns it: the older
203
+ pane's shutdown leaves the record and the claims to the newer one instead of
204
+ releasing them. Listening is still per process, so the survivor's watch is the
205
+ 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.
209
+ - **No network.** It is not a distributed bus and does not synchronize machines.
210
+
211
+ ## Pi integration
212
+
213
+ | Point | Detail |
214
+ |---|---|
215
+ | Tools | `session` — one action-shaped tool; `readOnlyHint: false`, `destructiveHint: true`, `openWorldHint: false`, `executionMode: "sequential"`. |
216
+ | 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. |
218
+ | 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
+ | 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
+ | 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. |
221
+ | Mode | Uses `ctx.mode` to decide whether `/sessions` mounts the board and how a label is derived; nothing joins at start in any mode; `askHuman` is not involved and no status chip is published. |
222
+
223
+ ## Design notes
224
+
225
+ - **Common git dir as the key.** `git rev-parse --git-common-dir` gives all
226
+ worktrees of one repository the same bus; `--show-toplevel` normalizes claim
227
+ paths within each worktree. Non-repository sessions fall back to their cwd.
228
+ - **One path, one claim.** The cwd is canonicalized at join and every claim or
229
+ release is re-keyed by its real target, so a working directory reached through
230
+ a symlink still resolves inside its own repository, a link inside the repository
231
+ and the file it names are the same claim, and a link that leaves the repository
232
+ is refused. The automatic label is the real basename of the cwd, so a session
233
+ started through a link named differently is named after the directory it is
234
+ actually in. An absolute path argument that sits outside the repository is
235
+ refused before it is re-keyed, and a relative one whose target leaves the
236
+ repository is refused by the re-keying itself.
237
+ - **One file per item.** Sessions never append to a shared registry: each session
238
+ writes its own record, each message is its own file, and a claim is a temp file
239
+ hard-linked into place — an exclusive create no reader can see half-written.
240
+ This avoids a cross-process lock for the common path.
241
+ - **Lazy join.** Loading a package extension must not make every subagent and
242
+ WeChat peer a team member. Nothing joins at start, in any mode; a session opts
243
+ in by joining, or by the first action that needs it to be addressable.
244
+ - **Listening is on the record.** `join` writes this session's watch state onto its
245
+ own record, so a peer's `status` read says whether mail will reach it without
246
+ asking it anything. The field is absent when the session is not listening — which
247
+ is also how a record written before the field existed reads, and neither is a
248
+ claim about a peer that has since gone quiet.
249
+ - **Sanitized at the boundary.** Messages are model-authored and are sanitized
250
+ before rendering, like other untrusted text in the transcript.
251
+ - **Poll, not `fs.watch`.** The listening watch reads this session's inbox directory
252
+ with one `readdir` every 2 s, the same trade `watch` makes for a file tail, so
253
+ it does not depend on a platform's event API or a filename in the event. Only
254
+ a listening session polls.
255
+ - **Read the board, prune on the next call.** The open board polls a read-only
256
+ `snapshot()` and `peek()` that hide a session past the TTL, a claim held by one,
257
+ and consume nothing. The physical prune, and the claim release that goes with
258
+ it, happen on the next `status` or `claim` call — which is the call that reader
259
+ is about to make.
260
+ - **Release by the stored path.** A claim is stored relative to the repo root of
261
+ the session that took it, so releasing one resolves nothing: re-resolving it
262
+ against a cwd below that root would target `src/src/api`. The screen refuses a
263
+ peer's claim before asking the store, which checks the owner again.
264
+ - **A pane is a log or prose.** `DetailContents.text` wraps at the render width
265
+ and `tail: false` opens it at the top, so a message reads as prose while a job
266
+ log keeps following its tail.
267
+ - **The notice reuses the shared report channel.** `lib/report.ts` already owns
268
+ whether an outcome may raise a turn; the bus supplies the pending mail and the
269
+ text, and bytefly renders the notice like a `job` or `watch` one.
270
+
271
+ ## Files
272
+
273
+ | File | Responsibility |
274
+ |---|---|
275
+ | `index.ts` | Disable guard, registration, the pending-report channel, and the session hooks. |
276
+ | `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. |
278
+ | `mail-watch.ts` | The armed inbox watch: the poll, the not-yet-reported queue, and its state. |
279
+ | `store.ts` | Atomic JSON files, inbox, peek, claims, pruning, and the one release loop. |
280
+ | `protocol.ts` | Pure labels, targets, paths, validation, conflict rules. |
281
+ | `overlap.ts` | The one "do these paths collide?" predicate: `claimsConflict`, which normalizes both spellings of a claim path first. |
282
+ | `resolve.ts` | `resolveClaimPaths` and `claimPathMessage`: a claim path made repository-relative, posix, and canonical through its real target, or the refusal naming why. |
283
+ | `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`. |
285
+ | `rows.ts` | The session/claim/message row builders and summaries shared by the board and the transcript. |
286
+ | `result.ts` | The `session` tool's expanded result block and its collapsed line. |
287
+ | `notice.ts` | The mail notice's model text and renderer details. |
288
+ | `tools.ts` | The `session` tool and its renderer. |
289
+ | `commands.ts` | `/sessions`. |
290
+ | `format.ts` | Model-facing listing and result text. |
291
+
292
+ ## Testing
293
+
294
+ The capability suites — `format`, `protocol`, `store`, `runtime`, `tui`, `tools`,
295
+ `overlap`, `resolve`, and `extension` — are declared in `test/suite-manifest.ts`.
296
+ They build a bus in a temp directory with a fixed clock, so none needs a live peer; `runtime.test.ts`
297
+ puts its real-repository cases behind `runSlowSuites`, which skips unless `CI` or
298
+ `PI_SLOW_TESTS=1` is set.