minimal-agent 0.13.0 → 0.14.0

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 (470) hide show
  1. package/README.md +6 -5
  2. package/package.json +60 -4
  3. package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
  4. package/plugins/cron-runner/src/commands.js +4 -4
  5. package/plugins/cron-runner/src/commands.ts +4 -4
  6. package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
  7. package/plugins/workflow-runner/commands/workflow.md +57 -90
  8. package/plugins/workflow-runner/commands/workflows.md +4 -4
  9. package/plugins/workflow-runner/plugin.js +24 -13
  10. package/plugins/workflow-runner/plugin.ts +28 -13
  11. package/plugins/workflow-runner/src/discovery.js +160 -0
  12. package/plugins/workflow-runner/src/discovery.ts +194 -0
  13. package/plugins/workflow-runner/src/goalAnchor.js +53 -0
  14. package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
  15. package/plugins/workflow-runner/src/index.js +197 -206
  16. package/plugins/workflow-runner/src/index.ts +248 -249
  17. package/plugins/workflow-runner/src/inputCoercion.js +40 -0
  18. package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
  19. package/plugins/workflow-runner/src/journal.js +224 -0
  20. package/plugins/workflow-runner/src/journal.ts +274 -0
  21. package/plugins/workflow-runner/src/jsonSchema.js +132 -0
  22. package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
  23. package/plugins/workflow-runner/src/moduleResolution.js +64 -0
  24. package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
  25. package/plugins/workflow-runner/src/resourceInputs.js +220 -0
  26. package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
  27. package/plugins/workflow-runner/src/runner.js +400 -489
  28. package/plugins/workflow-runner/src/runner.ts +488 -560
  29. package/plugins/workflow-runner/src/runtime/config.js +46 -0
  30. package/plugins/workflow-runner/src/runtime/config.ts +69 -0
  31. package/plugins/workflow-runner/src/runtime/context.js +1304 -0
  32. package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
  33. package/plugins/workflow-runner/src/runtime/exec.js +34 -0
  34. package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
  35. package/plugins/workflow-runner/src/runtime/flow.js +8 -0
  36. package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
  37. package/plugins/workflow-runner/src/runtime/fs.js +27 -0
  38. package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
  39. package/plugins/workflow-runner/src/runtime/model.js +6 -0
  40. package/plugins/workflow-runner/src/runtime/model.ts +8 -0
  41. package/plugins/workflow-runner/src/runtime/output.js +7 -0
  42. package/plugins/workflow-runner/src/runtime/output.ts +21 -0
  43. package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
  44. package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
  45. package/plugins/workflow-runner/src/runtime/state.js +16 -0
  46. package/plugins/workflow-runner/src/runtime/state.ts +19 -0
  47. package/plugins/workflow-runner/src/runtime/tools.js +3 -0
  48. package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
  49. package/plugins/workflow-runner/src/sandbox.js +141 -0
  50. package/plugins/workflow-runner/src/sandbox.ts +182 -0
  51. package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
  52. package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
  53. package/plugins/workflow-runner/src/taskIdentity.js +151 -307
  54. package/plugins/workflow-runner/src/taskIdentity.ts +207 -355
  55. package/plugins/workflow-runner/src/toolRuntime.js +29 -8
  56. package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
  57. package/plugins/workflow-runner/src/types.js +7 -211
  58. package/plugins/workflow-runner/src/types.ts +154 -623
  59. package/plugins/workflow-runner/src/util.js +47 -0
  60. package/plugins/workflow-runner/src/util.ts +51 -0
  61. package/plugins/workflow-runner/src/workflowContext.js +20 -0
  62. package/plugins/workflow-runner/src/workflowContext.ts +58 -0
  63. package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
  64. package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
  65. package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
  66. package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
  67. package/plugins/workflow-runner/src/workflowState.js +474 -802
  68. package/plugins/workflow-runner/src/workflowState.ts +575 -970
  69. package/plugins/workflow-runner/test/api.test.ts +223 -0
  70. package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
  71. package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
  72. package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
  73. package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
  74. package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
  75. package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
  76. package/plugins/workflow-runner/test/discovery.test.ts +234 -0
  77. package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
  78. package/plugins/workflow-runner/test/goalController.test.ts +317 -0
  79. package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
  80. package/plugins/workflow-runner/test/listResources.test.ts +106 -0
  81. package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
  82. package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
  83. package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
  84. package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
  85. package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
  86. package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
  87. package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
  88. package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
  89. package/skills/pptx/examples/assets/cover-bg.svg +23 -0
  90. package/skills/pptx/examples/assets/icon-spark.svg +5 -0
  91. package/skills/ui-design/SKILL.md +5 -2
  92. package/skills/ui-design/references/layout-composition.md +1 -1
  93. package/skills/workflow-creator/SKILL.md +344 -0
  94. package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
  95. package/skills/workflow-creator/references/api.md +216 -0
  96. package/skills/workflow-creator/references/migration.md +103 -0
  97. package/skills/workflow-creator/references/patterns.md +779 -0
  98. package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
  99. package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
  100. package/skills/workflow-creator/scripts/validate.mjs +275 -0
  101. package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
  102. package/src/advisor/advisorCoordinator.js +503 -0
  103. package/src/advisor/advisorCoordinator.ts +657 -0
  104. package/src/cli/configMenu.tsx +0 -1
  105. package/src/cli/print.js +27 -6
  106. package/src/cli/print.ts +48 -4
  107. package/src/cli/streamJson.js +5 -6
  108. package/src/cli/streamJson.ts +8 -4
  109. package/src/cli/wizards/advisorWizard.js +4 -2
  110. package/src/cli/wizards/advisorWizard.tsx +8 -2
  111. package/src/cli/wizards/contextAdvisorWizard.js +5 -4
  112. package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
  113. package/src/collaboration/taskFlow.js +115 -0
  114. package/src/collaboration/taskFlow.ts +169 -0
  115. package/src/config/configDefaults.js +41 -1
  116. package/src/config/configDefaults.ts +43 -1
  117. package/src/config/configDiagnostics.ts +2 -2
  118. package/src/config/configFile.js +40 -9
  119. package/src/config/configFile.ts +60 -11
  120. package/src/config/configService.js +419 -74
  121. package/src/config/configService.ts +703 -145
  122. package/src/config/numericLimits.js +22 -0
  123. package/src/config/numericLimits.ts +24 -0
  124. package/src/config.js +1 -0
  125. package/src/config.ts +3 -2
  126. package/src/context/budgetPlan.js +1 -1
  127. package/src/context/budgetPlan.ts +1 -1
  128. package/src/context/compact.js +7 -2
  129. package/src/context/compact.ts +9 -2
  130. package/src/context/microCompactLite.js +212 -13
  131. package/src/context/microCompactLite.ts +261 -12
  132. package/src/context/sessionRegistry.js +8 -1
  133. package/src/context/sessionRegistry.ts +18 -5
  134. package/src/context/toolResultArchive.js +72 -0
  135. package/src/context/toolResultArchive.ts +95 -0
  136. package/src/goal/errors.js +8 -0
  137. package/src/goal/errors.ts +18 -0
  138. package/src/goal/goalRuntime.js +344 -0
  139. package/src/goal/goalRuntime.ts +466 -0
  140. package/src/goal/goalStore.js +200 -0
  141. package/src/goal/goalStore.ts +219 -0
  142. package/src/goal/index.js +4 -0
  143. package/src/goal/index.ts +4 -0
  144. package/src/goal/statusMapping.js +71 -0
  145. package/src/goal/statusMapping.ts +95 -0
  146. package/src/goal/types.js +7 -0
  147. package/src/goal/types.ts +179 -0
  148. package/src/goal/workflowController.js +306 -0
  149. package/src/goal/workflowController.ts +360 -0
  150. package/src/harness/evidenceLedger.js +2 -0
  151. package/src/harness/evidenceLedger.ts +1 -0
  152. package/src/harness/stallGuard.js +9 -3
  153. package/src/harness/stallGuard.ts +7 -3
  154. package/src/images/imageResolver.js +161 -0
  155. package/src/images/imageResolver.ts +189 -0
  156. package/src/infra/verification.js +34 -5
  157. package/src/infra/verification.ts +39 -4
  158. package/src/llm/client.js +79 -21
  159. package/src/llm/client.ts +182 -35
  160. package/src/llm/gatewayClient.js +201 -0
  161. package/src/llm/gatewayClient.ts +299 -0
  162. package/src/llm/messageProvenance.js +46 -0
  163. package/src/llm/messageProvenance.ts +58 -0
  164. package/src/llm/modelProvider.js +1 -0
  165. package/src/llm/modelProvider.ts +22 -0
  166. package/src/llm/providers/anthropicFormat.js +108 -1
  167. package/src/llm/providers/anthropicFormat.ts +115 -4
  168. package/src/llm/providers/openaiFormat.js +9 -0
  169. package/src/llm/providers/openaiFormat.ts +12 -3
  170. package/src/loop.js +319 -54
  171. package/src/loop.ts +373 -57
  172. package/src/main.js +4 -1
  173. package/src/main.tsx +4 -1
  174. package/src/managedExecution/index.js +2 -0
  175. package/src/managedExecution/index.ts +2 -0
  176. package/src/managedExecution/registry.js +15 -0
  177. package/src/managedExecution/registry.ts +20 -0
  178. package/src/managedExecution/types.js +1 -0
  179. package/src/managedExecution/types.ts +86 -0
  180. package/src/modelRef.js +9 -0
  181. package/src/modelRef.ts +31 -0
  182. package/src/multimodal.js +14 -28
  183. package/src/multimodal.ts +17 -33
  184. package/src/net/lanDiscovery.js +65 -0
  185. package/src/net/lanDiscovery.ts +82 -0
  186. package/src/plugin-sdk.js +28 -3
  187. package/src/plugin-sdk.ts +39 -6
  188. package/src/plugins/pluginApi.ts +16 -1
  189. package/src/plugins/pluginLoader.js +4 -0
  190. package/src/plugins/pluginLoader.ts +6 -0
  191. package/src/plugins/pluginRunner.js +139 -7
  192. package/src/plugins/pluginRunner.ts +173 -9
  193. package/src/plugins/slashFallback.js +113 -0
  194. package/src/plugins/slashFallback.ts +137 -0
  195. package/src/prompts/policyRegistry.js +12 -32
  196. package/src/prompts/policyRegistry.ts +15 -32
  197. package/src/prompts/promptAssembly.js +103 -12
  198. package/src/prompts/promptAssembly.ts +114 -12
  199. package/src/prompts/skillList.js +21 -3
  200. package/src/prompts/skillList.ts +27 -2
  201. package/src/prompts/stableSystemPrompt.js +18 -0
  202. package/src/prompts/stableSystemPrompt.ts +33 -0
  203. package/src/runtime/agentRuntimeContext.js +8 -2
  204. package/src/runtime/agentRuntimeContext.ts +17 -4
  205. package/src/session/runAgentTurn.js +78 -12
  206. package/src/session/runAgentTurn.ts +105 -23
  207. package/src/session/runTurn.js +10 -9
  208. package/src/session/runTurn.ts +17 -8
  209. package/src/session/sessionState.ts +2 -0
  210. package/src/team/auth.js +186 -0
  211. package/src/team/auth.ts +298 -0
  212. package/src/team/cli.js +105 -0
  213. package/src/team/cli.ts +122 -0
  214. package/src/team/onlineToolContract.js +64 -0
  215. package/src/team/onlineToolContract.ts +109 -0
  216. package/src/team/toolPolicy.js +36 -0
  217. package/src/team/toolPolicy.ts +75 -0
  218. package/src/tools/advisor/advisor.js +90 -46
  219. package/src/tools/advisor/advisor.ts +100 -47
  220. package/src/tools/advisor/advisorDecision.js +71 -0
  221. package/src/tools/advisor/advisorDecision.ts +85 -0
  222. package/src/tools/agent/agent.js +18 -5
  223. package/src/tools/agent/agent.ts +21 -7
  224. package/src/tools/agent/agentControl.js +246 -0
  225. package/src/tools/agent/agentControl.ts +284 -0
  226. package/src/tools/agent/agentTaskService.js +40 -1
  227. package/src/tools/agent/agentTaskService.ts +44 -1
  228. package/src/tools/agent/agentTypes.js +14 -2
  229. package/src/tools/agent/agentTypes.ts +15 -2
  230. package/src/tools/applypatch/applyPatch.js +362 -0
  231. package/src/tools/applypatch/applyPatch.ts +428 -0
  232. package/src/tools/bash/bash.js +22 -15
  233. package/src/tools/bash/bash.ts +23 -17
  234. package/src/tools/capabilities.js +10 -5
  235. package/src/tools/capabilities.ts +10 -5
  236. package/src/tools/computeruse/computerIntent.js +32 -0
  237. package/src/tools/computeruse/computerIntent.ts +46 -0
  238. package/src/tools/computeruse/telemetryLog.js +12 -0
  239. package/src/tools/computeruse/telemetryLog.ts +16 -0
  240. package/src/tools/computeruse/vision/telemetry.js +1 -0
  241. package/src/tools/computeruse/vision/telemetry.ts +1 -0
  242. package/src/tools/effectiveToolSet.js +4 -0
  243. package/src/tools/effectiveToolSet.ts +4 -0
  244. package/src/tools/index.js +21 -2
  245. package/src/tools/index.ts +33 -2
  246. package/src/tools/presentresult/presentresult.js +6 -15
  247. package/src/tools/presentresult/presentresult.ts +6 -15
  248. package/src/tools/read/read.js +62 -9
  249. package/src/tools/read/read.ts +63 -9
  250. package/src/tools/shared/fileUtils.js +1 -2
  251. package/src/tools/shared/fileUtils.ts +1 -2
  252. package/src/tools/shared/toolOutputStore.js +23 -1
  253. package/src/tools/shared/toolOutputStore.ts +22 -1
  254. package/src/tools/skill/skill.js +9 -3
  255. package/src/tools/skill/skill.ts +8 -4
  256. package/src/tools/toolPromptGuides.js +117 -0
  257. package/src/tools/toolPromptGuides.ts +137 -0
  258. package/src/tools/webbrowser/agentBrowserPath.js +19 -9
  259. package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
  260. package/src/tools/webbrowser/browser.js +27 -11
  261. package/src/tools/webbrowser/browser.ts +68 -24
  262. package/src/tools/webbrowser/browserAutomation.js +329 -0
  263. package/src/tools/webbrowser/browserAutomation.ts +467 -0
  264. package/src/tools/webbrowser/browserAxState.js +97 -0
  265. package/src/tools/webbrowser/browserAxState.ts +142 -0
  266. package/src/tools/webbrowser/browserBatch.js +1578 -0
  267. package/src/tools/webbrowser/browserBatch.ts +2382 -0
  268. package/src/tools/webbrowser/browserCdp.js +208 -0
  269. package/src/tools/webbrowser/browserCdp.ts +255 -0
  270. package/src/tools/webbrowser/browserConditions.js +24 -0
  271. package/src/tools/webbrowser/browserConditions.ts +45 -0
  272. package/src/tools/webbrowser/browserContent.js +281 -0
  273. package/src/tools/webbrowser/browserContent.ts +438 -0
  274. package/src/tools/webbrowser/browserControl.js +131 -34
  275. package/src/tools/webbrowser/browserControl.ts +210 -56
  276. package/src/tools/webbrowser/browserDaemon.js +22 -14
  277. package/src/tools/webbrowser/browserDaemon.ts +54 -21
  278. package/src/tools/webbrowser/browserFrames.js +47 -0
  279. package/src/tools/webbrowser/browserFrames.ts +54 -0
  280. package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
  281. package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
  282. package/src/tools/webbrowser/browserLocator.js +140 -0
  283. package/src/tools/webbrowser/browserLocator.ts +194 -0
  284. package/src/tools/webbrowser/browserOutcome.js +10 -17
  285. package/src/tools/webbrowser/browserOutcome.ts +48 -31
  286. package/src/tools/webbrowser/browserRecovery.js +58 -0
  287. package/src/tools/webbrowser/browserRecovery.ts +83 -0
  288. package/src/tools/webbrowser/browserRunRegistry.js +35 -3
  289. package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
  290. package/src/tools/webbrowser/browserService.js +28 -2
  291. package/src/tools/webbrowser/browserService.ts +49 -9
  292. package/src/tools/webbrowser/browserStream.js +132 -33
  293. package/src/tools/webbrowser/browserStream.ts +224 -50
  294. package/src/tools/webbrowser/browserTabs.js +48 -15
  295. package/src/tools/webbrowser/browserTabs.ts +178 -96
  296. package/src/tools/webbrowser/webbrowser.js +679 -144
  297. package/src/tools/webbrowser/webbrowser.ts +975 -236
  298. package/src/tools/webfetch/preapproved.js +0 -2
  299. package/src/tools/webfetch/preapproved.ts +0 -2
  300. package/src/tools/websearch/websearch.js +129 -8
  301. package/src/tools/websearch/websearch.ts +138 -6
  302. package/src/types.ts +440 -38
  303. package/src/ui/App.js +377 -58
  304. package/src/ui/App.tsx +576 -111
  305. package/src/ui/LiveArea.js +14 -8
  306. package/src/ui/LiveArea.tsx +14 -10
  307. package/src/ui/MessageList.js +63 -20
  308. package/src/ui/MessageList.tsx +101 -16
  309. package/src/ui/ModelPicker.js +7 -4
  310. package/src/ui/ModelPicker.tsx +55 -8
  311. package/src/ui/hooks/useChat.js +152 -163
  312. package/src/ui/hooks/useChat.ts +396 -334
  313. package/src/ui/loginCommand.js +56 -0
  314. package/src/ui/loginCommand.ts +63 -0
  315. package/src/ui/toolProgress.js +41 -0
  316. package/src/ui/toolProgress.ts +44 -0
  317. package/src/web/browserBridge.js +20 -2
  318. package/src/web/browserBridge.ts +20 -2
  319. package/src/web/protocol.ts +1897 -194
  320. package/workflows/adaptive-research/workflow.mjs +86 -0
  321. package/workflows/e2e-capabilities/workflow.mjs +102 -0
  322. package/workflows/e2e-llm/workflow.mjs +52 -0
  323. package/workflows/e2e-smoke/workflow.mjs +59 -0
  324. package/workflows/e2e-vision-schema/README.md +48 -0
  325. package/workflows/e2e-vision-schema/workflow.mjs +149 -0
  326. package/workflows/e2e-write-greet/workflow.mjs +37 -0
  327. package/workflows/file-census/README.md +14 -0
  328. package/workflows/file-census/scripts/.gitkeep +2 -0
  329. package/workflows/file-census/workflow.mjs +110 -0
  330. package/workflows/goal/README.md +166 -0
  331. package/workflows/goal/prompts/audit.md +57 -0
  332. package/workflows/goal/prompts/contract.md +56 -0
  333. package/workflows/goal/prompts/iterate.md +37 -0
  334. package/workflows/goal/workflow.mjs +501 -0
  335. package/workflows/illustrate/README.md +90 -0
  336. package/workflows/illustrate/assets/engines.json +20 -0
  337. package/workflows/illustrate/assets/extract-baidu.js +21 -0
  338. package/workflows/illustrate/assets/extract-bing.js +33 -0
  339. package/workflows/illustrate/assets/extract-google.js +31 -0
  340. package/workflows/illustrate/workflow.mjs +301 -0
  341. package/workflows/plan/workflow.mjs +389 -0
  342. package/workflows/question-crops/README.md +35 -0
  343. package/workflows/question-crops/prompts/inventory.md +23 -0
  344. package/workflows/question-crops/scripts/image-ops.mjs +305 -0
  345. package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
  346. package/workflows/question-crops/workflow.mjs +172 -0
  347. package/workflows/ralph-loop/workflow.mjs +162 -0
  348. package/workflows/research/README.md +74 -0
  349. package/workflows/research/prompts/s0-goal.md +43 -0
  350. package/workflows/research/prompts/s1-early-exit.md +22 -0
  351. package/workflows/research/prompts/s1-map.md +58 -0
  352. package/workflows/research/prompts/s2-options.md +53 -0
  353. package/workflows/research/prompts/s3-deep-dive.md +56 -0
  354. package/workflows/research/prompts/s4-verify.md +74 -0
  355. package/workflows/research/prompts/s5-synthesis.md +81 -0
  356. package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
  357. package/workflows/research/prompts/s6-package.md +32 -0
  358. package/workflows/research/scripts/build-report.mjs +98 -0
  359. package/workflows/research/workflow.mjs +288 -0
  360. package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
  361. package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
  362. package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
  363. package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
  364. package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
  365. package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
  366. package/workflows/wechat-reading-shots/workflow.mjs +266 -0
  367. package/workflows/wrong-question-docx/README.md +55 -0
  368. package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
  369. package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
  370. package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
  371. package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
  372. package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
  373. package/workflows/wrong-question-docx/workflow.mjs +485 -0
  374. package/plugins/workflow-runner/src/agentCommon.js +0 -354
  375. package/plugins/workflow-runner/src/agentCommon.ts +0 -435
  376. package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
  377. package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
  378. package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
  379. package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
  380. package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
  381. package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
  382. package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
  383. package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
  384. package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
  385. package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
  386. package/plugins/workflow-runner/src/dslManifest.js +0 -72
  387. package/plugins/workflow-runner/src/dslManifest.ts +0 -127
  388. package/plugins/workflow-runner/src/expressions.js +0 -639
  389. package/plugins/workflow-runner/src/expressions.ts +0 -640
  390. package/plugins/workflow-runner/src/loader.js +0 -573
  391. package/plugins/workflow-runner/src/loader.ts +0 -721
  392. package/plugins/workflow-runner/src/multimodal.js +0 -38
  393. package/plugins/workflow-runner/src/multimodal.ts +0 -64
  394. package/plugins/workflow-runner/src/recovery.js +0 -126
  395. package/plugins/workflow-runner/src/recovery.ts +0 -156
  396. package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
  397. package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
  398. package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -42
  399. package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -52
  400. package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
  401. package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
  402. package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
  403. package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
  404. package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
  405. package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
  406. package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
  407. package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
  408. package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -227
  409. package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -274
  410. package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -61
  411. package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -72
  412. package/plugins/workflow-runner/src/stepPipeline.js +0 -277
  413. package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
  414. package/plugins/workflow-runner/test/agent.test.ts +0 -587
  415. package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
  416. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
  417. package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
  418. package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -167
  419. package/plugins/workflow-runner/test/context-files.test.ts +0 -320
  420. package/plugins/workflow-runner/test/e2e.test.ts +0 -370
  421. package/plugins/workflow-runner/test/e2eNewLayout.test.ts +0 -314
  422. package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -234
  423. package/plugins/workflow-runner/test/expressions.test.ts +0 -439
  424. package/plugins/workflow-runner/test/foundation.test.ts +0 -126
  425. package/plugins/workflow-runner/test/graceful.test.ts +0 -139
  426. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
  427. package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
  428. package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
  429. package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
  430. package/plugins/workflow-runner/test/loader.test.ts +0 -617
  431. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
  432. package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
  433. package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
  434. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -106
  435. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
  436. package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
  437. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -221
  438. package/plugins/workflow-runner/test/recovery.test.ts +0 -265
  439. package/plugins/workflow-runner/test/research.load.test.ts +0 -328
  440. package/plugins/workflow-runner/test/runner.test.ts +0 -911
  441. package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -436
  442. package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
  443. package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -198
  444. package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
  445. package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -354
  446. package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -324
  447. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
  448. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
  449. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
  450. package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
  451. package/plugins/workflow-runner/test/word.load.test.ts +0 -208
  452. package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
  453. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
  454. package/src/advisorState.js +0 -22
  455. package/src/cli/configWizard.js +0 -293
  456. package/src/tools/toolProgressBridge.js +0 -48
  457. package/workflows/adaptive-research.yaml +0 -50
  458. package/workflows/book-review-short.yaml +0 -125
  459. package/workflows/e2e-write-greet.yaml +0 -31
  460. package/workflows/illustrate.yaml +0 -227
  461. package/workflows/plan.yaml +0 -470
  462. package/workflows/ppt.yaml +0 -1670
  463. package/workflows/ralph-loop.yaml +0 -201
  464. package/workflows/research.yaml +0 -873
  465. package/workflows/schema.json +0 -176
  466. package/workflows/webppt.yaml +0 -496
  467. package/workflows/wechat-reading-shots.yaml +0 -315
  468. package/workflows/word.yaml +0 -720
  469. package/workflows/youtube-shorts.yaml +0 -202
  470. /package/workflows/{research.md → research/DESIGN.md} +0 -0
