@assistant-ui/core 0.3.18 → 0.3.19
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/adapters/speech.d.ts.map +1 -1
- package/dist/adapters/speech.js +13 -3
- package/dist/adapters/speech.js.map +1 -1
- package/dist/adapters/thread-history.d.ts +4 -0
- package/dist/adapters/thread-history.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +26 -11
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/interactable-composer-metadata.js +11 -9
- package/dist/model-context/interactable-composer-metadata.js.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.js +45 -7
- package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
- package/dist/react/client/DataRenderers.d.ts.map +1 -1
- package/dist/react/client/DataRenderers.js +20 -15
- package/dist/react/client/DataRenderers.js.map +1 -1
- package/dist/react/client/Interactables.d.ts.map +1 -1
- package/dist/react/client/Interactables.js +11 -5
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
- package/dist/react/client/RemoteThreadList.js +3 -2
- package/dist/react/client/RemoteThreadList.js.map +1 -1
- package/dist/react/client/interactable-model-context.d.ts.map +1 -1
- package/dist/react/client/interactable-model-context.js +3 -2
- package/dist/react/client/interactable-model-context.js.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +2 -2
- package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
- package/dist/react/interactables-legacy/useAssistantInteractable.js +3 -0
- package/dist/react/interactables-legacy/useAssistantInteractable.js.map +1 -1
- package/dist/react/model-context/useInteractable.d.ts.map +1 -1
- package/dist/react/model-context/useInteractable.js +4 -1
- package/dist/react/model-context/useInteractable.js.map +1 -1
- package/dist/react/model-context/useInteractableVersions.js +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.d.ts +11 -2
- package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.js +3 -2
- package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
- package/dist/react/primitives/part/PartMessages.d.ts +1 -0
- package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
- package/dist/react/primitives/part/PartMessages.js +1 -0
- package/dist/react/primitives/part/PartMessages.js.map +1 -1
- package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/react/primitives/thread/ThreadMessages.js +6 -5
- package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +2 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +2 -24
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +135 -374
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +1 -0
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +22 -2
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts +40 -15
- package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.js +62 -15
- package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +2 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +4 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/sdkIdentity.d.ts +6 -0
- package/dist/react/runtimes/cloud/sdkIdentity.d.ts.map +1 -0
- package/dist/react/runtimes/cloud/sdkIdentity.js +9 -0
- package/dist/react/runtimes/cloud/sdkIdentity.js.map +1 -0
- package/dist/react/runtimes/external-message-converter.d.ts +12 -2
- package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.js +26 -61
- package/dist/react/runtimes/external-message-converter.js.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.js +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
- package/dist/react/utils/groupParts.d.ts +6 -1
- package/dist/react/utils/groupParts.d.ts.map +1 -1
- package/dist/react/utils/groupParts.js +6 -0
- package/dist/react/utils/groupParts.js.map +1 -1
- package/dist/react/utils/useJSONEqual.d.ts +3 -1
- package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
- package/dist/react/utils/useJSONEqual.js +6 -1
- package/dist/react/utils/useJSONEqual.js.map +1 -1
- package/dist/react/utils/useJSONSchemaDependency.d.ts +7 -0
- package/dist/react/utils/useJSONSchemaDependency.d.ts.map +1 -0
- package/dist/react/utils/useJSONSchemaDependency.js +20 -0
- package/dist/react/utils/useJSONSchemaDependency.js.map +1 -0
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-runtime.js +2 -5
- package/dist/runtime/api/thread-runtime.js.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -0
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +46 -10
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -0
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +155 -64
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
- package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/queue/message-queue.d.ts +5 -0
- package/dist/runtime/queue/message-queue.d.ts.map +1 -1
- package/dist/runtime/queue/message-queue.js +61 -6
- package/dist/runtime/queue/message-queue.js.map +1 -1
- package/dist/runtime/utils/auto-status.d.ts +6 -2
- package/dist/runtime/utils/auto-status.d.ts.map +1 -1
- package/dist/runtime/utils/auto-status.js +12 -4
- package/dist/runtime/utils/auto-status.js.map +1 -1
- package/dist/runtime/utils/external-message-conversion.d.ts +16 -4
- package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
- package/dist/runtime/utils/external-message-conversion.js +48 -12
- package/dist/runtime/utils/external-message-conversion.js.map +1 -1
- package/dist/runtime/utils/message-repository.js +3 -3
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/thread-message-like.d.ts +2 -1
- package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
- package/dist/runtime/utils/thread-message-like.js +4 -2
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +8 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/internal.d.ts +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +4 -1
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
- package/dist/store/clients/external-thread.d.ts.map +1 -1
- package/dist/store/clients/external-thread.js +241 -200
- package/dist/store/clients/external-thread.js.map +1 -1
- package/dist/store/clients/thread-tasks.d.ts +13 -0
- package/dist/store/clients/thread-tasks.d.ts.map +1 -0
- package/dist/store/clients/thread-tasks.js +69 -0
- package/dist/store/clients/thread-tasks.js.map +1 -0
- package/dist/store/index.d.ts +3 -2
- package/dist/store/index.js +3 -3
- package/dist/store/primitive-predicates.d.ts.map +1 -1
- package/dist/store/primitive-predicates.js +4 -4
- package/dist/store/primitive-predicates.js.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.js +176 -128
- package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
- package/dist/store/scope-registration.d.ts +2 -0
- package/dist/store/scope-registration.d.ts.map +1 -1
- package/dist/store/scopes/task.d.ts +36 -0
- package/dist/store/scopes/task.d.ts.map +1 -0
- package/dist/store/scopes/task.js +0 -0
- package/dist/store/scopes/thread.d.ts +20 -0
- package/dist/store/scopes/thread.d.ts.map +1 -1
- package/dist/subscribable/subscribable.d.ts +2 -1
- package/dist/subscribable/subscribable.d.ts.map +1 -1
- package/dist/subscribable/subscribable.js +23 -15
- package/dist/subscribable/subscribable.js.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/message.d.ts +8 -1
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js.map +1 -1
- package/dist/utils/abortable-iterable.d.ts.map +1 -1
- package/dist/utils/abortable-iterable.js +30 -14
- package/dist/utils/abortable-iterable.js.map +1 -1
- package/dist/utils/getGroupStatus.d.ts +18 -2
- package/dist/utils/getGroupStatus.d.ts.map +1 -1
- package/dist/utils/getGroupStatus.js +32 -7
- package/dist/utils/getGroupStatus.js.map +1 -1
- package/dist/utils/wire-media.d.ts.map +1 -1
- package/dist/utils/wire-media.js +2 -2
- package/dist/utils/wire-media.js.map +1 -1
- package/package.json +6 -6
- package/src/adapters/speech.test.ts +236 -0
- package/src/adapters/speech.ts +17 -10
- package/src/adapters/thread-history.ts +3 -0
- package/src/index.ts +1 -0
- package/src/model-context/frame/host.test.ts +113 -1
- package/src/model-context/frame/host.ts +39 -12
- package/src/model-context/interactable-composer-metadata.test.ts +30 -0
- package/src/model-context/interactable-composer-metadata.ts +10 -10
- package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +244 -0
- package/src/react/adapters/LocalStorageThreadListAdapter.tsx +104 -7
- package/src/react/client/DataRenderers.test.tsx +45 -0
- package/src/react/client/DataRenderers.ts +13 -9
- package/src/react/client/Interactables.test.ts +44 -1
- package/src/react/client/Interactables.ts +7 -1
- package/src/react/client/RemoteThreadList.test.ts +41 -1
- package/src/react/client/RemoteThreadList.ts +15 -2
- package/src/react/client/interactable-model-context.test.ts +44 -0
- package/src/react/client/interactable-model-context.ts +3 -2
- package/src/react/index.ts +5 -1
- package/src/react/interactables-legacy/useAssistantInteractable.test.tsx +132 -0
- package/src/react/interactables-legacy/useAssistantInteractable.ts +10 -1
- package/src/react/model-context/useInteractable.test.tsx +177 -0
- package/src/react/model-context/useInteractable.ts +3 -0
- package/src/react/primitive-hooks/useActionBarCopy.test.ts +0 -1
- package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +0 -1
- package/src/react/primitives/message/MessageGroupedParts.test.tsx +96 -0
- package/src/react/primitives/message/MessageGroupedParts.tsx +15 -4
- package/src/react/primitives/part/PartMessages.test.tsx +104 -1
- package/src/react/primitives/part/PartMessages.tsx +1 -0
- package/src/react/primitives/thread/ThreadMessages.test.tsx +142 -0
- package/src/react/primitives/thread/ThreadMessages.tsx +8 -5
- package/src/react/providers/ReadonlyThreadProvider.test.tsx +56 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +7 -1
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +20 -14
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +221 -1
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +166 -512
- package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +77 -0
- package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +27 -1
- package/src/react/runtimes/cloud/auiV0.ts +160 -25
- package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +21 -0
- package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +7 -1
- package/src/react/runtimes/cloud/sdkIdentity.ts +9 -0
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +18 -4
- package/src/react/runtimes/external-message-converter.test.ts +136 -1
- package/src/react/runtimes/external-message-converter.ts +39 -93
- package/src/react/runtimes/useRemoteThreadListRuntime.ts +1 -1
- package/src/react/utils/groupParts.ts +12 -1
- package/src/react/utils/useJSONEqual.ts +9 -0
- package/src/react/utils/useJSONSchemaDependency.ts +22 -0
- package/src/runtime/api/thread-runtime.test.ts +58 -0
- package/src/runtime/api/thread-runtime.ts +2 -4
- package/src/runtime/base/base-composer-runtime-core.ts +69 -16
- package/src/runtime/base/base-thread-runtime-core.test.ts +1008 -1
- package/src/runtime/base/base-thread-runtime-core.ts +222 -83
- package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
- package/src/runtime/queue/message-queue.ts +68 -6
- package/src/runtime/utils/auto-status.test.ts +112 -1
- package/src/runtime/utils/auto-status.ts +31 -5
- package/src/runtime/utils/external-message-conversion.test.ts +134 -0
- package/src/runtime/utils/external-message-conversion.ts +122 -31
- package/src/runtime/utils/message-repository.test.ts +49 -0
- package/src/runtime/utils/message-repository.ts +3 -3
- package/src/runtime/utils/thread-message-like.test.ts +52 -0
- package/src/runtime/utils/thread-message-like.ts +4 -0
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +31 -1
- package/src/runtimes/local/local-thread-runtime-core.test.ts +21 -0
- package/src/runtimes/local/local-thread-runtime-core.ts +11 -1
- package/src/runtimes/tool-invocations/EDGE_CASES.md +2 -6
- package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +133 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +10 -6
- package/src/store/clients/external-thread.ts +19 -1
- package/src/store/clients/thread-tasks.test.ts +274 -0
- package/src/store/clients/thread-tasks.ts +138 -0
- package/src/store/index.ts +8 -0
- package/src/store/primitive-predicates.test.ts +48 -5
- package/src/store/primitive-predicates.ts +7 -2
- package/src/store/runtime-clients/thread-runtime-client.ts +31 -1
- package/src/store/scope-registration.ts +2 -0
- package/src/store/scopes/task.ts +34 -0
- package/src/store/scopes/thread.ts +16 -0
- package/src/store/thread-tasks.test.tsx +209 -0
- package/src/subscribable/subscribable.test.ts +156 -1
- package/src/subscribable/subscribable.ts +25 -15
- package/src/tests/MessageRepository.test.ts +1 -5
- package/src/tests/auiV0Encode.test.ts +300 -0
- package/src/tests/base-composer-runtime-core-send.test.ts +51 -0
- package/src/tests/base-composer-runtime-core.test.ts +314 -2
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +151 -0
- package/src/tests/external-thread-attachments.test.tsx +69 -0
- package/src/tests/external-thread-parity.test.tsx +34 -0
- package/src/tests/groupParts.test.ts +71 -0
- package/src/tests/message-queue.test.ts +253 -0
- package/src/tests/remote-thread-list-reactive-threadId.test.ts +51 -0
- package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +43 -0
- package/src/types/index.ts +1 -0
- package/src/types/message.ts +8 -0
- package/src/utils/abortable-iterable.test.ts +115 -0
- package/src/utils/abortable-iterable.ts +34 -23
- package/src/utils/getGroupStatus.test.ts +70 -4
- package/src/utils/getGroupStatus.ts +35 -12
- package/src/utils/wire-media.test.ts +10 -0
- package/src/utils/wire-media.ts +6 -2
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
MessagePartStatus,
|
|
9
9
|
ToolCallMessagePartStatus,
|
|
10
10
|
} from "../../../types/message";
|
|
11
|
-
import {
|
|
11
|
+
import { getGroupSummary } from "../../../utils/getGroupStatus";
|
|
12
12
|
import {
|
|
13
13
|
buildGroupTree,
|
|
14
14
|
GROUPBY_MEMO_KEY,
|
|
@@ -18,6 +18,14 @@ import {
|
|
|
18
18
|
import { MessagePartChildren, type EnrichedPartState } from "./MessageParts";
|
|
19
19
|
|
|
20
20
|
export namespace MessagePrimitiveGroupedParts {
|
|
21
|
+
/** Per status tallies over the group's `indices`; they sum to `indices.length`. */
|
|
22
|
+
export type GroupCounts = {
|
|
23
|
+
readonly running: number;
|
|
24
|
+
readonly complete: number;
|
|
25
|
+
readonly incomplete: number;
|
|
26
|
+
readonly requiresAction: number;
|
|
27
|
+
};
|
|
28
|
+
|
|
21
29
|
/**
|
|
22
30
|
* A coalesced group of adjacent parts. Surfaced through the same
|
|
23
31
|
* `{ part }` channel as a leaf {@link EnrichedPartState} so consumers
|
|
@@ -28,6 +36,8 @@ export namespace MessagePrimitiveGroupedParts {
|
|
|
28
36
|
export type GroupPart<TKey extends `group-${string}` = `group-${string}`> = {
|
|
29
37
|
readonly type: TKey;
|
|
30
38
|
readonly status: MessagePartStatus | ToolCallMessagePartStatus;
|
|
39
|
+
/** Per status tallies over `indices`. */
|
|
40
|
+
readonly counts: GroupCounts;
|
|
31
41
|
readonly indices: readonly number[];
|
|
32
42
|
};
|
|
33
43
|
|
|
@@ -85,10 +95,10 @@ export namespace MessagePrimitiveGroupedParts {
|
|
|
85
95
|
* `switch (part.type)` can tell groups apart from real part types.
|
|
86
96
|
*
|
|
87
97
|
* **Prefer {@link groupPartByType}** for the common case of mapping by
|
|
88
|
-
* `part.type` — it ships a stable memo fingerprint so the tree
|
|
98
|
+
* `part.type` or tool name — it ships a stable memo fingerprint so the tree
|
|
89
99
|
* survives unrelated re-renders. Use an inline function only when
|
|
90
100
|
* the helper isn't expressive enough (e.g. branching on
|
|
91
|
-
* `part.
|
|
101
|
+
* `part.parentId` or part metadata).
|
|
92
102
|
*
|
|
93
103
|
* The second argument is a {@link GroupByContext} carrying the tool-UI
|
|
94
104
|
* registry, for grouping that depends on it (e.g. standalone tool calls).
|
|
@@ -194,10 +204,11 @@ const renderNode = <TKey extends `group-${string}`>(
|
|
|
194
204
|
);
|
|
195
205
|
}
|
|
196
206
|
|
|
197
|
-
const status =
|
|
207
|
+
const { status, counts } = getGroupSummary(parts, node.indices);
|
|
198
208
|
const groupPart: MessagePrimitiveGroupedParts.GroupPart<TKey> = {
|
|
199
209
|
type: node.key as TKey,
|
|
200
210
|
status,
|
|
211
|
+
counts,
|
|
201
212
|
indices: node.indices,
|
|
202
213
|
};
|
|
203
214
|
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
4
|
+
import { useAuiState } from "@assistant-ui/store";
|
|
5
|
+
import { afterEach, describe, expect, expectTypeOf, it } from "vitest";
|
|
6
|
+
import type { ThreadMessage } from "../../../types/message";
|
|
7
|
+
import { AssistantRuntimeProvider } from "../../AssistantRuntimeProvider";
|
|
8
|
+
import { useExternalStoreRuntime } from "../../runtimes/useExternalStoreRuntime";
|
|
9
|
+
import { MessagePrimitiveParts } from "../message/MessageParts";
|
|
10
|
+
import { ThreadPrimitiveMessages } from "../thread/ThreadMessages";
|
|
2
11
|
import { PartPrimitiveMessages } from "./PartMessages";
|
|
3
12
|
|
|
4
13
|
const checkPartMessagesProps = () => {
|
|
@@ -15,3 +24,97 @@ const checkPartMessagesProps = () => {
|
|
|
15
24
|
</PartPrimitiveMessages>;
|
|
16
25
|
};
|
|
17
26
|
expectTypeOf(checkPartMessagesProps).toEqualTypeOf<() => void>();
|
|
27
|
+
|
|
28
|
+
const nestedAssistant: ThreadMessage = {
|
|
29
|
+
id: "nested-assistant",
|
|
30
|
+
createdAt: new Date(0),
|
|
31
|
+
role: "assistant",
|
|
32
|
+
content: [],
|
|
33
|
+
status: { type: "running" },
|
|
34
|
+
metadata: {
|
|
35
|
+
unstable_state: {},
|
|
36
|
+
unstable_annotations: [],
|
|
37
|
+
unstable_data: [],
|
|
38
|
+
steps: [],
|
|
39
|
+
custom: {},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const NestedThreadStatus = () => {
|
|
44
|
+
const isRunning = useAuiState((s) => s.thread.isRunning);
|
|
45
|
+
return (
|
|
46
|
+
<output data-testid="nested-thread-running">{String(isRunning)}</output>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const OuterMessageStatus = () => {
|
|
51
|
+
const status = useAuiState((s) => s.message.status?.type);
|
|
52
|
+
return <output data-testid="outer-message-status">{status}</output>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
afterEach(cleanup);
|
|
56
|
+
|
|
57
|
+
describe("PartPrimitiveMessages", () => {
|
|
58
|
+
it("keeps the outer message running and reports the nested thread as running after the outer run ends", () => {
|
|
59
|
+
const App = () => {
|
|
60
|
+
const runtime = useExternalStoreRuntime({
|
|
61
|
+
messages: [
|
|
62
|
+
{
|
|
63
|
+
id: "outer-assistant",
|
|
64
|
+
createdAt: new Date(0),
|
|
65
|
+
role: "assistant" as const,
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "tool-call" as const,
|
|
69
|
+
toolCallId: "delegate-1",
|
|
70
|
+
toolName: "delegate",
|
|
71
|
+
args: {},
|
|
72
|
+
argsText: "",
|
|
73
|
+
messages: [nestedAssistant],
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
metadata: {
|
|
77
|
+
unstable_state: {},
|
|
78
|
+
unstable_annotations: [],
|
|
79
|
+
unstable_data: [],
|
|
80
|
+
steps: [],
|
|
81
|
+
custom: {},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
isRunning: false,
|
|
86
|
+
convertMessage: (message) => message,
|
|
87
|
+
onNew: async () => {},
|
|
88
|
+
});
|
|
89
|
+
return (
|
|
90
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
91
|
+
<ThreadPrimitiveMessages>
|
|
92
|
+
{() => (
|
|
93
|
+
<>
|
|
94
|
+
<OuterMessageStatus />
|
|
95
|
+
<MessagePrimitiveParts>
|
|
96
|
+
{({ part }) =>
|
|
97
|
+
part.type === "tool-call" ? (
|
|
98
|
+
<PartPrimitiveMessages>
|
|
99
|
+
{() => <NestedThreadStatus />}
|
|
100
|
+
</PartPrimitiveMessages>
|
|
101
|
+
) : null
|
|
102
|
+
}
|
|
103
|
+
</MessagePrimitiveParts>
|
|
104
|
+
</>
|
|
105
|
+
)}
|
|
106
|
+
</ThreadPrimitiveMessages>
|
|
107
|
+
</AssistantRuntimeProvider>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
render(<App />);
|
|
112
|
+
|
|
113
|
+
expect(screen.getByTestId("outer-message-status").textContent).toBe(
|
|
114
|
+
"running",
|
|
115
|
+
);
|
|
116
|
+
expect(screen.getByTestId("nested-thread-running").textContent).toBe(
|
|
117
|
+
"true",
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -36,6 +36,7 @@ const usePartMessages = (): readonly ThreadMessage[] | undefined => {
|
|
|
36
36
|
* This primitive reads `messages` from the current tool call part in the PartScope
|
|
37
37
|
* and renders them using a readonly thread context. All existing message and part
|
|
38
38
|
* primitives work inside, and parent tool UI registrations are inherited.
|
|
39
|
+
* The nested thread reports `isRunning` while its trailing assistant message is running.
|
|
39
40
|
*
|
|
40
41
|
* @example
|
|
41
42
|
* ```tsx
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import { AuiProvider, useAui } from "@assistant-ui/store";
|
|
5
|
+
import { type FC, useState } from "react";
|
|
6
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
7
|
+
import {
|
|
8
|
+
ExternalThread,
|
|
9
|
+
type ExternalThreadProps,
|
|
10
|
+
} from "../../../store/clients/external-thread";
|
|
11
|
+
import type { MessageState } from "../../../store";
|
|
12
|
+
import { AssistantRuntimeProvider } from "../../AssistantRuntimeProvider";
|
|
13
|
+
import { useExternalStoreRuntime } from "../../runtimes/useExternalStoreRuntime";
|
|
14
|
+
import { ThreadPrimitiveMessages } from "./ThreadMessages";
|
|
15
|
+
|
|
16
|
+
const message = (id: string, role: "user" | "assistant") =>
|
|
17
|
+
({
|
|
18
|
+
id,
|
|
19
|
+
createdAt: new Date(0),
|
|
20
|
+
role,
|
|
21
|
+
content: [{ type: "text", text: id }],
|
|
22
|
+
attachments: [],
|
|
23
|
+
metadata: {
|
|
24
|
+
custom: {},
|
|
25
|
+
},
|
|
26
|
+
...(role === "assistant" ? { status: { type: "complete" } } : undefined),
|
|
27
|
+
}) as ExternalThreadProps["messages"][number];
|
|
28
|
+
|
|
29
|
+
const StatefulMessage = ({ message }: { message: MessageState }) => {
|
|
30
|
+
const [initialId] = useState(message.id);
|
|
31
|
+
return <span data-role={message.role}>{`${initialId}:${message.id}`}</span>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const renderMessages = (messages: ExternalThreadProps["messages"]) => {
|
|
35
|
+
const App: FC<{ messages: ExternalThreadProps["messages"] }> = ({
|
|
36
|
+
messages,
|
|
37
|
+
}) => {
|
|
38
|
+
const aui = useAui({ thread: ExternalThread({ messages }) });
|
|
39
|
+
return (
|
|
40
|
+
<AuiProvider value={aui}>
|
|
41
|
+
<ThreadPrimitiveMessages>
|
|
42
|
+
{({ message }) => <StatefulMessage message={message} />}
|
|
43
|
+
</ThreadPrimitiveMessages>
|
|
44
|
+
</AuiProvider>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
const result = render(<App messages={messages} />);
|
|
48
|
+
return {
|
|
49
|
+
rerender: (nextMessages: ExternalThreadProps["messages"]) =>
|
|
50
|
+
result.rerender(<App messages={nextMessages} />),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const renderExternalStoreMessages = (
|
|
55
|
+
messages: ExternalThreadProps["messages"],
|
|
56
|
+
isRunning: boolean,
|
|
57
|
+
) => {
|
|
58
|
+
const App: FC<{
|
|
59
|
+
messages: ExternalThreadProps["messages"];
|
|
60
|
+
isRunning: boolean;
|
|
61
|
+
}> = ({ messages, isRunning }) => {
|
|
62
|
+
const runtime = useExternalStoreRuntime({
|
|
63
|
+
messages,
|
|
64
|
+
isRunning,
|
|
65
|
+
convertMessage: (message) => message,
|
|
66
|
+
onNew: async () => {},
|
|
67
|
+
});
|
|
68
|
+
return (
|
|
69
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
70
|
+
<ThreadPrimitiveMessages>
|
|
71
|
+
{({ message }) => <StatefulMessage message={message} />}
|
|
72
|
+
</ThreadPrimitiveMessages>
|
|
73
|
+
</AssistantRuntimeProvider>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
const result = render(<App messages={messages} isRunning={isRunning} />);
|
|
77
|
+
return {
|
|
78
|
+
container: result.container,
|
|
79
|
+
rerender: (
|
|
80
|
+
nextMessages: ExternalThreadProps["messages"],
|
|
81
|
+
nextIsRunning: boolean,
|
|
82
|
+
) =>
|
|
83
|
+
result.rerender(
|
|
84
|
+
<App messages={nextMessages} isRunning={nextIsRunning} />,
|
|
85
|
+
),
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
describe("ThreadPrimitiveMessages", () => {
|
|
90
|
+
afterEach(cleanup);
|
|
91
|
+
|
|
92
|
+
it("keeps component state with the surviving message after removal", () => {
|
|
93
|
+
const first = message("first", "user");
|
|
94
|
+
const second = message("second", "assistant");
|
|
95
|
+
const thread = renderMessages([first, second]);
|
|
96
|
+
|
|
97
|
+
thread.rerender([second]);
|
|
98
|
+
|
|
99
|
+
expect(screen.queryByText("second:second")).not.toBeNull();
|
|
100
|
+
expect(screen.queryByText("first:second")).toBeNull();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("keeps component state with messages after prepending history", () => {
|
|
104
|
+
const first = message("first", "user");
|
|
105
|
+
const second = message("second", "assistant");
|
|
106
|
+
const thread = renderMessages([first, second]);
|
|
107
|
+
|
|
108
|
+
thread.rerender([message("zeroth", "assistant"), first, second]);
|
|
109
|
+
|
|
110
|
+
expect(screen.queryByText("first:first")).not.toBeNull();
|
|
111
|
+
expect(screen.queryByText("second:second")).not.toBeNull();
|
|
112
|
+
expect(screen.queryByText("first:zeroth")).toBeNull();
|
|
113
|
+
expect(screen.queryByText("second:first")).toBeNull();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("remounts a message when its runtime ID changes", () => {
|
|
117
|
+
const thread = renderMessages([message("client-id", "assistant")]);
|
|
118
|
+
|
|
119
|
+
thread.rerender([message("server-id", "assistant")]);
|
|
120
|
+
|
|
121
|
+
expect(screen.queryByText("server-id:server-id")).not.toBeNull();
|
|
122
|
+
expect(screen.queryByText("client-id:server-id")).toBeNull();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("remounts an optimistic placeholder as the real assistant message", async () => {
|
|
126
|
+
const userMessage = message("user-id", "user");
|
|
127
|
+
const thread = renderExternalStoreMessages([userMessage], true);
|
|
128
|
+
const optimisticRow = thread.container.querySelector(
|
|
129
|
+
'[data-role="assistant"]',
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(optimisticRow).not.toBeNull();
|
|
133
|
+
expect(optimisticRow?.textContent).not.toBe("assistant-id:assistant-id");
|
|
134
|
+
|
|
135
|
+
thread.rerender([userMessage, message("assistant-id", "assistant")], false);
|
|
136
|
+
|
|
137
|
+
await waitFor(() => {
|
|
138
|
+
expect(screen.queryByText("assistant-id:assistant-id")).not.toBeNull();
|
|
139
|
+
});
|
|
140
|
+
expect(optimisticRow?.isConnected).toBe(false);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
useMemo,
|
|
7
7
|
} from "react";
|
|
8
8
|
import { RenderChildrenWithAccessor, useAuiState } from "@assistant-ui/store";
|
|
9
|
+
import { useShallowSelector } from "@assistant-ui/store/internal";
|
|
9
10
|
import { MessageByIndexProvider } from "../../providers/MessageByIndexProvider";
|
|
10
11
|
import { MessageByIdProvider } from "../../providers/MessageByIdProvider";
|
|
11
12
|
import type { MessageState } from "../../../store";
|
|
@@ -241,12 +242,14 @@ ThreadPrimitiveUnstable_MessageById.displayName =
|
|
|
241
242
|
const ThreadPrimitiveMessagesInner: FC<{
|
|
242
243
|
children: (value: { message: MessageState }) => ReactNode;
|
|
243
244
|
}> = ({ children }) => {
|
|
244
|
-
const
|
|
245
|
+
const messageIds = useAuiState(
|
|
246
|
+
useShallowSelector((s) => s.thread.messages.map((message) => message.id)),
|
|
247
|
+
);
|
|
245
248
|
|
|
246
249
|
return useMemo(() => {
|
|
247
|
-
if (
|
|
248
|
-
return
|
|
249
|
-
<MessageByIndexProvider key={
|
|
250
|
+
if (messageIds.length === 0) return null;
|
|
251
|
+
return messageIds.map((messageId, index) => (
|
|
252
|
+
<MessageByIndexProvider key={messageId} index={index}>
|
|
250
253
|
<RenderChildrenWithAccessor
|
|
251
254
|
getItemState={(aui) => aui.thread.message({ index }).getState()}
|
|
252
255
|
>
|
|
@@ -260,7 +263,7 @@ const ThreadPrimitiveMessagesInner: FC<{
|
|
|
260
263
|
</RenderChildrenWithAccessor>
|
|
261
264
|
</MessageByIndexProvider>
|
|
262
265
|
));
|
|
263
|
-
}, [
|
|
266
|
+
}, [messageIds, children]);
|
|
264
267
|
};
|
|
265
268
|
|
|
266
269
|
/**
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import { useAuiState } from "@assistant-ui/store";
|
|
5
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
6
|
+
import type { ThreadMessage } from "../../types/message";
|
|
7
|
+
import { ReadonlyThreadProvider } from "./ReadonlyThreadProvider";
|
|
8
|
+
|
|
9
|
+
const messages = (status: "running" | "complete"): readonly ThreadMessage[] => [
|
|
10
|
+
{
|
|
11
|
+
id: "nested-assistant",
|
|
12
|
+
createdAt: new Date(0),
|
|
13
|
+
role: "assistant",
|
|
14
|
+
content: [],
|
|
15
|
+
status:
|
|
16
|
+
status === "running"
|
|
17
|
+
? { type: "running" }
|
|
18
|
+
: { type: "complete", reason: "unknown" },
|
|
19
|
+
metadata: {
|
|
20
|
+
unstable_state: {},
|
|
21
|
+
unstable_annotations: [],
|
|
22
|
+
unstable_data: [],
|
|
23
|
+
steps: [],
|
|
24
|
+
custom: {},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const ThreadStatus = () => {
|
|
30
|
+
const isRunning = useAuiState((s) => s.thread.isRunning);
|
|
31
|
+
return <output data-testid="thread-running">{String(isRunning)}</output>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
afterEach(cleanup);
|
|
35
|
+
|
|
36
|
+
describe("ReadonlyThreadProvider", () => {
|
|
37
|
+
it("reports isRunning from the trailing assistant message", async () => {
|
|
38
|
+
const result = render(
|
|
39
|
+
<ReadonlyThreadProvider messages={messages("running")}>
|
|
40
|
+
<ThreadStatus />
|
|
41
|
+
</ReadonlyThreadProvider>,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
expect(screen.getByTestId("thread-running").textContent).toBe("true");
|
|
45
|
+
|
|
46
|
+
result.rerender(
|
|
47
|
+
<ReadonlyThreadProvider messages={messages("complete")}>
|
|
48
|
+
<ThreadStatus />
|
|
49
|
+
</ReadonlyThreadProvider>,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
await waitFor(() => {
|
|
53
|
+
expect(screen.getByTestId("thread-running").textContent).toBe("false");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -58,6 +58,7 @@ import { useAui } from "@assistant-ui/store";
|
|
|
58
58
|
import type { ModelContextProvider } from "../../model-context/types";
|
|
59
59
|
import { RuntimeAdapterProvider } from "./RuntimeAdapterProvider";
|
|
60
60
|
import { useStableRuntimeAdapters } from "./useRuntimeAdapters";
|
|
61
|
+
import { invokeUserCallback } from "../../utils/invoke-user-callback";
|
|
61
62
|
|
|
62
63
|
const threadNotFoundError = (threadIdOrRemoteId: string, action: string) =>
|
|
63
64
|
new Error(`Thread "${threadIdOrRemoteId}" not found while ${action}.`);
|
|
@@ -573,7 +574,12 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
573
574
|
if (this._lastNotifiedThreadId === threadId) return;
|
|
574
575
|
this._lastNotifiedThreadId = threadId;
|
|
575
576
|
if (emit) {
|
|
576
|
-
|
|
577
|
+
invokeUserCallback(
|
|
578
|
+
"assistant-ui",
|
|
579
|
+
"onThreadIdChange",
|
|
580
|
+
this._options.onThreadIdChange,
|
|
581
|
+
threadId,
|
|
582
|
+
);
|
|
577
583
|
}
|
|
578
584
|
}
|
|
579
585
|
|
|
@@ -28,8 +28,8 @@ describe("extractAuiV0", () => {
|
|
|
28
28
|
it("reports a terminal message once with its usage", () => {
|
|
29
29
|
const result = extractAuiV0(auiV0Message({ type: "complete" }));
|
|
30
30
|
expect(result?.status).toBe("completed");
|
|
31
|
-
expect(result?.inputTokens).toBe(1);
|
|
32
|
-
expect(result?.outputTokens).toBe(2);
|
|
31
|
+
expect(result?.usage?.inputTokens).toBe(1);
|
|
32
|
+
expect(result?.usage?.outputTokens).toBe(2);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
it("sums step usage reported under the AI SDK v7 token details", () => {
|
|
@@ -57,22 +57,28 @@ describe("extractAuiV0", () => {
|
|
|
57
57
|
},
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
expect(result?.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
expect(result?.usage).toEqual({
|
|
61
|
+
inputTokens: 15,
|
|
62
|
+
outputTokens: 6,
|
|
63
|
+
cachedInputTokens: 9,
|
|
64
|
+
reasoningTokens: 2,
|
|
65
|
+
});
|
|
64
66
|
expect(result?.steps).toEqual([
|
|
65
67
|
{
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
usage: {
|
|
69
|
+
inputTokens: 10,
|
|
70
|
+
outputTokens: 4,
|
|
71
|
+
inputTokenDetails: { cacheReadTokens: 6 },
|
|
72
|
+
outputTokenDetails: { reasoningTokens: 1 },
|
|
73
|
+
},
|
|
70
74
|
},
|
|
71
75
|
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
usage: {
|
|
77
|
+
inputTokens: 5,
|
|
78
|
+
outputTokens: 2,
|
|
79
|
+
inputTokenDetails: { cacheReadTokens: 3 },
|
|
80
|
+
outputTokenDetails: { reasoningTokens: 1 },
|
|
81
|
+
},
|
|
76
82
|
},
|
|
77
83
|
]);
|
|
78
84
|
});
|