@assistant-ui/react 0.11.35 → 0.11.37

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 (221) hide show
  1. package/dist/client/AssistantClient.d.ts +13 -57
  2. package/dist/client/AssistantClient.d.ts.map +1 -1
  3. package/dist/client/AssistantClient.js +49 -26
  4. package/dist/client/AssistantClient.js.map +1 -1
  5. package/dist/client/ModelContext.d.ts +8 -0
  6. package/dist/client/ModelContext.d.ts.map +1 -0
  7. package/dist/client/ModelContext.js +21 -0
  8. package/dist/client/ModelContext.js.map +1 -0
  9. package/dist/client/ModelContextClient.d.ts +7 -0
  10. package/dist/client/ModelContextClient.d.ts.map +1 -0
  11. package/dist/client/ModelContextClient.js +18 -0
  12. package/dist/client/ModelContextClient.js.map +1 -0
  13. package/dist/client/ToolUIClient.d.ts.map +1 -1
  14. package/dist/client/ToolUIClient.js +45 -3
  15. package/dist/client/ToolUIClient.js.map +1 -1
  16. package/dist/client/ToolUIContext.d.ts +4 -0
  17. package/dist/client/ToolUIContext.d.ts.map +1 -0
  18. package/dist/client/ToolUIContext.js +20 -0
  19. package/dist/client/ToolUIContext.js.map +1 -0
  20. package/dist/client/Tools.d.ts +10 -0
  21. package/dist/client/Tools.d.ts.map +1 -0
  22. package/dist/client/Tools.js +55 -0
  23. package/dist/client/Tools.js.map +1 -0
  24. package/dist/client/types/ModelContext.d.ts +12 -0
  25. package/dist/client/types/ModelContext.d.ts.map +1 -0
  26. package/dist/client/types/ModelContext.js +1 -0
  27. package/dist/client/types/ModelContext.js.map +1 -0
  28. package/dist/client/types/ThreadList.d.ts.map +1 -1
  29. package/dist/client/types/ToolUI.d.ts +12 -1
  30. package/dist/client/types/ToolUI.d.ts.map +1 -1
  31. package/dist/client/types/Tools.d.ts +9 -0
  32. package/dist/client/types/Tools.d.ts.map +1 -0
  33. package/dist/client/types/Tools.js +1 -0
  34. package/dist/client/types/Tools.js.map +1 -0
  35. package/dist/context/providers/AttachmentByIndexProvider.d.ts.map +1 -1
  36. package/dist/context/providers/AttachmentByIndexProvider.js +20 -24
  37. package/dist/context/providers/AttachmentByIndexProvider.js.map +1 -1
  38. package/dist/context/providers/MessageByIndexProvider.d.ts.map +1 -1
  39. package/dist/context/providers/MessageByIndexProvider.js +27 -29
  40. package/dist/context/providers/MessageByIndexProvider.js.map +1 -1
  41. package/dist/context/providers/MessageProvider.d.ts.map +1 -1
  42. package/dist/context/providers/MessageProvider.js +11 -13
  43. package/dist/context/providers/MessageProvider.js.map +1 -1
  44. package/dist/context/providers/PartByIndexProvider.d.ts.map +1 -1
  45. package/dist/context/providers/PartByIndexProvider.js +11 -13
  46. package/dist/context/providers/PartByIndexProvider.js.map +1 -1
  47. package/dist/context/providers/TextMessagePartProvider.d.ts.map +1 -1
  48. package/dist/context/providers/TextMessagePartProvider.js +11 -13
  49. package/dist/context/providers/TextMessagePartProvider.js.map +1 -1
  50. package/dist/context/providers/ThreadListItemProvider.d.ts.map +1 -1
  51. package/dist/context/providers/ThreadListItemProvider.js +41 -45
  52. package/dist/context/providers/ThreadListItemProvider.js.map +1 -1
  53. package/dist/context/react/AssistantApiContext.d.ts +33 -4
  54. package/dist/context/react/AssistantApiContext.d.ts.map +1 -1
  55. package/dist/context/react/AssistantApiContext.js +35 -17
  56. package/dist/context/react/AssistantApiContext.js.map +1 -1
  57. package/dist/context/react/index.d.ts +1 -1
  58. package/dist/context/react/index.d.ts.map +1 -1
  59. package/dist/context/react/index.js +5 -1
  60. package/dist/context/react/index.js.map +1 -1
  61. package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts.map +1 -1
  62. package/dist/legacy-runtime/AssistantRuntimeProvider.js +7 -9
  63. package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
  64. package/dist/legacy-runtime/RuntimeAdapter.d.ts +7 -0
  65. package/dist/legacy-runtime/RuntimeAdapter.d.ts.map +1 -0
  66. package/dist/legacy-runtime/RuntimeAdapter.js +20 -0
  67. package/dist/legacy-runtime/RuntimeAdapter.js.map +1 -0
  68. package/dist/legacy-runtime/client/ThreadListRuntimeClient.d.ts +2 -0
  69. package/dist/legacy-runtime/client/ThreadListRuntimeClient.d.ts.map +1 -1
  70. package/dist/legacy-runtime/client/ThreadListRuntimeClient.js +6 -2
  71. package/dist/legacy-runtime/client/ThreadListRuntimeClient.js.map +1 -1
  72. package/dist/legacy-runtime/hooks/AssistantContext.d.ts +14 -1
  73. package/dist/legacy-runtime/hooks/AssistantContext.d.ts.map +1 -1
  74. package/dist/legacy-runtime/hooks/AssistantContext.js +1 -1
  75. package/dist/legacy-runtime/hooks/AssistantContext.js.map +1 -1
  76. package/dist/legacy-runtime/hooks/AttachmentContext.d.ts +6 -0
  77. package/dist/legacy-runtime/hooks/AttachmentContext.d.ts.map +1 -1
  78. package/dist/legacy-runtime/hooks/AttachmentContext.js.map +1 -1
  79. package/dist/legacy-runtime/hooks/ComposerContext.d.ts +41 -2
  80. package/dist/legacy-runtime/hooks/ComposerContext.d.ts.map +1 -1
  81. package/dist/legacy-runtime/hooks/ComposerContext.js.map +1 -1
  82. package/dist/legacy-runtime/hooks/MessageContext.d.ts +36 -2
  83. package/dist/legacy-runtime/hooks/MessageContext.d.ts.map +1 -1
  84. package/dist/legacy-runtime/hooks/MessageContext.js.map +1 -1
  85. package/dist/legacy-runtime/hooks/MessagePartContext.d.ts +6 -0
  86. package/dist/legacy-runtime/hooks/MessagePartContext.d.ts.map +1 -1
  87. package/dist/legacy-runtime/hooks/MessagePartContext.js.map +1 -1
  88. package/dist/legacy-runtime/hooks/ThreadContext.d.ts +26 -1
  89. package/dist/legacy-runtime/hooks/ThreadContext.d.ts.map +1 -1
  90. package/dist/legacy-runtime/hooks/ThreadContext.js.map +1 -1
  91. package/dist/legacy-runtime/hooks/ThreadListItemContext.d.ts +6 -0
  92. package/dist/legacy-runtime/hooks/ThreadListItemContext.d.ts.map +1 -1
  93. package/dist/legacy-runtime/hooks/ThreadListItemContext.js.map +1 -1
  94. package/dist/legacy-runtime/runtime-cores/core/AssistantRuntimeCore.d.ts +1 -0
  95. package/dist/legacy-runtime/runtime-cores/core/AssistantRuntimeCore.d.ts.map +1 -1
  96. package/dist/legacy-runtime/runtime-cores/core/BaseAssistantRuntimeCore.d.ts +1 -0
  97. package/dist/legacy-runtime/runtime-cores/core/BaseAssistantRuntimeCore.d.ts.map +1 -1
  98. package/dist/legacy-runtime/runtime-cores/core/BaseAssistantRuntimeCore.js +3 -0
  99. package/dist/legacy-runtime/runtime-cores/core/BaseAssistantRuntimeCore.js.map +1 -1
  100. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadListRuntimeCore.d.ts +4 -1
  101. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadListRuntimeCore.d.ts.map +1 -1
  102. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadListRuntimeCore.js +2 -2
  103. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadListRuntimeCore.js.map +1 -1
  104. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.d.ts.map +1 -1
  105. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.js +7 -1
  106. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.js.map +1 -1
  107. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.d.ts +2 -12
  108. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.d.ts.map +1 -1
  109. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.js +45 -15
  110. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.js.map +1 -1
  111. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.d.ts +2 -0
  112. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.d.ts.map +1 -1
  113. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.js +4 -2
  114. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.js.map +1 -1
  115. package/dist/legacy-runtime/runtime-cores/local/LocalThreadListRuntimeCore.d.ts +4 -1
  116. package/dist/legacy-runtime/runtime-cores/local/LocalThreadListRuntimeCore.d.ts.map +1 -1
  117. package/dist/legacy-runtime/runtime-cores/local/LocalThreadListRuntimeCore.js +2 -2
  118. package/dist/legacy-runtime/runtime-cores/local/LocalThreadListRuntimeCore.js.map +1 -1
  119. package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.js +1 -1
  120. package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.js.map +1 -1
  121. package/dist/model-context/index.d.ts +2 -0
  122. package/dist/model-context/index.d.ts.map +1 -1
  123. package/dist/model-context/index.js +6 -0
  124. package/dist/model-context/index.js.map +1 -1
  125. package/dist/model-context/makeAssistantVisible.js +1 -1
  126. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  127. package/dist/model-context/toolbox.d.ts +28 -0
  128. package/dist/model-context/toolbox.d.ts.map +1 -0
  129. package/dist/model-context/toolbox.js +15 -0
  130. package/dist/model-context/toolbox.js.map +1 -0
  131. package/dist/model-context/useAssistantInstructions.js +1 -1
  132. package/dist/model-context/useAssistantInstructions.js.map +1 -1
  133. package/dist/model-context/useAssistantTool.js +1 -1
  134. package/dist/model-context/useAssistantTool.js.map +1 -1
  135. package/dist/primitives/index.d.ts +1 -0
  136. package/dist/primitives/index.d.ts.map +1 -1
  137. package/dist/primitives/index.js +2 -0
  138. package/dist/primitives/index.js.map +1 -1
  139. package/dist/primitives/message/MessageParts.d.ts +31 -1
  140. package/dist/primitives/message/MessageParts.d.ts.map +1 -1
  141. package/dist/primitives/message/MessageParts.js +68 -25
  142. package/dist/primitives/message/MessageParts.js.map +1 -1
  143. package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
  144. package/dist/primitives/message/MessagePartsGrouped.js +2 -2
  145. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  146. package/dist/primitives/reasoning/index.d.ts +2 -0
  147. package/dist/primitives/reasoning/index.d.ts.map +1 -0
  148. package/dist/primitives/reasoning/index.js +6 -0
  149. package/dist/primitives/reasoning/index.js.map +1 -0
  150. package/dist/primitives/reasoning/useScrollLock.d.ts +29 -0
  151. package/dist/primitives/reasoning/useScrollLock.d.ts.map +1 -0
  152. package/dist/primitives/reasoning/useScrollLock.js +50 -0
  153. package/dist/primitives/reasoning/useScrollLock.js.map +1 -0
  154. package/dist/tests/setup.js +7 -7
  155. package/dist/tests/setup.js.map +1 -1
  156. package/dist/types/MessagePartComponentTypes.d.ts +6 -1
  157. package/dist/types/MessagePartComponentTypes.d.ts.map +1 -1
  158. package/dist/types/index.d.ts +1 -1
  159. package/dist/types/index.d.ts.map +1 -1
  160. package/dist/utils/tap-store/derived-scopes.d.ts +81 -0
  161. package/dist/utils/tap-store/derived-scopes.d.ts.map +1 -0
  162. package/dist/utils/tap-store/derived-scopes.js +67 -0
  163. package/dist/utils/tap-store/derived-scopes.js.map +1 -0
  164. package/dist/utils/tap-store/index.d.ts +1 -0
  165. package/dist/utils/tap-store/index.d.ts.map +1 -1
  166. package/dist/utils/tap-store/index.js +4 -0
  167. package/dist/utils/tap-store/index.js.map +1 -1
  168. package/dist/utils/tap-store/tap-api.js +1 -1
  169. package/dist/utils/tap-store/tap-api.js.map +1 -1
  170. package/package.json +9 -9
  171. package/src/client/AssistantClient.ts +64 -38
  172. package/src/client/ModelContext.ts +28 -0
  173. package/src/client/ModelContextClient.ts +16 -0
  174. package/src/client/ToolUIClient.ts +50 -3
  175. package/src/client/ToolUIContext.ts +22 -0
  176. package/src/client/Tools.ts +68 -0
  177. package/src/client/types/ModelContext.ts +15 -0
  178. package/src/client/types/ThreadList.ts +4 -0
  179. package/src/client/types/ToolUI.ts +10 -1
  180. package/src/client/types/Tools.ts +11 -0
  181. package/src/context/providers/AttachmentByIndexProvider.tsx +21 -25
  182. package/src/context/providers/MessageByIndexProvider.tsx +31 -33
  183. package/src/context/providers/MessageProvider.tsx +12 -14
  184. package/src/context/providers/PartByIndexProvider.tsx +12 -14
  185. package/src/context/providers/TextMessagePartProvider.tsx +12 -14
  186. package/src/context/providers/ThreadListItemProvider.tsx +42 -46
  187. package/src/context/react/AssistantApiContext.tsx +69 -29
  188. package/src/context/react/index.ts +5 -1
  189. package/src/legacy-runtime/AssistantRuntimeProvider.tsx +7 -9
  190. package/src/legacy-runtime/RuntimeAdapter.ts +19 -0
  191. package/src/legacy-runtime/client/ThreadListRuntimeClient.ts +10 -1
  192. package/src/legacy-runtime/hooks/AssistantContext.ts +16 -2
  193. package/src/legacy-runtime/hooks/AttachmentContext.ts +6 -0
  194. package/src/legacy-runtime/hooks/ComposerContext.ts +41 -2
  195. package/src/legacy-runtime/hooks/MessageContext.ts +37 -2
  196. package/src/legacy-runtime/hooks/MessagePartContext.ts +6 -0
  197. package/src/legacy-runtime/hooks/ThreadContext.ts +27 -1
  198. package/src/legacy-runtime/hooks/ThreadListItemContext.ts +6 -0
  199. package/src/legacy-runtime/runtime-cores/core/AssistantRuntimeCore.tsx +1 -0
  200. package/src/legacy-runtime/runtime-cores/core/BaseAssistantRuntimeCore.tsx +4 -0
  201. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadListRuntimeCore.tsx +4 -2
  202. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.tsx +7 -1
  203. package/src/legacy-runtime/runtime-cores/external-store/auto-status.tsx +49 -16
  204. package/src/legacy-runtime/runtime-cores/external-store/external-message-converter.tsx +4 -0
  205. package/src/legacy-runtime/runtime-cores/local/LocalThreadListRuntimeCore.tsx +4 -2
  206. package/src/legacy-runtime/runtime-cores/utils/MessageRepository.tsx +1 -1
  207. package/src/model-context/index.ts +9 -0
  208. package/src/model-context/makeAssistantVisible.tsx +1 -1
  209. package/src/model-context/toolbox.tsx +37 -0
  210. package/src/model-context/useAssistantInstructions.tsx +1 -1
  211. package/src/model-context/useAssistantTool.tsx +1 -1
  212. package/src/primitives/index.ts +1 -0
  213. package/src/primitives/message/MessageParts.tsx +114 -32
  214. package/src/primitives/message/MessagePartsGrouped.tsx +3 -2
  215. package/src/primitives/reasoning/index.ts +1 -0
  216. package/src/primitives/reasoning/useScrollLock.tsx +86 -0
  217. package/src/types/MessagePartComponentTypes.tsx +7 -1
  218. package/src/types/index.ts +2 -0
  219. package/src/utils/tap-store/derived-scopes.ts +185 -0
  220. package/src/utils/tap-store/index.ts +9 -0
  221. package/src/utils/tap-store/tap-api.ts +1 -1
