@iblai/iblai-js 2.3.9 → 2.3.10

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.
@@ -1134,7 +1134,9 @@ function useCuaDriver() {
1134
1134
  // treating null as denied would make Cowork unusable on Linux and Windows.
1135
1135
  const [accessibilityPermission, setAccessibilityPermission] = useState(null);
1136
1136
  const [screenRecordingPermission, setScreenRecordingPermission] = useState(null);
1137
- const hasCheckedStatus = useRef(false);
1137
+ // True once the host has answered a status check this mount — the retry
1138
+ // loop's stop condition, NOT a "ran once" guard (that was the bug).
1139
+ const statusSettled = useRef(false);
1138
1140
  /**
1139
1141
  * Read one permission. Checking never prompts (`AXIsProcessTrusted` /
1140
1142
  * `CGPreflightScreenCaptureAccess`), which is what makes it safe to do on mount.
@@ -1232,11 +1234,13 @@ function useCuaDriver() {
1232
1234
  }));
1233
1235
  }, [setState]);
1234
1236
  /**
1235
- * Query driver install status + session support from the host.
1237
+ * Query driver install status + session support from the host. Returns
1238
+ * whether the host actually answered — the mount effect retries on `false`,
1239
+ * because one failed check must not disable Cowork for the whole session.
1236
1240
  */
1237
1241
  const checkStatus = useCallback(async () => {
1238
1242
  if (!isAvailable)
1239
- return;
1243
+ return false;
1240
1244
  // Refresh both permissions alongside install status (independent, no prompt).
1241
1245
  refreshPermissions();
1242
1246
  try {
@@ -1249,10 +1253,12 @@ function useCuaDriver() {
1249
1253
  progress: result.installed ? 100 : prev.progress,
1250
1254
  lastUpdated: new Date().toISOString(),
1251
1255
  }));
1256
+ return true;
1252
1257
  }
1253
1258
  catch (error) {
1254
1259
  console.error('[useCuaDriver] Failed to check status:', error);
1255
1260
  setState((prev) => ({ ...prev, status: 'idle' }));
1261
+ return false;
1256
1262
  }
1257
1263
  }, [isAvailable, invoke, setState, refreshPermissions]);
1258
1264
  /**
@@ -1372,12 +1378,41 @@ function useCuaDriver() {
1372
1378
  };
1373
1379
  // Only re-subscribe if the host itself changes — never on state.
1374
1380
  }, [isAvailable, listen]);
1375
- // Check status once on mount (in the desktop app).
1381
+ // Check status on mount, and RETRY until the host answers once.
1382
+ //
1383
+ // This used to be one-shot behind a ref, which is why a built app could show
1384
+ // Cowork grey for a whole session: the single check raced the IPC bridge
1385
+ // being injected into the (remote) app origin, failed, and nothing ever asked
1386
+ // again — `status` stayed null and null renders as "not supported". Dev never
1387
+ // showed it because fast-refresh remounts re-ran the check constantly; a
1388
+ // production build gets exactly one mount, so it retries with backoff instead.
1389
+ // Capped: a host that still hasn't answered after ~75s is genuinely broken,
1390
+ // and install()/stop() re-check on their own paths anyway.
1376
1391
  useEffect(() => {
1377
- if (!isAvailable || hasCheckedStatus.current)
1392
+ if (!isAvailable)
1378
1393
  return;
1379
- hasCheckedStatus.current = true;
1380
- checkStatus();
1394
+ let cancelled = false;
1395
+ let timer;
1396
+ let attempt = 0;
1397
+ const MAX_ATTEMPTS = 8;
1398
+ const run = async () => {
1399
+ if (cancelled || statusSettled.current)
1400
+ return;
1401
+ if (await checkStatus()) {
1402
+ statusSettled.current = true;
1403
+ return;
1404
+ }
1405
+ attempt += 1;
1406
+ if (cancelled || attempt >= MAX_ATTEMPTS)
1407
+ return;
1408
+ timer = setTimeout(run, Math.min(1000 * 2 ** attempt, 15000));
1409
+ };
1410
+ run();
1411
+ return () => {
1412
+ cancelled = true;
1413
+ if (timer)
1414
+ clearTimeout(timer);
1415
+ };
1381
1416
  }, [isAvailable, checkStatus]);
1382
1417
  // Only expose the feature as available inside the desktop (Tauri) app.
1383
1418
  const finalIsAvailable = isAvailable && isTauriApp();
@@ -255692,7 +255692,9 @@ function useCuaDriver() {
255692
255692
  // treating null as denied would make Cowork unusable on Linux and Windows.
255693
255693
  const [accessibilityPermission, setAccessibilityPermission] = useState(null);
255694
255694
  const [screenRecordingPermission, setScreenRecordingPermission] = useState(null);
255695
- const hasCheckedStatus = useRef(false);
255695
+ // True once the host has answered a status check this mount — the retry
255696
+ // loop's stop condition, NOT a "ran once" guard (that was the bug).
255697
+ const statusSettled = useRef(false);
255696
255698
  /**
255697
255699
  * Read one permission. Checking never prompts (`AXIsProcessTrusted` /
255698
255700
  * `CGPreflightScreenCaptureAccess`), which is what makes it safe to do on mount.
@@ -255790,11 +255792,13 @@ function useCuaDriver() {
255790
255792
  }));
255791
255793
  }, [setState]);
255792
255794
  /**
255793
- * Query driver install status + session support from the host.
255795
+ * Query driver install status + session support from the host. Returns
255796
+ * whether the host actually answered — the mount effect retries on `false`,
255797
+ * because one failed check must not disable Cowork for the whole session.
255794
255798
  */
