minimal-agent 0.18.13 → 0.18.14

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 (174) hide show
  1. package/package.json +1 -3
  2. package/plugins/workflow-runner/src/runtime/context.js +1 -1
  3. package/plugins/workflow-runner/src/runtime/context.ts +1 -1
  4. package/plugins/workflow-runner/test/embeddedExecution.test.ts +2 -5
  5. package/plugins/workflow-runner/test/execLifetime.test.ts +15 -2
  6. package/skills/ssh-remote-dev/SKILL.md +61 -0
  7. package/skills/ssh-remote-dev/agents/openai.yaml +4 -0
  8. package/src/advisor/README.md +62 -24
  9. package/src/advisor/advisorNudge.js +199 -0
  10. package/src/advisor/advisorNudge.ts +232 -0
  11. package/src/advisor/deliveryReview.js +35 -12
  12. package/src/advisor/deliveryReview.ts +35 -11
  13. package/src/advisor/presentation.js +7 -10
  14. package/src/advisor/presentation.ts +10 -13
  15. package/src/advisor/reviewContext.js +22 -58
  16. package/src/advisor/reviewContext.ts +24 -67
  17. package/src/cli/configMenu.js +17 -128
  18. package/src/cli/configMenu.tsx +17 -193
  19. package/src/cli/print.js +2 -0
  20. package/src/cli/print.ts +2 -0
  21. package/src/cli/streamJson.js +2 -0
  22. package/src/cli/streamJson.ts +2 -0
  23. package/src/cli/wizards/advisorWizard.js +9 -7
  24. package/src/cli/wizards/advisorWizard.tsx +9 -7
  25. package/src/cli/wizards/contextAdvisorWizard.js +9 -7
  26. package/src/cli/wizards/contextAdvisorWizard.tsx +9 -7
  27. package/src/cli/wizards/providerPresets.js +27 -1
  28. package/src/cli/wizards/providerPresets.ts +44 -2
  29. package/src/cli/wizards/providerWizard.js +51 -10
  30. package/src/cli/wizards/providerWizard.tsx +59 -9
  31. package/src/config/configDefaults.js +1 -1
  32. package/src/config/configDefaults.ts +1 -1
  33. package/src/config/configDiagnostics.js +1 -21
  34. package/src/config/configDiagnostics.ts +3 -23
  35. package/src/config/configFile.js +11 -4
  36. package/src/config/configFile.ts +27 -7
  37. package/src/config/configManagement.js +1 -0
  38. package/src/config/configManagement.ts +2 -0
  39. package/src/config/configService.js +12 -8
  40. package/src/config/configService.ts +16 -11
  41. package/src/config/numericLimits.js +0 -4
  42. package/src/config/numericLimits.ts +0 -4
  43. package/src/config/reasoningEffort.js +16 -0
  44. package/src/config/reasoningEffort.ts +18 -0
  45. package/src/context/budgetPlan.js +1 -1
  46. package/src/context/budgetPlan.ts +1 -1
  47. package/src/context/optimizeConversation.js +1 -1
  48. package/src/context/optimizeConversation.ts +1 -1
  49. package/src/harness/stallGuard.js +8 -13
  50. package/src/harness/stallGuard.ts +8 -12
  51. package/src/llm/client.js +64 -17
  52. package/src/llm/client.ts +62 -7
  53. package/src/llm/providers/openaiFormat.js +14 -3
  54. package/src/llm/providers/openaiFormat.ts +17 -3
  55. package/src/llm/providers/responsesFormat.js +275 -0
  56. package/src/llm/providers/responsesFormat.ts +306 -0
  57. package/src/loop/README.md +18 -3
  58. package/src/loop/liveDiagramGate.js +63 -0
  59. package/src/loop/liveDiagramGate.ts +61 -0
  60. package/src/loop/messages.js +12 -0
  61. package/src/loop/messages.ts +13 -0
  62. package/src/loop/options.ts +7 -2
  63. package/src/loop/toolBatch.js +0 -1
  64. package/src/loop/toolBatch.ts +0 -1
  65. package/src/loop/toolExecutor.js +11 -46
  66. package/src/loop/toolExecutor.ts +12 -55
  67. package/src/loop/toolPresentation.js +2 -13
  68. package/src/loop/toolPresentation.ts +2 -13
  69. package/src/loop/toolSemantics.js +3 -4
  70. package/src/loop/toolSemantics.ts +4 -6
  71. package/src/loop.js +222 -214
  72. package/src/loop.ts +226 -232
  73. package/src/memory/dependencies.js +11 -3
  74. package/src/memory/dependencies.ts +11 -3
  75. package/src/memory/episodicMemory.js +59 -18
  76. package/src/memory/episodicMemory.ts +58 -18
  77. package/src/memory/experienceStore.js +4 -3
  78. package/src/memory/experienceStore.ts +4 -3
  79. package/src/memory/factCandidates.js +53 -1
  80. package/src/memory/factCandidates.ts +54 -1
  81. package/src/memory/factLearning.js +2 -2
  82. package/src/memory/factLearning.ts +2 -2
  83. package/src/memory/jobPolicy.js +24 -5
  84. package/src/memory/jobPolicy.ts +25 -5
  85. package/src/memory/learning.js +2 -2
  86. package/src/memory/learning.ts +2 -2
  87. package/src/memory/learningModel.js +7 -0
  88. package/src/memory/learningModel.ts +7 -0
  89. package/src/memory/managementService.js +8 -7
  90. package/src/memory/managementService.ts +16 -8
  91. package/src/memory/memoryService.js +104 -33
  92. package/src/memory/memoryService.ts +132 -28
  93. package/src/memory/projectKnowledge.js +39 -3
  94. package/src/memory/projectKnowledge.ts +45 -3
  95. package/src/memory/recallQuery.js +56 -0
  96. package/src/memory/recallQuery.ts +58 -0
  97. package/src/memory/retrieval.js +39 -10
  98. package/src/memory/retrieval.ts +46 -11
  99. package/src/memory/turnJournal.js +75 -10
  100. package/src/memory/turnJournal.ts +66 -10
  101. package/src/memory/types.ts +7 -1
  102. package/src/prompts/delegation.js +2 -1
  103. package/src/prompts/delegation.ts +3 -1
  104. package/src/prompts/policyRegistry.js +9 -12
  105. package/src/prompts/policyRegistry.ts +9 -12
  106. package/src/prompts/promptAssembly.js +6 -5
  107. package/src/prompts/promptAssembly.ts +8 -6
  108. package/src/prompts/visualPresentation.js +39 -10
  109. package/src/prompts/visualPresentation.ts +38 -9
  110. package/src/runtime/answerPreparation.js +1 -1
  111. package/src/runtime/answerPreparation.ts +1 -1
  112. package/src/runtime/fileLock.js +19 -3
  113. package/src/runtime/fileLock.ts +19 -3
  114. package/src/runtime/invocationPolicy.js +1 -1
  115. package/src/runtime/invocationPolicy.ts +1 -1
  116. package/src/runtime/outputContract.js +0 -4
  117. package/src/runtime/outputContract.ts +0 -7
  118. package/src/session/runAgentTurn.js +3 -0
  119. package/src/session/runAgentTurn.ts +3 -0
  120. package/src/team/onlineToolContract.js +0 -1
  121. package/src/team/onlineToolContract.ts +0 -1
  122. package/src/tools/advisor/advisor.js +207 -365
  123. package/src/tools/advisor/advisor.ts +211 -383
  124. package/src/tools/agent/agentTypes.js +0 -1
  125. package/src/tools/agent/agentTypes.ts +0 -1
  126. package/src/tools/agent/coordinator.js +4 -1
  127. package/src/tools/agent/coordinator.ts +4 -1
  128. package/src/tools/bash/semantics.js +4 -5
  129. package/src/tools/bash/semantics.ts +4 -5
  130. package/src/tools/capabilities.js +2 -3
  131. package/src/tools/capabilities.ts +2 -4
  132. package/src/tools/index.js +0 -16
  133. package/src/tools/index.ts +0 -16
  134. package/src/tools/memory/maintenance.js +8 -9
  135. package/src/tools/memory/maintenance.ts +13 -11
  136. package/src/tools/memory/memory.js +5 -1
  137. package/src/tools/memory/memory.ts +5 -0
  138. package/src/tools/optimizeConversation/README.md +4 -6
  139. package/src/tools/toolPromptGuides.js +2 -9
  140. package/src/tools/toolPromptGuides.ts +2 -9
  141. package/src/types.ts +28 -105
  142. package/src/ui/App.js +2 -0
  143. package/src/ui/App.tsx +1 -0
  144. package/src/ui/MessageList.js +8 -44
  145. package/src/ui/MessageList.tsx +3 -81
  146. package/src/ui/hooks/useChat.js +2 -0
  147. package/src/ui/hooks/useChat.ts +2 -0
  148. package/src/ui/toolProgress.js +1 -1
  149. package/src/ui/toolProgress.ts +1 -1
  150. package/src/web/protocol.ts +10 -3
  151. package/src/advisor/advisorCoordinator.js +0 -734
  152. package/src/advisor/advisorCoordinator.ts +0 -825
  153. package/src/cli/wizards/sshHostWizard.js +0 -156
  154. package/src/cli/wizards/sshHostWizard.tsx +0 -214
  155. package/src/context/sshHosts.js +0 -290
  156. package/src/context/sshHosts.ts +0 -312
  157. package/src/loop/publication.js +0 -94
  158. package/src/loop/publication.ts +0 -124
  159. package/src/runtime/finalAnswer.js +0 -9
  160. package/src/runtime/finalAnswer.ts +0 -13
  161. package/src/tools/advisor/advisorDecision.js +0 -79
  162. package/src/tools/advisor/advisorDecision.ts +0 -92
  163. package/src/tools/ssh/ssh.js +0 -152
  164. package/src/tools/ssh/ssh.ts +0 -167
  165. package/src/tools/ssh/sshAvailability.js +0 -35
  166. package/src/tools/ssh/sshAvailability.ts +0 -39
  167. package/src/tools/ssh/sshClient.js +0 -177
  168. package/src/tools/ssh/sshClient.ts +0 -189
  169. package/src/tools/ssh/sshDownload.js +0 -77
  170. package/src/tools/ssh/sshDownload.ts +0 -92
  171. package/src/tools/ssh/sshHostsTool.js +0 -36
  172. package/src/tools/ssh/sshHostsTool.ts +0 -48
  173. package/src/tools/ssh/sshUpload.js +0 -72
  174. package/src/tools/ssh/sshUpload.ts +0 -85
