xtralab 0.4.0 → 0.6.0

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.
Files changed (96) hide show
  1. package/README.md +110 -73
  2. package/lib/agentSessions.d.ts +50 -0
  3. package/lib/agentSessions.js +45 -0
  4. package/lib/fileBrowser/index.js +5 -1
  5. package/lib/git/commands.d.ts +12 -13
  6. package/lib/git/commands.js +7 -28
  7. package/lib/git/diffProvider.d.ts +8 -0
  8. package/lib/git/diffProvider.js +314 -0
  9. package/lib/git/diffSurface.d.ts +115 -0
  10. package/lib/git/diffSurface.js +342 -0
  11. package/lib/git/diffWidget.d.ts +8 -13
  12. package/lib/git/diffWidget.js +65 -311
  13. package/lib/git/imageDiff.d.ts +23 -0
  14. package/lib/git/imageDiff.js +256 -0
  15. package/lib/git/index.d.ts +10 -8
  16. package/lib/git/index.js +37 -52
  17. package/lib/index.d.ts +4 -2
  18. package/lib/index.js +14 -8
  19. package/lib/launcher/agents.d.ts +1 -1
  20. package/lib/launcher/agents.js +22 -13
  21. package/lib/launcher/commands.d.ts +32 -6
  22. package/lib/launcher/commands.js +81 -57
  23. package/lib/launcher/dashboard.d.ts +15 -0
  24. package/lib/launcher/dashboard.js +119 -5
  25. package/lib/launcher/editorRegistry.d.ts +43 -0
  26. package/lib/launcher/editorRegistry.js +96 -0
  27. package/lib/launcher/editors.d.ts +78 -0
  28. package/lib/launcher/editors.js +110 -0
  29. package/lib/launcher/icons.d.ts +25 -6
  30. package/lib/launcher/icons.js +116 -19
  31. package/lib/launcher/index.d.ts +2 -22
  32. package/lib/launcher/index.js +48 -25
  33. package/lib/launcher/registry.d.ts +21 -0
  34. package/lib/launcher/registry.js +28 -0
  35. package/lib/launcher/tokens.d.ts +41 -0
  36. package/lib/launcher/tokens.js +17 -0
  37. package/lib/terminals/detection.d.ts +11 -0
  38. package/lib/terminals/detection.js +50 -0
  39. package/lib/terminals/index.d.ts +27 -0
  40. package/lib/terminals/index.js +194 -0
  41. package/lib/terminals/model.d.ts +191 -0
  42. package/lib/terminals/model.js +418 -0
  43. package/lib/terminals/widget.d.ts +72 -0
  44. package/lib/terminals/widget.js +93 -0
  45. package/lib/topBar/icons.d.ts +13 -0
  46. package/lib/topBar/icons.js +27 -0
  47. package/lib/topBar/index.d.ts +16 -0
  48. package/lib/topBar/index.js +86 -0
  49. package/package.json +52 -35
  50. package/schema/launcher.json +56 -1
  51. package/src/agentSessions.ts +94 -0
  52. package/src/fileBrowser/index.ts +5 -1
  53. package/src/git/commands.ts +12 -38
  54. package/src/git/diffProvider.tsx +462 -0
  55. package/src/git/diffSurface.tsx +588 -0
  56. package/src/git/diffWidget.tsx +105 -465
  57. package/src/git/imageDiff.tsx +469 -0
  58. package/src/git/index.ts +38 -68
  59. package/src/index.ts +14 -8
  60. package/src/launcher/agents.ts +23 -14
  61. package/src/launcher/commands.ts +98 -60
  62. package/src/launcher/dashboard.tsx +205 -3
  63. package/src/launcher/editorRegistry.ts +149 -0
  64. package/src/launcher/editors.ts +183 -0
  65. package/src/launcher/icons.ts +120 -20
  66. package/src/launcher/index.ts +62 -27
  67. package/src/launcher/registry.ts +33 -0
  68. package/src/launcher/tokens.ts +51 -0
  69. package/src/terminals/detection.ts +65 -0
  70. package/src/terminals/index.ts +222 -0
  71. package/src/terminals/model.ts +497 -0
  72. package/src/terminals/widget.tsx +240 -0
  73. package/src/topBar/icons.ts +30 -0
  74. package/src/topBar/index.ts +121 -0
  75. package/style/git.css +267 -266
  76. package/style/index.css +2 -1
  77. package/style/index.js +2 -1
  78. package/style/launcher.css +124 -2
  79. package/style/terminals.css +235 -0
  80. package/style/topBar.css +38 -0
  81. package/lib/git/icons.d.ts +0 -14
  82. package/lib/git/icons.js +0 -19
  83. package/lib/git/panel.d.ts +0 -29
  84. package/lib/git/panel.js +0 -42
  85. package/lib/git/panelComponent.d.ts +0 -28
  86. package/lib/git/panelComponent.js +0 -320
  87. package/lib/statusBar/index.d.ts +0 -14
  88. package/lib/statusBar/index.js +0 -89
  89. package/lib/statusBar/widget.d.ts +0 -121
  90. package/lib/statusBar/widget.js +0 -237
  91. package/src/git/icons.ts +0 -20
  92. package/src/git/panel.tsx +0 -56
  93. package/src/git/panelComponent.tsx +0 -575
  94. package/src/statusBar/index.ts +0 -115
  95. package/src/statusBar/widget.tsx +0 -318
  96. package/style/statusBar.css +0 -95
