@huseynsnmz/pi-board 0.15.3 → 0.16.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 (211) hide show
  1. package/CHANGELOG.md +13 -4
  2. package/CONTRIBUTING.md +7 -0
  3. package/README.md +53 -419
  4. package/SECURITY.md +19 -0
  5. package/docs/how-it-works.md +31 -0
  6. package/docs/reference.md +276 -0
  7. package/package.json +5 -11
  8. package/src/index.ts +1 -1
  9. package/src/runtime/service.mjs +12 -0
  10. package/src/ui/dashboard.ts +14 -8
  11. package/IMPLEMENTATION_PLAN.md +0 -920
  12. package/PRD.md +0 -484
  13. package/PROGRESS.md +0 -142
  14. package/VERIFY.md +0 -115
  15. package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
  16. package/docs/EXPLORATION.md +0 -187
  17. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
  18. package/docs/RELEASE.md +0 -148
  19. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
  20. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
  21. package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
  22. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
  23. package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
  24. package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
  25. package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
  26. package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
  27. package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
  28. package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
  29. package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
  30. package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
  31. package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
  32. package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
  33. package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
  34. package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
  35. package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
  36. package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
  37. package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
  38. package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
  39. package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
  40. package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
  41. package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
  42. package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
  43. package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
  44. package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
  45. package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
  46. package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
  47. package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
  48. package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
  49. package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
  50. package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
  51. package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
  52. package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
  53. package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
  54. package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
  55. package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
  56. package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
  57. package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
  58. package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
  59. package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
  60. package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
  61. package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
  62. package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
  63. package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
  64. package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
  65. package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
  66. package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
  67. package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
  68. package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
  69. package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
  70. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
  71. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
  72. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
  73. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
  74. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
  75. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
  76. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
  77. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
  78. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
  79. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
  80. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
  81. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
  82. package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
  83. package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
  84. package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
  85. package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
  86. package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
  87. package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
  88. package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
  89. package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
  90. package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
  91. package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
  92. package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
  93. package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
  94. package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
  95. package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
  96. package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
  97. package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
  98. package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
  99. package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
  100. package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
  101. package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
  102. package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
  103. package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
  104. package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
  105. package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
  106. package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
  107. package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
  108. package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
  109. package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
  110. package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
  111. package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
  112. package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
  113. package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
  114. package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
  115. package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
  116. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
  117. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
  118. package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
  119. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
  120. package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
  121. package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
  122. package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
  123. package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
  124. package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
  125. package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
  126. package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
  127. package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
  128. package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
  129. package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
  130. package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
  131. package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
  132. package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
  133. package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
  134. package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
  135. package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
  136. package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
  137. package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
  138. package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
  139. package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
  140. package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
  141. package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
  142. package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
  143. package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
  144. package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
  145. package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
  146. package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
  147. package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
  148. package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
  149. package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
  150. package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
  151. package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
  152. package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
  153. package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
  154. package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
  155. package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
  156. package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
  157. package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
  158. package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
  159. package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
  160. package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
  161. package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
  162. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
  163. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
  164. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
  165. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
  166. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
  167. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
  168. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
  169. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
  170. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
  171. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
  172. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
  173. package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
  174. package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
  175. package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
  176. package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
  177. package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
  178. package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
  179. package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
  180. package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
  181. package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
  182. package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
  183. package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
  184. package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
  185. package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
  186. package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
  187. package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
  188. package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
  189. package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
  190. package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
  191. package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
  192. package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
  193. package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
  194. package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
  195. package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
  196. package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
  197. package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
  198. package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
  199. package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
  200. package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
  201. package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
  202. package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
  203. package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
  204. package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
  205. package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
  206. package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
  207. package/scripts/budget-audit.mjs +0 -135
  208. package/scripts/release.mjs +0 -446
  209. package/scripts/release_helper.mjs +0 -309
  210. package/scripts/run-perf-gate.mjs +0 -40
  211. package/scripts/run-tests.mjs +0 -375
