@assistant-ui/ai-sdk 0.0.7 → 0.0.8
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/README.md +1 -1
- package/dist/adapters/aiSDKFormatAdapter.d.ts +2 -2
- package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
- package/dist/adapters/vercelAttachmentAdapter.d.ts +1 -3
- package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -1
- package/dist/adapters/vercelAttachmentAdapter.js +3 -3
- package/dist/adapters/vercelAttachmentAdapter.js.map +1 -1
- package/dist/aiSDKExtras.d.ts +4 -6
- package/dist/aiSDKExtras.d.ts.map +1 -1
- package/dist/converters/convertMessage.d.ts +32 -24
- package/dist/converters/convertMessage.d.ts.map +1 -1
- package/dist/converters/convertMessage.js +43 -9
- package/dist/converters/convertMessage.js.map +1 -1
- package/dist/converters/modelContentEnvelope.d.ts +6 -7
- package/dist/converters/modelContentEnvelope.d.ts.map +1 -1
- package/dist/converters/toCreateMessage.d.ts +2 -4
- package/dist/converters/toCreateMessage.d.ts.map +1 -1
- package/dist/converters/toolOutputConversion.d.ts +20 -22
- package/dist/converters/toolOutputConversion.d.ts.map +1 -1
- package/dist/hooks.d.ts +2 -4
- package/dist/hooks.d.ts.map +1 -1
- package/dist/index.d.ts +17 -14
- package/dist/index.d.ts.map +1 -0
- package/dist/index.native.d.ts +15 -12
- package/dist/index.native.d.ts.map +1 -0
- package/dist/model-context/injectInteractableContext.d.ts +2 -4
- package/dist/model-context/injectInteractableContext.d.ts.map +1 -1
- package/dist/model-context/injectQuoteContext.d.ts +1 -3
- package/dist/model-context/injectQuoteContext.d.ts.map +1 -1
- package/dist/runtime/AISDKChat.d.ts +2 -4
- package/dist/runtime/AISDKChat.d.ts.map +1 -1
- package/dist/runtime/AISDKChat.js +1 -1
- package/dist/runtime/AISDKThreads.d.ts +29 -31
- package/dist/runtime/AISDKThreads.d.ts.map +1 -1
- package/dist/runtime/AISDKThreads.js +22 -23
- package/dist/runtime/AISDKThreads.js.map +1 -1
- package/dist/runtime/sdkIdentity.d.ts +1 -3
- package/dist/runtime/sdkIdentity.d.ts.map +1 -1
- package/dist/runtime/sdkIdentity.js +1 -1
- package/dist/runtime/useAISDKRuntime.d.ts +69 -70
- package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
- package/dist/runtime/useAISDKRuntime.js +112 -56
- package/dist/runtime/useAISDKRuntime.js.map +1 -1
- package/dist/runtime/useChatRuntime.d.ts +6 -8
- package/dist/runtime/useChatRuntime.d.ts.map +1 -1
- package/dist/runtime/useChatRuntime.js +14 -10
- package/dist/runtime/useChatRuntime.js.map +1 -1
- package/dist/runtime/useChatThread.d.ts +94 -89
- package/dist/runtime/useChatThread.d.ts.map +1 -1
- package/dist/runtime/useChatThread.js +4 -5
- package/dist/runtime/useChatThread.js.map +1 -1
- package/dist/runtime/useExternalHistory.d.ts +7 -7
- package/dist/runtime/useExternalHistory.d.ts.map +1 -1
- package/dist/runtime/useExternalHistory.js +229 -59
- package/dist/runtime/useExternalHistory.js.map +1 -1
- package/dist/runtime/useResourceCleanup.d.ts +1 -3
- package/dist/runtime/useResourceCleanup.d.ts.map +1 -1
- package/dist/runtime/useResourceCleanup.js +38 -14
- package/dist/runtime/useResourceCleanup.js.map +1 -1
- package/dist/runtime/useStreamingTiming.d.ts +3 -5
- package/dist/runtime/useStreamingTiming.d.ts.map +1 -1
- package/dist/runtime/useStreamingTiming.js +1 -1
- package/dist/tools/frontendTools.d.ts +21 -23
- package/dist/tools/frontendTools.d.ts.map +1 -1
- package/dist/tools/generativeTools.d.ts +24 -26
- package/dist/tools/generativeTools.d.ts.map +1 -1
- package/dist/tools/mcp-stdio.node.d.ts +2 -2
- package/dist/tools/mcp-stdio.node.d.ts.map +1 -0
- package/dist/tools/mcp-stdio.unsupported.d.ts +2 -4
- package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -1
- package/dist/transport/AssistantChatTransport.d.ts +14 -16
- package/dist/transport/AssistantChatTransport.d.ts.map +1 -1
- package/dist/transport/resumable.d.ts +25 -27
- package/dist/transport/resumable.d.ts.map +1 -1
- package/dist/usage.d.ts +8 -10
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js.map +1 -1
- package/dist/utils/getVercelAIMessages.d.ts +2 -4
- package/dist/utils/getVercelAIMessages.d.ts.map +1 -1
- package/dist/utils/sliceMessagesUntil.d.ts +1 -3
- package/dist/utils/sliceMessagesUntil.d.ts.map +1 -1
- package/package.json +9 -8
- package/src/adapters/vercelAttachmentAdapter.test.ts +23 -0
- package/src/adapters/vercelAttachmentAdapter.ts +2 -2
- package/src/converters/convertMessage.test.ts +478 -3
- package/src/converters/convertMessage.ts +100 -14
- package/src/index.native.ts +1 -1
- package/src/index.ts +1 -1
- package/src/runtime/AISDKThreads.cloud.test.ts +60 -2
- package/src/runtime/AISDKThreads.test.ts +79 -46
- package/src/runtime/AISDKThreads.ts +1 -1
- package/src/runtime/useAISDKRuntime.approval.integration.test.tsx +51 -0
- package/src/runtime/useAISDKRuntime.approval.test.tsx +225 -9
- package/src/runtime/useAISDKRuntime.test.ts +275 -5
- package/src/runtime/useAISDKRuntime.ts +141 -37
- package/src/runtime/useAISDKRuntime.voice.test.tsx +50 -1
- package/src/runtime/useChatRuntime.integration.test.tsx +56 -0
- package/src/runtime/useChatRuntime.test.ts +5 -1
- package/src/runtime/useChatRuntime.ts +6 -2
- package/src/runtime/useChatThread.transport.test.tsx +149 -0
- package/src/runtime/useChatThread.ts +33 -5
- package/src/runtime/useExternalHistory.test.ts +943 -3
- package/src/runtime/useExternalHistory.ts +366 -14
- package/src/runtime/useResourceCleanup.ts +41 -15
- package/src/usage.ts +1 -1
|
@@ -8,9 +8,14 @@ import type {
|
|
|
8
8
|
MessageFormatItem,
|
|
9
9
|
MessageFormatRepository,
|
|
10
10
|
ExportedMessageRepository,
|
|
11
|
+
RespondToToolApprovalOptions,
|
|
12
|
+
Unstable_ToolInteractionLog,
|
|
11
13
|
} from "@assistant-ui/core";
|
|
12
14
|
import { getExternalStoreMessages } from "@assistant-ui/core";
|
|
13
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
MessageRepository,
|
|
17
|
+
readToolInteractionLog,
|
|
18
|
+
} from "@assistant-ui/core/internal";
|
|
14
19
|
import { useAui } from "@assistant-ui/store";
|
|
15
20
|
import {
|
|
16
21
|
useRef,
|
|
@@ -55,6 +60,206 @@ const isTerminalMessage = (message: ThreadMessage) =>
|
|
|
55
60
|
message.status.type === "complete" ||
|
|
56
61
|
message.status.type === "incomplete";
|
|
57
62
|
|
|
63
|
+
const TOOL_ARTIFACTS_METADATA_KEY = "__aui_toolArtifacts";
|
|
64
|
+
const TOOL_INTERACTIONS_METADATA_KEY = "__aui_toolInteractions";
|
|
65
|
+
const TOOL_APPROVAL_RESPONSES_METADATA_KEY = "__aui_toolApprovalResponses";
|
|
66
|
+
|
|
67
|
+
type StoredToolApprovalResponse = Omit<
|
|
68
|
+
RespondToToolApprovalOptions,
|
|
69
|
+
"approvalId"
|
|
70
|
+
>;
|
|
71
|
+
|
|
72
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
73
|
+
value !== null && typeof value === "object" && !Array.isArray(value);
|
|
74
|
+
|
|
75
|
+
const collectToolArtifacts = (
|
|
76
|
+
message: ThreadMessage,
|
|
77
|
+
toolArtifacts: ReadonlyMap<string, unknown> | undefined,
|
|
78
|
+
) => {
|
|
79
|
+
if (!toolArtifacts) return undefined;
|
|
80
|
+
const entries = message.content.flatMap((part) => {
|
|
81
|
+
if (part.type !== "tool-call") return [];
|
|
82
|
+
const artifact = toolArtifacts.get(part.toolCallId);
|
|
83
|
+
return artifact === undefined ? [] : [[part.toolCallId, artifact] as const];
|
|
84
|
+
});
|
|
85
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const collectToolInteractions = (
|
|
89
|
+
message: ThreadMessage,
|
|
90
|
+
toolInteractions:
|
|
91
|
+
| ReadonlyMap<string, Unstable_ToolInteractionLog>
|
|
92
|
+
| undefined,
|
|
93
|
+
) => {
|
|
94
|
+
if (!toolInteractions) return undefined;
|
|
95
|
+
const entries = message.content.flatMap((part) => {
|
|
96
|
+
if (part.type !== "tool-call") return [];
|
|
97
|
+
const interactions = toolInteractions.get(part.toolCallId);
|
|
98
|
+
return interactions === undefined
|
|
99
|
+
? []
|
|
100
|
+
: [[part.toolCallId, interactions] as const];
|
|
101
|
+
});
|
|
102
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const collectToolApprovalResponses = (
|
|
106
|
+
message: ThreadMessage,
|
|
107
|
+
toolApprovalResponses:
|
|
108
|
+
| ReadonlyMap<string, RespondToToolApprovalOptions>
|
|
109
|
+
| undefined,
|
|
110
|
+
) => {
|
|
111
|
+
if (!toolApprovalResponses) return undefined;
|
|
112
|
+
const entries = message.content.flatMap((part) => {
|
|
113
|
+
if (part.type !== "tool-call" || !part.approval) return [];
|
|
114
|
+
const response = toolApprovalResponses.get(part.approval.id);
|
|
115
|
+
if (!response) return [];
|
|
116
|
+
return [
|
|
117
|
+
[
|
|
118
|
+
part.approval.id,
|
|
119
|
+
{
|
|
120
|
+
approved: response.approved,
|
|
121
|
+
...(response.optionId != null && { optionId: response.optionId }),
|
|
122
|
+
...(response.text != null && { text: response.text }),
|
|
123
|
+
...(response.reason != null && { reason: response.reason }),
|
|
124
|
+
},
|
|
125
|
+
] as const,
|
|
126
|
+
];
|
|
127
|
+
});
|
|
128
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const addToolData = <TMessage>(
|
|
132
|
+
message: TMessage,
|
|
133
|
+
toolArtifacts: Record<string, unknown> | undefined,
|
|
134
|
+
toolInteractions: Record<string, Unstable_ToolInteractionLog> | undefined,
|
|
135
|
+
toolApprovalResponses: Record<string, StoredToolApprovalResponse> | undefined,
|
|
136
|
+
): TMessage => {
|
|
137
|
+
if (
|
|
138
|
+
(!toolArtifacts && !toolInteractions && !toolApprovalResponses) ||
|
|
139
|
+
!isRecord(message) ||
|
|
140
|
+
!Array.isArray(message.parts)
|
|
141
|
+
)
|
|
142
|
+
return message;
|
|
143
|
+
const toolCallIds = message.parts.flatMap((part) => {
|
|
144
|
+
if (!isRecord(part) || typeof part.toolCallId !== "string") return [];
|
|
145
|
+
return [part.toolCallId];
|
|
146
|
+
});
|
|
147
|
+
const artifacts = toolArtifacts
|
|
148
|
+
? Object.fromEntries(
|
|
149
|
+
toolCallIds.flatMap((toolCallId) =>
|
|
150
|
+
Object.hasOwn(toolArtifacts, toolCallId)
|
|
151
|
+
? [[toolCallId, toolArtifacts[toolCallId]] as const]
|
|
152
|
+
: [],
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
: undefined;
|
|
156
|
+
const interactions = toolInteractions
|
|
157
|
+
? Object.fromEntries(
|
|
158
|
+
toolCallIds.flatMap((toolCallId) =>
|
|
159
|
+
Object.hasOwn(toolInteractions, toolCallId)
|
|
160
|
+
? [[toolCallId, toolInteractions[toolCallId]] as const]
|
|
161
|
+
: [],
|
|
162
|
+
),
|
|
163
|
+
)
|
|
164
|
+
: undefined;
|
|
165
|
+
const approvalIds = message.parts.flatMap((part) => {
|
|
166
|
+
if (!isRecord(part) || !isRecord(part.approval)) return [];
|
|
167
|
+
const approvalId = part.approval.id;
|
|
168
|
+
return typeof approvalId === "string" ? [approvalId] : [];
|
|
169
|
+
});
|
|
170
|
+
const approvalResponses = toolApprovalResponses
|
|
171
|
+
? Object.fromEntries(
|
|
172
|
+
approvalIds.flatMap((approvalId) =>
|
|
173
|
+
Object.hasOwn(toolApprovalResponses, approvalId)
|
|
174
|
+
? [[approvalId, toolApprovalResponses[approvalId]] as const]
|
|
175
|
+
: [],
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
: undefined;
|
|
179
|
+
const hasArtifacts = !!artifacts && Object.keys(artifacts).length > 0;
|
|
180
|
+
const hasInteractions =
|
|
181
|
+
!!interactions && Object.keys(interactions).length > 0;
|
|
182
|
+
const hasApprovalResponses =
|
|
183
|
+
!!approvalResponses && Object.keys(approvalResponses).length > 0;
|
|
184
|
+
if (!hasArtifacts && !hasInteractions && !hasApprovalResponses)
|
|
185
|
+
return message;
|
|
186
|
+
const metadata = isRecord(message.metadata) ? message.metadata : {};
|
|
187
|
+
return {
|
|
188
|
+
...message,
|
|
189
|
+
metadata: {
|
|
190
|
+
...metadata,
|
|
191
|
+
...(hasArtifacts && { [TOOL_ARTIFACTS_METADATA_KEY]: artifacts }),
|
|
192
|
+
...(hasInteractions && {
|
|
193
|
+
[TOOL_INTERACTIONS_METADATA_KEY]: interactions,
|
|
194
|
+
}),
|
|
195
|
+
...(hasApprovalResponses && {
|
|
196
|
+
[TOOL_APPROVAL_RESPONSES_METADATA_KEY]: approvalResponses,
|
|
197
|
+
}),
|
|
198
|
+
},
|
|
199
|
+
} as TMessage;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const restoreToolData = <TMessage>(
|
|
203
|
+
message: TMessage,
|
|
204
|
+
toolArtifacts: Map<string, unknown> | undefined,
|
|
205
|
+
toolInteractions: Map<string, Unstable_ToolInteractionLog> | undefined,
|
|
206
|
+
toolApprovalResponses: Map<string, RespondToToolApprovalOptions> | undefined,
|
|
207
|
+
): TMessage => {
|
|
208
|
+
if (!isRecord(message) || !isRecord(message.metadata)) return message;
|
|
209
|
+
const metadata = message.metadata;
|
|
210
|
+
const hasArtifacts = Object.hasOwn(metadata, TOOL_ARTIFACTS_METADATA_KEY);
|
|
211
|
+
const hasInteractions = Object.hasOwn(
|
|
212
|
+
metadata,
|
|
213
|
+
TOOL_INTERACTIONS_METADATA_KEY,
|
|
214
|
+
);
|
|
215
|
+
const hasApprovalResponses = Object.hasOwn(
|
|
216
|
+
metadata,
|
|
217
|
+
TOOL_APPROVAL_RESPONSES_METADATA_KEY,
|
|
218
|
+
);
|
|
219
|
+
if (!hasArtifacts && !hasInteractions && !hasApprovalResponses)
|
|
220
|
+
return message;
|
|
221
|
+
const artifacts = metadata[TOOL_ARTIFACTS_METADATA_KEY];
|
|
222
|
+
if (toolArtifacts && isRecord(artifacts)) {
|
|
223
|
+
for (const [toolCallId, artifact] of Object.entries(artifacts)) {
|
|
224
|
+
toolArtifacts.set(toolCallId, artifact);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const interactions = metadata[TOOL_INTERACTIONS_METADATA_KEY];
|
|
228
|
+
if (toolInteractions && isRecord(interactions)) {
|
|
229
|
+
for (const [toolCallId, value] of Object.entries(interactions)) {
|
|
230
|
+
const log = readToolInteractionLog(value);
|
|
231
|
+
if (log) toolInteractions.set(toolCallId, log);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const approvalResponses = metadata[TOOL_APPROVAL_RESPONSES_METADATA_KEY];
|
|
235
|
+
if (toolApprovalResponses && isRecord(approvalResponses)) {
|
|
236
|
+
for (const [approvalId, value] of Object.entries(approvalResponses)) {
|
|
237
|
+
if (!isRecord(value) || typeof value.approved !== "boolean") continue;
|
|
238
|
+
toolApprovalResponses.set(approvalId, {
|
|
239
|
+
approvalId,
|
|
240
|
+
approved: value.approved,
|
|
241
|
+
...(typeof value.optionId === "string" && {
|
|
242
|
+
optionId: value.optionId,
|
|
243
|
+
}),
|
|
244
|
+
...(typeof value.text === "string" && { text: value.text }),
|
|
245
|
+
...(typeof value.reason === "string" && { reason: value.reason }),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const {
|
|
250
|
+
[TOOL_ARTIFACTS_METADATA_KEY]: _,
|
|
251
|
+
[TOOL_INTERACTIONS_METADATA_KEY]: __,
|
|
252
|
+
[TOOL_APPROVAL_RESPONSES_METADATA_KEY]: ___,
|
|
253
|
+
...restMetadata
|
|
254
|
+
} = metadata;
|
|
255
|
+
const { metadata: _metadata, ...restMessage } = message;
|
|
256
|
+
return (
|
|
257
|
+
Object.keys(restMetadata).length === 0
|
|
258
|
+
? restMessage
|
|
259
|
+
: { ...restMessage, metadata: restMetadata }
|
|
260
|
+
) as TMessage;
|
|
261
|
+
};
|
|
262
|
+
|
|
58
263
|
const encodeContent = <TMessage>(
|
|
59
264
|
storageFormatAdapter: MessageFormatAdapter<TMessage, any>,
|
|
60
265
|
item: MessageFormatItem<TMessage>,
|
|
@@ -66,6 +271,12 @@ export const useExternalHistory = <TMessage>(
|
|
|
66
271
|
toThreadMessages: (messages: TMessage[]) => ThreadMessage[],
|
|
67
272
|
storageFormatAdapter: MessageFormatAdapter<TMessage, any>,
|
|
68
273
|
onSetMessages: (messages: TMessage[]) => void,
|
|
274
|
+
toolArtifacts?: Map<string, unknown>,
|
|
275
|
+
onToolArtifactsRestored?: () => void,
|
|
276
|
+
toolInteractions?: Map<string, Unstable_ToolInteractionLog>,
|
|
277
|
+
onToolInteractionsRestored?: () => void,
|
|
278
|
+
toolApprovalResponses?: Map<string, RespondToToolApprovalOptions>,
|
|
279
|
+
onToolApprovalResponsesRestored?: () => void,
|
|
69
280
|
) => {
|
|
70
281
|
const loadedRef = useRef(false);
|
|
71
282
|
const [itemEpoch, setItemEpoch] = useState(0);
|
|
@@ -108,17 +319,37 @@ export const useExternalHistory = <TMessage>(
|
|
|
108
319
|
const loadHistory = async () => {
|
|
109
320
|
try {
|
|
110
321
|
const repo = await formatAdapter.load();
|
|
111
|
-
|
|
112
|
-
|
|
322
|
+
toolArtifacts?.clear();
|
|
323
|
+
toolInteractions?.clear();
|
|
324
|
+
toolApprovalResponses?.clear();
|
|
325
|
+
const restoredMessages =
|
|
326
|
+
repo?.messages.map((item) => ({
|
|
327
|
+
...item,
|
|
328
|
+
message: restoreToolData(
|
|
329
|
+
item.message,
|
|
330
|
+
toolArtifacts,
|
|
331
|
+
toolInteractions,
|
|
332
|
+
toolApprovalResponses,
|
|
333
|
+
),
|
|
334
|
+
})) ?? [];
|
|
335
|
+
onToolArtifactsRestored?.();
|
|
336
|
+
onToolInteractionsRestored?.();
|
|
337
|
+
onToolApprovalResponsesRestored?.();
|
|
338
|
+
if (repo && restoredMessages.length > 0) {
|
|
339
|
+
const restoredRepo = { ...repo, messages: restoredMessages };
|
|
340
|
+
for (const [index, m] of repo.messages.entries()) {
|
|
113
341
|
persistedInnerMessages.current.set(
|
|
114
342
|
storageFormatAdapter.getId(m.message),
|
|
115
343
|
{
|
|
116
|
-
source:
|
|
344
|
+
source: restoredMessages[index]!.message,
|
|
117
345
|
content: encodeContent(storageFormatAdapter, m),
|
|
118
346
|
},
|
|
119
347
|
);
|
|
120
348
|
}
|
|
121
|
-
const converted = toExportedMessageRepository(
|
|
349
|
+
const converted = toExportedMessageRepository(
|
|
350
|
+
toThreadMessages,
|
|
351
|
+
restoredRepo,
|
|
352
|
+
);
|
|
122
353
|
runtimeRef.current.thread.import(converted);
|
|
123
354
|
|
|
124
355
|
const tempRepo = new MessageRepository();
|
|
@@ -177,6 +408,12 @@ export const useExternalHistory = <TMessage>(
|
|
|
177
408
|
aui,
|
|
178
409
|
itemEpoch,
|
|
179
410
|
storageFormatAdapter,
|
|
411
|
+
toolArtifacts,
|
|
412
|
+
onToolArtifactsRestored,
|
|
413
|
+
toolInteractions,
|
|
414
|
+
onToolInteractionsRestored,
|
|
415
|
+
toolApprovalResponses,
|
|
416
|
+
onToolApprovalResponsesRestored,
|
|
180
417
|
]);
|
|
181
418
|
|
|
182
419
|
const runStartRef = useRef<number | null>(null);
|
|
@@ -186,6 +423,84 @@ export const useExternalHistory = <TMessage>(
|
|
|
186
423
|
const wasRunningRef = useRef(false);
|
|
187
424
|
const toolCallCountRef = useRef(0);
|
|
188
425
|
|
|
426
|
+
const persistToolInteractions = useCallback(
|
|
427
|
+
(messageId: string) => {
|
|
428
|
+
const persistence = persistInFlightRef.current.then(async () => {
|
|
429
|
+
if (!formatAdapter?.update) return;
|
|
430
|
+
const messages = runtimeRef.current.thread.getState().messages;
|
|
431
|
+
const message = messages.find(
|
|
432
|
+
(item) =>
|
|
433
|
+
item.id === messageId ||
|
|
434
|
+
getExternalStoreMessages<TMessage>(item).some(
|
|
435
|
+
(innerMessage) =>
|
|
436
|
+
storageFormatAdapter.getId(innerMessage) === messageId,
|
|
437
|
+
),
|
|
438
|
+
);
|
|
439
|
+
if (!message) return;
|
|
440
|
+
|
|
441
|
+
const previousMessages = messages
|
|
442
|
+
.slice(0, messages.indexOf(message))
|
|
443
|
+
.flatMap(getExternalStoreMessages<TMessage>);
|
|
444
|
+
let parentId = previousMessages.at(-1)
|
|
445
|
+
? storageFormatAdapter.getId(previousMessages.at(-1)!)
|
|
446
|
+
: null;
|
|
447
|
+
const storedToolArtifacts = collectToolArtifacts(
|
|
448
|
+
message,
|
|
449
|
+
toolArtifacts,
|
|
450
|
+
);
|
|
451
|
+
const storedToolInteractions = collectToolInteractions(
|
|
452
|
+
message,
|
|
453
|
+
toolInteractions,
|
|
454
|
+
);
|
|
455
|
+
const storedToolApprovalResponses = collectToolApprovalResponses(
|
|
456
|
+
message,
|
|
457
|
+
toolApprovalResponses,
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
for (const innerMessage of getExternalStoreMessages<TMessage>(
|
|
461
|
+
message,
|
|
462
|
+
)) {
|
|
463
|
+
const item = {
|
|
464
|
+
parentId,
|
|
465
|
+
message: addToolData(
|
|
466
|
+
innerMessage,
|
|
467
|
+
storedToolArtifacts,
|
|
468
|
+
storedToolInteractions,
|
|
469
|
+
storedToolApprovalResponses,
|
|
470
|
+
),
|
|
471
|
+
};
|
|
472
|
+
const innerId = storageFormatAdapter.getId(item.message);
|
|
473
|
+
const persisted = persistedInnerMessages.current.get(innerId);
|
|
474
|
+
if (persisted) {
|
|
475
|
+
const content = encodeContent(storageFormatAdapter, item);
|
|
476
|
+
if (content === persisted.content) {
|
|
477
|
+
persisted.source = item.message;
|
|
478
|
+
} else {
|
|
479
|
+
await formatAdapter.update(item, innerId);
|
|
480
|
+
persistedInnerMessages.current.set(innerId, {
|
|
481
|
+
source: item.message,
|
|
482
|
+
content,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
parentId = innerId;
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
persistInFlightRef.current = persistence.catch(() => {});
|
|
490
|
+
return persistence.catch((error) => {
|
|
491
|
+
console.error("Failed to persist tool data:", error);
|
|
492
|
+
});
|
|
493
|
+
},
|
|
494
|
+
[
|
|
495
|
+
formatAdapter,
|
|
496
|
+
runtimeRef,
|
|
497
|
+
storageFormatAdapter,
|
|
498
|
+
toolArtifacts,
|
|
499
|
+
toolInteractions,
|
|
500
|
+
toolApprovalResponses,
|
|
501
|
+
],
|
|
502
|
+
);
|
|
503
|
+
|
|
189
504
|
useEffect(() => {
|
|
190
505
|
if (!formatAdapter) return;
|
|
191
506
|
const adapter = formatAdapter;
|
|
@@ -318,13 +633,27 @@ export const useExternalHistory = <TMessage>(
|
|
|
318
633
|
const getLastInnerId = (msgs: TMessage[]): string | null =>
|
|
319
634
|
msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)!) : null;
|
|
320
635
|
|
|
321
|
-
const toBatchItems = (
|
|
636
|
+
const toBatchItems = (
|
|
637
|
+
msgs: TMessage[],
|
|
638
|
+
toolArtifacts: Record<string, unknown> | undefined,
|
|
639
|
+
toolInteractions:
|
|
640
|
+
| Record<string, Unstable_ToolInteractionLog>
|
|
641
|
+
| undefined,
|
|
642
|
+
toolApprovalResponses:
|
|
643
|
+
| Record<string, StoredToolApprovalResponse>
|
|
644
|
+
| undefined,
|
|
645
|
+
) =>
|
|
322
646
|
msgs.map((msg, idx) => ({
|
|
323
647
|
parentId:
|
|
324
648
|
idx === 0
|
|
325
649
|
? lastInnerMessageId
|
|
326
650
|
: storageFormatAdapter.getId(msgs[idx - 1]!),
|
|
327
|
-
message:
|
|
651
|
+
message: addToolData(
|
|
652
|
+
msg,
|
|
653
|
+
toolArtifacts,
|
|
654
|
+
toolInteractions,
|
|
655
|
+
toolApprovalResponses,
|
|
656
|
+
),
|
|
328
657
|
}));
|
|
329
658
|
|
|
330
659
|
for (const message of messages) {
|
|
@@ -348,7 +677,12 @@ export const useExternalHistory = <TMessage>(
|
|
|
348
677
|
deferredTelemetryIds.current.add(message.id);
|
|
349
678
|
}
|
|
350
679
|
|
|
351
|
-
const batchItems = toBatchItems(
|
|
680
|
+
const batchItems = toBatchItems(
|
|
681
|
+
innerMessages,
|
|
682
|
+
collectToolArtifacts(message, toolArtifacts),
|
|
683
|
+
collectToolInteractions(message, toolInteractions),
|
|
684
|
+
collectToolApprovalResponses(message, toolApprovalResponses),
|
|
685
|
+
);
|
|
352
686
|
for (const item of batchItems) {
|
|
353
687
|
const innerId = storageFormatAdapter.getId(item.message);
|
|
354
688
|
const persisted = persistedInnerMessages.current.get(innerId);
|
|
@@ -385,10 +719,16 @@ export const useExternalHistory = <TMessage>(
|
|
|
385
719
|
|
|
386
720
|
if (deferredTelemetryIds.current.has(message.id) && isTerminal) {
|
|
387
721
|
deferredTelemetryIds.current.delete(message.id);
|
|
388
|
-
adapter.reportTelemetry?.(
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
722
|
+
adapter.reportTelemetry?.(
|
|
723
|
+
batchItems.map((item, index) => ({
|
|
724
|
+
...item,
|
|
725
|
+
message: innerMessages[index]!,
|
|
726
|
+
})),
|
|
727
|
+
{
|
|
728
|
+
...telemetryOptions,
|
|
729
|
+
message,
|
|
730
|
+
},
|
|
731
|
+
);
|
|
392
732
|
}
|
|
393
733
|
}
|
|
394
734
|
})
|
|
@@ -405,7 +745,14 @@ export const useExternalHistory = <TMessage>(
|
|
|
405
745
|
persistSettled(false);
|
|
406
746
|
}
|
|
407
747
|
};
|
|
408
|
-
}, [
|
|
748
|
+
}, [
|
|
749
|
+
formatAdapter,
|
|
750
|
+
storageFormatAdapter,
|
|
751
|
+
runtimeRef,
|
|
752
|
+
toolArtifacts,
|
|
753
|
+
toolInteractions,
|
|
754
|
+
toolApprovalResponses,
|
|
755
|
+
]);
|
|
409
756
|
|
|
410
757
|
const deleteMessage = useCallback(
|
|
411
758
|
async (messageId: string) => {
|
|
@@ -448,5 +795,10 @@ export const useExternalHistory = <TMessage>(
|
|
|
448
795
|
[formatAdapter, runtimeRef, storageFormatAdapter],
|
|
449
796
|
);
|
|
450
797
|
|
|
451
|
-
return {
|
|
798
|
+
return {
|
|
799
|
+
isLoading,
|
|
800
|
+
deleteMessage,
|
|
801
|
+
persistToolInteractions,
|
|
802
|
+
persistToolApprovalResponses: persistToolInteractions,
|
|
803
|
+
};
|
|
452
804
|
};
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
2
|
import { useAssistantClientDestroySignal } from "@assistant-ui/store/internal";
|
|
3
3
|
|
|
4
|
-
export const useResourceCleanup = (
|
|
4
|
+
export const useResourceCleanup = (
|
|
5
|
+
enabled: boolean,
|
|
6
|
+
cleanup: () => void,
|
|
7
|
+
hostDestroySignal?: AbortSignal,
|
|
8
|
+
) => {
|
|
5
9
|
const destroySignal = useAssistantClientDestroySignal();
|
|
6
10
|
const cleanupRef = useRef(cleanup);
|
|
7
11
|
const enabledRef = useRef(enabled);
|
|
8
|
-
const
|
|
12
|
+
const registrationRef = useRef<{
|
|
13
|
+
destroySignal: AbortSignal | undefined;
|
|
14
|
+
hostDestroySignal: AbortSignal | undefined;
|
|
15
|
+
controller: AbortController;
|
|
16
|
+
} | null>(null);
|
|
9
17
|
|
|
10
18
|
useEffect(() => {
|
|
11
19
|
cleanupRef.current = cleanup;
|
|
@@ -13,20 +21,38 @@ export const useResourceCleanup = (enabled: boolean, cleanup: () => void) => {
|
|
|
13
21
|
});
|
|
14
22
|
|
|
15
23
|
useEffect(() => {
|
|
16
|
-
if (!enabled || !destroySignal) return undefined;
|
|
17
|
-
|
|
24
|
+
if (!enabled || (!destroySignal && !hostDestroySignal)) return undefined;
|
|
25
|
+
const current = registrationRef.current;
|
|
26
|
+
if (
|
|
27
|
+
current !== null &&
|
|
28
|
+
current.destroySignal === destroySignal &&
|
|
29
|
+
current.hostDestroySignal === hostDestroySignal
|
|
30
|
+
) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
current?.controller.abort();
|
|
18
34
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
const registration = new AbortController();
|
|
36
|
+
registrationRef.current = {
|
|
37
|
+
destroySignal,
|
|
38
|
+
hostDestroySignal,
|
|
39
|
+
controller: registration,
|
|
40
|
+
};
|
|
41
|
+
const run = () => {
|
|
42
|
+
if (registration.signal.aborted) return;
|
|
43
|
+
registration.abort();
|
|
44
|
+
if (enabledRef.current) cleanupRef.current();
|
|
45
|
+
};
|
|
46
|
+
if (destroySignal?.aborted || hostDestroySignal?.aborted) {
|
|
47
|
+
run();
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const options = { once: true, signal: registration.signal };
|
|
51
|
+
destroySignal?.addEventListener("abort", run, options);
|
|
52
|
+
hostDestroySignal?.addEventListener("abort", run, options);
|
|
27
53
|
|
|
28
|
-
// The
|
|
29
|
-
//
|
|
54
|
+
// The listeners must survive standalone soft unmounts so a later
|
|
55
|
+
// permanent destroy still cleans up the retained resource state.
|
|
30
56
|
return undefined;
|
|
31
|
-
}, [destroySignal, enabled]);
|
|
57
|
+
}, [destroySignal, enabled, hostDestroySignal]);
|
|
32
58
|
};
|
package/src/usage.ts
CHANGED