@@ -1,34 +1,67 @@
1
+ import { ReadonlyJSONValue } from "assistant-stream/utils";
1
2
  import { MessageStatus } from "../../../types";
2
3
 
3
- const AUTO_STATUS_RUNNING = Object.freeze({ type: "running" });
4
- const AUTO_STATUS_COMPLETE = Object.freeze({
5
- type: "complete",
6
- reason: "unknown",
7
- });
4
+ const symbolAutoStatus = Symbol("autoStatus");
8
5
 
9
- const AUTO_STATUS_PENDING = Object.freeze({
10
- type: "requires-action",
11
- reason: "tool-calls",
12
- });
6
+ const AUTO_STATUS_RUNNING = Object.freeze(
7
+ Object.assign({ type: "running" as const }, { [symbolAutoStatus]: true }),
8
+ );
9
+ const AUTO_STATUS_COMPLETE = Object.freeze(
10
+ Object.assign(
11
+ {
12
+ type: "complete" as const,
13
+ reason: "unknown" as const,
14
+ },
15
+ { [symbolAutoStatus]: true },
16
+ ),
17
+ );
13
18
 
14
- const AUTO_STATUS_INTERRUPT = Object.freeze({
15
- type: "requires-action",
16
- reason: "interrupt",
17
- });
19
+ const AUTO_STATUS_PENDING = Object.freeze(
20
+ Object.assign(
21
+ {
22
+ type: "requires-action" as const,
23
+ reason: "tool-calls" as const,
24
+ },
25
+ { [symbolAutoStatus]: true },
26
+ ),
27
+ );
28
+
29
+ const AUTO_STATUS_INTERRUPT = Object.freeze(
30
+ Object.assign(
31
+ {
32
+ type: "requires-action" as const,
33
+ reason: "interrupt" as const,
34
+ },
35
+ { [symbolAutoStatus]: true },
36
+ ),
37
+ );
18
38
 