package/src/ui/App.tsx CHANGED
@@ -7,47 +7,67 @@
7
7
  * ============================================================
8
8
  */
9
9
 
10
- import React, { useDeferredValue } from 'react';
11
10
  import { Box, Text, useApp } from 'ink';
11
+ import React, { useDeferredValue } from 'react';
12
12
 
13
13
  import { getWorkingDir } from '../bootstrap/workingDir.js';
14
+ import {
15
+ createCliComputerRun,
16
+ readRecentComputerUseAudit,
17
+ } from '../cli/computerUse.js';
14
18
  import { ConfigMenu } from '../cli/configMenu.js';
19
+ import type { ModelConfig } from '../config/configFile.js';
15
20
  import { configService } from '../config/configService.js';
16
21
  import { listArchives, restoreArchive } from '../context/archive.js';
17
22
  import { saveContext } from '../context/persistContext.js';
18
23
  import { sessionFileFor } from '../context/sessionPath.js';
19
- import { workspaceIndexService, type WorkspaceRecord } from '../context/workspaceIndex.js';
20
- import { memoryService } from '../memory/memoryService.js';
21
24
  import {
25
+ type SessionSummary,
22
26
  createSession,
23
27
  listSessionSummaries,
24
28
  updateSessionMetadata,
25
- type SessionSummary,
26
29
  } from '../context/sessionRegistry.js';
