@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
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { resource } from "@assistant-ui/tap";
|
|
2
|
+
import type { ClientOutput } from "@assistant-ui/store";
|
|
3
|
+
import type {
|
|
4
|
+
ThreadMessage,
|
|
5
|
+
ToolCallMessagePart,
|
|
6
|
+
ToolCallMessagePartStatus,
|
|
7
|
+
} from "../../types/message";
|
|
8
|
+
import {
|
|
9
|
+
COMPLETE_STATUS,
|
|
10
|
+
toMessagePartStatus,
|
|
11
|
+
} from "../../utils/normalizePartStatus";
|
|
12
|
+
import type { TaskState } from "../scopes/task";
|
|
13
|
+
|
|
14
|
+
type TaskEntry = {
|
|
15
|
+
readonly task: TaskState;
|
|
16
|
+
readonly part: ToolCallMessagePart;
|
|
17
|
+
readonly statusType: ToolCallMessagePartStatus["type"];
|
|
18
|
+
readonly statusReason: string | undefined;
|
|
19
|
+
readonly statusError: unknown;
|
|
20
|
+
readonly messages: readonly ThreadMessage[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getStatusReason = (status: ToolCallMessagePartStatus) =>
|
|
24
|
+
"reason" in status ? status.reason : undefined;
|
|
25
|
+
|
|
26
|
+
const getStatusError = (status: ToolCallMessagePartStatus) =>
|
|
27
|
+
"error" in status ? status.error : undefined;
|
|
28
|
+
|
|
29
|
+
const MAX_TASK_DEPTH = 32;
|
|
30
|
+
|
|
31
|
+
const taskKeys = new WeakMap<TaskState, string>();
|
|
32
|
+
|
|
33
|
+
/** Lookup key for a task client: its document-order index path, unique by construction where ids from nested payloads are not. */
|
|
34
|
+
export const getTaskKey = (task: TaskState) => taskKeys.get(task) ?? task.id;
|
|
35
|
+
|
|
36
|
+
const resolveStatus = (
|
|
37
|
+
message: ThreadMessage,
|
|
38
|
+
partIndex: number,
|
|
39
|
+
part: ToolCallMessagePart,
|
|
40
|
+
): ToolCallMessagePartStatus =>
|
|
41
|
+
"status" in message && message.status
|
|
42
|
+
? toMessagePartStatus(message, partIndex, part)
|
|
43
|
+
: COMPLETE_STATUS;
|
|
44
|
+
|
|
45
|
+
export const createTaskDeriver = () => {
|
|
46
|
+
let previous: readonly TaskState[] = [];
|
|
47
|
+
let previousEntries = new Map<string, TaskEntry>();
|
|
48
|
+
|
|
49
|
+
return (messages: readonly ThreadMessage[]): readonly TaskState[] => {
|
|
50
|
+
const tasks: TaskState[] = [];
|
|
51
|
+
const entries = new Map<string, TaskEntry>();
|
|
52
|
+
let allEntriesReused = true;
|
|
53
|
+
|
|
54
|
+
const visit = (
|
|
55
|
+
threadMessages: readonly ThreadMessage[],
|
|
56
|
+
parentTaskId: string | null,
|
|
57
|
+
depth: number,
|
|
58
|
+
path: string,
|
|
59
|
+
) => {
|
|
60
|
+
if (depth > MAX_TASK_DEPTH) return;
|
|
61
|
+
for (const [messageIndex, message] of threadMessages.entries()) {
|
|
62
|
+
for (const [partIndex, part] of message.content.entries()) {
|
|
63
|
+
if (part.type !== "tool-call" || part.messages === undefined)
|
|
64
|
+
continue;
|
|
65
|
+
|
|
66
|
+
const nestedMessages = part.messages;
|
|
67
|
+
const status = resolveStatus(message, partIndex, part);
|
|
68
|
+
const statusReason = getStatusReason(status);
|
|
69
|
+
const statusError = getStatusError(status);
|
|
70
|
+
const entryKey = `${path}${messageIndex}.${partIndex}`;
|
|
71
|
+
const previousEntry = previousEntries.get(entryKey);
|
|
72
|
+
const task =
|
|
73
|
+
previousEntry?.part === part &&
|
|
74
|
+
previousEntry.statusType === status.type &&
|
|
75
|
+
previousEntry.statusReason === statusReason &&
|
|
76
|
+
Object.is(previousEntry.statusError, statusError) &&
|
|
77
|
+
previousEntry.messages === nestedMessages &&
|
|
78
|
+
previousEntry.task.messageId === message.id &&
|
|
79
|
+
previousEntry.task.parentTaskId === parentTaskId &&
|
|
80
|
+
previousEntry.task.depth === depth
|
|
81
|
+
? previousEntry.task
|
|
82
|
+
: {
|
|
83
|
+
id: part.toolCallId,
|
|
84
|
+
toolName: part.toolName,
|
|
85
|
+
args: part.args,
|
|
86
|
+
result: part.result,
|
|
87
|
+
...(part.isError === undefined
|
|
88
|
+
? undefined
|
|
89
|
+
: { isError: part.isError }),
|
|
90
|
+
status,
|
|
91
|
+
timing: part.timing,
|
|
92
|
+
messageId: message.id,
|
|
93
|
+
parentTaskId,
|
|
94
|
+
depth,
|
|
95
|
+
messages: nestedMessages,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (task !== previousEntry?.task) {
|
|
99
|
+
allEntriesReused = false;
|
|
100
|
+
taskKeys.set(task, entryKey);
|
|
101
|
+
}
|
|
102
|
+
tasks.push(task);
|
|
103
|
+
entries.set(entryKey, {
|
|
104
|
+
task,
|
|
105
|
+
part,
|
|
106
|
+
statusType: status.type,
|
|
107
|
+
statusReason,
|
|
108
|
+
statusError,
|
|
109
|
+
messages: nestedMessages,
|
|
110
|
+
});
|
|
111
|
+
visit(nestedMessages, task.id, depth + 1, `${entryKey}.`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
visit(messages, null, 0, "");
|
|
117
|
+
|
|
118
|
+
const result =
|
|
119
|
+
allEntriesReused &&
|
|
120
|
+
tasks.length === previous.length &&
|
|
121
|
+
tasks.every((task, index) => task === previous[index])
|
|
122
|
+
? previous
|
|
123
|
+
: tasks;
|
|
124
|
+
previous = result;
|
|
125
|
+
previousEntries = entries;
|
|
126
|
+
return result;
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const useTaskClient = ({
|
|
131
|
+
task,
|
|
132
|
+
}: {
|
|
133
|
+
task: TaskState;
|
|
134
|
+
}): ClientOutput<"task"> => ({
|
|
135
|
+
getState: () => task,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
export const TaskClient = resource(useTaskClient);
|
package/src/store/index.ts
CHANGED
|
@@ -48,6 +48,12 @@ export type {
|
|
|
48
48
|
QueueItemMeta,
|
|
49
49
|
QueueItemClientSchema,
|
|
50
50
|
} from "./scopes/queue-item";
|
|
51
|
+
export type {
|
|
52
|
+
TaskState,
|
|
53
|
+
TaskMethods,
|
|
54
|
+
TaskMeta,
|
|
55
|
+
TaskClientSchema,
|
|
56
|
+
} from "./scopes/task";
|
|
51
57
|
export type {
|
|
52
58
|
AttachmentState,
|
|
53
59
|
AttachmentMethods,
|
|
@@ -96,6 +102,8 @@ export {
|
|
|
96
102
|
export {
|
|
97
103
|
useExternalMessageConverter,
|
|
98
104
|
convertExternalMessages,
|
|
105
|
+
createExternalMessageConversionCache,
|
|
106
|
+
type ExternalMessageConversionCache,
|
|
99
107
|
type JoinStrategy,
|
|
100
108
|
} from "../react/runtimes/external-message-converter";
|
|
101
109
|
export {
|
|
@@ -50,8 +50,12 @@ describe("primitive predicates", () => {
|
|
|
50
50
|
).toBe(true);
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
it("actionBarReloadDisabled rejects user messages and
|
|
54
|
-
const thread = {
|
|
53
|
+
it("actionBarReloadDisabled rejects user messages, busy threads, and runtimes without reload", () => {
|
|
54
|
+
const thread = {
|
|
55
|
+
isRunning: false,
|
|
56
|
+
isDisabled: false,
|
|
57
|
+
capabilities: { reload: true },
|
|
58
|
+
};
|
|
55
59
|
expect(
|
|
56
60
|
actionBarReloadDisabled(
|
|
57
61
|
state({ thread, message: { role: "assistant" } }),
|
|
@@ -68,6 +72,14 @@ describe("primitive predicates", () => {
|
|
|
68
72
|
}),
|
|
69
73
|
),
|
|
70
74
|
).toBe(true);
|
|
75
|
+
expect(
|
|
76
|
+
actionBarReloadDisabled(
|
|
77
|
+
state({
|
|
78
|
+
thread: { ...thread, capabilities: { reload: false } },
|
|
79
|
+
message: { role: "assistant" },
|
|
80
|
+
}),
|
|
81
|
+
),
|
|
82
|
+
).toBe(true);
|
|
71
83
|
});
|
|
72
84
|
|
|
73
85
|
it("actionBarCopyDisabled requires settled content with non-empty text", () => {
|
|
@@ -109,7 +121,7 @@ describe("primitive predicates", () => {
|
|
|
109
121
|
it("branch picker predicates respect bounds and run capabilities", () => {
|
|
110
122
|
const thread = {
|
|
111
123
|
isRunning: false,
|
|
112
|
-
capabilities: { switchBranchDuringRun: false },
|
|
124
|
+
capabilities: { switchToBranch: true, switchBranchDuringRun: false },
|
|
113
125
|
};
|
|
114
126
|
const message = { branchNumber: 2, branchCount: 3 };
|
|
115
127
|
expect(branchPickerPreviousDisabled(state({ thread, message }))).toBe(
|
|
@@ -131,6 +143,16 @@ describe("primitive predicates", () => {
|
|
|
131
143
|
state({ thread: { ...thread, isRunning: true }, message }),
|
|
132
144
|
),
|
|
133
145
|
).toBe(true);
|
|
146
|
+
const noSwitching = {
|
|
147
|
+
...thread,
|
|
148
|
+
capabilities: { ...thread.capabilities, switchToBranch: false },
|
|
149
|
+
};
|
|
150
|
+
expect(
|
|
151
|
+
branchPickerPreviousDisabled(state({ thread: noSwitching, message })),
|
|
152
|
+
).toBe(true);
|
|
153
|
+
expect(
|
|
154
|
+
branchPickerNextDisabled(state({ thread: noSwitching, message })),
|
|
155
|
+
).toBe(true);
|
|
134
156
|
});
|
|
135
157
|
|
|
136
158
|
it("suggestionTriggerDisabled gates on send only for queueless runs", () => {
|
|
@@ -174,11 +196,32 @@ describe("primitive predicates", () => {
|
|
|
174
196
|
),
|
|
175
197
|
).toBe(true);
|
|
176
198
|
|
|
199
|
+
const editable = { optional: { thread: { capabilities: { edit: true } } } };
|
|
177
200
|
expect(
|
|
178
|
-
actionBarEditDisabled(
|
|
201
|
+
actionBarEditDisabled(
|
|
202
|
+
state({ ...editable, composer: { isEditing: false } }),
|
|
203
|
+
),
|
|
179
204
|
).toBe(false);
|
|
180
205
|
expect(
|
|
181
|
-
actionBarEditDisabled(
|
|
206
|
+
actionBarEditDisabled(
|
|
207
|
+
state({ ...editable, composer: { isEditing: true } }),
|
|
208
|
+
),
|
|
209
|
+
).toBe(true);
|
|
210
|
+
expect(
|
|
211
|
+
actionBarEditDisabled(
|
|
212
|
+
state({
|
|
213
|
+
optional: { thread: { capabilities: { edit: false } } },
|
|
214
|
+
composer: { isEditing: false },
|
|
215
|
+
}),
|
|
216
|
+
),
|
|
182
217
|
).toBe(true);
|
|
218
|
+
expect(
|
|
219
|
+
actionBarEditDisabled(
|
|
220
|
+
state({
|
|
221
|
+
optional: { thread: undefined },
|
|
222
|
+
composer: { isEditing: false },
|
|
223
|
+
}),
|
|
224
|
+
),
|
|
225
|
+
).toBe(false);
|
|
183
226
|
});
|
|
184
227
|
});
|
|
@@ -20,10 +20,13 @@ export const composerInputDisabled = (s: AssistantState): boolean =>
|
|
|
20
20
|
s.thread.isDisabled || s.composer.dictation?.inputDisabled === true;
|
|
21
21
|
|
|
22
22
|
export const actionBarEditDisabled = (s: AssistantState): boolean =>
|
|
23
|
-
s.composer.isEditing;
|
|
23
|
+
s.composer.isEditing || s.optional.thread?.capabilities.edit === false;
|
|
24
24
|
|
|
25
25
|
export const actionBarReloadDisabled = (s: AssistantState): boolean =>
|
|
26
|
-
s.thread.isRunning ||
|
|
26
|
+
s.thread.isRunning ||
|
|
27
|
+
s.thread.isDisabled ||
|
|
28
|
+
s.message.role !== "assistant" ||
|
|
29
|
+
!s.thread.capabilities.reload;
|
|
27
30
|
|
|
28
31
|
export const actionBarCopyDisabled = (s: AssistantState): boolean =>
|
|
29
32
|
!(
|
|
@@ -33,10 +36,12 @@ export const actionBarCopyDisabled = (s: AssistantState): boolean =>
|
|
|
33
36
|
|
|
34
37
|
export const branchPickerPreviousDisabled = (s: AssistantState): boolean =>
|
|
35
38
|
s.message.branchNumber <= 1 ||
|
|
39
|
+
!s.thread.capabilities.switchToBranch ||
|
|
36
40
|
(s.thread.isRunning && !s.thread.capabilities.switchBranchDuringRun);
|
|
37
41
|
|
|
38
42
|
export const branchPickerNextDisabled = (s: AssistantState): boolean =>
|
|
39
43
|
s.message.branchNumber >= s.message.branchCount ||
|
|
44
|
+
!s.thread.capabilities.switchToBranch ||
|
|
40
45
|
(s.thread.isRunning && !s.thread.capabilities.switchBranchDuringRun);
|
|
41
46
|
|
|
42
47
|
export const suggestionTriggerDisabled = (
|
|
@@ -16,6 +16,11 @@ import {
|
|
|
16
16
|
import { ComposerClient } from "./composer-runtime-client";
|
|
17
17
|
import { MessageClient } from "./message-runtime-client";
|
|
18
18
|
import { ThreadSuggestions } from "../clients/suggestions";
|
|
19
|
+
import {
|
|
20
|
+
createTaskDeriver,
|
|
21
|
+
getTaskKey,
|
|
22
|
+
TaskClient,
|
|
23
|
+
} from "../clients/thread-tasks";
|
|
19
24
|
import { useSubscribable } from "./useSubscribable";
|
|
20
25
|
import type { ThreadState } from "../scopes/thread";
|
|
21
26
|
|
|
@@ -70,6 +75,13 @@ const useThreadClient = ({
|
|
|
70
75
|
unsubscribers.push(unsubscribe);
|
|
71
76
|
}
|
|
72
77
|
|
|
78
|
+
unsubscribers.push(
|
|
79
|
+
runtime.unstable_on("toolApprovalAnswered", (payload) => {
|
|
80
|
+
const threadId = runtime.getState()?.threadId || "unknown";
|
|
81
|
+
emit("thread.toolApprovalAnswered", { threadId, ...payload });
|
|
82
|
+
}),
|
|
83
|
+
);
|
|
84
|
+
|
|
73
85
|
return () => {
|
|
74
86
|
for (const unsub of unsubscribers) unsub();
|
|
75
87
|
};
|
|
@@ -102,6 +114,16 @@ const useThreadClient = ({
|
|
|
102
114
|
const suggestions = useClientResource(
|
|
103
115
|
ThreadSuggestions(runtimeState.suggestions),
|
|
104
116
|
);
|
|
117
|
+
const taskDeriver = useMemo(() => createTaskDeriver(), []);
|
|
118
|
+
const tasks = useMemo(
|
|
119
|
+
() => taskDeriver(runtimeState.messages),
|
|
120
|
+
[taskDeriver, runtimeState.messages],
|
|
121
|
+
);
|
|
122
|
+
const taskClients = useClientLookup(
|
|
123
|
+
tasks.map((task) =>
|
|
124
|
+
withKey(getTaskKey(task), TaskClient({ task }), [task]),
|
|
125
|
+
),
|
|
126
|
+
);
|
|
105
127
|
const messages = useClientLookup(
|
|
106
128
|
runtimeState.messages.map((m) =>
|
|
107
129
|
withKey(
|
|
@@ -132,13 +154,21 @@ const useThreadClient = ({
|
|
|
132
154
|
|
|
133
155
|
composer: composer.state,
|
|
134
156
|
messages: messages.state,
|
|
157
|
+
tasks,
|
|
135
158
|
};
|
|
136
|
-
}, [runtimeState, messages, composer.state]);
|
|
159
|
+
}, [runtimeState, messages, composer.state, tasks]);
|
|
137
160
|
|
|
138
161
|
return {
|
|
139
162
|
getState: () => state,
|
|
140
163
|
composer: () => composer.methods,
|
|
141
164
|
suggestions: () => suggestions.methods,
|
|
165
|
+
task: (selector) => {
|
|
166
|
+
if ("id" in selector) {
|
|
167
|
+
const task = tasks.find((candidate) => candidate.id === selector.id);
|
|
168
|
+
return taskClients.get({ key: task ? getTaskKey(task) : selector.id });
|
|
169
|
+
}
|
|
170
|
+
return taskClients.get(selector);
|
|
171
|
+
},
|
|
142
172
|
append: (message) => {
|
|
143
173
|
const appended: Exclude<CreateAppendMessage, string> =
|
|
144
174
|
typeof message === "string"
|
|
@@ -10,6 +10,7 @@ import type { SuggestionsClientSchema } from "./scopes/suggestions";
|
|
|
10
10
|
import type { SuggestionClientSchema } from "./scopes/suggestion";
|
|
11
11
|
import type { ChainOfThoughtClientSchema } from "./scopes/chain-of-thought";
|
|
12
12
|
import type { QueueItemClientSchema } from "./scopes/queue-item";
|
|
13
|
+
import type { TaskClientSchema } from "./scopes/task";
|
|
13
14
|
|
|
14
15
|
declare module "@assistant-ui/store" {
|
|
15
16
|
interface ScopeRegistry {
|
|
@@ -25,5 +26,6 @@ declare module "@assistant-ui/store" {
|
|
|
25
26
|
suggestion: SuggestionClientSchema;
|
|
26
27
|
chainOfThought: ChainOfThoughtClientSchema;
|
|
27
28
|
queueItem: QueueItemClientSchema;
|
|
29
|
+
task: TaskClientSchema;
|
|
28
30
|
}
|
|
29
31
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ThreadMessage,
|
|
3
|
+
ToolCallMessagePart,
|
|
4
|
+
ToolCallMessagePartStatus,
|
|
5
|
+
} from "../../types/message";
|
|
6
|
+
|
|
7
|
+
/** A tool call that carries a nested conversation, read as one unit of child work; `id` is its toolCallId. */
|
|
8
|
+
export type TaskState = {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly toolName: string;
|
|
11
|
+
readonly args: ToolCallMessagePart["args"];
|
|
12
|
+
readonly result?: ToolCallMessagePart["result"];
|
|
13
|
+
readonly isError?: boolean;
|
|
14
|
+
readonly status: ToolCallMessagePartStatus;
|
|
15
|
+
readonly timing?: ToolCallMessagePart["timing"];
|
|
16
|
+
readonly messageId: string;
|
|
17
|
+
readonly parentTaskId: string | null;
|
|
18
|
+
readonly depth: number;
|
|
19
|
+
readonly messages: readonly ThreadMessage[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type TaskMethods = {
|
|
23
|
+
getState(): TaskState;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type TaskMeta = {
|
|
27
|
+
source: "thread";
|
|
28
|
+
query: { type: "index"; index: number } | { type: "id"; id: string };
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type TaskClientSchema = {
|
|
32
|
+
methods: TaskMethods;
|
|
33
|
+
meta: TaskMeta;
|
|
34
|
+
};
|
|
@@ -18,6 +18,7 @@ import type { ModelContext } from "../../model-context/types";
|
|
|
18
18
|
import type { MessageMethods, MessageState } from "./message";
|
|
19
19
|
import type { ComposerMethods, ComposerState } from "./composer";
|
|
20
20
|
import type { SuggestionsMethods } from "./suggestions";
|
|
21
|
+
import type { TaskMethods, TaskState } from "./task";
|
|
21
22
|
|
|
22
23
|
export type ThreadState = {
|
|
23
24
|
/**
|
|
@@ -44,6 +45,10 @@ export type ThreadState = {
|
|
|
44
45
|
* The messages in the currently selected branch of the thread.
|
|
45
46
|
*/
|
|
46
47
|
readonly messages: readonly MessageState[];
|
|
48
|
+
/**
|
|
49
|
+
* Child work derived from the thread's tool calls: every tool call that carries a nested conversation, in document order with nested tasks after their parent. The array keeps its identity while no task changed.
|
|
50
|
+
*/
|
|
51
|
+
readonly tasks: readonly TaskState[];
|
|
47
52
|
/**
|
|
48
53
|
* The thread state.
|
|
49
54
|
* @deprecated This feature is experimental
|
|
@@ -76,6 +81,10 @@ export type ThreadMethods = {
|
|
|
76
81
|
* The suggestions shown for this thread.
|
|
77
82
|
*/
|
|
78
83
|
suggestions(): SuggestionsMethods;
|
|
84
|
+
/**
|
|
85
|
+
* Access a task by index or toolCallId; an id resolves the first task with that toolCallId in document order.
|
|
86
|
+
*/
|
|
87
|
+
task(selector: { index: number } | { id: string }): TaskMethods;
|
|
79
88
|
/**
|
|
80
89
|
* Append a new message to the thread.
|
|
81
90
|
*
|
|
@@ -144,6 +153,13 @@ export type ThreadMeta = {
|
|
|
144
153
|
};
|
|
145
154
|
|
|
146
155
|
export type ThreadEvents = {
|
|
156
|
+
"thread.toolApprovalAnswered": {
|
|
157
|
+
threadId: string;
|
|
158
|
+
messageId: string;
|
|
159
|
+
toolCallId: string;
|
|
160
|
+
toolName: string;
|
|
161
|
+
approved: boolean;
|
|
162
|
+
};
|
|
147
163
|
/**
|
|
148
164
|
* A run started on this thread. Also observable as `isRunning` flipping to
|
|
149
165
|
* `true` in thread state.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { cleanup, render, waitFor } from "@testing-library/react";
|
|
4
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
5
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
6
|
+
import type { ThreadMessage } from "../types/message";
|
|
7
|
+
import { AssistantRuntimeProvider } from "../react/AssistantRuntimeProvider";
|
|
8
|
+
import { useExternalStoreRuntime } from "../react/runtimes/useExternalStoreRuntime";
|
|
9
|
+
import type { TaskState } from "./scopes/task";
|
|
10
|
+
|
|
11
|
+
const taskMessage = (
|
|
12
|
+
nestedStatus: { type: "running" } | { type: "complete"; reason: "stop" },
|
|
13
|
+
result?: unknown,
|
|
14
|
+
) =>
|
|
15
|
+
({
|
|
16
|
+
id: "message-1",
|
|
17
|
+
role: "assistant",
|
|
18
|
+
createdAt: new Date(0),
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: "tool-call",
|
|
22
|
+
toolCallId: "delegate-1",
|
|
23
|
+
toolName: "delegate",
|
|
24
|
+
args: { topic: "research" },
|
|
25
|
+
argsText: '{"topic":"research"}',
|
|
26
|
+
...(result !== undefined ? { result } : undefined),
|
|
27
|
+
messages: [
|
|
28
|
+
{
|
|
29
|
+
id: "nested-message-1",
|
|
30
|
+
role: "assistant",
|
|
31
|
+
createdAt: new Date(0),
|
|
32
|
+
content: [],
|
|
33
|
+
status: nestedStatus,
|
|
34
|
+
metadata: {
|
|
35
|
+
unstable_state: {},
|
|
36
|
+
unstable_annotations: [],
|
|
37
|
+
unstable_data: [],
|
|
38
|
+
steps: [],
|
|
39
|
+
custom: {},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
status: nestedStatus,
|
|
46
|
+
metadata: {
|
|
47
|
+
unstable_state: {},
|
|
48
|
+
unstable_annotations: [],
|
|
49
|
+
unstable_data: [],
|
|
50
|
+
steps: [],
|
|
51
|
+
custom: {},
|
|
52
|
+
},
|
|
53
|
+
}) as ThreadMessage;
|
|
54
|
+
|
|
55
|
+
afterEach(cleanup);
|
|
56
|
+
|
|
57
|
+
describe("thread tasks", () => {
|
|
58
|
+
it("exposes task state and accessors through an external-store runtime", async () => {
|
|
59
|
+
const captured: {
|
|
60
|
+
tasks?: readonly TaskState[];
|
|
61
|
+
byId?: TaskState;
|
|
62
|
+
byIndex?: TaskState;
|
|
63
|
+
} = {};
|
|
64
|
+
|
|
65
|
+
const Probe = () => {
|
|
66
|
+
const tasks = useAuiState((s) => s.thread.tasks);
|
|
67
|
+
const aui = useAui();
|
|
68
|
+
captured.tasks = tasks;
|
|
69
|
+
if (tasks.length > 0) {
|
|
70
|
+
captured.byId = aui.thread.task({ id: "delegate-1" }).getState();
|
|
71
|
+
captured.byIndex = aui.thread.task({ index: 0 }).getState();
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const App = ({ messages }: { messages: readonly ThreadMessage[] }) => {
|
|
77
|
+
const runtime = useExternalStoreRuntime({
|
|
78
|
+
messages,
|
|
79
|
+
convertMessage: (message) => message,
|
|
80
|
+
onNew: async () => {},
|
|
81
|
+
});
|
|
82
|
+
return (
|
|
83
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
84
|
+
<Probe />
|
|
85
|
+
</AssistantRuntimeProvider>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const view = render(<App messages={[taskMessage({ type: "running" })]} />);
|
|
90
|
+
|
|
91
|
+
await waitFor(() => expect(captured.tasks).toHaveLength(1));
|
|
92
|
+
expect(captured.tasks?.[0]).toMatchObject({
|
|
93
|
+
status: { type: "running" },
|
|
94
|
+
toolName: "delegate",
|
|
95
|
+
});
|
|
96
|
+
expect(captured.byId).toMatchObject({ messageId: "message-1" });
|
|
97
|
+
expect(captured.byIndex).toBe(captured.byId);
|
|
98
|
+
|
|
99
|
+
view.rerender(
|
|
100
|
+
<App
|
|
101
|
+
messages={[taskMessage({ type: "complete", reason: "stop" }, "done")]}
|
|
102
|
+
/>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
await waitFor(() =>
|
|
106
|
+
expect(captured.tasks?.[0]?.status).toEqual({ type: "complete" }),
|
|
107
|
+
);
|
|
108
|
+
expect(captured.tasks?.[0]).toMatchObject({
|
|
109
|
+
toolName: "delegate",
|
|
110
|
+
messageId: "message-1",
|
|
111
|
+
result: "done",
|
|
112
|
+
});
|
|
113
|
+
expect(captured.tasks?.[0]?.messages).toHaveLength(1);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("survives nested payloads that repeat ids and resolves the first task in document order", async () => {
|
|
117
|
+
const nested = (id: string, toolCallId: string) =>
|
|
118
|
+
({
|
|
119
|
+
id,
|
|
120
|
+
role: "assistant",
|
|
121
|
+
createdAt: new Date(0),
|
|
122
|
+
content: [
|
|
123
|
+
{
|
|
124
|
+
type: "tool-call",
|
|
125
|
+
toolCallId,
|
|
126
|
+
toolName: "search",
|
|
127
|
+
args: {},
|
|
128
|
+
argsText: "{}",
|
|
129
|
+
result: "ok",
|
|
130
|
+
messages: [],
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
status: { type: "complete", reason: "stop" },
|
|
134
|
+
metadata: {
|
|
135
|
+
unstable_state: {},
|
|
136
|
+
unstable_annotations: [],
|
|
137
|
+
unstable_data: [],
|
|
138
|
+
steps: [],
|
|
139
|
+
custom: {},
|
|
140
|
+
},
|
|
141
|
+
}) as ThreadMessage;
|
|
142
|
+
const message = {
|
|
143
|
+
id: "message-1",
|
|
144
|
+
role: "assistant",
|
|
145
|
+
createdAt: new Date(0),
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: "tool-call",
|
|
149
|
+
toolCallId: "delegate-a",
|
|
150
|
+
toolName: "delegate",
|
|
151
|
+
args: {},
|
|
152
|
+
argsText: "{}",
|
|
153
|
+
result: "ok",
|
|
154
|
+
messages: [nested("nested", "call_1")],
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
type: "tool-call",
|
|
158
|
+
toolCallId: "delegate-b",
|
|
159
|
+
toolName: "delegate",
|
|
160
|
+
args: {},
|
|
161
|
+
argsText: "{}",
|
|
162
|
+
result: "ok",
|
|
163
|
+
messages: [nested("nested", "call_1")],
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
status: { type: "complete", reason: "stop" },
|
|
167
|
+
metadata: {
|
|
168
|
+
unstable_state: {},
|
|
169
|
+
unstable_annotations: [],
|
|
170
|
+
unstable_data: [],
|
|
171
|
+
steps: [],
|
|
172
|
+
custom: {},
|
|
173
|
+
},
|
|
174
|
+
} as ThreadMessage;
|
|
175
|
+
const captured: { ids?: readonly string[]; first?: TaskState } = {};
|
|
176
|
+
const Probe = () => {
|
|
177
|
+
const tasks = useAuiState((s) => s.thread.tasks);
|
|
178
|
+
const aui = useAui();
|
|
179
|
+
captured.ids = tasks.map((task) => task.id);
|
|
180
|
+
if (tasks.length === 4) {
|
|
181
|
+
captured.first = aui.thread.task({ id: "call_1" }).getState();
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
};
|
|
185
|
+
const App = () => {
|
|
186
|
+
const runtime = useExternalStoreRuntime({
|
|
187
|
+
messages: [message],
|
|
188
|
+
convertMessage: (m) => m,
|
|
189
|
+
onNew: async () => {},
|
|
190
|
+
});
|
|
191
|
+
return (
|
|
192
|
+
<AssistantRuntimeProvider runtime={runtime}>
|
|
193
|
+
<Probe />
|
|
194
|
+
</AssistantRuntimeProvider>
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
render(<App />);
|
|
199
|
+
|
|
200
|
+
await waitFor(() => expect(captured.ids).toHaveLength(4));
|
|
201
|
+
expect(captured.ids).toEqual([
|
|
202
|
+
"delegate-a",
|
|
203
|
+
"call_1",
|
|
204
|
+
"delegate-b",
|
|
205
|
+
"call_1",
|
|
206
|
+
]);
|
|
207
|
+
expect(captured.first).toMatchObject({ parentTaskId: "delegate-a" });
|
|
208
|
+
});
|
|
209
|
+
});
|