@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.4.1 → 0.4.3

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/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 {
@@ -0,0 +1,39 @@
1
+ import type { Attachment } from '@bytexbyte/nxtlinq-ai-agent-core-development';
2
+
3
+ export type PendingTextToolRequest = {
4
+ content: string;
5
+ isPresetMessage: boolean;
6
+ attachments?: Attachment[];
7
+ };
8
+
9
+ /** Keeps one interrupted text-tool request and serializes all resume attempts. */
10
+ export class PendingTextToolRetryController {
11
+ private pending: PendingTextToolRequest | null = null;
12
+ private inFlight: Promise<boolean> | null = null;
13
+
14
+ set(request: PendingTextToolRequest): void {
15
+ this.pending = request;
16
+ }
17
+
18
+ clear(): void {
19
+ this.pending = null;
20
+ }
21
+
22
+ hasPending(): boolean {
23
+ return this.pending !== null;
24
+ }
25
+
26
+ retry(execute: (request: PendingTextToolRequest) => Promise<void>): Promise<boolean> {
27
+ if (this.inFlight) return this.inFlight;
28
+ if (!this.pending) return Promise.resolve(false);
29
+
30
+ const request = this.pending;
31
+ this.inFlight = (async () => {
32
+ await execute(request);
33
+ return true;
34
+ })().finally(() => {
35
+ this.inFlight = null;
36
+ });
37
+ return this.inFlight;
38
+ }
39
+ }
@@ -163,8 +163,9 @@ export interface ChatBotContextType {
163
163
 
164
164
  // Functions
165
165
  connectWallet: (autoShowSignInMessage?: boolean) => Promise<string | false | undefined>;
166
- signInWallet: (autoShowSuccessMessage?: boolean) => Promise<void>;
167
- sendMessage: (content: string, retryCount?: number) => Promise<void>;
166
+ signInWallet: (autoShowSuccessMessage?: boolean) => Promise<boolean>;
167
+ retryPendingTextTool: () => Promise<boolean>;
168
+ sendMessage: (content: string, retryCount?: number, isPresetMessage?: boolean, attachments?: Attachment[]) => Promise<void>;
168
169
  handleSubmit: (e: React.FormEvent, attachments?: Attachment[]) => Promise<void>;
169
170
  handlePresetMessage: (message: PresetMessage) => void;
170
171
  savePermissions: (newPermissions?: string[]) => Promise<void>;
@@ -194,7 +195,7 @@ export interface ChatBotContextType {
194
195
  // Additional properties for PermissionForm
195
196
  onSave: (newPermissions?: string[]) => Promise<void>;
196
197
  onConnectWallet: () => Promise<string | false | undefined>;
197
- onSignIn: () => Promise<void>;
198
+ onSignIn: () => Promise<boolean>;
198
199
  isNeedSignInWithWallet: boolean;
199
200
  onVerifyWallet: (method: 'berifyme' | 'custom') => Promise<void>;
200
201
  serviceId: string;
@@ -203,4 +204,4 @@ export interface ChatBotContextType {
203
204
  // Props
204
205
  props: ChatBotProps;
205
206
  nxtlinqApi: AITApi;
206
- }
207
+ }
@@ -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}>
@@ -319,6 +319,7 @@ export const MessageList: React.FC = () => {
319
319
  retryTtsWithGesture,
320
320
  connectWallet,
321
321
  signInWallet,
322
+ retryPendingTextTool,
322
323
  hitAddress,
323
324
  isAutoConnecting,
324
325
  isNeedSignInWithWallet,
@@ -330,6 +331,8 @@ export const MessageList: React.FC = () => {
330
331
  availableModels,
331
332
  serviceId,
332
333
  piiDisplayMode,
334
+ setShowPermissionForm,
335
+ setIsPermissionFormOpen,
333
336
  } = useChatBot();
334
337
  const messagesEndRef = React.useRef<HTMLDivElement>(null);
335
338
 
@@ -351,22 +354,27 @@ export const MessageList: React.FC = () => {
351
354
  }
352
355
 
353
356
  if (buttonType === 'connectWallet') {
354
- connectWallet(true);
357
+ const connected = await connectWallet(false);
358
+ if (connected) {
359
+ // Continue the interrupted flow immediately. This opens the wallet
360
+ // signature UI instead of leaving the user on a stale connect prompt.
361
+ const signedIn = await signInWallet(true);
362
+ if (signedIn) await retryPendingTextTool();
363
+ }
355
364
  } else if (buttonType === 'signIn') {
356
- signInWallet(true);
365
+ const signedIn = await signInWallet(true);
366
+ if (signedIn) await retryPendingTextTool();
357
367
  } else if (buttonType === 'enableAIT') {
358
368
  const requiredPermission = message.metadata?.requiredPermission;
359
369
  if (requiredPermission) {
360
370
  const success = await enableAIT(requiredPermission);
361
371
  if (success) {
362
- // Find the last user message
363
- const lastUserMsg = [...messages].reverse().find(m => m.role === 'user');
364
- if (lastUserMsg && lastUserMsg.content) {
365
- // Re-execute the previous user message (retryCount=1 to skip duplicate PII log)
366
- await sendMessage(lastUserMsg.content, 1);
367
- }
372
+ await retryPendingTextTool();
368
373
  }
369
374
  }
375
+ } else if (buttonType === 'verifyWallet') {
376
+ setIsPermissionFormOpen(true);
377
+ setShowPermissionForm(true);
370
378
  } else if (buttonType === 'continue') {
371
379
  const lastUserMsg = [...messages].reverse().find(m => m.role === 'user');
372
380
  if (lastUserMsg && lastUserMsg.content) {
@@ -672,6 +680,7 @@ export const MessageList: React.FC = () => {
672
680
  {isAutoConnecting ? 'Connecting...' :
673
681
  message.button === 'connectWallet' ? (Boolean(hitAddress) ? 'Connected' : walletTextUtils.getWalletText('Connect Wallet', serviceId)) :
674
682
  message.button === 'signIn' ? (!isNeedSignInWithWallet ? 'Signed In' : 'Sign In') :
683
+ message.button === 'verifyWallet' ? 'Verify Wallet' :
675
684
  message.button === 'continue' ? 'Continue' :
676
685
  message.button === 'enableAIT' ?
677
686
  ((isAITLoading || isAITEnabling) ? 'Enabling...' :
@@ -3,6 +3,7 @@ import * as React from 'react';
3
3
  import { css } from '@emotion/react';
4
4
  import { useChatBot } from '../context/ChatBotContext';
5
5
  import { walletTextUtils } from '@bytexbyte/nxtlinq-ai-agent-web-development';
6
+ import { hasRecordedWalletVerification } from '@bytexbyte/nxtlinq-ai-agent-core-development';
6
7
  import { actionButton, modalOverlay } from './styles/isolatedStyles';
7
8
 
8
9
  interface PermissionFormProps {
@@ -103,13 +104,10 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
103
104
 
104
105
  // Check if wallet IDV verification is required based on configuration
105
106
  const requireWalletIDVVerification = props.requireWalletIDVVerification ?? true; // Default to true
106
- const isWalletVerified = Boolean(walletInfo?.id);
107
- const isWalletVerifiedWithBerifyme = walletInfo?.method === 'berifyme';
108
-
109
- // Show verification prompt if:
110
- // 1. IDV verification is required AND wallet is not verified, OR
111
- // 2. IDV verification is required AND wallet is verified but not with Berifyme
112
- const shouldShowVerificationPrompt = requireWalletIDVVerification && (!isWalletVerified || !isWalletVerifiedWithBerifyme);
107
+ // Any recorded verification source is valid. Custom additionally requires
108
+ // its explicit username evidence so synthetic AIT-provisioned rows do not count.
109
+ const shouldShowVerificationPrompt = requireWalletIDVVerification
110
+ && (!walletInfo?.id || !hasRecordedWalletVerification(walletInfo));
113
111
 
114
112
  const handleSave = async () => {
115
113
  setIsSaving(true);
@@ -415,10 +413,7 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
415
413
  font-size: 16px !important;
416
414
  color: #666 !important;
417
415
  `}>
418
- {isWalletVerified && !isWalletVerifiedWithBerifyme
419
- ? walletTextUtils.getWalletText('Your wallet is verified with custom method, but Berify.me verification is required to continue.', serviceId)
420
- : walletTextUtils.getWalletText('Please verify your wallet with Berify.me to continue', serviceId)
421
- }
416
+ {walletTextUtils.getWalletText('Please verify your wallet identity to continue', serviceId)}
422
417
  </p>
423
418
  <button
424
419
  onClick={() => onVerifyWallet('berifyme')}
@@ -629,4 +624,4 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
629
624
  )}
630
625
  </div>
631
626
  );
632
- };
627
+ };