@aomi-labs/react 0.2.4 → 0.3.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.js CHANGED
@@ -18,438 +18,12 @@ var __spreadValues = (a, b) => {
18
18
  };
19
19
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
20
 
21
- // packages/react/src/backend/sse.ts
22
- function extractSseData(rawEvent) {
23
- const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart());
24
- if (!dataLines.length) return null;
25
- return dataLines.join("\n");
26
- }
27
- async function readSseStream(stream, signal, onMessage) {
28
- const reader = stream.getReader();
29
- const decoder = new TextDecoder();
30
- let buffer = "";
31
- try {
32
- while (!signal.aborted) {
33
- const { value, done } = await reader.read();
34
- if (done) break;
35
- buffer += decoder.decode(value, { stream: true });
36
- buffer = buffer.replace(/\r/g, "");
37
- let separatorIndex = buffer.indexOf("\n\n");
38
- while (separatorIndex >= 0) {
39
- const rawEvent = buffer.slice(0, separatorIndex);
40
- buffer = buffer.slice(separatorIndex + 2);
41
- const data = extractSseData(rawEvent);
42
- if (data) {
43
- onMessage(data);
44
- }
45
- separatorIndex = buffer.indexOf("\n\n");
46
- }
47
- }
48
- } finally {
49
- reader.releaseLock();
50
- }
51
- }
52
- function createSseSubscriber({
53
- backendUrl,
54
- getHeaders,
55
- shouldLog = process.env.NODE_ENV !== "production"
56
- }) {
57
- const subscriptions = /* @__PURE__ */ new Map();
58
- const subscribe2 = (sessionId, onUpdate, onError) => {
59
- const existing = subscriptions.get(sessionId);
60
- const listener = { onUpdate, onError };
61
- if (existing) {
62
- existing.listeners.add(listener);
63
- if (shouldLog) {
64
- console.debug("[aomi][sse] listener added", {
65
- sessionId,
66
- listeners: existing.listeners.size
67
- });
68
- }
69
- return () => {
70
- existing.listeners.delete(listener);
71
- if (shouldLog) {
72
- console.debug("[aomi][sse] listener removed", {
73
- sessionId,
74
- listeners: existing.listeners.size
75
- });
76
- }
77
- if (existing.listeners.size === 0) {
78
- existing.stop("unsubscribe");
79
- if (subscriptions.get(sessionId) === existing) {
80
- subscriptions.delete(sessionId);
81
- }
82
- }
83
- };
84
- }
85
- const subscription = {
86
- abortController: null,
87
- retries: 0,
88
- retryTimer: null,
89
- stopped: false,
90
- listeners: /* @__PURE__ */ new Set([listener]),
91
- stop: (reason) => {
92
- var _a;
93
- subscription.stopped = true;
94
- if (subscription.retryTimer) {
95
- clearTimeout(subscription.retryTimer);
96
- subscription.retryTimer = null;
97
- }
98
- (_a = subscription.abortController) == null ? void 0 : _a.abort();
99
- subscription.abortController = null;
100
- if (shouldLog) {
101
- console.debug("[aomi][sse] stop", {
102
- sessionId,
103
- reason,
104
- retries: subscription.retries
105
- });
106
- }
107
- }
108
- };
109
- const scheduleRetry = () => {
110
- if (subscription.stopped) return;
111
- subscription.retries += 1;
112
- const delayMs = Math.min(500 * 2 ** (subscription.retries - 1), 1e4);
113
- if (shouldLog) {
114
- console.debug("[aomi][sse] retry scheduled", {
115
- sessionId,
116
- delayMs,
117
- retries: subscription.retries
118
- });
119
- }
120
- subscription.retryTimer = setTimeout(() => {
121
- void open();
122
- }, delayMs);
123
- };
124
- const open = async () => {
125
- var _a;
126
- if (subscription.stopped) return;
127
- if (subscription.retryTimer) {
128
- clearTimeout(subscription.retryTimer);
129
- subscription.retryTimer = null;
130
- }
131
- const controller = new AbortController();
132
- subscription.abortController = controller;
133
- const openedAt = Date.now();
134
- try {
135
- const response = await fetch(`${backendUrl}/api/updates`, {
136
- headers: getHeaders(sessionId),
137
- signal: controller.signal
138
- });
139
- if (!response.ok) {
140
- throw new Error(
141
- `SSE HTTP ${response.status}: ${response.statusText}`
142
- );
143
- }
144
- if (!response.body) {
145
- throw new Error("SSE response missing body");
146
- }
147
- subscription.retries = 0;
148
- await readSseStream(response.body, controller.signal, (data) => {
149
- var _a2, _b;
150
- let parsed;
151
- try {
152
- parsed = JSON.parse(data);
153
- } catch (error) {
154
- for (const item of subscription.listeners) {
155
- (_a2 = item.onError) == null ? void 0 : _a2.call(item, error);
156
- }
157
- return;
158
- }
159
- for (const item of subscription.listeners) {
160
- try {
161
- item.onUpdate(parsed);
162
- } catch (error) {
163
- (_b = item.onError) == null ? void 0 : _b.call(item, error);
164
- }
165
- }
166
- });
167
- if (shouldLog) {
168
- console.debug("[aomi][sse] stream ended", {
169
- sessionId,
170
- aborted: controller.signal.aborted,
171
- stopped: subscription.stopped,
172
- durationMs: Date.now() - openedAt
173
- });
174
- }
175
- } catch (error) {
176
- if (!controller.signal.aborted && !subscription.stopped) {
177
- for (const item of subscription.listeners) {
178
- (_a = item.onError) == null ? void 0 : _a.call(item, error);
179
- }
180
- }
181
- }
182
- if (!subscription.stopped) {
183
- scheduleRetry();
184
- }
185
- };
186
- subscriptions.set(sessionId, subscription);
187
- void open();
188
- return () => {
189
- subscription.listeners.delete(listener);
190
- if (shouldLog) {
191
- console.debug("[aomi][sse] listener removed", {
192
- sessionId,
193
- listeners: subscription.listeners.size
194
- });
195
- }
196
- if (subscription.listeners.size === 0) {
197
- subscription.stop("unsubscribe");
198
- if (subscriptions.get(sessionId) === subscription) {
199
- subscriptions.delete(sessionId);
200
- }
201
- }
202
- };
203
- };
204
- return { subscribe: subscribe2 };
205
- }
206
-
207
- // packages/react/src/backend/client.ts
208
- var SESSION_ID_HEADER = "X-Session-Id";
209
- var API_KEY_HEADER = "X-API-Key";
210
- function toQueryString(payload) {
211
- const params = new URLSearchParams();
212
- for (const [key, value] of Object.entries(payload)) {
213
- if (value === void 0 || value === null) continue;
214
- params.set(key, String(value));
215
- }
216
- const qs = params.toString();
217
- return qs ? `?${qs}` : "";
218
- }
219
- function withSessionHeader(sessionId, init) {
220
- const headers = new Headers(init);
221
- headers.set(SESSION_ID_HEADER, sessionId);
222
- return headers;
223
- }
224
- async function postState(backendUrl, path, payload, sessionId, apiKey) {
225
- const query = toQueryString(payload);
226
- const url = `${backendUrl}${path}${query}`;
227
- const headers = new Headers(withSessionHeader(sessionId));
228
- if (apiKey) {
229
- headers.set(API_KEY_HEADER, apiKey);
230
- }
231
- const response = await fetch(url, {
232
- method: "POST",
233
- headers
234
- });
235
- if (!response.ok) {
236
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
237
- }
238
- return await response.json();
239
- }
240
- var BackendApi = class {
241
- constructor(backendUrl) {
242
- this.backendUrl = backendUrl;
243
- this.sseSubscriber = createSseSubscriber({
244
- backendUrl,
245
- getHeaders: (sessionId) => withSessionHeader(sessionId, { Accept: "text/event-stream" })
246
- });
247
- }
248
- async fetchState(sessionId, userState) {
249
- const url = new URL("/api/state", this.backendUrl);
250
- if (userState) {
251
- url.searchParams.set("user_state", JSON.stringify(userState));
252
- }
253
- const response = await fetch(url.toString(), {
254
- headers: withSessionHeader(sessionId)
255
- });
256
- if (!response.ok) {
257
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
258
- }
259
- return await response.json();
260
- }
261
- async postChatMessage(sessionId, message, namespace, publicKey, apiKey, userState) {
262
- const payload = { message, namespace };
263
- if (publicKey) {
264
- payload.public_key = publicKey;
265
- }
266
- if (userState) {
267
- payload.user_state = JSON.stringify(userState);
268
- }
269
- return postState(
270
- this.backendUrl,
271
- "/api/chat",
272
- payload,
273
- sessionId,
274
- apiKey
275
- );
276
- }
277
- async postSystemMessage(sessionId, message) {
278
- return postState(
279
- this.backendUrl,
280
- "/api/system",
281
- {
282
- message
283
- },
284
- sessionId
285
- );
286
- }
287
- async postInterrupt(sessionId) {
288
- return postState(
289
- this.backendUrl,
290
- "/api/interrupt",
291
- {},
292
- sessionId
293
- );
294
- }
295
- /**
296
- * Subscribe to SSE updates for a session.
297
- * Uses fetch streaming and reconnects on disconnects.
298
- * Returns an unsubscribe function.
299
- */
300
- subscribeSSE(sessionId, onUpdate, onError) {
301
- return this.sseSubscriber.subscribe(sessionId, onUpdate, onError);
302
- }
303
- async fetchThreads(publicKey) {
304
- const url = `${this.backendUrl}/api/sessions?public_key=${encodeURIComponent(publicKey)}`;
305
- const response = await fetch(url);
306
- if (!response.ok) {
307
- throw new Error(`Failed to fetch threads: HTTP ${response.status}`);
308
- }
309
- return await response.json();
310
- }
311
- async fetchThread(sessionId) {
312
- const url = `${this.backendUrl}/api/sessions/${encodeURIComponent(sessionId)}`;
313
- const response = await fetch(url, {
314
- headers: withSessionHeader(sessionId)
315
- });
316
- if (!response.ok) {
317
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
318
- }
319
- return await response.json();
320
- }
321
- async createThread(threadId, publicKey) {
322
- const body = {};
323
- if (publicKey) body.public_key = publicKey;
324
- const url = `${this.backendUrl}/api/sessions`;
325
- const response = await fetch(url, {
326
- method: "POST",
327
- headers: withSessionHeader(threadId, {
328
- "Content-Type": "application/json"
329
- }),
330
- body: JSON.stringify(body)
331
- });
332
- if (!response.ok) {
333
- throw new Error(`Failed to create thread: HTTP ${response.status}`);
334
- }
335
- return await response.json();
336
- }
337
- async archiveThread(sessionId) {
338
- const url = `${this.backendUrl}/api/sessions/${encodeURIComponent(sessionId)}/archive`;
339
- const response = await fetch(url, {
340
- method: "POST",
341
- headers: withSessionHeader(sessionId)
342
- });
343
- if (!response.ok) {
344
- throw new Error(`Failed to archive thread: HTTP ${response.status}`);
345
- }
346
- }
347
- async unarchiveThread(sessionId) {
348
- const url = `${this.backendUrl}/api/sessions/${encodeURIComponent(sessionId)}/unarchive`;
349
- const response = await fetch(url, {
350
- method: "POST",
351
- headers: withSessionHeader(sessionId)
352
- });
353
- if (!response.ok) {
354
- throw new Error(`Failed to unarchive thread: HTTP ${response.status}`);
355
- }
356
- }
357
- async deleteThread(sessionId) {
358
- const url = `${this.backendUrl}/api/sessions/${encodeURIComponent(sessionId)}`;
359
- const response = await fetch(url, {
360
- method: "DELETE",
361
- headers: withSessionHeader(sessionId)
362
- });
363
- if (!response.ok) {
364
- throw new Error(`Failed to delete thread: HTTP ${response.status}`);
365
- }
366
- }
367
- async renameThread(sessionId, newTitle) {
368
- const url = `${this.backendUrl}/api/sessions/${encodeURIComponent(sessionId)}`;
369
- const response = await fetch(url, {
370
- method: "PATCH",
371
- headers: withSessionHeader(sessionId, {
372
- "Content-Type": "application/json"
373
- }),
374
- body: JSON.stringify({ title: newTitle })
375
- });
376
- if (!response.ok) {
377
- throw new Error(`Failed to rename thread: HTTP ${response.status}`);
378
- }
379
- }
380
- async getSystemEvents(sessionId, count) {
381
- const url = new URL("/api/events", this.backendUrl);
382
- if (count !== void 0) {
383
- url.searchParams.set("count", String(count));
384
- }
385
- const response = await fetch(url.toString(), {
386
- headers: withSessionHeader(sessionId)
387
- });
388
- if (!response.ok) {
389
- if (response.status === 404) return [];
390
- throw new Error(`Failed to get system events: HTTP ${response.status}`);
391
- }
392
- return await response.json();
393
- }
394
- // ===========================================================================
395
- // Control API
396
- // ===========================================================================
397
- /**
398
- * Get allowed namespaces for the current request context.
399
- */
400
- async getNamespaces(sessionId, publicKey, apiKey) {
401
- const url = new URL("/api/control/namespaces", this.backendUrl);
402
- if (publicKey) {
403
- url.searchParams.set("public_key", publicKey);
404
- }
405
- console.log("[BackendApi.getNamespaces]", {
406
- backendUrl: this.backendUrl,
407
- fullUrl: url.toString(),
408
- sessionId,
409
- publicKey
410
- });
411
- const headers = new Headers(withSessionHeader(sessionId));
412
- if (apiKey) {
413
- headers.set(API_KEY_HEADER, apiKey);
414
- }
415
- const response = await fetch(url.toString(), { headers });
416
- if (!response.ok) {
417
- throw new Error(`Failed to get namespaces: HTTP ${response.status}`);
418
- }
419
- return await response.json();
420
- }
421
- /**
422
- * Get available models.
423
- */
424
- async getModels(sessionId) {
425
- const url = new URL("/api/control/models", this.backendUrl);
426
- console.log("[BackendApi.getModels]", {
427
- backendUrl: this.backendUrl,
428
- fullUrl: url.toString(),
429
- sessionId
430
- });
431
- const response = await fetch(url.toString(), {
432
- headers: withSessionHeader(sessionId)
433
- });
434
- if (!response.ok) {
435
- throw new Error(`Failed to get models: HTTP ${response.status}`);
436
- }
437
- return await response.json();
438
- }
439
- /**
440
- * Set the model selection for a session.
441
- */
442
- async setModel(sessionId, rig, namespace, apiKey) {
443
- const payload = { rig };
444
- if (namespace) {
445
- payload.namespace = namespace;
446
- }
447
- return postState(this.backendUrl, "/api/control/model", payload, sessionId, apiKey);
448
- }
449
- };
21
+ // packages/react/src/index.ts
22
+ import { AomiClient as AomiClient2 } from "@aomi-labs/client";
450
23
 