@@ -0,0 +1,50 @@
1
+ import { URLExt } from '@jupyterlab/coreutils';
2
+ import { ServerConnection } from '@jupyterlab/services';
3
+ /**
4
+ * Ask the xtralab server which of `commands` is currently running inside each
5
+ * open terminal session. Resolves to a map from session name to the matched
6
+ * agent command, or `null` for a session sitting at its prompt.
7
+ *
8
+ * On any failure (endpoint missing on an older server, network error,
9
+ * malformed response) the whole result is `null` — callers should treat that
10
+ * as "detection unavailable" and fall back to the optimistic launch tags
11
+ * rather than clearing any badges.
12
+ */
13
+ export async function fetchRunningAgents(commands) {
14
+ if (commands.length === 0) {
15
+ return {};
16
+ }
17
+ const settings = ServerConnection.makeSettings();
18
+ const url = URLExt.join(settings.baseUrl, 'xtralab', 'terminals', 'agents');
19
+ let response;
20
+ try {
21
+ response = await ServerConnection.makeRequest(url, {
22
+ method: 'POST',
23
+ body: JSON.stringify({ commands })
24
+ }, settings);
25
+ }
26
+ catch (error) {
27
+ console.warn('xtralab: running-agent detection failed', error);
28
+ return null;
29
+ }
30
+ if (!response.ok) {
31
+ console.warn(`xtralab: detection endpoint returned HTTP ${response.status}`);
32
+ return null;
33
+ }
34
+ let payload;
35
+ try {
36
+ payload = await response.json();
37
+ }
38
+ catch (error) {
39
+ console.warn('xtralab: detection response was not JSON', error);
40
+ return null;
41
+ }
42
+ if (!payload || typeof payload !== 'object') {
43
+ return null;
44
+ }
45
+ const result = {};
46
+ for (const [name, command] of Object.entries(payload)) {
47
+ result[name] = typeof command === 'string' && command ? command : null;
48
+ }
49
+ return result;
50
+ }
@@ -0,0 +1,27 @@
1
+ import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
+ /**
3
+ * Adds a left-sidebar panel that lists every running terminal session.
4
+ *
5
+ * Each row is labelled with the *real* title the running program
6
+ * published — the launcher's agent name, or whatever title an xterm
7
+ * escape sequence set — which the registry caches so it survives the
8
+ * user closing the tab while the session keeps running on the server.
9
+ * JupyterLab's built-in "Running Terminals and Kernels" panel lists the
10
+ * same sessions but only by their `terminals/<n>` name, so this panel
11
+ * resolves and caches the published title to show something meaningful.
12
+ *
13
+ * Clicking a row activates the existing tab if one is open, or reopens
14
+ * the session in a fresh terminal widget (`terminal:open`). The inline
15
+ * `×` button shuts the session down on the server. The header carries a
16
+ * `+` button and a stop button (the latter shuts every session down at
17
+ * once, after a confirmation).
18
+ *
19
+ * The `+` button drops down a menu — built from the launcher's shared
20
+ * `IAgentRegistry` — listing each available agent (Claude, Codex, …)
21
+ * followed by a plain terminal, so starting an agent session is as
22
+ * consistent here as in the launcher and reuses the very same registered
23
+ * commands and icons. When the launcher is disabled or no agents are
24
+ * installed, the button falls back to opening a plain terminal directly.
25
+ */
26
+ declare const plugin: JupyterFrontEndPlugin<void>;
27
+ export default plugin;
@@ -0,0 +1,194 @@
1
+ import { ILayoutRestorer } from '@jupyterlab/application';
2
+ import { Dialog, showDialog } from '@jupyterlab/apputils';
3
+ import { ITerminalTracker } from '@jupyterlab/terminal';
4
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
5
+ import { MenuSvg, terminalIcon } from '@jupyterlab/ui-components';
6
+ import { IAgentSessions } from '../agentSessions';
7
+ import { IEditorRegistry } from '../launcher/editorRegistry';
8
+ import { agentCommandId, IAgentRegistry } from '../launcher/tokens';
9
+ import { SessionRegistry } from './model';
10
+ import { RunningTerminals } from './widget';
11
+ const PLUGIN_ID = 'xtralab:terminals';
12
+ /**
13
+ * Adds a left-sidebar panel that lists every running terminal session.
14
+ *
15
+ * Each row is labelled with the *real* title the running program
16
+ * published — the launcher's agent name, or whatever title an xterm
17
+ * escape sequence set — which the registry caches so it survives the
18
+ * user closing the tab while the session keeps running on the server.
19
+ * JupyterLab's built-in "Running Terminals and Kernels" panel lists the
20
+ * same sessions but only by their `terminals/<n>` name, so this panel
21
+ * resolves and caches the published title to show something meaningful.
22
+ *
23
+ * Clicking a row activates the existing tab if one is open, or reopens
24
+ * the session in a fresh terminal widget (`terminal:open`). The inline
25
+ * `×` button shuts the session down on the server. The header carries a
26
+ * `+` button and a stop button (the latter shuts every session down at
27
+ * once, after a confirmation).
28
+ *
29
+ * The `+` button drops down a menu — built from the launcher's shared
30
+ * `IAgentRegistry` — listing each available agent (Claude, Codex, …)
31
+ * followed by a plain terminal, so starting an agent session is as
32
+ * consistent here as in the launcher and reuses the very same registered
33
+ * commands and icons. When the launcher is disabled or no agents are
34
+ * installed, the button falls back to opening a plain terminal directly.
35
+ */
36
+ const plugin = {
37
+ id: PLUGIN_ID,
38
+ description: 'A left-sidebar panel listing every running terminal session by its real (program-published) title, with activate/reopen, shutdown and new-terminal actions.',
39
+ autoStart: true,
40
+ requires: [ITerminalTracker],
41
+ optional: [
42
+ ILayoutRestorer,
43
+ ITranslator,
44
+ IAgentRegistry,
45
+ IEditorRegistry,
46
+ IAgentSessions
47
+ ],
48
+ activate: (app, tracker, restorer, translator, agentRegistry, editorRegistry, agentSessions) => {
49
+ const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
50
+ // Resolve a session's running command to an icon: the matching agent's
51
+ // logo, the matching editor's logo (Neovim/Vim), or the plain terminal
52
+ // icon when nothing recognised is running. Reads `agentRegistry.agents`
53
+ // live so it tracks settings changes. Used by the panel to badge each row.
54
+ const iconForCommand = (command) => {
55
+ var _a;
56
+ if (!command) {
57
+ return terminalIcon;
58
+ }
59
+ const agent = agentRegistry === null || agentRegistry === void 0 ? void 0 : agentRegistry.agents.find(a => a.command === command);
60
+ if (agent) {
61
+ return agent.icon;
62
+ }
63
+ // A terminal running an editor (Neovim/Vim, or a user-configured one) is
64
+ // badged with its logo, the same as an agent.
65
+ const editor = editorRegistry === null || editorRegistry === void 0 ? void 0 : editorRegistry.editors.find(e => e.command === command);
66
+ return (_a = editor === null || editor === void 0 ? void 0 : editor.icon) !== null && _a !== void 0 ? _a : terminalIcon;
67
+ };
68
+ const registry = new SessionRegistry({
69
+ serviceManager: app.serviceManager,
70
+ tracker,
71
+ // The shell tells the registry which widget is current, so the panel can
72
+ // highlight the terminal that is the current main-area widget (and
73
+ // nothing while a notebook or other widget is current instead).
74
+ shell: app.shell,
75
+ agentSessions,
76
+ // The commands the server should look for: the current agent and editor
77
+ // lists, each read live from its registry so they track settings changes
78
+ // — a terminal running an agent or an editor (Neovim/Vim, or a configured
79
+ // one) is badged.
80
+ detectCommands: () => {
81
+ var _a, _b;
82
+ return [
83
+ ...((_a = agentRegistry === null || agentRegistry === void 0 ? void 0 : agentRegistry.agents.map(a => a.command)) !== null && _a !== void 0 ? _a : []),
84
+ ...((_b = editorRegistry === null || editorRegistry === void 0 ? void 0 : editorRegistry.editors.map(e => e.command)) !== null && _b !== void 0 ? _b : [])
85
+ ];
86
+ }
87
+ });
88
+ // Mirror the agent/editor detected in each open terminal onto its main-area
89
+ // tab icon, so the tab matches its sidebar row. Re-running on every
90
+ // `stateChanged` is safe: `Title.icon` ignores an unchanged assignment, so
91
+ // the writes neither loop nor churn.
92
+ const syncTabIcons = () => {
93
+ tracker.forEach(widget => {
94
+ var _a;
95
+ const session = (_a = widget.content) === null || _a === void 0 ? void 0 : _a.session;
96
+ if (!session) {
97
+ return;
98
+ }
99
+ widget.title.icon = iconForCommand(registry.agentCommandFor(session.name));
100
+ });
101
+ };
102
+ registry.stateChanged.connect(syncTabIcons);
103
+ syncTabIcons();
104
+ const onActivate = (name) => {
105
+ // `terminal:open` is the upstream entry point that handles both
106
+ // sides of this: if a widget is already attached to the named
107
+ // session it activates that tab, otherwise it spins up a fresh
108
+ // widget connected to the live session.
109
+ void app.commands.execute('terminal:open', { name });
110
+ };
111
+ const onShutdown = (name) => {
112
+ void app.serviceManager.terminals.shutdown(name).catch(reason => {
113
+ console.error(`xtralab: failed to shut down terminal ${name}: ${reason}`);
114
+ });
115
+ };
116
+ const onShutdownAll = () => {
117
+ // Tearing down every session at once is hard to undo (each may be a
118
+ // running agent), so confirm before calling the manager's bulk
119
+ // shutdown. `runningChanged` then clears the list and the panel
120
+ // re-renders empty.
121
+ void showDialog({
122
+ title: trans.__('Shut Down All Terminals?'),
123
+ body: trans.__('Are you sure you want to permanently shut down all running terminals?'),
124
+ buttons: [
125
+ Dialog.cancelButton({ label: trans.__('Cancel') }),
126
+ Dialog.warnButton({ label: trans.__('Shut Down All') })
127
+ ]
128
+ })
129
+ .then(result => {
130
+ if (result.button.accept) {
131
+ return app.serviceManager.terminals.shutdownAll();
132
+ }
133
+ })
134
+ .catch(reason => {
135
+ console.error(`xtralab: failed to shut down all terminals: ${reason}`);
136
+ });
137
+ };
138
+ // The header "+" button. With the launcher's agent registry available
139
+ // it drops down a menu of the available agents (reusing their registered
140
+ // `xtralab:start-agent:<id>` commands, so the icons and labels match the
141
+ // launcher exactly) followed by a separator and a plain terminal. Built
142
+ // lazily and repopulated on each open so it always reflects the current,
143
+ // settings-driven agent list. Without the registry (launcher disabled)
144
+ // or with no agents installed, there is nothing to choose between, so
145
+ // the button opens a plain terminal directly in one click.
146
+ //
147
+ // `MenuSvg` (not the bare Lumino `Menu`) is what every JupyterLab menu
148
+ // uses: its renderer applies the `menuItem` LabIcon stylesheet, which
149
+ // sizes the agent icons to 16px and centers them vertically — the bare
150
+ // `Menu` renders the raw, oversized, misaligned SVGs.
151
+ let newMenu = null;
152
+ const onCreate = (anchor) => {
153
+ var _a;
154
+ const agents = (_a = agentRegistry === null || agentRegistry === void 0 ? void 0 : agentRegistry.agents) !== null && _a !== void 0 ? _a : [];
155
+ if (agents.length === 0) {
156
+ void app.commands.execute('terminal:create-new');
157
+ return;
158
+ }
159
+ if (!newMenu) {
160
+ newMenu = new MenuSvg({ commands: app.commands });
161
+ }
162
+ newMenu.clearItems();
163
+ for (const agent of agents) {
164
+ newMenu.addItem({ command: agentCommandId(agent.id) });
165
+ }
166
+ newMenu.addItem({ type: 'separator' });
167
+ newMenu.addItem({ command: 'terminal:create-new' });
168
+ newMenu.open(anchor.x, anchor.y);
169
+ };
170
+ const panel = new RunningTerminals({
171
+ registry,
172
+ trans,
173
+ iconForCommand,
174
+ onActivate,
175
+ onShutdown,
176
+ onShutdownAll,
177
+ onCreate
178
+ });
179
+ // Added to the left sidebar. The shipped `layout` setting in
180
+ // jupyter-config/labconfig gives each sidebar widget a default rank —
181
+ // this Terminals panel ranks first, so running agents sit one click
182
+ // away at the top. That setting assigns rank only and pins no area, so
183
+ // the "Move Widget" context menu can relocate the panel and the layout
184
+ // restorer remembers where the user puts it. The `rank` here is the
185
+ // in-code fallback used when the setting is absent. Sessions restored
186
+ // from a previous lab run land via `runningChanged` once the terminal
187
+ // manager is ready, which the panel's `UseSignal` picks up.
188
+ app.shell.add(panel, 'left', { rank: 1 });
189
+ if (restorer) {
190
+ restorer.add(panel, panel.id);
191
+ }
192
+ }
193
+ };
194
+ export default plugin;
@@ -0,0 +1,191 @@
1
+ import type { JupyterFrontEnd } from '@jupyterlab/application';
2
+ import type { MainAreaWidget } from '@jupyterlab/apputils';
3
+ import type { ServiceManager } from '@jupyterlab/services';
4
+ import type { ITerminal, ITerminalTracker } from '@jupyterlab/terminal';
5
+ import type { IDisposable } from '@lumino/disposable';
6
+ import { ISignal } from '@lumino/signaling';
7
+ import type { IAgentSessions } from '../agentSessions';
8
+ /**
9
+ * The widget shape every entry in `ITerminalTracker` takes — kept here
10
+ * so the registry's call sites read cleanly.
11
+ */
12
+ export type TerminalWidget = MainAreaWidget<ITerminal.ITerminal>;
13
+ /**
14
+ * Source-of-truth model for the running-terminals panel. Each running
15
+ * terminal session known to the server is one entry; the registry caches
16
+ * the last `widget.title.label` we observed so the agent name (or any
17
+ * xterm-published title) survives the user closing the tab while the
18
+ * session continues running on the backend.
19
+ *
20
+ * It also resolves *which agent is running* in each session, so the panel can
21
+ * badge rows with the agent's logo. Two inputs feed that, reconciled by
22
+ * {@link agentCommandFor}:
23
+ * - an optimistic launch tag ({@link IAgentSessions}) written when we start
24
+ * an agent ourselves — instant, but blind to the agent later exiting; and
25
+ * - authoritative server-side process detection, polled here, which works
26
+ * for hand-started agents too and clears once an agent exits.
27
+ *
28
+ * Upstream session signals:
29
+ * - `serviceManager.terminals.runningChanged` is the authoritative
30
+ * list of live sessions; everything not in it has been shut down
31
+ * server-side and we must drop it.
32
+ * - `tracker.widgetAdded` plus the per-widget `title.changed` /
33
+ * `disposed` signals keep our label cache in sync with whatever
34
+ * xterm/the launcher has set on the open tabs.
35
+ *
36
+ * `stateChanged` is emitted for every kind of update; the panel hooks it
37
+ * through a `UseSignal` so a single subscription re-renders the list on
38
+ * any change.
39
+ */
40
+ export declare class SessionRegistry implements IDisposable {
41
+ constructor(options: SessionRegistry.IOptions);
42
+ /**
43
+ * Emitted whenever the live session list, a cached label, or the detected
44
+ * running agent changes.
45
+ */
46
+ get stateChanged(): ISignal<this, void>;
47
+ get isDisposed(): boolean;
48
+ /**
49
+ * Names of all live sessions, ordered by their stable rank so the
50
+ * rendered list keeps a consistent order (roughly creation order) as
51
+ * new sessions are added and old ones shut down.
52
+ */
53
+ sessionNames(): string[];
54
+ /**
55
+ * True iff the named session is still on the server. Used by the panel
56
+ * to skip a row during the brief window between the session's shutdown
57
+ * and the next `runningChanged` arriving.
58
+ */
59
+ has(name: string): boolean;
60
+ /**
61
+ * Display name for the session. The cache wins because it only
62
+ * holds "real" labels (the launcher's agent name or an xterm escape
63
+ * sequence — see `_cacheLabel` for the filter); reading it first
64
+ * prevents the transient `Terminal {name}` an XTerm widget shows
65
+ * during reconnect from flickering into the panel. The live widget
66
+ * title is used when no cache exists, and the `Terminal {name}`
67
+ * fallback covers sessions we have never seen a widget for (e.g.
68
+ * surviving a lab reload).
69
+ */
70
+ labelFor(name: string): string;
71
+ /**
72
+ * The command of the agent running in the session, or `null` if none.
73
+ *
74
+ * Server-side detection is authoritative whenever it reports a running
75
+ * agent. A launch tag fills two gaps: the startup grace window right after
76
+ * we launch an agent (so its logo shows before its process is detectable),
77
+ * and any time detection is unavailable (older server, transient error).
78
+ * Once the grace window has passed and detection has reported the session
79
+ * as idle, the tag is ignored — that is how the badge clears when an agent
80
+ * exits.
81
+ */
82
+ agentCommandFor(name: string): string | null;
83
+ /**
84
+ * Return the open widget for a session, if any. The panel uses this to
85
+ * switch behaviour between "activate existing tab" and "open a new tab
86
+ * connected to the live session".
87
+ */
88
+ widgetFor(name: string): TerminalWidget | null;
89
+ /**
90
+ * Session name of the terminal that is currently the active widget in the
91
+ * shell's main area, or `null` when that widget is not a terminal (for
92
+ * example a notebook or text editor is current). The panel uses this to
93
+ * highlight the current terminal's row — mirroring how the file browser
94
+ * surfaces the open document — and to leave every row unhighlighted while a
95
+ * non-terminal tab is current.
96
+ */
97
+ currentSessionName(): string | null;
98
+ /**
99
+ * Stable per-session rank assigned in observation order. Used to keep
100
+ * the rendered list in a steady order so rows don't reshuffle as
101
+ * sessions come and go. Cleaned up when the session is shut down so the
102
+ * counter does not grow without bound across long sessions.
103
+ */
104
+ rankFor(name: string): number;
105
+ dispose(): void;
106
+ private _refreshLive;
107
+ private _onRunningChanged;
108
+ /**
109
+ * Adopt `next` as the live set and prune every per-session map for
110
+ * sessions that have gone away — labels, ranks, first-seen timestamps,
111
+ * detection results, and the shared launch tag. Without this the maps (and
112
+ * the rank counter) would grow without bound as terminals come and go.
113
+ */
114
+ private _reconcileLive;
115
+ /**
116
+ * Poll body: ask the server which agent runs in each terminal and update
117
+ * the detection map. On failure we keep the previous results rather than
118
+ * clearing them, so a transient error doesn't strip every badge.
119
+ */
120
+ private _refreshDetection;
121
+ private _onTagChanged;
122
+ private _onWidgetAdded;
123
+ private _onShellCurrentChanged;
124
+ /**
125
+ * Recompute which terminal (if any) is the active main-area widget and emit
126
+ * only when it changes. The terminal tracker tells us whether the shell's
127
+ * current widget is one of our terminals; anything else — a notebook, an
128
+ * editor, or nothing — clears the highlight.
129
+ */
130
+ private _updateCurrent;
131
+ private _trackWidget;
132
+ private _untrackWidget;
133
+ private _onTitleChanged;
134
+ /**
135
+ * Update the cached label for a session, but only when the new
136
+ * label is "real" — non-empty and not one of the transient defaults
137
+ * the XTerm widget cycles through during reconnect (`'...'` while
138
+ * the websocket is opening, `'Terminal {name}'` once
139
+ * `_initialConnection` fires). Without the filter, briefly
140
+ * reopening a tab while an agent has yet to re-emit its xterm
141
+ * title escape sequence would clobber the cached agent name with
142
+ * `Terminal 1`, which is exactly the regression we are guarding
143
+ * against. Live widget titles still display whatever the widget
144
+ * currently holds because `labelFor` consults the widget first;
145
+ * the filter only affects what survives a tab close.
146
+ */
147
+ private _cacheLabel;
148
+ private _onWidgetDisposed;
149
+ private _terminals;
150
+ private _tracker;
151
+ private _agentSessions;
152
+ private _detectCommands;
153
+ private _shell;
154
+ private _poll;
155
+ private _labels;
156
+ private _ranks;
157
+ private _firstSeen;
158
+ private _detected;
159
+ private _live;
160
+ private _currentName;
161
+ private _nextRank;
162
+ private _isDisposed;
163
+ private _stateChanged;
164
+ }
165
+ /**
166
+ * Construction options for {@link SessionRegistry}.
167
+ */
168
+ export declare namespace SessionRegistry {
169
+ interface IOptions {
170
+ serviceManager: ServiceManager.IManager;
171
+ tracker: ITerminalTracker;
172
+ /**
173
+ * The application shell, used to tell which widget is currently active so
174
+ * the panel can highlight the terminal that is the current main-area
175
+ * widget — and highlight nothing when that widget is not a terminal (a
176
+ * notebook, an editor, …). Optional: without it, or on a shell that cannot
177
+ * report `currentChanged`, the highlight stays off.
178
+ */
179
+ shell?: JupyterFrontEnd.IShell | null;
180
+ /**
181
+ * Shared launch-tag registry. When present, its records seed each row's
182
+ * agent badge until server-side detection takes over.
183
+ */
184
+ agentSessions?: IAgentSessions | null;
185
+ /**
186
+ * Returns the agent commands the server should look for when detecting
187
+ * running agents. Read on every poll so it tracks the live agent list.
188
+ */
189
+ detectCommands?: () => string[];
190
+ }
191
+ }