@multiclawcli/dashboard 0.12.0 → 0.13.0
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/README.md +5 -4
- package/dist/tui/commands.d.ts +14 -6
- package/dist/tui/components/interaction.d.ts +7 -10
- package/dist/tui/components/panels.d.ts +3 -3
- package/dist/tui/interaction.d.ts +2 -13
- package/dist/tui/layout.d.ts +0 -2
- package/dist/tui/state.d.ts +12 -20
- package/dist/tui.js +592 -485
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -30,12 +30,13 @@ rules are therefore testable without a terminal.
|
|
|
30
30
|
| Type | Holds |
|
|
31
31
|
|------|-------|
|
|
32
32
|
| `TurnView` | Everything that describes *a turn*: agents, stages, focus, scroll, phase, reply |
|
|
33
|
-
| `TuiState extends TurnView` | The above, for the live turn, plus `name
|
|
33
|
+
| `TuiState extends TurnView` | The above, for the live turn, plus `name` and `history` |
|
|
34
34
|
|
|
35
35
|
A finished turn is just a `TurnView` kept in `history`, and every pane takes a `TurnView` — so a past
|
|
36
|
-
turn renders through exactly the same components as the live one.
|
|
37
|
-
turn
|
|
38
|
-
|
|
36
|
+
turn renders through exactly the same components as the live one. The stream panel reads *past* the
|
|
37
|
+
single turn: `visibleTimeline` concatenates the focused agent's logs from `history` and the live turn,
|
|
38
|
+
with a divider row per turn, and `stream:scroll` / `stream:search` clamp against that whole timeline.
|
|
39
|
+
History is never edited in place — `stream:clear` only empties the live turn's buffer.
|
|
39
40
|
|
|
40
41
|
## Source layout
|
|
41
42
|
|
package/dist/tui/commands.d.ts
CHANGED
|
@@ -25,12 +25,20 @@ export interface CommandSpec {
|
|
|
25
25
|
completes?: "agent";
|
|
26
26
|
}
|
|
27
27
|
export declare const COMMANDS: readonly CommandSpec[];
|
|
28
|
+
export interface CommandContext {
|
|
29
|
+
history: boolean;
|
|
30
|
+
retry: boolean;
|
|
31
|
+
queue: number;
|
|
32
|
+
paused: boolean;
|
|
33
|
+
awaitingAnswer: boolean;
|
|
34
|
+
checkpoint: boolean;
|
|
35
|
+
}
|
|
28
36
|
export interface ParsedCommand {
|
|
29
37
|
name: string;
|
|
30
38
|
args: string[];
|
|
31
39
|
}
|
|
32
40
|
/** Commands that make sense in the current phase. */
|
|
33
|
-
export declare function availableCommands(running: boolean): CommandSpec[];
|
|
41
|
+
export declare function availableCommands(running: boolean, context?: CommandContext): CommandSpec[];
|
|
34
42
|
/**
|
|
35
43
|
* Whether a command may run in the current phase.
|
|
36
44
|
*
|
|
@@ -39,26 +47,26 @@ export declare function availableCommands(running: boolean): CommandSpec[];
|
|
|
39
47
|
* entirely. An unknown name is not "unavailable" — it is let through so the
|
|
40
48
|
* caller's `default:` branch can report it.
|
|
41
49
|
*/
|
|
42
|
-
export declare function canRun(name: string, running: boolean): boolean;
|
|
50
|
+
export declare function canRun(name: string, running: boolean, context?: CommandContext): boolean;
|
|
43
51
|
/**
|
|
44
52
|
* Why a command may not run in the current phase, or `undefined` when it may.
|
|
45
53
|
*
|
|
46
54
|
* The mirror of {@link canRun}, and the message names the phase the command
|
|
47
55
|
* *wants* rather than just saying it is unavailable: the two directions mean
|
|
48
56
|
* different things to the user. A mid-run `/replay` has to read as "wait for the
|
|
49
|
-
* turn to finish", while an idle `/
|
|
57
|
+
* turn to finish", while an idle `/steer` has to read as "this only applies to
|
|
50
58
|
* a running turn" — one string for both would leave the user without the fact
|
|
51
59
|
* they need to act on. An unknown name has no reason; it is let through so the
|
|
52
60
|
* caller's `default:` branch can report it.
|
|
53
61
|
*/
|
|
54
|
-
export declare function unavailableReason(name: string, running: boolean): string | undefined;
|
|
62
|
+
export declare function unavailableReason(name: string, running: boolean, context?: CommandContext): string | undefined;
|
|
55
63
|
/**
|
|
56
64
|
* Commands matching what has been typed after the slash.
|
|
57
65
|
*
|
|
58
|
-
* Name prefixes outrank description hits: someone typing `/
|
|
66
|
+
* Name prefixes outrank description hits: someone typing `/fo` wants `/focus`,
|
|
59
67
|
* not whichever command happens to mention "agent" in its blurb.
|
|
60
68
|
*/
|
|
61
|
-
export declare function matchCommands(query: string, running: boolean): CommandSpec[];
|
|
69
|
+
export declare function matchCommands(query: string, running: boolean, context?: CommandContext): CommandSpec[];
|
|
62
70
|
/** True once the line has committed to command mode. */
|
|
63
71
|
export declare function isCommandLine(text: string): boolean;
|
|
64
72
|
/**
|
|
@@ -12,28 +12,24 @@ import { type FormField, type FormState } from "../interaction";
|
|
|
12
12
|
* Everything here is one row per fact and clipped by the caller, which is why
|
|
13
13
|
* the row count in `interaction.ts` only has to be an upper bound.
|
|
14
14
|
*/
|
|
15
|
-
export declare function PermissionBar({ request, others, cursor, released, width, }: {
|
|
15
|
+
export declare function PermissionBar({ request, others, cursor, released, width, maxRows, }: {
|
|
16
16
|
request: RuntimePermissionRequest;
|
|
17
17
|
/** Requests behind this one, shown so "2 waiting" is not a bare number. */
|
|
18
18
|
others: readonly RuntimePermissionRequest[];
|
|
19
19
|
/** Highlighted option, or `undefined` when nothing has been picked yet. */
|
|
20
20
|
cursor: number | undefined;
|
|
21
|
-
/** The user asked for the keyboard back;
|
|
21
|
+
/** The user asked for the keyboard back; escape returns to its options. */
|
|
22
22
|
released: boolean;
|
|
23
23
|
width: number;
|
|
24
|
+
maxRows?: number;
|
|
24
25
|
}): import("react").JSX.Element;
|
|
25
26
|
/**
|
|
26
27
|
* The question, as a form.
|
|
27
28
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* that was now occupied by what they were typing. Here every field is a row, the
|
|
31
|
-
* focused row is the one the keystrokes go to, and choices are picked rather
|
|
32
|
-
* than spelled. The prompt below still does the typing: it already owns the
|
|
33
|
-
* caret, the wrapping and the paste handling, so a text field reuses it instead
|
|
34
|
-
* of growing a second editor.
|
|
29
|
+
* Fields are edited in the prompt, while choice fields use the arrow keys.
|
|
30
|
+
* Submit, skip and cancel are explicit actions after the last field.
|
|
35
31
|
*/
|
|
36
|
-
export declare function QuestionForm({ request, others, fields, form, draft, width, }: {
|
|
32
|
+
export declare function QuestionForm({ request, others, fields, form, draft, width, maxRows, }: {
|
|
37
33
|
request: RuntimeQuestionRequest;
|
|
38
34
|
others: readonly RuntimeQuestionRequest[];
|
|
39
35
|
fields: FormField[];
|
|
@@ -41,4 +37,5 @@ export declare function QuestionForm({ request, others, fields, form, draft, wid
|
|
|
41
37
|
/** Text in the prompt, which is the focused text field's editor. */
|
|
42
38
|
draft: string;
|
|
43
39
|
width: number;
|
|
40
|
+
maxRows?: number;
|
|
44
41
|
}): import("react").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LeaderMessage } from "@multiclawcli/core";
|
|
2
|
-
import { type TurnView } from "../state";
|
|
2
|
+
import { type TuiState, type TurnView } from "../state";
|
|
3
3
|
/** The roster: one row per agent, with the focused one called out by a caret. */
|
|
4
4
|
export declare function TeamPanel({ turn, width, now, spinner, }: {
|
|
5
5
|
turn: TurnView;
|
|
@@ -23,8 +23,8 @@ export declare function Divider(): import("react").JSX.Element;
|
|
|
23
23
|
* or parked. Output itself is rendered at normal brightness — the chrome is what
|
|
24
24
|
* should recede, not the agent's work.
|
|
25
25
|
*/
|
|
26
|
-
export declare function StreamPanel({
|
|
27
|
-
|
|
26
|
+
export declare function StreamPanel({ state, height, width, now, spinner, }: {
|
|
27
|
+
state: TuiState;
|
|
28
28
|
height: number;
|
|
29
29
|
width: number;
|
|
30
30
|
now: number;
|
|
@@ -2,19 +2,8 @@
|
|
|
2
2
|
* The two things an agent can stop and ask for, modelled as something the user
|
|
3
3
|
* can *point at* instead of something they have to spell out.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* question and answering it: remember an internal alias, map a number back onto
|
|
8
|
-
* a list rendered somewhere else, and hand-write JSON that happens to match a
|
|
9
|
-
* schema. None of those are the decision. So the rendered question becomes the
|
|
10
|
-
* thing that is operated on: the option you are looking at is the option you
|
|
11
|
-
* commit, and the field you are editing is the field that receives the
|
|
12
|
-
* keystroke.
|
|
13
|
-
*
|
|
14
|
-
* Pure on purpose. The keyboard rules are the part of this easiest to get
|
|
15
|
-
* subtly wrong — a bare key that both selects and types, a required field that
|
|
16
|
-
* silently submits empty — so they live here, where they can be tested without
|
|
17
|
-
* rendering a terminal.
|
|
5
|
+
* The docked controls keep permission decisions and structured answers tied to
|
|
6
|
+
* their requesting agent while leaving the execution stream visible.
|
|
18
7
|
*/
|
|
19
8
|
import type { RuntimePermissionRequest, RuntimeQuestionRequest } from "@multiclawcli/core";
|
|
20
9
|
export type PermissionOptionKind = RuntimePermissionRequest["options"][number]["kind"];
|
package/dist/tui/layout.d.ts
CHANGED
|
@@ -72,8 +72,6 @@ export interface LayoutInput {
|
|
|
72
72
|
promptRows?: number;
|
|
73
73
|
/** Whether a command result is waiting to be shown. */
|
|
74
74
|
notice: boolean;
|
|
75
|
-
/** A past turn is open for reading, which costs a banner row. */
|
|
76
|
-
viewing?: boolean;
|
|
77
75
|
/** Whether there is a leader or any stage to draw. */
|
|
78
76
|
hasRail: boolean;
|
|
79
77
|
/**
|
package/dist/tui/state.d.ts
CHANGED
|
@@ -131,14 +131,6 @@ export interface TuiState extends TurnView {
|
|
|
131
131
|
name: string;
|
|
132
132
|
/** Finished turns, oldest first. The live turn is `state` itself. */
|
|
133
133
|
history: TurnView[];
|
|
134
|
-
/**
|
|
135
|
-
* A finished turn opened for reading.
|
|
136
|
-
*
|
|
137
|
-
* Kept as a detached copy so scrolling and tabbing through it cannot disturb
|
|
138
|
-
* the turn that is actually running. While it is set the panes render it, and
|
|
139
|
-
* `focusId` / `scroll` edits land on it rather than on the live turn.
|
|
140
|
-
*/
|
|
141
|
-
view?: TurnView;
|
|
142
134
|
/**
|
|
143
135
|
* Whether tool detail is shown in the stream.
|
|
144
136
|
*
|
|
@@ -180,6 +172,7 @@ export type TuiAction = {
|
|
|
180
172
|
| {
|
|
181
173
|
type: "stream:search";
|
|
182
174
|
query: string;
|
|
175
|
+
match?: number;
|
|
183
176
|
viewport: number;
|
|
184
177
|
width: number;
|
|
185
178
|
}
|
|
@@ -203,15 +196,6 @@ export type TuiAction = {
|
|
|
203
196
|
agentId: string;
|
|
204
197
|
text: string;
|
|
205
198
|
}
|
|
206
|
-
/** Open a finished turn for reading, by its turn number. */
|
|
207
|
-
| {
|
|
208
|
-
type: "view:open";
|
|
209
|
-
turn: number;
|
|
210
|
-
}
|
|
211
|
-
/** Go back to the live turn. */
|
|
212
|
-
| {
|
|
213
|
-
type: "view:close";
|
|
214
|
-
}
|
|
215
199
|
/** Flip whether tool detail is shown in the stream. */
|
|
216
200
|
| {
|
|
217
201
|
type: "verbose:toggle";
|
|
@@ -272,6 +256,8 @@ export declare function seedAgents(config: MultiClawConfig): Record<string, Agen
|
|
|
272
256
|
*/
|
|
273
257
|
export declare function createInitialState(config: MultiClawConfig, history?: TurnView[]): TuiState;
|
|
274
258
|
export declare function reduceEvent(state: TuiState, event: MultiClawEvent): TuiState;
|
|
259
|
+
/** Matching wrapped rows in the focused agent's entire session. */
|
|
260
|
+
export declare function streamMatches(state: TuiState, query: string, width: number): number[];
|
|
275
261
|
/**
|
|
276
262
|
* The turns worth keeping: those already in `history`, plus the turn on screen
|
|
277
263
|
* once it has ended.
|
|
@@ -343,14 +329,20 @@ export interface LogWindow {
|
|
|
343
329
|
following: boolean;
|
|
344
330
|
}
|
|
345
331
|
/**
|
|
346
|
-
* The slice of the focused agent's
|
|
332
|
+
* The slice of the focused agent's session that fits the pane.
|
|
347
333
|
*
|
|
348
334
|
* Wrapped first, then windowed, and both steps are needed: the pane is a fixed
|
|
349
335
|
* number of *rows*, while the buffer is a string of *lines*, and one line can
|
|
350
336
|
* spend several rows. Slicing lines would hand the pane more rows than it has
|
|
351
337
|
* and push the newest output out of sight.
|
|
352
338
|
*
|
|
339
|
+
* Turn-divider rows are interspersed to give the reader an anchor: they show
|
|
340
|
+
* the turn number and a clipped requirement snippet so a reader scrolled back
|
|
341
|
+
* knows which request they are looking at.
|
|
342
|
+
*
|
|
353
343
|
* Clamping here as well as in the reducer keeps the window correct even when
|
|
354
|
-
* the pane is resized between two scroll actions.
|
|
344
|
+
* the pane is resized between two scroll actions. Scroll semantics: 0 means
|
|
345
|
+
* pinned to the newest output, positive values mean scrolled back, and
|
|
346
|
+
* `following` is true when the window is at the tail.
|
|
355
347
|
*/
|
|
356
|
-
export declare function
|
|
348
|
+
export declare function visibleTimeline(state: TuiState, agentId: string, viewport: number, width: number, scroll: number): LogWindow;
|