@aragon-agent/cli 0.6.33 → 0.6.35
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 +2305 -2242
- package/README.md +119 -18
- package/dist/agent/controller.d.ts +29 -0
- package/dist/agent/controller.js +42 -1
- package/dist/agent/kit-apply.d.ts +56 -0
- package/dist/agent/kit-apply.js +209 -0
- package/dist/agent/model-profile-settings.d.ts +28 -0
- package/dist/agent/model-profile-settings.js +142 -29
- package/dist/agent/reducer.d.ts +1 -1
- package/dist/commands/builtins.js +4 -0
- package/dist/commands/kit-command.d.ts +20 -0
- package/dist/commands/kit-command.js +131 -0
- package/dist/commands/perf.d.ts +10 -0
- package/dist/commands/perf.js +1 -1
- package/dist/config/kit-resolution.d.ts +93 -0
- package/dist/config/kit-resolution.js +226 -0
- package/dist/config/model-kits.d.ts +224 -0
- package/dist/config/model-kits.js +302 -0
- package/dist/config/model-profile-store.d.ts +25 -0
- package/dist/config/model-profiles.d.ts +41 -3
- package/dist/config/model-profiles.js +46 -5
- package/dist/config/schema.d.ts +16 -0
- package/dist/input/keymap.js +18 -4
- package/dist/ui/App.js +135 -15
- package/dist/ui/Header.d.ts +9 -1
- package/dist/ui/Header.js +8 -2
- package/dist/ui/PromptInput.js +1 -1
- package/dist/ui/ScrollbackShell.d.ts +38 -13
- package/dist/ui/ScrollbackShell.js +4 -3
- package/dist/ui/StatusBar.d.ts +13 -0
- package/dist/ui/StatusBar.js +10 -3
- package/dist/ui/TeamPanel.d.ts +12 -0
- package/dist/ui/TeamPanel.js +10 -31
- package/dist/ui/Transcript.js +22 -2
- package/dist/ui/entries/TeamCard.d.ts +18 -2
- package/dist/ui/entries/TeamCard.js +47 -13
- package/dist/ui/layout/header-hints.d.ts +11 -1
- package/dist/ui/layout/header-hints.js +35 -3
- package/dist/ui/layout/kit-chip.d.ts +50 -0
- package/dist/ui/layout/kit-chip.js +81 -0
- package/dist/ui/layout/virtual-window.js +9 -0
- package/dist/ui/overlays/HelpOverlay.js +7 -0
- package/dist/ui/overlays/KitEditor.d.ts +23 -0
- package/dist/ui/overlays/KitEditor.js +101 -0
- package/dist/ui/overlays/KitManager.d.ts +23 -0
- package/dist/ui/overlays/KitManager.js +104 -0
- package/dist/ui/overlays/KitPicker.d.ts +49 -0
- package/dist/ui/overlays/KitPicker.js +58 -0
- package/dist/ui/overlays/ModelProfilePicker.js +16 -6
- package/dist/ui/overlays/SettingsScreen.js +139 -16
- package/dist/ui/overlays/kit-picker-state.d.ts +87 -0
- package/dist/ui/overlays/kit-picker-state.js +136 -0
- package/dist/ui/overlays/kit-settings-state.d.ts +129 -0
- package/dist/ui/overlays/kit-settings-state.js +267 -0
- package/dist/ui/overlays/model-profile-state.d.ts +26 -3
- package/dist/ui/overlays/model-profile-state.js +59 -6
- package/dist/ui/scrollback/LiveTail.d.ts +44 -10
- package/dist/ui/scrollback/LiveTail.js +20 -4
- package/dist/ui/scrollback/limits.d.ts +16 -0
- package/dist/ui/scrollback/limits.js +16 -0
- package/dist/ui/scrollback/live-preview.js +10 -3
- package/dist/ui/scrollback/overflow-print.d.ts +108 -0
- package/dist/ui/scrollback/overflow-print.js +120 -0
- package/dist/ui/scrollback/renderer.d.ts +11 -1
- package/dist/ui/scrollback/renderer.js +53 -25
- package/dist/ui/scrollback/tail-anchor.d.ts +68 -0
- package/dist/ui/scrollback/tail-anchor.js +77 -0
- package/dist/ui/scrollback/tail-window.d.ts +21 -8
- package/dist/ui/scrollback/tail-window.js +26 -10
- package/dist/ui/scrollback/use-scrollback-view.d.ts +38 -10
- package/dist/ui/scrollback/use-scrollback-view.js +372 -85
- package/dist/ui/team-status.d.ts +141 -0
- package/dist/ui/team-status.js +224 -0
- package/dist/ui/transcript-text.js +13 -4
- package/dist/ui/use-wheel-routing.js +2 -2
- package/package.json +2 -2
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team status — the ONE authority that answers "what state is this child in, and
|
|
3
|
+
* how is that drawn / counted / timed" (tui-subagent-double-roster A-1).
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS FILE EXISTS. The phase -> glyph / colour / count question used to be
|
|
6
|
+
* answered by nine copies in seven files, and they disagreed. The transcript card
|
|
7
|
+
* asked `isOk(run) ? toolDone : toolError`, so six of the eight phases - every
|
|
8
|
+
* child that was still working - were painted with the FAILURE glyph, and an
|
|
9
|
+
* aborted child was counted as a failed one forever. That is not a palette
|
|
10
|
+
* mistake, it is a judgement implemented in the wrong number of places.
|
|
11
|
+
*
|
|
12
|
+
* THE VOCABULARY IS THE DOMAIN'S (analysis A-1' item 3). `ChildStatus` reuses
|
|
13
|
+
* `SubagentPhase`'s own words - `queued` / `running` / `done` / `failed` /
|
|
14
|
+
* `aborted` - and never invents a second dictionary (`pending` was proposed and
|
|
15
|
+
* rejected: `SubagentPhase` does not have it, and two words for one state is how
|
|
16
|
+
* the tenth copy gets written).
|
|
17
|
+
*
|
|
18
|
+
* FIVE STATES, NOT FOUR (A-1' item 2). `aborted` is NOT a failure. The runtime
|
|
19
|
+
* distinguishes them on purpose (`Runtime.settlePhase`: aborted first, then the
|
|
20
|
+
* timeout, then the error), and a user pressing Esc must not read their own
|
|
21
|
+
* action back as "3 failed". So `aborted` takes `glyphs.warn` and
|
|
22
|
+
* `theme.noticeWarn`, which collide with neither the done tick nor the failed
|
|
23
|
+
* cross in either glyph tier.
|
|
24
|
+
*
|
|
25
|
+
* ASCII ONLY: `src/ui/**` is inside `glyphs.test.ts`'s static scan, so every
|
|
26
|
+
* symbol here is injected through `pickGlyphs(caps)`.
|
|
27
|
+
*/
|
|
28
|
+
import type { Entry } from '../agent/reducer.js';
|
|
29
|
+
import type { SubagentRun } from '../team/types.js';
|
|
30
|
+
import type { Glyphs } from './glyphs.js';
|
|
31
|
+
import type { Theme } from './theme.js';
|
|
32
|
+
/**
|
|
33
|
+
* The five readings a child can have on screen. A TOTAL mapping of
|
|
34
|
+
* `SubagentPhase`, which is why `childStatus` needs no guard clauses: `starting`,
|
|
35
|
+
* `thinking`, `tool` and `waiting` are all "this child is working right now", and
|
|
36
|
+
* the per-row activity column is what tells them apart.
|
|
37
|
+
*/
|
|
38
|
+
export type ChildStatus = 'queued' | 'running' | 'done' | 'failed' | 'aborted';
|
|
39
|
+
/**
|
|
40
|
+
* The status of one child.
|
|
41
|
+
*
|
|
42
|
+
* A LOOKUP TABLE RATHER THAN A SWITCH, because the mapping is the whole content
|
|
43
|
+
* of this function: a `Record` keyed by the phase union is exhaustive at compile
|
|
44
|
+
* time (a new phase is a missing-key error) and cannot drift from the type.
|
|
45
|
+
*/
|
|
46
|
+
export declare function childStatus(run: SubagentRun): ChildStatus;
|
|
47
|
+
/** Per-status totals of a roster. `total` is the roster size, not the sum. */
|
|
48
|
+
export interface RosterCounts {
|
|
49
|
+
queued: number;
|
|
50
|
+
running: number;
|
|
51
|
+
done: number;
|
|
52
|
+
failed: number;
|
|
53
|
+
aborted: number;
|
|
54
|
+
total: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Count a roster once, by status.
|
|
58
|
+
*
|
|
59
|
+
* THE HEADER, THE CARD HEADLINE AND THE STATUS CHIP ALL READ THIS. Before it
|
|
60
|
+
* existed the header filtered `phase !== 'queued' && ...` for "running" and the
|
|
61
|
+
* card subtracted `runs.length - isOk(runs)`, so the same dispatch reported
|
|
62
|
+
* `0 running` above two rows reading `queued` and `1 failed` for a child that had
|
|
63
|
+
* never started.
|
|
64
|
+
*/
|
|
65
|
+
export declare function rosterCounts(runs: readonly SubagentRun[]): RosterCounts;
|
|
66
|
+
/** True while the child is doing something - the only state that animates. */
|
|
67
|
+
export declare function isRunningStatus(status: ChildStatus): boolean;
|
|
68
|
+
/** True once the child's outcome is final, whatever it is. */
|
|
69
|
+
export declare function isSettledStatus(status: ChildStatus): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* The static marker for a status.
|
|
72
|
+
*
|
|
73
|
+
* `running` gets `spinnerStill` because the animated `<Spinner>` is the caller's
|
|
74
|
+
* job: it needs `reducedMotion` and `caps.unicode`, and a component that owns its
|
|
75
|
+
* own `ink-spinner` is what `spinner-census.test.ts` counts. The caller swaps this
|
|
76
|
+
* glyph for the spinner when it may animate.
|
|
77
|
+
*/
|
|
78
|
+
export declare function statusGlyph(status: ChildStatus, glyphs: Glyphs): string;
|
|
79
|
+
/**
|
|
80
|
+
* The marker padded to the tier's widest one, for the leading cell of a row.
|
|
81
|
+
*
|
|
82
|
+
* THE PADDING IS NOT COSMETIC (A-1'a): the ASCII tier spells `[ok]` (4), `[x]` (3)
|
|
83
|
+
* and `!` (1), so an unpadded marker cell starts every label at a different
|
|
84
|
+
* column and the roster stops reading as a table. `TodoPanel` pads its marker cell
|
|
85
|
+
* for the same reason. The Unicode tier's markers are all one cell wide, so the
|
|
86
|
+
* padding is a no-op there.
|
|
87
|
+
*/
|
|
88
|
+
export declare function statusMarkerCell(status: ChildStatus, glyphs: Glyphs): string;
|
|
89
|
+
/**
|
|
90
|
+
* The colour of a status. One reading for the roster row, the card row and the
|
|
91
|
+
* card's rail marker.
|
|
92
|
+
*
|
|
93
|
+
* `aborted` deliberately does NOT share `toolError` with `failed`: an abort is the
|
|
94
|
+
* user's own Esc, and the analysis found that painting it red made a cancelled
|
|
95
|
+
* dispatch permanently unreadable in the history.
|
|
96
|
+
*/
|
|
97
|
+
export declare function statusColor(status: ChildStatus, theme: Theme): string | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* How long this child has been (or was) working, in ms.
|
|
100
|
+
*
|
|
101
|
+
* `endedAt ?? now`, NOT `endedAt ?? 0`. The transcript card used the latter, which
|
|
102
|
+
* made every RUNNING child read `0ms` while the roster beside it read `1m05s` for
|
|
103
|
+
* the same child - the pair of contradictory readouts the reporter screenshotted.
|
|
104
|
+
*/
|
|
105
|
+
export declare function childElapsedMs(run: SubagentRun, now: number): number;
|
|
106
|
+
/**
|
|
107
|
+
* The roster header's count section, e.g. `2 running · 0 done · 1 queued`.
|
|
108
|
+
*
|
|
109
|
+
* `queued` IS COUNTED (the old `isRunning` excluded it, so a fresh dispatch
|
|
110
|
+
* announced `0 running · 0 done` above five rows that each said `queued`), and a
|
|
111
|
+
* non-zero bucket is always named even when nothing is running - a roster whose
|
|
112
|
+
* children are all `aborted` must say so rather than `0 running · 0 done`.
|
|
113
|
+
*
|
|
114
|
+
* WIDTH LADDER: narrow terminals drop the least urgent clause first, in the order
|
|
115
|
+
* `done`, `aborted`, `failed`, `queued`; `running` is never dropped. The order is
|
|
116
|
+
* a product decision, not a detail: while a dispatch is live, what is happening
|
|
117
|
+
* now outranks what has already finished, and the per-row activity column carries
|
|
118
|
+
* the finished children's own facts anyway.
|
|
119
|
+
*/
|
|
120
|
+
export declare function rosterHeadline(counts: RosterCounts, glyphs: Glyphs, cols: number): string;
|
|
121
|
+
/**
|
|
122
|
+
* The settled card's outcome buckets, e.g. `2 ok, 1 aborted, 1 failed`.
|
|
123
|
+
*
|
|
124
|
+
* `ok` is always shown (it is the denominator a reader compares against) and a
|
|
125
|
+
* zero bucket is omitted, so a clean dispatch still reads `3 ok` and a failed one
|
|
126
|
+
* no longer claims that a cancelled child failed.
|
|
127
|
+
*/
|
|
128
|
+
export declare function outcomePhrase(counts: RosterCounts): string;
|
|
129
|
+
/**
|
|
130
|
+
* Whether this entry is the transcript card of a dispatch that is STILL RUNNING,
|
|
131
|
+
* i.e. the second live surface the reporter saw beside the roster.
|
|
132
|
+
*
|
|
133
|
+
* ONE PREDICATE, FOUR CONSUMERS (analysis A-2'): the transcript's `case 'team'`
|
|
134
|
+
* and `estimateEntryRows` (so the virtual window and the frame agree), the tail
|
|
135
|
+
* window and `LiveTail` (so the pump's print plan and the box agree), and the
|
|
136
|
+
* `Ctrl+O` finder plus `reprintExpanded` (so a keypress cannot print a stale
|
|
137
|
+
* snapshot of a card that is still being rewritten). `state.entries` is NEVER
|
|
138
|
+
* filtered: the entry's lifecycle and `session/persist.ts`'s `interrupted`
|
|
139
|
+
* normalisation depend on it staying there.
|
|
140
|
+
*/
|
|
141
|
+
export declare function isLiveTeamCard(entry: Entry): boolean;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team status — the ONE authority that answers "what state is this child in, and
|
|
3
|
+
* how is that drawn / counted / timed" (tui-subagent-double-roster A-1).
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS FILE EXISTS. The phase -> glyph / colour / count question used to be
|
|
6
|
+
* answered by nine copies in seven files, and they disagreed. The transcript card
|
|
7
|
+
* asked `isOk(run) ? toolDone : toolError`, so six of the eight phases - every
|
|
8
|
+
* child that was still working - were painted with the FAILURE glyph, and an
|
|
9
|
+
* aborted child was counted as a failed one forever. That is not a palette
|
|
10
|
+
* mistake, it is a judgement implemented in the wrong number of places.
|
|
11
|
+
*
|
|
12
|
+
* THE VOCABULARY IS THE DOMAIN'S (analysis A-1' item 3). `ChildStatus` reuses
|
|
13
|
+
* `SubagentPhase`'s own words - `queued` / `running` / `done` / `failed` /
|
|
14
|
+
* `aborted` - and never invents a second dictionary (`pending` was proposed and
|
|
15
|
+
* rejected: `SubagentPhase` does not have it, and two words for one state is how
|
|
16
|
+
* the tenth copy gets written).
|
|
17
|
+
*
|
|
18
|
+
* FIVE STATES, NOT FOUR (A-1' item 2). `aborted` is NOT a failure. The runtime
|
|
19
|
+
* distinguishes them on purpose (`Runtime.settlePhase`: aborted first, then the
|
|
20
|
+
* timeout, then the error), and a user pressing Esc must not read their own
|
|
21
|
+
* action back as "3 failed". So `aborted` takes `glyphs.warn` and
|
|
22
|
+
* `theme.noticeWarn`, which collide with neither the done tick nor the failed
|
|
23
|
+
* cross in either glyph tier.
|
|
24
|
+
*
|
|
25
|
+
* ASCII ONLY: `src/ui/**` is inside `glyphs.test.ts`'s static scan, so every
|
|
26
|
+
* symbol here is injected through `pickGlyphs(caps)`.
|
|
27
|
+
*/
|
|
28
|
+
const STATUS_BY_PHASE = {
|
|
29
|
+
queued: 'queued',
|
|
30
|
+
starting: 'running',
|
|
31
|
+
thinking: 'running',
|
|
32
|
+
tool: 'running',
|
|
33
|
+
waiting: 'running',
|
|
34
|
+
done: 'done',
|
|
35
|
+
failed: 'failed',
|
|
36
|
+
aborted: 'aborted',
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* The status of one child.
|
|
40
|
+
*
|
|
41
|
+
* A LOOKUP TABLE RATHER THAN A SWITCH, because the mapping is the whole content
|
|
42
|
+
* of this function: a `Record` keyed by the phase union is exhaustive at compile
|
|
43
|
+
* time (a new phase is a missing-key error) and cannot drift from the type.
|
|
44
|
+
*/
|
|
45
|
+
export function childStatus(run) {
|
|
46
|
+
return STATUS_BY_PHASE[run.phase];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Count a roster once, by status.
|
|
50
|
+
*
|
|
51
|
+
* THE HEADER, THE CARD HEADLINE AND THE STATUS CHIP ALL READ THIS. Before it
|
|
52
|
+
* existed the header filtered `phase !== 'queued' && ...` for "running" and the
|
|
53
|
+
* card subtracted `runs.length - isOk(runs)`, so the same dispatch reported
|
|
54
|
+
* `0 running` above two rows reading `queued` and `1 failed` for a child that had
|
|
55
|
+
* never started.
|
|
56
|
+
*/
|
|
57
|
+
export function rosterCounts(runs) {
|
|
58
|
+
const counts = {
|
|
59
|
+
queued: 0, running: 0, done: 0, failed: 0, aborted: 0, total: runs.length,
|
|
60
|
+
};
|
|
61
|
+
for (const run of runs)
|
|
62
|
+
counts[childStatus(run)] += 1;
|
|
63
|
+
return counts;
|
|
64
|
+
}
|
|
65
|
+
/** True while the child is doing something - the only state that animates. */
|
|
66
|
+
export function isRunningStatus(status) {
|
|
67
|
+
return status === 'running';
|
|
68
|
+
}
|
|
69
|
+
/** True once the child's outcome is final, whatever it is. */
|
|
70
|
+
export function isSettledStatus(status) {
|
|
71
|
+
return status === 'done' || status === 'failed' || status === 'aborted';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The static marker for a status.
|
|
75
|
+
*
|
|
76
|
+
* `running` gets `spinnerStill` because the animated `<Spinner>` is the caller's
|
|
77
|
+
* job: it needs `reducedMotion` and `caps.unicode`, and a component that owns its
|
|
78
|
+
* own `ink-spinner` is what `spinner-census.test.ts` counts. The caller swaps this
|
|
79
|
+
* glyph for the spinner when it may animate.
|
|
80
|
+
*/
|
|
81
|
+
export function statusGlyph(status, glyphs) {
|
|
82
|
+
switch (status) {
|
|
83
|
+
case 'queued':
|
|
84
|
+
return glyphs.toolPending;
|
|
85
|
+
case 'running':
|
|
86
|
+
return glyphs.spinnerStill;
|
|
87
|
+
case 'done':
|
|
88
|
+
return glyphs.toolDone;
|
|
89
|
+
case 'failed':
|
|
90
|
+
return glyphs.toolError;
|
|
91
|
+
case 'aborted':
|
|
92
|
+
return glyphs.warn;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** The widest marker this terminal's tier can produce (1 in Unicode, 4 in ASCII). */
|
|
96
|
+
function markerWidth(glyphs) {
|
|
97
|
+
return Math.max(glyphs.toolPending.length, glyphs.spinnerStill.length, glyphs.toolDone.length, glyphs.toolError.length, glyphs.warn.length);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The marker padded to the tier's widest one, for the leading cell of a row.
|
|
101
|
+
*
|
|
102
|
+
* THE PADDING IS NOT COSMETIC (A-1'a): the ASCII tier spells `[ok]` (4), `[x]` (3)
|
|
103
|
+
* and `!` (1), so an unpadded marker cell starts every label at a different
|
|
104
|
+
* column and the roster stops reading as a table. `TodoPanel` pads its marker cell
|
|
105
|
+
* for the same reason. The Unicode tier's markers are all one cell wide, so the
|
|
106
|
+
* padding is a no-op there.
|
|
107
|
+
*/
|
|
108
|
+
export function statusMarkerCell(status, glyphs) {
|
|
109
|
+
return statusGlyph(status, glyphs).padEnd(markerWidth(glyphs));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The colour of a status. One reading for the roster row, the card row and the
|
|
113
|
+
* card's rail marker.
|
|
114
|
+
*
|
|
115
|
+
* `aborted` deliberately does NOT share `toolError` with `failed`: an abort is the
|
|
116
|
+
* user's own Esc, and the analysis found that painting it red made a cancelled
|
|
117
|
+
* dispatch permanently unreadable in the history.
|
|
118
|
+
*/
|
|
119
|
+
export function statusColor(status, theme) {
|
|
120
|
+
switch (status) {
|
|
121
|
+
case 'queued':
|
|
122
|
+
return theme.toolPending;
|
|
123
|
+
case 'running':
|
|
124
|
+
return theme.toolRunning;
|
|
125
|
+
case 'done':
|
|
126
|
+
return theme.toolDone;
|
|
127
|
+
case 'failed':
|
|
128
|
+
return theme.toolError;
|
|
129
|
+
case 'aborted':
|
|
130
|
+
return theme.noticeWarn;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* How long this child has been (or was) working, in ms.
|
|
135
|
+
*
|
|
136
|
+
* `endedAt ?? now`, NOT `endedAt ?? 0`. The transcript card used the latter, which
|
|
137
|
+
* made every RUNNING child read `0ms` while the roster beside it read `1m05s` for
|
|
138
|
+
* the same child - the pair of contradictory readouts the reporter screenshotted.
|
|
139
|
+
*/
|
|
140
|
+
export function childElapsedMs(run, now) {
|
|
141
|
+
if (run.startedAt === undefined)
|
|
142
|
+
return 0;
|
|
143
|
+
return Math.max(0, (run.endedAt ?? now) - run.startedAt);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Columns the roster header spends on `team` (plus its marker) and on the
|
|
147
|
+
* incoming-message badge, so the count clauses are planned against what is left.
|
|
148
|
+
*/
|
|
149
|
+
const HEADER_RESERVED_COLS = 16;
|
|
150
|
+
const HEADER_MIN_CLAUSE_COLS = 12;
|
|
151
|
+
function joinClauses(clauses, glyphs) {
|
|
152
|
+
return clauses.map((clause) => clause.text).join(` ${glyphs.midDot} `);
|
|
153
|
+
}
|
|
154
|
+
/** Drop the clause that can be spared first, keeping display order intact. */
|
|
155
|
+
function dropLowestPriority(clauses) {
|
|
156
|
+
let victim = 0;
|
|
157
|
+
for (let index = 1; index < clauses.length; index += 1) {
|
|
158
|
+
if (clauses[index].priority < clauses[victim].priority)
|
|
159
|
+
victim = index;
|
|
160
|
+
}
|
|
161
|
+
return clauses.filter((_, index) => index !== victim);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The roster header's count section, e.g. `2 running · 0 done · 1 queued`.
|
|
165
|
+
*
|
|
166
|
+
* `queued` IS COUNTED (the old `isRunning` excluded it, so a fresh dispatch
|
|
167
|
+
* announced `0 running · 0 done` above five rows that each said `queued`), and a
|
|
168
|
+
* non-zero bucket is always named even when nothing is running - a roster whose
|
|
169
|
+
* children are all `aborted` must say so rather than `0 running · 0 done`.
|
|
170
|
+
*
|
|
171
|
+
* WIDTH LADDER: narrow terminals drop the least urgent clause first, in the order
|
|
172
|
+
* `done`, `aborted`, `failed`, `queued`; `running` is never dropped. The order is
|
|
173
|
+
* a product decision, not a detail: while a dispatch is live, what is happening
|
|
174
|
+
* now outranks what has already finished, and the per-row activity column carries
|
|
175
|
+
* the finished children's own facts anyway.
|
|
176
|
+
*/
|
|
177
|
+
export function rosterHeadline(counts, glyphs, cols) {
|
|
178
|
+
const clauses = [
|
|
179
|
+
{ priority: 5, text: `${counts.running} running` },
|
|
180
|
+
{ priority: 1, text: `${counts.done} done` },
|
|
181
|
+
];
|
|
182
|
+
if (counts.queued > 0)
|
|
183
|
+
clauses.push({ priority: 4, text: `${counts.queued} queued` });
|
|
184
|
+
if (counts.failed > 0)
|
|
185
|
+
clauses.push({ priority: 3, text: `${counts.failed} failed` });
|
|
186
|
+
if (counts.aborted > 0)
|
|
187
|
+
clauses.push({ priority: 2, text: `${counts.aborted} aborted` });
|
|
188
|
+
const budget = Math.max(HEADER_MIN_CLAUSE_COLS, cols - HEADER_RESERVED_COLS);
|
|
189
|
+
let kept = clauses;
|
|
190
|
+
while (kept.length > 1 && joinClauses(kept, glyphs).length > budget) {
|
|
191
|
+
kept = dropLowestPriority(kept);
|
|
192
|
+
}
|
|
193
|
+
return joinClauses(kept, glyphs);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* The settled card's outcome buckets, e.g. `2 ok, 1 aborted, 1 failed`.
|
|
197
|
+
*
|
|
198
|
+
* `ok` is always shown (it is the denominator a reader compares against) and a
|
|
199
|
+
* zero bucket is omitted, so a clean dispatch still reads `3 ok` and a failed one
|
|
200
|
+
* no longer claims that a cancelled child failed.
|
|
201
|
+
*/
|
|
202
|
+
export function outcomePhrase(counts) {
|
|
203
|
+
const parts = [`${counts.done} ok`];
|
|
204
|
+
if (counts.aborted > 0)
|
|
205
|
+
parts.push(`${counts.aborted} aborted`);
|
|
206
|
+
if (counts.failed > 0)
|
|
207
|
+
parts.push(`${counts.failed} failed`);
|
|
208
|
+
return parts.join(', ');
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Whether this entry is the transcript card of a dispatch that is STILL RUNNING,
|
|
212
|
+
* i.e. the second live surface the reporter saw beside the roster.
|
|
213
|
+
*
|
|
214
|
+
* ONE PREDICATE, FOUR CONSUMERS (analysis A-2'): the transcript's `case 'team'`
|
|
215
|
+
* and `estimateEntryRows` (so the virtual window and the frame agree), the tail
|
|
216
|
+
* window and `LiveTail` (so the pump's print plan and the box agree), and the
|
|
217
|
+
* `Ctrl+O` finder plus `reprintExpanded` (so a keypress cannot print a stale
|
|
218
|
+
* snapshot of a card that is still being rewritten). `state.entries` is NEVER
|
|
219
|
+
* filtered: the entry's lifecycle and `session/persist.ts`'s `interrupted`
|
|
220
|
+
* normalisation depend on it staying there.
|
|
221
|
+
*/
|
|
222
|
+
export function isLiveTeamCard(entry) {
|
|
223
|
+
return entry.kind === 'team' && entry.active === true;
|
|
224
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { formatCost, formatDuration, formatTokens, promptTokensOf } from '../agent/usage.js';
|
|
2
2
|
import { formatRetryLine } from '../agent/retry-view.js';
|
|
3
|
+
import { childStatus, outcomePhrase, rosterCounts, statusMarkerCell } from './team-status.js';
|
|
3
4
|
import { expandTabs } from './display-tab.js';
|
|
4
5
|
const DEFAULT_MAX_ENTRIES = 200;
|
|
5
6
|
const CONTINUATION_INDENT = ' ';
|
|
@@ -101,15 +102,23 @@ function renderEntry(entry, glyphs) {
|
|
|
101
102
|
// dispatch is often the most expensive thing in a session, so leaving it
|
|
102
103
|
// out of the plain-text replay would make the exported transcript
|
|
103
104
|
// unaccountable for most of its own cost.
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
// ONE COUNTING AUTHORITY (`team-status.ts`), so the replay cannot disagree with
|
|
106
|
+
// the card it replays: `runs.length - ok` counted a cancelled child as a failed
|
|
107
|
+
// one, and a still-working one as a failure (tui-subagent-double-roster E3/E8).
|
|
108
|
+
// A cancellation therefore gets its own bucket instead of inflating `failed`.
|
|
109
|
+
const counts = rosterCounts(entry.runs);
|
|
106
110
|
const head = entry.active || (entry.aborted && entry.durationMs === undefined)
|
|
107
111
|
? 'interrupted'
|
|
108
|
-
: `${entry.durationMs ?? 0}ms (${
|
|
112
|
+
: `${entry.durationMs ?? 0}ms (${outcomePhrase(counts)})`;
|
|
109
113
|
const lines = [
|
|
110
114
|
`${TOOL_INDENT}team ${entry.runs.length} subagents ${head}`,
|
|
111
115
|
...entry.runs.map((run) => {
|
|
112
|
-
|
|
116
|
+
// The same five-state judgement the card uses: only a real failure takes
|
|
117
|
+
// the cross, a cancelled child takes the warning and a running one the
|
|
118
|
+
// running marker. `statusMarkerCell`, NOT the bare glyph: the ASCII tier
|
|
119
|
+
// spells `[ok]`, `[x]` and `!` at three widths, and a replay whose label
|
|
120
|
+
// column shifts from row to row is unreadable.
|
|
121
|
+
const glyph = statusMarkerCell(childStatus(run), glyphs);
|
|
113
122
|
const detail = run.error ? ` ${run.error}` : '';
|
|
114
123
|
return `${TOOL_INDENT}${CONTINUATION_INDENT}${glyph} ${run.label} ${run.description}${detail}`;
|
|
115
124
|
}),
|
|
@@ -109,8 +109,8 @@ export function useWheelRouting(options) {
|
|
|
109
109
|
if (overlay) {
|
|
110
110
|
// Modes A only. `model` / `confirm` / `question` own their own keys and
|
|
111
111
|
// scrolling them would leave an inconsistent cursor (R-11).
|
|
112
|
-
const controlled = overlay === 'help' || overlay === 'settings'
|
|
113
|
-
|| overlay === '
|
|
112
|
+
const controlled = overlay === 'help' || overlay === 'settings' || overlay === 'plan'
|
|
113
|
+
|| overlay === 'queue' || overlay === 'kit';
|
|
114
114
|
if (!controlled)
|
|
115
115
|
return;
|
|
116
116
|
// D-3: the row is not consulted here either. Behind an overlay the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aragon-agent/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.35",
|
|
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.47",
|
|
60
60
|
"chalk": "^5.6.2",
|
|
61
61
|
"cli-highlight": "^2.0.0",
|
|
62
62
|
"commander": "^12.1.0",
|