451
24
  // packages/react/src/runtime/aomi-runtime.tsx
452
25
  import { useMemo as useMemo3 } from "react";
26
+ import { AomiClient } from "@aomi-labs/client";
453
27
 
454
28
  // packages/react/src/contexts/control-context.tsx
455
29
  import {
@@ -489,7 +63,7 @@ var logThreadMetadataChange = (source, threadId, prev, next) => {
489
63
  function initThreadControl() {
490
64
  return {
491
65
  model: null,
492
- namespace: null,
66
+ app: null,
493
67
  controlDirty: false,
494
68
  isProcessing: false
495
69
  };
@@ -573,7 +147,7 @@ var ThreadStore = class {
573
147
  initialThreadId,
574
148
  {
575
149
  title: "New Chat",
576
- status: "pending",
150
+ status: "regular",
577
151
  lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
578
152
  control: initThreadControl()
579
153
  }
@@ -645,7 +219,7 @@ function useControl() {
645
219
  }
646
220
  function ControlContextProvider({
647
221
  children,
648
- backendApi,
222
+ aomiClient,
649
223
  sessionId,
650
224
  publicKey,
651
225
  getThreadMetadata,
@@ -655,14 +229,14 @@ function ControlContextProvider({
655
229
  const [state, setStateInternal] = useState(() => ({
656
230
  apiKey: null,
657
231
  availableModels: [],
658
- authorizedNamespaces: [],
232
+ authorizedApps: [],
659
233
  defaultModel: null,
660
- defaultNamespace: null
234
+ defaultApp: null
661
235
  }));
662
236
  const stateRef = useRef(state);
663
237
  stateRef.current = state;
664
- const backendApiRef = useRef(backendApi);
665
- backendApiRef.current = backendApi;
238
+ const aomiClientRef = useRef(aomiClient);
239
+ aomiClientRef.current = aomiClient;
666
240
  const sessionIdRef = useRef(sessionId);
667
241
  sessionIdRef.current = sessionId;
668
242
  const publicKeyRef = useRef(publicKey);
@@ -696,33 +270,32 @@ function ControlContextProvider({
696
270
  }
697
271
  }, [state.apiKey]);
698
272
  useEffect(() => {
699
- const fetchNamespaces = async () => {
273
+ const fetchApps = async () => {
700
274
  var _a2, _b2;
701
275
  try {
702
- const namespaces = await backendApiRef.current.getNamespaces(
276
+ const apps = await aomiClientRef.current.getApps(
703
277
  sessionIdRef.current,
704
- publicKeyRef.current,
705
- (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
278
+ { publicKey: publicKeyRef.current, apiKey: (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0 }
706
279
  );
707
- const defaultNs = namespaces.includes("default") ? "default" : (_b2 = namespaces[0]) != null ? _b2 : null;
280
+ const defaultApp = apps.includes("default") ? "default" : (_b2 = apps[0]) != null ? _b2 : null;
708
281
  setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
709
- authorizedNamespaces: namespaces,
710
- defaultNamespace: defaultNs
282
+ authorizedApps: apps,
283
+ defaultApp
711
284
  }));
712
285
  } catch (error) {
713
- console.error("Failed to fetch namespaces:", error);
286
+ console.error("Failed to fetch apps:", error);
714
287
  setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
715
- authorizedNamespaces: ["default"],
716
- defaultNamespace: "default"
288
+ authorizedApps: ["default"],
289
+ defaultApp: "default"
717
290
  }));
718
291
  }
719
292
  };
720
- void fetchNamespaces();
293
+ void fetchApps();
721
294
  }, [state.apiKey]);
722
295
  useEffect(() => {
723
296
  const fetchModels = async () => {
724
297
  try {
725
- const models = await backendApiRef.current.getModels(
298
+ const models = await aomiClientRef.current.getModels(
726
299
  sessionIdRef.current
727
300
  );
728
301
  setStateInternal((prev) => {
@@ -747,7 +320,7 @@ function ControlContextProvider({
747
320
  }, []);
748
321
  const getAvailableModels = useCallback(async () => {
749
322
  try {
750
- const models = await backendApiRef.current.getModels(
323
+ const models = await aomiClientRef.current.getModels(
751
324
  sessionIdRef.current
752
325
  );
753
326
  setStateInternal((prev) => {
@@ -763,25 +336,24 @@ function ControlContextProvider({
763
336
  return [];
764
337
  }
765
338
  }, []);
766
- const getAuthorizedNamespaces = useCallback(async () => {
339
+ const getAuthorizedApps = useCallback(async () => {
767
340
  var _a2, _b2;
768
341
  try {
769
- const namespaces = await backendApiRef.current.getNamespaces(
342
+ const apps = await aomiClientRef.current.getApps(
770
343
  sessionIdRef.current,
771
- publicKeyRef.current,
772
- (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
344
+ { publicKey: publicKeyRef.current, apiKey: (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0 }
773
345
  );
774
- const defaultNs = namespaces.includes("default") ? "default" : (_b2 = namespaces[0]) != null ? _b2 : null;
346
+ const defaultApp = apps.includes("default") ? "default" : (_b2 = apps[0]) != null ? _b2 : null;
775
347
  setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
776
- authorizedNamespaces: namespaces,
777
- defaultNamespace: defaultNs
348
+ authorizedApps: apps,
349
+ defaultApp
778
350
  }));
779
- return namespaces;
351
+ return apps;
780
352
  } catch (error) {
781
- console.error("Failed to fetch namespaces:", error);
353
+ console.error("Failed to fetch apps:", error);
782
354
  setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
783
- authorizedNamespaces: ["default"],
784
- defaultNamespace: "default"
355
+ authorizedApps: ["default"],
356
+ defaultApp: "default"
785
357
  }));
786
358
  return ["default"];
787
359
  }
@@ -805,32 +377,31 @@ function ControlContextProvider({
805
377
  console.warn("[control-context] Cannot switch model while processing");
806
378
  return;
807
379
  }
808
- const namespace = (_d = (_c = currentControl.namespace) != null ? _c : stateRef.current.defaultNamespace) != null ? _d : "default";
380
+ const app = (_d = (_c = currentControl.app) != null ? _c : stateRef.current.defaultApp) != null ? _d : "default";
809
381
  console.log("[control-context] onModelSelect updating metadata", {
810
382
  threadId,
811
383
  model,
812
- namespace,
384
+ app,
813
385
  currentControl
814
386
  });
815
387
  updateThreadMetadataRef.current(threadId, {
816
388
  control: __spreadProps(__spreadValues({}, currentControl), {
817
389
  model,
818
- namespace,
390
+ app,
819
391
  controlDirty: true
820
392
  })
821
393
  });
822
394
  console.log("[control-context] onModelSelect calling backend setModel", {
823
395
  threadId,
824
396
  model,
825
- namespace,
826
- backendUrl: backendApiRef.current
397
+ app,
398
+ backendUrl: aomiClientRef.current
827
399
  });
828
400
  try {
829
- const result = await backendApiRef.current.setModel(
401
+ const result = await aomiClientRef.current.setModel(
830
402
  threadId,
831
403
  model,
832
- namespace,
833
- (_e = stateRef.current.apiKey) != null ? _e : void 0
404
+ { app, apiKey: (_e = stateRef.current.apiKey) != null ? _e : void 0 }
834
405
  );
835
406
  console.log("[control-context] onModelSelect backend result", result);
836
407
  } catch (err) {
@@ -838,34 +409,34 @@ function ControlContextProvider({
838
409
  throw err;
839
410
  }
840
411
  }, []);
841
- const onNamespaceSelect = useCallback((namespace) => {
412
+ const onAppSelect = useCallback((app) => {
842
413
  var _a2, _b2;
843
414
  const threadId = sessionIdRef.current;
844
415
  const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(threadId)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
845
416
  const isProcessing2 = currentControl.isProcessing;
846
- console.log("[control-context] onNamespaceSelect called", {
847
- namespace,
417
+ console.log("[control-context] onAppSelect called", {
418
+ app,
848
419
  isProcessing: isProcessing2,
849
420
  threadId
850
421
  });
851
422
  if (isProcessing2) {
852
423
  console.warn(
853
- "[control-context] Cannot switch namespace while processing"
424
+ "[control-context] Cannot switch app while processing"
854
425
  );
855
426
  return;
856
427
  }
857
- console.log("[control-context] onNamespaceSelect updating metadata", {
428
+ console.log("[control-context] onAppSelect updating metadata", {
858
429
  threadId,
859
- namespace,
430
+ app,
860
431
  currentControl
861
432
  });
862
433
  updateThreadMetadataRef.current(threadId, {
863
434
  control: __spreadProps(__spreadValues({}, currentControl), {
864
- namespace,
435
+ app,
865
436
  controlDirty: true
866
437
  })
867
438
  });
868
- console.log("[control-context] onNamespaceSelect metadata updated");
439
+ console.log("[control-context] onAppSelect metadata updated");
869
440
  }, []);
870
441
  const markControlSynced = useCallback(() => {
871
442
  var _a2, _b2;
@@ -895,11 +466,11 @@ function ControlContextProvider({
895
466
  if ("apiKey" in updates) {
896
467
  setApiKey((_a2 = updates.apiKey) != null ? _a2 : null);
897
468
  }
898
- if ("namespace" in updates && updates.namespace !== void 0 && updates.namespace !== null) {
899
- onNamespaceSelect(updates.namespace);
469
+ if ("app" in updates && updates.app !== void 0 && updates.app !== null) {
470
+ onAppSelect(updates.app);
900
471
  }
901
472
  },
902
- [setApiKey, onNamespaceSelect]
473
+ [setApiKey, onAppSelect]
903
474
  );
904
475
  return /* @__PURE__ */ jsx(
905
476
  ControlContext.Provider,
@@ -908,10 +479,10 @@ function ControlContextProvider({
908
479
  state,
909
480
  setApiKey,
910
481
  getAvailableModels,
911
- getAuthorizedNamespaces,
482
+ getAuthorizedApps,
912
483
  getCurrentThreadControl,
913
484
  onModelSelect,
914
- onNamespaceSelect,
485
+ onAppSelect,
915
486
  isProcessing,
916
487
  markControlSynced,
917
488
  getControlState,
@@ -932,20 +503,12 @@ import {
932
503
  useRef as useRef2,
933
504
  useState as useState2
934
505
  } from "react";
935
-
936
- // packages/react/src/backend/types.ts
937
- function isInlineCall(event) {
938
- return "InlineCall" in event;
939
- }
940
- function isSystemNotice(event) {
941
- return "SystemNotice" in event;
942
- }
943
- function isSystemError(event) {
944
- return "SystemError" in event;
945
- }
946
- function isAsyncCallback(event) {
947
- return "AsyncCallback" in event;
948
- }
506
+ import {
507
+ isInlineCall,
508
+ isSystemNotice,
509
+ isSystemError,
510
+ isAsyncCallback
511
+ } from "@aomi-labs/client";
949
512
 
950
513
  // packages/react/src/state/event-buffer.ts
951
514
  function createEventBuffer() {
@@ -1005,7 +568,7 @@ function useEventContext() {
1005
568
  }
1006
569
  function EventContextProvider({
1007
570
  children,
1008
- backendApi,
571
+ aomiClient,
1009
572
  sessionId
1010
573
  }) {
1011
574
  const bufferRef = useRef2(null);
@@ -1017,7 +580,7 @@ function EventContextProvider({
1017
580
  useEffect2(() => {
1018
581
  setSSEStatus(buffer, "connecting");
1019
582
  setSseStatus("connecting");
1020
- const unsubscribe = backendApi.subscribeSSE(
583
+ const unsubscribe = aomiClient.subscribeSSE(
1021
584
  sessionId,
1022
585
  (event) => {
1023
586
  enqueueInbound(buffer, {
@@ -1047,7 +610,7 @@ function EventContextProvider({
1047
610
  setSSEStatus(buffer, "disconnected");
1048
611
  setSseStatus("disconnected");
1049
612
  };
1050
- }, [backendApi, sessionId, buffer]);
613
+ }, [aomiClient, sessionId, buffer]);
1051
614
  const subscribeCallback = useCallback2(
1052
615
  (type, callback) => {
1053
616
  return subscribe(buffer, type, callback);
@@ -1061,12 +624,12 @@ function EventContextProvider({
1061
624
  type: event.type,
1062
625
  payload: event.payload
1063
626
  });
1064
- await backendApi.postSystemMessage(event.sessionId, message);
627
+ await aomiClient.sendSystemMessage(event.sessionId, message);
1065
628
  } catch (error) {
1066
629
  console.error("Failed to send outbound event:", error);
1067
630
  }
1068
631
  },
1069
- [backendApi]
632
+ [aomiClient]
1070
633
  );
1071
634
  const dispatchSystemEvents = useCallback2(
1072
635
  (sessionId2, events) => {
@@ -1284,7 +847,7 @@ function UserContextProvider({ children }) {
1284
847
  }
1285
848
 
1286
849
  // packages/react/src/runtime/core.tsx
1287
- import { useCallback as useCallback6, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef6 } from "react";
850
+ import { useCallback as useCallback7, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef7 } from "react";
1288
851
  import {
1289
852
  AssistantRuntimeProvider,
1290
853
  useExternalStoreRuntime
@@ -1398,28 +961,12 @@ var getChainInfo = (chainId) => chainId === void 0 ? void 0 : SUPPORTED_CHAINS.f
1398
961
  // packages/react/src/state/backend-state.ts
1399
962
  function createBackendState() {
1400
963
  return {
1401
- skipInitialFetch: /* @__PURE__ */ new Set(),
1402
- pendingChat: /* @__PURE__ */ new Map(),
1403
- runningThreads: /* @__PURE__ */ new Set(),
1404
- creatingThreadId: null,
1405
- createThreadPromise: null
964
+ runningThreads: /* @__PURE__ */ new Set()
1406
965
  };
1407
966
  }
1408
- function resolveThreadId(state, threadId) {
967
+ function resolveThreadId(_state, threadId) {
1409
968
  return threadId;
1410
969
  }
1411
- function isThreadReady(state, threadId) {
1412
- return state.creatingThreadId !== threadId;
1413
- }
1414
- function markSkipInitialFetch(state, threadId) {
1415
- state.skipInitialFetch.add(threadId);
1416
- }
1417
- function shouldSkipInitialFetch(state, threadId) {
1418
- return state.skipInitialFetch.has(threadId);
1419
- }
1420
- function clearSkipInitialFetch(state, threadId) {
1421
- state.skipInitialFetch.delete(threadId);
1422
- }
1423
970
  function setThreadRunning(state, threadId, running) {
1424
971
  if (running) {
1425
972
  state.runningThreads.add(threadId);
@@ -1430,21 +977,6 @@ function setThreadRunning(state, threadId, running) {
1430
977
  function isThreadRunning(state, threadId) {
1431
978
  return state.runningThreads.has(threadId);
1432
979
  }
1433
- function enqueuePendingChat(state, threadId, text) {
1434
- var _a;
1435
- const existing = (_a = state.pendingChat.get(threadId)) != null ? _a : [];
1436
- state.pendingChat.set(threadId, [...existing, text]);
1437
- }
1438
- function dequeuePendingChat(state, threadId) {
1439
- var _a;
1440
- const pending = (_a = state.pendingChat.get(threadId)) != null ? _a : [];
1441
- state.pendingChat.delete(threadId);
1442
- return pending;
1443
- }
1444
- function hasPendingChat(state, threadId) {
1445
- var _a, _b;
1446
- return ((_b = (_a = state.pendingChat.get(threadId)) == null ? void 0 : _a.length) != null ? _b : 0) > 0;
1447
- }
1448
980
 
1449
981
  // packages/react/src/runtime/message-controller.ts
1450
982
  var MessageController = class {
@@ -1452,11 +984,7 @@ var MessageController = class {
1452
984
  this.config = config;
1453
985
  }
1454
986
  inbound(threadId, msgs) {
1455
- const backendState = this.config.backendStateRef.current;
1456
987
  if (!msgs) return;
1457
- if (hasPendingChat(backendState, threadId)) {
1458
- return;
1459
- }
1460
988
  const threadMessages = [];
1461
989
  for (const msg of msgs) {
1462
990
  const threadMessage = toInboundMessage(msg);
@@ -1486,25 +1014,17 @@ var MessageController = class {
1486
1014
  threadState.updateThreadMetadata(threadId, {
1487
1015
  lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
1488
1016
  });
1489
- if (!isThreadReady(backendState, threadId)) {
1490
- this.markRunning(threadId, true);
1491
- enqueuePendingChat(backendState, threadId, text);
1492
- return;
1493
- }
1494
1017
  const backendThreadId = resolveThreadId(backendState, threadId);
1495
- const namespace = this.config.getNamespace();
1018
+ const app = this.config.getApp();
1496
1019
  const publicKey = (_b = (_a = this.config).getPublicKey) == null ? void 0 : _b.call(_a);
1497
1020
  const apiKey = (_e = (_d = (_c = this.config).getApiKey) == null ? void 0 : _d.call(_c)) != null ? _e : void 0;
1498
1021
  const userState = (_g = (_f = this.config).getUserState) == null ? void 0 : _g.call(_f);
1499
1022
  try {
1500
1023
  this.markRunning(threadId, true);
1501
- const response = await this.config.backendApiRef.current.postChatMessage(
1024
+ const response = await this.config.aomiClientRef.current.sendMessage(
1502
1025
  backendThreadId,
1503
1026
  text,
1504
- namespace,
1505
- publicKey,
1506
- apiKey,
1507
- userState
1027
+ { app, publicKey, apiKey, userState }
1508
1028
  );
1509
1029
  if (response == null ? void 0 : response.messages) {
1510
1030
  this.inbound(threadId, response.messages);
@@ -1522,40 +1042,13 @@ var MessageController = class {
1522
1042
  this.markRunning(threadId, false);
1523
1043
  }
1524
1044
  }
1525
- async flushPendingChat(threadId) {
1526
- var _a, _b, _c, _d, _e, _f, _g;
1527
- const backendState = this.config.backendStateRef.current;
1528
- const pending = dequeuePendingChat(backendState, threadId);
1529
- if (!pending.length) return;
1530
- const backendThreadId = resolveThreadId(backendState, threadId);
1531
- const namespace = this.config.getNamespace();
1532
- const publicKey = (_b = (_a = this.config).getPublicKey) == null ? void 0 : _b.call(_a);
1533
- const apiKey = (_e = (_d = (_c = this.config).getApiKey) == null ? void 0 : _d.call(_c)) != null ? _e : void 0;
1534
- const userState = (_g = (_f = this.config).getUserState) == null ? void 0 : _g.call(_f);
1535
- for (const text of pending) {
1536
- try {
1537
- await this.config.backendApiRef.current.postChatMessage(
1538
- backendThreadId,
1539
- text,
1540
- namespace,
1541
- publicKey,
1542
- apiKey,
1543
- userState
1544
- );
1545
- } catch (error) {
1546
- console.error("Failed to send queued message:", error);
1547
- }
1548
- }
1549
- this.config.polling.start(threadId);
1550
- }
1551
1045
  async cancel(threadId) {
1552
1046
  var _a;
1553
- const backendState = this.config.backendStateRef.current;
1554
- if (!isThreadReady(backendState, threadId)) return;
1555
1047
  this.config.polling.stop(threadId);
1048
+ const backendState = this.config.backendStateRef.current;
1556
1049
  const backendThreadId = resolveThreadId(backendState, threadId);
1557
1050
  try {
1558
- const response = await this.config.backendApiRef.current.postInterrupt(backendThreadId);
1051
+ const response = await this.config.aomiClientRef.current.interrupt(backendThreadId);
1559
1052
  if (response == null ? void 0 : response.messages) {
1560
1053
  this.inbound(threadId, response.messages);
1561
1054
  }
@@ -1591,7 +1084,6 @@ var PollingController = class {
1591
1084
  start(threadId) {
1592
1085
  var _a, _b;
1593
1086
  const backendState = this.config.backendStateRef.current;
1594
- if (!isThreadReady(backendState, threadId)) return;
1595
1087
  if (this.intervals.has(threadId)) return;
1596
1088
  const backendThreadId = resolveThreadId(backendState, threadId);
1597
1089
  setThreadRunning(backendState, threadId, true);
@@ -1604,7 +1096,7 @@ var PollingController = class {
1604
1096
  threadId
1605
1097
  );
1606
1098
  const userState = (_b2 = (_a2 = this.config).getUserState) == null ? void 0 : _b2.call(_a2);
1607
- const state = await this.config.backendApiRef.current.fetchState(
1099
+ const state = await this.config.aomiClientRef.current.fetchState(
1608
1100
  backendThreadId,
1609
1101
  userState
1610
1102
  );
@@ -1652,12 +1144,12 @@ var PollingController = class {
1652
1144
  };
1653
1145
 
1654
1146
  // packages/react/src/runtime/orchestrator.ts
1655
- function useRuntimeOrchestrator(backendApi, options) {
1147
+ function useRuntimeOrchestrator(aomiClient, options) {
1656
1148
  const threadContext = useThreadContext();
1657
1149
  const threadContextRef = useRef5(threadContext);
1658
1150
  threadContextRef.current = threadContext;
1659
- const backendApiRef = useRef5(backendApi);
1660
- backendApiRef.current = backendApi;
1151
+ const aomiClientRef = useRef5(aomiClient);
1152
+ aomiClientRef.current = aomiClient;
1661
1153
  const backendStateRef = useRef5(createBackendState());
1662
1154
  const [isRunning, setIsRunning] = useState5(false);
1663
1155
  const messageControllerRef = useRef5(null);
@@ -1665,7 +1157,7 @@ function useRuntimeOrchestrator(backendApi, options) {
1665
1157
  const pendingFetches = useRef5(/* @__PURE__ */ new Set());
1666
1158
  if (!pollingRef.current) {
1667
1159
  pollingRef.current = new PollingController({
1668
- backendApiRef,
1160
+ aomiClientRef,
1669
1161
  backendStateRef,
1670
1162
  applyMessages: (threadId, msgs) => {
1671
1163
  var _a;
@@ -1687,13 +1179,13 @@ function useRuntimeOrchestrator(backendApi, options) {
1687
1179
  }
1688
1180
  if (!messageControllerRef.current) {
1689
1181
  messageControllerRef.current = new MessageController({
1690
- backendApiRef,
1182
+ aomiClientRef,
1691
1183
  backendStateRef,
1692
1184
  threadContextRef,
1693
1185
  polling: pollingRef.current,
1694
1186
  setGlobalIsRunning: setIsRunning,
1695
1187
  getPublicKey: options.getPublicKey,
1696
- getNamespace: options.getNamespace,
1188
+ getApp: options.getApp,
1697
1189
  getApiKey: options.getApiKey,
1698
1190
  getUserState: options.getUserState,
1699
1191
  onSyncEvents: options.onSyncEvents
@@ -1701,26 +1193,12 @@ function useRuntimeOrchestrator(backendApi, options) {
1701
1193
  }
1702
1194
  const ensureInitialState = useCallback5(async (threadId) => {
1703
1195
  var _a, _b, _c, _d;
1704
- const backendState = backendStateRef.current;
1705
- if (shouldSkipInitialFetch(backendState, threadId)) {
1706
- clearSkipInitialFetch(backendState, threadId);
1707
- if (threadContextRef.current.currentThreadId === threadId) {
1708
- setIsRunning(false);
1709
- }
1710
- return;
1711
- }
1712
- if (!isThreadReady(backendState, threadId)) {
1713
- if (threadContextRef.current.currentThreadId === threadId) {
1714
- setIsRunning(false);
1715
- }
1716
- return;
1717
- }
1718
1196
  if (pendingFetches.current.has(threadId)) return;
1719
- const backendThreadId = resolveThreadId(backendState, threadId);
1197
+ const backendThreadId = resolveThreadId(backendStateRef.current, threadId);
1720
1198
  pendingFetches.current.add(threadId);
1721
1199
  try {
1722
1200
  const userState = (_a = options.getUserState) == null ? void 0 : _a.call(options);
1723
- const state = await backendApiRef.current.fetchState(
1201
+ const state = await aomiClientRef.current.fetchState(
1724
1202
  backendThreadId,
1725
1203
  userState
1726
1204
  );
@@ -1752,7 +1230,7 @@ function useRuntimeOrchestrator(backendApi, options) {
1752
1230
  isRunning,
1753
1231
  setIsRunning,
1754
1232
  ensureInitialState,
1755
- backendApiRef
1233
+ aomiClientRef
1756
1234
  };
1757
1235
  }
1758
1236
 
@@ -1783,151 +1261,35 @@ function buildThreadLists(threadMetadata) {
1783
1261
  return { regularThreads, archivedThreads };
1784
1262
  }
1785
1263
  function buildThreadListAdapter({
1786
- backendStateRef,
1787
- backendApiRef,
1264
+ aomiClientRef,
1788
1265
  threadContext,
1789
- currentThreadIdRef,
1790
- polling,
1791
- userAddress,
1792
- setIsRunning,
1793
- getNamespace,
1794
- getApiKey,
1795
- getUserState
1266
+ setIsRunning
1796
1267
  }) {
1797
- const backendState = backendStateRef.current;
1798
1268
  const { regularThreads, archivedThreads } = buildThreadLists(
1799
1269
  threadContext.allThreadsMetadata
1800
1270
  );
1801
- const preparePendingThread = (threadId) => {
1802
- const previousPendingId = backendState.creatingThreadId;
1803
- if (previousPendingId && previousPendingId !== threadId) {
1804
- threadContext.setThreadMetadata((prev) => {
1805
- const next = new Map(prev);
1806
- next.delete(previousPendingId);
1807
- return next;
1808
- });
1809
- threadContext.setThreads((prev) => {
1810
- const next = new Map(prev);
1811
- next.delete(previousPendingId);
1812
- return next;
1813
- });
1814
- backendState.pendingChat.delete(previousPendingId);
1815
- backendState.skipInitialFetch.delete(previousPendingId);
1816
- }
1817
- backendState.creatingThreadId = threadId;
1818
- backendState.pendingChat.delete(threadId);
1819
- threadContext.setThreadMetadata(
1820
- (prev) => new Map(prev).set(threadId, {
1821
- title: "New Chat",
1822
- status: "pending",
1823
- lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
1824
- control: initThreadControl()
1825
- })
1826
- );
1827
- threadContext.setThreadMessages(threadId, []);
1828
- threadContext.setCurrentThreadId(threadId);
1829
- setIsRunning(false);
1830
- threadContext.bumpThreadViewKey();
1831
- };
1832
- const findPendingThreadId = () => {
1833
- if (backendState.creatingThreadId) return backendState.creatingThreadId;
1834
- for (const [id, meta] of threadContext.allThreadsMetadata.entries()) {
1835
- if (meta.status === "pending") return id;
1836
- }
1837
- return null;
1838
- };
1839
1271
  return {
1840
1272
  threadId: threadContext.currentThreadId,
1841
1273
  threads: regularThreads,
1842
1274
  archivedThreads,
1843
- onSwitchToNewThread: async () => {
1844
- var _a;
1845
- const pendingId = findPendingThreadId();
1846
- if (pendingId) {
1847
- preparePendingThread(pendingId);
1848
- return;
1849
- }
1850
- if (backendState.createThreadPromise) {
1851
- preparePendingThread((_a = backendState.creatingThreadId) != null ? _a : generateUUID());
1852
- return;
1853
- }
1275
+ onSwitchToNewThread: () => {
1854
1276
  const threadId = generateUUID();
1855
- preparePendingThread(threadId);
1856
- const createPromise = backendApiRef.current.createThread(threadId, userAddress).then(async (newThread) => {
1857
- var _a2, _b;
1858
- const uiThreadId = (_a2 = backendState.creatingThreadId) != null ? _a2 : threadId;
1859
- const backendId = newThread.session_id;
1860
- if (uiThreadId !== backendId) {
1861
- console.warn("[aomi][thread] backend id mismatch", {
1862
- uiThreadId,
1863
- backendId
1864
- });
1865
- }
1866
- markSkipInitialFetch(backendState, uiThreadId);
1867
- threadContext.setThreadMetadata((prev) => {
1868
- var _a3, _b2, _c;
1869
- const next = new Map(prev);
1870
- const existing = next.get(uiThreadId);
1871
- const nextStatus = (existing == null ? void 0 : existing.status) === "archived" ? "archived" : "regular";
1872
- next.set(uiThreadId, {
1873
- title: (_a3 = existing == null ? void 0 : existing.title) != null ? _a3 : "New Chat",
1874
- status: nextStatus,
1875
- lastActiveAt: (_b2 = existing == null ? void 0 : existing.lastActiveAt) != null ? _b2 : (/* @__PURE__ */ new Date()).toISOString(),
1876
- control: (_c = existing == null ? void 0 : existing.control) != null ? _c : initThreadControl()
1877
- });
1878
- return next;
1879
- });
1880
- if (backendState.creatingThreadId === uiThreadId) {
1881
- backendState.creatingThreadId = null;
1882
- }
1883
- const pendingMessages = backendState.pendingChat.get(uiThreadId);
1884
- if (pendingMessages == null ? void 0 : pendingMessages.length) {
1885
- backendState.pendingChat.delete(uiThreadId);
1886
- const namespace = getNamespace();
1887
- const apiKey = (_b = getApiKey == null ? void 0 : getApiKey()) != null ? _b : void 0;
1888
- const userState = getUserState == null ? void 0 : getUserState();
1889
- for (const text of pendingMessages) {
1890
- try {
1891
- await backendApiRef.current.postChatMessage(
1892
- backendId,
1893
- text,
1894
- namespace,
1895
- userAddress,
1896
- apiKey,
1897
- userState
1898
- );
1899
- } catch (error) {
1900
- console.error("Failed to send queued message:", error);
1901
- }
1902
- }
1903
- if (currentThreadIdRef.current === uiThreadId) {
1904
- polling == null ? void 0 : polling.start(uiThreadId);
1905
- }
1906
- }
1907
- }).catch((error) => {
1908
- var _a2;
1909
- console.error("Failed to create new thread:", error);
1910
- const failedId = (_a2 = backendState.creatingThreadId) != null ? _a2 : threadId;
1911
- threadContext.setThreadMetadata((prev) => {
1912
- const next = new Map(prev);
1913
- next.delete(failedId);
1914
- return next;
1915
- });
1916
- threadContext.setThreads((prev) => {
1917
- const next = new Map(prev);
1918
- next.delete(failedId);
1919
- return next;
1920
- });
1921
- if (backendState.creatingThreadId === failedId) {
1922
- backendState.creatingThreadId = null;
1923
- }
1924
- }).finally(() => {
1925
- backendState.createThreadPromise = null;
1926
- });
1927
- backendState.createThreadPromise = createPromise;
1277
+ threadContext.setThreadMetadata(
1278
+ (prev) => new Map(prev).set(threadId, {
1279
+ title: "New Chat",
1280
+ status: "regular",
1281
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
1282
+ control: initThreadControl()
1283
+ })
1284
+ );
1285
+ threadContext.setThreadMessages(threadId, []);
1286
+ threadContext.setCurrentThreadId(threadId);
1287
+ setIsRunning(false);
1288
+ threadContext.bumpThreadViewKey();
1928
1289
  },
1929
1290
  onSwitchToThread: (threadId) => {
1930
1291
  threadContext.setCurrentThreadId(threadId);
1292
+ threadContext.bumpThreadViewKey();
1931
1293
  },
1932
1294
  onRename: async (threadId, newTitle) => {
1933
1295
  var _a, _b;
@@ -1937,7 +1299,7 @@ function buildThreadListAdapter({
1937
1299
  title: normalizedTitle
1938
1300
  });
1939
1301
  try {
1940
- await backendApiRef.current.renameThread(threadId, newTitle);
1302
+ await aomiClientRef.current.renameThread(threadId, newTitle);
1941
1303
  } catch (error) {
1942
1304
  console.error("Failed to rename thread:", error);
1943
1305
  threadContext.updateThreadMetadata(threadId, {
@@ -1948,7 +1310,7 @@ function buildThreadListAdapter({
1948
1310
  onArchive: async (threadId) => {
1949
1311
  threadContext.updateThreadMetadata(threadId, { status: "archived" });
1950
1312
  try {
1951
- await backendApiRef.current.archiveThread(threadId);
1313
+ await aomiClientRef.current.archiveThread(threadId);
1952
1314
  } catch (error) {
1953
1315
  console.error("Failed to archive thread:", error);
1954
1316
  threadContext.updateThreadMetadata(threadId, { status: "regular" });
@@ -1957,7 +1319,7 @@ function buildThreadListAdapter({
1957
1319
  onUnarchive: async (threadId) => {
1958
1320
  threadContext.updateThreadMetadata(threadId, { status: "regular" });
1959
1321
  try {
1960
- await backendApiRef.current.unarchiveThread(threadId);
1322
+ await aomiClientRef.current.unarchiveThread(threadId);
1961
1323
  } catch (error) {
1962
1324
  console.error("Failed to unarchive thread:", error);
1963
1325
  threadContext.updateThreadMetadata(threadId, { status: "archived" });
@@ -1965,7 +1327,7 @@ function buildThreadListAdapter({
1965
1327
  },
1966
1328
  onDelete: async (threadId) => {
1967
1329
  try {
1968
- await backendApiRef.current.deleteThread(threadId);
1330
+ await aomiClientRef.current.deleteThread(threadId);
1969
1331
  threadContext.setThreadMetadata((prev) => {
1970
1332
  const next = new Map(prev);
1971
1333
  next.delete(threadId);
@@ -1976,12 +1338,6 @@ function buildThreadListAdapter({
1976
1338
  next.delete(threadId);
1977
1339
  return next;
1978
1340
  });
1979
- backendState.pendingChat.delete(threadId);
1980
- backendState.skipInitialFetch.delete(threadId);
1981
- backendState.runningThreads.delete(threadId);
1982
- if (backendState.creatingThreadId === threadId) {
1983
- backendState.creatingThreadId = null;
1984
- }
1985
1341
  if (threadContext.currentThreadId === threadId) {
1986
1342
  const firstRegularThread = Array.from(
1987
1343
  threadContext.allThreadsMetadata.entries()
@@ -2024,11 +1380,223 @@ function useAomiRuntime() {
2024
1380
  return context;
2025
1381
  }
2026
1382
 
1383
+ // packages/react/src/handlers/wallet-handler.ts
1384
+ import { useCallback as useCallback6, useEffect as useEffect3, useRef as useRef6, useState as useState6 } from "react";
1385
+
1386
+ // packages/react/src/state/wallet-buffer.ts
1387
+ function createWalletBuffer() {
1388
+ return { queue: [], nextId: 1 };
1389
+ }
1390
+ function enqueue(buffer, kind, payload) {
1391
+ const request = {
1392
+ id: `wreq-${buffer.nextId++}`,
1393
+ kind,
1394
+ payload,
1395
+ status: "pending",
1396
+ timestamp: Date.now()
1397
+ };
1398
+ buffer.queue.push(request);
1399
+ return request;
1400
+ }
1401
+ function dequeue(buffer, id) {
1402
+ const index = buffer.queue.findIndex((r) => r.id === id);
1403
+ if (index === -1) return null;
1404
+ return buffer.queue.splice(index, 1)[0];
1405
+ }
1406
+ function markProcessing(buffer, id) {
1407
+ const request = buffer.queue.find((r) => r.id === id);
1408
+ if (!request || request.status !== "pending") return false;
1409
+ request.status = "processing";
1410
+ return true;
1411
+ }
1412
+ function getAll(buffer) {
1413
+ return [...buffer.queue];
1414
+ }
1415
+
1416
+ // packages/react/src/handlers/wallet-handler.ts
1417
+ function asRecord(value) {
1418
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1419
+ return value;
1420
+ }
1421
+ function getToolArgs(payload) {
1422
+ const root = asRecord(payload);
1423
+ const nestedArgs = asRecord(root == null ? void 0 : root.args);
1424
+ return nestedArgs != null ? nestedArgs : root != null ? root : {};
1425
+ }
1426
+ function parseChainId(value) {
1427
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1428
+ if (typeof value !== "string") return void 0;
1429
+ const trimmed = value.trim();
1430
+ if (!trimmed) return void 0;
1431
+ if (trimmed.startsWith("0x")) {
1432
+ const parsedHex = Number.parseInt(trimmed.slice(2), 16);
1433
+ return Number.isFinite(parsedHex) ? parsedHex : void 0;
1434
+ }
1435
+ const parsed = Number.parseInt(trimmed, 10);
1436
+ return Number.isFinite(parsed) ? parsed : void 0;
1437
+ }
1438
+ function normalizeTxPayload(payload) {
1439
+ var _a, _b, _c;
1440
+ const root = asRecord(payload);
1441
+ const args = getToolArgs(payload);
1442
+ const ctx = asRecord(root == null ? void 0 : root.ctx);
1443
+ const to = typeof args.to === "string" ? args.to : void 0;
1444
+ if (!to) return null;
1445
+ const valueRaw = args.value;
1446
+ const value = typeof valueRaw === "string" ? valueRaw : typeof valueRaw === "number" && Number.isFinite(valueRaw) ? String(Math.trunc(valueRaw)) : void 0;
1447
+ const data = typeof args.data === "string" ? args.data : void 0;
1448
+ const chainId = (_c = (_b = (_a = parseChainId(args.chainId)) != null ? _a : parseChainId(args.chain_id)) != null ? _b : parseChainId(ctx == null ? void 0 : ctx.user_chain_id)) != null ? _c : parseChainId(ctx == null ? void 0 : ctx.userChainId);
1449
+ return {
1450
+ to,
1451
+ value,
1452
+ data,
1453
+ chainId
1454
+ };
1455
+ }
1456
+ function normalizeEip712Payload(payload) {
1457
+ var _a;
1458
+ const args = getToolArgs(payload);
1459
+ const typedDataRaw = (_a = args.typed_data) != null ? _a : args.typedData;
1460
+ let typedData;
1461
+ if (typeof typedDataRaw === "string") {
1462
+ try {
1463
+ const parsed = JSON.parse(typedDataRaw);
1464
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1465
+ typedData = parsed;
1466
+ }
1467
+ } catch (e) {
1468
+ typedData = void 0;
1469
+ }
1470
+ } else if (typedDataRaw && typeof typedDataRaw === "object" && !Array.isArray(typedDataRaw)) {
1471
+ typedData = typedDataRaw;
1472
+ }
1473
+ const description = typeof args.description === "string" ? args.description : void 0;
1474
+ return {
1475
+ typed_data: typedData,
1476
+ description
1477
+ };
1478
+ }
1479
+ function useWalletHandler({
1480
+ sessionId,
1481
+ onRequestComplete
1482
+ }) {
1483
+ const { subscribe: subscribe2, sendOutboundSystem: sendOutbound } = useEventContext();
1484
+ const bufferRef = useRef6(createWalletBuffer());
1485
+ const [pendingRequests, setPendingRequests] = useState6([]);
1486
+ const syncState = useCallback6(() => {
1487
+ setPendingRequests(getAll(bufferRef.current));
1488
+ }, []);
1489
+ useEffect3(() => {
1490
+ const unsubscribe = subscribe2(
1491
+ "wallet_tx_request",
1492
+ (event) => {
1493
+ const payload = normalizeTxPayload(event.payload);
1494
+ if (!payload) {
1495
+ console.warn("[aomi][wallet] Ignoring tx request with invalid payload", event.payload);
1496
+ return;
1497
+ }
1498
+ enqueue(bufferRef.current, "transaction", payload);
1499
+ syncState();
1500
+ }
1501
+ );
1502
+ return unsubscribe;
1503
+ }, [subscribe2, syncState]);
1504
+ useEffect3(() => {
1505
+ const unsubscribe = subscribe2(
1506
+ "wallet_eip712_request",
1507
+ (event) => {
1508
+ var _a;
1509
+ const payload = normalizeEip712Payload((_a = event.payload) != null ? _a : {});
1510
+ enqueue(bufferRef.current, "eip712_sign", payload);
1511
+ syncState();
1512
+ }
1513
+ );
1514
+ return unsubscribe;
1515
+ }, [subscribe2, syncState]);
1516
+ const startProcessingCb = useCallback6(
1517
+ (id) => {
1518
+ markProcessing(bufferRef.current, id);
1519
+ syncState();
1520
+ },
1521
+ [syncState]
1522
+ );
1523
+ const resolveRequest = useCallback6(
1524
+ (id, result) => {
1525
+ var _a;
1526
+ const removed = dequeue(bufferRef.current, id);
1527
+ if (!removed) return;
1528
+ let outbound;
1529
+ if (removed.kind === "transaction") {
1530
+ outbound = sendOutbound({
1531
+ type: "wallet:tx_complete",
1532
+ sessionId,
1533
+ payload: {
1534
+ txHash: (_a = result.txHash) != null ? _a : "",
1535
+ status: "success",
1536
+ amount: result.amount
1537
+ }
1538
+ });
1539
+ } else {
1540
+ const eip712Payload = removed.payload;
1541
+ outbound = sendOutbound({
1542
+ type: "wallet_eip712_response",
1543
+ sessionId,
1544
+ payload: {
1545
+ status: "success",
1546
+ signature: result.signature,
1547
+ description: eip712Payload.description
1548
+ }
1549
+ });
1550
+ }
1551
+ outbound.then(() => onRequestComplete == null ? void 0 : onRequestComplete());
1552
+ syncState();
1553
+ },
1554
+ [sendOutbound, sessionId, syncState, onRequestComplete]
1555
+ );
1556
+ const rejectRequest = useCallback6(
1557
+ (id, error) => {
1558
+ const removed = dequeue(bufferRef.current, id);
1559
+ if (!removed) return;
1560
+ let outbound;
1561
+ if (removed.kind === "transaction") {
1562
+ outbound = sendOutbound({
1563
+ type: "wallet:tx_complete",
1564
+ sessionId,
1565
+ payload: {
1566
+ txHash: "",
1567
+ status: "failed"
1568
+ }
1569
+ });
1570
+ } else {
1571
+ const eip712Payload = removed.payload;
1572
+ outbound = sendOutbound({
1573
+ type: "wallet_eip712_response",
1574
+ sessionId,
1575
+ payload: {
1576
+ status: "failed",
1577
+ error: error != null ? error : "EIP-712 signing failed",
1578
+ description: eip712Payload.description
1579
+ }
1580
+ });
1581
+ }
1582
+ outbound.then(() => onRequestComplete == null ? void 0 : onRequestComplete());
1583
+ syncState();
1584
+ },
1585
+ [sendOutbound, sessionId, syncState, onRequestComplete]
1586
+ );
1587
+ return {
1588
+ pendingRequests,
1589
+ startProcessing: startProcessingCb,
1590
+ resolveRequest,
1591
+ rejectRequest
1592
+ };
1593
+ }
1594
+
2027
1595
  // packages/react/src/runtime/core.tsx
2028
1596
  import { jsx as jsx6 } from "react/jsx-runtime";
2029
1597
  function AomiRuntimeCore({
2030
1598
  children,
2031
- backendApi
1599
+ aomiClient
2032
1600
  }) {
2033
1601
  const threadContext = useThreadContext();
2034
1602
  const eventContext = useEventContext();
@@ -2043,18 +1611,18 @@ function AomiRuntimeCore({
2043
1611
  isRunning,
2044
1612
  setIsRunning,
2045
1613
  ensureInitialState,
2046
- backendApiRef
2047
- } = useRuntimeOrchestrator(backendApi, {
1614
+ aomiClientRef
1615
+ } = useRuntimeOrchestrator(aomiClient, {
2048
1616
  onSyncEvents: dispatchSystemEvents,
2049
1617
  getPublicKey: () => getUserState().address,
2050
1618
  getUserState,
2051
- getNamespace: () => {
1619
+ getApp: () => {
2052
1620
  var _a, _b;
2053
- return (_b = (_a = getCurrentThreadControl().namespace) != null ? _a : getControlState().defaultNamespace) != null ? _b : "default";
1621
+ return (_b = (_a = getCurrentThreadControl().app) != null ? _a : getControlState().defaultApp) != null ? _b : "default";
2054
1622
  },
2055
1623
  getApiKey: () => getControlState().apiKey
2056
1624
  });
2057
- useEffect3(() => {
1625
+ useEffect4(() => {
2058
1626
  const unsubscribe = onUserStateChange(async (newUser) => {
2059
1627
  const sessionId = threadContext.currentThreadId;
2060
1628
  const message = JSON.stringify({
@@ -2066,24 +1634,44 @@ function AomiRuntimeCore({
2066
1634
  ensName: newUser.ensName
2067
1635
  }
2068
1636
  });
2069
- await backendApiRef.current.postSystemMessage(sessionId, message);
1637
+ await aomiClientRef.current.sendSystemMessage(sessionId, message);
2070
1638
  });
2071
1639
  return unsubscribe;
2072
- }, [onUserStateChange, backendApiRef, threadContext.currentThreadId]);
2073
- const threadContextRef = useRef6(threadContext);
1640
+ }, [onUserStateChange, aomiClientRef, threadContext.currentThreadId]);
1641
+ const threadContextRef = useRef7(threadContext);
2074
1642
  threadContextRef.current = threadContext;
2075
- const currentThreadIdRef = useRef6(threadContext.currentThreadId);
2076
- useEffect3(() => {
1643
+ const currentThreadIdRef = useRef7(threadContext.currentThreadId);
1644
+ useEffect4(() => {
2077
1645
  currentThreadIdRef.current = threadContext.currentThreadId;
2078
1646
  }, [threadContext.currentThreadId]);
2079
- useEffect3(() => {
1647
+ const onWalletRequestComplete = useCallback7(() => {
1648
+ polling.start(currentThreadIdRef.current);
1649
+ }, [polling]);
1650
+ const walletHandler = useWalletHandler({
1651
+ sessionId: threadContext.currentThreadId,
1652
+ onRequestComplete: onWalletRequestComplete
1653
+ });
1654
+ useEffect4(() => {
1655
+ const unsubscribe = eventContext.subscribe(
1656
+ "user_state_request",
1657
+ () => {
1658
+ eventContext.sendOutboundSystem({
1659
+ type: "user_state_response",
1660
+ sessionId: threadContext.currentThreadId,
1661
+ payload: getUserState()
1662
+ });
1663
+ }
1664
+ );
1665
+ return unsubscribe;
1666
+ }, [eventContext, threadContext.currentThreadId, getUserState]);
1667
+ useEffect4(() => {
2080
1668
  void ensureInitialState(threadContext.currentThreadId);
2081
1669
  }, [ensureInitialState, threadContext.currentThreadId]);
2082
- useEffect3(() => {
1670
+ useEffect4(() => {
2083
1671
  const threadId = threadContext.currentThreadId;
2084
1672
  setIsRunning(isThreadRunning(backendStateRef.current, threadId));
2085
1673
  }, [backendStateRef, setIsRunning, threadContext.currentThreadId]);
2086
- useEffect3(() => {
1674
+ useEffect4(() => {
2087
1675
  const threadId = threadContext.currentThreadId;
2088
1676
  const currentMeta = threadContext.getThreadMetadata(threadId);
2089
1677
  if (currentMeta && currentMeta.control.isProcessing !== isRunning) {
@@ -2097,21 +1685,13 @@ function AomiRuntimeCore({
2097
1685
  const currentMessages = threadContext.getThreadMessages(
2098
1686
  threadContext.currentThreadId
2099
1687
  );
2100
- const resolvedSessionId = useMemo2(
2101
- () => resolveThreadId(backendStateRef.current, threadContext.currentThreadId),
2102
- [
2103
- backendStateRef,
2104
- threadContext.currentThreadId,
2105
- threadContext.allThreadsMetadata
2106
- ]
2107
- );
2108
- useEffect3(() => {
1688
+ useEffect4(() => {
2109
1689
  const userAddress = user.address;
2110
1690
  if (!userAddress) return;
2111
1691
  const fetchThreadList = async () => {
2112
1692
  var _a, _b, _c;
2113
1693
  try {
2114
- const threadList = await backendApiRef.current.fetchThreads(userAddress);
1694
+ const threadList = await aomiClientRef.current.listThreads(userAddress);
2115
1695
  const currentContext = threadContextRef.current;
2116
1696
  const newMetadata = new Map(currentContext.allThreadsMetadata);
2117
1697
  let maxChatNum = currentContext.threadCnt;
@@ -2143,25 +1723,25 @@ function AomiRuntimeCore({
2143
1723
  }
2144
1724
  };
2145
1725
  void fetchThreadList();
2146
- }, [user.address, backendApiRef]);
1726
+ }, [user.address, aomiClientRef]);
2147
1727
  const threadListAdapter = useMemo2(
2148
1728
  () => buildThreadListAdapter({
2149
1729
  backendStateRef,
2150
- backendApiRef,
1730
+ aomiClientRef,
2151
1731
  threadContext,
2152
1732
  currentThreadIdRef,
2153
1733
  polling,
2154
1734
  userAddress: user.address,
2155
1735
  setIsRunning,
2156
- getNamespace: () => {
1736
+ getApp: () => {
2157
1737
  var _a, _b;
2158
- return (_b = (_a = getCurrentThreadControl().namespace) != null ? _a : getControlState().defaultNamespace) != null ? _b : "default";
1738
+ return (_b = (_a = getCurrentThreadControl().app) != null ? _a : getControlState().defaultApp) != null ? _b : "default";
2159
1739
  },
2160
1740
  getApiKey: () => getControlState().apiKey,
2161
1741
  getUserState
2162
1742
  }),
2163
1743
  [
2164
- backendApiRef,
1744
+ aomiClientRef,
2165
1745
  polling,
2166
1746
  user.address,
2167
1747
  backendStateRef,
@@ -2173,70 +1753,41 @@ function AomiRuntimeCore({
2173
1753
  getUserState
2174
1754
  ]
2175
1755
  );
2176
- useEffect3(() => {
1756
+ useEffect4(() => {
2177
1757
  const backendState = backendStateRef.current;
2178
- const currentSessionId = threadContext.currentThreadId;
2179
- if (process.env.NODE_ENV !== "production") {
2180
- console.debug("[aomi][sse] subscribe", {
2181
- currentSessionId,
2182
- resolvedSessionId,
2183
- hasMapping: currentSessionId !== resolvedSessionId
2184
- });
2185
- }
2186
- const unsubscribe = backendApiRef.current.subscribeSSE(
2187
- resolvedSessionId,
2188
- (event) => {
2189
- const eventType = event.type;
2190
- const sessionId = event.session_id;
2191
- if (eventType === "title_changed") {
2192
- const newTitle = event.new_title;
2193
- const targetThreadId = resolveThreadId(backendState, sessionId);
2194
- const normalizedTitle = isPlaceholderTitle(newTitle) ? "" : newTitle;
2195
- if (process.env.NODE_ENV !== "production") {
2196
- console.debug("[aomi][sse] title_changed", {
2197
- sessionId,
2198
- newTitle,
2199
- normalizedTitle,
2200
- currentThreadId: threadContextRef.current.currentThreadId,
2201
- targetThreadId,
2202
- hasMapping: sessionId !== targetThreadId,
2203
- creatingThreadId: backendState.creatingThreadId
2204
- });
2205
- }
2206
- threadContextRef.current.setThreadMetadata((prev) => {
2207
- var _a, _b;
2208
- const next = new Map(prev);
2209
- const existing = next.get(targetThreadId);
2210
- const nextStatus = (existing == null ? void 0 : existing.status) === "archived" ? "archived" : "regular";
2211
- next.set(targetThreadId, {
2212
- title: normalizedTitle,
2213
- status: nextStatus,
2214
- lastActiveAt: (_a = existing == null ? void 0 : existing.lastActiveAt) != null ? _a : (/* @__PURE__ */ new Date()).toISOString(),
2215
- control: (_b = existing == null ? void 0 : existing.control) != null ? _b : initThreadControl()
2216
- });
2217
- return next;
2218
- });
2219
- if (!isPlaceholderTitle(newTitle) && backendState.creatingThreadId === targetThreadId) {
2220
- backendState.creatingThreadId = null;
2221
- }
2222
- }
1758
+ const unsubscribe = eventContext.subscribe("title_changed", (event) => {
1759
+ const sessionId = event.sessionId;
1760
+ const payload = event.payload;
1761
+ const newTitle = payload == null ? void 0 : payload.new_title;
1762
+ if (typeof newTitle !== "string") return;
1763
+ const targetThreadId = resolveThreadId(backendState, sessionId);
1764
+ const normalizedTitle = isPlaceholderTitle(newTitle) ? "" : newTitle;
1765
+ if (process.env.NODE_ENV !== "production") {
1766
+ console.debug("[aomi][sse] title_changed", {
1767
+ sessionId,
1768
+ newTitle,
1769
+ normalizedTitle,
1770
+ currentThreadId: threadContextRef.current.currentThreadId,
1771
+ targetThreadId
1772
+ });
2223
1773
  }
2224
- );
2225
- return () => {
2226
- unsubscribe == null ? void 0 : unsubscribe();
2227
- };
2228
- }, [
2229
- backendApiRef,
2230
- backendStateRef,
2231
- threadContext.currentThreadId,
2232
- resolvedSessionId
2233
- ]);
2234
- useEffect3(() => {
2235
- const threadId = threadContext.currentThreadId;
2236
- if (!isThreadReady(backendStateRef.current, threadId)) return;
2237
- void messageController.flushPendingChat(threadId);
2238
- }, [messageController, backendStateRef, threadContext.currentThreadId]);
2239
- useEffect3(() => {
1774
+ threadContextRef.current.setThreadMetadata((prev) => {
1775
+ var _a, _b;
1776
+ const next = new Map(prev);
1777
+ const existing = next.get(targetThreadId);
1778
+ const nextStatus = (existing == null ? void 0 : existing.status) === "archived" ? "archived" : "regular";
1779
+ next.set(targetThreadId, {
1780
+ title: normalizedTitle,
1781
+ status: nextStatus,
1782
+ lastActiveAt: (_a = existing == null ? void 0 : existing.lastActiveAt) != null ? _a : (/* @__PURE__ */ new Date()).toISOString(),
1783
+ control: (_b = existing == null ? void 0 : existing.control) != null ? _b : initThreadControl()
1784
+ });
1785
+ return next;
1786
+ });
1787
+ });
1788
+ return unsubscribe;
1789
+ }, [eventContext, backendStateRef]);
1790
+ useEffect4(() => {
2240
1791
  const showToolNotification = (eventType) => (event) => {
2241
1792
  const payload = event.payload;
2242
1793
  const toolName = typeof (payload == null ? void 0 : payload.tool_name) === "string" ? payload.tool_name : void 0;
@@ -2261,7 +1812,7 @@ function AomiRuntimeCore({
2261
1812
  unsubscribeComplete();
2262
1813
  };
2263
1814
  }, [eventContext, notificationContext]);
2264
- useEffect3(() => {
1815
+ useEffect4(() => {
2265
1816
  const unsubscribe = eventContext.subscribe("system_notice", (event) => {
2266
1817
  const payload = event.payload;
2267
1818
  const message = payload == null ? void 0 : payload.message;
@@ -2277,13 +1828,13 @@ function AomiRuntimeCore({
2277
1828
  convertMessage: (msg) => msg,
2278
1829
  adapters: { threadList: threadListAdapter }
2279
1830
  });
2280
- useEffect3(() => {
1831
+ useEffect4(() => {
2281
1832
  return () => {
2282
1833
  polling.stopAll();
2283
1834
  };
2284
1835
  }, [polling]);
2285
1836
  const userContext = useUser();
2286
- const sendMessage = useCallback6(
1837
+ const sendMessage = useCallback7(
2287
1838
  async (text) => {
2288
1839
  const appendMessage = {
2289
1840
  role: "user",
@@ -2296,39 +1847,39 @@ function AomiRuntimeCore({
2296
1847
  },
2297
1848
  [messageController, threadContext.currentThreadId]
2298
1849
  );
2299
- const cancelGeneration = useCallback6(() => {
1850
+ const cancelGeneration = useCallback7(() => {
2300
1851
  messageController.cancel(threadContext.currentThreadId);
2301
1852
  }, [messageController, threadContext.currentThreadId]);
2302
- const getMessages = useCallback6(
1853
+ const getMessages = useCallback7(
2303
1854
  (threadId) => {
2304
1855
  const id = threadId != null ? threadId : threadContext.currentThreadId;
2305
1856
  return threadContext.getThreadMessages(id);
2306
1857
  },
2307
1858
  [threadContext]
2308
1859
  );
2309
- const createThread = useCallback6(async () => {
1860
+ const createThread = useCallback7(async () => {
2310
1861
  await threadListAdapter.onSwitchToNewThread();
2311
1862
  return threadContextRef.current.currentThreadId;
2312
1863
  }, [threadListAdapter]);
2313
- const deleteThread = useCallback6(
1864
+ const deleteThread = useCallback7(
2314
1865
  async (threadId) => {
2315
1866
  await threadListAdapter.onDelete(threadId);
2316
1867
  },
2317
1868
  [threadListAdapter]
2318
1869
  );
2319
- const renameThread = useCallback6(
1870
+ const renameThread = useCallback7(
2320
1871
  async (threadId, title) => {
2321
1872
  await threadListAdapter.onRename(threadId, title);
2322
1873
  },
2323
1874
  [threadListAdapter]
2324
1875
  );
2325
- const archiveThread = useCallback6(
1876
+ const archiveThread = useCallback7(
2326
1877
  async (threadId) => {
2327
1878
  await threadListAdapter.onArchive(threadId);
2328
1879
  },
2329
1880
  [threadListAdapter]
2330
1881
  );
2331
- const selectThread = useCallback6(
1882
+ const selectThread = useCallback7(
2332
1883
  (threadId) => {
2333
1884
  if (threadContext.allThreadsMetadata.has(threadId)) {
2334
1885
  threadListAdapter.onSwitchToThread(threadId);
@@ -2365,6 +1916,11 @@ function AomiRuntimeCore({
2365
1916
  showNotification: notificationContext.showNotification,
2366
1917
  dismissNotification: notificationContext.dismissNotification,
2367
1918
  clearAllNotifications: notificationContext.clearAll,
1919
+ // Wallet API
1920
+ pendingWalletRequests: walletHandler.pendingRequests,
1921
+ startWalletRequest: walletHandler.startProcessing,
1922
+ resolveWalletRequest: walletHandler.resolveRequest,
1923
+ rejectWalletRequest: walletHandler.rejectRequest,
2368
1924
  // Event API
2369
1925
  subscribe: eventContext.subscribe,
2370
1926
  sendSystemCommand: eventContext.sendOutboundSystem,
@@ -2386,6 +1942,7 @@ function AomiRuntimeCore({
2386
1942
  sendMessage,
2387
1943
  cancelGeneration,
2388
1944
  notificationContext,
1945
+ walletHandler,
2389
1946
  eventContext
2390
1947
  ]
2391
1948
  );
@@ -2398,12 +1955,12 @@ function AomiRuntimeProvider({
2398
1955
  children,
2399
1956
  backendUrl = "http://localhost:8080"
2400
1957
  }) {
2401
- const backendApi = useMemo3(() => new BackendApi(backendUrl), [backendUrl]);
2402
- return /* @__PURE__ */ jsx7(ThreadContextProvider, { children: /* @__PURE__ */ jsx7(NotificationContextProvider, { children: /* @__PURE__ */ jsx7(UserContextProvider, { children: /* @__PURE__ */ jsx7(AomiRuntimeInner, { backendApi, children }) }) }) });
1958
+ const aomiClient = useMemo3(() => new AomiClient({ baseUrl: backendUrl }), [backendUrl]);
1959
+ return /* @__PURE__ */ jsx7(ThreadContextProvider, { children: /* @__PURE__ */ jsx7(NotificationContextProvider, { children: /* @__PURE__ */ jsx7(UserContextProvider, { children: /* @__PURE__ */ jsx7(AomiRuntimeInner, { aomiClient, children }) }) }) });
2403
1960
  }
2404
1961
  function AomiRuntimeInner({
2405
1962
  children,
2406
- backendApi
1963
+ aomiClient
2407
1964
  }) {
2408
1965
  var _a;
2409
1966
  const threadContext = useThreadContext();
@@ -2411,7 +1968,7 @@ function AomiRuntimeInner({
2411
1968
  return /* @__PURE__ */ jsx7(
2412
1969
  ControlContextProvider,
2413
1970
  {
2414
- backendApi,
1971
+ aomiClient,
2415
1972
  sessionId: threadContext.currentThreadId,
2416
1973
  publicKey: (_a = user.address) != null ? _a : void 0,
2417
1974
  getThreadMetadata: threadContext.getThreadMetadata,
@@ -2419,94 +1976,15 @@ function AomiRuntimeInner({
2419
1976
  children: /* @__PURE__ */ jsx7(
2420
1977
  EventContextProvider,
2421
1978
  {
2422
- backendApi,
1979
+ aomiClient,
2423
1980
  sessionId: threadContext.currentThreadId,
2424
- children: /* @__PURE__ */ jsx7(AomiRuntimeCore, { backendApi, children })
1981
+ children: /* @__PURE__ */ jsx7(AomiRuntimeCore, { aomiClient, children })
2425
1982
  }
2426
1983
  )
2427
1984
  }
2428
1985
  );
2429
1986
  }
2430
1987
 
2431
- // packages/react/src/handlers/wallet-handler.ts
2432
- import { useCallback as useCallback7, useEffect as useEffect4, useState as useState6 } from "react";
2433
- function useWalletHandler({
2434
- sessionId,
2435
- onTxRequest
2436
- }) {
2437
- const { subscribe: subscribe2, sendOutboundSystem: sendOutbound } = useEventContext();
2438
- const { setUser, getUserState } = useUser();
2439
- const [pendingTxRequests, setPendingTxRequests] = useState6(
2440
- []
2441
- );
2442
- useEffect4(() => {
2443
- const unsubscribe = subscribe2(
2444
- "wallet_tx_request",
2445
- (event) => {
2446
- const request = event.payload;
2447
- setPendingTxRequests((prev) => [...prev, request]);
2448
- onTxRequest == null ? void 0 : onTxRequest(request);
2449
- }
2450
- );
2451
- return unsubscribe;
2452
- }, [subscribe2, onTxRequest]);
2453
- useEffect4(() => {
2454
- const unsubscribe = subscribe2(
2455
- "user_state_request",
2456
- (event) => {
2457
- sendOutbound({
2458
- type: "user_state_response",
2459
- sessionId,
2460
- payload: getUserState()
2461
- });
2462
- }
2463
- );
2464
- return unsubscribe;
2465
- }, [subscribe2, onTxRequest]);
2466
- const sendTxComplete = useCallback7(
2467
- (tx) => {
2468
- sendOutbound({
2469
- type: "wallet:tx_complete",
2470
- sessionId,
2471
- payload: tx
2472
- });
2473
- },
2474
- [sendOutbound, sessionId]
2475
- );
2476
- const sendConnectionChange = useCallback7(
2477
- (status, address, chainId) => {
2478
- if (status === "connected") {
2479
- setUser({
2480
- isConnected: true,
2481
- address,
2482
- chainId
2483
- });
2484
- } else {
2485
- setUser({
2486
- isConnected: false,
2487
- address: void 0,
2488
- chainId: void 0
2489
- });
2490
- }
2491
- sendOutbound({
2492
- type: status === "connected" ? "wallet:connected" : "wallet:disconnected",
2493
- sessionId,
2494
- payload: { status, address }
2495
- });
2496
- },
2497
- [setUser, sendOutbound, sessionId]
2498
- );
2499
- const clearTxRequest = useCallback7((index) => {
2500
- setPendingTxRequests((prev) => prev.filter((_, i) => i !== index));
2501
- }, []);
2502
- return {
2503
- sendTxComplete,
2504
- sendConnectionChange,
2505
- pendingTxRequests,
2506
- clearTxRequest
2507
- };
2508
- }
2509
-
2510
1988
  // packages/react/src/handlers/notification-handler.ts
2511
1989
  import { useCallback as useCallback8, useEffect as useEffect5, useState as useState7 } from "react";
2512
1990
  var notificationIdCounter2 = 0;
@@ -2549,8 +2027,8 @@ function useNotificationHandler({
2549
2027
  };
2550
2028
  }
2551
2029
  export {
2030
+ AomiClient2 as AomiClient,
2552
2031
  AomiRuntimeProvider,
2553
- BackendApi,
2554
2032
  ControlContextProvider,
2555
2033
  EventContextProvider,
2556
2034
  NotificationContextProvider,