--- phase: 22-agent-streaming plan: "05" subsystem: chat-integration tags: [virtualization, streaming, chat, slash-commands, mentions, edit-retry] dependency_graph: requires: [22-01, 22-02, 22-03, 22-04] provides: [virtualized-message-list, fully-wired-chat-panel, chat-input-with-popovers, chat-api-edit-truncate] affects: - ui/src/components/ChatMessageList.tsx - ui/src/components/ChatPanel.tsx - ui/src/components/ChatInput.tsx - ui/src/api/chat.ts - ui/src/components/ChatMessageList.test.tsx - packages/shared/src/index.ts tech_stack: added: [] patterns: - "useVirtualizer from @tanstack/react-virtual with estimateSize: 80, overscan: 5" - "Dynamic height measurement via measureElement ref callback" - "Synthetic streaming entry appended to virtualizer list with id: __streaming__" - "virtualizer.measure() called on streamingContent change for growing message re-measurement" - "Slash command popover triggered by / at start of input" - "@mention popover triggered by /@word pattern match at end of input" - "resolveAgentFromContent routes slash > @mention > active agent" - "handleRetry looks up actual prior user message (not hardcoded text)" key_files: created: [] modified: - ui/src/components/ChatMessageList.tsx - ui/src/components/ChatPanel.tsx - ui/src/components/ChatInput.tsx - ui/src/api/chat.ts - ui/src/components/ChatMessageList.test.tsx - packages/shared/src/index.ts decisions: - "virtualizer.measure() called on streamingContent change to handle dynamically growing streaming message height (Pitfall 3 from RESEARCH.md)" - "Slash command popover takes priority over mention popover — only one active at a time" - "handleRetry truncates from the user message (not the assistant message) — this removes both the assistant message and any messages after it" - "postMessageAndStream and savePartialMessage added to chatApi — these were specified in 22-01 plan but missing from the implemented chat.ts" metrics: duration: "~20 minutes" completed: "2026-04-01" tasks: 3 files: 6 requirements: - PERF-03 - CHAT-01 - CHAT-08 - CHAT-10 - CHAT-11 - CHAT-12 - INPUT-05 - INPUT-06 - PERF-02 --- # Phase 22 Plan 05: Final Integration — Virtualized List, ChatPanel, ChatInput Summary One-liner: Virtualized ChatMessageList with @tanstack/react-virtual, fully-wired ChatPanel integrating all Phase 22 components, and ChatInput with slash command and @mention popovers. ## Tasks Completed | Task | Name | Commit | Key Files | |------|------|--------|-----------| | 1 | Virtualized ChatMessageList and chat API edit/truncate methods | 6eca2eff | ui/src/components/ChatMessageList.tsx, ui/src/api/chat.ts, ui/src/components/ChatMessageList.test.tsx | | 2 | Wire ChatPanel and ChatInput with all Phase 22 features | 3e4e1e72 | ui/src/components/ChatPanel.tsx, ui/src/components/ChatInput.tsx | | 3 | Automated verification (human verification deferred) | — | tsc: clean, vitest: 165 pass / 25 todo | ## What Was Built ### Task 1: Virtualized ChatMessageList + chatApi methods **`ui/src/components/ChatMessageList.tsx`** — Full rewrite: - `useVirtualizer` with `estimateSize: 80`, `overscan: 5`, dynamic `measureElement` ref - Synthetic streaming entry: `id: "__streaming__"`, `isStreamingEntry: true` appended when `isStreaming && streamingContent` - `virtualizer.measure()` called on `streamingContent` change for dynamic height re-measurement - Jump-to-bottom button appears when scrolled >200px from bottom - 3 loading Skeleton placeholders (varying widths) - Agent identity resolved from `agentMap` prop or streaming agent fallback props - Props: `streamingContent`, `isStreaming`, `streamingAgentName/Icon/Role`, `onEdit`, `onRetry`, `agentMap` **`ui/src/api/chat.ts`** — New methods: - `postMessageAndStream` — POST fetch with ReadableStream, parses SSE token/done/error events (was missing; now added) - `savePartialMessage` — delegates to `postMessage` for partial content persistence (was missing; now added) - `editMessage` — PATCH `/conversations/:id/messages/:msgId` - `truncateMessagesAfter` — DELETE `/conversations/:id/messages/after/:msgId` - `deleteMessage` — DELETE `/conversations/:id/messages/:msgId` ### Task 2: ChatPanel and ChatInput integration **`ui/src/components/ChatPanel.tsx`** — Full rewrite integrating all Phase 22 components: - `useStreamingChat` provides `streamingContent`, `isStreaming`, `startStream`, `stop` - `ChatAgentSelector` in header with `onAgentChange` → updates `activeAgentId` local state - `ChatStopButton` shown conditionally when `isStreaming` - `agentMap` built from agents query for message identity bars - `handleEdit`: `editMessage` + `truncateMessagesAfter` + `startStream` with edited content - `handleRetry`: finds actual last user message content, truncates from that user message onward (removes assistant + all after), re-streams - `resolveAgentFromContent` for agent routing from slash commands and @mentions - `ChatInput` receives `agents`, `agentsLoading`, `disabled`, custom `placeholder` **`ui/src/components/ChatInput.tsx`** — Updated with popover support: - New props: `placeholder`, `agents`, `agentsLoading` - Slash command state: `slashOpen`, `slashQuery` — triggered when input starts with `/` - @mention state: `mentionOpen`, `mentionQuery` — triggered by `/@word` pattern at end - `handleSlashSelect`: replaces input value with selected command + space - `handleMentionSelect`: replaces `@query` with `@agentName ` in input value - Slash popover takes priority (only one active at a time) - Escape key closes open popover before clearing input ### Task 3: Automated Verification Results **TypeScript:** `tsc --noEmit` — clean (no errors) **Vitest:** 41 test files, 165 tests passed, 25 todos (intentional scaffolding) **Human verification deferred** per execution directive (autonomous mode, no manual stops). The following 12 verification steps require browser interaction and are deferred to the next verification session: 1. CHAT-01 — streaming tokens appear word-by-word 2. PERF-02 — sub-100ms first token from echo stub 3. CHAT-12 — stop saves partial content with [stopped] suffix 4. CHAT-10 — edit message triggers regeneration 5. CHAT-11 — retry uses actual prior user message 6. CHAT-08 — agent selector routes to selected agent 7. INPUT-05 — slash command popover opens on /, routes to PM agent 8. INPUT-06 — @mention popover opens on @, routes to named agent 9. PERF-03 — virtualized list has limited DOM nodes for large conversations 10. AGENT-04 — agent name and colored icon above assistant messages 11. THEME-03 — agent colors distinguishable across all 3 themes 12. All 11 agent roles have distinct colors ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Bug] Fixed duplicate ChatConversation type exports in packages/shared/src/index.ts** - **Found during:** Task 1 (pre-existing, causing TypeScript compile failure) - **Issue:** ChatConversation, ChatMessage, and 3 other types were exported twice from `packages/shared/src/index.ts` — once at line 569 and again at line 622. This caused `TS2300: Duplicate identifier` errors. - **Fix:** Removed the redundant second export block (lines 622-628) - **Files modified:** `packages/shared/src/index.ts` - **Commit:** 6eca2eff **2. [Rule 1 - Bug] Added missing postMessageAndStream and savePartialMessage to chatApi** - **Found during:** Task 1 (pre-existing — useStreamingChat.ts and useStreamingChat.test.ts both referenced these methods but they were absent from chat.ts) - **Issue:** `useStreamingChat.ts` called `chatApi.postMessageAndStream` and `chatApi.savePartialMessage`, and the test file mocked them, but neither method existed in `ui/src/api/chat.ts`. The 22-01 SUMMARY stated they were added, but they were not present. - **Fix:** Added both methods with correct SSE ReadableStream parsing and partial message saving - **Files modified:** `ui/src/api/chat.ts` - **Commit:** 6eca2eff **3. [Rule 3 - Blocking] Installed @testing-library/react devDependency** - **Found during:** Task 1 verification (tsc --noEmit) - **Issue:** `@testing-library/react ^16.0.0` was in `ui/package.json` devDependencies but not installed; `useStreamingChat.test.ts` imports it causing TS2307 error - **Fix:** Ran `pnpm install` to install the declared but missing dependency - **Commit:** included in 6eca2eff ## Known Stubs None affecting this plan's goal. The `streamEcho` stub in the server yields word-by-word with 50ms delay — this is intentional and documented (Phase 23 replaces with real LLM adapter per DECISIONS.md). ## Self-Check: PASSED