@@ -1,11 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text, useInput } from 'ink';
3
3
  import { useCallback, useEffect, useState } from 'react';
4
- import { testAdvisorConnection, testConfiguredModel, testSshHostConnection, testToolConnection, } from '../config/configDiagnostics.js';
4
+ import { testAdvisorConnection, testConfiguredModel, testToolConnection, } from '../config/configDiagnostics.js';
5
5
  import { configService } from '../config/configService.js';
6
- import { getWorkingDir } from '../bootstrap/workingDir.js';
7
- import { listSshHosts, removeSshHost, setDefaultSshHost, } from '../context/sshHosts.js';
8
- import { workspaceIndexService } from '../context/workspaceIndex.js';
9
6
  import { applyNetworkProxy } from '../network/proxy.js';
10
7
  import { AgentRuntimeWizard } from './wizards/agentRuntimeWizard.js';
11
8
  import { BrowserWizard } from './wizards/browserWizard.js';
@@ -14,11 +11,9 @@ import { ImageWizard } from './wizards/imageWizard.js';
14
11
  import { OpenRouterWizard } from './wizards/openRouterWizard.js';
15
12
  import { ProviderWizard } from './wizards/providerWizard.js';
16
13
  import { SearchWizard } from './wizards/searchWizard.js';
17
- import { SshHostWizard } from './wizards/sshHostWizard.js';
18
14
  import { TtsWizard } from './wizards/ttsWizard.js';
