@huseynsnmz/pi-board 0.15.1 → 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 (215) hide show
  1. package/CHANGELOG.md +26 -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 +18 -19
  8. package/src/index.ts +9 -1
  9. package/src/runtime/service.mjs +12 -0
  10. package/src/ui/dashboard.ts +14 -8
  11. package/src/ui/double-left.ts +5 -1
  12. package/src/ui/focus.ts +7 -1
  13. package/src/ui/waiting.ts +10 -5
  14. package/IMPLEMENTATION_PLAN.md +0 -920
  15. package/PRD.md +0 -484
  16. package/PROGRESS.md +0 -142
  17. package/VERIFY.md +0 -115
  18. package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
  19. package/docs/EXPLORATION.md +0 -187
  20. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
  21. package/docs/RELEASE.md +0 -148
  22. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
  23. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
  24. package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
  25. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
  26. package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
  27. package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
  28. package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
  29. package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
  30. package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
  31. package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
  32. package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
  33. package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
  34. package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
  35. package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
  36. package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
  37. package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
  38. package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
  39. package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
  40. package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
  41. package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
  42. package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
  43. package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
  44. package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
  45. package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
  46. package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
  47. package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
  48. package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
  49. package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
  50. package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
  51. package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
  52. package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
  53. package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
  54. package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
  55. package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
  56. package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
  57. package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
  58. package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
  59. package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
  60. package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
  61. package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
  62. package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
  63. package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
  64. package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
  65. package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
  66. package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
  67. package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
  68. package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
  69. package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
  70. package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
  71. package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
  72. package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
  73. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
  74. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
  75. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
  76. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
  77. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
  78. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
  79. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
  80. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
  81. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
  82. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
  83. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
  84. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
  85. package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
  86. package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
  87. package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
  88. package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
  89. package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
  90. package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
  91. package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
  92. package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
  93. package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
  94. package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
  95. package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
  96. package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
  97. package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
  98. package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
  99. package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
  100. package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
  101. package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
  102. package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
  103. package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
  104. package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
  105. package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
  106. package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
  107. package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
  108. package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
  109. package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
  110. package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
  111. package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
  112. package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
  113. package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
  114. package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
  115. package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
  116. package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
  117. package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
  118. package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
  119. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
  120. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
  121. package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
  122. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
  123. package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
  124. package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
  125. package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
  126. package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
  127. package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
  128. package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
  129. package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
  130. package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
  131. package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
  132. package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
  133. package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
  134. package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
  135. package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
  136. package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
  137. package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
  138. package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
  139. package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
  140. package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
  141. package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
  142. package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
  143. package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
  144. package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
  145. package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
  146. package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
  147. package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
  148. package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
  149. package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
  150. package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
  151. package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
  152. package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
  153. package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
  154. package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
  155. package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
  156. package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
  157. package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
  158. package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
  159. package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
  160. package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
  161. package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
  162. package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
  163. package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
  164. package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
  165. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
  166. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
  167. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
  168. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
  169. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
  170. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
  171. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
  172. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
  173. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
  174. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
  175. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
  176. package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
  177. package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
  178. package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
  179. package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
  180. package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
  181. package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
  182. package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
  183. package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
  184. package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
  185. package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
  186. package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
  187. package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
  188. package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
  189. package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
  190. package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
  191. package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
  192. package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
  193. package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
  194. package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
  195. package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
  196. package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
  197. package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
  198. package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
  199. package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
  200. package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
  201. package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
  202. package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
  203. package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
  204. package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
  205. package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
  206. package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
  207. package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
  208. package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
  209. package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
  210. package/scripts/budget-audit.mjs +0 -135
  211. package/scripts/patch-vulns.mjs +0 -59
  212. package/scripts/release.mjs +0 -446
  213. package/scripts/release_helper.mjs +0 -309
  214. package/scripts/run-perf-gate.mjs +0 -40
  215. package/scripts/run-tests.mjs +0 -375
