@aiwayds/dsh-tui-pi 2.6.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 +1 -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 +10 -0
- package/lib/history-turns.js +16 -0
- package/lib/history-turns.js.map +1 -1
- package/lib/history.d.ts +29 -0
- package/lib/history.js +50 -2
- package/lib/history.js.map +1 -1
- package/lib/hotkeys.d.ts +1 -1
- package/lib/hotkeys.js +2 -3
- package/lib/hotkeys.js.map +1 -1
- package/lib/index.js +182 -50
- 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/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/tui.js +1 -1
- package/lib/tui.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ https://github.com/user-attachments/assets/6a7e00bb-1fd0-4bc5-9070-457f1e9fa54d
|
|
|
22
22
|
- [**Dynamic context pruning (DCP)**](docs/features/dcp.md) — context stays within limits automatically, with zero LLM calls.
|
|
23
23
|
- [**Persistent context**](docs/features/persistent-context.md) — your ground rules ride along on every request, hot-applied with no restart.
|
|
24
24
|
- [**Model profiles & favorites**](docs/features/model-profiles.md) — switch a whole model setup per project and keep the picker small.
|
|
25
|
-
- [**Agent preset switching**](docs/features/preset-switch.md) —
|
|
25
|
+
- [**Agent preset switching**](docs/features/preset-switch.md) — `/preset` between the shipped agent compositions (`standard`, `minimal`, …); a switch is confirmed and starts a NEW session on the preset (the current one stays resumable); what a preset really gates.
|
|
26
26
|
- [**Sessions & resume**](docs/features/sessions-resume.md) — sessions stay tidy automatically and resume in a few keystrokes; a cross-process writer guard keeps the log single-writer.
|
|
27
27
|
- [**History browser**](docs/features/history.md) — `/history` opens a fixed two-pane look-back over the session: completed turns on the left, the selected turn's replies on the right; copy a prompt back to the editor, or cold-read any stored session without resuming it (read-only, no writer lock).
|
|
28
28
|
- [**Themes**](docs/features/themes.md) — GitHub light/dark palettes, hot-switchable; `auto` follows your terminal.
|
|
@@ -85,7 +85,6 @@ dsh plugin --profile tui add @aiwayds/dsh-topics-memory
|
|
|
85
85
|
| `Ctrl+G` | Open the subagent picker (viewer `Enter` opens steer) |
|
|
86
86
|
| `Ctrl+O` | Pending-message queue (s steer now · d remove) |
|
|
87
87
|
| `Ctrl+Shift+F` | Transcript search (`Enter`/`Ctrl+G` next · `Shift+Enter`/`Ctrl+Shift+G` previous · `Esc` close) |
|
|
88
|
-
| `Tab` | Cycle agent presets |
|
|
89
88
|
| `↑` / `↓` | Browse submitted-message history |
|
|
90
89
|
|
|
91
90
|
Remap any app key through `~/.dsh/keybindings.json` (a partial JSON map, live-applied) or interactively with `/hotkeys`.
|
package/README.zh-CN.md
CHANGED
|
@@ -22,7 +22,7 @@ https://github.com/user-attachments/assets/6a7e00bb-1fd0-4bc5-9070-457f1e9fa54d
|
|
|
22
22
|
- [**动态上下文修剪(DCP)**](docs/features/dcp.md) —— 上下文自动保持在窗口内,零 LLM 调用。
|
|
23
23
|
- [**持久上下文**](docs/features/persistent-context.md) —— 你的基本规则随每个请求生效,热应用无需重启。
|
|
24
24
|
- [**模型 profile 与收藏**](docs/features/model-profiles.md) —— 按项目切换整套模型配置,选择器保持精简。
|
|
25
|
-
- [**Agent preset 切换**](docs/features/preset-switch.md) ——
|
|
25
|
+
- [**Agent preset 切换**](docs/features/preset-switch.md) —— `/preset` 在内置 agent 组合(`standard`、`minimal`……)间切换;切换需确认并会开启新会话(当前会话仍可 /resume 恢复);preset 到底管什么。
|
|
26
26
|
- [**Sessions 会话与恢复**](docs/features/sessions-resume.md) —— 会话自动保持整洁、几次按键恢复;跨进程写者守卫保证日志单写者。
|
|
27
27
|
- [**Themes 主题**](docs/features/themes.md) —— GitHub 明/暗配色热切换;`auto` 跟随终端。
|
|
28
28
|
- [**搜索、选择与图片**](docs/features/search-selection-images.md) —— `Ctrl+Shift+F` 全文搜索、划选复制到系统剪贴板、web/飞书附件内联渲染、LaTeX 转 Unicode 数学。
|
|
@@ -82,7 +82,6 @@ dsh plugin --profile tui add @aiwayds/dsh-topics-memory
|
|
|
82
82
|
| `Ctrl+G` | 打开 subagent 选择器(查看器内 `Enter` 打开 steer) |
|
|
83
83
|
| `Ctrl+O` | 待发消息队列(`s` 立即 steer · `d` 移除) |
|
|
84
84
|
| `Ctrl+Shift+F` | 全文搜索(`Enter`/`Ctrl+G` 下一个 · `Shift+Enter`/`Ctrl+Shift+G` 上一个 · `Esc` 关闭) |
|
|
85
|
-
| `Tab` | 循环切换 agent preset |
|
|
86
85
|
| `↑` / `↓` | 浏览已提交消息历史 |
|
|
87
86
|
|
|
88
87
|
通过 `~/.dsh/keybindings.json`(部分 JSON 映射,实时应用)或 `/hotkeys` 交互式重映射任意 app 按键。
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fork-at-turn confirmation dialog — the /history browser's `f` key.
|
|
3
|
+
*
|
|
4
|
+
* Forking at a turn starts a NEW session seeded with the browsed session's
|
|
5
|
+
* events through the selected turn's `turn/end` (inclusive) and switches to
|
|
6
|
+
* it; the current session stays untouched and resumable via /resume. That is
|
|
7
|
+
* a user-visible, hard-to-reverse-looking action, so it is gated behind this
|
|
8
|
+
* confirmation (mirroring the /resume repair dialog's pure-reducer split,
|
|
9
|
+
* src/repair-dialog.ts, so the decision matrix stays unit-testable without a
|
|
10
|
+
* terminal). Two options — Fork now / Cancel — Esc = cancel.
|
|
11
|
+
*
|
|
12
|
+
* The dialog mounts as its own overlay ON TOP of the open browser (the
|
|
13
|
+
* browser stays mounted underneath and keeps the keyboard after the dialog
|
|
14
|
+
* resolves; the switch itself runs after confirmation).
|
|
15
|
+
*/
|
|
16
|
+
import { type Component, type TUI } from '@earendil-works/pi-tui';
|
|
17
|
+
import { type TuiTheme } from './theme/index.ts';
|
|
18
|
+
/** The two choices in display order; index 0 is preselected. */
|
|
19
|
+
export declare const FORK_AT_TURN_OPTION_IDS: ReadonlyArray<'fork' | 'cancel'>;
|
|
20
|
+
/** Accent-BOLD dialog title (`N` = the selected turn's number). */
|
|
21
|
+
export declare function forkAtTurnTitle(turnLabel: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* The fixed body points, one entry per bullet — each wrapped to the terminal
|
|
24
|
+
* width by the panel before painting. `N` is the selected turn's number,
|
|
25
|
+
* `M` the session's total turn count (as listed). `detachedLiveId` is set
|
|
26
|
+
* when the browsed session is NOT the live one (a cold read): forking then
|
|
27
|
+
* detaches the LIVE session, and the body must say so — "the current
|
|
28
|
+
* session" alone would read as the browsed one and hide the live session's
|
|
29
|
+
* fate. Undefined = live browse (or nothing live at all), where the generic
|
|
30
|
+
* wording is exact.
|
|
31
|
+
*/
|
|
32
|
+
export declare function forkAtTurnBody(turnLabel: string, totalTurns: number, detachedLiveId?: string): readonly string[];
|
|
33
|
+
/** The fixed option rows for one target turn. */
|
|
34
|
+
export declare function forkAtTurnOptions(turnLabel: string): ReadonlyArray<{
|
|
35
|
+
id: 'fork' | 'cancel';
|
|
36
|
+
text: string;
|
|
37
|
+
}>;
|
|
38
|
+
/** Footer hint — hardcoded like every other panel footer (English-only). */
|
|
39
|
+
export declare const FORK_AT_TURN_FOOTER = "\u2191\u2193 select \u00B7 1/2 pick \u00B7 Enter confirm \u00B7 Esc cancel";
|
|
40
|
+
/** Pure dialog state: which row is highlighted, and the terminal outcome. */
|
|
41
|
+
export interface ForkAtTurnState {
|
|
42
|
+
selected: number;
|
|
43
|
+
/**
|
|
44
|
+
* Set once by a terminal key: `'confirm'` (Enter on a selection) or
|
|
45
|
+
* `'cancel'` (Esc). Further input is ignored afterwards.
|
|
46
|
+
*/
|
|
47
|
+
settled?: 'confirm' | 'cancel';
|
|
48
|
+
}
|
|
49
|
+
export declare function initialForkAtTurnState(): ForkAtTurnState;
|
|
50
|
+
/**
|
|
51
|
+
* Apply one raw key sequence to the dialog state. Unknown keys are no-ops;
|
|
52
|
+
* anything after a settle is ignored (single terminal outcome guard).
|
|
53
|
+
*/
|
|
54
|
+
export declare function updateForkAtTurn(state: ForkAtTurnState, data: string): ForkAtTurnState;
|
|
55
|
+
/** Resolved dialog outcome: the chosen action, or undefined on cancel. */
|
|
56
|
+
export declare function forkAtTurnOutcome(state: ForkAtTurnState): 'fork' | 'cancel' | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* The framed overlay component. Renders the title, the wrapped body points
|
|
59
|
+
* and the two option rows; every key goes through {@link updateForkAtTurn},
|
|
60
|
+
* and the first terminal key fires `onFinish` exactly once.
|
|
61
|
+
*/
|
|
62
|
+
export declare class ForkAtTurnPanel implements Component {
|
|
63
|
+
private readonly theme;
|
|
64
|
+
private readonly turnLabel;
|
|
65
|
+
private readonly totalTurns;
|
|
66
|
+
private readonly detachedLiveId;
|
|
67
|
+
private readonly onFinish;
|
|
68
|
+
private readonly requestRenderFn;
|
|
69
|
+
private state;
|
|
70
|
+
constructor(theme: TuiTheme, turnLabel: string, totalTurns: number, detachedLiveId: string | undefined, onFinish: (outcome: 'fork' | 'cancel' | undefined) => void, requestRender: () => void);
|
|
71
|
+
invalidate(): void;
|
|
72
|
+
render(width: number): string[];
|
|
73
|
+
handleInput(data: string): void;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Open the fork-at-turn confirmation dialog over the OPEN browser (its own
|
|
77
|
+
* overlay — the browser stays mounted underneath and regains the keyboard
|
|
78
|
+
* when the dialog resolves). Resolves `'fork'` when the user explicitly
|
|
79
|
+
* confirmed, `'cancelled'` otherwise (Esc, or an overlay that failed to
|
|
80
|
+
* mount — treated as cancel so a half-mounted dialog can never imply
|
|
81
|
+
* consent). Closing hands focus back through `restoreFocus` first.
|
|
82
|
+
*/
|
|
83
|
+
export declare function openForkAtTurnDialog(tui: TUI, theme: TuiTheme, turnLabel: string, totalTurns: number, detachedLiveId: string | undefined, restoreFocus: () => void): Promise<'fork' | 'cancelled'>;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fork-at-turn confirmation dialog — the /history browser's `f` key.
|
|
3
|
+
*
|
|
4
|
+
* Forking at a turn starts a NEW session seeded with the browsed session's
|
|
5
|
+
* events through the selected turn's `turn/end` (inclusive) and switches to
|
|
6
|
+
* it; the current session stays untouched and resumable via /resume. That is
|
|
7
|
+
* a user-visible, hard-to-reverse-looking action, so it is gated behind this
|
|
8
|
+
* confirmation (mirroring the /resume repair dialog's pure-reducer split,
|
|
9
|
+
* src/repair-dialog.ts, so the decision matrix stays unit-testable without a
|
|
10
|
+
* terminal). Two options — Fork now / Cancel — Esc = cancel.
|
|
11
|
+
*
|
|
12
|
+
* The dialog mounts as its own overlay ON TOP of the open browser (the
|
|
13
|
+
* browser stays mounted underneath and keeps the keyboard after the dialog
|
|
14
|
+
* resolves; the switch itself runs after confirmation).
|
|
15
|
+
*/
|
|
16
|
+
import { getKeybindings } from '@earendil-works/pi-tui';
|
|
17
|
+
import { PanelHost, panelThemeFns } from "./panels.js";
|
|
18
|
+
import { BOLD, RESET, ansiFg } from "./theme/index.js";
|
|
19
|
+
import { clipToWidth, wrapText } from "./text.js";
|
|
20
|
+
/** The two choices in display order; index 0 is preselected. */
|
|
21
|
+
export const FORK_AT_TURN_OPTION_IDS = ['fork', 'cancel'];
|
|
22
|
+
/** Accent-BOLD dialog title (`N` = the selected turn's number). */
|
|
23
|
+
export function forkAtTurnTitle(turnLabel) {
|
|
24
|
+
return `● Fork at turn ${turnLabel}?`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The fixed body points, one entry per bullet — each wrapped to the terminal
|
|
28
|
+
* width by the panel before painting. `N` is the selected turn's number,
|
|
29
|
+
* `M` the session's total turn count (as listed). `detachedLiveId` is set
|
|
30
|
+
* when the browsed session is NOT the live one (a cold read): forking then
|
|
31
|
+
* detaches the LIVE session, and the body must say so — "the current
|
|
32
|
+
* session" alone would read as the browsed one and hide the live session's
|
|
33
|
+
* fate. Undefined = live browse (or nothing live at all), where the generic
|
|
34
|
+
* wording is exact.
|
|
35
|
+
*/
|
|
36
|
+
export function forkAtTurnBody(turnLabel, totalTurns, detachedLiveId) {
|
|
37
|
+
return [
|
|
38
|
+
`The new session carries turns through ${turnLabel} (of ${totalTurns}); later turns stay in the current session.`,
|
|
39
|
+
detachedLiveId === undefined
|
|
40
|
+
? 'The current session stays resumable via /resume.'
|
|
41
|
+
: `Your live session ${detachedLiveId} will be detached — it stays resumable via /resume.`,
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
/** The fixed option rows for one target turn. */
|
|
45
|
+
export function forkAtTurnOptions(turnLabel) {
|
|
46
|
+
return [
|
|
47
|
+
{ id: 'fork', text: `Fork now — new session through turn ${turnLabel}` },
|
|
48
|
+
{ id: 'cancel', text: 'Cancel' },
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
/** Footer hint — hardcoded like every other panel footer (English-only). */
|
|
52
|
+
export const FORK_AT_TURN_FOOTER = '↑↓ select · 1/2 pick · Enter confirm · Esc cancel';
|
|
53
|
+
export function initialForkAtTurnState() {
|
|
54
|
+
return { selected: 0 };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Apply one raw key sequence to the dialog state. Unknown keys are no-ops;
|
|
58
|
+
* anything after a settle is ignored (single terminal outcome guard).
|
|
59
|
+
*/
|
|
60
|
+
export function updateForkAtTurn(state, data) {
|
|
61
|
+
if (state.settled !== undefined)
|
|
62
|
+
return state;
|
|
63
|
+
const kb = getKeybindings();
|
|
64
|
+
if (kb.matches(data, 'tui.select.cancel'))
|
|
65
|
+
return { ...state, settled: 'cancel' };
|
|
66
|
+
if (kb.matches(data, 'tui.input.submit'))
|
|
67
|
+
return { ...state, settled: 'confirm' };
|
|
68
|
+
if (kb.matches(data, 'tui.select.up')) {
|
|
69
|
+
return { ...state, selected: Math.max(0, state.selected - 1) };
|
|
70
|
+
}
|
|
71
|
+
if (kb.matches(data, 'tui.select.down')) {
|
|
72
|
+
return { ...state, selected: Math.min(FORK_AT_TURN_OPTION_IDS.length - 1, state.selected + 1) };
|
|
73
|
+
}
|
|
74
|
+
// Digit direct-select (1-based): selects the row, Enter still confirms —
|
|
75
|
+
// same select-then-confirm split as the routing dialog.
|
|
76
|
+
const digit = /^([1-9])$/.exec(data);
|
|
77
|
+
if (digit !== null) {
|
|
78
|
+
const index = Number(digit[1]) - 1;
|
|
79
|
+
if (index < FORK_AT_TURN_OPTION_IDS.length)
|
|
80
|
+
return { ...state, selected: index };
|
|
81
|
+
}
|
|
82
|
+
return state;
|
|
83
|
+
}
|
|
84
|
+
/** Resolved dialog outcome: the chosen action, or undefined on cancel. */
|
|
85
|
+
export function forkAtTurnOutcome(state) {
|
|
86
|
+
if (state.settled !== 'confirm')
|
|
87
|
+
return undefined;
|
|
88
|
+
return FORK_AT_TURN_OPTION_IDS[state.selected];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The framed overlay component. Renders the title, the wrapped body points
|
|
92
|
+
* and the two option rows; every key goes through {@link updateForkAtTurn},
|
|
93
|
+
* and the first terminal key fires `onFinish` exactly once.
|
|
94
|
+
*/
|
|
95
|
+
export class ForkAtTurnPanel {
|
|
96
|
+
theme;
|
|
97
|
+
turnLabel;
|
|
98
|
+
totalTurns;
|
|
99
|
+
detachedLiveId;
|
|
100
|
+
onFinish;
|
|
101
|
+
requestRenderFn;
|
|
102
|
+
state = initialForkAtTurnState();
|
|
103
|
+
constructor(theme, turnLabel, totalTurns, detachedLiveId, onFinish, requestRender) {
|
|
104
|
+
this.theme = theme;
|
|
105
|
+
this.turnLabel = turnLabel;
|
|
106
|
+
this.totalTurns = totalTurns;
|
|
107
|
+
this.detachedLiveId = detachedLiveId;
|
|
108
|
+
this.onFinish = onFinish;
|
|
109
|
+
this.requestRenderFn = requestRender;
|
|
110
|
+
}
|
|
111
|
+
invalidate() { }
|
|
112
|
+
render(width) {
|
|
113
|
+
const fns = panelThemeFns(this.theme);
|
|
114
|
+
const wrap = Math.max(2, width - 2);
|
|
115
|
+
// Word-wrap the body FIRST, then paint (iron rule: width math runs on
|
|
116
|
+
// plain text, ANSI goes on after clipping).
|
|
117
|
+
const lines = [
|
|
118
|
+
fns.accent(BOLD + clipToWidth(forkAtTurnTitle(this.turnLabel), wrap) + RESET),
|
|
119
|
+
...forkAtTurnBody(this.turnLabel, this.totalTurns, this.detachedLiveId).flatMap(point => wrapText(point, wrap).map(segment => fns.muted(clipToWidth(segment, wrap)))),
|
|
120
|
+
'',
|
|
121
|
+
];
|
|
122
|
+
const options = forkAtTurnOptions(this.turnLabel);
|
|
123
|
+
for (let i = 0; i < options.length; i++) {
|
|
124
|
+
const option = options[i];
|
|
125
|
+
const marker = i === this.state.selected ? '▸' : ' ';
|
|
126
|
+
const row = clipToWidth(`${marker} ${i + 1}. ${option.text}`, wrap);
|
|
127
|
+
lines.push(i === this.state.selected
|
|
128
|
+
? ansiFg(this.theme.palette.accent) + BOLD + row + RESET
|
|
129
|
+
: fns.muted(row));
|
|
130
|
+
}
|
|
131
|
+
lines.push('');
|
|
132
|
+
lines.push(fns.subtle(clipToWidth(FORK_AT_TURN_FOOTER, wrap)));
|
|
133
|
+
return lines;
|
|
134
|
+
}
|
|
135
|
+
handleInput(data) {
|
|
136
|
+
const previous = this.state;
|
|
137
|
+
this.state = updateForkAtTurn(this.state, data);
|
|
138
|
+
if (this.state === previous)
|
|
139
|
+
return;
|
|
140
|
+
if (this.state.settled === undefined) {
|
|
141
|
+
this.requestRenderFn();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
this.onFinish(forkAtTurnOutcome(this.state));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Open the fork-at-turn confirmation dialog over the OPEN browser (its own
|
|
149
|
+
* overlay — the browser stays mounted underneath and regains the keyboard
|
|
150
|
+
* when the dialog resolves). Resolves `'fork'` when the user explicitly
|
|
151
|
+
* confirmed, `'cancelled'` otherwise (Esc, or an overlay that failed to
|
|
152
|
+
* mount — treated as cancel so a half-mounted dialog can never imply
|
|
153
|
+
* consent). Closing hands focus back through `restoreFocus` first.
|
|
154
|
+
*/
|
|
155
|
+
export function openForkAtTurnDialog(tui, theme, turnLabel, totalTurns, detachedLiveId, restoreFocus) {
|
|
156
|
+
return new Promise(resolve => {
|
|
157
|
+
let settled = false;
|
|
158
|
+
const settle = (outcome) => {
|
|
159
|
+
if (settled)
|
|
160
|
+
return;
|
|
161
|
+
settled = true;
|
|
162
|
+
resolve(outcome);
|
|
163
|
+
};
|
|
164
|
+
// A half-mounted overlay must not strand the keyboard: PanelHost's
|
|
165
|
+
// onError closes + calls restoreFocus, then we settle as cancelled.
|
|
166
|
+
const host = new PanelHost(tui, theme, () => {
|
|
167
|
+
restoreFocus();
|
|
168
|
+
settle('cancelled');
|
|
169
|
+
});
|
|
170
|
+
const finish = (outcome) => {
|
|
171
|
+
host.close();
|
|
172
|
+
restoreFocus();
|
|
173
|
+
settle(outcome === 'fork' ? 'fork' : 'cancelled');
|
|
174
|
+
};
|
|
175
|
+
const panel = new ForkAtTurnPanel(theme, turnLabel, totalTurns, detachedLiveId, outcome => finish(outcome), () => tui.requestRender());
|
|
176
|
+
// maxHeight is a hard slice in pi-tui: title + 2 wrapped body points +
|
|
177
|
+
// 2 options + footer ≈ 8 content rows + 4 frame rows; 75% of the 24-row
|
|
178
|
+
// e2e floor is 18 (the /resume picker's headroom).
|
|
179
|
+
host.open(panel, '70%', '75%');
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=history-fork.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-fork.js","sourceRoot":"","sources":["../src/history-fork.ts"],"names":[],"mappings":"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"}
|
package/lib/history-turns.d.ts
CHANGED
|
@@ -83,3 +83,13 @@ export declare function matchesTurnFilter(turn: HistoryTurn, query: string): boo
|
|
|
83
83
|
* in first-appearance order. Empty string for a tool-less turn.
|
|
84
84
|
*/
|
|
85
85
|
export declare function toolCallSummary(names: readonly string[]): string;
|
|
86
|
+
/**
|
|
87
|
+
* The fork-at-turn seed: the session events from seq 0 through the selected
|
|
88
|
+
* turn's `turn/end` (inclusive) — the state as that turn finished; later
|
|
89
|
+
* turns stay out of the new session. `seq === array index` holds for live
|
|
90
|
+
* snapshots and persisted `inspect` events alike (restore validates the same
|
|
91
|
+
* contiguity), so a plain slice is exact. Returns [] when the turn is
|
|
92
|
+
* unknown or never closed — only completed turns are forkable. The same
|
|
93
|
+
* balanced-prefix shape the host's fork subagent backend seeds with.
|
|
94
|
+
*/
|
|
95
|
+
export declare function turnSeedSlice(events: readonly SessionEvent[], turn: number): readonly SessionEvent[];
|
package/lib/history-turns.js
CHANGED
|
@@ -167,4 +167,20 @@ export function toolCallSummary(names) {
|
|
|
167
167
|
const total = names.length;
|
|
168
168
|
return `${total} tool call${total === 1 ? '' : 's'}: ${parts.join(', ')}`;
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* The fork-at-turn seed: the session events from seq 0 through the selected
|
|
172
|
+
* turn's `turn/end` (inclusive) — the state as that turn finished; later
|
|
173
|
+
* turns stay out of the new session. `seq === array index` holds for live
|
|
174
|
+
* snapshots and persisted `inspect` events alike (restore validates the same
|
|
175
|
+
* contiguity), so a plain slice is exact. Returns [] when the turn is
|
|
176
|
+
* unknown or never closed — only completed turns are forkable. The same
|
|
177
|
+
* balanced-prefix shape the host's fork subagent backend seeds with.
|
|
178
|
+
*/
|
|
179
|
+
export function turnSeedSlice(events, turn) {
|
|
180
|
+
for (const grouped of groupHistoryTurns(events)) {
|
|
181
|
+
if (grouped.turn === turn)
|
|
182
|
+
return events.slice(0, grouped.seqEnd + 1);
|
|
183
|
+
}
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
170
186
|
//# sourceMappingURL=history-turns.js.map
|
package/lib/history-turns.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history-turns.js","sourceRoot":"","sources":["../src/history-turns.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"history-turns.js","sourceRoot":"","sources":["../src/history-turns.ts"],"names":[],"mappings":"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"}
|
package/lib/history.d.ts
CHANGED
|
@@ -121,6 +121,23 @@ export interface HistoryBrowserDeps {
|
|
|
121
121
|
getLiveEvents(): readonly SessionEvent[] | undefined;
|
|
122
122
|
/** Copy target: a plain editor setText (never submitted). */
|
|
123
123
|
copyToEditor(text: string): void;
|
|
124
|
+
/**
|
|
125
|
+
* Show the fork-at-turn confirmation over the open browser; resolves true
|
|
126
|
+
* = fork now. `turnLabel` is the selected turn's number (as displayed),
|
|
127
|
+
* `totalTurns` the session's listed turn count; `cold` is true when the
|
|
128
|
+
* browsed session is not the live one — the fork then detaches the LIVE
|
|
129
|
+
* session, and the dialog must say so instead of hiding it.
|
|
130
|
+
*/
|
|
131
|
+
confirmForkAtTurn(turnLabel: string, totalTurns: number, cold: boolean): Promise<boolean>;
|
|
132
|
+
/**
|
|
133
|
+
* Fork-and-switch at the turn boundary: start a new session on the CURRENT
|
|
134
|
+
* preset selection seeded with `seed` (the browsed session's prefix), and
|
|
135
|
+
* switch to it. Resolves when the new session is live; a rejection leaves
|
|
136
|
+
* every existing binding untouched (the browser stays open).
|
|
137
|
+
*/
|
|
138
|
+
forkAtTurn(seed: readonly SessionEvent[], parentSessionId: string): Promise<unknown>;
|
|
139
|
+
/** Buffered channel for fork failures (a transcript notice). */
|
|
140
|
+
reportError(message: string): void;
|
|
124
141
|
restoreFocus(): void;
|
|
125
142
|
requestRender(): void;
|
|
126
143
|
}
|
|
@@ -167,11 +184,14 @@ export declare class HistoryBrowserPanel implements Component {
|
|
|
167
184
|
private sessionId;
|
|
168
185
|
private live;
|
|
169
186
|
private turns;
|
|
187
|
+
/** The browsed session's raw events — the fork-at-turn slice source. */
|
|
188
|
+
private events;
|
|
170
189
|
private query;
|
|
171
190
|
private rows;
|
|
172
191
|
private status;
|
|
173
192
|
private closed;
|
|
174
193
|
private pickerLoading;
|
|
194
|
+
private forkInProgress;
|
|
175
195
|
/**
|
|
176
196
|
* Where the keyboard lives: the left list (default) or the right detail
|
|
177
197
|
* pane. `→` hands focus to the detail pane, `←`/Esc step back; only the
|
|
@@ -219,6 +239,15 @@ export declare class HistoryBrowserPanel implements Component {
|
|
|
219
239
|
* picker-local — reset on every `s` (CONTEXT.md "Filter").
|
|
220
240
|
*/
|
|
221
241
|
buildSessionPickerPanel(rows: readonly SessionPickRow[]): TablePanel<SessionPickRow>;
|
|
242
|
+
/**
|
|
243
|
+
* Fork at the selected turn (`f`): confirm over the open browser, then
|
|
244
|
+
* hand the turn-bounded seed to the fork-and-switch seam. The browsed
|
|
245
|
+
* session's events are the slice source — live snapshots and cold-read
|
|
246
|
+
* (`inspect`) events fork alike. Cancel or an empty slice (nothing
|
|
247
|
+
* completed to carry) changes nothing; a failed fork keeps the browser
|
|
248
|
+
* open with the failure on the status line plus a buffered notice.
|
|
249
|
+
*/
|
|
250
|
+
private forkAtSelectedTurn;
|
|
222
251
|
/** Open the session picker overlay (the browser stays mounted underneath). */
|
|
223
252
|
private openSessionPicker;
|
|
224
253
|
/** Live query swap: rebuild rows, keep the cursor on its turn when visible. */
|
package/lib/history.js
CHANGED
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
*/
|
|
58
58
|
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
59
59
|
import { Container, getKeybindings, HStack, matchesKey, Markdown, Spacer, Text, VStack, } from '@earendil-works/pi-tui';
|
|
60
|
-
import { groupHistoryTurns, matchesTurnFilter, toolCallSummary, turnPrimaryUserText, } from "./history-turns.js";
|
|
60
|
+
import { groupHistoryTurns, matchesTurnFilter, toolCallSummary, turnPrimaryUserText, turnSeedSlice, } from "./history-turns.js";
|
|
61
61
|
import { stopIcon } from "./icons.js";
|
|
62
62
|
import { isCorruptLogError } from "./log-repair.js";
|
|
63
63
|
import { autoColumns, PanelHost, panelThemeFns, TablePanel } from "./panels.js";
|
|
@@ -71,7 +71,7 @@ export const LEFT_PANE_MIN_COLUMNS = 30;
|
|
|
71
71
|
/** Rendered-line cap of one user bubble in the detail pane (spec: truncated). */
|
|
72
72
|
export const MAX_USER_BUBBLE_LINES = 40;
|
|
73
73
|
/** List footer hint (the detail pane carries the scroll hints). */
|
|
74
|
-
const HISTORY_FOOTER = '↑↓ navigate · Enter/c copy · → detail · s session · / filter · Esc close';
|
|
74
|
+
const HISTORY_FOOTER = '↑↓ navigate · Enter/c copy · f fork · → detail · s session · / filter · Esc close';
|
|
75
75
|
/**
|
|
76
76
|
* Content-row budget inside the framed overlay: showOverlay slices the
|
|
77
77
|
* component's lines at maxHeight ('85%' of the terminal — pi-tui floors the
|
|
@@ -372,11 +372,14 @@ export class HistoryBrowserPanel {
|
|
|
372
372
|
sessionId;
|
|
373
373
|
live;
|
|
374
374
|
turns;
|
|
375
|
+
/** The browsed session's raw events — the fork-at-turn slice source. */
|
|
376
|
+
events;
|
|
375
377
|
query = '';
|
|
376
378
|
rows;
|
|
377
379
|
status;
|
|
378
380
|
closed = false;
|
|
379
381
|
pickerLoading = false;
|
|
382
|
+
forkInProgress = false;
|
|
380
383
|
/**
|
|
381
384
|
* Where the keyboard lives: the left list (default) or the right detail
|
|
382
385
|
* pane. `→` hands focus to the detail pane, `←`/Esc step back; only the
|
|
@@ -397,6 +400,7 @@ export class HistoryBrowserPanel {
|
|
|
397
400
|
this.sessionId = loaded.sessionId;
|
|
398
401
|
this.live = loaded.live;
|
|
399
402
|
this.turns = groupHistoryTurns(loaded.events);
|
|
403
|
+
this.events = loaded.events;
|
|
400
404
|
this.rows = historyRows(this.turns, '');
|
|
401
405
|
this.listMax = listMaxVisible();
|
|
402
406
|
this.builtBudget = overlayContentBudget();
|
|
@@ -434,6 +438,8 @@ export class HistoryBrowserPanel {
|
|
|
434
438
|
shortcuts: {
|
|
435
439
|
c: () => this.copySelected(),
|
|
436
440
|
s: () => { void this.openSessionPicker(); },
|
|
441
|
+
// Fork at the selected turn (confirmed in a dialog over the browser).
|
|
442
|
+
f: () => { void this.forkAtSelectedTurn(); },
|
|
437
443
|
// Detail paging rides the list's shortcut map: the TablePanel checks
|
|
438
444
|
// shortcuts only OUTSIDE filter-input mode (the engaged input returns
|
|
439
445
|
// before the shortcut lookup), so `[`/`]` type into the query while
|
|
@@ -527,6 +533,12 @@ export class HistoryBrowserPanel {
|
|
|
527
533
|
this.detail.scrollByPage(1);
|
|
528
534
|
return;
|
|
529
535
|
}
|
|
536
|
+
// The selection is the same whichever pane is keyed: `f` forks at it
|
|
537
|
+
// from the detail focus too.
|
|
538
|
+
if (data === 'f') {
|
|
539
|
+
void this.forkAtSelectedTurn();
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
530
542
|
return;
|
|
531
543
|
}
|
|
532
544
|
// List focus: `→` hands focus to the detail pane — except while the
|
|
@@ -607,6 +619,7 @@ export class HistoryBrowserPanel {
|
|
|
607
619
|
this.sessionId = loaded.sessionId;
|
|
608
620
|
this.live = loaded.live;
|
|
609
621
|
this.turns = groupHistoryTurns(loaded.events);
|
|
622
|
+
this.events = loaded.events;
|
|
610
623
|
this.query = '';
|
|
611
624
|
this.status = undefined;
|
|
612
625
|
this.rows = historyRows(this.turns, '');
|
|
@@ -669,6 +682,41 @@ export class HistoryBrowserPanel {
|
|
|
669
682
|
picker = new TablePanel(this.deps.theme, options);
|
|
670
683
|
return picker;
|
|
671
684
|
}
|
|
685
|
+
/**
|
|
686
|
+
* Fork at the selected turn (`f`): confirm over the open browser, then
|
|
687
|
+
* hand the turn-bounded seed to the fork-and-switch seam. The browsed
|
|
688
|
+
* session's events are the slice source — live snapshots and cold-read
|
|
689
|
+
* (`inspect`) events fork alike. Cancel or an empty slice (nothing
|
|
690
|
+
* completed to carry) changes nothing; a failed fork keeps the browser
|
|
691
|
+
* open with the failure on the status line plus a buffered notice.
|
|
692
|
+
*/
|
|
693
|
+
async forkAtSelectedTurn() {
|
|
694
|
+
if (this.closed || this.forkInProgress)
|
|
695
|
+
return;
|
|
696
|
+
const row = this.list.selectedRow();
|
|
697
|
+
if (row === undefined)
|
|
698
|
+
return;
|
|
699
|
+
const seed = turnSeedSlice(this.events, row.turn.turn);
|
|
700
|
+
if (seed.length === 0)
|
|
701
|
+
return;
|
|
702
|
+
this.forkInProgress = true;
|
|
703
|
+
try {
|
|
704
|
+
const outcome = await this.deps.confirmForkAtTurn(row.turnLabel, this.turns.length, !this.live);
|
|
705
|
+
if (outcome !== true)
|
|
706
|
+
return;
|
|
707
|
+
await this.deps.forkAtTurn(seed, this.sessionId);
|
|
708
|
+
this.finish(`Forked at turn ${row.turnLabel} — new session opened.`);
|
|
709
|
+
}
|
|
710
|
+
catch (error) {
|
|
711
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
712
|
+
this.status = `Fork failed: ${message}`;
|
|
713
|
+
this.deps.reportError(`Fork at turn ${row.turnLabel} failed: ${message}`);
|
|
714
|
+
this.deps.requestRender();
|
|
715
|
+
}
|
|
716
|
+
finally {
|
|
717
|
+
this.forkInProgress = false;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
672
720
|
/** Open the session picker overlay (the browser stays mounted underneath). */
|
|
673
721
|
async openSessionPicker() {
|
|
674
722
|
if (this.pickerLoading || this.closed)
|