dsh-code 1.2.0 → 1.4.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 (102) hide show
  1. package/README.en.md +5 -5
  2. package/README.md +5 -5
  3. package/lib/index.mjs +6679 -5232
  4. package/lib/startup.mjs +1 -1
  5. package/lib/{theme-7u5Qo3dF.mjs → theme-B3orFUYz.mjs} +8 -0
  6. package/lib/types/app.d.ts +32 -45
  7. package/lib/types/attachments.d.ts +16 -7
  8. package/lib/types/completion.d.ts +29 -0
  9. package/lib/types/composer.d.ts +150 -0
  10. package/lib/types/git-workflow.d.ts +6 -0
  11. package/lib/types/index.d.ts +6 -188
  12. package/lib/types/locales/en.d.ts +65 -5
  13. package/lib/types/{authorization-panel.d.ts → panels/authorization-panel.d.ts} +6 -1
  14. package/lib/types/panels/completion-panel.d.ts +13 -0
  15. package/lib/types/panels/interaction-bars.d.ts +38 -0
  16. package/lib/types/{kernel-panels.d.ts → panels/kernel-panels.d.ts} +19 -38
  17. package/lib/types/{language-panel.d.ts → panels/language-panel.d.ts} +1 -1
  18. package/lib/types/panels/model-panels.d.ts +86 -0
  19. package/lib/types/{theme-panel.d.ts → panels/theme-panel.d.ts} +1 -1
  20. package/lib/types/{update-panel.d.ts → panels/update-panel.d.ts} +22 -1
  21. package/lib/types/provider-settings.d.ts +11 -0
  22. package/lib/types/render/inspector.d.ts +8 -0
  23. package/lib/types/render/status.d.ts +4 -1
  24. package/lib/types/render/text.d.ts +4 -0
  25. package/lib/types/runner/harness-gate.d.ts +83 -0
  26. package/lib/types/runner/input-history.d.ts +31 -0
  27. package/lib/types/runner/mode-cycle.d.ts +44 -0
  28. package/lib/types/runner/preferences.d.ts +40 -0
  29. package/lib/types/runner/quit.d.ts +27 -0
  30. package/lib/types/runner/search-rows.d.ts +38 -0
  31. package/lib/types/runner/session-io.d.ts +46 -0
  32. package/lib/types/runner/session-target.d.ts +42 -0
  33. package/lib/types/runner/startup-config.d.ts +33 -0
  34. package/lib/types/runner/submissions.d.ts +87 -0
  35. package/lib/types/session/attach.d.ts +39 -0
  36. package/lib/types/{history.d.ts → session/history.d.ts} +10 -0
  37. package/lib/types/{session-directory.d.ts → session/session-directory.d.ts} +25 -1
  38. package/lib/types/{session-switch.d.ts → session/session-switch.d.ts} +8 -0
  39. package/lib/types/{store.d.ts → session/store.d.ts} +1 -1
  40. package/lib/types/{subagents.d.ts → session/subagents.d.ts} +8 -1
  41. package/lib/types/settings-file.d.ts +10 -0
  42. package/lib/types/{panel-accent.d.ts → ui/panel-accent.d.ts} +1 -1
  43. package/lib/types/ui/panel-gap.d.ts +6 -0
  44. package/lib/types/ui/query-editor.d.ts +10 -0
  45. package/lib/types/ui/styled-rows.d.ts +8 -0
  46. package/lib/types/{terminal-title.d.ts → ui/terminal-title.d.ts} +1 -1
  47. package/lib/types/ui/ui-contract.d.ts +12 -0
  48. package/lib/types/ui/use-frames.d.ts +6 -0
  49. package/lib/types/ui/use-stable-input.d.ts +7 -0
  50. package/lib/types/version.d.ts +2 -0
  51. package/package.json +7 -5
  52. package/src/app.ts +707 -4108
  53. package/src/attachments.ts +65 -19
  54. package/src/completion.ts +117 -0
  55. package/src/composer.ts +1956 -0
  56. package/src/git-workflow.ts +18 -0
  57. package/src/index.ts +164 -645
  58. package/src/input-split.ts +24 -4
  59. package/src/internals.ts +1 -1
  60. package/src/locales/en.ts +66 -5
  61. package/src/locales/zh.ts +66 -5
  62. package/src/{authorization-panel.ts → panels/authorization-panel.ts} +30 -8
  63. package/src/panels/completion-panel.ts +79 -0
  64. package/src/panels/interaction-bars.ts +567 -0
  65. package/src/{kernel-panels.ts → panels/kernel-panels.ts} +142 -90
  66. package/src/{language-panel.ts → panels/language-panel.ts} +4 -4
  67. package/src/panels/model-panels.ts +1021 -0
  68. package/src/{theme-panel.ts → panels/theme-panel.ts} +5 -5
  69. package/src/{update-panel.ts → panels/update-panel.ts} +117 -10
  70. package/src/provider-settings.ts +38 -0
  71. package/src/rainbow.ts +13 -3
  72. package/src/render/inspector.ts +23 -0
  73. package/src/render/status.ts +80 -29
  74. package/src/render/text.ts +10 -1
  75. package/src/runner/harness-gate.ts +168 -0
  76. package/src/runner/input-history.ts +77 -0
  77. package/src/runner/mode-cycle.ts +49 -0
  78. package/src/runner/preferences.ts +67 -0
  79. package/src/runner/quit.ts +53 -0
  80. package/src/runner/search-rows.ts +55 -0
  81. package/src/runner/session-io.ts +206 -0
  82. package/src/runner/session-target.ts +81 -0
  83. package/src/runner/startup-config.ts +54 -0
  84. package/src/runner/submissions.ts +157 -0
  85. package/src/session/attach.ts +87 -0
  86. package/src/{fork.ts → session/fork.ts} +11 -7
  87. package/src/{history.ts → session/history.ts} +14 -0
  88. package/src/{session-directory.ts → session/session-directory.ts} +83 -4
  89. package/src/{session-switch.ts → session/session-switch.ts} +14 -0
  90. package/src/{store.ts → session/store.ts} +20 -2
  91. package/src/{subagents.ts → session/subagents.ts} +12 -1
  92. package/src/settings-file.ts +19 -1
  93. package/src/{panel-accent.ts → ui/panel-accent.ts} +1 -1
  94. package/src/ui/panel-gap.ts +9 -0
  95. package/src/ui/query-editor.ts +16 -0
  96. package/src/ui/styled-rows.ts +124 -0
  97. package/src/{terminal-title.ts → ui/terminal-title.ts} +1 -1
  98. package/src/ui/ui-contract.ts +10 -0
  99. package/src/ui/use-frames.ts +23 -0
  100. package/src/ui/use-stable-input.ts +17 -0
  101. package/src/version.ts +5 -0
  102. /package/lib/types/{fork.d.ts → session/fork.d.ts} +0 -0
