@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
|
@@ -10,6 +10,15 @@ type ThreadMessageLikeContentItem = Exclude<
|
|
|
10
10
|
export const isPendingToolCall = (c: ThreadMessageLikeContentItem): boolean =>
|
|
11
11
|
c.type === "tool-call" && c.result === undefined;
|
|
12
12
|
|
|
13
|
+
/** A pending tool call whose nested conversation is still streaming; the outer message stays running until the nested run settles. */
|
|
14
|
+
export const isBackgroundToolCall = (
|
|
15
|
+
c: ThreadMessageLikeContentItem,
|
|
16
|
+
): boolean => {
|
|
17
|
+
if (c.type !== "tool-call" || c.result !== undefined) return false;
|
|
18
|
+
const last = c.messages?.at(-1);
|
|
19
|
+
return last?.role === "assistant" && last.status.type === "running";
|
|
20
|
+
};
|
|
21
|
+
|
|
13
22
|
export const isInterruptedToolCall = (
|
|
14
23
|
c: ThreadMessageLikeContentItem,
|
|
15
24
|
): boolean => {
|
|
@@ -76,6 +85,7 @@ export const getAutoStatus = (
|
|
|
76
85
|
hasPendingToolCalls: boolean,
|
|
77
86
|
error?: ReadonlyJSONValue,
|
|
78
87
|
isCancelled?: boolean,
|
|
88
|
+
hasBackgroundToolCalls?: boolean,
|
|
79
89
|
): MessageStatus => {
|
|
80
90
|
if (isLast && error) {
|
|
81
91
|
return Object.assign(
|
|
@@ -92,13 +102,26 @@ export const getAutoStatus = (
|
|
|
92
102
|
? AUTO_STATUS_RUNNING
|
|
93
103
|
: hasInterruptedToolCalls
|
|
94
104
|
? AUTO_STATUS_INTERRUPT
|
|
95
|
-
:
|
|
96
|
-
?
|
|
97
|
-
:
|
|
98
|
-
?
|
|
99
|
-
:
|
|
105
|
+
: hasBackgroundToolCalls && !isCancelled
|
|
106
|
+
? AUTO_STATUS_RUNNING
|
|
107
|
+
: hasPendingToolCalls
|
|
108
|
+
? AUTO_STATUS_PENDING
|
|
109
|
+
: isCancelled
|
|
110
|
+
? AUTO_STATUS_CANCELLED
|
|
111
|
+
: AUTO_STATUS_COMPLETE;
|
|
100
112
|
};
|
|
101
113
|
|
|
114
|
+
/** Status for a message entering the repository: the nested conversation is not being fed here, so it never keeps the message running. */
|
|
115
|
+
export const getRepositoryContentAutoStatus = (
|
|
116
|
+
content: ThreadMessageLike["content"],
|
|
117
|
+
): MessageStatus =>
|
|
118
|
+
getAutoStatus(
|
|
119
|
+
false,
|
|
120
|
+
false,
|
|
121
|
+
typeof content !== "string" && content.some(isInterruptedToolCall),
|
|
122
|
+
typeof content !== "string" && content.some(isPendingToolCall),
|
|
123
|
+
);
|
|
124
|
+
|
|
102
125
|
export const getContentAutoStatus = (
|
|
103
126
|
content: ThreadMessageLike["content"],
|
|
104
127
|
isLast: boolean,
|
|
@@ -109,4 +132,7 @@ export const getContentAutoStatus = (
|
|
|
109
132
|
isRunning,
|
|
110
133
|
typeof content !== "string" && content.some(isInterruptedToolCall),
|
|
111
134
|
typeof content !== "string" && content.some(isPendingToolCall),
|
|
135
|
+
undefined,
|
|
136
|
+
undefined,
|
|
137
|
+
typeof content !== "string" && content.some(isBackgroundToolCall),
|
|
112
138
|
);
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import type { ThreadMessage } from "../../types/message";
|
|
2
3
|
import {
|
|
3
4
|
chunkExternalMessages,
|
|
4
5
|
convertExternalMessageCallback,
|
|
5
6
|
convertExternalMessageChunk,
|
|
7
|
+
joinExternalMessages,
|
|
6
8
|
type ExternalMessageConverterCallback,
|
|
7
9
|
type ExternalMessageConverterCallbackResult,
|
|
10
|
+
type ExternalMessageConverterMessage,
|
|
8
11
|
} from "./external-message-conversion";
|
|
9
12
|
|
|
10
13
|
describe("convertExternalMessageCallback", () => {
|
|
@@ -30,6 +33,75 @@ describe("convertExternalMessageCallback", () => {
|
|
|
30
33
|
});
|
|
31
34
|
});
|
|
32
35
|
|
|
36
|
+
describe("joinExternalMessages", () => {
|
|
37
|
+
it("preserves strict equality for malformed numeric tool-call IDs", () => {
|
|
38
|
+
const messages = [
|
|
39
|
+
{
|
|
40
|
+
role: "assistant",
|
|
41
|
+
content: [
|
|
42
|
+
{
|
|
43
|
+
type: "tool-call",
|
|
44
|
+
toolCallId: 1,
|
|
45
|
+
toolName: "search",
|
|
46
|
+
args: { query: "old" },
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
role: "assistant",
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: "tool-call",
|
|
55
|
+
toolCallId: 1,
|
|
56
|
+
toolName: "search",
|
|
57
|
+
args: { query: "new" },
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
] as unknown as ExternalMessageConverterMessage[];
|
|
62
|
+
|
|
63
|
+
expect(joinExternalMessages(messages).content).toMatchObject([
|
|
64
|
+
{
|
|
65
|
+
type: "tool-call",
|
|
66
|
+
toolCallId: 1,
|
|
67
|
+
args: { query: "new" },
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("does not merge malformed NaN tool-call IDs", () => {
|
|
73
|
+
const messages = [
|
|
74
|
+
{
|
|
75
|
+
role: "assistant",
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "tool-call",
|
|
79
|
+
toolCallId: Number.NaN,
|
|
80
|
+
toolName: "search",
|
|
81
|
+
args: { query: "first" },
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: "tool-call",
|
|
85
|
+
toolCallId: Number.NaN,
|
|
86
|
+
toolName: "search",
|
|
87
|
+
args: { query: "second" },
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
{ role: "tool", toolCallId: Number.NaN, result: "found" },
|
|
92
|
+
] as unknown as ExternalMessageConverterMessage[];
|
|
93
|
+
|
|
94
|
+
const result = joinExternalMessages(messages);
|
|
95
|
+
|
|
96
|
+
expect(result.content).toMatchObject([
|
|
97
|
+
{ type: "tool-call", args: { query: "first" } },
|
|
98
|
+
{ type: "tool-call", args: { query: "second" } },
|
|
99
|
+
]);
|
|
100
|
+
expect(result.content[0]).not.toHaveProperty("result");
|
|
101
|
+
expect(result.content[1]).not.toHaveProperty("result");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
33
105
|
describe("chunkExternalMessages", () => {
|
|
34
106
|
it("keeps a tool result with a non-joining assistant before starting the next chunk", () => {
|
|
35
107
|
const toolCall = {};
|
|
@@ -83,6 +155,68 @@ describe("chunkExternalMessages", () => {
|
|
|
83
155
|
});
|
|
84
156
|
|
|
85
157
|
describe("convertExternalMessageChunk", () => {
|
|
158
|
+
it("keeps an ended outer run running for a background tool call", () => {
|
|
159
|
+
const nestedAssistant: ThreadMessage = {
|
|
160
|
+
id: "nested-assistant",
|
|
161
|
+
createdAt: new Date(0),
|
|
162
|
+
role: "assistant",
|
|
163
|
+
content: [],
|
|
164
|
+
status: { type: "running" },
|
|
165
|
+
metadata: {
|
|
166
|
+
unstable_state: {},
|
|
167
|
+
unstable_annotations: [],
|
|
168
|
+
unstable_data: [],
|
|
169
|
+
steps: [],
|
|
170
|
+
custom: {},
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
const result = convertExternalMessageChunk(
|
|
174
|
+
{
|
|
175
|
+
inputs: [{}],
|
|
176
|
+
outputs: [
|
|
177
|
+
{
|
|
178
|
+
role: "assistant",
|
|
179
|
+
content: [
|
|
180
|
+
{
|
|
181
|
+
type: "tool-call",
|
|
182
|
+
toolName: "delegate",
|
|
183
|
+
args: {},
|
|
184
|
+
messages: [nestedAssistant],
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
0,
|
|
191
|
+
1,
|
|
192
|
+
false,
|
|
193
|
+
undefined,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
expect(result.status).toMatchObject({ type: "running" });
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("preserves assistant message modality", () => {
|
|
200
|
+
const result = convertExternalMessageChunk(
|
|
201
|
+
{
|
|
202
|
+
inputs: [{}],
|
|
203
|
+
outputs: [
|
|
204
|
+
{
|
|
205
|
+
role: "assistant",
|
|
206
|
+
content: "Spoken reply",
|
|
207
|
+
metadata: { modality: "voice" },
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
0,
|
|
212
|
+
1,
|
|
213
|
+
false,
|
|
214
|
+
undefined,
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
expect(result.metadata.modality).toBe("voice");
|
|
218
|
+
});
|
|
219
|
+
|
|
86
220
|
it("keeps separate tool calls without IDs", () => {
|
|
87
221
|
const result = convertExternalMessageChunk(
|
|
88
222
|
{
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
2
2
|
import type { ToolExecutionStatus } from "../../runtimes/tool-invocations/ToolInvocationTracker";
|
|
3
|
+
import { ThreadMessageConverter } from "../../runtimes/external-store/thread-message-converter";
|
|
3
4
|
import type {
|
|
4
5
|
ThreadAssistantMessage,
|
|
5
6
|
ThreadMessage,
|
|
@@ -11,6 +12,7 @@ import { isJSONValueEqual } from "../../utils/json/is-json-equal";
|
|
|
11
12
|
import {
|
|
12
13
|
getAutoStatus,
|
|
13
14
|
isAutoStatus,
|
|
15
|
+
isBackgroundToolCall,
|
|
14
16
|
isInterruptedToolCall,
|
|
15
17
|
isPendingToolCall,
|
|
16
18
|
} from "./auto-status";
|
|
@@ -132,6 +134,9 @@ const mergeInnerMessages = (existing: object, incoming: object) => ({
|
|
|
132
134
|
],
|
|
133
135
|
});
|
|
134
136
|
|
|
137
|
+
const isNaNToolCallId = (toolCallId: unknown) =>
|
|
138
|
+
typeof toolCallId === "number" && Number.isNaN(toolCallId);
|
|
139
|
+
|
|
135
140
|
export const joinExternalMessages = (
|
|
136
141
|
messages: readonly ExternalMessageConverterMessage[],
|
|
137
142
|
): ThreadMessageLike => {
|
|
@@ -142,11 +147,13 @@ export const joinExternalMessages = (
|
|
|
142
147
|
role: "assistant",
|
|
143
148
|
content: [],
|
|
144
149
|
};
|
|
150
|
+
const toolCallIndices = new Map<unknown, number>();
|
|
151
|
+
const reasoningIndices = new Map<string, number>();
|
|
145
152
|
for (const output of messages) {
|
|
146
153
|
if (output.role === "tool") {
|
|
147
|
-
const toolCallIdx =
|
|
148
|
-
(
|
|
149
|
-
|
|
154
|
+
const toolCallIdx = !isNaNToolCallId(output.toolCallId)
|
|
155
|
+
? (toolCallIndices.get(output.toolCallId) ?? -1)
|
|
156
|
+
: -1;
|
|
150
157
|
// Ignore orphaned tool results so one bad tool message does not
|
|
151
158
|
// prevent rendering the rest of the conversation.
|
|
152
159
|
if (toolCallIdx !== -1) {
|
|
@@ -239,6 +246,10 @@ export const joinExternalMessages = (
|
|
|
239
246
|
assistantMessage.metadata.timing = output.metadata.timing;
|
|
240
247
|
}
|
|
241
248
|
|
|
249
|
+
if (output.metadata.modality) {
|
|
250
|
+
assistantMessage.metadata.modality = output.metadata.modality;
|
|
251
|
+
}
|
|
252
|
+
|
|
242
253
|
if (output.metadata.submittedFeedback) {
|
|
243
254
|
assistantMessage.metadata.submittedFeedback =
|
|
244
255
|
output.metadata.submittedFeedback;
|
|
@@ -253,11 +264,8 @@ export const joinExternalMessages = (
|
|
|
253
264
|
// Add content parts, merging reasoning parts with same parentId
|
|
254
265
|
for (const part of content) {
|
|
255
266
|
if (part.type === "tool-call" && part.toolCallId) {
|
|
256
|
-
const existingIdx =
|
|
257
|
-
|
|
258
|
-
c.type === "tool-call" && c.toolCallId === part.toolCallId,
|
|
259
|
-
);
|
|
260
|
-
if (existingIdx !== -1) {
|
|
267
|
+
const existingIdx = toolCallIndices.get(part.toolCallId);
|
|
268
|
+
if (existingIdx !== undefined) {
|
|
261
269
|
const existing = assistantMessage.content[
|
|
262
270
|
existingIdx
|
|
263
271
|
] as typeof part;
|
|
@@ -275,13 +283,8 @@ export const joinExternalMessages = (
|
|
|
275
283
|
"parentId" in part &&
|
|
276
284
|
part.parentId
|
|
277
285
|
) {
|
|
278
|
-
const existingIdx =
|
|
279
|
-
|
|
280
|
-
c.type === "reasoning" &&
|
|
281
|
-
"parentId" in c &&
|
|
282
|
-
c.parentId === part.parentId,
|
|
283
|
-
);
|
|
284
|
-
if (existingIdx !== -1) {
|
|
286
|
+
const existingIdx = reasoningIndices.get(part.parentId);
|
|
287
|
+
if (existingIdx !== undefined) {
|
|
285
288
|
const existing = assistantMessage.content[
|
|
286
289
|
existingIdx
|
|
287
290
|
] as typeof part;
|
|
@@ -293,7 +296,21 @@ export const joinExternalMessages = (
|
|
|
293
296
|
continue;
|
|
294
297
|
}
|
|
295
298
|
}
|
|
299
|
+
const partIdx = assistantMessage.content.length;
|
|
296
300
|
assistantMessage.content.push(part);
|
|
301
|
+
if (
|
|
302
|
+
part.type === "tool-call" &&
|
|
303
|
+
!isNaNToolCallId(part.toolCallId) &&
|
|
304
|
+
!toolCallIndices.has(part.toolCallId)
|
|
305
|
+
) {
|
|
306
|
+
toolCallIndices.set(part.toolCallId, partIdx);
|
|
307
|
+
} else if (
|
|
308
|
+
part.type === "reasoning" &&
|
|
309
|
+
"parentId" in part &&
|
|
310
|
+
part.parentId
|
|
311
|
+
) {
|
|
312
|
+
reasoningIndices.set(part.parentId, partIdx);
|
|
313
|
+
}
|
|
297
314
|
}
|
|
298
315
|
break;
|
|
299
316
|
default: {
|
|
@@ -370,18 +387,46 @@ export const shallowArrayEqual = (
|
|
|
370
387
|
return true;
|
|
371
388
|
};
|
|
372
389
|
|
|
373
|
-
type
|
|
390
|
+
type ExternalMessageChunkConversionCache = {
|
|
374
391
|
message: ThreadMessage | undefined;
|
|
375
392
|
generatedFallbackMessages: WeakSet<object>;
|
|
376
393
|
};
|
|
377
394
|
|
|
395
|
+
type ExternalMessageConversionCallbackCacheEntry<T> =
|
|
396
|
+
ExternalMessageConverterCallbackResult<T> & {
|
|
397
|
+
metadata: ExternalMessageConverterMetadata;
|
|
398
|
+
callback: ExternalMessageConverterCallback<T>;
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
export type InternalExternalMessageConversionCache<
|
|
402
|
+
T extends WeakKey = WeakKey,
|
|
403
|
+
> = {
|
|
404
|
+
callbackCache: WeakMap<T, ExternalMessageConversionCallbackCacheEntry<T>>;
|
|
405
|
+
chunkCache: WeakMap<
|
|
406
|
+
ExternalMessageConverterMessage,
|
|
407
|
+
ExternalMessageConverterChunk<T>
|
|
408
|
+
>;
|
|
409
|
+
converterCache: ThreadMessageConverter;
|
|
410
|
+
// Generatedness is tracked by identity, not by id shape: a caller-supplied id that happens to match the generated pattern must never be rewritten.
|
|
411
|
+
generatedFallbackMessages: WeakSet<object>;
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
export const createExternalMessageConversionCache = <
|
|
415
|
+
T extends WeakKey = WeakKey,
|
|
416
|
+
>(): InternalExternalMessageConversionCache<T> => ({
|
|
417
|
+
callbackCache: new WeakMap(),
|
|
418
|
+
chunkCache: new WeakMap(),
|
|
419
|
+
converterCache: new ThreadMessageConverter(),
|
|
420
|
+
generatedFallbackMessages: new WeakSet(),
|
|
421
|
+
});
|
|
422
|
+
|
|
378
423
|
export const convertExternalMessageChunk = <T>(
|
|
379
424
|
message: ExternalMessageConverterChunk<T>,
|
|
380
425
|
idx: number,
|
|
381
426
|
chunkCount: number,
|
|
382
427
|
isRunning: boolean,
|
|
383
428
|
error: ReadonlyJSONValue | undefined,
|
|
384
|
-
cache?:
|
|
429
|
+
cache?: ExternalMessageChunkConversionCache,
|
|
385
430
|
cancelledMessageIds?: ReadonlySet<string>,
|
|
386
431
|
) => {
|
|
387
432
|
const isLast = idx === chunkCount - 1;
|
|
@@ -400,6 +445,9 @@ export const convertExternalMessageChunk = <T>(
|
|
|
400
445
|
const hasPendingToolCalls =
|
|
401
446
|
typeof joined.content === "object" &&
|
|
402
447
|
joined.content.some(isPendingToolCall);
|
|
448
|
+
const hasBackgroundToolCalls =
|
|
449
|
+
typeof joined.content === "object" &&
|
|
450
|
+
joined.content.some(isBackgroundToolCall);
|
|
403
451
|
const autoStatus = getAutoStatus(
|
|
404
452
|
isLast,
|
|
405
453
|
isRunning,
|
|
@@ -407,6 +455,7 @@ export const convertExternalMessageChunk = <T>(
|
|
|
407
455
|
hasPendingToolCalls,
|
|
408
456
|
isLast ? error : undefined,
|
|
409
457
|
isCancelled,
|
|
458
|
+
hasBackgroundToolCalls,
|
|
410
459
|
);
|
|
411
460
|
const fallbackId = `${FALLBACK_ID_PREFIX}${idx}`;
|
|
412
461
|
|
|
@@ -490,21 +539,63 @@ export const convertExternalMessages = <T extends WeakKey>(
|
|
|
490
539
|
callback: ExternalMessageConverterCallback<T>,
|
|
491
540
|
isRunning: boolean,
|
|
492
541
|
metadata: ExternalMessageConverterMetadata,
|
|
542
|
+
joinStrategy?: JoinStrategy,
|
|
543
|
+
cache?: InternalExternalMessageConversionCache<T>,
|
|
493
544
|
) => {
|
|
494
|
-
const callbackResults = messages.map((message) =>
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
545
|
+
const callbackResults = messages.map((message) => {
|
|
546
|
+
let result = cache?.callbackCache.get(message);
|
|
547
|
+
if (
|
|
548
|
+
!result ||
|
|
549
|
+
result.metadata !== metadata ||
|
|
550
|
+
result.callback !== callback
|
|
551
|
+
) {
|
|
552
|
+
result = {
|
|
553
|
+
...convertExternalMessageCallback(message, callback, metadata),
|
|
554
|
+
metadata,
|
|
555
|
+
callback,
|
|
556
|
+
};
|
|
557
|
+
cache?.callbackCache.set(message, result);
|
|
558
|
+
}
|
|
559
|
+
return result;
|
|
560
|
+
});
|
|
561
|
+
const chunks = chunkExternalMessages(callbackResults, joinStrategy).map(
|
|
562
|
+
(message) => {
|
|
563
|
+
const key = message.outputs[0];
|
|
564
|
+
if (!key || !cache) return message;
|
|
565
|
+
|
|
566
|
+
const cached = cache.chunkCache.get(key);
|
|
567
|
+
if (cached && shallowArrayEqual(cached.outputs, message.outputs)) {
|
|
568
|
+
return cached;
|
|
569
|
+
}
|
|
570
|
+
cache.chunkCache.set(key, message);
|
|
571
|
+
return message;
|
|
572
|
+
},
|
|
508
573
|
);
|
|
574
|
+
const result = cache
|
|
575
|
+
? cache.converterCache.convertMessages(chunks, (cached, message, idx) =>
|
|
576
|
+
convertExternalMessageChunk(
|
|
577
|
+
message,
|
|
578
|
+
idx,
|
|
579
|
+
chunks.length,
|
|
580
|
+
isRunning,
|
|
581
|
+
metadata.error,
|
|
582
|
+
{
|
|
583
|
+
message: cached,
|
|
584
|
+
generatedFallbackMessages: cache.generatedFallbackMessages,
|
|
585
|
+
},
|
|
586
|
+
metadata.cancelledMessageIds,
|
|
587
|
+
),
|
|
588
|
+
)
|
|
589
|
+
: chunks.map((message, idx) =>
|
|
590
|
+
convertExternalMessageChunk(
|
|
591
|
+
message,
|
|
592
|
+
idx,
|
|
593
|
+
chunks.length,
|
|
594
|
+
isRunning,
|
|
595
|
+
metadata.error,
|
|
596
|
+
undefined,
|
|
597
|
+
metadata.cancelledMessageIds,
|
|
598
|
+
),
|
|
599
|
+
);
|
|
509
600
|
return completeExternalMessageConversion(result, metadata.error);
|
|
510
601
|
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { ThreadMessage } from "../../types/message";
|
|
3
|
+
import { ExportedMessageRepository } from "./message-repository";
|
|
4
|
+
|
|
5
|
+
const nestedRunningAssistant: ThreadMessage = {
|
|
6
|
+
id: "nested-assistant",
|
|
7
|
+
createdAt: new Date(0),
|
|
8
|
+
role: "assistant",
|
|
9
|
+
content: [],
|
|
10
|
+
status: { type: "running" },
|
|
11
|
+
metadata: {
|
|
12
|
+
unstable_state: {},
|
|
13
|
+
unstable_annotations: [],
|
|
14
|
+
unstable_data: [],
|
|
15
|
+
steps: [],
|
|
16
|
+
custom: {},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const delegating = {
|
|
21
|
+
id: "assistant-1",
|
|
22
|
+
role: "assistant" as const,
|
|
23
|
+
content: [
|
|
24
|
+
{
|
|
25
|
+
type: "tool-call" as const,
|
|
26
|
+
toolCallId: "delegate-1",
|
|
27
|
+
toolName: "delegate",
|
|
28
|
+
args: {},
|
|
29
|
+
argsText: "",
|
|
30
|
+
messages: [nestedRunningAssistant],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe("ExportedMessageRepository", () => {
|
|
36
|
+
it("imports a message saved mid-delegation as pending, not running", () => {
|
|
37
|
+
const fromArray = ExportedMessageRepository.fromArray([delegating]);
|
|
38
|
+
const fromBranchable = ExportedMessageRepository.fromBranchableArray([
|
|
39
|
+
{ message: delegating, parentId: null },
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
for (const repository of [fromArray, fromBranchable]) {
|
|
43
|
+
expect(repository.messages[0]?.message.status).toMatchObject({
|
|
44
|
+
type: "requires-action",
|
|
45
|
+
reason: "tool-calls",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -2,7 +2,7 @@ import type { ThreadMessage } from "../../types/message";
|
|
|
2
2
|
import type { RunConfig } from "../../types/message";
|
|
3
3
|
import { generateId } from "../../utils/id";
|
|
4
4
|
import type { ThreadMessageLike } from "./thread-message-like";
|
|
5
|
-
import {
|
|
5
|
+
import { getRepositoryContentAutoStatus } from "./auto-status";
|
|
6
6
|
import { fromThreadMessageLike } from "./thread-message-like";
|
|
7
7
|
|
|
8
8
|
export type ExportedMessageRepositoryItem = {
|
|
@@ -28,7 +28,7 @@ export const ExportedMessageRepository = {
|
|
|
28
28
|
fromThreadMessageLike(
|
|
29
29
|
m,
|
|
30
30
|
generateId(),
|
|
31
|
-
|
|
31
|
+
getRepositoryContentAutoStatus(m.content),
|
|
32
32
|
),
|
|
33
33
|
);
|
|
34
34
|
|
|
@@ -62,7 +62,7 @@ export const ExportedMessageRepository = {
|
|
|
62
62
|
message: fromThreadMessageLike(
|
|
63
63
|
message,
|
|
64
64
|
message.id,
|
|
65
|
-
|
|
65
|
+
getRepositoryContentAutoStatus(message.content),
|
|
66
66
|
),
|
|
67
67
|
};
|
|
68
68
|
}),
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { fromThreadMessageLike } from "./thread-message-like";
|
|
3
|
+
|
|
4
|
+
describe("fromThreadMessageLike", () => {
|
|
5
|
+
it("preserves modality on user and assistant messages", () => {
|
|
6
|
+
const user = fromThreadMessageLike(
|
|
7
|
+
{
|
|
8
|
+
role: "user",
|
|
9
|
+
content: "Hello",
|
|
10
|
+
metadata: { modality: "voice" },
|
|
11
|
+
},
|
|
12
|
+
"user-id",
|
|
13
|
+
{ type: "complete", reason: "unknown" },
|
|
14
|
+
);
|
|
15
|
+
const assistant = fromThreadMessageLike(
|
|
16
|
+
{
|
|
17
|
+
role: "assistant",
|
|
18
|
+
content: "Hi",
|
|
19
|
+
metadata: { modality: "voice" },
|
|
20
|
+
},
|
|
21
|
+
"assistant-id",
|
|
22
|
+
{ type: "complete", reason: "unknown" },
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
expect(user.metadata.modality).toBe("voice");
|
|
26
|
+
expect(assistant.metadata.modality).toBe("voice");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("leaves modality absent when the input has none", () => {
|
|
30
|
+
const message = fromThreadMessageLike(
|
|
31
|
+
{ role: "user", content: "Hello" },
|
|
32
|
+
"user-id",
|
|
33
|
+
{ type: "complete", reason: "unknown" },
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
expect(message.metadata).not.toHaveProperty("modality");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("ignores modality on system messages", () => {
|
|
40
|
+
const message = fromThreadMessageLike(
|
|
41
|
+
{
|
|
42
|
+
role: "system",
|
|
43
|
+
content: "Instructions",
|
|
44
|
+
metadata: { modality: "voice" },
|
|
45
|
+
},
|
|
46
|
+
"system-id",
|
|
47
|
+
{ type: "complete", reason: "unknown" },
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(message.metadata).not.toHaveProperty("modality");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
} from "../../types/message";
|
|
21
21
|
import type { CompleteAttachment } from "../../types/attachment";
|
|
22
22
|
import type {
|
|
23
|
+
MessageModality,
|
|
23
24
|
MessageTiming,
|
|
24
25
|
PartProviderMetadata,
|
|
25
26
|
TextMessagePart,
|
|
@@ -88,6 +89,7 @@ export type ThreadMessageLike = {
|
|
|
88
89
|
readonly timing?: MessageTiming | undefined;
|
|
89
90
|
readonly submittedFeedback?: { readonly type: "positive" | "negative" };
|
|
90
91
|
readonly isOptimistic?: boolean | undefined;
|
|
92
|
+
readonly modality?: MessageModality | undefined;
|
|
91
93
|
readonly custom?: Record<string, unknown> | undefined;
|
|
92
94
|
}
|
|
93
95
|
| undefined;
|
|
@@ -223,6 +225,7 @@ export const fromThreadMessageLike = (
|
|
|
223
225
|
submittedFeedback: metadata.submittedFeedback,
|
|
224
226
|
}),
|
|
225
227
|
...(metadata?.isOptimistic && { isOptimistic: true }),
|
|
228
|
+
...(metadata?.modality && { modality: metadata.modality }),
|
|
226
229
|
},
|
|
227
230
|
} satisfies ThreadAssistantMessage;
|
|
228
231
|
|
|
@@ -263,6 +266,7 @@ export const fromThreadMessageLike = (
|
|
|
263
266
|
metadata: {
|
|
264
267
|
custom: metadata?.custom ?? {},
|
|
265
268
|
...(metadata?.isOptimistic && { isOptimistic: true }),
|
|
269
|
+
...(metadata?.modality && { modality: metadata.modality }),
|
|
266
270
|
},
|
|
267
271
|
} satisfies ThreadUserMessage;
|
|
268
272
|
|
|
@@ -637,6 +637,8 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
637
637
|
}
|
|
638
638
|
|
|
639
639
|
public async append(rawMessage: AppendMessage): Promise<void> {
|
|
640
|
+
if (this._isVoiceMessage(rawMessage.sourceId))
|
|
641
|
+
throw new Error("Voice transcript messages cannot be edited");
|
|
640
642
|
// sourceId marks an edit send; the parent may coincide with the head
|
|
641
643
|
// after a resync (e.g. cancelRun dropped the edited message).
|
|
642
644
|
const isEdit =
|
|
@@ -786,6 +788,8 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
786
788
|
public async startRun(config: StartRunConfig): Promise<void> {
|
|
787
789
|
if (!this._store.onReload)
|
|
788
790
|
throw new Error("Runtime does not support reloading messages.");
|
|
791
|
+
if (this._isVoiceMessage(config.sourceId))
|
|
792
|
+
throw new Error("Voice transcript messages cannot be reloaded");
|
|
789
793
|
|
|
790
794
|
this._pendingDeleteEvictions.clear();
|
|
791
795
|
|
|
@@ -800,6 +804,8 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
800
804
|
public async resumeRun(config: ResumeRunConfig): Promise<void> {
|
|
801
805
|
if (!this._store.onResume)
|
|
802
806
|
throw new Error("Runtime does not support resuming runs.");
|
|
807
|
+
if (this._isVoiceMessage(config.sourceId))
|
|
808
|
+
throw new Error("Voice transcript messages cannot be reloaded");
|
|
803
809
|
|
|
804
810
|
await this._store.onResume(config);
|
|
805
811
|
}
|
|
@@ -961,8 +967,32 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
961
967
|
): Promise<void> {
|
|
962
968
|
if (!this._store.onRespondToToolApproval)
|
|
963
969
|
throw new Error("Runtime does not support tool approvals.");
|
|
970
|
+
const message = this.messages.findLast(
|
|
971
|
+
(candidate) =>
|
|
972
|
+
candidate.role === "assistant" &&
|
|
973
|
+
candidate.content.some(
|
|
974
|
+
(part) =>
|
|
975
|
+
part.type === "tool-call" &&
|
|
976
|
+
part.approval?.id === options.approvalId,
|
|
977
|
+
),
|
|
978
|
+
);
|
|
979
|
+
const toolCall = message?.content.find(
|
|
980
|
+
(part) =>
|
|
981
|
+
part.type === "tool-call" && part.approval?.id === options.approvalId,
|
|
982
|
+
);
|
|
964
983
|
try {
|
|
965
|
-
return Promise.resolve(this._store.onRespondToToolApproval(options))
|
|
984
|
+
return Promise.resolve(this._store.onRespondToToolApproval(options)).then(
|
|
985
|
+
() => {
|
|
986
|
+
if (message && toolCall?.type === "tool-call") {
|
|
987
|
+
this._notifyToolApprovalAnswered(
|
|
988
|
+
message.id,
|
|
989
|
+
toolCall.toolCallId,
|
|
990
|
+
toolCall.toolName,
|
|
991
|
+
options.approved,
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
);
|
|
966
996
|
} catch (error) {
|
|
967
997
|
return Promise.reject(error);
|
|
968
998
|
}
|