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
@@ -6,7 +6,12 @@ import { normalizePromptCacheMode } from '../llm/promptCache.js';
6
6
  import { execSiblingDir } from '../utils/greenRoot.js';
7
7
  import { DEFAULT_ADVISOR_CONFIG, DEFAULT_BROWSER_CONFIG, DEFAULT_COMPUTER_USE_CONFIG, DEFAULT_NETWORK_PROXY_CONFIG, DEFAULT_SUBAGENTS_CONFIG, } from './configDefaults.js';
8
8
  import { CONFIG_LIMITS, normalizeLimited } from './numericLimits.js';
9
+ import { normalizeReasoningEffort } from './reasoningEffort.js';
9
10
  export { DEFAULT_ADVISOR_CONFIG, DEFAULT_BROWSER_CONFIG, DEFAULT_COMPUTER_USE_CONFIG, DEFAULT_NETWORK_PROXY_CONFIG, DEFAULT_SUBAGENTS_CONFIG, } from './configDefaults.js';
11
+ export { REASONING_EFFORTS, normalizeReasoningEffort } from './reasoningEffort.js';
12
+ export function normalizeApiFormat(value) {
13
+ return value === 'anthropic' || value === 'openai-responses' ? value : 'openai';
14
+ }
10
15
  export const DEFAULT_CONTEXT_WINDOW = 128_000;
11
16
  const LOCK_STALE_MS = 120_000;
12
17
  const LOCK_WAIT_MS = 15_000;
