fix(tui): hide empty sessions from the /sessions picker
Zero-message sessions no longer appear in the picker. Covers three cases: the currently-running session (its file exists but no prompt has landed in it yet), sessions the user exited immediately after /clear, and any stale empties that PruneEmptySessions hasn't swept yet. Resuming an empty session was always a no-op, so nothing is lost. The '(empty)' fallback summary in formatSessionRowPlain stays as defense in case MessageCount > 0 but FirstUserText is blank.
- commit
- 1a2ab427fe63f1e7617ad420b60c29b8245691a8
- author
- patriceckhart · · 2026-04-19 15:16 UTC
- parent
- 961f99d
- signature
- unsigned
- refs
- tag: v0.0.37
1 file · +14 -2
M internal/agent/modes/session_dialog.go +14 -2
| @@ -25,9 +25,21 @@ type sessionDialogAction struct { | |||
| 25 | 25 | ||
| 26 | 26 | func newSessionDialog() *sessionDialog { return &sessionDialog{} } | |
| 27 | 27 | ||
| 28 | - | // Open populates the dialog from root + cwd and shows it. | |
| 28 | + | // Open populates the dialog from root + cwd and shows it. Empty | |
| 29 | + | // sessions (zero messages) are filtered out so the currently-running | |
| 30 | + | // session, a freshly-opened one that hasn't received a prompt yet, | |
| 31 | + | // and any stale empties that haven't been pruned yet all stay out | |
| 32 | + | // of the picker. Resuming an empty session is a no-op anyway. | |
| 29 | 33 | func (d *sessionDialog) Open(root, cwd string) { | |
| 30 | - | d.sessions = core.DescribeSessions(root, cwd) | |
| 34 | + | all := core.DescribeSessions(root, cwd) | |
| 35 | + | filtered := make([]core.SessionSummary, 0, len(all)) | |
| 36 | + | for _, s := range all { | |
| 37 | + | if s.MessageCount == 0 { | |
| 38 | + | continue | |
| 39 | + | } | |
| 40 | + | filtered = append(filtered, s) | |
| 41 | + | } | |
| 42 | + | d.sessions = filtered | |
| 31 | 43 | d.cursor = 0 | |
| 32 | 44 | d.active = true | |
| 33 | 45 | } | |
| @@ -25,9 +25,21 @@ type sessionDialogAction struct { | |||||
| 25 | 25 | ||||
| 26 | func newSessionDialog() *sessionDialog { return &sessionDialog{} } | 26 | func newSessionDialog() *sessionDialog { return &sessionDialog{} } | ||
| 27 | 27 | ||||
| 28 | - | // Open populates the dialog from root + cwd and shows it. | 28 | + | // Open populates the dialog from root + cwd and shows it. Empty |
| 29 | + | // sessions (zero messages) are filtered out so the currently-running | |||
| 30 | + | // session, a freshly-opened one that hasn't received a prompt yet, | |||
| 31 | + | // and any stale empties that haven't been pruned yet all stay out | |||
| 32 | + | // of the picker. Resuming an empty session is a no-op anyway. | |||
| 29 | func (d *sessionDialog) Open(root, cwd string) { | 33 | func (d *sessionDialog) Open(root, cwd string) { | ||
| 30 | - | d.sessions = core.DescribeSessions(root, cwd) | 34 | + | all := core.DescribeSessions(root, cwd) |
| 35 | + | filtered := make([]core.SessionSummary, 0, len(all)) | |||
| 36 | + | for _, s := range all { | |||
| 37 | + | if s.MessageCount == 0 { | |||
| 38 | + | continue | |||
| 39 | + | } | |||
| 40 | + | filtered = append(filtered, s) | |||
| 41 | + | } | |||
| 42 | + | d.sessions = filtered | |||
| 31 | d.cursor = 0 | 43 | d.cursor = 0 | ||
| 32 | d.active = true | 44 | d.active = true | ||
| 33 | } | 45 | } | ||