jupyterlab_ai_code_assistants_extension 1.2.1 → 1.2.5

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/README.md CHANGED
@@ -35,8 +35,8 @@ Chat-panel extensions re-implement the agent loop and trail the real tool. This
35
35
  - **Conversation switcher** - a right-click "Switch and Manage Sessions" submenu lists a project's other conversations by name and short id with last-activity time; "Manage Sessions..." opens a searchable popup over the full list with multi-select delete and per-row open and copy-id buttons
36
36
  - **Branch session** - fork the current conversation into a new named session via the right-click menu; each assistant forks its own way (Claude's native `--fork-session`, Codex's `codex fork`, server-side copies for Kimi and Gemini) behind the same menu item
37
37
  - **Launch modes under each assistant's own name** - Claude's skip-permissions, Codex's approval bypass, Kimi's `--yolo`, Gemini's YOLO; unsafe variants carry a shield glyph in the launch menus
38
- - **Coloured terminal tabs** - each session's colour tints its terminal tab via the companion `jupyterlab_colourful_tab_extension` (installed automatically). Claude's own `/color` supplies its default, Kimi derives a stable colour from the conversation id; Codex and Gemini have no colour of their own until you set one, and a branched session inherits its parent's colour, which it keeps even if the parent's colour later changes or is reset
39
- - **Your own tab colour wins** - set a colour on a terminal tab and the extension remembers it for that conversation, overriding whatever the assistant chose. `Reset Tab Colour (n)` in the session's right-click menu hands back every colour you set by hand on that project's conversations, and appears once there is one to hand back. Use it rather than the tab menu's own Clear, which will not release a stored colour. A Codex or Kimi conversation that has never been resumed cannot be tracked yet, so a colour set on its tab is not kept
38
+ - **Coloured terminal tabs** - each session's colour tints its terminal tab via the companion `jupyterlab_colourful_tab_extension` (installed automatically). Claude's own `/color` supplies its default, Kimi derives a stable colour from the conversation id; Codex and Gemini have no colour of their own until you set one, and a branched session inherits its parent's colour, which it keeps even if the parent's colour later changes or is reset. Tinting needs the companion release that reports the colours you pick and lets another extension own a tab - against an older one this extension tints no tabs at all and says so once, and the companion's own right-click colours keep working as they always did
39
+ - **Your own tab colour wins** - set a colour on a terminal tab and the extension remembers it for that conversation, overriding whatever the assistant chose. `Reset Tab Colour (n)` in the session's right-click menu hands back every colour you set by hand on that project's conversations, and appears once there is one to hand back. Clearing the colour on the tab itself releases it too, for the conversation that terminal is running; `Reset Tab Colour` is the way back for a conversation with no tab open. A Codex or Kimi conversation that has never been resumed cannot be tracked yet, so a colour set on its tab is not kept
40
40
  - **Favorites** - star projects you keep coming back to via the right-click menu; favourites from the standalone extensions are migrated on first run
41
41
  - **Remove and clean up** - drop a project's history or a project's extra parallel sessions from the right-click menu, confirmation dialog first; removed files honour JupyterLab's "move files to trash" setting
42
42
  - **Activity at a glance** - each row shows its last activity in an aligned column; rows active within the last minute light up, rows idle for over a week dim, and rows with parallel conversations show a branch icon with the count
@@ -2,8 +2,10 @@ import { ServerConnection } from '@jupyterlab/services';
2
2
  import { ColourSource, ISession } from './types';
3
3
  /** The colour vocabulary of `jupyterlab_colourful_tab_extension`, in its own
4
4
  * order - that extension owns the tab CSS and these six ids; this module only
5
- * feeds it one of them. The order is load-bearing: its localStorage records a
6
- * user's menu choice as an INDEX into this list. */
5
+ * feeds it one of them. The order is still load-bearing, for `fnv1aColour`:
6
+ * both halves of this extension index into this list to derive a tint, so a
7
+ * reorder repaints every derived tab and makes the browser and the server
8
+ * disagree about the same conversation until the Python list moves with it. */
7
9
  export declare const TAB_COLOUR_IDS: readonly string[];
