@aomi-labs/react 0.2.1 → 0.2.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,2562 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
+ var __export = (target, all) => {
24
+ for (var name in all)
25
+ __defProp(target, name, { get: all[name], enumerable: true });
26
+ };
27
+ var __copyProps = (to, from, except, desc) => {
28
+ if (from && typeof from === "object" || typeof from === "function") {
29
+ for (let key of __getOwnPropNames(from))
30
+ if (!__hasOwnProp.call(to, key) && key !== except)
31
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
32
+ }
33
+ return to;
34
+ };
35
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
+
37
+ // packages/react/src/index.ts
38
+ var index_exports = {};
39
+ __export(index_exports, {
40
+ AomiRuntimeProvider: () => AomiRuntimeProvider,
41
+ BackendApi: () => BackendApi,
42
+ ControlContextProvider: () => ControlContextProvider,
43
+ EventContextProvider: () => EventContextProvider,
44
+ NotificationContextProvider: () => NotificationContextProvider,
45
+ ThreadContextProvider: () => ThreadContextProvider,
46
+ UserContextProvider: () => UserContextProvider,
47
+ cn: () => cn,
48
+ formatAddress: () => formatAddress,
49
+ getNetworkName: () => getNetworkName,
50
+ initThreadControl: () => initThreadControl,
51
+ useAomiRuntime: () => useAomiRuntime,
52
+ useControl: () => useControl,
53
+ useCurrentThreadMessages: () => useCurrentThreadMessages,
54
+ useCurrentThreadMetadata: () => useCurrentThreadMetadata,
55
+ useEventContext: () => useEventContext,
56
+ useNotification: () => useNotification,
57
+ useNotificationHandler: () => useNotificationHandler,
58
+ useThreadContext: () => useThreadContext,
59
+ useUser: () => useUser,
60
+ useWalletHandler: () => useWalletHandler
61
+ });
62
+ module.exports = __toCommonJS(index_exports);
63
+
64
+ // packages/react/src/backend/sse.ts
65
+ function extractSseData(rawEvent) {
66
+ const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart());
67
+ if (!dataLines.length) return null;
68
+ return dataLines.join("\n");
69
+ }
70
+ async function readSseStream(stream, signal, onMessage) {
71
+ const reader = stream.getReader();
72
+ const decoder = new TextDecoder();
73
+ let buffer = "";
74
+ try {
75
+ while (!signal.aborted) {
76
+ const { value, done } = await reader.read();
77
+ if (done) break;
78
+ buffer += decoder.decode(value, { stream: true });
79
+ buffer = buffer.replace(/\r/g, "");
80
+ let separatorIndex = buffer.indexOf("\n\n");
81
+ while (separatorIndex >= 0) {
82
+ const rawEvent = buffer.slice(0, separatorIndex);
83
+ buffer = buffer.slice(separatorIndex + 2);
84
+ const data = extractSseData(rawEvent);
85
+ if (data) {
86
+ onMessage(data);
87
+ }
88
+ separatorIndex = buffer.indexOf("\n\n");
89
+ }
90
+ }
91
+ } finally {
92
+ reader.releaseLock();
93
+ }
94
+ }
95
+ function createSseSubscriber({
96
+ backendUrl,
97
+ getHeaders,
98
+ shouldLog = process.env.NODE_ENV !== "production"
99
+ }) {
100
+ const subscriptions = /* @__PURE__ */ new Map();
101
+ const subscribe2 = (sessionId, onUpdate, onError) => {
102
+ const existing = subscriptions.get(sessionId);
103
+ const listener = { onUpdate, onError };
104
+ if (existing) {
105
+ existing.listeners.add(listener);
106
+ if (shouldLog) {
107
+ console.debug("[aomi][sse] listener added", {
108
+ sessionId,
109
+ listeners: existing.listeners.size
110
+ });
111
+ }
112
+ return () => {
113
+ existing.listeners.delete(listener);
114
+ if (shouldLog) {
115
+ console.debug("[aomi][sse] listener removed", {
116
+ sessionId,
117
+ listeners: existing.listeners.size
118
+ });
119
+ }
120
+ if (existing.listeners.size === 0) {
121
+ existing.stop("unsubscribe");
122
+ if (subscriptions.get(sessionId) === existing) {
123
+ subscriptions.delete(sessionId);
124
+ }
125
+ }
126
+ };
127
+ }
128
+ const subscription = {
129
+ abortController: null,
130
+ retries: 0,
131
+ retryTimer: null,
132
+ stopped: false,
133
+ listeners: /* @__PURE__ */ new Set([listener]),
134
+ stop: (reason) => {
135
+ var _a;
136
+ subscription.stopped = true;
137
+ if (subscription.retryTimer) {
138
+ clearTimeout(subscription.retryTimer);
139
+ subscription.retryTimer = null;
140
+ }
141
+ (_a = subscription.abortController) == null ? void 0 : _a.abort();
142
+ subscription.abortController = null;
143
+ if (shouldLog) {
144
+ console.debug("[aomi][sse] stop", {
145
+ sessionId,
146
+ reason,
147
+ retries: subscription.retries
148
+ });
149
+ }
150
+ }
151
+ };
152
+ const scheduleRetry = () => {
153
+ if (subscription.stopped) return;
154
+ subscription.retries += 1;
155
+ const delayMs = Math.min(500 * 2 ** (subscription.retries - 1), 1e4);
156
+ if (shouldLog) {
157
+ console.debug("[aomi][sse] retry scheduled", {
158
+ sessionId,
159
+ delayMs,
160
+ retries: subscription.retries
161
+ });
162
+ }
163
+ subscription.retryTimer = setTimeout(() => {
164
+ void open();
165
+ }, delayMs);
166
+ };
167
+ const open = async () => {
168
+ var _a;
169
+ if (subscription.stopped) return;
170
+ if (subscription.retryTimer) {
171
+ clearTimeout(subscription.retryTimer);
172
+ subscription.retryTimer = null;
173
+ }
174
+ const controller = new AbortController();
175
+ subscription.abortController = controller;
176
+ const openedAt = Date.now();
177
+ try {
178
+ const response = await fetch(`${backendUrl}/api/updates`, {
179
+ headers: getHeaders(sessionId),
180
+ signal: controller.signal
181
+ });
182
+ if (!response.ok) {
183
+ throw new Error(
184
+ `SSE HTTP ${response.status}: ${response.statusText}`
185
+ );
186
+ }
187
+ if (!response.body) {
188
+ throw new Error("SSE response missing body");
189
+ }
190
+ subscription.retries = 0;
191
+ await readSseStream(response.body, controller.signal, (data) => {
192
+ var _a2, _b;
193
+ let parsed;
194
+ try {
195
+ parsed = JSON.parse(data);
196
+ } catch (error) {
197
+ for (const item of subscription.listeners) {
198
+ (_a2 = item.onError) == null ? void 0 : _a2.call(item, error);
199
+ }
200
+ return;
201
+ }
202
+ for (const item of subscription.listeners) {
203
+ try {
204
+ item.onUpdate(parsed);
205
+ } catch (error) {
206
+ (_b = item.onError) == null ? void 0 : _b.call(item, error);
207
+ }
208
+ }
209
+ });
210
+ if (shouldLog) {
211
+ console.debug("[aomi][sse] stream ended", {
212
+ sessionId,
213
+ aborted: controller.signal.aborted,
214
+ stopped: subscription.stopped,
215
+ durationMs: Date.now() - openedAt
216
+ });
217
+ }
218
+ } catch (error) {
219
+ if (!controller.signal.aborted && !subscription.stopped) {
220
+ for (const item of subscription.listeners) {
221
+ (_a = item.onError) == null ? void 0 : _a.call(item, error);
222
+ }
223
+ }
224
+ }
225
+ if (!subscription.stopped) {
226
+ scheduleRetry();
227
+ }
228
+ };
229
+ subscriptions.set(sessionId, subscription);
230
+ void open();
231
+ return () => {
232
+ subscription.listeners.delete(listener);
233
+ if (shouldLog) {
234
+ console.debug("[aomi][sse] listener removed", {
235
+ sessionId,
236
+ listeners: subscription.listeners.size
237
+ });
238
+ }
239
+ if (subscription.listeners.size === 0) {
240
+ subscription.stop("unsubscribe");
241
+ if (subscriptions.get(sessionId) === subscription) {
242
+ subscriptions.delete(sessionId);
243
+ }
244
+ }
245
+ };
246
+ };
247
+ return { subscribe: subscribe2 };
248
+ }
249
+
250
+ // packages/react/src/backend/client.ts
251
+ var SESSION_ID_HEADER = "X-Session-Id";
252
+ var API_KEY_HEADER = "X-API-Key";
253
+ function toQueryString(payload) {
254
+ const params = new URLSearchParams();
255
+ for (const [key, value] of Object.entries(payload)) {
256
+ if (value === void 0 || value === null) continue;
257
+ params.set(key, String(value));
258
+ }
259
+ const qs = params.toString();
260
+ return qs ? `?${qs}` : "";
261
+ }
262
+ function withSessionHeader(sessionId, init) {
263
+ const headers = new Headers(init);
264
+ headers.set(SESSION_ID_HEADER, sessionId);
265
+ return headers;
266
+ }
267
+ async function postState(backendUrl, path, payload, sessionId, apiKey) {
268
+ const query = toQueryString(payload);
269
+ const url = `${backendUrl}${path}${query}`;
270
+ const headers = new Headers(withSessionHeader(sessionId));
271
+ if (apiKey) {
272
+ headers.set(API_KEY_HEADER, apiKey);
273
+ }
274
+ const response = await fetch(url, {
275
+ method: "POST",
276
+ headers
277
+ });
278
+ if (!response.ok) {
279
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
280
+ }
281
+ return await response.json();
282
+ }
283
+ var BackendApi = class {
284
+ constructor(backendUrl) {
285
+ this.backendUrl = backendUrl;
286
+ this.sseSubscriber = createSseSubscriber({
287
+ backendUrl,
288
+ getHeaders: (sessionId) => withSessionHeader(sessionId, { Accept: "text/event-stream" })
289
+ });
290
+ }
291
+ async fetchState(sessionId, userState) {
292
+ const url = new URL("/api/state", this.backendUrl);
293
+ if (userState) {
294
+ url.searchParams.set("user_state", JSON.stringify(userState));
295
+ }
296
+ const response = await fetch(url.toString(), {
297
+ headers: withSessionHeader(sessionId)
298
+ });
299
+ if (!response.ok) {
300
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
301
+ }
302
+ return await response.json();
303
+ }
304
+ async postChatMessage(sessionId, message, namespace, publicKey, apiKey) {
305
+ const payload = { message, namespace };
306
+ if (publicKey) {
307
+ payload.public_key = publicKey;
308
+ }
309
+ return postState(
310
+ this.backendUrl,
311
+ "/api/chat",
312
+ payload,
313
+ sessionId,
314
+ apiKey
315
+ );
316
+ }
317
+ async postSystemMessage(sessionId, message) {
318
+ return postState(
319
+ this.backendUrl,
320
+ "/api/system",
321
+ {
322
+ message
323
+ },
324
+ sessionId
325
+ );
326
+ }
327
+ async postInterrupt(sessionId) {
328
+ return postState(
329
+ this.backendUrl,
330
+ "/api/interrupt",
331
+ {},
332
+ sessionId
333
+ );
334
+ }
335
+ /**
336
+ * Subscribe to SSE updates for a session.
337
+ * Uses fetch streaming and reconnects on disconnects.
338
+ * Returns an unsubscribe function.
339
+ */
340
+ subscribeSSE(sessionId, onUpdate, onError) {
341
+ return this.sseSubscriber.subscribe(sessionId, onUpdate, onError);
342
+ }
343
+ async fetchThreads(publicKey) {
344
+ const url = `${this.backendUrl}/api/sessions?public_key=${encodeURIComponent(publicKey)}`;
345
+ const response = await fetch(url);
346
+ if (!response.ok) {
347
+ throw new Error(`Failed to fetch threads: HTTP ${response.status}`);
348
+ }
349
+ return await response.json();
350
+ }
351
+ async fetchThread(sessionId) {
352
+ const url = `${this.backendUrl}/api/sessions/${encodeURIComponent(sessionId)}`;
353
+ const response = await fetch(url, {
354
+ headers: withSessionHeader(sessionId)
355
+ });
356
+ if (!response.ok) {
357
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
358
+ }
359
+ return await response.json();
360
+ }
361
+ async createThread(threadId, publicKey) {
362
+ const body = {};
363
+ if (publicKey) body.public_key = publicKey;
364
+ const url = `${this.backendUrl}/api/sessions`;
365
+ const response = await fetch(url, {
366
+ method: "POST",
367
+ headers: withSessionHeader(threadId, {
368
+ "Content-Type": "application/json"
369
+ }),
370
+ body: JSON.stringify(body)
371
+ });
372
+ if (!response.ok) {
373
+ throw new Error(`Failed to create thread: HTTP ${response.status}`);
374
+ }
375
+ return await response.json();
376
+ }
377
+ async archiveThread(sessionId) {
378
+ const url = `${this.backendUrl}/api/sessions/${encodeURIComponent(sessionId)}/archive`;
379
+ const response = await fetch(url, {
380
+ method: "POST",
381
+ headers: withSessionHeader(sessionId)
382
+ });
383
+ if (!response.ok) {
384
+ throw new Error(`Failed to archive thread: HTTP ${response.status}`);
385
+ }
386
+ }
387
+ async unarchiveThread(sessionId) {
388
+ const url = `${this.backendUrl}/api/sessions/${encodeURIComponent(sessionId)}/unarchive`;
389
+ const response = await fetch(url, {
390
+ method: "POST",
391
+ headers: withSessionHeader(sessionId)
392
+ });
393
+ if (!response.ok) {
394
+ throw new Error(`Failed to unarchive thread: HTTP ${response.status}`);
395
+ }
396
+ }
397
+ async deleteThread(sessionId) {
398
+ const url = `${this.backendUrl}/api/sessions/${encodeURIComponent(sessionId)}`;
399
+ const response = await fetch(url, {
400
+ method: "DELETE",
401
+ headers: withSessionHeader(sessionId)
402
+ });
403
+ if (!response.ok) {
404
+ throw new Error(`Failed to delete thread: HTTP ${response.status}`);
405
+ }
406
+ }
407
+ async renameThread(sessionId, newTitle) {
408
+ const url = `${this.backendUrl}/api/sessions/${encodeURIComponent(sessionId)}`;
409
+ const response = await fetch(url, {
410
+ method: "PATCH",
411
+ headers: withSessionHeader(sessionId, {
412
+ "Content-Type": "application/json"
413
+ }),
414
+ body: JSON.stringify({ title: newTitle })
415
+ });
416
+ if (!response.ok) {
417
+ throw new Error(`Failed to rename thread: HTTP ${response.status}`);
418
+ }
419
+ }
420
+ async getSystemEvents(sessionId, count) {
421
+ const url = new URL("/api/events", this.backendUrl);
422
+ if (count !== void 0) {
423
+ url.searchParams.set("count", String(count));
424
+ }
425
+ const response = await fetch(url.toString(), {
426
+ headers: withSessionHeader(sessionId)
427
+ });
428
+ if (!response.ok) {
429
+ if (response.status === 404) return [];
430
+ throw new Error(`Failed to get system events: HTTP ${response.status}`);
431
+ }
432
+ return await response.json();
433
+ }
434
+ // ===========================================================================
435
+ // Control API
436
+ // ===========================================================================
437
+ /**
438
+ * Get allowed namespaces for the current request context.
439
+ */
440
+ async getNamespaces(sessionId, publicKey, apiKey) {
441
+ const url = new URL("/api/control/namespaces", this.backendUrl);
442
+ if (publicKey) {
443
+ url.searchParams.set("public_key", publicKey);
444
+ }
445
+ console.log("[BackendApi.getNamespaces]", {
446
+ backendUrl: this.backendUrl,
447
+ fullUrl: url.toString(),
448
+ sessionId,
449
+ publicKey
450
+ });
451
+ const headers = new Headers(withSessionHeader(sessionId));
452
+ if (apiKey) {
453
+ headers.set(API_KEY_HEADER, apiKey);
454
+ }
455
+ const response = await fetch(url.toString(), { headers });
456
+ if (!response.ok) {
457
+ throw new Error(`Failed to get namespaces: HTTP ${response.status}`);
458
+ }
459
+ return await response.json();
460
+ }
461
+ /**
462
+ * Get available models.
463
+ */
464
+ async getModels(sessionId) {
465
+ const url = new URL("/api/control/models", this.backendUrl);
466
+ console.log("[BackendApi.getModels]", {
467
+ backendUrl: this.backendUrl,
468
+ fullUrl: url.toString(),
469
+ sessionId
470
+ });
471
+ const response = await fetch(url.toString(), {
472
+ headers: withSessionHeader(sessionId)
473
+ });
474
+ if (!response.ok) {
475
+ throw new Error(`Failed to get models: HTTP ${response.status}`);
476
+ }
477
+ return await response.json();
478
+ }
479
+ /**
480
+ * Set the model selection for a session.
481
+ */
482
+ async setModel(sessionId, rig, namespace, apiKey) {
483
+ const payload = { rig };
484
+ if (namespace) {
485
+ payload.namespace = namespace;
486
+ }
487
+ return postState(this.backendUrl, "/api/control/model", payload, sessionId, apiKey);
488
+ }
489
+ };
490
+
491
+ // packages/react/src/runtime/aomi-runtime.tsx
492
+ var import_react10 = require("react");
493
+
494
+ // packages/react/src/contexts/control-context.tsx
495
+ var import_react = require("react");
496
+
497
+ // packages/react/src/utils/uuid.ts
498
+ function generateUUID() {
499
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
500
+ return crypto.randomUUID();
501
+ }
502
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
503
+ const r = Math.random() * 16 | 0;
504
+ const v = c === "x" ? r : r & 3 | 8;
505
+ return v.toString(16);
506
+ });
507
+ }
508
+
509
+ // packages/react/src/state/thread-store.ts
510
+ var shouldLogThreadUpdates = process.env.NODE_ENV !== "production";
511
+ var logThreadMetadataChange = (source, threadId, prev, next) => {
512
+ if (!shouldLogThreadUpdates) return;
513
+ if (!prev && !next) return;
514
+ if (!prev || !next) {
515
+ console.debug(`[aomi][thread:${source}]`, { threadId, prev, next });
516
+ return;
517
+ }
518
+ if (prev.title !== next.title || prev.status !== next.status || prev.lastActiveAt !== next.lastActiveAt) {
519
+ console.debug(`[aomi][thread:${source}]`, { threadId, prev, next });
520
+ }
521
+ };
522
+ function initThreadControl() {
523
+ return {
524
+ model: null,
525
+ namespace: null,
526
+ controlDirty: false,
527
+ isProcessing: false
528
+ };
529
+ }
530
+ var ThreadStore = class {
531
+ constructor(options) {
532
+ this.listeners = /* @__PURE__ */ new Set();
533
+ this.subscribe = (listener) => {
534
+ this.listeners.add(listener);
535
+ return () => {
536
+ this.listeners.delete(listener);
537
+ };
538
+ };
539
+ this.getSnapshot = () => this.snapshot;
540
+ this.setCurrentThreadId = (threadId) => {
541
+ this.ensureThreadExists(threadId);
542
+ this.updateState({ currentThreadId: threadId });
543
+ };
544
+ this.bumpThreadViewKey = () => {
545
+ this.updateState({ threadViewKey: this.state.threadViewKey + 1 });
546
+ };
547
+ this.setThreadCnt = (updater) => {
548
+ const nextCnt = this.resolveStateAction(updater, this.state.threadCnt);
549
+ this.updateState({ threadCnt: nextCnt });
550
+ };
551
+ this.setThreads = (updater) => {
552
+ const nextThreads = this.resolveStateAction(updater, this.state.threads);
553
+ this.updateState({ threads: new Map(nextThreads) });
554
+ };
555
+ this.setThreadMetadata = (updater) => {
556
+ const prevMetadata = this.state.threadMetadata;
557
+ const nextMetadata = this.resolveStateAction(updater, prevMetadata);
558
+ for (const [threadId, next] of nextMetadata.entries()) {
559
+ logThreadMetadataChange(
560
+ "setThreadMetadata",
561
+ threadId,
562
+ prevMetadata.get(threadId),
563
+ next
564
+ );
565
+ }
566
+ for (const [threadId, prev] of prevMetadata.entries()) {
567
+ if (!nextMetadata.has(threadId)) {
568
+ logThreadMetadataChange("setThreadMetadata", threadId, prev, void 0);
569
+ }
570
+ }
571
+ this.updateState({ threadMetadata: new Map(nextMetadata) });
572
+ };
573
+ this.setThreadMessages = (threadId, messages) => {
574
+ this.ensureThreadExists(threadId);
575
+ const nextThreads = new Map(this.state.threads);
576
+ nextThreads.set(threadId, messages);
577
+ this.updateState({ threads: nextThreads });
578
+ };
579
+ this.getThreadMessages = (threadId) => {
580
+ var _a;
581
+ return (_a = this.state.threads.get(threadId)) != null ? _a : [];
582
+ };
583
+ this.getThreadMetadata = (threadId) => {
584
+ return this.state.threadMetadata.get(threadId);
585
+ };
586
+ this.updateThreadMetadata = (threadId, updates) => {
587
+ const existing = this.state.threadMetadata.get(threadId);
588
+ if (!existing) {
589
+ return;
590
+ }
591
+ const next = __spreadValues(__spreadValues({}, existing), updates);
592
+ const nextMetadata = new Map(this.state.threadMetadata);
593
+ nextMetadata.set(threadId, next);
594
+ logThreadMetadataChange("updateThreadMetadata", threadId, existing, next);
595
+ this.updateState({ threadMetadata: nextMetadata });
596
+ };
597
+ var _a;
598
+ const initialThreadId = (_a = options == null ? void 0 : options.initialThreadId) != null ? _a : generateUUID();
599
+ this.state = {
600
+ currentThreadId: initialThreadId,
601
+ threadViewKey: 0,
602
+ threadCnt: 1,
603
+ threads: /* @__PURE__ */ new Map([[initialThreadId, []]]),
604
+ threadMetadata: /* @__PURE__ */ new Map([
605
+ [
606
+ initialThreadId,
607
+ {
608
+ title: "New Chat",
609
+ status: "pending",
610
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
611
+ control: initThreadControl()
612
+ }
613
+ ]
614
+ ])
615
+ };
616
+ this.snapshot = this.buildSnapshot();
617
+ }
618
+ emit() {
619
+ for (const listener of this.listeners) {
620
+ listener();
621
+ }
622
+ }
623
+ resolveStateAction(updater, current) {
624
+ return typeof updater === "function" ? updater(current) : updater;
625
+ }
626
+ ensureThreadExists(threadId) {
627
+ if (!this.state.threadMetadata.has(threadId)) {
628
+ const nextMetadata = new Map(this.state.threadMetadata);
629
+ nextMetadata.set(threadId, {
630
+ title: "New Chat",
631
+ status: "regular",
632
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
633
+ control: initThreadControl()
634
+ });
635
+ this.state = __spreadProps(__spreadValues({}, this.state), { threadMetadata: nextMetadata });
636
+ }
637
+ if (!this.state.threads.has(threadId)) {
638
+ const nextThreads = new Map(this.state.threads);
639
+ nextThreads.set(threadId, []);
640
+ this.state = __spreadProps(__spreadValues({}, this.state), { threads: nextThreads });
641
+ }
642
+ }
643
+ updateState(partial) {
644
+ this.state = __spreadValues(__spreadValues({}, this.state), partial);
645
+ this.snapshot = this.buildSnapshot();
646
+ this.emit();
647
+ }
648
+ buildSnapshot() {
649
+ return {
650
+ currentThreadId: this.state.currentThreadId,
651
+ setCurrentThreadId: this.setCurrentThreadId,
652
+ threadViewKey: this.state.threadViewKey,
653
+ bumpThreadViewKey: this.bumpThreadViewKey,
654
+ allThreads: this.state.threads,
655
+ setThreads: this.setThreads,
656
+ allThreadsMetadata: this.state.threadMetadata,
657
+ setThreadMetadata: this.setThreadMetadata,
658
+ threadCnt: this.state.threadCnt,
659
+ setThreadCnt: this.setThreadCnt,
660
+ getThreadMessages: this.getThreadMessages,
661
+ setThreadMessages: this.setThreadMessages,
662
+ getThreadMetadata: this.getThreadMetadata,
663
+ updateThreadMetadata: this.updateThreadMetadata
664
+ };
665
+ }
666
+ };
667
+
668
+ // packages/react/src/contexts/control-context.tsx
669
+ var import_jsx_runtime = require("react/jsx-runtime");
670
+ var API_KEY_STORAGE_KEY = "aomi_api_key";
671
+ var ControlContext = (0, import_react.createContext)(null);
672
+ function useControl() {
673
+ const ctx = (0, import_react.useContext)(ControlContext);
674
+ if (!ctx) {
675
+ throw new Error("useControl must be used within ControlContextProvider");
676
+ }
677
+ return ctx;
678
+ }
679
+ function ControlContextProvider({
680
+ children,
681
+ backendApi,
682
+ sessionId,
683
+ publicKey,
684
+ getThreadMetadata,
685
+ updateThreadMetadata
686
+ }) {
687
+ var _a, _b;
688
+ const [state, setStateInternal] = (0, import_react.useState)(() => ({
689
+ apiKey: null,
690
+ availableModels: [],
691
+ authorizedNamespaces: [],
692
+ defaultModel: null,
693
+ defaultNamespace: null
694
+ }));
695
+ const stateRef = (0, import_react.useRef)(state);
696
+ stateRef.current = state;
697
+ const backendApiRef = (0, import_react.useRef)(backendApi);
698
+ backendApiRef.current = backendApi;
699
+ const sessionIdRef = (0, import_react.useRef)(sessionId);
700
+ sessionIdRef.current = sessionId;
701
+ const publicKeyRef = (0, import_react.useRef)(publicKey);
702
+ publicKeyRef.current = publicKey;
703
+ const getThreadMetadataRef = (0, import_react.useRef)(getThreadMetadata);
704
+ getThreadMetadataRef.current = getThreadMetadata;
705
+ const updateThreadMetadataRef = (0, import_react.useRef)(updateThreadMetadata);
706
+ updateThreadMetadataRef.current = updateThreadMetadata;
707
+ const callbacks = (0, import_react.useRef)(/* @__PURE__ */ new Set());
708
+ const currentThreadMetadata = getThreadMetadata(sessionId);
709
+ const isProcessing = (_b = (_a = currentThreadMetadata == null ? void 0 : currentThreadMetadata.control) == null ? void 0 : _a.isProcessing) != null ? _b : false;
710
+ (0, import_react.useEffect)(() => {
711
+ var _a2, _b2;
712
+ try {
713
+ const storedApiKey = (_b2 = (_a2 = globalThis.localStorage) == null ? void 0 : _a2.getItem(API_KEY_STORAGE_KEY)) != null ? _b2 : null;
714
+ if (storedApiKey) {
715
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), { apiKey: storedApiKey }));
716
+ }
717
+ } catch (e) {
718
+ }
719
+ }, []);
720
+ (0, import_react.useEffect)(() => {
721
+ var _a2, _b2;
722
+ try {
723
+ if (state.apiKey) {
724
+ (_a2 = globalThis.localStorage) == null ? void 0 : _a2.setItem(API_KEY_STORAGE_KEY, state.apiKey);
725
+ } else {
726
+ (_b2 = globalThis.localStorage) == null ? void 0 : _b2.removeItem(API_KEY_STORAGE_KEY);
727
+ }
728
+ } catch (e) {
729
+ }
730
+ }, [state.apiKey]);
731
+ (0, import_react.useEffect)(() => {
732
+ const fetchNamespaces = async () => {
733
+ var _a2, _b2;
734
+ try {
735
+ const namespaces = await backendApiRef.current.getNamespaces(
736
+ sessionIdRef.current,
737
+ publicKeyRef.current,
738
+ (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
739
+ );
740
+ const defaultNs = namespaces.includes("default") ? "default" : (_b2 = namespaces[0]) != null ? _b2 : null;
741
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
742
+ authorizedNamespaces: namespaces,
743
+ defaultNamespace: defaultNs
744
+ }));
745
+ } catch (error) {
746
+ console.error("Failed to fetch namespaces:", error);
747
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
748
+ authorizedNamespaces: ["default"],
749
+ defaultNamespace: "default"
750
+ }));
751
+ }
752
+ };
753
+ void fetchNamespaces();
754
+ }, [state.apiKey]);
755
+ (0, import_react.useEffect)(() => {
756
+ const fetchModels = async () => {
757
+ try {
758
+ const models = await backendApiRef.current.getModels(
759
+ sessionIdRef.current
760
+ );
761
+ setStateInternal((prev) => {
762
+ var _a2;
763
+ return __spreadProps(__spreadValues({}, prev), {
764
+ availableModels: models,
765
+ defaultModel: (_a2 = models[0]) != null ? _a2 : null
766
+ });
767
+ });
768
+ } catch (error) {
769
+ console.error("Failed to fetch models:", error);
770
+ }
771
+ };
772
+ void fetchModels();
773
+ }, []);
774
+ const setApiKey = (0, import_react.useCallback)((apiKey) => {
775
+ setStateInternal((prev) => {
776
+ const next = __spreadProps(__spreadValues({}, prev), { apiKey: apiKey === "" ? null : apiKey });
777
+ callbacks.current.forEach((cb) => cb(next));
778
+ return next;
779
+ });
780
+ }, []);
781
+ const getAvailableModels = (0, import_react.useCallback)(async () => {
782
+ try {
783
+ const models = await backendApiRef.current.getModels(
784
+ sessionIdRef.current
785
+ );
786
+ setStateInternal((prev) => {
787
+ var _a2, _b2;
788
+ return __spreadProps(__spreadValues({}, prev), {
789
+ availableModels: models,
790
+ defaultModel: (_b2 = (_a2 = prev.defaultModel) != null ? _a2 : models[0]) != null ? _b2 : null
791
+ });
792
+ });
793
+ return models;
794
+ } catch (error) {
795
+ console.error("Failed to fetch models:", error);
796
+ return [];
797
+ }
798
+ }, []);
799
+ const getAuthorizedNamespaces = (0, import_react.useCallback)(async () => {
800
+ var _a2, _b2;
801
+ try {
802
+ const namespaces = await backendApiRef.current.getNamespaces(
803
+ sessionIdRef.current,
804
+ publicKeyRef.current,
805
+ (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
806
+ );
807
+ const defaultNs = namespaces.includes("default") ? "default" : (_b2 = namespaces[0]) != null ? _b2 : null;
808
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
809
+ authorizedNamespaces: namespaces,
810
+ defaultNamespace: defaultNs
811
+ }));
812
+ return namespaces;
813
+ } catch (error) {
814
+ console.error("Failed to fetch namespaces:", error);
815
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
816
+ authorizedNamespaces: ["default"],
817
+ defaultNamespace: "default"
818
+ }));
819
+ return ["default"];
820
+ }
821
+ }, []);
822
+ const getCurrentThreadControl = (0, import_react.useCallback)(() => {
823
+ var _a2;
824
+ const metadata = getThreadMetadataRef.current(sessionIdRef.current);
825
+ return (_a2 = metadata == null ? void 0 : metadata.control) != null ? _a2 : initThreadControl();
826
+ }, []);
827
+ const onModelSelect = (0, import_react.useCallback)(async (model) => {
828
+ var _a2, _b2, _c, _d, _e;
829
+ const threadId = sessionIdRef.current;
830
+ const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(threadId)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
831
+ const isProcessing2 = currentControl.isProcessing;
832
+ console.log("[control-context] onModelSelect called", {
833
+ model,
834
+ isProcessing: isProcessing2,
835
+ threadId
836
+ });
837
+ if (isProcessing2) {
838
+ console.warn("[control-context] Cannot switch model while processing");
839
+ return;
840
+ }
841
+ const namespace = (_d = (_c = currentControl.namespace) != null ? _c : stateRef.current.defaultNamespace) != null ? _d : "default";
842
+ console.log("[control-context] onModelSelect updating metadata", {
843
+ threadId,
844
+ model,
845
+ namespace,
846
+ currentControl
847
+ });
848
+ updateThreadMetadataRef.current(threadId, {
849
+ control: __spreadProps(__spreadValues({}, currentControl), {
850
+ model,
851
+ namespace,
852
+ controlDirty: true
853
+ })
854
+ });
855
+ console.log("[control-context] onModelSelect calling backend setModel", {
856
+ threadId,
857
+ model,
858
+ namespace,
859
+ backendUrl: backendApiRef.current
860
+ });
861
+ try {
862
+ const result = await backendApiRef.current.setModel(
863
+ threadId,
864
+ model,
865
+ namespace,
866
+ (_e = stateRef.current.apiKey) != null ? _e : void 0
867
+ );
868
+ console.log("[control-context] onModelSelect backend result", result);
869
+ } catch (err) {
870
+ console.error("[control-context] setModel failed:", err);
871
+ throw err;
872
+ }
873
+ }, []);
874
+ const onNamespaceSelect = (0, import_react.useCallback)((namespace) => {
875
+ var _a2, _b2;
876
+ const threadId = sessionIdRef.current;
877
+ const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(threadId)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
878
+ const isProcessing2 = currentControl.isProcessing;
879
+ console.log("[control-context] onNamespaceSelect called", {
880
+ namespace,
881
+ isProcessing: isProcessing2,
882
+ threadId
883
+ });
884
+ if (isProcessing2) {
885
+ console.warn(
886
+ "[control-context] Cannot switch namespace while processing"
887
+ );
888
+ return;
889
+ }
890
+ console.log("[control-context] onNamespaceSelect updating metadata", {
891
+ threadId,
892
+ namespace,
893
+ currentControl
894
+ });
895
+ updateThreadMetadataRef.current(threadId, {
896
+ control: __spreadProps(__spreadValues({}, currentControl), {
897
+ namespace,
898
+ controlDirty: true
899
+ })
900
+ });
901
+ console.log("[control-context] onNamespaceSelect metadata updated");
902
+ }, []);
903
+ const markControlSynced = (0, import_react.useCallback)(() => {
904
+ var _a2, _b2;
905
+ const threadId = sessionIdRef.current;
906
+ const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(threadId)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
907
+ if (currentControl.controlDirty) {
908
+ updateThreadMetadataRef.current(threadId, {
909
+ control: __spreadProps(__spreadValues({}, currentControl), {
910
+ controlDirty: false
911
+ })
912
+ });
913
+ }
914
+ }, []);
915
+ const getControlState = (0, import_react.useCallback)(() => stateRef.current, []);
916
+ const onControlStateChange = (0, import_react.useCallback)(
917
+ (callback) => {
918
+ callbacks.current.add(callback);
919
+ return () => {
920
+ callbacks.current.delete(callback);
921
+ };
922
+ },
923
+ []
924
+ );
925
+ const setState = (0, import_react.useCallback)(
926
+ (updates) => {
927
+ var _a2;
928
+ if ("apiKey" in updates) {
929
+ setApiKey((_a2 = updates.apiKey) != null ? _a2 : null);
930
+ }
931
+ if ("namespace" in updates && updates.namespace !== void 0 && updates.namespace !== null) {
932
+ onNamespaceSelect(updates.namespace);
933
+ }
934
+ },
935
+ [setApiKey, onNamespaceSelect]
936
+ );
937
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
938
+ ControlContext.Provider,
939
+ {
940
+ value: {
941
+ state,
942
+ setApiKey,
943
+ getAvailableModels,
944
+ getAuthorizedNamespaces,
945
+ getCurrentThreadControl,
946
+ onModelSelect,
947
+ onNamespaceSelect,
948
+ isProcessing,
949
+ markControlSynced,
950
+ getControlState,
951
+ onControlStateChange,
952
+ setState
953
+ },
954
+ children
955
+ }
956
+ );
957
+ }
958
+
959
+ // packages/react/src/contexts/event-context.tsx
960
+ var import_react2 = require("react");
961
+
962
+ // packages/react/src/backend/types.ts
963
+ function isInlineCall(event) {
964
+ return "InlineCall" in event;
965
+ }
966
+ function isSystemNotice(event) {
967
+ return "SystemNotice" in event;
968
+ }
969
+ function isSystemError(event) {
970
+ return "SystemError" in event;
971
+ }
972
+ function isAsyncCallback(event) {
973
+ return "AsyncCallback" in event;
974
+ }
975
+
976
+ // packages/react/src/state/event-buffer.ts
977
+ function createEventBuffer() {
978
+ return {
979
+ inboundQueue: [],
980
+ outboundQueue: [],
981
+ sseStatus: "disconnected",
982
+ lastEventId: null,
983
+ subscribers: /* @__PURE__ */ new Map()
984
+ };
985
+ }
986
+ function enqueueInbound(state, event) {
987
+ state.inboundQueue.push(__spreadProps(__spreadValues({}, event), {
988
+ status: "pending",
989
+ timestamp: Date.now()
990
+ }));
991
+ }
992
+ function subscribe(state, type, callback) {
993
+ if (!state.subscribers.has(type)) {
994
+ state.subscribers.set(type, /* @__PURE__ */ new Set());
995
+ }
996
+ state.subscribers.get(type).add(callback);
997
+ return () => {
998
+ var _a;
999
+ (_a = state.subscribers.get(type)) == null ? void 0 : _a.delete(callback);
1000
+ };
1001
+ }
1002
+ function dispatch(state, event) {
1003
+ const typeSubscribers = state.subscribers.get(event.type);
1004
+ if (typeSubscribers) {
1005
+ for (const callback of typeSubscribers) {
1006
+ callback(event);
1007
+ }
1008
+ }
1009
+ const allSubscribers = state.subscribers.get("*");
1010
+ if (allSubscribers) {
1011
+ for (const callback of allSubscribers) {
1012
+ callback(event);
1013
+ }
1014
+ }
1015
+ }
1016
+ function setSSEStatus(state, status) {
1017
+ state.sseStatus = status;
1018
+ }
1019
+
1020
+ // packages/react/src/contexts/event-context.tsx
1021
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1022
+ var EventContextState = (0, import_react2.createContext)(null);
1023
+ function useEventContext() {
1024
+ const context = (0, import_react2.useContext)(EventContextState);
1025
+ if (!context) {
1026
+ throw new Error(
1027
+ "useEventContext must be used within EventContextProvider. Wrap your app with <EventContextProvider>...</EventContextProvider>"
1028
+ );
1029
+ }
1030
+ return context;
1031
+ }
1032
+ function EventContextProvider({
1033
+ children,
1034
+ backendApi,
1035
+ sessionId
1036
+ }) {
1037
+ const bufferRef = (0, import_react2.useRef)(null);
1038
+ if (!bufferRef.current) {
1039
+ bufferRef.current = createEventBuffer();
1040
+ }
1041
+ const buffer = bufferRef.current;
1042
+ const [sseStatus, setSseStatus] = (0, import_react2.useState)("disconnected");
1043
+ (0, import_react2.useEffect)(() => {
1044
+ setSSEStatus(buffer, "connecting");
1045
+ setSseStatus("connecting");
1046
+ const unsubscribe = backendApi.subscribeSSE(
1047
+ sessionId,
1048
+ (event) => {
1049
+ enqueueInbound(buffer, {
1050
+ type: event.type,
1051
+ sessionId: event.session_id,
1052
+ payload: event
1053
+ });
1054
+ const inboundEvent = {
1055
+ type: event.type,
1056
+ sessionId: event.session_id,
1057
+ payload: event,
1058
+ status: "fetched",
1059
+ timestamp: Date.now()
1060
+ };
1061
+ dispatch(buffer, inboundEvent);
1062
+ },
1063
+ (error) => {
1064
+ console.error("SSE error:", error);
1065
+ setSSEStatus(buffer, "disconnected");
1066
+ setSseStatus("disconnected");
1067
+ }
1068
+ );
1069
+ setSSEStatus(buffer, "connected");
1070
+ setSseStatus("connected");
1071
+ return () => {
1072
+ unsubscribe();
1073
+ setSSEStatus(buffer, "disconnected");
1074
+ setSseStatus("disconnected");
1075
+ };
1076
+ }, [backendApi, sessionId, buffer]);
1077
+ const subscribeCallback = (0, import_react2.useCallback)(
1078
+ (type, callback) => {
1079
+ return subscribe(buffer, type, callback);
1080
+ },
1081
+ [buffer]
1082
+ );
1083
+ const sendOutbound = (0, import_react2.useCallback)(
1084
+ async (event) => {
1085
+ try {
1086
+ const message = JSON.stringify({
1087
+ type: event.type,
1088
+ payload: event.payload
1089
+ });
1090
+ await backendApi.postSystemMessage(event.sessionId, message);
1091
+ } catch (error) {
1092
+ console.error("Failed to send outbound event:", error);
1093
+ }
1094
+ },
1095
+ [backendApi]
1096
+ );
1097
+ const dispatchSystemEvents = (0, import_react2.useCallback)(
1098
+ (sessionId2, events) => {
1099
+ var _a;
1100
+ for (const event of events) {
1101
+ let eventType;
1102
+ let payload;
1103
+ if (isInlineCall(event)) {
1104
+ eventType = event.InlineCall.type;
1105
+ payload = (_a = event.InlineCall.payload) != null ? _a : event.InlineCall;
1106
+ } else if (isSystemNotice(event)) {
1107
+ eventType = "system_notice";
1108
+ payload = { message: event.SystemNotice };
1109
+ } else if (isSystemError(event)) {
1110
+ eventType = "system_error";
1111
+ payload = { message: event.SystemError };
1112
+ } else if (isAsyncCallback(event)) {
1113
+ eventType = "async_callback";
1114
+ payload = event.AsyncCallback;
1115
+ } else {
1116
+ console.warn("Unknown system event type:", event);
1117
+ continue;
1118
+ }
1119
+ const inboundEvent = {
1120
+ type: eventType,
1121
+ sessionId: sessionId2,
1122
+ payload,
1123
+ status: "fetched",
1124
+ timestamp: Date.now()
1125
+ };
1126
+ enqueueInbound(buffer, {
1127
+ type: eventType,
1128
+ sessionId: sessionId2,
1129
+ payload
1130
+ });
1131
+ dispatch(buffer, inboundEvent);
1132
+ }
1133
+ },
1134
+ [buffer]
1135
+ );
1136
+ const contextValue = {
1137
+ subscribe: subscribeCallback,
1138
+ sendOutboundSystem: sendOutbound,
1139
+ dispatchInboundSystem: dispatchSystemEvents,
1140
+ sseStatus
1141
+ };
1142
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EventContextState.Provider, { value: contextValue, children });
1143
+ }
1144
+
1145
+ // packages/react/src/contexts/notification-context.tsx
1146
+ var import_react3 = require("react");
1147
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1148
+ var NotificationContext = (0, import_react3.createContext)(null);
1149
+ function useNotification() {
1150
+ const context = (0, import_react3.useContext)(NotificationContext);
1151
+ if (!context) {
1152
+ throw new Error(
1153
+ "useNotification must be used within NotificationContextProvider"
1154
+ );
1155
+ }
1156
+ return context;
1157
+ }
1158
+ var notificationIdCounter = 0;
1159
+ function generateId() {
1160
+ return `notif-${Date.now()}-${++notificationIdCounter}`;
1161
+ }
1162
+ function NotificationContextProvider({
1163
+ children
1164
+ }) {
1165
+ const [notifications, setNotifications] = (0, import_react3.useState)([]);
1166
+ const showNotification = (0, import_react3.useCallback)((params) => {
1167
+ const id = generateId();
1168
+ const notification = __spreadProps(__spreadValues({}, params), {
1169
+ id,
1170
+ timestamp: Date.now()
1171
+ });
1172
+ setNotifications((prev) => [notification, ...prev]);
1173
+ return id;
1174
+ }, []);
1175
+ const dismissNotification = (0, import_react3.useCallback)((id) => {
1176
+ setNotifications((prev) => prev.filter((n) => n.id !== id));
1177
+ }, []);
1178
+ const clearAll = (0, import_react3.useCallback)(() => {
1179
+ setNotifications([]);
1180
+ }, []);
1181
+ const value = {
1182
+ notifications,
1183
+ showNotification,
1184
+ dismissNotification,
1185
+ clearAll
1186
+ };
1187
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(NotificationContext.Provider, { value, children });
1188
+ }
1189
+
1190
+ // packages/react/src/contexts/thread-context.tsx
1191
+ var import_react4 = require("react");
1192
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1193
+ var ThreadContextState = (0, import_react4.createContext)(null);
1194
+ function useThreadContext() {
1195
+ const context = (0, import_react4.useContext)(ThreadContextState);
1196
+ if (!context) {
1197
+ throw new Error(
1198
+ "useThreadContext must be used within ThreadContextProvider. Wrap your app with <ThreadContextProvider>...</ThreadContextProvider>"
1199
+ );
1200
+ }
1201
+ return context;
1202
+ }
1203
+ function ThreadContextProvider({
1204
+ children,
1205
+ initialThreadId
1206
+ }) {
1207
+ const storeRef = (0, import_react4.useRef)(null);
1208
+ if (!storeRef.current) {
1209
+ storeRef.current = new ThreadStore({ initialThreadId });
1210
+ }
1211
+ const store = storeRef.current;
1212
+ const value = (0, import_react4.useSyncExternalStore)(
1213
+ store.subscribe,
1214
+ store.getSnapshot,
1215
+ store.getSnapshot
1216
+ );
1217
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ThreadContextState.Provider, { value, children });
1218
+ }
1219
+ function useCurrentThreadMessages() {
1220
+ const { currentThreadId, getThreadMessages } = useThreadContext();
1221
+ return (0, import_react4.useMemo)(
1222
+ () => getThreadMessages(currentThreadId),
1223
+ [currentThreadId, getThreadMessages]
1224
+ );
1225
+ }
1226
+ function useCurrentThreadMetadata() {
1227
+ const { currentThreadId, getThreadMetadata } = useThreadContext();
1228
+ return (0, import_react4.useMemo)(
1229
+ () => getThreadMetadata(currentThreadId),
1230
+ [currentThreadId, getThreadMetadata]
1231
+ );
1232
+ }
1233
+
1234
+ // packages/react/src/contexts/user-context.tsx
1235
+ var import_react5 = require("react");
1236
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1237
+ var UserContext = (0, import_react5.createContext)(void 0);
1238
+ function useUser() {
1239
+ const context = (0, import_react5.useContext)(UserContext);
1240
+ if (!context) {
1241
+ throw new Error("useUser must be used within UserContextProvider");
1242
+ }
1243
+ return {
1244
+ user: context.user,
1245
+ setUser: context.setUser,
1246
+ getUserState: context.getUserState,
1247
+ onUserStateChange: context.onUserStateChange
1248
+ };
1249
+ }
1250
+ function UserContextProvider({ children }) {
1251
+ const [user, setUserState] = (0, import_react5.useState)({
1252
+ isConnected: false,
1253
+ address: void 0,
1254
+ chainId: void 0,
1255
+ ensName: void 0
1256
+ });
1257
+ const userRef = (0, import_react5.useRef)(user);
1258
+ userRef.current = user;
1259
+ const StateChangeCallbacks = (0, import_react5.useRef)(
1260
+ /* @__PURE__ */ new Set()
1261
+ );
1262
+ const setUser = (0, import_react5.useCallback)((data) => {
1263
+ setUserState((prev) => {
1264
+ const next = __spreadValues(__spreadValues({}, prev), data);
1265
+ StateChangeCallbacks.current.forEach((callback) => {
1266
+ callback(next);
1267
+ });
1268
+ return next;
1269
+ });
1270
+ }, []);
1271
+ const getUserState = (0, import_react5.useCallback)(() => userRef.current, []);
1272
+ const onUserStateChange = (0, import_react5.useCallback)(
1273
+ (callback) => {
1274
+ StateChangeCallbacks.current.add(callback);
1275
+ return () => {
1276
+ StateChangeCallbacks.current.delete(callback);
1277
+ };
1278
+ },
1279
+ []
1280
+ );
1281
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1282
+ UserContext.Provider,
1283
+ {
1284
+ value: {
1285
+ user,
1286
+ setUser,
1287
+ getUserState,
1288
+ onUserStateChange
1289
+ },
1290
+ children
1291
+ }
1292
+ );
1293
+ }
1294
+
1295
+ // packages/react/src/runtime/core.tsx
1296
+ var import_react8 = require("react");
1297
+ var import_react9 = require("@assistant-ui/react");
1298
+
1299
+ // packages/react/src/runtime/orchestrator.ts
1300
+ var import_react6 = require("react");
1301
+
1302
+ // packages/react/src/runtime/utils.ts
1303
+ var import_clsx = require("clsx");
1304
+ var import_tailwind_merge = require("tailwind-merge");
1305
+ function cn(...inputs) {
1306
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
1307
+ }
1308
+ var parseTimestamp = (value) => {
1309
+ if (value === void 0 || value === null) return 0;
1310
+ if (typeof value === "number") {
1311
+ return Number.isFinite(value) ? value < 1e12 ? value * 1e3 : value : 0;
1312
+ }
1313
+ const numeric = Number(value);
1314
+ if (!Number.isNaN(numeric)) {
1315
+ return numeric < 1e12 ? numeric * 1e3 : numeric;
1316
+ }
1317
+ const ts = Date.parse(value);
1318
+ return Number.isNaN(ts) ? 0 : ts;
1319
+ };
1320
+ var isPlaceholderTitle = (title) => {
1321
+ var _a;
1322
+ const normalized = (_a = title == null ? void 0 : title.trim()) != null ? _a : "";
1323
+ return !normalized || normalized.startsWith("#[");
1324
+ };
1325
+ function toInboundMessage(msg) {
1326
+ var _a;
1327
+ if (msg.sender === "system") return null;
1328
+ const content = [];
1329
+ const role = msg.sender === "user" ? "user" : "assistant";
1330
+ if (msg.content) {
1331
+ content.push({ type: "text", text: msg.content });
1332
+ }
1333
+ const [topic, toolContent] = (_a = parseToolPayload(msg)) != null ? _a : [];
1334
+ if (topic && toolContent) {
1335
+ content.push({
1336
+ type: "tool-call",
1337
+ toolCallId: `tool_${Date.now()}`,
1338
+ toolName: topic,
1339
+ args: void 0,
1340
+ result: (() => {
1341
+ try {
1342
+ return JSON.parse(toolContent);
1343
+ } catch (e) {
1344
+ return { args: toolContent };
1345
+ }
1346
+ })()
1347
+ });
1348
+ }
1349
+ const threadMessage = __spreadValues({
1350
+ role,
1351
+ content: content.length > 0 ? content : [{ type: "text", text: "" }]
1352
+ }, msg.timestamp && { createdAt: new Date(msg.timestamp) });
1353
+ return threadMessage;
1354
+ }
1355
+ function parseToolPayload(msg) {
1356
+ return parseToolResult(msg.tool_result);
1357
+ }
1358
+ function parseToolResult(toolResult) {
1359
+ if (!toolResult) return null;
1360
+ if (Array.isArray(toolResult) && toolResult.length === 2) {
1361
+ const [topic, content] = toolResult;
1362
+ return [String(topic), String(content != null ? content : "")];
1363
+ }
1364
+ return null;
1365
+ }
1366
+ var getNetworkName = (chainId) => {
1367
+ if (chainId === void 0) return "";
1368
+ const id = typeof chainId === "string" ? Number(chainId) : chainId;
1369
+ switch (id) {
1370
+ case 1:
1371
+ return "ethereum";
1372
+ case 137:
1373
+ return "polygon";
1374
+ case 42161:
1375
+ return "arbitrum";
1376
+ case 8453:
1377
+ return "base";
1378
+ case 10:
1379
+ return "optimism";
1380
+ case 11155111:
1381
+ return "sepolia";
1382
+ case 1337:
1383
+ case 31337:
1384
+ return "testnet";
1385
+ case 59140:
1386
+ return "linea-sepolia";
1387
+ case 59144:
1388
+ return "linea";
1389
+ default:
1390
+ return "testnet";
1391
+ }
1392
+ };
1393
+ var formatAddress = (addr) => addr ? `${addr.slice(0, 6)}...${addr.slice(-4)}` : "Connect Wallet";
1394
+
1395
+ // packages/react/src/state/backend-state.ts
1396
+ function createBackendState() {
1397
+ return {
1398
+ skipInitialFetch: /* @__PURE__ */ new Set(),
1399
+ pendingChat: /* @__PURE__ */ new Map(),
1400
+ runningThreads: /* @__PURE__ */ new Set(),
1401
+ creatingThreadId: null,
1402
+ createThreadPromise: null
1403
+ };
1404
+ }
1405
+ function resolveThreadId(state, threadId) {
1406
+ return threadId;
1407
+ }
1408
+ function isThreadReady(state, threadId) {
1409
+ return state.creatingThreadId !== threadId;
1410
+ }
1411
+ function markSkipInitialFetch(state, threadId) {
1412
+ state.skipInitialFetch.add(threadId);
1413
+ }
1414
+ function shouldSkipInitialFetch(state, threadId) {
1415
+ return state.skipInitialFetch.has(threadId);
1416
+ }
1417
+ function clearSkipInitialFetch(state, threadId) {
1418
+ state.skipInitialFetch.delete(threadId);
1419
+ }
1420
+ function setThreadRunning(state, threadId, running) {
1421
+ if (running) {
1422
+ state.runningThreads.add(threadId);
1423
+ } else {
1424
+ state.runningThreads.delete(threadId);
1425
+ }
1426
+ }
1427
+ function isThreadRunning(state, threadId) {
1428
+ return state.runningThreads.has(threadId);
1429
+ }
1430
+ function enqueuePendingChat(state, threadId, text) {
1431
+ var _a;
1432
+ const existing = (_a = state.pendingChat.get(threadId)) != null ? _a : [];
1433
+ state.pendingChat.set(threadId, [...existing, text]);
1434
+ }
1435
+ function dequeuePendingChat(state, threadId) {
1436
+ var _a;
1437
+ const pending = (_a = state.pendingChat.get(threadId)) != null ? _a : [];
1438
+ state.pendingChat.delete(threadId);
1439
+ return pending;
1440
+ }
1441
+ function hasPendingChat(state, threadId) {
1442
+ var _a, _b;
1443
+ return ((_b = (_a = state.pendingChat.get(threadId)) == null ? void 0 : _a.length) != null ? _b : 0) > 0;
1444
+ }
1445
+
1446
+ // packages/react/src/runtime/message-controller.ts
1447
+ var MessageController = class {
1448
+ constructor(config) {
1449
+ this.config = config;
1450
+ }
1451
+ inbound(threadId, msgs) {
1452
+ const backendState = this.config.backendStateRef.current;
1453
+ if (!msgs) return;
1454
+ if (hasPendingChat(backendState, threadId)) {
1455
+ return;
1456
+ }
1457
+ const threadMessages = [];
1458
+ for (const msg of msgs) {
1459
+ const threadMessage = toInboundMessage(msg);
1460
+ if (threadMessage) {
1461
+ threadMessages.push(threadMessage);
1462
+ }
1463
+ }
1464
+ this.getThreadContextApi().setThreadMessages(threadId, threadMessages);
1465
+ }
1466
+ async outbound(message, threadId) {
1467
+ var _a, _b, _c, _d, _e, _f;
1468
+ const backendState = this.config.backendStateRef.current;
1469
+ const text = message.content.filter(
1470
+ (part) => part.type === "text"
1471
+ ).map(
1472
+ (part) => part.text
1473
+ ).join("\n");
1474
+ if (!text) return;
1475
+ const threadState = this.getThreadContextApi();
1476
+ const existingMessages = threadState.getThreadMessages(threadId);
1477
+ const userMessage = {
1478
+ role: "user",
1479
+ content: [{ type: "text", text }],
1480
+ createdAt: /* @__PURE__ */ new Date()
1481
+ };
1482
+ threadState.setThreadMessages(threadId, [...existingMessages, userMessage]);
1483
+ threadState.updateThreadMetadata(threadId, {
1484
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
1485
+ });
1486
+ if (!isThreadReady(backendState, threadId)) {
1487
+ this.markRunning(threadId, true);
1488
+ enqueuePendingChat(backendState, threadId, text);
1489
+ return;
1490
+ }
1491
+ const backendThreadId = resolveThreadId(backendState, threadId);
1492
+ const namespace = this.config.getNamespace();
1493
+ const publicKey = (_b = (_a = this.config).getPublicKey) == null ? void 0 : _b.call(_a);
1494
+ const apiKey = (_e = (_d = (_c = this.config).getApiKey) == null ? void 0 : _d.call(_c)) != null ? _e : void 0;
1495
+ try {
1496
+ this.markRunning(threadId, true);
1497
+ const response = await this.config.backendApiRef.current.postChatMessage(
1498
+ backendThreadId,
1499
+ text,
1500
+ namespace,
1501
+ publicKey,
1502
+ apiKey
1503
+ );
1504
+ if (response == null ? void 0 : response.messages) {
1505
+ this.inbound(threadId, response.messages);
1506
+ }
1507
+ if (((_f = response == null ? void 0 : response.system_events) == null ? void 0 : _f.length) && this.config.onSyncEvents) {
1508
+ this.config.onSyncEvents(backendThreadId, response.system_events);
1509
+ }
1510
+ if (response == null ? void 0 : response.is_processing) {
1511
+ this.config.polling.start(threadId);
1512
+ } else if (!this.config.polling.isPolling(threadId)) {
1513
+ this.markRunning(threadId, false);
1514
+ }
1515
+ } catch (error) {
1516
+ console.error("Failed to send message:", error);
1517
+ this.markRunning(threadId, false);
1518
+ }
1519
+ }
1520
+ async flushPendingChat(threadId) {
1521
+ var _a, _b, _c, _d, _e;
1522
+ const backendState = this.config.backendStateRef.current;
1523
+ const pending = dequeuePendingChat(backendState, threadId);
1524
+ if (!pending.length) return;
1525
+ const backendThreadId = resolveThreadId(backendState, threadId);
1526
+ const namespace = this.config.getNamespace();
1527
+ const publicKey = (_b = (_a = this.config).getPublicKey) == null ? void 0 : _b.call(_a);
1528
+ const apiKey = (_e = (_d = (_c = this.config).getApiKey) == null ? void 0 : _d.call(_c)) != null ? _e : void 0;
1529
+ for (const text of pending) {
1530
+ try {
1531
+ await this.config.backendApiRef.current.postChatMessage(
1532
+ backendThreadId,
1533
+ text,
1534
+ namespace,
1535
+ publicKey,
1536
+ apiKey
1537
+ );
1538
+ } catch (error) {
1539
+ console.error("Failed to send queued message:", error);
1540
+ }
1541
+ }
1542
+ this.config.polling.start(threadId);
1543
+ }
1544
+ async cancel(threadId) {
1545
+ var _a;
1546
+ const backendState = this.config.backendStateRef.current;
1547
+ if (!isThreadReady(backendState, threadId)) return;
1548
+ this.config.polling.stop(threadId);
1549
+ const backendThreadId = resolveThreadId(backendState, threadId);
1550
+ try {
1551
+ const response = await this.config.backendApiRef.current.postInterrupt(backendThreadId);
1552
+ if (response == null ? void 0 : response.messages) {
1553
+ this.inbound(threadId, response.messages);
1554
+ }
1555
+ if (((_a = response == null ? void 0 : response.system_events) == null ? void 0 : _a.length) && this.config.onSyncEvents) {
1556
+ this.config.onSyncEvents(backendThreadId, response.system_events);
1557
+ }
1558
+ this.markRunning(threadId, false);
1559
+ } catch (error) {
1560
+ console.error("Failed to cancel:", error);
1561
+ }
1562
+ }
1563
+ markRunning(threadId, running) {
1564
+ var _a, _b;
1565
+ setThreadRunning(this.config.backendStateRef.current, threadId, running);
1566
+ if (this.config.threadContextRef.current.currentThreadId === threadId) {
1567
+ (_b = (_a = this.config).setGlobalIsRunning) == null ? void 0 : _b.call(_a, running);
1568
+ }
1569
+ }
1570
+ getThreadContextApi() {
1571
+ const { getThreadMessages, setThreadMessages, updateThreadMetadata } = this.config.threadContextRef.current;
1572
+ return { getThreadMessages, setThreadMessages, updateThreadMetadata };
1573
+ }
1574
+ };
1575
+
1576
+ // packages/react/src/runtime/polling-controller.ts
1577
+ var PollingController = class {
1578
+ constructor(config) {
1579
+ this.config = config;
1580
+ this.intervals = /* @__PURE__ */ new Map();
1581
+ var _a;
1582
+ this.intervalMs = (_a = config.intervalMs) != null ? _a : 500;
1583
+ }
1584
+ start(threadId) {
1585
+ var _a, _b;
1586
+ const backendState = this.config.backendStateRef.current;
1587
+ if (!isThreadReady(backendState, threadId)) return;
1588
+ if (this.intervals.has(threadId)) return;
1589
+ const backendThreadId = resolveThreadId(backendState, threadId);
1590
+ setThreadRunning(backendState, threadId, true);
1591
+ const tick = async () => {
1592
+ var _a2, _b2;
1593
+ if (!this.intervals.has(threadId)) return;
1594
+ try {
1595
+ console.log(
1596
+ "[PollingController] Fetching state for threadId:",
1597
+ threadId
1598
+ );
1599
+ const userState = (_b2 = (_a2 = this.config).getUserState) == null ? void 0 : _b2.call(_a2);
1600
+ const state = await this.config.backendApiRef.current.fetchState(
1601
+ backendThreadId,
1602
+ userState
1603
+ );
1604
+ if (!this.intervals.has(threadId)) return;
1605
+ this.handleState(threadId, state);
1606
+ } catch (error) {
1607
+ console.error("Polling error:", error);
1608
+ this.stop(threadId);
1609
+ }
1610
+ };
1611
+ const intervalId = setInterval(tick, this.intervalMs);
1612
+ this.intervals.set(threadId, intervalId);
1613
+ (_b = (_a = this.config).onStart) == null ? void 0 : _b.call(_a, threadId);
1614
+ }
1615
+ stop(threadId) {
1616
+ var _a, _b;
1617
+ const intervalId = this.intervals.get(threadId);
1618
+ if (intervalId) {
1619
+ clearInterval(intervalId);
1620
+ this.intervals.delete(threadId);
1621
+ }
1622
+ setThreadRunning(this.config.backendStateRef.current, threadId, false);
1623
+ (_b = (_a = this.config).onStop) == null ? void 0 : _b.call(_a, threadId);
1624
+ }
1625
+ isPolling(threadId) {
1626
+ return this.intervals.has(threadId);
1627
+ }
1628
+ stopAll() {
1629
+ for (const threadId of this.intervals.keys()) {
1630
+ this.stop(threadId);
1631
+ }
1632
+ }
1633
+ handleState(threadId, state) {
1634
+ var _a;
1635
+ if (((_a = state.system_events) == null ? void 0 : _a.length) && this.config.onSyncEvents) {
1636
+ const backendState = this.config.backendStateRef.current;
1637
+ const sessionId = resolveThreadId(backendState, threadId);
1638
+ this.config.onSyncEvents(sessionId, state.system_events);
1639
+ }
1640
+ this.config.applyMessages(threadId, state.messages);
1641
+ if (!state.is_processing) {
1642
+ this.stop(threadId);
1643
+ }
1644
+ }
1645
+ };
1646
+
1647
+ // packages/react/src/runtime/orchestrator.ts
1648
+ function useRuntimeOrchestrator(backendApi, options) {
1649
+ const threadContext = useThreadContext();
1650
+ const threadContextRef = (0, import_react6.useRef)(threadContext);
1651
+ threadContextRef.current = threadContext;
1652
+ const backendApiRef = (0, import_react6.useRef)(backendApi);
1653
+ backendApiRef.current = backendApi;
1654
+ const backendStateRef = (0, import_react6.useRef)(createBackendState());
1655
+ const [isRunning, setIsRunning] = (0, import_react6.useState)(false);
1656
+ const messageControllerRef = (0, import_react6.useRef)(null);
1657
+ const pollingRef = (0, import_react6.useRef)(null);
1658
+ const pendingFetches = (0, import_react6.useRef)(/* @__PURE__ */ new Set());
1659
+ if (!pollingRef.current) {
1660
+ pollingRef.current = new PollingController({
1661
+ backendApiRef,
1662
+ backendStateRef,
1663
+ applyMessages: (threadId, msgs) => {
1664
+ var _a;
1665
+ (_a = messageControllerRef.current) == null ? void 0 : _a.inbound(threadId, msgs);
1666
+ },
1667
+ onSyncEvents: options.onSyncEvents,
1668
+ getUserState: options.getUserState,
1669
+ onStart: (threadId) => {
1670
+ if (threadContextRef.current.currentThreadId === threadId) {
1671
+ setIsRunning(true);
1672
+ }
1673
+ },
1674
+ onStop: (threadId) => {
1675
+ if (threadContextRef.current.currentThreadId === threadId) {
1676
+ setIsRunning(false);
1677
+ }
1678
+ }
1679
+ });
1680
+ }
1681
+ if (!messageControllerRef.current) {
1682
+ messageControllerRef.current = new MessageController({
1683
+ backendApiRef,
1684
+ backendStateRef,
1685
+ threadContextRef,
1686
+ polling: pollingRef.current,
1687
+ setGlobalIsRunning: setIsRunning,
1688
+ getPublicKey: options.getPublicKey,
1689
+ getNamespace: options.getNamespace,
1690
+ getApiKey: options.getApiKey,
1691
+ onSyncEvents: options.onSyncEvents
1692
+ });
1693
+ }
1694
+ const ensureInitialState = (0, import_react6.useCallback)(async (threadId) => {
1695
+ var _a, _b, _c, _d;
1696
+ const backendState = backendStateRef.current;
1697
+ if (shouldSkipInitialFetch(backendState, threadId)) {
1698
+ clearSkipInitialFetch(backendState, threadId);
1699
+ if (threadContextRef.current.currentThreadId === threadId) {
1700
+ setIsRunning(false);
1701
+ }
1702
+ return;
1703
+ }
1704
+ if (!isThreadReady(backendState, threadId)) {
1705
+ if (threadContextRef.current.currentThreadId === threadId) {
1706
+ setIsRunning(false);
1707
+ }
1708
+ return;
1709
+ }
1710
+ if (pendingFetches.current.has(threadId)) return;
1711
+ const backendThreadId = resolveThreadId(backendState, threadId);
1712
+ pendingFetches.current.add(threadId);
1713
+ try {
1714
+ const userState = (_a = options.getUserState) == null ? void 0 : _a.call(options);
1715
+ const state = await backendApiRef.current.fetchState(
1716
+ backendThreadId,
1717
+ userState
1718
+ );
1719
+ (_b = messageControllerRef.current) == null ? void 0 : _b.inbound(threadId, state.messages);
1720
+ if (((_c = state.system_events) == null ? void 0 : _c.length) && options.onSyncEvents) {
1721
+ options.onSyncEvents(backendThreadId, state.system_events);
1722
+ }
1723
+ if (threadContextRef.current.currentThreadId === threadId) {
1724
+ if (state.is_processing) {
1725
+ setIsRunning(true);
1726
+ (_d = pollingRef.current) == null ? void 0 : _d.start(threadId);
1727
+ } else {
1728
+ setIsRunning(false);
1729
+ }
1730
+ }
1731
+ } catch (error) {
1732
+ console.error("Failed to fetch initial state:", error);
1733
+ if (threadContextRef.current.currentThreadId === threadId) {
1734
+ setIsRunning(false);
1735
+ }
1736
+ } finally {
1737
+ pendingFetches.current.delete(threadId);
1738
+ }
1739
+ }, []);
1740
+ return {
1741
+ backendStateRef,
1742
+ polling: pollingRef.current,
1743
+ messageController: messageControllerRef.current,
1744
+ isRunning,
1745
+ setIsRunning,
1746
+ ensureInitialState,
1747
+ backendApiRef
1748
+ };
1749
+ }
1750
+
1751
+ // packages/react/src/runtime/threadlist-adapter.ts
1752
+ var sortByLastActiveDesc = ([, metaA], [, metaB]) => {
1753
+ const tsA = parseTimestamp(metaA.lastActiveAt);
1754
+ const tsB = parseTimestamp(metaB.lastActiveAt);
1755
+ return tsB - tsA;
1756
+ };
1757
+ function buildThreadLists(threadMetadata) {
1758
+ const entries = Array.from(threadMetadata.entries()).filter(
1759
+ ([, meta]) => !isPlaceholderTitle(meta.title)
1760
+ );
1761
+ const regularThreads = entries.filter(([, meta]) => meta.status !== "archived").sort(sortByLastActiveDesc).map(
1762
+ ([id, meta]) => ({
1763
+ id,
1764
+ title: meta.title || "New Chat",
1765
+ status: "regular"
1766
+ })
1767
+ );
1768
+ const archivedThreads = entries.filter(([, meta]) => meta.status === "archived").sort(sortByLastActiveDesc).map(
1769
+ ([id, meta]) => ({
1770
+ id,
1771
+ title: meta.title || "New Chat",
1772
+ status: "archived"
1773
+ })
1774
+ );
1775
+ return { regularThreads, archivedThreads };
1776
+ }
1777
+ function buildThreadListAdapter({
1778
+ backendStateRef,
1779
+ backendApiRef,
1780
+ threadContext,
1781
+ currentThreadIdRef,
1782
+ polling,
1783
+ userAddress,
1784
+ setIsRunning,
1785
+ getNamespace,
1786
+ getApiKey
1787
+ }) {
1788
+ const backendState = backendStateRef.current;
1789
+ const { regularThreads, archivedThreads } = buildThreadLists(
1790
+ threadContext.allThreadsMetadata
1791
+ );
1792
+ const preparePendingThread = (threadId) => {
1793
+ const previousPendingId = backendState.creatingThreadId;
1794
+ if (previousPendingId && previousPendingId !== threadId) {
1795
+ threadContext.setThreadMetadata((prev) => {
1796
+ const next = new Map(prev);
1797
+ next.delete(previousPendingId);
1798
+ return next;
1799
+ });
1800
+ threadContext.setThreads((prev) => {
1801
+ const next = new Map(prev);
1802
+ next.delete(previousPendingId);
1803
+ return next;
1804
+ });
1805
+ backendState.pendingChat.delete(previousPendingId);
1806
+ backendState.skipInitialFetch.delete(previousPendingId);
1807
+ }
1808
+ backendState.creatingThreadId = threadId;
1809
+ backendState.pendingChat.delete(threadId);
1810
+ threadContext.setThreadMetadata(
1811
+ (prev) => new Map(prev).set(threadId, {
1812
+ title: "New Chat",
1813
+ status: "pending",
1814
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
1815
+ control: initThreadControl()
1816
+ })
1817
+ );
1818
+ threadContext.setThreadMessages(threadId, []);
1819
+ threadContext.setCurrentThreadId(threadId);
1820
+ setIsRunning(false);
1821
+ threadContext.bumpThreadViewKey();
1822
+ };
1823
+ const findPendingThreadId = () => {
1824
+ if (backendState.creatingThreadId) return backendState.creatingThreadId;
1825
+ for (const [id, meta] of threadContext.allThreadsMetadata.entries()) {
1826
+ if (meta.status === "pending") return id;
1827
+ }
1828
+ return null;
1829
+ };
1830
+ return {
1831
+ threadId: threadContext.currentThreadId,
1832
+ threads: regularThreads,
1833
+ archivedThreads,
1834
+ onSwitchToNewThread: async () => {
1835
+ var _a;
1836
+ const pendingId = findPendingThreadId();
1837
+ if (pendingId) {
1838
+ preparePendingThread(pendingId);
1839
+ return;
1840
+ }
1841
+ if (backendState.createThreadPromise) {
1842
+ preparePendingThread((_a = backendState.creatingThreadId) != null ? _a : generateUUID());
1843
+ return;
1844
+ }
1845
+ const threadId = generateUUID();
1846
+ preparePendingThread(threadId);
1847
+ const createPromise = backendApiRef.current.createThread(threadId, userAddress).then(async (newThread) => {
1848
+ var _a2, _b;
1849
+ const uiThreadId = (_a2 = backendState.creatingThreadId) != null ? _a2 : threadId;
1850
+ const backendId = newThread.session_id;
1851
+ if (uiThreadId !== backendId) {
1852
+ console.warn("[aomi][thread] backend id mismatch", {
1853
+ uiThreadId,
1854
+ backendId
1855
+ });
1856
+ }
1857
+ markSkipInitialFetch(backendState, uiThreadId);
1858
+ threadContext.setThreadMetadata((prev) => {
1859
+ var _a3, _b2, _c;
1860
+ const next = new Map(prev);
1861
+ const existing = next.get(uiThreadId);
1862
+ const nextStatus = (existing == null ? void 0 : existing.status) === "archived" ? "archived" : "regular";
1863
+ next.set(uiThreadId, {
1864
+ title: (_a3 = existing == null ? void 0 : existing.title) != null ? _a3 : "New Chat",
1865
+ status: nextStatus,
1866
+ lastActiveAt: (_b2 = existing == null ? void 0 : existing.lastActiveAt) != null ? _b2 : (/* @__PURE__ */ new Date()).toISOString(),
1867
+ control: (_c = existing == null ? void 0 : existing.control) != null ? _c : initThreadControl()
1868
+ });
1869
+ return next;
1870
+ });
1871
+ if (backendState.creatingThreadId === uiThreadId) {
1872
+ backendState.creatingThreadId = null;
1873
+ }
1874
+ const pendingMessages = backendState.pendingChat.get(uiThreadId);
1875
+ if (pendingMessages == null ? void 0 : pendingMessages.length) {
1876
+ backendState.pendingChat.delete(uiThreadId);
1877
+ const namespace = getNamespace();
1878
+ const apiKey = (_b = getApiKey == null ? void 0 : getApiKey()) != null ? _b : void 0;
1879
+ for (const text of pendingMessages) {
1880
+ try {
1881
+ await backendApiRef.current.postChatMessage(
1882
+ backendId,
1883
+ text,
1884
+ namespace,
1885
+ userAddress,
1886
+ apiKey
1887
+ );
1888
+ } catch (error) {
1889
+ console.error("Failed to send queued message:", error);
1890
+ }
1891
+ }
1892
+ if (currentThreadIdRef.current === uiThreadId) {
1893
+ polling == null ? void 0 : polling.start(uiThreadId);
1894
+ }
1895
+ }
1896
+ }).catch((error) => {
1897
+ var _a2;
1898
+ console.error("Failed to create new thread:", error);
1899
+ const failedId = (_a2 = backendState.creatingThreadId) != null ? _a2 : threadId;
1900
+ threadContext.setThreadMetadata((prev) => {
1901
+ const next = new Map(prev);
1902
+ next.delete(failedId);
1903
+ return next;
1904
+ });
1905
+ threadContext.setThreads((prev) => {
1906
+ const next = new Map(prev);
1907
+ next.delete(failedId);
1908
+ return next;
1909
+ });
1910
+ if (backendState.creatingThreadId === failedId) {
1911
+ backendState.creatingThreadId = null;
1912
+ }
1913
+ }).finally(() => {
1914
+ backendState.createThreadPromise = null;
1915
+ });
1916
+ backendState.createThreadPromise = createPromise;
1917
+ },
1918
+ onSwitchToThread: (threadId) => {
1919
+ threadContext.setCurrentThreadId(threadId);
1920
+ },
1921
+ onRename: async (threadId, newTitle) => {
1922
+ var _a, _b;
1923
+ const previousTitle = (_b = (_a = threadContext.getThreadMetadata(threadId)) == null ? void 0 : _a.title) != null ? _b : "";
1924
+ const normalizedTitle = isPlaceholderTitle(newTitle) ? "" : newTitle;
1925
+ threadContext.updateThreadMetadata(threadId, {
1926
+ title: normalizedTitle
1927
+ });
1928
+ try {
1929
+ await backendApiRef.current.renameThread(threadId, newTitle);
1930
+ } catch (error) {
1931
+ console.error("Failed to rename thread:", error);
1932
+ threadContext.updateThreadMetadata(threadId, {
1933
+ title: previousTitle
1934
+ });
1935
+ }
1936
+ },
1937
+ onArchive: async (threadId) => {
1938
+ threadContext.updateThreadMetadata(threadId, { status: "archived" });
1939
+ try {
1940
+ await backendApiRef.current.archiveThread(threadId);
1941
+ } catch (error) {
1942
+ console.error("Failed to archive thread:", error);
1943
+ threadContext.updateThreadMetadata(threadId, { status: "regular" });
1944
+ }
1945
+ },
1946
+ onUnarchive: async (threadId) => {
1947
+ threadContext.updateThreadMetadata(threadId, { status: "regular" });
1948
+ try {
1949
+ await backendApiRef.current.unarchiveThread(threadId);
1950
+ } catch (error) {
1951
+ console.error("Failed to unarchive thread:", error);
1952
+ threadContext.updateThreadMetadata(threadId, { status: "archived" });
1953
+ }
1954
+ },
1955
+ onDelete: async (threadId) => {
1956
+ try {
1957
+ await backendApiRef.current.deleteThread(threadId);
1958
+ threadContext.setThreadMetadata((prev) => {
1959
+ const next = new Map(prev);
1960
+ next.delete(threadId);
1961
+ return next;
1962
+ });
1963
+ threadContext.setThreads((prev) => {
1964
+ const next = new Map(prev);
1965
+ next.delete(threadId);
1966
+ return next;
1967
+ });
1968
+ backendState.pendingChat.delete(threadId);
1969
+ backendState.skipInitialFetch.delete(threadId);
1970
+ backendState.runningThreads.delete(threadId);
1971
+ if (backendState.creatingThreadId === threadId) {
1972
+ backendState.creatingThreadId = null;
1973
+ }
1974
+ if (threadContext.currentThreadId === threadId) {
1975
+ const firstRegularThread = Array.from(
1976
+ threadContext.allThreadsMetadata.entries()
1977
+ ).find(([id, meta]) => meta.status === "regular" && id !== threadId);
1978
+ if (firstRegularThread) {
1979
+ threadContext.setCurrentThreadId(firstRegularThread[0]);
1980
+ } else {
1981
+ const defaultId = "default-session";
1982
+ threadContext.setThreadMetadata(
1983
+ (prev) => new Map(prev).set(defaultId, {
1984
+ title: "New Chat",
1985
+ status: "regular",
1986
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
1987
+ control: initThreadControl()
1988
+ })
1989
+ );
1990
+ threadContext.setThreadMessages(defaultId, []);
1991
+ threadContext.setCurrentThreadId(defaultId);
1992
+ }
1993
+ }
1994
+ } catch (error) {
1995
+ console.error("Failed to delete thread:", error);
1996
+ throw error;
1997
+ }
1998
+ }
1999
+ };
2000
+ }
2001
+
2002
+ // packages/react/src/interface.tsx
2003
+ var import_react7 = require("react");
2004
+ var AomiRuntimeContext = (0, import_react7.createContext)(null);
2005
+ var AomiRuntimeApiProvider = AomiRuntimeContext.Provider;
2006
+ function useAomiRuntime() {
2007
+ const context = (0, import_react7.useContext)(AomiRuntimeContext);
2008
+ if (!context) {
2009
+ throw new Error(
2010
+ "useAomiRuntime must be used within AomiRuntimeProvider. Wrap your app with <AomiRuntimeProvider>...</AomiRuntimeProvider>"
2011
+ );
2012
+ }
2013
+ return context;
2014
+ }
2015
+
2016
+ // packages/react/src/runtime/core.tsx
2017
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2018
+ function AomiRuntimeCore({
2019
+ children,
2020
+ backendApi
2021
+ }) {
2022
+ const threadContext = useThreadContext();
2023
+ const eventContext = useEventContext();
2024
+ const notificationContext = useNotification();
2025
+ const { dispatchInboundSystem: dispatchSystemEvents } = eventContext;
2026
+ const { user, onUserStateChange, getUserState } = useUser();
2027
+ const { getControlState, getCurrentThreadControl } = useControl();
2028
+ const {
2029
+ backendStateRef,
2030
+ polling,
2031
+ messageController,
2032
+ isRunning,
2033
+ setIsRunning,
2034
+ ensureInitialState,
2035
+ backendApiRef
2036
+ } = useRuntimeOrchestrator(backendApi, {
2037
+ onSyncEvents: dispatchSystemEvents,
2038
+ getPublicKey: () => getUserState().address,
2039
+ getUserState,
2040
+ getNamespace: () => {
2041
+ var _a, _b;
2042
+ return (_b = (_a = getCurrentThreadControl().namespace) != null ? _a : getControlState().defaultNamespace) != null ? _b : "default";
2043
+ },
2044
+ getApiKey: () => getControlState().apiKey
2045
+ });
2046
+ (0, import_react8.useEffect)(() => {
2047
+ const unsubscribe = onUserStateChange(async (newUser) => {
2048
+ const sessionId = threadContext.currentThreadId;
2049
+ const message = JSON.stringify({
2050
+ type: "wallet:state_changed",
2051
+ payload: {
2052
+ address: newUser.address,
2053
+ chainId: newUser.chainId,
2054
+ isConnected: newUser.isConnected,
2055
+ ensName: newUser.ensName
2056
+ }
2057
+ });
2058
+ await backendApiRef.current.postSystemMessage(sessionId, message);
2059
+ });
2060
+ return unsubscribe;
2061
+ }, [onUserStateChange, backendApiRef, threadContext.currentThreadId]);
2062
+ const threadContextRef = (0, import_react8.useRef)(threadContext);
2063
+ threadContextRef.current = threadContext;
2064
+ const currentThreadIdRef = (0, import_react8.useRef)(threadContext.currentThreadId);
2065
+ (0, import_react8.useEffect)(() => {
2066
+ currentThreadIdRef.current = threadContext.currentThreadId;
2067
+ }, [threadContext.currentThreadId]);
2068
+ (0, import_react8.useEffect)(() => {
2069
+ void ensureInitialState(threadContext.currentThreadId);
2070
+ }, [ensureInitialState, threadContext.currentThreadId]);
2071
+ (0, import_react8.useEffect)(() => {
2072
+ const threadId = threadContext.currentThreadId;
2073
+ setIsRunning(isThreadRunning(backendStateRef.current, threadId));
2074
+ }, [backendStateRef, setIsRunning, threadContext.currentThreadId]);
2075
+ (0, import_react8.useEffect)(() => {
2076
+ const threadId = threadContext.currentThreadId;
2077
+ const currentMeta = threadContext.getThreadMetadata(threadId);
2078
+ if (currentMeta && currentMeta.control.isProcessing !== isRunning) {
2079
+ threadContext.updateThreadMetadata(threadId, {
2080
+ control: __spreadProps(__spreadValues({}, currentMeta.control), {
2081
+ isProcessing: isRunning
2082
+ })
2083
+ });
2084
+ }
2085
+ }, [isRunning, threadContext]);
2086
+ const currentMessages = threadContext.getThreadMessages(
2087
+ threadContext.currentThreadId
2088
+ );
2089
+ const resolvedSessionId = (0, import_react8.useMemo)(
2090
+ () => resolveThreadId(backendStateRef.current, threadContext.currentThreadId),
2091
+ [
2092
+ backendStateRef,
2093
+ threadContext.currentThreadId,
2094
+ threadContext.allThreadsMetadata
2095
+ ]
2096
+ );
2097
+ (0, import_react8.useEffect)(() => {
2098
+ const userAddress = user.address;
2099
+ if (!userAddress) return;
2100
+ const fetchThreadList = async () => {
2101
+ var _a, _b, _c;
2102
+ try {
2103
+ const threadList = await backendApiRef.current.fetchThreads(userAddress);
2104
+ const currentContext = threadContextRef.current;
2105
+ const newMetadata = new Map(currentContext.allThreadsMetadata);
2106
+ let maxChatNum = currentContext.threadCnt;
2107
+ for (const thread of threadList) {
2108
+ const rawTitle = (_a = thread.title) != null ? _a : "";
2109
+ const title = isPlaceholderTitle(rawTitle) ? "" : rawTitle;
2110
+ const lastActive = ((_b = newMetadata.get(thread.session_id)) == null ? void 0 : _b.lastActiveAt) || (/* @__PURE__ */ new Date()).toISOString();
2111
+ const existingControl = (_c = newMetadata.get(thread.session_id)) == null ? void 0 : _c.control;
2112
+ newMetadata.set(thread.session_id, {
2113
+ title,
2114
+ status: thread.is_archived ? "archived" : "regular",
2115
+ lastActiveAt: lastActive,
2116
+ control: existingControl != null ? existingControl : initThreadControl()
2117
+ });
2118
+ const match = title.match(/^Chat (\d+)$/);
2119
+ if (match) {
2120
+ const num = parseInt(match[1], 10);
2121
+ if (num > maxChatNum) {
2122
+ maxChatNum = num;
2123
+ }
2124
+ }
2125
+ }
2126
+ currentContext.setThreadMetadata(newMetadata);
2127
+ if (maxChatNum > currentContext.threadCnt) {
2128
+ currentContext.setThreadCnt(maxChatNum);
2129
+ }
2130
+ } catch (error) {
2131
+ console.error("Failed to fetch thread list:", error);
2132
+ }
2133
+ };
2134
+ void fetchThreadList();
2135
+ }, [user.address, backendApiRef]);
2136
+ const threadListAdapter = (0, import_react8.useMemo)(
2137
+ () => buildThreadListAdapter({
2138
+ backendStateRef,
2139
+ backendApiRef,
2140
+ threadContext,
2141
+ currentThreadIdRef,
2142
+ polling,
2143
+ userAddress: user.address,
2144
+ setIsRunning,
2145
+ getNamespace: () => {
2146
+ var _a, _b;
2147
+ return (_b = (_a = getCurrentThreadControl().namespace) != null ? _a : getControlState().defaultNamespace) != null ? _b : "default";
2148
+ },
2149
+ getApiKey: () => getControlState().apiKey
2150
+ }),
2151
+ [
2152
+ backendApiRef,
2153
+ polling,
2154
+ user.address,
2155
+ backendStateRef,
2156
+ setIsRunning,
2157
+ threadContext,
2158
+ threadContext.currentThreadId,
2159
+ threadContext.allThreadsMetadata,
2160
+ getControlState
2161
+ ]
2162
+ );
2163
+ (0, import_react8.useEffect)(() => {
2164
+ const backendState = backendStateRef.current;
2165
+ const currentSessionId = threadContext.currentThreadId;
2166
+ if (process.env.NODE_ENV !== "production") {
2167
+ console.debug("[aomi][sse] subscribe", {
2168
+ currentSessionId,
2169
+ resolvedSessionId,
2170
+ hasMapping: currentSessionId !== resolvedSessionId
2171
+ });
2172
+ }
2173
+ const unsubscribe = backendApiRef.current.subscribeSSE(
2174
+ resolvedSessionId,
2175
+ (event) => {
2176
+ const eventType = event.type;
2177
+ const sessionId = event.session_id;
2178
+ if (eventType === "title_changed") {
2179
+ const newTitle = event.new_title;
2180
+ const targetThreadId = resolveThreadId(backendState, sessionId);
2181
+ const normalizedTitle = isPlaceholderTitle(newTitle) ? "" : newTitle;
2182
+ if (process.env.NODE_ENV !== "production") {
2183
+ console.debug("[aomi][sse] title_changed", {
2184
+ sessionId,
2185
+ newTitle,
2186
+ normalizedTitle,
2187
+ currentThreadId: threadContextRef.current.currentThreadId,
2188
+ targetThreadId,
2189
+ hasMapping: sessionId !== targetThreadId,
2190
+ creatingThreadId: backendState.creatingThreadId
2191
+ });
2192
+ }
2193
+ threadContextRef.current.setThreadMetadata((prev) => {
2194
+ var _a, _b;
2195
+ const next = new Map(prev);
2196
+ const existing = next.get(targetThreadId);
2197
+ const nextStatus = (existing == null ? void 0 : existing.status) === "archived" ? "archived" : "regular";
2198
+ next.set(targetThreadId, {
2199
+ title: normalizedTitle,
2200
+ status: nextStatus,
2201
+ lastActiveAt: (_a = existing == null ? void 0 : existing.lastActiveAt) != null ? _a : (/* @__PURE__ */ new Date()).toISOString(),
2202
+ control: (_b = existing == null ? void 0 : existing.control) != null ? _b : initThreadControl()
2203
+ });
2204
+ return next;
2205
+ });
2206
+ if (!isPlaceholderTitle(newTitle) && backendState.creatingThreadId === targetThreadId) {
2207
+ backendState.creatingThreadId = null;
2208
+ }
2209
+ }
2210
+ }
2211
+ );
2212
+ return () => {
2213
+ unsubscribe == null ? void 0 : unsubscribe();
2214
+ };
2215
+ }, [
2216
+ backendApiRef,
2217
+ backendStateRef,
2218
+ threadContext.currentThreadId,
2219
+ resolvedSessionId
2220
+ ]);
2221
+ (0, import_react8.useEffect)(() => {
2222
+ const threadId = threadContext.currentThreadId;
2223
+ if (!isThreadReady(backendStateRef.current, threadId)) return;
2224
+ void messageController.flushPendingChat(threadId);
2225
+ }, [messageController, backendStateRef, threadContext.currentThreadId]);
2226
+ (0, import_react8.useEffect)(() => {
2227
+ const showToolNotification = (eventType) => (event) => {
2228
+ const payload = event.payload;
2229
+ const toolName = typeof (payload == null ? void 0 : payload.tool_name) === "string" ? payload.tool_name : void 0;
2230
+ const title = toolName ? `${eventType === "tool_update" ? "Tool update" : "Tool complete"}: ${toolName}` : eventType === "tool_update" ? "Tool update" : "Tool complete";
2231
+ const message = typeof (payload == null ? void 0 : payload.message) === "string" ? payload.message : typeof (payload == null ? void 0 : payload.result) === "string" ? payload.result : void 0;
2232
+ notificationContext.showNotification({
2233
+ type: "notice",
2234
+ title,
2235
+ message
2236
+ });
2237
+ };
2238
+ const unsubscribeUpdate = eventContext.subscribe(
2239
+ "tool_update",
2240
+ showToolNotification("tool_update")
2241
+ );
2242
+ const unsubscribeComplete = eventContext.subscribe(
2243
+ "tool_complete",
2244
+ showToolNotification("tool_complete")
2245
+ );
2246
+ return () => {
2247
+ unsubscribeUpdate();
2248
+ unsubscribeComplete();
2249
+ };
2250
+ }, [eventContext, notificationContext]);
2251
+ (0, import_react8.useEffect)(() => {
2252
+ const unsubscribe = eventContext.subscribe("system_notice", (event) => {
2253
+ const payload = event.payload;
2254
+ const message = payload == null ? void 0 : payload.message;
2255
+ });
2256
+ return unsubscribe;
2257
+ }, [eventContext, notificationContext]);
2258
+ const runtime = (0, import_react9.useExternalStoreRuntime)({
2259
+ messages: currentMessages,
2260
+ setMessages: (msgs) => threadContext.setThreadMessages(threadContext.currentThreadId, [...msgs]),
2261
+ isRunning,
2262
+ onNew: (message) => messageController.outbound(message, threadContext.currentThreadId),
2263
+ onCancel: () => messageController.cancel(threadContext.currentThreadId),
2264
+ convertMessage: (msg) => msg,
2265
+ adapters: { threadList: threadListAdapter }
2266
+ });
2267
+ (0, import_react8.useEffect)(() => {
2268
+ return () => {
2269
+ polling.stopAll();
2270
+ };
2271
+ }, [polling]);
2272
+ const userContext = useUser();
2273
+ const sendMessage = (0, import_react8.useCallback)(
2274
+ async (text) => {
2275
+ const appendMessage = {
2276
+ role: "user",
2277
+ content: [{ type: "text", text }]
2278
+ };
2279
+ await messageController.outbound(
2280
+ appendMessage,
2281
+ threadContext.currentThreadId
2282
+ );
2283
+ },
2284
+ [messageController, threadContext.currentThreadId]
2285
+ );
2286
+ const cancelGeneration = (0, import_react8.useCallback)(() => {
2287
+ messageController.cancel(threadContext.currentThreadId);
2288
+ }, [messageController, threadContext.currentThreadId]);
2289
+ const getMessages = (0, import_react8.useCallback)(
2290
+ (threadId) => {
2291
+ const id = threadId != null ? threadId : threadContext.currentThreadId;
2292
+ return threadContext.getThreadMessages(id);
2293
+ },
2294
+ [threadContext]
2295
+ );
2296
+ const createThread = (0, import_react8.useCallback)(async () => {
2297
+ await threadListAdapter.onSwitchToNewThread();
2298
+ return threadContextRef.current.currentThreadId;
2299
+ }, [threadListAdapter]);
2300
+ const deleteThread = (0, import_react8.useCallback)(
2301
+ async (threadId) => {
2302
+ await threadListAdapter.onDelete(threadId);
2303
+ },
2304
+ [threadListAdapter]
2305
+ );
2306
+ const renameThread = (0, import_react8.useCallback)(
2307
+ async (threadId, title) => {
2308
+ await threadListAdapter.onRename(threadId, title);
2309
+ },
2310
+ [threadListAdapter]
2311
+ );
2312
+ const archiveThread = (0, import_react8.useCallback)(
2313
+ async (threadId) => {
2314
+ await threadListAdapter.onArchive(threadId);
2315
+ },
2316
+ [threadListAdapter]
2317
+ );
2318
+ const selectThread = (0, import_react8.useCallback)(
2319
+ (threadId) => {
2320
+ if (threadContext.allThreadsMetadata.has(threadId)) {
2321
+ threadListAdapter.onSwitchToThread(threadId);
2322
+ } else {
2323
+ void threadListAdapter.onSwitchToNewThread();
2324
+ }
2325
+ },
2326
+ [threadContext.allThreadsMetadata, threadListAdapter]
2327
+ );
2328
+ const aomiRuntimeApi = (0, import_react8.useMemo)(
2329
+ () => ({
2330
+ // User API
2331
+ user: userContext.user,
2332
+ getUserState: userContext.getUserState,
2333
+ setUser: userContext.setUser,
2334
+ onUserStateChange: userContext.onUserStateChange,
2335
+ // Thread API
2336
+ currentThreadId: threadContext.currentThreadId,
2337
+ threadViewKey: threadContext.threadViewKey,
2338
+ threadMetadata: threadContext.allThreadsMetadata,
2339
+ getThreadMetadata: threadContext.getThreadMetadata,
2340
+ createThread,
2341
+ deleteThread,
2342
+ renameThread,
2343
+ archiveThread,
2344
+ selectThread,
2345
+ // Chat API
2346
+ isRunning,
2347
+ getMessages,
2348
+ sendMessage,
2349
+ cancelGeneration,
2350
+ // Notification API
2351
+ notifications: notificationContext.notifications,
2352
+ showNotification: notificationContext.showNotification,
2353
+ dismissNotification: notificationContext.dismissNotification,
2354
+ clearAllNotifications: notificationContext.clearAll,
2355
+ // Event API
2356
+ subscribe: eventContext.subscribe,
2357
+ sendSystemCommand: eventContext.sendOutboundSystem,
2358
+ sseStatus: eventContext.sseStatus
2359
+ }),
2360
+ [
2361
+ userContext,
2362
+ threadContext.currentThreadId,
2363
+ threadContext.threadViewKey,
2364
+ threadContext.allThreadsMetadata,
2365
+ threadContext.getThreadMetadata,
2366
+ createThread,
2367
+ deleteThread,
2368
+ renameThread,
2369
+ archiveThread,
2370
+ selectThread,
2371
+ isRunning,
2372
+ getMessages,
2373
+ sendMessage,
2374
+ cancelGeneration,
2375
+ notificationContext,
2376
+ eventContext
2377
+ ]
2378
+ );
2379
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AomiRuntimeApiProvider, { value: aomiRuntimeApi, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react9.AssistantRuntimeProvider, { runtime, children }) });
2380
+ }
2381
+
2382
+ // packages/react/src/runtime/aomi-runtime.tsx
2383
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2384
+ function AomiRuntimeProvider({
2385
+ children,
2386
+ backendUrl = "http://localhost:8080"
2387
+ }) {
2388
+ const backendApi = (0, import_react10.useMemo)(() => new BackendApi(backendUrl), [backendUrl]);
2389
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ThreadContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NotificationContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(UserContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AomiRuntimeInner, { backendApi, children }) }) }) });
2390
+ }
2391
+ function AomiRuntimeInner({
2392
+ children,
2393
+ backendApi
2394
+ }) {
2395
+ var _a;
2396
+ const threadContext = useThreadContext();
2397
+ const { user } = useUser();
2398
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2399
+ ControlContextProvider,
2400
+ {
2401
+ backendApi,
2402
+ sessionId: threadContext.currentThreadId,
2403
+ publicKey: (_a = user.address) != null ? _a : void 0,
2404
+ getThreadMetadata: threadContext.getThreadMetadata,
2405
+ updateThreadMetadata: threadContext.updateThreadMetadata,
2406
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2407
+ EventContextProvider,
2408
+ {
2409
+ backendApi,
2410
+ sessionId: threadContext.currentThreadId,
2411
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AomiRuntimeCore, { backendApi, children })
2412
+ }
2413
+ )
2414
+ }
2415
+ );
2416
+ }
2417
+
2418
+ // packages/react/src/handlers/wallet-handler.ts
2419
+ var import_react11 = require("react");
2420
+ function useWalletHandler({
2421
+ sessionId,
2422
+ onTxRequest
2423
+ }) {
2424
+ const { subscribe: subscribe2, sendOutboundSystem: sendOutbound } = useEventContext();
2425
+ const { setUser, getUserState } = useUser();
2426
+ const [pendingTxRequests, setPendingTxRequests] = (0, import_react11.useState)(
2427
+ []
2428
+ );
2429
+ (0, import_react11.useEffect)(() => {
2430
+ const unsubscribe = subscribe2(
2431
+ "wallet_tx_request",
2432
+ (event) => {
2433
+ const request = event.payload;
2434
+ setPendingTxRequests((prev) => [...prev, request]);
2435
+ onTxRequest == null ? void 0 : onTxRequest(request);
2436
+ }
2437
+ );
2438
+ return unsubscribe;
2439
+ }, [subscribe2, onTxRequest]);
2440
+ (0, import_react11.useEffect)(() => {
2441
+ const unsubscribe = subscribe2(
2442
+ "user_state_request",
2443
+ (event) => {
2444
+ sendOutbound({
2445
+ type: "user_state_response",
2446
+ sessionId,
2447
+ payload: getUserState()
2448
+ });
2449
+ }
2450
+ );
2451
+ return unsubscribe;
2452
+ }, [subscribe2, onTxRequest]);
2453
+ const sendTxComplete = (0, import_react11.useCallback)(
2454
+ (tx) => {
2455
+ sendOutbound({
2456
+ type: "wallet:tx_complete",
2457
+ sessionId,
2458
+ payload: tx
2459
+ });
2460
+ },
2461
+ [sendOutbound, sessionId]
2462
+ );
2463
+ const sendConnectionChange = (0, import_react11.useCallback)(
2464
+ (status, address, chainId) => {
2465
+ if (status === "connected") {
2466
+ setUser({
2467
+ isConnected: true,
2468
+ address,
2469
+ chainId
2470
+ });
2471
+ } else {
2472
+ setUser({
2473
+ isConnected: false,
2474
+ address: void 0,
2475
+ chainId: void 0
2476
+ });
2477
+ }
2478
+ sendOutbound({
2479
+ type: status === "connected" ? "wallet:connected" : "wallet:disconnected",
2480
+ sessionId,
2481
+ payload: { status, address }
2482
+ });
2483
+ },
2484
+ [setUser, sendOutbound, sessionId]
2485
+ );
2486
+ const clearTxRequest = (0, import_react11.useCallback)((index) => {
2487
+ setPendingTxRequests((prev) => prev.filter((_, i) => i !== index));
2488
+ }, []);
2489
+ return {
2490
+ sendTxComplete,
2491
+ sendConnectionChange,
2492
+ pendingTxRequests,
2493
+ clearTxRequest
2494
+ };
2495
+ }
2496
+
2497
+ // packages/react/src/handlers/notification-handler.ts
2498
+ var import_react12 = require("react");
2499
+ var notificationIdCounter2 = 0;
2500
+ function generateNotificationId() {
2501
+ return `notif-${Date.now()}-${++notificationIdCounter2}`;
2502
+ }
2503
+ function useNotificationHandler({
2504
+ onNotification
2505
+ } = {}) {
2506
+ const { subscribe: subscribe2 } = useEventContext();
2507
+ const [notifications, setNotifications] = (0, import_react12.useState)([]);
2508
+ (0, import_react12.useEffect)(() => {
2509
+ const unsubscribe = subscribe2("notification", (event) => {
2510
+ var _a, _b;
2511
+ const payload = event.payload;
2512
+ const notification = {
2513
+ id: generateNotificationId(),
2514
+ type: (_a = payload.type) != null ? _a : "notification",
2515
+ title: (_b = payload.title) != null ? _b : "Notification",
2516
+ body: payload.body,
2517
+ handled: false,
2518
+ timestamp: event.timestamp,
2519
+ sessionId: event.sessionId
2520
+ };
2521
+ setNotifications((prev) => [notification, ...prev]);
2522
+ onNotification == null ? void 0 : onNotification(notification);
2523
+ });
2524
+ return unsubscribe;
2525
+ }, [subscribe2, onNotification]);
2526
+ const unhandledCount = notifications.filter((n) => !n.handled).length;
2527
+ const markHandled = (0, import_react12.useCallback)((id) => {
2528
+ setNotifications(
2529
+ (prev) => prev.map((n) => n.id === id ? __spreadProps(__spreadValues({}, n), { handled: true }) : n)
2530
+ );
2531
+ }, []);
2532
+ return {
2533
+ notifications,
2534
+ unhandledCount,
2535
+ markDone: markHandled
2536
+ };
2537
+ }
2538
+ // Annotate the CommonJS export names for ESM import in node:
2539
+ 0 && (module.exports = {
2540
+ AomiRuntimeProvider,
2541
+ BackendApi,
2542
+ ControlContextProvider,
2543
+ EventContextProvider,
2544
+ NotificationContextProvider,
2545
+ ThreadContextProvider,
2546
+ UserContextProvider,
2547
+ cn,
2548
+ formatAddress,
2549
+ getNetworkName,
2550
+ initThreadControl,
2551
+ useAomiRuntime,
2552
+ useControl,
2553
+ useCurrentThreadMessages,
2554
+ useCurrentThreadMetadata,
2555
+ useEventContext,
2556
+ useNotification,
2557
+ useNotificationHandler,
2558
+ useThreadContext,
2559
+ useUser,
2560
+ useWalletHandler
2561
+ });
2562
+ //# sourceMappingURL=index.cjs.map