jupyterlab_ai_code_assistants_extension 1.2.31 → 1.2.38
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 +1 -1
- package/lib/core/panel.d.ts +1 -21
- package/lib/core/panel.js +24 -65
- package/lib/core/types.d.ts +2 -1
- package/lib/providers/claude.js +3 -3
- package/package.json +1 -1
- package/src/__tests__/panel.spec.ts +7 -5
- package/src/core/panel.ts +26 -70
- package/src/core/types.ts +2 -1
- package/src/providers/claude.ts +3 -3
- package/style/base.css +26 -17
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ Chat-panel extensions re-implement the agent loop and trail the real tool. This
|
|
|
41
41
|
- **Favorites** - star projects you keep coming back to via the right-click menu; favourites from the standalone extensions are migrated on first run
|
|
42
42
|
- **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
|
|
43
43
|
- **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
|
|
44
|
-
- **Remote control indicator and background agents** (Claude) - a green dot marks sessions actively under remote control, and a conversation held by a running background agent shows a `bg` chip; clicking
|
|
44
|
+
- **Remote control indicator and background agents** (Claude) - a green dot marks sessions actively under remote control, and a conversation held by a running background agent shows a `bg` chip; clicking stops the agent and resumes the same conversation in your terminal, so it becomes an ordinary interactive session
|
|
45
45
|
- **Search** - fuzzy filter per panel, toggled by the funnel button
|
|
46
46
|
- **Presentation modes** - label rows by session name or by path relative to the JupyterLab root
|
|
47
47
|
- **Conflict-safe upgrade** - if a retired standalone extension is still installed, its panel wins and this extension stands down for that assistant instead of showing a duplicate
|
package/lib/core/panel.d.ts
CHANGED
|
@@ -252,24 +252,6 @@ export declare class AssistantSessionsPanel extends Widget {
|
|
|
252
252
|
* that build a byte-identical launch, and the unmarked one is the default
|
|
253
253
|
* the user clicks. */
|
|
254
254
|
private _resolvedVariant;
|
|
255
|
-
/** The same mode as it applies to RESUME - of the active row by default, or
|
|
256
|
-
* of whichever conversation is named.
|
|
257
|
-
*
|
|
258
|
-
* An attach is not a launch: the server returns the argv before the flag is
|
|
259
|
-
* appended, deliberately, so promising the mode on a conversation a live
|
|
260
|
-
* worker already holds would name something the assistant never receives.
|
|
261
|
-
* That is true of resuming and of nothing else, so it is tested HERE rather
|
|
262
|
-
* than in `_resolvedVariant` - which is read by the `+` button and both
|
|
263
|
-
* new-session and branch items, none of which attach. Testing it there let
|
|
264
|
-
* one right-click on a background-agent row strip the mode's name off every
|
|
265
|
-
* one of those surfaces panel-wide, while their launches went on carrying it
|
|
266
|
-
* (DEF-37).
|
|
267
|
-
*
|
|
268
|
-
* The argument is what lets a surface that describes SOMEONE ELSE'S resume -
|
|
269
|
-
* a row tooltip built during a render, a branch item in the submenu - ask the
|
|
270
|
-
* question about that conversation rather than about the active row.
|
|
271
|
-
*/
|
|
272
|
-
private _resumeVariant;
|
|
273
255
|
/** The variant suffix a plain item wears while that mode is in force. */
|
|
274
256
|
private _variantSuffix;
|
|
275
257
|
/** Whether a forced variant would build the launch the plain item already
|
|
@@ -311,9 +293,7 @@ export declare class AssistantSessionsPanel extends Widget {
|
|
|
311
293
|
private _openManagePopup;
|
|
312
294
|
/** Tooltip for one Open button in the Manage Sessions popup. Same rule as the
|
|
313
295
|
* Open Branched Conversation submenu: the button launches a terminal, so it
|
|
314
|
-
* names the mode that launch carries
|
|
315
|
-
* is attached to and carries none. The row's own conversation is not in
|
|
316
|
-
* `_lastBranches`, so it answers from the session. */
|
|
296
|
+
* names the mode that launch carries. */
|
|
317
297
|
private _openBranchTitle;
|
|
318
298
|
/** The live-worker chip for a branch row, when the provider has workers at
|
|
319
299
|
* all. Returning null lets the popup fall back to its fork marker. */
|
package/lib/core/panel.js
CHANGED
|
@@ -1593,9 +1593,9 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1593
1593
|
badge.appendChild(document.createTextNode(String(session.extra_sessions + 1)));
|
|
1594
1594
|
name.appendChild(badge);
|
|
1595
1595
|
}
|
|
1596
|
-
// A live worker owns this conversation, so a click
|
|
1597
|
-
// resuming
|
|
1598
|
-
//
|
|
1596
|
+
// A live worker owns this conversation, so a click stops it before
|
|
1597
|
+
// resuming. Marked so the difference is visible before the click, not a
|
|
1598
|
+
// surprise after it. No own title: a child title shadows the
|
|
1599
1599
|
// row's, which would hide the path behind a two-character chip.
|
|
1600
1600
|
if (this._descriptor.hasBgAgents && session.bg_id) {
|
|
1601
1601
|
const bg = document.createElement('span');
|
|
@@ -1620,10 +1620,8 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1620
1620
|
row.appendChild(this._renderIndicator(session));
|
|
1621
1621
|
}
|
|
1622
1622
|
// No star in the Favorites section - every row there is one by definition.
|
|
1623
|
-
// The star sits before the time
|
|
1624
|
-
//
|
|
1625
|
-
// rather than however much shorter than "11mo ago" this label happens to
|
|
1626
|
-
// be.
|
|
1623
|
+
// The star sits before the time so the minimum-width time column stays the
|
|
1624
|
+
// rightmost alignment anchor across all rows.
|
|
1627
1625
|
if (session.favourite && sectionKey !== 'favourites') {
|
|
1628
1626
|
// A marker, not a control. It carried a `title` that made it read as
|
|
1629
1627
|
// one while a click fell through to the row and launched a terminal;
|
|
@@ -1636,8 +1634,8 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1636
1634
|
starFilledIcon.element({ container: star });
|
|
1637
1635
|
row.appendChild(star);
|
|
1638
1636
|
}
|
|
1639
|
-
// Always present
|
|
1640
|
-
//
|
|
1637
|
+
// Always present (empty without an mtime) so the star column keeps the
|
|
1638
|
+
// same anchor across every row in the panel.
|
|
1641
1639
|
const time = document.createElement('span');
|
|
1642
1640
|
time.className = 'jp-AiAssistantsPanel-rowTime';
|
|
1643
1641
|
time.textContent = session.file_mtime
|
|
@@ -1769,15 +1767,10 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1769
1767
|
lines.push('Status: active');
|
|
1770
1768
|
}
|
|
1771
1769
|
if (this._descriptor.hasBgAgents && s.bg_id) {
|
|
1772
|
-
lines.push(`Background agent: ${s.bg_id} (click
|
|
1770
|
+
lines.push(`Background agent: ${s.bg_id} (click stops it and resumes here)`);
|
|
1773
1771
|
}
|
|
1774
1772
|
lines.push(...((_c = (_b = (_a = this._hooks).tooltipLines) === null || _b === void 0 ? void 0 : _b.call(_a, s)) !== null && _c !== void 0 ? _c : []));
|
|
1775
|
-
|
|
1776
|
-
// the line above may have just said the click joins a live worker, and an
|
|
1777
|
-
// attach is issued before the mode is appended, so `_resolvedVariant`
|
|
1778
|
-
// promised a flag the server discards - one tooltip naming both, a line
|
|
1779
|
-
// apart (DEF-46).
|
|
1780
|
-
const variant = this._resumeVariant(s);
|
|
1773
|
+
const variant = this._resolvedVariant();
|
|
1781
1774
|
if (variant) {
|
|
1782
1775
|
// The row click launches with this, and the menu is the only other place
|
|
1783
1776
|
// that says so.
|
|
@@ -1893,27 +1886,6 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1893
1886
|
const resolved = resolvedLaunchModeEntry(this._descriptor.launchModes, this._modes);
|
|
1894
1887
|
return resolved;
|
|
1895
1888
|
}
|
|
1896
|
-
/** The same mode as it applies to RESUME - of the active row by default, or
|
|
1897
|
-
* of whichever conversation is named.
|
|
1898
|
-
*
|
|
1899
|
-
* An attach is not a launch: the server returns the argv before the flag is
|
|
1900
|
-
* appended, deliberately, so promising the mode on a conversation a live
|
|
1901
|
-
* worker already holds would name something the assistant never receives.
|
|
1902
|
-
* That is true of resuming and of nothing else, so it is tested HERE rather
|
|
1903
|
-
* than in `_resolvedVariant` - which is read by the `+` button and both
|
|
1904
|
-
* new-session and branch items, none of which attach. Testing it there let
|
|
1905
|
-
* one right-click on a background-agent row strip the mode's name off every
|
|
1906
|
-
* one of those surfaces panel-wide, while their launches went on carrying it
|
|
1907
|
-
* (DEF-37).
|
|
1908
|
-
*
|
|
1909
|
-
* The argument is what lets a surface that describes SOMEONE ELSE'S resume -
|
|
1910
|
-
* a row tooltip built during a render, a branch item in the submenu - ask the
|
|
1911
|
-
* question about that conversation rather than about the active row.
|
|
1912
|
-
*/
|
|
1913
|
-
_resumeVariant(target = this._activeSession) {
|
|
1914
|
-
const joining = this._descriptor.hasBgAgents && !!(target === null || target === void 0 ? void 0 : target.bg_id);
|
|
1915
|
-
return joining ? null : this._resolvedVariant();
|
|
1916
|
-
}
|
|
1917
1889
|
/** The variant suffix a plain item wears while that mode is in force. */
|
|
1918
1890
|
_variantSuffix() {
|
|
1919
1891
|
const resolved = this._resolvedVariant();
|
|
@@ -1953,16 +1925,15 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1953
1925
|
}
|
|
1954
1926
|
});
|
|
1955
1927
|
this._commands.addCommand(this._cmd('resume'), {
|
|
1956
|
-
//
|
|
1957
|
-
//
|
|
1958
|
-
// gets.
|
|
1928
|
+
// Resuming a conversation a live worker holds stops that worker first,
|
|
1929
|
+
// so a provider that has workers says which of the two this row gets.
|
|
1959
1930
|
label: () => {
|
|
1960
1931
|
var _a, _b, _c;
|
|
1961
|
-
const variant = this.
|
|
1932
|
+
const variant = this._resolvedVariant();
|
|
1962
1933
|
const verb = (_c = (_b = (_a = this._hooks).resumeLabel) === null || _b === void 0 ? void 0 : _b.call(_a, active())) !== null && _c !== void 0 ? _c : 'Resume';
|
|
1963
1934
|
return variant ? `${verb} (${variant.label})` : verb;
|
|
1964
1935
|
},
|
|
1965
|
-
icon: () => { var _a; return (((_a = this.
|
|
1936
|
+
icon: () => { var _a; return (((_a = this._resolvedVariant()) === null || _a === void 0 ? void 0 : _a.unsafe) ? shieldIcon : undefined); },
|
|
1966
1937
|
execute: () => {
|
|
1967
1938
|
const s = active();
|
|
1968
1939
|
if (s) {
|
|
@@ -1972,15 +1943,13 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1972
1943
|
});
|
|
1973
1944
|
for (const mode of this._variantModes) {
|
|
1974
1945
|
this._commands.addCommand(this._cmd(`resume-${mode.id}`), {
|
|
1975
|
-
|
|
1946
|
+
// The same verb as the plain item, so a row a live worker holds says
|
|
1947
|
+
// here too that the worker is stopped first.
|
|
1948
|
+
label: () => { var _a, _b, _c; return `${(_c = (_b = (_a = this._hooks).resumeLabel) === null || _b === void 0 ? void 0 : _b.call(_a, active())) !== null && _c !== void 0 ? _c : 'Resume'} (${mode.menuLabel})`; },
|
|
1976
1949
|
icon: shieldIcon,
|
|
1977
1950
|
// Absent while the plain item already resolves to this same mode - it
|
|
1978
1951
|
// would be a second entry building an identical launch.
|
|
1979
1952
|
isVisible: () => !this._buildsSameLaunch(mode),
|
|
1980
|
-
// A conversation a live worker already holds was started in whatever
|
|
1981
|
-
// mode that worker has; joining it cannot change that, so the variant
|
|
1982
|
-
// is disabled rather than silently joining in the wrong mode.
|
|
1983
|
-
isEnabled: () => { var _a; return !(this._descriptor.hasBgAgents && ((_a = active()) === null || _a === void 0 ? void 0 : _a.bg_id)); },
|
|
1984
1953
|
execute: () => {
|
|
1985
1954
|
const s = active();
|
|
1986
1955
|
if (s) {
|
|
@@ -2129,13 +2098,11 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2129
2098
|
this._commands.addCommand(this._cmd('open-branch'), {
|
|
2130
2099
|
// Names the mode the launch carries, like Resume, `+` and Branch Session
|
|
2131
2100
|
// do - this item opens a terminal, so the mode in force applies to it and
|
|
2132
|
-
// this was the one launch surface that stayed silent about it.
|
|
2133
|
-
// aware per conversation: the submenu passes `bg`, since a branch a live
|
|
2134
|
-
// worker holds is attached to and takes no mode (DEF-46's rule).
|
|
2101
|
+
// this was the one launch surface that stayed silent about it.
|
|
2135
2102
|
label: args => {
|
|
2136
2103
|
var _a;
|
|
2137
2104
|
const base = String((_a = args.label) !== null && _a !== void 0 ? _a : '');
|
|
2138
|
-
const variant = this.
|
|
2105
|
+
const variant = this._resolvedVariant();
|
|
2139
2106
|
return variant ? `${base} (${variant.label})` : base;
|
|
2140
2107
|
},
|
|
2141
2108
|
// Intent glyph, not row state - see switch-branch above.
|
|
@@ -2438,10 +2405,7 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2438
2405
|
command: this._cmd('open-branch'),
|
|
2439
2406
|
args: {
|
|
2440
2407
|
session_id: b.session_id,
|
|
2441
|
-
label: this._branchMenuLabel(b)
|
|
2442
|
-
// The label reads this to tell an attach from a launch; the
|
|
2443
|
-
// provider's own worker capability is tested where it is answered.
|
|
2444
|
-
...(b.bg_id ? { bg: b.bg_id } : {})
|
|
2408
|
+
label: this._branchMenuLabel(b)
|
|
2445
2409
|
}
|
|
2446
2410
|
});
|
|
2447
2411
|
}
|
|
@@ -2465,7 +2429,7 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2465
2429
|
branchName: b => this._branchDisplayName(b),
|
|
2466
2430
|
formatTime: ms => this._formatRelativeTime(ms),
|
|
2467
2431
|
branchBadge: b => this._bgBadgeFor(b),
|
|
2468
|
-
openTitle:
|
|
2432
|
+
openTitle: () => this._openBranchTitle(session),
|
|
2469
2433
|
onSwitch: id => void this._switchBranch(session, id),
|
|
2470
2434
|
onOpen: id => void this._openSession(session, undefined, id),
|
|
2471
2435
|
onDelete: ids => this._deleteBranches(session, ids),
|
|
@@ -2477,21 +2441,16 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2477
2441
|
}
|
|
2478
2442
|
/** Tooltip for one Open button in the Manage Sessions popup. Same rule as the
|
|
2479
2443
|
* Open Branched Conversation submenu: the button launches a terminal, so it
|
|
2480
|
-
* names the mode that launch carries
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
_openBranchTitle(session, sessionId) {
|
|
2484
|
-
var _a, _b, _c, _d;
|
|
2444
|
+
* names the mode that launch carries. */
|
|
2445
|
+
_openBranchTitle(session) {
|
|
2446
|
+
var _a, _b, _c;
|
|
2485
2447
|
// One terminal serves a project-scoped assistant, so the button focuses
|
|
2486
2448
|
// or starts it, under the provider's own verb for that.
|
|
2487
2449
|
if (this._descriptor.terminalScope === 'project') {
|
|
2488
2450
|
return ((_c = (_b = (_a = this._hooks).resumeLabel) === null || _b === void 0 ? void 0 : _b.call(_a, session)) !== null && _c !== void 0 ? _c : "Open the project's terminal");
|
|
2489
2451
|
}
|
|
2490
2452
|
const base = 'Open this conversation in its own terminal';
|
|
2491
|
-
const
|
|
2492
|
-
? session
|
|
2493
|
-
: ((_d = this._lastBranches.find(b => b.session_id === sessionId)) !== null && _d !== void 0 ? _d : null);
|
|
2494
|
-
const variant = this._resumeVariant(target);
|
|
2453
|
+
const variant = this._resolvedVariant();
|
|
2495
2454
|
return variant ? `${base} (${variant.label})` : base;
|
|
2496
2455
|
}
|
|
2497
2456
|
/** The live-worker chip for a branch row, when the provider has workers at
|
package/lib/core/types.d.ts
CHANGED
|
@@ -309,7 +309,8 @@ export interface ITabColourEvents {
|
|
|
309
309
|
* shared panel. */
|
|
310
310
|
export interface IProviderHooks {
|
|
311
311
|
/** Label for the primary open action, when the provider distinguishes verbs
|
|
312
|
-
* (
|
|
312
|
+
* (stopping a live worker before resuming is not the same as resuming a
|
|
313
|
+
* dormant store entry). */
|
|
313
314
|
resumeLabel?: (session: ISession | null) => string;
|
|
314
315
|
/** Extra lines for the row tooltip. */
|
|
315
316
|
tooltipLines?: (session: ISession) => string[];
|
package/lib/providers/claude.js
CHANGED
|
@@ -70,7 +70,7 @@ export const descriptor = {
|
|
|
70
70
|
legacyPluginId: 'jupyterlab_claude_code_extension'
|
|
71
71
|
};
|
|
72
72
|
export const hooks = {
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
resumeLabel: (session) => (session === null || session === void 0 ? void 0 : session.bg_id) ? '
|
|
73
|
+
// Opening a conversation a live agent holds stops the agent first, so the
|
|
74
|
+
// item says so before the click (DEF-PROV-272).
|
|
75
|
+
resumeLabel: (session) => (session === null || session === void 0 ? void 0 : session.bg_id) ? 'Stop Background Agent and Resume' : 'Resume'
|
|
76
76
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterlab_ai_code_assistants_extension",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.38",
|
|
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",
|
|
@@ -645,13 +645,15 @@ describe('DEF-46 - the row tooltip names only a mode the row launches with', ()
|
|
|
645
645
|
expect(tooltip([session()])).toContain('Launch mode: Skip Permissions');
|
|
646
646
|
});
|
|
647
647
|
|
|
648
|
-
it('
|
|
648
|
+
it('names the mode on a row a live worker holds, which is resumed too', () => {
|
|
649
649
|
panel.setModes({ skip: true });
|
|
650
|
-
//
|
|
651
|
-
//
|
|
650
|
+
// The click stops the worker and resumes the conversation, and a resume
|
|
651
|
+
// carries the mode (DEF-PROV-272).
|
|
652
652
|
const text = tooltip([session({ bg_id: 'abcd1234' })]);
|
|
653
|
-
expect(text).toContain(
|
|
654
|
-
|
|
653
|
+
expect(text).toContain(
|
|
654
|
+
'Background agent: abcd1234 (click stops it and resumes here)'
|
|
655
|
+
);
|
|
656
|
+
expect(text).toContain('Launch mode: Skip Permissions');
|
|
655
657
|
});
|
|
656
658
|
});
|
|
657
659
|
|
package/src/core/panel.ts
CHANGED
|
@@ -1899,9 +1899,9 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1899
1899
|
);
|
|
1900
1900
|
name.appendChild(badge);
|
|
1901
1901
|
}
|
|
1902
|
-
// A live worker owns this conversation, so a click
|
|
1903
|
-
// resuming
|
|
1904
|
-
//
|
|
1902
|
+
// A live worker owns this conversation, so a click stops it before
|
|
1903
|
+
// resuming. Marked so the difference is visible before the click, not a
|
|
1904
|
+
// surprise after it. No own title: a child title shadows the
|
|
1905
1905
|
// row's, which would hide the path behind a two-character chip.
|
|
1906
1906
|
if (this._descriptor.hasBgAgents && session.bg_id) {
|
|
1907
1907
|
const bg = document.createElement('span');
|
|
@@ -1927,10 +1927,8 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1927
1927
|
}
|
|
1928
1928
|
|
|
1929
1929
|
// No star in the Favorites section - every row there is one by definition.
|
|
1930
|
-
// The star sits before the time
|
|
1931
|
-
//
|
|
1932
|
-
// rather than however much shorter than "11mo ago" this label happens to
|
|
1933
|
-
// be.
|
|
1930
|
+
// The star sits before the time so the minimum-width time column stays the
|
|
1931
|
+
// rightmost alignment anchor across all rows.
|
|
1934
1932
|
if (session.favourite && sectionKey !== 'favourites') {
|
|
1935
1933
|
// A marker, not a control. It carried a `title` that made it read as
|
|
1936
1934
|
// one while a click fell through to the row and launched a terminal;
|
|
@@ -1944,8 +1942,8 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1944
1942
|
row.appendChild(star);
|
|
1945
1943
|
}
|
|
1946
1944
|
|
|
1947
|
-
// Always present
|
|
1948
|
-
//
|
|
1945
|
+
// Always present (empty without an mtime) so the star column keeps the
|
|
1946
|
+
// same anchor across every row in the panel.
|
|
1949
1947
|
const time = document.createElement('span');
|
|
1950
1948
|
time.className = 'jp-AiAssistantsPanel-rowTime';
|
|
1951
1949
|
time.textContent = session.file_mtime
|
|
@@ -2088,15 +2086,12 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2088
2086
|
lines.push('Status: active');
|
|
2089
2087
|
}
|
|
2090
2088
|
if (this._descriptor.hasBgAgents && s.bg_id) {
|
|
2091
|
-
lines.push(
|
|
2089
|
+
lines.push(
|
|
2090
|
+
`Background agent: ${s.bg_id} (click stops it and resumes here)`
|
|
2091
|
+
);
|
|
2092
2092
|
}
|
|
2093
2093
|
lines.push(...(this._hooks.tooltipLines?.(s) ?? []));
|
|
2094
|
-
|
|
2095
|
-
// the line above may have just said the click joins a live worker, and an
|
|
2096
|
-
// attach is issued before the mode is appended, so `_resolvedVariant`
|
|
2097
|
-
// promised a flag the server discards - one tooltip naming both, a line
|
|
2098
|
-
// apart (DEF-46).
|
|
2099
|
-
const variant = this._resumeVariant(s);
|
|
2094
|
+
const variant = this._resolvedVariant();
|
|
2100
2095
|
if (variant) {
|
|
2101
2096
|
// The row click launches with this, and the menu is the only other place
|
|
2102
2097
|
// that says so.
|
|
@@ -2232,30 +2227,6 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2232
2227
|
return resolved;
|
|
2233
2228
|
}
|
|
2234
2229
|
|
|
2235
|
-
/** The same mode as it applies to RESUME - of the active row by default, or
|
|
2236
|
-
* of whichever conversation is named.
|
|
2237
|
-
*
|
|
2238
|
-
* An attach is not a launch: the server returns the argv before the flag is
|
|
2239
|
-
* appended, deliberately, so promising the mode on a conversation a live
|
|
2240
|
-
* worker already holds would name something the assistant never receives.
|
|
2241
|
-
* That is true of resuming and of nothing else, so it is tested HERE rather
|
|
2242
|
-
* than in `_resolvedVariant` - which is read by the `+` button and both
|
|
2243
|
-
* new-session and branch items, none of which attach. Testing it there let
|
|
2244
|
-
* one right-click on a background-agent row strip the mode's name off every
|
|
2245
|
-
* one of those surfaces panel-wide, while their launches went on carrying it
|
|
2246
|
-
* (DEF-37).
|
|
2247
|
-
*
|
|
2248
|
-
* The argument is what lets a surface that describes SOMEONE ELSE'S resume -
|
|
2249
|
-
* a row tooltip built during a render, a branch item in the submenu - ask the
|
|
2250
|
-
* question about that conversation rather than about the active row.
|
|
2251
|
-
*/
|
|
2252
|
-
private _resumeVariant(
|
|
2253
|
-
target: { bg_id?: string | null } | null = this._activeSession
|
|
2254
|
-
): IResolvedLaunchMode | null {
|
|
2255
|
-
const joining = this._descriptor.hasBgAgents && !!target?.bg_id;
|
|
2256
|
-
return joining ? null : this._resolvedVariant();
|
|
2257
|
-
}
|
|
2258
|
-
|
|
2259
2230
|
/** The variant suffix a plain item wears while that mode is in force. */
|
|
2260
2231
|
private _variantSuffix(): string {
|
|
2261
2232
|
const resolved = this._resolvedVariant();
|
|
@@ -2304,15 +2275,14 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2304
2275
|
});
|
|
2305
2276
|
|
|
2306
2277
|
this._commands.addCommand(this._cmd('resume'), {
|
|
2307
|
-
//
|
|
2308
|
-
//
|
|
2309
|
-
// gets.
|
|
2278
|
+
// Resuming a conversation a live worker holds stops that worker first,
|
|
2279
|
+
// so a provider that has workers says which of the two this row gets.
|
|
2310
2280
|
label: () => {
|
|
2311
|
-
const variant = this.
|
|
2281
|
+
const variant = this._resolvedVariant();
|
|
2312
2282
|
const verb = this._hooks.resumeLabel?.(active()) ?? 'Resume';
|
|
2313
2283
|
return variant ? `${verb} (${variant.label})` : verb;
|
|
2314
2284
|
},
|
|
2315
|
-
icon: () => (this.
|
|
2285
|
+
icon: () => (this._resolvedVariant()?.unsafe ? shieldIcon : undefined),
|
|
2316
2286
|
execute: () => {
|
|
2317
2287
|
const s = active();
|
|
2318
2288
|
if (s) {
|
|
@@ -2323,15 +2293,14 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2323
2293
|
|
|
2324
2294
|
for (const mode of this._variantModes) {
|
|
2325
2295
|
this._commands.addCommand(this._cmd(`resume-${mode.id}`), {
|
|
2326
|
-
|
|
2296
|
+
// The same verb as the plain item, so a row a live worker holds says
|
|
2297
|
+
// here too that the worker is stopped first.
|
|
2298
|
+
label: () =>
|
|
2299
|
+
`${this._hooks.resumeLabel?.(active()) ?? 'Resume'} (${mode.menuLabel})`,
|
|
2327
2300
|
icon: shieldIcon,
|
|
2328
2301
|
// Absent while the plain item already resolves to this same mode - it
|
|
2329
2302
|
// would be a second entry building an identical launch.
|
|
2330
2303
|
isVisible: () => !this._buildsSameLaunch(mode),
|
|
2331
|
-
// A conversation a live worker already holds was started in whatever
|
|
2332
|
-
// mode that worker has; joining it cannot change that, so the variant
|
|
2333
|
-
// is disabled rather than silently joining in the wrong mode.
|
|
2334
|
-
isEnabled: () => !(this._descriptor.hasBgAgents && active()?.bg_id),
|
|
2335
2304
|
execute: () => {
|
|
2336
2305
|
const s = active();
|
|
2337
2306
|
if (s) {
|
|
@@ -2504,14 +2473,10 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2504
2473
|
this._commands.addCommand(this._cmd('open-branch'), {
|
|
2505
2474
|
// Names the mode the launch carries, like Resume, `+` and Branch Session
|
|
2506
2475
|
// do - this item opens a terminal, so the mode in force applies to it and
|
|
2507
|
-
// this was the one launch surface that stayed silent about it.
|
|
2508
|
-
// aware per conversation: the submenu passes `bg`, since a branch a live
|
|
2509
|
-
// worker holds is attached to and takes no mode (DEF-46's rule).
|
|
2476
|
+
// this was the one launch surface that stayed silent about it.
|
|
2510
2477
|
label: args => {
|
|
2511
2478
|
const base = String(args.label ?? '');
|
|
2512
|
-
const variant = this.
|
|
2513
|
-
args.bg ? { bg_id: String(args.bg) } : null
|
|
2514
|
-
);
|
|
2479
|
+
const variant = this._resolvedVariant();
|
|
2515
2480
|
return variant ? `${base} (${variant.label})` : base;
|
|
2516
2481
|
},
|
|
2517
2482
|
// Intent glyph, not row state - see switch-branch above.
|
|
@@ -2846,10 +2811,7 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2846
2811
|
command: this._cmd('open-branch'),
|
|
2847
2812
|
args: {
|
|
2848
2813
|
session_id: b.session_id,
|
|
2849
|
-
label: this._branchMenuLabel(b)
|
|
2850
|
-
// The label reads this to tell an attach from a launch; the
|
|
2851
|
-
// provider's own worker capability is tested where it is answered.
|
|
2852
|
-
...(b.bg_id ? { bg: b.bg_id } : {})
|
|
2814
|
+
label: this._branchMenuLabel(b)
|
|
2853
2815
|
}
|
|
2854
2816
|
});
|
|
2855
2817
|
}
|
|
@@ -2877,7 +2839,7 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2877
2839
|
branchName: b => this._branchDisplayName(b),
|
|
2878
2840
|
formatTime: ms => this._formatRelativeTime(ms),
|
|
2879
2841
|
branchBadge: b => this._bgBadgeFor(b),
|
|
2880
|
-
openTitle:
|
|
2842
|
+
openTitle: () => this._openBranchTitle(session),
|
|
2881
2843
|
onSwitch: id => void this._switchBranch(session, id),
|
|
2882
2844
|
onOpen: id => void this._openSession(session, undefined, id),
|
|
2883
2845
|
onDelete: ids => this._deleteBranches(session, ids),
|
|
@@ -2890,10 +2852,8 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2890
2852
|
|
|
2891
2853
|
/** Tooltip for one Open button in the Manage Sessions popup. Same rule as the
|
|
2892
2854
|
* Open Branched Conversation submenu: the button launches a terminal, so it
|
|
2893
|
-
* names the mode that launch carries
|
|
2894
|
-
|
|
2895
|
-
* `_lastBranches`, so it answers from the session. */
|
|
2896
|
-
private _openBranchTitle(session: ISession, sessionId: string): string {
|
|
2855
|
+
* names the mode that launch carries. */
|
|
2856
|
+
private _openBranchTitle(session: ISession): string {
|
|
2897
2857
|
// One terminal serves a project-scoped assistant, so the button focuses
|
|
2898
2858
|
// or starts it, under the provider's own verb for that.
|
|
2899
2859
|
if (this._descriptor.terminalScope === 'project') {
|
|
@@ -2902,11 +2862,7 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
2902
2862
|
);
|
|
2903
2863
|
}
|
|
2904
2864
|
const base = 'Open this conversation in its own terminal';
|
|
2905
|
-
const
|
|
2906
|
-
sessionId === session.session_id
|
|
2907
|
-
? session
|
|
2908
|
-
: (this._lastBranches.find(b => b.session_id === sessionId) ?? null);
|
|
2909
|
-
const variant = this._resumeVariant(target);
|
|
2865
|
+
const variant = this._resolvedVariant();
|
|
2910
2866
|
return variant ? `${base} (${variant.label})` : base;
|
|
2911
2867
|
}
|
|
2912
2868
|
|
package/src/core/types.ts
CHANGED
|
@@ -349,7 +349,8 @@ export interface ITabColourEvents {
|
|
|
349
349
|
* shared panel. */
|
|
350
350
|
export interface IProviderHooks {
|
|
351
351
|
/** Label for the primary open action, when the provider distinguishes verbs
|
|
352
|
-
* (
|
|
352
|
+
* (stopping a live worker before resuming is not the same as resuming a
|
|
353
|
+
* dormant store entry). */
|
|
353
354
|
resumeLabel?: (session: ISession | null) => string;
|
|
354
355
|
/** Extra lines for the row tooltip. */
|
|
355
356
|
tooltipLines?: (session: ISession) => string[];
|
package/src/providers/claude.ts
CHANGED
|
@@ -76,8 +76,8 @@ export const descriptor: IProviderDescriptor = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export const hooks: IProviderHooks = {
|
|
79
|
-
//
|
|
80
|
-
//
|
|
79
|
+
// Opening a conversation a live agent holds stops the agent first, so the
|
|
80
|
+
// item says so before the click (DEF-PROV-272).
|
|
81
81
|
resumeLabel: (session: ISession | null) =>
|
|
82
|
-
session?.bg_id ? '
|
|
82
|
+
session?.bg_id ? 'Stop Background Agent and Resume' : 'Resume'
|
|
83
83
|
};
|
package/style/base.css
CHANGED
|
@@ -13,6 +13,14 @@
|
|
|
13
13
|
on purpose - the partial row is the scroll-continuation cue. */
|
|
14
14
|
--aica-row-height: 24px;
|
|
15
15
|
|
|
16
|
+
/* Single source for the gap between the panel border and its content, on
|
|
17
|
+
both sides: the header, search field, error line, section headers and rows
|
|
18
|
+
all read it, so the title, the search field, the caret column and the
|
|
19
|
+
status-dot column keep one left edge. 4px, not 8px: the lists reserve a
|
|
20
|
+
scrollbar gutter as well, so 8px put the time column 23px from the right
|
|
21
|
+
border where the file browser's sits 12px from it. */
|
|
22
|
+
--aica-inset: 4px;
|
|
23
|
+
|
|
16
24
|
display: flex;
|
|
17
25
|
flex-direction: column;
|
|
18
26
|
position: relative;
|
|
@@ -27,7 +35,7 @@
|
|
|
27
35
|
.jp-AiAssistantsPanel-header {
|
|
28
36
|
display: flex;
|
|
29
37
|
align-items: center;
|
|
30
|
-
padding: 4px
|
|
38
|
+
padding: 4px var(--aica-inset);
|
|
31
39
|
border-bottom: 1px solid var(--jp-border-color2);
|
|
32
40
|
background: var(--jp-layout-color2);
|
|
33
41
|
flex: 0 0 auto;
|
|
@@ -87,7 +95,7 @@
|
|
|
87
95
|
flex: 0 0 auto;
|
|
88
96
|
display: flex;
|
|
89
97
|
position: relative;
|
|
90
|
-
margin: 4px
|
|
98
|
+
margin: 4px var(--aica-inset);
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
/* `display: flex` above would beat the user-agent `[hidden] { display: none }`,
|
|
@@ -169,7 +177,7 @@
|
|
|
169
177
|
server must leave the other panels alone. */
|
|
170
178
|
.jp-AiAssistantsPanel-error {
|
|
171
179
|
flex: 0 0 auto;
|
|
172
|
-
margin: 4px
|
|
180
|
+
margin: 4px var(--aica-inset);
|
|
173
181
|
padding: 4px 6px;
|
|
174
182
|
border-radius: 2px;
|
|
175
183
|
|
|
@@ -246,7 +254,7 @@
|
|
|
246
254
|
display: flex;
|
|
247
255
|
align-items: center;
|
|
248
256
|
width: 100%;
|
|
249
|
-
padding: 4px
|
|
257
|
+
padding: 4px var(--aica-inset);
|
|
250
258
|
background: var(--jp-layout-color2);
|
|
251
259
|
border: none;
|
|
252
260
|
cursor: pointer;
|
|
@@ -305,7 +313,7 @@
|
|
|
305
313
|
.jp-AiAssistantsPanel-row {
|
|
306
314
|
display: flex;
|
|
307
315
|
align-items: center;
|
|
308
|
-
padding: 0
|
|
316
|
+
padding: 0 var(--aica-inset);
|
|
309
317
|
height: var(--aica-row-height);
|
|
310
318
|
line-height: var(--aica-row-height);
|
|
311
319
|
cursor: pointer;
|
|
@@ -956,20 +964,21 @@ body[data-jp-theme-light='true'] .jp-AiAssistantsPanel-dotGlyph {
|
|
|
956
964
|
color: var(--jp-layout-color1);
|
|
957
965
|
}
|
|
958
966
|
|
|
959
|
-
/* Last-activity time on session rows - dim,
|
|
960
|
-
line
|
|
961
|
-
takes all the slack.
|
|
962
|
-
The label sizes to its own text. A fixed 4.5em box held the difference
|
|
963
|
-
between the label and the widest one ("11mo ago") open as empty space on its
|
|
964
|
-
LEFT, which is where the favourite star sits, so the star stood 32px from a
|
|
965
|
-
short label such as "now" and 6px from the longest - measured on the row
|
|
966
|
-
markup against this stylesheet, 2026-09-23. Sizing to the text puts the star
|
|
967
|
-
the row's own 6px gap from every label, and removes the one case the fixed
|
|
968
|
-
box still got wrong: a label wider than the box overflowed it and broke the
|
|
969
|
-
alignment the box was there to keep. Tabular figures keep the digits an even
|
|
970
|
-
width, as the branch popup's twin already does. */
|
|
967
|
+
/* Last-activity time on session rows - dim, minimum-width right-aligned column so
|
|
968
|
+
values line up across rows; the star column sits before it. */
|
|
971
969
|
.jp-AiAssistantsPanel-rowTime {
|
|
972
970
|
flex: 0 0 auto;
|
|
971
|
+
|
|
972
|
+
/* 4.5em, not 4em: the longest label this column draws ("11mo ago") measures
|
|
973
|
+
47.4px against a 43.3px box, so the one column promised as fixed-width
|
|
974
|
+
right-aligned went ragged on exactly the oldest rows. A minimum, not a
|
|
975
|
+
width: where system-ui is a wider face - DejaVu Sans on a stock Linux
|
|
976
|
+
desktop, 54.5px for the same label - a fixed box let the label spill 5.8px
|
|
977
|
+
past the column's right edge; a minimum grows that one box leftward
|
|
978
|
+
instead, so the labels stay aligned and only that row's star moves.
|
|
979
|
+
Tabular figures keep the digits aligned down the column, as the branch
|
|
980
|
+
popup's twin already does. */
|
|
981
|
+
min-width: 4.5em;
|
|
973
982
|
white-space: nowrap;
|
|
974
983
|
text-align: right;
|
|
975
984
|
color: var(--jp-ui-font-color2);
|