@aragon-agent/cli 0.6.34 → 0.6.36
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 +21 -0
- package/README.md +173 -7
- package/dist/agent/agent-mode.d.ts +20 -4
- package/dist/agent/agent-mode.js +20 -4
- package/dist/agent/controller.d.ts +67 -3
- package/dist/agent/controller.js +106 -5
- package/dist/agent/headless.js +31 -0
- 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 +13 -2
- package/dist/agent/reducer.js +1 -0
- package/dist/cli.js +12 -0
- package/dist/commands/agents-command.d.ts +15 -0
- package/dist/commands/agents-command.js +27 -0
- package/dist/commands/builtins.js +135 -4
- package/dist/commands/kit-command.d.ts +20 -0
- package/dist/commands/kit-command.js +131 -0
- package/dist/commands/perf.d.ts +11 -0
- package/dist/commands/perf.js +2 -1
- package/dist/commands/registry.d.ts +14 -0
- package/dist/config/app-paths.d.ts +15 -0
- package/dist/config/app-paths.js +33 -2
- package/dist/config/env.d.ts +4 -1
- package/dist/config/env.js +28 -2
- package/dist/config/kit-resolution.d.ts +93 -0
- package/dist/config/kit-resolution.js +226 -0
- package/dist/config/load.d.ts +10 -0
- package/dist/config/load.js +8 -1
- 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 +84 -0
- package/dist/config/schema.js +39 -0
- package/dist/config/store.js +12 -1
- package/dist/config/ui-state.d.ts +18 -0
- package/dist/config/ui-state.js +9 -0
- package/dist/exec/events.d.ts +25 -0
- package/dist/exec/events.js +4 -0
- package/dist/exec/index.js +16 -1
- package/dist/exec/runner.js +10 -0
- package/dist/input/keymap.js +18 -4
- package/dist/proc/limits.d.ts +16 -0
- package/dist/proc/limits.js +16 -0
- package/dist/proc/supervisor.d.ts +20 -1
- package/dist/proc/supervisor.js +42 -14
- package/dist/prompts/command.d.ts +34 -0
- package/dist/prompts/command.js +237 -0
- package/dist/prompts/copy.d.ts +89 -0
- package/dist/prompts/copy.js +131 -0
- package/dist/prompts/fill.d.ts +51 -0
- package/dist/prompts/fill.js +46 -0
- package/dist/prompts/limits.d.ts +53 -0
- package/dist/prompts/limits.js +53 -0
- package/dist/prompts/normalize.d.ts +53 -0
- package/dist/prompts/normalize.js +190 -0
- package/dist/prompts/resolve.d.ts +31 -0
- package/dist/prompts/resolve.js +60 -0
- package/dist/prompts/search.d.ts +58 -0
- package/dist/prompts/search.js +146 -0
- package/dist/prompts/store.d.ts +86 -0
- package/dist/prompts/store.js +453 -0
- package/dist/prompts/types.d.ts +204 -0
- package/dist/prompts/types.js +13 -0
- package/dist/session/checkpoint.d.ts +8 -1
- package/dist/session/checkpoint.js +6 -0
- package/dist/session/persist.d.ts +19 -1
- package/dist/session/persist.js +13 -1
- package/dist/session/startup-session.d.ts +7 -2
- package/dist/session/startup-session.js +5 -2
- package/dist/session/store.js +3 -0
- package/dist/session/validate-session.js +64 -1
- package/dist/team/limits.d.ts +25 -0
- package/dist/team/limits.js +26 -0
- package/dist/team/runtime.js +27 -2
- package/dist/team/settled.d.ts +57 -0
- package/dist/team/settled.js +87 -0
- package/dist/team/subagent.js +48 -2
- package/dist/team/timeline.d.ts +74 -0
- package/dist/team/timeline.js +101 -0
- package/dist/team/types.d.ts +24 -1
- package/dist/todo/acceptance.d.ts +120 -0
- package/dist/todo/acceptance.js +399 -0
- package/dist/todo/follow-through.d.ts +48 -23
- package/dist/todo/follow-through.js +110 -44
- package/dist/todo/limits.d.ts +62 -1
- package/dist/todo/limits.js +62 -1
- package/dist/todo/panel-rows.d.ts +29 -39
- package/dist/todo/panel-rows.js +37 -83
- package/dist/todo/plan-sections.d.ts +60 -0
- package/dist/todo/plan-sections.js +108 -0
- package/dist/todo/prompt.d.ts +17 -12
- package/dist/todo/prompt.js +59 -40
- package/dist/todo/row-window.d.ts +51 -0
- package/dist/todo/row-window.js +107 -0
- package/dist/todo/store.d.ts +139 -31
- package/dist/todo/store.js +271 -73
- package/dist/todo/todo-tool.d.ts +33 -8
- package/dist/todo/todo-tool.js +186 -30
- package/dist/todo/types.d.ts +58 -0
- package/dist/ui/App.js +414 -54
- package/dist/ui/Composer.d.ts +6 -2
- package/dist/ui/Composer.js +2 -2
- package/dist/ui/Header.d.ts +17 -1
- package/dist/ui/Header.js +8 -2
- package/dist/ui/PromptInput.d.ts +21 -1
- package/dist/ui/PromptInput.js +22 -2
- package/dist/ui/ScrollbackShell.d.ts +22 -7
- package/dist/ui/ScrollbackShell.js +8 -1
- package/dist/ui/StatusBar.d.ts +18 -0
- package/dist/ui/StatusBar.js +11 -3
- package/dist/ui/TeamPanel.d.ts +35 -41
- package/dist/ui/TeamPanel.js +53 -163
- package/dist/ui/TodoPanel.d.ts +17 -14
- package/dist/ui/TodoPanel.js +146 -38
- package/dist/ui/Transcript.js +23 -3
- package/dist/ui/composer-input.d.ts +10 -0
- package/dist/ui/composer-input.js +57 -24
- package/dist/ui/entries/TeamCard.d.ts +18 -2
- package/dist/ui/entries/TeamCard.js +47 -13
- package/dist/ui/entries/TodoCard.d.ts +26 -11
- package/dist/ui/entries/TodoCard.js +56 -15
- package/dist/ui/glyphs.d.ts +19 -0
- package/dist/ui/glyphs.js +4 -0
- package/dist/ui/layout/agents-board.d.ts +102 -0
- package/dist/ui/layout/agents-board.js +389 -0
- package/dist/ui/layout/cells.d.ts +61 -0
- package/dist/ui/layout/cells.js +156 -0
- package/dist/ui/layout/header-hints.d.ts +23 -3
- package/dist/ui/layout/header-hints.js +73 -7
- package/dist/ui/layout/kit-chip.d.ts +50 -0
- package/dist/ui/layout/kit-chip.js +81 -0
- package/dist/ui/layout/status-detail-layout.js +9 -0
- package/dist/ui/layout/status-layout.d.ts +13 -0
- package/dist/ui/layout/team-row.d.ts +139 -0
- package/dist/ui/layout/team-row.js +246 -0
- package/dist/ui/layout/todo-layout.d.ts +38 -0
- package/dist/ui/layout/todo-layout.js +75 -0
- package/dist/ui/layout/virtual-window.js +32 -3
- package/dist/ui/overlays/AgentsBoardOverlay.d.ts +57 -0
- package/dist/ui/overlays/AgentsBoardOverlay.js +183 -0
- package/dist/ui/overlays/HelpOverlay.js +24 -2
- 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/PromptEditorPage.d.ts +48 -0
- package/dist/ui/overlays/PromptEditorPage.js +227 -0
- package/dist/ui/overlays/PromptLibraryOverlay.d.ts +37 -0
- package/dist/ui/overlays/PromptLibraryOverlay.js +191 -0
- package/dist/ui/overlays/PromptPicker.d.ts +36 -0
- package/dist/ui/overlays/PromptPicker.js +129 -0
- package/dist/ui/overlays/PromptTextArea.d.ts +86 -0
- package/dist/ui/overlays/PromptTextArea.js +141 -0
- 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/overlays/prompt-library-state.d.ts +162 -0
- package/dist/ui/overlays/prompt-library-state.js +335 -0
- package/dist/ui/overlays/prompt-picker-state.d.ts +71 -0
- package/dist/ui/overlays/prompt-picker-state.js +98 -0
- package/dist/ui/scrollback/LiveTail.d.ts +2 -2
- package/dist/ui/scrollback/LiveTail.js +7 -2
- package/dist/ui/scrollback/band.js +14 -1
- package/dist/ui/scrollback/live-preview.js +10 -3
- package/dist/ui/scrollback/tail-window.d.ts +12 -0
- package/dist/ui/scrollback/tail-window.js +17 -2
- package/dist/ui/scrollback/use-scrollback-view.js +7 -0
- package/dist/ui/team-status.d.ts +166 -0
- package/dist/ui/team-status.js +252 -0
- package/dist/ui/transcript-text.js +13 -4
- package/dist/ui/use-startup-notices.d.ts +35 -1
- package/dist/ui/use-startup-notices.js +29 -2
- package/dist/ui/use-wheel-routing.js +10 -2
- package/package.json +2 -2
|
@@ -3,10 +3,20 @@ export interface ComposerSubmitResult {
|
|
|
3
3
|
accepted: boolean;
|
|
4
4
|
reason?: string;
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* A fill carries the prompt body; `submit` folds "insert and send" into the SAME
|
|
8
|
+
* transaction, so `Ctrl+Enter` in the picker is literally the Enter key one step
|
|
9
|
+
* later rather than a second submission path (quick-prompt-library P0-1 / I-9).
|
|
10
|
+
*/
|
|
6
11
|
export type ComposerInputIntent = InputSegment | {
|
|
7
12
|
kind: 'newline';
|
|
8
13
|
} | {
|
|
9
14
|
kind: 'submit';
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'fill';
|
|
17
|
+
text: string;
|
|
18
|
+
mode: 'insert' | 'replace';
|
|
19
|
+
submit: boolean;
|
|
10
20
|
};
|
|
11
21
|
export interface ComposerInputOptions {
|
|
12
22
|
editor: EditorState;
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import { editorReducer } from './editor-reducer.js';
|
|
2
|
+
import { planPromptFill } from '../prompts/fill.js';
|
|
2
3
|
const MORE_INPUT_NOTICE = 'More input is kept in the draft; press Enter to send.';
|
|
4
|
+
/**
|
|
5
|
+
* Apply one fill to one draft. TWO CALLS PER INTENT, on purpose: the same plan runs
|
|
6
|
+
* against the accepted and the rejected draft (P0-1), so a submission the host
|
|
7
|
+
* refuses leaves the FILLED text in the composer rather than the text from before
|
|
8
|
+
* the fill. Doing it once and sharing the result would silently drop the prompt the
|
|
9
|
+
* user just chose, which is the data loss this design exists to prevent.
|
|
10
|
+
*
|
|
11
|
+
* THE REDUCER OWNS THE RESULT. `planPromptFill` says what to insert; `insert` and
|
|
12
|
+
* `replace` decide the caret, the paste-token snapping and the draft-flag reset,
|
|
13
|
+
* exactly as they do for typing.
|
|
14
|
+
*/
|
|
15
|
+
function applyFill(editor, intent) {
|
|
16
|
+
const plan = planPromptFill({
|
|
17
|
+
draft: editor.buffer,
|
|
18
|
+
cursor: editor.cursor,
|
|
19
|
+
content: intent.text,
|
|
20
|
+
mode: intent.mode,
|
|
21
|
+
});
|
|
22
|
+
return plan.mode === 'replace'
|
|
23
|
+
? editorReducer(editor, { type: 'replace', buffer: plan.text, cursor: plan.cursor })
|
|
24
|
+
: editorReducer(editor, { type: 'insert', text: plan.text });
|
|
25
|
+
}
|
|
3
26
|
/**
|
|
4
27
|
* Plan both accepted and rejected drafts before any submission side effect.
|
|
5
28
|
* Uses caller-assigned paste IDs and pure callbacks; never mutates the source editor.
|
|
@@ -13,34 +36,44 @@ export function planComposerInput(options) {
|
|
|
13
36
|
let submission;
|
|
14
37
|
let notice;
|
|
15
38
|
for (const intent of options.intents) {
|
|
16
|
-
if (intent.kind === '
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
39
|
+
if (intent.kind === 'fill') {
|
|
40
|
+
nextEditor = applyFill(nextEditor, intent);
|
|
41
|
+
rejectedEditor = applyFill(rejectedEditor, intent);
|
|
42
|
+
// A plain fill stops here: the draft is filled, nothing is sent (AC-16).
|
|
43
|
+
if (!intent.submit)
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
else if (intent.kind !== 'submit') {
|
|
47
|
+
const segment = intent.kind === 'newline'
|
|
48
|
+
? { kind: 'text', text: '\n' } : intent;
|
|
49
|
+
const segments = [segment];
|
|
50
|
+
if (segment.kind === 'paste') {
|
|
51
|
+
// Inline pastes have no map entry; count every paste in this callback as before.
|
|
52
|
+
acceptedPastes.push(segment);
|
|
53
|
+
rejectedPastes.push(segment);
|
|
54
|
+
const refusal = options.checkPasteLimit(acceptedPastes, acceptedBase)
|
|
55
|
+
?? options.checkPasteLimit(rejectedPastes, options.editor);
|
|
56
|
+
if (refusal) {
|
|
57
|
+
return { nextEditor: options.editor, rejectedEditor: options.editor, refusal };
|
|
58
|
+
}
|
|
26
59
|
}
|
|
60
|
+
nextEditor = editorReducer(nextEditor, { type: 'input', segments });
|
|
61
|
+
rejectedEditor = editorReducer(rejectedEditor, { type: 'input', segments });
|
|
27
62
|
continue;
|
|
28
63
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
// THE ONE SUBMIT PATH. A `fill` with `submit` and a bare `submit` reach this
|
|
65
|
+
// block and nothing else, so queueing, paste expansion and the
|
|
66
|
+
// accepted/rejected contract are inherited rather than re-implemented.
|
|
67
|
+
const text = options.resolveSubmit(nextEditor);
|
|
68
|
+
if (submission !== undefined) {
|
|
69
|
+
notice = MORE_INPUT_NOTICE;
|
|
70
|
+
}
|
|
71
|
+
else if (text !== null && text.trim().length > 0) {
|
|
72
|
+
submission = text;
|
|
73
|
+
nextEditor = editorReducer(nextEditor, { type: 'clear' });
|
|
74
|
+
acceptedBase = nextEditor;
|
|
75
|
+
acceptedPastes = [];
|
|
41
76
|
}
|
|
42
|
-
nextEditor = editorReducer(nextEditor, { type: 'input', segments });
|
|
43
|
-
rejectedEditor = editorReducer(rejectedEditor, { type: 'input', segments });
|
|
44
77
|
}
|
|
45
78
|
return { nextEditor, rejectedEditor, submission, notice };
|
|
46
79
|
}
|
|
@@ -15,6 +15,7 @@ import React from 'react';
|
|
|
15
15
|
import type { Theme } from '../theme.js';
|
|
16
16
|
import type { TermCapabilities } from '../capabilities.js';
|
|
17
17
|
import type { SubagentRun } from '../../team/types.js';
|
|
18
|
+
import { type RosterCounts } from '../team-status.js';
|
|
18
19
|
export interface TeamCardProps {
|
|
19
20
|
requested: number;
|
|
20
21
|
runs: SubagentRun[];
|
|
@@ -25,9 +26,24 @@ export interface TeamCardProps {
|
|
|
25
26
|
reducedMotion?: boolean;
|
|
26
27
|
theme: Theme;
|
|
27
28
|
caps: TermCapabilities;
|
|
29
|
+
/**
|
|
30
|
+
* Wall-clock seconds, set only while the dispatch is live (`nowSecFor`). WITHOUT
|
|
31
|
+
* IT a running child's elapsed time came from `(endedAt ?? 0) - startedAt` - a
|
|
32
|
+
* negative number clamped to `0ms`, the readout that contradicted the roster
|
|
33
|
+
* beside it (A-1 / E4).
|
|
34
|
+
*/
|
|
35
|
+
nowSec?: number;
|
|
28
36
|
}
|
|
29
|
-
/**
|
|
30
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The card's aggregate colour.
|
|
39
|
+
*
|
|
40
|
+
* FAILURES DOMINATE, THEN A CANCELLATION, THEN A DISPATCH THAT NEVER SETTLED.
|
|
41
|
+
* The old version asked `runs.some((r) => !isOk(r))`, so an entry whose children
|
|
42
|
+
* were still working - and one the user had cancelled - was painted `toolError`
|
|
43
|
+
* (A-1 / E8). `RosterCounts` is passed IN rather than recomputed, so this colour
|
|
44
|
+
* and the headline cannot disagree inside one card.
|
|
45
|
+
*/
|
|
46
|
+
export declare function teamCardColor(counts: RosterCounts, active: boolean, aborted: boolean, theme: Theme): string | undefined;
|
|
31
47
|
declare function TeamCardImpl(props: TeamCardProps): React.ReactElement;
|
|
32
48
|
/**
|
|
33
49
|
* `React.memo` with the DEFAULT comparator (tui-render-performance L2 / R3).
|
|
@@ -18,37 +18,71 @@ import Spinner from 'ink-spinner';
|
|
|
18
18
|
import { pickGlyphs } from '../glyphs.js';
|
|
19
19
|
import { railBorderProps } from '../layout/Gutter.js';
|
|
20
20
|
import { formatDuration } from '../../agent/usage.js';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
import { childElapsedMs, childStatus, outcomePhrase, rosterCounts, statusColor, statusMarkerCell, } from '../team-status.js';
|
|
22
|
+
/**
|
|
23
|
+
* The card's aggregate colour.
|
|
24
|
+
*
|
|
25
|
+
* FAILURES DOMINATE, THEN A CANCELLATION, THEN A DISPATCH THAT NEVER SETTLED.
|
|
26
|
+
* The old version asked `runs.some((r) => !isOk(r))`, so an entry whose children
|
|
27
|
+
* were still working - and one the user had cancelled - was painted `toolError`
|
|
28
|
+
* (A-1 / E8). `RosterCounts` is passed IN rather than recomputed, so this colour
|
|
29
|
+
* and the headline cannot disagree inside one card.
|
|
30
|
+
*/
|
|
31
|
+
export function teamCardColor(counts, active, aborted, theme) {
|
|
26
32
|
if (active)
|
|
27
33
|
return theme.toolRunning;
|
|
28
|
-
if (
|
|
34
|
+
if (counts.failed > 0)
|
|
29
35
|
return theme.toolError;
|
|
36
|
+
if (aborted || counts.aborted > 0 || counts.queued > 0 || counts.running > 0) {
|
|
37
|
+
return theme.noticeWarn;
|
|
38
|
+
}
|
|
30
39
|
return theme.toolDone;
|
|
31
40
|
}
|
|
41
|
+
/** `nowSec` is seconds (`App`'s 1 Hz clock); a card measures in milliseconds. */
|
|
42
|
+
function liveClockMs(nowSec) {
|
|
43
|
+
return nowSec === undefined ? Date.now() : nowSec * 1000;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The elapsed column for one child: a MEASUREMENT, or an empty cell.
|
|
47
|
+
*
|
|
48
|
+
* `liveNow` is defined only while the dispatch is live, and that distinction is the
|
|
49
|
+
* whole point. A child restored by `/resume` keeps its `startedAt` and has no
|
|
50
|
+
* `endedAt` - `session/persist.ts` settles the ENTRY, it does not invent an end time
|
|
51
|
+
* for the run - so a figure computed against `Date.now()` would claim hours of work
|
|
52
|
+
* that never happened, next to an `interrupted (session resumed)` headline that says
|
|
53
|
+
* the opposite. An unmeasurable duration is left blank instead.
|
|
54
|
+
*/
|
|
55
|
+
function elapsedCell(run, liveNow) {
|
|
56
|
+
const end = run.endedAt ?? liveNow;
|
|
57
|
+
if (run.startedAt === undefined || end === undefined)
|
|
58
|
+
return '';
|
|
59
|
+
return formatDuration(childElapsedMs(run, end));
|
|
60
|
+
}
|
|
32
61
|
function TeamCardImpl(props) {
|
|
33
62
|
const { requested, runs, aborted, durationMs, active, expanded, reducedMotion, theme, caps } = props;
|
|
34
63
|
const glyphs = pickGlyphs(caps);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
64
|
+
// ONE COUNTING AUTHORITY (A-1): `ok`/`failed` used to be `isOk(...)` and
|
|
65
|
+
// `runs.length - ok`, which counted a cancelled child - and a still-working one -
|
|
66
|
+
// as a failed one (E3 / E8).
|
|
67
|
+
const counts = rosterCounts(runs);
|
|
68
|
+
const color = teamCardColor(counts, active, aborted, theme);
|
|
69
|
+
// A CLOCK ONLY WHILE THE DISPATCH IS LIVE: a settled card has nothing left to time,
|
|
70
|
+
// and a clock in the settled path is how a resumed dispatch came to read like a run
|
|
71
|
+
// that is still going (`elapsedCell`).
|
|
72
|
+
const liveNow = active ? liveClockMs(props.nowSec) : undefined;
|
|
38
73
|
// A dispatch entry restored from `/resume` is settled and aborted but has no
|
|
39
74
|
// duration: nothing measured it, because the process it belonged to is gone.
|
|
40
75
|
// Saying so plainly beats rendering `0.0s (0 ok, 3 failed)`, which reads like
|
|
41
76
|
// a dispatch that ran and lost (§5.3 / P1-5).
|
|
42
77
|
const interrupted = !active && aborted && durationMs === undefined;
|
|
43
|
-
const settled = runs.filter((r) => r.phase === 'done' || r.phase === 'failed' || r.phase === 'aborted').length;
|
|
44
78
|
const headline = interrupted
|
|
45
79
|
? 'interrupted (session resumed)'
|
|
46
80
|
: active
|
|
47
|
-
? `${
|
|
48
|
-
: `${formatDuration(durationMs ?? 0)} (${
|
|
81
|
+
? `${counts.queued + counts.running} running`
|
|
82
|
+
: `${formatDuration(durationMs ?? 0)} (${outcomePhrase(counts)})`;
|
|
49
83
|
const count = `${runs.length} ${runs.length === 1 ? 'subagent' : 'subagents'}`;
|
|
50
84
|
const dropped = requested > runs.length ? ` (${runs.length} of ${requested} requested)` : '';
|
|
51
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: theme.accent, bold: true, children: "team" }), _jsxs(Text, { color: theme.muted, children: [' ', count, dropped, ' '] }), active && !reducedMotion && caps.unicode ? (_jsxs(Text, { color: color, children: [_jsx(Spinner, { type: "dots" }), " ", headline] })) : (_jsxs(Text, { color: color, children: [active ? `${glyphs.spinnerStill} ` : '', headline] }))] }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, ...railBorderProps(glyphs.railVertical, theme.border), children: [runs.map((run) => (_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color:
|
|
85
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: theme.accent, bold: true, children: "team" }), _jsxs(Text, { color: theme.muted, children: [' ', count, dropped, ' '] }), active && !reducedMotion && caps.unicode ? (_jsxs(Text, { color: color, children: [_jsx(Spinner, { type: "dots" }), " ", headline] })) : (_jsxs(Text, { color: color, children: [active ? `${glyphs.spinnerStill} ` : '', headline] }))] }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, ...railBorderProps(glyphs.railVertical, theme.border), children: [runs.map((run) => (_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: statusColor(childStatus(run), theme), children: statusMarkerCell(childStatus(run), glyphs) }), _jsxs(Text, { color: theme.primary, children: [' ', run.label.padEnd(8).slice(0, 8)] }), _jsxs(Text, { color: theme.muted, children: [' ', run.description, ' ', elapsedCell(run, liveNow), ` ${run.turns} turns`, run.filesTouched.length > 0 ? ` ${run.filesTouched.length} files` : '', run.error ? ` ${run.error}` : ''] }), run.retry && (_jsxs(Text, { color: theme.noticeWarn, bold: true, children: [' ', "retry ", run.retry.attempt, "/", run.retry.maxRetries] }))] }, run.label))), expanded &&
|
|
52
86
|
runs.map((run) => (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: theme.accent, children: [run.label, " ", glyphs.midDot, " ", run.description] }), _jsx(Text, { color: theme.muted, children: run.summary ?? '(no summary)' })] }, `s-${run.label}`))), !expanded && !active && runs.length > 0 && (_jsxs(Text, { color: theme.muted, children: ["+", runs.length, " ", runs.length === 1 ? 'summary' : 'summaries', " (Ctrl+O)"] })), expanded && _jsx(Text, { color: theme.muted, children: "(Ctrl+O to collapse)" })] })] }));
|
|
53
87
|
}
|
|
54
88
|
/**
|
|
@@ -1,25 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TodoCard — the transcript checklist entry (todo-plan-execution §6.2
|
|
2
|
+
* TodoCard — the transcript checklist entry (todo-plan-execution §6.2;
|
|
3
|
+
* todo-acceptance-criteria §6.10).
|
|
3
4
|
*
|
|
4
5
|
* ASCII ONLY: every glyph comes from `pickGlyphs`.
|
|
5
6
|
*
|
|
6
|
-
* ONE CARD PER USER TURN, REWRITTEN IN PLACE. The rail is EPHEMERAL and this is
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* ONE CARD PER USER TURN, REWRITTEN IN PLACE. The rail is EPHEMERAL and this is the
|
|
8
|
+
* HISTORY, exactly as `TeamPanel` and `TeamCard` divide the same work. It renders
|
|
9
|
+
* through the shared `EntryFrame` supplied by `EntryView`, so a plan reads as one
|
|
10
|
+
* more step in the transcript rather than a second kind of object.
|
|
10
11
|
*
|
|
11
12
|
* THE FULL LIST, NO WINDOWING (the transcript scrolls; the rail does not), and
|
|
12
|
-
* NEVER ANIMATED even while `live` — a resumed session would otherwise spin
|
|
13
|
-
*
|
|
13
|
+
* NEVER ANIMATED even while `live` — a resumed session would otherwise spin forever
|
|
14
|
+
* on a card whose run ended days ago.
|
|
15
|
+
*
|
|
16
|
+
* THE ACCEPTANCE BLOCK ADDS ROWS, NOT A CARD (§6.10): same frame, same marker
|
|
17
|
+
* column, one block underneath. Only a FAILED criterion gets an evidence line
|
|
18
|
+
* (D-14), which is what keeps the height exactly `1 + items.length + failedCount`.
|
|
14
19
|
*/
|
|
15
20
|
import React from 'react';
|
|
16
21
|
import type { Theme } from '../theme.js';
|
|
17
22
|
import type { TermCapabilities } from '../capabilities.js';
|
|
18
|
-
import type { TodoItem } from '../../todo/types.js';
|
|
23
|
+
import type { AcceptanceSnapshot, TodoItem } from '../../todo/types.js';
|
|
19
24
|
export interface TodoCardProps {
|
|
20
25
|
items: TodoItem[];
|
|
21
26
|
doneCount: number;
|
|
22
27
|
total: number;
|
|
28
|
+
/**
|
|
29
|
+
* The acceptance projection (todo-acceptance-criteria §6.10).
|
|
30
|
+
*
|
|
31
|
+
* OPTIONAL, unlike `TodoSnapshot.acceptance` (P1-4): a `TodoSnapshot` has exactly
|
|
32
|
+
* one writer and can fail closed, while this entry's sources include transcript
|
|
33
|
+
* entries read back from a session file written before the feature existed. A
|
|
34
|
+
* required field there would be typed "present" and be `undefined` at runtime.
|
|
35
|
+
*/
|
|
36
|
+
acceptance?: AcceptanceSnapshot | null;
|
|
23
37
|
live: boolean;
|
|
24
38
|
/** Saved mid-run and resumed: the run behind this card died with the process. */
|
|
25
39
|
interrupted?: boolean;
|
|
@@ -31,9 +45,10 @@ export declare function todoCardColor(doneCount: number, total: number, live: bo
|
|
|
31
45
|
declare function TodoCardImpl(props: TodoCardProps): React.ReactElement;
|
|
32
46
|
/**
|
|
33
47
|
* `React.memo` with the DEFAULT comparator (tui-render-performance L2 / R3).
|
|
34
|
-
* `mapEntry` already preserves object identity for untouched entries, so the
|
|
35
|
-
*
|
|
36
|
-
* `
|
|
48
|
+
* `mapEntry` already preserves object identity for untouched entries, so the array
|
|
49
|
+
* props below are stable references on a settled card, and `theme` / `caps` are
|
|
50
|
+
* `useMemo`d in `App.tsx` (I-L2-1). `acceptance` is a NEW object whenever a verdict
|
|
51
|
+
* lands and the SAME reference otherwise, which is exactly what the comparator wants.
|
|
37
52
|
*/
|
|
38
53
|
export declare const TodoCard: React.MemoExoticComponent<typeof TodoCardImpl>;
|
|
39
54
|
export {};
|
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* TodoCard — the transcript checklist entry (todo-plan-execution §6.2
|
|
3
|
+
* TodoCard — the transcript checklist entry (todo-plan-execution §6.2;
|
|
4
|
+
* todo-acceptance-criteria §6.10).
|
|
4
5
|
*
|
|
5
6
|
* ASCII ONLY: every glyph comes from `pickGlyphs`.
|
|
6
7
|
*
|
|
7
|
-
* ONE CARD PER USER TURN, REWRITTEN IN PLACE. The rail is EPHEMERAL and this is
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* ONE CARD PER USER TURN, REWRITTEN IN PLACE. The rail is EPHEMERAL and this is the
|
|
9
|
+
* HISTORY, exactly as `TeamPanel` and `TeamCard` divide the same work. It renders
|
|
10
|
+
* through the shared `EntryFrame` supplied by `EntryView`, so a plan reads as one
|
|
11
|
+
* more step in the transcript rather than a second kind of object.
|
|
11
12
|
*
|
|
12
13
|
* THE FULL LIST, NO WINDOWING (the transcript scrolls; the rail does not), and
|
|
13
|
-
* NEVER ANIMATED even while `live` — a resumed session would otherwise spin
|
|
14
|
-
*
|
|
14
|
+
* NEVER ANIMATED even while `live` — a resumed session would otherwise spin forever
|
|
15
|
+
* on a card whose run ended days ago.
|
|
16
|
+
*
|
|
17
|
+
* THE ACCEPTANCE BLOCK ADDS ROWS, NOT A CARD (§6.10): same frame, same marker
|
|
18
|
+
* column, one block underneath. Only a FAILED criterion gets an evidence line
|
|
19
|
+
* (D-14), which is what keeps the height exactly `1 + items.length + failedCount`.
|
|
15
20
|
*/
|
|
16
21
|
import React from 'react';
|
|
17
22
|
import { Box, Text } from 'ink';
|
|
18
23
|
import { pickGlyphs } from '../glyphs.js';
|
|
19
24
|
import { railBorderProps } from '../layout/Gutter.js';
|
|
25
|
+
import { derivePlanPhase } from '../../todo/acceptance.js';
|
|
20
26
|
/** The card's aggregate colour: complete is done, anything else is in flight. */
|
|
21
27
|
export function todoCardColor(doneCount, total, live, theme) {
|
|
22
28
|
if (live)
|
|
@@ -39,19 +45,54 @@ function rowColor(item, theme) {
|
|
|
39
45
|
return theme.primary;
|
|
40
46
|
return theme.hintFg ?? theme.muted;
|
|
41
47
|
}
|
|
48
|
+
/** `passed` reuses the done glyph; `failed` is the one new meaning (D-9). */
|
|
49
|
+
function acceptanceMarker(item, glyphs) {
|
|
50
|
+
if (item.status === 'passed')
|
|
51
|
+
return glyphs.todoDone;
|
|
52
|
+
if (item.status === 'failed')
|
|
53
|
+
return glyphs.todoFail;
|
|
54
|
+
if (item.status === 'in_progress')
|
|
55
|
+
return glyphs.todoActive;
|
|
56
|
+
return glyphs.todoPending;
|
|
57
|
+
}
|
|
58
|
+
function acceptanceRowColor(item, theme) {
|
|
59
|
+
if (item.status === 'failed')
|
|
60
|
+
return theme.toolError;
|
|
61
|
+
if (item.status === 'passed')
|
|
62
|
+
return theme.muted;
|
|
63
|
+
if (item.status === 'in_progress')
|
|
64
|
+
return theme.primary;
|
|
65
|
+
return theme.hintFg ?? theme.muted;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* All passed reads as done, any failure as an error, otherwise muted; while the run
|
|
69
|
+
* is live and the plan is in VERIFY it reads as running - the same expression family
|
|
70
|
+
* `todoCardColor` uses one level up.
|
|
71
|
+
*/
|
|
72
|
+
function acceptanceTitleColor(acceptance, items, live, theme) {
|
|
73
|
+
if (acceptance.failedCount > 0)
|
|
74
|
+
return theme.toolError;
|
|
75
|
+
if (live && derivePlanPhase(items, acceptance.items) === 'verify')
|
|
76
|
+
return theme.toolRunning;
|
|
77
|
+
if (acceptance.total > 0 && acceptance.checkedCount === acceptance.total)
|
|
78
|
+
return theme.toolDone;
|
|
79
|
+
return theme.muted;
|
|
80
|
+
}
|
|
42
81
|
function TodoCardImpl(props) {
|
|
43
|
-
const { items, doneCount, total, live, interrupted, theme, caps } = props;
|
|
82
|
+
const { items, doneCount, total, acceptance, live, interrupted, theme, caps } = props;
|
|
44
83
|
const glyphs = pickGlyphs(caps);
|
|
84
|
+
const criteria = acceptance && acceptance.total > 0 ? acceptance : null;
|
|
45
85
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: theme.accent, bold: true, children: "Todos" }), _jsxs(Text, { color: todoCardColor(doneCount, total, live, theme), children: [' ', doneCount, "/", total] }), interrupted && (
|
|
46
|
-
// A resumed `2/7` would otherwise read as a run still in flight
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
_jsxs(Text, { color: theme.muted, children: [' ', "interrupted (session resumed)"] }))] }), _jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, ...railBorderProps(glyphs.railVertical, theme.border), children: items.map((item, index) => (_jsxs(Text, { wrap: "truncate", color: rowColor(item, theme), children: [marker(item, glyphs).padEnd(3), item.status === 'in_progress' ? item.activeForm : item.content] }, `${index}-${item.content}`))) })] }));
|
|
86
|
+
// A resumed `2/7` would otherwise read as a run still in flight (P2-6).
|
|
87
|
+
// Saying so plainly is the same fix `TeamCard` makes for a dispatch that
|
|
88
|
+
// never got a duration.
|
|
89
|
+
_jsxs(Text, { color: theme.muted, children: [' ', "interrupted (session resumed)"] }))] }), _jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, ...railBorderProps(glyphs.railVertical, theme.border), children: items.map((item, index) => (_jsxs(Text, { wrap: "truncate", color: rowColor(item, theme), children: [marker(item, glyphs).padEnd(3), item.status === 'in_progress' ? item.activeForm : item.content] }, `${index}-${item.content}`))) }), criteria && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: theme.accent, bold: true, children: "Acceptance" }), _jsxs(Text, { color: acceptanceTitleColor(criteria, items, live, theme), children: [' ', criteria.checkedCount, "/", criteria.total, " checked", criteria.failedCount > 0 ? ` !${criteria.failedCount} failed` : ''] })] }), _jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, ...railBorderProps(glyphs.railVertical, theme.border), children: criteria.items.map((item, index) => (_jsxs(React.Fragment, { children: [_jsxs(Text, { wrap: "truncate", color: acceptanceRowColor(item, theme), children: [acceptanceMarker(item, glyphs).padEnd(3), item.content] }), item.status === 'failed' && item.evidence.length > 0 && (_jsxs(Text, { wrap: "truncate", color: theme.muted, children: [' ', item.evidence] }))] }, `acc-${index}-${item.content}`))) })] }))] }));
|
|
50
90
|
}
|
|
51
91
|
/**
|
|
52
92
|
* `React.memo` with the DEFAULT comparator (tui-render-performance L2 / R3).
|
|
53
|
-
* `mapEntry` already preserves object identity for untouched entries, so the
|
|
54
|
-
*
|
|
55
|
-
* `
|
|
93
|
+
* `mapEntry` already preserves object identity for untouched entries, so the array
|
|
94
|
+
* props below are stable references on a settled card, and `theme` / `caps` are
|
|
95
|
+
* `useMemo`d in `App.tsx` (I-L2-1). `acceptance` is a NEW object whenever a verdict
|
|
96
|
+
* lands and the SAME reference otherwise, which is exactly what the comparator wants.
|
|
56
97
|
*/
|
|
57
98
|
export const TodoCard = React.memo(TodoCardImpl);
|
package/dist/ui/glyphs.d.ts
CHANGED
|
@@ -86,6 +86,15 @@ export interface Glyphs {
|
|
|
86
86
|
scrollThumb: string;
|
|
87
87
|
/** Redaction dot for `maskSecret` in the settings screen (§4.1 tier B). */
|
|
88
88
|
maskDot: string;
|
|
89
|
+
/**
|
|
90
|
+
* The Agents board's selection cursor (tui-subagent-panel D-14).
|
|
91
|
+
*
|
|
92
|
+
* BYTE-IDENTICAL to `todoActive`, and still a field of its own: the two carry
|
|
93
|
+
* different meanings - "the item the run is on" versus "the child this panel is
|
|
94
|
+
* showing" - and reusing the todo glyph would restyle the board the day someone
|
|
95
|
+
* redraws the TODO rail. `scrollTrack` / `scrollThumb` above are the same precedent.
|
|
96
|
+
*/
|
|
97
|
+
cursor: string;
|
|
89
98
|
/**
|
|
90
99
|
* Team mode (team-subagents §6.1). The rail marker for a dispatch card and
|
|
91
100
|
* the prefix on the panel's incoming-message preview.
|
|
@@ -108,6 +117,16 @@ export interface Glyphs {
|
|
|
108
117
|
todoPending: string;
|
|
109
118
|
todoActive: string;
|
|
110
119
|
todoDone: string;
|
|
120
|
+
/**
|
|
121
|
+
* The ONLY glyph this feature adds (todo-acceptance-criteria D-9): `passed` is a
|
|
122
|
+
* settled positive and reuses `todoDone`, while `failed` is a genuinely new
|
|
123
|
+
* meaning.
|
|
124
|
+
*
|
|
125
|
+
* The ASCII tier must still tell "done" from "failed", so `[!]` joins `[x]` - the
|
|
126
|
+
* same three columns, which is what keeps the mixed-width list aligned with no
|
|
127
|
+
* per-tier branch.
|
|
128
|
+
*/
|
|
129
|
+
todoFail: string;
|
|
111
130
|
/**
|
|
112
131
|
* API retry (llm-api-retry-backoff §6.5). The rail marker on the retry card.
|
|
113
132
|
*
|
package/dist/ui/glyphs.js
CHANGED
|
@@ -60,12 +60,14 @@ const UNICODE_GLYPHS = {
|
|
|
60
60
|
scrollTrack: '│',
|
|
61
61
|
scrollThumb: '█',
|
|
62
62
|
maskDot: '•',
|
|
63
|
+
cursor: '▸',
|
|
63
64
|
boxStyle: 'round',
|
|
64
65
|
teamAgent: '◆',
|
|
65
66
|
teamMail: '⇄',
|
|
66
67
|
todoPending: '○',
|
|
67
68
|
todoActive: '▸',
|
|
68
69
|
todoDone: '✓',
|
|
70
|
+
todoFail: '✗',
|
|
69
71
|
retry: '↻',
|
|
70
72
|
compaction: '⤓',
|
|
71
73
|
queued: '◷',
|
|
@@ -124,12 +126,14 @@ const ASCII_GLYPHS = {
|
|
|
124
126
|
scrollTrack: '|',
|
|
125
127
|
scrollThumb: '#',
|
|
126
128
|
maskDot: '*',
|
|
129
|
+
cursor: '>',
|
|
127
130
|
boxStyle: 'classic',
|
|
128
131
|
teamAgent: '*',
|
|
129
132
|
teamMail: '@',
|
|
130
133
|
todoPending: '[ ]',
|
|
131
134
|
todoActive: '>',
|
|
132
135
|
todoDone: '[x]',
|
|
136
|
+
todoFail: '[!]',
|
|
133
137
|
retry: '[r]',
|
|
134
138
|
compaction: '[c]',
|
|
135
139
|
queued: '*',
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Agents board's row model — pure projection, no Ink (tui-subagent-panel §4.4 / §7).
|
|
3
|
+
*
|
|
4
|
+
* WHY A PURE MODULE AND NOT JSX. The board's whole value is that it shows EVERY fact
|
|
5
|
+
* about every child, and the two things that can go wrong with that are both
|
|
6
|
+
* properties of a list: a row wider than the overlay (which silently desynchronises
|
|
7
|
+
* `OverlayFrame`'s position indicator) and a row count that grows without a bound.
|
|
8
|
+
* Both are assertable here, against plain strings, without a terminal - which is the
|
|
9
|
+
* same reason `layout/team-panel.ts` and `layout/budget.ts` are separate modules.
|
|
10
|
+
*
|
|
11
|
+
* LIVE-ONLY RULE (§4.4.2, RV-P0-2). The board has two sources with DIFFERENT fields:
|
|
12
|
+
* a live `TeamSnapshot` (has `messageCount`, `startedAt`, the activity prose and the
|
|
13
|
+
* timeline) and a settled `kind: 'team'` entry (has `durationMs` and the runs, and
|
|
14
|
+
* nothing else - `agent/reducer.ts`'s entry member carries no `messageCount` and no
|
|
15
|
+
* `startedAt`). A section whose source is missing is OMITTED, never filled with a
|
|
16
|
+
* zero: `mail 0` would be a lie, and an empty section title tells the reader nothing.
|
|
17
|
+
* The one section that survives both sources is `result` (`summary` / `error` is
|
|
18
|
+
* never stripped), which is precisely the fact a user opens the board to find.
|
|
19
|
+
*
|
|
20
|
+
* ASCII ONLY: `src/ui/**` is inside `glyphs.test.ts`'s static scan, so every symbol
|
|
21
|
+
* (the ellipsis, the separator, the cursor) arrives through `glyphs`.
|
|
22
|
+
*/
|
|
23
|
+
import type { Entry } from '../../agent/reducer.js';
|
|
24
|
+
import type { SubagentRun, TeamSnapshot } from '../../team/types.js';
|
|
25
|
+
import type { Glyphs } from '../glyphs.js';
|
|
26
|
+
export type BoardRowKind = 'summary' | 'blank' | 'section' | 'child' | 'field' | 'event' | 'hint';
|
|
27
|
+
/** Which palette slot a row draws with. Mapped to `Theme` one level up, in the component. */
|
|
28
|
+
export type BoardTone = 'accent' | 'primary' | 'muted' | 'warn' | 'error' | 'done' | 'hint';
|
|
29
|
+
export interface BoardRow {
|
|
30
|
+
/** React key: stable and unique (`kind:subject:index`). */
|
|
31
|
+
key: string;
|
|
32
|
+
kind: BoardRowKind;
|
|
33
|
+
/** One already-folded line, safe to hand to `<Text wrap="truncate">`. */
|
|
34
|
+
text: string;
|
|
35
|
+
tone: BoardTone;
|
|
36
|
+
selected?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface BoardSource {
|
|
39
|
+
dispatchId: string;
|
|
40
|
+
active: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* When the dispatch started. **A settled `team` entry has no such field**
|
|
43
|
+
* (`agent/reducer.ts`'s entry member), so it is optional: with no `startedAt` the
|
|
44
|
+
* summary shows `durationMs` alone rather than inventing an absolute instant.
|
|
45
|
+
*/
|
|
46
|
+
startedAt?: number;
|
|
47
|
+
endedAt?: number;
|
|
48
|
+
requested: number;
|
|
49
|
+
runs: readonly SubagentRun[];
|
|
50
|
+
/**
|
|
51
|
+
* Team-mail count. Live snapshots carry it; a settled entry does **not** - absent
|
|
52
|
+
* means the clause is omitted rather than rendered as `mail 0` (RV-P0-2).
|
|
53
|
+
*/
|
|
54
|
+
messageCount?: number;
|
|
55
|
+
/** A settled entry's own total (`teamEnd` writes it). */
|
|
56
|
+
durationMs?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The board's single source shape, resolved in ONE place.
|
|
60
|
+
*
|
|
61
|
+
* PRIORITY: live snapshot > (the component's frozen copy) > the most recent settled
|
|
62
|
+
* entry. This function answers the first and the third; the frozen copy belongs to
|
|
63
|
+
* the component because it only exists while a dispatch is being read (§4.4.5).
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveBoardSource(input: {
|
|
66
|
+
team: TeamSnapshot | null;
|
|
67
|
+
entries: readonly Entry[];
|
|
68
|
+
}): BoardSource | null;
|
|
69
|
+
/**
|
|
70
|
+
* The child the board is showing, resolved from its LABEL every frame (D-8).
|
|
71
|
+
*
|
|
72
|
+
* Labels are the only stable handle: `panel-rows.ts` re-sorts the roster by phase, so
|
|
73
|
+
* an index would silently jump to a different child the moment one of them settles.
|
|
74
|
+
* Fallback order is "the first running child, else the first child", which is the
|
|
75
|
+
* one a reader opening the board mid-dispatch wants.
|
|
76
|
+
*/
|
|
77
|
+
export declare function resolveSelection(runs: readonly SubagentRun[], label: string | null): SubagentRun | null;
|
|
78
|
+
export interface AgentBoardInput {
|
|
79
|
+
source: BoardSource;
|
|
80
|
+
/** Overlay content width. */
|
|
81
|
+
cols: number;
|
|
82
|
+
now: number;
|
|
83
|
+
selectedLabel: string | null;
|
|
84
|
+
glyphs: Glyphs;
|
|
85
|
+
native: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The board's rows, in the order a reader wants them: what the dispatch did, then who
|
|
89
|
+
* is in it, then everything about the selected child, then its history.
|
|
90
|
+
*
|
|
91
|
+
* Bounded by `TEAM_LIMITS.boardMaxRows`, and the truncation is ANNOUNCED rather than
|
|
92
|
+
* silent - a list that stops mid-fact with no notice reads as a rendering bug.
|
|
93
|
+
*/
|
|
94
|
+
export declare function buildAgentBoardRows(input: AgentBoardInput): BoardRow[];
|
|
95
|
+
/**
|
|
96
|
+
* The facts, in a FIXED order, and with the zero-valued ones ABSENT.
|
|
97
|
+
*
|
|
98
|
+
* `supervisor` / `retries` / `compactions` / `truncated` only appear when they carry a
|
|
99
|
+
* fact; a row reading `supervisor 0` teaches a reader nothing and pushes the section
|
|
100
|
+
* a row down for every child that was never touched.
|
|
101
|
+
*/
|
|
102
|
+
export declare function factPairs(run: SubagentRun, glyphs: Glyphs): [string, string][];
|