19
15
  import { NetworkProxyWizard } from './wizards/networkProxyWizard.js';
20
- const MAIN = ['模型与上下文', '工具与代理', '远程主机 (SSH)', '网络与 VPN 代理', '浏览器', '完成并返回聊天'];
21
- const SSH_HOST_ACTIONS = ['编辑', '测试连接', '设为默认主机', '删除', '返回'];
16
+ const MAIN = ['模型与上下文', '工具与代理', '网络与 VPN 代理', '浏览器', '完成并返回聊天'];
22
17
  const MODEL_CONTEXT = ['模型管理', '压缩与顾问', '测试顾问连接', '返回'];
23
18
  const TOOLS = [
24
19
  '电脑操作与子代理',
@@ -48,34 +43,15 @@ export function ConfigMenu({ onDone }) {
48
43
  const [editingModelId, setEditingModelId] = useState(null);
49
44
  const [message, setMessage] = useState(null);
50
45
  const [busy, setBusy] = useState(false);
51
- const [sshWorkspaceId, setSshWorkspaceId] = useState(null);
52
- const [sshHosts, setSshHosts] = useState(null);
53
- const [editingSshAlias, setEditingSshAlias] = useState(null);
54
46
  const refresh = useCallback(async () => {
55
47
  const snapshot = await configService.reload();
56
48
  applyNetworkProxy(snapshot.config?.networkProxy);
57
49
  setConfig(snapshot.config);
58
50
  setReadOnly(snapshot.readOnly);
59
51
  }, []);
60
- const refreshSshHosts = useCallback(async (workspaceId) => {
61
- setSshHosts(await listSshHosts(workspaceId));
62
- }, []);
63
52
  useEffect(() => {
64
53
  void refresh();
65
- // SSH 主机按工作区隔离;这里复用与主循环相同的解析函数(workspaceIndexService),
66
- // 保证 /config 里编辑的主机和 SSH 工具运行时读到的是同一份数据。找不到(从未
67
- // 注册过)就注册一次——能走到这里说明用户已经在一个真实交互会话里,不是
68
- // e2e/cron 那类刻意排除的临时目录。
69
- void (async () => {
70
- const cwd = getWorkingDir();
71
- const found = await workspaceIndexService.findByPath(cwd).catch(() => null);
72
- const workspace = found ?? await workspaceIndexService.register(cwd).catch(() => null);
73
- if (!workspace)
74
- return;
75
- setSshWorkspaceId(workspace.id);
76
- await refreshSshHosts(workspace.id);
77
- })();
78
- }, [refresh, refreshSshHosts]);
54
+ }, [refresh]);
79
55
  const navigate = (next) => {
80
56
  setIndex(0);
81
57
  setMessage(null);
@@ -98,27 +74,6 @@ export function ConfigMenu({ onDone }) {
98
74
  setBusy(false);
99
75
  }
100
76
  }, [refresh]);
