dsh-code 1.3.0 → 1.5.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 (103) hide show
  1. package/README.en.md +10 -8
  2. package/README.md +10 -8
  3. package/lib/index.mjs +6980 -5942
  4. package/lib/session-query.mjs +18 -1
  5. package/lib/types/app.d.ts +38 -65
  6. package/lib/types/completion.d.ts +29 -0
  7. package/lib/types/composer.d.ts +150 -0
  8. package/lib/types/git-workflow.d.ts +6 -0
  9. package/lib/types/index.d.ts +6 -188
  10. package/lib/types/locales/en.d.ts +20 -8
  11. package/lib/types/{authorization-panel.d.ts → panels/authorization-panel.d.ts} +6 -1
  12. package/lib/types/panels/completion-panel.d.ts +13 -0
  13. package/lib/types/panels/interaction-bars.d.ts +38 -0
  14. package/lib/types/{kernel-panels.d.ts → panels/kernel-panels.d.ts} +16 -37
  15. package/lib/types/{language-panel.d.ts → panels/language-panel.d.ts} +1 -1
  16. package/lib/types/panels/model-panels.d.ts +86 -0
  17. package/lib/types/{theme-panel.d.ts → panels/theme-panel.d.ts} +1 -1
  18. package/lib/types/{update-panel.d.ts → panels/update-panel.d.ts} +1 -1
  19. package/lib/types/provider-settings.d.ts +11 -0
  20. package/lib/types/render/inspector.d.ts +16 -0
  21. package/lib/types/render/projection-events.d.ts +33 -0
  22. package/lib/types/render/text.d.ts +4 -0
  23. package/lib/types/render/transcript-viewport.d.ts +32 -0
  24. package/lib/types/runner/harness-gate.d.ts +83 -0
  25. package/lib/types/runner/input-history.d.ts +31 -0
  26. package/lib/types/runner/mode-cycle.d.ts +44 -0
  27. package/lib/types/runner/preferences.d.ts +40 -0
  28. package/lib/types/runner/quit.d.ts +27 -0
  29. package/lib/types/runner/search-rows.d.ts +38 -0
  30. package/lib/types/runner/session-io.d.ts +57 -0
  31. package/lib/types/runner/session-target.d.ts +42 -0
  32. package/lib/types/runner/startup-config.d.ts +33 -0
  33. package/lib/types/runner/submissions.d.ts +87 -0
  34. package/lib/types/session/attach.d.ts +39 -0
  35. package/lib/types/{store.d.ts → session/store.d.ts} +1 -1
  36. package/lib/types/{subagents.d.ts → session/subagents.d.ts} +8 -1
  37. package/lib/types/session-query.d.ts +11 -0
  38. package/lib/types/settings-file.d.ts +10 -0
  39. package/lib/types/{panel-accent.d.ts → ui/panel-accent.d.ts} +1 -1
  40. package/lib/types/ui/panel-gap.d.ts +6 -0
  41. package/lib/types/ui/query-editor.d.ts +10 -0
  42. package/lib/types/ui/styled-rows.d.ts +8 -0
  43. package/lib/types/{terminal-title.d.ts → ui/terminal-title.d.ts} +1 -1
  44. package/lib/types/ui/ui-contract.d.ts +12 -0
  45. package/lib/types/ui/use-frames.d.ts +6 -0
  46. package/lib/types/ui/use-stable-input.d.ts +7 -0
  47. package/package.json +3 -2
  48. package/src/app.ts +802 -4162
  49. package/src/completion.ts +117 -0
  50. package/src/composer.ts +1956 -0
  51. package/src/git-workflow.ts +18 -0
  52. package/src/index.ts +113 -633
  53. package/src/internals.ts +1 -1
  54. package/src/locales/en.ts +20 -8
  55. package/src/locales/zh.ts +20 -8
  56. package/src/{authorization-panel.ts → panels/authorization-panel.ts} +25 -6
  57. package/src/panels/completion-panel.ts +79 -0
  58. package/src/panels/interaction-bars.ts +567 -0
  59. package/src/{kernel-panels.ts → panels/kernel-panels.ts} +84 -65
  60. package/src/{language-panel.ts → panels/language-panel.ts} +4 -4
  61. package/src/panels/model-panels.ts +1021 -0
  62. package/src/{theme-panel.ts → panels/theme-panel.ts} +5 -5
  63. package/src/{update-panel.ts → panels/update-panel.ts} +6 -6
  64. package/src/provider-settings.ts +38 -0
  65. package/src/render/inspector.ts +35 -0
  66. package/src/render/lines.ts +45 -18
  67. package/src/render/projection-events.ts +109 -0
  68. package/src/render/projection.ts +11 -2
  69. package/src/render/text.ts +8 -0
  70. package/src/render/tool-preview.ts +35 -2
  71. package/src/render/transcript-viewport.ts +61 -0
  72. package/src/render/usage.ts +1 -2
  73. package/src/runner/harness-gate.ts +168 -0
  74. package/src/runner/input-history.ts +77 -0
  75. package/src/runner/mode-cycle.ts +49 -0
  76. package/src/runner/preferences.ts +67 -0
  77. package/src/runner/quit.ts +53 -0
  78. package/src/runner/search-rows.ts +55 -0
  79. package/src/runner/session-io.ts +245 -0
  80. package/src/runner/session-target.ts +81 -0
  81. package/src/runner/startup-config.ts +54 -0
  82. package/src/runner/submissions.ts +157 -0
  83. package/src/session/attach.ts +87 -0
  84. package/src/{session-directory.ts → session/session-directory.ts} +1 -1
  85. package/src/{store.ts → session/store.ts} +1 -1
  86. package/src/{subagents.ts → session/subagents.ts} +12 -1
  87. package/src/session-query.ts +22 -0
  88. package/src/settings-file.ts +19 -1
  89. package/src/{panel-accent.ts → ui/panel-accent.ts} +1 -1
  90. package/src/ui/panel-gap.ts +9 -0
  91. package/src/ui/query-editor.ts +16 -0
  92. package/src/ui/styled-rows.ts +124 -0
  93. package/src/{terminal-title.ts → ui/terminal-title.ts} +1 -1
  94. package/src/ui/ui-contract.ts +10 -0
  95. package/src/ui/use-frames.ts +23 -0
  96. package/src/ui/use-stable-input.ts +17 -0
  97. /package/lib/types/{fork.d.ts → session/fork.d.ts} +0 -0
  98. /package/lib/types/{history.d.ts → session/history.d.ts} +0 -0
  99. /package/lib/types/{session-directory.d.ts → session/session-directory.d.ts} +0 -0
  100. /package/lib/types/{session-switch.d.ts → session/session-switch.d.ts} +0 -0
  101. /package/src/{fork.ts → session/fork.ts} +0 -0
  102. /package/src/{history.ts → session/history.ts} +0 -0
  103. /package/src/{session-switch.ts → session/session-switch.ts} +0 -0
