feat(23-03): wire useBrainstormerDefault and handleHandoff into ChatPanel

- Import useBrainstormerDefault hook and useToast context
- Auto-select general agent for new conversations (when activeConversationId is null)
- Add handleHandoff callback: calls chatApi.handoffSpec, invalidates messages cache, shows error toast on failure
- Pass onHandoff={handleHandoff} to ChatMessageList
This commit is contained in:
Nexus Dev 2026-04-01 21:59:51 +00:00
parent 71839e0032
commit 92d0ada080

View file

@ -1,8 +1,9 @@
import { useState, useMemo } from "react";
import { useState, useMemo, useEffect, useCallback } from "react";
import { X } from "lucide-react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useChatPanel } from "../context/ChatPanelContext";
import { useCompany } from "../context/CompanyContext";
import { useToast } from "../context/ToastContext";
import { ChatInput } from "./ChatInput";
import { ChatConversationList } from "./ChatConversationList";
import { ChatMessageList } from "./ChatMessageList";
@ -13,6 +14,7 @@ import { chatApi } from "../api/chat";
import { agentsApi } from "../api/agents";
import { useChatMessages } from "../hooks/useChatMessages";
import { useStreamingChat } from "../hooks/useStreamingChat";
import { useBrainstormerDefault } from "../hooks/useBrainstormerDefault";
import { resolveAgentFromContent } from "../lib/slash-commands";
import type { AgentRole } from "@paperclipai/shared";
@ -20,12 +22,33 @@ export function ChatPanel() {
const { chatOpen, setChatOpen, activeConversationId, setActiveConversationId } = useChatPanel();
const { selectedCompanyId } = useCompany();
const queryClient = useQueryClient();
const { pushToast } = useToast();
const [isSending, setIsSending] = useState(false);
const [activeAgentId, setActiveAgentId] = useState<string | null>(null);
const { messages } = useChatMessages(activeConversationId);
const { streamingContent, isStreaming, startStream, stop } = useStreamingChat(activeConversationId);
const brainstormerDefaultId = useBrainstormerDefault();
// Auto-select brainstormer (general agent) for new conversations
useEffect(() => {
if (activeAgentId === null && brainstormerDefaultId !== null && !activeConversationId) {
setActiveAgentId(brainstormerDefaultId);
}
}, [activeAgentId, brainstormerDefaultId, activeConversationId]);
// Handoff callback: call handoff API and invalidate messages cache
const handleHandoff = useCallback(async (spec: { what: string; why: string; constraints: string; success: string }) => {
if (!activeConversationId) return;
try {
await chatApi.handoffSpec(activeConversationId, spec, "pm");
queryClient.invalidateQueries({ queryKey: ["chat", "messages", activeConversationId] });
} catch {
pushToast({ title: "Could not send to PM. Try again.", tone: "error" });
}
}, [activeConversationId, queryClient, pushToast]);
// Load agents for routing and identity
const { data: agents = [], isLoading: agentsLoading } = useQuery({
queryKey: ["agents", selectedCompanyId],
@ -179,6 +202,7 @@ export function ChatPanel() {
streamingAgentRole={streamingAgent?.role ?? null}
onEdit={handleEdit}
onRetry={handleRetry}
onHandoff={handleHandoff}
agentMap={agentMap}
/>
) : (