@assistant-ui/react 0.11.45 → 0.11.47

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 (114) hide show
  1. package/dist/augmentations.d.ts.map +1 -1
  2. package/dist/client/ThreadMessageClient.js +2 -2
  3. package/dist/client/ThreadMessageClient.js.map +1 -1
  4. package/dist/client/types/ModelContext.d.ts.map +1 -1
  5. package/dist/context/react/AssistantApiContext.d.ts.map +1 -1
  6. package/dist/context/react/AssistantApiContext.js.map +1 -1
  7. package/dist/context/react/utils/createStateHookForRuntime.d.ts.map +1 -1
  8. package/dist/context/react/utils/createStateHookForRuntime.js.map +1 -1
  9. package/dist/context/react/utils/ensureBinding.js.map +1 -1
  10. package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts +3 -3
  11. package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts.map +1 -1
  12. package/dist/legacy-runtime/AssistantRuntimeProvider.js +3 -6
  13. package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
  14. package/dist/legacy-runtime/client/MessagePartRuntimeClient.js +1 -1
  15. package/dist/legacy-runtime/client/MessagePartRuntimeClient.js.map +1 -1
  16. package/dist/legacy-runtime/client/MessageRuntimeClient.js +1 -1
  17. package/dist/legacy-runtime/client/MessageRuntimeClient.js.map +1 -1
  18. package/dist/legacy-runtime/cloud/auiV0.js +1 -1
  19. package/dist/legacy-runtime/cloud/auiV0.js.map +1 -1
  20. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.d.ts.map +1 -1
  21. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.js +2 -1
  22. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.js.map +1 -1
  23. package/dist/legacy-runtime/runtime/AssistantRuntime.d.ts.map +1 -1
  24. package/dist/legacy-runtime/runtime/AssistantRuntime.js.map +1 -1
  25. package/dist/legacy-runtime/runtime/ComposerRuntime.js +2 -2
  26. package/dist/legacy-runtime/runtime/ComposerRuntime.js.map +1 -1
  27. package/dist/legacy-runtime/runtime/MessageRuntime.js +3 -3
  28. package/dist/legacy-runtime/runtime/MessageRuntime.js.map +1 -1
  29. package/dist/legacy-runtime/runtime/ThreadListRuntime.js +1 -1
  30. package/dist/legacy-runtime/runtime/ThreadListRuntime.js.map +1 -1
  31. package/dist/legacy-runtime/runtime/ThreadRuntime.js +2 -2
  32. package/dist/legacy-runtime/runtime/ThreadRuntime.js.map +1 -1
  33. package/dist/legacy-runtime/runtime-cores/adapters/RuntimeAdapterProvider.d.ts +1 -2
  34. package/dist/legacy-runtime/runtime-cores/adapters/RuntimeAdapterProvider.d.ts.map +1 -1
  35. package/dist/legacy-runtime/runtime-cores/adapters/RuntimeAdapterProvider.js.map +1 -1
  36. package/dist/legacy-runtime/runtime-cores/adapters/attachment/CompositeAttachmentAdapter.js +2 -2
  37. package/dist/legacy-runtime/runtime-cores/adapters/attachment/CompositeAttachmentAdapter.js.map +1 -1
  38. package/dist/legacy-runtime/runtime-cores/adapters/feedback/index.d.ts +1 -1
  39. package/dist/legacy-runtime/runtime-cores/adapters/feedback/index.d.ts.map +1 -1
  40. package/dist/legacy-runtime/runtime-cores/adapters/suggestion/index.d.ts +1 -1
  41. package/dist/legacy-runtime/runtime-cores/adapters/suggestion/index.d.ts.map +1 -1
  42. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
  43. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +2 -1
  44. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  45. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.d.ts.map +1 -1
  46. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.js +1 -1
  47. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.js.map +1 -1
  48. package/dist/legacy-runtime/runtime-cores/local/index.d.ts +1 -1
  49. package/dist/legacy-runtime/runtime-cores/local/index.d.ts.map +1 -1
  50. package/dist/legacy-runtime/runtime-cores/local/index.js +2 -3
  51. package/dist/legacy-runtime/runtime-cores/local/index.js.map +1 -1
  52. package/dist/legacy-runtime/runtime-cores/local/useLocalRuntime.d.ts +0 -2
  53. package/dist/legacy-runtime/runtime-cores/local/useLocalRuntime.d.ts.map +1 -1
  54. package/dist/legacy-runtime/runtime-cores/local/useLocalRuntime.js +3 -3
  55. package/dist/legacy-runtime/runtime-cores/local/useLocalRuntime.js.map +1 -1
  56. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  57. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.js.map +1 -1
  58. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  59. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  60. package/dist/legacy-runtime/runtime-cores/remote-thread-list/types.d.ts +6 -0
  61. package/dist/legacy-runtime/runtime-cores/remote-thread-list/types.d.ts.map +1 -1
  62. package/dist/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.d.ts.map +1 -1
  63. package/dist/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.js +15 -1
  64. package/dist/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.js.map +1 -1
  65. package/dist/primitives/actionBar/ActionBarCopy.d.ts.map +1 -1
  66. package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -1
  67. package/dist/primitives/message/MessageError.js +1 -2
  68. package/dist/primitives/message/MessageError.js.map +1 -1
  69. package/dist/primitives/message/MessageParts.d.ts.map +1 -1
  70. package/dist/primitives/message/MessageParts.js.map +1 -1
  71. package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
  72. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  73. package/dist/primitives/messagePart/useMessagePartData.d.ts.map +1 -1
  74. package/dist/primitives/messagePart/useMessagePartData.js.map +1 -1
  75. package/dist/types/index.d.ts.map +1 -1
  76. package/dist/utils/smooth/useSmooth.js +2 -2
  77. package/dist/utils/smooth/useSmooth.js.map +1 -1
  78. package/package.json +2 -5
  79. package/src/augmentations.ts +0 -2
  80. package/src/client/ThreadMessageClient.tsx +2 -2
  81. package/src/client/types/ModelContext.ts +0 -1
  82. package/src/context/react/AssistantApiContext.tsx +0 -2
  83. package/src/context/react/utils/createStateHookForRuntime.ts +0 -1
  84. package/src/context/react/utils/ensureBinding.ts +1 -1
  85. package/src/legacy-runtime/AssistantRuntimeProvider.tsx +5 -6
  86. package/src/legacy-runtime/client/MessagePartRuntimeClient.ts +1 -1
  87. package/src/legacy-runtime/client/MessageRuntimeClient.ts +1 -1
  88. package/src/legacy-runtime/cloud/auiV0.ts +1 -1
  89. package/src/legacy-runtime/cloud/useCloudThreadListRuntime.tsx +1 -0
  90. package/src/legacy-runtime/runtime/AssistantRuntime.ts +3 -1
  91. package/src/legacy-runtime/runtime/ComposerRuntime.ts +2 -2
  92. package/src/legacy-runtime/runtime/MessageRuntime.ts +3 -3
  93. package/src/legacy-runtime/runtime/ThreadListRuntime.ts +1 -1
  94. package/src/legacy-runtime/runtime/ThreadRuntime.ts +2 -2
  95. package/src/legacy-runtime/runtime-cores/adapters/RuntimeAdapterProvider.tsx +1 -1
  96. package/src/legacy-runtime/runtime-cores/adapters/attachment/CompositeAttachmentAdapter.ts +2 -2
  97. package/src/legacy-runtime/runtime-cores/adapters/feedback/index.ts +1 -1
  98. package/src/legacy-runtime/runtime-cores/adapters/suggestion/index.ts +1 -1
  99. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.tsx +1 -0
  100. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.tsx +1 -1
  101. package/src/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.tsx +1 -1
  102. package/src/legacy-runtime/runtime-cores/local/index.ts +1 -1
  103. package/src/legacy-runtime/runtime-cores/local/useLocalRuntime.tsx +2 -1
  104. package/src/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.tsx +0 -2
  105. package/src/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.tsx +0 -2
  106. package/src/legacy-runtime/runtime-cores/remote-thread-list/types.tsx +7 -0
  107. package/src/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.tsx +25 -1
  108. package/src/primitives/actionBar/ActionBarCopy.tsx +3 -1
  109. package/src/primitives/message/MessageError.tsx +1 -1
  110. package/src/primitives/message/MessageParts.tsx +0 -1
  111. package/src/primitives/message/MessagePartsGrouped.tsx +0 -1
  112. package/src/primitives/messagePart/useMessagePartData.tsx +1 -1
  113. package/src/types/index.ts +0 -1
  114. package/src/utils/smooth/useSmooth.tsx +2 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/primitives/message/MessagePartsGrouped.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n type FC,\n memo,\n PropsWithChildren,\n useMemo,\n} from \"react\";\nimport {\n useAssistantState,\n PartByIndexProvider,\n useAssistantApi,\n TextMessagePartProvider,\n} from \"../../context\";\nimport { MessagePartPrimitiveText } from \"../messagePart/MessagePartText\";\nimport { MessagePartPrimitiveImage } from \"../messagePart/MessagePartImage\";\nimport type {\n Unstable_AudioMessagePartComponent,\n EmptyMessagePartComponent,\n TextMessagePartComponent,\n ImageMessagePartComponent,\n SourceMessagePartComponent,\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n FileMessagePartComponent,\n ReasoningMessagePartComponent,\n} from \"../../types/MessagePartComponentTypes\";\nimport { MessagePartPrimitiveInProgress } from \"../messagePart/MessagePartInProgress\";\nimport { MessagePartStatus } from \"../../types/AssistantTypes\";\n\ntype MessagePartGroup = {\n groupKey: string | undefined;\n indices: number[];\n};\n\nexport type GroupingFunction = (parts: readonly any[]) => MessagePartGroup[];\n\n/**\n * Groups message parts by their parent ID.\n * Parts without a parent ID appear in their chronological position as individual groups.\n * Parts with the same parent ID are grouped together at the position of their first occurrence.\n */\nconst groupMessagePartsByParentId: GroupingFunction = (\n parts: readonly any[],\n): MessagePartGroup[] => {\n // Map maintains insertion order, so groups appear in order of first occurrence\n const groupMap = new Map<string, number[]>();\n\n // Process each part in order\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const parentId = part?.parentId as string | undefined;\n\n // For parts without parentId, assign a unique group ID to maintain their position\n const groupId = parentId ?? `__ungrouped_${i}`;\n\n // Get or create the indices array for this group\n const indices = groupMap.get(groupId) ?? [];\n indices.push(i);\n groupMap.set(groupId, indices);\n }\n\n // Convert map to array of groups\n const groups: MessagePartGroup[] = [];\n for (const [groupId, indices] of groupMap) {\n // Extract parentId (undefined for ungrouped parts)\n const groupKey = groupId.startsWith(\"__ungrouped_\") ? undefined : groupId;\n groups.push({ groupKey, indices });\n }\n\n return groups;\n};\n\nconst useMessagePartsGrouped = (\n groupingFunction: GroupingFunction,\n): MessagePartGroup[] => {\n const parts = useAssistantState(({ message }) => message.parts);\n\n return useMemo(() => {\n if (parts.length === 0) {\n return [];\n }\n return groupingFunction(parts);\n }, [parts, groupingFunction]);\n};\n\nexport namespace MessagePrimitiveUnstable_PartsGrouped {\n export type Props = {\n /**\n * Function that takes an array of message parts and returns an array of groups.\n * Each group contains a key (for identification) and an array of indices.\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * groupingFunction={(parts) => {\n * const groups = new Map<string, number[]>();\n * parts.forEach((part, i) => {\n * const key = part.parentId ?? `__ungrouped_${i}`;\n * const indices = groups.get(key) ?? [];\n * indices.push(i);\n * groups.set(key, indices);\n * });\n * return Array.from(groups.entries()).map(([key, indices]) => ({\n * key: key.startsWith(\"__ungrouped_\") ? undefined : key,\n * indices\n * }));\n * }}\n * ```\n *\n * @example\n * ```tsx\n * // Group by tool name\n * import { groupMessagePartsByToolName } from \"@assistant-ui/react\";\n *\n * <MessagePrimitive.Unstable_PartsGrouped\n * groupingFunction={groupMessagePartsByToolName}\n * components={{\n * Group: ({ key, indices, children }) => {\n * if (!key) return <>{children}</>;\n * return (\n * <div className=\"tool-group\">\n * <h4>Tool: {key}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\n groupingFunction: GroupingFunction;\n\n /**\n * Component configuration for rendering different types of message content.\n *\n * You can provide custom components for each content type (text, image, file, etc.)\n * and configure tool rendering behavior. If not provided, default components will be used.\n */\n components:\n | {\n /** Component for rendering empty messages */\n Empty?: EmptyMessagePartComponent | undefined;\n /** Component for rendering text content */\n Text?: TextMessagePartComponent | undefined;\n /** Component for rendering reasoning content (typically hidden) */\n Reasoning?: ReasoningMessagePartComponent | undefined;\n /** Component for rendering source content */\n Source?: SourceMessagePartComponent | undefined;\n /** Component for rendering image content */\n Image?: ImageMessagePartComponent | undefined;\n /** Component for rendering file content */\n File?: FileMessagePartComponent | undefined;\n /** Component for rendering audio content (experimental) */\n Unstable_Audio?: Unstable_AudioMessagePartComponent | undefined;\n /** Configuration for tool call rendering */\n tools?:\n | {\n /** Map of tool names to their specific components */\n by_name?:\n | Record<string, ToolCallMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unregistered tools */\n Fallback?: ComponentType<ToolCallMessagePartProps> | undefined;\n }\n | {\n /** Override component that handles all tool calls */\n Override: ComponentType<ToolCallMessagePartProps>;\n }\n | undefined;\n\n /**\n * Component for rendering grouped message parts.\n *\n * When provided, this component will automatically wrap message parts that share\n * the same group key as determined by the groupingFunction.\n *\n * The component receives:\n * - `groupKey`: The group key (or undefined for ungrouped parts)\n * - `indices`: Array of indices for the parts in this group\n * - `children`: The rendered message part components\n *\n * @example\n * ```tsx\n * // Collapsible group\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <details className=\"message-group\">\n * <summary>\n * Group {groupKey} ({indices.length} parts)\n * </summary>\n * <div className=\"group-content\">\n * {children}\n * </div>\n * </details>\n * );\n * }\n * ```\n *\n * @param groupKey - The group key (undefined for ungrouped parts)\n * @param indices - Array of indices for the parts in this group\n * @param children - Rendered message part components to display within the group\n */\n Group?: ComponentType<\n PropsWithChildren<{\n groupKey: string | undefined;\n indices: number[];\n }>\n >;\n }\n | undefined;\n };\n}\n\nconst ToolUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: ToolCallMessagePartComponent | undefined;\n} & ToolCallMessagePartProps) => {\n const Render = useAssistantState(({ tools }) => {\n const Render = tools.tools[props.toolName] ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst defaultComponents = {\n Text: () => (\n <p style={{ whiteSpace: \"pre-line\" }}>\n <MessagePartPrimitiveText />\n <MessagePartPrimitiveInProgress>\n <span style={{ fontFamily: \"revert\" }}>{\" \\u25CF\"}</span>\n </MessagePartPrimitiveInProgress>\n </p>\n ),\n Reasoning: () => null,\n Source: () => null,\n Image: () => <MessagePartPrimitiveImage />,\n File: () => null,\n Unstable_Audio: () => null,\n Group: ({ children }) => children,\n} satisfies MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n\ntype MessagePartComponentProps = {\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartComponent: FC<MessagePartComponentProps> = ({\n components: {\n Text = defaultComponents.Text,\n Reasoning = defaultComponents.Reasoning,\n Image = defaultComponents.Image,\n Source = defaultComponents.Source,\n File = defaultComponents.File,\n Unstable_Audio: Audio = defaultComponents.Unstable_Audio,\n tools = {},\n } = {},\n}) => {\n const api = useAssistantApi();\n const part = useAssistantState(({ part }) => part);\n\n const type = part.type;\n if (type === \"tool-call\") {\n const addResult = (result: any) => api.part().addToolResult(result);\n const resume = api.part().resumeToolCall;\n if (\"Override\" in tools)\n return <tools.Override {...part} addResult={addResult} resume={resume} />;\n const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;\n return (\n <ToolUIDisplay\n {...part}\n Fallback={Tool}\n addResult={addResult}\n resume={resume}\n />\n );\n }\n\n if (part.status?.type === \"requires-action\")\n throw new Error(\"Encountered unexpected requires-action status\");\n\n switch (type) {\n case \"text\":\n return <Text {...part} />;\n\n case \"reasoning\":\n return <Reasoning {...part} />;\n\n case \"source\":\n return <Source {...part} />;\n\n case \"image\":\n // eslint-disable-next-line jsx-a11y/alt-text\n return <Image {...part} />;\n\n case \"file\":\n return <File {...part} />;\n\n case \"audio\":\n return <Audio {...part} />;\n\n case \"data\":\n return null;\n\n default:\n const unhandledType: never = type;\n throw new Error(`Unknown message part type: ${unhandledType}`);\n }\n};\n\ntype MessagePartProps = {\n partIndex: number;\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartImpl: FC<MessagePartProps> = ({ partIndex, components }) => {\n return (\n <PartByIndexProvider index={partIndex}>\n <MessagePartComponent components={components} />\n </PartByIndexProvider>\n );\n};\n\nconst MessagePart = memo(\n MessagePartImpl,\n (prev, next) =>\n prev.partIndex === next.partIndex &&\n prev.components?.Text === next.components?.Text &&\n prev.components?.Reasoning === next.components?.Reasoning &&\n prev.components?.Source === next.components?.Source &&\n prev.components?.Image === next.components?.Image &&\n prev.components?.File === next.components?.File &&\n prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&\n prev.components?.tools === next.components?.tools &&\n prev.components?.Group === next.components?.Group,\n);\n\nconst EmptyPartFallback: FC<{\n status: MessagePartStatus;\n component: TextMessagePartComponent;\n}> = ({ status, component: Component }) => {\n return (\n <TextMessagePartProvider text=\"\" isRunning={status.type === \"running\"}>\n <Component type=\"text\" text=\"\" status={status} />\n </TextMessagePartProvider>\n );\n};\n\nconst COMPLETE_STATUS: MessagePartStatus = Object.freeze({\n type: \"complete\",\n});\n\nconst EmptyPartsImpl: FC<MessagePartComponentProps> = ({ components }) => {\n const status = useAssistantState(\n (s) => (s.message.status ?? COMPLETE_STATUS) as MessagePartStatus,\n );\n\n if (components?.Empty) return <components.Empty status={status} />;\n\n return (\n <EmptyPartFallback\n status={status}\n component={components?.Text ?? defaultComponents.Text}\n />\n );\n};\n\nconst EmptyParts = memo(\n EmptyPartsImpl,\n (prev, next) =>\n prev.components?.Empty === next.components?.Empty &&\n prev.components?.Text === next.components?.Text,\n);\n\n/**\n * Renders the parts of a message grouped by a custom grouping function.\n *\n * This component allows you to group message parts based on any criteria you define.\n * The grouping function receives all message parts and returns an array of groups,\n * where each group has a key and an array of part indices.\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * <MessagePrimitive.Unstable_PartsGrouped\n * components={{\n * Text: ({ text }) => <p className=\"message-text\">{text}</p>,\n * Image: ({ image }) => <img src={image} alt=\"Message image\" />,\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <div className=\"parent-group border rounded p-4\">\n * <h4>Parent ID: {groupKey}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Group by tool name\n * import { groupMessagePartsByToolName } from \"@assistant-ui/react\";\n *\n * <MessagePrimitive.Unstable_PartsGrouped\n * groupingFunction={groupMessagePartsByToolName}\n * components={{\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <div className=\"tool-group\">\n * <h4>Tool: {groupKey}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\nexport const MessagePrimitiveUnstable_PartsGrouped: FC<\n MessagePrimitiveUnstable_PartsGrouped.Props\n> = ({ groupingFunction, components }) => {\n const contentLength = useAssistantState(\n ({ message }) => message.parts.length,\n );\n const messageGroups = useMessagePartsGrouped(groupingFunction);\n\n const partsElements = useMemo(() => {\n if (contentLength === 0) {\n return <EmptyParts components={components} />;\n }\n\n return messageGroups.map((group, groupIndex) => {\n const GroupComponent = components?.Group ?? defaultComponents.Group;\n\n return (\n <GroupComponent\n key={`group-${groupIndex}-${group.groupKey ?? \"ungrouped\"}`}\n groupKey={group.groupKey}\n indices={group.indices}\n >\n {group.indices.map((partIndex) => (\n <MessagePart\n key={partIndex}\n partIndex={partIndex}\n components={components}\n />\n ))}\n </GroupComponent>\n );\n });\n }, [messageGroups, components, contentLength]);\n\n return <>{partsElements}</>;\n};\n\nMessagePrimitiveUnstable_PartsGrouped.displayName =\n \"MessagePrimitive.Unstable_PartsGrouped\";\n\n/**\n * Renders the parts of a message grouped by their parent ID.\n * This is a convenience wrapper around Unstable_PartsGrouped with parent ID grouping.\n *\n * @deprecated Use MessagePrimitive.Unstable_PartsGrouped instead for more flexibility\n */\nexport const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<\n Omit<MessagePrimitiveUnstable_PartsGrouped.Props, \"groupingFunction\">\n> = ({ components, ...props }) => {\n return (\n <MessagePrimitiveUnstable_PartsGrouped\n {...props}\n components={components}\n groupingFunction={groupMessagePartsByParentId}\n />\n );\n};\n\nMessagePrimitiveUnstable_PartsGroupedByParentId.displayName =\n \"MessagePrimitive.Unstable_PartsGroupedByParentId\";\n"],"mappings":"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","names":["Render","part"]}
