@assistant-ui/ai-sdk 0.0.1

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 (167) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -0
  3. package/dist/adapters/aiSDKFormatAdapter.d.ts +8 -0
  4. package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
  5. package/dist/adapters/aiSDKFormatAdapter.js +26 -0
  6. package/dist/adapters/aiSDKFormatAdapter.js.map +1 -0
  7. package/dist/adapters/vercelAttachmentAdapter.d.ts +6 -0
  8. package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -0
  9. package/dist/adapters/vercelAttachmentAdapter.js +37 -0
  10. package/dist/adapters/vercelAttachmentAdapter.js.map +1 -0
  11. package/dist/aiSDKExtras.d.ts +11 -0
  12. package/dist/aiSDKExtras.d.ts.map +1 -0
  13. package/dist/aiSDKExtras.js +7 -0
  14. package/dist/aiSDKExtras.js.map +1 -0
  15. package/dist/converters/convertMessage.d.ts +28 -0
  16. package/dist/converters/convertMessage.d.ts.map +1 -0
  17. package/dist/converters/convertMessage.js +249 -0
  18. package/dist/converters/convertMessage.js.map +1 -0
  19. package/dist/converters/modelContentEnvelope.d.ts +16 -0
  20. package/dist/converters/modelContentEnvelope.d.ts.map +1 -0
  21. package/dist/converters/modelContentEnvelope.js +22 -0
  22. package/dist/converters/modelContentEnvelope.js.map +1 -0
  23. package/dist/converters/toCreateMessage.d.ts +7 -0
  24. package/dist/converters/toCreateMessage.d.ts.map +1 -0
  25. package/dist/converters/toCreateMessage.js +58 -0
  26. package/dist/converters/toCreateMessage.js.map +1 -0
  27. package/dist/converters/toolOutputConversion.d.ts +29 -0
  28. package/dist/converters/toolOutputConversion.d.ts.map +1 -0
  29. package/dist/converters/toolOutputConversion.js +31 -0
  30. package/dist/converters/toolOutputConversion.js.map +1 -0
  31. package/dist/hooks.d.ts +19 -0
  32. package/dist/hooks.d.ts.map +1 -0
  33. package/dist/hooks.js +20 -0
  34. package/dist/hooks.js.map +1 -0
  35. package/dist/index.d.ts +14 -0
  36. package/dist/index.js +13 -0
  37. package/dist/index.native.d.ts +12 -0
  38. package/dist/index.native.js +11 -0
  39. package/dist/model-context/injectInteractableContext.d.ts +37 -0
  40. package/dist/model-context/injectInteractableContext.d.ts.map +1 -0
  41. package/dist/model-context/injectInteractableContext.js +52 -0
  42. package/dist/model-context/injectInteractableContext.js.map +1 -0
  43. package/dist/model-context/injectQuoteContext.d.ts +27 -0
  44. package/dist/model-context/injectQuoteContext.d.ts.map +1 -0
  45. package/dist/model-context/injectQuoteContext.js +51 -0
  46. package/dist/model-context/injectQuoteContext.js.map +1 -0
  47. package/dist/runtime/AISDKChat.d.ts +16 -0
  48. package/dist/runtime/AISDKChat.d.ts.map +1 -0
  49. package/dist/runtime/AISDKChat.js +69 -0
  50. package/dist/runtime/AISDKChat.js.map +1 -0
  51. package/dist/runtime/AISDKThreads.d.ts +45 -0
  52. package/dist/runtime/AISDKThreads.d.ts.map +1 -0
  53. package/dist/runtime/AISDKThreads.js +203 -0
  54. package/dist/runtime/AISDKThreads.js.map +1 -0
  55. package/dist/runtime/useAISDKRuntime.d.ts +49 -0
  56. package/dist/runtime/useAISDKRuntime.d.ts.map +1 -0
  57. package/dist/runtime/useAISDKRuntime.js +266 -0
  58. package/dist/runtime/useAISDKRuntime.js.map +1 -0
  59. package/dist/runtime/useChatRuntime.d.ts +13 -0
  60. package/dist/runtime/useChatRuntime.d.ts.map +1 -0
  61. package/dist/runtime/useChatRuntime.js +52 -0
  62. package/dist/runtime/useChatRuntime.js.map +1 -0
  63. package/dist/runtime/useChatThread.d.ts +77 -0
  64. package/dist/runtime/useChatThread.d.ts.map +1 -0
  65. package/dist/runtime/useChatThread.js +134 -0
  66. package/dist/runtime/useChatThread.js.map +1 -0
  67. package/dist/runtime/useExternalHistory.d.ts +11 -0
  68. package/dist/runtime/useExternalHistory.d.ts.map +1 -0
  69. package/dist/runtime/useExternalHistory.js +278 -0
  70. package/dist/runtime/useExternalHistory.js.map +1 -0
  71. package/dist/runtime/useResourceCleanup.d.ts +5 -0
  72. package/dist/runtime/useResourceCleanup.d.ts.map +1 -0
  73. package/dist/runtime/useResourceCleanup.js +47 -0
  74. package/dist/runtime/useResourceCleanup.js.map +1 -0
  75. package/dist/runtime/useStreamingTiming.d.ts +15 -0
  76. package/dist/runtime/useStreamingTiming.d.ts.map +1 -0
  77. package/dist/runtime/useStreamingTiming.js +38 -0
  78. package/dist/runtime/useStreamingTiming.js.map +1 -0
  79. package/dist/tools/frontendTools.d.ts +33 -0
  80. package/dist/tools/frontendTools.d.ts.map +1 -0
  81. package/dist/tools/frontendTools.js +33 -0
  82. package/dist/tools/frontendTools.js.map +1 -0
  83. package/dist/tools/generativeTools.d.ts +73 -0
  84. package/dist/tools/generativeTools.d.ts.map +1 -0
  85. package/dist/tools/generativeTools.js +245 -0
  86. package/dist/tools/generativeTools.js.map +1 -0
  87. package/dist/tools/mcp-stdio.node.d.ts +2 -0
  88. package/dist/tools/mcp-stdio.node.js +2 -0
  89. package/dist/tools/mcp-stdio.unsupported.d.ts +6 -0
  90. package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -0
  91. package/dist/tools/mcp-stdio.unsupported.js +11 -0
  92. package/dist/tools/mcp-stdio.unsupported.js.map +1 -0
  93. package/dist/transport/AssistantChatTransport.d.ts +23 -0
  94. package/dist/transport/AssistantChatTransport.d.ts.map +1 -0
  95. package/dist/transport/AssistantChatTransport.js +134 -0
  96. package/dist/transport/AssistantChatTransport.js.map +1 -0
  97. package/dist/transport/resumable.d.ts +38 -0
  98. package/dist/transport/resumable.d.ts.map +1 -0
  99. package/dist/transport/resumable.js +113 -0
  100. package/dist/transport/resumable.js.map +1 -0
  101. package/dist/usage.d.ts +19 -0
  102. package/dist/usage.d.ts.map +1 -0
  103. package/dist/usage.js +127 -0
  104. package/dist/usage.js.map +1 -0
  105. package/dist/utils/getVercelAIMessages.d.ts +7 -0
  106. package/dist/utils/getVercelAIMessages.d.ts.map +1 -0
  107. package/dist/utils/getVercelAIMessages.js +9 -0
  108. package/dist/utils/getVercelAIMessages.js.map +1 -0
  109. package/dist/utils/sliceMessagesUntil.d.ts +6 -0
  110. package/dist/utils/sliceMessagesUntil.d.ts.map +1 -0
  111. package/dist/utils/sliceMessagesUntil.js +12 -0
  112. package/dist/utils/sliceMessagesUntil.js.map +1 -0
  113. package/package.json +100 -0
  114. package/src/__tests__/standalone.e2e.ts +59 -0
  115. package/src/adapters/aiSDKFormatAdapter.ts +41 -0
  116. package/src/adapters/vercelAttachmentAdapter.test.ts +29 -0
  117. package/src/adapters/vercelAttachmentAdapter.ts +36 -0
  118. package/src/aiSDKExtras.ts +11 -0
  119. package/src/converters/convertMessage.test.ts +1130 -0
  120. package/src/converters/convertMessage.ts +457 -0
  121. package/src/converters/modelContentEnvelope.ts +41 -0
  122. package/src/converters/toCreateMessage.test.ts +596 -0
  123. package/src/converters/toCreateMessage.ts +99 -0
  124. package/src/converters/toolOutputConversion.ts +26 -0
  125. package/src/hooks.ts +23 -0
  126. package/src/index.native.ts +21 -0
  127. package/src/index.ts +29 -0
  128. package/src/model-context/injectInteractableContext.test.ts +167 -0
  129. package/src/model-context/injectInteractableContext.ts +61 -0
  130. package/src/model-context/injectQuoteContext.test.ts +33 -0
  131. package/src/model-context/injectQuoteContext.ts +62 -0
  132. package/src/runtime/AISDKChat.integration.test.tsx +47 -0
  133. package/src/runtime/AISDKChat.react-compiler.test.ts +49 -0
  134. package/src/runtime/AISDKChat.test.ts +193 -0
  135. package/src/runtime/AISDKChat.ts +47 -0
  136. package/src/runtime/AISDKThreads.cloud.test.ts +136 -0
  137. package/src/runtime/AISDKThreads.test.ts +548 -0
  138. package/src/runtime/AISDKThreads.ts +223 -0
  139. package/src/runtime/__tests__/controlled-transport.ts +43 -0
  140. package/src/runtime/useAISDKRuntime.approval.test.tsx +69 -0
  141. package/src/runtime/useAISDKRuntime.test.ts +1021 -0
  142. package/src/runtime/useAISDKRuntime.tool-output.test.tsx +113 -0
  143. package/src/runtime/useAISDKRuntime.ts +524 -0
  144. package/src/runtime/useChatOnTap.test.ts +58 -0
  145. package/src/runtime/useChatRuntime.integration.test.tsx +67 -0
  146. package/src/runtime/useChatRuntime.test.ts +522 -0
  147. package/src/runtime/useChatRuntime.ts +49 -0
  148. package/src/runtime/useChatThread.ts +286 -0
  149. package/src/runtime/useExternalHistory.test.ts +1289 -0
  150. package/src/runtime/useExternalHistory.ts +435 -0
  151. package/src/runtime/useResourceCleanup.ts +32 -0
  152. package/src/runtime/useStreamingTiming.ts +68 -0
  153. package/src/tools/frontendTools.test.ts +205 -0
  154. package/src/tools/frontendTools.ts +83 -0
  155. package/src/tools/generativeTools.test.ts +785 -0
  156. package/src/tools/generativeTools.ts +470 -0
  157. package/src/tools/mcp-stdio.node.ts +1 -0
  158. package/src/tools/mcp-stdio.unsupported.ts +12 -0
  159. package/src/transport/AssistantChatTransport.test.ts +300 -0
  160. package/src/transport/AssistantChatTransport.ts +204 -0
  161. package/src/transport/resumable.test.ts +262 -0
  162. package/src/transport/resumable.ts +161 -0
  163. package/src/usage.test.ts +186 -0
  164. package/src/usage.ts +174 -0
  165. package/src/utils/getVercelAIMessages.ts +11 -0
  166. package/src/utils/sliceMessagesUntil.test.ts +55 -0
  167. package/src/utils/sliceMessagesUntil.ts +20 -0
