@tangle-network/ui 1.0.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 (220) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -0
  4. package/dist/active-sessions-store-CeOmXgv5.d.ts +85 -0
  5. package/dist/artifact-pane-DvJyPWV4.d.ts +24 -0
  6. package/dist/auth.d.ts +74 -0
  7. package/dist/auth.js +15 -0
  8. package/dist/button-CMQuQEW_.d.ts +17 -0
  9. package/dist/chat.d.ts +232 -0
  10. package/dist/chat.js +30 -0
  11. package/dist/chunk-2NFQRQOD.js +1009 -0
  12. package/dist/chunk-2VH6PUXD.js +186 -0
  13. package/dist/chunk-34A66VBG.js +214 -0
  14. package/dist/chunk-3OI2QKFD.js +0 -0
  15. package/dist/chunk-4CLN43XT.js +45 -0
  16. package/dist/chunk-54SQQMMM.js +156 -0
  17. package/dist/chunk-5Z5ZYMOJ.js +0 -0
  18. package/dist/chunk-66BNMOVT.js +167 -0
  19. package/dist/chunk-6BGQA4BQ.js +0 -0
  20. package/dist/chunk-7UO2ZMRQ.js +133 -0
  21. package/dist/chunk-BX6AQMUS.js +183 -0
  22. package/dist/chunk-CD53GZOM.js +59 -0
  23. package/dist/chunk-CSAIKY36.js +54 -0
  24. package/dist/chunk-EEE55AVS.js +1201 -0
  25. package/dist/chunk-GYPQXTJU.js +230 -0
  26. package/dist/chunk-HFL6R6IF.js +37 -0
  27. package/dist/chunk-HJKCSXCH.js +737 -0
  28. package/dist/chunk-LISXUB4D.js +1222 -0
  29. package/dist/chunk-LQS34IGP.js +0 -0
  30. package/dist/chunk-MKTSMWVD.js +109 -0
  31. package/dist/chunk-NKDZ7GZE.js +192 -0
  32. package/dist/chunk-OEX7NZE3.js +321 -0
  33. package/dist/chunk-Q56BYXQF.js +61 -0
  34. package/dist/chunk-Q7EIIWTC.js +0 -0
  35. package/dist/chunk-REJESC5U.js +117 -0
  36. package/dist/chunk-RQGKSCEZ.js +0 -0
  37. package/dist/chunk-RQHJBTEU.js +10 -0
  38. package/dist/chunk-TMFOPHHN.js +299 -0
  39. package/dist/chunk-XGKULLYE.js +40 -0
  40. package/dist/chunk-XIHMJ7ZQ.js +614 -0
  41. package/dist/chunk-YJ2G3XO5.js +1048 -0
  42. package/dist/chunk-YNN4O57I.js +754 -0
  43. package/dist/code-block-DjXf8eOG.d.ts +19 -0
  44. package/dist/document-editor-pane-A5LT5H4N.js +12 -0
  45. package/dist/document-editor-pane-DyDEX_Zm.d.ts +124 -0
  46. package/dist/editor.d.ts +120 -0
  47. package/dist/editor.js +34 -0
  48. package/dist/files.d.ts +175 -0
  49. package/dist/files.js +20 -0
  50. package/dist/hooks.d.ts +56 -0
  51. package/dist/hooks.js +41 -0
  52. package/dist/index.d.ts +43 -0
  53. package/dist/index.js +446 -0
  54. package/dist/markdown.d.ts +15 -0
  55. package/dist/markdown.js +14 -0
  56. package/dist/message-BHWbxBtT.d.ts +15 -0
  57. package/dist/openui.d.ts +115 -0
  58. package/dist/openui.js +12 -0
  59. package/dist/parts-dj7AcUg0.d.ts +36 -0
  60. package/dist/primitives.d.ts +332 -0
  61. package/dist/primitives.js +191 -0
  62. package/dist/run-PfLmDAox.d.ts +41 -0
  63. package/dist/run.d.ts +69 -0
  64. package/dist/run.js +36 -0
  65. package/dist/sdk-hooks.d.ts +285 -0
  66. package/dist/sdk-hooks.js +31 -0
  67. package/dist/stores.d.ts +17 -0
  68. package/dist/stores.js +76 -0
  69. package/dist/tool-call-feed-Bs3MyQMT.d.ts +68 -0
  70. package/dist/tool-display-z4JcDmMQ.d.ts +32 -0
  71. package/dist/tool-previews.d.ts +48 -0
  72. package/dist/tool-previews.js +21 -0
  73. package/dist/types.d.ts +19 -0
  74. package/dist/types.js +1 -0
  75. package/dist/utils.d.ts +45 -0
  76. package/dist/utils.js +32 -0
  77. package/package.json +193 -0
  78. package/src/auth/auth.tsx +228 -0
  79. package/src/auth/index.ts +13 -0
  80. package/src/auth/login-layout.tsx +46 -0
  81. package/src/chat/agent-timeline.stories.tsx +429 -0
  82. package/src/chat/agent-timeline.tsx +360 -0
  83. package/src/chat/chat-container.tsx +486 -0
  84. package/src/chat/chat-input.stories.tsx +142 -0
  85. package/src/chat/chat-input.tsx +389 -0
  86. package/src/chat/chat-message.stories.tsx +237 -0
  87. package/src/chat/chat-message.tsx +129 -0
  88. package/src/chat/index.ts +18 -0
  89. package/src/chat/message-list.stories.tsx +336 -0
  90. package/src/chat/message-list.tsx +79 -0
  91. package/src/chat/thinking-indicator.stories.tsx +56 -0
  92. package/src/chat/thinking-indicator.tsx +30 -0
  93. package/src/chat/user-message.stories.tsx +92 -0
  94. package/src/chat/user-message.tsx +43 -0
  95. package/src/editor/document-editor-pane.tsx +351 -0
  96. package/src/editor/editor-provider.tsx +428 -0
  97. package/src/editor/editor-toolbar.tsx +130 -0
  98. package/src/editor/index.ts +31 -0
  99. package/src/editor/markdown-conversion.ts +21 -0
  100. package/src/editor/markdown-document-editor.tsx +137 -0
  101. package/src/editor/tiptap-editor.tsx +331 -0
  102. package/src/editor/use-editor.ts +221 -0
  103. package/src/files/file-artifact-pane.tsx +183 -0
  104. package/src/files/file-preview.tsx +342 -0
  105. package/src/files/file-tabs.tsx +71 -0
  106. package/src/files/file-tree.tsx +258 -0
  107. package/src/files/index.ts +17 -0
  108. package/src/files/rich-file-tree.stories.tsx +104 -0
  109. package/src/files/rich-file-tree.test.tsx +42 -0
  110. package/src/files/rich-file-tree.tsx +232 -0
  111. package/src/hooks/index.ts +10 -0
  112. package/src/hooks/use-auth.ts +153 -0
  113. package/src/hooks/use-auto-scroll.ts +59 -0
  114. package/src/hooks/use-dropdown-menu.ts +40 -0
  115. package/src/hooks/use-live-time.test.tsx +40 -0
  116. package/src/hooks/use-live-time.ts +27 -0
  117. package/src/hooks/use-realtime-session.ts +319 -0
  118. package/src/hooks/use-run-collapse-state.ts +25 -0
  119. package/src/hooks/use-run-groups.ts +111 -0
  120. package/src/hooks/use-sdk-session.ts +575 -0
  121. package/src/hooks/use-sse-stream.ts +475 -0
  122. package/src/hooks/use-tool-call-stream.ts +96 -0
  123. package/src/index.ts +14 -0
  124. package/src/lib/utils.ts +6 -0
  125. package/src/markdown/code-block.tsx +198 -0
  126. package/src/markdown/index.ts +2 -0
  127. package/src/markdown/markdown.stories.tsx +190 -0
  128. package/src/markdown/markdown.tsx +62 -0
  129. package/src/openui/index.ts +20 -0
  130. package/src/openui/openui-artifact-renderer.tsx +542 -0
  131. package/src/primitives/artifact-pane.tsx +91 -0
  132. package/src/primitives/avatar.stories.tsx +95 -0
  133. package/src/primitives/avatar.tsx +47 -0
  134. package/src/primitives/badge.stories.tsx +57 -0
  135. package/src/primitives/badge.tsx +97 -0
  136. package/src/primitives/button.stories.tsx +48 -0
  137. package/src/primitives/button.tsx +115 -0
  138. package/src/primitives/card.stories.tsx +53 -0
  139. package/src/primitives/card.tsx +98 -0
  140. package/src/primitives/code-block.stories.tsx +115 -0
  141. package/src/primitives/code-block.tsx +22 -0
  142. package/src/primitives/design-tokens.stories.tsx +162 -0
  143. package/src/primitives/dialog.stories.tsx +176 -0
  144. package/src/primitives/dialog.tsx +137 -0
  145. package/src/primitives/drop-zone.stories.tsx +123 -0
  146. package/src/primitives/drop-zone.tsx +131 -0
  147. package/src/primitives/dropdown-menu.stories.tsx +122 -0
  148. package/src/primitives/dropdown-menu.tsx +214 -0
  149. package/src/primitives/empty-state.stories.tsx +81 -0
  150. package/src/primitives/empty-state.tsx +40 -0
  151. package/src/primitives/index.ts +118 -0
  152. package/src/primitives/input.stories.tsx +113 -0
  153. package/src/primitives/input.tsx +136 -0
  154. package/src/primitives/label.stories.tsx +84 -0
  155. package/src/primitives/label.tsx +24 -0
  156. package/src/primitives/progress.stories.tsx +93 -0
  157. package/src/primitives/progress.tsx +50 -0
  158. package/src/primitives/segmented-control.test.tsx +328 -0
  159. package/src/primitives/segmented-control.tsx +154 -0
  160. package/src/primitives/select.stories.tsx +164 -0
  161. package/src/primitives/select.tsx +158 -0
  162. package/src/primitives/sidebar-drop-zone.stories.tsx +100 -0
  163. package/src/primitives/sidebar-drop-zone.tsx +149 -0
  164. package/src/primitives/skeleton.stories.tsx +79 -0
  165. package/src/primitives/skeleton.tsx +55 -0
  166. package/src/primitives/stat-card.stories.tsx +137 -0
  167. package/src/primitives/stat-card.tsx +97 -0
  168. package/src/primitives/switch.stories.tsx +85 -0
  169. package/src/primitives/switch.tsx +28 -0
  170. package/src/primitives/table.stories.tsx +170 -0
  171. package/src/primitives/table.tsx +116 -0
  172. package/src/primitives/tabs.stories.tsx +180 -0
  173. package/src/primitives/tabs.tsx +71 -0
  174. package/src/primitives/terminal-display.stories.tsx +191 -0
  175. package/src/primitives/terminal-display.tsx +189 -0
  176. package/src/primitives/theme-toggle.stories.tsx +32 -0
  177. package/src/primitives/theme-toggle.tsx +96 -0
  178. package/src/primitives/toast.stories.tsx +155 -0
  179. package/src/primitives/toast.tsx +190 -0
  180. package/src/primitives/upload-progress.stories.tsx +120 -0
  181. package/src/primitives/upload-progress.tsx +110 -0
  182. package/src/run/expanded-tool-detail.stories.tsx +182 -0
  183. package/src/run/expanded-tool-detail.tsx +186 -0
  184. package/src/run/index.ts +13 -0
  185. package/src/run/inline-thinking-item.stories.tsx +136 -0
  186. package/src/run/inline-thinking-item.tsx +120 -0
  187. package/src/run/inline-tool-item.stories.tsx +222 -0
  188. package/src/run/inline-tool-item.tsx +190 -0
  189. package/src/run/run-group.stories.tsx +322 -0
  190. package/src/run/run-group.tsx +569 -0
  191. package/src/run/run-item-primitives.tsx +17 -0
  192. package/src/run/tool-call-feed.stories.tsx +294 -0
  193. package/src/run/tool-call-feed.tsx +192 -0
  194. package/src/run/tool-call-step.stories.tsx +198 -0
  195. package/src/run/tool-call-step.tsx +240 -0
  196. package/src/sdk-hooks.ts +38 -0
  197. package/src/stores/active-sessions-store.ts +455 -0
  198. package/src/stores/chat-store.ts +43 -0
  199. package/src/stores/index.ts +2 -0
  200. package/src/tool-previews/command-preview.tsx +116 -0
  201. package/src/tool-previews/diff-preview.tsx +85 -0
  202. package/src/tool-previews/glob-results-preview.tsx +98 -0
  203. package/src/tool-previews/grep-results-preview.tsx +157 -0
  204. package/src/tool-previews/index.ts +22 -0
  205. package/src/tool-previews/preview-primitives.tsx +84 -0
  206. package/src/tool-previews/question-preview.tsx +101 -0
  207. package/src/tool-previews/web-search-preview.tsx +117 -0
  208. package/src/tool-previews/write-file-preview.tsx +80 -0
  209. package/src/types/branding.ts +11 -0
  210. package/src/types/index.ts +5 -0
  211. package/src/types/message.ts +13 -0
  212. package/src/types/parts.ts +51 -0
  213. package/src/types/run.ts +56 -0
  214. package/src/types/tool-display.ts +41 -0
  215. package/src/utils/copy-text.ts +30 -0
  216. package/src/utils/format.test.ts +43 -0
  217. package/src/utils/format.ts +56 -0
  218. package/src/utils/index.ts +10 -0
  219. package/src/utils/time-ago.ts +9 -0
  220. package/src/utils/tool-display.ts +238 -0