1
+ {"version":3,"sources":["../../../src/primitives/message/MessagePartsGrouped.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n type FC,\n memo,\n PropsWithChildren,\n useMemo,\n} from \"react\";\nimport {\n useAssistantState,\n PartByIndexProvider,\n useAssistantApi,\n TextMessagePartProvider,\n} from \"../../context\";\nimport { MessagePartPrimitiveText } from \"../messagePart/MessagePartText\";\nimport { MessagePartPrimitiveImage } from \"../messagePart/MessagePartImage\";\nimport type {\n Unstable_AudioMessagePartComponent,\n EmptyMessagePartComponent,\n TextMessagePartComponent,\n ImageMessagePartComponent,\n SourceMessagePartComponent,\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n FileMessagePartComponent,\n ReasoningMessagePartComponent,\n} from \"../../types/MessagePartComponentTypes\";\nimport { MessagePartPrimitiveInProgress } from \"../messagePart/MessagePartInProgress\";\nimport { MessagePartStatus } from \"../../types/AssistantTypes\";\n\ntype MessagePartGroup = {\n groupKey: string | undefined;\n indices: number[];\n};\n\nexport type GroupingFunction = (parts: readonly any[]) => MessagePartGroup[];\n\n/**\n * Groups message parts by their parent ID.\n * Parts without a parent ID appear in their chronological position as individual groups.\n * Parts with the same parent ID are grouped together at the position of their first occurrence.\n */\nconst groupMessagePartsByParentId: GroupingFunction = (\n parts: readonly any[],\n): MessagePartGroup[] => {\n // Map maintains insertion order, so groups appear in order of first occurrence\n const groupMap = new Map<string, number[]>();\n\n // Process each part in order\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const parentId = part?.parentId as string | undefined;\n\n // For parts without parentId, assign a unique group ID to maintain their position\n const groupId = parentId ?? `__ungrouped_${i}`;\n\n // Get or create the indices array for this group\n const indices = groupMap.get(groupId) ?? [];\n indices.push(i);\n groupMap.set(groupId, indices);\n }\n\n // Convert map to array of groups\n const groups: MessagePartGroup[] = [];\n for (const [groupId, indices] of groupMap) {\n // Extract parentId (undefined for ungrouped parts)\n const groupKey = groupId.startsWith(\"__ungrouped_\") ? undefined : groupId;\n groups.push({ groupKey, indices });\n }\n\n return groups;\n};\n\nconst useMessagePartsGrouped = (\n groupingFunction: GroupingFunction,\n): MessagePartGroup[] => {\n const parts = useAssistantState(({ message }) => message.parts);\n\n return useMemo(() => {\n if (parts.length === 0) {\n return [];\n }\n return groupingFunction(parts);\n }, [parts, groupingFunction]);\n};\n\nexport namespace MessagePrimitiveUnstable_PartsGrouped {\n export type Props = {\n /**\n * Function that takes an array of message parts and returns an array of groups.\n * Each group contains a key (for identification) and an array of indices.\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * groupingFunction={(parts) => {\n * const groups = new Map<string, number[]>();\n * parts.forEach((part, i) => {\n * const key = part.parentId ?? `__ungrouped_${i}`;\n * const indices = groups.get(key) ?? [];\n * indices.push(i);\n * groups.set(key, indices);\n * });\n * return Array.from(groups.entries()).map(([key, indices]) => ({\n * key: key.startsWith(\"__ungrouped_\") ? undefined : key,\n * indices\n * }));\n * }}\n * ```\n *\n * @example\n * ```tsx\n * // Group by tool name\n * import { groupMessagePartsByToolName } from \"@assistant-ui/react\";\n *\n * <MessagePrimitive.Unstable_PartsGrouped\n * groupingFunction={groupMessagePartsByToolName}\n * components={{\n * Group: ({ key, indices, children }) => {\n * if (!key) return <>{children}</>;\n * return (\n * <div className=\"tool-group\">\n * <h4>Tool: {key}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\n groupingFunction: GroupingFunction;\n\n /**\n * Component configuration for rendering different types of message content.\n *\n * You can provide custom components for each content type (text, image, file, etc.)\n * and configure tool rendering behavior. If not provided, default components will be used.\n */\n components:\n | {\n /** Component for rendering empty messages */\n Empty?: EmptyMessagePartComponent | undefined;\n /** Component for rendering text content */\n Text?: TextMessagePartComponent | undefined;\n /** Component for rendering reasoning content (typically hidden) */\n Reasoning?: ReasoningMessagePartComponent | undefined;\n /** Component for rendering source content */\n Source?: SourceMessagePartComponent | undefined;\n /** Component for rendering image content */\n Image?: ImageMessagePartComponent | undefined;\n /** Component for rendering file content */\n File?: FileMessagePartComponent | undefined;\n /** Component for rendering audio content (experimental) */\n Unstable_Audio?: Unstable_AudioMessagePartComponent | undefined;\n /** Configuration for tool call rendering */\n tools?:\n | {\n /** Map of tool names to their specific components */\n by_name?:\n | Record<string, ToolCallMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unregistered tools */\n Fallback?: ComponentType<ToolCallMessagePartProps> | undefined;\n }\n | {\n /** Override component that handles all tool calls */\n Override: ComponentType<ToolCallMessagePartProps>;\n }\n | undefined;\n\n /**\n * Component for rendering grouped message parts.\n *\n * When provided, this component will automatically wrap message parts that share\n * the same group key as determined by the groupingFunction.\n *\n * The component receives:\n * - `groupKey`: The group key (or undefined for ungrouped parts)\n * - `indices`: Array of indices for the parts in this group\n * - `children`: The rendered message part components\n *\n * @example\n * ```tsx\n * // Collapsible group\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <details className=\"message-group\">\n * <summary>\n * Group {groupKey} ({indices.length} parts)\n * </summary>\n * <div className=\"group-content\">\n * {children}\n * </div>\n * </details>\n * );\n * }\n * ```\n *\n * @param groupKey - The group key (undefined for ungrouped parts)\n * @param indices - Array of indices for the parts in this group\n * @param children - Rendered message part components to display within the group\n */\n Group?: ComponentType<\n PropsWithChildren<{\n groupKey: string | undefined;\n indices: number[];\n }>\n >;\n }\n | undefined;\n };\n}\n\nconst ToolUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: ToolCallMessagePartComponent | undefined;\n} & ToolCallMessagePartProps) => {\n const Render = useAssistantState(({ tools }) => {\n const Render = tools.tools[props.toolName] ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst defaultComponents = {\n Text: () => (\n <p style={{ whiteSpace: \"pre-line\" }}>\n <MessagePartPrimitiveText />\n <MessagePartPrimitiveInProgress>\n <span style={{ fontFamily: \"revert\" }}>{\" \\u25CF\"}</span>\n </MessagePartPrimitiveInProgress>\n </p>\n ),\n Reasoning: () => null,\n Source: () => null,\n Image: () => <MessagePartPrimitiveImage />,\n File: () => null,\n Unstable_Audio: () => null,\n Group: ({ children }) => children,\n} satisfies MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n\ntype MessagePartComponentProps = {\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartComponent: FC<MessagePartComponentProps> = ({\n components: {\n Text = defaultComponents.Text,\n Reasoning = defaultComponents.Reasoning,\n Image = defaultComponents.Image,\n Source = defaultComponents.Source,\n File = defaultComponents.File,\n Unstable_Audio: Audio = defaultComponents.Unstable_Audio,\n tools = {},\n } = {},\n}) => {\n const api = useAssistantApi();\n const part = useAssistantState(({ part }) => part);\n\n const type = part.type;\n if (type === \"tool-call\") {\n const addResult = (result: any) => api.part().addToolResult(result);\n const resume = api.part().resumeToolCall;\n if (\"Override\" in tools)\n return <tools.Override {...part} addResult={addResult} resume={resume} />;\n const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;\n return (\n <ToolUIDisplay\n {...part}\n Fallback={Tool}\n addResult={addResult}\n resume={resume}\n />\n );\n }\n\n if (part.status?.type === \"requires-action\")\n throw new Error(\"Encountered unexpected requires-action status\");\n\n switch (type) {\n case \"text\":\n return <Text {...part} />;\n\n case \"reasoning\":\n return <Reasoning {...part} />;\n\n case \"source\":\n return <Source {...part} />;\n\n case \"image\":\n return <Image {...part} />;\n\n case \"file\":\n return <File {...part} />;\n\n case \"audio\":\n return <Audio {...part} />;\n\n case \"data\":\n return null;\n\n default:\n const unhandledType: never = type;\n throw new Error(`Unknown message part type: ${unhandledType}`);\n }\n};\n\ntype MessagePartProps = {\n partIndex: number;\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartImpl: FC<MessagePartProps> = ({ partIndex, components }) => {\n return (\n <PartByIndexProvider index={partIndex}>\n <MessagePartComponent components={components} />\n </PartByIndexProvider>\n );\n};\n\nconst MessagePart = memo(\n MessagePartImpl,\n (prev, next) =>\n prev.partIndex === next.partIndex &&\n prev.components?.Text === next.components?.Text &&\n prev.components?.Reasoning === next.components?.Reasoning &&\n prev.components?.Source === next.components?.Source &&\n prev.components?.Image === next.components?.Image &&\n prev.components?.File === next.components?.File &&\n prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&\n prev.components?.tools === next.components?.tools &&\n prev.components?.Group === next.components?.Group,\n);\n\nconst EmptyPartFallback: FC<{\n status: MessagePartStatus;\n component: TextMessagePartComponent;\n}> = ({ status, component: Component }) => {\n return (\n <TextMessagePartProvider text=\"\" isRunning={status.type === \"running\"}>\n <Component type=\"text\" text=\"\" status={status} />\n </TextMessagePartProvider>\n );\n};\n\nconst COMPLETE_STATUS: MessagePartStatus = Object.freeze({\n type: \"complete\",\n});\n\nconst EmptyPartsImpl: FC<MessagePartComponentProps> = ({ components }) => {\n const status = useAssistantState(\n (s) => (s.message.status ?? COMPLETE_STATUS) as MessagePartStatus,\n );\n\n if (components?.Empty) return <components.Empty status={status} />;\n\n return (\n <EmptyPartFallback\n status={status}\n component={components?.Text ?? defaultComponents.Text}\n />\n );\n};\n\nconst EmptyParts = memo(\n EmptyPartsImpl,\n (prev, next) =>\n prev.components?.Empty === next.components?.Empty &&\n prev.components?.Text === next.components?.Text,\n);\n\n/**\n * Renders the parts of a message grouped by a custom grouping function.\n *\n * This component allows you to group message parts based on any criteria you define.\n * The grouping function receives all message parts and returns an array of groups,\n * where each group has a key and an array of part indices.\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * <MessagePrimitive.Unstable_PartsGrouped\n * components={{\n * Text: ({ text }) => <p className=\"message-text\">{text}</p>,\n * Image: ({ image }) => <img src={image} alt=\"Message image\" />,\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <div className=\"parent-group border rounded p-4\">\n * <h4>Parent ID: {groupKey}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Group by tool name\n * import { groupMessagePartsByToolName } from \"@assistant-ui/react\";\n *\n * <MessagePrimitive.Unstable_PartsGrouped\n * groupingFunction={groupMessagePartsByToolName}\n * components={{\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <div className=\"tool-group\">\n * <h4>Tool: {groupKey}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\nexport const MessagePrimitiveUnstable_PartsGrouped: FC<\n MessagePrimitiveUnstable_PartsGrouped.Props\n> = ({ groupingFunction, components }) => {\n const contentLength = useAssistantState(\n ({ message }) => message.parts.length,\n );\n const messageGroups = useMessagePartsGrouped(groupingFunction);\n\n const partsElements = useMemo(() => {\n if (contentLength === 0) {\n return <EmptyParts components={components} />;\n }\n\n return messageGroups.map((group, groupIndex) => {\n const GroupComponent = components?.Group ?? defaultComponents.Group;\n\n return (\n <GroupComponent\n key={`group-${groupIndex}-${group.groupKey ?? \"ungrouped\"}`}\n groupKey={group.groupKey}\n indices={group.indices}\n >\n {group.indices.map((partIndex) => (\n <MessagePart\n key={partIndex}\n partIndex={partIndex}\n components={components}\n />\n ))}\n </GroupComponent>\n );\n });\n }, [messageGroups, components, contentLength]);\n\n return <>{partsElements}</>;\n};\n\nMessagePrimitiveUnstable_PartsGrouped.displayName =\n \"MessagePrimitive.Unstable_PartsGrouped\";\n\n/**\n * Renders the parts of a message grouped by their parent ID.\n * This is a convenience wrapper around Unstable_PartsGrouped with parent ID grouping.\n *\n * @deprecated Use MessagePrimitive.Unstable_PartsGrouped instead for more flexibility\n */\nexport const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<\n Omit<MessagePrimitiveUnstable_PartsGrouped.Props, \"groupingFunction\">\n> = ({ components, ...props }) => {\n return (\n <MessagePrimitiveUnstable_PartsGrouped\n {...props}\n components={components}\n groupingFunction={groupMessagePartsByParentId}\n />\n );\n};\n\nMessagePrimitiveUnstable_PartsGroupedByParentId.displayName =\n \"MessagePrimitive.Unstable_PartsGroupedByParentId\";\n"],"mappings":"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","names":["Render","part"]}
@@ -1 +1 @@
1
- {"version":3,"file":"useMessagePartData.d.ts","sourceRoot":"","sources":["../../../src/primitives/messagePart/useMessagePartData.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,eAAO,MAAM,kBAAkB,GAAI,CAAC,GAAG,GAAG,EAAG,OAAO,MAAM,8BAiBzD,CAAC"}
1
+ {"version":3,"file":"useMessagePartData.d.ts","sourceRoot":"","sources":["../../../src/primitives/messagePart/useMessagePartData.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,eAAO,MAAM,kBAAkB,GAAI,CAAC,GAAG,GAAG,EAAE,OAAO,MAAM,8BAiBxD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/primitives/messagePart/useMessagePartData.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAssistantState } from \"../../context\";\nimport { DataMessagePart } from \"../../types\";\n\nexport const useMessagePartData = <T = any,>(name?: string) => {\n const part = useAssistantState(({ part }) => {\n if (part.type !== \"data\") {\n return null;\n }\n return part as DataMessagePart<T>;\n });\n\n if (!part) {\n return null;\n }\n\n if (name && part.name !== name) {\n return null;\n }\n\n return part;\n};\n"],"mappings":";;;AAEA,SAAS,yBAAyB;AAG3B,IAAM,qBAAqB,CAAW,SAAkB;AAC7D,QAAM,OAAO,kBAAkB,CAAC,EAAE,MAAAA,MAAK,MAAM;AAC3C,QAAIA,MAAK,SAAS,QAAQ;AACxB,aAAO;AAAA,IACT;AACA,WAAOA;AAAA,EACT,CAAC;AAED,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,KAAK,SAAS,MAAM;AAC9B,WAAO;AAAA,EACT;AAEA,SAAO;AACT;","names":["part"]}
1
+ {"version":3,"sources":["../../../src/primitives/messagePart/useMessagePartData.tsx"],"sourcesContent":["\"use client\";\n\nimport { useAssistantState } from \"../../context\";\nimport { DataMessagePart } from \"../../types\";\n\nexport const useMessagePartData = <T = any>(name?: string) => {\n const part = useAssistantState(({ part }) => {\n if (part.type !== \"data\") {\n return null;\n }\n return part as DataMessagePart<T>;\n });\n\n if (!part) {\n return null;\n }\n\n if (name && part.name !== name) {\n return null;\n }\n\n return part;\n};\n"],"mappings":";;;AAEA,SAAS,yBAAyB;AAG3B,IAAM,qBAAqB,CAAU,SAAkB;AAC5D,QAAM,OAAO,kBAAkB,CAAC,EAAE,MAAAA,MAAK,MAAM;AAC3C,QAAIA,MAAK,SAAS,QAAQ;AACxB,aAAO;AAAA,IACT;AACA,WAAOA;AAAA,EACT,CAAC;AAED,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,KAAK,SAAS,MAAM;AAC9B,WAAO;AAAA,EACT;AAEA,SAAO;AACT;","names":["part"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,yBAAyB,EAGzB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,EACjB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,kCAAkC,EAClC,8BAA8B,EAC9B,4BAA4B,EAC5B,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,6BAA6B,CAAC;AAGrC,YAAY,EAAE,oBAAoB,EAAE,MAAM,iDAAiD,CAAC;AAE5F,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,YAAY,EACV,mBAAmB,EACnB,sBAAsB,EACtB,cAAc,EACd,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,yBAAyB,EAEzB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,EACjB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,kCAAkC,EAClC,8BAA8B,EAC9B,4BAA4B,EAC5B,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,6BAA6B,CAAC;AAGrC,YAAY,EAAE,oBAAoB,EAAE,MAAM,iDAAiD,CAAC;AAE5F,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,YAAY,EACV,mBAAmB,EACnB,sBAAsB,EACtB,cAAc,EACd,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,cAAc,CAAC"}
@@ -68,10 +68,10 @@ var useSmooth = (state, smooth = false) => {
68
68
  });
69
69
  useEffect(() => {
70
70
  if (smoothStatusStore) {
71
- const target = displayedText !== text || state.status.type === "running" ? SMOOTH_STATUS : state.status;
71
+ const target = smooth && (displayedText !== text || state.status.type === "running") ? SMOOTH_STATUS : state.status;
72
72
  writableStore(smoothStatusStore).setState(target, true);
73
73
  }
74
- }, [smoothStatusStore, text, displayedText, state.status]);
74
+ }, [smoothStatusStore, smooth, text, displayedText, state.status]);
75
75
  const [animatorRef] = useState(
76
76
  new TextStreamAnimator(text, setText)
77
77
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/smooth/useSmooth.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAssistantState } from \"../../context\";\nimport {\n MessagePartStatus,\n ReasoningMessagePart,\n TextMessagePart,\n} from \"../../types/AssistantTypes\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\nimport { MessagePartState } from \"../../legacy-runtime/runtime/MessagePartRuntime\";\n\nclass TextStreamAnimator {\n private animationFrameId: number | null = null;\n private lastUpdateTime: number = Date.now();\n\n public targetText: string = \"\";\n\n constructor(\n public currentText: string,\n private setText: (newText: string) => void,\n ) {}\n\n start() {\n if (this.animationFrameId !== null) return;\n this.lastUpdateTime = Date.now();\n this.animate();\n }\n\n stop() {\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n }\n\n private animate = () => {\n const currentTime = Date.now();\n const deltaTime = currentTime - this.lastUpdateTime;\n let timeToConsume = deltaTime;\n\n const remainingChars = this.targetText.length - this.currentText.length;\n const baseTimePerChar = Math.min(5, 250 / remainingChars);\n\n let charsToAdd = 0;\n while (timeToConsume >= baseTimePerChar && charsToAdd < remainingChars) {\n charsToAdd++;\n timeToConsume -= baseTimePerChar;\n }\n\n if (charsToAdd !== remainingChars) {\n this.animationFrameId = requestAnimationFrame(this.animate);\n } else {\n this.animationFrameId = null;\n }\n if (charsToAdd === 0) return;\n\n this.currentText = this.targetText.slice(\n 0,\n this.currentText.length + charsToAdd,\n );\n this.lastUpdateTime = currentTime - timeToConsume;\n this.setText(this.currentText);\n };\n}\n\nconst SMOOTH_STATUS: MessagePartStatus = Object.freeze({\n type: \"running\",\n});\n\nexport const useSmooth = (\n state: MessagePartState & (TextMessagePart | ReasoningMessagePart),\n smooth: boolean = false,\n): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {\n const { text } = state;\n const id = useAssistantState(({ message }) => message.id);\n\n const idRef = useRef(id);\n const [displayedText, setDisplayedText] = useState(text);\n\n const smoothStatusStore = useSmoothStatusStore({ optional: true });\n const setText = useCallbackRef((text: string) => {\n setDisplayedText(text);\n if (smoothStatusStore) {\n const target =\n displayedText !== text || state.status.type === \"running\"\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n });\n\n // TODO this is hacky\n useEffect(() => {\n if (smoothStatusStore) {\n const target =\n displayedText !== text || state.status.type === \"running\"\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n }, [smoothStatusStore, text, displayedText, state.status]);\n\n const [animatorRef] = useState<TextStreamAnimator>(\n new TextStreamAnimator(text, setText),\n );\n\n useEffect(() => {\n if (!smooth) {\n animatorRef.stop();\n return;\n }\n\n if (idRef.current !== id || !text.startsWith(animatorRef.targetText)) {\n idRef.current = id;\n setText(text);\n\n animatorRef.currentText = text;\n animatorRef.targetText = text;\n animatorRef.stop();\n\n return;\n }\n\n animatorRef.targetText = text;\n animatorRef.start();\n }, [setText, animatorRef, id, smooth, text]);\n\n useEffect(() => {\n return () => {\n animatorRef.stop();\n };\n }, [animatorRef]);\n\n return useMemo(\n () =>\n smooth\n ? {\n type: \"text\",\n text: displayedText,\n status: text === displayedText ? state.status : SMOOTH_STATUS,\n }\n : state,\n [smooth, displayedText, state, text],\n );\n};\n"],"mappings":"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","names":["text"]}
1
+ {"version":3,"sources":["../../../src/utils/smooth/useSmooth.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAssistantState } from \"../../context\";\nimport {\n MessagePartStatus,\n ReasoningMessagePart,\n TextMessagePart,\n} from \"../../types/AssistantTypes\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\nimport { MessagePartState } from \"../../legacy-runtime/runtime/MessagePartRuntime\";\n\nclass TextStreamAnimator {\n private animationFrameId: number | null = null;\n private lastUpdateTime: number = Date.now();\n\n public targetText: string = \"\";\n\n constructor(\n public currentText: string,\n private setText: (newText: string) => void,\n ) {}\n\n start() {\n if (this.animationFrameId !== null) return;\n this.lastUpdateTime = Date.now();\n this.animate();\n }\n\n stop() {\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n }\n\n private animate = () => {\n const currentTime = Date.now();\n const deltaTime = currentTime - this.lastUpdateTime;\n let timeToConsume = deltaTime;\n\n const remainingChars = this.targetText.length - this.currentText.length;\n const baseTimePerChar = Math.min(5, 250 / remainingChars);\n\n let charsToAdd = 0;\n while (timeToConsume >= baseTimePerChar && charsToAdd < remainingChars) {\n charsToAdd++;\n timeToConsume -= baseTimePerChar;\n }\n\n if (charsToAdd !== remainingChars) {\n this.animationFrameId = requestAnimationFrame(this.animate);\n } else {\n this.animationFrameId = null;\n }\n if (charsToAdd === 0) return;\n\n this.currentText = this.targetText.slice(\n 0,\n this.currentText.length + charsToAdd,\n );\n this.lastUpdateTime = currentTime - timeToConsume;\n this.setText(this.currentText);\n };\n}\n\nconst SMOOTH_STATUS: MessagePartStatus = Object.freeze({\n type: \"running\",\n});\n\nexport const useSmooth = (\n state: MessagePartState & (TextMessagePart | ReasoningMessagePart),\n smooth: boolean = false,\n): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {\n const { text } = state;\n const id = useAssistantState(({ message }) => message.id);\n\n const idRef = useRef(id);\n const [displayedText, setDisplayedText] = useState(text);\n\n const smoothStatusStore = useSmoothStatusStore({ optional: true });\n const setText = useCallbackRef((text: string) => {\n setDisplayedText(text);\n if (smoothStatusStore) {\n const target =\n displayedText !== text || state.status.type === \"running\"\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n });\n\n // TODO this is hacky\n useEffect(() => {\n if (smoothStatusStore) {\n const target =\n smooth && (displayedText !== text || state.status.type === \"running\")\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n }, [smoothStatusStore, smooth, text, displayedText, state.status]);\n\n const [animatorRef] = useState<TextStreamAnimator>(\n new TextStreamAnimator(text, setText),\n );\n\n useEffect(() => {\n if (!smooth) {\n animatorRef.stop();\n return;\n }\n\n if (idRef.current !== id || !text.startsWith(animatorRef.targetText)) {\n idRef.current = id;\n setText(text);\n\n animatorRef.currentText = text;\n animatorRef.targetText = text;\n animatorRef.stop();\n\n return;\n }\n\n animatorRef.targetText = text;\n animatorRef.start();\n }, [setText, animatorRef, id, smooth, text]);\n\n useEffect(() => {\n return () => {\n animatorRef.stop();\n };\n }, [animatorRef]);\n\n return useMemo(\n () =>\n smooth\n ? {\n type: \"text\",\n text: displayedText,\n status: text === displayedText ? state.status : SMOOTH_STATUS,\n }\n : state,\n [smooth, displayedText, state, text],\n );\n};\n"],"mappings":";;;AAEA,SAAS,WAAW,SAAS,QAAQ,gBAAgB;AACrD,SAAS,yBAAyB;AAMlC,SAAS,sBAAsB;AAC/B,SAAS,4BAA4B;AACrC,SAAS,qBAAqB;AAG9B,IAAM,qBAAN,MAAyB;AAAA,EAMvB,YACS,aACC,SACR;AAFO;AACC;AAAA,EACP;AAAA,EARK,mBAAkC;AAAA,EAClC,iBAAyB,KAAK,IAAI;AAAA,EAEnC,aAAqB;AAAA,EAO5B,QAAQ;AACN,QAAI,KAAK,qBAAqB,KAAM;AACpC,SAAK,iBAAiB,KAAK,IAAI;AAC/B,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,OAAO;AACL,QAAI,KAAK,qBAAqB,MAAM;AAClC,2BAAqB,KAAK,gBAAgB;AAC1C,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AAAA,EAEQ,UAAU,MAAM;AACtB,UAAM,cAAc,KAAK,IAAI;AAC7B,UAAM,YAAY,cAAc,KAAK;AACrC,QAAI,gBAAgB;AAEpB,UAAM,iBAAiB,KAAK,WAAW,SAAS,KAAK,YAAY;AACjE,UAAM,kBAAkB,KAAK,IAAI,GAAG,MAAM,cAAc;AAExD,QAAI,aAAa;AACjB,WAAO,iBAAiB,mBAAmB,aAAa,gBAAgB;AACtE;AACA,uBAAiB;AAAA,IACnB;AAEA,QAAI,eAAe,gBAAgB;AACjC,WAAK,mBAAmB,sBAAsB,KAAK,OAAO;AAAA,IAC5D,OAAO;AACL,WAAK,mBAAmB;AAAA,IAC1B;AACA,QAAI,eAAe,EAAG;AAEtB,SAAK,cAAc,KAAK,WAAW;AAAA,MACjC;AAAA,MACA,KAAK,YAAY,SAAS;AAAA,IAC5B;AACA,SAAK,iBAAiB,cAAc;AACpC,SAAK,QAAQ,KAAK,WAAW;AAAA,EAC/B;AACF;AAEA,IAAM,gBAAmC,OAAO,OAAO;AAAA,EACrD,MAAM;AACR,CAAC;AAEM,IAAM,YAAY,CACvB,OACA,SAAkB,UAC8C;AAChE,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,KAAK,kBAAkB,CAAC,EAAE,QAAQ,MAAM,QAAQ,EAAE;AAExD,QAAM,QAAQ,OAAO,EAAE;AACvB,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,IAAI;AAEvD,QAAM,oBAAoB,qBAAqB,EAAE,UAAU,KAAK,CAAC;AACjE,QAAM,UAAU,eAAe,CAACA,UAAiB;AAC/C,qBAAiBA,KAAI;AACrB,QAAI,mBAAmB;AACrB,YAAM,SACJ,kBAAkBA,SAAQ,MAAM,OAAO,SAAS,YAC5C,gBACA,MAAM;AACZ,oBAAc,iBAAiB,EAAE,SAAS,QAAQ,IAAI;AAAA,IACxD;AAAA,EACF,CAAC;AAGD,YAAU,MAAM;AACd,QAAI,mBAAmB;AACrB,YAAM,SACJ,WAAW,kBAAkB,QAAQ,MAAM,OAAO,SAAS,aACvD,gBACA,MAAM;AACZ,oBAAc,iBAAiB,EAAE,SAAS,QAAQ,IAAI;AAAA,IACxD;AAAA,EACF,GAAG,CAAC,mBAAmB,QAAQ,MAAM,eAAe,MAAM,MAAM,CAAC;AAEjE,QAAM,CAAC,WAAW,IAAI;AAAA,IACpB,IAAI,mBAAmB,MAAM,OAAO;AAAA,EACtC;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ;AACX,kBAAY,KAAK;AACjB;AAAA,IACF;AAEA,QAAI,MAAM,YAAY,MAAM,CAAC,KAAK,WAAW,YAAY,UAAU,GAAG;AACpE,YAAM,UAAU;AAChB,cAAQ,IAAI;AAEZ,kBAAY,cAAc;AAC1B,kBAAY,aAAa;AACzB,kBAAY,KAAK;AAEjB;AAAA,IACF;AAEA,gBAAY,aAAa;AACzB,gBAAY,MAAM;AAAA,EACpB,GAAG,CAAC,SAAS,aAAa,IAAI,QAAQ,IAAI,CAAC;AAE3C,YAAU,MAAM;AACd,WAAO,MAAM;AACX,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAEhB,SAAO;AAAA,IACL,MACE,SACI;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ,SAAS,gBAAgB,MAAM,SAAS;AAAA,IAClD,IACA;AAAA,IACN,CAAC,QAAQ,eAAe,OAAO,IAAI;AAAA,EACrC;AACF;","names":["text"]}
package/package.json CHANGED
@@ -28,7 +28,7 @@
28
28
  "conversational-ui",