255795
255799
  const checkStatus = useCallback(async () => {
255796
255800
  if (!isAvailable)
255797
- return;
255801
+ return false;
255798
255802
  // Refresh both permissions alongside install status (independent, no prompt).
255799
255803
  refreshPermissions();
255800
255804
  try {
@@ -255807,10 +255811,12 @@ function useCuaDriver() {
255807
255811
  progress: result.installed ? 100 : prev.progress,
255808
255812
  lastUpdated: new Date().toISOString(),
255809
255813
  }));
255814
+ return true;
255810
255815
  }
255811
255816
  catch (error) {
255812
255817
  console.error('[useCuaDriver] Failed to check status:', error);
255813
255818
  setState((prev) => ({ ...prev, status: 'idle' }));
255819
+ return false;
255814
255820
  }
255815
255821
  }, [isAvailable, invoke, setState, refreshPermissions]);
255816
255822
  /**
@@ -255930,12 +255936,41 @@ function useCuaDriver() {
255930
255936
  };
255931
255937
  // Only re-subscribe if the host itself changes — never on state.
255932
255938
  }, [isAvailable, listen]);
255933
- // Check status once on mount (in the desktop app).
255939
+ // Check status on mount, and RETRY until the host answers once.
255940
+ //
255941
+ // This used to be one-shot behind a ref, which is why a built app could show
255942
+ // Cowork grey for a whole session: the single check raced the IPC bridge
255943
+ // being injected into the (remote) app origin, failed, and nothing ever asked
255944
+ // again — `status` stayed null and null renders as "not supported". Dev never
255945
+ // showed it because fast-refresh remounts re-ran the check constantly; a
255946
+ // production build gets exactly one mount, so it retries with backoff instead.
255947
+ // Capped: a host that still hasn't answered after ~75s is genuinely broken,
255948
+ // and install()/stop() re-check on their own paths anyway.
255934
255949
  useEffect(() => {
255935
- if (!isAvailable || hasCheckedStatus.current)
255950
+ if (!isAvailable)
255936
255951
  return;
255937
- hasCheckedStatus.current = true;
255938
- checkStatus();
255952
+ let cancelled = false;
255953
+ let timer;
255954
+ let attempt = 0;
255955
+ const MAX_ATTEMPTS = 8;
255956
+ const run = async () => {
255957
+ if (cancelled || statusSettled.current)
255958
+ return;
255959
+ if (await checkStatus()) {
255960
+ statusSettled.current = true;
255961
+ return;
255962
+ }
255963
+ attempt += 1;
255964
+ if (cancelled || attempt >= MAX_ATTEMPTS)
255965
+ return;
255966
+ timer = setTimeout(run, Math.min(1000 * 2 ** attempt, 15000));
255967
+ };
255968
+ run();
255969
+ return () => {
255970
+ cancelled = true;
255971
+ if (timer)
255972
+ clearTimeout(timer);
255973
+ };
255939
255974
  }, [isAvailable, checkStatus]);
255940
255975
  // Only expose the feature as available inside the desktop (Tauri) app.
255941
255976
  const finalIsAvailable = isAvailable && isTauriApp$1();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iblai/iblai-js",
3
- "version": "2.3.9",
3
+ "version": "2.3.10",
4
4
  "description": "Unified JavaScript SDK for IBL.ai — re-exports data-layer, web-containers, and web-utils under a single package",
5
5
  "type": "module",
6
6
  "engines": {
@@ -78,7 +78,7 @@
78
78
  "winston": "3.19.0",
79
79
  "@iblai/data-layer": "1.12.3",
80
80
  "@iblai/mcp": "1.8.10",
81
- "@iblai/web-containers": "1.16.5",
81
+ "@iblai/web-containers": "1.16.6",
82
82
  "@iblai/web-utils": "2.1.11"
83
83
  },
84
84
  "peerDependencies": {