@assistant-ui/react 0.14.27 → 0.14.29
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 +11 -1
- package/dist/client/ExternalThread.d.ts.map +1 -1
- package/dist/client/ExternalThread.js +758 -513
- package/dist/client/ExternalThread.js.map +1 -1
- package/dist/context/react/utils/createContextStoreHook.d.ts.map +1 -1
- package/dist/context/react/utils/createStateHookForRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
- package/dist/legacy-runtime/cloud/auiV0.d.ts +12 -1
- package/dist/legacy-runtime/cloud/auiV0.d.ts.map +1 -1
- package/dist/legacy-runtime/cloud/auiV0.js +2 -1
- package/dist/legacy-runtime/cloud/auiV0.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +7 -6
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +30 -15
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +8 -4
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +1 -0
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/model-context/makeAssistantVisible.d.ts.map +1 -1
- package/dist/model-context/makeAssistantVisible.js +7 -1
- package/dist/model-context/makeAssistantVisible.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarCopy.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarEdit.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarReload.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarRoot.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarRoot.js +3 -3
- package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarSpeak.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts.map +1 -1
- package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts +7 -6
- package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts.map +1 -1
- package/dist/primitives/actionBar/useActionBarFloatStatus.js +10 -11
- package/dist/primitives/actionBar/useActionBarFloatStatus.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreContent.d.ts.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreItem.d.ts.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreSeparator.d.ts.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreTrigger.d.ts.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalAnchor.d.ts.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalContent.d.ts.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalTrigger.d.ts.map +1 -1
- package/dist/primitives/assistantModal/popoverRenderPrimitives.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentRemove.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentRemove.js +1 -0
- package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
- package/dist/primitives/attachment/AttachmentRoot.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentThumb.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNext.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerPrevious.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerRoot.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.d.ts.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +1 -0
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtRoot.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js +8 -2
- package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -3
- package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
- package/dist/primitives/composer/ComposerCancel.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerDictate.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerDictationTranscript.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +6 -4
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerQuote.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerQuote.js.map +1 -1
- package/dist/primitives/composer/ComposerRoot.d.ts +5 -0
- package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerRoot.js +86 -40
- package/dist/primitives/composer/ComposerRoot.js.map +1 -1
- package/dist/primitives/composer/ComposerSend.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerSend.js +1 -0
- package/dist/primitives/composer/ComposerSend.js.map +1 -1
- package/dist/primitives/composer/ComposerStopDictation.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerStopDictation.js +1 -0
- 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.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverBack.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
- package/dist/primitives/composer/trigger/detectTrigger.d.ts +1 -1
- package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
- package/dist/primitives/dropdownMenuRenderPrimitives.d.ts.map +1 -1
- package/dist/primitives/error/ErrorMessage.d.ts.map +1 -1
- package/dist/primitives/error/ErrorMessage.js.map +1 -1
- package/dist/primitives/error/ErrorRoot.d.ts.map +1 -1
- package/dist/primitives/message/MessageParts.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/primitives/message/MessageRoot.d.ts.map +1 -1
- package/dist/primitives/messagePart/MessagePartImage.d.ts.map +1 -1
- package/dist/primitives/messagePart/MessagePartText.d.ts.map +1 -1
- package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartData.d.ts +3 -2
- package/dist/primitives/messagePart/useMessagePartData.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartData.js +3 -2
- package/dist/primitives/messagePart/useMessagePartData.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartFile.d.ts +5 -4
- package/dist/primitives/messagePart/useMessagePartFile.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartFile.js +11 -3
- package/dist/primitives/messagePart/useMessagePartFile.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartImage.d.ts +5 -4
- package/dist/primitives/messagePart/useMessagePartImage.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartImage.js +10 -3
- package/dist/primitives/messagePart/useMessagePartImage.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartReasoning.d.ts +5 -4
- package/dist/primitives/messagePart/useMessagePartReasoning.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartReasoning.js +10 -3
- package/dist/primitives/messagePart/useMessagePartReasoning.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartSource.d.ts +6 -4
- package/dist/primitives/messagePart/useMessagePartSource.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartSource.js +12 -3
- package/dist/primitives/messagePart/useMessagePartSource.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartText.d.ts +6 -5
- package/dist/primitives/messagePart/useMessagePartText.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartText.js +10 -3
- package/dist/primitives/messagePart/useMessagePartText.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.d.ts.map +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.js +1 -0
- package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.d.ts.map +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.js +1 -0
- package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemText.d.ts.map +1 -1
- package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.d.ts.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionDescription.d.ts.map +1 -1
- package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTitle.d.ts.map +1 -1
- package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.d.ts.map +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.js +1 -0
- package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
- package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadScrollToBottom.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadScrollToBottom.js +1 -0
- package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
- package/dist/primitives/thread/ThreadSuggestion.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadViewport.js +53 -19
- package/dist/primitives/thread/ThreadViewport.js.map +1 -1
- package/dist/primitives/thread/ThreadViewportFooter.d.ts.map +1 -1
- package/dist/primitives/thread/topAnchor/createReserveObservers.d.ts.map +1 -1
- package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.d.ts +4 -0
- package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.d.ts.map +1 -1
- package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js +14 -1
- package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js.map +1 -1
- package/dist/primitives/thread/topAnchor/topAnchorTurn.d.ts +13 -1
- package/dist/primitives/thread/topAnchor/topAnchorTurn.d.ts.map +1 -1
- package/dist/primitives/thread/topAnchor/topAnchorTurn.js +14 -1
- package/dist/primitives/thread/topAnchor/topAnchorTurn.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +2 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/primitives/threadList/ThreadListLoadMore.d.ts.map +1 -1
- package/dist/primitives/threadList/ThreadListNew.d.ts.map +1 -1
- package/dist/primitives/threadList/ThreadListRoot.d.ts.map +1 -1
- package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
- package/dist/primitives/threadListFocusGroup.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemArchive.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemDelete.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemUnarchive.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.d.ts.map +1 -1
- package/dist/tests/setup.d.ts +1 -1
- package/dist/utils/Primitive.d.ts.map +1 -1
- package/dist/utils/Primitive.js.map +1 -1
- package/dist/utils/createActionButton.d.ts.map +1 -1
- package/dist/utils/hooks/useOnResizeContent.js +1 -0
- package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
- package/dist/utils/hooks/useSizeHandle.js +1 -0
- package/dist/utils/hooks/useSizeHandle.js.map +1 -1
- package/dist/utils/smooth/useSmooth.d.ts.map +1 -1
- package/dist/utils/smooth/useSmooth.js +2 -2
- package/dist/utils/smooth/useSmooth.js.map +1 -1
- package/package.json +20 -20
- package/src/client/ExternalThread.ts +341 -53
- package/src/legacy-runtime/cloud/auiV0.ts +14 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/commandQueue.ts +4 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/runManager.ts +37 -10
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-delivery.test.tsx +79 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-parentid.test.tsx +97 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-scheduling.test.ts +113 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransport.spec.md +10 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +189 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +20 -4
- package/src/model-context/makeAssistantVisible.test.tsx +146 -0
- package/src/model-context/makeAssistantVisible.tsx +18 -1
- package/src/primitives/actionBar/useActionBarFloatStatus.ts +7 -5
- package/src/primitives/composer/ComposerAddAttachment.test.tsx +108 -0
- package/src/primitives/composer/ComposerAddAttachment.ts +10 -4
- package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +3 -2
- package/src/primitives/composer/ComposerAttachmentDropzone.tsx +3 -2
- package/src/primitives/composer/ComposerInput.test.tsx +98 -1
- package/src/primitives/composer/ComposerInput.tsx +10 -8
- package/src/primitives/composer/ComposerRoot.test.tsx +226 -5
- package/src/primitives/composer/ComposerRoot.tsx +50 -1
- package/src/primitives/messagePart/useMessagePartData.ts +3 -2
- package/src/primitives/messagePart/useMessagePartFile.ts +19 -7
- package/src/primitives/messagePart/useMessagePartImage.ts +18 -7
- package/src/primitives/messagePart/useMessagePartReasoning.ts +15 -6
- package/src/primitives/messagePart/useMessagePartSource.ts +20 -7
- package/src/primitives/messagePart/useMessagePartText.ts +15 -5
- package/src/primitives/thread/ThreadViewport.tsx +15 -0
- package/src/primitives/thread/topAnchor/mountTopAnchorReserve.test.ts +243 -0
- package/src/primitives/thread/topAnchor/mountTopAnchorReserve.ts +28 -7
- package/src/primitives/thread/topAnchor/topAnchorTurn.test.ts +69 -0
- package/src/primitives/thread/topAnchor/topAnchorTurn.ts +29 -0
- package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +86 -1
- package/src/primitives/thread/useThreadViewportAutoScroll.ts +6 -1
- package/src/tests/DataRenderers.test.tsx +212 -0
- package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +1 -11
- package/src/tests/external-thread-attachments.test.tsx +372 -0
- package/src/tests/external-thread-parity.test.tsx +232 -0
- package/src/tests/generative-ui.test.tsx +1 -46
- package/src/tests/messagePartSwitchRace.test.tsx +172 -0
- package/src/tests/messagePartTypeMismatch.test.tsx +146 -0
- package/src/tests/threadMessageById.test.tsx +0 -7
- package/src/utils/smooth/useSmooth.ts +7 -4
- package/src/tests/BaseComposerRuntimeCore.test.ts +0 -612
- package/src/tests/BaseSubscribable.test.ts +0 -54
- package/src/tests/DataRenderers.test.ts +0 -355
- package/src/tests/ExternalStoreThreadRuntimeCore.test.ts +0 -685
- package/src/tests/LocalThreadRuntimeCore.test.ts +0 -324
- package/src/tests/auiV0Encode.test.ts +0 -188
- package/src/tests/external-message-converter.test.ts +0 -346
- package/src/tests/shouldContinue.test.ts +0 -122
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
SourceProviderMetadata,
|
|
8
8
|
ThreadMessage,
|
|
9
9
|
TextMessagePart,
|
|
10
|
+
ToolApprovalOption,
|
|
10
11
|
Unstable_AudioMessagePart,
|
|
11
12
|
} from "@assistant-ui/core";
|
|
12
13
|
import { fromThreadMessageLike } from "../runtime-cores/external-store/ThreadMessageLike";
|
|
@@ -18,6 +19,16 @@ import type {
|
|
|
18
19
|
} from "assistant-stream/utils";
|
|
19
20
|
import type { ExportedMessageRepositoryItem } from "../runtime-cores/utils/MessageRepository";
|
|
20
21
|
|
|
22
|
+
type AuiV0ToolApproval = {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly approved?: boolean;
|
|
25
|
+
readonly reason?: string;
|
|
26
|
+
readonly isAutomatic?: boolean;
|
|
27
|
+
readonly options?: readonly ToolApprovalOption[];
|
|
28
|
+
readonly optionId?: string;
|
|
29
|
+
readonly resolution?: "cancelled" | "expired";
|
|
30
|
+
};
|
|
31
|
+
|
|
21
32
|
type AuiV0MessagePart =
|
|
22
33
|
| {
|
|
23
34
|
readonly type: "text";
|
|
@@ -51,6 +62,7 @@ type AuiV0MessagePart =
|
|
|
51
62
|
readonly args: ReadonlyJSONObject;
|
|
52
63
|
readonly result?: ReadonlyJSONValue;
|
|
53
64
|
readonly isError?: true;
|
|
65
|
+
readonly approval?: AuiV0ToolApproval;
|
|
54
66
|
}
|
|
55
67
|
| {
|
|
56
68
|
readonly type: "tool-call";
|
|
@@ -59,6 +71,7 @@ type AuiV0MessagePart =
|
|
|
59
71
|
readonly argsText: string;
|
|
60
72
|
readonly result?: ReadonlyJSONValue;
|
|
61
73
|
readonly isError?: true;
|
|
74
|
+
readonly approval?: AuiV0ToolApproval;
|
|
62
75
|
}
|
|
63
76
|
| {
|
|
64
77
|
readonly type: "image";
|
|
@@ -216,6 +229,7 @@ export function auiV0Encode(message: ThreadMessage): AuiV0Message {
|
|
|
216
229
|
? { result: part.result as ReadonlyJSONValue }
|
|
217
230
|
: undefined),
|
|
218
231
|
...(part.isError ? { isError: true } : undefined),
|
|
232
|
+
...(part.approval ? { approval: part.approval } : undefined),
|
|
219
233
|
};
|
|
220
234
|
}
|
|
221
235
|
|
|
@@ -56,7 +56,10 @@ export const useCommandQueue = (opts: { onQueue: () => void }) => {
|
|
|
56
56
|
}, []);
|
|
57
57
|
|
|
58
58
|
return {
|
|
59
|
-
|
|
59
|
+
// live getter — callbacks firing between renders must not see a stale snapshot
|
|
60
|
+
get state() {
|
|
61
|
+
return queueStateRef.current;
|
|
62
|
+
},
|
|
60
63
|
enqueue,
|
|
61
64
|
flush,
|
|
62
65
|
markDelivered,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useRef, useState } from "react";
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { useLatestRef } from "./useLatestRef";
|
|
3
3
|
|
|
4
4
|
export type RunManager = Readonly<{
|
|
@@ -7,6 +7,8 @@ export type RunManager = Readonly<{
|
|
|
7
7
|
cancel: () => void;
|
|
8
8
|
}>;
|
|
9
9
|
|
|
10
|
+
const disposeReason = Symbol("assistant-transport-dispose");
|
|
11
|
+
|
|
10
12
|
export function useRunManager(config: {
|
|
11
13
|
onRun: (signal: AbortSignal) => Promise<void>;
|
|
12
14
|
onFinish?: (() => void) | undefined;
|
|
@@ -16,6 +18,7 @@ export function useRunManager(config: {
|
|
|
16
18
|
const [isRunning, setIsRunning] = useState(false);
|
|
17
19
|
const stateRef = useRef({
|
|
18
20
|
pending: false,
|
|
21
|
+
disposed: false,
|
|
19
22
|
abortController: null as AbortController | null,
|
|
20
23
|
});
|
|
21
24
|
const onRunRef = useLatestRef(config.onRun);
|
|
@@ -29,29 +32,42 @@ export function useRunManager(config: {
|
|
|
29
32
|
const ac = new AbortController();
|
|
30
33
|
stateRef.current.abortController = ac;
|
|
31
34
|
|
|
35
|
+
// The dispose marker rides on the abort reason so a settling run detects
|
|
36
|
+
// its own disposal even after an effect cycle re-arms the manager.
|
|
37
|
+
const disposeAborted = () => ac.signal.reason === disposeReason;
|
|
38
|
+
|
|
32
39
|
queueMicrotask(async () => {
|
|
33
40
|
try {
|
|
34
|
-
|
|
41
|
+
if (!disposeAborted()) {
|
|
42
|
+
await onRunRef.current(ac.signal);
|
|
43
|
+
}
|
|
35
44
|
} catch (error) {
|
|
36
|
-
stateRef.current.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
if (!disposeAborted() && !stateRef.current.disposed) {
|
|
46
|
+
stateRef.current.pending = false;
|
|
47
|
+
if (ac.signal.aborted) {
|
|
48
|
+
onCancelRef.current?.();
|
|
49
|
+
} else {
|
|
50
|
+
await onErrorRef.current?.(error as Error);
|
|
51
|
+
}
|
|
41
52
|
}
|
|
42
53
|
} finally {
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
if (!disposeAborted() && !stateRef.current.disposed) {
|
|
55
|
+
onFinishRef.current?.();
|
|
56
|
+
}
|
|
57
|
+
if (!stateRef.current.disposed && stateRef.current.pending) {
|
|
45
58
|
startRun();
|
|
46
59
|
} else {
|
|
47
60
|
setIsRunning(false);
|
|
48
|
-
stateRef.current.abortController
|
|
61
|
+
if (stateRef.current.abortController === ac) {
|
|
62
|
+
stateRef.current.abortController = null;
|
|
63
|
+
}
|
|
49
64
|
}
|
|
50
65
|
}
|
|
51
66
|
});
|
|
52
67
|
}, [onRunRef, onFinishRef, onErrorRef, onCancelRef]);
|
|
53
68
|
|
|
54
69
|
const schedule = useCallback(() => {
|
|
70
|
+
if (stateRef.current.disposed) return;
|
|
55
71
|
if (stateRef.current.abortController) {
|
|
56
72
|
// Coalesce multiple schedules while running into a single follow-up run.
|
|
57
73
|
stateRef.current.pending = true;
|
|
@@ -60,6 +76,17 @@ export function useRunManager(config: {
|
|
|
60
76
|
startRun();
|
|
61
77
|
}, [startRun]);
|
|
62
78
|
|
|
79
|
+
// Strict-mode effects run setup / cleanup / setup on the same instance;
|
|
80
|
+
// arming on setup undoes the cleanup's dispose.
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
stateRef.current.disposed = false;
|
|
83
|
+
return () => {
|
|
84
|
+
stateRef.current.disposed = true;
|
|
85
|
+
stateRef.current.pending = false;
|
|
86
|
+
stateRef.current.abortController?.abort(disposeReason);
|
|
87
|
+
};
|
|
88
|
+
}, []);
|
|
89
|
+
|
|
63
90
|
const cancel = useCallback(() => {
|
|
64
91
|
stateRef.current.pending = false;
|
|
65
92
|
stateRef.current.abortController?.abort();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { act, render, waitFor } from "@testing-library/react";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import type { FC } from "react";
|
|
6
|
+
import { useAssistantTransportRuntime } from "./useAssistantTransportRuntime";
|
|
7
|
+
import type { AssistantRuntime } from "../../runtime/AssistantRuntime";
|
|
8
|
+
import { AssistantRuntimeProvider } from "../../../context";
|
|
9
|
+
import type {
|
|
10
|
+
AssistantTransportCommand,
|
|
11
|
+
AssistantTransportStateConverter,
|
|
12
|
+
} from "./types";
|
|
13
|
+
|
|
14
|
+
const emptySuccessfulResponse = () =>
|
|
15
|
+
new Response(
|
|
16
|
+
new ReadableStream<Uint8Array>({
|
|
17
|
+
start(controller) {
|
|
18
|
+
controller.close();
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
21
|
+
{ status: 200 },
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const setupRuntime = () => {
|
|
25
|
+
const fetchMock = vi.fn(async () => emptySuccessfulResponse());
|
|
26
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
27
|
+
|
|
28
|
+
const pendingRef: { current: AssistantTransportCommand[] } = { current: [] };
|
|
29
|
+
const converter: AssistantTransportStateConverter<Record<string, never>> = (
|
|
30
|
+
_state,
|
|
31
|
+
{ pendingCommands, isSending },
|
|
32
|
+
) => {
|
|
33
|
+
pendingRef.current = pendingCommands;
|
|
34
|
+
return { messages: [], isRunning: isSending };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const runtimeRef: { current: AssistantRuntime | null } = { current: null };
|
|
38
|
+
const App: FC = () => {
|
|
39
|
+
const runtime = useAssistantTransportRuntime({
|
|
40
|
+
initialState: {},
|
|
41
|
+
api: "http://localhost/api",
|
|
42
|
+
converter,
|
|
43
|
+
headers: {},
|
|
44
|
+
});
|
|
45
|
+
runtimeRef.current = runtime;
|
|
46
|
+
return (
|
|
47
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
48
|
+
{null}
|
|
49
|
+
</AssistantRuntimeProvider>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return { App, fetchMock, pendingRef, runtimeRef };
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
describe("assistant transport delivery contracts", () => {
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
vi.unstubAllGlobals();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("clears in-transit commands when a run succeeds without state chunks", async () => {
|
|
62
|
+
const { App, fetchMock, pendingRef, runtimeRef } = setupRuntime();
|
|
63
|
+
|
|
64
|
+
await act(async () => {
|
|
65
|
+
render(<App />);
|
|
66
|
+
});
|
|
67
|
+
await waitFor(() => expect(runtimeRef.current).not.toBeNull());
|
|
68
|
+
|
|
69
|
+
await act(async () => {
|
|
70
|
+
runtimeRef.current!.thread.append("m1");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1));
|
|
74
|
+
await waitFor(() =>
|
|
75
|
+
expect(runtimeRef.current!.thread.getState().isRunning).toBe(false),
|
|
76
|
+
);
|
|
77
|
+
await waitFor(() => expect(pendingRef.current).toHaveLength(0));
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { act, render, waitFor } from "@testing-library/react";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import type { FC } from "react";
|
|
6
|
+
import { useAssistantTransportRuntime } from "./useAssistantTransportRuntime";
|
|
7
|
+
import type { AssistantRuntime } from "../../runtime/AssistantRuntime";
|
|
8
|
+
import { AssistantRuntimeProvider } from "../../../context";
|
|
9
|
+
import type {
|
|
10
|
+
AssistantTransportCommand,
|
|
11
|
+
AssistantTransportStateConverter,
|
|
12
|
+
} from "./types";
|
|
13
|
+
|
|
14
|
+
const emptySuccessfulResponse = () =>
|
|
15
|
+
new Response(
|
|
16
|
+
new ReadableStream<Uint8Array>({
|
|
17
|
+
start(controller) {
|
|
18
|
+
controller.close();
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
21
|
+
{ status: 200 },
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const setupRuntime = () => {
|
|
25
|
+
const requestBodies: Record<string, unknown>[] = [];
|
|
26
|
+
const fetchMock = vi.fn(
|
|
27
|
+
async (_url: RequestInfo | URL, init?: RequestInit) => {
|
|
28
|
+
requestBodies.push(JSON.parse(init!.body as string));
|
|
29
|
+
return emptySuccessfulResponse();
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
33
|
+
|
|
34
|
+
const converter: AssistantTransportStateConverter<Record<string, never>> = (
|
|
35
|
+
_state,
|
|
36
|
+
{ isSending },
|
|
37
|
+
) => ({ messages: [], isRunning: isSending });
|
|
38
|
+
|
|
39
|
+
const runtimeRef: { current: AssistantRuntime | null } = { current: null };
|
|
40
|
+
const App: FC = () => {
|
|
41
|
+
const runtime = useAssistantTransportRuntime({
|
|
42
|
+
initialState: {},
|
|
43
|
+
api: "http://localhost/api",
|
|
44
|
+
converter,
|
|
45
|
+
headers: {},
|
|
46
|
+
});
|
|
47
|
+
runtimeRef.current = runtime;
|
|
48
|
+
return (
|
|
49
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
50
|
+
{null}
|
|
51
|
+
</AssistantRuntimeProvider>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return { App, fetchMock, requestBodies, runtimeRef };
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
describe("assistant transport parentId lifetime", () => {
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
vi.unstubAllGlobals();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("consumes parentId per run: the append's run carries it, a later sendCommand run omits it", async () => {
|
|
64
|
+
const { App, fetchMock, requestBodies, runtimeRef } = setupRuntime();
|
|
65
|
+
|
|
66
|
+
await act(async () => {
|
|
67
|
+
render(<App />);
|
|
68
|
+
});
|
|
69
|
+
await waitFor(() => expect(runtimeRef.current).not.toBeNull());
|
|
70
|
+
|
|
71
|
+
await act(async () => {
|
|
72
|
+
runtimeRef.current!.thread.append("m1");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1));
|
|
76
|
+
await waitFor(() =>
|
|
77
|
+
expect(runtimeRef.current!.thread.getState().isRunning).toBe(false),
|
|
78
|
+
);
|
|
79
|
+
expect(Object.hasOwn(requestBodies[0]!, "parentId")).toBe(true);
|
|
80
|
+
|
|
81
|
+
const extras = runtimeRef.current!.thread.getState().extras as {
|
|
82
|
+
sendCommand: (command: AssistantTransportCommand) => void;
|
|
83
|
+
};
|
|
84
|
+
await act(async () => {
|
|
85
|
+
extras.sendCommand({
|
|
86
|
+
type: "add-tool-result",
|
|
87
|
+
toolCallId: "t1",
|
|
88
|
+
toolName: "tool",
|
|
89
|
+
result: {},
|
|
90
|
+
isError: false,
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(2));
|
|
95
|
+
expect(Object.hasOwn(requestBodies[1]!, "parentId")).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
4
4
|
import { describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { useRef } from "react";
|
|
5
|
+
import { createElement, StrictMode, useRef } from "react";
|
|
6
6
|
import { useCommandQueue } from "./commandQueue";
|
|
7
7
|
import { useRunManager } from "./runManager";
|
|
8
8
|
import type { AssistantTransportCommand } from "./types";
|
|
@@ -29,6 +29,8 @@ const useTransportSchedulingHarness = (
|
|
|
29
29
|
opts: {
|
|
30
30
|
onRun?: (signal: AbortSignal) => Promise<void> | void;
|
|
31
31
|
onCancel?: (commands: AssistantTransportCommand[]) => void;
|
|
32
|
+
onError?: (commands: AssistantTransportCommand[]) => void;
|
|
33
|
+
onFinish?: () => void;
|
|
32
34
|
} = {},
|
|
33
35
|
) => {
|
|
34
36
|
const commandQueueRef = useRef<ReturnType<typeof useCommandQueue> | null>(
|
|
@@ -49,8 +51,10 @@ const useTransportSchedulingHarness = (
|
|
|
49
51
|
opts.onCancel?.(commands);
|
|
50
52
|
},
|
|
51
53
|
onError: async () => {
|
|
52
|
-
|
|
54
|
+
const queue = commandQueueRef.current!;
|
|
55
|
+
opts.onError?.([...queue.state.inTransit]);
|
|
53
56
|
},
|
|
57
|
+
onFinish: () => opts.onFinish?.(),
|
|
54
58
|
});
|
|
55
59
|
|
|
56
60
|
const commandQueue = useCommandQueue({
|
|
@@ -121,6 +125,27 @@ describe("assistant transport scheduling contracts", () => {
|
|
|
121
125
|
expect(result.current.runBatchesRef.current[0]).toHaveLength(1);
|
|
122
126
|
});
|
|
123
127
|
|
|
128
|
+
it("onError receives the live in-transit commands at error time", async () => {
|
|
129
|
+
const seen: AssistantTransportCommand[][] = [];
|
|
130
|
+
const { result } = renderHook(() =>
|
|
131
|
+
useTransportSchedulingHarness({
|
|
132
|
+
onRun: () => {
|
|
133
|
+
throw new Error("network error");
|
|
134
|
+
},
|
|
135
|
+
onError: (commands) => seen.push(commands),
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
act(() => {
|
|
140
|
+
result.current.commandQueue.enqueue(createMessageCommand("m1"));
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// The flush that moved m1 into transit has not re-rendered yet when the
|
|
144
|
+
// error fires; the queue state must be read live, not from a render snapshot.
|
|
145
|
+
await waitFor(() => expect(seen).toHaveLength(1));
|
|
146
|
+
expect(seen[0]).toEqual([createMessageCommand("m1")]);
|
|
147
|
+
});
|
|
148
|
+
|
|
124
149
|
it("cancel returns combined in-flight and queued commands", async () => {
|
|
125
150
|
const onCancel = vi.fn();
|
|
126
151
|
const { result } = renderHook(() =>
|
|
@@ -155,4 +180,90 @@ describe("assistant transport scheduling contracts", () => {
|
|
|
155
180
|
});
|
|
156
181
|
expect(onCancel.mock.calls[0]?.[0]).toHaveLength(2);
|
|
157
182
|
});
|
|
183
|
+
|
|
184
|
+
it("unmount aborts the in-flight run without invoking callbacks", async () => {
|
|
185
|
+
let aborted = false;
|
|
186
|
+
const onCancel = vi.fn();
|
|
187
|
+
const onError = vi.fn();
|
|
188
|
+
const onFinish = vi.fn();
|
|
189
|
+
const { result, unmount } = renderHook(() =>
|
|
190
|
+
useTransportSchedulingHarness({
|
|
191
|
+
onRun: (signal) =>
|
|
192
|
+
new Promise<void>((_resolve, reject) => {
|
|
193
|
+
signal.addEventListener(
|
|
194
|
+
"abort",
|
|
195
|
+
() => {
|
|
196
|
+
aborted = true;
|
|
197
|
+
reject(new Error("aborted"));
|
|
198
|
+
},
|
|
199
|
+
{ once: true },
|
|
200
|
+
);
|
|
201
|
+
}),
|
|
202
|
+
onCancel,
|
|
203
|
+
onError,
|
|
204
|
+
onFinish,
|
|
205
|
+
}),
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
act(() => {
|
|
209
|
+
result.current.commandQueue.enqueue(createMessageCommand("in-flight"));
|
|
210
|
+
});
|
|
211
|
+
await waitFor(() => {
|
|
212
|
+
expect(result.current.runBatchesRef.current).toHaveLength(1);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
unmount();
|
|
216
|
+
|
|
217
|
+
expect(aborted).toBe(true);
|
|
218
|
+
await act(async () => {});
|
|
219
|
+
expect(onCancel).not.toHaveBeenCalled();
|
|
220
|
+
expect(onError).not.toHaveBeenCalled();
|
|
221
|
+
expect(onFinish).not.toHaveBeenCalled();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("ignores schedules after unmount", async () => {
|
|
225
|
+
const { result, unmount } = renderHook(() =>
|
|
226
|
+
useTransportSchedulingHarness(),
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
unmount();
|
|
230
|
+
act(() => {
|
|
231
|
+
result.current.commandQueue.enqueue(createMessageCommand("late"));
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
await act(async () => {});
|
|
235
|
+
expect(result.current.runBatchesRef.current).toHaveLength(0);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("survives StrictMode double-mounting", async () => {
|
|
239
|
+
const gate = createDeferred();
|
|
240
|
+
const onFinish = vi.fn();
|
|
241
|
+
const { result } = renderHook(
|
|
242
|
+
() =>
|
|
243
|
+
useTransportSchedulingHarness({ onRun: () => gate.promise, onFinish }),
|
|
244
|
+
{ wrapper: ({ children }) => createElement(StrictMode, null, children) },
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
act(() => {
|
|
248
|
+
result.current.commandQueue.enqueue(createMessageCommand("m1"));
|
|
249
|
+
});
|
|
250
|
+
await waitFor(() => {
|
|
251
|
+
expect(result.current.runBatchesRef.current).toHaveLength(1);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// enqueued while the first run is in flight — must restart as a follow-up
|
|
255
|
+
act(() => {
|
|
256
|
+
result.current.commandQueue.enqueue(createMessageCommand("m2"));
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
gate.resolve();
|
|
260
|
+
await waitFor(() => {
|
|
261
|
+
expect(result.current.runBatchesRef.current).toHaveLength(2);
|
|
262
|
+
});
|
|
263
|
+
expect(result.current.runBatchesRef.current[1]).toHaveLength(1);
|
|
264
|
+
expect(onFinish).toHaveBeenCalled();
|
|
265
|
+
await waitFor(() => {
|
|
266
|
+
expect(result.current.runManager.isRunning).toBe(false);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
158
269
|
});
|
|
@@ -14,13 +14,15 @@ Command Scheduling
|
|
|
14
14
|
- If a run is in progress: do not start another; mark that a follow-up run is pending.
|
|
15
15
|
- When the current run ends: if commands were scheduled during the run, start a new run and publish them.
|
|
16
16
|
- If no run is in progress: start a run immediately and flush commands to the server.
|
|
17
|
-
-
|
|
17
|
+
- A follow-up run that finds an empty queue is a no-op: no request is sent and no error is surfaced.
|
|
18
|
+
- A resume run sends no commands; commands enqueued while it is pending or active are flushed in a follow-up run after it settles.
|
|
19
|
+
- Runs execute on a `queueMicrotask`, so multiple synchronous enqueues coalesce into a single request: the first run's flush takes all of them, and the coalesced follow-up run no-ops.
|
|
18
20
|
|
|
19
21
|
Command Queue
|
|
20
22
|
|
|
21
23
|
`useCommandQueue({ onQueue() { runManager.schedule(); } })`
|
|
22
24
|
|
|
23
|
-
- `enqueue(cmd)`: Adds a command to the queue. Calls `onQueue`
|
|
25
|
+
- `enqueue(cmd, { schedule? })`: Adds a command to the queue. Calls `onQueue` unless `schedule: false`.
|
|
24
26
|
- `flush(): Command[]`: Returns all queued commands, moves them into `inTransit`, and clears the queue.
|
|
25
27
|
- Internal state tracks `inTransit: Command[]` and `queued: Command[]`.
|
|
26
28
|
|
|
@@ -49,6 +51,11 @@ setInTransitCommands(commands);
|
|
|
49
51
|
setInTransitCommands(EMPTY_ARRAY);
|
|
50
52
|
setSnapshot(snapshot);
|
|
51
53
|
}
|
|
54
|
+
|
|
55
|
+
// A run that completes successfully confirms delivery even when no
|
|
56
|
+
// state-changing chunk was observed — clear in-transit commands here
|
|
57
|
+
// as well.
|
|
58
|
+
setInTransitCommands(EMPTY_ARRAY);
|
|
52
59
|
} catch (error) {
|
|
53
60
|
// Do not restore commands. Surface error to onError for state update.
|
|
54
61
|
callbacks.onError?.({
|
|
@@ -65,6 +72,7 @@ setInTransitCommands(commands);
|
|
|
65
72
|
|
|
66
73
|
- `schedule()`: Starts immediately if idle, or schedules at most one follow-up run to start right after the current run.
|
|
67
74
|
- `cancel()`: Aborts the active run via `signal` and clears any scheduled follow-up run. Does not restore commands.
|
|
75
|
+
- Unmount aborts the active run via `signal`; no callbacks (`onCancel`, `onError`, `onFinish`) are invoked. StrictMode's setup/cleanup/setup effect cycle re-arms the manager.
|
|
68
76
|
- `isRunning: boolean`: Indicates whether a run is currently active (internal to scheduling).
|
|
69
77
|
UI-facing `isRunning` is controlled by the converter output (see Converter).
|
|
70
78
|
- On cancellation, invoke `callbacks.onCancel?.({ commands, updateState })` where `commands` contains all pending work at the time of cancel: `[...inTransitCommands, ...queuedCommands]`. Note: after the first snapshot arrives, `inTransitCommands` are cleared to `[]`, so cancels after first byte will not include them.
|