@aiwayds/dsh-tui-pi 1.0.8 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/README.zh-CN.md +1 -0
- package/lib/attachments.d.ts +69 -0
- package/lib/attachments.js +110 -0
- package/lib/attachments.js.map +1 -0
- package/lib/btw-overlay.d.ts +55 -0
- package/lib/btw-overlay.js +153 -0
- package/lib/btw-overlay.js.map +1 -0
- package/lib/btw.d.ts +224 -0
- package/lib/btw.js +419 -0
- package/lib/btw.js.map +1 -0
- package/lib/clipboard.d.ts +10 -0
- package/lib/clipboard.js +13 -0
- package/lib/clipboard.js.map +1 -1
- package/lib/footer.d.ts +2 -1
- package/lib/footer.js +3 -1
- package/lib/footer.js.map +1 -1
- package/lib/index.js +94 -0
- package/lib/index.js.map +1 -1
- package/lib/messages.d.ts +2 -0
- package/lib/messages.js +24 -6
- package/lib/messages.js.map +1 -1
- package/lib/theme-settings.js +1 -0
- package/lib/theme-settings.js.map +1 -1
- package/lib/tui.d.ts +10 -0
- package/lib/tui.js +23 -1
- package/lib/tui.js.map +1 -1
- package/package.json +2 -2
package/lib/btw.d.ts
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /btw — by-the-way side questions (CONTEXT.md: Btw, Side call, Btw overlay,
|
|
3
|
+
* Last-btw slot, Queued btw).
|
|
4
|
+
*
|
|
5
|
+
* While the main agent is mid-turn, `/btw <question>` fires ONE tool-less
|
|
6
|
+
* one-shot model call over a read-only snapshot of the recent conversation
|
|
7
|
+
* and streams the answer into a framed overlay. The main line never sees it:
|
|
8
|
+
* nothing enters the session log, the inbox, or any main-line model request.
|
|
9
|
+
* The overlay is the only surface; closing it (Esc) does not stop the call —
|
|
10
|
+
* the finished exchange lands in the in-process Last-btw slot, reviewable
|
|
11
|
+
* via bare `/btw` until the process dies.
|
|
12
|
+
*
|
|
13
|
+
* Concurrency: at most one side call runs (BtwQueue); further submits queue
|
|
14
|
+
* behind it (bounded). Lifecycle: a main-line disruption — /new, /resume,
|
|
15
|
+
* a remote takeover, the stop gesture — cancels the running call and drops
|
|
16
|
+
* the queue; the caller wires `cancelAll` into those paths. Placement rationale:
|
|
17
|
+
* docs/adr/0001-btw-tui-owned-command.md.
|
|
18
|
+
*
|
|
19
|
+
* Split: pure decision layer here — arg parsing, snapshot assembly, the queue
|
|
20
|
+
* state machine, stream consumption and the controller, all over structural
|
|
21
|
+
* slices so the matrix runs without a terminal (test/btw.test.mjs). The
|
|
22
|
+
* overlay component + PanelHost glue live in btw-overlay.ts; the command
|
|
23
|
+
* wiring in index.ts.
|
|
24
|
+
*/
|
|
25
|
+
import { type Message, type ReasoningEffortId } from '@deepseek-ai/dsh-llm';
|
|
26
|
+
export declare const BTW_IDLE_NOTICE = "/btw answers alongside a running turn \u2014 the main line is idle, so just ask directly.";
|
|
27
|
+
export declare const BTW_USAGE: string;
|
|
28
|
+
export type ParsedBtwInput = {
|
|
29
|
+
kind: 'empty';
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'ok';
|
|
32
|
+
question: string;
|
|
33
|
+
modelOverride?: string;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'error';
|
|
36
|
+
error: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Parse the text after `/btw`. Everything is the question except one
|
|
40
|
+
* optional `--model provider/model` override (extractable from anywhere in
|
|
41
|
+
* the line). No input at all → `'empty'` (review / usage hint); a `--model`
|
|
42
|
+
* flag without a question, or a value without a `/`, → `'error'`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseBtwInput(rawInput: string | undefined): ParsedBtwInput;
|
|
45
|
+
/** Structural input event — the subset of SessionEvent the snapshot needs. */
|
|
46
|
+
export interface BtwSnapshotEvent {
|
|
47
|
+
readonly type: string;
|
|
48
|
+
readonly data: unknown;
|
|
49
|
+
}
|
|
50
|
+
/** Default recent-conversation messages carried into a side call. */
|
|
51
|
+
export declare const BTW_SNAPSHOT_DEFAULT_MESSAGES = 6;
|
|
52
|
+
/** Hard ceiling for the configurable snapshot size (env override clamp). */
|
|
53
|
+
export declare const BTW_SNAPSHOT_MAX_MESSAGES = 50;
|
|
54
|
+
/** Per-message text cap — the snapshot is context, not a transcript replay. */
|
|
55
|
+
export declare const BTW_MAX_MESSAGE_CHARS = 4000;
|
|
56
|
+
/**
|
|
57
|
+
* Resolve the snapshot size from the `DSH_TUI_BTW_CONTEXT_MESSAGES` env value:
|
|
58
|
+
* integer clamped to [0, BTW_SNAPSHOT_MAX_MESSAGES], anything else (unset,
|
|
59
|
+
* non-numeric) falls back to the default. 0 disables the snapshot — the side
|
|
60
|
+
* call then answers from the question alone.
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveSnapshotLimit(env: string | undefined): number;
|
|
63
|
+
/**
|
|
64
|
+
* Project the last `limit` user/assistant text exchanges out of the session
|
|
65
|
+
* event log, oldest first. Tool calls, reasoning, usage and every
|
|
66
|
+
* non-message event are dropped; text-only fresh blocks keep each source
|
|
67
|
+
* message's identity (id/role/source) while shedding everything a tool-less
|
|
68
|
+
* call cannot use. Malformed event data is skipped, never thrown.
|
|
69
|
+
*/
|
|
70
|
+
export declare function buildBtwSnapshot(events: readonly BtwSnapshotEvent[], limit: number): Message[];
|
|
71
|
+
/**
|
|
72
|
+
* The side-call message list: the snapshot in order, then the question as a
|
|
73
|
+
* plugin-sourced user message (it is not a real user turn of any session).
|
|
74
|
+
*/
|
|
75
|
+
export declare function buildBtwMessages(snapshot: readonly Message[], question: string): Message[];
|
|
76
|
+
/** System prompt for the side call — no tools, snapshot is context only. */
|
|
77
|
+
export declare const BTW_SYSTEM_PROMPT: string;
|
|
78
|
+
/** Maximum queued btw requests while one is running. */
|
|
79
|
+
export declare const BTW_QUEUE_CAP = 5;
|
|
80
|
+
export interface BtwJob {
|
|
81
|
+
readonly question: string;
|
|
82
|
+
readonly modelOverride?: string;
|
|
83
|
+
}
|
|
84
|
+
export type BtwSubmitResult = {
|
|
85
|
+
kind: 'started';
|
|
86
|
+
} | {
|
|
87
|
+
kind: 'queued';
|
|
88
|
+
position: number;
|
|
89
|
+
} | {
|
|
90
|
+
kind: 'rejected';
|
|
91
|
+
reason: string;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Single-flight queue: one running btw, bounded FIFO behind it. The queue is
|
|
95
|
+
* the concurrency truth — the controller's view state is derived from it.
|
|
96
|
+
*/
|
|
97
|
+
export declare class BtwQueue {
|
|
98
|
+
private currentJob;
|
|
99
|
+
private readonly waiting;
|
|
100
|
+
get running(): boolean;
|
|
101
|
+
get queuedCount(): number;
|
|
102
|
+
submit(job: BtwJob): BtwSubmitResult;
|
|
103
|
+
/** Settle the current job; returns the next job to launch, if any. */
|
|
104
|
+
finishCurrent(): BtwJob | undefined;
|
|
105
|
+
cancelAll(): {
|
|
106
|
+
canceledRunning: boolean;
|
|
107
|
+
canceledQueued: number;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Structural stream chunk — the subset of dsh-llm's StreamChunk a side call
|
|
112
|
+
* consumes (text deltas for the live view, the finish chunk for the outcome).
|
|
113
|
+
*/
|
|
114
|
+
export interface BtwStreamChunk {
|
|
115
|
+
readonly type: string;
|
|
116
|
+
readonly text?: string;
|
|
117
|
+
readonly reason?: {
|
|
118
|
+
readonly kind: string;
|
|
119
|
+
readonly failure?: {
|
|
120
|
+
readonly message?: string;
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export interface BtwCallOptions {
|
|
125
|
+
provider: string;
|
|
126
|
+
model: string;
|
|
127
|
+
reasoningEffort?: ReasoningEffortId;
|
|
128
|
+
messages: Message[];
|
|
129
|
+
system: string;
|
|
130
|
+
signal?: AbortSignal;
|
|
131
|
+
}
|
|
132
|
+
export type BtwStreamFn = (options: BtwCallOptions) => AsyncIterable<BtwStreamChunk>;
|
|
133
|
+
export type BtwFinish = {
|
|
134
|
+
kind: 'stop';
|
|
135
|
+
answer: string;
|
|
136
|
+
} | {
|
|
137
|
+
kind: 'aborted';
|
|
138
|
+
} | {
|
|
139
|
+
kind: 'error';
|
|
140
|
+
message: string;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Drain one side-call stream: forward text deltas as they arrive, map the
|
|
144
|
+
* finish chunk (or the stream's end / a throw) to a terminal outcome. A
|
|
145
|
+
* stream that ends without a finish chunk is an error, never a silent
|
|
146
|
+
* success; an aborted signal wins over whatever the iterator does next.
|
|
147
|
+
*/
|
|
148
|
+
export declare function consumeBtwStream(chunks: AsyncIterable<BtwStreamChunk>, onDelta: (text: string) => void, signal?: AbortSignal): Promise<BtwFinish>;
|
|
149
|
+
export type BtwRunStatus = 'streaming' | 'done' | 'error' | 'canceled';
|
|
150
|
+
/** View state of the running (or just-finished) side call; overlay-facing. */
|
|
151
|
+
export interface BtwRunState {
|
|
152
|
+
readonly question: string;
|
|
153
|
+
readonly modelLabel: string;
|
|
154
|
+
status: BtwRunStatus;
|
|
155
|
+
answerText: string;
|
|
156
|
+
error?: string;
|
|
157
|
+
}
|
|
158
|
+
/** The Last-btw slot — the in-process record of the most recent exchange. */
|
|
159
|
+
export interface BtwLastExchange {
|
|
160
|
+
readonly question: string;
|
|
161
|
+
readonly answer: string;
|
|
162
|
+
readonly modelLabel: string;
|
|
163
|
+
}
|
|
164
|
+
export type BtwNoticeKind = 'info' | 'error' | 'warning';
|
|
165
|
+
export interface BtwSelection {
|
|
166
|
+
provider: string;
|
|
167
|
+
model: string;
|
|
168
|
+
reasoningEffort?: ReasoningEffortId;
|
|
169
|
+
}
|
|
170
|
+
export interface BtwControllerDeps {
|
|
171
|
+
stream: BtwStreamFn;
|
|
172
|
+
resolveSelection: () => BtwSelection | undefined;
|
|
173
|
+
buildSnapshot: () => readonly Message[];
|
|
174
|
+
requestRender: () => void;
|
|
175
|
+
notify: (message: string, kind: BtwNoticeKind) => void;
|
|
176
|
+
/** The glue opens (or swaps) the overlay for a launched run. */
|
|
177
|
+
onRunStarted: (run: BtwRunState) => void;
|
|
178
|
+
/** cancelAll: the glue closes the overlay (the run is gone either way). */
|
|
179
|
+
onOverlayRequestedClose: () => void;
|
|
180
|
+
/**
|
|
181
|
+
* Whether a capturing surface (overlay / docked ask-user panel) currently
|
|
182
|
+
* owns the keyboard. Queue-drained launches under one skip the popup and
|
|
183
|
+
* run into the slot instead — the answer is reviewable via bare /btw.
|
|
184
|
+
*/
|
|
185
|
+
hasCapturingSurface?: () => boolean;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Owns the btw concurrency, the side-call execution and the Last-btw slot.
|
|
189
|
+
* The overlay glue (btw-overlay.ts) renders `currentRun` / `last` on every
|
|
190
|
+
* frame and reports overlay open/close back through `setOverlayOpen`.
|
|
191
|
+
*/
|
|
192
|
+
export declare class BtwController {
|
|
193
|
+
private readonly deps;
|
|
194
|
+
private readonly queue;
|
|
195
|
+
private abortController;
|
|
196
|
+
private run;
|
|
197
|
+
private lastExchange;
|
|
198
|
+
private overlayOpen;
|
|
199
|
+
constructor(deps: BtwControllerDeps);
|
|
200
|
+
/** View state for the overlay: the live/just-settled run wins over the slot. */
|
|
201
|
+
get currentRun(): BtwRunState | undefined;
|
|
202
|
+
get last(): BtwLastExchange | undefined;
|
|
203
|
+
get queuedCount(): number;
|
|
204
|
+
/** The glue reports overlay lifecycle so error notices know where to land. */
|
|
205
|
+
setOverlayOpen(open: boolean): void;
|
|
206
|
+
submit(job: BtwJob): BtwSubmitResult;
|
|
207
|
+
/**
|
|
208
|
+
* Bare `/btw`: an active run reopens live on its overlay; otherwise the
|
|
209
|
+
* Last-btw slot is shown; nothing at all → the caller shows the usage.
|
|
210
|
+
*/
|
|
211
|
+
openReview(): 'live' | 'review' | 'empty';
|
|
212
|
+
/**
|
|
213
|
+
* Main-line disruption (/new, /resume, remote takeover, stop gesture):
|
|
214
|
+
* abort the running call, drop the queue, close the overlay. The slot is
|
|
215
|
+
* untouched — a canceled run never overwrites the last completed one.
|
|
216
|
+
*/
|
|
217
|
+
cancelAll(): void;
|
|
218
|
+
/** TUI teardown: same as cancelAll without the overlay ceremony. */
|
|
219
|
+
dispose(): void;
|
|
220
|
+
private launch;
|
|
221
|
+
private execute;
|
|
222
|
+
/** Settle the queue slot; a queued job (if any) launches immediately. */
|
|
223
|
+
private drain;
|
|
224
|
+
}
|
package/lib/btw.js
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /btw — by-the-way side questions (CONTEXT.md: Btw, Side call, Btw overlay,
|
|
3
|
+
* Last-btw slot, Queued btw).
|
|
4
|
+
*
|
|
5
|
+
* While the main agent is mid-turn, `/btw <question>` fires ONE tool-less
|
|
6
|
+
* one-shot model call over a read-only snapshot of the recent conversation
|
|
7
|
+
* and streams the answer into a framed overlay. The main line never sees it:
|
|
8
|
+
* nothing enters the session log, the inbox, or any main-line model request.
|
|
9
|
+
* The overlay is the only surface; closing it (Esc) does not stop the call —
|
|
10
|
+
* the finished exchange lands in the in-process Last-btw slot, reviewable
|
|
11
|
+
* via bare `/btw` until the process dies.
|
|
12
|
+
*
|
|
13
|
+
* Concurrency: at most one side call runs (BtwQueue); further submits queue
|
|
14
|
+
* behind it (bounded). Lifecycle: a main-line disruption — /new, /resume,
|
|
15
|
+
* a remote takeover, the stop gesture — cancels the running call and drops
|
|
16
|
+
* the queue; the caller wires `cancelAll` into those paths. Placement rationale:
|
|
17
|
+
* docs/adr/0001-btw-tui-owned-command.md.
|
|
18
|
+
*
|
|
19
|
+
* Split: pure decision layer here — arg parsing, snapshot assembly, the queue
|
|
20
|
+
* state machine, stream consumption and the controller, all over structural
|
|
21
|
+
* slices so the matrix runs without a terminal (test/btw.test.mjs). The
|
|
22
|
+
* overlay component + PanelHost glue live in btw-overlay.ts; the command
|
|
23
|
+
* wiring in index.ts.
|
|
24
|
+
*/
|
|
25
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
26
|
+
// ------------------------------------------------------------ UI copy (English-only, AGENTS.md #4) --
|
|
27
|
+
export const BTW_IDLE_NOTICE = '/btw answers alongside a running turn — the main line is idle, so just ask directly.';
|
|
28
|
+
export const BTW_USAGE = 'Usage: /btw <question> — ask a side question while the main task runs. ' +
|
|
29
|
+
'The answer streams into a temporary overlay and is not kept in the session.';
|
|
30
|
+
/**
|
|
31
|
+
* Parse the text after `/btw`. Everything is the question except one
|
|
32
|
+
* optional `--model provider/model` override (extractable from anywhere in
|
|
33
|
+
* the line). No input at all → `'empty'` (review / usage hint); a `--model`
|
|
34
|
+
* flag without a question, or a value without a `/`, → `'error'`.
|
|
35
|
+
*/
|
|
36
|
+
export function parseBtwInput(rawInput) {
|
|
37
|
+
const raw = (rawInput ?? '').trim();
|
|
38
|
+
if (raw === '')
|
|
39
|
+
return { kind: 'empty' };
|
|
40
|
+
let modelOverride;
|
|
41
|
+
let question = raw;
|
|
42
|
+
const match = /(?:^|\s)--model\s+(\S+)/.exec(raw);
|
|
43
|
+
if (match !== null) {
|
|
44
|
+
modelOverride = match[1];
|
|
45
|
+
question = `${raw.slice(0, match.index)} ${raw.slice(match.index + match[0].length)}`.trim();
|
|
46
|
+
}
|
|
47
|
+
if (question === '')
|
|
48
|
+
return { kind: 'error', error: 'No question after /btw --model.' };
|
|
49
|
+
if (modelOverride !== undefined && !modelOverride.includes('/')) {
|
|
50
|
+
return { kind: 'error', error: `Invalid --model "${modelOverride}" — expected provider/model.` };
|
|
51
|
+
}
|
|
52
|
+
// The override key is omitted (not undefined-valued) when absent.
|
|
53
|
+
return modelOverride === undefined
|
|
54
|
+
? { kind: 'ok', question }
|
|
55
|
+
: { kind: 'ok', question, modelOverride };
|
|
56
|
+
}
|
|
57
|
+
/** Default recent-conversation messages carried into a side call. */
|
|
58
|
+
export const BTW_SNAPSHOT_DEFAULT_MESSAGES = 6;
|
|
59
|
+
/** Hard ceiling for the configurable snapshot size (env override clamp). */
|
|
60
|
+
export const BTW_SNAPSHOT_MAX_MESSAGES = 50;
|
|
61
|
+
/** Per-message text cap — the snapshot is context, not a transcript replay. */
|
|
62
|
+
export const BTW_MAX_MESSAGE_CHARS = 4000;
|
|
63
|
+
const TRUNCATION_SUFFIX = '\n…[truncated]';
|
|
64
|
+
/**
|
|
65
|
+
* Resolve the snapshot size from the `DSH_TUI_BTW_CONTEXT_MESSAGES` env value:
|
|
66
|
+
* integer clamped to [0, BTW_SNAPSHOT_MAX_MESSAGES], anything else (unset,
|
|
67
|
+
* non-numeric) falls back to the default. 0 disables the snapshot — the side
|
|
68
|
+
* call then answers from the question alone.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveSnapshotLimit(env) {
|
|
71
|
+
const parsed = Number.parseInt(env ?? '', 10);
|
|
72
|
+
if (Number.isNaN(parsed))
|
|
73
|
+
return BTW_SNAPSHOT_DEFAULT_MESSAGES;
|
|
74
|
+
return Math.min(Math.max(parsed, 0), BTW_SNAPSHOT_MAX_MESSAGES);
|
|
75
|
+
}
|
|
76
|
+
/** Join an unknown message `content` into plain text (text blocks only). */
|
|
77
|
+
function textOf(content) {
|
|
78
|
+
if (!Array.isArray(content))
|
|
79
|
+
return '';
|
|
80
|
+
const parts = [];
|
|
81
|
+
for (const block of content) {
|
|
82
|
+
if (block !== null && typeof block === 'object' &&
|
|
83
|
+
block.type === 'text' &&
|
|
84
|
+
typeof block.text === 'string') {
|
|
85
|
+
parts.push(block.text);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return parts.join('\n');
|
|
89
|
+
}
|
|
90
|
+
function clipMessage(text) {
|
|
91
|
+
return text.length > BTW_MAX_MESSAGE_CHARS
|
|
92
|
+
? text.slice(0, BTW_MAX_MESSAGE_CHARS) + TRUNCATION_SUFFIX
|
|
93
|
+
: text;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Project the last `limit` user/assistant text exchanges out of the session
|
|
97
|
+
* event log, oldest first. Tool calls, reasoning, usage and every
|
|
98
|
+
* non-message event are dropped; text-only fresh blocks keep each source
|
|
99
|
+
* message's identity (id/role/source) while shedding everything a tool-less
|
|
100
|
+
* call cannot use. Malformed event data is skipped, never thrown.
|
|
101
|
+
*/
|
|
102
|
+
export function buildBtwSnapshot(events, limit) {
|
|
103
|
+
const picked = [];
|
|
104
|
+
if (limit <= 0)
|
|
105
|
+
return picked;
|
|
106
|
+
for (let index = events.length - 1; index >= 0 && picked.length < limit; index -= 1) {
|
|
107
|
+
const event = events[index];
|
|
108
|
+
if (event?.type !== 'user/message' && event?.type !== 'assistant/message')
|
|
109
|
+
continue;
|
|
110
|
+
const data = event.data;
|
|
111
|
+
if (data === null || typeof data !== 'object')
|
|
112
|
+
continue;
|
|
113
|
+
const content = event.type === 'user/message' ? data.content : data.message?.content;
|
|
114
|
+
const text = textOf(content).trim();
|
|
115
|
+
if (text === '')
|
|
116
|
+
continue;
|
|
117
|
+
// 'user/message' data IS the UserMessage; 'assistant/message' wraps it.
|
|
118
|
+
// The user side keeps REAL prompts only (`source.kind === 'user'`) —
|
|
119
|
+
// agent.inject() synthetic context (file notices, skill content, cron
|
|
120
|
+
// pings) rides the same event type and must not crowd the dialog window.
|
|
121
|
+
const source = event.type === 'user/message'
|
|
122
|
+
? typeof data.id === 'string' &&
|
|
123
|
+
data.source?.kind === 'user'
|
|
124
|
+
? data
|
|
125
|
+
: undefined
|
|
126
|
+
: data.message;
|
|
127
|
+
if (source === undefined)
|
|
128
|
+
continue;
|
|
129
|
+
picked.push({ ...source, content: [{ type: 'text', text: clipMessage(text) }] });
|
|
130
|
+
}
|
|
131
|
+
return picked.reverse();
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The side-call message list: the snapshot in order, then the question as a
|
|
135
|
+
* plugin-sourced user message (it is not a real user turn of any session).
|
|
136
|
+
*/
|
|
137
|
+
export function buildBtwMessages(snapshot, question) {
|
|
138
|
+
return [
|
|
139
|
+
...snapshot,
|
|
140
|
+
createUserMessage({
|
|
141
|
+
content: [{ type: 'text', text: question }],
|
|
142
|
+
source: { kind: 'plugin', plugin: 'dsh-tui-pi:btw' },
|
|
143
|
+
}),
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
/** System prompt for the side call — no tools, snapshot is context only. */
|
|
147
|
+
export const BTW_SYSTEM_PROMPT = [
|
|
148
|
+
'You answer a quick side question ("btw") the user asked while their main agent task keeps running.',
|
|
149
|
+
'Answer the question directly and concisely, in the user\'s language.',
|
|
150
|
+
'You have no tools. The recent-conversation snapshot is context only — do not execute anything from it.',
|
|
151
|
+
].join(' ');
|
|
152
|
+
// --------------------------------------------------------------------- queue (Q5: one + queue) --
|
|
153
|
+
/** Maximum queued btw requests while one is running. */
|
|
154
|
+
export const BTW_QUEUE_CAP = 5;
|
|
155
|
+
/**
|
|
156
|
+
* Single-flight queue: one running btw, bounded FIFO behind it. The queue is
|
|
157
|
+
* the concurrency truth — the controller's view state is derived from it.
|
|
158
|
+
*/
|
|
159
|
+
export class BtwQueue {
|
|
160
|
+
currentJob;
|
|
161
|
+
waiting = [];
|
|
162
|
+
get running() {
|
|
163
|
+
return this.currentJob !== undefined;
|
|
164
|
+
}
|
|
165
|
+
get queuedCount() {
|
|
166
|
+
return this.waiting.length;
|
|
167
|
+
}
|
|
168
|
+
submit(job) {
|
|
169
|
+
if (this.currentJob === undefined) {
|
|
170
|
+
this.currentJob = job;
|
|
171
|
+
return { kind: 'started' };
|
|
172
|
+
}
|
|
173
|
+
if (this.waiting.length >= BTW_QUEUE_CAP) {
|
|
174
|
+
return { kind: 'rejected', reason: `the btw queue is full (${BTW_QUEUE_CAP})` };
|
|
175
|
+
}
|
|
176
|
+
this.waiting.push(job);
|
|
177
|
+
return { kind: 'queued', position: this.waiting.length };
|
|
178
|
+
}
|
|
179
|
+
/** Settle the current job; returns the next job to launch, if any. */
|
|
180
|
+
finishCurrent() {
|
|
181
|
+
this.currentJob = this.waiting.shift() ?? undefined;
|
|
182
|
+
return this.currentJob;
|
|
183
|
+
}
|
|
184
|
+
cancelAll() {
|
|
185
|
+
const canceledRunning = this.currentJob !== undefined;
|
|
186
|
+
const canceledQueued = this.waiting.length;
|
|
187
|
+
this.currentJob = undefined;
|
|
188
|
+
this.waiting.length = 0;
|
|
189
|
+
return { canceledRunning, canceledQueued };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Drain one side-call stream: forward text deltas as they arrive, map the
|
|
194
|
+
* finish chunk (or the stream's end / a throw) to a terminal outcome. A
|
|
195
|
+
* stream that ends without a finish chunk is an error, never a silent
|
|
196
|
+
* success; an aborted signal wins over whatever the iterator does next.
|
|
197
|
+
*/
|
|
198
|
+
export async function consumeBtwStream(chunks, onDelta, signal) {
|
|
199
|
+
let answer = '';
|
|
200
|
+
try {
|
|
201
|
+
for await (const chunk of chunks) {
|
|
202
|
+
if (signal?.aborted)
|
|
203
|
+
return { kind: 'aborted' };
|
|
204
|
+
if (chunk.type === 'text-delta' && typeof chunk.text === 'string' && chunk.text !== '') {
|
|
205
|
+
answer += chunk.text;
|
|
206
|
+
onDelta(chunk.text);
|
|
207
|
+
}
|
|
208
|
+
else if (chunk.type === 'finish') {
|
|
209
|
+
switch (chunk.reason?.kind) {
|
|
210
|
+
case 'stop':
|
|
211
|
+
return { kind: 'stop', answer };
|
|
212
|
+
case 'aborted':
|
|
213
|
+
return { kind: 'aborted' };
|
|
214
|
+
case 'error':
|
|
215
|
+
return { kind: 'error', message: chunk.reason.failure?.message ?? 'Unknown model stream error.' };
|
|
216
|
+
case 'max-tokens':
|
|
217
|
+
return { kind: 'error', message: 'The answer hit the output token cap.' };
|
|
218
|
+
case 'tool-calls':
|
|
219
|
+
return { kind: 'error', message: 'The side model unexpectedly requested tools.' };
|
|
220
|
+
default:
|
|
221
|
+
return { kind: 'error', message: `Unsupported stream finish: ${String(chunk.reason?.kind)}` };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { kind: 'error', message: 'The model stream ended without a finish chunk.' };
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
if (signal?.aborted)
|
|
229
|
+
return { kind: 'aborted' };
|
|
230
|
+
return { kind: 'error', message: error instanceof Error ? error.message : String(error) };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Owns the btw concurrency, the side-call execution and the Last-btw slot.
|
|
235
|
+
* The overlay glue (btw-overlay.ts) renders `currentRun` / `last` on every
|
|
236
|
+
* frame and reports overlay open/close back through `setOverlayOpen`.
|
|
237
|
+
*/
|
|
238
|
+
export class BtwController {
|
|
239
|
+
deps;
|
|
240
|
+
queue = new BtwQueue();
|
|
241
|
+
abortController;
|
|
242
|
+
run;
|
|
243
|
+
lastExchange;
|
|
244
|
+
overlayOpen = false;
|
|
245
|
+
constructor(deps) {
|
|
246
|
+
this.deps = deps;
|
|
247
|
+
}
|
|
248
|
+
/** View state for the overlay: the live/just-settled run wins over the slot. */
|
|
249
|
+
get currentRun() {
|
|
250
|
+
return this.run;
|
|
251
|
+
}
|
|
252
|
+
get last() {
|
|
253
|
+
return this.lastExchange;
|
|
254
|
+
}
|
|
255
|
+
get queuedCount() {
|
|
256
|
+
return this.queue.queuedCount;
|
|
257
|
+
}
|
|
258
|
+
/** The glue reports overlay lifecycle so error notices know where to land. */
|
|
259
|
+
setOverlayOpen(open) {
|
|
260
|
+
this.overlayOpen = open;
|
|
261
|
+
// A settled run whose only surface closed has no further viewer — prune
|
|
262
|
+
// it so a later render never resurrects a stale answer. A streaming run
|
|
263
|
+
// must survive the close (it delivers into the slot on settle).
|
|
264
|
+
if (!open && this.run !== undefined && this.run.status !== 'streaming') {
|
|
265
|
+
this.run = undefined;
|
|
266
|
+
this.deps.requestRender();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
submit(job) {
|
|
270
|
+
const outcome = this.queue.submit(job);
|
|
271
|
+
if (outcome.kind === 'started')
|
|
272
|
+
this.launch(job);
|
|
273
|
+
return outcome;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Bare `/btw`: an active run reopens live on its overlay; otherwise the
|
|
277
|
+
* Last-btw slot is shown; nothing at all → the caller shows the usage.
|
|
278
|
+
*/
|
|
279
|
+
openReview() {
|
|
280
|
+
if (this.run !== undefined) {
|
|
281
|
+
this.deps.onRunStarted(this.run);
|
|
282
|
+
return 'live';
|
|
283
|
+
}
|
|
284
|
+
if (this.lastExchange === undefined)
|
|
285
|
+
return 'empty';
|
|
286
|
+
this.deps.onRunStarted({
|
|
287
|
+
question: this.lastExchange.question,
|
|
288
|
+
modelLabel: this.lastExchange.modelLabel,
|
|
289
|
+
status: 'done',
|
|
290
|
+
answerText: this.lastExchange.answer,
|
|
291
|
+
});
|
|
292
|
+
return 'review';
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Main-line disruption (/new, /resume, remote takeover, stop gesture):
|
|
296
|
+
* abort the running call, drop the queue, close the overlay. The slot is
|
|
297
|
+
* untouched — a canceled run never overwrites the last completed one.
|
|
298
|
+
*/
|
|
299
|
+
cancelAll() {
|
|
300
|
+
const { canceledRunning, canceledQueued } = this.queue.cancelAll();
|
|
301
|
+
this.abortController?.abort();
|
|
302
|
+
this.abortController = undefined;
|
|
303
|
+
this.run = undefined;
|
|
304
|
+
if (canceledRunning || canceledQueued > 0)
|
|
305
|
+
this.deps.onOverlayRequestedClose();
|
|
306
|
+
}
|
|
307
|
+
/** TUI teardown: same as cancelAll without the overlay ceremony. */
|
|
308
|
+
dispose() {
|
|
309
|
+
this.queue.cancelAll();
|
|
310
|
+
this.abortController?.abort();
|
|
311
|
+
this.abortController = undefined;
|
|
312
|
+
this.run = undefined;
|
|
313
|
+
this.overlayOpen = false;
|
|
314
|
+
}
|
|
315
|
+
launch(job, promoted = false) {
|
|
316
|
+
const selection = job.modelOverride === undefined
|
|
317
|
+
? this.deps.resolveSelection()
|
|
318
|
+
: resolveOverride(job.modelOverride);
|
|
319
|
+
if (selection === undefined) {
|
|
320
|
+
this.deps.notify(job.modelOverride === undefined
|
|
321
|
+
? 'No model selected — btw cannot run.'
|
|
322
|
+
: `btw model "${job.modelOverride}" is not available.`, 'error');
|
|
323
|
+
this.drain();
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const run = {
|
|
327
|
+
question: job.question,
|
|
328
|
+
modelLabel: `${selection.provider}/${selection.model}`,
|
|
329
|
+
status: 'streaming',
|
|
330
|
+
answerText: '',
|
|
331
|
+
};
|
|
332
|
+
this.run = run;
|
|
333
|
+
const abort = new AbortController();
|
|
334
|
+
this.abortController = abort;
|
|
335
|
+
if (promoted && this.deps.hasCapturingSurface?.() === true) {
|
|
336
|
+
// A drained job popping up under an active dialog/overlay would steal
|
|
337
|
+
// the keyboard mid-flow — run into the slot instead; /btw reopens it.
|
|
338
|
+
this.deps.notify('btw running in the background — /btw to view the answer.', 'info');
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
this.deps.onRunStarted(run);
|
|
342
|
+
}
|
|
343
|
+
void this.execute(run, job, selection, abort.signal);
|
|
344
|
+
}
|
|
345
|
+
async execute(run, job, selection, signal) {
|
|
346
|
+
let finish;
|
|
347
|
+
try {
|
|
348
|
+
const messages = buildBtwMessages(this.deps.buildSnapshot(), job.question);
|
|
349
|
+
finish = await consumeBtwStream(this.deps.stream({
|
|
350
|
+
provider: selection.provider,
|
|
351
|
+
model: selection.model,
|
|
352
|
+
...(selection.reasoningEffort === undefined ? {} : { reasoningEffort: selection.reasoningEffort }),
|
|
353
|
+
messages,
|
|
354
|
+
system: BTW_SYSTEM_PROMPT,
|
|
355
|
+
signal,
|
|
356
|
+
}), delta => {
|
|
357
|
+
run.answerText += delta;
|
|
358
|
+
this.deps.requestRender();
|
|
359
|
+
}, signal);
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
// deps.stream throws synchronously (service missing / bad route) —
|
|
363
|
+
// same terminal path as a stream error, never an unhandled rejection.
|
|
364
|
+
finish = { kind: 'error', message: error instanceof Error ? error.message : String(error) };
|
|
365
|
+
}
|
|
366
|
+
// A cancelAll between launch and settle already reaped the run — never
|
|
367
|
+
// resurrect it (identity guard, steer-flow's flush-time liveness check).
|
|
368
|
+
if (this.run !== run)
|
|
369
|
+
return;
|
|
370
|
+
this.abortController = undefined;
|
|
371
|
+
if (finish.kind === 'stop') {
|
|
372
|
+
run.status = 'done';
|
|
373
|
+
// consumeBtwStream's accumulation and the onDelta mirror are the same
|
|
374
|
+
// sequence — the returned answer is the single source of truth.
|
|
375
|
+
this.lastExchange = {
|
|
376
|
+
question: run.question,
|
|
377
|
+
answer: finish.answer,
|
|
378
|
+
modelLabel: run.modelLabel,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
else if (finish.kind === 'aborted') {
|
|
382
|
+
run.status = 'canceled';
|
|
383
|
+
this.run = undefined;
|
|
384
|
+
this.deps.requestRender();
|
|
385
|
+
// Normally unreachable: aborts come from cancelAll/dispose, which reap
|
|
386
|
+
// the run first (the identity guard above returns). But an adapter may
|
|
387
|
+
// report an aborted finish on its own — never strand the queue on it.
|
|
388
|
+
this.drain();
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
run.status = 'error';
|
|
393
|
+
run.error = finish.message;
|
|
394
|
+
// No surface to show the failure on — the footer notice is the only
|
|
395
|
+
// channel left (the overlay path renders it in place).
|
|
396
|
+
if (!this.overlayOpen)
|
|
397
|
+
this.deps.notify(`btw failed: ${finish.message}`, 'error');
|
|
398
|
+
}
|
|
399
|
+
this.deps.requestRender();
|
|
400
|
+
this.drain();
|
|
401
|
+
}
|
|
402
|
+
/** Settle the queue slot; a queued job (if any) launches immediately. */
|
|
403
|
+
drain() {
|
|
404
|
+
const next = this.queue.finishCurrent();
|
|
405
|
+
if (next !== undefined)
|
|
406
|
+
this.launch(next, true);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Parse a `provider/model` override. Returns undefined when either half is
|
|
411
|
+
* empty — the caller surfaces the rejection; no throwing across the queue.
|
|
412
|
+
*/
|
|
413
|
+
function resolveOverride(modelOverride) {
|
|
414
|
+
const slash = modelOverride.indexOf('/');
|
|
415
|
+
if (slash <= 0 || slash === modelOverride.length - 1)
|
|
416
|
+
return undefined;
|
|
417
|
+
return { provider: modelOverride.slice(0, slash), model: modelOverride.slice(slash + 1) };
|
|
418
|
+
}
|
|
419
|
+
//# sourceMappingURL=btw.js.map
|
package/lib/btw.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"btw.js","sourceRoot":"","sources":["../src/btw.ts"],"names":[],"mappings":"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"}
|
package/lib/clipboard.d.ts
CHANGED
|
@@ -112,3 +112,13 @@ export declare function readClipboard(impl?: ClipboardImpl): Promise<string | nu
|
|
|
112
112
|
* full from a previous run; the seam keeps the test order independent.
|
|
113
113
|
*/
|
|
114
114
|
export declare function __resetClipboardUnavailableForTest(): void;
|
|
115
|
+
/** Environment variable selecting fullscreen copy-on-select (`DSH_TUI_COPY_ON_SELECT`). */
|
|
116
|
+
export declare const COPY_ON_SELECT_ENV = "DSH_TUI_COPY_ON_SELECT";
|
|
117
|
+
/**
|
|
118
|
+
* Resolve whether releasing a fullscreen mouse selection copies it to the
|
|
119
|
+
* clipboard (pi-tui `TuiAltScreen` `copyOnSelect`). Enabled by default — the
|
|
120
|
+
* same trade-off pi-tui picks and the behavior the plugin always had — so an
|
|
121
|
+
* unset/invalid value must never accidentally disable the feature; only an
|
|
122
|
+
* explicit off value (`0`/`false`/`off`) turns it off.
|
|
123
|
+
*/
|
|
124
|
+
export declare function resolveCopyOnSelect(env?: NodeJS.ProcessEnv): boolean;
|