@tangle-network/ui 11.10.0 → 11.11.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 (132) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +4 -1
  3. package/dist/active-sessions-store-Gt1R0rFG.js +262 -0
  4. package/dist/{active-sessions-store-CeOmXgv5.d.ts → active-sessions-store-nl70ZnMC.d.ts} +40 -41
  5. package/dist/artifact-pane-BL_DFWNl.d.ts +32 -0
  6. package/dist/artifact-pane-DMXT9a8U.js +70 -0
  7. package/dist/auth-DOETfnd9.js +154 -0
  8. package/dist/auth.d.ts +2 -74
  9. package/dist/auth.js +2 -16
  10. package/dist/branding-26d5ARIr.d.ts +14 -0
  11. package/dist/button-C87nEe2R.js +78 -0
  12. package/dist/button-yoIv77e2.d.ts +16 -0
  13. package/dist/chat-D4jAfEmj.js +667 -0
  14. package/dist/chat.d.ts +2 -201
  15. package/dist/chat.js +2 -31
  16. package/dist/code-block-B36KVUSN.js +165 -0
  17. package/dist/code-block-B9miUPht.d.ts +23 -0
  18. package/dist/diff-preview-BcK3nRXb.js +533 -0
  19. package/dist/document-editor-pane-BcoABlhn.d.ts +131 -0
  20. package/dist/document-editor-pane-BxlPjaTP.js +1287 -0
  21. package/dist/dropdown-menu-BpcLa5vF.js +101 -0
  22. package/dist/editor.d.ts +76 -78
  23. package/dist/editor.js +2 -34
  24. package/dist/file-format-BgXbr-Ib.js +160 -0
  25. package/dist/files-Bug33qCl.js +743 -0
  26. package/dist/files.d.ts +2 -243
  27. package/dist/files.js +3 -41
  28. package/dist/focus-BcE_SHqz.js +57 -0
  29. package/dist/format-DfcLWqMa.js +54 -0
  30. package/dist/hooks.d.ts +30 -37
  31. package/dist/hooks.js +111 -46
  32. package/dist/index-2UntyjBb.d.ts +52 -0
  33. package/dist/index-BgUHilZm.d.ts +108 -0
  34. package/dist/index-COgqsFAQ.d.ts +42 -0
  35. package/dist/index-CPDET_s1.d.ts +549 -0
  36. package/dist/index-CUhfHT_V.d.ts +204 -0
  37. package/dist/index-Cg6gYAqh.d.ts +73 -0
  38. package/dist/index-Cq-EJzqW.d.ts +16 -0
  39. package/dist/index-DhYmSaLF.d.ts +245 -0
  40. package/dist/index-DjKxO357.d.ts +128 -0
  41. package/dist/index.d.ts +26 -43
  42. package/dist/index.js +35 -472
  43. package/dist/markdown-BGqicV82.js +45 -0
  44. package/dist/markdown.d.ts +3 -18
  45. package/dist/markdown.js +3 -15
  46. package/dist/message-vhUI0c5G.d.ts +16 -0
  47. package/dist/nav.d.ts +6 -8
  48. package/dist/nav.js +29 -11
  49. package/dist/openui-artifact-renderer-D3E5bIAI.d.ts +114 -0
  50. package/dist/openui-artifact-renderer-DrU1AdVk.js +266 -0
  51. package/dist/openui.d.ts +2 -115
  52. package/dist/openui.js +2 -13
  53. package/dist/parts-5mhy9yJ3.d.ts +37 -0
  54. package/dist/primitives-Cb8GGVKr.js +1239 -0
  55. package/dist/primitives.d.ts +5 -525
  56. package/dist/primitives.js +9 -212
  57. package/dist/redaction-CGGB2ENG.js +74 -0
  58. package/dist/redaction.d.ts +2 -42
  59. package/dist/redaction.js +2 -7
  60. package/dist/run-OyWmIDqu.d.ts +41 -0
  61. package/dist/run-ZsIKPA6J.js +17 -0
  62. package/dist/run-group-BACLbCFl.js +410 -0
  63. package/dist/run.d.ts +3 -126
  64. package/dist/run.js +5 -40
  65. package/dist/sdk-hooks.d.ts +2 -285
  66. package/dist/sdk-hooks.js +3 -36
  67. package/dist/stores.d.ts +13 -14
  68. package/dist/stores.js +34 -76
  69. package/dist/table-Bss73DhB.js +146 -0
  70. package/dist/tabs-BVHR6NkO.js +40 -0
  71. package/dist/text-shimmer-BUUo6I-g.js +64 -0
  72. package/dist/tool-call-feed-BeB1_4pX.d.ts +47 -0
  73. package/dist/tool-call-feed-Djt2q11V.js +109 -0
  74. package/dist/tool-call-step-DNebqhmX.js +434 -0
  75. package/dist/tool-display-BAuAVa43.js +158 -0
  76. package/dist/{tool-display-z4JcDmMQ.d.ts → tool-display-DgmL5rIU.d.ts} +16 -16
  77. package/dist/tool-previews.d.ts +2 -48
  78. package/dist/tool-previews.js +2 -23
  79. package/dist/types.d.ts +6 -19
  80. package/dist/types.js +0 -1
  81. package/dist/use-auto-scroll-C5Pjeqon.js +153 -0
  82. package/dist/use-sse-stream-BVgO1QBN.js +884 -0
  83. package/dist/use-sse-stream-Dm6R5YWz.d.ts +282 -0
  84. package/dist/utils-Bl_CZ0Qp.js +37 -0
  85. package/dist/{chunk-RQHJBTEU.js → utils-C_uf36nf.js} +4 -6
  86. package/dist/utils.d.ts +2 -104
  87. package/dist/utils.js +6 -44
  88. package/package.json +39 -41
  89. package/src/lib/focus.ts +2 -1
  90. package/dist/artifact-pane-DpuWcAiF.d.ts +0 -33
  91. package/dist/button-CMQuQEW_.d.ts +0 -17
  92. package/dist/chunk-34A66VBG.js +0 -214
  93. package/dist/chunk-3OI2QKFD.js +0 -0
  94. package/dist/chunk-3XXBZL53.js +0 -174
  95. package/dist/chunk-42PAH5D2.js +0 -112
  96. package/dist/chunk-4CLN43XT.js +0 -45
  97. package/dist/chunk-55WHV7SS.js +0 -1604
  98. package/dist/chunk-565EATZZ.js +0 -156
  99. package/dist/chunk-5Z5ZYMOJ.js +0 -0
  100. package/dist/chunk-6BGQA4BQ.js +0 -0
  101. package/dist/chunk-BEBXZPHW.js +0 -299
  102. package/dist/chunk-C5WF4WMV.js +0 -734
  103. package/dist/chunk-HFL6R6IF.js +0 -37
  104. package/dist/chunk-HGML45QH.js +0 -104
  105. package/dist/chunk-HMAPIH7L.js +0 -595
  106. package/dist/chunk-K5XFRUS4.js +0 -1401
  107. package/dist/chunk-KFQGHD73.js +0 -198
  108. package/dist/chunk-L57WRIND.js +0 -59
  109. package/dist/chunk-LTEKI7YJ.js +0 -59
  110. package/dist/chunk-M3QZK62R.js +0 -233
  111. package/dist/chunk-ND3ZFXQK.js +0 -179
  112. package/dist/chunk-OEX7NZE3.js +0 -321
  113. package/dist/chunk-OL5GYLHV.js +0 -60
  114. package/dist/chunk-QGFYBNSO.js +0 -133
  115. package/dist/chunk-R7B3UWPF.js +0 -550
  116. package/dist/chunk-R7SUY4E3.js +0 -65
  117. package/dist/chunk-REJESC5U.js +0 -117
  118. package/dist/chunk-RQGKSCEZ.js +0 -0
  119. package/dist/chunk-SNLR2AEA.js +0 -40
  120. package/dist/chunk-WKWBTL2V.js +0 -15
  121. package/dist/chunk-X7CU776B.js +0 -1048
  122. package/dist/chunk-XU3HB5QL.js +0 -127
  123. package/dist/chunk-YTO5V6QS.js +0 -19
  124. package/dist/chunk-YU4E5J2N.js +0 -437
  125. package/dist/chunk-YZ6AK25N.js +0 -886
  126. package/dist/code-block-0kSpWMnf.d.ts +0 -25
  127. package/dist/document-editor-pane-IAEQn_Dl.d.ts +0 -128
  128. package/dist/document-editor-pane-QHDOBA43.js +0 -13
  129. package/dist/message-BHWbxBtT.d.ts +0 -15
  130. package/dist/parts-dj7AcUg0.d.ts +0 -36
  131. package/dist/run-PfLmDAox.d.ts +0 -41
  132. package/dist/tool-call-feed-D9iofJgW.d.ts +0 -46
@@ -1,1048 +0,0 @@
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-QGFYBNSO.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
- };