@aomi-labs/react 0.2.0 → 0.2.1

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