dsh-code 1.0.3 → 1.0.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/README.md +293 -285
- package/bin/deepseek.mjs +245 -12
- package/cordis.patch.yml +12 -14
- package/lib/index.mjs +1939 -903
- package/lib/types/app.d.ts +11 -2
- package/lib/types/commands.d.ts +13 -0
- package/lib/types/git-workflow.d.ts +7 -2
- package/lib/types/history.d.ts +18 -11
- package/lib/types/index.d.ts +28 -0
- package/lib/types/input-split.d.ts +54 -0
- package/lib/types/kernel-panels.d.ts +3 -1
- package/lib/types/keyboard.d.ts +8 -0
- package/lib/types/presets.d.ts +4 -1
- package/lib/types/provider-settings.d.ts +77 -0
- package/lib/types/questions.d.ts +16 -12
- package/lib/types/render/projection.d.ts +9 -2
- package/lib/types/render/status.d.ts +22 -15
- package/lib/types/settings-file.d.ts +8 -0
- package/lib/types/skills.d.ts +1 -1
- package/package.json +49 -46
- package/src/app.ts +5459 -4900
- package/src/approval.ts +8 -3
- package/src/authorization-panel.ts +2 -4
- package/src/commands.ts +27 -3
- package/src/git-workflow.ts +29 -10
- package/src/history.ts +22 -13
- package/src/index.ts +203 -61
- package/src/input-split.ts +191 -0
- package/src/internals.ts +26 -8
- package/src/kernel-panels.ts +26 -10
- package/src/keyboard.ts +123 -88
- package/src/mentions.ts +42 -9
- package/src/permissions.ts +1 -1
- package/src/presets.ts +19 -6
- package/src/provider-settings.ts +204 -0
- package/src/questions.ts +58 -55
- package/src/render/export.ts +7 -7
- package/src/render/lines.ts +24 -12
- package/src/render/markdown.ts +15 -13
- package/src/render/projection.ts +101 -13
- package/src/render/status.ts +76 -71
- package/src/render/text.ts +9 -3
- package/src/settings-file.ts +38 -6
- package/src/skills.ts +19 -6
- package/src/theme-panel.ts +79 -72
package/lib/types/app.d.ts
CHANGED
|
@@ -21,9 +21,9 @@ import { type ThemeName } from './theme.ts';
|
|
|
21
21
|
import type { TranscriptStore } from './store.ts';
|
|
22
22
|
import { type TranscriptEntry } from './render/projection.ts';
|
|
23
23
|
import type { ApprovalStore } from './approval.ts';
|
|
24
|
-
import type
|
|
24
|
+
import { type CommandsView } from './commands.ts';
|
|
25
25
|
import type { ModelDirectory, ModelRow } from './models.ts';
|
|
26
|
-
import type
|
|
26
|
+
import { type DiscoveredModelView, type ProviderConfiguration, type ProviderSettingsDirectory, type ProviderTargetView } from './provider-settings.ts';
|
|
27
27
|
import type { QuestionStore } from './questions.ts';
|
|
28
28
|
import type { SkillsView, SkillRow } from './skills.ts';
|
|
29
29
|
import { type MentionCandidate } from './mentions.ts';
|
|
@@ -108,6 +108,15 @@ export interface AppProps {
|
|
|
108
108
|
removeModelProvider?(target: ProviderTargetView): Promise<void>;
|
|
109
109
|
/** Save endpoint and explicit model capacities through the provider profile. */
|
|
110
110
|
saveModelProviderConfiguration?(target: ProviderTargetView, configuration: ProviderConfiguration): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Interrogate the provider's real endpoint (typed key wins over the stored
|
|
113
|
+
* credential) for the models it actually serves — the discovery stage of
|
|
114
|
+
* the provider setup page.
|
|
115
|
+
*/
|
|
116
|
+
discoverModelProvider?(target: ProviderTargetView, request: {
|
|
117
|
+
readonly apiKey?: string;
|
|
118
|
+
readonly baseURL?: string;
|
|
119
|
+
}, signal?: AbortSignal): Promise<readonly DiscoveredModelView[]>;
|
|
111
120
|
/** Provider authorization flows and value-free stored-record facts. */
|
|
112
121
|
loadProviderAuthorizations?(): Promise<ProviderAuthorizationDirectory>;
|
|
113
122
|
subscribeProviderAuthorizations?(listener: () => void): () => void;
|
package/lib/types/commands.d.ts
CHANGED
|
@@ -37,3 +37,16 @@ export declare function watchCommands(ctx: Context): CommandsView;
|
|
|
37
37
|
* @returns true when the line parses as `/name` or `/name input`.
|
|
38
38
|
*/
|
|
39
39
|
export declare function isSlashLine(line: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The submission payload for one composer line. Trim is a blank check, not a
|
|
42
|
+
* rewrite: an ordinary prompt keeps its exact leading indentation, inner
|
|
43
|
+
* layout, and trailing spaces (pasted code must reach the model verbatim).
|
|
44
|
+
* Only trailing line terminators are stripped — a draft's final newline is a
|
|
45
|
+
* paste/Enter artifact (an open bracketed paste turns Enter into an inserted
|
|
46
|
+
* newline), never deliberate content. A syntactic slash line still normalizes
|
|
47
|
+
* fully so command routing stays stable (completion inserts a trailing space
|
|
48
|
+
* after `/name`).
|
|
49
|
+
* @param line - the complete draft text.
|
|
50
|
+
* @returns the text to submit verbatim.
|
|
51
|
+
*/
|
|
52
|
+
export declare function submissionPayload(line: string): string;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Read-only Git inspection used by /diff and /review. Every diff
|
|
3
|
+
* invocation carries --no-ext-diff and --no-textconv, so configured
|
|
4
|
+
* external diff drivers and text converters can never execute as a
|
|
5
|
+
* side effect of reading a diff.
|
|
6
|
+
*/
|
|
2
7
|
export interface GitDiffSpec {
|
|
3
8
|
readonly label: string;
|
|
4
9
|
readonly args: readonly string[];
|
|
@@ -18,7 +23,7 @@ export declare function parseGitDiffFiles(text: string): readonly GitDiffFile[];
|
|
|
18
23
|
/** Parse the intentionally small, option-safe /diff argument vocabulary. */
|
|
19
24
|
export declare function parseGitDiffSpec(argument: string): GitDiffSpec;
|
|
20
25
|
/**
|
|
21
|
-
* Load one complete textual diff without invoking external
|
|
26
|
+
* Load one complete textual diff without invoking external programs.
|
|
22
27
|
* @param signal - aborted by the caller on session switches/quit, killing the
|
|
23
28
|
* git subprocess instead of letting a stale repository's diff land later.
|
|
24
29
|
*/
|
package/lib/types/history.d.ts
CHANGED
|
@@ -21,14 +21,21 @@ export declare function serializeHistoryEntry(text: string): string;
|
|
|
21
21
|
*/
|
|
22
22
|
export declare function parseHistoryFile(raw: string, max?: number): readonly string[];
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
24
|
+
* The append unit for the persistent file: one JSON line, so a multi-line
|
|
25
|
+
* draft still occupies exactly one physical line. Each submission appends
|
|
26
|
+
* this unit at the end of the file, so concurrent terminals add entries
|
|
27
|
+
* after each other. Node chunks one append at 512 KiB: a pasted entry
|
|
28
|
+
* beyond that size could interleave mid-line with another writer's
|
|
29
|
+
* chunks, and the damaged line then drops out at the next parse —
|
|
30
|
+
* recall tolerates the loss by design.
|
|
31
|
+
*/
|
|
32
|
+
export declare function historyLine(text: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Whether the file on disk differs from its canonical form (deduped and
|
|
35
|
+
* capped). True means stale lines have accumulated and the next boot
|
|
36
|
+
* should rewrite it once, atomically.
|
|
30
37
|
*/
|
|
31
|
-
export declare function
|
|
38
|
+
export declare function needsCompaction(raw: string, max?: number): boolean;
|
|
32
39
|
/**
|
|
33
40
|
* Record one in-session submission: empty text is ignored and an adjacent
|
|
34
41
|
* duplicate collapses (Codex `record_local_submission` semantics). The local
|
|
@@ -40,10 +47,10 @@ export declare function appendHistoryContent(current: string, text: string, max?
|
|
|
40
47
|
*/
|
|
41
48
|
export declare function recordLocalEntry(local: readonly string[], text: string, max?: number): readonly string[];
|
|
42
49
|
/**
|
|
43
|
-
* Serialize a capped entry list to the history file format (one JSON line
|
|
44
|
-
* entry, trailing newline). The
|
|
45
|
-
*
|
|
46
|
-
*
|
|
50
|
+
* Serialize a capped entry list to the history file format (one JSON line
|
|
51
|
+
* per entry, trailing newline). The boot-time compaction writes this
|
|
52
|
+
* canonical form once when stale lines have accumulated; submissions
|
|
53
|
+
* themselves only ever append a single line.
|
|
47
54
|
* @param entries - the entries to persist, oldest first.
|
|
48
55
|
* @returns the file content, '' for an empty list.
|
|
49
56
|
*/
|
package/lib/types/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { Context } from '@deepseek-ai/cordis';
|
|
12
12
|
import z from '@deepseek-ai/schemastery';
|
|
13
|
+
import { type ImageBlock } from '@deepseek-ai/dsh-llm';
|
|
13
14
|
import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session';
|
|
14
15
|
import type { SessionPersistence } from '@deepseek-ai/dsh-session-persistence';
|
|
15
16
|
import type { TuiStartup } from './startup.ts';
|
|
@@ -69,6 +70,33 @@ export interface QuitCleanupStep {
|
|
|
69
70
|
* @returns the names of the steps that started, in order (for tests).
|
|
70
71
|
*/
|
|
71
72
|
export declare function runQuitSequence(steps: readonly QuitCleanupStep[], exit: (code: number) => void, onError?: (name: string, error: unknown) => void): Promise<readonly string[]>;
|
|
73
|
+
/** One composer submission waiting behind the startup delivery. */
|
|
74
|
+
export interface QueuedSubmission {
|
|
75
|
+
readonly text: string;
|
|
76
|
+
readonly mode: 'followup' | 'steer';
|
|
77
|
+
readonly images: readonly ImageBlock[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Order-preserving gate for composer input while the startup prompt/images
|
|
81
|
+
* are still preparing. Anything submitted before the startup delivery settles
|
|
82
|
+
* queues and flushes afterwards in submit order, so the initial request can
|
|
83
|
+
* never be overtaken by typing that raced a slow image preparation. The flush
|
|
84
|
+
* also runs when the startup delivery fails: user input is never stranded.
|
|
85
|
+
*/
|
|
86
|
+
export declare class StartupInputGate {
|
|
87
|
+
private readonly deliver;
|
|
88
|
+
private readonly queued;
|
|
89
|
+
private pending;
|
|
90
|
+
constructor(deliver: (submission: QueuedSubmission) => void);
|
|
91
|
+
/** Submit one line: delivered now while idle, queued behind the startup delivery otherwise. */
|
|
92
|
+
submit(submission: QueuedSubmission): void;
|
|
93
|
+
/**
|
|
94
|
+
* Run the startup delivery — the callback receives the direct-delivery sink
|
|
95
|
+
* for the startup prompt itself — then flush everything that queued behind
|
|
96
|
+
* it, in order, even when the callback rejects.
|
|
97
|
+
*/
|
|
98
|
+
run(startup: (deliver: (submission: QueuedSubmission) => void) => Promise<void>): Promise<void>;
|
|
99
|
+
}
|
|
72
100
|
/**
|
|
73
101
|
* Resolve the invocation's target session against the persisted headers.
|
|
74
102
|
* @param startup - the parsed startup flags.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal input arrives as byte chunks, and one chunk can carry several
|
|
3
|
+
* keypresses: a fast space-then-enter, a bridged stdin that batches reads, a
|
|
4
|
+
* middle-click paste. Ink parses each chunk as exactly one keypress —
|
|
5
|
+
* `parseKeypress(' \r')` matches neither member, so both keys silently
|
|
6
|
+
* vanish (a multi-select question answered with an empty set). The splitter
|
|
7
|
+
* below cuts every chunk into the individual keypress units Ink's parser
|
|
8
|
+
* expects, keeping escape sequences and bracketed-paste blocks intact, and
|
|
9
|
+
* the stdin proxy feeds the split stream to the Ink mount.
|
|
10
|
+
*
|
|
11
|
+
* @module @deepseek-ai/dsh-tui/input-split
|
|
12
|
+
*/
|
|
13
|
+
import { PassThrough } from 'node:stream';
|
|
14
|
+
/** One keypress cut from the input stream. */
|
|
15
|
+
export interface KeypressSplitter {
|
|
16
|
+
/** Feed one chunk; returns every keypress unit this chunk completed. */
|
|
17
|
+
push(chunk: string): string[];
|
|
18
|
+
/** Whether an unterminated bracketed-paste block is currently held. */
|
|
19
|
+
openPaste(): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Last-resort escape hatch for a paste whose end marker never arrived:
|
|
22
|
+
* drop the start marker and emit the held bytes as plain keypress units so
|
|
23
|
+
* nothing (Esc and Ctrl+C included) stays hostage. Inert when no paste is
|
|
24
|
+
* open.
|
|
25
|
+
*/
|
|
26
|
+
releaseStalePaste(): string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build a stateful chunk splitter. A partial unit at the end of one chunk
|
|
30
|
+
* (a cut CSI sequence, an open paste block) waits in the buffer for the
|
|
31
|
+
* rest. A chunk-trailing lone ESC emits as the Escape key right away:
|
|
32
|
+
* terminals send Escape as its own chunk, and holding it hostage for a
|
|
33
|
+
* sequence that may never continue would break every Esc cancel.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createKeypressSplitter(): KeypressSplitter;
|
|
36
|
+
/** The stdin-shaped stream the Ink mount renders through. */
|
|
37
|
+
export interface TuiStdin extends PassThrough {
|
|
38
|
+
/** Mirrors the real stdin so Ink's raw-mode gate passes. */
|
|
39
|
+
isTTY: boolean;
|
|
40
|
+
/** Forwarded to the real stdin; Ink toggles it around focus. */
|
|
41
|
+
setRawMode(value: boolean): unknown;
|
|
42
|
+
ref(): void;
|
|
43
|
+
unref(): void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Wrap one real stdin in the splitting proxy: keypress units flow into a
|
|
47
|
+
* PassThrough Ink reads, while raw-mode/ref calls forward to the source.
|
|
48
|
+
* @param source - the process (or harness) input stream in raw mode.
|
|
49
|
+
* @returns the proxy stream plus a dispose that detaches the tap.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createSplitStdin(source: NodeJS.ReadStream): {
|
|
52
|
+
stdin: TuiStdin;
|
|
53
|
+
dispose(): void;
|
|
54
|
+
};
|
|
@@ -115,7 +115,7 @@ export declare function StatuslinePanel({ enabled, change, close }: {
|
|
|
115
115
|
* instead of a bare failure notice. Enter applies one level; Esc returns to
|
|
116
116
|
* the model list without applying.
|
|
117
117
|
*/
|
|
118
|
-
export declare function EffortPanel({ row, current, select, back }: {
|
|
118
|
+
export declare function EffortPanel({ row, current, select, back, onExit }: {
|
|
119
119
|
/** The model row whose advertised levels this stage lists. */
|
|
120
120
|
row: ModelRow;
|
|
121
121
|
/** Effective effort currently in force ('' when none), for the ● mark. */
|
|
@@ -124,6 +124,8 @@ export declare function EffortPanel({ row, current, select, back }: {
|
|
|
124
124
|
select(effortId: string): void;
|
|
125
125
|
/** Return to the model list without applying. */
|
|
126
126
|
back(): void;
|
|
127
|
+
/** Leave the whole /model flow (Ctrl+C). */
|
|
128
|
+
onExit(): void;
|
|
127
129
|
}): ReactElement;
|
|
128
130
|
/**
|
|
129
131
|
* The /agents panel (the Codex agent-picker contract, read-only): this
|
package/lib/types/keyboard.d.ts
CHANGED
|
@@ -42,6 +42,14 @@ export declare function stripTerminalFocusEvents(chunk: string, onFocus: (focuse
|
|
|
42
42
|
/** Bracketed paste markers as Ink delivers them (it strips the leading ESC). */
|
|
43
43
|
export declare const PASTE_START_MARKER = "[200~";
|
|
44
44
|
export declare const PASTE_END_MARKER = "[201~";
|
|
45
|
+
/**
|
|
46
|
+
* How long an unterminated bracketed-paste block may hold buffered bytes
|
|
47
|
+
* before the input splitter strips its start marker and releases them: a
|
|
48
|
+
* terminal that loses the end marker must never take the whole keyboard
|
|
49
|
+
* hostage (Esc/Ctrl+C included). Shared by the splitter and the composer's
|
|
50
|
+
* lost-paste safety net so both use one window.
|
|
51
|
+
*/
|
|
52
|
+
export declare const PASTE_BRACKET_TIMEOUT_MS = 1000;
|
|
45
53
|
/**
|
|
46
54
|
* Remove bracketed paste markers from one input chunk. Panel drafts accept raw
|
|
47
55
|
* `input` text, where an unhandled paste would otherwise persist the literal
|
package/lib/types/presets.d.ts
CHANGED
|
@@ -11,8 +11,11 @@ export type AgentPresetsService = AgentPresets;
|
|
|
11
11
|
export declare function agentPresetsFrom(ctx: Context): AgentPresetsService | undefined;
|
|
12
12
|
/** A preset may change only before the first durable turn begins. */
|
|
13
13
|
export declare function isBlankSession(events: readonly SessionEvent[]): boolean;
|
|
14
|
+
/** Translate a preset id recorded before an upstream rename to its current id. */
|
|
15
|
+
export declare function normalizePresetId(id: string): string;
|
|
16
|
+
export declare function normalizePresetId(id: string | undefined): string | undefined;
|
|
14
17
|
/** Latest logged selection wins; legacy sessions deliberately fall back to standard. */
|
|
15
|
-
export declare function resolvePreset(session: Pick<Session, 'header' | '
|
|
18
|
+
export declare function resolvePreset(session: Pick<Session, 'header' | 'snapshotEvents'>): string;
|
|
16
19
|
/** Resolve a pre-session choice, or recompose an active blank Agent. */
|
|
17
20
|
export declare function selectPreset(service: AgentPresetsService, agent: Agent | undefined, presetId: string): Promise<PresetRow>;
|
|
18
21
|
/** Recompose atomically from the caller's perspective, logging only success. */
|
|
@@ -59,8 +59,67 @@ export interface ProviderModelSettings {
|
|
|
59
59
|
/** The small, portable subset of a provider profile the terminal edits. */
|
|
60
60
|
export interface ProviderConfiguration {
|
|
61
61
|
readonly baseURL?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Wire protocol the stored profile names (e.g. `openai-responses`), when it
|
|
64
|
+
* names one. Load-only: the editor never writes it, but endpoint discovery
|
|
65
|
+
* passes it so the listing speaks the same protocol as real requests.
|
|
66
|
+
*/
|
|
67
|
+
readonly api?: string;
|
|
62
68
|
readonly models: readonly ProviderModelSettings[];
|
|
63
69
|
}
|
|
70
|
+
/** One model an endpoint reported about itself (mirrors `LlmDiscoveredModel`). */
|
|
71
|
+
export interface DiscoveredModelView {
|
|
72
|
+
/** Model id the endpoint accepts. */
|
|
73
|
+
readonly id: string;
|
|
74
|
+
/** Human-readable name when the endpoint supplies one. */
|
|
75
|
+
readonly name?: string;
|
|
76
|
+
/** Context window when disclosed; adoption still owes it if absent. */
|
|
77
|
+
readonly contextWindow?: number;
|
|
78
|
+
/** Output cap when disclosed. */
|
|
79
|
+
readonly maxTokens?: number;
|
|
80
|
+
}
|
|
81
|
+
/** One model an endpoint reported about itself (mirrors LlmDiscoveredModel). */
|
|
82
|
+
export interface DiscoveredModelView {
|
|
83
|
+
/** Model id the endpoint accepts. */
|
|
84
|
+
readonly id: string;
|
|
85
|
+
/** Human-readable name when the endpoint supplies one. */
|
|
86
|
+
readonly name?: string;
|
|
87
|
+
/** Context window when disclosed; adoption still owes it if absent. */
|
|
88
|
+
readonly contextWindow?: number;
|
|
89
|
+
/** Output cap when disclosed. */
|
|
90
|
+
readonly maxTokens?: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The seven canonical reasoning levels a reasoningEfforts key may name -
|
|
94
|
+
* pi-ai's THINKING_LEVELS. A pi-ai upgrade that adds or removes one fails
|
|
95
|
+
* upstream's own drift gate; this mirror exists so the terminal editor can
|
|
96
|
+
* validate drafts without importing the pi-ai package.
|
|
97
|
+
*/
|
|
98
|
+
export declare const REASONING_EFFORT_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
99
|
+
/**
|
|
100
|
+
* One stored reasoningEfforts declaration: a display-level to wire-value map
|
|
101
|
+
* (null sends no reasoning parameter), an explicit false disabling the
|
|
102
|
+
* picker, or undefined leaving the entry to inherit.
|
|
103
|
+
*/
|
|
104
|
+
export type ReasoningEffortsValue = Record<string, string | null> | false | undefined;
|
|
105
|
+
/** Whether a raw extras value is a declared efforts dict (non-empty, non-false). */
|
|
106
|
+
export declare function isDeclaredReasoningEfforts(value: unknown): value is Record<string, string | null>;
|
|
107
|
+
/**
|
|
108
|
+
* Parse the setup page's compact efforts draft into a storable declaration.
|
|
109
|
+
* Grammar: empty = clear back to inherit; the single token "false" = disable
|
|
110
|
+
* the picker; otherwise space-separated level:wire pairs where level is one
|
|
111
|
+
* of REASONING_EFFORT_LEVELS and wire is any non-empty string or the literal
|
|
112
|
+
* "null" (send no parameter).
|
|
113
|
+
*/
|
|
114
|
+
export declare function parseReasoningEffortsDraft(draft: string): {
|
|
115
|
+
readonly ok: true;
|
|
116
|
+
readonly value: ReasoningEffortsValue;
|
|
117
|
+
} | {
|
|
118
|
+
readonly ok: false;
|
|
119
|
+
readonly error: string;
|
|
120
|
+
};
|
|
121
|
+
/** Serialize a stored declaration back to the compact draft form (stored key order preserved). */
|
|
122
|
+
export declare function serializeReasoningEfforts(value: unknown): string;
|
|
64
123
|
/**
|
|
65
124
|
* One provider row in the TUI provider-management panel: the configurable
|
|
66
125
|
* directory entry joined with its settings profile and credential facts.
|
|
@@ -143,6 +202,24 @@ export declare function loadProviderSettings(ctx: Context): Promise<ProviderSett
|
|
|
143
202
|
export declare function saveProviderCredential(ctx: Context, target: ProviderTargetView, rawKey: string): Promise<void>;
|
|
144
203
|
/** Save the endpoint and an explicit model allow-list without rebuilding the profile. */
|
|
145
204
|
export declare function saveProviderConfiguration(ctx: Context, target: ProviderTargetView, configuration: ProviderConfiguration): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* Interrogate a provider endpoint for the models it really serves, through
|
|
207
|
+
* the model-discovery capability the provider's settings namespace
|
|
208
|
+
* registered — the same pipe the official Web Models page uses. The request
|
|
209
|
+
* is a draft: a typed key forces direct endpoint interrogation (gateway
|
|
210
|
+
* truth), while an empty key lets the harness resolve the route's stored
|
|
211
|
+
* credential; with neither baseURL nor route the adapter answers from its
|
|
212
|
+
* own knowledge.
|
|
213
|
+
* @param ctx - context carrying the `llm` service (optional discovery).
|
|
214
|
+
* @param target - provider row whose settings namespace serves the draft.
|
|
215
|
+
* @param request - typed key and/or endpoint override for this one probe.
|
|
216
|
+
* @param signal - caller cancellation (panel navigation aborts the probe).
|
|
217
|
+
* @returns the advertised models in endpoint order, deduplicated.
|
|
218
|
+
*/
|
|
219
|
+
export declare function discoverProviderModels(ctx: Context, target: ProviderTargetView, request: {
|
|
220
|
+
readonly apiKey?: string;
|
|
221
|
+
readonly baseURL?: string;
|
|
222
|
+
}, signal?: AbortSignal): Promise<readonly DiscoveredModelView[]>;
|
|
146
223
|
/**
|
|
147
224
|
* Remove the currently named credential without touching the provider
|
|
148
225
|
* profile. Only the resolved profile's own reference is unset; a dormant or
|
package/lib/types/questions.d.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The terminal ask_user_question
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* The terminal ask_user_question answerer: one `user-questions/request`
|
|
3
|
+
* waterfall listener that drives a FIFO queue — one question request on
|
|
4
|
+
* screen at a time, everything else waiting — then resolves the collected
|
|
5
|
+
* answers back into the waterfall. Mirrors the approval answerer's claim/
|
|
6
|
+
* defer split: only agents this TUI owns are answered, every other request
|
|
7
|
+
* falls through to the next answerer.
|
|
8
8
|
*
|
|
9
|
-
* Plan reviews (`exit_plan_mode`) arrive through the same
|
|
9
|
+
* Plan reviews (`exit_plan_mode`) arrive through the same waterfall with an
|
|
10
10
|
* `intent: { kind: 'plan-review' }` — the renderer highlights the approve
|
|
11
11
|
* option; the answer encoding is identical either way.
|
|
12
12
|
*
|
|
13
13
|
* @module @deepseek-ai/dsh-code/questions
|
|
14
14
|
*/
|
|
15
15
|
import type { Context } from '@deepseek-ai/cordis';
|
|
16
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
16
17
|
import { type AskUserQuestionAnswer, type AskUserQuestionRequest } from '@deepseek-ai/dsh-user-questions';
|
|
17
18
|
/** One question request waiting on the human, with its settle channels. */
|
|
18
19
|
export interface PendingQuestion {
|
|
@@ -42,9 +43,12 @@ export interface QuestionStore {
|
|
|
42
43
|
cancel(pending: PendingQuestion): void;
|
|
43
44
|
}
|
|
44
45
|
/**
|
|
45
|
-
* Mount the
|
|
46
|
-
* @param ctx - context
|
|
47
|
-
* @
|
|
48
|
-
*
|
|
46
|
+
* Mount the `user-questions/request` answerer over a FIFO queue.
|
|
47
|
+
* @param ctx - plugin context whose event bus carries the waterfall.
|
|
48
|
+
* @param owns - agents this terminal answers for; every other request is
|
|
49
|
+
* deferred back into the waterfall (`next()`), so sibling answerers stay
|
|
50
|
+
* usable. Agent-less asks are claimed: this TUI is the only human surface
|
|
51
|
+
* in the process.
|
|
52
|
+
* @returns the store the renderer subscribes to.
|
|
49
53
|
*/
|
|
50
|
-
export declare function mountQuestionProvider(ctx: Context): QuestionStore;
|
|
54
|
+
export declare function mountQuestionProvider(ctx: Context, owns: (agent: Agent) => boolean): QuestionStore;
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* @module @deepseek-ai/dsh-tui/render/projection
|
|
8
8
|
*/
|
|
9
9
|
import { type ImageBlock, type MessageId } from '@deepseek-ai/dsh-llm';
|
|
10
|
-
import type { SessionEvent
|
|
10
|
+
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
11
|
+
import type { TodoItem } from '@deepseek-ai/dsh-tool-todo';
|
|
11
12
|
import { type ToolDetail } from './tool-detail.ts';
|
|
12
13
|
/** One user prompt line. */
|
|
13
14
|
export interface UserEntry {
|
|
@@ -115,6 +116,8 @@ export interface RetryEntry {
|
|
|
115
116
|
kind: 'retry';
|
|
116
117
|
/** Correlation id shared with the matching `llm/retry-started`. */
|
|
117
118
|
retryId: string;
|
|
119
|
+
/** Retry policy mode from the event: `always` has no attempt cap. */
|
|
120
|
+
mode: 'normal' | 'always';
|
|
118
121
|
/** Attempt ordinal and its cap. */
|
|
119
122
|
attempt: number;
|
|
120
123
|
max: number;
|
|
@@ -122,7 +125,11 @@ export interface RetryEntry {
|
|
|
122
125
|
code: string;
|
|
123
126
|
/** Backoff wait before the next attempt, in ms. */
|
|
124
127
|
delayMs: number;
|
|
125
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* `running` while the backoff waits, `done` once the attempt started — or
|
|
130
|
+
* when the turn ended first (the turn-end sweep finalizes orphans so they
|
|
131
|
+
* never pin the settled boundary).
|
|
132
|
+
*/
|
|
126
133
|
state: 'running' | 'done';
|
|
127
134
|
}
|
|
128
135
|
/** Turn-tail deliverables: files mutated by the turn's diff-bearing tools. */
|
|
@@ -72,28 +72,35 @@ export declare const STATUS_ITEM_SEPARATOR = " \u00B7 ";
|
|
|
72
72
|
/** The Codex-style mode cycle hint appended to the permission badge. */
|
|
73
73
|
export declare const STATUS_CYCLE_HINT = " (shift+tab to cycle)";
|
|
74
74
|
/**
|
|
75
|
-
* Interior columns of the
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* group
|
|
79
|
-
* (see CONTEXT_MIN_WIDTH) rather than asking the layout for more room.
|
|
75
|
+
* Interior columns of the context bar. The layout starts every bar at this
|
|
76
|
+
* width so the drop ladder can pre-measure the group, then degrades the
|
|
77
|
+
* readout and shrinks the bar inside a tighter budget before dropping the
|
|
78
|
+
* group (see CONTEXT_MIN_WIDTH) rather than asking the layout for more room.
|
|
80
79
|
*/
|
|
81
80
|
export declare const CONTEXT_BAR_WIDTH = 24;
|
|
82
81
|
/**
|
|
83
|
-
* Render context occupancy as ONE stepless bar: a solid
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* the
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* The readout flips to amber once occupancy reaches the warning threshold.
|
|
91
|
-
* @param usedTokens - reported used tokens (drives the readout and percent).
|
|
82
|
+
* Render context occupancy as ONE stepless proportional bar: a solid
|
|
83
|
+
* DeepSeek-blue fill run tracking the occupancy and a dim dotted free
|
|
84
|
+
* track for the rest. Nothing else lives inside the bar — the usage
|
|
85
|
+
* readout rides outside it (see contextGroupSpans) — so the geometry
|
|
86
|
+
* always reads as the true remaining share. A given occupancy always
|
|
87
|
+
* renders the identical bar.
|
|
88
|
+
* @param usedTokens - reported used tokens.
|
|
92
89
|
* @param contextWindow - route capacity.
|
|
93
|
-
* @param width - total bar
|
|
90
|
+
* @param width - total bar columns.
|
|
94
91
|
* @returns tone-split spans for the footer to paint.
|
|
95
92
|
*/
|
|
96
93
|
export declare function contextBar(usedTokens: number, contextWindow: number, width: number): readonly StatusSpan[];
|
|
94
|
+
/** How much usage detail the context group's readout carries. */
|
|
95
|
+
export type ContextReadoutMode = 'full' | 'percent' | 'none';
|
|
96
|
+
/**
|
|
97
|
+
* Compose the context group: the proportional bar plus the usage readout
|
|
98
|
+
* OUTSIDE the bar, so the dotted track keeps its proportional meaning no
|
|
99
|
+
* matter how wide the readout is. `full` reads `12.3K/1.0M 25%`; `percent`
|
|
100
|
+
* drops the absolute pair; `none` is the bare bar. The readout turns amber
|
|
101
|
+
* once occupancy reaches the warning threshold.
|
|
102
|
+
*/
|
|
103
|
+
export declare function contextGroupSpans(usedTokens: number, contextWindow: number, barWidth: number, readout: ContextReadoutMode): readonly StatusSpan[];
|
|
97
104
|
/**
|
|
98
105
|
* One customizable status item (the Codex /statusline picker contract).
|
|
99
106
|
* 'left' items render as pipe-separated clusters after the identity dot;
|
|
@@ -15,6 +15,14 @@
|
|
|
15
15
|
*
|
|
16
16
|
* @module @deepseek-ai/dsh-code/settings-file
|
|
17
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* Write one file atomically: create the parent directory, write to a
|
|
20
|
+
* uniquely named temp file, and rename it into place. A crash midway
|
|
21
|
+
* can never leave a half-written document behind. Unique temp names
|
|
22
|
+
* keep concurrent writers (two terminals, two chains in one process)
|
|
23
|
+
* from sharing one temp path.
|
|
24
|
+
*/
|
|
25
|
+
export declare function writeFileAtomically(path: string, text: string): Promise<void>;
|
|
18
26
|
/** The serialized persistence surface; flush() is handed to the quit sequence. */
|
|
19
27
|
export interface UserSettingsPersistence {
|
|
20
28
|
/**
|
package/lib/types/skills.d.ts
CHANGED
|
@@ -43,5 +43,5 @@ interface SkillsWatch extends SkillsView {
|
|
|
43
43
|
* @param ctx - context carrying the `skills` service (optional).
|
|
44
44
|
* @returns the view the completion menu subscribes to.
|
|
45
45
|
*/
|
|
46
|
-
export declare function watchSkills(ctx: Context): SkillsWatch;
|
|
46
|
+
export declare function watchSkills(ctx: Context, fallbackCwd?: string): SkillsWatch;
|
|
47
47
|
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.0.
|
|
4
|
+
"version": "1.0.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"deepseek": "./bin/deepseek.mjs",
|
|
@@ -63,8 +63,9 @@
|
|
|
63
63
|
"node": "^22.19 || >=24"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@deepseek-ai/dsh-authorization": "0.1.
|
|
67
|
-
"@deepseek-ai/dsh-web-fetch-http": "0.1.
|
|
66
|
+
"@deepseek-ai/dsh-authorization": "0.1.2-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-web-fetch-http": "0.1.2-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-util-values": "0.1.2-rc.1",
|
|
68
69
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
69
70
|
"chalk": "^5.6.2",
|
|
70
71
|
"commander": "^15.0.0",
|
|
@@ -74,32 +75,32 @@
|
|
|
74
75
|
"peerDependencies": {
|
|
75
76
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
76
77
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.2",
|
|
77
|
-
"@deepseek-ai/dsh-attachment": "0.1.
|
|
78
|
-
"@deepseek-ai/dsh-agent": "0.1.
|
|
79
|
-
"@deepseek-ai/dsh-agent-default-model": "0.1.
|
|
80
|
-
"@deepseek-ai/dsh-agent-presets": "0.1.
|
|
81
|
-
"@deepseek-ai/dsh-cmdline": "0.1.
|
|
82
|
-
"@deepseek-ai/dsh-code-runtime-worker-thread": "0.1.
|
|
83
|
-
"@deepseek-ai/dsh-commands": "0.1.
|
|
84
|
-
"@deepseek-ai/dsh-compaction": "0.1.
|
|
85
|
-
"@deepseek-ai/dsh-cordis-host-runner": "0.1.
|
|
86
|
-
"@deepseek-ai/dsh-credentials": "0.1.
|
|
87
|
-
"@deepseek-ai/dsh-file-reference-local": "0.1.
|
|
88
|
-
"@deepseek-ai/dsh-goal": "0.1.
|
|
89
|
-
"@deepseek-ai/dsh-invariants": "0.1.
|
|
90
|
-
"@deepseek-ai/dsh-jobs": "0.1.
|
|
91
|
-
"@deepseek-ai/dsh-llm": "0.1.
|
|
92
|
-
"@deepseek-ai/dsh-llm-retry": "0.1.
|
|
93
|
-
"@deepseek-ai/dsh-permission-presets": "0.1.
|
|
94
|
-
"@deepseek-ai/dsh-plan-mode": "0.1.
|
|
95
|
-
"@deepseek-ai/dsh-sandbox-policy": "0.1.
|
|
96
|
-
"@deepseek-ai/dsh-session": "0.1.
|
|
97
|
-
"@deepseek-ai/dsh-session-persistence": "0.1.
|
|
98
|
-
"@deepseek-ai/dsh-session-reference": "0.1.
|
|
99
|
-
"@deepseek-ai/dsh-session-title": "0.1.
|
|
100
|
-
"@deepseek-ai/dsh-skill": "0.1.
|
|
101
|
-
"@deepseek-ai/dsh-user-approval": "0.1.
|
|
102
|
-
"@deepseek-ai/dsh-user-questions": "0.1.
|
|
78
|
+
"@deepseek-ai/dsh-attachment": "0.1.2-rc.1",
|
|
79
|
+
"@deepseek-ai/dsh-agent": "0.1.2-rc.1",
|
|
80
|
+
"@deepseek-ai/dsh-agent-default-model": "0.1.2-rc.1",
|
|
81
|
+
"@deepseek-ai/dsh-agent-presets": "0.1.2-rc.1",
|
|
82
|
+
"@deepseek-ai/dsh-cmdline": "0.1.2-rc.1",
|
|
83
|
+
"@deepseek-ai/dsh-code-runtime-worker-thread": "0.1.2-rc.1",
|
|
84
|
+
"@deepseek-ai/dsh-commands": "0.1.2-rc.1",
|
|
85
|
+
"@deepseek-ai/dsh-compaction": "0.1.2-rc.1",
|
|
86
|
+
"@deepseek-ai/dsh-cordis-host-runner": "0.1.2-rc.1",
|
|
87
|
+
"@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
|
|
88
|
+
"@deepseek-ai/dsh-file-reference-local": "0.1.2-rc.1",
|
|
89
|
+
"@deepseek-ai/dsh-goal": "0.1.2-rc.1",
|
|
90
|
+
"@deepseek-ai/dsh-invariants": "0.1.2-rc.1",
|
|
91
|
+
"@deepseek-ai/dsh-jobs": "0.1.2-rc.1",
|
|
92
|
+
"@deepseek-ai/dsh-llm": "0.1.2-rc.1",
|
|
93
|
+
"@deepseek-ai/dsh-llm-retry": "0.1.2-rc.1",
|
|
94
|
+
"@deepseek-ai/dsh-permission-presets": "0.1.2-rc.1",
|
|
95
|
+
"@deepseek-ai/dsh-plan-mode": "0.1.2-rc.1",
|
|
96
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.1.2-rc.1",
|
|
97
|
+
"@deepseek-ai/dsh-session": "0.1.2-rc.1",
|
|
98
|
+
"@deepseek-ai/dsh-session-persistence": "0.1.2-rc.1",
|
|
99
|
+
"@deepseek-ai/dsh-session-reference": "0.1.2-rc.1",
|
|
100
|
+
"@deepseek-ai/dsh-session-title": "0.1.2-rc.1",
|
|
101
|
+
"@deepseek-ai/dsh-skill": "0.1.2-rc.1",
|
|
102
|
+
"@deepseek-ai/dsh-user-approval": "0.1.2-rc.1",
|
|
103
|
+
"@deepseek-ai/dsh-user-questions": "0.1.2-rc.1"
|
|
103
104
|
},
|
|
104
105
|
"peerDependenciesMeta": {
|
|
105
106
|
"@deepseek-ai/cordis": { "optional": true },
|
|
@@ -132,23 +133,25 @@
|
|
|
132
133
|
"@deepseek-ai/dsh-user-questions": { "optional": true }
|
|
133
134
|
},
|
|
134
135
|
"devDependencies": {
|
|
135
|
-
"@deepseek-ai/dsh-agent-presets": "0.1.
|
|
136
|
-
"@deepseek-ai/dsh-attachment": "0.1.
|
|
137
|
-
"@deepseek-ai/dsh-compaction": "0.1.
|
|
138
|
-
"@deepseek-ai/dsh-commands": "0.1.
|
|
139
|
-
"@deepseek-ai/dsh-credentials": "0.1.
|
|
140
|
-
"@deepseek-ai/dsh-
|
|
141
|
-
"@deepseek-ai/dsh-
|
|
142
|
-
"@deepseek-ai/dsh-
|
|
143
|
-
"@deepseek-ai/dsh-
|
|
144
|
-
"@deepseek-ai/dsh-
|
|
145
|
-
"@deepseek-ai/dsh-
|
|
146
|
-
"@deepseek-ai/dsh-
|
|
147
|
-
"@deepseek-ai/dsh-
|
|
148
|
-
"@deepseek-ai/dsh-session-
|
|
149
|
-
"@deepseek-ai/dsh-
|
|
150
|
-
"@deepseek-ai/dsh-
|
|
151
|
-
"@deepseek-ai/dsh-
|
|
136
|
+
"@deepseek-ai/dsh-agent-presets": "0.1.2-rc.1",
|
|
137
|
+
"@deepseek-ai/dsh-attachment": "0.1.2-rc.1",
|
|
138
|
+
"@deepseek-ai/dsh-compaction": "0.1.2-rc.1",
|
|
139
|
+
"@deepseek-ai/dsh-commands": "0.1.2-rc.1",
|
|
140
|
+
"@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
|
|
141
|
+
"@deepseek-ai/dsh-settings": "0.1.2-rc.1",
|
|
142
|
+
"@deepseek-ai/dsh-tool-todo": "0.1.2-rc.1",
|
|
143
|
+
"@deepseek-ai/dsh-goal": "0.1.2-rc.1",
|
|
144
|
+
"@deepseek-ai/dsh-jobs": "0.1.2-rc.1",
|
|
145
|
+
"@deepseek-ai/dsh-llm-retry": "0.1.2-rc.1",
|
|
146
|
+
"@deepseek-ai/dsh-permission-presets": "0.1.2-rc.1",
|
|
147
|
+
"@deepseek-ai/dsh-plan-mode": "0.1.2-rc.1",
|
|
148
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.1.2-rc.1",
|
|
149
|
+
"@deepseek-ai/dsh-session-persistence": "0.1.2-rc.1",
|
|
150
|
+
"@deepseek-ai/dsh-session-reference": "0.1.2-rc.1",
|
|
151
|
+
"@deepseek-ai/dsh-session-title": "0.1.2-rc.1",
|
|
152
|
+
"@deepseek-ai/dsh-skill": "0.1.2-rc.1",
|
|
153
|
+
"@deepseek-ai/dsh-user-approval": "0.1.2-rc.1",
|
|
154
|
+
"@deepseek-ai/dsh-user-questions": "0.1.2-rc.1",
|
|
152
155
|
"@types/node": "^24.0.0",
|
|
153
156
|
"@types/react": "~18.3.1",
|
|
154
157
|
"tsdown": "^0.22.2",
|