101
- const mutateSsh = useCallback(async (operation, success) => {
102
- if (!sshWorkspaceId) {
103
- setMessage('无法确定当前工作区');
104
- return false;
105
- }
106
- setBusy(true);
107
- setMessage(null);
108
- try {
109
- await operation();
110
- await refreshSshHosts(sshWorkspaceId);
111
- setMessage(success);
112
- return true;
113
- }
114
- catch (error) {
115
- setMessage(error.message || '操作失败');
116
- return false;
117
- }
118
- finally {
119
- setBusy(false);
120
- }
121
- }, [sshWorkspaceId, refreshSshHosts]);
122
77
  useInput((input, key) => {
123
78
  if (busy ||
124
79
  [
@@ -131,7 +86,6 @@ export function ConfigMenu({ onDone }) {
131
86
  'tts',
132
87
  'network-proxy',
133
88
  'browser',
134
- 'ssh-host-wizard',
135
89
  ].includes(screen))
136
90
  return;
137
91
  const back = () => {
@@ -145,10 +99,6 @@ export function ConfigMenu({ onDone }) {
145
99
  navigate('models');
146
100
  else if (screen === 'tools')
147
101
  navigate('main');
148
- else if (screen === 'ssh-hosts')
149
- navigate('main');
150
- else if (screen === 'ssh-host-actions' || screen === 'delete-ssh-host')
151
- navigate('ssh-hosts');
152
102
  };
153
103
  if (key.escape || (key.ctrl && input === 'c')) {
154
104
  back();
@@ -164,11 +114,7 @@ export function ConfigMenu({ onDone }) {
164
114
  ? MODEL_ACTIONS.length
165
115
  : screen === 'models'
166
116
  ? (config?.models.length ?? 0) + 1
167
- : screen === 'ssh-hosts'
168
- ? (sshHosts?.hosts.length ?? 0) + 1
169
- : screen === 'ssh-host-actions'
170
- ? SSH_HOST_ACTIONS.length
171
- : 1;
117
+ : 1;
172
118
  if (key.upArrow) {
173
119
  setIndex((value) => (value - 1 + length) % length);
174
120
  return;
@@ -187,14 +133,6 @@ export function ConfigMenu({ onDone }) {
187
133
  });
188
134
  return;
189
135
  }
190
- if (screen === 'delete-ssh-host') {
191
- if (key.return && sshWorkspaceId && editingSshAlias)
192
- void mutateSsh(() => removeSshHost(sshWorkspaceId, editingSshAlias), '主机已删除').then((ok) => {
193
- if (ok)
194
- navigate('ssh-hosts');
195
- });
196
- return;
197
- }
198
136
  if (screen === 'tools' &&
199
137
  (input === 'd' || input === 't') &&
200
138
  index >= 1 &&
@@ -218,44 +156,12 @@ export function ConfigMenu({ onDone }) {
218
156
  else if (index === 1)
219
157
  navigate('tools');
220
158
  else if (index === 2)
221
- navigate('ssh-hosts');
222
- else if (index === 3)
223
159
  navigate('network-proxy');
224
- else if (index === 4)
160
+ else if (index === 3)
225
161
  navigate('browser');
226
162
  else
227
163
  onDone();
228
164
  }
229
- else if (screen === 'ssh-hosts') {
230
- const hosts = sshHosts?.hosts ?? [];
231
- if (index === hosts.length) {
232
- setEditingSshAlias(null);
233
- navigate('ssh-host-wizard');
234
- }
235
- else {
236
- setEditingSshAlias(hosts[index]?.alias ?? null);
237
- navigate('ssh-host-actions');
238
- }
239
- }
240
- else if (screen === 'ssh-host-actions' && sshWorkspaceId && editingSshAlias) {
241
- const host = sshHosts?.hosts.find((entry) => entry.alias === editingSshAlias);
242
- if (!host)
243
- return;
244
- if (index === 0)
245
- navigate('ssh-host-wizard');
246
- else if (index === 1)
247
- void mutateSsh(async () => {
248
- const result = await testSshHostConnection(sshWorkspaceId, editingSshAlias);
249
- if (!result.ok)
250
- throw new Error(result.message);
251
- }, '连接正常');
252
- else if (index === 2)
253
- void mutateSsh(() => setDefaultSshHost(sshWorkspaceId, editingSshAlias), '已设为默认主机');
254
- else if (index === 3)
255
- navigate('delete-ssh-host');
256
- else
257
- navigate('ssh-hosts');
258
- }
259
165
  else if (screen === 'model-context') {
260
166
  if (index === 0)
261
167
  navigate('models');
@@ -336,22 +242,18 @@ export function ConfigMenu({ onDone }) {
336
242
  ? 'models'
337
243
  : screen === 'context-advisor'
338
244
  ? 'model-context'
339
- : screen === 'ssh-host-wizard'
340
- ? 'ssh-hosts'
341
- : screen === 'browser' ||
342
- screen === 'tools' ||
343
- screen === 'network-proxy'
344
- ? 'main'
345
- : screen === 'agent-runtime' ||
346
- screen === 'search' ||
347
- screen === 'image' ||
348
- screen === 'openrouter' ||
349
- screen === 'tts'
350
- ? 'tools'
351
- : 'main');
245
+ : screen === 'browser' ||
246
+ screen === 'tools' ||
247
+ screen === 'network-proxy'
248
+ ? 'main'
249
+ : screen === 'agent-runtime' ||
250
+ screen === 'search' ||
251
+ screen === 'image' ||
252
+ screen === 'openrouter' ||
253
+ screen === 'tts'
254
+ ? 'tools'
255
+ : 'main');
352
256
  void refresh();
353
- if (screen === 'ssh-host-wizard' && sshWorkspaceId)
354
- void refreshSshHosts(sshWorkspaceId);
355
257
  };
356
258
  const editing = editingModelId
357
259
  ? config.models.find((model) => model.id === editingModelId)
@@ -362,7 +264,6 @@ export function ConfigMenu({ onDone }) {
362
264
  const mainItems = [
363
265
  `模型与上下文(${usableModels}/${config.models.length} 个模型可用)`,
364
266
  `工具与代理(${configuredTools}/4 项已配置${draftTools ? ` · ${draftTools} 项草稿` : ''})`,
365
- `远程主机 (SSH)(当前工作区 ${sshHosts?.hosts.length ?? 0} 个${sshHosts?.defaultHostAlias ? ` · 默认 ${sshHosts.defaultHostAlias}` : ''})`,
366
267
  `网络与 VPN 代理(${config.networkProxy?.enabled ? `已启用 · ${config.networkProxy.host}:${config.networkProxy.port}` : '已关闭'})`,
367
268
  `浏览器(${config.browser?.enabled !== false ? '已启用' : '已关闭'})`,
368
269
  '完成并返回聊天',
@@ -398,8 +299,6 @@ export function ConfigMenu({ onDone }) {
398
299
  return (_jsx(OpenRouterWizard, { existing: config.tools.openRouter, onDone: back, onCancel: back }));
399
300
  if (screen === 'tts')
400
301
  return (_jsx(TtsWizard, { existing: config.tools.textToSpeech, onDone: back, onCancel: back }));
401
- if (screen === 'ssh-host-wizard')
402
- return sshWorkspaceId ? (_jsx(SshHostWizard, { workspaceId: sshWorkspaceId, existing: sshHosts?.hosts.find((h) => h.alias === editingSshAlias) ?? null, onDone: back, onCancel: back })) : (_jsx(Box, { paddingX: 1, children: _jsx(Text, { color: "red", children: "\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u5DE5\u4F5C\u533A\uFF0C\u65E0\u6CD5\u914D\u7F6E SSH \u4E3B\u673A" }) }));
403
302
  if (screen === 'main')
404
303
  return (_jsx(MenuList, { title: "minimal-agent \u00B7 \u914D\u7F6E\u4E2D\u5FC3", items: mainItems, index: index }));
405
304
  if (screen === 'model-context')
@@ -409,22 +308,12 @@ export function ConfigMenu({ onDone }) {
409
308
  ? 'green'
410
309
  : 'red', children: message }))] }));
411
310
  if (screen === 'models')
412
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: "cyan", bold: true, children: "\u6A21\u578B\u7BA1\u7406" }), _jsx(Text, { color: "gray", children: "\u2605 \u57FA\u5EA7 \u00B7 \u2191\u2193 \u9009\u62E9 \u00B7 Enter \u64CD\u4F5C \u00B7 ESC \u8FD4\u56DE" }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [config.models.map((model, i) => (_jsxs(Text, { color: index === i ? 'cyan' : undefined, children: [index === i ? '> ' : ' ', model.id === config.baseModelId ? '★ ' : ' ', model.displayName ?? model.model, ' ', _jsxs(Text, { color: "gray", children: ["(", model.apiFormat, "/", model.model, !model.enabled
311
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: "cyan", bold: true, children: "\u6A21\u578B\u7BA1\u7406" }), _jsx(Text, { color: "gray", children: "\u2605 \u57FA\u5EA7 \u00B7 \u2191\u2193 \u9009\u62E9 \u00B7 Enter \u64CD\u4F5C \u00B7 ESC \u8FD4\u56DE" }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [config.models.map((model, i) => (_jsxs(Text, { color: index === i ? 'cyan' : undefined, children: [index === i ? '> ' : ' ', model.id === config.baseModelId ? '★ ' : ' ', model.displayName ?? model.model, ' ', _jsxs(Text, { color: "gray", children: ["(", model.apiFormat, "/", model.model, model.reasoningEffort ? ` · 推理${model.reasoningEffort}` : '', !model.enabled
413
312
  ? ' · 已停用'
414
313
  : !model.apiKey
415
314
  ? ' · 无密钥'
416
315
  : '', ")"] })] }, model.id))), _jsxs(Text, { color: index === config.models.length ? 'cyan' : undefined, children: [index === config.models.length ? '> ' : ' ', "\uFF0B \u6DFB\u52A0\u6A21\u578B"] })] }), message && _jsx(Text, { color: "yellow", children: message })] }));
417
316
  if (screen === 'delete-model')
418
317
  return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: "red", bold: true, children: "\u786E\u8BA4\u5220\u9664\u6A21\u578B" }), _jsxs(Text, { children: ["\u5C06\u5220\u9664\u201C", editing?.displayName ?? editing?.model, "\u201D\u3002\u8BE5\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002"] }), _jsx(Text, { color: "gray", children: "Enter \u786E\u8BA4\u5220\u9664 \u00B7 ESC \u53D6\u6D88" })] }));