19
39
  export const isAutoStatus = (status: MessageStatus) =>
20
- status === AUTO_STATUS_RUNNING || status === AUTO_STATUS_COMPLETE;
40
+ (status as any)[symbolAutoStatus] === true;
21
41
 
22
42
  export const getAutoStatus = (
23
43
  isLast: boolean,
24
44
  isRunning: boolean,
25
45
  hasInterruptedToolCalls: boolean,
26
46
  hasPendingToolCalls: boolean,
27
- ) =>
28
- isLast && isRunning
47
+ error?: ReadonlyJSONValue,
48
+ ): MessageStatus => {
49
+ if (isLast && error) {
50
+ return Object.assign(
51
+ {
52
+ type: "incomplete" as const,
53
+ reason: "error" as const,
54
+ error: error,
55
+ },
56
+ { [symbolAutoStatus]: true },
57
+ );
58
+ }
59
+
60
+ return isLast && isRunning
29
61
  ? AUTO_STATUS_RUNNING
30
62
  : hasInterruptedToolCalls
31
63
  ? AUTO_STATUS_INTERRUPT
32
64
  : hasPendingToolCalls
33
65
  ? AUTO_STATUS_PENDING
34
66
  : AUTO_STATUS_COMPLETE;
67
+ };
@@ -10,6 +10,7 @@ import { fromThreadMessageLike, ThreadMessageLike } from "./ThreadMessageLike";
10
10
  import { getAutoStatus, isAutoStatus } from "./auto-status";
