ai-schadcn-chat 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +307 -0
  3. package/dist/ChatPanel-Cyq7boWQ.js +3468 -0
  4. package/dist/ChatPanel-Cyq7boWQ.js.map +1 -0
  5. package/dist/ChatPanel-D3VL5Vdq.cjs +10 -0
  6. package/dist/ChatPanel-D3VL5Vdq.cjs.map +1 -0
  7. package/dist/ChatProvider-DcrNbVMh.cjs +2 -0
  8. package/dist/ChatProvider-DcrNbVMh.cjs.map +1 -0
  9. package/dist/ChatProvider-DoD4jj4M.js +128 -0
  10. package/dist/ChatProvider-DoD4jj4M.js.map +1 -0
  11. package/dist/chat-engine-BaFdimfx.cjs +8 -0
  12. package/dist/chat-engine-BaFdimfx.cjs.map +1 -0
  13. package/dist/chat-engine-Cja0KtWE.js +520 -0
  14. package/dist/chat-engine-Cja0KtWE.js.map +1 -0
  15. package/dist/components/chat/AgentManager.d.ts +11 -0
  16. package/dist/components/chat/AgentManager.d.ts.map +1 -0
  17. package/dist/components/chat/AgentManager.js +110 -0
  18. package/dist/components/chat/Attachment.d.ts +34 -0
  19. package/dist/components/chat/Attachment.d.ts.map +1 -0
  20. package/dist/components/chat/Attachment.js +59 -0
  21. package/dist/components/chat/AttachmentPreview.d.ts +8 -0
  22. package/dist/components/chat/AttachmentPreview.d.ts.map +1 -0
  23. package/dist/components/chat/AttachmentPreview.js +43 -0
  24. package/dist/components/chat/ChatComposer.d.ts +11 -0
  25. package/dist/components/chat/ChatComposer.d.ts.map +1 -0
  26. package/dist/components/chat/ChatComposer.js +10 -0
  27. package/dist/components/chat/ChatHeader.d.ts +5 -0
  28. package/dist/components/chat/ChatHeader.d.ts.map +1 -0
  29. package/dist/components/chat/ChatHeader.js +132 -0
  30. package/dist/components/chat/ChatPanel.d.ts +36 -0
  31. package/dist/components/chat/ChatPanel.d.ts.map +1 -0
  32. package/dist/components/chat/ChatPanel.js +55 -0
  33. package/dist/components/chat/ChatProvider.d.ts +58 -0
  34. package/dist/components/chat/ChatProvider.d.ts.map +1 -0
  35. package/dist/components/chat/ChatProvider.js +145 -0
  36. package/dist/components/chat/Chatbubble.d.ts +30 -0
  37. package/dist/components/chat/Chatbubble.d.ts.map +1 -0
  38. package/dist/components/chat/Chatbubble.js +50 -0
  39. package/dist/components/chat/Markdown.d.ts +35 -0
  40. package/dist/components/chat/Markdown.d.ts.map +1 -0
  41. package/dist/components/chat/Markdown.js +130 -0
  42. package/dist/components/chat/Message.d.ts +22 -0
  43. package/dist/components/chat/Message.d.ts.map +1 -0
  44. package/dist/components/chat/Message.js +21 -0
  45. package/dist/components/chat/MessageInput.d.ts +24 -0
  46. package/dist/components/chat/MessageInput.d.ts.map +1 -0
  47. package/dist/components/chat/MessageInput.js +255 -0
  48. package/dist/components/chat/MessageList.d.ts +9 -0
  49. package/dist/components/chat/MessageList.d.ts.map +1 -0
  50. package/dist/components/chat/MessageList.js +249 -0
  51. package/dist/components/chat/MessageMarker.d.ts +25 -0
  52. package/dist/components/chat/MessageMarker.d.ts.map +1 -0
  53. package/dist/components/chat/MessageMarker.js +31 -0
  54. package/dist/components/chat/MessageScroller.d.ts +12 -0
  55. package/dist/components/chat/MessageScroller.d.ts.map +1 -0
  56. package/dist/components/chat/MessageScroller.js +48 -0
  57. package/dist/components/chat/MessageScrollerPrimitives.d.ts +76 -0
  58. package/dist/components/chat/MessageScrollerPrimitives.d.ts.map +1 -0
  59. package/dist/components/chat/MessageScrollerPrimitives.js +438 -0
  60. package/dist/components/chat/ProviderManager.d.ts +10 -0
  61. package/dist/components/chat/ProviderManager.d.ts.map +1 -0
  62. package/dist/components/chat/ProviderManager.js +110 -0
  63. package/dist/components/chat/ReasoningPanel.d.ts +15 -0
  64. package/dist/components/chat/ReasoningPanel.d.ts.map +1 -0
  65. package/dist/components/chat/ReasoningPanel.js +40 -0
  66. package/dist/components/chat/ThemeToggle.d.ts +4 -0
  67. package/dist/components/chat/ThemeToggle.d.ts.map +1 -0
  68. package/dist/components/chat/ThemeToggle.js +59 -0
  69. package/dist/components/index.d.ts +44 -0
  70. package/dist/components/index.d.ts.map +1 -0
  71. package/dist/components/index.js +49 -0
  72. package/dist/components/ui/avatar.d.ts +11 -0
  73. package/dist/components/ui/avatar.d.ts.map +1 -0
  74. package/dist/components/ui/avatar.js +33 -0
  75. package/dist/components/ui/badge.d.ts +10 -0
  76. package/dist/components/ui/badge.d.ts.map +1 -0
  77. package/dist/components/ui/badge.js +19 -0
  78. package/dist/components/ui/button.d.ts +12 -0
  79. package/dist/components/ui/button.d.ts.map +1 -0
  80. package/dist/components/ui/button.js +34 -0
  81. package/dist/components/ui/card.d.ts +8 -0
  82. package/dist/components/ui/card.d.ts.map +1 -0
  83. package/dist/components/ui/card.js +15 -0
  84. package/dist/components/ui/checkbox.d.ts +5 -0
  85. package/dist/components/ui/checkbox.d.ts.map +1 -0
  86. package/dist/components/ui/checkbox.js +8 -0
  87. package/dist/components/ui/collapsible.d.ts +6 -0
  88. package/dist/components/ui/collapsible.d.ts.map +1 -0
  89. package/dist/components/ui/collapsible.js +5 -0
  90. package/dist/components/ui/dialog.d.ts +24 -0
  91. package/dist/components/ui/dialog.d.ts.map +1 -0
  92. package/dist/components/ui/dialog.js +30 -0
  93. package/dist/components/ui/dropdown-menu.d.ts +17 -0
  94. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  95. package/dist/components/ui/dropdown-menu.js +20 -0
  96. package/dist/components/ui/input.d.ts +4 -0
  97. package/dist/components/ui/input.d.ts.map +1 -0
  98. package/dist/components/ui/input.js +7 -0
  99. package/dist/components/ui/label.d.ts +6 -0
  100. package/dist/components/ui/label.d.ts.map +1 -0
  101. package/dist/components/ui/label.js +10 -0
  102. package/dist/components/ui/popover.d.ts +6 -0
  103. package/dist/components/ui/popover.d.ts.map +1 -0
  104. package/dist/components/ui/popover.js +9 -0
  105. package/dist/components/ui/scroll-area.d.ts +3 -0
  106. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  107. package/dist/components/ui/scroll-area.js +8 -0
  108. package/dist/components/ui/select.d.ts +14 -0
  109. package/dist/components/ui/select.d.ts.map +1 -0
  110. package/dist/components/ui/select.js +26 -0
  111. package/dist/components/ui/separator.d.ts +3 -0
  112. package/dist/components/ui/separator.d.ts.map +1 -0
  113. package/dist/components/ui/separator.js +6 -0
  114. package/dist/components/ui/switch.d.ts +3 -0
  115. package/dist/components/ui/switch.d.ts.map +1 -0
  116. package/dist/components/ui/switch.js +6 -0
  117. package/dist/components/ui/tabs.d.ts +6 -0
  118. package/dist/components/ui/tabs.d.ts.map +1 -0
  119. package/dist/components/ui/tabs.js +11 -0
  120. package/dist/components/ui/tooltip.d.ts +6 -0
  121. package/dist/components/ui/tooltip.d.ts.map +1 -0
  122. package/dist/components/ui/tooltip.js +9 -0
  123. package/dist/components.cjs +2 -0
  124. package/dist/components.cjs.map +1 -0
  125. package/dist/components.js +129 -0
  126. package/dist/components.js.map +1 -0
  127. package/dist/hooks/index.d.ts +2 -0
  128. package/dist/hooks/index.d.ts.map +1 -0
  129. package/dist/hooks/index.js +3 -0
  130. package/dist/hooks/useChat.d.ts +6 -0
  131. package/dist/hooks/useChat.d.ts.map +1 -0
  132. package/dist/hooks/useChat.js +5 -0
  133. package/dist/hooks.cjs +2 -0
  134. package/dist/hooks.cjs.map +1 -0
  135. package/dist/hooks.js +8 -0
  136. package/dist/hooks.js.map +1 -0
  137. package/dist/index-Ci4qkGOp.cjs +20 -0
  138. package/dist/index-Ci4qkGOp.cjs.map +1 -0
  139. package/dist/index-NhYr-cEJ.js +567 -0
  140. package/dist/index-NhYr-cEJ.js.map +1 -0
  141. package/dist/index.cjs +2 -0
  142. package/dist/index.cjs.map +1 -0
  143. package/dist/index.d.ts +33 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +32 -0
  146. package/dist/index.js.map +1 -0
  147. package/dist/lib/agents.d.ts +29 -0
  148. package/dist/lib/agents.d.ts.map +1 -0
  149. package/dist/lib/agents.js +145 -0
  150. package/dist/lib/attachments.d.ts +40 -0
  151. package/dist/lib/attachments.d.ts.map +1 -0
  152. package/dist/lib/attachments.js +152 -0
  153. package/dist/lib/chat-engine.d.ts +109 -0
  154. package/dist/lib/chat-engine.d.ts.map +1 -0
  155. package/dist/lib/chat-engine.js +505 -0
  156. package/dist/lib/index.d.ts +8 -0
  157. package/dist/lib/index.d.ts.map +1 -0
  158. package/dist/lib/index.js +7 -0
  159. package/dist/lib/persistence.d.ts +30 -0
  160. package/dist/lib/persistence.d.ts.map +1 -0
  161. package/dist/lib/persistence.js +104 -0
  162. package/dist/lib/providers.d.ts +40 -0
  163. package/dist/lib/providers.d.ts.map +1 -0
  164. package/dist/lib/providers.js +0 -0
  165. package/dist/lib/tw-merge.d.ts +9 -0
  166. package/dist/lib/tw-merge.d.ts.map +1 -0
  167. package/dist/lib/tw-merge.js +8 -0
  168. package/dist/lib/utils.d.ts +69 -0
  169. package/dist/lib/utils.d.ts.map +1 -0
  170. package/dist/lib/utils.js +166 -0
  171. package/dist/lib/voice.d.ts +57 -0
  172. package/dist/lib/voice.d.ts.map +1 -0
  173. package/dist/lib/voice.js +103 -0
  174. package/dist/lib.cjs +2 -0
  175. package/dist/lib.cjs.map +1 -0
  176. package/dist/lib.js +59 -0
  177. package/dist/lib.js.map +1 -0
  178. package/dist/marker.css +68 -0
  179. package/dist/package.json +6 -0
  180. package/dist/presets-BxnbImSM.cjs +2 -0
  181. package/dist/presets-BxnbImSM.cjs.map +1 -0
  182. package/dist/presets-D8VQTD8n.js +193 -0
  183. package/dist/presets-D8VQTD8n.js.map +1 -0
  184. package/dist/providers/anthropic.d.ts +30 -0
  185. package/dist/providers/anthropic.d.ts.map +1 -0
  186. package/dist/providers/anthropic.js +251 -0
  187. package/dist/providers/base.d.ts +55 -0
  188. package/dist/providers/base.d.ts.map +1 -0
  189. package/dist/providers/base.js +272 -0
  190. package/dist/providers/index.d.ts +15 -0
  191. package/dist/providers/index.d.ts.map +1 -0
  192. package/dist/providers/index.js +24 -0
  193. package/dist/providers/openai.d.ts +31 -0
  194. package/dist/providers/openai.d.ts.map +1 -0
  195. package/dist/providers/openai.js +192 -0
  196. package/dist/providers-BuaGy2tX.cjs +4 -0
  197. package/dist/providers-BuaGy2tX.cjs.map +1 -0
  198. package/dist/providers-kp-avTNl.js +324 -0
  199. package/dist/providers-kp-avTNl.js.map +1 -0
  200. package/dist/providers.cjs +2 -0
  201. package/dist/providers.cjs.map +1 -0
  202. package/dist/providers.js +14 -0
  203. package/dist/providers.js.map +1 -0
  204. package/dist/scroller.css +278 -0
  205. package/dist/style.css +1 -0
  206. package/dist/types/chat.d.ts +342 -0
  207. package/dist/types/chat.d.ts.map +1 -0
  208. package/dist/types/chat.js +4 -0
  209. package/dist/types/index.d.ts +9 -0
  210. package/dist/types/index.d.ts.map +1 -0
  211. package/dist/types/index.js +14 -0
  212. package/dist/types/presets.d.ts +25 -0
  213. package/dist/types/presets.d.ts.map +1 -0
  214. package/dist/types/presets.js +207 -0
  215. package/dist/types.cjs +2 -0
  216. package/dist/types.cjs.map +1 -0
  217. package/dist/types.js +16 -0
  218. package/dist/types.js.map +1 -0
  219. package/dist/typeset-presets.css +56 -0
  220. package/dist/typeset.css +505 -0
  221. package/package.json +158 -0