package/VERIFY.md DELETED
@@ -1,115 +0,0 @@
1
- # Manual Verification
2
-
3
- Steps you can run yourself to check the extension. Grouped from "no auth needed" to
4
- "needs pi provider auth". Commands assume you're in the repo root.
5
-
6
- ---
7
-
8
- ## 0. Static checks (no auth, fast)
9
-
10
- ```bash
11
- npm install # dev + runtime deps
12
- npm run typecheck # expect: 0 errors
13
- npm run test:perf # expect: `burst:`/`paced:` value lines, pass 3 / skipped 0 — the ONLY path that measures perf assertions; they skip under npm test (issue #121)
14
- npm test # expect: 0 failures (the 3 A11 perf tests skip here, reason points at `npm run test:perf`)
15
- npm run pack:dry # expect: pi-agent-board-<version>.tgz contents only include deploy files
16
- ```
17
-
18
- `npm test` includes a hermetic integration test that runs the **real detached runner** against
19
- a fake pi worker (`test-support/fake-pi.mjs`) — it proves dispatch → events → status/state →
20
- finalize (completed / needs_input / failed / stopped) without any model/network.
21
-
22
- ## 1. Does the extension load in pi? (no auth)
23
-
24
- ```bash
25
- pi --list-models -e "$(pwd)/src/index.ts"
26
- ```
27
- Expect a normal model table and **exit 0** (the extension factory ran without throwing). If you
28
- see an error mentioning `index.ts` / jiti / a missing import, that's a load problem.
29
-
30
- ## 2. CRITICAL: can pi reach a model at all? (needs pi auth)
31
-
32
- This is the thing that was blocked in the dev sandbox. The background worker is just
33
- `pi --mode json -p --session <file> "<prompt>"`, so first confirm a plain one-shot works:
34
-
35
- ```bash
36
- # Should print a JSON event stream that ENDS WITH a line of type "agent_end", then exit
37
- # within a few seconds. Watch for an "assistant" message_end before agent_end.
38
- pi --mode json -p --no-session "Reply with exactly: DONE" | tail -n 20
39
- ```
40
- - ✅ **Healthy:** you see `...message_end (assistant)...`, then `agent_end`, and the command exits.
41
- - ❌ **Hang:** it prints up to a `message_end` for the **user** message then sits idle (no
42
- assistant reply, never exits). That means pi has no working provider auth / network here.
43
- Fix pi's auth first:
44
- ```bash
45
- pi # then run /login inside pi, or set the provider key pi expects, e.g.
46
- # export ANTHROPIC_API_KEY=... (or OPENAI_API_KEY=... for gpt-4o summaries)
47
- ```
48
- Re-run the one-shot above until it ends in `agent_end`. The dashboard cannot show live results
49
- until this works — it's a pi setup step, independent of this extension.
50
-
51
- ## 3. Install the extension for normal use
52
-
53
- For a local checkout, install it as a Pi package:
54
- ```bash
55
- pi install "$(pwd)"
56
- ```
57
- Or symlink the repo into pi's global extensions dir (auto-discovered via the top-level `index.ts`):
58
- ```bash
59
- ln -s "$(pwd)" ~/.pi/agent/extensions/agent-board
60
- ```
61
- After publish, install with:
62
- ```bash
63
- pi install npm:@zhuxixi/pi-agent-board
64
- ```
65
- Then start pi normally:
66
- ```bash
67
- pi
68
- ```
69
-
70
- ## 4. Drive the dashboard
71
-
72
- Inside pi:
73
- 1. Type `/agent-board`, or start with `pi /agent-board` → the full-screen dashboard opens.
74
- - With `pi /agent-board`, quitting the dashboard exits Pi instead of dropping you into a normal chat session.
75
- - The startup path should feel cleaner than `/agent-board`: no normal Pi header/footer chrome and no dispatch notifications above the dashboard.
76
- 2. Press **i** to enter INSERT mode, then type a task in the bottom input (e.g. `list the files in this repo and summarize the README`) and press **Enter**.
77
- - A **Start session** dialog opens with **Start session** focused by default.
78
- - Press **Enter** again to launch immediately, or move with **↑/↓** to change **cwd**, **model**, or **thinking** first.
79
- - With automatic Done disabled by default, a successful run settles in **Needs instructions** until you review it and press **d** to confirm **Done**. Set `AGENT_BOARD_AUTO_STATE_NO_DONE=0` to restore automatic Done classification for this verification path.
80
- 3. **space** = peek when the input is empty (summary, blocker, latest output); in peek **r** = reply, **a** = attach.
81
- **→** / **>** = open a full-screen live session view without interrupting; **←** / **<** returns.
82
- 4. **enter** on an empty input = attach to the selected full session (confirms first if it's still running).
83
- You're now in the real Pi session; run `/agent-board` again to return.
84
- 5. Other keys: **/** filter (`s:running`, or free text), **Ctrl+R** rename, **Ctrl+T** pin, **Ctrl+S** stop,
85
- **x** then **y** delete (confirm prompt; archives the row, keeps the session file), **Ctrl+X** ×2 quick delete (no confirm),
86
- **?** help, **Esc** clears input / quits when empty.
87
-
88
- ## 5. Inspect the durable store on disk
89
-
90
- ```bash
91
- ls -R ~/.pi/agent/agent-board
92
- cat ~/.pi/agent/agent-board/roster.json
93
- cat ~/.pi/agent/agent-board/views/*/state.json
94
- cat ~/.pi/agent/agent-board/views/*/runs/*/status.json
95
- # raw worker event stream for a run:
96
- cat ~/.pi/agent/agent-board/views/*/runs/*/events.jsonl
97
- ```
98
-
99
- ## 6. Recovery / persistence
100
-
101
- - Start a dispatch, then quit pi (or `/reload`). Re-open pi and `/agent-board` — the row is still
102
- there with its last state (rehydrated from disk). If a run's runner died without finishing,
103
- the dashboard reconciles it to `failed (runner exited)` on open.
104
-
105
- ## 7. Same-repo session launches
106
-
107
- - Dispatch one task in a git repo, then (while it's running) dispatch a second in the
108
- **same repo**. Both launches should be allowed. The extension never creates a git worktree on
109
- its own.
110
-
111
- ## 8. Summary model
112
-
113
- Default summary model is **gpt-4o** (override `AGENT_BOARD_SUMMARY_MODEL=<model>`, disable with
114
- `AGENT_BOARD_SUMMARY_MODEL=off`; legacy `AGENT_VIEW_SUMMARY_MODEL` is also honored). It needs OpenAI auth; without it the row keeps its heuristic
115
- summary (e.g. the first sentence of the agent's last message or the active tool).
@@ -1,277 +0,0 @@
1
- # Batch Selection + Read/Unread Flow
2
-
3
- **Status:** Proposed
4
- **Context:** Pi Agent Board dashboard
5
- **Created:** 2026-06-02
6
-
7
- ## Assumption
8
-
9
- "Move to done to delete that batch" is treated as a **safe 2-step cleanup flow**:
10
-
11
- 1. bulk-select sessions,
12
- 2. move them to **Done**,
13
- 3. optionally delete that same batch from **Done** with a second explicit confirmation.
14
-
15
- This keeps "done" non-destructive and makes deletion intentional.
16
-
17
- ---
18
-
19
- ## 1. Goals
20
-
21
- ### A. Multi-session selection
22
- - Let users select multiple rows from the board.
23
- - Let users batch-mark them as **Done**.
24
- - Let users immediately clean up that same batch from the **Done** group.
25
-
26
- ### B. Read / Unread
27
- - Mark a session as **Read** once the user has actually visited it via attach / transcript-style deep view.
28
- - Show a **subtle unread hint** on rows with unseen activity.
29
- - If new activity happens after a visit, the row becomes **Unread** again.
30
-
31
- ---
32
-
33
- ## 2. UX overview
34
-
35
- ### Normal mode
36
- - Board behaves exactly like today: single active row, peek, attach, done, delete.
37
-
38
- ### Selection mode
39
- - User enters a temporary **multi-select mode**.
40
- - Selection count appears in header/footer: `3 selected`.
41
- - Rows show a subtle selected marker.
42
- - Actions become batch-oriented:
43
- - **Mark selected done**
44
- - **Delete selected** (only from Done, with confirm)
45
- - **Clear selection**
46
-
47
- ### Read state
48
- - Rows can be either:
49
- - **Unread** = new activity since last visit
50
- - **Read** = user has already visited the latest activity
51
- - Unread should not overpower semantic state; it is a secondary signal.
52
-
53
- ---
54
-
55
- ## 3. Multi-session selection flow
56
-
57
- ## 3.1 Enter selection mode
58
-
59
- **Trigger**
60
- - User presses a dedicated shortcut from board list view.
61
- - Suggested shortcut: `m` for **multi-select**.
62
-
63
- **Result**
64
- - Footer changes to selection hints.
65
- - Selection starts empty; the current row stays focused but is **not** auto-selected.
66
- - Header shows selection count.
67
-
68
- ---
69
-
70
- ## 3.2 Build the selection
71
-
72
- While in selection mode:
73
- - `↑ / ↓` moves cursor
74
- - `space` toggles current row in selection
75
- - `a` selects all visible rows in current filter/group
76
- - `u` clears the current selection without leaving selection mode
77
- - `esc` clears selection and exits selection mode
78
-
79
- Optional later:
80
- - `A` = select all rows in current state group
81
- - `u` = unselect all
82
-
83
- ---
84
-
85
- ## 3.3 Move selected rows to Done
86
-
87
- **Trigger**
88
- - User presses `d` while in selection mode.
89
-
90
- **Confirmation copy**
91
- - `Mark 5 selected sessions as done? (y/N)`
92
-
93
- **Rules**
94
- - Running/live rows are skipped.
95
- - Needs-input / idle / failed / stopped rows may be moved to Done.
96
- - Confirmation should mention skipped rows if any.
97
-
98
- **Result**
99
- - Matching rows are moved to `Done`.
100
- - The same rows remain selected.
101
- - Board auto-scrolls/focuses to the `Done` section.
102
- - Toast example: `Moved 5 to Done · skipped 2 live`
103
-
104
- This preserves the batch as a temporary working set for cleanup.
105
-
106
- ---
107
-
108
- ## 3.4 Delete the same batch from Done
109
-
110
- **Trigger**
111
- - With the same batch still selected, user presses `ctrl+x` or a batch delete key.
112
-
113
- **Guardrail**
114
- - Deletion is only enabled when all selected rows are already in `Done`.
115
- - If selection includes non-Done rows, show: `Only Done sessions can be batch deleted`.
116
-
117
- **Confirmation copy**
118
- - `Delete 5 done sessions? Session files are preserved. (y/N)`
119
-
120
- **Result**
121
- - Selected rows are archived from dashboard.
122
- - Selection clears.
123
- - Toast example: `Deleted 5 done sessions`
124
-
125
- ---
126
-
127
- ## 4. Read / Unread flow
128
-
129
- ## 4.1 What counts as Read
130
-
131
- A session becomes **Read** when the user intentionally opens that session in a deeper way:
132
- - attach via `enter` / `→`
133
- - transcript/full-session view
134
-
135
- **Not read by default:**
136
- - merely highlighting a row
137
- - passive polling updates
138
- - quick list navigation
139
- - optional: peek can remain non-read to preserve signal
140
-
141
- ---
142
-
143
- ## 4.2 When a row becomes Unread again
144
-
145
- A previously read row becomes **Unread** when a new assistant reply lands after the last visit, for example:
146
- - new assistant message
147
- - new blocker/question raised in that message
148
- - session re-enters `needs_input` because of that message
149
-
150
- User-authored actions alone should not create unread state, and intermediate tool churn alone should not flip unread.
151
-
152
- ---
153
-
154
- ## 4.3 Recommended subtle hint
155
-
156
- Use the **stage icon itself** as the unread signal:
157
- - **Unread:** stronger/heavier variant of that stage icon
158
- - **Read:** lighter/default variant of that stage icon
159
-
160
- Examples:
161
- - queued: `○` → `◎`
162
- - needs input: `◇` → `◆`
163
- - done: `✓` → `✔`
164
-
165
- Optional secondary treatment:
166
- - unread row title slightly brighter
167
- - read row title normal
168
-
169
- Avoid:
170
- - loud badges like `UNREAD`
171
- - full-row highlight
172
- - strong color conflict with semantic states like Failed / Needs input
173
-
174
- ### Priority of signals
175
- 1. semantic state (Running, Needs input, Done, Failed)
176
- 2. selection state
177
- 3. unread hint
178
-
179
- Unread should help scanning, not dominate the board.
180
-
181
- ---
182
-
183
- ## 5. State model
184
-
185
- ## 5.1 Batch selection
186
-
187
- Selection can stay **ephemeral UI state** in the dashboard component:
188
- - `selectedIds: Set<string>`
189
- - `selectionMode: boolean`
190
-
191
- No persistence needed for V1.
192
-
193
- ## 5.2 Read state
194
-
195
- Recommended durable fields:
196
- - `lastVisitedAt: number | null`
197
- - `lastAgentActivityAt: number | null`
198
-
199
- Derived flag:
200
- - `isUnread = lastAgentActivityAt > lastVisitedAt`
201
-
202
- This is simpler and more reliable than storing a raw boolean.
203
-
204
- ---
205
-
206
- ## 6. Edge cases
207
-
208
- ### Live rows inside a batch
209
- - Cannot be batch-done or batch-deleted.
210
- - Skip and report counts.
211
-
212
- ### Mixed-state selection
213
- - Batch done: allowed for inactive non-done rows.
214
- - Batch delete: only allowed if every selected row is already Done.
215
-
216
- ### New activity during selection
217
- - Keep row selected.
218
- - If it receives new output, unread hint can appear even while selected.
219
-
220
- ### Re-attaching to a row
221
- - Refresh `lastVisitedAt`.
222
- - Any prior unread marker clears.
223
-
224
- ### Peek behavior
225
- - Recommend: peek does **not** mark read.
226
- - Reason: peek is triage, attach/transcript is actual visit.
227
-
228
- ---
229
-
230
- ## 7. Suggested implementation slices
231
-
232
- ### Dashboard (`src/ui/dashboard.ts`)
233
- - add selection mode
234
- - add `selectedIds`
235
- - batch confirm flows
236
- - render selected marker + unread marker
237
-
238
- ### Service (`src/runtime/service.mjs`)
239
- - add `markCompletedMany(viewIds)`
240
- - add `archiveMany(viewIds)`
241
- - add `markVisited(viewId)`
242
-
243
- ### Store/types (`src/core/types.mjs`, `src/core/store.mjs`)
244
- - add read-tracking timestamps
245
- - expose unread derivation on rows
246
-
247
- ### Attach / session view entry points
248
- - mark row visited when attach or transcript view opens
249
-
250
- ---
251
-
252
- ## 8. Recommended default interaction summary
253
-
254
- ### Batch
255
- - `m` enter multi-select
256
- - `space` toggle row
257
- - `a` select all visible
258
- - `u` clear selection
259
- - `d` move selected to Done
260
- - `ctrl+x` delete selected Done batch
261
- - `esc` clear/exit
262
-
263
- ### Read state
264
- - attach / transcript => mark read
265
- - new assistant reply => unread again
266
- - unread indicator => subtle left dot/bar
267
-
268
- ---
269
-
270
- ## 9. Acceptance criteria
271
-
272
- - User can select multiple sessions from the board.
273
- - User can mark that selection Done in one action.
274
- - User can immediately delete that same batch from Done with explicit confirmation.
275
- - A session becomes Read after attach/transcript visit.
276
- - New agent activity turns a previously read row back to Unread.
277
- - Unread is visible through a subtle row hint, not a loud badge.
@@ -1,187 +0,0 @@
1
- # Exploration & Decisions — Pi Agent Board Extension
2
-
3
- **For future agents.** This is the distilled gist of the codebase recon done before
4
- implementing `IMPLEMENTATION_PLAN.md`. Read this first; it saves re-deriving the Pi API.
5
-
6
- Runtime target: `/opt/homebrew/lib/node_modules/@earendil-works/pi-coding-agent` (v0.75.5).
7
- `pi` binary is on PATH at `/opt/homebrew/bin/pi` → `dist/cli.js`.
8
-
9
- ---
10
-
11
- ## 1. How a Pi extension is loaded & shaped
12
-
13
- - An extension is a TS module: `export default function (pi: ExtensionAPI) { ... }`
14
- (may be `async`; Pi awaits it before `session_start`).
15
- - Auto-discovered from `~/.pi/agent/extensions/*.ts` or `<name>/index.ts` (global) and
16
- `.pi/extensions/...` (project). `/reload` hot-reloads them. `pi -e ./path.ts` for quick tests.
17
- - Pi's loader (jiti) resolves `@earendil-works/pi-coding-agent`, `@earendil-works/pi-ai`,
18
- `@earendil-works/pi-tui`, `typebox` **for you at runtime** — you do not bundle them.
19
- For local typechecking we path-map those to the global install (see `tsconfig.json`).
20
- - Loaded TS runs via jiti. **Detached external processes (our runner) must NOT rely on
21
- jiti** → the runner is a plain `.mjs` that we spawn with `node`.
22
-
23
- ## 2. ExtensionAPI surface we actually use
24
-
25
- - `pi.registerCommand(name, { description, handler(args, ctx), getArgumentCompletions? })`
26
- — handler `ctx` is `ExtensionCommandContext` (adds `newSession/fork/switchSession/
27
- navigateTree/reload/waitForIdle`). Session-switch methods deadlock from event handlers,
28
- so they exist only on command ctx.
29
- - `pi.registerShortcut(KeyId, { description, handler(ctx) })`.
30
- - `pi.registerFlag(name, { description, type:"boolean"|"string", default })` + `pi.getFlag`.
31
- - `pi.exec(cmd, args, { signal, timeout })` → `{ stdout, stderr, code, killed }`.
32
- - `pi.appendEntry(customType, data)` persists extension state into the session JSONL
33
- (custom entry, not sent to LLM). Restore by scanning `ctx.sessionManager.getEntries()`.
34
- - `ctx.ui.*`: `select/confirm/input/editor/notify`, `setStatus/setWidget/setFooter`,
35
- and **`custom<T>(factory, opts?) => Promise<T>`** (the dashboard surface).
36
- - `ctx.switchSession(path, { withSession })` — attach = switch the interactive session to
37
- a managed session file. `withSession(ctx)` runs **after** the old runtime is torn down;
38
- only use the fresh `ctx` passed in, never captured old `pi`/`ctx`/`sessionManager`.
39
-
40
- ### `ctx.ui.custom` (the important one)
41
- ```ts
42
- const result = await ctx.ui.custom<T>((tui, theme, keybindings, done) => {
43
- // return a Component: { render(width): string[]; invalidate(); handleInput?(data) }
44
- // call tui.requestRender() after state changes to repaint
45
- // call done(value) to close the surface and resolve the promise
46
- });
47
- ```
48
- - It **replaces the editor** with your component until `done()` (full-screen-ish surface).
49
- - It returns a `Promise`, NOT a handle. Live updates: hold a `tui` ref, call
50
- `tui.requestRender()` from a `setInterval`/file-watch/poll loop. Terminal height =
51
- `tui.terminal.rows`, width comes in as the `render(width)` arg.
52
- - `{ overlay: true, overlayOptions }` renders a floating modal instead (used for peek/dialogs
53
- if we want; MVP keeps peek as an internal mode of the dashboard component).
54
-
55
- ## 3. TUI building blocks (`@earendil-works/pi-tui`)
56
-
57
- Components implement `{ render(width:number):string[]; invalidate():void; handleInput?(data):void }`
58
- — each rendered line **must not exceed `width`** (use `truncateToWidth`).
59
- Exports we use: `Text, Box, Container, Spacer, Markdown, SelectList, SettingsList,
60
- matchesKey, Key, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component`.
61
- From coding-agent: `DynamicBorder, BorderedLoader, getMarkdownTheme, getSettingsListTheme,
62
- keyHint`. Theme: `theme.fg(color, text)`, `theme.bold/italic/strikethrough`.
63
- Key input: `matchesKey(data, Key.up)` etc. After state change in `handleInput`, call
64
- `tui.requestRender()`.
65
-
66
- ## 4. Sessions & the headless worker (the core mechanism)
67
-
68
- - Session files are JSONL trees (`id`/`parentId`); first line is a header
69
- `{"type":"session","version":3,"id":"<uuid>","timestamp":"<iso>","cwd":"<cwd>"}`.
70
- - **Launching a managed run:**
71
- `pi --mode json -p --session <file> "<prompt>"`, spawned with `cwd: <repo|worktree>`.
72
- - `--session <path>` with a path that contains `/` or ends `.jsonl` → `SessionManager.open(path)`.
73
- If the file **doesn't exist**, Pi creates a fresh session and persists to that **exact path**
74
- (verified in `dist/core/session-manager.ts` `open()` → `setSessionFile` else-branch).
75
- The header `cwd` becomes the spawn `cwd`, so always set `cwd` correctly.
76
- - `--mode json` = one-shot: it `session.subscribe((e)=>stdout(JSON.stringify(e)+"\n"))`,
77
- runs the single prompt to completion (a full agent loop, many turns/tools), then exits
78
- **with `agent_end` as the last event** — `worker.on("close")` is the run-finished signal.
79
- - **⚠️ AUTH/NETWORK CAVEAT (verified 2026-05-30):** the one-shot exits cleanly *only when the
80
- worker can actually reach the model provider*. With no/blocked pi provider auth (or a
81
- network/proxy stall), `pi --mode json -p` records `session → agent_start → turn_start →
82
- message_start(user) → message_end(user)` and then **blocks indefinitely at the provider
83
- request** (idle, ~3% CPU) — **no assistant reply, no `agent_end`, no process exit**. This is
84
- a pi/runtime condition, NOT an extension bug. The runner handles it correctly: the row stays
85
- `working/alive` until the user stops it (`s` in the dashboard → SIGTERM → finalized `stopped`).
86
- Note: pi's own provider auth is separate from Claude Code's `/login`. Validate with
87
- `pi --mode json -p --no-session "say hi"` — it must print events ending in `agent_end` and exit.
88
- - `-p` forces non-interactive. `--model <m>`, `--tools a,b`, `--append-system-prompt <file>`
89
- are available. `--no-session` = ephemeral (we do NOT use it for managed rows; we want persistence).
90
- - Reply / resume = launch another run against the **same** `--session <file>`; Pi appends to it.
91
-
92
- ### JSON event vocabulary actually emitted (parse these in the runner)
93
- First line: the session header `{type:"session",...}`. Then per `AgentSessionEvent`:
94
- `agent_start`, `agent_end`, `turn_start`, `turn_end`,
95
- `message_start` / `message_update` / `message_end` (each has `.message`: an AgentMessage),
96
- `tool_execution_start` `{toolCallId,toolName,args}`,
97
- `tool_execution_update` `{...,partialResult}`,
98
- `tool_execution_end` `{toolCallId,toolName,result,isError}`,
99
- `tool_call`, `tool_result`, plus `queue_update`, `compaction_*`, `auto_retry_*`,
100
- `model_select`, `thinking_level_*`, `session_*`.
101
- > NOTE: the subagent example reads `tool_result_end` — that string is **never emitted** (dead
102
- > defensive code). Use `message_end` (role assistant → text/toolCalls/usage/stopReason/errorMessage)
103
- > and `tool_execution_start/end` for live state. `agent_end` ⇒ run finished.
104
-
105
- AssistantMessage shape: `{ role:"assistant", content:(TextContent|ThinkingContent|ToolCall)[],
106
- model, usage, stopReason:"stop"|"length"|"toolUse"|"error"|"aborted", errorMessage? }`.
107
- ToolCall content block: `{ type:"toolCall", id, name, arguments }`.
108
-
109
- ## 5. Architecture chosen (matches plan §3.3) — file-backed detached runner
110
-
111
- ```
112
- Pi extension (parent, interactive) runner/job-runner.mjs (detached, 1 per run)
113
- /agent-board dashboard (ctx.ui.custom) ─► spawns: pi --mode json -p --session <file> <prompt>
114
- store reader/writer parses JSON event lines from worker stdout
115
- launches runner via child_process writes runs/<runId>/{status.json,events.jsonl,
116
- polls store files to repaint stdout.log,stderr.log,pid.json}
117
- updates views/<id>/state.json on progress/exit
118
- ```
119
- Why detached `.mjs` runner (not spawn pi directly from the extension, not a daemon):
120
- survives parent `/reload`/exit, durable status, no socket/daemon complexity. Each run is the
121
- durable monitor for one worker.
122
-
123
- ### Store layout (user-scoped) — `~/.pi/agent/agent-board/`
124
- ```
125
- roster.json { version, views: [viewId...] }
126
- views/<viewId>/meta.json stable: id,name,cwd,sessionFile,createdAt,pinned,kind,
127
- worktreeMode,worktreePath,defaultModel,source,archived
128
- views/<viewId>/state.json derived: currentRunId,semanticState,processState,summary,
129
- needsInput,hasError,latestAssistantPreview,latestTool,lastActivityAt
130
- views/<viewId>/runs/<runId>/status.json per-run durable snapshot (pid,startedAt,endedAt,
131
- exitCode,semanticState,currentTool,latestAssistantPreview,question,error)
132
- views/<viewId>/runs/<runId>/{events.jsonl,stdout.log,stderr.log,pid.json}
133
- sessions/<viewId>.jsonl the managed Pi session file (Pi creates/persists it)
134
- ```
135
- - One row = one real Pi session file. One run = one detached worker against it.
136
- - Both `state.json` (row, long-lived) and `status.json` (run, single attempt) exist so
137
- reply/resume/attach stay clean.
138
- - Atomic writes: write tmp + `rename`. Liveness: `pid.json` + `process.kill(pid,0)`.
139
-
140
- ## 6. State derivation rules (plan §9)
141
-
142
- Semantic: `queued`→`working` (alive, once assistant/tool activity); on clean exit
143
- `needs_input` (assistant ends asking) | `completed` | `idle`; on bad exit `failed`;
144
- user-stopped `stopped`. Process: `alive` if pid live else `exited`.
145
- Summary priority: model-summary(opt-in) → active tool (`Editing x`,`Running tests`) →
146
- blocker/question → first sentence of latest assistant → stderr/error → fallback status.
147
- Needs-input heuristic: assistant text ends in `?` or contains `need your input`/
148
- `which option`/`should I`/`please confirm`.
149
-
150
- ## 7. Safety / worktree (plan §11)
151
-
152
- Same-repo parallel **writers** require git worktree isolation (locked decision). MVP:
153
- explicit `worktreeMode:"worktree"` per dispatch → `git worktree add` under
154
- `~/.pi/agent/agent-board/worktrees/<viewId>`; persist `worktreePath`; block a 2nd active
155
- writer in the same repo if not isolated. Delete row ≠ delete data: archive row, keep
156
- session file; worktree removal needs explicit confirm.
157
-
158
- ## 8. Decisions made for this implementation
159
-
160
- 1. **Runner is `runner/job-runner.mjs` (plain ESM)**, spawned with `node`. It imports only
161
- `node:*`. The pi binary it spawns is passed in via run-config (`piBin`, default `"pi"`).
162
- 2. **Extension code is TS**, loaded by Pi via jiti. We add `package.json` + `tsconfig.json`
163
- only for local typecheck/tests (path-mapped to the global pi install); not needed at runtime.
164
- 3. **Summaries: cheap model is the DEFAULT** (`DEFAULT_SUMMARY_MODEL = "gpt-4o"` in the runner;
165
- user choice 2026-05-30), with the **heuristic kept as a graceful fallback** (no API key /
166
- offline / timeout) and still used for live state detection (needs-input, semantic state).
167
- Override with `AGENT_BOARD_SUMMARY_MODEL=<model>`, disable with `=off`. The summary call has a
168
- 15s safety timeout and runs *after* the heuristic terminal state is already persisted, so a
169
- slow/unreachable summary model never stalls the dashboard (row flips to final state at once,
170
- summary upgrades a few seconds later). gpt-4o needs OpenAI auth; without it → heuristic.
171
- 4. **Dashboard = single `ctx.ui.custom` component** owning list + peek + dispatch input as
172
- internal modes (plan §10.4 recommendation). Live updates via a poll loop calling
173
- `tui.requestRender()` (store files are the source of truth; cheap to re-read).
174
- 5. **Attach = `ctx.switchSession(sessionFile, { withSession })`** from the `/agent-board` command
175
- handler. If the row's run is alive → confirm interrupt (stop run) then attach (locked decision).
176
- 6. **Verification without a TTY/API key:** unit tests for all pure logic + an integration test
177
- that runs the real `.mjs` runner against a **fake `pi`** stub script emitting canned JSON
178
- event lines, asserting the durable store artifacts. (`test-support/fake-pi.mjs`.)
179
-
180
- ## 9. Gotchas
181
-
182
- - `ctx.ui.custom` callback's component must return lines ≤ width; always `truncateToWidth`.
183
- - Rebuild themed content on `invalidate()` (theme change) if you pre-bake `theme.fg(...)`.
184
- - Don't import `pi-subagents/src/...` internals (unstable) — reference only.
185
- - Session-switch `withSession` runs post-teardown; capture only plain strings/ids.
186
- - The runner must update `status.json` atomically and frequently but throttled (avoid fs thrash).
187
- - `process.kill(pid, 0)` throws ESRCH if dead, EPERM if alive-but-not-ours → treat EPERM as alive.