@@ -139,6 +139,23 @@ async function observeStableWithSkip(engine, indexed, signal, readCold = readCol
139
139
  }
140
140
  throw new SessionQueryError("session-search persistence observation did not stabilize after one retry", "SESSION_QUERY_PERSISTENCE_FAILED");
141
141
  }
142
+ /**
143
+ * Assert the private override seam this compatibility engine relies on.
144
+ * Harness keeps `_observeStable` private, so TypeScript cannot protect this
145
+ * subclass when upstream renames the method or changes its call signature.
146
+ * The exact-version launcher gate prevents unsupported hosts; this import-time
147
+ * check additionally prevents a newly built bundle from silently mounting an
148
+ * override the installed base will never call.
149
+ *
150
+ * @internal Exported for the compatibility regression.
151
+ */
152
+ function assertSessionQueryOverrideContract(candidate) {
153
+ if (typeof candidate !== "function") throw new TypeError("session-query sqlite engine must be a constructor");
154
+ const prototype = candidate.prototype;
155
+ const method = (prototype === void 0 ? void 0 : Object.getOwnPropertyDescriptor(prototype, "_observeStable"))?.value;
156
+ if (typeof method !== "function" || method.length !== 2) throw new Error("incompatible @deepseek-ai/dsh-session-query-sqlite: expected _observeStable(indexed, signal)");
157
+ }
158
+ assertSessionQueryOverrideContract(SqliteSessionQueryEngine);
142
159
  const EngineBase = SqliteSessionQueryEngine;
143
160
  /** The engine this bundle mounts in place of the base `session-query-sqlite` row. */
144
161
  var SkipTolerantSessionQueryEngine = class extends EngineBase {
@@ -147,4 +164,4 @@ var SkipTolerantSessionQueryEngine = class extends EngineBase {
147
164
  }
148
165
  };
149
166
  //#endregion
150
- export { SkipTolerantSessionQueryEngine, SkipTolerantSessionQueryEngine as default, observeStableWithSkip };
167
+ export { SkipTolerantSessionQueryEngine, SkipTolerantSessionQueryEngine as default, assertSessionQueryOverrideContract, observeStableWithSkip };
@@ -14,42 +14,35 @@
14
14
  * @module @deepseek-ai/dsh-code/app
15
15
  */
16
16
  import { type ReactElement } from 'react';
17
- import type { CommandDescriptor } from '@deepseek-ai/dsh-commands';
18
17
  import type { ContentBlock, FileBlock, ImageBlock } from '@deepseek-ai/dsh-llm';
19
18
  import type { AuthorizationInteraction, AuthorizationStatus } from '@deepseek-ai/dsh-authorization';
20
19
  import { type ThemeName } from './theme.ts';
21
20
  import { type LanguageName } from './i18n.ts';
22
21
  import type { LauncherUpdateStatus } from './update.ts';
