@ulearning/ai-sdk-adapter 0.0.0-stage → 1.0.0-next.0

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 (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +29 -2
  3. package/dist/adapters/aiSDKFormatAdapter.d.ts +6 -0
  4. package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
  5. package/dist/adapters/aiSDKFormatAdapter.js +17 -0
  6. package/dist/adapters/aiSDKFormatAdapter.js.map +1 -0
  7. package/dist/adapters/vercelAttachmentAdapter.d.ts +3 -0
  8. package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -0
  9. package/dist/adapters/vercelAttachmentAdapter.js +36 -0
  10. package/dist/adapters/vercelAttachmentAdapter.js.map +1 -0
  11. package/dist/aiSDKExtras.d.ts +13 -0
  12. package/dist/aiSDKExtras.d.ts.map +1 -0
  13. package/dist/aiSDKExtras.js +6 -0
  14. package/dist/aiSDKExtras.js.map +1 -0
  15. package/dist/converters/convertMessage.d.ts +44 -0
  16. package/dist/converters/convertMessage.d.ts.map +1 -0
  17. package/dist/converters/convertMessage.js +465 -0
  18. package/dist/converters/convertMessage.js.map +1 -0
  19. package/dist/converters/toCreateMessage.d.ts +4 -0
  20. package/dist/converters/toCreateMessage.d.ts.map +1 -0
  21. package/dist/converters/toCreateMessage.js +65 -0
  22. package/dist/converters/toCreateMessage.js.map +1 -0
  23. package/dist/converters/toolApprovalAnswers.d.ts +3 -0
  24. package/dist/converters/toolApprovalAnswers.d.ts.map +1 -0
  25. package/dist/converters/toolApprovalAnswers.js +16 -0
  26. package/dist/converters/toolApprovalAnswers.js.map +1 -0
  27. package/dist/hooks.d.ts +17 -0
  28. package/dist/hooks.d.ts.map +1 -0
  29. package/dist/hooks.js +19 -0
  30. package/dist/hooks.js.map +1 -0
  31. package/dist/index.d.ts +17 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +13 -0
  34. package/dist/model-context/injectInteractableContext.d.ts +37 -0
  35. package/dist/model-context/injectInteractableContext.d.ts.map +1 -0
  36. package/dist/model-context/injectInteractableContext.js +54 -0
  37. package/dist/model-context/injectInteractableContext.js.map +1 -0
  38. package/dist/model-context/injectQuoteContext.d.ts +25 -0
  39. package/dist/model-context/injectQuoteContext.d.ts.map +1 -0
  40. package/dist/model-context/injectQuoteContext.js +51 -0
  41. package/dist/model-context/injectQuoteContext.js.map +1 -0
  42. package/dist/runtime/AISDKChat.d.ts +13 -0
  43. package/dist/runtime/AISDKChat.d.ts.map +1 -0
  44. package/dist/runtime/AISDKChat.js +34 -0
  45. package/dist/runtime/AISDKChat.js.map +1 -0
  46. package/dist/runtime/AISDKThreads.d.ts +24 -0
  47. package/dist/runtime/AISDKThreads.d.ts.map +1 -0
  48. package/dist/runtime/AISDKThreads.js +92 -0
  49. package/dist/runtime/AISDKThreads.js.map +1 -0
  50. package/dist/runtime/DynamicChatTransport.d.ts +50 -0
  51. package/dist/runtime/DynamicChatTransport.d.ts.map +1 -0
  52. package/dist/runtime/DynamicChatTransport.js +153 -0
  53. package/dist/runtime/DynamicChatTransport.js.map +1 -0
  54. package/dist/runtime/getResumableAdapter.d.ts +5 -0
  55. package/dist/runtime/getResumableAdapter.d.ts.map +1 -0
  56. package/dist/runtime/getResumableAdapter.js +11 -0
  57. package/dist/runtime/getResumableAdapter.js.map +1 -0
  58. package/dist/runtime/toolHistoryCodec.d.ts +20 -0
  59. package/dist/runtime/toolHistoryCodec.d.ts.map +1 -0
  60. package/dist/runtime/toolHistoryCodec.js +122 -0
  61. package/dist/runtime/toolHistoryCodec.js.map +1 -0
  62. package/dist/runtime/useAISDKRuntime.d.ts +95 -0
  63. package/dist/runtime/useAISDKRuntime.d.ts.map +1 -0
  64. package/dist/runtime/useAISDKRuntime.js +754 -0
  65. package/dist/runtime/useAISDKRuntime.js.map +1 -0
  66. package/dist/runtime/useChatRuntime.d.ts +17 -0
  67. package/dist/runtime/useChatRuntime.d.ts.map +1 -0
  68. package/dist/runtime/useChatRuntime.js +50 -0
  69. package/dist/runtime/useChatRuntime.js.map +1 -0
  70. package/dist/runtime/useChatSnapshot.d.ts +6 -0
  71. package/dist/runtime/useChatSnapshot.d.ts.map +1 -0
  72. package/dist/runtime/useChatSnapshot.js +62 -0
  73. package/dist/runtime/useChatSnapshot.js.map +1 -0
  74. package/dist/runtime/useChatThread.d.ts +121 -0
  75. package/dist/runtime/useChatThread.d.ts.map +1 -0
  76. package/dist/runtime/useChatThread.js +255 -0
  77. package/dist/runtime/useChatThread.js.map +1 -0
  78. package/dist/runtime/useDynamicChatTransport.d.ts +4 -0
  79. package/dist/runtime/useDynamicChatTransport.d.ts.map +1 -0
  80. package/dist/runtime/useDynamicChatTransport.js +26 -0
  81. package/dist/runtime/useDynamicChatTransport.js.map +1 -0
  82. package/dist/runtime/useExternalHistory.d.ts +12 -0
  83. package/dist/runtime/useExternalHistory.d.ts.map +1 -0
  84. package/dist/runtime/useExternalHistory.js +556 -0
  85. package/dist/runtime/useExternalHistory.js.map +1 -0
  86. package/dist/runtime/useMessageQueue.d.ts +14 -0
  87. package/dist/runtime/useMessageQueue.d.ts.map +1 -0
  88. package/dist/runtime/useMessageQueue.js +142 -0
  89. package/dist/runtime/useMessageQueue.js.map +1 -0
  90. package/dist/runtime/useResourceCleanup.d.ts +2 -0
  91. package/dist/runtime/useResourceCleanup.d.ts.map +1 -0
  92. package/dist/runtime/useResourceCleanup.js +47 -0
  93. package/dist/runtime/useResourceCleanup.js.map +1 -0
  94. package/dist/runtime/useStreamingTiming.d.ts +12 -0
  95. package/dist/runtime/useStreamingTiming.d.ts.map +1 -0
  96. package/dist/runtime/useStreamingTiming.js +36 -0
  97. package/dist/runtime/useStreamingTiming.js.map +1 -0
  98. package/dist/tools/generativeTools.d.ts +71 -0
  99. package/dist/tools/generativeTools.d.ts.map +1 -0
  100. package/dist/tools/generativeTools.js +258 -0
  101. package/dist/tools/generativeTools.js.map +1 -0
  102. package/dist/tools/mcp-stdio.node.d.ts +2 -0
  103. package/dist/tools/mcp-stdio.node.d.ts.map +1 -0
  104. package/dist/tools/mcp-stdio.node.js +1 -0
  105. package/dist/tools/mcp-stdio.unsupported.d.ts +3 -0
  106. package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -0
  107. package/dist/tools/mcp-stdio.unsupported.js +10 -0
  108. package/dist/tools/mcp-stdio.unsupported.js.map +1 -0
  109. package/dist/transport/AssistantChatTransport.d.ts +20 -0
  110. package/dist/transport/AssistantChatTransport.d.ts.map +1 -0
  111. package/dist/transport/AssistantChatTransport.js +151 -0
  112. package/dist/transport/AssistantChatTransport.js.map +1 -0
  113. package/dist/transport/resumable.d.ts +36 -0
  114. package/dist/transport/resumable.d.ts.map +1 -0
  115. package/dist/transport/resumable.js +112 -0
  116. package/dist/transport/resumable.js.map +1 -0
  117. package/dist/usage.d.ts +24 -0
  118. package/dist/usage.d.ts.map +1 -0
  119. package/dist/usage.js +121 -0
  120. package/dist/usage.js.map +1 -0
  121. package/dist/utils/getVercelAIMessages.d.ts +4 -0
  122. package/dist/utils/getVercelAIMessages.d.ts.map +1 -0
  123. package/dist/utils/getVercelAIMessages.js +8 -0
  124. package/dist/utils/getVercelAIMessages.js.map +1 -0
  125. package/dist/utils/sliceMessagesUntil.d.ts +3 -0
  126. package/dist/utils/sliceMessagesUntil.d.ts.map +1 -0
  127. package/dist/utils/sliceMessagesUntil.js +10 -0
  128. package/dist/utils/sliceMessagesUntil.js.map +1 -0
  129. package/package.json +96 -3
  130. package/src/__tests__/standalone.e2e.ts +118 -0
  131. package/src/adapters/aiSDKFormatAdapter.test.ts +36 -0
  132. package/src/adapters/aiSDKFormatAdapter.ts +18 -0
  133. package/src/adapters/vercelAttachmentAdapter.test.ts +52 -0
  134. package/src/adapters/vercelAttachmentAdapter.ts +36 -0
  135. package/src/aiSDKExtras.ts +31 -0
  136. package/src/converters/convertMessage.metadata-key.test.tsx +182 -0
  137. package/src/converters/convertMessage.test.ts +2428 -0
  138. package/src/converters/convertMessage.tool-args-status.test.tsx +185 -0
  139. package/src/converters/convertMessage.ts +919 -0
  140. package/src/converters/toCreateMessage.test.ts +716 -0
  141. package/src/converters/toCreateMessage.ts +110 -0
  142. package/src/converters/toolApprovalAnswers.ts +27 -0
  143. package/src/hooks.test.ts +168 -0
  144. package/src/hooks.ts +26 -0
  145. package/src/index.ts +29 -0
  146. package/src/model-context/injectInteractableContext.test.ts +167 -0
  147. package/src/model-context/injectInteractableContext.ts +64 -0
  148. package/src/model-context/injectQuoteContext.test.ts +33 -0
  149. package/src/model-context/injectQuoteContext.ts +63 -0
  150. package/src/runtime/AISDKChat.artifacts.test.ts +48 -0
  151. package/src/runtime/AISDKChat.integration.test.tsx +148 -0
  152. package/src/runtime/AISDKChat.test.ts +241 -0
  153. package/src/runtime/AISDKChat.ts +43 -0
  154. package/src/runtime/AISDKThreads.test.ts +811 -0
  155. package/src/runtime/AISDKThreads.ts +194 -0
  156. package/src/runtime/DynamicChatTransport.test.ts +228 -0
  157. package/src/runtime/DynamicChatTransport.ts +281 -0
  158. package/src/runtime/__tests__/controlled-transport.ts +70 -0
  159. package/src/runtime/__tests__/tap.ts +80 -0
  160. package/src/runtime/__tests__/vue.ts +46 -0
  161. package/src/runtime/getResumableAdapter.ts +16 -0
  162. package/src/runtime/toolHistoryCodec.test.ts +187 -0
  163. package/src/runtime/toolHistoryCodec.ts +245 -0
  164. package/src/runtime/useAISDKRuntime.approval.integration.test.tsx +1956 -0
  165. package/src/runtime/useAISDKRuntime.approval.test.tsx +580 -0
  166. package/src/runtime/useAISDKRuntime.denied-tool.test.tsx +122 -0
  167. package/src/runtime/useAISDKRuntime.fast-refresh.test.tsx +302 -0
  168. package/src/runtime/useAISDKRuntime.metadata-cache.test.tsx +157 -0
  169. package/src/runtime/useAISDKRuntime.queue.test.tsx +707 -0
  170. package/src/runtime/useAISDKRuntime.reload.test.tsx +217 -0
  171. package/src/runtime/useAISDKRuntime.test.ts +2345 -0
  172. package/src/runtime/useAISDKRuntime.tool-output.test.tsx +113 -0
  173. package/src/runtime/useAISDKRuntime.ts +1503 -0
  174. package/src/runtime/useAISDKRuntime.voice.test.tsx +319 -0
  175. package/src/runtime/useChatOnTap.test.ts +60 -0
  176. package/src/runtime/useChatRuntime.fast-refresh.test.tsx +83 -0
  177. package/src/runtime/useChatRuntime.integration.test.tsx +661 -0
  178. package/src/runtime/useChatRuntime.local-storage.test.tsx +114 -0
  179. package/src/runtime/useChatRuntime.native.test.ts +147 -0
  180. package/src/runtime/useChatRuntime.run-telemetry.test.tsx +226 -0
  181. package/src/runtime/useChatRuntime.test.ts +619 -0
  182. package/src/runtime/useChatRuntime.ts +80 -0
  183. package/src/runtime/useChatSnapshot.test.ts +60 -0
  184. package/src/runtime/useChatSnapshot.ts +86 -0
  185. package/src/runtime/useChatThread.binding.test.tsx +140 -0
  186. package/src/runtime/useChatThread.test.ts +993 -0
  187. package/src/runtime/useChatThread.transport.test.tsx +149 -0
  188. package/src/runtime/useChatThread.ts +533 -0
  189. package/src/runtime/useDynamicChatTransport.ts +26 -0
  190. package/src/runtime/useExternalHistory.test.ts +4187 -0
  191. package/src/runtime/useExternalHistory.ts +902 -0
  192. package/src/runtime/useMessageQueue.effects.test.tsx +88 -0
  193. package/src/runtime/useMessageQueue.test.tsx +256 -0
  194. package/src/runtime/useMessageQueue.ts +189 -0
  195. package/src/runtime/useResourceCleanup.ts +58 -0
  196. package/src/runtime/useStreamingTiming.test.tsx +96 -0
  197. package/src/runtime/useStreamingTiming.ts +69 -0
  198. package/src/tools/generativeTools.test.ts +1058 -0
  199. package/src/tools/generativeTools.ts +491 -0
  200. package/src/tools/mcp-stdio.node.ts +1 -0
  201. package/src/tools/mcp-stdio.unsupported.ts +12 -0
  202. package/src/transport/AssistantChatTransport.test.ts +631 -0
  203. package/src/transport/AssistantChatTransport.ts +236 -0
  204. package/src/transport/resumable.test.ts +262 -0
  205. package/src/transport/resumable.ts +162 -0
  206. package/src/usage.test.ts +204 -0
  207. package/src/usage.ts +179 -0
  208. package/src/utils/getVercelAIMessages.ts +11 -0
  209. package/src/utils/sliceMessagesUntil.test.ts +51 -0
  210. package/src/utils/sliceMessagesUntil.ts +16 -0
@@ -0,0 +1,60 @@
1
+ import { expect, it, vi } from "vitest";
2
+ import { createTapRoot } from "@ulearning/ai-tap";
3
+ import { Chat } from "@ai-sdk/vue";
4
+ import type { UIMessage } from "ai";
5
+ import { useChatSnapshot } from "./useChatSnapshot";
6
+
7
+ it("preserves raw message identity and publishes a replaced array even with the same messages", async () => {
8
+ const first: UIMessage = {
9
+ id: "first",
10
+ role: "user",
11
+ parts: [{ type: "text", text: "First" }],
12
+ };
13
+ const second: UIMessage = {
14
+ id: "second",
15
+ role: "assistant",
16
+ parts: [{ type: "text", text: "Second" }],
17
+ };
18
+ const chat = new Chat({ messages: [first, second] });
19
+ const root = createTapRoot(function useSnapshotReader() {
20
+ return useChatSnapshot(chat, undefined);
21
+ });
22
+ try {
23
+ const before = root.getValue();
24
+ expect(before.messages[0]).toBe(first);
25
+ expect(before.messages[1]).toBe(second);
26
+ before.setMessages([...before.messages]);
27
+ await vi.waitFor(() =>
28
+ expect(root.getValue().messages).not.toBe(before.messages),
29
+ );
30
+ expect(root.getValue().messages[0]).toBe(first);
31
+ expect(root.getValue().messages[1]).toBe(second);
32
+ const replacement = root.getValue();
33
+ replacement.setMessages((messages) => messages);
34
+ // Re-reading an unchanged native state must return the same snapshot.
35
+ expect(root.getValue()).toBe(replacement);
36
+ } finally {
37
+ root.unmount();
38
+ }
39
+ });
40
+
41
+ it("releases native watches and pending throttled publications when its resource unmounts", async () => {
42
+ vi.useFakeTimers();
43
+ const chat = new Chat<UIMessage>({ messages: [] });
44
+ const root = createTapRoot(function useSnapshotReader() {
45
+ return useChatSnapshot(chat, 50);
46
+ });
47
+ const listener = vi.fn();
48
+ root.subscribe(listener);
49
+ const last = root.getValue();
50
+ root.unmount();
51
+ try {
52
+ chat.messages = [{ id: "late", role: "user", parts: [] }];
53
+ await vi.advanceTimersByTimeAsync(100);
54
+ expect(listener).not.toHaveBeenCalled();
55
+ expect(root.getValue()).toBe(last);
56
+ expect(vi.getTimerCount()).toBe(0);
57
+ } finally {
58
+ vi.useRealTimers();
59
+ }
60
+ });
@@ -0,0 +1,86 @@
1
+ import { effectScope, toRaw, watch } from "vue";
2
+ import { useMemo, useSyncExternalStore } from "@ulearning/ai-tap";
3
+ import type { Chat } from "@ai-sdk/vue";
4
+ import type { UIMessage } from "ai";
5
+ import type { AISDKChatHelpers } from "../aiSDKExtras";
6
+
7
+ /** One cached immutable snapshot per resource, without proxying message identities. */
8
+ export const useChatSnapshot = <UI_MESSAGE extends UIMessage>(
9
+ chat: Chat<UI_MESSAGE>,
10
+ throttle: number | undefined,
11
+ ): AISDKChatHelpers<UI_MESSAGE> => {
12
+ const store = useMemo(() => {
13
+ let snapshot: AISDKChatHelpers<UI_MESSAGE> | undefined;
14
+ let source: UI_MESSAGE[] | undefined;
15
+ const setMessages: AISDKChatHelpers<UI_MESSAGE>["setMessages"] = (next) => {
16
+ chat.messages =
17
+ typeof next === "function" ? next(toRaw(chat.messages)) : next;
18
+ };
19
+ const read = () => {
20
+ const messages = chat.messages.map(toRaw);
21
+ const currentSource = toRaw(chat.messages);
22
+ const status = chat.status;
23
+ const error = chat.error === undefined ? undefined : toRaw(chat.error);
24
+ if (
25
+ snapshot &&
26
+ source === currentSource &&
27
+ snapshot.status === status &&
28
+ snapshot.error === error &&
29
+ snapshot.messages.length === messages.length &&
30
+ snapshot.messages.every((message, index) => message === messages[index])
31
+ )
32
+ return snapshot;
33
+ source = currentSource;
34
+ snapshot = {
35
+ id: chat.id,
36
+ messages,
37
+ status,
38
+ error,
39
+ setMessages,
40
+ sendMessage: chat.sendMessage,
41
+ regenerate: chat.regenerate,
42
+ stop: chat.stop,
43
+ resumeStream: chat.resumeStream,
44
+ clearError: chat.clearError,
45
+ addToolOutput: chat.addToolOutput,
46
+ addToolResult: chat.addToolResult,
47
+ addToolApprovalResponse: chat.addToolApprovalResponse,
48
+ };
49
+ return snapshot;
50
+ };
51
+ return {
52
+ read,
53
+ subscribe(listener: () => void) {
54
+ const scope = effectScope(true);
55
+ let timer: ReturnType<typeof setTimeout> | undefined;
56
+ scope.run(() =>
57
+ watch(
58
+ read,
59
+ () => {
60
+ if (
61
+ !throttle ||
62
+ chat.status === "ready" ||
63
+ chat.status === "error"
64
+ ) {
65
+ if (timer !== undefined) clearTimeout(timer);
66
+ timer = undefined;
67
+ listener();
68
+ } else if (timer === undefined) {
69
+ timer = setTimeout(() => {
70
+ timer = undefined;
71
+ listener();
72
+ }, throttle);
73
+ }
74
+ },
75
+ { flush: "sync" },
76
+ ),
77
+ );
78
+ return () => {
79
+ scope.stop();
80
+ if (timer !== undefined) clearTimeout(timer);
81
+ };
82
+ },
83
+ };
84
+ }, [chat, throttle]);
85
+ return useSyncExternalStore(store.subscribe, store.read, store.read);
86
+ };
@@ -0,0 +1,140 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { defineComponent, h, onMounted, Suspense } from "vue";
4
+ import { render } from "./__tests__/vue";
5
+ import { useTapRoot } from "@ulearning/ai-tap";
6
+ import type { AssistantRuntime } from "@ulearning/ai-core";
7
+ import type { ChatInit, ChatTransport, UIMessage } from "ai";
8
+ import { beforeEach, describe, expect, it, vi } from "vitest";
9
+ import { AssistantChatTransport } from "../transport/AssistantChatTransport";
10
+ import { DynamicChatTransport } from "./DynamicChatTransport";
11
+
12
+ const mocks = vi.hoisted(() => ({
13
+ system: "",
14
+ useAISDKRuntime: vi.fn(),
15
+ transport: undefined as ChatTransport<UIMessage> | undefined,
16
+ }));
17
+
18
+ vi.mock("@ai-sdk/vue", async (importOriginal) => {
19
+ const actual = await importOriginal<typeof import("@ai-sdk/vue")>();
20
+ return {
21
+ ...actual,
22
+ Chat: class extends actual.Chat<UIMessage> {
23
+ constructor(config: ChatInit<UIMessage>) {
24
+ super(config);
25
+ mocks.transport = config.transport;
26
+ }
27
+ },
28
+ };
29
+ });
30
+
31
+ vi.mock("./useAISDKRuntime", async (importOriginal) => ({
32
+ ...(await importOriginal<typeof import("./useAISDKRuntime")>()),
33
+ useAISDKRuntime: mocks.useAISDKRuntime,
34
+ }));
35
+
36
+ import { useChatThread } from "./useChatThread";
37
+
38
+ const createRuntime = (system: string) =>
39
+ ({
40
+ thread: {
41
+ getModelContext: () => ({ system }),
42
+ getState: () => ({ isLoading: false }),
43
+ subscribe: () => () => {},
44
+ },
45
+ }) as unknown as AssistantRuntime;
46
+
47
+ describe("useChatThread transport binding", () => {
48
+ beforeEach(() => {
49
+ mocks.system = "";
50
+ mocks.useAISDKRuntime.mockReset();
51
+ mocks.transport = undefined;
52
+ mocks.useAISDKRuntime.mockImplementation(() => createRuntime(mocks.system));
53
+ });
54
+
55
+ it("keeps fallback wiring on committed values after an abandoned render", async () => {
56
+ const bodies: Array<{ id: string; system: string }> = [];
57
+ const sourceTransport = new AssistantChatTransport<UIMessage>({
58
+ fetch: vi.fn(async (_input, init) => {
59
+ bodies.push(JSON.parse(String(init?.body)));
60
+ return new Response(
61
+ new ReadableStream({ start: (controller) => controller.close() }),
62
+ { headers: { "content-type": "text/event-stream" } },
63
+ );
64
+ }),
65
+ });
66
+ const transport = new DynamicChatTransport(sourceTransport);
67
+ const pending = new Promise<never>(() => {});
68
+ let committedProxy: ChatTransport<UIMessage> | undefined;
69
+
70
+ const Blocker = defineComponent({
71
+ async setup() {
72
+ await pending;
73
+ return () => null;
74
+ },
75
+ });
76
+ const App = defineComponent(
77
+ (props: { system: string; remoteId: string; suspend: boolean }) => {
78
+ const root = useTapRoot(function ChatThreadBinding() {
79
+ mocks.system = props.system;
80
+ return useChatThread(
81
+ { transport },
82
+ {
83
+ id: "thread-id",
84
+ isMainThread: true,
85
+ getThreadListItem: () => ({
86
+ initialize: async () => ({
87
+ remoteId: props.remoteId,
88
+ externalId: undefined,
89
+ }),
90
+ }),
91
+ },
92
+ );
93
+ });
94
+ root.getValue();
95
+ const proxy = mocks.transport;
96
+ onMounted(() => {
97
+ committedProxy = proxy;
98
+ });
99
+ return () => (props.suspend ? h(Blocker) : null);
100
+ },
101
+ { props: ["system", "remoteId", "suspend"] },
102
+ );
103
+ const scene = (suspend: boolean) =>
104
+ h(Suspense, null, {
105
+ default: () =>
106
+ h(App, {
107
+ key: suspend ? "discarded" : "committed",
108
+ suspend,
109
+ system: suspend ? "discarded-system" : "committed-system",
110
+ remoteId: suspend ? "discarded-remote" : "committed-remote",
111
+ }),
112
+ fallback: () => null,
113
+ });
114
+ const view = render(scene(false));
115
+
116
+ transport.registerThread("thread-id", {});
117
+ await view.rerender(scene(true));
118
+
119
+ await committedProxy!.sendMessages({
120
+ trigger: "submit-message",
121
+ chatId: "thread-id",
122
+ messageId: undefined,
123
+ messages: [
124
+ {
125
+ id: "message-id",
126
+ role: "user",
127
+ parts: [{ type: "text", text: "hello" }],
128
+ },
129
+ ],
130
+ abortSignal: undefined,
131
+ });
132
+
133
+ expect(bodies).toEqual([
134
+ expect.objectContaining({
135
+ id: "committed-remote",
136
+ system: "committed-system",
137
+ }),
138
+ ]);
139
+ });
140
+ });