@aragon-agent/cli 0.6.35 → 0.6.37
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 +3173 -3071
- package/dist/agent/agent-mode.d.ts +20 -4
- package/dist/agent/agent-mode.js +20 -4
- package/dist/agent/controller.d.ts +38 -3
- package/dist/agent/controller.js +64 -4
- package/dist/agent/headless.js +31 -0
- 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 +131 -4
- 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/load.d.ts +10 -0
- package/dist/config/load.js +8 -1
- package/dist/config/schema.d.ts +68 -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/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 +310 -46
- package/dist/ui/Composer.d.ts +6 -2
- package/dist/ui/Composer.js +2 -2
- package/dist/ui/Header.d.ts +9 -1
- package/dist/ui/Header.js +2 -2
- package/dist/ui/PromptInput.d.ts +21 -1
- package/dist/ui/PromptInput.js +21 -1
- package/dist/ui/ScrollbackShell.d.ts +19 -19
- package/dist/ui/ScrollbackShell.js +8 -1
- package/dist/ui/StatusBar.d.ts +5 -0
- package/dist/ui/StatusBar.js +1 -0
- package/dist/ui/TeamPanel.d.ts +26 -44
- package/dist/ui/TeamPanel.js +46 -135
- package/dist/ui/TodoPanel.d.ts +17 -14
- package/dist/ui/TodoPanel.js +146 -38
- package/dist/ui/Transcript.js +1 -1
- package/dist/ui/composer-input.d.ts +10 -0
- package/dist/ui/composer-input.js +57 -24
- 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 +13 -3
- package/dist/ui/layout/header-hints.js +48 -14
- 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 +23 -3
- package/dist/ui/overlays/AgentsBoardOverlay.d.ts +57 -0
- package/dist/ui/overlays/AgentsBoardOverlay.js +183 -0
- package/dist/ui/overlays/HelpOverlay.js +17 -2
- 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/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/band.js +14 -1
- package/dist/ui/team-status.d.ts +25 -0
- package/dist/ui/team-status.js +31 -3
- 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 +9 -1
- package/package.json +2 -2
package/dist/ui/PromptInput.d.ts
CHANGED
|
@@ -102,8 +102,28 @@ interface PromptInputProps {
|
|
|
102
102
|
* paste payloads with it.
|
|
103
103
|
*/
|
|
104
104
|
draftClearNonce?: number;
|
|
105
|
+
/**
|
|
106
|
+
* App-owned ONE-SHOT fill (quick-prompt-library D-6 / §4.6).
|
|
107
|
+
*
|
|
108
|
+
* The payload-carrying sibling of `draftClearNonce`: `{ nonce, text, submit }`, with
|
|
109
|
+
* the same monotonic-nonce discipline, because a nonce is the only thing that makes
|
|
110
|
+
* an effect idempotent under StrictMode and re-renders (I-8). It is applied through
|
|
111
|
+
* `transact`, NOT by dispatching and calling `onSubmit` here: the composer has
|
|
112
|
+
* exactly one submission path and a fill must not become the second one (P0-1 / I-9).
|
|
113
|
+
*/
|
|
114
|
+
draftFill?: DraftFill;
|
|
115
|
+
/** `prompts.insertMode`: what a fill does to a draft that already has text. */
|
|
116
|
+
insertMode?: 'insert' | 'replace';
|
|
105
117
|
scrolledLines?: number;
|
|
106
118
|
}
|
|
119
|
+
/** A one-shot "put this text in the draft" payload (quick-prompt-library §4.6). */
|
|
120
|
+
export interface DraftFill {
|
|
121
|
+
/** Monotonic; the same value is applied at most once. */
|
|
122
|
+
nonce: number;
|
|
123
|
+
text: string;
|
|
124
|
+
/** `true` = apply and submit in ONE transaction (the picker's `Ctrl+Enter`). */
|
|
125
|
+
submit?: boolean;
|
|
126
|
+
}
|
|
107
127
|
export interface ScrollChip {
|
|
108
128
|
text: string;
|
|
109
129
|
/** FIXED width, so the number gaining a digit cannot re-wrap the draft (P2-4). */
|
|
@@ -168,5 +188,5 @@ export declare function fileTokenAt(buffer: string, cursor: number): {
|
|
|
168
188
|
end: number;
|
|
169
189
|
query: string;
|
|
170
190
|
} | null;
|
|
171
|
-
export declare function PromptInput({ cols: colsProp, terminalRows: terminalRowsProp, statusExpanded, cursorVisible, onInteraction, isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, onCompletionContextChange, deleteDisambiguated, agentMode, onSubmit, onHelp, onEscape, onEscapeDismiss, reducedMotion, borderColor, onDraftChange, draftClearNonce, onNotice, }: PromptInputProps): React.ReactElement;
|
|
191
|
+
export declare function PromptInput({ cols: colsProp, terminalRows: terminalRowsProp, statusExpanded, cursorVisible, onInteraction, isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, onCompletionContextChange, deleteDisambiguated, agentMode, onSubmit, onHelp, onEscape, onEscapeDismiss, reducedMotion, borderColor, onDraftChange, draftClearNonce, draftFill, insertMode, onNotice, }: PromptInputProps): React.ReactElement;
|
|
172
192
|
export {};
|
package/dist/ui/PromptInput.js
CHANGED
|
@@ -269,7 +269,7 @@ function isControlSeq(input) {
|
|
|
269
269
|
// ---------------------------------------------------------------------------
|
|
270
270
|
// Component
|
|
271
271
|
// ---------------------------------------------------------------------------
|
|
272
|
-
export function PromptInput({ cols: colsProp, terminalRows: terminalRowsProp, statusExpanded = false, cursorVisible = true, onInteraction, isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, onCompletionContextChange, deleteDisambiguated = false, agentMode, onSubmit, onHelp, onEscape, onEscapeDismiss, reducedMotion = false, borderColor, onDraftChange, draftClearNonce, onNotice, }) {
|
|
272
|
+
export function PromptInput({ cols: colsProp, terminalRows: terminalRowsProp, statusExpanded = false, cursorVisible = true, onInteraction, isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, onCompletionContextChange, deleteDisambiguated = false, agentMode, onSubmit, onHelp, onEscape, onEscapeDismiss, reducedMotion = false, borderColor, onDraftChange, draftClearNonce, draftFill, insertMode = 'insert', onNotice, }) {
|
|
273
273
|
const { stdout } = useStdout();
|
|
274
274
|
const [editor, dispatch] = useReducer(editorReducer, INITIAL_EDITOR_STATE);
|
|
275
275
|
const { buffer, cursor, historyIndex, dismissed, sel } = editor;
|
|
@@ -466,6 +466,26 @@ export function PromptInput({ cols: colsProp, terminalRows: terminalRowsProp, st
|
|
|
466
466
|
onNotice?.('warn', plan.notice);
|
|
467
467
|
}
|
|
468
468
|
};
|
|
469
|
+
// --- App-requested fill (quick-prompt-library §4.6 / D-6). -----------------
|
|
470
|
+
//
|
|
471
|
+
// THE NONCE IS THE DEPENDENCY, NOT THE TEXT, for the reason the clear effect above
|
|
472
|
+
// records: two fills of the same prompt in a row must both apply, and keying on the
|
|
473
|
+
// payload would silently drop the second one. `transact` is deliberately absent from
|
|
474
|
+
// the dependency list - it closes over THIS render's `editor`, and depending on it
|
|
475
|
+
// would re-run (and re-fill) the effect on every keystroke.
|
|
476
|
+
const appliedFill = useRef(0);
|
|
477
|
+
useEffect(() => {
|
|
478
|
+
if (!draftFill || draftFill.nonce <= appliedFill.current)
|
|
479
|
+
return;
|
|
480
|
+
appliedFill.current = draftFill.nonce;
|
|
481
|
+
transact([{
|
|
482
|
+
kind: 'fill',
|
|
483
|
+
text: draftFill.text,
|
|
484
|
+
mode: insertMode,
|
|
485
|
+
submit: draftFill.submit === true,
|
|
486
|
+
}]);
|
|
487
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- see the note above
|
|
488
|
+
}, [draftFill, insertMode]);
|
|
469
489
|
const recallUp = () => {
|
|
470
490
|
if (history.length === 0)
|
|
471
491
|
return;
|
|
@@ -18,22 +18,22 @@
|
|
|
18
18
|
* transcript; without one the rail and the roster simply had nowhere to go, so the
|
|
19
19
|
* default mode showed neither. The band is a row pool inside the live region:
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
* | tail window
|
|
23
|
-
*
|
|
24
|
-
* | team roster
|
|
25
|
-
*
|
|
21
|
+
* +----------------------------------------------+----------------+
|
|
22
|
+
* | tail window (height = band.previewRows) | TODO rail |
|
|
23
|
+
* +----------------------------------------------+ (height = |
|
|
24
|
+
* | team roster (width = contentCols, height = band.teamRows) |
|
|
25
|
+
* +----------------------------------------------------------------+
|
|
26
26
|
*
|
|
27
|
-
* THE ROSTER IS
|
|
28
|
-
* (tui-subagent-
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
27
|
+
* THE ROSTER IS A FULL-WIDTH SLOT BELOW THE ROW REGION
|
|
28
|
+
* (tui-subagent-panel §4.1.2 / D-2). It used to be the band's LEFT COLUMN bottom
|
|
29
|
+
* slot, which capped its width at `previewCols`: with the TODO rail visible that is
|
|
30
|
+
* 84 columns on a 100-column terminal, so the row's right-aligned clock could never
|
|
31
|
+
* reach the screen's edge however carefully the row itself was laid out. It is now a
|
|
32
|
+
* SIBLING of the row region and spans `contentCols`, so the clock lands on the last
|
|
33
|
+
* column the terminal can draw. The READING is unchanged, and it is what
|
|
34
|
+
* `tui-subagent-double-roster` A-3 established: the roster is transient status about
|
|
35
|
+
* the run in progress, so it belongs against the composer, where the user's eyes
|
|
36
|
+
* already are and where full-screen mode's viewport `footer` puts it.
|
|
37
37
|
*
|
|
38
38
|
* Every box in it has an explicit `height` and `width` plus `overflow="hidden"`, and
|
|
39
39
|
* that is a correctness requirement rather than tidiness: a box that grows with its
|
|
@@ -88,12 +88,12 @@ export interface ScrollbackShellProps {
|
|
|
88
88
|
/** Real components for the tail window (`LiveTail`). */
|
|
89
89
|
tail?: React.ReactNode;
|
|
90
90
|
/**
|
|
91
|
-
* The subagent roster: the
|
|
92
|
-
*
|
|
93
|
-
*
|
|
91
|
+
* The subagent roster: the band's FULL-WIDTH bottom slot, i.e. the rows closest to
|
|
92
|
+
* the composer. It is the only live subagent surface during a dispatch - the
|
|
93
|
+
* transcript's `team` card is suppressed while it is active.
|
|
94
94
|
*/
|
|
95
95
|
team?: React.ReactNode;
|
|
96
|
-
/** The TODO rail, drawn as the
|
|
96
|
+
/** The TODO rail, drawn as the row region's right-hand column. */
|
|
97
97
|
rail?: React.ReactNode;
|
|
98
98
|
composer: React.ReactNode;
|
|
99
99
|
status: React.ReactNode;
|
|
@@ -11,9 +11,16 @@ export function ScrollbackShell({ budget, header, overlay, liveTailLines, opener
|
|
|
11
11
|
// fixed (`flex-start`); `tail` alone may fall back to `flex-end` when it overflows.
|
|
12
12
|
const leftColumn = opener ?? tail;
|
|
13
13
|
const tailLines = liveTailLines.slice(-budget.liveTailRows);
|
|
14
|
+
// I-1 (tui-subagent-panel §4.1.3): `rowRegion + band.teamRows <= band.bandRows`. In
|
|
15
|
+
// `fill` mode `previewRows === railRows === bandRows - teamRows`, so the two heights
|
|
16
|
+
// add back up to the band exactly; `band.ts` owns both numbers and this is their
|
|
17
|
+
// only consumer.
|
|
18
|
+
const rowRegion = Math.max(band.previewRows, band.railRows);
|
|
14
19
|
// The overlay and the band are alternatives, not siblings: the band reports
|
|
15
20
|
// `bandRows = remaining` for an overlay frame, so drawing both would double the
|
|
16
21
|
// region's height and take the frame to the terminal height (the `ESC[3J` case).
|
|
17
|
-
const top = band.reason === 'overlay' ? (_jsx(Box, { flexDirection: "column", height: budget.overlayRows, flexShrink: 0, overflow: "hidden", children: overlay })) : band.bandRows > 0 ? (
|
|
22
|
+
const top = band.reason === 'overlay' ? (_jsx(Box, { flexDirection: "column", height: budget.overlayRows, flexShrink: 0, overflow: "hidden", children: overlay })) : band.bandRows > 0 ? (
|
|
23
|
+
// ROW REGION ABOVE, FULL-WIDTH ROSTER BELOW (tui-subagent-panel §4.1.2 / D-2).
|
|
24
|
+
_jsxs(Box, { flexDirection: "column", height: band.bandRows, flexShrink: 0, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "row", height: rowRegion, flexShrink: 0, overflow: "hidden", children: [_jsx(Box, { flexDirection: "column", width: budget.previewCols, height: rowRegion, flexShrink: 0, overflow: "hidden", children: band.previewRows > 0 && (_jsx(Box, { flexDirection: "column", height: band.previewRows, flexShrink: 0, justifyContent: "flex-start", overflow: "hidden", children: leftColumn ?? tailLines.map((line, index) => (_jsx(Text, { wrap: "truncate", color: theme.muted, children: line }, index))) })) }), band.railRows > 0 && (_jsx(Box, { height: band.railRows, width: band.railCols, flexShrink: 0, overflow: "hidden", children: rail }))] }), band.teamRows > 0 && (_jsx(Box, { width: width, height: band.teamRows, flexShrink: 0, overflow: "hidden", children: team }))] })) : null;
|
|
18
25
|
return (_jsxs(Box, { flexDirection: "column", height: budget.liveRows, width: width, overflow: "hidden", flexShrink: 0, children: [budget.headerRows > 0 && (_jsx(Box, { height: budget.headerRows, flexShrink: 0, overflow: "hidden", children: header })), top, _jsx(Box, { flexDirection: "column", height: budget.composerSlotRows, justifyContent: "flex-end", flexShrink: 0, overflow: "hidden", children: composer }), _jsx(Box, { height: budget.statusRows > 0 ? 1 : 0, flexShrink: 0, overflow: "hidden", children: status }), budget.statusRows === 2 && (_jsx(Box, { height: 1, flexShrink: 0, overflow: "hidden", children: details }))] }));
|
|
19
26
|
}
|
package/dist/ui/StatusBar.d.ts
CHANGED
package/dist/ui/StatusBar.js
CHANGED
|
@@ -41,6 +41,7 @@ export function StatusBar(props) {
|
|
|
41
41
|
compacting: props.compactionActive?.inFlight, retryActive: props.retryActive,
|
|
42
42
|
mode: props.agentMode, pendingMode: props.pendingAgentMode, servicesActive: props.servicesActive,
|
|
43
43
|
teamActive: props.teamActive, todoActive: props.todoActive,
|
|
44
|
+
...(props.acceptanceActive ? { acceptanceActive: props.acceptanceActive } : {}),
|
|
44
45
|
ecoRung: props.ecoRung, fastActive: props.fastActive, scrolledLines: props.scrolledLines,
|
|
45
46
|
});
|
|
46
47
|
const tones = { normal: props.theme.primary, muted: props.theme.muted,
|
package/dist/ui/TeamPanel.d.ts
CHANGED
|
@@ -20,9 +20,18 @@
|
|
|
20
20
|
* transcript's `team` card is suppressed while its dispatch is active
|
|
21
21
|
* (`team-status.ts::isLiveTeamCard`), so this roster is what the user watches
|
|
22
22
|
* during a dispatch and the card becomes the history afterwards. Native mode
|
|
23
|
-
* draws it as the
|
|
24
|
-
*
|
|
25
|
-
* footer, which is already above the
|
|
23
|
+
* draws it as the band's BOTTOM, FULL-WIDTH slot (`ScrollbackShell`), i.e.
|
|
24
|
+
* directly above the composer and spanning the TODO rail's columns as well; the
|
|
25
|
+
* full-screen mode draws it in the viewport footer, which is already above the
|
|
26
|
+
* composer.
|
|
27
|
+
*
|
|
28
|
+
* THE ROW IS A TABLE, NOT A PARAGRAPH (tui-subagent-panel §0.1 / §4.2). The
|
|
29
|
+
* columns are placed by COMPUTED widths (`layout/team-row.ts`) inside fixed-width
|
|
30
|
+
* boxes, never by `flexGrow`: a fixed-height parent box makes Yoga size a column
|
|
31
|
+
* to its widest child, so the old row measured 48 columns inside a 100-column
|
|
32
|
+
* terminal and the clock stopped beside the description instead of at the right
|
|
33
|
+
* edge. The description is the only flexible cell, which is what keeps the two
|
|
34
|
+
* right-hand cells at their absolute offsets.
|
|
26
35
|
*
|
|
27
36
|
* EVERY PHASE -> GLYPH / COLOUR / COUNT DECISION COMES FROM `team-status.ts`.
|
|
28
37
|
* The local copies this file used to carry are gone on purpose: the transcript
|
|
@@ -33,6 +42,20 @@ import type { Theme } from './theme.js';
|
|
|
33
42
|
import type { TermCapabilities } from './capabilities.js';
|
|
34
43
|
import { type TeamPanelLayout } from './layout/team-panel.js';
|
|
35
44
|
import type { SubagentRun, TeamSnapshot } from '../team/types.js';
|
|
45
|
+
/**
|
|
46
|
+
* THE ACTIVITY / BADGE HELPERS LIVE IN `layout/team-row.ts` NOW (v2 implementation
|
|
47
|
+
* note, recorded in the spec's "implementation findings"). `teamRowCells` has to
|
|
48
|
+
* compose the activity text with the supervisor badge, so keeping those functions
|
|
49
|
+
* in this component would make the two modules import each other. They are
|
|
50
|
+
* re-exported here so every existing import site keeps its path.
|
|
51
|
+
*/
|
|
52
|
+
export { activityBudget, activityLine, activityWithBadge, overseerBadge, teamClockText, teamRowCells, teamRowGeometry, } from './layout/team-row.js';
|
|
53
|
+
/**
|
|
54
|
+
* The panel's one supervisor status line (AC-5): the most recent intervention
|
|
55
|
+
* across the roster, human-phrased. Empty when the supervisor has not acted
|
|
56
|
+
* yet - furniture that says nothing is worse than no line.
|
|
57
|
+
*/
|
|
58
|
+
export declare function supervisorStatusLine(runs: SubagentRun[], at: number): string;
|
|
36
59
|
export interface TeamPanelProps {
|
|
37
60
|
snapshot: TeamSnapshot;
|
|
38
61
|
/** Terminal rows, for the one-line collapse below `panelCollapseRows`. */
|
|
@@ -50,45 +73,4 @@ export interface TeamPanelProps {
|
|
|
50
73
|
now?: number;
|
|
51
74
|
layout?: TeamPanelLayout;
|
|
52
75
|
}
|
|
53
|
-
export declare function activityBudget(cols: number): number;
|
|
54
|
-
/**
|
|
55
|
-
* The middle column: what this child is doing right now, in a few words.
|
|
56
|
-
*
|
|
57
|
-
* Two width policies, answering different questions. `wide` decides WHICH FACT
|
|
58
|
-
* to show - a whole path or its basename, a whole command line or its program
|
|
59
|
-
* name - mirroring `StatusBar`'s `statusCompactCols` degradation rather than
|
|
60
|
-
* inventing a second threshold. `activityBudget(cols)` decides HOW MUCH OF IT
|
|
61
|
-
* FITS, and it is a continuous function of width rather than a threshold,
|
|
62
|
-
* because the failure it prevents - a row wider than the terminal - is
|
|
63
|
-
* continuous.
|
|
64
|
-
*
|
|
65
|
-
* Exported for the same reason `buildSubagentTools` is: the width bound is the
|
|
66
|
-
* property this round has to keep, and asserting it through a rendered frame
|
|
67
|
-
* cannot distinguish "the arithmetic clamped it" from "Ink truncated it" - which
|
|
68
|
-
* is precisely the confusion that let v1's fixed-width column look fine at 100
|
|
69
|
-
* columns and break at 80.
|
|
70
|
-
*/
|
|
71
|
-
export declare function activityLine(run: SubagentRun, cols: number): string;
|
|
72
|
-
/**
|
|
73
|
-
* The row's supervisor badge (subagent-overseer-v2 AC-5): what the supervisor
|
|
74
|
-
* last did to THIS child and how long ago, e.g. `nudged 2m03s`.
|
|
75
|
-
*
|
|
76
|
-
* `lastIntervention.at` is ABSOLUTE epoch ms (R-P2-3), the same units as
|
|
77
|
-
* `startedAt`, so this is one subtraction - no second clock convention.
|
|
78
|
-
*/
|
|
79
|
-
export declare function overseerBadge(run: SubagentRun, at: number): string;
|
|
80
|
-
/**
|
|
81
|
-
* The activity column WITH the badge composed in (AC-5 / risk table): the
|
|
82
|
-
* badge rides the SAME `activityBudget(cols)` allocation the prose uses, so
|
|
83
|
-
* a row can never grow past its terminal; on a narrow terminal the badge
|
|
84
|
-
* degrades to the action word alone - the who-acted fact outranks the
|
|
85
|
-
* what-they-are-doing tail exactly when there is room for only one.
|
|
86
|
-
*/
|
|
87
|
-
export declare function activityWithBadge(run: SubagentRun, cols: number, at: number): string;
|
|
88
|
-
/**
|
|
89
|
-
* The panel's one supervisor status line (AC-5): the most recent intervention
|
|
90
|
-
* across the roster, human-phrased. Empty when the supervisor has not acted
|
|
91
|
-
* yet - furniture that says nothing is worse than no line.
|
|
92
|
-
*/
|
|
93
|
-
export declare function supervisorStatusLine(runs: SubagentRun[], at: number): string;
|
|
94
76
|
export declare function TeamPanel({ snapshot, rows, cols, reducedMotion, theme, caps, now, layout, }: TeamPanelProps): React.ReactElement | null;
|
package/dist/ui/TeamPanel.js
CHANGED
|
@@ -1,133 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import Spinner from 'ink-spinner';
|
|
4
4
|
import { pickGlyphs } from './glyphs.js';
|
|
5
|
-
import {
|
|
5
|
+
import { childStatus, isRunningStatus, rosterCounts, rosterHeadline, statusColor, } from './team-status.js';
|
|
6
6
|
import { formatDuration } from '../agent/usage.js';
|
|
7
|
-
import { describeToolActivity, sanitizeActivity } from '../team/activity.js';
|
|
8
|
-
import { TEAM_LIMITS } from '../team/limits.js';
|
|
9
7
|
import { buildTeamPanelLayout } from './layout/team-panel.js';
|
|
8
|
+
import { interventionWord, teamRowCells, teamRowGeometry, TEAM_CLOCK_CELLS, } from './layout/team-row.js';
|
|
9
|
+
import { fitCells } from './layout/cells.js';
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* marker(3) + label(8) + description(flex) + 2 + activity + 2 + elapsed(~6)
|
|
17
|
-
*
|
|
18
|
-
* and the comment on the description cell below promises that activity and
|
|
19
|
-
* elapsed never lose characters, i.e. that `description` absorbs ALL width
|
|
20
|
-
* pressure. A 64-character prose tail plus `writing: ` is 73, which on an
|
|
21
|
-
* 80-column terminal needs 94 columns before `description` gets one. The promise
|
|
22
|
-
* cannot be kept by wishing; the column has to be sized from `cols`.
|
|
23
|
-
*
|
|
24
|
-
* `WIDE_CHAR_ALLOWANCE` is the concession to there being no display-width helper
|
|
25
|
-
* in production code: this budget is denominated in code units while the
|
|
26
|
-
* terminal is denominated in columns, and CJK and emoji occupy two columns per
|
|
27
|
-
* unit, so the budget is halved against the reserve rather than spent to the
|
|
28
|
-
* last cell. Erring small costs a few characters of prose; erring large costs
|
|
29
|
-
* the elapsed column.
|
|
11
|
+
* THE ACTIVITY / BADGE HELPERS LIVE IN `layout/team-row.ts` NOW (v2 implementation
|
|
12
|
+
* note, recorded in the spec's "implementation findings"). `teamRowCells` has to
|
|
13
|
+
* compose the activity text with the supervisor badge, so keeping those functions
|
|
14
|
+
* in this component would make the two modules import each other. They are
|
|
15
|
+
* re-exported here so every existing import site keeps its path.
|
|
30
16
|
*/
|
|
31
|
-
|
|
32
|
-
const WIDE_CHAR_ALLOWANCE = 2;
|
|
33
|
-
export function activityBudget(cols) {
|
|
34
|
-
const spare = Math.floor((cols - ROW_RESERVED_COLS) / WIDE_CHAR_ALLOWANCE);
|
|
35
|
-
return Math.max(TEAM_LIMITS.activityMinChars, Math.min(TEAM_LIMITS.activityChars, spare));
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* The middle column: what this child is doing right now, in a few words.
|
|
39
|
-
*
|
|
40
|
-
* Two width policies, answering different questions. `wide` decides WHICH FACT
|
|
41
|
-
* to show - a whole path or its basename, a whole command line or its program
|
|
42
|
-
* name - mirroring `StatusBar`'s `statusCompactCols` degradation rather than
|
|
43
|
-
* inventing a second threshold. `activityBudget(cols)` decides HOW MUCH OF IT
|
|
44
|
-
* FITS, and it is a continuous function of width rather than a threshold,
|
|
45
|
-
* because the failure it prevents - a row wider than the terminal - is
|
|
46
|
-
* continuous.
|
|
47
|
-
*
|
|
48
|
-
* Exported for the same reason `buildSubagentTools` is: the width bound is the
|
|
49
|
-
* property this round has to keep, and asserting it through a rendered frame
|
|
50
|
-
* cannot distinguish "the arithmetic clamped it" from "Ink truncated it" - which
|
|
51
|
-
* is precisely the confusion that let v1's fixed-width column look fine at 100
|
|
52
|
-
* columns and break at 80.
|
|
53
|
-
*/
|
|
54
|
-
export function activityLine(run, cols) {
|
|
55
|
-
const wide = cols >= TEAM_LIMITS.activityWideCols;
|
|
56
|
-
const budget = activityBudget(cols);
|
|
57
|
-
switch (run.phase) {
|
|
58
|
-
case 'queued':
|
|
59
|
-
return 'queued';
|
|
60
|
-
case 'starting':
|
|
61
|
-
// The one row that legitimately restarts its clock, saying why (P2-4):
|
|
62
|
-
// F-2 ranks the running group by `startedAt` and a retry re-stamps it.
|
|
63
|
-
return run.retries ? 'starting (retry)' : 'starting';
|
|
64
|
-
case 'thinking':
|
|
65
|
-
// Clamped AT RENDER, against this terminal's budget. `run.activity` was
|
|
66
|
-
// clamped at STORE against `activityChars`; that is a ceiling, not a fit.
|
|
67
|
-
return run.activity ? `writing: ${sanitizeActivity(run.activity, budget)}` : 'thinking';
|
|
68
|
-
case 'tool':
|
|
69
|
-
case 'waiting':
|
|
70
|
-
return run.lastTool
|
|
71
|
-
? sanitizeActivity(describeToolActivity(run.lastTool, run.activityArgs, wide), budget)
|
|
72
|
-
: run.phase === 'waiting'
|
|
73
|
-
? 'waiting for mail'
|
|
74
|
-
: 'tool';
|
|
75
|
-
case 'done':
|
|
76
|
-
// `compacted N` ON THE SETTLED ROW ONLY (hardening §3.4.4 / W3). A running
|
|
77
|
-
// row is already competing for `activityBudget(cols)` with the child's own
|
|
78
|
-
// prose, and a marker that displaces what the child is DOING right now
|
|
79
|
-
// buys nothing - the dispatch report carries the same fact for good.
|
|
80
|
-
return run.compactions
|
|
81
|
-
? `done ${run.turns} turns compacted ${run.compactions}`
|
|
82
|
-
: `done ${run.turns} turns`;
|
|
83
|
-
case 'failed':
|
|
84
|
-
return 'failed';
|
|
85
|
-
case 'aborted':
|
|
86
|
-
return 'aborted';
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
/** The past-tense word the supervisor badge and status line render. */
|
|
90
|
-
function interventionWord(action) {
|
|
91
|
-
switch (action) {
|
|
92
|
-
case 'nudge':
|
|
93
|
-
return 'nudged';
|
|
94
|
-
case 'replace':
|
|
95
|
-
return 'replaced';
|
|
96
|
-
case 'abandon':
|
|
97
|
-
return 'abandoned';
|
|
98
|
-
default:
|
|
99
|
-
return 'waited';
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* The row's supervisor badge (subagent-overseer-v2 AC-5): what the supervisor
|
|
104
|
-
* last did to THIS child and how long ago, e.g. `nudged 2m03s`.
|
|
105
|
-
*
|
|
106
|
-
* `lastIntervention.at` is ABSOLUTE epoch ms (R-P2-3), the same units as
|
|
107
|
-
* `startedAt`, so this is one subtraction - no second clock convention.
|
|
108
|
-
*/
|
|
109
|
-
export function overseerBadge(run, at) {
|
|
110
|
-
const last = run.lastIntervention;
|
|
111
|
-
if (last === undefined)
|
|
112
|
-
return '';
|
|
113
|
-
return `${interventionWord(last.action)} ${formatDuration(Math.max(0, at - last.at))}`;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* The activity column WITH the badge composed in (AC-5 / risk table): the
|
|
117
|
-
* badge rides the SAME `activityBudget(cols)` allocation the prose uses, so
|
|
118
|
-
* a row can never grow past its terminal; on a narrow terminal the badge
|
|
119
|
-
* degrades to the action word alone - the who-acted fact outranks the
|
|
120
|
-
* what-they-are-doing tail exactly when there is room for only one.
|
|
121
|
-
*/
|
|
122
|
-
export function activityWithBadge(run, cols, at) {
|
|
123
|
-
const last = run.lastIntervention;
|
|
124
|
-
if (last === undefined)
|
|
125
|
-
return activityLine(run, cols);
|
|
126
|
-
const badge = overseerBadge(run, at);
|
|
127
|
-
if (cols < TEAM_LIMITS.activityWideCols)
|
|
128
|
-
return interventionWord(last.action);
|
|
129
|
-
return sanitizeActivity(`${badge}; ${activityLine(run, cols)}`, activityBudget(cols));
|
|
130
|
-
}
|
|
17
|
+
export { activityBudget, activityLine, activityWithBadge, overseerBadge, teamClockText, teamRowCells, teamRowGeometry, } from './layout/team-row.js';
|
|
131
18
|
/**
|
|
132
19
|
* The panel's one supervisor status line (AC-5): the most recent intervention
|
|
133
20
|
* across the roster, human-phrased. Empty when the supervisor has not acted
|
|
@@ -149,6 +36,9 @@ export function supervisorStatusLine(runs, at) {
|
|
|
149
36
|
const head = latest.reasonHead.length > 0 ? ` (${latest.reasonHead})` : '';
|
|
150
37
|
return `supervisor: ${interventionWord(latest.action)} ${label} ${ago} ago${head}`;
|
|
151
38
|
}
|
|
39
|
+
/** Columns the `ctrl+b` hint cell spends, and the width below which it is dropped. */
|
|
40
|
+
const HINT_CELLS = 'ctrl+b'.length;
|
|
41
|
+
const HINT_MIN_COLS = 100;
|
|
152
42
|
export function TeamPanel({ snapshot, rows, cols, reducedMotion, theme, caps, now, layout, }) {
|
|
153
43
|
const projection = layout ?? buildTeamPanelLayout({
|
|
154
44
|
snapshot, terminalRows: rows, availableRows: Infinity,
|
|
@@ -162,18 +52,44 @@ export function TeamPanel({ snapshot, rows, cols, reducedMotion, theme, caps, no
|
|
|
162
52
|
// said `queued`.
|
|
163
53
|
const counts = rosterCounts(snapshot.runs);
|
|
164
54
|
const elapsed = formatDuration(Math.max(0, at - snapshot.startedAt));
|
|
165
|
-
const
|
|
55
|
+
const g = teamRowGeometry(cols, glyphs);
|
|
56
|
+
// WIDTH IS BUDGETED ONCE (I-13). `rosterHeadline` subtracts its own
|
|
57
|
+
// HEADER_RESERVED_COLS(16) - it takes the ROW's column count, not a budget - so
|
|
58
|
+
// the caller may only take away the cells this feature ADDS. Subtracting 16 here
|
|
59
|
+
// too would spend the reserve twice and drop `done` / `queued` from a 100-column
|
|
60
|
+
// header that has room for them.
|
|
61
|
+
const showHint = cols >= HINT_MIN_COLS && !projection.collapsed;
|
|
62
|
+
const headlineCols = Math.max(0, cols - TEAM_CLOCK_CELLS - (showHint ? HINT_CELLS : 0));
|
|
63
|
+
const mailText = `${glyphs.teamMail} ${snapshot.messageCount}`;
|
|
64
|
+
// The badge takes its NATURAL width and the flexible left cell gives way: it is
|
|
65
|
+
// the only header cell whose content length varies, and pinning it to a computed
|
|
66
|
+
// width would clip `@ 12` on a row that has room for it.
|
|
67
|
+
const mailCells = Math.min(mailText.length, Math.max(0, cols - TEAM_CLOCK_CELLS));
|
|
68
|
+
/**
|
|
69
|
+
* The header is the row's own geometry one level up: a flexible left cell and
|
|
70
|
+
* fixed right-hand cells, so the dispatch clock and every child clock stand in
|
|
71
|
+
* the SAME column (I-5 / AC-2).
|
|
72
|
+
*
|
|
73
|
+
* THE ELAPSED TIME APPEARS ONLY IN THE CLOCK CELL (§4.3 / RV-P2-9). The old
|
|
74
|
+
* header also carried it inline among the count clauses; with a right-edge clock
|
|
75
|
+
* that would be the same number twice, and the pair could disagree for one frame
|
|
76
|
+
* every 200 ms.
|
|
77
|
+
*/
|
|
78
|
+
const header = (_jsxs(Box, { flexDirection: "row", width: "100%", height: 1, flexShrink: 0, overflow: "hidden", children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, overflow: "hidden", children: _jsxs(Text, { wrap: "truncate", color: theme.accent, bold: true, children: [glyphs.teamAgent, ' team ', _jsx(Text, { color: theme.muted, children: rosterHeadline(counts, glyphs, headlineCols) })] }) }), showHint && (_jsx(Box, { width: HINT_CELLS, flexShrink: 0, justifyContent: "flex-end", children: _jsx(Text, { color: theme.hintFg, children: "ctrl+b" }) })), snapshot.messageCount > 0 && (_jsx(Box, { width: mailCells, flexShrink: 0, justifyContent: "flex-end", children: _jsx(Text, { wrap: "truncate", color: theme.muted, children: mailText }) })), _jsx(Box, { width: TEAM_CLOCK_CELLS, flexShrink: 0, justifyContent: "flex-end", children: _jsx(Text, { wrap: "truncate", color: theme.accent, bold: true, children: elapsed }) })] }));
|
|
166
79
|
// Short terminals or a long draft leave only the shared header projection,
|
|
167
|
-
// which still answers whether anything is running and for how long.
|
|
80
|
+
// which still answers whether anything is running and for how long. The header
|
|
81
|
+
// keeps the count clauses AND the right-edge clock, so the collapsed form loses
|
|
82
|
+
// no information - only the per-child rows.
|
|
168
83
|
if (projection.collapsed) {
|
|
169
|
-
return _jsx(Box, { flexDirection: "column", flexShrink: 0, children: header });
|
|
84
|
+
return _jsx(Box, { flexDirection: "column", width: "100%", flexShrink: 0, children: header });
|
|
170
85
|
}
|
|
171
86
|
// RANKED, not sliced by array index (F-2). The slot pool hands out indices in
|
|
172
87
|
// order, so `slice(0, 5)` showed the five children that finished FIRST and hid
|
|
173
88
|
// everything still working behind `+N more` - at exactly the fan-out widths
|
|
174
89
|
// the requirement permits.
|
|
175
90
|
const { visible, hiddenTotal, hiddenRunning } = projection;
|
|
176
|
-
|
|
91
|
+
const supervisorLine = supervisorStatusLine(snapshot.runs, at);
|
|
92
|
+
return (_jsxs(Box, { flexDirection: "column", width: "100%", flexShrink: 0, children: [header, visible.map((run) => {
|
|
177
93
|
const status = childStatus(run);
|
|
178
94
|
const color = statusColor(status, theme);
|
|
179
95
|
// One spinner per RUNNING row, and the row cap is 5 precisely because
|
|
@@ -181,12 +97,7 @@ export function TeamPanel({ snapshot, rows, cols, reducedMotion, theme, caps, no
|
|
|
181
97
|
// `reducedMotion` and a non-Unicode terminal fall back to the same
|
|
182
98
|
// static marker, exactly as `AssistantEntry` and `ToolCard` already do.
|
|
183
99
|
const animate = isRunningStatus(status) && !reducedMotion && caps.unicode;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// supervisor has actually acted - the per-row badges above already
|
|
188
|
-
// carry the per-child facts.
|
|
189
|
-
const line = supervisorStatusLine(snapshot.runs, at);
|
|
190
|
-
return line ? (_jsxs(Text, { wrap: "truncate", color: theme.muted, children: [' ', line] })) : null;
|
|
191
|
-
})(), snapshot.lastMessage && (_jsxs(Text, { wrap: "truncate", color: theme.muted, children: [' ', glyphs.teamMail, " ", snapshot.lastMessage.from, " ", glyphs.arrowRight, " ", snapshot.lastMessage.to, ": ", snapshot.lastMessage.subject.replace(/\s+/g, ' ')] }))] }));
|
|
100
|
+
const cells = teamRowCells(run, cols, at, glyphs);
|
|
101
|
+
return (_jsxs(Box, { flexDirection: "row", width: "100%", height: 1, flexShrink: 0, overflow: "hidden", children: [_jsx(Box, { width: g.marker, flexShrink: 0, children: _jsxs(Text, { wrap: "truncate", color: color, children: [' ', animate ? _jsx(Spinner, { type: "dots" }) : cells.marker, ' '] }) }), _jsx(Box, { width: g.label, flexShrink: 0, children: _jsx(Text, { wrap: "truncate", color: theme.primary, children: cells.label }) }), _jsx(Box, { flexGrow: 1, flexShrink: 1, overflow: "hidden", children: _jsx(Text, { wrap: "truncate", color: theme.muted, children: cells.description }) }), _jsx(Box, { width: g.gap, flexShrink: 0, children: _jsx(Text, { children: " " }) }), _jsx(Box, { width: g.activity, flexShrink: 0, overflow: "hidden", children: _jsx(Text, { wrap: "truncate", color: color, children: cells.activity }) }), _jsx(Box, { width: g.clock, flexShrink: 0, justifyContent: "flex-end", children: _jsx(Text, { wrap: "truncate", color: theme.muted, children: cells.clock }) })] }, run.label));
|
|
102
|
+
}), hiddenTotal > 0 && (_jsxs(Text, { wrap: "truncate", color: theme.muted, children: [' ', "+", hiddenTotal, " more", hiddenRunning > 0 ? ` (${hiddenRunning} running)` : ''] })), supervisorLine.length > 0 && (_jsxs(Text, { wrap: "truncate", color: theme.muted, children: [' ', fitCells(supervisorLine, Math.max(0, cols - 2), glyphs.ellipsis)] })), snapshot.lastMessage && (_jsxs(Text, { wrap: "truncate", color: theme.muted, children: [' ', glyphs.teamMail, " ", snapshot.lastMessage.from, " ", glyphs.arrowRight, " ", snapshot.lastMessage.to, ": ", snapshot.lastMessage.subject.replace(/\s+/g, ' ')] }))] }));
|
|
192
103
|
}
|
package/dist/ui/TodoPanel.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TodoPanel — the right rail (todo-plan-execution §6.1
|
|
2
|
+
* TodoPanel — the right rail (todo-plan-execution §6.1; two sections per
|
|
3
|
+
* todo-acceptance-criteria §6.9).
|
|
3
4
|
*
|
|
4
5
|
* ASCII ONLY: every glyph comes from `pickGlyphs`; a literal here bypasses the
|
|
5
6
|
* capability probe and shows mojibake on a legacy console (`glyphs.ts` header).
|
|
@@ -8,22 +9,24 @@
|
|
|
8
9
|
* this component is not rendered at all, so a session that never plans has zero
|
|
9
10
|
* columns and zero layout shift (R-g's "no furniture when idle" / AC-24).
|
|
10
11
|
*
|
|
12
|
+
* NO NEW PROP CARRIES THE ACCEPTANCE TABLE (§6.9.1): `snapshot.acceptance` is
|
|
13
|
+
* already inside `snapshot`, and `null` IS the projection of "the capability is
|
|
14
|
+
* off", because a disabled store commits an empty table. Keeping the props surface
|
|
15
|
+
* narrow is what keeps `App`'s two mount points untouched.
|
|
16
|
+
*
|
|
11
17
|
* IT MUST BE MOUNTED IN `AppShell`'s MIDDLE BAND, NEVER IN THE BOTTOM CHROME
|
|
12
18
|
* (C-6 / I-4). This is a COLUMN BESIDE the viewport, and the middle band is the
|
|
13
|
-
* only row-wrapped region there is; moved down into the bottom chrome it would
|
|
14
|
-
*
|
|
19
|
+
* only row-wrapped region there is; moved down into the bottom chrome it would stop
|
|
20
|
+
* being a rail and start eating viewport height, growing and shrinking the
|
|
15
21
|
* transcript every time the list changed length.
|
|
16
22
|
*
|
|
17
23
|
* WIDTH IS MEASURED IN COLUMNS, NEVER IN CODE UNITS (P2-3). This rail is 14-36
|
|
18
24
|
* columns wide and the system prompt's last line is "Respond in the language the
|
|
19
|
-
* user writes in", so CJK
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* safe; a "characters that fit" calculation over `content` would not be. That is
|
|
25
|
-
* the whole of the lesson `TeamPanel`'s `WIDE_CHAR_ALLOWANCE` records at length,
|
|
26
|
-
* and the mitigation here is to have no such budget at all.
|
|
25
|
+
* user writes in", so CJK text in an 18-column rail is the NORMAL case rather than
|
|
26
|
+
* the edge case. Every text cell is `wrap="truncate"` and Ink's truncation is
|
|
27
|
+
* display-width aware, so nothing can overflow the box - and NO ARITHMETIC HERE MAY
|
|
28
|
+
* BUDGET USER TEXT BY `.length`. The count strings and `padStart(2)` cells below are
|
|
29
|
+
* ASCII counts and indices, which is why they are safe.
|
|
27
30
|
*/
|
|
28
31
|
import React from 'react';
|
|
29
32
|
import type { Theme } from './theme.js';
|
|
@@ -35,9 +38,9 @@ export interface TodoPanelProps {
|
|
|
35
38
|
width: number;
|
|
36
39
|
/**
|
|
37
40
|
* From `buildTodoRailLayout` after actual team/menu rows are subtracted.
|
|
38
|
-
* (I-10 / P1-3). The gate that mounts this component and the number it lays
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
+
* (I-10 / P1-3). The gate that mounts this component and the number it lays out
|
|
42
|
+
* against are ONE value computed once in `App`: a panel that mounts on one number
|
|
43
|
+
* and lays out against another renders nothing and reports nothing.
|
|
41
44
|
*/
|
|
42
45
|
rows: number;
|
|
43
46
|
/** Whether the agent is running; gates the spinner (never animate when idle). */
|