dsh-ssh-tui 0.1.6 → 0.1.8

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' },
@@ -800,6 +804,12 @@ function lastCodePoints(text, max) {
800
804
  return '';
801
805
  return Array.from(text).slice(-max).join('');
802
806
  }
807
+ /** Format a model list compactly: show the first few entries and an ellipsis. */
808
+ function formatModelList(models, max = 5) {
809
+ const shown = models.slice(0, max);
810
+ const text = shown.join(', ');
811
+ return models.length > max ? `${text}…(共 ${models.length} 个)` : text;
812
+ }
803
813
  /** Prefer the fields a human scans for; fall back to the first scalar pairs. */
804
814
  function friendlyArgsSummary(name, args) {
805
815
  const parsed = parseJsonArgs(args);
@@ -1159,6 +1169,7 @@ export class SshTui {
1159
1169
  resume;
1160
1170
  providerName;
1161
1171
  selectionRef;
1172
+ subagentSelection;
1162
1173
  onSwitchSession;
1163
1174
  onSelectionChanged;
1164
1175
  resumePicker;
@@ -1220,6 +1231,7 @@ export class SshTui {
1220
1231
  this.resume = config.resume === true;
1221
1232
  this.providerName = config.provider ?? 'deepseek-official';
1222
1233
  this.selectionRef = config.selectionRef;
1234
+ this.subagentSelection = config.subagentSelection ?? { current: { model: DEFAULT_SUBAGENT_MODEL } };
1223
1235
  this.onSwitchSession = config.onSwitchSession;
1224
1236
  this.onSelectionChanged = config.onSelectionChanged;
1225
1237
  this.resumePicker = config.resumePicker === true;
@@ -1237,7 +1249,7 @@ export class SshTui {
1237
1249
  process.stdin.on('data', this.handleData);
1238
1250
  process.stdout.on('resize', this.markDirty);
1239
1251
  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));
1252
+ 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
1253
  const questions = this.ctx.get('userQuestions');
1242
1254
  if (questions !== undefined) {
1243
1255
  this.userQuestionDisposer = questions.registerProvider({ ask: this.handleUserQuestions });
@@ -1699,7 +1711,9 @@ export class SshTui {
1699
1711
  case 'models':
1700
1712
  addDialog(`提供商:${providerLabel}`);
1701
1713
  addDialog('模型 ID(多个用逗号或空格分隔):');
1702
- addDialog(` 默认:${template.defaultModels.join(', ')}`);
1714
+ addDialog(ob.models.length > 0
1715
+ ? ` 已获取(${ob.models.length}):${formatModelList(ob.models, 6)}`
1716
+ : ` 默认:${template.defaultModels.join(', ')}`);
1703
1717
  if (template.api !== undefined)
1704
1718
  addDialog(' Ctrl+F = 从端点获取模型列表');
1705
1719
  addDialog(' Enter 确认,Esc 取消');
@@ -1710,7 +1724,7 @@ export class SshTui {
1710
1724
  addDialog(` Provider ID: ${ob.providerId}`);
1711
1725
  addDialog(` Base URL: ${ob.baseUrl === '' ? (template.defaultBaseUrl || '(默认)') : ob.baseUrl}`);
1712
1726
  addDialog(` API 协议: ${template.api ?? 'deepseek-official'}`);
1713
- addDialog(` 模型: ${ob.models.join(', ')}`);
1727
+ addDialog(` 模型: ${formatModelList(ob.models, 8)}`);
1714
1728
  addDialog(` API Key: ${sliceCodePoints(ob.key, 6)}…${lastCodePoints(ob.key, 4)}(长度 ${ob.key.length})`);
1715
1729
  addDialog(' y = 保存, n = 重填, Esc = 取消');
1716
1730
  break;
@@ -1829,6 +1843,12 @@ export class SshTui {
1829
1843
  const statsText = this.statsText();
1830
1844
  const statsLine = this.styleLine('system', fitLine(statsText === '' ? '— 尚无会话统计' : statsText));
1831
1845
  let statusText = `${this.status} [${this.presetName}] ${this.currentSelectionLabel()}`;
1846
+ const sub = this.subagentSelection.current;
1847
+ const subProvider = sub.provider ?? this.selectionRef?.current?.provider ?? this.agent.options.provider ?? this.providerName;
1848
+ const subEffort = sub.reasoningEffort === undefined ? '' : `(${sub.reasoningEffort})`;
1849
+ statusText += sub.provider === undefined
1850
+ ? ` · sub:${sub.model}${subEffort}`
1851
+ : ` · sub:${subProvider}/${sub.model}${subEffort}`;
1832
1852
  if (inputView.folded)
1833
1853
  statusText += ' · 输入已折叠 · Ctrl+T 展开';
1834
1854
  else if (inputRows > 1)
@@ -2041,6 +2061,24 @@ export class SshTui {
2041
2061
  return `\x1b[${code}m${safe}`;
2042
2062
  }
2043
2063
  // ── event handling ──────────────────────────────────────────────────────
2064
+ /**
2065
+ * Apply the TUI's subagent model selection to every child-agent request.
2066
+ * The parent request is left untouched (its own `/model` waterfall already
2067
+ * owns the route); direct children created by tool-subagent inherit the
2068
+ * parent provider unless `/submodel` stored an explicit subagent provider.
2069
+ */
2070
+ handleAgentRequest = async ({ agent }, next) => {
2071
+ const resolved = await next();
2072
+ if (agent === this.agent)
2073
+ return resolved;
2074
+ const selection = this.subagentSelection.current;
2075
+ return {
2076
+ ...resolved,
2077
+ ...(selection.provider === undefined ? {} : { provider: selection.provider }),
2078
+ model: selection.model,
2079
+ ...(selection.reasoningEffort === undefined ? {} : { reasoningEffort: selection.reasoningEffort }),
2080
+ };
2081
+ };
2044
2082
  handleSessionEvent = (session, event) => {
2045
2083
  if (session.id !== this.agent.id) {
2046
2084
  if (this.subagentSessions.has(session.id))
@@ -2728,17 +2766,21 @@ export class SshTui {
2728
2766
  effortOptions = [];
2729
2767
  }
2730
2768
  if (effortOptions.length === 0) {
2731
- effortOptions = ['off', 'high', 'max'].map(id => ({ id, label: id }));
2769
+ // No selectable reasoning effort for this model: do not invent
2770
+ // `off/high/max`, which the adapter may reject for the exact model.
2771
+ }
2772
+ let effort;
2773
+ if (effortOptions.length > 0) {
2774
+ const effortAnswer = await this.askQuestion({
2775
+ id: 'effort-pick',
2776
+ question: `选择思考强度(${selected.id})`,
2777
+ options: effortOptions.map(option => ({
2778
+ label: option.label,
2779
+ description: option.id === String(current?.reasoningEffort) ? '当前' : undefined,
2780
+ })),
2781
+ });
2782
+ effort = effortOptions.find(option => option.label === effortAnswer.selected[0])?.id;
2732
2783
  }
2733
- const effortAnswer = await this.askQuestion({
2734
- id: 'effort-pick',
2735
- question: `选择思考强度(${selected.id})`,
2736
- options: effortOptions.map(option => ({
2737
- label: option.label,
2738
- description: option.id === String(current?.reasoningEffort) ? '当前' : undefined,
2739
- })),
2740
- });
2741
- const effort = effortOptions.find(option => option.label === effortAnswer.selected[0])?.id;
2742
2784
  const next = {
2743
2785
  provider,
2744
2786
  model: selected.id,
@@ -2748,7 +2790,156 @@ export class SshTui {
2748
2790
  this.selectionRef.current = next;
2749
2791
  this.onSelectionChanged?.(next);
2750
2792
  await this.ctx.get('agentDefaultModel')?.saveSelection(next);
2751
- this.pushRow({ kind: 'system', text: `模型已切换:${selected.id}(思考强度 ${effort ?? '默认'});下一步请求生效。` });
2793
+ this.pushRow({
2794
+ kind: 'system',
2795
+ text: `模型已切换:${selected.id}(思考强度 ${effort ?? '默认'}${effortOptions.length === 0 ? ',该模型未声明可选强度' : ''});下一步请求生效。`,
2796
+ });
2797
+ this.markDirty();
2798
+ }
2799
+ /** Provider route the next subagent request should use. */
2800
+ effectiveSubagentProvider() {
2801
+ return this.subagentSelection.current.provider
2802
+ ?? this.selectionRef?.current?.provider
2803
+ ?? this.agent.options.provider
2804
+ ?? this.providerName;
2805
+ }
2806
+ /** Persist one subagent selection and publish it to the live request waterfall. */
2807
+ async saveSubagentSelection(next) {
2808
+ this.subagentSelection.current = next;
2809
+ const settings = this.ctx.get('settings');
2810
+ if (settings === undefined) {
2811
+ this.pushRow({ kind: 'error', text: '设置服务不可用,子代理选择仅当前会话生效。' });
2812
+ this.markDirty();
2813
+ return false;
2814
+ }
2815
+ await settings.replace(SUBAGENT_SETTINGS_NAMESPACE, subagentSettingsValue(next));
2816
+ return true;
2817
+ }
2818
+ /** Resolve the picker model list for one provider (endpoint first, then catalog). */
2819
+ async subagentModelOptions(provider) {
2820
+ const llm = this.ctx.get('llm');
2821
+ let options = [];
2822
+ let source = '已配置列表';
2823
+ if (this.piAiProviderProfile(provider) !== undefined || provider === 'opencode' || provider === 'opencode-go') {
2824
+ const previousStatus = this.status;
2825
+ try {
2826
+ this.status = `正在从端点获取子代理模型列表(${provider})…`;
2827
+ this.markDirty();
2828
+ options = await this.discoverEndpointModels(provider);
2829
+ if (options.length > 0) {
2830
+ source = '端点实时列表';
2831
+ try {
2832
+ const listed = (await llm?.listModels(provider)) ?? [];
2833
+ const endpointIds = new Set(options.map(model => model.id));
2834
+ for (const model of listed) {
2835
+ if (!endpointIds.has(model.id))
2836
+ options.push({ id: model.id, label: model.name || model.id });
2837
+ }
2838
+ }
2839
+ catch {
2840
+ // The endpoint list stands alone when the catalog cannot be read.
2841
+ }
2842
+ }
2843
+ }
2844
+ catch {
2845
+ options = [];
2846
+ }
2847
+ finally {
2848
+ this.status = previousStatus;
2849
+ this.markDirty();
2850
+ }
2851
+ }
2852
+ if (options.length === 0) {
2853
+ try {
2854
+ const listed = (await llm?.listModels(provider)) ?? [];
2855
+ options = listed.map(model => ({ id: model.id, label: model.name || model.id }));
2856
+ }
2857
+ catch {
2858
+ options = [];
2859
+ }
2860
+ }
2861
+ return { options, source };
2862
+ }
2863
+ /** /submodel: pick (or set) the model subagent children use. */
2864
+ async runSubmodelCommand(arg) {
2865
+ const provider = this.effectiveSubagentProvider();
2866
+ const current = this.subagentSelection.current;
2867
+ const direct = arg.trim();
2868
+ let selectedId = direct;
2869
+ if (selectedId === '') {
2870
+ const { options, source } = await this.subagentModelOptions(provider);
2871
+ if (options.length === 0) {
2872
+ options.push({ id: current.model, label: current.model });
2873
+ }
2874
+ const selected = await this.pickModelOption(options, provider, source, current.model);
2875
+ if (selected === undefined)
2876
+ return;
2877
+ selectedId = selected.id;
2878
+ }
2879
+ if (!(await this.ensureProviderModelConfigured(provider, selectedId)))
2880
+ return;
2881
+ const persisted = await this.saveSubagentSelection({ ...current, model: selectedId });
2882
+ this.pushRow({
2883
+ kind: 'system',
2884
+ text: `${current.provider === undefined
2885
+ ? `子代理模型已切换:${selectedId}(提供方跟随父会话 ${provider})。`
2886
+ : `子代理模型已切换:${selectedId}(提供方 ${provider})。`}${persisted ? '' : '(仅当前会话)'}`,
2887
+ });
2888
+ this.markDirty();
2889
+ }
2890
+ /** /subeffort: pick the reasoning effort subagent children use. */
2891
+ async runSubeffortCommand() {
2892
+ const provider = this.effectiveSubagentProvider();
2893
+ const current = this.subagentSelection.current;
2894
+ const llm = this.ctx.get('llm');
2895
+ let effortOptions = [];
2896
+ try {
2897
+ const info = await llm?.resolveModelInfo(provider, current.model);
2898
+ effortOptions = (info?.reasoning?.efforts ?? []).map(effort => ({ id: String(effort.id), label: effort.name }));
2899
+ }
2900
+ catch {
2901
+ effortOptions = [];
2902
+ }
2903
+ if (effortOptions.length === 0 && current.reasoningEffort === undefined) {
2904
+ this.pushRow({
2905
+ kind: 'system',
2906
+ text: `模型 ${provider}/${current.model} 未声明可选 reasoning effort,已保持提供商默认;请勿手动设置 high/max。`,
2907
+ });
2908
+ this.markDirty();
2909
+ return;
2910
+ }
2911
+ const choices = [
2912
+ { id: undefined, label: SUBAGENT_DEFAULT_EFFORT_LABEL },
2913
+ ...effortOptions.map(option => ({ id: option.id, label: option.label })),
2914
+ ];
2915
+ const answer = await this.askQuestion({
2916
+ id: 'subagent-effort-pick',
2917
+ question: `选择子代理思考强度(${provider}/${current.model})`,
2918
+ options: choices.map(option => ({
2919
+ label: option.label,
2920
+ description: option.id === undefined
2921
+ ? '清空自定义强度,跟随提供商/模型默认'
2922
+ : option.id === String(current.reasoningEffort)
2923
+ ? '当前'
2924
+ : undefined,
2925
+ })),
2926
+ });
2927
+ const picked = choices.find(option => option.label === answer.selected[0]);
2928
+ if (picked === undefined)
2929
+ return;
2930
+ const next = {
2931
+ ...current,
2932
+ ...(picked.id === undefined
2933
+ ? { reasoningEffort: undefined }
2934
+ : { reasoningEffort: ReasoningEffortId(picked.id) }),
2935
+ };
2936
+ const persisted = await this.saveSubagentSelection(next);
2937
+ this.pushRow({
2938
+ kind: 'system',
2939
+ text: `${picked.id === undefined
2940
+ ? '子代理思考强度已恢复为提供商默认。'
2941
+ : `子代理思考强度已切换:${picked.id}。`}${persisted ? '' : '(仅当前会话)'}`,
2942
+ });
2752
2943
  this.markDirty();
2753
2944
  }
2754
2945
  /** /mode: pick an agent preset (standard / minimal / code / cordis / routing-suite / ...). */
@@ -3486,7 +3677,7 @@ export class SshTui {
3486
3677
  state.models = ids;
3487
3678
  this.input = '';
3488
3679
  this.cursor = 0;
3489
- this.pushRow({ kind: 'system', text: `已从端点获取 ${ids.length} 个模型(Enter 确认,也可继续修改)。` });
3680
+ this.pushRow({ kind: 'system', text: `已从端点获取 ${ids.length} 个模型:${formatModelList(ids, 6)}(Enter 确认,也可继续修改)。` });
3490
3681
  }
3491
3682
  }
3492
3683
  catch (error) {
@@ -3844,6 +4035,28 @@ export class SshTui {
3844
4035
  this.markDirty();
3845
4036
  });
3846
4037
  break;
4038
+ case 'submodel':
4039
+ void this.runSubmodelCommand(arg).catch((error) => {
4040
+ if (error instanceof UserQuestionError) {
4041
+ this.pushRow({ kind: 'system', text: '子代理模型选择已取消。' });
4042
+ }
4043
+ else {
4044
+ this.pushRow({ kind: 'error', text: `/submodel failed: ${errorChain(error)}` });
4045
+ }
4046
+ this.markDirty();
4047
+ });
4048
+ break;
4049
+ case 'subeffort':
4050
+ void this.runSubeffortCommand().catch((error) => {
4051
+ if (error instanceof UserQuestionError) {
4052
+ this.pushRow({ kind: 'system', text: '子代理思考强度选择已取消。' });
4053
+ }
4054
+ else {
4055
+ this.pushRow({ kind: 'error', text: `/subeffort failed: ${errorChain(error)}` });
4056
+ }
4057
+ this.markDirty();
4058
+ });
4059
+ break;
3847
4060
  case 'mode':
3848
4061
  void this.runModeCommand().catch((error) => {
3849
4062
  if (error instanceof UserQuestionError) {