@assistant-ui/react 0.14.18 → 0.14.20
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.
- package/dist/client/ExternalThread.d.ts +3 -2
- package/dist/client/ExternalThread.d.ts.map +1 -1
- package/dist/client/ExternalThread.js +721 -256
- package/dist/client/ExternalThread.js.map +1 -1
- package/dist/client/InMemoryThreadList.d.ts.map +1 -1
- package/dist/client/InMemoryThreadList.js +292 -108
- package/dist/client/InMemoryThreadList.js.map +1 -1
- package/dist/client/SingleThreadList.js +139 -53
- package/dist/client/SingleThreadList.js.map +1 -1
- package/dist/context/ReadonlyStore.js.map +1 -1
- package/dist/context/providers/MessageProvider.js +38 -5
- package/dist/context/providers/MessageProvider.js.map +1 -1
- package/dist/context/providers/ThreadViewportProvider.js +76 -20
- package/dist/context/providers/ThreadViewportProvider.js.map +1 -1
- package/dist/context/react/ThreadViewportContext.js.map +1 -1
- package/dist/context/react/utils/createContextHook.js.map +1 -1
- package/dist/context/react/utils/createContextStoreHook.js +17 -2
- package/dist/context/react/utils/createContextStoreHook.js.map +1 -1
- package/dist/context/react/utils/createStateHookForRuntime.js.map +1 -1
- package/dist/context/react/utils/ensureBinding.js.map +1 -1
- package/dist/context/react/utils/useRuntimeState.js +18 -2
- package/dist/context/react/utils/useRuntimeState.js.map +1 -1
- package/dist/context/stores/ThreadViewport.js.map +1 -1
- package/dist/devtools/DevToolsHooks.js.map +1 -1
- package/dist/hooks/useMessageQuote.js.map +1 -1
- package/dist/hooks/useMessageTiming.js +4 -1
- package/dist/hooks/useMessageTiming.js.map +1 -1
- package/dist/hooks/useToolCallElapsed.d.ts +23 -0
- package/dist/hooks/useToolCallElapsed.d.ts.map +1 -0
- package/dist/hooks/useToolCallElapsed.js +72 -0
- package/dist/hooks/useToolCallElapsed.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/internal.js.map +1 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.js +46 -10
- package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
- package/dist/legacy-runtime/cloud/auiV0.js.map +1 -1
- package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.js +27 -6
- package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.js.map +1 -1
- package/dist/legacy-runtime/hooks/AssistantContext.js +13 -2
- package/dist/legacy-runtime/hooks/AssistantContext.js.map +1 -1
- package/dist/legacy-runtime/hooks/AttachmentContext.js +9 -1
- package/dist/legacy-runtime/hooks/AttachmentContext.js.map +1 -1
- package/dist/legacy-runtime/hooks/ComposerContext.js +9 -1
- package/dist/legacy-runtime/hooks/ComposerContext.js.map +1 -1
- package/dist/legacy-runtime/hooks/MessageContext.js +12 -2
- package/dist/legacy-runtime/hooks/MessageContext.js.map +1 -1
- package/dist/legacy-runtime/hooks/MessagePartContext.js +9 -1
- package/dist/legacy-runtime/hooks/MessagePartContext.js.map +1 -1
- package/dist/legacy-runtime/hooks/ThreadContext.js +33 -5
- package/dist/legacy-runtime/hooks/ThreadContext.js.map +1 -1
- package/dist/legacy-runtime/hooks/ThreadListItemContext.js +9 -1
- package/dist/legacy-runtime/hooks/ThreadListItemContext.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +3 -3
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.js +71 -31
- package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +24 -16
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +17 -12
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js +17 -3
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.js +6 -6
- package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/mcp-apps/McpAppsRemoteHost.js +3 -3
- package/dist/mcp-apps/McpAppsRemoteHost.js.map +1 -1
- package/dist/mcp-apps/app-frame.js +33 -14
- package/dist/mcp-apps/app-frame.js.map +1 -1
- package/dist/mcp-apps/bridge.js.map +1 -1
- package/dist/mcp-apps/types.js.map +1 -1
- package/dist/mcp-apps/utils.js.map +1 -1
- package/dist/model-context/frame/useAssistantFrameHost.js +32 -14
- package/dist/model-context/frame/useAssistantFrameHost.js.map +1 -1
- package/dist/model-context/makeAssistantVisible.js +64 -26
- package/dist/model-context/makeAssistantVisible.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarCopy.js +94 -20
- package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarEdit.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js +105 -37
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.js +60 -11
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.js +60 -11
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarInteractionContext.js +3 -1
- package/dist/primitives/actionBar/ActionBarInteractionContext.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarReload.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarRoot.js +84 -25
- package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarSpeak.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarStopSpeaking.js +45 -14
- package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -1
- package/dist/primitives/actionBar/useActionBarFloatStatus.js +22 -10
- package/dist/primitives/actionBar/useActionBarFloatStatus.js.map +1 -1
- package/dist/primitives/actionBar.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreContent.js +44 -7
- package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreItem.js +28 -6
- package/dist/primitives/actionBarMore/ActionBarMoreItem.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreRoot.js +103 -36
- package/dist/primitives/actionBarMore/ActionBarMoreRoot.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js +28 -6
- package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js +28 -6
- package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js.map +1 -1
- package/dist/primitives/actionBarMore/scope.js.map +1 -1
- package/dist/primitives/actionBarMore.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalAnchor.js +27 -6
- package/dist/primitives/assistantModal/AssistantModalAnchor.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalContent.js +71 -10
- package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js +93 -26
- package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalTrigger.js +27 -6
- package/dist/primitives/assistantModal/AssistantModalTrigger.js.map +1 -1
- package/dist/primitives/assistantModal/scope.js.map +1 -1
- package/dist/primitives/assistantModal.js.map +1 -1
- package/dist/primitives/attachment/AttachmentName.js +13 -1
- package/dist/primitives/attachment/AttachmentName.js.map +1 -1
- package/dist/primitives/attachment/AttachmentRemove.js +11 -4
- package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
- package/dist/primitives/attachment/AttachmentRoot.js +13 -4
- package/dist/primitives/attachment/AttachmentRoot.js.map +1 -1
- package/dist/primitives/attachment/AttachmentThumb.js +20 -9
- package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
- package/dist/primitives/attachment.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerCount.js +14 -2
- package/dist/primitives/branchPicker/BranchPickerCount.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNext.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNumber.js +14 -2
- package/dist/primitives/branchPicker/BranchPickerNumber.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerPrevious.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerRoot.js +34 -6
- package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -1
- package/dist/primitives/branchPicker.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +16 -5
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js +13 -4
- package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js.map +1 -1
- package/dist/primitives/chainOfThought.js.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js +37 -24
- package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +124 -49
- package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
- package/dist/primitives/composer/ComposerCancel.js.map +1 -1
- package/dist/primitives/composer/ComposerDictate.js.map +1 -1
- package/dist/primitives/composer/ComposerDictationTranscript.js +32 -7
- package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +26 -26
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerInputPluginContext.js +71 -25
- package/dist/primitives/composer/ComposerInputPluginContext.js.map +1 -1
- package/dist/primitives/composer/ComposerQuote.js +92 -23
- package/dist/primitives/composer/ComposerQuote.js.map +1 -1
- package/dist/primitives/composer/ComposerRoot.js +45 -11
- package/dist/primitives/composer/ComposerRoot.js.map +1 -1
- package/dist/primitives/composer/ComposerSend.js +9 -2
- package/dist/primitives/composer/ComposerSend.js.map +1 -1
- package/dist/primitives/composer/ComposerStopDictation.js +15 -5
- package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopover.js +215 -75
- package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverAction.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverBack.js +35 -7
- package/dist/primitives/composer/trigger/TriggerPopoverBack.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.js +134 -28
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.js +132 -28
- package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverResource.js +124 -52
- package/dist/primitives/composer/trigger/TriggerPopoverResource.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js +181 -78
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
- package/dist/primitives/composer/trigger/detectTrigger.js.map +1 -1
- package/dist/primitives/composer/trigger/index.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerDetectionResource.js +28 -14
- package/dist/primitives/composer/trigger/triggerDetectionResource.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerKeyboardResource.js +115 -58
- package/dist/primitives/composer/trigger/triggerKeyboardResource.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerNavigationResource.js +202 -70
- package/dist/primitives/composer/trigger/triggerNavigationResource.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerSelectionResource.js +49 -13
- package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
- package/dist/primitives/composer.js.map +1 -1
- package/dist/primitives/dropdownMenuRenderPrimitives.js.map +1 -1
- package/dist/primitives/error/ErrorMessage.js +28 -6
- package/dist/primitives/error/ErrorMessage.js.map +1 -1
- package/dist/primitives/error/ErrorRoot.js +14 -5
- package/dist/primitives/error/ErrorRoot.js.map +1 -1
- package/dist/primitives/error.js.map +1 -1
- package/dist/primitives/message/MessageError.js +2 -1
- package/dist/primitives/message/MessageError.js.map +1 -1
- package/dist/primitives/message/MessageIf.js +50 -20
- package/dist/primitives/message/MessageIf.js.map +1 -1
- package/dist/primitives/message/MessageParts.js +41 -7
- package/dist/primitives/message/MessageParts.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js +399 -94
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/primitives/message/MessageRoot.js +197 -65
- package/dist/primitives/message/MessageRoot.js.map +1 -1
- package/dist/primitives/message.js.map +1 -1
- package/dist/primitives/messagePart/MessagePartImage.js +15 -5
- package/dist/primitives/messagePart/MessagePartImage.js.map +1 -1
- package/dist/primitives/messagePart/MessagePartText.js +35 -7
- package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartData.js +5 -4
- package/dist/primitives/messagePart/useMessagePartData.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartFile.js +5 -4
- package/dist/primitives/messagePart/useMessagePartFile.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartImage.js +5 -4
- package/dist/primitives/messagePart/useMessagePartImage.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartReasoning.js +5 -4
- package/dist/primitives/messagePart/useMessagePartReasoning.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartSource.js +5 -4
- package/dist/primitives/messagePart/useMessagePartSource.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartText.js +5 -4
- package/dist/primitives/messagePart/useMessagePartText.js.map +1 -1
- package/dist/primitives/messagePart.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.js +11 -4
- package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.js +11 -4
- package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemText.js +20 -6
- package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
- package/dist/primitives/queueItem.js.map +1 -1
- package/dist/primitives/reasoning/useScrollLock.js +61 -43
- package/dist/primitives/reasoning/useScrollLock.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +56 -16
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +120 -59
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
- package/dist/primitives/selectionToolbar.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionDescription.js +20 -6
- package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTitle.js +20 -6
- package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.js +39 -26
- package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
- package/dist/primitives/suggestion.js.map +1 -1
- package/dist/primitives/thread/ThreadEmpty.js +6 -2
- package/dist/primitives/thread/ThreadEmpty.js.map +1 -1
- package/dist/primitives/thread/ThreadIf.js +32 -10
- package/dist/primitives/thread/ThreadIf.js.map +1 -1
- package/dist/primitives/thread/ThreadRoot.js +13 -4
- package/dist/primitives/thread/ThreadRoot.js.map +1 -1
- package/dist/primitives/thread/ThreadScrollToBottom.js +24 -6
- package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
- package/dist/primitives/thread/ThreadSuggestion.js +18 -6
- package/dist/primitives/thread/ThreadSuggestion.js.map +1 -1
- package/dist/primitives/thread/ThreadViewport.js +185 -47
- package/dist/primitives/thread/ThreadViewport.js.map +1 -1
- package/dist/primitives/thread/ThreadViewportFooter.js +22 -9
- package/dist/primitives/thread/ThreadViewportFooter.js.map +1 -1
- package/dist/primitives/thread/topAnchor/computeTopAnchorSlack.js.map +1 -1
- package/dist/primitives/thread/topAnchor/createReserveObservers.js.map +1 -1
- package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js.map +1 -1
- package/dist/primitives/thread/topAnchor/topAnchorTurn.js.map +1 -1
- package/dist/primitives/thread/topAnchor/topAnchorUtils.js.map +1 -1
- package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js +19 -4
- package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +16 -16
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/primitives/thread.js.map +1 -1
- package/dist/primitives/threadList/ThreadListLoadMore.js.map +1 -1
- package/dist/primitives/threadList/ThreadListNew.js +53 -11
- package/dist/primitives/threadList/ThreadListNew.js.map +1 -1
- package/dist/primitives/threadList/ThreadListRoot.js +13 -4
- package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
- package/dist/primitives/threadList.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemArchive.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemDelete.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemRoot.js +26 -7
- package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemUnarchive.js.map +1 -1
- package/dist/primitives/threadListItem.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js +44 -7
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js +28 -6
- package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js +25 -5
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js +28 -6
- package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js +28 -6
- package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js.map +1 -1
- package/dist/primitives/threadListItemMore/scope.js.map +1 -1
- package/dist/primitives/threadListItemMore.js.map +1 -1
- package/dist/sandbox-host/SandboxHost.js.map +1 -1
- package/dist/tests/remote-thread-list-test-helpers.js.map +1 -1
- package/dist/tests/setup.js.map +1 -1
- package/dist/unstable/useComposerInputHistory.js.map +1 -1
- package/dist/unstable/useMentionAdapter.js.map +1 -1
- package/dist/unstable/useMessageStallDetection.d.ts +29 -0
- package/dist/unstable/useMessageStallDetection.d.ts.map +1 -0
- package/dist/unstable/useMessageStallDetection.js +69 -0
- package/dist/unstable/useMessageStallDetection.js.map +1 -0
- package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
- package/dist/utils/Primitive.js +57 -12
- package/dist/utils/Primitive.js.map +1 -1
- package/dist/utils/createActionButton.js +23 -7
- package/dist/utils/createActionButton.js.map +1 -1
- package/dist/utils/getSelectionMessageId.js.map +1 -1
- package/dist/utils/hooks/useManagedRef.js +16 -8
- package/dist/utils/hooks/useManagedRef.js.map +1 -1
- package/dist/utils/hooks/useMediaQuery.js +25 -10
- package/dist/utils/hooks/useMediaQuery.js.map +1 -1
- package/dist/utils/hooks/useOnResizeContent.js +29 -19
- package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
- package/dist/utils/hooks/useOnScrollToBottom.js +20 -4
- package/dist/utils/hooks/useOnScrollToBottom.js.map +1 -1
- package/dist/utils/hooks/useSizeHandle.js +23 -15
- package/dist/utils/hooks/useSizeHandle.js.map +1 -1
- package/dist/utils/json/is-json-equal.js.map +1 -1
- package/dist/utils/json/is-json.js.map +1 -1
- package/dist/utils/smooth/SmoothContext.js +41 -11
- package/dist/utils/smooth/SmoothContext.js.map +1 -1
- package/dist/utils/smooth/useSmooth.js +5 -5
- package/dist/utils/smooth/useSmooth.js.map +1 -1
- package/dist/utils/useToolArgsFieldStatus.js +13 -5
- package/dist/utils/useToolArgsFieldStatus.js.map +1 -1
- package/package.json +9 -9
- package/src/client/ExternalThread.ts +81 -52
- package/src/client/InMemoryThreadList.ts +12 -14
- package/src/hooks/useToolCallElapsed.ts +52 -0
- package/src/index.ts +11 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.test.ts +10 -6
- package/src/primitives/composer/trigger/TriggerPopover.tsx +4 -5
- package/src/tests/toolCallTiming.test.tsx +221 -0
- package/src/unstable/useMessageStallDetection.ts +91 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSmooth.js","names":[],"sources":["../../../src/utils/smooth/useSmooth.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type {\n MessagePartStatus,\n ReasoningMessagePart,\n TextMessagePart,\n MessagePartState,\n} from \"@assistant-ui/core\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\n\n/**\n * Tuning options for the smooth text streaming animation.\n */\nexport type SmoothOptions = {\n /**\n * Target time in milliseconds to drain the backlog of unrevealed\n * characters. Larger values reveal long backlogs more gradually.\n * @default 250\n */\n drainMs?: number | undefined;\n /**\n * Maximum time in milliseconds between revealed characters, i.e. the\n * slowest reveal rate when the backlog is short.\n * @default 5\n */\n maxCharIntervalMs?: number | undefined;\n /**\n * Maximum number of characters revealed per animation frame.\n * @default Infinity\n */\n maxCharsPerFrame?: number | undefined;\n};\n\nconst DEFAULT_DRAIN_MS = 250;\nconst DEFAULT_MAX_CHAR_INTERVAL_MS = 5;\n\nclass TextStreamAnimator {\n private animationFrameId: number | null = null;\n private lastUpdateTime: number = Date.now();\n\n public targetText: string = \"\";\n public drainMs: number = DEFAULT_DRAIN_MS;\n public maxCharIntervalMs: number = DEFAULT_MAX_CHAR_INTERVAL_MS;\n public maxCharsPerFrame: number = Infinity;\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(\n this.maxCharIntervalMs,\n this.drainMs / remainingChars,\n );\n\n const frameLimit = Math.min(remainingChars, this.maxCharsPerFrame);\n let charsToAdd = 0;\n while (timeToConsume >= baseTimePerChar && charsToAdd < frameLimit) {\n charsToAdd++;\n timeToConsume -= baseTimePerChar;\n }\n // A cap-limited frame must not bank its surplus time, or the next\n // frame would burst past the cap.\n if (charsToAdd === frameLimit && frameLimit === this.maxCharsPerFrame) {\n timeToConsume = 0;\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\nconst positiveOr = (value: number | undefined, fallback: number): number =>\n value !== undefined && value > 0 ? value : fallback;\n\n/**\n * Animates streamed message part text with a typewriter-style reveal.\n *\n * Takes the current part state and a `smooth` argument: `false` disables,\n * `true` uses the default rate, and a {@link SmoothOptions} object tunes\n * the reveal. Returns the part state with `text` replaced by the revealed\n * prefix and `status` reporting `running` until the reveal catches up.\n *\n * @example\n * ```tsx\n * const { text, status } = useSmooth(useMessagePartText(), {\n * drainMs: 500,\n * maxCharsPerFrame: 30,\n * });\n * ```\n */\nexport const useSmooth = (\n state: MessagePartState & (TextMessagePart | ReasoningMessagePart),\n smooth: boolean | SmoothOptions = false,\n): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {\n const { text } = state;\n const options =\n typeof smooth === \"object\" && smooth !== null ? smooth : undefined;\n const enabled = smooth !== false && smooth !== null;\n const drainMs = positiveOr(options?.drainMs, DEFAULT_DRAIN_MS);\n const maxCharIntervalMs = positiveOr(\n options?.maxCharIntervalMs,\n DEFAULT_MAX_CHAR_INTERVAL_MS,\n );\n const maxCharsPerFrame = positiveOr(options?.maxCharsPerFrame, Infinity);\n\n const [displayedText, setDisplayedText] = useState(\n state.status.type === \"running\" ? \"\" : text,\n );\n\n // Render-phase resync on part flip or text discontinuity, so the\n // first paint after a thread switch never shows the previous\n // part's text (#4051). `displayedText` is already a prefix of\n // `text` during normal streaming, so use it as the previous-text\n // reference instead of carrying separate state — avoids the\n // double render per streaming token. Read part identity through\n // `useAuiState` so we actually subscribe to its changes instead\n // of relying on a render-time proxy reference that may be stable\n // across thread swaps.\n const aui = useAui();\n const part = useAuiState(() => aui.part());\n const [prevPart, setPrevPart] = useState(part);\n if (part !== prevPart || !text.startsWith(displayedText)) {\n setPrevPart(part);\n setDisplayedText(state.status.type === \"running\" ? \"\" : text);\n }\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 enabled && (displayedText !== text || state.status.type === \"running\")\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n }, [smoothStatusStore, enabled, text, displayedText, state.status]);\n\n const [animatorRef] = useState<TextStreamAnimator>(\n new TextStreamAnimator(displayedText, setText),\n );\n\n useEffect(() => {\n animatorRef.drainMs = drainMs;\n animatorRef.maxCharIntervalMs = maxCharIntervalMs;\n animatorRef.maxCharsPerFrame = maxCharsPerFrame;\n }, [animatorRef, drainMs, maxCharIntervalMs, maxCharsPerFrame]);\n\n const animatorPartRef = useRef(part);\n useEffect(() => {\n if (!enabled) {\n animatorRef.stop();\n return;\n }\n\n // Discontinuity: part flipped, or new text breaks continuation\n // of the animator's current target. Either case requires\n // resetting the cursor — without the part check, a new part\n // whose text happens to share a prefix with the previous target\n // would keep the stale cursor and flicker.\n const partChanged = animatorPartRef.current !== part;\n animatorPartRef.current = part;\n if (partChanged || !text.startsWith(animatorRef.targetText)) {\n if (state.status.type === \"running\") {\n animatorRef.currentText = \"\";\n animatorRef.targetText = text;\n animatorRef.start();\n } else {\n animatorRef.currentText = text;\n animatorRef.targetText = text;\n animatorRef.stop();\n }\n return;\n }\n\n animatorRef.targetText = text;\n animatorRef.start();\n }, [animatorRef, enabled, text, state.status.type, part]);\n\n useEffect(() => {\n return () => {\n animatorRef.stop();\n };\n }, [animatorRef]);\n\n return useMemo(\n () =>\n enabled\n ? {\n ...state,\n text: displayedText,\n status: text === displayedText ? state.status : SMOOTH_STATUS,\n }\n : state,\n [enabled, displayedText, state, text],\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useSmooth.js","names":["useEffect","useMemo","useRef","useState","useAui","useAuiState","MessagePartStatus","ReasoningMessagePart","TextMessagePart","MessagePartState","useCallbackRef","useSmoothStatusStore","writableStore","SmoothOptions","drainMs","maxCharIntervalMs","maxCharsPerFrame","DEFAULT_DRAIN_MS","DEFAULT_MAX_CHAR_INTERVAL_MS","TextStreamAnimator","animationFrameId","lastUpdateTime","Date","now","targetText","Infinity","constructor","currentText","setText","newText","start","animate","stop","cancelAnimationFrame","currentTime","deltaTime","timeToConsume","remainingChars","length","baseTimePerChar","Math","min","frameLimit","charsToAdd","requestAnimationFrame","slice","SMOOTH_STATUS","Object","freeze","type","positiveOr","value","fallback","undefined","useSmooth","state","smooth","text","options","enabled","displayedText","setDisplayedText","status","aui","part","prevPart","setPrevPart","startsWith","smoothStatusStore","optional","target","setState","animatorRef","animatorPartRef","partChanged","current"],"sources":["../../../src/utils/smooth/useSmooth.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type {\n MessagePartStatus,\n ReasoningMessagePart,\n TextMessagePart,\n MessagePartState,\n} from \"@assistant-ui/core\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\n\n/**\n * Tuning options for the smooth text streaming animation.\n */\nexport type SmoothOptions = {\n /**\n * Target time in milliseconds to drain the backlog of unrevealed\n * characters. Larger values reveal long backlogs more gradually.\n * @default 250\n */\n drainMs?: number | undefined;\n /**\n * Maximum time in milliseconds between revealed characters, i.e. the\n * slowest reveal rate when the backlog is short.\n * @default 5\n */\n maxCharIntervalMs?: number | undefined;\n /**\n * Maximum number of characters revealed per animation frame.\n * @default Infinity\n */\n maxCharsPerFrame?: number | undefined;\n};\n\nconst DEFAULT_DRAIN_MS = 250;\nconst DEFAULT_MAX_CHAR_INTERVAL_MS = 5;\n\nclass TextStreamAnimator {\n private animationFrameId: number | null = null;\n private lastUpdateTime: number = Date.now();\n\n public targetText: string = \"\";\n public drainMs: number = DEFAULT_DRAIN_MS;\n public maxCharIntervalMs: number = DEFAULT_MAX_CHAR_INTERVAL_MS;\n public maxCharsPerFrame: number = Infinity;\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(\n this.maxCharIntervalMs,\n this.drainMs / remainingChars,\n );\n\n const frameLimit = Math.min(remainingChars, this.maxCharsPerFrame);\n let charsToAdd = 0;\n while (timeToConsume >= baseTimePerChar && charsToAdd < frameLimit) {\n charsToAdd++;\n timeToConsume -= baseTimePerChar;\n }\n // A cap-limited frame must not bank its surplus time, or the next\n // frame would burst past the cap.\n if (charsToAdd === frameLimit && frameLimit === this.maxCharsPerFrame) {\n timeToConsume = 0;\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\nconst positiveOr = (value: number | undefined, fallback: number): number =>\n value !== undefined && value > 0 ? value : fallback;\n\n/**\n * Animates streamed message part text with a typewriter-style reveal.\n *\n * Takes the current part state and a `smooth` argument: `false` disables,\n * `true` uses the default rate, and a {@link SmoothOptions} object tunes\n * the reveal. Returns the part state with `text` replaced by the revealed\n * prefix and `status` reporting `running` until the reveal catches up.\n *\n * @example\n * ```tsx\n * const { text, status } = useSmooth(useMessagePartText(), {\n * drainMs: 500,\n * maxCharsPerFrame: 30,\n * });\n * ```\n */\nexport const useSmooth = (\n state: MessagePartState & (TextMessagePart | ReasoningMessagePart),\n smooth: boolean | SmoothOptions = false,\n): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {\n const { text } = state;\n const options =\n typeof smooth === \"object\" && smooth !== null ? smooth : undefined;\n const enabled = smooth !== false && smooth !== null;\n const drainMs = positiveOr(options?.drainMs, DEFAULT_DRAIN_MS);\n const maxCharIntervalMs = positiveOr(\n options?.maxCharIntervalMs,\n DEFAULT_MAX_CHAR_INTERVAL_MS,\n );\n const maxCharsPerFrame = positiveOr(options?.maxCharsPerFrame, Infinity);\n\n const [displayedText, setDisplayedText] = useState(\n state.status.type === \"running\" ? \"\" : text,\n );\n\n // Render-phase resync on part flip or text discontinuity, so the\n // first paint after a thread switch never shows the previous\n // part's text (#4051). `displayedText` is already a prefix of\n // `text` during normal streaming, so use it as the previous-text\n // reference instead of carrying separate state — avoids the\n // double render per streaming token. Read part identity through\n // `useAuiState` so we actually subscribe to its changes instead\n // of relying on a render-time proxy reference that may be stable\n // across thread swaps.\n const aui = useAui();\n const part = useAuiState(() => aui.part());\n const [prevPart, setPrevPart] = useState(part);\n if (part !== prevPart || !text.startsWith(displayedText)) {\n setPrevPart(part);\n setDisplayedText(state.status.type === \"running\" ? \"\" : text);\n }\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 enabled && (displayedText !== text || state.status.type === \"running\")\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n }, [smoothStatusStore, enabled, text, displayedText, state.status]);\n\n const [animatorRef] = useState<TextStreamAnimator>(\n new TextStreamAnimator(displayedText, setText),\n );\n\n useEffect(() => {\n animatorRef.drainMs = drainMs;\n animatorRef.maxCharIntervalMs = maxCharIntervalMs;\n animatorRef.maxCharsPerFrame = maxCharsPerFrame;\n }, [animatorRef, drainMs, maxCharIntervalMs, maxCharsPerFrame]);\n\n const animatorPartRef = useRef(part);\n useEffect(() => {\n if (!enabled) {\n animatorRef.stop();\n return;\n }\n\n // Discontinuity: part flipped, or new text breaks continuation\n // of the animator's current target. Either case requires\n // resetting the cursor — without the part check, a new part\n // whose text happens to share a prefix with the previous target\n // would keep the stale cursor and flicker.\n const partChanged = animatorPartRef.current !== part;\n animatorPartRef.current = part;\n if (partChanged || !text.startsWith(animatorRef.targetText)) {\n if (state.status.type === \"running\") {\n animatorRef.currentText = \"\";\n animatorRef.targetText = text;\n animatorRef.start();\n } else {\n animatorRef.currentText = text;\n animatorRef.targetText = text;\n animatorRef.stop();\n }\n return;\n }\n\n animatorRef.targetText = text;\n animatorRef.start();\n }, [animatorRef, enabled, text, state.status.type, part]);\n\n useEffect(() => {\n return () => {\n animatorRef.stop();\n };\n }, [animatorRef]);\n\n return useMemo(\n () =>\n enabled\n ? {\n ...state,\n text: displayedText,\n status: text === displayedText ? state.status : SMOOTH_STATUS,\n }\n : state,\n [enabled, displayedText, state, text],\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { useAuiState } from "@assistant-ui/store";
|
|
2
|
+
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
2
3
|
import { getPartialJsonObjectFieldState } from "assistant-stream/utils";
|
|
3
4
|
//#region src/utils/useToolArgsFieldStatus.ts
|
|
4
5
|
const COMPLETE_STATUS = { type: "complete" };
|
|
5
6
|
const useToolArgsFieldStatus = (fieldPath) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const $ = c(2);
|
|
8
|
+
let t0;
|
|
9
|
+
if ($[0] !== fieldPath) {
|
|
10
|
+
t0 = (s) => {
|
|
11
|
+
if (s.part.type !== "tool-call") throw new Error("useToolArgsFieldStatus can only be used inside tool-call message parts");
|
|
12
|
+
if (getPartialJsonObjectFieldState(s.part.args, fieldPath) === "complete" || s.part.status?.type === "requires-action") return COMPLETE_STATUS;
|
|
13
|
+
return s.part.status;
|
|
14
|
+
};
|
|
15
|
+
$[0] = fieldPath;
|
|
16
|
+
$[1] = t0;
|
|
17
|
+
} else t0 = $[1];
|
|
18
|
+
return useAuiState(t0);
|
|
11
19
|
};
|
|
12
20
|
//#endregion
|
|
13
21
|
export { useToolArgsFieldStatus };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useToolArgsFieldStatus.js","names":[],"sources":["../../src/utils/useToolArgsFieldStatus.ts"],"sourcesContent":["import { getPartialJsonObjectFieldState } from \"assistant-stream/utils\";\nimport { useAuiState } from \"@assistant-ui/store\";\n\nconst COMPLETE_STATUS = { type: \"complete\" };\n\nexport const useToolArgsFieldStatus = (fieldPath: (string | number)[]) => {\n return useAuiState((s) => {\n if (s.part.type !== \"tool-call\")\n throw new Error(\n \"useToolArgsFieldStatus can only be used inside tool-call message parts\",\n );\n\n const state = getPartialJsonObjectFieldState(s.part.args, fieldPath);\n if (state === \"complete\" || s.part.status?.type === \"requires-action\")\n return COMPLETE_STATUS;\n return s.part.status;\n });\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"useToolArgsFieldStatus.js","names":["getPartialJsonObjectFieldState","useAuiState","COMPLETE_STATUS","type","useToolArgsFieldStatus","fieldPath","$","_c","t0","s","part","Error","state","args","status"],"sources":["../../src/utils/useToolArgsFieldStatus.ts"],"sourcesContent":["import { getPartialJsonObjectFieldState } from \"assistant-stream/utils\";\nimport { useAuiState } from \"@assistant-ui/store\";\n\nconst COMPLETE_STATUS = { type: \"complete\" };\n\nexport const useToolArgsFieldStatus = (fieldPath: (string | number)[]) => {\n return useAuiState((s) => {\n if (s.part.type !== \"tool-call\")\n throw new Error(\n \"useToolArgsFieldStatus can only be used inside tool-call message parts\",\n );\n\n const state = getPartialJsonObjectFieldState(s.part.args, fieldPath);\n if (state === \"complete\" || s.part.status?.type === \"requires-action\")\n return COMPLETE_STATUS;\n return s.part.status;\n });\n};\n"],"mappings":";;;;AAGA,MAAME,kBAAkB,EAAEC,MAAM,WAAW;AAE3C,MAAaC,0BAAyBC,cAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAA,IAAAC;CAAA,IAAAF,EAAA,OAAAD,WAAA;EACjBG,MAAAC,MAAA;GACjB,IAAIA,EAACC,KAAKP,SAAU,aAClB,MAAM,IAAIQ,MACR,wEACF;GAGF,IADcX,+BAA+BS,EAACC,KAAKG,MAAOR,SACtDO,MAAU,cAAcH,EAACC,KAAKI,QAAaX,SAAK,mBAAiB,OAC5DD;GAAgB,OAClBO,EAACC,KAAKI;EAAO;EACrBR,EAAA,KAAAD;EAAAC,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CAAA,OAVML,YAAYO,EAUlB;AAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.20",
|
|
4
4
|
"description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -55,17 +55,17 @@
|
|
|
55
55
|
],
|
|
56
56
|
"sideEffects": false,
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@assistant-ui/core": "^0.2.
|
|
59
|
-
"@assistant-ui/store": "^0.2.
|
|
60
|
-
"@assistant-ui/tap": "^0.
|
|
58
|
+
"@assistant-ui/core": "^0.2.16",
|
|
59
|
+
"@assistant-ui/store": "^0.2.18",
|
|
60
|
+
"@assistant-ui/tap": "^0.9.1",
|
|
61
61
|
"@radix-ui/primitive": "^1.1.4",
|
|
62
62
|
"@radix-ui/react-compose-refs": "^1.1.3",
|
|
63
63
|
"@radix-ui/react-context": "^1.1.4",
|
|
64
64
|
"@radix-ui/react-primitive": "^2.1.5",
|
|
65
65
|
"@radix-ui/react-use-callback-ref": "^1.1.2",
|
|
66
66
|
"@radix-ui/react-use-escape-keydown": "^1.1.2",
|
|
67
|
-
"assistant-cloud": "^0.1.
|
|
68
|
-
"assistant-stream": "^0.3.
|
|
67
|
+
"assistant-cloud": "^0.1.33",
|
|
68
|
+
"assistant-stream": "^0.3.23",
|
|
69
69
|
"nanoid": "^5.1.11",
|
|
70
70
|
"radix-ui": "^1.5.0",
|
|
71
71
|
"react-textarea-autosize": "^8.5.9",
|
|
@@ -90,15 +90,15 @@
|
|
|
90
90
|
"devDependencies": {
|
|
91
91
|
"@testing-library/react": "^16.3.2",
|
|
92
92
|
"@types/json-schema": "^7.0.15",
|
|
93
|
-
"@types/node": "^25.9.
|
|
93
|
+
"@types/node": "^25.9.3",
|
|
94
94
|
"@types/react": "^19.2.17",
|
|
95
95
|
"@types/react-dom": "^19.2.3",
|
|
96
96
|
"jsdom": "^29.1.1",
|
|
97
97
|
"react": "^19.2.7",
|
|
98
98
|
"react-dom": "^19.2.7",
|
|
99
99
|
"vitest": "^4.1.8",
|
|
100
|
-
"@assistant-ui/vite": "0.0.
|
|
101
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
100
|
+
"@assistant-ui/vite": "0.0.5",
|
|
101
|
+
"@assistant-ui/x-buildutils": "0.0.14"
|
|
102
102
|
},
|
|
103
103
|
"publishConfig": {
|
|
104
104
|
"access": "public",
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
AppendMessage,
|
|
14
14
|
Attachment,
|
|
15
15
|
CreateAttachment,
|
|
16
|
+
RespondToToolApprovalOptions,
|
|
16
17
|
ThreadAssistantMessagePart,
|
|
17
18
|
ThreadUserMessagePart,
|
|
18
19
|
ThreadMessage,
|
|
@@ -21,7 +22,10 @@ import type {
|
|
|
21
22
|
} from "@assistant-ui/core";
|
|
22
23
|
import type { QueueItemState } from "@assistant-ui/core/store";
|
|
23
24
|
import type { ComposerSendOptions } from "@assistant-ui/core/store";
|
|
24
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
getThreadMessageText,
|
|
27
|
+
resolveToolApprovalResponse,
|
|
28
|
+
} from "@assistant-ui/core/internal";
|
|
25
29
|
import { ModelContext, Suggestions } from "@assistant-ui/core/store";
|
|
26
30
|
import { Tools, DataRenderers } from "@assistant-ui/core/react";
|
|
27
31
|
import { SingleThreadList } from "./SingleThreadList";
|
|
@@ -56,6 +60,8 @@ export type ExternalThreadProps = {
|
|
|
56
60
|
queue?: ExternalThreadQueueAdapter;
|
|
57
61
|
/** Branch adapter for runtimes that track sibling variants of messages. */
|
|
58
62
|
branches?: ExternalThreadBranchAdapter;
|
|
63
|
+
/** Callback for tool approval decisions. Absent: responding to an approval throws a capability error. */
|
|
64
|
+
onRespondToToolApproval?: (options: RespondToToolApprovalOptions) => void;
|
|
59
65
|
};
|
|
60
66
|
|
|
61
67
|
type MessageClientProps = {
|
|
@@ -65,6 +71,9 @@ type MessageClientProps = {
|
|
|
65
71
|
onReload?: () => void;
|
|
66
72
|
queue?: ExternalThreadQueueAdapter | undefined;
|
|
67
73
|
branches?: ExternalThreadBranchAdapter | undefined;
|
|
74
|
+
onRespondToToolApproval?:
|
|
75
|
+
| ((options: RespondToToolApprovalOptions) => void)
|
|
76
|
+
| undefined;
|
|
68
77
|
};
|
|
69
78
|
|
|
70
79
|
// Message Client - minimal implementation
|
|
@@ -75,29 +84,28 @@ const useMessageClient = ({
|
|
|
75
84
|
onReload,
|
|
76
85
|
queue,
|
|
77
86
|
branches,
|
|
87
|
+
onRespondToToolApproval,
|
|
78
88
|
}: MessageClientProps): ClientOutput<"message"> => {
|
|
79
89
|
const [isCopied, setIsCopied] = useState(false);
|
|
80
90
|
const [isHovering, setIsHovering] = useState(false);
|
|
81
91
|
const [isEditing, setIsEditing] = useState(false);
|
|
82
92
|
|
|
83
93
|
const partClients = useClientLookup(
|
|
84
|
-
() =>
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
message.content.map((part, idx) =>
|
|
95
|
+
withKey(idx, PartResource({ part, onRespondToToolApproval })),
|
|
96
|
+
),
|
|
87
97
|
);
|
|
88
98
|
|
|
89
99
|
const attachmentClients = useClientLookup(
|
|
90
|
-
() =>
|
|
91
|
-
(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}),
|
|
98
|
-
),
|
|
100
|
+
(message.attachments ?? []).map((attachment) =>
|
|
101
|
+
withKey(
|
|
102
|
+
attachment.id,
|
|
103
|
+
AttachmentResource({
|
|
104
|
+
attachment,
|
|
105
|
+
onRemove: () => {},
|
|
106
|
+
}),
|
|
99
107
|
),
|
|
100
|
-
|
|
108
|
+
),
|
|
101
109
|
);
|
|
102
110
|
|
|
103
111
|
const handleBeginEdit = () => {
|
|
@@ -211,10 +219,16 @@ const MessageClient = resource(useMessageClient);
|
|
|
211
219
|
|
|
212
220
|
type PartResourceProps = {
|
|
213
221
|
part: ThreadAssistantMessagePart | ThreadUserMessagePart;
|
|
222
|
+
onRespondToToolApproval?:
|
|
223
|
+
| ((options: RespondToToolApprovalOptions) => void)
|
|
224
|
+
| undefined;
|
|
214
225
|
};
|
|
215
226
|
|
|
216
227
|
// Part Client - minimal implementation
|
|
217
|
-
const usePartResource = ({
|
|
228
|
+
const usePartResource = ({
|
|
229
|
+
part,
|
|
230
|
+
onRespondToToolApproval,
|
|
231
|
+
}: PartResourceProps): ClientOutput<"part"> => {
|
|
218
232
|
const state = useMemo(
|
|
219
233
|
() => ({
|
|
220
234
|
...part,
|
|
@@ -227,7 +241,26 @@ const usePartResource = ({ part }: PartResourceProps): ClientOutput<"part"> => {
|
|
|
227
241
|
getState: () => state,
|
|
228
242
|
addToolResult: () => {},
|
|
229
243
|
resumeToolCall: () => {},
|
|
230
|
-
respondToToolApproval: () => {
|
|
244
|
+
respondToToolApproval: (response) => {
|
|
245
|
+
if (!onRespondToToolApproval)
|
|
246
|
+
throw new Error("Runtime does not support tool approvals.");
|
|
247
|
+
|
|
248
|
+
if (part.type !== "tool-call")
|
|
249
|
+
throw new Error(
|
|
250
|
+
"Tried to respond to tool approval on non-tool message part",
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
if (
|
|
254
|
+
!part.approval ||
|
|
255
|
+
part.approval.approved !== undefined ||
|
|
256
|
+
part.approval.resolution !== undefined
|
|
257
|
+
)
|
|
258
|
+
throw new Error("Tool call has no pending approval");
|
|
259
|
+
|
|
260
|
+
onRespondToToolApproval(
|
|
261
|
+
resolveToolApprovalResponse(part.approval, response),
|
|
262
|
+
);
|
|
263
|
+
},
|
|
231
264
|
};
|
|
232
265
|
};
|
|
233
266
|
|
|
@@ -325,35 +358,31 @@ const useComposerClientResource = ({
|
|
|
325
358
|
}, [isEditing]);
|
|
326
359
|
|
|
327
360
|
const attachmentClients = useClientLookup(
|
|
328
|
-
() =>
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}),
|
|
338
|
-
),
|
|
361
|
+
attachments.map((attachment, idx) =>
|
|
362
|
+
withKey(
|
|
363
|
+
attachment.id,
|
|
364
|
+
AttachmentResource({
|
|
365
|
+
attachment,
|
|
366
|
+
onRemove: () => {
|
|
367
|
+
setAttachments(attachments.filter((_, i) => i !== idx));
|
|
368
|
+
},
|
|
369
|
+
}),
|
|
339
370
|
),
|
|
340
|
-
|
|
371
|
+
),
|
|
341
372
|
);
|
|
342
373
|
|
|
343
374
|
const queueItems = queue?.items ?? EMPTY_QUEUE_ITEMS;
|
|
344
375
|
const queueItemClients = useClientLookup(
|
|
345
|
-
() =>
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
}),
|
|
354
|
-
),
|
|
376
|
+
queueItems.map((item) =>
|
|
377
|
+
withKey(
|
|
378
|
+
item.id,
|
|
379
|
+
QueueItemClient({
|
|
380
|
+
item,
|
|
381
|
+
onSteer: () => queue?.steer(item.id),
|
|
382
|
+
onRemove: () => queue?.remove(item.id),
|
|
383
|
+
}),
|
|
355
384
|
),
|
|
356
|
-
|
|
385
|
+
),
|
|
357
386
|
);
|
|
358
387
|
|
|
359
388
|
const state = useMemo(() => {
|
|
@@ -485,6 +514,7 @@ const useExternalThread = ({
|
|
|
485
514
|
onCancel,
|
|
486
515
|
queue,
|
|
487
516
|
branches,
|
|
517
|
+
onRespondToToolApproval,
|
|
488
518
|
}: ExternalThreadProps): ClientOutput<"thread"> => {
|
|
489
519
|
const handleReload = (messageId: string) => {
|
|
490
520
|
const messageIndex = messages.findIndex((m) => m.id === messageId);
|
|
@@ -496,19 +526,18 @@ const useExternalThread = ({
|
|
|
496
526
|
};
|
|
497
527
|
|
|
498
528
|
const messageClients = useClientLookup(
|
|
499
|
-
() =>
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
[messages, onEdit, queue, branches],
|
|
529
|
+
messages.map((msg, index) => {
|
|
530
|
+
const props: MessageClientProps = {
|
|
531
|
+
message: msg,
|
|
532
|
+
index,
|
|
533
|
+
onReload: () => handleReload(msg.id),
|
|
534
|
+
queue,
|
|
535
|
+
branches,
|
|
536
|
+
onRespondToToolApproval,
|
|
537
|
+
};
|
|
538
|
+
if (onEdit) props.onEdit = onEdit;
|
|
539
|
+
return withKey(msg.id, MessageClient(props));
|
|
540
|
+
}),
|
|
512
541
|
);
|
|
513
542
|
|
|
514
543
|
const handleCancelRun = () => {
|
|
@@ -129,21 +129,19 @@ const useInMemoryThreadList = (
|
|
|
129
129
|
};
|
|
130
130
|
|
|
131
131
|
const threadListItems = useClientLookup(
|
|
132
|
-
() =>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}),
|
|
144
|
-
),
|
|
132
|
+
threads.map((t) =>
|
|
133
|
+
withKey(
|
|
134
|
+
t.id,
|
|
135
|
+
ThreadListItemClient({
|
|
136
|
+
data: t,
|
|
137
|
+
onSwitchTo: () => handleSwitchToThread(t.id),
|
|
138
|
+
onUpdateCustom: (custom) => handleUpdateCustom(t.id, custom),
|
|
139
|
+
onArchive: () => handleArchive(t.id),
|
|
140
|
+
onUnarchive: () => handleUnarchive(t.id),
|
|
141
|
+
onDelete: () => handleDelete(t.id),
|
|
142
|
+
}),
|
|
145
143
|
),
|
|
146
|
-
|
|
144
|
+
),
|
|
147
145
|
);
|
|
148
146
|
|
|
149
147
|
// Create the main thread
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Hook that returns the elapsed wall-clock time of the current tool call in
|
|
8
|
+
* milliseconds, ticking once per second while the call runs.
|
|
9
|
+
*
|
|
10
|
+
* Reads `part.timing`. Returns `undefined` when the part is not a tool call,
|
|
11
|
+
* carries no timing, ended without a recorded completion (the duration is
|
|
12
|
+
* unknown), or when no message part scope is available (so kit components
|
|
13
|
+
* stay renderable standalone, e.g. in docs previews).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* function ToolDuration() {
|
|
18
|
+
* const elapsedMs = useToolCallElapsed();
|
|
19
|
+
* if (elapsedMs === undefined) return null;
|
|
20
|
+
* return <span>{(elapsedMs / 1000).toFixed(1)}s</span>;
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const useToolCallElapsed = (): number | undefined => {
|
|
25
|
+
const aui = useAui();
|
|
26
|
+
const hasPart = aui.part.source !== null;
|
|
27
|
+
const timing = useAuiState((s) =>
|
|
28
|
+
hasPart && s.part.type === "tool-call" ? s.part.timing : undefined,
|
|
29
|
+
);
|
|
30
|
+
const partRunning = useAuiState(
|
|
31
|
+
(s) =>
|
|
32
|
+
hasPart &&
|
|
33
|
+
s.part.type === "tool-call" &&
|
|
34
|
+
s.part.status.type === "running",
|
|
35
|
+
);
|
|
36
|
+
const running =
|
|
37
|
+
timing !== undefined && timing.completedAt === undefined && partRunning;
|
|
38
|
+
const [now, setNow] = useState(() => Date.now());
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!running) return undefined;
|
|
42
|
+
setNow(Date.now());
|
|
43
|
+
const id = setInterval(() => setNow(Date.now()), 1000);
|
|
44
|
+
return () => clearInterval(id);
|
|
45
|
+
}, [running]);
|
|
46
|
+
|
|
47
|
+
if (timing === undefined) return undefined;
|
|
48
|
+
if (timing.completedAt !== undefined)
|
|
49
|
+
return Math.max(0, timing.completedAt - timing.startedAt);
|
|
50
|
+
if (!running) return undefined;
|
|
51
|
+
return Math.max(0, now - timing.startedAt);
|
|
52
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -289,6 +289,12 @@ export { useThreadViewportAutoScroll } from "./primitives/thread/useThreadViewpo
|
|
|
289
289
|
export { useScrollLock } from "./primitives/reasoning/useScrollLock";
|
|
290
290
|
export { useMessageQuote } from "./hooks/useMessageQuote";
|
|
291
291
|
export { useMessageTiming } from "./hooks/useMessageTiming";
|
|
292
|
+
export { useToolCallElapsed } from "./hooks/useToolCallElapsed";
|
|
293
|
+
export {
|
|
294
|
+
unstable_useMessageStallDetection,
|
|
295
|
+
type Unstable_MessageStallDetection,
|
|
296
|
+
type Unstable_MessageStallDetectionOptions,
|
|
297
|
+
} from "./unstable/useMessageStallDetection";
|
|
292
298
|
export { useSmooth, type SmoothOptions } from "./utils/smooth/useSmooth";
|
|
293
299
|
|
|
294
300
|
// Re-export core types from @assistant-ui/core
|
|
@@ -309,7 +315,12 @@ export type {
|
|
|
309
315
|
GenerativeUINode,
|
|
310
316
|
GenerativeUISpec,
|
|
311
317
|
Unstable_AudioMessagePart,
|
|
318
|
+
RespondToToolApprovalOptions,
|
|
319
|
+
ToolApprovalOption,
|
|
320
|
+
ToolApprovalOptionKind,
|
|
321
|
+
ToolApprovalResponse,
|
|
312
322
|
ToolCallMessagePart,
|
|
323
|
+
ToolCallTiming,
|
|
313
324
|
ToolModelContentPart,
|
|
314
325
|
MessageStatus,
|
|
315
326
|
MessagePartStatus,
|
|
@@ -29,12 +29,16 @@ const createResponse = (
|
|
|
29
29
|
chunks: readonly string[],
|
|
30
30
|
replayContentLength?: number | string,
|
|
31
31
|
) =>
|
|
32
|
-
new Response(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
new Response(
|
|
33
|
+
createBody(chunks),
|
|
34
|
+
replayContentLength === undefined
|
|
35
|
+
? undefined
|
|
36
|
+
: {
|
|
37
|
+
headers: {
|
|
38
|
+
[REPLAY_CONTENT_LENGTH_HEADER]: String(replayContentLength),
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
);
|
|
38
42
|
|
|
39
43
|
const createRenderWait = () => {
|
|
40
44
|
const pending: Array<() => void> = [];
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
useCallback,
|
|
10
10
|
useContext,
|
|
11
11
|
useEffect,
|
|
12
|
+
useEffectEvent,
|
|
12
13
|
useId,
|
|
13
14
|
useMemo,
|
|
14
15
|
useRef,
|
|
@@ -161,23 +162,21 @@ export const ComposerPrimitiveTriggerPopover = forwardRef<
|
|
|
161
162
|
}),
|
|
162
163
|
);
|
|
163
164
|
|
|
164
|
-
|
|
165
|
-
const resourceRef = useRef(resource);
|
|
166
|
-
resourceRef.current = resource;
|
|
165
|
+
const getResource = useEffectEvent(() => resource);
|
|
167
166
|
|
|
168
167
|
const root = useTriggerPopoverRootContext();
|
|
169
168
|
useEffect(() => {
|
|
170
169
|
return root.register({
|
|
171
170
|
char,
|
|
172
171
|
...(behavior ? { behavior } : {}),
|
|
173
|
-
resource:
|
|
172
|
+
resource: getResource(),
|
|
174
173
|
});
|
|
175
174
|
}, [root, char, behavior]);
|
|
176
175
|
|
|
177
176
|
const pluginRegistry = useComposerInputPluginRegistryOptional();
|
|
178
177
|
useEffect(() => {
|
|
179
178
|
if (!pluginRegistry) return undefined;
|
|
180
|
-
return pluginRegistry.register(
|
|
179
|
+
return pluginRegistry.register(getResource());
|
|
181
180
|
}, [pluginRegistry]);
|
|
182
181
|
|
|
183
182
|
const open = behavior !== null && resource.open;
|