diff --git a/pkg/session/session.go b/pkg/session/session.go index c86d11615..9f1800318 100644 --- a/pkg/session/session.go +++ b/pkg/session/session.go @@ -418,6 +418,17 @@ func (s *Session) IsSubSession() bool { return s.ParentID != "" } +// MessageCount returns the number of items that contain a message. +func (s *Session) MessageCount() int { + n := 0 + for _, item := range s.Messages { + if item.IsMessage() { + n++ + } + } + return n +} + // New creates a new agent session func New(opts ...Opt) *Session { sessionID := uuid.New().String() diff --git a/pkg/session/store.go b/pkg/session/store.go index 9c46c34ad..d4b012400 100644 --- a/pkg/session/store.go +++ b/pkg/session/store.go @@ -71,6 +71,7 @@ type Summary struct { CreatedAt time.Time Starred bool BranchParentSessionID string + NumMessages int } // Store defines the interface for session storage @@ -164,6 +165,7 @@ func (s *InMemorySessionStore) GetSessionSummaries(_ context.Context) ([]Summary CreatedAt: value.CreatedAt, Starred: value.Starred, BranchParentSessionID: value.BranchParentSessionID, + NumMessages: value.MessageCount(), }) return true }) @@ -878,7 +880,11 @@ func (s *SQLiteSessionStore) GetSessions(ctx context.Context) ([]*Session, error // This is much faster than GetSessions as it doesn't load message content. func (s *SQLiteSessionStore) GetSessionSummaries(ctx context.Context) ([]Summary, error) { rows, err := s.db.QueryContext(ctx, - "SELECT id, title, created_at, starred, branch_parent_session_id FROM sessions WHERE parent_id IS NULL OR parent_id = '' ORDER BY created_at DESC") + `SELECT s.id, s.title, s.created_at, s.starred, s.branch_parent_session_id, + (SELECT COUNT(*) FROM session_items si WHERE si.session_id = s.id AND si.item_type = 'message') + FROM sessions s + WHERE s.parent_id IS NULL OR s.parent_id = '' + ORDER BY s.created_at DESC`) if err != nil { return nil, err } @@ -888,7 +894,8 @@ func (s *SQLiteSessionStore) GetSessionSummaries(ctx context.Context) ([]Summary for rows.Next() { var id, title, createdAtStr, starredStr string var branchParentID sql.NullString - if err := rows.Scan(&id, &title, &createdAtStr, &starredStr, &branchParentID); err != nil { + var numMessages int + if err := rows.Scan(&id, &title, &createdAtStr, &starredStr, &branchParentID, &numMessages); err != nil { return nil, err } createdAt, err := time.Parse(time.RFC3339, createdAtStr) @@ -905,6 +912,7 @@ func (s *SQLiteSessionStore) GetSessionSummaries(ctx context.Context) ([]Summary CreatedAt: createdAt, Starred: starred, BranchParentSessionID: branchParentID.String, + NumMessages: numMessages, }) } diff --git a/pkg/session/store_test.go b/pkg/session/store_test.go index 104914990..0fe436aba 100644 --- a/pkg/session/store_test.go +++ b/pkg/session/store_test.go @@ -215,10 +215,12 @@ func TestGetSessionSummaries(t *testing.T) { assert.Equal(t, "session-2", summaries[0].ID) assert.Equal(t, "Second Session", summaries[0].Title) assert.Equal(t, session2Time, summaries[0].CreatedAt) + assert.Equal(t, 1, summaries[0].NumMessages) assert.Equal(t, "session-1", summaries[1].ID) assert.Equal(t, "First Session", summaries[1].Title) assert.Equal(t, session1Time, summaries[1].CreatedAt) + assert.Equal(t, 1, summaries[1].NumMessages) } func TestBranchSessionCopiesPrefix(t *testing.T) { diff --git a/pkg/tui/dialog/session_browser.go b/pkg/tui/dialog/session_browser.go index eecd0d0f4..65464093b 100644 --- a/pkg/tui/dialog/session_browser.go +++ b/pkg/tui/dialog/session_browser.go @@ -317,13 +317,15 @@ func (d *sessionBrowserDialog) renderSession(sess session.Summary, selected bool title = "Untitled" } + suffix := fmt.Sprintf(" (%d) • %s", sess.NumMessages, d.timeAgo(sess.CreatedAt)) + starWidth := 3 - maxTitleLen := maxWidth - 25 - starWidth + maxTitleLen := max(1, maxWidth-len(suffix)-starWidth) if len(title) > maxTitleLen { title = title[:maxTitleLen-1] + "…" } - return styles.StarIndicator(sess.Starred) + titleStyle.Render(title) + timeStyle.Render(" • "+d.timeAgo(sess.CreatedAt)) + return styles.StarIndicator(sess.Starred) + titleStyle.Render(title) + timeStyle.Render(suffix) } func (d *sessionBrowserDialog) timeAgo(t time.Time) string {