@tangle-network/ui 11.12.0 → 11.13.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 (276) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/_virtual/_rolldown/runtime.js +13 -0
  3. package/dist/auth/auth.d.ts +53 -0
  4. package/dist/{auth-DOETfnd9.js → auth/auth.js} +7 -37
  5. package/dist/auth/login-layout.d.ts +20 -0
  6. package/dist/auth/login-layout.js +36 -0
  7. package/dist/auth.d.ts +2 -1
  8. package/dist/auth.js +2 -1
  9. package/dist/chat/agent-timeline.d.ts +83 -0
  10. package/dist/chat/agent-timeline.js +236 -0
  11. package/dist/chat/chat-container.d.ts +51 -0
  12. package/dist/chat/chat-container.js +309 -0
  13. package/dist/chat/chat-message.d.ts +22 -0
  14. package/dist/chat/chat-message.js +38 -0
  15. package/dist/chat/message-list.d.ts +32 -0
  16. package/dist/chat/message-list.js +37 -0
  17. package/dist/chat/thinking-indicator.d.ts +6 -0
  18. package/dist/chat/thinking-indicator.js +38 -0
  19. package/dist/chat/user-message.d.ts +21 -0
  20. package/dist/chat/user-message.js +42 -0
  21. package/dist/chat.d.ts +6 -1
  22. package/dist/chat.js +6 -1
  23. package/dist/editor/document-editor-pane.d.ts +37 -0
  24. package/dist/editor/document-editor-pane.js +199 -0
  25. package/dist/editor/editor-lazy.js +37 -0
  26. package/dist/editor/editor-loading.js +21 -0
  27. package/dist/editor/editor-peers.d.ts +6 -0
  28. package/dist/editor/editor-peers.js +155 -0
  29. package/dist/editor/editor-provider.d.ts +97 -0
  30. package/dist/editor/editor-provider.js +253 -0
  31. package/dist/editor/editor-toolbar.d.ts +11 -0
  32. package/dist/editor/editor-toolbar.js +106 -0
  33. package/dist/editor/markdown-conversion.js +23 -0
  34. package/dist/editor/markdown-document-editor.js +103 -0
  35. package/dist/editor/tiptap-editor.d.ts +45 -0
  36. package/dist/editor/tiptap-editor.js +271 -0
  37. package/dist/editor/use-editor.d.ts +67 -0
  38. package/dist/editor/use-editor.js +158 -0
  39. package/dist/editor.d.ts +6 -119
  40. package/dist/editor.js +5 -1
  41. package/dist/files/file-artifact-pane.d.ts +47 -0
  42. package/dist/files/file-artifact-pane.js +114 -0
  43. package/dist/files/file-format.d.ts +51 -0
  44. package/dist/{file-format-BgXbr-Ib.js → files/file-format.js} +1 -1
  45. package/dist/files/file-preview.d.ts +27 -0
  46. package/dist/files/file-preview.js +359 -0
  47. package/dist/files/file-tabs.d.ts +19 -0
  48. package/dist/files/file-tabs.js +48 -0
  49. package/dist/files/file-tree.d.ts +31 -0
  50. package/dist/files/file-tree.js +135 -0
  51. package/dist/files/rich-file-tree.d.ts +62 -0
  52. package/dist/files/rich-file-tree.js +122 -0
  53. package/dist/files.d.ts +6 -1
  54. package/dist/files.js +6 -2
  55. package/dist/hooks/use-auth.d.ts +40 -0
  56. package/dist/hooks/use-auth.js +91 -0
  57. package/dist/hooks/use-auto-scroll.d.ts +11 -0
  58. package/dist/hooks/use-auto-scroll.js +48 -0
  59. package/dist/hooks/use-dropdown-menu.d.ts +12 -0
  60. package/dist/hooks/use-dropdown-menu.js +31 -0
  61. package/dist/hooks/use-live-time.d.ts +7 -0
  62. package/dist/hooks/use-live-time.js +22 -0
  63. package/dist/hooks/use-realtime-session.d.ts +32 -0
  64. package/dist/hooks/use-realtime-session.js +235 -0
  65. package/dist/hooks/use-run-collapse-state.d.ts +13 -0
  66. package/dist/hooks/use-run-collapse-state.js +24 -0
  67. package/dist/hooks/use-run-groups.d.ts +11 -0
  68. package/dist/hooks/use-run-groups.js +85 -0
  69. package/dist/hooks/use-sdk-session.d.ts +59 -0
  70. package/dist/hooks/use-sdk-session.js +322 -0
  71. package/dist/hooks/use-sse-stream.d.ts +124 -0
  72. package/dist/hooks/use-sse-stream.js +229 -0
  73. package/dist/hooks/use-tool-call-stream.d.ts +23 -0
  74. package/dist/hooks/use-tool-call-stream.js +75 -0
  75. package/dist/hooks.d.ts +11 -49
  76. package/dist/hooks.js +10 -110
  77. package/dist/index.d.ts +99 -25
  78. package/dist/index.js +86 -36
  79. package/dist/lib/focus.d.ts +55 -0
  80. package/dist/{focus-BcE_SHqz.js → lib/focus.js} +1 -1
  81. package/dist/lib/utils.d.ts +4 -0
  82. package/dist/{utils-C_uf36nf.js → lib/utils.js} +1 -1
  83. package/dist/{code-block-B9miUPht.d.ts → markdown/code-block.d.ts} +5 -6
  84. package/dist/{code-block-B36KVUSN.js → markdown/code-block.js} +60 -55
  85. package/dist/{markdown-C0atSabr.d.ts → markdown/markdown.d.ts} +4 -5
  86. package/dist/{markdown-Db08KAYF.js → markdown/markdown.js} +3 -3
  87. package/dist/markdown.d.ts +2 -2
  88. package/dist/markdown.js +2 -2
  89. package/dist/{openui-artifact-renderer-fkBQaoLO.d.ts → openui/openui-artifact-renderer.d.ts} +20 -21
  90. package/dist/{openui-artifact-renderer-DG_i2lOR.js → openui/openui-artifact-renderer.js} +9 -7
  91. package/dist/{schema-BpW9rSSG.js → openui/schema-compiled.js} +2 -354
  92. package/dist/{schema-DAstk-J4.d.ts → openui/schema-data.d.ts} +3 -24
  93. package/dist/openui/schema-data.js +206 -0
  94. package/dist/openui-schema.d.ts +22 -3
  95. package/dist/openui-schema.js +138 -1
  96. package/dist/openui.d.ts +3 -2
  97. package/dist/openui.js +3 -2
  98. package/dist/{artifact-pane-BL_DFWNl.d.ts → primitives/artifact-pane.d.ts} +3 -4
  99. package/dist/{artifact-pane-DMXT9a8U.js → primitives/artifact-pane.js} +2 -2
  100. package/dist/primitives/avatar.d.ts +8 -0
  101. package/dist/primitives/avatar.js +25 -0
  102. package/dist/primitives/badge.d.ts +22 -0
  103. package/dist/primitives/badge.js +47 -0
  104. package/dist/{button-yoIv77e2.d.ts → primitives/button.d.ts} +2 -2
  105. package/dist/primitives/button.js +113 -0
  106. package/dist/primitives/card.d.ts +18 -0
  107. package/dist/primitives/card.js +52 -0
  108. package/dist/primitives/code-block.d.ts +6 -0
  109. package/dist/primitives/code-block.js +13 -0
  110. package/dist/primitives/dialog.d.ts +23 -0
  111. package/dist/primitives/dialog.js +59 -0
  112. package/dist/primitives/drop-zone.d.ts +23 -0
  113. package/dist/primitives/drop-zone.js +104 -0
  114. package/dist/primitives/dropdown-menu.d.ts +29 -0
  115. package/dist/{dropdown-menu-BpcLa5vF.js → primitives/dropdown-menu.js} +2 -23
  116. package/dist/primitives/empty-state.d.ts +11 -0
  117. package/dist/primitives/empty-state.js +32 -0
  118. package/dist/primitives/heading.d.ts +23 -0
  119. package/dist/primitives/heading.js +38 -0
  120. package/dist/primitives/input.d.ts +23 -0
  121. package/dist/primitives/input.js +110 -0
  122. package/dist/primitives/label.d.ts +6 -0
  123. package/dist/primitives/label.js +16 -0
  124. package/dist/primitives/metric-strip.d.ts +48 -0
  125. package/dist/primitives/metric-strip.js +52 -0
  126. package/dist/primitives/page-header.d.ts +25 -0
  127. package/dist/primitives/page-header.js +47 -0
  128. package/dist/primitives/page-shell.d.ts +14 -0
  129. package/dist/primitives/page-shell.js +17 -0
  130. package/dist/primitives/progress.d.ts +9 -0
  131. package/dist/primitives/progress.js +29 -0
  132. package/dist/primitives/segmented-control.d.ts +40 -0
  133. package/dist/primitives/segmented-control.js +57 -0
  134. package/dist/primitives/select.d.ts +15 -0
  135. package/dist/primitives/select.js +74 -0
  136. package/dist/primitives/sidebar-drop-zone.d.ts +21 -0
  137. package/dist/primitives/sidebar-drop-zone.js +88 -0
  138. package/dist/primitives/skeleton.d.ts +10 -0
  139. package/dist/primitives/skeleton.js +50 -0
  140. package/dist/primitives/stat-card.d.ts +16 -0
  141. package/dist/primitives/stat-card.js +60 -0
  142. package/dist/primitives/status-pill.d.ts +44 -0
  143. package/dist/primitives/status-pill.js +119 -0
  144. package/dist/primitives/switch.d.ts +6 -0
  145. package/dist/primitives/switch.js +16 -0
  146. package/dist/primitives/table.d.ts +22 -0
  147. package/dist/primitives/table.js +63 -0
  148. package/dist/primitives/tabs.d.ts +13 -0
  149. package/dist/{tabs-BVHR6NkO.js → primitives/tabs.js} +3 -3
  150. package/dist/primitives/terminal-display.d.ts +25 -0
  151. package/dist/primitives/terminal-display.js +109 -0
  152. package/dist/primitives/text-shimmer.d.ts +16 -0
  153. package/dist/{text-shimmer-BUUo6I-g.js → primitives/text-shimmer.js} +2 -2
  154. package/dist/primitives/theme-toggle.d.ts +11 -0
  155. package/dist/primitives/theme-toggle.js +154 -0
  156. package/dist/primitives/toast.d.ts +29 -0
  157. package/dist/primitives/toast.js +142 -0
  158. package/dist/primitives/toolbar.d.ts +55 -0
  159. package/dist/primitives/toolbar.js +37 -0
  160. package/dist/primitives/upload-progress.d.ts +23 -0
  161. package/dist/primitives/upload-progress.js +72 -0
  162. package/dist/primitives.d.ts +35 -5
  163. package/dist/primitives.js +35 -9
  164. package/dist/{index-COgqsFAQ.d.ts → redaction/redacted-document.d.ts} +6 -7
  165. package/dist/{redaction-CGGB2ENG.js → redaction/redacted-document.js} +2 -2
  166. package/dist/redaction.d.ts +1 -1
  167. package/dist/redaction.js +1 -1
  168. package/dist/run/expandable-content.js +41 -0
  169. package/dist/run/expanded-tool-detail.d.ts +12 -0
  170. package/dist/run/expanded-tool-detail.js +163 -0
  171. package/dist/run/inline-thinking-item.d.ts +11 -0
  172. package/dist/run/inline-thinking-item.js +60 -0
  173. package/dist/run/inline-tool-item.d.ts +25 -0
  174. package/dist/run/inline-tool-item.js +63 -0
  175. package/dist/run/run-group.d.ts +26 -0
  176. package/dist/{run-group-BWGeesgs.js → run/run-group.js} +9 -60
  177. package/dist/run/run-item-primitives.d.ts +5 -0
  178. package/dist/{run-ZsIKPA6J.js → run/run-item-primitives.js} +2 -2
  179. package/dist/run/run-row-shell.d.ts +53 -0
  180. package/dist/run/run-row-shell.js +114 -0
  181. package/dist/{tool-call-feed-BeB1_4pX.d.ts → run/tool-call-feed.d.ts} +7 -12
  182. package/dist/{tool-call-feed-BpmMLpPX.js → run/tool-call-feed.js} +4 -4
  183. package/dist/run/tool-call-step.d.ts +5 -0
  184. package/dist/run/tool-call-step.js +82 -0
  185. package/dist/run.d.ts +8 -2
  186. package/dist/run.js +7 -4
  187. package/dist/sdk-hooks.d.ts +8 -1
  188. package/dist/sdk-hooks.js +8 -2
  189. package/dist/stores/active-sessions-store.d.ts +83 -0
  190. package/dist/{active-sessions-store-Gt1R0rFG.js → stores/active-sessions-store.js} +1 -1
  191. package/dist/stores/chat-store.d.ts +14 -0
  192. package/dist/stores/chat-store.js +33 -0
  193. package/dist/stores.d.ts +3 -16
  194. package/dist/stores.js +2 -33
  195. package/dist/tool-previews/command-preview.d.ts +11 -0
  196. package/dist/tool-previews/command-preview.js +91 -0
  197. package/dist/tool-previews/diff-preview.d.ts +7 -0
  198. package/dist/tool-previews/diff-preview.js +76 -0
  199. package/dist/tool-previews/glob-results-preview.d.ts +7 -0
  200. package/dist/tool-previews/glob-results-preview.js +53 -0
  201. package/dist/tool-previews/grep-results-preview.d.ts +7 -0
  202. package/dist/tool-previews/grep-results-preview.js +99 -0
  203. package/dist/tool-previews/preview-primitives.js +58 -0
  204. package/dist/tool-previews/question-preview.d.ts +7 -0
  205. package/dist/tool-previews/question-preview.js +63 -0
  206. package/dist/tool-previews/web-search-preview.d.ts +7 -0
  207. package/dist/tool-previews/web-search-preview.js +78 -0
  208. package/dist/tool-previews/write-file-preview.d.ts +11 -0
  209. package/dist/tool-previews/write-file-preview.js +52 -0
  210. package/dist/tool-previews.d.ts +7 -1
  211. package/dist/tool-previews.js +7 -1
  212. package/dist/{branding-26d5ARIr.d.ts → types/branding.d.ts} +2 -3
  213. package/dist/{message-vhUI0c5G.d.ts → types/message.d.ts} +2 -3
  214. package/dist/{parts-5mhy9yJ3.d.ts → types/parts.d.ts} +8 -9
  215. package/dist/{run-OyWmIDqu.d.ts → types/run.d.ts} +9 -10
  216. package/dist/{tool-display-DgmL5rIU.d.ts → types/tool-display.d.ts} +5 -6
  217. package/dist/types.d.ts +5 -5
  218. package/dist/utils/copy-text.d.ts +7 -0
  219. package/dist/{utils-Bl_CZ0Qp.js → utils/copy-text.js} +1 -11
  220. package/dist/utils/format.d.ts +22 -0
  221. package/dist/{format-DfcLWqMa.js → utils/format.js} +1 -1
  222. package/dist/utils/time-ago.d.ts +3 -0
  223. package/dist/utils/time-ago.js +11 -0
  224. package/dist/utils/tool-display.d.ts +16 -0
  225. package/dist/{tool-display-BAuAVa43.js → utils/tool-display.js} +1 -1
  226. package/dist/utils.d.ts +6 -1
  227. package/dist/utils.js +6 -5
  228. package/package.json +2 -2
  229. package/src/files/rich-file-tree.stories.tsx +1 -1
  230. package/src/files/rich-file-tree.tsx +12 -9
  231. package/src/markdown/code-block.test.tsx +80 -2
  232. package/src/markdown/code-block.tsx +76 -80
  233. package/src/primitives/button.stories.tsx +37 -1
  234. package/src/primitives/button.test.tsx +230 -0
  235. package/src/primitives/button.tsx +62 -14
  236. package/src/primitives/card-anatomy.stories.tsx +51 -0
  237. package/src/primitives/card.test.tsx +71 -0
  238. package/src/primitives/card.tsx +25 -13
  239. package/src/primitives/heading.stories.tsx +35 -0
  240. package/src/primitives/heading.test.tsx +56 -0
  241. package/src/primitives/heading.tsx +76 -0
  242. package/src/primitives/index.ts +8 -0
  243. package/src/primitives/input.stories.tsx +28 -0
  244. package/src/primitives/input.test.tsx +161 -0
  245. package/src/primitives/input.tsx +34 -28
  246. package/src/primitives/page-header.stories.tsx +51 -0
  247. package/src/primitives/page-header.test.tsx +48 -0
  248. package/src/primitives/page-header.tsx +35 -35
  249. package/src/primitives/page-shell.stories.tsx +89 -0
  250. package/src/primitives/page-shell.test.tsx +24 -0
  251. package/src/primitives/page-shell.tsx +26 -0
  252. package/src/primitives/table-wrapper.stories.tsx +54 -0
  253. package/src/primitives/table.test.tsx +72 -0
  254. package/src/primitives/table.tsx +33 -12
  255. package/src/primitives/theme-toggle.test.tsx +167 -0
  256. package/src/primitives/theme-toggle.tsx +111 -39
  257. package/dist/active-sessions-store-nl70ZnMC.d.ts +0 -84
  258. package/dist/button-C87nEe2R.js +0 -78
  259. package/dist/chat-DDvF2oGQ.js +0 -672
  260. package/dist/diff-preview-BcK3nRXb.js +0 -533
  261. package/dist/document-editor-pane-BilJfiCo.d.ts +0 -136
  262. package/dist/document-editor-pane-POsq5Q0o.js +0 -1279
  263. package/dist/files-hywocVZz.js +0 -753
  264. package/dist/index-2UntyjBb.d.ts +0 -52
  265. package/dist/index-BgUHilZm.d.ts +0 -108
  266. package/dist/index-CJ8wRfiC.d.ts +0 -236
  267. package/dist/index-CPDET_s1.d.ts +0 -549
  268. package/dist/index-Cg6gYAqh.d.ts +0 -73
  269. package/dist/index-Cs7j8V7l.d.ts +0 -203
  270. package/dist/index-DjKxO357.d.ts +0 -128
  271. package/dist/primitives-Bx59fT-U.js +0 -1239
  272. package/dist/table-Bss73DhB.js +0 -146
  273. package/dist/tool-call-step-DNebqhmX.js +0 -434
  274. package/dist/use-auto-scroll-C5Pjeqon.js +0 -153
  275. package/dist/use-sse-stream-DjqRuDft.js +0 -884
  276. package/dist/use-sse-stream-Dm6R5YWz.d.ts +0 -282
