dsh-ssh-tui 0.1.6 → 0.1.7

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.
package/README.en.md CHANGED
@@ -155,6 +155,12 @@ configured catalog when the endpoint cannot be reached. Picking a model that
155
155
  is not stored in the provider profile automatically adds it to
156
156
  `llm-pi-ai.providers.<id>.models` so the harness can serve it.
157
157
 
158
+ Subagents follow the parent session's provider by default and run on the
159
+ lightweight `deepseek-v4-flash` model. `/submodel [model-id]` picks (or
160
+ directly sets) the subagent model, and `/subeffort` picks the subagent
161
+ reasoning effort or restores the provider default. Both commands are
162
+ remembered under `ssh-tui-subagent` in `$DSH_HOME/settings.yaml`.
163
+
158
164
  `/usage` (alias `/quota`) works when the current provider is an OpenCode
159
165
  source and keeps the two billing models distinct:
160
166
 
package/README.md CHANGED
@@ -120,9 +120,14 @@ dsh --profile tui --no-color
120
120
  | `Ctrl+D` | 退出 |
121
121
  | `Ctrl+L` | 重绘 |
122
122
 
123
- 斜杠命令:`/help`、`/model`、`/mode`、`/resume`、`/status`、`/subagents`、
124
- `/usage`(`/quota` 同义)、`/setup`、`/clear`,以及 harness 自带命令
125
- (`/goal`、`/plan`、`/compact` 等)。
123
+ 斜杠命令:`/help`、`/model`、`/submodel`、`/subeffort`、`/mode`、`/resume`、
124
+ `/status`、`/subagents`、`/usage`(`/quota` 同义)、`/setup`、`/clear`,
125
+ 以及 harness 自带命令(`/goal`、`/plan`、`/compact` 等)。
126
+
127
+ 子代理默认跟随父会话的提供方,模型默认为轻量的 `deepseek-v4-flash`:
128
+
129
+ - `/submodel [model-id]`:打开子代理模型选择器;带参数时直接指定模型;
130
+ - `/subeffort`:选择子代理思考强度,或恢复为“跟随提供商默认”。
126
131
 
127
132
  `/usage` 在当前提供商为 OpenCode 源时可用,并区分两种计费方式:
128
133
 
@@ -142,9 +147,14 @@ agent-default-model:
142
147
  reasoningEffort: max
143
148
  agent-presets:
144
149
  default: standard
