@aragon-agent/cli 0.6.31 → 0.6.32

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +2957 -2826
  3. package/dist/agent/controller.d.ts +46 -0
  4. package/dist/agent/controller.js +175 -7
  5. package/dist/agent/job-notices.d.ts +143 -0
  6. package/dist/agent/job-notices.js +182 -0
  7. package/dist/agent/reducer.d.ts +21 -1
  8. package/dist/agent/reducer.js +7 -0
  9. package/dist/agent/system-prompt.d.ts +12 -0
  10. package/dist/agent/system-prompt.js +2 -0
  11. package/dist/cli.js +232 -8
  12. package/dist/commands/builtins.js +23 -8
  13. package/dist/commands/perf.d.ts +54 -1
  14. package/dist/commands/perf.js +28 -4
  15. package/dist/config/cli-commands.d.ts +27 -0
  16. package/dist/config/cli-commands.js +54 -1
  17. package/dist/config/env.js +38 -0
  18. package/dist/config/load.js +50 -1
  19. package/dist/config/schema.d.ts +73 -0
  20. package/dist/config/schema.js +34 -0
  21. package/dist/config/store.js +14 -1
  22. package/dist/exec/events.d.ts +29 -3
  23. package/dist/exec/events.js +3 -12
  24. package/dist/exec/runner.d.ts +24 -0
  25. package/dist/exec/runner.js +79 -0
  26. package/dist/input/cursor-report.d.ts +61 -0
  27. package/dist/input/cursor-report.js +97 -0
  28. package/dist/input/stdin-filter.d.ts +22 -0
  29. package/dist/input/stdin-filter.js +48 -3
  30. package/dist/proc/limits.d.ts +76 -2
  31. package/dist/proc/limits.js +83 -2
  32. package/dist/proc/log-mirror.d.ts +59 -0
  33. package/dist/proc/log-mirror.js +203 -0
  34. package/dist/proc/log-ring.d.ts +22 -0
  35. package/dist/proc/log-ring.js +20 -1
  36. package/dist/proc/notices.d.ts +57 -0
  37. package/dist/proc/notices.js +101 -0
  38. package/dist/proc/progress.d.ts +40 -0
  39. package/dist/proc/progress.js +135 -0
  40. package/dist/proc/prompt.d.ts +24 -2
  41. package/dist/proc/prompt.js +51 -2
  42. package/dist/proc/supervisor.d.ts +35 -2
  43. package/dist/proc/supervisor.js +142 -43
  44. package/dist/proc/types.d.ts +89 -1
  45. package/dist/proc/wait.d.ts +57 -0
  46. package/dist/proc/wait.js +133 -0
  47. package/dist/session/persist.js +3 -1
  48. package/dist/session/validate-session.js +17 -0
  49. package/dist/tools/bash-tool.d.ts +12 -1
  50. package/dist/tools/bash-tool.js +100 -13
  51. package/dist/tools/index.d.ts +26 -2
  52. package/dist/tools/index.js +43 -2
  53. package/dist/tools/job-tools.d.ts +54 -0
  54. package/dist/tools/job-tools.js +329 -0
  55. package/dist/tools/proc-tools.d.ts +1 -1
  56. package/dist/tools/proc-tools.js +49 -17
  57. package/dist/ui/ActivityLine.js +7 -0
  58. package/dist/ui/App.js +64 -6
  59. package/dist/ui/ScrollbackShell.d.ts +30 -4
  60. package/dist/ui/ScrollbackShell.js +8 -3
  61. package/dist/ui/StatusBar.d.ts +5 -0
  62. package/dist/ui/StatusBar.js +1 -0
  63. package/dist/ui/Transcript.d.ts +14 -0
  64. package/dist/ui/Transcript.js +14 -2
  65. package/dist/ui/entries/ServiceCard.d.ts +17 -1
  66. package/dist/ui/entries/ServiceCard.js +39 -4
  67. package/dist/ui/layout/status-layout.d.ts +13 -1
  68. package/dist/ui/layout/status-layout.js +10 -0
  69. package/dist/ui/layout/virtual-window.js +14 -1
  70. package/dist/ui/run-status-row.d.ts +12 -0
  71. package/dist/ui/screen.d.ts +17 -4
  72. package/dist/ui/screen.js +14 -6
  73. package/dist/ui/scrollback/LiveTail.d.ts +64 -0
  74. package/dist/ui/scrollback/LiveTail.js +27 -0
  75. package/dist/ui/scrollback/band.d.ts +105 -0
  76. package/dist/ui/scrollback/band.js +109 -0
  77. package/dist/ui/scrollback/budget.d.ts +32 -3
  78. package/dist/ui/scrollback/budget.js +43 -6
  79. package/dist/ui/scrollback/canvas.d.ts +99 -0
  80. package/dist/ui/scrollback/canvas.js +113 -0
  81. package/dist/ui/scrollback/cursor-probe.d.ts +71 -0
  82. package/dist/ui/scrollback/cursor-probe.js +133 -0
  83. package/dist/ui/scrollback/limits.d.ts +49 -0
  84. package/dist/ui/scrollback/limits.js +49 -0
  85. package/dist/ui/scrollback/scrollback-writer.d.ts +104 -16
  86. package/dist/ui/scrollback/scrollback-writer.js +368 -82
  87. package/dist/ui/scrollback/use-scrollback-view.d.ts +11 -0
  88. package/dist/ui/scrollback/use-scrollback-view.js +37 -6
  89. package/dist/ui/scrollback/writer-shapes.d.ts +68 -2
  90. package/dist/ui/scrollback/writer-shapes.js +53 -8
  91. package/package.json +3 -3
