@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.4.0 → 0.4.2

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.
Files changed (146) hide show
  1. package/dist/context/ChatBotContext.d.ts.map +1 -1
  2. package/dist/context/ChatBotContext.js +84 -405
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/permissionState.d.ts +53 -0
  7. package/dist/permissionState.d.ts.map +1 -0
  8. package/dist/permissionState.js +217 -0
  9. package/dist/types/ChatBotTypes.d.ts +0 -5
  10. package/dist/types/ChatBotTypes.d.ts.map +1 -1
  11. package/dist/ui/ChatBotHeader.d.ts.map +1 -1
  12. package/dist/ui/ChatBotHeader.js +6 -2
  13. package/dist/ui/ChatBotUI.d.ts.map +1 -1
  14. package/dist/ui/ChatBotUI.js +13 -4
  15. package/dist/ui/MessageList.d.ts.map +1 -1
  16. package/dist/ui/MessageList.js +11 -6
  17. package/dist/ui/PermissionForm.d.ts.map +1 -1
  18. package/dist/ui/PermissionForm.js +6 -9
  19. package/dist/ui/styles/isolatedStyles.d.ts.map +1 -1
  20. package/dist/ui/styles/isolatedStyles.js +1 -0
  21. package/package.json +3 -3
  22. package/src/context/ChatBotContext.tsx +92 -437
  23. package/src/index.ts +5 -0
  24. package/src/types/ChatBotTypes.ts +2 -6
  25. package/src/ui/ChatBotHeader.tsx +9 -3
  26. package/src/ui/ChatBotUI.tsx +14 -5
  27. package/src/ui/MessageList.tsx +6 -0
  28. package/src/ui/PermissionForm.tsx +7 -12
  29. package/src/ui/styles/isolatedStyles.ts +1 -0
  30. package/dist/NxtlinqAgentChat.d.ts +0 -26
  31. package/dist/NxtlinqAgentChat.d.ts.map +0 -1
  32. package/dist/NxtlinqAgentChat.js +0 -28
  33. package/dist/components/AgentAssistantShell.d.ts +0 -5
  34. package/dist/components/AgentAssistantShell.d.ts.map +0 -1
  35. package/dist/components/AgentAssistantShell.js +0 -52
  36. package/dist/components/AgentComposer.d.ts +0 -3
  37. package/dist/components/AgentComposer.d.ts.map +0 -1
  38. package/dist/components/AgentComposer.js +0 -60
  39. package/dist/components/AgentMessageList.d.ts +0 -3
  40. package/dist/components/AgentMessageList.d.ts.map +0 -1
  41. package/dist/components/AgentMessageList.js +0 -37
  42. package/dist/components/AgentRemoteAudio.d.ts +0 -4
  43. package/dist/components/AgentRemoteAudio.d.ts.map +0 -1
  44. package/dist/components/AgentRemoteAudio.js +0 -34
  45. package/dist/components/AgentVoiceBar.d.ts +0 -3
  46. package/dist/components/AgentVoiceBar.d.ts.map +0 -1
  47. package/dist/components/AgentVoiceBar.js +0 -91
  48. package/dist/components/PresetMessageChips.d.ts +0 -3
  49. package/dist/components/PresetMessageChips.d.ts.map +0 -1
  50. package/dist/components/PresetMessageChips.js +0 -23
  51. package/dist/context/AgentAssistantContext.d.ts +0 -32
  52. package/dist/context/AgentAssistantContext.d.ts.map +0 -1
  53. package/dist/context/AgentAssistantContext.js +0 -179
  54. package/dist/legacy/assets/images/adiSideItalicDataUri.d.ts +0 -2
  55. package/dist/legacy/assets/images/adiSideItalicDataUri.d.ts.map +0 -1
  56. package/dist/legacy/assets/images/adiSideItalicDataUri.js +0 -1
  57. package/dist/legacy/chatbot/ChatBot.d.ts +0 -5
  58. package/dist/legacy/chatbot/ChatBot.d.ts.map +0 -1
  59. package/dist/legacy/chatbot/ChatBot.js +0 -35
  60. package/dist/legacy/chatbot/context/ChatBotContext.d.ts +0 -5
  61. package/dist/legacy/chatbot/context/ChatBotContext.d.ts.map +0 -1
  62. package/dist/legacy/chatbot/context/ChatBotContext.js +0 -2922
  63. package/dist/legacy/chatbot/types/ChatBotTypes.d.ts +0 -166
  64. package/dist/legacy/chatbot/types/ChatBotTypes.d.ts.map +0 -1
  65. package/dist/legacy/chatbot/types/ChatBotTypes.js +0 -1
  66. package/dist/legacy/chatbot/ui/BerifyMeModal.d.ts +0 -17
  67. package/dist/legacy/chatbot/ui/BerifyMeModal.d.ts.map +0 -1
  68. package/dist/legacy/chatbot/ui/BerifyMeModal.js +0 -110
  69. package/dist/legacy/chatbot/ui/ChatBotHeader.d.ts +0 -15
  70. package/dist/legacy/chatbot/ui/ChatBotHeader.d.ts.map +0 -1
  71. package/dist/legacy/chatbot/ui/ChatBotHeader.js +0 -62
  72. package/dist/legacy/chatbot/ui/ChatBotUI.d.ts +0 -3
  73. package/dist/legacy/chatbot/ui/ChatBotUI.d.ts.map +0 -1
  74. package/dist/legacy/chatbot/ui/ChatBotUI.js +0 -557
  75. package/dist/legacy/chatbot/ui/MessageInput.d.ts +0 -3
  76. package/dist/legacy/chatbot/ui/MessageInput.d.ts.map +0 -1
  77. package/dist/legacy/chatbot/ui/MessageInput.js +0 -321
  78. package/dist/legacy/chatbot/ui/MessageList.d.ts +0 -4
  79. package/dist/legacy/chatbot/ui/MessageList.d.ts.map +0 -1
  80. package/dist/legacy/chatbot/ui/MessageList.js +0 -455
  81. package/dist/legacy/chatbot/ui/ModelSelector.d.ts +0 -4
  82. package/dist/legacy/chatbot/ui/ModelSelector.d.ts.map +0 -1
  83. package/dist/legacy/chatbot/ui/ModelSelector.js +0 -122
  84. package/dist/legacy/chatbot/ui/NotificationModal.d.ts +0 -15
  85. package/dist/legacy/chatbot/ui/NotificationModal.d.ts.map +0 -1
  86. package/dist/legacy/chatbot/ui/NotificationModal.js +0 -53
  87. package/dist/legacy/chatbot/ui/PermissionForm.d.ts +0 -8
  88. package/dist/legacy/chatbot/ui/PermissionForm.d.ts.map +0 -1
  89. package/dist/legacy/chatbot/ui/PermissionForm.js +0 -465
  90. package/dist/legacy/chatbot/ui/PresetMessages.d.ts +0 -4
  91. package/dist/legacy/chatbot/ui/PresetMessages.d.ts.map +0 -1
  92. package/dist/legacy/chatbot/ui/PresetMessages.js +0 -33
  93. package/dist/legacy/chatbot/ui/VoiceModePanel.d.ts +0 -3
  94. package/dist/legacy/chatbot/ui/VoiceModePanel.d.ts.map +0 -1
  95. package/dist/legacy/chatbot/ui/VoiceModePanel.js +0 -95
  96. package/dist/legacy/chatbot/ui/chatBotHeaderParts.d.ts +0 -15
  97. package/dist/legacy/chatbot/ui/chatBotHeaderParts.d.ts.map +0 -1
  98. package/dist/legacy/chatbot/ui/chatBotHeaderParts.js +0 -50
  99. package/dist/legacy/chatbot/ui/styles/isolatedStyles.d.ts +0 -73
  100. package/dist/legacy/chatbot/ui/styles/isolatedStyles.d.ts.map +0 -1
  101. package/dist/legacy/chatbot/ui/styles/isolatedStyles.js +0 -985
  102. package/dist/legacy/index.d.ts +0 -14
  103. package/dist/legacy/index.d.ts.map +0 -1
  104. package/dist/legacy/index.js +0 -12
  105. package/dist/theme/defaultTheme.d.ts +0 -3
  106. package/dist/theme/defaultTheme.d.ts.map +0 -1
  107. package/dist/theme/defaultTheme.js +0 -20
  108. package/dist/types.d.ts +0 -62
  109. package/dist/types.d.ts.map +0 -1
  110. package/dist/types.js +0 -1
  111. package/dist/voice/useVoiceConnectOrchestration.d.ts +0 -21
  112. package/dist/voice/useVoiceConnectOrchestration.d.ts.map +0 -1
  113. package/dist/voice/useVoiceConnectOrchestration.js +0 -86
  114. package/dist/voice/useVoiceMicState.d.ts +0 -15
  115. package/dist/voice/useVoiceMicState.d.ts.map +0 -1
  116. package/dist/voice/useVoiceMicState.js +0 -94
  117. package/dist/voice/useVoiceSilenceCommit.d.ts +0 -10
  118. package/dist/voice/useVoiceSilenceCommit.d.ts.map +0 -1
  119. package/dist/voice/useVoiceSilenceCommit.js +0 -67
  120. package/dist/voice/useVoiceTranscriptMessages.d.ts +0 -18
  121. package/dist/voice/useVoiceTranscriptMessages.d.ts.map +0 -1
  122. package/dist/voice/useVoiceTranscriptMessages.js +0 -132
  123. package/dist/voice/useWsRealtimeAudio.d.ts +0 -18
  124. package/dist/voice/useWsRealtimeAudio.d.ts.map +0 -1
  125. package/dist/voice/useWsRealtimeAudio.js +0 -102
  126. package/dist/voice/voiceMicConstants.d.ts +0 -4
  127. package/dist/voice/voiceMicConstants.d.ts.map +0 -1
  128. package/dist/voice/voiceMicConstants.js +0 -10
  129. package/dist/voice/voiceUserBubble.d.ts +0 -10
  130. package/dist/voice/voiceUserBubble.d.ts.map +0 -1
  131. package/dist/voice/voiceUserBubble.js +0 -52
  132. package/dist/voice/ws/BrowserWsPcmPlayer.d.ts +0 -23
  133. package/dist/voice/ws/BrowserWsPcmPlayer.d.ts.map +0 -1
  134. package/dist/voice/ws/BrowserWsPcmPlayer.js +0 -137
  135. package/dist/voice/ws/BrowserWsPcmRecorder.d.ts +0 -17
  136. package/dist/voice/ws/BrowserWsPcmRecorder.d.ts.map +0 -1
  137. package/dist/voice/ws/BrowserWsPcmRecorder.js +0 -71
  138. package/dist/voice/ws/float32ToPcm16.d.ts +0 -2
  139. package/dist/voice/ws/float32ToPcm16.d.ts.map +0 -1
  140. package/dist/voice/ws/float32ToPcm16.js +0 -8
  141. package/dist/voice/ws/voiceSilenceConstants.d.ts +0 -5
  142. package/dist/voice/ws/voiceSilenceConstants.d.ts.map +0 -1
  143. package/dist/voice/ws/voiceSilenceConstants.js +0 -4
  144. package/dist/voice/ws/wsRealtimeConstants.d.ts +0 -2
  145. package/dist/voice/ws/wsRealtimeConstants.d.ts.map +0 -1
  146. package/dist/voice/ws/wsRealtimeConstants.js +0 -1
