@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.4.3 → 0.4.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.
@@ -0,0 +1,9 @@
1
+ export declare class AuthorizationLoadingState {
2
+ private aitLoading;
3
+ private autoConnecting;
4
+ constructor(aitLoading?: boolean, autoConnecting?: boolean);
5
+ setAITLoading(value: boolean): void;
6
+ setAutoConnecting(value: boolean): void;
7
+ isLoading(): boolean;
8
+ }
9
+ //# sourceMappingURL=authorizationLoadingState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorizationLoadingState.d.ts","sourceRoot":"","sources":["../src/authorizationLoadingState.ts"],"names":[],"mappings":"AAAA,qBAAa,yBAAyB;IACpC,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,cAAc,CAAU;gBAEpB,UAAU,UAAQ,EAAE,cAAc,UAAQ;IAKtD,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAInC,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAIvC,SAAS,IAAI,OAAO;CAGrB"}
@@ -0,0 +1,15 @@
1
+ export class AuthorizationLoadingState {
2
+ constructor(aitLoading = false, autoConnecting = false) {
3
+ this.aitLoading = aitLoading;
4
+ this.autoConnecting = autoConnecting;
5
+ }
6
+ setAITLoading(value) {
7
+ this.aitLoading = value;
8
+ }
9
+ setAutoConnecting(value) {
10
+ this.autoConnecting = value;
11
+ }
12
+ isLoading() {
13
+ return this.aitLoading || this.autoConnecting;
14
+ }
15
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ChatBotContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatBotContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA8B/B,OAAO,EAEL,kBAAkB,EAClB,YAAY,EAEb,MAAM,uBAAuB,CAAC;AAO/B,eAAO,MAAM,UAAU,0BAMtB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAo8FlD,CAAC"}
1
+ {"version":3,"file":"ChatBotContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatBotContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA8B/B,OAAO,EAEL,kBAAkB,EAClB,YAAY,EAEb,MAAM,uBAAuB,CAAC;AAQ/B,eAAO,MAAM,UAAU,0BAMtB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAw+FlD,CAAC"}
@@ -6,6 +6,7 @@ import { flushSync } from 'react-dom';
6
6
  import { v4 as uuidv4 } from 'uuid';
7
7
  import { createNxtlinqApi, setApiHosts, synthesizeSpeechToBuffer, streamSpeechToAudioContext, useLocalStorage, useSessionStorage, useSpeechToTextFromMic, useVoiceMode, metakeepClient, getEthers, sleep, walletTextUtils, } from '@bytexbyte/nxtlinq-ai-agent-web-development';
8
8
  import { authorizeTextFrontendTool, hasRecordedWalletVerification, validateWalletSession, } from '@bytexbyte/nxtlinq-ai-agent-core-development';
9
+ import { AuthorizationLoadingState } from '../authorizationLoadingState';
9
10
  import { PendingTextToolRetryController } from '../pendingTextToolRetry';
10
11
  const MIC_ENABLED_SESSION_KEY = 'chatbot-mic-enabled';
11
12
  const ChatBotContext = React.createContext(undefined);
@@ -145,6 +146,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
145
146
  const permissionsRef = React.useRef(permissions);
146
147
  const nxtlinqAITServiceAccessTokenRef = React.useRef(nxtlinqAITServiceAccessToken);
147
148
  const signerRef = React.useRef(signer);
149
+ const authorizationLoadingStateRef = React.useRef(new AuthorizationLoadingState(isAITLoading, isAutoConnecting));
148
150
  const pendingTextToolRetryRef = React.useRef(new PendingTextToolRetryController());
149
151
  const sendMessageRef = React.useRef(async () => { });
150
152
  const retryPendingTextTool = React.useCallback(() => {
@@ -483,6 +485,12 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
483
485
  React.useEffect(() => {
484
486
  permissionsRef.current = permissions;
485
487
  }, [permissions]);
488
+ React.useEffect(() => {
489
+ authorizationLoadingStateRef.current.setAITLoading(isAITLoading);
490
+ }, [isAITLoading]);
491
+ React.useEffect(() => {
492
+ authorizationLoadingStateRef.current.setAutoConnecting(isAutoConnecting);
493
+ }, [isAutoConnecting]);
486
494
  React.useEffect(() => {
487
495
  nxtlinqAITServiceAccessTokenRef.current = nxtlinqAITServiceAccessToken;
488
496
  }, [nxtlinqAITServiceAccessToken]);
@@ -875,12 +883,16 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
875
883
  const currentHitAddress = hitAddressRef.current || hitAddress;
876
884
  const currentToken = nxtlinqAITServiceAccessTokenRef.current || nxtlinqAITServiceAccessToken;
877
885
  if (!currentHitAddress) {
886
+ aitRef.current = null;
887
+ permissionsRef.current = [];
878
888
  setAit(null);
879
889
  setPermissions([]);
880
890
  setWalletInfo(null);
891
+ authorizationLoadingStateRef.current.setAITLoading(false);
881
892
  setIsAITLoading(false);
882
893
  return;
883
894
  }
895
+ authorizationLoadingStateRef.current.setAITLoading(true);
884
896
  setIsAITLoading(true);
885
897
  try {
886
898
  let walletAllowsAITLookup = !requireWalletIDVVerification;
@@ -910,6 +922,8 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
910
922
  // AIT existence is not identity verification. In strict mode, stop before
911
923
  // the legacy AIT lookup can synthesize a custom wallet record.
912
924
  if (currentToken && !walletAllowsAITLookup) {
925
+ aitRef.current = null;
926
+ permissionsRef.current = [];
913
927
  setAit(null);
914
928
  setPermissions([]);
915
929
  return;
@@ -923,28 +937,39 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
923
937
  }, currentToken);
924
938
  if ('error' in response) {
925
939
  console.error('Failed to fetch AIT:', response.error);
940
+ aitRef.current = null;
941
+ permissionsRef.current = [];
926
942
  setAit(null);
927
943
  setPermissions([]);
928
944
  return;
929
945
  }
946
+ aitRef.current = response;
930
947
  setAit(response);
931
948
  if (!isPermissionFormOpen || forceUpdatePermissions) {
932
949
  const newPermissions = response.metadata?.permissions || [];
950
+ permissionsRef.current = newPermissions;
933
951
  setPermissions(newPermissions);
934
952
  }
935
953
  }
936
954
  else {
937
955
  // No token available, clear AIT data
956
+ aitRef.current = null;
957
+ permissionsRef.current = [];
938
958
  setAit(null);
939
959
  setPermissions([]);
940
960
  }
941
961
  }
942
962
  catch (error) {
943
963
  console.error('Failed to fetch AIT:', error);
964
+ aitRef.current = null;
965
+ permissionsRef.current = [];
944
966
  setAit(null);
945
967
  setPermissions([]);
946
968
  }
947
969
  finally {
970
+ // Keep the authorization snapshot current before refreshAIT resolves.
971
+ // React may not commit setState until the immediate pending-tool retry.
972
+ authorizationLoadingStateRef.current.setAITLoading(false);
948
973
  setIsAITLoading(false);
949
974
  }
950
975
  };
