dsh-code 1.0.7 → 1.2.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.
Files changed (81) hide show
  1. package/README.en.md +70 -24
  2. package/README.md +71 -25
  3. package/bin/deepseek.mjs +202 -39
  4. package/cordis.patch.yml +13 -4
  5. package/lib/index.mjs +4063 -844
  6. package/lib/session-query.mjs +3 -2
  7. package/lib/startup.mjs +4 -4
  8. package/lib/{theme-DCT8Y2xf.mjs → theme-7u5Qo3dF.mjs} +657 -20
  9. package/lib/types/app.d.ts +100 -63
  10. package/lib/types/authorization-panel.d.ts +3 -3
  11. package/lib/types/git-workflow.d.ts +91 -2
  12. package/lib/types/i18n.d.ts +39 -0
  13. package/lib/types/index.d.ts +73 -1
  14. package/lib/types/input-split.d.ts +1 -1
  15. package/lib/types/kernel-panels.d.ts +86 -31
  16. package/lib/types/language-panel.d.ts +12 -0
  17. package/lib/types/locales/en.d.ts +450 -0
  18. package/lib/types/locales/zh.d.ts +9 -0
  19. package/lib/types/mentions.d.ts +7 -3
  20. package/lib/types/models.d.ts +14 -0
  21. package/lib/types/panel-accent.d.ts +28 -0
  22. package/lib/types/rainbow.d.ts +69 -0
  23. package/lib/types/render/animations.d.ts +42 -0
  24. package/lib/types/render/inspector.d.ts +26 -0
  25. package/lib/types/render/lines.d.ts +21 -1
  26. package/lib/types/render/markdown.d.ts +1 -1
  27. package/lib/types/render/projection.d.ts +95 -4
  28. package/lib/types/render/status.d.ts +8 -8
  29. package/lib/types/render/text.d.ts +6 -0
  30. package/lib/types/render/usage.d.ts +113 -0
  31. package/lib/types/session-directory.d.ts +17 -0
  32. package/lib/types/startup.d.ts +1 -1
  33. package/lib/types/terminal-title.d.ts +8 -0
  34. package/lib/types/theme-panel.d.ts +2 -2
  35. package/lib/types/theme.d.ts +271 -52
  36. package/lib/types/update-panel.d.ts +5 -5
  37. package/lib/types/update.d.ts +10 -1
  38. package/lib/types/version.d.ts +4 -3
  39. package/package.json +24 -7
  40. package/src/app.ts +1155 -478
  41. package/src/approval.ts +166 -166
  42. package/src/authorization-panel.ts +19 -16
  43. package/src/editor-keys.ts +371 -371
  44. package/src/git-workflow.ts +229 -3
  45. package/src/i18n.ts +68 -0
  46. package/src/index.ts +412 -76
  47. package/src/input-split.ts +3 -3
  48. package/src/kernel-panels.ts +471 -89
  49. package/src/keyboard.ts +5 -4
  50. package/src/language-panel.ts +53 -0
  51. package/src/locales/en.ts +489 -0
  52. package/src/locales/zh.ts +488 -0
  53. package/src/mentions.ts +8 -4
  54. package/src/models.ts +264 -212
  55. package/src/panel-accent.ts +41 -0
  56. package/src/presets.ts +1 -1
  57. package/src/provider-settings.ts +1 -1
  58. package/src/rainbow.ts +208 -0
  59. package/src/render/animations.ts +104 -6
  60. package/src/render/editor.ts +20 -20
  61. package/src/render/export.ts +116 -95
  62. package/src/render/inspector.ts +42 -0
  63. package/src/render/lines.ts +628 -415
  64. package/src/render/markdown.ts +15 -3
  65. package/src/render/projection.ts +429 -19
  66. package/src/render/status.ts +41 -35
  67. package/src/render/text.ts +14 -0
  68. package/src/render/tool-preview.ts +77 -77
  69. package/src/render/usage.ts +430 -0
  70. package/src/render/width.ts +2 -2
  71. package/src/session-directory.ts +8 -6
  72. package/src/session-query.ts +8 -4
  73. package/src/startup.ts +3 -3
  74. package/src/subagents.ts +229 -229
  75. package/src/terminal-title.ts +22 -5
  76. package/src/theme-panel.ts +17 -21
  77. package/src/theme.ts +281 -33
  78. package/src/update-panel.ts +37 -27
  79. package/src/update.ts +19 -3
  80. package/src/version.ts +58 -20
  81. package/src/whale-glyph.ts +23 -23
@@ -18,6 +18,7 @@ import type { CommandDescriptor } from '@deepseek-ai/dsh-commands';
18
18
  import type { ContentBlock, FileBlock, ImageBlock } from '@deepseek-ai/dsh-llm';
