jupyterlab_ai_code_assistants_extension 1.1.13 → 1.2.4
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 +2 -2
- package/lib/core/colour.d.ts +6 -12
- package/lib/core/colour.js +6 -35
- package/lib/core/panel.d.ts +8 -5
- package/lib/core/panel.js +15 -10
- package/lib/core/terminals.d.ts +97 -5
- package/lib/core/terminals.js +391 -62
- package/lib/core/types.d.ts +31 -0
- package/package.json +1 -1
- package/src/__tests__/colour.spec.ts +12 -33
- package/src/__tests__/tab-colour-capture.spec.ts +961 -0
- package/src/core/colour.ts +6 -35
- package/src/core/panel.ts +15 -10
- package/src/core/terminals.ts +448 -89
- package/src/core/types.ts +37 -0
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.
|
|
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
|
package/lib/core/colour.d.ts
CHANGED
|
@@ -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
|
|
6
|
-
*
|
|
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
|
-
*
|
|
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
|
package/lib/core/colour.js
CHANGED
|
@@ -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
|
|
16
|
-
*
|
|
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
|
-
*
|
|
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
|
package/lib/core/panel.d.ts
CHANGED
|
@@ -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
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* the
|
|
154
|
-
*
|
|
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
|
|
1064
|
-
*
|
|
1065
|
-
*
|
|
1066
|
-
* the
|
|
1067
|
-
*
|
|
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
|
|
1965
|
-
//
|
|
1966
|
-
//
|
|
1967
|
-
//
|
|
1968
|
-
//
|
|
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
|
package/lib/core/terminals.d.ts
CHANGED
|
@@ -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.
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
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
|
-
|
|
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 {
|