@itookit/dsht 0.3.8 → 0.5.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/README.i18n.yaml +2 -2
- package/README.md +30 -11
- package/README.zh.md +30 -11
- package/dist/cli/dsht.js +203 -18
- package/dist/cli/startup.d.ts +40 -0
- package/dist/cli/startup.js +295 -0
- package/dist/cli/trace-summary.d.ts +78 -0
- package/dist/cli/trace-summary.js +241 -0
- package/dist/cli/verifier.d.ts +60 -0
- package/dist/cli/verifier.js +242 -0
- package/dist/contracts.d.ts +344 -0
- package/dist/contracts.js +1 -0
- package/dist/controller/commands.d.ts +47 -0
- package/dist/controller/commands.js +322 -0
- package/dist/controller/connection.d.ts +11 -29
- package/dist/controller/connection.js +26 -60
- package/dist/controller/controller.d.ts +616 -166
- package/dist/controller/controller.js +1395 -146
- package/dist/controller/index.d.ts +8 -1
- package/dist/controller/index.js +5 -0
- package/dist/controller/loop-contract.d.ts +136 -0
- package/dist/controller/loop-contract.js +308 -0
- package/dist/controller/loop-prompts-schema.d.ts +56 -0
- package/dist/controller/loop-prompts-schema.js +144 -0
- package/dist/controller/loop-prompts.d.ts +55 -0
- package/dist/controller/loop-prompts.generated.d.ts +104 -0
- package/dist/controller/loop-prompts.generated.js +185 -0
- package/dist/controller/loop-prompts.js +104 -0
- package/dist/controller/loop-protocols.d.ts +39 -0
- package/dist/controller/loop-protocols.js +115 -0
- package/dist/controller/loop.d.ts +275 -0
- package/dist/controller/loop.js +378 -0
- package/dist/controller/prompts.d.ts +54 -0
- package/dist/controller/prompts.js +162 -0
- package/dist/controller/trace-log.d.ts +45 -0
- package/dist/controller/trace-log.js +144 -0
- package/dist/controller/verifier.d.ts +126 -0
- package/dist/controller/verifier.js +75 -0
- package/dist/cost/index.d.ts +1 -1
- package/dist/cost/index.js +1 -1
- package/dist/cost/ledger.d.ts +0 -1
- package/dist/cost/ledger.js +0 -1
- package/dist/json.d.ts +18 -0
- package/dist/json.js +19 -0
- package/dist/references.d.ts +25 -0
- package/dist/references.js +26 -0
- package/dist/session/connection-view.d.ts +2 -11
- package/dist/session/controller.d.ts +73 -72
- package/dist/session/controller.js +185 -209
- package/dist/session/history.d.ts +6 -18
- package/dist/session/history.js +1 -24
- package/dist/session/index.d.ts +9 -4
- package/dist/session/index.js +7 -3
- package/dist/session/info.d.ts +25 -52
- package/dist/session/info.js +39 -25
- package/dist/session/markdown.js +1 -1
- package/dist/session/math.js +1 -1
- package/dist/session/mutation-gate.d.ts +51 -0
- package/dist/session/mutation-gate.js +73 -0
- package/dist/session/navigation.d.ts +2 -89
- package/dist/session/navigation.js +2 -129
- package/dist/session/peek.d.ts +38 -0
- package/dist/session/peek.js +103 -0
- package/dist/session/references.d.ts +2 -20
- package/dist/session/references.js +1 -26
- package/dist/session/runtime.d.ts +26 -0
- package/dist/session/runtime.js +28 -0
- package/dist/session/telemetry.d.ts +12 -13
- package/dist/session/telemetry.js +27 -58
- package/dist/session/transcript.d.ts +0 -6
- package/dist/session/transcript.js +2 -15
- package/dist/session/types.d.ts +25 -0
- package/dist/session/types.js +0 -1
- package/dist/session-title.d.ts +9 -0
- package/dist/session-title.js +21 -0
- package/dist/shell/controller.d.ts +31 -1
- package/dist/shell/controller.js +34 -2
- package/dist/shell/index.d.ts +3 -3
- package/dist/shell/index.js +2 -2
- package/dist/shell/runner.d.ts +10 -0
- package/dist/shell/runner.js +48 -9
- package/dist/slash/index.d.ts +10 -0
- package/dist/slash/index.js +7 -0
- package/dist/slash/parse.d.ts +166 -0
- package/dist/slash/parse.js +259 -0
- package/dist/slash/pipeline.d.ts +140 -0
- package/dist/slash/pipeline.js +115 -0
- package/dist/slash/registry.d.ts +88 -0
- package/dist/slash/registry.js +177 -0
- package/dist/state.d.ts +14 -4
- package/dist/state.js +3 -2
- package/dist/text.d.ts +28 -0
- package/dist/text.js +55 -0
- package/dist/transport/events.d.ts +104 -0
- package/dist/transport/events.js +149 -0
- package/dist/transport/wire.d.ts +9 -17
- package/dist/transport/wire.js +2 -27
- package/dist/ui/app.js +856 -441
- package/dist/ui/chat/header.js +1 -1
- package/dist/ui/chat/history-view.d.ts +1 -1
- package/dist/ui/chat/loop-status.d.ts +11 -0
- package/dist/ui/chat/loop-status.js +28 -0
- package/dist/ui/chat/navigation-model.d.ts +86 -0
- package/dist/ui/chat/navigation-model.js +107 -0
- package/dist/ui/chat/shell-view.d.ts +15 -2
- package/dist/ui/chat/shell-view.js +37 -3
- package/dist/ui/chat/status.d.ts +47 -3
- package/dist/ui/chat/status.js +65 -50
- package/dist/ui/chat/viewport.d.ts +1 -1
- package/dist/ui/dialogs/cost.d.ts +21 -4
- package/dist/ui/dialogs/cost.js +7 -12
- package/dist/ui/dialogs/index.d.ts +22 -5
- package/dist/ui/dialogs/index.js +19 -3
- package/dist/ui/dialogs/loop.d.ts +43 -0
- package/dist/ui/dialogs/loop.js +224 -0
- package/dist/ui/dialogs/peek.d.ts +25 -0
- package/dist/ui/dialogs/peek.js +35 -0
- package/dist/ui/dialogs/picker.d.ts +2 -0
- package/dist/ui/dialogs/picker.js +4 -2
- package/dist/ui/input/mouse.d.ts +12 -2
- package/dist/ui/input/mouse.js +20 -7
- package/dist/ui/input/references.d.ts +1 -1
- package/dist/ui/status/model.d.ts +7 -0
- package/dist/ui/status/model.js +5 -0
- package/dist/ui/theme/index.d.ts +1 -1
- package/package.json +6 -4
- package/dist/ui/commands/parse.d.ts +0 -104
- package/dist/ui/commands/parse.js +0 -135
- package/dist/ui/commands/registry.d.ts +0 -33
- package/dist/ui/commands/registry.js +0 -73
|
@@ -14,24 +14,6 @@ export interface HistoryRow {
|
|
|
14
14
|
/** Draw the row on the theme's local-command bar, used by `!` commands. */
|
|
15
15
|
highlight?: boolean;
|
|
16
16
|
}
|
|
17
|
-
/** Wrap text into rows with the per-kind trimming rule.
|
|
18
|
-
* @param text - Text to wrap.
|
|
19
|
-
* @param width - Available terminal columns.
|
|
20
|
-
* @param kind - Part kind, which decides whether wrapped lines are trimmed.
|
|
21
|
-
* @param seq - Durable sequence, for committed parts.
|
|
22
|
-
* @returns The wrapped rows.
|
|
23
|
-
*/
|
|
24
|
-
/** Wrap plain local text into terminal rows, preserving its own whitespace.
|
|
25
|
-
*
|
|
26
|
-
* Command output is aligned by spaces and indented by stack traces, so this never collapses runs of
|
|
27
|
-
* whitespace the way tool summaries do; only the terminal width decides where a row breaks.
|
|
28
|
-
* @param text - Raw text, possibly containing newlines.
|
|
29
|
-
* @param width - Available terminal columns.
|
|
30
|
-
* @param kind - Row kind used for coloring.
|
|
31
|
-
* @param highlight - Whether the rows are a local command line drawn on the command bar.
|
|
32
|
-
* @returns One row per wrapped terminal line; empty text yields one empty row.
|
|
33
|
-
*/
|
|
34
|
-
export declare function plainRows(text: string, width: number, kind: RowKind, highlight?: boolean): HistoryRow[];
|
|
35
17
|
/** Drop all terminal rows and layout metadata for an evicted or inactive transcript.
|
|
36
18
|
* @param transcript - Transcript whose previously returned layout is no longer used.
|
|
37
19
|
*/
|
|
@@ -59,6 +41,12 @@ export declare function layoutStats(transcript: Transcript): {
|
|
|
59
41
|
* @param liveReasoning - Fold mode for completed live blocks; below 60 content columns it also folds active reasoning.
|
|
60
42
|
* @returns Row count, sequence offsets, and a viewport reader; `lines` materializes all rows for exports only.
|
|
61
43
|
*/
|
|
44
|
+
/** Plain view of one laid-out record: the projection engine's only public product.
|
|
45
|
+
*
|
|
46
|
+
* It exposes rows through `viewport` and message positions through `offsets`, so a caller can render
|
|
47
|
+
* a window without holding the record itself.
|
|
48
|
+
*/
|
|
49
|
+
export type SessionRender = ReturnType<typeof historyLayout>;
|
|
62
50
|
export declare function historyLayout(transcript: Transcript, width: number, reasoning?: Reasoning, overrides?: ReadonlySet<number>, liveReasoning?: Reasoning): {
|
|
63
51
|
messages: Message[];
|
|
64
52
|
offsets: Map<number, number>;
|
package/dist/session/history.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Indexed semantic history with bounded terminal-row caching and viewport-only materialization. */
|
|
2
2
|
import wrapAnsi from 'wrap-ansi';
|
|
3
|
-
import { toolLine } from "./transcript.js";
|
|
4
3
|
import { hasMarkdown, markdownRows } from "./markdown.js";
|
|
4
|
+
import { toolLine } from "../text.js";
|
|
5
5
|
/** A session owns one layout cache; dropped sessions release their entire cache. */
|
|
6
6
|
class LayoutIndex {
|
|
7
7
|
width;
|
|
@@ -140,21 +140,6 @@ const liveRows = new WeakMap();
|
|
|
140
140
|
* @param seq - Durable sequence, for committed parts.
|
|
141
141
|
* @returns The wrapped rows.
|
|
142
142
|
*/
|
|
143
|
-
/** Wrap plain local text into terminal rows, preserving its own whitespace.
|
|
144
|
-
*
|
|
145
|
-
* Command output is aligned by spaces and indented by stack traces, so this never collapses runs of
|
|
146
|
-
* whitespace the way tool summaries do; only the terminal width decides where a row breaks.
|
|
147
|
-
* @param text - Raw text, possibly containing newlines.
|
|
148
|
-
* @param width - Available terminal columns.
|
|
149
|
-
* @param kind - Row kind used for coloring.
|
|
150
|
-
* @param highlight - Whether the rows are a local command line drawn on the command bar.
|
|
151
|
-
* @returns One row per wrapped terminal line; empty text yields one empty row.
|
|
152
|
-
*/
|
|
153
|
-
export function plainRows(text, width, kind, highlight = false) {
|
|
154
|
-
const columns = Math.max(1, width);
|
|
155
|
-
const wrapped = wrapAnsi(text === '' ? ' ' : text, columns, { hard: true, trim: false });
|
|
156
|
-
return wrapped.split('\n').map(line => ({ text: line, kind, ...(highlight ? { highlight: true } : {}) }));
|
|
157
|
-
}
|
|
158
143
|
function wrapRows(text, width, kind, seq) {
|
|
159
144
|
return wrapAnsi(text, width, { hard: true, trim: !['tool', 'success', 'error'].includes(kind) })
|
|
160
145
|
.split('\n').map(text => ({ text, kind, seq }));
|
|
@@ -295,14 +280,6 @@ export function layoutStats(transcript) {
|
|
|
295
280
|
return undefined;
|
|
296
281
|
return { ...index.stats(), liveWraps: index.liveWraps.size, liveMarkdown: index.liveMarkdown.size };
|
|
297
282
|
}
|
|
298
|
-
/** Lay out an indexed conversation without concatenating its historical rows on every stream frame.
|
|
299
|
-
* @param transcript - Selected session's semantic content and unfinished assistant output.
|
|
300
|
-
* @param width - Available terminal columns.
|
|
301
|
-
* @param reasoning - Global committed/completed reasoning fold mode.
|
|
302
|
-
* @param overrides - Sequences whose fold mode differs from the global mode; replace the set on changes.
|
|
303
|
-
* @param liveReasoning - Fold mode for completed live blocks; below 60 content columns it also folds active reasoning.
|
|
304
|
-
* @returns Row count, sequence offsets, and a viewport reader; `lines` materializes all rows for exports only.
|
|
305
|
-
*/
|
|
306
283
|
export function historyLayout(transcript, width, reasoning = 'row', overrides = noOverrides, liveReasoning = reasoning) {
|
|
307
284
|
let index = indexes.get(transcript);
|
|
308
285
|
if (!index || index.width !== width || index.reasoning !== reasoning || index.overrides !== overrides) {
|
package/dist/session/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/** Session domain: the selected session, its transcript, layout, telemetry and navigation. */
|
|
2
2
|
export { SessionController } from './controller.ts';
|
|
3
|
-
export {
|
|
3
|
+
export { SessionMutationGate } from './mutation-gate.ts';
|
|
4
|
+
export type { MutationAdmission, MutationLane } from './mutation-gate.ts';
|
|
5
|
+
export { contentText, Transcript } from './transcript.ts';
|
|
6
|
+
export { toolLine } from '../text.ts';
|
|
4
7
|
export type { LivePhase, Message, MessagePart, ThoughtEntry } from './transcript.ts';
|
|
5
8
|
export { historyLayout, layoutStats, releaseHistoryLayout } from './history.ts';
|
|
6
9
|
export { markdownCacheStats } from './markdown.ts';
|
|
@@ -10,9 +13,11 @@ export type { QueuedInput } from './telemetry.ts';
|
|
|
10
13
|
export { DEFAULT_HISTORY_LIMITS, historyLimits } from './memory.ts';
|
|
11
14
|
export type { HistoryLimits } from './memory.ts';
|
|
12
15
|
export { DEFAULT_PROMPT_LIMITS, promptText, PromptIndex, SessionInfo } from './info.ts';
|
|
13
|
-
export type {
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
+
export type { InteractionState, ModelState, OptionState, PanelState, PromptEntry, PromptLimits, PromptRecord, } from './info.ts';
|
|
17
|
+
export { resolveTarget } from './navigation.ts';
|
|
18
|
+
export { sessionLabel } from '../session-title.ts';
|
|
19
|
+
export { fileReferences } from './references.ts';
|
|
20
|
+
export { activeReference, fileMention } from '../references.ts';
|
|
16
21
|
export type { FileReference } from './references.ts';
|
|
17
22
|
export { saveSessionLog } from './export.ts';
|
|
18
23
|
export type { HistorySearch, RemovalTarget } from './types.ts';
|
package/dist/session/index.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
/** Session domain: the selected session, its transcript, layout, telemetry and navigation. */
|
|
2
2
|
export { SessionController } from "./controller.js";
|
|
3
|
-
export {
|
|
3
|
+
export { SessionMutationGate } from "./mutation-gate.js";
|
|
4
|
+
export { contentText, Transcript } from "./transcript.js";
|
|
5
|
+
export { toolLine } from "../text.js";
|
|
4
6
|
export { historyLayout, layoutStats, releaseHistoryLayout } from "./history.js";
|
|
5
7
|
export { markdownCacheStats } from "./markdown.js";
|
|
6
8
|
export { Telemetry } from "./telemetry.js";
|
|
7
9
|
export { DEFAULT_HISTORY_LIMITS, historyLimits } from "./memory.js";
|
|
8
10
|
export { DEFAULT_PROMPT_LIMITS, promptText, PromptIndex, SessionInfo } from "./info.js";
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
+
export { resolveTarget } from "./navigation.js";
|
|
12
|
+
export { sessionLabel } from "../session-title.js";
|
|
13
|
+
export { fileReferences } from "./references.js";
|
|
14
|
+
export { activeReference, fileMention } from "../references.js";
|
|
11
15
|
export { saveSessionLog } from "./export.js";
|
package/dist/session/info.d.ts
CHANGED
|
@@ -1,17 +1,5 @@
|
|
|
1
|
-
/** Session-owned prompt index: every user prompt of the selected session, plus its recall cursor.
|
|
2
|
-
*
|
|
3
|
-
* The index replaces a plain bounded recall buffer. Each durable entry carries the sequence it came
|
|
4
|
-
* from, so an entry evicted by the budgets stays recoverable: the loaded window refills anything the
|
|
5
|
-
* transcript still holds, and `session/page` refetches anything older than the window. Budgets
|
|
6
|
-
* therefore bound memory without deciding reachability — which is what a base-200 buffer got wrong.
|
|
7
|
-
*
|
|
8
|
-
* Locally submitted slash commands never become durable records, so they are retained too, marked as
|
|
9
|
-
* non-durable. A durable echo of a locally recorded prompt upgrades that entry instead of adding a
|
|
10
|
-
* second copy, keeping the refill boundary (the oldest durable sequence) exact.
|
|
11
|
-
*/
|
|
12
|
-
import { type Reasoning } from './history.ts';
|
|
13
1
|
import { Transcript } from './transcript.ts';
|
|
14
|
-
import type { HistorySearch } from './types.ts';
|
|
2
|
+
import type { AnswerValue, HistorySearch } from './types.ts';
|
|
15
3
|
import type { ObjectValue } from '../transport/wire.ts';
|
|
16
4
|
/** A durable user prompt as the transcript reports it, before retention. */
|
|
17
5
|
export interface PromptRecord {
|
|
@@ -31,49 +19,24 @@ export interface PromptLimits {
|
|
|
31
19
|
export declare const DEFAULT_PROMPT_LIMITS: PromptLimits;
|
|
32
20
|
/** Flatten one transcript prompt into the single line the composer recalls. */
|
|
33
21
|
export declare function promptText(value: string): string;
|
|
34
|
-
/** The selected session's composer: its draft, the caret in it, and a draft a dialog parked aside. */
|
|
35
|
-
export interface ComposerState {
|
|
36
|
-
draft: string;
|
|
37
|
-
cursor: number;
|
|
38
|
-
parked: string;
|
|
39
|
-
}
|
|
40
|
-
/** How the selected session's record is being read: which window, where, and what is expanded.
|
|
41
|
-
*
|
|
42
|
-
* Everything here changes how the record renders, which is why it is session state rather than a
|
|
43
|
-
* transient panel flag; the record's content stays in `Transcript`. `window` is a strong reference
|
|
44
|
-
* released by `closeWindow`, and the layout cache keyed by it is a weak one.
|
|
45
|
-
*/
|
|
46
|
-
export interface ViewState {
|
|
47
|
-
/** Detached record shown instead of the live transcript while reading jumped-to history. */
|
|
48
|
-
window?: Transcript;
|
|
49
|
-
scroll: number;
|
|
50
|
-
/** Reading protection: reclamation pauses until the reader returns to the live end. */
|
|
51
|
-
pinned: boolean;
|
|
52
|
-
/** Message sequences whose reasoning is expanded beyond the default fold. */
|
|
53
|
-
folds: ReadonlySet<number>;
|
|
54
|
-
/** Fold mode for the live attempt's completed reasoning. */
|
|
55
|
-
liveReasoning: Reasoning;
|
|
56
|
-
}
|
|
57
|
-
/** Composer-adjacent `@` reference menu: the highlighted row and the draft that dismissed it. */
|
|
58
|
-
export interface ReferenceState {
|
|
59
|
-
index: number;
|
|
60
|
-
dismissed?: string;
|
|
61
|
-
}
|
|
62
22
|
/** Model dialog step: the catalog plus the provider or model being inspected. */
|
|
63
23
|
export interface ModelState {
|
|
64
24
|
catalog: ObjectValue;
|
|
65
25
|
provider?: string;
|
|
66
26
|
model?: ObjectValue;
|
|
67
27
|
}
|
|
68
|
-
/** Panels the reader opened
|
|
28
|
+
/** Panels the reader opened; visibility and query text only, so the UI owns them.
|
|
69
29
|
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* is focus
|
|
30
|
+
* One container rather than one flag per call site, so the composition root has a single panel set
|
|
31
|
+
* to gate keys, reset on a session switch and close from a command. Every session panel's rows come
|
|
32
|
+
* from the selected record and its cursor is focus held by `Picker`; the one exception is `prompts`,
|
|
33
|
+
* which lists a client-global file and is therefore not derived from the record.
|
|
73
34
|
*/
|
|
74
35
|
export interface PanelState {
|
|
75
36
|
thoughts: boolean;
|
|
76
37
|
queue: boolean;
|
|
38
|
+
/** Saved shortcut prompts opened by `/prompt`; a client-global list, kept here for one registry. */
|
|
39
|
+
prompts?: boolean;
|
|
77
40
|
model?: ModelState;
|
|
78
41
|
history?: {
|
|
79
42
|
query: string;
|
|
@@ -98,7 +61,7 @@ export interface OptionState {
|
|
|
98
61
|
* `SessionController.interactions`, which is derived per session on every publish.
|
|
99
62
|
*/
|
|
100
63
|
export interface InteractionState {
|
|
101
|
-
answers: Record<string,
|
|
64
|
+
answers: Record<string, AnswerValue['answers']>;
|
|
102
65
|
option?: OptionState;
|
|
103
66
|
approval?: {
|
|
104
67
|
eventId: string;
|
|
@@ -107,7 +70,7 @@ export interface InteractionState {
|
|
|
107
70
|
}
|
|
108
71
|
/** Client-owned state of the selected session, reset whenever another session is opened.
|
|
109
72
|
*
|
|
110
|
-
* It holds the record, the prompt index, the
|
|
73
|
+
* It holds the record, the prompt index, the reading view and the local interaction
|
|
111
74
|
* state because all five belong to one session and none of them is owned by the host beyond what the
|
|
112
75
|
* record mirrors; everything derivable from `Telemetry` or `CostLedger` stays out (see the design's
|
|
113
76
|
* §5.7.6). The record is referenced here and nowhere else, so "the selected session" has one entry.
|
|
@@ -117,11 +80,9 @@ export declare class SessionInfo {
|
|
|
117
80
|
/** The selected session's record. Replaced — never mutated in place — when another session opens. */
|
|
118
81
|
record: Transcript;
|
|
119
82
|
readonly prompts: PromptIndex;
|
|
120
|
-
|
|
121
|
-
|
|
83
|
+
/** Detached history window the reader jumped to; released by `closeWindow` and `reset`. */
|
|
84
|
+
window?: Transcript;
|
|
122
85
|
readonly interaction: InteractionState;
|
|
123
|
-
readonly reference: ReferenceState;
|
|
124
|
-
readonly panels: PanelState;
|
|
125
86
|
constructor(sessionId?: string);
|
|
126
87
|
/** Forget everything a previous session owned, keeping this instance identity-stable for `State`. */
|
|
127
88
|
reset(sessionId?: string): void;
|
|
@@ -143,6 +104,12 @@ export declare class PromptIndex {
|
|
|
143
104
|
private shed;
|
|
144
105
|
private position;
|
|
145
106
|
private draft;
|
|
107
|
+
/** Prompts the client sent on the operator's behalf; their durable echo never enters recall.
|
|
108
|
+
*
|
|
109
|
+
* Kept across `reset()` so re-opening the session in the same process stays clean, and bounded
|
|
110
|
+
* because an agent loop can send a prompt per attempt.
|
|
111
|
+
*/
|
|
112
|
+
private readonly internal;
|
|
146
113
|
constructor(limits?: PromptLimits);
|
|
147
114
|
/** Retained entry count, so a caller can tell an empty index from a parked cursor. */
|
|
148
115
|
get length(): number;
|
|
@@ -167,8 +134,14 @@ export declare class PromptIndex {
|
|
|
167
134
|
* live window holds, so the lazy backward step could not recover them.
|
|
168
135
|
*/
|
|
169
136
|
markComplete(): void;
|
|
170
|
-
/** Forget one session's prompts and cursor. */
|
|
137
|
+
/** Forget one session's prompts and cursor; client-generated suppression is process-wide. */
|
|
171
138
|
reset(): void;
|
|
139
|
+
/** Remember one prompt the client sent itself, so its durable echo never enters recall.
|
|
140
|
+
*
|
|
141
|
+
* An agent loop submits many turns; without this they would crowd out what the operator typed.
|
|
142
|
+
* @param value - Prompt text the client sent on the operator's behalf.
|
|
143
|
+
*/
|
|
144
|
+
suppress(value: string): void;
|
|
172
145
|
/** Fold one transcript scan: its prompts, plus the watermark it covered.
|
|
173
146
|
*
|
|
174
147
|
* The watermark advances even when the scan found no prompt, so a turn of assistant and tool
|
package/dist/session/info.js
CHANGED
|
@@ -15,11 +15,13 @@ import { Transcript } from "./transcript.js";
|
|
|
15
15
|
export const DEFAULT_PROMPT_LIMITS = { maxEntries: 2000, maxBytes: 512 * 1024 };
|
|
16
16
|
/** Longest single prompt worth recalling; a larger one is skipped rather than truncated in place. */
|
|
17
17
|
const MAX_ENTRY_CHARS = 128 * 1024;
|
|
18
|
+
/** Client-generated prompts remembered for recall suppression; a bounded run needs far fewer. */
|
|
19
|
+
const MAX_INTERNAL_PROMPTS = 512;
|
|
18
20
|
/** Flatten one transcript prompt into the single line the composer recalls. */
|
|
19
21
|
export function promptText(value) { return value.replace(/\r?\n/g, ' ').trim(); }
|
|
20
22
|
/** Client-owned state of the selected session, reset whenever another session is opened.
|
|
21
23
|
*
|
|
22
|
-
* It holds the record, the prompt index, the
|
|
24
|
+
* It holds the record, the prompt index, the reading view and the local interaction
|
|
23
25
|
* state because all five belong to one session and none of them is owned by the host beyond what the
|
|
24
26
|
* record mirrors; everything derivable from `Telemetry` or `CostLedger` stays out (see the design's
|
|
25
27
|
* §5.7.6). The record is referenced here and nowhere else, so "the selected session" has one entry.
|
|
@@ -29,11 +31,9 @@ export class SessionInfo {
|
|
|
29
31
|
/** The selected session's record. Replaced — never mutated in place — when another session opens. */
|
|
30
32
|
record = new Transcript();
|
|
31
33
|
prompts = new PromptIndex();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
/** Detached history window the reader jumped to; released by `closeWindow` and `reset`. */
|
|
35
|
+
window;
|
|
34
36
|
interaction = { answers: {} };
|
|
35
|
-
reference = { index: 0 };
|
|
36
|
-
panels = { thoughts: false, queue: false };
|
|
37
37
|
constructor(sessionId = '') {
|
|
38
38
|
this.sessionId = sessionId;
|
|
39
39
|
}
|
|
@@ -45,32 +45,18 @@ export class SessionInfo {
|
|
|
45
45
|
this.record.dispose();
|
|
46
46
|
this.record = new Transcript();
|
|
47
47
|
this.prompts.reset();
|
|
48
|
-
this.composer.draft = '';
|
|
49
|
-
this.composer.cursor = 0;
|
|
50
|
-
this.composer.parked = '';
|
|
51
|
-
this.view.scroll = 0;
|
|
52
|
-
this.view.pinned = false;
|
|
53
|
-
this.view.folds = new Set();
|
|
54
|
-
this.view.liveReasoning = 'row';
|
|
55
48
|
this.interaction.answers = {};
|
|
56
49
|
this.interaction.option = undefined;
|
|
57
50
|
this.interaction.approval = undefined;
|
|
58
|
-
this.reference.index = 0;
|
|
59
|
-
this.reference.dismissed = undefined;
|
|
60
|
-
this.panels.thoughts = false;
|
|
61
|
-
this.panels.queue = false;
|
|
62
|
-
this.panels.model = undefined;
|
|
63
|
-
this.panels.history = undefined;
|
|
64
|
-
this.panels.search = undefined;
|
|
65
51
|
}
|
|
66
52
|
/** Release the detached history window, if the reader has one open. */
|
|
67
53
|
closeWindow() {
|
|
68
|
-
const window = this.
|
|
54
|
+
const window = this.window;
|
|
69
55
|
if (!window)
|
|
70
56
|
return;
|
|
71
57
|
releaseHistoryLayout(window);
|
|
72
58
|
window.dispose();
|
|
73
|
-
this.
|
|
59
|
+
this.window = undefined;
|
|
74
60
|
}
|
|
75
61
|
}
|
|
76
62
|
/** Seq-ordered prompts with a recall cursor, owned by the selected session.
|
|
@@ -88,6 +74,12 @@ export class PromptIndex {
|
|
|
88
74
|
shed = false;
|
|
89
75
|
position;
|
|
90
76
|
draft = '';
|
|
77
|
+
/** Prompts the client sent on the operator's behalf; their durable echo never enters recall.
|
|
78
|
+
*
|
|
79
|
+
* Kept across `reset()` so re-opening the session in the same process stays clean, and bounded
|
|
80
|
+
* because an agent loop can send a prompt per attempt.
|
|
81
|
+
*/
|
|
82
|
+
internal = new Set();
|
|
91
83
|
constructor(limits = DEFAULT_PROMPT_LIMITS) {
|
|
92
84
|
this.limits = limits;
|
|
93
85
|
}
|
|
@@ -122,7 +114,7 @@ export class PromptIndex {
|
|
|
122
114
|
*/
|
|
123
115
|
markComplete() { if (!this.shed)
|
|
124
116
|
this.complete = true; }
|
|
125
|
-
/** Forget one session's prompts and cursor. */
|
|
117
|
+
/** Forget one session's prompts and cursor; client-generated suppression is process-wide. */
|
|
126
118
|
reset() {
|
|
127
119
|
this.entries = [];
|
|
128
120
|
this.bytes = 0;
|
|
@@ -132,6 +124,24 @@ export class PromptIndex {
|
|
|
132
124
|
this.position = undefined;
|
|
133
125
|
this.draft = '';
|
|
134
126
|
}
|
|
127
|
+
/** Remember one prompt the client sent itself, so its durable echo never enters recall.
|
|
128
|
+
*
|
|
129
|
+
* An agent loop submits many turns; without this they would crowd out what the operator typed.
|
|
130
|
+
* @param value - Prompt text the client sent on the operator's behalf.
|
|
131
|
+
*/
|
|
132
|
+
suppress(value) {
|
|
133
|
+
const text = promptText(value);
|
|
134
|
+
if (!text)
|
|
135
|
+
return;
|
|
136
|
+
this.internal.delete(text);
|
|
137
|
+
this.internal.add(text);
|
|
138
|
+
while (this.internal.size > MAX_INTERNAL_PROMPTS) {
|
|
139
|
+
const oldest = this.internal.values().next().value;
|
|
140
|
+
if (oldest === undefined)
|
|
141
|
+
break;
|
|
142
|
+
this.internal.delete(oldest);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
135
145
|
/** Fold one transcript scan: its prompts, plus the watermark it covered.
|
|
136
146
|
*
|
|
137
147
|
* The watermark advances even when the scan found no prompt, so a turn of assistant and tool
|
|
@@ -154,7 +164,10 @@ export class PromptIndex {
|
|
|
154
164
|
* @param value - Submitted command text; consecutive repeats coalesce.
|
|
155
165
|
*/
|
|
156
166
|
record(value) {
|
|
157
|
-
const
|
|
167
|
+
const text = promptText(value);
|
|
168
|
+
if (this.internal.has(text))
|
|
169
|
+
return;
|
|
170
|
+
const entry = { seq: this.newestSeq, text, durable: false };
|
|
158
171
|
if (!this.retainable(entry) || this.entries.at(-1)?.text === entry.text)
|
|
159
172
|
return;
|
|
160
173
|
this.entries.push(entry);
|
|
@@ -167,7 +180,7 @@ export class PromptIndex {
|
|
|
167
180
|
*/
|
|
168
181
|
prepend(values) {
|
|
169
182
|
const older = values.map(value => ({ seq: value.seq, text: promptText(value.text), durable: true }))
|
|
170
|
-
.filter(value => this.retainable(value));
|
|
183
|
+
.filter(value => !this.internal.has(value.text) && this.retainable(value));
|
|
171
184
|
if (!older.length)
|
|
172
185
|
return 0;
|
|
173
186
|
this.entries = [...older, ...this.entries];
|
|
@@ -213,7 +226,8 @@ export class PromptIndex {
|
|
|
213
226
|
/** Retain one durable prompt; a durable echo upgrades a local entry instead of duplicating it. */
|
|
214
227
|
push(value) {
|
|
215
228
|
const entry = { seq: value.seq, text: promptText(value.text), durable: true };
|
|
216
|
-
|
|
229
|
+
// A prompt the client itself sent (an agent loop) is durable history, but not composer recall.
|
|
230
|
+
if (this.internal.has(entry.text) || !this.retainable(entry))
|
|
217
231
|
return;
|
|
218
232
|
const last = this.entries.at(-1);
|
|
219
233
|
if (last && last.text === entry.text) {
|
package/dist/session/markdown.js
CHANGED
|
@@ -5,7 +5,7 @@ import stringWidth from 'string-width';
|
|
|
5
5
|
import wrapAnsi from 'wrap-ansi';
|
|
6
6
|
import { stripVTControlCharacters } from 'node:util';
|
|
7
7
|
import { decodeHTML } from 'entities';
|
|
8
|
-
import { safeText } from "../
|
|
8
|
+
import { safeText } from "../text.js";
|
|
9
9
|
import { renderMath } from "./math.js";
|
|
10
10
|
const parser = new Marked({ gfm: true });
|
|
11
11
|
// Math is tokenized before Markdown escapes, but after fenced and inline code have claimed their source.
|
package/dist/session/math.js
CHANGED
|
@@ -8,7 +8,7 @@ import { TextNode } from '@mathjax/src/js/core/MmlTree/MmlNode.js';
|
|
|
8
8
|
import { SerializedMmlVisitor } from '@mathjax/src/js/core/MmlTree/SerializedMmlVisitor.js';
|
|
9
9
|
import '@mathjax/src/js/input/tex/base/BaseConfiguration.js';
|
|
10
10
|
import '@mathjax/src/js/input/tex/ams/AmsConfiguration.js';
|
|
11
|
-
import { safeText } from "../
|
|
11
|
+
import { safeText } from "../text.js";
|
|
12
12
|
RegisterHTMLHandler(liteAdaptor());
|
|
13
13
|
const superscripts = Object.fromEntries([...('0123456789+-=()ni')].map((c, i) => [c, [...'⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿⁱ'][i]]));
|
|
14
14
|
const subscripts = Object.fromEntries([...'0123456789+-=()aehijklmnoprstuvx'].map((c, i) => [c, [...'₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ'][i]]));
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** One session's mutation admission order, with a control lane that overtakes the normal waiters.
|
|
2
|
+
*
|
|
3
|
+
* The gate serializes the **admission** of a mutation — checking state, deciding, and issuing the
|
|
4
|
+
* request — not the remote mutation itself. A section therefore returns as soon as the request is
|
|
5
|
+
* issued, and the caller awaits the host's answer outside the gate: a `/compact` can take minutes, and
|
|
6
|
+
* holding the gate for it would make `cancel` wait for exactly the operation it exists to interrupt.
|
|
7
|
+
* The `return dispatch()` below is what enforces that: the gate releases when the section *returns*,
|
|
8
|
+
* so even an `async` section holds it only up to its first `await`.
|
|
9
|
+
*
|
|
10
|
+
* Two lanes:
|
|
11
|
+
*
|
|
12
|
+
* * `normal` — a prompt, steering, a host command, an answer, a queue removal: arrival order;
|
|
13
|
+
* * `control` — `cancelTurn`/`interrupt` and `/loop stop`: waits only for the section already running,
|
|
14
|
+
* so it is admitted ahead of every normal admission that is still waiting.
|
|
15
|
+
*
|
|
16
|
+
* The key is the **target** session, not the selected one: cancelling a forked verifier addresses the
|
|
17
|
+
* verifier's own session, and that must not queue behind the reviewed session's writes.
|
|
18
|
+
*/
|
|
19
|
+
/** Which queue one mutation admission joins. */
|
|
20
|
+
export type MutationLane = 'normal' | 'control';
|
|
21
|
+
/** One admission, as the gate reports it so the trace can answer "who dispatched first". */
|
|
22
|
+
export interface MutationAdmission {
|
|
23
|
+
/** Session the mutation targets; never inferred from the selection. */
|
|
24
|
+
readonly sessionId: string;
|
|
25
|
+
/** Lane the admission used. */
|
|
26
|
+
readonly lane: MutationLane;
|
|
27
|
+
/** Whether another admission already held this session's gate when this one arrived. */
|
|
28
|
+
readonly waited: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Serializes one session's mutation admissions. */
|
|
31
|
+
export declare class SessionMutationGate {
|
|
32
|
+
private readonly report?;
|
|
33
|
+
private readonly lanes;
|
|
34
|
+
/** @param report - Optional observer, called once per admission in dispatch order. */
|
|
35
|
+
constructor(report?: ((admission: MutationAdmission) => void) | undefined);
|
|
36
|
+
/** Wait for this session's turn, then run one section inside the gate.
|
|
37
|
+
*
|
|
38
|
+
* `dispatch` must decide and **issue**: return the promise the caller will await, rather than
|
|
39
|
+
* awaiting it here, or the gate stays held for the whole remote mutation.
|
|
40
|
+
* @param sessionId - Target session the mutation belongs to.
|
|
41
|
+
* @param lane - `control` overtakes waiting normal admissions; `normal` keeps arrival order.
|
|
42
|
+
* @param dispatch - Synchronous decision plus request issue.
|
|
43
|
+
* @returns What `dispatch` returned; a returned promise is adopted, so the caller may await the
|
|
44
|
+
* host's answer without holding the gate.
|
|
45
|
+
*/
|
|
46
|
+
admit<T>(sessionId: string, lane: MutationLane, dispatch: () => T): Promise<T>;
|
|
47
|
+
/** Take the gate now, or queue for it behind the running section. */
|
|
48
|
+
private acquire;
|
|
49
|
+
/** Hand the gate to the next waiter, control first, or free it. */
|
|
50
|
+
private release;
|
|
51
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** One session's mutation admission order, with a control lane that overtakes the normal waiters.
|
|
2
|
+
*
|
|
3
|
+
* The gate serializes the **admission** of a mutation — checking state, deciding, and issuing the
|
|
4
|
+
* request — not the remote mutation itself. A section therefore returns as soon as the request is
|
|
5
|
+
* issued, and the caller awaits the host's answer outside the gate: a `/compact` can take minutes, and
|
|
6
|
+
* holding the gate for it would make `cancel` wait for exactly the operation it exists to interrupt.
|
|
7
|
+
* The `return dispatch()` below is what enforces that: the gate releases when the section *returns*,
|
|
8
|
+
* so even an `async` section holds it only up to its first `await`.
|
|
9
|
+
*
|
|
10
|
+
* Two lanes:
|
|
11
|
+
*
|
|
12
|
+
* * `normal` — a prompt, steering, a host command, an answer, a queue removal: arrival order;
|
|
13
|
+
* * `control` — `cancelTurn`/`interrupt` and `/loop stop`: waits only for the section already running,
|
|
14
|
+
* so it is admitted ahead of every normal admission that is still waiting.
|
|
15
|
+
*
|
|
16
|
+
* The key is the **target** session, not the selected one: cancelling a forked verifier addresses the
|
|
17
|
+
* verifier's own session, and that must not queue behind the reviewed session's writes.
|
|
18
|
+
*/
|
|
19
|
+
/** Serializes one session's mutation admissions. */
|
|
20
|
+
export class SessionMutationGate {
|
|
21
|
+
report;
|
|
22
|
+
lanes = new Map();
|
|
23
|
+
/** @param report - Optional observer, called once per admission in dispatch order. */
|
|
24
|
+
constructor(report) {
|
|
25
|
+
this.report = report;
|
|
26
|
+
}
|
|
27
|
+
/** Wait for this session's turn, then run one section inside the gate.
|
|
28
|
+
*
|
|
29
|
+
* `dispatch` must decide and **issue**: return the promise the caller will await, rather than
|
|
30
|
+
* awaiting it here, or the gate stays held for the whole remote mutation.
|
|
31
|
+
* @param sessionId - Target session the mutation belongs to.
|
|
32
|
+
* @param lane - `control` overtakes waiting normal admissions; `normal` keeps arrival order.
|
|
33
|
+
* @param dispatch - Synchronous decision plus request issue.
|
|
34
|
+
* @returns What `dispatch` returned; a returned promise is adopted, so the caller may await the
|
|
35
|
+
* host's answer without holding the gate.
|
|
36
|
+
*/
|
|
37
|
+
async admit(sessionId, lane, dispatch) {
|
|
38
|
+
const state = this.lanes.get(sessionId) ?? { busy: false, control: [], normal: [] };
|
|
39
|
+
this.lanes.set(sessionId, state);
|
|
40
|
+
const waited = state.busy;
|
|
41
|
+
await this.acquire(state, lane);
|
|
42
|
+
this.report?.({ sessionId, lane, waited });
|
|
43
|
+
try {
|
|
44
|
+
return dispatch();
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
this.release(sessionId, state);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Take the gate now, or queue for it behind the running section. */
|
|
51
|
+
async acquire(state, lane) {
|
|
52
|
+
if (!state.busy) {
|
|
53
|
+
state.busy = true;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// The waiter that is woken inherits the gate: `busy` stays true across the handoff, so an
|
|
57
|
+
// admission arriving during it queues instead of overtaking the waiter already promised the turn.
|
|
58
|
+
await new Promise(resolve => (lane === 'control' ? state.control : state.normal).push(resolve));
|
|
59
|
+
}
|
|
60
|
+
/** Hand the gate to the next waiter, control first, or free it. */
|
|
61
|
+
release(sessionId, state) {
|
|
62
|
+
const next = state.control.shift() ?? state.normal.shift();
|
|
63
|
+
if (next !== undefined) {
|
|
64
|
+
next();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
state.busy = false;
|
|
68
|
+
// A lane nobody is waiting for would otherwise stay behind for the life of the process, and long
|
|
69
|
+
// runs create a session per verification.
|
|
70
|
+
if (this.lanes.get(sessionId) === state)
|
|
71
|
+
this.lanes.delete(sessionId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -1,91 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
import { type ObjectValue } from '../
|
|
3
|
-
/** Parse workspace and resume navigation, including their long aliases. */
|
|
4
|
-
export declare function navigationCommand(value: string): {
|
|
5
|
-
kind: 'workspace' | 'session';
|
|
6
|
-
query?: string;
|
|
7
|
-
} | undefined;
|
|
8
|
-
/** Resolve the host's title projection, falling back to the session ID. */
|
|
9
|
-
export declare function sessionLabel(session: ObjectValue): string;
|
|
1
|
+
/** Unambiguous slash-command target resolution and host title projection. */
|
|
2
|
+
import { type ObjectValue } from '../json.ts';
|
|
10
3
|
/** Match an exact ID or name before a unique ID prefix; never choose an ambiguous target. */
|
|
11
4
|
export declare function resolveTarget(items: ObjectValue[], query: string, id: string, names: (item: ObjectValue) => string[]): ObjectValue;
|
|
12
|
-
/** User-visible activity of one session, most actionable first.
|
|
13
|
-
*
|
|
14
|
-
* `needs` is the only state that asks the user to do something, and it outranks the host's running
|
|
15
|
-
* flag because a turn waiting on an answer is running only in the mechanical sense. `blank` stays a
|
|
16
|
-
* marker for a session that never sent a turn, but it is not a status and never enters a rollup.
|
|
17
|
-
*/
|
|
18
|
-
export type SessionState = 'needs' | 'running' | 'idle' | 'blank';
|
|
19
|
-
/** Classify one session from the host's list summary; nothing is inferred from silence.
|
|
20
|
-
* @param session - Session summary from `session/list`.
|
|
21
|
-
* @param pending - Whether this client holds an unanswered interaction for that session.
|
|
22
|
-
* @returns Needs-you while an answer is owed, running while its agent works, otherwise idle or blank.
|
|
23
|
-
*/
|
|
24
|
-
export declare function sessionState(session: ObjectValue, pending?: boolean): SessionState;
|
|
25
|
-
/** Leading marker per state: a question mark, a working clock, a filled dot, and an unused circle. */
|
|
26
|
-
export declare const SESSION_MARKERS: Record<SessionState, string>;
|
|
27
|
-
/** One word per state, so every screen names the same state the same way. */
|
|
28
|
-
export declare const STATE_LABELS: Record<SessionState, string>;
|
|
29
|
-
/** Coarse age of a session's last activity, so the column stays steady between list refreshes.
|
|
30
|
-
* @param time - Epoch milliseconds of the last activity, when the summary reported one.
|
|
31
|
-
* @param now - Current epoch milliseconds.
|
|
32
|
-
* @returns `now`, minutes, hours or days.
|
|
33
|
-
*/
|
|
34
|
-
export declare function activityAge(time: number | undefined, now: number): string;
|
|
35
|
-
/** Status cell for one session row: its state marker and the age of its last activity.
|
|
36
|
-
* @param session - Session summary from `session/list`.
|
|
37
|
-
* @param now - Current epoch milliseconds.
|
|
38
|
-
* @param pending - Whether this client holds an unanswered interaction for that session.
|
|
39
|
-
* @returns Marker with an optional age, without a trailing space when unknown.
|
|
40
|
-
*/
|
|
41
|
-
export declare function sessionStatus(session: ObjectValue, now: number, pending?: boolean): string;
|
|
42
|
-
/** States a workspace rollup reports, most actionable first. */
|
|
43
|
-
export declare const ROLLUP_STATES: readonly ["needs", "running", "idle"];
|
|
44
|
-
/** One state a workspace rollup reports. */
|
|
45
|
-
export type RollupState = (typeof ROLLUP_STATES)[number];
|
|
46
|
-
/** One counted state of a workspace rollup. */
|
|
47
|
-
export interface RollupCount {
|
|
48
|
-
state: RollupState;
|
|
49
|
-
count: number;
|
|
50
|
-
}
|
|
51
|
-
/** How much room a rollup has for words. */
|
|
52
|
-
export type RollupStyle = 'words' | 'badges';
|
|
53
|
-
/** Count the sessions of one workspace by the state each reports.
|
|
54
|
-
*
|
|
55
|
-
* Blank sessions are counted by neither a badge nor a word: a session that never sent a turn is the
|
|
56
|
-
* absence of activity, and listing it beside real work only makes the rollup harder to read.
|
|
57
|
-
* @param sessions - Sessions whose `sessionIds` belong to the workspace.
|
|
58
|
-
* @param pending - Session IDs this client holds an unanswered interaction for.
|
|
59
|
-
* @returns One count per state that occurs, most actionable first, or an empty list.
|
|
60
|
-
*/
|
|
61
|
-
export declare function workspaceCounts(sessions: readonly ObjectValue[], pending?: ReadonlySet<string>): RollupCount[];
|
|
62
|
-
/** Render one rollup as separately coloured cells.
|
|
63
|
-
*
|
|
64
|
-
* Each cell after the first carries the separator that joins it to the previous one, so a caller can
|
|
65
|
-
* colour the cells independently without losing the text {@link workspaceStatus} would produce.
|
|
66
|
-
* @param counts - Counts from {@link workspaceCounts}.
|
|
67
|
-
* @param style - `words` spells each state out; `badges` keeps only the marker and the count.
|
|
68
|
-
* @returns The cells in the order given, with their separators.
|
|
69
|
-
*/
|
|
70
|
-
export declare function workspaceSegments(counts: readonly RollupCount[], style?: RollupStyle): {
|
|
71
|
-
state: RollupState;
|
|
72
|
-
text: string;
|
|
73
|
-
}[];
|
|
74
|
-
/** Render one rollup as plain text, the same way every screen and test reads it.
|
|
75
|
-
* @param counts - Counts from {@link workspaceCounts}.
|
|
76
|
-
* @param style - `words` spells each state out; `badges` keeps only the marker and the count.
|
|
77
|
-
* @returns The joined cell text, empty when nothing was counted.
|
|
78
|
-
*/
|
|
79
|
-
export declare function workspaceStatus(counts: readonly RollupCount[], style?: RollupStyle): string;
|
|
80
|
-
/** Marker key for the compact rollup, which has no room for the words. */
|
|
81
|
-
export declare const ROLLUP_LEGEND: string;
|
|
82
|
-
/** Secondary path text for one workspace row.
|
|
83
|
-
*
|
|
84
|
-
* The title is usually the last path segment, so repeating it wastes the row; the parent directory
|
|
85
|
-
* is what distinguishes two checkouts. A title that does not name the last segment keeps the full
|
|
86
|
-
* path, because dropping it would hide where the workspace actually lives.
|
|
87
|
-
* @param path - Registered host directory.
|
|
88
|
-
* @param title - Workspace title as the row already shows it.
|
|
89
|
-
* @returns The path to show beside the row, or an empty string when nothing is left.
|
|
90
|
-
*/
|
|
91
|
-
export declare function workspaceDetail(path: string, title: string): string;
|