19
19
  import type { AuthorizationInteraction, AuthorizationStatus } from '@deepseek-ai/dsh-authorization';
20
20
  import { type ThemeName } from './theme.ts';
21
+ import { type LanguageName } from './i18n.ts';
21
22
  import type { LauncherUpdateStatus } from './update.ts';
22
23
  import type { TranscriptStore } from './store.ts';
23
24
  import { type TranscriptEntry } from './render/projection.ts';
@@ -29,16 +30,26 @@ import type { QuestionStore } from './questions.ts';
29
30
  import type { SkillsView, SkillRow } from './skills.ts';
30
31
  import { type MentionCandidate } from './mentions.ts';
31
32
  import type { SubagentFeedView } from './subagents.ts';
32
- import { type JobRow } from './kernel-panels.ts';
33
+ import type { UsageView } from './render/usage.ts';
34
+ import { type JobRow, type SearchRow } from './kernel-panels.ts';
33
35
  import type { PresetRow } from './presets.ts';
34
36
  import type { PermissionRow } from './permissions.ts';
35
37
  import type { PluginRow } from './plugin-inventory.ts';
36
38
  import type { SessionDirectoryOptions, SessionRow } from './session-directory.ts';
37
- import type { GitDiffView } from './git-workflow.ts';
39
+ import { type GitDiffView, type ReviewBranch, type ReviewCommit, type ReviewSelection } from './git-workflow.ts';
38
40
  import { type ProviderAuthorizationDirectory, type ProviderAuthorizationRow } from './authorization.ts';
39
41
  import { type FilePathInspection, type ImagePathInspection } from './attachments.ts';
40
42
  /** Visual priority for one bounded local notice. */
41
43
  export type NoticeTone = 'info' | 'warning' | 'error';
44
+ /** One mutation the terminal may request for a pending next-turn inbox item. */
45
+ export type QueueMutation = {
46
+ readonly kind: 'remove';
47
+ } | {
48
+ readonly kind: 'edit';
49
+ readonly text: string;
50
+ } | {
51
+ readonly kind: 'steer';
52
+ };
42
53
  /** Props the runner hands the app; callbacks stay owned by the runner. */
