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
@@ -86,8 +86,15 @@ export declare const en: {
86
86
  readonly 'schedule.titlePlural': "/schedule · {count} active reminders";
87
87
  readonly 'schedule.empty': "no active reminders — the model creates them with schedule_create";
88
88
  readonly 'document.loading': "loading transcript…";
89
+ readonly 'attach.bar': "observing · {label} · r reload · esc/ctrl+d back";
90
+ readonly 'attach.loading': "loading {label}'s conversation…";
91
+ readonly 'attach.status': "subagent {label} · {id} · {turns} turns";
92
+ readonly 'notice.attachUnavailable': "subagent attachment is unavailable in this profile";
89
93
  readonly 'document.compact': "transcript · esc close";
90
- readonly 'document.footer': "lines {from}-{to}/{total} · ↑↓/pg/g/G · t/esc close";
94
+ readonly 'document.expanded': "expanded";
95
+ readonly 'document.refreshHint': "r refresh";
96
+ readonly 'document.footerExpanded': "lines {from}-{to}/{total} · ↑↓/pg/g/G{refresh} · esc/ctrl+d back";
97
+ readonly 'document.footer': "lines {from}-{to}/{total} · ↑↓/pg/g/G · enter/f expand · t/esc close";
91
98
  readonly 'review.picker.title': "/review · choose a target";
92
99
  readonly 'review.picker.uncommitted': "Review uncommitted changes (staged / unstaged / new files)";
93
100
  readonly 'review.picker.branch': "Compare against a base branch (lists local branches)";
@@ -132,7 +139,7 @@ export declare const en: {
132
139
  readonly 'cmd.theme': "switch the color theme";
133
140
  readonly 'cmd.language': "switch the interface language (/language [en|zh])";
134
141
  readonly 'cmd.rainbow': "reroll or pin the rainbow palette (/rainbow [seed])";
135
- readonly 'cmd.animation': "toggle timed animations (/animation [on|off])";
142
+ readonly 'cmd.animation': "toggle decorative animations; shimmer and activity stay live (/animation [on|off])";
136
143
  readonly 'cmd.history': "search and recall past prompts";
137
144
  readonly 'cmd.queue': "manage messages queued for the next turn";
138
145
  readonly 'cmd.usage': "show this session token usage";
@@ -268,12 +275,12 @@ export declare const en: {
268
275
  readonly 'notice.imageCancelled': "image submission cancelled";
269
276
  readonly 'notice.themeConfigUnreadable': "theme config unreadable, using dark: {message}";
270
277
  readonly 'notice.languageConfigUnreadable': "language config unreadable, using english: {message}";
271
- readonly 'notice.animationsConfigUnreadable': "animations config unreadable, animations stay on: {message}";
278
+ readonly 'notice.animationsConfigUnreadable': "decorative animation config unreadable, effects stay on: {message}";
272
279
  readonly 'notice.statuslineConfigUnreadable': "statusline config unreadable, using defaults: {message}";
273
280
  readonly 'notice.sessionSearchUnavailable': "session search is unavailable in this deployment";
274
281
  readonly 'notice.subagentsReadOnly': "subagent conversations are read-only";
275
282
  readonly 'notice.statuslineSaveFailed': "statusline save failed: {message}";
276
- readonly 'notice.animationsSaveFailed': "animations save failed: {message}";
283
+ readonly 'notice.animationsSaveFailed': "decorative animation save failed: {message}";
277
284
  readonly 'notice.commandRegistryMissing': "no command registry is mounted in this composition";
278
285
  readonly 'notice.commandFallbackFailed': "command fallback failed: {message}";
279
286
  readonly 'notice.commandFailed': "command failed: {message}";
@@ -324,7 +331,7 @@ export declare const en: {
324
331
  readonly 'notice.commandAttachments': "commands cannot carry attachments; the line will be sent to the model as a prompt";
325
332
  readonly 'notice.copyFailed': "copy failed: {message}";
326
333
  readonly 'notice.vscodeKeysFailed': "vscode-keys failed: {message}";
327
- readonly 'notice.animationState': "animations {state}";
334
+ readonly 'notice.animationState': "decorative animations {state}; activity indicators stay live";
328
335
  readonly 'notice.deleteFailed': "delete failed: {message}";
329
336
  readonly 'notice.modelChangedPlaceholder': "model → {model} · image history will be sent as text placeholders";
330
337
  readonly 'notice.modelNextStep': "model → next step uses {model}";
@@ -417,7 +424,6 @@ export declare const en: {
417
424
  readonly 'panel.provider.notConfiguredDivider': "── not configured ──";
418
425
  readonly 'panel.provider.title': "/model — providers";
419
426
  readonly 'panel.provider.titleCount': "/model — providers · {index}/{total}";
420
- readonly 'panel.provider.state': "{route} · {value}";
421
427
  readonly 'panel.provider.key': "key {value}";
422
428
  readonly 'panel.provider.authConfigured': "provider auth";
423
429
  readonly 'panel.provider.noKey': "key missing";
@@ -432,7 +438,7 @@ export declare const en: {
432
438
  readonly 'panel.provider.readOnlyLogin': "this login record is read-only";
433
439
  readonly 'panel.provider.notManaged': "this provider is not managed by Harness settings";
434
440
  readonly 'panel.providers.compact': "/model providers · enter configure · d remove key · esc back";
435
- readonly 'panel.providers.footer': "↑↓ move · enter configure · l login · o logout · d remove key · x remove provider · r retry · esc back";
441
+ readonly 'panel.providers.footer': "↑↓ move · enter configure · d remove key · x remove provider · r retry · esc back";
436
442
  readonly 'panel.update.compact': "/update · {summary}";
437
443
  readonly 'panel.update.checking': "checking…";
438
444
  readonly 'panel.update.probeFailed': "probe failed";
@@ -460,8 +466,14 @@ export declare const en: {
460
466
  readonly 'panel.setup.copyInto': "copy verbatim into {id}";
461
467
  readonly 'panel.setup.copyTitle': "/model — copy efforts";
462
468
  readonly 'panel.setup.copyFooter': "↑↓ move · enter copy · esc back";
469
+ readonly 'panel.setup.mode.subscribe': "Subscription";
470
+ readonly 'panel.setup.mode.key': "Key";
471
+ readonly 'panel.setup.mode.login.plan': "signed in · plan";
472
+ readonly 'panel.setup.mode.login.none': "not signed in";
473
+ readonly 'panel.setup.mode.login.running': "signing in…";
474
+ readonly 'panel.setup.mode.overridden': "⚠ the settings key overrides the plan sign-in";
463
475
  readonly 'panel.setup.title': "/model — configure {provider}";
464
- readonly 'panel.setup.footer': "↑↓ move · ←→ in/out · space remove · e efforts · c copy efforts · tab discover · enter save · esc back";
476
+ readonly 'panel.setup.footer': "↑↓ move · ←→ in/out · space remove · e efforts · c copy efforts · tab per channel (subscription = web sign-in, key = discover) · enter save · esc back";
465
477
  readonly 'panel.discovery.compact': "model discovery · terminal too small · esc back";
466
478
  readonly 'panel.discovery.loading': "discovering models…";
467
479
  readonly 'panel.discovery.empty': "the endpoint advertised no models; add ids by hand on the setup page";
@@ -2,9 +2,14 @@
2
2
  import { type ReactElement } from 'react';
3
3
  import { type AuthorizationInteraction, type AuthorizationStatus } from '@deepseek-ai/dsh-authorization';
4
4
  import type { CredentialKey } from '@deepseek-ai/dsh-credentials';
5
- import type { ProviderAuthorizationRow } from './authorization.ts';
5
+ import type { ProviderAuthorizationRow } from '../authorization.ts';
6
6
  export interface ProviderAuthorizationPanelProps {
7
7
  readonly row: ProviderAuthorizationRow;
8
+ /**
9
+ * Start this method immediately instead of showing the picker: a
10
+ * single-method provider signs in with no extra step.
11
+ */
12
+ readonly autoStartMethod?: string;
8
13
  begin(row: ProviderAuthorizationRow, method: string, interaction: AuthorizationInteraction, signal: AbortSignal): Promise<AuthorizationStatus>;
9
14
  cancel(key: CredentialKey): void;
10
15
  openUrl(url: string): boolean;
@@ -0,0 +1,13 @@
1
+ /** Bounded slash-command and mention completion menu. */
2
+ import { type ReactElement } from 'react';
3
+ import type { CompletionCandidate } from '../completion.ts';
4
+ /** Total physical rows occupied by the completion menu at this terminal height. */
5
+ export declare function completionMenuRowCount(terminalRows: number, rowCount: number): number;
6
+ /** Completion view anchored directly above the composer band. */
7
+ export declare function CompletionMenu({ active, mention, index, rows, error }: {
8
+ active: boolean;
9
+ mention: boolean;
10
+ index: number;
11
+ rows: readonly CompletionCandidate[];
12
+ error?: string;
13
+ }): ReactElement | undefined;
@@ -0,0 +1,38 @@
1
+ /** Approval and structured-question keyboard-owned terminal surfaces. */
2
+ import { type ReactElement } from 'react';
3
+ import type { ApprovalSnapshot } from '../approval.ts';
4
+ import type { QuestionSnapshot, QuestionStore } from '../questions.ts';
5
+ import type { NoticeTone } from '../ui/ui-contract.ts';
6
+ /**
7
+ * The approval dialog (Codex ApprovalOverlay contract): a bold question
8
+ * header, the bounded command body with an explicit overflow marker, a
9
+ * numbered option list with a `›` cursor, single-key shortcuts, and digits
10
+ * for direct selection. Askers queue FIFO — the count rides the header.
11
+ * The upstream answerer vocabulary stays binary (`allowed-once` /
12
+ * `rejected`): "tell it what to do differently" rejects and hands the
13
+ * composer back with a hint notice, exactly Codex's decline-then-type flow.
14
+ */
15
+ export declare function ApprovalBar({ snapshot, locked, notify, interrupt, summarize }: {
16
+ snapshot: ApprovalSnapshot;
17
+ locked: boolean;
18
+ notify: (text: string, tone?: NoticeTone) => void;
19
+ /** Cancel the running turn (Ctrl+C), matching the composer's busy branch. */
20
+ interrupt: () => boolean;
21
+ /** Render as the bounded one-line form even on tall terminals (another
22
+ * human-asked surface already owns the full panel budget). */
23
+ summarize?: boolean;
24
+ }): ReactElement | undefined;
25
+ /**
26
+ * The ask_user_question bar: walks one request question by question,
27
+ * retaining an independent draft for every question. Options use Space/1-9
28
+ * to toggle a multi-select, Enter to confirm, and arrows/Ctrl+P/N to move
29
+ * between questions. Plan reviews arrive through the same service with a
30
+ * `plan-review` intent — the approve option gets a ✓ mark, the answer
31
+ * encoding stays identical.
32
+ */
33
+ export declare function QuestionBar({ store, snapshot, locked }: {
34
+ store: QuestionStore;
35
+ snapshot: QuestionSnapshot;
36
+ locked: boolean;
37
+ }): ReactElement | undefined;
38
+ /** The /model panel: a scrolling list over the advisory model directory. */
@@ -1,24 +1,18 @@
1
1
  /** Bounded, composer-safe panels for preset, session, and plugin kernel views. */
2
2
  import { type ReactElement } from 'react';
3
- import type { ModelDirectory, ModelRow } from './models.ts';
4
- import type { SubagentRow } from './subagents.ts';
5
- import type { ScheduleRow } from './render/projection.ts';
6
- import type { PermissionRow } from './permissions.ts';
7
- import type { PresetRow } from './presets.ts';
8
- import type { PluginRow } from './plugin-inventory.ts';
9
- import type { SessionDirectoryOptions, SessionRow } from './session-directory.ts';
10
- import type { ReviewBranch, ReviewCommit, ReviewSelection } from './git-workflow.ts';
11
- import { type UsageView } from './render/usage.ts';
12
- import { type StatusItemId } from './render/status.ts';
13
- /**
14
- * Apply one keystroke to a panel search query. IME commits arrive as one
15
- * multi-character chunk, so the whole printable run is appended; paste
16
- * markers are stripped and control-laden chunks are ignored.
17
- */
18
- export declare function editQuery(query: string, input: string, key: {
19
- backspace?: boolean;
20
- delete?: boolean;
21
- }): string | undefined;
3
+ import type { ModelDirectory, ModelRow } from '../models.ts';
4
+ import type { SubagentRow } from '../session/subagents.ts';
5
+ import type { SearchRow } from '../runner/search-rows.ts';
6
+ export type { SearchRow } from '../runner/search-rows.ts';
7
+ import type { ScheduleRow } from '../render/projection.ts';
8
+ import type { PermissionRow } from '../permissions.ts';
9
+ import type { PresetRow } from '../presets.ts';
10
+ import type { PluginRow } from '../plugin-inventory.ts';
11
+ import type { SessionDirectoryOptions, SessionRow } from '../session/session-directory.ts';
12
+ import type { ReviewBranch, ReviewCommit, ReviewSelection } from '../git-workflow.ts';
13
+ import { type UsageView } from '../render/usage.ts';
14
+ import { type StatusItemId } from '../render/status.ts';
15
+ export { editQuery } from '../ui/query-editor.ts';
22
16
  export declare function ModePanel({ current, load, select, close }: {
23
17
  current: string;
24
18
  load: () => Promise<readonly PresetRow[]>;
@@ -110,23 +104,6 @@ export declare function ReviewPickerPanel({ loadBranches, loadCommits, choose, c
110
104
  choose: (selection: ReviewSelection) => void;
111
105
  close: () => void;
112
106
  }): ReactElement;
113
- /** One cross-session full-text search hit mapped from the session-query engine. */
114
- export interface SearchRow {
115
- /** Session id (Enter resumes it through the switch machinery). */
116
- readonly id: string;
117
- /** Display label: session title or the short id form. */
118
- readonly label: string;
119
- /** Secondary facts line (workspace · preset markers). */
120
- readonly detail: string;
121
- /** Bounded plain-text excerpt around the strongest match. */
122
- readonly snippet: string;
123
- /** Match timestamp (relative labels derive from it). */
124
- readonly updatedAt: number;
125
- /** Whether the hit is a delegated subagent conversation (not resumable). */
126
- readonly subagent: boolean;
127
- /** Whether Enter may switch into it. */
128
- readonly resumable: boolean;
129
- }
130
107
  /**
131
108
  * The /search panel: full-text search over every persisted session through
132
109
  * the in-process session-query engine (the same corpus the model's
@@ -182,13 +159,15 @@ export declare function EffortPanel({ row, current, select, back, onExit }: {
182
159
  * Enter/t opening the child's full transcript in the shared read-only
183
160
  * document view (the same projection the exporter uses).
184
161
  */
185
- export declare function AgentsPanel({ live, load, readTranscript, close }: {
162
+ export declare function AgentsPanel({ live, load, readTranscript, attach, close }: {
186
163
  /** Live feed rows (child sessions observed this process). */
187
164
  live: readonly SubagentRow[];
188
165
  /** Load this session's persisted child sessions by lineage. */
189
166
  load: () => Promise<readonly SessionRow[]>;
190
167
  /** Read one child session's full transcript as markdown. */
191
168
  readTranscript: (id: string, signal?: AbortSignal) => Promise<string>;
169
+ /** Attach to one child as the whole view (the runner's live buses). */
170
+ attach: ((id: string, label: string) => void) | undefined;
192
171
  close: () => void;
193
172
  }): ReactElement;
194
173
  /**
@@ -4,7 +4,7 @@
4
4
  * repainting the whole screen so Static-region text switches too).
5
5
  */
6
6
  import { type ReactElement } from 'react';
7
- import { type LanguageName } from './i18n.ts';
7
+ import { type LanguageName } from '../i18n.ts';
8
8
  export declare function LanguagePanel({ current, select, close }: {
9
9
  current: LanguageName;
10
10
  select: (name: LanguageName) => void;
@@ -0,0 +1,86 @@
1
+ /** Model selection and provider-management panels for the terminal app. */
2
+ import { type ReactElement } from 'react';
3
+ import type { ModelDirectory, ModelRow } from '../models.ts';
4
+ import { type DiscoveredModelView, type ProviderConfiguration, type ProviderSettingsDirectory, type ProviderTargetView } from '../provider-settings.ts';
5
+ import { type ProviderAuthorizationDirectory, type ProviderAuthorizationRow } from '../authorization.ts';
6
+ export declare function ModelPanel({ directory, error, current, onSelect, onProviders, onRetry, onClose }: {
7
+ directory: ModelDirectory | undefined;
8
+ error: string | undefined;
9
+ /** `provider/model` label of the applied model: the cursor lands on it once. */
10
+ current?: string;
11
+ onSelect: (row: ModelRow) => void;
12
+ onProviders?: () => void;
13
+ onRetry: () => void;
14
+ onClose: () => void;
15
+ }): ReactElement;
16
+ /** The provider-management stage reached from /model with `a`. */
17
+ export declare function ProviderPanel({ directory, error, authorizations, authorizationError, onConfigure, onUnset, onRemove, onRetry, onBack, onExit }: {
18
+ directory: ProviderSettingsDirectory | undefined;
19
+ error: string | undefined;
20
+ authorizations: ProviderAuthorizationDirectory | undefined;
21
+ authorizationError: string | undefined;
22
+ onConfigure: (target: ProviderTargetView) => void;
23
+ onUnset: (target: ProviderTargetView) => void;
24
+ onRemove: (target: ProviderTargetView) => void;
25
+ onRetry: () => void;
26
+ onBack: () => void;
27
+ /** Leave the whole /model flow (Ctrl+C), not just this stage. */
28
+ onExit: () => void;
29
+ }): ReactElement;
30
+ /** Provider configuration editor: only explicit models are written to settings. */
31
+ /**
32
+ * The unified provider setup page: API key, endpoint, and the explicit model
33
+ * list (with per-model context/output capacities) on ONE screen — the deep
34
+ * Tab menu and the separate key panel merged into a single discoverable
35
+ * surface. A saved key rides the same Enter as the endpoint and models; an
36
+ * empty endpoint keeps the provider's official default. Tab moves to the
37
+ * discovery page, which interrogates the real endpoint and returns checkable
38
+ * models for adoption; the last row also accepts hand-typed model ids.
39
+ */
40
+ /** One declarable donor the setup page can copy reasoning efforts from verbatim. */
41
+ export interface EffortDonor {
42
+ /** Provider route the declaration lives on. */
43
+ readonly provider: string;
44
+ /** Model id the declaration belongs to. */
45
+ readonly id: string;
46
+ /** The stored display-level to wire-value map, copied verbatim. */
47
+ readonly efforts: Record<string, string | null>;
48
+ }
49
+ export declare function ProviderSetupPanel({ target, authorization, onSubscribe, save, saveCredential, discover, effortDonors, done, back, onExit }: {
50
+ target: ProviderTargetView;
51
+ /**
52
+ * The provider's sign-in flow, when it offers a web (OAuth) channel: its
53
+ * presence adds the subscription/key mode layer at the top of the page.
54
+ */
55
+ authorization: ProviderAuthorizationRow | undefined;
56
+ /** Open the subscription channel: sign in when logged out, confirm sign-out when logged in. */
57
+ onSubscribe: (() => void) | undefined;
58
+ /** Models with declared efforts (settings first, catalog-advertised after) a model row can copy from. */
59
+ effortDonors: readonly EffortDonor[];
60
+ save: (target: ProviderTargetView, configuration: ProviderConfiguration) => Promise<void>;
61
+ saveCredential: ((target: ProviderTargetView, key: string) => Promise<void>) | undefined;
62
+ discover: (target: ProviderTargetView, request: {
63
+ readonly apiKey?: string;
64
+ readonly baseURL?: string;
65
+ }, signal?: AbortSignal) => Promise<readonly DiscoveredModelView[]>;
66
+ /** Report a successful save so the surface can notice the key rotation. */
67
+ done: (result: {
68
+ readonly key: boolean;
69
+ }) => void;
70
+ back: () => void;
71
+ /** Leave the whole /model flow (Ctrl+C), not just this page. */
72
+ onExit: () => void;
73
+ }): ReactElement;
74
+ /** Bounded destructive-action confirmation for credential or provider removal. */
75
+ export declare function ProviderConfirmPanel({ target, kind, confirm, done, back }: {
76
+ target: ProviderTargetView;
77
+ kind: 'credential' | 'provider';
78
+ confirm: (target: ProviderTargetView) => Promise<void>;
79
+ done: () => void;
80
+ back: () => void;
81
+ }): ReactElement;
82
+ /**
83
+ * The /help overlay: one scrolling card with the keyboard map, the TUI-local
84
+ * commands, the live registry commands, and the user-invocable skills — the
85
+ * real command surface, replacing the one-line notice.
86
+ */
@@ -7,7 +7,7 @@
7
7
  * @module @deepseek-ai/dsh-tui/theme-panel
8
8
  */
9
9
  import { type ReactElement } from 'react';
10
- import { type ThemeName } from './theme.ts';
10
+ import { type ThemeName } from '../theme.ts';
11
11
  /**
12
12
  * The /theme list: one row per theme, the current one marked with ●, the
13
13
  * focused one with ›. Enter applies the focused theme (the runner persists
@@ -9,7 +9,7 @@
9
9
  * here.
10
10
  */
11
11
  import { type ReactElement } from 'react';
12
- import type { LauncherUpdatePlan, LauncherUpdateStatus } from './update.ts';
12
+ import type { LauncherUpdatePlan, LauncherUpdateStatus } from '../update.ts';
13
13
  /** Retained apply-progress lines (ring tail; npm output is ephemeral). */
14
14
  export declare const UPDATE_OUTPUT_CAP = 800;
15
15
  /** Keep the newest UPDATE_OUTPUT_CAP lines of streamed update output. */
@@ -197,6 +197,17 @@ export declare function loadProviderSettings(ctx: Context): Promise<ProviderSett
197
197
  export declare function saveProviderCredential(ctx: Context, target: ProviderTargetView, rawKey: string): Promise<void>;
198
198
  /** Save the endpoint and an explicit model allow-list without rebuilding the profile. */
199
199
  export declare function saveProviderConfiguration(ctx: Context, target: ProviderTargetView, configuration: ProviderConfiguration): Promise<void>;
200
+ /**
201
+ * Switch one provider route to its subscription (plan sign-in) channel: the
202
+ * profile keeps neither a key reference nor an explicit model list, so the
203
+ * official catalog endpoint serves requests and the stored OAuth record — not
204
+ * a settings-named key override — authenticates them. Enabling a route that
205
+ * has no profile yet creates the empty profile that registers it.
206
+ * @param ctx - context carrying the `settings` service.
207
+ * @param target - provider row whose settings namespace owns the profile.
208
+ * @throws when the route is unmanaged or settings are read-only.
209
+ */
210
+ export declare function enableProviderSubscription(ctx: Context, target: ProviderTargetView): Promise<void>;
200
211
  /**
201
212
  * Interrogate a provider endpoint for the models it really serves, through
202
213
  * the model-discovery capability the provider's settings namespace
@@ -1,4 +1,12 @@
1
1
  /** Pure viewport, selection, and scrolling rules for exclusive TUI panels. */
2
+ import type { TranscriptEntry } from './projection.ts';
3
+ /**
4
+ * Remove assistant settlements that contain reasoning but no final response.
5
+ * They remain in the durable transcript and in the Ctrl/Alt+R fold; excluding
6
+ * them only from Ctrl+O navigation prevents short progress thoughts from
7
+ * appearing as a long run of misleading `reply` entries.
8
+ */
9
+ export declare function inspectableTranscriptEntries(entries: readonly TranscriptEntry[]): readonly TranscriptEntry[];
2
10
  /** Terminal-space allocation for the inspector's one dynamic screen. */
3
11
  export interface InspectorViewport {
4
12
  /** Maximum dynamic rows, kept strictly below the terminal height. */
@@ -48,6 +56,14 @@ export declare function liveRegionBudget(chrome: LiveRegionChrome): number;
48
56
  * accumulated `<Static>` output on every frame.
49
57
  */
50
58
  export declare function panelViewport(columns: number, rows: number): InspectorViewport;
59
+ /**
60
+ * Viewport for an expanded (near-fullscreen) document overlay: the document
61
+ * replaces the transcript area entirely, so the half-screen overlay cap and
62
+ * the inter-section gaps drop away while the bottom live region (composer,
63
+ * status) and the Static-rewrite margin stay reserved. The result stays
64
+ * below terminal height, which is what keeps Ink off its full-rewrite path.
65
+ */
66
+ export declare function expandedDocumentViewport(columns: number, rows: number): InspectorViewport;
51
67
  /** Backward-compatible name for the Ctrl+O-specific caller and tests. */
52
68
  export declare function inspectorViewport(columns: number, rows: number): InspectorViewport;
53
69
  /** Clamp a first-visible row to the range representable by one viewport. */
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Explicit compatibility policy for durable session events entering the TUI.
3
+ *
4
+ * The Harness event map is merge-extensible, so a reducer default branch is
5
+ * unavoidable: deployments may mount plugins this bundle does not know. The
6
+ * default must not, however, let a NEW first-party event silently bypass
7
+ * review. Tests compare this policy with `KNOWN_SESSION_EVENT_TYPES`; every
8
+ * kernel event must be deliberately classified before a Harness-line bump.
9
+ *
10
+ * @module @deepseek-ai/dsh-tui/render/projection-events
11
+ */
12
+ /** How one durable event relates to the terminal transcript projection. */
13
+ export type SessionEventDisposition = 'transcript' | 'other-surface' | 'ignored' | 'unknown';
14
+ /** Events folded directly into `TranscriptView`. */
15
+ export declare const TRANSCRIPT_SESSION_EVENT_TYPES: readonly ["agent/inbox/spliced", "assistant/attempt", "assistant/message", "command/done", "command/run", "compaction/end", "compaction/prune", "compaction/summary", "goal/change", "llm/retry", "llm/retry-started", "permission/preset", "plan/mode", "request/context", "request/header", "sandbox/mode", "schedule/change", "session/title", "step/start", "system/message", "todo/write", "tool-workflow/agent-end", "tool-workflow/agent-start", "tool-workflow/run-end", "tool-workflow/run-start", "tool/call", "tool/ptc-dispatch", "tool/ptc-dispatch-start", "tool/result", "turn/end", "turn/start", "user/message"];
16
+ /**
17
+ * Events consumed by another TUI adapter rather than the transcript fold.
18
+ * Keeping these separate documents that they are visible behavior, not
19
+ * accidental no-ops.
20
+ */
21
+ export declare const OTHER_SURFACE_SESSION_EVENT_TYPES: readonly ["agent-preset/selected", "model/selection", "subagent/catalog"];
22
+ /**
23
+ * Kernel events deliberately omitted from terminal presentation. Their
24
+ * model-visible consequences arrive through projected message/tool/session
25
+ * events, while audit-only facts do not need a second transcript row.
26
+ */
27
+ export declare const IGNORED_SESSION_EVENT_TYPES: readonly ["approval/asked", "approval/decided", "approval/policy", "compaction/start", "deliverables/presented", "feedback/message-delete", "feedback/message-put", "feedback/record", "hook/invoked", "hook/result", "session-log-deepseek/delivery-accepted", "session/end-seed", "session/title-llm-request", "step/end", "subagent/descriptor", "subagent/model-selection-policy", "team/member", "team/message/delivered", "team/message/queued", "team/task", "web/deepseek-search-llm-request"];
28
+ /**
29
+ * Classify a durable event without rejecting merge-extensible plugin events.
30
+ * `unknown` remains a runtime no-op for forward/plugin compatibility; the
31
+ * known-event coverage test is what makes a first-party addition fail CI.
32
+ */
33
+ export declare function sessionEventDisposition(type: string): SessionEventDisposition;
@@ -11,6 +11,8 @@
11
11
  *
12
12
  * @module @deepseek-ai/dsh-code/render/text
13
13
  */
14
+ import { stringWidth } from './width.ts';
15
+ export { stringWidth };
14
16
  /**
15
17
  * Compact token count: exact below 1K, then one-decimal-ish K/M.
16
18
  * @param n - token count.
@@ -35,6 +37,8 @@ export declare function singleLineText(text: string): string;
35
37
  * combining sequences never split mid-cluster.
36
38
  */
37
39
  export declare function truncateColumns(text: string, columns: number): string;
40
+ /** Sanitize, clip, and right-pad one cell to an exact terminal-column width. */
41
+ export declare function padColumns(text: string, columns: number): string;
38
42
  /** A display-safe suffix bounded by terminal rows and columns. */
39
43
  export interface DisplayTail {
40
44
  /** Sanitized suffix suitable for direct terminal rendering. */
@@ -0,0 +1,32 @@
1
+ /** Physical-row viewport state for native scrollback plus a bottom-anchored tail. */
2
+ export interface TranscriptViewportCursor {
3
+ readonly sessionKey: string;
4
+ readonly epoch: number;
5
+ readonly flushedRows: number;
6
+ }
7
+ export interface TranscriptViewportInput {
8
+ readonly sessionKey: string;
9
+ readonly epoch: number;
10
+ /** Physical durable transcript rows in the current rendered-history window. */
11
+ readonly totalRows: number;
12
+ /** Maximum durable rows retained outside Static for the live viewport. */
13
+ readonly retainedRows: number;
14
+ }
15
+ export interface TranscriptViewportStep {
16
+ readonly cursor: TranscriptViewportCursor;
17
+ /** Durable physical rows emitted through Static. */
18
+ readonly staticRows: number;
19
+ /** Durable physical rows still available to the live viewport. */
20
+ readonly liveRows: number;
21
+ }
22
+ /**
23
+ * Advance the monotonic physical-row flush cursor.
24
+ *
25
+ * Ordinary renders never pull a row back out of native scrollback. A source-
26
+ * backed replay (epoch change) flushes the complete durable window exactly
27
+ * once after the caller clears/remounts Static; subsequent durable rows start
28
+ * a fresh retained tail. Session/reset shrink starts from the new source.
29
+ */
30
+ export declare function advanceTranscriptViewport(previous: TranscriptViewportCursor, input: TranscriptViewportInput): TranscriptViewportStep;
31
+ /** Real history rows that fit beside the currently painted stream/tool rows. */
32
+ export declare function visibleTranscriptRows(historyRows: number, occupiedRows: number, capacity: number): number;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Load-time DeepSeek Harness version gate.
3
+ *
4
+ * The terminal driver is validated against exactly one Harness snapshot, and
5
+ * the host resolves every bare `@deepseek-ai/*` import the plugin makes
6
+ * against ITS OWN installed copies (the profile's `HostResolvedRootInclude`
7
+ * redirects bare names to the installed-host base). No install-time or
8
+ * load-time check exists anywhere on that path, so a user who updates `dsh`
9
+ * would otherwise run this build against unvetted upstream code silently.
10
+ *
11
+ * This gate mirrors how the host itself reads its identity: the running CLI
12
+ * entry (`process.argv[1]`) sits inside the `@deepseek-ai/dsh` package, whose
13
+ * manifest version is released in lockstep with the Harness packages bundled
14
+ * beside it. When the probe can identify the host it compares the running
15
+ * versions against the one this build declares and fails fast with an
16
+ * actionable message; when it cannot identify the host it stays silent rather
17
+ * than brick a supported embedding on a false positive.
18
+ *
19
+ * @module @deepseek-ai/dsh-code/runner/harness-gate
20
+ */
21
+ /** The one Harness release this build was validated against. */
22
+ export declare const EXPECTED_HARNESS_VERSION = "0.1.5-rc.2";
23
+ /** Host packages whose bundled copy the plugin binds to at runtime. */
24
+ export declare const HARNESS_GATE_PACKAGES: readonly ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-session"];
25
+ /** Filesystem slice the probe needs; injectable so tests stay pure. */
26
+ export interface HarnessFs {
27
+ /** Resolve a symlinked entry to its real path. */
28
+ realpathSync(path: string): string;
29
+ /** Read and parse one package.json; undefined when absent or unreadable. */
30
+ readManifest(path: string): {
31
+ name?: unknown;
32
+ version?: unknown;
33
+ } | undefined;
34
+ }
35
+ /** What the probe learned about the running host. */
36
+ export interface HarnessProbe {
37
+ /** Installed-host package root, when identified. */
38
+ readonly hostRoot?: string;
39
+ /** Installed-host CLI version (`@deepseek-ai/dsh`). */
40
+ readonly hostVersion?: string;
41
+ /** Version of each gate package bundled beside the host, when present. */
42
+ readonly packages: Readonly<Record<string, string | undefined>>;
43
+ }
44
+ /**
45
+ * Identify the running host and the Harness versions it provides.
46
+ * @param argv1 - `process.argv[1]`, the running CLI entry path.
47
+ * @param fs - filesystem slice; defaults to the real one.
48
+ * @returns the probe result; `packages` is empty when the host is unknown.
49
+ */
50
+ export declare function probeRunningHarness(argv1: string | undefined, fs?: HarnessFs): HarnessProbe;
51
+ /** One host fact that disagrees with the version this build declares. */
52
+ export interface HarnessMismatch {
53
+ /** What was compared: the host CLI or one bundled package. */
54
+ readonly source: string;
55
+ /** The version this build was validated against. */
56
+ readonly expected: string;
57
+ /** The version the running host actually provides. */
58
+ readonly provided: string;
59
+ }
60
+ /**
61
+ * Compare one probe against the expected snapshot. Unknown facts never
62
+ * mismatch: the gate must stay silent when it cannot judge, not brick a
63
+ * supported embedding on missing evidence.
64
+ * @param expected - the Harness version this build declares.
65
+ * @param probe - the running host's identified versions.
66
+ * @returns every concrete disagreement, empty when compatible or unknown.
67
+ */
68
+ export declare function harnessVersionMismatches(expected: string, probe: HarnessProbe): readonly HarnessMismatch[];
69
+ /**
70
+ * Render the gate failure the user sees on stderr.
71
+ * @param expected - the Harness version this build declares.
72
+ * @param mismatches - the disagreements to report.
73
+ * @param hostRoot - installed-host root, when identified, for the fix hint.
74
+ * @returns the multi-line diagnostic.
75
+ */
76
+ export declare function harnessGateMessage(expected: string, mismatches: readonly HarnessMismatch[], hostRoot?: string): string;
77
+ /**
78
+ * Refuse to run against an identified-but-incompatible host.
79
+ * @param expected - the Harness version this build declares.
80
+ * @param probe - the running host's identified versions.
81
+ * @throws an Error carrying the gate diagnostic when a version disagrees.
82
+ */
83
+ export declare function requireHarnessVersion(expected: string, probe: HarnessProbe): void;
@@ -0,0 +1,31 @@
1
+ /** Global input recall: the appended prompts and their durable JSONL file.
2
+ *
3
+ * One JSONL file under the DSH home. A missing file means an empty history and
4
+ * unreadable or corrupt content degrades to the valid lines it could parse,
5
+ * silently — recall is a convenience surface, never a gate.
6
+ *
7
+ * @module @deepseek-ai/dsh-code/input-history
8
+ */
9
+ /** Live recall state plus its serialized durable writes. */
10
+ export interface InputHistoryStore {
11
+ /** The recall list, newest last; read live by the composer. */
12
+ readonly entries: () => readonly string[];
13
+ /** Append one submitted line and persist it; '' is ignored. */
14
+ readonly record: (text: string) => void;
15
+ /** Await the pending writes (the quit flush). */
16
+ readonly flush: () => Promise<void>;
17
+ }
18
+ /**
19
+ * Load the recall history and return its live store.
20
+ *
21
+ * Serialized history writes: each submission appends one JSON line at the end
22
+ * of the file, so concurrent terminals add entries after each other instead of
23
+ * overwriting snapshots they read at their own boot. A multi-line draft still
24
+ * occupies one physical line (JSON escapes the newline), and a regular-length
25
+ * line reaches the disk as one positioned write; an oversized paste may
26
+ * interleave mid-line, which the next parse simply drops.
27
+ *
28
+ * @param path - absolute path of the JSONL recall file.
29
+ * @param onFailure - receives the write failure message for a bounded notice.
30
+ */
31
+ export declare function createInputHistory(path: string, onFailure: (message: string) => void): InputHistoryStore;