@aragon-agent/cli 0.6.37 → 0.6.38
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/commands/perf.d.ts +7 -1
- package/dist/commands/perf.js +2 -2
- package/dist/ui/scrollback/canvas.d.ts +3 -2
- package/dist/ui/scrollback/canvas.js +3 -2
- package/dist/ui/scrollback/scrollback-writer.d.ts +13 -1
- package/dist/ui/scrollback/scrollback-writer.js +109 -10
- package/package.json +2 -2
package/dist/commands/perf.d.ts
CHANGED
|
@@ -157,6 +157,12 @@ export interface ScrollbackPerf {
|
|
|
157
157
|
elidedEntries: number;
|
|
158
158
|
guardHits: number;
|
|
159
159
|
guardHitsOutsideResize: number;
|
|
160
|
+
/**
|
|
161
|
+
* Frames taller than the screen, drawn after giving way at their TOP (I-B17). They are
|
|
162
|
+
* no longer swallowed, so `guardHits` stopped being the whole story: a region that
|
|
163
|
+
* outgrew its terminal shows up here instead of there.
|
|
164
|
+
*/
|
|
165
|
+
tallFramesTrimmed: number;
|
|
160
166
|
clippedRows: number;
|
|
161
167
|
fallbacks: number;
|
|
162
168
|
resizes: number;
|
|
@@ -184,7 +190,7 @@ export interface ScrollbackPerf {
|
|
|
184
190
|
anchorTop: number;
|
|
185
191
|
/** Rows this session pushed into the terminal's own scrollback. */
|
|
186
192
|
anchorScrollRows: number;
|
|
187
|
-
/** Times the geometry was dropped on purpose (column change,
|
|
193
|
+
/** Times the geometry was dropped on purpose (column change, nothing left to pin). */
|
|
188
194
|
anchorResets: number;
|
|
189
195
|
/** Measurements accepted after the first: the re-probe count, not the resize count. */
|
|
190
196
|
anchorReprobes: number;
|
package/dist/commands/perf.js
CHANGED
|
@@ -86,8 +86,8 @@ function formatScrollbackLine() {
|
|
|
86
86
|
`diff ${stats.framesDiff}, full ${stats.framesFull}, repaint ${stats.repaints} - ` +
|
|
87
87
|
`static rows ${stats.staticRowsWritten} - elided ${stats.elidedEntries} - ` +
|
|
88
88
|
`guardHits ${stats.guardHits} (outside resize ${stats.guardHitsOutsideResize}) - ` +
|
|
89
|
-
`
|
|
90
|
-
`resizes ${stats.resizes}`,
|
|
89
|
+
`trimmed ${stats.tallFramesTrimmed} - clippedRows ${stats.clippedRows} - ` +
|
|
90
|
+
`fallbacks ${stats.fallbacks} - resizes ${stats.resizes}`,
|
|
91
91
|
];
|
|
92
92
|
}
|
|
93
93
|
function mb(bytes) {
|
|
@@ -78,8 +78,9 @@ export declare function screenCapacity(rows: number | undefined): number;
|
|
|
78
78
|
export declare function clampSpaceBelow(spaceBelow: number, rows: number | undefined): number;
|
|
79
79
|
/**
|
|
80
80
|
* A terminal that grew adds rows BELOW the cursor, so the capacity grows by the
|
|
81
|
-
* exact difference (D6). Shrinking is NOT handled here:
|
|
82
|
-
*
|
|
81
|
+
* exact difference (D6). Shrinking is NOT handled here either: the writer keeps the
|
|
82
|
+
* rows the screen still has and re-pins them (I-B17) instead of guessing a smaller
|
|
83
|
+
* value, which would only hide a lost measurement (D5).
|
|
83
84
|
*/
|
|
84
85
|
export declare function applyResizeGrowth(capacity: number, rowsPrev: number, rowsNow: number): number;
|
|
85
86
|
/**
|
|
@@ -48,8 +48,9 @@ export function clampSpaceBelow(spaceBelow, rows) {
|
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* A terminal that grew adds rows BELOW the cursor, so the capacity grows by the
|
|
51
|
-
* exact difference (D6). Shrinking is NOT handled here:
|
|
52
|
-
*
|
|
51
|
+
* exact difference (D6). Shrinking is NOT handled here either: the writer keeps the
|
|
52
|
+
* rows the screen still has and re-pins them (I-B17) instead of guessing a smaller
|
|
53
|
+
* value, which would only hide a lost measurement (D5).
|
|
53
54
|
*/
|
|
54
55
|
export function applyResizeGrowth(capacity, rowsPrev, rowsNow) {
|
|
55
56
|
// No capacity to grow: a broken number must not become a canvas by arithmetic.
|
|
@@ -54,6 +54,16 @@
|
|
|
54
54
|
* - I-B15 · every batch starts by reconciling `top + capacity` with the live
|
|
55
55
|
* `rows - 1` (a resize event can arrive one frame late), and a column change
|
|
56
56
|
* resets before it writes.
|
|
57
|
+
* - I-B17 · a SHRINK re-pins the canvas, it never drops it. The canvas keeps
|
|
58
|
+
* `min(canvasRows, screenCapacity(rows))` rows — the screen keeps the rows nearest
|
|
59
|
+
* the parked cursor, so the survivors are its SUFFIX — and its top moves down to
|
|
60
|
+
* `capacity - kept`, which preserves LB-3. Dropping the cache instead made the next
|
|
61
|
+
* batch a SEED, and a seed assumes nothing is below the cursor
|
|
62
|
+
* (`writer-shapes.ts:60-64`); after a shrink that is false, so the batch overflowed
|
|
63
|
+
* the screen and the previous frame — status row included — went into the
|
|
64
|
+
* scrollback: the same row in two places. For the same reason a frame TALLER than
|
|
65
|
+
* the screen gives way at its TOP instead of being swallowed whole: the composer
|
|
66
|
+
* and the status line are the two rows a human is interacting with.
|
|
57
67
|
* - I-A9 · the writer never writes a probe byte; it only asks the host, which owns
|
|
58
68
|
* deferral, coalescing and the death switch.
|
|
59
69
|
*/
|
|
@@ -102,6 +112,8 @@ export interface ScrollbackWriterStats {
|
|
|
102
112
|
guardHits: number;
|
|
103
113
|
/** The subset NOT explained by a recent resize: the real defect signal. */
|
|
104
114
|
guardHitsOutsideResize: number;
|
|
115
|
+
/** Frames taller than the screen, drawn after giving way at their TOP (I-B17). */
|
|
116
|
+
tallFramesTrimmed: number;
|
|
105
117
|
clippedRows: number;
|
|
106
118
|
elidedEntries: number;
|
|
107
119
|
resizes: number;
|
|
@@ -116,7 +128,7 @@ export interface ScrollbackWriterStats {
|
|
|
116
128
|
anchorGapRows: number;
|
|
117
129
|
/** Rows this session pushed into the scrollback (the "is it scrolling" signal). */
|
|
118
130
|
anchorScrollRows: number;
|
|
119
|
-
/** Times the geometry was dropped on purpose (column change,
|
|
131
|
+
/** Times the geometry was dropped on purpose (column change, nothing left to pin). */
|
|
120
132
|
anchorResets: number;
|
|
121
133
|
/** Measurements accepted AFTER the first one — the re-probe count (not resize events). */
|
|
122
134
|
anchorReprobes: number;
|
|
@@ -54,6 +54,16 @@
|
|
|
54
54
|
* - I-B15 · every batch starts by reconciling `top + capacity` with the live
|
|
55
55
|
* `rows - 1` (a resize event can arrive one frame late), and a column change
|
|
56
56
|
* resets before it writes.
|
|
57
|
+
* - I-B17 · a SHRINK re-pins the canvas, it never drops it. The canvas keeps
|
|
58
|
+
* `min(canvasRows, screenCapacity(rows))` rows — the screen keeps the rows nearest
|
|
59
|
+
* the parked cursor, so the survivors are its SUFFIX — and its top moves down to
|
|
60
|
+
* `capacity - kept`, which preserves LB-3. Dropping the cache instead made the next
|
|
61
|
+
* batch a SEED, and a seed assumes nothing is below the cursor
|
|
62
|
+
* (`writer-shapes.ts:60-64`); after a shrink that is false, so the batch overflowed
|
|
63
|
+
* the screen and the previous frame — status row included — went into the
|
|
64
|
+
* scrollback: the same row in two places. For the same reason a frame TALLER than
|
|
65
|
+
* the screen gives way at its TOP instead of being swallowed whole: the composer
|
|
66
|
+
* and the status line are the two rows a human is interacting with.
|
|
57
67
|
* - I-A9 · the writer never writes a probe byte; it only asks the host, which owns
|
|
58
68
|
* deferral, coalescing and the death switch.
|
|
59
69
|
*/
|
|
@@ -110,6 +120,7 @@ export function createScrollbackWriter(o) {
|
|
|
110
120
|
fallbacks: 0, guardHits: 0, guardHitsOutsideResize: 0, clippedRows: 0, elidedEntries: 0,
|
|
111
121
|
resizes: 0, repaints: 0, anchorSource: 'none', anchorCanvasRows: 0, anchorHeldRows: 0,
|
|
112
122
|
anchorGapRows: 0, anchorScrollRows: 0, anchorResets: 0, anchorReprobes: 0,
|
|
123
|
+
tallFramesTrimmed: 0,
|
|
113
124
|
};
|
|
114
125
|
const liveCols = () => {
|
|
115
126
|
const cols = o.cols();
|
|
@@ -205,6 +216,68 @@ export function createScrollbackWriter(o) {
|
|
|
205
216
|
stats.bytesWritten += bytes.length;
|
|
206
217
|
return bytes;
|
|
207
218
|
};
|
|
219
|
+
/**
|
|
220
|
+
* Drop from the canvas cache the rows a shorter screen no longer has (I-B17).
|
|
221
|
+
*
|
|
222
|
+
* A shrink deletes rows ABOVE the parked cursor, so what survives is the canvas's
|
|
223
|
+
* SUFFIX: the gap goes first, then the oldest content rows. Trimming the cache is
|
|
224
|
+
* what lets the next batch be a DIFF against what is really on screen. Dropping the
|
|
225
|
+
* cache (`reset()`) made it a SEED instead, and a seed assumes nothing is below the
|
|
226
|
+
* cursor (`writer-shapes.ts:60-64`) — after a shrink that is false, so the batch
|
|
227
|
+
* overflowed and the previous frame, status row included, was pushed into the
|
|
228
|
+
* scrollback instead of being erased.
|
|
229
|
+
*/
|
|
230
|
+
const trimCanvasCache = (capacity) => {
|
|
231
|
+
const lost = canvasRows - capacity;
|
|
232
|
+
if (lost <= 0)
|
|
233
|
+
return;
|
|
234
|
+
const lostFromGap = Math.min(lost, gapRows);
|
|
235
|
+
gapRows -= lostFromGap;
|
|
236
|
+
held = held.slice(Math.min(lost - lostFromGap, held.length));
|
|
237
|
+
prev = prev === null ? null : prev.slice(lost);
|
|
238
|
+
canvasRows = capacity;
|
|
239
|
+
// A drastic shrink can eat into the region itself (E2). `frameRows` is the height
|
|
240
|
+
// the next diff is aligned against, so it may never claim rows that are gone.
|
|
241
|
+
frameRows = Math.min(frameRows, Math.max(0, capacity - gapRows - held.length));
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* Re-pin a canvas the terminal just got SHORTER, instead of dropping it (I-B17).
|
|
245
|
+
*
|
|
246
|
+
* `top` moves DOWN (`capacity - kept`) because the rows the screen lost were the ones
|
|
247
|
+
* above the cursor: the parked row is still `rows - 1`, so LB-3 holds for the same
|
|
248
|
+
* reason it always did.
|
|
249
|
+
*
|
|
250
|
+
* `capacity < 1` keeps the old answer (E3): a terminal with no row above the parked one
|
|
251
|
+
* has no canvas left to address, and re-measuring is then the only honest move. Columns
|
|
252
|
+
* are deliberately NOT handled here: a width change re-flows the rows themselves, so
|
|
253
|
+
* the cache describes nothing and still resets (D5 / E4).
|
|
254
|
+
*/
|
|
255
|
+
const repinShrunk = (rows) => {
|
|
256
|
+
const room = screenCapacity(rows);
|
|
257
|
+
// Only a MEASURED canvas may be re-pinned. `unknown` means nothing ever established
|
|
258
|
+
// that the cursor sits on the last row, and the arithmetic below starts from exactly
|
|
259
|
+
// that assumption; the append path erases nothing, so it stays the honest answer and
|
|
260
|
+
// keeps its bytes exactly as they were (I-B7).
|
|
261
|
+
const capacity = anchor.kind === 'pinned' ? Math.min(canvasRows, room) : 0;
|
|
262
|
+
if (capacity < 1) {
|
|
263
|
+
reset();
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
trimCanvasCache(capacity);
|
|
267
|
+
anchor = { kind: 'pinned', top: room - capacity, capacity };
|
|
268
|
+
seedDownToTop = 0;
|
|
269
|
+
lastRows = rows;
|
|
270
|
+
// The re-draw this answers is the host's, deferred and coalesced (P0-2 / P0-3):
|
|
271
|
+
// `anchorDirty` is what lets a `repaint()` land while the feed is idle.
|
|
272
|
+
anchorDirty = true;
|
|
273
|
+
publishAnchorStats();
|
|
274
|
+
// Re-measure anyway (I-B14). Keeping the canvas is a bet that the parked row is still
|
|
275
|
+
// `rows - 1`, and only a measurement can confirm the terminal agrees; dropping the
|
|
276
|
+
// probe here would leave a wrong geometry with nothing left to correct it. A drag
|
|
277
|
+
// that fires dozens of resize events still costs one probe: the host coalesces them
|
|
278
|
+
// (P1-3 / E6), and `anchorReprobes` counts measurements, never requests.
|
|
279
|
+
requestProbe();
|
|
280
|
+
};
|
|
208
281
|
/**
|
|
209
282
|
* Bring `top + capacity` back in line with the live `rows - 1` BEFORE the batch
|
|
210
283
|
* reads either of them (§6.11, [P0-2]).
|
|
@@ -235,7 +308,10 @@ export function createScrollbackWriter(o) {
|
|
|
235
308
|
lastRows = rows;
|
|
236
309
|
return;
|
|
237
310
|
}
|
|
238
|
-
|
|
311
|
+
// The `resize` event can arrive a whole frame late, and on a terminal that never
|
|
312
|
+
// answers CPR it never arrives at all: this batch is then the last chance to keep
|
|
313
|
+
// the canvas instead of dropping it (I-B17, W6).
|
|
314
|
+
repinShrunk(rows);
|
|
239
315
|
};
|
|
240
316
|
const afterCanvasBatch = (layout, heldNext, frame, raw, cols, rows) => {
|
|
241
317
|
prev = [...new Array(layout.gap).fill(''), ...heldNext, ...frame];
|
|
@@ -402,6 +478,23 @@ export function createScrollbackWriter(o) {
|
|
|
402
478
|
canvasRows = lines.length;
|
|
403
479
|
gapRows = 0;
|
|
404
480
|
};
|
|
481
|
+
/**
|
|
482
|
+
* Give way at the TOP of a frame taller than the screen (I-B17 / W4).
|
|
483
|
+
*
|
|
484
|
+
* Such a frame cannot be addressed as ONE block, and swallowing it left the PREVIOUS
|
|
485
|
+
* frame on screen — which is how the status row came to be printed twice. The region's
|
|
486
|
+
* last rows are the composer and the status line, the two rows a human is interacting
|
|
487
|
+
* with, so the oldest rows go first and the next (shorter) frame restores the rest.
|
|
488
|
+
*
|
|
489
|
+
* BOTH entry points that draw a frame need this: `repaint()` replays the frame it kept,
|
|
490
|
+
* and that frame can predate a shrink that has since made the screen shorter.
|
|
491
|
+
*/
|
|
492
|
+
const giveWayAtTop = (lines, rows) => {
|
|
493
|
+
if (lines.length + 1 <= rows)
|
|
494
|
+
return lines;
|
|
495
|
+
stats.tallFramesTrimmed += 1;
|
|
496
|
+
return lines.slice(lines.length - (rows - 1));
|
|
497
|
+
};
|
|
405
498
|
const transform = (chunk) => {
|
|
406
499
|
if (finalized)
|
|
407
500
|
return '';
|
|
@@ -419,12 +512,7 @@ export function createScrollbackWriter(o) {
|
|
|
419
512
|
}
|
|
420
513
|
if (lines === null)
|
|
421
514
|
return countFallback(!firstChunk);
|
|
422
|
-
|
|
423
|
-
// Unreachable by construction (I-S1); never erase on a guess.
|
|
424
|
-
reset();
|
|
425
|
-
return '';
|
|
426
|
-
}
|
|
427
|
-
return assemble(lines, rows, liveCols(), 'frame');
|
|
515
|
+
return assemble(giveWayAtTop(lines, rows), rows, liveCols(), 'frame');
|
|
428
516
|
};
|
|
429
517
|
const repaint = () => {
|
|
430
518
|
repaintQueued = false;
|
|
@@ -437,7 +525,10 @@ export function createScrollbackWriter(o) {
|
|
|
437
525
|
const rows = liveRows();
|
|
438
526
|
if (rows === undefined || rows < 2)
|
|
439
527
|
return '';
|
|
440
|
-
|
|
528
|
+
// The kept frame may predate a shrink, so a replay gets the same top give-way as a
|
|
529
|
+
// live frame (W4): drawing it whole would overflow the new screen and push this
|
|
530
|
+
// session's own rows into the scrollback through the repaint door.
|
|
531
|
+
return assemble(giveWayAtTop(prevRawFrame, rows), rows, liveCols(), 'repaint');
|
|
441
532
|
};
|
|
442
533
|
const finalize = (summary) => {
|
|
443
534
|
if (finalized)
|
|
@@ -478,8 +569,10 @@ export function createScrollbackWriter(o) {
|
|
|
478
569
|
}
|
|
479
570
|
if (rowsNow !== undefined && lastRows !== undefined) {
|
|
480
571
|
if (rowsNow < lastRows) {
|
|
481
|
-
//
|
|
482
|
-
reset
|
|
572
|
+
// I-B17: the screen kept the rows nearest the cursor, so re-pin the canvas onto
|
|
573
|
+
// them instead of dropping it. A reset made the next batch a seed over a screen
|
|
574
|
+
// that is not empty, which is what left the old status row in the scrollback.
|
|
575
|
+
repinShrunk(rowsNow);
|
|
483
576
|
return;
|
|
484
577
|
}
|
|
485
578
|
if (anchor.kind === 'pinned') {
|
|
@@ -503,6 +596,12 @@ export function createScrollbackWriter(o) {
|
|
|
503
596
|
return;
|
|
504
597
|
const rows = liveRows();
|
|
505
598
|
const capacity = screenCapacity(rows);
|
|
599
|
+
// I-B17: a measurement can arrive after the terminal got shorter without ever
|
|
600
|
+
// announcing it (`rows` is read live, the `resize` event is optional). The cache
|
|
601
|
+
// must then stop describing rows the screen has already dropped, or the reach
|
|
602
|
+
// below would hand the next batch an erase distance longer than the canvas left.
|
|
603
|
+
if (rows !== undefined)
|
|
604
|
+
trimCanvasCache(capacity);
|
|
506
605
|
// I-B14: the report is "rows BELOW the cursor", and the canvas also still owns
|
|
507
606
|
// `canvasRows` rows ABOVE it, so the reachable height is their sum. Keeping only
|
|
508
607
|
// the first term collapses the canvas onto the parked row, and the next batch
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aragon-agent/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.38",
|
|
4
4
|
"description": "AragonAgent CLI — a Claude-Code / Codex-style interactive terminal UI (TUI) on top of the @aragon-agent/core engine. Installable and runnable via npm / npx.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AragonAgent contributors",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"prepublishOnly": "npm run build"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@aragon-agent/core": "^0.2.
|
|
59
|
+
"@aragon-agent/core": "^0.2.50",
|
|
60
60
|
"chalk": "^5.6.2",
|
|
61
61
|
"cli-highlight": "^2.0.0",
|
|
62
62
|
"commander": "^12.1.0",
|