@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.
Files changed (185) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +173 -7
  3. package/dist/agent/agent-mode.d.ts +20 -4
  4. package/dist/agent/agent-mode.js +20 -4
  5. package/dist/agent/controller.d.ts +67 -3
  6. package/dist/agent/controller.js +106 -5
  7. package/dist/agent/headless.js +31 -0
  8. package/dist/agent/kit-apply.d.ts +56 -0
  9. package/dist/agent/kit-apply.js +209 -0
  10. package/dist/agent/model-profile-settings.d.ts +28 -0
  11. package/dist/agent/model-profile-settings.js +142 -29
  12. package/dist/agent/reducer.d.ts +13 -2
  13. package/dist/agent/reducer.js +1 -0
  14. package/dist/cli.js +12 -0
  15. package/dist/commands/agents-command.d.ts +15 -0
  16. package/dist/commands/agents-command.js +27 -0
  17. package/dist/commands/builtins.js +135 -4
  18. package/dist/commands/kit-command.d.ts +20 -0
  19. package/dist/commands/kit-command.js +131 -0
  20. package/dist/commands/perf.d.ts +11 -0
  21. package/dist/commands/perf.js +2 -1
  22. package/dist/commands/registry.d.ts +14 -0
  23. package/dist/config/app-paths.d.ts +15 -0
  24. package/dist/config/app-paths.js +33 -2
  25. package/dist/config/env.d.ts +4 -1
  26. package/dist/config/env.js +28 -2
  27. package/dist/config/kit-resolution.d.ts +93 -0
  28. package/dist/config/kit-resolution.js +226 -0
  29. package/dist/config/load.d.ts +10 -0
  30. package/dist/config/load.js +8 -1
  31. package/dist/config/model-kits.d.ts +224 -0
  32. package/dist/config/model-kits.js +302 -0
  33. package/dist/config/model-profile-store.d.ts +25 -0
  34. package/dist/config/model-profiles.d.ts +41 -3
  35. package/dist/config/model-profiles.js +46 -5
  36. package/dist/config/schema.d.ts +84 -0
  37. package/dist/config/schema.js +39 -0
  38. package/dist/config/store.js +12 -1
  39. package/dist/config/ui-state.d.ts +18 -0
  40. package/dist/config/ui-state.js +9 -0
  41. package/dist/exec/events.d.ts +25 -0
  42. package/dist/exec/events.js +4 -0
  43. package/dist/exec/index.js +16 -1
  44. package/dist/exec/runner.js +10 -0
  45. package/dist/input/keymap.js +18 -4
  46. package/dist/proc/limits.d.ts +16 -0
  47. package/dist/proc/limits.js +16 -0
  48. package/dist/proc/supervisor.d.ts +20 -1
  49. package/dist/proc/supervisor.js +42 -14
  50. package/dist/prompts/command.d.ts +34 -0
  51. package/dist/prompts/command.js +237 -0
  52. package/dist/prompts/copy.d.ts +89 -0
  53. package/dist/prompts/copy.js +131 -0
  54. package/dist/prompts/fill.d.ts +51 -0
  55. package/dist/prompts/fill.js +46 -0
  56. package/dist/prompts/limits.d.ts +53 -0
  57. package/dist/prompts/limits.js +53 -0
  58. package/dist/prompts/normalize.d.ts +53 -0
  59. package/dist/prompts/normalize.js +190 -0
  60. package/dist/prompts/resolve.d.ts +31 -0
  61. package/dist/prompts/resolve.js +60 -0
  62. package/dist/prompts/search.d.ts +58 -0
  63. package/dist/prompts/search.js +146 -0
  64. package/dist/prompts/store.d.ts +86 -0
  65. package/dist/prompts/store.js +453 -0
  66. package/dist/prompts/types.d.ts +204 -0
  67. package/dist/prompts/types.js +13 -0
  68. package/dist/session/checkpoint.d.ts +8 -1
  69. package/dist/session/checkpoint.js +6 -0
  70. package/dist/session/persist.d.ts +19 -1
  71. package/dist/session/persist.js +13 -1
  72. package/dist/session/startup-session.d.ts +7 -2
  73. package/dist/session/startup-session.js +5 -2
  74. package/dist/session/store.js +3 -0
  75. package/dist/session/validate-session.js +64 -1
  76. package/dist/team/limits.d.ts +25 -0
  77. package/dist/team/limits.js +26 -0
  78. package/dist/team/runtime.js +27 -2
  79. package/dist/team/settled.d.ts +57 -0
  80. package/dist/team/settled.js +87 -0
  81. package/dist/team/subagent.js +48 -2
  82. package/dist/team/timeline.d.ts +74 -0
  83. package/dist/team/timeline.js +101 -0
  84. package/dist/team/types.d.ts +24 -1
  85. package/dist/todo/acceptance.d.ts +120 -0
  86. package/dist/todo/acceptance.js +399 -0
  87. package/dist/todo/follow-through.d.ts +48 -23
  88. package/dist/todo/follow-through.js +110 -44
  89. package/dist/todo/limits.d.ts +62 -1
  90. package/dist/todo/limits.js +62 -1
  91. package/dist/todo/panel-rows.d.ts +29 -39
  92. package/dist/todo/panel-rows.js +37 -83
  93. package/dist/todo/plan-sections.d.ts +60 -0
  94. package/dist/todo/plan-sections.js +108 -0
  95. package/dist/todo/prompt.d.ts +17 -12
  96. package/dist/todo/prompt.js +59 -40
  97. package/dist/todo/row-window.d.ts +51 -0
  98. package/dist/todo/row-window.js +107 -0
  99. package/dist/todo/store.d.ts +139 -31
  100. package/dist/todo/store.js +271 -73
  101. package/dist/todo/todo-tool.d.ts +33 -8
  102. package/dist/todo/todo-tool.js +186 -30
  103. package/dist/todo/types.d.ts +58 -0
  104. package/dist/ui/App.js +414 -54
  105. package/dist/ui/Composer.d.ts +6 -2
  106. package/dist/ui/Composer.js +2 -2
  107. package/dist/ui/Header.d.ts +17 -1
  108. package/dist/ui/Header.js +8 -2
  109. package/dist/ui/PromptInput.d.ts +21 -1
  110. package/dist/ui/PromptInput.js +22 -2
  111. package/dist/ui/ScrollbackShell.d.ts +22 -7
  112. package/dist/ui/ScrollbackShell.js +8 -1
  113. package/dist/ui/StatusBar.d.ts +18 -0
  114. package/dist/ui/StatusBar.js +11 -3
  115. package/dist/ui/TeamPanel.d.ts +35 -41
  116. package/dist/ui/TeamPanel.js +53 -163
  117. package/dist/ui/TodoPanel.d.ts +17 -14
  118. package/dist/ui/TodoPanel.js +146 -38
  119. package/dist/ui/Transcript.js +23 -3
  120. package/dist/ui/composer-input.d.ts +10 -0
  121. package/dist/ui/composer-input.js +57 -24
  122. package/dist/ui/entries/TeamCard.d.ts +18 -2
  123. package/dist/ui/entries/TeamCard.js +47 -13
  124. package/dist/ui/entries/TodoCard.d.ts +26 -11
  125. package/dist/ui/entries/TodoCard.js +56 -15
  126. package/dist/ui/glyphs.d.ts +19 -0
  127. package/dist/ui/glyphs.js +4 -0
  128. package/dist/ui/layout/agents-board.d.ts +102 -0
  129. package/dist/ui/layout/agents-board.js +389 -0
  130. package/dist/ui/layout/cells.d.ts +61 -0
  131. package/dist/ui/layout/cells.js +156 -0
  132. package/dist/ui/layout/header-hints.d.ts +23 -3
  133. package/dist/ui/layout/header-hints.js +73 -7
  134. package/dist/ui/layout/kit-chip.d.ts +50 -0
  135. package/dist/ui/layout/kit-chip.js +81 -0
  136. package/dist/ui/layout/status-detail-layout.js +9 -0
  137. package/dist/ui/layout/status-layout.d.ts +13 -0
  138. package/dist/ui/layout/team-row.d.ts +139 -0
  139. package/dist/ui/layout/team-row.js +246 -0
  140. package/dist/ui/layout/todo-layout.d.ts +38 -0
  141. package/dist/ui/layout/todo-layout.js +75 -0
  142. package/dist/ui/layout/virtual-window.js +32 -3
  143. package/dist/ui/overlays/AgentsBoardOverlay.d.ts +57 -0
  144. package/dist/ui/overlays/AgentsBoardOverlay.js +183 -0
  145. package/dist/ui/overlays/HelpOverlay.js +24 -2
  146. package/dist/ui/overlays/KitEditor.d.ts +23 -0
  147. package/dist/ui/overlays/KitEditor.js +101 -0
  148. package/dist/ui/overlays/KitManager.d.ts +23 -0
  149. package/dist/ui/overlays/KitManager.js +104 -0
  150. package/dist/ui/overlays/KitPicker.d.ts +49 -0
  151. package/dist/ui/overlays/KitPicker.js +58 -0
  152. package/dist/ui/overlays/ModelProfilePicker.js +16 -6
  153. package/dist/ui/overlays/PromptEditorPage.d.ts +48 -0
  154. package/dist/ui/overlays/PromptEditorPage.js +227 -0
  155. package/dist/ui/overlays/PromptLibraryOverlay.d.ts +37 -0
  156. package/dist/ui/overlays/PromptLibraryOverlay.js +191 -0
  157. package/dist/ui/overlays/PromptPicker.d.ts +36 -0
  158. package/dist/ui/overlays/PromptPicker.js +129 -0
  159. package/dist/ui/overlays/PromptTextArea.d.ts +86 -0
  160. package/dist/ui/overlays/PromptTextArea.js +141 -0
  161. package/dist/ui/overlays/SettingsScreen.js +139 -16
  162. package/dist/ui/overlays/kit-picker-state.d.ts +87 -0
  163. package/dist/ui/overlays/kit-picker-state.js +136 -0
  164. package/dist/ui/overlays/kit-settings-state.d.ts +129 -0
  165. package/dist/ui/overlays/kit-settings-state.js +267 -0
  166. package/dist/ui/overlays/model-profile-state.d.ts +26 -3
  167. package/dist/ui/overlays/model-profile-state.js +59 -6
  168. package/dist/ui/overlays/prompt-library-state.d.ts +162 -0
  169. package/dist/ui/overlays/prompt-library-state.js +335 -0
  170. package/dist/ui/overlays/prompt-picker-state.d.ts +71 -0
  171. package/dist/ui/overlays/prompt-picker-state.js +98 -0
  172. package/dist/ui/scrollback/LiveTail.d.ts +2 -2
  173. package/dist/ui/scrollback/LiveTail.js +7 -2
  174. package/dist/ui/scrollback/band.js +14 -1
  175. package/dist/ui/scrollback/live-preview.js +10 -3
  176. package/dist/ui/scrollback/tail-window.d.ts +12 -0
  177. package/dist/ui/scrollback/tail-window.js +17 -2
  178. package/dist/ui/scrollback/use-scrollback-view.js +7 -0
  179. package/dist/ui/team-status.d.ts +166 -0
  180. package/dist/ui/team-status.js +252 -0
  181. package/dist/ui/transcript-text.js +13 -4
  182. package/dist/ui/use-startup-notices.d.ts +35 -1
  183. package/dist/ui/use-startup-notices.js +29 -2
  184. package/dist/ui/use-wheel-routing.js +10 -2
  185. package/package.json +2 -2