150
+ ssh-tui-subagent:
151
+ model: deepseek-v4-flash
152
+ # provider 可省略:省略时子代理跟随父会话提供方
153
+ # reasoningEffort 可省略:省略时跟随提供商/模型默认
145
154
  ```
146
155
 
147
- `/model` 与 `/mode` 的修改会写回这里,web 端与 TUI 共用同一份设置。
156
+ `/model`、`/mode`、`/submodel` 与 `/subeffort` 的修改会写回这里,
157
+ web 端与 TUI 共用同一份设置。
148
158
 
149
159
  对 OpenCode 和其他第三方提供商,`/model` 会先调用提供商的端点
150
160
  (`GET {baseURL}/models`)获取实时模型列表;端点不可达时回退到已配置的
package/cordis.patch.yml CHANGED
@@ -35,6 +35,25 @@
35
35
  thinking: enabled
36
36
  reasoningEffort: max
37
37
 
38
+ # Subagents follow the parent session's provider and default to the
39
+ # lightweight model. The TUI's /submodel and /subeffort commands can override
40
+ # both model and reasoning effort at runtime through the request waterfall.
41
+ - id: tool-subagent
42
+ config:
43
+ provider: spawn
44
+ toolName: subagent
45
+ backgroundMode: continuable
46
+ agentOptions:
47
+ model: deepseek-v4-flash
48
+
49
+ - id: tool-subagent-fork
50
+ config:
51
+ provider: fork
52
+ toolName: subagent_fork
53
+ backgroundMode: one-shot
54
+ agentOptions:
55
+ model: deepseek-v4-flash
56
+
38
57
  - insert:
39
58
  # The agent-mode roster (standard / PTC / minimal / cordis / routing-suite).
40
59
  # The launcher injects the shipped preset root into this row when it is
package/lib/index.js CHANGED
@@ -11,6 +11,7 @@ import { randomUUID } from 'node:crypto';
11
11
  import { showSessionPicker } from './picker.js';
12
12
  import { mountTui } from './tui.js';
13
13
  import { defaultReasoningEffort } from './reasoning.js';
14
+ import { createSubagentSelection } from './subagent-model.js';
14
15
  export const name = 'ssh-tui';
15
16
  /** Core services required before the terminal channel can drive an agent. */
16
17
  export const inject = ['agents', 'agentDefaultModel'];
@@ -24,6 +25,7 @@ export function apply(ctx, config) {
24
25
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
25
26
  throw new Error('dsh-ssh-tui: both stdin and stdout must be TTYs; use a terminal/SSH session');
26
27
  }
28
+ const subagentSelection = createSubagentSelection(ctx);
27
29
  ctx.effect(() => {
28
30
  let disposed = false;
29
31
  let switching = false;
@@ -131,6 +133,7 @@ export function apply(ctx, config) {
131
133
  provider,
132
134
  model,
133
135
  selectionRef,
136
+ subagentSelection,
134
137
  presetId,
135
138
  presetName,
136
139
  goodbye: goodbyeFor(String(sessionId)),
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Subagent model defaults and the settings section the TUI edits with
3
+ * `/submodel` and `/subeffort`.
4
+ *
5
+ * The provider is deliberately inherited from the running parent session:
6
+ * the subagent selection only overrides the provider when the user stored an
7
+ * explicit route. The model defaults to the lightweight `deepseek-v4-flash`.
8
+ */
9
+ import z from '@deepseek-ai/schemastery';
10
+ import { ReasoningEffortId } from '@deepseek-ai/dsh-llm';
11
+ import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings';
12
+ /** Settings namespace carrying the TUI's subagent model selection. */
13
+ export const SUBAGENT_SETTINGS_NAMESPACE = settingsNamespace('ssh-tui-subagent');
14
+ /** Lightweight default model used for subagent children. */
15
+ export const DEFAULT_SUBAGENT_MODEL = 'deepseek-v4-flash';
16
+ /** Settings schema for `$DSH_HOME/settings.yaml`. */
17
+ export const SUBAGENT_SETTINGS_SCHEMA = z.object({
18
+ provider: z.string(),
19
+ model: z.string().default(DEFAULT_SUBAGENT_MODEL),
20
+ reasoningEffort: z.string(),
21
+ });
22
+ /** Normalize a raw settings section into a live typed selection. */
23
+ export function normalizeSubagentSelection(value) {
24
+ const raw = (typeof value === 'object' && value !== null ? value : {});
25
+ return {
26
+ ...(typeof raw.provider === 'string' && raw.provider.trim() !== ''
27
+ ? { provider: raw.provider.trim() }
28
+ : {}),
29
+ model: typeof raw.model === 'string' && raw.model.trim() !== ''
30
+ ? raw.model.trim()
31
+ : DEFAULT_SUBAGENT_MODEL,
32
+ ...(typeof raw.reasoningEffort === 'string' && raw.reasoningEffort.trim() !== ''
33
+ ? { reasoningEffort: ReasoningEffortId(raw.reasoningEffort.trim()) }
34
+ : {}),
35
+ };
36
+ }
37
+ /**
38
+ * Install the settings-backed subagent selection for one TUI plugin fiber.
39
+ * Returns the mutable ref the request waterfall and the slash commands share.
40
+ */
41
+ export function createSubagentSelection(ctx) {
42
+ let source = () => ({ model: DEFAULT_SUBAGENT_MODEL });
43
+ const ref = { current: normalizeSubagentSelection(source()) };
44
+ installSettingsSection(ctx, SUBAGENT_SETTINGS_NAMESPACE, SUBAGENT_SETTINGS_SCHEMA, {
45
+ model: DEFAULT_SUBAGENT_MODEL,
46
+ }, {
47
+ setSource: (current) => {
48
+ source = () => current();
49
+ },
50
+ onChange: () => {
51
+ ref.current = normalizeSubagentSelection(source());
52
+ },
53
+ });
54
+ return ref;
55
+ }
56
+ /** Serialize a live selection back into the settings document shape. */
57
+ export function subagentSettingsValue(selection) {
58
+ return {
59
+ ...(selection.provider === undefined ? {} : { provider: selection.provider }),
60
+ model: selection.model,
61
+ ...(selection.reasoningEffort === undefined ? {} : { reasoningEffort: String(selection.reasoningEffort) }),
62
+ };
63
+ }
64
+ //# sourceMappingURL=subagent-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-model.js","sourceRoot":"","sources":["../src/subagent-model.ts"],"names":[],"mappings":"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"}
package/lib/tui.js CHANGED
@@ -21,6 +21,7 @@ import { SessionId } from '@deepseek-ai/dsh-session';
21
21
  import { settingsNamespace } from '@deepseek-ai/dsh-settings';
22
22
  import { formatSessionTime, listResumableSessions } from './session-list.js';
23
23
  import { defaultReasoningEffort } from './reasoning.js';
24
+ import { DEFAULT_SUBAGENT_MODEL, SUBAGENT_SETTINGS_NAMESPACE, subagentSettingsValue, } from './subagent-model.js';
24
25
  import { UserQuestionError, } from '@deepseek-ai/dsh-user-questions';
25
26
  const PROVIDER_TEMPLATES = {
26
27
  official: {
@@ -63,6 +64,7 @@ const WAIT_INDICATOR_MS = 8000;
63
64
  const STALL_WARNING_MS = 60000;
64
65
  const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
65
66
  const QUESTION_OPTION_KEYS = '123456789abcdefghijklmnopqrstuvwxyz';
67
+ const SUBAGENT_DEFAULT_EFFORT_LABEL = '跟随提供商默认';
66
68
  const RESERVED_BOTTOM_LINES = 3; // input line + stats line + status line
67
69
  const MAX_TRANSCRIPT_ROWS = 5000;
68
70
  const IS_WINDOWS = process.platform === 'win32';
@@ -195,6 +197,8 @@ const DEEPSEEK_LOGO_VARIANTS = [
195
197
  const LOCAL_COMMANDS = [
196
198
  { name: 'help', description: 'show all available commands' },
197
199
  { name: 'model', description: 'select model and reasoning effort (same provider)' },
200
+ { name: 'submodel', description: `select subagent model (default ${DEFAULT_SUBAGENT_MODEL}, same provider as parent)` },
201
+ { name: 'subeffort', description: 'select subagent reasoning effort (default follows provider)' },
198
202
  { name: 'mode', description: 'switch agent mode / preset (standard, minimal, code, cordis, routing-suite, ...)' },
199
203
  { name: 'quit', description: 'exit the TUI' },
200
204
  { name: 'exit', description: 'exit the TUI' },
@@ -1159,6 +1163,7 @@ export class SshTui {
1159
1163
  resume;
1160
1164
  providerName;
1161
1165
  selectionRef;
1166
+ subagentSelection;
1162
1167
  onSwitchSession;
1163
1168
  onSelectionChanged;
1164
1169
  resumePicker;
@@ -1220,6 +1225,7 @@ export class SshTui {
1220
1225
  this.resume = config.resume === true;
1221
1226
  this.providerName = config.provider ?? 'deepseek-official';
1222
1227
  this.selectionRef = config.selectionRef;
1228
+ this.subagentSelection = config.subagentSelection ?? { current: { model: DEFAULT_SUBAGENT_MODEL } };
1223
1229
  this.onSwitchSession = config.onSwitchSession;
1224
1230
  this.onSelectionChanged = config.onSelectionChanged;
1225
1231
  this.resumePicker = config.resumePicker === true;
@@ -1237,7 +1243,7 @@ export class SshTui {
1237
1243
  process.stdin.on('data', this.handleData);
1238
1244
  process.stdout.on('resize', this.markDirty);
1239
1245
  process.on('SIGWINCH', this.markDirty);
1240
- this.disposers.push(this.ctx.on('session/event', this.handleSessionEvent), this.ctx.on('agent/status', this.handleStatus), this.ctx.on('agent/error', this.handleError), this.ctx.on('agent/disposed', this.handleDisposed), this.ctx.on('agent/inbox/claimed', this.handleInboxClaimed), this.ctx.on('agent/inbox/discarded', this.handleInboxDiscarded), this.ctx.on('subagent/start', this.handleSubagentStart), this.ctx.on('subagent/end', this.handleSubagentEnd), this.ctx.on('approval/request', this.handleApproval));
1246
+ this.disposers.push(this.ctx.on('session/event', this.handleSessionEvent), this.ctx.on('agent/status', this.handleStatus), this.ctx.on('agent/error', this.handleError), this.ctx.on('agent/disposed', this.handleDisposed), this.ctx.on('agent/inbox/claimed', this.handleInboxClaimed), this.ctx.on('agent/inbox/discarded', this.handleInboxDiscarded), this.ctx.on('agent/request', this.handleAgentRequest), this.ctx.on('subagent/start', this.handleSubagentStart), this.ctx.on('subagent/end', this.handleSubagentEnd), this.ctx.on('approval/request', this.handleApproval));
1241
1247
  const questions = this.ctx.get('userQuestions');
1242
1248
  if (questions !== undefined) {
1243
1249
  this.userQuestionDisposer = questions.registerProvider({ ask: this.handleUserQuestions });
@@ -1829,6 +1835,12 @@ export class SshTui {
1829
1835
  const statsText = this.statsText();
1830
1836
  const statsLine = this.styleLine('system', fitLine(statsText === '' ? '— 尚无会话统计' : statsText));
1831
1837
  let statusText = `${this.status} [${this.presetName}] ${this.currentSelectionLabel()}`;
1838
+ const sub = this.subagentSelection.current;
1839
+ const subProvider = sub.provider ?? this.selectionRef?.current?.provider ?? this.agent.options.provider ?? this.providerName;
1840
+ const subEffort = sub.reasoningEffort === undefined ? '' : `(${sub.reasoningEffort})`;
1841
+ statusText += sub.provider === undefined
1842
+ ? ` · sub:${sub.model}${subEffort}`
1843
+ : ` · sub:${subProvider}/${sub.model}${subEffort}`;
1832
1844
  if (inputView.folded)
1833
1845
  statusText += ' · 输入已折叠 · Ctrl+T 展开';
1834
1846
  else if (inputRows > 1)
@@ -2041,6 +2053,24 @@ export class SshTui {
2041
2053
  return `\x1b[${code}m${safe}`;
2042
2054
  }
2043
2055
  // ── event handling ──────────────────────────────────────────────────────
2056
+ /**
2057
+ * Apply the TUI's subagent model selection to every child-agent request.
2058
+ * The parent request is left untouched (its own `/model` waterfall already
2059
+ * owns the route); direct children created by tool-subagent inherit the
2060
+ * parent provider unless `/submodel` stored an explicit subagent provider.
2061
+ */
2062
+ handleAgentRequest = async ({ agent }, next) => {
2063
+ const resolved = await next();
2064
+ if (agent === this.agent)
2065
+ return resolved;
2066
+ const selection = this.subagentSelection.current;
2067
+ return {
2068
+ ...resolved,
2069
+ ...(selection.provider === undefined ? {} : { provider: selection.provider }),
2070
+ model: selection.model,
2071
+ ...(selection.reasoningEffort === undefined ? {} : { reasoningEffort: selection.reasoningEffort }),
2072
+ };
2073
+ };
2044
2074
  handleSessionEvent = (session, event) => {
2045
2075
  if (session.id !== this.agent.id) {
2046
2076
  if (this.subagentSessions.has(session.id))
@@ -2751,6 +2781,147 @@ export class SshTui {
2751
2781
  this.pushRow({ kind: 'system', text: `模型已切换:${selected.id}(思考强度 ${effort ?? '默认'});下一步请求生效。` });
2752
2782
  this.markDirty();
2753
2783
  }
2784
+ /** Provider route the next subagent request should use. */
2785
+ effectiveSubagentProvider() {
2786
+ return this.subagentSelection.current.provider
2787
+ ?? this.selectionRef?.current?.provider
2788
+ ?? this.agent.options.provider
2789
+ ?? this.providerName;
2790
+ }
2791
+ /** Persist one subagent selection and publish it to the live request waterfall. */
2792
+ async saveSubagentSelection(next) {
2793
+ this.subagentSelection.current = next;
2794
+ const settings = this.ctx.get('settings');
2795
+ if (settings === undefined) {
2796
+ this.pushRow({ kind: 'error', text: '设置服务不可用,子代理选择仅当前会话生效。' });
2797
+ this.markDirty();
2798
+ return false;
2799
+ }
2800
+ await settings.replace(SUBAGENT_SETTINGS_NAMESPACE, subagentSettingsValue(next));
2801
+ return true;
2802
+ }
2803
+ /** Resolve the picker model list for one provider (endpoint first, then catalog). */
2804
+ async subagentModelOptions(provider) {
2805
+ const llm = this.ctx.get('llm');
2806
+ let options = [];
2807
+ let source = '已配置列表';
2808
+ if (this.piAiProviderProfile(provider) !== undefined || provider === 'opencode' || provider === 'opencode-go') {
2809
+ const previousStatus = this.status;
2810
+ try {
2811
+ this.status = `正在从端点获取子代理模型列表(${provider})…`;
2812
+ this.markDirty();
2813
+ options = await this.discoverEndpointModels(provider);
2814
+ if (options.length > 0) {
2815
+ source = '端点实时列表';
2816
+ try {
2817
+ const listed = (await llm?.listModels(provider)) ?? [];
2818
+ const endpointIds = new Set(options.map(model => model.id));
2819
+ for (const model of listed) {
2820
+ if (!endpointIds.has(model.id))
2821
+ options.push({ id: model.id, label: model.name || model.id });
2822
+ }
2823
+ }
2824
+ catch {
2825
+ // The endpoint list stands alone when the catalog cannot be read.
2826
+ }
2827
+ }
2828
+ }
2829
+ catch {
2830
+ options = [];
2831
+ }
2832
+ finally {
2833
+ this.status = previousStatus;
2834
+ this.markDirty();
2835
+ }
2836
+ }
2837
+ if (options.length === 0) {
2838
+ try {
2839
+ const listed = (await llm?.listModels(provider)) ?? [];
2840
+ options = listed.map(model => ({ id: model.id, label: model.name || model.id }));
2841
+ }
2842
+ catch {
2843
+ options = [];
2844
+ }
2845
+ }
2846
+ return { options, source };
2847
+ }
2848
+ /** /submodel: pick (or set) the model subagent children use. */
2849
+ async runSubmodelCommand(arg) {
2850
+ const provider = this.effectiveSubagentProvider();
2851
+ const current = this.subagentSelection.current;
2852
+ const direct = arg.trim();
2853
+ let selectedId = direct;
2854
+ if (selectedId === '') {
2855
+ const { options, source } = await this.subagentModelOptions(provider);
2856
+ if (options.length === 0) {
2857
+ options.push({ id: current.model, label: current.model });
2858
+ }
2859
+ const selected = await this.pickModelOption(options, provider, source, current.model);
2860
+ if (selected === undefined)
2861
+ return;
2862
+ selectedId = selected.id;
2863
+ }
2864
+ if (!(await this.ensureProviderModelConfigured(provider, selectedId)))
2865
+ return;
2866
+ const persisted = await this.saveSubagentSelection({ ...current, model: selectedId });
2867
+ this.pushRow({
2868
+ kind: 'system',
2869
+ text: `${current.provider === undefined
2870
+ ? `子代理模型已切换:${selectedId}(提供方跟随父会话 ${provider})。`
2871
+ : `子代理模型已切换:${selectedId}(提供方 ${provider})。`}${persisted ? '' : '(仅当前会话)'}`,
2872
+ });
2873
+ this.markDirty();
2874
+ }
2875
+ /** /subeffort: pick the reasoning effort subagent children use. */
2876
+ async runSubeffortCommand() {
2877
+ const provider = this.effectiveSubagentProvider();
2878
+ const current = this.subagentSelection.current;
2879
+ const llm = this.ctx.get('llm');
2880
+ let effortOptions = [];
2881
+ try {
2882
+ const info = await llm?.resolveModelInfo(provider, current.model);
2883
+ effortOptions = (info?.reasoning?.efforts ?? []).map(effort => ({ id: String(effort.id), label: effort.name }));
2884
+ }
2885
+ catch {
2886
+ effortOptions = [];
2887
+ }
2888
+ if (effortOptions.length === 0) {
2889
+ effortOptions = ['off', 'high', 'max'].map(id => ({ id, label: id }));
2890
+ }
2891
+ const choices = [
2892
+ { id: undefined, label: SUBAGENT_DEFAULT_EFFORT_LABEL },
2893
+ ...effortOptions.map(option => ({ id: option.id, label: option.label })),
2894
+ ];
2895
+ const answer = await this.askQuestion({
2896
+ id: 'subagent-effort-pick',
2897
+ question: `选择子代理思考强度(${provider}/${current.model})`,
2898
+ options: choices.map(option => ({
2899
+ label: option.label,
2900
+ description: option.id === undefined
2901
+ ? '清空自定义强度,跟随提供商/模型默认'
2902
+ : option.id === String(current.reasoningEffort)
2903
+ ? '当前'
2904
+ : undefined,
2905
+ })),
2906
+ });
2907
+ const picked = choices.find(option => option.label === answer.selected[0]);
2908
+ if (picked === undefined)
2909
+ return;
2910
+ const next = {
2911
+ ...current,
2912
+ ...(picked.id === undefined
2913
+ ? { reasoningEffort: undefined }
2914
+ : { reasoningEffort: ReasoningEffortId(picked.id) }),
2915
+ };
2916
+ const persisted = await this.saveSubagentSelection(next);
2917
+ this.pushRow({
2918
+ kind: 'system',
2919
+ text: `${picked.id === undefined
2920
+ ? '子代理思考强度已恢复为提供商默认。'
2921
+ : `子代理思考强度已切换:${picked.id}。`}${persisted ? '' : '(仅当前会话)'}`,
2922
+ });
2923
+ this.markDirty();
2924
+ }
2754
2925
  /** /mode: pick an agent preset (standard / minimal / code / cordis / routing-suite / ...). */
2755
2926
  async runModeCommand() {
2756
2927
  const agentPresets = this.ctx.get('agentPresets');
@@ -3844,6 +4015,28 @@ export class SshTui {
3844
4015
  this.markDirty();
3845
4016
  });
3846
4017
  break;
4018
+ case 'submodel':
4019
+ void this.runSubmodelCommand(arg).catch((error) => {
4020
+ if (error instanceof UserQuestionError) {
4021
+ this.pushRow({ kind: 'system', text: '子代理模型选择已取消。' });
4022
+ }
4023
+ else {
4024
+ this.pushRow({ kind: 'error', text: `/submodel failed: ${errorChain(error)}` });
4025
+ }
4026
+ this.markDirty();
4027
+ });
4028
+ break;
4029
+ case 'subeffort':
4030
+ void this.runSubeffortCommand().catch((error) => {
4031
+ if (error instanceof UserQuestionError) {
4032
+ this.pushRow({ kind: 'system', text: '子代理思考强度选择已取消。' });
4033
+ }
4034
+ else {
4035
+ this.pushRow({ kind: 'error', text: `/subeffort failed: ${errorChain(error)}` });
4036
+ }
4037
+ this.markDirty();
4038
+ });
4039
+ break;
3847
4040
  case 'mode':
3848
4041
  void this.runModeCommand().catch((error) => {
3849
4042
  if (error instanceof UserQuestionError) {