jupyterlab_ai_code_assistants_extension 1.0.29 → 1.0.30

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/lib/index.js CHANGED
@@ -64,18 +64,34 @@ const plugin = {
64
64
  let status = null;
65
65
  let settings = null;
66
66
  let sidebar = DEFAULT_SIDEBAR;
67
+ let probeGeneration = 0;
67
68
  /** Ask the server which providers exist and which of their binaries are on
68
- * PATH. A failure leaves the extension activated with no panels - never a
69
- * dialog, and never a thrown activation. */
69
+ * PATH. An unsuperseded failure leaves the extension activated with no
70
+ * panels until a later probe succeeds - never a dialog, never a thrown
71
+ * activation.
72
+ *
73
+ * Probes overlap: a probe can outlive one issued after it, so verdicts do
74
+ * not arrive in the order they were asked for. A ROSTER is always written,
75
+ * whichever settles last - both are answers from the same server, and the
76
+ * loser is at worst one client deadline stale (DEF-125). A FAILURE is not an answer, so it
77
+ * is written only while no newer probe has been issued; otherwise a
78
+ * timeout could discard a roster the server has since confirmed, undocking
79
+ * every panel while it is healthy (DEF-121). No request is suppressed. */
70
80
  const probeStatus = async () => {
81
+ const mine = ++probeGeneration;
71
82
  try {
72
83
  status = await requestAPI('status', serverSettings, {
73
84
  cache: 'no-store'
74
85
  });
75
86
  }
76
87
  catch (err) {
77
- console.warn(`${LOG_PREFIX} status probe failed; no assistant panels will be docked.`, err);
78
- status = null;
88
+ // No cadence named here: this also prints from the activation probe,
89
+ // and neither the interval nor the wake listeners are armed until
90
+ // after `await migrate()` below (DEF-122).
91
+ console.warn(`${LOG_PREFIX} status probe failed; panels dock again once a later probe succeeds.`, err);
92
+ if (mine === probeGeneration) {
93
+ status = null;
94
+ }
79
95
  }
80
96
  };
81
97
  const statusFor = (id) => { var _a, _b; return (_b = (_a = status === null || status === void 0 ? void 0 : status.providers) === null || _a === void 0 ? void 0 : _a.find(p => p.id === id)) !== null && _b !== void 0 ? _b : null; };
@@ -306,6 +322,21 @@ const plugin = {
306
322
  window.setInterval(() => {
307
323
  void probeStatus().then(reconcile);
308
324
  }, STATUS_INTERVAL_MS);
325
+ // A suspended network stack - laptop asleep, tab backgrounded - is exactly
326
+ // when the probe fails, and the slow cadence above would leave the sidebar
327
+ // panel-less for up to a minute after the machine comes back. A wake fires
328
+ // both events, so it costs two probes; deliberately, because a guard that
329
+ // let the second join the first answered the wake with a request issued
330
+ // before it, and starved the interval above of its own tick (DEF-117).
331
+ const reprobe = () => {
332
+ void probeStatus().then(reconcile);
333
+ };
334
+ window.addEventListener('online', reprobe);
335
+ document.addEventListener('visibilitychange', () => {
336
+ if (document.visibilityState === 'visible') {
337
+ reprobe();
338
+ }
339
+ });
309
340
  console.log(`${LOG_PREFIX} ${live.size} of ${registry.ids.length} assistant panel(s) docked.`);
310
341
  }
311
342
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab_ai_code_assistants_extension",
3
- "version": "1.0.29",
3
+ "version": "1.0.30",
4
4
  "description": "Jupyterlab extension to replace individual jupyterlab_*_code_extension and integrate into one, that offers joint capability, joint settings page and individual right toolbar panels per code assistant, with the ability in settings to decide which code assistant support is on (default - all are enabled)",
5
5
  "keywords": [
6
6
  "jupyter",
package/src/index.ts CHANGED
@@ -116,20 +116,37 @@ const plugin: JupyterFrontEndPlugin<void> = {
116
116
  let settings: ISettingRegistry.ISettings | null = null;
117
117
  let sidebar: Sidebar = DEFAULT_SIDEBAR;
118
118
 
119
+ let probeGeneration = 0;
120
+
119
121
  /** Ask the server which providers exist and which of their binaries are on
120
- * PATH. A failure leaves the extension activated with no panels - never a
121
- * dialog, and never a thrown activation. */
122
+ * PATH. An unsuperseded failure leaves the extension activated with no
123
+ * panels until a later probe succeeds - never a dialog, never a thrown
124
+ * activation.
125
+ *
126
+ * Probes overlap: a probe can outlive one issued after it, so verdicts do
127
+ * not arrive in the order they were asked for. A ROSTER is always written,
128
+ * whichever settles last - both are answers from the same server, and the
129
+ * loser is at worst one client deadline stale (DEF-125). A FAILURE is not an answer, so it
130
+ * is written only while no newer probe has been issued; otherwise a
131
+ * timeout could discard a roster the server has since confirmed, undocking
132
+ * every panel while it is healthy (DEF-121). No request is suppressed. */
122
133
  const probeStatus = async (): Promise<void> => {
134
+ const mine = ++probeGeneration;
123
135
  try {
124
136
  status = await requestAPI<IStatusResponse>('status', serverSettings, {
125
137
  cache: 'no-store'
126
138
  });
127
139
  } catch (err) {
140
+ // No cadence named here: this also prints from the activation probe,
141
+ // and neither the interval nor the wake listeners are armed until
142
+ // after `await migrate()` below (DEF-122).
128
143
  console.warn(
129
- `${LOG_PREFIX} status probe failed; no assistant panels will be docked.`,
144
+ `${LOG_PREFIX} status probe failed; panels dock again once a later probe succeeds.`,
130
145
  err
131
146
  );
132
- status = null;
147
+ if (mine === probeGeneration) {
148
+ status = null;
149
+ }
133
150
  }
134
151
  };
135
152
 
@@ -392,6 +409,22 @@ const plugin: JupyterFrontEndPlugin<void> = {
392
409
  void probeStatus().then(reconcile);
393
410
  }, STATUS_INTERVAL_MS);
394
411
 
412
+ // A suspended network stack - laptop asleep, tab backgrounded - is exactly
413
+ // when the probe fails, and the slow cadence above would leave the sidebar
414
+ // panel-less for up to a minute after the machine comes back. A wake fires
415
+ // both events, so it costs two probes; deliberately, because a guard that
416
+ // let the second join the first answered the wake with a request issued
417
+ // before it, and starved the interval above of its own tick (DEF-117).
418
+ const reprobe = (): void => {
419
+ void probeStatus().then(reconcile);
420
+ };
421
+ window.addEventListener('online', reprobe);
422
+ document.addEventListener('visibilitychange', () => {
423
+ if (document.visibilityState === 'visible') {
424
+ reprobe();
425
+ }
426
+ });
427
+
395
428
  console.log(
396
429
  `${LOG_PREFIX} ${live.size} of ${registry.ids.length} assistant panel(s) docked.`
397
430
  );