jupyterlab_ai_code_assistants_extension 1.2.38 → 1.2.41

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.
@@ -41,8 +41,8 @@ function warnLegacyCompanion() {
41
41
  // And said where the user is looking. The symptom is tabs that are simply
42
42
  // plain, which reads as a feature that was never on rather than as something
43
43
  // to fix, and a console line only reaches someone already suspecting the
44
- // cause. The two neighbouring "an installed thing is wrong" announcements in
45
- // this extension both pair the console line with this toast. It does not
44
+ // cause. The retired-extension notice in index.ts pairs its console line
45
+ // with a toast the same way. It does not
46
46
  // close on its own, because the condition does not go away on its own.
47
47
  Notification.warning(message, { autoClose: false });
48
48
  }
package/lib/index.js CHANGED
@@ -242,7 +242,7 @@ const plugin = {
242
242
  /** Bring the docked panels in line with settings and CLI availability.
243
243
  * Both must hold for a panel to be present. */
244
244
  const reconcile = () => {
245
- var _a, _b, _c, _d, _e;
245
+ var _a, _b, _c;
246
246
  for (const descriptor of registry.descriptors) {
247
247
  const id = descriptor.id;
248
248
  if (suppressed.has(id)) {
@@ -258,38 +258,26 @@ const plugin = {
258
258
  // the server's word on each binary is final.
259
259
  const roster = status;
260
260
  const available = roster === null ? true : probe !== null && probe.available !== false;
261
- // Enabled but binary absent must not be a silent state: the user sees
262
- // a missing panel with no clue that PATH under the Jupyter server
263
- // differs from their shell. Once per id, not per reconcile.
264
- // Only when the probe SUCCEEDED and this provider is the one missing.
265
- // Blaming PATH for a server the frontend never reached names the wrong
266
- // cause; and every provider defaults to enabled, so a machine with one
267
- // assistant installed would otherwise raise three toasts about assistants
268
- // the user has never heard of. The console line stays for that case.
269
- const chosen = (settings === null || settings === void 0 ? void 0 : settings.get(`providers.${id}.enabled`).user) !== undefined;
261
+ // Enabled but binary absent is logged to the console, never announced:
262
+ // enabled means the extension supports the assistant, not that it is
263
+ // installed, so a toast would name a tool the user never installed
264
+ // (DEF-PANE-277). Only when the probe SUCCEEDED and this provider is
265
+ // the one missing - blaming PATH for a server the frontend never
266
+ // reached names the wrong cause. Once per id, not per reconcile.
270
267
  if (probe !== null &&
271
268
  probe.available === false &&
272
269
  enabled &&
273
- chosen &&
274
270
  !warnedUnavailable.has(id)) {
275
271
  warnedUnavailable.add(id);
276
272
  const binary = (_b = (_a = registry.get(id)) === null || _a === void 0 ? void 0 : _a.descriptor.cliBinary) !== null && _b !== void 0 ? _b : id;
277
- const label = (_d = (_c = registry.get(id)) === null || _c === void 0 ? void 0 : _c.descriptor.label) !== null && _d !== void 0 ? _d : id;
278
273
  console.info(`${LOG_PREFIX} "${id}" is enabled but its \`${binary}\` binary was not found on the server's PATH; panel not shown.`);
279
- // And say it where the user is looking. A console line satisfies the
280
- // comment above only for someone who already suspects the cause; the
281
- // symptom is a panel that never appears, which reads as a broken
282
- // install rather than a PATH difference between the Jupyter server
283
- // and the user's shell. The retired-extension case next door already
284
- // gets a notification, so the COMMON failure was the quiet one.
285
- Notification.warning(`${label} is enabled but \`${binary}\` was not found on the Jupyter server's PATH, so its panel is not shown.`, { autoClose: 8000 });
286
274
  }
287
275
  // The latch is per ABSENCE, not per page load. Once the server reports
288
- // the binary present again, a later disappearance gets its own warning:
276
+ // the binary present again, a later disappearance gets its own line:
289
277
  // without this, one transient absence - a CLI upgrade unlinking the
290
278
  // binary for a moment, or an obsolete roster from an overlapping probe -
291
279
  // permanently suppresses the real one (DEF-125). It cannot make the
292
- // warning repeat while the state is unchanged, which is what the
280
+ // line repeat while the state is unchanged, which is what the
293
281
  // once-per-id latch above exists to prevent: the two conditions are
294
282
  // mutually exclusive.
295
283
  if (probe !== null && probe.available !== false) {
@@ -302,7 +290,7 @@ const plugin = {
302
290
  // A panel docked before the first roster was built without the
303
291
  // server root, and cannot resolve paths until it has one.
304
292
  if (roster !== null) {
305
- panel.setRoot((_e = roster.root_dir) !== null && _e !== void 0 ? _e : '', roster.delete_to_trash !== false);
293
+ panel.setRoot((_c = roster.root_dir) !== null && _c !== void 0 ? _c : '', roster.delete_to_trash !== false);
306
294
  }
307
295
  }
308
296
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab_ai_code_assistants_extension",
3
- "version": "1.2.38",
3
+ "version": "1.2.41",
4
4
  "description": "A full launcher and manager for every AI code assistant in JupyterLab - Claude Code, Codex, Kimi, Gemini and DeepSeek. Start, resume, fork, switch, and clean up CLI sessions from a side panel per assistant - one click lands you in the right terminal with the assistant already running, no duplicate tabs, no UUID hunting.",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -500,6 +500,40 @@ describe('ACC-PROV - the activation contract', () => {
500
500
  live.docked.forEach(widget => widget.dispose());
501
501
  });
502
502
 
503
+ it('DEF-PANE-277 - an enabled assistant whose binary is absent gets no panel and no toast', async () => {
504
+ const toasted = jest
505
+ .spyOn(Notification, 'warning')
506
+ .mockImplementation(() => '');
507
+ const logged = jest
508
+ .spyOn(console, 'info')
509
+ .mockImplementation(() => undefined);
510
+ const absent = PROVIDERS[1].descriptor.id;
511
+
512
+ // Saved explicitly, as the settings editor writes it. Enabled means the
513
+ // extension supports the assistant, not that it is installed.
514
+ const live = await activate({
515
+ composite: { [`providers.${absent}.enabled`]: true },
516
+ answers: [() => Promise.resolve(roster(absent))]
517
+ });
518
+
519
+ expect(
520
+ live.docked
521
+ .filter(widget => !widget.isDisposed)
522
+ .map(widget => widget.descriptor.id)
523
+ ).not.toContain(absent);
524
+ expect(toasted).not.toHaveBeenCalled();
525
+ // The reason stays in the browser console, once.
526
+ expect(
527
+ logged.mock.calls
528
+ .map(args => String(args[0]))
529
+ .filter(line => line.includes(`"${absent}"`))
530
+ ).toHaveLength(1);
531
+
532
+ toasted.mockRestore();
533
+ logged.mockRestore();
534
+ live.docked.forEach(widget => widget.dispose());
535
+ });
536
+
503
537
  it('ACC-PROV-16 - a binary installed after start gains its panel on the next probe', async () => {
504
538
  const late = PROVIDERS[2].descriptor.id;
505
539
  let present = false;
@@ -65,8 +65,8 @@ function warnLegacyCompanion(): void {
65
65
  // And said where the user is looking. The symptom is tabs that are simply
66
66
  // plain, which reads as a feature that was never on rather than as something
67
67
  // to fix, and a console line only reaches someone already suspecting the
68
- // cause. The two neighbouring "an installed thing is wrong" announcements in
69
- // this extension both pair the console line with this toast. It does not
68
+ // cause. The retired-extension notice in index.ts pairs its console line
69
+ // with a toast the same way. It does not
70
70
  // close on its own, because the condition does not go away on its own.
71
71
  Notification.warning(message, { autoClose: false });
72
72
  }
package/src/index.ts CHANGED
@@ -333,46 +333,30 @@ const plugin: JupyterFrontEndPlugin<void> = {
333
333
  const roster = status;
334
334
  const available =
335
335
  roster === null ? true : probe !== null && probe.available !== false;
336
- // Enabled but binary absent must not be a silent state: the user sees
337
- // a missing panel with no clue that PATH under the Jupyter server
338
- // differs from their shell. Once per id, not per reconcile.
339
- // Only when the probe SUCCEEDED and this provider is the one missing.
340
- // Blaming PATH for a server the frontend never reached names the wrong
341
- // cause; and every provider defaults to enabled, so a machine with one
342
- // assistant installed would otherwise raise three toasts about assistants
343
- // the user has never heard of. The console line stays for that case.
344
- const chosen =
345
- settings?.get(`providers.${id}.enabled`).user !== undefined;
336
+ // Enabled but binary absent is logged to the console, never announced:
337
+ // enabled means the extension supports the assistant, not that it is
338
+ // installed, so a toast would name a tool the user never installed
339
+ // (DEF-PANE-277). Only when the probe SUCCEEDED and this provider is
340
+ // the one missing - blaming PATH for a server the frontend never
341
+ // reached names the wrong cause. Once per id, not per reconcile.
346
342
  if (
347
343
  probe !== null &&
348
344
  probe.available === false &&
349
345
  enabled &&
350
- chosen &&
351
346
  !warnedUnavailable.has(id)
352
347
  ) {
353
348
  warnedUnavailable.add(id);
354
349
  const binary = registry.get(id)?.descriptor.cliBinary ?? id;
355
- const label = registry.get(id)?.descriptor.label ?? id;
356
350
  console.info(
357
351
  `${LOG_PREFIX} "${id}" is enabled but its \`${binary}\` binary was not found on the server's PATH; panel not shown.`
358
352
  );
359
- // And say it where the user is looking. A console line satisfies the
360
- // comment above only for someone who already suspects the cause; the
361
- // symptom is a panel that never appears, which reads as a broken
362
- // install rather than a PATH difference between the Jupyter server
363
- // and the user's shell. The retired-extension case next door already
364
- // gets a notification, so the COMMON failure was the quiet one.
365
- Notification.warning(
366
- `${label} is enabled but \`${binary}\` was not found on the Jupyter server's PATH, so its panel is not shown.`,
367
- { autoClose: 8000 }
368
- );
369
353
  }
370
354
  // The latch is per ABSENCE, not per page load. Once the server reports
371
- // the binary present again, a later disappearance gets its own warning:
355
+ // the binary present again, a later disappearance gets its own line:
372
356
  // without this, one transient absence - a CLI upgrade unlinking the
373
357
  // binary for a moment, or an obsolete roster from an overlapping probe -
374
358
  // permanently suppresses the real one (DEF-125). It cannot make the
375
- // warning repeat while the state is unchanged, which is what the
359
+ // line repeat while the state is unchanged, which is what the
376
360
  // once-per-id latch above exists to prevent: the two conditions are
377
361
  // mutually exclusive.
378
362
  if (probe !== null && probe.available !== false) {