Duo Chat Frontend Architecture Overhaul
## Summary
This epic tracks the implementation of the Duo Chat Architecture Blueprint, which defines the target frontend architecture for GitLab's Duo Agentic Chat system. The blueprint addresses critical architectural issues and establishes a clear path forward for stabilizing, improving, and scaling the chat experience.
**Blueprint MR:** [gitlab-com/content-sites/handbook!17985](https://gitlab.com/gitlab-com/content-sites/handbook/-/merge_requests/17985)
## Key Problems Being Addressed
1. **Fragmented State Management** - Chat state is scattered across 7+ storage systems (Vue.observable, Vuex, Apollo cache, raw WebSocket, Event Hub, provide/inject, browser storage) with no single source of truth, causing inconsistent behavior, duplicated messages, and hard-to-reproduce bugs.
2. **Split Codebase Friction** - The chat is split across the `duo-ui` npm library and the GitLab monolith, causing slow iteration (npm publish cycle), untested cross-context behavior, and inconsistent UI across environments.
3. **Testing & Observability Gaps** - Insufficient automated tests, hard-to-trace bugs across layers, and regressions discovered in production rather than caught before release.
4. **Streaming Coupled to Component Lifecycle** - WebSocket connections are tied to `duo_agentic_chat.vue`, meaning streams are severed when users navigate away, resize windows, or switch tabs, resulting in lost AI responses.
## Goals
1. **Unified State Management** - Establish Apollo cache as the single source of truth for all chat state
2. **Single Codebase Ownership** - Transition from duo-ui to vendored components owned within the monolith
3. **Comprehensive Test Coverage & Observability** - Introduce automated testing guardrails and traceable data flow
4. **External Trigger Support** - Enable features like troubleshoot buttons and work item summaries to interact with chat
## Implementation Phases
### Phase 1: Streaming Architecture - StreamManager Service
- Create `StreamManager` singleton service class (`ee/app/assets/javascripts/ai/services/stream_manager.js`)
- Initialize service in `init_duo_panel.js` alongside Apollo provider
- Decouple WebSocket management from component lifecycle
- Update `duo_agentic_chat.vue` to use service instead of direct WebSocket
- Remove `beforeDestroy` socket cleanup from component
- Handle edge cases: navigation mid-stream, multiple active streams, reconnection/recovery
- Update tests to mock service instead of WebSocket directly
### Phase 2: Apollo Cache Migration (State Unification)
- **Step 1**: Stop adding new state to Vuex; use Apollo for all new features
- **Step 2**: Migrate message state from Vuex to Apollo cache
- **Step 3**: Migrate `duoChatGlobalState` (Vue.observable) to Apollo `@client` fields
- **Step 4**: Remove Vuex store (`tanuki_bot/store`) entirely
- Establish client-only Apollo state for: `chatState @client`, `uiState @client`, `pendingMessages @client`
### Phase 3: Agent & Model Selection Refactor
- Extract agent selector from `new_chat_button.vue` into standalone `agent_selector.vue`
- Create standalone `model_selector.vue`
- Persist agent/model selection in localStorage/cookies (required for external triggers)
- Sync localStorage values to Apollo cache for reactive UI
- Ensure external triggers can read selections before chat component mounts
### Phase 4: External Triggers Architecture
- Standardize on `DuoChatQuickAction` component pattern for external triggers
- Use event hub emissions (`SHOW_NEW_CHAT`, `QUEUE_CHAT_COMMAND`) for decoupled communication
- Support troubleshoot button, work item summary, and future trigger use cases
- Ensure triggers work from any page across GitLab
### Phase 5: duo-ui to Vendored Components Transition
- Vendor chat components into `ee/app/assets/javascripts/ai/components/chat/`
- Mount vendored components as web components for interoperability
- Key components: `message_list.vue`, `message_bubble.vue`, `chat_input.vue`
- Remove dependency on `duo-ui` npm package for chat components
### Phase 6: UI/UX Improvements
- Chat input expand/collapse toggle (P1)
- Keyboard shortcuts: Shift+Enter for newline, Enter to send (P1)
- Auto-resize input with content (P2)
- Max height with scroll (P2)
- User-draggable resize handle (P3)
- Navigation rail integration with agent selector and future interactive elements
### Future Enhancements
- **SharedWorker** for streaming that survives page navigation (not just component unmount)
- File attachments support
- Vue Router migration for AI panel tabs (related: gitlab-org/gitlab#590510)
## Key Architectural Decisions
| Topic | Decision |
|-------|----------|
| Chat state terminology | **New** (empty), **Current** (selected), **Old** (non-selected) |
| Deleted chats | Permanently removed, not recoverable |
| Data flow standard | Apollo cache as single source of truth |
| Streaming architecture | Main-thread StreamManager service (SharedWorker as future enhancement) |
| Agent/Model persistence | localStorage/cookies (required for external triggers) |
| External triggers | Separate from slash commands, work with agentic chat |
| Input box | Expand/collapse toggle first, then auto-resize |
| duo-ui transition | Move to vendored components mounted as web components |
## References
- [Duo Chat Architecture Blueprint MR](https://gitlab.com/gitlab-com/content-sites/handbook/-/merge_requests/17985)
- [Codebase: ee/app/assets/javascripts/ai/duo_agentic_chat](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/app/assets/javascripts/ai/duo_agentic_chat)
epic