43
54
  export interface AppProps {
44
55
  /** Event-fed transcript store for the live session. */
@@ -77,130 +88,156 @@ export interface AppProps {
77
88
  * an attachment prepare resolves after the app remounted onto another
78
89
  * session, and the runner drops the stale delivery then.
79
90
  */
80
- dispatch(text: string, attachments?: readonly ContentBlock[], origin?: string): void;
81
- /** Submit steering, with the same stale-delivery guard as {@link dispatch}. */
82
- steer(text: string, attachments?: readonly ContentBlock[], origin?: string): void;
91
+ dispatch: (text: string, attachments?: readonly ContentBlock[], origin?: string) => void;
92
+ /**
93
+ * Submit one line as steering: it joins the turn already running at its next
94
+ * step boundary instead of waiting for the next turn. Same stale-delivery
95
+ * guard as {@link dispatch}.
96
+ */
97
+ steer: (text: string, attachments?: readonly ContentBlock[], origin?: string) => void;
83
98
  /**
84
99
  * The FULL current session identity ('' while the first session is pending)
85
100
  * — the stale-delivery origin above. Distinct from the short display id.
86
101
  */
87
102
  sessionKey: string;
88
103
  /** Interrupt the running turn (Esc); true when a turn was cancelled. */
89
- interrupt(): boolean;
104
+ interrupt: () => boolean;
90
105
  /** Quit: unmount, flush, and request process exit. */
91
- quit(): void;
106
+ quit: () => void;
92
107
  /** Load the selectable model directory (called when /model opens). */
93
- loadModels(): Promise<ModelDirectory>;
108
+ loadModels: () => Promise<ModelDirectory>;
94
109
  /** Load @mention candidates for the typed query (files + sessions). */
95
- loadMentions(query: string, signal?: AbortSignal): Promise<readonly MentionCandidate[]>;
110
+ loadMentions: (query: string, signal?: AbortSignal) => Promise<readonly MentionCandidate[]>;
96
111
  /** Validate draft image paths without committing attachment objects. */
97
- inspectImages(paths: readonly string[]): Promise<readonly ImagePathInspection[]>;
112
+ inspectImages: (paths: readonly string[]) => Promise<readonly ImagePathInspection[]>;
98
113
  /** Validate, normalize and persist images immediately before submission. */
99
- prepareImages(paths: readonly string[], signal?: AbortSignal): Promise<readonly ImageBlock[]>;
114
+ prepareImages: (paths: readonly string[], signal?: AbortSignal) => Promise<readonly ImageBlock[]>;
100
115
  /** Validate draft non-image file paths without committing attachment objects. */
101
- inspectFiles(paths: readonly string[]): Promise<readonly FilePathInspection[]>;
116
+ inspectFiles: (paths: readonly string[]) => Promise<readonly FilePathInspection[]>;
102
117
  /** Persist non-image files immediately before submission as durable file blocks. */
103
- prepareFiles(paths: readonly string[], signal?: AbortSignal): Promise<readonly FileBlock[]>;
118
+ prepareFiles: (paths: readonly string[], signal?: AbortSignal) => Promise<readonly FileBlock[]>;
104
119
  /** Apply one /model selection (with an advertised reasoning effort, when picked); returns the display label. */
105
- selectModel(row: ModelRow, effortId?: string): string;
120
+ selectModel: (row: ModelRow, effortId?: string) => string;
106
121
  /** The /subagent override label, '' when delegated agents follow the current model. */
107
122
  subagentModel: string;
108
123
  /** Apply one /subagent model pick; returns the override label. */
109
- setSubagentModel(row: ModelRow, effortId?: string): string;
124
+ setSubagentModel: (row: ModelRow, effortId?: string) => string;
110
125
  /** Drop the /subagent override (delegated agents follow the current model). */
111
- clearSubagentModel(): void;
126
+ clearSubagentModel: () => void;
112
127
  /** Delete one session subtree; resolves with the outcome line. */
113
- deleteSession(id: string): Promise<string>;
128
+ deleteSession: (id: string) => Promise<string>;
114
129
  /** Load provider/settings/credential facts for the optional /model provider stage. */
115
- loadModelProviders?(): Promise<ProviderSettingsDirectory>;
130
+ loadModelProviders?: () => Promise<ProviderSettingsDirectory>;
116
131
  /** Subscribe to Harness credential/settings/adapter invalidations while /model is open. */
117
- subscribeModelProviders?(listener: () => void): () => void;
132
+ subscribeModelProviders?: (listener: () => void) => () => void;
118
133
  /** Store or rotate one provider credential through the Harness credential service. */
119
- saveModelProviderCredential?(target: ProviderTargetView, key: string): Promise<void>;
134
+ saveModelProviderCredential?: (target: ProviderTargetView, key: string) => Promise<void>;
120
135
  /** Remove one writable provider credential without removing its settings profile. */
121
- unsetModelProviderCredential?(target: ProviderTargetView): Promise<void>;
136
+ unsetModelProviderCredential?: (target: ProviderTargetView) => Promise<void>;
122
137
  /** Remove one user-owned provider profile and its page-managed credential. */
123
- removeModelProvider?(target: ProviderTargetView): Promise<void>;
138
+ removeModelProvider?: (target: ProviderTargetView) => Promise<void>;
124
139
  /** Save endpoint and explicit model capacities through the provider profile. */
125
- saveModelProviderConfiguration?(target: ProviderTargetView, configuration: ProviderConfiguration): Promise<void>;
140
+ saveModelProviderConfiguration?: (target: ProviderTargetView, configuration: ProviderConfiguration) => Promise<void>;
126
141
  /**
127
142
  * Interrogate the provider's real endpoint (typed key wins over the stored
128
143
  * credential) for the models it actually serves — the discovery stage of
129
144
  * the provider setup page.
130
145
  */
131
- discoverModelProvider?(target: ProviderTargetView, request: {
146
+ discoverModelProvider?: (target: ProviderTargetView, request: {
132
147
  readonly apiKey?: string;
133
148
  readonly baseURL?: string;
134
- }, signal?: AbortSignal): Promise<readonly DiscoveredModelView[]>;
149
+ }, signal?: AbortSignal) => Promise<readonly DiscoveredModelView[]>;
135
150
  /** Provider authorization flows and value-free stored-record facts. */
136
- loadProviderAuthorizations?(): Promise<ProviderAuthorizationDirectory>;
137
- subscribeProviderAuthorizations?(listener: () => void): () => void;
138
- beginProviderAuthorization?(row: ProviderAuthorizationRow, method: string, interaction: AuthorizationInteraction, signal: AbortSignal): Promise<AuthorizationStatus>;
139
- cancelProviderAuthorization?(row: ProviderAuthorizationRow): void;
140
- logoutProviderAuthorization?(row: ProviderAuthorizationRow): Promise<void>;
141
- openAuthorizationUrl?(url: string): boolean;
142
- copyTextValue?(text: string): Promise<void>;
151
+ loadProviderAuthorizations?: () => Promise<ProviderAuthorizationDirectory>;
152
+ subscribeProviderAuthorizations?: (listener: () => void) => () => void;
153
+ beginProviderAuthorization?: (row: ProviderAuthorizationRow, method: string, interaction: AuthorizationInteraction, signal: AbortSignal) => Promise<AuthorizationStatus>;
154
+ cancelProviderAuthorization?: (row: ProviderAuthorizationRow) => void;
155
+ logoutProviderAuthorization?: (row: ProviderAuthorizationRow) => Promise<void>;
156
+ openAuthorizationUrl?: (url: string) => boolean;
157
+ copyTextValue?: (text: string) => Promise<void>;
143
158
  /** Cycle to the next mode station (Shift+Tab): a permission preset or a plan switch; returns the notice label. */
144
- cycleMode(): string;
159
+ cycleMode: () => string;
145
160
  /** Pre-session plan choice: shows the plan badge before the first session exists. */
146
161
  pendingPlan?: boolean;
147
162
  /** Select or inspect a permission preset without requiring a pre-existing session. */
148
- setPermission(id: string): string;
163
+ setPermission: (id: string) => string;
149
164
  /** Export the transcript to a markdown file (/export [path]); reports via notices. */
150
- exportTranscript(argument: string): Promise<void>;
165
+ exportTranscript: (argument: string) => Promise<void>;
151
166
  /** Rename the session (/title <text>); returns the outcome line for the notice. */
152
- renameTitle(argument: string): string;
167
+ renameTitle: (argument: string) => string;
153
168
  /** Copy the latest complete assistant response; resolves to notice text. */
154
- copyLastResponse(): Promise<string>;
169
+ copyLastResponse: () => Promise<string>;
155
170
  /** Load a complete read-only Git diff for the file-oriented viewport. */
156
- loadGitDiff(argument: string): Promise<GitDiffView>;
171
+ loadGitDiff: (argument: string) => Promise<GitDiffView>;
172
+ /** Local branches for the /review picker (absent: the picker hides the branch phase's list). */
173
+ listReviewBranches?: (signal?: AbortSignal) => Promise<readonly ReviewBranch[]>;
174
+ /** Recent commits on the current branch for the /review picker. */
175
+ listReviewCommits?: (signal?: AbortSignal) => Promise<readonly ReviewCommit[]>;
157
176
  /** Start a model review after applying the read-only permission preset. */
158
- reviewChanges(argument: string): void;
177
+ reviewChanges: (selection: ReviewSelection) => void;
159
178
  /** Preset/session/plugin kernel operations. */
160
- loadPresets(): Promise<readonly PresetRow[]>;
161
- switchMode(id: string): Promise<string>;
179
+ loadPresets: () => Promise<readonly PresetRow[]>;
180
+ switchMode: (id: string) => Promise<string>;
162
181
  /** Load the switchable permission presets for the /permission panel. */
163
- loadPermissions(): Promise<readonly PermissionRow[]>;
164
- createSession(mode?: string): void;
182
+ loadPermissions: () => Promise<readonly PermissionRow[]>;
183
+ createSession: (mode?: string) => void;
165
184
  /** Fork the active session at a completed-turn boundary. */
166
- forkSession(argument: string): void;
167
- loadSessions(options: SessionDirectoryOptions, signal?: AbortSignal): Promise<readonly SessionRow[]>;
168
- loadSessionTranscript(id: string, signal?: AbortSignal): Promise<string>;
185
+ forkSession: (argument: string) => void;
186
+ loadSessions: (options: SessionDirectoryOptions, signal?: AbortSignal) => Promise<readonly SessionRow[]>;
187
+ loadSessionTranscript: (id: string, signal?: AbortSignal) => Promise<string>;
188
+ /** Read the current session's usage blocks (projections plus per-turn fold). */
189
+ loadUsage: () => Promise<UsageView>;
190
+ /**
191
+ * Full-text search over every persisted session (the in-process
192
+ * session-query engine). Absent when the deployment disabled the row;
193
+ * /search degrades to a notice instead of opening the panel.
194
+ */
195
+ searchSessions?: (query: string, signal?: AbortSignal) => Promise<readonly SearchRow[]>;
169
196
  /** Load this session's subagent conversations (children by lineage). */
170
- loadSubagents(): Promise<readonly SessionRow[]>;
171
- switchSession(row: SessionRow): void;
172
- cancelSessionSwitch(): boolean;
173
- loadPlugins(): readonly PluginRow[];
197
+ loadSubagents: () => Promise<readonly SessionRow[]>;
198
+ switchSession: (row: SessionRow) => void;
199
+ cancelSessionSwitch: () => boolean;
200
+ loadPlugins: () => readonly PluginRow[];
174
201
  /** Caller-visible background jobs (the host jobs registry, read-only). */
175
- loadJobs(): readonly JobRow[];
202
+ loadJobs: () => readonly JobRow[];
176
203
  /** Probe the launcher's aligned update plan (read-only; never installs). */
177
- probeUpdate(): Promise<LauncherUpdateStatus>;
204
+ probeUpdate: () => Promise<LauncherUpdateStatus>;
178
205
  /** Run the launcher's aligned update; streams sanitized lines; resolves with the exit code. */
179
- applyUpdate(onLine: (line: string) => void): Promise<number>;
206
+ applyUpdate: (onLine: (line: string) => void, plan?: {
207
+ readonly dshSpec: string;
208
+ readonly codeSpec: string;
209
+ readonly pluginSpecs: readonly string[];
210
+ }) => Promise<number>;
180
211
  /** Registers the app's notice channel with the runner (called once on mount). */
181
- onBridgeReady(bridge: {
182
- notify(text: string, tone?: NoticeTone): void;
183
- }): void;
212
+ onBridgeReady: (bridge: {
213
+ notify: (text: string, tone?: NoticeTone) => void;
214
+ }) => void;
184
215
  /** Ordered enabled status items (/statusline config); the runner owns persistence. */
185
216
  statusline: readonly string[];
186
217
  /** Persist a new statusline item set; the runner surfaces IO failures as notices. */
187
- saveStatusline(items: readonly string[]): void;
218
+ saveStatusline: (items: readonly string[]) => void;
219
+ /** Apply and persist one /language selection; the runner owns the language.json file. */
220
+ saveLanguage: (name: LanguageName) => void;
188
221
  /** Apply and persist one /theme selection; the runner owns the theme.json file. */
189
- saveTheme?(name: ThemeName): void;
222
+ saveTheme?: (name: ThemeName) => void;
190
223
  /** Whether timed animations run at startup (animations.json; on by default
191
224
  * — like parseAnimationsPref, only an explicit false disables them). */
192
225
  animations?: boolean;
193
226
  /** Apply and persist one /animation toggle; the runner owns the file. */
194
- saveAnimations?(enabled: boolean): void;
227
+ saveAnimations?: (enabled: boolean) => void;
195
228
  /** Persistent cross-session input history (oldest first); the runner owns the file. */
196
229
  history: readonly string[];
197
230
  /** Persist one submitted prompt to the global history file. */
198
- recordHistory(text: string): void;
199
- /** Cancel one queued inbox message by identity (Delete on the empty composer). */
200
- cancelQueued(messageId: string): void;
231
+ recordHistory: (text: string) => void;
232
+ /** Mutate one next-turn inbox message; durable inbox splices reconcile the result. */
233
+ updateQueued?: (messageId: string, action: QueueMutation) => void;
201
234
  /** Apply the Ctrl+R terminal passthrough to the detected editor (/vscode-keys); resolves to a one-line summary. */
202
- applyEditorKeys(): Promise<string>;
235
+ applyEditorKeys: () => Promise<string>;
203
236
  }
237
+ /** Rows in the exact next-turn inbox order, never transcript append order. */
238
+ export declare function queuedInboxRows(entries: readonly TranscriptEntry[], ids: readonly string[]): readonly Extract<TranscriptEntry, {
239
+ kind: 'pending';
240
+ }>[];
204
241
  /** One completion candidate row. */
205
242
  interface CompletionCandidate {
206
243
  /** Insertion text for the command name (with leading slash). */
@@ -16,7 +16,7 @@ export interface ProviderAuthorizationPanelProps {
16
16
  export declare function ProviderAuthorizationPanel(props: ProviderAuthorizationPanelProps): ReactElement;
17
17
  export declare function ProviderAuthorizationLogoutPanel({ row, confirm, done, back }: {
18
18
  row: ProviderAuthorizationRow;
19
- confirm(row: ProviderAuthorizationRow): Promise<void>;
20
- done(): void;
21
- back(): void;
19
+ confirm: (row: ProviderAuthorizationRow) => Promise<void>;
20
+ done: () => void;
21
+ back: () => void;
22
22
  }): ReactElement;
@@ -28,5 +28,94 @@ export declare function parseGitDiffSpec(argument: string): GitDiffSpec;
28
28
  * git subprocess instead of letting a stale repository's diff land later.
29
29
  */
30
30
  export declare function loadGitDiff(cwd: string, argument: string, signal?: AbortSignal): Promise<GitDiffView>;
31
- /** Review prompt capped before it reaches a provider context window. */
32
- export declare function buildReviewPrompt(diff: string, label: string, maxChars?: number): string;
31
+ /** One /review run's selection: what to review plus an optional user note. */
32
+ export type ReviewSelection = {
33
+ readonly kind: 'uncommitted';
34
+ } | {
35
+ readonly kind: 'base-branch';
36
+ readonly branch: string;
37
+ readonly mergeBase?: string;
38
+ } | {
39
+ readonly kind: 'commit';
40
+ readonly sha: string;
41
+ } | {
42
+ readonly kind: 'custom';
43
+ readonly instructions: string;
44
+ };
45
+ /**
46
+ * The /review argument is always a free-form note applied to the uncommitted
47
+ * working tree (`/review 使用中文` reviews the uncommitted diff in Chinese);
48
+ * branch and commit targets come from the candidate picker, never from
49
+ * argument guessing.
50
+ */
51
+ export declare function parseReviewArgument(argument: string): ReviewSelection;
52
+ /** The merge base of HEAD and one branch, or undefined when git cannot compute one. */
53
+ export declare function mergeBaseWith(cwd: string, branch: string, signal?: AbortSignal): Promise<string | undefined>;
54
+ /** One commit's own patch (parent..commit), falling back to `git show` for root commits. */
55
+ export declare function loadCommitDiff(cwd: string, sha: string, signal?: AbortSignal): Promise<GitDiffView>;
56
+ /**
57
+ * Build the in-session review prompt: the diff is pasted whole (truncated at
58
+ * the character cap with an explicit marker the model can see), the rubric's
59
+ * essentials ride along — P0-P3 priorities, file/line anchors, only defects
60
+ * this change introduced — and an optional user note (language, focus)
61
+ * prefixes everything as the user's explicit instruction.
62
+ */
63
+ export declare function buildReviewPrompt(diff: string, label: string, note?: string, maxChars?: number): string;
64
+ /** One branch candidate for the review picker. */
65
+ export interface ReviewBranch {
66
+ readonly name: string;
67
+ }
68
+ /** One commit candidate for the review picker. */
69
+ export interface ReviewCommit {
70
+ readonly sha: string;
71
+ readonly title: string;
72
+ /** Committer timestamp in Unix epoch milliseconds. */
73
+ readonly at: number;
74
+ }
75
+ /**
76
+ * Local branch names for the review picker, newest activity first and with
77
+ * the current branch excluded (reviewing against it is always empty).
78
+ */
79
+ export declare function listReviewBranches(cwd: string, signal?: AbortSignal): Promise<readonly ReviewBranch[]>;
80
+ /** Recent commits on the current branch for the review picker. */
81
+ export declare function listReviewCommits(cwd: string, signal?: AbortSignal, limit?: number): Promise<readonly ReviewCommit[]>;
82
+ /** One finished review, ready for the terminal's result panel. */
83
+ export interface ReviewResultView {
84
+ /** Panel header naming what was reviewed. */
85
+ readonly title: string;
86
+ /** The reviewer's reply markdown (the findings list). */
87
+ readonly body: string;
88
+ /** Compact summary: finding counts per priority plus the verdict. */
89
+ readonly summary: string;
90
+ /** The review session id, so follow-ups can @-reference its context. */
91
+ readonly sessionId: string;
92
+ }
93
+ /** One parsed review finding from the reviewer's closing JSON block. */
94
+ export interface ReviewFinding {
95
+ /** Priority 0-3 for P0-P3; undefined when the reviewer omitted it. */
96
+ readonly priority?: number;
97
+ /** Finding title, already free of the [P#] prefix when present. */
98
+ readonly title: string;
99
+ /** File path the finding anchors to, when stated. */
100
+ readonly path?: string;
101
+ /** Line range as written, when stated. */
102
+ readonly range?: string;
103
+ }
104
+ /** The parsed conclusion of one review reply. */
105
+ export interface ReviewConclusion {
106
+ readonly findings: readonly ReviewFinding[];
107
+ readonly overall?: 'correct' | 'incorrect';
108
+ readonly explanation?: string;
109
+ }
110
+ /**
111
+ * Parse the reviewer's reply into a conclusion: whole-text JSON first, then
112
+ * the first balanced JSON object anywhere in the text, then undefined (the
113
+ * reply renders as plain markdown with no summary line). Malformed fields
114
+ * are dropped, never thrown.
115
+ */
116
+ export declare function parseReviewConclusion(text: string): ReviewConclusion | undefined;
117
+ /**
118
+ * One compact summary line for a finished review: finding counts per
119
+ * priority plus the overall verdict, or a no-findings phrasing.
120
+ */
121
+ export declare function reviewSummaryLine(conclusion: ReviewConclusion): string;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Interface language for the TUI: a module-level current language with a
3
+ * message accessor, mirroring the theme module's setTheme/getPalette shape
4
+ * so a language switch re-renders every translated surface on the next
5
+ * render without touching call sites. English is the default; the persisted
6
+ * choice lives in language.json next to theme.json (see the runner's
7
+ * persistence block).
8
+ *
9
+ * @module @deepseek-ai/dsh-tui/i18n
10
+ */
11
+ import { type MessageKey } from './locales/en.ts';
12
+ export type { MessageKey } from './locales/en.ts';
13
+ /** Selectable interface languages: English (default) and Chinese. */
14
+ export type LanguageName = 'en' | 'zh';
15
+ /** Valid language names for argument parsing. */
16
+ export declare const LANGUAGE_NAMES: readonly LanguageName[];
17
+ /** One language's picker row. */
18
+ export interface LanguageDescriptor {
19
+ readonly id: LanguageName;
20
+ readonly label: string;
21
+ }
22
+ /** The /language picker rows in canonical order. */
23
+ export declare const LANGUAGES: readonly LanguageDescriptor[];
24
+ /**
25
+ * Parse a persisted or typed language name: only 'en' and 'zh' survive;
26
+ * anything else falls back to English.
27
+ */
28
+ export declare function parseLanguageName(value: unknown): LanguageName;
29
+ /** The language name in force. */
30
+ export declare function getLanguage(): LanguageName;
31
+ /** Switch the active language; the next render paints with the new catalog. */
32
+ export declare function setLanguage(name: LanguageName): void;
33
+ /**
34
+ * One message from the active catalog, with `{n}`-style placeholders filled
35
+ * from the params record. Unknown placeholders stay literal; a missing key
36
+ * falls back to the English entry so a catalog gap degrades visibly but
37
+ * never crashes.
38
+ */
39
+ export declare function t(key: MessageKey, params?: Readonly<Record<string, string | number>>): string;
@@ -10,10 +10,13 @@
10
10
  */
11
11
  import type { Context } from '@deepseek-ai/cordis';
12
12
  import z from '@deepseek-ai/schemastery';
13
+ import type { Agent, AgentStatus, Inbox } from '@deepseek-ai/dsh-agent';
13
14
  import { type ContentBlock } from '@deepseek-ai/dsh-llm';
14
- import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session';
15
+ import { SessionId, type SessionEvent, type SessionHeader, type UserMessage } from '@deepseek-ai/dsh-session';
15
16
  import type { SessionPersistence } from '@deepseek-ai/dsh-session-persistence';
17
+ import { type QueueMutation } from './app.ts';
16
18
  import type { TuiStartup } from './startup.ts';
19
+ import type { SearchRow } from './kernel-panels.ts';
17
20
  /** Stable Cordis plugin name. */
18
21
  export declare const name = "tui-runner";
19
22
  /** Core services required before the interactive session can start. */
@@ -39,6 +42,8 @@ interface Target {
39
42
  cwd?: string;
40
43
  seed?: readonly SessionEvent[];
41
44
  parentSession?: SessionId;
45
+ /** Marks the session as a subagent conversation in the durable header. */
46
+ origin?: 'subagent';
42
47
  seedLength?: number;
43
48
  }
44
49
  /**
@@ -73,9 +78,48 @@ export declare function runQuitSequence(steps: readonly QuitCleanupStep[], exit:
73
78
  /** One composer submission waiting behind the startup delivery. */
74
79
  export interface QueuedSubmission {
75
80
  readonly text: string;
81
+ /** `steer` inserts into the running turn; `followup` waits for the next one. */
76
82
  readonly mode: 'followup' | 'steer';
77
83
  readonly images: readonly ContentBlock[];
78
84
  }
85
+ /** What one requested queue mutation did; the runner maps it to one notice. */
86
+ export type QueueMutationOutcome = 'removed' | 'edited' | 'steered' | 'unavailable' | 'empty' | 'steerUnavailable';
87
+ /**
88
+ * Replace one queued message's text while keeping its attachments. A queue
89
+ * edit rewrites what the user typed, not what they attached: image and file
90
+ * blocks ride through in delivery order (text first, then attachments, the
91
+ * shape {@link deliverLine} submits). Dropping them here would silently strip
92
+ * an attachment the user already confirmed, so this is the edit's single
93
+ * definition and the panel's read-only marker only mirrors it.
94
+ */
95
+ export declare function queueEditContent(content: readonly ContentBlock[], text: string): ContentBlock[];
96
+ /**
97
+ * Apply one terminal queue mutation to the live inbox. The decision and the
98
+ * inbox change are pure over the supplied handles so every branch is testable
99
+ * without an agent; steering itself is injected because it wakes the driver
100
+ * rather than mutating the inbox. The durable inbox splices remain the UI's
101
+ * single source of truth — this helper never reports a state the inbox did not
102
+ * actually reach.
103
+ * @param inbox - the live agent inbox (pending lists plus its mutators).
104
+ * @param status - the agent's lifecycle status; steering needs `running`.
105
+ * @param messageId - identity of the queued message to mutate.
106
+ * @param action - the requested mutation.
107
+ * @param steer - submits the removed message as next-step steering.
108
+ * @returns the outcome the caller reports.
109
+ */
110
+ export declare function applyQueueMutation(inbox: Pick<Inbox, 'nextTurn' | 'append' | 'remove' | 'replace'>, status: AgentStatus, messageId: string, action: QueueMutation, steer: (message: UserMessage) => void): QueueMutationOutcome;
111
+ /**
112
+ * Cancel the active turn while keeping the next-turn queue, then wake the
113
+ * driver again so the preserved messages actually run. `cancel` clears
114
+ * pending work by default and never wakes the driver on its own, so the queue
115
+ * is captured first and re-submitted afterwards: a waking submission latches
116
+ * the wake while the aborted activity converges to idle, which is what turns
117
+ * "preserved" into "sent next" instead of "parked forever". Next-step
118
+ * steering is deliberately dropped — it belonged to the cancelled turn.
119
+ * @param agent - the live agent handle.
120
+ * @returns how many queued messages were preserved across the abort.
121
+ */
122
+ export declare function cancelPreservingQueue(agent: Pick<Agent, 'inbox' | 'cancel' | 'followup'>): number;
79
123
  /**
80
124
  * Whether a tagged submission still belongs to the active session. Attachment
81
125
  * prepares resolve on the microtask timeline, while a queued session switch
@@ -85,6 +129,25 @@ export interface QueuedSubmission {
85
129
  * An untagged (synchronous) or pending-session ('') submission always passes.
86
130
  */
87
131
  export declare function submissionBelongsToSession(origin: string | undefined, activeSessionId: string | undefined): boolean;
132
+ /**
133
+ * Root-log catalog facts a resumed session must replay into the subagent
134
+ * feed: constructor seeds never fire on the live bus, so without this the
135
+ * children of a resumed session vanish behind a restart. The empty-child
136
+ * placeholder row (childId '') is a placeholder, not a child, and stays out.
137
+ */
138
+ export declare function subagentCatalogSeed(events: readonly SessionEvent[]): readonly SessionEvent<'subagent/catalog'>[];
139
+ /**
140
+ * Map one cross-session full-text hit onto the /search panel's row (pure).
141
+ * Labels fall back to the short id form — the engine's hit carries the
142
+ * strongest matching event, not the title observation.
143
+ */
144
+ export declare function searchHitToRow(hit: {
145
+ header: SessionHeader;
146
+ bestMatch: {
147
+ snippet: string;
148
+ time: number;
149
+ };
150
+ }): SearchRow;
88
151
  /** One Shift+Tab station decision for the mode cycle. */
89
152
  export type ModeCycleDecision = {
90
153
  readonly kind: 'permission';
@@ -105,12 +168,21 @@ export type ModeCycleDecision = {
105
168
  * parked on read-only), and leaving it lands on the next preset after the
106
169
  * most restrictive one. Without the /plan command the cycle is exactly the
107
170
  * preset table.
171
+ *
172
+ * `planIntent` covers the committed fold's commit lag: upstream queues a
173
+ * plan switch during an open turn (and the command pipeline is async even
174
+ * idle), so the durable plan/mode event lands AFTER the press that chose
175
+ * it. While an intent from an earlier press is in flight it — not the
176
+ * stale committed fold — decides the station, so repeated presses advance
177
+ * the cycle instead of re-issuing the same plan transition (the stuck
178
+ * plan-on/plan-off toggle). Undefined falls back to the committed fold.
108
179
  */
109
180
  export declare function planCycleDecision(input: {
110
181
  readonly names: readonly string[];
111
182
  readonly current: string;
112
183
  readonly inPlan: boolean;
113
184
  readonly planAvailable: boolean;
185
+ readonly planIntent?: boolean;
114
186
  }): ModeCycleDecision | undefined;
115
187
  /**
116
188
  * Order-preserving gate for composer input while the startup prompt/images
@@ -50,5 +50,5 @@ export interface TuiStdin extends PassThrough {
50
50
  */
51
51
  export declare function createSplitStdin(source: NodeJS.ReadStream): {
52
52
  stdin: TuiStdin;
53
- dispose(): void;
53
+ dispose: () => void;
54
54
  };