@assistant-ui/core 0.1.17 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +45 -0
  2. package/dist/index.d.ts +5 -4
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/react/client/Tools.d.ts +6 -1
  7. package/dist/react/client/Tools.d.ts.map +1 -1
  8. package/dist/react/client/Tools.js +16 -19
  9. package/dist/react/client/Tools.js.map +1 -1
  10. package/dist/react/index.d.ts +2 -1
  11. package/dist/react/index.d.ts.map +1 -1
  12. package/dist/react/index.js +1 -0
  13. package/dist/react/index.js.map +1 -1
  14. package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
  15. package/dist/react/primitive-hooks/useComposerSend.js +2 -3
  16. package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
  17. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts +5 -0
  18. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -0
  19. package/dist/react/primitive-hooks/useThreadListLoadMore.js +11 -0
  20. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -0
  21. package/dist/react/primitives/message/MessageAttachments.js +1 -1
  22. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  23. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  24. package/dist/react/primitives/message/MessageParts.js +14 -10
  25. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  26. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +2 -2
  27. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  28. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +4 -3
  29. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  30. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +8 -4
  31. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  32. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +86 -38
  33. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  34. package/dist/react/runtimes/cloud/auiV0.d.ts +10 -1
  35. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  36. package/dist/react/runtimes/cloud/auiV0.js +21 -3
  37. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  38. package/dist/react/runtimes/useLocalRuntime.d.ts +1 -1
  39. package/dist/react/runtimes/useToolInvocations.d.ts +2 -1
  40. package/dist/react/runtimes/useToolInvocations.d.ts.map +1 -1
  41. package/dist/react/runtimes/useToolInvocations.js +16 -1
  42. package/dist/react/runtimes/useToolInvocations.js.map +1 -1
  43. package/dist/react/types/scopes/tools.d.ts +4 -0
  44. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  45. package/dist/runtime/api/assistant-runtime.d.ts +0 -33
  46. package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
  47. package/dist/runtime/api/assistant-runtime.js +0 -23
  48. package/dist/runtime/api/assistant-runtime.js.map +1 -1
  49. package/dist/runtime/api/bindings.d.ts +1 -3
  50. package/dist/runtime/api/bindings.d.ts.map +1 -1
  51. package/dist/runtime/api/composer-runtime.d.ts +1 -0
  52. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  53. package/dist/runtime/api/composer-runtime.js +2 -0
  54. package/dist/runtime/api/composer-runtime.js.map +1 -1
  55. package/dist/runtime/api/message-runtime.d.ts +1 -6
  56. package/dist/runtime/api/message-runtime.d.ts.map +1 -1
  57. package/dist/runtime/api/message-runtime.js.map +1 -1
  58. package/dist/runtime/api/thread-list-runtime.d.ts +4 -0
  59. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  60. package/dist/runtime/api/thread-list-runtime.js +6 -0
  61. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  62. package/dist/runtime/api/thread-runtime.d.ts +3 -24
  63. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  64. package/dist/runtime/api/thread-runtime.js +1 -20
  65. package/dist/runtime/api/thread-runtime.js.map +1 -1
  66. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -0
  67. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  68. package/dist/runtime/base/base-composer-runtime-core.js +1 -1
  69. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  70. package/dist/runtime/base/base-thread-runtime-core.d.ts +1 -1
  71. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  72. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts +1 -0
  73. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
  74. package/dist/runtime/base/default-edit-composer-runtime-core.js +3 -0
  75. package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
  76. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -0
  77. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  78. package/dist/runtime/base/default-thread-composer-runtime-core.js +12 -1
  79. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  80. package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -0
  81. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  82. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +3 -0
  83. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  84. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -4
  85. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  86. package/dist/runtime/utils/chat-model-adapter.d.ts +0 -4
  87. package/dist/runtime/utils/chat-model-adapter.d.ts.map +1 -1
  88. package/dist/runtime/utils/external-store-message.d.ts +0 -4
  89. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  90. package/dist/runtime/utils/external-store-message.js +0 -7
  91. package/dist/runtime/utils/external-store-message.js.map +1 -1
  92. package/dist/runtimes/assistant-transport/utils.d.ts +0 -9
  93. package/dist/runtimes/assistant-transport/utils.d.ts.map +1 -1
  94. package/dist/runtimes/assistant-transport/utils.js +0 -13
  95. package/dist/runtimes/assistant-transport/utils.js.map +1 -1
  96. package/dist/runtimes/external-store/external-store-adapter.d.ts +15 -0
  97. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  98. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +1 -1
  99. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  100. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +14 -12
  101. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  102. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +1 -1
  103. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  104. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +2 -3
  105. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  106. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  107. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  108. package/dist/runtimes/local/local-thread-runtime-core.js +1 -4
  109. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  110. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +2 -1
  111. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  112. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +2 -3
  113. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  114. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
  115. package/dist/runtimes/remote-thread-list/empty-thread-core.js +2 -3
  116. package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
  117. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
  118. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  119. package/dist/runtimes/remote-thread-list/remote-thread-state.js +34 -0
  120. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  121. package/dist/runtimes/remote-thread-list/types.d.ts +5 -1
  122. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  123. package/dist/store/clients/no-op-composer-client.d.ts.map +1 -1
  124. package/dist/store/clients/no-op-composer-client.js +1 -0
  125. package/dist/store/clients/no-op-composer-client.js.map +1 -1
  126. package/dist/store/clients/thread-message-client.d.ts.map +1 -1
  127. package/dist/store/clients/thread-message-client.js +0 -1
  128. package/dist/store/clients/thread-message-client.js.map +1 -1
  129. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  130. package/dist/store/runtime-clients/composer-runtime-client.js +1 -0
  131. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  132. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  133. package/dist/store/runtime-clients/thread-list-runtime-client.js +3 -0
  134. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  135. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  136. package/dist/store/runtime-clients/thread-runtime-client.js +0 -1
  137. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  138. package/dist/store/scopes/composer.d.ts +9 -0
  139. package/dist/store/scopes/composer.d.ts.map +1 -1
  140. package/dist/store/scopes/message.d.ts +1 -3
  141. package/dist/store/scopes/message.d.ts.map +1 -1
  142. package/dist/store/scopes/thread.d.ts +0 -4
  143. package/dist/store/scopes/thread.d.ts.map +1 -1
  144. package/dist/store/scopes/threads.d.ts +3 -0
  145. package/dist/store/scopes/threads.d.ts.map +1 -1
  146. package/dist/types/index.d.ts +1 -1
  147. package/dist/types/index.d.ts.map +1 -1
  148. package/dist/types/message.d.ts +28 -1
  149. package/dist/types/message.d.ts.map +1 -1
  150. package/dist/types/message.js +2 -1
  151. package/dist/types/message.js.map +1 -1
  152. package/package.json +11 -12
  153. package/src/index.ts +8 -6
  154. package/src/react/client/Tools.ts +46 -22
  155. package/src/react/index.ts +2 -1
  156. package/src/react/primitive-hooks/useComposerSend.ts +2 -3
  157. package/src/react/primitive-hooks/useThreadListLoadMore.ts +15 -0
  158. package/src/react/primitives/message/MessageAttachments.test.tsx +50 -0
  159. package/src/react/primitives/message/MessageAttachments.tsx +1 -1
  160. package/src/react/primitives/message/MessageParts.tsx +20 -9
  161. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +7 -6
  162. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +96 -43
  163. package/src/react/runtimes/cloud/auiV0.ts +37 -4
  164. package/src/react/runtimes/useToolInvocations.ts +21 -1
  165. package/src/react/types/scopes/tools.ts +5 -0
  166. package/src/runtime/api/assistant-runtime.ts +0 -62
  167. package/src/runtime/api/bindings.ts +1 -6
  168. package/src/runtime/api/composer-runtime.ts +3 -0
  169. package/src/runtime/api/message-runtime.ts +1 -8
  170. package/src/runtime/api/thread-list-runtime.ts +10 -0
  171. package/src/runtime/api/thread-runtime.ts +1 -46
  172. package/src/runtime/base/base-composer-runtime-core.ts +2 -1
  173. package/src/runtime/base/base-thread-runtime-core.ts +1 -1
  174. package/src/runtime/base/default-edit-composer-runtime-core.ts +4 -0
  175. package/src/runtime/base/default-thread-composer-runtime-core.ts +12 -1
  176. package/src/runtime/interfaces/composer-runtime-core.ts +1 -0
  177. package/src/runtime/interfaces/thread-list-runtime-core.ts +3 -0
  178. package/src/runtime/interfaces/thread-runtime-core.ts +6 -5
  179. package/src/runtime/utils/chat-model-adapter.ts +0 -5
  180. package/src/runtime/utils/external-store-message.ts +0 -8
  181. package/src/runtimes/assistant-transport/utils.ts +0 -28
  182. package/src/runtimes/external-store/external-store-adapter.ts +15 -0
  183. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +15 -9
  184. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +2 -4
  185. package/src/runtimes/local/local-thread-runtime-core.ts +1 -5
  186. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +2 -4
  187. package/src/runtimes/remote-thread-list/empty-thread-core.ts +2 -4
  188. package/src/runtimes/remote-thread-list/remote-thread-state.ts +54 -1
  189. package/src/runtimes/remote-thread-list/types.ts +6 -1
  190. package/src/store/clients/no-op-composer-client.ts +1 -0
  191. package/src/store/clients/thread-message-client.ts +0 -1
  192. package/src/store/runtime-clients/composer-runtime-client.ts +1 -0
  193. package/src/store/runtime-clients/thread-list-runtime-client.ts +3 -0
  194. package/src/store/runtime-clients/thread-runtime-client.ts +0 -1
  195. package/src/store/scopes/composer.ts +9 -0
  196. package/src/store/scopes/message.ts +1 -6
  197. package/src/store/scopes/thread.ts +0 -5
  198. package/src/store/scopes/threads.ts +3 -0
  199. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +448 -0
  200. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +6 -1
  201. package/src/tests/auiV0Encode.test.ts +55 -0
  202. package/src/tests/composer-can-send.test.ts +112 -0
  203. package/src/tests/external-store-thread-list-runtime-core.test.ts +34 -0
  204. package/src/types/index.ts +2 -0
  205. package/src/types/message.ts +44 -7