@@ -5,7 +5,7 @@ import type { Theme } from './theme.js';
5
5
  import type { TermCapabilities } from './capabilities.js';
6
6
  import { pickGlyphs } from './glyphs.js';
7
7
  import type { ComposerSubmitResult } from './composer-input.js';
8
- import { type CommandOption } from './PromptInput.js';
8
+ import { type CommandOption, type DraftFill } from './PromptInput.js';
9
9
  import type { RunActivity } from './run-status-row.js';
10
10
  import { type AgentMode } from '../agent/agent-mode.js';
11
11
  /** Legacy caller shape retained for source compatibility; Composer no longer mounts it. */
@@ -77,6 +77,10 @@ export interface ComposerProps {
77
77
  * PromptInput unchanged.
78
78
  */
79
79
  draftClearNonce?: number;
80
+ /** Downward half of the fill channel; forwarded to `PromptInput` unchanged. */
81
+ draftFill?: DraftFill;
82
+ /** `prompts.insertMode`, forwarded unchanged. */
83
+ insertMode?: 'insert' | 'replace';
80
84
  /** Paste refusals from `PromptInput`, routed to `App`'s notice dispatch. */
81
85
  onNotice?: (level: 'warn' | 'error', text: string) => void;
82
86
  theme: Theme;
@@ -101,5 +105,5 @@ declare function hintText(opts: {
101
105
  }): string;
102
106
  /** Compatibility export for existing hint contract tests. */
103
107
  export declare const hintTextForTest: typeof hintText;
104
- export declare function Composer({ deleteDisambiguated, onCompletionContextChange, cols, terminalRows, statusExpanded, cursorVisible, onInteraction, measureRef, isActive, reducedMotion, onEscape, onEscapeDismiss, running, history, commands, cwd, agentMode, popupMaxRows, popupMaxHeight, onPopupRowsChange, onDraftRows, onDraftPresence, draftClearNonce, onNotice, theme, caps, onSubmit, onHelp, }: ComposerProps): React.ReactElement;
108
+ export declare function Composer({ deleteDisambiguated, onCompletionContextChange, cols, terminalRows, statusExpanded, cursorVisible, onInteraction, measureRef, isActive, reducedMotion, onEscape, onEscapeDismiss, running, history, commands, cwd, agentMode, popupMaxRows, popupMaxHeight, onPopupRowsChange, onDraftRows, onDraftPresence, draftClearNonce, draftFill, insertMode, onNotice, theme, caps, onSubmit, onHelp, }: ComposerProps): React.ReactElement;
105
109
  export {};
@@ -36,7 +36,7 @@ function hintText(opts) {
36
36
  }
37
37
  /** Compatibility export for existing hint contract tests. */
38
38
  export const hintTextForTest = hintText;
39
- export function Composer({ deleteDisambiguated, onCompletionContextChange, cols, terminalRows, statusExpanded, cursorVisible, onInteraction, measureRef, isActive, reducedMotion, onEscape, onEscapeDismiss, running, history, commands, cwd, agentMode, popupMaxRows, popupMaxHeight, onPopupRowsChange, onDraftRows, onDraftPresence, draftClearNonce, onNotice, theme, caps, onSubmit, onHelp, }) {
39
+ export function Composer({ deleteDisambiguated, onCompletionContextChange, cols, terminalRows, statusExpanded, cursorVisible, onInteraction, measureRef, isActive, reducedMotion, onEscape, onEscapeDismiss, running, history, commands, cwd, agentMode, popupMaxRows, popupMaxHeight, onPopupRowsChange, onDraftRows, onDraftPresence, draftClearNonce, draftFill, insertMode, onNotice, theme, caps, onSubmit, onHelp, }) {
40
40
  const [hasDraft, setHasDraft] = useState(false);
41
41
  const onDraftChange = useCallback((next) => {
42
42
  setHasDraft(next.hasDraft);
@@ -57,5 +57,5 @@ export function Composer({ deleteDisambiguated, onCompletionContextChange, cols,
57
57
  : hasDraft
58
58
  ? theme.focusBorder ?? theme.primary
59
59
  : theme.idleBorder ?? theme.border;
60
- return (_jsx(Box, { ref: measureRef, width: cols, flexDirection: "column", flexShrink: 0, children: _jsx(PromptInput, { deleteDisambiguated: deleteDisambiguated, onCompletionContextChange: onCompletionContextChange, cols: cols, terminalRows: terminalRows, statusExpanded: statusExpanded, cursorVisible: cursorVisible, onInteraction: onInteraction, isActive: isActive, reducedMotion: reducedMotion, onEscape: onEscape, onEscapeDismiss: onEscapeDismiss, running: running, history: history, commands: commands, cwd: cwd, theme: theme, caps: caps, popupMaxRows: popupMaxRows, popupMaxHeight: popupMaxHeight, onPopupRowsChange: onPopupRowsChange, onSubmit: onSubmit, onHelp: onHelp, agentMode: agentMode, borderColor: borderColor, onDraftChange: onDraftChange, draftClearNonce: draftClearNonce, onNotice: onNotice }) }));
60
+ return (_jsx(Box, { ref: measureRef, width: cols, flexDirection: "column", flexShrink: 0, children: _jsx(PromptInput, { deleteDisambiguated: deleteDisambiguated, onCompletionContextChange: onCompletionContextChange, cols: cols, terminalRows: terminalRows, statusExpanded: statusExpanded, cursorVisible: cursorVisible, onInteraction: onInteraction, isActive: isActive, reducedMotion: reducedMotion, onEscape: onEscape, onEscapeDismiss: onEscapeDismiss, running: running, history: history, commands: commands, cwd: cwd, theme: theme, caps: caps, popupMaxRows: popupMaxRows, popupMaxHeight: popupMaxHeight, onPopupRowsChange: onPopupRowsChange, onSubmit: onSubmit, onHelp: onHelp, agentMode: agentMode, borderColor: borderColor, onDraftChange: onDraftChange, draftClearNonce: draftClearNonce, draftFill: draftFill, insertMode: insertMode, onNotice: onNotice }) }));
61
61
  }
@@ -15,6 +15,22 @@ interface HeaderProps {
15
15
  statusExpanded?: boolean;
16
16
  scrolledLines?: number;
17
17
  overlayOpen?: boolean;
18
+ /**
19
+ * Whether the expanded status row teaches `ctrl+K` (agent-model-kits §4.7).
20
+ *
21
+ * DEFAULTS TO TRUE because that is the requirement - `ctrl+G` must reveal the
22
+ * kit key - and because a default of `false` would leave the one caller that
23
+ * forgets the prop silently teaching nothing.
24
+ */
25
+ kitHint?: boolean;
26
+ /**
27
+ * Whether the collapsed row teaches `ctrl+P` (quick-prompt-library D-15).
28
+ *
29
+ * DEFAULTS TO TRUE for the same reason `kitHint` does: the requirement is that the
30
+ * key be visible, and a default of `false` would leave the one caller that forgets
31
+ * the prop silently teaching nothing. `cfg.prompts.hint` is what turns it off.
32
+ */
33
+ promptsHint?: boolean;
18
34
  }
19
- export declare function Header({ cwd, provider, model, hasKey, variant, theme, caps, columns, statusExpanded, scrolledLines, overlayOpen }: HeaderProps): React.ReactElement;
35
+ export declare function Header({ cwd, provider, model, hasKey, variant, theme, caps, columns, statusExpanded, scrolledLines, overlayOpen, kitHint, promptsHint }: HeaderProps): React.ReactElement;
20
36
  export {};
package/dist/ui/Header.js CHANGED
@@ -3,13 +3,19 @@ import { basename } from 'node:path';
3
3
  import { Box, Text } from 'ink';
4
4
  import { Logo } from './Logo.js';
5
5
  import { planHeaderHints, HEADER_HINT_LIMITS } from './layout/header-hints.js';
6
- export function Header({ cwd, provider, model, hasKey, variant, theme, caps, columns = 80, statusExpanded = false, scrolledLines = 0, overlayOpen = false }) {
6
+ export function Header({ cwd, provider, model, hasKey, variant, theme, caps, columns = 80, statusExpanded = false, scrolledLines = 0, overlayOpen = false, kitHint = true, promptsHint = true }) {
7
7
  // The right-hand hints are planned, not assembled (tui-branch-terminal-shortcut
8
8
  // 3.9 / D-11). `plan.text` is exactly `plan.cells` wide and already carries the
9
9
  // scroll chip's own trailing spacing, so this component can no longer clip a
10
10
  // hint by guessing one cell short - and the ladder that decides which hints
11
11
  // survive a narrow terminal lives in a pure function with its own tests.
12
- const plan = planHeaderHints({ columns, statusExpanded, overlayOpen, scrolledLines });
12
+ //
13
+ // `kitHint` is the `^K switch kit` clause (agent-model-kits §4.7): `App`
14
+ // passes `true` always, because the requirement is that `ctrl+G` reveals the
15
+ // key - and the user with no kits needs that hint most.
16
+ const plan = planHeaderHints({
17
+ columns, statusExpanded, overlayOpen, scrolledLines, kitHint, promptsHint,
18
+ });
13
19
  // THE TWO HALVES ARE SEPARATED BY CELLS, NOT BY LUCK (defect 12). The left half
14
20
  // is `wrap="truncate"`, which draws its ellipsis in the LAST cell it owns; with
15
21
  // the right half starting in the very next cell a narrow terminal read
@@ -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 {};
@@ -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;
@@ -596,7 +616,7 @@ export function PromptInput({ cols: colsProp, terminalRows: terminalRowsProp, st
596
616
  return;
597
617
  }
598
618
  if (key.ctrl)
599
- return; // App owns Ctrl+C / L / T / O.
619
+ return; // App owns Ctrl+C / L / T / O, and now Ctrl+K (the kit picker).
600
620
  if (key.return) {
601
621
  if (key.meta || key.shift) {
602
622
  insert('\n');
@@ -18,11 +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
- * | team roster (height = band.teamRows) | TODO rail |
23
- * +---------------------------------------------+ (height = |
24
- * | tail window (height = band.previewRows) | band.railRows) |
25
- * +---------------------------------------------+-----------------+
21
+ * +----------------------------------------------+----------------+
22
+ * | tail window (height = band.previewRows) | TODO rail |
23
+ * +----------------------------------------------+ (height = |
24
+ * | team roster (width = contentCols, height = band.teamRows) |
25
+ * +----------------------------------------------------------------+
26
+ *
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.
26
37
  *
27
38
  * Every box in it has an explicit `height` and `width` plus `overflow="hidden"`, and
28
39
  * that is a correctness requirement rather than tidiness: a box that grows with its
@@ -76,9 +87,13 @@ export interface ScrollbackShellProps {
76
87
  opener?: React.ReactNode;
77
88
  /** Real components for the tail window (`LiveTail`). */
78
89
  tail?: React.ReactNode;
79
- /** The subagent roster, drawn at the top of the band's left column. */
90
+ /**
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
+ */
80
95
  team?: React.ReactNode;
81
- /** The TODO rail, drawn as the band's right-hand column. */
96
+ /** The TODO rail, drawn as the row region's right-hand column. */
82
97
  rail?: React.ReactNode;
83
98
  composer: React.ReactNode;
84
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 ? (_jsxs(Box, { flexDirection: "row", height: band.bandRows, flexShrink: 0, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", width: budget.previewCols, flexShrink: 0, overflow: "hidden", children: [band.teamRows > 0 && (_jsx(Box, { height: band.teamRows, flexShrink: 0, overflow: "hidden", children: team })), 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 }))] })) : null;
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
  }
@@ -50,6 +50,19 @@ export interface StatusBarProps {
50
50
  * existing test render exactly what they rendered before (I-6).
51
51
  */
52
52
  sessionId?: string;
53
+ /**
54
+ * The agent kit in effect, or absent when there is none (agent-model-kits
55
+ * §4.8 / D-12).
56
+ *
57
+ * OPTIONAL AND ABSENT BY DEFAULT, for the same reason `sessionId` is: with no
58
+ * kit the plan is hidden, `kitCells` is 0, and this row's output is
59
+ * byte-identical to the pre-feature build (I-9).
60
+ */
61
+ kit?: {
62
+ name: string;
63
+ modified: boolean;
64
+ otherNames: readonly string[];
65
+ } | null;
53
66
  agentMode?: AgentMode;
54
67
  pendingAgentMode?: AgentMode | null;
55
68
  teamActive?: {
@@ -60,6 +73,11 @@ export interface StatusBarProps {
60
73
  done: number;
61
74
  total: number;
62
75
  };
76
+ acceptanceActive?: {
77
+ checked: number;
78
+ total: number;
79
+ failed: number;
80
+ };
63
81
  servicesActive?: {
64
82
  live: number;
65
83
  };
@@ -6,6 +6,7 @@ import { pickGlyphs } from './glyphs.js';
6
6
  import { liveSpinner } from './ActivityLine.js';
7
7
  import { planPrimaryStatusFields } from './layout/status-layout.js';
8
8
  import { planSessionChip } from './layout/session-chip.js';
9
+ import { planKitChip } from './layout/kit-chip.js';
9
10
  /** An invisible leading byte change forces Ink to repaint on Ctrl+L. */
10
11
  const redrawChar = (nonce) => nonce % 2 ? String.fromCharCode(0x00a0) : ' ';
11
12
  export function StatusBar(props) {
@@ -21,8 +22,14 @@ export function StatusBar(props) {
21
22
  ...(props.sessionId ? { sessionId: props.sessionId } : {}),
22
23
  });
23
24
  const chipCells = chip.visible ? chip.cells + chip.gap : 0;
25
+ // THE KIT CHIP IS PLANNED SECOND, OUT OF WHAT THE SESSION CHIP LEFT (D-12):
26
+ // the session id is never truncated, so it is not the thing that gives way.
27
+ // With no kit the plan is hidden and `kitCells` is 0, which keeps this row
28
+ // byte-identical to the build before the feature (I-9).
29
+ const kit = planKitChip({ columns: cols - chipCells, kit: props.kit });
30
+ const kitCells = kit.visible ? kit.cells + kit.gap : 0;
24
31
  const plan = planPrimaryStatusFields({
25
- reserveRight: chipCells,
32
+ reserveRight: chipCells + kitCells,
26
33
  columns: cols, phase, pendingCount: props.pendingSteering?.length ?? 0,
27
34
  context: props.context, usageTotal: props.usageTotal,
28
35
  thinkingLevel: props.thinkingLevel, elapsedMs: props.elapsedMs, tokPerSec: props.tokPerSec,
@@ -34,12 +41,13 @@ export function StatusBar(props) {
34
41
  compacting: props.compactionActive?.inFlight, retryActive: props.retryActive,
35
42
  mode: props.agentMode, pendingMode: props.pendingAgentMode, servicesActive: props.servicesActive,
36
43
  teamActive: props.teamActive, todoActive: props.todoActive,
44
+ ...(props.acceptanceActive ? { acceptanceActive: props.acceptanceActive } : {}),
37
45
  ecoRung: props.ecoRung, fastActive: props.fastActive, scrolledLines: props.scrolledLines,
38
46
  });
39
47
  const tones = { normal: props.theme.primary, muted: props.theme.muted,
40
48
  warning: props.theme.noticeWarn, error: props.theme.noticeError,
41
49
  accent: props.theme.accent };
42
- return _jsxs(Box, { width: cols, height: 1, flexShrink: 0, flexDirection: "row", overflow: "hidden", children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { children: redrawChar(props.redrawNonce ?? 0) }) }), _jsx(Box, { width: Math.max(0, cols - 1 - chipCells), flexShrink: 0, flexDirection: "row", overflow: "hidden", children: plan.fields.map((item, index) => _jsxs(React.Fragment, { children: [index > 0 && _jsx(Box, { width: plan.separatorCells, flexShrink: 0, children: _jsx(Text, { color: props.theme.muted, children: plan.separator }) }), _jsx(Box, { width: item.cells, flexShrink: 0, children: _jsx(Text, { color: tones[item.tone], wrap: "truncate", children: item.id === 'phase' && active
50
+ return _jsxs(Box, { width: cols, height: 1, flexShrink: 0, flexDirection: "row", overflow: "hidden", children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { children: redrawChar(props.redrawNonce ?? 0) }) }), _jsx(Box, { width: Math.max(0, cols - 1 - chipCells - kitCells), flexShrink: 0, flexDirection: "row", overflow: "hidden", children: plan.fields.map((item, index) => _jsxs(React.Fragment, { children: [index > 0 && _jsx(Box, { width: plan.separatorCells, flexShrink: 0, children: _jsx(Text, { color: props.theme.muted, children: plan.separator }) }), _jsx(Box, { width: item.cells, flexShrink: 0, children: _jsx(Text, { color: tones[item.tone], wrap: "truncate", children: item.id === 'phase' && active
43
51
  ? _jsxs(_Fragment, { children: [spinner ?? glyph, item.text.slice(glyph.length)] })
44
- : item.text }) })] }, item.id)) }), chip.visible && _jsx(Box, { width: chipCells, flexShrink: 0, justifyContent: "flex-end", overflow: "hidden", children: _jsx(Text, { color: props.theme.muted, wrap: "truncate", children: chip.text }) })] });
52
+ : item.text }) })] }, item.id)) }), kit.visible && _jsx(Box, { width: kitCells, flexShrink: 0, justifyContent: "flex-end", overflow: "hidden", children: _jsx(Text, { color: props.theme.muted, wrap: "truncate", children: kit.text }) }), chip.visible && _jsx(Box, { width: chipCells, flexShrink: 0, justifyContent: "flex-end", overflow: "hidden", children: _jsx(Text, { color: props.theme.muted, wrap: "truncate", children: chip.text }) })] });
45
53
  }
@@ -15,12 +15,47 @@
15
15
  * with the other transient rows — toast, composer, status bar — rather than
16
16
  * inside the scrolling transcript, where it would be interleaved with history
17
17
  * and scroll out of sight exactly when the user wants to watch it.
18
+ *
19
+ * IT IS THE ONLY LIVE SUBAGENT SURFACE (tui-subagent-double-roster A-2). The
20
+ * transcript's `team` card is suppressed while its dispatch is active
21
+ * (`team-status.ts::isLiveTeamCard`), so this roster is what the user watches
22
+ * during a dispatch and the card becomes the history afterwards. Native mode
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.
35
+ *
36
+ * EVERY PHASE -> GLYPH / COLOUR / COUNT DECISION COMES FROM `team-status.ts`.
37
+ * The local copies this file used to carry are gone on purpose: the transcript
38
+ * card kept a second copy and the two disagreed about what "running" looks like.
18
39
  */
19
40
  import React from 'react';
20
41
  import type { Theme } from './theme.js';
21
42
  import type { TermCapabilities } from './capabilities.js';
22
43
  import { type TeamPanelLayout } from './layout/team-panel.js';
23
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;
24
59
  export interface TeamPanelProps {
25
60
  snapshot: TeamSnapshot;
26
61
  /** Terminal rows, for the one-line collapse below `panelCollapseRows`. */
@@ -38,45 +73,4 @@ export interface TeamPanelProps {
38
73
  now?: number;
39
74
  layout?: TeamPanelLayout;
40
75
  }
41
- export declare function activityBudget(cols: number): number;
42
- /**
43
- * The middle column: what this child is doing right now, in a few words.
44
- *
45
- * Two width policies, answering different questions. `wide` decides WHICH FACT
46
- * to show - a whole path or its basename, a whole command line or its program
47
- * name - mirroring `StatusBar`'s `statusCompactCols` degradation rather than
48
- * inventing a second threshold. `activityBudget(cols)` decides HOW MUCH OF IT
49
- * FITS, and it is a continuous function of width rather than a threshold,
50
- * because the failure it prevents - a row wider than the terminal - is
51
- * continuous.
52
- *
53
- * Exported for the same reason `buildSubagentTools` is: the width bound is the
54
- * property this round has to keep, and asserting it through a rendered frame
55
- * cannot distinguish "the arithmetic clamped it" from "Ink truncated it" - which
56
- * is precisely the confusion that let v1's fixed-width column look fine at 100
57
- * columns and break at 80.
58
- */
59
- export declare function activityLine(run: SubagentRun, cols: number): string;
60
- /**
61
- * The row's supervisor badge (subagent-overseer-v2 AC-5): what the supervisor
62
- * last did to THIS child and how long ago, e.g. `nudged 2m03s`.
63
- *
64
- * `lastIntervention.at` is ABSOLUTE epoch ms (R-P2-3), the same units as
65
- * `startedAt`, so this is one subtraction - no second clock convention.
66
- */
67
- export declare function overseerBadge(run: SubagentRun, at: number): string;
68
- /**
69
- * The activity column WITH the badge composed in (AC-5 / risk table): the
70
- * badge rides the SAME `activityBudget(cols)` allocation the prose uses, so
71
- * a row can never grow past its terminal; on a narrow terminal the badge
72
- * degrades to the action word alone - the who-acted fact outranks the
73
- * what-they-are-doing tail exactly when there is room for only one.
74
- */
75
- export declare function activityWithBadge(run: SubagentRun, cols: number, at: number): string;
76
- /**
77
- * The panel's one supervisor status line (AC-5): the most recent intervention
78
- * across the roster, human-phrased. Empty when the supervisor has not acted
79
- * yet - furniture that says nothing is worse than no line.
80
- */
81
- export declare function supervisorStatusLine(runs: SubagentRun[], at: number): string;
82
76
  export declare function TeamPanel({ snapshot, rows, cols, reducedMotion, theme, caps, now, layout, }: TeamPanelProps): React.ReactElement | null;