import { useEffect } from "react"; import { useCopilotKit } from "../providers/CopilotKitProvider"; import type { ReactFrontendTool } from "../types/frontend-tool"; const EMPTY_DEPS: ReadonlyArray = []; export function useFrontendTool< T extends Record = Record, >(tool: ReactFrontendTool, deps?: ReadonlyArray) { const { copilotkit } = useCopilotKit(); const extraDeps = deps ?? EMPTY_DEPS; useEffect(() => { const name = tool.name; // Always register/override the tool for this name on mount if (copilotkit.getTool({ toolName: name, agentId: tool.agentId })) { console.warn( `Tool '${name}' already exists for agent '${tool.agentId || "global"}'. Overriding with latest registration.`, ); copilotkit.removeTool(name, tool.agentId); } copilotkit.addTool(tool); // Register/override renderer by name and agentId through core. // The render function is registered even when tool.parameters is // undefined — tools like HITL confirm dialogs have no parameters // but still need their UI rendered in the chat. if (tool.render) { copilotkit.addHookRenderToolCall({ name, args: tool.parameters, agentId: tool.agentId, render: tool.render, }); } return () => { copilotkit.removeTool(name, tool.agentId); // we are intentionally not removing the render here so that the tools can still render in the chat history }; // Depend on stable keys by default and allow callers to opt into // additional dependencies for dynamic tool configuration. // tool.available is included so toggling availability re-registers the tool. }, [tool.name, tool.available, copilotkit, extraDeps.length, ...extraDeps]); }