track chat activity and unread state per session#62
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4f2da33b6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| store.setStreamingMessageId(event.payload.sessionId, null); | ||
| store.setChatState(event.payload.sessionId, "idle"); |
There was a problem hiding this comment.
Ignore completion events for sessions already removed
Because this hook now listens globally, acp:done can fire after a session has been archived/removed from useChatSessionStore (for example, while a background run is finishing). Writing runtime state unconditionally here recreates sessionStateById[sessionId] for a session that no longer exists, which leaves orphan unread/runtime entries that are never surfaced or cleaned up. Add a getSession(sessionId) guard before mutating chat runtime in this handler.
Useful? React with 👍 / 👎.
| if (event.payload.sessionId !== sessionIdRef.current) return; | ||
| const modelName = | ||
| event.payload.currentModelName ?? event.payload.currentModelId; | ||
| useChatSessionStore |
There was a problem hiding this comment.
Skip metadata updates for unknown session IDs
With the per-app listener, acp:model_state events are now processed even when the session is not present locally (e.g., removed/archived session). Calling updateSession unconditionally triggers persistence in chatSessionStore.updateSession, so this path can emit backend update calls and error logs for non-existent sessions. Guard on getSession(event.payload.sessionId) before updating metadata.
Useful? React with 👍 / 👎.
Merges 7 commits from main into tho/boss-ui: - Image paste support in chat input (block#68) - Tab name text unselectable (block#67) - Chat title immediate update on send (block#66) - Artifact v1 file viewing (block#63) - Cmd+W tab close (block#64) - Chat activity/unread state tracking (block#62) - Sidebar hierarchy polish + faster reloads (block#61) Key conflict resolutions: - Keep Tailwind v4 CSS-based config (delete tailwind.config.js) - Keep boss-ui dialog.tsx, add showCloseButton prop from main - Add text-foreground-subtle token to boss-ui theme system - Keep ToolCallAdapter (boss-ui), adopt ToolChainCards pattern from main - Delete MarkdownContent/ToolCallCard (replaced by boss-ui ai-elements) - Adopt SessionActivityIndicator from main into sidebar - Adopt ClickableImage/ImageLightbox from main - Merge drag-and-drop image support into ChatInput - Keep boss-ui hover:bg-accent/50 treatment throughout sidebar Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Category: improvement
User Impact: Users can see which chats are still busy or have unread output, and session-specific status data now stays with the correct chat when they switch tabs.
Problem: Chat runtime state was effectively shared across sessions, which made per-tab status, unread tracking, and token/model display unreliable once multiple chats were open. The tool usage pill could also wrap awkwardly with long tool names, which made the inline transcript harder to scan.
Solution: Move streaming, token, error, and unread state into per-session runtime records, route ACP stream updates globally by
sessionId, and surface running/unread indicators in tabs and sidebars with a shared accent-aware indicator component. Clamp long tool names to a single line with truncation and tooltip fallback, then add regression coverage around the multi-session behavior.File changes
src/app/AppShell.tsx
Wires the app shell to the global ACP stream listener, derives tab activity from per-session runtime state, clears unread state when the active chat is viewed, and preserves the newer startup hook shape from
main.src/features/chat/hooks/tests/useAcpStream.test.ts
Reworks stream-hook coverage around session-targeted updates, background unread marking, and listener lifecycle after moving to a global listener.
src/features/chat/hooks/tests/useChat.test.ts
Adds coverage for persona-aware cancellation across remounts and for concurrent sends from different sessions.
src/features/chat/hooks/useAcpStream.ts
Removes the per-view session filter, updates session runtimes by
sessionId, and marks background sessions unread on completion.src/features/chat/hooks/useChat.ts
Switches chat operations to session-scoped runtime state and keeps cancellation tied to the active streaming persona for that session.
src/features/chat/lib/sessionActivity.ts
Adds a small helper to derive whether a chat should be treated as actively running.
src/features/chat/stores/tests/chatStore.test.ts
Refreshes store tests around per-session runtime records, unread state, streaming state, and cleanup behavior.
src/features/chat/stores/chatStore.ts
Replaces the old global runtime fields with per-session runtime storage for chat state, tokens, streaming message ids, errors, and unread flags.
src/features/chat/ui/ChatView.tsx
Consumes token state from the session-aware
useChathook and removes the old per-view ACP listener.src/features/chat/ui/ToolCallCard.tsx
Keeps tool pills left-aligned, clamps long tool names to a single line, and preserves the full tool name in a tooltip.
src/features/chat/ui/tests/ToolCallCard.test.tsx
Adds regression coverage for long tool-name truncation.
src/features/sidebar/ui/Sidebar.tsx
Derives sidebar chat metadata from per-session runtime state so project and recent lists can show running and unread state accurately.
src/features/sidebar/ui/SidebarChatRow.tsx
Accepts running and unread props and renders the shared activity indicator.
src/features/sidebar/ui/SidebarProjectsSection.tsx
Passes activity state through both expanded and collapsed project chat rows and uses the shared overlay indicator in collapsed recents.
src/features/sidebar/ui/tests/SidebarChatRow.test.tsx
Adds coverage for running and unread indicators in sidebar rows.
src/features/tabs/types.ts
Extends tab metadata with running and unread state.
src/features/tabs/ui/TabBar.test.tsx
Adds tab-bar coverage for running and unread indicators.
src/features/tabs/ui/TabBar.tsx
Displays per-session running and unread state in tabs via the shared indicator.
src/shared/types/chat.ts
Introduces the shared
SessionChatRuntimetype used to model session-specific runtime state.src/shared/ui/SessionActivityIndicator.test.tsx
Adds direct coverage for inline and overlay activity indicator variants.
src/shared/ui/SessionActivityIndicator.tsx
Introduces a shared accent-aware activity indicator used by the tab and sidebar surfaces.
Reproduction Steps