@@ -64,7 +69,7 @@ function parseModel(value) {
64
69
  const now = Date.now();
65
70
  return {
66
71
  id,
67
- apiFormat: raw.apiFormat === 'anthropic' ? 'anthropic' : 'openai',
72
+ apiFormat: normalizeApiFormat(raw.apiFormat),
68
73
  ...(displayName ? { displayName } : {}),
69
74
  baseURL,
70
75
  apiKey,
@@ -76,6 +81,9 @@ function parseModel(value) {
76
81
  ? { supportsJsonSchema: raw.supportsJsonSchema }
77
82
  : {}),
78
83
  ...(typeof raw.reasoning === 'boolean' ? { reasoning: raw.reasoning } : {}),
84
+ ...(normalizeReasoningEffort(raw.reasoningEffort)
85
+ ? { reasoningEffort: normalizeReasoningEffort(raw.reasoningEffort) }
86
+ : {}),
79
87
  promptCache: {
80
88
  mode: normalizePromptCacheMode(raw.promptCache?.mode
81
89
  ?? defaultPromptCacheMode(baseURL)),
@@ -150,9 +158,9 @@ function parseAdvisor(value) {
150
158
  // 默认关闭;旧配置显式 enabled:true 仍保持用户选择。
151
159
  enabled: raw.enabled === true,
152
160
  mode: raw.mode === 'manual' ? 'manual' : 'auto',
161
+ enforcement: raw.enforcement === 'followup' ? 'followup' : 'remind',
153
162
  modelId: nonEmpty(raw.modelId) ?? null,
154
163
  contextPolicy: 'full-relevant',
155
- shareSensitive: raw.shareSensitive === true,
156
164
  inputTokenBudget: normalizeLimited(raw.inputTokenBudget, CONFIG_LIMITS.advisorInputTokenBudget, DEFAULT_ADVISOR_CONFIG.inputTokenBudget),
157
165
  timeoutMs: normalizeLimited(raw.timeoutMs, CONFIG_LIMITS.advisorTimeoutMs, DEFAULT_ADVISOR_CONFIG.timeoutMs),
158
166
  };
@@ -262,7 +270,6 @@ function parseLegacyAdvisorConfig(value) {
262
270
  mode: raw.mode === 'manual' ? 'manual' : 'auto',
263
271
  provider,
264
272
  supportsVision: raw.supportsVision === true,
265
- shareSensitive: raw.shareSensitive === true,
266
273
  inputTokenBudget: normalizeLimited(raw.inputTokenBudget, CONFIG_LIMITS.advisorInputTokenBudget, DEFAULT_ADVISOR_CONFIG.inputTokenBudget),
267
274
  timeoutMs: normalizeLimited(raw.timeoutMs, CONFIG_LIMITS.advisorTimeoutMs, DEFAULT_ADVISOR_CONFIG.timeoutMs),
268
275
  };
@@ -319,9 +326,9 @@ function buildV4FromLegacy(legacyProvider, legacyAdvisor, subagents, tools, save
319
326
  advisor = {
320
327
  enabled: legacyAdvisor.enabled,
321
328
  mode: legacyAdvisor.mode,
329
+ enforcement: 'remind',
322
330
  modelId: advisorModelId,
323
331
  contextPolicy: 'full-relevant',
324
- shareSensitive: legacyAdvisor.shareSensitive,
325
332
  inputTokenBudget: legacyAdvisor.inputTokenBudget,
326
333
  timeoutMs: legacyAdvisor.timeoutMs,
327
334
  };
@@ -14,6 +14,7 @@ import {
14
14
  DEFAULT_SUBAGENTS_CONFIG,
15
15
  } from './configDefaults.js';
16
16
  import { CONFIG_LIMITS, normalizeLimited } from './numericLimits.js';
17
+ import { type ReasoningEffort, normalizeReasoningEffort } from './reasoningEffort.js';
17
18
 
18
19
  export {
19
20
  DEFAULT_ADVISOR_CONFIG,
@@ -24,7 +25,19 @@ export {
24
25
  } from './configDefaults.js';
25
26
 
26
27
  export type ImageProtocol = 'auto' | 'chat' | 'images';
27
- export type ApiFormat = 'openai' | 'anthropic';
28
+ /**
29
+ * openai Chat Completions(DeepSeek/月之暗面/智谱/火山/MiniMax/OpenRouter 都按它兼容)
30
+ * openai-responses OpenAI Responses API(/v1/responses,OpenAI 当前主推接口)
31
+ * anthropic Anthropic Messages API
32
+ */
33
+ export type ApiFormat = 'openai' | 'openai-responses' | 'anthropic';
34
+
35
+ export { REASONING_EFFORTS, normalizeReasoningEffort, type ReasoningEffort } from './reasoningEffort.js';
36
+
37
+ export function normalizeApiFormat(value: unknown): ApiFormat {
38
+ return value === 'anthropic' || value === 'openai-responses' ? value : 'openai';
39
+ }
40
+
28
41
 
29
42
  /** 一个模型条目——「模型列表」的元素,基座模型和顾问模型都从这里引用。 */
30
43
  export interface ModelConfig {
@@ -42,6 +55,8 @@ export interface ModelConfig {
42
55
  supportsJsonSchema?: boolean;
43
56
  /** Request provider reasoning when supported (serialized as reasoning.enabled). */
44
57
  reasoning?: boolean;
58
+ /** 推理强度;缺省 = 不发送,沿用服务端对该模型的默认强度。 */
59
+ reasoningEffort?: ReasoningEffort;
45
60
  promptCache?: PromptCacheConfig;
46
61
  enabled: boolean;
47
62
  createdAt: number;
@@ -54,10 +69,14 @@ export type WorkflowModelRoles = Record<string, string>;
54
69
  export interface AdvisorConfig {
55
70
  enabled: boolean;
56
71
  mode: 'auto' | 'manual';
72
+ /**
73
+ * remind = 只提醒(默认);followup = 实质回答发布后仍未复核时,再发一次请求让模型自己补问顾问。
74
+ * 给不会主动咨询的弱基座模型用;回答不撤回,Advisor 仍由模型自己调用。
75
+ */
76
+ enforcement: 'remind' | 'followup';
57
77
  /** 引用 models[].id;null = 未选。顾问不再自带一整套独立 provider 字段。 */
58
78
  modelId: string | null;
59
79
  contextPolicy: 'full-relevant';
60
- shareSensitive: boolean;
61
80
  /** 顾问单次输入预算。独立于模型声明的上下文窗口,避免百万窗口吞入整段历史。 */
62
81
  inputTokenBudget: number;
63
82
  timeoutMs: number;
@@ -206,7 +225,7 @@ function parseModel(value: unknown): ModelConfig | null {
206
225
  const now = Date.now();
207
226
  return {
208
227
  id,
209
- apiFormat: raw.apiFormat === 'anthropic' ? 'anthropic' : 'openai',
228
+ apiFormat: normalizeApiFormat(raw.apiFormat),
210
229
  ...(displayName ? { displayName } : {}),
211
230
  baseURL,
212
231
  apiKey,
@@ -218,6 +237,9 @@ function parseModel(value: unknown): ModelConfig | null {
218
237
  ? { supportsJsonSchema: raw.supportsJsonSchema }
219
238
  : {}),
220
239
  ...(typeof raw.reasoning === 'boolean' ? { reasoning: raw.reasoning } : {}),
240
+ ...(normalizeReasoningEffort(raw.reasoningEffort)
241
+ ? { reasoningEffort: normalizeReasoningEffort(raw.reasoningEffort) }
242
+ : {}),
221
243
  promptCache: {
222
244
  mode: normalizePromptCacheMode(
223
245
  (raw.promptCache as Record<string, unknown> | undefined)?.mode
@@ -291,9 +313,9 @@ function parseAdvisor(value: unknown): AdvisorConfig | undefined {
291
313
  // 默认关闭;旧配置显式 enabled:true 仍保持用户选择。
292
314
  enabled: raw.enabled === true,
293
315
  mode: raw.mode === 'manual' ? 'manual' : 'auto',
316
+ enforcement: raw.enforcement === 'followup' ? 'followup' : 'remind',
294
317
  modelId: nonEmpty(raw.modelId) ?? null,
295
318
  contextPolicy: 'full-relevant',
296
- shareSensitive: raw.shareSensitive === true,
297
319
  inputTokenBudget: normalizeLimited(raw.inputTokenBudget, CONFIG_LIMITS.advisorInputTokenBudget, DEFAULT_ADVISOR_CONFIG.inputTokenBudget),
298
320
  timeoutMs: normalizeLimited(raw.timeoutMs, CONFIG_LIMITS.advisorTimeoutMs, DEFAULT_ADVISOR_CONFIG.timeoutMs),
299
321
  };
@@ -394,7 +416,6 @@ interface LegacyAdvisorConfig {
394
416
  mode: 'auto' | 'manual';
395
417
  provider: LegacyProviderConfig;
396
418
  supportsVision: boolean;
397
- shareSensitive: boolean;
398
419
  inputTokenBudget: number;
399
420
  timeoutMs: number;
400
421
  }
@@ -428,7 +449,6 @@ function parseLegacyAdvisorConfig(value: unknown): LegacyAdvisorConfig | undefin
428
449
  mode: raw.mode === 'manual' ? 'manual' : 'auto',
429
450
  provider,
430
451
  supportsVision: raw.supportsVision === true,
431
- shareSensitive: raw.shareSensitive === true,
432
452
  inputTokenBudget: normalizeLimited(raw.inputTokenBudget, CONFIG_LIMITS.advisorInputTokenBudget, DEFAULT_ADVISOR_CONFIG.inputTokenBudget),
433
453
  timeoutMs: normalizeLimited(raw.timeoutMs, CONFIG_LIMITS.advisorTimeoutMs, DEFAULT_ADVISOR_CONFIG.timeoutMs),
434
454
  };
@@ -490,9 +510,9 @@ function buildV4FromLegacy(
490
510
  advisor = {
491
511
  enabled: legacyAdvisor.enabled,
492
512
  mode: legacyAdvisor.mode,
513
+ enforcement: 'remind',
493
514
  modelId: advisorModelId,
494
515
  contextPolicy: 'full-relevant',
495
- shareSensitive: legacyAdvisor.shareSensitive,
496
516
  inputTokenBudget: legacyAdvisor.inputTokenBudget,
497
517
  timeoutMs: legacyAdvisor.timeoutMs,
498
518
  };
@@ -33,6 +33,7 @@ export function buildConfigManagementView(snapshot) {
33
33
  contextWindow: model.contextWindow,
34
34
  ...(model.maxTokens ? { maxTokens: model.maxTokens } : {}),
35
35
  ...(model.supportsVision ? { supportsVision: true } : {}),
36
+ ...(model.reasoningEffort ? { reasoningEffort: model.reasoningEffort } : {}),
36
37
  promptCache: model.promptCache ?? { mode: 'auto' },
37
38
  enabled: model.enabled,
38
39
  updatedAt: model.updatedAt,
@@ -23,6 +23,7 @@ export interface ManagedModelView {
23
23
  contextWindow: number;
24
24
  maxTokens?: number;
25
25
  supportsVision?: boolean;
26
+ reasoningEffort?: ModelConfig['reasoningEffort'];
26
27
  promptCache: NonNullable<ModelConfig['promptCache']>;
27
28
  enabled: boolean;
28
29
  updatedAt: number;
@@ -63,6 +64,7 @@ export function buildConfigManagementView(snapshot: ConfigSnapshot) {
63
64
  contextWindow: model.contextWindow,
64
65
  ...(model.maxTokens ? { maxTokens: model.maxTokens } : {}),
65
66
  ...(model.supportsVision ? { supportsVision: true } : {}),
67
+ ...(model.reasoningEffort ? { reasoningEffort: model.reasoningEffort } : {}),
66
68
  promptCache: model.promptCache ?? { mode: 'auto' },
67
69
  enabled: model.enabled,
68
70
  updatedAt: model.updatedAt,
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { modelRefFromLegacy, } from '../modelRef.js';
3
3
  import { fetchOnlineModels, loadTeamCredentials, resolveOnlineModel, } from '../team/auth.js';
4
- import { DEFAULT_ADVISOR_CONFIG, DEFAULT_BROWSER_CONFIG, DEFAULT_COMPUTER_USE_CONFIG, DEFAULT_NETWORK_PROXY_CONFIG, DEFAULT_SUBAGENTS_CONFIG, defaultMaxTokens, readMinimalAgentConfig, updateMinimalAgentConfig, } from './configFile.js';
4
+ import { normalizeApiFormat, normalizeReasoningEffort, DEFAULT_ADVISOR_CONFIG, DEFAULT_BROWSER_CONFIG, DEFAULT_COMPUTER_USE_CONFIG, DEFAULT_NETWORK_PROXY_CONFIG, DEFAULT_SUBAGENTS_CONFIG, defaultMaxTokens, readMinimalAgentConfig, updateMinimalAgentConfig, } from './configFile.js';
5
5
  import { CONFIG_LIMITS, normalizeLimited } from './numericLimits.js';
6
6
  import { applyNetworkProxy } from '../network/proxy.js';
7
7
  const DEFAULT_CONTEXT_WINDOW = 128_000;
@@ -169,6 +169,7 @@ function resolveProvider(entry, compactRatio) {
169
169
  ? { supportsJsonSchema: entry.supportsJsonSchema }
170
170
  : {}),
171
171
  ...(entry.reasoning === true ? { reasoning: true } : {}),
172
+ ...(entry.reasoningEffort ? { reasoningEffort: entry.reasoningEffort } : {}),
172
173
  promptCache: entry.promptCache ?? { mode: 'auto' },
173
174
  ...(compactRatio !== undefined ? { compactRatio } : {}),
174
175
  // cache_edits 式回收:仅 Anthropic 格式 + 显式 env 开启时生效(非公开协议,
@@ -208,7 +209,7 @@ function applyModelsPatch(models, ops) {
208
209
  for (const item of ops) {
209
210
  if (item.op === 'add') {
210
211
  const m = item.model;
211
- const apiFormat = m.apiFormat === 'anthropic' ? 'anthropic' : 'openai';
212
+ const apiFormat = normalizeApiFormat(m.apiFormat);
212
213
  const baseURL = m.baseURL?.trim();
213
214
  const modelId = m.model?.trim();
214
215
  const apiKey = m.apiKey?.trim();
@@ -230,6 +231,9 @@ function applyModelsPatch(models, ops) {
230
231
  ? { supportsJsonSchema: m.supportsJsonSchema }
231
232
  : {}),
232
233
  ...(m.reasoning !== undefined ? { reasoning: m.reasoning === true } : {}),
234
+ ...(normalizeReasoningEffort(m.reasoningEffort)
235
+ ? { reasoningEffort: normalizeReasoningEffort(m.reasoningEffort) }
236
+ : {}),
233
237
  promptCache: m.promptCache ?? { mode: 'auto' },
234
238
  enabled: m.enabled ?? true,
235
239
  createdAt: now,
@@ -244,11 +248,7 @@ function applyModelsPatch(models, ops) {
244
248
  const patch = item.patch;
245
249
  const next = {
246
250
  ...existing,
247
- ...(patch.apiFormat
248
- ? {
249
- apiFormat: patch.apiFormat === 'anthropic' ? 'anthropic' : 'openai',
250
- }
251
- : {}),
251
+ ...(patch.apiFormat ? { apiFormat: normalizeApiFormat(patch.apiFormat) } : {}),
252
252
  ...(patch.displayName !== undefined
253
253
  ? { displayName: patch.displayName.trim() || undefined }
254
254
  : {}),
@@ -267,6 +267,10 @@ function applyModelsPatch(models, ops) {
267
267
  ...(patch.reasoning !== undefined
268
268
  ? { reasoning: patch.reasoning === true }
269
269
  : {}),
270
+ // null = 清除(回到服务端默认强度);未提供 = 不动。
271
+ ...(patch.reasoningEffort !== undefined
272
+ ? { reasoningEffort: normalizeReasoningEffort(patch.reasoningEffort) }
273
+ : {}),
270
274
  ...(patch.promptCache ? { promptCache: patch.promptCache } : {}),
271
275
  ...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
272
276
  updatedAt: now,
@@ -332,11 +336,11 @@ function applyConfigPatch(base, patch) {
332
336
  advisor = {
333
337
  enabled: patch.advisor.enabled ?? advisor?.enabled ?? DEFAULT_ADVISOR_CONFIG.enabled,
334
338
  mode: patch.advisor.mode ?? advisor?.mode ?? 'auto',
339
+ enforcement: patch.advisor.enforcement ?? advisor?.enforcement ?? 'remind',
335
340
  modelId: patch.advisor.modelId !== undefined
336
341
  ? patch.advisor.modelId
337
342
  : (advisor?.modelId ?? null),
338
343
  contextPolicy: 'full-relevant',
339
- shareSensitive: patch.advisor.shareSensitive ?? advisor?.shareSensitive ?? false,
340
344
  inputTokenBudget: normalizeLimited(patch.advisor.inputTokenBudget ?? advisor?.inputTokenBudget, CONFIG_LIMITS.advisorInputTokenBudget, DEFAULT_ADVISOR_CONFIG.inputTokenBudget),
341
345
  timeoutMs: normalizeLimited(patch.advisor.timeoutMs ?? advisor?.timeoutMs, CONFIG_LIMITS.advisorTimeoutMs, DEFAULT_ADVISOR_CONFIG.timeoutMs),
342
346
  };
@@ -16,6 +16,8 @@ import type { GatewayProvider, LocalProvider, Provider } from '../types.js';
16
16
  import {
17
17
  type AdvisorConfig,
18
18
  type ApiFormat,
19
+ normalizeApiFormat,
20
+ normalizeReasoningEffort,
19
21
  type BrowserConfig,
20
22
  type ComputerUseConfig,
21
23
  DEFAULT_ADVISOR_CONFIG,
@@ -66,8 +68,10 @@ export type ModelOp =
66
68
  | {
67
69
  op: 'update';
68
70
  id: string;
69
- patch: Partial<Omit<ModelConfig, 'id' | 'createdAt' | 'updatedAt'>> & {
71
+ patch: Partial<Omit<ModelConfig, 'id' | 'createdAt' | 'updatedAt' | 'reasoningEffort'>> & {
70
72
  clearApiKey?: boolean;
73
+ /** null = 清除,回到服务端对该模型的默认强度。 */
74
+ reasoningEffort?: ModelConfig['reasoningEffort'] | null;
71
75
  };
72
76
  }
73
77
  | { op: 'remove'; id: string };
@@ -311,6 +315,7 @@ function resolveProvider(
311
315
  ? { supportsJsonSchema: entry.supportsJsonSchema }
312
316
  : {}),
313
317
  ...(entry.reasoning === true ? { reasoning: true } : {}),
318
+ ...(entry.reasoningEffort ? { reasoningEffort: entry.reasoningEffort } : {}),
314
319
  promptCache: entry.promptCache ?? { mode: 'auto' },
315
320
  ...(compactRatio !== undefined ? { compactRatio } : {}),
316
321
  // cache_edits 式回收:仅 Anthropic 格式 + 显式 env 开启时生效(非公开协议,
@@ -364,8 +369,7 @@ function applyModelsPatch(
364
369
  for (const item of ops) {
365
370
  if (item.op === 'add') {
366
371
  const m = item.model;
367
- const apiFormat: ApiFormat =
368
- m.apiFormat === 'anthropic' ? 'anthropic' : 'openai';
372
+ const apiFormat: ApiFormat = normalizeApiFormat(m.apiFormat);
369
373
  const baseURL = m.baseURL?.trim();
370
374
  const modelId = m.model?.trim();
371
375
  const apiKey = m.apiKey?.trim();
@@ -388,6 +392,9 @@ function applyModelsPatch(
388
392
  ? { supportsJsonSchema: m.supportsJsonSchema }
389
393
  : {}),
390
394
  ...(m.reasoning !== undefined ? { reasoning: m.reasoning === true } : {}),
395
+ ...(normalizeReasoningEffort(m.reasoningEffort)
396
+ ? { reasoningEffort: normalizeReasoningEffort(m.reasoningEffort) }
397
+ : {}),
391
398
  promptCache: m.promptCache ?? { mode: 'auto' },
392
399
  enabled: m.enabled ?? true,
393
400
  createdAt: now,
@@ -400,12 +407,7 @@ function applyModelsPatch(
400
407
  const patch = item.patch;
401
408
  const next: ModelConfig = {
402
409
  ...existing,
403
- ...(patch.apiFormat
404
- ? {
405
- apiFormat:
406
- patch.apiFormat === 'anthropic' ? 'anthropic' : 'openai',
407
- }
408
- : {}),
410
+ ...(patch.apiFormat ? { apiFormat: normalizeApiFormat(patch.apiFormat) } : {}),
409
411
  ...(patch.displayName !== undefined
410
412
  ? { displayName: patch.displayName.trim() || undefined }
411
413
  : {}),
@@ -424,6 +426,10 @@ function applyModelsPatch(
424
426
  ...(patch.reasoning !== undefined
425
427
  ? { reasoning: patch.reasoning === true }
426
428
  : {}),
429
+ // null = 清除(回到服务端默认强度);未提供 = 不动。
430
+ ...(patch.reasoningEffort !== undefined
431
+ ? { reasoningEffort: normalizeReasoningEffort(patch.reasoningEffort) }
432
+ : {}),
427
433
  ...(patch.promptCache ? { promptCache: patch.promptCache } : {}),
428
434
  ...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
429
435
  updatedAt: now,
@@ -498,13 +504,12 @@ function applyConfigPatch(
498
504
  advisor = {
499
505
  enabled: patch.advisor.enabled ?? advisor?.enabled ?? DEFAULT_ADVISOR_CONFIG.enabled,
500
506
  mode: patch.advisor.mode ?? advisor?.mode ?? 'auto',
507
+ enforcement: patch.advisor.enforcement ?? advisor?.enforcement ?? 'remind',
501
508
  modelId:
502
509
  patch.advisor.modelId !== undefined
503
510
  ? patch.advisor.modelId
504
511
  : (advisor?.modelId ?? null),
505
512
  contextPolicy: 'full-relevant',
506
- shareSensitive:
507
- patch.advisor.shareSensitive ?? advisor?.shareSensitive ?? false,
508
513
  inputTokenBudget: normalizeLimited(
509
514
  patch.advisor.inputTokenBudget ?? advisor?.inputTokenBudget,
510
515
  CONFIG_LIMITS.advisorInputTokenBudget,
@@ -62,10 +62,6 @@ export const CONFIG_LIMITS = {
62
62
  modelContextWindow: { min: 1_000, max: 20_000_000, step: 1_000, kind: 'integer' },
63
63
  /** 模型条目的单次输出上限(可留空 = 用公式推荐值,故 UI 允许空串,这里只约束填了值的情况)。 */
64
64
  modelMaxTokens: { min: 256, max: 1_000_000, step: 256, kind: 'integer' },
65
- /** SSH 主机端口。TCP 端口的合法范围。 */
66
- sshPort: { min: 1, max: 65_535, step: 1, kind: 'integer' },
67
- /** SSH 单次操作(连接+执行/传输)超时(毫秒),上界给到 10 分钟容纳慢速链路上的大文件传输。 */
68
- sshTimeoutMs: { min: 1_000, max: 600_000, step: 1_000, kind: 'integer' },
69
65
  };
70
66
  /** 比例类字段保留的小数位(4 位比例 = 2 位百分比)。 */
71
67
  const RATIO_DECIMALS = 4;
@@ -79,10 +79,6 @@ export const CONFIG_LIMITS = {
79
79
  /** 模型条目的单次输出上限(可留空 = 用公式推荐值,故 UI 允许空串,这里只约束填了值的情况)。 */
80
80
  modelMaxTokens: { min: 256, max: 1_000_000, step: 256, kind: 'integer' },
81
81
 
82
- /** SSH 主机端口。TCP 端口的合法范围。 */
83
- sshPort: { min: 1, max: 65_535, step: 1, kind: 'integer' },
84
- /** SSH 单次操作(连接+执行/传输)超时(毫秒),上界给到 10 分钟容纳慢速链路上的大文件传输。 */
85
- sshTimeoutMs: { min: 1_000, max: 600_000, step: 1_000, kind: 'integer' },
86
82
  } as const satisfies Record<string, NumericLimit>;
87
83
 
88
84
  export type ConfigLimitKey = keyof typeof CONFIG_LIMITS;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 推理强度(纯逻辑、零依赖)。
3
+ *
4
+ * 独立成文件是因为 Desktop 的浏览器端经 `minimal-agent/provider-presets` 引用它:
5
+ * 放在 configFile.ts 里会把 node:fs 等整串依赖拖进前端包。
6
+ *
7
+ * 不同模型认的档位不同(有的没有 none/minimal,有的才有 xhigh),所以这里只收「OpenAI
8
+ * 系文档出现过的全部取值」,具体某个模型接不接受由服务端裁决:被拒绝时 client.ts 会去掉
9
+ * 该参数重发一次并告警,而不是让整个对话 400。
10
+ */
11
+ export const REASONING_EFFORTS = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'];
12
+ export function normalizeReasoningEffort(value) {
13
+ return typeof value === 'string' && REASONING_EFFORTS.includes(value)
14
+ ? value
15
+ : undefined;
16
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 推理强度(纯逻辑、零依赖)。
3
+ *
4
+ * 独立成文件是因为 Desktop 的浏览器端经 `minimal-agent/provider-presets` 引用它:
5
+ * 放在 configFile.ts 里会把 node:fs 等整串依赖拖进前端包。
6
+ *
7
+ * 不同模型认的档位不同(有的没有 none/minimal,有的才有 xhigh),所以这里只收「OpenAI
8
+ * 系文档出现过的全部取值」,具体某个模型接不接受由服务端裁决:被拒绝时 client.ts 会去掉
9
+ * 该参数重发一次并告警,而不是让整个对话 400。
10
+ */
11
+ export const REASONING_EFFORTS = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'] as const;
12
+ export type ReasoningEffort = (typeof REASONING_EFFORTS)[number];
13
+
14
+ export function normalizeReasoningEffort(value: unknown): ReasoningEffort | undefined {
15
+ return typeof value === 'string' && (REASONING_EFFORTS as readonly string[]).includes(value)
16
+ ? (value as ReasoningEffort)
17
+ : undefined;
18
+ }
@@ -20,7 +20,7 @@ export function buildContextBudgetPlan(input) {
20
20
  overBudget: totalInputTokens > inputLimit,
21
21
  };
22
22
  }
23
- const AUXILIARY_REMOVAL_ORDER = ['AdvisorDecision', 'Advisor', 'Agent'];
23
+ const AUXILIARY_REMOVAL_ORDER = ['Advisor', 'Agent'];
24
24
  /**
25
25
  * Last-resort schema reduction. Normal requests keep the full catalog; this is
26
26
  * used only after history compaction still cannot fit the provider window.
@@ -47,7 +47,7 @@ export interface EmergencyToolFit {
47
47
  plan: ContextBudgetPlan;
48
48
  }
49
49
 
50
- const AUXILIARY_REMOVAL_ORDER = ['AdvisorDecision', 'Advisor', 'Agent'];
50
+ const AUXILIARY_REMOVAL_ORDER = ['Advisor', 'Agent'];
51
51
 
52
52
  /**
53
53
  * Last-resort schema reduction. Normal requests keep the full catalog; this is
@@ -41,7 +41,7 @@ export function createConversationOptimizer(options) {
41
41
  degraded: false, before: compact.before, after: compact.after,
42
42
  messagesRemoved: compact.messagesRemoved, tokensFreed: compact.tokensFreed,
43
43
  });
44
- return { ok: true, content: `已优化对话:已完成历史的估算 token 从 ${compact.before} 降至 ${compact.after},释放 ${compact.tokensFreed}。近期消息和当前工具调用保留;历史证据与顾问状态保留在本机会话中,原 tool_call_id 仍可用于 AdvisorDecision。顾问按预算选取记录,不代表已审阅全部历史。本结果不是一手证据。继续未完成任务,无需复述摘要。` };
44
+ return { ok: true, content: `已优化对话:已完成历史的估算 token 从 ${compact.before} 降至 ${compact.after},释放 ${compact.tokensFreed}。近期消息和当前工具调用保留;历史证据保留在本机会话中;顾问评审时会一并展开这些原始记录。本结果不是一手证据。继续未完成任务,无需复述摘要。` };
45
45
  }
46
46
  catch {
47
47
  return { ok: false, error: signal?.aborted ? 'aborted'
@@ -48,7 +48,7 @@ export function createConversationOptimizer(options: {
48
48
  degraded: false, before: compact.before, after: compact.after,
49
49
  messagesRemoved: compact.messagesRemoved, tokensFreed: compact.tokensFreed,
50
50
  });
51
- return { ok: true, content: `已优化对话:已完成历史的估算 token 从 ${compact.before} 降至 ${compact.after},释放 ${compact.tokensFreed}。近期消息和当前工具调用保留;历史证据与顾问状态保留在本机会话中,原 tool_call_id 仍可用于 AdvisorDecision。顾问按预算选取记录,不代表已审阅全部历史。本结果不是一手证据。继续未完成任务,无需复述摘要。` };
51
+ return { ok: true, content: `已优化对话:已完成历史的估算 token 从 ${compact.before} 降至 ${compact.after},释放 ${compact.tokensFreed}。近期消息和当前工具调用保留;历史证据保留在本机会话中;顾问评审时会一并展开这些原始记录。本结果不是一手证据。继续未完成任务,无需复述摘要。` };
52
52
  } catch {
53
53
  return { ok: false, error: signal?.aborted ? 'aborted'
54
54
  : '优化未成功生成有效摘要,原历史保持不变。继续任务,不要重复调用。' };
@@ -12,18 +12,13 @@ export class StallGuard {
12
12
  #sequence = 0;
13
13
  #observed = new Set();
14
14
  #problems = new Map();
15
- /** Complete one consultation per two failed attempts, not on every retry. */
16
- completeReview(signature) {
17
- for (const [key, problem] of this.#problems) {
18
- if (problem.signature !== signature)
19
- continue;
20
- this.#problems.delete(key);
21
- if (problem.calls.has(this.#lastFailureCall)) {
22
- this.#failureCount = 0;
23
- this.#lastFailureCall = '';
24
- this.#lastFailure = '';
25
- }
26
- }
15
+ /** After a successful consultation the failing path may be tried again: the
16
+ * repeated-call rejection and the open problems start from zero. */
17
+ noteConsultation() {
18
+ this.#problems.clear();
19
+ this.#failureCount = 0;
20
+ this.#lastFailureCall = '';
21
+ this.#lastFailure = '';
27
22
  }
28
23
  /** Called immediately after execution, so a serial batch cannot run attempt 3
29
24
  * before its second failure has reached the advisor coordinator. */
@@ -31,7 +26,7 @@ export class StallGuard {
31
26
  if (this.#observed.has(call.id))
32
27
  return { stalled: false };
33
28
  this.#observed.add(call.id);
34
- if (call.function.name === 'Advisor' || call.function.name === 'AdvisorDecision'
29
+ if (call.function.name === 'Advisor'
35
30
  || (!result.ok && (result.disposition === 'deferred' || result.error === 'aborted')))
36
31
  return { stalled: false };
37
32
  const callSignature = hash(`${call.function.name}\0${call.function.arguments}`);
@@ -23,17 +23,13 @@ export class StallGuard {
23
23
  readonly #observed = new Set<string>();
24
24
  readonly #problems = new Map<string, { signature: string; calls: Set<string>; attempts: string[]; notified: boolean }>();
25
25
 
26
- /** Complete one consultation per two failed attempts, not on every retry. */
27
- completeReview(signature: string): void {
28
- for (const [key, problem] of this.#problems) {
29
- if (problem.signature !== signature) continue;
30
- this.#problems.delete(key);
31
- if (problem.calls.has(this.#lastFailureCall)) {
32
- this.#failureCount = 0;
33
- this.#lastFailureCall = '';
34
- this.#lastFailure = '';
35
- }
36
- }
26
+ /** After a successful consultation the failing path may be tried again: the
27
+ * repeated-call rejection and the open problems start from zero. */
28
+ noteConsultation(): void {
29
+ this.#problems.clear();
30
+ this.#failureCount = 0;
31
+ this.#lastFailureCall = '';
32
+ this.#lastFailure = '';
37
33
  }
38
34
 
39
35
  /** Called immediately after execution, so a serial batch cannot run attempt 3
@@ -41,7 +37,7 @@ export class StallGuard {
41
37
  observeAttempt(call: ToolCall, result: ToolResult): StallObservation {
42
38
  if (this.#observed.has(call.id)) return { stalled: false };
43
39
  this.#observed.add(call.id);
44
- if (call.function.name === 'Advisor' || call.function.name === 'AdvisorDecision'
40
+ if (call.function.name === 'Advisor'
45
41
  || (!result.ok && (result.disposition === 'deferred' || result.error === 'aborted'))) return { stalled: false };
46
42
  const callSignature = hash(`${call.function.name}\0${call.function.arguments}`);
47
43
  const failed = result.problem?.outcome === 'failed' || !result.ok