@tambo-ai/react 1.0.0-rc.3 → 1.0.0-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/model/component-metadata.d.ts +4 -4
  2. package/dist/model/component-metadata.js.map +1 -1
  3. package/dist/util/registry-validators.js +1 -1
  4. package/dist/util/registry-validators.js.map +1 -1
  5. package/dist/v1/hooks/use-tambo-v1-send-message.d.ts +1 -1
  6. package/dist/v1/hooks/use-tambo-v1-send-message.js +9 -9
  7. package/dist/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
  8. package/dist/v1/hooks/use-tambo-v1.d.ts +9 -0
  9. package/dist/v1/hooks/use-tambo-v1.d.ts.map +1 -1
  10. package/dist/v1/hooks/use-tambo-v1.js +31 -3
  11. package/dist/v1/hooks/use-tambo-v1.js.map +1 -1
  12. package/dist/v1/hooks/use-tambo-v1.test.js +122 -3
  13. package/dist/v1/hooks/use-tambo-v1.test.js.map +1 -1
  14. package/dist/v1/providers/tambo-v1-stream-context.test.js +2 -2
  15. package/dist/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
  16. package/dist/v1/types/event.d.ts +10 -1
  17. package/dist/v1/types/event.d.ts.map +1 -1
  18. package/dist/v1/types/event.js +3 -0
  19. package/dist/v1/types/event.js.map +1 -1
  20. package/dist/v1/types/event.test.js +30 -0
  21. package/dist/v1/types/event.test.js.map +1 -1
  22. package/dist/v1/types/message.d.ts +5 -2
  23. package/dist/v1/types/message.d.ts.map +1 -1
  24. package/dist/v1/types/message.js.map +1 -1
  25. package/dist/v1/types/thread.d.ts +2 -2
  26. package/dist/v1/types/thread.d.ts.map +1 -1
  27. package/dist/v1/types/thread.js.map +1 -1
  28. package/dist/v1/utils/event-accumulator.d.ts +5 -5
  29. package/dist/v1/utils/event-accumulator.d.ts.map +1 -1
  30. package/dist/v1/utils/event-accumulator.js +57 -3
  31. package/dist/v1/utils/event-accumulator.js.map +1 -1
  32. package/dist/v1/utils/event-accumulator.test.js +129 -11
  33. package/dist/v1/utils/event-accumulator.test.js.map +1 -1
  34. package/esm/model/component-metadata.d.ts +4 -4
  35. package/esm/model/component-metadata.js.map +1 -1
  36. package/esm/util/registry-validators.js +1 -1
  37. package/esm/util/registry-validators.js.map +1 -1
  38. package/esm/v1/hooks/use-tambo-v1-send-message.d.ts +1 -1
  39. package/esm/v1/hooks/use-tambo-v1-send-message.js +9 -9
  40. package/esm/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
  41. package/esm/v1/hooks/use-tambo-v1.d.ts +9 -0
  42. package/esm/v1/hooks/use-tambo-v1.d.ts.map +1 -1
  43. package/esm/v1/hooks/use-tambo-v1.js +32 -4
  44. package/esm/v1/hooks/use-tambo-v1.js.map +1 -1
  45. package/esm/v1/hooks/use-tambo-v1.test.js +122 -3
  46. package/esm/v1/hooks/use-tambo-v1.test.js.map +1 -1
  47. package/esm/v1/providers/tambo-v1-stream-context.test.js +2 -2
  48. package/esm/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
  49. package/esm/v1/types/event.d.ts +10 -1
  50. package/esm/v1/types/event.d.ts.map +1 -1
  51. package/esm/v1/types/event.js +3 -0
  52. package/esm/v1/types/event.js.map +1 -1
  53. package/esm/v1/types/event.test.js +31 -1
  54. package/esm/v1/types/event.test.js.map +1 -1
  55. package/esm/v1/types/message.d.ts +5 -2
  56. package/esm/v1/types/message.d.ts.map +1 -1
  57. package/esm/v1/types/message.js.map +1 -1
  58. package/esm/v1/types/thread.d.ts +2 -2
  59. package/esm/v1/types/thread.d.ts.map +1 -1
  60. package/esm/v1/types/thread.js.map +1 -1
  61. package/esm/v1/utils/event-accumulator.d.ts +5 -5
  62. package/esm/v1/utils/event-accumulator.d.ts.map +1 -1
  63. package/esm/v1/utils/event-accumulator.js +57 -3
  64. package/esm/v1/utils/event-accumulator.js.map +1 -1
  65. package/esm/v1/utils/event-accumulator.test.js +129 -11
  66. package/esm/v1/utils/event-accumulator.test.js.map +1 -1
  67. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"use-tambo-v1.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1.ts"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * useTambo - Main Hook\n *\n * Combines all contexts into a single hook for convenient access\n * to thread state, streaming status, registry, and client.\n */\n\nimport { EventType } from \"@ag-ui/core\";\nimport type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport React, {\n useCallback,\n useContext,\n useMemo,\n useRef,\n type ReactElement,\n} from \"react\";\nimport { useTamboClient } from \"../../providers/tambo-client-provider\";\nimport { useTamboConfig } from \"../providers/tambo-v1-provider\";\nimport { useTamboAuthState } from \"./use-tambo-v1-auth-state\";\nimport type { TamboAuthState } from \"../types/auth\";\nimport {\n TamboRegistryContext,\n type TamboRegistryContext as TamboRegistryContextType,\n} from \"../../providers/tambo-registry-provider\";\nimport { ComponentRenderer } from \"../components/v1-component-renderer\";\nimport {\n useStreamDispatch,\n useStreamState,\n useThreadManagement,\n type ThreadManagement,\n} from \"../providers/tambo-v1-stream-context\";\nimport type {\n Content,\n TamboToolDisplayProps,\n TamboThreadMessage,\n TamboToolUseContent,\n} from \"../types/message\";\nimport type { StreamingState } from \"../types/thread\";\nimport {\n isPlaceholderThreadId,\n type ThreadState,\n} from \"../utils/event-accumulator\";\n\n/**\n * Return type for useTambo hook\n */\nexport interface UseTamboReturn {\n /**\n * The Tambo API client instance\n */\n client: TamboAI;\n\n /**\n * Current thread state for the given threadId, or undefined if not loaded\n */\n thread: ThreadState | undefined;\n\n /**\n * Messages in the current thread\n */\n messages: TamboThreadMessage[];\n\n /**\n * Current streaming state\n */\n streamingState: StreamingState;\n\n /**\n * Whether the thread is currently streaming a response\n */\n isStreaming: boolean;\n\n /**\n * Whether the thread is waiting for the AI to start responding\n */\n isWaiting: boolean;\n\n /**\n * Whether the thread is idle (not streaming or waiting)\n */\n isIdle: boolean;\n\n /**\n * Register a component with the registry\n */\n registerComponent: TamboRegistryContextType[\"registerComponent\"];\n\n /**\n * Register a tool with the registry\n */\n registerTool: TamboRegistryContextType[\"registerTool\"];\n\n /**\n * Register multiple tools with the registry\n */\n registerTools: TamboRegistryContextType[\"registerTools\"];\n\n /**\n * The component registry (Map of name -> component definition)\n */\n componentList: TamboRegistryContextType[\"componentList\"];\n\n /**\n * The tool registry (Map of name -> tool definition)\n */\n toolRegistry: TamboRegistryContextType[\"toolRegistry\"];\n\n /**\n * Current thread ID (always available - uses \"placeholder\" for new threads)\n */\n currentThreadId: string;\n\n /**\n * Initialize a new thread in the stream context\n */\n initThread: ThreadManagement[\"initThread\"];\n\n /**\n * Switch the current active thread\n */\n switchThread: ThreadManagement[\"switchThread\"];\n\n /**\n * Start a new thread (generates a temporary ID)\n */\n startNewThread: ThreadManagement[\"startNewThread\"];\n\n /**\n * Dispatch function for stream events (advanced usage)\n */\n dispatch: ReturnType<typeof useStreamDispatch>;\n\n /**\n * Cancel the current run on this thread.\n * Optimistically updates local state and sends cancellation request to the API.\n * No-op if there's no active run or thread is a placeholder.\n */\n cancelRun: () => Promise<void>;\n\n /**\n * Current authentication state.\n * Use this to show auth-related UI or conditionally render features.\n */\n authState: TamboAuthState;\n\n /**\n * Shorthand for `authState.status === \"identified\"`.\n * When true, the SDK is ready to make API calls.\n */\n isIdentified: boolean;\n}\n\n/**\n * Main hook for accessing Tambo functionality.\n *\n * Combines thread state, streaming status, registry, and client\n * into a single convenient hook.\n *\n * Messages returned include renderedComponent on component content blocks,\n * allowing direct rendering via {content.renderedComponent}.\n * @param threadId - Optional thread ID to get state for\n * @returns Combined context with thread state, messages, and utilities\n * @example\n * ```tsx\n * function ChatInterface() {\n * const {\n * thread,\n * messages,\n * isStreaming,\n * registerComponent,\n * } = useTambo('thread_123');\n *\n * return (\n * <div>\n * {messages.map(msg => <Message key={msg.id} message={msg} />)}\n * {isStreaming && <LoadingIndicator />}\n * </div>\n * );\n * }\n * ```\n */\n/**\n * Cache entry for a rendered component wrapper.\n * Stores the element and the props JSON used to create it.\n */\ninterface ComponentCacheEntry {\n element: ReactElement;\n propsJson: string;\n}\n\n/**\n *\n * @returns The combined Tambo context\n */\nexport function useTambo(): UseTamboReturn {\n const client = useTamboClient();\n const { userKey } = useTamboConfig();\n const streamState = useStreamState();\n const dispatch = useStreamDispatch();\n const registry = useContext(TamboRegistryContext);\n const threadManagement = useThreadManagement();\n const authState = useTamboAuthState();\n\n // Cache for rendered component wrappers - maintains stable element references\n // across renders when props haven't changed\n const componentCacheRef = useRef<Map<string, ComponentCacheEntry>>(new Map());\n\n // Get thread state for the current thread\n const threadState = streamState.threadMap[streamState.currentThreadId];\n\n // Cancel the current run on this thread\n const cancelRun = useCallback(async () => {\n const runId = threadState?.streaming.runId;\n const threadId = streamState.currentThreadId;\n\n // No-op if there's no active run or thread is a placeholder\n if (!runId || isPlaceholderThreadId(threadId)) {\n return;\n }\n\n // Optimistically update local state with RUN_ERROR event\n dispatch({\n type: \"EVENT\",\n threadId,\n event: {\n type: EventType.RUN_ERROR,\n message: \"Run cancelled\",\n code: \"CANCELLED\",\n timestamp: Date.now(),\n },\n });\n\n // Call API to cancel the run\n try {\n await client.threads.runs.delete(runId, { threadId, userKey });\n } catch (error) {\n // Log but don't rethrow - local state is already updated\n console.warn(\"Failed to cancel run on server:\", error);\n }\n }, [\n client,\n userKey,\n streamState.currentThreadId,\n threadState?.streaming.runId,\n dispatch,\n ]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => {\n const thread = threadState;\n const rawMessages = thread?.thread.messages ?? [];\n const streamingState: StreamingState = thread?.streaming ?? {\n status: \"idle\" as const,\n };\n\n // Build a set of tool_use IDs that have completed (have a matching tool_result)\n // We need to look across all messages since tool_result might be in a different message\n const completedToolIds = new Set<string>();\n for (const msg of rawMessages) {\n for (const content of msg.content) {\n if (content.type === \"tool_result\") {\n completedToolIds.add(content.toolUseId);\n }\n }\n }\n\n // Transform messages to add computed properties to content blocks\n const messages = rawMessages.map((message): TamboThreadMessage => {\n const transformedContent = message.content.map((content): Content => {\n // Transform tool_use content to add computed state\n if (content.type === \"tool_use\") {\n const hasCompleted = completedToolIds.has(content.id);\n const input = content.input ?? {};\n\n // Extract Tambo display props from input\n const tamboDisplayProps: TamboToolDisplayProps = {\n _tambo_displayMessage:\n typeof input._tambo_displayMessage === \"string\"\n ? input._tambo_displayMessage\n : undefined,\n _tambo_statusMessage:\n typeof input._tambo_statusMessage === \"string\"\n ? input._tambo_statusMessage\n : undefined,\n _tambo_completionStatusMessage:\n typeof input._tambo_completionStatusMessage === \"string\"\n ? input._tambo_completionStatusMessage\n : undefined,\n };\n\n // Compute status message based on completion state\n const statusMessage = hasCompleted\n ? (tamboDisplayProps._tambo_completionStatusMessage ??\n `Called ${content.name}`)\n : (tamboDisplayProps._tambo_statusMessage ??\n `Calling ${content.name}`);\n\n // Filter out _tambo_* properties from input - consumers only see actual tool params\n const cleanInput: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(input)) {\n if (!key.startsWith(\"_tambo_\")) {\n cleanInput[key] = value;\n }\n }\n\n const v1Content: TamboToolUseContent = {\n ...content,\n input: cleanInput,\n hasCompleted,\n statusMessage,\n tamboDisplayProps,\n };\n return v1Content;\n }\n\n if (content.type !== \"component\") {\n return content;\n }\n\n const componentContent = content;\n const propsJson = JSON.stringify(componentContent.props ?? {});\n const cache = componentCacheRef.current;\n const cached = cache.get(componentContent.id);\n\n // Return cached element if props haven't changed\n if (cached?.propsJson === propsJson) {\n return {\n ...componentContent,\n renderedComponent: cached.element,\n };\n }\n\n // Create new wrapper element\n const element = React.createElement(ComponentRenderer, {\n key: componentContent.id,\n content: componentContent,\n threadId: streamState.currentThreadId,\n messageId: message.id,\n });\n\n // Update cache\n cache.set(componentContent.id, { element, propsJson });\n\n return {\n ...componentContent,\n renderedComponent: element,\n };\n });\n\n return {\n ...message,\n content: transformedContent,\n };\n });\n\n return {\n client,\n thread,\n messages,\n streamingState,\n isStreaming: streamingState.status === \"streaming\",\n isWaiting: streamingState.status === \"waiting\",\n isIdle: streamingState.status === \"idle\",\n registerComponent: registry.registerComponent,\n registerTool: registry.registerTool,\n registerTools: registry.registerTools,\n componentList: registry.componentList,\n toolRegistry: registry.toolRegistry,\n currentThreadId: streamState.currentThreadId,\n initThread: threadManagement.initThread,\n switchThread: threadManagement.switchThread,\n startNewThread: threadManagement.startNewThread,\n dispatch,\n cancelRun,\n authState,\n isIdentified: authState.status === \"identified\",\n };\n }, [\n cancelRun,\n client,\n threadState,\n registry,\n streamState.currentThreadId,\n threadManagement,\n dispatch,\n authState,\n ]);\n}\n"]}
