@bytexbyte/nxtlinq-ai-agent-ui-react-development 0.4.12 → 0.4.14

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,CAggGlD,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 {
@@ -2342,41 +2353,68 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2342
2353
  // Save permissions
2343
2354
  const savePermissions = async (newPermissions) => {
2344
2355
  setIsDisabled(true);
2345
- try {
2346
- if (!hitAddress) {
2347
- throw new Error('Please connect your wallet first');
2348
- }
2349
- const context = {
2350
- controller: hitAddress,
2351
- serviceId,
2352
- ...(roles !== undefined ? { roles: [...roles] } : {}),
2353
- ...(permissionGroup !== undefined ? { permissionGroup } : {}),
2354
- };
2355
- const checkedPermissions = [...(newPermissions ?? permissions)];
2356
- await runtimeMutationQueueRef.current.run(runtimeAITSubjectKey(context), async () => {
2357
- runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
2358
- await replaceRuntimeActivePermissions(context, checkedPermissions);
2359
- });
2360
- showSuccess('AIT permissions saved successfully! You can now use the AI agent with your configured permissions.');
2361
- setShowPermissionForm(false);
2362
- setIsPermissionFormOpen(false);
2363
- await retryPendingTextTool();
2364
- }
2365
- catch (error) {
2366
- if (!(error instanceof StaleRuntimeAITResponseError)) {
2367
- console.error('Failed to update AIT deny-list:', error);
2368
- }
2356
+ if (!hitAddress) {
2369
2357
  setIsDisabled(false);
2370
- if (error instanceof StaleRuntimeAITResponseError) {
2371
- 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();
2372
2391
  }
2373
- else if (error instanceof Error) {
2374
- 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
+ }
2375
2409
  }
2376
- else {
2377
- showError('Failed to save permissions. Please try again.');
2410
+ finally {
2411
+ if (permissionSaveOperationRef.current?.operationId === operationId) {
2412
+ permissionSaveOperationRef.current = null;
2413
+ }
2378
2414
  }
2379
- }
2415
+ })();
2416
+ permissionSaveOperationRef.current = { operationId, subjectKey, promise: operation };
2417
+ return operation;
2380
2418
  };
2381
2419
  // Handle verify wallet click
2382
2420
  const handleVerifyWalletClick = async (method) => {
@@ -2653,6 +2691,10 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2653
2691
  updateExternalId();
2654
2692
  }, [hitAddress, nxtlinqApi, authFields, pseudoId]);
2655
2693
  const runtimeAuthorizationMatchesRenderedContext = runtimeAuthorizationMatchesContext(runtimeAuthorizationContextKey, runtimeContext);
