@assistant-ui/react-google-adk 0.0.34 → 0.0.36
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 +12 -2
- package/dist/AdkClient.d.ts +4 -0
- package/dist/AdkClient.d.ts.map +1 -1
- package/dist/AdkClient.js +12 -7
- package/dist/AdkClient.js.map +1 -1
- package/dist/AdkSessionAdapter.d.ts.map +1 -1
- package/dist/AdkSessionAdapter.js +7 -7
- package/dist/AdkSessionAdapter.js.map +1 -1
- package/dist/AdkThreadController.d.ts +15 -0
- package/dist/AdkThreadController.d.ts.map +1 -0
- package/dist/AdkThreadController.js +35 -0
- package/dist/AdkThreadController.js.map +1 -0
- package/dist/adkThreadState.d.ts +54 -0
- package/dist/adkThreadState.d.ts.map +1 -0
- package/dist/adkThreadState.js +93 -0
- package/dist/adkThreadState.js.map +1 -0
- package/dist/convertToAdkMessages.js +1 -1
- package/dist/convertToAdkMessages.js.map +1 -1
- package/dist/sdkIdentity.js +1 -1
- package/dist/server/createAdkApiRoute.d.ts +37 -6
- package/dist/server/createAdkApiRoute.d.ts.map +1 -1
- package/dist/server/createAdkApiRoute.js +55 -5
- package/dist/server/createAdkApiRoute.js.map +1 -1
- package/dist/server/parseAdkRequest.d.ts +4 -1
- package/dist/server/parseAdkRequest.d.ts.map +1 -1
- package/dist/server/parseAdkRequest.js +5 -1
- package/dist/server/parseAdkRequest.js.map +1 -1
- package/dist/useAdkMessages.d.ts +29 -4
- package/dist/useAdkMessages.d.ts.map +1 -1
- package/dist/useAdkMessages.js +62 -76
- package/dist/useAdkMessages.js.map +1 -1
- package/dist/useAdkRuntime.d.ts +7 -1
- package/dist/useAdkRuntime.d.ts.map +1 -1
- package/dist/useAdkRuntime.js +182 -54
- package/dist/useAdkRuntime.js.map +1 -1
- package/package.json +6 -5
- package/src/AdkClient.test.ts +78 -2
- package/src/AdkClient.ts +24 -6
- package/src/AdkSessionAdapter.ts +1 -1
- package/src/AdkThreadController.test.ts +90 -0
- package/src/AdkThreadController.ts +45 -0
- package/src/adkThreadState.test.ts +207 -0
- package/src/adkThreadState.ts +124 -0
- package/src/convertToAdkMessages.test.ts +19 -0
- package/src/convertToAdkMessages.ts +1 -1
- package/src/hooks.test.tsx +1 -0
- package/src/server/createAdkApiRoute.controls.test.ts +66 -0
- package/src/server/createAdkApiRoute.test.ts +282 -0
- package/src/server/createAdkApiRoute.ts +119 -11
- package/src/server/parseAdkRequest.test.ts +11 -3
- package/src/server/parseAdkRequest.ts +7 -1
- package/src/useAdkMessages.test.ts +43 -0
- package/src/useAdkMessages.ts +89 -96
- package/src/useAdkRuntime.cancellation.test.tsx +4 -3
- package/src/useAdkRuntime.cloud-options.test.tsx +59 -0
- package/src/useAdkRuntime.refetch.test.tsx +548 -4
- package/src/useAdkRuntime.replacement.test.tsx +718 -1
- package/src/useAdkRuntime.ts +253 -75
- package/src/useAdkRuntimeApproval.test.tsx +390 -35
- package/dist/raceWithAbortSignal.d.ts +0 -2
- package/dist/raceWithAbortSignal.d.ts.map +0 -1
- package/dist/raceWithAbortSignal.js +0 -45
- package/dist/raceWithAbortSignal.js.map +0 -1
- package/src/raceWithAbortSignal.test.ts +0 -73
- package/src/raceWithAbortSignal.ts +0 -48
package/src/useAdkRuntime.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
useMemo,
|
|
5
5
|
useRef,
|
|
6
6
|
useState,
|
|
7
|
+
useSyncExternalStore,
|
|
7
8
|
} from "react";
|
|
8
9
|
import {
|
|
9
10
|
pickExternalStoreSharedOptions,
|
|
@@ -21,6 +22,9 @@ import {
|
|
|
21
22
|
import {
|
|
22
23
|
createAbortableThreadLoad,
|
|
23
24
|
createCloudThreadListAdapterCreateFallback,
|
|
25
|
+
isRecord,
|
|
26
|
+
RunLeases,
|
|
27
|
+
type RunLease,
|
|
24
28
|
} from "@assistant-ui/core/internal";
|
|
25
29
|
import {
|
|
26
30
|
useCloudThreadListAdapter,
|
|
@@ -41,7 +45,7 @@ import type {
|
|
|
41
45
|
OnAdkCustomEventCallback,
|
|
42
46
|
OnAdkAgentTransferCallback,
|
|
43
47
|
} from "./types";
|
|
44
|
-
import {
|
|
48
|
+
import { useAdkMessagesInternal } from "./useAdkMessages";
|
|
45
49
|
import {
|
|
46
50
|
convertAdkMessage,
|
|
47
51
|
createAdkMessageConverter,
|
|
@@ -71,6 +75,7 @@ export type UseAdkRuntimeOptions = ExternalStoreSharedOptions & {
|
|
|
71
75
|
*/
|
|
72
76
|
onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;
|
|
73
77
|
autoCancelPendingToolCalls?: boolean | undefined;
|
|
78
|
+
/** @deprecated Experimental since 2025-01-03. Not scheduled for removal; the API may change in any release. */
|
|
74
79
|
unstable_allowCancellation?: boolean | undefined;
|
|
75
80
|
getCheckpointId?: (
|
|
76
81
|
threadId: string,
|
|
@@ -105,6 +110,11 @@ export type UseAdkRuntimeOptions = ExternalStoreSharedOptions & {
|
|
|
105
110
|
}
|
|
106
111
|
| undefined;
|
|
107
112
|
cloud?: AssistantCloud | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* Stable identity for the account or workspace owning Cloud runtime state.
|
|
115
|
+
* Provide it from the first render and change it when that scope changes.
|
|
116
|
+
*/
|
|
117
|
+
scopeId?: string | undefined;
|
|
108
118
|
/**
|
|
109
119
|
* A `RemoteThreadListAdapter` to use instead of the cloud adapter.
|
|
110
120
|
* Use with `createAdkSessionAdapter` for ADK session-backed persistence.
|
|
@@ -123,7 +133,64 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
123
133
|
eventHandlers,
|
|
124
134
|
} = options;
|
|
125
135
|
const aui = useAui();
|
|
136
|
+
const runConfigByToolCallIdRef = useRef(new Map<string, unknown>());
|
|
137
|
+
|
|
138
|
+
const rememberMessageOwnership = useCallback(
|
|
139
|
+
(newMessages: AdkMessage[], runConfig: unknown) => {
|
|
140
|
+
const toolOwnership = runConfigByToolCallIdRef.current;
|
|
141
|
+
for (const message of newMessages) {
|
|
142
|
+
if (message.type !== "ai") continue;
|
|
143
|
+
for (const toolCall of message.tool_calls ?? []) {
|
|
144
|
+
if (!isRecord(toolCall)) continue;
|
|
145
|
+
if (!toolOwnership.has(toolCall.id)) {
|
|
146
|
+
toolOwnership.set(toolCall.id, runConfig);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
[],
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const seedMessageOwnership = useCallback((history: AdkMessage[]) => {
|
|
155
|
+
const currentOwnership = runConfigByToolCallIdRef.current;
|
|
156
|
+
const nextOwnership = new Map<string, unknown>();
|
|
157
|
+
for (const message of history) {
|
|
158
|
+
if (message.type !== "ai") continue;
|
|
159
|
+
for (const toolCall of message.tool_calls ?? []) {
|
|
160
|
+
if (!isRecord(toolCall)) continue;
|
|
161
|
+
// Loaded ids must remain present even without a local owner because
|
|
162
|
+
// streamed event windows use has() to avoid attributing them later.
|
|
163
|
+
nextOwnership.set(
|
|
164
|
+
toolCall.id,
|
|
165
|
+
currentOwnership.has(toolCall.id)
|
|
166
|
+
? currentOwnership.get(toolCall.id)
|
|
167
|
+
: undefined,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
runConfigByToolCallIdRef.current = nextOwnership;
|
|
172
|
+
}, []);
|
|
173
|
+
|
|
174
|
+
const pruneMessageOwnership = useCallback((history: AdkMessage[]) => {
|
|
175
|
+
const toolCallIds = new Set<string>();
|
|
176
|
+
for (const message of history) {
|
|
177
|
+
if (message.type !== "ai") continue;
|
|
178
|
+
for (const toolCall of message.tool_calls ?? []) {
|
|
179
|
+
if (!isRecord(toolCall)) continue;
|
|
180
|
+
toolCallIds.add(toolCall.id);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
for (const id of runConfigByToolCallIdRef.current.keys()) {
|
|
184
|
+
if (!toolCallIds.has(id)) runConfigByToolCallIdRef.current.delete(id);
|
|
185
|
+
}
|
|
186
|
+
}, []);
|
|
187
|
+
|
|
188
|
+
const getToolRunConfig = useCallback((toolCallId: string) => {
|
|
189
|
+
return runConfigByToolCallIdRef.current.get(toolCallId);
|
|
190
|
+
}, []);
|
|
191
|
+
|
|
126
192
|
const {
|
|
193
|
+
controller,
|
|
127
194
|
messages,
|
|
128
195
|
stateDelta,
|
|
129
196
|
agentInfo,
|
|
@@ -136,11 +203,12 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
136
203
|
sendMessage,
|
|
137
204
|
cancel,
|
|
138
205
|
setMessages,
|
|
139
|
-
replaceMessages,
|
|
140
|
-
applySnapshot,
|
|
141
|
-
} =
|
|
206
|
+
replaceMessages: replaceAdkMessages,
|
|
207
|
+
applySnapshot: applyAdkSnapshot,
|
|
208
|
+
} = useAdkMessagesInternal({
|
|
142
209
|
stream,
|
|
143
210
|
...(eventHandlers && { eventHandlers }),
|
|
211
|
+
onMessages: rememberMessageOwnership,
|
|
144
212
|
});
|
|
145
213
|
|
|
146
214
|
const loadRef = useRef(load);
|
|
@@ -148,10 +216,40 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
148
216
|
loadRef.current = load;
|
|
149
217
|
}, [load]);
|
|
150
218
|
const [loadController] = useState(createAbortableThreadLoad);
|
|
219
|
+
const initialLoadRef = useRef<{
|
|
220
|
+
promise: Promise<void>;
|
|
221
|
+
active: boolean;
|
|
222
|
+
snapshot: AdkThreadSnapshot | undefined;
|
|
223
|
+
} | null>(null);
|
|
224
|
+
const waitForInitialLoad = () => {
|
|
225
|
+
const load = initialLoadRef.current;
|
|
226
|
+
if (!load) return undefined;
|
|
227
|
+
return load.promise.then(() => ({
|
|
228
|
+
active:
|
|
229
|
+
load.active &&
|
|
230
|
+
(!threadListItem ||
|
|
231
|
+
aui.threads.getState().mainThreadId === threadListItem.getState().id),
|
|
232
|
+
snapshot: load.snapshot,
|
|
233
|
+
}));
|
|
234
|
+
};
|
|
151
235
|
const messagesRef = useRef(messages);
|
|
152
236
|
useInsertionEffect(() => {
|
|
153
237
|
messagesRef.current = messages;
|
|
154
238
|
}, [messages]);
|
|
239
|
+
const applySnapshot = useCallback(
|
|
240
|
+
(snapshot: AdkThreadSnapshot) => {
|
|
241
|
+
seedMessageOwnership(snapshot.messages);
|
|
242
|
+
applyAdkSnapshot(snapshot);
|
|
243
|
+
},
|
|
244
|
+
[applyAdkSnapshot, seedMessageOwnership],
|
|
245
|
+
);
|
|
246
|
+
const replaceMessages = useCallback(
|
|
247
|
+
(nextMessages: AdkMessage[]) => {
|
|
248
|
+
pruneMessageOwnership(nextMessages);
|
|
249
|
+
replaceAdkMessages(nextMessages);
|
|
250
|
+
},
|
|
251
|
+
[pruneMessageOwnership, replaceAdkMessages],
|
|
252
|
+
);
|
|
155
253
|
const [isLoadingThread, setIsLoadingThread] = useState(
|
|
156
254
|
() =>
|
|
157
255
|
load !== undefined && aui.threadListItem.getState().externalId != null,
|
|
@@ -169,21 +267,47 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
169
267
|
useInsertionEffect(() => {
|
|
170
268
|
isRunningRef.current = effectiveIsRunning;
|
|
171
269
|
}, [effectiveIsRunning]);
|
|
172
|
-
const
|
|
270
|
+
const [runLeases] = useState(() => new RunLeases());
|
|
271
|
+
const reloadLookupRef = useRef<{
|
|
272
|
+
lease: RunLease;
|
|
273
|
+
beforeReload: AdkThreadSnapshot;
|
|
274
|
+
} | null>(null);
|
|
173
275
|
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
config: AdkSendMessageConfig,
|
|
276
|
+
const runExclusive = async (
|
|
277
|
+
run: (isCurrent: () => boolean) => Promise<void>,
|
|
177
278
|
) => {
|
|
178
|
-
const
|
|
279
|
+
const lease = runLeases.begin();
|
|
179
280
|
try {
|
|
180
281
|
setIsRunning(true);
|
|
181
|
-
await
|
|
282
|
+
await run(lease.isCurrent);
|
|
182
283
|
} finally {
|
|
183
|
-
if (
|
|
284
|
+
if (lease.isCurrent()) setIsRunning(false);
|
|
184
285
|
}
|
|
185
286
|
};
|
|
186
287
|
|
|
288
|
+
const handleSendMessage = (
|
|
289
|
+
msgs: AdkMessage[],
|
|
290
|
+
config: AdkSendMessageConfig,
|
|
291
|
+
) => {
|
|
292
|
+
const isToolContinuation =
|
|
293
|
+
msgs.length > 0 && msgs.every((msg) => msg.type === "tool");
|
|
294
|
+
const continuationConfig =
|
|
295
|
+
isToolContinuation && config.runConfig === undefined
|
|
296
|
+
? {
|
|
297
|
+
...config,
|
|
298
|
+
runConfig: getToolRunConfig(msgs[0]!.tool_call_id),
|
|
299
|
+
}
|
|
300
|
+
: config;
|
|
301
|
+
|
|
302
|
+
return runExclusive(() => sendMessage(msgs, continuationConfig));
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const stopRun = () => {
|
|
306
|
+
runLeases.invalidate();
|
|
307
|
+
setIsRunning(false);
|
|
308
|
+
cancel();
|
|
309
|
+
};
|
|
310
|
+
|
|
187
311
|
const { approvals: toolApprovals, key: toolApprovalsKey } =
|
|
188
312
|
projectAdkToolApprovals(messages);
|
|
189
313
|
// The messageConverter memo below reads this during render, where the ref
|
|
@@ -233,43 +357,20 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
233
357
|
adkMessagesRef.current = messages;
|
|
234
358
|
}, [messages]);
|
|
235
359
|
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
message: AdkMessage & { id: string };
|
|
241
|
-
runConfig: AppendMessage["runConfig"];
|
|
242
|
-
}
|
|
243
|
-
>(),
|
|
360
|
+
const stagedMessageCount = useSyncExternalStore(
|
|
361
|
+
controller.subscribe,
|
|
362
|
+
controller.getStagedMessageCount,
|
|
363
|
+
controller.getStagedMessageCount,
|
|
244
364
|
);
|
|
245
|
-
const [stagedMessageCount, setStagedMessageCount] = useState(0);
|
|
246
365
|
const hasStagedMessages = stagedMessageCount > 0;
|
|
247
366
|
|
|
248
|
-
const getStagedRun = (parentId: string | null) => {
|
|
249
|
-
if (!parentId || !stagedMessagesRef.current.has(parentId)) return null;
|
|
250
|
-
|
|
251
|
-
const staged: AdkMessage[] = [];
|
|
252
|
-
for (const message of adkMessagesRef.current) {
|
|
253
|
-
if (message.id && stagedMessagesRef.current.has(message.id)) {
|
|
254
|
-
staged.push(stagedMessagesRef.current.get(message.id)!.message);
|
|
255
|
-
}
|
|
256
|
-
if (message.id === parentId) break;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return {
|
|
260
|
-
messages: staged,
|
|
261
|
-
runConfig: stagedMessagesRef.current.get(parentId)!.runConfig,
|
|
262
|
-
};
|
|
263
|
-
};
|
|
264
|
-
|
|
265
367
|
const stageUserMessage = (msg: AppendMessage) => {
|
|
266
368
|
const stagedMessage = toAdkUserMessage(msg);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
runConfig: msg.runConfig,
|
|
369
|
+
controller.dispatch({
|
|
370
|
+
type: "staged.stage",
|
|
371
|
+
entry: { message: stagedMessage, runConfig: msg.runConfig },
|
|
270
372
|
});
|
|
271
|
-
|
|
272
|
-
const nextMessages = [...adkMessagesRef.current, stagedMessage];
|
|
373
|
+
const nextMessages = [...controller.getState().messages, stagedMessage];
|
|
273
374
|
adkMessagesRef.current = nextMessages;
|
|
274
375
|
setMessages(nextMessages);
|
|
275
376
|
};
|
|
@@ -312,7 +413,14 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
312
413
|
messagesRef.current !== messagesAtLoadStart)
|
|
313
414
|
)
|
|
314
415
|
return;
|
|
416
|
+
reloadLookupRef.current = null;
|
|
315
417
|
applySnapshot(snapshot);
|
|
418
|
+
messagesRef.current = snapshot.messages;
|
|
419
|
+
adkMessagesRef.current = snapshot.messages;
|
|
420
|
+
longRunningToolIdsRef.current = snapshot.longRunningToolIds ?? [];
|
|
421
|
+
if (purpose === "initial" && initialLoadRef.current) {
|
|
422
|
+
initialLoadRef.current.snapshot = snapshot;
|
|
423
|
+
}
|
|
316
424
|
},
|
|
317
425
|
onSettled: () => {
|
|
318
426
|
setIsLoadingThread(false);
|
|
@@ -326,11 +434,26 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
326
434
|
);
|
|
327
435
|
|
|
328
436
|
useReplaySafeEffect(() => {
|
|
329
|
-
|
|
437
|
+
let release!: () => void;
|
|
438
|
+
const barrier = {
|
|
439
|
+
promise: new Promise<void>((resolve) => {
|
|
440
|
+
release = resolve;
|
|
441
|
+
}),
|
|
442
|
+
active: true,
|
|
443
|
+
snapshot: undefined as AdkThreadSnapshot | undefined,
|
|
444
|
+
};
|
|
445
|
+
initialLoadRef.current = barrier;
|
|
446
|
+
const settle = () => {
|
|
447
|
+
if (initialLoadRef.current === barrier) initialLoadRef.current = null;
|
|
448
|
+
release();
|
|
449
|
+
};
|
|
450
|
+
void runLoad().then(settle, settle);
|
|
330
451
|
return () => {
|
|
452
|
+
barrier.active = false;
|
|
331
453
|
// Whatever is current, not this effect's own controller: a refetch swaps
|
|
332
454
|
// the ref, and one in flight at unmount must be aborted too.
|
|
333
455
|
loadController.abort();
|
|
456
|
+
settle();
|
|
334
457
|
setIsLoadingThread(false);
|
|
335
458
|
};
|
|
336
459
|
}, [threadListItem]);
|
|
@@ -353,9 +476,17 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
353
476
|
authRequests,
|
|
354
477
|
escalated,
|
|
355
478
|
messageMetadata,
|
|
356
|
-
send:
|
|
479
|
+
send: (messages, config) => {
|
|
480
|
+
const initialLoad = waitForInitialLoad();
|
|
481
|
+
if (!initialLoad) return handleSendMessage(messages, config);
|
|
482
|
+
return initialLoad.then(({ active }) =>
|
|
483
|
+
active ? handleSendMessage(messages, config) : undefined,
|
|
484
|
+
);
|
|
485
|
+
},
|
|
357
486
|
}),
|
|
358
487
|
onNew: async (msg) => {
|
|
488
|
+
const initialLoad = await waitForInitialLoad();
|
|
489
|
+
if (initialLoad && !initialLoad.active) return;
|
|
359
490
|
if (!(msg.startRun ?? msg.role === "user")) {
|
|
360
491
|
stageUserMessage(msg);
|
|
361
492
|
return;
|
|
@@ -363,7 +494,11 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
363
494
|
|
|
364
495
|
const cancellations =
|
|
365
496
|
autoCancelPendingToolCalls !== false
|
|
366
|
-
? getPendingCancellations(
|
|
497
|
+
? getPendingCancellations(
|
|
498
|
+
initialLoad?.snapshot?.messages ?? messagesRef.current,
|
|
499
|
+
initialLoad?.snapshot?.longRunningToolIds ??
|
|
500
|
+
longRunningToolIdsRef.current,
|
|
501
|
+
)
|
|
367
502
|
: [];
|
|
368
503
|
|
|
369
504
|
return handleSendMessage(
|
|
@@ -380,6 +515,9 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
380
515
|
},
|
|
381
516
|
onEdit: getCheckpointId
|
|
382
517
|
? async (msg) => {
|
|
518
|
+
const initialLoad = waitForInitialLoad();
|
|
519
|
+
if (initialLoad && !(await initialLoad).active) return;
|
|
520
|
+
stopRun();
|
|
383
521
|
const truncated = truncateAdkMessages(
|
|
384
522
|
threadMessagesRef.current,
|
|
385
523
|
msg.parentId,
|
|
@@ -387,44 +525,41 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
387
525
|
replaceMessages(truncated);
|
|
388
526
|
if (!(msg.startRun ?? msg.role === "user")) {
|
|
389
527
|
const stagedMessage = toAdkUserMessage(msg);
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
runConfig: msg.runConfig,
|
|
528
|
+
controller.dispatch({
|
|
529
|
+
type: "staged.stage",
|
|
530
|
+
entry: { message: stagedMessage, runConfig: msg.runConfig },
|
|
393
531
|
});
|
|
394
|
-
setStagedMessageCount(stagedMessagesRef.current.size);
|
|
395
532
|
const nextMessages = [...truncated, stagedMessage];
|
|
396
533
|
adkMessagesRef.current = nextMessages;
|
|
397
534
|
setMessages(nextMessages);
|
|
398
535
|
return;
|
|
399
536
|
}
|
|
537
|
+
const editedMessage = toAdkUserMessage(msg);
|
|
538
|
+
setMessages([...truncated, editedMessage]);
|
|
400
539
|
const externalId = aui.threadListItem.getState().externalId;
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
id: generateId(),
|
|
408
|
-
type: "human",
|
|
409
|
-
content: getMessageContent(msg),
|
|
410
|
-
},
|
|
411
|
-
],
|
|
412
|
-
{
|
|
540
|
+
return runExclusive(async (isCurrent) => {
|
|
541
|
+
const checkpointId = externalId
|
|
542
|
+
? await getCheckpointId(externalId, truncated)
|
|
543
|
+
: null;
|
|
544
|
+
if (!isCurrent()) return;
|
|
545
|
+
await sendMessage([editedMessage], {
|
|
413
546
|
runConfig: msg.runConfig,
|
|
414
547
|
...(checkpointId && { checkpointId }),
|
|
415
|
-
}
|
|
416
|
-
);
|
|
548
|
+
});
|
|
549
|
+
});
|
|
417
550
|
}
|
|
418
551
|
: undefined,
|
|
419
552
|
...(getCheckpointId || hasStagedMessages
|
|
420
553
|
? {
|
|
421
554
|
onReload: async (parentId, config) => {
|
|
422
|
-
const
|
|
555
|
+
const initialLoad = waitForInitialLoad();
|
|
556
|
+
if (initialLoad && !(await initialLoad).active) return;
|
|
557
|
+
const stagedRun = controller.getStagedRun(parentId);
|
|
423
558
|
if (stagedRun) {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
559
|
+
controller.dispatch({
|
|
560
|
+
type: "staged.unstage",
|
|
561
|
+
ids: stagedRun.messages.map((message) => message.id!),
|
|
562
|
+
});
|
|
428
563
|
return handleSendMessage(stagedRun.messages, {
|
|
429
564
|
runConfig: config.runConfig ?? stagedRun.runConfig,
|
|
430
565
|
});
|
|
@@ -433,18 +568,48 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
433
568
|
if (!getCheckpointId)
|
|
434
569
|
throw new Error("Runtime does not support reloading messages.");
|
|
435
570
|
|
|
571
|
+
stopRun();
|
|
572
|
+
const beforeReload: AdkThreadSnapshot = {
|
|
573
|
+
messages: adkMessagesRef.current,
|
|
574
|
+
longRunningToolIds,
|
|
575
|
+
toolConfirmations,
|
|
576
|
+
authRequests,
|
|
577
|
+
escalated,
|
|
578
|
+
messageMetadata,
|
|
579
|
+
stateDelta,
|
|
580
|
+
artifactDelta,
|
|
581
|
+
agentInfo,
|
|
582
|
+
};
|
|
436
583
|
const truncated = truncateAdkMessages(
|
|
437
584
|
threadMessagesRef.current,
|
|
438
585
|
parentId,
|
|
439
586
|
);
|
|
440
587
|
replaceMessages(truncated);
|
|
441
588
|
const externalId = aui.threadListItem.getState().externalId;
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
589
|
+
return runExclusive(async (isCurrent) => {
|
|
590
|
+
const lookup = {
|
|
591
|
+
lease: runLeases.current(),
|
|
592
|
+
beforeReload,
|
|
593
|
+
};
|
|
594
|
+
reloadLookupRef.current = lookup;
|
|
595
|
+
let checkpointId: string | null;
|
|
596
|
+
try {
|
|
597
|
+
checkpointId = externalId
|
|
598
|
+
? await getCheckpointId(externalId, truncated)
|
|
599
|
+
: null;
|
|
600
|
+
} catch (error) {
|
|
601
|
+
if (isCurrent() && reloadLookupRef.current === lookup)
|
|
602
|
+
applySnapshot(beforeReload);
|
|
603
|
+
throw error;
|
|
604
|
+
} finally {
|
|
605
|
+
if (reloadLookupRef.current === lookup)
|
|
606
|
+
reloadLookupRef.current = null;
|
|
607
|
+
}
|
|
608
|
+
if (!isCurrent()) return;
|
|
609
|
+
await sendMessage([], {
|
|
610
|
+
runConfig: config.runConfig,
|
|
611
|
+
...(checkpointId && { checkpointId }),
|
|
612
|
+
});
|
|
448
613
|
});
|
|
449
614
|
},
|
|
450
615
|
}
|
|
@@ -456,6 +621,8 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
456
621
|
isError,
|
|
457
622
|
artifact,
|
|
458
623
|
}) => {
|
|
624
|
+
const initialLoad = waitForInitialLoad();
|
|
625
|
+
if (initialLoad && !(await initialLoad).active) return;
|
|
459
626
|
await handleSendMessage(
|
|
460
627
|
[
|
|
461
628
|
{
|
|
@@ -472,6 +639,8 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
472
639
|
);
|
|
473
640
|
},
|
|
474
641
|
onRespondToToolApproval: async (options) => {
|
|
642
|
+
const initialLoad = waitForInitialLoad();
|
|
643
|
+
if (initialLoad && !(await initialLoad).active) return;
|
|
475
644
|
await handleSendMessage(
|
|
476
645
|
[
|
|
477
646
|
toAdkToolConfirmationReply(
|
|
@@ -484,7 +653,14 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
484
653
|
},
|
|
485
654
|
onCancel: unstable_allowCancellation
|
|
486
655
|
? async () => {
|
|
487
|
-
|
|
656
|
+
const lookup = reloadLookupRef.current;
|
|
657
|
+
const beforeReload = lookup?.lease.isCurrent()
|
|
658
|
+
? lookup.beforeReload
|
|
659
|
+
: undefined;
|
|
660
|
+
stopRun();
|
|
661
|
+
// A reload stopped before it sent leaves the ADK session holding the
|
|
662
|
+
// turn it removed, so the thread shows that turn again.
|
|
663
|
+
if (beforeReload) applySnapshot(beforeReload);
|
|
488
664
|
}
|
|
489
665
|
: undefined,
|
|
490
666
|
...(load !== undefined && {
|
|
@@ -497,6 +673,7 @@ const useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {
|
|
|
497
673
|
|
|
498
674
|
export const useAdkRuntime = ({
|
|
499
675
|
cloud,
|
|
676
|
+
scopeId,
|
|
500
677
|
sessionAdapter,
|
|
501
678
|
create,
|
|
502
679
|
delete: deleteFn,
|
|
@@ -507,6 +684,7 @@ export const useAdkRuntime = ({
|
|
|
507
684
|
const cloudAdapter = useCloudThreadListAdapter({
|
|
508
685
|
sdk: ADK_SDK,
|
|
509
686
|
cloud,
|
|
687
|
+
scopeId,
|
|
510
688
|
create: createCloudThreadListAdapterCreateFallback(
|
|
511
689
|
create,
|
|
512
690
|
aui.threadListItem,
|