@vincemakes/kiso-tui 0.39.2 → 0.40.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/dist/compositor.d.ts +29 -3
- package/dist/compositor.js +114 -7
- package/dist/context-ledger.d.ts +5 -0
- package/dist/context-ledger.js +11 -2
- package/dist/editor.d.ts +5 -2
- package/dist/editor.js +69 -9
- package/dist/index.d.ts +3 -3
- package/dist/index.js +6 -3
- package/dist/lines.d.ts +5 -0
- package/dist/lines.js +19 -4
- package/dist/pick-input.d.ts +5 -1
- package/dist/pick-input.js +33 -5
- package/dist/session-picker.d.ts +54 -26
- package/dist/session-picker.js +109 -54
- package/dist/status.d.ts +62 -2
- package/dist/status.js +94 -42
- package/package.json +2 -2
package/dist/status.js
CHANGED
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
* widens to name the new gesture.
|
|
23
23
|
*/
|
|
24
24
|
import { kUnit } from "./lines.js";
|
|
25
|
+
import { meterGlyphs } from "./context-ledger.js";
|
|
26
|
+
/** The compacting row's bar width — short: it shares a row. */
|
|
27
|
+
const BAR_ON_ROW = 6;
|
|
25
28
|
import { elapsedLabel } from "@vincemakes/kiso-tui-cells";
|
|
26
29
|
import { TWINKLE } from "@vincemakes/kiso-tui-cells/render";
|
|
27
30
|
import { displayWidth } from "@vincemakes/kiso-tui-cells/width";
|
|
@@ -85,31 +88,88 @@ export function decodeRate(outputTokens, elapsedMs) {
|
|
|
85
88
|
const rate = Math.round(outputTokens / (elapsedMs / 1000));
|
|
86
89
|
return rate > 0 ? rate : null;
|
|
87
90
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
export function composeRow(head, segments, W) {
|
|
92
|
+
const present = segments.filter((x) => x != null && x.text !== "");
|
|
93
|
+
const join = (xs) => [head, ...xs.map((x) => x.text)].join(" · ");
|
|
94
|
+
const full = join(present);
|
|
95
|
+
if (W === undefined || displayWidth(full) <= W)
|
|
96
|
+
return full;
|
|
97
|
+
// 1. elide every label in its middle
|
|
98
|
+
let row = present.map((x) => (x.kind === "label" ? { ...x, text: elideMiddle(x.text, LABEL_ON_ROW) } : x));
|
|
99
|
+
if (displayWidth(join(row)) <= W)
|
|
100
|
+
return join(row);
|
|
101
|
+
// 2. drop hints from the end, one at a time
|
|
102
|
+
for (let i = row.length - 1; i >= 0; i -= 1) {
|
|
103
|
+
if (row[i].kind !== "hint")
|
|
104
|
+
continue;
|
|
105
|
+
row = [...row.slice(0, i), ...row.slice(i + 1)];
|
|
106
|
+
if (displayWidth(join(row)) <= W)
|
|
107
|
+
return join(row);
|
|
108
|
+
}
|
|
109
|
+
// 3. facts are never dropped: past this point the row is over budget,
|
|
110
|
+
// and it is invariant ①'s to cut — which it will do to the LAST
|
|
111
|
+
// segment, so the order of facts is the order of their importance.
|
|
112
|
+
return join(row);
|
|
113
|
+
}
|
|
114
|
+
/** `retrying 3/10 · network · 4s` — ONE fact: the attempt, the budget it
|
|
115
|
+
* counts against, what failed, and how long until it is tried. Whole
|
|
116
|
+
* seconds, rounded UP, so the row never says 0s while still waiting. */
|
|
117
|
+
export function retrySegment(r) {
|
|
118
|
+
const head = `retrying ${r.attempt}/${r.maxRetries} · ${r.code}`;
|
|
119
|
+
return r.remainingMs > 0 ? `${head} · ${Math.ceil(r.remainingMs / 1000)}s` : head;
|
|
120
|
+
}
|
|
121
|
+
export function runningStatus(glyph, since, outTokens, ctxRatio, tokPerSec = null, W, retry) {
|
|
99
122
|
const out = outTokens !== null ? ` ↓ ${kUnit(outTokens)} tokens` : "";
|
|
100
|
-
// TPS-1: after each call SETTLES within the turn, between the tokens
|
|
101
|
-
// segment and the stop hint. The default is null and that is the honest
|
|
102
|
-
// rule spelled as a default — the recovery flow has no per-call timing
|
|
103
|
-
// state, so its row says nothing rather than guessing.
|
|
104
|
-
const rate = tokPerSec !== null ? ` · ${tokPerSec} tok/s` : "";
|
|
105
123
|
const seconds = Math.max(1, Math.round((Date.now() - since) / 1000));
|
|
106
|
-
return `${glyph} working ${elapsedLabel(seconds)}${out}
|
|
124
|
+
return composeRow(`${glyph} working ${elapsedLabel(seconds)}${out}`, [
|
|
125
|
+
// ADR-0005 Amendment 2: a pending retry is a FACT and sits first — it
|
|
126
|
+
// is the one thing on the row that explains why nothing is arriving,
|
|
127
|
+
// and a retry budget of minutes with nothing on screen reads as a
|
|
128
|
+
// hung session.
|
|
129
|
+
retry != null ? { kind: "fact", text: retrySegment(retry) } : null,
|
|
130
|
+
// TPS-1: after each call SETTLES within the turn, between the tokens
|
|
131
|
+
// segment and the stop hint. The default is null and that is the
|
|
132
|
+
// honest rule spelled as a default — the recovery flow has no
|
|
133
|
+
// per-call timing state, so its row says nothing rather than guessing.
|
|
134
|
+
tokPerSec !== null ? { kind: "fact", text: `${tokPerSec} tok/s` } : null,
|
|
135
|
+
{ kind: "hint", text: "esc stop" },
|
|
136
|
+
{ kind: "hint", text: "alt+⏎ redirect" },
|
|
137
|
+
{ kind: "fact", text: ctxSegment(ctxRatio) },
|
|
138
|
+
], W);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The COMPACTING row (W18): the covered rounds, the pre-call token
|
|
142
|
+
* estimate, and the elapsed seconds — all knowable before the one summary
|
|
143
|
+
* call returns, which has no fraction of its own. Moved here from an
|
|
144
|
+
* inline template in dispatch (0.40.0) so it composes like every other
|
|
145
|
+
* row and has a place for what the launch build adds to it.
|
|
146
|
+
*/
|
|
147
|
+
export function compactingStatus(glyph, rounds, tokens, elapsedSeconds, W, retry, progress) {
|
|
148
|
+
// 0.40.0: with a budget to measure against, the covered size and the bar
|
|
149
|
+
// are ONE fact — what went in, and how much of the output budget has
|
|
150
|
+
// come out. Without a budget the row keeps the covered size alone: the
|
|
151
|
+
// bar never invents a denominator.
|
|
152
|
+
const covered = progress != null && progress.budget !== null && progress.budget > 0
|
|
153
|
+
? `~${kUnit(tokens)} \u2192 ${meterGlyphs(progress.produced / progress.budget, BAR_ON_ROW)} ${kUnit(progress.produced)}/${kUnit(progress.budget)}`
|
|
154
|
+
: `~${kUnit(tokens)} tokens`;
|
|
155
|
+
return composeRow(`${glyph} compacting`, [
|
|
156
|
+
{ kind: "fact", text: `${rounds} rounds` },
|
|
157
|
+
{ kind: "fact", text: covered },
|
|
158
|
+
// why the figure jumped when the usage landed — a HINT, so a narrow
|
|
159
|
+
// row gives it up before the bar, the seconds or the retry
|
|
160
|
+
progress?.reasoningUnseen === true && progress.budget !== null ? { kind: "hint", text: "incl. unstreamed reasoning" } : null,
|
|
161
|
+
{ kind: "fact", text: `${Math.max(0, elapsedSeconds)}s` },
|
|
162
|
+
// ADR-0005 Amendment 2: the summary call retries under the kernel's
|
|
163
|
+
// policy, and a retry here is the same fact it is on the running row.
|
|
164
|
+
retry != null ? { kind: "fact", text: retrySegment(retry) } : null,
|
|
165
|
+
], W);
|
|
107
166
|
}
|
|
108
|
-
/** DF-0330-F1 — how far
|
|
109
|
-
* visible columns keeps a head and a tail:
|
|
110
|
-
* says which binding is driving, and the
|
|
111
|
-
* distinguish one id from its neighbours
|
|
112
|
-
|
|
167
|
+
/** DF-0330-F1 — how far a LABEL may be squeezed on the ROW; the model id
|
|
168
|
+
* is the one there is. Twenty visible columns keeps a head and a tail:
|
|
169
|
+
* `deepseek-v…s-on-0910` still says which binding is driving, and the
|
|
170
|
+
* tail is where the parts that distinguish one id from its neighbours
|
|
171
|
+
* live (`-flash`, `-0910`). Read by `composeRow`. */
|
|
172
|
+
const LABEL_ON_ROW = 20;
|
|
113
173
|
/** Elide in the MIDDLE, keeping the head and the tail. A string already
|
|
114
174
|
* within budget is returned untouched, so this is a no-op for every
|
|
115
175
|
* ordinary model name.
|
|
@@ -169,27 +229,19 @@ function elideMiddle(text, max) {
|
|
|
169
229
|
* No `W` means no dropping, which is what the callers that do not know
|
|
170
230
|
* their width should get: today's row, unchanged.
|
|
171
231
|
*/
|
|
172
|
-
export function idleStatus(tier, model, ctxRatio, meter, W) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
232
|
+
export function idleStatus(tier, model, ctxRatio, meter, W, floorOff = false) {
|
|
233
|
+
return composeRow(`▸ ${tier}`, [
|
|
234
|
+
// 0.40.0: the catastrophe floor is on by default and says nothing;
|
|
235
|
+
// OFF is the state worth seeing, and a fact beside the tier it
|
|
236
|
+
// changes the meaning of.
|
|
237
|
+
floorOff ? { kind: "fact", text: "floor off" } : null,
|
|
238
|
+
{ kind: "hint", text: "/mode to switch" },
|
|
239
|
+
{ kind: "label", text: model },
|
|
240
|
+
meter?.cacheHitPct != null ? { kind: "fact", text: `CH ${Math.round(meter.cacheHitPct)}%` } : null,
|
|
180
241
|
// costUsd deliberately NOT rendered — see StatusMeter.costUsd.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return parts.join(" · ");
|
|
185
|
-
};
|
|
186
|
-
const full = compose(model, true);
|
|
187
|
-
if (W === undefined || displayWidth(full) <= W)
|
|
188
|
-
return full;
|
|
189
|
-
const squeezed = compose(elideMiddle(model, MODEL_ON_ROW), true);
|
|
190
|
-
if (displayWidth(squeezed) <= W)
|
|
191
|
-
return squeezed;
|
|
192
|
-
return compose(elideMiddle(model, MODEL_ON_ROW), false);
|
|
242
|
+
{ kind: "fact", text: ctxSegment(ctxRatio) },
|
|
243
|
+
meter?.tokPerSec != null ? { kind: "fact", text: `${meter.tokPerSec} tok/s` } : null, // TPS-1: last, after the ctx estimate
|
|
244
|
+
], W);
|
|
193
245
|
}
|
|
194
246
|
/** TUI2-R1 (E) — the cache hit rate the status row shows, from the usage
|
|
195
247
|
* the CLI already tracks. The denominator is the TOTAL the model was
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-tui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "kiso tui \u2014 the pure terminal layer (cell renderer, dock, raw editor, diff, palette). Zero runtime dependencies: input is data, output is bytes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://github.com/vincemakes/kiso/tree/main/packages/tui#readme",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@vincemakes/kiso-tui-cells": "0.
|
|
38
|
+
"@vincemakes/kiso-tui-cells": "0.40.0"
|
|
39
39
|
}
|
|
40
40
|
}
|