419
- if (screen === 'ssh-hosts') {
420
- const hosts = sshHosts?.hosts ?? [];
421
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsxs(Text, { color: "cyan", bold: true, children: ["\u8FDC\u7A0B\u4E3B\u673A (SSH) \u00B7 \u5F53\u524D\u5DE5\u4F5C\u533A\uFF1A", getWorkingDir()] }), _jsx(Text, { color: "gray", children: "\u2606 \u9ED8\u8BA4\u4E3B\u673A \u00B7 \u2191\u2193 \u9009\u62E9 \u00B7 Enter \u64CD\u4F5C \u00B7 ESC \u8FD4\u56DE" }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [hosts.length === 0 && index === 0 && (_jsx(Text, { color: "gray", children: "\uFF08\u5F53\u524D\u5DE5\u4F5C\u533A\u5C1A\u672A\u914D\u7F6E\u4EFB\u4F55\u4E3B\u673A\uFF09" })), hosts.map((host, i) => (_jsxs(Text, { color: index === i ? 'cyan' : undefined, children: [index === i ? '> ' : ' ', host.alias === sshHosts?.defaultHostAlias ? '☆ ' : ' ', host.alias, ' ', _jsxs(Text, { color: "gray", children: ["(", host.username, "@", host.host, ":", host.port, " \u00B7 ", host.authMethod === 'password' ? '密码' : '密钥', " \u00B7 ", host.remoteShell, ")"] })] }, host.alias))), _jsxs(Text, { color: index === hosts.length ? 'cyan' : undefined, children: [index === hosts.length ? '> ' : ' ', "\uFF0B \u6DFB\u52A0\u4E3B\u673A"] })] }), message && _jsx(Text, { color: message.includes('正常') || message.includes('已') ? 'green' : 'red', children: message })] }));
422
- }
423
- if (screen === 'ssh-host-actions') {
424
- const host = sshHosts?.hosts.find((entry) => entry.alias === editingSshAlias);
425
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(MenuList, { title: host ? `${host.alias}(${host.username}@${host.host}:${host.port})` : 'SSH 主机操作', items: SSH_HOST_ACTIONS, index: index }), busy && _jsx(Text, { color: "yellow", children: "\u6B63\u5728\u5904\u7406..." }), message && (_jsx(Text, { color: message.includes('正常') || message.includes('已') ? 'green' : 'red', children: message }))] }));
426
- }
427
- if (screen === 'delete-ssh-host')
428
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: "red", bold: true, children: "\u786E\u8BA4\u5220\u9664 SSH \u4E3B\u673A" }), _jsxs(Text, { children: ["\u5C06\u5220\u9664\u4E3B\u673A\u201C", editingSshAlias, "\u201D\u3002\u8BE5\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002"] }), _jsx(Text, { color: "gray", children: "Enter \u786E\u8BA4\u5220\u9664 \u00B7 ESC \u53D6\u6D88" })] }));
429
318
  return (_jsxs(Box, { flexDirection: "column", children: [_jsx(MenuList, { title: editing?.displayName ?? editing?.model ?? '模型操作', items: MODEL_ACTIONS, index: index }), busy && _jsx(Text, { color: "yellow", children: "\u6B63\u5728\u5904\u7406..." }), message && (_jsx(Text, { color: message.includes('正常') || message.includes('已') ? 'green' : 'red', children: message }))] }));
430
319
  }
@@ -4,19 +4,10 @@ import React, { useCallback, useEffect, useState } from 'react';
4
4
  import {
5
5
  testAdvisorConnection,
6
6
  testConfiguredModel,
7
- testSshHostConnection,
8
7
  testToolConnection,
9
8
  } from '../config/configDiagnostics.js';
10
9
  import type { MinimalAgentConfig, ModelConfig } from '../config/configFile.js';
11
10
  import { configService } from '../config/configService.js';
12
- import { getWorkingDir } from '../bootstrap/workingDir.js';
13
- import {
14
- listSshHosts,
15
- removeSshHost,
16
- setDefaultSshHost,
17
- type SshHostsData,
18
- } from '../context/sshHosts.js';
19
- import { workspaceIndexService } from '../context/workspaceIndex.js';
20
11
  import { applyNetworkProxy } from '../network/proxy.js';
21
12
  import type { Provider } from '../types.js';
22
13
  import { AgentRuntimeWizard } from './wizards/agentRuntimeWizard.js';
@@ -26,7 +17,6 @@ import { ImageWizard } from './wizards/imageWizard.js';
26
17
  import { OpenRouterWizard } from './wizards/openRouterWizard.js';
27
18
  import { ProviderWizard } from './wizards/providerWizard.js';