1
+ {"version":3,"file":"use-tambo-v1.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1.ts"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * useTambo - Main Hook\n *\n * Combines all contexts into a single hook for convenient access\n * to thread state, streaming status, registry, and client.\n */\n\nimport { EventType } from \"@ag-ui/core\";\nimport type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport React, {\n useCallback,\n useContext,\n useMemo,\n useRef,\n type ReactElement,\n} from \"react\";\nimport {\n useTamboClient,\n useTamboQueryClient,\n} from \"../../providers/tambo-client-provider\";\nimport { useTamboConfig } from \"../providers/tambo-v1-provider\";\nimport { useTamboAuthState } from \"./use-tambo-v1-auth-state\";\nimport type { TamboAuthState } from \"../types/auth\";\nimport {\n TamboRegistryContext,\n type TamboRegistryContext as TamboRegistryContextType,\n} from \"../../providers/tambo-registry-provider\";\nimport { ComponentRenderer } from \"../components/v1-component-renderer\";\nimport {\n useStreamDispatch,\n useStreamState,\n useThreadManagement,\n type ThreadManagement,\n} from \"../providers/tambo-v1-stream-context\";\nimport type {\n Content,\n TamboToolDisplayProps,\n TamboThreadMessage,\n TamboToolUseContent,\n} from \"../types/message\";\nimport type { StreamingState } from \"../types/thread\";\nimport {\n isPlaceholderThreadId,\n type ThreadState,\n} from \"../utils/event-accumulator\";\n\n/**\n * Return type for useTambo hook\n */\nexport interface UseTamboReturn {\n /**\n * The Tambo API client instance\n */\n client: TamboAI;\n\n /**\n * Current thread state for the given threadId, or undefined if not loaded\n */\n thread: ThreadState | undefined;\n\n /**\n * Messages in the current thread\n */\n messages: TamboThreadMessage[];\n\n /**\n * Current streaming state\n */\n streamingState: StreamingState;\n\n /**\n * Whether the thread is currently streaming a response\n */\n isStreaming: boolean;\n\n /**\n * Whether the thread is waiting for the AI to start responding\n */\n isWaiting: boolean;\n\n /**\n * Whether the thread is idle (not streaming or waiting)\n */\n isIdle: boolean;\n\n /**\n * Register a component with the registry\n */\n registerComponent: TamboRegistryContextType[\"registerComponent\"];\n\n /**\n * Register a tool with the registry\n */\n registerTool: TamboRegistryContextType[\"registerTool\"];\n\n /**\n * Register multiple tools with the registry\n */\n registerTools: TamboRegistryContextType[\"registerTools\"];\n\n /**\n * The component registry (Map of name -> component definition)\n */\n componentList: TamboRegistryContextType[\"componentList\"];\n\n /**\n * The tool registry (Map of name -> tool definition)\n */\n toolRegistry: TamboRegistryContextType[\"toolRegistry\"];\n\n /**\n * Current thread ID (always available - uses \"placeholder\" for new threads)\n */\n currentThreadId: string;\n\n /**\n * Initialize a new thread in the stream context\n */\n initThread: ThreadManagement[\"initThread\"];\n\n /**\n * Switch the current active thread\n */\n switchThread: ThreadManagement[\"switchThread\"];\n\n /**\n * Start a new thread (generates a temporary ID)\n */\n startNewThread: ThreadManagement[\"startNewThread\"];\n\n /**\n * Dispatch function for stream events (advanced usage)\n */\n dispatch: ReturnType<typeof useStreamDispatch>;\n\n /**\n * Cancel the current run on this thread.\n * Optimistically updates local state and sends cancellation request to the API.\n * No-op if there's no active run or thread is a placeholder.\n */\n cancelRun: () => Promise<void>;\n\n /**\n * Current authentication state.\n * Use this to show auth-related UI or conditionally render features.\n */\n authState: TamboAuthState;\n\n /**\n * Shorthand for `authState.status === \"identified\"`.\n * When true, the SDK is ready to make API calls.\n */\n isIdentified: boolean;\n\n /**\n * Update a thread's name.\n * Useful for implementing manual thread renaming UI in history sidebars.\n * Cache invalidation is best-effort; failures will be logged and won't reject.\n * @param threadId - ID of the thread to rename\n * @param name - New name for the thread\n * @returns Promise that resolves when the update completes\n */\n updateThreadName: (threadId: string, name: string) => Promise<void>;\n}\n\n/**\n * Main hook for accessing Tambo functionality.\n *\n * Combines thread state, streaming status, registry, and client\n * into a single convenient hook.\n *\n * Messages returned include renderedComponent on component content blocks,\n * allowing direct rendering via {content.renderedComponent}.\n * @param threadId - Optional thread ID to get state for\n * @returns Combined context with thread state, messages, and utilities\n * @example\n * ```tsx\n * function ChatInterface() {\n * const {\n * thread,\n * messages,\n * isStreaming,\n * registerComponent,\n * } = useTambo('thread_123');\n *\n * return (\n * <div>\n * {messages.map(msg => <Message key={msg.id} message={msg} />)}\n * {isStreaming && <LoadingIndicator />}\n * </div>\n * );\n * }\n * ```\n */\n/**\n * Cache entry for a rendered component wrapper.\n * Stores the element and the props JSON used to create it.\n */\ninterface ComponentCacheEntry {\n element: ReactElement;\n propsJson: string;\n}\n\n/**\n *\n * @returns The combined Tambo context\n */\nexport function useTambo(): UseTamboReturn {\n const client = useTamboClient();\n const queryClient = useTamboQueryClient();\n const { userKey } = useTamboConfig();\n const streamState = useStreamState();\n const dispatch = useStreamDispatch();\n const registry = useContext(TamboRegistryContext);\n const threadManagement = useThreadManagement();\n const authState = useTamboAuthState();\n\n // Cache for rendered component wrappers - maintains stable element references\n // across renders when props haven't changed\n const componentCacheRef = useRef<Map<string, ComponentCacheEntry>>(new Map());\n\n // Get thread state for the current thread\n const threadState = streamState.threadMap[streamState.currentThreadId];\n\n // Keep a live snapshot of the threadMap for callbacks without forcing them to\n // re-create on every stream state update.\n const threadMapRef = useRef(streamState.threadMap);\n threadMapRef.current = streamState.threadMap;\n\n // Cancel the current run on this thread\n const cancelRun = useCallback(async () => {\n const runId = threadState?.streaming.runId;\n const threadId = streamState.currentThreadId;\n\n // No-op if there's no active run or thread is a placeholder\n if (!runId || isPlaceholderThreadId(threadId)) {\n return;\n }\n\n // Optimistically update local state with RUN_ERROR event\n dispatch({\n type: \"EVENT\",\n threadId,\n event: {\n type: EventType.RUN_ERROR,\n message: \"Run cancelled\",\n code: \"CANCELLED\",\n timestamp: Date.now(),\n },\n });\n\n // Call API to cancel the run\n try {\n await client.threads.runs.delete(runId, { threadId, userKey });\n } catch (error) {\n // Log but don't rethrow - local state is already updated\n console.warn(\"Failed to cancel run on server:\", error);\n }\n }, [\n client,\n userKey,\n streamState.currentThreadId,\n threadState?.streaming.runId,\n dispatch,\n ]);\n\n // Update a thread's name\n const updateThreadName = useCallback(\n async (threadId: string, name: string) => {\n await client.threads.update(threadId, { name });\n\n if (threadMapRef.current[threadId]) {\n dispatch({\n type: \"UPDATE_THREAD_NAME\",\n threadId,\n name: name,\n });\n }\n\n try {\n await Promise.all([\n queryClient.invalidateQueries({\n queryKey: [\"v1-threads\", \"list\"],\n }),\n queryClient.invalidateQueries({\n queryKey: [\"v1-threads\", threadId],\n }),\n ]);\n } catch (error) {\n console.warn(\n \"[useTambo] Failed to invalidate thread queries after rename:\",\n error,\n );\n }\n },\n [client, dispatch, queryClient],\n );\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => {\n const thread = threadState;\n const rawMessages = thread?.thread.messages ?? [];\n const streamingState: StreamingState = thread?.streaming ?? {\n status: \"idle\" as const,\n };\n\n // Build a set of tool_use IDs that have completed (have a matching tool_result)\n // We need to look across all messages since tool_result might be in a different message\n const completedToolIds = new Set<string>();\n for (const msg of rawMessages) {\n for (const content of msg.content) {\n if (content.type === \"tool_result\") {\n completedToolIds.add(content.toolUseId);\n }\n }\n }\n\n // Transform messages to add computed properties to content blocks\n const messages = rawMessages.map((message): TamboThreadMessage => {\n const transformedContent = message.content.map((content): Content => {\n // Transform tool_use content to add computed state\n if (content.type === \"tool_use\") {\n const hasCompleted = completedToolIds.has(content.id);\n const input = content.input ?? {};\n\n // Extract Tambo display props from input\n const tamboDisplayProps: TamboToolDisplayProps = {\n _tambo_statusMessage:\n typeof input._tambo_statusMessage === \"string\"\n ? input._tambo_statusMessage\n : undefined,\n _tambo_completionStatusMessage:\n typeof input._tambo_completionStatusMessage === \"string\"\n ? input._tambo_completionStatusMessage\n : undefined,\n };\n\n // Compute status message based on completion state\n const statusMessage = hasCompleted\n ? (tamboDisplayProps._tambo_completionStatusMessage ??\n `Called ${content.name}`)\n : (tamboDisplayProps._tambo_statusMessage ??\n `Calling ${content.name}`);\n\n // Filter out _tambo_* properties from input - consumers only see actual tool params\n const cleanInput: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(input)) {\n if (!key.startsWith(\"_tambo_\")) {\n cleanInput[key] = value;\n }\n }\n\n const v1Content: TamboToolUseContent = {\n ...content,\n input: cleanInput,\n hasCompleted,\n statusMessage,\n tamboDisplayProps,\n };\n return v1Content;\n }\n\n if (content.type !== \"component\") {\n return content;\n }\n\n const componentContent = content;\n const propsJson = JSON.stringify(componentContent.props ?? {});\n const cache = componentCacheRef.current;\n const cached = cache.get(componentContent.id);\n\n // Return cached element if props haven't changed\n if (cached?.propsJson === propsJson) {\n return {\n ...componentContent,\n renderedComponent: cached.element,\n };\n }\n\n // Create new wrapper element\n const element = React.createElement(ComponentRenderer, {\n key: componentContent.id,\n content: componentContent,\n threadId: streamState.currentThreadId,\n messageId: message.id,\n });\n\n // Update cache\n cache.set(componentContent.id, { element, propsJson });\n\n return {\n ...componentContent,\n renderedComponent: element,\n };\n });\n\n return {\n ...message,\n content: transformedContent,\n };\n });\n\n return {\n client,\n thread,\n messages,\n streamingState,\n isStreaming: streamingState.status === \"streaming\",\n isWaiting: streamingState.status === \"waiting\",\n isIdle: streamingState.status === \"idle\",\n registerComponent: registry.registerComponent,\n registerTool: registry.registerTool,\n registerTools: registry.registerTools,\n componentList: registry.componentList,\n toolRegistry: registry.toolRegistry,\n currentThreadId: streamState.currentThreadId,\n initThread: threadManagement.initThread,\n switchThread: threadManagement.switchThread,\n startNewThread: threadManagement.startNewThread,\n dispatch,\n cancelRun,\n authState,\n isIdentified: authState.status === \"identified\",\n updateThreadName,\n };\n }, [\n cancelRun,\n updateThreadName,\n client,\n threadState,\n registry,\n streamState.currentThreadId,\n threadManagement,\n dispatch,\n authState,\n ]);\n}\n"]}
@@ -68,9 +68,12 @@ describe("useTambo", () => {
68
68
  };
69
69
  }
