@assistant-ui/react 0.14.8 → 0.14.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/client/ExternalThread.d.ts.map +1 -1
  2. package/dist/client/ExternalThread.js +2 -1
  3. package/dist/client/ExternalThread.js.map +1 -1
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.js +2 -2
  6. package/dist/internal.d.ts +3 -4
  7. package/dist/internal.js +1 -3
  8. package/dist/internal.js.map +1 -1
  9. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +3 -2
  10. package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts.map +1 -1
  11. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
  12. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +4 -10
  13. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  14. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts +1 -1
  15. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
  16. package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
  17. package/dist/mcp-apps/app-frame.js.map +1 -1
  18. package/dist/primitives/assistantModal/AssistantModalRoot.js +5 -1
  19. package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
  20. package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
  21. package/dist/primitives/message/MessagePartsGrouped.js +5 -2
  22. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  23. package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
  24. package/dist/utils/useToolArgsFieldStatus.d.ts.map +1 -1
  25. package/package.json +4 -4
  26. package/src/client/ExternalThread.ts +1 -0
  27. package/src/index.ts +1 -0
  28. package/src/internal.ts +1 -4
  29. package/src/legacy-runtime/runtime/ThreadListRuntime.ts +1 -4
  30. package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +3 -1
  31. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +7 -15
  32. package/src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.ts +1 -1
  33. package/src/mcp-apps/McpAppRenderer.tsx +1 -1
  34. package/src/mcp-apps/app-frame.tsx +2 -2
  35. package/src/primitives/assistantModal/AssistantModalRoot.tsx +1 -1
  36. package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +1 -1
  37. package/src/primitives/composer/ComposerInput.test.tsx +1 -1
  38. package/src/primitives/composer/trigger/TriggerPopoverRootContext.test.tsx +1 -1
  39. package/src/primitives/message/MessagePartsGrouped.tsx +10 -1
  40. package/src/primitives/threadListItem/ThreadListItemTitle.test.tsx +1 -1
  41. package/src/tests/BaseComposerRuntimeCore.test.ts +2 -2
  42. package/src/tests/MessageParts.loading.test.tsx +1 -1
  43. package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +2 -4
  44. package/src/tests/auiV0Encode.test.ts +1 -1
  45. package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.d.ts +0 -2
  46. package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js +0 -2
  47. package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.test.ts +0 -892
  48. package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.ts +0 -4
