@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.4.0 → 2.0.0
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/context/ChatBotContext.d.ts.map +1 -1
- package/dist/context/ChatBotContext.js +179 -156
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/permissionState.d.ts +53 -0
- package/dist/permissionState.d.ts.map +1 -0
- package/dist/permissionState.js +217 -0
- package/dist/types/ChatBotTypes.d.ts +6 -5
- 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/package.json +3 -3
- package/src/context/ChatBotContext.tsx +252 -191
- package/src/index.ts +0 -1
- package/src/permissionState.ts +313 -0
- package/src/types/ChatBotTypes.ts +7 -7
- package/src/ui/ModelSelector.tsx +5 -5
- package/src/ui/PermissionForm.tsx +20 -31
- package/dist/NxtlinqAgentChat.d.ts +0 -26
- package/dist/NxtlinqAgentChat.d.ts.map +0 -1
- package/dist/NxtlinqAgentChat.js +0 -28
- package/dist/components/AgentAssistantShell.d.ts +0 -5
- package/dist/components/AgentAssistantShell.d.ts.map +0 -1
- package/dist/components/AgentAssistantShell.js +0 -52
- package/dist/components/AgentComposer.d.ts +0 -3
- package/dist/components/AgentComposer.d.ts.map +0 -1
- package/dist/components/AgentComposer.js +0 -60
- package/dist/components/AgentMessageList.d.ts +0 -3
- package/dist/components/AgentMessageList.d.ts.map +0 -1
- package/dist/components/AgentMessageList.js +0 -37
- package/dist/components/AgentRemoteAudio.d.ts +0 -4
- package/dist/components/AgentRemoteAudio.d.ts.map +0 -1
- package/dist/components/AgentRemoteAudio.js +0 -34
- package/dist/components/AgentVoiceBar.d.ts +0 -3
- package/dist/components/AgentVoiceBar.d.ts.map +0 -1
- package/dist/components/AgentVoiceBar.js +0 -91
- package/dist/components/PresetMessageChips.d.ts +0 -3
- package/dist/components/PresetMessageChips.d.ts.map +0 -1
- package/dist/components/PresetMessageChips.js +0 -23
- package/dist/context/AgentAssistantContext.d.ts +0 -32
- package/dist/context/AgentAssistantContext.d.ts.map +0 -1
- package/dist/context/AgentAssistantContext.js +0 -179
- package/dist/legacy/assets/images/adiSideItalicDataUri.d.ts +0 -2
- package/dist/legacy/assets/images/adiSideItalicDataUri.d.ts.map +0 -1
- package/dist/legacy/assets/images/adiSideItalicDataUri.js +0 -1
- package/dist/legacy/chatbot/ChatBot.d.ts +0 -5
- package/dist/legacy/chatbot/ChatBot.d.ts.map +0 -1
- package/dist/legacy/chatbot/ChatBot.js +0 -35
- package/dist/legacy/chatbot/context/ChatBotContext.d.ts +0 -5
- package/dist/legacy/chatbot/context/ChatBotContext.d.ts.map +0 -1
- package/dist/legacy/chatbot/context/ChatBotContext.js +0 -2922
- package/dist/legacy/chatbot/types/ChatBotTypes.d.ts +0 -166
- package/dist/legacy/chatbot/types/ChatBotTypes.d.ts.map +0 -1
- package/dist/legacy/chatbot/types/ChatBotTypes.js +0 -1
- package/dist/legacy/chatbot/ui/BerifyMeModal.d.ts +0 -17
- package/dist/legacy/chatbot/ui/BerifyMeModal.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/BerifyMeModal.js +0 -110
- package/dist/legacy/chatbot/ui/ChatBotHeader.d.ts +0 -15
- package/dist/legacy/chatbot/ui/ChatBotHeader.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/ChatBotHeader.js +0 -62
- package/dist/legacy/chatbot/ui/ChatBotUI.d.ts +0 -3
- package/dist/legacy/chatbot/ui/ChatBotUI.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/ChatBotUI.js +0 -557
- package/dist/legacy/chatbot/ui/MessageInput.d.ts +0 -3
- package/dist/legacy/chatbot/ui/MessageInput.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/MessageInput.js +0 -321
- package/dist/legacy/chatbot/ui/MessageList.d.ts +0 -4
- package/dist/legacy/chatbot/ui/MessageList.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/MessageList.js +0 -455
- package/dist/legacy/chatbot/ui/ModelSelector.d.ts +0 -4
- package/dist/legacy/chatbot/ui/ModelSelector.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/ModelSelector.js +0 -122
- package/dist/legacy/chatbot/ui/NotificationModal.d.ts +0 -15
- package/dist/legacy/chatbot/ui/NotificationModal.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/NotificationModal.js +0 -53
- package/dist/legacy/chatbot/ui/PermissionForm.d.ts +0 -8
- package/dist/legacy/chatbot/ui/PermissionForm.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/PermissionForm.js +0 -465
- package/dist/legacy/chatbot/ui/PresetMessages.d.ts +0 -4
- package/dist/legacy/chatbot/ui/PresetMessages.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/PresetMessages.js +0 -33
- package/dist/legacy/chatbot/ui/VoiceModePanel.d.ts +0 -3
- package/dist/legacy/chatbot/ui/VoiceModePanel.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/VoiceModePanel.js +0 -95
- package/dist/legacy/chatbot/ui/chatBotHeaderParts.d.ts +0 -15
- package/dist/legacy/chatbot/ui/chatBotHeaderParts.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/chatBotHeaderParts.js +0 -50
- package/dist/legacy/chatbot/ui/styles/isolatedStyles.d.ts +0 -73
- package/dist/legacy/chatbot/ui/styles/isolatedStyles.d.ts.map +0 -1
- package/dist/legacy/chatbot/ui/styles/isolatedStyles.js +0 -985
- package/dist/legacy/index.d.ts +0 -14
- package/dist/legacy/index.d.ts.map +0 -1
- package/dist/legacy/index.js +0 -12
- package/dist/theme/defaultTheme.d.ts +0 -3
- package/dist/theme/defaultTheme.d.ts.map +0 -1
- package/dist/theme/defaultTheme.js +0 -20
- package/dist/types.d.ts +0 -62
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
- package/dist/voice/useVoiceConnectOrchestration.d.ts +0 -21
- package/dist/voice/useVoiceConnectOrchestration.d.ts.map +0 -1
- package/dist/voice/useVoiceConnectOrchestration.js +0 -86
- package/dist/voice/useVoiceMicState.d.ts +0 -15
- package/dist/voice/useVoiceMicState.d.ts.map +0 -1
- package/dist/voice/useVoiceMicState.js +0 -94
- package/dist/voice/useVoiceSilenceCommit.d.ts +0 -10
- package/dist/voice/useVoiceSilenceCommit.d.ts.map +0 -1
- package/dist/voice/useVoiceSilenceCommit.js +0 -67
- package/dist/voice/useVoiceTranscriptMessages.d.ts +0 -18
- package/dist/voice/useVoiceTranscriptMessages.d.ts.map +0 -1
- package/dist/voice/useVoiceTranscriptMessages.js +0 -132
- package/dist/voice/useWsRealtimeAudio.d.ts +0 -18
- package/dist/voice/useWsRealtimeAudio.d.ts.map +0 -1
- package/dist/voice/useWsRealtimeAudio.js +0 -102
- package/dist/voice/voiceMicConstants.d.ts +0 -4
- package/dist/voice/voiceMicConstants.d.ts.map +0 -1
- package/dist/voice/voiceMicConstants.js +0 -10
- package/dist/voice/voiceUserBubble.d.ts +0 -10
- package/dist/voice/voiceUserBubble.d.ts.map +0 -1
- package/dist/voice/voiceUserBubble.js +0 -52
- package/dist/voice/ws/BrowserWsPcmPlayer.d.ts +0 -23
- package/dist/voice/ws/BrowserWsPcmPlayer.d.ts.map +0 -1
- package/dist/voice/ws/BrowserWsPcmPlayer.js +0 -137
- package/dist/voice/ws/BrowserWsPcmRecorder.d.ts +0 -17
- package/dist/voice/ws/BrowserWsPcmRecorder.d.ts.map +0 -1
- package/dist/voice/ws/BrowserWsPcmRecorder.js +0 -71
- package/dist/voice/ws/float32ToPcm16.d.ts +0 -2
- package/dist/voice/ws/float32ToPcm16.d.ts.map +0 -1
- package/dist/voice/ws/float32ToPcm16.js +0 -8
- package/dist/voice/ws/voiceSilenceConstants.d.ts +0 -5
- package/dist/voice/ws/voiceSilenceConstants.d.ts.map +0 -1
- package/dist/voice/ws/voiceSilenceConstants.js +0 -4
- package/dist/voice/ws/wsRealtimeConstants.d.ts +0 -2
- package/dist/voice/ws/wsRealtimeConstants.d.ts.map +0 -1
- package/dist/voice/ws/wsRealtimeConstants.js +0 -1
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
Attachment,
|
|
24
24
|
ClientTtsVoiceSettings,
|
|
25
25
|
Message,
|
|
26
|
+
RuntimeAITResult,
|
|
26
27
|
ServicePermission,
|
|
27
28
|
} from '@bytexbyte/nxtlinq-ai-agent-core-development';
|
|
28
29
|
import {
|
|
@@ -31,6 +32,22 @@ import {
|
|
|
31
32
|
ChatBotProps,
|
|
32
33
|
PresetMessage
|
|
33
34
|
} from '../types/ChatBotTypes';
|
|
35
|
+
import {
|
|
36
|
+
dispatchRuntimeAITCompleteReplacement,
|
|
37
|
+
removeOneDeniedPermission,
|
|
38
|
+
replaceCurrentMaximumDenyList,
|
|
39
|
+
RuntimeAITMutationQueue,
|
|
40
|
+
RuntimeAITRecomputeCoordinator,
|
|
41
|
+
StaleRuntimeAITResponseError,
|
|
42
|
+
runtimeAITContextKey,
|
|
43
|
+
runtimeAITSubjectKey,
|
|
44
|
+
runtimeAuthorizationMatchesContext,
|
|
45
|
+
runtimeAuthorizationState,
|
|
46
|
+
unavailableRuntimeAuthorization,
|
|
47
|
+
useProviderRuntimeCommit,
|
|
48
|
+
useRuntimeAITContextCommit,
|
|
49
|
+
type RuntimeAITContext,
|
|
50
|
+
} from '../permissionState';
|
|
34
51
|
|
|
35
52
|
const MIC_ENABLED_SESSION_KEY = 'chatbot-mic-enabled';
|
|
36
53
|
|
|
@@ -61,6 +78,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
61
78
|
environment = 'production',
|
|
62
79
|
onVerifyWallet,
|
|
63
80
|
permissionGroup,
|
|
81
|
+
roles,
|
|
64
82
|
children,
|
|
65
83
|
// AI Model related attributes
|
|
66
84
|
onModelChange,
|
|
@@ -85,9 +103,6 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
85
103
|
debugVoiceRms = false,
|
|
86
104
|
sttGlossary,
|
|
87
105
|
}) => {
|
|
88
|
-
// Set API hosts immediately based on environment (before any API calls)
|
|
89
|
-
setApiHosts(environment);
|
|
90
|
-
|
|
91
106
|
const nxtlinqApi = React.useMemo(
|
|
92
107
|
() => (serviceToken || getAuthToken
|
|
93
108
|
? createNxtlinqApi({ apiKey, apiSecret, serviceToken, getAuthToken })
|
|
@@ -147,7 +162,22 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
147
162
|
const [hitAddress, setHitAddress] = React.useState<string | null>(null);
|
|
148
163
|
const [ait, setAit] = React.useState<AIT | null>(null);
|
|
149
164
|
const [permissions, setPermissions] = React.useState<string[]>([]);
|
|
150
|
-
const [
|
|
165
|
+
const [maximumPermissions, setMaximumPermissions] = React.useState<string[]>([]);
|
|
166
|
+
const [deniedPermissions, setDeniedPermissions] = React.useState<string[]>([]);
|
|
167
|
+
const [runtimePermissionsUnavailable, setRuntimePermissionsUnavailable] = React.useState(false);
|
|
168
|
+
const [runtimeAuthorizationContextKey, setRuntimeAuthorizationContextKey] =
|
|
169
|
+
React.useState<string | null>(null);
|
|
170
|
+
const [servicePermissionCatalog, setServicePermissionCatalog] = React.useState<ServicePermission[]>([]);
|
|
171
|
+
const availablePermissions = React.useMemo(
|
|
172
|
+
() => maximumPermissions.map((label) =>
|
|
173
|
+
servicePermissionCatalog.find((permission) => permission.label === label) ?? {
|
|
174
|
+
id: label,
|
|
175
|
+
label,
|
|
176
|
+
description: '',
|
|
177
|
+
groups: [],
|
|
178
|
+
}),
|
|
179
|
+
[maximumPermissions, servicePermissionCatalog],
|
|
180
|
+
);
|
|
151
181
|
const [showPermissionForm, setShowPermissionForm] = React.useState(false);
|
|
152
182
|
const [isPermissionFormOpen, setIsPermissionFormOpen] = React.useState(false);
|
|
153
183
|
const [isAITLoading, setIsAITLoading] = React.useState(false);
|
|
@@ -156,6 +186,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
156
186
|
const [walletInfo, setWalletInfo] = React.useState<any>(null);
|
|
157
187
|
const [isWalletLoading, setIsWalletLoading] = React.useState(false);
|
|
158
188
|
const [isAutoConnecting, setIsAutoConnecting] = React.useState(false);
|
|
189
|
+
const runtimeRolesKey = JSON.stringify(roles ?? null);
|
|
159
190
|
|
|
160
191
|
// Persistent data (always use localStorage)
|
|
161
192
|
const [nxtlinqAITServiceAccessToken, setNxtlinqAITServiceAccessToken] = useLocalStorage<string>('nxtlinqAITServiceAccessToken', '');
|
|
@@ -212,8 +243,17 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
212
243
|
const hitAddressRef = React.useRef(hitAddress);
|
|
213
244
|
const aitRef = React.useRef(ait);
|
|
214
245
|
const permissionsRef = React.useRef(permissions);
|
|
246
|
+
const deniedPermissionsRef = React.useRef(deniedPermissions);
|
|
215
247
|
const nxtlinqAITServiceAccessTokenRef = React.useRef(nxtlinqAITServiceAccessToken);
|
|
216
248
|
const signerRef = React.useRef(signer);
|
|
249
|
+
const runtimeRecomputeCoordinatorRef = React.useRef(new RuntimeAITRecomputeCoordinator());
|
|
250
|
+
const runtimeMutationQueueRef = React.useRef(new RuntimeAITMutationQueue());
|
|
251
|
+
useProviderRuntimeCommit(
|
|
252
|
+
hitAddressRef,
|
|
253
|
+
hitAddress,
|
|
254
|
+
environment,
|
|
255
|
+
setApiHosts,
|
|
256
|
+
);
|
|
217
257
|
|
|
218
258
|
// Helper function to combine customUsername with Adilas customUserInfo
|
|
219
259
|
const getFinalCustomUsername = React.useCallback((username: string | undefined): string | undefined => {
|
|
@@ -546,10 +586,6 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
546
586
|
}, []);
|
|
547
587
|
|
|
548
588
|
// Update refs when state changes
|
|
549
|
-
React.useEffect(() => {
|
|
550
|
-
hitAddressRef.current = hitAddress;
|
|
551
|
-
}, [hitAddress]);
|
|
552
|
-
|
|
553
589
|
React.useEffect(() => {
|
|
554
590
|
aitRef.current = ait;
|
|
555
591
|
}, [ait]);
|
|
@@ -558,6 +594,10 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
558
594
|
permissionsRef.current = permissions;
|
|
559
595
|
}, [permissions]);
|
|
560
596
|
|
|
597
|
+
React.useEffect(() => {
|
|
598
|
+
deniedPermissionsRef.current = deniedPermissions;
|
|
599
|
+
}, [deniedPermissions]);
|
|
600
|
+
|
|
561
601
|
React.useEffect(() => {
|
|
562
602
|
nxtlinqAITServiceAccessTokenRef.current = nxtlinqAITServiceAccessToken;
|
|
563
603
|
}, [nxtlinqAITServiceAccessToken]);
|
|
@@ -979,7 +1019,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
979
1019
|
console.error('Failed to fetch permissions:', result.error);
|
|
980
1020
|
return undefined;
|
|
981
1021
|
}
|
|
982
|
-
|
|
1022
|
+
setServicePermissionCatalog(result.permissions);
|
|
983
1023
|
return result.permissions;
|
|
984
1024
|
} catch (error) {
|
|
985
1025
|
console.error('Error fetching permissions:', error);
|
|
@@ -987,12 +1027,116 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
987
1027
|
}
|
|
988
1028
|
};
|
|
989
1029
|
|
|
990
|
-
|
|
1030
|
+
const clearRuntimeAuthorization = React.useCallback(() => {
|
|
1031
|
+
const cleared = unavailableRuntimeAuthorization(deniedPermissionsRef.current);
|
|
1032
|
+
const activeContext = runtimeRecomputeCoordinatorRef.current.getActiveContext();
|
|
1033
|
+
aitRef.current = null;
|
|
1034
|
+
permissionsRef.current = [];
|
|
1035
|
+
setAit(cleared.ait);
|
|
1036
|
+
setPermissions(cleared.effectivePermissions);
|
|
1037
|
+
setMaximumPermissions(cleared.maximumPermissions);
|
|
1038
|
+
setRuntimePermissionsUnavailable(cleared.unavailable);
|
|
1039
|
+
setRuntimeAuthorizationContextKey(
|
|
1040
|
+
activeContext ? runtimeAITContextKey(activeContext) : null,
|
|
1041
|
+
);
|
|
1042
|
+
}, []);
|
|
1043
|
+
|
|
1044
|
+
const applyRuntimeResult = React.useCallback((result: RuntimeAITResult, context: RuntimeAITContext) => {
|
|
1045
|
+
const next = runtimeAuthorizationState(result, context.controller, context.serviceId);
|
|
1046
|
+
aitRef.current = next.ait;
|
|
1047
|
+
permissionsRef.current = next.effectivePermissions;
|
|
1048
|
+
setAit(next.ait);
|
|
1049
|
+
setPermissions(next.effectivePermissions);
|
|
1050
|
+
setMaximumPermissions(next.maximumPermissions);
|
|
1051
|
+
setDeniedPermissions(next.deniedPermissions);
|
|
1052
|
+
deniedPermissionsRef.current = next.deniedPermissions;
|
|
1053
|
+
setRuntimePermissionsUnavailable(false);
|
|
1054
|
+
setRuntimeAuthorizationContextKey(runtimeAITContextKey(context));
|
|
1055
|
+
return result;
|
|
1056
|
+
}, []);
|
|
1057
|
+
|
|
1058
|
+
const runtimeContext = React.useMemo<RuntimeAITContext | null>(() => hitAddress ? {
|
|
1059
|
+
controller: hitAddress,
|
|
1060
|
+
serviceId,
|
|
1061
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
1062
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
1063
|
+
} : null, [hitAddress, permissionGroup, runtimeRolesKey, serviceId]);
|
|
1064
|
+
const invalidateCommittedRuntimeContext = React.useCallback(() => {
|
|
1065
|
+
const activeContext = runtimeRecomputeCoordinatorRef.current.getActiveContext();
|
|
1066
|
+
aitRef.current = null;
|
|
1067
|
+
permissionsRef.current = [];
|
|
1068
|
+
deniedPermissionsRef.current = [];
|
|
1069
|
+
setAit(null);
|
|
1070
|
+
setPermissions([]);
|
|
1071
|
+
setMaximumPermissions([]);
|
|
1072
|
+
setDeniedPermissions([]);
|
|
1073
|
+
setRuntimePermissionsUnavailable(activeContext !== null);
|
|
1074
|
+
setRuntimeAuthorizationContextKey(
|
|
1075
|
+
activeContext ? runtimeAITContextKey(activeContext) : null,
|
|
1076
|
+
);
|
|
1077
|
+
}, []);
|
|
1078
|
+
useRuntimeAITContextCommit(
|
|
1079
|
+
runtimeRecomputeCoordinatorRef.current,
|
|
1080
|
+
runtimeContext,
|
|
1081
|
+
invalidateCommittedRuntimeContext,
|
|
1082
|
+
);
|
|
1083
|
+
|
|
1084
|
+
const recomputeRuntimeAIT = React.useCallback(async (context: RuntimeAITContext) => {
|
|
1085
|
+
const capturedContext: RuntimeAITContext = {
|
|
1086
|
+
controller: context.controller,
|
|
1087
|
+
serviceId: context.serviceId,
|
|
1088
|
+
...(context.roles !== undefined ? { roles: [...context.roles] } : {}),
|
|
1089
|
+
...(context.permissionGroup !== undefined ? { permissionGroup: context.permissionGroup } : {}),
|
|
1090
|
+
};
|
|
1091
|
+
return runtimeRecomputeCoordinatorRef.current.run(
|
|
1092
|
+
capturedContext,
|
|
1093
|
+
() => nxtlinqApi.ait.recomputeAIT(capturedContext),
|
|
1094
|
+
applyRuntimeResult,
|
|
1095
|
+
clearRuntimeAuthorization,
|
|
1096
|
+
);
|
|
1097
|
+
}, [applyRuntimeResult, clearRuntimeAuthorization, nxtlinqApi]);
|
|
1098
|
+
|
|
1099
|
+
const queueRuntimeAITRefresh = React.useCallback(
|
|
1100
|
+
(context: RuntimeAITContext) => runtimeMutationQueueRef.current.run(
|
|
1101
|
+
runtimeAITSubjectKey(context),
|
|
1102
|
+
async () => {
|
|
1103
|
+
runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
|
|
1104
|
+
return recomputeRuntimeAIT(context);
|
|
1105
|
+
},
|
|
1106
|
+
),
|
|
1107
|
+
[recomputeRuntimeAIT],
|
|
1108
|
+
);
|
|
1109
|
+
|
|
1110
|
+
const convergeRuntimeAITSubject = React.useCallback(async (subjectKey: string) => {
|
|
1111
|
+
const activeContext = runtimeRecomputeCoordinatorRef.current.getActiveContext();
|
|
1112
|
+
if (!activeContext || runtimeAITSubjectKey(activeContext) !== subjectKey) {
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
await recomputeRuntimeAIT(activeContext);
|
|
1116
|
+
}, [recomputeRuntimeAIT]);
|
|
1117
|
+
|
|
1118
|
+
const dispatchRuntimeDenyListReplacement = React.useCallback(async (
|
|
1119
|
+
context: RuntimeAITContext,
|
|
1120
|
+
nextDeniedPermissions: string[],
|
|
1121
|
+
) => dispatchRuntimeAITCompleteReplacement(
|
|
1122
|
+
runtimeRecomputeCoordinatorRef.current,
|
|
1123
|
+
context,
|
|
1124
|
+
() => nxtlinqApi.ait.updatePermissionDenyList({
|
|
1125
|
+
...context,
|
|
1126
|
+
deniedPermissions: nextDeniedPermissions,
|
|
1127
|
+
}),
|
|
1128
|
+
convergeRuntimeAITSubject,
|
|
1129
|
+
clearRuntimeAuthorization,
|
|
1130
|
+
), [clearRuntimeAuthorization, convergeRuntimeAITSubject, nxtlinqApi]);
|
|
1131
|
+
|
|
1132
|
+
// Refresh the authoritative runtime AIT state.
|
|
991
1133
|
const refreshAIT = async (forceUpdatePermissions = false) => {
|
|
992
1134
|
if (!hitAddress) {
|
|
993
1135
|
setAit(null);
|
|
994
1136
|
setPermissions([]);
|
|
1137
|
+
setMaximumPermissions([]);
|
|
995
1138
|
setWalletInfo(null);
|
|
1139
|
+
setRuntimePermissionsUnavailable(false);
|
|
996
1140
|
setIsAITLoading(false);
|
|
997
1141
|
return;
|
|
998
1142
|
}
|
|
@@ -1017,35 +1161,18 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1017
1161
|
}
|
|
1018
1162
|
}
|
|
1019
1163
|
|
|
1020
|
-
//
|
|
1164
|
+
// Recompute is the sole mint/reuse path and the only permission authority.
|
|
1021
1165
|
if (nxtlinqAITServiceAccessToken) {
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
controller: hitAddress,
|
|
1025
|
-
customUsername: (!requireWalletIDVVerification && customUsername) ? getFinalCustomUsername(customUsername) : undefined
|
|
1026
|
-
}, nxtlinqAITServiceAccessToken);
|
|
1027
|
-
|
|
1028
|
-
if ('error' in response) {
|
|
1029
|
-
console.error('Failed to fetch AIT:', response.error);
|
|
1030
|
-
setAit(null);
|
|
1031
|
-
setPermissions([]);
|
|
1032
|
-
return;
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
setAit(response);
|
|
1036
|
-
if (!isPermissionFormOpen || forceUpdatePermissions) {
|
|
1037
|
-
const newPermissions = response.metadata?.permissions || [];
|
|
1038
|
-
setPermissions(newPermissions);
|
|
1166
|
+
if (runtimeContext) {
|
|
1167
|
+
await queueRuntimeAITRefresh(runtimeContext);
|
|
1039
1168
|
}
|
|
1040
1169
|
} else {
|
|
1041
|
-
|
|
1042
|
-
setAit(null);
|
|
1043
|
-
setPermissions([]);
|
|
1170
|
+
clearRuntimeAuthorization();
|
|
1044
1171
|
}
|
|
1045
1172
|
} catch (error) {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1173
|
+
if (!(error instanceof StaleRuntimeAITResponseError)) {
|
|
1174
|
+
console.error('Failed to recompute AIT:', error);
|
|
1175
|
+
}
|
|
1049
1176
|
} finally {
|
|
1050
1177
|
setIsAITLoading(false);
|
|
1051
1178
|
}
|
|
@@ -1250,10 +1377,13 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
1250
1377
|
}
|
|
1251
1378
|
const { accessToken } = response;
|
|
1252
1379
|
setNxtlinqAITServiceAccessToken(accessToken);
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1380
|
+
localStorage.setItem('nxtlinqAITServiceAccessToken', JSON.stringify(accessToken));
|
|
1381
|
+
await queueRuntimeAITRefresh({
|
|
1382
|
+
controller: addressToUse,
|
|
1383
|
+
serviceId,
|
|
1384
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
1385
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
1386
|
+
});
|
|
1257
1387
|
|
|
1258
1388
|
if (autoShowSuccessMessage) {
|
|
1259
1389
|
showSuccess(walletTextUtils.getWalletText('Successfully signed in with your HIT wallet. You can now use the AI agent.', serviceId));
|
|
@@ -2717,105 +2847,6 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
2717
2847
|
}
|
|
2718
2848
|
};
|
|
2719
2849
|
|
|
2720
|
-
// Generate and register AIT
|
|
2721
|
-
const generateAndRegisterAIT = async (newPermissions?: string[], isFromAIAgent = false) => {
|
|
2722
|
-
let currentSigner = signer;
|
|
2723
|
-
let currentAddress = hitAddress;
|
|
2724
|
-
|
|
2725
|
-
// If we have an address but no signer, try to auto-connect wallet
|
|
2726
|
-
if (currentAddress && !currentSigner) {
|
|
2727
|
-
const storedWalletAddress = localStorage.getItem('walletAddress');
|
|
2728
|
-
if (storedWalletAddress === currentAddress) {
|
|
2729
|
-
try {
|
|
2730
|
-
const autoConnectResult = await connectWallet(false); // Don't show sign-in message yet
|
|
2731
|
-
if (autoConnectResult) {
|
|
2732
|
-
// Wait for state to update
|
|
2733
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
2734
|
-
// Get the updated signer and address
|
|
2735
|
-
currentSigner = signerRef.current || signer;
|
|
2736
|
-
currentAddress = hitAddressRef.current || hitAddress;
|
|
2737
|
-
}
|
|
2738
|
-
} catch (error) {
|
|
2739
|
-
console.error('Auto-connect failed during AIT generation:', error);
|
|
2740
|
-
}
|
|
2741
|
-
}
|
|
2742
|
-
}
|
|
2743
|
-
|
|
2744
|
-
if (!currentSigner || !currentAddress) {
|
|
2745
|
-
throw new Error('Missing signer or wallet address');
|
|
2746
|
-
}
|
|
2747
|
-
|
|
2748
|
-
return generateAndRegisterAITWithSigner(newPermissions, isFromAIAgent, currentSigner, currentAddress);
|
|
2749
|
-
};
|
|
2750
|
-
|
|
2751
|
-
// Generate and register AIT with explicit signer and address
|
|
2752
|
-
const generateAndRegisterAITWithSigner = async (
|
|
2753
|
-
newPermissions?: string[],
|
|
2754
|
-
isFromAIAgent = false,
|
|
2755
|
-
explicitSigner?: ethers.JsonRpcSigner,
|
|
2756
|
-
explicitAddress?: string
|
|
2757
|
-
) => {
|
|
2758
|
-
const currentSigner = explicitSigner || signer;
|
|
2759
|
-
const currentAddress = explicitAddress || hitAddress;
|
|
2760
|
-
|
|
2761
|
-
if (!currentSigner || !currentAddress) {
|
|
2762
|
-
throw new Error('Missing signer or wallet address');
|
|
2763
|
-
}
|
|
2764
|
-
|
|
2765
|
-
// If AI Agent is creating, must check if user has existing AIT
|
|
2766
|
-
if (isFromAIAgent && !aitRef.current) {
|
|
2767
|
-
throw new Error('You must have an existing AIT before AI Agent can create new AITs');
|
|
2768
|
-
}
|
|
2769
|
-
|
|
2770
|
-
const timestamp = Math.floor(Date.now() / 1000);
|
|
2771
|
-
const aitId = `did:polygon:nxtlinq:${currentAddress}:${timestamp}`;
|
|
2772
|
-
|
|
2773
|
-
const metadata = {
|
|
2774
|
-
permissions: newPermissions || permissions,
|
|
2775
|
-
issuedBy: currentAddress,
|
|
2776
|
-
};
|
|
2777
|
-
|
|
2778
|
-
const metadataStr = stringify(metadata);
|
|
2779
|
-
// ethers v6: utils.keccak256 -> keccak256, utils.toUtf8Bytes -> toUtf8Bytes
|
|
2780
|
-
const metadataHash = ethers.keccak256(ethers.toUtf8Bytes(metadataStr));
|
|
2781
|
-
|
|
2782
|
-
const uploadResponse = await nxtlinqApi.metadata.createMetadata(metadata, nxtlinqAITServiceAccessToken || '');
|
|
2783
|
-
if ('error' in uploadResponse) {
|
|
2784
|
-
throw new Error(`Failed to upload metadata: ${uploadResponse.error}`);
|
|
2785
|
-
}
|
|
2786
|
-
|
|
2787
|
-
const { metadataCid } = uploadResponse;
|
|
2788
|
-
|
|
2789
|
-
const createAITParams = {
|
|
2790
|
-
aitId,
|
|
2791
|
-
controller: currentAddress,
|
|
2792
|
-
serviceId,
|
|
2793
|
-
metadataHash,
|
|
2794
|
-
metadataCid,
|
|
2795
|
-
isFromAIAgent: isFromAIAgent,
|
|
2796
|
-
parentAITId: isFromAIAgent ? aitRef.current?.aitId : undefined,
|
|
2797
|
-
};
|
|
2798
|
-
|
|
2799
|
-
const createAITResponse = await nxtlinqApi.ait.createAIT(createAITParams, nxtlinqAITServiceAccessToken || '');
|
|
2800
|
-
|
|
2801
|
-
if ('error' in createAITResponse) {
|
|
2802
|
-
throw new Error(`Failed to create AIT: ${createAITResponse.error}`);
|
|
2803
|
-
}
|
|
2804
|
-
|
|
2805
|
-
const aitInfo = {
|
|
2806
|
-
aitId,
|
|
2807
|
-
controller: currentAddress,
|
|
2808
|
-
metadata,
|
|
2809
|
-
metadataHash,
|
|
2810
|
-
metadataCid,
|
|
2811
|
-
};
|
|
2812
|
-
|
|
2813
|
-
setAit(aitInfo);
|
|
2814
|
-
setPermissions(newPermissions || permissions);
|
|
2815
|
-
};
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
2850
|
// Auto enable AIT permission
|
|
2820
2851
|
const enableAIT = async (toolName: string) => {
|
|
2821
2852
|
if (isAITEnabling) return false; // Prevent duplicate
|
|
@@ -2882,40 +2913,45 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
2882
2913
|
}
|
|
2883
2914
|
|
|
2884
2915
|
try {
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2916
|
+
const context: RuntimeAITContext = {
|
|
2917
|
+
controller: currentHitAddress,
|
|
2918
|
+
serviceId,
|
|
2919
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
2920
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
2921
|
+
};
|
|
2922
|
+
const enabled = await runtimeMutationQueueRef.current.run(
|
|
2923
|
+
runtimeAITSubjectKey(context),
|
|
2924
|
+
async () => {
|
|
2925
|
+
runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
|
|
2926
|
+
const current = await recomputeRuntimeAIT(context);
|
|
2927
|
+
if (!current.maximumPermissions.includes(toolName)) {
|
|
2928
|
+
return false;
|
|
2929
|
+
}
|
|
2930
|
+
const nextDeniedPermissions = removeOneDeniedPermission(
|
|
2931
|
+
current.deniedPermissions,
|
|
2932
|
+
toolName,
|
|
2933
|
+
);
|
|
2934
|
+
runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
|
|
2935
|
+
await dispatchRuntimeDenyListReplacement(context, nextDeniedPermissions);
|
|
2936
|
+
return true;
|
|
2937
|
+
},
|
|
2938
|
+
);
|
|
2939
|
+
if (!enabled) {
|
|
2888
2940
|
showError(`Tool ${toolName} is not available for your current identity provider`);
|
|
2889
2941
|
setIsAITEnabling(false);
|
|
2890
2942
|
return false;
|
|
2891
2943
|
}
|
|
2892
|
-
|
|
2893
|
-
// Get current permissions from AIT metadata instead of React state
|
|
2894
|
-
// This ensures we don't lose existing permissions when enabling new ones
|
|
2895
|
-
const currentAITPermissions = aitRef.current?.metadata?.permissions || permissions;
|
|
2896
|
-
const newPermissions = [...currentAITPermissions];
|
|
2897
|
-
if (!newPermissions.includes(toolName)) {
|
|
2898
|
-
newPermissions.push(toolName);
|
|
2899
|
-
}
|
|
2900
|
-
|
|
2901
|
-
// Filter out deleted permissions before saving
|
|
2902
|
-
const validPermissions = newPermissions.filter(permission =>
|
|
2903
|
-
availablePermissionLabels.includes(permission)
|
|
2904
|
-
);
|
|
2905
|
-
|
|
2906
|
-
// Generate and register AIT with new permissions
|
|
2907
|
-
// For auto-enable, we should create a regular AIT (not AI Agent AIT) if user doesn't have existing AIT
|
|
2908
|
-
const shouldCreateAsAIAgent = !!aitRef.current; // Only create as AI Agent if user already has an AIT
|
|
2909
|
-
|
|
2910
|
-
await generateAndRegisterAITWithSigner(validPermissions, shouldCreateAsAIAgent, currentSigner, currentHitAddress);
|
|
2911
2944
|
showSuccess('AIT permission enabled successfully! You can now use the AI agent.');
|
|
2912
|
-
await refreshAIT(true);
|
|
2913
2945
|
setIsAITEnabling(false);
|
|
2914
2946
|
return true;
|
|
2915
2947
|
} catch (error) {
|
|
2916
|
-
|
|
2948
|
+
if (!(error instanceof StaleRuntimeAITResponseError)) {
|
|
2949
|
+
console.error('Failed to auto-enable AIT:', error);
|
|
2950
|
+
}
|
|
2917
2951
|
setIsAITEnabling(false);
|
|
2918
|
-
if (error instanceof
|
|
2952
|
+
if (error instanceof StaleRuntimeAITResponseError) {
|
|
2953
|
+
return false;
|
|
2954
|
+
} else if (error instanceof Error) {
|
|
2919
2955
|
showError(error.message);
|
|
2920
2956
|
} else {
|
|
2921
2957
|
showError('Failed to enable AIT permission. Please try again.');
|
|
@@ -2928,22 +2964,41 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
2928
2964
|
const savePermissions = async (newPermissions?: string[]) => {
|
|
2929
2965
|
setIsDisabled(true);
|
|
2930
2966
|
try {
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
const
|
|
2935
|
-
|
|
2967
|
+
if (!hitAddress) {
|
|
2968
|
+
throw new Error('Please connect your wallet first');
|
|
2969
|
+
}
|
|
2970
|
+
const context: RuntimeAITContext = {
|
|
2971
|
+
controller: hitAddress,
|
|
2972
|
+
serviceId,
|
|
2973
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
2974
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
2975
|
+
};
|
|
2976
|
+
const checkedPermissions = [...(newPermissions ?? permissions)];
|
|
2977
|
+
await runtimeMutationQueueRef.current.run(
|
|
2978
|
+
runtimeAITSubjectKey(context),
|
|
2979
|
+
async () => {
|
|
2980
|
+
runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
|
|
2981
|
+
const fresh = await recomputeRuntimeAIT(context);
|
|
2982
|
+
const nextDeniedPermissions = replaceCurrentMaximumDenyList(
|
|
2983
|
+
fresh.deniedPermissions,
|
|
2984
|
+
fresh.maximumPermissions,
|
|
2985
|
+
checkedPermissions,
|
|
2986
|
+
);
|
|
2987
|
+
runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
|
|
2988
|
+
await dispatchRuntimeDenyListReplacement(context, nextDeniedPermissions);
|
|
2989
|
+
},
|
|
2936
2990
|
);
|
|
2937
|
-
|
|
2938
|
-
await generateAndRegisterAIT(validPermissions, false);
|
|
2939
2991
|
showSuccess('AIT permissions saved successfully! You can now use the AI agent with your configured permissions.');
|
|
2940
2992
|
setShowPermissionForm(false);
|
|
2941
2993
|
setIsPermissionFormOpen(false);
|
|
2942
|
-
await refreshAIT(true);
|
|
2943
2994
|
} catch (error) {
|
|
2944
|
-
|
|
2995
|
+
if (!(error instanceof StaleRuntimeAITResponseError)) {
|
|
2996
|
+
console.error('Failed to update AIT deny-list:', error);
|
|
2997
|
+
}
|
|
2945
2998
|
setIsDisabled(false);
|
|
2946
|
-
if (error instanceof
|
|
2999
|
+
if (error instanceof StaleRuntimeAITResponseError) {
|
|
3000
|
+
return;
|
|
3001
|
+
} else if (error instanceof Error) {
|
|
2947
3002
|
showError(error.message);
|
|
2948
3003
|
} else {
|
|
2949
3004
|
showError('Failed to save permissions. Please try again.');
|
|
@@ -2992,14 +3047,12 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
2992
3047
|
const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
|
|
2993
3048
|
if (!('error' in walletResponse)) {
|
|
2994
3049
|
setWalletInfo(walletResponse);
|
|
2995
|
-
|
|
2996
|
-
serviceId,
|
|
3050
|
+
await queueRuntimeAITRefresh({
|
|
2997
3051
|
controller: address,
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
}
|
|
3052
|
+
serviceId,
|
|
3053
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
3054
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
3055
|
+
});
|
|
3003
3056
|
}
|
|
3004
3057
|
} finally {
|
|
3005
3058
|
setIsWalletLoading(false);
|
|
@@ -3030,14 +3083,12 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
3030
3083
|
const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
|
|
3031
3084
|
if (!('error' in walletResponse)) {
|
|
3032
3085
|
setWalletInfo(walletResponse);
|
|
3033
|
-
|
|
3034
|
-
serviceId,
|
|
3086
|
+
await queueRuntimeAITRefresh({
|
|
3035
3087
|
controller: address,
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
}
|
|
3088
|
+
serviceId,
|
|
3089
|
+
...(roles !== undefined ? { roles: [...roles] } : {}),
|
|
3090
|
+
...(permissionGroup !== undefined ? { permissionGroup } : {}),
|
|
3091
|
+
});
|
|
3041
3092
|
}
|
|
3042
3093
|
} finally {
|
|
3043
3094
|
setIsWalletLoading(false);
|
|
@@ -3144,7 +3195,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
3144
3195
|
if (hitAddress && nxtlinqAITServiceAccessToken) {
|
|
3145
3196
|
refreshAIT();
|
|
3146
3197
|
}
|
|
3147
|
-
}, [hitAddress, nxtlinqAITServiceAccessToken]);
|
|
3198
|
+
}, [hitAddress, nxtlinqAITServiceAccessToken, permissionGroup, runtimeRolesKey, serviceId]);
|
|
3148
3199
|
|
|
3149
3200
|
// Set loading state when permission form opens
|
|
3150
3201
|
React.useEffect(() => {
|
|
@@ -3232,6 +3283,8 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
3232
3283
|
updateExternalId()
|
|
3233
3284
|
}, [hitAddress, nxtlinqApi, authFields, pseudoId]);
|
|
3234
3285
|
|
|
3286
|
+
const runtimeAuthorizationMatchesRenderedContext =
|
|
3287
|
+
runtimeAuthorizationMatchesContext(runtimeAuthorizationContextKey, runtimeContext);
|
|
3235
3288
|
const contextValue: ChatBotContextType = {
|
|
3236
3289
|
// State
|
|
3237
3290
|
messages,
|
|
@@ -3239,9 +3292,16 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
3239
3292
|
isLoading,
|
|
3240
3293
|
isOpen,
|
|
3241
3294
|
hitAddress,
|
|
3242
|
-
ait,
|
|
3243
|
-
permissions,
|
|
3244
|
-
|
|
3295
|
+
ait: runtimeAuthorizationMatchesRenderedContext ? ait : null,
|
|
3296
|
+
permissions: runtimeAuthorizationMatchesRenderedContext ? permissions : [],
|
|
3297
|
+
maximumPermissions: runtimeAuthorizationMatchesRenderedContext ? maximumPermissions : [],
|
|
3298
|
+
deniedPermissions: runtimeAuthorizationMatchesRenderedContext ? deniedPermissions : [],
|
|
3299
|
+
runtimePermissionsUnavailable: runtimeAuthorizationMatchesRenderedContext
|
|
3300
|
+
? runtimePermissionsUnavailable
|
|
3301
|
+
: runtimeContext !== null,
|
|
3302
|
+
availablePermissions: runtimeAuthorizationMatchesRenderedContext
|
|
3303
|
+
? availablePermissions
|
|
3304
|
+
: [],
|
|
3245
3305
|
showPermissionForm,
|
|
3246
3306
|
isPermissionFormOpen,
|
|
3247
3307
|
isAITLoading,
|
|
@@ -3278,7 +3338,6 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
3278
3338
|
setIsOpen,
|
|
3279
3339
|
setShowPermissionForm,
|
|
3280
3340
|
setIsPermissionFormOpen,
|
|
3281
|
-
setPermissions,
|
|
3282
3341
|
setIsDisabled,
|
|
3283
3342
|
setIsWalletLoading,
|
|
3284
3343
|
setNotification,
|
|
@@ -3328,6 +3387,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
3328
3387
|
onVerifyWallet: (method: 'berifyme' | 'custom') => handleVerifyWalletClick(method),
|
|
3329
3388
|
serviceId,
|
|
3330
3389
|
permissionGroup,
|
|
3390
|
+
roles,
|
|
3331
3391
|
|
|
3332
3392
|
// Props
|
|
3333
3393
|
props: {
|
|
@@ -3346,6 +3406,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
|
|
|
3346
3406
|
getAuthToken,
|
|
3347
3407
|
onVerifyWallet,
|
|
3348
3408
|
permissionGroup,
|
|
3409
|
+
roles,
|
|
3349
3410
|
onModelChange,
|
|
3350
3411
|
storageMode,
|
|
3351
3412
|
requireWalletIDVVerification,
|