27
- import type { Message, Provider } from '../types.js';
28
- import type { ModelConfig } from '../config/configFile.js';
30
+ import {
31
+ type WorkspaceRecord,
32
+ workspaceIndexService,
33
+ } from '../context/workspaceIndex.js';
34
+ import { memoryService } from '../memory/memoryService.js';
35
+ import { legacyModelId, modelRefFromLegacy } from '../modelRef.js';
29
36
  import { activatePlugins } from '../plugins/pluginLifecycle.js';
30
37
  import { runAgentTurn } from '../session/runAgentTurn.js';
31
- import { writeClipboardText } from '../utils/clipboardImage.js';
32
38
  import {
33
- createCliComputerRun,
34
- readRecentComputerUseAudit,
35
- } from '../cli/computerUse.js';
39
+ type OnlineModelCatalogItem,
40
+ beginTeamLogin,
41
+ clearTeamCredentials,
42
+ fetchOnlineModels,
43
+ fetchTeamSubscription,
44
+ fetchTeamUsage,
45
+ loadTeamCredentials,
46
+ revokeTeamCredentials,
47
+ } from '../team/auth.js';
36
48
  import { computerUseService } from '../tools/computeruse/computerUseService.js';
37
- import { advisorHelpText, parseAdvisorCommand } from './advisorCommand.js';
49
+ import type { Message, Provider } from '../types.js';
50
+ import { writeClipboardText } from '../utils/clipboardImage.js';
38
51
  import { AskQuestion } from './AskQuestion.js';
