@assistant-ui/react-google-adk 0.0.30 → 0.0.32

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.
Files changed (103) hide show
  1. package/dist/AdkClient.d.ts +26 -29
  2. package/dist/AdkClient.d.ts.map +1 -1
  3. package/dist/AdkClient.js +5 -3
  4. package/dist/AdkClient.js.map +1 -1
  5. package/dist/AdkEventAccumulator.d.ts +41 -45
  6. package/dist/AdkEventAccumulator.d.ts.map +1 -1
  7. package/dist/AdkEventAccumulator.js +63 -99
  8. package/dist/AdkEventAccumulator.js.map +1 -1
  9. package/dist/AdkSessionAdapter.d.ts +41 -39
  10. package/dist/AdkSessionAdapter.d.ts.map +1 -1
  11. package/dist/AdkSessionAdapter.js +11 -7
  12. package/dist/AdkSessionAdapter.js.map +1 -1
  13. package/dist/adkAuthRequest.d.ts +17 -0
  14. package/dist/adkAuthRequest.d.ts.map +1 -0
  15. package/dist/adkAuthRequest.js +45 -0
  16. package/dist/adkAuthRequest.js.map +1 -0
  17. package/dist/adkExtras.d.ts +2 -5
  18. package/dist/adkExtras.d.ts.map +1 -1
  19. package/dist/adkToolApproval.d.ts +18 -21
  20. package/dist/adkToolApproval.d.ts.map +1 -1
  21. package/dist/adkToolApproval.js +1 -3
  22. package/dist/adkToolApproval.js.map +1 -1
  23. package/dist/contentToParts.d.ts +2 -5
  24. package/dist/contentToParts.d.ts.map +1 -1
  25. package/dist/convertAdkMessages.d.ts +5 -8
  26. package/dist/convertAdkMessages.d.ts.map +1 -1
  27. package/dist/convertAdkMessages.js +4 -3
  28. package/dist/convertAdkMessages.js.map +1 -1
  29. package/dist/convertToAdkMessages.d.ts +36 -39
  30. package/dist/convertToAdkMessages.d.ts.map +1 -1
  31. package/dist/hooks.d.ts +25 -23
  32. package/dist/hooks.d.ts.map +1 -1
  33. package/dist/hooks.js +12 -10
  34. package/dist/hooks.js.map +1 -1
  35. package/dist/index.d.ts +11 -10
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/normalizeAdkPart.d.ts +2 -0
  38. package/dist/normalizeAdkPart.d.ts.map +1 -0
  39. package/dist/normalizeAdkPart.js +31 -0
  40. package/dist/normalizeAdkPart.js.map +1 -0
  41. package/dist/parseAdkEvent.d.ts +2 -5
  42. package/dist/parseAdkEvent.d.ts.map +1 -1
  43. package/dist/raceWithAbortSignal.d.ts +2 -0
  44. package/dist/raceWithAbortSignal.d.ts.map +1 -0
  45. package/dist/raceWithAbortSignal.js +45 -0
  46. package/dist/raceWithAbortSignal.js.map +1 -0
  47. package/dist/sdkIdentity.d.ts +2 -5
  48. package/dist/sdkIdentity.d.ts.map +1 -1
  49. package/dist/sdkIdentity.js +1 -1
  50. package/dist/server/adkEventStream.d.ts +25 -27
  51. package/dist/server/adkEventStream.d.ts.map +1 -1
  52. package/dist/server/adkEventStream.js +23 -15
  53. package/dist/server/adkEventStream.js.map +1 -1
  54. package/dist/server/createAdkApiRoute.d.ts +23 -25
  55. package/dist/server/createAdkApiRoute.d.ts.map +1 -1
  56. package/dist/server/index.d.ts +4 -4
  57. package/dist/server/index.d.ts.map +1 -0
  58. package/dist/server/parseAdkRequest.d.ts +18 -20
  59. package/dist/server/parseAdkRequest.d.ts.map +1 -1
  60. package/dist/structuredEvents.d.ts +2 -5
  61. package/dist/structuredEvents.d.ts.map +1 -1
  62. package/dist/structuredEvents.js +1 -1
  63. package/dist/structuredEvents.js.map +1 -1
  64. package/dist/toAdkFunctionResponse.d.ts +2 -5
  65. package/dist/toAdkFunctionResponse.d.ts.map +1 -1
  66. package/dist/trimTrailingSlashes.d.ts +1 -4
  67. package/dist/trimTrailingSlashes.d.ts.map +1 -1
  68. package/dist/types.d.ts +253 -256
  69. package/dist/types.d.ts.map +1 -1
  70. package/dist/types.js.map +1 -1
  71. package/dist/useAdkMessages.d.ts +26 -29
  72. package/dist/useAdkMessages.d.ts.map +1 -1
  73. package/dist/useAdkMessages.js +2 -0
  74. package/dist/useAdkMessages.js.map +1 -1
  75. package/dist/useAdkRuntime.d.ts +50 -52
  76. package/dist/useAdkRuntime.d.ts.map +1 -1
  77. package/dist/useAdkRuntime.js +1 -1
  78. package/package.json +10 -9
  79. package/src/AdkClient.test.ts +50 -0
  80. package/src/AdkClient.ts +10 -3
  81. package/src/AdkEventAccumulator.test.ts +312 -11
  82. package/src/AdkEventAccumulator.ts +68 -142
  83. package/src/AdkSessionAdapter.test.ts +284 -1
  84. package/src/AdkSessionAdapter.ts +32 -8
  85. package/src/adkAuthRequest.test.ts +101 -0
  86. package/src/adkAuthRequest.ts +50 -0
  87. package/src/adkToolApproval.test.ts +0 -4
  88. package/src/adkToolApproval.ts +1 -3
  89. package/src/convertAdkMessages.test.ts +41 -0
  90. package/src/convertAdkMessages.ts +19 -7
  91. package/src/hooks.render.test.tsx +95 -2
  92. package/src/hooks.ts +12 -13
  93. package/src/normalizeAdkPart.ts +35 -0
  94. package/src/raceWithAbortSignal.test.ts +73 -0
  95. package/src/raceWithAbortSignal.ts +48 -0
  96. package/src/server/adkEventStream.test.ts +147 -0
  97. package/src/server/adkEventStream.ts +27 -20
  98. package/src/structuredEvents.test.ts +69 -0
  99. package/src/structuredEvents.ts +4 -1
  100. package/src/types.ts +1 -1
  101. package/src/useAdkMessages.test.ts +102 -3
  102. package/src/useAdkMessages.ts +2 -0
  103. package/src/useAdkRuntime.replacement.test.tsx +11 -7
