@assistant-ui/react-google-adk 0.0.28 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AdkClient.d.ts.map +1 -1
- package/dist/AdkClient.js +2 -1
- package/dist/AdkClient.js.map +1 -1
- package/dist/AdkEventAccumulator.d.ts +1 -1
- package/dist/AdkEventAccumulator.d.ts.map +1 -1
- package/dist/AdkEventAccumulator.js +33 -12
- package/dist/AdkEventAccumulator.js.map +1 -1
- package/dist/convertToAdkMessages.d.ts +10 -1
- package/dist/convertToAdkMessages.d.ts.map +1 -1
- package/dist/convertToAdkMessages.js +20 -6
- package/dist/convertToAdkMessages.js.map +1 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +6 -8
- package/dist/hooks.js.map +1 -1
- package/dist/parseAdkEvent.d.ts.map +1 -1
- package/dist/parseAdkEvent.js +15 -2
- package/dist/parseAdkEvent.js.map +1 -1
- package/dist/sdkIdentity.d.ts +6 -0
- package/dist/sdkIdentity.d.ts.map +1 -0
- package/dist/sdkIdentity.js +9 -0
- package/dist/sdkIdentity.js.map +1 -0
- package/dist/server/parseAdkRequest.d.ts.map +1 -1
- package/dist/server/parseAdkRequest.js +2 -1
- package/dist/server/parseAdkRequest.js.map +1 -1
- package/dist/toAdkFunctionResponse.d.ts +6 -0
- package/dist/toAdkFunctionResponse.d.ts.map +1 -0
- package/dist/toAdkFunctionResponse.js +11 -0
- package/dist/toAdkFunctionResponse.js.map +1 -0
- package/dist/useAdkMessages.d.ts.map +1 -1
- package/dist/useAdkMessages.js +22 -10
- package/dist/useAdkMessages.js.map +1 -1
- package/dist/useAdkRuntime.d.ts.map +1 -1
- package/dist/useAdkRuntime.js +6 -2
- package/dist/useAdkRuntime.js.map +1 -1
- package/package.json +6 -6
- package/src/AdkClient.test.ts +192 -2
- package/src/AdkClient.ts +2 -1
- package/src/AdkEventAccumulator.test.ts +297 -0
- package/src/AdkEventAccumulator.ts +35 -5
- package/src/AdkSessionAdapter.test.ts +88 -0
- package/src/convertToAdkMessages.test.ts +70 -0
- package/src/convertToAdkMessages.ts +19 -4
- package/src/hooks.render.test.tsx +74 -0
- package/src/hooks.test.tsx +33 -0
- package/src/hooks.ts +17 -22
- package/src/parseAdkEvent.ts +36 -7
- package/src/sdkIdentity.ts +9 -0
- package/src/server/parseAdkRequest.test.ts +63 -0
- package/src/server/parseAdkRequest.ts +2 -1
- package/src/toAdkFunctionResponse.test.ts +46 -0
- package/src/toAdkFunctionResponse.ts +18 -0
- package/src/useAdkMessages.test.ts +247 -0
- package/src/useAdkMessages.ts +51 -15
- package/src/useAdkRuntime.replacement.test.tsx +138 -0
- package/src/useAdkRuntime.ts +5 -1
- package/src/useAdkRuntimeApproval.test.tsx +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseAdkEvent.js","names":[],"sources":["../src/parseAdkEvent.ts"],"sourcesContent":["import type { AdkEvent } from \"./types\";\n\
|
|
1
|
+
{"version":3,"file":"parseAdkEvent.js","names":[],"sources":["../src/parseAdkEvent.ts"],"sourcesContent":["import { isRecord } from \"@assistant-ui/core/internal\";\nimport type { AdkEvent } from \"./types\";\n\nconst invalidField = (\n errorPrefix: string,\n field: string,\n expectation: string,\n): Error =>\n new Error(`${errorPrefix}: expected \"${field}\" to be ${expectation}.`);\n\nexport function parseAdkEventValue(\n value: unknown,\n errorPrefix: string,\n): AdkEvent {\n if (!isRecord(value) || Object.keys(value).length === 0) {\n throw new Error(`${errorPrefix}: expected a non-empty object.`);\n }\n\n const {\n id: rawId,\n content: rawContent,\n ...event\n } = value as Record<string, unknown>;\n if (\n rawId != null &&\n typeof rawId !== \"string\" &&\n (typeof rawId !== \"number\" || !Number.isFinite(rawId))\n ) {\n throw new Error(\n `${errorPrefix}: expected \"id\" to be a string or finite number when present.`,\n );\n }\n\n let content: Record<string, unknown> | undefined;\n if (rawContent != null) {\n if (!isRecord(rawContent)) {\n throw invalidField(errorPrefix, \"content\", \"an object when present\");\n }\n\n const { parts, ...contentFields } = rawContent;\n if (parts != null && (!Array.isArray(parts) || !parts.every(isRecord))) {\n throw invalidField(\n errorPrefix,\n \"content.parts\",\n \"an array of objects when present\",\n );\n }\n\n content = {\n ...contentFields,\n ...(parts != null && { parts }),\n };\n }\n\n const errorMessage =\n \"error\" in event && typeof event.error === \"string\"\n ? event.error\n : undefined;\n return {\n ...event,\n ...(content !== undefined && { content }),\n ...(rawId != null && { id: String(rawId) }),\n ...(errorMessage !== undefined &&\n !(\"errorMessage\" in event) &&\n !(\"error_message\" in event) && {\n errorMessage,\n }),\n } as AdkEvent;\n}\n"],"mappings":";;AAGA,MAAM,gBACJ,aACA,OACA,gCAEA,IAAI,MAAM,GAAG,YAAY,cAAc,MAAM,UAAU,YAAY,EAAE;AAEvE,SAAgB,mBACd,OACA,aACU;CACV,IAAI,CAAC,SAAS,KAAK,KAAK,OAAO,KAAK,KAAK,CAAC,CAAC,WAAW,GACpD,MAAM,IAAI,MAAM,GAAG,YAAY,+BAA+B;CAGhE,MAAM,EACJ,IAAI,OACJ,SAAS,YACT,GAAG,UACD;CACJ,IACE,SAAS,QACT,OAAO,UAAU,aAChB,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,IAEpD,MAAM,IAAI,MACR,GAAG,YAAY,8DACjB;CAGF,IAAI;CACJ,IAAI,cAAc,MAAM;EACtB,IAAI,CAAC,SAAS,UAAU,GACtB,MAAM,aAAa,aAAa,WAAW,wBAAwB;EAGrE,MAAM,EAAE,OAAO,GAAG,kBAAkB;EACpC,IAAI,SAAS,SAAS,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,MAAM,QAAQ,IAClE,MAAM,aACJ,aACA,iBACA,kCACF;EAGF,UAAU;GACR,GAAG;GACH,GAAI,SAAS,QAAQ,EAAE,MAAM;EAC/B;CACF;CAEA,MAAM,eACJ,WAAW,SAAS,OAAO,MAAM,UAAU,WACvC,MAAM,QACN,KAAA;CACN,OAAO;EACL,GAAG;EACH,GAAI,YAAY,KAAA,KAAa,EAAE,QAAQ;EACvC,GAAI,SAAS,QAAQ,EAAE,IAAI,OAAO,KAAK,EAAE;EACzC,GAAI,iBAAiB,KAAA,KACnB,EAAE,kBAAkB,UACpB,EAAE,mBAAmB,UAAU,EAC7B,aACF;CACJ;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdkIdentity.d.ts","names":[],"sources":["../src/sdkIdentity.ts"],"mappings":";;cAEa,SAAS"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdkIdentity.js","names":[],"sources":["../src/sdkIdentity.ts"],"sourcesContent":["import type { SdkIdentity } from \"assistant-cloud\";\n\nexport const ADK_SDK: SdkIdentity = {\n name: \"@assistant-ui/react-google-adk\",\n version:\n typeof __AUI_PACKAGE_VERSION__ === \"string\"\n ? __AUI_PACKAGE_VERSION__\n : \"0.0.0\",\n};\n"],"mappings":";AAEA,MAAa,UAAuB;CAClC,MAAM;CACN,SAAA;AAIF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseAdkRequest.d.ts","names":[],"sources":["../../src/server/parseAdkRequest.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"parseAdkRequest.d.ts","names":[],"sources":["../../src/server/parseAdkRequest.ts"],"mappings":";;KAIK;EAEC;EACA;EACA,QAAQ,MAAM;EACd,QAAQ;EACR,aAAa;;EAGb;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR,aAAa;;;;;;;;;;;;;;;;;;;;;;;cA6HN,kBACX,SAAS,YACR,QAAQ;;;;;;;;;;;;;cAsFE,eACX,QAAQ;EACL;EAAc,OAAO,MAAM"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toAdkFunctionResponse } from "../toAdkFunctionResponse.js";
|
|
1
2
|
import { isRecord } from "@assistant-ui/core/internal";
|
|
2
3
|
//#region src/server/parseAdkRequest.ts
|
|
3
4
|
const invalidField = (field, expectation) => /* @__PURE__ */ new Error(`Invalid Google ADK proxy request field "${field}": expected ${expectation}.`);
|
|
@@ -133,7 +134,7 @@ const toAdkContent = (parsed) => {
|
|
|
133
134
|
parts: [{ functionResponse: {
|
|
134
135
|
name: parsed.toolName,
|
|
135
136
|
id: parsed.toolCallId,
|
|
136
|
-
response: parsed.result
|
|
137
|
+
response: toAdkFunctionResponse(parsed.result, parsed.isError)
|
|
137
138
|
} }]
|
|
138
139
|
};
|
|
139
140
|
if (parsed.parts?.length) return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseAdkRequest.js","names":[],"sources":["../../src/server/parseAdkRequest.ts"],"sourcesContent":["import { isRecord } from \"@assistant-ui/core/internal\";\nimport type { AdkSendMessageConfig } from \"../types\";\n\ntype ParsedAdkRequest =\n | {\n type: \"message\";\n text: string;\n parts?: Array<Record<string, unknown>> | undefined;\n config: AdkSendMessageConfig;\n stateDelta?: Record<string, unknown> | undefined;\n }\n | {\n type: \"tool-result\";\n toolCallId: string;\n toolName: string;\n result: unknown;\n isError: boolean;\n config: AdkSendMessageConfig;\n stateDelta?: Record<string, unknown> | undefined;\n };\n\nconst invalidField = (field: string, expectation: string): Error =>\n new Error(\n `Invalid Google ADK proxy request field \"${field}\": expected ${expectation}.`,\n );\n\nconst readString = (body: Record<string, unknown>, field: string): string => {\n const value = body[field];\n if (typeof value !== \"string\") throw invalidField(field, \"a string\");\n return value;\n};\n\nconst readOptionalString = (\n body: Record<string, unknown>,\n field: string,\n): string | undefined => {\n const value = body[field];\n if (value === undefined) return undefined;\n if (typeof value !== \"string\") throw invalidField(field, \"a string\");\n return value;\n};\n\nconst validateOptionalPartString = (\n part: Record<string, unknown>,\n key: string,\n field: string,\n) => {\n if (part[key] !== undefined && typeof part[key] !== \"string\") {\n throw invalidField(field, \"a string\");\n }\n};\n\n// Nested fields are validated only when present: ADK serializes with\n// exclude_none semantics and the Part vocabulary grows upstream, so absent\n// fields and unknown part kinds pass through to the runner untouched.\nconst validatePart = (part: Record<string, unknown>, index: number) => {\n const field = (name: string) => `parts[${index}].${name}`;\n if (Object.keys(part).length === 0) {\n throw invalidField(`parts[${index}]`, \"a non-empty part object\");\n }\n if (part.thought !== undefined && typeof part.thought !== \"boolean\") {\n throw invalidField(field(\"thought\"), \"a boolean\");\n }\n if (part.text !== undefined && typeof part.text !== \"string\") {\n throw invalidField(field(\"text\"), \"a string\");\n }\n\n const validateRecordField = (\n key: string,\n validateContent: (content: Record<string, unknown>) => void,\n ) => {\n const value = part[key];\n if (value === undefined) return;\n if (!isRecord(value)) throw invalidField(field(key), \"an object\");\n validateContent(value);\n };\n\n validateRecordField(\"functionCall\", (content) => {\n validateOptionalPartString(content, \"name\", field(\"functionCall.name\"));\n validateOptionalPartString(content, \"id\", field(\"functionCall.id\"));\n if (content.args !== undefined && !isRecord(content.args)) {\n throw invalidField(field(\"functionCall.args\"), \"an object\");\n }\n });\n validateRecordField(\"functionResponse\", (content) => {\n validateOptionalPartString(content, \"name\", field(\"functionResponse.name\"));\n validateOptionalPartString(content, \"id\", field(\"functionResponse.id\"));\n });\n validateRecordField(\"executableCode\", (content) => {\n validateOptionalPartString(content, \"code\", field(\"executableCode.code\"));\n validateOptionalPartString(\n content,\n \"language\",\n field(\"executableCode.language\"),\n );\n });\n validateRecordField(\"codeExecutionResult\", (content) => {\n validateOptionalPartString(\n content,\n \"output\",\n field(\"codeExecutionResult.output\"),\n );\n validateOptionalPartString(\n content,\n \"outcome\",\n field(\"codeExecutionResult.outcome\"),\n );\n });\n validateRecordField(\"inlineData\", (content) => {\n validateOptionalPartString(\n content,\n \"mimeType\",\n field(\"inlineData.mimeType\"),\n );\n validateOptionalPartString(content, \"data\", field(\"inlineData.data\"));\n });\n validateRecordField(\"fileData\", (content) => {\n validateOptionalPartString(content, \"fileUri\", field(\"fileData.fileUri\"));\n validateOptionalPartString(content, \"mimeType\", field(\"fileData.mimeType\"));\n });\n};\n\n/**\n * Parses an incoming HTTP request into a structured ADK request.\n *\n * Supports two request shapes:\n *\n * 1. User message:\n * ```json\n * { \"message\": \"Hello\", \"runConfig\": {}, \"stateDelta\": {} }\n * ```\n *\n * 2. Tool result:\n * ```json\n * {\n * \"type\": \"tool-result\",\n * \"toolCallId\": \"call_123\",\n * \"toolName\": \"search\",\n * \"result\": { ... },\n * \"isError\": false\n * }\n * ```\n */\nexport const parseAdkRequest = async (\n request: Request,\n): Promise<ParsedAdkRequest> => {\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n throw new Error(\n 'Invalid JSON in Google ADK proxy request body. Expected a JSON object like {\"message\":\"Hello\"} or {\"type\":\"tool-result\",...}.',\n );\n }\n\n if (!isRecord(body)) {\n throw new Error(\"Google ADK proxy request body must be a JSON object\");\n }\n\n const config: AdkSendMessageConfig = {};\n if (body.runConfig !== undefined) config.runConfig = body.runConfig;\n const checkpointId = readOptionalString(body, \"checkpointId\");\n if (checkpointId !== undefined) config.checkpointId = checkpointId;\n\n const stateDelta = body.stateDelta;\n if (stateDelta !== undefined && !isRecord(stateDelta)) {\n throw invalidField(\"stateDelta\", \"an object\");\n }\n\n if (body.type === \"tool-result\") {\n if (!(\"result\" in body)) {\n throw invalidField(\"result\", \"a value\");\n }\n if (body.isError !== undefined && typeof body.isError !== \"boolean\") {\n throw invalidField(\"isError\", \"a boolean\");\n }\n return {\n type: \"tool-result\",\n toolCallId: readString(body, \"toolCallId\"),\n toolName: readString(body, \"toolName\"),\n result: body.result,\n isError: body.isError ?? false,\n config,\n ...(stateDelta != null && { stateDelta }),\n };\n }\n\n if (body.type !== undefined && body.type !== \"message\") {\n throw invalidField(\"type\", '\"message\", \"tool-result\", or omitted');\n }\n\n const text = body.message;\n if (text !== undefined && typeof text !== \"string\") {\n throw invalidField(\"message\", \"a string\");\n }\n\n const parts = body.parts;\n if (\n parts !== undefined &&\n (!Array.isArray(parts) || !parts.every(isRecord))\n ) {\n throw invalidField(\"parts\", \"an array of objects\");\n }\n parts?.forEach(validatePart);\n if (!(\"message\" in body) && !(\"parts\" in body)) {\n throw new Error(\n 'Invalid Google ADK proxy request: expected a \"message\" string or a \"parts\" array.',\n );\n }\n\n return {\n type: \"message\",\n text: text ?? \"\",\n ...(parts !== undefined && { parts }),\n config,\n ...(stateDelta != null && { stateDelta }),\n };\n};\n\n/**\n * Converts a parsed ADK request into a Google GenAI Content object\n * suitable for `Runner.runAsync({ newMessage })`.\n *\n * @example\n * ```ts\n * const parsed = await parseAdkRequest(req);\n * const newMessage = toAdkContent(parsed);\n * const events = runner.runAsync({ userId, sessionId, newMessage, stateDelta: parsed.stateDelta });\n * return adkEventStream(events);\n * ```\n */\nexport const toAdkContent = (\n parsed: ParsedAdkRequest,\n): { role: string; parts: Array<Record<string, unknown>> } => {\n if (parsed.type === \"tool-result\") {\n return {\n role: \"user\",\n parts: [\n {\n functionResponse: {\n name: parsed.toolName,\n id: parsed.toolCallId,\n response: parsed.result,\n },\n },\n ],\n };\n }\n\n // If raw parts are provided (multimodal), use them directly\n if (parsed.parts?.length) {\n return { role: \"user\", parts: parsed.parts };\n }\n\n return { role: \"user\", parts: [{ text: parsed.text }] };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parseAdkRequest.js","names":[],"sources":["../../src/server/parseAdkRequest.ts"],"sourcesContent":["import { isRecord } from \"@assistant-ui/core/internal\";\nimport type { AdkSendMessageConfig } from \"../types\";\nimport { toAdkFunctionResponse } from \"../toAdkFunctionResponse\";\n\ntype ParsedAdkRequest =\n | {\n type: \"message\";\n text: string;\n parts?: Array<Record<string, unknown>> | undefined;\n config: AdkSendMessageConfig;\n stateDelta?: Record<string, unknown> | undefined;\n }\n | {\n type: \"tool-result\";\n toolCallId: string;\n toolName: string;\n result: unknown;\n isError: boolean;\n config: AdkSendMessageConfig;\n stateDelta?: Record<string, unknown> | undefined;\n };\n\nconst invalidField = (field: string, expectation: string): Error =>\n new Error(\n `Invalid Google ADK proxy request field \"${field}\": expected ${expectation}.`,\n );\n\nconst readString = (body: Record<string, unknown>, field: string): string => {\n const value = body[field];\n if (typeof value !== \"string\") throw invalidField(field, \"a string\");\n return value;\n};\n\nconst readOptionalString = (\n body: Record<string, unknown>,\n field: string,\n): string | undefined => {\n const value = body[field];\n if (value === undefined) return undefined;\n if (typeof value !== \"string\") throw invalidField(field, \"a string\");\n return value;\n};\n\nconst validateOptionalPartString = (\n part: Record<string, unknown>,\n key: string,\n field: string,\n) => {\n if (part[key] !== undefined && typeof part[key] !== \"string\") {\n throw invalidField(field, \"a string\");\n }\n};\n\n// Nested fields are validated only when present: ADK serializes with\n// exclude_none semantics and the Part vocabulary grows upstream, so absent\n// fields and unknown part kinds pass through to the runner untouched.\nconst validatePart = (part: Record<string, unknown>, index: number) => {\n const field = (name: string) => `parts[${index}].${name}`;\n if (Object.keys(part).length === 0) {\n throw invalidField(`parts[${index}]`, \"a non-empty part object\");\n }\n if (part.thought !== undefined && typeof part.thought !== \"boolean\") {\n throw invalidField(field(\"thought\"), \"a boolean\");\n }\n if (part.text !== undefined && typeof part.text !== \"string\") {\n throw invalidField(field(\"text\"), \"a string\");\n }\n\n const validateRecordField = (\n key: string,\n validateContent: (content: Record<string, unknown>) => void,\n ) => {\n const value = part[key];\n if (value === undefined) return;\n if (!isRecord(value)) throw invalidField(field(key), \"an object\");\n validateContent(value);\n };\n\n validateRecordField(\"functionCall\", (content) => {\n validateOptionalPartString(content, \"name\", field(\"functionCall.name\"));\n validateOptionalPartString(content, \"id\", field(\"functionCall.id\"));\n if (content.args !== undefined && !isRecord(content.args)) {\n throw invalidField(field(\"functionCall.args\"), \"an object\");\n }\n });\n validateRecordField(\"functionResponse\", (content) => {\n validateOptionalPartString(content, \"name\", field(\"functionResponse.name\"));\n validateOptionalPartString(content, \"id\", field(\"functionResponse.id\"));\n });\n validateRecordField(\"executableCode\", (content) => {\n validateOptionalPartString(content, \"code\", field(\"executableCode.code\"));\n validateOptionalPartString(\n content,\n \"language\",\n field(\"executableCode.language\"),\n );\n });\n validateRecordField(\"codeExecutionResult\", (content) => {\n validateOptionalPartString(\n content,\n \"output\",\n field(\"codeExecutionResult.output\"),\n );\n validateOptionalPartString(\n content,\n \"outcome\",\n field(\"codeExecutionResult.outcome\"),\n );\n });\n validateRecordField(\"inlineData\", (content) => {\n validateOptionalPartString(\n content,\n \"mimeType\",\n field(\"inlineData.mimeType\"),\n );\n validateOptionalPartString(content, \"data\", field(\"inlineData.data\"));\n });\n validateRecordField(\"fileData\", (content) => {\n validateOptionalPartString(content, \"fileUri\", field(\"fileData.fileUri\"));\n validateOptionalPartString(content, \"mimeType\", field(\"fileData.mimeType\"));\n });\n};\n\n/**\n * Parses an incoming HTTP request into a structured ADK request.\n *\n * Supports two request shapes:\n *\n * 1. User message:\n * ```json\n * { \"message\": \"Hello\", \"runConfig\": {}, \"stateDelta\": {} }\n * ```\n *\n * 2. Tool result:\n * ```json\n * {\n * \"type\": \"tool-result\",\n * \"toolCallId\": \"call_123\",\n * \"toolName\": \"search\",\n * \"result\": { ... },\n * \"isError\": false\n * }\n * ```\n */\nexport const parseAdkRequest = async (\n request: Request,\n): Promise<ParsedAdkRequest> => {\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n throw new Error(\n 'Invalid JSON in Google ADK proxy request body. Expected a JSON object like {\"message\":\"Hello\"} or {\"type\":\"tool-result\",...}.',\n );\n }\n\n if (!isRecord(body)) {\n throw new Error(\"Google ADK proxy request body must be a JSON object\");\n }\n\n const config: AdkSendMessageConfig = {};\n if (body.runConfig !== undefined) config.runConfig = body.runConfig;\n const checkpointId = readOptionalString(body, \"checkpointId\");\n if (checkpointId !== undefined) config.checkpointId = checkpointId;\n\n const stateDelta = body.stateDelta;\n if (stateDelta !== undefined && !isRecord(stateDelta)) {\n throw invalidField(\"stateDelta\", \"an object\");\n }\n\n if (body.type === \"tool-result\") {\n if (!(\"result\" in body)) {\n throw invalidField(\"result\", \"a value\");\n }\n if (body.isError !== undefined && typeof body.isError !== \"boolean\") {\n throw invalidField(\"isError\", \"a boolean\");\n }\n return {\n type: \"tool-result\",\n toolCallId: readString(body, \"toolCallId\"),\n toolName: readString(body, \"toolName\"),\n result: body.result,\n isError: body.isError ?? false,\n config,\n ...(stateDelta != null && { stateDelta }),\n };\n }\n\n if (body.type !== undefined && body.type !== \"message\") {\n throw invalidField(\"type\", '\"message\", \"tool-result\", or omitted');\n }\n\n const text = body.message;\n if (text !== undefined && typeof text !== \"string\") {\n throw invalidField(\"message\", \"a string\");\n }\n\n const parts = body.parts;\n if (\n parts !== undefined &&\n (!Array.isArray(parts) || !parts.every(isRecord))\n ) {\n throw invalidField(\"parts\", \"an array of objects\");\n }\n parts?.forEach(validatePart);\n if (!(\"message\" in body) && !(\"parts\" in body)) {\n throw new Error(\n 'Invalid Google ADK proxy request: expected a \"message\" string or a \"parts\" array.',\n );\n }\n\n return {\n type: \"message\",\n text: text ?? \"\",\n ...(parts !== undefined && { parts }),\n config,\n ...(stateDelta != null && { stateDelta }),\n };\n};\n\n/**\n * Converts a parsed ADK request into a Google GenAI Content object\n * suitable for `Runner.runAsync({ newMessage })`.\n *\n * @example\n * ```ts\n * const parsed = await parseAdkRequest(req);\n * const newMessage = toAdkContent(parsed);\n * const events = runner.runAsync({ userId, sessionId, newMessage, stateDelta: parsed.stateDelta });\n * return adkEventStream(events);\n * ```\n */\nexport const toAdkContent = (\n parsed: ParsedAdkRequest,\n): { role: string; parts: Array<Record<string, unknown>> } => {\n if (parsed.type === \"tool-result\") {\n return {\n role: \"user\",\n parts: [\n {\n functionResponse: {\n name: parsed.toolName,\n id: parsed.toolCallId,\n response: toAdkFunctionResponse(parsed.result, parsed.isError),\n },\n },\n ],\n };\n }\n\n // If raw parts are provided (multimodal), use them directly\n if (parsed.parts?.length) {\n return { role: \"user\", parts: parsed.parts };\n }\n\n return { role: \"user\", parts: [{ text: parsed.text }] };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/toAdkFunctionResponse.d.ts
|
|
2
|
+
declare const isAdkFunctionError: (response: unknown) => boolean;
|
|
3
|
+
declare const toAdkFunctionResponse: (result: unknown, isError?: boolean) => Record<string, unknown>;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { isAdkFunctionError, toAdkFunctionResponse };
|
|
6
|
+
//# sourceMappingURL=toAdkFunctionResponse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toAdkFunctionResponse.d.ts","names":[],"sources":["../src/toAdkFunctionResponse.ts"],"mappings":";cAEa,qBAAsB;cAKtB,wBACX,iBACA,sBACC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { isRecord } from "@assistant-ui/core/internal";
|
|
2
|
+
//#region src/toAdkFunctionResponse.ts
|
|
3
|
+
const isAdkFunctionError = (response) => isRecord(response) && Object.hasOwn(response, "error") && response.error !== void 0;
|
|
4
|
+
const toAdkFunctionResponse = (result, isError = false) => {
|
|
5
|
+
if (isError && !isAdkFunctionError(result)) return { error: result ?? null };
|
|
6
|
+
return Array.isArray(result) ? { results: result } : isRecord(result) ? result : { result };
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
export { isAdkFunctionError, toAdkFunctionResponse };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=toAdkFunctionResponse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toAdkFunctionResponse.js","names":[],"sources":["../src/toAdkFunctionResponse.ts"],"sourcesContent":["import { isRecord } from \"@assistant-ui/core/internal\";\n\nexport const isAdkFunctionError = (response: unknown): boolean =>\n isRecord(response) &&\n Object.hasOwn(response, \"error\") &&\n response.error !== undefined;\n\nexport const toAdkFunctionResponse = (\n result: unknown,\n isError = false,\n): Record<string, unknown> => {\n if (isError && !isAdkFunctionError(result)) return { error: result ?? null };\n return Array.isArray(result)\n ? { results: result }\n : isRecord(result)\n ? result\n : { result };\n};\n"],"mappings":";;AAEA,MAAa,sBAAsB,aACjC,SAAS,QAAQ,KACjB,OAAO,OAAO,UAAU,OAAO,KAC/B,SAAS,UAAU,KAAA;AAErB,MAAa,yBACX,QACA,UAAU,UACkB;CAC5B,IAAI,WAAW,CAAC,mBAAmB,MAAM,GAAG,OAAO,EAAE,OAAO,UAAU,KAAK;CAC3E,OAAO,MAAM,QAAQ,MAAM,IACvB,EAAE,SAAS,OAAO,IAClB,SAAS,MAAM,IACb,SACA,EAAE,OAAO;AACjB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAdkMessages.d.ts","names":[],"sources":["../src/useAdkMessages.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"useAdkMessages.d.ts","names":[],"sources":["../src/useAdkMessages.ts"],"mappings":";;KAgCY;EACV,QAAQ;EACR;IACE,UAAU;IACV,gBAAgB;IAChB,kBAAkB;;;cAcT,mBAAkB,QAAA,iBAG5B;;;;IAIQ;IACE;;;;;;;;EAsFW,cAAA,aAAA,cAAY,QAAU,yBAAoB;;EAvDhB,cAAA,MAAA;EAmCvC,kBAAA,MAAA;EAnBI,gBAAA,UAAA"}
|
package/dist/useAdkMessages.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { contentToParts } from "./contentToParts.js";
|
|
2
|
+
import { toAdkFunctionResponse } from "./toAdkFunctionResponse.js";
|
|
2
3
|
import { AdkEventAccumulator } from "./AdkEventAccumulator.js";
|
|
4
|
+
import { abortableIterable, invokeUserCallback, openAbortableIterable } from "@assistant-ui/core/internal";
|
|
3
5
|
import { generateId } from "@assistant-ui/core";
|
|
4
|
-
import { invokeUserCallback } from "@assistant-ui/core/internal";
|
|
5
6
|
import { useAui } from "@assistant-ui/store";
|
|
6
7
|
import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState } from "react";
|
|
7
8
|
//#region src/useAdkMessages.ts
|
|
@@ -12,7 +13,7 @@ const useAdkMessages = ({ stream, eventHandlers }) => {
|
|
|
12
13
|
const [messages, _setMessages] = useState([]);
|
|
13
14
|
const [stateDelta, setStateDelta] = useState({});
|
|
14
15
|
const [agentInfo, setAgentInfo] = useState({});
|
|
15
|
-
const [longRunningToolIds,
|
|
16
|
+
const [longRunningToolIds, _setLongRunningToolIds] = useState([]);
|
|
16
17
|
const [artifactDelta, setArtifactDelta] = useState({});
|
|
17
18
|
const [toolConfirmations, setToolConfirmations] = useState([]);
|
|
18
19
|
const [authRequests, setAuthRequests] = useState([]);
|
|
@@ -20,6 +21,7 @@ const useAdkMessages = ({ stream, eventHandlers }) => {
|
|
|
20
21
|
const [messageMetadata, setMessageMetadata] = useState(/* @__PURE__ */ new Map());
|
|
21
22
|
const lastTransferToAgentRef = useRef(void 0);
|
|
22
23
|
const messagesRef = useRef(messages);
|
|
24
|
+
const longRunningToolIdsRef = useRef(longRunningToolIds);
|
|
23
25
|
const stateDeltaRef = useRef(stateDelta);
|
|
24
26
|
useInsertionEffect(() => {
|
|
25
27
|
stateDeltaRef.current = stateDelta;
|
|
@@ -36,6 +38,10 @@ const useAdkMessages = ({ stream, eventHandlers }) => {
|
|
|
36
38
|
messagesRef.current = msgs;
|
|
37
39
|
_setMessages(msgs);
|
|
38
40
|
}, []);
|
|
41
|
+
const setLongRunningToolIds = useCallback((ids) => {
|
|
42
|
+
longRunningToolIdsRef.current = ids;
|
|
43
|
+
_setLongRunningToolIds(ids);
|
|
44
|
+
}, []);
|
|
39
45
|
/**
|
|
40
46
|
* Swap the thread over to a loaded snapshot in one commit. Unlike
|
|
41
47
|
* {@link replaceMessages} this never passes through a cleared state, so a
|
|
@@ -52,7 +58,7 @@ const useAdkMessages = ({ stream, eventHandlers }) => {
|
|
|
52
58
|
setStateDelta(snapshot.stateDelta ?? {});
|
|
53
59
|
setArtifactDelta(snapshot.artifactDelta ?? {});
|
|
54
60
|
setAgentInfo(snapshot.agentInfo ?? {});
|
|
55
|
-
}, [setMessagesImmediate]);
|
|
61
|
+
}, [setLongRunningToolIds, setMessagesImmediate]);
|
|
56
62
|
const replaceMessages = useCallback((msgs) => {
|
|
57
63
|
setMessagesImmediate(msgs);
|
|
58
64
|
setLongRunningToolIds([]);
|
|
@@ -60,7 +66,7 @@ const useAdkMessages = ({ stream, eventHandlers }) => {
|
|
|
60
66
|
setAuthRequests([]);
|
|
61
67
|
setEscalated(false);
|
|
62
68
|
setMessageMetadata(/* @__PURE__ */ new Map());
|
|
63
|
-
}, [setMessagesImmediate]);
|
|
69
|
+
}, [setLongRunningToolIds, setMessagesImmediate]);
|
|
64
70
|
const abortControllerRef = useRef(null);
|
|
65
71
|
const { onError, onCustomEvent, onAgentTransfer } = useMemo(() => eventHandlers ?? {}, [eventHandlers]);
|
|
66
72
|
const aui = useAui();
|
|
@@ -70,20 +76,25 @@ const useAdkMessages = ({ stream, eventHandlers }) => {
|
|
|
70
76
|
id: generateId()
|
|
71
77
|
});
|
|
72
78
|
const resentIds = new Set(newMessagesWithId.map((m) => m.id));
|
|
73
|
-
const
|
|
79
|
+
const answeredToolCallIds = new Set(newMessagesWithId.flatMap((m) => m.type === "tool" ? [m.tool_call_id] : []));
|
|
80
|
+
const accumulator = new AdkEventAccumulator(messagesRef.current.filter((m) => !resentIds.has(m.id)), longRunningToolIdsRef.current.filter((id) => !answeredToolCallIds.has(id)));
|
|
74
81
|
for (const event of messagesToEvents(newMessagesWithId)) accumulator.processEvent(event);
|
|
75
82
|
setMessagesImmediate(accumulator.getMessages());
|
|
83
|
+
setLongRunningToolIds(accumulator.getLongRunningToolIds());
|
|
84
|
+
abortControllerRef.current?.abort();
|
|
76
85
|
const abortController = new AbortController();
|
|
77
86
|
abortControllerRef.current = abortController;
|
|
78
87
|
try {
|
|
79
|
-
const response = await stream(newMessagesWithId, {
|
|
88
|
+
const response = await openAbortableIterable(stream(newMessagesWithId, {
|
|
80
89
|
...config,
|
|
81
90
|
abortSignal: abortController.signal,
|
|
82
91
|
initialize: async () => {
|
|
83
92
|
return await aui.threadListItem.initialize();
|
|
84
93
|
}
|
|
85
|
-
});
|
|
86
|
-
|
|
94
|
+
}), abortController.signal);
|
|
95
|
+
if (!response) return;
|
|
96
|
+
for await (const event of abortableIterable(response, abortController.signal)) {
|
|
97
|
+
if (abortController.signal.aborted || abortControllerRef.current !== abortController) break;
|
|
87
98
|
const updatedMessages = accumulator.processEvent(event);
|
|
88
99
|
setMessagesImmediate(updatedMessages);
|
|
89
100
|
setStateDelta({
|
|
@@ -112,13 +123,14 @@ const useAdkMessages = ({ stream, eventHandlers }) => {
|
|
|
112
123
|
if (event.errorCode || event.errorMessage) invokeAdkRuntimeCallback("onError", onError, event.errorMessage ?? event.errorCode);
|
|
113
124
|
}
|
|
114
125
|
} catch (error) {
|
|
115
|
-
if (!abortController.signal.aborted && !(error instanceof Error && error.name === "AbortError")) throw error;
|
|
126
|
+
if (!abortController.signal.aborted && abortControllerRef.current === abortController && !(error instanceof Error && error.name === "AbortError")) throw error;
|
|
116
127
|
} finally {
|
|
117
128
|
if (abortControllerRef.current === abortController) abortControllerRef.current = null;
|
|
118
129
|
}
|
|
119
130
|
}, [
|
|
120
131
|
aui,
|
|
121
132
|
setMessagesImmediate,
|
|
133
|
+
setLongRunningToolIds,
|
|
122
134
|
stream,
|
|
123
135
|
onError,
|
|
124
136
|
onCustomEvent,
|
|
@@ -205,7 +217,7 @@ const messageToEvent = (msg) => {
|
|
|
205
217
|
parts: [{ functionResponse: {
|
|
206
218
|
name: msg.name,
|
|
207
219
|
id: msg.tool_call_id,
|
|
208
|
-
response
|
|
220
|
+
response: toAdkFunctionResponse(response, msg.status === "error")
|
|
209
221
|
} }]
|
|
210
222
|
}
|
|
211
223
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAdkMessages.js","names":[],"sources":["../src/useAdkMessages.ts"],"sourcesContent":["import {\n useState,\n useCallback,\n useEffect,\n useInsertionEffect,\n useRef,\n useMemo,\n} from \"react\";\nimport { generateId } from \"@assistant-ui/core\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { invokeUserCallback } from \"@assistant-ui/core/internal\";\nimport { AdkEventAccumulator } from \"./AdkEventAccumulator\";\nimport { contentToParts } from \"./contentToParts\";\nimport type {\n AdkEvent,\n AdkMessage,\n AdkMessageMetadata,\n AdkSendMessageConfig,\n AdkStreamCallback,\n AdkToolConfirmation,\n AdkAuthRequest,\n AdkThreadSnapshot,\n OnAdkErrorCallback,\n OnAdkCustomEventCallback,\n OnAdkAgentTransferCallback,\n} from \"./types\";\n\nexport type UseAdkMessagesOptions = {\n stream: AdkStreamCallback;\n eventHandlers?: {\n onError?: OnAdkErrorCallback;\n onCustomEvent?: OnAdkCustomEventCallback;\n onAgentTransfer?: OnAdkAgentTransferCallback;\n };\n};\n\ntype AdkRuntimeCallbackName = \"onError\" | \"onCustomEvent\" | \"onAgentTransfer\";\n\nconst invokeAdkRuntimeCallback = <TArgs extends readonly unknown[]>(\n name: AdkRuntimeCallbackName,\n callback: ((...args: TArgs) => unknown) | undefined,\n ...args: TArgs\n): void => {\n void invokeUserCallback(\"react-google-adk\", name, callback, ...args);\n};\n\nexport const useAdkMessages = ({\n stream,\n eventHandlers,\n}: UseAdkMessagesOptions) => {\n const [messages, _setMessages] = useState<AdkMessage[]>([]);\n const [stateDelta, setStateDelta] = useState<Record<string, unknown>>({});\n const [agentInfo, setAgentInfo] = useState<{\n name?: string | undefined;\n branch?: string | undefined;\n }>({});\n const [longRunningToolIds, setLongRunningToolIds] = useState<string[]>([]);\n const [artifactDelta, setArtifactDelta] = useState<Record<string, number>>(\n {},\n );\n const [toolConfirmations, setToolConfirmations] = useState<\n AdkToolConfirmation[]\n >([]);\n const [authRequests, setAuthRequests] = useState<AdkAuthRequest[]>([]);\n const [escalated, setEscalated] = useState(false);\n const [messageMetadata, setMessageMetadata] = useState<\n Map<string, AdkMessageMetadata>\n >(new Map());\n const lastTransferToAgentRef = useRef<string | undefined>(undefined);\n // setMessagesImmediate is the only writer of the messages state and publishes\n // this ref with it, so the ref never trails a commit.\n const messagesRef = useRef(messages);\n const stateDeltaRef = useRef(stateDelta);\n useInsertionEffect(() => {\n stateDeltaRef.current = stateDelta;\n }, [stateDelta]);\n const artifactDeltaRef = useRef(artifactDelta);\n useInsertionEffect(() => {\n artifactDeltaRef.current = artifactDelta;\n }, [artifactDelta]);\n const messageMetadataRef = useRef(messageMetadata);\n useInsertionEffect(() => {\n messageMetadataRef.current = messageMetadata;\n }, [messageMetadata]);\n\n const setMessagesImmediate = useCallback((msgs: AdkMessage[]) => {\n messagesRef.current = msgs;\n _setMessages(msgs);\n }, []);\n\n /**\n * Swap the thread over to a loaded snapshot in one commit. Unlike\n * {@link replaceMessages} this never passes through a cleared state, so a\n * refetch that lands while a confirmation is on screen replaces it rather\n * than blanking it first.\n */\n const applySnapshot = useCallback(\n (snapshot: AdkThreadSnapshot) => {\n setMessagesImmediate(snapshot.messages);\n setLongRunningToolIds(snapshot.longRunningToolIds ?? []);\n setToolConfirmations(snapshot.toolConfirmations ?? []);\n setAuthRequests(snapshot.authRequests ?? []);\n setEscalated(snapshot.escalated ?? false);\n setMessageMetadata(snapshot.messageMetadata ?? new Map());\n setStateDelta(snapshot.stateDelta ?? {});\n setArtifactDelta(snapshot.artifactDelta ?? {});\n setAgentInfo(snapshot.agentInfo ?? {});\n },\n [setMessagesImmediate],\n );\n\n // Replace the message list AND reset derived per-turn HITL state.\n // Used by truncation paths (edit, reload) so that stale interrupt\n // markers and per-message metadata from the removed messages don't leak\n // into the next turn.\n const replaceMessages = useCallback(\n (msgs: AdkMessage[]) => {\n setMessagesImmediate(msgs);\n setLongRunningToolIds([]);\n setToolConfirmations([]);\n setAuthRequests([]);\n setEscalated(false);\n setMessageMetadata(new Map());\n },\n [setMessagesImmediate],\n );\n\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const { onError, onCustomEvent, onAgentTransfer } = useMemo(\n () => eventHandlers ?? {},\n [eventHandlers],\n );\n\n const aui = useAui();\n const sendMessage = useCallback(\n async (newMessages: AdkMessage[], config: AdkSendMessageConfig) => {\n const newMessagesWithId = newMessages.map((m) =>\n m.id ? m : { ...m, id: generateId() },\n ) as AdkMessage[];\n\n // A staged message is already in the thread under its own id, and the\n // merged event below re-emits the whole batch under the first one. Seeding\n // with the originals would leave every later staged id beside the merged\n // copy of itself.\n const resentIds = new Set(newMessagesWithId.map((m) => m.id));\n const accumulator = new AdkEventAccumulator(\n messagesRef.current.filter((m) => !resentIds.has(m.id)),\n );\n for (const event of messagesToEvents(newMessagesWithId)) {\n accumulator.processEvent(event);\n }\n setMessagesImmediate(accumulator.getMessages());\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n try {\n const response = await stream(newMessagesWithId, {\n ...config,\n abortSignal: abortController.signal,\n initialize: async () => {\n return await aui.threadListItem.initialize();\n },\n });\n\n for await (const event of response) {\n const updatedMessages = accumulator.processEvent(event);\n setMessagesImmediate(updatedMessages);\n setStateDelta({\n ...stateDeltaRef.current,\n ...accumulator.getStateDelta(),\n });\n setAgentInfo(accumulator.getAgentInfo());\n setLongRunningToolIds(accumulator.getLongRunningToolIds());\n setArtifactDelta({\n ...artifactDeltaRef.current,\n ...accumulator.getArtifactDelta(),\n });\n setToolConfirmations(accumulator.getToolConfirmations());\n setAuthRequests(accumulator.getAuthRequests());\n setEscalated(accumulator.isEscalated());\n {\n const newMeta = accumulator.getMessageMetadata();\n if (newMeta.size > 0) {\n setMessageMetadata(\n new Map([...messageMetadataRef.current, ...newMeta]),\n );\n }\n }\n\n const transfer = accumulator.getLastTransferToAgent();\n if (transfer && transfer !== lastTransferToAgentRef.current) {\n lastTransferToAgentRef.current = transfer;\n invokeAdkRuntimeCallback(\n \"onAgentTransfer\",\n onAgentTransfer,\n transfer,\n );\n }\n\n // Fire custom event callback for events with customMetadata\n if (event.customMetadata && onCustomEvent) {\n for (const [key, value] of Object.entries(event.customMetadata)) {\n invokeAdkRuntimeCallback(\n \"onCustomEvent\",\n onCustomEvent,\n key,\n value,\n );\n }\n }\n\n if (event.errorCode || event.errorMessage) {\n invokeAdkRuntimeCallback(\n \"onError\",\n onError,\n event.errorMessage ?? event.errorCode,\n );\n }\n }\n } catch (error) {\n if (\n !abortController.signal.aborted &&\n !(error instanceof Error && error.name === \"AbortError\")\n ) {\n throw error;\n }\n } finally {\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n }\n },\n [\n aui,\n setMessagesImmediate,\n stream,\n onError,\n onCustomEvent,\n onAgentTransfer,\n ],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, []);\n\n useEffect(() => cancel, [cancel]);\n\n return {\n messages,\n stateDelta,\n agentInfo,\n longRunningToolIds,\n artifactDelta,\n toolConfirmations,\n authRequests,\n escalated,\n messageMetadata,\n sendMessage,\n cancel,\n setMessages: setMessagesImmediate,\n replaceMessages,\n applySnapshot,\n };\n};\n\n/**\n * Transport sends every human and tool message of one `send` call as a single\n * ADK `Content`, and ADK parses that event's function responses before running\n * any tool, so the batch runs whole or not at all. The optimistic projection\n * has to sit on the same boundary, so a run of those messages becomes one\n * synthetic event whose parts come from the same per-message conversion.\n *\n * The transport drops `ai` messages from that `Content`, so one interleaved\n * between two replies does not split the batch on the wire and must not split\n * it here either. It still becomes its own event, placed after the merged one,\n * so the optimistic projection keeps the assistant turn.\n *\n * @internal — exported for unit tests.\n */\nexport const messagesToEvents = (messages: AdkMessage[]): AdkEvent[] => {\n // A reload sends no messages at all, and the empty user content the transport\n // puts on the wire for it is not part of the optimistic view: projecting one\n // would put an empty user bubble above every regenerated turn.\n if (messages.length === 0) return [];\n\n const events: AdkEvent[] = [];\n const run: AdkMessage[] = [];\n let runIndex = 0;\n\n for (const msg of messages) {\n if (msg.type === \"ai\") {\n events.push(messageToEvent(msg));\n } else {\n if (run.length === 0) runIndex = events.length;\n run.push(msg);\n }\n }\n\n const parts = run.flatMap((m) => messageToEvent(m).content?.parts ?? []);\n const human = run.find((m) => m.type === \"human\");\n\n // A batch that contributes no part still reaches the wire: the transport\n // sends an empty user `Content`, which a reload replays as an empty human\n // message. Emitting it here keeps the optimistic view equal to that replay.\n if (parts.length === 0) parts.push({ text: \"\" });\n\n const event: AdkEvent = { id: (human ?? run[0])?.id ?? generateId() };\n if (human || run.length === 0) event.author = \"user\";\n event.content = { role: \"user\", parts };\n events.splice(run.length > 0 ? runIndex : events.length, 0, event);\n\n return events;\n};\n\n/** @internal — exported for unit tests. */\nexport const messageToEvent = (msg: AdkMessage): AdkEvent => {\n if (msg.type === \"human\") {\n return {\n id: msg.id ?? generateId(),\n author: \"user\",\n content: { role: \"user\", parts: contentToParts(msg.content) },\n };\n }\n\n if (msg.type === \"tool\") {\n let response: unknown;\n try {\n response = JSON.parse(msg.content);\n } catch {\n response = msg.content;\n }\n return {\n id: msg.id ?? generateId(),\n content: {\n role: \"user\",\n parts: [\n {\n functionResponse: {\n name: msg.name,\n id: msg.tool_call_id,\n response,\n },\n },\n ],\n },\n };\n }\n\n const result: AdkEvent = { id: msg.id ?? generateId() };\n if (msg.author != null) result.author = msg.author;\n result.content = {\n role: \"model\",\n parts: [\n ...contentToParts(msg.content),\n ...(msg.tool_calls?.map((tc) => ({\n functionCall: { name: tc.name, id: tc.id, args: { ...tc.args } },\n })) ?? []),\n ],\n };\n return result;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useAdkMessages.js","names":[],"sources":["../src/useAdkMessages.ts"],"sourcesContent":["import {\n useState,\n useCallback,\n useEffect,\n useInsertionEffect,\n useRef,\n useMemo,\n} from \"react\";\nimport { generateId } from \"@assistant-ui/core\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n abortableIterable,\n invokeUserCallback,\n openAbortableIterable,\n} from \"@assistant-ui/core/internal\";\nimport { AdkEventAccumulator } from \"./AdkEventAccumulator\";\nimport { contentToParts } from \"./contentToParts\";\nimport { toAdkFunctionResponse } from \"./toAdkFunctionResponse\";\nimport type {\n AdkEvent,\n AdkMessage,\n AdkMessageMetadata,\n AdkSendMessageConfig,\n AdkStreamCallback,\n AdkToolConfirmation,\n AdkAuthRequest,\n AdkThreadSnapshot,\n OnAdkErrorCallback,\n OnAdkCustomEventCallback,\n OnAdkAgentTransferCallback,\n} from \"./types\";\n\nexport type UseAdkMessagesOptions = {\n stream: AdkStreamCallback;\n eventHandlers?: {\n onError?: OnAdkErrorCallback;\n onCustomEvent?: OnAdkCustomEventCallback;\n onAgentTransfer?: OnAdkAgentTransferCallback;\n };\n};\n\ntype AdkRuntimeCallbackName = \"onError\" | \"onCustomEvent\" | \"onAgentTransfer\";\n\nconst invokeAdkRuntimeCallback = <TArgs extends readonly unknown[]>(\n name: AdkRuntimeCallbackName,\n callback: ((...args: TArgs) => unknown) | undefined,\n ...args: TArgs\n): void => {\n void invokeUserCallback(\"react-google-adk\", name, callback, ...args);\n};\n\nexport const useAdkMessages = ({\n stream,\n eventHandlers,\n}: UseAdkMessagesOptions) => {\n const [messages, _setMessages] = useState<AdkMessage[]>([]);\n const [stateDelta, setStateDelta] = useState<Record<string, unknown>>({});\n const [agentInfo, setAgentInfo] = useState<{\n name?: string | undefined;\n branch?: string | undefined;\n }>({});\n const [longRunningToolIds, _setLongRunningToolIds] = useState<string[]>([]);\n const [artifactDelta, setArtifactDelta] = useState<Record<string, number>>(\n {},\n );\n const [toolConfirmations, setToolConfirmations] = useState<\n AdkToolConfirmation[]\n >([]);\n const [authRequests, setAuthRequests] = useState<AdkAuthRequest[]>([]);\n const [escalated, setEscalated] = useState(false);\n const [messageMetadata, setMessageMetadata] = useState<\n Map<string, AdkMessageMetadata>\n >(new Map());\n const lastTransferToAgentRef = useRef<string | undefined>(undefined);\n // setMessagesImmediate and setLongRunningToolIds are the only writers of their state and publish these refs with it, so neither ref trails a commit.\n const messagesRef = useRef(messages);\n const longRunningToolIdsRef = useRef(longRunningToolIds);\n const stateDeltaRef = useRef(stateDelta);\n useInsertionEffect(() => {\n stateDeltaRef.current = stateDelta;\n }, [stateDelta]);\n const artifactDeltaRef = useRef(artifactDelta);\n useInsertionEffect(() => {\n artifactDeltaRef.current = artifactDelta;\n }, [artifactDelta]);\n const messageMetadataRef = useRef(messageMetadata);\n useInsertionEffect(() => {\n messageMetadataRef.current = messageMetadata;\n }, [messageMetadata]);\n\n const setMessagesImmediate = useCallback((msgs: AdkMessage[]) => {\n messagesRef.current = msgs;\n _setMessages(msgs);\n }, []);\n const setLongRunningToolIds = useCallback((ids: string[]) => {\n longRunningToolIdsRef.current = ids;\n _setLongRunningToolIds(ids);\n }, []);\n\n /**\n * Swap the thread over to a loaded snapshot in one commit. Unlike\n * {@link replaceMessages} this never passes through a cleared state, so a\n * refetch that lands while a confirmation is on screen replaces it rather\n * than blanking it first.\n */\n const applySnapshot = useCallback(\n (snapshot: AdkThreadSnapshot) => {\n setMessagesImmediate(snapshot.messages);\n setLongRunningToolIds(snapshot.longRunningToolIds ?? []);\n setToolConfirmations(snapshot.toolConfirmations ?? []);\n setAuthRequests(snapshot.authRequests ?? []);\n setEscalated(snapshot.escalated ?? false);\n setMessageMetadata(snapshot.messageMetadata ?? new Map());\n setStateDelta(snapshot.stateDelta ?? {});\n setArtifactDelta(snapshot.artifactDelta ?? {});\n setAgentInfo(snapshot.agentInfo ?? {});\n },\n [setLongRunningToolIds, setMessagesImmediate],\n );\n\n // Replace the message list AND reset derived per-turn HITL state.\n // Used by truncation paths (edit, reload) so that stale interrupt\n // markers and per-message metadata from the removed messages don't leak\n // into the next turn.\n const replaceMessages = useCallback(\n (msgs: AdkMessage[]) => {\n setMessagesImmediate(msgs);\n setLongRunningToolIds([]);\n setToolConfirmations([]);\n setAuthRequests([]);\n setEscalated(false);\n setMessageMetadata(new Map());\n },\n [setLongRunningToolIds, setMessagesImmediate],\n );\n\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const { onError, onCustomEvent, onAgentTransfer } = useMemo(\n () => eventHandlers ?? {},\n [eventHandlers],\n );\n\n const aui = useAui();\n const sendMessage = useCallback(\n async (newMessages: AdkMessage[], config: AdkSendMessageConfig) => {\n const newMessagesWithId = newMessages.map((m) =>\n m.id ? m : { ...m, id: generateId() },\n ) as AdkMessage[];\n\n // A staged message is already in the thread under its own id, and the\n // merged event below re-emits the whole batch under the first one. Seeding\n // with the originals would leave every later staged id beside the merged\n // copy of itself.\n const resentIds = new Set(newMessagesWithId.map((m) => m.id));\n // The optimistic event for a tool-only batch carries no author, so the accumulator cannot settle the calls this send answers.\n const answeredToolCallIds = new Set(\n newMessagesWithId.flatMap((m) =>\n m.type === \"tool\" ? [m.tool_call_id] : [],\n ),\n );\n const accumulator = new AdkEventAccumulator(\n messagesRef.current.filter((m) => !resentIds.has(m.id)),\n longRunningToolIdsRef.current.filter(\n (id) => !answeredToolCallIds.has(id),\n ),\n );\n for (const event of messagesToEvents(newMessagesWithId)) {\n accumulator.processEvent(event);\n }\n setMessagesImmediate(accumulator.getMessages());\n setLongRunningToolIds(accumulator.getLongRunningToolIds());\n\n // Google ADK replaces active runs, while React LangGraph queues sends.\n abortControllerRef.current?.abort();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n try {\n const response = await openAbortableIterable(\n stream(newMessagesWithId, {\n ...config,\n abortSignal: abortController.signal,\n initialize: async () => {\n return await aui.threadListItem.initialize();\n },\n }),\n abortController.signal,\n );\n if (!response) return;\n\n for await (const event of abortableIterable(\n response,\n abortController.signal,\n )) {\n if (\n abortController.signal.aborted ||\n abortControllerRef.current !== abortController\n ) {\n break;\n }\n const updatedMessages = accumulator.processEvent(event);\n setMessagesImmediate(updatedMessages);\n setStateDelta({\n ...stateDeltaRef.current,\n ...accumulator.getStateDelta(),\n });\n setAgentInfo(accumulator.getAgentInfo());\n setLongRunningToolIds(accumulator.getLongRunningToolIds());\n setArtifactDelta({\n ...artifactDeltaRef.current,\n ...accumulator.getArtifactDelta(),\n });\n setToolConfirmations(accumulator.getToolConfirmations());\n setAuthRequests(accumulator.getAuthRequests());\n setEscalated(accumulator.isEscalated());\n {\n const newMeta = accumulator.getMessageMetadata();\n if (newMeta.size > 0) {\n setMessageMetadata(\n new Map([...messageMetadataRef.current, ...newMeta]),\n );\n }\n }\n\n const transfer = accumulator.getLastTransferToAgent();\n if (transfer && transfer !== lastTransferToAgentRef.current) {\n lastTransferToAgentRef.current = transfer;\n invokeAdkRuntimeCallback(\n \"onAgentTransfer\",\n onAgentTransfer,\n transfer,\n );\n }\n\n // Fire custom event callback for events with customMetadata\n if (event.customMetadata && onCustomEvent) {\n for (const [key, value] of Object.entries(event.customMetadata)) {\n invokeAdkRuntimeCallback(\n \"onCustomEvent\",\n onCustomEvent,\n key,\n value,\n );\n }\n }\n\n if (event.errorCode || event.errorMessage) {\n invokeAdkRuntimeCallback(\n \"onError\",\n onError,\n event.errorMessage ?? event.errorCode,\n );\n }\n }\n } catch (error) {\n if (\n !abortController.signal.aborted &&\n abortControllerRef.current === abortController &&\n !(error instanceof Error && error.name === \"AbortError\")\n ) {\n throw error;\n }\n } finally {\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n }\n },\n [\n aui,\n setMessagesImmediate,\n setLongRunningToolIds,\n stream,\n onError,\n onCustomEvent,\n onAgentTransfer,\n ],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, []);\n\n useEffect(() => cancel, [cancel]);\n\n return {\n messages,\n stateDelta,\n agentInfo,\n longRunningToolIds,\n artifactDelta,\n toolConfirmations,\n authRequests,\n escalated,\n messageMetadata,\n sendMessage,\n cancel,\n setMessages: setMessagesImmediate,\n replaceMessages,\n applySnapshot,\n };\n};\n\n/**\n * Transport sends every human and tool message of one `send` call as a single\n * ADK `Content`, and ADK parses that event's function responses before running\n * any tool, so the batch runs whole or not at all. The optimistic projection\n * has to sit on the same boundary, so a run of those messages becomes one\n * synthetic event whose parts come from the same per-message conversion.\n *\n * The transport drops `ai` messages from that `Content`, so one interleaved\n * between two replies does not split the batch on the wire and must not split\n * it here either. It still becomes its own event, placed after the merged one,\n * so the optimistic projection keeps the assistant turn.\n *\n * @internal — exported for unit tests.\n */\nexport const messagesToEvents = (messages: AdkMessage[]): AdkEvent[] => {\n // A reload sends no messages at all, and the empty user content the transport\n // puts on the wire for it is not part of the optimistic view: projecting one\n // would put an empty user bubble above every regenerated turn.\n if (messages.length === 0) return [];\n\n const events: AdkEvent[] = [];\n const run: AdkMessage[] = [];\n let runIndex = 0;\n\n for (const msg of messages) {\n if (msg.type === \"ai\") {\n events.push(messageToEvent(msg));\n } else {\n if (run.length === 0) runIndex = events.length;\n run.push(msg);\n }\n }\n\n const parts = run.flatMap((m) => messageToEvent(m).content?.parts ?? []);\n const human = run.find((m) => m.type === \"human\");\n\n // A batch that contributes no part still reaches the wire: the transport\n // sends an empty user `Content`, which a reload replays as an empty human\n // message. Emitting it here keeps the optimistic view equal to that replay.\n if (parts.length === 0) parts.push({ text: \"\" });\n\n const event: AdkEvent = { id: (human ?? run[0])?.id ?? generateId() };\n if (human || run.length === 0) event.author = \"user\";\n event.content = { role: \"user\", parts };\n events.splice(run.length > 0 ? runIndex : events.length, 0, event);\n\n return events;\n};\n\n/** @internal — exported for unit tests. */\nexport const messageToEvent = (msg: AdkMessage): AdkEvent => {\n if (msg.type === \"human\") {\n return {\n id: msg.id ?? generateId(),\n author: \"user\",\n content: { role: \"user\", parts: contentToParts(msg.content) },\n };\n }\n\n if (msg.type === \"tool\") {\n let response: unknown;\n try {\n response = JSON.parse(msg.content);\n } catch {\n response = msg.content;\n }\n return {\n id: msg.id ?? generateId(),\n content: {\n role: \"user\",\n parts: [\n {\n functionResponse: {\n name: msg.name,\n id: msg.tool_call_id,\n response: toAdkFunctionResponse(response, msg.status === \"error\"),\n },\n },\n ],\n },\n };\n }\n\n const result: AdkEvent = { id: msg.id ?? generateId() };\n if (msg.author != null) result.author = msg.author;\n result.content = {\n role: \"model\",\n parts: [\n ...contentToParts(msg.content),\n ...(msg.tool_calls?.map((tc) => ({\n functionCall: { name: tc.name, id: tc.id, args: { ...tc.args } },\n })) ?? []),\n ],\n };\n return result;\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAdkRuntime.d.ts","names":[],"sources":["../src/useAdkRuntime.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"useAdkRuntime.d.ts","names":[],"sources":["../src/useAdkRuntime.ts"],"mappings":";;;;KA6DY,uBAAuB;EACjC,QAAQ;;;;;;;;;EASR,qBAAqB;EACrB;EACA;EACA,mBACE,kBACA,gBAAgB,iBACb;;;;;;;EAOL,QACE,kBACA;IAAY,SAAS;QAClB,QAAQ;EACb,eAAe;IAAU;;EACzB,UAAU,qBAAqB;EAC/B;IAEM,cAAc;IACd,SAAS;IACT,YAAY;IACZ,QAAQ;IACR,WAAW;;EAGjB;IAEM,UAAU;IACV,gBAAgB;IAChB,kBAAkB;;EAGxB,QAAQ;;;;;EAKR,iBAAiB;;cAkYN,kBAAiB,OAAA,gBAAA,QAAA,QAAA,UAAA,qBAAA,WAO3B,sDAAoB"}
|
package/dist/useAdkRuntime.js
CHANGED
|
@@ -3,9 +3,10 @@ import { projectAdkToolApprovals, toAdkToolConfirmationReply } from "./adkToolAp
|
|
|
3
3
|
import { convertAdkMessage, createAdkMessageConverter } from "./convertAdkMessages.js";
|
|
4
4
|
import { getMessageContent, getPendingCancellations, toAdkUserMessage, truncateAdkMessages } from "./convertToAdkMessages.js";
|
|
5
5
|
import { useAdkMessages } from "./useAdkMessages.js";
|
|
6
|
+
import { ADK_SDK } from "./sdkIdentity.js";
|
|
7
|
+
import { createAbortableThreadLoad, createCloudThreadListAdapterCreateFallback } from "@assistant-ui/core/internal";
|
|
6
8
|
import { generateId, pickExternalStoreSharedOptions } from "@assistant-ui/core";
|
|
7
9
|
import { useCloudThreadListAdapter, useExternalMessageConverter, useExternalStoreRuntime, useRemoteThreadListRuntime } from "@assistant-ui/core/react";
|
|
8
|
-
import { createAbortableThreadLoad, createCloudThreadListAdapterCreateFallback } from "@assistant-ui/core/internal";
|
|
9
10
|
import { useAui } from "@assistant-ui/store";
|
|
10
11
|
import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState } from "react";
|
|
11
12
|
//#region src/useAdkRuntime.ts
|
|
@@ -34,12 +35,14 @@ const useAdkRuntimeImpl = (options) => {
|
|
|
34
35
|
useInsertionEffect(() => {
|
|
35
36
|
isRunningRef.current = effectiveIsRunning;
|
|
36
37
|
}, [effectiveIsRunning]);
|
|
38
|
+
const runGenerationRef = useRef(0);
|
|
37
39
|
const handleSendMessage = async (msgs, config) => {
|
|
40
|
+
const generation = ++runGenerationRef.current;
|
|
38
41
|
try {
|
|
39
42
|
setIsRunning(true);
|
|
40
43
|
await sendMessage(msgs, config);
|
|
41
44
|
} finally {
|
|
42
|
-
setIsRunning(false);
|
|
45
|
+
if (runGenerationRef.current === generation) setIsRunning(false);
|
|
43
46
|
}
|
|
44
47
|
};
|
|
45
48
|
const { approvals: toolApprovals, key: toolApprovalsKey } = projectAdkToolApprovals(messages);
|
|
@@ -229,6 +232,7 @@ const useAdkRuntimeImpl = (options) => {
|
|
|
229
232
|
const useAdkRuntime = ({ cloud, sessionAdapter, create, delete: deleteFn, onThreadIdChange, ...options }) => {
|
|
230
233
|
const aui = useAui();
|
|
231
234
|
const cloudAdapter = useCloudThreadListAdapter({
|
|
235
|
+
sdk: ADK_SDK,
|
|
232
236
|
cloud,
|
|
233
237
|
create: createCloudThreadListAdapterCreateFallback(create, aui.threadListItem),
|
|
234
238
|
delete: deleteFn
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAdkRuntime.js","names":[],"sources":["../src/useAdkRuntime.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n pickExternalStoreSharedOptions,\n type AttachmentAdapter,\n type DictationAdapter,\n type ExternalStoreSharedOptions,\n type FeedbackAdapter,\n type RealtimeVoiceAdapter,\n type SpeechSynthesisAdapter,\n type AppendMessage,\n type ToolCallMessagePart,\n type ToolExecutionStatus,\n generateId,\n} from \"@assistant-ui/core\";\nimport {\n createAbortableThreadLoad,\n createCloudThreadListAdapterCreateFallback,\n} from \"@assistant-ui/core/internal\";\nimport {\n useCloudThreadListAdapter,\n useRemoteThreadListRuntime,\n useExternalMessageConverter,\n useExternalStoreRuntime,\n} from \"@assistant-ui/core/react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport type { AssistantCloud } from \"assistant-cloud\";\nimport type { RemoteThreadListAdapter } from \"@assistant-ui/core\";\nimport type {\n AdkMessage,\n AdkThreadSnapshot,\n AdkSendMessageConfig,\n AdkStreamCallback,\n OnAdkErrorCallback,\n OnAdkCustomEventCallback,\n OnAdkAgentTransferCallback,\n} from \"./types\";\nimport { useAdkMessages } from \"./useAdkMessages\";\nimport {\n convertAdkMessage,\n createAdkMessageConverter,\n} from \"./convertAdkMessages\";\nimport {\n getMessageContent,\n getPendingCancellations,\n toAdkUserMessage,\n truncateAdkMessages,\n} from \"./convertToAdkMessages\";\nimport {\n projectAdkToolApprovals,\n toAdkToolConfirmationReply,\n} from \"./adkToolApproval\";\nimport { adkExtras } from \"./adkExtras\";\n\nexport type UseAdkRuntimeOptions = ExternalStoreSharedOptions & {\n stream: AdkStreamCallback;\n /**\n * Called whenever the active thread's canonical (remote) ID changes, so the\n * value can be treated as a managed/controlled variable (e.g. synced to a URL\n * query param). Only the settled remote ID is emitted: while a freshly created\n * thread is still optimistic the value is `undefined`, and the real ID is\n * emitted once the thread is initialized; the transient local ID is never\n * surfaced.\n */\n onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n unstable_allowCancellation?: boolean | undefined;\n getCheckpointId?: (\n threadId: string,\n parentMessages: AdkMessage[],\n ) => Promise<string | null>;\n /**\n * Loads a thread's stored state. Called when the thread opens, and again for\n * `threads.reloadMainThread()`, which refetches in place rather than\n * remounting the runtime; the signal aborts a load the runtime no longer\n * needs.\n */\n load?: (\n threadId: string,\n options?: { signal?: AbortSignal | undefined },\n ) => Promise<AdkThreadSnapshot>;\n create?: () => Promise<{ externalId: string }>;\n delete?: (threadId: string) => Promise<void>;\n adapters?:\n | {\n attachments?: AttachmentAdapter;\n speech?: SpeechSynthesisAdapter;\n dictation?: DictationAdapter;\n voice?: RealtimeVoiceAdapter;\n feedback?: FeedbackAdapter;\n }\n | undefined;\n eventHandlers?:\n | {\n onError?: OnAdkErrorCallback;\n onCustomEvent?: OnAdkCustomEventCallback;\n onAgentTransfer?: OnAdkAgentTransferCallback;\n }\n | undefined;\n cloud?: AssistantCloud | undefined;\n /**\n * A `RemoteThreadListAdapter` to use instead of the cloud adapter.\n * Use with `createAdkSessionAdapter` for ADK session-backed persistence.\n */\n sessionAdapter?: RemoteThreadListAdapter | undefined;\n};\n\nconst useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {\n const {\n autoCancelPendingToolCalls,\n adapters: { attachments, dictation, feedback, speech, voice } = {},\n unstable_allowCancellation,\n stream,\n load,\n getCheckpointId,\n eventHandlers,\n } = options;\n const aui = useAui();\n const {\n messages,\n stateDelta,\n agentInfo,\n longRunningToolIds,\n artifactDelta,\n toolConfirmations,\n authRequests,\n escalated,\n messageMetadata,\n sendMessage,\n cancel,\n setMessages,\n replaceMessages,\n applySnapshot,\n } = useAdkMessages({\n stream,\n ...(eventHandlers && { eventHandlers }),\n });\n\n const loadRef = useRef(load);\n useInsertionEffect(() => {\n loadRef.current = load;\n }, [load]);\n const loadController = useMemo(createAbortableThreadLoad, []);\n const messagesRef = useRef(messages);\n useInsertionEffect(() => {\n messagesRef.current = messages;\n }, [messages]);\n const [isLoadingThread, setIsLoadingThread] = useState(\n () =>\n load !== undefined && aui.threadListItem.getState().externalId != null,\n );\n\n const [isRunning, setIsRunning] = useState(false);\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n const hasExecutingTools = Object.values(toolStatuses).some(\n (s) => s?.type === \"executing\",\n );\n const effectiveIsRunning = isRunning || hasExecutingTools;\n const isRunningRef = useRef(effectiveIsRunning);\n useInsertionEffect(() => {\n isRunningRef.current = effectiveIsRunning;\n }, [effectiveIsRunning]);\n\n const handleSendMessage = async (\n msgs: AdkMessage[],\n config: AdkSendMessageConfig,\n ) => {\n try {\n setIsRunning(true);\n await sendMessage(msgs, config);\n } finally {\n setIsRunning(false);\n }\n };\n\n const { approvals: toolApprovals, key: toolApprovalsKey } =\n projectAdkToolApprovals(messages);\n // The messageConverter memo below reads this during render, where the ref\n // must carry the same render's approvals; a commit-scoped write would feed\n // the memo the previous commit's approvals whenever the key changes. No\n // callback reads it — approval replies project from the committed messages.\n const toolApprovalsRef = useRef(toolApprovals);\n toolApprovalsRef.current = toolApprovals;\n\n const longRunningToolIdsRef = useRef(longRunningToolIds);\n useInsertionEffect(() => {\n longRunningToolIdsRef.current = longRunningToolIds;\n }, [longRunningToolIds]);\n // ADK resolves every call it did not mark long-running itself, and yields\n // that call to the client one or more events before its own response, so\n // only a long-running call is the client's to execute.\n const isClientToolCall = useCallback(\n (toolCall: ToolCallMessagePart) =>\n longRunningToolIdsRef.current.includes(toolCall.toolCallId),\n [],\n );\n\n const messageConverter = useMemo(\n () =>\n toolApprovalsKey === \"\"\n ? convertAdkMessage\n : createAdkMessageConverter(toolApprovalsRef.current),\n [toolApprovalsKey],\n );\n\n const threadMessages = useExternalMessageConverter({\n callback: messageConverter,\n messages,\n isRunning: effectiveIsRunning,\n });\n\n const threadMessagesRef = useRef(threadMessages);\n useInsertionEffect(() => {\n threadMessagesRef.current = threadMessages;\n }, [threadMessages]);\n\n // Staging assigns adkMessagesRef.current directly, so the effect must key on\n // the committed messages alone; a dep-less publication would clobber the\n // optimistic value on any unrelated commit.\n const adkMessagesRef = useRef(messages);\n useInsertionEffect(() => {\n adkMessagesRef.current = messages;\n }, [messages]);\n\n const stagedMessagesRef = useRef(\n new Map<\n string,\n {\n message: AdkMessage & { id: string };\n runConfig: AppendMessage[\"runConfig\"];\n }\n >(),\n );\n const [stagedMessageCount, setStagedMessageCount] = useState(0);\n const hasStagedMessages = stagedMessageCount > 0;\n\n const getStagedRun = (parentId: string | null) => {\n if (!parentId || !stagedMessagesRef.current.has(parentId)) return null;\n\n const staged: AdkMessage[] = [];\n for (const message of adkMessagesRef.current) {\n if (message.id && stagedMessagesRef.current.has(message.id)) {\n staged.push(stagedMessagesRef.current.get(message.id)!.message);\n }\n if (message.id === parentId) break;\n }\n\n return {\n messages: staged,\n runConfig: stagedMessagesRef.current.get(parentId)!.runConfig,\n };\n };\n\n const stageUserMessage = (msg: AppendMessage) => {\n const stagedMessage = toAdkUserMessage(msg);\n stagedMessagesRef.current.set(stagedMessage.id, {\n message: stagedMessage,\n runConfig: msg.runConfig,\n });\n setStagedMessageCount(stagedMessagesRef.current.size);\n const nextMessages = [...adkMessagesRef.current, stagedMessage];\n adkMessagesRef.current = nextMessages;\n setMessages(nextMessages);\n };\n\n // The scoped client, not `aui` itself: useAui returns a render-bound\n // instance, so depending on it would re-run the load on every render.\n const threadListItem =\n aui.threadListItem.source !== null ? aui.threadListItem : undefined;\n\n const runLoad = useCallback(\n (purpose: \"initial\" | \"reload\" = \"initial\") => {\n const loadFn = loadRef.current;\n if (!loadFn || !threadListItem) return Promise.resolve();\n\n const externalId = threadListItem.getState().externalId;\n if (externalId == null) return Promise.resolve();\n\n // The initial load is already fetching what a refetch would ask for, and\n // taking it over strands the thread's history if the refetch then fails.\n // Aborting a load the runtime no longer needs is not a failure.\n // A refetch reports the failure to whoever awaited it; the initial load\n // has no caller to tell.\n return loadController.run({\n purpose,\n load: async (signal) => {\n const messagesAtLoadStart = messagesRef.current;\n if (purpose === \"initial\") setIsLoadingThread(true);\n\n const snapshot = await loadFn(externalId, { signal });\n if (signal.aborted) return;\n // A snapshot the session assembled before a run cannot speak for what\n // that run has since produced, and an ADK id cannot correlate a\n // message sent optimistically with the one the session stored for it,\n // so there is nothing here that could merge the two. A refetch that\n // raced a run therefore defers to the run, whether the run started\n // during the load or was already streaming when it began.\n if (\n purpose === \"reload\" &&\n (isRunningRef.current ||\n messagesRef.current !== messagesAtLoadStart)\n )\n return;\n applySnapshot(snapshot);\n },\n onSettled: () => {\n setIsLoadingThread(false);\n },\n onInitialError: (error) => {\n console.warn(\"Failed to load ADK session:\", error);\n },\n });\n },\n [threadListItem, loadController, applySnapshot],\n );\n\n useEffect(() => {\n runLoad();\n return () => {\n // Whatever is current, not this effect's own controller: a refetch swaps\n // the ref, and one in flight at unmount must be aborted too.\n loadController.abort();\n setIsLoadingThread(false);\n };\n }, [loadController, runLoad]);\n\n const runtime = useExternalStoreRuntime({\n ...pickExternalStoreSharedOptions(options),\n isRunning,\n isLoading: isLoadingThread,\n messages: threadMessages,\n unstable_enableToolInvocations: true,\n unstable_isClientToolCall: isClientToolCall,\n setToolStatuses,\n adapters: { attachments, dictation, feedback, speech, voice },\n extras: adkExtras.provide({\n agentInfo,\n stateDelta,\n artifactDelta,\n longRunningToolIds,\n toolConfirmations,\n authRequests,\n escalated,\n messageMetadata,\n send: handleSendMessage,\n }),\n onNew: async (msg) => {\n if (!(msg.startRun ?? msg.role === \"user\")) {\n stageUserMessage(msg);\n return;\n }\n\n const cancellations =\n autoCancelPendingToolCalls !== false\n ? getPendingCancellations(messages, longRunningToolIds)\n : [];\n\n return handleSendMessage(\n [\n ...cancellations,\n {\n id: generateId(),\n type: \"human\",\n content: getMessageContent(msg),\n },\n ],\n { runConfig: msg.runConfig },\n );\n },\n onEdit: getCheckpointId\n ? async (msg) => {\n const truncated = truncateAdkMessages(\n threadMessagesRef.current,\n msg.parentId,\n );\n replaceMessages(truncated);\n if (!(msg.startRun ?? msg.role === \"user\")) {\n const stagedMessage = toAdkUserMessage(msg);\n stagedMessagesRef.current.set(stagedMessage.id, {\n message: stagedMessage,\n runConfig: msg.runConfig,\n });\n setStagedMessageCount(stagedMessagesRef.current.size);\n const nextMessages = [...truncated, stagedMessage];\n adkMessagesRef.current = nextMessages;\n setMessages(nextMessages);\n return;\n }\n const externalId = aui.threadListItem.getState().externalId;\n const checkpointId = externalId\n ? await getCheckpointId(externalId, truncated)\n : null;\n return handleSendMessage(\n [\n {\n id: generateId(),\n type: \"human\",\n content: getMessageContent(msg),\n },\n ],\n {\n runConfig: msg.runConfig,\n ...(checkpointId && { checkpointId }),\n },\n );\n }\n : undefined,\n ...(getCheckpointId || hasStagedMessages\n ? {\n onReload: async (parentId, config) => {\n const stagedRun = getStagedRun(parentId);\n if (stagedRun) {\n for (const message of stagedRun.messages) {\n stagedMessagesRef.current.delete(message.id);\n }\n setStagedMessageCount(stagedMessagesRef.current.size);\n return handleSendMessage(stagedRun.messages, {\n runConfig: config.runConfig ?? stagedRun.runConfig,\n });\n }\n\n if (!getCheckpointId)\n throw new Error(\"Runtime does not support reloading messages.\");\n\n const truncated = truncateAdkMessages(\n threadMessagesRef.current,\n parentId,\n );\n replaceMessages(truncated);\n const externalId = aui.threadListItem.getState().externalId;\n const checkpointId = externalId\n ? await getCheckpointId(externalId, truncated)\n : null;\n return handleSendMessage([], {\n runConfig: config.runConfig,\n ...(checkpointId && { checkpointId }),\n });\n },\n }\n : {}),\n onAddToolResult: async ({\n toolCallId,\n toolName,\n result,\n isError,\n artifact,\n }) => {\n await handleSendMessage(\n [\n {\n id: generateId(),\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n artifact,\n status: isError ? \"error\" : \"success\",\n },\n ],\n {},\n );\n },\n onRespondToToolApproval: async (options) => {\n await handleSendMessage(\n [\n toAdkToolConfirmationReply(\n options,\n projectAdkToolApprovals(adkMessagesRef.current).approvals,\n ),\n ],\n {},\n );\n },\n onCancel: unstable_allowCancellation\n ? async () => {\n cancel();\n }\n : undefined,\n ...(load !== undefined && {\n onRefetchThread: () => runLoad(\"reload\"),\n }),\n });\n\n return runtime;\n};\n\nexport const useAdkRuntime = ({\n cloud,\n sessionAdapter,\n create,\n delete: deleteFn,\n onThreadIdChange,\n ...options\n}: UseAdkRuntimeOptions) => {\n const aui = useAui();\n const cloudAdapter = useCloudThreadListAdapter({\n cloud,\n create: createCloudThreadListAdapterCreateFallback(\n create,\n aui.threadListItem,\n ),\n delete: deleteFn,\n });\n\n const adapter = sessionAdapter ?? cloudAdapter;\n\n return useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useAdkRuntimeImpl(options);\n },\n adapter,\n allowNesting: true,\n onThreadIdChange,\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useAdkRuntime.js","names":[],"sources":["../src/useAdkRuntime.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n pickExternalStoreSharedOptions,\n type AttachmentAdapter,\n type DictationAdapter,\n type ExternalStoreSharedOptions,\n type FeedbackAdapter,\n type RealtimeVoiceAdapter,\n type SpeechSynthesisAdapter,\n type AppendMessage,\n type ToolCallMessagePart,\n type ToolExecutionStatus,\n generateId,\n} from \"@assistant-ui/core\";\nimport {\n createAbortableThreadLoad,\n createCloudThreadListAdapterCreateFallback,\n} from \"@assistant-ui/core/internal\";\nimport {\n useCloudThreadListAdapter,\n useRemoteThreadListRuntime,\n useExternalMessageConverter,\n useExternalStoreRuntime,\n} from \"@assistant-ui/core/react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport type { AssistantCloud } from \"assistant-cloud\";\nimport type { RemoteThreadListAdapter } from \"@assistant-ui/core\";\nimport type {\n AdkMessage,\n AdkThreadSnapshot,\n AdkSendMessageConfig,\n AdkStreamCallback,\n OnAdkErrorCallback,\n OnAdkCustomEventCallback,\n OnAdkAgentTransferCallback,\n} from \"./types\";\nimport { useAdkMessages } from \"./useAdkMessages\";\nimport {\n convertAdkMessage,\n createAdkMessageConverter,\n} from \"./convertAdkMessages\";\nimport {\n getMessageContent,\n getPendingCancellations,\n toAdkUserMessage,\n truncateAdkMessages,\n} from \"./convertToAdkMessages\";\nimport {\n projectAdkToolApprovals,\n toAdkToolConfirmationReply,\n} from \"./adkToolApproval\";\nimport { adkExtras } from \"./adkExtras\";\nimport { ADK_SDK } from \"./sdkIdentity\";\n\nexport type UseAdkRuntimeOptions = ExternalStoreSharedOptions & {\n stream: AdkStreamCallback;\n /**\n * Called whenever the active thread's canonical (remote) ID changes, so the\n * value can be treated as a managed/controlled variable (e.g. synced to a URL\n * query param). Only the settled remote ID is emitted: while a freshly created\n * thread is still optimistic the value is `undefined`, and the real ID is\n * emitted once the thread is initialized; the transient local ID is never\n * surfaced.\n */\n onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n unstable_allowCancellation?: boolean | undefined;\n getCheckpointId?: (\n threadId: string,\n parentMessages: AdkMessage[],\n ) => Promise<string | null>;\n /**\n * Loads a thread's stored state. Called when the thread opens, and again for\n * `threads.reloadMainThread()`, which refetches in place rather than\n * remounting the runtime; the signal aborts a load the runtime no longer\n * needs.\n */\n load?: (\n threadId: string,\n options?: { signal?: AbortSignal | undefined },\n ) => Promise<AdkThreadSnapshot>;\n create?: () => Promise<{ externalId: string }>;\n delete?: (threadId: string) => Promise<void>;\n adapters?:\n | {\n attachments?: AttachmentAdapter;\n speech?: SpeechSynthesisAdapter;\n dictation?: DictationAdapter;\n voice?: RealtimeVoiceAdapter;\n feedback?: FeedbackAdapter;\n }\n | undefined;\n eventHandlers?:\n | {\n onError?: OnAdkErrorCallback;\n onCustomEvent?: OnAdkCustomEventCallback;\n onAgentTransfer?: OnAdkAgentTransferCallback;\n }\n | undefined;\n cloud?: AssistantCloud | undefined;\n /**\n * A `RemoteThreadListAdapter` to use instead of the cloud adapter.\n * Use with `createAdkSessionAdapter` for ADK session-backed persistence.\n */\n sessionAdapter?: RemoteThreadListAdapter | undefined;\n};\n\nconst useAdkRuntimeImpl = (options: UseAdkRuntimeOptions) => {\n const {\n autoCancelPendingToolCalls,\n adapters: { attachments, dictation, feedback, speech, voice } = {},\n unstable_allowCancellation,\n stream,\n load,\n getCheckpointId,\n eventHandlers,\n } = options;\n const aui = useAui();\n const {\n messages,\n stateDelta,\n agentInfo,\n longRunningToolIds,\n artifactDelta,\n toolConfirmations,\n authRequests,\n escalated,\n messageMetadata,\n sendMessage,\n cancel,\n setMessages,\n replaceMessages,\n applySnapshot,\n } = useAdkMessages({\n stream,\n ...(eventHandlers && { eventHandlers }),\n });\n\n const loadRef = useRef(load);\n useInsertionEffect(() => {\n loadRef.current = load;\n }, [load]);\n const loadController = useMemo(createAbortableThreadLoad, []);\n const messagesRef = useRef(messages);\n useInsertionEffect(() => {\n messagesRef.current = messages;\n }, [messages]);\n const [isLoadingThread, setIsLoadingThread] = useState(\n () =>\n load !== undefined && aui.threadListItem.getState().externalId != null,\n );\n\n const [isRunning, setIsRunning] = useState(false);\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n const hasExecutingTools = Object.values(toolStatuses).some(\n (s) => s?.type === \"executing\",\n );\n const effectiveIsRunning = isRunning || hasExecutingTools;\n const isRunningRef = useRef(effectiveIsRunning);\n useInsertionEffect(() => {\n isRunningRef.current = effectiveIsRunning;\n }, [effectiveIsRunning]);\n const runGenerationRef = useRef(0);\n\n const handleSendMessage = async (\n msgs: AdkMessage[],\n config: AdkSendMessageConfig,\n ) => {\n const generation = ++runGenerationRef.current;\n try {\n setIsRunning(true);\n await sendMessage(msgs, config);\n } finally {\n if (runGenerationRef.current === generation) setIsRunning(false);\n }\n };\n\n const { approvals: toolApprovals, key: toolApprovalsKey } =\n projectAdkToolApprovals(messages);\n // The messageConverter memo below reads this during render, where the ref\n // must carry the same render's approvals; a commit-scoped write would feed\n // the memo the previous commit's approvals whenever the key changes. No\n // callback reads it — approval replies project from the committed messages.\n const toolApprovalsRef = useRef(toolApprovals);\n toolApprovalsRef.current = toolApprovals;\n\n const longRunningToolIdsRef = useRef(longRunningToolIds);\n useInsertionEffect(() => {\n longRunningToolIdsRef.current = longRunningToolIds;\n }, [longRunningToolIds]);\n // ADK resolves every call it did not mark long-running itself, and yields\n // that call to the client one or more events before its own response, so\n // only a long-running call is the client's to execute.\n const isClientToolCall = useCallback(\n (toolCall: ToolCallMessagePart) =>\n longRunningToolIdsRef.current.includes(toolCall.toolCallId),\n [],\n );\n\n const messageConverter = useMemo(\n () =>\n toolApprovalsKey === \"\"\n ? convertAdkMessage\n : createAdkMessageConverter(toolApprovalsRef.current),\n [toolApprovalsKey],\n );\n\n const threadMessages = useExternalMessageConverter({\n callback: messageConverter,\n messages,\n isRunning: effectiveIsRunning,\n });\n\n const threadMessagesRef = useRef(threadMessages);\n useInsertionEffect(() => {\n threadMessagesRef.current = threadMessages;\n }, [threadMessages]);\n\n // Staging assigns adkMessagesRef.current directly, so the effect must key on\n // the committed messages alone; a dep-less publication would clobber the\n // optimistic value on any unrelated commit.\n const adkMessagesRef = useRef(messages);\n useInsertionEffect(() => {\n adkMessagesRef.current = messages;\n }, [messages]);\n\n const stagedMessagesRef = useRef(\n new Map<\n string,\n {\n message: AdkMessage & { id: string };\n runConfig: AppendMessage[\"runConfig\"];\n }\n >(),\n );\n const [stagedMessageCount, setStagedMessageCount] = useState(0);\n const hasStagedMessages = stagedMessageCount > 0;\n\n const getStagedRun = (parentId: string | null) => {\n if (!parentId || !stagedMessagesRef.current.has(parentId)) return null;\n\n const staged: AdkMessage[] = [];\n for (const message of adkMessagesRef.current) {\n if (message.id && stagedMessagesRef.current.has(message.id)) {\n staged.push(stagedMessagesRef.current.get(message.id)!.message);\n }\n if (message.id === parentId) break;\n }\n\n return {\n messages: staged,\n runConfig: stagedMessagesRef.current.get(parentId)!.runConfig,\n };\n };\n\n const stageUserMessage = (msg: AppendMessage) => {\n const stagedMessage = toAdkUserMessage(msg);\n stagedMessagesRef.current.set(stagedMessage.id, {\n message: stagedMessage,\n runConfig: msg.runConfig,\n });\n setStagedMessageCount(stagedMessagesRef.current.size);\n const nextMessages = [...adkMessagesRef.current, stagedMessage];\n adkMessagesRef.current = nextMessages;\n setMessages(nextMessages);\n };\n\n // The scoped client, not `aui` itself: useAui returns a render-bound\n // instance, so depending on it would re-run the load on every render.\n const threadListItem =\n aui.threadListItem.source !== null ? aui.threadListItem : undefined;\n\n const runLoad = useCallback(\n (purpose: \"initial\" | \"reload\" = \"initial\") => {\n const loadFn = loadRef.current;\n if (!loadFn || !threadListItem) return Promise.resolve();\n\n const externalId = threadListItem.getState().externalId;\n if (externalId == null) return Promise.resolve();\n\n // The initial load is already fetching what a refetch would ask for, and\n // taking it over strands the thread's history if the refetch then fails.\n // Aborting a load the runtime no longer needs is not a failure.\n // A refetch reports the failure to whoever awaited it; the initial load\n // has no caller to tell.\n return loadController.run({\n purpose,\n load: async (signal) => {\n const messagesAtLoadStart = messagesRef.current;\n if (purpose === \"initial\") setIsLoadingThread(true);\n\n const snapshot = await loadFn(externalId, { signal });\n if (signal.aborted) return;\n // A snapshot the session assembled before a run cannot speak for what\n // that run has since produced, and an ADK id cannot correlate a\n // message sent optimistically with the one the session stored for it,\n // so there is nothing here that could merge the two. A refetch that\n // raced a run therefore defers to the run, whether the run started\n // during the load or was already streaming when it began.\n if (\n purpose === \"reload\" &&\n (isRunningRef.current ||\n messagesRef.current !== messagesAtLoadStart)\n )\n return;\n applySnapshot(snapshot);\n },\n onSettled: () => {\n setIsLoadingThread(false);\n },\n onInitialError: (error) => {\n console.warn(\"Failed to load ADK session:\", error);\n },\n });\n },\n [threadListItem, loadController, applySnapshot],\n );\n\n useEffect(() => {\n runLoad();\n return () => {\n // Whatever is current, not this effect's own controller: a refetch swaps\n // the ref, and one in flight at unmount must be aborted too.\n loadController.abort();\n setIsLoadingThread(false);\n };\n }, [loadController, runLoad]);\n\n const runtime = useExternalStoreRuntime({\n ...pickExternalStoreSharedOptions(options),\n isRunning,\n isLoading: isLoadingThread,\n messages: threadMessages,\n unstable_enableToolInvocations: true,\n unstable_isClientToolCall: isClientToolCall,\n setToolStatuses,\n adapters: { attachments, dictation, feedback, speech, voice },\n extras: adkExtras.provide({\n agentInfo,\n stateDelta,\n artifactDelta,\n longRunningToolIds,\n toolConfirmations,\n authRequests,\n escalated,\n messageMetadata,\n send: handleSendMessage,\n }),\n onNew: async (msg) => {\n if (!(msg.startRun ?? msg.role === \"user\")) {\n stageUserMessage(msg);\n return;\n }\n\n const cancellations =\n autoCancelPendingToolCalls !== false\n ? getPendingCancellations(messages, longRunningToolIds)\n : [];\n\n return handleSendMessage(\n [\n ...cancellations,\n {\n id: generateId(),\n type: \"human\",\n content: getMessageContent(msg),\n },\n ],\n { runConfig: msg.runConfig },\n );\n },\n onEdit: getCheckpointId\n ? async (msg) => {\n const truncated = truncateAdkMessages(\n threadMessagesRef.current,\n msg.parentId,\n );\n replaceMessages(truncated);\n if (!(msg.startRun ?? msg.role === \"user\")) {\n const stagedMessage = toAdkUserMessage(msg);\n stagedMessagesRef.current.set(stagedMessage.id, {\n message: stagedMessage,\n runConfig: msg.runConfig,\n });\n setStagedMessageCount(stagedMessagesRef.current.size);\n const nextMessages = [...truncated, stagedMessage];\n adkMessagesRef.current = nextMessages;\n setMessages(nextMessages);\n return;\n }\n const externalId = aui.threadListItem.getState().externalId;\n const checkpointId = externalId\n ? await getCheckpointId(externalId, truncated)\n : null;\n return handleSendMessage(\n [\n {\n id: generateId(),\n type: \"human\",\n content: getMessageContent(msg),\n },\n ],\n {\n runConfig: msg.runConfig,\n ...(checkpointId && { checkpointId }),\n },\n );\n }\n : undefined,\n ...(getCheckpointId || hasStagedMessages\n ? {\n onReload: async (parentId, config) => {\n const stagedRun = getStagedRun(parentId);\n if (stagedRun) {\n for (const message of stagedRun.messages) {\n stagedMessagesRef.current.delete(message.id);\n }\n setStagedMessageCount(stagedMessagesRef.current.size);\n return handleSendMessage(stagedRun.messages, {\n runConfig: config.runConfig ?? stagedRun.runConfig,\n });\n }\n\n if (!getCheckpointId)\n throw new Error(\"Runtime does not support reloading messages.\");\n\n const truncated = truncateAdkMessages(\n threadMessagesRef.current,\n parentId,\n );\n replaceMessages(truncated);\n const externalId = aui.threadListItem.getState().externalId;\n const checkpointId = externalId\n ? await getCheckpointId(externalId, truncated)\n : null;\n return handleSendMessage([], {\n runConfig: config.runConfig,\n ...(checkpointId && { checkpointId }),\n });\n },\n }\n : {}),\n onAddToolResult: async ({\n toolCallId,\n toolName,\n result,\n isError,\n artifact,\n }) => {\n await handleSendMessage(\n [\n {\n id: generateId(),\n type: \"tool\",\n name: toolName,\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n artifact,\n status: isError ? \"error\" : \"success\",\n },\n ],\n {},\n );\n },\n onRespondToToolApproval: async (options) => {\n await handleSendMessage(\n [\n toAdkToolConfirmationReply(\n options,\n projectAdkToolApprovals(adkMessagesRef.current).approvals,\n ),\n ],\n {},\n );\n },\n onCancel: unstable_allowCancellation\n ? async () => {\n cancel();\n }\n : undefined,\n ...(load !== undefined && {\n onRefetchThread: () => runLoad(\"reload\"),\n }),\n });\n\n return runtime;\n};\n\nexport const useAdkRuntime = ({\n cloud,\n sessionAdapter,\n create,\n delete: deleteFn,\n onThreadIdChange,\n ...options\n}: UseAdkRuntimeOptions) => {\n const aui = useAui();\n const cloudAdapter = useCloudThreadListAdapter({\n sdk: ADK_SDK,\n cloud,\n create: createCloudThreadListAdapterCreateFallback(\n create,\n aui.threadListItem,\n ),\n delete: deleteFn,\n });\n\n const adapter = sessionAdapter ?? cloudAdapter;\n\n return useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useAdkRuntimeImpl(options);\n },\n adapter,\n allowNesting: true,\n onThreadIdChange,\n });\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-google-adk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.30",
|
|
4
4
|
"description": "Google ADK adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"google-adk",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
],
|
|
35
35
|
"sideEffects": false,
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@assistant-ui/core": "^0.3.
|
|
38
|
-
"@assistant-ui/store": "^0.3.
|
|
37
|
+
"@assistant-ui/core": "^0.3.19",
|
|
38
|
+
"@assistant-ui/store": "^0.3.13",
|
|
39
39
|
"assistant-cloud": "*",
|
|
40
|
-
"assistant-stream": "^0.3.
|
|
40
|
+
"assistant-stream": "^0.3.43"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@google/adk": ">=0.5.0",
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
56
|
+
"@assistant-ui/x-buildutils": "0.0.27",
|
|
57
57
|
"@testing-library/dom": "^10.4.1",
|
|
58
58
|
"@testing-library/react": "^16.3.3",
|
|
59
59
|
"@types/react": "^19.2.18",
|
|
60
60
|
"jsdom": "^30.0.1",
|
|
61
61
|
"react": "^19.2.8",
|
|
62
|
-
"vitest": "^
|
|
62
|
+
"vitest": "^5.0.0"
|
|
63
63
|
},
|
|
64
64
|
"publishConfig": {
|
|
65
65
|
"access": "public",
|