@@ -25,6 +25,10 @@ import type {
25
25
  Message,
26
26
  ServicePermission,
27
27
  } from '@bytexbyte/nxtlinq-ai-agent-core-development';
28
+ import {
29
+ authorizeTextFrontendTool,
30
+ hasRecordedWalletVerification,
31
+ } from '@bytexbyte/nxtlinq-ai-agent-core-development';
28
32
  import {
29
33
  AIModel,
30
34
  ChatBotContextType,
@@ -79,8 +83,6 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
79
83
  isStopRecordingOnSend = false,
80
84
  // Custom error message to display in chat
81
85
  customError,
82
- // PII display mode
83
- piiDisplayMode = 'redacted',
84
86
  voiceThresholds,
85
87
  debugVoiceRms = false,
86
88
  sttGlossary,
@@ -894,6 +896,12 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
894
896
  const [modelsFromApi, setModelsFromApi] = React.useState<AIModel[]>([]);
895
897
  const [selectedModelIndex, setSelectedModelIndex] = useLocalStorage<number>('selectedAIModelIndex', 0);
896
898
 
899
+ // PII protection status: fetched from the service's own config (via serviceId + credentials
900
+ // already provided to the SDK), not passed in by the host app. Defaults to off until the fetch
901
+ // below resolves, matching how modelsFromApi starts empty.
902
+ const [enablePiiProtection, setEnablePiiProtection] = React.useState(false);
903
+ const piiDisplayMode: 'plain' | 'redacted' = enablePiiProtection ? 'redacted' : 'plain';
904
+
897
905
  // Use models from API only (no default fallback)
898
906
  // This ensures SDK always uses Dashboard-configured models
899
907
  const effectiveAvailableModels = React.useMemo(() => {
@@ -915,6 +923,9 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
915
923
  if ('availableModels' in result && Array.isArray(result.availableModels)) {
916
924
  setModelsFromApi(result.availableModels);
917
925
  }
926
+ if ('enablePiiProtection' in result) {
927
+ setEnablePiiProtection(!!result.enablePiiProtection);
928
+ }
918
929
  } catch (error) {
919
930
  console.error('Failed to fetch available models from API:', error);
920
931
  // Silently fail and use default models
@@ -999,13 +1010,18 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
999
1010
 
1000
1011
  setIsAITLoading(true);
1001
1012
  try {
1013
+ let walletAllowsAITLookup = !requireWalletIDVVerification;
1014
+
1002
1015
  // Get wallet info first - always try to get wallet info if we have a token
1003
1016
  if (nxtlinqAITServiceAccessToken) {
1004
1017
  try {
1005
1018
  const walletResponse = await nxtlinqApi.wallet.getWallet({ address: hitAddress }, nxtlinqAITServiceAccessToken);
1006
1019
  if (!('error' in walletResponse)) {
1007
1020
  setWalletInfo(walletResponse);
1021
+ walletAllowsAITLookup = !requireWalletIDVVerification
1022
+ || hasRecordedWalletVerification(walletResponse);
1008
1023
  } else {
1024
+ setWalletInfo(null);
1009
1025
  // Check if the error is due to invalid/expired token
1010
1026
  if (walletResponse.error.includes('Invalid or expired token')) {
1011
1027
  console.log('Token appears to be invalid during wallet info fetch, clearing it');
@@ -1013,10 +1029,19 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1013
1029
  }
1014
1030
  }
1015
1031
  } catch (error) {
1032
+ setWalletInfo(null);
1016
1033
  console.error('Failed to fetch wallet info:', error);
1017
1034
  }
1018
1035
  }
1019
1036
 
1037
+ // AIT existence is not identity verification. In strict mode, stop before
1038
+ // the legacy AIT lookup can synthesize a custom wallet record.
1039
+ if (nxtlinqAITServiceAccessToken && !walletAllowsAITLookup) {
1040
+ setAit(null);
1041
+ setPermissions([]);
1042
+ return;
1043
+ }
1044
+
1020
1045
  // Only try to fetch AIT if we have a token
1021
1046
  if (nxtlinqAITServiceAccessToken) {
1022
1047
  const response = await nxtlinqApi.ait.getAITByServiceIdAndController({
@@ -1267,363 +1292,70 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1267
1292
  };
1268
1293
 
1269
1294
  // Check permissions
1270
- const hasPermission = async (requiredPermission: string, autoRetry = true, onAutoConnect?: () => void, onAutoSignIn?: () => void): Promise<boolean> => {
1295
+ const hasPermission = async (
1296
+ requiredPermission: string,
1297
+ ): Promise<boolean> => {
1271
1298
  // Use refs to get latest state values
1272
1299
  const currentHitAddress = hitAddressRef.current;
1273
- const currentAit = aitRef.current;
1274
- const currentPermissions = permissionsRef.current;
1275
1300
  const currentToken = nxtlinqAITServiceAccessTokenRef.current;
1276
1301
 
1277
- if (!currentHitAddress) {
1278
- if (autoRetry) {
1279
- setIsLoading(false); // Stop thinking before showing message
1280
- setMessages(prev => [...prev, {
1281
- id: Date.now().toString(),
1282
- content: walletTextUtils.getWalletText('Please connect your HIT wallet to continue.', serviceId),
1283
- role: 'assistant',
1284
- timestamp: new Date().toISOString(),
1285
- button: 'connectWallet'
1286
- }]);
1287
-
1288
- try {
1289
- setIsAutoConnecting(true); // Mark as auto-connecting
1290
- await connectWallet(false); // Don't show sign-in message yet
1291
- setIsAutoConnecting(false); // Clear auto-connecting state
1292
-
1293
- // Show brief success message for auto-connect
1294
- showSuccess(walletTextUtils.getWalletText('Auto wallet connection successful', serviceId));
1295
-
1296
-
1297
- onAutoConnect?.(); // Call callback if provided
1298
- await new Promise(resolve => setTimeout(resolve, 2000));
1299
-
1300
- // After auto connect, if not signed in, then auto sign-in
1301
- const tokenAfterConnect = nxtlinqAITServiceAccessTokenRef.current;
1302
- if (!tokenAfterConnect) {
1303
- setIsAutoConnecting(true);
1304
- await signInWallet(false);
1305
- onAutoSignIn?.(); // Call callback if provided
1306
- setIsAutoConnecting(false);
1307
- showSuccess(walletTextUtils.getWalletText('Auto sign-in successful after wallet connect', serviceId));
1308
- await refreshAIT();
1309
- // Wait for AIT to be fully loaded with polling
1310
- let attempts = 0;
1311
- const maxAttempts = 5;
1312
- while (!aitRef.current && attempts < maxAttempts) {
1313
- await new Promise(resolve => setTimeout(resolve, 2000));
1314
- attempts++;
1315
- }
1316
- }
1317
-
1318
- // If connection (and sign-in if needed) successful, continue with permission check
1319
- const result = await hasPermission(requiredPermission, false);
1320
- return result;
1321
- } catch (error) {
1322
- console.error('Failed to auto-connect wallet:', error);
1323
- setIsAutoConnecting(false); // Clear auto-connecting state on error
1324
- return false;
1325
- }
1326
- }
1327
- // If autoRetry is false, don't show message again, just return false
1328
- return false;
1329
- }
1330
-
1331
- if (!currentToken) {
1332
- if (autoRetry) {
1333
- setIsLoading(false); // Stop thinking before showing message
1334
- setMessages(prev => [...prev, {
1335
- id: Date.now().toString(),
1336
- content: walletTextUtils.getWalletText('Please sign in with your HIT wallet to continue.', serviceId),
1337
- role: 'assistant',
1338
- timestamp: new Date().toISOString(),
1339
- button: 'signIn'
1340
- }]);
1341
-
1342
- try {
1343
- setIsAutoConnecting(true); // Mark as auto-signing
1344
- await signInWallet(false); // Don't show success message yet
1345
- onAutoSignIn?.(); // Call callback if provided
1346
- setIsAutoConnecting(false); // Clear auto-signing state
1347
-
1348
- // Show brief success message for auto-sign-in
1349
- showSuccess('Auto sign-in successful');
1350
-
1351
- // Ensure AIT is refreshed after sign-in
1352
- await refreshAIT();
1353
-
1354
- // Wait for AIT to be fully loaded with polling
1355
- let attempts = 0;
1356
- const maxAttempts = 5; // Wait up to 10 seconds (5 * 2000ms)
1357
- while (!aitRef.current && attempts < maxAttempts) {
1358
- await new Promise(resolve => setTimeout(resolve, 2000));
1359
- attempts++;
1360
- }
1361
-
1362
- // Only continue if AIT is actually loaded
1363
- if (aitRef.current) {
1364
- // Wait a bit more to ensure permissions are also loaded
1365
- await new Promise(resolve => setTimeout(resolve, 2000));
1366
- // If sign-in successful, continue with permission check
1367
- const result = await hasPermission(requiredPermission, false);
1368
- return result;
1369
- } else {
1370
- return false;
1371
- }
1372
- } catch (error) {
1373
- console.error('Failed to auto-sign-in wallet:', error);
1374
- setIsAutoConnecting(false); // Clear auto-signing state on error
1375
- return false;
1376
- }
1377
- }
1378
- // If autoRetry is false, don't show message again, just return false
1379
- return false;
1380
- }
1381
-
1382
- try {
1383
- const payload = JSON.parse(atob(currentToken.split('.')[1]));
1384
- const address = payload.address;
1385
- if (address !== currentHitAddress) {
1386
- setNxtlinqAITServiceAccessToken('');
1387
- if (autoRetry) {
1388
- setIsLoading(false); // Stop thinking before showing message
1389
- setMessages(prev => [...prev, {
1390
- id: Date.now().toString(),
1391
- content: walletTextUtils.getWalletText('Wallet address mismatch. Please sign in with the correct wallet.', serviceId),
1392
- role: 'assistant',
1393
- timestamp: new Date().toISOString(),
1394
- button: 'signIn'
1395
- }]);
1396
-
1397
- try {
1398
- setIsAutoConnecting(true); // Mark as auto-signing
1399
- await signInWallet(false); // Don't show success message yet
1400
- onAutoSignIn?.(); // Call callback if provided
1401
- setIsAutoConnecting(false); // Clear auto-signing state
1402
-
1403
- // Show brief success message for auto-sign-in after address mismatch
1404
- showSuccess('Auto sign-in successful after address mismatch');
1405
-
1406
- // Ensure AIT is refreshed after sign-in
1407
- await refreshAIT();
1408
-
1409
- // Wait for AIT to be fully loaded with polling
1410
- let attempts = 0;
1411
- const maxAttempts = 5; // Wait up to 10 seconds (5 * 2000ms)
1412
- while (!aitRef.current && attempts < maxAttempts) {
1413
- await new Promise(resolve => setTimeout(resolve, 2000));
1414
- attempts++;
1415
- }
1416
-
1417
- // Only continue if AIT is actually loaded
1418
- if (aitRef.current) {
1419
- // Wait a bit more to ensure permissions are also loaded
1420
- await new Promise(resolve => setTimeout(resolve, 2000));
1421
- // If sign-in successful, continue with permission check
1422
- const result = await hasPermission(requiredPermission, false);
1423
- return result;
1424
- } else {
1425
- return false;
1426
- }
1427
- } catch (error) {
1428
- console.error('Failed to auto-sign-in after address mismatch:', error);
1429
- setIsAutoConnecting(false); // Clear auto-signing state on error
1430
- return false;
1431
- }
1432
- }
1433
- // If autoRetry is false, don't show message again, just return false
1434
- return false;
1435
- }
1436
- } catch (error) {
1437
- console.error('Error parsing token:', error);
1438
- setNxtlinqAITServiceAccessToken('');
1439
- if (autoRetry) {
1440
- setIsLoading(false); // Stop thinking before showing message
1441
- setMessages(prev => [...prev, {
1442
- id: Date.now().toString(),
1443
- content: walletTextUtils.getWalletText('Invalid wallet session. Please sign in again.', serviceId),
1444
- role: 'assistant',
1445
- timestamp: new Date().toISOString(),
1446
- button: 'signIn'
1447
- }]);
1448
-
1449
- try {
1450
- setIsAutoConnecting(true); // Mark as auto-signing
1451
- await signInWallet(false); // Don't show success message yet
1452
- onAutoSignIn?.(); // Call callback if provided
1453
- setIsAutoConnecting(false); // Clear auto-signing state
1454
-
1455
- // Show brief success message for auto-sign-in after token parse error
1456
- showSuccess('Auto sign-in successful after token error');
1457
-
1458
- // Ensure AIT is refreshed after sign-in
1459
- await refreshAIT();
1460
-
1461
- // Wait for AIT to be fully loaded with polling
1462
- let attempts = 0;
1463
- const maxAttempts = 5; // Wait up to 10 seconds (5 * 2000ms)
1464
- while (!aitRef.current && attempts < maxAttempts) {
1465
- await new Promise(resolve => setTimeout(resolve, 2000));
1466
- attempts++;
1467
- }
1468
-
1469
- // Only continue if AIT is actually loaded
1470
- if (aitRef.current) {
1471
- // Wait a bit more to ensure permissions are also loaded
1472
- await new Promise(resolve => setTimeout(resolve, 2000));
1473
- // If sign-in successful, continue with permission check
1474
- const result = await hasPermission(requiredPermission, false);
1475
- return result;
1476
- } else {
1477
- return false;
1478
- }
1479
- } catch (signInError) {
1480
- console.error('Failed to auto-sign-in after token parse error:', signInError);
1481
- setIsAutoConnecting(false); // Clear auto-signing state on error
1482
- return false;
1483
- }
1484
- }
1485
- // If autoRetry is false, don't show message again, just return false
1486
- return false;
1487
- }
1488
-
1489
- if (!currentAit) {
1490
- // Show loading message if AIT is still loading
1491
- if (isAITLoading) {
1492
- setIsLoading(false); // Stop thinking before showing message
1493
- setMessages(prev => [...prev, {
1494
- id: Date.now().toString(),
1495
- content: walletTextUtils.getWalletText('Loading your wallet configuration... Please wait a moment.', serviceId),
1496
- role: 'assistant',
1497
- timestamp: new Date().toISOString()
1498
- }]);
1499
- return false;
1500
- }
1501
-
1502
- // If AIT is not loaded but we have a token, try to refresh it once
1503
- if (currentToken && !isAITLoading) {
1504
- try {
1505
- await refreshAIT();
1506
- // Wait for AIT to be loaded with polling
1507
- let attempts = 0;
1508
- const maxAttempts = 5; // Wait up to 10 seconds (5 * 2000ms)
1509
- while (!aitRef.current && attempts < maxAttempts) {
1510
- await new Promise(resolve => setTimeout(resolve, 2000));
1511
- attempts++;
1512
- }
1513
- // Check again after refresh
1514
- if (!aitRef.current) {
1515
- setIsLoading(false); // Stop thinking before showing message
1516
- setMessages(prev => [...prev, {
1517
- id: Date.now().toString(),
1518
- content: walletTextUtils.getWalletText('No AIT found for your wallet. Please click the settings button (⚙️) to configure your AIT permissions.', serviceId),
1519
- role: 'assistant',
1520
- timestamp: new Date().toISOString()
1521
- }]);
1522
- return false;
1523
- }
1524
- } catch (error) {
1525
- console.error('Failed to refresh AIT during permission check:', error);
1526
- setIsLoading(false); // Stop thinking before showing message
1527
- setMessages(prev => [...prev, {
1528
- id: Date.now().toString(),
1529
- content: 'No AIT found for your wallet. Please click the settings button (⚙️) to configure your AIT permissions.',
1530
- role: 'assistant',
1531
- timestamp: new Date().toISOString()
1532
- }]);
1533
- return false;
1534
- }
1535
- } else {
1536
- setIsLoading(false); // Stop thinking before showing message
1537
- setMessages(prev => [...prev, {
1538
- id: Date.now().toString(),
1539
- content: 'No AIT found for your wallet. Please click the settings button (⚙️) to configure your AIT permissions.',
1540
- role: 'assistant',
1541
- timestamp: new Date().toISOString()
1542
- }]);
1543
- return false;
1544
- }
1545
- }
1546
-
1547
- if (availablePermissions.length === 0) {
1548
- setIsLoading(false); // Stop thinking before showing message
1549
- setMessages(prev => [...prev, {
1550
- id: Date.now().toString(),
1551
- content: `No permissions available for your current identity provider. Please check your service configuration or contact support. Service ID: ${serviceId}, Permission Group: ${permissionGroup || 'None'}`,
1552
- role: 'assistant',
1553
- timestamp: new Date().toISOString()
1554
- }]);
1555
- return false;
1556
- }
1557
-
1558
- const checkToolPermissionResult = await nxtlinqApi.agent.checkToolPermission({
1302
+ const authorization = await authorizeTextFrontendTool({
1303
+ api: nxtlinqApi,
1304
+ serviceId,
1305
+ toolName: requiredPermission,
1306
+ snapshot: {
1307
+ walletAddress: currentHitAddress,
1308
+ walletToken: currentToken,
1309
+ // Legacy AIT issuance is unscoped (`externalId=''`). Do not impose a
1310
+ // wallet-address subject until issuance/migration supports it.
1311
+ externalId: undefined,
1312
+ requireWalletIDVVerification,
1313
+ loading: isAITLoading || isAutoConnecting,
1314
+ },
1559
1315
  ...authFields(),
1560
- aitToken: currentToken!,
1561
- controller: currentHitAddress!,
1562
- toolName: requiredPermission
1563
- })
1564
-
1565
- if ('error' in checkToolPermissionResult) {
1566
- // Check if AIT is still loading or if we just auto-connected
1567
- if (isAITLoading || isAutoConnecting) {
1568
- setIsLoading(false); // Stop thinking before showing message
1569
- setMessages(prev => [...prev, {
1570
- id: Date.now().toString(),
1571
- content: walletTextUtils.getWalletText('Loading your wallet configuration... Please wait a moment.', serviceId),
1572
- role: 'assistant',
1573
- timestamp: new Date().toISOString()
1574
- }]);
1575
- return false;
1576
- }
1577
-
1578
- // Refresh service permissions before judging; use latest result to decide message
1579
- const freshAvailablePermissions = await fetchAvailablePermissions();
1580
- const latestAvailablePermissions = freshAvailablePermissions ?? availablePermissions;
1581
-
1582
- const requiredPermission = checkToolPermissionResult.requiredPermission;
1583
- const isInServiceList = latestAvailablePermissions.some((p) => p.label === requiredPermission);
1584
-
1585
- // Permission not in service's available list → "not available for your current identity provider"
1586
- if (!isInServiceList) {
1587
- setIsLoading(false); // Stop thinking before showing message
1588
- setMessages(prev => [...prev, {
1589
- id: Date.now().toString(),
1590
- content: `This permission (${requiredPermission}) is not available for your current identity provider.`,
1591
- role: 'assistant',
1592
- timestamp: new Date().toISOString()
1593
- }]);
1594
- return false;
1595
- }
1596
-
1597
- // AIT loaded but permissions empty (no AIT found)
1598
- if (currentAit && currentPermissions.length === 0) {
1599
- setIsLoading(false); // Stop thinking before showing message
1600
- setMessages(prev => [...prev, {
1601
- id: Date.now().toString(),
1602
- content: 'No AIT found for your wallet. Please click the settings button (⚙️) to configure your AIT permissions.',
1603
- role: 'assistant',
1604
- timestamp: new Date().toISOString()
1605
- }]);
1606
- return false;
1607
- }
1608
-
1609
- // User has AIT but hasn't enabled this permission → prompt to enable
1610
- setIsLoading(false); // Stop thinking before showing message
1611
- const permissionMsg = `You don't have the required AIT permission: ${requiredPermission}. Would you like to enable AIT permission?`;
1612
- setMessages(prev => [
1613
- ...prev,
1614
- {
1615
- id: Date.now().toString(),
1616
- content: permissionMsg,
1617
- role: 'assistant',
1618
- timestamp: new Date().toISOString(),
1619
- button: 'enableAIT',
1620
- metadata: { requiredPermission }
1621
- }
1622
- ]);
1623
- return false;
1624
- }
1625
-
1626
- return true;
1316
+ customUsername: (!requireWalletIDVVerification && customUsername)
1317
+ ? getFinalCustomUsername(customUsername)
1318
+ : undefined,
1319
+ });
1320
+ if (authorization.status === 'allowed') return true;
1321
+
1322
+ setIsLoading(false);
1323
+ const recovery: Message = {
1324
+ id: Date.now().toString(),
1325
+ content: 'Authorization is temporarily unavailable. Please try again.',
1326
+ role: 'assistant',
1327
+ timestamp: new Date().toISOString(),
1328
+ };
1329
+ switch (authorization.status) {
1330
+ case 'wallet_disconnected':
1331
+ recovery.content = walletTextUtils.getWalletText('Please connect your HIT wallet to continue.', serviceId);
1332
+ recovery.button = 'connectWallet';
1333
+ break;
1334
+ case 'wallet_signed_out':
1335
+ recovery.content = walletTextUtils.getWalletText('Please sign in with your HIT wallet to continue.', serviceId);
1336
+ recovery.button = 'signIn';
1337
+ break;
1338
+ case 'wallet_session_invalid':
1339
+ recovery.content = walletTextUtils.getWalletText('Invalid wallet session. Please sign in again.', serviceId);
1340
+ recovery.button = 'signIn';
1341
+ break;
1342
+ case 'wallet_verification_required':
1343
+ recovery.content = 'Please verify your wallet identity before using this tool.';
1344
+ recovery.button = 'verifyWallet';
1345
+ break;
1346
+ case 'ait_not_found':
1347
+ recovery.content = 'No AIT found for your wallet. Please open settings to configure your AIT.';
1348
+ break;
1349
+ case 'permission_denied':
1350
+ recovery.content = `You don't have the required AIT permission: ${authorization.requiredPermission}. Would you like to enable AIT permission?`;
1351
+ recovery.button = 'enableAIT';
1352
+ recovery.metadata = { requiredPermission: authorization.requiredPermission };
1353
+ break;
1354
+ case 'authorization_unavailable':
1355
+ break;
1356
+ }
1357
+ setMessages(prev => [...prev, recovery]);
1358
+ return false;
1627
1359
  };
1628
1360
 
1629
1361
  // AI Model related functions
@@ -1679,6 +1411,9 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1679
1411
  const hasContent = content.trim() || (attachments && attachments.length > 0);
1680
1412
  if (!hasContent || isLoading) return;
1681
1413
  setPendingAutoTts(null);
1414
+ const turnExternalId = typeof window !== 'undefined'
1415
+ ? localStorage.getItem('walletAddress') || undefined
1416
+ : hitAddressRef.current || undefined;
1682
1417
 
1683
1418
  const currentModel = getCurrentModel();
1684
1419
  // Initialize with current model, will be updated with actual model from backend response
@@ -1757,7 +1492,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1757
1492
  model: currentModel.value,
1758
1493
  ...authFields(),
1759
1494
  pseudoId: pseudoId,
1760
- externalId: localStorage.getItem('walletAddress') || undefined,
1495
+ externalId: turnExternalId,
1761
1496
  customUserInfo,
1762
1497
  customUsername,
1763
1498
  message: content || (attachments && attachments.length > 0 ? `Uploaded ${attachments.length} file(s)` : ''),
@@ -1987,93 +1722,13 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1987
1722
 
1988
1723
  let toolMsg = '';
1989
1724
  if (onToolUse) {
1990
- let wasAutoConnected = false;
1991
- let wasAutoSignedIn = false;
1992
- // Added: Mark if permission denied due to missing AIT permission
1993
- let permissionDenied = false;
1994
- // Use requiredPermission from response if available, otherwise fall back to toolUse.name
1995
- const permissionToCheck = (response as any)?.requiredPermission || toolUse.name;
1996
-
1997
1725
  const isToolAllowed = await hasPermission(
1998
- permissionToCheck,
1999
- true,
2000
- () => { wasAutoConnected = true },
2001
- () => { wasAutoSignedIn = true }
1726
+ toolUse.name,
2002
1727
  );
2003
1728
 
2004
- // If currentPermissions does not include permissionToCheck and availablePermissionLabels includes permissionToCheck, it means AIT permission is missing
2005
- if (!isToolAllowed && !permissions.includes(permissionToCheck) && availablePermissions.map(p => p.label).includes(permissionToCheck)) {
2006
- permissionDenied = true;
2007
- }
1729
+ // Typed preflight already emitted the only permitted recovery UI.
2008
1730
  if (!isToolAllowed) {
2009
- // If permission denied due to missing AIT permission return
2010
- if (permissionDenied) {
2011
- setIsLoading(false);
2012
- return;
2013
- }
2014
-
2015
- if (isSemiAutomaticMode) {
2016
- setIsLoading(false);
2017
- setMessages(prev => [...prev, {
2018
- id: Date.now().toString(),
2019
- content: 'Click button to continue using tool',
2020
- role: 'assistant',
2021
- timestamp: new Date().toISOString(),
2022
- button: 'continue'
2023
- }]);
2024
- return;
2025
- } else {
2026
- // Only retry for auto-connect/auto-sign-in scenarios
2027
- if (wasAutoConnected && retryCount < 1) {
2028
- // Clear loading state and retry immediately
2029
- setIsLoading(false);
2030
-
2031
- // Check if wallet is already signed in
2032
- const currentToken = nxtlinqAITServiceAccessTokenRef.current;
2033
-
2034
- if (!currentToken) {
2035
- // If not signed in, directly retry the message without waiting for AIT
2036
- setTimeout(() => {
2037
- sendMessage(content, retryCount + 1, isPresetMessage);
2038
- }, 2000);
2039
- } else {
2040
- // If already signed in, wait for AIT to be fully loaded before retrying
2041
- setTimeout(async () => {
2042
- // Wait for AIT to be loaded if needed
2043
- if (!aitRef.current && nxtlinqAITServiceAccessTokenRef.current) {
2044
- await refreshAIT();
2045
- }
2046
-
2047
- // Wait for AIT to be fully loaded with polling
2048
- let attempts = 0;
2049
- const maxAttempts = 5; // Wait up to 10 seconds (5 * 2000ms)
2050
- while (!aitRef.current && attempts < maxAttempts) {
2051
- await new Promise(resolve => setTimeout(resolve, 2000));
2052
- attempts++;
2053
- }
2054
-
2055
- // Only retry if AIT is actually loaded
2056
- if (aitRef.current) {
2057
- // Wait a bit more to ensure permissions are also loaded
2058
- await new Promise(resolve => setTimeout(resolve, 3000));
2059
- sendMessage(content, retryCount + 1, isPresetMessage);
2060
- }
2061
- }, 2000);
2062
- }
2063
- }
2064
- return;
2065
- }
2066
- }
2067
-
2068
- if (isSemiAutomaticMode && wasAutoSignedIn) {
2069
1731
  setIsLoading(false);
2070
- setMessages(prev => [...prev, {
2071
- id: Date.now().toString(),
2072
- content: 'Click button to continue using tool',
2073
- role: 'assistant',
2074
- timestamp: new Date().toISOString(),
2075
- button: 'continue'
2076
- }]);
2077
1732
  return;
2078
1733
  }
2079
1734
 
package/src/index.ts CHANGED
@@ -32,6 +32,8 @@ export type {
32
32
  Message,
33
33
  SendMessageOptions,
34
34
  NxtlinqAgentSnapshot,
35
+ TextToolAuthorizationSnapshot,
36
+ TextToolAuthorizationOutcome,
35
37
  VoiceSession,
36
38
  VoiceStatus,
37
39
  } from '@bytexbyte/nxtlinq-ai-agent-core-development';
@@ -41,6 +43,9 @@ export {
41
43
  setApiHosts,
42
44
  VoiceNotSupportedError,
43
45
  STORAGE_KEYS,
46
+ authorizeTextFrontendTool,
47
+ hasRecordedWalletVerification,
48
+ validateWalletSession,
44
49
  } from '@bytexbyte/nxtlinq-ai-agent-core-development';
45
50
 
46
51
  export {
@@ -81,11 +81,6 @@ export interface ChatBotProps {
81
81
  isStopRecordingOnSend?: boolean; // Stop recording automatically when sending message (default: false)
82
82
  // Custom error message to display in chat
83
83
  customError?: string; // Custom error message to display as a chat message when provided
84
- /** PII display mode: how PII protection is visualized in chat messages
85
- * - 'plain': No visualization, messages look like normal chat
86
- * - 'redacted': Anonymized text with PII tokens displayed (default)
87
- */
88
- piiDisplayMode?: 'plain' | 'redacted';
89
84
  /** Override mic VAD thresholds. Useful for tuning sensitivity per deployment. */
90
85
  voiceThresholds?: {
91
86
  speechRmsThreshold?: number;
@@ -139,7 +134,8 @@ export interface ChatBotContextType {
139
134
  selectedModelIndex: number;
140
135
  showModelSelector: boolean;
141
136
  suggestions: PresetMessage[];
142
- // PII Protection visualization
137
+ // PII Protection visualization — derived internally from the service's own config
138
+ // (fetched via serviceId + credentials), never settable from outside the provider.
143
139
  piiDisplayMode: 'plain' | 'redacted';
144
140
  // Realtime voice mode (WebRTC)
145
141
  isVoiceMode: boolean;