@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.
- package/dist/client/ExternalThread.d.ts.map +1 -1
- package/dist/client/ExternalThread.js +2 -1
- package/dist/client/ExternalThread.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/internal.d.ts +3 -4
- package/dist/internal.js +1 -3
- package/dist/internal.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +3 -2
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +4 -10
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/mcp-apps/app-frame.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js +5 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js +5 -2
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
- package/dist/utils/useToolArgsFieldStatus.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/client/ExternalThread.ts +1 -0
- package/src/index.ts +1 -0
- package/src/internal.ts +1 -4
- package/src/legacy-runtime/runtime/ThreadListRuntime.ts +1 -4
- package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +3 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +7 -15
- package/src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.ts +1 -1
- package/src/mcp-apps/McpAppRenderer.tsx +1 -1
- package/src/mcp-apps/app-frame.tsx +2 -2
- package/src/primitives/assistantModal/AssistantModalRoot.tsx +1 -1
- package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +1 -1
- package/src/primitives/composer/ComposerInput.test.tsx +1 -1
- package/src/primitives/composer/trigger/TriggerPopoverRootContext.test.tsx +1 -1
- package/src/primitives/message/MessagePartsGrouped.tsx +10 -1
- package/src/primitives/threadListItem/ThreadListItemTitle.test.tsx +1 -1
- package/src/tests/BaseComposerRuntimeCore.test.ts +2 -2
- package/src/tests/MessageParts.loading.test.tsx +1 -1
- package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +2 -4
- package/src/tests/auiV0Encode.test.ts +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.d.ts +0 -2
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js +0 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.test.ts +0 -892
- 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
|
-
}, [
|
|
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
|
|
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,
|
|
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":";;;;;;;;;;;;;;;AA0CA,MAAM,+BACJ,UACuB;CAEvB,MAAM,2BAAW,IAAI,IAAsB;CAG3C,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EAKrC,MAAM,UAJO,MAAM,IACI,YAGK,eAAe;EAG3C,MAAM,UAAU,SAAS,IAAI,OAAO,KAAK,CAAC;EAC1C,QAAQ,KAAK,CAAC;EACd,SAAS,IAAI,SAAS,OAAO;CAC/B;CAGA,MAAM,SAA6B,CAAC;CACpC,KAAK,MAAM,CAAC,SAAS,YAAY,UAAU;EAEzC,MAAM,WAAW,QAAQ,WAAW,cAAc,IAAI,KAAA,IAAY;EAClE,OAAO,KAAK;GAAE;GAAU;EAAQ,CAAC;CACnC;CAEA,OAAO;AACT;AAEA,MAAM,0BACJ,qBACuB;CACvB,MAAM,QAAQ,aAAa,MAAM,EAAE,QAAQ,KAAK;CAEhD,OAAO,cAAc;EACnB,IAAI,MAAM,WAAW,GACnB,OAAO,CAAC;EAEV,OAAO,iBAAiB,KAAK;CAC/B,GAAG,CAAC,OAAO,gBAAgB,CAAC;AAC9B;AA8IA,MAAM,iBAAiB,EACrB,UACA,GAAG,YAG4B;CAC/B,MAAM,SAAS,aAAa,MAAM;EAChC,MAAM,SAAS,EAAE,MAAM,MAAM,MAAM,aAAa;EAChD,IAAI,MAAM,QAAQ,MAAM,GAAG,OAAO,OAAO,MAAM;EAC/C,OAAO;CACT,CAAC;CACD,IAAI,CAAC,QAAQ,OAAO;CACpB,OAAO,oBAAC,QAAD,EAAQ,GAAI,MAAQ,CAAA;AAC7B;AAEA,MAAM,iBAAiB,EACrB,UACA,GAAG,YAGwB;CAC3B,MAAM,SAAS,aAAa,MAAM;EAChC,MAAM,SAAS,EAAE,cAAc,UAAU,MAAM,SAAS;EACxD,IAAI,MAAM,QAAQ,MAAM,GAAG,OAAO,OAAO,MAAM;EAC/C,OAAO;CACT,CAAC;CACD,IAAI,CAAC,QAAQ,OAAO;CACpB,OAAO,oBAAC,QAAD,EAAQ,GAAI,MAAQ,CAAA;AAC7B;AAEA,MAAM,oBAAoB;CACxB,YACE,qBAAC,KAAD;EAAG,OAAO,EAAE,YAAY,WAAW;YAAnC,CACE,oBAAC,0BAAD,CAA2B,CAAA,GAC3B,oBAAC,gCAAD,EAAA,UACE,oBAAC,QAAD;GAAM,OAAO,EAAE,YAAY,SAAS;aAAI;EAAgB,CAAA,EAC1B,CAAA,CAC/B;;CAEL,iBAAiB;CACjB,cAAc;CACd,aAAa,oBAAC,2BAAD,CAA4B,CAAA;CACzC,YAAY;CACZ,sBAAsB;CACtB,QAAQ,EAAE,eAAe;AAC3B;AAMA,MAAM,wBAAuD,EAC3D,YAAY,EACV,OAAO,kBAAkB,MACzB,YAAY,kBAAkB,WAC9B,QAAQ,kBAAkB,OAC1B,SAAS,kBAAkB,QAC3B,OAAO,kBAAkB,MACzB,gBAAgB,QAAQ,kBAAkB,gBAC1C,QAAQ,CAAC,GACT,SACE,CAAC,QACD;CACJ,MAAM,MAAM,OAAO;CACnB,MAAM,OAAO,aAAa,MAAM,EAAE,IAAI;CAEtC,MAAM,OAAO,KAAK;CAClB,IAAI,SAAS,aAAa;EACxB,MAAM,YAAY,IAAI,KAAK,EAAE;EAC7B,MAAM,SAAS,IAAI,KAAK,EAAE;EAC1B,IAAI,cAAc,OAChB,OAAO,oBAAC,MAAM,UAAP;GAAgB,GAAI;GAAiB;GAAmB;EAAS,CAAA;EAC1E,MAAM,OAAO,MAAM,UAAU,KAAK,aAAa,MAAM;EACrD,OACE,oBAAC,eAAD;GACE,GAAI;GACJ,UAAU;GACC;GACH;EACT,CAAA;CAEL;CAEA,IAAI,KAAK,QAAQ,SAAS,mBACxB,MAAM,IAAI,MAAM,+CAA+C;CAEjE,QAAQ,MAAR;EACE,KAAK,QACH,OAAO,oBAAC,MAAD,EAAM,GAAI,KAAO,CAAA;EAE1B,KAAK,aACH,OAAO,oBAAC,WAAD,EAAW,GAAI,KAAO,CAAA;EAE/B,KAAK,UACH,OAAO,oBAAC,QAAD,EAAQ,GAAI,KAAO,CAAA;EAE5B,KAAK,SACH,OAAO,oBAAC,OAAD,EAAO,GAAI,KAAO,CAAA;EAE3B,KAAK,QACH,OAAO,oBAAC,MAAD,EAAM,GAAI,KAAO,CAAA;EAE1B,KAAK,SACH,OAAO,oBAAC,OAAD,EAAO,GAAI,KAAO,CAAA;EAE3B,KAAK,QAAQ;GACX,MAAM,OAAO,MAAM,UAAU,KAAK,SAAS,MAAM;GACjD,OAAO,oBAAC,eAAD;IAAe,GAAI;IAAM,UAAU;GAAO,CAAA;EACnD;EAEA;GACE,QAAQ,KAAK,8BAA8B,MAAM;GACjD,OAAO;CACX;AACF;AAOA,MAAM,mBAAyC,EAAE,WAAW,iBAAiB;CAC3E,OACE,oBAAC,qBAAD;EAAqB,OAAO;YAC1B,oBAAC,sBAAD,EAAkC,WAAa,CAAA;CAC5B,CAAA;AAEzB;AAEA,MAAM,cAAc,KAClB,kBACC,MAAM,SACL,KAAK,cAAc,KAAK,aACxB,KAAK,YAAY,SAAS,KAAK,YAAY,QAC3C,KAAK,YAAY,cAAc,KAAK,YAAY,aAChD,KAAK,YAAY,WAAW,KAAK,YAAY,UAC7C,KAAK,YAAY,UAAU,KAAK,YAAY,SAC5C,KAAK,YAAY,SAAS,KAAK,YAAY,QAC3C,KAAK,YAAY,mBAAmB,KAAK,YAAY,kBACrD,KAAK,YAAY,UAAU,KAAK,YAAY,SAC5C,KAAK,YAAY,SAAS,KAAK,YAAY,QAC3C,KAAK,YAAY,UAAU,KAAK,YAAY,KAChD;AAEA,MAAM,qBAGA,EAAE,QAAQ,WAAW,gBAAgB;CACzC,OACE,oBAAC,yBAAD;EAAyB,MAAK;EAAG,WAAW,OAAO,SAAS;YAC1D,oBAAC,WAAD;GAAW,MAAK;GAAO,MAAK;GAAW;EAAS,CAAA;CACzB,CAAA;AAE7B;AAEA,MAAM,kBAAqC,OAAO,OAAO,EACvD,MAAM,WACR,CAAC;AAED,MAAM,kBAAiD,EAAE,iBAAiB;CACxE,MAAM,SAAS,aACZ,MAAO,EAAE,QAAQ,UAAU,eAC9B;CAEA,IAAI,YAAY,OAAO,OAAO,oBAAC,WAAW,OAAZ,EAA0B,OAAS,CAAA;CAEjE,OACE,oBAAC,mBAAD;EACU;EACR,WAAW,YAAY,QAAQ,kBAAkB;CAClD,CAAA;AAEL;AAEA,MAAM,aAAa,KACjB,iBACC,MAAM,SACL,KAAK,YAAY,UAAU,KAAK,YAAY,SAC5C,KAAK,YAAY,SAAS,KAAK,YAAY,IAC/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,yCAER,EAAE,kBAAkB,iBAAiB;CACxC,MAAM,gBAAgB,aAAa,MAAM,EAAE,QAAQ,MAAM,MAAM;CAC/D,MAAM,gBAAgB,uBAAuB,gBAAgB;CA4B7D,OAAO,oBAAA,UAAA,EAAA,UA1Be,cAAc;EAClC,IAAI,kBAAkB,GACpB,OAAO,oBAAC,YAAD,EAAwB,WAAa,CAAA;EAG9C,OAAO,cAAc,KAAK,OAAO,eAAe;GAG9C,OACE,oBAHqB,YAAY,SAAS,kBAAkB,OAG5D;IAEE,UAAU,MAAM;IAChB,SAAS,MAAM;cAEd,MAAM,QAAQ,KAAK,cAClB,oBAAC,aAAD;KAEa;KACC;IACb,GAHM,SAGN,CACF;GACa,GAXT,SAAS,WAAW,GAAG,MAAM,YAAY,aAWhC;EAEpB,CAAC;CACH,GAAG;EAAC;EAAe;EAAY;CAAa,CAEtB,EAAI,CAAA;AAC5B;AAEA,sCAAsC,cACpC;;;;;;;AAQF,MAAa,mDAER,EAAE,YAAY,GAAG,YAAY;CAChC,OACE,oBAAC,uCAAD;EACE,GAAI;EACQ;EACZ,kBAAkB;CACnB,CAAA;AAEL;AAEA,gDAAgD,cAC9C"}
|
|
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.
|
|
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.
|
|
51
|
+
"@assistant-ui/core": "^0.2.7",
|
|
52
52
|
"@assistant-ui/store": "^0.2.12",
|
|
53
|
-
"@assistant-ui/tap": "^0.5.
|
|
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.
|
|
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",
|
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 "
|
|
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 =
|
package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts
CHANGED
|
@@ -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 {
|
|
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 "
|
|
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(
|
|
@@ -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
|
|
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
|
|