@@ -1,581 +0,0 @@
1
- # Implementation Plan: PTY-backed Live Attach
2
-
3
- **Status:** Proposed / spike-ready
4
- **Goal:** make attaching to a live agent-board row feel like attaching to the same normal Pi session, without interrupting the running work.
5
- **Created:** 2026-05-31
6
-
7
- ## 1. Decision
8
-
9
- The previous `SessionClient`/RPC idea is clean, but it will not be 1:1 with normal Pi unless we reimplement or refactor a lot of interactive Pi UI behavior.
10
-
11
- For a 1:1 user experience, invert the design:
12
-
13
- > A background row should be hosted by a real interactive Pi process running inside a PTY. Attach should connect the user to that live PTY, not recreate Pi UI over RPC.
14
-
15
- Current MVP keeps a detached one-shot JSON worker:
16
-
17
- ```text
18
- Dashboard
19
- └─ job-runner.mjs
20
- └─ pi --mode json -p --session <file> <prompt>
21
- ├─ emits JSON events
22
- ├─ writes session file
23
- └─ exits after the prompt finishes
24
- ```
25
-
26
- Target live-attach model:
27
-
28
- ```text
29
- Dashboard
30
- └─ pty-runner.mjs
31
- ├─ control.sock
32
- ├─ host.json
33
- └─ PTY
34
- └─ pi --session <file> <initial prompt>
35
- ├─ real interactive Pi TUI
36
- ├─ real slash commands/keybindings/extensions
37
- ├─ writes same session file
38
- └─ stays alive until explicitly closed
39
- ```
40
-
41
- ## 2. Critical exploration findings
42
-
43
- ### Existing code that helps
44
-
45
- - `src/runtime/service.mjs` already owns dispatch/reply/stop/archive and same-repo worktree safety.
46
- - `runner/job-runner.mjs` already demonstrates detached durable process ownership.
47
- - Store layout under `~/.pi/agent/agent-board/` is sound: `views/<id>/meta.json`, `state.json`, per-run artifacts, session file per row.
48
- - `src/index.ts` already mirrors foreground session events into managed row state via `service.syncForegroundEvent(...)`.
49
- - `src/commands/agent-board.ts` already owns attach and back-to-dashboard behavior.
50
- - `src/ui/dashboard.ts` already has list/peek/reply/session modes and can add a new attach mode/result.
51
-
52
- ### Important mismatch in current state model
53
-
54
- `Row.alive` currently means “the current managed run pid is alive” and is also overloaded for foreground mirrored activity.
55
-
56
- For PTY hosting we need two separate concepts:
57
-
58
- 1. **host liveness** — is the interactive Pi PTY process alive and attachable?
59
- 2. **agent activity** — is the agent currently processing/streaming/tooling?
60
-
61
- A PTY-hosted row can be:
62
-
63
- ```text
64
- host: alive
65
- agent activity: idle/completed/needs_input
66
- ```
67
-
68
- That should still be directly attachable without fallback resume.
69
-
70
- ### Biggest technical caveat
71
-
72
- Pi extension `ctx.ui.custom()` renders line-based components. It does **not** expose a documented raw terminal takeover API.
73
-
74
- Therefore there are three implementation options:
75
-
76
- | Option | Description | 1:1 fidelity | Fits extension-only? | Recommendation |
77
- |---|---|---:|---:|---|
78
- | A | PTY + terminal emulator component (`xterm-headless` style) | high, not perfect | yes | try first |
79
- | B | Pi core raw terminal takeover API | highest | requires Pi core change | later if needed |
80
- | C | standalone/tmux-like external agent-board CLI | highest | separate CLI, not pure `/agent-board` | fallback |
81
-
82
- This plan attempts **Option A** first because it preserves the extension flow and avoids rebuilding the agent protocol/UI. It runs real Pi, captures its PTY output, and renders a virtual terminal buffer inside `ctx.ui.custom()`.
83
-
84
- ## 3. Target architecture
85
-
86
- ```text
87
- ┌──────────────────────────────┐
88
- │ Parent Pi / /agent-board dashboard │
89
- └──────────────┬───────────────┘
90
- │ dispatch
91
- ▼
92
- ┌────────────────────────────────────────────────────────────┐
93
- │ pty-runner.mjs │
94
- │ │
95
- │ host.json durable host status │
96
- │ control.sock IPC for attach/input/resize/stop │
97
- │ screen.log optional raw PTY output log │
98
- │ │
99
- │ ┌──────────────────────────────────────────────────────┐ │
100
- │ │ PTY │ │
101
- │ │ └─ pi --session <managed.jsonl> <initial prompt> │ │
102
- │ │ └─ real Pi interactive TUI │ │
103
- │ └──────────────────────────────────────────────────────┘ │
104
- └────────────────────────────────────────────────────────────┘
105
- ▲
106
- │ child extension events write row state
107
- │ AGENT_BOARD_ROOT / AGENT_BOARD_VIEW_ID
108
- │
109
- ┌──────────────┴───────────────┐
110
- │ agent-board extension in child │
111
- │ mirrors agent/tool/message │
112
- │ events to state.json │
113
- └──────────────────────────────┘
114
- ```
115
-
116
- Attach path:
117
-
118
- ```text
119
- User presses Enter on live row
120
- ├─ if host socket alive:
121
- │ open PtyAttachComponent
122
- │ connect to control.sock
123
- │ replay PTY snapshot/log into virtual terminal
124
- │ forward keyboard input to PTY
125
- │ intercept detach chord only
126
- │
127
- └─ else:
128
- fallback to existing ctx.switchSession(sessionFile)
129
- ```
130
-
131
- ## 4. Store/data model changes
132
-
133
- ### 4.1 Add host status file
134
-
135
- New path helpers in `src/core/paths.mjs`:
136
-
137
- ```js
138
- hostPath(root, viewId) // views/<viewId>/host.json
139
- controlSocketPath(root,id) // views/<viewId>/control.sock
140
- screenLogPath(root,id) // views/<viewId>/screen.log
141
- ```
142
-
143
- New shape in `src/core/types.mjs`:
144
-
145
- ```ts
146
- type HostMode = "json-runner" | "pty";
147
- type HostState = "starting" | "alive" | "exited" | "failed";
148
-
149
- interface HostStatus {
150
- version: 1;
151
- viewId: string;
152
- mode: HostMode;
153
- runnerPid: number | null;
154
- childPid: number | null;
155
- socketPath: string | null;
156
- state: HostState;
157
- startedAt: number;
158
- lastSeenAt: number;
159
- endedAt: number | null;
160
- exitCode: number | null;
161
- error: string | null;
162
- cols: number;
163
- rows: number;
164
- attachedClients: number;
165
- }
166
- ```
167
-
168
- ### 4.2 Extend `Row`
169
-
170
- In `src/core/store.mjs`, `loadRow()` should return:
171
-
172
- ```ts
173
- interface Row {
174
- meta: ViewMeta;
175
- state: ViewState | null;
176
- alive: boolean; // current agent activity/run alive, legacy-compatible
177
- hostAlive: boolean; // PTY host/socket alive and attachable
178
- host: HostStatus | null;
179
- }
180
- ```
181
-
182
- Dashboard row labels should distinguish:
183
-
184
- ```text
185
- ● working agent active
186
- ◌ hosted Pi process alive but idle
187
- ✓ completed completed, host may still be alive
188
- ```
189
-
190
- ## 5. Runner changes
191
-
192
- ### 5.1 Keep current JSON runner as fallback
193
-
194
- Do not delete `runner/job-runner.mjs`. Keep it for:
195
-
196
- - tests,
197
- - non-PTY fallback,
198
- - environments where `node-pty` cannot install,
199
- - comparison/debugging.
200
-
201
- ### 5.2 Add `runner/pty-runner.mjs`
202
-
203
- Responsibilities:
204
-
205
- 1. read `HostConfig` from JSON file,
206
- 2. create/remove stale socket,
207
- 3. spawn real interactive Pi in a PTY,
208
- 4. write `host.json`,
209
- 5. expose control socket,
210
- 6. append raw PTY output to `screen.log`,
211
- 7. broadcast output to attached clients,
212
- 8. accept input/resize/stop commands,
213
- 9. finalize host status on exit.
214
-
215
- Proposed host config:
216
-
217
- ```ts
218
- interface HostConfig {
219
- root: string;
220
- viewId: string;
221
- sessionFile: string;
222
- cwd: string;
223
- initialPrompt: string | null;
224
- piCommand: string;
225
- piArgsPrefix: string[];
226
- model: string | null;
227
- tools: string | null;
228
- env: Record<string, string>;
229
- cols: number;
230
- rows: number;
231
- }
232
- ```
233
-
234
- Spawn args:
235
-
236
- ```js
237
- const args = [
238
- ...piArgsPrefix,
239
- "--session", sessionFile,
240
- ];
241
- if (model) args.push("--model", model);
242
- if (tools) args.push("--tools", tools);
243
- if (initialPrompt) args.push(initialPrompt);
244
- ```
245
-
246
- Environment injected into child Pi:
247
-
248
- ```text
249
- AGENT_BOARD_ROOT=<root>
250
- AGENT_BOARD_VIEW_ID=<viewId>
251
- AGENT_BOARD_CHILD=1
252
- AGENT_BOARD_HOSTED=pty
253
- ```
254
-
255
- `AGENT_BOARD_CHILD=1` lets the extension avoid dashboard-first startup behavior and any recursive agent-board UI side effects inside hosted children.
256
-
257
- ### 5.3 Control socket protocol
258
-
259
- Use JSONL messages over a Unix socket.
260
-
261
- Client → runner:
262
-
263
- ```json
264
- {"type":"hello","clientId":"...","wantOutput":true}
265
- {"type":"input","data":"raw terminal bytes"}
266
- {"type":"resize","cols":120,"rows":36}
267
- {"type":"detach","clientId":"..."}
268
- {"type":"interrupt"}
269
- {"type":"terminate"}
270
- {"type":"get_status"}
271
- ```
272
-
273
- Runner → client:
274
-
275
- ```json
276
- {"type":"hello","status":{...}}
277
- {"type":"output","data":"raw terminal bytes"}
278
- {"type":"status","status":{...}}
279
- {"type":"exit","exitCode":0}
280
- {"type":"error","message":"..."}
281
- ```
282
-
283
- For attach, the parent sends raw input bytes through `input`. The attach surface intercepts only `←` when the child input line appears empty; all other keys, including Pi's native `ctrl+]` editor shortcut, pass through to the child.
284
-
285
- > **Historical:** superseded by the editor_state side channel (issue #91 Phase 6) — `←` now detaches only when the child pushes `editorEmpty=true`; otherwise (draft or unknown state) it is forwarded to the child. `Ctrl+←` always detaches.
286
-
287
- The detach chord is `←` because it is already the board navigation key and preserves Pi editor keybindings.
288
-
289
- ## 6. Attach UI design
290
-
291
- ### 6.1 Option A: virtual terminal component
292
-
293
- Add `src/ui/pty-attach.ts`:
294
-
295
- ```ts
296
- export class PtyAttachComponent implements Component {
297
- // connects to control.sock
298
- // feeds output into terminal emulator buffer
299
- // renders buffer lines via ctx.ui.custom
300
- // forwards input to socket
301
- // sends resize from tui.terminal rows/cols
302
- // done({ action: "detached" }) on detach chord
303
- }
304
- ```
305
-
306
- Potential dependency choices:
307
-
308
- - `node-pty` for PTY creation in runner,
309
- - `xterm-headless` or equivalent for terminal emulation in attach component.
310
-
311
- If adding dependencies, update `package.json` `dependencies`, not `devDependencies`, because Pi package installs use production deps.
312
-
313
- Expected fidelity:
314
-
315
- - normal Pi input behavior: high, because real Pi receives real terminal bytes,
316
- - normal Pi visual behavior: high for text UI,
317
- - possible gaps: inline images, OSC hyperlinks, exact hardware cursor/IME, mouse support.
318
-
319
- ### 6.2 Option B: raw takeover API if Option A is insufficient
320
-
321
- If virtual rendering cannot meet expectations, propose a Pi core API:
322
-
323
- ```ts
324
- ctx.ui.rawTerminalSession(async ({ input, output, resize, restore }) => {
325
- // parent TUI suspends rendering
326
- // extension proxies bytes to/from child PTY
327
- });
328
- ```
329
-
330
- This would give true terminal proxy behavior, but requires Pi core work outside this extension.
331
-
332
- ## 7. Extension changes
333
-
334
- ### 7.1 `src/index.ts`
335
-
336
- Current extension mirrors events for any managed foreground session. Adjust it for hosted child processes:
337
-
338
- - if `process.env.AGENT_BOARD_CHILD === "1"`, skip dashboard auto-open handling,
339
- - still register event listeners,
340
- - when `AGENT_BOARD_VIEW_ID` is present, mirror events directly to that row,
341
- - keep footer status disabled/no-op in child to avoid confusing nested hosted Pi.
342
-
343
- Potential helper:
344
-
345
- ```ts
346
- const hostedViewId = process.env.AGENT_BOARD_VIEW_ID;
347
- const isHostedChild = process.env.AGENT_BOARD_CHILD === "1";
348
- ```
349
-
350
- ### 7.2 `src/runtime/service.mjs`
351
-
352
- Add methods:
353
-
354
- ```js
355
- launchHost(meta, initialPrompt)
356
- ensureHost(viewId)
357
- attachTarget(viewId) // { kind: "pty", socketPath } | { kind: "session", sessionFile }
358
- stopActivity(viewId) // sends interrupt to socket if hosted, else current stop
359
- terminateHost(viewId)
360
- injectReply(viewId, text) // if host alive, paste/submit into PTY; else fallback launch one-shot/host
361
- ```
362
-
363
- Dispatch flow becomes:
364
-
365
- ```text
366
- createView
367
- launchHost(meta, prompt)
368
- state = queued/working once child extension events arrive
369
- ```
370
-
371
- Reply flow:
372
-
373
- ```text
374
- if hostAlive:
375
- send text + Enter to PTY
376
- else:
377
- launchHost(existingMeta, text) or existing JSON reply fallback
378
- ```
379
-
380
- Stop flow needs two actions:
381
-
382
- - **interrupt active agent**: send Escape or RPC-like interrupt if available,
383
- - **kill hosted Pi**: terminate PTY runner and child process.
384
-
385
- Existing `ctrl+s stop` should initially mean “interrupt/stop active work”; add a separate confirm for killing host if needed.
386
-
387
- ### 7.3 `src/commands/agent-board.ts`
388
-
389
- Change attach decision:
390
-
391
- ```ts
392
- if (row.hostAlive) {
393
- openPtyAttach(ctx, row)
394
- } else if (row.alive) {
395
- existing confirm interrupt + switchSession fallback
396
- } else {
397
- ctx.switchSession(row.meta.sessionFile, ...)
398
- }
399
- ```
400
-
401
- `openPtyAttach` should return to the dashboard without switching sessions.
402
-
403
- ### 7.4 `src/ui/dashboard.ts`
404
-
405
- Update row rendering:
406
-
407
- - show host-alive indicator,
408
- - attach hint says `enter attach live` when `hostAlive`,
409
- - running row no longer always asks “Interrupt and attach?” if host socket exists,
410
- - session read-only view remains useful as non-interrupting transcript.
411
-
412
- ## 8. Worktree/safety implications
413
-
414
- Existing same-repo writer worktree isolation remains valid.
415
-
416
- PTY-hosted sessions make the isolation rule more important because hosts can stay alive after task completion. Active writer detection should consider:
417
-
418
- ```text
419
- hostAlive && writeCapable && worktreeMode !== "worktree"
420
- ```
421
-
422
- However, an idle hosted process may not be writing. For MVP, be conservative:
423
-
424
- - if another non-worktree host is alive in the same repo, force worktree for new write-capable dispatch,
425
- - later refine by checking `state.semanticState === "working"`.
426
-
427
- ## 9. Testing plan
428
-
429
- ### 9.1 Unit tests
430
-
431
- Add tests for:
432
-
433
- - host path helpers,
434
- - host status read/write,
435
- - `loadRow().hostAlive`,
436
- - service attach target selection,
437
- - hosted vs fallback reply behavior,
438
- - child event mirroring does not mark idle host as dead.
439
-
440
- ### 9.2 Runner integration tests
441
-
442
- Add fake interactive child script:
443
-
444
- ```text
445
- test-support/fake-pty-pi.mjs
446
- ```
447
-
448
- It should:
449
-
450
- - write recognizable ANSI output,
451
- - accept stdin,
452
- - echo prompts,
453
- - simulate busy/idle states through environment or marker files,
454
- - exit on a command.
455
-
456
- Test `pty-runner.mjs`:
457
-
458
- - creates socket and host.json,
459
- - broadcasts PTY output,
460
- - forwards input,
461
- - handles resize,
462
- - finalizes on child exit,
463
- - kills child on terminate.
464
-
465
- ### 9.3 Attach component tests
466
-
467
- Keep most logic pure:
468
-
469
- - socket client parser,
470
- - terminal buffer projection,
471
- - detach chord interception,
472
- - resize event generation.
473
-
474
- Manual verification is required for final TUI fidelity.
475
-
476
- ## 10. Phased implementation
477
-
478
- ### Phase 0 — feasibility spike
479
-
480
- Goal: prove or disprove extension-only PTY attach.
481
-
482
- Tasks:
483
-
484
- 1. Add temporary `node-pty` experiment script outside app flow.
485
- 2. Spawn `pi --session <tmp.jsonl> "say hi"` inside PTY.
486
- 3. Verify Pi runs initial prompt and remains interactive afterward.
487
- 4. Build tiny `ctx.ui.custom` component that renders a fake ANSI stream through a terminal emulator.
488
- 5. Confirm parent Pi TUI can render the virtual terminal without corrupting its own screen.
489
- 6. Confirm child extension can mirror events via `AGENT_BOARD_ROOT/VIEW_ID`.
490
-
491
- Exit criteria:
492
-
493
- - Can attach, see live Pi UI, type into it, detach, and keep child alive.
494
-
495
- If this fails due to public TUI limitations, switch to Option B/C instead of forcing a bad clone.
496
-
497
- ### Phase 1 — host store + service plumbing
498
-
499
- - Add `HostStatus` types/path helpers/store functions.
500
- - Extend `Row` with `host`/`hostAlive`.
501
- - Add service methods for host launch/status/terminate.
502
- - Keep legacy JSON runner as fallback.
503
-
504
- ### Phase 2 — PTY runner
505
-
506
- - Add `runner/pty-runner.mjs`.
507
- - Add socket protocol.
508
- - Add fake PTY integration tests.
509
- - Persist `host.json` and `screen.log`.
510
-
511
- ### Phase 3 — child event mirroring
512
-
513
- - Modify `src/index.ts` for hosted child env vars.
514
- - Make `syncForegroundEvent` or new `syncHostedEvent` update row activity without confusing host liveness.
515
- - Verify state transitions: queued → working → completed/needs_input while host remains alive.
516
-
517
- ### Phase 4 — live attach component
518
-
519
- - Add socket client.
520
- - Add virtual terminal renderer.
521
- - Intercept detach chord.
522
- - Forward all other input bytes.
523
- - Handle resize.
524
- - Integrate into `/agent-board` attach flow.
525
-
526
- ### Phase 5 — reply/stop behavior
527
-
528
- - Reply to host by injecting text + Enter into PTY.
529
- - Stop active work by sending Escape first.
530
- - Add terminate-host confirm if needed.
531
- - Preserve existing fallback for dead hosts.
532
-
533
- ### Phase 6 — hardening/polish
534
-
535
- - stale socket cleanup,
536
- - host TTL or user-visible “close host” command,
537
- - dependency install docs,
538
- - dashboard indicators,
539
- - worktree cleanup with live hosts,
540
- - manual verification matrix.
541
-
542
- ## 11. Acceptance criteria
543
-
544
- MVP live attach is accepted when:
545
-
546
- 1. Dispatch creates a real managed session and launches an interactive hosted Pi.
547
- 2. Dashboard shows the row working via child extension event mirroring.
548
- 3. Pressing attach while work is live opens the hosted session without interrupting it.
549
- 4. User can type normally into the attached Pi session.
550
- 5. Slash commands like `/session`, `/model`, `/tree` are handled by the child Pi, not reimplemented by agent-board.
551
- 6. Detach returns to dashboard and child Pi keeps running.
552
- 7. Reattach returns to the same live child Pi process.
553
- 8. If the host dies, attach falls back to `ctx.switchSession(sessionFile)`.
554
- 9. Existing tests pass; new host tests cover runner/socket/store behavior.
555
-
556
- ## 12. Risk register
557
-
558
- | Risk | Impact | Mitigation |
559
- |---|---|---|
560
- | `ctx.ui.custom` cannot faithfully render child terminal output | high | Phase 0 spike; fallback to Pi core raw takeover API or standalone CLI |
561
- | `node-pty` native install friction | medium | keep JSON runner fallback; document dependency; consider tmux fallback |
562
- | child Pi extension recursion | medium | `AGENT_BOARD_CHILD=1`; skip dashboard auto-open/footer in child |
563
- | host liveness conflated with agent activity | high | add `host.json`; separate `hostAlive` from `row.alive` |
564
- | worktree safety too conservative with idle hosts | low/medium | conservative MVP, later refine with activity state |
565
- | `←` is also a child-editor cursor key | low | detach only when the child input line appears empty; keep all other editor shortcuts, including `ctrl+]`, pass-through. **Superseded (issue #91 Phase 6):** detach now gates on the pushed `editorEmpty` state, not the rendered buffer; `Ctrl+←` always detaches |
566
- | terminal images/OSC links not perfect in virtual renderer | medium | document limitation; raw takeover/core API if needed |
567
-
568
- ## 13. Confidence
569
-
570
- - **PTY-hosted process + socket + status mirroring:** high, ~80–85%.
571
- - **Extension-only virtual-terminal attach:** medium, ~60–70% until Phase 0 proves TUI fidelity.
572
- - **True raw 1:1 terminal attach:** high conceptually, but likely needs Pi core or standalone CLI support.
573
-
574
- ## 14. Recommended next step
575
-
576
- Do **Phase 0 only** first. Do not refactor the whole service until we prove that a parent Pi extension can render and drive a child Pi PTY well enough inside `ctx.ui.custom()`.
577
-
578
- If Phase 0 passes, proceed with phases 1–6. If it fails, stop and design either:
579
-
580
- 1. a small Pi core raw-terminal takeover API, or
581
- 2. a standalone `pi-agent-board` CLI that owns the terminal like tmux.