39
52
  import { InputBox } from './InputBox.js';
40
53
  import { LiveArea } from './LiveArea.js';
41
- import { QueuedMessagesBanner } from './QueuedMessagesBanner.js';
42
54
  import { MessageList } from './MessageList.js';
43
55
  import { ModelPicker } from './ModelPicker.js';
56
+ import type { PickerModel } from './ModelPicker.js';
57
+ import { QueuedMessagesBanner } from './QueuedMessagesBanner.js';
44
58
  import { SessionPicker } from './SessionPicker.js';
45
- import { WorkspacePicker } from './WorkspacePicker.js';
46
59
  import { StatusLine } from './StatusLine.js';
47
- import { theme } from './theme.js';
60
+ import { WorkspacePicker } from './WorkspacePicker.js';
61
+ import { advisorHelpText, parseAdvisorCommand } from './advisorCommand.js';
48
62
  import { parseComputerCommand } from './computerCommand.js';
49
63
  import { useChat } from './hooks/useChat.js';
50
64
  import { useTerminalSize } from './hooks/useTerminalSize.js';
65
+ import {
66
+ isLogoutCommand,
67
+ loginHelpText,
68
+ parseLoginCommand,
69
+ } from './loginCommand.js';
70
+ import { theme } from './theme.js';
51
71
 
52
72
  /**
53
73
  * ANSI escape: 清屏 + 光标归位。
@@ -68,6 +88,40 @@ function formatProviderName(apiFormat: string): string {
68
88
  return apiFormat;
69
89
  }
70
90
 
91
+ function toPickerModel(model: OnlineModelCatalogItem): PickerModel {
92
+ return {
93
+ id: legacyModelId({
94
+ source: 'gateway',
95
+ serverId: 'gateway',
96
+ modelId: model.id,
97
+ }),
98
+ apiFormat: model.apiFormat,
99
+ displayName: model.displayName,
100
+ model: model.model,
101
+ contextWindow: model.contextWindow,
102
+ maxTokens: model.maxTokens,
103
+ supportsVision: model.supportsVision,
104
+ enabled: true,
105
+ source: 'gateway',
106
+ isDefault: model.isDefault,
107
+ availability: model.availability,
108
+ };
109
+ }
110
+
111
+ async function openTeamBrowser(url: string): Promise<void> {
112
+ const command =
113
+ process.platform === 'darwin'
114
+ ? ['open', url]
115
+ : process.platform === 'win32'
116
+ ? ['cmd', '/c', 'start', '', url]
117
+ : ['xdg-open', url];
118
+ try {
119
+ Bun.spawn({ cmd: command, stdout: 'ignore', stderr: 'ignore' });
120
+ } catch {
121
+ /* TUI prints the URL below */
122
+ }
123
+ }
124
+
71
125
  interface Props {
72
126
  provider: Provider;
73
127
  /** 启动时的历史(含 system prompt);可能是从 last-context.json 加载来的旧对话。 */
@@ -94,19 +148,21 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
94
148
  provider,
95
149
  signal: controller.signal,
96
150
  runAgentTurn,
97
- }).then((fn) => {
98
- if (controller.signal.aborted) {
99
- fn();
100
- return;
101
- }
102
- cleanup = fn;
103
- }).catch((err) => {
104
- console.warn(
105
- `[minimal-agent] plugin activation failed: ${
106
- err instanceof Error ? err.message : String(err)
107
- }`,
108
- );
109
- });
151
+ })
152
+ .then((fn) => {
153
+ if (controller.signal.aborted) {
154
+ fn();
155
+ return;
156
+ }
157
+ cleanup = fn;
158
+ })
159
+ .catch((err) => {
160
+ console.warn(
161
+ `[minimal-agent] plugin activation failed: ${
162
+ err instanceof Error ? err.message : String(err)
163
+ }`,
164
+ );
165
+ });
110
166
  return () => {
111
167
  controller.abort();
112
168
  cleanup?.();
@@ -148,15 +204,25 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
148
204
  // /session /sessions /resume(裸命令)打开的选择器数据;非 null 时顶替 InputBox。
149
205
  // 纯本地 UI 态,不需要塞进 useChat——不像 pendingAsk 那样由 askBridge 从外部注入,
150
206
  // 这里完全由 handleSubmit 自己的分支触发/清空。
151
- const [sessionPicker, setSessionPicker] = React.useState<SessionSummary[] | null>(null);
152
- const [workspacePicker, setWorkspacePicker] = React.useState<WorkspaceRecord[] | null>(null);
207
+ const [sessionPicker, setSessionPicker] = React.useState<
208
+ SessionSummary[] | null
209
+ >(null);
210
+ const [workspacePicker, setWorkspacePicker] = React.useState<
211
+ WorkspaceRecord[] | null
212
+ >(null);
153
213
 
154
214
  // /config —— 硬编码菜单覆盖层,纯本地 UI 态(与 sessionPicker 同构)。
155
215
  const [configMenuOpen, setConfigMenuOpen] = React.useState(false);
156
216
 
157
217
  // /model —— 基座模型选择器;/advisor select —— 顾问模型选择器
158
- const [baseModelPicker, setBaseModelPicker] = React.useState<{ models: ModelConfig[]; currentId: string | null } | null>(null);
159
- const [advisorModelPicker, setAdvisorModelPicker] = React.useState<{ models: ModelConfig[]; currentId: string | null } | null>(null);
218
+ const [baseModelPicker, setBaseModelPicker] = React.useState<{
219
+ models: PickerModel[];
220
+ currentId: string | null;
221
+ } | null>(null);
222
+ const [advisorModelPicker, setAdvisorModelPicker] = React.useState<{
223
+ models: PickerModel[];
224
+ currentId: string | null;
225
+ } | null>(null);
160
226
 
161
227
  // 强制刷新计数,用于切换模型后触发 StatusLine 重渲染
162
228
  const [, forceRerender] = React.useState(0);
@@ -173,9 +239,14 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
173
239
  const handleSessionCreate = React.useCallback(
174
240
  async (title: string) => {
175
241
  setSessionPicker(null);
176
- const summary = await createSession(getWorkingDir(), title ? { title } : {});
242
+ const summary = await createSession(
243
+ getWorkingDir(),
244
+ title ? { title } : {},
245
+ );
177
246
  await chat.switchSession(summary.id);
178
- chat.ephemeralLine(`[/session] 已新建并切换到 session "${summary.id}"(${summary.title})`);
247
+ chat.ephemeralLine(
248
+ `[/session] 已新建并切换到 session "${summary.id}"(${summary.title})`,
249
+ );
179
250
  },
180
251
  [chat.switchSession, chat.ephemeralLine],
181
252
  );
@@ -187,13 +258,16 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
187
258
  setWorkspacePicker(await workspaceIndexService.list());
188
259
  }, [chat.isLoading]);
189
260
 
190
- const handleWorkspaceSelect = React.useCallback(async (cwd: string, sessionId: string) => {
191
- const switched = await chat.switchWorkspace(cwd, sessionId);
192
- setWorkspacePicker(null);
193
- if (switched) {
194
- chat.ephemeralLine(`[/workspaces] 已切换到 ${cwd} · ${sessionId}`);
195
- }
196
- }, [chat.switchWorkspace, chat.ephemeralLine]);
261
+ const handleWorkspaceSelect = React.useCallback(
262
+ async (cwd: string, sessionId: string) => {
263
+ const switched = await chat.switchWorkspace(cwd, sessionId);
264
+ setWorkspacePicker(null);
265
+ if (switched) {
266
+ chat.ephemeralLine(`[/workspaces] 已切换到 ${cwd} · ${sessionId}`);
267
+ }
268
+ },
269
+ [chat.switchWorkspace, chat.ephemeralLine],
270
+ );
197
271
 