@@ -0,0 +1,520 @@
1
+ import { f as A, u as f, b as O, r as M, q as D, v as E, j as k, o as J, h as _ } from "./index-NhYr-cEJ.js";
2
+ async function U(i, t) {
3
+ const e = (t.ui?.maxFileSizeMb ?? 10) * 1024 * 1024, s = new Set(
4
+ t.ui?.acceptedFileTypes ?? [
5
+ "image/png",
6
+ "image/jpeg",
7
+ "image/gif",
8
+ "image/webp",
9
+ "application/pdf",
10
+ "text/plain",
11
+ "text/markdown",
12
+ "text/csv",
13
+ "application/json"
14
+ ]
15
+ ), r = [];
16
+ for (const o of i) {
17
+ if (o.size > e)
18
+ throw new Error(
19
+ `${o.name} is ${A(o.size)} — exceeds the ${A(e)} limit`
20
+ );
21
+ if (s.size > 0 && !s.has(o.type))
22
+ throw new Error(`${o.name} has unsupported type ${o.type || "unknown"}`);
23
+ const c = {
24
+ id: f("att"),
25
+ name: o.name,
26
+ mimeType: o.type || "application/octet-stream",
27
+ size: o.size
28
+ };
29
+ o.type.startsWith("text/") || o.type === "application/json" ? c.text = await O(o) : c.dataUrl = await M(o), r.push(c);
30
+ }
31
+ return r;
32
+ }
33
+ function W(i, t, e) {
34
+ if (!t?.length) return i;
35
+ if (e === "anthropic") {
36
+ const o = D({ text: i, attachments: t });
37
+ return o.length ? JSON.stringify({ __type: "ai-chat-blocks", blocks: o }) : i;
38
+ }
39
+ const r = E({ text: i, attachments: t });
40
+ return r.length ? JSON.stringify({ __type: "ai-chat-blocks", blocks: r }) : i;
41
+ }
42
+ function F(i) {
43
+ if (typeof document > "u") return;
44
+ const t = i.dataUrl ?? i.url;
45
+ if (!t) return;
46
+ const e = document.createElement("a");
47
+ e.href = t, e.download = i.name, e.rel = "noopener", document.body.appendChild(e), e.click(), e.remove();
48
+ }
49
+ function K(i) {
50
+ return i.startsWith("image/") ? "image" : i.startsWith("video/") ? "video" : i.startsWith("audio/") ? "audio" : i === "application/pdf" ? "pdf" : i.startsWith("text/") || i === "application/json" ? "text" : "file";
51
+ }
52
+ function P(i) {
53
+ if (!i.startsWith("{")) return i;
54
+ try {
55
+ const t = JSON.parse(i);
56
+ if (t?.__type === "ai-chat-blocks" && Array.isArray(t.blocks))
57
+ return t.blocks.map((e) => e.type === "text" && typeof e.text == "string" ? e.text : "").filter(Boolean).join(`
58
+ `).trim();
59
+ } catch {
60
+ }
61
+ return i;
62
+ }
63
+ function q(i) {
64
+ let t = i.content;
65
+ if (t.startsWith("{"))
66
+ try {
67
+ const s = JSON.parse(t);
68
+ s?.__type === "ai-chat-blocks" && Array.isArray(s.blocks) && (t = s.blocks.map(
69
+ (r) => r?.type === "text" && typeof r.text == "string" ? r.text : ""
70
+ ).filter(Boolean).join(`
71
+ `));
72
+ } catch {
73
+ }
74
+ const e = (i.attachments ?? []).map((s) => s.name).join(", ");
75
+ return e ? `${t}
76
+
77
+ šŸ“Ž ${e}` : t;
78
+ }
79
+ const v = "ai-schadcn-chat:conversation:";
80
+ function S(i) {
81
+ if (typeof window > "u") return null;
82
+ try {
83
+ const t = window.localStorage.getItem(v + i);
84
+ if (!t) return null;
85
+ const e = JSON.parse(t);
86
+ return e?.messages ? e : null;
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+ function $(i) {
92
+ if (!(typeof window > "u"))
93
+ try {
94
+ const t = i.messages.map((e) => ({
95
+ ...e,
96
+ toolCalls: (e.toolCalls ?? []).map((s) => ({
97
+ ...s,
98
+ result: s.result instanceof Object ? R(s.result) : s.result
99
+ }))
100
+ }));
101
+ window.localStorage.setItem(
102
+ v + i.id,
103
+ JSON.stringify({ ...i, messages: t })
104
+ );
105
+ } catch {
106
+ }
107
+ }
108
+ function j() {
109
+ if (typeof window > "u") return [];
110
+ const i = [];
111
+ for (let t = 0; t < window.localStorage.length; t++) {
112
+ const e = window.localStorage.key(t);
113
+ if (!(!e || !e.startsWith(v)))
114
+ try {
115
+ const s = window.localStorage.getItem(e);
116
+ if (!s) continue;
117
+ const r = JSON.parse(s);
118
+ r?.id && i.push(r);
119
+ } catch {
120
+ }
121
+ }
122
+ return i.sort((t, e) => e.updatedAt - t.updatedAt);
123
+ }
124
+ function G(i) {
125
+ if (!(typeof window > "u"))
126
+ try {
127
+ window.localStorage.removeItem(v + i);
128
+ } catch {
129
+ }
130
+ }
131
+ function X() {
132
+ if (!(typeof window > "u"))
133
+ try {
134
+ const i = [];
135
+ for (let t = 0; t < window.localStorage.length; t++) {
136
+ const e = window.localStorage.key(t);
137
+ e && e.startsWith(v) && i.push(e);
138
+ }
139
+ i.forEach((t) => window.localStorage.removeItem(t));
140
+ } catch {
141
+ }
142
+ }
143
+ function R(i) {
144
+ try {
145
+ return JSON.parse(JSON.stringify(i));
146
+ } catch {
147
+ return;
148
+ }
149
+ }
150
+ function T(i, t = 1e3) {
151
+ return i.messages.length <= t ? i : {
152
+ ...i,
153
+ messages: i.messages.slice(-t)
154
+ };
155
+ }
156
+ class H {
157
+ config;
158
+ provider;
159
+ listeners = /* @__PURE__ */ new Set();
160
+ conversations = /* @__PURE__ */ new Map();
161
+ activeId;
162
+ constructor(t, e) {
163
+ if (this.config = t, this.provider = k(t), this.activeId = e ?? f("conv"), this.config.persistKey) {
164
+ const s = S(this.activeId);
165
+ s ? this.conversations.set(this.activeId, {
166
+ id: this.activeId,
167
+ messages: s.messages
168
+ }) : this.conversations.set(this.activeId, { id: this.activeId, messages: [] });
169
+ } else
170
+ this.conversations.set(this.activeId, { id: this.activeId, messages: [] });
171
+ }
172
+ getConfig() {
173
+ return this.config;
174
+ }
175
+ updateConfig(t) {
176
+ const e = typeof t == "function" ? t(this.config) : t;
177
+ this.config = { ...this.config, ...e }, (e.provider || e.model) && (this.provider = k(this.config)), this.emit();
178
+ }
179
+ getActiveConversationId() {
180
+ return this.activeId;
181
+ }
182
+ /**
183
+ * Return the count of in-flight tool calls for the active conversation.
184
+ *
185
+ * Default returns 0 because the engine does not yet expose tool-call
186
+ * activity tracking publicly — this method is the hook that any future
187
+ * tool-call plumbing can fill in. Consumers reading this in their UI
188
+ * (e.g. a header badge behind `ui.showToolCalls`) will get a sensible
189
+ * default until the engine wires real tool-call state.
190
+ */
191
+ getActiveToolCallCount() {
192
+ return 0;
193
+ }
194
+ setActiveConversationId(t) {
195
+ if (!this.conversations.has(t)) {
196
+ const e = this.config.persistKey ? S(t) : null;
197
+ this.conversations.set(t, { id: t, messages: e?.messages ?? [] });
198
+ }
199
+ this.activeId = t, this.emit();
200
+ }
201
+ listConversationIds() {
202
+ return Array.from(this.conversations.keys());
203
+ }
204
+ /**
205
+ * Rich conversation list for history menus: a title (first user message),
206
+ * last-updated timestamp, and message count. Merges in-memory conversations
207
+ * with persisted ones (persisted wins for title/timestamp). Sorted newest
208
+ * first; the active conversation is always present.
209
+ */
210
+ listConversationsMeta() {
211
+ const t = /* @__PURE__ */ new Map();
212
+ for (const e of this.conversations.values())
213
+ t.set(e.id, {
214
+ id: e.id,
215
+ title: z(e.messages),
216
+ updatedAt: 0,
217
+ messageCount: e.messages.length
218
+ });
219
+ if (this.config.persistKey)
220
+ for (const e of j()) {
221
+ const s = t.get(e.id);
222
+ t.set(e.id, {
223
+ id: e.id,
224
+ title: e.title || s?.title || "New chat",
225
+ updatedAt: e.updatedAt,
226
+ messageCount: e.messages.length
227
+ });
228
+ }
229
+ return Array.from(t.values()).sort((e, s) => s.updatedAt - e.updatedAt);
230
+ }
231
+ getMessages() {
232
+ return this.conversations.get(this.activeId)?.messages ?? [];
233
+ }
234
+ /**
235
+ * Returns a shallow copy of the active conversation's messages,
236
+ * cached between `emit()` calls and only rebuilt when the engine
237
+ * signals a real change.
238
+ *
239
+ * Why this exists: React bindings consume this via `useSyncExternalStore`,
240
+ * which bails out when the snapshot reference is identical to the
241
+ * previous one (that's the contract). Because we mutate `messages`
242
+ * in place (`push`, etc.) the underlying array reference never changes
243
+ * — so the user bubble would never render. The fix is to cache a fresh
244
+ * copy between `emit()` calls and invalidate the cache when something
245
+ * actually changes. Returning a NEW array on every call would itself
246
+ * infinite-loop the snapshot.
247
+ */
248
+ snapshotCache = [];
249
+ snapshotDirty = !0;
250
+ getMessagesSnapshot() {
251
+ if (this.snapshotDirty) {
252
+ const t = this.conversations.get(this.activeId)?.messages;
253
+ this.snapshotCache = t ? [...t] : [], this.snapshotDirty = !1;
254
+ }
255
+ return this.snapshotCache;
256
+ }
257
+ subscribe(t) {
258
+ return this.listeners.add(t), () => this.listeners.delete(t);
259
+ }
260
+ emit() {
261
+ this.snapshotDirty = !0;
262
+ for (const t of this.listeners) t();
263
+ }
264
+ persist() {
265
+ if (!this.config.persistKey) return;
266
+ const t = this.conversations.get(this.activeId);
267
+ if (!t) return;
268
+ const e = t.messages.find((r) => r.role === "user"), s = e ? b(e.content.replace(/[\n\r]+/g, " "), 60) : "New chat";
269
+ $(
270
+ T({
271
+ id: t.id,
272
+ title: s,
273
+ messages: t.messages,
274
+ updatedAt: Date.now(),
275
+ config: { model: this.config.model.id, temperature: this.config.temperature }
276
+ })
277
+ );
278
+ }
279
+ /**
280
+ * Public: stop the currently streaming response, if any.
281
+ */
282
+ abort() {
283
+ this.provider.abort();
284
+ }
285
+ /**
286
+ * Public: clear the active conversation.
287
+ */
288
+ clear() {
289
+ this.conversations.set(this.activeId, { id: this.activeId, messages: [] }), this.persist(), this.emit();
290
+ }
291
+ /**
292
+ * Public: switch the active conversation (creates it if missing).
293
+ */
294
+ newConversation() {
295
+ const t = f("conv");
296
+ return this.conversations.set(t, { id: t, messages: [] }), this.activeId = t, this.persist(), this.emit(), t;
297
+ }
298
+ /** Public: remove a single message from the active conversation. */
299
+ deleteMessage(t) {
300
+ const e = this.conversations.get(this.activeId);
301
+ e && (e.messages = e.messages.filter((s) => s.id !== t), this.persist(), this.emit());
302
+ }
303
+ deleteConversation(t) {
304
+ this.conversations.delete(t), this.activeId === t && (this.activeId = this.conversations.keys().next().value ?? f("conv"), this.conversations.set(this.activeId, { id: this.activeId, messages: [] })), this.persist(), this.emit();
305
+ }
306
+ /**
307
+ * Public: edit a previous user message and re-run the assistant from there.
308
+ * Truncates all messages after `messageId` and triggers a new completion.
309
+ */
310
+ async editAndResend(t, e) {
311
+ const s = this.conversations.get(this.activeId);
312
+ if (!s) return;
313
+ const r = s.messages.findIndex((o) => o.id === t);
314
+ r < 0 || s.messages[r].role !== "user" || (s.messages[r] = { ...s.messages[r], content: e, status: "complete" }, s.messages = s.messages.slice(0, r + 1), this.emit(), this.persist(), await this.runCompletion());
315
+ }
316
+ /**
317
+ * Public: re-run the assistant for the most recent user turn.
318
+ */
319
+ async regenerate() {
320
+ const t = this.conversations.get(this.activeId);
321
+ if (t) {
322
+ for (let e = t.messages.length - 1; e >= 0; e--) {
323
+ if (t.messages[e].role === "assistant") {
324
+ t.messages.splice(e, 1);
325
+ break;
326
+ }
327
+ if (t.messages[e].role === "user") break;
328
+ }
329
+ this.emit(), this.persist(), await this.runCompletion();
330
+ }
331
+ }
332
+ /**
333
+ * Public: send a user message (optionally with attachments) and stream the
334
+ * assistant reply.
335
+ */
336
+ async sendUserMessage(t, e) {
337
+ const s = this.conversations.get(this.activeId);
338
+ if (!s || !t.trim() && !e?.length) return;
339
+ const r = W(
340
+ t,
341
+ e,
342
+ this.config.model.provider ?? this.config.provider.kind
343
+ ), o = {
344
+ id: f("msg"),
345
+ role: "user",
346
+ content: r,
347
+ attachments: e,
348
+ createdAt: Date.now(),
349
+ status: "complete"
350
+ };
351
+ s.messages.push(o), this.emit(), this.persist(), await this.runCompletion();
352
+ }
353
+ /**
354
+ * Public: send files (browser File objects) directly. Convenience wrapper
355
+ * around sendUserMessage + filesToAttachments.
356
+ */
357
+ async sendFiles(t, e) {
358
+ const s = await U(e, this.config);
359
+ return this.sendUserMessage(t, s);
360
+ }
361
+ /**
362
+ * Internal: stream one assistant turn. Handles tool calls in a loop
363
+ * (max 8 iterations to prevent infinite loops).
364
+ */
365
+ async runCompletion() {
366
+ const t = this.conversations.get(this.activeId);
367
+ if (!t) return;
368
+ const e = f("msg");
369
+ let s = {
370
+ id: e,
371
+ role: "assistant",
372
+ content: "",
373
+ createdAt: Date.now(),
374
+ status: "streaming"
375
+ };
376
+ t.messages.push(s);
377
+ const r = 8;
378
+ for (let o = 0; o < r; o++)
379
+ try {
380
+ const { toolCalls: c, finished: d, finalUsage: h, model: g } = await this.streamAssistantTurn(s, t);
381
+ if (c.length > 0 && this.config.tools?.length) {
382
+ s.status = "complete", s.toolCalls = c.map((a) => ({
383
+ id: a.id,
384
+ name: a.name,
385
+ arguments: a.arguments,
386
+ status: a.error ? "error" : "complete",
387
+ result: a.result,
388
+ error: a.error
389
+ }));
390
+ for (const a of c) {
391
+ const C = this.config.tools?.find((p) => p.name === a.name), m = {
392
+ id: f("msg"),
393
+ role: "tool",
394
+ content: a.error ? `Error: ${a.error}` : typeof a.result == "string" ? a.result : JSON.stringify(a.result ?? null),
395
+ createdAt: Date.now(),
396
+ status: "complete",
397
+ toolCallId: a.id,
398
+ name: a.name
399
+ };
400
+ t.messages.push(m), this.emit(), this.persist(), !a.error && o < r - 1 && (s = {
401
+ id: f("msg"),
402
+ role: "assistant",
403
+ content: "",
404
+ createdAt: Date.now(),
405
+ status: "streaming"
406
+ }, t.messages.push(s));
407
+ }
408
+ if (!d) continue;
409
+ break;
410
+ }
411
+ if (d) {
412
+ s.status = "complete", s.completedAt = Date.now(), h && (s.usage = h), g && (s.model = g);
413
+ break;
414
+ }
415
+ } catch (c) {
416
+ const d = J(c);
417
+ s.status = "error", s.error = d, this.config.onError?.(d, { conversationId: this.activeId }), this.emit(), this.persist();
418
+ return;
419
+ }
420
+ this.config.onResponse?.(s, { conversationId: this.activeId }), this.emit(), this.persist();
421
+ }
422
+ /**
423
+ * Streams one assistant message. Returns:
424
+ * - toolCalls: tool_use / tool_calls chunks collected during the stream
425
+ * - finished: true if the API emitted a non-tool finish reason
426
+ * - finalUsage / model: usage & model metadata when available
427
+ */
428
+ async streamAssistantTurn(t, e) {
429
+ const s = this.config.maxContextTokens ?? 32e3, r = _(e.messages, s), o = /* @__PURE__ */ new Map();
430
+ let c = "", d = "", h, g, a;
431
+ const C = this.provider.stream(
432
+ {
433
+ messages: r,
434
+ signal: void 0
435
+ },
436
+ this.config
437
+ );
438
+ let m;
439
+ for await (const n of C) {
440
+ if (n.error) {
441
+ m = n.error, c += n.error.message ? `
442
+
443
+ [error] ${n.error.message}` : "", h = "error";
444
+ break;
445
+ }
446
+ if (n.model && (a = n.model), n.usage && (g = n.usage), n.finishReason && (h = n.finishReason), n.delta && (t.startedAt || (t.startedAt = Date.now()), n.delta.startsWith("\0think:") ? (d += n.delta.slice(7), t.reasoning = d) : (c += n.delta, t.content = c), this.emit()), n.toolCall) {
447
+ const l = o.get(n.toolCall.id) ?? {
448
+ id: n.toolCall.id,
449
+ name: n.toolCall.name,
450
+ arguments: "",
451
+ raw: ""
452
+ };
453
+ n.toolCall.name && (l.name = n.toolCall.name), n.toolCall.arguments && (l.raw += n.toolCall.arguments), l.arguments = l.raw, o.set(n.toolCall.id, l), this.emit();
454
+ }
455
+ }
456
+ t.content = c;
457
+ const p = [];
458
+ for (const n of o.values()) {
459
+ let l = {};
460
+ if (typeof n.raw == "string" && n.raw.trim().length > 0)
461
+ try {
462
+ l = JSON.parse(n.raw);
463
+ } catch {
464
+ l = { raw: n.raw };
465
+ }
466
+ const I = this.config.tools?.find((u) => u.name === n.name);
467
+ let w, y;
468
+ if (I?.handler)
469
+ try {
470
+ w = await I.handler(l, { conversationId: this.activeId });
471
+ } catch (u) {
472
+ y = (u instanceof Error ? u.message : String(u)) || "Tool failed";
473
+ }
474
+ else
475
+ w = { ok: !1, message: `No handler registered for tool "${n.name}".` };
476
+ p.push({
477
+ id: n.id,
478
+ name: n.name,
479
+ arguments: l,
480
+ status: y ? "error" : "complete",
481
+ result: w,
482
+ error: y
483
+ });
484
+ }
485
+ if (m) throw m;
486
+ const x = h === "stop" || h === "length" || h === "content_filter", N = p.length > 0;
487
+ return { toolCalls: p, finished: x && !N, finalUsage: g, model: a };
488
+ }
489
+ }
490
+ function b(i, t) {
491
+ return i.length <= t ? i : `${i.slice(0, t - 1)}…`;
492
+ }
493
+ function z(i) {
494
+ const t = i.find((s) => s.role === "user");
495
+ if (!t) return "New chat";
496
+ let e = t.content;
497
+ if (e.startsWith("{"))
498
+ try {
499
+ const s = JSON.parse(e);
500
+ s?.__type === "ai-chat-blocks" && (e = s.blocks?.map((r) => r.type === "text" ? r.text ?? "" : "").filter(Boolean).join(" ") || "Attachment");
501
+ } catch {
502
+ }
503
+ return b(e.replace(/[\n\r]+/g, " ").trim(), 60) || "New chat";
504
+ }
505
+ export {
506
+ H as C,
507
+ K as a,
508
+ F as b,
509
+ X as c,
510
+ G as d,
511
+ P as e,
512
+ U as f,
513
+ S as g,
514
+ W as h,
515
+ q as i,
516
+ j as l,
517
+ $ as s,
518
+ T as t
519
+ };
520
+ //# sourceMappingURL=chat-engine-Cja0KtWE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-engine-Cja0KtWE.js","sources":["../src/lib/attachments.ts","../src/lib/persistence.ts","../src/lib/chat-engine.ts"],"sourcesContent":["import type { AttachmentMeta, ChatConfig, ChatMessage } from \"../types/chat.js\";\nimport { formatBytes, readFileAsDataUrl, readFileAsText, uid } from \"./utils.js\";\nimport { buildAnthropicUserContent } from \"../providers/anthropic.js\";\nimport { buildOpenAIUserContent } from \"../providers/openai.js\";\n\n/**\n * Convert a list of browser File objects into AttachmentMeta records.\n * Large text files (.md/.txt/.csv/.json) are read as text for cheap\n * context-injection. Binary files are stored as data URLs for vision\n * models (subject to the cfg.ui.maxFileSizeMb cap).\n */\nexport async function filesToAttachments(\n files: File[],\n cfg: ChatConfig,\n): Promise<AttachmentMeta[]> {\n const maxBytes = (cfg.ui?.maxFileSizeMb ?? 10) * 1024 * 1024;\n const accepted = new Set<string>(\n cfg.ui?.acceptedFileTypes ?? [\n \"image/png\",\n \"image/jpeg\",\n \"image/gif\",\n \"image/webp\",\n \"application/pdf\",\n \"text/plain\",\n \"text/markdown\",\n \"text/csv\",\n \"application/json\",\n ],\n );\n\n const out: AttachmentMeta[] = [];\n for (const file of files) {\n if (file.size > maxBytes) {\n throw new Error(\n `${file.name} is ${formatBytes(file.size)} — exceeds the ${formatBytes(maxBytes)} limit`,\n );\n }\n if (accepted.size > 0 && !accepted.has(file.type)) {\n throw new Error(`${file.name} has unsupported type ${file.type || \"unknown\"}`);\n }\n const att: AttachmentMeta = {\n id: uid(\"att\"),\n name: file.name,\n mimeType: file.type || \"application/octet-stream\",\n size: file.size,\n };\n if (file.type.startsWith(\"text/\") || file.type === \"application/json\") {\n att.text = await readFileAsText(file);\n } else {\n att.dataUrl = await readFileAsDataUrl(file);\n }\n out.push(att);\n }\n return out;\n}\n\n/**\n * Serialize a (text, attachments) tuple into the structured user content\n * the provider adapters recognize. Returns either a plain string (most\n * turns) or a JSON-encoded marker (`{__type:\"ai-chat-blocks\",blocks:[...]}`).\n *\n * Adapters inspect the marker and expand it into native content blocks.\n */\nexport function serializeUserContent(\n text: string,\n attachments: AttachmentMeta[] | undefined,\n providerKind: ChatConfig[\"provider\"][\"kind\"],\n): string {\n if (!attachments?.length) return text;\n const provider = providerKind;\n if (provider === \"anthropic\") {\n const blocks = buildAnthropicUserContent({ text, attachments });\n if (!blocks.length) return text;\n return JSON.stringify({ __type: \"ai-chat-blocks\", blocks });\n }\n // OpenAI / openai-compatible share the same multimodal format.\n const blocks = buildOpenAIUserContent({ text, attachments });\n if (!blocks.length) return text;\n return JSON.stringify({ __type: \"ai-chat-blocks\", blocks });\n}\n\n/**\n * Download an attachment by triggering a synthetic anchor click.\n * Used for \"Download\" buttons next to each file chip in the UI.\n */\nexport function downloadAttachment(att: AttachmentMeta): void {\n if (typeof document === \"undefined\") return;\n const href = att.dataUrl ?? att.url;\n if (!href) return;\n const a = document.createElement(\"a\");\n a.href = href;\n a.download = att.name;\n a.rel = \"noopener\";\n document.body.appendChild(a);\n a.click();\n a.remove();\n}\n\n/**\n * Friendly preview thumbnail for non-image attachments.\n */\nexport function attachmentIcon(mimeType: string): string {\n if (mimeType.startsWith(\"image/\")) return \"image\";\n if (mimeType.startsWith(\"video/\")) return \"video\";\n if (mimeType.startsWith(\"audio/\")) return \"audio\";\n if (mimeType === \"application/pdf\") return \"pdf\";\n if (mimeType.startsWith(\"text/\") || mimeType === \"application/json\") return \"text\";\n return \"file\";\n}\n\n/**\n * Extract the human-readable text from a message's `content`. When the\n * content is the internal `ai-chat-blocks` JSON marker (used when a turn\n * carries attachments), the text blocks are concatenated and the binary\n * image/file blocks are dropped — so the UI never renders raw JSON. Plain\n * content is returned unchanged.\n */\nexport function extractMessageText(content: string): string {\n if (!content.startsWith(\"{\")) return content;\n try {\n const parsed = JSON.parse(content) as {\n __type?: string;\n blocks?: { type?: string; text?: string }[];\n };\n if (parsed?.__type === \"ai-chat-blocks\" && Array.isArray(parsed.blocks)) {\n return parsed.blocks\n .map((b) => (b.type === \"text\" && typeof b.text === \"string\" ? b.text : \"\"))\n .filter(Boolean)\n .join(\"\\n\")\n .trim();\n }\n } catch {\n /* fall through — not a blocks marker */\n }\n return content;\n}\n\n/**\n * Reconstruct a plain-text representation of a user message:\n * strips the JSON marker, surfaces attachment names, and returns the\n * raw text the user typed. Used by the on-screen header.\n */\nexport function summarizeUserMessage(msg: ChatMessage): string {\n let text = msg.content;\n if (text.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(text);\n if (parsed?.__type === \"ai-chat-blocks\" && Array.isArray(parsed.blocks)) {\n text = parsed.blocks\n .map((b: { type?: string; text?: string }) =>\n b?.type === \"text\" && typeof b.text === \"string\" ? b.text : \"\",\n )\n .filter(Boolean)\n .join(\"\\n\");\n }\n } catch {\n // fall through\n }\n }\n const names = (msg.attachments ?? []).map((a) => a.name).join(\", \");\n return names ? `${text}\\n\\nšŸ“Ž ${names}` : text;\n}","import type { ChatMessage } from \"../types/chat.js\";\n\nconst STORAGE_PREFIX = \"ai-schadcn-chat:conversation:\";\n\n/**\n * Per-conversation persistence using `localStorage` by default.\n * The package intentionally avoids a hard dep on Indexeddb — the\n * persisted payload is small (messages + metadata) and localStorage\n * is available in every React 18/19 environment we target.\n *\n * For SSR safety, all reads/writes are guarded behind a typeof window check.\n */\nexport interface PersistedConversation {\n id: string;\n title: string;\n messages: ChatMessage[];\n updatedAt: number;\n config?: { model?: string; temperature?: number };\n}\n\nexport function loadConversation(id: string): PersistedConversation | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(STORAGE_PREFIX + id);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as PersistedConversation;\n if (!parsed?.messages) return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport function saveConversation(conv: PersistedConversation): void {\n if (typeof window === \"undefined\") return;\n try {\n // Strip tool-call `result` payloads that may contain Functions or DOM nodes —\n // they're not JSON-serializable and would crash the save.\n const safeMessages = conv.messages.map((m) => ({\n ...m,\n toolCalls: (m.toolCalls ?? []).map((tc) => ({\n ...tc,\n result: tc.result instanceof Object ? safeClone(tc.result) : tc.result,\n })),\n }));\n window.localStorage.setItem(\n STORAGE_PREFIX + conv.id,\n JSON.stringify({ ...conv, messages: safeMessages }),\n );\n } catch {\n // quota / privacy mode — silently skip; the chat still works in memory.\n }\n}\n\nexport function listConversations(): PersistedConversation[] {\n if (typeof window === \"undefined\") return [];\n const out: PersistedConversation[] = [];\n for (let i = 0; i < window.localStorage.length; i++) {\n const key = window.localStorage.key(i);\n if (!key || !key.startsWith(STORAGE_PREFIX)) continue;\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) continue;\n const parsed = JSON.parse(raw) as PersistedConversation;\n if (parsed?.id) out.push(parsed);\n } catch {\n // skip corrupted entries\n }\n }\n return out.sort((a, b) => b.updatedAt - a.updatedAt);\n}\n\nexport function deleteConversation(id: string): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.removeItem(STORAGE_PREFIX + id);\n } catch {\n // ignore\n }\n}\n\nexport function clearAllConversations(): void {\n if (typeof window === \"undefined\") return;\n try {\n const toRemove: string[] = [];\n for (let i = 0; i < window.localStorage.length; i++) {\n const key = window.localStorage.key(i);\n if (key && key.startsWith(STORAGE_PREFIX)) toRemove.push(key);\n }\n toRemove.forEach((k) => window.localStorage.removeItem(k));\n } catch {\n // ignore\n }\n}\n\nfunction safeClone(value: unknown): unknown {\n try {\n return JSON.parse(JSON.stringify(value));\n } catch {\n return undefined;\n }\n}\n\n/**\n * Trim persisted messages to a sane size to prevent runaway storage use.\n * 1000 messages Ɨ ~5KB each = 5MB, well within localStorage limits.\n */\nexport function trimPersisted(conv: PersistedConversation, max = 1000): PersistedConversation {\n if (conv.messages.length <= max) return conv;\n return {\n ...conv,\n messages: conv.messages.slice(-max),\n };\n}","import type {\n AttachmentMeta,\n ChatConfig,\n ChatError,\n ChatMessage,\n ChatResponseChunk,\n ConversationMeta,\n ProviderAdapter,\n Role,\n ToolCallRecord,\n ToolDefinition,\n} from \"../types/chat.js\";\nimport { createProvider } from \"../providers/index.js\";\nimport {\n estimateTokens,\n sleep,\n toError,\n trimMessagesByTokens,\n uid,\n} from \"./utils.js\";\nimport { filesToAttachments, serializeUserContent } from \"./attachments.js\";\nimport {\n listConversations as listPersistedConversations,\n loadConversation,\n saveConversation,\n trimPersisted,\n} from \"./persistence.js\";\nimport { toChatError } from \"../providers/base.js\";\n\n/**\n * Public, framework-agnostic chat engine. Wraps a streaming ProviderAdapter\n * and exposes a subscribe-based API for any UI framework (React/Vue/Solid).\n *\n * - Conversations have a stable id; switching ids swaps the active buffer\n * - messages stream incrementally; partial content is observable mid-flight\n * - tool calls are dispatched through user-supplied handlers when present\n * - persistence is opt-in via `cfg.persistKey`\n */\nexport class ChatEngine {\n private config: ChatConfig;\n private provider: ProviderAdapter;\n private listeners = new Set<() => void>();\n private conversations = new Map<string, ConversationState>();\n private activeId: string;\n\n constructor(config: ChatConfig, initialConversationId?: string) {\n this.config = config;\n this.provider = createProvider(config);\n this.activeId = initialConversationId ?? uid(\"conv\");\n if (this.config.persistKey) {\n // Restore active conversation if it was persisted with the same key prefix.\n // For now: restore the active id if it exists.\n const persisted = loadConversation(this.activeId);\n if (persisted) {\n this.conversations.set(this.activeId, {\n id: this.activeId,\n messages: persisted.messages,\n });\n } else {\n this.conversations.set(this.activeId, { id: this.activeId, messages: [] });\n }\n } else {\n this.conversations.set(this.activeId, { id: this.activeId, messages: [] });\n }\n }\n\n public getConfig(): ChatConfig {\n return this.config;\n }\n\n public updateConfig(partial: Partial<ChatConfig>): void;\n public updateConfig(updater: (current: ChatConfig) => Partial<ChatConfig>): void;\n public updateConfig(\n arg: Partial<ChatConfig> | ((current: ChatConfig) => Partial<ChatConfig>),\n ): void {\n const partial =\n typeof arg === \"function\" ? arg(this.config) : arg;\n this.config = { ...this.config, ...partial };\n if (partial.provider || partial.model) {\n this.provider = createProvider(this.config);\n }\n this.emit();\n }\n\n public getActiveConversationId(): string {\n return this.activeId;\n }\n\n /**\n * Return the count of in-flight tool calls for the active conversation.\n *\n * Default returns 0 because the engine does not yet expose tool-call\n * activity tracking publicly — this method is the hook that any future\n * tool-call plumbing can fill in. Consumers reading this in their UI\n * (e.g. a header badge behind `ui.showToolCalls`) will get a sensible\n * default until the engine wires real tool-call state.\n */\n public getActiveToolCallCount(): number {\n return 0;\n }\n\n public setActiveConversationId(id: string): void {\n if (!this.conversations.has(id)) {\n // Hydrate from persistence when switching to a stored conversation\n // that isn't in memory yet; otherwise start a fresh empty one.\n const persisted = this.config.persistKey ? loadConversation(id) : null;\n this.conversations.set(id, { id, messages: persisted?.messages ?? [] });\n }\n this.activeId = id;\n this.emit();\n }\n\n public listConversationIds(): string[] {\n return Array.from(this.conversations.keys());\n }\n\n /**\n * Rich conversation list for history menus: a title (first user message),\n * last-updated timestamp, and message count. Merges in-memory conversations\n * with persisted ones (persisted wins for title/timestamp). Sorted newest\n * first; the active conversation is always present.\n */\n public listConversationsMeta(): ConversationMeta[] {\n const metas = new Map<string, ConversationMeta>();\n for (const conv of this.conversations.values()) {\n metas.set(conv.id, {\n id: conv.id,\n title: deriveTitle(conv.messages),\n updatedAt: 0,\n messageCount: conv.messages.length,\n });\n }\n if (this.config.persistKey) {\n for (const p of listPersistedConversations()) {\n const existing = metas.get(p.id);\n metas.set(p.id, {\n id: p.id,\n title: p.title || existing?.title || \"New chat\",\n updatedAt: p.updatedAt,\n messageCount: p.messages.length,\n });\n }\n }\n return Array.from(metas.values()).sort((a, b) => b.updatedAt - a.updatedAt);\n }\n\n public getMessages(): ChatMessage[] {\n return this.conversations.get(this.activeId)?.messages ?? [];\n }\n\n /**\n * Returns a shallow copy of the active conversation's messages,\n * cached between `emit()` calls and only rebuilt when the engine\n * signals a real change.\n *\n * Why this exists: React bindings consume this via `useSyncExternalStore`,\n * which bails out when the snapshot reference is identical to the\n * previous one (that's the contract). Because we mutate `messages`\n * in place (`push`, etc.) the underlying array reference never changes\n * — so the user bubble would never render. The fix is to cache a fresh\n * copy between `emit()` calls and invalidate the cache when something\n * actually changes. Returning a NEW array on every call would itself\n * infinite-loop the snapshot.\n */\n private snapshotCache: ChatMessage[] = [];\n private snapshotDirty = true;\n\n public getMessagesSnapshot(): ChatMessage[] {\n if (this.snapshotDirty) {\n const msgs = this.conversations.get(this.activeId)?.messages;\n this.snapshotCache = msgs ? [...msgs] : [];\n this.snapshotDirty = false;\n }\n return this.snapshotCache;\n }\n\n public subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private emit(): void {\n this.snapshotDirty = true;\n for (const l of this.listeners) l();\n }\n\n private persist(): void {\n if (!this.config.persistKey) return;\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n const first = conv.messages.find((m) => m.role === \"user\");\n const title = first ? truncate(first.content.replace(/[\\n\\r]+/g, \" \"), 60) : \"New chat\";\n saveConversation(\n trimPersisted({\n id: conv.id,\n title,\n messages: conv.messages,\n updatedAt: Date.now(),\n config: { model: this.config.model.id, temperature: this.config.temperature },\n }),\n );\n }\n\n /**\n * Public: stop the currently streaming response, if any.\n */\n public abort(): void {\n this.provider.abort();\n }\n\n /**\n * Public: clear the active conversation.\n */\n public clear(): void {\n this.conversations.set(this.activeId, { id: this.activeId, messages: [] });\n this.persist();\n this.emit();\n }\n\n /**\n * Public: switch the active conversation (creates it if missing).\n */\n public newConversation(): string {\n const id = uid(\"conv\");\n this.conversations.set(id, { id, messages: [] });\n this.activeId = id;\n this.persist();\n this.emit();\n return id;\n }\n\n /** Public: remove a single message from the active conversation. */\n public deleteMessage(messageId: string): void {\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n conv.messages = conv.messages.filter((m) => m.id !== messageId);\n this.persist();\n this.emit();\n }\n\n public deleteConversation(id: string): void {\n this.conversations.delete(id);\n if (this.activeId === id) {\n this.activeId = this.conversations.keys().next().value ?? uid(\"conv\");\n this.conversations.set(this.activeId, { id: this.activeId, messages: [] });\n }\n this.persist();\n this.emit();\n }\n\n /**\n * Public: edit a previous user message and re-run the assistant from there.\n * Truncates all messages after `messageId` and triggers a new completion.\n */\n public async editAndResend(messageId: string, newContent: string): Promise<void> {\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n const idx = conv.messages.findIndex((m) => m.id === messageId);\n if (idx < 0 || conv.messages[idx].role !== \"user\") return;\n conv.messages[idx] = { ...conv.messages[idx], content: newContent, status: \"complete\" };\n conv.messages = conv.messages.slice(0, idx + 1);\n this.emit();\n this.persist();\n await this.runCompletion();\n }\n\n /**\n * Public: re-run the assistant for the most recent user turn.\n */\n public async regenerate(): Promise<void> {\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n // Drop the last assistant message (or error).\n for (let i = conv.messages.length - 1; i >= 0; i--) {\n if (conv.messages[i].role === \"assistant\") {\n conv.messages.splice(i, 1);\n break;\n }\n if (conv.messages[i].role === \"user\") break;\n }\n this.emit();\n this.persist();\n await this.runCompletion();\n }\n\n /**\n * Public: send a user message (optionally with attachments) and stream the\n * assistant reply.\n */\n public async sendUserMessage(text: string, attachments?: AttachmentMeta[]): Promise<void> {\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n if (!text.trim() && !attachments?.length) return;\n\n const content = serializeUserContent(\n text,\n attachments,\n this.config.model.provider ?? this.config.provider.kind,\n );\n\n const userMsg: ChatMessage = {\n id: uid(\"msg\"),\n role: \"user\",\n content,\n attachments,\n createdAt: Date.now(),\n status: \"complete\",\n };\n conv.messages.push(userMsg);\n this.emit();\n this.persist();\n\n await this.runCompletion();\n }\n\n /**\n * Public: send files (browser File objects) directly. Convenience wrapper\n * around sendUserMessage + filesToAttachments.\n */\n public async sendFiles(text: string, files: File[]): Promise<void> {\n const attachments = await filesToAttachments(files, this.config);\n return this.sendUserMessage(text, attachments);\n }\n\n /**\n * Internal: stream one assistant turn. Handles tool calls in a loop\n * (max 8 iterations to prevent infinite loops).\n */\n private async runCompletion(): Promise<void> {\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n\n const assistantId = uid(\"msg\");\n let assistantMsg: ChatMessage = {\n id: assistantId,\n role: \"assistant\",\n content: \"\",\n createdAt: Date.now(),\n status: \"streaming\",\n };\n conv.messages.push(assistantMsg);\n\n const maxIter = 8;\n for (let iter = 0; iter < maxIter; iter++) {\n try {\n const { toolCalls, finished, finalUsage, model } =\n await this.streamAssistantTurn(assistantMsg, conv);\n\n if (toolCalls.length > 0 && this.config.tools?.length) {\n assistantMsg.status = \"complete\";\n // Materialize tool-call records on the assistant message.\n assistantMsg.toolCalls = toolCalls.map((tc) => ({\n id: tc.id,\n name: tc.name,\n arguments: tc.arguments,\n status: tc.error ? (\"error\" as const) : (\"complete\" as const),\n result: tc.result,\n error: tc.error,\n }));\n\n // Execute each tool and append tool messages.\n for (const tc of toolCalls) {\n const def = this.config.tools?.find((t) => t.name === tc.name);\n const toolMsg: ChatMessage = {\n id: uid(\"msg\"),\n role: \"tool\",\n content: tc.error\n ? `Error: ${tc.error}`\n : typeof tc.result === \"string\"\n ? tc.result\n : JSON.stringify(tc.result ?? null),\n createdAt: Date.now(),\n status: \"complete\",\n toolCallId: tc.id,\n name: tc.name,\n };\n conv.messages.push(toolMsg);\n this.emit();\n this.persist();\n // If a tool produced a result we want the assistant to react to,\n // we let the next iteration append another assistant message.\n // To support that, push a fresh assistant placeholder so the loop\n // continues the conversation. We only do this when the tool\n // returned a usable payload (no error) AND there are still\n // iterations left.\n if (!tc.error && iter < maxIter - 1) {\n assistantId;\n assistantMsg = {\n id: uid(\"msg\"),\n role: \"assistant\",\n content: \"\",\n createdAt: Date.now(),\n status: \"streaming\",\n };\n conv.messages.push(assistantMsg);\n }\n }\n if (!finished) continue;\n break;\n }\n\n if (finished) {\n assistantMsg.status = \"complete\";\n assistantMsg.completedAt = Date.now();\n if (finalUsage) assistantMsg.usage = finalUsage;\n if (model) assistantMsg.model = model;\n break;\n }\n } catch (err) {\n const ce = toChatError(err);\n assistantMsg.status = \"error\";\n assistantMsg.error = ce;\n this.config.onError?.(ce, { conversationId: this.activeId });\n this.emit();\n this.persist();\n return;\n }\n }\n\n this.config.onResponse?.(assistantMsg, { conversationId: this.activeId });\n this.emit();\n this.persist();\n }\n\n /**\n * Streams one assistant message. Returns:\n * - toolCalls: tool_use / tool_calls chunks collected during the stream\n * - finished: true if the API emitted a non-tool finish reason\n * - finalUsage / model: usage & model metadata when available\n */\n private async streamAssistantTurn(\n assistantMsg: ChatMessage,\n conv: ConversationState,\n ): Promise<{\n toolCalls: Array<ToolCallRecord & { arguments: Record<string, unknown> }>;\n finished: boolean;\n finalUsage?: ChatMessage[\"usage\"];\n model?: string;\n }> {\n const maxTokens = this.config.maxContextTokens ?? 32_000;\n const messages = trimMessagesByTokens(conv.messages, maxTokens);\n\n const collected = new Map<\n string,\n { id: string; name: string; arguments: Record<string, unknown> | string; raw: string }\n >();\n let buffer = \"\";\n let reasoningBuffer = \"\";\n let finishReason: ChatResponseChunk[\"finishReason\"];\n let finalUsage: ChatMessage[\"usage\"];\n let finalModel: string | undefined;\n\n const stream = this.provider.stream(\n {\n messages,\n signal: undefined,\n },\n this.config,\n );\n\n let lastError: ChatError | undefined;\n\n for await (const chunk of stream) {\n if (chunk.error) {\n lastError = chunk.error;\n buffer += chunk.error.message ? `\\n\\n[error] ${chunk.error.message}` : \"\";\n finishReason = \"error\";\n break;\n }\n if (chunk.model) finalModel = chunk.model;\n if (chunk.usage) finalUsage = chunk.usage;\n if (chunk.finishReason) finishReason = chunk.finishReason;\n if (chunk.delta) {\n if (!assistantMsg.startedAt) assistantMsg.startedAt = Date.now();\n if (chunk.delta.startsWith(\"\\u0000think:\")) {\n reasoningBuffer += chunk.delta.slice(\"\\u0000think:\".length);\n assistantMsg.reasoning = reasoningBuffer;\n } else {\n buffer += chunk.delta;\n assistantMsg.content = buffer;\n }\n this.emit();\n }\n if (chunk.toolCall) {\n const tc = collected.get(chunk.toolCall.id) ?? {\n id: chunk.toolCall.id,\n name: chunk.toolCall.name,\n arguments: \"\",\n raw: \"\",\n };\n if (chunk.toolCall.name) tc.name = chunk.toolCall.name;\n if (chunk.toolCall.arguments) tc.raw += chunk.toolCall.arguments;\n tc.arguments = tc.raw;\n collected.set(chunk.toolCall.id, tc);\n this.emit();\n }\n }\n\n // Persist incremental buffer onto the assistant message.\n assistantMsg.content = buffer;\n\n // Materialize tool-call records.\n const toolCalls: Array<ToolCallRecord & { arguments: Record<string, unknown> }> = [];\n for (const tc of collected.values()) {\n let parsedArgs: Record<string, unknown> = {};\n if (typeof tc.raw === \"string\" && tc.raw.trim().length > 0) {\n try {\n parsedArgs = JSON.parse(tc.raw);\n } catch {\n parsedArgs = { raw: tc.raw };\n }\n }\n const def = this.config.tools?.find((t) => t.name === tc.name);\n let result: unknown;\n let error: string | undefined;\n if (def?.handler) {\n try {\n const handlerResult = await def.handler(parsedArgs, { conversationId: this.activeId });\n result = handlerResult;\n } catch (err) {\n error = (err instanceof Error ? err.message : String(err)) || \"Tool failed\";\n }\n } else {\n // No handler — surface a soft warning so the model can still react.\n result = { ok: false, message: `No handler registered for tool \"${tc.name}\".` };\n }\n toolCalls.push({\n id: tc.id,\n name: tc.name,\n arguments: parsedArgs,\n status: error ? (\"error\" as const) : (\"complete\" as const),\n result,\n error,\n });\n }\n\n if (lastError) throw lastError;\n\n const finished = finishReason === \"stop\" || finishReason === \"length\" || finishReason === \"content_filter\";\n const hasToolCalls = toolCalls.length > 0;\n return { toolCalls, finished: finished && !hasToolCalls, finalUsage, model: finalModel };\n }\n}\n\ninterface ConversationState {\n id: string;\n messages: ChatMessage[];\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length <= max ? text : `${text.slice(0, max - 1)}…`;\n}\n\n/** Derive a human title from the first user message (ignores JSON markers). */\nfunction deriveTitle(messages: ChatMessage[]): string {\n const first = messages.find((m) => m.role === \"user\");\n if (!first) return \"New chat\";\n let content = first.content;\n if (content.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(content) as {\n __type?: string;\n blocks?: { type?: string; text?: string }[];\n };\n if (parsed?.__type === \"ai-chat-blocks\") {\n content =\n parsed.blocks\n ?.map((b) => (b.type === \"text\" ? (b.text ?? \"\") : \"\"))\n .filter(Boolean)\n .join(\" \") || \"Attachment\";\n }\n } catch {\n /* keep raw */\n }\n }\n return truncate(content.replace(/[\\n\\r]+/g, \" \").trim(), 60) || \"New chat\";\n}"],"names":["filesToAttachments","files","cfg","maxBytes","accepted","out","file","formatBytes","att","uid","readFileAsText","readFileAsDataUrl","serializeUserContent","text","attachments","providerKind","blocks","buildAnthropicUserContent","buildOpenAIUserContent","downloadAttachment","href","a","attachmentIcon","mimeType","extractMessageText","content","parsed","b","summarizeUserMessage","msg","names","STORAGE_PREFIX","loadConversation","id","raw","saveConversation","conv","safeMessages","m","tc","safeClone","listConversations","i","key","deleteConversation","clearAllConversations","toRemove","k","value","trimPersisted","max","ChatEngine","config","initialConversationId","createProvider","persisted","arg","partial","metas","deriveTitle","p","listPersistedConversations","existing","msgs","listener","l","first","title","truncate","messageId","newContent","idx","userMsg","assistantId","assistantMsg","maxIter","iter","toolCalls","finished","finalUsage","model","def","t","toolMsg","err","ce","toChatError","maxTokens","messages","trimMessagesByTokens","collected","buffer","reasoningBuffer","finishReason","finalModel","stream","lastError","chunk","parsedArgs","result","error","hasToolCalls"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Agent } from "../../types/chat.js";
2
+ export interface AgentManagerProps {
3
+ open: boolean;
4
+ onOpenChange: (open: boolean) => void;
5
+ activeAgentId?: string | null;
6
+ onApply: (agent: Agent) => void;
7
+ /** Open straight into the create form. */
8
+ startInCreate?: boolean;
9
+ }
10
+ export declare function AgentManager({ open, onOpenChange, activeAgentId, onApply, startInCreate, }: AgentManagerProps): import("react").JSX.Element;
11
+ //# sourceMappingURL=AgentManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentManager.d.ts","sourceRoot":"","sources":["../../../src/components/chat/AgentManager.tsx"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAWjD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAChC,0CAA0C;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAiCD,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,YAAY,EACZ,aAAa,EACb,OAAO,EACP,aAAqB,GACtB,EAAE,iBAAiB,+BA4EnB"}