@absolutejs/ai 0.0.1 → 0.0.2

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.
@@ -0,0 +1,736 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __name = (target, name) => {
5
+ Object.defineProperty(target, "name", {
6
+ value: name,
7
+ enumerable: false,
8
+ configurable: true
9
+ });
10
+ return target;
11
+ };
12
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
13
+ var __typeError = (msg) => {
14
+ throw TypeError(msg);
15
+ };
16
+ var __defNormalProp = (obj, key, value) => (key in obj) ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
17
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
18
+ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
19
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
20
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
21
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
22
+ var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
23
+ var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
24
+ var __expectFn = (fn) => fn !== undefined && typeof fn !== "function" ? __typeError("Function expected") : fn;
25
+ var __decoratorContext = (kind, name, done, metadata, fns) => ({
26
+ kind: __decoratorStrings[kind],
27
+ name,
28
+ metadata,
29
+ addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
30
+ });
31
+ var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
32
+ var __runInitializers = (array, flags, self, value) => {
33
+ for (var i = 0, fns = array[flags >> 1], n = fns && fns.length;i < n; i++)
34
+ flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
35
+ return value;
36
+ };
37
+ var __decorateElement = (array, flags, name, decorators, target, extra) => {
38
+ var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
39
+ var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
40
+ var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
41
+ var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
42
+ get [name]() {
43
+ return __privateGet(this, extra);
44
+ },
45
+ set [name](x) {
46
+ __privateSet(this, extra, x);
47
+ }
48
+ }, name));
49
+ k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
50
+ for (var i = decorators.length - 1;i >= 0; i--) {
51
+ ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
52
+ if (k) {
53
+ ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => (name in x) };
54
+ if (k ^ 3)
55
+ access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
56
+ if (k > 2)
57
+ access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
58
+ }
59
+ it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? undefined : { get: desc.get, set: desc.set } : target, ctx);
60
+ done._ = 1;
61
+ if (k ^ 4 || it === undefined)
62
+ __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
63
+ else if (typeof it !== "object" || it === null)
64
+ __typeError("Object expected");
65
+ else
66
+ __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
67
+ }
68
+ return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
69
+ };
70
+
71
+ // src/react/ai/AIStreamProvider.tsx
72
+ import {
73
+ createContext,
74
+ useContext,
75
+ useEffect,
76
+ useRef
77
+ } from "react";
78
+
79
+ // types/typeGuards.ts
80
+ var isValidAIServerMessage = (data) => {
81
+ if (!data || typeof data !== "object") {
82
+ return false;
83
+ }
84
+ if (!("type" in data) || typeof data.type !== "string") {
85
+ return false;
86
+ }
87
+ switch (data.type) {
88
+ case "chunk":
89
+ case "thinking":
90
+ return "content" in data && typeof data.content === "string" && "messageId" in data && "conversationId" in data;
91
+ case "tool_status":
92
+ return "name" in data && "status" in data && "messageId" in data && "conversationId" in data;
93
+ case "image":
94
+ return "data" in data && typeof data.data === "string" && "format" in data && typeof data.format === "string" && "isPartial" in data && typeof data.isPartial === "boolean" && "messageId" in data && "conversationId" in data;
95
+ case "complete":
96
+ return "messageId" in data && "conversationId" in data;
97
+ case "rag_retrieved":
98
+ return "conversationId" in data && "messageId" in data && "sources" in data && Array.isArray(data.sources);
99
+ case "error":
100
+ return "message" in data && typeof data.message === "string";
101
+ default:
102
+ return false;
103
+ }
104
+ };
105
+
106
+ // src/ai/client/connection.ts
107
+ var WS_OPEN = 1;
108
+ var WS_NORMAL_CLOSURE = 1000;
109
+ var WS_CLOSED = 3;
110
+ var DEFAULT_PING_INTERVAL = 30000;
111
+ var RECONNECT_INITIAL_DELAY = 500;
112
+ var RECONNECT_POLL_INTERVAL = 300;
113
+ var DEFAULT_MAX_RECONNECT_ATTEMPTS = 60;
114
+ var noop = () => {};
115
+ var noopUnsubscribe = () => noop;
116
+ var NOOP_CONNECTION = {
117
+ close: noop,
118
+ send: noop,
119
+ subscribe: noopUnsubscribe,
120
+ getReadyState: () => WS_CLOSED
121
+ };
122
+ var buildWsUrl = (path) => {
123
+ const { hostname, port, protocol } = window.location;
124
+ const wsProtocol = protocol === "https:" ? "wss:" : "ws:";
125
+ const portSuffix = port ? `:${port}` : "";
126
+ return `${wsProtocol}//${hostname}${portSuffix}${path}`;
127
+ };
128
+ var parseServerMessage = (event) => {
129
+ let data;
130
+ try {
131
+ data = JSON.parse(String(event.data));
132
+ } catch {
133
+ return null;
134
+ }
135
+ if (data && typeof data === "object" && "type" in data && data.type === "pong") {
136
+ return null;
137
+ }
138
+ if (!isValidAIServerMessage(data)) {
139
+ return null;
140
+ }
141
+ return data;
142
+ };
143
+ var createAIConnection = (path, options = {}) => {
144
+ if (typeof window === "undefined") {
145
+ return NOOP_CONNECTION;
146
+ }
147
+ const shouldReconnect = options.reconnect !== false;
148
+ const pingInterval = options.pingInterval ?? DEFAULT_PING_INTERVAL;
149
+ const maxReconnectAttempts = options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;
150
+ const listeners = new Set;
151
+ const connState = {
152
+ isConnected: false,
153
+ pendingMessages: [],
154
+ pingInterval: null,
155
+ reconnectAttempts: 0,
156
+ reconnectTimeout: null,
157
+ ws: null
158
+ };
159
+ const flushPendingMessages = () => {
160
+ if (connState.ws?.readyState !== WS_OPEN) {
161
+ return;
162
+ }
163
+ while (connState.pendingMessages.length > 0) {
164
+ const next = connState.pendingMessages.shift();
165
+ if (typeof next === "string") {
166
+ connState.ws.send(next);
167
+ }
168
+ }
169
+ };
170
+ const clearTimers = () => {
171
+ if (connState.pingInterval) {
172
+ clearInterval(connState.pingInterval);
173
+ connState.pingInterval = null;
174
+ }
175
+ if (connState.reconnectTimeout) {
176
+ clearTimeout(connState.reconnectTimeout);
177
+ connState.reconnectTimeout = null;
178
+ }
179
+ };
180
+ const scheduleReconnect = () => {
181
+ connState.reconnectAttempts++;
182
+ const delay = connState.reconnectAttempts === 1 ? RECONNECT_INITIAL_DELAY : RECONNECT_POLL_INTERVAL;
183
+ connState.reconnectTimeout = setTimeout(() => {
184
+ if (connState.reconnectAttempts > maxReconnectAttempts) {
185
+ return;
186
+ }
187
+ connect();
188
+ }, delay);
189
+ };
190
+ const connect = () => {
191
+ const url = buildWsUrl(path);
192
+ const wsInstance = new WebSocket(url, options.protocols);
193
+ wsInstance.onopen = () => {
194
+ connState.isConnected = true;
195
+ connState.reconnectAttempts = 0;
196
+ flushPendingMessages();
197
+ connState.pingInterval = setInterval(() => {
198
+ if (wsInstance.readyState === WS_OPEN && connState.isConnected) {
199
+ wsInstance.send(JSON.stringify({ type: "ping" }));
200
+ }
201
+ }, pingInterval);
202
+ };
203
+ wsInstance.onmessage = (event) => {
204
+ const message = parseServerMessage(event);
205
+ if (!message) {
206
+ return;
207
+ }
208
+ listeners.forEach((listener) => listener(message));
209
+ };
210
+ wsInstance.onclose = (event) => {
211
+ connState.isConnected = false;
212
+ clearTimers();
213
+ const shouldAttemptReconnect = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && connState.reconnectAttempts < maxReconnectAttempts;
214
+ if (shouldAttemptReconnect) {
215
+ scheduleReconnect();
216
+ }
217
+ };
218
+ wsInstance.onerror = () => {};
219
+ connState.ws = wsInstance;
220
+ };
221
+ const send = (msg) => {
222
+ const serialized = JSON.stringify(msg);
223
+ if (connState.ws?.readyState === WS_OPEN) {
224
+ connState.ws.send(serialized);
225
+ return;
226
+ }
227
+ connState.pendingMessages.push(serialized);
228
+ };
229
+ const subscribe = (callback) => {
230
+ listeners.add(callback);
231
+ return () => {
232
+ listeners.delete(callback);
233
+ };
234
+ };
235
+ const close = () => {
236
+ clearTimers();
237
+ if (connState.ws) {
238
+ connState.ws.close(WS_NORMAL_CLOSURE);
239
+ connState.ws = null;
240
+ }
241
+ connState.isConnected = false;
242
+ listeners.clear();
243
+ };
244
+ const getReadyState = () => connState.ws?.readyState ?? WS_CLOSED;
245
+ connect();
246
+ return { close, getReadyState, send, subscribe };
247
+ };
248
+
249
+ // src/ai/client/messageStore.ts
250
+ var EMPTY_STATE = {
251
+ activeConversationId: null,
252
+ conversations: new Map,
253
+ error: null,
254
+ isStreaming: false
255
+ };
256
+ var initialActiveConversationId = null;
257
+ var initialError = null;
258
+ var freshState = () => ({
259
+ activeConversationId: initialActiveConversationId,
260
+ conversations: new Map,
261
+ error: initialError,
262
+ isStreaming: false
263
+ });
264
+ var findAssistantMessage = (conversation, messageId) => conversation.messages.find((msg) => msg.id === messageId && msg.role === "assistant");
265
+ var getOrCreate = (state, conversationId) => {
266
+ let conversation = state.conversations.get(conversationId);
267
+ if (!conversation) {
268
+ conversation = {
269
+ createdAt: Date.now(),
270
+ id: conversationId,
271
+ messages: []
272
+ };
273
+ state.conversations.set(conversationId, conversation);
274
+ }
275
+ return conversation;
276
+ };
277
+ var handleSend = (state, action) => {
278
+ const conversation = getOrCreate(state, action.conversationId);
279
+ const message = {
280
+ attachments: action.attachments,
281
+ content: action.content,
282
+ conversationId: action.conversationId,
283
+ id: action.messageId,
284
+ role: "user",
285
+ timestamp: Date.now()
286
+ };
287
+ conversation.messages = [...conversation.messages, message];
288
+ state.activeConversationId = action.conversationId;
289
+ state.error = null;
290
+ state.isStreaming = true;
291
+ };
292
+ var handleChunk = (state, action) => {
293
+ const conversation = getOrCreate(state, action.conversationId);
294
+ const existingIdx = conversation.messages.findIndex((msg) => msg.id === action.messageId && msg.role === "assistant");
295
+ if (existingIdx >= 0) {
296
+ const prevContent = conversation.messages[existingIdx]?.content ?? "";
297
+ conversation.messages = conversation.messages.map((msg, idx) => idx === existingIdx ? { ...msg, content: prevContent + action.content } : msg);
298
+ return;
299
+ }
300
+ const message = {
301
+ content: action.content,
302
+ conversationId: action.conversationId,
303
+ id: action.messageId,
304
+ isStreaming: true,
305
+ role: "assistant",
306
+ timestamp: Date.now()
307
+ };
308
+ conversation.messages = [...conversation.messages, message];
309
+ };
310
+ var handleThinking = (state, action) => {
311
+ const conversation = getOrCreate(state, action.conversationId);
312
+ const existingIdx = conversation.messages.findIndex((msg) => msg.id === action.messageId && msg.role === "assistant");
313
+ if (existingIdx >= 0) {
314
+ const prevThinking = conversation.messages[existingIdx]?.thinking ?? "";
315
+ conversation.messages = conversation.messages.map((msg, idx) => idx === existingIdx ? { ...msg, thinking: prevThinking + action.content } : msg);
316
+ return;
317
+ }
318
+ const message = {
319
+ content: "",
320
+ conversationId: action.conversationId,
321
+ id: action.messageId,
322
+ isStreaming: true,
323
+ role: "assistant",
324
+ thinking: action.content,
325
+ timestamp: Date.now()
326
+ };
327
+ conversation.messages = [...conversation.messages, message];
328
+ };
329
+ var upsertToolCall = (message, toolCall) => {
330
+ if (!message.toolCalls) {
331
+ message.toolCalls = [toolCall];
332
+ return;
333
+ }
334
+ const existingIdx = message.toolCalls.findIndex((existing) => existing.name === toolCall.name);
335
+ if (existingIdx >= 0) {
336
+ message.toolCalls[existingIdx] = toolCall;
337
+ } else {
338
+ message.toolCalls = [...message.toolCalls, toolCall];
339
+ }
340
+ };
341
+ var getOrCreateAssistantMessage = (conversation, messageId, conversationId) => {
342
+ const existing = findAssistantMessage(conversation, messageId);
343
+ if (existing) {
344
+ return existing;
345
+ }
346
+ const message = {
347
+ content: "",
348
+ conversationId,
349
+ id: messageId,
350
+ isStreaming: true,
351
+ role: "assistant",
352
+ timestamp: Date.now()
353
+ };
354
+ conversation.messages = [...conversation.messages, message];
355
+ return message;
356
+ };
357
+ var handleRAGRetrieved = (state, action) => {
358
+ const conversation = getOrCreate(state, action.conversationId);
359
+ const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
360
+ message.sources = action.sources;
361
+ message.retrievalStartedAt = action.retrievalStartedAt ?? message.retrievalStartedAt;
362
+ message.retrievedAt = action.retrievedAt;
363
+ message.retrievalDurationMs = action.retrievalDurationMs;
364
+ message.retrievalTrace = action.trace;
365
+ conversation.messages = [...conversation.messages];
366
+ };
367
+ var handleRAGRetrieving = (state, action) => {
368
+ const conversation = getOrCreate(state, action.conversationId);
369
+ const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
370
+ message.retrievalStartedAt = action.retrievalStartedAt;
371
+ conversation.messages = [...conversation.messages];
372
+ };
373
+ var handleToolStatus = (state, action) => {
374
+ const conversation = getOrCreate(state, action.conversationId);
375
+ const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
376
+ const toolCall = {
377
+ id: action.messageId,
378
+ input: action.input,
379
+ name: action.name,
380
+ result: action.status === "complete" ? action.result ?? "" : undefined
381
+ };
382
+ upsertToolCall(message, toolCall);
383
+ conversation.messages = [...conversation.messages];
384
+ };
385
+ var markMessageComplete = (conversation, messageId, usage, durationMs, model, sources) => {
386
+ conversation.messages = conversation.messages.map((msg) => msg.id === messageId && msg.role === "assistant" ? {
387
+ ...msg,
388
+ durationMs,
389
+ isStreaming: false,
390
+ model,
391
+ sources: sources ?? msg.sources,
392
+ usage
393
+ } : msg);
394
+ };
395
+ var NOT_FOUND = -1;
396
+ var findExistingImageById = (images, imageId) => {
397
+ if (!imageId)
398
+ return NOT_FOUND;
399
+ return images.findIndex((img) => img.imageId === imageId);
400
+ };
401
+ var findReplaceablePartialIndex = (images) => {
402
+ const lastIdx = images.length - 1;
403
+ if (lastIdx >= 0 && images[lastIdx]?.isPartial) {
404
+ return lastIdx;
405
+ }
406
+ return NOT_FOUND;
407
+ };
408
+ var upsertImage = (message, imageData) => {
409
+ if (!message.images) {
410
+ message.images = [imageData];
411
+ return;
412
+ }
413
+ const existingIdx = findExistingImageById(message.images, imageData.imageId);
414
+ if (existingIdx >= 0) {
415
+ message.images[existingIdx] = imageData;
416
+ return;
417
+ }
418
+ const replaceableIdx = findReplaceablePartialIndex(message.images);
419
+ if (replaceableIdx >= 0) {
420
+ message.images[replaceableIdx] = imageData;
421
+ return;
422
+ }
423
+ message.images = [...message.images, imageData];
424
+ };
425
+ var handleImage = (state, action) => {
426
+ const conversation = getOrCreate(state, action.conversationId);
427
+ const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
428
+ upsertImage(message, {
429
+ data: action.data,
430
+ format: action.format,
431
+ imageId: action.imageId,
432
+ isPartial: action.isPartial,
433
+ revisedPrompt: action.revisedPrompt
434
+ });
435
+ conversation.messages = [...conversation.messages];
436
+ };
437
+ var handleComplete = (state, action) => {
438
+ const conversation = state.conversations.get(action.conversationId);
439
+ if (conversation) {
440
+ markMessageComplete(conversation, action.messageId, action.usage, action.durationMs, action.model, action.sources);
441
+ }
442
+ state.isStreaming = false;
443
+ };
444
+ var markConversationStreamsComplete = (conversation) => {
445
+ const streamingMessages = conversation.messages.filter((msg) => msg.isStreaming);
446
+ if (streamingMessages.length === 0) {
447
+ return;
448
+ }
449
+ for (const msg of streamingMessages) {
450
+ msg.isStreaming = false;
451
+ }
452
+ conversation.messages = [...conversation.messages];
453
+ };
454
+ var markAllStreamsComplete = (state) => {
455
+ for (const [, conversation] of state.conversations) {
456
+ markConversationStreamsComplete(conversation);
457
+ }
458
+ };
459
+ var handleBranch = (state, action) => {
460
+ const source = state.conversations.get(action.oldConversationId);
461
+ if (!source) {
462
+ return;
463
+ }
464
+ const cutoffIndex = source.messages.findIndex((msg) => msg.id === action.fromMessageId);
465
+ if (cutoffIndex < 0) {
466
+ return;
467
+ }
468
+ const branchedMessages = source.messages.slice(0, cutoffIndex + 1).map((msg) => ({ ...msg, conversationId: action.newConversationId }));
469
+ const newConversation = {
470
+ createdAt: Date.now(),
471
+ id: action.newConversationId,
472
+ messages: branchedMessages
473
+ };
474
+ state.conversations.set(action.newConversationId, newConversation);
475
+ state.activeConversationId = action.newConversationId;
476
+ };
477
+ var applyAction = (state, action) => {
478
+ switch (action.type) {
479
+ case "send":
480
+ handleSend(state, action);
481
+ break;
482
+ case "chunk":
483
+ handleChunk(state, action);
484
+ break;
485
+ case "thinking":
486
+ handleThinking(state, action);
487
+ break;
488
+ case "tool_status":
489
+ handleToolStatus(state, action);
490
+ break;
491
+ case "image":
492
+ handleImage(state, action);
493
+ break;
494
+ case "complete":
495
+ handleComplete(state, action);
496
+ break;
497
+ case "error":
498
+ state.error = action.message;
499
+ state.isStreaming = false;
500
+ break;
501
+ case "rag_retrieving":
502
+ handleRAGRetrieving(state, action);
503
+ break;
504
+ case "rag_retrieved":
505
+ handleRAGRetrieved(state, action);
506
+ break;
507
+ case "cancel":
508
+ state.isStreaming = false;
509
+ markAllStreamsComplete(state);
510
+ break;
511
+ case "branch":
512
+ handleBranch(state, action);
513
+ break;
514
+ case "set_conversation":
515
+ state.activeConversationId = action.conversationId;
516
+ break;
517
+ }
518
+ };
519
+ var createAIMessageStore = () => {
520
+ let state = freshState();
521
+ const subscribers = new Set;
522
+ return {
523
+ dispatch: (action) => {
524
+ applyAction(state, action);
525
+ state = { ...state, conversations: new Map(state.conversations) };
526
+ subscribers.forEach((callback) => callback());
527
+ },
528
+ getServerSnapshot: () => EMPTY_STATE,
529
+ getSnapshot: () => state,
530
+ subscribe: (callback) => {
531
+ subscribers.add(callback);
532
+ return () => {
533
+ subscribers.delete(callback);
534
+ };
535
+ }
536
+ };
537
+ };
538
+
539
+ // src/ai/client/actions.ts
540
+ var serverMessageToAction = (message) => {
541
+ switch (message.type) {
542
+ case "chunk":
543
+ return {
544
+ content: message.content,
545
+ conversationId: message.conversationId,
546
+ messageId: message.messageId,
547
+ type: "chunk"
548
+ };
549
+ case "thinking":
550
+ return {
551
+ content: message.content,
552
+ conversationId: message.conversationId,
553
+ messageId: message.messageId,
554
+ type: "thinking"
555
+ };
556
+ case "tool_status":
557
+ return {
558
+ conversationId: message.conversationId,
559
+ input: message.input,
560
+ messageId: message.messageId,
561
+ name: message.name,
562
+ result: message.result,
563
+ status: message.status,
564
+ type: "tool_status"
565
+ };
566
+ case "image":
567
+ return {
568
+ conversationId: message.conversationId,
569
+ data: message.data,
570
+ format: message.format,
571
+ imageId: message.imageId,
572
+ isPartial: message.isPartial,
573
+ messageId: message.messageId,
574
+ revisedPrompt: message.revisedPrompt,
575
+ type: "image"
576
+ };
577
+ case "complete":
578
+ return {
579
+ conversationId: message.conversationId,
580
+ durationMs: message.durationMs,
581
+ messageId: message.messageId,
582
+ model: message.model,
583
+ sources: message.sources,
584
+ type: "complete",
585
+ usage: message.usage
586
+ };
587
+ case "rag_retrieving":
588
+ return {
589
+ conversationId: message.conversationId,
590
+ messageId: message.messageId,
591
+ retrievalStartedAt: message.retrievalStartedAt,
592
+ type: "rag_retrieving"
593
+ };
594
+ case "rag_retrieved":
595
+ return {
596
+ conversationId: message.conversationId,
597
+ messageId: message.messageId,
598
+ retrievalDurationMs: message.retrievalDurationMs,
599
+ retrievalStartedAt: message.retrievalStartedAt,
600
+ retrievedAt: message.retrievedAt,
601
+ sources: message.sources,
602
+ trace: message.trace,
603
+ type: "rag_retrieved"
604
+ };
605
+ case "error":
606
+ return { message: message.message, type: "error" };
607
+ default:
608
+ return null;
609
+ }
610
+ };
611
+
612
+ // src/react/ai/AIStreamProvider.tsx
613
+ import { jsxDEV } from "react/jsx-dev-runtime";
614
+ var AIStreamContext = createContext(null);
615
+ var AIStreamProvider = ({
616
+ children,
617
+ path
618
+ }) => {
619
+ const ref = useRef(null);
620
+ if (!ref.current) {
621
+ const connection = createAIConnection(path);
622
+ const store = createAIMessageStore();
623
+ ref.current = { connection, store };
624
+ }
625
+ useEffect(() => {
626
+ const { current } = ref;
627
+ if (!current) {
628
+ return;
629
+ }
630
+ const { connection, store } = current;
631
+ const unsubscribe = connection.subscribe((message) => {
632
+ const action = serverMessageToAction(message);
633
+ if (action) {
634
+ store.dispatch(action);
635
+ }
636
+ });
637
+ return () => {
638
+ unsubscribe();
639
+ connection.close();
640
+ };
641
+ }, []);
642
+ return /* @__PURE__ */ jsxDEV(AIStreamContext.Provider, {
643
+ value: ref.current,
644
+ children
645
+ }, undefined, false, undefined, this);
646
+ };
647
+ var useAIStreamContext = () => useContext(AIStreamContext);
648
+ // src/react/ai/useAIStream.ts
649
+ import { useCallback, useEffect as useEffect2, useRef as useRef2, useSyncExternalStore } from "react";
650
+
651
+ // src/ai/protocol.ts
652
+ var generateId = () => crypto.randomUUID();
653
+
654
+ // src/react/ai/useAIStream.ts
655
+ var useAIStream = (path, conversationId) => {
656
+ const context = useAIStreamContext();
657
+ const standaloneRef = useRef2(null);
658
+ const isStandalone = !context;
659
+ if (isStandalone && !standaloneRef.current && path) {
660
+ const connection2 = createAIConnection(path);
661
+ const store2 = createAIMessageStore();
662
+ standaloneRef.current = { connection: connection2, store: store2 };
663
+ }
664
+ const resolved = context ?? standaloneRef.current;
665
+ if (!resolved) {
666
+ throw new Error("useAIStream requires either an AIStreamProvider or a path argument");
667
+ }
668
+ const { connection, store } = resolved;
669
+ useEffect2(() => {
670
+ if (!isStandalone) {
671
+ return;
672
+ }
673
+ const unsubscribe = connection.subscribe((message) => {
674
+ const action = serverMessageToAction(message);
675
+ if (action) {
676
+ store.dispatch(action);
677
+ }
678
+ });
679
+ return () => {
680
+ unsubscribe();
681
+ connection.close();
682
+ };
683
+ }, [connection, isStandalone, store]);
684
+ const state = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot);
685
+ const activeConvId = conversationId ?? state.activeConversationId;
686
+ const conversation = activeConvId ? state.conversations.get(activeConvId) : undefined;
687
+ const messages = conversation?.messages ?? [];
688
+ const send = useCallback((content, attachments) => {
689
+ const convId = activeConvId ?? generateId();
690
+ const msgId = generateId();
691
+ store.dispatch({
692
+ attachments,
693
+ content,
694
+ conversationId: convId,
695
+ messageId: msgId,
696
+ type: "send"
697
+ });
698
+ connection.send({
699
+ attachments,
700
+ content,
701
+ conversationId: convId,
702
+ type: "message"
703
+ });
704
+ }, [activeConvId, connection, store]);
705
+ const cancel = useCallback(() => {
706
+ if (activeConvId) {
707
+ store.dispatch({ type: "cancel" });
708
+ connection.send({ conversationId: activeConvId, type: "cancel" });
709
+ }
710
+ }, [activeConvId, connection, store]);
711
+ const branch = useCallback((messageId, content) => {
712
+ if (activeConvId) {
713
+ connection.send({
714
+ content,
715
+ conversationId: activeConvId,
716
+ messageId,
717
+ type: "branch"
718
+ });
719
+ }
720
+ }, [activeConvId, connection]);
721
+ return {
722
+ branch,
723
+ cancel,
724
+ error: state.error,
725
+ isStreaming: state.isStreaming,
726
+ messages,
727
+ send
728
+ };
729
+ };
730
+ export {
731
+ useAIStream,
732
+ AIStreamProvider
733
+ };
734
+
735
+ //# debugId=70342D4BF2770C0F64756E2164756E21
736
+ //# sourceMappingURL=index.js.map