198
272
  const handleBaseModelSelect = React.useCallback(
199
273
  async (modelId: string | null) => {
@@ -201,16 +275,54 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
201
275
  if (!modelId) return;
202
276
  const snap = await configService.getSnapshot();
203
277
  const model = snap.config?.models.find((m) => m.id === modelId);
278
+ const selectedRef = modelRefFromLegacy(modelId);
279
+ const isGateway = selectedRef.source === 'gateway';
280
+ if (isGateway) {
281
+ const provider = await configService.providerForRef(selectedRef);
282
+ if (provider) {
283
+ const gatewayProvider =
284
+ provider.source === 'gateway' ? provider : null;
285
+ if (!gatewayProvider) return;
286
+ await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, {
287
+ modelId: null,
288
+ preferredModel: {
289
+ source: 'gateway',
290
+ serverId: gatewayProvider.gatewayUrl,
291
+ modelId: selectedRef.modelId,
292
+ },
293
+ effectiveModel: {
294
+ source: 'gateway',
295
+ serverId: gatewayProvider.gatewayUrl,
296
+ modelId: selectedRef.modelId,
297
+ },
298
+ restoreWhenAvailable: true,
299
+ });
300
+ chat.setProvider(provider);
301
+ forceRerender((v) => v + 1);
302
+ chat.ephemeralLine(
303
+ `[/model] 当前 Session 已切换为在线模型 ${provider.name},下一轮对话生效。`,
304
+ );
305
+ }
306
+ return;
307
+ }
204
308
  if (!model) {
205
309
  chat.ephemeralLine(`[/model] 未找到模型 "${modelId}"`);
206
310
  return;
207
311
  }
208
312
  const newProvider = await configService.providerForModel(modelId);
209
313
  if (newProvider) {
210
- await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, { modelId });
314
+ await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, {
315
+ modelId: null,
316
+ preferredModel: { source: 'local', modelId },
317
+ effectiveModel: { source: 'local', modelId },
318
+ fallbackReason: undefined,
319
+ restoreWhenAvailable: false,
320
+ });
211
321
  chat.setProvider(newProvider);
212
322
  forceRerender((v) => v + 1);
213
- chat.ephemeralLine(`[/model] 当前 Session 已切换为 ${model.displayName || model.model}(${formatProviderName(model.apiFormat)}),下一轮对话生效。`);
323
+ chat.ephemeralLine(
324
+ `[/model] 当前 Session 已切换为 ${model.displayName || model.model}(${formatProviderName(model.apiFormat)}),下一轮对话生效。`,
325
+ );
214
326
  }
215
327
  },
216
328
  [chat.activeSessionId, chat.setProvider, chat.ephemeralLine],
@@ -218,23 +330,120 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
218
330
 
219
331
  React.useEffect(() => {
220
332
  let cancelled = false;
221
- void listSessionSummaries(getWorkingDir()).then(async (sessions) => {
222
- const modelId = sessions.find((session) => session.id === chat.activeSessionId)?.modelId;
223
- const provider = await configService.providerForModel(modelId ?? null)
224
- ?? await configService.providerForModel(null);
225
- if (!cancelled && provider) chat.setProvider(provider);
226
- }).catch(() => {});
227
- return () => { cancelled = true; };
228
- }, [chat.activeSessionId, chat.setProvider]);
333
+ void listSessionSummaries(getWorkingDir())
334
+ .then(async (sessions) => {
335
+ const currentSession = sessions.find(
336
+ (session) => session.id === chat.activeSessionId,
337
+ );
338
+ const preferredModel =
339
+ currentSession?.preferredModel ??
340
+ (currentSession?.modelId
341
+ ? modelRefFromLegacy(currentSession.modelId)
342
+ : null);
343
+ let provider: Provider | null = null;
344
+ let fallbackReason: import('../modelRef.js').ModelFallbackReason = null;
345
+ if (preferredModel) {
346
+ const resolved = await configService.resolveModelRef(preferredModel);
347
+ provider = resolved.provider;
348
+ fallbackReason = resolved.fallbackReason;
349
+ if (!provider && preferredModel.source === 'gateway')
350
+ provider = await configService.providerForModel(null);
351
+ } else {
352
+ provider = await configService.onlineDefaultProvider();
353
+ if (!provider) provider = await configService.providerForModel(null);
354
+ }
355
+ if (cancelled) return;
356
+ if (provider) {
357
+ const localModelId =
358
+ currentSession?.preferredModel?.source === 'local'
359
+ ? currentSession.preferredModel.modelId
360
+ : currentSession?.modelId &&
361
+ !currentSession.modelId.startsWith('online:')
362
+ ? currentSession.modelId
363
+ : ((await configService.getSnapshot()).config?.baseModelId ??
364
+ provider.model);
365
+ const effectiveModel =
366
+ provider.source === 'gateway'
367
+ ? {
368
+ source: 'gateway' as const,
369
+ serverId: provider.gatewayUrl ?? 'gateway',
370
+ modelId: provider.remoteModelId ?? provider.model,
371
+ }
372
+ : { source: 'local' as const, modelId: localModelId };
373
+ const isGatewayPreference = preferredModel?.source === 'gateway';
374
+ const shouldPersist =
375
+ (provider.source === 'gateway' && !preferredModel) ||
376
+ (isGatewayPreference &&
377
+ (currentSession?.effectiveModel?.source !==
378
+ effectiveModel.source ||
379
+ currentSession?.effectiveModel?.modelId !==
380
+ effectiveModel.modelId ||
381
+ currentSession.fallbackReason !== fallbackReason));
382
+ chat.setProvider(provider);
383
+ if (
384
+ shouldPersist ||
385
+ (isGatewayPreference && provider.source === 'local')
386
+ ) {
387
+ await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, {
388
+ ...(provider.source === 'gateway' && !preferredModel
389
+ ? { preferredModel: effectiveModel }
390
+ : {}),
391
+ effectiveModel,
392
+ fallbackReason:
393
+ provider.source === 'local' ? fallbackReason : null,
394
+ restoreWhenAvailable:
395
+ isGatewayPreference || provider.source === 'gateway',
396
+ }).catch(() => {});
397
+ if (isGatewayPreference && provider.source === 'local')
398
+ chat.ephemeralLine(
399
+ `[/model] 在线模型暂不可用,已回退本地模型(${fallbackReason ?? 'offline'});服务恢复后将自动恢复。`,
400
+ );
401
+ else if (
402
+ isGatewayPreference &&
403
+ provider.source === 'gateway' &&
404
+ currentSession?.fallbackReason
405
+ )
406
+ chat.ephemeralLine(
407
+ `[/model] 在线模型已恢复,当前 Session 已恢复在线模型 ${provider.name}。`,
408
+ );
409
+ }
410
+ } else if (preferredModel?.source === 'gateway') {
411
+ chat.ephemeralLine(
412
+ '[/model] 在线模型不可用,且未配置本地模型;请连接团队服务器或配置本地模型。',
413
+ );
414
+ }
415
+ })
416
+ .catch(() => {});
417
+ return () => {
418
+ cancelled = true;
419
+ };
420
+ }, [chat.activeSessionId, chat.setProvider, chat.ephemeralLine]);
229
421
 
230
422
  const handleAdvisorModelSelect = React.useCallback(
231
423
  async (modelId: string | null) => {
232
424
  setAdvisorModelPicker(null);
233
425
  if (modelId === null) {
234
- await configService.savePatch({ advisor: { modelId: null, enabled: false } });
426
+ await configService.savePatch({
427
+ advisor: { modelId: null, enabled: false },
428
+ });
235
429
  chat.ephemeralLine('[/advisor] 已关闭顾问模型。');
236
430
  return;
237
431
  }
432
+ const selectedRef = modelRefFromLegacy(modelId);
433
+ if (selectedRef.source === 'gateway') {
434
+ const provider = await configService.providerForRef(selectedRef);
435
+ if (!provider) {
436
+ chat.ephemeralLine(
437
+ '[/advisor] 在线模型不可用、未授权或设备凭据已过期。',
438
+ );
439
+ return;
440
+ }
441
+ await configService.savePatch({ advisor: { modelId, enabled: true } });
442
+ chat.ephemeralLine(
443
+ `[/advisor] 已设置顾问模型为 ${provider.name}(在线)。`,
444
+ );
445
+ return;
446
+ }
238
447
  const snap = await configService.getSnapshot();
239
448
  const model = snap.config?.models.find((m) => m.id === modelId);
240
449
  if (!model) {
@@ -242,7 +451,9 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
242
451
  return;
243
452
  }
244
453
  await configService.savePatch({ advisor: { modelId, enabled: true } });
245
- chat.ephemeralLine(`[/advisor] 已设置顾问模型为 ${model.displayName || model.model}(${formatProviderName(model.apiFormat)})。`);
454
+ chat.ephemeralLine(
455
+ `[/advisor] 已设置顾问模型为 ${model.displayName || model.model}(${formatProviderName(model.apiFormat)})。`,
456
+ );
246
457
  },
247
458
  [chat.ephemeralLine],
248
459
  );