@@ -22,4 +22,53 @@ export declare const SCROLLBACK_LIMITS: {
22
22
  readonly maxLiveTailRows: 12;
23
23
  /** ... and as a share of the terminal height. */
24
24
  readonly liveTailRowShare: 0.4;
25
+ /**
26
+ * Live-region `EntryView` instances per frame (release-gate-and-native-panels §6.5).
27
+ *
28
+ * A STRUCTURAL CAP of the same kind as `maxLiveTailRows`: a screenful is at most 12
29
+ * rows plus 4 component instances, so the band costs the same order of magnitude as
30
+ * the full-screen live tail and the render governor and every `memo` boundary keep
31
+ * working. Raising it makes the per-frame cost grow linearly with no windowing.
32
+ */
33
+ readonly liveTailMaxEntries: 4;
34
+ /**
35
+ * Rows the rail needs before the band will show it: `TODO_LIMITS.panelMinRows + 1`.
36
+ *
37
+ * The `+ 1` is not decoration - the rail takes its rows from the same pool as one
38
+ * preview row, and a band that seats the rail but no preview loses the streaming
39
+ * answer entirely (band.ts step 3 / AC-12).
40
+ */
41
+ readonly railBandMinRows: 4;
42
+ /**
43
+ * How long the cursor-position probe waits for `ESC[<row>;<col>R`.
44
+ *
45
+ * A STRUCTURAL CAP, not a preference (tui-bottom-anchored-live-region §4.4): it
46
+ * bounds "how much startup latency a terminal that does not answer may cost". The
47
+ * round trip is under 5 ms locally and under 60 ms over SSH, so 150 ms is
48
+ * generous, and the probe runs once per session — never per frame, never per
49
+ * keystroke. Anything longer and a terminal without CPR support would pay it on
50
+ * every launch.
51
+ */
52
+ readonly anchorProbeTimeoutMs: 150;
53
+ /**
54
+ * Settle window before re-measuring the cursor after a resize.
55
+ *
56
+ * A STRUCTURAL CAP for the same reason: a drag fires dozens of resize events, and
57
+ * one CPR per event would both flood the input channel and make the region jitter.
58
+ * 200 ms is below the point a user perceives as "the input box is stuck".
59
+ */
60
+ readonly anchorReprobeDelayMs: 200;
61
+ /**
62
+ * Structural ceiling on the height of a STARTING canvas.
63
+ *
64
+ * It bounds the bytes a batch may write and the rows the content cache may hold on
65
+ * an extreme terminal (`rows` 3000). Two limits on how it may be applied, both
66
+ * arithmetic rather than taste:
67
+ * - the rows a canvas leaves unused do not disappear, they stay above it as the
68
+ * permanent region, so a smaller starting canvas never loses content;
69
+ * - it must NEVER clip a live canvas. A canvas that has overflowed and scrolled
70
+ * grows to `rows - 1`, and shrinking one back would make the next erase reach
71
+ * above its own top: host output erased, every visible row printed twice.
72
+ */
73
+ readonly maxCanvasRows: 200;
25
74
  };
