dsh-code 1.0.5 → 1.0.7
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 +338 -286
- package/README.md +68 -16
- package/bin/deepseek.mjs +204 -4
- package/cordis.patch.yml +105 -7
- package/lib/index.mjs +2148 -439
- package/lib/session-query.mjs +149 -0
- package/lib/types/app.d.ts +34 -8
- package/lib/types/attachments.d.ts +36 -4
- package/lib/types/index.d.ts +38 -2
- package/lib/types/kernel-panels.d.ts +23 -0
- package/lib/types/provider-settings.d.ts +6 -11
- package/lib/types/render/animations.d.ts +74 -7
- package/lib/types/render/editor.d.ts +4 -3
- package/lib/types/render/export.d.ts +0 -6
- package/lib/types/render/fuzzy.d.ts +21 -0
- package/lib/types/render/ime-cursor.d.ts +60 -0
- package/lib/types/render/projection.d.ts +80 -4
- package/lib/types/render/status.d.ts +1 -1
- package/lib/types/session-directory.d.ts +48 -13
- package/lib/types/session-query.d.ts +92 -0
- package/lib/types/store.d.ts +3 -0
- package/lib/types/terminal-title.d.ts +58 -0
- package/lib/types/update-panel.d.ts +49 -0
- package/lib/types/update.d.ts +66 -0
- package/package.json +307 -162
- package/src/app.ts +730 -266
- package/src/attachments.ts +110 -11
- package/src/commands.ts +35 -5
- package/src/index.ts +1986 -1779
- package/src/internals.ts +66 -40
- package/src/kernel-panels.ts +89 -3
- package/src/provider-settings.ts +12 -12
- package/src/render/animations.ts +606 -403
- package/src/render/editor.ts +5 -4
- package/src/render/export.ts +13 -3
- package/src/render/fuzzy.ts +83 -0
- package/src/render/ime-cursor.ts +147 -0
- package/src/render/projection.ts +1974 -1621
- package/src/render/status.ts +18 -4
- package/src/session-directory.ts +94 -16
- package/src/session-query.ts +235 -0
- package/src/skills.ts +23 -9
- package/src/store.ts +39 -1
- package/src/subagents.ts +26 -3
- package/src/terminal-title.ts +173 -0
- package/src/update-panel.ts +246 -0
- package/src/update.ts +110 -0
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @deepseek-ai/dsh-tui/render/projection
|
|
8
8
|
*/
|
|
9
|
-
import { type ImageBlock, type MessageId } from '@deepseek-ai/dsh-llm';
|
|
9
|
+
import { type ImageBlock, type MessageId, type StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
10
|
+
import type { FileAttachmentRef } from '@deepseek-ai/dsh-attachment';
|
|
10
11
|
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
11
12
|
import type { TodoItem } from '@deepseek-ai/dsh-tool-todo';
|
|
12
13
|
import { type ToolDetail } from './tool-detail.ts';
|
|
@@ -20,6 +21,8 @@ export interface UserEntry {
|
|
|
20
21
|
notice: boolean;
|
|
21
22
|
/** Durable image references carried by this prompt. */
|
|
22
23
|
images?: readonly ImageBlock['attachment'][];
|
|
24
|
+
/** Durable file references carried by this prompt (0.1.5 file blocks). */
|
|
25
|
+
files?: readonly FileAttachmentRef[];
|
|
23
26
|
}
|
|
24
27
|
/** One user message waiting in the agent inbox (the web's queued-message row). */
|
|
25
28
|
export interface PendingEntry {
|
|
@@ -32,6 +35,8 @@ export interface PendingEntry {
|
|
|
32
35
|
text: string;
|
|
33
36
|
/** Durable image references queued with this prompt. */
|
|
34
37
|
images?: readonly ImageBlock['attachment'][];
|
|
38
|
+
/** Durable file references queued with this prompt (0.1.5 file blocks). */
|
|
39
|
+
files?: readonly FileAttachmentRef[];
|
|
35
40
|
}
|
|
36
41
|
/** One authoritative assembled assistant reply. */
|
|
37
42
|
export interface AssistantEntry {
|
|
@@ -214,11 +219,43 @@ export interface TranscriptStats {
|
|
|
214
219
|
*/
|
|
215
220
|
reasoningEffort: string;
|
|
216
221
|
}
|
|
222
|
+
/** One active reminder folded from durable `schedule/change` events. */
|
|
223
|
+
export interface ScheduleRow {
|
|
224
|
+
readonly id: string;
|
|
225
|
+
readonly kind: 'after' | 'at' | 'every';
|
|
226
|
+
readonly prompt: string;
|
|
227
|
+
/** Next due time (epoch ms); the /schedule panel derives overdue/relative labels. */
|
|
228
|
+
readonly targetAt: number;
|
|
229
|
+
/** Recurrence seconds for 'every' rows, undefined otherwise. */
|
|
230
|
+
readonly everySeconds?: number;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* The durable `schedule/change` payload shape this fold consumes. Upstream
|
|
234
|
+
* strict-decodes the whole transition stream before appending, so unknown
|
|
235
|
+
* ids here are corrupt-input edges that degrade to a no-op.
|
|
236
|
+
*/
|
|
237
|
+
export interface ScheduleChangeLike {
|
|
238
|
+
readonly operation: 'create' | 'delete' | 'dispatch';
|
|
239
|
+
readonly schedule?: {
|
|
240
|
+
readonly id: string;
|
|
241
|
+
readonly kind: 'after' | 'at' | 'every';
|
|
242
|
+
readonly prompt: string;
|
|
243
|
+
readonly afterSeconds?: number;
|
|
244
|
+
readonly everySeconds?: number;
|
|
245
|
+
readonly scheduledAt: string;
|
|
246
|
+
};
|
|
247
|
+
readonly id?: string;
|
|
248
|
+
readonly acceptedAt?: string;
|
|
249
|
+
}
|
|
250
|
+
/** Fold one `schedule/change` into the active-reminder list (create/delete/dispatch). */
|
|
251
|
+
export declare function applyScheduleChange(rows: readonly ScheduleRow[], data: ScheduleChangeLike): readonly ScheduleRow[];
|
|
252
|
+
/** First anchor-aligned target after `acceptedAt`, stepping from the previous aligned target. */
|
|
253
|
+
export declare function nextEveryTarget(previousTarget: number, acceptedAt: number, everySeconds: number): number;
|
|
217
254
|
/** The complete TUI transcript view for one session. */
|
|
218
255
|
export interface TranscriptView {
|
|
219
256
|
/** Settled entries in log order. */
|
|
220
257
|
entries: readonly TranscriptEntry[];
|
|
221
|
-
/** Bounded text tail accumulated from
|
|
258
|
+
/** Bounded text tail accumulated from live stream frames since the last settlement. */
|
|
222
259
|
streaming: string;
|
|
223
260
|
/** Bounded thinking tail accumulated from reasoning deltas since the last flush. */
|
|
224
261
|
streamingReasoning: string;
|
|
@@ -249,10 +286,19 @@ export interface TranscriptView {
|
|
|
249
286
|
permission: string;
|
|
250
287
|
/** Latest session title folded from the last `session/title` event, empty before one. */
|
|
251
288
|
title: string;
|
|
289
|
+
/**
|
|
290
|
+
* Effective system prompt assembled from `system/message` surface nodes
|
|
291
|
+
* (v3): the head node's text joined with every later non-empty node, blank
|
|
292
|
+
* lines between. Empty before the first system node or when every node is
|
|
293
|
+
* empty ("no system prompt").
|
|
294
|
+
*/
|
|
295
|
+
systemPrompt: string;
|
|
252
296
|
/** Sandbox-mode override folded from the last `sandbox/mode` event, empty when never switched. */
|
|
253
297
|
sandbox: string;
|
|
254
298
|
/** Current long-running goal folded from the last `goal/change`, undefined when cleared. */
|
|
255
299
|
goal: GoalFold | undefined;
|
|
300
|
+
/** Active reminders folded from `schedule/change` events, oldest target first at render. */
|
|
301
|
+
schedules: readonly ScheduleRow[];
|
|
256
302
|
/**
|
|
257
303
|
* Ordered live message ids per inbox target, mirrored from
|
|
258
304
|
* `agent/inbox/spliced` exactly like the upstream Inbox projection — the
|
|
@@ -279,12 +325,16 @@ export interface TranscriptView {
|
|
|
279
325
|
turnFiles: Map<number, Set<string>>;
|
|
280
326
|
turnSteps: Map<number, string>;
|
|
281
327
|
turnTools: Map<number, Set<string>>;
|
|
328
|
+
/** Live `system/message` surface nodes by event seq (empty string = an empty node). */
|
|
329
|
+
systemNodes: Map<number, string>;
|
|
282
330
|
};
|
|
283
331
|
}
|
|
284
332
|
/** Human-readable bounded image labels for transcript, inspector, and export surfaces. */
|
|
285
333
|
export declare function imageLabels(images: readonly ImageBlock['attachment'][] | undefined): string;
|
|
286
|
-
/**
|
|
287
|
-
export declare function
|
|
334
|
+
/** Human-readable bounded file labels for the same surfaces (0.1.5 file blocks). */
|
|
335
|
+
export declare function fileLabels(files: readonly FileAttachmentRef[] | undefined): string;
|
|
336
|
+
/** Prompt text with its durable image and file labels, without exposing local paths or bytes. */
|
|
337
|
+
export declare function promptDisplayText(entry: Pick<UserEntry | PendingEntry, 'text' | 'images' | 'files'>): string;
|
|
288
338
|
/** A fresh, empty transcript view. */
|
|
289
339
|
export declare function createTranscriptView(): TranscriptView;
|
|
290
340
|
/**
|
|
@@ -341,8 +391,10 @@ export interface ReplayAccumulator {
|
|
|
341
391
|
plan: boolean;
|
|
342
392
|
permission: string;
|
|
343
393
|
title: string;
|
|
394
|
+
systemPrompt: string;
|
|
344
395
|
sandbox: string;
|
|
345
396
|
goal: GoalFold | undefined;
|
|
397
|
+
schedules: readonly ScheduleRow[];
|
|
346
398
|
stats: TranscriptStats;
|
|
347
399
|
stepStart: Map<string, number>;
|
|
348
400
|
toolStart: Map<string, number>;
|
|
@@ -352,6 +404,8 @@ export interface ReplayAccumulator {
|
|
|
352
404
|
turnFiles: Map<number, Set<string>>;
|
|
353
405
|
turnSteps: Map<number, string>;
|
|
354
406
|
turnTools: Map<number, Set<string>>;
|
|
407
|
+
/** Live `system/message` surface nodes by event seq (empty string = an empty node). */
|
|
408
|
+
systemNodes: Map<number, string>;
|
|
355
409
|
/** Entry-level container operations performed so far (test instrumentation). */
|
|
356
410
|
ops: number;
|
|
357
411
|
}
|
|
@@ -400,6 +454,28 @@ export declare function snapshotReplayView(acc: ReplayAccumulator): TranscriptVi
|
|
|
400
454
|
* @returns the folded view.
|
|
401
455
|
*/
|
|
402
456
|
export declare function projectEvents(events: readonly SessionEvent[]): TranscriptView;
|
|
457
|
+
/**
|
|
458
|
+
* Fold one process-local assistant-stream chunk frame (session-log v2+ keeps
|
|
459
|
+
* durable logs settlement-only; live typing rides the `agent/assistant-stream`
|
|
460
|
+
* agent event). Same first-token anchoring the durable `assistant/chunk` event
|
|
461
|
+
* used to carry: the first non-empty delta anchors the TTFT and empty
|
|
462
|
+
* keep-alive deltas do not count. The caller maps the frame's attempt to the
|
|
463
|
+
* `turn:step` key (the start frame owns turn/step; chunk frames do not).
|
|
464
|
+
* @param acc - the live replay accumulator.
|
|
465
|
+
* @param key - the `turn:step` key the attempt's start frame declared.
|
|
466
|
+
* @param time - the frame's safe-integer timestamp.
|
|
467
|
+
* @param chunk - the model chunk the frame carries.
|
|
468
|
+
* @returns whether the accumulator changed (the store stays silent otherwise).
|
|
469
|
+
*/
|
|
470
|
+
export declare function applyAssistantStreamChunk(acc: ReplayAccumulator, key: string, time: number, chunk: StreamChunk): boolean;
|
|
471
|
+
/**
|
|
472
|
+
* Drop the live streaming tails without a settlement (an `agent/assistant-stream`
|
|
473
|
+
* end frame with an `abandoned` outcome, or a session switch). The next start
|
|
474
|
+
* frame rebuilds from scratch.
|
|
475
|
+
* @param acc - the live replay accumulator.
|
|
476
|
+
* @returns whether any tail text was discarded.
|
|
477
|
+
*/
|
|
478
|
+
export declare function clearAssistantStream(acc: ReplayAccumulator): boolean;
|
|
403
479
|
/**
|
|
404
480
|
* The append-only flush boundary for a transcript view: the count of entries
|
|
405
481
|
* no later event can remove. Entries at or beyond this index are mutable and
|
|
@@ -33,7 +33,7 @@ export declare function cacheHitPercent(usage: TranscriptStats['usage']): number
|
|
|
33
33
|
* Presentation tones for status spans; the footer maps each to a theme color
|
|
34
34
|
* (Codex status-line accents: model/path/branch/state/usage categories).
|
|
35
35
|
*/
|
|
36
|
-
export type StatusTone = 'model' | 'live' | 'path' | 'branch' | 'value' | 'label' | 'meta' | 'accent' | 'success' | 'warn' | 'error' | 'ctxFill';
|
|
36
|
+
export type StatusTone = 'model' | 'live' | 'path' | 'branch' | 'value' | 'label' | 'meta' | 'accent' | 'success' | 'plan' | 'warn' | 'error' | 'ctxFill';
|
|
37
37
|
/** One colored run inside the status bar. */
|
|
38
38
|
export interface StatusSpan {
|
|
39
39
|
text: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Lightweight session-directory projection for the /resume picker. */
|
|
2
|
-
import type
|
|
2
|
+
import { type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session';
|
|
3
3
|
export interface SessionRecord {
|
|
4
4
|
readonly header: SessionHeader;
|
|
5
5
|
readonly live: boolean;
|
|
@@ -77,23 +77,58 @@ export declare function projectSessionRows(records: readonly SessionRecord[], op
|
|
|
77
77
|
export declare function mergeSessionTitles(rows: readonly SessionRow[], observations: readonly TitleObservationResult[]): SessionRow[];
|
|
78
78
|
/**
|
|
79
79
|
* Encode a session id the way the JSONL backend does for its on-disk layout
|
|
80
|
-
* (`encodeSegment`: safe units literal, everything else `~XXXX`). Used
|
|
81
|
-
* validate
|
|
82
|
-
* any deletion touches the filesystem — a local copy of the pure upstream
|
|
80
|
+
* (`encodeSegment`: safe units literal, everything else `~XXXX`). Used to
|
|
81
|
+
* validate and derive session directories — a local copy of the pure upstream
|
|
83
82
|
* contract, kept in sync with `session-persistence-jsonl/src/format.ts`.
|
|
84
83
|
*/
|
|
85
84
|
export declare function encodeSessionSegment(raw: string): string;
|
|
86
|
-
/** The session-log artifact names the JSONL backend may create. */
|
|
87
|
-
export declare const SESSION_ARTIFACT_NAMES: readonly string[];
|
|
88
85
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
|
|
94
|
-
|
|
86
|
+
* Encode a project cwd the way the JSONL backend groups sessions on disk
|
|
87
|
+
* (`projectKey`: separators collapse to one `-`, everything else mirrors
|
|
88
|
+
* `encodeSegment`, bounded to 251 chars). A local copy of the pure upstream
|
|
89
|
+
* contract, kept in sync with `session-persistence-jsonl/src/format.ts`.
|
|
90
|
+
*/
|
|
91
|
+
export declare function encodeProjectKey(cwd: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* Derive one session's artifact directory under the JSONL backend root,
|
|
94
|
+
* mirroring the upstream `<root>/<projectKey(cwd)>/<encodeSegment(id)>/`
|
|
95
|
+
* layout (0.1.5 `sessionDir`/`projectDir`).
|
|
96
|
+
* @param root - the JSONL backend's configured session root.
|
|
97
|
+
* @param cwd - the session's pinned working directory, when the header has one.
|
|
98
|
+
* @param id - the session id.
|
|
99
|
+
* @returns the absolute session directory path.
|
|
100
|
+
*/
|
|
101
|
+
export declare function sessionDirectoryFor(root: string, cwd: string | undefined, id: string): string;
|
|
102
|
+
/**
|
|
103
|
+
* The canonical session-log artifact filenames the JSONL backend may create:
|
|
104
|
+
* format v0 writes the bare `session.jsonl` name; v1+ write
|
|
105
|
+
* `session.vN.jsonl`, each generation optionally zstd-compressed. Multiple
|
|
106
|
+
* immutable generations may coexist in one session directory (0.1.5). The
|
|
107
|
+
* range follows the installed session package's `SESSION_FORMAT_VERSION`, so
|
|
108
|
+
* a future generation joins the enumeration with the dependency bump.
|
|
109
|
+
*/
|
|
110
|
+
export declare function sessionArtifactNames(): readonly string[];
|
|
111
|
+
/** True for one canonical session-log artifact filename the backend may own. */
|
|
112
|
+
export declare function isSessionArtifactName(name: string): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Guard a derived session directory before deletion (codex's scoped-path
|
|
115
|
+
* check, adapted to the JSONL layout): the directory's base name must be
|
|
116
|
+
* exactly `encodeSegment(id)` beneath its project grouping.
|
|
117
|
+
* @param dir - the derived session artifact directory.
|
|
118
|
+
* @param id - the session id the directory claims to belong to.
|
|
119
|
+
* @returns the guarded directory, or undefined when the layout is unexpected.
|
|
120
|
+
*/
|
|
121
|
+
export declare function sessionArtifactDirectory(dir: string, id: string): string | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* The JSONL backend's configured session root, when the mounted backend
|
|
124
|
+
* exposes one. The upstream service contract dropped `locate()` in 0.1.5
|
|
125
|
+
* (artifact paths are backend-private; only refusal diagnostics carry them),
|
|
126
|
+
* so the TUI derives artifact paths from the backend's public plugin config.
|
|
127
|
+
* Backends without a JSONL-style config (or a foreign shape) yield undefined
|
|
128
|
+
* and callers degrade: mtime sorting falls back to createdAt and /delete
|
|
129
|
+
* refuses, exactly as before.
|
|
95
130
|
*/
|
|
96
|
-
export declare function
|
|
131
|
+
export declare function jsonlSessionRoot(persistence: unknown): string | undefined;
|
|
97
132
|
/**
|
|
98
133
|
* Collect one session's deletion subtree: the id plus every record whose
|
|
99
134
|
* parent chain leads to it (codex deletes subagent threads with their root).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skip-tolerant session-query engine for this terminal.
|
|
3
|
+
*
|
|
4
|
+
* The upstream SqliteSessionQueryEngine reconciliation observes EVERY
|
|
5
|
+
* persisted session before each search, and ONE unreadable source (for
|
|
6
|
+
* example a pre-release session artifact the frozen format codecs reject)
|
|
7
|
+
* fails the whole pass with SESSION_QUERY_PERSISTENCE_FAILED — every
|
|
8
|
+
* cross-session search dies because of a single old file nobody opened
|
|
9
|
+
* otherwise. This subclass overrides only the observation loop so an
|
|
10
|
+
* unreadable source is skipped with a warning and the rest of the corpus
|
|
11
|
+
* indexes normally; skipped sessions are retried on later reconciliations
|
|
12
|
+
* and rejoin automatically once a host that can read them is installed.
|
|
13
|
+
*
|
|
14
|
+
* Vendored surface note: `_observeStable` and its module-local helpers are
|
|
15
|
+
* private upstream; this file re-declares the observation loop against the
|
|
16
|
+
* pinned @deepseek-ai line (see package.json peers) and must be re-checked
|
|
17
|
+
* whenever that line moves. The engine class and the tool boundary also
|
|
18
|
+
* share ONE physical parent-package instance through this bundle, which
|
|
19
|
+
* restores instanceof-based typed error messages on the search path.
|
|
20
|
+
*/
|
|
21
|
+
import SqliteSessionQueryEngine, { type Config } from '@deepseek-ai/dsh-session-query-sqlite';
|
|
22
|
+
import { buildSessionEventSearchDocuments } from '@deepseek-ai/dsh-session-query';
|
|
23
|
+
import type { SessionEvent, SessionHeader, SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session';
|
|
24
|
+
import type { SessionPersistenceRevision, SessionPersistenceSnapshot } from '@deepseek-ai/dsh-session-persistence';
|
|
25
|
+
/** One observed session: detached header plus its derived search documents. */
|
|
26
|
+
interface ObservedSession {
|
|
27
|
+
header: SessionHeader;
|
|
28
|
+
inheritedEventCount: SessionLogOffset;
|
|
29
|
+
documents: readonly ReturnType<typeof buildSessionEventSearchDocuments>[number][];
|
|
30
|
+
fingerprint: string;
|
|
31
|
+
}
|
|
32
|
+
/** One persisted snapshot as the reconciliation sees it (loaded once readable). */
|
|
33
|
+
interface ObservedPersistedSession {
|
|
34
|
+
header: SessionHeader;
|
|
35
|
+
revision: SessionPersistenceRevision;
|
|
36
|
+
loaded?: ObservedSession;
|
|
37
|
+
/** Diagnosis for a cold read that failed; the session stays unindexed. */
|
|
38
|
+
unreadable?: string;
|
|
39
|
+
}
|
|
40
|
+
/** The engine-internal state the observation loop touches. */
|
|
41
|
+
export interface EngineSurface {
|
|
42
|
+
readonly ctx: {
|
|
43
|
+
sessions: {
|
|
44
|
+
list(): readonly {
|
|
45
|
+
header: SessionHeader;
|
|
46
|
+
inheritedEventCount: SessionLogOffset;
|
|
47
|
+
snapshotEvents(): readonly SessionEvent[];
|
|
48
|
+
id: SessionId;
|
|
49
|
+
}[];
|
|
50
|
+
get(id: SessionId): unknown;
|
|
51
|
+
};
|
|
52
|
+
logger?: {
|
|
53
|
+
warn(format: string, ...args: readonly unknown[]): void;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
readonly _persistenceBinding: {
|
|
57
|
+
readonly identity: symbol;
|
|
58
|
+
readonly service?: {
|
|
59
|
+
list(options?: {
|
|
60
|
+
readonly signal?: AbortSignal;
|
|
61
|
+
}): Promise<readonly SessionPersistenceSnapshot[]>;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
_lastPersistenceIdentity: symbol | undefined;
|
|
65
|
+
}
|
|
66
|
+
type ColdRead = (persistence: NonNullable<EngineSurface['_persistenceBinding']['service']>, id: SessionId, signal: AbortSignal | undefined) => Promise<{
|
|
67
|
+
header: SessionHeader;
|
|
68
|
+
inheritedEventCount: SessionLogOffset;
|
|
69
|
+
events: readonly SessionEvent[];
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* The skip-tolerant observation pass: structurally the upstream loop, with
|
|
73
|
+
* the per-source cold read wrapped so one unreadable session degrades to a
|
|
74
|
+
* warning instead of failing every search. Exported for unit tests with an
|
|
75
|
+
* injectable cold reader.
|
|
76
|
+
*/
|
|
77
|
+
export declare function observeStableWithSkip(engine: EngineSurface, indexed: ReadonlyMap<SessionId, {
|
|
78
|
+
revision: SessionPersistenceRevision;
|
|
79
|
+
}>, signal: AbortSignal | undefined, readCold?: ColdRead): Promise<{
|
|
80
|
+
persistenceBinding: EngineSurface['_persistenceBinding'];
|
|
81
|
+
persisted: Map<SessionId, ObservedPersistedSession>;
|
|
82
|
+
live: Map<SessionId, ObservedSession>;
|
|
83
|
+
}>;
|
|
84
|
+
declare const EngineBase: abstract new (ctx: never, config: Config) => EngineSurface & object;
|
|
85
|
+
/** The engine this bundle mounts in place of the base `session-query-sqlite` row. */
|
|
86
|
+
export declare class SkipTolerantSessionQueryEngine extends EngineBase {
|
|
87
|
+
_observeStable(indexed: ReadonlyMap<SessionId, {
|
|
88
|
+
revision: SessionPersistenceRevision;
|
|
89
|
+
}>, signal: AbortSignal | undefined): Promise<unknown>;
|
|
90
|
+
}
|
|
91
|
+
declare const _default: typeof SqliteSessionQueryEngine;
|
|
92
|
+
export default _default;
|
package/lib/types/store.d.ts
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
*
|
|
30
30
|
* @module @deepseek-ai/dsh-tui/store
|
|
31
31
|
*/
|
|
32
|
+
import type { AssistantStreamFrame } from '@deepseek-ai/dsh-agent';
|
|
32
33
|
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
33
34
|
import { type TranscriptView } from './render/projection.ts';
|
|
34
35
|
/** The externally readable, event-fed transcript store for one session. */
|
|
@@ -39,6 +40,8 @@ export interface TranscriptStore {
|
|
|
39
40
|
subscribe(listener: () => void): () => void;
|
|
40
41
|
/** Fold one session event; ignored events change nothing and notify nobody. */
|
|
41
42
|
apply(event: SessionEvent): void;
|
|
43
|
+
/** Fold one live assistant-stream frame; frames without visible deltas stay silent. */
|
|
44
|
+
applyStreamFrame(frame: AssistantStreamFrame): void;
|
|
42
45
|
/** Drop the folded view entirely (/clear): the next event starts a fresh one. */
|
|
43
46
|
reset(): void;
|
|
44
47
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal tab/window title management for the TUI.
|
|
3
|
+
*
|
|
4
|
+
* Terminals label their tab from the window title, which an application sets
|
|
5
|
+
* with an OSC 0 sequence; without one the tab shows the process name ("node").
|
|
6
|
+
* The title text is untrusted display content (session names arrive through
|
|
7
|
+
* events and user input), so it is sanitized before emission: control
|
|
8
|
+
* characters and bidi/invisible formatting codepoints are stripped, whitespace
|
|
9
|
+
* runs collapse to single spaces, and the result is bounded. Clearing writes
|
|
10
|
+
* an empty OSC payload and the terminal falls back to its own default; the
|
|
11
|
+
* previously set title is not portable to read back and is never restored.
|
|
12
|
+
*
|
|
13
|
+
* @module @deepseek-ai/dsh-code/terminal-title
|
|
14
|
+
*/
|
|
15
|
+
/** Tab label before a session carries a name. */
|
|
16
|
+
export declare const DEFAULT_TERMINAL_TITLE = "deepseek";
|
|
17
|
+
/** Practical upper bound on title length, in visible characters: long enough
|
|
18
|
+
* for session names, short enough for tab bars and window managers. */
|
|
19
|
+
export declare const MAX_TERMINAL_TITLE_CHARS = 240;
|
|
20
|
+
/** Normalize untrusted title text into one bounded display line: disallowed
|
|
21
|
+
* codepoints dropped, whitespace runs collapsed to single spaces, leading and
|
|
22
|
+
* trailing whitespace removed, length bounded. */
|
|
23
|
+
export declare function sanitizeTerminalTitle(text: string): string;
|
|
24
|
+
/** Build one OSC 0 title sequence. An empty sanitized title yields an empty
|
|
25
|
+
* string: emitting nothing is distinct from clearing, which is a separate
|
|
26
|
+
* policy decision made by the caller. */
|
|
27
|
+
export declare function terminalTitleSequence(text: string): string;
|
|
28
|
+
/** Clear the managed title with an empty OSC payload; the terminal falls back
|
|
29
|
+
* to its own default label. */
|
|
30
|
+
export declare function clearTerminalTitleSequence(): string;
|
|
31
|
+
/** Outcome of the VS Code settings alignment. */
|
|
32
|
+
export interface VsCodeTitleSettingResult {
|
|
33
|
+
wrote: boolean;
|
|
34
|
+
reason?: 'not-vscode' | 'unparseable' | 'key-present' | 'error';
|
|
35
|
+
}
|
|
36
|
+
/** VS Code renders an application-set tab title only when
|
|
37
|
+
* "terminal.integrated.tabs.title" maps to the sequence variable; the editor
|
|
38
|
+
* default shows the process name instead ("node" for a Node CLI). Inside a VS
|
|
39
|
+
* Code integrated terminal, align the user settings once: if the key is
|
|
40
|
+
* absent, insert it and keep a one-shot backup of the original file. A value
|
|
41
|
+
* the user already set is never overwritten, an unparseable file is never
|
|
42
|
+
* touched, and every failure degrades to a no-op - the OSC and process-title
|
|
43
|
+
* channels keep working everywhere else. */
|
|
44
|
+
export declare function ensureVsCodeTabTitleSetting(options?: {
|
|
45
|
+
env?: NodeJS.ProcessEnv;
|
|
46
|
+
settingsFile?: string;
|
|
47
|
+
isTTY?: boolean;
|
|
48
|
+
}): VsCodeTitleSettingResult;
|
|
49
|
+
/**
|
|
50
|
+
* Keep the terminal tab label on `title` (sanitized; empty titles leave the
|
|
51
|
+
* current label alone). Two delivery channels run in parallel: the OSC 0
|
|
52
|
+
* sequence to stdout, and the host process title. Writes are deduplicated by
|
|
53
|
+
* title, and on unmount the managed title is cleared and the process title
|
|
54
|
+
* restored so the host shell regains its default label.
|
|
55
|
+
*/
|
|
56
|
+
export declare function useTerminalTitle(title: string, options?: {
|
|
57
|
+
clearOnUnmount?: boolean;
|
|
58
|
+
}): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The /update panel: one bounded surface over the launcher update
|
|
3
|
+
* pipeline. The panel never decides versions itself — it renders the
|
|
4
|
+
* launcher `update --json` probe (plan plus refusals), takes one
|
|
5
|
+
* confirmation, then streams `update --apply` progress and reports the
|
|
6
|
+
* result with a restart hint. Every alignment guarantee (host pinned to
|
|
7
|
+
* the release peers line, companion plugins carried, downgrade and
|
|
8
|
+
* local-checkout refusals) lives in the launcher and is only displayed
|
|
9
|
+
* here.
|
|
10
|
+
*/
|
|
11
|
+
import { type ReactElement } from 'react';
|
|
12
|
+
import type { LauncherUpdateStatus } from './update.ts';
|
|
13
|
+
/** Retained apply-progress lines (ring tail; npm output is ephemeral). */
|
|
14
|
+
export declare const UPDATE_OUTPUT_CAP = 800;
|
|
15
|
+
/** Keep the newest UPDATE_OUTPUT_CAP lines of streamed update output. */
|
|
16
|
+
export declare function clipUpdateLines(lines: readonly string[]): readonly string[];
|
|
17
|
+
/** One display row of the update surface. */
|
|
18
|
+
export interface UpdateRow {
|
|
19
|
+
readonly key: string;
|
|
20
|
+
readonly text: string;
|
|
21
|
+
readonly tone?: 'ok' | 'warn' | 'error' | 'dim';
|
|
22
|
+
}
|
|
23
|
+
/** The plan view derived from one probe: facts to show and whether apply may run. */
|
|
24
|
+
export interface UpdatePlanView {
|
|
25
|
+
readonly rows: readonly UpdateRow[];
|
|
26
|
+
readonly runnable: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Rendered facts of one probe: current/target versions, actions, refusals. */
|
|
29
|
+
export declare function updatePlanView(status: LauncherUpdateStatus): UpdatePlanView;
|
|
30
|
+
/** Panel lifecycle phases; the footer names the keys each phase accepts. */
|
|
31
|
+
export type UpdatePhase = 'probe' | 'error' | 'plan' | 'apply' | 'done';
|
|
32
|
+
/** Footer hint line per phase; the plan phase names the confirm key only when runnable. */
|
|
33
|
+
export declare function updateFooter(phase: UpdatePhase, runnable: boolean, upToDate: boolean): string;
|
|
34
|
+
/**
|
|
35
|
+
* The /update surface: probe on open (and on r), confirm with enter/y,
|
|
36
|
+
* stream the aligned apply, and land on a bounded result view. Escape is
|
|
37
|
+
* locked while the apply child runs — killing npm mid-install is exactly
|
|
38
|
+
* the half-updated state this command exists to prevent.
|
|
39
|
+
*/
|
|
40
|
+
export declare function UpdatePanel({ probe, apply, close, notify }: {
|
|
41
|
+
/** Read-only probe of the launcher update status (never installs). */
|
|
42
|
+
probe(): Promise<LauncherUpdateStatus>;
|
|
43
|
+
/** Run the aligned update; streams sanitized progress lines. */
|
|
44
|
+
apply(onLine: (line: string) => void): Promise<number>;
|
|
45
|
+
/** Close the panel (App keeps ownership of the flag). */
|
|
46
|
+
close(): void;
|
|
47
|
+
/** One bounded cross-surface notice (phase completions). */
|
|
48
|
+
notify(text: string, tone?: 'info' | 'warning' | 'error'): void;
|
|
49
|
+
}): ReactElement;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child-process adapter for the launcher update pipeline. The launcher
|
|
3
|
+
* (bin/deepseek.mjs) stays the single owner of update semantics — plan,
|
|
4
|
+
* guards, and the aligned install sequence — so the TUI only spawns
|
|
5
|
+
* `update --json` (read-only probe) and `update --apply` (streamed run)
|
|
6
|
+
* and never re-implements version-line decisions.
|
|
7
|
+
*/
|
|
8
|
+
import { type ChildProcess } from 'node:child_process';
|
|
9
|
+
/** Spawn double acceptable to the adapter (tests inject an EventEmitter). */
|
|
10
|
+
export type SpawnLike = (command: string, args: readonly string[], options: {
|
|
11
|
+
readonly stdio: readonly string[];
|
|
12
|
+
readonly windowsHide: boolean;
|
|
13
|
+
}) => ChildProcess;
|
|
14
|
+
/** Structured `update --json` payload; mirrors the launcher buildUpdateStatus. */
|
|
15
|
+
export interface LauncherUpdateStatus {
|
|
16
|
+
readonly code: {
|
|
17
|
+
readonly running: string;
|
|
18
|
+
readonly latest: string | null;
|
|
19
|
+
};
|
|
20
|
+
readonly host: {
|
|
21
|
+
readonly installed: string | null;
|
|
22
|
+
readonly targetLine: string | null;
|
|
23
|
+
};
|
|
24
|
+
readonly profile: {
|
|
25
|
+
readonly spec: string | null;
|
|
26
|
+
readonly mounted: string | null;
|
|
27
|
+
readonly localCheckout: boolean;
|
|
28
|
+
};
|
|
29
|
+
readonly plan: {
|
|
30
|
+
readonly dshSpec: string;
|
|
31
|
+
readonly codeSpec: string;
|
|
32
|
+
readonly pluginSpecs: readonly string[];
|
|
33
|
+
};
|
|
34
|
+
readonly blockers: {
|
|
35
|
+
readonly registry: string | null;
|
|
36
|
+
readonly downgrade: boolean;
|
|
37
|
+
readonly localCheckout: readonly string[] | null;
|
|
38
|
+
};
|
|
39
|
+
readonly upToDate: boolean;
|
|
40
|
+
}
|
|
41
|
+
/** The launcher entrypoint that ships beside this bundle (lib/../bin). */
|
|
42
|
+
export declare function launcherUpdateCommand(args: readonly string[], moduleUrl?: string): {
|
|
43
|
+
readonly command: string;
|
|
44
|
+
readonly args: string[];
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Split a streamed chunk sequence into complete display lines: CR is
|
|
48
|
+
* stripped, a chunk boundary may split a line, and the trailing partial
|
|
49
|
+
* stays pending until its newline arrives (npm writes whole lines, but a
|
|
50
|
+
* pipe may cut anywhere). Blank lines carry no progress information and
|
|
51
|
+
* are dropped so the panel budget is not spent on gaps.
|
|
52
|
+
*/
|
|
53
|
+
export declare function createLineSplitter(onLine: (line: string) => void): (chunk: string) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Probe the aligned update status. Read-only: `update --json` never
|
|
56
|
+
* installs anything. The probe is bounded (npm view may hang on a broken
|
|
57
|
+
* network) and resolves with the parsed status.
|
|
58
|
+
*/
|
|
59
|
+
export declare function probeLauncherUpdate(spawnProcess?: SpawnLike): Promise<LauncherUpdateStatus>;
|
|
60
|
+
/**
|
|
61
|
+
* Run the aligned update (`update --apply`) as a child process and stream
|
|
62
|
+
* its sanitized progress lines to the caller. Resolves with the child
|
|
63
|
+
* exit code (0 success); rejects only when the process could not start.
|
|
64
|
+
* No timeout: an npm install may legitimately take minutes.
|
|
65
|
+
*/
|
|
66
|
+
export declare function applyLauncherUpdate(onLine: (line: string) => void, spawnProcess?: SpawnLike): Promise<number>;
|