@aomi-labs/react 0.3.18 → 0.3.19

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 CHANGED
@@ -50,34 +50,34 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
50
50
  // packages/react/src/index.ts
51
51
  var index_exports = {};
52
52
  __export(index_exports, {
53
- AomiClient: () => import_client7.AomiClient,
53
+ AomiClient: () => import_client8.AomiClient,
54
54
  AomiRuntimeProvider: () => AomiRuntimeProvider,
55
55
  ControlContextProvider: () => ControlContextProvider,
56
- DISABLED_PROVIDER_STATE: () => import_client8.DISABLED_PROVIDER_STATE,
56
+ DISABLED_PROVIDER_STATE: () => import_client9.DISABLED_PROVIDER_STATE,
57
57
  EventContextProvider: () => EventContextProvider,
58
- MAX_AUTO_FEE_WEI: () => import_client8.MAX_AUTO_FEE_WEI,
58
+ MAX_AUTO_FEE_WEI: () => import_client9.MAX_AUTO_FEE_WEI,
59
59
  NotificationContextProvider: () => NotificationContextProvider,
60
60
  RuntimeUserStateProvider: () => RuntimeUserStateProvider,
61
61
  SUPPORTED_CHAINS: () => SUPPORTED_CHAINS,
62
62
  ThreadContextProvider: () => ThreadContextProvider,
63
63
  UserContextProvider: () => UserContextProvider,
64
64
  UserState: () => import_client2.UserState,
65
- aaModeFromExecutionKind: () => import_client8.aaModeFromExecutionKind,
66
- appendFeeCallToPayload: () => import_client8.appendFeeCallToPayload,
67
- buildFeeAAWalletCall: () => import_client8.buildFeeAAWalletCall,
65
+ aaModeFromExecutionKind: () => import_client9.aaModeFromExecutionKind,
66
+ appendFeeCallToPayload: () => import_client9.appendFeeCallToPayload,
67
+ buildFeeAAWalletCall: () => import_client9.buildFeeAAWalletCall,
68
68
  cn: () => cn,
69
- executeWalletCalls: () => import_client8.executeWalletCalls,
69
+ executeWalletCalls: () => import_client9.executeWalletCalls,
70
70
  formatAddress: () => formatAddress,
71
71
  getChainInfo: () => getChainInfo,
72
72
  getNetworkName: () => getNetworkName,
73
- hydrateTxPayloadFromUserState: () => import_client8.hydrateTxPayloadFromUserState,
73
+ hydrateTxPayloadFromUserState: () => import_client9.hydrateTxPayloadFromUserState,
74
74
  initThreadControl: () => initThreadControl,
75
- normalizeSimulatedFee: () => import_client8.normalizeSimulatedFee,
76
- parseChainId: () => import_client8.parseChainId,
75
+ normalizeSimulatedFee: () => import_client9.normalizeSimulatedFee,
76
+ parseChainId: () => import_client9.parseChainId,
77
77
  resolveAutoModel: () => resolveAutoModel,
78
- toAAWalletCall: () => import_client8.toAAWalletCall,
79
- toAAWalletCalls: () => import_client8.toAAWalletCalls,
80
- toViemSignTypedDataArgs: () => import_client8.toViemSignTypedDataArgs,
78
+ toAAWalletCall: () => import_client9.toAAWalletCall,
79
+ toAAWalletCalls: () => import_client9.toAAWalletCalls,
80
+ toViemSignTypedDataArgs: () => import_client9.toViemSignTypedDataArgs,
81
81
  useAomiRuntime: () => useAomiRuntime,
82
82
  useControl: () => useControl,
83
83
  useCurrentThreadMessages: () => useCurrentThreadMessages,
@@ -90,12 +90,12 @@ __export(index_exports, {
90
90
  useWalletHandler: () => useWalletHandler
91
91
  });
92
92
  module.exports = __toCommonJS(index_exports);
93
- var import_client7 = require("@aomi-labs/client");
94
93
  var import_client8 = require("@aomi-labs/client");
94
+ var import_client9 = require("@aomi-labs/client");
95
95
 
96
96
  // packages/react/src/runtime/aomi-runtime.tsx
97
97
  var import_react12 = require("react");
98
- var import_client6 = require("@aomi-labs/client");
98
+ var import_client7 = require("@aomi-labs/client");
99
99
 
100
100
  // packages/react/src/contexts/control-context.tsx
101
101
  var import_react = require("react");
@@ -212,6 +212,7 @@ var ThreadStore = class {
212
212
  };
213
213
  this.snapshot = this.buildSnapshot();
214
214
  this.emit();
215
+ return threadId;
215
216
  };
216
217
  this.updateThreadMetadata = (threadId, updates) => {
217
218
  const existing = this.state.threadMetadata.get(threadId);
@@ -313,13 +314,9 @@ function resolveAutoModel(models) {
313
314
  return (_a = models[0]) != null ? _a : null;
314
315
  }
315
316
 
316
- // packages/react/src/contexts/control-context.tsx
317
- var import_jsx_runtime = require("react/jsx-runtime");
318
- var API_KEY_STORAGE_KEY = "aomi_api_key";
317
+ // packages/react/src/utils/client-session.ts
319
318
  var CLIENT_ID_STORAGE_KEY = "aomi_client_id";
320
- var PROVIDER_KEYS_STORAGE_KEY = "aomi_provider_keys";
321
- var MODEL_SELECTION_STORAGE_KEY = "aomi_model_selection";
322
- var PROVIDER_KEY_SECRET_PREFIX = "PROVIDER_KEY:";
319
+ var CONTROL_SESSION_PREFIX = "control:";
323
320
  function getOrCreateClientId() {
324
321
  var _a, _b, _c, _d, _e;
325
322
  try {
@@ -338,6 +335,17 @@ function getOrCreateClientId() {
338
335
  }
339
336
  return clientId;
340
337
  }
338
+ function getControlSessionId(clientId, fallbackSessionId) {
339
+ const trimmedClientId = clientId == null ? void 0 : clientId.trim();
340
+ return trimmedClientId ? `${CONTROL_SESSION_PREFIX}${trimmedClientId}` : fallbackSessionId;
341
+ }
342
+
343
+ // packages/react/src/contexts/control-context.tsx
344
+ var import_jsx_runtime = require("react/jsx-runtime");
345
+ var API_KEY_STORAGE_KEY = "aomi_api_key";
346
+ var PROVIDER_KEYS_STORAGE_KEY = "aomi_provider_keys";
347
+ var MODEL_SELECTION_STORAGE_KEY = "aomi_model_selection";
348
+ var PROVIDER_KEY_SECRET_PREFIX = "PROVIDER_KEY:";
341
349
  function getDefaultApp(apps) {
342
350
  var _a;
343
351
  return apps.includes("default") ? "default" : (_a = apps[0]) != null ? _a : null;
@@ -430,6 +438,10 @@ function ControlContextProvider({
430
438
  const updateThreadMetadataRef = (0, import_react.useRef)(updateThreadMetadata);
431
439
  updateThreadMetadataRef.current = updateThreadMetadata;
432
440
  const callbacks = (0, import_react.useRef)(/* @__PURE__ */ new Set());
441
+ const getCurrentControlSessionId = (0, import_react.useCallback)(
442
+ () => getControlSessionId(stateRef.current.clientId, sessionIdRef.current),
443
+ []
444
+ );
433
445
  const currentThreadMetadata = getThreadMetadata(sessionId);
434
446
  const isProcessing = (_b = (_a = currentThreadMetadata == null ? void 0 : currentThreadMetadata.control) == null ? void 0 : _a.isProcessing) != null ? _b : false;
435
447
  (0, import_react.useEffect)(() => {
@@ -496,18 +508,21 @@ function ControlContextProvider({
496
508
  for (const [provider, entry] of Object.entries(keys)) {
497
509
  secrets[`${PROVIDER_KEY_SECRET_PREFIX}${provider}`] = entry.apiKey;
498
510
  }
499
- void aomiClientRef.current.ingestSecrets(state.clientId, secrets).catch((err) => {
511
+ void aomiClientRef.current.ingestSecrets(getCurrentControlSessionId(), state.clientId, secrets).catch((err) => {
500
512
  console.error("Failed to auto-ingest provider keys:", err);
501
513
  });
502
- }, [state.clientId, state.providerKeys]);
514
+ }, [getCurrentControlSessionId, state.clientId, state.providerKeys]);
503
515
  (0, import_react.useEffect)(() => {
504
516
  const fetchApps = async () => {
505
517
  var _a2;
506
518
  try {
507
- const apps = await aomiClientRef.current.getApps(sessionIdRef.current, {
508
- publicKey: publicKeyRef.current,
509
- apiKey: (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
510
- });
519
+ const apps = await aomiClientRef.current.getApps(
520
+ getCurrentControlSessionId(),
521
+ {
522
+ publicKey: publicKeyRef.current,
523
+ apiKey: (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
524
+ }
525
+ );
511
526
  const defaultApp = getDefaultApp(apps);
512
527
  setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
513
528
  authorizedApps: apps,
@@ -522,12 +537,12 @@ function ControlContextProvider({
522
537
  }
523
538
  };
524
539
  void fetchApps();
525
- }, [state.apiKey, publicKey]);
540
+ }, [getCurrentControlSessionId, state.apiKey, publicKey]);
526
541
  (0, import_react.useEffect)(() => {
527
542
  const fetchModels = async () => {
528
543
  try {
529
544
  const models = await aomiClientRef.current.getModels(
530
- sessionIdRef.current
545
+ getCurrentControlSessionId()
531
546
  );
532
547
  setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
533
548
  availableModels: models,
@@ -538,7 +553,7 @@ function ControlContextProvider({
538
553
  }
539
554
  };
540
555
  void fetchModels();
541
- }, []);
556
+ }, [getCurrentControlSessionId]);
542
557
  const setApiKey = (0, import_react.useCallback)((apiKey) => {
543
558
  setStateInternal((prev) => {
544
559
  const next = __spreadProps(__spreadValues({}, prev), { apiKey: apiKey === "" ? null : apiKey });
@@ -551,19 +566,24 @@ function ControlContextProvider({
551
566
  const clientId = stateRef.current.clientId;
552
567
  if (!clientId) throw new Error("clientId not initialized");
553
568
  const { handles } = await aomiClientRef.current.ingestSecrets(
569
+ getCurrentControlSessionId(),
554
570
  clientId,
555
571
  secrets
556
572
  );
557
573
  return handles;
558
574
  },
559
- []
575
+ [getCurrentControlSessionId]
560
576
  );
561
577
  const clearSecrets = (0, import_react.useCallback)(async () => {
562
578
  var _a2, _b2;
563
579
  const clientId = stateRef.current.clientId;
564
580
  if (!clientId) return;
565
- await ((_b2 = (_a2 = aomiClientRef.current).clearSecrets) == null ? void 0 : _b2.call(_a2, clientId));
566
- }, []);
581
+ await ((_b2 = (_a2 = aomiClientRef.current).clearSecrets) == null ? void 0 : _b2.call(
582
+ _a2,
583
+ getCurrentControlSessionId(),
584
+ clientId
585
+ ));
586
+ }, [getCurrentControlSessionId]);
567
587
  const setProviderKey = (0, import_react.useCallback)(
568
588
  async (provider, apiKey, label) => {
569
589
  const trimmed = apiKey.trim();
@@ -583,21 +603,26 @@ function ControlContextProvider({
583
603
  const clientId = stateRef.current.clientId;
584
604
  if (clientId) {
585
605
  try {
586
- await aomiClientRef.current.ingestSecrets(clientId, {
587
- [`${PROVIDER_KEY_SECRET_PREFIX}${provider}`]: trimmed
588
- });
606
+ await aomiClientRef.current.ingestSecrets(
607
+ getCurrentControlSessionId(),
608
+ clientId,
609
+ {
610
+ [`${PROVIDER_KEY_SECRET_PREFIX}${provider}`]: trimmed
611
+ }
612
+ );
589
613
  } catch (err) {
590
614
  console.error("Failed to ingest provider key:", err);
591
615
  }
592
616
  }
593
617
  },
594
- []
618
+ [getCurrentControlSessionId]
595
619
  );
596
620
  const removeProviderKey = (0, import_react.useCallback)(
597
621
  async (provider) => {
598
622
  const clientId = stateRef.current.clientId;
599
623
  if (clientId) {
600
624
  await aomiClientRef.current.deleteSecret(
625
+ getCurrentControlSessionId(),
601
626
  clientId,
602
627
  `${PROVIDER_KEY_SECRET_PREFIX}${provider}`
603
628
  );
@@ -609,7 +634,7 @@ function ControlContextProvider({
609
634
  return next;
610
635
  });
611
636
  },
612
- []
637
+ [getCurrentControlSessionId]
613
638
  );
614
639
  const getProviderKeys = (0, import_react.useCallback)(
615
640
  () => stateRef.current.providerKeys,
@@ -623,7 +648,7 @@ function ControlContextProvider({
623
648
  const getAvailableModels = (0, import_react.useCallback)(async () => {
624
649
  try {
625
650
  const models = await aomiClientRef.current.getModels(
626
- sessionIdRef.current
651
+ getCurrentControlSessionId()
627
652
  );
628
653
  setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
629
654
  availableModels: models,
@@ -634,14 +659,17 @@ function ControlContextProvider({
634
659
  console.error("Failed to fetch models:", error);
635
660
  return [];
636
661
  }
637
- }, []);
662
+ }, [getCurrentControlSessionId]);
638
663
  const getAuthorizedApps = (0, import_react.useCallback)(async () => {
639
664
  var _a2;
640
665
  try {
641
- const apps = await aomiClientRef.current.getApps(sessionIdRef.current, {
642
- publicKey: publicKeyRef.current,
643
- apiKey: (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
644
- });
666
+ const apps = await aomiClientRef.current.getApps(
667
+ getCurrentControlSessionId(),
668
+ {
669
+ publicKey: publicKeyRef.current,
670
+ apiKey: (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
671
+ }
672
+ );
645
673
  const defaultApp = getDefaultApp(apps);
646
674
  setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
647
675
  authorizedApps: apps,
@@ -656,7 +684,7 @@ function ControlContextProvider({
656
684
  }));
657
685
  return ["default"];
658
686
  }
659
- }, []);
687
+ }, [getCurrentControlSessionId]);
660
688
  const getCurrentThreadControl = (0, import_react.useCallback)(() => {
661
689
  var _a2;
662
690
  const metadata = getThreadMetadataRef.current(sessionIdRef.current);
@@ -1205,7 +1233,7 @@ function UserContextProvider({ children }) {
1205
1233
  // packages/react/src/runtime/core.tsx
1206
1234
  var import_react10 = require("react");
1207
1235
  var import_react11 = require("@assistant-ui/react");
1208
- var import_client5 = require("@aomi-labs/client");
1236
+ var import_client6 = require("@aomi-labs/client");
1209
1237
 
1210
1238
  // packages/react/src/runtime/orchestrator.ts
1211
1239
  var import_react6 = require("react");
@@ -1557,6 +1585,7 @@ function useRuntimeOrchestrator(aomiClient, options) {
1557
1585
  cleanups.push(forwardEvent("system_notice"));
1558
1586
  cleanups.push(forwardEvent("system_error"));
1559
1587
  cleanups.push(forwardEvent("async_callback"));
1588
+ cleanups.push(forwardEvent("user_state_request"));
1560
1589
  listenerCleanups.current.set(threadId, () => {
1561
1590
  for (const cleanup of cleanups) cleanup();
1562
1591
  });
@@ -1965,39 +1994,8 @@ function useWalletHandler({
1965
1994
 
1966
1995
  // packages/react/src/runtime/user-state-provider.tsx
1967
1996
  var import_react9 = require("react");
1997
+ var import_client5 = require("@aomi-labs/client");
1968
1998
  var import_jsx_runtime6 = require("react/jsx-runtime");
1969
- function stableStateString(state) {
1970
- return JSON.stringify(state != null ? state : {});
1971
- }
1972
- function RuntimeUserStateProvider({
1973
- children,
1974
- sessionManager,
1975
- getUserState,
1976
- onUserStateChange
1977
- }) {
1978
- const lastSerializedStateRef = (0, import_react9.useRef)("");
1979
- (0, import_react9.useEffect)(() => {
1980
- const applyToSessions = (next) => {
1981
- const serialized = stableStateString(next);
1982
- if (serialized === lastSerializedStateRef.current) {
1983
- return;
1984
- }
1985
- lastSerializedStateRef.current = serialized;
1986
- sessionManager.forEach((session) => {
1987
- session.resolveUserState(next);
1988
- });
1989
- };
1990
- applyToSessions(getUserState());
1991
- const unsubscribe = onUserStateChange((next) => {
1992
- applyToSessions(next);
1993
- });
1994
- return unsubscribe;
1995
- }, [getUserState, onUserStateChange, sessionManager]);
1996
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children });
1997
- }
1998
-
1999
- // packages/react/src/runtime/core.tsx
2000
- var import_jsx_runtime7 = require("react/jsx-runtime");
2001
1999
  var THREAD_PREFETCH_LIMIT = 5;
2002
2000
  var PREFETCH_IDLE_TIMEOUT_MS = 1500;
2003
2001
  function scheduleBackgroundTask(task) {
@@ -2014,65 +2012,14 @@ function scheduleBackgroundTask(task) {
2014
2012
  const timeoutId = runtimeGlobal.setTimeout(task, 0);
2015
2013
  return () => runtimeGlobal.clearTimeout(timeoutId);
2016
2014
  }
2017
- function AomiRuntimeCore({
2018
- children,
2019
- aomiClient
2020
- }) {
2021
- const threadContext = useThreadContext();
2022
- const eventContext = useEventContext();
2023
- const notificationContext = useNotification();
2024
- const { user, onUserStateChange, getUserState } = useUser();
2025
- const {
2026
- getControlState,
2027
- getCurrentThreadApp,
2028
- getPreferredThreadControl,
2029
- syncCurrentThreadControl
2030
- } = useControl();
2031
- const sessionManagerRef = (0, import_react10.useRef)(null);
2032
- const walletHandler = useWalletHandler({
2033
- getSession: () => {
2034
- var _a;
2035
- return (_a = sessionManagerRef.current) == null ? void 0 : _a.get(threadContext.currentThreadId);
2036
- }
2037
- });
2038
- const {
2039
- sessionManager,
2040
- getSession,
2041
- isRunning,
2042
- setIsRunning,
2043
- ensureInitialState,
2044
- sendMessage: orchestratorSendMessage,
2045
- cancelGeneration: orchestratorCancel,
2046
- closeSession,
2047
- closeIdleSessionsExcept,
2048
- closeAllSessions,
2049
- aomiClientRef
2050
- } = useRuntimeOrchestrator(aomiClient, {
2051
- getPublicKey: () => import_client5.UserState.isConnected(getUserState()) ? import_client5.UserState.address(getUserState()) : void 0,
2052
- getUserState,
2053
- getApp: getCurrentThreadApp,
2054
- getApiKey: () => getControlState().apiKey,
2055
- getClientId: () => {
2056
- var _a;
2057
- return (_a = getControlState().clientId) != null ? _a : void 0;
2058
- },
2059
- prepareThreadForSend: async (threadId) => {
2060
- await ensureBackendThread(threadId);
2061
- await syncCurrentThreadControl();
2062
- },
2063
- onPendingRequestsChange: walletHandler.setRequests,
2064
- onEvent: (event) => eventContext.dispatch(event)
2065
- });
2066
- sessionManagerRef.current = sessionManager;
2067
- const threadContextRef = (0, import_react10.useRef)(threadContext);
2068
- threadContextRef.current = threadContext;
2069
- const remoteThreadIdsRef = (0, import_react10.useRef)(/* @__PURE__ */ new Set());
2070
- const warmedThreadIdsRef = (0, import_react10.useRef)(/* @__PURE__ */ new Set());
2071
- const warmPromisesRef = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
2072
- const prefetchCancelRef = (0, import_react10.useRef)(null);
2073
- const [isThreadLoading, setIsThreadLoading] = (0, import_react10.useState)(false);
2074
- const [isThreadListLoading, setIsThreadListLoading] = (0, import_react10.useState)(true);
2075
- const walletSnapshot = (0, import_react10.useCallback)(
2015
+ function stableStateString(state) {
2016
+ return JSON.stringify(state != null ? state : {});
2017
+ }
2018
+ function useWalletStateSync(context, sessions, remoteThreads) {
2019
+ const { getUserState, onUserStateChange, threadContextRef } = context;
2020
+ const { aomiClientRef } = sessions;
2021
+ const { remoteThreadIdsRef } = remoteThreads;
2022
+ const walletSnapshot = (0, import_react9.useCallback)(
2076
2023
  (nextUser) => {
2077
2024
  var _a;
2078
2025
  return {
@@ -2084,17 +2031,23 @@ function AomiRuntimeCore({
2084
2031
  },
2085
2032
  [getUserState]
2086
2033
  );
2087
- const lastWalletStateRef = (0, import_react10.useRef)(walletSnapshot(getUserState()));
2088
- (0, import_react10.useEffect)(() => {
2034
+ const lastWalletStateRef = (0, import_react9.useRef)(walletSnapshot(getUserState()));
2035
+ (0, import_react9.useEffect)(() => {
2089
2036
  lastWalletStateRef.current = walletSnapshot(getUserState());
2090
2037
  const unsubscribe = onUserStateChange(async (newUser) => {
2038
+ var _a, _b;
2091
2039
  const nextWalletState = walletSnapshot(newUser);
2092
2040
  const prevWalletState = lastWalletStateRef.current;
2041
+ const previousAddress = (_a = prevWalletState.address) == null ? void 0 : _a.toLowerCase();
2042
+ const nextAddress = (_b = nextWalletState.address) == null ? void 0 : _b.toLowerCase();
2093
2043
  if (prevWalletState.address === nextWalletState.address && prevWalletState.chain_id === nextWalletState.chain_id && prevWalletState.is_connected === nextWalletState.is_connected && prevWalletState.ens_name === nextWalletState.ens_name) {
2094
2044
  return;
2095
2045
  }
2096
2046
  lastWalletStateRef.current = nextWalletState;
2097
- const sessionId = threadContext.currentThreadId;
2047
+ if (previousAddress !== void 0 && nextAddress !== void 0 && previousAddress !== nextAddress) {
2048
+ return;
2049
+ }
2050
+ const sessionId = threadContextRef.current.currentThreadId;
2098
2051
  if (!remoteThreadIdsRef.current.has(sessionId)) {
2099
2052
  return;
2100
2053
  }
@@ -2106,39 +2059,56 @@ function AomiRuntimeCore({
2106
2059
  });
2107
2060
  return unsubscribe;
2108
2061
  }, [
2109
- onUserStateChange,
2110
2062
  aomiClientRef,
2111
- threadContext.currentThreadId,
2112
2063
  getUserState,
2064
+ onUserStateChange,
2065
+ remoteThreadIdsRef,
2066
+ threadContextRef,
2113
2067
  walletSnapshot
2114
2068
  ]);
2115
- const warmThread = (0, import_react10.useCallback)(
2116
- async (threadId) => {
2117
- if (!remoteThreadIdsRef.current.has(threadId) || warmedThreadIdsRef.current.has(threadId)) {
2118
- return;
2119
- }
2120
- const existingPromise = warmPromisesRef.current.get(threadId);
2121
- if (existingPromise) {
2122
- return existingPromise;
2123
- }
2124
- const warmPromise = (async () => {
2125
- const userState = getUserState();
2126
- await aomiClientRef.current.createThread(
2127
- threadId,
2128
- import_client5.UserState.isConnected(userState) ? import_client5.UserState.address(userState) : void 0
2129
- );
2130
- warmedThreadIdsRef.current.add(threadId);
2131
- })();
2132
- warmPromisesRef.current.set(threadId, warmPromise);
2133
- try {
2134
- await warmPromise;
2135
- } finally {
2136
- warmPromisesRef.current.delete(threadId);
2137
- }
2138
- },
2139
- [aomiClientRef, getUserState]
2140
- );
2141
- const scheduleThreadPrefetch = (0, import_react10.useCallback)(
2069
+ }
2070
+ function useUserStateRequestResponder(context, sessions) {
2071
+ const eventContext = useEventContext();
2072
+ const { getUserState, threadContextRef } = context;
2073
+ const { getSession } = sessions;
2074
+ (0, import_react9.useEffect)(() => {
2075
+ const unsubscribe = eventContext.subscribe("user_state_request", () => {
2076
+ var _a, _b;
2077
+ const sessionId = threadContextRef.current.currentThreadId;
2078
+ const session = getSession(sessionId);
2079
+ const payload = (_b = (_a = import_client5.UserState.reconcile(session.getUserState(), getUserState())) != null ? _a : session.getUserState()) != null ? _b : getUserState();
2080
+ eventContext.sendOutboundSystem({
2081
+ type: "user_state_response",
2082
+ sessionId,
2083
+ payload
2084
+ });
2085
+ });
2086
+ return unsubscribe;
2087
+ }, [eventContext, getSession, getUserState, threadContextRef]);
2088
+ }
2089
+ function useRemoteThreadListSync(context, sessions, remoteThreads) {
2090
+ const [isThreadListLoading, setIsThreadListLoading] = (0, import_react9.useState)(true);
2091
+ const prefetchCancelRef = (0, import_react9.useRef)(null);
2092
+ const lastConnectedAddressRef = (0, import_react9.useRef)(void 0);
2093
+ const {
2094
+ getControlState,
2095
+ threadContextRef,
2096
+ user
2097
+ } = context;
2098
+ const {
2099
+ aomiClientRef,
2100
+ closeAllSessions,
2101
+ ensureInitialState,
2102
+ sessionManager,
2103
+ setIsThreadLoading
2104
+ } = sessions;
2105
+ const {
2106
+ remoteThreadIdsRef,
2107
+ warmPromisesRef,
2108
+ warmedThreadIdsRef,
2109
+ warmThread
2110
+ } = remoteThreads;
2111
+ const scheduleThreadPrefetch = (0, import_react9.useCallback)(
2142
2112
  (threadIds) => {
2143
2113
  var _a;
2144
2114
  (_a = prefetchCancelRef.current) == null ? void 0 : _a.call(prefetchCancelRef);
@@ -2157,8 +2127,9 @@ function AomiRuntimeCore({
2157
2127
  }
2158
2128
  try {
2159
2129
  await warmThread(threadId);
2160
- if (cancelled || !remoteThreadIdsRef.current.has(threadId))
2130
+ if (cancelled || !remoteThreadIdsRef.current.has(threadId)) {
2161
2131
  return;
2132
+ }
2162
2133
  await ensureInitialState(threadId);
2163
2134
  } catch (error) {
2164
2135
  console.debug("Failed to prefetch thread:", threadId, error);
@@ -2171,81 +2142,16 @@ function AomiRuntimeCore({
2171
2142
  cancelScheduledTask();
2172
2143
  };
2173
2144
  },
2174
- [ensureInitialState, warmThread]
2175
- );
2176
- (0, import_react10.useEffect)(() => {
2177
- const unsubscribe = eventContext.subscribe("user_state_request", () => {
2178
- var _a, _b, _c;
2179
- const session = (_b = (_a = sessionManagerRef.current) == null ? void 0 : _a.get(threadContext.currentThreadId)) != null ? _b : getSession(threadContext.currentThreadId);
2180
- eventContext.sendOutboundSystem({
2181
- type: "user_state_response",
2182
- sessionId: threadContext.currentThreadId,
2183
- payload: (_c = session.getUserState()) != null ? _c : getUserState()
2184
- });
2185
- });
2186
- return unsubscribe;
2187
- }, [eventContext, threadContext.currentThreadId, getSession, getUserState]);
2188
- const ensureBackendThread = (0, import_react10.useCallback)(
2189
- async (threadId) => {
2190
- if (remoteThreadIdsRef.current.has(threadId)) return;
2191
- const userState = getUserState();
2192
- await aomiClientRef.current.createThread(
2193
- threadId,
2194
- import_client5.UserState.isConnected(userState) ? import_client5.UserState.address(userState) : void 0
2195
- );
2196
- remoteThreadIdsRef.current.add(threadId);
2197
- warmedThreadIdsRef.current.add(threadId);
2198
- },
2199
- [aomiClientRef, getUserState]
2200
- );
2201
- (0, import_react10.useEffect)(() => {
2202
- const threadId = threadContext.currentThreadId;
2203
- closeIdleSessionsExcept(threadId);
2204
- if (!remoteThreadIdsRef.current.has(threadId)) {
2205
- setIsThreadLoading(false);
2206
- return;
2207
- }
2208
- let cancelled = false;
2209
- setIsThreadLoading(true);
2210
- void (async () => {
2211
- try {
2212
- await warmThread(threadId);
2213
- if (!cancelled) {
2214
- await ensureInitialState(threadId);
2215
- }
2216
- } finally {
2217
- if (!cancelled) {
2218
- setIsThreadLoading(false);
2219
- }
2220
- }
2221
- })();
2222
- return () => {
2223
- cancelled = true;
2224
- };
2225
- }, [
2226
- closeIdleSessionsExcept,
2227
- ensureInitialState,
2228
- threadContext.currentThreadId,
2229
- warmThread
2230
- ]);
2231
- (0, import_react10.useEffect)(() => {
2232
- const threadId = threadContext.currentThreadId;
2233
- const currentMeta = threadContext.getThreadMetadata(threadId);
2234
- if (currentMeta && currentMeta.control.isProcessing !== isRunning) {
2235
- threadContext.updateThreadMetadata(threadId, {
2236
- control: __spreadProps(__spreadValues({}, currentMeta.control), {
2237
- isProcessing: isRunning
2238
- })
2239
- });
2240
- }
2241
- }, [isRunning, threadContext]);
2242
- const currentMessages = threadContext.getThreadMessages(
2243
- threadContext.currentThreadId
2145
+ [ensureInitialState, remoteThreadIdsRef, threadContextRef, warmThread]
2244
2146
  );
2245
- (0, import_react10.useEffect)(() => {
2246
- var _a;
2147
+ (0, import_react9.useEffect)(() => {
2148
+ var _a, _b;
2247
2149
  const userAddress = import_client5.UserState.isConnected(user) ? import_client5.UserState.address(user) : void 0;
2150
+ const normalizedUserAddress = userAddress == null ? void 0 : userAddress.toLowerCase();
2151
+ const previousAddress = lastConnectedAddressRef.current;
2152
+ const walletChanged = previousAddress !== void 0 && normalizedUserAddress !== void 0 && previousAddress !== normalizedUserAddress;
2248
2153
  if (!userAddress) {
2154
+ lastConnectedAddressRef.current = void 0;
2249
2155
  const hadRemoteThreads = remoteThreadIdsRef.current.size > 0;
2250
2156
  const hadSessions = sessionManager.size > 0;
2251
2157
  setIsThreadListLoading(false);
@@ -2260,23 +2166,46 @@ function AomiRuntimeCore({
2260
2166
  }
2261
2167
  return;
2262
2168
  }
2169
+ lastConnectedAddressRef.current = normalizedUserAddress;
2170
+ const resetThreadId = walletChanged ? threadContextRef.current.resetToDefault() : void 0;
2171
+ if (walletChanged) {
2172
+ (_b = prefetchCancelRef.current) == null ? void 0 : _b.call(prefetchCancelRef);
2173
+ prefetchCancelRef.current = null;
2174
+ remoteThreadIdsRef.current.clear();
2175
+ warmedThreadIdsRef.current.clear();
2176
+ warmPromisesRef.current.clear();
2177
+ closeAllSessions();
2178
+ }
2263
2179
  let cancelled = false;
2264
2180
  setIsThreadListLoading(true);
2265
2181
  const fetchThreadList = async () => {
2266
- var _a2, _b, _c;
2182
+ var _a2, _b2, _c;
2267
2183
  try {
2268
2184
  const remoteThreadIdsAtFetchStart = new Set(remoteThreadIdsRef.current);
2269
- const threadList = await aomiClientRef.current.listThreads(userAddress);
2270
- if (cancelled) return;
2271
2185
  const currentContext = threadContextRef.current;
2186
+ const controlSessionId = getControlSessionId(
2187
+ getControlState().clientId,
2188
+ resetThreadId != null ? resetThreadId : currentContext.currentThreadId
2189
+ );
2190
+ const threadList = await aomiClientRef.current.listThreads(
2191
+ controlSessionId,
2192
+ userAddress
2193
+ );
2194
+ if (cancelled) return;
2272
2195
  const remoteThreadIds = /* @__PURE__ */ new Set();
2273
- const newMetadata = new Map(currentContext.allThreadsMetadata);
2274
- let maxChatNum = currentContext.threadCnt;
2196
+ const newMetadata = resetThreadId !== void 0 ? new Map(
2197
+ (() => {
2198
+ const resetMetadata = threadContextRef.current.getThreadMetadata(resetThreadId);
2199
+ return resetMetadata ? [[resetThreadId, resetMetadata]] : [];
2200
+ })()
2201
+ ) : new Map(currentContext.allThreadsMetadata);
2202
+ const baseThreadCount = resetThreadId !== void 0 ? 1 : currentContext.threadCnt;
2203
+ let maxChatNum = baseThreadCount;
2275
2204
  for (const thread of threadList) {
2276
2205
  remoteThreadIds.add(thread.session_id);
2277
2206
  const rawTitle = (_a2 = thread.title) != null ? _a2 : "";
2278
2207
  const title = isPlaceholderTitle(rawTitle) ? "" : rawTitle;
2279
- const lastActive = ((_b = newMetadata.get(thread.session_id)) == null ? void 0 : _b.lastActiveAt) || (/* @__PURE__ */ new Date()).toISOString();
2208
+ const lastActive = ((_b2 = newMetadata.get(thread.session_id)) == null ? void 0 : _b2.lastActiveAt) || (/* @__PURE__ */ new Date()).toISOString();
2280
2209
  const existingControl = (_c = newMetadata.get(thread.session_id)) == null ? void 0 : _c.control;
2281
2210
  newMetadata.set(thread.session_id, {
2282
2211
  title,
@@ -2304,16 +2233,17 @@ function AomiRuntimeCore({
2304
2233
  )
2305
2234
  );
2306
2235
  currentContext.setThreadMetadata(newMetadata);
2307
- if (maxChatNum > currentContext.threadCnt) {
2236
+ if (maxChatNum > baseThreadCount) {
2308
2237
  currentContext.setThreadCnt(maxChatNum);
2309
2238
  }
2310
2239
  scheduleThreadPrefetch(threadList.map((thread) => thread.session_id));
2311
- if (remoteThreadIds.has(currentContext.currentThreadId)) {
2240
+ const activeThreadId = threadContextRef.current.currentThreadId;
2241
+ if (remoteThreadIds.has(activeThreadId)) {
2312
2242
  setIsThreadLoading(true);
2313
2243
  try {
2314
- await warmThread(currentContext.currentThreadId);
2244
+ await warmThread(activeThreadId);
2315
2245
  if (!cancelled) {
2316
- await ensureInitialState(currentContext.currentThreadId);
2246
+ await ensureInitialState(activeThreadId);
2317
2247
  }
2318
2248
  } finally {
2319
2249
  if (!cancelled) {
@@ -2337,12 +2267,248 @@ function AomiRuntimeCore({
2337
2267
  prefetchCancelRef.current = null;
2338
2268
  };
2339
2269
  }, [
2340
- user,
2341
2270
  aomiClientRef,
2271
+ closeAllSessions,
2342
2272
  ensureInitialState,
2273
+ getControlState,
2274
+ remoteThreadIdsRef,
2343
2275
  scheduleThreadPrefetch,
2276
+ sessionManager,
2277
+ setIsThreadLoading,
2278
+ threadContextRef,
2279
+ user,
2280
+ warmPromisesRef,
2281
+ warmedThreadIdsRef,
2344
2282
  warmThread
2345
2283
  ]);
2284
+ return { isThreadListLoading };
2285
+ }
2286
+ function useRuntimeUserStateEffects({
2287
+ sessions: {
2288
+ aomiClientRef,
2289
+ sessionManager,
2290
+ getSession,
2291
+ closeAllSessions,
2292
+ ensureInitialState,
2293
+ setIsThreadLoading
2294
+ },
2295
+ remoteThreads
2296
+ }) {
2297
+ const threadContext = useThreadContext();
2298
+ const { user, getUserState, onUserStateChange } = useUser();
2299
+ const { getControlState } = useControl();
2300
+ const threadContextRef = (0, import_react9.useRef)(threadContext);
2301
+ threadContextRef.current = threadContext;
2302
+ const context = {
2303
+ getControlState,
2304
+ getUserState,
2305
+ onUserStateChange,
2306
+ threadContextRef,
2307
+ user
2308
+ };
2309
+ const sessions = {
2310
+ aomiClientRef,
2311
+ sessionManager,
2312
+ getSession,
2313
+ closeAllSessions,
2314
+ ensureInitialState,
2315
+ setIsThreadLoading
2316
+ };
2317
+ useWalletStateSync(context, sessions, remoteThreads);
2318
+ useUserStateRequestResponder(context, sessions);
2319
+ return useRemoteThreadListSync(context, sessions, remoteThreads);
2320
+ }
2321
+ function RuntimeUserStateProvider({
2322
+ children,
2323
+ sessionManager,
2324
+ getUserState,
2325
+ onUserStateChange
2326
+ }) {
2327
+ const lastSerializedStateRef = (0, import_react9.useRef)("");
2328
+ (0, import_react9.useEffect)(() => {
2329
+ const applyToSessions = (next) => {
2330
+ const serialized = stableStateString(next);
2331
+ if (serialized === lastSerializedStateRef.current) {
2332
+ return;
2333
+ }
2334
+ lastSerializedStateRef.current = serialized;
2335
+ sessionManager.forEach((session) => {
2336
+ session.resolveUserState(next);
2337
+ });
2338
+ };
2339
+ applyToSessions(getUserState());
2340
+ const unsubscribe = onUserStateChange((next) => {
2341
+ applyToSessions(next);
2342
+ });
2343
+ return unsubscribe;
2344
+ }, [getUserState, onUserStateChange, sessionManager]);
2345
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children });
2346
+ }
2347
+
2348
+ // packages/react/src/runtime/core.tsx
2349
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2350
+ function AomiRuntimeCore({
2351
+ children,
2352
+ aomiClient
2353
+ }) {
2354
+ const threadContext = useThreadContext();
2355
+ const eventContext = useEventContext();
2356
+ const notificationContext = useNotification();
2357
+ const { getUserState } = useUser();
2358
+ const {
2359
+ getControlState,
2360
+ getCurrentThreadApp,
2361
+ getPreferredThreadControl,
2362
+ syncCurrentThreadControl
2363
+ } = useControl();
2364
+ const sessionManagerRef = (0, import_react10.useRef)(null);
2365
+ const walletHandler = useWalletHandler({
2366
+ getSession: () => {
2367
+ var _a;
2368
+ return (_a = sessionManagerRef.current) == null ? void 0 : _a.get(threadContext.currentThreadId);
2369
+ }
2370
+ });
2371
+ const {
2372
+ sessionManager,
2373
+ getSession,
2374
+ isRunning,
2375
+ setIsRunning,
2376
+ ensureInitialState,
2377
+ sendMessage: orchestratorSendMessage,
2378
+ cancelGeneration: orchestratorCancel,
2379
+ closeSession,
2380
+ closeIdleSessionsExcept,
2381
+ closeAllSessions,
2382
+ aomiClientRef
2383
+ } = useRuntimeOrchestrator(aomiClient, {
2384
+ getPublicKey: () => import_client6.UserState.isConnected(getUserState()) ? import_client6.UserState.address(getUserState()) : void 0,
2385
+ getUserState,
2386
+ getApp: getCurrentThreadApp,
2387
+ getApiKey: () => getControlState().apiKey,
2388
+ getClientId: () => {
2389
+ var _a;
2390
+ return (_a = getControlState().clientId) != null ? _a : void 0;
2391
+ },
2392
+ prepareThreadForSend: async (threadId) => {
2393
+ await ensureBackendThread(threadId);
2394
+ await syncCurrentThreadControl();
2395
+ },
2396
+ onPendingRequestsChange: walletHandler.setRequests,
2397
+ onEvent: (event) => eventContext.dispatch(event)
2398
+ });
2399
+ sessionManagerRef.current = sessionManager;
2400
+ const threadContextRef = (0, import_react10.useRef)(threadContext);
2401
+ threadContextRef.current = threadContext;
2402
+ const remoteThreadIdsRef = (0, import_react10.useRef)(/* @__PURE__ */ new Set());
2403
+ const warmedThreadIdsRef = (0, import_react10.useRef)(/* @__PURE__ */ new Set());
2404
+ const warmPromisesRef = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
2405
+ const [isThreadLoading, setIsThreadLoading] = (0, import_react10.useState)(false);
2406
+ const warmThread = (0, import_react10.useCallback)(
2407
+ async (threadId) => {
2408
+ if (!remoteThreadIdsRef.current.has(threadId) || warmedThreadIdsRef.current.has(threadId)) {
2409
+ return;
2410
+ }
2411
+ const existingPromise = warmPromisesRef.current.get(threadId);
2412
+ if (existingPromise) {
2413
+ return existingPromise;
2414
+ }
2415
+ const warmPromise = (async () => {
2416
+ const userState = getUserState();
2417
+ await aomiClientRef.current.createThread(
2418
+ threadId,
2419
+ import_client6.UserState.isConnected(userState) ? import_client6.UserState.address(userState) : void 0
2420
+ );
2421
+ warmedThreadIdsRef.current.add(threadId);
2422
+ })();
2423
+ warmPromisesRef.current.set(threadId, warmPromise);
2424
+ try {
2425
+ await warmPromise;
2426
+ } finally {
2427
+ warmPromisesRef.current.delete(threadId);
2428
+ }
2429
+ },
2430
+ [aomiClientRef, getUserState]
2431
+ );
2432
+ const ensureBackendThread = (0, import_react10.useCallback)(
2433
+ async (threadId) => {
2434
+ if (remoteThreadIdsRef.current.has(threadId)) return;
2435
+ const userState = getUserState();
2436
+ await aomiClientRef.current.createThread(
2437
+ threadId,
2438
+ import_client6.UserState.isConnected(userState) ? import_client6.UserState.address(userState) : void 0
2439
+ );
2440
+ remoteThreadIdsRef.current.add(threadId);
2441
+ warmedThreadIdsRef.current.add(threadId);
2442
+ },
2443
+ [aomiClientRef, getUserState]
2444
+ );
2445
+ const getRuntimeSession = (0, import_react10.useCallback)(
2446
+ (threadId) => {
2447
+ var _a, _b;
2448
+ return (_b = (_a = sessionManagerRef.current) == null ? void 0 : _a.get(threadId)) != null ? _b : getSession(threadId);
2449
+ },
2450
+ [getSession]
2451
+ );
2452
+ const { isThreadListLoading } = useRuntimeUserStateEffects({
2453
+ sessions: {
2454
+ aomiClientRef,
2455
+ sessionManager,
2456
+ getSession: getRuntimeSession,
2457
+ closeAllSessions,
2458
+ ensureInitialState,
2459
+ setIsThreadLoading
2460
+ },
2461
+ remoteThreads: {
2462
+ remoteThreadIdsRef,
2463
+ warmPromisesRef,
2464
+ warmedThreadIdsRef,
2465
+ warmThread
2466
+ }
2467
+ });
2468
+ (0, import_react10.useEffect)(() => {
2469
+ const threadId = threadContext.currentThreadId;
2470
+ closeIdleSessionsExcept(threadId);
2471
+ if (!remoteThreadIdsRef.current.has(threadId)) {
2472
+ setIsThreadLoading(false);
2473
+ return;
2474
+ }
2475
+ let cancelled = false;
2476
+ setIsThreadLoading(true);
2477
+ void (async () => {
2478
+ try {
2479
+ await warmThread(threadId);
2480
+ if (!cancelled) {
2481
+ await ensureInitialState(threadId);
2482
+ }
2483
+ } finally {
2484
+ if (!cancelled) {
2485
+ setIsThreadLoading(false);
2486
+ }
2487
+ }
2488
+ })();
2489
+ return () => {
2490
+ cancelled = true;
2491
+ };
2492
+ }, [
2493
+ closeIdleSessionsExcept,
2494
+ ensureInitialState,
2495
+ threadContext.currentThreadId,
2496
+ warmThread
2497
+ ]);
2498
+ (0, import_react10.useEffect)(() => {
2499
+ const threadId = threadContext.currentThreadId;
2500
+ const currentMeta = threadContext.getThreadMetadata(threadId);
2501
+ if (currentMeta && currentMeta.control.isProcessing !== isRunning) {
2502
+ threadContext.updateThreadMetadata(threadId, {
2503
+ control: __spreadProps(__spreadValues({}, currentMeta.control), {
2504
+ isProcessing: isRunning
2505
+ })
2506
+ });
2507
+ }
2508
+ }, [isRunning, threadContext]);
2509
+ const currentMessages = threadContext.getThreadMessages(
2510
+ threadContext.currentThreadId
2511
+ );
2346
2512
  const isRemoteThread = (0, import_react10.useCallback)(
2347
2513
  (threadId) => remoteThreadIdsRef.current.has(threadId),
2348
2514
  []
@@ -2419,8 +2585,6 @@ function AomiRuntimeCore({
2419
2585
  });
2420
2586
  (0, import_react10.useEffect)(() => {
2421
2587
  return () => {
2422
- var _a;
2423
- (_a = prefetchCancelRef.current) == null ? void 0 : _a.call(prefetchCancelRef);
2424
2588
  closeAllSessions();
2425
2589
  };
2426
2590
  }, [closeAllSessions]);
@@ -2570,7 +2734,7 @@ function AomiRuntimeProvider({
2570
2734
  clientOptions
2571
2735
  }) {
2572
2736
  const aomiClient = (0, import_react12.useMemo)(
2573
- () => new import_client6.AomiClient(__spreadValues({ baseUrl: backendUrl }, clientOptions)),
2737
+ () => new import_client7.AomiClient(__spreadValues({ baseUrl: backendUrl }, clientOptions)),
2574
2738
  [backendUrl, clientOptions]
2575
2739
  );
2576
2740
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ThreadContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NotificationContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(UserContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AomiRuntimeInner, { aomiClient, children }) }) }) });
@@ -2587,7 +2751,7 @@ function AomiRuntimeInner({
2587
2751
  {
2588
2752
  aomiClient,
2589
2753
  sessionId: threadContext.currentThreadId,
2590
- publicKey: import_client6.UserState.isConnected(user) ? (_a = import_client6.UserState.address(user)) != null ? _a : void 0 : void 0,
2754
+ publicKey: import_client7.UserState.isConnected(user) ? (_a = import_client7.UserState.address(user)) != null ? _a : void 0 : void 0,
2591
2755
  getThreadMetadata: threadContext.getThreadMetadata,
2592
2756
  updateThreadMetadata: threadContext.updateThreadMetadata,
2593
2757
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(