11
11
  import { ThreadMessage, ToolCallMessagePart } from "../../../types";
12
12
  import { ToolExecutionStatus } from "../assistant-transport/useToolInvocations";
13
+ import { ReadonlyJSONValue } from "assistant-stream/utils";
13
14
 
14
15
  export namespace useExternalMessageConverter {
15
16
  export type Message =
@@ -30,6 +31,7 @@ export namespace useExternalMessageConverter {
30
31
 
31
32
  export type Metadata = {
32
33
  readonly toolStatuses?: Record<string, ToolExecutionStatus>;
34
+ readonly error?: ReadonlyJSONValue;
33
35
  };
34
36
 
35
37
  export type Callback<T> = (
@@ -263,6 +265,7 @@ export const convertExternalMessages = <T extends WeakKey>(
263
265
  isRunning,
264
266
  hasSuspendedToolCalls,
265
267
  hasPendingToolCalls,
268
+ isLast ? metadata.error : undefined,
266
269
  );
267
270
  const newMessage = fromThreadMessageLike(
268
271
  joined,
@@ -348,6 +351,7 @@ export const useExternalMessageConverter = <T extends WeakKey>({
348
351
  isRunning,
349
352
  hasSuspendedToolCalls,
350
353
  hasPendingToolCalls,
354
+ isLast ? state.metadata.error : undefined,
351
355
  );
352
356
 
353
357
  if (
@@ -104,8 +104,10 @@ export class LocalThreadListRuntimeCore
104
104
  throw new Error("Method not implemented.");
105
105
  }
106
106
 
107
- public initialize(): never {
108
- throw new Error("Method not implemented.");
107
+ public initialize(
108
+ threadId: string,
109
+ ): Promise<{ remoteId: string; externalId: string | undefined }> {
110
+ return Promise.resolve({ remoteId: threadId, externalId: undefined });
109
111
  }
110
112
 
111
113
  public generateTitle(): never {
@@ -67,7 +67,7 @@ export const ExportedMessageRepository = {
67
67
  fromThreadMessageLike(
68
68
  m,
69
69
  generateId(),
70
- getAutoStatus(false, false, false, false),
70
+ getAutoStatus(false, false, false, false, undefined),
71
71
  ),
72
72
  );
73
73
 
@@ -19,5 +19,14 @@ export { tool } from "./tool";
19
19
 
20
20
  export { makeAssistantVisible } from "./makeAssistantVisible";
21
21
 
22
+ export {
23
+ Toolkit,
24
+ type ToolDefinition,
25
+ type ToolkitFallback,
26
+ type ToolkitLayout,
27
+ } from "./toolbox";
28
+
29
+ export { Tools } from "../client/Tools";
30
+
22
31
  export * from "./registry";
23
32
  export * from "./frame";
@@ -76,7 +76,7 @@ export const makeAssistantVisible = <T extends ComponentType<any>>(
76
76
 
77
77
  const { clickable, editable } = config ?? {};
78
78
  useEffect(() => {
79
- return api.registerModelContextProvider({
79
+ return api.modelContext().register({
80
80
  getModelContext: () => {
81
81
  return {
82
82
  tools: {
@@ -0,0 +1,37 @@
1
+ "use client";
2
+
3
+ import type { ComponentType, ReactNode } from "react";
4
+ import type { Tool } from "assistant-stream";
5
+ import type { ToolCallMessagePartComponent } from "../types/MessagePartComponentTypes";
6
+
7
+ export type ToolDefinition<
8
+ TArgs extends Record<string, unknown>,
9
+ TResult,
10
+ > = Tool<TArgs, TResult> & {
11
+ render?: ToolCallMessagePartComponent<TArgs, TResult> | undefined;
12
+ };
13
+
14
+ export const FallbackSymbol = Symbol("Toolkit.Fallback");
15
+ export const LayoutSymbol = Symbol("Toolkit.Layout");
16
+
17
+ export type ToolkitFallback = {
18
+ render: ToolCallMessagePartComponent<unknown, unknown>;
19
+ };
20
+
21
+ export type ToolkitLayout = {
22
+ render: ComponentType<{ children: ReactNode }>;
23
+ };
24
+
25
+ export type Toolkit = Record<string, ToolDefinition<any, any>> & {
26
+ [FallbackSymbol]?: ToolkitFallback;
27
+ [LayoutSymbol]?: ToolkitLayout;
28
+ };
29
+
30
+ export const Toolkit = {
31
+ Fallback: FallbackSymbol,
32
+ Layout: LayoutSymbol,
33
+ } as const;
34
+
35
+ export type ToolsConfig = {
36
+ toolkit: Toolkit;
37
+ };
@@ -27,7 +27,7 @@ export const useAssistantInstructions = (
27
27
  const config = {
28
28
  system: instruction,
29
29
  };
30
- return api.registerModelContextProvider({
30
+ return api.modelContext().register({
31
31
  getModelContext: () => config,
32
32
  });
33
33
  }, [api, instruction, disabled]);
@@ -33,7 +33,7 @@ export const useAssistantTool = <
33
33
  [toolName]: rest,
34
34
  },
35
35
  };
36
- return api.registerModelContextProvider({
36
+ return api.modelContext().register({
37
37
  getModelContext: () => context,
38
38
  });
39
39
  }, [api, tool]);
@@ -16,3 +16,4 @@ export { useMessagePartSource } from "./messagePart/useMessagePartSource";
16
16
  export { useMessagePartFile } from "./messagePart/useMessagePartFile";
17
17
  export { useMessagePartImage } from "./messagePart/useMessagePartImage";
18
18
  export { useThreadViewportAutoScroll } from "./thread/useThreadViewportAutoScroll";
19
+ export { useScrollLock } from "./reasoning";
@@ -25,6 +25,7 @@ import type {
25
25
  ToolCallMessagePartProps,
26
26
  FileMessagePartComponent,
27
27
  ReasoningMessagePartComponent,
28
+ ReasoningGroupComponent,
28
29
  } from "../../types/MessagePartComponentTypes";
29
30
  import { MessagePartPrimitiveInProgress } from "../messagePart/MessagePartInProgress";
30
31
  import { MessagePartStatus } from "../../types/AssistantTypes";
@@ -32,50 +33,75 @@ import { useShallow } from "zustand/shallow";
32
33
 
33
34
  type MessagePartRange =
34
35
  | { type: "single"; index: number }
35
- | { type: "toolGroup"; startIndex: number; endIndex: number };
36
+ | { type: "toolGroup"; startIndex: number; endIndex: number }
37
+ | { type: "reasoningGroup"; startIndex: number; endIndex: number };
36
38
 
37
39
  /**
38
- * Groups consecutive tool-call message parts into ranges.
39
- * Always groups tool calls, even if there's only one.
40
+ * Creates a group state manager for a specific part type.
41
+ * Returns functions to start, end, and finalize groups.
42
+ */
43
+ const createGroupState = <T extends "toolGroup" | "reasoningGroup">(
44
+ groupType: T,
45
+ ) => {
46
+ let start = -1;
47
+
48
+ return {
49
+ startGroup: (index: number) => {
50
+ if (start === -1) {
51
+ start = index;
52
+ }
53
+ },
54
+ endGroup: (endIndex: number, ranges: MessagePartRange[]) => {
55
+ if (start !== -1) {
56
+ ranges.push({
57
+ type: groupType,
58
+ startIndex: start,
59
+ endIndex,
60
+ } as MessagePartRange);
61
+ start = -1;
62
+ }
63
+ },
64
+ finalize: (endIndex: number, ranges: MessagePartRange[]) => {
65
+ if (start !== -1) {
66
+ ranges.push({
67
+ type: groupType,
68
+ startIndex: start,
69
+ endIndex,
70
+ } as MessagePartRange);
71
+ }
72
+ },
73
+ };
74
+ };
75
+
76
+ /**
77
+ * Groups consecutive tool-call and reasoning message parts into ranges.
78
+ * Always groups tool calls and reasoning parts, even if there's only one.
40
79
  */
41
80
  const groupMessageParts = (
42
81
  messageTypes: readonly string[],
43
82
  ): MessagePartRange[] => {
44
83
  const ranges: MessagePartRange[] = [];
45
- let currentToolGroupStart = -1;
84
+ const toolGroup = createGroupState("toolGroup");
85
+ const reasoningGroup = createGroupState("reasoningGroup");
46
86
 
47
87
  for (let i = 0; i < messageTypes.length; i++) {
48
88
  const type = messageTypes[i];
49
89
 
50
90
  if (type === "tool-call") {
51
- // Start a new tool group if we haven't started one
52
- if (currentToolGroupStart === -1) {
53
- currentToolGroupStart = i;
54
- }
91
+ reasoningGroup.endGroup(i - 1, ranges);
92
+ toolGroup.startGroup(i);
93
+ } else if (type === "reasoning") {
94
+ toolGroup.endGroup(i - 1, ranges);
95
+ reasoningGroup.startGroup(i);
55
96
  } else {
56
- // End current tool group if it exists
57
- if (currentToolGroupStart !== -1) {
58
- ranges.push({
59
- type: "toolGroup",
60
- startIndex: currentToolGroupStart,
61
- endIndex: i - 1,
62
- });
63
- currentToolGroupStart = -1;
64
- }
65
-
66
- // Add non-tool-call part individually
97
+ toolGroup.endGroup(i - 1, ranges);
98
+ reasoningGroup.endGroup(i - 1, ranges);
67
99
  ranges.push({ type: "single", index: i });
68
100
  }
69
101
  }
70
102
 
71
- // Handle any remaining tool group at the end
72
- if (currentToolGroupStart !== -1) {
73
- ranges.push({
74
- type: "toolGroup",
75
- startIndex: currentToolGroupStart,
76
- endIndex: messageTypes.length - 1,
77
- });
78
- }
103
+ toolGroup.finalize(messageTypes.length - 1, ranges);
104
+ reasoningGroup.finalize(messageTypes.length - 1, ranges);
79
105
 
80
106
  return ranges;
81
107
  };
@@ -184,6 +210,37 @@ export namespace MessagePrimitiveParts {
184
210
  ToolGroup?: ComponentType<
185
211
  PropsWithChildren<{ startIndex: number; endIndex: number }>
186
212
  >;
213
+
214
+ /**
215
+ * Component for rendering grouped reasoning parts.
216
+ *
217
+ * When provided, this component will automatically wrap reasoning message parts
218
+ * (one or more consecutive) in a group container. Each reasoning part inside
219
+ * renders its own text independently - no text merging occurs.
220
+ *
221
+ * The component receives:
222
+ * - `startIndex`: The index of the first reasoning part in the group
223
+ * - `endIndex`: The index of the last reasoning part in the group
224
+ * - `children`: The rendered Reasoning components (one per part)
225
+ *
226
+ * @example
227
+ * ```tsx
228
+ * // Collapsible reasoning group
229
+ * ReasoningGroup: ({ children }) => (
230
+ * <details className="reasoning-group">
231
+ * <summary>Reasoning</summary>
232
+ * <div className="reasoning-content">
233
+ * {children}
234
+ * </div>
235
+ * </details>
236
+ * )
237
+ * ```
238
+ *
239
+ * @param startIndex - Index of the first reasoning part in the group
240
+ * @param endIndex - Index of the last reasoning part in the group
241
+ * @param children - Rendered reasoning part components
242
+ */
243
+ ReasoningGroup?: ReasoningGroupComponent;
187
244
  }
188
245
  | undefined;
189
246
  };
@@ -196,8 +253,9 @@ const ToolUIDisplay = ({
196
253
  Fallback: ToolCallMessagePartComponent | undefined;
197
254
  } & ToolCallMessagePartProps) => {
198
255
  const Render = useAssistantState(({ toolUIs }) => {
199
- const Render = toolUIs[props.toolName] ?? Fallback;
200
- if (Array.isArray(Render)) return Render[0];
256
+ const Render =
257
+ toolUIs.tools[props.toolName] ?? toolUIs.fallback ?? Fallback;
258
+ if (Array.isArray(Render)) return Render[0] ?? Fallback;
201
259
  return Render;
202
260
  });
203
261
  if (!Render) return null;
@@ -219,6 +277,7 @@ const defaultComponents = {
219
277
  File: () => null,
220
278
  Unstable_Audio: () => null,
221
279
  ToolGroup: ({ children }) => children,
280
+ ReasoningGroup: ({ children }) => children,
222
281
  } satisfies MessagePrimitiveParts.Props["components"];
223
282
 
224
283
  type MessagePartComponentProps = {
@@ -327,7 +386,8 @@ export const MessagePrimitivePartByIndex: FC<MessagePrimitivePartByIndex.Props>
327
386
  prev.components?.File === next.components?.File &&
328
387
  prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&
329
388
  prev.components?.tools === next.components?.tools &&
330
- prev.components?.ToolGroup === next.components?.ToolGroup,
389
+ prev.components?.ToolGroup === next.components?.ToolGroup &&
390
+ prev.components?.ReasoningGroup === next.components?.ReasoningGroup,
331
391
  );
332
392
 
333
393
  MessagePrimitivePartByIndex.displayName = "MessagePrimitive.PartByIndex";
@@ -415,12 +475,12 @@ export const MessagePrimitiveParts: FC<MessagePrimitiveParts.Props> = ({
415
475
  components={components}
416
476
  />
417
477
  );
418
- } else {
478
+ } else if (range.type === "toolGroup") {
419
479
  const ToolGroupComponent =
420
480
  components!.ToolGroup ?? defaultComponents.ToolGroup;
421
481
  return (
422
482
  <ToolGroupComponent
423
- key={range.startIndex}
483
+ key={`tool-${range.startIndex}`}
424
484
  startIndex={range.startIndex}
425
485
  endIndex={range.endIndex}
426
486
  >
@@ -436,6 +496,28 @@ export const MessagePrimitiveParts: FC<MessagePrimitiveParts.Props> = ({
436
496
  )}
437
497
  </ToolGroupComponent>
438
498
  );
499
+ } else {
500
+ // reasoningGroup
501
+ const ReasoningGroupComponent =
502
+ components!.ReasoningGroup ?? defaultComponents.ReasoningGroup;
503
+ return (
504
+ <ReasoningGroupComponent
505
+ key={`reasoning-${range.startIndex}`}
506
+ startIndex={range.startIndex}
507
+ endIndex={range.endIndex}
508
+ >
509
+ {Array.from(
510
+ { length: range.endIndex - range.startIndex + 1 },
511
+ (_, i) => (
512
+ <MessagePrimitivePartByIndex
513
+ key={i}
514
+ index={range.startIndex + i}
515
+ components={components}
516
+ />
517
+ ),
518
+ )}
519
+ </ReasoningGroupComponent>
520
+ );
439
521
  }
440
522
  });
441
523
  }, [messageRanges, components, contentLength]);
@@ -221,8 +221,9 @@ const ToolUIDisplay = ({
221
221
  Fallback: ToolCallMessagePartComponent | undefined;
222
222
  } & ToolCallMessagePartProps) => {
223
223
  const Render = useAssistantState(({ toolUIs }) => {
224
- const Render = toolUIs[props.toolName] ?? Fallback;
225
- if (Array.isArray(Render)) return Render[0];
224
+ const Render =
225
+ toolUIs.tools[props.toolName] ?? toolUIs.fallback ?? Fallback;
226
+ if (Array.isArray(Render)) return Render[0] ?? Fallback;
226
227
  return Render;
227
228
  });
228
229
  if (!Render) return null;
@@ -0,0 +1 @@
1
+ export { useScrollLock } from "./useScrollLock";
@@ -0,0 +1,86 @@
1
+ "use client";
2
+
3
+ import { type RefObject, useCallback, useEffect, useRef } from "react";
4
+
5
+ /**
6
+ * Locks scroll position during collapsible/height animations and hides scrollbar.
7
+ *
8
+ * This utility prevents page jumps when content height changes during animations,
9
+ * providing a smooth user experience. It finds the nearest scrollable ancestor and
10
+ * temporarily locks its scroll position while the animation completes.
11
+ *
12
+ * - Prevents forced reflows: no layout reads, mutations scoped to scrollable parent only
13
+ * - Reactive: only intercepts scroll events when browser actually adjusts
14
+ * - Cleans up automatically after animation duration
15
+ *
16
+ * @param animatedElementRef - Ref to the animated element
17
+ * @param animationDuration - Lock duration in milliseconds
18
+ * @returns Function to activate the scroll lock
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * const collapsibleRef = useRef<HTMLDivElement>(null);
23
+ * const lockScroll = useScrollLock(collapsibleRef, 200);
24
+ *
25
+ * const handleCollapse = () => {
26
+ * lockScroll(); // Lock scroll before collapsing
27
+ * setIsOpen(false);
28
+ * };
29
+ * ```
30
+ */
31
+ export const useScrollLock = <T extends HTMLElement = HTMLElement>(
32
+ animatedElementRef: RefObject<T | null>,
33
+ animationDuration: number,
34
+ ) => {
35
+ const scrollContainerRef = useRef<HTMLElement | null>(null);
36
+ const cleanupRef = useRef<(() => void) | null>(null);
37
+
38
+ useEffect(() => {
39
+ return () => {
40
+ cleanupRef.current?.();
41
+ };
42
+ }, []);
43
+
44
+ const lockScroll = useCallback(() => {
45
+ cleanupRef.current?.();
46
+
47
+ (function findScrollableAncestor() {
48
+ if (scrollContainerRef.current || !animatedElementRef.current) return;
49
+
50
+ let el: HTMLElement | null = animatedElementRef.current;
51
+ while (el) {
52
+ const { overflowY } = getComputedStyle(el);
53
+ if (overflowY === "scroll" || overflowY === "auto") {
54
+ scrollContainerRef.current = el;
55
+ break;
56
+ }
57
+ el = el.parentElement;
58
+ }
59
+ })();
60
+
61
+ const scrollContainer = scrollContainerRef.current;
62
+ if (!scrollContainer) return;
63
+
64
+ const scrollPosition = scrollContainer.scrollTop;
65
+ const scrollbarWidth = scrollContainer.style.scrollbarWidth;
66
+
67
+ scrollContainer.style.scrollbarWidth = "none";
68
+
69
+ const resetPosition = () => (scrollContainer.scrollTop = scrollPosition);
70
+ scrollContainer.addEventListener("scroll", resetPosition);
71
+
72
+ const timeoutId = setTimeout(() => {
73
+ scrollContainer.removeEventListener("scroll", resetPosition);
74
+ scrollContainer.style.scrollbarWidth = scrollbarWidth;
75
+ cleanupRef.current = null;
76
+ }, animationDuration);
77
+
78
+ cleanupRef.current = () => {
79
+ clearTimeout(timeoutId);
80
+ scrollContainer.removeEventListener("scroll", resetPosition);
81
+ scrollContainer.style.scrollbarWidth = scrollbarWidth;
82
+ };
83
+ }, [animationDuration, animatedElementRef]);
84
+
85
+ return lockScroll;
86
+ };
@@ -1,4 +1,4 @@
1
- import type { ComponentType } from "react";
1
+ import type { ComponentType, PropsWithChildren } from "react";
2
2
  import type {
3
3
  MessagePartStatus,
4
4
  FileMessagePart,
@@ -24,6 +24,12 @@ export type ReasoningMessagePartProps = MessagePartState & ReasoningMessagePart;
24
24
  export type ReasoningMessagePartComponent =
25
25
  ComponentType<ReasoningMessagePartProps>;
26
26
 
27
+ export type ReasoningGroupProps = PropsWithChildren<{
28
+ startIndex: number;
29
+ endIndex: number;
30
+ }>;
31
+ export type ReasoningGroupComponent = ComponentType<ReasoningGroupProps>;
32
+
27
33
  export type SourceMessagePartProps = MessagePartState & SourceMessagePart;
28
34
  export type SourceMessagePartComponent = ComponentType<SourceMessagePartProps>;
29
35
 
@@ -44,6 +44,8 @@ export type {
44
44
  Unstable_AudioMessagePartProps,
45
45
  ToolCallMessagePartComponent,
46
46
  ToolCallMessagePartProps,
47
+ ReasoningGroupProps,
48
+ ReasoningGroupComponent,
47
49
  } from "./MessagePartComponentTypes";
48
50
 
49
51
  // Thread list item types