@@ -1 +1 @@
1
- {"version":3,"file":"app-frame.js","names":[],"sources":["../../src/mcp-apps/app-frame.tsx"],"sourcesContent":["\"use client\";\n\nimport { type MutableRefObject, useEffect, useRef, useState } from \"react\";\nimport { type RenderedFrame, SafeContentFrame } from \"safe-content-frame\";\nimport { type McpAppBridge, createMcpAppBridge } from \"./bridge\";\nimport type {\n McpAppBridgeHandlers,\n McpAppFrameProps,\n McpAppHostContext,\n} from \"./types\";\n\nconst DEFAULT_PRODUCT = \"assistant-ui-mcp-app\";\nconst INIT_TIMEOUT_MS = 5000;\nconst DEFAULT_MAX_HEIGHT = 800;\n\nfunction useBridgeNotify<T>(\n value: T | undefined,\n bridgeRef: MutableRefObject<McpAppBridge | null>,\n widgetReadyRef: MutableRefObject<boolean>,\n pendingRef: MutableRefObject<T | undefined>,\n lastSentRef: MutableRefObject<T | undefined>,\n notify: (bridge: McpAppBridge, v: T) => void,\n) {\n // biome-ignore lint/correctness/useExhaustiveDependencies: refs and notify are stable; we re-run only when value changes.\n useEffect(() => {\n if (!bridgeRef.current) return;\n if (value === undefined) return;\n if (lastSentRef.current === value) return;\n if (!widgetReadyRef.current) {\n pendingRef.current = value;\n return;\n }\n notify(bridgeRef.current, value);\n lastSentRef.current = value;\n }, [value]);\n}\n\ntype LiveSnapshot = {\n handlers: McpAppBridgeHandlers | undefined;\n hostInfo: McpAppFrameProps[\"hostInfo\"];\n hostContext: McpAppFrameProps[\"hostContext\"];\n input: unknown;\n output: unknown;\n};\n\n// Proxy each per-call handler through liveRef so the bridge always dispatches\n// to the latest handler reference (e.g. inline callbacks closing over state).\n// Capability presence is snapshot at mount: a handler added later requires a\n// remount (keyed on resource URI) to expose the capability to the widget.\nfunction buildLiveHandlers(\n initial: McpAppBridgeHandlers | undefined,\n liveRef: { readonly current: LiveSnapshot },\n): McpAppBridgeHandlers {\n const live = () => liveRef.current.handlers;\n const has = <K extends keyof McpAppBridgeHandlers>(key: K) =>\n initial?.[key] !== undefined;\n const out: McpAppBridgeHandlers = {};\n if (has(\"allowedTools\")) {\n Object.defineProperty(out, \"allowedTools\", {\n get: () => live()?.allowedTools,\n enumerable: true,\n configurable: true,\n });\n }\n const liveCall = <K extends keyof McpAppBridgeHandlers>(\n key: K,\n ): NonNullable<McpAppBridgeHandlers[K]> =>\n ((p: unknown) => {\n const fn = live()?.[key] as ((p: unknown) => unknown) | undefined;\n if (!fn) {\n throw new Error(`${key} handler is no longer available`);\n }\n return fn(p);\n }) as NonNullable<McpAppBridgeHandlers[K]>;\n if (has(\"callTool\")) out.callTool = liveCall(\"callTool\");\n if (has(\"readResource\")) out.readResource = liveCall(\"readResource\");\n if (has(\"listResources\")) out.listResources = liveCall(\"listResources\");\n if (has(\"openLink\")) out.openLink = liveCall(\"openLink\");\n if (has(\"sendMessage\")) out.sendMessage = liveCall(\"sendMessage\");\n if (has(\"updateModelContext\"))\n out.updateModelContext = liveCall(\"updateModelContext\");\n if (has(\"requestDisplayMode\"))\n out.requestDisplayMode = liveCall(\"requestDisplayMode\");\n out.onSizeChange = (p) => live()?.onSizeChange?.(p);\n out.onInitialized = () => live()?.onInitialized?.();\n out.onRequestTeardown = (p) => live()?.onRequestTeardown?.(p);\n out.onLog = (p) => live()?.onLog?.(p);\n out.onError = (e) => live()?.onError?.(e);\n return out;\n}\n\nexport function McpAppFrame({\n app,\n resource,\n input,\n output,\n sandbox,\n handlers,\n hostInfo,\n hostContext,\n maxHeight = DEFAULT_MAX_HEIGHT,\n}: McpAppFrameProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [contentHeight, setContentHeight] = useState<number | undefined>(\n undefined,\n );\n const bridgeRef = useRef<McpAppBridge | null>(null);\n const lastSentInputRef = useRef<unknown>(undefined);\n const lastSentOutputRef = useRef<unknown>(undefined);\n const lastSentHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n // Per MCP Apps spec, the host should defer notifications until the widget\n // signals readiness via `notifications/initialized`. Until then, we record\n // pending values and flush them on init.\n const widgetReadyRef = useRef(false);\n const pendingInputRef = useRef<unknown>(undefined);\n const pendingOutputRef = useRef<unknown>(undefined);\n const pendingHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n\n const liveRef = useRef<LiveSnapshot>(null!);\n liveRef.current = {\n handlers,\n hostInfo,\n hostContext,\n input,\n output,\n };\n\n const resourceUri = resource.uri;\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-mounts only on resource URI; live values flow through liveRef\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n let initTimeoutId: ReturnType<typeof setTimeout> | null = null;\n let frame: RenderedFrame | null = null;\n const sb = sandbox;\n const html = resource.html;\n\n const scf = new SafeContentFrame(sb?.product ?? DEFAULT_PRODUCT, {\n ...(sb?.sandbox !== undefined && { sandbox: sb.sandbox }),\n ...(sb?.useShadowDom !== undefined && { useShadowDom: sb.useShadowDom }),\n ...(sb?.enableBrowserCaching !== undefined && {\n enableBrowserCaching: sb.enableBrowserCaching,\n }),\n ...(sb?.salt !== undefined && { salt: sb.salt }),\n });\n\n const renderOpts =\n sb?.unsafeDocumentWrite !== undefined\n ? { unsafeDocumentWrite: sb.unsafeDocumentWrite }\n : undefined;\n\n scf\n .renderHtml(html, container, renderOpts)\n .then((rendered) => {\n if (cancelled) {\n rendered.dispose();\n return;\n }\n frame = rendered;\n const current = liveRef.current;\n const liveHandlers = buildLiveHandlers(current.handlers, liveRef);\n const liveOnInitialized = liveHandlers.onInitialized;\n const flushPending = () => {\n if (widgetReadyRef.current) return;\n widgetReadyRef.current = true;\n const b = bridgeRef.current;\n if (!b) return;\n if (pendingInputRef.current !== undefined) {\n b.notifyToolInput(pendingInputRef.current);\n lastSentInputRef.current = pendingInputRef.current;\n pendingInputRef.current = undefined;\n }\n if (pendingOutputRef.current !== undefined) {\n b.notifyToolResult(pendingOutputRef.current);\n lastSentOutputRef.current = pendingOutputRef.current;\n pendingOutputRef.current = undefined;\n }\n if (pendingHostContextRef.current !== undefined) {\n b.notifyHostContextChanged(pendingHostContextRef.current);\n lastSentHostContextRef.current = pendingHostContextRef.current;\n pendingHostContextRef.current = undefined;\n }\n };\n const wrappedHandlers: McpAppBridgeHandlers = {\n ...liveHandlers,\n onInitialized: () => {\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n flushPending();\n liveOnInitialized?.();\n },\n onSizeChange: (p) => {\n if (\n typeof p.height === \"number\" &&\n Number.isFinite(p.height) &&\n p.height > 0\n ) {\n setContentHeight(p.height);\n }\n liveHandlers.onSizeChange?.(p);\n },\n };\n // Safety net: if the widget never sends notifications/initialized\n // (broken or non-spec-compliant), flush the queue anyway so the host\n // doesn't appear hung.\n initTimeoutId = setTimeout(() => {\n initTimeoutId = null;\n flushPending();\n }, INIT_TIMEOUT_MS);\n bridgeRef.current = createMcpAppBridge({\n frame: rendered,\n handlers: wrappedHandlers,\n hostInfo: current.hostInfo,\n hostContext: current.hostContext,\n });\n\n if (current.input !== undefined)\n pendingInputRef.current = current.input;\n if (current.output !== undefined)\n pendingOutputRef.current = current.output;\n // hostContext is delivered inside the ui/initialize response; subsequent\n // changes flow through useBridgeNotify's pending path.\n })\n .catch((err) => {\n liveRef.current.handlers?.onError?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n });\n\n return () => {\n cancelled = true;\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n bridgeRef.current?.dispose();\n bridgeRef.current = null;\n frame?.dispose();\n frame = null;\n lastSentInputRef.current = undefined;\n lastSentOutputRef.current = undefined;\n lastSentHostContextRef.current = undefined;\n widgetReadyRef.current = false;\n pendingInputRef.current = undefined;\n pendingOutputRef.current = undefined;\n pendingHostContextRef.current = undefined;\n setContentHeight(undefined);\n };\n }, [resourceUri]);\n\n useBridgeNotify(\n input,\n bridgeRef,\n widgetReadyRef,\n pendingInputRef,\n lastSentInputRef,\n (b, v) => b.notifyToolInput(v),\n );\n useBridgeNotify(\n output,\n bridgeRef,\n widgetReadyRef,\n pendingOutputRef,\n lastSentOutputRef,\n (b, v) => b.notifyToolResult(v),\n );\n useBridgeNotify(\n hostContext,\n bridgeRef,\n widgetReadyRef,\n pendingHostContextRef,\n lastSentHostContextRef,\n (b, v) => b.notifyHostContextChanged(v),\n );\n\n const resolvedHeight =\n contentHeight != null ? Math.min(contentHeight, maxHeight) : undefined;\n const mergedStyle =\n resolvedHeight != null\n ? { ...sandbox?.style, height: resolvedHeight }\n : sandbox?.style;\n\n return (\n <div\n ref={containerRef}\n className={sandbox?.className}\n style={mergedStyle}\n data-mcp-app-resource={app.resourceUri}\n data-mcp-app-prefers-border={\n resource.meta?.prefersBorder ? \"\" : undefined\n }\n />\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-frame.js","names":[],"sources":["../../src/mcp-apps/app-frame.tsx"],"sourcesContent":["\"use client\";\n\nimport { type MutableRefObject, useEffect, useRef, useState } from \"react\";\nimport { type RenderedFrame, SafeContentFrame } from \"safe-content-frame\";\nimport { type McpAppBridge, createMcpAppBridge } from \"./bridge\";\nimport type {\n McpAppBridgeHandlers,\n McpAppFrameProps,\n McpAppHostContext,\n} from \"./types\";\n\nconst DEFAULT_PRODUCT = \"assistant-ui-mcp-app\";\nconst INIT_TIMEOUT_MS = 5000;\nconst DEFAULT_MAX_HEIGHT = 800;\n\nfunction useBridgeNotify<T>(\n value: T | undefined,\n bridgeRef: MutableRefObject<McpAppBridge | null>,\n widgetReadyRef: MutableRefObject<boolean>,\n pendingRef: MutableRefObject<T | undefined>,\n lastSentRef: MutableRefObject<T | undefined>,\n notify: (bridge: McpAppBridge, v: T) => void,\n) {\n useEffect(() => {\n if (!bridgeRef.current) return;\n if (value === undefined) return;\n if (lastSentRef.current === value) return;\n if (!widgetReadyRef.current) {\n pendingRef.current = value;\n return;\n }\n notify(bridgeRef.current, value);\n lastSentRef.current = value;\n // oxlint-disable-next-line tap-hooks/exhaustive-deps -- refs are stable; notify is assumed stable; re-run only when value changes\n }, [value]);\n}\n\ntype LiveSnapshot = {\n handlers: McpAppBridgeHandlers | undefined;\n hostInfo: McpAppFrameProps[\"hostInfo\"];\n hostContext: McpAppFrameProps[\"hostContext\"];\n input: unknown;\n output: unknown;\n};\n\n// Proxy each per-call handler through liveRef so the bridge always dispatches\n// to the latest handler reference (e.g. inline callbacks closing over state).\n// Capability presence is snapshot at mount: a handler added later requires a\n// remount (keyed on resource URI) to expose the capability to the widget.\nfunction buildLiveHandlers(\n initial: McpAppBridgeHandlers | undefined,\n liveRef: { readonly current: LiveSnapshot },\n): McpAppBridgeHandlers {\n const live = () => liveRef.current.handlers;\n const has = <K extends keyof McpAppBridgeHandlers>(key: K) =>\n initial?.[key] !== undefined;\n const out: McpAppBridgeHandlers = {};\n if (has(\"allowedTools\")) {\n Object.defineProperty(out, \"allowedTools\", {\n get: () => live()?.allowedTools,\n enumerable: true,\n configurable: true,\n });\n }\n const liveCall = <K extends keyof McpAppBridgeHandlers>(\n key: K,\n ): NonNullable<McpAppBridgeHandlers[K]> =>\n ((p: unknown) => {\n const fn = live()?.[key] as ((p: unknown) => unknown) | undefined;\n if (!fn) {\n throw new Error(`${key} handler is no longer available`);\n }\n return fn(p);\n }) as NonNullable<McpAppBridgeHandlers[K]>;\n if (has(\"callTool\")) out.callTool = liveCall(\"callTool\");\n if (has(\"readResource\")) out.readResource = liveCall(\"readResource\");\n if (has(\"listResources\")) out.listResources = liveCall(\"listResources\");\n if (has(\"openLink\")) out.openLink = liveCall(\"openLink\");\n if (has(\"sendMessage\")) out.sendMessage = liveCall(\"sendMessage\");\n if (has(\"updateModelContext\"))\n out.updateModelContext = liveCall(\"updateModelContext\");\n if (has(\"requestDisplayMode\"))\n out.requestDisplayMode = liveCall(\"requestDisplayMode\");\n out.onSizeChange = (p) => live()?.onSizeChange?.(p);\n out.onInitialized = () => live()?.onInitialized?.();\n out.onRequestTeardown = (p) => live()?.onRequestTeardown?.(p);\n out.onLog = (p) => live()?.onLog?.(p);\n out.onError = (e) => live()?.onError?.(e);\n return out;\n}\n\nexport function McpAppFrame({\n app,\n resource,\n input,\n output,\n sandbox,\n handlers,\n hostInfo,\n hostContext,\n maxHeight = DEFAULT_MAX_HEIGHT,\n}: McpAppFrameProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [contentHeight, setContentHeight] = useState<number | undefined>(\n undefined,\n );\n const bridgeRef = useRef<McpAppBridge | null>(null);\n const lastSentInputRef = useRef<unknown>(undefined);\n const lastSentOutputRef = useRef<unknown>(undefined);\n const lastSentHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n // Per MCP Apps spec, the host should defer notifications until the widget\n // signals readiness via `notifications/initialized`. Until then, we record\n // pending values and flush them on init.\n const widgetReadyRef = useRef(false);\n const pendingInputRef = useRef<unknown>(undefined);\n const pendingOutputRef = useRef<unknown>(undefined);\n const pendingHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n\n const liveRef = useRef<LiveSnapshot>(null!);\n liveRef.current = {\n handlers,\n hostInfo,\n hostContext,\n input,\n output,\n };\n\n const resourceUri = resource.uri;\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n let initTimeoutId: ReturnType<typeof setTimeout> | null = null;\n let frame: RenderedFrame | null = null;\n const sb = sandbox;\n const html = resource.html;\n\n const scf = new SafeContentFrame(sb?.product ?? DEFAULT_PRODUCT, {\n ...(sb?.sandbox !== undefined && { sandbox: sb.sandbox }),\n ...(sb?.useShadowDom !== undefined && { useShadowDom: sb.useShadowDom }),\n ...(sb?.enableBrowserCaching !== undefined && {\n enableBrowserCaching: sb.enableBrowserCaching,\n }),\n ...(sb?.salt !== undefined && { salt: sb.salt }),\n });\n\n const renderOpts =\n sb?.unsafeDocumentWrite !== undefined\n ? { unsafeDocumentWrite: sb.unsafeDocumentWrite }\n : undefined;\n\n scf\n .renderHtml(html, container, renderOpts)\n .then((rendered) => {\n if (cancelled) {\n rendered.dispose();\n return;\n }\n frame = rendered;\n const current = liveRef.current;\n const liveHandlers = buildLiveHandlers(current.handlers, liveRef);\n const liveOnInitialized = liveHandlers.onInitialized;\n const flushPending = () => {\n if (widgetReadyRef.current) return;\n widgetReadyRef.current = true;\n const b = bridgeRef.current;\n if (!b) return;\n if (pendingInputRef.current !== undefined) {\n b.notifyToolInput(pendingInputRef.current);\n lastSentInputRef.current = pendingInputRef.current;\n pendingInputRef.current = undefined;\n }\n if (pendingOutputRef.current !== undefined) {\n b.notifyToolResult(pendingOutputRef.current);\n lastSentOutputRef.current = pendingOutputRef.current;\n pendingOutputRef.current = undefined;\n }\n if (pendingHostContextRef.current !== undefined) {\n b.notifyHostContextChanged(pendingHostContextRef.current);\n lastSentHostContextRef.current = pendingHostContextRef.current;\n pendingHostContextRef.current = undefined;\n }\n };\n const wrappedHandlers: McpAppBridgeHandlers = {\n ...liveHandlers,\n onInitialized: () => {\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n flushPending();\n liveOnInitialized?.();\n },\n onSizeChange: (p) => {\n if (\n typeof p.height === \"number\" &&\n Number.isFinite(p.height) &&\n p.height > 0\n ) {\n setContentHeight(p.height);\n }\n liveHandlers.onSizeChange?.(p);\n },\n };\n // Safety net: if the widget never sends notifications/initialized\n // (broken or non-spec-compliant), flush the queue anyway so the host\n // doesn't appear hung.\n initTimeoutId = setTimeout(() => {\n initTimeoutId = null;\n flushPending();\n }, INIT_TIMEOUT_MS);\n bridgeRef.current = createMcpAppBridge({\n frame: rendered,\n handlers: wrappedHandlers,\n hostInfo: current.hostInfo,\n hostContext: current.hostContext,\n });\n\n if (current.input !== undefined)\n pendingInputRef.current = current.input;\n if (current.output !== undefined)\n pendingOutputRef.current = current.output;\n // hostContext is delivered inside the ui/initialize response; subsequent\n // changes flow through useBridgeNotify's pending path.\n })\n .catch((err) => {\n liveRef.current.handlers?.onError?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n });\n\n return () => {\n cancelled = true;\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n bridgeRef.current?.dispose();\n bridgeRef.current = null;\n frame?.dispose();\n frame = null;\n lastSentInputRef.current = undefined;\n lastSentOutputRef.current = undefined;\n lastSentHostContextRef.current = undefined;\n widgetReadyRef.current = false;\n pendingInputRef.current = undefined;\n pendingOutputRef.current = undefined;\n pendingHostContextRef.current = undefined;\n setContentHeight(undefined);\n };\n // oxlint-disable-next-line tap-hooks/exhaustive-deps -- re-mount only on resource URI change; live values flow through liveRef\n }, [resourceUri]);\n\n useBridgeNotify(\n input,\n bridgeRef,\n widgetReadyRef,\n pendingInputRef,\n lastSentInputRef,\n (b, v) => b.notifyToolInput(v),\n );\n useBridgeNotify(\n output,\n bridgeRef,\n widgetReadyRef,\n pendingOutputRef,\n lastSentOutputRef,\n (b, v) => b.notifyToolResult(v),\n );\n useBridgeNotify(\n hostContext,\n bridgeRef,\n widgetReadyRef,\n pendingHostContextRef,\n lastSentHostContextRef,\n (b, v) => b.notifyHostContextChanged(v),\n );\n\n const resolvedHeight =\n contentHeight != null ? Math.min(contentHeight, maxHeight) : undefined;\n const mergedStyle =\n resolvedHeight != null\n ? { ...sandbox?.style, height: resolvedHeight }\n : sandbox?.style;\n\n return (\n <div\n ref={containerRef}\n className={sandbox?.className}\n style={mergedStyle}\n data-mcp-app-resource={app.resourceUri}\n data-mcp-app-prefers-border={\n resource.meta?.prefersBorder ? \"\" : undefined\n }\n />\n );\n}\n"],"mappings":"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"}
@@ -14,7 +14,11 @@ const useAssistantModalOpenState = ({ defaultOpen = false, unstable_openOnRunSta
14
14
  return aui.on("thread.runStart", () => {
15
15
  setOpen(true);
16
16
  });
17
- }, [unstable_openOnRunStart, aui]);
17
+ }, [
18
+ unstable_openOnRunStart,
19
+ aui,
20
+ setOpen
21
+ ]);
18
22
  return state;
