dsh-code 0.6.1 → 0.8.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 +20 -6
- package/README.md +20 -6
- package/lib/index.mjs +3952 -1315
- package/lib/startup.mjs +21 -9
- package/lib/theme-BEi4i_aN.mjs +624 -0
- package/lib/types/app.d.ts +108 -4
- package/lib/types/history.d.ts +15 -4
- package/lib/types/index.d.ts +49 -0
- package/lib/types/kernel-panels.d.ts +28 -0
- package/lib/types/mentions.d.ts +29 -12
- package/lib/types/models.d.ts +66 -0
- package/lib/types/permissions.d.ts +37 -0
- package/lib/types/presets.d.ts +2 -0
- package/lib/types/provider-settings.d.ts +144 -0
- package/lib/types/questions.d.ts +2 -0
- package/lib/types/render/animations.d.ts +177 -2
- package/lib/types/render/lines.d.ts +6 -0
- package/lib/types/render/markdown.d.ts +3 -3
- package/lib/types/render/projection.d.ts +123 -3
- package/lib/types/render/status.d.ts +35 -24
- package/lib/types/render/text.d.ts +14 -7
- package/lib/types/render/tool-detail.d.ts +3 -1
- package/lib/types/render/tool-preview.d.ts +4 -1
- package/lib/types/session-directory.d.ts +15 -0
- package/lib/types/startup.d.ts +12 -4
- package/lib/types/store.d.ts +13 -2
- package/lib/types/theme-panel.d.ts +24 -0
- package/lib/types/theme.d.ts +158 -2
- package/lib/types/version.d.ts +5 -0
- package/package.json +1 -1
- package/src/app.ts +1283 -206
- package/src/approval.ts +11 -2
- package/src/history.ts +20 -5
- package/src/index.ts +1207 -905
- package/src/kernel-panels.ts +518 -419
- package/src/mentions.ts +57 -27
- package/src/models.ts +200 -66
- package/src/permissions.ts +85 -0
- package/src/presets.ts +12 -0
- package/src/provider-settings.ts +520 -0
- package/src/questions.ts +15 -5
- package/src/render/animations.ts +373 -2
- package/src/render/lines.ts +21 -6
- package/src/render/markdown.ts +302 -4
- package/src/render/projection.ts +1419 -659
- package/src/render/status.ts +650 -603
- package/src/render/text.ts +28 -9
- package/src/render/tool-detail.ts +81 -40
- package/src/render/tool-preview.ts +18 -2
- package/src/session-directory.ts +44 -5
- package/src/skills.ts +8 -4
- package/src/startup.ts +119 -109
- package/src/store.ts +26 -8
- package/src/theme-panel.ts +72 -0
- package/src/theme.ts +206 -70
- package/src/version.ts +16 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider settings adapter for the TUI `/model` provider-management panel:
|
|
3
|
+
* the same-process equivalent of the web host's Models page join
|
|
4
|
+
* (`packages/client/ui-settings-models`), reading the advisory `ctx.llm`
|
|
5
|
+
* registry, the redacted `ctx.settings` descriptors, and the value-free
|
|
6
|
+
* `ctx.credentials` facts directly. Secrets never cross this module: settings
|
|
7
|
+
* are read with `redactSecrets: true`, credentials are only ever described
|
|
8
|
+
* (never resolved), and every message is single-line without embedding key
|
|
9
|
+
* data.
|
|
10
|
+
*
|
|
11
|
+
* @module @deepseek-ai/dsh-tui/provider-settings
|
|
12
|
+
*/
|
|
13
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
14
|
+
/**
|
|
15
|
+
* The conventional credential reference for a provider route: `<ROUTE>_API_KEY`
|
|
16
|
+
* with the route uppercased and every non-alphanumeric run collapsed to one
|
|
17
|
+
* underscore — the exact derivation the official Models page uses
|
|
18
|
+
* (`deriveKeyRef` in `ui-settings-models`), so a key saved here is found there.
|
|
19
|
+
* @param provider - provider route id (e.g. `pi-ai`, `minimax-cn`).
|
|
20
|
+
* @returns the derived reference name (e.g. `PI_AI_API_KEY`).
|
|
21
|
+
*/
|
|
22
|
+
export declare function deriveCredentialRef(provider: string): string;
|
|
23
|
+
/** Value-free facts about one credential reference — never the value. */
|
|
24
|
+
export interface ProviderCredentialFacts {
|
|
25
|
+
/** Whether the reference currently resolves to a stored value. */
|
|
26
|
+
readonly configured: boolean;
|
|
27
|
+
/** Source layer supplying the value; absent while unconfigured. */
|
|
28
|
+
readonly source?: string;
|
|
29
|
+
/** Whether a write through this panel would currently succeed. */
|
|
30
|
+
readonly writable: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* One row's credential state: value-free facts once the reference was
|
|
34
|
+
* described, a bounded error when that describe failed (the row itself is
|
|
35
|
+
* never dropped), or `undefined` when the row names no reference to describe —
|
|
36
|
+
* an unmanaged active provider, a dormant route, or a profile authenticating
|
|
37
|
+
* through the provider's own path.
|
|
38
|
+
*/
|
|
39
|
+
export type ProviderCredentialView = ({
|
|
40
|
+
readonly kind: 'facts';
|
|
41
|
+
} & ProviderCredentialFacts) | {
|
|
42
|
+
readonly kind: 'error';
|
|
43
|
+
readonly message: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* One provider row in the TUI provider-management panel: the configurable
|
|
47
|
+
* directory entry joined with its settings profile and credential facts.
|
|
48
|
+
* Every mutation below addresses this row, and the caller passes the row back
|
|
49
|
+
* after re-loading so the revision/ref facts are current.
|
|
50
|
+
*/
|
|
51
|
+
export interface ProviderTargetView {
|
|
52
|
+
/** Provider route id (`GenerateOptions.provider`). */
|
|
53
|
+
readonly provider: string;
|
|
54
|
+
/** Human-readable provider name. */
|
|
55
|
+
readonly displayName: string;
|
|
56
|
+
/** Whether an adapter currently serves this route. */
|
|
57
|
+
readonly active: boolean;
|
|
58
|
+
/** User-settings namespace whose section configures this provider; '' when unmanaged. */
|
|
59
|
+
readonly settingsNs: string;
|
|
60
|
+
/** Path from that section's root to this provider's profile; [] when the whole section is the profile. */
|
|
61
|
+
readonly settingsPath: readonly string[];
|
|
62
|
+
/** Revision of the owning settings section at load (0 when no namespace resolved). */
|
|
63
|
+
readonly settingsRevision: number;
|
|
64
|
+
/** Whether the resolved profile exists (the whole section, or at `settingsPath`). */
|
|
65
|
+
readonly configured: boolean;
|
|
66
|
+
/** Whether only the user settings layer carries the profile, so removal restores the base. */
|
|
67
|
+
readonly removable: boolean;
|
|
68
|
+
/** The credential reference the resolved profile names, when one does. */
|
|
69
|
+
readonly credentialRef?: string;
|
|
70
|
+
/** The conventional reference a save uses for a dormant or ref-less profile. */
|
|
71
|
+
readonly suggestedRef: string;
|
|
72
|
+
/** Credential facts, a bounded describe error, or undefined when there is no ref to describe. */
|
|
73
|
+
readonly credential: ProviderCredentialView | undefined;
|
|
74
|
+
/** The owning adapter reports this route as hand-declared (absent when it draws no distinction). */
|
|
75
|
+
readonly declared?: boolean;
|
|
76
|
+
}
|
|
77
|
+
/** The resolved provider/settings/credential join. */
|
|
78
|
+
export interface ProviderSettingsDirectory {
|
|
79
|
+
/** Provider rows: configurable-directory order first, active-unmanaged rows after. */
|
|
80
|
+
readonly rows: readonly ProviderTargetView[];
|
|
81
|
+
/** Whether the settings provider accepts writes (mirrors the web page's flag). */
|
|
82
|
+
readonly writable: boolean;
|
|
83
|
+
/** Non-fatal join failures (settings/directory reads), for a degradation notice. */
|
|
84
|
+
readonly failures: readonly string[];
|
|
85
|
+
}
|
|
86
|
+
/** Subscribe to the same provider-directory invalidations as the official Web Models page. */
|
|
87
|
+
export declare function subscribeProviderSettings(ctx: Context, listener: () => void): () => void;
|
|
88
|
+
/** A single-line, bounded error from the provider-management adapter. */
|
|
89
|
+
export declare class ProviderSettingsError extends Error {
|
|
90
|
+
constructor(message: string);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Join the configurable-provider directory, the redacted settings
|
|
94
|
+
* namespaces, and the referenced credentials into panel rows, web-parity:
|
|
95
|
+
* - directory entries merge with `listProviders()` to mark each live or
|
|
96
|
+
* dormant, and routes registered without a directory declaration appear as
|
|
97
|
+
* read-only/unmanaged rows (no settings address);
|
|
98
|
+
* - a whole-section entry is configured whenever its namespace resolves;
|
|
99
|
+
* a path-addressed one only when the profile resolves there;
|
|
100
|
+
* - a row is removable when the user layer alone carries its profile;
|
|
101
|
+
* - only refs named by resolved profiles are described, and a per-ref failure
|
|
102
|
+
* degrades to that row's bounded error instead of losing it.
|
|
103
|
+
* Absent `settings`/`credentials` services are tolerated the same way.
|
|
104
|
+
* @param ctx - context carrying the `llm` service (settings/credentials optional).
|
|
105
|
+
* @returns the resolved directory; empty rows when `llm` is unavailable.
|
|
106
|
+
*/
|
|
107
|
+
export declare function loadProviderSettings(ctx: Context): Promise<ProviderSettingsDirectory>;
|
|
108
|
+
/**
|
|
109
|
+
* Store a provider API key, web-parity: validate with `normalizeApiKey`
|
|
110
|
+
* (single-line, actionable errors that never echo the key), materialize the
|
|
111
|
+
* profile/`apiKeyEnv` through `settings.mutate` first when the resolved
|
|
112
|
+
* profile names no reference (dormant route or ref-less profile), then store
|
|
113
|
+
* under the trusted named ref or the derived conventional ref. An existing
|
|
114
|
+
* whole-section DeepSeek whose resolved profile already names
|
|
115
|
+
* `DEEPSEEK_API_KEY` needs no settings mutation. Env-supplied read-only keys
|
|
116
|
+
* are refused before any service call.
|
|
117
|
+
* @param ctx - context carrying `settings` (when materializing) and `credentials`.
|
|
118
|
+
* @param target - the joined row to write through.
|
|
119
|
+
* @param rawKey - the key exactly as typed; surrounding whitespace is trimmed.
|
|
120
|
+
* @throws {@link ProviderSettingsError} with a single-line, key-free message.
|
|
121
|
+
*/
|
|
122
|
+
export declare function saveProviderCredential(ctx: Context, target: ProviderTargetView, rawKey: string): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Remove the currently named credential without touching the provider
|
|
125
|
+
* profile. Only the resolved profile's own reference is unset; a dormant or
|
|
126
|
+
* ref-less row (nothing to remove), an already-absent key, and an
|
|
127
|
+
* env-supplied read-only key are rejected safely before any service call.
|
|
128
|
+
* @param ctx - context carrying the `credentials` service.
|
|
129
|
+
* @param target - the joined row whose named credential to unset.
|
|
130
|
+
* @throws {@link ProviderSettingsError} with a single-line, key-free message.
|
|
131
|
+
*/
|
|
132
|
+
export declare function unsetProviderCredential(ctx: Context, target: ProviderTargetView): Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Remove a user-added provider profile, web-parity: only `removable` rows may
|
|
135
|
+
* be removed; a page-managed credential — the derived ref, configured and
|
|
136
|
+
* writable — is unset first (so a second-step failure leaves the row visible
|
|
137
|
+
* and the operation retryable), then `settings.mutate` unsets
|
|
138
|
+
* `target.settingsPath`. Both steps are idempotent. A hand-named credential
|
|
139
|
+
* ref may be shared elsewhere and is left alone.
|
|
140
|
+
* @param ctx - context carrying `credentials` and `settings`.
|
|
141
|
+
* @param target - the joined row to remove.
|
|
142
|
+
* @throws {@link ProviderSettingsError} with a single-line, key-free message.
|
|
143
|
+
*/
|
|
144
|
+
export declare function removeProviderSettings(ctx: Context, target: ProviderTargetView): Promise<void>;
|
package/lib/types/questions.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export interface PendingQuestion {
|
|
|
22
22
|
resolve(answers: AskUserQuestionAnswer): void;
|
|
23
23
|
/** Reject the provider promise as aborted (also used for Esc cancel). */
|
|
24
24
|
reject(error: Error): void;
|
|
25
|
+
/** Detach the request's abort listener once the question settles (internal). */
|
|
26
|
+
detachAbort?(): void;
|
|
25
27
|
}
|
|
26
28
|
/** The pending-question snapshot the renderer subscribes to. */
|
|
27
29
|
export interface QuestionSnapshot {
|
|
@@ -3,11 +3,20 @@
|
|
|
3
3
|
* the StateDot "ongoing" pixel chase (3×3 ring, 125ms flat-hold brightness
|
|
4
4
|
* steps, 1s cycle) becomes the single-cell stepped pulse below and the
|
|
5
5
|
* full-ring clockwise braille chase in {@link BUSY_CHASE_FRAMES}, and the
|
|
6
|
-
* streaming caret blink is the Claude-Code convention.
|
|
7
|
-
*
|
|
6
|
+
* streaming caret blink is the Claude-Code convention.
|
|
7
|
+
*
|
|
8
|
+
* The DeepSeek model-switch easter egg ports Codex's effort-ignition "Wave"
|
|
9
|
+
* style (`codex-rs/tui/src/bottom_pane/effort_ignition_styles.rs`): switching
|
|
10
|
+
* INTO an official DeepSeek route sweeps a blue wave across the composer's
|
|
11
|
+
* input row — one column per cell, `backgroundColor` = the sampled wave
|
|
12
|
+
* color — then, on the deepseek (Ultra-equivalent) tier, drops the `· ✦ ✧`
|
|
13
|
+
* sparkle sequence into the rightmost blank cell before fading. The prompt
|
|
14
|
+
* marker keeps the tier accent afterwards (persistent, like Codex's prompt
|
|
15
|
+
* charge). Pure functions only — the Ink layer owns timers and colors.
|
|
8
16
|
*
|
|
9
17
|
* @module @deepseek-ai/dsh-code/render/animations
|
|
10
18
|
*/
|
|
19
|
+
import type { RgbTriple } from '../theme.ts';
|
|
11
20
|
/** Single-cell stepped pulse: flat holds mirroring the web's 125ms keyframes. */
|
|
12
21
|
export declare const PULSE_FRAMES: readonly ["█", "█", "▆", "▃", "▁", "▃", "▆", "█"];
|
|
13
22
|
/** Pulse frame for a monotonic tick. */
|
|
@@ -22,3 +31,169 @@ export declare const BUSY_CHASE_FRAMES: readonly ["⣾", "⣽", "⣻", "⢿", "
|
|
|
22
31
|
export declare function busyChaseFrame(tick: number): string;
|
|
23
32
|
/** Caret visibility: half the ticks on, half off (530ms blink). */
|
|
24
33
|
export declare function caretVisible(tick: number): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The one-shot DeepSeek model-switch easter egg: when the status bar model
|
|
36
|
+
* label switches to an official DeepSeek route, the composer's input row
|
|
37
|
+
* plays Codex's effort-ignition "Wave" — a blue crest sweeping the content
|
|
38
|
+
* row column by column (background tint ≤ 0.55 under the draft), plus the
|
|
39
|
+
* Ultra-style `· ✦ ✧` sparkles on the deepseek tier — and the prompt marker
|
|
40
|
+
* keeps the tier accent afterwards. The Ink layer owns the timer and reads
|
|
41
|
+
* the ACTIVE palette anchors (`getPalette`); everything below is pure
|
|
42
|
+
* interpolation over the colors it is given.
|
|
43
|
+
*/
|
|
44
|
+
/** Frame cadence of the DeepSeek wave: Codex's IGNITION_FRAME_TICK (33ms ≈ 30fps). */
|
|
45
|
+
export declare const DEEPSEEK_WAVE_TICK_MS = 33;
|
|
46
|
+
/**
|
|
47
|
+
* The two DeepSeek wave tiers. The concept maps Codex's reasoning tiers to
|
|
48
|
+
* model ids: `flash` runs the Max parameters, `deepseek` (pro models) runs
|
|
49
|
+
* the Ultra parameters (dual band + tail sparkles on the Wave style).
|
|
50
|
+
*/
|
|
51
|
+
export type DeepseekWaveTier = 'flash' | 'deepseek';
|
|
52
|
+
/**
|
|
53
|
+
* The three ignition styles — Codex `IgnitionStyle`: a traveling crest
|
|
54
|
+
* (Wave), a drifting multi-hue band (Aurora), and an expanding ring (Pulse).
|
|
55
|
+
* One style is picked at random per trigger and never repeats the previous.
|
|
56
|
+
*/
|
|
57
|
+
export type DeepseekWaveStyle = 'wave' | 'aurora' | 'pulse';
|
|
58
|
+
/** All styles in canonical order, for random selection and tests. */
|
|
59
|
+
export declare const DEEPSEEK_WAVE_STYLES: readonly DeepseekWaveStyle[];
|
|
60
|
+
/** Wave half-width in columns — Codex WAVE_HALF_WIDTH (9). */
|
|
61
|
+
export declare const WAVE_HALF_WIDTH = 9;
|
|
62
|
+
/** Pulse ring half-width in columns — Codex PULSE_HALF_WIDTH (4.5). */
|
|
63
|
+
export declare const PULSE_HALF_WIDTH = 4.5;
|
|
64
|
+
/** Sparkle start and frame cadence — Codex SPARK_START / SPARK_FRAME. */
|
|
65
|
+
export declare const SPARK_START_MS = 900;
|
|
66
|
+
export declare const SPARK_FRAME_MS = 100;
|
|
67
|
+
/** Sparkle glyphs in frame order — Codex SPARK_GLYPHS (`· ✦ ✧`). */
|
|
68
|
+
export declare const SPARK_GLYPHS: readonly ["·", "✦", "✧"];
|
|
69
|
+
/**
|
|
70
|
+
* Band tables — Codex `bands(style, tier)`. Each entry is a triple whose
|
|
71
|
+
* meaning depends on the style: Wave/Pulse use `(launch, travel, strength)`;
|
|
72
|
+
* Aurora uses `(speed, phase, hueIndex)`.
|
|
73
|
+
*/
|
|
74
|
+
export type DeepseekWaveBand = readonly [number, number, number];
|
|
75
|
+
export declare const DEEPSEEK_WAVE_BANDS: Readonly<Record<DeepseekWaveStyle, Readonly<Record<DeepseekWaveTier, readonly DeepseekWaveBand[]>>>>;
|
|
76
|
+
/** Extra display time applied to every Codex ignition style. */
|
|
77
|
+
export declare const DEEPSEEK_WAVE_DURATION_EXTENSION_MS = 200;
|
|
78
|
+
/**
|
|
79
|
+
* Total visible duration: the Codex ignition duration plus 200ms so its motion
|
|
80
|
+
* remains readable in a busy terminal.
|
|
81
|
+
* @param tier - the active wave tier.
|
|
82
|
+
* @param style - the active ignition style.
|
|
83
|
+
* @returns the duration in milliseconds.
|
|
84
|
+
*/
|
|
85
|
+
export declare function deepseekWaveDuration(tier: DeepseekWaveTier, style?: DeepseekWaveStyle): number;
|
|
86
|
+
/**
|
|
87
|
+
* Pick one ignition style at random, never repeating the previous one —
|
|
88
|
+
* Codex `IgnitionStyle::random`. Falls back to the remaining styles.
|
|
89
|
+
* @param previous - the style of the last trigger, if any.
|
|
90
|
+
* @returns a style different from `previous`.
|
|
91
|
+
*/
|
|
92
|
+
export declare function deepseekWaveStyleRandom(previous: DeepseekWaveStyle | undefined): DeepseekWaveStyle;
|
|
93
|
+
/**
|
|
94
|
+
* Blank-cell background the wave tint blends toward — fixed approximations
|
|
95
|
+
* of the terminal's default background, mirroring Codex's
|
|
96
|
+
* `user_message_bg_rgb` (which derives a near-black / near-white bubble tint
|
|
97
|
+
* from the terminal background). The Ink layer picks the active theme's one.
|
|
98
|
+
*/
|
|
99
|
+
export declare const WAVE_BASE_DARK: RgbTriple;
|
|
100
|
+
export declare const WAVE_BASE_LIGHT: RgbTriple;
|
|
101
|
+
/**
|
|
102
|
+
* Tier for a `provider/model` label: a model id containing `flash` runs the
|
|
103
|
+
* single-band flash tier; everything else (pro/reasoner/chat) runs the
|
|
104
|
+
* dual-band deepseek tier. Mirrors Codex's Max→Ultra mapping.
|
|
105
|
+
* @param model - the `provider/model` label of the applied model.
|
|
106
|
+
* @returns the wave tier for that model.
|
|
107
|
+
*/
|
|
108
|
+
export declare function deepseekWaveTier(model: string): DeepseekWaveTier;
|
|
109
|
+
/**
|
|
110
|
+
* Cosine window — Codex `crest`: 1 exactly under the wave center, 0 from
|
|
111
|
+
* one half-width away.
|
|
112
|
+
* @param distance - distance from the crest center in half-widths.
|
|
113
|
+
* @returns the crest strength in 0..1.
|
|
114
|
+
*/
|
|
115
|
+
export declare function crest(distance: number): number;
|
|
116
|
+
/**
|
|
117
|
+
* Cubic ease-in-out — Codex `ease_in_out`: flat at both ends, steepest in
|
|
118
|
+
* the middle, so the crest accelerates and eases instead of sliding linearly.
|
|
119
|
+
* @param progress - raw progress (clamped to 0..1).
|
|
120
|
+
* @returns the eased progress in 0..1.
|
|
121
|
+
*/
|
|
122
|
+
export declare function easeInOut(progress: number): number;
|
|
123
|
+
/**
|
|
124
|
+
* Fade-in/fade-out envelope — Codex `envelope`: linear ramp over `fadeIn`
|
|
125
|
+
* at the start and `fadeOut` at the end, plateau at 1 between, 0 outside the
|
|
126
|
+
* total. The Wave style keeps the envelope at 1 (Codex paints Wave without
|
|
127
|
+
* an envelope); exported for the Aurora-style fades and for tests.
|
|
128
|
+
* @param elapsed - seconds since the animation started.
|
|
129
|
+
* @param total - total duration in seconds.
|
|
130
|
+
* @param fadeIn - seconds of fade-in.
|
|
131
|
+
* @param fadeOut - seconds of fade-out.
|
|
132
|
+
* @returns the envelope value in 0..1.
|
|
133
|
+
*/
|
|
134
|
+
export declare function envelope(elapsed: number, total: number, fadeIn: number, fadeOut: number): number;
|
|
135
|
+
/**
|
|
136
|
+
* The background color for one composer-row column at a tick — Codex
|
|
137
|
+
* `paint_bands` + `Canvas::tint` for all three styles. Bands overlap with a
|
|
138
|
+
* max for Wave/Pulse and a SUM for Aurora (Codex differs by style), the
|
|
139
|
+
* weighted hues mix per column (Wave/Pulse always end on hue 0), the tint
|
|
140
|
+
* blends the mixed hue toward the blank-cell base at the style's alpha cap,
|
|
141
|
+
* and Aurora applies its own fade envelope. Returns `null` when the column
|
|
142
|
+
* should stay transparent, so the row returns to no `backgroundColor` on
|
|
143
|
+
* both ends.
|
|
144
|
+
* @param tick - wave frame (0, 1, … at DEEPSEEK_WAVE_TICK_MS).
|
|
145
|
+
* @param column - column index in the content row (0..width-1).
|
|
146
|
+
* @param width - content-row width in columns.
|
|
147
|
+
* @param tier - the wave tier (flash = Max, deepseek = Ultra parameters).
|
|
148
|
+
* @param style - the ignition style.
|
|
149
|
+
* @param hues - the tier's three hues.
|
|
150
|
+
* @param base - the blank-cell base color the tint blends toward.
|
|
151
|
+
* @returns the blended RGB background, or null for transparent.
|
|
152
|
+
*/
|
|
153
|
+
export declare function deepseekWaveColumnBg(tick: number, column: number, width: number, tier: DeepseekWaveTier, style: DeepseekWaveStyle, hues: readonly [RgbTriple, RgbTriple, RgbTriple], base: RgbTriple): RgbTriple | null;
|
|
154
|
+
/**
|
|
155
|
+
* The sparkle glyph for a tick — Codex `spark_frame`, sampled on the same
|
|
156
|
+
* proportionally slowed DeepSeek Wave timeline as the composer background.
|
|
157
|
+
* The Ink layer still must skip occupied cells.
|
|
158
|
+
* @param tick - wave frame at DEEPSEEK_WAVE_TICK_MS.
|
|
159
|
+
* @returns the sparkle glyph, or null outside the stretched tail window.
|
|
160
|
+
*/
|
|
161
|
+
export declare function deepseekWaveSpark(tick: number): string | null;
|
|
162
|
+
/**
|
|
163
|
+
* The composer BORDER color at a tick: the frame breathes with the wave —
|
|
164
|
+
* the palette's static dim blends toward the tier accent as the crest is
|
|
165
|
+
* alive and back, so the frame glows up while the wave sweeps and settles to
|
|
166
|
+
* dim on both ends (first==last frame==dim, no hard jump).
|
|
167
|
+
* @param tick - wave frame at DEEPSEEK_WAVE_TICK_MS.
|
|
168
|
+
* @param tier - the wave tier.
|
|
169
|
+
* @param hues - the tier's three hues; the border blends toward hues[0].
|
|
170
|
+
* @param dim - the palette's static dim RGB (the resting border color).
|
|
171
|
+
* @returns the blended border RGB.
|
|
172
|
+
*/
|
|
173
|
+
export declare function deepseekWaveBorderColor(tick: number, tier: DeepseekWaveTier, style: DeepseekWaveStyle, hues: readonly [RgbTriple, RgbTriple, RgbTriple], dim: RgbTriple): RgbTriple;
|
|
174
|
+
/**
|
|
175
|
+
* Whether the `deepseek` wordmark rides the wave at this tick: it fades in
|
|
176
|
+
* shortly after the first crest launches and out before the wave settles,
|
|
177
|
+
* so the brand name surfaces through the sweep's middle. The Ink layer
|
|
178
|
+
* places it in the row's blank mid-section (never over real draft text).
|
|
179
|
+
* @param tick - wave frame at DEEPSEEK_WAVE_TICK_MS.
|
|
180
|
+
* @param tier - the wave tier.
|
|
181
|
+
* @returns true while the wordmark should be visible.
|
|
182
|
+
*/
|
|
183
|
+
export declare function deepseekWaveWordVisible(tick: number, tier: DeepseekWaveTier, style?: DeepseekWaveStyle): boolean;
|
|
184
|
+
/**
|
|
185
|
+
* The per-character color for the `deepseek` wordmark: the tier's hues
|
|
186
|
+
* cycled per character (d→hue0, e→hue1, e→hue2, …), a brand-gradient text.
|
|
187
|
+
* @param index - character index in the wordmark.
|
|
188
|
+
* @param hues - the tier's three hues.
|
|
189
|
+
* @returns the hue for that character.
|
|
190
|
+
*/
|
|
191
|
+
export declare function deepseekWaveWordHue(index: number, hues: readonly [RgbTriple, RgbTriple, RgbTriple]): RgbTriple;
|
|
192
|
+
/**
|
|
193
|
+
* True when a `provider/model` status label addresses the official DeepSeek
|
|
194
|
+
* route: either segment contains `deepseek` (case-insensitive), covering the
|
|
195
|
+
* `deepseek-official` provider route and its `deepseek-*` model ids.
|
|
196
|
+
* @param label - the status bar model label (`provider/model`).
|
|
197
|
+
* @returns whether the label names an official DeepSeek model.
|
|
198
|
+
*/
|
|
199
|
+
export declare function isOfficialDeepSeekLabel(label: string): boolean;
|
|
@@ -24,6 +24,12 @@ export declare function styledLines(segments: readonly StyledSegment[], columns:
|
|
|
24
24
|
export declare function textLines(text: string, columns: number, style?: LineStyle): readonly StyledLine[];
|
|
25
25
|
/** Markdown rows re-hardened so a single long word cannot escape the budget. */
|
|
26
26
|
export declare function markdownLines(text: string, columns: number): readonly StyledLine[];
|
|
27
|
+
/**
|
|
28
|
+
* Codex-style reasoning rows: the marker occupies the reply gutter and every
|
|
29
|
+
* wrapped or explicit continuation starts with the same two-column indent, so
|
|
30
|
+
* reasoning content and assistant Markdown share one left edge.
|
|
31
|
+
*/
|
|
32
|
+
export declare function reasoningLines(text: string, columns: number): readonly StyledLine[];
|
|
27
33
|
/**
|
|
28
34
|
* Convert one durable transcript entry to its complete scrollable row model.
|
|
29
35
|
* The source entry stays intact; only the caller's visible slice is rendered.
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* block/inline parser producing styled line segments the Ink renderer maps
|
|
4
4
|
* to colored text. No ANSI here — the app owns color mapping, tests own the
|
|
5
5
|
* structure. The subset mirrors what agent replies actually emit: headings,
|
|
6
|
-
* emphasis, inline/fenced code, flat lists, blockquotes, links, rules,
|
|
7
|
-
* wrapped paragraphs. Unknown syntax degrades to plain text
|
|
6
|
+
* emphasis, inline/fenced code, flat lists, blockquotes, links, rules, GFM
|
|
7
|
+
* tables, and wrapped paragraphs. Unknown syntax degrades to plain text.
|
|
8
8
|
*
|
|
9
9
|
* @module @deepseek-ai/dsh-code/render/markdown
|
|
10
10
|
*/
|
|
11
11
|
/** Style classes the renderer emits; the app maps them to colors/props. */
|
|
12
|
-
export type MdStyle = 'plain' | 'bold' | 'italic' | 'boldItalic' | 'code' | 'accent' | 'dim' | 'strike';
|
|
12
|
+
export type MdStyle = 'plain' | 'bold' | 'italic' | 'boldItalic' | 'code' | 'accent' | 'accentBold' | 'dim' | 'strike';
|
|
13
13
|
/** One styled run of text. */
|
|
14
14
|
export interface MdSegment {
|
|
15
15
|
/** Visible text (no ANSI). */
|
|
@@ -138,6 +138,24 @@ export interface UsageTotals {
|
|
|
138
138
|
/** Cache-read tokens over the whole log (0 when the adapter reports none). */
|
|
139
139
|
cacheReadTokens: number;
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Estimated used tokens per context content type, folded from transcript
|
|
143
|
+
* events via {@link estimateTokens}. The segmented context bar's composition
|
|
144
|
+
* source: proportions across types are meaningful, absolute values are not
|
|
145
|
+
* (they never touch billing or the reported `lastPromptTokens`).
|
|
146
|
+
*/
|
|
147
|
+
export interface ContextSegments {
|
|
148
|
+
/** Rendered system-prompt text (latest `request/header`) plus injected-context notices. */
|
|
149
|
+
system: number;
|
|
150
|
+
/** Direct human prompts (durable `user/message` rows). */
|
|
151
|
+
prompt: number;
|
|
152
|
+
/** Assistant text blocks (visible replies). */
|
|
153
|
+
assistant: number;
|
|
154
|
+
/** Assistant reasoning blocks (hidden thinking). */
|
|
155
|
+
thinking: number;
|
|
156
|
+
/** Tool call arguments plus result text. */
|
|
157
|
+
tools: number;
|
|
158
|
+
}
|
|
141
159
|
/** Window-scoped figures the status line shows; timing uses event timestamps. */
|
|
142
160
|
export interface TranscriptStats {
|
|
143
161
|
/** Durable turns opened (`turn/start` events). */
|
|
@@ -154,6 +172,8 @@ export interface TranscriptStats {
|
|
|
154
172
|
lastPromptTokens: number;
|
|
155
173
|
/** Newest advertised route capacity, 0 when no adapter ever advertised one. */
|
|
156
174
|
contextWindow: number;
|
|
175
|
+
/** Estimated used tokens per content type (the segmented bar's composition). */
|
|
176
|
+
contextSegments: ContextSegments;
|
|
157
177
|
/** Summed first-token waits: `step/start` → first non-empty chunk, in ms. */
|
|
158
178
|
ttftMs: number;
|
|
159
179
|
/** Steps that produced a first chunk (the TTFT average's denominator). */
|
|
@@ -162,6 +182,14 @@ export interface TranscriptStats {
|
|
|
162
182
|
decodeMs: number;
|
|
163
183
|
/** Completion tokens over timed decode spans (the tok/s numerator). */
|
|
164
184
|
decodeTokens: number;
|
|
185
|
+
/**
|
|
186
|
+
* Adapter-owned reasoning effort of the latest `request/header` config —
|
|
187
|
+
* the EFFECTIVE effort the session actually uses (a materialized model
|
|
188
|
+
* default is included, exactly as the adapter resolved it). Empty when the
|
|
189
|
+
* header carried none (provider-default behavior). The status line appends
|
|
190
|
+
* it to the model segment as `provider/model@effort`.
|
|
191
|
+
*/
|
|
192
|
+
reasoningEffort: string;
|
|
165
193
|
}
|
|
166
194
|
/** The complete TUI transcript view for one session. */
|
|
167
195
|
export interface TranscriptView {
|
|
@@ -208,7 +236,10 @@ export interface TranscriptView {
|
|
|
208
236
|
/**
|
|
209
237
|
* Fold-internal timing anchors, never rendered: open step and tool-call
|
|
210
238
|
* start timestamps the next `assistant/message` / `tool/result` resolves
|
|
211
|
-
* against. Keyed `turn:step` and by call id.
|
|
239
|
+
* against. Keyed `turn:step` and by call id. `turnSteps`/`turnTools`
|
|
240
|
+
* track which step/tool anchors still belong to the open turn so
|
|
241
|
+
* `turn/end` (and a superseding `step/start`) can sweep anchors an
|
|
242
|
+
* interruption left behind; `turnFiles` keys mutated paths by turn.
|
|
212
243
|
*/
|
|
213
244
|
readonly anchors: {
|
|
214
245
|
stepStart: Map<string, number>;
|
|
@@ -217,6 +248,8 @@ export interface TranscriptView {
|
|
|
217
248
|
compactionTokens: Map<string, number>;
|
|
218
249
|
lastPruneTokens: number;
|
|
219
250
|
turnFiles: Map<number, Set<string>>;
|
|
251
|
+
turnSteps: Map<number, string>;
|
|
252
|
+
turnTools: Map<number, Set<string>>;
|
|
220
253
|
};
|
|
221
254
|
}
|
|
222
255
|
/** A fresh, empty transcript view. */
|
|
@@ -228,8 +261,92 @@ export declare function createTranscriptView(): TranscriptView;
|
|
|
228
261
|
* @returns the view after the event; the input view is never mutated.
|
|
229
262
|
*/
|
|
230
263
|
export declare function projectEvent(view: TranscriptView, event: SessionEvent): TranscriptView;
|
|
264
|
+
/**
|
|
265
|
+
* Mutable replay accumulator: folds a persisted log into the identical view
|
|
266
|
+
* `projectEvent` would produce, but in near-linear time. Where `projectEvent`
|
|
267
|
+
* is copy-on-write — every append/scan rebuilds the whole `entries` array, so
|
|
268
|
+
* folding a full log costs O(N²) — the accumulator appends by push, resolves
|
|
269
|
+
* id-keyed updates (tool/result, command/done, retry-started) through index
|
|
270
|
+
* maps, and tombstones retired pending rows, so the whole log folds in O(N)
|
|
271
|
+
* plus one compaction pass when tombstones exist.
|
|
272
|
+
*
|
|
273
|
+
* Index maps never delete: every appended row registers its index, so an id
|
|
274
|
+
* lookup miss provably means no matching row exists and the update is an O(1)
|
|
275
|
+
* no-op (a malicious/orphan-heavy log cannot force per-orphan full-array
|
|
276
|
+
* scans). Each id maps to ALL of its indices, so a duplicate id updates every
|
|
277
|
+
* matching row exactly like the copy-on-write reducer.
|
|
278
|
+
*
|
|
279
|
+
* @internal Exported only so tests can (a) prove replay ≡ sequential
|
|
280
|
+
* `projectEvent` folds and (b) assert the linear complexity deterministically
|
|
281
|
+
* via {@link ReplayAccumulator.ops}, which counts entry-level container work
|
|
282
|
+
* instead of relying on wall-clock thresholds. No public consumer.
|
|
283
|
+
*/
|
|
284
|
+
export interface ReplayAccumulator {
|
|
285
|
+
/** Working entry list; `undefined` marks a retired pending row (tombstone). */
|
|
286
|
+
entries: (TranscriptEntry | undefined)[];
|
|
287
|
+
/** callId → every index into `entries` holding a `tool` row with that id. */
|
|
288
|
+
toolIndex: Map<string, number[]>;
|
|
289
|
+
/** commandId → every index into `entries` holding a `command` row with that id. */
|
|
290
|
+
commandIndex: Map<string, number[]>;
|
|
291
|
+
/** retryId → every index into `entries` holding a `retry` row with that id. */
|
|
292
|
+
retryIndex: Map<string, number[]>;
|
|
293
|
+
/** messageId → every index into `entries` holding a `pending` row with that id. */
|
|
294
|
+
pendingIndex: Map<string, number[]>;
|
|
295
|
+
/** Tombstone count; zero means `entries` is already the final array. */
|
|
296
|
+
removedCount: number;
|
|
297
|
+
/** Mutable inbox id lists, mirroring `view.pending` order per target. */
|
|
298
|
+
pendingTurn: string[];
|
|
299
|
+
pendingStep: string[];
|
|
300
|
+
streaming: string;
|
|
301
|
+
streamingReasoning: string;
|
|
302
|
+
todos: readonly TodoItem[];
|
|
303
|
+
busy: boolean;
|
|
304
|
+
busySince: number;
|
|
305
|
+
model: string;
|
|
306
|
+
plan: boolean;
|
|
307
|
+
permission: string;
|
|
308
|
+
title: string;
|
|
309
|
+
sandbox: string;
|
|
310
|
+
goal: GoalFold | undefined;
|
|
311
|
+
stats: TranscriptStats;
|
|
312
|
+
stepStart: Map<string, number>;
|
|
313
|
+
toolStart: Map<string, number>;
|
|
314
|
+
firstChunkAt: Map<string, number>;
|
|
315
|
+
compactionTokens: Map<string, number>;
|
|
316
|
+
lastPruneTokens: number;
|
|
317
|
+
turnFiles: Map<number, Set<string>>;
|
|
318
|
+
turnSteps: Map<number, string>;
|
|
319
|
+
turnTools: Map<number, Set<string>>;
|
|
320
|
+
/** Entry-level container operations performed so far (test instrumentation). */
|
|
321
|
+
ops: number;
|
|
322
|
+
}
|
|
323
|
+
/** @internal A fresh replay accumulator whose state mirrors `createTranscriptView()`. */
|
|
324
|
+
export declare function createReplayAccumulator(): ReplayAccumulator;
|
|
325
|
+
/**
|
|
326
|
+
* Fold one session event into a replay accumulator. This mirrors
|
|
327
|
+
* {@link projectEvent} case for case — same stats arithmetic, same anchor
|
|
328
|
+
* set/delete behavior, same entry shapes — so the finished view is identical
|
|
329
|
+
* to a sequential fold; only the `entries` container operations are mutable.
|
|
330
|
+
*
|
|
331
|
+
* @internal Test-instrumentation path; `projectEvents` is the public entry.
|
|
332
|
+
*/
|
|
333
|
+
export declare function replayProjectEvent(acc: ReplayAccumulator, event: SessionEvent): void;
|
|
334
|
+
/**
|
|
335
|
+
* Materialize the accumulated fold as a `TranscriptView`, compacting any
|
|
336
|
+
* retired tombstones. The anchors maps are handed through as-is (their
|
|
337
|
+
* content is identical to a sequential fold's).
|
|
338
|
+
*
|
|
339
|
+
* @internal Test-instrumentation path; `projectEvents` is the public entry.
|
|
340
|
+
*/
|
|
341
|
+
export declare function finishReplay(acc: ReplayAccumulator): TranscriptView;
|
|
231
342
|
/**
|
|
232
343
|
* Fold a replayed event history into one view.
|
|
344
|
+
*
|
|
345
|
+
* Folding is near-linear in the log size: the mutable replay accumulator
|
|
346
|
+
* appends in place and resolves id-keyed updates through index maps, so a
|
|
347
|
+
* long persisted session replays without the O(N²) copy-on-write rebuilds a
|
|
348
|
+
* naive sequential fold would incur. The result is identical to folding
|
|
349
|
+
* {@link projectEvent} per event in order.
|
|
233
350
|
* @param events - events in `seq` order.
|
|
234
351
|
* @returns the folded view.
|
|
235
352
|
*/
|
|
@@ -243,8 +360,11 @@ export declare function projectEvents(events: readonly SessionEvent[]): Transcri
|
|
|
243
360
|
* the inbox claims or cancels them durably (`agent/inbox/spliced` removals,
|
|
244
361
|
* `user/message` retirement), and an append-only `<Static>` flush cannot
|
|
245
362
|
* erase a row that vanishes from the view — the retired row would ghost on
|
|
246
|
-
* screen until the next source-backed replay.
|
|
247
|
-
*
|
|
363
|
+
* screen until the next source-backed replay. Running commands join the
|
|
364
|
+
* mutable boundary for the same reason in reverse: `command/done` mutates the
|
|
365
|
+
* row's state/summary, so a flushed row would keep its stale running mark
|
|
366
|
+
* until a resize-triggered replay. Everything else (including a completed
|
|
367
|
+
* tail) is final: later events only APPEND new rows.
|
|
248
368
|
* @param entries - the view's transcript entries in order.
|
|
249
369
|
* @returns the count of entries safe to flush (0 for an empty transcript).
|
|
250
370
|
*/
|