23
- import type { TranscriptStore } from './store.ts';
22
+ import type { TranscriptStore } from './session/store.ts';
24
23
  import { type TranscriptEntry } from './render/projection.ts';
24
+ import { type SubagentAttachmentServices } from './session/attach.ts';
25
25
  import type { ApprovalStore } from './approval.ts';
26
- import { type CommandsView } from './commands.ts';
26
+ import type { CommandsView } from './commands.ts';
27
27
  import type { ModelDirectory, ModelRow } from './models.ts';
28
28
  import { type DiscoveredModelView, type ProviderConfiguration, type ProviderSettingsDirectory, type ProviderTargetView } from './provider-settings.ts';
29
29
  import type { QuestionStore } from './questions.ts';
30
- import type { SkillsView, SkillRow } from './skills.ts';
31
- import { type MentionCandidate } from './mentions.ts';
32
- import type { SubagentFeedView } from './subagents.ts';
30
+ import type { SkillsView } from './skills.ts';
31
+ import type { MentionCandidate } from './mentions.ts';
32
+ import type { SubagentFeedView } from './session/subagents.ts';
33
33
  import type { UsageView } from './render/usage.ts';
34
- import { type JobRow, type SearchRow } from './kernel-panels.ts';
34
+ import { type JobRow, type SearchRow } from './panels/kernel-panels.ts';
35
35
  import type { PresetRow } from './presets.ts';
36
36
  import type { PermissionRow } from './permissions.ts';
37
37
  import type { PluginRow } from './plugin-inventory.ts';
38
- import type { SessionDirectoryOptions, SessionRow } from './session-directory.ts';
39
- import { type GitDiffView, type ReviewBranch, type ReviewCommit, type ReviewSelection } from './git-workflow.ts';
40
- import { type ProviderAuthorizationDirectory, type ProviderAuthorizationRow } from './authorization.ts';
41
- import { type FilePathInspection, type ImagePathInspection } from './attachments.ts';
42
- /** Visual priority for one bounded local notice. */
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
- };
38
+ import type { SessionDirectoryOptions, SessionRow } from './session/session-directory.ts';
39
+ import type { GitDiffView, ReviewBranch, ReviewCommit, ReviewSelection } from './git-workflow.ts';
40
+ import type { ProviderAuthorizationDirectory, ProviderAuthorizationRow } from './authorization.ts';
41
+ import type { FilePathInspection, ImagePathInspection } from './attachments.ts';
42
+ export { completionCandidates, stepCompletionIndex } from './completion.ts';
43
+ import type { NoticeTone, QueueMutation } from './ui/ui-contract.ts';
44
+ export type { NoticeTone, QueueMutation } from './ui/ui-contract.ts';
45
+ import { type StyledLine } from './render/lines.ts';
53
46
  /** Props the runner hands the app; callbacks stay owned by the runner. */