19
23
  };
20
24
  const AssistantModalPrimitiveRoot = ({ __scopeAssistantModal, defaultOpen, unstable_openOnRunStart, open, onOpenChange, ...rest }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantModalRoot.js","names":["PopoverPrimitive"],"sources":["../../../src/primitives/assistantModal/AssistantModalRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, useEffect, useState } from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport { type ScopedProps, usePopoverScope } from \"./scope\";\nimport { useAui } from \"@assistant-ui/store\";\n\nexport namespace AssistantModalPrimitiveRoot {\n export type Props = PopoverPrimitive.PopoverProps & {\n unstable_openOnRunStart?: boolean | undefined;\n };\n}\n\nconst useAssistantModalOpenState = ({\n defaultOpen = false,\n unstable_openOnRunStart = true,\n}: {\n defaultOpen?: boolean | undefined;\n unstable_openOnRunStart?: boolean | undefined;\n}) => {\n const state = useState(defaultOpen);\n\n const [, setOpen] = state;\n const aui = useAui();\n useEffect(() => {\n if (!unstable_openOnRunStart) return undefined;\n\n return aui.on(\"thread.runStart\", () => {\n setOpen(true);\n });\n }, [unstable_openOnRunStart, aui]);\n\n return state;\n};\n\nexport const AssistantModalPrimitiveRoot: FC<\n AssistantModalPrimitiveRoot.Props\n> = ({\n __scopeAssistantModal,\n defaultOpen,\n unstable_openOnRunStart,\n open,\n onOpenChange,\n ...rest\n}: ScopedProps<AssistantModalPrimitiveRoot.Props>) => {\n const scope = usePopoverScope(__scopeAssistantModal);\n\n const [modalOpen, setOpen] = useAssistantModalOpenState({\n defaultOpen,\n unstable_openOnRunStart,\n });\n\n const openChangeHandler = (open: boolean) => {\n onOpenChange?.(open);\n setOpen(open);\n };\n\n return (\n <PopoverPrimitive.Root\n {...scope}\n open={open === undefined ? modalOpen : open}\n onOpenChange={openChangeHandler}\n {...rest}\n />\n );\n};\n\nAssistantModalPrimitiveRoot.displayName = \"AssistantModalPrimitive.Root\";\n"],"mappings":";;;;;;;AAaA,MAAM,8BAA8B,EAClC,cAAc,OACd,0BAA0B,WAItB;CACJ,MAAM,QAAQ,SAAS,WAAW;CAElC,MAAM,GAAG,WAAW;CACpB,MAAM,MAAM,OAAO;CACnB,gBAAgB;EACd,IAAI,CAAC,yBAAyB,OAAO,KAAA;EAErC,OAAO,IAAI,GAAG,yBAAyB;GACrC,QAAQ,IAAI;EACd,CAAC;CACH,GAAG,CAAC,yBAAyB,GAAG,CAAC;CAEjC,OAAO;AACT;AAEA,MAAa,+BAER,EACH,uBACA,aACA,yBACA,MACA,cACA,GAAG,WACiD;CACpD,MAAM,QAAQ,gBAAgB,qBAAqB;CAEnD,MAAM,CAAC,WAAW,WAAW,2BAA2B;EACtD;EACA;CACF,CAAC;CAED,MAAM,qBAAqB,SAAkB;EAC3C,eAAe,IAAI;EACnB,QAAQ,IAAI;CACd;CAEA,OACE,oBAACA,QAAiB,MAAlB;EACE,GAAI;EACJ,MAAM,SAAS,KAAA,IAAY,YAAY;EACvC,cAAc;EACd,GAAI;CACL,CAAA;AAEL;AAEA,4BAA4B,cAAc"}