@@ -1158,7 +1183,9 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
1158
1183
  // wallet-address subject until issuance/migration supports it.
1159
1184
  externalId: undefined,
1160
1185
  requireWalletIDVVerification,
1161
- loading: isAITLoading || isAutoConnecting,
1186
+ // Use synchronously maintained refs so a retry immediately following
1187
+ // refreshAIT() does not observe the previous React render's loading state.
1188
+ loading: authorizationLoadingStateRef.current.isLoading(),
1162
1189
  },
1163
1190
  ...authFields(),
1164
1191
  customUsername: (!requireWalletIDVVerification && customUsername)
@@ -2223,8 +2250,11 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2223
2250
  metadataHash,
2224
2251
  metadataCid,
2225
2252
  };
2253
+ const nextPermissions = newPermissions || permissions;
2254
+ aitRef.current = aitInfo;
2255
+ permissionsRef.current = nextPermissions;
2226
2256
  setAit(aitInfo);
2227
- setPermissions(newPermissions || permissions);
2257
+ setPermissions(nextPermissions);
2228
2258
  };
2229
2259
  // Auto enable AIT permission
2230
2260
  const enableAIT = async (toolName) => {
@@ -2394,6 +2424,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2394
2424
  customUsername: (!requireWalletIDVVerification && customUsername) ? getFinalCustomUsername(customUsername) : undefined
2395
2425
  }, token);
2396
2426
  if (!('error' in aitResponse)) {
2427
+ aitRef.current = aitResponse;
2397
2428
  setAit(aitResponse);
2398
2429
  }
2399
2430
  }
