Unified entry point hook that dispatches chat state between two transport adapters — SSE/Guide and NATS/Mingo — based on the `activeMode` flag, while keeping both hooks always mounted to satisfy React's rules of hooks. ## Key Components ### `ChatMode` Union type `'guide' | 'mingo'` discriminating the active transport. ### `UseUnifiedChatModes` Per-mode configuration object. Each slot is optional — omitting a key leaves that adapter idle with a stable no-op fallback. | Field | Type | Description | |---|---|---| | `guide` | `UseSseChatAdapterOptions` | SSE transport config (reads ambient `ChatRuntimeContext`) | | `mingo` | `UseNatsChatAdapterConfig` | NATS transport config with explicit dialog ID and publish callback | ### `UseUnifiedChatOptions` Hook input shape. | Field | Description | |---|---| | `modes` | Per-mode configs | | `activeMode` | Which transport is live | | `mingoStateOverride` | Host-owned `UnifiedChatState` injected in place of internal NATS state, allowing state to survive panel unmounts | ### `useUnifiedChat(options)` Main hook. Returns a stable `UnifiedChatState` composed of: - **Active adapter state** — messages, streaming phase, token usage, connection - **Stable action callbacks** — `sendMessage`, `stopMessage`, `clearMessages`, `discussRef`, `displayRef` - **Dialog management** — `selectDialog`, `startNewDialog`, `deleteDialog`, `renameDialog`, `archiveDialog`, `loadMoreDialogs`, `setDialogScope`, `reloadDialogs`, `loadMoreMessages` - **Approvals** — `approveRequest`, `rejectRequest` Callbacks are bound through `activeStateRef` (a live ref updated every render) so their identities remain referentially stable across streaming chunks, preventing unnecessary downstream re-renders. ## Usage Example ```typescript // Guide-only consumer const chatState = useUnifiedChat({ activeMode: 'guide', modes: { guide: { systemPrompt: 'You are a helpful guide.' }, }, }) // Dual-mode consumer with host-managed Mingo state const chatState = useUnifiedChat({ activeMode: currentMode, // 'guide' | 'mingo' modes: { guide: { systemPrompt: '...' }, mingo: { dialogId: activeDialogId, getNatsWsUrl: () => natsWsUrl, publishUserMessage: (msg) => natsClient.publish(msg), }, }, mingoStateOverride: storeBackedMingoState, // survives panel unmounts }) chatState.sendMessage('Hello!', { attachments: [] }) ``` ## Notes - **Both hooks always run** regardless of `activeMode`; the inactive adapter receives a disabled fallback config and an `active: false` flag, keeping it completely idle while preserving its local message buffer for seamless mode-flips. - `mingoStateOverride` is the recommended seam for hosts that manage Mingo state externally (e.g., in a Zustand store); pass it without `modes.mingo` to avoid opening a redundant NATS subscription. - Mode-toggle UI is not part of this hook — it belongs in the `` shell component.