28
19
  import { SearchWizard } from './wizards/searchWizard.js';
29
- import { SshHostWizard } from './wizards/sshHostWizard.js';
30
20
  import { TtsWizard } from './wizards/ttsWizard.js';
31
21
  import { NetworkProxyWizard } from './wizards/networkProxyWizard.js';
32
22
 
@@ -45,10 +35,6 @@ type Screen =
45
35
  | 'openrouter'
46
36
  | 'tts'
47
37
  | 'browser'
48
- | 'ssh-hosts'
49
- | 'ssh-host-actions'
50
- | 'delete-ssh-host'
51
- | 'ssh-host-wizard'
52
38
  | 'network-proxy';
53
39
 
54
40
  interface Props {
@@ -56,8 +42,7 @@ interface Props {
56
42
  onDone: (provider?: Provider) => void;
57
43
  }
58
44
 
59
- const MAIN = ['模型与上下文', '工具与代理', '远程主机 (SSH)', '网络与 VPN 代理', '浏览器', '完成并返回聊天'];
60
- const SSH_HOST_ACTIONS = ['编辑', '测试连接', '设为默认主机', '删除', '返回'];
45
+ const MAIN = ['模型与上下文', '工具与代理', '网络与 VPN 代理', '浏览器', '完成并返回聊天'];
61
46
  const MODEL_CONTEXT = ['模型管理', '压缩与顾问', '测试顾问连接', '返回'];
62
47
  const TOOLS = [
63
48
  '电脑操作与子代理',
@@ -114,33 +99,15 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
114
99
  const [editingModelId, setEditingModelId] = useState<string | null>(null);
115
100
  const [message, setMessage] = useState<string | null>(null);
116
101
  const [busy, setBusy] = useState(false);
117
- const [sshWorkspaceId, setSshWorkspaceId] = useState<string | null>(null);
118
- const [sshHosts, setSshHosts] = useState<SshHostsData | null>(null);
119
- const [editingSshAlias, setEditingSshAlias] = useState<string | null>(null);
120
102
  const refresh = useCallback(async () => {
121
103
  const snapshot = await configService.reload();
122
104
  applyNetworkProxy(snapshot.config?.networkProxy);
123
105
  setConfig(snapshot.config);
124
106
  setReadOnly(snapshot.readOnly);
125
107
  }, []);
126
- const refreshSshHosts = useCallback(async (workspaceId: string) => {
127
- setSshHosts(await listSshHosts(workspaceId));
128
- }, []);
129
108
  useEffect(() => {
130
109
  void refresh();
131
- // SSH 主机按工作区隔离;这里复用与主循环相同的解析函数(workspaceIndexService),
132
- // 保证 /config 里编辑的主机和 SSH 工具运行时读到的是同一份数据。找不到(从未
133
- // 注册过)就注册一次——能走到这里说明用户已经在一个真实交互会话里,不是
134
- // e2e/cron 那类刻意排除的临时目录。
135
- void (async () => {
136
- const cwd = getWorkingDir();
137
- const found = await workspaceIndexService.findByPath(cwd).catch(() => null);
138
- const workspace = found ?? await workspaceIndexService.register(cwd).catch(() => null);
139
- if (!workspace) return;
140
- setSshWorkspaceId(workspace.id);
141
- await refreshSshHosts(workspace.id);
142
- })();
143
- }, [refresh, refreshSshHosts]);
110
+ }, [refresh]);
144
111
  const navigate = (next: Screen) => {
145
112
  setIndex(0);
146
113
  setMessage(null);
@@ -167,31 +134,6 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
167
134
  },
168
135
  [refresh],
169
136
  );
170
- const mutateSsh = useCallback(
171
- async (
172
- operation: () => Promise<unknown>,
173
- success: string,
174
- ): Promise<boolean> => {
175
- if (!sshWorkspaceId) {
176
- setMessage('无法确定当前工作区');
177
- return false;
178
- }
179
- setBusy(true);
180
- setMessage(null);
181
- try {
182
- await operation();
183
- await refreshSshHosts(sshWorkspaceId);
184
- setMessage(success);
185
- return true;
186
- } catch (error) {
187
- setMessage((error as Error).message || '操作失败');
188
- return false;
189
- } finally {
190
- setBusy(false);
191
- }
192
- },
193
- [sshWorkspaceId, refreshSshHosts],
194
- );
195
137
 
196
138
  useInput((input, key) => {
197
139
  if (
@@ -206,7 +148,6 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
206
148
  'tts',
207
149
  'network-proxy',
208
150
  'browser',
209
- 'ssh-host-wizard',
210
151
  ].includes(screen)
211
152
  )
212
153
  return;
@@ -217,9 +158,6 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
217
158
  else if (screen === 'model-actions' || screen === 'delete-model')
218
159
  navigate('models');
219
160
  else if (screen === 'tools') navigate('main');
220
- else if (screen === 'ssh-hosts') navigate('main');
221
- else if (screen === 'ssh-host-actions' || screen === 'delete-ssh-host')
222
- navigate('ssh-hosts');
223
161
  };
224
162
  if (key.escape || (key.ctrl && input === 'c')) {
225
163
  back();
@@ -236,11 +174,7 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
236
174
  ? MODEL_ACTIONS.length
237
175
  : screen === 'models'
238
176
  ? (config?.models.length ?? 0) + 1
239
- : screen === 'ssh-hosts'
240
- ? (sshHosts?.hosts.length ?? 0) + 1
241
- : screen === 'ssh-host-actions'
242
- ? SSH_HOST_ACTIONS.length
243
- : 1;
177
+ : 1;
244
178
  if (key.upArrow) {
245
179
  setIndex((value) => (value - 1 + length) % length);
246
180
  return;
@@ -262,16 +196,6 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
262
196
  });
263
197
  return;
264
198
  }
