@zhuxixi/pi-agent-board 0.3.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 (65) hide show
  1. package/IMPLEMENTATION_PLAN.md +920 -0
  2. package/LICENSE +21 -0
  3. package/PRD.md +484 -0
  4. package/PROGRESS.md +127 -0
  5. package/README.md +131 -0
  6. package/VERIFY.md +113 -0
  7. package/docs/BATCH_SELECTION_READ_FLOW.md +277 -0
  8. package/docs/EXPLORATION.md +187 -0
  9. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +579 -0
  10. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +704 -0
  11. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +499 -0
  12. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +366 -0
  13. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +105 -0
  14. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +142 -0
  15. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +59 -0
  16. package/index.ts +6 -0
  17. package/package.json +81 -0
  18. package/runner/job-runner.mjs +420 -0
  19. package/runner/pty-runner.mjs +310 -0
  20. package/runner/state-runner.mjs +120 -0
  21. package/runner/title-runner.mjs +80 -0
  22. package/scripts/patch-vulns.mjs +59 -0
  23. package/src/commands/agent-board.ts +318 -0
  24. package/src/commands/attach-flow.ts +231 -0
  25. package/src/commands/bg.ts +70 -0
  26. package/src/core/atomic.mjs +145 -0
  27. package/src/core/auto-state.mjs +320 -0
  28. package/src/core/dashboard-render.mjs +10 -0
  29. package/src/core/derive.mjs +114 -0
  30. package/src/core/diagnostics.mjs +109 -0
  31. package/src/core/events.mjs +268 -0
  32. package/src/core/evidence.mjs +242 -0
  33. package/src/core/follow-up-queue.mjs +193 -0
  34. package/src/core/heuristics.mjs +240 -0
  35. package/src/core/ids.mjs +35 -0
  36. package/src/core/invocation.mjs +43 -0
  37. package/src/core/launch-options.mjs +317 -0
  38. package/src/core/launch.mjs +116 -0
  39. package/src/core/locks.mjs +80 -0
  40. package/src/core/paths.mjs +86 -0
  41. package/src/core/pid.mjs +42 -0
  42. package/src/core/prewarm-schedule.mjs +41 -0
  43. package/src/core/prompt-transport.mjs +13 -0
  44. package/src/core/pty-attach-jiggle-retry.mjs +90 -0
  45. package/src/core/pty-attach-render.mjs +51 -0
  46. package/src/core/pty-input.mjs +15 -0
  47. package/src/core/pty-links.mjs +71 -0
  48. package/src/core/pty-scroll.mjs +155 -0
  49. package/src/core/pty-support.mjs +327 -0
  50. package/src/core/repo.mjs +47 -0
  51. package/src/core/rows.mjs +290 -0
  52. package/src/core/screen-log-gc.mjs +198 -0
  53. package/src/core/screen-log.mjs +160 -0
  54. package/src/core/session-view.mjs +174 -0
  55. package/src/core/steering-prompts.mjs +34 -0
  56. package/src/core/steering.mjs +133 -0
  57. package/src/core/store.mjs +308 -0
  58. package/src/core/title.mjs +43 -0
  59. package/src/core/types.mjs +380 -0
  60. package/src/core/worktree.mjs +64 -0
  61. package/src/index.ts +109 -0
  62. package/src/runtime/service.mjs +1194 -0
  63. package/src/ui/dashboard-evidence.mjs +85 -0
  64. package/src/ui/dashboard.ts +1952 -0
  65. package/src/ui/pty-attach.ts +1378 -0
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # Pi Agent Board
2
+
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/zhuxixi/pi-agent-board/main/assets/banner.png" alt="Pi Agent Board" width="100%" />
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://github.com/zhuxixi/pi-agent-board/blob/main/assets/demo.mp4"><strong>30s demo</strong></a>
9
+ | <a href="https://pi.dev/packages?name=@zhuxixi/pi-agent-board">Pi package gallery</a>
10
+ | <a href="https://www.npmjs.com/package/@zhuxixi/pi-agent-board">npm</a>
11
+ </p>
12
+
13
+ Pi Agent Board is a full-screen TUI dashboard for [Pi](https://github.com/earendil-works/pi-mono). It lets you dispatch, monitor, peek/reply to, attach to, and clean up multiple background Pi sessions from one place.
14
+
15
+ ## Problems It Solves
16
+
17
+ - Run several Pi tasks at once without losing track of which are queued, running, waiting for input, in progress, done, failed, or stopped.
18
+ - Keep real Pi sessions durable and resumable after `/reload`, closing Pi, or restarting the terminal.
19
+ - Check the latest output and answer follow-up questions without interrupting a running session.
20
+ - Attach to the full interactive Pi session only when hands-on work is needed.
21
+ - Manage work across multiple projects in one global board.
22
+
23
+ ## Install
24
+
25
+ From npm:
26
+
27
+ ```bash
28
+ pi install npm:@zhuxixi/pi-agent-board
29
+ pi /agent-board
30
+ ```
31
+
32
+ You can also start Pi normally and run `/agent-board`.
33
+
34
+ Pi Agent Board requires Node 20+ and working Pi provider auth. If rows stay in `Running`, first confirm Pi can complete a one-shot model call:
35
+
36
+ ```bash
37
+ pi --mode json -p --no-session "Reply with exactly: DONE"
38
+ ```
39
+
40
+ That command should produce an assistant reply and finish with an `agent_end` event.
41
+
42
+ From a local checkout:
43
+
44
+ ```bash
45
+ npm install
46
+ pi install "$(pwd)"
47
+ pi /agent-board
48
+ ```
49
+
50
+ For auto-discovery while developing:
51
+
52
+ ```bash
53
+ ln -s "$(pwd)" ~/.pi/agent/extensions/agent-board
54
+ pi
55
+ ```
56
+
57
+ Remove that symlink when you no longer want Pi to auto-load the checkout. If you installed by path, remove it with `pi remove "$(pwd)"`.
58
+
59
+ ## Use
60
+
61
+ Open the board with `pi /agent-board` or `/agent-board` inside Pi.
62
+
63
+ - Type a task in the bottom input, then press `enter`.
64
+ - Confirm **Start session**, or adjust `cwd`, model, and thinking level first.
65
+ - Watch rows move through `Queued`, `Running`, `Needs input`, `In Progress`, `Done`, `Failed`, and `Stopped`.
66
+ - Press `space` to peek at the selected row's summary, blocker, and latest output.
67
+ - Press `r` to reply inline without attaching.
68
+ - Press `enter`, `right`, or `>` to attach to the real Pi session.
69
+ - Press `v` for a read-only live transcript.
70
+ - Press `/` to filter by text or state, such as `s:running`.
71
+ - Press `ctrl+r` rename, `ctrl+t` pin, `ctrl+s` stop, `d` mark done, `m` multi-select, `ctrl+x` delete/archive, `X` delete inactive rows in the selected state, and `?` for help.
72
+
73
+ Rows are stored under `~/.pi/agent/agent-board/` by default. Deleting a row archives it from the board; it does not remove the underlying Pi session file.
74
+
75
+ ## Configuration
76
+
77
+ Useful environment variables:
78
+
79
+ | Variable | Use |
80
+ | --- | --- |
81
+ | `AGENT_BOARD_ROOT` | Store location. Defaults to `~/.pi/agent/agent-board/`. |
82
+ | `AGENT_BOARD_AUTO_STATE=off` | Disable automatic terminal-state moves. |
83
+ | `AGENT_BOARD_AUTO_STATE_MODEL=<model>` | Model for classifying finished turns. Defaults to `gpt-4o`; use `off` for heuristic-only. |
84
+ | `AGENT_BOARD_SUMMARY_MODEL=<model>` | Model for short row summaries. Defaults to `gpt-4o`; use `off` to disable. |
85
+ | `AGENT_BOARD_TITLE_MODEL=<model>` | Model for generated session titles. Defaults to `openai-codex/gpt-5.5`; use `off` to disable. |
86
+ | `AGENT_BOARD_TITLE_THINKING_LEVEL=<level>` | Thinking level for title generation. Defaults to `low`; use `off` to omit it. |
87
+ | `AGENT_BOARD_DISABLE_PTY=1` | Disable PTY attach mode. |
88
+ | `AGENT_BOARD_FORCE_PTY=1` | Force PTY attach mode. |
89
+ | `AGENT_BOARD_ATTACH_MOUSE=0` | Disable attach-view mouse handling and use terminal-native selection. |
90
+ | `AGENT_BOARD_WHEEL_LINES=<1-50>` | Lines scrolled per mouse-wheel event in attach view. Defaults to `1`. |
91
+
92
+ Legacy `AGENT_VIEW_*` variables are still honored for migration.
93
+
94
+ If the board reports `node-pty unavailable`, press `!` in the dashboard for diagnosis and fix steps.
95
+
96
+ ## Develop
97
+
98
+ ```bash
99
+ npm install
100
+ npm run typecheck
101
+ npm test
102
+ npm run pack:dry
103
+ ```
104
+
105
+ Run all checks with:
106
+
107
+ ```bash
108
+ npm run verify
109
+ ```
110
+
111
+ `npm run verify` runs typecheck, tests, and a dry npm pack.
112
+
113
+ ## Publish
114
+
115
+ Before publishing a new release, bump the package version, run verification, then publish:
116
+
117
+ ```bash
118
+ npm run verify
119
+ npm version patch
120
+ npm publish
121
+ ```
122
+
123
+ If the version is already bumped, skip `npm version patch`.
124
+
125
+ Use `npm version minor` or `npm version major` instead when the release warrants it. After publish, users install with:
126
+
127
+ ```bash
128
+ pi install npm:@zhuxixi/pi-agent-board
129
+ ```
130
+
131
+ The Pi package gallery uses the `pi.video` and `pi.image` URLs from `package.json`.
package/VERIFY.md ADDED
@@ -0,0 +1,113 @@
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 test # expect: 0 failures
14
+ npm run pack:dry # expect: pi-agent-board-<version>.tgz contents only include deploy files
15
+ ```
16
+
17
+ `npm test` includes a hermetic integration test that runs the **real detached runner** against
18
+ a fake pi worker (`test-support/fake-pi.mjs`) — it proves dispatch → events → status/state →
19
+ finalize (completed / needs_input / failed / stopped) without any model/network.
20
+
21
+ ## 1. Does the extension load in pi? (no auth)
22
+
23
+ ```bash
24
+ pi --list-models -e "$(pwd)/src/index.ts"
25
+ ```
26
+ Expect a normal model table and **exit 0** (the extension factory ran without throwing). If you
27
+ see an error mentioning `index.ts` / jiti / a missing import, that's a load problem.
28
+
29
+ ## 2. CRITICAL: can pi reach a model at all? (needs pi auth)
30
+
31
+ This is the thing that was blocked in the dev sandbox. The background worker is just
32
+ `pi --mode json -p --session <file> "<prompt>"`, so first confirm a plain one-shot works:
33
+
34
+ ```bash
35
+ # Should print a JSON event stream that ENDS WITH a line of type "agent_end", then exit
36
+ # within a few seconds. Watch for an "assistant" message_end before agent_end.
37
+ pi --mode json -p --no-session "Reply with exactly: DONE" | tail -n 20
38
+ ```
39
+ - ✅ **Healthy:** you see `...message_end (assistant)...`, then `agent_end`, and the command exits.
40
+ - ❌ **Hang:** it prints up to a `message_end` for the **user** message then sits idle (no
41
+ assistant reply, never exits). That means pi has no working provider auth / network here.
42
+ Fix pi's auth first:
43
+ ```bash
44
+ pi # then run /login inside pi, or set the provider key pi expects, e.g.
45
+ # export ANTHROPIC_API_KEY=... (or OPENAI_API_KEY=... for gpt-4o summaries)
46
+ ```
47
+ Re-run the one-shot above until it ends in `agent_end`. The dashboard cannot show live results
48
+ until this works — it's a pi setup step, independent of this extension.
49
+
50
+ ## 3. Install the extension for normal use
51
+
52
+ For a local checkout, install it as a Pi package:
53
+ ```bash
54
+ pi install "$(pwd)"
55
+ ```
56
+ Or symlink the repo into pi's global extensions dir (auto-discovered via the top-level `index.ts`):
57
+ ```bash
58
+ ln -s "$(pwd)" ~/.pi/agent/extensions/agent-board
59
+ ```
60
+ After publish, install with:
61
+ ```bash
62
+ pi install npm:pi-agent-board
63
+ ```
64
+ Then start pi normally:
65
+ ```bash
66
+ pi
67
+ ```
68
+
69
+ ## 4. Drive the dashboard
70
+
71
+ Inside pi:
72
+ 1. Type `/agent-board`, or start with `pi /agent-board` → the full-screen dashboard opens.
73
+ - With `pi /agent-board`, quitting the dashboard exits Pi instead of dropping you into a normal chat session.
74
+ - The startup path should feel cleaner than `/agent-board`: no normal Pi header/footer chrome and no dispatch notifications above the dashboard.
75
+ 2. Type a task in the bottom input (e.g. `list the files in this repo and summarize the README`), then press **Enter**.
76
+ - A **Start session** dialog opens with **Start session** focused by default.
77
+ - Press **Enter** again to launch immediately, or move with **↑/↓** to change **cwd**, **model**, or **thinking** first.
78
+ - A row appears and moves `Queued → Running → Done` (needs step 2 healthy).
79
+ 3. **space** = peek when the input is empty (summary, blocker, latest output); in peek **r** = reply, **a** = attach.
80
+ **→** / **>** = open a full-screen live session view without interrupting; **←** / **<** returns.
81
+ 4. **enter** on an empty input = attach to the selected full session (confirms first if it's still running).
82
+ You're now in the real Pi session; run `/agent-board` again to return.
83
+ 5. Other keys: **/** filter (`s:running`, or free text), **Ctrl+R** rename, **Ctrl+T** pin, **Ctrl+S** stop,
84
+ **Ctrl+X** delete (archives the row, keeps the session file), **?** help, **Esc** clears input / quits when empty.
85
+
86
+ ## 5. Inspect the durable store on disk
87
+
88
+ ```bash
89
+ ls -R ~/.pi/agent/agent-board
90
+ cat ~/.pi/agent/agent-board/roster.json
91
+ cat ~/.pi/agent/agent-board/views/*/state.json
92
+ cat ~/.pi/agent/agent-board/views/*/runs/*/status.json
93
+ # raw worker event stream for a run:
94
+ cat ~/.pi/agent/agent-board/views/*/runs/*/events.jsonl
95
+ ```
96
+
97
+ ## 6. Recovery / persistence
98
+
99
+ - Start a dispatch, then quit pi (or `/reload`). Re-open pi and `/agent-board` — the row is still
100
+ there with its last state (rehydrated from disk). If a run's runner died without finishing,
101
+ the dashboard reconciles it to `failed (runner exited)` on open.
102
+
103
+ ## 7. Same-repo session launches
104
+
105
+ - Dispatch one task in a git repo, then (while it's running) dispatch a second in the
106
+ **same repo**. Both launches should be allowed. The extension never creates a git worktree on
107
+ its own.
108
+
109
+ ## 8. Summary model
110
+
111
+ Default summary model is **gpt-4o** (override `AGENT_BOARD_SUMMARY_MODEL=<model>`, disable with
112
+ `AGENT_BOARD_SUMMARY_MODEL=off`; legacy `AGENT_VIEW_SUMMARY_MODEL` is also honored). It needs OpenAI auth; without it the row keeps its heuristic
113
+ summary (e.g. the first sentence of the agent's last message or the active tool).
@@ -0,0 +1,277 @@
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.