codsh-bundle 0.3.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.
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The terminal's user-questions provider: it renders each question as a
3
+ * numbered menu, reads one line per question, and encodes the answer.
4
+ *
5
+ * A question may offer options, free text, or both. Selecting by number
6
+ * answers with that option's label; typing anything else answers as `custom`,
7
+ * which is the encoding `ask_user_question` documents for an "Other" reply.
8
+ * @module codsh-bundle/src/questions
9
+ */
10
+ import type { AskUserQuestionAnswer, AskUserQuestionAnswerItem, AskUserQuestionItem, AskUserQuestionRequest } from '@deepseek-ai/dsh-user-questions';
11
+ import type { SelectOutcome, SelectSpec } from './selector.ts';
12
+ import type { Theme } from './theme.ts';
13
+ /** Puts one selection to the keyboard; absent on a pipe, which types instead. */
14
+ export type SelectAsk = (spec: SelectSpec, signal?: AbortSignal) => Promise<SelectOutcome>;
15
+ /** Reads one answer from the person. */
16
+ export interface LineReader {
17
+ /**
18
+ * Read one submission.
19
+ * @param signal - aborts the read when the owning tool call is cancelled.
20
+ * @returns the answer, or undefined when input ended or the read aborted.
21
+ */
22
+ read(signal?: AbortSignal): Promise<string | undefined>;
23
+ }
24
+ /**
25
+ * Parse a selection line against one question's options.
26
+ *
27
+ * A comma-separated list of numbers selects those options; a multi-select
28
+ * question accepts several, a single-select takes the first. Anything that is
29
+ * not a valid index becomes the free-text answer.
30
+ * @param line - the line the person typed.
31
+ * @param question - the question being answered.
32
+ * @returns the encoded answer for this question.
33
+ */
34
+ export declare function encodeAnswer(line: string, question: AskUserQuestionItem): AskUserQuestionAnswerItem;
35
+ /**
36
+ * Render one question as the lines shown above its prompt.
37
+ * @param question - the question to render.
38
+ * @param theme - styling for the heading and option list.
39
+ * @returns the lines to print.
40
+ */
41
+ export declare function questionLines(question: AskUserQuestionItem, theme: Theme): string[];
42
+ /** Answers `ask_user_question` from the terminal. */
43
+ export declare class TerminalQuestions {
44
+ private readonly reader;
45
+ private readonly theme;
46
+ private readonly write;
47
+ /** The arrow-key selection, offered only where keys can arrive. */
48
+ private readonly select?;
49
+ constructor(reader: LineReader, theme: Theme, write: (line: string) => void,
50
+ /** The arrow-key selection, offered only where keys can arrive. */
51
+ select?: SelectAsk | undefined);
52
+ /**
53
+ * Put every question in one request to the person, in order.
54
+ * @param request - the questions, owner agent, and abort signal.
55
+ * @returns one answer per question, in request order.
56
+ */
57
+ ask(request: AskUserQuestionRequest): Promise<AskUserQuestionAnswer>;
58
+ /**
59
+ * Put one question to the person.
60
+ * @param question - the question.
61
+ * @param signal - aborts with the owning tool call.
62
+ * @returns the encoded answer.
63
+ */
64
+ private one;
65
+ }
@@ -0,0 +1,204 @@
1
+ /**
2
+ * The session's own screen: an alternate-screen viewport with its own scrollback.
3
+ *
4
+ * This is what makes a session feel like a place rather than a run of output.
5
+ * The terminal's buffer is left exactly as the person had it — their shell
6
+ * history is neither scrolled away nor interleaved — and everything this
7
+ * surface shows lives in a buffer it owns: the transcript scrolls inside the
8
+ * viewport while the input box stays where it is, at the bottom.
9
+ *
10
+ * Owning the viewport means doing three jobs the terminal used to do. Lines are
11
+ * wrapped here ({@link wrapAll}), because a line that overflows would otherwise
12
+ * overwrite the row below. Scrolling is ours, because the terminal's scrollback
13
+ * does not exist on the alternate screen. And every frame is painted as a
14
+ * whole, diffed against the last one — which is what removes the class of bug
15
+ * that relative erase arithmetic keeps producing.
16
+ * @module codsh-bundle/src/screen
17
+ */
18
+ /** Where the cursor belongs within the chrome rows. */
19
+ export interface ChromeCursor {
20
+ row: number;
21
+ column: number;
22
+ }
23
+ /** What the screen writes to and measures itself against. */
24
+ export interface ScreenHost {
25
+ /** Emit raw bytes to the terminal. */
26
+ write(data: string): void;
27
+ /** Display columns currently available. */
28
+ columns(): number;
29
+ /** Screen rows currently available. */
30
+ rows(): number;
31
+ }
32
+ /** An alternate-screen viewport over a scrollback buffer this surface owns. */
33
+ export declare class Screen {
34
+ private readonly host;
35
+ /** Logical transcript lines, unwrapped, oldest first. */
36
+ private logical;
37
+ /** The same lines wrapped to the current width — what the viewport slices. */
38
+ private physical;
39
+ /** The bottom rows: input box, menu, indicator, status. */
40
+ private chrome;
41
+ private chromeCursor;
42
+ /** Whether the chrome holds input focus, which is when the cursor shows. */
43
+ private chromeFocus;
44
+ /** Physical rows hidden below the viewport; zero means following the tail. */
45
+ private offset;
46
+ /** What to show while scrolled back, drawn over the viewport's top row. */
47
+ private notice;
48
+ /** A mouse selection over the transcript, in physical-row coordinates. */
49
+ private selection;
50
+ /** Collapsed blocks in the transcript, in order, with both of their forms. */
51
+ private folds;
52
+ /** Whether the folds currently show their full form. */
53
+ private expanded;
54
+ /** The last painted frame, so a repaint only touches rows that changed. */
55
+ private painted;
56
+ /** Width the current frame was painted at, to detect a resize. */
57
+ private paintedColumns;
58
+ private active;
59
+ constructor(host: ScreenHost);
60
+ /** Whether the alternate screen is currently held. */
61
+ get entered(): boolean;
62
+ /** Physical rows scrolled up out of view; zero means the tail is showing. */
63
+ get scrolledBy(): number;
64
+ /** Take the alternate screen and start reporting the mouse. */
65
+ enter(): void;
66
+ /**
67
+ * Give the terminal back exactly as it was.
68
+ *
69
+ * Idempotent, because every exit path calls it — a normal quit, an
70
+ * interrupt, and a crash handler all have to leave the terminal usable.
71
+ */
72
+ leave(): void;
73
+ /**
74
+ * Append finished transcript lines.
75
+ *
76
+ * Following the tail is the default; a person who has scrolled up stays
77
+ * where they are, and the new rows accumulate below them.
78
+ * @param lines - the lines to keep, already styled.
79
+ */
80
+ append(lines: readonly string[]): void;
81
+ /**
82
+ * Append one collapsible block: its summary now, its full form on demand.
83
+ *
84
+ * This is what makes every long block — not merely the latest — expandable:
85
+ * the buffer keeps both forms, and toggling rebuilds the transcript in
86
+ * place, exactly like a details/summary element.
87
+ * @param summary - the collapsed lines, already styled.
88
+ * @param full - the expanded lines, already styled.
89
+ */
90
+ appendFold(summary: readonly string[], full: readonly string[]): void;
91
+ /**
92
+ * Turn the last `count` appended lines into a collapsible block after the
93
+ * fact.
94
+ *
95
+ * This is how a finished answer becomes foldable without ever having been
96
+ * withheld: it streamed in the open, and only once complete does it grow a
97
+ * summary form. The block starts expanded — the person is reading it — and
98
+ * collapses with the rest when the conversation moves on.
99
+ * @param count - how many trailing lines the block owns.
100
+ * @param summary - the collapsed lines, already styled.
101
+ */
102
+ foldBack(count: number, summary: readonly string[]): void;
103
+ /** Whether any collapsible block exists. */
104
+ get hasFolds(): boolean;
105
+ /** Whether the folds currently show their full form. */
106
+ get foldsExpanded(): boolean;
107
+ /**
108
+ * Swap every fold between its summary and its full form.
109
+ * @returns false when there is nothing to toggle.
110
+ */
111
+ toggleFolds(): boolean;
112
+ /** Return every fold to its summary, the way moving on reads as dismissal. */
113
+ collapseFolds(): void;
114
+ /** Put every fold into one form, whatever mix of states they are in now. */
115
+ private setFolds;
116
+ /**
117
+ * Replace the bottom rows.
118
+ * @param rows - the chrome, top to bottom.
119
+ * @param cursor - where the cursor belongs among them.
120
+ * @param focus - whether to show the cursor there.
121
+ */
122
+ setChrome(rows: readonly string[], cursor: ChromeCursor, focus: boolean): void;
123
+ /**
124
+ * Set the line shown while the reader is away from the tail.
125
+ *
126
+ * Drawn OVER the viewport's top row rather than added to the chrome: a notice
127
+ * that changed the chrome's height would move the input box as a side effect
128
+ * of scrolling, and would make a page up and a page down different sizes.
129
+ * @param text - the styled notice, already fitted.
130
+ */
131
+ setScrollNotice(text: string): void;
132
+ /**
133
+ * Scroll the transcript.
134
+ * @param delta - rows to move; negative scrolls back into history.
135
+ */
136
+ scrollBy(delta: number): void;
137
+ /**
138
+ * Scroll by a whole viewport, which is what the page keys mean.
139
+ * @param direction - -1 for back into history, 1 towards the tail.
140
+ */
141
+ scrollPage(direction: -1 | 1): void;
142
+ /** Jump back to the tail, which is also what a new submission does. */
143
+ scrollToBottom(): void;
144
+ /**
145
+ * Drop the transcript, keeping the chrome.
146
+ *
147
+ * Ctrl-L on a shared terminal clears a viewport the person may want back; on
148
+ * our own screen the buffer IS the session's history, so this empties it.
149
+ */
150
+ clearTranscript(): void;
151
+ /** Re-wrap and repaint after the terminal changed size. */
152
+ resize(): void;
153
+ /**
154
+ * Anchor a selection where the left button went down.
155
+ *
156
+ * The terminal cannot select for us while mouse reporting is on, so the
157
+ * viewport does it: press anchors, motion extends, release copies — the
158
+ * shape opencode and Claude give the same gesture.
159
+ * @param row - terminal row, 1-based.
160
+ * @param column - terminal column, 1-based.
161
+ */
162
+ mouseDown(row: number, column: number): void;
163
+ /**
164
+ * Extend the selection to where the pointer moved.
165
+ * @param row - terminal row, 1-based.
166
+ * @param column - terminal column, 1-based.
167
+ */
168
+ mouseDrag(row: number, column: number): void;
169
+ /**
170
+ * Finish the gesture.
171
+ *
172
+ * The highlight stays up — the copy already happened, and the marks show
173
+ * what it took — until the next click or reflow dismisses it.
174
+ * @returns the selected text, or undefined for a bare click.
175
+ */
176
+ mouseUp(): string | undefined;
177
+ /** The selection's bounds in order, top-left first. */
178
+ private orderedSelection;
179
+ /** The plain text under the selection, visual rows joined by newlines. */
180
+ private selectedText;
181
+ /**
182
+ * Map a terminal position to a physical buffer position.
183
+ * @param row - terminal row, 1-based.
184
+ * @param column - terminal column, 1-based.
185
+ * @param clamp - pull an outside position to the nearest content row, the
186
+ * way dragging past an edge keeps selecting, instead of refusing it.
187
+ * @returns the position, or undefined when it misses the content.
188
+ */
189
+ private locate;
190
+ /** Rows the transcript viewport occupies. */
191
+ private viewportHeight;
192
+ /** Columns content is laid out for, one short of the width so no row wraps. */
193
+ private contentColumns;
194
+ /** Re-wrap every kept line at the current width. */
195
+ private rewrap;
196
+ /**
197
+ * Compose and paint the frame.
198
+ *
199
+ * The viewport is padded at the top when the transcript is shorter than the
200
+ * screen, which is what puts the chrome at the bottom from the first frame
201
+ * rather than wherever output happened to reach.
202
+ */
203
+ private render;
204
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The arrow-key selection widget: approvals, questions, and any other choice a
3
+ * person makes by moving a marker rather than by typing an answer.
4
+ *
5
+ * Pure state, like the editor: keys in, a verdict and rows out. One widget
6
+ * serves single-select, multi-select, and shortcut keys, because three slightly
7
+ * different pickers is how the same bug ships three times.
8
+ * @module codsh-bundle/src/selector
9
+ */
10
+ import type { Key } from './keys.ts';
11
+ import type { Theme } from './theme.ts';
12
+ /** One choice on offer. */
13
+ export interface SelectOption {
14
+ /** What the choice is, shown as its row. */
15
+ label: string;
16
+ /** Extra context shown dimly beside the label. */
17
+ detail?: string;
18
+ /** Single key that picks this option outright (e.g. `y`). */
19
+ shortcut?: string;
20
+ }
21
+ /** What a selection asks. */
22
+ export interface SelectSpec {
23
+ /** The question, shown above the options. */
24
+ title: string;
25
+ options: readonly SelectOption[];
26
+ /** Whether several options may be chosen; Space toggles, Enter confirms. */
27
+ multi?: boolean;
28
+ /** Label for a trailing "type your own" row; absent offers none. */
29
+ custom?: string;
30
+ }
31
+ /** How one selection ended. */
32
+ export type SelectOutcome = {
33
+ kind: 'chosen';
34
+ indices: number[];
35
+ } | {
36
+ kind: 'custom';
37
+ } | {
38
+ kind: 'cancelled';
39
+ };
40
+ /** What a key did to the selection. */
41
+ export type SelectorStep = {
42
+ kind: 'pending';
43
+ } | {
44
+ kind: 'done';
45
+ outcome: SelectOutcome;
46
+ };
47
+ export declare class Selector {
48
+ private readonly spec;
49
+ private selected;
50
+ private readonly checked;
51
+ constructor(spec: SelectSpec);
52
+ /** How many rows the widget offers, the custom row included. */
53
+ private get count();
54
+ /** Whether a row index is the custom "type your own" row. */
55
+ private isCustom;
56
+ /**
57
+ * Apply one key.
58
+ * @param key - the decoded keystroke.
59
+ * @returns whether the selection settled, and how.
60
+ */
61
+ handle(key: Key): SelectorStep;
62
+ /**
63
+ * Resolve a typed character: a digit jumps, a shortcut picks, Space toggles.
64
+ * @param text - what was typed.
65
+ * @returns whether the selection settled.
66
+ */
67
+ private typed;
68
+ /**
69
+ * Settle on a row.
70
+ * @param index - the row accepted.
71
+ * @returns the settled step.
72
+ */
73
+ private accept;
74
+ /**
75
+ * Render the widget.
76
+ * @param theme - styling for the marker, shortcuts, and details.
77
+ * @param columns - display columns available per row.
78
+ * @returns the rows, title first.
79
+ */
80
+ view(theme: Theme, columns: number): string[];
81
+ /**
82
+ * One option's label with its number and shortcut.
83
+ * @param option - the option to label.
84
+ * @param theme - styling for the shortcut.
85
+ * @returns the label text.
86
+ */
87
+ private label;
88
+ /**
89
+ * One rendered row.
90
+ * @param index - the row's index.
91
+ * @param label - the row's label, already styled.
92
+ * @param detail - dim context beside it.
93
+ * @param theme - styling for the marker and detail.
94
+ * @param columns - display columns available.
95
+ * @returns the row text.
96
+ */
97
+ private row;
98
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The working indicator: one rewritable line saying the agent is busy, how long
3
+ * it has been, and which key stops it.
4
+ *
5
+ * It occupies the console's single live line, so the transcript above it stays
6
+ * append-only and the indicator never survives into a redirected transcript.
7
+ * @module codsh-bundle/src/spinner
8
+ */
9
+ import type { Theme } from './theme.ts';
10
+ /** The console surface a spinner drives. */
11
+ export interface LiveSurface {
12
+ setLive(text: string | undefined): void;
13
+ readonly isTty: boolean;
14
+ }
15
+ /** Formats the indicator's text for one tick. */
16
+ export interface SpinnerLabel {
17
+ /** What the agent is doing, e.g. `working`. */
18
+ verb: string;
19
+ /** Key that cancels, named for the surface that can read it. */
20
+ interrupt: string;
21
+ /** Live extra segment, read each tick — e.g. tokens spent so far. */
22
+ detail?: () => string | undefined;
23
+ }
24
+ /**
25
+ * Render one tick of the indicator.
26
+ * @param frame - the frame index, taken modulo the frame count.
27
+ * @param elapsedMs - milliseconds since the work started.
28
+ * @param label - the verb and interrupt key to name.
29
+ * @param theme - styling for the frame and the hint.
30
+ * @returns the line to display.
31
+ */
32
+ export declare function spinnerText(frame: number, elapsedMs: number, label: SpinnerLabel, theme: Theme): string;
33
+ /** Drives the working indicator for as long as the agent is busy. */
34
+ export declare class Spinner {
35
+ private readonly surface;
36
+ private readonly theme;
37
+ private readonly label;
38
+ /** Injected so tests advance time without waiting for it. */
39
+ private readonly now;
40
+ private timer;
41
+ private frame;
42
+ private startedAt;
43
+ constructor(surface: LiveSurface, theme: Theme, label: SpinnerLabel,
44
+ /** Injected so tests advance time without waiting for it. */
45
+ now?: () => number);
46
+ /** Whether the indicator is running. */
47
+ get running(): boolean;
48
+ /**
49
+ * Start the indicator, or do nothing when it is already running or the
50
+ * surface has no cursor to rewrite.
51
+ *
52
+ * The clock keeps running across a pause: the elapsed figure is the whole
53
+ * turn's, and an indicator that restarted from zero at every tool call would
54
+ * report each step instead.
55
+ */
56
+ start(): void;
57
+ /** Hide the indicator without forgetting when the turn began. */
58
+ pause(): void;
59
+ /** Stop the indicator: the turn is over, and the next one starts at zero. */
60
+ stop(): void;
61
+ /** Paint the current frame. */
62
+ private draw;
63
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The terminal app's command-line provider: it parses the optional task
3
+ * positional, the session-continuation flags, the preset override, and
4
+ * `--help`, then publishes {@link CODING_CLI_STARTUP_SERVICE}. The runner is an
5
+ * ordinary consumer whose lazy config waits for that service.
6
+ * @module codsh-bundle/startup
7
+ */
8
+ import type { Context } from '@deepseek-ai/cordis';
9
+ /** Stable Cordis plugin name. */
10
+ export declare const name = "coding-cli-startup";
11
+ /** Services required before the invocation can be resolved. */
12
+ export declare const inject: string[];
13
+ /** Service provided by this plugin and injected by the terminal runner. */
14
+ export declare const CODING_CLI_STARTUP_SERVICE = "codingCliStartup";
15
+ /** What the runner row reads from {@link CODING_CLI_STARTUP_SERVICE}. */
16
+ export interface CodingCliStartupValues {
17
+ /** Opening task text, or the empty string when the session starts at the prompt. */
18
+ task: string;
19
+ /** Session to reopen: an explicit id, `'latest'` for `--continue`, or the empty string for a new session. */
20
+ resume: string;
21
+ /** Preset id overriding the roster default, or the empty string to accept it. */
22
+ preset: string;
23
+ /** Render the answer and exit rather than entering the interactive loop. */
24
+ print: boolean;
25
+ }
26
+ /**
27
+ * Parse and provide this invocation as an ordinary Cordis service. On `--help`
28
+ * and on a usage error nothing is provided, so the runner never mounts.
29
+ * @param ctx - plugin context carrying the command line.
30
+ */
31
+ export declare function apply(ctx: Context): void;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The status readout shown with the prompt: what model and composition answer,
3
+ * where the session is, and how much context is left.
4
+ *
5
+ * Every figure is read from a durable projection or a logged fold rather than
6
+ * tracked here, so a resumed session reports the same numbers it ended with.
7
+ * @module codsh-bundle/src/status
8
+ */
9
+ import type { ContextPressureProjection, TokenUsageProjection } from '@deepseek-ai/dsh-token-meter/client';
10
+ import type { Theme } from './theme.ts';
11
+ /** Everything one status line reports. */
12
+ export interface StatusFacts {
13
+ /** Model route answering this session. */
14
+ model: string;
15
+ /** Composed preset, absent when the deployment composes no roster. */
16
+ preset?: string | undefined;
17
+ /** Permission preset name, absent when none is composed. */
18
+ permission?: string | undefined;
19
+ /** Whether plan mode is holding. */
20
+ planMode: boolean;
21
+ /** Session workspace. */
22
+ cwd: string;
23
+ /** Checked-out branch, absent outside a repository. */
24
+ branch?: string | undefined;
25
+ /** Cumulative provider usage, absent before the first reported request. */
26
+ usage?: TokenUsageProjection | undefined;
27
+ /** Context occupancy, absent before the first reported request. */
28
+ context?: ContextPressureProjection | undefined;
29
+ }
30
+ /**
31
+ * Abbreviate a token count the way a status line wants it: exact while small,
32
+ * one decimal at thousands, whole at millions.
33
+ * @param tokens - the count to render.
34
+ * @returns the abbreviated figure.
35
+ */
36
+ export declare function formatTokens(tokens: number): string;
37
+ /**
38
+ * Total tokens a session has spent across every bucket.
39
+ *
40
+ * The four buckets are disjoint by contract — reasoning already sits inside
41
+ * output — so a plain sum is the session total.
42
+ * @param usage - cumulative usage, or undefined before any request.
43
+ * @returns the total, or undefined when nothing is recorded.
44
+ */
45
+ export declare function totalTokens(usage: TokenUsageProjection | undefined): number | undefined;
46
+ /**
47
+ * Percentage of the context window still free for the next request.
48
+ *
49
+ * `projectedTokens` is what the NEXT prompt would cost, which is the figure a
50
+ * person deciding whether to keep going needs; it also moves the instant a
51
+ * compaction shadows a span, where the raw sample cannot.
52
+ * @param context - the occupancy projection.
53
+ * @returns whole percent remaining, or undefined without both figures.
54
+ */
55
+ export declare function contextLeftPercent(context: ContextPressureProjection | undefined): number | undefined;
56
+ /**
57
+ * Shorten a path for display, collapsing the home directory to `~`.
58
+ * @param path - the absolute path.
59
+ * @param home - the home directory to collapse; defaults to the real one.
60
+ * @returns the display path.
61
+ */
62
+ export declare function displayPath(path: string, home?: string): string;
63
+ /**
64
+ * Read the checked-out branch by walking up to the repository's `HEAD`.
65
+ *
66
+ * Read rather than shelled out: `git` may be absent, slow, or blocked by the
67
+ * sandbox, and a status line must never be the reason a prompt stalls. A
68
+ * detached head reports no branch rather than a bare revision, which would read
69
+ * as a branch named after a hash.
70
+ * @param cwd - directory to start from.
71
+ * @returns the branch name, or undefined outside a repository or when detached.
72
+ */
73
+ export declare function gitBranch(cwd: string): Promise<string | undefined>;
74
+ /**
75
+ * Render the status line.
76
+ *
77
+ * Segments that have nothing to report are dropped rather than shown empty, so
78
+ * a fresh session reads as short rather than as broken.
79
+ * @param facts - what to report.
80
+ * @param theme - styling for the segments.
81
+ * @param columns - display columns available; a longer line is cut, never wrapped.
82
+ * @returns the line, unstyled when the theme is plain.
83
+ */
84
+ export declare function statusLine(facts: StatusFacts, theme: Theme, columns: number): string;
85
+ /**
86
+ * Render the fuller readout `/status` answers with.
87
+ *
88
+ * The status line is a glance; this is the place a person looks when the glance
89
+ * raised a question, so it names each usage bucket rather than one total.
90
+ * @param facts - what to report.
91
+ * @param session - the session identity, which `--resume` takes.
92
+ * @returns the report, one `label: value` per line.
93
+ */
94
+ export declare function statusReport(facts: StatusFacts, session: string): string;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Assistant text as it arrives.
3
+ *
4
+ * Token-level display and Markdown rendering pull against each other: a line
5
+ * cannot be styled until it is complete, and a terminal cannot restyle a line
6
+ * that has scrolled. The split is what resolves it — the line being typed lives
7
+ * in the console's one rewritable region as raw text, and the moment it ends it
8
+ * is rendered and written permanently.
9
+ * @module codsh-bundle/src/streaming
10
+ */
11
+ import type { Theme } from './theme.ts';
12
+ /** What one delta produced: finished lines, and the line still being typed. */
13
+ export interface StreamStep {
14
+ /** Rendered lines to append to the transcript. */
15
+ lines: string[];
16
+ /** The in-progress line for the live region, or undefined when none is open. */
17
+ live: string | undefined;
18
+ }
19
+ /** Accumulates assistant text deltas into rendered lines. */
20
+ export declare class TextStream {
21
+ private readonly theme;
22
+ /** Display columns, so the in-progress line never wraps the live region. */
23
+ private readonly columns;
24
+ /**
25
+ * Render lines as dim plain text instead of Markdown. Reasoning wants
26
+ * this: it is the model thinking aloud, not an answer to typeset.
27
+ */
28
+ private readonly plain;
29
+ private markdown;
30
+ private partial;
31
+ private seen;
32
+ constructor(theme: Theme,
33
+ /** Display columns, so the in-progress line never wraps the live region. */
34
+ columns: () => number,
35
+ /**
36
+ * Render lines as dim plain text instead of Markdown. Reasoning wants
37
+ * this: it is the model thinking aloud, not an answer to typeset.
38
+ */
39
+ plain?: boolean);
40
+ /** Whether this message has produced any text yet. */
41
+ get streamed(): boolean;
42
+ /**
43
+ * Take one text delta.
44
+ * @param delta - the text fragment, which may contain any number of newlines.
45
+ * @returns the lines to append and the line still open.
46
+ */
47
+ push(delta: string): StreamStep;
48
+ /**
49
+ * Close the message, rendering whatever line was still open.
50
+ *
51
+ * Called when the model finishes and when a turn is cancelled mid-line: the
52
+ * text already shown has to land in the transcript either way, or the live
53
+ * region would take it away again.
54
+ * @returns the remaining lines to append.
55
+ */
56
+ flush(): string[];
57
+ /** Render one complete line in this stream's mode. */
58
+ private renderLine;
59
+ /**
60
+ * The in-progress line as the live region should show it.
61
+ *
62
+ * Raw rather than rendered: it is not a line yet, and inside a fenced block it
63
+ * is code that Markdown must not touch. Truncated because the live region is
64
+ * one row — a wrapped live line cannot be erased by a single carriage return.
65
+ * @returns the text, or undefined when no line is open.
66
+ */
67
+ private liveText;
68
+ }