dsh-code 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +5 -5
- package/README.md +5 -5
- package/lib/index.mjs +955 -233
- package/lib/startup.mjs +1 -1
- package/lib/{theme-7u5Qo3dF.mjs → theme-B3orFUYz.mjs} +8 -0
- package/lib/types/app.d.ts +20 -0
- package/lib/types/attachments.d.ts +16 -7
- package/lib/types/history.d.ts +10 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/kernel-panels.d.ts +3 -1
- package/lib/types/locales/en.d.ts +49 -1
- package/lib/types/render/status.d.ts +4 -1
- package/lib/types/session-directory.d.ts +25 -1
- package/lib/types/session-switch.d.ts +8 -0
- package/lib/types/update-panel.d.ts +22 -1
- package/lib/types/version.d.ts +2 -0
- package/package.json +7 -5
- package/src/app.ts +288 -166
- package/src/attachments.ts +65 -19
- package/src/authorization-panel.ts +5 -2
- package/src/fork.ts +11 -7
- package/src/history.ts +14 -0
- package/src/index.ts +92 -53
- package/src/input-split.ts +24 -4
- package/src/kernel-panels.ts +60 -27
- package/src/locales/en.ts +50 -1
- package/src/locales/zh.ts +50 -1
- package/src/rainbow.ts +13 -3
- package/src/render/status.ts +80 -29
- package/src/render/text.ts +2 -1
- package/src/session-directory.ts +82 -3
- package/src/session-switch.ts +14 -0
- package/src/store.ts +19 -1
- package/src/update-panel.ts +112 -5
- package/src/version.ts +5 -0
package/lib/startup.mjs
CHANGED
|
@@ -538,6 +538,10 @@ function hueOf([r, g, b]) {
|
|
|
538
538
|
if (max === g) return (60 * (b - r) / span + 120) % 360;
|
|
539
539
|
return (60 * (r - g) / span + 240) % 360;
|
|
540
540
|
}
|
|
541
|
+
/** Strict RGB equality for the wrap-collision repair. */
|
|
542
|
+
function sameRgb(left, right) {
|
|
543
|
+
return left[0] === right[0] && left[1] === right[1] && left[2] === right[2];
|
|
544
|
+
}
|
|
541
545
|
/** A dark row tint for diff backgrounds: the hue at 22% strength over black. */
|
|
542
546
|
function darkTint(hue) {
|
|
543
547
|
return [
|
|
@@ -603,6 +607,10 @@ function rollRainbow(seed) {
|
|
|
603
607
|
TONE_ORDER.forEach((tone, index) => {
|
|
604
608
|
toneColors[tone] = pool[(offset + index) % pool.length];
|
|
605
609
|
});
|
|
610
|
+
if (sameRgb(toneColors.live, toneColors.error)) {
|
|
611
|
+
const replacement = pool.find((hue) => !sameRgb(hue, toneColors.live) && !sameRgb(hue, toneColors.warn));
|
|
612
|
+
if (replacement !== void 0) toneColors.error = replacement;
|
|
613
|
+
}
|
|
606
614
|
return {
|
|
607
615
|
seed,
|
|
608
616
|
palette,
|
package/lib/types/app.d.ts
CHANGED
|
@@ -234,6 +234,20 @@ export interface AppProps {
|
|
|
234
234
|
/** Apply the Ctrl+R terminal passthrough to the detected editor (/vscode-keys); resolves to a one-line summary. */
|
|
235
235
|
applyEditorKeys: () => Promise<string>;
|
|
236
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* The streaming buffer rendered with a hard size cap: the live region must
|
|
239
|
+
* ALWAYS fit the terminal, or Ink's erase/rewrite of a dynamic tree taller
|
|
240
|
+
* than the screen freezes (cursor-up past the top, garbage, no scroll). The
|
|
241
|
+
* cap counts explicit newlines and terminal wrapping, slicing from the END so
|
|
242
|
+
* the freshest tokens stay visible while a long reply streams; the complete
|
|
243
|
+
* text lands in the flushed scrollback once the turn assembles it.
|
|
244
|
+
*
|
|
245
|
+
* Body wrap width for a streaming tail. `rowColumns` is the same width
|
|
246
|
+
* passed to `transcriptEntryLines` (terminal minus the last-column safety);
|
|
247
|
+
* the hanging prefix then shrinks the body so streamed text and settled
|
|
248
|
+
* markdown wrap on the same column.
|
|
249
|
+
*/
|
|
250
|
+
export declare function streamTailBodyColumns(rowColumns: number, prefix: string, continuationPrefix?: string): number;
|
|
237
251
|
/** Rows in the exact next-turn inbox order, never transcript append order. */
|
|
238
252
|
export declare function queuedInboxRows(entries: readonly TranscriptEntry[], ids: readonly string[]): readonly Extract<TranscriptEntry, {
|
|
239
253
|
kind: 'pending';
|
|
@@ -247,6 +261,12 @@ interface CompletionCandidate {
|
|
|
247
261
|
/** Candidate origin; skills land the same literal text but route through the prompt. */
|
|
248
262
|
origin: 'command' | 'skill' | 'mention';
|
|
249
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Wrap one completion-menu cursor step. An empty menu keeps the index at 0:
|
|
266
|
+
* `% 0` is NaN, and a NaN index silently disables the highlight, the accept
|
|
267
|
+
* key, and any later Enter that runs through the menu.
|
|
268
|
+
*/
|
|
269
|
+
export declare function stepCompletionIndex(index: number, delta: number, count: number): number;
|
|
250
270
|
/**
|
|
251
271
|
* Resolve completion candidates for the current input: TUI-local commands,
|
|
252
272
|
* the live registry descriptors, and user-invocable skills, filtered by the
|
|
@@ -26,17 +26,26 @@ export declare const MAX_FILES_PER_MESSAGE = 8;
|
|
|
26
26
|
export declare function detectImageMediaType(data: Uint8Array): ImageMediaType | undefined;
|
|
27
27
|
/** Whether a path-like token is worth probing as an image attachment. */
|
|
28
28
|
export declare function looksLikeImagePath(path: string): boolean;
|
|
29
|
+
/** Strip one layer of ASCII or Unicode quotes and shell-escaped spaces. */
|
|
30
|
+
export declare function unwrapDroppedPath(token: string): string;
|
|
31
|
+
/** Absolute/relative drop with a dotted leaf — including unquoted spaces. */
|
|
32
|
+
export declare function looksLikeFilesystemDrop(path: string): boolean;
|
|
29
33
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
* A composer draft that is a filesystem path, not a slash command.
|
|
35
|
+
* `/usage` stays a command; `/Users/foo.png` and `C:\temp\a.png` are drops.
|
|
36
|
+
*/
|
|
37
|
+
export declare function looksLikePathDraft(value: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Parse a paste or file-drop into image and file paths: image-suffixed
|
|
40
|
+
* tokens stay images, other path-shaped tokens ride as file attachments
|
|
41
|
+
* (0.1.5 file blocks), and anything that is neither leaves both empty —
|
|
42
|
+
* the caller then treats the paste as plain text.
|
|
34
43
|
*
|
|
35
44
|
* File tokens are held to an absolute-path-with-shape bar (drive/backslash
|
|
36
|
-
* or a
|
|
45
|
+
* or a dotted leaf after a separator): a dropped terminal path always
|
|
37
46
|
* carries one of those, while prose, slash commands, and option flags never
|
|
38
|
-
* do. A POSIX absolute path without
|
|
39
|
-
*
|
|
47
|
+
* do. A POSIX absolute path without a dotted leaf falls through as text —
|
|
48
|
+
* the @ mention route still attaches such files deliberately.
|
|
40
49
|
*/
|
|
41
50
|
export declare function parsePastedAttachmentPaths(input: string): {
|
|
42
51
|
readonly images: readonly string[];
|
package/lib/types/history.d.ts
CHANGED
|
@@ -78,6 +78,16 @@ export interface RecallState {
|
|
|
78
78
|
}
|
|
79
79
|
/** Fresh navigation state over one recall space. */
|
|
80
80
|
export declare function beginRecall(entries: readonly string[], draft: string): RecallState;
|
|
81
|
+
/**
|
|
82
|
+
* Join a panel-recalled entry onto the draft already in the composer: the
|
|
83
|
+
* draft is extended, never replaced, so picking a history row cannot discard
|
|
84
|
+
* work in progress. An empty draft takes the entry as-is; otherwise the entry
|
|
85
|
+
* starts on its own line unless the draft already ends one.
|
|
86
|
+
* @param draft - the composer text before the recall.
|
|
87
|
+
* @param entry - the sanitized text of the accepted row.
|
|
88
|
+
* @returns the text to place in the composer.
|
|
89
|
+
*/
|
|
90
|
+
export declare function appendRecall(draft: string, entry: string): string;
|
|
81
91
|
/** The outcome of one recall step. */
|
|
82
92
|
export interface RecallStep {
|
|
83
93
|
state: RecallState;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ import z from '@deepseek-ai/schemastery';
|
|
|
13
13
|
import type { Agent, AgentStatus, Inbox } from '@deepseek-ai/dsh-agent';
|
|
14
14
|
import { type ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
15
15
|
import { SessionId, type SessionEvent, type SessionHeader, type UserMessage } from '@deepseek-ai/dsh-session';
|
|
16
|
-
import type
|
|
16
|
+
import { type SessionPersistence } from '@deepseek-ai/dsh-session-persistence';
|
|
17
17
|
import { type QueueMutation } from './app.ts';
|
|
18
18
|
import type { TuiStartup } from './startup.ts';
|
|
19
19
|
import type { SearchRow } from './kernel-panels.ts';
|
|
@@ -66,7 +66,7 @@ export declare function JobsPanel({ load, close }: {
|
|
|
66
66
|
load: () => readonly JobRow[];
|
|
67
67
|
close: () => void;
|
|
68
68
|
}): ReactElement;
|
|
69
|
-
export declare function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete, deleteConfirmId, reloadToken, deleteMode, close }: {
|
|
69
|
+
export declare function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete, deleteConfirmId, reloadToken, deleteMode, presetId, close }: {
|
|
70
70
|
currentCwd: string;
|
|
71
71
|
load: (options: SessionDirectoryOptions, signal?: AbortSignal) => Promise<readonly SessionRow[]>;
|
|
72
72
|
readTranscript: (id: string, signal?: AbortSignal) => Promise<string>;
|
|
@@ -79,6 +79,8 @@ export declare function ResumePanel({ currentCwd, load, readTranscript, select,
|
|
|
79
79
|
reloadToken?: number;
|
|
80
80
|
/** Opened via /delete: hint-first delete mode. */
|
|
81
81
|
deleteMode?: boolean;
|
|
82
|
+
/** `/delete <id>` argument to resolve after the listing loads. */
|
|
83
|
+
presetId?: string;
|
|
82
84
|
close: () => void;
|
|
83
85
|
}): ReactElement;
|
|
84
86
|
/**
|
|
@@ -51,7 +51,37 @@ export declare const en: {
|
|
|
51
51
|
readonly 'search.compact.searching': "searching…";
|
|
52
52
|
readonly 'search.compact.error': "error: {message}";
|
|
53
53
|
readonly 'search.footer': "↑↓ move · enter search/resume · esc close";
|
|
54
|
+
readonly 'header.hint': "/help · Esc interrupt · Ctrl+C quit";
|
|
55
|
+
readonly 'header.hintResumed': "resumed · /help · Esc interrupt";
|
|
54
56
|
readonly 'statusline.title': "/statusline · items apply to the live status line below";
|
|
57
|
+
readonly 'statusline.item.model': "model";
|
|
58
|
+
readonly 'statusline.item.model.desc': "provider/model serving this session";
|
|
59
|
+
readonly 'statusline.item.cwd': "cwd";
|
|
60
|
+
readonly 'statusline.item.cwd.desc': "working-directory basename";
|
|
61
|
+
readonly 'statusline.item.mode': "mode";
|
|
62
|
+
readonly 'statusline.item.mode.desc': "agent preset composing the session";
|
|
63
|
+
readonly 'statusline.item.branch': "branch";
|
|
64
|
+
readonly 'statusline.item.branch.desc': "git branch inside a repository";
|
|
65
|
+
readonly 'statusline.item.context': "context";
|
|
66
|
+
readonly 'statusline.item.context.desc': "context-window occupancy meter";
|
|
67
|
+
readonly 'statusline.item.permission': "permission";
|
|
68
|
+
readonly 'statusline.item.permission.desc': "permission preset badge with cycle hint";
|
|
69
|
+
readonly 'statusline.item.plan': "plan";
|
|
70
|
+
readonly 'statusline.item.plan.desc': "plan-mode state mark";
|
|
71
|
+
readonly 'statusline.item.turns': "turns";
|
|
72
|
+
readonly 'statusline.item.turns.desc': "turn and step counters";
|
|
73
|
+
readonly 'statusline.item.durations': "durations";
|
|
74
|
+
readonly 'statusline.item.durations.desc': "llm/ttft/decode/tool wall time";
|
|
75
|
+
readonly 'statusline.item.cache': "cache";
|
|
76
|
+
readonly 'statusline.item.cache.desc': "cache-hit share of billed input";
|
|
77
|
+
readonly 'statusline.item.tokens': "tokens";
|
|
78
|
+
readonly 'statusline.item.tokens.desc': "cumulative input/output tokens";
|
|
79
|
+
readonly 'statusline.item.title': "title";
|
|
80
|
+
readonly 'statusline.item.title.desc': "session title or short id";
|
|
81
|
+
readonly 'statusline.item.goal': "goal";
|
|
82
|
+
readonly 'statusline.item.goal.desc': "live goal phase and round progress";
|
|
83
|
+
readonly 'statusline.item.sandbox': "sandbox";
|
|
84
|
+
readonly 'statusline.item.sandbox.desc': "divergent sandbox-mode override";
|
|
55
85
|
readonly 'schedule.title': "/schedule · {count} active reminder";
|
|
56
86
|
readonly 'schedule.titlePlural': "/schedule · {count} active reminders";
|
|
57
87
|
readonly 'schedule.empty': "no active reminders — the model creates them with schedule_create";
|
|
@@ -197,10 +227,16 @@ export declare const en: {
|
|
|
197
227
|
readonly 'status.label.out': "out";
|
|
198
228
|
readonly 'status.label.mode': "/mode";
|
|
199
229
|
readonly 'status.label.context': "context";
|
|
230
|
+
readonly 'status.label.sandbox': "sandbox";
|
|
231
|
+
readonly 'status.plan.on': "plan on";
|
|
232
|
+
readonly 'status.plan.mark': "⧉ plan";
|
|
233
|
+
readonly 'status.goal.round': "◎ round {current}/{max}";
|
|
234
|
+
readonly 'status.goal.phase': "◎ {phase}";
|
|
200
235
|
readonly 'frozen.keysGoTo': "keys go to {owner} · esc {action}";
|
|
201
236
|
readonly 'frozen.action.rejects': "rejects";
|
|
202
237
|
readonly 'frozen.action.cancels': "cancels";
|
|
203
238
|
readonly 'frozen.action.closes': "closes";
|
|
239
|
+
readonly 'frozen.action.waits': "waits";
|
|
204
240
|
readonly 'time.justNow': "now";
|
|
205
241
|
readonly 'time.minutesAgo': "{n}m ago";
|
|
206
242
|
readonly 'time.hoursAgo': "{n}h ago";
|
|
@@ -212,6 +248,7 @@ export declare const en: {
|
|
|
212
248
|
readonly 'notice.themeSaveFailed': "theme save failed: {message}";
|
|
213
249
|
readonly 'notice.languageSaveFailed': "language save failed: {message}";
|
|
214
250
|
readonly 'notice.alreadyActive': "that session is already active";
|
|
251
|
+
readonly 'notice.permissionPresetsUnmounted': "permission presets are not mounted in this composition";
|
|
215
252
|
readonly 'notice.queueCancelled': "queued message cancelled";
|
|
216
253
|
readonly 'notice.queueActionFailed': "queue action failed: {message}";
|
|
217
254
|
readonly 'notice.queueUnavailable': "queued message is no longer pending";
|
|
@@ -226,6 +263,8 @@ export declare const en: {
|
|
|
226
263
|
readonly 'notice.usage.animation': "usage: /animation [on|off]";
|
|
227
264
|
readonly 'notice.usage.language': "usage: /language [en|zh]";
|
|
228
265
|
readonly 'notice.usage.rainbow': "usage: /rainbow [seed]";
|
|
266
|
+
readonly 'notice.usage.bareCommand': "usage: /{name}";
|
|
267
|
+
readonly 'notice.switchInProgress': "switching sessions — send again once the new session is up, or /resume cancel";
|
|
229
268
|
readonly 'notice.imageCancelled': "image submission cancelled";
|
|
230
269
|
readonly 'notice.themeConfigUnreadable': "theme config unreadable, using dark: {message}";
|
|
231
270
|
readonly 'notice.languageConfigUnreadable': "language config unreadable, using english: {message}";
|
|
@@ -256,6 +295,13 @@ export declare const en: {
|
|
|
256
295
|
readonly 'notice.resumeFailed': "resume failed: {message}";
|
|
257
296
|
readonly 'notice.forkFailed': "fork failed: {message}";
|
|
258
297
|
readonly 'notice.sessionSwitchFailed': "session switch failed: {message}";
|
|
298
|
+
readonly 'notice.sessionCreated': "created {id} · mode {mode}";
|
|
299
|
+
readonly 'notice.sessionResumed': "resumed {id} · mode {mode}";
|
|
300
|
+
readonly 'notice.sessionSwitchedDirty': "switched to {id}, but {detail}";
|
|
301
|
+
readonly 'notice.flushFailed': "previous session flush failed: {message}";
|
|
302
|
+
readonly 'notice.agentReleaseFailed': "previous agent release failed: {message}";
|
|
303
|
+
readonly 'notice.copyEmpty': "nothing to copy yet";
|
|
304
|
+
readonly 'notice.copied': "copied latest response";
|
|
259
305
|
readonly 'notice.switchQueued': "will switch to {label} when the current turn finishes · /resume cancel to abort";
|
|
260
306
|
readonly 'notice.reviewStarted': "review started under read-only permissions";
|
|
261
307
|
readonly 'notice.reviewFailed': "review failed: {message}";
|
|
@@ -307,7 +353,8 @@ export declare const en: {
|
|
|
307
353
|
readonly 'panel.footer.chooseSelect': "↑↓ choose · enter select · r refresh · esc close";
|
|
308
354
|
readonly 'panel.footer.inspectDetails': "↑↓ inspect · enter details · r refresh · esc close";
|
|
309
355
|
readonly 'panel.footer.inspectRefresh': "↑↓ inspect · r refresh · esc close";
|
|
310
|
-
readonly 'panel.footer.resume': "tab/←→ filters · ↑↓/pg navigate · ctrl+f search · e details · t transcript ·
|
|
356
|
+
readonly 'panel.footer.resume': "tab/←→ filters · ↑↓/pg navigate · ctrl+f search · e details · t transcript · enter resume";
|
|
357
|
+
readonly 'panel.footer.delete': "tab/←→ filters · ↑↓/pg navigate · ctrl+f search · e details · t transcript · enter delete · esc close";
|
|
311
358
|
readonly 'panel.footer.transcript': "lines {from}-{to}/{total} · ↑↓/pg/g/G · t/esc close";
|
|
312
359
|
readonly 'panel.footer.history': "↑↓ move · g/G ends · enter fill · esc close";
|
|
313
360
|
readonly 'panel.footer.review': "↑↓ move · enter select · esc back · q close";
|
|
@@ -319,6 +366,7 @@ export declare const en: {
|
|
|
319
366
|
readonly 'panel.jobs.title': "/jobs · background tasks · {count}";
|
|
320
367
|
readonly 'panel.resume.deleteTitle': "permanently delete {target}? this cannot be undone · subagent threads go too";
|
|
321
368
|
readonly 'panel.resume.title': "/resume{mode}{search} · {toolbar}";
|
|
369
|
+
readonly 'panel.delete.title': "/delete{search} · {toolbar}";
|
|
322
370
|
readonly 'panel.noActiveReminders': "no active reminders — the model schedules via schedule_create";
|
|
323
371
|
readonly 'panel.notMounted': "not mounted";
|
|
324
372
|
readonly 'panel.broken': "broken: {message}";
|
|
@@ -119,6 +119,8 @@ export interface StatusItemInfo {
|
|
|
119
119
|
}
|
|
120
120
|
/** The full item catalog in canonical order (the /statusline default). */
|
|
121
121
|
export declare const STATUS_ITEMS: readonly StatusItemInfo[];
|
|
122
|
+
/** Picker rows with labels/descriptions in the active interface language. */
|
|
123
|
+
export declare function localizedStatusItems(): readonly StatusItemInfo[];
|
|
122
124
|
/**
|
|
123
125
|
* Default order: the whole catalog (matches the pre-customization bar).
|
|
124
126
|
* The busy dot is not an item — it always leads the identity cluster.
|
|
@@ -178,7 +180,8 @@ export declare function permissionTone(permission: string): StatusTone;
|
|
|
178
180
|
/**
|
|
179
181
|
* Compose the two-row footer layout under a column budget. Row 1 keeps model,
|
|
180
182
|
* cwd, mode, branch, context, then the right-pinned permission badge and cycle
|
|
181
|
-
* hint. It
|
|
183
|
+
* hint. It shrinks the context bar, drops the hint, and peels trailing
|
|
184
|
+
* identity facts before removing the context group or the permission badge.
|
|
182
185
|
* Row 2 fits all secondary figures and state within its own budget.
|
|
183
186
|
* @param facts - identity facts resolved by the runner.
|
|
184
187
|
* @param stats - session figures folded from the durable log.
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
/** Lightweight session-directory projection for the /resume picker. */
|
|
2
|
-
import { type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session';
|
|
2
|
+
import { SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session';
|
|
3
3
|
export interface SessionRecord {
|
|
4
4
|
readonly header: SessionHeader;
|
|
5
5
|
readonly live: boolean;
|
|
6
6
|
readonly persisted: boolean;
|
|
7
7
|
}
|
|
8
|
+
/** Minimal write handle retained while a planned deletion touches artifacts. */
|
|
9
|
+
export interface SessionDeletionLease {
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
/** Public persistence operation used to acquire the backend's write lease. */
|
|
13
|
+
export interface SessionDeletionPersistence {
|
|
14
|
+
open(id: SessionId, access: 'write'): Promise<SessionDeletionLease>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Acquire every subtree member's cross-process write lease before deleting
|
|
18
|
+
* any artifact. A partial acquisition is rolled back, so callers either hold
|
|
19
|
+
* the whole deletion boundary or touch nothing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function acquireSessionDeletionLeases(persistence: SessionDeletionPersistence, ids: readonly string[]): Promise<readonly SessionDeletionLease[]>;
|
|
22
|
+
/** Release deletion leases in reverse acquisition order. */
|
|
23
|
+
export declare function releaseSessionDeletionLeases(leases: readonly SessionDeletionLease[]): Promise<void>;
|
|
8
24
|
export interface TitleObservationResult {
|
|
9
25
|
readonly sessionId: string;
|
|
10
26
|
readonly status: 'fulfilled' | 'rejected';
|
|
@@ -78,6 +94,12 @@ export declare function isSubagentSession(header: SessionHeader): boolean;
|
|
|
78
94
|
* @throws when nothing matches or the prefix is ambiguous.
|
|
79
95
|
*/
|
|
80
96
|
export declare function matchSessionId(headers: readonly SessionHeader[], wanted: string): SessionHeader;
|
|
97
|
+
/**
|
|
98
|
+
* Unique picker-row match by exact id, unique prefix, or unique suffix.
|
|
99
|
+
* The resume list shows `id.slice(-12)`, so `/delete` arguments are often
|
|
100
|
+
* that tail rather than a leading prefix.
|
|
101
|
+
*/
|
|
102
|
+
export declare function matchSessionRow(rows: readonly SessionRow[], wanted: string): SessionRow;
|
|
81
103
|
/** The newest persisted ROOT session pinned to this cwd, or undefined. */
|
|
82
104
|
export declare function newestRootForCwd(headers: readonly SessionHeader[], cwd: string): SessionHeader | undefined;
|
|
83
105
|
/**
|
|
@@ -90,6 +112,8 @@ export declare function newestRootForCwd(headers: readonly SessionHeader[], cwd:
|
|
|
90
112
|
* @param updated - per-session last-activity timestamps, when resolved.
|
|
91
113
|
*/
|
|
92
114
|
export declare function projectSessionRows(records: readonly SessionRecord[], options: SessionDirectoryOptions, updated?: ReadonlyMap<string, number>): SessionRow[];
|
|
115
|
+
/** True when the picker query hits id, path, preset, or the displayed title. */
|
|
116
|
+
export declare function sessionRowMatchesQuery(row: Pick<SessionRow, 'id' | 'cwd' | 'workspace' | 'preset' | 'title'>, query: string): boolean;
|
|
93
117
|
/** Merge page-local title observations without disturbing directory order. */
|
|
94
118
|
export declare function mergeSessionTitles(rows: readonly SessionRow[], observations: readonly TitleObservationResult[]): SessionRow[];
|
|
95
119
|
/**
|
|
@@ -8,10 +8,18 @@ export declare class SessionSwitchQueue<T> {
|
|
|
8
8
|
private readonly failed;
|
|
9
9
|
private pending;
|
|
10
10
|
private pumping;
|
|
11
|
+
private running;
|
|
11
12
|
constructor(execute: (value: T) => Promise<void>, failed: (error: unknown) => void);
|
|
12
13
|
/** Queue a request; a later request replaces any request still waiting. */
|
|
13
14
|
request(activity: IdleActivity, value: T): 'queued' | 'started';
|
|
14
15
|
/** Cancel only work that has not begun activation. */
|
|
15
16
|
cancel(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Whether a queued change is being activated right now. Between the idle
|
|
19
|
+
* wait and the handoff the old session is still installed, so a submission
|
|
20
|
+
* made in that window would start a turn the handoff then discards; callers
|
|
21
|
+
* use this to refuse one instead of losing it.
|
|
22
|
+
*/
|
|
23
|
+
get activating(): boolean;
|
|
16
24
|
private pump;
|
|
17
25
|
}
|
|
@@ -9,11 +9,31 @@
|
|
|
9
9
|
* here.
|
|
10
10
|
*/
|
|
11
11
|
import { type ReactElement } from 'react';
|
|
12
|
-
import type { LauncherUpdateStatus } from './update.ts';
|
|
12
|
+
import type { LauncherUpdatePlan, LauncherUpdateStatus } from './update.ts';
|
|
13
13
|
/** Retained apply-progress lines (ring tail; npm output is ephemeral). */
|
|
14
14
|
export declare const UPDATE_OUTPUT_CAP = 800;
|
|
15
15
|
/** Keep the newest UPDATE_OUTPUT_CAP lines of streamed update output. */
|
|
16
16
|
export declare function clipUpdateLines(lines: readonly string[]): readonly string[];
|
|
17
|
+
/** One in-flight `update --apply`. Closing the panel must not start a second. */
|
|
18
|
+
interface UpdateApplyJob {
|
|
19
|
+
readonly promise: Promise<number>;
|
|
20
|
+
readonly lines: string[];
|
|
21
|
+
readonly lineListeners: Set<(line: string) => void>;
|
|
22
|
+
}
|
|
23
|
+
/** True while an apply child is alive, even if the /update panel is closed. */
|
|
24
|
+
export declare function isUpdateApplyRunning(): boolean;
|
|
25
|
+
/** Subscribe to apply-running changes (frozen-hint "esc waits"). */
|
|
26
|
+
export declare function subscribeUpdateApplyRunning(listener: (running: boolean) => void): () => void;
|
|
27
|
+
/** The live apply job, if any: lines already streamed plus the shared promise. */
|
|
28
|
+
export declare function currentUpdateApply(): UpdateApplyJob | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Run `apply` once. A second call while the child is alive reuses the same
|
|
31
|
+
* promise and replays buffered lines — closing /update and opening it again
|
|
32
|
+
* must not spawn a second `npm install`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function runUpdateApply(apply: (onLine: (line: string) => void, plan: LauncherUpdatePlan) => Promise<number>, plan: LauncherUpdatePlan, onLine?: (line: string) => void): Promise<number>;
|
|
35
|
+
/** Drop a leftover job between tests. */
|
|
36
|
+
export declare function resetUpdateApply(): void;
|
|
17
37
|
/** One display row of the update surface. */
|
|
18
38
|
export interface UpdateRow {
|
|
19
39
|
readonly key: string;
|
|
@@ -47,3 +67,4 @@ export declare function UpdatePanel({ probe, apply, close, notify }: {
|
|
|
47
67
|
/** One bounded cross-surface notice (phase completions). */
|
|
48
68
|
notify: (text: string, tone?: 'info' | 'warning' | 'error') => void;
|
|
49
69
|
}): ReactElement;
|
|
70
|
+
export {};
|
package/lib/types/version.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** Installed dsh-code version exposed by the terminal header. */
|
|
2
2
|
/** Version of the installed dsh-code package. */
|
|
3
3
|
export declare const DSH_CODE_VERSION: string;
|
|
4
|
+
/** Header brand line: hide a missing-manifest fallback so we never paint v0.0.0. */
|
|
5
|
+
export declare function headerBrandTitle(version?: string): string;
|
|
4
6
|
/**
|
|
5
7
|
* Resolve the running dsh CLI host's version from its entry file
|
|
6
8
|
* (`process.argv[1]`, e.g. `.../@deepseek-ai/dsh/lib/bin.js` or a PATH
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-code",
|
|
3
3
|
"description": "DeepSeek Harness CLI core bundle: interactive coding terminal, durable sessions, and model management for dsh --profile cli",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.0",
|
|
5
|
+
"packageManager": "pnpm@12.4.1",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"bin": {
|
|
7
8
|
"deepseek": "./bin/deepseek.mjs",
|
|
@@ -57,16 +58,17 @@
|
|
|
57
58
|
},
|
|
58
59
|
"scripts": {
|
|
59
60
|
"build": "tsdown && tsc -p tsconfig.json",
|
|
61
|
+
"audit:prod": "pnpm audit --prod --audit-level moderate",
|
|
60
62
|
"test": "vitest run",
|
|
61
63
|
"test:coverage": "vitest run --coverage",
|
|
62
64
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
63
65
|
"typecheck:tests": "tsc -p tsconfig.test.json --noEmit",
|
|
64
|
-
"lint": "eslint .",
|
|
65
|
-
"lint:fix": "eslint . --fix",
|
|
66
|
+
"lint": "eslint . --max-warnings 0",
|
|
67
|
+
"lint:fix": "eslint . --fix --max-warnings 0",
|
|
66
68
|
"verify": "pnpm lint && pnpm typecheck && pnpm typecheck:tests && pnpm test",
|
|
67
69
|
"gen:whale": "tsx scripts/gen-whale-glyph.ts",
|
|
68
70
|
"prepare": "pnpm build",
|
|
69
|
-
"prepublishOnly": "pnpm
|
|
71
|
+
"prepublishOnly": "pnpm audit:prod && pnpm exec tsx scripts/check-release.ts --package-version && pnpm verify && pnpm build"
|
|
70
72
|
},
|
|
71
73
|
"engines": {
|
|
72
74
|
"node": "^22.19 || >=24"
|
|
@@ -314,7 +316,7 @@
|
|
|
314
316
|
"@vitest/coverage-v8": "^3.2.7",
|
|
315
317
|
"eslint": "^10.10.0",
|
|
316
318
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
317
|
-
"js-yaml": "^4.3.
|
|
319
|
+
"js-yaml": "^4.3.2",
|
|
318
320
|
"tsdown": "^0.22.2",
|
|
319
321
|
"tsx": "^4.22.4",
|
|
320
322
|
"typescript": "^5.9.0",
|