@@ -270,7 +481,9 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
270
481
  async (text: string) => {
271
482
  const trimmed = text.trim();
272
483
  const isBareSessionCmd =
273
- trimmed === '/session' || trimmed === '/sessions' || trimmed === '/resume';
484
+ trimmed === '/session' ||
485
+ trimmed === '/sessions' ||
486
+ trimmed === '/resume';
274
487
  const sessionArgMatch = /^\/(?:session|resume)\s+(.+)$/.exec(trimmed);
275
488
 
276
489
  if (trimmed === '/workspaces') {
@@ -279,10 +492,19 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
279
492
  }
280
493
 
281
494
  if (trimmed === '/copy') {
282
- const latest = [...historyRef.current].reverse().find(
283
- (message) => message.role === 'assistant' && typeof message.content === 'string' && message.content.length > 0,
284
- );
285
- if (!latest || latest.role !== 'assistant' || typeof latest.content !== 'string') {
495
+ const latest = [...historyRef.current]
496
+ .reverse()
497
+ .find(
498
+ (message) =>
499
+ message.role === 'assistant' &&
500
+ typeof message.content === 'string' &&
501
+ message.content.length > 0,
502
+ );
503
+ if (
504
+ !latest ||
505
+ latest.role !== 'assistant' ||
506
+ typeof latest.content !== 'string'
507
+ ) {
286
508
  chat.ephemeralLine('[/copy] 当前会话还没有可复制的 AI 最终回复。');
287
509
  return;
288
510
  }
@@ -290,7 +512,9 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
290
512
  await writeClipboardText(latest.content);
291
513
  chat.ephemeralLine('[/copy] 已复制最近一条 AI 最终回复。');
292
514
  } catch (error) {
293
- chat.ephemeralLine(`[/copy] ${error instanceof Error ? error.message : String(error)}`);
515
+ chat.ephemeralLine(
516
+ `[/copy] ${error instanceof Error ? error.message : String(error)}`,
517
+ );
294
518
  }
295
519
  return;
296
520
  }
@@ -302,7 +526,7 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
302
526
  if (sub === 'help') {
303
527
  const helperAvailable = computerUseService.available();
304
528
  let helperBackend = '未知';
305
- let modeLabel = chat.computerUseMode;
529
+ const modeLabel = chat.computerUseMode;
306
530
  try {
307
531
  const workspace = await workspaceIndexService
308
532
  .findByPath(getWorkingDir())
@@ -315,7 +539,9 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
315
539
  allowedFileRoots: [getWorkingDir()],
316
540
  supervised: true,
317
541
  });
318
- const resp = await computerUseService.request(context, { action: 'status' });
542
+ const resp = await computerUseService.request(context, {
543
+ action: 'status',
544
+ });
319
545
  if (resp.ok && resp.state?.backend) {
320
546
  helperBackend = resp.state.backend;
321
547
  }
@@ -432,7 +658,7 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
432
658
  .map((window) => window.appId)
433
659
  .filter((id, index, all) => all.indexOf(id) === index);
434
660
  chat.ephemeralLine(
435
- `[/computer] 可见应用:${apps?.length ? apps.join('、') : response.content ?? '当前没有可见应用'}`,
661
+ `[/computer] 可见应用:${apps?.length ? apps.join('、') : (response.content ?? '当前没有可见应用')}`,
436
662
  );
437
663
  return;
438
664
  }
@@ -444,11 +670,7 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
444
670
  }
445
671
  // status
446
672
  chat.ephemeralLine(
447
- `[/computer] 状态:\n` +
448
- ` 模式:${chat.computerUseMode}\n` +
449
- ` Helper:${computerUseService.available() ? '✅ 可用' : '❌ 不可用'}\n` +
450
- ` Backend:${response.state?.backend ?? '未知'}\n` +
451
- ` 提示:输入 /computer help 查看所有可用命令`,
673
+ `[/computer] 状态:\n 模式:${chat.computerUseMode}\n Helper:${computerUseService.available() ? '✅ 可用' : '❌ 不可用'}\n Backend:${response.state?.backend ?? '未知'}\n 提示:输入 /computer help 查看所有可用命令`,
452
674
  );
453
675
  return;
454
676
  }
@@ -461,20 +683,30 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
461
683
  }
462
684
 
463
685
  if (trimmed === '/memories') {
464
- const workspace = await workspaceIndexService.findByPath(getWorkingDir()).catch(() => null);
686
+ const workspace = await workspaceIndexService
687
+ .findByPath(getWorkingDir())
688
+ .catch(() => null);
465
689
  const records = await memoryService.recall('', workspace?.id, 100);
466
690
  if (!records.length) {
467
691
  chat.ephemeralLine('[/memories] 当前没有本机长期记忆。');
468
692
  return;
469
693
  }
470
- chat.ephemeralLine(`[/memories] 本机长期记忆:\n${records.map((record) =>
471
- ` ${record.id} · ${record.scope === 'personal' ? '个人' : '工作区'} · ${record.subject}: ${record.statement}`).join('\n')}\n使用 /forget-memory <id> 删除一条记忆。`);
694
+ chat.ephemeralLine(
695
+ `[/memories] 本机长期记忆:\n${records
696
+ .map(
697
+ (record) =>
698
+ ` ${record.id} · ${record.scope === 'personal' ? '个人' : '工作区'} · ${record.subject}: ${record.statement}`,
699
+ )
700
+ .join('\n')}\n使用 /forget-memory <id> 删除一条记忆。`,
701
+ );
472
702
  return;
473
703
  }
474
704
 
475
705
  if (trimmed.startsWith('/forget-memory ')) {
476
706
  const id = trimmed.slice('/forget-memory '.length).trim();
477
- const workspace = await workspaceIndexService.findByPath(getWorkingDir()).catch(() => null);
707
+ const workspace = await workspaceIndexService
708
+ .findByPath(getWorkingDir())
709
+ .catch(() => null);
478
710
  const records = await memoryService.recall('', workspace?.id, 100);
479
711
  const record = records.find((item) => item.id === id);
480
712
  if (!record) {
@@ -520,7 +752,9 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
520
752
  )
521
753
  .join('\n');
522
754
  const tail =
523
- entries.length > 30 ? `\n ...(共 ${entries.length} 条,仅显示前 30)` : '';
755
+ entries.length > 30
756
+ ? `\n ...(共 ${entries.length} 条,仅显示前 30)`
757
+ : '';
524
758
  chat.ephemeralLine(
525
759
  `[/list-archive] 归档列表(最新优先):\n${lines}${tail}`,
526
760
  );
@@ -540,7 +774,10 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
540
774
  return;
541
775
  }
542
776
  // 覆盖当前 active session 文件;强制 reload 让 history 替换
543
- const targetFile = sessionFileFor(getWorkingDir(), chat.activeSessionId);
777
+ const targetFile = sessionFileFor(
778
+ getWorkingDir(),
779
+ chat.activeSessionId,
780
+ );
544
781
  await saveContext(messages, targetFile);
545
782
  await chat.switchSession(chat.activeSessionId, { force: true });
546
783
  chat.ephemeralLine(
@@ -571,45 +808,226 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
571
808
  return;
572
809
  }
573
810
 