54
47
  export interface AppProps {
55
48
  /** Event-fed transcript store for the live session. */
@@ -132,6 +125,8 @@ export interface AppProps {
132
125
  subscribeModelProviders?: (listener: () => void) => () => void;
133
126
  /** Store or rotate one provider credential through the Harness credential service. */
134
127
  saveModelProviderCredential?: (target: ProviderTargetView, key: string) => Promise<void>;
128
+ /** Switch a provider route to its subscription channel (drops the key reference). */
129
+ enableModelProviderSubscription?: (target: ProviderTargetView) => Promise<void>;
135
130
  /** Remove one writable provider credential without removing its settings profile. */
136
131
  unsetModelProviderCredential?: (target: ProviderTargetView) => Promise<void>;
137
132
  /** Remove one user-owned provider profile and its page-managed credential. */
@@ -195,6 +190,8 @@ export interface AppProps {
195
190
  searchSessions?: (query: string, signal?: AbortSignal) => Promise<readonly SearchRow[]>;
196
191
  /** Load this session's subagent conversations (children by lineage). */
197
192
  loadSubagents: () => Promise<readonly SessionRow[]>;
193
+ /** Live subagent attachment: seed + the two real-time buses, runner-wired. */
194
+ attachSubagent?: SubagentAttachmentServices;
198
195
  switchSession: (row: SessionRow) => void;
199
196
  cancelSessionSwitch: () => boolean;
200
197
  loadPlugins: () => readonly PluginRow[];
@@ -220,8 +217,8 @@ export interface AppProps {
220
217
  saveLanguage: (name: LanguageName) => void;
221
218
  /** Apply and persist one /theme selection; the runner owns the theme.json file. */
222
219
  saveTheme?: (name: ThemeName) => void;
223
- /** Whether timed animations run at startup (animations.json; on by default
224
- * like parseAnimationsPref, only an explicit false disables them). */
220
+ /** Whether decorative animations run at startup (animations.json; on by
221
+ * default). Functional activity indicators remain live when disabled. */
225
222
  animations?: boolean;
226
223
  /** Apply and persist one /animation toggle; the runner owns the file. */
227
224
  saveAnimations?: (enabled: boolean) => void;
@@ -252,44 +249,17 @@ export declare function streamTailBodyColumns(rowColumns: number, prefix: string
252
249
  export declare function queuedInboxRows(entries: readonly TranscriptEntry[], ids: readonly string[]): readonly Extract<TranscriptEntry, {
253
250
  kind: 'pending';
254
251
  }>[];
255
- /** One completion candidate row. */
256
- interface CompletionCandidate {
257
- /** Insertion text for the command name (with leading slash). */
258
- label: string;
259
- /** Human-readable description shown beside the label. */
260
- description: string;
261
- /** Candidate origin; skills land the same literal text but route through the prompt. */
262
- origin: 'command' | 'skill' | 'mention';
252
+ interface SettledPhysicalRow {
253
+ /** Stable one-row Static element. */
254
+ readonly element: ReactElement;
255
+ /** The same row model reused by the mutable viewport tail. */
256
+ readonly line: StyledLine;
263
257
  }
264
- /**
265
- * Wrap one completion-menu cursor step. An empty menu keeps the index at 0:
266
- * `% 0` is NaN, and a NaN index silently disables the highlight, the accept
267
- * key, and any later Enter that runs through the menu.
268
- */
269
- export declare function stepCompletionIndex(index: number, delta: number, count: number): number;
270
- /**
271
- * Resolve completion candidates for the current input: TUI-local commands,
272
- * the live registry descriptors, and user-invocable skills, filtered by the
273
- * typed prefix. Command names win collisions (the dispatch tries the
274
- * registry first and only then falls through to the skill gesture), and a
275
- * later duplicate name never renders twice.
276
- *
277
- * A bare `/` returns the FULL merged list — Codex's command popup shows every
278
- * command inside a scroll window on an empty filter, and the menu's own
279
- * selection window bounds the visible rows, so no slice cap is needed.
280
- */
281
- export declare function completionCandidates(value: string, descriptors: readonly CommandDescriptor[], skills: readonly SkillRow[]): readonly CompletionCandidate[];
282
- /** One cached settled row: the row Box plus its roomy-prompt spacers. */
283
258
  interface SettledRowRecord {
284
- /** The row Box element (keyed by the entry's settled index). */
285
- box: ReactElement;
286
- /** The roomy-prompt spacer BEFORE the row, or undefined. */
287
- before: ReactElement | undefined;
288
- /** The roomy-prompt spacer AFTER the row, or undefined. */
289
- after: ReactElement | undefined;
290
- /** Physical rows this record contributes (row body plus spacers) — the
291
- * unit of the rendered-history cap. */
292
- rows: number;
259
+ /** Physical rows for one settled entry, including roomy-prompt spacers. */
260
+ readonly physical: readonly SettledPhysicalRow[];
261
+ /** Physical rows this record contributes — the rendered-history-cap unit. */
262
+ readonly rows: number;
293
263
  }
294
264
  /** The incremental settled-history cache (see `computeSettledRows`). */
295
265
  interface SettledRowsCache {
@@ -309,10 +279,13 @@ interface SettledRowsCache {
309
279
  epoch: number;
310
280
  /** The terminal width the rows were wrapped for; a change forces a rebuild. */
311
281
  columns: number;
312
- /** The flat row list (header + optional hint + per-entry before/box/after). */
282
+ /** Header/hint followed by one element per physical transcript row. */
313
283
  flat: ReactElement[];
284
+ /** Physical transcript rows only (header/hint excluded). */
285
+ physical: SettledPhysicalRow[];
314
286
  /** Settled entries dropped from the window's head (rendering only — the
315
- * event log keeps everything; Ctrl+O and /export read it directly). */
287
+ * event log keeps everything; /export reads all of it and Ctrl+O reads its
288
+ * inspectable entries). */
316
289
  droppedEntries: number;
317
290
  /** Physical rows the window's entries contribute (excludes header/hint). */
318
291
  totalRows: number;
@@ -342,8 +315,9 @@ interface SettledRowsResult {
342
315
  *
343
316
  * RENDERED-HISTORY CAP: the window holds at most `rowCap` physical rows of
344
317
  * settled transcript (header and hint reserved on top). The cap exists only
345
- * here — the event log, the store projection, /export, Ctrl+O, and /resume
346
- * keep the full history. Ink 5's <Static> is a consumption counter
318
+ * here — the event log, the store projection, /export, and /resume keep the
319
+ * full history; Ctrl+O keeps its inspectable subset. Ink 5's <Static> is a
320
+ * consumption counter
347
321
  * (items.slice(index) keyed on length): deleting head items mid-stream while
348
322
  * appending tail items can permanently swallow new rows, so the append branch
349
323
  * NEVER drops the head — it only accounts rows and flags `needsTrim` once the
@@ -365,4 +339,3 @@ interface SettledRowsResult {
365
339
  export declare function computeSettledRows(previous: SettledRowsCache | undefined, entries: readonly TranscriptEntry[], settled: number, showReasoning: boolean, resumed: boolean, epoch: number, columns?: number, rowCap?: number): SettledRowsResult;
366
340
  /** The whole terminal app; state arrives via the store, output via Ink. */
367
341
  export declare function App(props: AppProps): ReactElement;
368
- export {};
@@ -0,0 +1,29 @@
1
+ /** Slash-command parsing and completion candidates shared by the composer and help panel. */
2
+ import type { CommandDescriptor } from '@deepseek-ai/dsh-commands';
3
+ import { type MessageKey } from './i18n.ts';
4
+ import type { SkillRow } from './skills.ts';
5
+ /** One TUI-owned slash command: label plus its i18n description key. */
6
+ export interface LocalCommand {
7
+ readonly label: string;
8
+ readonly descriptionKey: MessageKey;
9
+ }
10
+ /** One source of truth for TUI-owned slash commands in completion and `/help`. */
11
+ export declare const LOCAL_COMMANDS: readonly LocalCommand[];
12
+ export declare const LOCAL_COMMAND_NAMES: ReadonlySet<string>;
13
+ /** TUI-local commands that reject trailing input instead of forwarding it as a prompt. */
14
+ export declare const BARE_LOCAL_COMMANDS: ReadonlySet<string>;
15
+ /** Split a slash line into the command name and any trailing input. */
16
+ export declare function slashNameAndArgs(text: string): {
17
+ readonly name: string;
18
+ readonly args: string;
19
+ } | undefined;
20
+ /** One completion candidate row. */
21
+ export interface CompletionCandidate {
22
+ readonly label: string;
23
+ readonly description: string;
24
+ readonly origin: 'command' | 'skill' | 'mention';
25
+ }
26
+ /** Wrap one completion-menu cursor step; an empty menu keeps the index at zero. */
27
+ export declare function stepCompletionIndex(index: number, delta: number, count: number): number;
28
+ /** Merge local commands, registry descriptors, and skills using dispatch shadowing order. */
29
+ export declare function completionCandidates(value: string, descriptors: readonly CommandDescriptor[], skills: readonly SkillRow[]): readonly CompletionCandidate[];
@@ -0,0 +1,150 @@
1
+ /** Composer input, command routing, attachments, and one-shot composer animations. */
2
+ import { type ReactElement } from 'react';
3
+ import type { CommandDescriptor } from '@deepseek-ai/dsh-commands';
4
+ import type { ContentBlock, FileBlock, ImageBlock } from '@deepseek-ai/dsh-llm';
5
+ import { type FilePathInspection, type ImagePathInspection } from './attachments.ts';
6
+ import type { SkillRow } from './skills.ts';
7
+ import { type MentionCandidate } from './mentions.ts';
8
+ import { type ReviewSelection } from './git-workflow.ts';
9
+ import { type LanguageName } from './i18n.ts';
10
+ import { type DeepseekWaveStyle, type DeepseekWaveTier } from './render/animations.ts';
11
+ import type { TranscriptEntry } from './render/projection.ts';
12
+ import type { NoticeTone, QueueMutation } from './ui/ui-contract.ts';
13
+ /**
14
+ * The prompt box: TUI-local slash commands handled locally, other lines
15
+ * dispatched; input editing keeps a cursor with history and completion.
16
+ * While a modal (approval / question / model panel) owns the keys, the
17
+ * box passes every key through untouched.
18
+ */
19
+ export declare function Composer({ active, frozen, frozenHint, busy, descriptors, skills, dispatch, steer, submitMode, cycleSubmitMode, interrupt, quit, openModel, openEffort, openHelp, openMode, openPermission, openResume, openSearch, openPlugin, openUpdate, openSchedule, openJobs, openStatusline, openTheme, openLanguage, saveLanguage, openHistory, openQueue, openAgents, openSubagent, openTodos, openUsage, openDelete, openDiff, openReviewPicker, reviewChanges, deleteConfirm, confirmDelete, cancelDelete, createSession, forkSession, cancelSessionSwitch, notify, applyEditorKeys, hasNotice, dismissNotice, toggleReasoning, openVerbose, clearView, refresh, loadMentions, inspectImages, prepareImages, inspectFiles, prepareFiles, cycleMode, exportTranscript, renameTitle, copyLastResponse, recallSpace, recordLocal, recordHistory, queued, updateQueued, historyFill, historyConsumed, animations, applyAnimations, applyRainbow, rainbowBurstId, waveTier, waveStyle, maxRows, anchorRowsBelow, tabTitle, onEditorRows, onMenuRows, sessionKey }: {
20
+ active: boolean;
21
+ frozen: boolean;
22
+ /** Frozen-band hint naming the surface that owns the keyboard; an empty
23
+ * draft otherwise advertises typing that the composer cannot accept. */
24
+ frozenHint?: string;
25
+ busy: boolean;
26
+ descriptors: readonly CommandDescriptor[];
27
+ skills: readonly SkillRow[];
28
+ dispatch: (text: string, attachments?: readonly ContentBlock[], origin?: string) => void;
29
+ /** Submit as steering into the running turn (see {@link AppProps.steer}). */
30
+ steer: (text: string, attachments?: readonly ContentBlock[], origin?: string) => void;
31
+ /** Delivery mode the next submission uses; Tab on an empty composer flips it. */
32
+ submitMode: 'queue' | 'steer';
33
+ /** Flip {@link submitMode} and report the new mode. */
34
+ cycleSubmitMode: () => void;
35
+ /** The full current session identity ('' while pending); the delivery origin. */
36
+ sessionKey: string;
37
+ interrupt: () => boolean;
38
+ quit: () => void;
39
+ openModel: () => void;
40
+ openEffort: () => void;
41
+ openHelp: () => void;
42
+ openMode: () => void;
43
+ openPermission: () => void;
44
+ openResume: () => void;
45
+ /** Open the /search panel with an optional seed query. */
46
+ openSearch: (query: string) => void;
47
+ openPlugin: (query?: string) => void;
48
+ /** Open the /update panel (aligned upgrade surface). */
49
+ openUpdate: () => void;
50
+ /** Open the /schedule reminder panel (read-only catalog). */
51
+ openSchedule: () => void;
52
+ openJobs: () => void;
53
+ openStatusline: () => void;
54
+ openTheme: () => void;
55
+ /** Open the /language picker (bare /language). */
56
+ openLanguage: () => void;
57
+ /** Apply and persist a language chosen by argument. */
58
+ saveLanguage: (name: LanguageName) => void;
59
+ openHistory: () => void;
60
+ openQueue: () => void;
61
+ /** Open the /agents panel (live subagent feed + transcript entry). */
62
+ openAgents: () => void;
63
+ /** Open the /subagent model panel. */
64
+ openSubagent: () => void;
65
+ /** Open the /todos subpage (full todo list in one bounded panel). */
66
+ openTodos: () => void;
67
+ openUsage: () => void;
68
+ /** Open the dedicated /delete picker, optionally pre-armed on one id. */
69
+ openDelete: (id?: string) => void;
70
+ openDiff: (argument: string) => void;
71
+ reviewChanges: (selection: ReviewSelection) => void;
72
+ /** Open the /review candidate picker (bare /review). */
73
+ openReviewPicker: () => void;
74
+ /** The row id awaiting y/n in this box, when a deletion is pending. */
75
+ deleteConfirm?: string;
76
+ /** Confirm the pending deletion (y in the box). */
77
+ confirmDelete: () => void;
78
+ /** Cancel the pending deletion (any other key in the box). */
79
+ cancelDelete: () => void;
80
+ createSession: (mode?: string) => void;
81
+ forkSession: (argument: string) => void;
82
+ cancelSessionSwitch: () => boolean;
83
+ notify: (text: string, tone?: NoticeTone) => void;
84
+ /** Apply the Ctrl+R passthrough to the detected editor (/vscode-keys); resolves to a one-line summary. */
85
+ applyEditorKeys: () => Promise<string>;
86
+ hasNotice: boolean;
87
+ dismissNotice: () => void;
88
+ toggleReasoning: () => void;
89
+ openVerbose: () => void;
90
+ clearView: () => void;
91
+ refresh: () => void;
92
+ loadMentions: (query: string, signal?: AbortSignal) => Promise<readonly MentionCandidate[]>;
93
+ inspectImages: (paths: readonly string[]) => Promise<readonly ImagePathInspection[]>;
94
+ prepareImages: (paths: readonly string[], signal?: AbortSignal) => Promise<readonly ImageBlock[]>;
95
+ inspectFiles: (paths: readonly string[]) => Promise<readonly FilePathInspection[]>;
96
+ prepareFiles: (paths: readonly string[], signal?: AbortSignal) => Promise<readonly FileBlock[]>;
97
+ cycleMode: () => string;
98
+ exportTranscript: (argument: string) => Promise<void>;
99
+ renameTitle: (argument: string) => string;
100
+ copyLastResponse: () => Promise<string>;
101
+ /** Newest-first recall space (persistent + in-session, deduped). */
102
+ recallSpace: readonly string[];
103
+ /** Record one in-session submission (deduped, local only). */
104
+ recordLocal: (text: string) => void;
105
+ /** Persist one submission to the global history file. */
106
+ recordHistory: (text: string) => void;
107
+ /** Next-turn inbox rows, ordered exactly as the durable inbox. */
108
+ queued: readonly Extract<TranscriptEntry, {
109
+ kind: 'pending';
110
+ }>[];
111
+ updateQueued?: (messageId: string, action: QueueMutation) => void;
112
+ /** Accepted /history entry waiting to be placed into the composer. */
113
+ historyFill: {
114
+ text: string;
115
+ index: number;
116
+ } | undefined;
117
+ /** Marks the accepted entry consumed (called after the fill is applied). */
118
+ historyConsumed: () => void;
119
+ /** Whether decorative animations run; caret and busy activity stay live. */
120
+ animations: boolean;
121
+ /** Apply and report one /animation toggle (App persists through the runner). */
122
+ applyAnimations: (enabled: boolean) => void;
123
+ /** Reroll or pin the rainbow palette (switches to rainbow if needed). */
124
+ applyRainbow: (seed?: number) => void;
125
+ /** Monotonic id of the in-flight /rainbow composer burst; 0 means none. */
126
+ rainbowBurstId: number;
127
+ /** DeepSeek easter-egg wave tier of the applied route (null otherwise):
128
+ * official DeepSeek models drive their flash/pro tiers, non-DeepSeek
129
+ * models running an effort above high drive the "Into the Unknown"
130
+ * variant. Drives the persistent prompt glyph/accent and the sparkle
131
+ * tier. */
132
+ waveTier: DeepseekWaveTier | null;
133
+ /** The ignition style running, if any: Wave / Aurora / Pulse. */
134
+ waveStyle: DeepseekWaveStyle | null;
135
+ /** Maximum physical editor rows the composer may occupy (see composerMaxRows). */
136
+ maxRows: number;
137
+ /** Terminal rows below the composer the editor does not own: the status
138
+ * footer and Ink's parked cursor row. The IME anchor adds these to the
139
+ * caret's in-band offset to reach that parked position. */
140
+ anchorRowsBelow: number;
141
+ /** The managed terminal tab label; re-asserted on terminal focus-in so a
142
+ * background process sharing the console cannot keep it overwritten. */
143
+ tabTitle: string;
144
+ /** Reports the editor's current physical row count so the live budget stays exact. */
145
+ onEditorRows: (rows: number) => void;
146
+ /** Reports the open completion menu's physical row count (0 when closed)
147
+ * for the same reason: the dynamic budget must reserve it, not overflow. */
148
+ onMenuRows: (rows: number) => void;
149
+ }): ReactElement;
150
+ /** One cached settled row: the row Box plus its roomy-prompt spacers. */
@@ -119,3 +119,9 @@ export declare function parseReviewConclusion(text: string): ReviewConclusion |
119
119
  * priority plus the overall verdict, or a no-findings phrasing.
120
120
  */
121
121
  export declare function reviewSummaryLine(conclusion: ReviewConclusion): string;
122
+ /**
123
+ * Resolve the working directory's git branch for the status line.
124
+ * @param cwd - the session's working directory.
125
+ * @returns the branch name, or '' outside a repository or on a detached HEAD.
126
+ */
127
+ export declare function gitBranch(cwd: string): string;
@@ -10,13 +10,12 @@
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';
14
- import { type ContentBlock } from '@deepseek-ai/dsh-llm';
15
- import { SessionId, type SessionEvent, type SessionHeader, type UserMessage } from '@deepseek-ai/dsh-session';
16
- import { type SessionPersistence } from '@deepseek-ai/dsh-session-persistence';
17
- import { type QueueMutation } from './app.ts';
18
- import type { TuiStartup } from './startup.ts';
19
- import type { SearchRow } from './kernel-panels.ts';
13
+ export { planCycleDecision, type ModeCycleDecision } from './runner/mode-cycle.ts';
14
+ export { runQuitSequence, type QuitCleanupStep } from './runner/quit.ts';
15
+ export { exportSessionIdSuffix, resolveTarget } from './runner/session-target.ts';
16
+ export { applyQueueMutation, cancelPreservingQueue, queueEditContent, StartupInputGate, submissionBelongsToSession, type QueuedSubmission, type QueueMutationOutcome, } from './runner/submissions.ts';
17
+ export { subagentCatalogSeed } from './session/subagents.ts';
18
+ export { searchHitToRow } from './runner/search-rows.ts';
20
19
  /** Stable Cordis plugin name. */
21
20
  export declare const name = "tui-runner";
22
21
  /** Core services required before the interactive session can start. */
@@ -34,190 +33,9 @@ export interface Config {
34
33
  };
35
34
  }
36
35
  export declare const Config: z<Config>;
37
- /** The session identity this invocation will run, plus whether it is resumed. */
38
- interface Target {
39
- sessionId: string;
40
- resume: boolean;
41
- mode?: string;
42
- cwd?: string;
43
- seed?: readonly SessionEvent[];
44
- parentSession?: SessionId;
45
- /** Marks the session as a subagent conversation in the durable header. */
46
- origin?: 'subagent';
47
- seedLength?: number;
48
- }
49
- /**
50
- * Reduce a session id to a filename-safe /export default-name suffix. Session
51
- * ids are normally minted `session-<uuid>`, but `--session` accepts arbitrary
52
- * user text: path separators must never leak into the default export filename
53
- * (which would escape the session cwd).
54
- * @param id - the session id.
55
- * @returns at most the last 8 filename-safe characters.
56
- */
57
- export declare function exportSessionIdSuffix(id: string): string;
58
- /** One ordered step of the terminal quit cleanup. */
59
- export interface QuitCleanupStep {
60
- /** Step label used in diagnostics and tests. */
61
- readonly name: string;
62
- /** The step's async work; a rejection is contained by the sequence. */
63
- readonly run: () => Promise<void>;
64
- }
65
- /**
66
- * Run the ordered quit cleanup, then request exit. Every step rejection is
67
- * contained (reported through `onError`) so a failed flush or dispose never
68
- * skips the remaining cleanup; the exit request is always reached exactly
69
- * once.
70
- * @param steps - the cleanup steps in dependency order (settle the visible
71
- * session, await the final in-flight composition, await durable recall).
72
- * @param exit - the terminal exit request (code 0).
73
- * @param onError - optional failure sink; called once per failing step and
74
- * itself contained, so a throwing sink cannot abort the sequence.
75
- * @returns the names of the steps that started, in order (for tests).
76
- */
77
- export declare function runQuitSequence(steps: readonly QuitCleanupStep[], exit: (code: number) => void, onError?: (name: string, error: unknown) => void): Promise<readonly string[]>;
78
- /** One composer submission waiting behind the startup delivery. */
79
- export interface QueuedSubmission {
80
- readonly text: string;
81
- /** `steer` inserts into the running turn; `followup` waits for the next one. */
82
- readonly mode: 'followup' | 'steer';
83
- readonly images: readonly ContentBlock[];
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;
123
- /**
124
- * Whether a tagged submission still belongs to the active session. Attachment
125
- * prepares resolve on the microtask timeline, while a queued session switch
126
- * remounts the app asynchronously — the composing instance's unmount cleanup
127
- * runs too late to abort, so the delivery itself carries the composing
128
- * session's full id and the runner drops it here when the world moved on.
129
- * An untagged (synchronous) or pending-session ('') submission always passes.
130
- */
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;
151
- /** One Shift+Tab station decision for the mode cycle. */
152
- export type ModeCycleDecision = {
153
- readonly kind: 'permission';
154
- readonly preset: string;
155
- } | {
156
- readonly kind: 'plan-on';
157
- } | {
158
- readonly kind: 'plan-off';
159
- readonly preset: string;
160
- };
161
- /**
162
- * Decide the next Shift+Tab station. The cycle keeps the preset table's
163
- * own order (most restrictive first) and inserts ONE plan station between
164
- * the most restrictive preset and the wrap target: with the shipped three
165
- * presets the user sees workspace-write → danger-full-access → read-only
166
- * → plan → workspace-write. Plan IS the most restrictive preset plus the
167
- * plan prompt layer — entering it switches nothing (the cycle is already
168
- * parked on read-only), and leaving it lands on the next preset after the
169
- * most restrictive one. Without the /plan command the cycle is exactly the
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.
179
- */
180
- export declare function planCycleDecision(input: {
181
- readonly names: readonly string[];
182
- readonly current: string;
183
- readonly inPlan: boolean;
184
- readonly planAvailable: boolean;
185
- readonly planIntent?: boolean;
186
- }): ModeCycleDecision | undefined;
187
- /**
188
- * Order-preserving gate for composer input while the startup prompt/images
189
- * are still preparing. Anything submitted before the startup delivery settles
190
- * queues and flushes afterwards in submit order, so the initial request can
191
- * never be overtaken by typing that raced a slow image preparation. The flush
192
- * also runs when the startup delivery fails: user input is never stranded.
193
- */
194
- export declare class StartupInputGate {
195
- private readonly deliver;
196
- private readonly queued;
197
- private pending;
198
- constructor(deliver: (submission: QueuedSubmission) => void);
199
- /** Submit one line: delivered now while idle, queued behind the startup delivery otherwise. */
200
- submit(submission: QueuedSubmission): void;
201
- /**
202
- * Run the startup delivery — the callback receives the direct-delivery sink
203
- * for the startup prompt itself — then flush everything that queued behind
204
- * it, in order, even when the callback rejects.
205
- */
206
- run(startup: (deliver: (submission: QueuedSubmission) => void) => Promise<void>): Promise<void>;
207
- }
208
- /**
209
- * Resolve the invocation's target session against the persisted headers.
210
- * @param startup - the parsed startup flags.
211
- * @param persistence - the persistence service; required for resume/latest.
212
- * @param cwd - the working directory `--continue` filters by.
213
- * @returns the target identity.
214
- * @throws with a user-facing message when the flags name nothing resolvable.
215
- */
216
- export declare function resolveTarget(startup: TuiStartup, persistence: SessionPersistence | undefined, cwd: string): Promise<Target>;
217
36
  /**
218
37
  * Mount the interactive terminal driver.
219
38
  * @param ctx - plugin context carrying core services and the launcher-provided exit request.
220
39
  * @param config - validated startup config resolved from the tuiStartup provider.
221
40
  */
222
41
  export declare function apply(ctx: Context, config: Config): void;
223
- export {};