70
70
  // Wrapper that uses the real reducer so state updates properly
71
- function createWrapperWithRealReducer(initialState) {
71
+ function createWrapperWithRealReducer(initialState, stateRef) {
72
72
  return function WrapperWithRealReducer({ children, }) {
73
73
  const [state, dispatch] = useReducer(streamReducer, initialState);
74
+ if (stateRef) {
75
+ stateRef.current = state;
76
+ }
74
77
  return (React.createElement(QueryClientProvider, { client: queryClient },
75
78
  React.createElement(TamboRegistryContext.Provider, { value: mockRegistry },
76
79
  React.createElement(TamboStreamProvider, { state: state, dispatch: dispatch }, children))));
@@ -85,6 +88,7 @@ describe("useTambo", () => {
85
88
  jest.mocked(useTamboClient).mockReturnValue(mockTamboClient);
86
89
  jest.mocked(useTamboQueryClient).mockReturnValue(queryClient);
87
90
  jest.clearAllMocks();
91
+ delete mockTamboClient.threads.update;
88
92
  });
89
93
  it("returns client from useTamboClient", () => {
90
94
  const { result } = renderHook(() => useTambo(), {
@@ -700,7 +704,6 @@ describe("useTambo", () => {
700
704
  units: "celsius",
701
705
  _tambo_statusMessage: "Fetching...",
702
706
  _tambo_completionStatusMessage: "Done",
703
- _tambo_displayMessage: "Weather lookup",
704
707
  },
705
708
  },
706
709
  ],
@@ -727,7 +730,6 @@ describe("useTambo", () => {
727
730
  expect(content.input).toEqual({ location: "NYC", units: "celsius" });
728
731
  expect(content.input._tambo_statusMessage).toBeUndefined();
729
732
  expect(content.input._tambo_completionStatusMessage).toBeUndefined();
730
- expect(content.input._tambo_displayMessage).toBeUndefined();
731
733
  });
732
734
  it("handles tool_use with empty input", () => {
733
735
  const state = {
@@ -1015,5 +1017,122 @@ describe("useTambo", () => {
1015
1017
  consoleWarnSpy.mockRestore();
1016
1018
  });
1017
1019
  });
1020
+ describe("updateThreadName", () => {
1021
+ it("updates local thread name and invalidates caches on success", async () => {
1022
+ const mockUpdate = jest.fn().mockResolvedValue({});
1023
+ // TypeScript SDK will be updated to include this method
1024
+ mockTamboClient.threads.update = mockUpdate;
1025
+ const invalidateQueriesSpy = jest.spyOn(queryClient, "invalidateQueries");
1026
+ const initialState = {
1027
+ threadMap: {
1028
+ thread_456: {
1029
+ thread: {
1030
+ id: "thread_456",
1031
+ messages: [],
1032
+ status: "idle",
1033
+ createdAt: new Date().toISOString(),
1034
+ updatedAt: new Date().toISOString(),
1035
+ lastRunCancelled: false,
1036
+ name: "Old Name",
1037
+ },
1038
+ streaming: { status: "idle" },
1039
+ accumulatingToolArgs: new Map(),
1040
+ },
1041
+ },
1042
+ currentThreadId: "thread_456",
1043
+ };
1044
+ const { result } = renderHook(() => useTambo(), {
1045
+ wrapper: createWrapperWithRealReducer(initialState),
1046
+ });
1047
+ await act(async () => {
1048
+ await result.current.updateThreadName("thread_456", "My New Thread");
1049
+ });
1050
+ expect(mockUpdate).toHaveBeenCalledWith("thread_456", {
1051
+ name: "My New Thread",
1052
+ });
1053
+ expect(result.current.thread?.thread.name).toBe("My New Thread");
1054
+ const invalidatedKeys = invalidateQueriesSpy.mock.calls
1055
+ .map(([arg]) => arg.queryKey)
1056
+ .filter(Boolean);
1057
+ expect(invalidatedKeys).toContainEqual(["v1-threads", "list"]);
1058
+ expect(invalidatedKeys).toContainEqual(["v1-threads", "thread_456"]);
1059
+ });
1060
+ it("does not create local thread state when thread isn't loaded", async () => {
1061
+ const mockUpdate = jest.fn().mockResolvedValue({});
1062
+ // TypeScript SDK will be updated to include this method
1063
+ mockTamboClient.threads.update = mockUpdate;
1064
+ const invalidateQueriesSpy = jest.spyOn(queryClient, "invalidateQueries");
1065
+ // Track the internal StreamState from the real reducer so we can assert we
1066
+ // don't create a local thread entry for threads that haven't been loaded.
1067
+ const stateRef = {
1068
+ current: undefined,
1069
+ };
1070
+ const initialState = {
1071
+ threadMap: {},
1072
+ currentThreadId: "placeholder",
1073
+ };
1074
+ const { result } = renderHook(() => useTambo(), {
1075
+ wrapper: createWrapperWithRealReducer(initialState, stateRef),
1076
+ });
1077
+ await act(async () => {
1078
+ await result.current.updateThreadName("thread_789", "New Title");
1079
+ });
1080
+ expect(mockUpdate).toHaveBeenCalledWith("thread_789", {
1081
+ name: "New Title",
1082
+ });
1083
+ const invalidatedKeys = invalidateQueriesSpy.mock.calls
1084
+ .map(([arg]) => arg.queryKey)
1085
+ .filter(Boolean);
1086
+ expect(invalidatedKeys).toContainEqual(["v1-threads", "list"]);
1087
+ expect(invalidatedKeys).toContainEqual(["v1-threads", "thread_789"]);
1088
+ expect(stateRef.current?.threadMap.thread_789).toBeUndefined();
1089
+ act(() => {
1090
+ result.current.switchThread("thread_789");
1091
+ });
1092
+ expect(result.current.thread).toBeUndefined();
1093
+ });
1094
+ it("propagates errors and does not update local state", async () => {
1095
+ const mockUpdate = jest
1096
+ .fn()
1097
+ .mockRejectedValue(new Error("Network error"));
1098
+ // TypeScript SDK will be updated to include this method
1099
+ mockTamboClient.threads.update = mockUpdate;
1100
+ const invalidateQueriesSpy = jest.spyOn(queryClient, "invalidateQueries");
1101
+ const initialState = {
1102
+ threadMap: {
1103
+ thread_456: {
1104
+ thread: {
1105
+ id: "thread_456",
1106
+ messages: [],
1107
+ status: "idle",
1108
+ createdAt: new Date().toISOString(),
1109
+ updatedAt: new Date().toISOString(),
1110
+ lastRunCancelled: false,
1111
+ name: "Old Name",
1112
+ },
1113
+ streaming: { status: "idle" },
1114
+ accumulatingToolArgs: new Map(),
1115
+ },
1116
+ },
1117
+ currentThreadId: "thread_456",
1118
+ };
1119
+ const { result } = renderHook(() => useTambo(), {
1120
+ wrapper: createWrapperWithRealReducer(initialState),
1121
+ });
1122
+ let caughtError;
1123
+ await act(async () => {
1124
+ try {
1125
+ await result.current.updateThreadName("thread_456", "My New Thread");
1126
+ }
1127
+ catch (error) {
1128
+ caughtError = error;
1129
+ }
1130
+ });
1131
+ expect(caughtError).toBeInstanceOf(Error);
1132
+ expect(caughtError.message).toBe("Network error");
1133
+ expect(result.current.thread?.thread.name).toBe("Old Name");
1134
+ expect(invalidateQueriesSpy).not.toHaveBeenCalled();
1135
+ });
1136
+ });
1018
1137
  });
1019
1138
  //# sourceMappingURL=use-tambo-v1.test.js.map