@assistant-ui/ai-sdk 0.0.7 → 0.0.9
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/LICENSE +1 -1
- 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 +34 -24
- package/dist/converters/convertMessage.d.ts.map +1 -1
- package/dist/converters/convertMessage.js +56 -12
- 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/AISDKChat.js.map +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 +76 -70
- package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
- package/dist/runtime/useAISDKRuntime.js +311 -77
- 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 +44 -15
- 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 +231 -60
- 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/AssistantChatTransport.js +9 -2
- package/dist/transport/AssistantChatTransport.js.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 +13 -9
- package/src/adapters/vercelAttachmentAdapter.test.ts +23 -0
- package/src/adapters/vercelAttachmentAdapter.ts +2 -2
- package/src/converters/convertMessage.test.ts +541 -3
- package/src/converters/convertMessage.ts +123 -15
- package/src/index.native.ts +1 -1
- package/src/index.ts +1 -1
- package/src/runtime/AISDKChat.ts +0 -4
- package/src/runtime/AISDKThreads.cloud.test.ts +60 -2
- package/src/runtime/AISDKThreads.test.ts +105 -46
- package/src/runtime/AISDKThreads.ts +1 -1
- package/src/runtime/useAISDKRuntime.approval.integration.test.tsx +1639 -19
- package/src/runtime/useAISDKRuntime.approval.test.tsx +252 -9
- package/src/runtime/useAISDKRuntime.fast-refresh.test.tsx +182 -0
- package/src/runtime/useAISDKRuntime.test.ts +275 -5
- package/src/runtime/useAISDKRuntime.ts +533 -60
- package/src/runtime/useAISDKRuntime.voice.test.tsx +50 -1
- package/src/runtime/useChatRuntime.fast-refresh.test.tsx +94 -0
- package/src/runtime/useChatRuntime.integration.test.tsx +143 -65
- package/src/runtime/useChatRuntime.test.ts +11 -7
- package/src/runtime/useChatRuntime.ts +6 -2
- package/src/runtime/useChatThread.test.ts +166 -2
- package/src/runtime/useChatThread.transport.test.tsx +152 -0
- package/src/runtime/useChatThread.ts +89 -22
- package/src/runtime/useExternalHistory.test.ts +943 -3
- package/src/runtime/useExternalHistory.ts +378 -15
- package/src/runtime/useResourceCleanup.ts +41 -15
- package/src/transport/AssistantChatTransport.test.ts +164 -0
- package/src/transport/AssistantChatTransport.ts +22 -2
- 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,
|
|
@@ -46,15 +51,226 @@ export const toExportedMessageRepository = <TMessage>(
|
|
|
46
51
|
};
|
|
47
52
|
};
|
|
48
53
|
|
|
54
|
+
const hasUnansweredApproval = (message: ThreadMessage) =>
|
|
55
|
+
message.content.some(
|
|
56
|
+
(part) =>
|
|
57
|
+
part.type === "tool-call" &&
|
|
58
|
+
part.approval != null &&
|
|
59
|
+
part.approval.approved === undefined &&
|
|
60
|
+
part.approval.resolution === undefined,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// Core reports a run paused on an unanswered approval as an interrupt, not as tool calls.
|
|
49
64
|
const isAwaitingToolApproval = (message: ThreadMessage) =>
|
|
50
65
|
message.status?.type === "requires-action" &&
|
|
51
|
-
message.status.reason === "tool-calls"
|
|
66
|
+
(message.status.reason === "tool-calls" ||
|
|
67
|
+
(message.status.reason === "interrupt" && hasUnansweredApproval(message)));
|
|
52
68
|
|
|
53
69
|
const isTerminalMessage = (message: ThreadMessage) =>
|
|
54
70
|
message.status === undefined ||
|
|
55
71
|
message.status.type === "complete" ||
|
|
56
72
|
message.status.type === "incomplete";
|
|
57
73
|
|
|
74
|
+
const TOOL_ARTIFACTS_METADATA_KEY = "__aui_toolArtifacts";
|
|
75
|
+
const TOOL_INTERACTIONS_METADATA_KEY = "__aui_toolInteractions";
|
|
76
|
+
const TOOL_APPROVAL_RESPONSES_METADATA_KEY = "__aui_toolApprovalResponses";
|
|
77
|
+
|
|
78
|
+
type StoredToolApprovalResponse = Omit<
|
|
79
|
+
RespondToToolApprovalOptions,
|
|
80
|
+
"approvalId"
|
|
81
|
+
>;
|
|
82
|
+
|
|
83
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
84
|
+
value !== null && typeof value === "object" && !Array.isArray(value);
|
|
85
|
+
|
|
86
|
+
const collectToolArtifacts = (
|
|
87
|
+
message: ThreadMessage,
|
|
88
|
+
toolArtifacts: ReadonlyMap<string, unknown> | undefined,
|
|
89
|
+
) => {
|
|
90
|
+
if (!toolArtifacts) return undefined;
|
|
91
|
+
const entries = message.content.flatMap((part) => {
|
|
92
|
+
if (part.type !== "tool-call") return [];
|
|
93
|
+
const artifact = toolArtifacts.get(part.toolCallId);
|
|
94
|
+
return artifact === undefined ? [] : [[part.toolCallId, artifact] as const];
|
|
95
|
+
});
|
|
96
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const collectToolInteractions = (
|
|
100
|
+
message: ThreadMessage,
|
|
101
|
+
toolInteractions:
|
|
102
|
+
| ReadonlyMap<string, Unstable_ToolInteractionLog>
|
|
103
|
+
| undefined,
|
|
104
|
+
) => {
|
|
105
|
+
if (!toolInteractions) return undefined;
|
|
106
|
+
const entries = message.content.flatMap((part) => {
|
|
107
|
+
if (part.type !== "tool-call") return [];
|
|
108
|
+
const interactions = toolInteractions.get(part.toolCallId);
|
|
109
|
+
return interactions === undefined
|
|
110
|
+
? []
|
|
111
|
+
: [[part.toolCallId, interactions] as const];
|
|
112
|
+
});
|
|
113
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const collectToolApprovalResponses = (
|
|
117
|
+
message: ThreadMessage,
|
|
118
|
+
toolApprovalResponses:
|
|
119
|
+
| ReadonlyMap<string, RespondToToolApprovalOptions>
|
|
120
|
+
| undefined,
|
|
121
|
+
) => {
|
|
122
|
+
if (!toolApprovalResponses) return undefined;
|
|
123
|
+
const entries = message.content.flatMap((part) => {
|
|
124
|
+
if (part.type !== "tool-call" || !part.approval) return [];
|
|
125
|
+
const response = toolApprovalResponses.get(part.approval.id);
|
|
126
|
+
if (!response) return [];
|
|
127
|
+
return [
|
|
128
|
+
[
|
|
129
|
+
part.approval.id,
|
|
130
|
+
{
|
|
131
|
+
approved: response.approved,
|
|
132
|
+
...(response.optionId != null && { optionId: response.optionId }),
|
|
133
|
+
...(response.text != null && { text: response.text }),
|
|
134
|
+
...(response.reason != null && { reason: response.reason }),
|
|
135
|
+
},
|
|
136
|
+
] as const,
|
|
137
|
+
];
|
|
138
|
+
});
|
|
139
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const addToolData = <TMessage>(
|
|
143
|
+
message: TMessage,
|
|
144
|
+
toolArtifacts: Record<string, unknown> | undefined,
|
|
145
|
+
toolInteractions: Record<string, Unstable_ToolInteractionLog> | undefined,
|
|
146
|
+
toolApprovalResponses: Record<string, StoredToolApprovalResponse> | undefined,
|
|
147
|
+
): TMessage => {
|
|
148
|
+
if (
|
|
149
|
+
(!toolArtifacts && !toolInteractions && !toolApprovalResponses) ||
|
|
150
|
+
!isRecord(message) ||
|
|
151
|
+
!Array.isArray(message.parts)
|
|
152
|
+
)
|
|
153
|
+
return message;
|
|
154
|
+
const toolCallIds = message.parts.flatMap((part) => {
|
|
155
|
+
if (!isRecord(part) || typeof part.toolCallId !== "string") return [];
|
|
156
|
+
return [part.toolCallId];
|
|
157
|
+
});
|
|
158
|
+
const artifacts = toolArtifacts
|
|
159
|
+
? Object.fromEntries(
|
|
160
|
+
toolCallIds.flatMap((toolCallId) =>
|
|
161
|
+
Object.hasOwn(toolArtifacts, toolCallId)
|
|
162
|
+
? [[toolCallId, toolArtifacts[toolCallId]] as const]
|
|
163
|
+
: [],
|
|
164
|
+
),
|
|
165
|
+
)
|
|
166
|
+
: undefined;
|
|
167
|
+
const interactions = toolInteractions
|
|
168
|
+
? Object.fromEntries(
|
|
169
|
+
toolCallIds.flatMap((toolCallId) =>
|
|
170
|
+
Object.hasOwn(toolInteractions, toolCallId)
|
|
171
|
+
? [[toolCallId, toolInteractions[toolCallId]] as const]
|
|
172
|
+
: [],
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
: undefined;
|
|
176
|
+
const approvalIds = message.parts.flatMap((part) => {
|
|
177
|
+
if (!isRecord(part) || !isRecord(part.approval)) return [];
|
|
178
|
+
const approvalId = part.approval.id;
|
|
179
|
+
return typeof approvalId === "string" ? [approvalId] : [];
|
|
180
|
+
});
|
|
181
|
+
const approvalResponses = toolApprovalResponses
|
|
182
|
+
? Object.fromEntries(
|
|
183
|
+
approvalIds.flatMap((approvalId) =>
|
|
184
|
+
Object.hasOwn(toolApprovalResponses, approvalId)
|
|
185
|
+
? [[approvalId, toolApprovalResponses[approvalId]] as const]
|
|
186
|
+
: [],
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
: undefined;
|
|
190
|
+
const hasArtifacts = !!artifacts && Object.keys(artifacts).length > 0;
|
|
191
|
+
const hasInteractions =
|
|
192
|
+
!!interactions && Object.keys(interactions).length > 0;
|
|
193
|
+
const hasApprovalResponses =
|
|
194
|
+
!!approvalResponses && Object.keys(approvalResponses).length > 0;
|
|
195
|
+
if (!hasArtifacts && !hasInteractions && !hasApprovalResponses)
|
|
196
|
+
return message;
|
|
197
|
+
const metadata = isRecord(message.metadata) ? message.metadata : {};
|
|
198
|
+
return {
|
|
199
|
+
...message,
|
|
200
|
+
metadata: {
|
|
201
|
+
...metadata,
|
|
202
|
+
...(hasArtifacts && { [TOOL_ARTIFACTS_METADATA_KEY]: artifacts }),
|
|
203
|
+
...(hasInteractions && {
|
|
204
|
+
[TOOL_INTERACTIONS_METADATA_KEY]: interactions,
|
|
205
|
+
}),
|
|
206
|
+
...(hasApprovalResponses && {
|
|
207
|
+
[TOOL_APPROVAL_RESPONSES_METADATA_KEY]: approvalResponses,
|
|
208
|
+
}),
|
|
209
|
+
},
|
|
210
|
+
} as TMessage;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const restoreToolData = <TMessage>(
|
|
214
|
+
message: TMessage,
|
|
215
|
+
toolArtifacts: Map<string, unknown> | undefined,
|
|
216
|
+
toolInteractions: Map<string, Unstable_ToolInteractionLog> | undefined,
|
|
217
|
+
toolApprovalResponses: Map<string, RespondToToolApprovalOptions> | undefined,
|
|
218
|
+
): TMessage => {
|
|
219
|
+
if (!isRecord(message) || !isRecord(message.metadata)) return message;
|
|
220
|
+
const metadata = message.metadata;
|
|
221
|
+
const hasArtifacts = Object.hasOwn(metadata, TOOL_ARTIFACTS_METADATA_KEY);
|
|
222
|
+
const hasInteractions = Object.hasOwn(
|
|
223
|
+
metadata,
|
|
224
|
+
TOOL_INTERACTIONS_METADATA_KEY,
|
|
225
|
+
);
|
|
226
|
+
const hasApprovalResponses = Object.hasOwn(
|
|
227
|
+
metadata,
|
|
228
|
+
TOOL_APPROVAL_RESPONSES_METADATA_KEY,
|
|
229
|
+
);
|
|
230
|
+
if (!hasArtifacts && !hasInteractions && !hasApprovalResponses)
|
|
231
|
+
return message;
|
|
232
|
+
const artifacts = metadata[TOOL_ARTIFACTS_METADATA_KEY];
|
|
233
|
+
if (toolArtifacts && isRecord(artifacts)) {
|
|
234
|
+
for (const [toolCallId, artifact] of Object.entries(artifacts)) {
|
|
235
|
+
toolArtifacts.set(toolCallId, artifact);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const interactions = metadata[TOOL_INTERACTIONS_METADATA_KEY];
|
|
239
|
+
if (toolInteractions && isRecord(interactions)) {
|
|
240
|
+
for (const [toolCallId, value] of Object.entries(interactions)) {
|
|
241
|
+
const log = readToolInteractionLog(value);
|
|
242
|
+
if (log) toolInteractions.set(toolCallId, log);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const approvalResponses = metadata[TOOL_APPROVAL_RESPONSES_METADATA_KEY];
|
|
246
|
+
if (toolApprovalResponses && isRecord(approvalResponses)) {
|
|
247
|
+
for (const [approvalId, value] of Object.entries(approvalResponses)) {
|
|
248
|
+
if (!isRecord(value) || typeof value.approved !== "boolean") continue;
|
|
249
|
+
toolApprovalResponses.set(approvalId, {
|
|
250
|
+
approvalId,
|
|
251
|
+
approved: value.approved,
|
|
252
|
+
...(typeof value.optionId === "string" && {
|
|
253
|
+
optionId: value.optionId,
|
|
254
|
+
}),
|
|
255
|
+
...(typeof value.text === "string" && { text: value.text }),
|
|
256
|
+
...(typeof value.reason === "string" && { reason: value.reason }),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const {
|
|
261
|
+
[TOOL_ARTIFACTS_METADATA_KEY]: _,
|
|
262
|
+
[TOOL_INTERACTIONS_METADATA_KEY]: __,
|
|
263
|
+
[TOOL_APPROVAL_RESPONSES_METADATA_KEY]: ___,
|
|
264
|
+
...restMetadata
|
|
265
|
+
} = metadata;
|
|
266
|
+
const { metadata: _metadata, ...restMessage } = message;
|
|
267
|
+
return (
|
|
268
|
+
Object.keys(restMetadata).length === 0
|
|
269
|
+
? restMessage
|
|
270
|
+
: { ...restMessage, metadata: restMetadata }
|
|
271
|
+
) as TMessage;
|
|
272
|
+
};
|
|
273
|
+
|
|
58
274
|
const encodeContent = <TMessage>(
|
|
59
275
|
storageFormatAdapter: MessageFormatAdapter<TMessage, any>,
|
|
60
276
|
item: MessageFormatItem<TMessage>,
|
|
@@ -66,6 +282,12 @@ export const useExternalHistory = <TMessage>(
|
|
|
66
282
|
toThreadMessages: (messages: TMessage[]) => ThreadMessage[],
|
|
67
283
|
storageFormatAdapter: MessageFormatAdapter<TMessage, any>,
|
|
68
284
|
onSetMessages: (messages: TMessage[]) => void,
|
|
285
|
+
toolArtifacts?: Map<string, unknown>,
|
|
286
|
+
onToolArtifactsRestored?: () => void,
|
|
287
|
+
toolInteractions?: Map<string, Unstable_ToolInteractionLog>,
|
|
288
|
+
onToolInteractionsRestored?: () => void,
|
|
289
|
+
toolApprovalResponses?: Map<string, RespondToToolApprovalOptions>,
|
|
290
|
+
onToolApprovalResponsesRestored?: () => void,
|
|
69
291
|
) => {
|
|
70
292
|
const loadedRef = useRef(false);
|
|
71
293
|
const [itemEpoch, setItemEpoch] = useState(0);
|
|
@@ -108,17 +330,37 @@ export const useExternalHistory = <TMessage>(
|
|
|
108
330
|
const loadHistory = async () => {
|
|
109
331
|
try {
|
|
110
332
|
const repo = await formatAdapter.load();
|
|
111
|
-
|
|
112
|
-
|
|
333
|
+
toolArtifacts?.clear();
|
|
334
|
+
toolInteractions?.clear();
|
|
335
|
+
toolApprovalResponses?.clear();
|
|
336
|
+
const restoredMessages =
|
|
337
|
+
repo?.messages.map((item) => ({
|
|
338
|
+
...item,
|
|
339
|
+
message: restoreToolData(
|
|
340
|
+
item.message,
|
|
341
|
+
toolArtifacts,
|
|
342
|
+
toolInteractions,
|
|
343
|
+
toolApprovalResponses,
|
|
344
|
+
),
|
|
345
|
+
})) ?? [];
|
|
346
|
+
onToolArtifactsRestored?.();
|
|
347
|
+
onToolInteractionsRestored?.();
|
|
348
|
+
onToolApprovalResponsesRestored?.();
|
|
349
|
+
if (repo && restoredMessages.length > 0) {
|
|
350
|
+
const restoredRepo = { ...repo, messages: restoredMessages };
|
|
351
|
+
for (const [index, m] of repo.messages.entries()) {
|
|
113
352
|
persistedInnerMessages.current.set(
|
|
114
353
|
storageFormatAdapter.getId(m.message),
|
|
115
354
|
{
|
|
116
|
-
source:
|
|
355
|
+
source: restoredMessages[index]!.message,
|
|
117
356
|
content: encodeContent(storageFormatAdapter, m),
|
|
118
357
|
},
|
|
119
358
|
);
|
|
120
359
|
}
|
|
121
|
-
const converted = toExportedMessageRepository(
|
|
360
|
+
const converted = toExportedMessageRepository(
|
|
361
|
+
toThreadMessages,
|
|
362
|
+
restoredRepo,
|
|
363
|
+
);
|
|
122
364
|
runtimeRef.current.thread.import(converted);
|
|
123
365
|
|
|
124
366
|
const tempRepo = new MessageRepository();
|
|
@@ -177,6 +419,12 @@ export const useExternalHistory = <TMessage>(
|
|
|
177
419
|
aui,
|
|
178
420
|
itemEpoch,
|
|
179
421
|
storageFormatAdapter,
|
|
422
|
+
toolArtifacts,
|
|
423
|
+
onToolArtifactsRestored,
|
|
424
|
+
toolInteractions,
|
|
425
|
+
onToolInteractionsRestored,
|
|
426
|
+
toolApprovalResponses,
|
|
427
|
+
onToolApprovalResponsesRestored,
|
|
180
428
|
]);
|
|
181
429
|
|
|
182
430
|
const runStartRef = useRef<number | null>(null);
|
|
@@ -186,6 +434,84 @@ export const useExternalHistory = <TMessage>(
|
|
|
186
434
|
const wasRunningRef = useRef(false);
|
|
187
435
|
const toolCallCountRef = useRef(0);
|
|
188
436
|
|
|
437
|
+
const persistToolInteractions = useCallback(
|
|
438
|
+
(messageId: string) => {
|
|
439
|
+
const persistence = persistInFlightRef.current.then(async () => {
|
|
440
|
+
if (!formatAdapter?.update) return;
|
|
441
|
+
const messages = runtimeRef.current.thread.getState().messages;
|
|
442
|
+
const message = messages.find(
|
|
443
|
+
(item) =>
|
|
444
|
+
item.id === messageId ||
|
|
445
|
+
getExternalStoreMessages<TMessage>(item).some(
|
|
446
|
+
(innerMessage) =>
|
|
447
|
+
storageFormatAdapter.getId(innerMessage) === messageId,
|
|
448
|
+
),
|
|
449
|
+
);
|
|
450
|
+
if (!message) return;
|
|
451
|
+
|
|
452
|
+
const previousMessages = messages
|
|
453
|
+
.slice(0, messages.indexOf(message))
|
|
454
|
+
.flatMap(getExternalStoreMessages<TMessage>);
|
|
455
|
+
let parentId = previousMessages.at(-1)
|
|
456
|
+
? storageFormatAdapter.getId(previousMessages.at(-1)!)
|
|
457
|
+
: null;
|
|
458
|
+
const storedToolArtifacts = collectToolArtifacts(
|
|
459
|
+
message,
|
|
460
|
+
toolArtifacts,
|
|
461
|
+
);
|
|
462
|
+
const storedToolInteractions = collectToolInteractions(
|
|
463
|
+
message,
|
|
464
|
+
toolInteractions,
|
|
465
|
+
);
|
|
466
|
+
const storedToolApprovalResponses = collectToolApprovalResponses(
|
|
467
|
+
message,
|
|
468
|
+
toolApprovalResponses,
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
for (const innerMessage of getExternalStoreMessages<TMessage>(
|
|
472
|
+
message,
|
|
473
|
+
)) {
|
|
474
|
+
const item = {
|
|
475
|
+
parentId,
|
|
476
|
+
message: addToolData(
|
|
477
|
+
innerMessage,
|
|
478
|
+
storedToolArtifacts,
|
|
479
|
+
storedToolInteractions,
|
|
480
|
+
storedToolApprovalResponses,
|
|
481
|
+
),
|
|
482
|
+
};
|
|
483
|
+
const innerId = storageFormatAdapter.getId(item.message);
|
|
484
|
+
const persisted = persistedInnerMessages.current.get(innerId);
|
|
485
|
+
if (persisted) {
|
|
486
|
+
const content = encodeContent(storageFormatAdapter, item);
|
|
487
|
+
if (content === persisted.content) {
|
|
488
|
+
persisted.source = item.message;
|
|
489
|
+
} else {
|
|
490
|
+
await formatAdapter.update(item, innerId);
|
|
491
|
+
persistedInnerMessages.current.set(innerId, {
|
|
492
|
+
source: item.message,
|
|
493
|
+
content,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
parentId = innerId;
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
persistInFlightRef.current = persistence.catch(() => {});
|
|
501
|
+
return persistence.catch((error) => {
|
|
502
|
+
console.error("Failed to persist tool data:", error);
|
|
503
|
+
});
|
|
504
|
+
},
|
|
505
|
+
[
|
|
506
|
+
formatAdapter,
|
|
507
|
+
runtimeRef,
|
|
508
|
+
storageFormatAdapter,
|
|
509
|
+
toolArtifacts,
|
|
510
|
+
toolInteractions,
|
|
511
|
+
toolApprovalResponses,
|
|
512
|
+
],
|
|
513
|
+
);
|
|
514
|
+
|
|
189
515
|
useEffect(() => {
|
|
190
516
|
if (!formatAdapter) return;
|
|
191
517
|
const adapter = formatAdapter;
|
|
@@ -318,13 +644,27 @@ export const useExternalHistory = <TMessage>(
|
|
|
318
644
|
const getLastInnerId = (msgs: TMessage[]): string | null =>
|
|
319
645
|
msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)!) : null;
|
|
320
646
|
|
|
321
|
-
const toBatchItems = (
|
|
647
|
+
const toBatchItems = (
|
|
648
|
+
msgs: TMessage[],
|
|
649
|
+
toolArtifacts: Record<string, unknown> | undefined,
|
|
650
|
+
toolInteractions:
|
|
651
|
+
| Record<string, Unstable_ToolInteractionLog>
|
|
652
|
+
| undefined,
|
|
653
|
+
toolApprovalResponses:
|
|
654
|
+
| Record<string, StoredToolApprovalResponse>
|
|
655
|
+
| undefined,
|
|
656
|
+
) =>
|
|
322
657
|
msgs.map((msg, idx) => ({
|
|
323
658
|
parentId:
|
|
324
659
|
idx === 0
|
|
325
660
|
? lastInnerMessageId
|
|
326
661
|
: storageFormatAdapter.getId(msgs[idx - 1]!),
|
|
327
|
-
message:
|
|
662
|
+
message: addToolData(
|
|
663
|
+
msg,
|
|
664
|
+
toolArtifacts,
|
|
665
|
+
toolInteractions,
|
|
666
|
+
toolApprovalResponses,
|
|
667
|
+
),
|
|
328
668
|
}));
|
|
329
669
|
|
|
330
670
|
for (const message of messages) {
|
|
@@ -348,7 +688,12 @@ export const useExternalHistory = <TMessage>(
|
|
|
348
688
|
deferredTelemetryIds.current.add(message.id);
|
|
349
689
|
}
|
|
350
690
|
|
|
351
|
-
const batchItems = toBatchItems(
|
|
691
|
+
const batchItems = toBatchItems(
|
|
692
|
+
innerMessages,
|
|
693
|
+
collectToolArtifacts(message, toolArtifacts),
|
|
694
|
+
collectToolInteractions(message, toolInteractions),
|
|
695
|
+
collectToolApprovalResponses(message, toolApprovalResponses),
|
|
696
|
+
);
|
|
352
697
|
for (const item of batchItems) {
|
|
353
698
|
const innerId = storageFormatAdapter.getId(item.message);
|
|
354
699
|
const persisted = persistedInnerMessages.current.get(innerId);
|
|
@@ -385,10 +730,16 @@ export const useExternalHistory = <TMessage>(
|
|
|
385
730
|
|
|
386
731
|
if (deferredTelemetryIds.current.has(message.id) && isTerminal) {
|
|
387
732
|
deferredTelemetryIds.current.delete(message.id);
|
|
388
|
-
adapter.reportTelemetry?.(
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
733
|
+
adapter.reportTelemetry?.(
|
|
734
|
+
batchItems.map((item, index) => ({
|
|
735
|
+
...item,
|
|
736
|
+
message: innerMessages[index]!,
|
|
737
|
+
})),
|
|
738
|
+
{
|
|
739
|
+
...telemetryOptions,
|
|
740
|
+
message,
|
|
741
|
+
},
|
|
742
|
+
);
|
|
392
743
|
}
|
|
393
744
|
}
|
|
394
745
|
})
|
|
@@ -405,7 +756,14 @@ export const useExternalHistory = <TMessage>(
|
|
|
405
756
|
persistSettled(false);
|
|
406
757
|
}
|
|
407
758
|
};
|
|
408
|
-
}, [
|
|
759
|
+
}, [
|
|
760
|
+
formatAdapter,
|
|
761
|
+
storageFormatAdapter,
|
|
762
|
+
runtimeRef,
|
|
763
|
+
toolArtifacts,
|
|
764
|
+
toolInteractions,
|
|
765
|
+
toolApprovalResponses,
|
|
766
|
+
]);
|
|
409
767
|
|
|
410
768
|
const deleteMessage = useCallback(
|
|
411
769
|
async (messageId: string) => {
|
|
@@ -448,5 +806,10 @@ export const useExternalHistory = <TMessage>(
|
|
|
448
806
|
[formatAdapter, runtimeRef, storageFormatAdapter],
|
|
449
807
|
);
|
|
450
808
|
|
|
451
|
-
return {
|
|
809
|
+
return {
|
|
810
|
+
isLoading,
|
|
811
|
+
deleteMessage,
|
|
812
|
+
persistToolInteractions,
|
|
813
|
+
persistToolApprovalResponses: persistToolInteractions,
|
|
814
|
+
};
|
|
452
815
|
};
|
|
@@ -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
|
};
|