@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.4.1 → 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.
@@ -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,
@@ -1006,13 +1010,18 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1006
1010
 
1007
1011
  setIsAITLoading(true);
1008
1012
  try {
1013
+ let walletAllowsAITLookup = !requireWalletIDVVerification;
1014
+
1009
1015
  // Get wallet info first - always try to get wallet info if we have a token
1010
1016
  if (nxtlinqAITServiceAccessToken) {
1011
1017
  try {
1012
1018
  const walletResponse = await nxtlinqApi.wallet.getWallet({ address: hitAddress }, nxtlinqAITServiceAccessToken);
1013
1019
  if (!('error' in walletResponse)) {
1014
1020
  setWalletInfo(walletResponse);
1021
+ walletAllowsAITLookup = !requireWalletIDVVerification
1022
+ || hasRecordedWalletVerification(walletResponse);
1015
1023
  } else {
1024
+ setWalletInfo(null);
1016
1025
  // Check if the error is due to invalid/expired token
1017
1026
  if (walletResponse.error.includes('Invalid or expired token')) {
1018
1027
  console.log('Token appears to be invalid during wallet info fetch, clearing it');
@@ -1020,10 +1029,19 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1020
1029
  }
1021
1030
  }
1022
1031
  } catch (error) {
1032
+ setWalletInfo(null);
1023
1033
  console.error('Failed to fetch wallet info:', error);
1024
1034
  }
1025
1035
  }
1026
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
+
1027
1045
  // Only try to fetch AIT if we have a token
1028
1046
  if (nxtlinqAITServiceAccessToken) {
1029
1047
  const response = await nxtlinqApi.ait.getAITByServiceIdAndController({
@@ -1274,363 +1292,70 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1274
1292
  };
1275
1293
 
1276
1294
  // Check permissions
1277
- const hasPermission = async (requiredPermission: string, autoRetry = true, onAutoConnect?: () => void, onAutoSignIn?: () => void): Promise<boolean> => {
1295
+ const hasPermission = async (
1296
+ requiredPermission: string,
1297
+ ): Promise<boolean> => {
1278
1298
  // Use refs to get latest state values
1279
1299
  const currentHitAddress = hitAddressRef.current;
1280
- const currentAit = aitRef.current;
1281
- const currentPermissions = permissionsRef.current;
1282
1300
  const currentToken = nxtlinqAITServiceAccessTokenRef.current;
1283
1301
 
1284
- if (!currentHitAddress) {
1285
- if (autoRetry) {
1286
- setIsLoading(false); // Stop thinking before showing message
1287
- setMessages(prev => [...prev, {
1288
- id: Date.now().toString(),
1289
- content: walletTextUtils.getWalletText('Please connect your HIT wallet to continue.', serviceId),
1290
- role: 'assistant',
1291
- timestamp: new Date().toISOString(),
1292
- button: 'connectWallet'
1293
- }]);
1294
-
1295
- try {
1296
- setIsAutoConnecting(true); // Mark as auto-connecting
1297
- await connectWallet(false); // Don't show sign-in message yet
1298
- setIsAutoConnecting(false); // Clear auto-connecting state
1299
-
1300
- // Show brief success message for auto-connect
1301
- showSuccess(walletTextUtils.getWalletText('Auto wallet connection successful', serviceId));
1302
-
1303
-
1304
- onAutoConnect?.(); // Call callback if provided
1305
- await new Promise(resolve => setTimeout(resolve, 2000));
1306
-
1307
- // After auto connect, if not signed in, then auto sign-in
1308
- const tokenAfterConnect = nxtlinqAITServiceAccessTokenRef.current;
1309
- if (!tokenAfterConnect) {
1310
- setIsAutoConnecting(true);
1311
- await signInWallet(false);
1312
- onAutoSignIn?.(); // Call callback if provided
1313
- setIsAutoConnecting(false);
1314
- showSuccess(walletTextUtils.getWalletText('Auto sign-in successful after wallet connect', serviceId));
1315
- await refreshAIT();
1316
- // Wait for AIT to be fully loaded with polling
1317
- let attempts = 0;
1318
- const maxAttempts = 5;
1319
- while (!aitRef.current && attempts < maxAttempts) {
1320
- await new Promise(resolve => setTimeout(resolve, 2000));
1321
- attempts++;
1322
- }
1323
- }
1324
-
1325
- // If connection (and sign-in if needed) successful, continue with permission check
1326
- const result = await hasPermission(requiredPermission, false);
1327
- return result;
1328
- } catch (error) {
1329
- console.error('Failed to auto-connect wallet:', error);
1330
- setIsAutoConnecting(false); // Clear auto-connecting state on error
1331
- return false;
1332
- }
1333
- }
1334
- // If autoRetry is false, don't show message again, just return false
1335
- return false;
1336
- }
1337
-
1338
- if (!currentToken) {
1339
- if (autoRetry) {
1340
- setIsLoading(false); // Stop thinking before showing message
1341
- setMessages(prev => [...prev, {
1342
- id: Date.now().toString(),
1343
- content: walletTextUtils.getWalletText('Please sign in with your HIT wallet to continue.', serviceId),
1344
- role: 'assistant',
1345
- timestamp: new Date().toISOString(),
1346
- button: 'signIn'
1347
- }]);
1348
-
1349
- try {
1350
- setIsAutoConnecting(true); // Mark as auto-signing
1351
- await signInWallet(false); // Don't show success message yet
1352
- onAutoSignIn?.(); // Call callback if provided
1353
- setIsAutoConnecting(false); // Clear auto-signing state
1354
-
1355
- // Show brief success message for auto-sign-in
1356
- showSuccess('Auto sign-in successful');
1357
-
1358
- // Ensure AIT is refreshed after sign-in
1359
- await refreshAIT();
1360
-
1361
- // Wait for AIT to be fully loaded with polling
1362
- let attempts = 0;
1363
- const maxAttempts = 5; // Wait up to 10 seconds (5 * 2000ms)
1364
- while (!aitRef.current && attempts < maxAttempts) {
1365
- await new Promise(resolve => setTimeout(resolve, 2000));
1366
- attempts++;
1367
- }
1368
-
1369
- // Only continue if AIT is actually loaded
1370
- if (aitRef.current) {
1371
- // Wait a bit more to ensure permissions are also loaded
1372
- await new Promise(resolve => setTimeout(resolve, 2000));
1373
- // If sign-in successful, continue with permission check
1374
- const result = await hasPermission(requiredPermission, false);
1375
- return result;
1376
- } else {
1377
- return false;
1378
- }
1379
- } catch (error) {
1380
- console.error('Failed to auto-sign-in wallet:', error);
1381
- setIsAutoConnecting(false); // Clear auto-signing state on error
1382
- return false;
1383
- }
1384
- }
1385
- // If autoRetry is false, don't show message again, just return false
1386
- return false;
1387
- }
1388
-
1389
- try {
1390
- const payload = JSON.parse(atob(currentToken.split('.')[1]));
1391
- const address = payload.address;
1392
- if (address !== currentHitAddress) {
1393
- setNxtlinqAITServiceAccessToken('');
1394
- if (autoRetry) {
1395
- setIsLoading(false); // Stop thinking before showing message
1396
- setMessages(prev => [...prev, {
1397
- id: Date.now().toString(),
1398
- content: walletTextUtils.getWalletText('Wallet address mismatch. Please sign in with the correct wallet.', serviceId),
1399
- role: 'assistant',
1400
- timestamp: new Date().toISOString(),
1401
- button: 'signIn'
1402
- }]);
1403
-
1404
- try {
1405
- setIsAutoConnecting(true); // Mark as auto-signing
1406
- await signInWallet(false); // Don't show success message yet
1407
- onAutoSignIn?.(); // Call callback if provided
1408
- setIsAutoConnecting(false); // Clear auto-signing state
1409
-
1410
- // Show brief success message for auto-sign-in after address mismatch
1411
- showSuccess('Auto sign-in successful after address mismatch');
1412
-
1413
- // Ensure AIT is refreshed after sign-in
1414
- await refreshAIT();
1415
-
1416
- // Wait for AIT to be fully loaded with polling
1417
- let attempts = 0;
1418
- const maxAttempts = 5; // Wait up to 10 seconds (5 * 2000ms)
1419
- while (!aitRef.current && attempts < maxAttempts) {
1420
- await new Promise(resolve => setTimeout(resolve, 2000));
1421
- attempts++;
1422
- }
1423
-
1424
- // Only continue if AIT is actually loaded
1425
- if (aitRef.current) {
1426
- // Wait a bit more to ensure permissions are also loaded
1427
- await new Promise(resolve => setTimeout(resolve, 2000));
1428
- // If sign-in successful, continue with permission check
1429
- const result = await hasPermission(requiredPermission, false);
1430
- return result;
1431
- } else {
1432
- return false;
1433
- }
1434
- } catch (error) {
1435
- console.error('Failed to auto-sign-in after address mismatch:', error);
1436
- setIsAutoConnecting(false); // Clear auto-signing state on error
1437
- return false;
1438
- }
1439
- }
1440
- // If autoRetry is false, don't show message again, just return false
1441
- return false;
1442
- }
1443
- } catch (error) {
1444
- console.error('Error parsing token:', error);
1445
- setNxtlinqAITServiceAccessToken('');
1446
- if (autoRetry) {
1447
- setIsLoading(false); // Stop thinking before showing message
1448
- setMessages(prev => [...prev, {
1449
- id: Date.now().toString(),
1450
- content: walletTextUtils.getWalletText('Invalid wallet session. Please sign in again.', serviceId),
1451
- role: 'assistant',
1452
- timestamp: new Date().toISOString(),
1453
- button: 'signIn'
1454
- }]);
1455
-
1456
- try {
1457
- setIsAutoConnecting(true); // Mark as auto-signing
1458
- await signInWallet(false); // Don't show success message yet
1459
- onAutoSignIn?.(); // Call callback if provided
1460
- setIsAutoConnecting(false); // Clear auto-signing state
1461
-
1462
- // Show brief success message for auto-sign-in after token parse error
1463
- showSuccess('Auto sign-in successful after token error');
1464
-
1465
- // Ensure AIT is refreshed after sign-in
1466
- await refreshAIT();
1467
-
1468
- // Wait for AIT to be fully loaded with polling
1469
- let attempts = 0;
1470
- const maxAttempts = 5; // Wait up to 10 seconds (5 * 2000ms)
1471
- while (!aitRef.current && attempts < maxAttempts) {
1472
- await new Promise(resolve => setTimeout(resolve, 2000));
1473
- attempts++;
1474
- }
1475
-
1476
- // Only continue if AIT is actually loaded
1477
- if (aitRef.current) {
1478
- // Wait a bit more to ensure permissions are also loaded
1479
- await new Promise(resolve => setTimeout(resolve, 2000));
1480
- // If sign-in successful, continue with permission check
1481
- const result = await hasPermission(requiredPermission, false);
1482
- return result;
1483
- } else {
1484
- return false;
1485
- }
1486
- } catch (signInError) {
1487
- console.error('Failed to auto-sign-in after token parse error:', signInError);
1488
- setIsAutoConnecting(false); // Clear auto-signing state on error
1489
- return false;
1490
- }
1491
- }
1492
- // If autoRetry is false, don't show message again, just return false
1493
- return false;
1494
- }
1495
-
1496
- if (!currentAit) {
1497
- // Show loading message if AIT is still loading
1498
- if (isAITLoading) {
1499
- setIsLoading(false); // Stop thinking before showing message
1500
- setMessages(prev => [...prev, {
1501
- id: Date.now().toString(),
1502
- content: walletTextUtils.getWalletText('Loading your wallet configuration... Please wait a moment.', serviceId),
1503
- role: 'assistant',
1504
- timestamp: new Date().toISOString()
1505
- }]);
1506
- return false;
1507
- }
1508
-
1509
- // If AIT is not loaded but we have a token, try to refresh it once
1510
- if (currentToken && !isAITLoading) {
1511
- try {
1512
- await refreshAIT();
1513
- // Wait for AIT to be loaded with polling
1514
- let attempts = 0;
1515
- const maxAttempts = 5; // Wait up to 10 seconds (5 * 2000ms)
1516
- while (!aitRef.current && attempts < maxAttempts) {
1517
- await new Promise(resolve => setTimeout(resolve, 2000));
1518
- attempts++;
1519
- }
1520
- // Check again after refresh
1521
- if (!aitRef.current) {
1522
- setIsLoading(false); // Stop thinking before showing message
1523
- setMessages(prev => [...prev, {
1524
- id: Date.now().toString(),
1525
- content: walletTextUtils.getWalletText('No AIT found for your wallet. Please click the settings button (⚙️) to configure your AIT permissions.', serviceId),
1526
- role: 'assistant',
1527
- timestamp: new Date().toISOString()
1528
- }]);
1529
- return false;
1530
- }
1531
- } catch (error) {
1532
- console.error('Failed to refresh AIT during permission check:', error);
1533
- setIsLoading(false); // Stop thinking before showing message
1534
- setMessages(prev => [...prev, {
1535
- id: Date.now().toString(),
1536
- content: 'No AIT found for your wallet. Please click the settings button (⚙️) to configure your AIT permissions.',
1537
- role: 'assistant',
1538
- timestamp: new Date().toISOString()
1539
- }]);
1540
- return false;
1541
- }
1542
- } else {
1543
- setIsLoading(false); // Stop thinking before showing message
1544
- setMessages(prev => [...prev, {
1545
- id: Date.now().toString(),
1546
- content: 'No AIT found for your wallet. Please click the settings button (⚙️) to configure your AIT permissions.',
1547
- role: 'assistant',
1548
- timestamp: new Date().toISOString()
1549
- }]);
1550
- return false;
1551
- }
1552
- }
1553
-
1554
- if (availablePermissions.length === 0) {
1555
- setIsLoading(false); // Stop thinking before showing message
1556
- setMessages(prev => [...prev, {
1557
- id: Date.now().toString(),
1558
- content: `No permissions available for your current identity provider. Please check your service configuration or contact support. Service ID: ${serviceId}, Permission Group: ${permissionGroup || 'None'}`,
1559
- role: 'assistant',
1560
- timestamp: new Date().toISOString()
1561
- }]);
1562
- return false;
1563
- }
1564
-
1565
- 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
+ },
1566
1315
  ...authFields(),
1567
- aitToken: currentToken!,
1568
- controller: currentHitAddress!,
1569
- toolName: requiredPermission
1570
- })
1571
-
1572
- if ('error' in checkToolPermissionResult) {
1573
- // Check if AIT is still loading or if we just auto-connected
1574
- if (isAITLoading || isAutoConnecting) {
1575
- setIsLoading(false); // Stop thinking before showing message
1576
- setMessages(prev => [...prev, {
1577
- id: Date.now().toString(),
1578
- content: walletTextUtils.getWalletText('Loading your wallet configuration... Please wait a moment.', serviceId),
1579
- role: 'assistant',
1580
- timestamp: new Date().toISOString()
1581
- }]);
1582
- return false;
1583
- }
1584
-
1585
- // Refresh service permissions before judging; use latest result to decide message
1586
- const freshAvailablePermissions = await fetchAvailablePermissions();
1587
- const latestAvailablePermissions = freshAvailablePermissions ?? availablePermissions;
1588
-
1589
- const requiredPermission = checkToolPermissionResult.requiredPermission;
1590
- const isInServiceList = latestAvailablePermissions.some((p) => p.label === requiredPermission);
1591
-
1592
- // Permission not in service's available list → "not available for your current identity provider"
1593
- if (!isInServiceList) {
1594
- setIsLoading(false); // Stop thinking before showing message
1595
- setMessages(prev => [...prev, {
1596
- id: Date.now().toString(),
1597
- content: `This permission (${requiredPermission}) is not available for your current identity provider.`,
1598
- role: 'assistant',
1599
- timestamp: new Date().toISOString()
1600
- }]);
1601
- return false;
1602
- }
1603
-
1604
- // AIT loaded but permissions empty (no AIT found)
1605
- if (currentAit && currentPermissions.length === 0) {
1606
- setIsLoading(false); // Stop thinking before showing message
1607
- setMessages(prev => [...prev, {
1608
- id: Date.now().toString(),
1609
- content: 'No AIT found for your wallet. Please click the settings button (⚙️) to configure your AIT permissions.',
1610
- role: 'assistant',
1611
- timestamp: new Date().toISOString()
1612
- }]);
1613
- return false;
1614
- }
1615
-
1616
- // User has AIT but hasn't enabled this permission → prompt to enable
1617
- setIsLoading(false); // Stop thinking before showing message
1618
- const permissionMsg = `You don't have the required AIT permission: ${requiredPermission}. Would you like to enable AIT permission?`;
1619
- setMessages(prev => [
1620
- ...prev,
1621
- {
1622
- id: Date.now().toString(),
1623
- content: permissionMsg,
1624
- role: 'assistant',
1625
- timestamp: new Date().toISOString(),
1626
- button: 'enableAIT',
1627
- metadata: { requiredPermission }
1628
- }
1629
- ]);
1630
- return false;
1631
- }
1632
-
1633
- 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;
1634
1359
  };
1635
1360
 
1636
1361
  // AI Model related functions
@@ -1686,6 +1411,9 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1686
1411
  const hasContent = content.trim() || (attachments && attachments.length > 0);
1687
1412
  if (!hasContent || isLoading) return;
1688
1413
  setPendingAutoTts(null);
1414
+ const turnExternalId = typeof window !== 'undefined'
1415
+ ? localStorage.getItem('walletAddress') || undefined
1416
+ : hitAddressRef.current || undefined;
1689
1417
 
1690
1418
  const currentModel = getCurrentModel();
1691
1419
  // Initialize with current model, will be updated with actual model from backend response
@@ -1764,7 +1492,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1764
1492
  model: currentModel.value,
1765
1493
  ...authFields(),
1766
1494
  pseudoId: pseudoId,
1767
- externalId: localStorage.getItem('walletAddress') || undefined,
1495
+ externalId: turnExternalId,
1768
1496
  customUserInfo,
1769
1497
  customUsername,
1770
1498
  message: content || (attachments && attachments.length > 0 ? `Uploaded ${attachments.length} file(s)` : ''),
@@ -1994,93 +1722,13 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1994
1722
 
1995
1723
  let toolMsg = '';
1996
1724
  if (onToolUse) {
1997
- let wasAutoConnected = false;
1998
- let wasAutoSignedIn = false;
1999
- // Added: Mark if permission denied due to missing AIT permission
2000
- let permissionDenied = false;
2001
- // Use requiredPermission from response if available, otherwise fall back to toolUse.name
2002
- const permissionToCheck = (response as any)?.requiredPermission || toolUse.name;
2003
-
2004
1725
  const isToolAllowed = await hasPermission(
2005
- permissionToCheck,
2006
- true,
2007
- () => { wasAutoConnected = true },
2008
- () => { wasAutoSignedIn = true }
1726
+ toolUse.name,
2009
1727
  );
2010
1728
 
2011
- // If currentPermissions does not include permissionToCheck and availablePermissionLabels includes permissionToCheck, it means AIT permission is missing
2012
- if (!isToolAllowed && !permissions.includes(permissionToCheck) && availablePermissions.map(p => p.label).includes(permissionToCheck)) {
2013
- permissionDenied = true;
2014
- }
1729
+ // Typed preflight already emitted the only permitted recovery UI.
2015
1730
  if (!isToolAllowed) {
2016
- // If permission denied due to missing AIT permission return
2017
- if (permissionDenied) {
2018
- setIsLoading(false);
2019
- return;
2020
- }
2021
-
2022
- if (isSemiAutomaticMode) {
2023
- setIsLoading(false);
2024
- setMessages(prev => [...prev, {
2025
- id: Date.now().toString(),
2026
- content: 'Click button to continue using tool',
2027
- role: 'assistant',
2028
- timestamp: new Date().toISOString(),
2029
- button: 'continue'
2030
- }]);
2031
- return;
2032
- } else {
2033
- // Only retry for auto-connect/auto-sign-in scenarios
2034
- if (wasAutoConnected && retryCount < 1) {
2035
- // Clear loading state and retry immediately
2036
- setIsLoading(false);
2037
-
2038
- // Check if wallet is already signed in
2039
- const currentToken = nxtlinqAITServiceAccessTokenRef.current;
2040
-
2041
- if (!currentToken) {
2042
- // If not signed in, directly retry the message without waiting for AIT
2043
- setTimeout(() => {
2044
- sendMessage(content, retryCount + 1, isPresetMessage);
2045
- }, 2000);
2046
- } else {
2047
- // If already signed in, wait for AIT to be fully loaded before retrying
2048
- setTimeout(async () => {
2049
- // Wait for AIT to be loaded if needed
2050
- if (!aitRef.current && nxtlinqAITServiceAccessTokenRef.current) {
2051
- await refreshAIT();
2052
- }
2053
-
2054
- // Wait for AIT to be fully loaded with polling
2055
- let attempts = 0;
2056
- const maxAttempts = 5; // Wait up to 10 seconds (5 * 2000ms)
2057
- while (!aitRef.current && attempts < maxAttempts) {
2058
- await new Promise(resolve => setTimeout(resolve, 2000));
2059
- attempts++;
2060
- }
2061
-
2062
- // Only retry if AIT is actually loaded
2063
- if (aitRef.current) {
2064
- // Wait a bit more to ensure permissions are also loaded
2065
- await new Promise(resolve => setTimeout(resolve, 3000));
2066
- sendMessage(content, retryCount + 1, isPresetMessage);
2067
- }
2068
- }, 2000);
2069
- }
2070
- }
2071
- return;
2072
- }
2073
- }
2074
-
2075
- if (isSemiAutomaticMode && wasAutoSignedIn) {
2076
1731
  setIsLoading(false);
2077
- setMessages(prev => [...prev, {
2078
- id: Date.now().toString(),
2079
- content: 'Click button to continue using tool',
2080
- role: 'assistant',
2081
- timestamp: new Date().toISOString(),
2082
- button: 'continue'
2083
- }]);
2084
1732
  return;
2085
1733
  }
2086
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 {
@@ -1,6 +1,7 @@
1
1
  /** @jsxImportSource @emotion/react */
2
2
  import { css } from '@emotion/react';
3
3
  import * as React from 'react';
4
+ import { hasRecordedWalletVerification } from '@bytexbyte/nxtlinq-ai-agent-core-development';
4
5
  import { useDraggable, useLocalStorage, useResizable, walletTextUtils } from '@bytexbyte/nxtlinq-ai-agent-web-development';
5
6
  import { useChatBot } from '../context/ChatBotContext';
6
7
  import { ChatBotHeader } from './ChatBotHeader';
@@ -161,7 +162,8 @@ export const ChatBotUI: React.FC = () => {
161
162
  const urlParams = new URLSearchParams(window.location.search);
162
163
  const hasBerifymeToken = urlParams.get('token') && urlParams.get('method') === 'berifyme';
163
164
 
164
- const isWalletVerifiedWithBerifyme = walletInfo?.id && walletInfo?.method === 'berifyme';
165
+ const hasRecordedVerification = Boolean(walletInfo?.id)
166
+ && hasRecordedWalletVerification(walletInfo);
165
167
 
166
168
  // Helper function to update IDV suggestion state based on storage value
167
169
  const updateIDVSuggestionState = React.useCallback((dismissedValue: string | null) => {
@@ -221,7 +223,7 @@ export const ChatBotUI: React.FC = () => {
221
223
  const shouldShowBanner = hitAddress &&
222
224
  !props.requireWalletIDVVerification &&
223
225
  !hasBerifymeToken &&
224
- !isWalletVerifiedWithBerifyme &&
226
+ !hasRecordedVerification &&
225
227
  !isNeedSignInWithWallet;
226
228
 
227
229
  if (shouldShowBanner) {
@@ -229,7 +231,7 @@ export const ChatBotUI: React.FC = () => {
229
231
  const shouldShowBannerAfterDelay = hitAddress &&
230
232
  !props.requireWalletIDVVerification &&
231
233
  !hasBerifymeToken &&
232
- !isWalletVerifiedWithBerifyme &&
234
+ !hasRecordedVerification &&
233
235
  !isNeedSignInWithWallet;
234
236
 
235
237
  if (shouldShowBannerAfterDelay) {
@@ -728,7 +730,7 @@ export const ChatBotUI: React.FC = () => {
728
730
  onClose={handleClose}
729
731
  />
730
732
 
731
- {showIDVSuggestion && hitAddress && !props.requireWalletIDVVerification && !hasBerifymeToken && !isWalletVerifiedWithBerifyme && (
733
+ {showIDVSuggestion && hitAddress && !props.requireWalletIDVVerification && !hasBerifymeToken && !hasRecordedVerification && (
732
734
  <div
733
735
  data-idv-banner
734
736
  css={idvBanner}>
@@ -330,6 +330,8 @@ export const MessageList: React.FC = () => {
330
330
  availableModels,
331
331
  serviceId,
332
332
  piiDisplayMode,
333
+ setShowPermissionForm,
334
+ setIsPermissionFormOpen,
333
335
  } = useChatBot();
334
336
  const messagesEndRef = React.useRef<HTMLDivElement>(null);
335
337
 
@@ -367,6 +369,9 @@ export const MessageList: React.FC = () => {
367
369
  }
368
370
  }
369
371
  }
372
+ } else if (buttonType === 'verifyWallet') {
373
+ setIsPermissionFormOpen(true);
374
+ setShowPermissionForm(true);
370
375
  } else if (buttonType === 'continue') {
371
376
  const lastUserMsg = [...messages].reverse().find(m => m.role === 'user');
372
377
  if (lastUserMsg && lastUserMsg.content) {
@@ -672,6 +677,7 @@ export const MessageList: React.FC = () => {
672
677
  {isAutoConnecting ? 'Connecting...' :
673
678
  message.button === 'connectWallet' ? (Boolean(hitAddress) ? 'Connected' : walletTextUtils.getWalletText('Connect Wallet', serviceId)) :
674
679
  message.button === 'signIn' ? (!isNeedSignInWithWallet ? 'Signed In' : 'Sign In') :
680
+ message.button === 'verifyWallet' ? 'Verify Wallet' :
675
681
  message.button === 'continue' ? 'Continue' :
676
682
  message.button === 'enableAIT' ?
677
683
  ((isAITLoading || isAITEnabling) ? 'Enabling...' :