@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.
- package/dist/context/ChatBotContext.d.ts.map +1 -1
- package/dist/context/ChatBotContext.js +75 -402
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +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/ui/ChatBotUI.d.ts.map +1 -1
- package/dist/ui/ChatBotUI.js +6 -4
- package/dist/ui/MessageList.d.ts.map +1 -1
- package/dist/ui/MessageList.js +11 -6
- package/dist/ui/PermissionForm.d.ts.map +1 -1
- package/dist/ui/PermissionForm.js +6 -9
- package/package.json +4 -4
- package/src/context/ChatBotContext.tsx +83 -435
- package/src/index.ts +5 -0
- package/src/ui/ChatBotUI.tsx +6 -4
- package/src/ui/MessageList.tsx +6 -0
- package/src/ui/PermissionForm.tsx +7 -12
|
@@ -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 (
|
|
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
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
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
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
})
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
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:
|
|
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
|
-
|
|
2006
|
-
true,
|
|
2007
|
-
() => { wasAutoConnected = true },
|
|
2008
|
-
() => { wasAutoSignedIn = true }
|
|
1726
|
+
toolUse.name,
|
|
2009
1727
|
);
|
|
2010
1728
|
|
|
2011
|
-
//
|
|
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 {
|
package/src/ui/ChatBotUI.tsx
CHANGED
|
@@ -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
|
|
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
|
-
!
|
|
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
|
-
!
|
|
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 && !
|
|
733
|
+
{showIDVSuggestion && hitAddress && !props.requireWalletIDVVerification && !hasBerifymeToken && !hasRecordedVerification && (
|
|
732
734
|
<div
|
|
733
735
|
data-idv-banner
|
|
734
736
|
css={idvBanner}>
|
package/src/ui/MessageList.tsx
CHANGED
|
@@ -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...' :
|