8
10
  /** Map a conversation id onto one of the six colour ids. FNV-1a (32-bit) - a
9
11
  * stable string hash with good avalanche on short hex-ish ids; `Math.imul`
@@ -19,14 +21,6 @@ export declare const TAB_COLOUR_IDS: readonly string[];
19
21
  * the next time somebody rewrites either loop. Every real conversation id is
20
22
  * ASCII, where all three readings coincide, so no existing tab changes colour. */
21
23
  export declare function fnv1aColour(sessionId: string): string;
22
- /** Colour the user set by hand on a terminal tab, or null.
23
- *
24
- * The colourful-tab extension stores its right-click menu choices as
25
- * `{ "terminal:<session name>": <index into TAB_COLOUR_IDS> }`. Reading it is
26
- * what makes "set the colour on the tab" register as the conversation's
27
- * colour - for every assistant, including one whose CLI has a colour concept
28
- * of its own. */
29
- export declare function readUserSetTabColour(terminalName: string): string | null;
30
24
  /** Resolve the ladder for one conversation. `userSet` is this extension's own
31
25
  * store, `native` the conversation's own colour (only ever populated by a
32
26
  * `native` provider). */
@@ -115,8 +109,8 @@ export declare class ColourStore {
115
109
  * `handSet` false marks it inherited - written on a fork's behalf rather
116
110
  * than chosen on its tab.
117
111
  *
118
- * Answers whether the store now HOLDS the colour, which is what
119
- * `reconcileColours` gates its paint on.
112
+ * Answers whether the store now HOLDS the colour, which is what the capture
113
+ * of a colour picked on a tab gates its repaint on - see `_captureColour`.
120
114
  *
121
115
  * The cache is written only once the server has confirmed. Writing it
122
116
  * eagerly and undoing that on failure looks cheaper and is not: nothing here
@@ -12,8 +12,10 @@
12
12
  import { requestProvider } from './request';
13
13
  /** The colour vocabulary of `jupyterlab_colourful_tab_extension`, in its own
14
14
  * order - that extension owns the tab CSS and these six ids; this module only
15
- * feeds it one of them. The order is load-bearing: its localStorage records a
16
- * user's menu choice as an INDEX into this list. */
15
+ * feeds it one of them. The order is still load-bearing, for `fnv1aColour`:
16
+ * both halves of this extension index into this list to derive a tint, so a
17
+ * reorder repaints every derived tab and makes the browser and the server
18
+ * disagree about the same conversation until the Python list moves with it. */
17
19
  export const TAB_COLOUR_IDS = [
18
20
  'rose',
19
21
  'peach',
@@ -22,10 +24,6 @@ export const TAB_COLOUR_IDS = [
22
24
  'sky',
23
25
  'lavender'
24
26
  ];
25
- /** Key `jupyterlab_colourful_tab_extension` persists menu-set tab colours
26
- * under, and the `terminal:<session name>` shape of its entries. Read only -
27
- * this module never writes into another extension's storage. */
28
- const TAB_COLOUR_STORAGE_KEY = 'jupyterlab-colourful-tab-colours';
29
27
  /** Map a conversation id onto one of the six colour ids. FNV-1a (32-bit) - a
30
28
  * stable string hash with good avalanche on short hex-ish ids; `Math.imul`
31
29
  * keeps the multiplication in 32-bit integer semantics. The same id always
@@ -47,33 +45,6 @@ export function fnv1aColour(sessionId) {
47
45
  }
48
46
  return TAB_COLOUR_IDS[(hash >>> 0) % TAB_COLOUR_IDS.length];
49
47
  }
50
- /** Colour the user set by hand on a terminal tab, or null.
51
- *
52
- * The colourful-tab extension stores its right-click menu choices as
53
- * `{ "terminal:<session name>": <index into TAB_COLOUR_IDS> }`. Reading it is
54
- * what makes "set the colour on the tab" register as the conversation's
55
- * colour - for every assistant, including one whose CLI has a colour concept
56
- * of its own. */
57
- export function readUserSetTabColour(terminalName) {
58
- var _a;
59
- try {
60
- const raw = window.localStorage.getItem(TAB_COLOUR_STORAGE_KEY);
61
- if (!raw) {
62
- return null;
63
- }
64
- const parsed = JSON.parse(raw);
65
- const index = parsed[`terminal:${terminalName}`];
66
- if (typeof index !== 'number') {
67
- return null;
68
- }
69
- return (_a = TAB_COLOUR_IDS[index]) !== null && _a !== void 0 ? _a : null;
70
- }
71
- catch (_err) {
72
- // localStorage unavailable, or another extension's schema changed under
73
- // us - no user colour is a valid answer, a thrown error is not.
74
- return null;
75
- }
76
- }
77
48
  /** Resolve the ladder for one conversation. `userSet` is this extension's own
78
49
  * store, `native` the conversation's own colour (only ever populated by a
79
50
  * `native` provider). */
@@ -250,8 +221,8 @@ export class ColourStore {
250
221
  * `handSet` false marks it inherited - written on a fork's behalf rather
251
222
  * than chosen on its tab.
252
223
  *
253
- * Answers whether the store now HOLDS the colour, which is what
254
- * `reconcileColours` gates its paint on.
224
+ * Answers whether the store now HOLDS the colour, which is what the capture
225
+ * of a colour picked on a tab gates its repaint on - see `_captureColour`.
255
226
  *
256
227
  * The cache is written only once the server has confirmed. Writing it
257
228
  * eagerly and undoing that on failure looks cheaper and is not: nothing here
@@ -147,11 +147,14 @@ export declare class AssistantSessionsPanel extends Widget {
147
147
  * until the next reconcile tick. One request for the whole set, so a release
148
148
  * is never left half applied.
149
149
  *
150
- * The colourful-tab extension's own menu record is normally consumed when
151
- * the colour is captured, so this is the release. A tab closed in the window
152
- * between the capture write and the paint keeps that record, and reopening
153
- * the terminal re-captures the colour - rare, and visible rather than
154
- * silent, since the tab still wears it. */
150
+ * The colourful-tab extension persists nothing for a tab this extension has
151
+ * claimed, so it holds no record of the released colour to drop alongside
152
+ * the store entry: dropping that entry and repainting is the whole of it.
153
+ * Clearing the colour on the tab itself reaches the same place by the other
154
+ * route - the
155
+ * companion reports it as a choice with no colour and the handler forgets
156
+ * the conversation - but only for a conversation whose terminal is open and
157
+ * probes as this assistant's, which is why this path exists as well. */
155
158
  private _resetColours;
156
159
  /** Copy the parent's tint onto a fresh branch.
157
160
  *
package/lib/core/panel.js CHANGED
@@ -1060,11 +1060,14 @@ export class AssistantSessionsPanel extends Widget {
1060
1060
  * until the next reconcile tick. One request for the whole set, so a release
1061
1061
  * is never left half applied.
1062
1062
  *
1063
- * The colourful-tab extension's own menu record is normally consumed when
1064
- * the colour is captured, so this is the release. A tab closed in the window
1065
- * between the capture write and the paint keeps that record, and reopening
1066
- * the terminal re-captures the colour - rare, and visible rather than
1067
- * silent, since the tab still wears it. */
1063
+ * The colourful-tab extension persists nothing for a tab this extension has
1064
+ * claimed, so it holds no record of the released colour to drop alongside
1065
+ * the store entry: dropping that entry and repainting is the whole of it.
1066
+ * Clearing the colour on the tab itself reaches the same place by the other
1067
+ * route - the
1068
+ * companion reports it as a choice with no colour and the handler forgets
1069
+ * the conversation - but only for a conversation whose terminal is open and
1070
+ * probes as this assistant's, which is why this path exists as well. */
1068
1071
  async _resetColours(sessionIds) {
1069
1072
  if (!(await this._colours.forget(sessionIds))) {
1070
1073
  // No refresh afterwards: `_fetch` clears the inline error, and a failure
@@ -1961,11 +1964,13 @@ export class AssistantSessionsPanel extends Widget {
1961
1964
  }
1962
1965
  }
1963
1966
  });
1964
- // The only way back out of a hand-set tab colour. The colourful-tab
1965
- // extension's own Clear touches its storage and the tab's classes, neither
1966
- // of which this extension reads once the colour is in its store, so
1967
- // without this item the override would be permanent - and on a native
1968
- // assistant that means `/color` silently never showing again.
1967
+ // The way back out of a hand-set tab colour for a conversation whose
1968
+ // terminal is not open. Clear on the tab itself is reported by the
1969
+ // colourful-tab extension as a choice with no colour and does drop the
1970
+ // override, but only for the conversation that terminal is running - so
1971
+ // without this item an override on a branch with no tab open would be
1972
+ // permanent, and on a native assistant that means `/color` silently never
1973
+ // showing again.
1969
1974
  //
1970
1975
  // It covers every conversation of the row, not just the current one: a
1971
1976
  // terminal opened on a branch files its colour under THAT branch, and this
@@ -87,10 +87,16 @@ export declare class TerminalManager {
87
87
  *
88
88
  * Each terminal's conversation is re-resolved on every pass rather than
89
89
  * taken from the launch cache, which pins the launch-time conversation and
90
- * goes stale on an in-place switch. On the way through, a colour the user
91
- * set by hand on the tab is written back into this provider's colour store,
92
- * which is what makes "set the tab colour" mean "this conversation's colour"
93
- * even for an assistant whose CLI has no colour concept.
90
+ * goes stale on an in-place switch.
91
+ *
92
+ * It touches this extension's own store and nothing else - no browser
93
+ * storage, no other extension's records, and that must stay true. The colour
94
+ * the user picks on a tab arrives as a signal instead (`_onColourChanged`);
95
+ * reading it back out of the companion's storage is what made a recycled
96
+ * terminal name hand a dead terminal's colour to a live conversation,
97
+ * permanently, at the top of the ladder. The only write this pass makes is a
98
+ * choice that signal already delivered, whose conversation was unreadable at
99
+ * the time - see `_pendingChoices`.
94
100
  */
95
101
  reconcileColours(): Promise<void>;
96
102
  /** Effective colour of one conversation as the cache currently reads it, for
@@ -109,6 +115,86 @@ export declare class TerminalManager {
109
115
  * another extension, both are called as `void`, and turning tab colouring
110
116
  * off must not spill an unhandled rejection. */
111
117
  clearColours(): Promise<void>;
118
+ /** The user picked a colour on a tab, or cleared one.
119
+ *
120
+ * The slot itself stays synchronous - Lumino ignores what a slot returns, so
121
+ * an async slot's rejection would surface as an unhandled one.
122
+ */
123
+ private _onColourChanged;
124
+ /** File one choice against the conversation the coloured terminal is
125
+ * actually running.
126
+ *
127
+ * Only a terminal this provider owns, and only one whose conversation the
128
+ * server READ from the running process. A widget whose conversation cannot
129
+ * be read is never filed against one guessed from its folder: a project has
130
+ * many conversations, and filing the user's colour under the wrong one
131
+ * recolours a conversation they never touched. That choice waits instead,
132
+ * but only where the probe named the terminal - see `_pendingChoices`.
133
+ */
134
+ private _captureColour;
135
+ /** Write one choice into this extension's own store - a colour for a pick, a
136
+ * forget for a clear - and answer whether the store now holds it.
137
+ *
138
+ * Nothing is painted for a refused write, at either call site. The tab is
139
+ * claimed, so the companion holds no record of this choice and there is
140
+ * nothing in the store either - every colour this extension could paint is
141
+ * the conversation's PREVIOUS one, which is what the pick was meant to
142
+ * replace. What the user sees instead is the class the companion's menu put
143
+ * on the tab element, and that survives only until the next tab switch:
144
+ * Lumino rebuilds a tab's class attribute from the widget title whenever the
145
+ * current tab changes, and the title still carries that previous colour. So
146
+ * the choice is lost either way - the warning is what makes it something
147
+ * other than silent. */
148
+ private _fileChoice;
149
+ /** Tell the companion this tab's colour is ours to persist.
150
+ *
151
+ * Once per terminal, not once per pass - the pass runs every 30 seconds and
152
+ * each claim is a disposable the companion has to hold. A widget id is a
153
+ * safe key for that: JupyterLab mints one uuid per widget (`id-<uuid4>`,
154
+ * verified against 4.6.3 on 2026-09-04) and never hands it to a second
155
+ * widget, so a claim held under an id belongs to the widget still wearing
156
+ * it. Terminal NAMES are what terminado recycles - which is why the
157
+ * companion fingerprints what it persists, and why nothing here keys on a
158
+ * name.
159
+ */
160
+ private _claimTab;
161
+ /** Give up every claim this pass did not renew.
162
+ *
163
+ * A claim says "this extension draws this tab and records what you pick on
164
+ * it", and that stops being true the moment the terminal stops probing as
165
+ * this assistant's - it exited, another assistant took the pty, or the
166
+ * widget is gone from the tracker altogether. Holding on would leave a
167
+ * colour picked on that tab persisted by nobody, and the map growing by one
168
+ * entry per closed terminal for the life of the page.
169
+ *
170
+ * A terminal whose probe merely failed is released too, since an unreadable
171
+ * terminal is not evidence of ownership. The next pass that reads it claims
172
+ * it again, and re-claiming costs nothing visible: a claim suppresses the
173
+ * companion's persistence and narrows its repaint to a fingerprint-verified
174
+ * entry, deleting nothing it stored before, and the next paint puts this
175
+ * conversation's colour back. */
176
+ private _releaseLapsedClaims;
177
+ private _releaseClaims;
178
+ /** Forget the choices whose premise no longer holds.
179
+ *
180
+ * A choice is held on one answer only - this assistant's terminal, its
181
+ * conversation not readable yet - and it stops being a choice about anything
182
+ * the moment either half of that stops being true.
183
+ *
184
+ * The terminal CLOSED. A pending choice is about one tab and must not outlive
185
+ * it. A widget id is never handed to a second widget, so the record could
186
+ * never be filed again - what it would do is hold that widget's id, one entry
187
+ * per closed terminal, for the life of the page.
188
+ *
189
+ * The terminal is no longer THIS ASSISTANT'S, per an answer the server
190
+ * actually gave. The conversation the pick was made under has ended, and the
191
+ * assistant coming back in the same terminal starts a NEW one - so a choice
192
+ * kept across that gap is filed against a conversation the user never picked
193
+ * a colour for, as hand-set, at the top of the ladder. A probe that failed is
194
+ * deliberately not evidence here: it says only that the server did not
195
+ * answer, and discarding the pick on that basis loses a colour the user chose
196
+ * seconds ago. */
197
+ private _dropStalePending;
112
198
  private _applyColour;
113
199
  private _eachAssistantTerminal;
114
200
  private _liveTerminals;
@@ -118,13 +204,19 @@ export declare class TerminalManager {
118
204
  private readonly _descriptor;
119
205
  private readonly _colours;
120
206
  private readonly _tracker;
121
- private readonly _colourfulTabs;
207
+ /** The injected colour token narrowed to its ownership API, or null when the
208
+ * companion is absent or too old to carry it. The only handle on the
209
+ * companion this class has: with no ownership there is no painting either. */
210
+ private readonly _tabs;
122
211
  private readonly _serverSettings;
212
+ private readonly _claims;
213
+ private readonly _pendingChoices;
123
214
  private readonly _byProject;
124
215
  private readonly _pending;
125
216
  private _sessions;
126
217
  private _colouredTabs;
127
218
  private _colourHandle;
219
+ private _pass;
128
220
  private _disposed;
129
221
  }
130
222
  export declare namespace TerminalManager {