@assistant-ui/react 0.14.28 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/ExternalThread.d.ts +11 -1
- package/dist/client/ExternalThread.d.ts.map +1 -1
- package/dist/client/ExternalThread.js +762 -516
- package/dist/client/ExternalThread.js.map +1 -1
- package/dist/client/InMemoryThreadList.js +3 -3
- package/dist/client/InMemoryThreadList.js.map +1 -1
- package/dist/context/index.d.ts +1 -8
- package/dist/context/index.js +1 -8
- package/dist/index.d.ts +1 -8
- package/dist/index.js +1 -8
- package/dist/legacy-runtime/cloud/auiV0.d.ts +12 -1
- package/dist/legacy-runtime/cloud/auiV0.d.ts.map +1 -1
- package/dist/legacy-runtime/cloud/auiV0.js +2 -1
- package/dist/legacy-runtime/cloud/auiV0.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +7 -6
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +30 -15
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +11 -7
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +1 -0
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.js +1 -1
- package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/model-context/makeAssistantVisible.js +26 -25
- package/dist/model-context/makeAssistantVisible.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js +3 -3
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarRoot.js +3 -3
- package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
- package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts +7 -6
- package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts.map +1 -1
- package/dist/primitives/actionBar/useActionBarFloatStatus.js +10 -11
- package/dist/primitives/actionBar/useActionBarFloatStatus.js.map +1 -1
- package/dist/primitives/attachment/AttachmentRemove.js +4 -3
- package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +4 -3
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js +11 -5
- package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +2 -4
- package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +18 -16
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerQuote.js +1 -1
- package/dist/primitives/composer/ComposerQuote.js.map +1 -1
- package/dist/primitives/composer/ComposerSend.js +1 -0
- package/dist/primitives/composer/ComposerSend.js.map +1 -1
- package/dist/primitives/composer/ComposerStopDictation.js +4 -3
- package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerSelectionResource.js +3 -3
- package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js +67 -89
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/primitives/message/MessageRoot.js +3 -3
- package/dist/primitives/message/MessageRoot.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.js +4 -3
- package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.js +4 -3
- package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.js +17 -15
- package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
- package/dist/primitives/thread/ThreadScrollToBottom.js +1 -0
- package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
- package/dist/unstable/useComposerInput.js +2 -2
- package/dist/unstable/useComposerInput.js.map +1 -1
- package/dist/unstable/useComposerInputHistory.js +6 -6
- package/dist/unstable/useComposerInputHistory.js.map +1 -1
- package/dist/unstable/useMentionAdapter.js +1 -1
- package/dist/unstable/useMentionAdapter.js.map +1 -1
- package/dist/utils/hooks/useOnResizeContent.js +1 -0
- package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
- package/dist/utils/hooks/useSizeHandle.js +1 -0
- package/dist/utils/hooks/useSizeHandle.js.map +1 -1
- package/dist/utils/smooth/SmoothContext.js +3 -3
- package/dist/utils/smooth/SmoothContext.js.map +1 -1
- package/dist/utils/smooth/useSmooth.d.ts.map +1 -1
- package/dist/utils/smooth/useSmooth.js +3 -3
- package/dist/utils/smooth/useSmooth.js.map +1 -1
- package/package.json +9 -9
- package/src/client/ExternalThread.ts +345 -56
- package/src/client/InMemoryThreadList.ts +3 -3
- package/src/context/index.ts +0 -44
- package/src/legacy-runtime/cloud/auiV0.ts +14 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/commandQueue.ts +4 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/runManager.ts +37 -10
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-delivery.test.tsx +79 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-parentid.test.tsx +97 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-scheduling.test.ts +113 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransport.spec.md +10 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +185 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +21 -5
- package/src/mcp-apps/McpAppRenderer.tsx +1 -1
- package/src/model-context/makeAssistantVisible.test.tsx +1 -1
- package/src/model-context/makeAssistantVisible.tsx +1 -1
- package/src/primitives/actionBar/ActionBarExportMarkdown.tsx +1 -1
- package/src/primitives/actionBar/useActionBarFloatStatus.ts +7 -5
- package/src/primitives/attachment/AttachmentRemove.ts +1 -1
- package/src/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.ts +1 -1
- package/src/primitives/composer/ComposerAddAttachment.test.tsx +108 -0
- package/src/primitives/composer/ComposerAddAttachment.ts +11 -5
- package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +5 -4
- package/src/primitives/composer/ComposerAttachmentDropzone.tsx +4 -3
- package/src/primitives/composer/ComposerInput.test.tsx +104 -7
- package/src/primitives/composer/ComposerInput.tsx +22 -20
- package/src/primitives/composer/ComposerQuote.tsx +1 -1
- package/src/primitives/composer/ComposerStopDictation.ts +1 -1
- package/src/primitives/composer/trigger/triggerSelectionResource.ts +7 -9
- package/src/primitives/message/MessagePartsGrouped.tsx +6 -8
- package/src/primitives/message/MessageRoot.tsx +1 -1
- package/src/primitives/queueItem/QueueItemRemove.ts +1 -1
- package/src/primitives/queueItem/QueueItemSteer.ts +1 -1
- package/src/primitives/selectionToolbar/SelectionToolbarQuote.tsx +1 -1
- package/src/primitives/suggestion/SuggestionTrigger.ts +9 -9
- package/src/tests/DataRenderers.test.tsx +212 -0
- package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +1 -11
- package/src/tests/RemoteThreadListRuntime.deferredProvider.test.tsx +3 -3
- package/src/tests/external-thread-attachments.test.tsx +300 -11
- package/src/tests/external-thread-branches.test.tsx +10 -10
- package/src/tests/external-thread-parity.test.tsx +230 -0
- package/src/tests/generative-ui.test.tsx +1 -46
- package/src/tests/local-runtime-queue.test.tsx +29 -34
- package/src/tests/messagePartSwitchRace.test.tsx +46 -15
- package/src/tests/messagePartTypeMismatch.test.tsx +2 -2
- package/src/tests/threadMessageById.test.tsx +0 -7
- package/src/unstable/useComposerInput.test.tsx +2 -2
- package/src/unstable/useComposerInput.ts +2 -2
- package/src/unstable/useComposerInputHistory.test.tsx +3 -3
- package/src/unstable/useComposerInputHistory.ts +6 -6
- package/src/unstable/useMentionAdapter.ts +1 -1
- package/src/utils/smooth/SmoothContext.tsx +1 -1
- package/src/utils/smooth/useSmooth.test.tsx +1 -1
- package/src/utils/smooth/useSmooth.ts +8 -5
- package/dist/context/react/utils/createStateHookForRuntime.d.ts +0 -34
- package/dist/context/react/utils/createStateHookForRuntime.d.ts.map +0 -1
- package/dist/context/react/utils/createStateHookForRuntime.js +0 -21
- package/dist/context/react/utils/createStateHookForRuntime.js.map +0 -1
- package/dist/legacy-runtime/hooks/AssistantContext.d.ts +0 -74
- package/dist/legacy-runtime/hooks/AssistantContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/AssistantContext.js +0 -29
- package/dist/legacy-runtime/hooks/AssistantContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/AttachmentContext.d.ts +0 -1007
- package/dist/legacy-runtime/hooks/AttachmentContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/AttachmentContext.js +0 -47
- package/dist/legacy-runtime/hooks/AttachmentContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/ComposerContext.d.ts +0 -141
- package/dist/legacy-runtime/hooks/ComposerContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/ComposerContext.js +0 -65
- package/dist/legacy-runtime/hooks/ComposerContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/MessageContext.d.ts +0 -155
- package/dist/legacy-runtime/hooks/MessageContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/MessageContext.js +0 -68
- package/dist/legacy-runtime/hooks/MessageContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/MessagePartContext.d.ts +0 -44
- package/dist/legacy-runtime/hooks/MessagePartContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/MessagePartContext.js +0 -26
- package/dist/legacy-runtime/hooks/MessagePartContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/ThreadContext.d.ts +0 -139
- package/dist/legacy-runtime/hooks/ThreadContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/ThreadContext.js +0 -84
- package/dist/legacy-runtime/hooks/ThreadContext.js.map +0 -1
- package/dist/legacy-runtime/hooks/ThreadListItemContext.d.ts +0 -44
- package/dist/legacy-runtime/hooks/ThreadListItemContext.d.ts.map +0 -1
- package/dist/legacy-runtime/hooks/ThreadListItemContext.js +0 -26
- package/dist/legacy-runtime/hooks/ThreadListItemContext.js.map +0 -1
- package/src/context/react/utils/createStateHookForRuntime.ts +0 -82
- package/src/legacy-runtime/hooks/AssistantContext.ts +0 -67
- package/src/legacy-runtime/hooks/AttachmentContext.ts +0 -99
- package/src/legacy-runtime/hooks/ComposerContext.ts +0 -131
- package/src/legacy-runtime/hooks/MessageContext.ts +0 -127
- package/src/legacy-runtime/hooks/MessagePartContext.ts +0 -32
- package/src/legacy-runtime/hooks/ThreadContext.ts +0 -120
- package/src/legacy-runtime/hooks/ThreadListItemContext.ts +0 -36
- package/src/tests/BaseComposerRuntimeCore.test.ts +0 -621
- package/src/tests/BaseSubscribable.test.ts +0 -54
- package/src/tests/DataRenderers.test.ts +0 -355
- package/src/tests/ExternalStoreThreadRuntimeCore.test.ts +0 -685
- package/src/tests/LocalThreadRuntimeCore.test.ts +0 -324
- package/src/tests/auiV0Encode.test.ts +0 -188
- package/src/tests/external-message-converter.test.ts +0 -346
- package/src/tests/shouldContinue.test.ts +0 -122
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
useState,
|
|
3
|
+
useMemo,
|
|
4
|
+
useEffect,
|
|
5
|
+
useEffectEvent,
|
|
6
|
+
useCallback,
|
|
7
|
+
useRef,
|
|
8
|
+
} from "react";
|
|
2
9
|
import { resource, withKey } from "@assistant-ui/tap";
|
|
3
10
|
import {
|
|
4
11
|
type ClientElement,
|
|
@@ -10,19 +17,28 @@ import {
|
|
|
10
17
|
} from "@assistant-ui/store";
|
|
11
18
|
|
|
12
19
|
import type {
|
|
20
|
+
AddToolResultOptions,
|
|
13
21
|
AppendMessage,
|
|
14
22
|
Attachment,
|
|
23
|
+
AttachmentAdapter,
|
|
15
24
|
CreateAttachment,
|
|
25
|
+
PendingAttachment,
|
|
16
26
|
RespondToToolApprovalOptions,
|
|
27
|
+
ResumeToolCallOptions,
|
|
28
|
+
MessagePartStatus,
|
|
17
29
|
ThreadAssistantMessagePart,
|
|
18
30
|
ThreadUserMessagePart,
|
|
19
31
|
ThreadMessage,
|
|
32
|
+
ToolCallMessagePartStatus,
|
|
20
33
|
ExternalThreadQueueAdapter,
|
|
21
34
|
ExternalThreadBranchAdapter,
|
|
22
35
|
} from "@assistant-ui/core";
|
|
36
|
+
import { ToolResponse } from "assistant-stream";
|
|
37
|
+
import type { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
23
38
|
import type { QueueItemState } from "@assistant-ui/core/store";
|
|
24
39
|
import type { ComposerSendOptions } from "@assistant-ui/core/store";
|
|
25
40
|
import {
|
|
41
|
+
fileMatchesAccept,
|
|
26
42
|
getThreadMessageText,
|
|
27
43
|
isCreateAttachment,
|
|
28
44
|
resolveToolApprovalResponse,
|
|
@@ -38,9 +54,35 @@ export type ExternalThreadMessage = ThreadMessage & {
|
|
|
38
54
|
id: string;
|
|
39
55
|
};
|
|
40
56
|
|
|
57
|
+
const COMPLETE_STATUS: MessagePartStatus = Object.freeze({
|
|
58
|
+
type: "complete",
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Legacy runtime parity (toMessagePartStatus): a tool call without a result
|
|
62
|
+
// carries its message's status; the last part of a streaming message streams.
|
|
63
|
+
const derivePartStatus = (
|
|
64
|
+
message: ExternalThreadMessage,
|
|
65
|
+
partIndex: number,
|
|
66
|
+
part: ThreadAssistantMessagePart | ThreadUserMessagePart,
|
|
67
|
+
): ToolCallMessagePartStatus => {
|
|
68
|
+
if (message.role !== "assistant" || !message.status) return COMPLETE_STATUS;
|
|
69
|
+
|
|
70
|
+
if (part.type === "tool-call") {
|
|
71
|
+
if (!part.result) return message.status as ToolCallMessagePartStatus;
|
|
72
|
+
return COMPLETE_STATUS;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const isLastPart = partIndex === Math.max(0, message.content.length - 1);
|
|
76
|
+
if (message.status.type === "requires-action") return COMPLETE_STATUS;
|
|
77
|
+
return isLastPart ? (message.status as MessagePartStatus) : COMPLETE_STATUS;
|
|
78
|
+
};
|
|
79
|
+
|
|
41
80
|
export type ExternalThreadProps = {
|
|
42
81
|
messages: readonly ExternalThreadMessage[];
|
|
43
82
|
isRunning?: boolean;
|
|
83
|
+
isLoading?: boolean | undefined;
|
|
84
|
+
state?: ReadonlyJSONValue | undefined;
|
|
85
|
+
extras?: unknown;
|
|
44
86
|
/**
|
|
45
87
|
* Whether sending new messages is currently disabled. When `true`, the
|
|
46
88
|
* thread composer's input remains usable but `send()` is a no-op and
|
|
@@ -57,6 +99,12 @@ export type ExternalThreadProps = {
|
|
|
57
99
|
onReload?: (parentId: string | null) => void;
|
|
58
100
|
onStartRun?: () => void;
|
|
59
101
|
onCancel?: () => void;
|
|
102
|
+
onResume?: (() => void) | undefined;
|
|
103
|
+
onAddToolResult?: ((options: AddToolResultOptions) => void) | undefined;
|
|
104
|
+
/** Callback for resuming a tool call that is waiting for human input. */
|
|
105
|
+
onResumeToolCall?: ((options: ResumeToolCallOptions) => void) | undefined;
|
|
106
|
+
onLoadExternalState?: ((state: unknown) => void) | undefined;
|
|
107
|
+
attachmentAdapter?: AttachmentAdapter | undefined;
|
|
60
108
|
/** Queue adapter for runtimes that support message queuing and steering. */
|
|
61
109
|
queue?: ExternalThreadQueueAdapter;
|
|
62
110
|
/** Branch adapter for runtimes that track sibling variants of messages. */
|
|
@@ -68,6 +116,7 @@ export type ExternalThreadProps = {
|
|
|
68
116
|
type MessageClientProps = {
|
|
69
117
|
message: ExternalThreadMessage;
|
|
70
118
|
index: number;
|
|
119
|
+
parentId: string | null;
|
|
71
120
|
onEdit?: (message: AppendMessage) => void;
|
|
72
121
|
onReload?: () => void;
|
|
73
122
|
queue?: ExternalThreadQueueAdapter | undefined;
|
|
@@ -75,17 +124,24 @@ type MessageClientProps = {
|
|
|
75
124
|
onRespondToToolApproval?:
|
|
76
125
|
| ((options: RespondToToolApprovalOptions) => void)
|
|
77
126
|
| undefined;
|
|
127
|
+
onAddToolResult?: ((options: AddToolResultOptions) => void) | undefined;
|
|
128
|
+
onResumeToolCall?: ((options: ResumeToolCallOptions) => void) | undefined;
|
|
129
|
+
attachmentAdapter?: AttachmentAdapter | undefined;
|
|
78
130
|
};
|
|
79
131
|
|
|
80
132
|
// Message Client - minimal implementation
|
|
81
133
|
const useMessageClient = ({
|
|
82
134
|
message,
|
|
83
135
|
index,
|
|
136
|
+
parentId,
|
|
84
137
|
onEdit,
|
|
85
138
|
onReload,
|
|
86
139
|
queue,
|
|
87
140
|
branches,
|
|
88
141
|
onRespondToToolApproval,
|
|
142
|
+
onAddToolResult,
|
|
143
|
+
onResumeToolCall,
|
|
144
|
+
attachmentAdapter,
|
|
89
145
|
}: MessageClientProps): ClientOutput<"message"> => {
|
|
90
146
|
const [isCopied, setIsCopied] = useState(false);
|
|
91
147
|
const [isHovering, setIsHovering] = useState(false);
|
|
@@ -93,7 +149,17 @@ const useMessageClient = ({
|
|
|
93
149
|
|
|
94
150
|
const partClients = useClientLookup(
|
|
95
151
|
message.content.map((part, idx) =>
|
|
96
|
-
withKey(
|
|
152
|
+
withKey(
|
|
153
|
+
idx,
|
|
154
|
+
PartResource({
|
|
155
|
+
part,
|
|
156
|
+
status: derivePartStatus(message, idx, part),
|
|
157
|
+
messageId: message.id,
|
|
158
|
+
onRespondToToolApproval,
|
|
159
|
+
onAddToolResult,
|
|
160
|
+
onResumeToolCall,
|
|
161
|
+
}),
|
|
162
|
+
),
|
|
97
163
|
),
|
|
98
164
|
);
|
|
99
165
|
|
|
@@ -121,7 +187,7 @@ const useMessageClient = ({
|
|
|
121
187
|
queue?.clear("edit");
|
|
122
188
|
onEdit?.({
|
|
123
189
|
...msg,
|
|
124
|
-
parentId
|
|
190
|
+
parentId,
|
|
125
191
|
sourceId: message.id,
|
|
126
192
|
});
|
|
127
193
|
setIsEditing(false);
|
|
@@ -137,6 +203,7 @@ const useMessageClient = ({
|
|
|
137
203
|
onSend: handleSendEdit,
|
|
138
204
|
message,
|
|
139
205
|
queue,
|
|
206
|
+
attachmentAdapter,
|
|
140
207
|
}),
|
|
141
208
|
);
|
|
142
209
|
|
|
@@ -149,7 +216,7 @@ const useMessageClient = ({
|
|
|
149
216
|
return {
|
|
150
217
|
...message,
|
|
151
218
|
attachments: message.attachments ?? [],
|
|
152
|
-
parentId
|
|
219
|
+
parentId,
|
|
153
220
|
isLast: false, // Will be set by thread
|
|
154
221
|
branchNumber,
|
|
155
222
|
branchCount,
|
|
@@ -162,6 +229,7 @@ const useMessageClient = ({
|
|
|
162
229
|
};
|
|
163
230
|
}, [
|
|
164
231
|
message,
|
|
232
|
+
parentId,
|
|
165
233
|
isCopied,
|
|
166
234
|
isHovering,
|
|
167
235
|
index,
|
|
@@ -220,28 +288,65 @@ const MessageClient = resource(useMessageClient);
|
|
|
220
288
|
|
|
221
289
|
type PartResourceProps = {
|
|
222
290
|
part: ThreadAssistantMessagePart | ThreadUserMessagePart;
|
|
291
|
+
status: ToolCallMessagePartStatus;
|
|
292
|
+
messageId: string;
|
|
223
293
|
onRespondToToolApproval?:
|
|
224
294
|
| ((options: RespondToToolApprovalOptions) => void)
|
|
225
295
|
| undefined;
|
|
296
|
+
onAddToolResult?: ((options: AddToolResultOptions) => void) | undefined;
|
|
297
|
+
onResumeToolCall?: ((options: ResumeToolCallOptions) => void) | undefined;
|
|
226
298
|
};
|
|
227
299
|
|
|
228
300
|
// Part Client - minimal implementation
|
|
229
301
|
const usePartResource = ({
|
|
230
302
|
part,
|
|
303
|
+
status,
|
|
304
|
+
messageId,
|
|
231
305
|
onRespondToToolApproval,
|
|
306
|
+
onAddToolResult,
|
|
307
|
+
onResumeToolCall,
|
|
232
308
|
}: PartResourceProps): ClientOutput<"part"> => {
|
|
233
309
|
const state = useMemo(
|
|
234
310
|
() => ({
|
|
235
311
|
...part,
|
|
236
|
-
status
|
|
312
|
+
status,
|
|
237
313
|
}),
|
|
238
|
-
[part],
|
|
314
|
+
[part, status],
|
|
239
315
|
);
|
|
240
316
|
|
|
241
317
|
return {
|
|
242
318
|
getState: () => state,
|
|
243
|
-
addToolResult: () => {
|
|
244
|
-
|
|
319
|
+
addToolResult: (result) => {
|
|
320
|
+
if (!onAddToolResult)
|
|
321
|
+
throw new Error(
|
|
322
|
+
"Runtime does not support tool results (onAddToolResult is not set).",
|
|
323
|
+
);
|
|
324
|
+
if (part.type !== "tool-call")
|
|
325
|
+
throw new Error("Tried to add tool result on non-tool message part");
|
|
326
|
+
|
|
327
|
+
const response = ToolResponse.toResponse(result);
|
|
328
|
+
onAddToolResult({
|
|
329
|
+
messageId,
|
|
330
|
+
toolName: part.toolName,
|
|
331
|
+
toolCallId: part.toolCallId,
|
|
332
|
+
result: response.result as ReadonlyJSONValue,
|
|
333
|
+
isError: response.isError,
|
|
334
|
+
...(response.artifact !== undefined && { artifact: response.artifact }),
|
|
335
|
+
...(response.modelContent !== undefined && {
|
|
336
|
+
modelContent: response.modelContent,
|
|
337
|
+
}),
|
|
338
|
+
});
|
|
339
|
+
},
|
|
340
|
+
resumeToolCall: (payload) => {
|
|
341
|
+
if (!onResumeToolCall)
|
|
342
|
+
throw new Error(
|
|
343
|
+
"Runtime does not support resuming tool calls (onResumeToolCall is not set).",
|
|
344
|
+
);
|
|
345
|
+
if (part.type !== "tool-call")
|
|
346
|
+
throw new Error("Tried to resume tool call on non-tool message part");
|
|
347
|
+
|
|
348
|
+
onResumeToolCall({ toolCallId: part.toolCallId, payload });
|
|
349
|
+
},
|
|
245
350
|
respondToToolApproval: (response) => {
|
|
246
351
|
if (!onRespondToToolApproval)
|
|
247
352
|
throw new Error("Runtime does not support tool approvals.");
|
|
@@ -269,7 +374,7 @@ const PartResource = resource(usePartResource);
|
|
|
269
374
|
|
|
270
375
|
type AttachmentResourceProps = {
|
|
271
376
|
attachment: Attachment;
|
|
272
|
-
onRemove?: () => void
|
|
377
|
+
onRemove?: () => void | Promise<void>;
|
|
273
378
|
};
|
|
274
379
|
|
|
275
380
|
// Attachment Client - minimal implementation
|
|
@@ -280,7 +385,7 @@ const useAttachmentResource = ({
|
|
|
280
385
|
return {
|
|
281
386
|
getState: () => attachment,
|
|
282
387
|
remove: async () => {
|
|
283
|
-
onRemove?.();
|
|
388
|
+
await onRemove?.();
|
|
284
389
|
},
|
|
285
390
|
};
|
|
286
391
|
};
|
|
@@ -297,6 +402,7 @@ type ComposerClientResourceProps = {
|
|
|
297
402
|
onSend?: (message: AppendMessage) => void;
|
|
298
403
|
message?: ExternalThreadMessage;
|
|
299
404
|
queue?: ExternalThreadQueueAdapter | undefined;
|
|
405
|
+
attachmentAdapter?: AttachmentAdapter | undefined;
|
|
300
406
|
};
|
|
301
407
|
|
|
302
408
|
const useQueueItemClient = ({
|
|
@@ -317,6 +423,33 @@ const useQueueItemClient = ({
|
|
|
317
423
|
|
|
318
424
|
const QueueItemClient = resource(useQueueItemClient);
|
|
319
425
|
|
|
426
|
+
const drainAdapterAdd = async (
|
|
427
|
+
result: ReturnType<AttachmentAdapter["add"]>,
|
|
428
|
+
upsert: (attachment: Attachment) => void,
|
|
429
|
+
) => {
|
|
430
|
+
if (Symbol.asyncIterator in result) {
|
|
431
|
+
for await (const attachment of result) {
|
|
432
|
+
upsert(attachment);
|
|
433
|
+
}
|
|
434
|
+
} else {
|
|
435
|
+
upsert(await result);
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// State whose setter tracks the latest value in a ref, so imperative
|
|
440
|
+
// call sequences (setText immediately followed by send) observe the write
|
|
441
|
+
// before React re-renders — legacy composer parity.
|
|
442
|
+
const useLiveState = <T>(initial: T) => {
|
|
443
|
+
const [state, setState] = useState(initial);
|
|
444
|
+
const ref = useRef(state);
|
|
445
|
+
const set = useCallback((next: T | ((prev: T) => T)) => {
|
|
446
|
+
ref.current =
|
|
447
|
+
typeof next === "function" ? (next as (prev: T) => T)(ref.current) : next;
|
|
448
|
+
setState(ref.current);
|
|
449
|
+
}, []);
|
|
450
|
+
return [state, set, ref] as const;
|
|
451
|
+
};
|
|
452
|
+
|
|
320
453
|
// Composer Client - minimal implementation
|
|
321
454
|
const useComposerClientResource = ({
|
|
322
455
|
type,
|
|
@@ -328,12 +461,19 @@ const useComposerClientResource = ({
|
|
|
328
461
|
onSend,
|
|
329
462
|
message,
|
|
330
463
|
queue,
|
|
464
|
+
attachmentAdapter,
|
|
331
465
|
}: ComposerClientResourceProps): ClientOutput<"composer"> => {
|
|
332
|
-
const [text, setText] =
|
|
333
|
-
const [role, setRole] =
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const [
|
|
466
|
+
const [text, setText, textRef] = useLiveState("");
|
|
467
|
+
const [role, setRole, roleRef] = useLiveState<
|
|
468
|
+
"user" | "assistant" | "system"
|
|
469
|
+
>("user");
|
|
470
|
+
const [runConfig, setRunConfig, runConfigRef] = useLiveState<
|
|
471
|
+
Record<string, unknown>
|
|
472
|
+
>({});
|
|
473
|
+
const [attachments, setAttachments, attachmentsRef] = useLiveState<
|
|
474
|
+
readonly Attachment[]
|
|
475
|
+
>([]);
|
|
476
|
+
const [quote, setQuote, quoteRef] = useLiveState<
|
|
337
477
|
{ readonly text: string; readonly messageId: string } | undefined
|
|
338
478
|
>(undefined);
|
|
339
479
|
|
|
@@ -359,19 +499,41 @@ const useComposerClientResource = ({
|
|
|
359
499
|
}, [isEditing]);
|
|
360
500
|
|
|
361
501
|
const attachmentClients = useClientLookup(
|
|
362
|
-
attachments.map((attachment
|
|
502
|
+
attachments.map((attachment) =>
|
|
363
503
|
withKey(
|
|
364
504
|
attachment.id,
|
|
365
505
|
AttachmentResource({
|
|
366
506
|
attachment,
|
|
367
|
-
onRemove: () => {
|
|
368
|
-
|
|
507
|
+
onRemove: async () => {
|
|
508
|
+
await attachmentAdapter?.remove(attachment);
|
|
509
|
+
setAttachments((prev) =>
|
|
510
|
+
prev.filter((a) => a.id !== attachment.id),
|
|
511
|
+
);
|
|
369
512
|
},
|
|
370
513
|
}),
|
|
371
514
|
),
|
|
372
515
|
),
|
|
373
516
|
);
|
|
374
517
|
|
|
518
|
+
const removePendingAttachments = async (removed: readonly Attachment[]) => {
|
|
519
|
+
if (!attachmentAdapter) return;
|
|
520
|
+
await Promise.all(
|
|
521
|
+
removed
|
|
522
|
+
.filter((a) => a.status.type !== "complete")
|
|
523
|
+
.map((a) => attachmentAdapter.remove(a)),
|
|
524
|
+
);
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const upsertAttachment = (attachment: Attachment) => {
|
|
528
|
+
setAttachments((prev) => {
|
|
529
|
+
const idx = prev.findIndex((a) => a.id === attachment.id);
|
|
530
|
+
if (idx === -1) return [...prev, attachment];
|
|
531
|
+
const next = [...prev];
|
|
532
|
+
next[idx] = attachment;
|
|
533
|
+
return next;
|
|
534
|
+
});
|
|
535
|
+
};
|
|
536
|
+
|
|
375
537
|
const queueItems = queue?.items ?? EMPTY_QUEUE_ITEMS;
|
|
376
538
|
const queueItemClients = useClientLookup(
|
|
377
539
|
queueItems.map((item) =>
|
|
@@ -396,7 +558,7 @@ const useComposerClientResource = ({
|
|
|
396
558
|
isEditing,
|
|
397
559
|
canCancel,
|
|
398
560
|
canSend: isEditing && !isEmpty && !isSendDisabled,
|
|
399
|
-
attachmentAccept: "*",
|
|
561
|
+
attachmentAccept: attachmentAdapter?.accept ?? "*",
|
|
400
562
|
isEmpty,
|
|
401
563
|
type,
|
|
402
564
|
dictation: undefined,
|
|
@@ -415,6 +577,7 @@ const useComposerClientResource = ({
|
|
|
415
577
|
attachments.length,
|
|
416
578
|
quote,
|
|
417
579
|
queueItems,
|
|
580
|
+
attachmentAdapter?.accept,
|
|
418
581
|
]);
|
|
419
582
|
|
|
420
583
|
return {
|
|
@@ -423,7 +586,24 @@ const useComposerClientResource = ({
|
|
|
423
586
|
setRole,
|
|
424
587
|
setRunConfig,
|
|
425
588
|
addAttachment: async (fileOrAttachment: File | CreateAttachment) => {
|
|
426
|
-
if (
|
|
589
|
+
if (attachmentAdapter) {
|
|
590
|
+
const file = isCreateAttachment(fileOrAttachment)
|
|
591
|
+
? {
|
|
592
|
+
name: fileOrAttachment.name,
|
|
593
|
+
type: fileOrAttachment.contentType ?? "",
|
|
594
|
+
}
|
|
595
|
+
: { name: fileOrAttachment.name, type: fileOrAttachment.type };
|
|
596
|
+
if (!fileMatchesAccept(file, attachmentAdapter.accept))
|
|
597
|
+
throw new Error(
|
|
598
|
+
`File type ${file.type || "unknown"} is not accepted. Accepted types: ${attachmentAdapter.accept}`,
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
if (!isCreateAttachment(fileOrAttachment) && attachmentAdapter) {
|
|
602
|
+
await drainAdapterAdd(
|
|
603
|
+
attachmentAdapter.add({ file: fileOrAttachment }),
|
|
604
|
+
upsertAttachment,
|
|
605
|
+
);
|
|
606
|
+
} else if (!isCreateAttachment(fileOrAttachment)) {
|
|
427
607
|
const newAttachment: Attachment = {
|
|
428
608
|
id: Math.random().toString(36).substring(7),
|
|
429
609
|
type: "file",
|
|
@@ -433,7 +613,7 @@ const useComposerClientResource = ({
|
|
|
433
613
|
status: { type: "complete" },
|
|
434
614
|
content: [],
|
|
435
615
|
};
|
|
436
|
-
setAttachments([...
|
|
616
|
+
setAttachments((prev) => [...prev, newAttachment]);
|
|
437
617
|
} else {
|
|
438
618
|
const newAttachment: Attachment = {
|
|
439
619
|
id: fileOrAttachment.id ?? Math.random().toString(36).substring(7),
|
|
@@ -443,11 +623,13 @@ const useComposerClientResource = ({
|
|
|
443
623
|
content: fileOrAttachment.content,
|
|
444
624
|
status: { type: "complete" },
|
|
445
625
|
};
|
|
446
|
-
setAttachments([...
|
|
626
|
+
setAttachments((prev) => [...prev, newAttachment]);
|
|
447
627
|
}
|
|
448
628
|
},
|
|
449
629
|
clearAttachments: async () => {
|
|
630
|
+
const removed = attachmentsRef.current;
|
|
450
631
|
setAttachments([]);
|
|
632
|
+
await removePendingAttachments(removed);
|
|
451
633
|
},
|
|
452
634
|
attachment: (selector) => {
|
|
453
635
|
if ("id" in selector) {
|
|
@@ -456,37 +638,69 @@ const useComposerClientResource = ({
|
|
|
456
638
|
return attachmentClients.get(selector);
|
|
457
639
|
},
|
|
458
640
|
reset: async () => {
|
|
641
|
+
const removed = attachmentsRef.current;
|
|
459
642
|
setText("");
|
|
460
643
|
setRole("user");
|
|
461
644
|
setRunConfig({});
|
|
462
645
|
setAttachments([]);
|
|
463
646
|
setQuote(undefined);
|
|
647
|
+
await removePendingAttachments(removed);
|
|
464
648
|
},
|
|
465
649
|
send: (opts?: ComposerSendOptions) => {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
attachments: attachments as any,
|
|
473
|
-
createdAt: new Date(),
|
|
474
|
-
parentId: null,
|
|
475
|
-
sourceId: null,
|
|
476
|
-
runConfig,
|
|
477
|
-
startRun: opts?.startRun,
|
|
478
|
-
metadata: {
|
|
479
|
-
custom: { ...(currentQuote ? { quote: currentQuote } : {}) },
|
|
480
|
-
},
|
|
481
|
-
};
|
|
482
|
-
if (queue) {
|
|
483
|
-
queue.enqueue(composedMessage, { steer: opts?.steer ?? false });
|
|
484
|
-
} else {
|
|
485
|
-
onSend?.(composedMessage);
|
|
486
|
-
}
|
|
650
|
+
const currentQuote = quoteRef.current;
|
|
651
|
+
const currentText = textRef.current;
|
|
652
|
+
const currentAttachments = attachmentsRef.current;
|
|
653
|
+
const isEmpty = !currentText.trim() && !currentAttachments.length;
|
|
654
|
+
if (!isEditing || isEmpty || isSendDisabled) return;
|
|
655
|
+
|
|
487
656
|
setText("");
|
|
488
657
|
setAttachments([]);
|
|
489
658
|
setQuote(undefined);
|
|
659
|
+
|
|
660
|
+
const dispatch = (sendAttachments: readonly Attachment[]) => {
|
|
661
|
+
const composedMessage: AppendMessage = {
|
|
662
|
+
role: roleRef.current,
|
|
663
|
+
content: currentText
|
|
664
|
+
? [{ type: "text" as const, text: currentText }]
|
|
665
|
+
: [],
|
|
666
|
+
attachments: sendAttachments as any,
|
|
667
|
+
createdAt: new Date(),
|
|
668
|
+
parentId: null,
|
|
669
|
+
sourceId: null,
|
|
670
|
+
runConfig: runConfigRef.current,
|
|
671
|
+
startRun: opts?.startRun,
|
|
672
|
+
metadata: {
|
|
673
|
+
custom: { ...(currentQuote ? { quote: currentQuote } : {}) },
|
|
674
|
+
},
|
|
675
|
+
};
|
|
676
|
+
if (queue) {
|
|
677
|
+
queue.enqueue(composedMessage, { steer: opts?.steer ?? false });
|
|
678
|
+
} else {
|
|
679
|
+
onSend?.(composedMessage);
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
if (attachmentAdapter && currentAttachments.length > 0) {
|
|
684
|
+
void Promise.all(
|
|
685
|
+
currentAttachments.map((attachment) =>
|
|
686
|
+
attachment.status.type === "complete"
|
|
687
|
+
? attachment
|
|
688
|
+
: attachmentAdapter.send(attachment as PendingAttachment),
|
|
689
|
+
),
|
|
690
|
+
).then(dispatch, (error) => {
|
|
691
|
+
// Upload failed: merge the failed send back into the draft.
|
|
692
|
+
setText((prev) =>
|
|
693
|
+
currentText && prev
|
|
694
|
+
? currentText + "\n" + prev
|
|
695
|
+
: currentText || prev,
|
|
696
|
+
);
|
|
697
|
+
setQuote((prev) => prev ?? currentQuote);
|
|
698
|
+
setAttachments((prev) => [...currentAttachments, ...prev]);
|
|
699
|
+
console.error("Failed to send attachments", error);
|
|
700
|
+
});
|
|
701
|
+
} else {
|
|
702
|
+
dispatch(currentAttachments);
|
|
703
|
+
}
|
|
490
704
|
},
|
|
491
705
|
cancel: onCancel,
|
|
492
706
|
beginEdit: () => {
|
|
@@ -503,20 +717,50 @@ const useComposerClientResource = ({
|
|
|
503
717
|
|
|
504
718
|
const ComposerClientResource = resource(useComposerClientResource);
|
|
505
719
|
|
|
720
|
+
const dedupeMessagesById = (messages: readonly ExternalThreadMessage[]) => {
|
|
721
|
+
const seenIds = new Set<string>();
|
|
722
|
+
const deduped: ExternalThreadMessage[] = [];
|
|
723
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
724
|
+
const message = messages[i]!;
|
|
725
|
+
if (seenIds.has(message.id)) {
|
|
726
|
+
console.warn(
|
|
727
|
+
`ExternalThread: duplicate message id "${message.id}" in the provided messages array; keeping the last occurrence.`,
|
|
728
|
+
);
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
seenIds.add(message.id);
|
|
732
|
+
deduped.push(message);
|
|
733
|
+
}
|
|
734
|
+
return deduped.length === messages.length ? messages : deduped.reverse();
|
|
735
|
+
};
|
|
736
|
+
|
|
506
737
|
// External Thread Client
|
|
507
738
|
const useExternalThread = ({
|
|
508
|
-
messages,
|
|
739
|
+
messages: messagesProp,
|
|
509
740
|
isRunning = false,
|
|
741
|
+
isLoading = false,
|
|
742
|
+
state: threadState,
|
|
743
|
+
extras,
|
|
510
744
|
isSendDisabled = false,
|
|
511
745
|
onNew,
|
|
512
746
|
onEdit,
|
|
513
747
|
onReload,
|
|
514
748
|
onStartRun,
|
|
515
749
|
onCancel,
|
|
750
|
+
onResume,
|
|
751
|
+
onAddToolResult,
|
|
752
|
+
onResumeToolCall,
|
|
753
|
+
onLoadExternalState,
|
|
754
|
+
attachmentAdapter,
|
|
516
755
|
queue,
|
|
517
756
|
branches,
|
|
518
757
|
onRespondToToolApproval,
|
|
519
758
|
}: ExternalThreadProps): ClientOutput<"thread"> => {
|
|
759
|
+
const messages = useMemo(
|
|
760
|
+
() => dedupeMessagesById(messagesProp),
|
|
761
|
+
[messagesProp],
|
|
762
|
+
);
|
|
763
|
+
|
|
520
764
|
const handleReload = (messageId: string) => {
|
|
521
765
|
const messageIndex = messages.findIndex((m) => m.id === messageId);
|
|
522
766
|
if (messageIndex === -1) return;
|
|
@@ -531,10 +775,14 @@ const useExternalThread = ({
|
|
|
531
775
|
const props: MessageClientProps = {
|
|
532
776
|
message: msg,
|
|
533
777
|
index,
|
|
778
|
+
parentId: index > 0 ? messages[index - 1]!.id : null,
|
|
534
779
|
onReload: () => handleReload(msg.id),
|
|
535
780
|
queue,
|
|
536
781
|
branches,
|
|
537
782
|
onRespondToToolApproval,
|
|
783
|
+
onAddToolResult,
|
|
784
|
+
onResumeToolCall,
|
|
785
|
+
attachmentAdapter,
|
|
538
786
|
};
|
|
539
787
|
if (onEdit) props.onEdit = onEdit;
|
|
540
788
|
return withKey(msg.id, MessageClient(props));
|
|
@@ -547,9 +795,25 @@ const useExternalThread = ({
|
|
|
547
795
|
};
|
|
548
796
|
|
|
549
797
|
const handleSendNew = (message: AppendMessage) => {
|
|
550
|
-
|
|
798
|
+
// The composer does not know the thread; stamp the current head as the
|
|
799
|
+
// parent (legacy composer parity).
|
|
800
|
+
onNew?.({ ...message, parentId: messages.at(-1)?.id ?? null });
|
|
551
801
|
};
|
|
552
802
|
|
|
803
|
+
const headId = messages.at(-1)?.id ?? null;
|
|
804
|
+
const composerQueue = useMemo(
|
|
805
|
+
(): ExternalThreadQueueAdapter | undefined =>
|
|
806
|
+
queue && {
|
|
807
|
+
...queue,
|
|
808
|
+
enqueue: (message, options) =>
|
|
809
|
+
queue.enqueue(
|
|
810
|
+
{ ...message, parentId: message.parentId ?? headId },
|
|
811
|
+
options,
|
|
812
|
+
),
|
|
813
|
+
},
|
|
814
|
+
[queue, headId],
|
|
815
|
+
);
|
|
816
|
+
|
|
553
817
|
const composerClient = useClientResource(
|
|
554
818
|
ComposerClientResource({
|
|
555
819
|
type: "thread",
|
|
@@ -558,12 +822,16 @@ const useExternalThread = ({
|
|
|
558
822
|
isSendDisabled,
|
|
559
823
|
onCancel: handleCancelRun,
|
|
560
824
|
onSend: handleSendNew,
|
|
561
|
-
queue,
|
|
825
|
+
queue: composerQueue,
|
|
826
|
+
attachmentAdapter,
|
|
562
827
|
}),
|
|
563
828
|
);
|
|
564
829
|
|
|
565
830
|
const hasQueue = !!queue;
|
|
566
831
|
const hasBranches = !!branches;
|
|
832
|
+
const hasEdit = !!onEdit;
|
|
833
|
+
const hasReload = !!onReload;
|
|
834
|
+
const hasAttachments = !!attachmentAdapter;
|
|
567
835
|
const state = useMemo(() => {
|
|
568
836
|
const messageStates = messageClients.state.map((s, idx, arr) => ({
|
|
569
837
|
...s,
|
|
@@ -573,15 +841,15 @@ const useExternalThread = ({
|
|
|
573
841
|
return {
|
|
574
842
|
isEmpty: messages.length === 0,
|
|
575
843
|
isDisabled: false,
|
|
576
|
-
isLoading
|
|
844
|
+
isLoading,
|
|
577
845
|
isRunning,
|
|
578
846
|
capabilities: {
|
|
579
|
-
edit:
|
|
847
|
+
edit: hasEdit,
|
|
580
848
|
delete: false,
|
|
581
|
-
reload:
|
|
849
|
+
reload: hasReload,
|
|
582
850
|
cancel: isRunning,
|
|
583
851
|
speech: false,
|
|
584
|
-
attachments:
|
|
852
|
+
attachments: hasAttachments,
|
|
585
853
|
feedback: false,
|
|
586
854
|
voice: false,
|
|
587
855
|
switchToBranch: hasBranches,
|
|
@@ -591,9 +859,9 @@ const useExternalThread = ({
|
|
|
591
859
|
queue: hasQueue,
|
|
592
860
|
},
|
|
593
861
|
messages: messageStates,
|
|
594
|
-
state: {},
|
|
862
|
+
state: threadState ?? {},
|
|
595
863
|
suggestions: [],
|
|
596
|
-
extras
|
|
864
|
+
extras,
|
|
597
865
|
speech: undefined,
|
|
598
866
|
voice: undefined,
|
|
599
867
|
composer: composerClient.state,
|
|
@@ -601,8 +869,14 @@ const useExternalThread = ({
|
|
|
601
869
|
}, [
|
|
602
870
|
messages,
|
|
603
871
|
isRunning,
|
|
872
|
+
isLoading,
|
|
873
|
+
threadState,
|
|
874
|
+
extras,
|
|
604
875
|
hasQueue,
|
|
605
876
|
hasBranches,
|
|
877
|
+
hasEdit,
|
|
878
|
+
hasReload,
|
|
879
|
+
hasAttachments,
|
|
606
880
|
messageClients.state,
|
|
607
881
|
composerClient.state,
|
|
608
882
|
]);
|
|
@@ -644,8 +918,21 @@ const useExternalThread = ({
|
|
|
644
918
|
startRun: () => {
|
|
645
919
|
onStartRun?.();
|
|
646
920
|
},
|
|
647
|
-
resumeRun: () => {
|
|
921
|
+
resumeRun: () => {
|
|
922
|
+
if (!onResume)
|
|
923
|
+
throw new Error(
|
|
924
|
+
"Runtime does not support resuming runs (onResume is not set).",
|
|
925
|
+
);
|
|
926
|
+
onResume();
|
|
927
|
+
},
|
|
648
928
|
cancelRun: handleCancelRun,
|
|
929
|
+
importExternalState: (state: unknown) => {
|
|
930
|
+
if (!onLoadExternalState)
|
|
931
|
+
throw new Error(
|
|
932
|
+
"Runtime does not support importing external states (onLoadExternalState is not set).",
|
|
933
|
+
);
|
|
934
|
+
onLoadExternalState(state);
|
|
935
|
+
},
|
|
649
936
|
getModelContext: () => ({ tools: {}, config: {} }),
|
|
650
937
|
export: () => ({ messages: [] }),
|
|
651
938
|
import: () => {},
|
|
@@ -672,10 +959,12 @@ attachTransformScopes(useExternalThread, (scopes, parent) => {
|
|
|
672
959
|
if (!scopes.threads && parent.threads.source === null) {
|
|
673
960
|
const threadElement = scopes.thread as ClientElement<"thread">;
|
|
674
961
|
scopes.threads = SingleThreadList({ thread: threadElement });
|
|
962
|
+
// scopes mount in key order; re-declare thread after the threads source it resolves from
|
|
963
|
+
delete scopes.thread;
|
|
675
964
|
scopes.thread = Derived({
|
|
676
965
|
source: "threads",
|
|
677
966
|
query: { type: "main" },
|
|
678
|
-
get: (aui) => aui.threads
|
|
967
|
+
get: (aui) => aui.threads.thread("main"),
|
|
679
968
|
});
|
|
680
969
|
}
|
|
681
970
|
|
|
@@ -683,14 +972,14 @@ attachTransformScopes(useExternalThread, (scopes, parent) => {
|
|
|
683
972
|
scopes.threadListItem = Derived({
|
|
684
973
|
source: "threads",
|
|
685
974
|
query: { type: "main" },
|
|
686
|
-
get: (aui) => aui.threads
|
|
975
|
+
get: (aui) => aui.threads.item("main"),
|
|
687
976
|
});
|
|
688
977
|
}
|
|
689
978
|
|
|
690
979
|
scopes.composer ??= Derived({
|
|
691
980
|
source: "thread",
|
|
692
981
|
query: {},
|
|
693
|
-
get: (aui) => aui.thread
|
|
982
|
+
get: (aui) => aui.thread.composer(),
|
|
694
983
|
});
|
|
695
984
|
|
|
696
985
|
if (!scopes.modelContext && parent.modelContext.source === null) {
|