@@ -22,4 +22,53 @@ export const SCROLLBACK_LIMITS = {
22
22
  maxLiveTailRows: 12,
23
23
  /** ... and as a share of the terminal height. */
24
24
  liveTailRowShare: 0.4,
25
+ /**
26
+ * Live-region `EntryView` instances per frame (release-gate-and-native-panels §6.5).
27
+ *
28
+ * A STRUCTURAL CAP of the same kind as `maxLiveTailRows`: a screenful is at most 12
29
+ * rows plus 4 component instances, so the band costs the same order of magnitude as
30
+ * the full-screen live tail and the render governor and every `memo` boundary keep
31
+ * working. Raising it makes the per-frame cost grow linearly with no windowing.
32
+ */
33
+ liveTailMaxEntries: 4,
34
+ /**
35
+ * Rows the rail needs before the band will show it: `TODO_LIMITS.panelMinRows + 1`.
36
+ *
37
+ * The `+ 1` is not decoration - the rail takes its rows from the same pool as one
38
+ * preview row, and a band that seats the rail but no preview loses the streaming
39
+ * answer entirely (band.ts step 3 / AC-12).
40
+ */
41
+ railBandMinRows: 4,
42
+ /**
43
+ * How long the cursor-position probe waits for `ESC[<row>;<col>R`.
44
+ *
45
+ * A STRUCTURAL CAP, not a preference (tui-bottom-anchored-live-region §4.4): it
46
+ * bounds "how much startup latency a terminal that does not answer may cost". The
47
+ * round trip is under 5 ms locally and under 60 ms over SSH, so 150 ms is
48
+ * generous, and the probe runs once per session — never per frame, never per
49
+ * keystroke. Anything longer and a terminal without CPR support would pay it on
50
+ * every launch.
51
+ */
52
+ anchorProbeTimeoutMs: 150,
53
+ /**
54
+ * Settle window before re-measuring the cursor after a resize.
55
+ *
56
+ * A STRUCTURAL CAP for the same reason: a drag fires dozens of resize events, and
57
+ * one CPR per event would both flood the input channel and make the region jitter.
58
+ * 200 ms is below the point a user perceives as "the input box is stuck".
59
+ */
60
+ anchorReprobeDelayMs: 200,
61
+ /**
62
+ * Structural ceiling on the height of a STARTING canvas.
63
+ *
64
+ * It bounds the bytes a batch may write and the rows the content cache may hold on
65
+ * an extreme terminal (`rows` 3000). Two limits on how it may be applied, both
66
+ * arithmetic rather than taste:
67
+ * - the rows a canvas leaves unused do not disappear, they stay above it as the
68
+ * permanent region, so a smaller starting canvas never loses content;
69
+ * - it must NEVER clip a live canvas. A canvas that has overflowed and scrolled
70
+ * grows to `rows - 1`, and shrinking one back would make the next erase reach
71
+ * above its own top: host output erased, every visible row printed twice.
72
+ */
73
+ maxCanvasRows: 200,
25
74
  };