@@ -0,0 +1,1048 @@
1
+ import {
2
+ bumpActiveSessionActivity,
3
+ registerActiveSession,
4
+ setActiveSessionAttention,
5
+ setActiveSessionConnection,
6
+ setActiveSessionError,
7
+ setActiveSessionRunning,
8
+ setForegroundActiveSession,
9
+ unregisterActiveSession,
10
+ updateActiveSessionMeta
11
+ } from "./chunk-OEX7NZE3.js";
12
+ import {
13
+ parseToolEvent
14
+ } from "./chunk-7UO2ZMRQ.js";
15
+
16
+ // src/hooks/use-dropdown-menu.ts
17
+ import { useEffect, useRef, useState } from "react";
18
+ function useDropdownMenu(options) {
19
+ const closeOnEsc = options?.closeOnEsc ?? true;
20
+ const [open, setOpen] = useState(false);
21
+ const ref = useRef(null);
22
+ useEffect(() => {
23
+ function handleClick(e) {
24
+ if (ref.current && !ref.current.contains(e.target)) {
25
+ setOpen(false);
26
+ }
27
+ }
28
+ if (open) {
29
+ document.addEventListener("mousedown", handleClick);
30
+ }
31
+ return () => document.removeEventListener("mousedown", handleClick);
32
+ }, [open]);
33
+ useEffect(() => {
34
+ if (!open || !closeOnEsc) return;
35
+ function handleKey(e) {
36
+ if (e.key === "Escape") setOpen(false);
37
+ }
38
+ document.addEventListener("keydown", handleKey);
39
+ return () => document.removeEventListener("keydown", handleKey);
40
+ }, [open, closeOnEsc]);
41
+ return {
42
+ open,
43
+ setOpen,
44
+ ref,
45
+ toggle: () => setOpen((prev) => !prev),
46
+ close: () => setOpen(false)
47
+ };
48
+ }
49
+
50
+ // src/hooks/use-tool-call-stream.ts
51
+ import { useState as useState2, useCallback, useRef as useRef2 } from "react";
52
+ function useToolCallStream() {
53
+ const [segments, setSegments] = useState2([]);
54
+ const pendingToolsRef = useRef2(/* @__PURE__ */ new Map());
55
+ const lastSegmentKindRef = useRef2(null);
56
+ const pushText = useCallback((delta) => {
57
+ setSegments((prev) => {
58
+ const last = prev[prev.length - 1];
59
+ if (last && last.kind === "text") {
60
+ return [...prev.slice(0, -1), { kind: "text", content: last.content + delta }];
61
+ }
62
+ return [...prev, { kind: "text", content: delta }];
63
+ });
64
+ lastSegmentKindRef.current = "text";
65
+ }, []);
66
+ const pushEvent = useCallback((event) => {
67
+ const toolCall = parseToolEvent(event);
68
+ if (!toolCall) return;
69
+ if (event.type === "tool.invocation" || event.type === "tool_use") {
70
+ pendingToolsRef.current.set(toolCall.id, toolCall);
71
+ setSegments((prev) => [
72
+ ...prev,
73
+ { kind: "tool_call", call: { ...toolCall, status: "running" } }
74
+ ]);
75
+ lastSegmentKindRef.current = "tool";
76
+ }
77
+ }, []);
78
+ const completeToolCall = useCallback(
79
+ (id, result) => {
80
+ const pending = pendingToolsRef.current.get(id);
81
+ if (pending) {
82
+ pending.status = result.error ? "error" : "success";
83
+ pending.output = result.output || result.error;
84
+ pending.duration = result.duration;
85
+ pendingToolsRef.current.delete(id);
86
+ }
87
+ setSegments(
88
+ (prev) => prev.map((seg) => {
89
+ if (seg.kind === "tool_call" && seg.call.id === id) {
90
+ return {
91
+ kind: "tool_call",
92
+ call: {
93
+ ...seg.call,
94
+ status: result.error ? "error" : "success",
95
+ output: result.output || result.error,
96
+ duration: result.duration
97
+ }
98
+ };
99
+ }
100
+ return seg;
101
+ })
102
+ );
103
+ },
104
+ []
105
+ );
106
+ const reset = useCallback(() => {
107
+ setSegments([]);
108
+ pendingToolsRef.current.clear();
109
+ lastSegmentKindRef.current = null;
110
+ }, []);
111
+ return { segments, pushEvent, pushText, completeToolCall, reset };
112
+ }
113
+
114
+ // src/hooks/use-realtime-session.ts
115
+ import { createElement, useEffect as useEffect2, useMemo, useRef as useRef3, useState as useState3 } from "react";
116
+ function parseEvent(message) {
117
+ try {
118
+ const parsed = JSON.parse(message);
119
+ return typeof parsed?.type === "string" ? parsed : null;
120
+ } catch {
121
+ return null;
122
+ }
123
+ }
124
+ function eventTimestamp(event) {
125
+ const rootTimestamp = event.timestamp;
126
+ if (typeof rootTimestamp === "number" && Number.isFinite(rootTimestamp)) {
127
+ return rootTimestamp;
128
+ }
129
+ const raw = event.data?.timestamp ?? event.data?.ts ?? event.data?.time ?? event.data?.eventAt;
130
+ if (typeof raw === "number" && Number.isFinite(raw)) return raw;
131
+ return null;
132
+ }
133
+ function resolveErrorMessage(event) {
134
+ const message = event.data?.message;
135
+ return typeof message === "string" && message.length > 0 ? message : null;
136
+ }
137
+ function updateStoreFromEvent(sessionId, event) {
138
+ const lastEventAt = eventTimestamp(event) ?? Date.now();
139
+ bumpActiveSessionActivity(sessionId, { lastEventAt });
140
+ if (event.type === "session.run.started") {
141
+ setActiveSessionRunning(sessionId, true, { lastEventAt });
142
+ return;
143
+ }
144
+ if (event.type === "session.run.completed" || event.type === "done" || event.type === "result") {
145
+ setActiveSessionRunning(sessionId, false, { lastEventAt });
146
+ return;
147
+ }
148
+ if (event.type === "session.attention") {
149
+ setActiveSessionAttention(sessionId, true, { lastEventAt });
150
+ return;
151
+ }
152
+ if (event.type === "error" || event.type === "session.run.failed") {
153
+ setActiveSessionError(sessionId, resolveErrorMessage(event) ?? "Session error");
154
+ return;
155
+ }
156
+ }
157
+ function useRealtimeSession({
158
+ sessionId,
159
+ projectId = null,
160
+ projectLabel,
161
+ title,
162
+ href,
163
+ metadata,
164
+ connectUrl,
165
+ enabled = true,
166
+ foreground = true,
167
+ keepRegistered = true,
168
+ reconnect = true,
169
+ reconnectIntervalMs = 1500,
170
+ maxReconnectAttempts = Infinity,
171
+ transportMode = "websocket",
172
+ onEvent,
173
+ onOpen,
174
+ onClose,
175
+ onError
176
+ }) {
177
+ const [connectionState, setConnectionState] = useState3("disconnected");
178
+ const [lastError, setLastError] = useState3(null);
179
+ const [reconnectAttempts, setReconnectAttempts] = useState3(0);
180
+ const socketRef = useRef3(null);
181
+ const timerRef = useRef3(null);
182
+ const reconnectAttemptsRef = useRef3(0);
183
+ const shouldReconnectRef = useRef3(true);
184
+ const registration = useMemo(
185
+ () => ({
186
+ sessionId,
187
+ projectId,
188
+ projectLabel,
189
+ title,
190
+ href,
191
+ metadata
192
+ }),
193
+ [href, metadata, projectId, projectLabel, sessionId, title]
194
+ );
195
+ useEffect2(() => {
196
+ if (!sessionId || !keepRegistered) return void 0;
197
+ registerActiveSession(registration);
198
+ return () => {
199
+ unregisterActiveSession(sessionId);
200
+ };
201
+ }, [keepRegistered, registration, sessionId]);
202
+ useEffect2(() => {
203
+ if (!sessionId || !keepRegistered) return;
204
+ updateActiveSessionMeta(sessionId, {
205
+ projectId,
206
+ projectLabel,
207
+ title,
208
+ href,
209
+ metadata
210
+ });
211
+ }, [href, keepRegistered, metadata, projectId, projectLabel, sessionId, title]);
212
+ useEffect2(() => {
213
+ if (!sessionId || !foreground) return void 0;
214
+ setForegroundActiveSession(sessionId);
215
+ return () => {
216
+ setForegroundActiveSession(null);
217
+ };
218
+ }, [foreground, sessionId]);
219
+ useEffect2(() => {
220
+ if (!enabled || !sessionId || !connectUrl || typeof window === "undefined") {
221
+ setConnectionState("disconnected");
222
+ if (sessionId) {
223
+ setActiveSessionConnection(sessionId, {
224
+ connectionState: "disconnected",
225
+ reconnectState: "idle",
226
+ transportMode
227
+ });
228
+ }
229
+ return void 0;
230
+ }
231
+ shouldReconnectRef.current = true;
232
+ const clearReconnectTimer = () => {
233
+ if (timerRef.current != null) {
234
+ window.clearTimeout(timerRef.current);
235
+ timerRef.current = null;
236
+ }
237
+ };
238
+ const connect = () => {
239
+ clearReconnectTimer();
240
+ setConnectionState(reconnectAttemptsRef.current > 0 ? "reconnecting" : "connecting");
241
+ setActiveSessionConnection(sessionId, {
242
+ connectionState: reconnectAttemptsRef.current > 0 ? "reconnecting" : "connecting",
243
+ reconnectState: reconnectAttemptsRef.current > 0 ? "reconnecting" : "idle",
244
+ transportMode,
245
+ lastError: null
246
+ });
247
+ const socket = new window.WebSocket(connectUrl);
248
+ socketRef.current = socket;
249
+ socket.onopen = () => {
250
+ reconnectAttemptsRef.current = 0;
251
+ setReconnectAttempts(0);
252
+ setLastError(null);
253
+ setConnectionState("connected");
254
+ registerActiveSession(registration);
255
+ setActiveSessionConnection(sessionId, {
256
+ connectionState: "connected",
257
+ reconnectState: "idle",
258
+ transportMode,
259
+ lastError: null,
260
+ lastEventAt: Date.now()
261
+ });
262
+ onOpen?.();
263
+ };
264
+ socket.onmessage = (message) => {
265
+ const event = parseEvent(message.data);
266
+ if (!event) return;
267
+ registerActiveSession(registration);
268
+ updateStoreFromEvent(sessionId, event);
269
+ onEvent?.(event);
270
+ };
271
+ socket.onerror = () => {
272
+ const nextError = "Realtime session connection error";
273
+ setLastError(nextError);
274
+ setConnectionState("error");
275
+ setActiveSessionConnection(sessionId, {
276
+ connectionState: "error",
277
+ reconnectState: reconnect ? "reconnecting" : "failed",
278
+ transportMode,
279
+ lastError: nextError
280
+ });
281
+ onError?.(new Error(nextError));
282
+ };
283
+ socket.onclose = () => {
284
+ socketRef.current = null;
285
+ onClose?.();
286
+ if (!shouldReconnectRef.current || !reconnect) {
287
+ setConnectionState("disconnected");
288
+ setActiveSessionConnection(sessionId, {
289
+ connectionState: "disconnected",
290
+ reconnectState: "idle",
291
+ transportMode
292
+ });
293
+ return;
294
+ }
295
+ reconnectAttemptsRef.current += 1;
296
+ setReconnectAttempts(reconnectAttemptsRef.current);
297
+ if (reconnectAttemptsRef.current > maxReconnectAttempts) {
298
+ setConnectionState("error");
299
+ setActiveSessionConnection(sessionId, {
300
+ connectionState: "error",
301
+ reconnectState: "failed",
302
+ transportMode,
303
+ lastError: "Unable to reconnect to realtime session"
304
+ });
305
+ return;
306
+ }
307
+ setConnectionState("reconnecting");
308
+ timerRef.current = window.setTimeout(connect, reconnectIntervalMs);
309
+ };
310
+ };
311
+ connect();
312
+ return () => {
313
+ shouldReconnectRef.current = false;
314
+ clearReconnectTimer();
315
+ const socket = socketRef.current;
316
+ socketRef.current = null;
317
+ if (socket && socket.readyState < WebSocket.CLOSING) {
318
+ socket.close();
319
+ }
320
+ };
321
+ }, [
322
+ connectUrl,
323
+ enabled,
324
+ maxReconnectAttempts,
325
+ onClose,
326
+ onError,
327
+ onEvent,
328
+ onOpen,
329
+ reconnect,
330
+ reconnectIntervalMs,
331
+ registration,
332
+ sessionId,
333
+ transportMode
334
+ ]);
335
+ return {
336
+ connectionState,
337
+ lastError,
338
+ reconnectAttempts,
339
+ isConnected: connectionState === "connected"
340
+ };
341
+ }
342
+ function RealtimeSessionRegistryItem(props) {
343
+ useRealtimeSession({
344
+ ...props,
345
+ foreground: props.foreground ?? false
346
+ });
347
+ return null;
348
+ }
349
+ function RealtimeSessionRegistry({ sessions }) {
350
+ return sessions.map(
351
+ (session) => createElement(RealtimeSessionRegistryItem, {
352
+ key: session.key ?? session.sessionId,
353
+ ...session
354
+ })
355
+ );
356
+ }
357
+
358
+ // src/hooks/use-sdk-session.ts
359
+ import { useCallback as useCallback2, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
360
+ function uid() {
361
+ if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.randomUUID === "function") {
362
+ try {
363
+ return globalThis.crypto.randomUUID();
364
+ } catch {
365
+ }
366
+ }
367
+ return `sdk-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
368
+ }
369
+ function toMillis(value) {
370
+ if (value == null) return void 0;
371
+ if (typeof value === "number") return value;
372
+ if (value instanceof Date) return value.getTime();
373
+ const millis = new Date(value).getTime();
374
+ return Number.isFinite(millis) ? millis : void 0;
375
+ }
376
+ function asRecord(value) {
377
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
378
+ }
379
+ function asString(value) {
380
+ return typeof value === "string" && value.length > 0 ? value : void 0;
381
+ }
382
+ function textPartsFromContent(content, attachments) {
383
+ const attachmentText = attachments?.length ? `
384
+
385
+ Attachments:
386
+ ${attachments.map((attachment) => `- ${attachment.name}`).join("\n")}` : "";
387
+ const text = `${content}${attachmentText}`.trim();
388
+ return text ? [{ type: "text", text }] : [];
389
+ }
390
+ function normalizeTime(value) {
391
+ const record = asRecord(value);
392
+ if (!record) return void 0;
393
+ const start = Number(record.start ?? record.startedAt ?? record.started_at);
394
+ const end = Number(record.end ?? record.completedAt ?? record.completed_at);
395
+ if (!Number.isFinite(start) && !Number.isFinite(end)) {
396
+ return void 0;
397
+ }
398
+ return {
399
+ start: Number.isFinite(start) ? start : void 0,
400
+ end: Number.isFinite(end) ? end : void 0
401
+ };
402
+ }
403
+ function normalizeStatus(value, output, error) {
404
+ if (value === "pending" || value === "running" || value === "completed" || value === "error") {
405
+ return value;
406
+ }
407
+ if (typeof error === "string" && error.length > 0) {
408
+ return "error";
409
+ }
410
+ if (output !== void 0) {
411
+ return "completed";
412
+ }
413
+ return "running";
414
+ }
415
+ function resolveToolIdentity(rawPart) {
416
+ return String(
417
+ rawPart.id ?? rawPart.callID ?? rawPart.callId ?? rawPart.toolUseId ?? rawPart.toolCallId ?? rawPart.tool ?? rawPart.name ?? "tool"
418
+ );
419
+ }
420
+ function normalizePart(rawPart) {
421
+ const type = String(rawPart.type ?? "");
422
+ if (type === "text") {
423
+ return {
424
+ type: "text",
425
+ text: asString(rawPart.text) ?? asString(rawPart.content) ?? ""
426
+ };
427
+ }
428
+ if (type === "reasoning") {
429
+ return {
430
+ type: "reasoning",
431
+ text: asString(rawPart.text) ?? asString(rawPart.content) ?? "",
432
+ time: normalizeTime(rawPart.time)
433
+ };
434
+ }
435
+ if (type === "tool") {
436
+ const stateRecord = asRecord(rawPart.state);
437
+ const input = stateRecord?.input ?? rawPart.input;
438
+ const output = stateRecord?.output ?? rawPart.output;
439
+ const error = stateRecord?.error ?? rawPart.error;
440
+ return {
441
+ type: "tool",
442
+ id: resolveToolIdentity(rawPart),
443
+ tool: String(rawPart.tool ?? rawPart.name ?? "tool"),
444
+ callID: rawPart.callID != null || rawPart.callId != null ? String(rawPart.callID ?? rawPart.callId) : void 0,
445
+ state: {
446
+ status: normalizeStatus(stateRecord?.status ?? rawPart.status, output, error),
447
+ input,
448
+ output,
449
+ error: typeof error === "string" ? error : void 0,
450
+ metadata: asRecord(stateRecord?.metadata) ?? asRecord(rawPart.metadata),
451
+ time: normalizeTime(stateRecord?.time ?? rawPart.time)
452
+ }
453
+ };
454
+ }
455
+ return null;
456
+ }
457
+ function getPartKey(rawPart) {
458
+ const type = String(rawPart.type ?? "unknown");
459
+ if (type === "tool") {
460
+ return `tool:${resolveToolIdentity(rawPart)}`;
461
+ }
462
+ if (type === "reasoning") {
463
+ return `reasoning:${String(rawPart.id ?? rawPart.partId ?? rawPart.index ?? "current")}`;
464
+ }
465
+ return `text:${String(rawPart.id ?? rawPart.partId ?? rawPart.index ?? "current")}`;
466
+ }
467
+ function mergePart(existing, incoming, delta) {
468
+ if (!existing) {
469
+ if (incoming.type === "text" && delta) {
470
+ return { type: "text", text: delta };
471
+ }
472
+ return incoming;
473
+ }
474
+ if (existing.type === "text" && incoming.type === "text") {
475
+ return {
476
+ type: "text",
477
+ text: delta ? `${existing.text}${delta}` : incoming.text,
478
+ synthetic: incoming.synthetic ?? existing.synthetic
479
+ };
480
+ }
481
+ if (existing.type === "reasoning" && incoming.type === "reasoning") {
482
+ return {
483
+ ...existing,
484
+ ...incoming,
485
+ text: delta && incoming.text === existing.text ? `${existing.text}${delta}` : incoming.text || existing.text,
486
+ time: incoming.time ?? existing.time
487
+ };
488
+ }
489
+ if (existing.type === "tool" && incoming.type === "tool") {
490
+ return {
491
+ ...existing,
492
+ ...incoming,
493
+ state: {
494
+ ...existing.state,
495
+ ...incoming.state,
496
+ time: incoming.state.time ?? existing.state.time
497
+ }
498
+ };
499
+ }
500
+ return incoming;
501
+ }
502
+ function mapSeeds(messages) {
503
+ return {
504
+ messages: messages.map((message, index) => ({
505
+ id: message.id,
506
+ role: message.role,
507
+ _insertionIndex: index,
508
+ time: {
509
+ created: toMillis(message.createdAt) ?? Date.now()
510
+ }
511
+ })),
512
+ partMap: Object.fromEntries(
513
+ messages.map((message) => [
514
+ message.id,
515
+ message.parts ?? textPartsFromContent(message.content ?? "", message.attachments)
516
+ ])
517
+ )
518
+ };
519
+ }
520
+ function useSdkSession({
521
+ initialMessages = []
522
+ } = {}) {
523
+ const initialConversation = useMemo2(
524
+ () => mapSeeds(initialMessages),
525
+ [initialMessages]
526
+ );
527
+ const [conversation, setConversation] = useState4(initialConversation);
528
+ const [isStreaming, setIsStreaming] = useState4(false);
529
+ const activeAssistantIdRef = useRef4(null);
530
+ const insertionIndexRef = useRef4(initialConversation.messages.length);
531
+ const partIndexRef = useRef4({});
532
+ const replaceHistory = useCallback2((messages) => {
533
+ const next = mapSeeds(messages);
534
+ setConversation(next);
535
+ setIsStreaming(false);
536
+ activeAssistantIdRef.current = null;
537
+ insertionIndexRef.current = next.messages.length;
538
+ partIndexRef.current = {};
539
+ }, []);
540
+ const appendUserMessage = useCallback2(
541
+ ({
542
+ id = uid(),
543
+ role = "user",
544
+ content,
545
+ createdAt,
546
+ attachments
547
+ }) => {
548
+ setConversation((prev) => ({
549
+ messages: [
550
+ ...prev.messages,
551
+ {
552
+ id,
553
+ role,
554
+ _insertionIndex: insertionIndexRef.current++,
555
+ time: {
556
+ created: toMillis(createdAt) ?? Date.now()
557
+ }
558
+ }
559
+ ],
560
+ partMap: {
561
+ ...prev.partMap,
562
+ [id]: textPartsFromContent(content, attachments)
563
+ }
564
+ }));
565
+ return id;
566
+ },
567
+ []
568
+ );
569
+ const beginAssistantMessage = useCallback2(
570
+ ({
571
+ id = uid(),
572
+ role = "assistant",
573
+ createdAt
574
+ } = {}) => {
575
+ setConversation((prev) => ({
576
+ messages: [
577
+ ...prev.messages,
578
+ {
579
+ id,
580
+ role,
581
+ _insertionIndex: insertionIndexRef.current++,
582
+ time: {
583
+ created: toMillis(createdAt) ?? Date.now()
584
+ }
585
+ }
586
+ ],
587
+ partMap: {
588
+ ...prev.partMap,
589
+ [id]: prev.partMap[id] ?? []
590
+ }
591
+ }));
592
+ activeAssistantIdRef.current = id;
593
+ partIndexRef.current[id] = partIndexRef.current[id] ?? {};
594
+ setIsStreaming(true);
595
+ return id;
596
+ },
597
+ []
598
+ );
599
+ const completeAssistantMessage = useCallback2(
600
+ ({ messageId, finalText } = {}) => {
601
+ const targetId = messageId ?? activeAssistantIdRef.current;
602
+ if (!targetId) {
603
+ setIsStreaming(false);
604
+ return;
605
+ }
606
+ if (finalText) {
607
+ setConversation((prev) => {
608
+ const existingParts = prev.partMap[targetId] ?? [];
609
+ const nextParts = [...existingParts];
610
+ const textIndex = nextParts.findIndex((part) => part.type === "text");
611
+ if (textIndex === -1) {
612
+ nextParts.push({ type: "text", text: finalText });
613
+ } else {
614
+ nextParts[textIndex] = { type: "text", text: finalText };
615
+ }
616
+ return {
617
+ ...prev,
618
+ partMap: {
619
+ ...prev.partMap,
620
+ [targetId]: nextParts
621
+ }
622
+ };
623
+ });
624
+ }
625
+ delete partIndexRef.current[targetId];
626
+ if (activeAssistantIdRef.current === targetId) {
627
+ activeAssistantIdRef.current = null;
628
+ }
629
+ setIsStreaming(false);
630
+ },
631
+ []
632
+ );
633
+ const failAssistantMessage = useCallback2(
634
+ (error, options) => {
635
+ const targetId = options?.messageId ?? activeAssistantIdRef.current;
636
+ if (!targetId) {
637
+ setIsStreaming(false);
638
+ return;
639
+ }
640
+ setConversation((prev) => ({
641
+ ...prev,
642
+ partMap: {
643
+ ...prev.partMap,
644
+ [targetId]: [{ type: "text", text: `Error: ${error}` }]
645
+ }
646
+ }));
647
+ delete partIndexRef.current[targetId];
648
+ if (activeAssistantIdRef.current === targetId) {
649
+ activeAssistantIdRef.current = null;
650
+ }
651
+ setIsStreaming(false);
652
+ },
653
+ []
654
+ );
655
+ const applySdkEvent = useCallback2(
656
+ (event, options) => {
657
+ const eventData = asRecord(event.data) ?? {};
658
+ if (event.type === "message.updated") {
659
+ const id = asString(eventData.id) ?? asString(eventData.messageId) ?? options?.messageId;
660
+ const role = asString(eventData.role) ?? "assistant";
661
+ if (!id) {
662
+ return;
663
+ }
664
+ setConversation((prev) => {
665
+ if (prev.messages.some((message) => message.id === id)) {
666
+ return prev;
667
+ }
668
+ return {
669
+ ...prev,
670
+ messages: [
671
+ ...prev.messages,
672
+ {
673
+ id,
674
+ role,
675
+ _insertionIndex: insertionIndexRef.current++,
676
+ time: { created: Date.now() }
677
+ }
678
+ ],
679
+ partMap: {
680
+ ...prev.partMap,
681
+ [id]: prev.partMap[id] ?? []
682
+ }
683
+ };
684
+ });
685
+ if (role === "assistant" || role === "system") {
686
+ activeAssistantIdRef.current = id;
687
+ partIndexRef.current[id] = partIndexRef.current[id] ?? {};
688
+ setIsStreaming(true);
689
+ }
690
+ return;
691
+ }
692
+ if (event.type === "message.part.updated") {
693
+ const rawPart = asRecord(eventData.part) ?? eventData;
694
+ const targetId = options?.messageId ?? activeAssistantIdRef.current;
695
+ const delta = asString(eventData.delta);
696
+ if (!targetId || !rawPart) {
697
+ return;
698
+ }
699
+ const normalizedPart = normalizePart(rawPart);
700
+ if (!normalizedPart) {
701
+ return;
702
+ }
703
+ const key = getPartKey(rawPart);
704
+ setConversation((prev) => {
705
+ const existingParts = prev.partMap[targetId] ?? [];
706
+ const nextParts = [...existingParts];
707
+ const indexMap = partIndexRef.current[targetId] ?? (partIndexRef.current[targetId] = {});
708
+ const existingIndex = indexMap[key];
709
+ if (existingIndex == null) {
710
+ indexMap[key] = nextParts.length;
711
+ nextParts.push(mergePart(void 0, normalizedPart, delta));
712
+ } else {
713
+ nextParts[existingIndex] = mergePart(
714
+ nextParts[existingIndex],
715
+ normalizedPart,
716
+ delta
717
+ );
718
+ }
719
+ return {
720
+ ...prev,
721
+ partMap: {
722
+ ...prev.partMap,
723
+ [targetId]: nextParts
724
+ }
725
+ };
726
+ });
727
+ activeAssistantIdRef.current = targetId;
728
+ setIsStreaming(true);
729
+ return;
730
+ }
731
+ if (event.type === "result") {
732
+ completeAssistantMessage({
733
+ messageId: options?.messageId,
734
+ finalText: asString(eventData.finalText)
735
+ });
736
+ return;
737
+ }
738
+ if (event.type === "done") {
739
+ completeAssistantMessage({ messageId: options?.messageId });
740
+ return;
741
+ }
742
+ if (event.type === "error") {
743
+ failAssistantMessage(
744
+ asString(eventData.message) ?? "Agent error",
745
+ { messageId: options?.messageId }
746
+ );
747
+ }
748
+ },
749
+ [completeAssistantMessage, failAssistantMessage]
750
+ );
751
+ const reset = useCallback2(() => {
752
+ setConversation({ messages: [], partMap: {} });
753
+ setIsStreaming(false);
754
+ activeAssistantIdRef.current = null;
755
+ insertionIndexRef.current = 0;
756
+ partIndexRef.current = {};
757
+ }, []);
758
+ return {
759
+ messages: conversation.messages,
760
+ partMap: conversation.partMap,
761
+ isStreaming,
762
+ activeAssistantMessageId: activeAssistantIdRef.current,
763
+ replaceHistory,
764
+ appendUserMessage,
765
+ beginAssistantMessage,
766
+ applySdkEvent,
767
+ completeAssistantMessage,
768
+ failAssistantMessage,
769
+ setStreaming: setIsStreaming,
770
+ reset
771
+ };
772
+ }
773
+
774
+ // src/hooks/use-sse-stream.ts
775
+ import * as React from "react";
776
+ function useSSEStream(options) {
777
+ const {
778
+ url,
779
+ authToken,
780
+ autoReconnect = true,
781
+ maxRetries = 5,
782
+ reconnectDelay = 1e3,
783
+ eventTypes,
784
+ onEvent,
785
+ onStateChange,
786
+ onError,
787
+ headers,
788
+ enabled = true
789
+ } = options;
790
+ const [state, setState] = React.useState("disconnected");
791
+ const [events, setEvents] = React.useState([]);
792
+ const [lastEvent, setLastEvent] = React.useState(null);
793
+ const [error, setError] = React.useState(null);
794
+ const [lastEventTime, setLastEventTime] = React.useState(Date.now());
795
+ const [timeSinceLastEvent, setTimeSinceLastEvent] = React.useState(0);
796
+ const retryCountRef = React.useRef(0);
797
+ const eventSourceRef = React.useRef(null);
798
+ const abortControllerRef = React.useRef(null);
799
+ const reconnectTimeoutRef = React.useRef(null);
800
+ const lastEventIdRef = React.useRef(void 0);
801
+ React.useEffect(() => {
802
+ const interval = setInterval(() => {
803
+ setTimeSinceLastEvent(Date.now() - lastEventTime);
804
+ }, 1e3);
805
+ return () => clearInterval(interval);
806
+ }, [lastEventTime]);
807
+ React.useEffect(() => {
808
+ onStateChange?.(state);
809
+ }, [state, onStateChange]);
810
+ const handleEvent = React.useCallback(
811
+ (eventType, data, id) => {
812
+ const event = {
813
+ id,
814
+ event: eventType,
815
+ data,
816
+ timestamp: Date.now()
817
+ };
818
+ if (id) {
819
+ lastEventIdRef.current = id;
820
+ }
821
+ setLastEventTime(Date.now());
822
+ setLastEvent(event);
823
+ setEvents((prev) => {
824
+ const next = [...prev, event];
825
+ return next.length > 1e3 ? next.slice(-1e3) : next;
826
+ });
827
+ onEvent?.(event);
828
+ },
829
+ [onEvent]
830
+ );
831
+ const disconnect = React.useCallback(() => {
832
+ if (reconnectTimeoutRef.current) {
833
+ clearTimeout(reconnectTimeoutRef.current);
834
+ reconnectTimeoutRef.current = null;
835
+ }
836
+ if (abortControllerRef.current) {
837
+ abortControllerRef.current.abort();
838
+ abortControllerRef.current = null;
839
+ }
840
+ if (eventSourceRef.current) {
841
+ eventSourceRef.current.close();
842
+ eventSourceRef.current = null;
843
+ }
844
+ setState("disconnected");
845
+ }, []);
846
+ const connect = React.useCallback(() => {
847
+ disconnect();
848
+ if (!url || !enabled) {
849
+ return;
850
+ }
851
+ setState("connecting");
852
+ setError(null);
853
+ const connectUrl = new URL(url, window.location.origin);
854
+ if (lastEventIdRef.current) {
855
+ connectUrl.searchParams.set("lastEventId", lastEventIdRef.current);
856
+ }
857
+ if (authToken || headers) {
858
+ abortControllerRef.current = new AbortController();
859
+ const fetchHeaders = {
860
+ Accept: "text/event-stream",
861
+ "Cache-Control": "no-cache",
862
+ ...headers
863
+ };
864
+ if (authToken) {
865
+ fetchHeaders.Authorization = authToken.startsWith("Bearer ") ? authToken : `Bearer ${authToken}`;
866
+ }
867
+ if (lastEventIdRef.current) {
868
+ fetchHeaders["Last-Event-ID"] = lastEventIdRef.current;
869
+ }
870
+ fetch(connectUrl.toString(), {
871
+ headers: fetchHeaders,
872
+ signal: abortControllerRef.current.signal
873
+ }).then(async (response) => {
874
+ if (!response.ok) {
875
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
876
+ }
877
+ if (!response.body) {
878
+ throw new Error("Response body is null");
879
+ }
880
+ setState("connected");
881
+ retryCountRef.current = 0;
882
+ const reader = response.body.getReader();
883
+ const decoder = new TextDecoder();
884
+ let buffer = "";
885
+ while (true) {
886
+ const { done, value } = await reader.read();
887
+ if (done) {
888
+ break;
889
+ }
890
+ buffer += decoder.decode(value, { stream: true });
891
+ const lines = buffer.split("\n");
892
+ buffer = lines.pop() || "";
893
+ let currentEvent = "";
894
+ let currentData = "";
895
+ let currentId;
896
+ for (const line of lines) {
897
+ if (line.startsWith("event:")) {
898
+ currentEvent = line.slice(6).trim();
899
+ } else if (line.startsWith("data:")) {
900
+ currentData += (currentData ? "\n" : "") + line.slice(5).trim();
901
+ } else if (line.startsWith("id:")) {
902
+ currentId = line.slice(3).trim();
903
+ } else if (line === "" && currentData) {
904
+ try {
905
+ const parsedData = JSON.parse(currentData);
906
+ const eventType = currentEvent || "message";
907
+ if (!eventTypes || eventTypes.includes(eventType)) {
908
+ handleEvent(eventType, parsedData, currentId);
909
+ }
910
+ } catch {
911
+ if (!eventTypes || eventTypes.includes(currentEvent || "message")) {
912
+ handleEvent(
913
+ currentEvent || "message",
914
+ currentData,
915
+ currentId
916
+ );
917
+ }
918
+ }
919
+ currentEvent = "";
920
+ currentData = "";
921
+ currentId = void 0;
922
+ }
923
+ }
924
+ }
925
+ if (autoReconnect && enabled) {
926
+ setState("reconnecting");
927
+ const delay = reconnectDelay * 2 ** retryCountRef.current;
928
+ reconnectTimeoutRef.current = setTimeout(
929
+ () => {
930
+ retryCountRef.current += 1;
931
+ connect();
932
+ },
933
+ Math.min(delay, 3e4)
934
+ );
935
+ } else {
936
+ setState("disconnected");
937
+ }
938
+ }).catch((err) => {
939
+ if (err.name === "AbortError") {
940
+ return;
941
+ }
942
+ setError(err);
943
+ onError?.(err);
944
+ setState("error");
945
+ if (autoReconnect && enabled && retryCountRef.current < maxRetries) {
946
+ setState("reconnecting");
947
+ const delay = reconnectDelay * 2 ** retryCountRef.current;
948
+ reconnectTimeoutRef.current = setTimeout(
949
+ () => {
950
+ retryCountRef.current += 1;
951
+ connect();
952
+ },
953
+ Math.min(delay, 3e4)
954
+ );
955
+ }
956
+ });
957
+ } else {
958
+ const es = new EventSource(connectUrl.toString());
959
+ eventSourceRef.current = es;
960
+ es.onopen = () => {
961
+ setState("connected");
962
+ retryCountRef.current = 0;
963
+ };
964
+ es.onerror = () => {
965
+ const err = new Error("EventSource connection error");
966
+ setError(err);
967
+ onError?.(err);
968
+ if (autoReconnect && enabled && retryCountRef.current < maxRetries) {
969
+ setState("reconnecting");
970
+ es.close();
971
+ const delay = reconnectDelay * 2 ** retryCountRef.current;
972
+ reconnectTimeoutRef.current = setTimeout(
973
+ () => {
974
+ retryCountRef.current += 1;
975
+ connect();
976
+ },
977
+ Math.min(delay, 3e4)
978
+ );
979
+ } else {
980
+ setState("error");
981
+ }
982
+ };
983
+ const types = eventTypes || ["message"];
984
+ for (const type of types) {
985
+ es.addEventListener(type, (e) => {
986
+ try {
987
+ const data = JSON.parse(e.data);
988
+ handleEvent(type, data, e.lastEventId);
989
+ } catch {
990
+ handleEvent(type, e.data, e.lastEventId);
991
+ }
992
+ });
993
+ }
994
+ if (!eventTypes) {
995
+ es.onmessage = (e) => {
996
+ try {
997
+ const data = JSON.parse(e.data);
998
+ handleEvent("message", data, e.lastEventId);
999
+ } catch {
1000
+ handleEvent("message", e.data, e.lastEventId);
1001
+ }
1002
+ };
1003
+ }
1004
+ }
1005
+ }, [
1006
+ url,
1007
+ authToken,
1008
+ headers,
1009
+ enabled,
1010
+ autoReconnect,
1011
+ maxRetries,
1012
+ reconnectDelay,
1013
+ eventTypes,
1014
+ handleEvent,
1015
+ onError,
1016
+ disconnect
1017
+ ]);
1018
+ React.useEffect(() => {
1019
+ if (enabled) {
1020
+ connect();
1021
+ }
1022
+ return () => disconnect();
1023
+ }, [enabled, connect, disconnect]);
1024
+ const clearEvents = React.useCallback(() => {
1025
+ setEvents([]);
1026
+ setLastEvent(null);
1027
+ }, []);
1028
+ return {
1029
+ state,
1030
+ events,
1031
+ lastEvent,
1032
+ error,
1033
+ connect,
1034
+ disconnect,
1035
+ clearEvents,
1036
+ retryCount: retryCountRef.current,
1037
+ timeSinceLastEvent
1038
+ };
1039
+ }
1040
+
1041
+ export {
1042
+ useDropdownMenu,
1043
+ useToolCallStream,
1044
+ useRealtimeSession,
1045
+ RealtimeSessionRegistry,
1046
+ useSdkSession,
1047
+ useSSEStream
1048
+ };