2694
+ const renderedPermissionSaveState = runtimeContext
2695
+ && permissionSaveState?.subjectKey === runtimeAITSubjectKey(runtimeContext)
2696
+ ? permissionSaveState
2697
+ : null;
2656
2698
  const contextValue = {
2657
2699
  // State
2658
2700
  messages,
@@ -2673,6 +2715,8 @@ customError, voiceThresholds, debugVoiceRms = false, sttGlossary, }) => {
2673
2715
  showPermissionForm,
2674
2716
  isPermissionFormOpen,
2675
2717
  isAITLoading,
2718
+ permissionSaveDraft: renderedPermissionSaveState?.permissions ?? null,
2719
+ isPermissionSaving: renderedPermissionSaveState?.status === 'saving',
2676
2720
  isDisabled,
2677
2721
  walletInfo,
2678
2722
  isWalletLoading,
@@ -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;
@@ -1 +1 @@
1
- {"version":3,"file":"ChatBotTypes.d.ts","sourceRoot":"","sources":["../../src/types/ChatBotTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAErI,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;AAGD,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AACD,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,CACV,OAAO,EAAE,OAAO,EAChB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,KAAK,IAAI,KACP,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC7B,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,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC;IACjD,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC;QAC7B,KAAK,EAAE,MAAM,CAAC;KACf,GAAG,SAAS,CAAC,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC,WAAW,CAAC,EAAE,iBAAiB,GAAG,eAAe,CAAC;IAElD,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAErC,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC,YAAY,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACvC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,eAAe,CAAC,EAAE;QAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sGAAsG;IACtG,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IAEjC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,6BAA6B,EAAE,OAAO,CAAC;IACvC,oBAAoB,EAAE,iBAAiB,EAAE,CAAC;IAC1C,kBAAkB,EAAE,OAAO,CAAC;IAC5B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,GAAG,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE;QACZ,IAAI,EAAE,OAAO,CAAC;QACd,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;QAC/C,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,YAAY,EAAE,OAAO,CAAC;IACtB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAChD,eAAe,EAAE,OAAO,CAAC;IAEzB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,EAAE,aAAa,EAAE,CAAC;IAG7B,cAAc,EAAE,OAAO,GAAG,UAAU,CAAC;IAErC,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IAGvB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,qBAAqB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,uBAAuB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,aAAa,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,eAAe,EAAE,CAAC,YAAY,EAAE,GAAG,KAAK,IAAI,CAAC;IAE7C,qBAAqB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,CAAC,WAAW,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IACvD,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,sBAAsB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAGnD,aAAa,EAAE,CAAC,qBAAqB,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC,CAAC;IACxF,YAAY,EAAE,CAAC,sBAAsB,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrE,oBAAoB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5H,YAAY,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChF,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,eAAe,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,uBAAuB,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,EAAE,CAAC,sBAAsB,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,IAAI,CAAC;IAE3B,iBAAiB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,eAAe,EAAE,MAAM,OAAO,CAAC;IAE/B,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,gBAAgB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/E,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,2BAA2B,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAGpD,MAAM,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,eAAe,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC,CAAC;IAC3D,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACjC,sBAAsB,EAAE,OAAO,CAAC;IAChC,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAGjB,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB"}
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":"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.12",
3
+ "version": "0.4.14",
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",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@bytexbyte/nxtlinq-ai-agent-core-development": "2.0.5",
42
- "@bytexbyte/nxtlinq-ai-agent-web-development": "1.0.6",
42
+ "@bytexbyte/nxtlinq-ai-agent-web-development": "1.0.7",
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 {
@@ -2677,41 +2704,71 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
2677
2704
  // Save permissions
2678
2705
  const savePermissions = async (newPermissions?: string[]) => {
2679
2706
  setIsDisabled(true);
2680
- try {
2681
- if (!hitAddress) {
2682
- throw new Error('Please connect your wallet first');
2683
- }
2684
- const context: RuntimeAITContext = {
2685
- controller: hitAddress,
2686
- serviceId,
2687
- ...(roles !== undefined ? { roles: [...roles] } : {}),
2688
- ...(permissionGroup !== undefined ? { permissionGroup } : {}),
2689
- };
2690
- const checkedPermissions = [...(newPermissions ?? permissions)];
2691
- await runtimeMutationQueueRef.current.run(
2692
- runtimeAITSubjectKey(context),
2693
- async () => {
2694
- runtimeRecomputeCoordinatorRef.current.assertContextActive(context);
2695
- await replaceRuntimeActivePermissions(context, checkedPermissions);
2696
- },
2697
- );
2698
- showSuccess('AIT permissions saved successfully! You can now use the AI agent with your configured permissions.');
2699
- setShowPermissionForm(false);
2700
- setIsPermissionFormOpen(false);
2701
- await retryPendingTextTool();
2702
- } catch (error) {
2703
- if (!(error instanceof StaleRuntimeAITResponseError)) {
2704
- console.error('Failed to update AIT deny-list:', error);
2705
- }
2707
+ if (!hitAddress) {
2706
2708
  setIsDisabled(false);
2707
- if (error instanceof StaleRuntimeAITResponseError) {
2708
- return;
2709
- } else if (error instanceof Error) {
2710
- showError(error.message);
2711
- } else {
2712
- showError('Failed to save permissions. Please try again.');
2713
- }
2709
+ showError('Please connect your wallet first');
2710
+ return;
2714
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;
2715
2772
  };
2716
2773
 
2717
2774
  // Handle verify wallet click
@@ -2998,6 +3055,10 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
2998
3055
 
2999
3056
  const runtimeAuthorizationMatchesRenderedContext =
3000
3057
  runtimeAuthorizationMatchesContext(runtimeAuthorizationContextKey, runtimeContext);
3058
+ const renderedPermissionSaveState = runtimeContext
3059
+ && permissionSaveState?.subjectKey === runtimeAITSubjectKey(runtimeContext)
3060
+ ? permissionSaveState
3061
+ : null;
3001
3062
  const contextValue: ChatBotContextType = {
3002
3063
  // State
3003
3064
  messages,
@@ -3018,6 +3079,8 @@ export const ChatBotProvider: React.FC<ChatBotProps> = ({
3018
3079
  showPermissionForm,
3019
3080
  isPermissionFormOpen,
3020
3081
  isAITLoading,
3082
+ permissionSaveDraft: renderedPermissionSaveState?.permissions ?? null,
3083
+ isPermissionSaving: renderedPermissionSaveState?.status === 'saving',
3021
3084
  isDisabled,
3022
3085
  walletInfo,
3023
3086
  isWalletLoading,
@@ -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;
@@ -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
  </>