@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.4.4 → 0.4.7
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/aitPermissionPropagation.d.ts +16 -0
- package/dist/aitPermissionPropagation.d.ts.map +1 -0
- package/dist/aitPermissionPropagation.js +21 -0
- package/dist/context/ChatBotContext.d.ts.map +1 -1
- package/dist/context/ChatBotContext.js +230 -231
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/piiMessageState.d.ts +16 -0
- package/dist/piiMessageState.d.ts.map +1 -0
- package/dist/piiMessageState.js +18 -0
- package/dist/suggestionState.d.ts +4 -0
- package/dist/suggestionState.d.ts.map +1 -0
- package/dist/suggestionState.js +14 -0
- package/dist/types/ChatBotTypes.d.ts +7 -6
- package/dist/types/ChatBotTypes.d.ts.map +1 -1
- package/dist/ui/ModelSelector.js +2 -2
- package/dist/ui/PermissionForm.d.ts.map +1 -1
- package/dist/ui/PermissionForm.js +14 -28
- package/dist/ui/PresetMessages.d.ts.map +1 -1
- package/dist/ui/PresetMessages.js +7 -4
- package/package.json +4 -4
- package/src/aitPermissionPropagation.ts +40 -0
- package/src/context/ChatBotContext.tsx +309 -266
- package/src/index.ts +0 -1
- package/src/permissionState.ts +313 -0
- package/src/piiMessageState.ts +35 -0
- package/src/suggestionState.ts +20 -0
- package/src/types/ChatBotTypes.ts +7 -7
- package/src/ui/ModelSelector.tsx +5 -5
- package/src/ui/PermissionForm.tsx +19 -30
- package/src/ui/PresetMessages.tsx +9 -6
|
@@ -8,6 +8,9 @@ import { createNxtlinqApi, setApiHosts, synthesizeSpeechToBuffer, streamSpeechTo
|
|
|
8
8
|
import { authorizeTextFrontendTool, hasRecordedWalletVerification, validateWalletSession, } from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
9
9
|
import { AuthorizationLoadingState } from '../authorizationLoadingState';
|
|
10
10
|
import { PendingTextToolRetryController } from '../pendingTextToolRetry';
|
|
11
|
+
import { completePiiScan } from '../piiMessageState';
|
|
12
|
+
import { filterSuggestions } from '../suggestionState';
|
|
13
|
+
import { dispatchRuntimeAITCompleteReplacement, removeOneDeniedPermission, replaceCurrentMaximumDenyList, RuntimeAITMutationQueue, RuntimeAITRecomputeCoordinator, StaleRuntimeAITResponseError, runtimeAITContextKey, runtimeAITSubjectKey, runtimeAuthorizationMatchesContext, runtimeAuthorizationState, unavailableRuntimeAuthorization, useProviderRuntimeCommit, useRuntimeAITContextCommit, } from '../permissionState';
|
|
11
14
|
const MIC_ENABLED_SESSION_KEY = 'chatbot-mic-enabled';
|
|
12
15
|
const ChatBotContext = React.createContext(undefined);
|
|
13
16
|
export const useChatBot = () => {
|
|
@@ -17,7 +20,7 @@ export const useChatBot = () => {
|
|
|
17
20
|
}
|
|
18
21
|
return context;
|
|
19
22
|
};
|
|
20
|
-
export const ChatBotProvider = ({ onMessage, onError, onToolUse, presetMessages = [], placeholder = 'Type a message...', className = '', maxRetries = 3, retryDelay = 2000, serviceId, apiKey, apiSecret, serviceToken, getAuthToken, environment = 'production', onVerifyWallet, permissionGroup, children,
|
|
23
|
+
export const ChatBotProvider = ({ onMessage, onError, onToolUse, presetMessages = [], placeholder = 'Type a message...', className = '', maxRetries = 3, retryDelay = 2000, serviceId, apiKey, apiSecret, serviceToken, getAuthToken, environment = 'production', onVerifyWallet, permissionGroup, roles, children,
|
|
21
24
|
// AI Model related attributes
|
|
22
25
|
onModelChange,
|
|
23
26
|
// Storage mode configuration
|
|
@@ -34,8 +37,6 @@ idvBannerDismissSeconds = 86400,
|
|
|
34
37
|
isStopRecordingOnSend = false,
|
|
35
38
|
// Custom error message to display in chat
|
|
36
39
|
customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
37
|
-
// Set API hosts immediately based on environment (before any API calls)
|
|
38
|
-
setApiHosts(environment);
|
|
39
40
|
const nxtlinqApi = React.useMemo(() => (serviceToken || getAuthToken
|
|
40
41
|
? createNxtlinqApi({ apiKey, apiSecret, serviceToken, getAuthToken })
|
|
41
42
|
: createNxtlinqApi(apiKey ?? '', apiSecret ?? '')), [apiKey, apiSecret, serviceToken, getAuthToken]);
|
|
@@ -87,7 +88,17 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
87
88
|
const [hitAddress, setHitAddress] = React.useState(null);
|
|
88
89
|
const [ait, setAit] = React.useState(null);
|
|
89
90
|
const [permissions, setPermissions] = React.useState([]);
|
|
90
|
-
const [
|
|
91
|
+
const [maximumPermissions, setMaximumPermissions] = React.useState([]);
|
|
92
|
+
const [deniedPermissions, setDeniedPermissions] = React.useState([]);
|
|
93
|
+
const [runtimePermissionsUnavailable, setRuntimePermissionsUnavailable] = React.useState(false);
|
|
94
|
+
const [runtimeAuthorizationContextKey, setRuntimeAuthorizationContextKey] = React.useState(null);
|
|
95
|
+
const [servicePermissionCatalog, setServicePermissionCatalog] = React.useState([]);
|
|
96
|
+
const availablePermissions = React.useMemo(() => maximumPermissions.map((label) => servicePermissionCatalog.find((permission) => permission.label === label) ?? {
|
|
97
|
+
id: label,
|
|
98
|
+
label,
|
|
99
|
+
description: '',
|
|
100
|
+
groups: [],
|
|
101
|
+
}), [maximumPermissions, servicePermissionCatalog]);
|
|
91
102
|
const [showPermissionForm, setShowPermissionForm] = React.useState(false);
|
|
92
103
|
const [isPermissionFormOpen, setIsPermissionFormOpen] = React.useState(false);
|
|
93
104
|
const [isAITLoading, setIsAITLoading] = React.useState(false);
|
|
@@ -96,6 +107,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
96
107
|
const [walletInfo, setWalletInfo] = React.useState(null);
|
|
97
108
|
const [isWalletLoading, setIsWalletLoading] = React.useState(false);
|
|
98
109
|
const [isAutoConnecting, setIsAutoConnecting] = React.useState(false);
|
|
110
|
+
const runtimeRolesKey = JSON.stringify(roles ?? null);
|
|
99
111
|
// Persistent data (always use localStorage)
|
|
100
112
|
const [nxtlinqAITServiceAccessToken, setNxtlinqAITServiceAccessToken] = useLocalStorage('nxtlinqAITServiceAccessToken', '');
|
|
101
113
|
const [pseudoId, setPseudoId] = useLocalStorage('pseudoId', uuidv4());
|
|
@@ -144,6 +156,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
144
156
|
const hitAddressRef = React.useRef(hitAddress);
|
|
145
157
|
const aitRef = React.useRef(ait);
|
|
146
158
|
const permissionsRef = React.useRef(permissions);
|
|
159
|
+
const deniedPermissionsRef = React.useRef(deniedPermissions);
|
|
147
160
|
const nxtlinqAITServiceAccessTokenRef = React.useRef(nxtlinqAITServiceAccessToken);
|
|
148
161
|
const signerRef = React.useRef(signer);
|
|
149
162
|
const authorizationLoadingStateRef = React.useRef(new AuthorizationLoadingState(isAITLoading, isAutoConnecting));
|
|
@@ -158,6 +171,9 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
158
171
|
await sendMessageRef.current(request.content, 1, request.isPresetMessage, request.attachments);
|
|
159
172
|
});
|
|
160
173
|
}, []);
|
|
174
|
+
const runtimeRecomputeCoordinatorRef = React.useRef(new RuntimeAITRecomputeCoordinator());
|
|
175
|
+
const runtimeMutationQueueRef = React.useRef(new RuntimeAITMutationQueue());
|
|
176
|
+
useProviderRuntimeCommit(hitAddressRef, hitAddress, environment, setApiHosts);
|
|
161
177
|
// Helper function to combine customUsername with Adilas customUserInfo
|
|
162
178
|
const getFinalCustomUsername = React.useCallback((username) => {
|
|
163
179
|
if (!username)
|
|
@@ -187,6 +203,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
187
203
|
const textInputRef = React.useRef(null);
|
|
188
204
|
const lastPartialRangeRef = React.useRef(null);
|
|
189
205
|
const lastAutoSentTranscriptRef = React.useRef('');
|
|
206
|
+
const presetSendInFlightRef = React.useRef(false);
|
|
190
207
|
const autoSendTimerRef = React.useRef(null);
|
|
191
208
|
const isCorrectingRef = React.useRef(false);
|
|
192
209
|
const lastCustomErrorRef = React.useRef(undefined);
|
|
@@ -476,9 +493,6 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
476
493
|
}
|
|
477
494
|
}, []);
|
|
478
495
|
// Update refs when state changes
|
|
479
|
-
React.useEffect(() => {
|
|
480
|
-
hitAddressRef.current = hitAddress;
|
|
481
|
-
}, [hitAddress]);
|
|
482
496
|
React.useEffect(() => {
|
|
483
497
|
aitRef.current = ait;
|
|
484
498
|
}, [ait]);
|
|
@@ -491,6 +505,9 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
491
505
|
React.useEffect(() => {
|
|
492
506
|
authorizationLoadingStateRef.current.setAutoConnecting(isAutoConnecting);
|
|
493
507
|
}, [isAutoConnecting]);
|
|
508
|
+
React.useEffect(() => {
|
|
509
|
+
deniedPermissionsRef.current = deniedPermissions;
|
|
510
|
+
}, [deniedPermissions]);
|
|
494
511
|
React.useEffect(() => {
|
|
495
512
|
nxtlinqAITServiceAccessTokenRef.current = nxtlinqAITServiceAccessToken;
|
|
496
513
|
}, [nxtlinqAITServiceAccessToken]);
|
|
@@ -870,7 +887,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
870
887
|
console.error('Failed to fetch permissions:', result.error);
|
|
871
888
|
return undefined;
|
|
872
889
|
}
|
|
873
|
-
|
|
890
|
+
setServicePermissionCatalog(result.permissions);
|
|
874
891
|
return result.permissions;
|
|
875
892
|
}
|
|
876
893
|
catch (error) {
|
|
@@ -878,7 +895,74 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
878
895
|
return undefined;
|
|
879
896
|
}
|
|
880
897
|
};
|
|
881
|
-
|
|
898
|
+
const clearRuntimeAuthorization = React.useCallback(() => {
|
|
899
|
+
const cleared = unavailableRuntimeAuthorization(deniedPermissionsRef.current);
|
|
900
|
+
const activeContext = runtimeRecomputeCoordinatorRef.current.getActiveContext();
|
|
901
|
+
aitRef.current = null;
|
|
902
|
+
permissionsRef.current = [];
|
|
903
|
+
setAit(cleared.ait);
|
|
904
|
+
setPermissions(cleared.effectivePermissions);
|
|
905
|
+
setMaximumPermissions(cleared.maximumPermissions);
|
|
906
|
+
setRuntimePermissionsUnavailable(cleared.unavailable);
|
|
907
|
+
setRuntimeAuthorizationContextKey(activeContext ? runtimeAITContextKey(activeContext) : null);
|
|
908
|
+
}, []);
|
|
909
|
+
const applyRuntimeResult = React.useCallback((result, context) => {
|
|
910
|
+
const next = runtimeAuthorizationState(result, context.controller, context.serviceId);
|
|
911
|
+
aitRef.current = next.ait;
|
|
912
|
+
permissionsRef.current = next.effectivePermissions;
|
|
913
|
+
setAit(next.ait);
|
|
914
|
+
setPermissions(next.effectivePermissions);
|
|
915
|
+
setMaximumPermissions(next.maximumPermissions);
|
|
916
|
+
setDeniedPermissions(next.deniedPermissions);
|
|
917
|
+
deniedPermissionsRef.current = next.deniedPermissions;
|
|
918
|
+
setRuntimePermissionsUnavailable(false);
|
|
919
|
+
setRuntimeAuthorizationContextKey(runtimeAITContextKey(context));
|
|
920
|
+
return result;
|
|
921
|
+
}, []);
|
|
922
|
+
const runtimeContext = React.useMemo(() => hitAddress ? {
|
|
923
|
+
controller: hitAddress,
|
|
924
|
+
serviceId,
|
|
925
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
926
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
927
|
+
} : null, [hitAddress, permissionGroup, runtimeRolesKey, serviceId]);
|
|
928
|
+
const invalidateCommittedRuntimeContext = React.useCallback(() => {
|
|
929
|
+
const activeContext = runtimeRecomputeCoordinatorRef.current.getActiveContext();
|
|
930
|
+
aitRef.current = null;
|
|
931
|
+
permissionsRef.current = [];
|
|
932
|
+
deniedPermissionsRef.current = [];
|
|
933
|
+
setAit(null);
|
|
934
|
+
setPermissions([]);
|
|
935
|
+
setMaximumPermissions([]);
|
|
936
|
+
setDeniedPermissions([]);
|
|
937
|
+
setRuntimePermissionsUnavailable(activeContext !== null);
|
|
938
|
+
setRuntimeAuthorizationContextKey(activeContext ? runtimeAITContextKey(activeContext) : null);
|
|
939
|
+
}, []);
|
|
940
|
+
useRuntimeAITContextCommit(runtimeRecomputeCoordinatorRef.current, runtimeContext, invalidateCommittedRuntimeContext);
|
|
941
|
+
const recomputeRuntimeAIT = React.useCallback(async (context) => {
|
|
942
|
+
const capturedContext = {
|
|
943
|
+
controller: context.controller,
|
|
944
|
+
serviceId: context.serviceId,
|
|
945
|
+
...(context.roles !== undefined ? { roles: [...context.roles] } : {}),
|
|
946
|
+
...(context.permissionGroup !== undefined ? { permissionGroup: context.permissionGroup } : {}),
|
|
947
|
+
};
|
|
948
|
+
return runtimeRecomputeCoordinatorRef.current.run(capturedContext, () => nxtlinqApi.ait.recomputeAIT(capturedContext), applyRuntimeResult, clearRuntimeAuthorization);
|
|
949
|
+
}, [applyRuntimeResult, clearRuntimeAuthorization, nxtlinqApi]);
|
|
950
|
+
const queueRuntimeAITRefresh = React.useCallback((context) => runtimeMutationQueueRef.current.run(runtimeAITSubjectKey(context), async () => {
|
|
951
|
+
runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
|
|
952
|
+
return recomputeRuntimeAIT(context);
|
|
953
|
+
}), [recomputeRuntimeAIT]);
|
|
954
|
+
const convergeRuntimeAITSubject = React.useCallback(async (subjectKey) => {
|
|
955
|
+
const activeContext = runtimeRecomputeCoordinatorRef.current.getActiveContext();
|
|
956
|
+
if (!activeContext || runtimeAITSubjectKey(activeContext) !== subjectKey) {
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
await recomputeRuntimeAIT(activeContext);
|
|
960
|
+
}, [recomputeRuntimeAIT]);
|
|
961
|
+
const dispatchRuntimeDenyListReplacement = React.useCallback(async (context, nextDeniedPermissions) => dispatchRuntimeAITCompleteReplacement(runtimeRecomputeCoordinatorRef.current, context, () => nxtlinqApi.ait.updatePermissionDenyList({
|
|
962
|
+
...context,
|
|
963
|
+
deniedPermissions: nextDeniedPermissions,
|
|
964
|
+
}), convergeRuntimeAITSubject, clearRuntimeAuthorization), [clearRuntimeAuthorization, convergeRuntimeAITSubject, nxtlinqApi]);
|
|
965
|
+
// Refresh the authoritative runtime AIT state.
|
|
882
966
|
const refreshAIT = async (forceUpdatePermissions = false) => {
|
|
883
967
|
const currentHitAddress = hitAddressRef.current || hitAddress;
|
|
884
968
|
const currentToken = nxtlinqAITServiceAccessTokenRef.current || nxtlinqAITServiceAccessToken;
|
|
@@ -887,8 +971,10 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
887
971
|
permissionsRef.current = [];
|
|
888
972
|
setAit(null);
|
|
889
973
|
setPermissions([]);
|
|
974
|
+
setMaximumPermissions([]);
|
|
890
975
|
setWalletInfo(null);
|
|
891
976
|
authorizationLoadingStateRef.current.setAITLoading(false);
|
|
977
|
+
setRuntimePermissionsUnavailable(false);
|
|
892
978
|
setIsAITLoading(false);
|
|
893
979
|
return;
|
|
894
980
|
}
|
|
@@ -919,52 +1005,24 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
919
1005
|
console.error('Failed to fetch wallet info:', error);
|
|
920
1006
|
}
|
|
921
1007
|
}
|
|
922
|
-
// AIT existence is not identity verification. In strict mode, stop before
|
|
923
|
-
// the legacy AIT lookup can synthesize a custom wallet record.
|
|
924
1008
|
if (currentToken && !walletAllowsAITLookup) {
|
|
925
|
-
|
|
926
|
-
permissionsRef.current = [];
|
|
927
|
-
setAit(null);
|
|
928
|
-
setPermissions([]);
|
|
1009
|
+
clearRuntimeAuthorization();
|
|
929
1010
|
return;
|
|
930
1011
|
}
|
|
931
|
-
//
|
|
1012
|
+
// Recompute is the sole mint/reuse path and the only permission authority.
|
|
932
1013
|
if (currentToken) {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
controller: currentHitAddress,
|
|
936
|
-
customUsername: (!requireWalletIDVVerification && customUsername) ? getFinalCustomUsername(customUsername) : undefined
|
|
937
|
-
}, currentToken);
|
|
938
|
-
if ('error' in response) {
|
|
939
|
-
console.error('Failed to fetch AIT:', response.error);
|
|
940
|
-
aitRef.current = null;
|
|
941
|
-
permissionsRef.current = [];
|
|
942
|
-
setAit(null);
|
|
943
|
-
setPermissions([]);
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
aitRef.current = response;
|
|
947
|
-
setAit(response);
|
|
948
|
-
if (!isPermissionFormOpen || forceUpdatePermissions) {
|
|
949
|
-
const newPermissions = response.metadata?.permissions || [];
|
|
950
|
-
permissionsRef.current = newPermissions;
|
|
951
|
-
setPermissions(newPermissions);
|
|
1014
|
+
if (runtimeContext) {
|
|
1015
|
+
await queueRuntimeAITRefresh(runtimeContext);
|
|
952
1016
|
}
|
|
953
1017
|
}
|
|
954
1018
|
else {
|
|
955
|
-
|
|
956
|
-
aitRef.current = null;
|
|
957
|
-
permissionsRef.current = [];
|
|
958
|
-
setAit(null);
|
|
959
|
-
setPermissions([]);
|
|
1019
|
+
clearRuntimeAuthorization();
|
|
960
1020
|
}
|
|
961
1021
|
}
|
|
962
1022
|
catch (error) {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
setAit(null);
|
|
967
|
-
setPermissions([]);
|
|
1023
|
+
if (!(error instanceof StaleRuntimeAITResponseError)) {
|
|
1024
|
+
console.error('Failed to recompute AIT:', error);
|
|
1025
|
+
}
|
|
968
1026
|
}
|
|
969
1027
|
finally {
|
|
970
1028
|
// Keep the authorization snapshot current before refreshAIT resolves.
|
|
@@ -1151,9 +1209,13 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1151
1209
|
const { accessToken } = response;
|
|
1152
1210
|
setNxtlinqAITServiceAccessToken(accessToken);
|
|
1153
1211
|
nxtlinqAITServiceAccessTokenRef.current = accessToken;
|
|
1154
|
-
|
|
1155
|
-
await
|
|
1156
|
-
|
|
1212
|
+
localStorage.setItem('nxtlinqAITServiceAccessToken', JSON.stringify(accessToken));
|
|
1213
|
+
await queueRuntimeAITRefresh({
|
|
1214
|
+
controller: addressToUse,
|
|
1215
|
+
serviceId,
|
|
1216
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
1217
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
1218
|
+
});
|
|
1157
1219
|
if (autoShowSuccessMessage) {
|
|
1158
1220
|
showSuccess(walletTextUtils.getWalletText('Successfully signed in with your HIT wallet. You can now use the AI agent.', serviceId));
|
|
1159
1221
|
}
|
|
@@ -1188,6 +1250,8 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1188
1250
|
loading: authorizationLoadingStateRef.current.isLoading(),
|
|
1189
1251
|
},
|
|
1190
1252
|
...authFields(),
|
|
1253
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
1254
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
1191
1255
|
customUsername: (!requireWalletIDVVerification && customUsername)
|
|
1192
1256
|
? getFinalCustomUsername(customUsername)
|
|
1193
1257
|
: undefined,
|
|
@@ -1252,7 +1316,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1252
1316
|
}
|
|
1253
1317
|
return effectiveAvailableModels[safeIndex];
|
|
1254
1318
|
}, [effectiveAvailableModels, selectedModelIndex]);
|
|
1255
|
-
const updateSuggestions = React.useCallback(async (pseudoId, externalId) => {
|
|
1319
|
+
const updateSuggestions = React.useCallback(async (pseudoId, externalId, lastUserMessage) => {
|
|
1256
1320
|
const result = await nxtlinqApi.agent.generateSuggestions({
|
|
1257
1321
|
...authFields(),
|
|
1258
1322
|
pseudoId,
|
|
@@ -1263,11 +1327,8 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1263
1327
|
setSuggestions([]);
|
|
1264
1328
|
return;
|
|
1265
1329
|
}
|
|
1266
|
-
setSuggestions(result.suggestions.map((sug) => ({
|
|
1267
|
-
|
|
1268
|
-
autoSend: true
|
|
1269
|
-
})));
|
|
1270
|
-
}, []);
|
|
1330
|
+
setSuggestions(filterSuggestions(result.suggestions.map((sug) => ({ text: sug, autoSend: true })), lastUserMessage));
|
|
1331
|
+
}, [nxtlinqApi, authFields, setSuggestions]);
|
|
1271
1332
|
// Updated sendMessage function to support different AI models and attachments
|
|
1272
1333
|
const sendMessage = async (content, retryCount = 0, isPresetMessage = false, attachments, clientPipelineOverride) => {
|
|
1273
1334
|
const hasContent = content.trim() || (attachments && attachments.length > 0);
|
|
@@ -1387,15 +1448,15 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1387
1448
|
const updated = [...prev];
|
|
1388
1449
|
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1389
1450
|
if (updated[i].role === 'user' && updated[i].piiStatus === 'scanning') {
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1451
|
+
if (step === 'scan_complete') {
|
|
1452
|
+
// PII work is complete independently of any later tool
|
|
1453
|
+
// authorization. Never leave the bubble in Sending while
|
|
1454
|
+
// waiting for wallet recovery or permission elevation.
|
|
1455
|
+
updated[i] = completePiiScan(updated[i], data);
|
|
1456
|
+
}
|
|
1457
|
+
else {
|
|
1458
|
+
updated[i] = { ...updated[i], piiStep };
|
|
1397
1459
|
}
|
|
1398
|
-
updated[i] = { ...updated[i], ...patch };
|
|
1399
1460
|
break;
|
|
1400
1461
|
}
|
|
1401
1462
|
}
|
|
@@ -1403,6 +1464,26 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1403
1464
|
});
|
|
1404
1465
|
} : undefined,
|
|
1405
1466
|
});
|
|
1467
|
+
// PII scanning is complete when the Agent response arrives, regardless
|
|
1468
|
+
// of whether a later frontend-tool authorization step can continue.
|
|
1469
|
+
if (piiDisplayMode === 'redacted') {
|
|
1470
|
+
const anonymizedUserMsg = response.piiProtection?.anonymizedUserMessage;
|
|
1471
|
+
const piiMappingData = response.piiProtection?.mapping ?? undefined;
|
|
1472
|
+
setMessages(prev => {
|
|
1473
|
+
const updated = [...prev];
|
|
1474
|
+
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1475
|
+
if (updated[i].role === 'user' && updated[i].piiStatus === 'scanning') {
|
|
1476
|
+
updated[i] = completePiiScan(updated[i], {
|
|
1477
|
+
entityCount: piiMappingData ? Object.keys(piiMappingData).length : 0,
|
|
1478
|
+
anonymizedUserMessage: anonymizedUserMsg,
|
|
1479
|
+
mapping: piiMappingData,
|
|
1480
|
+
});
|
|
1481
|
+
break;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
return updated;
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1406
1487
|
if (!('error' in response && response.error)) {
|
|
1407
1488
|
const tr = response;
|
|
1408
1489
|
if (tr.ttsVoice &&
|
|
@@ -1527,7 +1608,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1527
1608
|
if (!replyText.trim()) {
|
|
1528
1609
|
replyText = 'Sorry, I cannot understand your question';
|
|
1529
1610
|
}
|
|
1530
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
1611
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1531
1612
|
setMessages(prev => prev.map(m => m.id === streamAssistantId
|
|
1532
1613
|
? {
|
|
1533
1614
|
...m,
|
|
@@ -1666,7 +1747,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1666
1747
|
// Don't block the UI if update fails
|
|
1667
1748
|
}
|
|
1668
1749
|
}
|
|
1669
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
1750
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1670
1751
|
// Skip creating a new botResponse since we already updated the streaming message
|
|
1671
1752
|
}
|
|
1672
1753
|
else {
|
|
@@ -1705,7 +1786,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1705
1786
|
// Don't block the UI if update fails
|
|
1706
1787
|
}
|
|
1707
1788
|
}
|
|
1708
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
1789
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1709
1790
|
const newBotResponse = {
|
|
1710
1791
|
id: (Date.now() + 1).toString(),
|
|
1711
1792
|
content: mergedContent,
|
|
@@ -1731,7 +1812,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1731
1812
|
.map((item) => item.text)
|
|
1732
1813
|
.join(' ') || 'Sorry, I cannot understand your question'
|
|
1733
1814
|
: response.reply || 'Sorry, I cannot understand your question';
|
|
1734
|
-
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined);
|
|
1815
|
+
updateSuggestions(pseudoId, localStorage.getItem('walletAddress') || undefined, content);
|
|
1735
1816
|
const newBotResponse = {
|
|
1736
1817
|
id: (Date.now() + 1).toString(),
|
|
1737
1818
|
content: replyText,
|
|
@@ -1769,38 +1850,6 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
1769
1850
|
setMessages(prev => [...prev, newBotResponse]);
|
|
1770
1851
|
botResponse = newBotResponse;
|
|
1771
1852
|
}
|
|
1772
|
-
// ===== PII Protection: Update user message with anonymized version =====
|
|
1773
|
-
const anonymizedUserMsg = response.piiProtection?.anonymizedUserMessage;
|
|
1774
|
-
const piiMappingData = response.piiProtection?.mapping ?? undefined;
|
|
1775
|
-
if (anonymizedUserMsg) {
|
|
1776
|
-
setMessages(prev => {
|
|
1777
|
-
const updated = [...prev];
|
|
1778
|
-
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1779
|
-
if (updated[i].role === 'user') {
|
|
1780
|
-
updated[i] = {
|
|
1781
|
-
...updated[i],
|
|
1782
|
-
piiProtection: { anonymizedContent: anonymizedUserMsg, mapping: piiMappingData },
|
|
1783
|
-
piiStatus: 'complete',
|
|
1784
|
-
};
|
|
1785
|
-
break;
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
|
-
return updated;
|
|
1789
|
-
});
|
|
1790
|
-
}
|
|
1791
|
-
else if (piiDisplayMode === 'redacted') {
|
|
1792
|
-
// No PII detected — set piiStatus to 'none' to trigger "No sensitive data" indicator
|
|
1793
|
-
setMessages(prev => {
|
|
1794
|
-
const updated = [...prev];
|
|
1795
|
-
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1796
|
-
if (updated[i].role === 'user' && updated[i].piiStatus === 'scanning') {
|
|
1797
|
-
updated[i] = { ...updated[i], piiStatus: 'none' };
|
|
1798
|
-
break;
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
return updated;
|
|
1802
|
-
});
|
|
1803
|
-
}
|
|
1804
1853
|
// Execute redirect after all message processing is complete
|
|
1805
1854
|
if (redirectUrl) {
|
|
1806
1855
|
// Use setTimeout to ensure the message is displayed before redirect
|
|
@@ -2141,20 +2190,18 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2141
2190
|
return { url: result.url };
|
|
2142
2191
|
}, [nxtlinqApi, authFields, pseudoId]);
|
|
2143
2192
|
// Handle preset message
|
|
2144
|
-
const handlePresetMessage = (message) => {
|
|
2193
|
+
const handlePresetMessage = async (message) => {
|
|
2145
2194
|
// If preset is configured as auto-send, avoid duplicate sends when user clicks repeatedly
|
|
2146
2195
|
if (message.autoSend) {
|
|
2147
2196
|
const trimmedText = (message.text || '').trim();
|
|
2148
2197
|
if (!trimmedText)
|
|
2149
2198
|
return;
|
|
2150
2199
|
// Prevent sending messages while AI Agent is processing
|
|
2151
|
-
if (isLoading) {
|
|
2152
|
-
return;
|
|
2153
|
-
}
|
|
2154
|
-
// If this exact preset text was just sent (by auto-send / manual / preset), skip to prevent duplicates
|
|
2155
|
-
if (lastAutoSentTranscriptRef.current === trimmedText) {
|
|
2200
|
+
if (isLoading || presetSendInFlightRef.current) {
|
|
2156
2201
|
return;
|
|
2157
2202
|
}
|
|
2203
|
+
presetSendInFlightRef.current = true;
|
|
2204
|
+
setSuggestions(previous => filterSuggestions(previous, trimmedText));
|
|
2158
2205
|
// For preset messages, we need to add the user message first since sendMessage won't add it on retries
|
|
2159
2206
|
const userMessage = {
|
|
2160
2207
|
id: Date.now().toString(),
|
|
@@ -2171,91 +2218,17 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2171
2218
|
// Mark as last sent to guard against rapid re-clicks and other duplicate flows
|
|
2172
2219
|
lastAutoSentTranscriptRef.current = trimmedText;
|
|
2173
2220
|
// Pass a flag to indicate this is a preset message so sendMessage won't add user message again
|
|
2174
|
-
|
|
2221
|
+
try {
|
|
2222
|
+
await sendMessage(trimmedText, 0, true);
|
|
2223
|
+
}
|
|
2224
|
+
finally {
|
|
2225
|
+
presetSendInFlightRef.current = false;
|
|
2226
|
+
}
|
|
2175
2227
|
}
|
|
2176
2228
|
else {
|
|
2177
2229
|
setInputValue(message.text);
|
|
2178
2230
|
}
|
|
2179
2231
|
};
|
|
2180
|
-
// Generate and register AIT
|
|
2181
|
-
const generateAndRegisterAIT = async (newPermissions, isFromAIAgent = false) => {
|
|
2182
|
-
let currentSigner = signer;
|
|
2183
|
-
let currentAddress = hitAddress;
|
|
2184
|
-
// If we have an address but no signer, try to auto-connect wallet
|
|
2185
|
-
if (currentAddress && !currentSigner) {
|
|
2186
|
-
const storedWalletAddress = localStorage.getItem('walletAddress');
|
|
2187
|
-
if (storedWalletAddress === currentAddress) {
|
|
2188
|
-
try {
|
|
2189
|
-
const autoConnectResult = await connectWallet(false); // Don't show sign-in message yet
|
|
2190
|
-
if (autoConnectResult) {
|
|
2191
|
-
// Wait for state to update
|
|
2192
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
2193
|
-
// Get the updated signer and address
|
|
2194
|
-
currentSigner = signerRef.current || signer;
|
|
2195
|
-
currentAddress = hitAddressRef.current || hitAddress;
|
|
2196
|
-
}
|
|
2197
|
-
}
|
|
2198
|
-
catch (error) {
|
|
2199
|
-
console.error('Auto-connect failed during AIT generation:', error);
|
|
2200
|
-
}
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
if (!currentSigner || !currentAddress) {
|
|
2204
|
-
throw new Error('Missing signer or wallet address');
|
|
2205
|
-
}
|
|
2206
|
-
return generateAndRegisterAITWithSigner(newPermissions, isFromAIAgent, currentSigner, currentAddress);
|
|
2207
|
-
};
|
|
2208
|
-
// Generate and register AIT with explicit signer and address
|
|
2209
|
-
const generateAndRegisterAITWithSigner = async (newPermissions, isFromAIAgent = false, explicitSigner, explicitAddress) => {
|
|
2210
|
-
const currentSigner = explicitSigner || signer;
|
|
2211
|
-
const currentAddress = explicitAddress || hitAddress;
|
|
2212
|
-
if (!currentSigner || !currentAddress) {
|
|
2213
|
-
throw new Error('Missing signer or wallet address');
|
|
2214
|
-
}
|
|
2215
|
-
// If AI Agent is creating, must check if user has existing AIT
|
|
2216
|
-
if (isFromAIAgent && !aitRef.current) {
|
|
2217
|
-
throw new Error('You must have an existing AIT before AI Agent can create new AITs');
|
|
2218
|
-
}
|
|
2219
|
-
const timestamp = Math.floor(Date.now() / 1000);
|
|
2220
|
-
const aitId = `did:polygon:nxtlinq:${currentAddress}:${timestamp}`;
|
|
2221
|
-
const metadata = {
|
|
2222
|
-
permissions: newPermissions || permissions,
|
|
2223
|
-
issuedBy: currentAddress,
|
|
2224
|
-
};
|
|
2225
|
-
const metadataStr = stringify(metadata);
|
|
2226
|
-
// ethers v6: utils.keccak256 -> keccak256, utils.toUtf8Bytes -> toUtf8Bytes
|
|
2227
|
-
const metadataHash = ethers.keccak256(ethers.toUtf8Bytes(metadataStr));
|
|
2228
|
-
const uploadResponse = await nxtlinqApi.metadata.createMetadata(metadata, nxtlinqAITServiceAccessToken || '');
|
|
2229
|
-
if ('error' in uploadResponse) {
|
|
2230
|
-
throw new Error(`Failed to upload metadata: ${uploadResponse.error}`);
|
|
2231
|
-
}
|
|
2232
|
-
const { metadataCid } = uploadResponse;
|
|
2233
|
-
const createAITParams = {
|
|
2234
|
-
aitId,
|
|
2235
|
-
controller: currentAddress,
|
|
2236
|
-
serviceId,
|
|
2237
|
-
metadataHash,
|
|
2238
|
-
metadataCid,
|
|
2239
|
-
isFromAIAgent: isFromAIAgent,
|
|
2240
|
-
parentAITId: isFromAIAgent ? aitRef.current?.aitId : undefined,
|
|
2241
|
-
};
|
|
2242
|
-
const createAITResponse = await nxtlinqApi.ait.createAIT(createAITParams, nxtlinqAITServiceAccessToken || '');
|
|
2243
|
-
if ('error' in createAITResponse) {
|
|
2244
|
-
throw new Error(`Failed to create AIT: ${createAITResponse.error}`);
|
|
2245
|
-
}
|
|
2246
|
-
const aitInfo = {
|
|
2247
|
-
aitId,
|
|
2248
|
-
controller: currentAddress,
|
|
2249
|
-
metadata,
|
|
2250
|
-
metadataHash,
|
|
2251
|
-
metadataCid,
|
|
2252
|
-
};
|
|
2253
|
-
const nextPermissions = newPermissions || permissions;
|
|
2254
|
-
aitRef.current = aitInfo;
|
|
2255
|
-
permissionsRef.current = nextPermissions;
|
|
2256
|
-
setAit(aitInfo);
|
|
2257
|
-
setPermissions(nextPermissions);
|
|
2258
|
-
};
|
|
2259
2232
|
// Auto enable AIT permission
|
|
2260
2233
|
const enableAIT = async (toolName) => {
|
|
2261
2234
|
if (isAITEnabling)
|
|
@@ -2317,35 +2290,41 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2317
2290
|
}
|
|
2318
2291
|
}
|
|
2319
2292
|
try {
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2293
|
+
const context = {
|
|
2294
|
+
controller: currentHitAddress,
|
|
2295
|
+
serviceId,
|
|
2296
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
2297
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
2298
|
+
};
|
|
2299
|
+
const enabled = await runtimeMutationQueueRef.current.run(runtimeAITSubjectKey(context), async () => {
|
|
2300
|
+
runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
|
|
2301
|
+
const current = await recomputeRuntimeAIT(context);
|
|
2302
|
+
if (!current.maximumPermissions.includes(toolName)) {
|
|
2303
|
+
return false;
|
|
2304
|
+
}
|
|
2305
|
+
const nextDeniedPermissions = removeOneDeniedPermission(current.deniedPermissions, toolName);
|
|
2306
|
+
runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
|
|
2307
|
+
await dispatchRuntimeDenyListReplacement(context, nextDeniedPermissions);
|
|
2308
|
+
return true;
|
|
2309
|
+
});
|
|
2310
|
+
if (!enabled) {
|
|
2323
2311
|
showError(`Tool ${toolName} is not available for your current identity provider`);
|
|
2324
2312
|
setIsAITEnabling(false);
|
|
2325
2313
|
return false;
|
|
2326
2314
|
}
|
|
2327
|
-
// Get current permissions from AIT metadata instead of React state
|
|
2328
|
-
// This ensures we don't lose existing permissions when enabling new ones
|
|
2329
|
-
const currentAITPermissions = aitRef.current?.metadata?.permissions || permissions;
|
|
2330
|
-
const newPermissions = [...currentAITPermissions];
|
|
2331
|
-
if (!newPermissions.includes(toolName)) {
|
|
2332
|
-
newPermissions.push(toolName);
|
|
2333
|
-
}
|
|
2334
|
-
// Filter out deleted permissions before saving
|
|
2335
|
-
const validPermissions = newPermissions.filter(permission => availablePermissionLabels.includes(permission));
|
|
2336
|
-
// Generate and register AIT with new permissions
|
|
2337
|
-
// For auto-enable, we should create a regular AIT (not AI Agent AIT) if user doesn't have existing AIT
|
|
2338
|
-
const shouldCreateAsAIAgent = !!aitRef.current; // Only create as AI Agent if user already has an AIT
|
|
2339
|
-
await generateAndRegisterAITWithSigner(validPermissions, shouldCreateAsAIAgent, currentSigner, currentHitAddress);
|
|
2340
2315
|
showSuccess('AIT permission enabled successfully! You can now use the AI agent.');
|
|
2341
|
-
await refreshAIT(true);
|
|
2342
2316
|
setIsAITEnabling(false);
|
|
2343
2317
|
return true;
|
|
2344
2318
|
}
|
|
2345
2319
|
catch (error) {
|
|
2346
|
-
|
|
2320
|
+
if (!(error instanceof StaleRuntimeAITResponseError)) {
|
|
2321
|
+
console.error('Failed to auto-enable AIT:', error);
|
|
2322
|
+
}
|
|
2347
2323
|
setIsAITEnabling(false);
|
|
2348
|
-
if (error instanceof
|
|
2324
|
+
if (error instanceof StaleRuntimeAITResponseError) {
|
|
2325
|
+
return false;
|
|
2326
|
+
}
|
|
2327
|
+
else if (error instanceof Error) {
|
|
2349
2328
|
showError(error.message);
|
|
2350
2329
|
}
|
|
2351
2330
|
else {
|
|
@@ -2358,11 +2337,23 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2358
2337
|
const savePermissions = async (newPermissions) => {
|
|
2359
2338
|
setIsDisabled(true);
|
|
2360
2339
|
try {
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
const
|
|
2365
|
-
|
|
2340
|
+
if (!hitAddress) {
|
|
2341
|
+
throw new Error('Please connect your wallet first');
|
|
2342
|
+
}
|
|
2343
|
+
const context = {
|
|
2344
|
+
controller: hitAddress,
|
|
2345
|
+
serviceId,
|
|
2346
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
2347
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
2348
|
+
};
|
|
2349
|
+
const checkedPermissions = [...(newPermissions ?? permissions)];
|
|
2350
|
+
await runtimeMutationQueueRef.current.run(runtimeAITSubjectKey(context), async () => {
|
|
2351
|
+
runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
|
|
2352
|
+
const fresh = await recomputeRuntimeAIT(context);
|
|
2353
|
+
const nextDeniedPermissions = replaceCurrentMaximumDenyList(fresh.deniedPermissions, fresh.maximumPermissions, checkedPermissions);
|
|
2354
|
+
runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
|
|
2355
|
+
await dispatchRuntimeDenyListReplacement(context, nextDeniedPermissions);
|
|
2356
|
+
});
|
|
2366
2357
|
showSuccess('AIT permissions saved successfully! You can now use the AI agent with your configured permissions.');
|
|
2367
2358
|
setShowPermissionForm(false);
|
|
2368
2359
|
setIsPermissionFormOpen(false);
|
|
@@ -2370,9 +2361,14 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2370
2361
|
await retryPendingTextTool();
|
|
2371
2362
|
}
|
|
2372
2363
|
catch (error) {
|
|
2373
|
-
|
|
2364
|
+
if (!(error instanceof StaleRuntimeAITResponseError)) {
|
|
2365
|
+
console.error('Failed to update AIT deny-list:', error);
|
|
2366
|
+
}
|
|
2374
2367
|
setIsDisabled(false);
|
|
2375
|
-
if (error instanceof
|
|
2368
|
+
if (error instanceof StaleRuntimeAITResponseError) {
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2371
|
+
else if (error instanceof Error) {
|
|
2376
2372
|
showError(error.message);
|
|
2377
2373
|
}
|
|
2378
2374
|
else {
|
|
@@ -2418,15 +2414,12 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2418
2414
|
const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
|
|
2419
2415
|
if (!('error' in walletResponse)) {
|
|
2420
2416
|
setWalletInfo(walletResponse);
|
|
2421
|
-
|
|
2422
|
-
serviceId,
|
|
2417
|
+
await queueRuntimeAITRefresh({
|
|
2423
2418
|
controller: address,
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
setAit(aitResponse);
|
|
2429
|
-
}
|
|
2419
|
+
serviceId,
|
|
2420
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
2421
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
2422
|
+
});
|
|
2430
2423
|
}
|
|
2431
2424
|
}
|
|
2432
2425
|
finally {
|
|
@@ -2460,15 +2453,12 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2460
2453
|
const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
|
|
2461
2454
|
if (!('error' in walletResponse)) {
|
|
2462
2455
|
setWalletInfo(walletResponse);
|
|
2463
|
-
|
|
2464
|
-
serviceId,
|
|
2456
|
+
await queueRuntimeAITRefresh({
|
|
2465
2457
|
controller: address,
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
setAit(aitResponse);
|
|
2471
|
-
}
|
|
2458
|
+
serviceId,
|
|
2459
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
2460
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
2461
|
+
});
|
|
2472
2462
|
}
|
|
2473
2463
|
}
|
|
2474
2464
|
finally {
|
|
@@ -2580,7 +2570,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2580
2570
|
if (hitAddress && nxtlinqAITServiceAccessToken) {
|
|
2581
2571
|
refreshAIT();
|
|
2582
2572
|
}
|
|
2583
|
-
}, [hitAddress, nxtlinqAITServiceAccessToken]);
|
|
2573
|
+
}, [hitAddress, nxtlinqAITServiceAccessToken, permissionGroup, runtimeRolesKey, serviceId]);
|
|
2584
2574
|
// Set loading state when permission form opens
|
|
2585
2575
|
React.useEffect(() => {
|
|
2586
2576
|
if (isPermissionFormOpen && hitAddress) {
|
|
@@ -2660,6 +2650,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2660
2650
|
};
|
|
2661
2651
|
updateExternalId();
|
|
2662
2652
|
}, [hitAddress, nxtlinqApi, authFields, pseudoId]);
|
|
2653
|
+
const runtimeAuthorizationMatchesRenderedContext = runtimeAuthorizationMatchesContext(runtimeAuthorizationContextKey, runtimeContext);
|
|
2663
2654
|
const contextValue = {
|
|
2664
2655
|
// State
|
|
2665
2656
|
messages,
|
|
@@ -2667,9 +2658,16 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2667
2658
|
isLoading,
|
|
2668
2659
|
isOpen,
|
|
2669
2660
|
hitAddress,
|
|
2670
|
-
ait,
|
|
2671
|
-
permissions,
|
|
2672
|
-
|
|
2661
|
+
ait: runtimeAuthorizationMatchesRenderedContext ? ait : null,
|
|
2662
|
+
permissions: runtimeAuthorizationMatchesRenderedContext ? permissions : [],
|
|
2663
|
+
maximumPermissions: runtimeAuthorizationMatchesRenderedContext ? maximumPermissions : [],
|
|
2664
|
+
deniedPermissions: runtimeAuthorizationMatchesRenderedContext ? deniedPermissions : [],
|
|
2665
|
+
runtimePermissionsUnavailable: runtimeAuthorizationMatchesRenderedContext
|
|
2666
|
+
? runtimePermissionsUnavailable
|
|
2667
|
+
: runtimeContext !== null,
|
|
2668
|
+
availablePermissions: runtimeAuthorizationMatchesRenderedContext
|
|
2669
|
+
? availablePermissions
|
|
2670
|
+
: [],
|
|
2673
2671
|
showPermissionForm,
|
|
2674
2672
|
isPermissionFormOpen,
|
|
2675
2673
|
isAITLoading,
|
|
@@ -2705,7 +2703,6 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2705
2703
|
setIsOpen,
|
|
2706
2704
|
setShowPermissionForm,
|
|
2707
2705
|
setIsPermissionFormOpen,
|
|
2708
|
-
setPermissions,
|
|
2709
2706
|
setIsDisabled,
|
|
2710
2707
|
setIsWalletLoading,
|
|
2711
2708
|
setNotification,
|
|
@@ -2754,6 +2751,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2754
2751
|
onVerifyWallet: (method) => handleVerifyWalletClick(method),
|
|
2755
2752
|
serviceId,
|
|
2756
2753
|
permissionGroup,
|
|
2754
|
+
roles,
|
|
2757
2755
|
// Props
|
|
2758
2756
|
props: {
|
|
2759
2757
|
onMessage,
|
|
@@ -2771,6 +2769,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
|
|
|
2771
2769
|
getAuthToken,
|
|
2772
2770
|
onVerifyWallet,
|
|
2773
2771
|
permissionGroup,
|
|
2772
|
+
roles,
|
|
2774
2773
|
onModelChange,
|
|
2775
2774
|
storageMode,
|
|
2776
2775
|
requireWalletIDVVerification,
|