11"use client" ;
22
3+ import { isAgentTool } from "@shared" ;
34import { Bot , Wrench } from "lucide-react" ;
4- import { type ReactNode , useCallback , useEffect , useState } from "react" ;
5+ import {
6+ type ReactNode ,
7+ useCallback ,
8+ useEffect ,
9+ useMemo ,
10+ useState ,
11+ } from "react" ;
512import { Button } from "@/components/ui/button" ;
613import { Checkbox } from "@/components/ui/checkbox" ;
714import { ExpandableText } from "@/components/ui/expandable-text" ;
@@ -11,7 +18,18 @@ import {
1118 HoverCardTrigger ,
1219} from "@/components/ui/hover-card" ;
1320import { useAgentDelegations } from "@/lib/agent-tools.query" ;
14- import { useChatProfileMcpTools } from "@/lib/chat.query" ;
21+ import {
22+ useChatProfileMcpTools ,
23+ useConversationEnabledTools ,
24+ useProfileToolsWithIds ,
25+ useUpdateConversationEnabledTools ,
26+ } from "@/lib/chat.query" ;
27+ import {
28+ addPendingAction ,
29+ applyPendingActions ,
30+ getPendingActions ,
31+ type PendingToolAction ,
32+ } from "@/lib/pending-tool-state" ;
1533import { cn } from "@/lib/utils" ;
1634
1735// Component to display tools for a specific agent
@@ -52,78 +70,145 @@ interface AgentToolsDisplayProps {
5270 addAgentsButton : ReactNode ;
5371}
5472
55- // Local storage key for disabled delegations
56- const getStorageKey = ( agentId : string , conversationId ?: string ) =>
57- `disabled-delegations:${ agentId } :${ conversationId || "initial" } ` ;
58-
5973/**
6074 * Display agent delegations (agents this agent can delegate to).
61- * Uses the agent_tools/delegations data like the canvas .
62- * Supports enable/disable toggle with state persisted in localStorage .
75+ * Uses database-backed enabled tools state (same as ChatToolsDisplay for MCP tools) .
76+ * Supports enable/disable toggle persisted via conversation_enabled_tools API .
6377 */
6478export function AgentToolsDisplay ( {
6579 agentId,
6680 conversationId,
6781 addAgentsButton,
6882} : AgentToolsDisplayProps ) {
69- // Fetch delegated agents from agent_tools (like canvas )
70- const { data : delegatedAgents = [ ] , isLoading } =
83+ // Fetch delegated agents for display info (name, description )
84+ const { data : delegatedAgents = [ ] , isLoading : isLoadingAgents } =
7185 useAgentDelegations ( agentId ) ;
7286
73- // Track disabled delegation agent IDs
74- const [ disabledAgentIds , setDisabledAgentIds ] = useState < Set < string > > (
75- new Set ( ) ,
87+ // Fetch all profile tools to get delegation tool IDs
88+ const { data : profileTools = [ ] , isLoading : isLoadingTools } =
89+ useProfileToolsWithIds ( agentId ) ;
90+
91+ // Filter for delegation tools only (tools with name starting with agent__)
92+ const delegationTools = useMemo (
93+ ( ) => profileTools . filter ( ( tool ) => isAgentTool ( tool . name ) ) ,
94+ [ profileTools ] ,
7695 ) ;
7796
78- // Load disabled state from localStorage on mount
79- useEffect ( ( ) => {
80- const storageKey = getStorageKey ( agentId , conversationId ) ;
81- const stored = localStorage . getItem ( storageKey ) ;
82- if ( stored ) {
83- try {
84- const parsed = JSON . parse ( stored ) ;
85- if ( Array . isArray ( parsed ) ) {
86- setDisabledAgentIds ( new Set ( parsed ) ) ;
87- }
88- } catch {
89- // Ignore invalid JSON
97+ // Create a map from target agent ID to tool ID for quick lookup
98+ const targetAgentToToolId = useMemo ( ( ) => {
99+ const map = new Map < string , string > ( ) ;
100+ for ( const tool of delegationTools ) {
101+ if ( tool . delegateToAgentId ) {
102+ map . set ( tool . delegateToAgentId , tool . id ) ;
90103 }
104+ }
105+ return map ;
106+ } , [ delegationTools ] ) ;
107+
108+ // Local pending actions for display (synced with localStorage)
109+ const [ localPendingActions , setLocalPendingActions ] = useState <
110+ PendingToolAction [ ]
111+ > ( [ ] ) ;
112+
113+ // Load pending actions from localStorage on mount and when context changes
114+ useEffect ( ( ) => {
115+ if ( ! conversationId ) {
116+ const actions = getPendingActions ( agentId ) ;
117+ setLocalPendingActions ( actions ) ;
91118 } else {
92- setDisabledAgentIds ( new Set ( ) ) ;
119+ setLocalPendingActions ( [ ] ) ;
93120 }
94121 } , [ agentId , conversationId ] ) ;
95122
96- // Save disabled state to localStorage
97- const saveDisabledState = useCallback (
98- ( newDisabled : Set < string > ) => {
99- const storageKey = getStorageKey ( agentId , conversationId ) ;
100- localStorage . setItem ( storageKey , JSON . stringify ( [ ...newDisabled ] ) ) ;
123+ // Fetch enabled tools for the conversation
124+ const { data : enabledToolsData } =
125+ useConversationEnabledTools ( conversationId ) ;
126+ const enabledToolIds = enabledToolsData ?. enabledToolIds ?? [ ] ;
127+ const hasCustomSelection = enabledToolsData ?. hasCustomSelection ?? false ;
128+
129+ // Mutation for updating enabled tools
130+ const updateEnabledTools = useUpdateConversationEnabledTools ( ) ;
131+
132+ // Default enabled tools: all delegation tools are enabled by default
133+ const defaultEnabledToolIds = useMemo (
134+ ( ) => delegationTools . map ( ( t ) => t . id ) ,
135+ [ delegationTools ] ,
136+ ) ;
137+
138+ // Compute current enabled tools (same pattern as ChatToolsDisplay)
139+ const currentEnabledToolIds = useMemo ( ( ) => {
140+ if ( conversationId && hasCustomSelection ) {
141+ return enabledToolIds ;
142+ }
143+
144+ // Start with defaults (all delegation tools enabled)
145+ const baseIds = defaultEnabledToolIds ;
146+
147+ // If no conversation, apply pending actions for display
148+ if ( ! conversationId && localPendingActions . length > 0 ) {
149+ return applyPendingActions ( baseIds , localPendingActions ) ;
150+ }
151+
152+ return baseIds ;
153+ } , [
154+ conversationId ,
155+ hasCustomSelection ,
156+ enabledToolIds ,
157+ defaultEnabledToolIds ,
158+ localPendingActions ,
159+ ] ) ;
160+
161+ const enabledToolIdsSet = new Set ( currentEnabledToolIds ) ;
162+
163+ // Check if a delegation is enabled (by target agent ID)
164+ const isEnabled = useCallback (
165+ ( targetAgentId : string ) => {
166+ const toolId = targetAgentToToolId . get ( targetAgentId ) ;
167+ if ( ! toolId ) return true ; // Default to enabled if tool not found
168+ return enabledToolIdsSet . has ( toolId ) ;
101169 } ,
102- [ agentId , conversationId ] ,
170+ [ targetAgentToToolId , enabledToolIdsSet ] ,
103171 ) ;
104172
105- // Toggle delegation enabled/disabled
173+ // Handle toggling a delegation (by target agent ID)
106174 const handleToggle = useCallback (
107175 ( targetAgentId : string ) => {
108- setDisabledAgentIds ( ( prev ) => {
109- const newSet = new Set ( prev ) ;
110- if ( newSet . has ( targetAgentId ) ) {
111- newSet . delete ( targetAgentId ) ;
112- } else {
113- newSet . add ( targetAgentId ) ;
114- }
115- saveDisabledState ( newSet ) ;
116- return newSet ;
176+ const toolId = targetAgentToToolId . get ( targetAgentId ) ;
177+ if ( ! toolId ) return ;
178+
179+ const currentlyEnabled = enabledToolIdsSet . has ( toolId ) ;
180+
181+ if ( ! conversationId ) {
182+ // Store in localStorage and update local state
183+ const action : PendingToolAction = currentlyEnabled
184+ ? { type : "disable" , toolId }
185+ : { type : "enable" , toolId } ;
186+ addPendingAction ( action , agentId ) ;
187+ setLocalPendingActions ( ( prev ) => [ ...prev , action ] ) ;
188+ return ;
189+ }
190+
191+ // Update via API
192+ const newEnabledToolIds = currentlyEnabled
193+ ? currentEnabledToolIds . filter ( ( id ) => id !== toolId )
194+ : [ ...currentEnabledToolIds , toolId ] ;
195+
196+ updateEnabledTools . mutateAsync ( {
197+ conversationId,
198+ toolIds : newEnabledToolIds ,
117199 } ) ;
118200 } ,
119- [ saveDisabledState ] ,
201+ [
202+ targetAgentToToolId ,
203+ enabledToolIdsSet ,
204+ conversationId ,
205+ agentId ,
206+ currentEnabledToolIds ,
207+ updateEnabledTools ,
208+ ] ,
120209 ) ;
121210
122- // Check if a delegation is enabled
123- const isEnabled = useCallback (
124- ( targetAgentId : string ) => ! disabledAgentIds . has ( targetAgentId ) ,
125- [ disabledAgentIds ] ,
126- ) ;
211+ const isLoading = isLoadingAgents || isLoadingTools ;
127212
128213 if ( isLoading || delegatedAgents . length === 0 ) {
129214 return null ;
0 commit comments