@@ -2435,6 +2466,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2435
2466
  customUsername: (!requireWalletIDVVerification && customUsername) ? getFinalCustomUsername(customUsername) : undefined
2436
2467
  }, token);
2437
2468
  if (!('error' in aitResponse)) {
2469
+ aitRef.current = aitResponse;
2438
2470
  setAit(aitResponse);
2439
2471
  }
2440
2472
  }
@@ -2552,6 +2584,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2552
2584
  // Set loading state when permission form opens
2553
2585
  React.useEffect(() => {
2554
2586
  if (isPermissionFormOpen && hitAddress) {
2587
+ authorizationLoadingStateRef.current.setAITLoading(true);
2555
2588
  setIsAITLoading(true);
2556
2589
  }
2557
2590
  }, [isPermissionFormOpen, hitAddress]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytexbyte/nxtlinq-ai-agent-ui-react-development",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Official React Web UI for nxtlinq AI Agent — drop-in chat widget",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,21 @@
1
+ export class AuthorizationLoadingState {
2
+ private aitLoading: boolean;
3
+ private autoConnecting: boolean;
4
+
5
+ constructor(aitLoading = false, autoConnecting = false) {
6
+ this.aitLoading = aitLoading;
7
+ this.autoConnecting = autoConnecting;
8
+ }
9
+
10
+ setAITLoading(value: boolean): void {
11
+ this.aitLoading = value;
12
+ }
13
+
14
+ setAutoConnecting(value: boolean): void {
15
+ this.autoConnecting = value;
16
+ }
17
+
18
+ isLoading(): boolean {
19
+ return this.aitLoading || this.autoConnecting;
20
+ }
21
+ }
@@ -36,6 +36,7 @@ import {
36
36
  ChatBotProps,
37
37
  PresetMessage
38
38
  } from '../types/ChatBotTypes';
39
+ import { AuthorizationLoadingState } from '../authorizationLoadingState';
39
40
  import { PendingTextToolRetryController } from '../pendingTextToolRetry';
40
41
 
41
42
  const MIC_ENABLED_SESSION_KEY = 'chatbot-mic-enabled';
@@ -218,6 +219,9 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
218
219
  const permissionsRef = React.useRef(permissions);
219
220
  const nxtlinqAITServiceAccessTokenRef = React.useRef(nxtlinqAITServiceAccessToken);
220
221
  const signerRef = React.useRef(signer);
222
+ const authorizationLoadingStateRef = React.useRef(
223
+ new AuthorizationLoadingState(isAITLoading, isAutoConnecting),
224
+ );
221
225
  const pendingTextToolRetryRef = React.useRef(new PendingTextToolRetryController());
222
226
  const sendMessageRef = React.useRef<ChatBotContextType['sendMessage']>(async () => {});
223
227
  const retryPendingTextTool = React.useCallback(() => {
@@ -578,6 +582,14 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
578
582
  permissionsRef.current = permissions;
579
583
  }, [permissions]);
580
584
 
585
+ React.useEffect(() => {
586
+ authorizationLoadingStateRef.current.setAITLoading(isAITLoading);
587
+ }, [isAITLoading]);
588
+
589
+ React.useEffect(() => {
590
+ authorizationLoadingStateRef.current.setAutoConnecting(isAutoConnecting);
591
+ }, [isAutoConnecting]);
592
+
581
593
  React.useEffect(() => {
582
594
  nxtlinqAITServiceAccessTokenRef.current = nxtlinqAITServiceAccessToken;
583
595
  }, [nxtlinqAITServiceAccessToken]);
@@ -1021,13 +1033,17 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1021
1033
  const currentHitAddress = hitAddressRef.current || hitAddress;
1022
1034
  const currentToken = nxtlinqAITServiceAccessTokenRef.current || nxtlinqAITServiceAccessToken;
1023
1035
  if (!currentHitAddress) {
1036
+ aitRef.current = null;
1037
+ permissionsRef.current = [];
1024
1038
  setAit(null);
1025
1039
  setPermissions([]);
1026
1040
  setWalletInfo(null);
1041
+ authorizationLoadingStateRef.current.setAITLoading(false);
1027
1042
  setIsAITLoading(false);
1028
1043
  return;
1029
1044
  }
1030
1045
 
1046
+ authorizationLoadingStateRef.current.setAITLoading(true);
1031
1047
  setIsAITLoading(true);
1032
1048
  try {
1033
1049
  let walletAllowsAITLookup = !requireWalletIDVVerification;
@@ -1057,6 +1073,8 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1057
1073
  // AIT existence is not identity verification. In strict mode, stop before
1058
1074
  // the legacy AIT lookup can synthesize a custom wallet record.
1059
1075
  if (currentToken && !walletAllowsAITLookup) {
1076
+ aitRef.current = null;
1077
+ permissionsRef.current = [];
1060
1078
  setAit(null);
1061
1079
  setPermissions([]);
1062
1080
  return;
@@ -1072,26 +1090,37 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1072
1090
 
1073
1091
  if ('error' in response) {
1074
1092
  console.error('Failed to fetch AIT:', response.error);
1093
+ aitRef.current = null;
1094
+ permissionsRef.current = [];
1075
1095
  setAit(null);
1076
1096
  setPermissions([]);
1077
1097
  return;
1078
1098
  }
1079
1099
 
1100
+ aitRef.current = response;
1080
1101
  setAit(response);
1081
1102
  if (!isPermissionFormOpen || forceUpdatePermissions) {
1082
1103
  const newPermissions = response.metadata?.permissions || [];
1104
+ permissionsRef.current = newPermissions;
1083
1105
  setPermissions(newPermissions);
1084
1106
  }
1085
1107
  } else {
1086
1108
  // No token available, clear AIT data
1109
+ aitRef.current = null;
1110
+ permissionsRef.current = [];
1087
1111
  setAit(null);
1088
1112
  setPermissions([]);
1089
1113
  }
1090
1114
  } catch (error) {
1091
1115
  console.error('Failed to fetch AIT:', error);
1116
+ aitRef.current = null;
1117
+ permissionsRef.current = [];
1092
1118
  setAit(null);
1093
1119
  setPermissions([]);
1094
1120
  } finally {
1121
+ // Keep the authorization snapshot current before refreshAIT resolves.
1122
+ // React may not commit setState until the immediate pending-tool retry.
1123
+ authorizationLoadingStateRef.current.setAITLoading(false);
1095
1124
  setIsAITLoading(false);
1096
1125
  }
1097
1126
  };
