@aomi-labs/react 0.3.2 → 0.3.4

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.d.cts CHANGED
@@ -16,10 +16,13 @@ type UserState = {
16
16
  chainId?: number;
17
17
  isConnected: boolean;
18
18
  ensName?: string;
19
+ ext?: Record<string, unknown>;
19
20
  };
20
21
  declare function useUser(): {
21
22
  user: UserState;
22
23
  setUser: (data: Partial<UserState>) => void;
24
+ addExtValue: (key: string, value: unknown) => void;
25
+ removeExtValue: (key: string) => void;
23
26
  getUserState: () => UserState;
24
27
  onUserStateChange: (callback: (user: UserState) => void) => () => void;
25
28
  };
@@ -166,6 +169,10 @@ type AomiRuntimeApi = {
166
169
  getUserState: () => UserState;
167
170
  /** Update user state (partial updates merged with existing state) */
168
171
  setUser: (data: Partial<UserState>) => void;
172
+ /** Add or overwrite a value in user_state.ext */
173
+ addExtValue: (key: string, value: unknown) => void;
174
+ /** Remove a value from user_state.ext */
175
+ removeExtValue: (key: string) => void;
169
176
  /** Subscribe to user state changes. Returns unsubscribe function. */
170
177
  onUserStateChange: (callback: (user: UserState) => void) => () => void;
171
178
  /** ID of the currently active thread */
@@ -318,6 +325,8 @@ declare const getChainInfo: (chainId: number | undefined) => ChainInfo | undefin
318
325
  type ControlState = {
319
326
  /** API key for authenticated requests */
320
327
  apiKey: string | null;
328
+ /** Stable client identifier for this browser tab (associates sessions with secrets) */
329
+ clientId: string | null;
321
330
  /** Available models fetched from backend */
322
331
  availableModels: string[];
323
332
  /** Authorized apps fetched from backend */
@@ -328,10 +337,14 @@ type ControlState = {
328
337
  defaultApp: string | null;
329
338
  };
330
339
  type ControlContextApi = {
331
- /** Global state (apiKey, available models/apps) */
340
+ /** Global state (apiKey, clientId, available models/apps) */
332
341
  state: ControlState;
333
342
  /** Update global state (apiKey only) */
334
343
  setApiKey: (apiKey: string | null) => void;
344
+ /** Ingest secrets into the backend vault, returns opaque handles */
345
+ ingestSecrets: (secrets: Record<string, string>) => Promise<Record<string, string>>;
346
+ /** Clear all secrets from the backend vault */
347
+ clearSecrets: () => Promise<void>;
335
348
  /** Fetch available models from backend */
336
349
  getAvailableModels: () => Promise<string[]>;
337
350
  /** Fetch authorized apps from backend */
package/dist/index.d.ts CHANGED
@@ -16,10 +16,13 @@ type UserState = {
16
16
  chainId?: number;
17
17
  isConnected: boolean;
18
18
  ensName?: string;
19
+ ext?: Record<string, unknown>;
19
20
  };
20
21
  declare function useUser(): {
21
22
  user: UserState;
22
23
  setUser: (data: Partial<UserState>) => void;
24
+ addExtValue: (key: string, value: unknown) => void;
25
+ removeExtValue: (key: string) => void;
23
26
  getUserState: () => UserState;
24
27
  onUserStateChange: (callback: (user: UserState) => void) => () => void;
25
28
  };
@@ -166,6 +169,10 @@ type AomiRuntimeApi = {
166
169
  getUserState: () => UserState;
167
170
  /** Update user state (partial updates merged with existing state) */
168
171
  setUser: (data: Partial<UserState>) => void;
172
+ /** Add or overwrite a value in user_state.ext */
173
+ addExtValue: (key: string, value: unknown) => void;
174
+ /** Remove a value from user_state.ext */
175
+ removeExtValue: (key: string) => void;
169
176
  /** Subscribe to user state changes. Returns unsubscribe function. */
170
177
  onUserStateChange: (callback: (user: UserState) => void) => () => void;
171
178
  /** ID of the currently active thread */
@@ -318,6 +325,8 @@ declare const getChainInfo: (chainId: number | undefined) => ChainInfo | undefin
318
325
  type ControlState = {
319
326
  /** API key for authenticated requests */
320
327
  apiKey: string | null;
328
+ /** Stable client identifier for this browser tab (associates sessions with secrets) */
329
+ clientId: string | null;
321
330
  /** Available models fetched from backend */
322
331
  availableModels: string[];
323
332
  /** Authorized apps fetched from backend */
@@ -328,10 +337,14 @@ type ControlState = {
328
337
  defaultApp: string | null;
329
338
  };
330
339
  type ControlContextApi = {
331
- /** Global state (apiKey, available models/apps) */
340
+ /** Global state (apiKey, clientId, available models/apps) */
332
341
  state: ControlState;
333
342
  /** Update global state (apiKey only) */
334
343
  setApiKey: (apiKey: string | null) => void;
344
+ /** Ingest secrets into the backend vault, returns opaque handles */
345
+ ingestSecrets: (secrets: Record<string, string>) => Promise<Record<string, string>>;
346
+ /** Clear all secrets from the backend vault */
347
+ clearSecrets: () => Promise<void>;
335
348
  /** Fetch available models from backend */
336
349
  getAvailableModels: () => Promise<string[]>;
337
350
  /** Fetch authorized apps from backend */
package/dist/index.js CHANGED
@@ -239,6 +239,7 @@ function ControlContextProvider({
239
239
  var _a, _b;
240
240
  const [state, setStateInternal] = useState(() => ({
241
241
  apiKey: null,
242
+ clientId: null,
242
243
  availableModels: [],
243
244
  authorizedApps: [],
244
245
  defaultModel: null,
@@ -259,6 +260,11 @@ function ControlContextProvider({
259
260
  const callbacks = useRef(/* @__PURE__ */ new Set());
260
261
  const currentThreadMetadata = getThreadMetadata(sessionId);
261
262
  const isProcessing = (_b = (_a = currentThreadMetadata == null ? void 0 : currentThreadMetadata.control) == null ? void 0 : _a.isProcessing) != null ? _b : false;
263
+ useEffect(() => {
264
+ var _a2, _b2, _c;
265
+ const clientId = (_c = (_b2 = (_a2 = globalThis.crypto) == null ? void 0 : _a2.randomUUID) == null ? void 0 : _b2.call(_a2)) != null ? _c : `client-${Date.now()}`;
266
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), { clientId }));
267
+ }, []);
262
268
  useEffect(() => {
263
269
  var _a2, _b2;
264
270
  try {
@@ -332,6 +338,24 @@ function ControlContextProvider({
332
338
  return next;
333
339
  });
334
340
  }, []);
341
+ const ingestSecrets = useCallback(
342
+ async (secrets) => {
343
+ const clientId = stateRef.current.clientId;
344
+ if (!clientId) throw new Error("clientId not initialized");
345
+ const { handles } = await aomiClientRef.current.ingestSecrets(
346
+ clientId,
347
+ secrets
348
+ );
349
+ return handles;
350
+ },
351
+ []
352
+ );
353
+ const clearSecrets = useCallback(async () => {
354
+ var _a2, _b2;
355
+ const clientId = stateRef.current.clientId;
356
+ if (!clientId) return;
357
+ await ((_b2 = (_a2 = aomiClientRef.current).clearSecrets) == null ? void 0 : _b2.call(_a2, clientId));
358
+ }, []);
335
359
  const getAvailableModels = useCallback(async () => {
336
360
  try {
337
361
  const models = await aomiClientRef.current.getModels(
@@ -512,6 +536,8 @@ function ControlContextProvider({
512
536
  value: {
513
537
  state,
514
538
  setApiKey,
539
+ ingestSecrets,
540
+ clearSecrets,
515
541
  getAvailableModels,
516
542
  getAuthorizedApps,
517
543
  getCurrentThreadControl,
@@ -832,6 +858,8 @@ function useUser() {
832
858
  return {
833
859
  user: context.user,
834
860
  setUser: context.setUser,
861
+ addExtValue: context.addExtValue,
862
+ removeExtValue: context.removeExtValue,
835
863
  getUserState: context.getUserState,
836
864
  onUserStateChange: context.onUserStateChange
837
865
  };
@@ -841,7 +869,8 @@ function UserContextProvider({ children }) {
841
869
  isConnected: false,
842
870
  address: void 0,
843
871
  chainId: void 0,
844
- ensName: void 0
872
+ ensName: void 0,
873
+ ext: void 0
845
874
  });
846
875
  const userRef = useRef4(user);
847
876
  userRef.current = user;
@@ -857,6 +886,36 @@ function UserContextProvider({ children }) {
857
886
  return next;
858
887
  });
859
888
  }, []);
889
+ const addExtValue = useCallback4((key, value) => {
890
+ setUserState((prev) => {
891
+ var _a;
892
+ const next = __spreadProps(__spreadValues({}, prev), {
893
+ ext: __spreadProps(__spreadValues({}, (_a = prev.ext) != null ? _a : {}), {
894
+ [key]: value
895
+ })
896
+ });
897
+ StateChangeCallbacks.current.forEach((callback) => {
898
+ callback(next);
899
+ });
900
+ return next;
901
+ });
902
+ }, []);
903
+ const removeExtValue = useCallback4((key) => {
904
+ setUserState((prev) => {
905
+ if (!prev.ext || !(key in prev.ext)) {
906
+ return prev;
907
+ }
908
+ const nextExt = __spreadValues({}, prev.ext);
909
+ delete nextExt[key];
910
+ const next = __spreadProps(__spreadValues({}, prev), {
911
+ ext: Object.keys(nextExt).length > 0 ? nextExt : void 0
912
+ });
913
+ StateChangeCallbacks.current.forEach((callback) => {
914
+ callback(next);
915
+ });
916
+ return next;
917
+ });
918
+ }, []);
860
919
  const getUserState = useCallback4(() => userRef.current, []);
861
920
  const onUserStateChange = useCallback4(
862
921
  (callback) => {
@@ -873,6 +932,8 @@ function UserContextProvider({ children }) {
873
932
  value: {
874
933
  user,
875
934
  setUser,
935
+ addExtValue,
936
+ removeExtValue,
876
937
  getUserState,
877
938
  onUserStateChange
878
939
  },
@@ -1030,7 +1091,7 @@ var MessageController = class {
1030
1091
  this.getThreadContextApi().setThreadMessages(threadId, threadMessages);
1031
1092
  }
1032
1093
  async outbound(message, threadId) {
1033
- var _a, _b, _c, _d, _e, _f, _g, _h;
1094
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1034
1095
  const backendState = this.config.backendStateRef.current;
1035
1096
  const text = message.content.filter(
1036
1097
  (part) => part.type === "text"
@@ -1053,18 +1114,19 @@ var MessageController = class {
1053
1114
  const app = this.config.getApp();
1054
1115
  const publicKey = (_b = (_a = this.config).getPublicKey) == null ? void 0 : _b.call(_a);
1055
1116
  const apiKey = (_e = (_d = (_c = this.config).getApiKey) == null ? void 0 : _d.call(_c)) != null ? _e : void 0;
1056
- const userState = (_g = (_f = this.config).getUserState) == null ? void 0 : _g.call(_f);
1117
+ const clientId = (_g = (_f = this.config).getClientId) == null ? void 0 : _g.call(_f);
1118
+ const userState = (_i = (_h = this.config).getUserState) == null ? void 0 : _i.call(_h);
1057
1119
  try {
1058
1120
  this.markRunning(threadId, true);
1059
1121
  const response = await this.config.aomiClientRef.current.sendMessage(
1060
1122
  backendThreadId,
1061
1123
  text,
1062
- { app, publicKey, apiKey, userState }
1124
+ { app, publicKey, apiKey, userState, clientId }
1063
1125
  );
1064
1126
  if (response == null ? void 0 : response.messages) {
1065
1127
  this.inbound(threadId, response.messages);
1066
1128
  }
1067
- if (((_h = response == null ? void 0 : response.system_events) == null ? void 0 : _h.length) && this.config.onSyncEvents) {
1129
+ if (((_j = response == null ? void 0 : response.system_events) == null ? void 0 : _j.length) && this.config.onSyncEvents) {
1068
1130
  this.config.onSyncEvents(backendThreadId, response.system_events);
1069
1131
  }
1070
1132
  if (response == null ? void 0 : response.is_processing) {
@@ -1123,7 +1185,7 @@ var PollingController = class {
1123
1185
  const backendThreadId = resolveThreadId(backendState, threadId);
1124
1186
  setThreadRunning(backendState, threadId, true);
1125
1187
  const tick = async () => {
1126
- var _a2, _b2;
1188
+ var _a2, _b2, _c, _d;
1127
1189
  if (!this.intervals.has(threadId)) return;
1128
1190
  try {
1129
1191
  console.log(
@@ -1131,9 +1193,11 @@ var PollingController = class {
1131
1193
  threadId
1132
1194
  );
1133
1195
  const userState = (_b2 = (_a2 = this.config).getUserState) == null ? void 0 : _b2.call(_a2);
1196
+ const clientId = (_d = (_c = this.config).getClientId) == null ? void 0 : _d.call(_c);
1134
1197
  const state = await this.config.aomiClientRef.current.fetchState(
1135
1198
  backendThreadId,
1136
- userState
1199
+ userState,
1200
+ clientId
1137
1201
  );
1138
1202
  if (!this.intervals.has(threadId)) return;
1139
1203
  this.handleState(threadId, state);
@@ -1200,6 +1264,7 @@ function useRuntimeOrchestrator(aomiClient, options) {
1200
1264
  },
1201
1265
  onSyncEvents: options.onSyncEvents,
1202
1266
  getUserState: options.getUserState,
1267
+ getClientId: options.getClientId,
1203
1268
  onStart: (threadId) => {
1204
1269
  if (threadContextRef.current.currentThreadId === threadId) {
1205
1270
  setIsRunning(true);
@@ -1222,29 +1287,32 @@ function useRuntimeOrchestrator(aomiClient, options) {
1222
1287
  getPublicKey: options.getPublicKey,
1223
1288
  getApp: options.getApp,
1224
1289
  getApiKey: options.getApiKey,
1290
+ getClientId: options.getClientId,
1225
1291
  getUserState: options.getUserState,
1226
1292
  onSyncEvents: options.onSyncEvents
1227
1293
  });
1228
1294
  }
1229
1295
  const ensureInitialState = useCallback5(async (threadId) => {
1230
- var _a, _b, _c, _d;
1296
+ var _a, _b, _c, _d, _e;
1231
1297
  if (pendingFetches.current.has(threadId)) return;
1232
1298
  const backendThreadId = resolveThreadId(backendStateRef.current, threadId);
1233
1299
  pendingFetches.current.add(threadId);
1234
1300
  try {
1235
1301
  const userState = (_a = options.getUserState) == null ? void 0 : _a.call(options);
1302
+ const clientId = (_b = options.getClientId) == null ? void 0 : _b.call(options);
1236
1303
  const state = await aomiClientRef.current.fetchState(
1237
1304
  backendThreadId,
1238
- userState
1305
+ userState,
1306
+ clientId
1239
1307
  );
1240
- (_b = messageControllerRef.current) == null ? void 0 : _b.inbound(threadId, state.messages);
1241
- if (((_c = state.system_events) == null ? void 0 : _c.length) && options.onSyncEvents) {
1308
+ (_c = messageControllerRef.current) == null ? void 0 : _c.inbound(threadId, state.messages);
1309
+ if (((_d = state.system_events) == null ? void 0 : _d.length) && options.onSyncEvents) {
1242
1310
  options.onSyncEvents(backendThreadId, state.system_events);
1243
1311
  }
1244
1312
  if (threadContextRef.current.currentThreadId === threadId) {
1245
1313
  if (state.is_processing) {
1246
1314
  setIsRunning(true);
1247
- (_d = pollingRef.current) == null ? void 0 : _d.start(threadId);
1315
+ (_e = pollingRef.current) == null ? void 0 : _e.start(threadId);
1248
1316
  } else {
1249
1317
  setIsRunning(false);
1250
1318
  }
@@ -1580,7 +1648,7 @@ function AomiRuntimeCore({
1580
1648
  const notificationContext = useNotification();
1581
1649
  const { dispatchInboundSystem: dispatchSystemEvents } = eventContext;
1582
1650
  const { user, onUserStateChange, getUserState } = useUser();
1583
- const { getControlState, getCurrentThreadApp } = useControl();
1651
+ const { getControlState, getCurrentThreadApp, clearSecrets } = useControl();
1584
1652
  const {
1585
1653
  backendStateRef,
1586
1654
  polling,
@@ -1594,24 +1662,46 @@ function AomiRuntimeCore({
1594
1662
  getPublicKey: () => getUserState().address,
1595
1663
  getUserState,
1596
1664
  getApp: getCurrentThreadApp,
1597
- getApiKey: () => getControlState().apiKey
1665
+ getApiKey: () => getControlState().apiKey,
1666
+ getClientId: () => {
1667
+ var _a;
1668
+ return (_a = getControlState().clientId) != null ? _a : void 0;
1669
+ }
1598
1670
  });
1671
+ const walletSnapshot = useCallback7(
1672
+ (nextUser) => ({
1673
+ address: nextUser.address,
1674
+ chainId: nextUser.chainId,
1675
+ isConnected: nextUser.isConnected,
1676
+ ensName: nextUser.ensName
1677
+ }),
1678
+ [getUserState]
1679
+ );
1680
+ const lastWalletStateRef = useRef7(walletSnapshot(getUserState()));
1599
1681
  useEffect4(() => {
1682
+ lastWalletStateRef.current = walletSnapshot(getUserState());
1600
1683
  const unsubscribe = onUserStateChange(async (newUser) => {
1684
+ const nextWalletState = walletSnapshot(newUser);
1685
+ const prevWalletState = lastWalletStateRef.current;
1686
+ if (prevWalletState.address === nextWalletState.address && prevWalletState.chainId === nextWalletState.chainId && prevWalletState.isConnected === nextWalletState.isConnected && prevWalletState.ensName === nextWalletState.ensName) {
1687
+ return;
1688
+ }
1689
+ lastWalletStateRef.current = nextWalletState;
1601
1690
  const sessionId = threadContext.currentThreadId;
1602
1691
  const message = JSON.stringify({
1603
1692
  type: "wallet:state_changed",
1604
- payload: {
1605
- address: newUser.address,
1606
- chainId: newUser.chainId,
1607
- isConnected: newUser.isConnected,
1608
- ensName: newUser.ensName
1609
- }
1693
+ payload: nextWalletState
1610
1694
  });
1611
1695
  await aomiClientRef.current.sendSystemMessage(sessionId, message);
1612
1696
  });
1613
1697
  return unsubscribe;
1614
- }, [onUserStateChange, aomiClientRef, threadContext.currentThreadId]);
1698
+ }, [
1699
+ onUserStateChange,
1700
+ aomiClientRef,
1701
+ threadContext.currentThreadId,
1702
+ getUserState,
1703
+ walletSnapshot
1704
+ ]);
1615
1705
  const threadContextRef = useRef7(threadContext);
1616
1706
  threadContextRef.current = threadContext;
1617
1707
  const currentThreadIdRef = useRef7(threadContext.currentThreadId);
@@ -1803,8 +1893,9 @@ function AomiRuntimeCore({
1803
1893
  useEffect4(() => {
1804
1894
  return () => {
1805
1895
  polling.stopAll();
1896
+ void clearSecrets();
1806
1897
  };
1807
- }, [polling]);
1898
+ }, [polling, clearSecrets]);
1808
1899
  const userContext = useUser();
1809
1900
  const sendMessage = useCallback7(
1810
1901
  async (text) => {
@@ -1867,6 +1958,8 @@ function AomiRuntimeCore({
1867
1958
  user: userContext.user,
1868
1959
  getUserState: userContext.getUserState,
1869
1960
  setUser: userContext.setUser,
1961
+ addExtValue: userContext.addExtValue,
1962
+ removeExtValue: userContext.removeExtValue,
1870
1963
  onUserStateChange: userContext.onUserStateChange,
1871
1964
  // Thread API
1872
1965
  currentThreadId: threadContext.currentThreadId,