@@ -1,32 +1,29 @@
1
- import { AdkAuthRequest, AdkMessage, AdkMessageMetadata, AdkSendMessageConfig, AdkStreamCallback, AdkThreadSnapshot, AdkToolConfirmation, OnAdkAgentTransferCallback, OnAdkCustomEventCallback, OnAdkErrorCallback } from "./types.js";
2
- //#region src/useAdkMessages.d.ts
3
- type UseAdkMessagesOptions = {
4
- stream: AdkStreamCallback;
5
- eventHandlers?: {
6
- onError?: OnAdkErrorCallback;
7
- onCustomEvent?: OnAdkCustomEventCallback;
8
- onAgentTransfer?: OnAdkAgentTransferCallback;
9
- };
1
+ import type { AdkMessage, AdkMessageMetadata, AdkSendMessageConfig, AdkStreamCallback, AdkToolConfirmation, AdkAuthRequest, AdkThreadSnapshot, OnAdkErrorCallback, OnAdkCustomEventCallback, OnAdkAgentTransferCallback } from "./types.js";
2
+ export type UseAdkMessagesOptions = {
3
+ stream: AdkStreamCallback;
4
+ eventHandlers?: {
5
+ onError?: OnAdkErrorCallback;
6
+ onCustomEvent?: OnAdkCustomEventCallback;
7
+ onAgentTransfer?: OnAdkAgentTransferCallback;
8
+ };
10
9
  };
11
- declare const useAdkMessages: ({ stream, eventHandlers }: UseAdkMessagesOptions) => {
12
- messages: AdkMessage[];
13
- stateDelta: Record<string, unknown>;
14
- agentInfo: {
15
- name?: string | undefined;
16
- branch?: string | undefined;
17
- };
18
- longRunningToolIds: string[];
19
- artifactDelta: Record<string, number>;
20
- toolConfirmations: AdkToolConfirmation[];
21
- authRequests: AdkAuthRequest[];
22
- escalated: boolean;
23
- messageMetadata: Map<string, AdkMessageMetadata>;
24
- sendMessage: (newMessages: AdkMessage[], config: AdkSendMessageConfig) => Promise<void>;
25
- cancel: () => void;
26
- setMessages: (msgs: AdkMessage[]) => void;
27
- replaceMessages: (msgs: AdkMessage[]) => void;
28
- applySnapshot: (snapshot: AdkThreadSnapshot) => void;
10
+ export declare const useAdkMessages: ({ stream, eventHandlers, }: UseAdkMessagesOptions) => {
11
+ messages: AdkMessage[];
12
+ stateDelta: Record<string, unknown>;
13
+ agentInfo: {
14
+ name?: string | undefined;
15
+ branch?: string | undefined;
16
+ };
17
+ longRunningToolIds: string[];
18
+ artifactDelta: Record<string, number>;
19
+ toolConfirmations: AdkToolConfirmation[];
20
+ authRequests: AdkAuthRequest[];
21
+ escalated: boolean;
22
+ messageMetadata: Map<string, AdkMessageMetadata>;
23
+ sendMessage: (newMessages: AdkMessage[], config: AdkSendMessageConfig) => Promise<void>;
24
+ cancel: () => void;
25
+ setMessages: (msgs: AdkMessage[]) => void;
26
+ replaceMessages: (msgs: AdkMessage[]) => void;
27
+ applySnapshot: (snapshot: AdkThreadSnapshot) => void;
29
28
  };
30
- //#endregion
31
- export { UseAdkMessagesOptions, useAdkMessages };
32
29
  //# sourceMappingURL=useAdkMessages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAdkMessages.d.ts","names":[],"sources":["../src/useAdkMessages.ts"],"mappings":";;KAgCY;EACV,QAAQ;EACR;IACE,UAAU;IACV,gBAAgB;IAChB,kBAAkB;;;cAcT,mBAAkB,QAAA,iBAG5B;;;;IAIQ;IACE;;;;;;;;EAsFW,cAAA,aAAA,cAAY,QAAU,yBAAoB;;EAvDhB,cAAA,MAAA;EAmCvC,kBAAA,MAAA;EAnBI,gBAAA,UAAA"}
1
+ {"version":3,"file":"useAdkMessages.d.ts","sourceRoot":"","sources":["../src/useAdkMessages.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAEV,UAAU,EACV,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC3B,mBAAgB;AAEjB,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,aAAa,CAAC,EAAE;QACd,OAAO,CAAC,EAAE,kBAAkB,CAAC;QAC7B,aAAa,CAAC,EAAE,wBAAwB,CAAC;QACzC,eAAe,CAAC,EAAE,0BAA0B,CAAC;KAC9C,CAAC;CACH,CAAC;AAYF,eAAO,MAAM,cAAc,GAAI,4BAG5B,qBAAqB;;;;eAIb,MAAM,GAAG,SAAS;iBAChB,MAAM,GAAG,SAAS;;;;;;;;+BAsFP,UAAU,EAAE,UAAU,oBAAoB;;wBAvDhB,UAAU,EAAE;4BAmCnD,UAAU,EAAE;8BAnBR,iBAAiB;CAwM/B,CAAC"}
@@ -81,6 +81,8 @@ const useAdkMessages = ({ stream, eventHandlers }) => {
81
81
  for (const event of messagesToEvents(newMessagesWithId)) accumulator.processEvent(event);
82
82
  setMessagesImmediate(accumulator.getMessages());
83
83
  setLongRunningToolIds(accumulator.getLongRunningToolIds());
84
+ setToolConfirmations(accumulator.getToolConfirmations());
85
+ setAuthRequests(accumulator.getAuthRequests());
84
86
  abortControllerRef.current?.abort();
85
87
  const abortController = new AbortController();
86
88
  abortControllerRef.current = abortController;
@@ -1 +1 @@
1
- {"version":3,"file":"useAdkMessages.js","names":[],"sources":["../src/useAdkMessages.ts"],"sourcesContent":["import {\n useState,\n useCallback,\n useEffect,\n useInsertionEffect,\n useRef,\n useMemo,\n} from \"react\";\nimport { generateId } from \"@assistant-ui/core\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n abortableIterable,\n invokeUserCallback,\n openAbortableIterable,\n} from \"@assistant-ui/core/internal\";\nimport { AdkEventAccumulator } from \"./AdkEventAccumulator\";\nimport { contentToParts } from \"./contentToParts\";\nimport { toAdkFunctionResponse } from \"./toAdkFunctionResponse\";\nimport type {\n AdkEvent,\n AdkMessage,\n AdkMessageMetadata,\n AdkSendMessageConfig,\n AdkStreamCallback,\n AdkToolConfirmation,\n AdkAuthRequest,\n AdkThreadSnapshot,\n OnAdkErrorCallback,\n OnAdkCustomEventCallback,\n OnAdkAgentTransferCallback,\n} from \"./types\";\n\nexport type UseAdkMessagesOptions = {\n stream: AdkStreamCallback;\n eventHandlers?: {\n onError?: OnAdkErrorCallback;\n onCustomEvent?: OnAdkCustomEventCallback;\n onAgentTransfer?: OnAdkAgentTransferCallback;\n };\n};\n\ntype AdkRuntimeCallbackName = \"onError\" | \"onCustomEvent\" | \"onAgentTransfer\";\n\nconst invokeAdkRuntimeCallback = <TArgs extends readonly unknown[]>(\n name: AdkRuntimeCallbackName,\n callback: ((...args: TArgs) => unknown) | undefined,\n ...args: TArgs\n): void => {\n void invokeUserCallback(\"react-google-adk\", name, callback, ...args);\n};\n\nexport const useAdkMessages = ({\n stream,\n eventHandlers,\n}: UseAdkMessagesOptions) => {\n const [messages, _setMessages] = useState<AdkMessage[]>([]);\n const [stateDelta, setStateDelta] = useState<Record<string, unknown>>({});\n const [agentInfo, setAgentInfo] = useState<{\n name?: string | undefined;\n branch?: string | undefined;\n }>({});\n const [longRunningToolIds, _setLongRunningToolIds] = useState<string[]>([]);\n const [artifactDelta, setArtifactDelta] = useState<Record<string, number>>(\n {},\n );\n const [toolConfirmations, setToolConfirmations] = useState<\n AdkToolConfirmation[]\n >([]);\n const [authRequests, setAuthRequests] = useState<AdkAuthRequest[]>([]);\n const [escalated, setEscalated] = useState(false);\n const [messageMetadata, setMessageMetadata] = useState<\n Map<string, AdkMessageMetadata>\n >(new Map());\n const lastTransferToAgentRef = useRef<string | undefined>(undefined);\n // setMessagesImmediate and setLongRunningToolIds are the only writers of their state and publish these refs with it, so neither ref trails a commit.\n const messagesRef = useRef(messages);\n const longRunningToolIdsRef = useRef(longRunningToolIds);\n const stateDeltaRef = useRef(stateDelta);\n useInsertionEffect(() => {\n stateDeltaRef.current = stateDelta;\n }, [stateDelta]);\n const artifactDeltaRef = useRef(artifactDelta);\n useInsertionEffect(() => {\n artifactDeltaRef.current = artifactDelta;\n }, [artifactDelta]);\n const messageMetadataRef = useRef(messageMetadata);\n useInsertionEffect(() => {\n messageMetadataRef.current = messageMetadata;\n }, [messageMetadata]);\n\n const setMessagesImmediate = useCallback((msgs: AdkMessage[]) => {\n messagesRef.current = msgs;\n _setMessages(msgs);\n }, []);\n const setLongRunningToolIds = useCallback((ids: string[]) => {\n longRunningToolIdsRef.current = ids;\n _setLongRunningToolIds(ids);\n }, []);\n\n /**\n * Swap the thread over to a loaded snapshot in one commit. Unlike\n * {@link replaceMessages} this never passes through a cleared state, so a\n * refetch that lands while a confirmation is on screen replaces it rather\n * than blanking it first.\n */\n const applySnapshot = useCallback(\n (snapshot: AdkThreadSnapshot) => {\n setMessagesImmediate(snapshot.messages);\n setLongRunningToolIds(snapshot.longRunningToolIds ?? []);\n setToolConfirmations(snapshot.toolConfirmations ?? []);\n setAuthRequests(snapshot.authRequests ?? []);\n setEscalated(snapshot.escalated ?? false);\n setMessageMetadata(snapshot.messageMetadata ?? new Map());\n setStateDelta(snapshot.stateDelta ?? {});\n setArtifactDelta(snapshot.artifactDelta ?? {});\n setAgentInfo(snapshot.agentInfo ?? {});\n },\n [setLongRunningToolIds, setMessagesImmediate],\n );\n\n // Replace the message list AND reset derived per-turn HITL state.\n // Used by truncation paths (edit, reload) so that stale interrupt\n // markers and per-message metadata from the removed messages don't leak\n // into the next turn.\n const replaceMessages = useCallback(\n (msgs: AdkMessage[]) => {\n setMessagesImmediate(msgs);\n setLongRunningToolIds([]);\n setToolConfirmations([]);\n setAuthRequests([]);\n setEscalated(false);\n setMessageMetadata(new Map());\n },\n [setLongRunningToolIds, setMessagesImmediate],\n );\n\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const { onError, onCustomEvent, onAgentTransfer } = useMemo(\n () => eventHandlers ?? {},\n [eventHandlers],\n );\n\n const aui = useAui();\n const sendMessage = useCallback(\n async (newMessages: AdkMessage[], config: AdkSendMessageConfig) => {\n const newMessagesWithId = newMessages.map((m) =>\n m.id ? m : { ...m, id: generateId() },\n ) as AdkMessage[];\n\n // A staged message is already in the thread under its own id, and the\n // merged event below re-emits the whole batch under the first one. Seeding\n // with the originals would leave every later staged id beside the merged\n // copy of itself.\n const resentIds = new Set(newMessagesWithId.map((m) => m.id));\n // The optimistic event for a tool-only batch carries no author, so the accumulator cannot settle the calls this send answers.\n const answeredToolCallIds = new Set(\n newMessagesWithId.flatMap((m) =>\n m.type === \"tool\" ? [m.tool_call_id] : [],\n ),\n );\n const accumulator = new AdkEventAccumulator(\n messagesRef.current.filter((m) => !resentIds.has(m.id)),\n longRunningToolIdsRef.current.filter(\n (id) => !answeredToolCallIds.has(id),\n ),\n );\n for (const event of messagesToEvents(newMessagesWithId)) {\n accumulator.processEvent(event);\n }\n setMessagesImmediate(accumulator.getMessages());\n setLongRunningToolIds(accumulator.getLongRunningToolIds());\n\n // Google ADK replaces active runs, while React LangGraph queues sends.\n abortControllerRef.current?.abort();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n try {\n const response = await openAbortableIterable(\n stream(newMessagesWithId, {\n ...config,\n abortSignal: abortController.signal,\n initialize: async () => {\n return await aui.threadListItem.initialize();\n },\n }),\n abortController.signal,\n );\n if (!response) return;\n\n for await (const event of abortableIterable(\n response,\n abortController.signal,\n )) {\n if (\n abortController.signal.aborted ||\n abortControllerRef.current !== abortController\n ) {\n break;\n }\n const updatedMessages = accumulator.processEvent(event);\n setMessagesImmediate(updatedMessages);\n setStateDelta({\n ...stateDeltaRef.current,\n ...accumulator.getStateDelta(),\n });\n setAgentInfo(accumulator.getAgentInfo());\n setLongRunningToolIds(accumulator.getLongRunningToolIds());\n setArtifactDelta({\n ...artifactDeltaRef.current,\n ...accumulator.getArtifactDelta(),\n });\n setToolConfirmations(accumulator.getToolConfirmations());\n setAuthRequests(accumulator.getAuthRequests());\n setEscalated(accumulator.isEscalated());\n {\n const newMeta = accumulator.getMessageMetadata();\n if (newMeta.size > 0) {\n setMessageMetadata(\n new Map([...messageMetadataRef.current, ...newMeta]),\n );\n }\n }\n\n const transfer = accumulator.getLastTransferToAgent();\n if (transfer && transfer !== lastTransferToAgentRef.current) {\n lastTransferToAgentRef.current = transfer;\n invokeAdkRuntimeCallback(\n \"onAgentTransfer\",\n onAgentTransfer,\n transfer,\n );\n }\n\n // Fire custom event callback for events with customMetadata\n if (event.customMetadata && onCustomEvent) {\n for (const [key, value] of Object.entries(event.customMetadata)) {\n invokeAdkRuntimeCallback(\n \"onCustomEvent\",\n onCustomEvent,\n key,\n value,\n );\n }\n }\n\n if (event.errorCode || event.errorMessage) {\n invokeAdkRuntimeCallback(\n \"onError\",\n onError,\n event.errorMessage ?? event.errorCode,\n );\n }\n }\n } catch (error) {\n if (\n !abortController.signal.aborted &&\n abortControllerRef.current === abortController &&\n !(error instanceof Error && error.name === \"AbortError\")\n ) {\n throw error;\n }\n } finally {\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n }\n },\n [\n aui,\n setMessagesImmediate,\n setLongRunningToolIds,\n stream,\n onError,\n onCustomEvent,\n onAgentTransfer,\n ],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, []);\n\n useEffect(() => cancel, [cancel]);\n\n return {\n messages,\n stateDelta,\n agentInfo,\n longRunningToolIds,\n artifactDelta,\n toolConfirmations,\n authRequests,\n escalated,\n messageMetadata,\n sendMessage,\n cancel,\n setMessages: setMessagesImmediate,\n replaceMessages,\n applySnapshot,\n };\n};\n\n/**\n * Transport sends every human and tool message of one `send` call as a single\n * ADK `Content`, and ADK parses that event's function responses before running\n * any tool, so the batch runs whole or not at all. The optimistic projection\n * has to sit on the same boundary, so a run of those messages becomes one\n * synthetic event whose parts come from the same per-message conversion.\n *\n * The transport drops `ai` messages from that `Content`, so one interleaved\n * between two replies does not split the batch on the wire and must not split\n * it here either. It still becomes its own event, placed after the merged one,\n * so the optimistic projection keeps the assistant turn.\n *\n * @internal — exported for unit tests.\n */\nexport const messagesToEvents = (messages: AdkMessage[]): AdkEvent[] => {\n // A reload sends no messages at all, and the empty user content the transport\n // puts on the wire for it is not part of the optimistic view: projecting one\n // would put an empty user bubble above every regenerated turn.\n if (messages.length === 0) return [];\n\n const events: AdkEvent[] = [];\n const run: AdkMessage[] = [];\n let runIndex = 0;\n\n for (const msg of messages) {\n if (msg.type === \"ai\") {\n events.push(messageToEvent(msg));\n } else {\n if (run.length === 0) runIndex = events.length;\n run.push(msg);\n }\n }\n\n const parts = run.flatMap((m) => messageToEvent(m).content?.parts ?? []);\n const human = run.find((m) => m.type === \"human\");\n\n // A batch that contributes no part still reaches the wire: the transport\n // sends an empty user `Content`, which a reload replays as an empty human\n // message. Emitting it here keeps the optimistic view equal to that replay.\n if (parts.length === 0) parts.push({ text: \"\" });\n\n const event: AdkEvent = { id: (human ?? run[0])?.id ?? generateId() };\n if (human || run.length === 0) event.author = \"user\";\n event.content = { role: \"user\", parts };\n events.splice(run.length > 0 ? runIndex : events.length, 0, event);\n\n return events;\n};\n\n/** @internal — exported for unit tests. */\nexport const messageToEvent = (msg: AdkMessage): AdkEvent => {\n if (msg.type === \"human\") {\n return {\n id: msg.id ?? generateId(),\n author: \"user\",\n content: { role: \"user\", parts: contentToParts(msg.content) },\n };\n }\n\n if (msg.type === \"tool\") {\n let response: unknown;\n try {\n response = JSON.parse(msg.content);\n } catch {\n response = msg.content;\n }\n return {\n id: msg.id ?? generateId(),\n content: {\n role: \"user\",\n parts: [\n {\n functionResponse: {\n name: msg.name,\n id: msg.tool_call_id,\n response: toAdkFunctionResponse(response, msg.status === \"error\"),\n },\n },\n ],\n },\n };\n }\n\n const result: AdkEvent = { id: msg.id ?? generateId() };\n if (msg.author != null) result.author = msg.author;\n result.content = {\n role: \"model\",\n parts: [\n ...contentToParts(msg.content),\n ...(msg.tool_calls?.map((tc) => ({\n functionCall: { name: tc.name, id: tc.id, args: { ...tc.args } },\n })) ?? []),\n ],\n };\n return result;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useAdkMessages.js","names":[],"sources":["../src/useAdkMessages.ts"],"sourcesContent":["import {\n useState,\n useCallback,\n useEffect,\n useInsertionEffect,\n useRef,\n useMemo,\n} from \"react\";\nimport { generateId } from \"@assistant-ui/core\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n abortableIterable,\n invokeUserCallback,\n openAbortableIterable,\n} from \"@assistant-ui/core/internal\";\nimport { AdkEventAccumulator } from \"./AdkEventAccumulator\";\nimport { contentToParts } from \"./contentToParts\";\nimport { toAdkFunctionResponse } from \"./toAdkFunctionResponse\";\nimport type {\n AdkEvent,\n AdkMessage,\n AdkMessageMetadata,\n AdkSendMessageConfig,\n AdkStreamCallback,\n AdkToolConfirmation,\n AdkAuthRequest,\n AdkThreadSnapshot,\n OnAdkErrorCallback,\n OnAdkCustomEventCallback,\n OnAdkAgentTransferCallback,\n} from \"./types\";\n\nexport type UseAdkMessagesOptions = {\n stream: AdkStreamCallback;\n eventHandlers?: {\n onError?: OnAdkErrorCallback;\n onCustomEvent?: OnAdkCustomEventCallback;\n onAgentTransfer?: OnAdkAgentTransferCallback;\n };\n};\n\ntype AdkRuntimeCallbackName = \"onError\" | \"onCustomEvent\" | \"onAgentTransfer\";\n\nconst invokeAdkRuntimeCallback = <TArgs extends readonly unknown[]>(\n name: AdkRuntimeCallbackName,\n callback: ((...args: TArgs) => unknown) | undefined,\n ...args: TArgs\n): void => {\n void invokeUserCallback(\"react-google-adk\", name, callback, ...args);\n};\n\nexport const useAdkMessages = ({\n stream,\n eventHandlers,\n}: UseAdkMessagesOptions) => {\n const [messages, _setMessages] = useState<AdkMessage[]>([]);\n const [stateDelta, setStateDelta] = useState<Record<string, unknown>>({});\n const [agentInfo, setAgentInfo] = useState<{\n name?: string | undefined;\n branch?: string | undefined;\n }>({});\n const [longRunningToolIds, _setLongRunningToolIds] = useState<string[]>([]);\n const [artifactDelta, setArtifactDelta] = useState<Record<string, number>>(\n {},\n );\n const [toolConfirmations, setToolConfirmations] = useState<\n AdkToolConfirmation[]\n >([]);\n const [authRequests, setAuthRequests] = useState<AdkAuthRequest[]>([]);\n const [escalated, setEscalated] = useState(false);\n const [messageMetadata, setMessageMetadata] = useState<\n Map<string, AdkMessageMetadata>\n >(new Map());\n const lastTransferToAgentRef = useRef<string | undefined>(undefined);\n // setMessagesImmediate and setLongRunningToolIds are the only writers of their state and publish these refs with it, so neither ref trails a commit.\n const messagesRef = useRef(messages);\n const longRunningToolIdsRef = useRef(longRunningToolIds);\n const stateDeltaRef = useRef(stateDelta);\n useInsertionEffect(() => {\n stateDeltaRef.current = stateDelta;\n }, [stateDelta]);\n const artifactDeltaRef = useRef(artifactDelta);\n useInsertionEffect(() => {\n artifactDeltaRef.current = artifactDelta;\n }, [artifactDelta]);\n const messageMetadataRef = useRef(messageMetadata);\n useInsertionEffect(() => {\n messageMetadataRef.current = messageMetadata;\n }, [messageMetadata]);\n\n const setMessagesImmediate = useCallback((msgs: AdkMessage[]) => {\n messagesRef.current = msgs;\n _setMessages(msgs);\n }, []);\n const setLongRunningToolIds = useCallback((ids: string[]) => {\n longRunningToolIdsRef.current = ids;\n _setLongRunningToolIds(ids);\n }, []);\n\n /**\n * Swap the thread over to a loaded snapshot in one commit. Unlike\n * {@link replaceMessages} this never passes through a cleared state, so a\n * refetch that lands while a confirmation is on screen replaces it rather\n * than blanking it first.\n */\n const applySnapshot = useCallback(\n (snapshot: AdkThreadSnapshot) => {\n setMessagesImmediate(snapshot.messages);\n setLongRunningToolIds(snapshot.longRunningToolIds ?? []);\n setToolConfirmations(snapshot.toolConfirmations ?? []);\n setAuthRequests(snapshot.authRequests ?? []);\n setEscalated(snapshot.escalated ?? false);\n setMessageMetadata(snapshot.messageMetadata ?? new Map());\n setStateDelta(snapshot.stateDelta ?? {});\n setArtifactDelta(snapshot.artifactDelta ?? {});\n setAgentInfo(snapshot.agentInfo ?? {});\n },\n [setLongRunningToolIds, setMessagesImmediate],\n );\n\n // Replace the message list AND reset derived per-turn HITL state.\n // Used by truncation paths (edit, reload) so that stale interrupt\n // markers and per-message metadata from the removed messages don't leak\n // into the next turn.\n const replaceMessages = useCallback(\n (msgs: AdkMessage[]) => {\n setMessagesImmediate(msgs);\n setLongRunningToolIds([]);\n setToolConfirmations([]);\n setAuthRequests([]);\n setEscalated(false);\n setMessageMetadata(new Map());\n },\n [setLongRunningToolIds, setMessagesImmediate],\n );\n\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const { onError, onCustomEvent, onAgentTransfer } = useMemo(\n () => eventHandlers ?? {},\n [eventHandlers],\n );\n\n const aui = useAui();\n const sendMessage = useCallback(\n async (newMessages: AdkMessage[], config: AdkSendMessageConfig) => {\n const newMessagesWithId = newMessages.map((m) =>\n m.id ? m : { ...m, id: generateId() },\n ) as AdkMessage[];\n\n // A staged message is already in the thread under its own id, and the\n // merged event below re-emits the whole batch under the first one. Seeding\n // with the originals would leave every later staged id beside the merged\n // copy of itself.\n const resentIds = new Set(newMessagesWithId.map((m) => m.id));\n // The optimistic event for a tool-only batch carries no author, so the accumulator cannot settle the calls this send answers.\n const answeredToolCallIds = new Set(\n newMessagesWithId.flatMap((m) =>\n m.type === \"tool\" ? [m.tool_call_id] : [],\n ),\n );\n const accumulator = new AdkEventAccumulator(\n messagesRef.current.filter((m) => !resentIds.has(m.id)),\n longRunningToolIdsRef.current.filter(\n (id) => !answeredToolCallIds.has(id),\n ),\n );\n for (const event of messagesToEvents(newMessagesWithId)) {\n accumulator.processEvent(event);\n }\n setMessagesImmediate(accumulator.getMessages());\n setLongRunningToolIds(accumulator.getLongRunningToolIds());\n setToolConfirmations(accumulator.getToolConfirmations());\n setAuthRequests(accumulator.getAuthRequests());\n\n // Google ADK replaces active runs, while React LangGraph queues sends.\n abortControllerRef.current?.abort();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n try {\n const response = await openAbortableIterable(\n stream(newMessagesWithId, {\n ...config,\n abortSignal: abortController.signal,\n initialize: async () => {\n return await aui.threadListItem.initialize();\n },\n }),\n abortController.signal,\n );\n if (!response) return;\n\n for await (const event of abortableIterable(\n response,\n abortController.signal,\n )) {\n if (\n abortController.signal.aborted ||\n abortControllerRef.current !== abortController\n ) {\n break;\n }\n const updatedMessages = accumulator.processEvent(event);\n setMessagesImmediate(updatedMessages);\n setStateDelta({\n ...stateDeltaRef.current,\n ...accumulator.getStateDelta(),\n });\n setAgentInfo(accumulator.getAgentInfo());\n setLongRunningToolIds(accumulator.getLongRunningToolIds());\n setArtifactDelta({\n ...artifactDeltaRef.current,\n ...accumulator.getArtifactDelta(),\n });\n setToolConfirmations(accumulator.getToolConfirmations());\n setAuthRequests(accumulator.getAuthRequests());\n setEscalated(accumulator.isEscalated());\n {\n const newMeta = accumulator.getMessageMetadata();\n if (newMeta.size > 0) {\n setMessageMetadata(\n new Map([...messageMetadataRef.current, ...newMeta]),\n );\n }\n }\n\n const transfer = accumulator.getLastTransferToAgent();\n if (transfer && transfer !== lastTransferToAgentRef.current) {\n lastTransferToAgentRef.current = transfer;\n invokeAdkRuntimeCallback(\n \"onAgentTransfer\",\n onAgentTransfer,\n transfer,\n );\n }\n\n // Fire custom event callback for events with customMetadata\n if (event.customMetadata && onCustomEvent) {\n for (const [key, value] of Object.entries(event.customMetadata)) {\n invokeAdkRuntimeCallback(\n \"onCustomEvent\",\n onCustomEvent,\n key,\n value,\n );\n }\n }\n\n if (event.errorCode || event.errorMessage) {\n invokeAdkRuntimeCallback(\n \"onError\",\n onError,\n event.errorMessage ?? event.errorCode,\n );\n }\n }\n } catch (error) {\n if (\n !abortController.signal.aborted &&\n abortControllerRef.current === abortController &&\n !(error instanceof Error && error.name === \"AbortError\")\n ) {\n throw error;\n }\n } finally {\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n }\n },\n [\n aui,\n setMessagesImmediate,\n setLongRunningToolIds,\n stream,\n onError,\n onCustomEvent,\n onAgentTransfer,\n ],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, []);\n\n useEffect(() => cancel, [cancel]);\n\n return {\n messages,\n stateDelta,\n agentInfo,\n longRunningToolIds,\n artifactDelta,\n toolConfirmations,\n authRequests,\n escalated,\n messageMetadata,\n sendMessage,\n cancel,\n setMessages: setMessagesImmediate,\n replaceMessages,\n applySnapshot,\n };\n};\n\n/**\n * Transport sends every human and tool message of one `send` call as a single\n * ADK `Content`, and ADK parses that event's function responses before running\n * any tool, so the batch runs whole or not at all. The optimistic projection\n * has to sit on the same boundary, so a run of those messages becomes one\n * synthetic event whose parts come from the same per-message conversion.\n *\n * The transport drops `ai` messages from that `Content`, so one interleaved\n * between two replies does not split the batch on the wire and must not split\n * it here either. It still becomes its own event, placed after the merged one,\n * so the optimistic projection keeps the assistant turn.\n *\n * @internal — exported for unit tests.\n */\nexport const messagesToEvents = (messages: AdkMessage[]): AdkEvent[] => {\n // A reload sends no messages at all, and the empty user content the transport\n // puts on the wire for it is not part of the optimistic view: projecting one\n // would put an empty user bubble above every regenerated turn.\n if (messages.length === 0) return [];\n\n const events: AdkEvent[] = [];\n const run: AdkMessage[] = [];\n let runIndex = 0;\n\n for (const msg of messages) {\n if (msg.type === \"ai\") {\n events.push(messageToEvent(msg));\n } else {\n if (run.length === 0) runIndex = events.length;\n run.push(msg);\n }\n }\n\n const parts = run.flatMap((m) => messageToEvent(m).content?.parts ?? []);\n const human = run.find((m) => m.type === \"human\");\n\n // A batch that contributes no part still reaches the wire: the transport\n // sends an empty user `Content`, which a reload replays as an empty human\n // message. Emitting it here keeps the optimistic view equal to that replay.\n if (parts.length === 0) parts.push({ text: \"\" });\n\n const event: AdkEvent = { id: (human ?? run[0])?.id ?? generateId() };\n if (human || run.length === 0) event.author = \"user\";\n event.content = { role: \"user\", parts };\n events.splice(run.length > 0 ? runIndex : events.length, 0, event);\n\n return events;\n};\n\n/** @internal — exported for unit tests. */\nexport const messageToEvent = (msg: AdkMessage): AdkEvent => {\n if (msg.type === \"human\") {\n return {\n id: msg.id ?? generateId(),\n author: \"user\",\n content: { role: \"user\", parts: contentToParts(msg.content) },\n };\n }\n\n if (msg.type === \"tool\") {\n let response: unknown;\n try {\n response = JSON.parse(msg.content);\n } catch {\n response = msg.content;\n }\n return {\n id: msg.id ?? generateId(),\n content: {\n role: \"user\",\n parts: [\n {\n functionResponse: {\n name: msg.name,\n id: msg.tool_call_id,\n response: toAdkFunctionResponse(response, msg.status === \"error\"),\n },\n },\n ],\n },\n };\n }\n\n const result: AdkEvent = { id: msg.id ?? generateId() };\n if (msg.author != null) result.author = msg.author;\n result.content = {\n role: \"model\",\n parts: [\n ...contentToParts(msg.content),\n ...(msg.tool_calls?.map((tc) => ({\n functionCall: { name: tc.name, id: tc.id, args: { ...tc.args } },\n })) ?? []),\n ],\n };\n return result;\n};\n"],"mappings":";;;;;;;;AA2CA,MAAM,4BACJ,MACA,UACA,GAAG,SACM;CACT,mBAAwB,oBAAoB,MAAM,UAAU,GAAG,IAAI;AACrE;AAEA,MAAa,kBAAkB,EAC7B,QACA,oBAC2B;CAC3B,MAAM,CAAC,UAAU,gBAAgB,SAAuB,CAAC,CAAC;CAC1D,MAAM,CAAC,YAAY,iBAAiB,SAAkC,CAAC,CAAC;CACxE,MAAM,CAAC,WAAW,gBAAgB,SAG/B,CAAC,CAAC;CACL,MAAM,CAAC,oBAAoB,0BAA0B,SAAmB,CAAC,CAAC;CAC1E,MAAM,CAAC,eAAe,oBAAoB,SACxC,CAAC,CACH;CACA,MAAM,CAAC,mBAAmB,wBAAwB,SAEhD,CAAC,CAAC;CACJ,MAAM,CAAC,cAAc,mBAAmB,SAA2B,CAAC,CAAC;CACrE,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAChD,MAAM,CAAC,iBAAiB,sBAAsB,yBAE5C,IAAI,IAAI,CAAC;CACX,MAAM,yBAAyB,OAA2B,KAAA,CAAS;CAEnE,MAAM,cAAc,OAAO,QAAQ;CACnC,MAAM,wBAAwB,OAAO,kBAAkB;CACvD,MAAM,gBAAgB,OAAO,UAAU;CACvC,yBAAyB;EACvB,cAAc,UAAU;CAC1B,GAAG,CAAC,UAAU,CAAC;CACf,MAAM,mBAAmB,OAAO,aAAa;CAC7C,yBAAyB;EACvB,iBAAiB,UAAU;CAC7B,GAAG,CAAC,aAAa,CAAC;CAClB,MAAM,qBAAqB,OAAO,eAAe;CACjD,yBAAyB;EACvB,mBAAmB,UAAU;CAC/B,GAAG,CAAC,eAAe,CAAC;CAEpB,MAAM,uBAAuB,aAAa,SAAuB;EAC/D,YAAY,UAAU;EACtB,aAAa,IAAI;CACnB,GAAG,CAAC,CAAC;CACL,MAAM,wBAAwB,aAAa,QAAkB;EAC3D,sBAAsB,UAAU;EAChC,uBAAuB,GAAG;CAC5B,GAAG,CAAC,CAAC;;;;;;;CAQL,MAAM,gBAAgB,aACnB,aAAgC;EAC/B,qBAAqB,SAAS,QAAQ;EACtC,sBAAsB,SAAS,sBAAsB,CAAC,CAAC;EACvD,qBAAqB,SAAS,qBAAqB,CAAC,CAAC;EACrD,gBAAgB,SAAS,gBAAgB,CAAC,CAAC;EAC3C,aAAa,SAAS,aAAa,KAAK;EACxC,mBAAmB,SAAS,mCAAmB,IAAI,IAAI,CAAC;EACxD,cAAc,SAAS,cAAc,CAAC,CAAC;EACvC,iBAAiB,SAAS,iBAAiB,CAAC,CAAC;EAC7C,aAAa,SAAS,aAAa,CAAC,CAAC;CACvC,GACA,CAAC,uBAAuB,oBAAoB,CAC9C;CAMA,MAAM,kBAAkB,aACrB,SAAuB;EACtB,qBAAqB,IAAI;EACzB,sBAAsB,CAAC,CAAC;EACxB,qBAAqB,CAAC,CAAC;EACvB,gBAAgB,CAAC,CAAC;EAClB,aAAa,KAAK;EAClB,mCAAmB,IAAI,IAAI,CAAC;CAC9B,GACA,CAAC,uBAAuB,oBAAoB,CAC9C;CAEA,MAAM,qBAAqB,OAA+B,IAAI;CAE9D,MAAM,EAAE,SAAS,eAAe,oBAAoB,cAC5C,iBAAiB,CAAC,GACxB,CAAC,aAAa,CAChB;CAEA,MAAM,MAAM,OAAO;CACnB,MAAM,cAAc,YAClB,OAAO,aAA2B,WAAiC;EACjE,MAAM,oBAAoB,YAAY,KAAK,MACzC,EAAE,KAAK,IAAI;GAAE,GAAG;GAAG,IAAI,WAAW;EAAE,CACtC;EAMA,MAAM,YAAY,IAAI,IAAI,kBAAkB,KAAK,MAAM,EAAE,EAAE,CAAC;EAE5D,MAAM,sBAAsB,IAAI,IAC9B,kBAAkB,SAAS,MACzB,EAAE,SAAS,SAAS,CAAC,EAAE,YAAY,IAAI,CAAC,CAC1C,CACF;EACA,MAAM,cAAc,IAAI,oBACtB,YAAY,QAAQ,QAAQ,MAAM,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC,GACtD,sBAAsB,QAAQ,QAC3B,OAAO,CAAC,oBAAoB,IAAI,EAAE,CACrC,CACF;EACA,KAAK,MAAM,SAAS,iBAAiB,iBAAiB,GACpD,YAAY,aAAa,KAAK;EAEhC,qBAAqB,YAAY,YAAY,CAAC;EAC9C,sBAAsB,YAAY,sBAAsB,CAAC;EACzD,qBAAqB,YAAY,qBAAqB,CAAC;EACvD,gBAAgB,YAAY,gBAAgB,CAAC;EAG7C,mBAAmB,SAAS,MAAM;EAClC,MAAM,kBAAkB,IAAI,gBAAgB;EAC5C,mBAAmB,UAAU;EAE7B,IAAI;GACF,MAAM,WAAW,MAAM,sBACrB,OAAO,mBAAmB;IACxB,GAAG;IACH,aAAa,gBAAgB;IAC7B,YAAY,YAAY;KACtB,OAAO,MAAM,IAAI,eAAe,WAAW;IAC7C;GACF,CAAC,GACD,gBAAgB,MAClB;GACA,IAAI,CAAC,UAAU;GAEf,WAAW,MAAM,SAAS,kBACxB,UACA,gBAAgB,MAClB,GAAG;IACD,IACE,gBAAgB,OAAO,WACvB,mBAAmB,YAAY,iBAE/B;IAEF,MAAM,kBAAkB,YAAY,aAAa,KAAK;IACtD,qBAAqB,eAAe;IACpC,cAAc;KACZ,GAAG,cAAc;KACjB,GAAG,YAAY,cAAc;IAC/B,CAAC;IACD,aAAa,YAAY,aAAa,CAAC;IACvC,sBAAsB,YAAY,sBAAsB,CAAC;IACzD,iBAAiB;KACf,GAAG,iBAAiB;KACpB,GAAG,YAAY,iBAAiB;IAClC,CAAC;IACD,qBAAqB,YAAY,qBAAqB,CAAC;IACvD,gBAAgB,YAAY,gBAAgB,CAAC;IAC7C,aAAa,YAAY,YAAY,CAAC;IACtC;KACE,MAAM,UAAU,YAAY,mBAAmB;KAC/C,IAAI,QAAQ,OAAO,GACjB,mBACE,IAAI,IAAI,CAAC,GAAG,mBAAmB,SAAS,GAAG,OAAO,CAAC,CACrD;IAEJ;IAEA,MAAM,WAAW,YAAY,uBAAuB;IACpD,IAAI,YAAY,aAAa,uBAAuB,SAAS;KAC3D,uBAAuB,UAAU;KACjC,yBACE,mBACA,iBACA,QACF;IACF;IAGA,IAAI,MAAM,kBAAkB,eAC1B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,cAAc,GAC5D,yBACE,iBACA,eACA,KACA,KACF;IAIJ,IAAI,MAAM,aAAa,MAAM,cAC3B,yBACE,WACA,SACA,MAAM,gBAAgB,MAAM,SAC9B;GAEJ;EACF,SAAS,OAAO;GACd,IACE,CAAC,gBAAgB,OAAO,WACxB,mBAAmB,YAAY,mBAC/B,EAAE,iBAAiB,SAAS,MAAM,SAAS,eAE3C,MAAM;EAEV,UAAU;GACR,IAAI,mBAAmB,YAAY,iBACjC,mBAAmB,UAAU;EAEjC;CACF,GACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAEA,MAAM,SAAS,kBAAkB;EAC/B,IAAI,mBAAmB,SACrB,mBAAmB,QAAQ,MAAM;CAErC,GAAG,CAAC,CAAC;CAEL,gBAAgB,QAAQ,CAAC,MAAM,CAAC;CAEhC,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa;EACb;EACA;CACF;AACF;;;;;;;;;;;;;;;AAgBA,MAAa,oBAAoB,aAAuC;CAItE,IAAI,SAAS,WAAW,GAAG,OAAO,CAAC;CAEnC,MAAM,SAAqB,CAAC;CAC5B,MAAM,MAAoB,CAAC;CAC3B,IAAI,WAAW;CAEf,KAAK,MAAM,OAAO,UAChB,IAAI,IAAI,SAAS,MACf,OAAO,KAAK,eAAe,GAAG,CAAC;MAC1B;EACL,IAAI,IAAI,WAAW,GAAG,WAAW,OAAO;EACxC,IAAI,KAAK,GAAG;CACd;CAGF,MAAM,QAAQ,IAAI,SAAS,MAAM,eAAe,CAAC,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC;CACvE,MAAM,QAAQ,IAAI,MAAM,MAAM,EAAE,SAAS,OAAO;CAKhD,IAAI,MAAM,WAAW,GAAG,MAAM,KAAK,EAAE,MAAM,GAAG,CAAC;CAE/C,MAAM,QAAkB,EAAE,KAAK,SAAS,IAAI,GAAA,EAAK,MAAM,WAAW,EAAE;CACpE,IAAI,SAAS,IAAI,WAAW,GAAG,MAAM,SAAS;CAC9C,MAAM,UAAU;EAAE,MAAM;EAAQ;CAAM;CACtC,OAAO,OAAO,IAAI,SAAS,IAAI,WAAW,OAAO,QAAQ,GAAG,KAAK;CAEjE,OAAO;AACT;;AAGA,MAAa,kBAAkB,QAA8B;CAC3D,IAAI,IAAI,SAAS,SACf,OAAO;EACL,IAAI,IAAI,MAAM,WAAW;EACzB,QAAQ;EACR,SAAS;GAAE,MAAM;GAAQ,OAAO,eAAe,IAAI,OAAO;EAAE;CAC9D;CAGF,IAAI,IAAI,SAAS,QAAQ;EACvB,IAAI;EACJ,IAAI;GACF,WAAW,KAAK,MAAM,IAAI,OAAO;EACnC,QAAQ;GACN,WAAW,IAAI;EACjB;EACA,OAAO;GACL,IAAI,IAAI,MAAM,WAAW;GACzB,SAAS;IACP,MAAM;IACN,OAAO,CACL,EACE,kBAAkB;KAChB,MAAM,IAAI;KACV,IAAI,IAAI;KACR,UAAU,sBAAsB,UAAU,IAAI,WAAW,OAAO;IAClE,EACF,CACF;GACF;EACF;CACF;CAEA,MAAM,SAAmB,EAAE,IAAI,IAAI,MAAM,WAAW,EAAE;CACtD,IAAI,IAAI,UAAU,MAAM,OAAO,SAAS,IAAI;CAC5C,OAAO,UAAU;EACf,MAAM;EACN,OAAO,CACL,GAAG,eAAe,IAAI,OAAO,GAC7B,GAAI,IAAI,YAAY,KAAK,QAAQ,EAC/B,cAAc;GAAE,MAAM,GAAG;GAAM,IAAI,GAAG;GAAI,MAAM,EAAE,GAAG,GAAG,KAAK;EAAE,EACjE,EAAE,KAAK,CAAC,CACV;CACF;CACA,OAAO;AACT"}
@@ -1,54 +1,52 @@
1
- import { AdkMessage, AdkStreamCallback, AdkThreadSnapshot, OnAdkAgentTransferCallback, OnAdkCustomEventCallback, OnAdkErrorCallback } from "./types.js";
2
- import { AttachmentAdapter, DictationAdapter, ExternalStoreSharedOptions, FeedbackAdapter, RealtimeVoiceAdapter, RemoteThreadListAdapter, SpeechSynthesisAdapter } from "@assistant-ui/core";
3
- import { AssistantCloud } from "assistant-cloud";
4
- //#region src/useAdkRuntime.d.ts
5
- type UseAdkRuntimeOptions = ExternalStoreSharedOptions & {
6
- stream: AdkStreamCallback;
7
- /**
8
- * Called whenever the active thread's canonical (remote) ID changes, so the
9
- * value can be treated as a managed/controlled variable (e.g. synced to a URL
10
- * query param). Only the settled remote ID is emitted: while a freshly created
11
- * thread is still optimistic the value is `undefined`, and the real ID is
12
- * emitted once the thread is initialized; the transient local ID is never
13
- * surfaced.
14
- */
15
- onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;
16
- autoCancelPendingToolCalls?: boolean | undefined;
17
- unstable_allowCancellation?: boolean | undefined;
18
- getCheckpointId?: (threadId: string, parentMessages: AdkMessage[]) => Promise<string | null>;
19
- /**
20
- * Loads a thread's stored state. Called when the thread opens, and again for
21
- * `threads.reloadMainThread()`, which refetches in place rather than
22
- * remounting the runtime; the signal aborts a load the runtime no longer
23
- * needs.
24
- */
25
- load?: (threadId: string, options?: {
26
- signal?: AbortSignal | undefined;
27
- }) => Promise<AdkThreadSnapshot>;
28
- create?: () => Promise<{
29
- externalId: string;
30
- }>;
31
- delete?: (threadId: string) => Promise<void>;
32
- adapters?: {
33
- attachments?: AttachmentAdapter;
34
- speech?: SpeechSynthesisAdapter;
35
- dictation?: DictationAdapter;
36
- voice?: RealtimeVoiceAdapter;
37
- feedback?: FeedbackAdapter;
38
- } | undefined;
39
- eventHandlers?: {
40
- onError?: OnAdkErrorCallback;
41
- onCustomEvent?: OnAdkCustomEventCallback;
42
- onAgentTransfer?: OnAdkAgentTransferCallback;
43
- } | undefined;
44
- cloud?: AssistantCloud | undefined;
45
- /**
46
- * A `RemoteThreadListAdapter` to use instead of the cloud adapter.
47
- * Use with `createAdkSessionAdapter` for ADK session-backed persistence.
48
- */
49
- sessionAdapter?: RemoteThreadListAdapter | undefined;
1
+ import { type AttachmentAdapter, type DictationAdapter, type ExternalStoreSharedOptions, type FeedbackAdapter, type RealtimeVoiceAdapter, type SpeechSynthesisAdapter } from "@assistant-ui/core";
2
+ import type { AssistantCloud } from "assistant-cloud";
3
+ import type { RemoteThreadListAdapter } from "@assistant-ui/core";
4
+ import type { AdkMessage, AdkThreadSnapshot, AdkStreamCallback, OnAdkErrorCallback, OnAdkCustomEventCallback, OnAdkAgentTransferCallback } from "./types.js";
5
+ export type UseAdkRuntimeOptions = ExternalStoreSharedOptions & {
6
+ stream: AdkStreamCallback;
7
+ /**
8
+ * Called whenever the active thread's canonical (remote) ID changes, so the
9
+ * value can be treated as a managed/controlled variable (e.g. synced to a URL
10
+ * query param). Only the settled remote ID is emitted: while a freshly created
11
+ * thread is still optimistic the value is `undefined`, and the real ID is
12
+ * emitted once the thread is initialized; the transient local ID is never
13
+ * surfaced.
14
+ */
15
+ onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;
16
+ autoCancelPendingToolCalls?: boolean | undefined;
17
+ unstable_allowCancellation?: boolean | undefined;
18
+ getCheckpointId?: (threadId: string, parentMessages: AdkMessage[]) => Promise<string | null>;
19
+ /**
20
+ * Loads a thread's stored state. Called when the thread opens, and again for
21
+ * `threads.reloadMainThread()`, which refetches in place rather than
22
+ * remounting the runtime; the signal aborts a load the runtime no longer
23
+ * needs.
24
+ */
25
+ load?: (threadId: string, options?: {
26
+ signal?: AbortSignal | undefined;
27
+ }) => Promise<AdkThreadSnapshot>;
28
+ create?: () => Promise<{
29
+ externalId: string;
30
+ }>;
31
+ delete?: (threadId: string) => Promise<void>;
32
+ adapters?: {
33
+ attachments?: AttachmentAdapter;
34
+ speech?: SpeechSynthesisAdapter;
35
+ dictation?: DictationAdapter;
36
+ voice?: RealtimeVoiceAdapter;
37
+ feedback?: FeedbackAdapter;
38
+ } | undefined;
39
+ eventHandlers?: {
40
+ onError?: OnAdkErrorCallback;
41
+ onCustomEvent?: OnAdkCustomEventCallback;
42
+ onAgentTransfer?: OnAdkAgentTransferCallback;
43
+ } | undefined;
44
+ cloud?: AssistantCloud | undefined;
45
+ /**
46
+ * A `RemoteThreadListAdapter` to use instead of the cloud adapter.
47
+ * Use with `createAdkSessionAdapter` for ADK session-backed persistence.
48
+ */
49
+ sessionAdapter?: RemoteThreadListAdapter | undefined;
50
50
  };
51
- declare const useAdkRuntime: ({ cloud, sessionAdapter, create, delete: deleteFn, onThreadIdChange, ...options }: UseAdkRuntimeOptions) => import("@assistant-ui/core").AssistantRuntime;
52
- //#endregion
53
- export { UseAdkRuntimeOptions, useAdkRuntime };
51
+ export declare const useAdkRuntime: ({ cloud, sessionAdapter, create, delete: deleteFn, onThreadIdChange, ...options }: UseAdkRuntimeOptions) => import("@assistant-ui/core").AssistantRuntime;
54
52
  //# sourceMappingURL=useAdkRuntime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAdkRuntime.d.ts","names":[],"sources":["../src/useAdkRuntime.ts"],"mappings":";;;;KA6DY,uBAAuB;EACjC,QAAQ;;;;;;;;;EASR,qBAAqB;EACrB;EACA;EACA,mBACE,kBACA,gBAAgB,iBACb;;;;;;;EAOL,QACE,kBACA;IAAY,SAAS;QAClB,QAAQ;EACb,eAAe;IAAU;;EACzB,UAAU,qBAAqB;EAC/B;IAEM,cAAc;IACd,SAAS;IACT,YAAY;IACZ,QAAQ;IACR,WAAW;;EAGjB;IAEM,UAAU;IACV,gBAAgB;IAChB,kBAAkB;;EAGxB,QAAQ;;;;;EAKR,iBAAiB;;cAkYN,kBAAiB,OAAA,gBAAA,QAAA,QAAA,UAAA,qBAAA,WAO3B,sDAAoB"}
1
+ {"version":3,"file":"useAdkRuntime.d.ts","sourceRoot":"","sources":["../src/useAdkRuntime.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAK5B,MAAM,oBAAoB,CAAC;AAY5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EAEjB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC3B,mBAAgB;AAmBjB,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,GAAG;IAC9D,MAAM,EAAE,iBAAiB,CAAC;IAC1B;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACxE,0BAA0B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjD,0BAA0B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjD,eAAe,CAAC,EAAE,CAChB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,UAAU,EAAE,KACzB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,CACL,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;KAAE,KAC3C,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,QAAQ,CAAC,EACL;QACE,WAAW,CAAC,EAAE,iBAAiB,CAAC;QAChC,MAAM,CAAC,EAAE,sBAAsB,CAAC;QAChC,SAAS,CAAC,EAAE,gBAAgB,CAAC;QAC7B,KAAK,CAAC,EAAE,oBAAoB,CAAC;QAC7B,QAAQ,CAAC,EAAE,eAAe,CAAC;KAC5B,GACD,SAAS,CAAC;IACd,aAAa,CAAC,EACV;QACE,OAAO,CAAC,EAAE,kBAAkB,CAAC;QAC7B,aAAa,CAAC,EAAE,wBAAwB,CAAC;QACzC,eAAe,CAAC,EAAE,0BAA0B,CAAC;KAC9C,GACD,SAAS,CAAC;IACd,KAAK,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IACnC;;;OAGG;IACH,cAAc,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CACtD,CAAC;AAiYF,eAAO,MAAM,aAAa,GAAI,mFAO3B,oBAAoB,kDAsBtB,CAAC"}
@@ -1,5 +1,5 @@
1
- import { adkExtras } from "./adkExtras.js";
2
1
  import { projectAdkToolApprovals, toAdkToolConfirmationReply } from "./adkToolApproval.js";
2
+ import { adkExtras } from "./adkExtras.js";
3
3
  import { convertAdkMessage, createAdkMessageConverter } from "./convertAdkMessages.js";
4
4
  import { getMessageContent, getPendingCancellations, toAdkUserMessage, truncateAdkMessages } from "./convertToAdkMessages.js";
5
5
  import { useAdkMessages } from "./useAdkMessages.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-google-adk",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "description": "Google ADK adapter for assistant-ui",
5
5
  "keywords": [
6
6
  "google-adk",
@@ -34,10 +34,10 @@
34
34
  ],
35
35
  "sideEffects": false,
36
36
  "dependencies": {
37
- "@assistant-ui/core": "^0.3.19",
38
- "@assistant-ui/store": "^0.3.13",
37
+ "@assistant-ui/core": "^0.3.21",
38
+ "@assistant-ui/store": "^0.3.15",
39
39
  "assistant-cloud": "*",
40
- "assistant-stream": "^0.3.43"
40
+ "assistant-stream": "^0.3.45"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@google/adk": ">=0.5.0",
@@ -53,13 +53,13 @@
53
53
  }
54
54
  },
55
55
  "devDependencies": {
56
- "@assistant-ui/x-buildutils": "0.0.27",
57
- "@testing-library/dom": "^10.4.1",
56
+ "@assistant-ui/x-buildutils": "0.0.29",
57
+ "@testing-library/dom": "^10.4.2",
58
58
  "@testing-library/react": "^16.3.3",
59
- "@types/react": "^19.2.18",
59
+ "@types/react": "^19.3.0",
60
60
  "jsdom": "^30.0.1",
61
- "react": "^19.2.8",
62
- "vitest": "^5.0.0"
61
+ "react": "^19.3.0",
62
+ "vitest": "^5.0.1"
63
63
  },
64
64
  "publishConfig": {
65
65
  "access": "public",
@@ -77,6 +77,7 @@
77
77
  "scripts": {
78
78
  "build": "aui-build",
79
79
  "test": "vitest run",
80
+ "typecheck": "tsc --noEmit",
80
81
  "test:watch": "vitest"
81
82
  }
82
83
  }
@@ -124,6 +124,24 @@ describe.each(["direct", "proxy", "proxy batch"] as const)(
124
124
  // ── Proxy mode ──
125
125
 
126
126
  describe("createAdkStream - proxy mode", () => {
127
+ it("aborts while dynamic headers are pending", async () => {
128
+ const controller = new AbortController();
129
+ const stream = createAdkStream({
130
+ api: "/api/adk",
131
+ headers: () => new Promise<Record<string, string>>(() => {}),
132
+ });
133
+ const events = await stream(
134
+ [{ id: "m1", type: "human", content: "Hello" }],
135
+ makeConfig({ abortSignal: controller.signal }),
136
+ );
137
+
138
+ const next = events.next();
139
+ controller.abort();
140
+
141
+ await expect(next).rejects.toBe(controller.signal.reason);
142
+ expect(mockFetch).not.toHaveBeenCalled();
143
+ });
144
+
127
145
  it("accumulates snake_case image and file parts from SSE", async () => {
128
146
  const event = {
129
147
  id: "media",
@@ -1077,6 +1095,38 @@ describe("createAdkStream - error handling", () => {
1077
1095
  'Expected ADK stream response Content-Type "text/event-stream", received no Content-Type header',
1078
1096
  );
1079
1097
  });
1098
+
1099
+ it("rejects event-stream responses without a body", async () => {
1100
+ mockFetch.mockResolvedValueOnce(sseResponse(null));
1101
+
1102
+ const stream = createAdkStream({ api: "/api/adk" });
1103
+ await expect(async () => {
1104
+ const gen = await stream(
1105
+ [{ id: "m1", type: "human", content: "Hi" }],
1106
+ makeConfig(),
1107
+ );
1108
+ for await (const _ of gen) {
1109
+ /* noop */
1110
+ }
1111
+ }).rejects.toThrow("Expected ADK stream response body, received no body");
1112
+ });
1113
+
1114
+ it("rejects non-standard responses with an undefined body", async () => {
1115
+ const response = sseResponse(null);
1116
+ Object.defineProperty(response, "body", { value: undefined });
1117
+ mockFetch.mockResolvedValueOnce(response);
1118
+
1119
+ const stream = createAdkStream({ api: "/api/adk" });
1120
+ await expect(async () => {
1121
+ const gen = await stream(
1122
+ [{ id: "m1", type: "human", content: "Hi" }],
1123
+ makeConfig(),
1124
+ );
1125
+ for await (const _ of gen) {
1126
+ /* noop */
1127
+ }
1128
+ }).rejects.toThrow("Expected ADK stream response body, received no body");
1129
+ });
1080
1130
  });
1081
1131
 
1082
1132
  // ── Headers ──
package/src/AdkClient.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { SSEEventDecoder } from "assistant-stream/utils";
2
2
  import { contentToParts } from "./contentToParts";
3
3
  import { parseAdkEventValue } from "./parseAdkEvent";
4
+ import { raceWithAbortSignal } from "./raceWithAbortSignal";
4
5
  import { toAdkFunctionResponse } from "./toAdkFunctionResponse";
5
6
  import { trimTrailingSlashes } from "./trimTrailingSlashes";
6
7
  import type {
@@ -76,7 +77,7 @@ export function createAdkStream(
76
77
  }
77
78
 
78
79
  return async function* (messages, config) {
79
- const headers = await resolveHeaders(options.headers);
80
+ const headers = await resolveHeaders(options.headers, config.abortSignal);
80
81
 
81
82
  let url: string;
82
83
  let body: unknown;
@@ -149,9 +150,12 @@ async function resolveHeaders(
149
150
  | Record<string, string>
150
151
  | (() => Record<string, string> | Promise<Record<string, string>>)
151
152
  | undefined,
153
+ signal?: AbortSignal,
152
154
  ): Promise<Record<string, string>> {
153
155
  if (!headers) return {};
154
- if (typeof headers === "function") return await headers();
156
+ if (typeof headers === "function") {
157
+ return await raceWithAbortSignal(signal, headers);
158
+ }
155
159
  return headers;
156
160
  }
157
161
 
@@ -252,7 +256,10 @@ function messagesToProxyBody(
252
256
  }
253
257
 
254
258
  async function* parseSSEResponse(response: Response): AsyncGenerator<AdkEvent> {
255
- const reader = response.body!.getReader();
259
+ if (!response.body) {
260
+ throw new Error("Expected ADK stream response body, received no body");
261
+ }
262
+ const reader = response.body.getReader();
256
263
  const decoder = new TextDecoder();
257
264
  const sseDecoder = new SSEEventDecoder({ trailing: "dispatch" });
258
265