574
- // /plan <目标> —— 便捷别名,等价 /workflow plan(跑 plan 工作流:
575
- // 调研 → AskUserQuestion 选择 → 产出 ./PLAN.md)。改写后透传给 pluginRunner。
576
- // 目标里的引号剥掉,避免破坏 workflow 参数 tokenizer("..." 整体当一个位置参数)。
577
- if (text.startsWith('/plan ') || text.trim() === '/plan') {
578
- const goal = text.slice('/plan'.length).trim().replace(/"/g, '');
579
- if (goal.length === 0) {
811
+ // ⚠️ 这里曾有一个 `/plan` 的硬编码特例(改写成 `/workflow plan`)。
812
+ // 已删 —— `src/plugins/slashFallback.ts` 把它通用化了:`/xxx` 没命中插件
813
+ // 命令时会按「工具 → skill → 工作流目录」继续找,所以 `/plan`、`/research`、
814
+ // `/pptx` 全都自动可用,加一条工作流不用再回来改 TUI。
815
+
816
+ // /model —— 列出已配置的基座模型并切换选择器;/model <id> 直接切换
817
+ // /login /logout —— 团队在线模型登录;不登录不影响本机模型(原 /team,改名理由见 loginCommand.ts)。
818
+ if (isLogoutCommand(trimmed)) {
819
+ // A standalone session can retain an old auth file from a previous
820
+ // team run. Only revoke over the network when the credential matches
821
+ // the currently configured server; otherwise just clear it locally.
822
+ const configuredServer = await configService.resolveTeamServerUrl();
823
+ if (configuredServer.value) {
824
+ const credentials = await loadTeamCredentials(configuredServer.value);
825
+ if (credentials) await revokeTeamCredentials(credentials);
826
+ else await clearTeamCredentials();
827
+ } else {
828
+ // No configured server is the standalone boundary. Do not let
829
+ // revokeTeamCredentials() discover and contact a stale auth file.
830
+ await clearTeamCredentials();
831
+ }
832
+ chat.ephemeralLine(
833
+ '[/logout] 已登出并撤销本机设备授权;本地模型配置和 Session 未受影响。',
834
+ );
835
+ return;
836
+ }
837
+ // 发起一次登录尝试。普通用户只使用发行版/配置中的服务端地址。
838
+ const attemptTeamLogin = async (server?: string, sourceNote?: string) => {
839
+ if (sourceNote) chat.ephemeralLine(`[/login] ${sourceNote}`);
840
+ try {
841
+ const resolved = server
842
+ ? { value: server, source: 'explicit' }
843
+ : await configService.resolveTeamServerUrl();
844
+ if (!resolved.value)
845
+ throw new Error('KakadeAI 登录服务暂时不可用,请稍后重试');
846
+ const login = await beginTeamLogin(
847
+ resolved.value,
848
+ 'minimal-agent TUI',
849
+ );
580
850
  chat.ephemeralLine(
581
- '[/plan] 用法:/plan <目标>(= /workflow plan:调研→提问选择→产出 ./PLAN.md)',
851
+ `[/login] 已打开浏览器授权:${login.authorizeUrl}`,
582
852
  );
853
+ await openTeamBrowser(login.authorizeUrl);
854
+ await login.complete();
855
+ chat.ephemeralLine(
856
+ '[/login] 登录完成;使用 /login models 查看在线模型。',
857
+ );
858
+ } catch (error) {
859
+ chat.ephemeralLine(
860
+ `[/login] ${error instanceof Error ? error.message : String(error)}`,
861
+ );
862
+ }
863
+ };
864
+ const loginCommand = parseLoginCommand(trimmed);
865
+ if (loginCommand.matched) {
866
+ if (loginCommand.command === 'help') {
867
+ chat.ephemeralLine(loginHelpText());
868
+ return;
869
+ }
870
+ if (loginCommand.command === 'login') {
871
+ await attemptTeamLogin(loginCommand.server);
872
+ return;
873
+ }
874
+ const configuredServer = await configService.resolveTeamServerUrl();
875
+ const credentials = configuredServer.value
876
+ ? await loadTeamCredentials(configuredServer.value)
877
+ : null;
878
+ if (loginCommand.command === 'models') {
879
+ if (!credentials) {
880
+ chat.ephemeralLine(
881
+ '[/login models] 未登录;当前仅使用本机模型。输入 /login 登录。',
882
+ );
883
+ return;
884
+ }
885
+ try {
886
+ const models = await fetchOnlineModels(credentials);
887
+ chat.ephemeralLine(
888
+ models.length
889
+ ? models
890
+ .map(
891
+ (model) =>
892
+ `${model.displayName} · ${model.model}${model.isDefault ? ' · 默认' : ''}${model.availability?.reason ? ` · ${model.availability.reason}` : ''}`,
893
+ )
894
+ .join('\n')
895
+ : '没有可用在线模型。',
896
+ );
897
+ } catch (error) {
898
+ chat.ephemeralLine(
899
+ `[/login models] ${error instanceof Error ? error.message : String(error)}`,
900
+ );
901
+ }
902
+ return;
903
+ }
904
+ if (loginCommand.command === 'usage') {
905
+ if (!credentials) {
906
+ chat.ephemeralLine('[/login usage] 未登录。');
907
+ return;
908
+ }
909
+ try {
910
+ const subscription = await fetchTeamSubscription(credentials);
911
+ const usage = await fetchTeamUsage(credentials);
912
+ chat.ephemeralLine(
913
+ subscription
914
+ ? `[/login usage] ${subscription.planName} · ${subscription.status}\n请求 ${subscription.requestCount}/${subscription.requestLimit} · 输入 ${subscription.inputTokens}/${subscription.inputTokenLimit} · 输出 ${subscription.outputTokens}/${subscription.outputTokenLimit}\n模型记录 ${usage.length} 条`
915
+ : '[/login usage] 当前没有有效订阅。',
916
+ );
917
+ } catch (error) {
918
+ chat.ephemeralLine(
919
+ `[/login usage] ${error instanceof Error ? error.message : String(error)}`,
920
+ );
921
+ }
922
+ return;
923
+ }
924
+ if (loginCommand.command === 'status') {
925
+ if (!credentials) {
926
+ chat.ephemeralLine(
927
+ '[/login status] 未登录;当前仅使用本机模型。输入 /login 登录。',
928
+ );
929
+ } else {
930
+ try {
931
+ const online = await fetchOnlineModels(credentials);
932
+ chat.ephemeralLine(
933
+ `[/login status] 已连接 ${credentials.serverUrl};可用在线模型 ${online.length} 个。/logout 登出。`,
934
+ );
935
+ } catch (error) {
936
+ chat.ephemeralLine(
937
+ `[/login status] 设备凭据存在,但在线服务不可用:${error instanceof Error ? error.message : String(error)};当前使用本机模型。`,
938
+ );
939
+ }
940
+ }
941
+ return;
942
+ }
943
+ if (loginCommand.command === 'tools') {
944
+ const current = (await listSessionSummaries(getWorkingDir())).find(
945
+ (session) => session.id === chat.activeSessionId,
946
+ );
947
+ const value = loginCommand.value;
948
+ if (!value || value === 'list') {
949
+ chat.ephemeralLine(
950
+ `[/login tools] 当前 Session 类别:${current?.onlineToolCategories?.join(', ') || '未设置(使用服务器模型预设)'}\n可选:workspace web computer subagents integrations\n设置:/login tools workspace,web`,
951
+ );
952
+ } else {
953
+ const categories = [
954
+ ...new Set(value.split(/[ ,]+/).filter(Boolean)),
955
+ ];
956
+ const allowed = new Set([
957
+ 'workspace',
958
+ 'web',
959
+ 'computer',
960
+ 'subagents',
961
+ 'integrations',
962
+ ]);
963
+ if (categories.some((category) => !allowed.has(category))) {
964
+ chat.ephemeralLine(
965
+ '[/login tools] 类别无效,可用:workspace web computer subagents integrations',
966
+ );
967
+ return;
968
+ }
969
+ await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, {
970
+ onlineToolCategories: categories,
971
+ });
972
+ chat.ephemeralLine(
973
+ `[/login tools] 已设置当前 Session 工具类别:${categories.join(', ') || '无'}`,
974
+ );
975
+ }
583
976
  return;
584
977
  }
585
- void chat.submit(`/workflow plan "${goal}"`);
586
- return;
587
978
  }
588
979
 
589
- // /model —— 列出已配置的基座模型并切换选择器;/model <id> 直接切换
590
980
  const modelArgMatch = /^\/model(?:\s+(.+))?$/.exec(trimmed);
591
981
  if (modelArgMatch) {
592
982
  const arg = modelArgMatch[1]?.trim();
593
983
  const models = await configService.models();
594
- const enabledModels = models.filter((m) => m.enabled && m.apiKey);
595
- if (enabledModels.length === 0) {
596
- chat.ephemeralLine('[/model] 尚未配置任何模型,请先通过 Desktop 设置或 /config 添加模型。');
984
+ const enabledModels: PickerModel[] = models.filter(
985
+ (m) => m.enabled && m.apiKey,
986
+ );
987
+ const configuredServer = await configService.resolveTeamServerUrl();
988
+ const credentials = configuredServer.value
989
+ ? await loadTeamCredentials(configuredServer.value)
990
+ : null;
991
+ const online = credentials
992
+ ? await fetchOnlineModels(credentials).catch((error) => {
993
+ chat.ephemeralLine(
994
+ `[/model] 在线模型暂时不可用:${error instanceof Error ? error.message : String(error)};仍可选择本机模型。`,
995
+ );
996
+ return [];
997
+ })
998
+ : [];
999
+ const onlinePickerModels: PickerModel[] = online.map(toPickerModel);
1000
+ const pickerModels = [...enabledModels, ...onlinePickerModels];
1001
+ if (pickerModels.length === 0) {
1002
+ chat.ephemeralLine(
1003
+ '[/model] 尚未配置任何模型,请先通过 Desktop 设置或 /config 添加模型。',
1004
+ );
597
1005
  return;
598
1006
  }
599
1007
  if (arg) {
600
1008
  // 直接切换到指定模型(支持精确匹配 id 或模糊匹配 displayName/model)
601
- const target = enabledModels.find((m) => m.id === arg || m.model === arg || m.displayName === arg);
1009
+ const target = pickerModels.find(
1010
+ (m) => m.id === arg || m.model === arg || m.displayName === arg,
1011
+ );
602
1012
  if (!target) {
603
- chat.ephemeralLine(`[/model] 未找到模型 "${arg}",可用模型:\n${enabledModels.map((m) => ` ${m.id} · ${m.displayName || m.model}`).join('\n')}`);
1013
+ chat.ephemeralLine(
1014
+ `[/model] 未找到模型 "${arg}",可用模型:\n${pickerModels.map((m) => ` ${m.id} · ${m.displayName || m.model}`).join('\n')}`,
1015
+ );
604
1016
  return;
605
1017
  }
606
1018
  await handleBaseModelSelect(target.id);
607
1019
  return;
608
1020
  }
609
1021
  // 无参数:打开交互选择器
610
- const currentSession = (await listSessionSummaries(getWorkingDir())).find((session) => session.id === chat.activeSessionId);
1022
+ const currentSession = (
1023
+ await listSessionSummaries(getWorkingDir())
1024
+ ).find((session) => session.id === chat.activeSessionId);
611
1025
  const snap = await configService.getSnapshot();
612
- setBaseModelPicker({ models: enabledModels, currentId: currentSession?.modelId ?? snap.config?.baseModelId ?? null });
1026
+ const currentRef = currentSession?.preferredModel;
1027
+ const currentId = currentRef
1028
+ ? legacyModelId(currentRef)
1029
+ : (currentSession?.modelId ?? snap.config?.baseModelId ?? null);
1030
+ setBaseModelPicker({ models: pickerModels, currentId });
613
1031
  return;
614
1032
  }
615
1033
 
@@ -645,42 +1063,70 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
645
1063
 
646
1064
  if (sub === 'on') {
647
1065
  if (!advisor) {
648
- chat.ephemeralLine(
649
- '[/advisor] 未配置顾问模型,无法开启。',
650
- );
1066
+ chat.ephemeralLine('[/advisor] 未配置顾问模型,无法开启。');
651
1067
  } else {
652
1068
  await configService.savePatch({ advisor: { enabled: true } });
653
- chat.ephemeralLine(`[/advisor] 顾问模型已开启(${advisor.provider.name}/${advisor.provider.model})。`);
1069
+ chat.ephemeralLine(
1070
+ `[/advisor] 顾问模型已开启(${advisor.provider.name}/${advisor.provider.model})。`,
1071
+ );
654
1072
  }
655
1073
  return;
656
1074
  }
657
1075
 
658
1076
  if (sub === 'off') {
659
- if (advisor) await configService.savePatch({ advisor: { enabled: false } });
1077
+ if (advisor)
1078
+ await configService.savePatch({ advisor: { enabled: false } });
660
1079
  chat.ephemeralLine('[/advisor] 顾问模型已关闭。');
661
1080
  return;
662
1081
  }
663
1082
 
664
1083
  if (sub === 'select') {
665
1084
  const models = await configService.models();
666
- const enabledModels = models.filter((m) => m.enabled && m.apiKey);
667
- if (enabledModels.length === 0) {
1085
+ const enabledModels: PickerModel[] = models.filter(
1086
+ (m) => m.enabled && m.apiKey,
1087
+ );
1088
+ const configuredServer = await configService.resolveTeamServerUrl();
1089
+ const credentials = configuredServer.value
1090
+ ? await loadTeamCredentials(configuredServer.value)
1091
+ : null;
1092
+ const online = credentials
1093
+ ? await fetchOnlineModels(credentials).catch((error) => {
1094
+ chat.ephemeralLine(
1095
+ `[/advisor] 在线顾问模型暂时不可用:${error instanceof Error ? error.message : String(error)};仍可选择本机顾问模型。`,
1096
+ );
1097
+ return [];
1098
+ })
1099
+ : [];
1100
+ const pickerModels = [...enabledModels, ...online.map(toPickerModel)];
1101
+ if (pickerModels.length === 0) {
668
1102
  chat.ephemeralLine('[/advisor] 尚未配置任何模型,请先添加模型。');
669
1103
  return;
670
1104
  }
671
1105
  if (note) {
672
1106
  // /advisor select <id> 直接指定模型
673
- const target = enabledModels.find((m) => m.id === note || m.model === note || m.displayName === note);
1107
+ const target = pickerModels.find(
1108
+ (m) =>
1109
+ m.id === note || m.model === note || m.displayName === note,
1110
+ );
674
1111
  if (!target) {
675
- chat.ephemeralLine(`[/advisor] 未找到模型 "${note}",可用模型:\n${enabledModels.map((m) => ` ${m.id} · ${m.displayName || m.model}`).join('\n')}`);
1112
+ chat.ephemeralLine(
1113
+ `[/advisor] 未找到模型 "${note}",可用模型:\n${pickerModels.map((m) => ` ${m.id} · ${m.displayName || m.model}`).join('\n')}`,
1114
+ );
676
1115
  return;
677
1116
  }
678
- await configService.savePatch({ advisor: { modelId: target.id, enabled: true } });
679
- chat.ephemeralLine(`[/advisor] 已设置顾问模型为 ${target.displayName || target.model}(${formatProviderName(target.apiFormat)})。`);
1117
+ await configService.savePatch({
1118
+ advisor: { modelId: target.id, enabled: true },
1119
+ });
1120
+ chat.ephemeralLine(
1121
+ `[/advisor] 已设置顾问模型为 ${target.displayName || target.model}(${formatProviderName(target.apiFormat)})。`,
1122
+ );
680
1123
  } else {
681
1124
  // 无参数:打开交互选择器(含"关闭"选项)
682
1125
  const snap = await configService.getSnapshot();
683
- setAdvisorModelPicker({ models: enabledModels, currentId: snap.config?.advisor?.modelId ?? null });
1126
+ setAdvisorModelPicker({
1127
+ models: pickerModels,
1128
+ currentId: snap.config?.advisor?.modelId ?? null,
1129
+ });
684
1130
  }
685
1131
  return;
686
1132
  }
@@ -688,11 +1134,15 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
688
1134
  // sub === 'ask':立即请求顾问意见。走 chat.runManualAdvisor 而不是裸 runAdvisor——
689
1135
  // 它负责过程显示(LiveArea 进度行)与 ESC 中断,否则最长 180 秒 TUI 全静默。
690
1136
  if (!advisor) {
691
- chat.ephemeralLine('[/advisor] 未配置顾问模型:先用 /advisor select 选一个,或在 Desktop 设置 → 模型中配置。');
1137
+ chat.ephemeralLine(
1138
+ '[/advisor] 未配置顾问模型:先用 /advisor select 选一个,或在 Desktop 设置 → 模型中配置。',
1139
+ );
692
1140
  return;
693
1141
  }
694
1142
  if (!advisor.enabled) {
695
- chat.ephemeralLine('[/advisor] 顾问模型当前已关闭;/advisor on 开启后再咨询。');
1143
+ chat.ephemeralLine(
1144
+ '[/advisor] 顾问模型当前已关闭;/advisor on 开启后再咨询。',
1145
+ );
696
1146
  return;
697
1147
  }
698
1148
  const result = await chat.runManualAdvisor(note);
@@ -725,7 +1175,6 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
725
1175
  chat.activeSessionId,
726
1176
  chat.submit,
727
1177
  chat.runManualAdvisor,
728
- chat.setProvider,
729
1178
  chat.computerUseMode,
730
1179
  chat.setComputerUseMode,
731
1180
  chat.stopComputerUse,
@@ -735,7 +1184,6 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
735
1184
  handleSessionCreate,
736
1185
  openWorkspacePicker,
737
1186
  handleBaseModelSelect,
738
- handleAdvisorModelSelect,
739
1187
  ],
740
1188
  );
741
1189
 
@@ -784,10 +1232,19 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
784
1232
  <QueuedMessagesBanner items={chat.queuedItems} />
785
1233
 
786
1234
  {chat.pendingComputerIntervention && (
787
- <Box flexDirection="column" borderStyle="round" borderColor={theme.warning} paddingX={1} marginBottom={1}>
788
- <Text color={theme.warning}>需要人工操作:{chat.pendingComputerIntervention.sanitizedSummary}</Text>
1235
+ <Box
1236
+ flexDirection="column"
1237
+ borderStyle="round"
1238
+ borderColor={theme.warning}
1239
+ paddingX={1}
1240
+ marginBottom={1}
1241
+ >
1242
+ <Text color={theme.warning}>
1243
+ 需要人工操作:{chat.pendingComputerIntervention.sanitizedSummary}
1244
+ </Text>
789
1245
  <Text dimColor>
790
- 请直接在目标应用中处理;敏感内容不会进入终端或模型。完成后输入 /computer resume,取消输入 /computer cancel。
1246
+ 请直接在目标应用中处理;敏感内容不会进入终端或模型。完成后输入
1247
+ /computer resume,取消输入 /computer cancel。
791
1248
  </Text>
792
1249
  </Box>
793
1250
  )}
@@ -802,8 +1259,13 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
802
1259
  {/* 中断提示 */}
803
1260
  {chat.interrupted && (
804
1261
  <Box marginBottom={1}>
805
- <Text color={theme.warning}>⚡ 操作被用户中断,等待新的任务输入...</Text>
806
- <Text dimColor> 调整上一次计划:直接说调整内容 / 重新跑 /plan 目标 / 新任务直接说</Text>
1262
+ <Text color={theme.warning}>
1263
+ ⚡ 操作被用户中断,等待新的任务输入...
1264
+ </Text>
1265
+ <Text dimColor>
1266
+ {' '}
1267
+ 调整上一次计划:直接说调整内容 / 重新跑 /plan 目标 / 新任务直接说
1268
+ </Text>
807
1269
  </Box>
808
1270
  )}
809
1271
 
@@ -812,7 +1274,10 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
812
1274
  或普通 InputBox——都用 useInput,同时挂会抢键 → 必须互斥渲染。
813
1275
  选完/取消/关闭后各自状态归位,InputBox 自动回归。 */}
814
1276
  {configMenuOpen ? (
815
- <ConfigMenu mode="reconfigure" onDone={() => setConfigMenuOpen(false)} />
1277
+ <ConfigMenu
1278
+ mode="reconfigure"
1279
+ onDone={() => setConfigMenuOpen(false)}
1280
+ />
816
1281
  ) : baseModelPicker ? (
817
1282
  <ModelPicker
818
1283
  models={baseModelPicker.models}