@multiclawcli/dashboard 0.12.0 → 0.14.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 +4 -4
- package/dist/tui/interaction.d.ts +2 -13
- package/dist/tui/layout.d.ts +2 -2
- package/dist/tui/session.d.ts +4 -0
- package/dist/tui/state.d.ts +20 -21
- package/dist/tui.js +753 -524
- 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
|
-
import type
|
|
2
|
-
import { type TurnView } from "../state";
|
|
1
|
+
import { type LeaderMessage } from "@multiclawcli/core";
|
|
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,8 @@ export interface LayoutInput {
|
|
|
72
72
|
promptRows?: number;
|
|
73
73
|
/** Whether a command result is waiting to be shown. */
|
|
74
74
|
notice: boolean;
|
|
75
|
-
/** A
|
|
76
|
-
|
|
75
|
+
/** A single row showing route and selection reason. */
|
|
76
|
+
details?: boolean;
|
|
77
77
|
/** Whether there is a leader or any stage to draw. */
|
|
78
78
|
hasRail: boolean;
|
|
79
79
|
/**
|
package/dist/tui/session.d.ts
CHANGED
|
@@ -24,6 +24,10 @@ export declare const SESSION_VERSION = 1;
|
|
|
24
24
|
export declare const MAX_JOURNAL_LOG_CHARS = 1200;
|
|
25
25
|
/** The turn that was in flight when the journal was last written. */
|
|
26
26
|
export interface JournalTurn {
|
|
27
|
+
work?: import("@multiclawcli/core").WorkContext;
|
|
28
|
+
routing?: import("@multiclawcli/core").RoutingInfo;
|
|
29
|
+
sessionEvents?: import("@multiclawcli/core").SessionEvent[];
|
|
30
|
+
agentResults?: import("@multiclawcli/core").AgentResult[];
|
|
27
31
|
turn: number;
|
|
28
32
|
requirement: string;
|
|
29
33
|
startedAt?: number;
|
package/dist/tui/state.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export interface AgentView {
|
|
|
33
33
|
max: number;
|
|
34
34
|
};
|
|
35
35
|
error?: string;
|
|
36
|
+
handoffReason?: string;
|
|
36
37
|
/** Context-window usage the runtime reported, when it reported any. */
|
|
37
38
|
usage?: AgentOutput["usage"];
|
|
38
39
|
/** USD cost the runtime reported, when the provider priced itself. */
|
|
@@ -42,7 +43,7 @@ export interface AgentView {
|
|
|
42
43
|
* `/retry` can ask the agent to continue where it left off instead of
|
|
43
44
|
* re-exploring from scratch.
|
|
44
45
|
*/
|
|
45
|
-
|
|
46
|
+
session?: import("@multiclawcli/core").SessionRecord;
|
|
46
47
|
logs: LogSegment[];
|
|
47
48
|
}
|
|
48
49
|
export type TuiPhase = "idle" | "running" | "complete";
|
|
@@ -60,6 +61,11 @@ export interface TuiStageInfo extends StageInfo {
|
|
|
60
61
|
* a `TurnView` so they can render one without knowing which it is.
|
|
61
62
|
*/
|
|
62
63
|
export interface TurnView {
|
|
64
|
+
work?: import("@multiclawcli/core").WorkContext;
|
|
65
|
+
routing?: import("@multiclawcli/core").RoutingInfo;
|
|
66
|
+
sessionEvents?: import("@multiclawcli/core").SessionEvent[];
|
|
67
|
+
agentResults?: import("@multiclawcli/core").AgentResult[];
|
|
68
|
+
effectiveAgentResults?: import("@multiclawcli/core").AgentResult[];
|
|
63
69
|
turn: number;
|
|
64
70
|
requirement?: string;
|
|
65
71
|
effectiveRequirement?: string;
|
|
@@ -131,14 +137,6 @@ export interface TuiState extends TurnView {
|
|
|
131
137
|
name: string;
|
|
132
138
|
/** Finished turns, oldest first. The live turn is `state` itself. */
|
|
133
139
|
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
140
|
/**
|
|
143
141
|
* Whether tool detail is shown in the stream.
|
|
144
142
|
*
|
|
@@ -180,6 +178,7 @@ export type TuiAction = {
|
|
|
180
178
|
| {
|
|
181
179
|
type: "stream:search";
|
|
182
180
|
query: string;
|
|
181
|
+
match?: number;
|
|
183
182
|
viewport: number;
|
|
184
183
|
width: number;
|
|
185
184
|
}
|
|
@@ -203,15 +202,6 @@ export type TuiAction = {
|
|
|
203
202
|
agentId: string;
|
|
204
203
|
text: string;
|
|
205
204
|
}
|
|
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
205
|
/** Flip whether tool detail is shown in the stream. */
|
|
216
206
|
| {
|
|
217
207
|
type: "verbose:toggle";
|
|
@@ -272,6 +262,8 @@ export declare function seedAgents(config: MultiClawConfig): Record<string, Agen
|
|
|
272
262
|
*/
|
|
273
263
|
export declare function createInitialState(config: MultiClawConfig, history?: TurnView[]): TuiState;
|
|
274
264
|
export declare function reduceEvent(state: TuiState, event: MultiClawEvent): TuiState;
|
|
265
|
+
/** Matching wrapped rows in the focused agent's entire session. */
|
|
266
|
+
export declare function streamMatches(state: TuiState, query: string, width: number): number[];
|
|
275
267
|
/**
|
|
276
268
|
* The turns worth keeping: those already in `history`, plus the turn on screen
|
|
277
269
|
* once it has ended.
|
|
@@ -310,6 +302,7 @@ export declare function recordedTurns(state: TuiState): TurnView[];
|
|
|
310
302
|
* restart.
|
|
311
303
|
*/
|
|
312
304
|
export declare function conversationTurns(state: TuiState): TurnRecord[];
|
|
305
|
+
export declare function latestWorkFromTurns(turns: readonly TurnView[]): import("@multiclawcli/core").WorkContext | undefined;
|
|
313
306
|
export declare function tuiReducer(state: TuiState, action: TuiAction): TuiState;
|
|
314
307
|
/**
|
|
315
308
|
* Roll a stage's member statuses up into one status for the pipeline bar.
|
|
@@ -343,14 +336,20 @@ export interface LogWindow {
|
|
|
343
336
|
following: boolean;
|
|
344
337
|
}
|
|
345
338
|
/**
|
|
346
|
-
* The slice of the focused agent's
|
|
339
|
+
* The slice of the focused agent's session that fits the pane.
|
|
347
340
|
*
|
|
348
341
|
* Wrapped first, then windowed, and both steps are needed: the pane is a fixed
|
|
349
342
|
* number of *rows*, while the buffer is a string of *lines*, and one line can
|
|
350
343
|
* spend several rows. Slicing lines would hand the pane more rows than it has
|
|
351
344
|
* and push the newest output out of sight.
|
|
352
345
|
*
|
|
346
|
+
* Turn-divider rows are interspersed to give the reader an anchor: they show
|
|
347
|
+
* the turn number and a clipped requirement snippet so a reader scrolled back
|
|
348
|
+
* knows which request they are looking at.
|
|
349
|
+
*
|
|
353
350
|
* Clamping here as well as in the reducer keeps the window correct even when
|
|
354
|
-
* the pane is resized between two scroll actions.
|
|
351
|
+
* the pane is resized between two scroll actions. Scroll semantics: 0 means
|
|
352
|
+
* pinned to the newest output, positive values mean scrolled back, and
|
|
353
|
+
* `following` is true when the window is at the tail.
|
|
355
354
|
*/
|
|
356
|
-
export declare function
|
|
355
|
+
export declare function visibleTimeline(state: TuiState, agentId: string, viewport: number, width: number, scroll: number): LogWindow;
|