{"version":3,"file":"use-render-tool-call.mjs","names":[],"sources":["../../src/hooks/use-render-tool-call.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useSyncExternalStore } from \"react\";\nimport { ToolCall, ToolMessage } from \"@ag-ui/core\";\nimport { ToolCallStatus } from \"@copilotkitnext/core\";\nimport { useCopilotKit } from \"@/providers/CopilotKitProvider\";\nimport { useCopilotChatConfiguration } from \"@/providers/CopilotChatConfigurationProvider\";\nimport { DEFAULT_AGENT_ID } from \"@copilotkitnext/shared\";\nimport { partialJSONParse } from \"@copilotkitnext/shared\";\nimport { ReactToolCallRenderer } from \"@/types/react-tool-call-renderer\";\n\nexport interface UseRenderToolCallProps {\n  toolCall: ToolCall;\n  toolMessage?: ToolMessage;\n}\n\n/**\n * Props for the memoized ToolCallRenderer component\n */\ninterface ToolCallRendererProps {\n  toolCall: ToolCall;\n  toolMessage?: ToolMessage;\n  RenderComponent: ReactToolCallRenderer<unknown>[\"render\"];\n  isExecuting: boolean;\n}\n\n/**\n * Memoized component that renders a single tool call.\n * This prevents unnecessary re-renders when parent components update\n * but the tool call data hasn't changed.\n */\nconst ToolCallRenderer = React.memo(\n  function ToolCallRenderer({\n    toolCall,\n    toolMessage,\n    RenderComponent,\n    isExecuting,\n  }: ToolCallRendererProps) {\n    // Memoize args based on the arguments string to maintain stable reference\n    const args = useMemo(\n      () => partialJSONParse(toolCall.function.arguments),\n      [toolCall.function.arguments],\n    );\n\n    const toolName = toolCall.function.name;\n\n    // Render based on status to preserve discriminated union type inference\n    if (toolMessage) {\n      return (\n        <RenderComponent\n          name={toolName}\n          args={args}\n          status={ToolCallStatus.Complete}\n          result={toolMessage.content}\n        />\n      );\n    } else if (isExecuting) {\n      return (\n        <RenderComponent\n          name={toolName}\n          args={args}\n          status={ToolCallStatus.Executing}\n          result={undefined}\n        />\n      );\n    } else {\n      return (\n        <RenderComponent\n          name={toolName}\n          args={args}\n          status={ToolCallStatus.InProgress}\n          result={undefined}\n        />\n      );\n    }\n  },\n  // Custom comparison function to prevent re-renders when tool call data hasn't changed\n  (prevProps, nextProps) => {\n    // Compare tool call identity and content\n    if (prevProps.toolCall.id !== nextProps.toolCall.id) return false;\n    if (prevProps.toolCall.function.name !== nextProps.toolCall.function.name)\n      return false;\n    if (\n      prevProps.toolCall.function.arguments !==\n      nextProps.toolCall.function.arguments\n    )\n      return false;\n\n    // Compare tool message (result)\n    const prevResult = prevProps.toolMessage?.content;\n    const nextResult = nextProps.toolMessage?.content;\n    if (prevResult !== nextResult) return false;\n\n    // Compare executing state\n    if (prevProps.isExecuting !== nextProps.isExecuting) return false;\n\n    // Compare render component reference\n    if (prevProps.RenderComponent !== nextProps.RenderComponent) return false;\n\n    return true;\n  },\n);\n\n/**\n * Hook that returns a function to render tool calls based on the render functions\n * defined in CopilotKitProvider.\n *\n * @returns A function that takes a tool call and optional tool message and returns the rendered component\n */\nexport function useRenderToolCall() {\n  const { copilotkit, executingToolCallIds } = useCopilotKit();\n  const config = useCopilotChatConfiguration();\n  const agentId = config?.agentId ?? DEFAULT_AGENT_ID;\n\n  // Subscribe to render tool calls changes using useSyncExternalStore\n  // This ensures we always have the latest value, even if subscriptions run in any order\n  const renderToolCalls = useSyncExternalStore(\n    (callback) => {\n      return copilotkit.subscribe({\n        onRenderToolCallsChanged: callback,\n      }).unsubscribe;\n    },\n    () => copilotkit.renderToolCalls,\n    () => copilotkit.renderToolCalls,\n  );\n\n  // Note: executingToolCallIds is now provided by CopilotKitProvider context.\n  // This is critical for HITL reconnection: when connecting to a thread with\n  // pending tool calls, the onToolExecutionStart event fires before child components\n  // mount. By tracking at the provider level, the executing state is already\n  // available when this hook first runs.\n\n  const renderToolCall = useCallback(\n    ({\n      toolCall,\n      toolMessage,\n    }: UseRenderToolCallProps): React.ReactElement | null => {\n      // Find the render config for this tool call by name\n      // For rendering, we show all tool calls regardless of agentId\n      // The agentId scoping only affects handler execution (in core)\n      // Priority order:\n      // 1. Exact match by name (prefer agent-specific if multiple exist)\n      // 2. Wildcard (*) renderer\n      const exactMatches = renderToolCalls.filter(\n        (rc) => rc.name === toolCall.function.name,\n      );\n\n      // If multiple renderers with same name exist, prefer the one matching our agentId\n      const renderConfig =\n        exactMatches.find((rc) => rc.agentId === agentId) ||\n        exactMatches.find((rc) => !rc.agentId) ||\n        exactMatches[0] ||\n        renderToolCalls.find((rc) => rc.name === \"*\");\n\n      if (!renderConfig) {\n        return null;\n      }\n\n      const RenderComponent = renderConfig.render;\n      const isExecuting = executingToolCallIds.has(toolCall.id);\n\n      // Use the memoized ToolCallRenderer component to prevent unnecessary re-renders\n      return (\n        <ToolCallRenderer\n          key={toolCall.id}\n          toolCall={toolCall}\n          toolMessage={toolMessage}\n          RenderComponent={RenderComponent}\n          isExecuting={isExecuting}\n        />\n      );\n    },\n    [renderToolCalls, executingToolCallIds, agentId],\n  );\n\n  return renderToolCall;\n}\n"],"mappings":";;;;;;;;;;;;;AA6BA,MAAM,mBAAmB,MAAM,KAC7B,SAAS,iBAAiB,EACxB,UACA,aACA,iBACA,eACwB;CAExB,MAAM,OAAO,cACL,iBAAiB,SAAS,SAAS,UAAU,EACnD,CAAC,SAAS,SAAS,UAAU,CAC9B;CAED,MAAM,WAAW,SAAS,SAAS;AAGnC,KAAI,YACF,QACE,oBAAC;EACC,MAAM;EACA;EACN,QAAQ,eAAe;EACvB,QAAQ,YAAY;GACpB;UAEK,YACT,QACE,oBAAC;EACC,MAAM;EACA;EACN,QAAQ,eAAe;EACvB,QAAQ;GACR;KAGJ,QACE,oBAAC;EACC,MAAM;EACA;EACN,QAAQ,eAAe;EACvB,QAAQ;GACR;IAKP,WAAW,cAAc;AAExB,KAAI,UAAU,SAAS,OAAO,UAAU,SAAS,GAAI,QAAO;AAC5D,KAAI,UAAU,SAAS,SAAS,SAAS,UAAU,SAAS,SAAS,KACnE,QAAO;AACT,KACE,UAAU,SAAS,SAAS,cAC5B,UAAU,SAAS,SAAS,UAE5B,QAAO;AAKT,KAFmB,UAAU,aAAa,YACvB,UAAU,aAAa,QACX,QAAO;AAGtC,KAAI,UAAU,gBAAgB,UAAU,YAAa,QAAO;AAG5D,KAAI,UAAU,oBAAoB,UAAU,gBAAiB,QAAO;AAEpE,QAAO;EAEV;;;;;;;AAQD,SAAgB,oBAAoB;CAClC,MAAM,EAAE,YAAY,yBAAyB,eAAe;CAE5D,MAAM,UADS,6BAA6B,EACpB,WAAW;CAInC,MAAM,kBAAkB,sBACrB,aAAa;AACZ,SAAO,WAAW,UAAU,EAC1B,0BAA0B,UAC3B,CAAC,CAAC;UAEC,WAAW,uBACX,WAAW,gBAClB;AAmDD,QA3CuB,aACpB,EACC,UACA,kBACuD;EAOvD,MAAM,eAAe,gBAAgB,QAClC,OAAO,GAAG,SAAS,SAAS,SAAS,KACvC;EAGD,MAAM,eACJ,aAAa,MAAM,OAAO,GAAG,YAAY,QAAQ,IACjD,aAAa,MAAM,OAAO,CAAC,GAAG,QAAQ,IACtC,aAAa,MACb,gBAAgB,MAAM,OAAO,GAAG,SAAS,IAAI;AAE/C,MAAI,CAAC,aACH,QAAO;EAGT,MAAM,kBAAkB,aAAa;AAIrC,SACE,oBAAC;GAEW;GACG;GACI;GACjB,aATgB,qBAAqB,IAAI,SAAS,GAAG;KAKhD,SAAS,GAKd;IAGN;EAAC;EAAiB;EAAsB;EAAQ,CACjD"}