@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
@@ -0,0 +1,884 @@
1
+ import { n as parseToolEvent } from "./tool-call-feed-Djt2q11V.js";
2
+ import { _ as setForegroundActiveSession, d as registerActiveSession, g as setActiveSessionRunning, h as setActiveSessionError, m as setActiveSessionConnection, n as bumpActiveSessionActivity, p as setActiveSessionAttention, v as unregisterActiveSession, y as updateActiveSessionMeta } from "./active-sessions-store-Gt1R0rFG.js";
3
+ import * as React from "react";
4
+ import { createElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
5
+ //#region src/hooks/use-dropdown-menu.ts
6
+ function useDropdownMenu(options) {
7
+ const closeOnEsc = options?.closeOnEsc ?? true;
8
+ const [open, setOpen] = useState(false);
9
+ const ref = useRef(null);
10
+ useEffect(() => {
11
+ function handleClick(e) {
12
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
13
+ }
14
+ if (open) document.addEventListener("mousedown", handleClick);
15
+ return () => document.removeEventListener("mousedown", handleClick);
16
+ }, [open]);
17
+ useEffect(() => {
18
+ if (!open || !closeOnEsc) return;
19
+ function handleKey(e) {
20
+ if (e.key === "Escape") setOpen(false);
21
+ }
22
+ document.addEventListener("keydown", handleKey);
23
+ return () => document.removeEventListener("keydown", handleKey);
24
+ }, [open, closeOnEsc]);
25
+ return {
26
+ open,
27
+ setOpen,
28
+ ref,
29
+ toggle: () => setOpen((prev) => !prev),
30
+ close: () => setOpen(false)
31
+ };
32
+ }
33
+ //#endregion
34
+ //#region src/hooks/use-tool-call-stream.ts
35
+ /**
36
+ * useToolCallStream — parses SSE events into ToolCallFeed segments.
37
+ *
38
+ * Takes raw SSE event data from the orchestrator stream and produces
39
+ * structured FeedSegments that ToolCallFeed can render.
40
+ */
41
+ function useToolCallStream() {
42
+ const [segments, setSegments] = useState([]);
43
+ const pendingToolsRef = useRef(/* @__PURE__ */ new Map());
44
+ const lastSegmentKindRef = useRef(null);
45
+ const pushText = useCallback((delta) => {
46
+ setSegments((prev) => {
47
+ const last = prev[prev.length - 1];
48
+ if (last && last.kind === "text") return [...prev.slice(0, -1), {
49
+ kind: "text",
50
+ content: last.content + delta
51
+ }];
52
+ return [...prev, {
53
+ kind: "text",
54
+ content: delta
55
+ }];
56
+ });
57
+ lastSegmentKindRef.current = "text";
58
+ }, []);
59
+ return {
60
+ segments,
61
+ pushEvent: useCallback((event) => {
62
+ const toolCall = parseToolEvent(event);
63
+ if (!toolCall) return;
64
+ if (event.type === "tool.invocation" || event.type === "tool_use") {
65
+ pendingToolsRef.current.set(toolCall.id, toolCall);
66
+ setSegments((prev) => [...prev, {
67
+ kind: "tool_call",
68
+ call: {
69
+ ...toolCall,
70
+ status: "running"
71
+ }
72
+ }]);
73
+ lastSegmentKindRef.current = "tool";
74
+ }
75
+ }, []),
76
+ pushText,
77
+ completeToolCall: useCallback((id, result) => {
78
+ const pending = pendingToolsRef.current.get(id);
79
+ if (pending) {
80
+ pending.status = result.error ? "error" : "success";
81
+ pending.output = result.output || result.error;
82
+ pending.duration = result.duration;
83
+ pendingToolsRef.current.delete(id);
84
+ }
85
+ setSegments((prev) => prev.map((seg) => {
86
+ if (seg.kind === "tool_call" && seg.call.id === id) return {
87
+ kind: "tool_call",
88
+ call: {
89
+ ...seg.call,
90
+ status: result.error ? "error" : "success",
91
+ output: result.output || result.error,
92
+ duration: result.duration
93
+ }
94
+ };
95
+ return seg;
96
+ }));
97
+ }, []),
98
+ reset: useCallback(() => {
99
+ setSegments([]);
100
+ pendingToolsRef.current.clear();
101
+ lastSegmentKindRef.current = null;
102
+ }, [])
103
+ };
104
+ }
105
+ //#endregion
106
+ //#region src/hooks/use-realtime-session.ts
107
+ function parseEvent(message) {
108
+ try {
109
+ const parsed = JSON.parse(message);
110
+ return typeof parsed?.type === "string" ? parsed : null;
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+ function eventTimestamp(event) {
116
+ const rootTimestamp = event.timestamp;
117
+ if (typeof rootTimestamp === "number" && Number.isFinite(rootTimestamp)) return rootTimestamp;
118
+ const raw = event.data?.timestamp ?? event.data?.ts ?? event.data?.time ?? event.data?.eventAt;
119
+ if (typeof raw === "number" && Number.isFinite(raw)) return raw;
120
+ return null;
121
+ }
122
+ function resolveErrorMessage(event) {
123
+ const message = event.data?.message;
124
+ return typeof message === "string" && message.length > 0 ? message : null;
125
+ }
126
+ function updateStoreFromEvent(sessionId, event) {
127
+ const lastEventAt = eventTimestamp(event) ?? Date.now();
128
+ bumpActiveSessionActivity(sessionId, { lastEventAt });
129
+ if (event.type === "session.run.started") {
130
+ setActiveSessionRunning(sessionId, true, { lastEventAt });
131
+ return;
132
+ }
133
+ if (event.type === "session.run.completed" || event.type === "done" || event.type === "result") {
134
+ setActiveSessionRunning(sessionId, false, { lastEventAt });
135
+ return;
136
+ }
137
+ if (event.type === "session.attention") {
138
+ setActiveSessionAttention(sessionId, true, { lastEventAt });
139
+ return;
140
+ }
141
+ if (event.type === "error" || event.type === "session.run.failed") {
142
+ setActiveSessionError(sessionId, resolveErrorMessage(event) ?? "Session error");
143
+ return;
144
+ }
145
+ }
146
+ function useRealtimeSession({ sessionId, projectId = null, projectLabel, title, href, metadata, connectUrl, enabled = true, foreground = true, keepRegistered = true, reconnect = true, reconnectIntervalMs = 1500, maxReconnectAttempts = Infinity, transportMode = "websocket", onEvent, onOpen, onClose, onError }) {
147
+ const [connectionState, setConnectionState] = useState("disconnected");
148
+ const [lastError, setLastError] = useState(null);
149
+ const [reconnectAttempts, setReconnectAttempts] = useState(0);
150
+ const socketRef = useRef(null);
151
+ const timerRef = useRef(null);
152
+ const reconnectAttemptsRef = useRef(0);
153
+ const shouldReconnectRef = useRef(true);
154
+ const registration = useMemo(() => ({
155
+ sessionId,
156
+ projectId,
157
+ projectLabel,
158
+ title,
159
+ href,
160
+ metadata
161
+ }), [
162
+ href,
163
+ metadata,
164
+ projectId,
165
+ projectLabel,
166
+ sessionId,
167
+ title
168
+ ]);
169
+ useEffect(() => {
170
+ if (!sessionId || !keepRegistered) return void 0;
171
+ registerActiveSession(registration);
172
+ return () => {
173
+ unregisterActiveSession(sessionId);
174
+ };
175
+ }, [
176
+ keepRegistered,
177
+ registration,
178
+ sessionId
179
+ ]);
180
+ useEffect(() => {
181
+ if (!sessionId || !keepRegistered) return;
182
+ updateActiveSessionMeta(sessionId, {
183
+ projectId,
184
+ projectLabel,
185
+ title,
186
+ href,
187
+ metadata
188
+ });
189
+ }, [
190
+ href,
191
+ keepRegistered,
192
+ metadata,
193
+ projectId,
194
+ projectLabel,
195
+ sessionId,
196
+ title
197
+ ]);
198
+ useEffect(() => {
199
+ if (!sessionId || !foreground) return void 0;
200
+ setForegroundActiveSession(sessionId);
201
+ return () => {
202
+ setForegroundActiveSession(null);
203
+ };
204
+ }, [foreground, sessionId]);
205
+ useEffect(() => {
206
+ if (!enabled || !sessionId || !connectUrl || typeof window === "undefined") {
207
+ setConnectionState("disconnected");
208
+ if (sessionId) setActiveSessionConnection(sessionId, {
209
+ connectionState: "disconnected",
210
+ reconnectState: "idle",
211
+ transportMode
212
+ });
213
+ return;
214
+ }
215
+ shouldReconnectRef.current = true;
216
+ const clearReconnectTimer = () => {
217
+ if (timerRef.current != null) {
218
+ window.clearTimeout(timerRef.current);
219
+ timerRef.current = null;
220
+ }
221
+ };
222
+ const connect = () => {
223
+ clearReconnectTimer();
224
+ setConnectionState(reconnectAttemptsRef.current > 0 ? "reconnecting" : "connecting");
225
+ setActiveSessionConnection(sessionId, {
226
+ connectionState: reconnectAttemptsRef.current > 0 ? "reconnecting" : "connecting",
227
+ reconnectState: reconnectAttemptsRef.current > 0 ? "reconnecting" : "idle",
228
+ transportMode,
229
+ lastError: null
230
+ });
231
+ const socket = new window.WebSocket(connectUrl);
232
+ socketRef.current = socket;
233
+ socket.onopen = () => {
234
+ reconnectAttemptsRef.current = 0;
235
+ setReconnectAttempts(0);
236
+ setLastError(null);
237
+ setConnectionState("connected");
238
+ registerActiveSession(registration);
239
+ setActiveSessionConnection(sessionId, {
240
+ connectionState: "connected",
241
+ reconnectState: "idle",
242
+ transportMode,
243
+ lastError: null,
244
+ lastEventAt: Date.now()
245
+ });
246
+ onOpen?.();
247
+ };
248
+ socket.onmessage = (message) => {
249
+ const event = parseEvent(message.data);
250
+ if (!event) return;
251
+ registerActiveSession(registration);
252
+ updateStoreFromEvent(sessionId, event);
253
+ onEvent?.(event);
254
+ };
255
+ socket.onerror = () => {
256
+ const nextError = "Realtime session connection error";
257
+ setLastError(nextError);
258
+ setConnectionState("error");
259
+ setActiveSessionConnection(sessionId, {
260
+ connectionState: "error",
261
+ reconnectState: reconnect ? "reconnecting" : "failed",
262
+ transportMode,
263
+ lastError: nextError
264
+ });
265
+ onError?.(/* @__PURE__ */ new Error(nextError));
266
+ };
267
+ socket.onclose = () => {
268
+ socketRef.current = null;
269
+ onClose?.();
270
+ if (!shouldReconnectRef.current || !reconnect) {
271
+ setConnectionState("disconnected");
272
+ setActiveSessionConnection(sessionId, {
273
+ connectionState: "disconnected",
274
+ reconnectState: "idle",
275
+ transportMode
276
+ });
277
+ return;
278
+ }
279
+ reconnectAttemptsRef.current += 1;
280
+ setReconnectAttempts(reconnectAttemptsRef.current);
281
+ if (reconnectAttemptsRef.current > maxReconnectAttempts) {
282
+ setConnectionState("error");
283
+ setActiveSessionConnection(sessionId, {
284
+ connectionState: "error",
285
+ reconnectState: "failed",
286
+ transportMode,
287
+ lastError: "Unable to reconnect to realtime session"
288
+ });
289
+ return;
290
+ }
291
+ setConnectionState("reconnecting");
292
+ timerRef.current = window.setTimeout(connect, reconnectIntervalMs);
293
+ };
294
+ };
295
+ connect();
296
+ return () => {
297
+ shouldReconnectRef.current = false;
298
+ clearReconnectTimer();
299
+ const socket = socketRef.current;
300
+ socketRef.current = null;
301
+ if (socket && socket.readyState < WebSocket.CLOSING) socket.close();
302
+ };
303
+ }, [
304
+ connectUrl,
305
+ enabled,
306
+ maxReconnectAttempts,
307
+ onClose,
308
+ onError,
309
+ onEvent,
310
+ onOpen,
311
+ reconnect,
312
+ reconnectIntervalMs,
313
+ registration,
314
+ sessionId,
315
+ transportMode
316
+ ]);
317
+ return {
318
+ connectionState,
319
+ lastError,
320
+ reconnectAttempts,
321
+ isConnected: connectionState === "connected"
322
+ };
323
+ }
324
+ function RealtimeSessionRegistryItem(props) {
325
+ useRealtimeSession({
326
+ ...props,
327
+ foreground: props.foreground ?? false
328
+ });
329
+ return null;
330
+ }
331
+ function RealtimeSessionRegistry({ sessions }) {
332
+ return sessions.map((session) => createElement(RealtimeSessionRegistryItem, {
333
+ key: session.key ?? session.sessionId,
334
+ ...session
335
+ }));
336
+ }
337
+ //#endregion
338
+ //#region src/hooks/use-sdk-session.ts
339
+ function uid() {
340
+ if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.randomUUID === "function") try {
341
+ return globalThis.crypto.randomUUID();
342
+ } catch {}
343
+ return `sdk-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
344
+ }
345
+ function toMillis(value) {
346
+ if (value == null) return void 0;
347
+ if (typeof value === "number") return value;
348
+ if (value instanceof Date) return value.getTime();
349
+ const millis = new Date(value).getTime();
350
+ return Number.isFinite(millis) ? millis : void 0;
351
+ }
352
+ function asRecord(value) {
353
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
354
+ }
355
+ function asString(value) {
356
+ return typeof value === "string" && value.length > 0 ? value : void 0;
357
+ }
358
+ function textPartsFromContent(content, attachments) {
359
+ const text = `${content}${attachments?.length ? `\n\nAttachments:\n${attachments.map((attachment) => `- ${attachment.name}`).join("\n")}` : ""}`.trim();
360
+ return text ? [{
361
+ type: "text",
362
+ text
363
+ }] : [];
364
+ }
365
+ function normalizeTime(value) {
366
+ const record = asRecord(value);
367
+ if (!record) return void 0;
368
+ const start = Number(record.start ?? record.startedAt ?? record.started_at);
369
+ const end = Number(record.end ?? record.completedAt ?? record.completed_at);
370
+ if (!Number.isFinite(start) && !Number.isFinite(end)) return;
371
+ return {
372
+ start: Number.isFinite(start) ? start : void 0,
373
+ end: Number.isFinite(end) ? end : void 0
374
+ };
375
+ }
376
+ function normalizeStatus(value, output, error) {
377
+ if (value === "pending" || value === "running" || value === "completed" || value === "error") return value;
378
+ if (typeof error === "string" && error.length > 0) return "error";
379
+ if (output !== void 0) return "completed";
380
+ return "running";
381
+ }
382
+ function resolveToolIdentity(rawPart) {
383
+ return String(rawPart.id ?? rawPart.callID ?? rawPart.callId ?? rawPart.toolUseId ?? rawPart.toolCallId ?? rawPart.tool ?? rawPart.name ?? "tool");
384
+ }
385
+ function normalizePart(rawPart) {
386
+ const type = String(rawPart.type ?? "");
387
+ if (type === "text") return {
388
+ type: "text",
389
+ text: asString(rawPart.text) ?? asString(rawPart.content) ?? ""
390
+ };
391
+ if (type === "reasoning") return {
392
+ type: "reasoning",
393
+ text: asString(rawPart.text) ?? asString(rawPart.content) ?? "",
394
+ time: normalizeTime(rawPart.time)
395
+ };
396
+ if (type === "tool") {
397
+ const stateRecord = asRecord(rawPart.state);
398
+ const input = stateRecord?.input ?? rawPart.input;
399
+ const output = stateRecord?.output ?? rawPart.output;
400
+ const error = stateRecord?.error ?? rawPart.error;
401
+ return {
402
+ type: "tool",
403
+ id: resolveToolIdentity(rawPart),
404
+ tool: String(rawPart.tool ?? rawPart.name ?? "tool"),
405
+ callID: rawPart.callID != null || rawPart.callId != null ? String(rawPart.callID ?? rawPart.callId) : void 0,
406
+ state: {
407
+ status: normalizeStatus(stateRecord?.status ?? rawPart.status, output, error),
408
+ input,
409
+ output,
410
+ error: typeof error === "string" ? error : void 0,
411
+ metadata: asRecord(stateRecord?.metadata) ?? asRecord(rawPart.metadata),
412
+ time: normalizeTime(stateRecord?.time ?? rawPart.time)
413
+ }
414
+ };
415
+ }
416
+ return null;
417
+ }
418
+ function getPartKey(rawPart) {
419
+ const type = String(rawPart.type ?? "unknown");
420
+ if (type === "tool") return `tool:${resolveToolIdentity(rawPart)}`;
421
+ if (type === "reasoning") return `reasoning:${String(rawPart.id ?? rawPart.partId ?? rawPart.index ?? "current")}`;
422
+ return `text:${String(rawPart.id ?? rawPart.partId ?? rawPart.index ?? "current")}`;
423
+ }
424
+ function mergePart(existing, incoming, delta) {
425
+ if (!existing) {
426
+ if (incoming.type === "text" && delta) return {
427
+ type: "text",
428
+ text: delta
429
+ };
430
+ return incoming;
431
+ }
432
+ if (existing.type === "text" && incoming.type === "text") return {
433
+ type: "text",
434
+ text: delta ? `${existing.text}${delta}` : incoming.text,
435
+ synthetic: incoming.synthetic ?? existing.synthetic
436
+ };
437
+ if (existing.type === "reasoning" && incoming.type === "reasoning") return {
438
+ ...existing,
439
+ ...incoming,
440
+ text: delta && incoming.text === existing.text ? `${existing.text}${delta}` : incoming.text || existing.text,
441
+ time: incoming.time ?? existing.time
442
+ };
443
+ if (existing.type === "tool" && incoming.type === "tool") return {
444
+ ...existing,
445
+ ...incoming,
446
+ state: {
447
+ ...existing.state,
448
+ ...incoming.state,
449
+ time: incoming.state.time ?? existing.state.time
450
+ }
451
+ };
452
+ return incoming;
453
+ }
454
+ function mapSeeds(messages) {
455
+ return {
456
+ messages: messages.map((message, index) => ({
457
+ id: message.id,
458
+ role: message.role,
459
+ _insertionIndex: index,
460
+ time: { created: toMillis(message.createdAt) ?? Date.now() }
461
+ })),
462
+ partMap: Object.fromEntries(messages.map((message) => [message.id, message.parts ?? textPartsFromContent(message.content ?? "", message.attachments)]))
463
+ };
464
+ }
465
+ function useSdkSession({ initialMessages = [] } = {}) {
466
+ const initialConversation = useMemo(() => mapSeeds(initialMessages), [initialMessages]);
467
+ const [conversation, setConversation] = useState(initialConversation);
468
+ const [isStreaming, setIsStreaming] = useState(false);
469
+ const activeAssistantIdRef = useRef(null);
470
+ const insertionIndexRef = useRef(initialConversation.messages.length);
471
+ const partIndexRef = useRef({});
472
+ const replaceHistory = useCallback((messages) => {
473
+ const next = mapSeeds(messages);
474
+ setConversation(next);
475
+ setIsStreaming(false);
476
+ activeAssistantIdRef.current = null;
477
+ insertionIndexRef.current = next.messages.length;
478
+ partIndexRef.current = {};
479
+ }, []);
480
+ const appendUserMessage = useCallback(({ id = uid(), role = "user", content, createdAt, attachments }) => {
481
+ setConversation((prev) => ({
482
+ messages: [...prev.messages, {
483
+ id,
484
+ role,
485
+ _insertionIndex: insertionIndexRef.current++,
486
+ time: { created: toMillis(createdAt) ?? Date.now() }
487
+ }],
488
+ partMap: {
489
+ ...prev.partMap,
490
+ [id]: textPartsFromContent(content, attachments)
491
+ }
492
+ }));
493
+ return id;
494
+ }, []);
495
+ const beginAssistantMessage = useCallback(({ id = uid(), role = "assistant", createdAt } = {}) => {
496
+ setConversation((prev) => ({
497
+ messages: [...prev.messages, {
498
+ id,
499
+ role,
500
+ _insertionIndex: insertionIndexRef.current++,
501
+ time: { created: toMillis(createdAt) ?? Date.now() }
502
+ }],
503
+ partMap: {
504
+ ...prev.partMap,
505
+ [id]: prev.partMap[id] ?? []
506
+ }
507
+ }));
508
+ activeAssistantIdRef.current = id;
509
+ partIndexRef.current[id] = partIndexRef.current[id] ?? {};
510
+ setIsStreaming(true);
511
+ return id;
512
+ }, []);
513
+ const completeAssistantMessage = useCallback(({ messageId, finalText } = {}) => {
514
+ const targetId = messageId ?? activeAssistantIdRef.current;
515
+ if (!targetId) {
516
+ setIsStreaming(false);
517
+ return;
518
+ }
519
+ if (finalText) setConversation((prev) => {
520
+ const nextParts = [...prev.partMap[targetId] ?? []];
521
+ const textIndex = nextParts.findIndex((part) => part.type === "text");
522
+ if (textIndex === -1) nextParts.push({
523
+ type: "text",
524
+ text: finalText
525
+ });
526
+ else nextParts[textIndex] = {
527
+ type: "text",
528
+ text: finalText
529
+ };
530
+ return {
531
+ ...prev,
532
+ partMap: {
533
+ ...prev.partMap,
534
+ [targetId]: nextParts
535
+ }
536
+ };
537
+ });
538
+ delete partIndexRef.current[targetId];
539
+ if (activeAssistantIdRef.current === targetId) activeAssistantIdRef.current = null;
540
+ setIsStreaming(false);
541
+ }, []);
542
+ const failAssistantMessage = useCallback((error, options) => {
543
+ const targetId = options?.messageId ?? activeAssistantIdRef.current;
544
+ if (!targetId) {
545
+ setIsStreaming(false);
546
+ return;
547
+ }
548
+ setConversation((prev) => ({
549
+ ...prev,
550
+ partMap: {
551
+ ...prev.partMap,
552
+ [targetId]: [{
553
+ type: "text",
554
+ text: `Error: ${error}`
555
+ }]
556
+ }
557
+ }));
558
+ delete partIndexRef.current[targetId];
559
+ if (activeAssistantIdRef.current === targetId) activeAssistantIdRef.current = null;
560
+ setIsStreaming(false);
561
+ }, []);
562
+ const applySdkEvent = useCallback((event, options) => {
563
+ const eventData = asRecord(event.data) ?? {};
564
+ if (event.type === "message.updated") {
565
+ const id = asString(eventData.id) ?? asString(eventData.messageId) ?? options?.messageId;
566
+ const role = asString(eventData.role) ?? "assistant";
567
+ if (!id) return;
568
+ setConversation((prev) => {
569
+ if (prev.messages.some((message) => message.id === id)) return prev;
570
+ return {
571
+ ...prev,
572
+ messages: [...prev.messages, {
573
+ id,
574
+ role,
575
+ _insertionIndex: insertionIndexRef.current++,
576
+ time: { created: Date.now() }
577
+ }],
578
+ partMap: {
579
+ ...prev.partMap,
580
+ [id]: prev.partMap[id] ?? []
581
+ }
582
+ };
583
+ });
584
+ if (role === "assistant" || role === "system") {
585
+ activeAssistantIdRef.current = id;
586
+ partIndexRef.current[id] = partIndexRef.current[id] ?? {};
587
+ setIsStreaming(true);
588
+ }
589
+ return;
590
+ }
591
+ if (event.type === "message.part.updated") {
592
+ const rawPart = asRecord(eventData.part) ?? eventData;
593
+ const targetId = options?.messageId ?? activeAssistantIdRef.current;
594
+ const delta = asString(eventData.delta);
595
+ if (!targetId || !rawPart) return;
596
+ const normalizedPart = normalizePart(rawPart);
597
+ if (!normalizedPart) return;
598
+ const key = getPartKey(rawPart);
599
+ setConversation((prev) => {
600
+ const nextParts = [...prev.partMap[targetId] ?? []];
601
+ const indexMap = partIndexRef.current[targetId] ?? (partIndexRef.current[targetId] = {});
602
+ const existingIndex = indexMap[key];
603
+ if (existingIndex == null) {
604
+ indexMap[key] = nextParts.length;
605
+ nextParts.push(mergePart(void 0, normalizedPart, delta));
606
+ } else nextParts[existingIndex] = mergePart(nextParts[existingIndex], normalizedPart, delta);
607
+ return {
608
+ ...prev,
609
+ partMap: {
610
+ ...prev.partMap,
611
+ [targetId]: nextParts
612
+ }
613
+ };
614
+ });
615
+ activeAssistantIdRef.current = targetId;
616
+ setIsStreaming(true);
617
+ return;
618
+ }
619
+ if (event.type === "result") {
620
+ completeAssistantMessage({
621
+ messageId: options?.messageId,
622
+ finalText: asString(eventData.finalText)
623
+ });
624
+ return;
625
+ }
626
+ if (event.type === "done") {
627
+ completeAssistantMessage({ messageId: options?.messageId });
628
+ return;
629
+ }
630
+ if (event.type === "error") failAssistantMessage(asString(eventData.message) ?? "Agent error", { messageId: options?.messageId });
631
+ }, [completeAssistantMessage, failAssistantMessage]);
632
+ const reset = useCallback(() => {
633
+ setConversation({
634
+ messages: [],
635
+ partMap: {}
636
+ });
637
+ setIsStreaming(false);
638
+ activeAssistantIdRef.current = null;
639
+ insertionIndexRef.current = 0;
640
+ partIndexRef.current = {};
641
+ }, []);
642
+ return {
643
+ messages: conversation.messages,
644
+ partMap: conversation.partMap,
645
+ isStreaming,
646
+ activeAssistantMessageId: activeAssistantIdRef.current,
647
+ replaceHistory,
648
+ appendUserMessage,
649
+ beginAssistantMessage,
650
+ applySdkEvent,
651
+ completeAssistantMessage,
652
+ failAssistantMessage,
653
+ setStreaming: setIsStreaming,
654
+ reset
655
+ };
656
+ }
657
+ //#endregion
658
+ //#region src/hooks/use-sse-stream.ts
659
+ /**
660
+ * React hook for consuming SSE streams with automatic reconnection,
661
+ * event buffering, and state management.
662
+ *
663
+ * @example
664
+ * ```tsx
665
+ * const { events, state, lastEvent } = useSSEStream({
666
+ * url: '/api/v1/tasks/123/stream',
667
+ * authToken: 'Bearer xxx',
668
+ * onEvent: (e) => console.log('Event:', e),
669
+ * });
670
+ *
671
+ * return (
672
+ * <div>
673
+ * <p>Status: {state}</p>
674
+ * {events.map((e, i) => (
675
+ * <div key={i}>{e.event}: {JSON.stringify(e.data)}</div>
676
+ * ))}
677
+ * </div>
678
+ * );
679
+ * ```
680
+ */
681
+ function useSSEStream(options) {
682
+ const { url, authToken, autoReconnect = true, maxRetries = 5, reconnectDelay = 1e3, eventTypes, onEvent, onStateChange, onError, headers, enabled = true } = options;
683
+ const [state, setState] = React.useState("disconnected");
684
+ const [events, setEvents] = React.useState([]);
685
+ const [lastEvent, setLastEvent] = React.useState(null);
686
+ const [error, setError] = React.useState(null);
687
+ const [lastEventTime, setLastEventTime] = React.useState(Date.now());
688
+ const [timeSinceLastEvent, setTimeSinceLastEvent] = React.useState(0);
689
+ const retryCountRef = React.useRef(0);
690
+ const eventSourceRef = React.useRef(null);
691
+ const abortControllerRef = React.useRef(null);
692
+ const reconnectTimeoutRef = React.useRef(null);
693
+ const lastEventIdRef = React.useRef(void 0);
694
+ React.useEffect(() => {
695
+ const interval = setInterval(() => {
696
+ setTimeSinceLastEvent(Date.now() - lastEventTime);
697
+ }, 1e3);
698
+ return () => clearInterval(interval);
699
+ }, [lastEventTime]);
700
+ React.useEffect(() => {
701
+ onStateChange?.(state);
702
+ }, [state, onStateChange]);
703
+ const handleEvent = React.useCallback((eventType, data, id) => {
704
+ const event = {
705
+ id,
706
+ event: eventType,
707
+ data,
708
+ timestamp: Date.now()
709
+ };
710
+ if (id) lastEventIdRef.current = id;
711
+ setLastEventTime(Date.now());
712
+ setLastEvent(event);
713
+ setEvents((prev) => {
714
+ const next = [...prev, event];
715
+ return next.length > 1e3 ? next.slice(-1e3) : next;
716
+ });
717
+ onEvent?.(event);
718
+ }, [onEvent]);
719
+ const disconnect = React.useCallback(() => {
720
+ if (reconnectTimeoutRef.current) {
721
+ clearTimeout(reconnectTimeoutRef.current);
722
+ reconnectTimeoutRef.current = null;
723
+ }
724
+ if (abortControllerRef.current) {
725
+ abortControllerRef.current.abort();
726
+ abortControllerRef.current = null;
727
+ }
728
+ if (eventSourceRef.current) {
729
+ eventSourceRef.current.close();
730
+ eventSourceRef.current = null;
731
+ }
732
+ setState("disconnected");
733
+ }, []);
734
+ const connect = React.useCallback(() => {
735
+ disconnect();
736
+ if (!url || !enabled) return;
737
+ setState("connecting");
738
+ setError(null);
739
+ const connectUrl = new URL(url, window.location.origin);
740
+ if (lastEventIdRef.current) connectUrl.searchParams.set("lastEventId", lastEventIdRef.current);
741
+ if (authToken || headers) {
742
+ abortControllerRef.current = new AbortController();
743
+ const fetchHeaders = {
744
+ Accept: "text/event-stream",
745
+ "Cache-Control": "no-cache",
746
+ ...headers
747
+ };
748
+ if (authToken) fetchHeaders.Authorization = authToken.startsWith("Bearer ") ? authToken : `Bearer ${authToken}`;
749
+ if (lastEventIdRef.current) fetchHeaders["Last-Event-ID"] = lastEventIdRef.current;
750
+ fetch(connectUrl.toString(), {
751
+ headers: fetchHeaders,
752
+ signal: abortControllerRef.current.signal
753
+ }).then(async (response) => {
754
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
755
+ if (!response.body) throw new Error("Response body is null");
756
+ setState("connected");
757
+ retryCountRef.current = 0;
758
+ const reader = response.body.getReader();
759
+ const decoder = new TextDecoder();
760
+ let buffer = "";
761
+ while (true) {
762
+ const { done, value } = await reader.read();
763
+ if (done) break;
764
+ buffer += decoder.decode(value, { stream: true });
765
+ const lines = buffer.split("\n");
766
+ buffer = lines.pop() || "";
767
+ let currentEvent = "";
768
+ let currentData = "";
769
+ let currentId;
770
+ for (const line of lines) if (line.startsWith("event:")) currentEvent = line.slice(6).trim();
771
+ else if (line.startsWith("data:")) currentData += (currentData ? "\n" : "") + line.slice(5).trim();
772
+ else if (line.startsWith("id:")) currentId = line.slice(3).trim();
773
+ else if (line === "" && currentData) {
774
+ try {
775
+ const parsedData = JSON.parse(currentData);
776
+ const eventType = currentEvent || "message";
777
+ if (!eventTypes || eventTypes.includes(eventType)) handleEvent(eventType, parsedData, currentId);
778
+ } catch {
779
+ if (!eventTypes || eventTypes.includes(currentEvent || "message")) handleEvent(currentEvent || "message", currentData, currentId);
780
+ }
781
+ currentEvent = "";
782
+ currentData = "";
783
+ currentId = void 0;
784
+ }
785
+ }
786
+ if (autoReconnect && enabled) {
787
+ setState("reconnecting");
788
+ const delay = reconnectDelay * 2 ** retryCountRef.current;
789
+ reconnectTimeoutRef.current = setTimeout(() => {
790
+ retryCountRef.current += 1;
791
+ connect();
792
+ }, Math.min(delay, 3e4));
793
+ } else setState("disconnected");
794
+ }).catch((err) => {
795
+ if (err.name === "AbortError") return;
796
+ setError(err);
797
+ onError?.(err);
798
+ setState("error");
799
+ if (autoReconnect && enabled && retryCountRef.current < maxRetries) {
800
+ setState("reconnecting");
801
+ const delay = reconnectDelay * 2 ** retryCountRef.current;
802
+ reconnectTimeoutRef.current = setTimeout(() => {
803
+ retryCountRef.current += 1;
804
+ connect();
805
+ }, Math.min(delay, 3e4));
806
+ }
807
+ });
808
+ } else {
809
+ const es = new EventSource(connectUrl.toString());
810
+ eventSourceRef.current = es;
811
+ es.onopen = () => {
812
+ setState("connected");
813
+ retryCountRef.current = 0;
814
+ };
815
+ es.onerror = () => {
816
+ const err = /* @__PURE__ */ new Error("EventSource connection error");
817
+ setError(err);
818
+ onError?.(err);
819
+ if (autoReconnect && enabled && retryCountRef.current < maxRetries) {
820
+ setState("reconnecting");
821
+ es.close();
822
+ const delay = reconnectDelay * 2 ** retryCountRef.current;
823
+ reconnectTimeoutRef.current = setTimeout(() => {
824
+ retryCountRef.current += 1;
825
+ connect();
826
+ }, Math.min(delay, 3e4));
827
+ } else setState("error");
828
+ };
829
+ const types = eventTypes || ["message"];
830
+ for (const type of types) es.addEventListener(type, (e) => {
831
+ try {
832
+ const data = JSON.parse(e.data);
833
+ handleEvent(type, data, e.lastEventId);
834
+ } catch {
835
+ handleEvent(type, e.data, e.lastEventId);
836
+ }
837
+ });
838
+ if (!eventTypes) es.onmessage = (e) => {
839
+ try {
840
+ const data = JSON.parse(e.data);
841
+ handleEvent("message", data, e.lastEventId);
842
+ } catch {
843
+ handleEvent("message", e.data, e.lastEventId);
844
+ }
845
+ };
846
+ }
847
+ }, [
848
+ url,
849
+ authToken,
850
+ headers,
851
+ enabled,
852
+ autoReconnect,
853
+ maxRetries,
854
+ reconnectDelay,
855
+ eventTypes,
856
+ handleEvent,
857
+ onError,
858
+ disconnect
859
+ ]);
860
+ React.useEffect(() => {
861
+ if (enabled) connect();
862
+ return () => disconnect();
863
+ }, [
864
+ enabled,
865
+ connect,
866
+ disconnect
867
+ ]);
868
+ return {
869
+ state,
870
+ events,
871
+ lastEvent,
872
+ error,
873
+ connect,
874
+ disconnect,
875
+ clearEvents: React.useCallback(() => {
876
+ setEvents([]);
877
+ setLastEvent(null);
878
+ }, []),
879
+ retryCount: retryCountRef.current,
880
+ timeSinceLastEvent
881
+ };
882
+ }
883
+ //#endregion
884
+ export { useToolCallStream as a, useRealtimeSession as i, useSdkSession as n, useDropdownMenu as o, RealtimeSessionRegistry as r, useSSEStream as t };