dsh-ssh-tui 0.6.1 → 0.6.3

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.
@@ -59,9 +59,6 @@ export declare const LOCAL_COMMANDS: readonly [{
59
59
  }, {
60
60
  readonly name: "subagents";
61
61
  readonly key: "cmd.subagents";
62
- }, {
63
- readonly name: "resume";
64
- readonly key: "cmd.resume";
65
62
  }, {
66
63
  readonly name: "setup";
67
64
  readonly key: "cmd.setup";
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Agent-preset display names, and the profile row the terminal `/mode` needs.
3
+ *
4
+ * A shipped preset's name resolves through the id-keyed dictionary this module
5
+ * owns — the same rule `@deepseek-ai/dsh-agent-presets/display` applies on the
6
+ * browser surface — so `/language` reaches the picker, the banner, and
7
+ * `/status`. A user-authored preset keeps the name its own `preset.yml`
8
+ * published: that metadata is never translated.
9
+ *
10
+ * A terminal profile built on `dsh-base` composes no preset roster, and this
11
+ * plugin's own bundle patch may not mount one (DSH STORE accepts additive
12
+ * plugin-owned rows only), so the profile's user layer owns the row. When it is
13
+ * absent `/mode` reports the exact repair instead of a dead end.
14
+ * @module dsh-ssh-tui/preset-label
15
+ */
16
+ /** Whether this id belongs to the shipped, translatable roster. */
17
+ export declare function isShippedPreset(id: string): boolean;
18
+ /**
19
+ * Resolve the name a picker, banner, or report renders for one preset.
20
+ *
21
+ * A published name equal to the id is the roster's own "no metadata" answer,
22
+ * not a name, so it falls through to the dictionary or back to the id.
23
+ * @param id - the preset id, also the last-resort label.
24
+ * @param name - display name the preset published, when it published one.
25
+ * @param trust - `user` keeps published metadata; anything else (including an
26
+ * unknown trust) resolves a shipped id through the dictionary.
27
+ * @returns the localized or published name, never empty.
28
+ */
29
+ export declare function presetLabel(id: string, name?: string, trust?: string): string;
30
+ /**
31
+ * The profile this process booted, from `--profile <name>` / `--profile=<name>`.
32
+ *
33
+ * The launcher passes the same arguments to the front end and to the Host it
34
+ * spawns (only `--resume`/`--new` are rewritten), so both report one profile.
35
+ * @param argv - the argument list to read.
36
+ * @returns the profile name, or the TUI's own default when none was named.
37
+ */
38
+ export declare function profileFromArgv(argv?: readonly string[]): string;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The profile patch that mounts the agent-preset roster `/mode` needs.
3
+ *
4
+ * A terminal profile built on `dsh-base` composes no preset roster (only the
5
+ * Web bundle does), and this plugin's own bundle patch may not mount one: DSH
6
+ * STORE accepts additive rows with plugin-owned ids and no `@deepseek-ai/*`
7
+ * module names. The profile's user layer is the supported home for the row, so
8
+ * both the install script and the running TUI write the same block here — the
9
+ * TUI needs it because `dsh plugin add dsh-ssh-tui@latest` (the in-app update
10
+ * path) never runs `scripts/`, which npm installs do not ship.
11
+ *
12
+ * The roster is not cosmetic: without it `/mode` cannot switch, and the tools
13
+ * the shipped presets own (`ask_user_question`, `present`, PTC's presentation)
14
+ * are absent from the agent's catalog.
15
+ * @module dsh-ssh-tui/preset-rows
16
+ */
17
+ /**
18
+ * The exact profile patch block that mounts the roster and the two host
19
+ * services the shipped presets need. `scripts/ensure-profile-rows.sh` carries
20
+ * the same text; a test compares the two so they cannot drift.
21
+ */
22
+ export declare const ROSTER_PATCH_BLOCK = "# dsh-ssh-tui /mode: the agent-preset roster (standard / minimal / PTC /\n# cordis, plus every preset under $DSH_HOME/.agent-presets) and the two host\n# services the shipped presets need. dsh-base composes no roster in a terminal\n# profile, and a third-party bundle patch may not mount an @deepseek-ai row, so\n# the profile's user layer owns them.\n- insert:\n - id: agent-presets\n name: '@deepseek-ai/dsh-agent-presets'\n config:\n default: standard\n\n - id: code-runtime\n name: '@deepseek-ai/dsh-code-runtime-worker-thread'\n\n - id: subagent-model-selection-settings\n name: '@deepseek-ai/dsh-tool-subagent/model-selection-settings'\n";
23
+ /** The profile patch file the roster block belongs in. */
24
+ export declare function rosterPatchPath(home: string, profile: string): string;
25
+ /**
26
+ * The patch text with the roster block appended, or `undefined` when the file
27
+ * already names the roster row.
28
+ *
29
+ * A file that is exactly `[]` (the profile template) is replaced, so the result
30
+ * stays a valid top-level patch list; anything else keeps its content and the
31
+ * block is appended after a blank line.
32
+ * @param existing - the patch file's current text.
33
+ * @returns the new text, or `undefined` when nothing has to change.
34
+ */
35
+ export declare function rosterPatchText(existing: string): string | undefined;
36
+ /**
37
+ * Mount the roster in one profile, unless it is already composed.
38
+ *
39
+ * Idempotent: a profile whose patch already names the roster row (one bundling
40
+ * `@deepseek-ai/dsh-web-app`, for example) is left untouched. The write is a
41
+ * plain whole-file replace because the file is small and read once at boot; a
42
+ * half-written patch would only be seen by the next launch.
43
+ * @param home - the harness home carrying `profiles/`.
44
+ * @param profile - the profile to patch.
45
+ * @returns `present` when the row already exists, else `written`.
46
+ */
47
+ export declare function ensureRosterRows(home: string, profile: string): Promise<'present' | 'written'>;
@@ -2,11 +2,16 @@
2
2
  * Shared reasoning-effort defaults for the SSH TUI.
3
3
  *
4
4
  * OpenCode / third-party (llm-pi-ai) routes carry no adapter-level reasoning
5
- * default (unlike `llm-deepseek`, whose row declares `reasoningEffort: max`).
6
- * Without an explicit effort the model streams its thinking as plain `text`
7
- * chunks instead of `reasoning` blocks, so the TUI never receives the data
8
- * needed to render the collapsible `思考中` block. This helper picks a
5
+ * default. Without an explicit effort the model streams its thinking as plain
6
+ * `text` chunks instead of `reasoning` blocks, so the TUI never receives the
7
+ * data needed to render the collapsible `思考中` block. This helper picks a
9
8
  * supported default so the fold has data to show.
9
+ *
10
+ * `llm-deepseek` is not covered here: its adapter always reports a
11
+ * `defaultEffort`, which the LLM runtime materializes into the request on its
12
+ * own. Since the 0.5.1 STORE compliance trim dropped the bundle patch's
13
+ * `reasoningEffort: max` row override, that adapter default is `high` unless a
14
+ * deployment sets it (or `/effort` picks one, remembered per route).
10
15
  */
11
16
  import { type LlmRuntime, type ReasoningEffortId } from '@deepseek-ai/dsh-llm';
12
17
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Shared history-session listing for the SSH TUI: the launch picker and the
3
- * in-app `/resume` command use the same candidates, labels, and ordering, so
4
- * both surfaces offer the same sessions.
2
+ * Shared history-session listing for the SSH TUI: the launch picker's lazy
3
+ * pages and the eager `listResumableSessions` listing use the same candidates,
4
+ * labels, and ordering, so both surfaces offer the same sessions.
5
5
  */
6
6
  import { listAttachableHosts } from './session-lock.js';
7
7
  /** Last path segment for the footer chip (`\root\genshin\srv` → `srv`). */
@@ -71,7 +71,7 @@ export interface ResumableSessionPage {
71
71
  export interface ResumableSessionPager {
72
72
  /** Inspect onward until `size` more rows exist, or history runs out. */
73
73
  page(size?: number): Promise<ResumableSessionPage>;
74
- /** Read every remaining candidate (the `/resume` flow wants the whole list). */
74
+ /** Read every remaining candidate (the eager listing wants the whole list). */
75
75
  complete(): Promise<ResumableSession[]>;
76
76
  }
77
77
  /** Sessions read before the picker's first paint, and per lazy page after it. */
@@ -77,8 +77,8 @@ export declare function windowsProcessMatchesLock(lock: SessionLockInfo, identit
77
77
  * behavior is kept.
78
78
  *
79
79
  * Async because the Windows probe spawns PowerShell, and this runs on the
80
- * render path (`/resume` inspects every lock); a synchronous spawn would
81
- * freeze painting and keystrokes for the duration of the probe.
80
+ * history-listing path (the picker inspects every lock); a synchronous spawn
81
+ * would freeze painting and keystrokes for the duration of the probe.
82
82
  */
83
83
  export declare function lockOwnerIsAlive(lock: SessionLockInfo): Promise<boolean>;
84
84
  export declare function formatLockHeldMessage(lock: SessionLockInfo): string;
@@ -63,12 +63,14 @@ export interface TuiConfig {
63
63
  presetId?: string;
64
64
  /** Display name of the active preset. */
65
65
  presetName?: string;
66
- /** Switch the running TUI to another session (used by /resume). */
67
- onSwitchSession?: (sessionId: string) => Promise<void> | void;
66
+ /**
67
+ * Trust of the root the active preset came from (`system`/`user`). A `user`
68
+ * preset keeps its own published name; a shipped one resolves through the
69
+ * locale dictionary.
70
+ */
71
+ presetTrust?: string;
68
72
  /** Notify the launcher of an explicit in-process selection change. */
69
73
  onSelectionChanged?: (selection: ModelSelection) => void;
70
- /** Open the history-session picker immediately after mounting (--resume). */
71
- resumePicker?: boolean;
72
74
  /**
73
75
  * Minimum milliseconds between paints while a turn is streaming.
74
76
  * Jump-host / proxied SSH can raise this so token ticks do not flood the
@@ -157,9 +159,7 @@ export declare class SshTui {
157
159
  private readonly providerName;
158
160
  private readonly selectionRef;
159
161
  private readonly subagentSelection;
160
- private readonly onSwitchSession;
161
162
  private readonly onSelectionChanged;
162
- private readonly resumePicker;
163
163
  private readonly disposers;
164
164
  private userQuestionDisposer;
165
165
  private presetId;
@@ -574,8 +574,14 @@ export declare class SshTui {
574
574
  private runDisconnectCommand;
575
575
  /** /mode: pick an agent preset (standard / minimal / ptc / cordis / routing-suite / ...). */
576
576
  private runModeCommand;
577
- /** /resume: switch to a past session, or open a picker when no id is given. */
578
- private runResumeCommand;
577
+ /**
578
+ * `/mode fix`: write the roster row into this profile's user patch layer.
579
+ *
580
+ * The write only takes effect on the next launch — the loader composes the
581
+ * patch tree once at boot — so the report says so instead of pretending the
582
+ * running session gained a roster.
583
+ */
584
+ private repairRoster;
579
585
  /** Current provider route selected for the running agent. */
580
586
  private currentProvider;
581
587
  /** Resolve one credential reference without exposing its value. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ssh-tui",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "SSH-friendly interactive terminal TUI plugin for DeepSeek Harness",
5
5
  "keywords": [
6
6
  "deepseek-harness",