@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
|
@@ -1,685 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
ExternalStoreThreadRuntimeCore,
|
|
4
|
-
hasUpcomingMessage,
|
|
5
|
-
} from "../legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore";
|
|
6
|
-
import type { ExternalStoreAdapter } from "../legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter";
|
|
7
|
-
import type { ModelContextProvider } from "@assistant-ui/core";
|
|
8
|
-
import type { AppendMessage, ThreadMessage } from "@assistant-ui/core";
|
|
9
|
-
|
|
10
|
-
const createContextProvider = (): ModelContextProvider => ({
|
|
11
|
-
getModelContext: () => ({}),
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const createUserMessage = (id: string, text = "Hello"): ThreadMessage =>
|
|
15
|
-
({
|
|
16
|
-
id,
|
|
17
|
-
role: "user" as const,
|
|
18
|
-
createdAt: new Date(),
|
|
19
|
-
content: [{ type: "text" as const, text }],
|
|
20
|
-
attachments: [],
|
|
21
|
-
metadata: {
|
|
22
|
-
custom: {},
|
|
23
|
-
},
|
|
24
|
-
}) as ThreadMessage;
|
|
25
|
-
|
|
26
|
-
const createAssistantMessage = (id: string, text = "Hi there"): ThreadMessage =>
|
|
27
|
-
({
|
|
28
|
-
id,
|
|
29
|
-
role: "assistant" as const,
|
|
30
|
-
createdAt: new Date(),
|
|
31
|
-
content: [{ type: "text" as const, text }],
|
|
32
|
-
status: { type: "complete" as const, reason: "stop" as const },
|
|
33
|
-
metadata: {
|
|
34
|
-
unstable_state: null,
|
|
35
|
-
unstable_annotations: [],
|
|
36
|
-
unstable_data: [],
|
|
37
|
-
steps: [],
|
|
38
|
-
custom: {},
|
|
39
|
-
},
|
|
40
|
-
}) as ThreadMessage;
|
|
41
|
-
|
|
42
|
-
const createBaseAdapter = (
|
|
43
|
-
overrides: Partial<ExternalStoreAdapter<ThreadMessage>> = {},
|
|
44
|
-
): ExternalStoreAdapter<ThreadMessage> => ({
|
|
45
|
-
messages: [],
|
|
46
|
-
onNew: vi.fn(async () => {}),
|
|
47
|
-
...overrides,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe("ExternalStoreThreadRuntimeCore", () => {
|
|
51
|
-
let contextProvider: ModelContextProvider;
|
|
52
|
-
|
|
53
|
-
beforeEach(() => {
|
|
54
|
-
contextProvider = createContextProvider();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe("hasUpcomingMessage", () => {
|
|
58
|
-
it("returns true when running and last message is not assistant", () => {
|
|
59
|
-
const messages = [createUserMessage("u1")];
|
|
60
|
-
expect(hasUpcomingMessage(true, messages)).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("returns false when running and last message is assistant", () => {
|
|
64
|
-
const messages = [createAssistantMessage("a1")];
|
|
65
|
-
expect(hasUpcomingMessage(true, messages)).toBe(false);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("returns false when not running", () => {
|
|
69
|
-
const messages = [createUserMessage("u1")];
|
|
70
|
-
expect(hasUpcomingMessage(false, messages)).toBe(false);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("returns true for empty messages when running", () => {
|
|
74
|
-
expect(hasUpcomingMessage(true, [])).toBe(true);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe("capabilities derived from adapter", () => {
|
|
79
|
-
it("enables edit when onEdit is provided", () => {
|
|
80
|
-
const adapter = createBaseAdapter({
|
|
81
|
-
onEdit: vi.fn(async () => {}),
|
|
82
|
-
});
|
|
83
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
84
|
-
expect(core.capabilities.edit).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("disables edit when onEdit is not provided", () => {
|
|
88
|
-
const adapter = createBaseAdapter();
|
|
89
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
90
|
-
expect(core.capabilities.edit).toBe(false);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("enables reload when onReload is provided", () => {
|
|
94
|
-
const adapter = createBaseAdapter({
|
|
95
|
-
onReload: vi.fn(async () => {}),
|
|
96
|
-
});
|
|
97
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
98
|
-
expect(core.capabilities.reload).toBe(true);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("disables reload when onReload is not provided", () => {
|
|
102
|
-
const adapter = createBaseAdapter();
|
|
103
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
104
|
-
expect(core.capabilities.reload).toBe(false);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("enables cancel when onCancel is provided", () => {
|
|
108
|
-
const adapter = createBaseAdapter({
|
|
109
|
-
onCancel: vi.fn(async () => {}),
|
|
110
|
-
});
|
|
111
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
112
|
-
expect(core.capabilities.cancel).toBe(true);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("disables cancel when onCancel is not provided", () => {
|
|
116
|
-
const adapter = createBaseAdapter();
|
|
117
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
118
|
-
expect(core.capabilities.cancel).toBe(false);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("enables switchToBranch when setMessages is provided", () => {
|
|
122
|
-
const adapter = createBaseAdapter({
|
|
123
|
-
setMessages: vi.fn(),
|
|
124
|
-
});
|
|
125
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
126
|
-
expect(core.capabilities.switchToBranch).toBe(true);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("defaults unstable_copy to true", () => {
|
|
130
|
-
const adapter = createBaseAdapter();
|
|
131
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
132
|
-
expect(core.capabilities.unstable_copy).toBe(true);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("disables unstable_copy when explicitly set to false", () => {
|
|
136
|
-
const adapter = createBaseAdapter({
|
|
137
|
-
unstable_capabilities: { copy: false },
|
|
138
|
-
});
|
|
139
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
140
|
-
expect(core.capabilities.unstable_copy).toBe(false);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("append", () => {
|
|
145
|
-
it("calls onNew when parentId matches last message id", async () => {
|
|
146
|
-
const onNew = vi.fn(async () => {});
|
|
147
|
-
const messages = [createUserMessage("u1"), createAssistantMessage("a1")];
|
|
148
|
-
const adapter = createBaseAdapter({ messages, onNew });
|
|
149
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
150
|
-
|
|
151
|
-
const appendMessage: AppendMessage = {
|
|
152
|
-
role: "user",
|
|
153
|
-
content: [{ type: "text", text: "Follow up" }],
|
|
154
|
-
attachments: [],
|
|
155
|
-
createdAt: new Date(),
|
|
156
|
-
parentId: "a1",
|
|
157
|
-
sourceId: null,
|
|
158
|
-
runConfig: undefined,
|
|
159
|
-
metadata: { custom: {} },
|
|
160
|
-
} as AppendMessage;
|
|
161
|
-
|
|
162
|
-
await core.append(appendMessage);
|
|
163
|
-
expect(onNew).toHaveBeenCalledWith(appendMessage);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("calls onEdit when parentId does not match last message id", async () => {
|
|
167
|
-
const onEdit = vi.fn(async () => {});
|
|
168
|
-
const onNew = vi.fn(async () => {});
|
|
169
|
-
const messages = [createUserMessage("u1"), createAssistantMessage("a1")];
|
|
170
|
-
const adapter = createBaseAdapter({ messages, onNew, onEdit });
|
|
171
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
172
|
-
|
|
173
|
-
const appendMessage: AppendMessage = {
|
|
174
|
-
role: "user",
|
|
175
|
-
content: [{ type: "text", text: "Edit" }],
|
|
176
|
-
attachments: [],
|
|
177
|
-
createdAt: new Date(),
|
|
178
|
-
parentId: "u1",
|
|
179
|
-
sourceId: null,
|
|
180
|
-
runConfig: undefined,
|
|
181
|
-
metadata: { custom: {} },
|
|
182
|
-
} as AppendMessage;
|
|
183
|
-
|
|
184
|
-
await core.append(appendMessage);
|
|
185
|
-
expect(onEdit).toHaveBeenCalledWith(appendMessage);
|
|
186
|
-
expect(onNew).not.toHaveBeenCalled();
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("throws when adapter has no onEdit and parentId differs from head", async () => {
|
|
190
|
-
const messages = [createUserMessage("u1"), createAssistantMessage("a1")];
|
|
191
|
-
const adapter = createBaseAdapter({ messages });
|
|
192
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
193
|
-
|
|
194
|
-
const appendMessage: AppendMessage = {
|
|
195
|
-
role: "user",
|
|
196
|
-
content: [{ type: "text", text: "Edit" }],
|
|
197
|
-
attachments: [],
|
|
198
|
-
createdAt: new Date(),
|
|
199
|
-
parentId: "u1",
|
|
200
|
-
sourceId: null,
|
|
201
|
-
runConfig: undefined,
|
|
202
|
-
metadata: { custom: {} },
|
|
203
|
-
} as AppendMessage;
|
|
204
|
-
|
|
205
|
-
await expect(core.append(appendMessage)).rejects.toThrow(
|
|
206
|
-
"Runtime does not support editing messages.",
|
|
207
|
-
);
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
describe("startRun", () => {
|
|
212
|
-
it("delegates to onReload", async () => {
|
|
213
|
-
const onReload = vi.fn(async () => {});
|
|
214
|
-
const adapter = createBaseAdapter({ onReload });
|
|
215
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
216
|
-
|
|
217
|
-
await core.startRun({ parentId: "msg-1", sourceId: null, runConfig: {} });
|
|
218
|
-
expect(onReload).toHaveBeenCalledWith("msg-1", {
|
|
219
|
-
parentId: "msg-1",
|
|
220
|
-
sourceId: null,
|
|
221
|
-
runConfig: {},
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it("throws when adapter has no onReload", async () => {
|
|
226
|
-
const adapter = createBaseAdapter();
|
|
227
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
228
|
-
|
|
229
|
-
await expect(
|
|
230
|
-
core.startRun({ parentId: "msg-1", sourceId: null, runConfig: {} }),
|
|
231
|
-
).rejects.toThrow("Runtime does not support reloading messages.");
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
describe("cancelRun", () => {
|
|
236
|
-
it("delegates to onCancel", () => {
|
|
237
|
-
const onCancel = vi.fn();
|
|
238
|
-
const messages = [createUserMessage("u1"), createAssistantMessage("a1")];
|
|
239
|
-
const adapter = createBaseAdapter({
|
|
240
|
-
messages,
|
|
241
|
-
onCancel,
|
|
242
|
-
setMessages: vi.fn(),
|
|
243
|
-
});
|
|
244
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
245
|
-
|
|
246
|
-
core.cancelRun();
|
|
247
|
-
expect(onCancel).toHaveBeenCalled();
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it("throws when adapter has no onCancel", () => {
|
|
251
|
-
const adapter = createBaseAdapter();
|
|
252
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
253
|
-
|
|
254
|
-
expect(() => core.cancelRun()).toThrow(
|
|
255
|
-
"Runtime does not support cancelling runs.",
|
|
256
|
-
);
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it("keeps a partially-streamed optimistic message on cancel", async () => {
|
|
260
|
-
// Only empty optimistic heads are evicted; one with content survives.
|
|
261
|
-
const optimisticAssistant = {
|
|
262
|
-
...createAssistantMessage("server-msg", "partial answer"),
|
|
263
|
-
status: { type: "running" as const },
|
|
264
|
-
metadata: {
|
|
265
|
-
unstable_state: null,
|
|
266
|
-
unstable_annotations: [],
|
|
267
|
-
unstable_data: [],
|
|
268
|
-
steps: [],
|
|
269
|
-
custom: {},
|
|
270
|
-
isOptimistic: true,
|
|
271
|
-
},
|
|
272
|
-
} as ThreadMessage;
|
|
273
|
-
const setMessages = vi.fn();
|
|
274
|
-
const adapter = createBaseAdapter({
|
|
275
|
-
messages: [createUserMessage("u1"), optimisticAssistant],
|
|
276
|
-
isRunning: true,
|
|
277
|
-
onCancel: vi.fn(),
|
|
278
|
-
setMessages,
|
|
279
|
-
});
|
|
280
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
281
|
-
|
|
282
|
-
core.cancelRun();
|
|
283
|
-
|
|
284
|
-
// cancelRun resyncs to the store via setTimeout(0); inspect the push.
|
|
285
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
286
|
-
const lastCall = setMessages.mock.lastCall?.[0] as ThreadMessage[];
|
|
287
|
-
expect(lastCall.map((m) => m.id)).toContain("server-msg");
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it("evicts an empty optimistic head on cancel", async () => {
|
|
291
|
-
// An empty optimistic head is dropped on cancel, falling back to its prev.
|
|
292
|
-
const optimisticAssistant = {
|
|
293
|
-
...createAssistantMessage("server-msg", ""),
|
|
294
|
-
content: [],
|
|
295
|
-
status: { type: "running" as const },
|
|
296
|
-
metadata: {
|
|
297
|
-
unstable_state: null,
|
|
298
|
-
unstable_annotations: [],
|
|
299
|
-
unstable_data: [],
|
|
300
|
-
steps: [],
|
|
301
|
-
custom: {},
|
|
302
|
-
isOptimistic: true,
|
|
303
|
-
},
|
|
304
|
-
} as ThreadMessage;
|
|
305
|
-
const setMessages = vi.fn();
|
|
306
|
-
const adapter = createBaseAdapter({
|
|
307
|
-
messages: [createUserMessage("u1"), optimisticAssistant],
|
|
308
|
-
isRunning: true,
|
|
309
|
-
onCancel: vi.fn(),
|
|
310
|
-
setMessages,
|
|
311
|
-
});
|
|
312
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
313
|
-
|
|
314
|
-
core.cancelRun();
|
|
315
|
-
|
|
316
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
317
|
-
const lastCall = setMessages.mock.lastCall?.[0] as ThreadMessage[];
|
|
318
|
-
expect(lastCall.map((m) => m.id)).not.toContain("server-msg");
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
describe("optimistic assistant message", () => {
|
|
323
|
-
it("adds optimistic assistant message when running with last user message", () => {
|
|
324
|
-
const userMsg = createUserMessage("u1");
|
|
325
|
-
const adapter = createBaseAdapter({
|
|
326
|
-
messages: [userMsg],
|
|
327
|
-
isRunning: true,
|
|
328
|
-
});
|
|
329
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
330
|
-
|
|
331
|
-
const messages = core.messages;
|
|
332
|
-
expect(messages.length).toBe(2);
|
|
333
|
-
expect(messages[0]!.id).toBe("u1");
|
|
334
|
-
expect(messages[1]!.role).toBe("assistant");
|
|
335
|
-
expect(messages[1]!.content).toEqual([]);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it("does not add optimistic message when last message is assistant", () => {
|
|
339
|
-
const adapter = createBaseAdapter({
|
|
340
|
-
messages: [createUserMessage("u1"), createAssistantMessage("a1")],
|
|
341
|
-
isRunning: true,
|
|
342
|
-
});
|
|
343
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
344
|
-
|
|
345
|
-
const messages = core.messages;
|
|
346
|
-
expect(messages.length).toBe(2);
|
|
347
|
-
expect(messages[1]!.role).toBe("assistant");
|
|
348
|
-
expect(messages[1]!.id).toBe("a1");
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it("does not add optimistic message when not running", () => {
|
|
352
|
-
const adapter = createBaseAdapter({
|
|
353
|
-
messages: [createUserMessage("u1")],
|
|
354
|
-
isRunning: false,
|
|
355
|
-
});
|
|
356
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
357
|
-
|
|
358
|
-
expect(core.messages.length).toBe(1);
|
|
359
|
-
});
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
describe("adapter update via __internal_setAdapter", () => {
|
|
363
|
-
it("updates capabilities when adapter changes", () => {
|
|
364
|
-
const adapter1 = createBaseAdapter();
|
|
365
|
-
const core = new ExternalStoreThreadRuntimeCore(
|
|
366
|
-
contextProvider,
|
|
367
|
-
adapter1,
|
|
368
|
-
);
|
|
369
|
-
expect(core.capabilities.edit).toBe(false);
|
|
370
|
-
|
|
371
|
-
const adapter2 = createBaseAdapter({
|
|
372
|
-
onEdit: vi.fn(async () => {}),
|
|
373
|
-
});
|
|
374
|
-
core.__internal_setAdapter(adapter2);
|
|
375
|
-
expect(core.capabilities.edit).toBe(true);
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
it("updates isDisabled from adapter", () => {
|
|
379
|
-
const adapter = createBaseAdapter({ isDisabled: true });
|
|
380
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
381
|
-
expect(core.isDisabled).toBe(true);
|
|
382
|
-
|
|
383
|
-
core.__internal_setAdapter(createBaseAdapter({ isDisabled: false }));
|
|
384
|
-
expect(core.isDisabled).toBe(false);
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
it("updates suggestions from adapter", () => {
|
|
388
|
-
const suggestions = [{ prompt: "Tell me a joke" }];
|
|
389
|
-
const adapter = createBaseAdapter({ suggestions });
|
|
390
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
391
|
-
expect(core.suggestions).toEqual(suggestions);
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
it("skips update when same adapter reference is passed", () => {
|
|
395
|
-
const adapter = createBaseAdapter({
|
|
396
|
-
messages: [createUserMessage("u1")],
|
|
397
|
-
});
|
|
398
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
399
|
-
const spy = vi.fn();
|
|
400
|
-
core.subscribe(spy);
|
|
401
|
-
spy.mockClear();
|
|
402
|
-
|
|
403
|
-
// Same reference — should not notify
|
|
404
|
-
core.__internal_setAdapter(adapter);
|
|
405
|
-
expect(spy).not.toHaveBeenCalled();
|
|
406
|
-
});
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
describe("switchToBranch", () => {
|
|
410
|
-
it("throws when setMessages is not provided", () => {
|
|
411
|
-
const adapter = createBaseAdapter({
|
|
412
|
-
messages: [createUserMessage("u1")],
|
|
413
|
-
});
|
|
414
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
415
|
-
|
|
416
|
-
expect(() => core.switchToBranch("u1")).toThrow(
|
|
417
|
-
"Runtime does not support switching branches.",
|
|
418
|
-
);
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
it("silently ignores branch switch while running", () => {
|
|
422
|
-
const setMessages = vi.fn();
|
|
423
|
-
const adapter = createBaseAdapter({
|
|
424
|
-
messages: [createUserMessage("u1"), createAssistantMessage("a1")],
|
|
425
|
-
isRunning: true,
|
|
426
|
-
setMessages,
|
|
427
|
-
});
|
|
428
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
429
|
-
|
|
430
|
-
// Should not throw, should silently return
|
|
431
|
-
core.switchToBranch("u1");
|
|
432
|
-
expect(setMessages).not.toHaveBeenCalled();
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
describe("exportExternalState and importExternalState", () => {
|
|
437
|
-
it("delegates export to adapter", () => {
|
|
438
|
-
const state = { key: "value" };
|
|
439
|
-
const adapter = createBaseAdapter({
|
|
440
|
-
onExportExternalState: vi.fn(() => state),
|
|
441
|
-
});
|
|
442
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
443
|
-
|
|
444
|
-
expect(core.exportExternalState()).toEqual(state);
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
it("throws on export when adapter has no onExportExternalState", () => {
|
|
448
|
-
const adapter = createBaseAdapter();
|
|
449
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
450
|
-
|
|
451
|
-
expect(() => core.exportExternalState()).toThrow(
|
|
452
|
-
"Runtime does not support exporting external states.",
|
|
453
|
-
);
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
it("delegates import to adapter", () => {
|
|
457
|
-
const onLoadExternalState = vi.fn();
|
|
458
|
-
const adapter = createBaseAdapter({ onLoadExternalState });
|
|
459
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
460
|
-
|
|
461
|
-
const state = { key: "value" };
|
|
462
|
-
core.importExternalState(state);
|
|
463
|
-
expect(onLoadExternalState).toHaveBeenCalledWith(state);
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
it("throws on import when adapter has no onLoadExternalState", () => {
|
|
467
|
-
const adapter = createBaseAdapter();
|
|
468
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
469
|
-
|
|
470
|
-
expect(() => core.importExternalState({})).toThrow(
|
|
471
|
-
"Runtime does not support importing external states.",
|
|
472
|
-
);
|
|
473
|
-
});
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
describe("beginEdit", () => {
|
|
477
|
-
it("throws when adapter has no onEdit", () => {
|
|
478
|
-
const adapter = createBaseAdapter({
|
|
479
|
-
messages: [createUserMessage("u1")],
|
|
480
|
-
});
|
|
481
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
482
|
-
|
|
483
|
-
expect(() => core.beginEdit("u1")).toThrow(
|
|
484
|
-
"Runtime does not support editing.",
|
|
485
|
-
);
|
|
486
|
-
});
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
describe("subscribe", () => {
|
|
490
|
-
it("notifies subscribers when adapter changes", () => {
|
|
491
|
-
const adapter1 = createBaseAdapter({
|
|
492
|
-
messages: [createUserMessage("u1")],
|
|
493
|
-
});
|
|
494
|
-
const core = new ExternalStoreThreadRuntimeCore(
|
|
495
|
-
contextProvider,
|
|
496
|
-
adapter1,
|
|
497
|
-
);
|
|
498
|
-
|
|
499
|
-
const callback = vi.fn();
|
|
500
|
-
core.subscribe(callback);
|
|
501
|
-
callback.mockClear();
|
|
502
|
-
|
|
503
|
-
const adapter2 = createBaseAdapter({
|
|
504
|
-
messages: [createUserMessage("u1"), createAssistantMessage("a1")],
|
|
505
|
-
});
|
|
506
|
-
core.__internal_setAdapter(adapter2);
|
|
507
|
-
expect(callback).toHaveBeenCalled();
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
it("returns unsubscribe function", () => {
|
|
511
|
-
const adapter = createBaseAdapter({
|
|
512
|
-
messages: [createUserMessage("u1")],
|
|
513
|
-
});
|
|
514
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
515
|
-
|
|
516
|
-
const callback = vi.fn();
|
|
517
|
-
const unsub = core.subscribe(callback);
|
|
518
|
-
callback.mockClear();
|
|
519
|
-
|
|
520
|
-
unsub();
|
|
521
|
-
|
|
522
|
-
core.__internal_setAdapter(
|
|
523
|
-
createBaseAdapter({
|
|
524
|
-
messages: [createUserMessage("u1"), createAssistantMessage("a1")],
|
|
525
|
-
}),
|
|
526
|
-
);
|
|
527
|
-
expect(callback).not.toHaveBeenCalled();
|
|
528
|
-
});
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
describe("addToolResult", () => {
|
|
532
|
-
it("throws when adapter has no onAddToolResult", () => {
|
|
533
|
-
const adapter = createBaseAdapter();
|
|
534
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
535
|
-
|
|
536
|
-
expect(() =>
|
|
537
|
-
core.addToolResult({
|
|
538
|
-
messageId: "m1",
|
|
539
|
-
toolName: "tool",
|
|
540
|
-
toolCallId: "tc1",
|
|
541
|
-
result: {},
|
|
542
|
-
isError: false,
|
|
543
|
-
}),
|
|
544
|
-
).toThrow("Runtime does not support tool results.");
|
|
545
|
-
});
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
describe("resumeRun", () => {
|
|
549
|
-
it("throws when adapter has no onResume", async () => {
|
|
550
|
-
const adapter = createBaseAdapter();
|
|
551
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
|
|
552
|
-
|
|
553
|
-
await expect(
|
|
554
|
-
core.resumeRun({ parentId: "msg-1" } as any),
|
|
555
|
-
).rejects.toThrow("Runtime does not support resuming runs.");
|
|
556
|
-
});
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
describe("adapter requires messages or messageRepository", () => {
|
|
560
|
-
it("throws when adapter has neither messages nor messageRepository", () => {
|
|
561
|
-
const adapter = {
|
|
562
|
-
onNew: vi.fn(async () => {}),
|
|
563
|
-
} as unknown as ExternalStoreAdapter<ThreadMessage>;
|
|
564
|
-
|
|
565
|
-
expect(
|
|
566
|
-
() => new ExternalStoreThreadRuntimeCore(contextProvider, adapter),
|
|
567
|
-
).toThrow(
|
|
568
|
-
"ExternalStoreAdapter must provide either 'messages' or 'messageRepository'",
|
|
569
|
-
);
|
|
570
|
-
});
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
describe("messageRepository incremental sync", () => {
|
|
574
|
-
const u1 = createUserMessage("u1");
|
|
575
|
-
const a1 = createAssistantMessage("a1");
|
|
576
|
-
|
|
577
|
-
const repoAdapter = (
|
|
578
|
-
messages: { message: ThreadMessage; parentId: string | null }[],
|
|
579
|
-
headId: string | null,
|
|
580
|
-
overrides: Partial<ExternalStoreAdapter<ThreadMessage>> = {},
|
|
581
|
-
): ExternalStoreAdapter<ThreadMessage> => ({
|
|
582
|
-
messageRepository: { headId, messages },
|
|
583
|
-
onNew: vi.fn(async () => {}),
|
|
584
|
-
...overrides,
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
it("imports the initial repository", () => {
|
|
588
|
-
const core = new ExternalStoreThreadRuntimeCore(
|
|
589
|
-
contextProvider,
|
|
590
|
-
repoAdapter([{ message: u1, parentId: null }], "u1"),
|
|
591
|
-
);
|
|
592
|
-
expect(core.messages.map((m) => m.id)).toEqual(["u1"]);
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
it("adds a message when the repository is replaced", () => {
|
|
596
|
-
const core = new ExternalStoreThreadRuntimeCore(
|
|
597
|
-
contextProvider,
|
|
598
|
-
repoAdapter([{ message: u1, parentId: null }], "u1"),
|
|
599
|
-
);
|
|
600
|
-
core.__internal_setAdapter(
|
|
601
|
-
repoAdapter(
|
|
602
|
-
[
|
|
603
|
-
{ message: u1, parentId: null },
|
|
604
|
-
{ message: a1, parentId: "u1" },
|
|
605
|
-
],
|
|
606
|
-
"a1",
|
|
607
|
-
),
|
|
608
|
-
);
|
|
609
|
-
expect(core.messages.map((m) => m.id)).toEqual(["u1", "a1"]);
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
it("deletes a message no longer present in the new repository", () => {
|
|
613
|
-
const core = new ExternalStoreThreadRuntimeCore(
|
|
614
|
-
contextProvider,
|
|
615
|
-
repoAdapter(
|
|
616
|
-
[
|
|
617
|
-
{ message: u1, parentId: null },
|
|
618
|
-
{ message: a1, parentId: "u1" },
|
|
619
|
-
],
|
|
620
|
-
"a1",
|
|
621
|
-
),
|
|
622
|
-
);
|
|
623
|
-
expect(core.messages.map((m) => m.id)).toEqual(["u1", "a1"]);
|
|
624
|
-
|
|
625
|
-
core.__internal_setAdapter(
|
|
626
|
-
repoAdapter([{ message: u1, parentId: null }], "u1"),
|
|
627
|
-
);
|
|
628
|
-
expect(core.messages.map((m) => m.id)).toEqual(["u1"]);
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
it("matches a fresh import when built incrementally", () => {
|
|
632
|
-
const incremental = new ExternalStoreThreadRuntimeCore(
|
|
633
|
-
contextProvider,
|
|
634
|
-
repoAdapter([{ message: u1, parentId: null }], "u1"),
|
|
635
|
-
);
|
|
636
|
-
incremental.__internal_setAdapter(
|
|
637
|
-
repoAdapter(
|
|
638
|
-
[
|
|
639
|
-
{ message: u1, parentId: null },
|
|
640
|
-
{ message: a1, parentId: "u1" },
|
|
641
|
-
],
|
|
642
|
-
"a1",
|
|
643
|
-
),
|
|
644
|
-
);
|
|
645
|
-
const fresh = new ExternalStoreThreadRuntimeCore(
|
|
646
|
-
contextProvider,
|
|
647
|
-
repoAdapter(
|
|
648
|
-
[
|
|
649
|
-
{ message: u1, parentId: null },
|
|
650
|
-
{ message: a1, parentId: "u1" },
|
|
651
|
-
],
|
|
652
|
-
"a1",
|
|
653
|
-
),
|
|
654
|
-
);
|
|
655
|
-
expect(incremental.messages.map((m) => m.id)).toEqual(
|
|
656
|
-
fresh.messages.map((m) => m.id),
|
|
657
|
-
);
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
it("coalesces an isRunning flip on an unchanged repository reference", () => {
|
|
661
|
-
const messageRepository = {
|
|
662
|
-
headId: "u1",
|
|
663
|
-
messages: [{ message: u1, parentId: null }],
|
|
664
|
-
};
|
|
665
|
-
const onNew = vi.fn(async () => {});
|
|
666
|
-
const core = new ExternalStoreThreadRuntimeCore(contextProvider, {
|
|
667
|
-
messageRepository,
|
|
668
|
-
onNew,
|
|
669
|
-
isRunning: false,
|
|
670
|
-
});
|
|
671
|
-
expect(core.messages.map((m) => m.id)).toEqual(["u1"]);
|
|
672
|
-
|
|
673
|
-
core.__internal_setAdapter({ messageRepository, onNew, isRunning: true });
|
|
674
|
-
expect(core.messages.length).toBe(2);
|
|
675
|
-
expect(core.messages[1]!.role).toBe("assistant");
|
|
676
|
-
|
|
677
|
-
core.__internal_setAdapter({
|
|
678
|
-
messageRepository,
|
|
679
|
-
onNew,
|
|
680
|
-
isRunning: false,
|
|
681
|
-
});
|
|
682
|
-
expect(core.messages.map((m) => m.id)).toEqual(["u1"]);
|
|
683
|
-
});
|
|
684
|
-
});
|
|
685
|
-
});
|