1
+ {"version":3,"file":"AssistantModalRoot.js","names":["PopoverPrimitive"],"sources":["../../../src/primitives/assistantModal/AssistantModalRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, useEffect, useState } from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport { type ScopedProps, usePopoverScope } from \"./scope\";\nimport { useAui } from \"@assistant-ui/store\";\n\nexport namespace AssistantModalPrimitiveRoot {\n export type Props = PopoverPrimitive.PopoverProps & {\n unstable_openOnRunStart?: boolean | undefined;\n };\n}\n\nconst useAssistantModalOpenState = ({\n defaultOpen = false,\n unstable_openOnRunStart = true,\n}: {\n defaultOpen?: boolean | undefined;\n unstable_openOnRunStart?: boolean | undefined;\n}) => {\n const state = useState(defaultOpen);\n\n const [, setOpen] = state;\n const aui = useAui();\n useEffect(() => {\n if (!unstable_openOnRunStart) return undefined;\n\n return aui.on(\"thread.runStart\", () => {\n setOpen(true);\n });\n }, [unstable_openOnRunStart, aui, setOpen]);\n\n return state;\n};\n\nexport const AssistantModalPrimitiveRoot: FC<\n AssistantModalPrimitiveRoot.Props\n> = ({\n __scopeAssistantModal,\n defaultOpen,\n unstable_openOnRunStart,\n open,\n onOpenChange,\n ...rest\n}: ScopedProps<AssistantModalPrimitiveRoot.Props>) => {\n const scope = usePopoverScope(__scopeAssistantModal);\n\n const [modalOpen, setOpen] = useAssistantModalOpenState({\n defaultOpen,\n unstable_openOnRunStart,\n });\n\n const openChangeHandler = (open: boolean) => {\n onOpenChange?.(open);\n setOpen(open);\n };\n\n return (\n <PopoverPrimitive.Root\n {...scope}\n open={open === undefined ? modalOpen : open}\n onOpenChange={openChangeHandler}\n {...rest}\n />\n );\n};\n\nAssistantModalPrimitiveRoot.displayName = \"AssistantModalPrimitive.Root\";\n"],"mappings":";;;;;;;AAaA,MAAM,8BAA8B,EAClC,cAAc,OACd,0BAA0B,WAItB;CACJ,MAAM,QAAQ,SAAS,WAAW;CAElC,MAAM,GAAG,WAAW;CACpB,MAAM,MAAM,OAAO;CACnB,gBAAgB;EACd,IAAI,CAAC,yBAAyB,OAAO,KAAA;EAErC,OAAO,IAAI,GAAG,yBAAyB;GACrC,QAAQ,IAAI;EACd,CAAC;CACH,GAAG;EAAC;EAAyB;EAAK;CAAO,CAAC;CAE1C,OAAO;AACT;AAEA,MAAa,+BAER,EACH,uBACA,aACA,yBACA,MACA,cACA,GAAG,WACiD;CACpD,MAAM,QAAQ,gBAAgB,qBAAqB;CAEnD,MAAM,CAAC,WAAW,WAAW,2BAA2B;EACtD;EACA;CACF,CAAC;CAED,MAAM,qBAAqB,SAAkB;EAC3C,eAAe,IAAI;EACnB,QAAQ,IAAI;CACd;CAEA,OACE,oBAACA,QAAiB,MAAlB;EACE,GAAI;EACJ,MAAM,SAAS,KAAA,IAAY,YAAY;EACvC,cAAc;EACd,GAAI;CACL,CAAA;AAEL;AAEA,4BAA4B,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"MessagePartsGrouped.d.ts","names":[],"sources":["../../../src/primitives/message/MessagePartsGrouped.tsx"],"mappings":";;;;KA8BK,gBAAA;EACH,QAAA;EACA,OAAO;AAAA;AAAA,KAGG,gBAAA,IAAoB,KAAA,qBAA0B,gBAAgB;AAAA,kBAmDzD,qCAAA;EAAA,KACH,KAAA;IApDF;;;;AAA8D;AAmD1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA6CI,gBAAA,EAAkB,gBAAA;IAqBL;;;;;;IAbb,UAAA;MAwBY,6CArBN,KAAA,GAAQ,yBAAA,cAyBR;MAvBA,IAAA,GAAO,wBAAA,cA2BG;MAzBV,SAAA,GAAY,6BAAA,cA4BN;MA1BN,MAAA,GAAS,0BAAA,cA0BsB;MAxB/B,KAAA,GAAQ,yBAAA,cA4BQ;MA1BhB,IAAA,GAAO,wBAAA,cA+DP;MA7DA,cAAA,GAAiB,kCAAA,cA8Df;MA5DF,IAAA;QA8DI,kDA3DE,OAAA,GACI,MAAA,SAAe,wBAAA,2BA0Dd;QAvDL,QAAA,GAAW,wBAAA;MAAA,eAsRzB;MAlRQ,KAAA;QAiR0C,qDA9QpC,OAAA,GACI,MAAA,SAAe,4BAAA,2BA8QK;QA3QxB,QAAA,GAAW,aAAA,CAAc,wBAAA;MAAA;QAsT5B,qDAlTG,QAAA,EAAU,aAAA,CAAc,wBAAA;MAAA;MAmTjC;;;;;;;;;;AAA2C;;;;;;;;;;;;;;;;;;;;;;;MA9QxC,KAAA,GAAQ,aAAA,CACN,iBAAA;QACE,QAAA;QACA,OAAA;MAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA8ND,qCAAA,EAAuC,EAAE,CACpD,qCAAA,CAAsC,KAAA;;;;;;;cA2C3B,+CAAA,EAAiD,EAAA,CAC5D,IAAA,CAAK,qCAAA,CAAsC,KAAA"}
1
+ {"version":3,"file":"MessagePartsGrouped.d.ts","names":[],"sources":["../../../src/primitives/message/MessagePartsGrouped.tsx"],"mappings":";;;;KA8BK,gBAAA;EACH,QAAA;EACA,OAAO;AAAA;AAAA,KAGG,gBAAA,IAAoB,KAAA,qBAA0B,gBAAgB;AAAA,kBAmDzD,qCAAA;EAAA,KACH,KAAA;IApDF;;;;AAA8D;AAmD1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA6CI,gBAAA,EAAkB,gBAAA;IAqBL;;;;;;IAbb,UAAA;MAwBY,6CArBN,KAAA,GAAQ,yBAAA,cAyBR;MAvBA,IAAA,GAAO,wBAAA,cA2BG;MAzBV,SAAA,GAAY,6BAAA,cA4BN;MA1BN,MAAA,GAAS,0BAAA,cA0BsB;MAxB/B,KAAA,GAAQ,yBAAA,cA4BQ;MA1BhB,IAAA,GAAO,wBAAA,cA+DP;MA7DA,cAAA,GAAiB,kCAAA,cA8Df;MA5DF,IAAA;QA8DI,kDA3DE,OAAA,GACI,MAAA,SAAe,wBAAA,2BA0Dd;QAvDL,QAAA,GAAW,wBAAA;MAAA,eA+RzB;MA3RQ,KAAA;QA0R0C,qDAvRpC,OAAA,GACI,MAAA,SAAe,4BAAA,2BAuRK;QApRxB,QAAA,GAAW,aAAA,CAAc,wBAAA;MAAA;QA+T5B,qDA3TG,QAAA,EAAU,aAAA,CAAc,wBAAA;MAAA;MA4TjC;;;;;;;;;;AAA2C;;;;;;;;;;;;;;;;;;;;;;;MAvRxC,KAAA,GAAQ,aAAA,CACN,iBAAA;QACE,QAAA;QACA,OAAA;MAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAuOD,qCAAA,EAAuC,EAAE,CACpD,qCAAA,CAAsC,KAAA;;;;;;;cA2C3B,+CAAA,EAAiD,EAAA,CAC5D,IAAA,CAAK,qCAAA,CAAsC,KAAA"}
@@ -78,17 +78,20 @@ const MessagePartComponent = ({ components: { Text = defaultComponents.Text, Rea
78
78
  if (type === "tool-call") {
79
79
  const addResult = aui.part().addToolResult;
80
80
  const resume = aui.part().resumeToolCall;
81
+ const respondToApproval = aui.part().respondToToolApproval;
81
82
  if ("Override" in tools) return /* @__PURE__ */ jsx(tools.Override, {
82
83
  ...part,
83
84
  addResult,
84
- resume
85
+ resume,
86
+ respondToApproval
85
87
  });
86
88
  const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;
87
89
  return /* @__PURE__ */ jsx(ToolUIDisplay, {
88
90
  ...part,
89
91
  Fallback: Tool,
90
92
  addResult,
91
- resume
93
+ resume,
94
+ respondToApproval
92
95
  });
93
96
  }
94
97
  if (part.status?.type === "requires-action") throw new Error("Encountered unexpected requires-action status");
@@ -1 +1 @@
1
- {"version":3,"file":"MessagePartsGrouped.js","names":[],"sources":["../../../src/primitives/message/MessagePartsGrouped.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n type FC,\n memo,\n type PropsWithChildren,\n useMemo,\n} from \"react\";\nimport { useAuiState, useAui } from \"@assistant-ui/store\";\nimport { PartByIndexProvider } from \"../../context/providers/PartByIndexProvider\";\nimport { TextMessagePartProvider } from \"../../context/providers/TextMessagePartProvider\";\nimport { MessagePartPrimitiveText } from \"../messagePart/MessagePartText\";\nimport { MessagePartPrimitiveImage } from \"../messagePart/MessagePartImage\";\nimport type {\n Unstable_AudioMessagePartComponent,\n DataMessagePartComponent,\n DataMessagePartProps,\n EmptyMessagePartComponent,\n TextMessagePartComponent,\n ImageMessagePartComponent,\n SourceMessagePartComponent,\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n FileMessagePartComponent,\n ReasoningMessagePartComponent,\n} from \"@assistant-ui/core/react\";\nimport { MessagePartPrimitiveInProgress } from \"../messagePart/MessagePartInProgress\";\nimport type { MessagePartStatus } from \"@assistant-ui/core\";\n\ntype MessagePartGroup = {\n groupKey: string | undefined;\n indices: number[];\n};\n\nexport type GroupingFunction = (parts: readonly any[]) => MessagePartGroup[];\n\n/**\n * Groups message parts by their parent ID.\n * Parts without a parent ID appear in their chronological position as individual groups.\n * Parts with the same parent ID are grouped together at the position of their first occurrence.\n */\nconst groupMessagePartsByParentId: GroupingFunction = (\n parts: readonly any[],\n): MessagePartGroup[] => {\n // Map maintains insertion order, so groups appear in order of first occurrence\n const groupMap = new Map<string, number[]>();\n\n // Process each part in order\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const parentId = part?.parentId as string | undefined;\n\n // For parts without parentId, assign a unique group ID to maintain their position\n const groupId = parentId ?? `__ungrouped_${i}`;\n\n // Get or create the indices array for this group\n const indices = groupMap.get(groupId) ?? [];\n indices.push(i);\n groupMap.set(groupId, indices);\n }\n\n // Convert map to array of groups\n const groups: MessagePartGroup[] = [];\n for (const [groupId, indices] of groupMap) {\n // Extract parentId (undefined for ungrouped parts)\n const groupKey = groupId.startsWith(\"__ungrouped_\") ? undefined : groupId;\n groups.push({ groupKey, indices });\n }\n\n return groups;\n};\n\nconst useMessagePartsGrouped = (\n groupingFunction: GroupingFunction,\n): MessagePartGroup[] => {\n const parts = useAuiState((s) => s.message.parts);\n\n return useMemo(() => {\n if (parts.length === 0) {\n return [];\n }\n return groupingFunction(parts);\n }, [parts, groupingFunction]);\n};\n\nexport namespace MessagePrimitiveUnstable_PartsGrouped {\n export type Props = {\n /**\n * Function that takes an array of message parts and returns an array of groups.\n * Each group contains a key (for identification) and an array of indices.\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * groupingFunction={(parts) => {\n * const groups = new Map<string, number[]>();\n * parts.forEach((part, i) => {\n * const key = part.parentId ?? `__ungrouped_${i}`;\n * const indices = groups.get(key) ?? [];\n * indices.push(i);\n * groups.set(key, indices);\n * });\n * return Array.from(groups.entries()).map(([key, indices]) => ({\n * key: key.startsWith(\"__ungrouped_\") ? undefined : key,\n * indices\n * }));\n * }}\n * ```\n *\n * @example\n * ```tsx\n * // Group by tool name\n * import { groupMessagePartsByToolName } from \"@assistant-ui/react\";\n *\n * <MessagePrimitive.Unstable_PartsGrouped\n * groupingFunction={groupMessagePartsByToolName}\n * components={{\n * Group: ({ key, indices, children }) => {\n * if (!key) return <>{children}</>;\n * return (\n * <div className=\"tool-group\">\n * <h4>Tool: {key}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\n groupingFunction: GroupingFunction;\n\n /**\n * Component configuration for rendering different types of message content.\n *\n * You can provide custom components for each content type (text, image, file, etc.)\n * and configure tool rendering behavior. If not provided, default components will be used.\n */\n components:\n | {\n /** Component for rendering empty messages */\n Empty?: EmptyMessagePartComponent | undefined;\n /** Component for rendering text content */\n Text?: TextMessagePartComponent | undefined;\n /** Component for rendering reasoning content (typically hidden) */\n Reasoning?: ReasoningMessagePartComponent | undefined;\n /** Component for rendering source content */\n Source?: SourceMessagePartComponent | undefined;\n /** Component for rendering image content */\n Image?: ImageMessagePartComponent | undefined;\n /** Component for rendering file content */\n File?: FileMessagePartComponent | undefined;\n /** Component for rendering audio content (experimental) */\n Unstable_Audio?: Unstable_AudioMessagePartComponent | undefined;\n /** Configuration for data part rendering */\n data?:\n | {\n /** Map data event names to specific components */\n by_name?:\n | Record<string, DataMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unmatched data events */\n Fallback?: DataMessagePartComponent | undefined;\n }\n | undefined;\n /** Configuration for tool call rendering */\n tools?:\n | {\n /** Map of tool names to their specific components */\n by_name?:\n | Record<string, ToolCallMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unregistered tools */\n Fallback?: ComponentType<ToolCallMessagePartProps> | undefined;\n }\n | {\n /** Override component that handles all tool calls */\n Override: ComponentType<ToolCallMessagePartProps>;\n }\n | undefined;\n\n /**\n * Component for rendering grouped message parts.\n *\n * When provided, this component will automatically wrap message parts that share\n * the same group key as determined by the groupingFunction.\n *\n * The component receives:\n * - `groupKey`: The group key (or undefined for ungrouped parts)\n * - `indices`: Array of indices for the parts in this group\n * - `children`: The rendered message part components\n *\n * @example\n * ```tsx\n * // Collapsible group\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <details className=\"message-group\">\n * <summary>\n * Group {groupKey} ({indices.length} parts)\n * </summary>\n * <div className=\"group-content\">\n * {children}\n * </div>\n * </details>\n * );\n * }\n * ```\n *\n * @param groupKey - The group key (undefined for ungrouped parts)\n * @param indices - Array of indices for the parts in this group\n * @param children - Rendered message part components to display within the group\n */\n Group?: ComponentType<\n PropsWithChildren<{\n groupKey: string | undefined;\n indices: number[];\n }>\n >;\n }\n | undefined;\n };\n}\n\nconst ToolUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: ToolCallMessagePartComponent | undefined;\n} & ToolCallMessagePartProps) => {\n const Render = useAuiState((s) => {\n const Render = s.tools.tools[props.toolName] ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst DataUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: DataMessagePartComponent | undefined;\n} & DataMessagePartProps) => {\n const Render = useAuiState((s) => {\n const Render = s.dataRenderers.renderers[props.name] ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst defaultComponents = {\n Text: () => (\n <p style={{ whiteSpace: \"pre-line\" }}>\n <MessagePartPrimitiveText />\n <MessagePartPrimitiveInProgress>\n <span style={{ fontFamily: \"revert\" }}>{\" \\u25CF\"}</span>\n </MessagePartPrimitiveInProgress>\n </p>\n ),\n Reasoning: () => null,\n Source: () => null,\n Image: () => <MessagePartPrimitiveImage />,\n File: () => null,\n Unstable_Audio: () => null,\n Group: ({ children }) => children,\n} satisfies MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n\ntype MessagePartComponentProps = {\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartComponent: FC<MessagePartComponentProps> = ({\n components: {\n Text = defaultComponents.Text,\n Reasoning = defaultComponents.Reasoning,\n Image = defaultComponents.Image,\n Source = defaultComponents.Source,\n File = defaultComponents.File,\n Unstable_Audio: Audio = defaultComponents.Unstable_Audio,\n tools = {},\n data,\n } = {},\n}) => {\n const aui = useAui();\n const part = useAuiState((s) => s.part);\n\n const type = part.type;\n if (type === \"tool-call\") {\n const addResult = aui.part().addToolResult;\n const resume = aui.part().resumeToolCall;\n if (\"Override\" in tools)\n return <tools.Override {...part} addResult={addResult} resume={resume} />;\n const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;\n return (\n <ToolUIDisplay\n {...part}\n Fallback={Tool}\n addResult={addResult}\n resume={resume}\n />\n );\n }\n\n if (part.status?.type === \"requires-action\")\n throw new Error(\"Encountered unexpected requires-action status\");\n\n switch (type) {\n case \"text\":\n return <Text {...part} />;\n\n case \"reasoning\":\n return <Reasoning {...part} />;\n\n case \"source\":\n return <Source {...part} />;\n\n case \"image\":\n return <Image {...part} />;\n\n case \"file\":\n return <File {...part} />;\n\n case \"audio\":\n return <Audio {...part} />;\n\n case \"data\": {\n const Data = data?.by_name?.[part.name] ?? data?.Fallback;\n return <DataUIDisplay {...part} Fallback={Data} />;\n }\n\n default:\n console.warn(`Unknown message part type: ${type}`);\n return null;\n }\n};\n\ntype MessagePartProps = {\n partIndex: number;\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartImpl: FC<MessagePartProps> = ({ partIndex, components }) => {\n return (\n <PartByIndexProvider index={partIndex}>\n <MessagePartComponent components={components} />\n </PartByIndexProvider>\n );\n};\n\nconst MessagePart = memo(\n MessagePartImpl,\n (prev, next) =>\n prev.partIndex === next.partIndex &&\n prev.components?.Text === next.components?.Text &&\n prev.components?.Reasoning === next.components?.Reasoning &&\n prev.components?.Source === next.components?.Source &&\n prev.components?.Image === next.components?.Image &&\n prev.components?.File === next.components?.File &&\n prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&\n prev.components?.tools === next.components?.tools &&\n prev.components?.data === next.components?.data &&\n prev.components?.Group === next.components?.Group,\n);\n\nconst EmptyPartFallback: FC<{\n status: MessagePartStatus;\n component: TextMessagePartComponent;\n}> = ({ status, component: Component }) => {\n return (\n <TextMessagePartProvider text=\"\" isRunning={status.type === \"running\"}>\n <Component type=\"text\" text=\"\" status={status} />\n </TextMessagePartProvider>\n );\n};\n\nconst COMPLETE_STATUS: MessagePartStatus = Object.freeze({\n type: \"complete\",\n});\n\nconst EmptyPartsImpl: FC<MessagePartComponentProps> = ({ components }) => {\n const status = useAuiState(\n (s) => (s.message.status ?? COMPLETE_STATUS) as MessagePartStatus,\n );\n\n if (components?.Empty) return <components.Empty status={status} />;\n\n return (\n <EmptyPartFallback\n status={status}\n component={components?.Text ?? defaultComponents.Text}\n />\n );\n};\n\nconst EmptyParts = memo(\n EmptyPartsImpl,\n (prev, next) =>\n prev.components?.Empty === next.components?.Empty &&\n prev.components?.Text === next.components?.Text,\n);\n\n/**\n * Renders the parts of a message grouped by a custom grouping function.\n *\n * This component allows you to group message parts based on any criteria you define.\n * The grouping function receives all message parts and returns an array of groups,\n * where each group has a key and an array of part indices.\n *\n * @deprecated Prefer `<MessagePrimitive.GroupedParts>` for adjacent\n * grouping — it dispatches all rendering through one `switch (part.type)`\n * and supports nested group paths. Keep this primitive only for\n * non-adjacent clustering (e.g., gathering parts with the same parent-id\n * across the message).\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * <MessagePrimitive.Unstable_PartsGrouped\n * components={{\n * Text: ({ text }) => <p className=\"message-text\">{text}</p>,\n * Image: ({ image }) => <img src={image} alt=\"Message image\" />,\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <div className=\"parent-group border rounded p-4\">\n * <h4>Parent ID: {groupKey}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\nexport const MessagePrimitiveUnstable_PartsGrouped: FC<\n MessagePrimitiveUnstable_PartsGrouped.Props\n> = ({ groupingFunction, components }) => {\n const contentLength = useAuiState((s) => s.message.parts.length);\n const messageGroups = useMessagePartsGrouped(groupingFunction);\n\n const partsElements = useMemo(() => {\n if (contentLength === 0) {\n return <EmptyParts components={components} />;\n }\n\n return messageGroups.map((group, groupIndex) => {\n const GroupComponent = components?.Group ?? defaultComponents.Group;\n\n return (\n <GroupComponent\n key={`group-${groupIndex}-${group.groupKey ?? \"ungrouped\"}`}\n groupKey={group.groupKey}\n indices={group.indices}\n >\n {group.indices.map((partIndex) => (\n <MessagePart\n key={partIndex}\n partIndex={partIndex}\n components={components}\n />\n ))}\n </GroupComponent>\n );\n });\n }, [messageGroups, components, contentLength]);\n\n return <>{partsElements}</>;\n};\n\nMessagePrimitiveUnstable_PartsGrouped.displayName =\n \"MessagePrimitive.Unstable_PartsGrouped\";\n\n/**\n * Renders the parts of a message grouped by their parent ID.\n * This is a convenience wrapper around Unstable_PartsGrouped with parent ID grouping.\n *\n * @deprecated Use MessagePrimitive.Unstable_PartsGrouped instead for more flexibility\n */\nexport const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<\n Omit<MessagePrimitiveUnstable_PartsGrouped.Props, \"groupingFunction\">\n> = ({ components, ...props }) => {\n return (\n <MessagePrimitiveUnstable_PartsGrouped\n {...props}\n components={components}\n groupingFunction={groupMessagePartsByParentId}\n />\n );\n};\n\nMessagePrimitiveUnstable_PartsGroupedByParentId.displayName =\n \"MessagePrimitive.Unstable_PartsGroupedByParentId\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"MessagePartsGrouped.js","names":[],"sources":["../../../src/primitives/message/MessagePartsGrouped.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n type FC,\n memo,\n type PropsWithChildren,\n useMemo,\n} from \"react\";\nimport { useAuiState, useAui } from \"@assistant-ui/store\";\nimport { PartByIndexProvider } from \"../../context/providers/PartByIndexProvider\";\nimport { TextMessagePartProvider } from \"../../context/providers/TextMessagePartProvider\";\nimport { MessagePartPrimitiveText } from \"../messagePart/MessagePartText\";\nimport { MessagePartPrimitiveImage } from \"../messagePart/MessagePartImage\";\nimport type {\n Unstable_AudioMessagePartComponent,\n DataMessagePartComponent,\n DataMessagePartProps,\n EmptyMessagePartComponent,\n TextMessagePartComponent,\n ImageMessagePartComponent,\n SourceMessagePartComponent,\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n FileMessagePartComponent,\n ReasoningMessagePartComponent,\n} from \"@assistant-ui/core/react\";\nimport { MessagePartPrimitiveInProgress } from \"../messagePart/MessagePartInProgress\";\nimport type { MessagePartStatus } from \"@assistant-ui/core\";\n\ntype MessagePartGroup = {\n groupKey: string | undefined;\n indices: number[];\n};\n\nexport type GroupingFunction = (parts: readonly any[]) => MessagePartGroup[];\n\n/**\n * Groups message parts by their parent ID.\n * Parts without a parent ID appear in their chronological position as individual groups.\n * Parts with the same parent ID are grouped together at the position of their first occurrence.\n */\nconst groupMessagePartsByParentId: GroupingFunction = (\n parts: readonly any[],\n): MessagePartGroup[] => {\n // Map maintains insertion order, so groups appear in order of first occurrence\n const groupMap = new Map<string, number[]>();\n\n // Process each part in order\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const parentId = part?.parentId as string | undefined;\n\n // For parts without parentId, assign a unique group ID to maintain their position\n const groupId = parentId ?? `__ungrouped_${i}`;\n\n // Get or create the indices array for this group\n const indices = groupMap.get(groupId) ?? [];\n indices.push(i);\n groupMap.set(groupId, indices);\n }\n\n // Convert map to array of groups\n const groups: MessagePartGroup[] = [];\n for (const [groupId, indices] of groupMap) {\n // Extract parentId (undefined for ungrouped parts)\n const groupKey = groupId.startsWith(\"__ungrouped_\") ? undefined : groupId;\n groups.push({ groupKey, indices });\n }\n\n return groups;\n};\n\nconst useMessagePartsGrouped = (\n groupingFunction: GroupingFunction,\n): MessagePartGroup[] => {\n const parts = useAuiState((s) => s.message.parts);\n\n return useMemo(() => {\n if (parts.length === 0) {\n return [];\n }\n return groupingFunction(parts);\n }, [parts, groupingFunction]);\n};\n\nexport namespace MessagePrimitiveUnstable_PartsGrouped {\n export type Props = {\n /**\n * Function that takes an array of message parts and returns an array of groups.\n * Each group contains a key (for identification) and an array of indices.\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * groupingFunction={(parts) => {\n * const groups = new Map<string, number[]>();\n * parts.forEach((part, i) => {\n * const key = part.parentId ?? `__ungrouped_${i}`;\n * const indices = groups.get(key) ?? [];\n * indices.push(i);\n * groups.set(key, indices);\n * });\n * return Array.from(groups.entries()).map(([key, indices]) => ({\n * key: key.startsWith(\"__ungrouped_\") ? undefined : key,\n * indices\n * }));\n * }}\n * ```\n *\n * @example\n * ```tsx\n * // Group by tool name\n * import { groupMessagePartsByToolName } from \"@assistant-ui/react\";\n *\n * <MessagePrimitive.Unstable_PartsGrouped\n * groupingFunction={groupMessagePartsByToolName}\n * components={{\n * Group: ({ key, indices, children }) => {\n * if (!key) return <>{children}</>;\n * return (\n * <div className=\"tool-group\">\n * <h4>Tool: {key}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\n groupingFunction: GroupingFunction;\n\n /**\n * Component configuration for rendering different types of message content.\n *\n * You can provide custom components for each content type (text, image, file, etc.)\n * and configure tool rendering behavior. If not provided, default components will be used.\n */\n components:\n | {\n /** Component for rendering empty messages */\n Empty?: EmptyMessagePartComponent | undefined;\n /** Component for rendering text content */\n Text?: TextMessagePartComponent | undefined;\n /** Component for rendering reasoning content (typically hidden) */\n Reasoning?: ReasoningMessagePartComponent | undefined;\n /** Component for rendering source content */\n Source?: SourceMessagePartComponent | undefined;\n /** Component for rendering image content */\n Image?: ImageMessagePartComponent | undefined;\n /** Component for rendering file content */\n File?: FileMessagePartComponent | undefined;\n /** Component for rendering audio content (experimental) */\n Unstable_Audio?: Unstable_AudioMessagePartComponent | undefined;\n /** Configuration for data part rendering */\n data?:\n | {\n /** Map data event names to specific components */\n by_name?:\n | Record<string, DataMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unmatched data events */\n Fallback?: DataMessagePartComponent | undefined;\n }\n | undefined;\n /** Configuration for tool call rendering */\n tools?:\n | {\n /** Map of tool names to their specific components */\n by_name?:\n | Record<string, ToolCallMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unregistered tools */\n Fallback?: ComponentType<ToolCallMessagePartProps> | undefined;\n }\n | {\n /** Override component that handles all tool calls */\n Override: ComponentType<ToolCallMessagePartProps>;\n }\n | undefined;\n\n /**\n * Component for rendering grouped message parts.\n *\n * When provided, this component will automatically wrap message parts that share\n * the same group key as determined by the groupingFunction.\n *\n * The component receives:\n * - `groupKey`: The group key (or undefined for ungrouped parts)\n * - `indices`: Array of indices for the parts in this group\n * - `children`: The rendered message part components\n *\n * @example\n * ```tsx\n * // Collapsible group\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <details className=\"message-group\">\n * <summary>\n * Group {groupKey} ({indices.length} parts)\n * </summary>\n * <div className=\"group-content\">\n * {children}\n * </div>\n * </details>\n * );\n * }\n * ```\n *\n * @param groupKey - The group key (undefined for ungrouped parts)\n * @param indices - Array of indices for the parts in this group\n * @param children - Rendered message part components to display within the group\n */\n Group?: ComponentType<\n PropsWithChildren<{\n groupKey: string | undefined;\n indices: number[];\n }>\n >;\n }\n | undefined;\n };\n}\n\nconst ToolUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: ToolCallMessagePartComponent | undefined;\n} & ToolCallMessagePartProps) => {\n const Render = useAuiState((s) => {\n const Render = s.tools.tools[props.toolName] ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst DataUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: DataMessagePartComponent | undefined;\n} & DataMessagePartProps) => {\n const Render = useAuiState((s) => {\n const Render = s.dataRenderers.renderers[props.name] ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst defaultComponents = {\n Text: () => (\n <p style={{ whiteSpace: \"pre-line\" }}>\n <MessagePartPrimitiveText />\n <MessagePartPrimitiveInProgress>\n <span style={{ fontFamily: \"revert\" }}>{\" \\u25CF\"}</span>\n </MessagePartPrimitiveInProgress>\n </p>\n ),\n Reasoning: () => null,\n Source: () => null,\n Image: () => <MessagePartPrimitiveImage />,\n File: () => null,\n Unstable_Audio: () => null,\n Group: ({ children }) => children,\n} satisfies MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n\ntype MessagePartComponentProps = {\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartComponent: FC<MessagePartComponentProps> = ({\n components: {\n Text = defaultComponents.Text,\n Reasoning = defaultComponents.Reasoning,\n Image = defaultComponents.Image,\n Source = defaultComponents.Source,\n File = defaultComponents.File,\n Unstable_Audio: Audio = defaultComponents.Unstable_Audio,\n tools = {},\n data,\n } = {},\n}) => {\n const aui = useAui();\n const part = useAuiState((s) => s.part);\n\n const type = part.type;\n if (type === \"tool-call\") {\n const addResult = aui.part().addToolResult;\n const resume = aui.part().resumeToolCall;\n const respondToApproval = aui.part().respondToToolApproval;\n if (\"Override\" in tools)\n return (\n <tools.Override\n {...part}\n addResult={addResult}\n resume={resume}\n respondToApproval={respondToApproval}\n />\n );\n const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;\n return (\n <ToolUIDisplay\n {...part}\n Fallback={Tool}\n addResult={addResult}\n resume={resume}\n respondToApproval={respondToApproval}\n />\n );\n }\n\n if (part.status?.type === \"requires-action\")\n throw new Error(\"Encountered unexpected requires-action status\");\n\n switch (type) {\n case \"text\":\n return <Text {...part} />;\n\n case \"reasoning\":\n return <Reasoning {...part} />;\n\n case \"source\":\n return <Source {...part} />;\n\n case \"image\":\n return <Image {...part} />;\n\n case \"file\":\n return <File {...part} />;\n\n case \"audio\":\n return <Audio {...part} />;\n\n case \"data\": {\n const Data = data?.by_name?.[part.name] ?? data?.Fallback;\n return <DataUIDisplay {...part} Fallback={Data} />;\n }\n\n default:\n console.warn(`Unknown message part type: ${type}`);\n return null;\n }\n};\n\ntype MessagePartProps = {\n partIndex: number;\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartImpl: FC<MessagePartProps> = ({ partIndex, components }) => {\n return (\n <PartByIndexProvider index={partIndex}>\n <MessagePartComponent components={components} />\n </PartByIndexProvider>\n );\n};\n\nconst MessagePart = memo(\n MessagePartImpl,\n (prev, next) =>\n prev.partIndex === next.partIndex &&\n prev.components?.Text === next.components?.Text &&\n prev.components?.Reasoning === next.components?.Reasoning &&\n prev.components?.Source === next.components?.Source &&\n prev.components?.Image === next.components?.Image &&\n prev.components?.File === next.components?.File &&\n prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&\n prev.components?.tools === next.components?.tools &&\n prev.components?.data === next.components?.data &&\n prev.components?.Group === next.components?.Group,\n);\n\nconst EmptyPartFallback: FC<{\n status: MessagePartStatus;\n component: TextMessagePartComponent;\n}> = ({ status, component: Component }) => {\n return (\n <TextMessagePartProvider text=\"\" isRunning={status.type === \"running\"}>\n <Component type=\"text\" text=\"\" status={status} />\n </TextMessagePartProvider>\n );\n};\n\nconst COMPLETE_STATUS: MessagePartStatus = Object.freeze({\n type: \"complete\",\n});\n\nconst EmptyPartsImpl: FC<MessagePartComponentProps> = ({ components }) => {\n const status = useAuiState(\n (s) => (s.message.status ?? COMPLETE_STATUS) as MessagePartStatus,\n );\n\n if (components?.Empty) return <components.Empty status={status} />;\n\n return (\n <EmptyPartFallback\n status={status}\n component={components?.Text ?? defaultComponents.Text}\n />\n );\n};\n\nconst EmptyParts = memo(\n EmptyPartsImpl,\n (prev, next) =>\n prev.components?.Empty === next.components?.Empty &&\n prev.components?.Text === next.components?.Text,\n);\n\n/**\n * Renders the parts of a message grouped by a custom grouping function.\n *\n * This component allows you to group message parts based on any criteria you define.\n * The grouping function receives all message parts and returns an array of groups,\n * where each group has a key and an array of part indices.\n *\n * @deprecated Prefer `<MessagePrimitive.GroupedParts>` for adjacent\n * grouping — it dispatches all rendering through one `switch (part.type)`\n * and supports nested group paths. Keep this primitive only for\n * non-adjacent clustering (e.g., gathering parts with the same parent-id\n * across the message).\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * <MessagePrimitive.Unstable_PartsGrouped\n * components={{\n * Text: ({ text }) => <p className=\"message-text\">{text}</p>,\n * Image: ({ image }) => <img src={image} alt=\"Message image\" />,\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <div className=\"parent-group border rounded p-4\">\n * <h4>Parent ID: {groupKey}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\nexport const MessagePrimitiveUnstable_PartsGrouped: FC<\n MessagePrimitiveUnstable_PartsGrouped.Props\n> = ({ groupingFunction, components }) => {\n const contentLength = useAuiState((s) => s.message.parts.length);\n const messageGroups = useMessagePartsGrouped(groupingFunction);\n\n const partsElements = useMemo(() => {\n if (contentLength === 0) {\n return <EmptyParts components={components} />;\n }\n\n return messageGroups.map((group, groupIndex) => {\n const GroupComponent = components?.Group ?? defaultComponents.Group;\n\n return (\n <GroupComponent\n key={`group-${groupIndex}-${group.groupKey ?? \"ungrouped\"}`}\n groupKey={group.groupKey}\n indices={group.indices}\n >\n {group.indices.map((partIndex) => (\n <MessagePart\n key={partIndex}\n partIndex={partIndex}\n components={components}\n />\n ))}\n </GroupComponent>\n );\n });\n }, [messageGroups, components, contentLength]);\n\n return <>{partsElements}</>;\n};\n\nMessagePrimitiveUnstable_PartsGrouped.displayName =\n \"MessagePrimitive.Unstable_PartsGrouped\";\n\n/**\n * Renders the parts of a message grouped by their parent ID.\n * This is a convenience wrapper around Unstable_PartsGrouped with parent ID grouping.\n *\n * @deprecated Use MessagePrimitive.Unstable_PartsGrouped instead for more flexibility\n */\nexport const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<\n Omit<MessagePrimitiveUnstable_PartsGrouped.Props, \"groupingFunction\">\n> = ({ components, ...props }) => {\n return (\n <MessagePrimitiveUnstable_PartsGrouped\n {...props}\n components={components}\n groupingFunction={groupMessagePartsByParentId}\n />\n );\n};\n\nMessagePrimitiveUnstable_PartsGroupedByParentId.displayName =\n \"MessagePrimitive.Unstable_PartsGroupedByParentId\";\n"],"mappings":"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"}
@@ -1,7 +1,5 @@
1
1
  //#region src/utils/useToolArgsFieldStatus.d.ts
2
2
  declare const useToolArgsFieldStatus: (fieldPath: (string | number)[]) => {
3
- type: string;
4
- } | {
5
3
  readonly type: "running";
6
4
  } | {
7
5
  readonly type: "complete";
@@ -9,6 +7,8 @@ declare const useToolArgsFieldStatus: (fieldPath: (string | number)[]) => {
9
7
  readonly type: "incomplete";
10
8
  readonly reason: "cancelled" | "length" | "content-filter" | "other" | "error";
11
9
  readonly error?: unknown;
10
+ } | {
11
+ type: string;
12
12
  };
13
13
  //#endregion
14
14
  export { useToolArgsFieldStatus };
@@ -1 +1 @@
1
- {"version":3,"file":"useToolArgsFieldStatus.d.ts","names":[],"sources":["../../src/utils/useToolArgsFieldStatus.ts"],"mappings":";cAKa,sBAAA,GAA0B,SAAA"}
1
+ {"version":3,"file":"useToolArgsFieldStatus.d.ts","names":[],"sources":["../../src/utils/useToolArgsFieldStatus.ts"],"mappings":";cAKa,sBAAA,GAA0B,SAAA;EAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react",
3
- "version": "0.14.8",
3
+ "version": "0.14.11",
4
4
  "description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
5
5
  "keywords": [
6
6
  "radix-ui",
@@ -48,9 +48,9 @@
48
48
  ],
49
49
  "sideEffects": false,
50
50
  "dependencies": {
51
- "@assistant-ui/core": "^0.2.5",
51
+ "@assistant-ui/core": "^0.2.7",
52
52
  "@assistant-ui/store": "^0.2.12",
53
- "@assistant-ui/tap": "^0.5.12",
53
+ "@assistant-ui/tap": "^0.5.13",
54
54
  "@radix-ui/primitive": "^1.1.3",
55
55
  "@radix-ui/react-compose-refs": "^1.1.2",
56
56
  "@radix-ui/react-context": "^1.1.3",
@@ -58,7 +58,7 @@
58
58
  "@radix-ui/react-use-callback-ref": "^1.1.1",
59
59
  "@radix-ui/react-use-escape-keydown": "^1.1.1",
60
60
  "assistant-cloud": "^0.1.29",
61
- "assistant-stream": "^0.3.16",
61
+ "assistant-stream": "^0.3.17",
62
62
  "nanoid": "^5.1.11",
63
63
  "radix-ui": "^1.4.3",
64
64
  "react-textarea-autosize": "^8.5.9",
@@ -220,6 +220,7 @@ const PartResource = resource(
220
220
  getState: () => state,
221
221
  addToolResult: () => {},
222
222
  resumeToolCall: () => {},
223
+ respondToToolApproval: () => {},
223
224
  };
224
225
  },
225
226
  );
package/src/index.ts CHANGED
@@ -254,6 +254,7 @@ export * as ThreadListItemPrimitive from "./primitives/threadListItem";
254
254
  export * as ThreadListItemMorePrimitive from "./primitives/threadListItemMore";
255
255
  export * as SelectionToolbarPrimitive from "./primitives/selectionToolbar";
256
256
 
257
+ export { groupPartByType } from "@assistant-ui/core/react";
257
258
  export { useMessagePartText } from "./primitives/messagePart/useMessagePartText";
258
259
  export { useMessagePartReasoning } from "./primitives/messagePart/useMessagePartReasoning";
259
260
  export { useMessagePartSource } from "./primitives/messagePart/useMessagePartSource";
package/src/internal.ts CHANGED
@@ -23,10 +23,7 @@ export type {
23
23
 
24
24
  // React-specific (stay in react)
25
25
  export { splitLocalRuntimeOptions } from "./legacy-runtime/runtime-cores/local/LocalRuntimeOptions";
26
- export {
27
- useToolInvocations,
28
- type ToolExecutionStatus,
29
- } from "@assistant-ui/core/react";
26
+ export type { ToolExecutionStatus } from "@assistant-ui/core";
30
27
 
31
28
  export { useSmooth } from "./utils/smooth/useSmooth";
32
29
  export {
@@ -1,7 +1,4 @@
1
- export type {
2
- ThreadListState,
3
- ThreadListRuntime,
4
- } from "@assistant-ui/core";
1
+ export type { ThreadListState, ThreadListRuntime } from "@assistant-ui/core";
5
2
 
6
3
  export type { ThreadListRuntimeCoreBinding } from "@assistant-ui/core/internal";
7
4
  export { ThreadListRuntimeImpl } from "@assistant-ui/core/internal";
@@ -1,3 +1,4 @@
1
+ import type { ToolModelContentPart } from "assistant-stream";
1
2
  import type { ThreadMessage } from "@assistant-ui/core";
2
3
  import type { ReadonlyJSONValue } from "assistant-stream/utils";
3
4
  import type {
@@ -7,7 +8,7 @@ import type {
7
8
  LanguageModelConfig,
8
9
  } from "@assistant-ui/core";
9
10
  import type { UserCommands } from "../../../augmentations";
10
- import type { ToolExecutionStatus } from "./useToolInvocations";
11
+ import type { ToolExecutionStatus } from "@assistant-ui/core";
11
12
 
12
13
  // Message part types
13
14
  export type TextPart = {
@@ -47,6 +48,7 @@ export type AddToolResultCommand = {
47
48
  readonly result: ReadonlyJSONValue;
48
49
  readonly isError: boolean;
49
50
  readonly artifact?: ReadonlyJSONValue;
51
+ readonly modelContent?: readonly ToolModelContentPart[];
50
52
  };
51
53
 
52
54
  export type AssistantTransportCommand =
@@ -9,7 +9,7 @@ import {
9
9
  import { useExternalStoreRuntime } from "../external-store/useExternalStoreRuntime";
10
10
  import type { AssistantRuntime } from "../../runtime/AssistantRuntime";
11
11
  import type { AddToolResultOptions } from "@assistant-ui/core";
12
- import { useState, useRef, useMemo } from "react";
12
+ import { useMemo, useRef, useState } from "react";
13
13
  import {
14
14
  AssistantMessageAccumulator,
15
15
  DataStreamDecoder,
@@ -29,10 +29,7 @@ import type {
29
29
  import { useCommandQueue } from "./commandQueue";
30
30
  import { useRunManager } from "./runManager";
31
31
  import { useConvertedState } from "./useConvertedState";
32
- import {
33
- type ToolExecutionStatus,
34
- useToolInvocations,
35
- } from "./useToolInvocations";
32
+ import type { ToolExecutionStatus } from "@assistant-ui/core";
36
33
  import { createRequestHeaders } from "@assistant-ui/core";
37
34
  import { useRemoteThreadListRuntime } from "../remote-thread-list/useRemoteThreadListRuntime";
38
35
  import { InMemoryThreadListAdapter } from "@assistant-ui/core";
@@ -303,6 +300,8 @@ const useAssistantTransportThreadRuntime = <T>(
303
300
  state: converted.state,
304
301
  isRunning: converted.isRunning,
305
302
  adapters: options.adapters,
303
+ unstable_enableToolInvocations: true,
304
+ setToolStatuses,
306
305
  extras: {
307
306
  [symbolAssistantTransportExtras]: true,
308
307
  sendCommand: (command: AssistantTransportCommand) => {
@@ -324,7 +323,6 @@ const useAssistantTransportThreadRuntime = <T>(
324
323
  }),
325
324
  onCancel: async () => {
326
325
  runManager.cancel();
327
- await toolInvocations.abort();
328
326
  },
329
327
  onResume: async () => {
330
328
  if (!options.resumeApi)
@@ -343,25 +341,19 @@ const useAssistantTransportThreadRuntime = <T>(
343
341
  toolName: toolOptions.toolName,
344
342
  isError: toolOptions.isError,
345
343
  ...(toolOptions.artifact && { artifact: toolOptions.artifact }),
344
+ ...(toolOptions.modelContent !== undefined && {
345
+ modelContent: toolOptions.modelContent,
346
+ }),
346
347
  };
347
348
 
348
349
  commandQueue.enqueue(command);
349
350
  },
350
351
  onLoadExternalState: async (state) => {
351
352
  agentStateRef.current = state as T;
352
- toolInvocations.reset();
353
353
  rerender((prev) => prev + 1);
354
354
  },
355
355
  });
356
356
 
357
- // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
358
- const toolInvocations = useToolInvocations({
359
- state: converted,
360
- getTools: () => runtime.thread.getModelContext().tools,
361
- onResult: commandQueue.enqueue,
362
- setToolStatuses,
363
- });
364
-
365
357
  return runtime;
366
358
  };
367
359
 
@@ -4,7 +4,7 @@ import type {
4
4
  AssistantTransportState,
5
5
  AssistantTransportStateConverter,
6
6
  } from "./types";
7
- import type { ToolExecutionStatus } from "./useToolInvocations";
7
+ import type { ToolExecutionStatus } from "@assistant-ui/core";
8
8
 
9
9
  export function useConvertedState<T>(
10
10
  converter: AssistantTransportStateConverter<T>,
@@ -113,7 +113,6 @@ function InlineRenderer({
113
113
  const [loadedResource, setLoadedResource] = useState<LoadedResourceState>();
114
114
 
115
115
  const resourceUri = appForRender?.resourceUri;
116
- // biome-ignore lint/correctness/useExhaustiveDependencies: re-fetches only when URI changes; mcp.app object identity is unstable across renders
117
116
  useEffect(() => {
118
117
  if (appForRender == null || resourceUri == null) return;
119
118
  let cancelled = false;
@@ -137,6 +136,7 @@ function InlineRenderer({
137
136
  return () => {
138
137
  cancelled = true;
139
138
  };
139
+ // oxlint-disable-next-line tap-hooks/exhaustive-deps -- re-fetch only when URI changes; appForRender identity is unstable and internalsRef is a stable ref
140
140
  }, [resourceUri]);
141
141
 
142
142
  const bridgeHandlers = useMemo<McpAppBridgeHandlers>(
@@ -21,7 +21,6 @@ function useBridgeNotify<T>(
21
21
  lastSentRef: MutableRefObject<T | undefined>,
22
22
  notify: (bridge: McpAppBridge, v: T) => void,
23
23
  ) {
24
- // biome-ignore lint/correctness/useExhaustiveDependencies: refs and notify are stable; we re-run only when value changes.
25
24
  useEffect(() => {
26
25
  if (!bridgeRef.current) return;
27
26
  if (value === undefined) return;
@@ -32,6 +31,7 @@ function useBridgeNotify<T>(
32
31
  }
33
32
  notify(bridgeRef.current, value);
34
33
  lastSentRef.current = value;
34
+ // oxlint-disable-next-line tap-hooks/exhaustive-deps -- refs are stable; notify is assumed stable; re-run only when value changes
35
35
  }, [value]);
36
36
  }
37
37
 
@@ -131,7 +131,6 @@ export function McpAppFrame({
131
131
 
132
132
  const resourceUri = resource.uri;
133
133
 
134
- // biome-ignore lint/correctness/useExhaustiveDependencies: re-mounts only on resource URI; live values flow through liveRef
135
134
  useEffect(() => {
136
135
  const container = containerRef.current;
137
136
  if (!container) return;
@@ -255,6 +254,7 @@ export function McpAppFrame({
255
254
  pendingHostContextRef.current = undefined;
256
255
  setContentHeight(undefined);
257
256
  };
257
+ // oxlint-disable-next-line tap-hooks/exhaustive-deps -- re-mount only on resource URI change; live values flow through liveRef
258
258
  }, [resourceUri]);
259
259
 
260
260
  useBridgeNotify(
@@ -28,7 +28,7 @@ const useAssistantModalOpenState = ({
28
28
  return aui.on("thread.runStart", () => {
29
29
  setOpen(true);
30
30
  });
31
- }, [unstable_openOnRunStart, aui]);
31
+ }, [unstable_openOnRunStart, aui, setOpen]);
32
32
 
33
33
  return state;
34
34
  };
@@ -8,7 +8,7 @@ import { ComposerPrimitiveAttachmentDropzone } from "./ComposerAttachmentDropzon
8
8
 
9
9
  const addAttachment = vi.fn<(file: File) => Promise<void>>();
10
10
 
11
- globalThis.IS_REACT_ACT_ENVIRONMENT = true;
11
+ (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
12
12
 
13
13
  vi.mock("@assistant-ui/store", async (importOriginal) => {
14
14
  const actual = await importOriginal<typeof import("@assistant-ui/store")>();
@@ -33,7 +33,7 @@ const plugin = {
33
33
 
34
34
  let pluginRegistry: { getPlugins: () => (typeof plugin)[] } | null = null;
35
35
 
36
- globalThis.IS_REACT_ACT_ENVIRONMENT = true;
36
+ (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
37
37
 
38
38
  vi.mock("@assistant-ui/store", () => {
39
39
  const aui = {
@@ -11,7 +11,7 @@ import {
11
11
  useTriggerPopoverAriaPublish,
12
12
  } from "./TriggerPopoverRootContext";
13
13
 
14
- globalThis.IS_REACT_ACT_ENVIRONMENT = true;
14
+ (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
15
15
 
16
16
  type PublishHandle = ReturnType<typeof useTriggerPopoverAriaPublish>;
17
17
 
@@ -294,8 +294,16 @@ const MessagePartComponent: FC<MessagePartComponentProps> = ({
294
294
  if (type === "tool-call") {
295
295
  const addResult = aui.part().addToolResult;
296
296
  const resume = aui.part().resumeToolCall;
297
+ const respondToApproval = aui.part().respondToToolApproval;
297
298
  if ("Override" in tools)
298
- return <tools.Override {...part} addResult={addResult} resume={resume} />;
299
+ return (
300
+ <tools.Override
301
+ {...part}
302
+ addResult={addResult}
303
+ resume={resume}
304
+ respondToApproval={respondToApproval}
305
+ />
306
+ );
299
307
  const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;
300
308
  return (
301
309
  <ToolUIDisplay
@@ -303,6 +311,7 @@ const MessagePartComponent: FC<MessagePartComponentProps> = ({
303
311
  Fallback={Tool}
304
312
  addResult={addResult}
305
313
  resume={resume}
314
+ respondToApproval={respondToApproval}
306
315
  />
307
316
  );
308
317
  }
@@ -5,7 +5,7 @@ import { ThreadListItemPrimitiveTitle } from "./ThreadListItemTitle";
5
5
 
6
6
  const mockUseAuiState = vi.fn();
7
7
  type UseAuiStateSelector = Parameters<
8
- typeof import("@assistant-ui/store")["useAuiState"]
8
+ (typeof import("@assistant-ui/store"))["useAuiState"]
9
9
  >[0];
10
10
 
11
11
  vi.mock("@assistant-ui/store", async (importOriginal) => {
@@ -1,10 +1,10 @@
1
1
  import { describe, it, expect, vi, beforeEach } from "vitest";
2
2
  import { BaseComposerRuntimeCore } from "../legacy-runtime/runtime-cores/composer/BaseComposerRuntimeCore";
3
- import type { AttachmentAdapter } from "../legacy-runtime/runtime-cores/adapters/attachment";
4
- import type { DictationAdapter } from "../legacy-runtime/runtime-cores/adapters/speech/SpeechAdapterTypes";
5
3
  import type {
6
4
  AppendMessage,
5
+ AttachmentAdapter,
7
6
  CreateAttachment,
7
+ DictationAdapter,
8
8
  PendingAttachment,
9
9
  SendOptions,
10
10
  } from "@assistant-ui/core";
@@ -71,7 +71,7 @@ const RuntimeProvider: FC<
71
71
 
72
72
  const renderThread = (MessageComponent: FC, messages?: ThreadMessageLike[]) => {
73
73
  render(
74
- <RuntimeProvider messages={messages}>
74
+ <RuntimeProvider messages={messages!}>
75
75
  <ThreadPrimitive.Messages components={{ Message: MessageComponent }} />
76
76
  </RuntimeProvider>,
77
77
  );
@@ -7,15 +7,13 @@ import {
7
7
  RuntimeAdapterProvider,
8
8
  useRemoteThreadListRuntime,
9
9
  useRuntimeAdapters,
10
+ type RuntimeAdapters as RuntimeAdaptersShape,
10
11
  } from "@assistant-ui/core/react";
11
12
  import { makeAdapter } from "./remote-thread-list-test-helpers";
12
13
  import { useLocalRuntime } from "../legacy-runtime/runtime-cores/local/useLocalRuntime";
13
14
  import { AssistantRuntimeProvider } from "../context";
14
15
  import type { ChatModelAdapter, RemoteThreadListAdapter } from "../index";
15
- import type {
16
- RuntimeAdapters as RuntimeAdaptersShape,
17
- ThreadHistoryAdapter,
18
- } from "@assistant-ui/core";
16
+ import type { ThreadHistoryAdapter } from "@assistant-ui/core";
19
17
 
20
18
  type CapturedAdapters = RuntimeAdaptersShape | null;
21
19
 
@@ -9,7 +9,7 @@ describe("auiV0Encode", () => {
9
9
  role: "assistant",
10
10
  status: { type: "complete", reason: "stop" },
11
11
  metadata: {
12
- unstable_state: undefined,
12
+ unstable_state: null,
13
13
  unstable_annotations: [],
14
14
  unstable_data: [],
15
15
  steps: [],
@@ -1,2 +0,0 @@
1
- import { ToolExecutionStatus, useToolInvocations } from "@assistant-ui/core/react";
2
- export { type ToolExecutionStatus, useToolInvocations };
@@ -1,2 +0,0 @@
1
- import { useToolInvocations } from "@assistant-ui/core/react";
2
- export { useToolInvocations };