@assistant-ui/react-google-adk 0.0.33 → 0.0.34

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AdkEventAccumulator.d.ts","sourceRoot":"","sources":["../src/AdkEventAccumulator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,QAAQ,EAER,UAAU,EAGV,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EACnB,mBAAgB;AA4BjB;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,QAAQ,KAAG,OAgBjD,CAAC;AAuHF,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,0BAA0B,CAAS;IAC3C,OAAO,CAAC,+BAA+B,CAAS;IAChD,OAAO,CAAC,sBAAsB,CAAM;IACpC,OAAO,CAAC,qBAAqB,CAAgD;IAC7E,OAAO,CAAC,wBAAwB,CACV;IACtB,OAAO,CAAC,aAAa,CAGd;IACP,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,yBAAyB,CAAqB;IACtD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,kBAAkB,CAAyC;IAGnE,OAAO,CAAC,iBAAiB,CAA6B;gBAEpD,eAAe,CAAC,EAAE,UAAU,EAAE,EAC9B,yBAAyB,CAAC,EAAE,SAAS,MAAM,EAAE;IAY/C,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,EAAE;IAyL9C,OAAO,CAAC,WAAW;IA4GnB,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,oBAAoB;IAyB5B,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,2BAA2B;IAenC,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,eAAe;IAWvB,WAAW,IAAI,UAAU,EAAE;IAI3B,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIxC,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAI1C,YAAY,IAAI;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;IAI1E,sBAAsB,IAAI,MAAM,GAAG,SAAS;IAI5C,qBAAqB,IAAI,MAAM,EAAE;IAIjC,oBAAoB,IAAI,mBAAmB,EAAE;IAoB7C,eAAe,IAAI,cAAc,EAAE;IAanC,WAAW,IAAI,OAAO;IAItB,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC;CAGtD"}
1
+ {"version":3,"file":"AdkEventAccumulator.d.ts","sourceRoot":"","sources":["../src/AdkEventAccumulator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,QAAQ,EAER,UAAU,EAGV,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EACnB,mBAAgB;AA4BjB;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,QAAQ,KAAG,OAgBjD,CAAC;AAuHF,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,0BAA0B,CAAS;IAC3C,OAAO,CAAC,+BAA+B,CAAS;IAChD,OAAO,CAAC,sBAAsB,CAAM;IACpC,OAAO,CAAC,qBAAqB,CAAgD;IAC7E,OAAO,CAAC,wBAAwB,CACV;IACtB,OAAO,CAAC,aAAa,CAGd;IACP,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,yBAAyB,CAAqB;IACtD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,kBAAkB,CAAyC;IAGnE,OAAO,CAAC,iBAAiB,CAA6B;gBAEpD,eAAe,CAAC,EAAE,UAAU,EAAE,EAC9B,yBAAyB,CAAC,EAAE,SAAS,MAAM,EAAE;IAY/C,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,EAAE;IAyL9C,OAAO,CAAC,WAAW;IA4GnB,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,oBAAoB;IAyB5B,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,2BAA2B;IAenC,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,eAAe;IAYvB,WAAW,IAAI,UAAU,EAAE;IAI3B,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIxC,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAI1C,YAAY,IAAI;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;IAI1E,sBAAsB,IAAI,MAAM,GAAG,SAAS;IAI5C,qBAAqB,IAAI,MAAM,EAAE;IAIjC,oBAAoB,IAAI,mBAAmB,EAAE;IAoB7C,eAAe,IAAI,cAAc,EAAE;IAanC,WAAW,IAAI,OAAO;IAItB,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC;CAGtD"}
@@ -424,7 +424,10 @@ var AdkEventAccumulator = class {
424
424
  const calls = /* @__PURE__ */ new Map();
425
425
  for (const msg of this.messagesMap.values()) {
426
426
  if (msg.type !== "ai") continue;
427
- for (const call of msg.tool_calls ?? []) if (call.name === name && !calls.has(call.id)) calls.set(call.id, call);
427
+ for (const call of msg.tool_calls ?? []) {
428
+ if (!isRecord(call)) continue;
429
+ if (call.name === name && !calls.has(call.id)) calls.set(call.id, call);
430
+ }
428
431
  }
429
432
  return [...calls.values()];
430
433
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AdkEventAccumulator.js","names":[],"sources":["../src/AdkEventAccumulator.ts"],"sourcesContent":["import { generateId } from \"@assistant-ui/core\";\nimport { isRecord } from \"@assistant-ui/core/internal\";\nimport type { MessageStatus } from \"@assistant-ui/core\";\nimport type {\n AdkEvent,\n AdkEventPart,\n AdkMessage,\n AdkMessageContentPart,\n AdkToolCall,\n AdkToolConfirmation,\n AdkAuthRequest,\n AdkMessageMetadata,\n} from \"./types\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport { normalizeAdkPart } from \"./normalizeAdkPart\";\nimport { projectAdkToolApprovals } from \"./adkToolApproval\";\nimport { isAdkFunctionError } from \"./toAdkFunctionResponse\";\n\ntype InProgressMessage = AdkMessage & { type: \"ai\" };\n\n/**\n * A session load replays the stored events through a fresh accumulator, so a\n * message needs an id derived from the event that carries it rather than one\n * minted per replay. An event with no id of its own has never been through the\n * session and has nothing stable to derive from, so it keeps a generated one.\n *\n * A human message keeps the bare event id it has always had. The other kinds\n * take a suffixed namespace, since one event can carry several of them: a tool\n * message by the index of its part, an assistant message by how many this\n * event has already opened.\n */\nconst toolMessageId = (event: AdkEvent, partIndex: number): string =>\n event.id ? `${event.id}:${partIndex}` : generateId();\n\nconst aiMessageId = (event: AdkEvent, ordinal: number): string =>\n event.id ? `${event.id}:ai${ordinal === 0 ? \"\" : ordinal}` : generateId();\n\nconst ADK_REQUEST_CONFIRMATION = \"adk_request_confirmation\";\nconst ADK_REQUEST_CREDENTIAL = \"adk_request_credential\";\n\n/**\n * Checks if an event is a final response using the same logic as ADK's\n * `isFinalResponse()`.\n */\nexport const isFinalResponse = (event: AdkEvent): boolean => {\n if (\n event.actions?.skipSummarization ||\n (event.longRunningToolIds && event.longRunningToolIds.length > 0)\n ) {\n return true;\n }\n\n const parts = event.content?.parts;\n return (\n !event.partial &&\n (parts?.length ?? 0) > 0 &&\n !parts!.some((p) => p.functionCall) &&\n !parts!.some((p) => p.functionResponse) &&\n !parts![parts!.length - 1]?.codeExecutionResult\n );\n};\n\nconst CONTENT_FILTER_REASONS = new Set([\n \"SAFETY\",\n \"RECITATION\",\n \"BLOCKLIST\",\n \"PROHIBITED_CONTENT\",\n \"SPII\",\n \"LANGUAGE\",\n \"IMAGE_SAFETY\",\n \"IMAGE_RECITATION\",\n \"IMAGE_OTHER\",\n \"IMAGE_PROHIBITED_CONTENT\",\n]);\n\nconst ERROR_REASONS = new Set([\n \"MALFORMED_FUNCTION_CALL\",\n \"UNEXPECTED_TOOL_CALL\",\n]);\n\nconst finishReasonToStatus = (\n finishReason: string | undefined,\n): MessageStatus => {\n if (finishReason === \"MAX_TOKENS\") {\n return { type: \"incomplete\", reason: \"length\" };\n }\n if (finishReason && CONTENT_FILTER_REASONS.has(finishReason)) {\n return {\n type: \"incomplete\",\n reason: \"content-filter\",\n error: `Content filtered: ${finishReason}`,\n };\n }\n if (finishReason && ERROR_REASONS.has(finishReason)) {\n return {\n type: \"incomplete\",\n reason: \"error\",\n error: `LLM error: ${finishReason}`,\n };\n }\n return { type: \"complete\", reason: \"stop\" };\n};\n\nconst mediaToContentPart = ({\n inlineData,\n fileData,\n}: Record<string, unknown>): AdkMessageContentPart | undefined => {\n if (isRecord(inlineData)) {\n const { mimeType, data } = inlineData;\n if (typeof mimeType !== \"string\" || typeof data !== \"string\") return;\n return mimeType.startsWith(\"image/\")\n ? { type: \"image\", mimeType, data }\n : { type: \"file\", mimeType, data };\n }\n if (!isRecord(fileData)) return;\n const { fileUri, mimeType } = fileData;\n if (typeof fileUri !== \"string\") return;\n return typeof mimeType !== \"string\" || mimeType.startsWith(\"image/\")\n ? { type: \"image_url\", url: fileUri }\n : { type: \"file_url\", url: fileUri, mimeType };\n};\n\n// ── Snake_case normalization ──\n\nconst normalizeEvent = (event: AdkEvent): AdkEvent => {\n const e = event as Record<string, unknown>;\n const result: Record<string, unknown> = { ...e };\n if (\"error_code\" in e && !(\"errorCode\" in e)) result.errorCode = e.error_code;\n if (\"error_message\" in e && !(\"errorMessage\" in e))\n result.errorMessage = e.error_message;\n if (\"long_running_tool_ids\" in e && !(\"longRunningToolIds\" in e))\n result.longRunningToolIds = e.long_running_tool_ids;\n if (\"turn_complete\" in e && !(\"turnComplete\" in e))\n result.turnComplete = e.turn_complete;\n if (\"finish_reason\" in e && !(\"finishReason\" in e))\n result.finishReason = e.finish_reason;\n if (\"invocation_id\" in e && !(\"invocationId\" in e))\n result.invocationId = e.invocation_id;\n if (\"custom_metadata\" in e && !(\"customMetadata\" in e))\n result.customMetadata = e.custom_metadata;\n if (\"grounding_metadata\" in e && !(\"groundingMetadata\" in e))\n result.groundingMetadata = e.grounding_metadata;\n if (\"citation_metadata\" in e && !(\"citationMetadata\" in e))\n result.citationMetadata = e.citation_metadata;\n if (\"usage_metadata\" in e && !(\"usageMetadata\" in e))\n result.usageMetadata = e.usage_metadata;\n\n if (result.actions) {\n const a = result.actions as Record<string, unknown>;\n const na: Record<string, unknown> = { ...a };\n if (\"state_delta\" in a && !(\"stateDelta\" in a))\n na.stateDelta = a.state_delta;\n if (\"artifact_delta\" in a && !(\"artifactDelta\" in a))\n na.artifactDelta = a.artifact_delta;\n if (\"transfer_to_agent\" in a && !(\"transferToAgent\" in a))\n na.transferToAgent = a.transfer_to_agent;\n if (\"skip_summarization\" in a && !(\"skipSummarization\" in a))\n na.skipSummarization = a.skip_summarization;\n if (\"requested_auth_configs\" in a && !(\"requestedAuthConfigs\" in a))\n na.requestedAuthConfigs = a.requested_auth_configs;\n if (\n \"requested_tool_confirmations\" in a &&\n !(\"requestedToolConfirmations\" in a)\n )\n na.requestedToolConfirmations = a.requested_tool_confirmations;\n result.actions = na;\n }\n\n if (result.content && (result.content as Record<string, unknown>).parts) {\n const content = result.content as Record<string, unknown>;\n const parts = content.parts as Record<string, unknown>[];\n result.content = { ...content, parts: parts.map(normalizeAdkPart) };\n }\n\n return result as AdkEvent;\n};\n\n// ── Accumulator ──\n\nexport class AdkEventAccumulator {\n private messagesMap = new Map<string, AdkMessage>();\n private currentMessageId: string | null = null;\n private partialTextBuffer = \"\";\n private finalTextReplacedThisEvent = false;\n private finalReasoningReplacedThisEvent = false;\n private partialReasoningBuffer = \"\";\n private accumulatedStateDelta: Record<string, unknown> = Object.create(null);\n private accumulatedArtifactDelta: Record<string, number> =\n Object.create(null);\n private lastAgentInfo: {\n name?: string | undefined;\n branch?: string | undefined;\n } = {};\n private lastTransferToAgent: string | undefined;\n private pendingLongRunningToolIds = new Set<string>();\n private escalated = false;\n private messageMetadataMap = new Map<string, AdkMessageMetadata>();\n // How many assistant messages each event has opened, so a replay of that\n // event opens them with the same ids.\n private aiMessageOrdinals = new Map<string, number>();\n constructor(\n initialMessages?: AdkMessage[],\n initialLongRunningToolIds?: readonly string[],\n ) {\n if (initialMessages) {\n for (const msg of initialMessages) {\n this.messagesMap.set(msg.id, msg);\n }\n }\n if (initialLongRunningToolIds) {\n this.pendingLongRunningToolIds = new Set(initialLongRunningToolIds);\n }\n }\n\n processEvent(rawEvent: AdkEvent): AdkMessage[] {\n const event = normalizeEvent(rawEvent);\n\n // Accumulate state delta\n if (event.actions?.stateDelta) {\n Object.assign(this.accumulatedStateDelta, event.actions.stateDelta);\n }\n\n // Accumulate artifact delta\n if (event.actions?.artifactDelta) {\n Object.assign(this.accumulatedArtifactDelta, event.actions.artifactDelta);\n }\n\n // Track escalation\n if (event.actions?.escalate) {\n this.escalated = true;\n }\n\n // Track agent transfer\n if (event.actions?.transferToAgent) {\n this.lastTransferToAgent = event.actions.transferToAgent;\n }\n\n // Track long-running tool IDs. Accumulate across events so that multiple\n // HITL interrupts emitted in the same turn are all tracked — a\n // single-event replacement would drop earlier ids.\n if (event.longRunningToolIds?.length) {\n for (const id of event.longRunningToolIds) {\n this.pendingLongRunningToolIds.add(id);\n }\n }\n\n // Track agent info\n if (event.author && event.author !== \"user\") {\n this.lastAgentInfo = {\n name: event.author ?? undefined,\n branch: event.branch ?? undefined,\n };\n }\n\n // Handle interrupted events\n if (event.interrupted) {\n if (this.currentMessageId) {\n const msg = this.messagesMap.get(this.currentMessageId);\n if (msg && msg.type === \"ai\" && !msg.status) {\n const updated: InProgressMessage = {\n ...msg,\n content: [...this.getContentArray(msg)],\n status: { type: \"incomplete\", reason: \"cancelled\" },\n };\n this.messagesMap.set(updated.id, updated);\n }\n }\n this.finalizeCurrentMessage();\n return this.getMessages();\n }\n\n // Handle error events\n if (event.errorCode || event.errorMessage) {\n this.finalizeCurrentMessage();\n const errorMsg = this.getOrCreateAiMessage(event);\n const errorText =\n event.errorMessage ?? event.errorCode ?? \"Unknown error\";\n const updated: InProgressMessage = {\n ...errorMsg,\n content: [\n ...this.getContentArray(errorMsg),\n { type: \"text\", text: errorText },\n ],\n status: { type: \"incomplete\", reason: \"error\", error: errorText },\n };\n this.messagesMap.set(updated.id, updated);\n this.currentMessageId = null;\n return this.getMessages();\n }\n\n const parts = event.content?.parts;\n if (!parts?.length) {\n // Track metadata even for content-less events (e.g. turnComplete)\n this.trackMessageMetadata(event);\n return this.getMessages();\n }\n\n // User-authored events → create human message, not AI.\n // Without this, user events fall through to processPart →\n // getOrCreateAiMessage, producing type:\"ai\" messages that\n // convertAdkMessage maps to role:\"assistant\".\n if (event.author === \"user\") {\n this.finalizeCurrentMessage();\n const humanParts: AdkMessageContentPart[] = [];\n const toolMessages: AdkMessage[] = [];\n for (const [index, part] of parts.entries()) {\n if (part.text != null && !part.thought) {\n humanParts.push({ type: \"text\", text: part.text });\n } else if (part.inlineData || part.fileData) {\n const mediaPart = mediaToContentPart(part);\n if (mediaPart) humanParts.push(mediaPart);\n } else if (part.functionResponse?.id) {\n // ADK records tool confirmation and other client-supplied tool\n // results as user-authored function responses, and its request\n // confirmation processors search user events for them. Dropping\n // them here would replay a settled gate as pending. A response\n // carrying no id answers no call: core drops it as an orphan, and\n // keeping it would let it settle the batch it was grouped into.\n toolMessages.push({\n id: toolMessageId(event, index),\n type: \"tool\",\n tool_call_id: part.functionResponse.id,\n name: part.functionResponse.name,\n content: JSON.stringify(part.functionResponse.response),\n status: isAdkFunctionError(part.functionResponse.response)\n ? \"error\"\n : \"success\",\n });\n // Only a user-authored response settles a long-running call; the response ADK authors for one is the tool's interim result.\n this.pendingLongRunningToolIds.delete(part.functionResponse.id);\n }\n }\n // The replies answer the preceding assistant turn, so they are emitted\n // before any user content in the same event. A human message between the\n // tool call and its reply splits them into separate converted messages,\n // orphaning the reply and leaving its gate unsettled.\n for (const toolMsg of toolMessages) {\n this.messagesMap.set(toolMsg.id, toolMsg);\n }\n if (humanParts.length > 0) {\n const id = event.id ?? generateId();\n const first = humanParts[0];\n const content: string | AdkMessageContentPart[] =\n humanParts.length === 1 && first?.type === \"text\"\n ? first.text\n : humanParts;\n this.messagesMap.set(id, { id, type: \"human\", content });\n }\n return this.getMessages();\n }\n\n // If author changed, finalize previous message\n if (this.currentMessageId && event.author && event.author !== \"user\") {\n const current = this.messagesMap.get(this.currentMessageId);\n if (current && current.type === \"ai\" && current.author !== event.author) {\n this.finalizeCurrentMessage();\n }\n }\n\n // Replace-semantics close out the streamed partial buffer, which only\n // the first final text/reasoning part of an event may do; later parts\n // of the same event are distinct content and append.\n this.finalTextReplacedThisEvent = false;\n this.finalReasoningReplacedThisEvent = false;\n for (const [index, part] of parts.entries()) {\n this.processPart(part, event, index);\n }\n\n // Track per-message metadata (grounding, citation, usage)\n this.trackMessageMetadata(event);\n\n // Check isFinalResponse (can be true even for partial events via skipSummarization/longRunningToolIds)\n if (isFinalResponse(event) && this.currentMessageId) {\n const msg = this.messagesMap.get(this.currentMessageId);\n // Skip manual \"complete\" when longRunningToolIds is the sole reason for\n // isFinalResponse — let auto-status apply requires-action for pending tool calls.\n const isHitlOnly =\n event.longRunningToolIds &&\n event.longRunningToolIds.length > 0 &&\n !event.actions?.skipSummarization;\n if (msg && msg.type === \"ai\" && !msg.status && !isHitlOnly) {\n const status = finishReasonToStatus(event.finishReason);\n const updated: InProgressMessage = {\n ...msg,\n content: [...this.getContentArray(msg)],\n status,\n };\n this.messagesMap.set(updated.id, updated);\n }\n }\n\n // Non-partial event finalizes the current message\n if (!event.partial || isFinalResponse(event)) {\n this.finalizeCurrentMessage();\n }\n\n return this.getMessages();\n }\n\n private processPart(\n part: AdkEventPart,\n event: AdkEvent,\n partIndex: number,\n ): void {\n // Text with thought=true → reasoning (accumulated)\n if (part.text != null && part.thought) {\n const msg = this.getOrCreateAiMessage(event);\n if (event.partial) {\n this.partialReasoningBuffer += part.text;\n this.replaceLastReasoningContent(msg, this.partialReasoningBuffer);\n } else if (!this.finalReasoningReplacedThisEvent) {\n this.finalReasoningReplacedThisEvent = true;\n this.partialReasoningBuffer = \"\";\n this.replaceLastReasoningContent(msg, part.text);\n } else {\n this.appendContent(msg, { type: \"reasoning\", text: part.text });\n }\n return;\n }\n\n // Regular text\n if (part.text != null) {\n const msg = this.getOrCreateAiMessage(event);\n if (event.partial) {\n this.partialTextBuffer += part.text;\n this.replaceLastTextContent(msg, this.partialTextBuffer);\n } else if (!this.finalTextReplacedThisEvent) {\n this.finalTextReplacedThisEvent = true;\n this.partialTextBuffer = \"\";\n this.replaceLastTextContent(msg, part.text);\n } else {\n this.appendContent(msg, { type: \"text\", text: part.text });\n }\n return;\n }\n\n // Function call — skip partial events (args may be incomplete)\n if (part.functionCall) {\n if (event.partial) return;\n const msg = this.getOrCreateAiMessage(event);\n const toolCall: AdkToolCall = {\n id: part.functionCall.id ?? generateId(),\n name: part.functionCall.name,\n args: (part.functionCall.args ?? {}) as ReadonlyJSONObject,\n argsText: JSON.stringify(part.functionCall.args ?? {}),\n };\n const existing = [...(msg.tool_calls ?? [])];\n const idx = existing.findIndex((tc) => tc.id === toolCall.id);\n if (idx >= 0) {\n existing[idx] = toolCall;\n } else {\n existing.push(toolCall);\n }\n const updated: InProgressMessage = {\n ...msg,\n content: [...this.getContentArray(msg)],\n tool_calls: existing,\n };\n this.messagesMap.set(updated.id, updated);\n return;\n }\n\n // Function response → tool message\n if (part.functionResponse) {\n this.finalizeCurrentMessage();\n const toolMsg: AdkMessage = {\n id: toolMessageId(event, partIndex),\n type: \"tool\",\n tool_call_id: part.functionResponse.id ?? \"\",\n name: part.functionResponse.name,\n content: JSON.stringify(part.functionResponse.response),\n status: isAdkFunctionError(part.functionResponse.response)\n ? \"error\"\n : \"success\",\n };\n this.messagesMap.set(toolMsg.id, toolMsg);\n return;\n }\n\n // Executable code\n if (part.executableCode) {\n const msg = this.getOrCreateAiMessage(event);\n this.appendContent(msg, {\n type: \"code\",\n code: part.executableCode.code,\n language: part.executableCode.language ?? \"python\",\n });\n return;\n }\n\n // Code execution result\n if (part.codeExecutionResult) {\n const msg = this.getOrCreateAiMessage(event);\n this.appendContent(msg, {\n type: \"code_result\",\n output: part.codeExecutionResult.output,\n outcome: part.codeExecutionResult.outcome ?? \"OUTCOME_OK\",\n });\n return;\n }\n\n const mediaPart = mediaToContentPart(part);\n if (!mediaPart) return;\n const msg = this.getOrCreateAiMessage(event);\n this.appendContent(msg, mediaPart);\n }\n\n private trackMessageMetadata(event: AdkEvent): void {\n if (\n !this.currentMessageId ||\n (!event.groundingMetadata &&\n !event.citationMetadata &&\n !event.usageMetadata)\n )\n return;\n\n const existing = this.messageMetadataMap.get(this.currentMessageId) ?? {};\n const updated: AdkMessageMetadata = { ...existing };\n if (event.groundingMetadata)\n updated.groundingMetadata = event.groundingMetadata;\n if (event.citationMetadata)\n updated.citationMetadata = event.citationMetadata;\n if (event.usageMetadata) updated.usageMetadata = event.usageMetadata;\n this.messageMetadataMap.set(this.currentMessageId, updated);\n }\n\n private getContentArray(msg: AdkMessage): AdkMessageContentPart[] {\n return Array.isArray(msg.content)\n ? (msg.content as AdkMessageContentPart[])\n : [];\n }\n\n private getOrCreateAiMessage(event: AdkEvent): InProgressMessage {\n if (this.currentMessageId) {\n const existing = this.messagesMap.get(this.currentMessageId);\n if (existing && existing.type === \"ai\") {\n return existing;\n }\n }\n\n const ordinal = this.aiMessageOrdinals.get(event.id ?? \"\") ?? 0;\n this.aiMessageOrdinals.set(event.id ?? \"\", ordinal + 1);\n const id = aiMessageId(event, ordinal);\n const msg: InProgressMessage = {\n id,\n type: \"ai\",\n content: [] as AdkMessageContentPart[],\n ...(event.author != null && { author: event.author }),\n ...(event.branch != null && { branch: event.branch }),\n };\n this.messagesMap.set(id, msg);\n this.currentMessageId = id;\n this.partialTextBuffer = \"\";\n this.partialReasoningBuffer = \"\";\n return msg;\n }\n\n private appendContent(\n msg: InProgressMessage,\n part: AdkMessageContentPart,\n ): void {\n const content = [...this.getContentArray(msg), part];\n const updated: InProgressMessage = { ...msg, content };\n this.messagesMap.set(updated.id, updated);\n }\n\n private replaceLastTextContent(msg: InProgressMessage, text: string): void {\n const content = [...this.getContentArray(msg)];\n const lastTextIdx = content.findLastIndex((p) => p.type === \"text\");\n if (lastTextIdx >= 0) {\n content[lastTextIdx] = { type: \"text\", text };\n } else {\n content.push({ type: \"text\", text });\n }\n const updated: InProgressMessage = { ...msg, content };\n this.messagesMap.set(updated.id, updated);\n }\n\n private replaceLastReasoningContent(\n msg: InProgressMessage,\n text: string,\n ): void {\n const content = [...this.getContentArray(msg)];\n const lastIdx = content.findLastIndex((p) => p.type === \"reasoning\");\n if (lastIdx >= 0) {\n content[lastIdx] = { type: \"reasoning\", text };\n } else {\n content.push({ type: \"reasoning\", text });\n }\n const updated: InProgressMessage = { ...msg, content };\n this.messagesMap.set(updated.id, updated);\n }\n\n private finalizeCurrentMessage(): void {\n this.partialTextBuffer = \"\";\n this.partialReasoningBuffer = \"\";\n this.currentMessageId = null;\n }\n\n // ADK resumes a confirmation or credential request only on a reply that quotes its synthetic call's id, so those calls are the whole record; requestedToolConfirmations and requestedAuthConfigs key the gated call instead, which no reply answers.\n private getRequestCalls(name: string): AdkToolCall[] {\n const calls = new Map<string, AdkToolCall>();\n for (const msg of this.messagesMap.values()) {\n if (msg.type !== \"ai\") continue;\n for (const call of msg.tool_calls ?? []) {\n if (call.name === name && !calls.has(call.id)) calls.set(call.id, call);\n }\n }\n return [...calls.values()];\n }\n\n getMessages(): AdkMessage[] {\n return [...this.messagesMap.values()];\n }\n\n getStateDelta(): Record<string, unknown> {\n return { ...this.accumulatedStateDelta };\n }\n\n getArtifactDelta(): Record<string, number> {\n return { ...this.accumulatedArtifactDelta };\n }\n\n getAgentInfo(): { name?: string | undefined; branch?: string | undefined } {\n return { ...this.lastAgentInfo };\n }\n\n getLastTransferToAgent(): string | undefined {\n return this.lastTransferToAgent;\n }\n\n getLongRunningToolIds(): string[] {\n return [...this.pendingLongRunningToolIds];\n }\n\n getToolConfirmations(): AdkToolConfirmation[] {\n const { approvals } = projectAdkToolApprovals(this.getMessages());\n return this.getRequestCalls(ADK_REQUEST_CONFIRMATION)\n .filter(({ id }) => approvals.get(id)?.approved === undefined)\n .map(({ id, args = {} }) => {\n const original = (args.originalFunctionCall ??\n args.original_function_call) as Record<string, unknown> | undefined;\n const confirmation = (args.toolConfirmation ??\n args.tool_confirmation) as Record<string, unknown> | undefined;\n return {\n toolCallId: id,\n toolName: (original?.name as string) ?? \"\",\n args: (original?.args as Record<string, unknown>) ?? {},\n hint: (confirmation?.hint as string) ?? \"\",\n confirmed: false,\n payload: confirmation?.payload,\n };\n });\n }\n\n getAuthRequests(): AdkAuthRequest[] {\n const answered = new Set<string>();\n for (const msg of this.messagesMap.values()) {\n if (msg.type === \"tool\") answered.add(msg.tool_call_id);\n }\n return this.getRequestCalls(ADK_REQUEST_CREDENTIAL)\n .filter(({ id }) => !answered.has(id))\n .map(({ id, args = {} }) => ({\n toolCallId: id,\n authConfig: args.auth_config ?? args.authConfig,\n }));\n }\n\n isEscalated(): boolean {\n return this.escalated;\n }\n\n getMessageMetadata(): Map<string, AdkMessageMetadata> {\n return new Map(this.messageMetadataMap);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdkEventAccumulator.js","names":[],"sources":["../src/AdkEventAccumulator.ts"],"sourcesContent":["import { generateId } from \"@assistant-ui/core\";\nimport { isRecord } from \"@assistant-ui/core/internal\";\nimport type { MessageStatus } from \"@assistant-ui/core\";\nimport type {\n AdkEvent,\n AdkEventPart,\n AdkMessage,\n AdkMessageContentPart,\n AdkToolCall,\n AdkToolConfirmation,\n AdkAuthRequest,\n AdkMessageMetadata,\n} from \"./types\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\nimport { normalizeAdkPart } from \"./normalizeAdkPart\";\nimport { projectAdkToolApprovals } from \"./adkToolApproval\";\nimport { isAdkFunctionError } from \"./toAdkFunctionResponse\";\n\ntype InProgressMessage = AdkMessage & { type: \"ai\" };\n\n/**\n * A session load replays the stored events through a fresh accumulator, so a\n * message needs an id derived from the event that carries it rather than one\n * minted per replay. An event with no id of its own has never been through the\n * session and has nothing stable to derive from, so it keeps a generated one.\n *\n * A human message keeps the bare event id it has always had. The other kinds\n * take a suffixed namespace, since one event can carry several of them: a tool\n * message by the index of its part, an assistant message by how many this\n * event has already opened.\n */\nconst toolMessageId = (event: AdkEvent, partIndex: number): string =>\n event.id ? `${event.id}:${partIndex}` : generateId();\n\nconst aiMessageId = (event: AdkEvent, ordinal: number): string =>\n event.id ? `${event.id}:ai${ordinal === 0 ? \"\" : ordinal}` : generateId();\n\nconst ADK_REQUEST_CONFIRMATION = \"adk_request_confirmation\";\nconst ADK_REQUEST_CREDENTIAL = \"adk_request_credential\";\n\n/**\n * Checks if an event is a final response using the same logic as ADK's\n * `isFinalResponse()`.\n */\nexport const isFinalResponse = (event: AdkEvent): boolean => {\n if (\n event.actions?.skipSummarization ||\n (event.longRunningToolIds && event.longRunningToolIds.length > 0)\n ) {\n return true;\n }\n\n const parts = event.content?.parts;\n return (\n !event.partial &&\n (parts?.length ?? 0) > 0 &&\n !parts!.some((p) => p.functionCall) &&\n !parts!.some((p) => p.functionResponse) &&\n !parts![parts!.length - 1]?.codeExecutionResult\n );\n};\n\nconst CONTENT_FILTER_REASONS = new Set([\n \"SAFETY\",\n \"RECITATION\",\n \"BLOCKLIST\",\n \"PROHIBITED_CONTENT\",\n \"SPII\",\n \"LANGUAGE\",\n \"IMAGE_SAFETY\",\n \"IMAGE_RECITATION\",\n \"IMAGE_OTHER\",\n \"IMAGE_PROHIBITED_CONTENT\",\n]);\n\nconst ERROR_REASONS = new Set([\n \"MALFORMED_FUNCTION_CALL\",\n \"UNEXPECTED_TOOL_CALL\",\n]);\n\nconst finishReasonToStatus = (\n finishReason: string | undefined,\n): MessageStatus => {\n if (finishReason === \"MAX_TOKENS\") {\n return { type: \"incomplete\", reason: \"length\" };\n }\n if (finishReason && CONTENT_FILTER_REASONS.has(finishReason)) {\n return {\n type: \"incomplete\",\n reason: \"content-filter\",\n error: `Content filtered: ${finishReason}`,\n };\n }\n if (finishReason && ERROR_REASONS.has(finishReason)) {\n return {\n type: \"incomplete\",\n reason: \"error\",\n error: `LLM error: ${finishReason}`,\n };\n }\n return { type: \"complete\", reason: \"stop\" };\n};\n\nconst mediaToContentPart = ({\n inlineData,\n fileData,\n}: Record<string, unknown>): AdkMessageContentPart | undefined => {\n if (isRecord(inlineData)) {\n const { mimeType, data } = inlineData;\n if (typeof mimeType !== \"string\" || typeof data !== \"string\") return;\n return mimeType.startsWith(\"image/\")\n ? { type: \"image\", mimeType, data }\n : { type: \"file\", mimeType, data };\n }\n if (!isRecord(fileData)) return;\n const { fileUri, mimeType } = fileData;\n if (typeof fileUri !== \"string\") return;\n return typeof mimeType !== \"string\" || mimeType.startsWith(\"image/\")\n ? { type: \"image_url\", url: fileUri }\n : { type: \"file_url\", url: fileUri, mimeType };\n};\n\n// ── Snake_case normalization ──\n\nconst normalizeEvent = (event: AdkEvent): AdkEvent => {\n const e = event as Record<string, unknown>;\n const result: Record<string, unknown> = { ...e };\n if (\"error_code\" in e && !(\"errorCode\" in e)) result.errorCode = e.error_code;\n if (\"error_message\" in e && !(\"errorMessage\" in e))\n result.errorMessage = e.error_message;\n if (\"long_running_tool_ids\" in e && !(\"longRunningToolIds\" in e))\n result.longRunningToolIds = e.long_running_tool_ids;\n if (\"turn_complete\" in e && !(\"turnComplete\" in e))\n result.turnComplete = e.turn_complete;\n if (\"finish_reason\" in e && !(\"finishReason\" in e))\n result.finishReason = e.finish_reason;\n if (\"invocation_id\" in e && !(\"invocationId\" in e))\n result.invocationId = e.invocation_id;\n if (\"custom_metadata\" in e && !(\"customMetadata\" in e))\n result.customMetadata = e.custom_metadata;\n if (\"grounding_metadata\" in e && !(\"groundingMetadata\" in e))\n result.groundingMetadata = e.grounding_metadata;\n if (\"citation_metadata\" in e && !(\"citationMetadata\" in e))\n result.citationMetadata = e.citation_metadata;\n if (\"usage_metadata\" in e && !(\"usageMetadata\" in e))\n result.usageMetadata = e.usage_metadata;\n\n if (result.actions) {\n const a = result.actions as Record<string, unknown>;\n const na: Record<string, unknown> = { ...a };\n if (\"state_delta\" in a && !(\"stateDelta\" in a))\n na.stateDelta = a.state_delta;\n if (\"artifact_delta\" in a && !(\"artifactDelta\" in a))\n na.artifactDelta = a.artifact_delta;\n if (\"transfer_to_agent\" in a && !(\"transferToAgent\" in a))\n na.transferToAgent = a.transfer_to_agent;\n if (\"skip_summarization\" in a && !(\"skipSummarization\" in a))\n na.skipSummarization = a.skip_summarization;\n if (\"requested_auth_configs\" in a && !(\"requestedAuthConfigs\" in a))\n na.requestedAuthConfigs = a.requested_auth_configs;\n if (\n \"requested_tool_confirmations\" in a &&\n !(\"requestedToolConfirmations\" in a)\n )\n na.requestedToolConfirmations = a.requested_tool_confirmations;\n result.actions = na;\n }\n\n if (result.content && (result.content as Record<string, unknown>).parts) {\n const content = result.content as Record<string, unknown>;\n const parts = content.parts as Record<string, unknown>[];\n result.content = { ...content, parts: parts.map(normalizeAdkPart) };\n }\n\n return result as AdkEvent;\n};\n\n// ── Accumulator ──\n\nexport class AdkEventAccumulator {\n private messagesMap = new Map<string, AdkMessage>();\n private currentMessageId: string | null = null;\n private partialTextBuffer = \"\";\n private finalTextReplacedThisEvent = false;\n private finalReasoningReplacedThisEvent = false;\n private partialReasoningBuffer = \"\";\n private accumulatedStateDelta: Record<string, unknown> = Object.create(null);\n private accumulatedArtifactDelta: Record<string, number> =\n Object.create(null);\n private lastAgentInfo: {\n name?: string | undefined;\n branch?: string | undefined;\n } = {};\n private lastTransferToAgent: string | undefined;\n private pendingLongRunningToolIds = new Set<string>();\n private escalated = false;\n private messageMetadataMap = new Map<string, AdkMessageMetadata>();\n // How many assistant messages each event has opened, so a replay of that\n // event opens them with the same ids.\n private aiMessageOrdinals = new Map<string, number>();\n constructor(\n initialMessages?: AdkMessage[],\n initialLongRunningToolIds?: readonly string[],\n ) {\n if (initialMessages) {\n for (const msg of initialMessages) {\n this.messagesMap.set(msg.id, msg);\n }\n }\n if (initialLongRunningToolIds) {\n this.pendingLongRunningToolIds = new Set(initialLongRunningToolIds);\n }\n }\n\n processEvent(rawEvent: AdkEvent): AdkMessage[] {\n const event = normalizeEvent(rawEvent);\n\n // Accumulate state delta\n if (event.actions?.stateDelta) {\n Object.assign(this.accumulatedStateDelta, event.actions.stateDelta);\n }\n\n // Accumulate artifact delta\n if (event.actions?.artifactDelta) {\n Object.assign(this.accumulatedArtifactDelta, event.actions.artifactDelta);\n }\n\n // Track escalation\n if (event.actions?.escalate) {\n this.escalated = true;\n }\n\n // Track agent transfer\n if (event.actions?.transferToAgent) {\n this.lastTransferToAgent = event.actions.transferToAgent;\n }\n\n // Track long-running tool IDs. Accumulate across events so that multiple\n // HITL interrupts emitted in the same turn are all tracked — a\n // single-event replacement would drop earlier ids.\n if (event.longRunningToolIds?.length) {\n for (const id of event.longRunningToolIds) {\n this.pendingLongRunningToolIds.add(id);\n }\n }\n\n // Track agent info\n if (event.author && event.author !== \"user\") {\n this.lastAgentInfo = {\n name: event.author ?? undefined,\n branch: event.branch ?? undefined,\n };\n }\n\n // Handle interrupted events\n if (event.interrupted) {\n if (this.currentMessageId) {\n const msg = this.messagesMap.get(this.currentMessageId);\n if (msg && msg.type === \"ai\" && !msg.status) {\n const updated: InProgressMessage = {\n ...msg,\n content: [...this.getContentArray(msg)],\n status: { type: \"incomplete\", reason: \"cancelled\" },\n };\n this.messagesMap.set(updated.id, updated);\n }\n }\n this.finalizeCurrentMessage();\n return this.getMessages();\n }\n\n // Handle error events\n if (event.errorCode || event.errorMessage) {\n this.finalizeCurrentMessage();\n const errorMsg = this.getOrCreateAiMessage(event);\n const errorText =\n event.errorMessage ?? event.errorCode ?? \"Unknown error\";\n const updated: InProgressMessage = {\n ...errorMsg,\n content: [\n ...this.getContentArray(errorMsg),\n { type: \"text\", text: errorText },\n ],\n status: { type: \"incomplete\", reason: \"error\", error: errorText },\n };\n this.messagesMap.set(updated.id, updated);\n this.currentMessageId = null;\n return this.getMessages();\n }\n\n const parts = event.content?.parts;\n if (!parts?.length) {\n // Track metadata even for content-less events (e.g. turnComplete)\n this.trackMessageMetadata(event);\n return this.getMessages();\n }\n\n // User-authored events → create human message, not AI.\n // Without this, user events fall through to processPart →\n // getOrCreateAiMessage, producing type:\"ai\" messages that\n // convertAdkMessage maps to role:\"assistant\".\n if (event.author === \"user\") {\n this.finalizeCurrentMessage();\n const humanParts: AdkMessageContentPart[] = [];\n const toolMessages: AdkMessage[] = [];\n for (const [index, part] of parts.entries()) {\n if (part.text != null && !part.thought) {\n humanParts.push({ type: \"text\", text: part.text });\n } else if (part.inlineData || part.fileData) {\n const mediaPart = mediaToContentPart(part);\n if (mediaPart) humanParts.push(mediaPart);\n } else if (part.functionResponse?.id) {\n // ADK records tool confirmation and other client-supplied tool\n // results as user-authored function responses, and its request\n // confirmation processors search user events for them. Dropping\n // them here would replay a settled gate as pending. A response\n // carrying no id answers no call: core drops it as an orphan, and\n // keeping it would let it settle the batch it was grouped into.\n toolMessages.push({\n id: toolMessageId(event, index),\n type: \"tool\",\n tool_call_id: part.functionResponse.id,\n name: part.functionResponse.name,\n content: JSON.stringify(part.functionResponse.response),\n status: isAdkFunctionError(part.functionResponse.response)\n ? \"error\"\n : \"success\",\n });\n // Only a user-authored response settles a long-running call; the response ADK authors for one is the tool's interim result.\n this.pendingLongRunningToolIds.delete(part.functionResponse.id);\n }\n }\n // The replies answer the preceding assistant turn, so they are emitted\n // before any user content in the same event. A human message between the\n // tool call and its reply splits them into separate converted messages,\n // orphaning the reply and leaving its gate unsettled.\n for (const toolMsg of toolMessages) {\n this.messagesMap.set(toolMsg.id, toolMsg);\n }\n if (humanParts.length > 0) {\n const id = event.id ?? generateId();\n const first = humanParts[0];\n const content: string | AdkMessageContentPart[] =\n humanParts.length === 1 && first?.type === \"text\"\n ? first.text\n : humanParts;\n this.messagesMap.set(id, { id, type: \"human\", content });\n }\n return this.getMessages();\n }\n\n // If author changed, finalize previous message\n if (this.currentMessageId && event.author && event.author !== \"user\") {\n const current = this.messagesMap.get(this.currentMessageId);\n if (current && current.type === \"ai\" && current.author !== event.author) {\n this.finalizeCurrentMessage();\n }\n }\n\n // Replace-semantics close out the streamed partial buffer, which only\n // the first final text/reasoning part of an event may do; later parts\n // of the same event are distinct content and append.\n this.finalTextReplacedThisEvent = false;\n this.finalReasoningReplacedThisEvent = false;\n for (const [index, part] of parts.entries()) {\n this.processPart(part, event, index);\n }\n\n // Track per-message metadata (grounding, citation, usage)\n this.trackMessageMetadata(event);\n\n // Check isFinalResponse (can be true even for partial events via skipSummarization/longRunningToolIds)\n if (isFinalResponse(event) && this.currentMessageId) {\n const msg = this.messagesMap.get(this.currentMessageId);\n // Skip manual \"complete\" when longRunningToolIds is the sole reason for\n // isFinalResponse — let auto-status apply requires-action for pending tool calls.\n const isHitlOnly =\n event.longRunningToolIds &&\n event.longRunningToolIds.length > 0 &&\n !event.actions?.skipSummarization;\n if (msg && msg.type === \"ai\" && !msg.status && !isHitlOnly) {\n const status = finishReasonToStatus(event.finishReason);\n const updated: InProgressMessage = {\n ...msg,\n content: [...this.getContentArray(msg)],\n status,\n };\n this.messagesMap.set(updated.id, updated);\n }\n }\n\n // Non-partial event finalizes the current message\n if (!event.partial || isFinalResponse(event)) {\n this.finalizeCurrentMessage();\n }\n\n return this.getMessages();\n }\n\n private processPart(\n part: AdkEventPart,\n event: AdkEvent,\n partIndex: number,\n ): void {\n // Text with thought=true → reasoning (accumulated)\n if (part.text != null && part.thought) {\n const msg = this.getOrCreateAiMessage(event);\n if (event.partial) {\n this.partialReasoningBuffer += part.text;\n this.replaceLastReasoningContent(msg, this.partialReasoningBuffer);\n } else if (!this.finalReasoningReplacedThisEvent) {\n this.finalReasoningReplacedThisEvent = true;\n this.partialReasoningBuffer = \"\";\n this.replaceLastReasoningContent(msg, part.text);\n } else {\n this.appendContent(msg, { type: \"reasoning\", text: part.text });\n }\n return;\n }\n\n // Regular text\n if (part.text != null) {\n const msg = this.getOrCreateAiMessage(event);\n if (event.partial) {\n this.partialTextBuffer += part.text;\n this.replaceLastTextContent(msg, this.partialTextBuffer);\n } else if (!this.finalTextReplacedThisEvent) {\n this.finalTextReplacedThisEvent = true;\n this.partialTextBuffer = \"\";\n this.replaceLastTextContent(msg, part.text);\n } else {\n this.appendContent(msg, { type: \"text\", text: part.text });\n }\n return;\n }\n\n // Function call — skip partial events (args may be incomplete)\n if (part.functionCall) {\n if (event.partial) return;\n const msg = this.getOrCreateAiMessage(event);\n const toolCall: AdkToolCall = {\n id: part.functionCall.id ?? generateId(),\n name: part.functionCall.name,\n args: (part.functionCall.args ?? {}) as ReadonlyJSONObject,\n argsText: JSON.stringify(part.functionCall.args ?? {}),\n };\n const existing = [...(msg.tool_calls ?? [])];\n const idx = existing.findIndex((tc) => tc.id === toolCall.id);\n if (idx >= 0) {\n existing[idx] = toolCall;\n } else {\n existing.push(toolCall);\n }\n const updated: InProgressMessage = {\n ...msg,\n content: [...this.getContentArray(msg)],\n tool_calls: existing,\n };\n this.messagesMap.set(updated.id, updated);\n return;\n }\n\n // Function response → tool message\n if (part.functionResponse) {\n this.finalizeCurrentMessage();\n const toolMsg: AdkMessage = {\n id: toolMessageId(event, partIndex),\n type: \"tool\",\n tool_call_id: part.functionResponse.id ?? \"\",\n name: part.functionResponse.name,\n content: JSON.stringify(part.functionResponse.response),\n status: isAdkFunctionError(part.functionResponse.response)\n ? \"error\"\n : \"success\",\n };\n this.messagesMap.set(toolMsg.id, toolMsg);\n return;\n }\n\n // Executable code\n if (part.executableCode) {\n const msg = this.getOrCreateAiMessage(event);\n this.appendContent(msg, {\n type: \"code\",\n code: part.executableCode.code,\n language: part.executableCode.language ?? \"python\",\n });\n return;\n }\n\n // Code execution result\n if (part.codeExecutionResult) {\n const msg = this.getOrCreateAiMessage(event);\n this.appendContent(msg, {\n type: \"code_result\",\n output: part.codeExecutionResult.output,\n outcome: part.codeExecutionResult.outcome ?? \"OUTCOME_OK\",\n });\n return;\n }\n\n const mediaPart = mediaToContentPart(part);\n if (!mediaPart) return;\n const msg = this.getOrCreateAiMessage(event);\n this.appendContent(msg, mediaPart);\n }\n\n private trackMessageMetadata(event: AdkEvent): void {\n if (\n !this.currentMessageId ||\n (!event.groundingMetadata &&\n !event.citationMetadata &&\n !event.usageMetadata)\n )\n return;\n\n const existing = this.messageMetadataMap.get(this.currentMessageId) ?? {};\n const updated: AdkMessageMetadata = { ...existing };\n if (event.groundingMetadata)\n updated.groundingMetadata = event.groundingMetadata;\n if (event.citationMetadata)\n updated.citationMetadata = event.citationMetadata;\n if (event.usageMetadata) updated.usageMetadata = event.usageMetadata;\n this.messageMetadataMap.set(this.currentMessageId, updated);\n }\n\n private getContentArray(msg: AdkMessage): AdkMessageContentPart[] {\n return Array.isArray(msg.content)\n ? (msg.content as AdkMessageContentPart[])\n : [];\n }\n\n private getOrCreateAiMessage(event: AdkEvent): InProgressMessage {\n if (this.currentMessageId) {\n const existing = this.messagesMap.get(this.currentMessageId);\n if (existing && existing.type === \"ai\") {\n return existing;\n }\n }\n\n const ordinal = this.aiMessageOrdinals.get(event.id ?? \"\") ?? 0;\n this.aiMessageOrdinals.set(event.id ?? \"\", ordinal + 1);\n const id = aiMessageId(event, ordinal);\n const msg: InProgressMessage = {\n id,\n type: \"ai\",\n content: [] as AdkMessageContentPart[],\n ...(event.author != null && { author: event.author }),\n ...(event.branch != null && { branch: event.branch }),\n };\n this.messagesMap.set(id, msg);\n this.currentMessageId = id;\n this.partialTextBuffer = \"\";\n this.partialReasoningBuffer = \"\";\n return msg;\n }\n\n private appendContent(\n msg: InProgressMessage,\n part: AdkMessageContentPart,\n ): void {\n const content = [...this.getContentArray(msg), part];\n const updated: InProgressMessage = { ...msg, content };\n this.messagesMap.set(updated.id, updated);\n }\n\n private replaceLastTextContent(msg: InProgressMessage, text: string): void {\n const content = [...this.getContentArray(msg)];\n const lastTextIdx = content.findLastIndex((p) => p.type === \"text\");\n if (lastTextIdx >= 0) {\n content[lastTextIdx] = { type: \"text\", text };\n } else {\n content.push({ type: \"text\", text });\n }\n const updated: InProgressMessage = { ...msg, content };\n this.messagesMap.set(updated.id, updated);\n }\n\n private replaceLastReasoningContent(\n msg: InProgressMessage,\n text: string,\n ): void {\n const content = [...this.getContentArray(msg)];\n const lastIdx = content.findLastIndex((p) => p.type === \"reasoning\");\n if (lastIdx >= 0) {\n content[lastIdx] = { type: \"reasoning\", text };\n } else {\n content.push({ type: \"reasoning\", text });\n }\n const updated: InProgressMessage = { ...msg, content };\n this.messagesMap.set(updated.id, updated);\n }\n\n private finalizeCurrentMessage(): void {\n this.partialTextBuffer = \"\";\n this.partialReasoningBuffer = \"\";\n this.currentMessageId = null;\n }\n\n // ADK resumes a confirmation or credential request only on a reply that quotes its synthetic call's id, so those calls are the whole record; requestedToolConfirmations and requestedAuthConfigs key the gated call instead, which no reply answers.\n private getRequestCalls(name: string): AdkToolCall[] {\n const calls = new Map<string, AdkToolCall>();\n for (const msg of this.messagesMap.values()) {\n if (msg.type !== \"ai\") continue;\n for (const call of msg.tool_calls ?? []) {\n if (!isRecord(call)) continue;\n if (call.name === name && !calls.has(call.id)) calls.set(call.id, call);\n }\n }\n return [...calls.values()];\n }\n\n getMessages(): AdkMessage[] {\n return [...this.messagesMap.values()];\n }\n\n getStateDelta(): Record<string, unknown> {\n return { ...this.accumulatedStateDelta };\n }\n\n getArtifactDelta(): Record<string, number> {\n return { ...this.accumulatedArtifactDelta };\n }\n\n getAgentInfo(): { name?: string | undefined; branch?: string | undefined } {\n return { ...this.lastAgentInfo };\n }\n\n getLastTransferToAgent(): string | undefined {\n return this.lastTransferToAgent;\n }\n\n getLongRunningToolIds(): string[] {\n return [...this.pendingLongRunningToolIds];\n }\n\n getToolConfirmations(): AdkToolConfirmation[] {\n const { approvals } = projectAdkToolApprovals(this.getMessages());\n return this.getRequestCalls(ADK_REQUEST_CONFIRMATION)\n .filter(({ id }) => approvals.get(id)?.approved === undefined)\n .map(({ id, args = {} }) => {\n const original = (args.originalFunctionCall ??\n args.original_function_call) as Record<string, unknown> | undefined;\n const confirmation = (args.toolConfirmation ??\n args.tool_confirmation) as Record<string, unknown> | undefined;\n return {\n toolCallId: id,\n toolName: (original?.name as string) ?? \"\",\n args: (original?.args as Record<string, unknown>) ?? {},\n hint: (confirmation?.hint as string) ?? \"\",\n confirmed: false,\n payload: confirmation?.payload,\n };\n });\n }\n\n getAuthRequests(): AdkAuthRequest[] {\n const answered = new Set<string>();\n for (const msg of this.messagesMap.values()) {\n if (msg.type === \"tool\") answered.add(msg.tool_call_id);\n }\n return this.getRequestCalls(ADK_REQUEST_CREDENTIAL)\n .filter(({ id }) => !answered.has(id))\n .map(({ id, args = {} }) => ({\n toolCallId: id,\n authConfig: args.auth_config ?? args.authConfig,\n }));\n }\n\n isEscalated(): boolean {\n return this.escalated;\n }\n\n getMessageMetadata(): Map<string, AdkMessageMetadata> {\n return new Map(this.messageMetadataMap);\n }\n}\n"],"mappings":"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"}
@@ -121,7 +121,7 @@ const projectAdkToolApprovals = (messages) => {
121
121
  for (const message of messages) {
122
122
  if (message.type !== "ai") continue;
123
123
  for (const call of message.tool_calls ?? []) {
124
- if (call.name !== "adk_request_confirmation") continue;
124
+ if (call?.name !== "adk_request_confirmation") continue;
125
125
  const approved = replies.get(call.id);
126
126
  const approval = {
127
127
  id: call.id,
@@ -1 +1 @@
1
- {"version":3,"file":"adkToolApproval.js","names":[],"sources":["../src/adkToolApproval.ts"],"sourcesContent":["import { generateId } from \"@assistant-ui/core\";\nimport type {\n RespondToToolApprovalOptions,\n ToolCallMessagePart,\n} from \"@assistant-ui/core\";\nimport type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport type { AdkMessage } from \"./types\";\n\nexport type AdkToolApproval = NonNullable<ToolCallMessagePart[\"approval\"]>;\n\nexport const ADK_REQUEST_CONFIRMATION = \"adk_request_confirmation\";\n\nconst parseJson = (raw: string): unknown => {\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n};\n\ntype AdkConfirmationRead = {\n /**\n * False only where ADK raises reading the reply, which aborts every function\n * response in the same event rather than denying this one.\n */\n readable: boolean;\n /** Undefined where ADK records no confirmation and the gate stays open. */\n confirmed: boolean | undefined;\n};\n\n/**\n * The value the transport puts on `functionResponse.response`: the reply\n * content parsed, or the raw text where it is not JSON.\n */\nconst wireResponseOf = (content: string): unknown => {\n const parsed = parseJson(content);\n return parsed === undefined ? content : parsed;\n};\n\n/**\n * Mirrors ADK's own reading of a confirmation reply, whose three outcomes the\n * gate has to distinguish. A falsy response records nothing, so the gate stays\n * open and its siblings are untouched. Otherwise ADK either unwraps its client\n * wrapper — a lone `response` key whose value it parses as JSON text — or reads\n * `confirmed` off the response directly, and resumes the tool unconfirmed\n * (a denial) for anything that is not explicitly `true`; a bare string, number,\n * or array is such a denial, not an unreadable reply. Only where that reading\n * raises is the reply unreadable, and the raise is uncaught in ADK, so the\n * whole event it belongs to is abandoned.\n */\nconst readConfirmation = (content: unknown): AdkConfirmationRead => {\n if (typeof content !== \"string\")\n return { readable: false, confirmed: undefined };\n const response = wireResponseOf(content);\n try {\n if (!response) return { readable: true, confirmed: undefined };\n const record = response as Record<string, unknown>;\n const decide = (value: unknown) =>\n typeof value === \"object\" &&\n value !== null &&\n (value as { confirmed?: unknown }).confirmed === true;\n // `in` raises on a primitive response, as ADK's own wrapper test does.\n if (Object.keys(record).length === 1 && \"response\" in record) {\n const inner = parseJson(String(record.response));\n // ADK parses the wrapped text without a `try`, so it raises here.\n if (inner === undefined) return { readable: false, confirmed: undefined };\n if (!inner) return { readable: true, confirmed: undefined };\n return { readable: true, confirmed: decide(inner) };\n }\n return { readable: true, confirmed: decide(record) };\n } catch {\n return { readable: false, confirmed: undefined };\n }\n};\n\n/**\n * ADK's confirmation processor parses every function response in an event\n * before running any tool, so one unreadable reply aborts the event and none of\n * its siblings execute. The accumulator mints a tool message id of\n * `<eventId>:<partIndex>` per response, so that prefix is the batch ADK either\n * runs whole or not at all; a locally minted reply carries a bare uuid and is\n * its own batch.\n */\nconst sourceEventOf = (toolMessageId: string): string =>\n toolMessageId.replace(/:\\d+$/, \"\");\n\n/**\n * ADK builds the confirmation request around the call it gates, carrying that call verbatim in `originalFunctionCall`; `original_function_call` is accepted as well, matching the accumulator.\n */\nconst gatedCallIdOf = (args: unknown): string | undefined => {\n if (typeof args !== \"object\" || args === null) return undefined;\n const record = args as {\n originalFunctionCall?: unknown;\n original_function_call?: unknown;\n };\n const original = record.originalFunctionCall ?? record.original_function_call;\n if (typeof original !== \"object\" || original === null) return undefined;\n const id = (original as { id?: unknown }).id;\n return typeof id === \"string\" && id.length > 0 ? id : undefined;\n};\n\nexport type AdkToolApprovalProjection = {\n approvals: ReadonlyMap<string, AdkToolApproval>;\n /**\n * Identity of the decision state. Core re-converts a cached message object\n * only when the converter callback changes, so the callback is rebuilt when\n * this key moves rather than on every render.\n */\n key: string;\n};\n\n/**\n * ADK requests a confirmation by emitting a synthetic `adk_request_confirmation`\n * tool call carrying a fresh id, and resumes only when a reply quotes that id.\n * The gated tool's own id is never answerable, so every approval carries the\n * synthetic id — but ADK yields the gated call to the client first, in its own\n * event, so both calls sit result-less in the transcript and both would render\n * an approval control. The gated call is therefore mapped to the same approval\n * rather than left bare, which would let it answer by writing a tool result the\n * agent never produced.\n */\nexport const projectAdkToolApprovals = (\n messages: readonly AdkMessage[],\n): AdkToolApprovalProjection => {\n const batches = new Map<\n string,\n { readable: boolean; entries: [string, boolean | undefined][] }\n >();\n for (const message of messages) {\n if (message.type !== \"tool\") continue;\n if (message.name !== ADK_REQUEST_CONFIRMATION) continue;\n const key = sourceEventOf(message.id);\n let batch = batches.get(key);\n if (batch === undefined) {\n batch = { readable: true, entries: [] };\n batches.set(key, batch);\n }\n const { readable, confirmed } = readConfirmation(message.content);\n if (!readable) batch.readable = false;\n batch.entries.push([message.tool_call_id, confirmed]);\n }\n\n const replies = new Map<string, boolean>();\n for (const batch of batches.values()) {\n for (const [toolCallId, confirmed] of batch.entries) {\n if (batch.readable && confirmed !== undefined)\n replies.set(toolCallId, confirmed);\n else replies.delete(toolCallId);\n }\n }\n\n const approvals = new Map<string, AdkToolApproval>();\n for (const message of messages) {\n if (message.type !== \"ai\") continue;\n for (const call of message.tool_calls ?? []) {\n if (call.name !== ADK_REQUEST_CONFIRMATION) continue;\n const approved = replies.get(call.id);\n const approval: AdkToolApproval = {\n id: call.id,\n ...(approved !== undefined && { approved }),\n };\n approvals.set(call.id, approval);\n // ADK yields the gated call in its own event before the confirmation is\n // requested, so that call is also in the transcript, result-less, and\n // inherits the message's `requires-action` status. Left ungated it\n // renders a second approval control with no approval behind it, which\n // answers by writing a tool result the agent never produced. The same\n // approval carries the synthetic id, so answering either one replies to\n // the gate ADK is actually waiting on.\n const gatedId = gatedCallIdOf(call.args);\n if (gatedId !== undefined) approvals.set(gatedId, approval);\n }\n }\n\n return {\n approvals,\n key: approvals.size === 0 ? \"\" : JSON.stringify([...approvals]),\n };\n};\n\nexport const toAdkConfirmationReply = (\n toolCallId: string,\n confirmed: boolean,\n payload?: ReadonlyJSONValue,\n): AdkMessage & { type: \"tool\" } => ({\n id: generateId(),\n type: \"tool\",\n tool_call_id: toolCallId,\n name: ADK_REQUEST_CONFIRMATION,\n content: JSON.stringify({\n confirmed,\n ...(payload != null && { payload }),\n }),\n status: \"success\",\n});\n\n/**\n * ADK confirmations are a plain confirm/reject pair, so no option or reason\n * from core has a field to land in. An id that is not an open gate would send a\n * reply ADK silently ignores, so it throws instead.\n */\nexport const toAdkToolConfirmationReply = (\n { approvalId, approved }: RespondToToolApprovalOptions,\n approvals: ReadonlyMap<string, AdkToolApproval>,\n): AdkMessage & { type: \"tool\" } => {\n const approval = approvals.get(approvalId);\n if (approval === undefined || approval.approved !== undefined)\n throw new Error(\n `No pending ADK tool confirmation for approval id \"${approvalId}\"`,\n );\n\n return toAdkConfirmationReply(approvalId, approved);\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"adkToolApproval.js","names":[],"sources":["../src/adkToolApproval.ts"],"sourcesContent":["import { generateId } from \"@assistant-ui/core\";\nimport type {\n RespondToToolApprovalOptions,\n ToolCallMessagePart,\n} from \"@assistant-ui/core\";\nimport type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport type { AdkMessage } from \"./types\";\n\nexport type AdkToolApproval = NonNullable<ToolCallMessagePart[\"approval\"]>;\n\nexport const ADK_REQUEST_CONFIRMATION = \"adk_request_confirmation\";\n\nconst parseJson = (raw: string): unknown => {\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n};\n\ntype AdkConfirmationRead = {\n /**\n * False only where ADK raises reading the reply, which aborts every function\n * response in the same event rather than denying this one.\n */\n readable: boolean;\n /** Undefined where ADK records no confirmation and the gate stays open. */\n confirmed: boolean | undefined;\n};\n\n/**\n * The value the transport puts on `functionResponse.response`: the reply\n * content parsed, or the raw text where it is not JSON.\n */\nconst wireResponseOf = (content: string): unknown => {\n const parsed = parseJson(content);\n return parsed === undefined ? content : parsed;\n};\n\n/**\n * Mirrors ADK's own reading of a confirmation reply, whose three outcomes the\n * gate has to distinguish. A falsy response records nothing, so the gate stays\n * open and its siblings are untouched. Otherwise ADK either unwraps its client\n * wrapper — a lone `response` key whose value it parses as JSON text — or reads\n * `confirmed` off the response directly, and resumes the tool unconfirmed\n * (a denial) for anything that is not explicitly `true`; a bare string, number,\n * or array is such a denial, not an unreadable reply. Only where that reading\n * raises is the reply unreadable, and the raise is uncaught in ADK, so the\n * whole event it belongs to is abandoned.\n */\nconst readConfirmation = (content: unknown): AdkConfirmationRead => {\n if (typeof content !== \"string\")\n return { readable: false, confirmed: undefined };\n const response = wireResponseOf(content);\n try {\n if (!response) return { readable: true, confirmed: undefined };\n const record = response as Record<string, unknown>;\n const decide = (value: unknown) =>\n typeof value === \"object\" &&\n value !== null &&\n (value as { confirmed?: unknown }).confirmed === true;\n // `in` raises on a primitive response, as ADK's own wrapper test does.\n if (Object.keys(record).length === 1 && \"response\" in record) {\n const inner = parseJson(String(record.response));\n // ADK parses the wrapped text without a `try`, so it raises here.\n if (inner === undefined) return { readable: false, confirmed: undefined };\n if (!inner) return { readable: true, confirmed: undefined };\n return { readable: true, confirmed: decide(inner) };\n }\n return { readable: true, confirmed: decide(record) };\n } catch {\n return { readable: false, confirmed: undefined };\n }\n};\n\n/**\n * ADK's confirmation processor parses every function response in an event\n * before running any tool, so one unreadable reply aborts the event and none of\n * its siblings execute. The accumulator mints a tool message id of\n * `<eventId>:<partIndex>` per response, so that prefix is the batch ADK either\n * runs whole or not at all; a locally minted reply carries a bare uuid and is\n * its own batch.\n */\nconst sourceEventOf = (toolMessageId: string): string =>\n toolMessageId.replace(/:\\d+$/, \"\");\n\n/**\n * ADK builds the confirmation request around the call it gates, carrying that call verbatim in `originalFunctionCall`; `original_function_call` is accepted as well, matching the accumulator.\n */\nconst gatedCallIdOf = (args: unknown): string | undefined => {\n if (typeof args !== \"object\" || args === null) return undefined;\n const record = args as {\n originalFunctionCall?: unknown;\n original_function_call?: unknown;\n };\n const original = record.originalFunctionCall ?? record.original_function_call;\n if (typeof original !== \"object\" || original === null) return undefined;\n const id = (original as { id?: unknown }).id;\n return typeof id === \"string\" && id.length > 0 ? id : undefined;\n};\n\nexport type AdkToolApprovalProjection = {\n approvals: ReadonlyMap<string, AdkToolApproval>;\n /**\n * Identity of the decision state. Core re-converts a cached message object\n * only when the converter callback changes, so the callback is rebuilt when\n * this key moves rather than on every render.\n */\n key: string;\n};\n\n/**\n * ADK requests a confirmation by emitting a synthetic `adk_request_confirmation`\n * tool call carrying a fresh id, and resumes only when a reply quotes that id.\n * The gated tool's own id is never answerable, so every approval carries the\n * synthetic id — but ADK yields the gated call to the client first, in its own\n * event, so both calls sit result-less in the transcript and both would render\n * an approval control. The gated call is therefore mapped to the same approval\n * rather than left bare, which would let it answer by writing a tool result the\n * agent never produced.\n */\nexport const projectAdkToolApprovals = (\n messages: readonly AdkMessage[],\n): AdkToolApprovalProjection => {\n const batches = new Map<\n string,\n { readable: boolean; entries: [string, boolean | undefined][] }\n >();\n for (const message of messages) {\n if (message.type !== \"tool\") continue;\n if (message.name !== ADK_REQUEST_CONFIRMATION) continue;\n const key = sourceEventOf(message.id);\n let batch = batches.get(key);\n if (batch === undefined) {\n batch = { readable: true, entries: [] };\n batches.set(key, batch);\n }\n const { readable, confirmed } = readConfirmation(message.content);\n if (!readable) batch.readable = false;\n batch.entries.push([message.tool_call_id, confirmed]);\n }\n\n const replies = new Map<string, boolean>();\n for (const batch of batches.values()) {\n for (const [toolCallId, confirmed] of batch.entries) {\n if (batch.readable && confirmed !== undefined)\n replies.set(toolCallId, confirmed);\n else replies.delete(toolCallId);\n }\n }\n\n const approvals = new Map<string, AdkToolApproval>();\n for (const message of messages) {\n if (message.type !== \"ai\") continue;\n for (const call of message.tool_calls ?? []) {\n if (call?.name !== ADK_REQUEST_CONFIRMATION) continue;\n const approved = replies.get(call.id);\n const approval: AdkToolApproval = {\n id: call.id,\n ...(approved !== undefined && { approved }),\n };\n approvals.set(call.id, approval);\n // ADK yields the gated call in its own event before the confirmation is\n // requested, so that call is also in the transcript, result-less, and\n // inherits the message's `requires-action` status. Left ungated it\n // renders a second approval control with no approval behind it, which\n // answers by writing a tool result the agent never produced. The same\n // approval carries the synthetic id, so answering either one replies to\n // the gate ADK is actually waiting on.\n const gatedId = gatedCallIdOf(call.args);\n if (gatedId !== undefined) approvals.set(gatedId, approval);\n }\n }\n\n return {\n approvals,\n key: approvals.size === 0 ? \"\" : JSON.stringify([...approvals]),\n };\n};\n\nexport const toAdkConfirmationReply = (\n toolCallId: string,\n confirmed: boolean,\n payload?: ReadonlyJSONValue,\n): AdkMessage & { type: \"tool\" } => ({\n id: generateId(),\n type: \"tool\",\n tool_call_id: toolCallId,\n name: ADK_REQUEST_CONFIRMATION,\n content: JSON.stringify({\n confirmed,\n ...(payload != null && { payload }),\n }),\n status: \"success\",\n});\n\n/**\n * ADK confirmations are a plain confirm/reject pair, so no option or reason\n * from core has a field to land in. An id that is not an open gate would send a\n * reply ADK silently ignores, so it throws instead.\n */\nexport const toAdkToolConfirmationReply = (\n { approvalId, approved }: RespondToToolApprovalOptions,\n approvals: ReadonlyMap<string, AdkToolApproval>,\n): AdkMessage & { type: \"tool\" } => {\n const approval = approvals.get(approvalId);\n if (approval === undefined || approval.approved !== undefined)\n throw new Error(\n `No pending ADK tool confirmation for approval id \"${approvalId}\"`,\n );\n\n return toAdkConfirmationReply(approvalId, approved);\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"convertAdkMessages.d.ts","sourceRoot":"","sources":["../src/convertAdkMessages.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAEL,KAAK,eAAe,EACrB,6BAA0B;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAyB,mBAAgB;AAiGjE,eAAO,MAAM,yBAAyB,GAElC,WAAW,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,KAC9C,2BAA2B,CAAC,QAAQ,CAAC,UAAU,CAgEjD,CAAC;AAEJ,eAAO,MAAM,iBAAiB,EAAE,2BAA2B,CAAC,QAAQ,CAAC,UAAU,CACnC,CAAC"}
1
+ {"version":3,"file":"convertAdkMessages.d.ts","sourceRoot":"","sources":["../src/convertAdkMessages.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAEL,KAAK,eAAe,EACrB,6BAA0B;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAyB,mBAAgB;AA8GjE,eAAO,MAAM,yBAAyB,GAElC,WAAW,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,KAC9C,2BAA2B,CAAC,QAAQ,CAAC,UAAU,CAqEjD,CAAC;AAEJ,eAAO,MAAM,iBAAiB,EAAE,2BAA2B,CAAC,QAAQ,CAAC,UAAU,CACnC,CAAC"}
@@ -13,25 +13,34 @@ const contentToParts = (content, role) => {
13
13
  type: "text",
14
14
  text: typeof part.text === "string" ? part.text : ""
15
15
  };
16
- case "reasoning": return {
17
- type: "reasoning",
18
- text: typeof part.text === "string" ? part.text : ""
19
- };
20
- case "image": return {
21
- type: "image",
22
- image: `data:${part.mimeType};base64,${part.data}`
23
- };
24
- case "image_url": return {
25
- type: "image",
26
- image: part.url
27
- };
28
- case "file": return {
29
- type: "file",
30
- data: part.data,
31
- mimeType: part.mimeType,
32
- ...part.filename != null && { filename: part.filename }
33
- };
16
+ case "reasoning":
17
+ if (role === "user") return null;
18
+ return {
19
+ type: "reasoning",
20
+ text: typeof part.text === "string" ? part.text : ""
21
+ };
22
+ case "image":
23
+ if (typeof part.mimeType !== "string" || typeof part.data !== "string") return null;
24
+ return {
25
+ type: "image",
26
+ image: `data:${part.mimeType};base64,${part.data}`
27
+ };
28
+ case "image_url":
29
+ if (typeof part.url !== "string") return null;
30
+ return {
31
+ type: "image",
32
+ image: part.url
33
+ };
34
+ case "file":
35
+ if (typeof part.mimeType !== "string" || typeof part.data !== "string") return null;
36
+ return {
37
+ type: "file",
38
+ data: part.data,
39
+ mimeType: part.mimeType,
40
+ ...part.filename != null && { filename: part.filename }
41
+ };
34
42
  case "file_url":
43
+ if (typeof part.url !== "string") return null;
35
44
  if (role === "user") return {
36
45
  type: "file",
37
46
  data: part.url,
@@ -79,7 +88,8 @@ const createAdkMessageConverter = (approvals) => (message) => {
79
88
  content: contentToParts(message.content, "user")
80
89
  };
81
90
  case "ai": {
82
- const toolCallParts = message.tool_calls?.map((tc) => {
91
+ const toolCallParts = message.tool_calls?.flatMap((tc) => {
92
+ if (typeof tc?.name !== "string" || tc.name.length === 0) return [];
83
93
  const approval = approvals.get(tc.id);
84
94
  return {
85
95
  type: "tool-call",
@@ -112,6 +122,7 @@ const createAdkMessageConverter = (approvals) => (message) => {
112
122
  isError: message.status === "error"
113
123
  };
114
124
  }
125
+ default: return [];
115
126
  }
116
127
  };
117
128
  const convertAdkMessage = createAdkMessageConverter(EMPTY_APPROVALS);
@@ -1 +1 @@
1
- {"version":3,"file":"convertAdkMessages.js","names":[],"sources":["../src/convertAdkMessages.ts"],"sourcesContent":["\"use client\";\n\nimport type { ToolCallMessagePart } from \"@assistant-ui/core\";\nimport type { useExternalMessageConverter } from \"@assistant-ui/core/react\";\nimport {\n ADK_REQUEST_CONFIRMATION,\n type AdkToolApproval,\n} from \"./adkToolApproval\";\nimport type { AdkMessage, AdkMessageContentPart } from \"./types\";\n\ntype ContentPart =\n | { type: \"text\"; text: string }\n | { type: \"reasoning\"; text: string }\n | { type: \"image\"; image: string }\n | {\n type: \"file\";\n data: string;\n mimeType: string;\n filename?: string;\n sourceType?: \"url\";\n }\n | { type: \"data\"; name: string; data: unknown };\n\nconst contentToParts = (\n content: AdkMessage[\"content\"],\n role: \"user\" | \"assistant\",\n): ContentPart[] => {\n if (typeof content === \"string\")\n return [{ type: \"text\" as const, text: content }];\n if (!Array.isArray(content)) return [];\n\n return content\n .filter(\n (part): part is AdkMessageContentPart =>\n typeof part === \"object\" && part !== null,\n )\n .map((part): ContentPart | null => {\n switch (part.type) {\n case \"text\":\n return {\n type: \"text\",\n text: typeof part.text === \"string\" ? part.text : \"\",\n };\n case \"reasoning\":\n return {\n type: \"reasoning\",\n text: typeof part.text === \"string\" ? part.text : \"\",\n };\n case \"image\":\n return {\n type: \"image\",\n image: `data:${part.mimeType};base64,${part.data}`,\n };\n case \"image_url\":\n return { type: \"image\", image: part.url };\n case \"file\":\n return {\n type: \"file\",\n data: part.data,\n mimeType: part.mimeType,\n ...(part.filename != null && { filename: part.filename }),\n };\n case \"file_url\":\n if (role === \"user\") {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mimeType ?? \"application/octet-stream\",\n sourceType: \"url\",\n };\n }\n return {\n type: \"data\",\n name: \"file_url\",\n data: {\n url: part.url,\n ...(part.mimeType != null && { mimeType: part.mimeType }),\n },\n };\n case \"code\":\n return {\n type: \"data\",\n name: \"executable_code\",\n data: { code: part.code, language: part.language },\n };\n case \"code_result\":\n return {\n type: \"data\",\n name: \"code_execution_result\",\n data: { output: part.output, outcome: part.outcome },\n };\n case \"activity\":\n return {\n type: \"text\",\n text: typeof part.message === \"string\" ? part.message : \"\",\n };\n default:\n return null;\n }\n })\n .filter((p): p is NonNullable<typeof p> => p !== null);\n};\n\nconst EMPTY_APPROVALS: ReadonlyMap<string, AdkToolApproval> = new Map();\n\nexport const createAdkMessageConverter =\n (\n approvals: ReadonlyMap<string, AdkToolApproval>,\n ): useExternalMessageConverter.Callback<AdkMessage> =>\n (message) => {\n switch (message.type) {\n case \"human\":\n return {\n role: \"user\",\n id: message.id,\n content: contentToParts(message.content, \"user\"),\n };\n\n case \"ai\": {\n const toolCallParts: ToolCallMessagePart[] =\n message.tool_calls?.map((tc) => {\n const approval = approvals.get(tc.id);\n return {\n type: \"tool-call\",\n toolCallId: tc.id,\n toolName: tc.name,\n args: tc.args,\n argsText: tc.argsText ?? JSON.stringify(tc.args),\n ...(approval && { approval }),\n };\n }) ?? [];\n\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n ...contentToParts(message.content, \"assistant\"),\n ...toolCallParts,\n ],\n ...(message.status && { status: message.status }),\n ...(message.author && {\n metadata: {\n custom: { author: message.author, branch: message.branch },\n },\n }),\n };\n }\n\n case \"tool\": {\n // A confirmation reply ADK could not read leaves its gate undecided.\n // The reply is not the agent's output, so it is dropped rather than\n // shown as the call's result while the gate waits to be answered again.\n // Only a reply to the confirmation itself is dropped: the gated call\n // carries the same approval, and its own result is the agent's real\n // output.\n const approval = approvals.get(message.tool_call_id);\n if (\n message.name === ADK_REQUEST_CONFIRMATION &&\n approval !== undefined &&\n approval.approved === undefined\n )\n return [];\n\n return {\n role: \"tool\",\n toolCallId: message.tool_call_id,\n toolName: message.name,\n result: message.content,\n isError: message.status === \"error\",\n };\n }\n }\n };\n\nexport const convertAdkMessage: useExternalMessageConverter.Callback<AdkMessage> =\n createAdkMessageConverter(EMPTY_APPROVALS);\n"],"mappings":";;;AAuBA,MAAM,kBACJ,SACA,SACkB;CAClB,IAAI,OAAO,YAAY,UACrB,OAAO,CAAC;EAAE,MAAM;EAAiB,MAAM;CAAQ,CAAC;CAClD,IAAI,CAAC,MAAM,QAAQ,OAAO,GAAG,OAAO,CAAC;CAErC,OAAO,QACJ,QACE,SACC,OAAO,SAAS,YAAY,SAAS,IACzC,CAAC,CACA,KAAK,SAA6B;EACjC,QAAQ,KAAK,MAAb;GACE,KAAK,QACH,OAAO;IACL,MAAM;IACN,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;GACpD;GACF,KAAK,aACH,OAAO;IACL,MAAM;IACN,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;GACpD;GACF,KAAK,SACH,OAAO;IACL,MAAM;IACN,OAAO,QAAQ,KAAK,SAAS,UAAU,KAAK;GAC9C;GACF,KAAK,aACH,OAAO;IAAE,MAAM;IAAS,OAAO,KAAK;GAAI;GAC1C,KAAK,QACH,OAAO;IACL,MAAM;IACN,MAAM,KAAK;IACX,UAAU,KAAK;IACf,GAAI,KAAK,YAAY,QAAQ,EAAE,UAAU,KAAK,SAAS;GACzD;GACF,KAAK;IACH,IAAI,SAAS,QACX,OAAO;KACL,MAAM;KACN,MAAM,KAAK;KACX,UAAU,KAAK,YAAY;KAC3B,YAAY;IACd;IAEF,OAAO;KACL,MAAM;KACN,MAAM;KACN,MAAM;MACJ,KAAK,KAAK;MACV,GAAI,KAAK,YAAY,QAAQ,EAAE,UAAU,KAAK,SAAS;KACzD;IACF;GACF,KAAK,QACH,OAAO;IACL,MAAM;IACN,MAAM;IACN,MAAM;KAAE,MAAM,KAAK;KAAM,UAAU,KAAK;IAAS;GACnD;GACF,KAAK,eACH,OAAO;IACL,MAAM;IACN,MAAM;IACN,MAAM;KAAE,QAAQ,KAAK;KAAQ,SAAS,KAAK;IAAQ;GACrD;GACF,KAAK,YACH,OAAO;IACL,MAAM;IACN,MAAM,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;GAC1D;GACF,SACE,OAAO;EACX;CACF,CAAC,CAAC,CACD,QAAQ,MAAkC,MAAM,IAAI;AACzD;AAEA,MAAM,kCAAwD,IAAI,IAAI;AAEtE,MAAa,6BAET,eAED,YAAY;CACX,QAAQ,QAAQ,MAAhB;EACE,KAAK,SACH,OAAO;GACL,MAAM;GACN,IAAI,QAAQ;GACZ,SAAS,eAAe,QAAQ,SAAS,MAAM;EACjD;EAEF,KAAK,MAAM;GACT,MAAM,gBACJ,QAAQ,YAAY,KAAK,OAAO;IAC9B,MAAM,WAAW,UAAU,IAAI,GAAG,EAAE;IACpC,OAAO;KACL,MAAM;KACN,YAAY,GAAG;KACf,UAAU,GAAG;KACb,MAAM,GAAG;KACT,UAAU,GAAG,YAAY,KAAK,UAAU,GAAG,IAAI;KAC/C,GAAI,YAAY,EAAE,SAAS;IAC7B;GACF,CAAC,KAAK,CAAC;GAET,OAAO;IACL,MAAM;IACN,IAAI,QAAQ;IACZ,SAAS,CACP,GAAG,eAAe,QAAQ,SAAS,WAAW,GAC9C,GAAG,aACL;IACA,GAAI,QAAQ,UAAU,EAAE,QAAQ,QAAQ,OAAO;IAC/C,GAAI,QAAQ,UAAU,EACpB,UAAU,EACR,QAAQ;KAAE,QAAQ,QAAQ;KAAQ,QAAQ,QAAQ;IAAO,EAC3D,EACF;GACF;EACF;EAEA,KAAK,QAAQ;GAOX,MAAM,WAAW,UAAU,IAAI,QAAQ,YAAY;GACnD,IACE,QAAQ,SAAA,8BACR,aAAa,KAAA,KACb,SAAS,aAAa,KAAA,GAEtB,OAAO,CAAC;GAEV,OAAO;IACL,MAAM;IACN,YAAY,QAAQ;IACpB,UAAU,QAAQ;IAClB,QAAQ,QAAQ;IAChB,SAAS,QAAQ,WAAW;GAC9B;EACF;CACF;AACF;AAEF,MAAa,oBACX,0BAA0B,eAAe"}
1
+ {"version":3,"file":"convertAdkMessages.js","names":[],"sources":["../src/convertAdkMessages.ts"],"sourcesContent":["\"use client\";\n\nimport type { ToolCallMessagePart } from \"@assistant-ui/core\";\nimport type { useExternalMessageConverter } from \"@assistant-ui/core/react\";\nimport {\n ADK_REQUEST_CONFIRMATION,\n type AdkToolApproval,\n} from \"./adkToolApproval\";\nimport type { AdkMessage, AdkMessageContentPart } from \"./types\";\n\ntype ContentPart =\n | { type: \"text\"; text: string }\n | { type: \"reasoning\"; text: string }\n | { type: \"image\"; image: string }\n | {\n type: \"file\";\n data: string;\n mimeType: string;\n filename?: string;\n sourceType?: \"url\";\n }\n | { type: \"data\"; name: string; data: unknown };\n\nconst contentToParts = (\n content: AdkMessage[\"content\"],\n role: \"user\" | \"assistant\",\n): ContentPart[] => {\n if (typeof content === \"string\")\n return [{ type: \"text\" as const, text: content }];\n if (!Array.isArray(content)) return [];\n\n return content\n .filter(\n (part): part is AdkMessageContentPart =>\n typeof part === \"object\" && part !== null,\n )\n .map((part): ContentPart | null => {\n switch (part.type) {\n case \"text\":\n return {\n type: \"text\",\n text: typeof part.text === \"string\" ? part.text : \"\",\n };\n case \"reasoning\":\n if (role === \"user\") return null;\n return {\n type: \"reasoning\",\n text: typeof part.text === \"string\" ? part.text : \"\",\n };\n case \"image\":\n if (\n typeof part.mimeType !== \"string\" ||\n typeof part.data !== \"string\"\n )\n return null;\n return {\n type: \"image\",\n image: `data:${part.mimeType};base64,${part.data}`,\n };\n case \"image_url\":\n if (typeof part.url !== \"string\") return null;\n return { type: \"image\", image: part.url };\n case \"file\":\n if (\n typeof part.mimeType !== \"string\" ||\n typeof part.data !== \"string\"\n )\n return null;\n return {\n type: \"file\",\n data: part.data,\n mimeType: part.mimeType,\n ...(part.filename != null && { filename: part.filename }),\n };\n case \"file_url\":\n if (typeof part.url !== \"string\") return null;\n if (role === \"user\") {\n return {\n type: \"file\",\n data: part.url,\n mimeType: part.mimeType ?? \"application/octet-stream\",\n sourceType: \"url\",\n };\n }\n return {\n type: \"data\",\n name: \"file_url\",\n data: {\n url: part.url,\n ...(part.mimeType != null && { mimeType: part.mimeType }),\n },\n };\n case \"code\":\n return {\n type: \"data\",\n name: \"executable_code\",\n data: { code: part.code, language: part.language },\n };\n case \"code_result\":\n return {\n type: \"data\",\n name: \"code_execution_result\",\n data: { output: part.output, outcome: part.outcome },\n };\n case \"activity\":\n return {\n type: \"text\",\n text: typeof part.message === \"string\" ? part.message : \"\",\n };\n default:\n return null;\n }\n })\n .filter((p): p is NonNullable<typeof p> => p !== null);\n};\n\nconst EMPTY_APPROVALS: ReadonlyMap<string, AdkToolApproval> = new Map();\n\nexport const createAdkMessageConverter =\n (\n approvals: ReadonlyMap<string, AdkToolApproval>,\n ): useExternalMessageConverter.Callback<AdkMessage> =>\n (message) => {\n switch (message.type) {\n case \"human\":\n return {\n role: \"user\",\n id: message.id,\n content: contentToParts(message.content, \"user\"),\n };\n\n case \"ai\": {\n const toolCallParts: ToolCallMessagePart[] =\n message.tool_calls?.flatMap((tc) => {\n if (typeof tc?.name !== \"string\" || tc.name.length === 0) return [];\n const approval = approvals.get(tc.id);\n return {\n type: \"tool-call\",\n toolCallId: tc.id,\n toolName: tc.name,\n args: tc.args,\n argsText: tc.argsText ?? JSON.stringify(tc.args),\n ...(approval && { approval }),\n };\n }) ?? [];\n\n return {\n role: \"assistant\",\n id: message.id,\n content: [\n ...contentToParts(message.content, \"assistant\"),\n ...toolCallParts,\n ],\n ...(message.status && { status: message.status }),\n ...(message.author && {\n metadata: {\n custom: { author: message.author, branch: message.branch },\n },\n }),\n };\n }\n\n case \"tool\": {\n // A confirmation reply ADK could not read leaves its gate undecided.\n // The reply is not the agent's output, so it is dropped rather than\n // shown as the call's result while the gate waits to be answered again.\n // Only a reply to the confirmation itself is dropped: the gated call\n // carries the same approval, and its own result is the agent's real\n // output.\n const approval = approvals.get(message.tool_call_id);\n if (\n message.name === ADK_REQUEST_CONFIRMATION &&\n approval !== undefined &&\n approval.approved === undefined\n )\n return [];\n\n return {\n role: \"tool\",\n toolCallId: message.tool_call_id,\n toolName: message.name,\n result: message.content,\n isError: message.status === \"error\",\n };\n }\n\n default:\n message satisfies never;\n return [];\n }\n };\n\nexport const convertAdkMessage: useExternalMessageConverter.Callback<AdkMessage> =\n createAdkMessageConverter(EMPTY_APPROVALS);\n"],"mappings":";;;AAuBA,MAAM,kBACJ,SACA,SACkB;CAClB,IAAI,OAAO,YAAY,UACrB,OAAO,CAAC;EAAE,MAAM;EAAiB,MAAM;CAAQ,CAAC;CAClD,IAAI,CAAC,MAAM,QAAQ,OAAO,GAAG,OAAO,CAAC;CAErC,OAAO,QACJ,QACE,SACC,OAAO,SAAS,YAAY,SAAS,IACzC,CAAC,CACA,KAAK,SAA6B;EACjC,QAAQ,KAAK,MAAb;GACE,KAAK,QACH,OAAO;IACL,MAAM;IACN,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;GACpD;GACF,KAAK;IACH,IAAI,SAAS,QAAQ,OAAO;IAC5B,OAAO;KACL,MAAM;KACN,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;IACpD;GACF,KAAK;IACH,IACE,OAAO,KAAK,aAAa,YACzB,OAAO,KAAK,SAAS,UAErB,OAAO;IACT,OAAO;KACL,MAAM;KACN,OAAO,QAAQ,KAAK,SAAS,UAAU,KAAK;IAC9C;GACF,KAAK;IACH,IAAI,OAAO,KAAK,QAAQ,UAAU,OAAO;IACzC,OAAO;KAAE,MAAM;KAAS,OAAO,KAAK;IAAI;GAC1C,KAAK;IACH,IACE,OAAO,KAAK,aAAa,YACzB,OAAO,KAAK,SAAS,UAErB,OAAO;IACT,OAAO;KACL,MAAM;KACN,MAAM,KAAK;KACX,UAAU,KAAK;KACf,GAAI,KAAK,YAAY,QAAQ,EAAE,UAAU,KAAK,SAAS;IACzD;GACF,KAAK;IACH,IAAI,OAAO,KAAK,QAAQ,UAAU,OAAO;IACzC,IAAI,SAAS,QACX,OAAO;KACL,MAAM;KACN,MAAM,KAAK;KACX,UAAU,KAAK,YAAY;KAC3B,YAAY;IACd;IAEF,OAAO;KACL,MAAM;KACN,MAAM;KACN,MAAM;MACJ,KAAK,KAAK;MACV,GAAI,KAAK,YAAY,QAAQ,EAAE,UAAU,KAAK,SAAS;KACzD;IACF;GACF,KAAK,QACH,OAAO;IACL,MAAM;IACN,MAAM;IACN,MAAM;KAAE,MAAM,KAAK;KAAM,UAAU,KAAK;IAAS;GACnD;GACF,KAAK,eACH,OAAO;IACL,MAAM;IACN,MAAM;IACN,MAAM;KAAE,QAAQ,KAAK;KAAQ,SAAS,KAAK;IAAQ;GACrD;GACF,KAAK,YACH,OAAO;IACL,MAAM;IACN,MAAM,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;GAC1D;GACF,SACE,OAAO;EACX;CACF,CAAC,CAAC,CACD,QAAQ,MAAkC,MAAM,IAAI;AACzD;AAEA,MAAM,kCAAwD,IAAI,IAAI;AAEtE,MAAa,6BAET,eAED,YAAY;CACX,QAAQ,QAAQ,MAAhB;EACE,KAAK,SACH,OAAO;GACL,MAAM;GACN,IAAI,QAAQ;GACZ,SAAS,eAAe,QAAQ,SAAS,MAAM;EACjD;EAEF,KAAK,MAAM;GACT,MAAM,gBACJ,QAAQ,YAAY,SAAS,OAAO;IAClC,IAAI,OAAO,IAAI,SAAS,YAAY,GAAG,KAAK,WAAW,GAAG,OAAO,CAAC;IAClE,MAAM,WAAW,UAAU,IAAI,GAAG,EAAE;IACpC,OAAO;KACL,MAAM;KACN,YAAY,GAAG;KACf,UAAU,GAAG;KACb,MAAM,GAAG;KACT,UAAU,GAAG,YAAY,KAAK,UAAU,GAAG,IAAI;KAC/C,GAAI,YAAY,EAAE,SAAS;IAC7B;GACF,CAAC,KAAK,CAAC;GAET,OAAO;IACL,MAAM;IACN,IAAI,QAAQ;IACZ,SAAS,CACP,GAAG,eAAe,QAAQ,SAAS,WAAW,GAC9C,GAAG,aACL;IACA,GAAI,QAAQ,UAAU,EAAE,QAAQ,QAAQ,OAAO;IAC/C,GAAI,QAAQ,UAAU,EACpB,UAAU,EACR,QAAQ;KAAE,QAAQ,QAAQ;KAAQ,QAAQ,QAAQ;IAAO,EAC3D,EACF;GACF;EACF;EAEA,KAAK,QAAQ;GAOX,MAAM,WAAW,UAAU,IAAI,QAAQ,YAAY;GACnD,IACE,QAAQ,SAAA,8BACR,aAAa,KAAA,KACb,SAAS,aAAa,KAAA,GAEtB,OAAO,CAAC;GAEV,OAAO;IACL,MAAM;IACN,YAAY,QAAQ;IACpB,UAAU,QAAQ;IAClB,QAAQ,QAAQ;IAChB,SAAS,QAAQ,WAAW;GAC9B;EACF;EAEA,SAEE,OAAO,CAAC;CACZ;AACF;AAEF,MAAa,oBACX,0BAA0B,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"convertToAdkMessages.d.ts","sourceRoot":"","sources":["../src/convertToAdkMessages.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAgB;AAE1C,+BAA+B;AAC/B,eAAO,MAAM,iBAAiB,GAAI,KAAK,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAwEnD,CAAC;AAEF,+BAA+B;AAC/B,eAAO,MAAM,mBAAmB,GAAI,UAAU,UAAU,EAAE,uCAczD,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,UAAU,EAAE,EACtB,oBAAoB,SAAS,MAAM,EAAE,KACpC,KAAK,CAAC,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAWrC,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,gBAAgB,SAAS,aAAa,EAAE,EACxC,UAAU,MAAM,GAAG,IAAI,KACtB,UAAU,EASZ,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,KAAK,aAAa,EAClB,WAAiB,KAChB,UAAU,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAIzC,CAAC"}
1
+ {"version":3,"file":"convertToAdkMessages.d.ts","sourceRoot":"","sources":["../src/convertToAdkMessages.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAgB;AAE1C,+BAA+B;AAC/B,eAAO,MAAM,iBAAiB,GAAI,KAAK,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAwEnD,CAAC;AAEF,+BAA+B;AAC/B,eAAO,MAAM,mBAAmB,GAAI,UAAU,UAAU,EAAE,uCAgBzD,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,UAAU,EAAE,EACtB,oBAAoB,SAAS,MAAM,EAAE,KACpC,KAAK,CAAC,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAWrC,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,gBAAgB,SAAS,aAAa,EAAE,EACxC,UAAU,MAAM,GAAG,IAAI,KACtB,UAAU,EASZ,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,KAAK,aAAa,EAClB,WAAiB,KAChB,UAAU,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAIzC,CAAC"}
@@ -1,4 +1,4 @@
1
- import { createToolCallCancellationStub, parseDataUrl, resolveFilePartSource, resolveImageMediaType, scanPendingToolCalls } from "@assistant-ui/core/internal";
1
+ import { createToolCallCancellationStub, isRecord, parseDataUrl, resolveFilePartSource, resolveImageMediaType, scanPendingToolCalls } from "@assistant-ui/core/internal";
2
2
  import { generateId, getExternalStoreMessages } from "@assistant-ui/core";
3
3
  //#region src/convertToAdkMessages.ts
4
4
  /** Exported for unit tests. */
@@ -61,7 +61,7 @@ const getMessageContent = (msg) => {
61
61
  /** Exported for unit tests. */
62
62
  const getPendingToolCalls = (messages) => {
63
63
  return scanPendingToolCalls(messages, (message) => {
64
- if (message.type === "ai") return { toolCalls: message.tool_calls ?? [] };
64
+ if (message.type === "ai") return { toolCalls: (message.tool_calls ?? []).filter(isRecord) };
65
65
  if (message.type === "tool") return { toolCallId: message.tool_call_id };
66
66
  }, (toolCall) => toolCall.id);
67
67
  };
@@ -1 +1 @@
1
- {"version":3,"file":"convertToAdkMessages.js","names":["_exhaustiveCheck"],"sources":["../src/convertToAdkMessages.ts"],"sourcesContent":["import {\n generateId,\n getExternalStoreMessages,\n type AppendMessage,\n type ThreadMessage,\n} from \"@assistant-ui/core\";\nimport {\n createToolCallCancellationStub,\n parseDataUrl,\n resolveFilePartSource,\n resolveImageMediaType,\n scanPendingToolCalls,\n} from \"@assistant-ui/core/internal\";\nimport type { AdkMessage } from \"./types\";\n\n/** Exported for unit tests. */\nexport const getMessageContent = (msg: AppendMessage) => {\n const allContent = [\n ...msg.content.map((part) => ({ part, contentType: undefined })),\n ...(msg.attachments?.flatMap((attachment) =>\n attachment.content.map((part) => ({\n part,\n contentType: attachment.contentType,\n })),\n ) ?? []),\n ];\n const content = allContent.flatMap(({ part, contentType }) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n case \"image\": {\n const parsed = parseDataUrl(part.image);\n if (parsed) {\n return {\n type: \"image\" as const,\n mimeType: resolveImageMediaType(part.image, contentType),\n data: parsed.data,\n };\n }\n return { type: \"image_url\" as const, url: part.image };\n }\n case \"file\": {\n const source = resolveFilePartSource(part);\n if (source.kind === \"url\") {\n return {\n type: \"file_url\" as const,\n url: source.url,\n mimeType: part.mimeType,\n };\n }\n return {\n type: \"file\" as const,\n mimeType: part.mimeType,\n // Lands in Gemini `inlineData.data`, which takes bare base64, so a\n // data URL envelope is stripped rather than forwarded.\n data: source.data,\n ...(part.filename != null && { filename: part.filename }),\n };\n }\n case \"audio\": {\n const parsed = parseDataUrl(part.audio.data);\n return {\n type: \"file\" as const,\n mimeType: `audio/${part.audio.format}`,\n data: parsed?.data ?? part.audio.data,\n };\n }\n case \"data\":\n return [];\n\n case \"tool-call\":\n throw new Error(\"Tool call appends are not supported.\");\n\n default: {\n const _exhaustiveCheck: \"reasoning\" | \"source\" | \"generative-ui\" = type;\n throw new Error(\n `Unsupported append message part type: ${_exhaustiveCheck}`,\n );\n }\n }\n });\n\n if (content.length === 1 && content[0]?.type === \"text\") {\n return content[0].text ?? \"\";\n }\n\n return content;\n};\n\n/** Exported for unit tests. */\nexport const getPendingToolCalls = (messages: AdkMessage[]) => {\n return scanPendingToolCalls(\n messages,\n (message) => {\n if (message.type === \"ai\") {\n return { toolCalls: message.tool_calls ?? [] };\n }\n if (message.type === \"tool\") {\n return { toolCallId: message.tool_call_id };\n }\n return undefined;\n },\n (toolCall) => toolCall.id,\n );\n};\n\n/**\n * Exported for unit tests.\n *\n * Returns `{cancelled: true}` tool responses for pending tool calls when the\n * user sends a new turn, EXCEPT for HITL interrupts marked via\n * `long_running_tool_ids` (`adk_request_input`, `adk_request_confirmation`,\n * `adk_request_credential`). Those must be answered through a dedicated tool\n * UI + submit helper, not auto-cancelled.\n */\nexport const getPendingCancellations = (\n messages: AdkMessage[],\n longRunningToolIds: readonly string[],\n): Array<AdkMessage & { type: \"tool\" }> => {\n const longRunningSet = new Set(longRunningToolIds);\n return getPendingToolCalls(messages)\n .filter((t) => !longRunningSet.has(t.id))\n .map(\n (t) =>\n ({\n id: generateId(),\n ...createToolCallCancellationStub(t),\n }) satisfies AdkMessage & { type: \"tool\" },\n );\n};\n\nexport const truncateAdkMessages = (\n threadMessages: readonly ThreadMessage[],\n parentId: string | null,\n): AdkMessage[] => {\n if (parentId === null) return [];\n const parentIndex = threadMessages.findIndex((m) => m.id === parentId);\n if (parentIndex === -1) return [];\n const truncated: AdkMessage[] = [];\n for (let i = 0; i <= parentIndex && i < threadMessages.length; i++) {\n truncated.push(...getExternalStoreMessages<AdkMessage>(threadMessages[i]!));\n }\n return truncated;\n};\n\nexport const toAdkUserMessage = (\n msg: AppendMessage,\n id = generateId(),\n): AdkMessage & { type: \"human\"; id: string } => ({\n id,\n type: \"human\",\n content: getMessageContent(msg),\n});\n"],"mappings":";;;;AAgBA,MAAa,qBAAqB,QAAuB;CAUvD,MAAM,UAAU,CARd,GAAG,IAAI,QAAQ,KAAK,UAAU;EAAE;EAAM,aAAa,KAAA;CAAU,EAAE,GAC/D,GAAI,IAAI,aAAa,SAAS,eAC5B,WAAW,QAAQ,KAAK,UAAU;EAChC;EACA,aAAa,WAAW;CAC1B,EAAE,CACJ,KAAK,CAAC,CAEiB,CAAC,CAAC,SAAS,EAAE,MAAM,kBAAkB;EAC5D,MAAM,OAAO,KAAK;EAClB,QAAQ,MAAR;GACE,KAAK,QACH,OAAO;IAAE,MAAM;IAAiB,MAAM,KAAK;GAAK;GAClD,KAAK,SAAS;IACZ,MAAM,SAAS,aAAa,KAAK,KAAK;IACtC,IAAI,QACF,OAAO;KACL,MAAM;KACN,UAAU,sBAAsB,KAAK,OAAO,WAAW;KACvD,MAAM,OAAO;IACf;IAEF,OAAO;KAAE,MAAM;KAAsB,KAAK,KAAK;IAAM;GACvD;GACA,KAAK,QAAQ;IACX,MAAM,SAAS,sBAAsB,IAAI;IACzC,IAAI,OAAO,SAAS,OAClB,OAAO;KACL,MAAM;KACN,KAAK,OAAO;KACZ,UAAU,KAAK;IACjB;IAEF,OAAO;KACL,MAAM;KACN,UAAU,KAAK;KAGf,MAAM,OAAO;KACb,GAAI,KAAK,YAAY,QAAQ,EAAE,UAAU,KAAK,SAAS;IACzD;GACF;GACA,KAAK,SAAS;IACZ,MAAM,SAAS,aAAa,KAAK,MAAM,IAAI;IAC3C,OAAO;KACL,MAAM;KACN,UAAU,SAAS,KAAK,MAAM;KAC9B,MAAM,QAAQ,QAAQ,KAAK,MAAM;IACnC;GACF;GACA,KAAK,QACH,OAAO,CAAC;GAEV,KAAK,aACH,MAAM,IAAI,MAAM,sCAAsC;GAExD,SAEE,MAAM,IAAI,MACR,yCAAyCA,MAC3C;EAEJ;CACF,CAAC;CAED,IAAI,QAAQ,WAAW,KAAK,QAAQ,EAAE,EAAE,SAAS,QAC/C,OAAO,QAAQ,EAAE,CAAC,QAAQ;CAG5B,OAAO;AACT;;AAGA,MAAa,uBAAuB,aAA2B;CAC7D,OAAO,qBACL,WACC,YAAY;EACX,IAAI,QAAQ,SAAS,MACnB,OAAO,EAAE,WAAW,QAAQ,cAAc,CAAC,EAAE;EAE/C,IAAI,QAAQ,SAAS,QACnB,OAAO,EAAE,YAAY,QAAQ,aAAa;CAG9C,IACC,aAAa,SAAS,EACzB;AACF;;;;;;;;;;AAWA,MAAa,2BACX,UACA,uBACyC;CACzC,MAAM,iBAAiB,IAAI,IAAI,kBAAkB;CACjD,OAAO,oBAAoB,QAAQ,CAAC,CACjC,QAAQ,MAAM,CAAC,eAAe,IAAI,EAAE,EAAE,CAAC,CAAC,CACxC,KACE,OACE;EACC,IAAI,WAAW;EACf,GAAG,+BAA+B,CAAC;CACrC,EACJ;AACJ;AAEA,MAAa,uBACX,gBACA,aACiB;CACjB,IAAI,aAAa,MAAM,OAAO,CAAC;CAC/B,MAAM,cAAc,eAAe,WAAW,MAAM,EAAE,OAAO,QAAQ;CACrE,IAAI,gBAAgB,IAAI,OAAO,CAAC;CAChC,MAAM,YAA0B,CAAC;CACjC,KAAK,IAAI,IAAI,GAAG,KAAK,eAAe,IAAI,eAAe,QAAQ,KAC7D,UAAU,KAAK,GAAG,yBAAqC,eAAe,EAAG,CAAC;CAE5E,OAAO;AACT;AAEA,MAAa,oBACX,KACA,KAAK,WAAW,OACgC;CAChD;CACA,MAAM;CACN,SAAS,kBAAkB,GAAG;AAChC"}
1
+ {"version":3,"file":"convertToAdkMessages.js","names":["_exhaustiveCheck"],"sources":["../src/convertToAdkMessages.ts"],"sourcesContent":["import { isRecord } from \"@assistant-ui/core/internal\";\nimport {\n generateId,\n getExternalStoreMessages,\n type AppendMessage,\n type ThreadMessage,\n} from \"@assistant-ui/core\";\nimport {\n createToolCallCancellationStub,\n parseDataUrl,\n resolveFilePartSource,\n resolveImageMediaType,\n scanPendingToolCalls,\n} from \"@assistant-ui/core/internal\";\nimport type { AdkMessage } from \"./types\";\n\n/** Exported for unit tests. */\nexport const getMessageContent = (msg: AppendMessage) => {\n const allContent = [\n ...msg.content.map((part) => ({ part, contentType: undefined })),\n ...(msg.attachments?.flatMap((attachment) =>\n attachment.content.map((part) => ({\n part,\n contentType: attachment.contentType,\n })),\n ) ?? []),\n ];\n const content = allContent.flatMap(({ part, contentType }) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n case \"image\": {\n const parsed = parseDataUrl(part.image);\n if (parsed) {\n return {\n type: \"image\" as const,\n mimeType: resolveImageMediaType(part.image, contentType),\n data: parsed.data,\n };\n }\n return { type: \"image_url\" as const, url: part.image };\n }\n case \"file\": {\n const source = resolveFilePartSource(part);\n if (source.kind === \"url\") {\n return {\n type: \"file_url\" as const,\n url: source.url,\n mimeType: part.mimeType,\n };\n }\n return {\n type: \"file\" as const,\n mimeType: part.mimeType,\n // Lands in Gemini `inlineData.data`, which takes bare base64, so a\n // data URL envelope is stripped rather than forwarded.\n data: source.data,\n ...(part.filename != null && { filename: part.filename }),\n };\n }\n case \"audio\": {\n const parsed = parseDataUrl(part.audio.data);\n return {\n type: \"file\" as const,\n mimeType: `audio/${part.audio.format}`,\n data: parsed?.data ?? part.audio.data,\n };\n }\n case \"data\":\n return [];\n\n case \"tool-call\":\n throw new Error(\"Tool call appends are not supported.\");\n\n default: {\n const _exhaustiveCheck: \"reasoning\" | \"source\" | \"generative-ui\" = type;\n throw new Error(\n `Unsupported append message part type: ${_exhaustiveCheck}`,\n );\n }\n }\n });\n\n if (content.length === 1 && content[0]?.type === \"text\") {\n return content[0].text ?? \"\";\n }\n\n return content;\n};\n\n/** Exported for unit tests. */\nexport const getPendingToolCalls = (messages: AdkMessage[]) => {\n return scanPendingToolCalls(\n messages,\n (message) => {\n if (message.type === \"ai\") {\n return {\n toolCalls: (message.tool_calls ?? []).filter(isRecord),\n };\n }\n if (message.type === \"tool\") {\n return { toolCallId: message.tool_call_id };\n }\n return undefined;\n },\n (toolCall) => toolCall.id,\n );\n};\n\n/**\n * Exported for unit tests.\n *\n * Returns `{cancelled: true}` tool responses for pending tool calls when the\n * user sends a new turn, EXCEPT for HITL interrupts marked via\n * `long_running_tool_ids` (`adk_request_input`, `adk_request_confirmation`,\n * `adk_request_credential`). Those must be answered through a dedicated tool\n * UI + submit helper, not auto-cancelled.\n */\nexport const getPendingCancellations = (\n messages: AdkMessage[],\n longRunningToolIds: readonly string[],\n): Array<AdkMessage & { type: \"tool\" }> => {\n const longRunningSet = new Set(longRunningToolIds);\n return getPendingToolCalls(messages)\n .filter((t) => !longRunningSet.has(t.id))\n .map(\n (t) =>\n ({\n id: generateId(),\n ...createToolCallCancellationStub(t),\n }) satisfies AdkMessage & { type: \"tool\" },\n );\n};\n\nexport const truncateAdkMessages = (\n threadMessages: readonly ThreadMessage[],\n parentId: string | null,\n): AdkMessage[] => {\n if (parentId === null) return [];\n const parentIndex = threadMessages.findIndex((m) => m.id === parentId);\n if (parentIndex === -1) return [];\n const truncated: AdkMessage[] = [];\n for (let i = 0; i <= parentIndex && i < threadMessages.length; i++) {\n truncated.push(...getExternalStoreMessages<AdkMessage>(threadMessages[i]!));\n }\n return truncated;\n};\n\nexport const toAdkUserMessage = (\n msg: AppendMessage,\n id = generateId(),\n): AdkMessage & { type: \"human\"; id: string } => ({\n id,\n type: \"human\",\n content: getMessageContent(msg),\n});\n"],"mappings":";;;;AAiBA,MAAa,qBAAqB,QAAuB;CAUvD,MAAM,UAAU,CARd,GAAG,IAAI,QAAQ,KAAK,UAAU;EAAE;EAAM,aAAa,KAAA;CAAU,EAAE,GAC/D,GAAI,IAAI,aAAa,SAAS,eAC5B,WAAW,QAAQ,KAAK,UAAU;EAChC;EACA,aAAa,WAAW;CAC1B,EAAE,CACJ,KAAK,CAAC,CAEiB,CAAC,CAAC,SAAS,EAAE,MAAM,kBAAkB;EAC5D,MAAM,OAAO,KAAK;EAClB,QAAQ,MAAR;GACE,KAAK,QACH,OAAO;IAAE,MAAM;IAAiB,MAAM,KAAK;GAAK;GAClD,KAAK,SAAS;IACZ,MAAM,SAAS,aAAa,KAAK,KAAK;IACtC,IAAI,QACF,OAAO;KACL,MAAM;KACN,UAAU,sBAAsB,KAAK,OAAO,WAAW;KACvD,MAAM,OAAO;IACf;IAEF,OAAO;KAAE,MAAM;KAAsB,KAAK,KAAK;IAAM;GACvD;GACA,KAAK,QAAQ;IACX,MAAM,SAAS,sBAAsB,IAAI;IACzC,IAAI,OAAO,SAAS,OAClB,OAAO;KACL,MAAM;KACN,KAAK,OAAO;KACZ,UAAU,KAAK;IACjB;IAEF,OAAO;KACL,MAAM;KACN,UAAU,KAAK;KAGf,MAAM,OAAO;KACb,GAAI,KAAK,YAAY,QAAQ,EAAE,UAAU,KAAK,SAAS;IACzD;GACF;GACA,KAAK,SAAS;IACZ,MAAM,SAAS,aAAa,KAAK,MAAM,IAAI;IAC3C,OAAO;KACL,MAAM;KACN,UAAU,SAAS,KAAK,MAAM;KAC9B,MAAM,QAAQ,QAAQ,KAAK,MAAM;IACnC;GACF;GACA,KAAK,QACH,OAAO,CAAC;GAEV,KAAK,aACH,MAAM,IAAI,MAAM,sCAAsC;GAExD,SAEE,MAAM,IAAI,MACR,yCAAyCA,MAC3C;EAEJ;CACF,CAAC;CAED,IAAI,QAAQ,WAAW,KAAK,QAAQ,EAAE,EAAE,SAAS,QAC/C,OAAO,QAAQ,EAAE,CAAC,QAAQ;CAG5B,OAAO;AACT;;AAGA,MAAa,uBAAuB,aAA2B;CAC7D,OAAO,qBACL,WACC,YAAY;EACX,IAAI,QAAQ,SAAS,MACnB,OAAO,EACL,YAAY,QAAQ,cAAc,CAAC,EAAA,CAAG,OAAO,QAAQ,EACvD;EAEF,IAAI,QAAQ,SAAS,QACnB,OAAO,EAAE,YAAY,QAAQ,aAAa;CAG9C,IACC,aAAa,SAAS,EACzB;AACF;;;;;;;;;;AAWA,MAAa,2BACX,UACA,uBACyC;CACzC,MAAM,iBAAiB,IAAI,IAAI,kBAAkB;CACjD,OAAO,oBAAoB,QAAQ,CAAC,CACjC,QAAQ,MAAM,CAAC,eAAe,IAAI,EAAE,EAAE,CAAC,CAAC,CACxC,KACE,OACE;EACC,IAAI,WAAW;EACf,GAAG,+BAA+B,CAAC;CACrC,EACJ;AACJ;AAEA,MAAa,uBACX,gBACA,aACiB;CACjB,IAAI,aAAa,MAAM,OAAO,CAAC;CAC/B,MAAM,cAAc,eAAe,WAAW,MAAM,EAAE,OAAO,QAAQ;CACrE,IAAI,gBAAgB,IAAI,OAAO,CAAC;CAChC,MAAM,YAA0B,CAAC;CACjC,KAAK,IAAI,IAAI,GAAG,KAAK,eAAe,IAAI,eAAe,QAAQ,KAC7D,UAAU,KAAK,GAAG,yBAAqC,eAAe,EAAG,CAAC;CAE5E,OAAO;AACT;AAEA,MAAa,oBACX,KACA,KAAK,WAAW,OACgC;CAChD;CACA,MAAM;CACN,SAAS,kBAAkB,GAAG;AAChC"}
@@ -1,7 +1,7 @@
1
1
  //#region src/sdkIdentity.ts
2
2
  const ADK_SDK = {
3
3
  name: "@assistant-ui/react-google-adk",
4
- version: "0.0.33"
4
+ version: "0.0.34"
5
5
  };
6
6
  //#endregion
7
7
  export { ADK_SDK };
@@ -1 +1 @@
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;;;;;;;;+BAqFP,UAAU,EAAE,UAAU,oBAAoB;;wBAvDhB,UAAU,EAAE;4BAmCnD,UAAU,EAAE;8BAnBR,iBAAiB;CAyM/B,CAAC"}
1
+ {"version":3,"file":"useAdkMessages.d.ts","sourceRoot":"","sources":["../src/useAdkMessages.ts"],"names":[],"mappings":"AAmBA,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;;;;;;;;+BAqFP,UAAU,EAAE,UAAU,oBAAoB;;wBAvDhB,UAAU,EAAE;4BAmCnD,UAAU,EAAE;8BAnBR,iBAAiB;CAuO/B,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { contentToParts } from "./contentToParts.js";
2
2
  import { toAdkFunctionResponse } from "./toAdkFunctionResponse.js";
3
3
  import { AdkEventAccumulator } from "./AdkEventAccumulator.js";
4
- import { abortableIterable, invokeUserCallback, openAbortableIterable } from "@assistant-ui/core/internal";
4
+ import { abortableIterable, invokeUserCallback, isRecord, openAbortableIterable } from "@assistant-ui/core/internal";
5
5
  import { generateId } from "@assistant-ui/core";
6
6
  import { useAui } from "@assistant-ui/store";
7
7
  import { useReplaySafeEffect } from "@assistant-ui/store/internal";
@@ -79,8 +79,11 @@ const useAdkMessages = ({ stream, eventHandlers }) => {
79
79
  const answeredToolCallIds = new Set(newMessagesWithId.flatMap((m) => m.type === "tool" ? [m.tool_call_id] : []));
80
80
  const accumulator = new AdkEventAccumulator(messagesRef.current.filter((m) => !resentIds.has(m.id)), longRunningToolIdsRef.current.filter((id) => !answeredToolCallIds.has(id)));
81
81
  for (const event of messagesToEvents(newMessagesWithId)) accumulator.processEvent(event);
82
- setMessagesImmediate(accumulator.getMessages());
83
- setLongRunningToolIds(accumulator.getLongRunningToolIds());
82
+ const initialMessages = accumulator.getMessages();
83
+ const initialMessageIds = new Set(initialMessages.map((m) => m.id));
84
+ const initialLongRunningToolIds = accumulator.getLongRunningToolIds();
85
+ setMessagesImmediate(initialMessages);
86
+ setLongRunningToolIds(initialLongRunningToolIds);
84
87
  setToolConfirmations(accumulator.getToolConfirmations());
85
88
  setAuthRequests(accumulator.getAuthRequests());
86
89
  let lastTransferToAgent;
@@ -128,7 +131,21 @@ const useAdkMessages = ({ stream, eventHandlers }) => {
128
131
  } catch (error) {
129
132
  if (!abortController.signal.aborted && abortControllerRef.current === abortController && !(error instanceof Error && error.name === "AbortError")) throw error;
130
133
  } finally {
131
- if (abortControllerRef.current === abortController) abortControllerRef.current = null;
134
+ if (abortControllerRef.current === abortController) {
135
+ if (abortController.signal.aborted) {
136
+ setLongRunningToolIds(accumulator.getLongRunningToolIds().filter((id) => initialLongRunningToolIds.includes(id)));
137
+ const updatedMessages = messagesRef.current;
138
+ const lastAssistantMessage = updatedMessages.findLast((m) => m.type === "ai");
139
+ if (lastAssistantMessage && !initialMessageIds.has(lastAssistantMessage.id) && !lastAssistantMessage.status) setMessagesImmediate(updatedMessages.map((m) => m === lastAssistantMessage ? {
140
+ ...lastAssistantMessage,
141
+ status: {
142
+ type: "incomplete",
143
+ reason: "cancelled"
144
+ }
145
+ } : m));
146
+ }
147
+ abortControllerRef.current = null;
148
+ }
132
149
  }
133
150
  }, [
134
151
  aui,
@@ -229,11 +246,11 @@ const messageToEvent = (msg) => {
229
246
  if (msg.author != null) result.author = msg.author;
230
247
  result.content = {
231
248
  role: "model",
232
- parts: [...contentToParts(msg.content), ...msg.tool_calls?.map((tc) => ({ functionCall: {
249
+ parts: [...contentToParts(msg.content), ...(msg.tool_calls ?? []).filter(isRecord).map((tc) => ({ functionCall: {
233
250
  name: tc.name,
234
251
  id: tc.id,
235
252
  args: { ...tc.args }
236
- } })) ?? []]
253
+ } }))]
237
254
  };
238
255
  return result;
239
256
  };