@linxiraos/pi-tui 1.3.2 → 1.4.1
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 +181 -181
- package/dist/types/apps/session-picker.d.ts +13 -1
- package/dist/types/autocomplete.d.ts +6 -0
- package/dist/types/components/sparkline.d.ts +34 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/native/describe.d.ts +7 -1
- package/dist/types/native/overlay.d.ts +2 -0
- package/dist/types/native/tone.d.ts +2 -0
- package/dist/types/overlays/agent-hub.d.ts +3 -3
- package/dist/types/overlays/approval-dialog.d.ts +15 -1
- package/dist/types/overlays/jobs-panel.d.ts +3 -0
- package/dist/types/overlays/model-browser.d.ts +1 -1
- package/dist/types/overlays/resume-picker.d.ts +293 -0
- package/dist/types/overlays/resume-tree.d.ts +353 -0
- package/dist/types/overlays/session-selector.d.ts +22 -4
- package/dist/types/render/output-block.d.ts +2 -0
- package/dist/types/render/tool-card.d.ts +2 -0
- package/dist/types/sixel.d.ts +28 -0
- package/dist/types/status-line/host.d.ts +3 -0
- package/dist/types/terminal-capabilities.d.ts +6 -1
- package/dist/types/tools/bash.d.ts +2 -0
- package/dist/types/tools/default-renderer.d.ts +3 -0
- package/dist/types/tools/eval.d.ts +9 -8
- package/dist/types/tools/hub.d.ts +3 -1
- package/dist/types/tools/irc.d.ts +11 -1
- package/dist/types/tools/job-widget.d.ts +226 -0
- package/dist/types/tools/json-tree.d.ts +2 -0
- package/dist/types/tools/line-ranges.d.ts +8 -1
- package/dist/types/tools/mcp.d.ts +23 -0
- package/dist/types/tools/output-meta.d.ts +1 -1
- package/dist/types/tools/read.d.ts +8 -2
- package/dist/types/tools/renderer.d.ts +8 -2
- package/dist/types/tools/result-card.d.ts +1 -1
- package/dist/types/tools/streaming-output.d.ts +1 -0
- package/dist/types/tools/task.d.ts +12 -0
- package/dist/types/tools/wait.d.ts +11 -0
- package/package.json +9 -9
- package/src/apps/session-picker.ts +20 -2
- package/src/autocomplete.d.ts +6 -0
- package/src/autocomplete.ts +21 -6
- package/src/chat/skill-message.ts +16 -17
- package/src/chat/tool-execution.ts +35 -0
- package/src/chrome/transcript-container.ts +85 -13
- package/src/components/editor.ts +17 -7
- package/src/components/image.ts +5 -4
- package/src/components/markdown.ts +11 -3
- package/src/components/sparkline.d.ts +34 -0
- package/src/components/sparkline.ts +89 -0
- package/src/index.d.ts +1 -0
- package/src/index.ts +1 -0
- package/src/native/backend.ts +32 -6
- package/src/native/describe.d.ts +7 -1
- package/src/native/describe.ts +18 -1
- package/src/native/overlay.d.ts +2 -0
- package/src/native/overlay.ts +3 -0
- package/src/native/tone.d.ts +2 -0
- package/src/native/tone.ts +22 -0
- package/src/overlays/agent-hub.ts +14 -10
- package/src/overlays/agent-transcript-viewer.ts +2 -2
- package/src/overlays/agents-hub.ts +2 -1
- package/src/overlays/approval-dialog.ts +59 -13
- package/src/overlays/hub-frame.ts +2 -1
- package/src/overlays/jobs-panel.ts +24 -2
- package/src/overlays/model-browser.ts +3 -0
- package/src/overlays/model-hub.ts +2 -1
- package/src/overlays/resume-picker.ts +706 -0
- package/src/overlays/resume-tree.ts +1901 -0
- package/src/overlays/session-selector.ts +579 -39
- package/src/prompt/attachment-chips.ts +1 -1
- package/src/render/output-block.d.ts +2 -0
- package/src/render/output-block.ts +25 -12
- package/src/render/tool-card.d.ts +2 -0
- package/src/render/tool-card.ts +6 -0
- package/src/sixel.d.ts +28 -0
- package/src/sixel.ts +185 -0
- package/src/status-line/component.ts +19 -2
- package/src/status-line/host.ts +3 -0
- package/src/terminal-capabilities.d.ts +6 -1
- package/src/terminal-capabilities.ts +54 -3
- package/src/theme/symbols.ts +5 -3
- package/src/tools/bash.d.ts +2 -0
- package/src/tools/bash.ts +25 -9
- package/src/tools/default-renderer.d.ts +3 -0
- package/src/tools/default-renderer.ts +54 -17
- package/src/tools/eval.d.ts +9 -8
- package/src/tools/eval.ts +32 -14
- package/src/tools/find.ts +4 -5
- package/src/tools/glob.ts +4 -6
- package/src/tools/grep.ts +2 -4
- package/src/tools/hub.ts +4 -1
- package/src/tools/irc.d.ts +11 -1
- package/src/tools/irc.ts +11 -1
- package/src/tools/job-widget.d.ts +226 -0
- package/src/tools/job-widget.ts +1036 -0
- package/src/tools/json-tree.d.ts +2 -0
- package/src/tools/json-tree.ts +8 -0
- package/src/tools/line-ranges.d.ts +8 -1
- package/src/tools/line-ranges.ts +23 -13
- package/src/tools/mcp.d.ts +23 -0
- package/src/tools/mcp.ts +44 -1
- package/src/tools/output-meta.d.ts +1 -1
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/proc-render.ts +1 -0
- package/src/tools/read.d.ts +8 -2
- package/src/tools/read.ts +64 -21
- package/src/tools/renderer.d.ts +8 -2
- package/src/tools/renderer.ts +8 -2
- package/src/tools/result-card.d.ts +1 -1
- package/src/tools/result-card.ts +1 -2
- package/src/tools/streaming-output.d.ts +1 -0
- package/src/tools/streaming-output.ts +11 -9
- package/src/tools/task.d.ts +12 -0
- package/src/tools/task.ts +25 -1
- package/src/tools/wait.d.ts +11 -0
- package/src/tools/wait.ts +296 -147
- package/src/tui.ts +45 -5
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live widgets for background jobs: the rendered state of what a job is
|
|
3
|
+
* doing (a benchmark's requests/s over time, tests passed per model, CI jobs
|
|
4
|
+
* green so far), shown in the `wait` card, the HUD's jobs pill and the jobs
|
|
5
|
+
* sheet.
|
|
6
|
+
*
|
|
7
|
+
* A widget is data, not code. In coding-agent, a cheap model writes a
|
|
8
|
+
* `JobWidgetSpec` once per job (rules that pull numbers out of the job's
|
|
9
|
+
* output streams, and the blocks to draw them with); the job-widgets runtime
|
|
10
|
+
* then applies those rules to every new line and recomputes a
|
|
11
|
+
* {@link JobWidgetView}. Renderers here draw that view as TSP nodes (Tern) or
|
|
12
|
+
* ANSI lines, and never see a rule or a regex.
|
|
13
|
+
*/
|
|
14
|
+
import type { TspPropNode, TspSpan, TspTone } from "@linxiraos/pi-wire";
|
|
15
|
+
import type { NativeNode } from "../native/node.js";
|
|
16
|
+
import type { Theme } from "../theme/theme.js";
|
|
17
|
+
/** Where a widget's view stands. */
|
|
18
|
+
export type JobWidgetState =
|
|
19
|
+
/** The cheap model is writing the spec; the view holds the model-free fallback meanwhile. */
|
|
20
|
+
"authoring"
|
|
21
|
+
/** The spec's rules drive the view. */
|
|
22
|
+
| "live"
|
|
23
|
+
/** No spec (no model, authoring failed, nothing to read yet): last line and rates only. */
|
|
24
|
+
| "fallback";
|
|
25
|
+
/** A formatted number with the direction it moved, for headlines and stats. */
|
|
26
|
+
export interface JobWidgetTrend {
|
|
27
|
+
/** `▲ 15%`, `▼ 3.2 ms`. */
|
|
28
|
+
text: string;
|
|
29
|
+
/** `success` when the move is toward `better`, `error` away from it, `muted` without a direction. */
|
|
30
|
+
tone: "success" | "error" | "muted";
|
|
31
|
+
}
|
|
32
|
+
/** The headline value: the one number a glance should take away (`186 tok/s`). */
|
|
33
|
+
export interface JobWidgetHeadline {
|
|
34
|
+
/** Formatted value with its unit. */
|
|
35
|
+
text: string;
|
|
36
|
+
/** What the value is, when the title does not already say it (`decode`, `p50`). */
|
|
37
|
+
label?: string;
|
|
38
|
+
trend?: JobWidgetTrend;
|
|
39
|
+
}
|
|
40
|
+
/** A time series drawn as columns, newest right, with optional reference lines. */
|
|
41
|
+
export interface JobWidgetSparkBlock {
|
|
42
|
+
kind: "spark";
|
|
43
|
+
/** What the series measures (`tok/s · nvfp4 b1`). */
|
|
44
|
+
label: string;
|
|
45
|
+
/** At most 48 values, oldest first, already downsampled. */
|
|
46
|
+
values: number[];
|
|
47
|
+
/** Domain of the chart; covers the values and every mark. */
|
|
48
|
+
min: number;
|
|
49
|
+
max: number;
|
|
50
|
+
/** Horizontal reference lines in value units (a target, a baseline). */
|
|
51
|
+
marks?: {
|
|
52
|
+
value: number;
|
|
53
|
+
label: string;
|
|
54
|
+
tone?: TspTone;
|
|
55
|
+
}[];
|
|
56
|
+
/** Latest value, formatted (`186 tok/s`). */
|
|
57
|
+
last: string;
|
|
58
|
+
/** Per-point tooltip text, same length as `values` (`12:04:31 · 186 tok/s`). */
|
|
59
|
+
tips?: string[];
|
|
60
|
+
tone?: TspTone;
|
|
61
|
+
}
|
|
62
|
+
/** One bar per key (a model, a shard): a value against a common maximum. */
|
|
63
|
+
export interface JobWidgetBarsBlock {
|
|
64
|
+
kind: "bars";
|
|
65
|
+
label: string;
|
|
66
|
+
items: {
|
|
67
|
+
label: string;
|
|
68
|
+
value: number;
|
|
69
|
+
text: string;
|
|
70
|
+
tone?: TspTone;
|
|
71
|
+
}[];
|
|
72
|
+
/** The value a full bar stands for. */
|
|
73
|
+
max: number;
|
|
74
|
+
}
|
|
75
|
+
/** Progress of a countable whole (`7/9 trials`). */
|
|
76
|
+
export interface JobWidgetMeterBlock {
|
|
77
|
+
kind: "meter";
|
|
78
|
+
label: string;
|
|
79
|
+
/** 0–1. */
|
|
80
|
+
value: number;
|
|
81
|
+
/** `7/9 trials`. */
|
|
82
|
+
text: string;
|
|
83
|
+
tone?: TspTone;
|
|
84
|
+
}
|
|
85
|
+
/** A few labelled numbers on one line (`p50 41 ms · p99 212 ms · errors 0`). */
|
|
86
|
+
export interface JobWidgetStatBlock {
|
|
87
|
+
kind: "stat";
|
|
88
|
+
items: {
|
|
89
|
+
label: string;
|
|
90
|
+
text: string;
|
|
91
|
+
tone?: TspTone;
|
|
92
|
+
trend?: JobWidgetTrend;
|
|
93
|
+
}[];
|
|
94
|
+
}
|
|
95
|
+
/** One cell per key with its latest state (CI jobs, shards, workers). */
|
|
96
|
+
export interface JobWidgetGridBlock {
|
|
97
|
+
kind: "grid";
|
|
98
|
+
label: string;
|
|
99
|
+
items: {
|
|
100
|
+
label: string;
|
|
101
|
+
text: string;
|
|
102
|
+
tone: TspTone;
|
|
103
|
+
}[];
|
|
104
|
+
}
|
|
105
|
+
/** The latest line a rule captured (the test now running, the current phase). */
|
|
106
|
+
export interface JobWidgetTextBlock {
|
|
107
|
+
kind: "text";
|
|
108
|
+
text: string;
|
|
109
|
+
}
|
|
110
|
+
export type JobWidgetBlock = JobWidgetSparkBlock | JobWidgetBarsBlock | JobWidgetMeterBlock | JobWidgetStatBlock | JobWidgetGridBlock | JobWidgetTextBlock;
|
|
111
|
+
/**
|
|
112
|
+
* Everything a renderer needs to draw one job's widget. Plain JSON: it rides
|
|
113
|
+
* on `JobSnapshot.widget` through `wait` progress updates and is persisted
|
|
114
|
+
* with the final `wait` result, so a sealed card redraws the last state after
|
|
115
|
+
* a session reload.
|
|
116
|
+
*/
|
|
117
|
+
export interface JobWidgetView {
|
|
118
|
+
/** Short name of what is being watched (`verb-bench · 3 models`). */
|
|
119
|
+
title: string;
|
|
120
|
+
headline?: JobWidgetHeadline;
|
|
121
|
+
/** What the job is doing now: a subagent's stage, or the last line of output. */
|
|
122
|
+
stage?: string;
|
|
123
|
+
/** Top to bottom; the first block is the primary one, and compact views show only it. */
|
|
124
|
+
blocks: JobWidgetBlock[];
|
|
125
|
+
state: JobWidgetState;
|
|
126
|
+
/** Epoch ms of the newest data the view reflects. */
|
|
127
|
+
updatedAt: number;
|
|
128
|
+
}
|
|
129
|
+
/** A running job's widget, as the HUD and the status line list them. */
|
|
130
|
+
export interface JobWidgetEntry {
|
|
131
|
+
id: string;
|
|
132
|
+
type: JobTileType;
|
|
133
|
+
view: JobWidgetView;
|
|
134
|
+
}
|
|
135
|
+
/** What a glance at a widget reads: its headline, else the primary figure of its blocks. */
|
|
136
|
+
export interface JobWidgetGlance {
|
|
137
|
+
text: string;
|
|
138
|
+
label?: string;
|
|
139
|
+
trend?: JobWidgetTrend;
|
|
140
|
+
}
|
|
141
|
+
/** What one glance at the widget reads: the headline, else the latest figure of its first spark or meter. */
|
|
142
|
+
export declare function jobWidgetGlance(view: JobWidgetView): JobWidgetGlance | undefined;
|
|
143
|
+
/** The widget's first spark cut to its newest `points` values (the HUD's mini chart); undefined without data. */
|
|
144
|
+
export declare function jobWidgetSpark(view: JobWidgetView, points?: number): JobWidgetSparkBlock | undefined;
|
|
145
|
+
/** The entry whose live widget (one a spec drives) saw data last. */
|
|
146
|
+
export declare function latestJobWidget<T extends JobWidgetEntry>(entries: readonly T[]): T | undefined;
|
|
147
|
+
/** The widget as one plain line for tooltips: `verb-bench · 3 models — decode 186 tok/s ▲ 15%`. */
|
|
148
|
+
export declare function jobWidgetSummary(view: JobWidgetView): string;
|
|
149
|
+
/** Spans of a glance: muted label, strong number, toned trend. */
|
|
150
|
+
export declare function jobWidgetGlanceSpans(glance: JobWidgetGlance): TspSpan[];
|
|
151
|
+
/**
|
|
152
|
+
* A glance for the ANSI status line: the figure with its trend, then a
|
|
153
|
+
* `cells`-wide sparkline of the primary spark when there is one.
|
|
154
|
+
*/
|
|
155
|
+
export declare function formatJobWidgetGlance(view: JobWidgetView, theme: Theme, cells?: number): string;
|
|
156
|
+
/**
|
|
157
|
+
* A job's whole widget as a TSP subtree (role `zeta.job.widget`), as the jobs
|
|
158
|
+
* sheet details it: a head row (title, stage, headline) over every block.
|
|
159
|
+
*/
|
|
160
|
+
export declare function describeJobWidget(view: JobWidgetView): NativeNode;
|
|
161
|
+
/**
|
|
162
|
+
* The HUD jobs pill's hover card: up to four running jobs as tiles, then a
|
|
163
|
+
* count of the rest; undefined without widgets. Its tiles carry no clock:
|
|
164
|
+
* Tern draws the card only once hovered, and a clock drawn then would start
|
|
165
|
+
* from the last time the program wrote.
|
|
166
|
+
*/
|
|
167
|
+
export declare function describeJobWidgetsHovercard(entries: readonly JobWidgetEntry[]): TspPropNode | undefined;
|
|
168
|
+
/** The HUD's mini chart: the newest values of the widget's first spark; undefined without one. */
|
|
169
|
+
export declare function describeJobWidgetMiniSpark(view: JobWidgetView): NativeNode | undefined;
|
|
170
|
+
/** Where a tiled job stands. */
|
|
171
|
+
export type JobTileStatus = "running" | "completed" | "failed" | "cancelled";
|
|
172
|
+
/** What kind of job a tile shows: a subagent (`task`) goes by its name, any other job by what it runs. */
|
|
173
|
+
export type JobTileType = "bash" | "task" | "eval";
|
|
174
|
+
/** One job as a tile draws it: who it is, how long it has run, and its widget when it has one. */
|
|
175
|
+
export interface JobTile {
|
|
176
|
+
id: string;
|
|
177
|
+
type: JobTileType;
|
|
178
|
+
status: JobTileStatus;
|
|
179
|
+
/** The job's label (a command): the title of a job that is no subagent, before a widget names it. */
|
|
180
|
+
label: string;
|
|
181
|
+
/** How long the job has run; a tile without it shows no clock. */
|
|
182
|
+
durationMs?: number;
|
|
183
|
+
/** Whether the job is live (its dot pulses, its clock runs); settled and sealed tiles freeze. */
|
|
184
|
+
live: boolean;
|
|
185
|
+
/** The job's widget; only a live one (a spec drives it) draws figures and charts, any lends its stage. */
|
|
186
|
+
widget?: JobWidgetView;
|
|
187
|
+
/** A task's own completion estimate (0–100), drawn as a ring. */
|
|
188
|
+
completionPercent?: number;
|
|
189
|
+
/** What the job is doing now, when its widget names nothing: a task's stage in its own words. */
|
|
190
|
+
stage?: string;
|
|
191
|
+
exitCode?: number;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Jobs as one frame of equal tiles (role `zeta.job.tiles`) that wraps onto
|
|
195
|
+
* more rows when the frame runs out of width.
|
|
196
|
+
*/
|
|
197
|
+
export declare function describeJobTiles(tiles: readonly JobTile[]): NativeNode;
|
|
198
|
+
/**
|
|
199
|
+
* One job as a tile (role `zeta.job.tile`): a head line (status, name, id,
|
|
200
|
+
* a task's own completion estimate as a ring, clock), then what a live
|
|
201
|
+
* widget shows (the figure a glance takes away, its primary block, quiet
|
|
202
|
+
* lines of stats) and the stage. With nothing to chart, the stage is the
|
|
203
|
+
* tile's body.
|
|
204
|
+
*/
|
|
205
|
+
export declare function describeJobTile(tile: JobTile): NativeNode;
|
|
206
|
+
/**
|
|
207
|
+
* A job's whole widget as terminal lines, each within `width` (capped at 80
|
|
208
|
+
* so bars stay next to their labels): the head line (title, stage, headline
|
|
209
|
+
* at the right), then every block under a shared label column.
|
|
210
|
+
*/
|
|
211
|
+
export declare function renderJobWidgetLines(view: JobWidgetView, width: number, theme: Theme): string[];
|
|
212
|
+
/** How the ANSI wait card draws a tile. */
|
|
213
|
+
export interface JobTileLineOptions {
|
|
214
|
+
/** Opens the title line: the caller's status icon and type badge. */
|
|
215
|
+
lead: string;
|
|
216
|
+
/** Closes the title line at the right, after the figure: the duration. */
|
|
217
|
+
meta: string;
|
|
218
|
+
/** Every block rather than the primary one alone (an expanded card). */
|
|
219
|
+
full?: boolean;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* A tile as terminal lines within `width`: the lead, name and id with the
|
|
223
|
+
* figure and meta at the right, then a live widget's primary block (every
|
|
224
|
+
* block when `full`) and dim lines of stats and stage, capped at 80 cells.
|
|
225
|
+
*/
|
|
226
|
+
export declare function renderJobTileLines(tile: JobTile, width: number, theme: Theme, options: JobTileLineOptions): string[];
|
|
@@ -70,6 +70,8 @@ export interface JsonTreeDescribeOptions {
|
|
|
70
70
|
openDepth?: number;
|
|
71
71
|
/** Root object keys omitted. Defaults to internal tool argument metadata. */
|
|
72
72
|
hiddenRootKeys?: readonly string[];
|
|
73
|
+
/** Called after the tree is built if a depth, node or scalar cap left part of the value out. */
|
|
74
|
+
onTruncated?: () => void;
|
|
73
75
|
}
|
|
74
76
|
/**
|
|
75
77
|
* TSP `tree` of a JSON value: containers become expandable nodes labelled with
|
|
@@ -10,12 +10,19 @@ export interface LineRange {
|
|
|
10
10
|
export declare const LINE_RANGE_CHUNK_SOURCE: string;
|
|
11
11
|
/** Parse a single `N`, `N-M`, `N-`, `N+K`, or `..`-aliased (`N..M`, `N..`) chunk. Throws via {@link ToolError} on invalid bounds. */
|
|
12
12
|
export declare function parseLineRangeChunk(sel: string): LineRange | null;
|
|
13
|
+
/** What a bare `N` selects in a one-chunk list: lines `N` onward (`:50`), or the single line `N`. */
|
|
14
|
+
export type BareLineMode = "onward" | "single";
|
|
13
15
|
/**
|
|
14
16
|
* Parse a comma-separated list of line ranges (e.g. `5-16,960-973` or `19,59`). Returns
|
|
15
17
|
* the ranges in ascending order with overlapping/adjacent ranges merged so
|
|
16
18
|
* downstream consumers can stream the file in a single forward pass per range.
|
|
17
19
|
*/
|
|
18
|
-
export declare function parseLineRanges(sel: string): [LineRange, ...LineRange[]] | null;
|
|
20
|
+
export declare function parseLineRanges(sel: string, bare?: BareLineMode): [LineRange, ...LineRange[]] | null;
|
|
21
|
+
/**
|
|
22
|
+
* Sort non-empty `ranges` by start line and merge the ones that overlap or touch;
|
|
23
|
+
* an open-ended range (`endLine` undefined) reaches EOF and absorbs every later one.
|
|
24
|
+
*/
|
|
25
|
+
export declare function mergeLineRanges(ranges: readonly LineRange[]): [LineRange, ...LineRange[]];
|
|
19
26
|
/**
|
|
20
27
|
* Extract the line-range component from a read-tool selector that may also
|
|
21
28
|
* carry a verbatim/index display mode (`raw`, `conflicts`) — alone or compounded
|
|
@@ -63,6 +63,27 @@ export interface MCPResourceContent {
|
|
|
63
63
|
}
|
|
64
64
|
/** Supported MCP result content blocks retained in display metadata. */
|
|
65
65
|
export type MCPContent = MCPTextContent | MCPImageContent | MCPResourceContent;
|
|
66
|
+
/** Server-reported progress of an in-flight MCP request. */
|
|
67
|
+
export interface MCPProgress {
|
|
68
|
+
progress: number;
|
|
69
|
+
total?: number;
|
|
70
|
+
message?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Read MCP progress fields from an untrusted record: `progress` must be a
|
|
74
|
+
* finite number; `total` is kept only when finite and `message` only when a
|
|
75
|
+
* string. Shared by the transports, the Eval bridge, and the status renderer.
|
|
76
|
+
*/
|
|
77
|
+
export declare function parseMCPProgress(value: unknown): MCPProgress | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* One-line display text for MCP progress, at most `maxWidth` cells: the
|
|
80
|
+
* message, with `progress/total` when the server reports a total. The bare
|
|
81
|
+
* progress value appears only when there is no message: without a total it is
|
|
82
|
+
* an opaque counter, not a percentage. Home paths in the message show as `~`,
|
|
83
|
+
* and a long message is cut before the count, so the count stays visible.
|
|
84
|
+
* Every progress display (tool card and both Eval status renderers) uses it.
|
|
85
|
+
*/
|
|
86
|
+
export declare function formatMCPProgress(progress: MCPProgress, maxWidth?: number): string;
|
|
66
87
|
/** MCP result details shared by renderers and programmatic tool consumers. */
|
|
67
88
|
export interface MCPToolDetails {
|
|
68
89
|
/** Server name */
|
|
@@ -81,6 +102,8 @@ export interface MCPToolDetails {
|
|
|
81
102
|
provider?: string;
|
|
82
103
|
/** Provider display name (e.g., "Claude Code", "MCP Config") */
|
|
83
104
|
providerName?: string;
|
|
105
|
+
/** Latest progress of a running call; present only on partial updates. */
|
|
106
|
+
progress?: MCPProgress;
|
|
84
107
|
/** Structured output metadata (set by the spill wrapper when output is truncated to an artifact). */
|
|
85
108
|
meta?: OutputMeta;
|
|
86
109
|
}
|
|
@@ -110,7 +110,7 @@ export interface OutputMeta {
|
|
|
110
110
|
/** Capture failure of this output itself; aggregate reports keep source failures on their entries. */
|
|
111
111
|
artifactError?: OutputArtifactError;
|
|
112
112
|
source?: SourceMeta;
|
|
113
|
-
/** The output is a bounded page of session artifact
|
|
113
|
+
/** The output is a bounded page of a file or session artifact its `source` re-reads with line selectors; spill-to-artifact skips it. */
|
|
114
114
|
pagedSource?: true;
|
|
115
115
|
diagnostics?: DiagnosticMeta;
|
|
116
116
|
limits?: LimitsMeta;
|
|
@@ -71,8 +71,14 @@ export interface ReadToolDetails {
|
|
|
71
71
|
*/
|
|
72
72
|
displayReadTargetLinks?: Array<string | null>;
|
|
73
73
|
}
|
|
74
|
-
/**
|
|
75
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Split a filesystem path from its trailing read selector. `foldListColons: false`
|
|
76
|
+
* keeps a colon repeated inside a list as part of the path (`a:1-30,:-30` peels
|
|
77
|
+
* only `:-30`), for callers checking whether that shorter split names a real file.
|
|
78
|
+
*/
|
|
79
|
+
export declare function splitPathAndSel(rawPath: string, options?: {
|
|
80
|
+
foldListColons?: boolean;
|
|
81
|
+
}): {
|
|
76
82
|
path: string;
|
|
77
83
|
sel?: string;
|
|
78
84
|
};
|
|
@@ -27,8 +27,9 @@ export interface RenderResultOptions {
|
|
|
27
27
|
executionStarted?: boolean;
|
|
28
28
|
/**
|
|
29
29
|
* Milliseconds since this call began executing (`tool_execution_start`),
|
|
30
|
-
* or its total once settled; undefined before it starts.
|
|
31
|
-
*
|
|
30
|
+
* or its total once settled; undefined before it starts. The terminal
|
|
31
|
+
* transcript's options object computes it on read, so a render-time read
|
|
32
|
+
* is live while the block's spinner ticks.
|
|
32
33
|
*/
|
|
33
34
|
elapsedMs?: number;
|
|
34
35
|
/** The call was abandoned without a final result (turn aborted, sealed). Native describe hooks only. */
|
|
@@ -177,6 +178,11 @@ export interface ToolRenderer<TArgs = unknown, TDetails = unknown> {
|
|
|
177
178
|
* `options.spinnerFrame`.
|
|
178
179
|
*/
|
|
179
180
|
animatedPartialResult?: boolean | ((args: TArgs) => boolean);
|
|
181
|
+
/**
|
|
182
|
+
* Repaint the block once a second while its call executes, for renderers
|
|
183
|
+
* that show live `options.elapsedMs` without consuming the spinner frame.
|
|
184
|
+
*/
|
|
185
|
+
tickElapsed?: boolean;
|
|
180
186
|
/**
|
|
181
187
|
* Whether replacing a pending call render with the first result requires a
|
|
182
188
|
* full viewport repaint. Use for merged renderers whose pending rows can be
|
|
@@ -16,5 +16,5 @@ export interface CardToolResult {
|
|
|
16
16
|
export declare function firstText(result: CardToolResult): string;
|
|
17
17
|
/** Sanitize one display line: tabs become spaces, carriage returns are dropped. */
|
|
18
18
|
export declare function safe(value: string): string;
|
|
19
|
-
/**
|
|
19
|
+
/** Card whose lines are sanitized and truncated, re-rendered when the expanded state flips. */
|
|
20
20
|
export declare function card(lines: (width: number, expanded: boolean) => string[], options: RenderResultOptions): Component;
|
|
@@ -261,6 +261,7 @@ export declare class OutputSink {
|
|
|
261
261
|
* Replace the in-memory buffer with the given text. Used when an upstream
|
|
262
262
|
* minimizer rewrites the captured output after the raw bytes have already
|
|
263
263
|
* been streamed.
|
|
264
|
+
* The replacement is held to the per-line column cap like streamed output.
|
|
264
265
|
*
|
|
265
266
|
* After this call the buffer is authoritative: streaming counters realign
|
|
266
267
|
* to the replacement, the retained head window is cleared, and head
|
|
@@ -264,6 +264,18 @@ export interface AgentProgress {
|
|
|
264
264
|
advisor?: boolean;
|
|
265
265
|
/** The agent's latest self-estimate of task completion (0–100), from the periodic `task.completionProbe` side request. */
|
|
266
266
|
completionPercent?: number;
|
|
267
|
+
/**
|
|
268
|
+
* The agent's current step in its own words (one line, ≤ 80 chars), from the
|
|
269
|
+
* same probe reply as {@link completionPercent}. Running rows prefer it over
|
|
270
|
+
* `lastIntent`; the job widget uses it as its stage line.
|
|
271
|
+
*/
|
|
272
|
+
stage?: string;
|
|
273
|
+
/**
|
|
274
|
+
* What the agent says shows its progress live (one line, ≤ 200 chars), e.g.
|
|
275
|
+
* "BENCH lines in bg_2 pipe:1, tok_s higher is better, target 70% of roofline";
|
|
276
|
+
* from the probe reply. Feeds the job widget author's brief; never rendered.
|
|
277
|
+
*/
|
|
278
|
+
watch?: string;
|
|
267
279
|
/** Data extracted by registered subprocess tool handlers (keyed by tool name) */
|
|
268
280
|
extractedToolData?: Record<string, unknown[]>;
|
|
269
281
|
/**
|
|
@@ -3,6 +3,7 @@ import type { Theme } from "../theme/theme.js";
|
|
|
3
3
|
import { type OutputMeta } from "./output-meta.js";
|
|
4
4
|
import { type ConfiguredThinkingLevel } from "../render/render-utils.js";
|
|
5
5
|
import type { StructuredSubagentOutput } from "./task.js";
|
|
6
|
+
import { type JobWidgetView } from "./job-widget.js";
|
|
6
7
|
import type { RenderResultOptions, ToolActivitySummary } from "./renderer.js";
|
|
7
8
|
import type { IrcDeliveryReceipt, IrcMessage } from "./irc.js";
|
|
8
9
|
import type { NativeToolView, ToolRenderResult } from "./renderer.js";
|
|
@@ -29,6 +30,10 @@ export interface JobSnapshot {
|
|
|
29
30
|
advisor?: boolean;
|
|
30
31
|
/** The task agent's latest self-estimated completion (0–100); see `AgentProgress.completionPercent`. */
|
|
31
32
|
completionPercent?: number;
|
|
33
|
+
/** The task agent's latest stage in its own words; see `AgentProgress.stage`. */
|
|
34
|
+
stage?: string;
|
|
35
|
+
/** The job's live widget, while one is being drawn; frozen at its last state once the job settles. */
|
|
36
|
+
widget?: JobWidgetView;
|
|
32
37
|
resultText?: string;
|
|
33
38
|
errorText?: string;
|
|
34
39
|
/** Source-output metadata retained for per-job warnings and persisted row rendering. */
|
|
@@ -96,6 +101,12 @@ export interface CoordinationDetails {
|
|
|
96
101
|
agents?: AgentActivitySnapshot[];
|
|
97
102
|
/** `wait` was cut short by steering, a peer message, or a completion notice that injects next. */
|
|
98
103
|
interrupted?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* `wait` returned early without a message because continuing would deadlock:
|
|
106
|
+
* the peer it was waiting on is itself waiting on this agent. Both sides can
|
|
107
|
+
* keep working — answer the peer, then wait again.
|
|
108
|
+
*/
|
|
109
|
+
status?: "peer-waiting-on-you";
|
|
99
110
|
}
|
|
100
111
|
/** Pending wait frame. */
|
|
101
112
|
declare function waitRenderCall(_args: object, _options: RenderResultOptions, uiTheme: Theme): Component;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@linxiraos/pi-tui",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.4.1",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"homepage": "https://linxira-os.github.io/zeta/",
|
|
7
7
|
"author": "Linxira-OS",
|
|
@@ -38,14 +38,14 @@
|
|
|
38
38
|
"gen:glyphs": "bun scripts/gen-glyphs.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@linxiraos/pi-omptype": "1.
|
|
42
|
-
"@linxiraos/pi-agent-core": "1.
|
|
43
|
-
"@linxiraos/pi-ai": "1.
|
|
44
|
-
"@linxiraos/pi-catalog": "1.
|
|
45
|
-
"@linxiraos/pi-natives": "1.
|
|
46
|
-
"@linxiraos/pi-utils": "1.
|
|
47
|
-
"@linxiraos/pi-wire": "1.
|
|
48
|
-
"@linxiraos/pi-snapcompact": "1.
|
|
41
|
+
"@linxiraos/pi-omptype": "1.4.1",
|
|
42
|
+
"@linxiraos/pi-agent-core": "1.4.1",
|
|
43
|
+
"@linxiraos/pi-ai": "1.4.1",
|
|
44
|
+
"@linxiraos/pi-catalog": "1.4.1",
|
|
45
|
+
"@linxiraos/pi-natives": "1.4.1",
|
|
46
|
+
"@linxiraos/pi-utils": "1.4.1",
|
|
47
|
+
"@linxiraos/pi-wire": "1.4.1",
|
|
48
|
+
"@linxiraos/pi-snapcompact": "1.4.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"kitty-vt-wasm": "^0.2.0"
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { logger } from "@linxiraos/pi-utils";
|
|
2
2
|
import {
|
|
3
|
+
type ResumeSourceLike,
|
|
3
4
|
SessionSelectorComponent,
|
|
4
|
-
type SessionSelectorEntry,
|
|
5
5
|
type SessionHistoryMatcher,
|
|
6
|
+
type SessionSelectorEntry,
|
|
7
|
+
type SessionSelectorOptions,
|
|
6
8
|
} from "../overlays/session-selector";
|
|
9
|
+
import type { ResumeTreeSource } from "../overlays/resume-tree";
|
|
7
10
|
import { runStandaloneTui } from "./standalone-picker";
|
|
8
11
|
|
|
9
12
|
/** Persistence and history capabilities supplied by the session-owning host. */
|
|
@@ -12,6 +15,8 @@ export interface SessionPickerHost<T extends SessionSelectorEntry = SessionSelec
|
|
|
12
15
|
loadHistoryMatcher?(): SessionHistoryMatcher;
|
|
13
16
|
deleteSession?(session: T): Promise<boolean>;
|
|
14
17
|
loadAllSessions?(): Promise<T[]>;
|
|
18
|
+
/** Builds the three-level resume source for the standalone `--resume` tree. */
|
|
19
|
+
loadResumeSource?(deleteMode: "trash" | "permanent", workspacePath?: string): Promise<ResumeTreeSource | undefined>;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
/** Presentation and capability controls for the standalone session picker. */
|
|
@@ -24,6 +29,12 @@ export interface SessionPickerOptions<T extends SessionSelectorEntry = SessionSe
|
|
|
24
29
|
allowGlobalScope?: boolean;
|
|
25
30
|
historySearch?: boolean;
|
|
26
31
|
pinnedIds?: ReadonlySet<string>;
|
|
32
|
+
/** Opt into the three-level resume tree; `deleteMode` words the confirm step. */
|
|
33
|
+
resume?: { deleteMode: "trash" | "permanent"; workspacePath?: string };
|
|
34
|
+
/** The host's resume model; when set, the native picker renders the project tree. */
|
|
35
|
+
resumeSource?: ResumeSourceLike;
|
|
36
|
+
/** Delete wording: in-session moves to trash, `--resume` deletes permanently. */
|
|
37
|
+
deleteMode?: "trash" | "permanent";
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
/**
|
|
@@ -51,6 +62,11 @@ export async function selectSession<T extends SessionSelectorEntry>(
|
|
|
51
62
|
}
|
|
52
63
|
}
|
|
53
64
|
|
|
65
|
+
const resumeMode = options.resume?.deleteMode;
|
|
66
|
+
const resumeSource = resumeMode
|
|
67
|
+
? await host.loadResumeSource?.(resumeMode, options.resume?.workspacePath)
|
|
68
|
+
: undefined;
|
|
69
|
+
|
|
54
70
|
return runStandaloneTui<T | null>(
|
|
55
71
|
({ ui, finish }) => {
|
|
56
72
|
const selector = new SessionSelectorComponent(
|
|
@@ -73,7 +89,9 @@ export async function selectSession<T extends SessionSelectorEntry>(
|
|
|
73
89
|
scopeLabel: options.scopeLabel,
|
|
74
90
|
showCwd: options.showCwd,
|
|
75
91
|
pinnedIds,
|
|
76
|
-
|
|
92
|
+
resumeSource: resumeSource ?? options.resumeSource,
|
|
93
|
+
deleteMode: resumeMode ?? options.deleteMode,
|
|
94
|
+
} as SessionSelectorOptions<T> & { resumeSource?: ResumeTreeSource; deleteMode?: "trash" | "permanent" },
|
|
77
95
|
);
|
|
78
96
|
selector.setOnRequestRender(() => ui.requestRender());
|
|
79
97
|
return selector;
|
package/src/autocomplete.d.ts
CHANGED
|
@@ -39,6 +39,12 @@ export interface AutocompleteItem {
|
|
|
39
39
|
state?: string;
|
|
40
40
|
/** Dim hint text shown inline after cursor when this item is selected */
|
|
41
41
|
hint?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Slash command matched only through its description, not its name or an alias:
|
|
44
|
+
* a browse hit, so Enter submits the typed text instead of running it unless the
|
|
45
|
+
* user moved the selection onto it.
|
|
46
|
+
*/
|
|
47
|
+
descriptionMatch?: boolean;
|
|
42
48
|
}
|
|
43
49
|
type Awaitable<T> = T | Promise<T>;
|
|
44
50
|
export interface SlashCommand {
|
package/src/autocomplete.ts
CHANGED
|
@@ -226,6 +226,12 @@ export interface AutocompleteItem {
|
|
|
226
226
|
state?: string;
|
|
227
227
|
/** Dim hint text shown inline after cursor when this item is selected */
|
|
228
228
|
hint?: string;
|
|
229
|
+
/**
|
|
230
|
+
* Slash command matched only through its description, not its name or an alias:
|
|
231
|
+
* a browse hit, so Enter submits the typed text instead of running it unless the
|
|
232
|
+
* user moved the selection onto it.
|
|
233
|
+
*/
|
|
234
|
+
descriptionMatch?: boolean;
|
|
229
235
|
}
|
|
230
236
|
|
|
231
237
|
type Awaitable<T> = T | Promise<T>;
|
|
@@ -332,14 +338,18 @@ function getCommandAliases(cmd: CommandEntry): string[] {
|
|
|
332
338
|
}
|
|
333
339
|
|
|
334
340
|
function getStaticCommandDescription(cmd: CommandEntry): string {
|
|
335
|
-
|
|
341
|
+
const raw: unknown = cmd.description;
|
|
342
|
+
const description = typeof raw === "function" ? (raw as () => string)() : raw;
|
|
343
|
+
return typeof description === "string" ? description : "";
|
|
336
344
|
}
|
|
337
345
|
|
|
338
346
|
function getAutocompleteCommandDescription(cmd: CommandEntry): string {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
347
|
+
const candidate: unknown =
|
|
348
|
+
"getAutocompleteDescription" in cmd && typeof cmd.getAutocompleteDescription === "function"
|
|
349
|
+
? cmd.getAutocompleteDescription()
|
|
350
|
+
: cmd.description;
|
|
351
|
+
const description = typeof candidate === "function" ? (candidate as () => string)() : candidate;
|
|
352
|
+
return typeof description === "string" && description.length > 0 ? description : getStaticCommandDescription(cmd);
|
|
343
353
|
}
|
|
344
354
|
|
|
345
355
|
/**
|
|
@@ -437,6 +447,7 @@ function buildSlashCommandCompletions(
|
|
|
437
447
|
...(cmd.iconName && { iconName: cmd.iconName }),
|
|
438
448
|
...(fullDesc && { description: fullDesc }),
|
|
439
449
|
...nativeTextMemo,
|
|
450
|
+
...(nameScore === 0 && { descriptionMatch: true }),
|
|
440
451
|
};
|
|
441
452
|
}
|
|
442
453
|
|
|
@@ -444,7 +455,11 @@ function buildSlashCommandCompletions(
|
|
|
444
455
|
for (const alias of getCommandAliases(cmd)) {
|
|
445
456
|
if (alias === name) continue;
|
|
446
457
|
const aliasScore = scoreCommandTextMatch(lowerPrefix, alias.toLowerCase());
|
|
447
|
-
if (aliasScore === 0
|
|
458
|
+
if (aliasScore === 0) continue;
|
|
459
|
+
// An alias can match even when the description outranks it; Enter
|
|
460
|
+
// must still accept the command rather than submit the raw query.
|
|
461
|
+
if (best?.descriptionMatch) delete best.descriptionMatch;
|
|
462
|
+
if (best && aliasScore <= best.score) continue;
|
|
448
463
|
const fullDesc = resolveFullDesc();
|
|
449
464
|
best = {
|
|
450
465
|
value: alias,
|
|
@@ -14,8 +14,8 @@ import { collapseSkillTokens, skillChipLabel, skillChipStyle, skillToken } from
|
|
|
14
14
|
import { tuiText, tuiTextFmt } from "../i18n";
|
|
15
15
|
import { type UserBubbleOptions, UserMessageComponent, userBubbleColor } from "./user-message";
|
|
16
16
|
import type { TspSpan } from "@linxiraos/pi-wire";
|
|
17
|
-
import { card, md, node, span
|
|
18
|
-
import { type NativeChild, type NativeNode, type NativeUiEvent
|
|
17
|
+
import { card, md, node, span } from "../native/describe";
|
|
18
|
+
import { type NativeChild, type NativeNode, type NativeUiEvent } from "../native/node";
|
|
19
19
|
import { Memo } from "../native/memo";
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -64,8 +64,7 @@ export class SkillMessageComponent extends Container {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
handleNativeEvent(event: NativeUiEvent): void {
|
|
67
|
-
|
|
68
|
-
if (expanded !== undefined) this.setExpanded(expanded);
|
|
67
|
+
if (event.type === "toggle" && event.key === "prompt") this.setExpanded(!event.collapsed);
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
/**
|
|
@@ -101,20 +100,20 @@ export class SkillMessageComponent extends Container {
|
|
|
101
100
|
const children: NativeChild[] = [];
|
|
102
101
|
if (draft) children.push(md(draft));
|
|
103
102
|
const promptText = this.#expanded ? this.#extractText() : "";
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
children.length > 0 ? children : [text([span(label, "muted")])],
|
|
103
|
+
children.push(
|
|
104
|
+
node(
|
|
105
|
+
"section",
|
|
106
|
+
{
|
|
107
|
+
head: [span("prompt", "muted")],
|
|
108
|
+
role: "zeta.skill.prompt",
|
|
109
|
+
collapsible: true,
|
|
110
|
+
collapsed: !this.#expanded,
|
|
111
|
+
},
|
|
112
|
+
promptText ? [md(promptText)] : [],
|
|
113
|
+
"prompt",
|
|
114
|
+
),
|
|
117
115
|
);
|
|
116
|
+
return card({ role: "zeta.skill", tone: "user", head }, children);
|
|
118
117
|
});
|
|
119
118
|
}
|
|
120
119
|
|