package/lib/startup.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { r as THEME_NAMES } from "./theme-7u5Qo3dF.mjs";
1
+ import { r as THEME_NAMES } from "./theme-B3orFUYz.mjs";
2
2
  import { Command } from "commander";
3
3
  import { parseCmdline } from "@deepseek-ai/dsh-cmdline";
4
4
  //#region src/startup.ts
@@ -538,6 +538,10 @@ function hueOf([r, g, b]) {
538
538
  if (max === g) return (60 * (b - r) / span + 120) % 360;
539
539
  return (60 * (r - g) / span + 240) % 360;
540
540
  }
541
+ /** Strict RGB equality for the wrap-collision repair. */
542
+ function sameRgb(left, right) {
543
+ return left[0] === right[0] && left[1] === right[1] && left[2] === right[2];
544
+ }
541
545
  /** A dark row tint for diff backgrounds: the hue at 22% strength over black. */
542
546
  function darkTint(hue) {
543
547
  return [
@@ -603,6 +607,10 @@ function rollRainbow(seed) {
603
607
  TONE_ORDER.forEach((tone, index) => {
604
608
  toneColors[tone] = pool[(offset + index) % pool.length];
605
609
  });
610
+ if (sameRgb(toneColors.live, toneColors.error)) {
611
+ const replacement = pool.find((hue) => !sameRgb(hue, toneColors.live) && !sameRgb(hue, toneColors.warn));
612
+ if (replacement !== void 0) toneColors.error = replacement;
613
+ }
606
614
  return {
607
615
  seed,
608
616
  palette,
@@ -14,42 +14,34 @@
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';
53
45
  /** Props the runner hands the app; callbacks stay owned by the runner. */
54
46
  export interface AppProps {
55
47
  /** Event-fed transcript store for the live session. */
@@ -132,6 +124,8 @@ export interface AppProps {
132
124
  subscribeModelProviders?: (listener: () => void) => () => void;
133
125
  /** Store or rotate one provider credential through the Harness credential service. */
134
126
  saveModelProviderCredential?: (target: ProviderTargetView, key: string) => Promise<void>;
127
+ /** Switch a provider route to its subscription channel (drops the key reference). */
128
+ enableModelProviderSubscription?: (target: ProviderTargetView) => Promise<void>;
135
129
  /** Remove one writable provider credential without removing its settings profile. */
136
130
  unsetModelProviderCredential?: (target: ProviderTargetView) => Promise<void>;
137
131
  /** Remove one user-owned provider profile and its page-managed credential. */
@@ -195,6 +189,8 @@ export interface AppProps {
195
189
  searchSessions?: (query: string, signal?: AbortSignal) => Promise<readonly SearchRow[]>;
196
190
  /** Load this session's subagent conversations (children by lineage). */
197
191
  loadSubagents: () => Promise<readonly SessionRow[]>;
192
+ /** Live subagent attachment: seed + the two real-time buses, runner-wired. */
193
+ attachSubagent?: SubagentAttachmentServices;
198
194
  switchSession: (row: SessionRow) => void;
199
195
  cancelSessionSwitch: () => boolean;
200
196
  loadPlugins: () => readonly PluginRow[];
@@ -234,32 +230,24 @@ export interface AppProps {
234
230
  /** Apply the Ctrl+R terminal passthrough to the detected editor (/vscode-keys); resolves to a one-line summary. */
235
231
  applyEditorKeys: () => Promise<string>;
236
232
  }
233
+ /**
234
+ * The streaming buffer rendered with a hard size cap: the live region must
235
+ * ALWAYS fit the terminal, or Ink's erase/rewrite of a dynamic tree taller
236
+ * than the screen freezes (cursor-up past the top, garbage, no scroll). The
237
+ * cap counts explicit newlines and terminal wrapping, slicing from the END so
238
+ * the freshest tokens stay visible while a long reply streams; the complete
239
+ * text lands in the flushed scrollback once the turn assembles it.
240
+ *
241
+ * Body wrap width for a streaming tail. `rowColumns` is the same width
242
+ * passed to `transcriptEntryLines` (terminal minus the last-column safety);
243
+ * the hanging prefix then shrinks the body so streamed text and settled
244
+ * markdown wrap on the same column.
245
+ */
246
+ export declare function streamTailBodyColumns(rowColumns: number, prefix: string, continuationPrefix?: string): number;
237
247
  /** Rows in the exact next-turn inbox order, never transcript append order. */
238
248
  export declare function queuedInboxRows(entries: readonly TranscriptEntry[], ids: readonly string[]): readonly Extract<TranscriptEntry, {
239
249
  kind: 'pending';
240
250
  }>[];
241
- /** One completion candidate row. */
242
- interface CompletionCandidate {
243
- /** Insertion text for the command name (with leading slash). */
244
- label: string;
245
- /** Human-readable description shown beside the label. */
246
- description: string;
247
- /** Candidate origin; skills land the same literal text but route through the prompt. */
248
- origin: 'command' | 'skill' | 'mention';
249
- }
250
- /**
251
- * Resolve completion candidates for the current input: TUI-local commands,
252
- * the live registry descriptors, and user-invocable skills, filtered by the
253
- * typed prefix. Command names win collisions (the dispatch tries the
254
- * registry first and only then falls through to the skill gesture), and a
255
- * later duplicate name never renders twice.
256
- *
257
- * A bare `/` returns the FULL merged list — Codex's command popup shows every
258
- * command inside a scroll window on an empty filter, and the menu's own
259
- * selection window bounds the visible rows, so no slice cap is needed.
260
- */
261
- export declare function completionCandidates(value: string, descriptors: readonly CommandDescriptor[], skills: readonly SkillRow[]): readonly CompletionCandidate[];
262
- /** One cached settled row: the row Box plus its roomy-prompt spacers. */
263
251
  interface SettledRowRecord {
264
252
  /** The row Box element (keyed by the entry's settled index). */
265
253
  box: ReactElement;
@@ -345,4 +333,3 @@ interface SettledRowsResult {
345
333
  export declare function computeSettledRows(previous: SettledRowsCache | undefined, entries: readonly TranscriptEntry[], settled: number, showReasoning: boolean, resumed: boolean, epoch: number, columns?: number, rowCap?: number): SettledRowsResult;
346
334
  /** The whole terminal app; state arrives via the store, output via Ink. */
347
335
  export declare function App(props: AppProps): ReactElement;
348
- export {};
@@ -26,17 +26,26 @@ export declare const MAX_FILES_PER_MESSAGE = 8;
26
26
  export declare function detectImageMediaType(data: Uint8Array): ImageMediaType | undefined;
27
27
  /** Whether a path-like token is worth probing as an image attachment. */
28
28
  export declare function looksLikeImagePath(path: string): boolean;
29
+ /** Strip one layer of ASCII or Unicode quotes and shell-escaped spaces. */
30
+ export declare function unwrapDroppedPath(token: string): string;
31
+ /** Absolute/relative drop with a dotted leaf — including unquoted spaces. */
32
+ export declare function looksLikeFilesystemDrop(path: string): boolean;
29
33
  /**
30
- * Parse a paste/drop into its image and file paths: image-suffixed tokens
31
- * stay images, other path-shaped tokens ride as file attachments (0.1.5
32
- * file blocks), and anything that is neither leaves both empty — the caller
33
- * then treats the paste as plain text.
34
+ * A composer draft that is a filesystem path, not a slash command.
35
+ * `/usage` stays a command; `/Users/foo.png` and `C:\temp\a.png` are drops.
36
+ */
37
+ export declare function looksLikePathDraft(value: string): boolean;
38
+ /**
39
+ * Parse a paste or file-drop into image and file paths: image-suffixed
40
+ * tokens stay images, other path-shaped tokens ride as file attachments
41
+ * (0.1.5 file blocks), and anything that is neither leaves both empty —
42
+ * the caller then treats the paste as plain text.
34
43
  *
35
44
  * File tokens are held to an absolute-path-with-shape bar (drive/backslash
36
- * or a dot-suffixed leaf after a separator): a dropped terminal path always
45
+ * or a dotted leaf after a separator): a dropped terminal path always
37
46
  * carries one of those, while prose, slash commands, and option flags never
38
- * do. A POSIX absolute path without any dot-suffixed leaf falls through as
39
- * text — the @ mention route still attaches such files deliberately.
47
+ * do. A POSIX absolute path without a dotted leaf falls through as text —
48
+ * the @ mention route still attaches such files deliberately.
40
49
  */
41
50
  export declare function parsePastedAttachmentPaths(input: string): {
42
51
  readonly images: readonly string[];
@@ -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 timed animations run (shimmer, chase, blink, wave). */
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 {};