dsh-ssh-tui 0.8.0 → 0.8.2-rc.1
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/CHANGELOG.md +150 -0
- package/README.en.md +74 -8
- package/README.md +451 -577
- package/docs/display-mode.md +122 -0
- package/docs/release-notes-0.7.4.md +91 -0
- package/docs/release-notes-0.8.0.md +56 -0
- package/docs/release-notes-0.8.1.md +79 -0
- package/docs/release-notes-0.8.2-rc.1.md +159 -0
- package/docs/remote-ops.md +51 -0
- package/docs/terminals.md +53 -0
- package/docs/windows.md +13 -0
- package/lib/approval-state.js +148 -0
- package/lib/approval-state.js.map +1 -0
- package/lib/attach.js +4 -4
- package/lib/attach.js.map +1 -1
- package/lib/auth-failure.js +74 -0
- package/lib/auth-failure.js.map +1 -1
- package/lib/color-depth.js +26 -0
- package/lib/color-depth.js.map +1 -1
- package/lib/commands.js +2 -0
- package/lib/commands.js.map +1 -1
- package/lib/copy-text.js +30 -8
- package/lib/copy-text.js.map +1 -1
- package/lib/dialogs.js +124 -0
- package/lib/dialogs.js.map +1 -1
- package/lib/display-mode.js +147 -0
- package/lib/display-mode.js.map +1 -0
- package/lib/display-sock.js +490 -35
- package/lib/display-sock.js.map +1 -1
- package/lib/footer-accents.js +28 -0
- package/lib/footer-accents.js.map +1 -0
- package/lib/footer-budget.js +328 -0
- package/lib/footer-budget.js.map +1 -0
- package/lib/footer-format.js +433 -0
- package/lib/footer-format.js.map +1 -0
- package/lib/footer-layout.js +407 -0
- package/lib/footer-layout.js.map +1 -0
- package/lib/footer-meters.js +185 -0
- package/lib/footer-meters.js.map +1 -0
- package/lib/footer.js +19 -561
- package/lib/footer.js.map +1 -1
- package/lib/glyph-measure.js +92 -0
- package/lib/glyph-measure.js.map +1 -0
- package/lib/i18n/en.js +83 -7
- package/lib/i18n/en.js.map +1 -1
- package/lib/i18n/zh.js +85 -7
- package/lib/i18n/zh.js.map +1 -1
- package/lib/index.js +73 -5
- package/lib/index.js.map +1 -1
- package/lib/paint.js +75 -17
- package/lib/paint.js.map +1 -1
- package/lib/picker.js +14 -13
- package/lib/picker.js.map +1 -1
- package/lib/plan-projection.js +291 -0
- package/lib/plan-projection.js.map +1 -0
- package/lib/plan.js +61 -11
- package/lib/plan.js.map +1 -1
- package/lib/platform.js +96 -0
- package/lib/platform.js.map +1 -1
- package/lib/question-state.js +83 -0
- package/lib/question-state.js.map +1 -0
- package/lib/representation.js +283 -0
- package/lib/representation.js.map +1 -0
- package/lib/screen.js +105 -0
- package/lib/screen.js.map +1 -0
- package/lib/selection.js +5 -3
- package/lib/selection.js.map +1 -1
- package/lib/session-blank.js +81 -0
- package/lib/session-blank.js.map +1 -0
- package/lib/session-list.js +102 -81
- package/lib/session-list.js.map +1 -1
- package/lib/startup.js +56 -0
- package/lib/startup.js.map +1 -1
- package/lib/stats.js +101 -3
- package/lib/stats.js.map +1 -1
- package/lib/subagent-model.js +8 -7
- package/lib/subagent-model.js.map +1 -1
- package/lib/term-text.js +436 -69
- package/lib/term-text.js.map +1 -1
- package/lib/terminal-input.js +132 -10
- package/lib/terminal-input.js.map +1 -1
- package/lib/theme.js +335 -0
- package/lib/theme.js.map +1 -0
- package/lib/throughput.js +305 -0
- package/lib/throughput.js.map +1 -0
- package/lib/tool-present.js +67 -15
- package/lib/tool-present.js.map +1 -1
- package/lib/tui.js +4186 -1133
- package/lib/tui.js.map +1 -1
- package/lib/types/approval-state.d.ts +75 -0
- package/lib/types/attach.d.ts +6 -2
- package/lib/types/auth-failure.d.ts +55 -0
- package/lib/types/color-depth.d.ts +21 -0
- package/lib/types/commands.d.ts +6 -0
- package/lib/types/copy-text.d.ts +18 -1
- package/lib/types/dialogs.d.ts +147 -1
- package/lib/types/display-mode.d.ts +99 -0
- package/lib/types/display-sock.d.ts +90 -0
- package/lib/types/footer-accents.d.ts +22 -0
- package/lib/types/footer-budget.d.ts +204 -0
- package/lib/types/footer-format.d.ts +221 -0
- package/lib/types/footer-layout.d.ts +292 -0
- package/lib/types/footer-meters.d.ts +151 -0
- package/lib/types/footer.d.ts +19 -269
- package/lib/types/glyph-measure.d.ts +41 -0
- package/lib/types/index.d.ts +13 -0
- package/lib/types/paint.d.ts +48 -0
- package/lib/types/plan-projection.d.ts +162 -0
- package/lib/types/plan.d.ts +18 -2
- package/lib/types/platform.d.ts +83 -0
- package/lib/types/question-state.d.ts +96 -0
- package/lib/types/representation.d.ts +498 -0
- package/lib/types/screen.d.ts +126 -0
- package/lib/types/session-blank.d.ts +51 -0
- package/lib/types/session-list.d.ts +34 -0
- package/lib/types/startup.d.ts +30 -0
- package/lib/types/stats.d.ts +64 -1
- package/lib/types/subagent-model.d.ts +7 -6
- package/lib/types/term-text.d.ts +100 -24
- package/lib/types/terminal-input.d.ts +37 -0
- package/lib/types/theme.d.ts +109 -0
- package/lib/types/throughput.d.ts +163 -0
- package/lib/types/tool-present.d.ts +43 -2
- package/lib/types/transcript-types.d.ts +63 -2
- package/lib/types/tui.d.ts +960 -12
- package/package.json +73 -67
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Footer layout: the two chrome rows, and the `/status` report that spells the
|
|
3
|
+
* first one out.
|
|
4
|
+
*
|
|
5
|
+
* Layout assembles chips and rows; it does not decide how a chip degrades (that
|
|
6
|
+
* is `footer-budget.ts`) and does not spell a reading (that is
|
|
7
|
+
* `footer-format.ts`). The contract it does own is *where a signal lives*: the
|
|
8
|
+
* status row carries health, activity, speed, quota and context, the workspace
|
|
9
|
+
* row carries metadata, and the counters the status row gave up are answered by
|
|
10
|
+
* {@link formatStatusReport}.
|
|
11
|
+
*
|
|
12
|
+
* @module dsh-ssh-tui/footer-layout
|
|
13
|
+
*/
|
|
14
|
+
import { t } from './i18n/index.js';
|
|
15
|
+
import { pinEmojiCells, truncateAnsiToWidth, visibleWidth } from './term-text.js';
|
|
16
|
+
import { describeSubagentFit, subagentProviderDiffers } from './subagent-model.js';
|
|
17
|
+
import { activeTheme, themeExtraToken, themeToken } from './theme.js';
|
|
18
|
+
import { downgradeSgr } from './color-depth.js';
|
|
19
|
+
import { formatQuotaStatusLine, } from './quota.js';
|
|
20
|
+
import { formatRate } from './throughput.js';
|
|
21
|
+
import { sessionTokenTotal } from './stats.js';
|
|
22
|
+
import { describeProviderRoute, formatContextPressureStatusLine, formatDuration, formatTokens, formatTokensPerSecond, shortModelName, subagentRouteLabel, } from './footer-format.js';
|
|
23
|
+
export const WAIT_INDICATOR_MS = 8000;
|
|
24
|
+
/** Stats groups in drop order (last is dropped first when the row is too wide). */
|
|
25
|
+
/**
|
|
26
|
+
* @deprecated Legacy surface, removed in 0.9.
|
|
27
|
+
*
|
|
28
|
+
* The status row shows one session total and `/status` prints the counters; this
|
|
29
|
+
* group list was the previous footer's first row, and the only thing still
|
|
30
|
+
* composing it is `statsText()` (itself test-only). Kept exported for 0.8.x.
|
|
31
|
+
*/
|
|
32
|
+
export function footerStatsGroups(stats) {
|
|
33
|
+
const groups = [];
|
|
34
|
+
if (stats.steps > 0)
|
|
35
|
+
groups.push(t('footer.turnsSteps', { turns: stats.turns, steps: stats.steps }));
|
|
36
|
+
const billedInput = stats.inputTokens + stats.cacheReadTokens + stats.cacheWriteTokens;
|
|
37
|
+
if (billedInput > 0 || stats.outputTokens > 0) {
|
|
38
|
+
groups.push(t('footer.tokens', { input: formatTokens(billedInput), output: formatTokens(stats.outputTokens) }));
|
|
39
|
+
}
|
|
40
|
+
const speeds = [];
|
|
41
|
+
if (stats.decodeMs > 0 && stats.decodeTokens > 0) {
|
|
42
|
+
speeds.push(formatTokensPerSecond(stats.decodeTokens / (stats.decodeMs / 1_000)));
|
|
43
|
+
}
|
|
44
|
+
else if (stats.ttftSteps > 0) {
|
|
45
|
+
speeds.push(t('footer.ttft', { duration: formatDuration(stats.ttftMs / stats.ttftSteps) }));
|
|
46
|
+
}
|
|
47
|
+
if (speeds.length > 0)
|
|
48
|
+
groups.push(speeds.join(' '));
|
|
49
|
+
const durations = [];
|
|
50
|
+
if (stats.llmMs > 0)
|
|
51
|
+
durations.push(t('footer.llmMs', { duration: formatDuration(stats.llmMs) }));
|
|
52
|
+
if (stats.toolMs > 0)
|
|
53
|
+
durations.push(t('footer.toolMs', { duration: formatDuration(stats.toolMs) }));
|
|
54
|
+
if (durations.length > 0)
|
|
55
|
+
groups.push(durations.join(' '));
|
|
56
|
+
if (billedInput > 0)
|
|
57
|
+
groups.push(t('footer.cacheHit', { percent: Math.round(stats.cacheReadTokens / billedInput * 100) }));
|
|
58
|
+
return groups;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Fit an ordered strip into `width` cells, losing text before graphics.
|
|
62
|
+
*
|
|
63
|
+
* The old fitter dropped whole groups from the end, so a narrow terminal lost
|
|
64
|
+
* the ⚠ and the context ring — the two signals that say something is wrong —
|
|
65
|
+
* while keeping long numeric groups. Two passes now: every chip trades its text
|
|
66
|
+
* for its glyph in reverse priority order, and only then do whole groups go,
|
|
67
|
+
* again lowest priority first. The result never exceeds the width.
|
|
68
|
+
*
|
|
69
|
+
* Chips may carry SGR accents (the link pips, the ⚠) and the caller may pass a
|
|
70
|
+
* styled separator, so both the measurement and the final cut are ANSI-aware:
|
|
71
|
+
* feeding styled text to a plain-text truncator strips the accent's `ESC` and
|
|
72
|
+
* leaves its `[32m` body on the grid.
|
|
73
|
+
*/
|
|
74
|
+
/**
|
|
75
|
+
* @deprecated Legacy surface, removed in 0.9.
|
|
76
|
+
*
|
|
77
|
+
* The default row stopped using this when the footer became the status row
|
|
78
|
+
* (`docs/plans/footer-debt.md`, D-7): what is left of it is the previous
|
|
79
|
+
* generation's fitting and formatting, kept exported so 0.8.x does not break an
|
|
80
|
+
* importer. New callers want the budget module's `runtimeStrip`, the meters, or
|
|
81
|
+
* `formatStatusReport` for the spelled-out numbers.
|
|
82
|
+
*/
|
|
83
|
+
export function fitFooterChips(chips, width, separator = ' │ ') {
|
|
84
|
+
const limit = Math.max(1, width);
|
|
85
|
+
const state = chips
|
|
86
|
+
.filter(chip => chip.long !== '' || chip.short !== '')
|
|
87
|
+
.map(chip => ({ chip, full: true, present: true }));
|
|
88
|
+
const render = () => state
|
|
89
|
+
.filter(entry => entry.present)
|
|
90
|
+
.map(entry => (entry.full ? entry.chip.long : entry.chip.short))
|
|
91
|
+
.filter(text => text !== '')
|
|
92
|
+
.join(separator);
|
|
93
|
+
// Budget the way it will be painted: the frame pins a symbol like ⚠ with a
|
|
94
|
+
// variation selector and a reserving space, so the raw string measures one or
|
|
95
|
+
// two cells narrower than the row it becomes. Fitting the unpinned text left
|
|
96
|
+
// a row that overflowed the terminal by exactly that much.
|
|
97
|
+
const used = () => visibleWidth(pinEmojiCells(render()));
|
|
98
|
+
const byLeastImportant = [...state].sort((a, b) => b.chip.priority - a.chip.priority);
|
|
99
|
+
for (const entry of byLeastImportant) {
|
|
100
|
+
if (used() <= limit)
|
|
101
|
+
break;
|
|
102
|
+
entry.full = false;
|
|
103
|
+
}
|
|
104
|
+
for (const entry of byLeastImportant) {
|
|
105
|
+
if (used() <= limit)
|
|
106
|
+
break;
|
|
107
|
+
entry.present = false;
|
|
108
|
+
}
|
|
109
|
+
if (state.length > 0 && state.every(entry => !entry.present)) {
|
|
110
|
+
// Nothing fits, not even the glyphs. Keeping the most important group and
|
|
111
|
+
// letting the clip shorten it beats handing the user a blank row: a ⚠ cut
|
|
112
|
+
// to one cell still says the install is broken.
|
|
113
|
+
const best = [...state].sort((a, b) => a.chip.priority - b.chip.priority)[0];
|
|
114
|
+
if (best !== undefined) {
|
|
115
|
+
best.present = true;
|
|
116
|
+
best.full = false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return truncateAnsiToWidth(render(), limit);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The roster's health as a chip: absent means `/mode` cannot switch presets and
|
|
123
|
+
* the preset-owned tools are missing. It leads the status row and keeps its glyph
|
|
124
|
+
* longest, because it is the one group that reports a broken install.
|
|
125
|
+
*/
|
|
126
|
+
export function footerHealthChip(missing, color = false,
|
|
127
|
+
/** Which rows are missing: the 0.1.5 roster, or the 0.1.7 agent plane. */
|
|
128
|
+
kind = 'roster') {
|
|
129
|
+
if (!missing)
|
|
130
|
+
return undefined;
|
|
131
|
+
const glyph = color ? `\x1b[${themeExtraToken(activeTheme(), 'warn')}m⚠\x1b[0m` : '⚠';
|
|
132
|
+
return {
|
|
133
|
+
id: 'health',
|
|
134
|
+
long: `${glyph} ${t(kind === 'agent-plane' ? 'footer.agentPlaneMissing' : 'footer.rosterMissing')}`,
|
|
135
|
+
short: glyph,
|
|
136
|
+
priority: 0,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* @deprecated Legacy surface, removed in 0.9.
|
|
141
|
+
*
|
|
142
|
+
* The default row stopped using this when the footer became the status row
|
|
143
|
+
* (`docs/plans/footer-debt.md`, D-7): what is left of it is the previous
|
|
144
|
+
* generation's fitting and formatting, kept exported so 0.8.x does not break an
|
|
145
|
+
* importer. New callers want the budget module's `runtimeStrip`, the meters, or
|
|
146
|
+
* `formatStatusReport` for the spelled-out numbers.
|
|
147
|
+
*/
|
|
148
|
+
export function fitFooterStatsLine(chip, groups, width) {
|
|
149
|
+
const kept = [...groups];
|
|
150
|
+
const render = () => kept.length === 0 ? chip : `${chip} │ ${kept.join(' │ ')}`;
|
|
151
|
+
while (kept.length > 0 && visibleWidth(render()) > width)
|
|
152
|
+
kept.pop();
|
|
153
|
+
return truncateAnsiToWidth(render(), Math.max(1, width));
|
|
154
|
+
}
|
|
155
|
+
export function footerActivity(input) {
|
|
156
|
+
if (input.planReview)
|
|
157
|
+
return { kind: 'plan-review', text: t('footer.planReview') };
|
|
158
|
+
if (input.waitingQuestion)
|
|
159
|
+
return { kind: 'waiting', text: t('footer.waiting') };
|
|
160
|
+
// An approval is the one wait that used to read as idle: it is a `confirm`
|
|
161
|
+
// dialog, and the status row only recognised `questions` ones.
|
|
162
|
+
if (input.waitingApproval)
|
|
163
|
+
return { kind: 'approval', text: t('footer.approval') };
|
|
164
|
+
if (input.compacting)
|
|
165
|
+
return { kind: 'compacting', text: t('footer.compacting') };
|
|
166
|
+
// Only a running turn can be retrying: a chip left over from a finished turn
|
|
167
|
+
// (a missed event, or a retry of a background request such as the session
|
|
168
|
+
// title) must not hide the state the user is actually in.
|
|
169
|
+
if (input.running && input.retry !== undefined) {
|
|
170
|
+
return { kind: 'retry', text: t('footer.retry', { retry: input.retry.retry, max: input.retry.maxRetries }) };
|
|
171
|
+
}
|
|
172
|
+
if (input.subagents > 0) {
|
|
173
|
+
// `agent · 3m12s`: the verb and the clock. Which child it is (model, effort,
|
|
174
|
+
// role) is the identity row's job — putting `sub:grok-4.7(xhigh)` here broke
|
|
175
|
+
// the row's width on exactly the sessions that spawn the most children.
|
|
176
|
+
return { kind: 'agent', text: t('footer.agent') };
|
|
177
|
+
}
|
|
178
|
+
if (input.running && input.tools > 0) {
|
|
179
|
+
// The tool names itself rather than reporting a count: `⠹ edit · 28s` says
|
|
180
|
+
// what the session is doing, while `工具 1 · 28s` says how many of something
|
|
181
|
+
// unnamed are open. A count only appears when several run at once, where the
|
|
182
|
+
// question "which one" has no single answer.
|
|
183
|
+
const label = input.toolLabel?.trim() ?? '';
|
|
184
|
+
if (label !== '')
|
|
185
|
+
return { kind: 'tools', text: label };
|
|
186
|
+
return { kind: 'tools', text: t('footer.tools', { count: input.tools }) };
|
|
187
|
+
}
|
|
188
|
+
if (input.planLeftOpen)
|
|
189
|
+
return { kind: 'plan-open', text: t('footer.planOpen') };
|
|
190
|
+
if (input.planPending)
|
|
191
|
+
return { kind: 'plan-pending', text: t('footer.planSwitching') };
|
|
192
|
+
if (input.planActive)
|
|
193
|
+
return { kind: 'plan-pending', text: t('footer.planMode') };
|
|
194
|
+
if (input.goalPhase === 'active')
|
|
195
|
+
return { kind: 'goal', text: t('footer.goalActive') };
|
|
196
|
+
if (input.goalPhase === 'paused')
|
|
197
|
+
return { kind: 'goal', text: t('footer.goalPaused') };
|
|
198
|
+
if (input.goalPhase === 'blocked')
|
|
199
|
+
return { kind: 'goal', text: t('footer.goalBlocked') };
|
|
200
|
+
if (input.running && input.idleMs > WAIT_INDICATOR_MS) {
|
|
201
|
+
return { kind: 'waiting-llm', text: t('footer.waitSeconds', { seconds: Math.floor(input.idleMs / 1000) }) };
|
|
202
|
+
}
|
|
203
|
+
// What the running turn is actually doing. The window before the first token
|
|
204
|
+
// stays `运行中`: calling it 思考中 would claim the model is reasoning when the
|
|
205
|
+
// request may not even have been accepted yet, and would put a decode clock on
|
|
206
|
+
// a step that has decoded nothing.
|
|
207
|
+
if (input.running) {
|
|
208
|
+
if (input.streamingText === true)
|
|
209
|
+
return { kind: 'reply', text: t('footer.reply') };
|
|
210
|
+
if (input.streamingReasoning === true)
|
|
211
|
+
return { kind: 'thinking', text: t('footer.thinking') };
|
|
212
|
+
return { kind: 'idle', text: t('footer.running') };
|
|
213
|
+
}
|
|
214
|
+
return { kind: 'idle', text: t('footer.idle') };
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Paint the `sub:` chip on an otherwise muted status line.
|
|
218
|
+
*
|
|
219
|
+
* Only a *foreign* route is accented: cyan when `/submodel` pinned a supplier
|
|
220
|
+
* the parent is not on. A child that follows the parent (or that was pinned
|
|
221
|
+
* back onto the parent's own provider) keeps the identity row's mute — the
|
|
222
|
+
* accent means "this route is not your parent's", and spending it on every
|
|
223
|
+
* child made the following case look pinned.
|
|
224
|
+
*
|
|
225
|
+
* The mute (`90`) is reopened after the chip so the rest of the identity row
|
|
226
|
+
* stays dim.
|
|
227
|
+
*/
|
|
228
|
+
export function paintFooterSubagentChip(line, chip, foreign, muteSgr = themeToken(activeTheme(), 'system'), depth = 'truecolor', identitySgr = themeExtraToken(activeTheme(), 'subagent-foreign')) {
|
|
229
|
+
if (!foreign || chip === '' || !line.includes(chip))
|
|
230
|
+
return line;
|
|
231
|
+
const code = downgradeSgr(identitySgr, depth);
|
|
232
|
+
if (code === '')
|
|
233
|
+
return line;
|
|
234
|
+
const mute = muteSgr === '' ? '0' : muteSgr;
|
|
235
|
+
return line.replace(chip, `\x1b[${code}m${chip}\x1b[0m\x1b[${mute}m`);
|
|
236
|
+
}
|
|
237
|
+
export function footerSubagentForeign(input) {
|
|
238
|
+
return subagentProviderDiffers(input.provider, input.subProvider);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* The workspace row: what this session is, not what it is doing.
|
|
242
|
+
*
|
|
243
|
+
* Quota and context used to live here and now own capacity meters on the row
|
|
244
|
+
* above, so they are deliberately absent: a number painted twice is a number the
|
|
245
|
+
* reader has to check twice. What is left is the configuration the header does
|
|
246
|
+
* not carry — the preset, the working directory, the child route, and the
|
|
247
|
+
* input's own transient state.
|
|
248
|
+
*/
|
|
249
|
+
export function footerIdentityParts(input, options = {}) {
|
|
250
|
+
const parts = [];
|
|
251
|
+
if (input.compactView === true)
|
|
252
|
+
parts.push(`[${t('view.footerCompact')}]`);
|
|
253
|
+
// The preset is *not* here: the header prints it permanently, so a second copy
|
|
254
|
+
// in a footer fighting for cells bought nothing. The row carries what the header
|
|
255
|
+
// does not - where this session is working, and what is waiting for it.
|
|
256
|
+
if (input.cwdLabel !== undefined && input.cwdLabel !== '')
|
|
257
|
+
parts.push(input.cwdLabel);
|
|
258
|
+
// A balance-only route (DeepSeek) has no quota window to gauge, so its remaining
|
|
259
|
+
// credit stays as reference metadata rather than pretending to be a capacity
|
|
260
|
+
// meter it cannot fill - and it outlives the configuration below, because on
|
|
261
|
+
// that route it is the row's only reading.
|
|
262
|
+
if (input.balanceText !== undefined && input.balanceText !== '')
|
|
263
|
+
parts.push(input.balanceText);
|
|
264
|
+
if (options.omitModel !== true) {
|
|
265
|
+
const modelName = shortModelName(input.model);
|
|
266
|
+
const effort = input.effort === undefined || input.effort.trim() === '' ? '' : input.effort.trim();
|
|
267
|
+
const model = [modelName, effort].filter(part => part !== '').join(' ');
|
|
268
|
+
// Only an embedded surface with no header asks for this; `/status` is where
|
|
269
|
+
// the exact route lives, so it sits with the rest of the configuration.
|
|
270
|
+
if (model !== '')
|
|
271
|
+
parts.push(model);
|
|
272
|
+
}
|
|
273
|
+
if (input.search !== undefined)
|
|
274
|
+
parts.push(t('footer.search', { index: input.search.index + 1, total: input.search.total }));
|
|
275
|
+
if (input.foldedInput)
|
|
276
|
+
parts.push(t('footer.inputFolded'));
|
|
277
|
+
else if (input.multiLineInput)
|
|
278
|
+
parts.push(t('footer.multiLine'));
|
|
279
|
+
if (input.queued > 0)
|
|
280
|
+
parts.push(t('footer.queued', { count: input.queued }));
|
|
281
|
+
// The child route closes the row: configuration, inherited by every child and
|
|
282
|
+
// changed only by `/submodel`, so it is the first thing a narrow row gives up.
|
|
283
|
+
const sub = subagentRouteLabel(input.subModel, input.subProvider, input.subEffort);
|
|
284
|
+
if (sub !== '')
|
|
285
|
+
parts.push(sub);
|
|
286
|
+
// The subscription closes the row, behind even the child route: it is the
|
|
287
|
+
// context everything else is billed against, not something the reader acts on,
|
|
288
|
+
// so it is the first part a narrow row discards.
|
|
289
|
+
if (input.planBadge !== undefined && input.planBadge !== '')
|
|
290
|
+
parts.push(input.planBadge);
|
|
291
|
+
return parts;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Fit the workspace row under `width`.
|
|
295
|
+
*
|
|
296
|
+
* The metadata is secondary by construction: it follows the activity label at a
|
|
297
|
+
* dimmer intensity and is dropped from the right, one part at a time, rather
|
|
298
|
+
* than being allowed to wrap into the status row above it.
|
|
299
|
+
*/
|
|
300
|
+
export function fitFooterStatusLine(activity, identity, width) {
|
|
301
|
+
const kept = [...identity];
|
|
302
|
+
// An empty label means this row carries metadata only (the status row's
|
|
303
|
+
// activity lives one line up), and then the joining spaces go with it: a row
|
|
304
|
+
// that opened with two blank cells read as a layout bug.
|
|
305
|
+
const render = () => {
|
|
306
|
+
const parts = kept.filter(part => part !== '');
|
|
307
|
+
if (activity === '')
|
|
308
|
+
return parts.join(' · ');
|
|
309
|
+
return parts.length === 0 ? activity : `${activity} ${parts.join(' · ')}`;
|
|
310
|
+
};
|
|
311
|
+
// From the right, which is the priority order the parts are already in: the
|
|
312
|
+
// child route goes first, then the row's state, and the working directory last.
|
|
313
|
+
while (kept.length > 0 && visibleWidth(render()) > width)
|
|
314
|
+
kept.pop();
|
|
315
|
+
return truncateAnsiToWidth(render(), Math.max(1, width));
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Lines printed by `/status` — SSH first-boot diagnostics, no extra command.
|
|
319
|
+
*
|
|
320
|
+
* This is where the status row's removed telemetry lives. Turns, steps, model
|
|
321
|
+
* time, tool time, cache hit and the input/output breakdown are session
|
|
322
|
+
* analytics: they answer no question a live operator is asking, which is why
|
|
323
|
+
* they left the default row, and their disappearance would be information loss —
|
|
324
|
+
* so `/status` prints all of them, plus the throughput accounting, in the shapes
|
|
325
|
+
* the row used to use.
|
|
326
|
+
*/
|
|
327
|
+
export function formatStatusReport(input) {
|
|
328
|
+
const route = describeProviderRoute(input.provider);
|
|
329
|
+
const effort = input.effort === undefined ? '' : ` (${input.effort})`;
|
|
330
|
+
const fit = describeSubagentFit({
|
|
331
|
+
parentProvider: input.provider,
|
|
332
|
+
parentModel: input.parentModel,
|
|
333
|
+
subProvider: input.subProvider,
|
|
334
|
+
subModel: input.subModel,
|
|
335
|
+
});
|
|
336
|
+
return [
|
|
337
|
+
`session: ${input.sessionId}`,
|
|
338
|
+
`plugin: dsh-ssh-tui ${input.pluginVersion}`,
|
|
339
|
+
`cwd: ${input.cwd ?? ''}`,
|
|
340
|
+
`route: ${input.provider}/${input.model}${effort}`,
|
|
341
|
+
`provider: ${route.kind}`,
|
|
342
|
+
`status: ${input.agentStatus}`,
|
|
343
|
+
`preset: ${input.preset}`,
|
|
344
|
+
`subagents: ${input.activeSubagents}`,
|
|
345
|
+
fit.line,
|
|
346
|
+
`plan: ${input.plan}`,
|
|
347
|
+
formatQuotaStatusLine(input.quota),
|
|
348
|
+
formatContextPressureStatusLine(input.context),
|
|
349
|
+
...(input.stats === undefined ? [] : formatStatusStats(input.stats)),
|
|
350
|
+
...(input.throughput === undefined ? [] : [formatStatusThroughput(input.throughput)]),
|
|
351
|
+
`paint: ${input.paint}`,
|
|
352
|
+
`disconnect: ${input.disconnect ?? 'pause'}`,
|
|
353
|
+
input.waitingQuestions > 0 ? `questions: waiting ${input.waitingQuestions}` : 'questions: none',
|
|
354
|
+
];
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* The stats block `/status` prints: everything the default row gave up.
|
|
358
|
+
*
|
|
359
|
+
* `tokens/sec` here is the exact per-step rate (`decodeTokens / decodeMs`), not
|
|
360
|
+
* the running estimate, and the line says which span it covers: the last step
|
|
361
|
+
* and the session so far are different questions and only one of them is a
|
|
362
|
+
* benchmark.
|
|
363
|
+
*/
|
|
364
|
+
export function formatStatusStats(stats) {
|
|
365
|
+
const billedInput = stats.inputTokens + stats.cacheReadTokens + stats.cacheWriteTokens;
|
|
366
|
+
const total = sessionTokenTotal({
|
|
367
|
+
inputTokens: stats.inputTokens,
|
|
368
|
+
outputTokens: stats.outputTokens,
|
|
369
|
+
reportedTokens: stats.totalTokens,
|
|
370
|
+
reportedSteps: stats.totalTokens > 0 ? 1 : 0,
|
|
371
|
+
unreportedSteps: stats.totalTokens > 0 ? 0 : 1,
|
|
372
|
+
cacheReadTokens: stats.cacheReadTokens,
|
|
373
|
+
cacheWriteTokens: stats.cacheWriteTokens,
|
|
374
|
+
});
|
|
375
|
+
const lines = [
|
|
376
|
+
`turns: ${stats.turns}`,
|
|
377
|
+
`steps: ${stats.steps}`,
|
|
378
|
+
`tokens: in ${formatTokens(billedInput)} · out ${formatTokens(stats.outputTokens)}`
|
|
379
|
+
+ (total === undefined
|
|
380
|
+
? ''
|
|
381
|
+
: ` · total ${formatTokens(total.tokens)} (${total.basis === 'harness' ? 'harness totalTokens' : 'billed parts summed'})`),
|
|
382
|
+
`tokens: cached read ${formatTokens(stats.cacheReadTokens)} · cached write ${formatTokens(stats.cacheWriteTokens)}`,
|
|
383
|
+
];
|
|
384
|
+
if (billedInput > 0)
|
|
385
|
+
lines.push(`cache: hit ${Math.round(stats.cacheReadTokens / billedInput * 100)}%`);
|
|
386
|
+
if (stats.llmMs > 0)
|
|
387
|
+
lines.push(`model time: ${formatDuration(stats.llmMs)}`);
|
|
388
|
+
if (stats.toolMs > 0)
|
|
389
|
+
lines.push(`tool time: ${formatDuration(stats.toolMs)}`);
|
|
390
|
+
if (stats.ttftSteps > 0) {
|
|
391
|
+
const steps = stats.ttftSteps === 1 ? 'step' : 'steps';
|
|
392
|
+
lines.push(`ttft: ${formatDuration(stats.ttftMs / stats.ttftSteps)} avg over ${stats.ttftSteps} ${steps}`);
|
|
393
|
+
}
|
|
394
|
+
return lines;
|
|
395
|
+
}
|
|
396
|
+
/** One line: what the throughput number means, and which span it covers. */
|
|
397
|
+
export function formatStatusThroughput(throughput) {
|
|
398
|
+
const parts = [];
|
|
399
|
+
parts.push(throughput.settledRate === undefined
|
|
400
|
+
? 'tokens/sec: no settled step yet'
|
|
401
|
+
: `tokens/sec: ${formatRate(throughput.settledRate)} (last settled step, output tokens / decode ms)`);
|
|
402
|
+
if (throughput.liveRate !== undefined && throughput.fresh) {
|
|
403
|
+
parts.push(`~${formatRate(throughput.liveRate)} live (estimated from ${throughput.liveChars} streamed chars)`);
|
|
404
|
+
}
|
|
405
|
+
return parts.join(' · ');
|
|
406
|
+
}
|
|
407
|
+
//# sourceMappingURL=footer-layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footer-layout.js","sourceRoot":"","sources":["../src/footer-layout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three meter primitives the status row is built from.
|
|
3
|
+
*
|
|
4
|
+
* One component per kind of question, because a health question and a capacity
|
|
5
|
+
* question do not look alike:
|
|
6
|
+
*
|
|
7
|
+
* - {@link healthMeter} — "is this healthy?" Discrete levels, no bar. This is the
|
|
8
|
+
* SSH chip's four pips, generalised.
|
|
9
|
+
* - {@link capacityMeter} — "how much is left / how much is used?" A bar and a
|
|
10
|
+
* percent. The *direction* is a parameter, which is what lets quota (remaining)
|
|
11
|
+
* and context (used) share one implementation.
|
|
12
|
+
* - {@link performanceValue} — "how fast is it going?" A bare number, neutral,
|
|
13
|
+
* with a mark for the estimate and a dimmer form for a finished step.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here knows what a row is, how wide it is, or in what order chips go:
|
|
16
|
+
* that is `footer-budget.ts`, and keeping the two apart is what stops a meter
|
|
17
|
+
* from growing a layout opinion.
|
|
18
|
+
*
|
|
19
|
+
* @module dsh-ssh-tui/footer-meters
|
|
20
|
+
*/
|
|
21
|
+
import { asciiChromeEnabled } from './term-text.js';
|
|
22
|
+
import { activeTheme, themeThresholdToken } from './theme.js';
|
|
23
|
+
import { formatRate } from './throughput.js';
|
|
24
|
+
import { accent } from './color-depth.js';
|
|
25
|
+
import { CONTEXT_PRESSURE_DANGER_RATIO, CONTEXT_PRESSURE_WARN_RATIO, } from './footer-format.js';
|
|
26
|
+
/**
|
|
27
|
+
* The row's visual vocabulary, as reusable primitives.
|
|
28
|
+
*
|
|
29
|
+
* Three components, because the row answers three different kinds of question:
|
|
30
|
+
*
|
|
31
|
+
* - {@link healthMeter} — "is this healthy?" Discrete levels, no bar. This is
|
|
32
|
+
* the SSH chip's four pips, generalised; nothing else on the row copies it,
|
|
33
|
+
* because a health question and a capacity question do not look alike.
|
|
34
|
+
* - {@link capacityMeter} — "how much is left / how much is used?" A bar with a
|
|
35
|
+
* percent. The *direction* is a parameter, which is what lets quota (remaining)
|
|
36
|
+
* and context (used) share one implementation.
|
|
37
|
+
* - {@link performanceValue} — "how fast is it going?" A bare number, neutral,
|
|
38
|
+
* with a mark for the estimate and a dimmer form for a finished step.
|
|
39
|
+
*
|
|
40
|
+
* All three take `ascii` and answer with the project's own fallback glyphs, and
|
|
41
|
+
* none of them reach for a threshold colour on their own: the caller decides
|
|
42
|
+
* whether the number it is drawing has bands at all.
|
|
43
|
+
*/
|
|
44
|
+
/** How many level marks a health meter has: the four pips the SSH chip always had. */
|
|
45
|
+
export const HEALTH_METER_LEVELS = 4;
|
|
46
|
+
/** Full and empty level marks. The empty one is the same cell, hollow. */
|
|
47
|
+
export const HEALTH_MARK_FILLED = '●';
|
|
48
|
+
export const HEALTH_MARK_EMPTY = '○';
|
|
49
|
+
/** ASCII fallback for the same four levels, from the project's own glyph table. */
|
|
50
|
+
export const HEALTH_ASCII_FILLED = '*';
|
|
51
|
+
export const HEALTH_ASCII_EMPTY = 'o';
|
|
52
|
+
/** Filled and empty cells of a capacity meter, matching the existing quota bar. */
|
|
53
|
+
export const CAPACITY_CELL_FILLED = '█';
|
|
54
|
+
export const CAPACITY_CELL_EMPTY = '░';
|
|
55
|
+
/** ASCII fallback for the capacity meter, from the project's own glyph table. */
|
|
56
|
+
export const CAPACITY_ASCII_FILLED = '#';
|
|
57
|
+
export const CAPACITY_ASCII_EMPTY = '.';
|
|
58
|
+
/** Segment counts by row budget. Three is the smallest bar that still reads. */
|
|
59
|
+
export const CAPACITY_SEGMENTS_WIDE = 8;
|
|
60
|
+
export const CAPACITY_SEGMENTS_MEDIUM = 5;
|
|
61
|
+
export const CAPACITY_SEGMENTS_NARROW = 3;
|
|
62
|
+
/**
|
|
63
|
+
* Colours for a capacity meter's filled portion.
|
|
64
|
+
*
|
|
65
|
+
* Context and quota answer different questions and have different runways, so
|
|
66
|
+
* they get different bands — but each one reuses a threshold the session already
|
|
67
|
+
* enforces, so the meter can never disagree with the alert beside it:
|
|
68
|
+
* context reuses the pressure levels `contextPressureView` uses (warn at 80%,
|
|
69
|
+
* danger at 95%, the same numbers that drive the compaction prompt), and quota
|
|
70
|
+
* reuses the first two of `QUOTA_ALERT_THRESHOLDS` (50 / 25 remaining), which is
|
|
71
|
+
* where the session starts telling the user about it.
|
|
72
|
+
*/
|
|
73
|
+
export const CONTEXT_METER_WARN_PERCENT = CONTEXT_PRESSURE_WARN_RATIO * 100;
|
|
74
|
+
export const CONTEXT_METER_DANGER_PERCENT = CONTEXT_PRESSURE_DANGER_RATIO * 100;
|
|
75
|
+
/** First two entries of `quota.ts`'s `QUOTA_ALERT_THRESHOLDS`, in remaining percent. */
|
|
76
|
+
export const QUOTA_METER_WARN_PERCENT = 50;
|
|
77
|
+
export const QUOTA_METER_DANGER_PERCENT = 25;
|
|
78
|
+
/** Which band a capacity reading falls in, given what the number means. */
|
|
79
|
+
export function capacityLevel(percent, basis = 'used', quota = false) {
|
|
80
|
+
if (!Number.isFinite(percent))
|
|
81
|
+
return 'ok';
|
|
82
|
+
if (basis === 'remaining') {
|
|
83
|
+
// Remaining runs out downwards, so the bands are read on the way down; a
|
|
84
|
+
// quota is only "over" when almost nothing is left.
|
|
85
|
+
const warn = quota ? QUOTA_METER_WARN_PERCENT : 100 - CONTEXT_METER_WARN_PERCENT;
|
|
86
|
+
const danger = quota ? QUOTA_METER_DANGER_PERCENT : 100 - CONTEXT_METER_DANGER_PERCENT;
|
|
87
|
+
if (percent <= danger)
|
|
88
|
+
return 'over';
|
|
89
|
+
if (percent <= warn)
|
|
90
|
+
return 'warn';
|
|
91
|
+
return 'ok';
|
|
92
|
+
}
|
|
93
|
+
const warn = quota ? 100 - QUOTA_METER_WARN_PERCENT : CONTEXT_METER_WARN_PERCENT;
|
|
94
|
+
const danger = quota ? 100 - QUOTA_METER_DANGER_PERCENT : CONTEXT_METER_DANGER_PERCENT;
|
|
95
|
+
if (percent >= danger)
|
|
96
|
+
return 'over';
|
|
97
|
+
if (percent >= warn)
|
|
98
|
+
return 'warn';
|
|
99
|
+
return 'ok';
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* One capacity meter: `CTX ███████░░░ 61%`.
|
|
103
|
+
*
|
|
104
|
+
* An unusable percent paints a hollow bar with a question mark rather than `0%`:
|
|
105
|
+
* a reading nobody took is not a reading of zero, and `?%` is the same claim the
|
|
106
|
+
* quota chip has always made before its first reading lands.
|
|
107
|
+
*
|
|
108
|
+
* Colour, when asked for, lands only on the cells that carry the meaning — the
|
|
109
|
+
* filled run and the percent. Tinting the label and the empty cells would make
|
|
110
|
+
* the whole chip one colour and lose the hierarchy it exists to create.
|
|
111
|
+
*/
|
|
112
|
+
export function capacityMeter(percent, options = {}) {
|
|
113
|
+
const segments = Math.max(0, options.segments ?? CAPACITY_SEGMENTS_WIDE);
|
|
114
|
+
const ascii = options.ascii ?? asciiChromeEnabled();
|
|
115
|
+
const filledGlyph = ascii ? CAPACITY_ASCII_FILLED : CAPACITY_CELL_FILLED;
|
|
116
|
+
const emptyGlyph = ascii ? CAPACITY_ASCII_EMPTY : CAPACITY_CELL_EMPTY;
|
|
117
|
+
const label = options.label === undefined || options.label === '' ? '' : `${options.label} `;
|
|
118
|
+
const detail = options.detail === undefined || options.detail === '' ? '' : ` · ${options.detail}`;
|
|
119
|
+
if (percent === undefined || !Number.isFinite(percent)) {
|
|
120
|
+
return { text: `${label}${emptyGlyph.repeat(segments)} ?%${detail}`, level: 'ok' };
|
|
121
|
+
}
|
|
122
|
+
const clamped = Math.max(0, Math.min(100, percent));
|
|
123
|
+
const level = capacityLevel(clamped, options.basis ?? 'used', options.quota === true);
|
|
124
|
+
const filled = Math.round(clamped / 100 * segments);
|
|
125
|
+
const percentText = `${Math.round(clamped)}%`;
|
|
126
|
+
const color = options.color === true && options.depth !== 'none';
|
|
127
|
+
const paint = (text) => color
|
|
128
|
+
? accent(text, themeThresholdToken(activeTheme(), level), options.depth ?? 'truecolor')
|
|
129
|
+
: text;
|
|
130
|
+
if (options.percentOnly === true) {
|
|
131
|
+
return { text: `${label}${paint(percentText)}${detail}`, level };
|
|
132
|
+
}
|
|
133
|
+
const bar = `${paint(filledGlyph.repeat(filled))}${emptyGlyph.repeat(segments - filled)}`;
|
|
134
|
+
return { text: `${label}${bar} ${paint(percentText)}${detail}`, level };
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* One health meter: `●●●●`.
|
|
138
|
+
*
|
|
139
|
+
* Discrete levels, never a bar: a link is one of five states, and four pips say
|
|
140
|
+
* which at a glance. This is the component the SSH chip has always been — it
|
|
141
|
+
* simply became reusable when the quota and context meters arrived, so that the
|
|
142
|
+
* two things that *are* capacities stopped being drawn as pips.
|
|
143
|
+
*/
|
|
144
|
+
export function healthMeter(level, options = {}) {
|
|
145
|
+
const levels = Math.max(1, options.levels ?? HEALTH_METER_LEVELS);
|
|
146
|
+
const filled = Math.max(0, Math.min(levels, Math.round(level)));
|
|
147
|
+
const ascii = options.ascii ?? asciiChromeEnabled();
|
|
148
|
+
const on = ascii ? HEALTH_ASCII_FILLED : HEALTH_MARK_FILLED;
|
|
149
|
+
const off = ascii ? HEALTH_ASCII_EMPTY : HEALTH_MARK_EMPTY;
|
|
150
|
+
return `${on.repeat(filled)}${off.repeat(levels - filled)}`;
|
|
151
|
+
}
|
|
152
|
+
/** Which form of the throughput number can be painted at all. */
|
|
153
|
+
export function performanceValueState(throughput, running) {
|
|
154
|
+
if (throughput === undefined)
|
|
155
|
+
return 'unavailable';
|
|
156
|
+
if (running && throughput.fresh && throughput.liveRate !== undefined)
|
|
157
|
+
return 'live';
|
|
158
|
+
if (!running && throughput.settledRate !== undefined && throughput.settledRate > 0)
|
|
159
|
+
return 'settled';
|
|
160
|
+
return 'unavailable';
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The performance value: `160 tok/s`, `~160 tok/s`, `— tok/s`.
|
|
164
|
+
*
|
|
165
|
+
* Tokens per second is neutral telemetry, so this component never reaches for a
|
|
166
|
+
* threshold colour: the harness publishes no performance classification, and the
|
|
167
|
+
* same number means different things on a reasoning model, a small fast one, and
|
|
168
|
+
* a long-context request. `~` marks the live estimate — characters per second
|
|
169
|
+
* scaled by this session's own tokens-per-character — and the settled value has
|
|
170
|
+
* no mark because it is exact. A live value is lifted out of the row's muted
|
|
171
|
+
* style because it is the one number on the row that is *moving*; every theme
|
|
172
|
+
* understands "default intensity", and the mono theme keeps the distinction.
|
|
173
|
+
*/
|
|
174
|
+
export function performanceValue(throughput, running, options = {}) {
|
|
175
|
+
const unit = options.short === true ? 't/s' : 'tok/s';
|
|
176
|
+
const state = options.state ?? performanceValueState(throughput, running);
|
|
177
|
+
if (state === 'unavailable')
|
|
178
|
+
return `— ${unit}`;
|
|
179
|
+
if (state === 'live') {
|
|
180
|
+
const text = `~${formatRate(throughput?.liveRate ?? 0)} ${unit}`;
|
|
181
|
+
return options.color === true ? `\x1b[0m${text}\x1b[0m` : text;
|
|
182
|
+
}
|
|
183
|
+
return `${formatRate(throughput?.settledRate ?? 0)} ${unit}`;
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=footer-meters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footer-meters.js","sourceRoot":"","sources":["../src/footer-meters.ts"],"names":[],"mappings":"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"}
|