@aiwayds/dsh-tui-pi 2.5.0 → 2.7.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.
- package/README.md +2 -2
- package/README.zh-CN.md +1 -2
- package/lib/history-fork.d.ts +83 -0
- package/lib/history-fork.js +182 -0
- package/lib/history-fork.js.map +1 -0
- package/lib/history-turns.d.ts +95 -0
- package/lib/history-turns.js +186 -0
- package/lib/history-turns.js.map +1 -0
- package/lib/history.d.ts +268 -0
- package/lib/history.js +794 -0
- package/lib/history.js.map +1 -0
- package/lib/hotkeys.d.ts +1 -1
- package/lib/hotkeys.js +2 -3
- package/lib/hotkeys.js.map +1 -1
- package/lib/index.js +218 -51
- package/lib/index.js.map +1 -1
- package/lib/keymap.d.ts +0 -5
- package/lib/keymap.js +3 -8
- package/lib/keymap.js.map +1 -1
- package/lib/panels.d.ts +15 -0
- package/lib/panels.js +13 -3
- package/lib/panels.js.map +1 -1
- package/lib/preset-dialog.d.ts +152 -0
- package/lib/preset-dialog.js +254 -0
- package/lib/preset-dialog.js.map +1 -0
- package/lib/preset.d.ts +16 -9
- package/lib/preset.js +18 -11
- package/lib/preset.js.map +1 -1
- package/lib/session.d.ts +41 -0
- package/lib/session.js +82 -25
- package/lib/session.js.map +1 -1
- package/lib/sessions.d.ts +43 -0
- package/lib/sessions.js +9 -5
- package/lib/sessions.js.map +1 -1
- package/lib/tui.js +1 -1
- package/lib/tui.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preset-switch confirmation dialog + the switch orchestration.
|
|
3
|
+
*
|
|
4
|
+
* Switching presets is an EXPLICIT action with a visible cost: the preset is
|
|
5
|
+
* baked into a session at creation time, so applying a new selection means
|
|
6
|
+
* starting a NEW session — the current one is detached (and stays resumable
|
|
7
|
+
* via /resume). Whenever a live session exists, the switch therefore goes
|
|
8
|
+
* through this confirmation gate first (mirroring the /resume repair
|
|
9
|
+
* dialog's pure-reducer split, src/repair-dialog.ts, so the decision matrix
|
|
10
|
+
* stays unit-testable without a terminal); on a fresh TUI without a session
|
|
11
|
+
* there is nothing to leave behind and the selection applies directly.
|
|
12
|
+
*
|
|
13
|
+
* The dialog offers exactly three ways out: FORK the current conversation
|
|
14
|
+
* into the new session on the preset (context weight included), start FRESH
|
|
15
|
+
* (an empty session on the preset), or CANCEL. Title/body wording
|
|
16
|
+
* distinguishes a switch to a different preset from a restart on the
|
|
17
|
+
* current one. ↑↓ moves the selection, `1`/`2`/`3` select directly, Enter
|
|
18
|
+
* confirms, Esc = cancel. Framing/focus follow the shared overlay contract:
|
|
19
|
+
* PanelHost framing, close re-focuses the CURRENT editor instance through
|
|
20
|
+
* `restoreFocus`.
|
|
21
|
+
*/
|
|
22
|
+
import { type Component, type TUI } from '@earendil-works/pi-tui';
|
|
23
|
+
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
24
|
+
import type { PresetEntry, PresetState } from './preset.ts';
|
|
25
|
+
import { type TuiTheme } from './theme/index.ts';
|
|
26
|
+
/** The three choices in display order; index 0 is preselected. */
|
|
27
|
+
export declare const PRESET_CONFIRM_OPTION_IDS: ReadonlyArray<'fork' | 'fresh' | 'cancel'>;
|
|
28
|
+
/**
|
|
29
|
+
* How the dialog words one target: a move to a DIFFERENT preset vs a
|
|
30
|
+
* restart on the already-selected one. The three outcomes are the same
|
|
31
|
+
* either way — only the labels and the title change.
|
|
32
|
+
*/
|
|
33
|
+
export interface PresetConfirmWording {
|
|
34
|
+
title: string;
|
|
35
|
+
/** The fork option's row text (carries the conversation). */
|
|
36
|
+
fork: string;
|
|
37
|
+
/** The fresh option's row text (empty new session). */
|
|
38
|
+
fresh: string;
|
|
39
|
+
/** The first body point (what "applying" means here). */
|
|
40
|
+
firstPoint: string;
|
|
41
|
+
}
|
|
42
|
+
/** The dialog wording for `name`: `restart` marks a re-selection of the current preset. */
|
|
43
|
+
export declare function presetConfirmWording(name: string, restart: boolean): PresetConfirmWording;
|
|
44
|
+
/** The fixed option rows for one wording. */
|
|
45
|
+
export declare function presetConfirmOptions(wording: PresetConfirmWording): ReadonlyArray<{
|
|
46
|
+
id: 'fork' | 'fresh' | 'cancel';
|
|
47
|
+
text: string;
|
|
48
|
+
}>;
|
|
49
|
+
/** Accent-BOLD dialog title. */
|
|
50
|
+
export declare function presetConfirmTitle(wording: PresetConfirmWording): string;
|
|
51
|
+
/**
|
|
52
|
+
* The fixed body points, one entry per bullet — each wrapped to the terminal
|
|
53
|
+
* width by the panel before painting.
|
|
54
|
+
*/
|
|
55
|
+
export declare function presetConfirmBody(wording: PresetConfirmWording): readonly string[];
|
|
56
|
+
/** Footer hint — hardcoded like every other panel footer (English-only). */
|
|
57
|
+
export declare const PRESET_CONFIRM_FOOTER = "\u2191\u2193 select \u00B7 1/2/3 pick \u00B7 Enter confirm \u00B7 Esc cancel";
|
|
58
|
+
/** Pure dialog state: which row is highlighted, and the terminal outcome. */
|
|
59
|
+
export interface PresetConfirmState {
|
|
60
|
+
selected: number;
|
|
61
|
+
/**
|
|
62
|
+
* Set once by a terminal key: `'confirm'` (Enter on a selection) or
|
|
63
|
+
* `'cancel'` (Esc). Further input is ignored afterwards.
|
|
64
|
+
*/
|
|
65
|
+
settled?: 'confirm' | 'cancel';
|
|
66
|
+
}
|
|
67
|
+
export declare function initialPresetConfirmState(): PresetConfirmState;
|
|
68
|
+
/**
|
|
69
|
+
* Apply one raw key sequence to the dialog state. Unknown keys are no-ops;
|
|
70
|
+
* anything after a settle is ignored (single terminal outcome guard).
|
|
71
|
+
*/
|
|
72
|
+
export declare function updatePresetConfirm(state: PresetConfirmState, data: string): PresetConfirmState;
|
|
73
|
+
/** Resolved dialog outcome: the chosen action, or undefined on cancel. */
|
|
74
|
+
export declare function presetConfirmOutcome(state: PresetConfirmState): 'fork' | 'fresh' | 'cancel' | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* The framed overlay component. Renders the title, the wrapped body points
|
|
77
|
+
* and the three option rows; every key goes through
|
|
78
|
+
* {@link updatePresetConfirm}, and the first terminal key fires `onFinish`
|
|
79
|
+
* exactly once.
|
|
80
|
+
*/
|
|
81
|
+
export declare class PresetConfirmPanel implements Component {
|
|
82
|
+
private readonly theme;
|
|
83
|
+
private readonly wording;
|
|
84
|
+
private readonly onFinish;
|
|
85
|
+
private readonly requestRenderFn;
|
|
86
|
+
private state;
|
|
87
|
+
constructor(theme: TuiTheme, wording: PresetConfirmWording, onFinish: (outcome: 'fork' | 'fresh' | 'cancel' | undefined) => void, requestRender: () => void);
|
|
88
|
+
invalidate(): void;
|
|
89
|
+
render(width: number): string[];
|
|
90
|
+
handleInput(data: string): void;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Open the preset-switch confirmation dialog for one target preset.
|
|
94
|
+
* Resolves the chosen action (`'fork'` / `'fresh'`) or `'cancelled'` (Esc,
|
|
95
|
+
* or an overlay that failed to mount — treated as cancel so a half-mounted
|
|
96
|
+
* dialog can never imply consent). Closing always hands focus back through
|
|
97
|
+
* `restoreFocus` before the promise settles.
|
|
98
|
+
*/
|
|
99
|
+
export declare function openPresetConfirmDialog(tui: TUI, theme: TuiTheme, presetName: string, restart: boolean, restoreFocus: () => void): Promise<'fork' | 'fresh' | 'cancelled'>;
|
|
100
|
+
/** What one switch attempt ended as (the command echo rides on `message`). */
|
|
101
|
+
export interface PresetSwitchOutcome {
|
|
102
|
+
/** True when the selection was committed and the session restarted. */
|
|
103
|
+
switched: boolean;
|
|
104
|
+
/** The user-facing echo line (English-only). */
|
|
105
|
+
message: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The live seams the switch needs — structural so tests can pass fakes; the
|
|
109
|
+
* index.ts wiring assigns the real closures once.
|
|
110
|
+
*/
|
|
111
|
+
export interface PresetSwitchDeps {
|
|
112
|
+
/** Whether a live session exists right now (drives the confirmation gate). */
|
|
113
|
+
hasLiveSession(): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Show the confirmation dialog; resolves the user's choice. `restart` is
|
|
116
|
+
* true when the target equals the current selection (session restart, not
|
|
117
|
+
* a switch) — the dialog wording says so.
|
|
118
|
+
*/
|
|
119
|
+
confirmSwitch(presetName: string, restart: boolean): Promise<'fork' | 'fresh' | 'cancel'>;
|
|
120
|
+
/**
|
|
121
|
+
* Fresh start: record the selection (`bridge.setAgentPreset`) and start a
|
|
122
|
+
* new EMPTY session on it (the /new detach path), then refresh the footer.
|
|
123
|
+
*/
|
|
124
|
+
commit(presetId: string): Promise<void> | void;
|
|
125
|
+
/**
|
|
126
|
+
* Fork & switch: record the selection and start the new session on it
|
|
127
|
+
* SEEDED with the current conversation (snapshot → `agents.create` with
|
|
128
|
+
* seed + fork lineage → bridge rebind → transcript replay), then refresh
|
|
129
|
+
* the footer.
|
|
130
|
+
*/
|
|
131
|
+
forkCommit(presetId: string): Promise<void> | void;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* One preset switch, from any entry path (picker Enter, `/preset <name>`,
|
|
135
|
+
* `/preset next`): with a live session the confirmation dialog gates the
|
|
136
|
+
* switch — cancelling changes NOTHING (selection index untouched, no
|
|
137
|
+
* detach); `fresh` commits an empty new session (the /new path); `fork`
|
|
138
|
+
* commits a new session seeded with the current conversation. Without a
|
|
139
|
+
* live session the selection applies directly (the first submit already
|
|
140
|
+
* creates the session on it). Pure with respect to `state` apart from the
|
|
141
|
+
* commit-index write, and fully injectable.
|
|
142
|
+
*/
|
|
143
|
+
export declare function performPresetSwitch(state: PresetState, target: PresetEntry, deps: PresetSwitchDeps): Promise<PresetSwitchOutcome>;
|
|
144
|
+
/**
|
|
145
|
+
* The balanced completed-turn prefix of a session snapshot: every event up
|
|
146
|
+
* to and including the last `turn/end` — the in-flight turn is unbalanced
|
|
147
|
+
* and cannot replay as a valid seed. Contiguous from seq 0 (live sequence
|
|
148
|
+
* numbers equal array indexes) and empty before any completed turn, in which
|
|
149
|
+
* case a fork degrades to a fresh session. The same slice the host's fork
|
|
150
|
+
* subagent backend (`dsh-subagent-fork-in-process`) takes.
|
|
151
|
+
*/
|
|
152
|
+
export declare function completedTurnSeed(events: readonly SessionEvent[]): readonly SessionEvent[];
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preset-switch confirmation dialog + the switch orchestration.
|
|
3
|
+
*
|
|
4
|
+
* Switching presets is an EXPLICIT action with a visible cost: the preset is
|
|
5
|
+
* baked into a session at creation time, so applying a new selection means
|
|
6
|
+
* starting a NEW session — the current one is detached (and stays resumable
|
|
7
|
+
* via /resume). Whenever a live session exists, the switch therefore goes
|
|
8
|
+
* through this confirmation gate first (mirroring the /resume repair
|
|
9
|
+
* dialog's pure-reducer split, src/repair-dialog.ts, so the decision matrix
|
|
10
|
+
* stays unit-testable without a terminal); on a fresh TUI without a session
|
|
11
|
+
* there is nothing to leave behind and the selection applies directly.
|
|
12
|
+
*
|
|
13
|
+
* The dialog offers exactly three ways out: FORK the current conversation
|
|
14
|
+
* into the new session on the preset (context weight included), start FRESH
|
|
15
|
+
* (an empty session on the preset), or CANCEL. Title/body wording
|
|
16
|
+
* distinguishes a switch to a different preset from a restart on the
|
|
17
|
+
* current one. ↑↓ moves the selection, `1`/`2`/`3` select directly, Enter
|
|
18
|
+
* confirms, Esc = cancel. Framing/focus follow the shared overlay contract:
|
|
19
|
+
* PanelHost framing, close re-focuses the CURRENT editor instance through
|
|
20
|
+
* `restoreFocus`.
|
|
21
|
+
*/
|
|
22
|
+
import { getKeybindings } from '@earendil-works/pi-tui';
|
|
23
|
+
import { PanelHost, panelThemeFns } from "./panels.js";
|
|
24
|
+
import { BOLD, RESET, ansiFg } from "./theme/index.js";
|
|
25
|
+
import { clipToWidth, wrapText } from "./text.js";
|
|
26
|
+
/** The three choices in display order; index 0 is preselected. */
|
|
27
|
+
export const PRESET_CONFIRM_OPTION_IDS = ['fork', 'fresh', 'cancel'];
|
|
28
|
+
/** The dialog wording for `name`: `restart` marks a re-selection of the current preset. */
|
|
29
|
+
export function presetConfirmWording(name, restart) {
|
|
30
|
+
return restart
|
|
31
|
+
? {
|
|
32
|
+
title: `● Restart session on ${name}?`,
|
|
33
|
+
fork: `Fork & restart — new session on ${name}, carrying this conversation`,
|
|
34
|
+
fresh: `Restart now — new empty session on ${name}`,
|
|
35
|
+
firstPoint: `Restarting starts a NEW session on ${name}.`,
|
|
36
|
+
}
|
|
37
|
+
: {
|
|
38
|
+
title: `● Switch preset to ${name}?`,
|
|
39
|
+
fork: `Fork & switch — new session on ${name}, carrying this conversation`,
|
|
40
|
+
fresh: `Fresh start — new empty session on ${name}`,
|
|
41
|
+
firstPoint: `Switching starts a NEW session on ${name}.`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** The fixed option rows for one wording. */
|
|
45
|
+
export function presetConfirmOptions(wording) {
|
|
46
|
+
return [
|
|
47
|
+
{ id: 'fork', text: wording.fork },
|
|
48
|
+
{ id: 'fresh', text: wording.fresh },
|
|
49
|
+
{ id: 'cancel', text: 'Cancel — stay on the current session' },
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
/** Accent-BOLD dialog title. */
|
|
53
|
+
export function presetConfirmTitle(wording) {
|
|
54
|
+
return wording.title;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The fixed body points, one entry per bullet — each wrapped to the terminal
|
|
58
|
+
* width by the panel before painting.
|
|
59
|
+
*/
|
|
60
|
+
export function presetConfirmBody(wording) {
|
|
61
|
+
return [
|
|
62
|
+
wording.firstPoint,
|
|
63
|
+
'Fork carries this conversation into the new session (compacted context included) — fresh starts empty.',
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
/** Footer hint — hardcoded like every other panel footer (English-only). */
|
|
67
|
+
export const PRESET_CONFIRM_FOOTER = '↑↓ select · 1/2/3 pick · Enter confirm · Esc cancel';
|
|
68
|
+
export function initialPresetConfirmState() {
|
|
69
|
+
return { selected: 0 };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Apply one raw key sequence to the dialog state. Unknown keys are no-ops;
|
|
73
|
+
* anything after a settle is ignored (single terminal outcome guard).
|
|
74
|
+
*/
|
|
75
|
+
export function updatePresetConfirm(state, data) {
|
|
76
|
+
if (state.settled !== undefined)
|
|
77
|
+
return state;
|
|
78
|
+
const kb = getKeybindings();
|
|
79
|
+
if (kb.matches(data, 'tui.select.cancel'))
|
|
80
|
+
return { ...state, settled: 'cancel' };
|
|
81
|
+
if (kb.matches(data, 'tui.input.submit'))
|
|
82
|
+
return { ...state, settled: 'confirm' };
|
|
83
|
+
if (kb.matches(data, 'tui.select.up')) {
|
|
84
|
+
return { ...state, selected: Math.max(0, state.selected - 1) };
|
|
85
|
+
}
|
|
86
|
+
if (kb.matches(data, 'tui.select.down')) {
|
|
87
|
+
return { ...state, selected: Math.min(PRESET_CONFIRM_OPTION_IDS.length - 1, state.selected + 1) };
|
|
88
|
+
}
|
|
89
|
+
// Digit direct-select (1-based): selects the row, Enter still confirms —
|
|
90
|
+
// same select-then-confirm split as the routing dialog.
|
|
91
|
+
const digit = /^([1-9])$/.exec(data);
|
|
92
|
+
if (digit !== null) {
|
|
93
|
+
const index = Number(digit[1]) - 1;
|
|
94
|
+
if (index < PRESET_CONFIRM_OPTION_IDS.length)
|
|
95
|
+
return { ...state, selected: index };
|
|
96
|
+
}
|
|
97
|
+
return state;
|
|
98
|
+
}
|
|
99
|
+
/** Resolved dialog outcome: the chosen action, or undefined on cancel. */
|
|
100
|
+
export function presetConfirmOutcome(state) {
|
|
101
|
+
if (state.settled !== 'confirm')
|
|
102
|
+
return undefined;
|
|
103
|
+
return PRESET_CONFIRM_OPTION_IDS[state.selected];
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The framed overlay component. Renders the title, the wrapped body points
|
|
107
|
+
* and the three option rows; every key goes through
|
|
108
|
+
* {@link updatePresetConfirm}, and the first terminal key fires `onFinish`
|
|
109
|
+
* exactly once.
|
|
110
|
+
*/
|
|
111
|
+
export class PresetConfirmPanel {
|
|
112
|
+
theme;
|
|
113
|
+
wording;
|
|
114
|
+
onFinish;
|
|
115
|
+
requestRenderFn;
|
|
116
|
+
state = initialPresetConfirmState();
|
|
117
|
+
constructor(theme, wording, onFinish, requestRender) {
|
|
118
|
+
this.theme = theme;
|
|
119
|
+
this.wording = wording;
|
|
120
|
+
this.onFinish = onFinish;
|
|
121
|
+
this.requestRenderFn = requestRender;
|
|
122
|
+
}
|
|
123
|
+
invalidate() { }
|
|
124
|
+
render(width) {
|
|
125
|
+
const fns = panelThemeFns(this.theme);
|
|
126
|
+
const wrap = Math.max(2, width - 2);
|
|
127
|
+
// Word-wrap the body FIRST, then paint (iron rule: width math runs on
|
|
128
|
+
// plain text, ANSI goes on after clipping).
|
|
129
|
+
const lines = [
|
|
130
|
+
fns.accent(BOLD + clipToWidth(presetConfirmTitle(this.wording), wrap) + RESET),
|
|
131
|
+
...presetConfirmBody(this.wording).flatMap(point => wrapText(point, wrap).map(segment => fns.muted(clipToWidth(segment, wrap)))),
|
|
132
|
+
'',
|
|
133
|
+
];
|
|
134
|
+
const options = presetConfirmOptions(this.wording);
|
|
135
|
+
for (let i = 0; i < options.length; i++) {
|
|
136
|
+
const option = options[i];
|
|
137
|
+
const marker = i === this.state.selected ? '▸' : ' ';
|
|
138
|
+
const row = clipToWidth(`${marker} ${i + 1}. ${option.text}`, wrap);
|
|
139
|
+
lines.push(i === this.state.selected
|
|
140
|
+
? ansiFg(this.theme.palette.accent) + BOLD + row + RESET
|
|
141
|
+
: fns.muted(row));
|
|
142
|
+
}
|
|
143
|
+
lines.push('');
|
|
144
|
+
lines.push(fns.subtle(clipToWidth(PRESET_CONFIRM_FOOTER, wrap)));
|
|
145
|
+
return lines;
|
|
146
|
+
}
|
|
147
|
+
handleInput(data) {
|
|
148
|
+
const previous = this.state;
|
|
149
|
+
this.state = updatePresetConfirm(this.state, data);
|
|
150
|
+
if (this.state === previous)
|
|
151
|
+
return;
|
|
152
|
+
if (this.state.settled === undefined) {
|
|
153
|
+
this.requestRenderFn();
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
this.onFinish(presetConfirmOutcome(this.state));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Open the preset-switch confirmation dialog for one target preset.
|
|
161
|
+
* Resolves the chosen action (`'fork'` / `'fresh'`) or `'cancelled'` (Esc,
|
|
162
|
+
* or an overlay that failed to mount — treated as cancel so a half-mounted
|
|
163
|
+
* dialog can never imply consent). Closing always hands focus back through
|
|
164
|
+
* `restoreFocus` before the promise settles.
|
|
165
|
+
*/
|
|
166
|
+
export function openPresetConfirmDialog(tui, theme, presetName, restart, restoreFocus) {
|
|
167
|
+
return new Promise(resolve => {
|
|
168
|
+
let settled = false;
|
|
169
|
+
const settle = (outcome) => {
|
|
170
|
+
if (settled)
|
|
171
|
+
return;
|
|
172
|
+
settled = true;
|
|
173
|
+
resolve(outcome);
|
|
174
|
+
};
|
|
175
|
+
// A half-mounted overlay must not strand the keyboard: PanelHost's
|
|
176
|
+
// onError closes + calls restoreFocus, then we settle as cancelled.
|
|
177
|
+
const host = new PanelHost(tui, theme, () => {
|
|
178
|
+
restoreFocus();
|
|
179
|
+
settle('cancelled');
|
|
180
|
+
});
|
|
181
|
+
const finish = (outcome) => {
|
|
182
|
+
host.close();
|
|
183
|
+
restoreFocus();
|
|
184
|
+
settle(outcome === 'fork' || outcome === 'fresh' ? outcome : 'cancelled');
|
|
185
|
+
};
|
|
186
|
+
const panel = new PresetConfirmPanel(theme, presetConfirmWording(presetName, restart), outcome => finish(outcome), () => tui.requestRender());
|
|
187
|
+
// maxHeight is a hard slice in pi-tui: title + 2 wrapped body points +
|
|
188
|
+
// 3 options + footer ≈ 9 content rows + 4 frame rows; 75% of the 24-row
|
|
189
|
+
// e2e floor is 18 (the /resume picker's headroom).
|
|
190
|
+
host.open(panel, '70%', '75%');
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* One preset switch, from any entry path (picker Enter, `/preset <name>`,
|
|
195
|
+
* `/preset next`): with a live session the confirmation dialog gates the
|
|
196
|
+
* switch — cancelling changes NOTHING (selection index untouched, no
|
|
197
|
+
* detach); `fresh` commits an empty new session (the /new path); `fork`
|
|
198
|
+
* commits a new session seeded with the current conversation. Without a
|
|
199
|
+
* live session the selection applies directly (the first submit already
|
|
200
|
+
* creates the session on it). Pure with respect to `state` apart from the
|
|
201
|
+
* commit-index write, and fully injectable.
|
|
202
|
+
*/
|
|
203
|
+
export async function performPresetSwitch(state, target, deps) {
|
|
204
|
+
if (deps.hasLiveSession()) {
|
|
205
|
+
const restart = state.roster[state.index]?.id === target.id;
|
|
206
|
+
const action = await deps.confirmSwitch(target.name, restart);
|
|
207
|
+
if (action === 'cancel') {
|
|
208
|
+
return { switched: false, message: 'Preset unchanged — still on the current session.' };
|
|
209
|
+
}
|
|
210
|
+
// Snapshot BEFORE the index write: a commit that throws must not leave
|
|
211
|
+
// the selection (and with it the footer) advertising a preset the live
|
|
212
|
+
// session is not on.
|
|
213
|
+
const previousIndex = state.index;
|
|
214
|
+
const index = state.roster.findIndex(preset => preset.id === target.id);
|
|
215
|
+
try {
|
|
216
|
+
if (index >= 0)
|
|
217
|
+
state.index = index;
|
|
218
|
+
if (action === 'fork') {
|
|
219
|
+
await deps.forkCommit(target.id);
|
|
220
|
+
return { switched: true, message: `Preset → ${target.name} — new session forked from this conversation.` };
|
|
221
|
+
}
|
|
222
|
+
await deps.commit(target.id);
|
|
223
|
+
return { switched: true, message: `Preset → ${target.name} — new empty session started on it.` };
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
state.index = previousIndex;
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const index = state.roster.findIndex(preset => preset.id === target.id);
|
|
231
|
+
if (index >= 0)
|
|
232
|
+
state.index = index;
|
|
233
|
+
await deps.commit(target.id);
|
|
234
|
+
return { switched: true, message: `Preset → ${target.name} — new session started on it.` };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* The balanced completed-turn prefix of a session snapshot: every event up
|
|
238
|
+
* to and including the last `turn/end` — the in-flight turn is unbalanced
|
|
239
|
+
* and cannot replay as a valid seed. Contiguous from seq 0 (live sequence
|
|
240
|
+
* numbers equal array indexes) and empty before any completed turn, in which
|
|
241
|
+
* case a fork degrades to a fresh session. The same slice the host's fork
|
|
242
|
+
* subagent backend (`dsh-subagent-fork-in-process`) takes.
|
|
243
|
+
*/
|
|
244
|
+
export function completedTurnSeed(events) {
|
|
245
|
+
let lastEnd = -1;
|
|
246
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
247
|
+
if (events[i].type === 'turn/end') {
|
|
248
|
+
lastEnd = i;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return lastEnd < 0 ? [] : events.slice(0, lastEnd + 1);
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=preset-dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-dialog.js","sourceRoot":"","sources":["../src/preset-dialog.ts"],"names":[],"mappings":"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"}
|
package/lib/preset.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent-preset state manager — scans the filesystem for preset directories
|
|
3
3
|
* (same discovery logic as @deepseek-ai/dsh-agent-presets), tracks the user's
|
|
4
|
-
*
|
|
4
|
+
* /preset selection, and formats the footer label.
|
|
5
5
|
*
|
|
6
6
|
* Presets are a dsh deployment concept: each preset composes a session's agent
|
|
7
7
|
* from a different set of plugins/tools. The TUI scans the preset roots once
|
|
8
|
-
* at startup and lets the user
|
|
9
|
-
*
|
|
8
|
+
* at startup and lets the user switch through /preset; a switch is an explicit,
|
|
9
|
+
* confirmed action that starts a NEW session on the chosen preset (a fresh TUI
|
|
10
|
+
* without a live session applies the selection directly).
|
|
10
11
|
*
|
|
11
|
-
* The roster is O(1) to read (an in-memory array)
|
|
12
|
-
*
|
|
13
|
-
* scans the filesystem.
|
|
12
|
+
* The roster is O(1) to read (an in-memory array). The module is pure except
|
|
13
|
+
* for the async `fetchPresetRoster` which scans the filesystem.
|
|
14
14
|
*/
|
|
15
15
|
/** One preset entry from the deployment roster. */
|
|
16
16
|
export interface PresetEntry {
|
|
@@ -55,8 +55,15 @@ export declare function resolvePresetRoots(): PresetRoot[];
|
|
|
55
55
|
* presets are found.
|
|
56
56
|
*/
|
|
57
57
|
export declare function fetchPresetRoster(): Promise<PresetEntry[]>;
|
|
58
|
-
/**
|
|
59
|
-
|
|
58
|
+
/**
|
|
59
|
+
* The entry `/preset next` would switch to — the roster item after the
|
|
60
|
+
* current selection, wrapping around. PURE (no index mutation): the switch
|
|
61
|
+
* only commits through the confirmation flow, so `next` must be able to
|
|
62
|
+
* preview its target first. With 0 or 1 entries there is nothing to switch
|
|
63
|
+
* to (the caller short-circuits); with ≥2 the result always differs from
|
|
64
|
+
* the current selection.
|
|
65
|
+
*/
|
|
66
|
+
export declare function peekNextPreset(state: PresetState): PresetEntry | undefined;
|
|
60
67
|
/** Return the currently selected preset, or undefined when the roster is empty. */
|
|
61
68
|
export declare function currentPreset(state: PresetState): PresetEntry | undefined;
|
|
62
69
|
/**
|
|
@@ -67,7 +74,7 @@ export declare function currentPreset(state: PresetState): PresetEntry | undefin
|
|
|
67
74
|
export declare function findPresetByName(state: PresetState, name: string): PresetEntry | undefined;
|
|
68
75
|
/** The preset id selected out of the box when the roster supplies it. It
|
|
69
76
|
* mirrors the dsh shipped default, but there is no mechanical binding:
|
|
70
|
-
* until the user
|
|
77
|
+
* until the user switches via /preset, the server-side
|
|
71
78
|
* `agent-presets.default` setting still governs session creation. */
|
|
72
79
|
export declare const DEFAULT_PRESET_ID = "standard";
|
|
73
80
|
/**
|
package/lib/preset.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent-preset state manager — scans the filesystem for preset directories
|
|
3
3
|
* (same discovery logic as @deepseek-ai/dsh-agent-presets), tracks the user's
|
|
4
|
-
*
|
|
4
|
+
* /preset selection, and formats the footer label.
|
|
5
5
|
*
|
|
6
6
|
* Presets are a dsh deployment concept: each preset composes a session's agent
|
|
7
7
|
* from a different set of plugins/tools. The TUI scans the preset roots once
|
|
8
|
-
* at startup and lets the user
|
|
9
|
-
*
|
|
8
|
+
* at startup and lets the user switch through /preset; a switch is an explicit,
|
|
9
|
+
* confirmed action that starts a NEW session on the chosen preset (a fresh TUI
|
|
10
|
+
* without a live session applies the selection directly).
|
|
10
11
|
*
|
|
11
|
-
* The roster is O(1) to read (an in-memory array)
|
|
12
|
-
*
|
|
13
|
-
* scans the filesystem.
|
|
12
|
+
* The roster is O(1) to read (an in-memory array). The module is pure except
|
|
13
|
+
* for the async `fetchPresetRoster` which scans the filesystem.
|
|
14
14
|
*/
|
|
15
15
|
import { access, readdir, readFile } from 'node:fs/promises';
|
|
16
16
|
import { join, resolve } from 'node:path';
|
|
@@ -151,11 +151,18 @@ export async function fetchPresetRoster() {
|
|
|
151
151
|
}
|
|
152
152
|
return roster;
|
|
153
153
|
}
|
|
154
|
-
/**
|
|
155
|
-
|
|
154
|
+
/**
|
|
155
|
+
* The entry `/preset next` would switch to — the roster item after the
|
|
156
|
+
* current selection, wrapping around. PURE (no index mutation): the switch
|
|
157
|
+
* only commits through the confirmation flow, so `next` must be able to
|
|
158
|
+
* preview its target first. With 0 or 1 entries there is nothing to switch
|
|
159
|
+
* to (the caller short-circuits); with ≥2 the result always differs from
|
|
160
|
+
* the current selection.
|
|
161
|
+
*/
|
|
162
|
+
export function peekNextPreset(state) {
|
|
156
163
|
if (state.roster.length <= 1)
|
|
157
|
-
return;
|
|
158
|
-
state.
|
|
164
|
+
return undefined;
|
|
165
|
+
return state.roster[(state.index + 1) % state.roster.length];
|
|
159
166
|
}
|
|
160
167
|
/** Return the currently selected preset, or undefined when the roster is empty. */
|
|
161
168
|
export function currentPreset(state) {
|
|
@@ -174,7 +181,7 @@ export function findPresetByName(state, name) {
|
|
|
174
181
|
}
|
|
175
182
|
/** The preset id selected out of the box when the roster supplies it. It
|
|
176
183
|
* mirrors the dsh shipped default, but there is no mechanical binding:
|
|
177
|
-
* until the user
|
|
184
|
+
* until the user switches via /preset, the server-side
|
|
178
185
|
* `agent-presets.default` setting still governs session creation. */
|
|
179
186
|
export const DEFAULT_PRESET_ID = 'standard';
|
|
180
187
|
/**
|
package/lib/preset.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset.js","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"preset.js","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AA0B5C,sEAAsE;AACtE,IAAI,kBAA4C,CAAA;AAChD,MAAM,UAAU,uBAAuB,CAAC,KAA+B;IACrE,kBAAkB,GAAG,KAAK,CAAA;AAC5B,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,KAAK,GAAiB,EAAE,CAAA;IAC9B,oEAAoE;IACpE,kEAAkE;IAClE,2EAA2E;IAC3E,4CAA4C;IAC5C,MAAM,YAAY,GAAG;QACnB,qGAAqG;QACrG,kGAAkG;QAClG,uEAAuE;QACvE,oEAAoE;KACrE,CAAA;IACD,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC1C,CAAC;IACD,uCAAuC;IACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAA;IAC3D,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;IAC7C,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,oBAAoB,GAAqC;IAC7D,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,QAAQ;IAChB,GAAG,EAAE,KAAK;CACX,CAAA;AAED;;;;;GAKG;AACH,KAAK,UAAU,QAAQ,CAAC,IAAgB;IACtC,IAAI,OAAmC,CAAA;IACvC,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA,CAAC,2BAA2B;IACvC,CAAC;IACD,MAAM,OAAO,GAAkB,EAAE,CAAA;IACjC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAQ;QAClC,kFAAkF;QAClF,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACvC,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAA;QACrD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,eAAe,CAAC,CAAA;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ,CAAC,6BAA6B;QACxC,CAAC;QACD,gEAAgE;QAChE,uEAAuE;QACvE,sCAAsC;QACtC,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;QACrB,IAAI,WAA+B,CAAA;QACnC,IAAI,IAAY,CAAA;QAChB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,CAAA;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,GAAG,EAAE,CAAA;QACX,CAAC;QACD,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;YAC/C,IAAI,SAAS;gBAAE,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;YACzC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAA;YACtD,IAAI,SAAS;gBAAE,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAClD,CAAC;QACD,wEAAwE;QACxE,yCAAyC;QACzC,MAAM,YAAY,GAAG,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA;QAC3D,OAAO,CAAC,IAAI,CAAC;YACX,EAAE,EAAE,KAAK,CAAC,IAAI;YACd,IAAI,EAAE,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI;YAC9C,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,WAAW;YACX,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,KAAK,EAAE,kCAAkC;SACrD,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,KAAK,GAAG,kBAAkB,IAAI,kBAAkB,EAAE,CAAA;IACxD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,MAAM,MAAM,GAAkB,EAAE,CAAA;IAChC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,MAAM,IAAI,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAAE,SAAQ;YACjC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YACnB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IAC9C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;AAC9D,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,aAAa,CAAC,KAAkB;IAC9C,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAkB,EAAE,IAAY;IAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;IAChC,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAC3B,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK;WACzB,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK;WAC9B,CAAC,CAAC,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC,CAAA;AAChF,CAAC;AAED;;;sEAGsE;AACtE,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAA8B;IAC/D,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,iBAAiB,CAAC,CAAA;IAC3D,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAA8B;IAC9D,OAAO,KAAK,EAAE,IAAI,IAAI,EAAE,CAAA;AAC1B,CAAC"}
|
package/lib/session.d.ts
CHANGED
|
@@ -334,6 +334,37 @@ export declare class DshSessionBridge {
|
|
|
334
334
|
* point; switching away or exiting releases what we established.
|
|
335
335
|
*/
|
|
336
336
|
private ownWithLock;
|
|
337
|
+
/**
|
|
338
|
+
* Tear down the current live binding — the shared prelude of every session
|
|
339
|
+
* switch (resume, fork): stop the remote watch, dispose the handle, clear
|
|
340
|
+
* the subagent tracker and re-seed the model selection (a live /model or
|
|
341
|
+
* /think choice survives the switch; otherwise the composed default
|
|
342
|
+
* applies). The renderer keeps its live snapshot across the teardown (it
|
|
343
|
+
* must survive theme-switch rebuilds), so an empty `onLive` emit is
|
|
344
|
+
* required to drop the previous session's agents block before the next
|
|
345
|
+
* session's events rebuild it.
|
|
346
|
+
*/
|
|
347
|
+
private teardownLiveBinding;
|
|
348
|
+
/**
|
|
349
|
+
* Fork-and-switch: create a NEW session seeded with a balanced
|
|
350
|
+
* completed-turn prefix of the current session's log, then rebind this
|
|
351
|
+
* bridge to it — the resume lifecycle (teardown, tracker clear, rebind)
|
|
352
|
+
* over `agents.create` instead of `agents.resume`. The new session carries
|
|
353
|
+
* durable fork lineage (`parentSession` + `isSeeded` + the inherited
|
|
354
|
+
* prefix length) and composes under `presetId` (`meta.agentPreset`) — a
|
|
355
|
+
* seeded USER session, deliberately NOT a subagent child (no
|
|
356
|
+
* `origin`/delegationDepth, so /resume keeps it and the tracker ignores
|
|
357
|
+
* it). The seed must be sliced from the live session BEFORE calling this —
|
|
358
|
+
* the in-memory snapshot is the stable source. The teardown runs FIRST:
|
|
359
|
+
* `markTuiSurface` provides a cordis service on the agent scope and two
|
|
360
|
+
* live agents cannot coexist on it (the /new cycle only works because
|
|
361
|
+
* dispose reclaims the service before the next create). A failed create
|
|
362
|
+
* therefore leaves the old session DETACHED but fully resumable.
|
|
363
|
+
* @param seed contiguous-from-0 events ending at a `turn/end` (non-empty).
|
|
364
|
+
* @param parentSessionId the session the seed came from (durable lineage).
|
|
365
|
+
* @param presetId the preset id the new session composes under.
|
|
366
|
+
*/
|
|
367
|
+
forkCurrentSession(seed: readonly SessionEvent[], parentSessionId: SessionId, presetId: string | undefined): Promise<AgentHandle>;
|
|
337
368
|
/**
|
|
338
369
|
* Resume a persisted session: tear down the live agent (disposers are kept —
|
|
339
370
|
* the session-id filter re-binds to the resumed id) and load the persisted
|
|
@@ -397,6 +428,16 @@ export declare class DshSessionBridge {
|
|
|
397
428
|
/** The composed global default (`agent-default-model`) as a plain selection. */
|
|
398
429
|
private composedDefaultSelection;
|
|
399
430
|
/** Create the agent with the composed default model selection. */
|
|
431
|
+
/**
|
|
432
|
+
* Create the agent with the composed default model selection. `fork`
|
|
433
|
+
* seeds the new session with a balanced completed-turn prefix of another
|
|
434
|
+
* session's log (the preset-switch "Fork & switch" path): durable lineage
|
|
435
|
+
* (`parentSession` + `isSeeded` + the inherited prefix length) plus the
|
|
436
|
+
* fork's own `agentPreset` — recording the preset is what keeps the
|
|
437
|
+
* session's history reconstructable (a cold read that dropped it would
|
|
438
|
+
* rebuild turns under the wrong composition, the same rule
|
|
439
|
+
* `childSessionMeta` documents for subagent children).
|
|
440
|
+
*/
|
|
400
441
|
private createSession;
|
|
401
442
|
/** The model selection shown in the footer (live value after `/model`). */
|
|
402
443
|
getSelection(): ModelSelection | undefined;
|