@assistant-ui/react 0.15.4 → 0.15.5
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 +383 -368
- package/dist/client/ExternalThread.js.map +1 -1
- package/dist/context/providers/MessageProvider.js +15 -34
- package/dist/context/providers/MessageProvider.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts +6 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts.map +1 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.js +10 -8
- package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +8 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +5 -1
- 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 +44 -3
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
- package/dist/primitives/attachment/AttachmentThumb.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentThumb.js +17 -14
- package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +1 -0
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemText.d.ts.map +1 -1
- package/dist/primitives/queueItem/QueueItemText.js +10 -4
- package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +5 -4
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
- package/dist/utils/createActionButton.js +1 -1
- package/dist/utils/createActionButton.js.map +1 -1
- package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
- package/package.json +6 -6
- package/src/client/ExternalThread.ts +28 -16
- package/src/context/providers/MessageProvider.tsx +8 -6
- package/src/index.ts +5 -1
- package/src/legacy-runtime/AssistantRuntimeProvider.tsx +13 -3
- package/src/legacy-runtime/runtime-cores/assistant-transport/runManager.ts +14 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-scheduling.test.ts +38 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +5 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransport.spec.md +7 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +290 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +62 -1
- package/src/primitives/actionBar/ActionBarExportMarkdown.tsx +1 -1
- package/src/primitives/attachment/AttachmentThumb.test.tsx +70 -0
- package/src/primitives/attachment/AttachmentThumb.tsx +8 -4
- package/src/primitives/composer/ComposerInput.test.tsx +53 -2
- package/src/primitives/composer/ComposerInput.tsx +3 -0
- package/src/primitives/queueItem/QueueItemText.tsx +8 -2
- package/src/primitives/selectionToolbar/SelectionToolbarRoot.test.tsx +74 -0
- package/src/primitives/selectionToolbar/SelectionToolbarRoot.tsx +3 -3
- package/src/tests/external-thread-parity.test.tsx +9 -4
- package/src/tests/local-runtime-queue.test.tsx +199 -6
- package/src/utils/createActionButton.test.tsx +18 -0
- package/src/utils/createActionButton.tsx +1 -1
|
@@ -4,6 +4,7 @@ import { getSelectionMessageId } from "../../utils/getSelectionMessageId.js";
|
|
|
4
4
|
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
5
5
|
import { createContext, forwardRef, useContext, useEffect, useState } from "@assistant-ui/tap/react-shim";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
import { composeEventHandlers } from "@radix-ui/primitive";
|
|
7
8
|
import { createPortal } from "react-dom";
|
|
8
9
|
//#region src/primitives/selectionToolbar/SelectionToolbarRoot.tsx
|
|
9
10
|
const SelectionToolbarContext = createContext(null);
|
|
@@ -117,10 +118,7 @@ const SelectionToolbarPrimitiveRoot = forwardRef((t0, forwardedRef) => {
|
|
|
117
118
|
const positionStyle = t5;
|
|
118
119
|
let t6;
|
|
119
120
|
if ($[10] !== onMouseDown) {
|
|
120
|
-
t6 = (
|
|
121
|
-
e.preventDefault();
|
|
122
|
-
onMouseDown?.(e);
|
|
123
|
-
};
|
|
121
|
+
t6 = composeEventHandlers(onMouseDown, _temp);
|
|
124
122
|
$[10] = onMouseDown;
|
|
125
123
|
$[11] = t6;
|
|
126
124
|
} else t6 = $[11];
|
|
@@ -151,6 +149,9 @@ const SelectionToolbarPrimitiveRoot = forwardRef((t0, forwardedRef) => {
|
|
|
151
149
|
return t8;
|
|
152
150
|
});
|
|
153
151
|
SelectionToolbarPrimitiveRoot.displayName = "SelectionToolbarPrimitive.Root";
|
|
152
|
+
function _temp(e) {
|
|
153
|
+
e.preventDefault();
|
|
154
|
+
}
|
|
154
155
|
//#endregion
|
|
155
156
|
export { SelectionToolbarPrimitiveRoot, useSelectionToolbarInfo };
|
|
156
157
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectionToolbarRoot.js","names":["c","_c","Primitive","ComponentPropsWithoutRef","ComponentRef","createContext","forwardRef","useContext","useEffect","useState","createPortal","getSelectionMessageId","SelectionInfo","text","messageId","rect","DOMRect","SelectionToolbarContext","useSelectionToolbarInfo","SelectionToolbarPrimitiveRoot","Element","div","Props","t0","forwardedRef","$","onMouseDown","props","style","info","setInfo","t1","t2","Symbol","for","checkSelection","requestAnimationFrame","sel","window","getSelection","isCollapsed","toString","trim","range","getRangeAt","getBoundingClientRect","handleSelectionCollapse","sel_0","handleScroll","document","addEventListener","removeEventListener","t3","top","t4","left","width","t5","position","transform","zIndex","positionStyle","t6","
|
|
1
|
+
{"version":3,"file":"SelectionToolbarRoot.js","names":["c","_c","Primitive","composeEventHandlers","ComponentPropsWithoutRef","ComponentRef","createContext","forwardRef","useContext","useEffect","useState","createPortal","getSelectionMessageId","SelectionInfo","text","messageId","rect","DOMRect","SelectionToolbarContext","useSelectionToolbarInfo","SelectionToolbarPrimitiveRoot","Element","div","Props","t0","forwardedRef","$","onMouseDown","props","style","info","setInfo","t1","t2","Symbol","for","checkSelection","requestAnimationFrame","sel","window","getSelection","isCollapsed","toString","trim","range","getRangeAt","getBoundingClientRect","handleSelectionCollapse","sel_0","handleScroll","document","addEventListener","removeEventListener","t3","top","t4","left","width","t5","position","transform","zIndex","positionStyle","t6","_temp","t7","t8","body","displayName","e","preventDefault"],"sources":["../../../src/primitives/selectionToolbar/SelectionToolbarRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { Primitive } from \"../../utils/Primitive\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport {\n type ComponentPropsWithoutRef,\n type ComponentRef,\n createContext,\n forwardRef,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { getSelectionMessageId } from \"../../utils/getSelectionMessageId\";\n\ntype SelectionInfo = {\n text: string;\n messageId: string;\n rect: DOMRect;\n};\n\nconst SelectionToolbarContext = createContext<SelectionInfo | null>(null);\n\nexport const useSelectionToolbarInfo = () =>\n useContext(SelectionToolbarContext);\n\nexport namespace SelectionToolbarPrimitiveRoot {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;\n}\n\n/**\n * A floating toolbar that appears when text is selected within a message.\n *\n * Listens for mouse and keyboard selection events, validates that the\n * selection is within a single message, and renders a positioned portal\n * near the selection. Prevents mousedown from clearing the selection.\n *\n * @example\n * ```tsx\n * <SelectionToolbarPrimitive.Root>\n * <SelectionToolbarPrimitive.Quote>Quote</SelectionToolbarPrimitive.Quote>\n * </SelectionToolbarPrimitive.Root>\n * ```\n */\nexport const SelectionToolbarPrimitiveRoot = forwardRef<\n SelectionToolbarPrimitiveRoot.Element,\n SelectionToolbarPrimitiveRoot.Props\n>(({ onMouseDown, style, ...props }, forwardedRef) => {\n const [info, setInfo] = useState<SelectionInfo | null>(null);\n\n useEffect(() => {\n const checkSelection = () => {\n requestAnimationFrame(() => {\n const sel = window.getSelection();\n if (!sel || sel.isCollapsed) {\n setInfo(null);\n return;\n }\n\n const text = sel.toString().trim();\n if (!text) {\n setInfo(null);\n return;\n }\n\n const messageId = getSelectionMessageId(sel);\n if (!messageId) {\n setInfo(null);\n return;\n }\n\n const range = sel.getRangeAt(0);\n const rect = range.getBoundingClientRect();\n setInfo({ text, messageId, rect });\n });\n };\n\n const handleSelectionCollapse = () => {\n const sel = window.getSelection();\n if (!sel || sel.isCollapsed) {\n setInfo(null);\n }\n };\n\n const handleScroll = () => {\n setInfo(null);\n };\n\n document.addEventListener(\"mouseup\", checkSelection);\n document.addEventListener(\"keyup\", checkSelection);\n document.addEventListener(\"selectionchange\", handleSelectionCollapse);\n document.addEventListener(\"scroll\", handleScroll, true);\n\n return () => {\n document.removeEventListener(\"mouseup\", checkSelection);\n document.removeEventListener(\"keyup\", checkSelection);\n document.removeEventListener(\"selectionchange\", handleSelectionCollapse);\n document.removeEventListener(\"scroll\", handleScroll, true);\n };\n }, []);\n\n if (!info) return null;\n\n const positionStyle: React.CSSProperties = {\n position: \"fixed\",\n top: `${info.rect.top - 8}px`,\n left: `${info.rect.left + info.rect.width / 2}px`,\n transform: \"translate(-50%, -100%)\",\n zIndex: 50,\n ...style,\n };\n\n return createPortal(\n <SelectionToolbarContext.Provider value={info}>\n <Primitive.div\n {...props}\n ref={forwardedRef}\n style={positionStyle}\n onMouseDown={composeEventHandlers(onMouseDown, (e) => {\n // Prevent mousedown from clearing the text selection\n e.preventDefault();\n })}\n />\n </SelectionToolbarContext.Provider>,\n document.body,\n );\n});\n\nSelectionToolbarPrimitiveRoot.displayName = \"SelectionToolbarPrimitive.Root\";\n"],"mappings":"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"}
|
|
@@ -21,8 +21,8 @@ const createActionButton = (displayName, useActionButton, forwardProps = []) =>
|
|
|
21
21
|
let t4;
|
|
22
22
|
if ($[0] !== forwardedRef || $[1] !== primitiveProps || $[2] !== t0.button || $[3] !== t2 || $[4] !== t3) {
|
|
23
23
|
t4 = /* @__PURE__ */ jsx(t0.button, {
|
|
24
|
-
...primitiveProps,
|
|
25
24
|
type: t1,
|
|
25
|
+
...primitiveProps,
|
|
26
26
|
ref: forwardedRef,
|
|
27
27
|
disabled: t2,
|
|
28
28
|
onClick: t3
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createActionButton.js","names":["ComponentRef","forwardRef","ComponentPropsWithoutRef","MouseEventHandler","Primitive","composeEventHandlers","ActionButtonCallback","props","TProps","HTMLButtonElement","PrimitiveButtonProps","button","ActionButtonProps","THook","ActionButtonElement","createActionButton","displayName","useActionButton","forwardProps","NonNullable","ActionButton","forwardedRef","$","_c","forwardedProps","primitiveProps","Object","keys","Array","forEach","key","includes","callback","undefined","t0","t1","t2","disabled","t3","onClick","t4"],"sources":["../../src/utils/createActionButton.tsx"],"sourcesContent":["import {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n type MouseEventHandler,\n} from \"react\";\nimport { Primitive } from \"./Primitive\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\n\ntype ActionButtonCallback<TProps> = (\n props: TProps,\n) => MouseEventHandler<HTMLButtonElement> | null;\n\ntype PrimitiveButtonProps = ComponentPropsWithoutRef<typeof Primitive.button>;\n\nexport type ActionButtonProps<THook> = PrimitiveButtonProps &\n (THook extends (props: infer TProps) => unknown ? TProps : never);\n\nexport type ActionButtonElement = ComponentRef<typeof Primitive.button>;\n\nexport const createActionButton = <TProps,>(\n displayName: string,\n useActionButton: ActionButtonCallback<TProps>,\n forwardProps: (keyof NonNullable<TProps>)[] = [],\n) => {\n const ActionButton = forwardRef<\n ActionButtonElement,\n PrimitiveButtonProps & TProps\n >((props, forwardedRef) => {\n const forwardedProps = {} as TProps;\n const primitiveProps = {} as PrimitiveButtonProps;\n\n (Object.keys(props) as Array<keyof typeof props>).forEach((key) => {\n if (forwardProps.includes(key as keyof TProps)) {\n (forwardedProps as any)[key] = props[key];\n } else {\n (primitiveProps as any)[key] = props[key];\n }\n });\n\n const callback = useActionButton(forwardedProps as TProps) ?? undefined;\n return (\n <Primitive.button\n
|
|
1
|
+
{"version":3,"file":"createActionButton.js","names":["ComponentRef","forwardRef","ComponentPropsWithoutRef","MouseEventHandler","Primitive","composeEventHandlers","ActionButtonCallback","props","TProps","HTMLButtonElement","PrimitiveButtonProps","button","ActionButtonProps","THook","ActionButtonElement","createActionButton","displayName","useActionButton","forwardProps","NonNullable","ActionButton","forwardedRef","$","_c","forwardedProps","primitiveProps","Object","keys","Array","forEach","key","includes","callback","undefined","t0","t1","t2","disabled","t3","onClick","t4"],"sources":["../../src/utils/createActionButton.tsx"],"sourcesContent":["import {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n type MouseEventHandler,\n} from \"react\";\nimport { Primitive } from \"./Primitive\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\n\ntype ActionButtonCallback<TProps> = (\n props: TProps,\n) => MouseEventHandler<HTMLButtonElement> | null;\n\ntype PrimitiveButtonProps = ComponentPropsWithoutRef<typeof Primitive.button>;\n\nexport type ActionButtonProps<THook> = PrimitiveButtonProps &\n (THook extends (props: infer TProps) => unknown ? TProps : never);\n\nexport type ActionButtonElement = ComponentRef<typeof Primitive.button>;\n\nexport const createActionButton = <TProps,>(\n displayName: string,\n useActionButton: ActionButtonCallback<TProps>,\n forwardProps: (keyof NonNullable<TProps>)[] = [],\n) => {\n const ActionButton = forwardRef<\n ActionButtonElement,\n PrimitiveButtonProps & TProps\n >((props, forwardedRef) => {\n const forwardedProps = {} as TProps;\n const primitiveProps = {} as PrimitiveButtonProps;\n\n (Object.keys(props) as Array<keyof typeof props>).forEach((key) => {\n if (forwardProps.includes(key as keyof TProps)) {\n (forwardedProps as any)[key] = props[key];\n } else {\n (primitiveProps as any)[key] = props[key];\n }\n });\n\n const callback = useActionButton(forwardedProps as TProps) ?? undefined;\n return (\n <Primitive.button\n type=\"button\"\n {...primitiveProps}\n ref={forwardedRef}\n disabled={primitiveProps.disabled || !callback}\n onClick={composeEventHandlers(primitiveProps.onClick, callback)}\n />\n );\n });\n\n ActionButton.displayName = displayName;\n\n return ActionButton;\n};\n"],"mappings":";;;;;;AAoBA,MAAae,sBACXC,aACAC,iBACAC,eAA8C,CAAA,MAC3C;CACH,MAAME,eAAenB,YAGnBM,OAAAc,iBAAA;EAAA,MAAAC,IAAAC,EAAA,CAAA;EACA,MAAAC,iBAAuB,CAAC;EACxB,MAAAC,iBAAuB,CAAC;EAExB,OAAOE,KAAMpB,KAAK,CAAC,CAA6BsB,SAAUC,QAAA;GACxD,IAAIZ,aAAYa,SAAUD,GAAmB,GAC3C,eAAwBA,OAAOvB,MAAMuB;QAErC,eAAwBA,OAAOvB,MAAMuB;EACtC,CACF;EAED,MAAAE,WAAiBf,gBAAgBO,cAAqC,KAArDS,KAAAA;EAEd,MAAAC,KAAA9B;EACM,MAAA+B,KAAA;EAGK,MAAAC,KAAAX,eAAcY,YAAd,CAA4BL;EAC7B,MAAAM,KAAAjC,qBAAqBoB,eAAcc,SAAUP,QAAQ;EAAC,IAAAQ;EAAA,IAAAlB,EAAA,OAAAD,gBAAAC,EAAA,OAAAG,kBAAAH,EAAA,OAAAY,GAAAvB,UAAAW,EAAA,OAAAc,MAAAd,EAAA,OAAAgB,IAAA;GALjEE,KAAA,oBAAA,GAAA,QAAA;IACO,MAAAL;IAAQ,GACTV;IACCJ,KAAAA;IACK,UAAAe;IACD,SAAAE;GAAsD,CAAA;GAC/DhB,EAAA,KAAAD;GAAAC,EAAA,KAAAG;GAAAH,EAAA,KAAAY,GAAAvB;GAAAW,EAAA,KAAAc;GAAAd,EAAA,KAAAgB;GAAAhB,EAAA,KAAAkB;EAAA,OAAAA,KAAAlB,EAAA;EAAA,OANFkB;CAME,CAEL;CAEDpB,aAAaJ,cAAcA;CAE3B,OAAOI;AACT"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
//#region src/utils/useToolArgsFieldStatus.d.ts
|
|
2
2
|
declare const useToolArgsFieldStatus: (fieldPath: (string | number)[]) => {
|
|
3
|
+
type: string;
|
|
4
|
+
} | {
|
|
3
5
|
readonly type: "running";
|
|
4
6
|
} | {
|
|
5
7
|
readonly type: "complete";
|
|
@@ -7,8 +9,6 @@ declare const useToolArgsFieldStatus: (fieldPath: (string | number)[]) => {
|
|
|
7
9
|
readonly type: "incomplete";
|
|
8
10
|
readonly reason: "cancelled" | "length" | "content-filter" | "other" | "error";
|
|
9
11
|
readonly error?: unknown;
|
|
10
|
-
} | {
|
|
11
|
-
type: string;
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
14
14
|
export { useToolArgsFieldStatus };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.5",
|
|
4
4
|
"description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -55,9 +55,9 @@
|
|
|
55
55
|
],
|
|
56
56
|
"sideEffects": false,
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@assistant-ui/core": "^0.3.
|
|
59
|
-
"@assistant-ui/store": "^0.3.
|
|
60
|
-
"@assistant-ui/tap": "^0.9.
|
|
58
|
+
"@assistant-ui/core": "^0.3.6",
|
|
59
|
+
"@assistant-ui/store": "^0.3.4",
|
|
60
|
+
"@assistant-ui/tap": "^0.9.10",
|
|
61
61
|
"@radix-ui/primitive": "^1.1.7",
|
|
62
62
|
"@radix-ui/react-collection": "^1.1.15",
|
|
63
63
|
"@radix-ui/react-compose-refs": "^1.1.5",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@radix-ui/react-use-controllable-state": "^1.2.6",
|
|
68
68
|
"@radix-ui/react-use-escape-keydown": "^1.1.5",
|
|
69
69
|
"assistant-cloud": "^0.1.38",
|
|
70
|
-
"assistant-stream": "^0.3.
|
|
70
|
+
"assistant-stream": "^0.3.34",
|
|
71
71
|
"nanoid": "^6.0.0",
|
|
72
72
|
"radix-ui": "^1.6.7",
|
|
73
73
|
"react-textarea-autosize": "^8.5.9",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"react-dom": "^19.2.8",
|
|
101
101
|
"vitest": "^4.1.10",
|
|
102
102
|
"@assistant-ui/vite": "0.0.11",
|
|
103
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
103
|
+
"@assistant-ui/x-buildutils": "0.0.21"
|
|
104
104
|
},
|
|
105
105
|
"publishConfig": {
|
|
106
106
|
"access": "public",
|
|
@@ -32,6 +32,7 @@ import type {
|
|
|
32
32
|
ToolCallMessagePartStatus,
|
|
33
33
|
ExternalThreadQueueAdapter,
|
|
34
34
|
ExternalThreadBranchAdapter,
|
|
35
|
+
QueuePlacement,
|
|
35
36
|
} from "@assistant-ui/core";
|
|
36
37
|
import { ToolResponse } from "assistant-stream";
|
|
37
38
|
import type { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
@@ -175,7 +176,6 @@ const useMessageClient = ({
|
|
|
175
176
|
};
|
|
176
177
|
|
|
177
178
|
const handleSendEdit = (msg: AppendMessage) => {
|
|
178
|
-
queue?.clear("edit");
|
|
179
179
|
onEdit?.({
|
|
180
180
|
...msg,
|
|
181
181
|
parentId,
|
|
@@ -395,16 +395,17 @@ type ComposerClientResourceProps = {
|
|
|
395
395
|
|
|
396
396
|
const useQueueItemClient = ({
|
|
397
397
|
item,
|
|
398
|
-
|
|
398
|
+
onMove,
|
|
399
399
|
onRemove,
|
|
400
400
|
}: {
|
|
401
401
|
item: QueueItemState;
|
|
402
|
-
|
|
402
|
+
onMove: (placement: QueuePlacement) => void;
|
|
403
403
|
onRemove: () => void;
|
|
404
404
|
}): ClientOutput<"queueItem"> => {
|
|
405
405
|
return {
|
|
406
406
|
getState: () => item,
|
|
407
|
-
steer:
|
|
407
|
+
steer: () => onMove({ lane: "steer", insertAfter: null }),
|
|
408
|
+
move: onMove,
|
|
408
409
|
remove: onRemove,
|
|
409
410
|
};
|
|
410
411
|
};
|
|
@@ -522,14 +523,24 @@ const useComposerClientResource = ({
|
|
|
522
523
|
});
|
|
523
524
|
};
|
|
524
525
|
|
|
525
|
-
const
|
|
526
|
+
const steerItems = queue?.steerItems ?? EMPTY_QUEUE_ITEMS;
|
|
527
|
+
const laneItems = queue?.items ?? EMPTY_QUEUE_ITEMS;
|
|
528
|
+
const queueItems = useMemo(
|
|
529
|
+
() =>
|
|
530
|
+
steerItems.length === 0
|
|
531
|
+
? laneItems
|
|
532
|
+
: laneItems.length === 0
|
|
533
|
+
? steerItems
|
|
534
|
+
: [...steerItems, ...laneItems],
|
|
535
|
+
[steerItems, laneItems],
|
|
536
|
+
);
|
|
526
537
|
const queueItemClients = useClientLookup(
|
|
527
538
|
queueItems.map((item) =>
|
|
528
539
|
withKey(
|
|
529
540
|
item.id,
|
|
530
541
|
QueueItemClient({
|
|
531
542
|
item,
|
|
532
|
-
|
|
543
|
+
onMove: (placement) => queue?.move(item.id, placement),
|
|
533
544
|
onRemove: () => queue?.remove(item.id),
|
|
534
545
|
}),
|
|
535
546
|
),
|
|
@@ -662,7 +673,8 @@ const useComposerClientResource = ({
|
|
|
662
673
|
},
|
|
663
674
|
};
|
|
664
675
|
if (queue) {
|
|
665
|
-
|
|
676
|
+
if (opts?.steer ?? canCancel) queue.steer(composedMessage);
|
|
677
|
+
else queue.enqueue(composedMessage);
|
|
666
678
|
} else {
|
|
667
679
|
onSend?.(composedMessage);
|
|
668
680
|
}
|
|
@@ -697,7 +709,10 @@ const useComposerClientResource = ({
|
|
|
697
709
|
startDictation: () => {},
|
|
698
710
|
stopDictation: () => {},
|
|
699
711
|
setQuote,
|
|
700
|
-
queueItem: (selector: { index: number }) => {
|
|
712
|
+
queueItem: (selector: { index: number } | { id: string }) => {
|
|
713
|
+
if ("id" in selector) {
|
|
714
|
+
return queueItemClients.get({ key: selector.id });
|
|
715
|
+
}
|
|
701
716
|
return queueItemClients.get(selector);
|
|
702
717
|
},
|
|
703
718
|
};
|
|
@@ -754,7 +769,6 @@ const useExternalThread = ({
|
|
|
754
769
|
if (messageIndex === -1) return;
|
|
755
770
|
|
|
756
771
|
const parentId = messageIndex > 0 ? messages[messageIndex - 1]!.id : null;
|
|
757
|
-
queue?.clear("reload");
|
|
758
772
|
onReload?.(parentId);
|
|
759
773
|
};
|
|
760
774
|
|
|
@@ -778,7 +792,6 @@ const useExternalThread = ({
|
|
|
778
792
|
);
|
|
779
793
|
|
|
780
794
|
const handleCancelRun = () => {
|
|
781
|
-
queue?.clear("cancel-run");
|
|
782
795
|
onCancel?.();
|
|
783
796
|
};
|
|
784
797
|
|
|
@@ -793,11 +806,10 @@ const useExternalThread = ({
|
|
|
793
806
|
(): ExternalThreadQueueAdapter | undefined =>
|
|
794
807
|
queue && {
|
|
795
808
|
...queue,
|
|
796
|
-
enqueue: (message
|
|
797
|
-
queue.enqueue(
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
),
|
|
809
|
+
enqueue: (message) =>
|
|
810
|
+
queue.enqueue({ ...message, parentId: message.parentId ?? headId }),
|
|
811
|
+
steer: (message) =>
|
|
812
|
+
queue.steer({ ...message, parentId: message.parentId ?? headId }),
|
|
801
813
|
},
|
|
802
814
|
[queue, headId],
|
|
803
815
|
);
|
|
@@ -898,7 +910,7 @@ const useExternalThread = ({
|
|
|
898
910
|
startRun: message.startRun,
|
|
899
911
|
};
|
|
900
912
|
if (queue) {
|
|
901
|
-
queue.enqueue(appendMessage
|
|
913
|
+
queue.enqueue(appendMessage);
|
|
902
914
|
} else {
|
|
903
915
|
onNew?.(appendMessage);
|
|
904
916
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import type { FC, PropsWithChildren } from "react";
|
|
4
|
-
import { useAui, AuiProvider } from "@assistant-ui/store";
|
|
4
|
+
import { useAui, AuiConfig, AuiProvider } from "@assistant-ui/store";
|
|
5
5
|
import {
|
|
6
6
|
type ThreadMessageClientProps,
|
|
7
7
|
ThreadMessageClient,
|
|
@@ -10,9 +10,11 @@ import {
|
|
|
10
10
|
export const MessageProvider: FC<
|
|
11
11
|
PropsWithChildren<ThreadMessageClientProps>
|
|
12
12
|
> = ({ children, ...props }) => {
|
|
13
|
-
const aui = useAui(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const aui = useAui();
|
|
14
|
+
const config = AuiConfig({ message: ThreadMessageClient(props) });
|
|
15
|
+
return (
|
|
16
|
+
<AuiProvider extends={aui} config={config}>
|
|
17
|
+
{children}
|
|
18
|
+
</AuiProvider>
|
|
19
|
+
);
|
|
18
20
|
};
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export {
|
|
5
5
|
useAui,
|
|
6
6
|
AuiProvider,
|
|
7
|
+
AuiConfig,
|
|
7
8
|
useAuiState,
|
|
8
9
|
useAuiEvent,
|
|
9
10
|
AuiIf,
|
|
@@ -167,7 +168,10 @@ export type { LocalRuntimeOptions } from "./legacy-runtime/runtime-cores/local/L
|
|
|
167
168
|
// --- remote-thread-list ---
|
|
168
169
|
export { useRemoteThreadListRuntime } from "./legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime";
|
|
169
170
|
export { useCloudThreadListAdapter } from "./legacy-runtime/runtime-cores/remote-thread-list/adapter/cloud";
|
|
170
|
-
export type {
|
|
171
|
+
export type {
|
|
172
|
+
RemoteThreadListAdapter,
|
|
173
|
+
RemoteThreadListProviderComponent,
|
|
174
|
+
} from "@assistant-ui/core";
|
|
171
175
|
export { InMemoryThreadListAdapter } from "@assistant-ui/core";
|
|
172
176
|
|
|
173
177
|
// Re-export from @assistant-ui/core (runtime-cores root)
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { type FC, memo, type PropsWithChildren, useEffect } from "react";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type AssistantClient,
|
|
6
|
+
type AuiConfig,
|
|
7
|
+
useAui,
|
|
8
|
+
} from "@assistant-ui/store";
|
|
5
9
|
import type { AssistantRuntime } from "./runtime/AssistantRuntime";
|
|
6
10
|
import { AssistantProviderBase } from "@assistant-ui/core/react";
|
|
7
11
|
import { ThreadPrimitiveViewportProvider } from "../context/providers/ThreadViewportProvider";
|
|
@@ -24,6 +28,12 @@ export namespace AssistantRuntimeProvider {
|
|
|
24
28
|
* @defaultValue undefined
|
|
25
29
|
*/
|
|
26
30
|
aui?: AssistantClient;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Optional extra scopes provided alongside the runtime's `threads`
|
|
34
|
+
* scope; build with `AuiConfig`.
|
|
35
|
+
*/
|
|
36
|
+
config?: AuiConfig;
|
|
27
37
|
}>;
|
|
28
38
|
}
|
|
29
39
|
|
|
@@ -39,9 +49,9 @@ const DevToolsRegistration: FC = () => {
|
|
|
39
49
|
|
|
40
50
|
export const AssistantRuntimeProviderImpl: FC<
|
|
41
51
|
AssistantRuntimeProvider.Props
|
|
42
|
-
> = ({ children, aui, runtime }) => {
|
|
52
|
+
> = ({ children, aui, config, runtime }) => {
|
|
43
53
|
return (
|
|
44
|
-
<AssistantProviderBase runtime={runtime} aui={aui ?? null}>
|
|
54
|
+
<AssistantProviderBase runtime={runtime} aui={aui ?? null} config={config}>
|
|
45
55
|
<DevToolsRegistration />
|
|
46
56
|
{/* TODO temporarily allow accessing viewport state from outside the viewport */}
|
|
47
57
|
{/* TODO figure out if this behavior should be deprecated, since it is quite hacky */}
|
|
@@ -9,6 +9,13 @@ export type RunManager = Readonly<{
|
|
|
9
9
|
|
|
10
10
|
const disposeReason = Symbol("assistant-transport-dispose");
|
|
11
11
|
|
|
12
|
+
const reportFinishError = (error: unknown) => {
|
|
13
|
+
console.error(
|
|
14
|
+
"[assistant-ui] Assistant transport onFinish callback threw an error",
|
|
15
|
+
error,
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
12
19
|
export function useRunManager(config: {
|
|
13
20
|
onRun: (signal: AbortSignal) => Promise<void>;
|
|
14
21
|
onFinish?: (() => void) | undefined;
|
|
@@ -52,7 +59,13 @@ export function useRunManager(config: {
|
|
|
52
59
|
}
|
|
53
60
|
} finally {
|
|
54
61
|
if (!disposeAborted() && !stateRef.current.disposed) {
|
|
55
|
-
|
|
62
|
+
try {
|
|
63
|
+
void Promise.resolve(onFinishRef.current?.()).catch(
|
|
64
|
+
reportFinishError,
|
|
65
|
+
);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
reportFinishError(error);
|
|
68
|
+
}
|
|
56
69
|
}
|
|
57
70
|
if (!stateRef.current.disposed && stateRef.current.pending) {
|
|
58
71
|
startRun();
|
|
@@ -146,6 +146,44 @@ describe("assistant transport scheduling contracts", () => {
|
|
|
146
146
|
expect(seen[0]).toEqual([createMessageCommand("m1")]);
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
+
it("settles the run when onFinish throws", async () => {
|
|
150
|
+
const error = new Error("telemetry failed");
|
|
151
|
+
const onFinish = vi.fn<() => void>().mockImplementationOnce(() => {
|
|
152
|
+
throw error;
|
|
153
|
+
});
|
|
154
|
+
const consoleError = vi
|
|
155
|
+
.spyOn(console, "error")
|
|
156
|
+
.mockImplementation(() => {});
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const { result } = renderHook(() =>
|
|
160
|
+
useTransportSchedulingHarness({ onFinish }),
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
act(() => {
|
|
164
|
+
result.current.commandQueue.enqueue(createMessageCommand("m1"));
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
await waitFor(() => {
|
|
168
|
+
expect(result.current.runManager.isRunning).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
act(() => {
|
|
172
|
+
result.current.commandQueue.enqueue(createMessageCommand("m2"));
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
await waitFor(() => {
|
|
176
|
+
expect(result.current.runBatchesRef.current).toHaveLength(2);
|
|
177
|
+
});
|
|
178
|
+
expect(consoleError).toHaveBeenCalledWith(
|
|
179
|
+
"[assistant-ui] Assistant transport onFinish callback threw an error",
|
|
180
|
+
error,
|
|
181
|
+
);
|
|
182
|
+
} finally {
|
|
183
|
+
consoleError.mockRestore();
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
149
187
|
it("cancel returns combined in-flight and queued commands", async () => {
|
|
150
188
|
const onCancel = vi.fn();
|
|
151
189
|
const { result } = renderHook(() =>
|
|
@@ -92,7 +92,9 @@ export type AssistantTransportProtocol = "data-stream" | "assistant-transport";
|
|
|
92
92
|
|
|
93
93
|
export type SendCommandsRequestBody = {
|
|
94
94
|
commands: QueuedCommand[];
|
|
95
|
-
|
|
95
|
+
/** Absent on a resume with `resumeStateApi`; the server replays from its retained snapshot. */
|
|
96
|
+
state?: unknown;
|
|
97
|
+
runId?: string;
|
|
96
98
|
system: string | undefined;
|
|
97
99
|
tools: Record<string, unknown> | undefined;
|
|
98
100
|
callSettings: LanguageModelV1CallSettings | undefined;
|
|
@@ -109,6 +111,8 @@ export type AssistantTransportOptions<T> = {
|
|
|
109
111
|
initialState: T;
|
|
110
112
|
api: string;
|
|
111
113
|
resumeApi?: string;
|
|
114
|
+
/** Endpoint that returns the retained initial state and run ID for a resume stream. A 204 response means no run is active and the resume is skipped. */
|
|
115
|
+
resumeStateApi?: string;
|
|
112
116
|
protocol?: AssistantTransportProtocol;
|
|
113
117
|
/**
|
|
114
118
|
* When `false`, stream decoding and state reconciliation tolerate malformed
|
|
@@ -16,6 +16,13 @@ Command Scheduling
|
|
|
16
16
|
- If no run is in progress: start a run immediately and flush commands to the server.
|
|
17
17
|
- A follow-up run that finds an empty queue is a no-op: no request is sent and no error is surfaced.
|
|
18
18
|
- A resume run sends no commands; commands enqueued while it is pending or active are flushed in a follow-up run after it settles.
|
|
19
|
+
|
|
20
|
+
Resume State
|
|
21
|
+
|
|
22
|
+
- With `resumeStateApi` configured, a resume first posts `{ threadId }` there; the endpoint returns `{ runId, state }`, the state that started the active run.
|
|
23
|
+
- The resume request carries `runId` and no `state`: the server replays from the snapshot it retained for that run, so neither `body` overrides nor a `prepareSendCommandsRequest` rebuild can substitute a different base. `runId` takes precedence over `body` fields and is re-attached after `prepareSendCommandsRequest` so a rebuilt body cannot drop it. The local base is replaced by the snapshot only after the resume stream responds OK, so a rejected resume keeps the local state.
|
|
24
|
+
- A 204 response means no active run: the resume is skipped without error, and queued commands are flushed in a follow-up run.
|
|
25
|
+
- A malformed snapshot response fails the resume before the replay request is sent.
|
|
19
26
|
- Runs execute on a `queueMicrotask`, so multiple synchronous enqueues coalesce into a single request: the first run's flush takes all of them, and the coalesced follow-up run no-ops.
|
|
20
27
|
|
|
21
28
|
Command Queue
|