@@ -1,32 +1,64 @@
1
1
  /**
2
2
  * ScrollbackWriter — the byte contract of native-scroll mode
3
- * (tui-native-terminal-scroll §4.3).
3
+ * (tui-native-terminal-scroll §4.3, extended by tui-bottom-anchored-live-region
4
+ * and re-based on a CANVAS by tui-canvas-bottom-anchor).
4
5
  *
5
6
  * It implements `FrameDiffer`, so `stdout-frame-writer.ts::wrapStdoutForFrames`
6
7
  * binds it to a real stream exactly as it binds the full-screen differ. What it
7
8
  * does differently: the screen is NOT the app's. Settled conversation rows are
8
9
  * printed into the host terminal's own scrollback, and Ink's frame is only a small
9
- * LIVE REGION parked directly under the last printed row.
10
+ * LIVE REGION parked under the last printed row.
10
11
  *
11
- * GEOMETRY IS RELATIVE (D2). A batch is `CUU(H0)` to the top of the previous
12
- * region, erase down, print static rows, print the new region, park one row below
13
- * it. Nothing in that sequence depends on the terminal's absolute row count, so a
14
- * terminal that scrolls underneath it cannot misplace anything.
12
+ * GEOMETRY IS RELATIVE (D2/D4). A batch is `CUU(canvasRows)` to the top of the
13
+ * previous CANVAS, erase down, skip the gap, print the content rows, print the
14
+ * region, park on the last row. Nothing needs the terminal's absolute row count, so
15
+ * a terminal that scrolls underneath cannot misplace anything.
16
+ *
17
+ * THE CANVAS (tui-canvas-bottom-anchor D1/D3). The `rows - 1` rows above the parked
18
+ * row belong to the writer and are always laid out top-down as
19
+ * `[gap][content][region]`. The previous feature put its blank rows between the
20
+ * content and the region (`[content][pad][region]`), which is why a short
21
+ * conversation left a reply floating far above the composer. The blank space now
22
+ * lives above every visible content row, so the newest printed row always touches
23
+ * the top row of the live region, while the composer stays on the last two rows.
24
+ * The gap is SKIPPED with CUD and never written, so a blank row can never be pushed
25
+ * into the scrollback (I-B3).
26
+ *
27
+ * THE PRICE, and it is the only one: the content rows still on the canvas can be
28
+ * re-drawn when the layout changes (a region collapse, a longer draft), because the
29
+ * rows that the region gives back have to move onto the canvas top. That is why the
30
+ * writer keeps a bounded cache of them (`held`, at most `capacity - H` rows). A row
31
+ * that leaves the canvas — pushed into the scrollback or erased at exit — is never
32
+ * written again, so the history the user scrolls through is exactly what was
33
+ * printed, once (I-B5). `anchor: unknown` (nothing measured: no TTY, no filter, no
34
+ * CPR answer) keeps the pre-feature bytes exactly, which is what keeps the old byte
35
+ * tests meaningful (I-B7).
15
36
  *
16
37
  * THE INVARIANTS THAT FAIL SILENTLY IF DROPPED:
17
- * - I-S4 · every batch parks the cursor at (regionBottom + 1, column 0), the same
18
- * place Ink's `previousLineCount = H + 1` expects, so falling back to a raw Ink
19
- * write is still correct.
38
+ * - I-S4 · every batch parks the cursor one row below the region, column 0, the
39
+ * same place Ink's `previousLineCount = H + 1` expects.
20
40
  * - I-S8 · geometry in doubt (foreign write, unparseable chunk) => `prev = null`
21
- * => next batch is a SEED. Never keep erasing on a stale `H0`.
22
- * - I-S14 · after `finalize` EVERY Ink write is swallowed. Ink's unmount renders
23
- * once more (and a 32 ms trailing throttle call can follow); passed through,
24
- * its `eraseLines(H + 1)` would erase the summary and the end of the
25
- * conversation. With an unknown or sub-2-row terminal the frame is still
26
- * parsed so `prev`/`H0` track Ink's own `previousLineCount`.
41
+ * => next batch is a SEED. Never keep erasing on a stale height.
42
+ * - I-S14 · after `finalize` EVERY Ink write is swallowed.
27
43
  * - I-S13 · no emitted row is wider than `cols - 1` (clipped, counted).
44
+ * - I-B1 · a batch ends parked on `rows - 1`, which is what the next `CUU` assumes.
45
+ * - I-B3 · blank rows only ever exist as SKIPPED rows, and a batch that scrolls has
46
+ * `gap = 0`, so the scrollback never receives an empty row from this file.
47
+ * - I-B5 · `held` is a contiguous suffix of the printed content, and `dropped` is
48
+ * exactly the number of rows this batch pushed off the top. The BYTES carry the
49
+ * untrimmed content: the terminal can only scroll away rows it was given.
50
+ * - I-B14 · a re-measurement re-derives `capacity` from `spaceBelow + canvasRows`,
51
+ * never from `spaceBelow` alone: the canvas itself still owns the rows above the
52
+ * cursor, and forgetting them makes the next batch read its own screen as an
53
+ * overflow and print every visible row a second time.
54
+ * - I-B15 · every batch starts by reconciling `top + capacity` with the live
55
+ * `rows - 1` (a resize event can arrive one frame late), and a column change
56
+ * resets before it writes.
57
+ * - I-A9 · the writer never writes a probe byte; it only asks the host, which owns
58
+ * deferral, coalescing and the death switch.
28
59
  */
29
60
  import type { FrameDiffer } from '../frame-differ.js';
61
+ import { type AnchorSource } from './canvas.js';
30
62
  import type { ScrollbackDrain } from './drain.js';
