@antst/dashi 0.1.0-alpha.5
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/LICENSE +21 -0
- package/README.md +25 -0
- package/lib/application.d.ts +43 -0
- package/lib/application.js +260 -0
- package/lib/at-trigger.d.ts +8 -0
- package/lib/at-trigger.js +13 -0
- package/lib/catalogs.d.ts +23 -0
- package/lib/catalogs.js +218 -0
- package/lib/clipboard-image.d.ts +3 -0
- package/lib/clipboard-image.js +74 -0
- package/lib/completion-trigger.d.ts +2 -0
- package/lib/completion-trigger.js +7 -0
- package/lib/external-editor.d.ts +2 -0
- package/lib/external-editor.js +39 -0
- package/lib/history-records.d.ts +4 -0
- package/lib/history-records.js +14 -0
- package/lib/image-input.d.ts +8 -0
- package/lib/image-input.js +37 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +250 -0
- package/lib/input.d.ts +10 -0
- package/lib/input.js +220 -0
- package/lib/mouse-input.d.ts +1 -0
- package/lib/mouse-input.js +6 -0
- package/lib/presentation.d.ts +9 -0
- package/lib/presentation.js +54 -0
- package/lib/renderer.d.ts +19 -0
- package/lib/renderer.js +414 -0
- package/lib/rewind.d.ts +18 -0
- package/lib/rewind.js +84 -0
- package/lib/session-export.d.ts +3 -0
- package/lib/session-export.js +27 -0
- package/lib/session-list.d.ts +15 -0
- package/lib/session-list.js +65 -0
- package/lib/session-runtime.d.ts +35 -0
- package/lib/session-runtime.js +1027 -0
- package/lib/shell-command.d.ts +9 -0
- package/lib/shell-command.js +62 -0
- package/lib/state.d.ts +493 -0
- package/lib/state.js +641 -0
- package/lib/terminal-guard.d.ts +14 -0
- package/lib/terminal-guard.js +76 -0
- package/lib/tool-presentation.d.ts +14 -0
- package/lib/tool-presentation.js +156 -0
- package/lib/transcript.d.ts +12 -0
- package/lib/transcript.js +304 -0
- package/lib/tui-root.d.ts +20 -0
- package/lib/tui-root.js +25 -0
- package/package.json +65 -0
- package/validated-dsh-versions.json +3 -0
package/lib/input.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { isKeyRelease, matchesKey } from '@earendil-works/pi-tui';
|
|
2
|
+
export function caretOffset(text, cursor) {
|
|
3
|
+
const lines = text.split('\n');
|
|
4
|
+
let offset = 0;
|
|
5
|
+
for (let line = 0; line < cursor.line; line++)
|
|
6
|
+
offset += (lines[line]?.length ?? 0) + 1;
|
|
7
|
+
return Math.min(text.length, offset + cursor.col);
|
|
8
|
+
}
|
|
9
|
+
export function installInput(tui, bindings) {
|
|
10
|
+
return tui.addInputListener((data) => {
|
|
11
|
+
if (data.includes('\u001B[200~')) {
|
|
12
|
+
const state = bindings.readState();
|
|
13
|
+
const decision = state.decisions[0];
|
|
14
|
+
const question = decision?.kind === 'question' ? decision.questions[decision.index] : undefined;
|
|
15
|
+
const customQuestion = question !== undefined && decision?.kind === 'question'
|
|
16
|
+
&& decision.cursor === question.options.length;
|
|
17
|
+
const completion = state.overlay?.kind === 'list' && state.overlay.purpose === 'completion';
|
|
18
|
+
return customQuestion || state.search !== undefined || completion
|
|
19
|
+
|| decision === undefined && state.overlay === undefined ? undefined : { consume: true };
|
|
20
|
+
}
|
|
21
|
+
if (isKeyRelease(data))
|
|
22
|
+
return { consume: true };
|
|
23
|
+
const state = bindings.readState();
|
|
24
|
+
const ctrlC = matchesKey(data, 'ctrl+c');
|
|
25
|
+
const ctrlD = matchesKey(data, 'ctrl+d');
|
|
26
|
+
if ((state.rewindArmed && !matchesKey(data, 'escape')) || (state.exitArmed && !ctrlC && !ctrlD)) {
|
|
27
|
+
bindings.dispatch({ type: 'disarm-exit' });
|
|
28
|
+
}
|
|
29
|
+
if (ctrlC) {
|
|
30
|
+
bindings.dispatch({ type: 'ctrl-c' });
|
|
31
|
+
return { consume: true };
|
|
32
|
+
}
|
|
33
|
+
const decision = bindings.readState().decisions[0];
|
|
34
|
+
if (decision !== undefined) {
|
|
35
|
+
const question = decision.kind === 'question' ? decision.questions[decision.index] : undefined;
|
|
36
|
+
const custom = question !== undefined && decision.cursor === question.options.length;
|
|
37
|
+
if (matchesKey(data, 'escape'))
|
|
38
|
+
bindings.dispatch({ type: 'decision-cancel' });
|
|
39
|
+
else if (matchesKey(data, 'up'))
|
|
40
|
+
bindings.dispatch({ type: 'decision-move', offset: -1 });
|
|
41
|
+
else if (matchesKey(data, 'down'))
|
|
42
|
+
bindings.dispatch({ type: 'decision-move', offset: 1 });
|
|
43
|
+
else if (matchesKey(data, 'enter'))
|
|
44
|
+
bindings.dispatch({ type: 'decision-submit' });
|
|
45
|
+
else if (custom)
|
|
46
|
+
return undefined;
|
|
47
|
+
else if (matchesKey(data, 'space'))
|
|
48
|
+
bindings.dispatch({ type: 'decision-toggle' });
|
|
49
|
+
else {
|
|
50
|
+
for (let number = 1; number <= 9; number++) {
|
|
51
|
+
if (!matchesKey(data, String(number)))
|
|
52
|
+
continue;
|
|
53
|
+
bindings.dispatch({ type: 'decision-number', number });
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { consume: true };
|
|
58
|
+
}
|
|
59
|
+
if (matchesKey(data, 'f1')) {
|
|
60
|
+
bindings.dispatch({ type: 'help' });
|
|
61
|
+
return { consume: true };
|
|
62
|
+
}
|
|
63
|
+
const search = bindings.readState().search;
|
|
64
|
+
if (search !== undefined) {
|
|
65
|
+
if (matchesKey(data, 'escape'))
|
|
66
|
+
bindings.dispatch({ type: 'search-close' });
|
|
67
|
+
else if (matchesKey(data, 'enter') || matchesKey(data, 'down')) {
|
|
68
|
+
bindings.dispatch({ type: 'search-move', offset: 1 });
|
|
69
|
+
}
|
|
70
|
+
else if (matchesKey(data, 'shift+enter') || matchesKey(data, 'up')) {
|
|
71
|
+
bindings.dispatch({ type: 'search-move', offset: -1 });
|
|
72
|
+
}
|
|
73
|
+
else
|
|
74
|
+
return undefined;
|
|
75
|
+
return { consume: true };
|
|
76
|
+
}
|
|
77
|
+
if (matchesKey(data, 'ctrl+f')) {
|
|
78
|
+
bindings.dispatch({ type: 'search-open', scope: 'transcript' });
|
|
79
|
+
return { consume: true };
|
|
80
|
+
}
|
|
81
|
+
if (matchesKey(data, 'ctrl+b')) {
|
|
82
|
+
bindings.dispatch({ type: 'open-details' });
|
|
83
|
+
return { consume: true };
|
|
84
|
+
}
|
|
85
|
+
if (matchesKey(data, 'shift+tab')) {
|
|
86
|
+
bindings.dispatch({ type: 'cycle-permission' });
|
|
87
|
+
return { consume: true };
|
|
88
|
+
}
|
|
89
|
+
const overlay = bindings.readState().overlay;
|
|
90
|
+
if (overlay !== undefined) {
|
|
91
|
+
if (matchesKey(data, 'escape'))
|
|
92
|
+
bindings.dispatch({ type: 'overlay-close' });
|
|
93
|
+
else if (overlay.kind === 'history' && matchesKey(data, 'up'))
|
|
94
|
+
bindings.dispatch({ type: 'history-move', offset: -1 });
|
|
95
|
+
else if (overlay.kind === 'history' && matchesKey(data, 'down'))
|
|
96
|
+
bindings.dispatch({ type: 'history-move', offset: 1 });
|
|
97
|
+
else if (overlay.kind === 'history' && matchesKey(data, '/'))
|
|
98
|
+
bindings.dispatch({ type: 'search-open', scope: 'history' });
|
|
99
|
+
else if (overlay.kind === 'history' && matchesKey(data, 'y'))
|
|
100
|
+
bindings.dispatch({ type: 'history-copy' });
|
|
101
|
+
else if (matchesKey(data, 'up'))
|
|
102
|
+
bindings.dispatch({ type: 'overlay-move', offset: -1 });
|
|
103
|
+
else if (matchesKey(data, 'down'))
|
|
104
|
+
bindings.dispatch({ type: 'overlay-move', offset: 1 });
|
|
105
|
+
else if (matchesKey(data, 'enter'))
|
|
106
|
+
bindings.dispatch({ type: 'overlay-submit', execute: true });
|
|
107
|
+
else if (matchesKey(data, 'tab'))
|
|
108
|
+
bindings.dispatch({ type: 'overlay-submit' });
|
|
109
|
+
else if (overlay.kind === 'list' && overlay.purpose === 'completion') {
|
|
110
|
+
bindings.dispatch({ type: 'overlay-close' });
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
for (let number = 1; number <= 9; number++) {
|
|
115
|
+
if (!matchesKey(data, String(number)))
|
|
116
|
+
continue;
|
|
117
|
+
bindings.dispatch({ type: 'overlay-number', number });
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return { consume: true };
|
|
122
|
+
}
|
|
123
|
+
if (matchesKey(data, 'ctrl+v')) {
|
|
124
|
+
bindings.dispatch({ type: 'clipboard-paste' });
|
|
125
|
+
return { consume: true };
|
|
126
|
+
}
|
|
127
|
+
if (matchesKey(data, 'pageUp')) {
|
|
128
|
+
bindings.dispatch(bindings.readState().inline
|
|
129
|
+
? { type: 'open-history' }
|
|
130
|
+
: { type: 'scroll', direction: 'page-up' });
|
|
131
|
+
return { consume: true };
|
|
132
|
+
}
|
|
133
|
+
if (matchesKey(data, 'pageDown')) {
|
|
134
|
+
bindings.dispatch({ type: 'scroll', direction: 'page-down' });
|
|
135
|
+
return { consume: true };
|
|
136
|
+
}
|
|
137
|
+
if (matchesKey(data, 'ctrl+home')) {
|
|
138
|
+
bindings.dispatch(bindings.readState().inline
|
|
139
|
+
? { type: 'open-history' }
|
|
140
|
+
: { type: 'scroll', direction: 'home' });
|
|
141
|
+
return { consume: true };
|
|
142
|
+
}
|
|
143
|
+
if (matchesKey(data, 'ctrl+end')) {
|
|
144
|
+
bindings.dispatch({ type: 'scroll', direction: 'end' });
|
|
145
|
+
return { consume: true };
|
|
146
|
+
}
|
|
147
|
+
if (matchesKey(data, 'tab')) {
|
|
148
|
+
const state = bindings.readState();
|
|
149
|
+
const cursor = bindings.readComposerCursor?.() ?? { col: state.composer.length, line: 0 };
|
|
150
|
+
bindings.dispatch({ type: 'request-completion', caret: caretOffset(state.composer, cursor) });
|
|
151
|
+
return { consume: true };
|
|
152
|
+
}
|
|
153
|
+
if (matchesKey(data, 'ctrl+r')) {
|
|
154
|
+
bindings.dispatch({ type: 'request-search' });
|
|
155
|
+
return { consume: true };
|
|
156
|
+
}
|
|
157
|
+
if (matchesKey(data, 'up') && !bindings.readState().composer.includes('\n')) {
|
|
158
|
+
bindings.dispatch({ type: 'recall-move', offset: -1 });
|
|
159
|
+
return { consume: true };
|
|
160
|
+
}
|
|
161
|
+
if (matchesKey(data, 'down') && bindings.readState().recall !== undefined) {
|
|
162
|
+
bindings.dispatch({ type: 'recall-move', offset: 1 });
|
|
163
|
+
return { consume: true };
|
|
164
|
+
}
|
|
165
|
+
if (matchesKey(data, 'ctrl+o')) {
|
|
166
|
+
bindings.dispatch({ type: 'toggle-tool-mode' });
|
|
167
|
+
return { consume: true };
|
|
168
|
+
}
|
|
169
|
+
if (matchesKey(data, 'ctrl+t')) {
|
|
170
|
+
bindings.dispatch({ type: 'toggle-send-mode' });
|
|
171
|
+
return { consume: true };
|
|
172
|
+
}
|
|
173
|
+
if (matchesKey(data, 'ctrl+s')) {
|
|
174
|
+
const state = bindings.readState();
|
|
175
|
+
bindings.dispatch({
|
|
176
|
+
type: 'stash-toggle', cursor: bindings.readComposerCursor?.() ?? { col: state.composer.length, line: 0 },
|
|
177
|
+
});
|
|
178
|
+
return { consume: true };
|
|
179
|
+
}
|
|
180
|
+
if (matchesKey(data, 'ctrl+g')) {
|
|
181
|
+
bindings.dispatch({ type: 'external-edit' });
|
|
182
|
+
return { consume: true };
|
|
183
|
+
}
|
|
184
|
+
if (matchesKey(data, 'backspace')) {
|
|
185
|
+
const state = bindings.readState();
|
|
186
|
+
if (state.composer === '' && state.attachments.length > 0) {
|
|
187
|
+
bindings.dispatch({ type: 'attachment-remove', index: state.attachments.length - 1 });
|
|
188
|
+
return { consume: true };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (data === '\n' || matchesKey(data, 'ctrl+j')
|
|
192
|
+
|| matchesKey(data, 'shift+enter') || matchesKey(data, 'alt+enter')) {
|
|
193
|
+
bindings.insertNewline?.();
|
|
194
|
+
return { consume: true };
|
|
195
|
+
}
|
|
196
|
+
if (matchesKey(data, 'enter')) {
|
|
197
|
+
bindings.dispatch({ type: 'submit' });
|
|
198
|
+
return { consume: true };
|
|
199
|
+
}
|
|
200
|
+
if (ctrlD && bindings.readState().composer === '') {
|
|
201
|
+
bindings.dispatch({ type: 'ctrl-d' });
|
|
202
|
+
return { consume: true };
|
|
203
|
+
}
|
|
204
|
+
if (matchesKey(data, 'ctrl+l')) {
|
|
205
|
+
bindings.dispatch({ type: 'redraw' });
|
|
206
|
+
return { consume: true };
|
|
207
|
+
}
|
|
208
|
+
if (matchesKey(data, 'ctrl+z')) {
|
|
209
|
+
bindings.dispatch({ type: 'suspend' });
|
|
210
|
+
return { consume: true };
|
|
211
|
+
}
|
|
212
|
+
if (matchesKey(data, 'escape')) {
|
|
213
|
+
bindings.dispatch({ type: 'escape' });
|
|
214
|
+
return { consume: true };
|
|
215
|
+
}
|
|
216
|
+
if (bindings.readState().exitArmed)
|
|
217
|
+
bindings.dispatch({ type: 'disarm-exit' });
|
|
218
|
+
return undefined;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function filterWheelInput(data: string): -3 | 3 | string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { SessionJob } from '@deepseek-ai/dsh-api-session-controller';
|
|
3
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
4
|
+
import type { JobView, SubagentView } from './state.js';
|
|
5
|
+
/** Web-equivalent context occupancy; undefined until both DSH projection fields exist. */
|
|
6
|
+
export declare function contextPercent(value: unknown): number | undefined;
|
|
7
|
+
export declare function jobViews(jobs: readonly SessionJob[]): readonly JobView[];
|
|
8
|
+
/** Read DSH's direct-child catalog and projection-backed summary facts. */
|
|
9
|
+
export declare function subagentViews(ctx: Context, parentId: SessionId, signal: AbortSignal): Promise<readonly SubagentView[]>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Web-equivalent context occupancy; undefined until both DSH projection fields exist. */
|
|
2
|
+
export function contextPercent(value) {
|
|
3
|
+
if (typeof value !== 'object' || value === null)
|
|
4
|
+
return undefined;
|
|
5
|
+
const pressure = value;
|
|
6
|
+
const used = typeof pressure.projectedTokens === 'number' ? pressure.projectedTokens : pressure.pressureTokens;
|
|
7
|
+
return typeof used !== 'number' || typeof pressure.contextWindow !== 'number'
|
|
8
|
+
? undefined
|
|
9
|
+
: Math.min(100, Math.round(used / pressure.contextWindow * 100));
|
|
10
|
+
}
|
|
11
|
+
export function jobViews(jobs) {
|
|
12
|
+
return jobs.map(job => ({
|
|
13
|
+
id: String(job.id), kind: job.kind, label: job.label, startedAt: job.startedAt, status: job.status,
|
|
14
|
+
...(job.detail === undefined ? {} : { detail: job.detail }),
|
|
15
|
+
...(job.finishedAt === undefined ? {} : { finishedAt: job.finishedAt }),
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
function summaryTitle(summary) {
|
|
19
|
+
const title = summary?.projections?.values.title;
|
|
20
|
+
return typeof title === 'string' && title !== '' ? title : undefined;
|
|
21
|
+
}
|
|
22
|
+
function tokenTotal(summary) {
|
|
23
|
+
const usage = summary?.projections?.values.tokenUsage;
|
|
24
|
+
return usage === undefined ? undefined
|
|
25
|
+
: usage.uncachedInputTokens + usage.outputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
|
|
26
|
+
}
|
|
27
|
+
/** Read DSH's direct-child catalog and projection-backed summary facts. */
|
|
28
|
+
export async function subagentViews(ctx, parentId, signal) {
|
|
29
|
+
const subagents = ctx.get('subagents');
|
|
30
|
+
if (subagents === undefined)
|
|
31
|
+
return [];
|
|
32
|
+
const [catalog, listed] = await Promise.all([
|
|
33
|
+
subagents.remoteExportList(parentId, signal),
|
|
34
|
+
ctx.sessionController.list({}, signal),
|
|
35
|
+
]);
|
|
36
|
+
const summaries = new Map(listed.items.map(summary => [String(summary.sessionId), summary]));
|
|
37
|
+
return catalog.entries.flatMap((entry) => {
|
|
38
|
+
if (entry.kind !== 'child')
|
|
39
|
+
return [];
|
|
40
|
+
const summary = summaries.get(String(entry.id));
|
|
41
|
+
const timing = summary?.projections?.values.subagentTiming;
|
|
42
|
+
const title = summaryTitle(summary);
|
|
43
|
+
const tokens = tokenTotal(summary);
|
|
44
|
+
return [{
|
|
45
|
+
id: String(entry.id), label: entry.label ?? String(entry.id), mode: entry.mode, state: entry.activity,
|
|
46
|
+
...(timing === undefined ? {} : {
|
|
47
|
+
elapsedMs: timing.settledMs,
|
|
48
|
+
...(timing.active === undefined ? {} : { active: timing.active }),
|
|
49
|
+
}),
|
|
50
|
+
...(title === undefined ? {} : { summary: title }),
|
|
51
|
+
...(tokens === undefined ? {} : { tokens }),
|
|
52
|
+
}];
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Terminal } from '@earendil-works/pi-tui';
|
|
2
|
+
import { type InputBindings } from './input.js';
|
|
3
|
+
import type { ComposerCursor } from './state.js';
|
|
4
|
+
import { type TerminalSession } from './terminal-guard.js';
|
|
5
|
+
export type CopyResult = 'ok' | 'too-large' | 'unsupported';
|
|
6
|
+
export interface Renderer extends TerminalSession {
|
|
7
|
+
bell(): void;
|
|
8
|
+
copy(text: string): CopyResult;
|
|
9
|
+
materializedCells(): number;
|
|
10
|
+
setComposer(text: string, cursor?: ComposerCursor): void;
|
|
11
|
+
}
|
|
12
|
+
export interface RendererOptions extends InputBindings {
|
|
13
|
+
readonly accessible?: boolean;
|
|
14
|
+
readonly inline: boolean;
|
|
15
|
+
readonly terminal?: Terminal;
|
|
16
|
+
readonly terminalType?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function supportsOsc52(terminalType: string | undefined): boolean;
|
|
19
|
+
export declare function createRenderer(options: RendererOptions): Renderer;
|