@@ -0,0 +1,52 @@
1
+ "use client";
2
+ import { useChatThread } from "./useChatThread.js";
3
+ import { useCloudThreadListAdapter, useRemoteThreadListRuntime } from "@assistant-ui/core/react";
4
+ import { useAui, useAuiState } from "@assistant-ui/store";
5
+ import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
6
+ //#region src/runtime/useChatRuntime.ts
7
+ const useChatThreadRuntime = (options) => {
8
+ const $ = c(6);
9
+ const id = useAuiState(_temp);
10
+ const isMainThread = useAuiState(_temp2);
11
+ const aui = useAui();
12
+ let t0;
13
+ if ($[0] !== aui) {
14
+ t0 = () => aui.threadListItem.source ? aui.threadListItem : void 0;
15
+ $[0] = aui;
16
+ $[1] = t0;
17
+ } else t0 = $[1];
18
+ let t1;
19
+ if ($[2] !== id || $[3] !== isMainThread || $[4] !== t0) {
20
+ t1 = {
21
+ id,
22
+ isMainThread,
23
+ getThreadListItem: t0
24
+ };
25
+ $[2] = id;
26
+ $[3] = isMainThread;
27
+ $[4] = t0;
28
+ $[5] = t1;
29
+ } else t1 = $[5];
30
+ return useChatThread(options, t1);
31
+ };
32
+ const useChatRuntime = ({ cloud, onThreadIdChange, ...options } = {}) => {
33
+ const cloudAdapter = useCloudThreadListAdapter({ cloud });
34
+ return useRemoteThreadListRuntime({
35
+ runtimeHook: function RuntimeHook() {
36
+ return useChatThreadRuntime(options);
37
+ },
38
+ adapter: cloudAdapter,
39
+ allowNesting: true,
40
+ onThreadIdChange
41
+ });
42
+ };
43
+ function _temp(s) {
44
+ return s.threadListItem.id;
45
+ }
46
+ function _temp2(s_0) {
47
+ return s_0.threads.mainThreadId === s_0.threadListItem.id;
48
+ }
49
+ //#endregion
50
+ export { useChatRuntime };
51
+
52
+ //# sourceMappingURL=useChatRuntime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatRuntime.js","names":["c","_c","UIMessage","AssistantCloud","AssistantRuntime","useCloudThreadListAdapter","useRemoteThreadListRuntime","useAui","useAuiState","useChatThread","ChatThreadOptions","UseChatRuntimeOptions","UI_MESSAGE","cloud","onThreadIdChange","threadId","useChatThreadRuntime","options","$","id","_temp","isMainThread","_temp2","aui","t0","threadListItem","source","undefined","t1","getThreadListItem","useChatRuntime","cloudAdapter","runtimeHook","RuntimeHook","adapter","allowNesting","s","s_0","threads","mainThreadId"],"sources":["../../src/runtime/useChatRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport type { UIMessage } from \"@ai-sdk/react\";\nimport type { AssistantCloud } from \"assistant-cloud\";\nimport type { AssistantRuntime } from \"@assistant-ui/core\";\nimport {\n useCloudThreadListAdapter,\n useRemoteThreadListRuntime,\n} from \"@assistant-ui/core/react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport { useChatThread, type ChatThreadOptions } from \"./useChatThread\";\n\nexport type UseChatRuntimeOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatThreadOptions<UI_MESSAGE> & {\n cloud?: AssistantCloud | undefined;\n onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;\n };\n\nconst useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(\n options?: ChatThreadOptions<UI_MESSAGE>,\n): AssistantRuntime => {\n const id = useAuiState((s) => s.threadListItem.id);\n const isMainThread = useAuiState(\n (s) => s.threads.mainThreadId === s.threadListItem.id,\n );\n const aui = useAui();\n return useChatThread(options, {\n id,\n isMainThread,\n getThreadListItem: () =>\n aui.threadListItem.source ? aui.threadListItem : undefined,\n });\n};\n\nexport const useChatRuntime = <UI_MESSAGE extends UIMessage = UIMessage>({\n cloud,\n onThreadIdChange,\n ...options\n}: UseChatRuntimeOptions<UI_MESSAGE> = {}): AssistantRuntime => {\n const cloudAdapter = useCloudThreadListAdapter({ cloud });\n return useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n return useChatThreadRuntime(options);\n },\n adapter: cloudAdapter,\n allowNesting: true,\n onThreadIdChange,\n });\n};\n"],"mappings":";;;;;;AAkBA,MAAMgB,wBAAuBC,YAAA;CAAA,MAAAC,IAAAjB,EAAA,CAAA;CAG3B,MAAAkB,KAAWX,YAAYY,KAA0B;CACjD,MAAAC,eAAqBb,YACnBc,MACF;CACA,MAAAC,MAAYhB,OAAO;CAAE,IAAAiB;CAAA,IAAAN,EAAA,OAAAK,KAAA;EAIAC,WACjBD,IAAGE,eAAeC,SAAUH,IAAGE,iBAA/BE,KAAAA;EAA0DT,EAAA,KAAAK;EAAAL,EAAA,KAAAM;CAAA,OAAAA,KAAAN,EAAA;CAAA,IAAAU;CAAA,IAAAV,EAAA,OAAAC,MAAAD,EAAA,OAAAG,gBAAAH,EAAA,OAAAM,IAAA;EAJhCI,KAAA;GAAAT;GAAAE;GAAAQ,mBAGTL;EAErB;EAACN,EAAA,KAAAC;EAAAD,EAAA,KAAAG;EAAAH,EAAA,KAAAM;EAAAN,EAAA,KAAAU;CAAA,OAAAA,KAAAV,EAAA;CAAA,OALMT,cAAcQ,SAASW,EAK7B;AAAC;AAGJ,MAAaE,kBAA4D,EACvEjB,OACAC,kBACA,GAAGG,YACkC,CAAC,MAAwB;CAC9D,MAAMc,eAAe1B,0BAA0B,EAAEQ,MAAM,CAAC;CACxD,OAAOP,2BAA2B;EAChC0B,aAAa,SAASC,cAAc;GAClC,OAAOjB,qBAAqBC,OAAO;EACrC;EACAiB,SAASH;EACTI,cAAc;EACdrB;CACF,CAAC;AACH;AA9B6B,SAAAM,MAAAgB,GAAA;CAAA,OAGGA,EAACX,eAAeN;AAAG;AAHtB,SAAAG,OAAAe,KAAA;CAAA,OAKlBD,IAACE,QAAQC,iBAAkBH,IAACX,eAAeN;AAAG"}
@@ -0,0 +1,77 @@
1
+ import { AISDKRuntimeAdapter, CustomToCreateMessageFunction } from "./useAISDKRuntime.js";
2
+ import { InitializableThreadListItem } from "../transport/AssistantChatTransport.js";
3
+ import { ChatInit, ChatTransport } from "ai";
4
+ import { AssistantRuntime, ExternalStoreSharedOptions } from "@assistant-ui/core";
5
+ import { Chat, UIMessage as UIMessage$1 } from "@ai-sdk/react";
6
+ //#region src/runtime/useChatThread.d.ts
7
+ type ChatThreadOptions<UI_MESSAGE extends UIMessage$1 = UIMessage$1> = ChatInit<UI_MESSAGE> & ExternalStoreSharedOptions & {
8
+ adapters?: AISDKRuntimeAdapter["adapters"] | undefined;
9
+ toCreateMessage?: CustomToCreateMessageFunction;
10
+ onResume?: AISDKRuntimeAdapter["onResume"];
11
+ onResumeToolCall?: AISDKRuntimeAdapter["onResumeToolCall"];
12
+ /**
13
+ * Called when an automatic resumable stream reconnect fails. Use this to
14
+ * surface a toast, report telemetry, or mark the thread as needing a
15
+ * retry. The failed stream id is cleared after the callback unless a
16
+ * newer id has replaced it.
17
+ */
18
+ onResumeError?: ((error: unknown) => void) | undefined;
19
+ joinStrategy?: AISDKRuntimeAdapter["joinStrategy"];
20
+ };
21
+ type ChatThreadEnvironment<UI_MESSAGE extends UIMessage$1 = UIMessage$1> = {
22
+ id: string;
23
+ isMainThread: boolean;
24
+ getThreadListItem: () => InitializableThreadListItem | undefined;
25
+ stopOnClientDestroy?: boolean;
26
+ /**
27
+ * An externally owned chat instance. State lives on the instance, so it
28
+ * survives the hosting resource unmounting; construction options are read
29
+ * from the instance.
30
+ */
31
+ chat?: Chat<UI_MESSAGE> | undefined;
32
+ };
33
+ /**
34
+ * Splits the combined options into the assistant-ui side and the `ChatInit`
35
+ * remainder the AI SDK consumes, so external `Chat` construction forwards the
36
+ * same fields `useChat` would.
37
+ */
38
+ declare const splitChatThreadOptions: <UI_MESSAGE extends UIMessage$1>(options: ChatThreadOptions<UI_MESSAGE> | undefined) => {
39
+ adapters: ({
40
+ attachments?: import("@assistant-ui/core").AttachmentAdapter | undefined;
41
+ speech?: import("@assistant-ui/core").SpeechSynthesisAdapter | undefined;
42
+ dictation?: import("@assistant-ui/core").DictationAdapter | undefined;
43
+ voice?: import("@assistant-ui/core").RealtimeVoiceAdapter | undefined;
44
+ feedback?: import("@assistant-ui/core").FeedbackAdapter | undefined;
45
+ threadList?: import("@assistant-ui/core").ExternalStoreThreadListAdapter | undefined;
46
+ } & {
47
+ history?: import("@assistant-ui/core").ThreadHistoryAdapter | undefined;
48
+ suggestion?: import("@assistant-ui/core").SuggestionAdapter | undefined;
49
+ }) | undefined;
50
+ transport: ChatTransport<UI_MESSAGE> | undefined;
51
+ toCreateMessage: CustomToCreateMessageFunction | undefined;
52
+ onResume: ((config: import("@assistant-ui/core").ResumeRunConfig) => Promise<void>) | undefined;
53
+ onResumeToolCall: ((options: {
54
+ toolCallId: string;
55
+ payload: unknown;
56
+ }) => void) | undefined;
57
+ onResumeError: ((error: unknown) => void) | undefined;
58
+ joinStrategy: import("@assistant-ui/core/store").JoinStrategy | undefined;
59
+ chatInit: {
60
+ id?: string;
61
+ messageMetadataSchema?: import("ai").FlexibleSchema<UI_MESSAGE extends UIMessage$1<infer METADATA, import("ai").UIDataTypes, import("ai").UITools> ? METADATA : unknown>;
62
+ dataPartSchemas?: (UI_MESSAGE extends UIMessage$1<unknown, infer DATA_TYPES extends import("ai").UIDataTypes, import("ai").UITools> ? DATA_TYPES : import("ai").UIDataTypes) extends (infer T extends import("ai").UIDataTypes) ? { [K in keyof T]: import("ai").FlexibleSchema<T[K]>; } : never;
63
+ messages?: UI_MESSAGE[];
64
+ generateId?: import("ai").IdGenerator;
65
+ onError?: import("ai").ChatOnErrorCallback;
66
+ onToolCall?: import("ai").ChatOnToolCallCallback<UI_MESSAGE>;
67
+ onFinish?: import("ai").ChatOnFinishCallback<UI_MESSAGE>;
68
+ onData?: import("ai").ChatOnDataCallback<UI_MESSAGE>;
69
+ sendAutomaticallyWhen?: (options: {
70
+ messages: UI_MESSAGE[];
71
+ }) => boolean | PromiseLike<boolean>;
72
+ };
73
+ };
74
+ declare const useChatThread: <UI_MESSAGE extends UIMessage$1 = UIMessage$1>(options: ChatThreadOptions<UI_MESSAGE> | undefined, env: ChatThreadEnvironment<UI_MESSAGE>) => AssistantRuntime;
75
+ //#endregion
76
+ export { ChatThreadEnvironment, ChatThreadOptions, splitChatThreadOptions, useChatThread };
77
+ //# sourceMappingURL=useChatThread.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatThread.d.ts","names":[],"sources":["../../src/runtime/useChatThread.ts"],"mappings":";;;;;;KA+BY,kBAAkB,mBAAmB,cAAY,eAC3D,SAAS,cACP;EACE,WAAW;EACX,kBAAkB;EAClB,WAAW;EACX,mBAAmB;;;;;;;EAOnB,kBAAkB;EAClB,eAAe;;KAGT,sBAAsB,mBAAmB,cAAY;EAC/D;EACA;EACA,yBAAyB;EACzB;;;;;;EAMA,OAAO,KAAK;;;;;;;cA4DD,yBAA0B,mBAAmB,aACxD,SAAS,kBAAkB;;;;;;;;;;;;;;;;;;;EA3EE,iBAAA;;;;;;;;;;;;;;;;;cA4GlB,gBAAiB,mBAAmB,cAAY,aAC3D,SAAS,kBAAkB,yBAC3B,KAAK,sBAAsB,gBAC1B"}
@@ -0,0 +1,134 @@
1
+ "use client";
2
+ import { useAISDKRuntime } from "./useAISDKRuntime.js";
3
+ import { AssistantChatTransport } from "../transport/AssistantChatTransport.js";
4
+ import { useResourceCleanup } from "./useResourceCleanup.js";
5
+ import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from "@assistant-ui/tap/react-shim";
6
+ import { pickExternalStoreSharedOptions } from "@assistant-ui/core";
7
+ import { useChat } from "@ai-sdk/react";
8
+ //#region src/runtime/useChatThread.ts
9
+ const useDynamicChatTransport = (transport) => {
10
+ const transportRef = useRef(transport);
11
+ useEffect(() => {
12
+ transportRef.current = transport;
13
+ });
14
+ return useMemo(() => new Proxy(transportRef.current, { get(_, prop) {
15
+ const res = transportRef.current[prop];
16
+ return typeof res === "function" ? res.bind(transportRef.current) : res;
17
+ } }), []);
18
+ };
19
+ const getResumableAdapter = (transport) => {
20
+ if (transport instanceof AssistantChatTransport) return transport.getResumableAdapter();
21
+ const candidate = transport.getResumableAdapter;
22
+ if (typeof candidate !== "function") return void 0;
23
+ return candidate.call(transport);
24
+ };
25
+ const getNoPendingStreamId = () => null;
26
+ const resumedStreamIdsByStorage = /* @__PURE__ */ new WeakMap();
27
+ const getResumedStreamIds = (storage) => {
28
+ if (!storage) return /* @__PURE__ */ new Set();
29
+ let resumedStreamIds = resumedStreamIdsByStorage.get(storage);
30
+ if (!resumedStreamIds) {
31
+ resumedStreamIds = /* @__PURE__ */ new Set();
32
+ resumedStreamIdsByStorage.set(storage, resumedStreamIds);
33
+ }
34
+ return resumedStreamIds;
35
+ };
36
+ /**
37
+ * Splits the combined options into the assistant-ui side and the `ChatInit`
38
+ * remainder the AI SDK consumes, so external `Chat` construction forwards the
39
+ * same fields `useChat` would.
40
+ */
41
+ const splitChatThreadOptions = (options) => {
42
+ const { adapters, transport, toCreateMessage, isDisabled: _isDisabled, isSendDisabled: _isSendDisabled, unstable_capabilities: _unstable_capabilities, suggestions: _suggestions, onResume, onResumeToolCall, onResumeError, joinStrategy, ...chatInit } = options ?? {};
43
+ return {
44
+ adapters,
45
+ transport,
46
+ toCreateMessage,
47
+ onResume,
48
+ onResumeToolCall,
49
+ onResumeError,
50
+ joinStrategy,
51
+ chatInit
52
+ };
53
+ };
54
+ const useChatThread = (options, env) => {
55
+ const { adapters, transport: transportOptions, toCreateMessage, onResume, onResumeToolCall, onResumeError, joinStrategy, chatInit: chatOptions } = splitChatThreadOptions(options);
56
+ const { id, isMainThread, getThreadListItem, stopOnClientDestroy = false, chat: externalChat } = env;
57
+ const defaultTransport = useMemo(() => new AssistantChatTransport(), []);
58
+ const sourceTransport = transportOptions ?? defaultTransport;
59
+ const transport = useDynamicChatTransport(sourceTransport);
60
+ const chat = useChat({
61
+ ...chatOptions,
62
+ id,
63
+ transport,
64
+ ...externalChat !== void 0 && { chat: externalChat }
65
+ });
66
+ useResourceCleanup(stopOnClientDestroy, () => {
67
+ chat.stop().catch(() => {});
68
+ });
69
+ const runtime = useAISDKRuntime(chat, {
70
+ adapters,
71
+ ...pickExternalStoreSharedOptions(options ?? {}),
72
+ ...toCreateMessage && { toCreateMessage },
73
+ ...onResume && { onResume },
74
+ ...onResumeToolCall && { onResumeToolCall },
75
+ ...joinStrategy && { joinStrategy }
76
+ });
77
+ if (sourceTransport instanceof AssistantChatTransport) {
78
+ sourceTransport.setRuntime(runtime);
79
+ sourceTransport.__internal_setGetThreadListItem(getThreadListItem);
80
+ }
81
+ const subscribeToRuntime = useCallback((callback) => runtime.thread.subscribe(callback), [runtime]);
82
+ const getHistoryLoadingSnapshot = useCallback(() => runtime.thread.getState().isLoading, [runtime]);
83
+ const isLoadingHistory = useSyncExternalStore(subscribeToRuntime, getHistoryLoadingSnapshot, getHistoryLoadingSnapshot);
84
+ const resumableStorage = useMemo(() => getResumableAdapter(sourceTransport)?.storage, [sourceTransport]);
85
+ const subscribeToResumableStorage = useCallback((callback_0) => isMainThread ? resumableStorage?.subscribe?.(callback_0, id) ?? (() => {}) : () => {}, [
86
+ id,
87
+ isMainThread,
88
+ resumableStorage
89
+ ]);
90
+ const getPendingStreamId = useCallback(() => isMainThread ? resumableStorage?.getStreamId(id) ?? null : null, [
91
+ id,
92
+ isMainThread,
93
+ resumableStorage
94
+ ]);
95
+ const pendingStreamId = useSyncExternalStore(subscribeToResumableStorage, getPendingStreamId, getNoPendingStreamId);
96
+ const isChatRunning = chat.status === "submitted" || chat.status === "streaming";
97
+ const resumedStreamIds = useMemo(() => getResumedStreamIds(resumableStorage), [resumableStorage]);
98
+ const onResumeErrorRef = useRef(onResumeError);
99
+ useEffect(() => {
100
+ onResumeErrorRef.current = onResumeError;
101
+ });
102
+ useEffect(() => {
103
+ if (!pendingStreamId || resumedStreamIds.has(pendingStreamId)) return;
104
+ if (isChatRunning) {
105
+ resumedStreamIds.add(pendingStreamId);
106
+ return;
107
+ }
108
+ if (isLoadingHistory) return;
109
+ resumedStreamIds.add(pendingStreamId);
110
+ chat.resumeStream().catch((err) => {
111
+ console.warn("[assistant-ui] resumable: resume failed", err);
112
+ try {
113
+ onResumeErrorRef.current?.(err);
114
+ } catch (callbackError) {
115
+ console.error("[assistant-ui] resumable: onResumeError callback failed", callbackError);
116
+ } finally {
117
+ if (resumableStorage?.getStreamId(id) === pendingStreamId) resumableStorage.clear(id);
118
+ }
119
+ });
120
+ }, [
121
+ chat,
122
+ id,
123
+ isChatRunning,
124
+ isLoadingHistory,
125
+ pendingStreamId,
126
+ resumableStorage,
127
+ resumedStreamIds
128
+ ]);
129
+ return runtime;
130
+ };
131
+ //#endregion
132
+ export { splitChatThreadOptions, useChatThread };
133
+
134
+ //# sourceMappingURL=useChatThread.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatThread.js","names":["useChat","Chat","UIMessage","pickExternalStoreSharedOptions","AssistantRuntime","ExternalStoreSharedOptions","useAISDKRuntime","AISDKRuntimeAdapter","CustomToCreateMessageFunction","ChatInit","ChatTransport","AssistantChatTransport","InitializableThreadListItem","AssistantChatResumableOptions","ResumableClientStorage","useCallback","useEffect","useMemo","useRef","useSyncExternalStore","useResourceCleanup","ChatThreadOptions","UI_MESSAGE","adapters","toCreateMessage","onResume","onResumeToolCall","onResumeError","error","joinStrategy","ChatThreadEnvironment","id","isMainThread","getThreadListItem","stopOnClientDestroy","chat","useDynamicChatTransport","transport","transportRef","current","dynamicTransport","Proxy","get","_","prop","res","bind","getResumableAdapter","candidate","undefined","call","getNoPendingStreamId","resumedStreamIdsByStorage","WeakMap","Set","getResumedStreamIds","storage","resumedStreamIds","set","splitChatThreadOptions","options","isDisabled","_isDisabled","isSendDisabled","_isSendDisabled","unstable_capabilities","_unstable_capabilities","suggestions","_suggestions","chatInit","useChatThread","env","transportOptions","chatOptions","externalChat","defaultTransport","sourceTransport","stop","catch","runtime","setRuntime","__internal_setGetThreadListItem","subscribeToRuntime","callback","thread","subscribe","getHistoryLoadingSnapshot","getState","isLoading","isLoadingHistory","resumableStorage","subscribeToResumableStorage","getPendingStreamId","getStreamId","pendingStreamId","isChatRunning","status","onResumeErrorRef","has","add","resumeStream","err","console","warn","callbackError","clear"],"sources":["../../src/runtime/useChatThread.ts"],"sourcesContent":["\"use client\";\n\nimport { useChat, type Chat, type UIMessage } from \"@ai-sdk/react\";\nimport {\n pickExternalStoreSharedOptions,\n type AssistantRuntime,\n type ExternalStoreSharedOptions,\n} from \"@assistant-ui/core\";\nimport {\n useAISDKRuntime,\n type AISDKRuntimeAdapter,\n type CustomToCreateMessageFunction,\n} from \"./useAISDKRuntime\";\nimport type { ChatInit, ChatTransport } from \"ai\";\nimport {\n AssistantChatTransport,\n type InitializableThreadListItem,\n} from \"../transport/AssistantChatTransport\";\nimport type {\n AssistantChatResumableOptions,\n ResumableClientStorage,\n} from \"../transport/resumable\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport { useResourceCleanup } from \"./useResourceCleanup\";\n\nexport type ChatThreadOptions<UI_MESSAGE extends UIMessage = UIMessage> =\n ChatInit<UI_MESSAGE> &\n ExternalStoreSharedOptions & {\n adapters?: AISDKRuntimeAdapter[\"adapters\"] | undefined;\n toCreateMessage?: CustomToCreateMessageFunction;\n onResume?: AISDKRuntimeAdapter[\"onResume\"];\n onResumeToolCall?: AISDKRuntimeAdapter[\"onResumeToolCall\"];\n /**\n * Called when an automatic resumable stream reconnect fails. Use this to\n * surface a toast, report telemetry, or mark the thread as needing a\n * retry. The failed stream id is cleared after the callback unless a\n * newer id has replaced it.\n */\n onResumeError?: ((error: unknown) => void) | undefined;\n joinStrategy?: AISDKRuntimeAdapter[\"joinStrategy\"];\n };\n\nexport type ChatThreadEnvironment<UI_MESSAGE extends UIMessage = UIMessage> = {\n id: string;\n isMainThread: boolean;\n getThreadListItem: () => InitializableThreadListItem | undefined;\n stopOnClientDestroy?: boolean;\n /**\n * An externally owned chat instance. State lives on the instance, so it\n * survives the hosting resource unmounting; construction options are read\n * from the instance.\n */\n chat?: Chat<UI_MESSAGE> | undefined;\n};\n\nconst useDynamicChatTransport = <UI_MESSAGE extends UIMessage = UIMessage>(\n transport: ChatTransport<UI_MESSAGE>,\n): ChatTransport<UI_MESSAGE> => {\n const transportRef = useRef<ChatTransport<UI_MESSAGE>>(transport);\n useEffect(() => {\n transportRef.current = transport;\n });\n const dynamicTransport = useMemo(\n () =>\n new Proxy(transportRef.current, {\n get(_, prop) {\n const res =\n transportRef.current[prop as keyof ChatTransport<UI_MESSAGE>];\n return typeof res === \"function\"\n ? res.bind(transportRef.current)\n : res;\n },\n }),\n [],\n );\n return dynamicTransport;\n};\n\nconst getResumableAdapter = <UI_MESSAGE extends UIMessage>(\n transport: ChatTransport<UI_MESSAGE>,\n): AssistantChatResumableOptions | undefined => {\n if (transport instanceof AssistantChatTransport) {\n return transport.getResumableAdapter();\n }\n const candidate = (transport as { getResumableAdapter?: () => unknown })\n .getResumableAdapter;\n if (typeof candidate !== \"function\") return undefined;\n return candidate.call(transport) as AssistantChatResumableOptions | undefined;\n};\n\nconst getNoPendingStreamId = () => null;\n\nconst resumedStreamIdsByStorage = new WeakMap<\n ResumableClientStorage,\n Set<string>\n>();\n\nconst getResumedStreamIds = (storage: ResumableClientStorage | undefined) => {\n if (!storage) return new Set<string>();\n let resumedStreamIds = resumedStreamIdsByStorage.get(storage);\n if (!resumedStreamIds) {\n resumedStreamIds = new Set();\n resumedStreamIdsByStorage.set(storage, resumedStreamIds);\n }\n return resumedStreamIds;\n};\n\n/**\n * Splits the combined options into the assistant-ui side and the `ChatInit`\n * remainder the AI SDK consumes, so external `Chat` construction forwards the\n * same fields `useChat` would.\n */\nexport const splitChatThreadOptions = <UI_MESSAGE extends UIMessage>(\n options: ChatThreadOptions<UI_MESSAGE> | undefined,\n) => {\n const {\n adapters,\n transport,\n toCreateMessage,\n isDisabled: _isDisabled,\n isSendDisabled: _isSendDisabled,\n unstable_capabilities: _unstable_capabilities,\n suggestions: _suggestions,\n onResume,\n onResumeToolCall,\n onResumeError,\n joinStrategy,\n ...chatInit\n } = options ?? {};\n // peel guard: any shared key left in `chatInit` collapses this to `never`\n true satisfies keyof typeof chatInit &\n keyof ExternalStoreSharedOptions extends never\n ? true\n : never;\n return {\n adapters,\n transport,\n toCreateMessage,\n onResume,\n onResumeToolCall,\n onResumeError,\n joinStrategy,\n chatInit,\n };\n};\n\nexport const useChatThread = <UI_MESSAGE extends UIMessage = UIMessage>(\n options: ChatThreadOptions<UI_MESSAGE> | undefined,\n env: ChatThreadEnvironment<UI_MESSAGE>,\n): AssistantRuntime => {\n const {\n adapters,\n transport: transportOptions,\n toCreateMessage,\n onResume,\n onResumeToolCall,\n onResumeError,\n joinStrategy,\n chatInit: chatOptions,\n } = splitChatThreadOptions(options);\n\n const {\n id,\n isMainThread,\n getThreadListItem,\n stopOnClientDestroy = false,\n chat: externalChat,\n } = env;\n\n const defaultTransport = useMemo(() => new AssistantChatTransport(), []);\n const sourceTransport = transportOptions ?? defaultTransport;\n const transport = useDynamicChatTransport(sourceTransport);\n\n const chat = useChat({\n ...chatOptions,\n id,\n transport,\n ...(externalChat !== undefined && { chat: externalChat }),\n });\n\n useResourceCleanup(stopOnClientDestroy, () => {\n void chat.stop().catch(() => {});\n });\n\n const runtime = useAISDKRuntime(chat, {\n adapters,\n ...pickExternalStoreSharedOptions(options ?? {}),\n ...(toCreateMessage && { toCreateMessage }),\n ...(onResume && { onResume }),\n ...(onResumeToolCall && { onResumeToolCall }),\n ...(joinStrategy && { joinStrategy }),\n });\n\n if (sourceTransport instanceof AssistantChatTransport) {\n sourceTransport.setRuntime(runtime);\n sourceTransport.__internal_setGetThreadListItem(getThreadListItem);\n }\n\n const subscribeToRuntime = useCallback(\n (callback: () => void) => runtime.thread.subscribe(callback),\n [runtime],\n );\n const getHistoryLoadingSnapshot = useCallback(\n () => runtime.thread.getState().isLoading,\n [runtime],\n );\n const isLoadingHistory = useSyncExternalStore(\n subscribeToRuntime,\n getHistoryLoadingSnapshot,\n getHistoryLoadingSnapshot,\n );\n\n const resumableStorage = useMemo(\n () => getResumableAdapter(sourceTransport)?.storage,\n [sourceTransport],\n );\n const subscribeToResumableStorage = useCallback(\n (callback: () => void) =>\n isMainThread\n ? (resumableStorage?.subscribe?.(callback, id) ?? (() => {}))\n : () => {},\n [id, isMainThread, resumableStorage],\n );\n const getPendingStreamId = useCallback(\n () => (isMainThread ? (resumableStorage?.getStreamId(id) ?? null) : null),\n [id, isMainThread, resumableStorage],\n );\n const pendingStreamId = useSyncExternalStore(\n subscribeToResumableStorage,\n getPendingStreamId,\n getNoPendingStreamId,\n );\n const isChatRunning =\n chat.status === \"submitted\" || chat.status === \"streaming\";\n\n const resumedStreamIds = useMemo(\n () => getResumedStreamIds(resumableStorage),\n [resumableStorage],\n );\n const onResumeErrorRef = useRef(onResumeError);\n useEffect(() => {\n onResumeErrorRef.current = onResumeError;\n });\n useEffect(() => {\n if (!pendingStreamId || resumedStreamIds.has(pendingStreamId)) {\n return;\n }\n if (isChatRunning) {\n resumedStreamIds.add(pendingStreamId);\n return;\n }\n if (isLoadingHistory) return;\n resumedStreamIds.add(pendingStreamId);\n chat.resumeStream().catch((err: unknown) => {\n console.warn(\"[assistant-ui] resumable: resume failed\", err);\n try {\n onResumeErrorRef.current?.(err);\n } catch (callbackError) {\n console.error(\n \"[assistant-ui] resumable: onResumeError callback failed\",\n callbackError,\n );\n } finally {\n if (resumableStorage?.getStreamId(id) === pendingStreamId) {\n resumableStorage.clear(id);\n }\n }\n });\n }, [\n chat,\n id,\n isChatRunning,\n isLoadingHistory,\n pendingStreamId,\n resumableStorage,\n resumedStreamIds,\n ]);\n\n return runtime;\n};\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { RefObject } from "react";
2
+ import { AssistantRuntime, ExportedMessageRepository, MessageFormatAdapter, MessageFormatRepository, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
3
+ //#region src/runtime/useExternalHistory.d.ts
4
+ declare const toExportedMessageRepository: <TMessage>(toThreadMessages: (messages: TMessage[]) => ThreadMessage[], messages: MessageFormatRepository<TMessage>) => ExportedMessageRepository;
5
+ declare const useExternalHistory: <TMessage>(runtimeRef: RefObject<AssistantRuntime>, historyAdapter: ThreadHistoryAdapter | undefined, toThreadMessages: (messages: TMessage[]) => ThreadMessage[], storageFormatAdapter: MessageFormatAdapter<TMessage, any>, onSetMessages: (messages: TMessage[]) => void) => {
6
+ isLoading: boolean;
7
+ deleteMessage: (messageId: string) => Promise<void>;
8
+ };
9
+ //#endregion
10
+ export { toExportedMessageRepository, useExternalHistory };
11
+ //# sourceMappingURL=useExternalHistory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useExternalHistory.d.ts","names":[],"sources":["../../src/runtime/useExternalHistory.ts"],"mappings":";;;cAsBa,8BAA+B,UAC1C,mBAAmB,UAAU,eAAe,iBAC5C,UAAU,wBAAwB,cACjC;cAoCU,qBAAsB,UACjC,YAAY,UAAU,mBACtB,gBAAgB,kCAChB,mBAAmB,UAAU,eAAe,iBAC5C,sBAAsB,qBAAqB,gBAC3C,gBAAgB,UAAU;;EAsUN,gBAAA,sBAAM"}
@@ -0,0 +1,278 @@
1
+ "use client";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
3
+ import { getExternalStoreMessages } from "@assistant-ui/core";
4
+ import { MessageRepository } from "@assistant-ui/core/internal";
5
+ import { useAui } from "@assistant-ui/store";
6
+ //#region src/runtime/useExternalHistory.ts
7
+ const toExportedMessageRepository = (toThreadMessages, messages) => {
8
+ const survivingIds = /* @__PURE__ */ new Set();
9
+ const survivors = messages.messages.flatMap((m) => {
10
+ const message = toThreadMessages([m.message])[0];
11
+ if (!message) {
12
+ console.warn("Skipping a stored message that could not be loaded.");
13
+ return [];
14
+ }
15
+ if (m.parentId && !survivingIds.has(m.parentId)) return [];
16
+ survivingIds.add(message.id);
17
+ return [{
18
+ ...m,
19
+ message
20
+ }];
21
+ });
22
+ return {
23
+ headId: messages.headId && survivingIds.has(messages.headId) ? messages.headId : null,
24
+ messages: survivors
25
+ };
26
+ };
27
+ const isAwaitingToolApproval = (message) => message.status?.type === "requires-action" && message.status.reason === "tool-calls";
28
+ const snapshotExternalMessages = (messages) => new Map(messages.map((message) => [message.id, [...getExternalStoreMessages(message)]]));
29
+ const useExternalHistory = (runtimeRef, historyAdapter, toThreadMessages, storageFormatAdapter, onSetMessages) => {
30
+ const loadedRef = useRef(false);
31
+ const [itemEpoch, setItemEpoch] = useState(0);
32
+ const aui = useAui();
33
+ const optionalThreadListItem = useCallback(() => aui.threadListItem.source ? aui.threadListItem : null, [aui]);
34
+ const [hasLoaded, setHasLoaded] = useState(false);
35
+ const historyIds = useRef(/* @__PURE__ */ new Set());
36
+ const persistedInnerIds = useRef(/* @__PURE__ */ new Set());
37
+ const deferredTelemetryIds = useRef(/* @__PURE__ */ new Set());
38
+ const persistedExternalMessages = useRef(/* @__PURE__ */ new Map());
39
+ const onSetMessagesRef = useRef(onSetMessages);
40
+ useEffect(() => {
41
+ onSetMessagesRef.current = onSetMessages;
42
+ });
43
+ const formatAdapter = useMemo(() => {
44
+ if (!historyAdapter) return void 0;
45
+ if (!historyAdapter.withFormat) throw new Error("useAISDKRuntime: ThreadHistoryAdapter is missing the required `withFormat` method.");
46
+ return historyAdapter.withFormat(storageFormatAdapter);
47
+ }, [historyAdapter, storageFormatAdapter]);
48
+ const isLoading = formatAdapter != null && !hasLoaded;
49
+ useEffect(() => {
50
+ if (!formatAdapter || loadedRef.current) return void 0;
51
+ const loadHistory = async () => {
52
+ try {
53
+ const repo = await formatAdapter.load();
54
+ if (repo && repo.messages.length > 0) {
55
+ for (const m of repo.messages) persistedInnerIds.current.add(storageFormatAdapter.getId(m.message));
56
+ const converted = toExportedMessageRepository(toThreadMessages, repo);
57
+ runtimeRef.current.thread.import(converted);
58
+ const tempRepo = new MessageRepository();
59
+ tempRepo.import(converted);
60
+ const messages = tempRepo.getMessages();
61
+ onSetMessagesRef.current(messages.flatMap(getExternalStoreMessages));
62
+ historyIds.current = /* @__PURE__ */ new Set();
63
+ for (const m_0 of converted.messages) {
64
+ historyIds.current.add(m_0.message.id);
65
+ if (isAwaitingToolApproval(m_0.message)) deferredTelemetryIds.current.add(m_0.message.id);
66
+ }
67
+ persistedExternalMessages.current = snapshotExternalMessages(converted.messages.map((m_1) => m_1.message));
68
+ }
69
+ } catch (error) {
70
+ console.error("Failed to load message history:", error);
71
+ } finally {
72
+ setHasLoaded(true);
73
+ }
74
+ };
75
+ formatAdapter.pin?.();
76
+ if (!optionalThreadListItem()?.getState().remoteId) {
77
+ setHasLoaded(true);
78
+ return aui.subscribe(() => {
79
+ if (optionalThreadListItem()?.getState().remoteId) setItemEpoch((n) => n + 1);
80
+ });
81
+ }
82
+ const threadState = runtimeRef.current.thread.getState();
83
+ if (threadState.isRunning || threadState.messages.length > 0) {
84
+ loadedRef.current = true;
85
+ setHasLoaded(true);
86
+ return;
87
+ }
88
+ loadedRef.current = true;
89
+ loadHistory();
90
+ }, [
91
+ formatAdapter,
92
+ toThreadMessages,
93
+ runtimeRef,
94
+ optionalThreadListItem,
95
+ aui,
96
+ itemEpoch,
97
+ storageFormatAdapter
98
+ ]);
99
+ const runStartRef = useRef(null);
100
+ const persistTimerRef = useRef(null);
101
+ const persistInFlightRef = useRef(Promise.resolve());
102
+ const stepBoundariesRef = useRef([]);
103
+ const wasRunningRef = useRef(false);
104
+ const toolCallCountRef = useRef(0);
105
+ useEffect(() => {
106
+ if (!formatAdapter) return;
107
+ const adapter = formatAdapter;
108
+ const unsubscribe = runtimeRef.current.thread.subscribe(() => {
109
+ const threadState_0 = runtimeRef.current.thread.getState();
110
+ const { isRunning } = threadState_0;
111
+ const wasRunning = wasRunningRef.current;
112
+ wasRunningRef.current = isRunning;
113
+ if (runStartRef.current != null) {
114
+ const lastMsg = threadState_0.messages.at(-1);
115
+ if (lastMsg?.role === "assistant") {
116
+ const currentToolCallCount = lastMsg.content.filter((p) => p.type === "tool-call").length;
117
+ while (toolCallCountRef.current < currentToolCallCount) {
118
+ stepBoundariesRef.current.push(Date.now() - runStartRef.current);
119
+ toolCallCountRef.current++;
120
+ }
121
+ }
122
+ }
123
+ if (isRunning) {
124
+ if (runStartRef.current == null) {
125
+ runStartRef.current = Date.now();
126
+ stepBoundariesRef.current = [];
127
+ toolCallCountRef.current = 0;
128
+ adapter.pin?.();
129
+ }
130
+ if (persistTimerRef.current) {
131
+ clearTimeout(persistTimerRef.current);
132
+ persistTimerRef.current = null;
133
+ }
134
+ return;
135
+ }
136
+ if (!wasRunning) return;
137
+ if (runStartRef.current != null) stepBoundariesRef.current.push(Date.now() - runStartRef.current);
138
+ if (persistTimerRef.current) clearTimeout(persistTimerRef.current);
139
+ persistTimerRef.current = setTimeout(() => {
140
+ persistSettled(false);
141
+ }, 0);
142
+ });
143
+ function persistSettled(ignoreRunning) {
144
+ persistTimerRef.current = null;
145
+ const latest = runtimeRef.current.thread.getState();
146
+ if (!ignoreRunning && latest.isRunning) return;
147
+ const boundaries = stepBoundariesRef.current;
148
+ const durationMs = boundaries.length > 0 ? boundaries.at(-1) : void 0;
149
+ if (boundaries.length === 1 && durationMs != null) {
150
+ const lastAssistant = latest.messages.findLast((m_2) => m_2.role === "assistant");
151
+ if (lastAssistant) {
152
+ const tcCount = lastAssistant.content.filter((p_0) => p_0.type === "tool-call").length;
153
+ if (tcCount > 0) {
154
+ const totalSteps = tcCount + 1;
155
+ const stepDur = durationMs / totalSteps;
156
+ boundaries.length = 0;
157
+ for (let i = 0; i < totalSteps; i++) boundaries.push(Math.round((i + 1) * stepDur));
158
+ }
159
+ }
160
+ }
161
+ const stepTimestamps = boundaries.length > 1 ? boundaries.map((endMs, i_0) => ({
162
+ start_ms: i_0 === 0 ? 0 : boundaries[i_0 - 1],
163
+ end_ms: endMs
164
+ })) : void 0;
165
+ runStartRef.current = null;
166
+ stepBoundariesRef.current = [];
167
+ const telemetryOptions = {
168
+ ...durationMs != null ? { durationMs } : void 0,
169
+ ...stepTimestamps != null ? { stepTimestamps } : void 0
170
+ };
171
+ persistInFlightRef.current = persistInFlightRef.current.then(async () => {
172
+ const changedRunMessageIds = /* @__PURE__ */ new Set();
173
+ for (const message of latest.messages) {
174
+ const externalMessages = getExternalStoreMessages(message);
175
+ const previous = persistedExternalMessages.current.get(message.id);
176
+ if (previous === void 0 || previous.length !== externalMessages.length || externalMessages.some((item, index) => item !== previous[index])) changedRunMessageIds.add(message.id);
177
+ }
178
+ const { messages: messages_0 } = latest;
179
+ let lastInnerMessageId = null;
180
+ const failedUpdateIds = /* @__PURE__ */ new Set();
181
+ const getLastInnerId = (msgs) => msgs.length > 0 ? storageFormatAdapter.getId(msgs.at(-1)) : null;
182
+ const toBatchItems = (msgs_0) => msgs_0.map((msg, idx) => ({
183
+ parentId: idx === 0 ? lastInnerMessageId : storageFormatAdapter.getId(msgs_0[idx - 1]),
184
+ message: msg
185
+ }));
186
+ for (const message_0 of messages_0) {
187
+ const innerMessages = getExternalStoreMessages(message_0);
188
+ const isTerminal = message_0.status === void 0 || message_0.status.type === "complete" || message_0.status.type === "incomplete";
189
+ const isAwaitingToolCalls = isAwaitingToolApproval(message_0);
190
+ if (!(isTerminal || isAwaitingToolCalls && adapter.update !== void 0)) {
191
+ lastInnerMessageId = getLastInnerId(innerMessages) ?? lastInnerMessageId;
192
+ continue;
193
+ }
194
+ const isPersistedMessage = historyIds.current.has(message_0.id);
195
+ if (isPersistedMessage && !changedRunMessageIds.has(message_0.id)) {
196
+ lastInnerMessageId = getLastInnerId(innerMessages) ?? lastInnerMessageId;
197
+ continue;
198
+ }
199
+ if (!isPersistedMessage) {
200
+ historyIds.current.add(message_0.id);
201
+ deferredTelemetryIds.current.add(message_0.id);
202
+ }
203
+ const batchItems = toBatchItems(innerMessages);
204
+ for (const item_0 of batchItems) {
205
+ const innerId = storageFormatAdapter.getId(item_0.message);
206
+ if (!persistedInnerIds.current.has(innerId)) {
207
+ await adapter.append(item_0);
208
+ persistedInnerIds.current.add(innerId);
209
+ } else if (durationMs !== void 0) try {
210
+ await adapter.update?.(item_0, innerId);
211
+ } catch {
212
+ failedUpdateIds.add(message_0.id);
213
+ }
214
+ }
215
+ lastInnerMessageId = getLastInnerId(innerMessages) ?? lastInnerMessageId;
216
+ if (deferredTelemetryIds.current.has(message_0.id) && isTerminal) {
217
+ deferredTelemetryIds.current.delete(message_0.id);
218
+ adapter.reportTelemetry?.(batchItems, telemetryOptions);
219
+ }
220
+ }
221
+ const nextSnapshot = snapshotExternalMessages(latest.messages);
222
+ for (const id of failedUpdateIds) nextSnapshot.delete(id);
223
+ persistedExternalMessages.current = nextSnapshot;
224
+ }).catch((error_0) => {
225
+ console.error("Failed to persist message history:", error_0);
226
+ });
227
+ }
228
+ return () => {
229
+ unsubscribe();
230
+ if (persistTimerRef.current) {
231
+ clearTimeout(persistTimerRef.current);
232
+ persistTimerRef.current = null;
233
+ persistSettled(false);
234
+ }
235
+ };
236
+ }, [
237
+ formatAdapter,
238
+ storageFormatAdapter,
239
+ runtimeRef
240
+ ]);
241
+ return {
242
+ isLoading,
243
+ deleteMessage: useCallback(async (messageId) => {
244
+ const deleteMessages = formatAdapter?.delete?.bind(formatAdapter);
245
+ if (!deleteMessages) return;
246
+ const messages_1 = runtimeRef.current.thread.getState().messages;
247
+ const messageIndex = messages_1.findIndex((m_3) => m_3.id === messageId);
248
+ if (messageIndex === -1) return;
249
+ const previousInnerMessages = messages_1.slice(0, messageIndex).flatMap(getExternalStoreMessages);
250
+ let parentId = previousInnerMessages.at(-1) ? storageFormatAdapter.getId(previousInnerMessages.at(-1)) : null;
251
+ const itemsToDelete = getExternalStoreMessages(messages_1[messageIndex]).map((message_1) => {
252
+ const item_1 = {
253
+ parentId,
254
+ message: message_1
255
+ };
256
+ parentId = storageFormatAdapter.getId(message_1);
257
+ return item_1;
258
+ });
259
+ const deletion = persistInFlightRef.current.then(async () => {
260
+ await deleteMessages(itemsToDelete);
261
+ historyIds.current.delete(messageId);
262
+ deferredTelemetryIds.current.delete(messageId);
263
+ persistedExternalMessages.current.delete(messageId);
264
+ for (const item_2 of itemsToDelete) persistedInnerIds.current.delete(storageFormatAdapter.getId(item_2.message));
265
+ });
266
+ persistInFlightRef.current = deletion.catch(() => {});
267
+ await deletion;
268
+ }, [
269
+ formatAdapter,
270
+ runtimeRef,
271
+ storageFormatAdapter
272
+ ])
273
+ };
274
+ };
275
+ //#endregion
276
+ export { toExportedMessageRepository, useExternalHistory };
277
+
278
+ //# sourceMappingURL=useExternalHistory.js.map