29
29
  "conversational-ai"
30
30
  ],
31
- "version": "0.11.45",
31
+ "version": "0.11.47",
32
32
  "license": "MIT",
33
33
  "type": "module",
34
34
  "exports": {
@@ -84,8 +84,6 @@
84
84
  "@stryker-mutator/vitest-runner": "^9.4.0",
85
85
  "@types/json-schema": "^7.0.15",
86
86
  "@types/node": "^24.10.1",
87
- "eslint": "^9",
88
- "eslint-config-next": "16.0.4",
89
87
  "tsx": "^4.20.6",
90
88
  "vitest": "^4.0.14",
91
89
  "@assistant-ui/x-buildutils": "0.0.1"
@@ -106,7 +104,6 @@
106
104
  "build": "tsx scripts/build.mts",
107
105
  "test": "vitest run",
108
106
  "test:watch": "vitest",
109
- "test:mutation": "stryker run",
110
- "lint": "eslint ."
107
+ "test:mutation": "stryker run"
111
108
  }
112
109
  }
@@ -22,10 +22,8 @@
22
22
  * ```
23
23
  */
24
24
  export namespace Assistant {
25
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
26
25
  export interface Commands {}
27
26
 
28
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
29
27
  export interface ExternalState {}
30
28
  }
31
29
 
@@ -40,7 +40,7 @@ const ThreadMessagePartClient = resource(
40
40
  {
41
41
  key:
42
42
  state.type === "tool-call"
43
- ? "toolCallId-" + state.toolCallId
43
+ ? `toolCallId-${state.toolCallId}`
44
44
  : undefined,
45
45
  },
46
46
  );
@@ -122,7 +122,7 @@ export const ThreadMessageClient = resource(
122
122
  if ("index" in selector) {
123
123
  return parts.api({ index: selector.index });
124
124
  } else {
125
- return parts.api({ key: "toolCallId-" + selector.toolCallId });
125
+ return parts.api({ key: `toolCallId-${selector.toolCallId}` });
126
126
  }
127
127
  },
128
128
  attachment: (selector) => {
@@ -1,7 +1,6 @@
1
1
  import type { Unsubscribe } from "../../types";
2
2
  import type { ModelContextProvider } from "../../model-context/ModelContextTypes";
3
3
 
4
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
5
4
  export type ModelContextState = {};
6
5
 
7
6
  export type ModelContextApi = ModelContextProvider & {
@@ -287,10 +287,8 @@ export function useAssistantApi(): AssistantApi;
287
287
  export function useAssistantApi(config: AssistantClientProps): AssistantApi;
288
288
  export function useAssistantApi(config?: AssistantClientProps): AssistantApi {
289
289
  if (config) {
290
- // eslint-disable-next-line react-hooks/rules-of-hooks
291
290
  return useExtendedAssistantApiImpl(config);
292
291
  } else {
293
- // eslint-disable-next-line react-hooks/rules-of-hooks
294
292
  return useAssistantApiImpl();
295
293
  }
296
294
  }
@@ -75,7 +75,6 @@ export function createStateHookForRuntime<TState>(
75
75
  if (!store) return null;
76
76
 
77
77
  // it is ok to call useRuntimeStateInternal conditionally because it will never become null if its available
78
- // eslint-disable-next-line react-hooks/rules-of-hooks
79
78
  return useRuntimeStateInternal(store, selector);
80
79
  }
81
80
 
@@ -24,7 +24,7 @@ const debugVerifyPrototype = (
24
24
 
25
25
  if (unboundMethods.length > 0) {
26
26
  throw new Error(
27
- "The following methods are not bound: " + JSON.stringify(unboundMethods),
27
+ `The following methods are not bound: ${JSON.stringify(unboundMethods)}`,
28
28
  );
29
29
  }
30
30
 
@@ -2,15 +2,15 @@
2
2
 
3
3
  import { FC, memo, PropsWithChildren } from "react";
4
4
  import {
5
- AssistantProvider,
6
5
  useAssistantApi,
6
+ AssistantProvider,
7
7
  } from "../context/react/AssistantApiContext";
8
8
  import { AssistantRuntime } from "./runtime/AssistantRuntime";
9
9
  import { AssistantRuntimeCore } from "./runtime-cores/core/AssistantRuntimeCore";
10
10
  import { RuntimeAdapter } from "./RuntimeAdapter";
11
11
  import { ThreadPrimitiveViewportProvider } from "../context/providers/ThreadViewportProvider";
12
12
 
13
- export namespace AssistantProvider {
13
+ export namespace AssistantRuntimeProvider {
14
14
  export type Props = PropsWithChildren<{
15
15
  /**
16
16
  * The runtime to provide to the rest of your app.
@@ -23,10 +23,9 @@ const getRenderComponent = (runtime: AssistantRuntime) => {
23
23
  return (runtime as { _core?: AssistantRuntimeCore })._core?.RenderComponent;
24
24
  };
25
25
 
26
- export const AssistantRuntimeProviderImpl: FC<AssistantProvider.Props> = ({
27
- children,
28
- runtime,
29
- }) => {
26
+ export const AssistantRuntimeProviderImpl: FC<
27
+ AssistantRuntimeProvider.Props
28
+ > = ({ children, runtime }) => {
30
29
  const api = useAssistantApi({
31
30
  threads: RuntimeAdapter(runtime),
32
31
  });
@@ -18,7 +18,7 @@ export const MessagePartClient = resource(
18
18
  return tapApi<MessagePartClientApi>(api, {
19
19
  key:
20
20
  runtimeState.type === "tool-call"
21
- ? "toolCallId-" + runtimeState.toolCallId
21
+ ? `toolCallId-${runtimeState.toolCallId}`
22
22
  : undefined,
23
23
  });
24
24
  },
@@ -116,7 +116,7 @@ export const MessageClient = resource(
116
116
  if ("index" in selector) {
117
117
  return parts.api({ index: selector.index });
118
118
  } else {
119
- return parts.api({ key: "toolCallId-" + selector.toolCallId });
119
+ return parts.api({ key: `toolCallId-${selector.toolCallId}` });
120
120
  }
121
121
  },
122
122
 
@@ -92,7 +92,7 @@ export const auiV0Encode = (message: ThreadMessage): AuiV0Message => {
92
92
  case "tool-call": {
93
93
  if (!isJSONValue(part.result)) {
94
94
  console.warn(
95
- "tool-call result is not JSON! " + JSON.stringify(part),
95
+ `tool-call result is not JSON! ${JSON.stringify(part)}`,
96
96
  );
97
97
  }
98
98
  return {
@@ -26,6 +26,7 @@ export const useCloudThreadListRuntime = ({
26
26
  const runtime = useRemoteThreadListRuntime({
27
27
  runtimeHook: runtimeHook,
28
28
  adapter,
29
+ allowNesting: true,
29
30
  });
30
31
 
31
32
  return runtime;
@@ -101,7 +101,9 @@ export class AssistantRuntimeImpl implements AssistantRuntime {
101
101
 
102
102
  public reset({
103
103
  initialMessages,
104
- }: { initialMessages?: ThreadMessageLike[] } = {}) {
104
+ }: {
105
+ initialMessages?: ThreadMessageLike[];
106
+ } = {}) {
105
107
  return this._core.threads
106
108
  .getMainThreadRuntimeCore()
107
109
  .import(ExportedMessageRepository.fromArray(initialMessages ?? []));
@@ -335,7 +335,7 @@ export class ThreadComposerRuntimeImpl
335
335
  ...this.path,
336
336
  attachmentSource: "thread-composer",
337
337
  attachmentSelector: { type: "index", index: idx },
338
- ref: this.path.ref + `${this.path.ref}.attachments[${idx}]`,
338
+ ref: `${this.path.ref}${this.path.ref}.attachments[${idx}]`,
339
339
  },
340
340
  getState: () => {
341
341
  const attachments = this.getState().attachments;
@@ -423,7 +423,7 @@ export class EditComposerRuntimeImpl
423
423
  ...this.path,
424
424
  attachmentSource: "edit-composer",
425
425
  attachmentSelector: { type: "index", index: idx },
426
- ref: this.path.ref + `${this.path.ref}.attachments[${idx}]`,
426
+ ref: `${this.path.ref}${this.path.ref}.attachments[${idx}]`,
427
427
  },
428
428
  getState: () => {
429
429
  const attachments = this.getState().attachments;
@@ -149,7 +149,7 @@ export class MessageRuntimeImpl implements MessageRuntime {
149
149
  new NestedSubscriptionSubject({
150
150
  path: {
151
151
  ...this.path,
152
- ref: this.path.ref + `${this.path.ref}.composer`,
152
+ ref: `${this.path.ref}${this.path.ref}.composer`,
153
153
  composerSource: "edit",
154
154
  },
155
155
  getState: this._getEditComposerRuntimeCore,
@@ -270,7 +270,7 @@ export class MessageRuntimeImpl implements MessageRuntime {
270
270
  new ShallowMemoizeSubject({
271
271
  path: {
272
272
  ...this.path,
273
- ref: this.path.ref + `${this.path.ref}.content[${idx}]`,
273
+ ref: `${this.path.ref}${this.path.ref}.content[${idx}]`,
274
274
  messagePartSelector: { type: "index", index: idx },
275
275
  },
276
276
  getState: () => {
@@ -314,7 +314,7 @@ export class MessageRuntimeImpl implements MessageRuntime {
314
314
  new ShallowMemoizeSubject({
315
315
  path: {
316
316
  ...this.path,
317
- ref: this.path.ref + `${this.path.ref}.attachments[${idx}]`,
317
+ ref: `${this.path.ref}${this.path.ref}.attachments[${idx}]`,
318
318
  attachmentSource: "message",
319
319
  attachmentSelector: { type: "index", index: idx },
320
320
  },
@@ -163,7 +163,7 @@ export class ThreadListRuntimeImpl implements ThreadListRuntime {
163
163
  return new this._runtimeFactory(
164
164
  new NestedSubscriptionSubject({
165
165
  path: {
166
- ref: "threads[threadId=" + JSON.stringify(threadId) + "]",
166
+ ref: `threads[threadId=${JSON.stringify(threadId)}]`,
167
167
  threadSelector: { type: "threadId", threadId },
168
168
  },
169
169
  getState: () => this._core.getThreadRuntimeCore(threadId),
@@ -338,7 +338,7 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
338
338
  new NestedSubscriptionSubject({
339
339
  path: {
340
340
  ...this.path,
341
- ref: this.path.ref + `${this.path.ref}.composer`,
341
+ ref: `${this.path.ref}${this.path.ref}.composer`,
342
342
  composerSource: "thread",
343
343
  },
344
344
  getState: () => this._threadBinding.getState().composer,
@@ -437,7 +437,7 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
437
437
  return this._getMessageRuntime(
438
438
  {
439
439
  ...this.path,
440
- ref: this.path.ref + `${this.path.ref}.messages[${idx}]`,
440
+ ref: `${this.path.ref}${this.path.ref}.messages[${idx}]`,
441
441
  messageSelector: { type: "index", index: idx },
442
442
  },
443
443
  () => {
@@ -13,7 +13,7 @@ export type RuntimeAdapters = {
13
13
 
14
14
  const RuntimeAdaptersContext = createContext<RuntimeAdapters | null>(null);
15
15
 
16
- namespace RuntimeAdapterProvider {
16
+ export namespace RuntimeAdapterProvider {
17
17
  export type Props = {
18
18
  adapters: RuntimeAdapters;
19
19
  children: ReactNode;
@@ -19,7 +19,7 @@ function fileMatchesAccept(
19
19
  .map((type) => type.trim().toLowerCase());
20
20
 
21
21
  // Get the file's extension and MIME type
22
- const fileExtension = "." + file.name.split(".").pop()!.toLowerCase();
22
+ const fileExtension = `.${file.name.split(".").pop()!.toLowerCase()}`;
23
23
  const fileMimeType = file.type.toLowerCase();
24
24
 
25
25
  for (const type of allowedTypes) {
@@ -37,7 +37,7 @@ function fileMatchesAccept(
37
37
  // Check for wildcard MIME type match
38
38
  if (type.endsWith("/*")) {
39
39
  const generalType = type.split("/")[0]!;
40
- if (fileMimeType.startsWith(generalType + "/")) {
40
+ if (fileMimeType.startsWith(`${generalType}/`)) {
41
41
  return true;
42
42
  }
43
43
  }
@@ -1 +1 @@
1
- export { type FeedbackAdapter } from "./FeedbackAdapter";
1
+ export type { FeedbackAdapter } from "./FeedbackAdapter";
@@ -1 +1 @@
1
- export { type SuggestionAdapter } from "./SuggestionAdapter";
1
+ export type { SuggestionAdapter } from "./SuggestionAdapter";
@@ -325,6 +325,7 @@ export const useAssistantTransportRuntime = <T,>(
325
325
  return useAssistantTransportThreadRuntime(options);
326
326
  },
327
327
  adapter: new InMemoryThreadListAdapter(),
328
+ allowNesting: true,
328
329
  });
329
330
  return runtime;
330
331
  };
@@ -70,7 +70,7 @@ type ExternalStoreAdapterBase<T> = {
70
70
  onNew: (message: AppendMessage) => Promise<void>;
71
71
  onEdit?: ((message: AppendMessage) => Promise<void>) | undefined;
72
72
  onReload?: // TODO: remove parentId in 0.12.0
73
- | ((parentId: string | null, config: StartRunConfig) => Promise<void>)
73
+ | ((parentId: string | null, config: StartRunConfig) => Promise<void>)
74
74
  | undefined;
75
75
  onResume?: ((config: ResumeRunConfig) => Promise<void>) | undefined;
76
76
  onCancel?: (() => Promise<void>) | undefined;
@@ -132,7 +132,7 @@ export const fromThreadMessageLike = (
132
132
  const { parentId, messages, ...basePart } = part;
133
133
  const commonProps = {
134
134
  ...basePart,
135
- toolCallId: part.toolCallId ?? "tool-" + generateId(),
135
+ toolCallId: part.toolCallId ?? `tool-${generateId()}`,
136
136
  ...(parentId !== undefined && { parentId }),
137
137
  ...(messages !== undefined && { messages }),
138
138
  };
@@ -1,4 +1,4 @@
1
- export { useLocalRuntime, useLocalThreadRuntime } from "./useLocalRuntime";
1
+ export { useLocalRuntime } from "./useLocalRuntime";
2
2
  export type { LocalRuntimeOptions } from "./LocalRuntimeOptions";
3
3
  export type {
4
4
  ChatModelAdapter,
@@ -9,7 +9,7 @@ import { useRemoteThreadListRuntime } from "../remote-thread-list/useRemoteThrea
9
9
  import { useCloudThreadListAdapter } from "../remote-thread-list/adapter/cloud";
10
10
  import { AssistantRuntimeImpl } from "../../../internal";
11
11
 
12
- export const useLocalThreadRuntime = (
12
+ const useLocalThreadRuntime = (
13
13
  adapter: ChatModelAdapter,
14
14
  { initialMessages, ...options }: LocalRuntimeOptions,
15
15
  ) => {
@@ -57,5 +57,6 @@ export const useLocalRuntime = (
57
57
  return useLocalThreadRuntime(adapter, options);
58
58
  },
59
59
  adapter: cloudAdapter,
60
+ allowNesting: true,
60
61
  });
61
62
  };
@@ -1,4 +1,3 @@
1
- /* eslint-disable react-hooks/rules-of-hooks */
2
1
  "use client";
3
2
 
4
3
  import {
@@ -128,7 +127,6 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
128
127
  private _OuterActiveThreadProvider: FC<{
129
128
  threadId: string;
130
129
  provider: ComponentType<PropsWithChildren>;
131
- // eslint-disable-next-line react/display-name
132
130
  }> = memo(({ threadId, provider: Provider }) => {
133
131
  // Runtime is provided by ThreadListItemByIdProvider
134
132
 
@@ -605,9 +605,7 @@ export class RemoteThreadListThreadListRuntimeCore
605
605
  private useBoundIds = create<string[]>(() => []);
606
606
 
607
607
  public __internal_RenderComponent: FC = () => {
608
- // eslint-disable-next-line react-hooks/rules-of-hooks
609
608
  const id = useId();
610
- // eslint-disable-next-line react-hooks/rules-of-hooks
611
609
  useEffect(() => {
612
610
  this.useBoundIds.setState((s) => [...s, id], true);
613
611
  return () => {
@@ -39,4 +39,11 @@ export type RemoteThreadListAdapter = {
39
39
  export type RemoteThreadListOptions = {
40
40
  runtimeHook: () => AssistantRuntime;
41
41
  adapter: RemoteThreadListAdapter;
42
+
43
+ /**
44
+ * When true, if this runtime is used inside another RemoteThreadListRuntime,
45
+ * it becomes a no-op and simply calls the runtimeHook directly.
46
+ * This allows wrapping runtimes that internally use RemoteThreadListRuntime.
47
+ */
48
+ allowNesting?: boolean | undefined;
42
49
  };
@@ -7,6 +7,7 @@ import { RemoteThreadListOptions } from "./types";
7
7
  import { AssistantRuntimeImpl } from "../../../internal";
8
8
  import { AssistantRuntimeCore } from "../core/AssistantRuntimeCore";
9
9
  import { AssistantRuntime } from "../../runtime/AssistantRuntime";
10
+ import { useAssistantApiImpl } from "../../../context/react/AssistantApiContext";
10
11
 
11
12
  class RemoteThreadListRuntimeCore
12
13
  extends BaseAssistantRuntimeCore
@@ -27,7 +28,7 @@ class RemoteThreadListRuntimeCore
27
28
  }
28
29
  }
29
30
 
30
- export const useRemoteThreadListRuntime = (
31
+ const useRemoteThreadListRuntimeImpl = (
31
32
  options: RemoteThreadListOptions,
32
33
  ): AssistantRuntime => {
33
34
  const [runtime] = useState(() => new RemoteThreadListRuntimeCore(options));
@@ -37,3 +38,26 @@ export const useRemoteThreadListRuntime = (
37
38
  }, [runtime, options]);
38
39
  return useMemo(() => new AssistantRuntimeImpl(runtime), [runtime]);
39
40
  };
41
+
42
+ export const useRemoteThreadListRuntime = (
43
+ options: RemoteThreadListOptions,
44
+ ): AssistantRuntime => {
45
+ const api = useAssistantApiImpl();
46
+ const isNested = api.threadListItem.source !== null;
47
+
48
+ if (isNested) {
49
+ if (!options.allowNesting) {
50
+ throw new Error(
51
+ "useRemoteThreadListRuntime cannot be nested inside another RemoteThreadListRuntime. " +
52
+ "Set allowNesting: true to allow nesting (the inner runtime will become a no-op).",
53
+ );
54
+ }
55
+
56
+ // If allowNesting is true and already inside a thread list context,
57
+ // just call the runtimeHook directly (no-op behavior)
58
+ return options.runtimeHook();
59
+ }
60
+
61
+ // eslint-disable-next-line react-hooks/rules-of-hooks
62
+ return useRemoteThreadListRuntimeImpl(options);
63
+ };
@@ -33,7 +33,9 @@ import { useAssistantState, useAssistantApi } from "../../context";
33
33
  */
34
34
  const useActionBarPrimitiveCopy = ({
35
35
  copiedDuration = 3000,
36
- }: { copiedDuration?: number | undefined } = {}) => {
36
+ }: {
37
+ copiedDuration?: number | undefined;
38
+ } = {}) => {
37
39
  const api = useAssistantApi();
38
40
  const hasCopyableContent = useAssistantState(({ message }) => {
39
41
  return (