@@ -0,0 +1,322 @@
1
+ import { useCallback, useMemo, useRef, useState } from "react";
2
+ //#region src/hooks/use-sdk-session.ts
3
+ function uid() {
4
+ if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.randomUUID === "function") try {
5
+ return globalThis.crypto.randomUUID();
6
+ } catch {}
7
+ return `sdk-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
8
+ }
9
+ function toMillis(value) {
10
+ if (value == null) return void 0;
11
+ if (typeof value === "number") return value;
12
+ if (value instanceof Date) return value.getTime();
13
+ const millis = new Date(value).getTime();
14
+ return Number.isFinite(millis) ? millis : void 0;
15
+ }
16
+ function asRecord(value) {
17
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
18
+ }
19
+ function asString(value) {
20
+ return typeof value === "string" && value.length > 0 ? value : void 0;
21
+ }
22
+ function textPartsFromContent(content, attachments) {
23
+ const text = `${content}${attachments?.length ? `\n\nAttachments:\n${attachments.map((attachment) => `- ${attachment.name}`).join("\n")}` : ""}`.trim();
24
+ return text ? [{
25
+ type: "text",
26
+ text
27
+ }] : [];
28
+ }
29
+ function normalizeTime(value) {
30
+ const record = asRecord(value);
31
+ if (!record) return void 0;
32
+ const start = Number(record.start ?? record.startedAt ?? record.started_at);
33
+ const end = Number(record.end ?? record.completedAt ?? record.completed_at);
34
+ if (!Number.isFinite(start) && !Number.isFinite(end)) return;
35
+ return {
36
+ start: Number.isFinite(start) ? start : void 0,
37
+ end: Number.isFinite(end) ? end : void 0
38
+ };
39
+ }
40
+ function normalizeStatus(value, output, error) {
41
+ if (value === "pending" || value === "running" || value === "completed" || value === "error") return value;
42
+ if (typeof error === "string" && error.length > 0) return "error";
43
+ if (output !== void 0) return "completed";
44
+ return "running";
45
+ }
46
+ function resolveToolIdentity(rawPart) {
47
+ return String(rawPart.id ?? rawPart.callID ?? rawPart.callId ?? rawPart.toolUseId ?? rawPart.toolCallId ?? rawPart.tool ?? rawPart.name ?? "tool");
48
+ }
49
+ function normalizePart(rawPart) {
50
+ const type = String(rawPart.type ?? "");
51
+ if (type === "text") return {
52
+ type: "text",
53
+ text: asString(rawPart.text) ?? asString(rawPart.content) ?? ""
54
+ };
55
+ if (type === "reasoning") return {
56
+ type: "reasoning",
57
+ text: asString(rawPart.text) ?? asString(rawPart.content) ?? "",
58
+ time: normalizeTime(rawPart.time)
59
+ };
60
+ if (type === "tool") {
61
+ const stateRecord = asRecord(rawPart.state);
62
+ const input = stateRecord?.input ?? rawPart.input;
63
+ const output = stateRecord?.output ?? rawPart.output;
64
+ const error = stateRecord?.error ?? rawPart.error;
65
+ return {
66
+ type: "tool",
67
+ id: resolveToolIdentity(rawPart),
68
+ tool: String(rawPart.tool ?? rawPart.name ?? "tool"),
69
+ callID: rawPart.callID != null || rawPart.callId != null ? String(rawPart.callID ?? rawPart.callId) : void 0,
70
+ state: {
71
+ status: normalizeStatus(stateRecord?.status ?? rawPart.status, output, error),
72
+ input,
73
+ output,
74
+ error: typeof error === "string" ? error : void 0,
75
+ metadata: asRecord(stateRecord?.metadata) ?? asRecord(rawPart.metadata),
76
+ time: normalizeTime(stateRecord?.time ?? rawPart.time)
77
+ }
78
+ };
79
+ }
80
+ return null;
81
+ }
82
+ function getPartKey(rawPart) {
83
+ const type = String(rawPart.type ?? "unknown");
84
+ if (type === "tool") return `tool:${resolveToolIdentity(rawPart)}`;
85
+ if (type === "reasoning") return `reasoning:${String(rawPart.id ?? rawPart.partId ?? rawPart.index ?? "current")}`;
86
+ return `text:${String(rawPart.id ?? rawPart.partId ?? rawPart.index ?? "current")}`;
87
+ }
88
+ function mergePart(existing, incoming, delta) {
89
+ if (!existing) {
90
+ if (incoming.type === "text" && delta) return {
91
+ type: "text",
92
+ text: delta
93
+ };
94
+ return incoming;
95
+ }
96
+ if (existing.type === "text" && incoming.type === "text") return {
97
+ type: "text",
98
+ text: delta ? `${existing.text}${delta}` : incoming.text,
99
+ synthetic: incoming.synthetic ?? existing.synthetic
100
+ };
101
+ if (existing.type === "reasoning" && incoming.type === "reasoning") return {
102
+ ...existing,
103
+ ...incoming,
104
+ text: delta && incoming.text === existing.text ? `${existing.text}${delta}` : incoming.text || existing.text,
105
+ time: incoming.time ?? existing.time
106
+ };
107
+ if (existing.type === "tool" && incoming.type === "tool") return {
108
+ ...existing,
109
+ ...incoming,
110
+ state: {
111
+ ...existing.state,
112
+ ...incoming.state,
113
+ time: incoming.state.time ?? existing.state.time
114
+ }
115
+ };
116
+ return incoming;
117
+ }
118
+ function mapSeeds(messages) {
119
+ return {
120
+ messages: messages.map((message, index) => ({
121
+ id: message.id,
122
+ role: message.role,
123
+ _insertionIndex: index,
124
+ time: { created: toMillis(message.createdAt) ?? Date.now() }
125
+ })),
126
+ partMap: Object.fromEntries(messages.map((message) => [message.id, message.parts ?? textPartsFromContent(message.content ?? "", message.attachments)]))
127
+ };
128
+ }
129
+ function useSdkSession({ initialMessages = [] } = {}) {
130
+ const initialConversation = useMemo(() => mapSeeds(initialMessages), [initialMessages]);
131
+ const [conversation, setConversation] = useState(initialConversation);
132
+ const [isStreaming, setIsStreaming] = useState(false);
133
+ const activeAssistantIdRef = useRef(null);
134
+ const insertionIndexRef = useRef(initialConversation.messages.length);
135
+ const partIndexRef = useRef({});
136
+ const replaceHistory = useCallback((messages) => {
137
+ const next = mapSeeds(messages);
138
+ setConversation(next);
139
+ setIsStreaming(false);
140
+ activeAssistantIdRef.current = null;
141
+ insertionIndexRef.current = next.messages.length;
142
+ partIndexRef.current = {};
143
+ }, []);
144
+ const appendUserMessage = useCallback(({ id = uid(), role = "user", content, createdAt, attachments }) => {
145
+ setConversation((prev) => ({
146
+ messages: [...prev.messages, {
147
+ id,
148
+ role,
149
+ _insertionIndex: insertionIndexRef.current++,
150
+ time: { created: toMillis(createdAt) ?? Date.now() }
151
+ }],
152
+ partMap: {
153
+ ...prev.partMap,
154
+ [id]: textPartsFromContent(content, attachments)
155
+ }
156
+ }));
157
+ return id;
158
+ }, []);
159
+ const beginAssistantMessage = useCallback(({ id = uid(), role = "assistant", createdAt } = {}) => {
160
+ setConversation((prev) => ({
161
+ messages: [...prev.messages, {
162
+ id,
163
+ role,
164
+ _insertionIndex: insertionIndexRef.current++,
165
+ time: { created: toMillis(createdAt) ?? Date.now() }
166
+ }],
167
+ partMap: {
168
+ ...prev.partMap,
169
+ [id]: prev.partMap[id] ?? []
170
+ }
171
+ }));
172
+ activeAssistantIdRef.current = id;
173
+ partIndexRef.current[id] = partIndexRef.current[id] ?? {};
174
+ setIsStreaming(true);
175
+ return id;
176
+ }, []);
177
+ const completeAssistantMessage = useCallback(({ messageId, finalText } = {}) => {
178
+ const targetId = messageId ?? activeAssistantIdRef.current;
179
+ if (!targetId) {
180
+ setIsStreaming(false);
181
+ return;
182
+ }
183
+ if (finalText) setConversation((prev) => {
184
+ const nextParts = [...prev.partMap[targetId] ?? []];
185
+ const textIndex = nextParts.findIndex((part) => part.type === "text");
186
+ if (textIndex === -1) nextParts.push({
187
+ type: "text",
188
+ text: finalText
189
+ });
190
+ else nextParts[textIndex] = {
191
+ type: "text",
192
+ text: finalText
193
+ };
194
+ return {
195
+ ...prev,
196
+ partMap: {
197
+ ...prev.partMap,
198
+ [targetId]: nextParts
199
+ }
200
+ };
201
+ });
202
+ delete partIndexRef.current[targetId];
203
+ if (activeAssistantIdRef.current === targetId) activeAssistantIdRef.current = null;
204
+ setIsStreaming(false);
205
+ }, []);
206
+ const failAssistantMessage = useCallback((error, options) => {
207
+ const targetId = options?.messageId ?? activeAssistantIdRef.current;
208
+ if (!targetId) {
209
+ setIsStreaming(false);
210
+ return;
211
+ }
212
+ setConversation((prev) => ({
213
+ ...prev,
214
+ partMap: {
215
+ ...prev.partMap,
216
+ [targetId]: [{
217
+ type: "text",
218
+ text: `Error: ${error}`
219
+ }]
220
+ }
221
+ }));
222
+ delete partIndexRef.current[targetId];
223
+ if (activeAssistantIdRef.current === targetId) activeAssistantIdRef.current = null;
224
+ setIsStreaming(false);
225
+ }, []);
226
+ const applySdkEvent = useCallback((event, options) => {
227
+ const eventData = asRecord(event.data) ?? {};
228
+ if (event.type === "message.updated") {
229
+ const id = asString(eventData.id) ?? asString(eventData.messageId) ?? options?.messageId;
230
+ const role = asString(eventData.role) ?? "assistant";
231
+ if (!id) return;
232
+ setConversation((prev) => {
233
+ if (prev.messages.some((message) => message.id === id)) return prev;
234
+ return {
235
+ ...prev,
236
+ messages: [...prev.messages, {
237
+ id,
238
+ role,
239
+ _insertionIndex: insertionIndexRef.current++,
240
+ time: { created: Date.now() }
241
+ }],
242
+ partMap: {
243
+ ...prev.partMap,
244
+ [id]: prev.partMap[id] ?? []
245
+ }
246
+ };
247
+ });
248
+ if (role === "assistant" || role === "system") {
249
+ activeAssistantIdRef.current = id;
250
+ partIndexRef.current[id] = partIndexRef.current[id] ?? {};
251
+ setIsStreaming(true);
252
+ }
253
+ return;
254
+ }
255
+ if (event.type === "message.part.updated") {
256
+ const rawPart = asRecord(eventData.part) ?? eventData;
257
+ const targetId = options?.messageId ?? activeAssistantIdRef.current;
258
+ const delta = asString(eventData.delta);
259
+ if (!targetId || !rawPart) return;
260
+ const normalizedPart = normalizePart(rawPart);
261
+ if (!normalizedPart) return;
262
+ const key = getPartKey(rawPart);
263
+ setConversation((prev) => {
264
+ const nextParts = [...prev.partMap[targetId] ?? []];
265
+ const indexMap = partIndexRef.current[targetId] ?? (partIndexRef.current[targetId] = {});
266
+ const existingIndex = indexMap[key];
267
+ if (existingIndex == null) {
268
+ indexMap[key] = nextParts.length;
269
+ nextParts.push(mergePart(void 0, normalizedPart, delta));
270
+ } else nextParts[existingIndex] = mergePart(nextParts[existingIndex], normalizedPart, delta);
271
+ return {
272
+ ...prev,
273
+ partMap: {
274
+ ...prev.partMap,
275
+ [targetId]: nextParts
276
+ }
277
+ };
278
+ });
279
+ activeAssistantIdRef.current = targetId;
280
+ setIsStreaming(true);
281
+ return;
282
+ }
283
+ if (event.type === "result") {
284
+ completeAssistantMessage({
285
+ messageId: options?.messageId,
286
+ finalText: asString(eventData.finalText)
287
+ });
288
+ return;
289
+ }
290
+ if (event.type === "done") {
291
+ completeAssistantMessage({ messageId: options?.messageId });
292
+ return;
293
+ }
294
+ if (event.type === "error") failAssistantMessage(asString(eventData.message) ?? "Agent error", { messageId: options?.messageId });
295
+ }, [completeAssistantMessage, failAssistantMessage]);
296
+ const reset = useCallback(() => {
297
+ setConversation({
298
+ messages: [],
299
+ partMap: {}
300
+ });
301
+ setIsStreaming(false);
302
+ activeAssistantIdRef.current = null;
303
+ insertionIndexRef.current = 0;
304
+ partIndexRef.current = {};
305
+ }, []);
306
+ return {
307
+ messages: conversation.messages,
308
+ partMap: conversation.partMap,
309
+ isStreaming,
310
+ activeAssistantMessageId: activeAssistantIdRef.current,
311
+ replaceHistory,
312
+ appendUserMessage,
313
+ beginAssistantMessage,
314
+ applySdkEvent,
315
+ completeAssistantMessage,
316
+ failAssistantMessage,
317
+ setStreaming: setIsStreaming,
318
+ reset
319
+ };
320
+ }
321
+ //#endregion
322
+ export { useSdkSession };
@@ -0,0 +1,124 @@
1
+ //#region src/hooks/use-sse-stream.d.ts
2
+ /**
3
+ * SSE Event data structure
4
+ */
5
+ export interface SSEEvent<T = unknown> {
6
+ id?: string;
7
+ event: string;
8
+ data: T;
9
+ timestamp: number;
10
+ }
11
+ /**
12
+ * Connection state for SSE streams
13
+ */
14
+ export type ConnectionState = "connecting" | "connected" | "reconnecting" | "disconnected" | "error";
15
+ /**
16
+ * SSE stream options
17
+ */
18
+ export interface UseSSEStreamOptions<T = unknown> {
19
+ /** URL to connect to */
20
+ url: string;
21
+ /** Authorization header value */
22
+ authToken?: string;
23
+ /** Enable automatic reconnection (default: true) */
24
+ autoReconnect?: boolean;
25
+ /** Max reconnection attempts (default: 5) */
26
+ maxRetries?: number;
27
+ /** Base delay between reconnects in ms (default: 1000) */
28
+ reconnectDelay?: number;
29
+ /** Event types to listen for (default: all) */
30
+ eventTypes?: string[];
31
+ /** Callback for each event */
32
+ onEvent?: (event: SSEEvent<T>) => void;
33
+ /** Callback on connection state change */
34
+ onStateChange?: (state: ConnectionState) => void;
35
+ /** Callback on error */
36
+ onError?: (error: Error) => void;
37
+ /** Custom headers */
38
+ headers?: Record<string, string>;
39
+ /** Whether to start connected (default: true) */
40
+ enabled?: boolean;
41
+ }
42
+ /**
43
+ * SSE stream result
44
+ */
45
+ export interface UseSSEStreamResult<T = unknown> {
46
+ /** Current connection state */
47
+ state: ConnectionState;
48
+ /** All received events */
49
+ events: SSEEvent<T>[];
50
+ /** Most recent event */
51
+ lastEvent: SSEEvent<T> | null;
52
+ /** Error if any */
53
+ error: Error | null;
54
+ /** Manually connect */
55
+ connect: () => void;
56
+ /** Manually disconnect */
57
+ disconnect: () => void;
58
+ /** Clear events buffer */
59
+ clearEvents: () => void;
60
+ /** Retry count */
61
+ retryCount: number;
62
+ /** Time since last event (ms) */
63
+ timeSinceLastEvent: number;
64
+ }
65
+ /**
66
+ * React hook for consuming SSE streams with automatic reconnection,
67
+ * event buffering, and state management.
68
+ *
69
+ * @example
70
+ * ```tsx
71
+ * const { events, state, lastEvent } = useSSEStream({
72
+ * url: '/api/v1/tasks/123/stream',
73
+ * authToken: 'Bearer xxx',
74
+ * onEvent: (e) => console.log('Event:', e),
75
+ * });
76
+ *
77
+ * return (
78
+ * <div>
79
+ * <p>Status: {state}</p>
80
+ * {events.map((e, i) => (
81
+ * <div key={i}>{e.event}: {JSON.stringify(e.data)}</div>
82
+ * ))}
83
+ * </div>
84
+ * );
85
+ * ```
86
+ */
87
+ export declare function useSSEStream<T = unknown>(options: UseSSEStreamOptions<T>): UseSSEStreamResult<T>;
88
+ /**
89
+ * Typed event types for common streaming scenarios
90
+ */
91
+ export interface TaskStreamEvent {
92
+ task_id: string;
93
+ status?: string;
94
+ progress?: number;
95
+ message?: string;
96
+ result?: unknown;
97
+ error?: string;
98
+ }
99
+ export interface AgentStreamEvent {
100
+ type: "message.updated" | "tool_call" | "tool_result" | "llm_response" | "error" | "session.idle" | "execution.started" | "execution.result";
101
+ data: unknown;
102
+ timestamp?: number;
103
+ }
104
+ export interface TerminalStreamEvent {
105
+ type: "output" | "input" | "error" | "exit";
106
+ data: string;
107
+ timestamp: number;
108
+ }
109
+ export interface AutomationStreamEvent {
110
+ automation_id: string;
111
+ event_id?: string;
112
+ status?: string;
113
+ variant_id?: string;
114
+ output?: string;
115
+ action_result?: unknown;
116
+ error?: string;
117
+ }
118
+ export interface BotStreamEvent {
119
+ bot_id: string;
120
+ type: "balance" | "trade" | "decision" | "log" | "error";
121
+ data: unknown;
122
+ timestamp: number;
123
+ }
124
+ //#endregion
@@ -0,0 +1,229 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ //#region src/hooks/use-sse-stream.ts
4
+ /**
5
+ * React hook for consuming SSE streams with automatic reconnection,
6
+ * event buffering, and state management.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { events, state, lastEvent } = useSSEStream({
11
+ * url: '/api/v1/tasks/123/stream',
12
+ * authToken: 'Bearer xxx',
13
+ * onEvent: (e) => console.log('Event:', e),
14
+ * });
15
+ *
16
+ * return (
17
+ * <div>
18
+ * <p>Status: {state}</p>
19
+ * {events.map((e, i) => (
20
+ * <div key={i}>{e.event}: {JSON.stringify(e.data)}</div>
21
+ * ))}
22
+ * </div>
23
+ * );
24
+ * ```
25
+ */
26
+ function useSSEStream(options) {
27
+ const { url, authToken, autoReconnect = true, maxRetries = 5, reconnectDelay = 1e3, eventTypes, onEvent, onStateChange, onError, headers, enabled = true } = options;
28
+ const [state, setState] = React.useState("disconnected");
29
+ const [events, setEvents] = React.useState([]);
30
+ const [lastEvent, setLastEvent] = React.useState(null);
31
+ const [error, setError] = React.useState(null);
32
+ const [lastEventTime, setLastEventTime] = React.useState(Date.now());
33
+ const [timeSinceLastEvent, setTimeSinceLastEvent] = React.useState(0);
34
+ const retryCountRef = React.useRef(0);
35
+ const eventSourceRef = React.useRef(null);
36
+ const abortControllerRef = React.useRef(null);
37
+ const reconnectTimeoutRef = React.useRef(null);
38
+ const lastEventIdRef = React.useRef(void 0);
39
+ React.useEffect(() => {
40
+ const interval = setInterval(() => {
41
+ setTimeSinceLastEvent(Date.now() - lastEventTime);
42
+ }, 1e3);
43
+ return () => clearInterval(interval);
44
+ }, [lastEventTime]);
45
+ React.useEffect(() => {
46
+ onStateChange?.(state);
47
+ }, [state, onStateChange]);
48
+ const handleEvent = React.useCallback((eventType, data, id) => {
49
+ const event = {
50
+ id,
51
+ event: eventType,
52
+ data,
53
+ timestamp: Date.now()
54
+ };
55
+ if (id) lastEventIdRef.current = id;
56
+ setLastEventTime(Date.now());
57
+ setLastEvent(event);
58
+ setEvents((prev) => {
59
+ const next = [...prev, event];
60
+ return next.length > 1e3 ? next.slice(-1e3) : next;
61
+ });
62
+ onEvent?.(event);
63
+ }, [onEvent]);
64
+ const disconnect = React.useCallback(() => {
65
+ if (reconnectTimeoutRef.current) {
66
+ clearTimeout(reconnectTimeoutRef.current);
67
+ reconnectTimeoutRef.current = null;
68
+ }
69
+ if (abortControllerRef.current) {
70
+ abortControllerRef.current.abort();
71
+ abortControllerRef.current = null;
72
+ }
73
+ if (eventSourceRef.current) {
74
+ eventSourceRef.current.close();
75
+ eventSourceRef.current = null;
76
+ }
77
+ setState("disconnected");
78
+ }, []);
79
+ const connect = React.useCallback(() => {
80
+ disconnect();
81
+ if (!url || !enabled) return;
82
+ setState("connecting");
83
+ setError(null);
84
+ const connectUrl = new URL(url, window.location.origin);
85
+ if (lastEventIdRef.current) connectUrl.searchParams.set("lastEventId", lastEventIdRef.current);
86
+ if (authToken || headers) {
87
+ abortControllerRef.current = new AbortController();
88
+ const fetchHeaders = {
89
+ Accept: "text/event-stream",
90
+ "Cache-Control": "no-cache",
91
+ ...headers
92
+ };
93
+ if (authToken) fetchHeaders.Authorization = authToken.startsWith("Bearer ") ? authToken : `Bearer ${authToken}`;
94
+ if (lastEventIdRef.current) fetchHeaders["Last-Event-ID"] = lastEventIdRef.current;
95
+ fetch(connectUrl.toString(), {
96
+ headers: fetchHeaders,
97
+ signal: abortControllerRef.current.signal
98
+ }).then(async (response) => {
99
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
100
+ if (!response.body) throw new Error("Response body is null");
101
+ setState("connected");
102
+ retryCountRef.current = 0;
103
+ const reader = response.body.getReader();
104
+ const decoder = new TextDecoder();
105
+ let buffer = "";
106
+ while (true) {
107
+ const { done, value } = await reader.read();
108
+ if (done) break;
109
+ buffer += decoder.decode(value, { stream: true });
110
+ const lines = buffer.split("\n");
111
+ buffer = lines.pop() || "";
112
+ let currentEvent = "";
113
+ let currentData = "";
114
+ let currentId;
115
+ for (const line of lines) if (line.startsWith("event:")) currentEvent = line.slice(6).trim();
116
+ else if (line.startsWith("data:")) currentData += (currentData ? "\n" : "") + line.slice(5).trim();
117
+ else if (line.startsWith("id:")) currentId = line.slice(3).trim();
118
+ else if (line === "" && currentData) {
119
+ try {
120
+ const parsedData = JSON.parse(currentData);
121
+ const eventType = currentEvent || "message";
122
+ if (!eventTypes || eventTypes.includes(eventType)) handleEvent(eventType, parsedData, currentId);
123
+ } catch {
124
+ if (!eventTypes || eventTypes.includes(currentEvent || "message")) handleEvent(currentEvent || "message", currentData, currentId);
125
+ }
126
+ currentEvent = "";
127
+ currentData = "";
128
+ currentId = void 0;
129
+ }
130
+ }
131
+ if (autoReconnect && enabled) {
132
+ setState("reconnecting");
133
+ const delay = reconnectDelay * 2 ** retryCountRef.current;
134
+ reconnectTimeoutRef.current = setTimeout(() => {
135
+ retryCountRef.current += 1;
136
+ connect();
137
+ }, Math.min(delay, 3e4));
138
+ } else setState("disconnected");
139
+ }).catch((err) => {
140
+ if (err.name === "AbortError") return;
141
+ setError(err);
142
+ onError?.(err);
143
+ setState("error");
144
+ if (autoReconnect && enabled && retryCountRef.current < maxRetries) {
145
+ setState("reconnecting");
146
+ const delay = reconnectDelay * 2 ** retryCountRef.current;
147
+ reconnectTimeoutRef.current = setTimeout(() => {
148
+ retryCountRef.current += 1;
149
+ connect();
150
+ }, Math.min(delay, 3e4));
151
+ }
152
+ });
153
+ } else {
154
+ const es = new EventSource(connectUrl.toString());
155
+ eventSourceRef.current = es;
156
+ es.onopen = () => {
157
+ setState("connected");
158
+ retryCountRef.current = 0;
159
+ };
160
+ es.onerror = () => {
161
+ const err = /* @__PURE__ */ new Error("EventSource connection error");
162
+ setError(err);
163
+ onError?.(err);
164
+ if (autoReconnect && enabled && retryCountRef.current < maxRetries) {
165
+ setState("reconnecting");
166
+ es.close();
167
+ const delay = reconnectDelay * 2 ** retryCountRef.current;
168
+ reconnectTimeoutRef.current = setTimeout(() => {
169
+ retryCountRef.current += 1;
170
+ connect();
171
+ }, Math.min(delay, 3e4));
172
+ } else setState("error");
173
+ };
174
+ const types = eventTypes || ["message"];
175
+ for (const type of types) es.addEventListener(type, (e) => {
176
+ try {
177
+ const data = JSON.parse(e.data);
178
+ handleEvent(type, data, e.lastEventId);
179
+ } catch {
180
+ handleEvent(type, e.data, e.lastEventId);
181
+ }
182
+ });
183
+ if (!eventTypes) es.onmessage = (e) => {
184
+ try {
185
+ const data = JSON.parse(e.data);
186
+ handleEvent("message", data, e.lastEventId);
187
+ } catch {
188
+ handleEvent("message", e.data, e.lastEventId);
189
+ }
190
+ };
191
+ }
192
+ }, [
193
+ url,
194
+ authToken,
195
+ headers,
196
+ enabled,
197
+ autoReconnect,
198
+ maxRetries,
199
+ reconnectDelay,
200
+ eventTypes,
201
+ handleEvent,
202
+ onError,
203
+ disconnect
204
+ ]);
205
+ React.useEffect(() => {
206
+ if (enabled) connect();
207
+ return () => disconnect();
208
+ }, [
209
+ enabled,
210
+ connect,
211
+ disconnect
212
+ ]);
213
+ return {
214
+ state,
215
+ events,
216
+ lastEvent,
217
+ error,
218
+ connect,
219
+ disconnect,
220
+ clearEvents: React.useCallback(() => {
221
+ setEvents([]);
222
+ setLastEvent(null);
223
+ }, []),
224
+ retryCount: retryCountRef.current,
225
+ timeSinceLastEvent
226
+ };
227
+ }
228
+ //#endregion
229
+ export { useSSEStream };
@@ -0,0 +1,23 @@
1
+ import { FeedSegment } from "../run/tool-call-feed.js";
2
+ //#region src/hooks/use-tool-call-stream.d.ts
3
+ export interface UseToolCallStreamReturn {
4
+ /** Current feed segments (text + tool calls interleaved) */
5
+ segments: FeedSegment[];
6
+ /** Push a raw SSE event into the stream */
7
+ pushEvent: (event: {
8
+ type: string;
9
+ data: Record<string, unknown>;
10
+ }) => void;
11
+ /** Push a text delta (from message.part.updated) */
12
+ pushText: (delta: string) => void;
13
+ /** Mark a tool call as complete */
14
+ completeToolCall: (id: string, result: {
15
+ output?: string;
16
+ error?: string;
17
+ duration?: number;
18
+ }) => void;
19
+ /** Reset the stream */
20
+ reset: () => void;
21
+ }
22
+ export declare function useToolCallStream(): UseToolCallStreamReturn;
23
+ //#endregion