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
@@ -51,13 +51,50 @@ export declare const en: {
51
51
  readonly 'search.compact.searching': "searching…";
52
52
  readonly 'search.compact.error': "error: {message}";
53
53
  readonly 'search.footer': "↑↓ move · enter search/resume · esc close";
54
+ readonly 'header.hint': "/help · Esc interrupt · Ctrl+C quit";
55
+ readonly 'header.hintResumed': "resumed · /help · Esc interrupt";
54
56
  readonly 'statusline.title': "/statusline · items apply to the live status line below";
57
+ readonly 'statusline.item.model': "model";
58
+ readonly 'statusline.item.model.desc': "provider/model serving this session";
59
+ readonly 'statusline.item.cwd': "cwd";
60
+ readonly 'statusline.item.cwd.desc': "working-directory basename";
61
+ readonly 'statusline.item.mode': "mode";
62
+ readonly 'statusline.item.mode.desc': "agent preset composing the session";
63
+ readonly 'statusline.item.branch': "branch";
64
+ readonly 'statusline.item.branch.desc': "git branch inside a repository";
65
+ readonly 'statusline.item.context': "context";
66
+ readonly 'statusline.item.context.desc': "context-window occupancy meter";
67
+ readonly 'statusline.item.permission': "permission";
68
+ readonly 'statusline.item.permission.desc': "permission preset badge with cycle hint";
69
+ readonly 'statusline.item.plan': "plan";
70
+ readonly 'statusline.item.plan.desc': "plan-mode state mark";
71
+ readonly 'statusline.item.turns': "turns";
72
+ readonly 'statusline.item.turns.desc': "turn and step counters";
73
+ readonly 'statusline.item.durations': "durations";
74
+ readonly 'statusline.item.durations.desc': "llm/ttft/decode/tool wall time";
75
+ readonly 'statusline.item.cache': "cache";
76
+ readonly 'statusline.item.cache.desc': "cache-hit share of billed input";
77
+ readonly 'statusline.item.tokens': "tokens";
78
+ readonly 'statusline.item.tokens.desc': "cumulative input/output tokens";
79
+ readonly 'statusline.item.title': "title";
80
+ readonly 'statusline.item.title.desc': "session title or short id";
81
+ readonly 'statusline.item.goal': "goal";
82
+ readonly 'statusline.item.goal.desc': "live goal phase and round progress";
83
+ readonly 'statusline.item.sandbox': "sandbox";
84
+ readonly 'statusline.item.sandbox.desc': "divergent sandbox-mode override";
55
85
  readonly 'schedule.title': "/schedule · {count} active reminder";
56
86
  readonly 'schedule.titlePlural': "/schedule · {count} active reminders";
57
87
  readonly 'schedule.empty': "no active reminders — the model creates them with schedule_create";
58
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";
59
93
  readonly 'document.compact': "transcript · esc close";
60
- 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";
61
98
  readonly 'review.picker.title': "/review · choose a target";
62
99
  readonly 'review.picker.uncommitted': "Review uncommitted changes (staged / unstaged / new files)";
63
100
  readonly 'review.picker.branch': "Compare against a base branch (lists local branches)";
@@ -197,10 +234,16 @@ export declare const en: {
197
234
  readonly 'status.label.out': "out";
198
235
  readonly 'status.label.mode': "/mode";
199
236
  readonly 'status.label.context': "context";
237
+ readonly 'status.label.sandbox': "sandbox";
238
+ readonly 'status.plan.on': "plan on";
239
+ readonly 'status.plan.mark': "⧉ plan";
240
+ readonly 'status.goal.round': "◎ round {current}/{max}";
241
+ readonly 'status.goal.phase': "◎ {phase}";
200
242
  readonly 'frozen.keysGoTo': "keys go to {owner} · esc {action}";
201
243
  readonly 'frozen.action.rejects': "rejects";
202
244
  readonly 'frozen.action.cancels': "cancels";
203
245
  readonly 'frozen.action.closes': "closes";
246
+ readonly 'frozen.action.waits': "waits";
204
247
  readonly 'time.justNow': "now";
205
248
  readonly 'time.minutesAgo': "{n}m ago";
206
249
  readonly 'time.hoursAgo': "{n}h ago";
@@ -212,6 +255,7 @@ export declare const en: {
212
255
  readonly 'notice.themeSaveFailed': "theme save failed: {message}";
213
256
  readonly 'notice.languageSaveFailed': "language save failed: {message}";
214
257
  readonly 'notice.alreadyActive': "that session is already active";
258
+ readonly 'notice.permissionPresetsUnmounted': "permission presets are not mounted in this composition";
215
259
  readonly 'notice.queueCancelled': "queued message cancelled";
216
260
  readonly 'notice.queueActionFailed': "queue action failed: {message}";
217
261
  readonly 'notice.queueUnavailable': "queued message is no longer pending";
@@ -226,6 +270,8 @@ export declare const en: {
226
270
  readonly 'notice.usage.animation': "usage: /animation [on|off]";
227
271
  readonly 'notice.usage.language': "usage: /language [en|zh]";
228
272
  readonly 'notice.usage.rainbow': "usage: /rainbow [seed]";
273
+ readonly 'notice.usage.bareCommand': "usage: /{name}";
274
+ readonly 'notice.switchInProgress': "switching sessions — send again once the new session is up, or /resume cancel";
229
275
  readonly 'notice.imageCancelled': "image submission cancelled";
230
276
  readonly 'notice.themeConfigUnreadable': "theme config unreadable, using dark: {message}";
231
277
  readonly 'notice.languageConfigUnreadable': "language config unreadable, using english: {message}";
@@ -256,6 +302,13 @@ export declare const en: {
256
302
  readonly 'notice.resumeFailed': "resume failed: {message}";
257
303
  readonly 'notice.forkFailed': "fork failed: {message}";
258
304
  readonly 'notice.sessionSwitchFailed': "session switch failed: {message}";
305
+ readonly 'notice.sessionCreated': "created {id} · mode {mode}";
306
+ readonly 'notice.sessionResumed': "resumed {id} · mode {mode}";
307
+ readonly 'notice.sessionSwitchedDirty': "switched to {id}, but {detail}";
308
+ readonly 'notice.flushFailed': "previous session flush failed: {message}";
309
+ readonly 'notice.agentReleaseFailed': "previous agent release failed: {message}";
310
+ readonly 'notice.copyEmpty': "nothing to copy yet";
311
+ readonly 'notice.copied': "copied latest response";
259
312
  readonly 'notice.switchQueued': "will switch to {label} when the current turn finishes · /resume cancel to abort";
260
313
  readonly 'notice.reviewStarted': "review started under read-only permissions";
261
314
  readonly 'notice.reviewFailed': "review failed: {message}";
@@ -307,7 +360,8 @@ export declare const en: {
307
360
  readonly 'panel.footer.chooseSelect': "↑↓ choose · enter select · r refresh · esc close";
308
361
  readonly 'panel.footer.inspectDetails': "↑↓ inspect · enter details · r refresh · esc close";
309
362
  readonly 'panel.footer.inspectRefresh': "↑↓ inspect · r refresh · esc close";
310
- readonly 'panel.footer.resume': "tab/←→ filters · ↑↓/pg navigate · ctrl+f search · e details · t transcript · d delete · enter resume";
363
+ readonly 'panel.footer.resume': "tab/←→ filters · ↑↓/pg navigate · ctrl+f search · e details · t transcript · enter resume";
364
+ readonly 'panel.footer.delete': "tab/←→ filters · ↑↓/pg navigate · ctrl+f search · e details · t transcript · enter delete · esc close";
311
365
  readonly 'panel.footer.transcript': "lines {from}-{to}/{total} · ↑↓/pg/g/G · t/esc close";
312
366
  readonly 'panel.footer.history': "↑↓ move · g/G ends · enter fill · esc close";
313
367
  readonly 'panel.footer.review': "↑↓ move · enter select · esc back · q close";
@@ -319,6 +373,7 @@ export declare const en: {
319
373
  readonly 'panel.jobs.title': "/jobs · background tasks · {count}";
320
374
  readonly 'panel.resume.deleteTitle': "permanently delete {target}? this cannot be undone · subagent threads go too";
321
375
  readonly 'panel.resume.title': "/resume{mode}{search} · {toolbar}";
376
+ readonly 'panel.delete.title': "/delete{search} · {toolbar}";
322
377
  readonly 'panel.noActiveReminders': "no active reminders — the model schedules via schedule_create";
323
378
  readonly 'panel.notMounted': "not mounted";
324
379
  readonly 'panel.broken': "broken: {message}";
@@ -369,7 +424,6 @@ export declare const en: {
369
424
  readonly 'panel.provider.notConfiguredDivider': "── not configured ──";
370
425
  readonly 'panel.provider.title': "/model — providers";
371
426
  readonly 'panel.provider.titleCount': "/model — providers · {index}/{total}";
372
- readonly 'panel.provider.state': "{route} · {value}";
373
427
  readonly 'panel.provider.key': "key {value}";
374
428
  readonly 'panel.provider.authConfigured': "provider auth";
375
429
  readonly 'panel.provider.noKey': "key missing";
@@ -384,7 +438,7 @@ export declare const en: {
384
438
  readonly 'panel.provider.readOnlyLogin': "this login record is read-only";
385
439
  readonly 'panel.provider.notManaged': "this provider is not managed by Harness settings";
386
440
  readonly 'panel.providers.compact': "/model providers · enter configure · d remove key · esc back";
387
- 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";
388
442
  readonly 'panel.update.compact': "/update · {summary}";
389
443
  readonly 'panel.update.checking': "checking…";
390
444
  readonly 'panel.update.probeFailed': "probe failed";
@@ -412,8 +466,14 @@ export declare const en: {
412
466
  readonly 'panel.setup.copyInto': "copy verbatim into {id}";
413
467
  readonly 'panel.setup.copyTitle': "/model — copy efforts";
414
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";
415
475
  readonly 'panel.setup.title': "/model — configure {provider}";
416
- 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";
417
477
  readonly 'panel.discovery.compact': "model discovery · terminal too small · esc back";
418
478
  readonly 'panel.discovery.loading': "discovering models…";
419
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[]>;
@@ -66,7 +60,7 @@ export declare function JobsPanel({ load, close }: {
66
60
  load: () => readonly JobRow[];
67
61
  close: () => void;
68
62
  }): ReactElement;
69
- export declare function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete, deleteConfirmId, reloadToken, deleteMode, close }: {
63
+ export declare function ResumePanel({ currentCwd, load, readTranscript, select, requestDelete, deleteConfirmId, reloadToken, deleteMode, presetId, close }: {
70
64
  currentCwd: string;
71
65
  load: (options: SessionDirectoryOptions, signal?: AbortSignal) => Promise<readonly SessionRow[]>;
72
66
  readTranscript: (id: string, signal?: AbortSignal) => Promise<string>;
@@ -79,6 +73,8 @@ export declare function ResumePanel({ currentCwd, load, readTranscript, select,
79
73
  reloadToken?: number;
80
74
  /** Opened via /delete: hint-first delete mode. */
81
75
  deleteMode?: boolean;
76
+ /** `/delete <id>` argument to resolve after the listing loads. */
77
+ presetId?: string;
82
78
  close: () => void;
83
79
  }): ReactElement;
84
80
  /**
@@ -108,23 +104,6 @@ export declare function ReviewPickerPanel({ loadBranches, loadCommits, choose, c
108
104
  choose: (selection: ReviewSelection) => void;
109
105
  close: () => void;
110
106
  }): ReactElement;
111
- /** One cross-session full-text search hit mapped from the session-query engine. */
112
- export interface SearchRow {
113
- /** Session id (Enter resumes it through the switch machinery). */
114
- readonly id: string;
115
- /** Display label: session title or the short id form. */
116
- readonly label: string;
117
- /** Secondary facts line (workspace · preset markers). */
118
- readonly detail: string;
119
- /** Bounded plain-text excerpt around the strongest match. */
120
- readonly snippet: string;
121
- /** Match timestamp (relative labels derive from it). */
122
- readonly updatedAt: number;
123
- /** Whether the hit is a delegated subagent conversation (not resumable). */
124
- readonly subagent: boolean;
125
- /** Whether Enter may switch into it. */
126
- readonly resumable: boolean;
127
- }
128
107
  /**
129
108
  * The /search panel: full-text search over every persisted session through
130
109
  * the in-process session-query engine (the same corpus the model's
@@ -180,13 +159,15 @@ export declare function EffortPanel({ row, current, select, back, onExit }: {
180
159
  * Enter/t opening the child's full transcript in the shared read-only
181
160
  * document view (the same projection the exporter uses).
182
161
  */
183
- export declare function AgentsPanel({ live, load, readTranscript, close }: {
162
+ export declare function AgentsPanel({ live, load, readTranscript, attach, close }: {
184
163
  /** Live feed rows (child sessions observed this process). */
185
164
  live: readonly SubagentRow[];
186
165
  /** Load this session's persisted child sessions by lineage. */
187
166
  load: () => Promise<readonly SessionRow[]>;
188
167
  /** Read one child session's full transcript as markdown. */
189
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;
190
171
  close: () => void;
191
172
  }): ReactElement;
192
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,11 +9,31 @@
9
9
  * here.
10
10
  */
11
11
  import { type ReactElement } from 'react';
12
- import type { 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. */
16
16
  export declare function clipUpdateLines(lines: readonly string[]): readonly string[];
17
+ /** One in-flight `update --apply`. Closing the panel must not start a second. */
18
+ interface UpdateApplyJob {
19
+ readonly promise: Promise<number>;
20
+ readonly lines: string[];
21
+ readonly lineListeners: Set<(line: string) => void>;
22
+ }
23
+ /** True while an apply child is alive, even if the /update panel is closed. */
24
+ export declare function isUpdateApplyRunning(): boolean;
25
+ /** Subscribe to apply-running changes (frozen-hint "esc waits"). */
26
+ export declare function subscribeUpdateApplyRunning(listener: (running: boolean) => void): () => void;
27
+ /** The live apply job, if any: lines already streamed plus the shared promise. */
28
+ export declare function currentUpdateApply(): UpdateApplyJob | undefined;
29
+ /**
30
+ * Run `apply` once. A second call while the child is alive reuses the same
31
+ * promise and replays buffered lines — closing /update and opening it again
32
+ * must not spawn a second `npm install`.
33
+ */
34
+ export declare function runUpdateApply(apply: (onLine: (line: string) => void, plan: LauncherUpdatePlan) => Promise<number>, plan: LauncherUpdatePlan, onLine?: (line: string) => void): Promise<number>;
35
+ /** Drop a leftover job between tests. */
36
+ export declare function resetUpdateApply(): void;
17
37
  /** One display row of the update surface. */
18
38
  export interface UpdateRow {
19
39
  readonly key: string;
@@ -47,3 +67,4 @@ export declare function UpdatePanel({ probe, apply, close, notify }: {
47
67
  /** One bounded cross-surface notice (phase completions). */
48
68
  notify: (text: string, tone?: 'info' | 'warning' | 'error') => void;
49
69
  }): ReactElement;
70
+ export {};
@@ -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
@@ -48,6 +48,14 @@ export declare function liveRegionBudget(chrome: LiveRegionChrome): number;
48
48
  * accumulated `<Static>` output on every frame.
49
49
  */
50
50
  export declare function panelViewport(columns: number, rows: number): InspectorViewport;
51
+ /**
52
+ * Viewport for an expanded (near-fullscreen) document overlay: the document
53
+ * replaces the transcript area entirely, so the half-screen overlay cap and
54
+ * the inter-section gaps drop away while the bottom live region (composer,
55
+ * status) and the Static-rewrite margin stay reserved. The result stays
56
+ * below terminal height, which is what keeps Ink off its full-rewrite path.
57
+ */
58
+ export declare function expandedDocumentViewport(columns: number, rows: number): InspectorViewport;
51
59
  /** Backward-compatible name for the Ctrl+O-specific caller and tests. */
52
60
  export declare function inspectorViewport(columns: number, rows: number): InspectorViewport;
53
61
  /** Clamp a first-visible row to the range representable by one viewport. */
@@ -119,6 +119,8 @@ export interface StatusItemInfo {
119
119
  }
120
120
  /** The full item catalog in canonical order (the /statusline default). */
121
121
  export declare const STATUS_ITEMS: readonly StatusItemInfo[];
122
+ /** Picker rows with labels/descriptions in the active interface language. */
123
+ export declare function localizedStatusItems(): readonly StatusItemInfo[];
122
124
  /**
123
125
  * Default order: the whole catalog (matches the pre-customization bar).
124
126
  * The busy dot is not an item — it always leads the identity cluster.
@@ -178,7 +180,8 @@ export declare function permissionTone(permission: string): StatusTone;
178
180
  /**
179
181
  * Compose the two-row footer layout under a column budget. Row 1 keeps model,
180
182
  * cwd, mode, branch, context, then the right-pinned permission badge and cycle
181
- * hint. It drops hint, context, and permission before ellipsizing identity.
183
+ * hint. It shrinks the context bar, drops the hint, and peels trailing
184
+ * identity facts before removing the context group or the permission badge.
182
185
  * Row 2 fits all secondary figures and state within its own budget.
183
186
  * @param facts - identity facts resolved by the runner.
184
187
  * @param stats - session figures folded from the durable log.
@@ -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,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;