@@ -0,0 +1,50 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import type { ReactElement } from "react";
3
+ import { MessagePrimitiveAttachments } from "./MessageAttachments";
4
+
5
+ const mockUseAuiState = vi.fn();
6
+ type UseAuiStateSelector = Parameters<
7
+ typeof import("@assistant-ui/store")["useAuiState"]
8
+ >[0];
9
+ type AttachmentsElement = ReactElement<{ children: () => null }>;
10
+
11
+ vi.mock("react", async (importOriginal) => {
12
+ const actual = await importOriginal<typeof import("react")>();
13
+ return {
14
+ ...actual,
15
+ useMemo: (factory: () => unknown) => factory(),
16
+ };
17
+ });
18
+
19
+ vi.mock("@assistant-ui/store", async (importOriginal) => {
20
+ const actual = await importOriginal<typeof import("@assistant-ui/store")>();
21
+ return {
22
+ ...actual,
23
+ useAuiState: (selector: UseAuiStateSelector) => mockUseAuiState(selector),
24
+ };
25
+ });
26
+
27
+ const renderAttachmentsInner = () => {
28
+ const element = MessagePrimitiveAttachments({
29
+ children: () => null,
30
+ }) as AttachmentsElement;
31
+
32
+ const Inner = element.type as (props: typeof element.props) => unknown;
33
+ return Inner(element.props);
34
+ };
35
+
36
+ describe("MessagePrimitiveAttachments", () => {
37
+ it("treats missing user message attachments as empty", () => {
38
+ mockUseAuiState.mockImplementation((selector: UseAuiStateSelector) =>
39
+ selector({
40
+ message: {
41
+ role: "user",
42
+ attachments: undefined,
43
+ },
44
+ } as never),
45
+ );
46
+
47
+ expect(() => renderAttachmentsInner()).not.toThrow();
48
+ expect(renderAttachmentsInner()).toEqual([]);
49
+ });
50
+ });
@@ -93,7 +93,7 @@ const MessagePrimitiveAttachmentsInner: FC<{
93
93
  }> = ({ children }) => {
94
94
  const attachmentsCount = useAuiState((s) => {
95
95
  if (s.message.role !== "user") return 0;
96
- return s.message.attachments.length;
96
+ return (s.message.attachments ?? []).length;
97
97
  });
98
98
 
99
99
  return useMemo(
@@ -31,8 +31,9 @@ import type {
31
31
  ReasoningGroupComponent,
32
32
  QuoteMessagePartComponent,
33
33
  } from "../../types/MessagePartComponentTypes";
34
- import type { MessagePartStatus } from "../../../types/message";
34
+ import { isMcpAppUri, type MessagePartStatus } from "../../../types/message";
35
35
  import type { DataRenderersState } from "../../types/scopes/dataRenderers";
36
+ import type { ToolsState } from "../../types/scopes/tools";
36
37
  import { useShallow } from "zustand/shallow";
37
38
 
38
39
  type MessagePartRange =
@@ -511,6 +512,19 @@ const QuoteRendererImpl: FC<{ Quote: QuoteMessagePartComponent }> = ({
511
512
 
512
513
  const QuoteRenderer = memo(QuoteRendererImpl);
513
514
 
515
+ function resolveToolRender(
516
+ toolsState: ToolsState,
517
+ part: Extract<PartState, { type: "tool-call" }>,
518
+ ): ToolCallMessagePartComponent | null {
519
+ const entry = toolsState.tools[part.toolName];
520
+ const named = Array.isArray(entry) ? (entry[0] ?? null) : (entry ?? null);
521
+ if (named) return named;
522
+ if (isMcpAppUri(part.mcp?.app?.resourceUri) && toolsState.mcpApp) {
523
+ return toolsState.mcpApp.render;
524
+ }
525
+ return null;
526
+ }
527
+
514
528
  /**
515
529
  * Stable propless component that renders the registered tool UI for the
516
530
  * current part context. Reads tool registry and part state from context.
@@ -518,12 +532,9 @@ const QuoteRenderer = memo(QuoteRendererImpl);
518
532
  const RegisteredToolUI: FC = () => {
519
533
  const aui = useAui();
520
534
  const part = useAuiState((s) => s.part);
521
- const Render = useAuiState((s) => {
522
- if (s.part.type !== "tool-call") return null;
523
- const entry = s.tools.tools[s.part.toolName];
524
- if (Array.isArray(entry)) return entry[0] ?? null;
525
- return entry ?? null;
526
- });
535
+ const Render = useAuiState((s) =>
536
+ s.part.type === "tool-call" ? resolveToolRender(s.tools, s.part) : null,
537
+ );
527
538
 
528
539
  if (!Render || part.type !== "tool-call") return null;
529
540
 
@@ -638,8 +649,8 @@ export const MessagePartChildren: FC<{
638
649
  get part() {
639
650
  const state = getItem();
640
651
  if (state.type === "tool-call") {
641
- const entry = aui.tools().getState().tools[state.toolName];
642
- const hasUI = Array.isArray(entry) ? !!entry[0] : !!entry;
652
+ const toolsState = aui.tools().getState();
653
+ const hasUI = resolveToolRender(toolsState, state) !== null;
643
654
  const partMethods = aui.message().part({ index });
644
655
  return {
645
656
  ...state,
@@ -94,7 +94,8 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
94
94
  }
95
95
  }
96
96
 
97
- // Rendered outside the user's Provider so deferred `children` cannot strand the binding.
97
+ // Rendered as a child of the user's Provider so the runtime hook can
98
+ // read context the Provider injects (e.g. RuntimeAdapterProvider).
98
99
  private _RuntimeBinder: FC<PropsWithChildren<{ threadId: string }>> = ({
99
100
  threadId,
100
101
  children,
@@ -172,7 +173,7 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
172
173
  console.warn(
173
174
  "RemoteThreadListAdapter.unstable_Provider did not render its `children` synchronously. " +
174
175
  "Render `children` on first commit; deferring them behind a loading state, Suspense boundary, " +
175
- "or `useEffect` gate leaves thread context unavailable to downstream consumers.",
176
+ "or `useEffect` gate strands the runtime binder and leaves the thread without context.",
176
177
  );
177
178
  }
178
179
  }, 100);
@@ -183,11 +184,11 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
183
184
 
184
185
  return (
185
186
  <ThreadListItemRuntimeProvider runtime={runtime}>
186
- <this._RuntimeBinder threadId={threadId}>
187
- <Provider>
187
+ <Provider>
188
+ <this._RuntimeBinder threadId={threadId}>
188
189
  <ProviderRenderDetector detectorRef={detectorRef} />
189
- </Provider>
190
- </this._RuntimeBinder>
190
+ </this._RuntimeBinder>
191
+ </Provider>
191
192
  </ThreadListItemRuntimeProvider>
192
193
  );
193
194
  });
@@ -5,12 +5,13 @@ import { OptimisticState } from "../../runtimes/remote-thread-list/optimistic-st
5
5
  import { EMPTY_THREAD_CORE } from "../../runtimes/remote-thread-list/empty-thread-core";
6
6
  import type {
7
7
  RemoteThreadData,
8
- THREAD_MAPPING_ID,
9
8
  RemoteThreadState,
10
9
  } from "../../runtimes/remote-thread-list/remote-thread-state";
11
10
  import {
11
+ classifyThreads,
12
12
  createThreadMappingId,
13
13
  getThreadData,
14
+ normalizeCursor,
14
15
  updateStatusReducer,
15
16
  } from "../../runtimes/remote-thread-list/remote-thread-state";
16
17
  import type { RemoteThreadListOptions } from "../../runtimes/remote-thread-list/types";
@@ -29,11 +30,14 @@ export class RemoteThreadListThreadListRuntimeCore
29
30
  private readonly _hookManager: RemoteThreadListHookInstanceManager;
30
31
 
31
32
  private _loadThreadsPromise: Promise<void> | undefined;
33
+ private _loadMorePromise: Promise<void> | undefined;
32
34
  private _loadGeneration = 0;
33
35
 
34
36
  private _mainThreadId!: string;
35
37
  private readonly _state = new OptimisticState<RemoteThreadState>({
36
38
  isLoading: true,
39
+ isLoadingMore: false,
40
+ cursor: undefined,
37
41
  newThreadId: undefined,
38
42
  threadIds: [],
39
43
  archivedThreadIds: [],
@@ -61,62 +65,33 @@ export class RemoteThreadListThreadListRuntimeCore
61
65
  // biome-ignore lint/suspicious/noThenProperty: OptimisticState reducer pattern
62
66
  then: (state, l) => {
63
67
  if (generation !== this._loadGeneration) return state;
64
- const newThreadIds = [];
65
- const newArchivedThreadIds = [];
66
- const newThreadIdMap = {} as Record<string, THREAD_MAPPING_ID>;
67
- const newThreadData = {} as Record<
68
- THREAD_MAPPING_ID,
69
- RemoteThreadData
70
- >;
71
-
72
- for (const thread of l.threads) {
73
- switch (thread.status) {
74
- case "regular":
75
- newThreadIds.push(thread.remoteId);
76
- break;
77
- case "archived":
78
- newArchivedThreadIds.push(thread.remoteId);
79
- break;
80
- default: {
81
- const _exhaustiveCheck: never = thread.status;
82
- throw new Error(`Unsupported state: ${_exhaustiveCheck}`);
83
- }
84
- }
85
-
86
- const mappingId = createThreadMappingId(thread.remoteId);
87
- newThreadIdMap[thread.remoteId] = mappingId;
88
- newThreadData[mappingId] = {
89
- id: thread.remoteId,
90
- remoteId: thread.remoteId,
91
- externalId: thread.externalId,
92
- status: thread.status,
93
- title: thread.title,
94
- custom: thread.custom,
95
- initializeTask: Promise.resolve({
96
- remoteId: thread.remoteId,
97
- externalId: thread.externalId,
98
- }),
99
- };
100
- }
68
+ const fresh = classifyThreads(l.threads, {
69
+ threadIds: [],
70
+ archivedThreadIds: [],
71
+ threadIdMap: {},
72
+ threadData: {},
73
+ });
101
74
 
102
75
  return {
103
76
  ...state,
104
77
  isLoading: false,
105
- threadIds: newThreadIds,
106
- archivedThreadIds: newArchivedThreadIds,
78
+ cursor: normalizeCursor(l.nextCursor),
79
+ threadIds: fresh.threadIds,
80
+ archivedThreadIds: fresh.archivedThreadIds,
107
81
  threadIdMap: {
108
82
  ...state.threadIdMap,
109
- ...newThreadIdMap,
83
+ ...fresh.threadIdMap,
110
84
  },
111
85
  threadData: {
112
86
  ...state.threadData,
113
- ...newThreadData,
87
+ ...fresh.threadData,
114
88
  },
115
89
  };
116
90
  },
117
91
  })
118
- .catch(() => {
92
+ .catch((error: unknown) => {
119
93
  if (generation !== this._loadGeneration) return;
94
+ console.error("[assistant-ui] thread list load failed:", error);
120
95
  this._loadThreadsPromise = undefined;
121
96
  this._state.update({
122
97
  ...this._state.baseValue,
@@ -129,6 +104,58 @@ export class RemoteThreadListThreadListRuntimeCore
129
104
  return this._loadThreadsPromise;
130
105
  }
131
106
 
107
+ public loadMore(): Promise<void> {
108
+ if (this._loadMorePromise) return this._loadMorePromise;
109
+
110
+ const initialState = this._state.value;
111
+ if (initialState.cursor === undefined || initialState.isLoading) {
112
+ return Promise.resolve();
113
+ }
114
+
115
+ const generation = this._loadGeneration;
116
+ const adapter = this._options.adapter;
117
+ const cursor = initialState.cursor;
118
+
119
+ const dedup = this._state
120
+ .optimisticUpdate({
121
+ execute: () => adapter.list({ after: cursor }),
122
+ loading: (state) => ({ ...state, isLoadingMore: true }),
123
+ // biome-ignore lint/suspicious/noThenProperty: OptimisticState reducer pattern
124
+ then: (state, l) => {
125
+ if (generation !== this._loadGeneration) return state;
126
+ if (adapter !== this._options.adapter) return state;
127
+
128
+ const appended = classifyThreads(l.threads, {
129
+ threadIds: [...state.threadIds],
130
+ archivedThreadIds: [...state.archivedThreadIds],
131
+ threadIdMap: { ...state.threadIdMap },
132
+ threadData: { ...state.threadData },
133
+ });
134
+
135
+ return {
136
+ ...state,
137
+ isLoadingMore: false,
138
+ cursor: normalizeCursor(l.nextCursor),
139
+ threadIds: appended.threadIds,
140
+ archivedThreadIds: appended.archivedThreadIds,
141
+ threadIdMap: appended.threadIdMap,
142
+ threadData: appended.threadData,
143
+ };
144
+ },
145
+ })
146
+ .catch((error: unknown) => {
147
+ console.error("[assistant-ui] thread list loadMore failed:", error);
148
+ })
149
+ .then(() => {
150
+ if (this._loadMorePromise === dedup) {
151
+ this._loadMorePromise = undefined;
152
+ }
153
+ });
154
+
155
+ this._loadMorePromise = dedup;
156
+ return dedup;
157
+ }
158
+
132
159
  private readonly contextProvider: ModelContextProvider;
133
160
 
134
161
  constructor(
@@ -156,6 +183,9 @@ export class RemoteThreadListThreadListRuntimeCore
156
183
  public __internal_setOptions(options: RemoteThreadListOptions) {
157
184
  if (this._options === options) return;
158
185
 
186
+ const adapterChanged =
187
+ this._options !== undefined && this._options.adapter !== options.adapter;
188
+
159
189
  this._options = options;
160
190
 
161
191
  const Provider = options.adapter.unstable_Provider ?? Fragment;
@@ -164,6 +194,16 @@ export class RemoteThreadListThreadListRuntimeCore
164
194
  }
165
195
 
166
196
  this._hookManager.setRuntimeHook(options.runtimeHook);
197
+
198
+ if (adapterChanged) {
199
+ this._loadGeneration++;
200
+ this._loadThreadsPromise = undefined;
201
+ this._loadMorePromise = undefined;
202
+ this._state.update({
203
+ ...this._state.baseValue,
204
+ cursor: undefined,
205
+ });
206
+ }
167
207
  }
168
208
 
169
209
  public __internal_load() {
@@ -179,6 +219,11 @@ export class RemoteThreadListThreadListRuntimeCore
179
219
  public reload() {
180
220
  this._loadGeneration++;
181
221
  this._loadThreadsPromise = undefined;
222
+ this._loadMorePromise = undefined;
223
+ this._state.update({
224
+ ...this._state.baseValue,
225
+ cursor: undefined,
226
+ });
182
227
  return this.getLoadThreadsPromise();
183
228
  }
184
229
 
@@ -186,6 +231,14 @@ export class RemoteThreadListThreadListRuntimeCore
186
231
  return this._state.value.isLoading;
187
232
  }
188
233
 
234
+ public get isLoadingMore() {
235
+ return this._state.value.isLoadingMore;
236
+ }
237
+
238
+ public get hasMore() {
239
+ return this._state.value.cursor !== undefined;
240
+ }
241
+
189
242
  public get threadIds() {
190
243
  return this._state.value.threadIds;
191
244
  }
@@ -1,4 +1,8 @@
1
- import type { MessageStatus, ThreadMessage } from "../../../types/message";
1
+ import type {
2
+ MessageStatus,
3
+ SourceProviderMetadata,
4
+ ThreadMessage,
5
+ } from "../../../types/message";
2
6
  import { fromThreadMessageLike } from "../../../runtime/utils/thread-message-like";
3
7
  import type { CloudMessage } from "assistant-cloud";
4
8
  import { isJSONValue } from "../../../utils/json/is-json";
@@ -23,6 +27,16 @@ type AuiV0MessagePart =
23
27
  readonly id: string;
24
28
  readonly url: string;
25
29
  readonly title?: string;
30
+ readonly providerMetadata?: SourceProviderMetadata;
31
+ }
32
+ | {
33
+ readonly type: "source";
34
+ readonly sourceType: "document";
35
+ readonly id: string;
36
+ readonly title: string;
37
+ readonly mediaType: string;
38
+ readonly filename?: string;
39
+ readonly providerMetadata?: SourceProviderMetadata;
26
40
  }
27
41
  | {
28
42
  readonly type: "tool-call";
@@ -90,12 +104,31 @@ export function auiV0Encode(message: ThreadMessage): AuiV0Message {
90
104
  return { type: "reasoning", text: part.text };
91
105
 
92
106
  case "source":
107
+ if (part.sourceType === "url") {
108
+ return {
109
+ type: "source",
110
+ sourceType: "url",
111
+ id: part.id,
112
+ url: part.url,
113
+ ...(part.title != null ? { title: part.title } : undefined),
114
+ ...(part.providerMetadata != null
115
+ ? { providerMetadata: part.providerMetadata }
116
+ : undefined),
117
+ };
118
+ }
119
+
93
120
  return {
94
121
  type: "source",
95
- sourceType: part.sourceType,
122
+ sourceType: "document",
96
123
  id: part.id,
97
- url: part.url,
98
- ...(part.title ? { title: part.title } : undefined),
124
+ title: part.title,
125
+ mediaType: part.mediaType,
126
+ ...(part.filename != null
127
+ ? { filename: part.filename }
128
+ : undefined),
129
+ ...(part.providerMetadata != null
130
+ ? { providerMetadata: part.providerMetadata }
131
+ : undefined),
99
132
  };
100
133
 
101
134
  case "tool-call": {
@@ -7,6 +7,7 @@ import {
7
7
  ToolResponse,
8
8
  unstable_toolResultStream,
9
9
  type Tool,
10
+ type ToolModelContentPart,
10
11
  } from "assistant-stream";
11
12
  import {
12
13
  AssistantMetaTransformStream,
@@ -28,6 +29,7 @@ export type AddToolResultCommand = {
28
29
  readonly result: ReadonlyJSONValue;
29
30
  readonly isError: boolean;
30
31
  readonly artifact?: ReadonlyJSONValue;
32
+ readonly modelContent?: readonly ToolModelContentPart[];
31
33
  };
32
34
 
33
35
  const isArgsTextComplete = (argsText: string) => {
@@ -124,6 +126,7 @@ export function useToolInvocations({
124
126
  Object.entries(tools).map(([name, tool]) => {
125
127
  const execute = tool.execute;
126
128
  const streamCall = tool.streamCall;
129
+ const toModelOutput = tool.toModelOutput;
127
130
 
128
131
  const wrappedTool = {
129
132
  ...tool,
@@ -145,6 +148,15 @@ export function useToolInvocations({
145
148
  toolCallId: getLogicalToolCallId(context.toolCallId),
146
149
  }),
147
150
  }),
151
+ ...(toModelOutput !== undefined && {
152
+ toModelOutput: (
153
+ options: Parameters<NonNullable<typeof toModelOutput>>[0],
154
+ ) =>
155
+ toModelOutput({
156
+ ...options,
157
+ toolCallId: getLogicalToolCallId(options.toolCallId),
158
+ }),
159
+ }),
148
160
  } as Tool;
149
161
  return [name, wrappedTool];
150
162
  }),
@@ -249,7 +261,12 @@ export function useToolInvocations({
249
261
  toolName: chunk.meta.toolName,
250
262
  result: chunk.result,
251
263
  isError: chunk.isError,
252
- ...(chunk.artifact && { artifact: chunk.artifact }),
264
+ ...(chunk.artifact !== undefined && {
265
+ artifact: chunk.artifact,
266
+ }),
267
+ ...(chunk.modelContent !== undefined && {
268
+ modelContent: chunk.modelContent,
269
+ }),
253
270
  });
254
271
  }
255
272
  },
@@ -533,6 +550,9 @@ export function useToolInvocations({
533
550
  result: content.result as ReadonlyJSONValue,
534
551
  artifact: content.artifact as ReadonlyJSONValue | undefined,
535
552
  isError: content.isError,
553
+ ...(content.modelContent !== undefined
554
+ ? { modelContent: content.modelContent }
555
+ : {}),
536
556
  }),
537
557
  );
538
558
  lastState.controller.close();
@@ -1,8 +1,13 @@
1
1
  import type { ToolCallMessagePartComponent } from "../MessagePartComponentTypes";
2
2
  import type { Unsubscribe } from "../../..";
3
3
 
4
+ export type McpAppResourceOutput = {
5
+ readonly render: ToolCallMessagePartComponent;
6
+ };
7
+
4
8
  export type ToolsState = {
5
9
  tools: Record<string, ToolCallMessagePartComponent[]>;
10
+ mcpApp?: McpAppResourceOutput | undefined;
6
11
  };
7
12
 
8
13
  export type ToolsMethods = {
@@ -6,8 +6,6 @@ import {
6
6
  type ThreadListRuntime,
7
7
  ThreadListRuntimeImpl,
8
8
  } from "./thread-list-runtime";
9
- import { ExportedMessageRepository } from "../utils/message-repository";
10
- import type { ThreadMessageLike } from "../utils/thread-message-like";
11
9
 
12
10
  export type AssistantRuntime = {
13
11
  /**
@@ -20,49 +18,16 @@ export type AssistantRuntime = {
20
18
  */
21
19
  readonly thread: ThreadRuntime;
22
20
 
23
- /**
24
- * @deprecated This field was renamed to `threads`.
25
- */
26
- readonly threadList: ThreadListRuntime;
27
-
28
- /**
29
- * Switch to a new thread.
30
- *
31
- * @deprecated This method was moved to `threads.switchToNewThread`.
32
- */
33
- switchToNewThread(): void;
34
-
35
- /**
36
- * Switch to a thread.
37
- *
38
- * @param threadId The thread ID to switch to.
39
- * @deprecated This method was moved to `threads.switchToThread`.
40
- */
41
- switchToThread(threadId: string): void;
42
-
43
21
  /**
44
22
  * Register a model context provider. Model context providers are configuration such as system message, temperature, etc. that are set in the frontend.
45
23
  *
46
24
  * @param provider The model context provider to register.
47
25
  */
48
26
  registerModelContextProvider(provider: ModelContextProvider): Unsubscribe;
49
-
50
- /**
51
- * @deprecated This method was renamed to `registerModelContextProvider`.
52
- */
53
- registerModelConfigProvider(provider: ModelContextProvider): Unsubscribe;
54
-
55
- /**
56
- * @deprecated Use `runtime.thread.reset(initialMessages)`.
57
- */
58
- reset: unknown; // make it a type error
59
27
  };
60
28
 
61
29
  export class AssistantRuntimeImpl implements AssistantRuntime {
62
30
  public readonly threads;
63
- public get threadList() {
64
- return this.threads;
65
- }
66
31
 
67
32
  public readonly _thread: ThreadRuntime;
68
33
 
@@ -74,42 +39,15 @@ export class AssistantRuntimeImpl implements AssistantRuntime {
74
39
  }
75
40
 
76
41
  protected __internal_bindMethods() {
77
- this.switchToNewThread = this.switchToNewThread.bind(this);
78
- this.switchToThread = this.switchToThread.bind(this);
79
42
  this.registerModelContextProvider =
80
43
  this.registerModelContextProvider.bind(this);
81
- this.registerModelConfigProvider =
82
- this.registerModelConfigProvider.bind(this);
83
- this.reset = this.reset.bind(this);
84
44
  }
85
45
 
86
46
  public get thread() {
87
47
  return this._thread;
88
48
  }
89
49
 
90
- public switchToNewThread() {
91
- return this._core.threads.switchToNewThread();
92
- }
93
-
94
- public switchToThread(threadId: string) {
95
- return this._core.threads.switchToThread(threadId);
96
- }
97
-
98
50
  public registerModelContextProvider(provider: ModelContextProvider) {
99
51
  return this._core.registerModelContextProvider(provider);
100
52
  }
101
-
102
- public registerModelConfigProvider(provider: ModelContextProvider) {
103
- return this.registerModelContextProvider(provider);
104
- }
105
-
106
- public reset({
107
- initialMessages,
108
- }: {
109
- initialMessages?: ThreadMessageLike[];
110
- } = {}) {
111
- return this._core.threads
112
- .getMainThreadRuntimeCore()
113
- .import(ExportedMessageRepository.fromArray(initialMessages ?? []));
114
- }
115
53
  }
@@ -5,10 +5,7 @@ import type {
5
5
  EditComposerRuntimeCore,
6
6
  ThreadComposerRuntimeCore,
7
7
  } from "../interfaces/composer-runtime-core";
8
- import type {
9
- SpeechState,
10
- SubmittedFeedback,
11
- } from "../interfaces/thread-runtime-core";
8
+ import type { SpeechState } from "../interfaces/thread-runtime-core";
12
9
  import type { ComposerRuntimePath, MessageRuntimePath } from "./paths";
13
10
 
14
11
  export type ComposerRuntimeCoreBinding = SubscribableWithState<
@@ -34,8 +31,6 @@ export type MessageStateBinding = SubscribableWithState<
34
31
  readonly branchNumber: number;
35
32
  readonly branchCount: number;
36
33
  readonly speech: SpeechState | undefined;
37
- /** @deprecated Use `message.metadata.submittedFeedback` instead. This will be removed in 0.12.0. */
38
- readonly submittedFeedback: SubmittedFeedback | undefined;
39
34
  },
40
35
  MessageRuntimePath
41
36
  >;
@@ -41,6 +41,7 @@ export type {
41
41
 
42
42
  type BaseComposerState = {
43
43
  readonly canCancel: boolean;
44
+ readonly canSend: boolean;
44
45
  readonly isEditing: boolean;
45
46
  readonly isEmpty: boolean;
46
47
 
@@ -86,6 +87,7 @@ const getThreadComposerState = (
86
87
 
87
88
  isEditing: runtime?.isEditing ?? false,
88
89
  canCancel: runtime?.canCancel ?? false,
90
+ canSend: runtime?.canSend ?? false,
89
91
  isEmpty: runtime?.isEmpty ?? true,
90
92
 
91
93
  attachments: runtime?.attachments ?? EMPTY_ARRAY,
@@ -108,6 +110,7 @@ const getEditComposerState = (
108
110
 
109
111
  isEditing: runtime?.isEditing ?? false,
110
112
  canCancel: runtime?.canCancel ?? false,
113
+ canSend: runtime?.canSend ?? false,
111
114
  isEmpty: runtime?.isEmpty ?? true,
112
115
 
113
116
  text: runtime?.text ?? "",
@@ -1,7 +1,4 @@
1
- import type {
2
- SpeechState,
3
- SubmittedFeedback,
4
- } from "../interfaces/thread-runtime-core";
1
+ import type { SpeechState } from "../interfaces/thread-runtime-core";
5
2
  import { symbolInnerMessage } from "../utils/external-store-message";
6
3
  import type {
7
4
  ToolCallMessagePartStatus,
@@ -90,10 +87,6 @@ export type MessageState = ThreadMessage & {
90
87
  * @deprecated This API is still under active development and might change without notice.
91
88
  */
92
89
  readonly speech: SpeechState | undefined;
93
- /**
94
- * @deprecated Use `message.metadata.submittedFeedback` instead. This will be removed in 0.12.0.
95
- */
96
- readonly submittedFeedback: SubmittedFeedback | undefined;
97
90
  };
98
91
 
99
92
  export type { MessageStateBinding } from "./bindings";