@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.4.11 → 0.4.13

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ChatBotContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatBotContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA+B/B,OAAO,EAEL,kBAAkB,EAClB,YAAY,EAEb,MAAM,uBAAuB,CAAC;AAwB/B,eAAO,MAAM,UAAU,0BAMtB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAo/FlD,CAAC"}
1
+ {"version":3,"file":"ChatBotContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatBotContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA+B/B,OAAO,EAEL,kBAAkB,EAClB,YAAY,EAEb,MAAM,uBAAuB,CAAC;AAqC/B,eAAO,MAAM,UAAU,0BAMtB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAkjGlD,CAAC"}
@@ -103,6 +103,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
103
103
  const [showPermissionForm, setShowPermissionForm] = React.useState(false);
104
104
  const [isPermissionFormOpen, setIsPermissionFormOpen] = React.useState(false);
105
105
  const [isAITLoading, setIsAITLoading] = React.useState(false);
106
+ const [permissionSaveState, setPermissionSaveState] = React.useState(null);
106
107
  const [isDisabled, setIsDisabled] = React.useState(true);
107
108
  const [signer, setSigner] = React.useState(null);
108
109
  const [walletInfo, setWalletInfo] = React.useState(null);
@@ -174,6 +175,8 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
174
175
  }, []);
175
176
  const runtimeRecomputeCoordinatorRef = React.useRef(new RuntimeAITRecomputeCoordinator());
176
177
  const runtimeMutationQueueRef = React.useRef(new RuntimeAITMutationQueue());
178
+ const permissionSaveOperationRef = React.useRef(null);
179
+ const permissionSaveOperationIdRef = React.useRef(0);
177
180
  useProviderRuntimeCommit(hitAddressRef, hitAddress, environment, setApiHosts);
178
181
  // Helper function to combine customUsername with Adilas customUserInfo