265
- if (screen === 'delete-ssh-host') {
266
- if (key.return && sshWorkspaceId && editingSshAlias)
267
- void mutateSsh(
268
- () => removeSshHost(sshWorkspaceId, editingSshAlias),
269
- '主机已删除',
270
- ).then((ok) => {
271
- if (ok) navigate('ssh-hosts');
272
- });
273
- return;
274
- }
275
199
  if (
276
200
  screen === 'tools' &&
277
201
  (input === 'd' || input === 't') &&
@@ -297,35 +221,9 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
297
221
  if (screen === 'main') {
298
222
  if (index === 0) navigate('model-context');
299
223
  else if (index === 1) navigate('tools');
300
- else if (index === 2) navigate('ssh-hosts');
301
- else if (index === 3) navigate('network-proxy');
302
- else if (index === 4) navigate('browser');
224
+ else if (index === 2) navigate('network-proxy');
225
+ else if (index === 3) navigate('browser');
303
226
  else onDone();
304
- } else if (screen === 'ssh-hosts') {
305
- const hosts = sshHosts?.hosts ?? [];
306
- if (index === hosts.length) {
307
- setEditingSshAlias(null);
308
- navigate('ssh-host-wizard');
309
- } else {
310
- setEditingSshAlias(hosts[index]?.alias ?? null);
311
- navigate('ssh-host-actions');
312
- }
313
- } else if (screen === 'ssh-host-actions' && sshWorkspaceId && editingSshAlias) {
314
- const host = sshHosts?.hosts.find((entry) => entry.alias === editingSshAlias);
315
- if (!host) return;
316
- if (index === 0) navigate('ssh-host-wizard');
317
- else if (index === 1)
318
- void mutateSsh(async () => {
319
- const result = await testSshHostConnection(sshWorkspaceId, editingSshAlias);
320
- if (!result.ok) throw new Error(result.message);
321
- }, '连接正常');
322
- else if (index === 2)
323
- void mutateSsh(
324
- () => setDefaultSshHost(sshWorkspaceId, editingSshAlias),
325
- '已设为默认主机',
326
- );
327
- else if (index === 3) navigate('delete-ssh-host');
328
- else navigate('ssh-hosts');
329
227
  } else if (screen === 'model-context') {
330
228
  if (index === 0) navigate('models');
331
229
  else if (index === 1) navigate('context-advisor');
@@ -418,22 +316,19 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
418
316
  ? 'models'
419
317
  : screen === 'context-advisor'
420
318
  ? 'model-context'
421
- : screen === 'ssh-host-wizard'
422
- ? 'ssh-hosts'
423
- : screen === 'browser' ||
424
- screen === 'tools' ||
425
- screen === 'network-proxy'
426
- ? 'main'
427
- : screen === 'agent-runtime' ||
428
- screen === 'search' ||
429
- screen === 'image' ||
430
- screen === 'openrouter' ||
431
- screen === 'tts'
432
- ? 'tools'
433
- : 'main',
319
+ : screen === 'browser' ||
320
+ screen === 'tools' ||
321
+ screen === 'network-proxy'
322
+ ? 'main'
323
+ : screen === 'agent-runtime' ||
324
+ screen === 'search' ||
325
+ screen === 'image' ||
326
+ screen === 'openrouter' ||
327
+ screen === 'tts'
328
+ ? 'tools'
329
+ : 'main',
434
330
  );
435
331
  void refresh();
436
- if (screen === 'ssh-host-wizard' && sshWorkspaceId) void refreshSshHosts(sshWorkspaceId);
437
332
  };
438
333
  const editing = editingModelId
439
334
  ? config.models.find((model) => model.id === editingModelId)
@@ -446,7 +341,6 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
446
341
  const mainItems = [
447
342
  `模型与上下文(${usableModels}/${config.models.length} 个模型可用)`,
448
343
  `工具与代理(${configuredTools}/4 项已配置${draftTools ? ` · ${draftTools} 项草稿` : ''})`,
449
- `远程主机 (SSH)(当前工作区 ${sshHosts?.hosts.length ?? 0} 个${sshHosts?.defaultHostAlias ? ` · 默认 ${sshHosts.defaultHostAlias}` : ''})`,
450
344
  `网络与 VPN 代理(${config.networkProxy?.enabled ? `已启用 · ${config.networkProxy.host}:${config.networkProxy.port}` : '已关闭'})`,
451
345
  `浏览器(${config.browser?.enabled !== false ? '已启用' : '已关闭'})`,
452
346
  '完成并返回聊天',
@@ -531,19 +425,6 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
531
425
  onCancel={back}
532
426
  />
533
427
  );
534
- if (screen === 'ssh-host-wizard')
535
- return sshWorkspaceId ? (
536
- <SshHostWizard
537
- workspaceId={sshWorkspaceId}
538
- existing={sshHosts?.hosts.find((h) => h.alias === editingSshAlias) ?? null}
539
- onDone={back}
540
- onCancel={back}
541
- />
542
- ) : (
543
- <Box paddingX={1}>
544
- <Text color="red">无法确定当前工作区,无法配置 SSH 主机</Text>
545
- </Box>
546
- );
547
428
  if (screen === 'main')