@@ -1329,7 +1358,9 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1329
1358
  // wallet-address subject until issuance/migration supports it.
1330
1359
  externalId: undefined,
1331
1360
  requireWalletIDVVerification,
1332
- loading: isAITLoading || isAutoConnecting,
1361
+ // Use synchronously maintained refs so a retry immediately following
1362
+ // refreshAIT() does not observe the previous React render's loading state.
1363
+ loading: authorizationLoadingStateRef.current.isLoading(),
1333
1364
  },
1334
1365
  ...authFields(),
1335
1366
  customUsername: (!requireWalletIDVVerification && customUsername)
@@ -2502,8 +2533,11 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
2502
2533
  metadataCid,
2503
2534
  };
2504
2535
 
2536
+ const nextPermissions = newPermissions || permissions;
2537
+ aitRef.current = aitInfo;
2538
+ permissionsRef.current = nextPermissions;
2505
2539
  setAit(aitInfo);
2506
- setPermissions(newPermissions || permissions);
2540
+ setPermissions(nextPermissions);
2507
2541
  };
2508
2542
 
2509
2543
 
@@ -2691,6 +2725,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
2691
2725
  customUsername: (!requireWalletIDVVerification && customUsername) ? getFinalCustomUsername(customUsername) : undefined
2692
2726
  }, token);
2693
2727
  if (!('error' in aitResponse)) {
2728
+ aitRef.current = aitResponse;
2694
2729
  setAit(aitResponse);
2695
2730
  }
2696
2731
  }
@@ -2730,6 +2765,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
2730
2765
  customUsername: (!requireWalletIDVVerification && customUsername) ? getFinalCustomUsername(customUsername) : undefined
2731
2766
  }, token);
2732
2767
  if (!('error' in aitResponse)) {
2768
+ aitRef.current = aitResponse;
2733
2769
  setAit(aitResponse);
2734
2770
  }
2735
2771
  }
@@ -2846,6 +2882,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
2846
2882
  // Set loading state when permission form opens
2847
2883
  React.useEffect(() => {
2848
2884
  if (isPermissionFormOpen && hitAddress) {
2885
+ authorizationLoadingStateRef.current.setAITLoading(true);
2849
2886
  setIsAITLoading(true);
2850
2887
  }
2851
2888
  }, [isPermissionFormOpen, hitAddress]);