179
182
  const getFinalCustomUsername = React.useCallback((username) => {
@@ -972,6 +975,14 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
972
975
  setIsAITLoading(false);
973
976
  return;
974
977
  }
978
+ // updateActivePermissions publishes the authoritative result itself. A modal reopened while
979
+ // that mutation is still running must keep rendering its submitted draft instead of queuing
980
+ // a redundant GET that replaces the form with a loading state.
981
+ const activePermissionSave = permissionSaveOperationRef.current;
982
+ if (runtimeContext
983
+ && activePermissionSave?.subjectKey === runtimeAITSubjectKey(runtimeContext)) {
984
+ return;
985
+ }
975
986
  authorizationLoadingStateRef.current.setAITLoading(true);
976
987
  setIsAITLoading(true);
977
988
  try {
@@ -1494,6 +1505,15 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
1494
1505
  if ('error' in response || response.result === 'error') {
1495
1506
  // Check if it's an AIT-related error
1496
1507
  const errorResponse = response;
1508
+ // A backend authorization rejection happens before the frontend sees a
1509
+ // toolCall, so the normal preflight path below cannot remember this
1510
+ // turn. Preserve it here so a successful wallet/permission recovery
1511
+ // can replay the exact original request once.
1512
+ pendingTextToolRetryRef.current.captureAuthorizationRecovery(errorResponse, {
1513
+ content,
1514
+ isPresetMessage,
1515
+ attachments,
1516
+ });
1497
1517
  if (errorResponse.requiresWalletConnection) {
1498
1518
  // Case where wallet connection is required - trigger auto-connect logic
1499
1519
  // Try to auto-connect wallet first
@@ -2050,7 +2070,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2050
2070
  stopTextToSpeechAndReset();
2051
2071
  }
2052
2072
  }, [textToSpeechEnabled, stopTextToSpeechAndReset]);
2053
- const { isVoiceMode, voiceStatus, isVoiceConnecting, isMicMuted, remoteAudioRef, enterVoiceMode, exitVoiceMode, toggleMicMute: toggleVoiceMicMute, interruptVoice, micRms, } = useVoiceMode({
2073
+ const { isVoiceMode, voiceStatus, isVoiceConnecting, isMicMuted, remoteAudioRef, enterVoiceMode, exitVoiceMode, toggleMicMute: toggleVoiceMicMute, interruptVoice, retryVoicePermissionRequest, micRms, } = useVoiceMode({
2054
2074
  apiKey: apiKey ?? '',
2055
2075
  apiSecret: apiSecret ?? '',
2056
2076
  serviceToken,
@@ -2060,6 +2080,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2060
2080
  aitId: ait?.aitId,
2061
2081
  walletAddress: hitAddress || undefined,
2062
2082
  aitToken: nxtlinqAITServiceAccessToken || undefined,
2083
+ permissions,
2063
2084
  metadata: customUserInfo,
2064
2085
  nxtlinqApi,
2065
2086
  getMessages: () => messagesRef.current,
@@ -2332,41 +2353,68 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2332
2353
  // Save permissions
2333
2354
  const savePermissions = async (newPermissions) => {
2334
2355
  setIsDisabled(true);
2335
- try {
2336
- if (!hitAddress) {
2337
- throw new Error('Please connect your wallet first');
2338
- }
2339
- const context = {
2340
- controller: hitAddress,
2341
- serviceId,
2342
- ...(roles !== undefined ? { roles: [...roles] } : {}),
2343
- ...(permissionGroup !== undefined ? { permissionGroup } : {}),
2344
- };
2345
- const checkedPermissions = [...(newPermissions ?? permissions)];
2346
- await runtimeMutationQueueRef.current.run(runtimeAITSubjectKey(context), async () => {
2347
- runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
2348
- await replaceRuntimeActivePermissions(context, checkedPermissions);
2349
- });
2350
- showSuccess('AIT permissions saved successfully! You can now use the AI agent with your configured permissions.');
2351
- setShowPermissionForm(false);
2352
- setIsPermissionFormOpen(false);
2353
- await retryPendingTextTool();
2354
- }
2355
- catch (error) {
2356
- if (!(error instanceof StaleRuntimeAITResponseError)) {
2357
- console.error('Failed to update AIT deny-list:', error);
2358
- }
2356
+ if (!hitAddress) {
2359
2357
  setIsDisabled(false);
2360
- if (error instanceof StaleRuntimeAITResponseError) {
2361
- return;
2358
+ showError('Please connect your wallet first');
2359
+ return;
2360
+ }
2361
+ const context = {
2362
+ controller: hitAddress,
2363
+ serviceId,
2364
+ ...(roles !== undefined ? { roles: [...roles] } : {}),
2365
+ ...(permissionGroup !== undefined ? { permissionGroup } : {}),
2366
+ };
2367
+ const subjectKey = runtimeAITSubjectKey(context);
2368
+ const activeOperation = permissionSaveOperationRef.current;
2369
+ if (activeOperation?.subjectKey === subjectKey) {
2370
+ return activeOperation.promise;
2371
+ }
2372
+ const checkedPermissions = [...(newPermissions ?? permissions)];
2373
+ const operationId = ++permissionSaveOperationIdRef.current;
2374
+ setPermissionSaveState({
2375
+ operationId,
2376
+ subjectKey,
2377
+ permissions: checkedPermissions,
2378
+ status: 'saving',
2379
+ });
2380
+ const operation = (async () => {
2381
+ try {
2382
+ await runtimeMutationQueueRef.current.run(subjectKey, async () => {
2383
+ runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
2384
+ await replaceRuntimeActivePermissions(context, checkedPermissions);
2385
+ });
2386
+ showSuccess('AIT permissions saved successfully! You can now use the AI agent with your configured permissions.');
2387
+ setShowPermissionForm(false);
2388
+ setIsPermissionFormOpen(false);
2389
+ setPermissionSaveState((current) => current?.operationId === operationId ? null : current);
2390
+ await retryPendingTextTool();
2362
2391
  }
2363
- else if (error instanceof Error) {
2364
- showError(error.message);
2392
+ catch (error) {
2393
+ setPermissionSaveState((current) => current?.operationId === operationId
2394
+ ? { ...current, status: 'failed' }
2395
+ : current);
2396
+ if (!(error instanceof StaleRuntimeAITResponseError)) {
2397
+ console.error('Failed to update AIT deny-list:', error);
2398
+ }
2399
+ setIsDisabled(false);
2400
+ if (error instanceof StaleRuntimeAITResponseError) {
2401
+ return;
2402
+ }
2403
+ else if (error instanceof Error) {
2404
+ showError(error.message);
2405
+ }
2406
+ else {
2407
+ showError('Failed to save permissions. Please try again.');
2408
+ }
2365
2409
  }
2366
- else {
2367
- showError('Failed to save permissions. Please try again.');
2410
+ finally {
2411
+ if (permissionSaveOperationRef.current?.operationId === operationId) {
2412
+ permissionSaveOperationRef.current = null;
2413
+ }
2368
2414
  }
2369
- }
2415
+ })();
2416
+ permissionSaveOperationRef.current = { operationId, subjectKey, promise: operation };
2417
+ return operation;
2370
2418
  };
2371
2419
  // Handle verify wallet click
2372
2420
  const handleVerifyWalletClick = async (method) => {
@@ -2643,6 +2691,10 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2643
2691
  updateExternalId();
2644
2692
  }, [hitAddress, nxtlinqApi, authFields, pseudoId]);
2645
2693
  const runtimeAuthorizationMatchesRenderedContext = runtimeAuthorizationMatchesContext(runtimeAuthorizationContextKey, runtimeContext);
2694
+ const renderedPermissionSaveState = runtimeContext
2695
+ && permissionSaveState?.subjectKey === runtimeAITSubjectKey(runtimeContext)
2696
+ ? permissionSaveState
2697
+ : null;
2646
2698
  const contextValue = {
2647
2699
  // State
2648
2700
  messages,
@@ -2663,6 +2715,8 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2663
2715
  showPermissionForm,
2664
2716
  isPermissionFormOpen,
2665
2717
  isAITLoading,
2718
+ permissionSaveDraft: renderedPermissionSaveState?.permissions ?? null,
2719
+ isPermissionSaving: renderedPermissionSaveState?.status === 'saving',
2666
2720
  isDisabled,
2667
2721
  walletInfo,
2668
2722
  isWalletLoading,
@@ -2733,6 +2787,7 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2733
2787
  exitVoiceMode: handleExitVoiceMode,
2734
2788
  toggleVoiceMicMute,
2735
2789
  interruptVoice,
2790
+ retryVoicePermissionRequest,
2736
2791
  micRms,
2737
2792
  debugVoiceRms,
2738
2793
  // Additional properties for PermissionForm
@@ -4,11 +4,16 @@ export type PendingTextToolRequest = {
4
4
  isPresetMessage: boolean;
5
5
  attachments?: Attachment[];
6
6
  };
7
+ export type TextToolAuthorizationRecovery = {
8
+ requiresWalletConnection?: boolean;
9
+ requiresPermissionUpdate?: boolean;
10
+ };
7
11
  /** Keeps one interrupted text-tool request and serializes all resume attempts. */
8
12
  export declare class PendingTextToolRetryController {
9
13
  private pending;
10
14
  private inFlight;
11
15
  set(request: PendingTextToolRequest): void;
16
+ captureAuthorizationRecovery(recovery: TextToolAuthorizationRecovery, request: PendingTextToolRequest): boolean;
12
17
  clear(): void;
13
18
  hasPending(): boolean;
14
19
  retry(execute: (request: PendingTextToolRequest) => Promise<void>): Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"pendingTextToolRetry.d.ts","sourceRoot":"","sources":["../src/pendingTextToolRetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAE/E,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B,CAAC;AAEF,kFAAkF;AAClF,qBAAa,8BAA8B;IACzC,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,QAAQ,CAAiC;IAEjD,GAAG,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI;IAI1C,KAAK,IAAI,IAAI;IAIb,UAAU,IAAI,OAAO;IAIrB,KAAK,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAarF"}
1
+ {"version":3,"file":"pendingTextToolRetry.d.ts","sourceRoot":"","sources":["../src/pendingTextToolRetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAE/E,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,kFAAkF;AAClF,qBAAa,8BAA8B;IACzC,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,QAAQ,CAAiC;IAEjD,GAAG,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI;IAI1C,4BAA4B,CAC1B,QAAQ,EAAE,6BAA6B,EACvC,OAAO,EAAE,sBAAsB,GAC9B,OAAO;IAMV,KAAK,IAAI,IAAI;IAIb,UAAU,IAAI,OAAO;IAIrB,KAAK,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAarF"}
@@ -7,6 +7,12 @@ export class PendingTextToolRetryController {
7
7
  set(request) {
8
8
  this.pending = request;
9
9
  }
10
+ captureAuthorizationRecovery(recovery, request) {
11
+ if (!recovery.requiresWalletConnection && !recovery.requiresPermissionUpdate)
12
+ return false;
13
+ this.set(request);
14
+ return true;
15
+ }
10
16
  clear() {
11
17
  this.pending = null;
12
18
  }
@@ -89,6 +89,8 @@ export interface ChatBotContextType {
89
89
  showPermissionForm: boolean;
90
90
  isPermissionFormOpen: boolean;
91
91
  isAITLoading: boolean;
92
+ permissionSaveDraft: string[] | null;
93
+ isPermissionSaving: boolean;
92
94
  isAITEnabling: boolean;
93
95
  isDisabled: boolean;
94
96
  walletInfo: any;
@@ -164,6 +166,7 @@ export interface ChatBotContextType {
164
166
  exitVoiceMode: () => Promise<void>;
165
167
  toggleVoiceMicMute: () => void;
166
168
  interruptVoice: () => void;
169
+ retryVoicePermissionRequest: () => Promise<boolean>;
167
170
  onSave: (newPermissions?: string[]) => Promise<void>;
168
171
  onConnectWallet: () => Promise<string | false | undefined>;
169
172
  onSignIn: () => Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"ChatBotTypes.d.ts","sourceRoot":"","sources":["../../src/types/ChatBotTypes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ChatBotTypes.d.ts","sourceRoot":"","sources":["../../src/types/ChatBotTypes.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/ui/MessageList.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAuT/B,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EA4c/B,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/ui/MessageList.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAuT/B,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAqd/B,CAAC"}
@@ -231,7 +231,7 @@ const PiiStatusHeader = React.memo(({ message }) => {
231
231
  return (_jsxs("div", { css: piiHeaderRow, children: [_jsx(ShieldIcon, {}), _jsx("span", { children: "\u2713 Protected" })] }));
232
232
  });
233
233
  export const MessageList = () => {
234
- const { messages, isLoading, isTtsProcessing, requiresGesture, retryTtsWithGesture, connectWallet, signInWallet, retryPendingTextTool, hitAddress, isAutoConnecting, isNeedSignInWithWallet, enableAIT, isAITLoading, isAITEnabling, sendMessage, permissions, availableModels, serviceId, piiDisplayMode, setShowPermissionForm, setIsPermissionFormOpen, } = useChatBot();
234
+ const { messages, isLoading, isTtsProcessing, requiresGesture, retryTtsWithGesture, connectWallet, signInWallet, retryPendingTextTool, retryVoicePermissionRequest, hitAddress, isAutoConnecting, isNeedSignInWithWallet, enableAIT, isAITLoading, isAITEnabling, sendMessage, permissions, availableModels, serviceId, piiDisplayMode, setShowPermissionForm, setIsPermissionFormOpen, } = useChatBot();
235
235
  const messagesEndRef = React.useRef(null);
236
236
  const scrollToBottom = () => {
237
237
  messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
@@ -246,6 +246,14 @@ export const MessageList = () => {
246
246
  if (isLoading) {
247
247
  return;
248
248
  }
249
+ const retryInterruptedTool = async () => {
250
+ if (message.metadata?.voiceAuthorizationPrompt === true) {
251
+ await retryVoicePermissionRequest();
252
+ }
253
+ else {
254
+ await retryPendingTextTool();
255
+ }
256
+ };
249
257
  if (buttonType === 'connectWallet') {
250
258
  const connected = await connectWallet(false);
251
259
  if (connected) {
@@ -253,20 +261,20 @@ export const MessageList = () => {
253
261
  // signature UI instead of leaving the user on a stale connect prompt.
254
262
  const signedIn = await signInWallet(true);
255
263
  if (signedIn)
256
- await retryPendingTextTool();
264
+ await retryInterruptedTool();
257
265
  }
258
266
  }
259
267
  else if (buttonType === 'signIn') {
260
268
  const signedIn = await signInWallet(true);
261
269
  if (signedIn)
262
- await retryPendingTextTool();
270
+ await retryInterruptedTool();
263
271
  }
264
272
  else if (buttonType === 'enableAIT') {
265
273
  const requiredPermission = message.metadata?.requiredPermission;
266
274
  if (requiredPermission) {
267
275
  const success = await enableAIT(requiredPermission);
268
276
  if (success) {
269
- await retryPendingTextTool();
277
+ await retryInterruptedTool();
270
278
  }
271
279
  }
272
280
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PermissionForm.d.ts","sourceRoot":"","sources":["../../src/ui/PermissionForm.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,UAAU,mBAAmB;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA2lBxD,CAAC"}
1
+ {"version":3,"file":"PermissionForm.d.ts","sourceRoot":"","sources":["../../src/ui/PermissionForm.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,UAAU,mBAAmB;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA6lBxD,CAAC"}
@@ -7,18 +7,24 @@ import { walletTextUtils } from '@bytexbyte/nxtlinq-ai-agent-web-development';
7
7
  import { hasRecordedWalletVerification } from '@bytexbyte/nxtlinq-ai-agent-core-development';
8
8
  import { actionButton } from './styles/isolatedStyles';
9
9
  export const PermissionForm = ({ onClose }) => {
10
- const { hitAddress, permissions, availablePermissions, runtimePermissionsUnavailable, setIsDisabled, onSave, onConnectWallet, onSignIn, isNeedSignInWithWallet, walletInfo, onVerifyWallet, serviceId, isAITLoading, isWalletLoading = false, refreshAIT, props } = useChatBot();
11
- const [isSaving, setIsSaving] = React.useState(false);
12
- const [tempPermissions, setTempPermissions] = React.useState(permissions);
10
+ const { hitAddress, permissions, availablePermissions, runtimePermissionsUnavailable, setIsDisabled, onSave, onConnectWallet, onSignIn, isNeedSignInWithWallet, walletInfo, onVerifyWallet, serviceId, isAITLoading, permissionSaveDraft, isPermissionSaving, isWalletLoading = false, refreshAIT, props } = useChatBot();
11
+ const [tempPermissions, setTempPermissions] = React.useState(permissionSaveDraft ?? permissions);
13
12
  const [hasUserInteracted, setHasUserInteracted] = React.useState(false);
13
+ const isPermissionFormBusy = isAITLoading || isPermissionSaving;
14
+ const hasPermissionSnapshot = permissionSaveDraft !== null
15
+ || availablePermissions.length > 0
16
+ || permissions.length > 0;
14
17
  // Check if ALL permission is selected
15
18
  const isAllSelected = tempPermissions.includes('ALL');
16
19
  // Update temp permissions when permissions change, but only if user hasn't interacted
17
20
  React.useEffect(() => {
18
- if (!hasUserInteracted) {
21
+ if (permissionSaveDraft) {
22
+ setTempPermissions(permissionSaveDraft);
23
+ }
24
+ else if (!hasUserInteracted) {
19
25
  setTempPermissions(permissions);
20
26
  }
21
- }, [permissions, hasUserInteracted]);
27
+ }, [permissions, permissionSaveDraft, hasUserInteracted]);
22
28
  // When ALL is selected, automatically select all other permissions
23
29
  // This handles cases where ALL is set from initial permissions or when availablePermissions loads
24
30
  React.useEffect(() => {
@@ -57,14 +63,8 @@ export const PermissionForm = ({ onClose }) => {
57
63
  const shouldShowVerificationPrompt = requireWalletIDVVerification
58
64
  && (!walletInfo?.id || !hasRecordedWalletVerification(walletInfo));
59
65
  const handleSave = async () => {
60
- setIsSaving(true);
61
- try {
62
- await onSave(tempPermissions);
63
- setHasUserInteracted(false);
64
- }
65
- finally {
66
- setIsSaving(false);
67
- }
66
+ await onSave(tempPermissions);
67
+ setHasUserInteracted(false);
68
68
  };
69
69
  const handleCancel = () => {
70
70
  // Reset temp permissions to original permissions
@@ -317,7 +317,7 @@ export const PermissionForm = ({ onClose }) => {
317
317
  font-size: 16px !important;
318
318
  color: #666 !important;
319
319
  flex-shrink: 0 !important;
320
- `, children: "Permissions" }), isAITLoading ? (_jsx("div", { css: css `
320
+ `, children: "Permissions" }), isAITLoading && !hasPermissionSnapshot ? (_jsx("div", { css: css `
321
321
  background-color: #f8f9fa !important;
322
322
  padding: 16px !important;
323
323
  border-radius: 8px !important;
@@ -353,17 +353,17 @@ export const PermissionForm = ({ onClose }) => {
353
353
  display: flex !important;
354
354
  align-items: center !important;
355
355
  gap: 12px !important;
356
- cursor: ${isAITLoading || isOtherPermissionDisabled ? 'not-allowed' : 'pointer'} !important;
356
+ cursor: ${isPermissionFormBusy || isOtherPermissionDisabled ? 'not-allowed' : 'pointer'} !important;
357
357
  padding: 8px !important;
358
358
  border-radius: 6px !important;
359
359
  transition: background-color 0.2s !important;
360
- opacity: ${isAITLoading || isOtherPermissionDisabled ? 0.6 : 1} !important;
360
+ opacity: ${isPermissionFormBusy || isOtherPermissionDisabled ? 0.6 : 1} !important;
361
361
 
362
362
  &:hover {
363
- background-color: ${!isAITLoading && !isOtherPermissionDisabled ? '#e9ecef' : 'transparent'} !important;
363
+ background-color: ${!isPermissionFormBusy && !isOtherPermissionDisabled ? '#e9ecef' : 'transparent'} !important;
364
364
  }
365
365
  `, children: [_jsx("input", { type: "checkbox", checked: isChecked, onChange: () => {
366
- if (!isAITLoading) {
366
+ if (!isPermissionFormBusy) {
367
367
  if (isAllPermission) {
368
368
  // Handle ALL permission toggle
369
369
  if (isAllSelected) {
@@ -392,11 +392,11 @@ export const PermissionForm = ({ onClose }) => {
392
392
  setHasUserInteracted(true);
393
393
  setIsDisabled(false);
394
394
  }
395
- }, disabled: isAITLoading || runtimePermissionsUnavailable || isOtherPermissionDisabled, css: css `
395
+ }, disabled: isPermissionFormBusy || runtimePermissionsUnavailable || isOtherPermissionDisabled, css: css `
396
396
  margin: 0 !important;
397
397
  width: 18px !important;
398
398
  height: 18px !important;
399
- cursor: ${isAITLoading || isOtherPermissionDisabled ? 'not-allowed' : 'pointer'} !important;
399
+ cursor: ${isPermissionFormBusy || isOtherPermissionDisabled ? 'not-allowed' : 'pointer'} !important;
400
400
  ` }), _jsx("span", { css: css `
401
401
  font-size: 14px !important;
402
402
  color: ${isOtherPermissionDisabled ? '#999' : '#333'} !important;
@@ -425,13 +425,13 @@ export const PermissionForm = ({ onClose }) => {
425
425
  background-color: #e9ecef !important;
426
426
  border-color: #ced4da !important;
427
427
  }
428
- `, children: "Cancel" }), _jsx("button", { onClick: handleSave, disabled: !hasPermissionChanges() || isSaving || isAITLoading || runtimePermissionsUnavailable, css: css `
428
+ `, children: "Cancel" }), _jsx("button", { onClick: handleSave, disabled: !hasPermissionChanges() || isPermissionFormBusy || runtimePermissionsUnavailable, css: css `
429
429
  padding: 10px 20px !important;
430
- background-color: ${!hasPermissionChanges() || isSaving || isAITLoading ? '#e9ecef' : '#007bff'} !important;
431
- color: ${!hasPermissionChanges() || isSaving || isAITLoading ? '#6c757d' : 'white'} !important;
430
+ background-color: ${!hasPermissionChanges() || isPermissionFormBusy ? '#e9ecef' : '#007bff'} !important;
431
+ color: ${!hasPermissionChanges() || isPermissionFormBusy ? '#6c757d' : 'white'} !important;
432
432
  border: none !important;
433
433
  border-radius: 8px !important;
434
- cursor: ${!hasPermissionChanges() || isSaving || isAITLoading ? 'not-allowed' : 'pointer'} !important;
434
+ cursor: ${!hasPermissionChanges() || isPermissionFormBusy ? 'not-allowed' : 'pointer'} !important;
435
435
  font-size: 14px !important;
436
436
  font-weight: 500 !important;
437
437
  transition: all 0.2s !important;
@@ -444,5 +444,5 @@ export const PermissionForm = ({ onClose }) => {
444
444
  &:hover:not(:disabled) {
445
445
  background-color: #0056b3 !important;
446
446
  }
447
- `, children: isSaving ? 'Saving...' : 'Save' })] })] }))] }));
447
+ `, children: isPermissionSaving ? 'Saving...' : isAITLoading ? 'Refreshing...' : 'Save' })] })] }))] }));
448
448
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytexbyte/nxtlinq-ai-agent-ui-react-development",
3
- "version": "0.4.11",
3
+ "version": "0.4.13",
4
4
  "description": "Official React Web UI for nxtlinq AI Agent — drop-in chat widget",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -38,8 +38,8 @@
38
38
  "react-dom": ">=18.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@bytexbyte/nxtlinq-ai-agent-core-development": "2.0.4",
42
- "@bytexbyte/nxtlinq-ai-agent-web-development": "1.0.5",
41
+ "@bytexbyte/nxtlinq-ai-agent-core-development": "2.0.5",
42
+ "@bytexbyte/nxtlinq-ai-agent-web-development": "1.0.6",
43
43
  "@emotion/react": "^11.14.0",
44
44
  "@emotion/styled": "^11.14.1",
45
45
  "@mui/icons-material": "^7.2.0",
@@ -58,6 +58,19 @@ import {
58
58
 
59
59
  const MIC_ENABLED_SESSION_KEY = 'chatbot-mic-enabled';
60
60
 
61
+ interface PermissionSaveState {
62
+ operationId: number;
63
+ subjectKey: string;
64
+ permissions: string[];
65
+ status: 'saving' | 'failed';
66
+ }
67
+
68
+ interface PermissionSaveOperation {
69
+ operationId: number;
70
+ subjectKey: string;
71
+ promise: Promise<void>;
72
+ }
73
+
61
74
  const ChatBotContext = React.createContext<ChatBotContextType | undefined>(undefined);
62
75
 
63
76
  export const useChatBot = () => {
@@ -186,6 +199,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
186
199
  const [showPermissionForm, setShowPermissionForm] = React.useState(false);
187
200
  const [isPermissionFormOpen, setIsPermissionFormOpen] = React.useState(false);
188
201
  const [isAITLoading, setIsAITLoading] = React.useState(false);
202
+ const [permissionSaveState, setPermissionSaveState] = React.useState<PermissionSaveState | null>(null);
189
203
  const [isDisabled, setIsDisabled] = React.useState(true);
190
204
  const [signer, setSigner] = React.useState<ethers.JsonRpcSigner | null>(null);
191
205
  const [walletInfo, setWalletInfo] = React.useState<any>(null);
@@ -272,6 +286,8 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
272
286
  }, []);
273
287
  const runtimeRecomputeCoordinatorRef = React.useRef(new RuntimeAITRecomputeCoordinator());
274
288
  const runtimeMutationQueueRef = React.useRef(new RuntimeAITMutationQueue());
289
+ const permissionSaveOperationRef = React.useRef<PermissionSaveOperation | null>(null);
290
+ const permissionSaveOperationIdRef = React.useRef(0);
275
291
  useProviderRuntimeCommit(
276
292
  hitAddressRef,
277
293
  hitAddress,
@@ -1179,6 +1195,17 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1179
1195
  return;
1180
1196
  }
1181
1197
 
1198
+ // updateActivePermissions publishes the authoritative result itself. A modal reopened while
1199
+ // that mutation is still running must keep rendering its submitted draft instead of queuing
1200
+ // a redundant GET that replaces the form with a loading state.
1201
+ const activePermissionSave = permissionSaveOperationRef.current;
1202
+ if (
1203
+ runtimeContext
1204
+ && activePermissionSave?.subjectKey === runtimeAITSubjectKey(runtimeContext)
1205
+ ) {
1206
+ return;
1207
+ }
1208
+
1182
1209
  authorizationLoadingStateRef.current.setAITLoading(true);
1183
1210
  setIsAITLoading(true);
1184
1211
  try {
@@ -1757,6 +1784,15 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
1757
1784
  if ('error' in response || (response as any).result === 'error') {
1758
1785
  // Check if it's an AIT-related error
1759
1786
  const errorResponse = response as any;
1787
+ // A backend authorization rejection happens before the frontend sees a
1788
+ // toolCall, so the normal preflight path below cannot remember this
1789
+ // turn. Preserve it here so a successful wallet/permission recovery
1790
+ // can replay the exact original request once.
1791
+ pendingTextToolRetryRef.current.captureAuthorizationRecovery(errorResponse, {
1792
+ content,
1793
+ isPresetMessage,
1794
+ attachments,
1795
+ });
1760
1796
  if (errorResponse.requiresWalletConnection) {
1761
1797
  // Case where wallet connection is required - trigger auto-connect logic
1762
1798
  // Try to auto-connect wallet first
@@ -2365,6 +2401,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
2365
2401
  exitVoiceMode,
2366
2402
  toggleMicMute: toggleVoiceMicMute,
2367
2403
  interruptVoice,
2404
+ retryVoicePermissionRequest,
2368
2405
  micRms,
2369
2406
  } = useVoiceMode({
2370
2407
  apiKey: apiKey ?? '',
@@ -2376,6 +2413,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
2376
2413
  aitId: ait?.aitId,
2377
2414
  walletAddress: hitAddress || undefined,
2378
2415
  aitToken: nxtlinqAITServiceAccessToken || undefined,
2416
+ permissions,
2379
2417
  metadata: customUserInfo,
2380
2418
  nxtlinqApi,
2381
2419
  getMessages: () => messagesRef.current,
@@ -2666,41 +2704,71 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
2666
2704
  // Save permissions
2667
2705
  const savePermissions = async (newPermissions?: string[]) => {
2668
2706
  setIsDisabled(true);
2669
- try {
2670
- if (!hitAddress) {
2671
- throw new Error('Please connect your wallet first');
2672
- }
2673
- const context: RuntimeAITContext = {
2674
- controller: hitAddress,
2675
- serviceId,
2676
- ...(roles !== undefined ? { roles: [...roles] } : {}),
2677
- ...(permissionGroup !== undefined ? { permissionGroup } : {}),
2678
- };
2679
- const checkedPermissions = [...(newPermissions ?? permissions)];
2680
- await runtimeMutationQueueRef.current.run(
2681
- runtimeAITSubjectKey(context),
2682
- async () => {
2683
- runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
2684
- await replaceRuntimeActivePermissions(context, checkedPermissions);
2685
- },
2686
- );
2687
- showSuccess('AIT permissions saved successfully! You can now use the AI agent with your configured permissions.');
2688
- setShowPermissionForm(false);
2689
- setIsPermissionFormOpen(false);
2690
- await retryPendingTextTool();
2691
- } catch (error) {
2692
- if (!(error instanceof StaleRuntimeAITResponseError)) {
2693
- console.error('Failed to update AIT deny-list:', error);
2694
- }
2707
+ if (!hitAddress) {
2695
2708
  setIsDisabled(false);
2696
- if (error instanceof StaleRuntimeAITResponseError) {
2697
- return;
2698
- } else if (error instanceof Error) {
2699
- showError(error.message);
2700
- } else {
2701
- showError('Failed to save permissions. Please try again.');
2702
- }
2709
+ showError('Please connect your wallet first');
2710
+ return;
2703
2711
  }
2712
+
2713
+ const context: RuntimeAITContext = {
2714
+ controller: hitAddress,
2715
+ serviceId,
2716
+ ...(roles !== undefined ? { roles: [...roles] } : {}),
2717
+ ...(permissionGroup !== undefined ? { permissionGroup } : {}),
2718
+ };
2719
+ const subjectKey = runtimeAITSubjectKey(context);
2720
+ const activeOperation = permissionSaveOperationRef.current;
2721
+ if (activeOperation?.subjectKey === subjectKey) {
2722
+ return activeOperation.promise;
2723
+ }
2724
+
2725
+ const checkedPermissions = [...(newPermissions ?? permissions)];
2726
+ const operationId = ++permissionSaveOperationIdRef.current;
2727
+ setPermissionSaveState({
2728
+ operationId,
2729
+ subjectKey,
2730
+ permissions: checkedPermissions,
2731
+ status: 'saving',
2732
+ });
2733
+
2734
+ const operation = (async () => {
2735
+ try {
2736
+ await runtimeMutationQueueRef.current.run(
2737
+ subjectKey,
2738
+ async () => {
2739
+ runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
2740
+ await replaceRuntimeActivePermissions(context, checkedPermissions);
2741
+ },
2742
+ );
2743
+ showSuccess('AIT permissions saved successfully! You can now use the AI agent with your configured permissions.');
2744
+ setShowPermissionForm(false);
2745
+ setIsPermissionFormOpen(false);
2746
+ setPermissionSaveState((current) => current?.operationId === operationId ? null : current);
2747
+ await retryPendingTextTool();
2748
+ } catch (error) {
2749
+ setPermissionSaveState((current) => current?.operationId === operationId
2750
+ ? { ...current, status: 'failed' }
2751
+ : current);
2752
+ if (!(error instanceof StaleRuntimeAITResponseError)) {
2753
+ console.error('Failed to update AIT deny-list:', error);
2754
+ }
2755
+ setIsDisabled(false);
2756
+ if (error instanceof StaleRuntimeAITResponseError) {
2757
+ return;
2758
+ } else if (error instanceof Error) {
2759
+ showError(error.message);
2760
+ } else {
2761
+ showError('Failed to save permissions. Please try again.');
2762
+ }
2763
+ } finally {
2764
+ if (permissionSaveOperationRef.current?.operationId === operationId) {
2765
+ permissionSaveOperationRef.current = null;
2766
+ }
2767
+ }
2768
+ })();
2769
+
2770
+ permissionSaveOperationRef.current = { operationId, subjectKey, promise: operation };
2771
+ return operation;
2704
2772
  };
2705
2773
 
2706
2774
  // Handle verify wallet click
@@ -2987,6 +3055,10 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
2987
3055
 
2988
3056
  const runtimeAuthorizationMatchesRenderedContext =
2989
3057
  runtimeAuthorizationMatchesContext(runtimeAuthorizationContextKey, runtimeContext);
3058
+ const renderedPermissionSaveState = runtimeContext
3059
+ && permissionSaveState?.subjectKey === runtimeAITSubjectKey(runtimeContext)
3060
+ ? permissionSaveState
3061
+ : null;
2990
3062
  const contextValue: ChatBotContextType = {
2991
3063
  // State
2992
3064
  messages,
@@ -3007,6 +3079,8 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
3007
3079
  showPermissionForm,
3008
3080
  isPermissionFormOpen,
3009
3081
  isAITLoading,
3082
+ permissionSaveDraft: renderedPermissionSaveState?.permissions ?? null,
3083
+ isPermissionSaving: renderedPermissionSaveState?.status === 'saving',
3010
3084
  isDisabled,
3011
3085
  walletInfo,
3012
3086
  isWalletLoading,
@@ -3079,6 +3153,7 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
3079
3153
  exitVoiceMode: handleExitVoiceMode,
3080
3154
  toggleVoiceMicMute,
3081
3155
  interruptVoice,
3156
+ retryVoicePermissionRequest,
3082
3157
  micRms,
3083
3158
  debugVoiceRms,
3084
3159
 
@@ -6,6 +6,11 @@ export type PendingTextToolRequest = {
6
6
  attachments?: Attachment[];
7
7
  };
8
8
 
9
+ export type TextToolAuthorizationRecovery = {
10
+ requiresWalletConnection?: boolean;
11
+ requiresPermissionUpdate?: boolean;
12
+ };
13
+
9
14
  /** Keeps one interrupted text-tool request and serializes all resume attempts. */
10
15
  export class PendingTextToolRetryController {
11
16
  private pending: PendingTextToolRequest | null = null;
@@ -15,6 +20,15 @@ export class PendingTextToolRetryController {
15
20
  this.pending = request;
16
21
  }
17
22
 
23
+ captureAuthorizationRecovery(
24
+ recovery: TextToolAuthorizationRecovery,
25
+ request: PendingTextToolRequest,
26
+ ): boolean {
27
+ if (!recovery.requiresWalletConnection && !recovery.requiresPermissionUpdate) return false;
28
+ this.set(request);
29
+ return true;
30
+ }
31
+
18
32
  clear(): void {
19
33
  this.pending = null;
20
34
  }
@@ -107,6 +107,8 @@ export interface ChatBotContextType {
107
107
  showPermissionForm: boolean;
108
108
  isPermissionFormOpen: boolean;
109
109
  isAITLoading: boolean;
110
+ permissionSaveDraft: string[] | null;
111
+ isPermissionSaving: boolean;
110
112
  isAITEnabling: boolean;
111
113
  isDisabled: boolean;
112
114
  walletInfo: any;
@@ -190,6 +192,7 @@ export interface ChatBotContextType {
190
192
  exitVoiceMode: () => Promise<void>;
191
193
  toggleVoiceMicMute: () => void;
192
194
  interruptVoice: () => void;
195
+ retryVoicePermissionRequest: () => Promise<boolean>;
193
196
 
194
197
  // Additional properties for PermissionForm
195
198
  onSave: (newPermissions?: string[]) => Promise<void>;
@@ -320,6 +320,7 @@ export const MessageList: React.FC = () => {
320
320
  connectWallet,
321
321
  signInWallet,
322
322
  retryPendingTextTool,
323
+ retryVoicePermissionRequest,
323
324
  hitAddress,
324
325
  isAutoConnecting,
325
326
  isNeedSignInWithWallet,
@@ -353,23 +354,31 @@ export const MessageList: React.FC = () => {
353
354
  return;
354
355
  }
355
356
 
357
+ const retryInterruptedTool = async () => {
358
+ if (message.metadata?.voiceAuthorizationPrompt === true) {
359
+ await retryVoicePermissionRequest();
360
+ } else {
361
+ await retryPendingTextTool();
362
+ }
363
+ };
364
+
356
365
  if (buttonType === 'connectWallet') {
357
366
  const connected = await connectWallet(false);
358
367
  if (connected) {
359
368
  // Continue the interrupted flow immediately. This opens the wallet
360
369
  // signature UI instead of leaving the user on a stale connect prompt.
361
370
  const signedIn = await signInWallet(true);
362
- if (signedIn) await retryPendingTextTool();
371
+ if (signedIn) await retryInterruptedTool();
363
372
  }
364
373
  } else if (buttonType === 'signIn') {
365
374
  const signedIn = await signInWallet(true);
366
- if (signedIn) await retryPendingTextTool();
375
+ if (signedIn) await retryInterruptedTool();
367
376
  } else if (buttonType === 'enableAIT') {
368
377
  const requiredPermission = message.metadata?.requiredPermission;
369
378
  if (requiredPermission) {
370
379
  const success = await enableAIT(requiredPermission);
371
380
  if (success) {
372
- await retryPendingTextTool();
381
+ await retryInterruptedTool();
373
382
  }
374
383
  }
375
384
  } else if (buttonType === 'verifyWallet') {
@@ -25,24 +25,31 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
25
25
  onVerifyWallet,
26
26
  serviceId,
27
27
  isAITLoading,
28
+ permissionSaveDraft,
29
+ isPermissionSaving,
28
30
  isWalletLoading = false,
29
31
  refreshAIT,
30
32
  props
31
33
  } = useChatBot();
32
34
 
33
- const [isSaving, setIsSaving] = React.useState(false);
34
- const [tempPermissions, setTempPermissions] = React.useState<string[]>(permissions);
35
+ const [tempPermissions, setTempPermissions] = React.useState<string[]>(permissionSaveDraft ?? permissions);
35
36
  const [hasUserInteracted, setHasUserInteracted] = React.useState(false);
37
+ const isPermissionFormBusy = isAITLoading || isPermissionSaving;
38
+ const hasPermissionSnapshot = permissionSaveDraft !== null
39
+ || availablePermissions.length > 0
40
+ || permissions.length > 0;
36
41
 
37
42
  // Check if ALL permission is selected
38
43
  const isAllSelected = tempPermissions.includes('ALL');
39
44
 
40
45
  // Update temp permissions when permissions change, but only if user hasn't interacted
41
46
  React.useEffect(() => {
42
- if (!hasUserInteracted) {
47
+ if (permissionSaveDraft) {
48
+ setTempPermissions(permissionSaveDraft);
49
+ } else if (!hasUserInteracted) {
43
50
  setTempPermissions(permissions);
44
51
  }
45
- }, [permissions, hasUserInteracted]);
52
+ }, [permissions, permissionSaveDraft, hasUserInteracted]);
46
53
 
47
54
  // When ALL is selected, automatically select all other permissions
48
55
  // This handles cases where ALL is set from initial permissions or when availablePermissions loads
@@ -86,13 +93,8 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
86
93
  && (!walletInfo?.id || !hasRecordedWalletVerification(walletInfo));
87
94
 
88
95
  const handleSave = async () => {
89
- setIsSaving(true);
90
- try {
91
- await onSave(tempPermissions);
92
- setHasUserInteracted(false);
93
- } finally {
94
- setIsSaving(false);
95
- }
96
+ await onSave(tempPermissions);
97
+ setHasUserInteracted(false);
96
98
  };
97
99
 
98
100
  const handleCancel = () => {
@@ -439,7 +441,7 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
439
441
  color: #666 !important;
440
442
  flex-shrink: 0 !important;
441
443
  `}>Permissions</h4>
442
- {isAITLoading ? (
444
+ {isAITLoading && !hasPermissionSnapshot ? (
443
445
  <div css={css`
444
446
  background-color: #f8f9fa !important;
445
447
  padding: 16px !important;
@@ -489,14 +491,14 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
489
491
  display: flex !important;
490
492
  align-items: center !important;
491
493
  gap: 12px !important;
492
- cursor: ${isAITLoading || isOtherPermissionDisabled ? 'not-allowed' : 'pointer'} !important;
494
+ cursor: ${isPermissionFormBusy || isOtherPermissionDisabled ? 'not-allowed' : 'pointer'} !important;
493
495
  padding: 8px !important;
494
496
  border-radius: 6px !important;
495
497
  transition: background-color 0.2s !important;
496
- opacity: ${isAITLoading || isOtherPermissionDisabled ? 0.6 : 1} !important;
498
+ opacity: ${isPermissionFormBusy || isOtherPermissionDisabled ? 0.6 : 1} !important;
497
499
 
498
500
  &:hover {
499
- background-color: ${!isAITLoading && !isOtherPermissionDisabled ? '#e9ecef' : 'transparent'} !important;
501
+ background-color: ${!isPermissionFormBusy && !isOtherPermissionDisabled ? '#e9ecef' : 'transparent'} !important;
500
502
  }
501
503
  `}
502
504
  >
@@ -504,7 +506,7 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
504
506
  type="checkbox"
505
507
  checked={isChecked}
506
508
  onChange={() => {
507
- if (!isAITLoading) {
509
+ if (!isPermissionFormBusy) {
508
510
  if (isAllPermission) {
509
511
  // Handle ALL permission toggle
510
512
  if (isAllSelected) {
@@ -532,12 +534,12 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
532
534
  setIsDisabled(false);
533
535
  }
534
536
  }}
535
- disabled={isAITLoading || runtimePermissionsUnavailable || isOtherPermissionDisabled}
537
+ disabled={isPermissionFormBusy || runtimePermissionsUnavailable || isOtherPermissionDisabled}
536
538
  css={css`
537
539
  margin: 0 !important;
538
540
  width: 18px !important;
539
541
  height: 18px !important;
540
- cursor: ${isAITLoading || isOtherPermissionDisabled ? 'not-allowed' : 'pointer'} !important;
542
+ cursor: ${isPermissionFormBusy || isOtherPermissionDisabled ? 'not-allowed' : 'pointer'} !important;
541
543
  `}
542
544
  />
543
545
  <span css={css`
@@ -584,14 +586,14 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
584
586
  </button>
585
587
  <button
586
588
  onClick={handleSave}
587
- disabled={!hasPermissionChanges() || isSaving || isAITLoading || runtimePermissionsUnavailable}
589
+ disabled={!hasPermissionChanges() || isPermissionFormBusy || runtimePermissionsUnavailable}
588
590
  css={css`
589
591
  padding: 10px 20px !important;
590
- background-color: ${!hasPermissionChanges() || isSaving || isAITLoading ? '#e9ecef' : '#007bff'} !important;
591
- color: ${!hasPermissionChanges() || isSaving || isAITLoading ? '#6c757d' : 'white'} !important;
592
+ background-color: ${!hasPermissionChanges() || isPermissionFormBusy ? '#e9ecef' : '#007bff'} !important;
593
+ color: ${!hasPermissionChanges() || isPermissionFormBusy ? '#6c757d' : 'white'} !important;
592
594
  border: none !important;
593
595
  border-radius: 8px !important;
594
- cursor: ${!hasPermissionChanges() || isSaving || isAITLoading ? 'not-allowed' : 'pointer'} !important;
596
+ cursor: ${!hasPermissionChanges() || isPermissionFormBusy ? 'not-allowed' : 'pointer'} !important;
595
597
  font-size: 14px !important;
596
598
  font-weight: 500 !important;
597
599
  transition: all 0.2s !important;
@@ -606,7 +608,7 @@ export const PermissionForm: React.FC<PermissionFormProps> = ({ onClose }) => {
606
608
  }
607
609
  `}
608
610
  >
609
- {isSaving ? 'Saving...' : 'Save'}
611
+ {isPermissionSaving ? 'Saving...' : isAITLoading ? 'Refreshing...' : 'Save'}
610
612
  </button>
611
613
  </div>
612
614
  </>