31
63
  export interface ScrollbackWriterOptions {
32
64
  rows: () => number | undefined;
@@ -45,6 +77,17 @@ export interface ScrollbackWriterOptions {
45
77
  * arrive without a new Ink frame, and to flush the rest of an oversized feed.
46
78
  */
47
79
  requestRepaint?: () => void;
80
+ /**
81
+ * Ask the host for one more cursor measurement (resize settled, geometry lost).
82
+ *
83
+ * CONTRACT (P0-3), and the writer does NOT implement any of it: the call can
84
+ * arrive from inside Ink's `stdout.write` (`invalidate` is called by
85
+ * `transform`), so the host MUST defer it with `setImmediate`, MUST coalesce
86
+ * everything inside the window into one `ESC[6n`, and MUST drop it after
87
+ * `finalize`/`dispose`/`exit` — a probe written at a terminal already handed
88
+ * back to the shell leaks `^[[12;1R` onto the user's prompt (I-A9).
89
+ */
90
+ requestAnchorProbe?: () => void;
48
91
  /** Injected clock for the resize grace window. */
49
92
  now?: () => number;
50
93
  }
@@ -63,12 +106,57 @@ export interface ScrollbackWriterStats {
63
106
  elidedEntries: number;
64
107
  resizes: number;
65
108
  repaints: number;
109
+ /** Where the anchor came from: `probe`, one-off `fill`, or never measured. */
110
+ anchorSource: AnchorSource;
111
+ /** Canvas height of the most recent batch — the next erase distance (0 = unknown). */
112
+ anchorCanvasRows: number;
113
+ /** Content rows inside that canvas. */
114
+ anchorHeldRows: number;
115
+ /** Blank rows above the content in that canvas. */
116
+ anchorGapRows: number;
117
+ /** Rows this session pushed into the scrollback (the "is it scrolling" signal). */
118
+ anchorScrollRows: number;
119
+ /** Times the geometry was dropped on purpose (column change, shrink, gap lost). */
120
+ anchorResets: number;
121
+ /** Measurements accepted AFTER the first one — the re-probe count (not resize events). */
122
+ anchorReprobes: number;
123
+ }
124
+ export interface CanvasStats {
125
+ /** `'pinned'` = measured, drawing a canvas; `'unknown'` = legacy append path. */
126
+ state: 'pinned' | 'unknown';
127
+ /** Probe / fill origin; `'none'` = never measured. */
128
+ source: AnchorSource;
129
+ /** Rows of the most recent canvas (0 while unknown). */
130
+ canvasRows: number;
131
+ /** Content rows inside it. */
132
+ heldRows: number;
133
+ /** Blank rows above the content. */
134
+ gapRows: number;
135
+ /** Canvas top row (0 = the canvas starts at the first screen row). */
136
+ top: number;
66
137
  }
67
138
  export interface ScrollbackWriter extends FrameDiffer {
68
139
  /** Idempotent. Returns the exit bytes; afterwards every Ink write is swallowed. */
69
140
  finalize(summary: readonly string[]): string;
70
- /** Rows of the live region as last parked. */
141
+ /**
142
+ * Rows of the LIVE REGION as last parked — the frame height `H`, NOT the canvas
143
+ * (P1-4). `/perf` prints this as "scrollback region N rows", and it has to keep
144
+ * meaning the same number; the canvas height is `anchorStats().canvasRows`.
145
+ */
71
146
  regionRows(): number;
72
147
  scrollbackStats(): ScrollbackWriterStats;
148
+ /**
149
+ * The host measured how many blank rows are left below the parked cursor
150
+ * (`0` = already on the last row). Writes no bytes; the next batch — or the
151
+ * `repaint()` the host schedules right after — consumes it.
152
+ *
153
+ * Idempotent and overwritable: the post-resize re-probe calls it again, and it
154
+ * must then arrive at the SAME capacity the resize delta did (I-B14). Side
155
+ * effect: sets `anchorDirty`, which is what lets `repaint()` redraw while the
156
+ * feed is idle (P0-2).
157
+ */
158
+ setAnchorSpaceBelow(spaceBelow: number, source: AnchorSource): void;
159
+ /** For `/perf` and tests. Produces no bytes. */
160
+ anchorStats(): CanvasStats;
73
161
  }
74
162
  export declare function createScrollbackWriter(o: ScrollbackWriterOptions): ScrollbackWriter;