@bytexbyte/nxtlinq-ai-agent-sdk 1.2.0 → 1.2.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/README.md CHANGED
@@ -17,6 +17,7 @@ A powerful SDK for building intelligent conversation applications with Nxtlinq A
17
17
  - 🔑 Permission-based access control
18
18
  - 🔍 Enhanced error handling and debugging
19
19
  - ⚡ Improved async operation handling
20
+ - 🔔 Unified notification system with Modal prompts
20
21
 
21
22
  ## Installation
22
23
 
@@ -70,6 +71,8 @@ function App() {
70
71
  }
71
72
  ```
72
73
 
74
+ > **Note**: The SDK now includes a unified notification system. All success, error, warning, and info messages are displayed as Modal prompts in the top-right corner, providing a consistent and non-intrusive user experience.
75
+
73
76
  ## Berify.me Integration
74
77
 
75
78
  The SDK supports Berify.me wallet verification for enhanced security:
@@ -130,6 +133,17 @@ function App() {
130
133
  | retryDelay | number | No | Delay between retries in milliseconds (default: 1000) |
131
134
  | permissionGroup | string | No | Permission group name for filtering permissions |
132
135
 
136
+ ### Notification System
137
+
138
+ The SDK includes a unified notification system that displays Modal prompts for various operations:
139
+
140
+ - **Success Notifications**: Green modal for successful operations (auto-hide after 3 seconds)
141
+ - **Error Notifications**: Red modal for error messages (auto-hide after 5 seconds)
142
+ - **Warning Notifications**: Orange modal for warnings (auto-hide after 4 seconds)
143
+ - **Info Notifications**: Blue modal for informational messages (auto-hide after 3 seconds)
144
+
145
+ All notifications appear in the top-right corner and can be manually dismissed by clicking the × button.
146
+
133
147
  ## Types
134
148
 
135
149
  ### Message
@@ -1 +1 @@
1
- {"version":3,"file":"ChatBot.d.ts","sourceRoot":"","sources":["../../src/components/ChatBot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,EAAE,OAAO,EAAkC,MAAM,kBAAkB,CAAC;AAE3E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC1D,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC;QAC7B,KAAK,EAAE,MAAM,CAAC;KACf,GAAG,SAAS,CAAC,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA0VD,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CA08B1C,CAAC"}
1
+ {"version":3,"file":"ChatBot.d.ts","sourceRoot":"","sources":["../../src/components/ChatBot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,EAAE,OAAO,EAAkC,MAAM,kBAAkB,CAAC;AAE3E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC1D,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC;QAC7B,KAAK,EAAE,MAAM,CAAC;KACf,GAAG,SAAS,CAAC,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA8hBD,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAmjC1C,CAAC"}
@@ -5,8 +5,64 @@ import { createNxtlinqApi } from '../api/nxtlinq-api';
5
5
  import stringify from 'fast-json-stable-stringify';
6
6
  import metakeepClient from '../core/metakeepClient';
7
7
  import useLocalStorage from '../core/lib/useLocalStorage';
8
- const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled, onClose, onSave, onConnectWallet, onSignIn, isNeedSignInWithWallet, walletInfo, onVerifyWallet, serviceId, nxtlinqApi, permissionGroup }) => {
8
+ const NotificationModal = ({ notification, onClose }) => {
9
+ const getStyles = () => {
10
+ const baseStyles = {
11
+ position: 'fixed',
12
+ bottom: 20,
13
+ right: 20,
14
+ padding: '12px 24px',
15
+ borderRadius: 8,
16
+ zIndex: 2000,
17
+ fontWeight: 600,
18
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
19
+ transition: 'all 0.3s ease',
20
+ display: 'flex',
21
+ alignItems: 'center',
22
+ gap: '12px',
23
+ minWidth: '300px',
24
+ maxWidth: '500px'
25
+ };
26
+ switch (notification.type) {
27
+ case 'success':
28
+ return { ...baseStyles, background: '#4caf50', color: 'white' };
29
+ case 'error':
30
+ return { ...baseStyles, background: '#f44336', color: 'white' };
31
+ case 'warning':
32
+ return { ...baseStyles, background: '#ff9800', color: 'white' };
33
+ default:
34
+ return { ...baseStyles, background: '#2196f3', color: 'white' };
35
+ }
36
+ };
37
+ const getIcon = () => {
38
+ switch (notification.type) {
39
+ case 'success':
40
+ return '✅';
41
+ case 'error':
42
+ return '❌';
43
+ case 'warning':
44
+ return 'âš ī¸';
45
+ default:
46
+ return 'â„šī¸';
47
+ }
48
+ };
49
+ return (notification.show && (_jsxs("div", { style: getStyles(), children: [_jsx("span", { style: { fontSize: '18px' }, children: getIcon() }), _jsx("span", { style: { flex: 1 }, children: notification.message }), _jsx("button", { onClick: onClose, style: {
50
+ background: 'none',
51
+ border: 'none',
52
+ color: 'white',
53
+ fontSize: '18px',
54
+ cursor: 'pointer',
55
+ padding: '4px',
56
+ display: 'flex',
57
+ alignItems: 'center',
58
+ justifyContent: 'center',
59
+ borderRadius: '4px',
60
+ transition: 'background-color 0.2s'
61
+ }, onMouseOver: (e) => e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.2)', onMouseOut: (e) => e.currentTarget.style.backgroundColor = 'transparent', children: "\u00D7" })] })));
62
+ };
63
+ const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled, onClose, onSave, onConnectWallet, onSignIn, isNeedSignInWithWallet, walletInfo, onVerifyWallet, serviceId, nxtlinqApi, permissionGroup, isAITLoading, isWalletLoading = false }) => {
9
64
  const [availablePermissions, setAvailablePermissions] = React.useState([]);
65
+ const [isSaving, setIsSaving] = React.useState(false);
10
66
  const fetchAvailablePermissions = async () => {
11
67
  if (!serviceId)
12
68
  return;
@@ -28,7 +84,72 @@ const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled
28
84
  React.useEffect(() => {
29
85
  fetchAvailablePermissions();
30
86
  }, [serviceId, nxtlinqApi, permissionGroup]);
31
- const isWalletVerified = walletInfo?.id || !isNeedSignInWithWallet;
87
+ const isWalletVerified = Boolean(walletInfo?.id);
88
+ const handleSave = async () => {
89
+ setIsSaving(true);
90
+ try {
91
+ await onSave();
92
+ }
93
+ finally {
94
+ setIsSaving(false);
95
+ }
96
+ };
97
+ // Show loading state while checking wallet status
98
+ if (isWalletLoading) {
99
+ return (_jsxs("div", { style: {
100
+ backgroundColor: 'white',
101
+ padding: '24px',
102
+ borderRadius: '12px',
103
+ width: '480px',
104
+ maxWidth: '90%',
105
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
106
+ }, children: [_jsxs("div", { style: {
107
+ display: 'flex',
108
+ justifyContent: 'space-between',
109
+ alignItems: 'center',
110
+ marginBottom: '24px'
111
+ }, children: [_jsx("h3", { style: {
112
+ margin: 0,
113
+ fontSize: '20px',
114
+ fontWeight: '600',
115
+ color: '#1a1a1a'
116
+ }, children: "AIT Settings" }), _jsx("button", { onClick: onClose, style: {
117
+ background: 'none',
118
+ border: 'none',
119
+ fontSize: '24px',
120
+ cursor: 'pointer',
121
+ color: '#666',
122
+ padding: '4px',
123
+ display: 'flex',
124
+ alignItems: 'center',
125
+ justifyContent: 'center'
126
+ }, children: "\u00D7" })] }), _jsxs("div", { style: { textAlign: 'center', padding: '32px 0' }, children: [_jsx("div", { style: {
127
+ width: '64px',
128
+ height: '64px',
129
+ margin: '0 auto 16px',
130
+ backgroundColor: '#f5f5f5',
131
+ borderRadius: '50%',
132
+ display: 'flex',
133
+ alignItems: 'center',
134
+ justifyContent: 'center'
135
+ }, children: _jsx("div", { style: {
136
+ width: '32px',
137
+ height: '32px',
138
+ border: '3px solid #e3e3e3',
139
+ borderTop: '3px solid #007bff',
140
+ borderRadius: '50%',
141
+ animation: 'spin 1s linear infinite'
142
+ } }) }), _jsx("p", { style: {
143
+ marginBottom: '24px',
144
+ fontSize: '16px',
145
+ color: '#666'
146
+ }, children: "Checking wallet status..." })] }), _jsx("style", { children: `
147
+ @keyframes spin {
148
+ 0% { transform: rotate(0deg); }
149
+ 100% { transform: rotate(360deg); }
150
+ }
151
+ ` })] }));
152
+ }
32
153
  return (_jsxs("div", { style: {
33
154
  backgroundColor: 'white',
34
155
  padding: '24px',
@@ -147,7 +268,14 @@ const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled
147
268
  marginBottom: '12px',
148
269
  fontSize: '16px',
149
270
  color: '#666'
150
- }, children: "Permissions" }), _jsx("div", { style: {
271
+ }, children: "Permissions" }), isAITLoading ? (_jsx("div", { style: {
272
+ backgroundColor: '#f8f9fa',
273
+ padding: '16px',
274
+ borderRadius: '8px',
275
+ border: '1px solid #e9ecef',
276
+ textAlign: 'center',
277
+ color: '#666'
278
+ }, children: "Loading permissions..." })) : (_jsx("div", { style: {
151
279
  backgroundColor: '#f8f9fa',
152
280
  padding: '16px',
153
281
  borderRadius: '8px',
@@ -156,25 +284,36 @@ const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled
156
284
  display: 'flex',
157
285
  alignItems: 'center',
158
286
  gap: '12px',
159
- cursor: 'pointer',
287
+ cursor: isAITLoading ? 'not-allowed' : 'pointer',
160
288
  padding: '8px',
161
289
  borderRadius: '6px',
162
- transition: 'background-color 0.2s'
163
- }, onMouseOver: (e) => e.currentTarget.style.backgroundColor = '#e9ecef', onMouseOut: (e) => e.currentTarget.style.backgroundColor = 'transparent', children: [_jsx("input", { type: "checkbox", checked: permissions.includes(permission.label), onChange: () => {
164
- const newPermissions = permissions.includes(permission.label)
165
- ? permissions.filter(p => p !== permission.label)
166
- : [...permissions, permission.label].sort();
167
- setPermissions(newPermissions);
168
- setIsDisabled(false);
169
- }, style: {
290
+ transition: 'background-color 0.2s',
291
+ opacity: isAITLoading ? 0.6 : 1
292
+ }, onMouseOver: (e) => {
293
+ if (!isAITLoading) {
294
+ e.currentTarget.style.backgroundColor = '#e9ecef';
295
+ }
296
+ }, onMouseOut: (e) => {
297
+ if (!isAITLoading) {
298
+ e.currentTarget.style.backgroundColor = 'transparent';
299
+ }
300
+ }, children: [_jsx("input", { type: "checkbox", checked: permissions.includes(permission.label), onChange: () => {
301
+ if (!isAITLoading) {
302
+ const newPermissions = permissions.includes(permission.label)
303
+ ? permissions.filter(p => p !== permission.label)
304
+ : [...permissions, permission.label].sort();
305
+ setPermissions(newPermissions);
306
+ setIsDisabled(false);
307
+ }
308
+ }, disabled: isAITLoading, style: {
170
309
  margin: 0,
171
310
  width: '18px',
172
311
  height: '18px',
173
- cursor: 'pointer'
312
+ cursor: isAITLoading ? 'not-allowed' : 'pointer'
174
313
  } }), _jsx("span", { style: {
175
314
  fontSize: '14px',
176
315
  color: '#333'
177
- }, children: permission.label })] }) }, permission.id))) })] }), _jsxs("div", { style: {
316
+ }, children: permission.label })] }) }, permission.id))) }))] }), _jsxs("div", { style: {
178
317
  display: 'flex',
179
318
  justifyContent: 'flex-end',
180
319
  gap: '12px',
@@ -196,25 +335,25 @@ const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled
196
335
  }, onMouseOut: (e) => {
197
336
  e.currentTarget.style.backgroundColor = '#f8f9fa';
198
337
  e.currentTarget.style.borderColor = '#dee2e6';
199
- }, children: "Cancel" }), _jsx("button", { onClick: onSave, disabled: permissions.length === 0, style: {
338
+ }, children: "Cancel" }), _jsx("button", { onClick: handleSave, disabled: permissions.length === 0 || isSaving || isAITLoading, style: {
200
339
  padding: '10px 20px',
201
- backgroundColor: permissions.length === 0 ? '#e9ecef' : '#007bff',
202
- color: 'white',
340
+ backgroundColor: permissions.length === 0 || isSaving || isAITLoading ? '#e9ecef' : '#007bff',
341
+ color: permissions.length === 0 || isSaving || isAITLoading ? '#6c757d' : 'white',
203
342
  border: 'none',
204
343
  borderRadius: '8px',
205
- cursor: permissions.length === 0 ? 'not-allowed' : 'pointer',
344
+ cursor: permissions.length === 0 || isSaving || isAITLoading ? 'not-allowed' : 'pointer',
206
345
  fontSize: '14px',
207
346
  fontWeight: '500',
208
347
  transition: 'background-color 0.2s'
209
348
  }, onMouseOver: (e) => {
210
- if (permissions.length > 0) {
349
+ if (permissions.length > 0 && !isSaving && !isAITLoading) {
211
350
  e.currentTarget.style.backgroundColor = '#0056b3';
212
351
  }
213
352
  }, onMouseOut: (e) => {
214
- if (permissions.length > 0) {
353
+ if (permissions.length > 0 && !isSaving && !isAITLoading) {
215
354
  e.currentTarget.style.backgroundColor = '#007bff';
216
355
  }
217
- }, children: "Save" })] })] }))] }));
356
+ }, children: isSaving ? 'Saving...' : 'Save' })] })] }))] }));
218
357
  };
219
358
  export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], placeholder = 'Type a message...', className = '', maxRetries = 3, retryDelay = 1000, serviceId, apiKey, apiSecret, onVerifyWallet, permissionGroup }) => {
220
359
  const [messages, setMessages] = React.useState([]);
@@ -226,14 +365,43 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
226
365
  const [permissions, setPermissions] = React.useState([]);
227
366
  const [availablePermissions, setAvailablePermissions] = React.useState([]);
228
367
  const [showPermissionForm, setShowPermissionForm] = React.useState(false);
229
- const [success, setSuccess] = React.useState(false);
368
+ const [isPermissionFormOpen, setIsPermissionFormOpen] = React.useState(false);
369
+ const [isAITLoading, setIsAITLoading] = React.useState(false);
230
370
  const messagesEndRef = React.useRef(null);
231
371
  const [isDisabled, setIsDisabled] = React.useState(true);
232
372
  const [signer, setSigner] = React.useState(null);
233
373
  const [walletInfo, setWalletInfo] = React.useState(null);
234
374
  const verifyWalletBtnRef = React.useRef(null);
235
375
  const [nxtlinqAITServiceAccessToken, setNxtlinqAITServiceAccessToken] = useLocalStorage('nxtlinqAITServiceAccessToken', '');
376
+ const [isWalletLoading, setIsWalletLoading] = React.useState(false);
377
+ const [notification, setNotification] = React.useState({
378
+ show: false,
379
+ type: 'info',
380
+ message: '',
381
+ autoHide: true,
382
+ duration: 5000
383
+ });
236
384
  const nxtlinqApi = React.useMemo(() => createNxtlinqApi(apiKey, apiSecret), [apiKey, apiSecret]);
385
+ // Unified notification functions
386
+ const showNotification = (type, message, duration = 5000) => {
387
+ setNotification({
388
+ show: true,
389
+ type,
390
+ message,
391
+ autoHide: true,
392
+ duration
393
+ });
394
+ // Auto-hide
395
+ if (duration > 0) {
396
+ setTimeout(() => {
397
+ setNotification(prev => ({ ...prev, show: false }));
398
+ }, duration);
399
+ }
400
+ };
401
+ const showSuccess = (message) => showNotification('success', message, 3000);
402
+ const showError = (message) => showNotification('error', message, 5000);
403
+ const showWarning = (message) => showNotification('warning', message, 4000);
404
+ const showInfo = (message) => showNotification('info', message, 3000);
237
405
  const fetchAvailablePermissions = async () => {
238
406
  if (!serviceId)
239
407
  return;
@@ -252,7 +420,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
252
420
  console.error('Error fetching permissions:', error);
253
421
  }
254
422
  };
255
- const refreshAIT = async () => {
423
+ const refreshAIT = async (forceUpdatePermissions = false) => {
256
424
  console.log('refreshAIT called');
257
425
  console.log('hitAddress:', hitAddress);
258
426
  console.log('nxtlinqAITServiceAccessToken:', nxtlinqAITServiceAccessToken);
@@ -261,6 +429,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
261
429
  setPermissions([]);
262
430
  return;
263
431
  }
432
+ setIsAITLoading(true);
264
433
  try {
265
434
  const response = await nxtlinqApi.ait.getAITByServiceIdAndController({
266
435
  serviceId,
@@ -274,13 +443,19 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
274
443
  }
275
444
  console.log('AIT response:', response);
276
445
  setAit(response);
277
- setPermissions(response.metadata?.permissions || []);
446
+ // Update permissions if form is not open OR if force update is requested
447
+ if (!isPermissionFormOpen || forceUpdatePermissions) {
448
+ setPermissions(response.metadata?.permissions || []);
449
+ }
278
450
  }
279
451
  catch (error) {
280
452
  console.error('Failed to fetch AIT:', error);
281
453
  setAit(null);
282
454
  setPermissions([]);
283
455
  }
456
+ finally {
457
+ setIsAITLoading(false);
458
+ }
284
459
  };
285
460
  React.useEffect(() => {
286
461
  console.log('hitAddress:', hitAddress);
@@ -325,6 +500,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
325
500
  const handleVerifySuccess = (address) => {
326
501
  localStorage.setItem(`wallet_verified_${address}`, 'true');
327
502
  const getWalletInfo = async () => {
503
+ setIsWalletLoading(true);
328
504
  try {
329
505
  const token = JSON.parse(localStorage.getItem('nxtlinqAITServiceAccessToken') || '');
330
506
  const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
@@ -344,6 +520,9 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
344
520
  catch (error) {
345
521
  console.error('Failed to update wallet info after verification:', error);
346
522
  }
523
+ finally {
524
+ setIsWalletLoading(false);
525
+ }
347
526
  };
348
527
  getWalletInfo();
349
528
  };
@@ -355,16 +534,25 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
355
534
  if (isNeedSignInWithWallet) {
356
535
  return;
357
536
  }
358
- const getWalletResponse = await nxtlinqApi.wallet.getWallet({ address: hitAddress }, nxtlinqAITServiceAccessToken);
359
- if ('error' in getWalletResponse) {
360
- if (getWalletResponse.error === 'Wallet not found') {
361
- console.log('Wallet not found - this is expected for new users');
537
+ setIsWalletLoading(true);
538
+ try {
539
+ const getWalletResponse = await nxtlinqApi.wallet.getWallet({ address: hitAddress }, nxtlinqAITServiceAccessToken);
540
+ if ('error' in getWalletResponse) {
541
+ if (getWalletResponse.error === 'Wallet not found') {
542
+ console.log('Wallet not found - this is expected for new users');
543
+ return;
544
+ }
545
+ console.error(getWalletResponse.error);
362
546
  return;
363
547
  }
364
- console.error(getWalletResponse.error);
365
- return;
548
+ setWalletInfo(getWalletResponse);
549
+ }
550
+ catch (error) {
551
+ console.error('Error getting wallet info:', error);
552
+ }
553
+ finally {
554
+ setIsWalletLoading(false);
366
555
  }
367
- setWalletInfo(getWalletResponse);
368
556
  };
369
557
  getWalletInfo();
370
558
  }, [hitAddress, isNeedSignInWithWallet]);
@@ -377,7 +565,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
377
565
  signer: !!signer
378
566
  });
379
567
  if (!hitAddress) {
380
- alert('Please connect your wallet first.');
568
+ showError('Please connect your wallet first.');
381
569
  return;
382
570
  }
383
571
  try {
@@ -403,19 +591,29 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
403
591
  if ('error' in verifyWalletResponse) {
404
592
  if (verifyWalletResponse.error === 'Wallet already exists') {
405
593
  // If wallet exists, get wallet info directly
406
- const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
407
- if (!('error' in walletResponse)) {
408
- setWalletInfo(walletResponse);
409
- const aitResponse = await nxtlinqApi.ait.getAITByServiceIdAndController({ serviceId, controller: address }, token);
410
- if (!('error' in aitResponse)) {
411
- setAit(aitResponse);
594
+ setIsWalletLoading(true);
595
+ try {
596
+ const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
597
+ if (!('error' in walletResponse)) {
598
+ setWalletInfo(walletResponse);
599
+ const aitResponse = await nxtlinqApi.ait.getAITByServiceIdAndController({ serviceId, controller: address }, token);
600
+ if (!('error' in aitResponse)) {
601
+ setAit(aitResponse);
602
+ }
412
603
  }
413
604
  }
414
- // Clean up token from URL
605
+ finally {
606
+ setIsWalletLoading(false);
607
+ }
608
+ // Clean up URL parameters after wallet verification
415
609
  if (typeof window !== 'undefined') {
416
610
  try {
417
611
  const url = new URL(window.location.href);
612
+ // Clean up all Berify.me related parameters
418
613
  url.searchParams.delete('token');
614
+ url.searchParams.delete('isAutoConnect');
615
+ url.searchParams.delete('method');
616
+ url.searchParams.delete('returnUrl');
419
617
  window.history.replaceState({}, '', url.toString());
420
618
  }
421
619
  catch (e) {
@@ -423,28 +621,40 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
423
621
  }
424
622
  }
425
623
  setIsLoading(false);
624
+ // Use Modal notification instead of ChatBot message
625
+ showSuccess('Wallet verification completed successfully! Your wallet is now verified and ready to use.');
426
626
  // Refresh AIT after wallet verification
427
627
  refreshAIT();
428
628
  return { token, hitAddress: address };
429
629
  }
430
- alert(verifyWalletResponse.error);
630
+ showError(verifyWalletResponse.error);
431
631
  setIsLoading(false);
432
632
  return;
433
633
  }
434
634
  // Verification successful, get wallet info
435
- const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
436
- if (!('error' in walletResponse)) {
437
- setWalletInfo(walletResponse);
438
- const aitResponse = await nxtlinqApi.ait.getAITByServiceIdAndController({ serviceId, controller: address }, token);
439
- if (!('error' in aitResponse)) {
440
- setAit(aitResponse);
635
+ setIsWalletLoading(true);
636
+ try {
637
+ const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
638
+ if (!('error' in walletResponse)) {
639
+ setWalletInfo(walletResponse);
640
+ const aitResponse = await nxtlinqApi.ait.getAITByServiceIdAndController({ serviceId, controller: address }, token);
641
+ if (!('error' in aitResponse)) {
642
+ setAit(aitResponse);
643
+ }
441
644
  }
442
645
  }
443
- // Clean up token from URL
646
+ finally {
647
+ setIsWalletLoading(false);
648
+ }
649
+ // Clean up URL parameters after wallet verification
444
650
  if (typeof window !== 'undefined') {
445
651
  try {
446
652
  const url = new URL(window.location.href);
653
+ // Clean up all Berify.me related parameters
447
654
  url.searchParams.delete('token');
655
+ url.searchParams.delete('isAutoConnect');
656
+ url.searchParams.delete('method');
657
+ url.searchParams.delete('returnUrl');
448
658
  window.history.replaceState({}, '', url.toString());
449
659
  }
450
660
  catch (e) {
@@ -452,6 +662,8 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
452
662
  }
453
663
  }
454
664
  setIsLoading(false);
665
+ // Use Modal notification instead of ChatBot message
666
+ showSuccess('Wallet verification completed successfully! Your wallet is now verified and ready to use.');
455
667
  // Refresh AIT after wallet verification
456
668
  refreshAIT();
457
669
  return { token, hitAddress: address };
@@ -466,7 +678,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
466
678
  msg = error.message;
467
679
  }
468
680
  console.error('Wallet verification failed:', error);
469
- alert(msg);
681
+ showError(msg);
470
682
  setIsLoading(false);
471
683
  throw error;
472
684
  }
@@ -478,7 +690,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
478
690
  catch (error) {
479
691
  console.error('Failed to verify wallet:', error);
480
692
  setIsLoading(false);
481
- alert('Failed to verify wallet. Please try again.');
693
+ showError('Failed to verify wallet. Please try again.');
482
694
  throw error;
483
695
  }
484
696
  };
@@ -559,12 +771,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
559
771
  }
560
772
  else {
561
773
  // User is already signed in
562
- setMessages(prev => [...prev, {
563
- id: Date.now().toString(),
564
- content: 'Successfully connected your HIT wallet. You are already signed in and can use the AI agent.',
565
- role: 'assistant',
566
- timestamp: new Date().toISOString()
567
- }]);
774
+ showSuccess('Successfully connected your HIT wallet. You are already signed in and can use the AI agent.');
568
775
  }
569
776
  }
570
777
  return userAddress;
@@ -579,17 +786,17 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
579
786
  }, [nxtlinqAITServiceAccessToken, refreshAIT, isNeedSignInWithWallet]);
580
787
  const signInWallet = async (autoShowSuccessMessage = true) => {
581
788
  if (!hitAddress) {
582
- alert('Please connect your wallet first.');
789
+ showError('Please connect your wallet first.');
583
790
  return;
584
791
  }
585
792
  if (!signer) {
586
- alert('Please connect your wallet first.');
793
+ showError('Please connect your wallet first.');
587
794
  return;
588
795
  }
589
796
  try {
590
797
  const nonceResponse = await nxtlinqApi.auth.getNonce({ address: hitAddress });
591
798
  if ('error' in nonceResponse) {
592
- alert(nonceResponse.error);
799
+ showError(nonceResponse.error);
593
800
  return;
594
801
  }
595
802
  const payload = {
@@ -604,26 +811,21 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
604
811
  signature
605
812
  });
606
813
  if ('error' in response) {
607
- alert(response.error);
814
+ showError(response.error);
608
815
  return;
609
816
  }
610
817
  const { accessToken } = response;
611
818
  setNxtlinqAITServiceAccessToken(accessToken);
612
819
  // Auto-show connected message after signing in
613
820
  if (autoShowSuccessMessage) {
614
- setMessages(prev => [...prev, {
615
- id: Date.now().toString(),
616
- content: 'Successfully signed in with your HIT wallet. You can now use the AI agent.',
617
- role: 'assistant',
618
- timestamp: new Date().toISOString()
619
- }]);
821
+ showSuccess('Successfully signed in with your HIT wallet. You can now use the AI agent.');
620
822
  }
621
823
  // Refresh AIT after signing in
622
824
  refreshAIT();
623
825
  }
624
826
  catch (error) {
625
827
  console.error('Failed to sign in:', error);
626
- alert('Failed to sign in. Please try again.');
828
+ showError('Failed to sign in. Please try again.');
627
829
  }
628
830
  };
629
831
  const hasPermission = async (toolName) => {
@@ -637,7 +839,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
637
839
  }]);
638
840
  return false;
639
841
  }
640
- // Check if user has signed in with wallet
842
+ // Check if user has signed in with wallet and token is valid
641
843
  if (!nxtlinqAITServiceAccessToken) {
642
844
  setMessages(prev => [...prev, {
643
845
  id: Date.now().toString(),
@@ -648,6 +850,51 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
648
850
  }]);
649
851
  return false;
650
852
  }
853
+ // Validate token - check if it's expired or for different address
854
+ try {
855
+ const payload = JSON.parse(atob(nxtlinqAITServiceAccessToken.split('.')[1]));
856
+ const exp = payload.exp * 1000; // Convert to milliseconds
857
+ const now = Date.now();
858
+ if (exp < now) {
859
+ // Clear invalid token
860
+ setNxtlinqAITServiceAccessToken('');
861
+ setMessages(prev => [...prev, {
862
+ id: Date.now().toString(),
863
+ content: 'Your wallet session has expired. Please sign in again.',
864
+ role: 'assistant',
865
+ timestamp: new Date().toISOString(),
866
+ button: 'signIn'
867
+ }]);
868
+ return false;
869
+ }
870
+ // Check if the token's payload has the same address as the wallet address
871
+ const address = payload.address;
872
+ if (address !== hitAddress) {
873
+ // Clear mismatched token
874
+ setNxtlinqAITServiceAccessToken('');
875
+ setMessages(prev => [...prev, {
876
+ id: Date.now().toString(),
877
+ content: 'Wallet address mismatch. Please sign in with the correct wallet.',
878
+ role: 'assistant',
879
+ timestamp: new Date().toISOString(),
880
+ button: 'signIn'
881
+ }]);
882
+ return false;
883
+ }
884
+ }
885
+ catch (error) {
886
+ console.error('Error parsing token:', error);
887
+ // Clear invalid token
888
+ setNxtlinqAITServiceAccessToken('');
889
+ setMessages(prev => [...prev, {
890
+ id: Date.now().toString(),
891
+ content: 'Invalid wallet session. Please sign in again.',
892
+ role: 'assistant',
893
+ timestamp: new Date().toISOString(),
894
+ button: 'signIn'
895
+ }]);
896
+ return false;
897
+ }
651
898
  if (!ait) {
652
899
  setMessages(prev => [...prev, {
653
900
  id: Date.now().toString(),
@@ -813,20 +1060,22 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
813
1060
  setAit(aitInfo);
814
1061
  };
815
1062
  const savePermissions = async () => {
816
- setIsLoading(true);
817
1063
  setIsDisabled(true);
818
1064
  try {
819
1065
  await generateAndRegisterAIT();
820
- setIsLoading(false);
821
- setSuccess(true);
1066
+ // Use Modal notification instead of ChatBot message
1067
+ showSuccess('AIT permissions saved successfully! You can now use the AI agent with your configured permissions.');
822
1068
  setShowPermissionForm(false);
1069
+ setIsPermissionFormOpen(false);
823
1070
  }
824
1071
  catch (error) {
825
1072
  console.error('Failed to generate AIT:', error);
826
- setIsLoading(false);
827
1073
  setIsDisabled(false);
828
1074
  if (error instanceof Error) {
829
- alert(error.message);
1075
+ showError(error.message);
1076
+ }
1077
+ else {
1078
+ showError('Failed to save permissions. Please try again.');
830
1079
  }
831
1080
  }
832
1081
  };
@@ -872,7 +1121,12 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
872
1121
  display: 'flex',
873
1122
  justifyContent: 'space-between',
874
1123
  alignItems: 'center'
875
- }, children: [_jsx("h3", { style: { margin: 0 }, children: "AI Agent" }), _jsxs("div", { style: { display: 'flex', gap: '10px' }, children: [_jsx("button", { onClick: () => setShowPermissionForm(true), style: {
1124
+ }, children: [_jsx("h3", { style: { margin: 0 }, children: "AI Agent" }), _jsxs("div", { style: { display: 'flex', gap: '10px' }, children: [_jsx("button", { onClick: async () => {
1125
+ setShowPermissionForm(true);
1126
+ setIsPermissionFormOpen(true);
1127
+ // Force refresh AIT data when opening the form to show latest permissions
1128
+ await refreshAIT(true);
1129
+ }, style: {
876
1130
  background: 'none',
877
1131
  border: 'none',
878
1132
  color: 'white',
@@ -981,22 +1235,8 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
981
1235
  alignItems: 'center',
982
1236
  justifyContent: 'center',
983
1237
  zIndex: 2000
984
- }, children: _jsx(PermissionForm, { hitAddress: hitAddress, permissions: permissions, setPermissions: setPermissions, setIsDisabled: setIsDisabled, onClose: () => setShowPermissionForm(false), onConnectWallet: () => connectWallet(false), onSignIn: () => signInWallet(false), onSave: savePermissions, isNeedSignInWithWallet: isNeedSignInWithWallet, walletInfo: walletInfo, onVerifyWallet: handleVerifyWalletClick, serviceId: serviceId, nxtlinqApi: nxtlinqApi, permissionGroup: permissionGroup }) })), success && (_jsxs("div", { style: {
985
- position: 'fixed',
986
- bottom: 100,
987
- right: 40,
988
- background: '#4caf50',
989
- color: 'white',
990
- padding: '12px 24px',
991
- borderRadius: 8,
992
- zIndex: 2000,
993
- fontWeight: 600,
994
- }, children: ["Saved successfully!", _jsx("button", { style: {
995
- background: 'none',
996
- border: 'none',
997
- color: 'white',
998
- marginLeft: 16,
999
- fontSize: 18,
1000
- cursor: 'pointer'
1001
- }, onClick: () => setSuccess(false), children: "\u00D7" })] }))] }));
1238
+ }, children: _jsx(PermissionForm, { hitAddress: hitAddress, permissions: permissions, setPermissions: setPermissions, setIsDisabled: setIsDisabled, onClose: () => {
1239
+ setShowPermissionForm(false);
1240
+ setIsPermissionFormOpen(false);
1241
+ }, onConnectWallet: () => connectWallet(false), onSignIn: () => signInWallet(false), onSave: savePermissions, isNeedSignInWithWallet: isNeedSignInWithWallet, walletInfo: walletInfo, onVerifyWallet: handleVerifyWalletClick, serviceId: serviceId, nxtlinqApi: nxtlinqApi, permissionGroup: permissionGroup, isAITLoading: isAITLoading, isWalletLoading: isWalletLoading }) })), notification.show && (_jsx(NotificationModal, { notification: notification, onClose: () => setNotification({ ...notification, show: false }) }))] }));
1002
1242
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytexbyte/nxtlinq-ai-agent-sdk",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Nxtlinq AI Agent SDK - Proprietary Software with enhanced async operation handling",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",