548
429
  return (
549
430
  <MenuList
@@ -600,6 +481,7 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
600
481
  {model.displayName ?? model.model}{' '}
601
482
  <Text color="gray">
602
483
  ({model.apiFormat}/{model.model}
484
+ {model.reasoningEffort ? ` · 推理${model.reasoningEffort}` : ''}
603
485
  {!model.enabled
604
486
  ? ' · 已停用'
605
487
  : !model.apiKey
@@ -628,64 +510,6 @@ export function ConfigMenu({ onDone }: Props): JSX.Element {
628
510
  <Text color="gray">Enter 确认删除 · ESC 取消</Text>
629
511
  </Box>
630
512
  );
631
- if (screen === 'ssh-hosts') {
632
- const hosts = sshHosts?.hosts ?? [];
633
- return (
634
- <Box flexDirection="column" paddingX={1}>
635
- <Text color="cyan" bold>
636
- 远程主机 (SSH) · 当前工作区:{getWorkingDir()}
637
- </Text>
638
- <Text color="gray">☆ 默认主机 · ↑↓ 选择 · Enter 操作 · ESC 返回</Text>
639
- <Box flexDirection="column" marginTop={1}>
640
- {hosts.length === 0 && index === 0 && (
641
- <Text color="gray">(当前工作区尚未配置任何主机)</Text>
642
- )}
643
- {hosts.map((host, i) => (
644
- <Text key={host.alias} color={index === i ? 'cyan' : undefined}>
645
- {index === i ? '> ' : ' '}
646
- {host.alias === sshHosts?.defaultHostAlias ? '☆ ' : ' '}
647
- {host.alias}{' '}
648
- <Text color="gray">
649
- ({host.username}@{host.host}:{host.port} · {host.authMethod === 'password' ? '密码' : '密钥'} · {host.remoteShell})
650
- </Text>
651
- </Text>
652
- ))}
653
- <Text color={index === hosts.length ? 'cyan' : undefined}>
654
- {index === hosts.length ? '> ' : ' '}+ 添加主机
655
- </Text>
656
- </Box>
657
- {message && <Text color={message.includes('正常') || message.includes('已') ? 'green' : 'red'}>{message}</Text>}
658
- </Box>
659
- );
660
- }
661
- if (screen === 'ssh-host-actions') {
662
- const host = sshHosts?.hosts.find((entry) => entry.alias === editingSshAlias);
663
- return (
664
- <Box flexDirection="column">
665
- <MenuList
666
- title={host ? `${host.alias}(${host.username}@${host.host}:${host.port})` : 'SSH 主机操作'}
667
- items={SSH_HOST_ACTIONS}
668
- index={index}
669
- />
670
- {busy && <Text color="yellow">正在处理...</Text>}
671
- {message && (
672
- <Text color={message.includes('正常') || message.includes('已') ? 'green' : 'red'}>
673
- {message}
674
- </Text>
675
- )}
676
- </Box>
677
- );
678
- }
679
- if (screen === 'delete-ssh-host')
680
- return (
681
- <Box flexDirection="column" paddingX={1}>
682
- <Text color="red" bold>
683
- 确认删除 SSH 主机
684
- </Text>
685
- <Text>将删除主机“{editingSshAlias}”。该操作不可撤销。</Text>
686
- <Text color="gray">Enter 确认删除 · ESC 取消</Text>
687
- </Box>
688
- );
689
513
  return (
690
514
  <Box flexDirection="column">
691
515
  <MenuList
package/src/cli/print.js CHANGED
@@ -22,6 +22,7 @@
22
22
  * minimal-agent -p --output-format json "提示" # 结构化结局
23
23
  * ============================================================
24
24
  */
25
+ import { previousUserRequest } from '../memory/recallQuery.js';
25
26
  import { getContextPath, saveContext } from '../context/persistContext.js';
26
27
  import { updateSessionMetadata } from '../context/sessionRegistry.js';
27
28
  import { workspaceIndexService } from '../context/workspaceIndex.js';
@@ -328,6 +329,7 @@ export async function runPrintMode(provider, args, initialHistory, options) {
328
329
  memoryAccess: runtimeContext.memoryAccess,
329
330
  browser: browserPromptRuntime(runtimeContext.browser, provider.supportsVision),
330
331
  currentUserInput: prompt,
332
+ previousUserInput: previousUserRequest(history),
331
333
  contextWindow: provider.contextWindow,
332
334
  },
333
335
  }).catch(() => undefined);
package/src/cli/print.ts CHANGED
@@ -23,6 +23,7 @@
23
23
  * ============================================================
24
24
  */
25
25
 
26
+ import { previousUserRequest } from '../memory/recallQuery.js';
26
27
  import { getContextPath, saveContext } from '../context/persistContext.js';
27
28
  import { updateSessionMetadata } from '../context/sessionRegistry.js';
28
29
  import { workspaceIndexService } from '../context/workspaceIndex.js';
@@ -473,6 +474,7 @@ export async function runPrintMode(
473
474
  memoryAccess: runtimeContext.memoryAccess,
474
475
  browser: browserPromptRuntime(runtimeContext.browser, provider.supportsVision),
475
476
  currentUserInput: prompt,
477
+ previousUserInput: previousUserRequest(history),
476
478
  contextWindow: provider.contextWindow,
477
479
  },
478
480
  }).catch(() => undefined);
@@ -18,6 +18,7 @@
18
18
  * - 跑完 / 中断 / 出错都先 saveContext 再退出(finalize 还会 unref stdin,让本轮结束后进程能退出)。
19
19
  * ============================================================
20
20
  */
21
+ import { previousUserRequest } from '../memory/recallQuery.js';
21
22
  import crypto from 'node:crypto';
22
23
  import { getWorkingDir } from '../bootstrap/workingDir.js';
23
24
  import { forceCompact } from '../context/compact.js';
@@ -351,6 +352,7 @@ export async function runStreamJsonMode(provider, args, initialHistory, options)
351
352
  memoryAccess: runtimeContext.memoryAccess,
352
353
  browser: browserPromptRuntime(runtimeContext.browser, provider.supportsVision),
353
354
  currentUserInput: prompt,
355
+ previousUserInput: previousUserRequest(history),
354
356
  contextWindow: provider.contextWindow,
355
357
  },
356
358
  }).catch(() => undefined);
@@ -19,6 +19,7 @@
19
19
  * ============================================================
20
20
  */
21
21
 
22
+ import { previousUserRequest } from '../memory/recallQuery.js';
22
23
  import crypto from 'node:crypto';
23
24
  import { getWorkingDir } from '../bootstrap/workingDir.js';
24
25
  import { forceCompact } from '../context/compact.js';
@@ -458,6 +459,7 @@ export async function runStreamJsonMode(
458
459
  memoryAccess: runtimeContext.memoryAccess,
459
460
  browser: browserPromptRuntime(runtimeContext.browser, provider.supportsVision),
460
461
  currentUserInput: prompt,
462
+ previousUserInput: previousUserRequest(history),
461
463
  contextWindow: provider.contextWindow,
462
464
  },
463
465
  }).catch(() => undefined);