minimal-agent 0.10.0 → 0.12.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 (573) hide show
  1. package/README.md +11 -3
  2. package/package.json +52 -5
  3. package/plugins/computer_use/.claude-plugin/plugin.json +6 -0
  4. package/plugins/computer_use/README.md +94 -0
  5. package/plugins/computer_use/app/_template.md +142 -0
  6. package/plugins/computer_use/app/chrome.md +165 -0
  7. package/plugins/computer_use/app/finder.md +76 -0
  8. package/plugins/computer_use/app/macos-system-settings.md +81 -0
  9. package/plugins/computer_use/app/microsoft-excel.md +80 -0
  10. package/plugins/computer_use/app/microsoft-powerpoint.md +80 -0
  11. package/plugins/computer_use/app/microsoft-word.md +79 -0
  12. package/plugins/computer_use/app/wechat.md +413 -0
  13. package/plugins/computer_use/app/weread.md +319 -0
  14. package/plugins/computer_use/commands/computeruse.md +25 -0
  15. package/plugins/computer_use/commands/cu.md +9 -0
  16. package/plugins/computer_use/plugin.js +26 -0
  17. package/plugins/computer_use/plugin.ts +30 -0
  18. package/plugins/computer_use/src/capture.js +90 -0
  19. package/plugins/computer_use/src/capture.ts +104 -0
  20. package/plugins/computer_use/src/commands.js +91 -0
  21. package/plugins/computer_use/src/commands.ts +103 -0
  22. package/plugins/computer_use/src/daemon.js +134 -0
  23. package/plugins/computer_use/src/daemon.ts +161 -0
  24. package/plugins/computer_use/src/manuals.js +135 -0
  25. package/plugins/computer_use/src/manuals.ts +159 -0
  26. package/plugins/computer_use/src/verify.js +168 -0
  27. package/plugins/computer_use/src/verify.ts +219 -0
  28. package/plugins/computer_use/test/commands.test.ts +73 -0
  29. package/plugins/cron-runner/plugin.ts +38 -0
  30. package/plugins/cron-runner/src/commands.ts +373 -0
  31. package/plugins/cron-runner/src/cronTime.ts +155 -0
  32. package/plugins/cron-runner/src/lock.ts +79 -0
  33. package/plugins/cron-runner/src/runs.ts +99 -0
  34. package/plugins/cron-runner/src/scheduleEngine.ts +76 -0
  35. package/plugins/cron-runner/src/scheduler.ts +376 -0
  36. package/plugins/cron-runner/src/schema.ts +139 -0
  37. package/plugins/cron-runner/src/service.ts +193 -0
  38. package/plugins/cron-runner/src/store.ts +333 -0
  39. package/plugins/cron-runner/src/types.ts +141 -0
  40. package/plugins/cron-runner/test/cronTime.test.ts +41 -0
  41. package/plugins/cron-runner/test/plugin-integration.test.ts +47 -0
  42. package/plugins/cron-runner/test/scheduleEngine.test.ts +41 -0
  43. package/plugins/cron-runner/test/service.test.ts +76 -0
  44. package/plugins/cron-runner/test/store-and-scheduler.test.ts +501 -0
  45. package/plugins/workflow-runner/plugin.js +56 -2
  46. package/plugins/workflow-runner/plugin.ts +113 -0
  47. package/plugins/workflow-runner/src/agentCommon.js +43 -4
  48. package/plugins/workflow-runner/src/agentCommon.ts +435 -0
  49. package/plugins/workflow-runner/src/controlFlow/branch.js +40 -0
  50. package/plugins/workflow-runner/src/controlFlow/branch.ts +46 -0
  51. package/plugins/workflow-runner/src/controlFlow/loop.js +211 -0
  52. package/plugins/workflow-runner/src/controlFlow/loop.ts +248 -0
  53. package/plugins/workflow-runner/src/controlFlow/pause.js +129 -0
  54. package/plugins/workflow-runner/src/controlFlow/pause.ts +141 -0
  55. package/plugins/workflow-runner/src/controlFlow/signal.js +21 -0
  56. package/plugins/workflow-runner/src/controlFlow/signal.ts +29 -0
  57. package/plugins/workflow-runner/src/controlFlow/types.js +18 -0
  58. package/plugins/workflow-runner/src/controlFlow/types.ts +63 -0
  59. package/plugins/workflow-runner/src/dslManifest.ts +127 -0
  60. package/plugins/workflow-runner/src/expressions.ts +622 -0
  61. package/plugins/workflow-runner/src/index.js +13 -4
  62. package/plugins/workflow-runner/src/index.ts +338 -0
  63. package/plugins/workflow-runner/src/jsonPayload.ts +84 -0
  64. package/plugins/workflow-runner/src/loader.js +23 -0
  65. package/plugins/workflow-runner/src/loader.ts +721 -0
  66. package/plugins/workflow-runner/src/multimodal.js +2 -1
  67. package/plugins/workflow-runner/src/multimodal.ts +64 -0
  68. package/plugins/workflow-runner/src/recovery.ts +149 -0
  69. package/plugins/workflow-runner/src/runner.js +75 -659
  70. package/plugins/workflow-runner/src/runner.ts +604 -0
  71. package/plugins/workflow-runner/src/stepExecutors/agent.ts +234 -0
  72. package/plugins/workflow-runner/src/stepExecutors/assert.ts +50 -0
  73. package/plugins/workflow-runner/src/stepExecutors/echo.js +31 -0
  74. package/plugins/workflow-runner/src/stepExecutors/echo.ts +38 -0
  75. package/plugins/workflow-runner/src/stepExecutors/llm.ts +133 -0
  76. package/plugins/workflow-runner/src/stepExecutors/set.js +49 -0
  77. package/plugins/workflow-runner/src/stepExecutors/set.ts +56 -0
  78. package/plugins/workflow-runner/src/stepExecutors/skill.js +5 -6
  79. package/plugins/workflow-runner/src/stepExecutors/skill.ts +140 -0
  80. package/plugins/workflow-runner/src/stepExecutors/tool.js +80 -8
  81. package/plugins/workflow-runner/src/stepExecutors/tool.ts +256 -0
  82. package/plugins/workflow-runner/src/stepExecutors/verify.ts +68 -0
  83. package/plugins/workflow-runner/src/stepPipeline.js +277 -0
  84. package/plugins/workflow-runner/src/stepPipeline.ts +366 -0
  85. package/plugins/workflow-runner/src/taskIdentity.ts +405 -0
  86. package/plugins/workflow-runner/src/toolRuntime.js +48 -0
  87. package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
  88. package/plugins/workflow-runner/src/types.js +37 -1
  89. package/plugins/workflow-runner/src/types.ts +602 -0
  90. package/plugins/workflow-runner/src/workflowState.ts +733 -0
  91. package/plugins/workflow-runner/test/agent.test.ts +587 -0
  92. package/plugins/workflow-runner/test/agentLlmError.test.ts +373 -0
  93. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +65 -0
  94. package/plugins/workflow-runner/test/captureMisbind.test.ts +99 -0
  95. package/plugins/workflow-runner/test/cli.e2e.test.ts +147 -0
  96. package/plugins/workflow-runner/test/context-files.test.ts +320 -0
  97. package/plugins/workflow-runner/test/e2e.test.ts +370 -0
  98. package/plugins/workflow-runner/test/engineRobustness.test.ts +226 -0
  99. package/plugins/workflow-runner/test/expressions.test.ts +397 -0
  100. package/plugins/workflow-runner/test/foundation.test.ts +126 -0
  101. package/plugins/workflow-runner/test/graceful.test.ts +139 -0
  102. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +47 -0
  103. package/plugins/workflow-runner/test/hostCapability.test.ts +108 -0
  104. package/plugins/workflow-runner/test/illustrate.load.test.ts +183 -0
  105. package/plugins/workflow-runner/test/jsonPayload.test.ts +83 -0
  106. package/plugins/workflow-runner/test/loader.test.ts +617 -0
  107. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +33 -0
  108. package/plugins/workflow-runner/test/multimodal.test.ts +342 -0
  109. package/plugins/workflow-runner/test/output-schema.test.ts +213 -0
  110. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +87 -0
  111. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +269 -0
  112. package/plugins/workflow-runner/test/ppt.load.test.ts +1220 -0
  113. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +220 -0
  114. package/plugins/workflow-runner/test/recovery.test.ts +265 -0
  115. package/plugins/workflow-runner/test/runner.test.ts +911 -0
  116. package/plugins/workflow-runner/test/runnerPreflight.test.ts +379 -0
  117. package/plugins/workflow-runner/test/skillTurnDone.test.ts +71 -0
  118. package/plugins/workflow-runner/test/stateLifecycle.test.ts +150 -0
  119. package/plugins/workflow-runner/test/stepPipeline.test.ts +114 -0
  120. package/plugins/workflow-runner/test/taskArbitration.test.ts +338 -0
  121. package/plugins/workflow-runner/test/taskIdentity.test.ts +237 -0
  122. package/plugins/workflow-runner/test/taskLifecycle.test.ts +270 -0
  123. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +221 -0
  124. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +29 -0
  125. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
  126. package/plugins/workflow-runner/test/webppt.load.test.ts +250 -0
  127. package/plugins/workflow-runner/test/word.load.test.ts +208 -0
  128. package/plugins/workflow-runner/test/workflows.smoke.test.ts +76 -0
  129. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +207 -0
  130. package/skills/browser-automation/SKILL.md +9 -19
  131. package/skills/web-crawler/SKILL.md +9 -11
  132. package/src/askBridge.ts +249 -0
  133. package/src/bootstrap/cwdArg.js +10 -3
  134. package/src/bootstrap/cwdArg.ts +31 -0
  135. package/src/bootstrap/workingDir.ts +48 -0
  136. package/src/cli/args.js +70 -7
  137. package/src/cli/args.ts +146 -0
  138. package/src/cli/computerUse.js +185 -0
  139. package/src/cli/computerUse.ts +221 -0
  140. package/src/cli/configMenu.js +274 -202
  141. package/src/cli/configMenu.tsx +521 -0
  142. package/src/cli/memoryAccess.js +45 -0
  143. package/src/cli/memoryAccess.ts +53 -0
  144. package/src/cli/print.js +119 -5
  145. package/src/cli/print.ts +597 -0
  146. package/src/cli/quickModelSetup.js +60 -0
  147. package/src/cli/quickModelSetup.tsx +96 -0
  148. package/src/cli/streamJson.js +80 -4
  149. package/src/cli/streamJson.ts +433 -0
  150. package/src/cli/wizards/advisorWizard.js +1 -0
  151. package/src/cli/wizards/advisorWizard.tsx +195 -0
  152. package/src/cli/wizards/agentRuntimeWizard.js +84 -0
  153. package/src/cli/wizards/agentRuntimeWizard.tsx +134 -0
  154. package/src/cli/wizards/browserWizard.js +55 -0
  155. package/src/cli/wizards/browserWizard.tsx +77 -0
  156. package/src/cli/wizards/contextAdvisorWizard.js +84 -0
  157. package/src/cli/wizards/contextAdvisorWizard.tsx +124 -0
  158. package/src/cli/wizards/fieldForm.js +7 -4
  159. package/src/cli/wizards/fieldForm.tsx +250 -0
  160. package/src/cli/wizards/imageWizard.js +4 -5
  161. package/src/cli/wizards/imageWizard.tsx +96 -0
  162. package/src/cli/wizards/openRouterWizard.tsx +48 -0
  163. package/src/cli/wizards/providerPresets.js +1 -8
  164. package/src/cli/wizards/providerPresets.ts +106 -0
  165. package/src/cli/wizards/providerWizard.js +152 -205
  166. package/src/cli/wizards/providerWizard.tsx +266 -0
  167. package/src/cli/wizards/searchWizard.tsx +47 -0
  168. package/src/cli/wizards/ttsWizard.js +4 -3
  169. package/src/cli/wizards/ttsWizard.tsx +61 -0
  170. package/src/config/configDefaults.js +25 -0
  171. package/src/config/configDefaults.ts +33 -0
  172. package/src/config/configDiagnostics.js +117 -0
  173. package/src/config/configDiagnostics.ts +166 -0
  174. package/src/config/configFile.js +52 -32
  175. package/src/config/configFile.ts +633 -0
  176. package/src/config/configManagement.js +126 -0
  177. package/src/config/configManagement.ts +159 -0
  178. package/src/config/configService.js +66 -11
  179. package/src/config/configService.ts +554 -0
  180. package/src/config/numericLimits.js +61 -0
  181. package/src/config/numericLimits.ts +80 -0
  182. package/src/config.ts +109 -0
  183. package/src/context/agentHome.ts +11 -0
  184. package/src/context/archive.ts +144 -0
  185. package/src/context/budgetPlan.js +54 -0
  186. package/src/context/budgetPlan.ts +88 -0
  187. package/src/context/compact.js +179 -106
  188. package/src/context/compact.ts +584 -0
  189. package/src/context/contextBundle.js +119 -0
  190. package/src/context/contextBundle.ts +176 -0
  191. package/src/context/contextLock.ts +84 -0
  192. package/src/context/microCompactLite.ts +218 -0
  193. package/src/context/persistContext.ts +177 -0
  194. package/src/context/reactiveCompact.js +19 -4
  195. package/src/context/reactiveCompact.ts +240 -0
  196. package/src/context/recentDirs.ts +42 -0
  197. package/src/context/sessionPath.ts +206 -0
  198. package/src/context/sessionRegistry.js +8 -2
  199. package/src/context/sessionRegistry.ts +414 -0
  200. package/src/context/snipCompact.ts +163 -0
  201. package/src/context/tokenCounter.ts +106 -0
  202. package/src/context/workspaceIndex.js +2 -2
  203. package/src/context/workspaceIndex.ts +751 -0
  204. package/src/entry.compiled.tsx +20 -0
  205. package/src/harness/claimValidation.js +9 -0
  206. package/src/harness/claimValidation.ts +22 -0
  207. package/src/harness/evidenceLedger.js +92 -0
  208. package/src/harness/evidenceLedger.ts +96 -0
  209. package/src/harness/stallGuard.js +60 -0
  210. package/src/harness/stallGuard.ts +81 -0
  211. package/src/humanIntervention.js +129 -0
  212. package/src/humanIntervention.ts +216 -0
  213. package/src/infra/verification.ts +295 -0
  214. package/src/irreversible/rules.js +216 -0
  215. package/src/irreversible/rules.ts +261 -0
  216. package/src/llm/client.js +40 -3
  217. package/src/llm/client.ts +621 -0
  218. package/src/llm/promptCache.js +27 -0
  219. package/src/llm/promptCache.ts +41 -0
  220. package/src/llm/providers/anthropicFormat.js +41 -5
  221. package/src/llm/providers/anthropicFormat.ts +290 -0
  222. package/src/llm/providers/openaiFormat.js +186 -8
  223. package/src/llm/providers/openaiFormat.ts +428 -0
  224. package/src/loop.js +614 -63
  225. package/src/loop.ts +1454 -0
  226. package/src/main.js +67 -10
  227. package/src/main.tsx +344 -0
  228. package/src/memory/codec.js +204 -0
  229. package/src/memory/codec.ts +249 -0
  230. package/src/memory/constants.js +34 -0
  231. package/src/memory/constants.ts +50 -0
  232. package/src/memory/episodicMemory.js +684 -0
  233. package/src/memory/episodicMemory.ts +815 -0
  234. package/src/memory/fileStore.js +148 -0
  235. package/src/memory/fileStore.ts +184 -0
  236. package/src/memory/memoryService.js +570 -0
  237. package/src/memory/memoryService.ts +859 -0
  238. package/src/memory/migration.js +128 -0
  239. package/src/memory/migration.ts +160 -0
  240. package/src/memory/repository.js +789 -0
  241. package/src/memory/repository.ts +893 -0
  242. package/src/memory/retrieval.js +108 -0
  243. package/src/memory/retrieval.ts +145 -0
  244. package/src/memory/searchIndex.js +107 -0
  245. package/src/memory/searchIndex.ts +153 -0
  246. package/src/memory/topicView.js +82 -0
  247. package/src/memory/topicView.ts +95 -0
  248. package/src/memory/turnJournal.js +95 -0
  249. package/src/memory/turnJournal.ts +120 -0
  250. package/src/memory/types.js +2 -0
  251. package/src/memory/types.ts +186 -0
  252. package/src/messageQueue.ts +123 -0
  253. package/src/multimodal.js +128 -46
  254. package/src/multimodal.ts +349 -0
  255. package/src/plugin-sdk.js +21 -1
  256. package/src/plugin-sdk.ts +134 -0
  257. package/src/plugins/builtinPluginIds.js +47 -0
  258. package/src/plugins/builtinPluginIds.ts +51 -0
  259. package/src/plugins/builtinRegistry.ts +43 -0
  260. package/src/plugins/commandRouter.js +40 -26
  261. package/src/plugins/commandRouter.ts +261 -0
  262. package/src/plugins/hookEngine.ts +341 -0
  263. package/src/plugins/pluginApi.ts +133 -0
  264. package/src/plugins/pluginLifecycle.ts +34 -0
  265. package/src/plugins/pluginLoader.ts +147 -0
  266. package/src/plugins/pluginManifest.js +127 -0
  267. package/src/plugins/pluginManifest.ts +152 -0
  268. package/src/plugins/pluginRunner.js +47 -2
  269. package/src/plugins/pluginRunner.ts +207 -0
  270. package/src/plugins/transcript.ts +208 -0
  271. package/src/prompts/lint.js +169 -0
  272. package/src/prompts/lint.ts +220 -0
  273. package/src/prompts/pluginList.js +32 -16
  274. package/src/prompts/pluginList.ts +86 -0
  275. package/src/prompts/policyRegistry.js +318 -0
  276. package/src/prompts/policyRegistry.ts +358 -0
  277. package/src/prompts/projectInstructions.js +24 -0
  278. package/src/prompts/projectInstructions.ts +81 -0
  279. package/src/prompts/promptAssembly.js +311 -202
  280. package/src/prompts/promptAssembly.ts +510 -0
  281. package/src/prompts/skillList.js +106 -45
  282. package/src/prompts/skillList.ts +198 -0
  283. package/src/prompts/system.js +31 -6
  284. package/src/prompts/system.ts +81 -0
  285. package/src/runtime/agentRuntimeContext.js +53 -0
  286. package/src/runtime/agentRuntimeContext.ts +112 -0
  287. package/src/session/runAgentTurn.js +71 -12
  288. package/src/session/runAgentTurn.ts +181 -0
  289. package/src/session/runTurn.js +29 -0
  290. package/src/session/runTurn.ts +160 -0
  291. package/src/session/runtimeStore.js +149 -0
  292. package/src/session/runtimeStore.ts +197 -0
  293. package/src/session/sessionState.js +79 -3
  294. package/src/session/sessionState.ts +134 -0
  295. package/src/toolGate.ts +50 -0
  296. package/src/tools/advisor/advisor.js +76 -15
  297. package/src/tools/advisor/advisor.ts +512 -0
  298. package/src/tools/agent/agent.js +245 -29
  299. package/src/tools/agent/agent.ts +837 -0
  300. package/src/tools/agent/agentTaskService.js +60 -0
  301. package/src/tools/agent/agentTaskService.ts +113 -0
  302. package/src/tools/agent/agentTypes.js +20 -8
  303. package/src/tools/agent/agentTypes.ts +94 -0
  304. package/src/tools/agent/coordinator.ts +111 -0
  305. package/src/tools/agent/worktree.ts +296 -0
  306. package/src/tools/ask/ask.js +17 -0
  307. package/src/tools/ask/ask.ts +122 -0
  308. package/src/tools/bash/attachImage.js +223 -0
  309. package/src/tools/bash/attachImage.ts +281 -0
  310. package/src/tools/bash/bash.js +132 -14
  311. package/src/tools/bash/bash.ts +642 -0
  312. package/src/tools/bash/semantics.ts +103 -0
  313. package/src/tools/bash/warnings.js +17 -11
  314. package/src/tools/bash/warnings.ts +117 -0
  315. package/src/tools/capabilities.js +42 -0
  316. package/src/tools/capabilities.ts +69 -0
  317. package/src/tools/computeruse/accessibilityPermission.js +261 -0
  318. package/src/tools/computeruse/accessibilityPermission.ts +294 -0
  319. package/src/tools/computeruse/axSearch.js +96 -0
  320. package/src/tools/computeruse/axSearch.ts +120 -0
  321. package/src/tools/computeruse/axSnap.js +272 -0
  322. package/src/tools/computeruse/axSnap.ts +349 -0
  323. package/src/tools/computeruse/capabilityLabels.js +95 -0
  324. package/src/tools/computeruse/capabilityLabels.ts +115 -0
  325. package/src/tools/computeruse/computerUseService.js +2235 -0
  326. package/src/tools/computeruse/computerUseService.ts +2686 -0
  327. package/src/tools/computeruse/computeruse.js +2742 -0
  328. package/src/tools/computeruse/computeruse.ts +3265 -0
  329. package/src/tools/computeruse/diagnosticTrace.js +154 -0
  330. package/src/tools/computeruse/diagnosticTrace.ts +195 -0
  331. package/src/tools/computeruse/diagnostics.js +369 -0
  332. package/src/tools/computeruse/diagnostics.ts +453 -0
  333. package/src/tools/computeruse/grants.js +53 -0
  334. package/src/tools/computeruse/grants.ts +70 -0
  335. package/src/tools/computeruse/humanBoundary.js +159 -0
  336. package/src/tools/computeruse/humanBoundary.ts +199 -0
  337. package/src/tools/computeruse/intervention.js +77 -0
  338. package/src/tools/computeruse/intervention.ts +110 -0
  339. package/src/tools/computeruse/manualSchema.js +709 -0
  340. package/src/tools/computeruse/manualSchema.ts +916 -0
  341. package/src/tools/computeruse/observationImages.js +705 -0
  342. package/src/tools/computeruse/observationImages.ts +889 -0
  343. package/src/tools/computeruse/protocol.js +60 -0
  344. package/src/tools/computeruse/protocol.ts +515 -0
  345. package/src/tools/computeruse/telemetryLog.js +57 -0
  346. package/src/tools/computeruse/telemetryLog.ts +67 -0
  347. package/src/tools/computeruse/vision/appGuide.js +325 -0
  348. package/src/tools/computeruse/vision/appGuide.ts +482 -0
  349. package/src/tools/computeruse/vision/focus.js +291 -0
  350. package/src/tools/computeruse/vision/focus.ts +382 -0
  351. package/src/tools/computeruse/vision/frames.js +248 -0
  352. package/src/tools/computeruse/vision/frames.ts +329 -0
  353. package/src/tools/computeruse/vision/geometry.js +131 -0
  354. package/src/tools/computeruse/vision/geometry.ts +163 -0
  355. package/src/tools/computeruse/vision/imaging.js +201 -0
  356. package/src/tools/computeruse/vision/imaging.ts +281 -0
  357. package/src/tools/computeruse/vision/png.js +274 -0
  358. package/src/tools/computeruse/vision/png.ts +317 -0
  359. package/src/tools/computeruse/vision/raster.js +320 -0
  360. package/src/tools/computeruse/vision/raster.ts +408 -0
  361. package/src/tools/computeruse/vision/settle.js +131 -0
  362. package/src/tools/computeruse/vision/settle.ts +220 -0
  363. package/src/tools/computeruse/vision/telemetry.js +196 -0
  364. package/src/tools/computeruse/vision/telemetry.ts +241 -0
  365. package/src/tools/computeruse/visionOcr.js +115 -0
  366. package/src/tools/computeruse/visionOcr.ts +128 -0
  367. package/src/tools/computeruse/waitFor.js +237 -0
  368. package/src/tools/computeruse/waitFor.ts +291 -0
  369. package/src/tools/download/download.js +10 -6
  370. package/src/tools/download/download.ts +187 -0
  371. package/src/tools/edit/edit.js +30 -18
  372. package/src/tools/edit/edit.ts +349 -0
  373. package/src/tools/edit/multi-edit.js +25 -14
  374. package/src/tools/edit/multi-edit.ts +223 -0
  375. package/src/tools/effectiveToolSet.js +73 -0
  376. package/src/tools/effectiveToolSet.ts +91 -0
  377. package/src/tools/genimage/genimage.ts +257 -0
  378. package/src/tools/genspeech/genspeech.ts +83 -0
  379. package/src/tools/glob/glob.js +176 -29
  380. package/src/tools/glob/glob.ts +283 -0
  381. package/src/tools/grep/grep.js +61 -19
  382. package/src/tools/grep/grep.ts +239 -0
  383. package/src/tools/grep/rgPath.ts +206 -0
  384. package/src/tools/index.js +99 -5
  385. package/src/tools/index.ts +293 -0
  386. package/src/tools/lsp/config.js +113 -0
  387. package/src/tools/lsp/config.ts +113 -0
  388. package/src/tools/lsp/lsp.js +256 -0
  389. package/src/tools/lsp/lsp.ts +274 -0
  390. package/src/tools/lsp/service.js +303 -0
  391. package/src/tools/lsp/service.ts +296 -0
  392. package/src/tools/memory/memory.js +520 -0
  393. package/src/tools/memory/memory.ts +625 -0
  394. package/src/tools/memory/policy.js +53 -0
  395. package/src/tools/memory/policy.ts +72 -0
  396. package/src/tools/notebook/notebookEdit.js +114 -0
  397. package/src/tools/notebook/notebookEdit.ts +126 -0
  398. package/src/tools/powershell/powershell.js +210 -0
  399. package/src/tools/powershell/powershell.ts +239 -0
  400. package/src/tools/presentresult/presentresult.js +145 -0
  401. package/src/tools/presentresult/presentresult.ts +156 -0
  402. package/src/tools/read/read.js +371 -163
  403. package/src/tools/read/read.ts +558 -0
  404. package/src/tools/shared/diffSummary.js +44 -0
  405. package/src/tools/shared/diffSummary.ts +63 -0
  406. package/src/tools/shared/fileActivity.js +51 -0
  407. package/src/tools/shared/fileActivity.ts +66 -0
  408. package/src/tools/shared/fileMutationBus.js +13 -0
  409. package/src/tools/shared/fileMutationBus.ts +14 -0
  410. package/src/tools/shared/fileState.js +73 -65
  411. package/src/tools/shared/fileState.ts +145 -0
  412. package/src/tools/shared/fileUtils.js +81 -4
  413. package/src/tools/shared/fileUtils.ts +504 -0
  414. package/src/tools/shared/killTree.ts +50 -0
  415. package/src/tools/shared/permanentDeleteGuard.js +26 -0
  416. package/src/tools/shared/permanentDeleteGuard.ts +34 -0
  417. package/src/tools/shared/schemas.ts +20 -0
  418. package/src/tools/shared/toolOutputStore.js +97 -0
  419. package/src/tools/shared/toolOutputStore.ts +110 -0
  420. package/src/tools/skill/skill.js +86 -0
  421. package/src/tools/skill/skill.ts +94 -0
  422. package/src/tools/snip/snip.js +67 -0
  423. package/src/tools/snip/snip.ts +80 -0
  424. package/src/tools/tasks/backgroundTaskService.js +275 -0
  425. package/src/tools/tasks/backgroundTaskService.ts +369 -0
  426. package/src/tools/tasks/processEvidence.js +24 -0
  427. package/src/tools/tasks/processEvidence.ts +34 -0
  428. package/src/tools/tasks/taskManagement.js +163 -0
  429. package/src/tools/tasks/taskManagement.ts +180 -0
  430. package/src/tools/tasks/taskOutput.js +113 -0
  431. package/src/tools/tasks/taskOutput.ts +145 -0
  432. package/src/tools/tasks/taskStop.js +74 -0
  433. package/src/tools/tasks/taskStop.ts +99 -0
  434. package/src/tools/tasks/taskStore.js +46 -0
  435. package/src/tools/tasks/taskStore.ts +77 -0
  436. package/src/tools/terminal/terminalCapture.js +167 -0
  437. package/src/tools/terminal/terminalCapture.ts +179 -0
  438. package/src/tools/trash/trash.js +233 -0
  439. package/src/tools/trash/trash.ts +283 -0
  440. package/src/tools/types.js +1 -1
  441. package/src/tools/types.ts +24 -0
  442. package/src/tools/webbrowser/agentBrowserPath.ts +286 -0
  443. package/src/tools/webbrowser/browser.js +214 -11
  444. package/src/tools/webbrowser/browser.ts +293 -0
  445. package/src/tools/webbrowser/browserControl.js +153 -48
  446. package/src/tools/webbrowser/browserControl.ts +262 -0
  447. package/src/tools/webbrowser/browserDaemon.js +147 -39
  448. package/src/tools/webbrowser/browserDaemon.ts +270 -0
  449. package/src/tools/webbrowser/browserOutcome.js +273 -0
  450. package/src/tools/webbrowser/browserOutcome.ts +306 -0
  451. package/src/tools/webbrowser/browserRunRegistry.js +150 -0
  452. package/src/tools/webbrowser/browserRunRegistry.ts +179 -0
  453. package/src/tools/webbrowser/browserService.js +20 -6
  454. package/src/tools/webbrowser/browserService.ts +56 -0
  455. package/src/tools/webbrowser/browserStream.js +30 -1
  456. package/src/tools/webbrowser/browserStream.ts +216 -0
  457. package/src/tools/webbrowser/browserTabs.js +143 -0
  458. package/src/tools/webbrowser/browserTabs.ts +210 -0
  459. package/src/tools/webbrowser/webbrowser.js +831 -150
  460. package/src/tools/webbrowser/webbrowser.ts +1537 -0
  461. package/src/tools/webfetch/preapproved.ts +274 -0
  462. package/src/tools/webfetch/webfetch.js +24 -23
  463. package/src/tools/webfetch/webfetch.ts +383 -0
  464. package/src/tools/websearch/websearch.js +12 -11
  465. package/src/tools/websearch/websearch.ts +292 -0
  466. package/src/tools/worktree/sessionWorktree.js +128 -0
  467. package/src/tools/worktree/sessionWorktree.ts +158 -0
  468. package/src/tools/write/write.js +27 -44
  469. package/src/tools/write/write.ts +136 -0
  470. package/src/types.ts +960 -0
  471. package/src/ui/App.js +216 -12
  472. package/src/ui/App.tsx +879 -0
  473. package/src/ui/AskQuestion.tsx +300 -0
  474. package/src/ui/InputBox.js +77 -1
  475. package/src/ui/InputBox.tsx +567 -0
  476. package/src/ui/LiveArea.tsx +344 -0
  477. package/src/ui/MessageList.js +27 -6
  478. package/src/ui/MessageList.tsx +559 -0
  479. package/src/ui/ModelPicker.tsx +107 -0
  480. package/src/ui/QueuedMessagesBanner.tsx +51 -0
  481. package/src/ui/Root.js +6 -8
  482. package/src/ui/Root.tsx +100 -0
  483. package/src/ui/SessionPicker.tsx +169 -0
  484. package/src/ui/StatusLine.js +22 -1
  485. package/src/ui/StatusLine.tsx +145 -0
  486. package/src/ui/WorkspacePicker.tsx +84 -0
  487. package/src/ui/advisorCommand.js +54 -0
  488. package/src/ui/advisorCommand.ts +63 -0
  489. package/src/ui/computerCommand.js +19 -0
  490. package/src/ui/computerCommand.ts +35 -0
  491. package/src/ui/figures.ts +45 -0
  492. package/src/ui/hooks/askSlot.ts +112 -0
  493. package/src/ui/hooks/useChat.js +271 -7
  494. package/src/ui/hooks/useChat.ts +1578 -0
  495. package/src/ui/hooks/useInputHistory.ts +235 -0
  496. package/src/ui/hooks/usePasteHandler.ts +220 -0
  497. package/src/ui/hooks/useTerminalSize.ts +41 -0
  498. package/src/ui/hooks/useTextBuffer.ts +108 -0
  499. package/src/ui/hooks/useTokenUsage.ts +44 -0
  500. package/src/ui/liveViewport.ts +189 -0
  501. package/src/ui/markdown.ts +230 -0
  502. package/src/ui/textBuffer.ts +239 -0
  503. package/src/ui/theme.ts +35 -0
  504. package/src/utils/clipboardImage.js +358 -0
  505. package/src/utils/clipboardImage.ts +420 -0
  506. package/src/utils/debugLog.ts +50 -0
  507. package/src/utils/greenRoot.ts +35 -0
  508. package/src/utils/packageRoot.ts +40 -0
  509. package/src/utils/pathContains.js +28 -0
  510. package/src/utils/pathContains.ts +31 -0
  511. package/src/utils/resourcePaths.ts +71 -0
  512. package/src/utils/stripThink.ts +49 -0
  513. package/src/utils/zodToJson.ts +73 -0
  514. package/src/web/browserBridge.js +365 -66
  515. package/src/web/browserBridge.ts +672 -0
  516. package/src/web/protocol.ts +1804 -0
  517. package/vendor/computer-use/arm64-darwin/cu-helperd +0 -0
  518. package/workflows/ppt.yaml +13 -2
  519. package/workflows/wechat-reading-shots.yaml +315 -0
  520. package/workflows/word.yaml +11 -1
  521. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  522. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  523. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  524. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  525. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  526. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  527. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  528. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  529. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  530. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  531. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  532. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  533. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  534. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  535. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  536. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  537. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  538. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  539. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  540. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  541. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  542. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  543. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  544. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  545. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  546. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  547. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  548. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  549. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  550. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  551. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  552. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  553. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  554. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  555. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  556. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  557. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  558. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  559. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  560. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  561. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  562. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  563. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  564. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  565. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  566. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  567. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  568. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  569. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  570. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  571. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  572. /package/skills/ai-image/scripts/{generate_test.mjs → generate.selftest.mjs} +0 -0
  573. /package/skills/ai-image/scripts/{generate_from_plan_test.mjs → generate_from_plan.selftest.mjs} +0 -0
@@ -0,0 +1,633 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { chmod, copyFile, mkdir, readFile, rename, rm, stat, unlink, writeFile } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+
6
+ import { execSiblingDir } from '../utils/greenRoot.js';
7
+ import type { PromptCacheConfig } from '../types.js';
8
+ import { normalizePromptCacheMode } from '../llm/promptCache.js';
9
+ import { CONFIG_LIMITS, normalizeLimited } from './numericLimits.js';
10
+ import {
11
+ DEFAULT_BROWSER_CONFIG,
12
+ DEFAULT_COMPUTER_USE_CONFIG,
13
+ DEFAULT_SUBAGENTS_CONFIG,
14
+ } from './configDefaults.js';
15
+
16
+ export {
17
+ DEFAULT_BROWSER_CONFIG,
18
+ DEFAULT_COMPUTER_USE_CONFIG,
19
+ DEFAULT_SUBAGENTS_CONFIG,
20
+ } from './configDefaults.js';
21
+
22
+ export type ImageProtocol = 'auto' | 'chat' | 'images';
23
+ export type ApiFormat = 'openai' | 'anthropic';
24
+
25
+ /** 一个模型条目——「模型列表」的元素,基座模型和顾问模型都从这里引用。 */
26
+ export interface ModelConfig {
27
+ id: string;
28
+ apiFormat: ApiFormat;
29
+ /** 可选展示名;缺省时 UI/解析都回退用 model 字符串本身——「名称」不再是必填概念。 */
30
+ displayName?: string;
31
+ baseURL: string;
32
+ apiKey: string;
33
+ model: string;
34
+ contextWindow: number;
35
+ maxTokens?: number;
36
+ supportsVision?: boolean;
37
+ promptCache?: PromptCacheConfig;
38
+ enabled: boolean;
39
+ createdAt: number;
40
+ updatedAt: number;
41
+ }
42
+
43
+ export interface AdvisorConfig {
44
+ enabled: boolean;
45
+ mode: 'auto' | 'manual';
46
+ /** 引用 models[].id;null = 未选。顾问不再自带一整套独立 provider 字段。 */
47
+ modelId: string | null;
48
+ contextPolicy: 'full-relevant';
49
+ shareSensitive: boolean;
50
+ /** 顾问单次输入预算。独立于模型声明的上下文窗口,避免百万窗口吞入整段历史。 */
51
+ inputTokenBudget: number;
52
+ timeoutMs: number;
53
+ }
54
+
55
+ export interface SubagentsConfig {
56
+ enabled: boolean;
57
+ maxCallsPerTurn: number;
58
+ maxConcurrent: number;
59
+ defaultWriteIsolation: 'none' | 'worktree';
60
+ timeoutMs: number;
61
+ exploreMaxTurns: number;
62
+ generalMaxTurns: number;
63
+ }
64
+
65
+ export interface BrowserConfig {
66
+ enabled: boolean;
67
+ profileId: 'default';
68
+ developerMode: boolean;
69
+ allowedCdpOrigins: string[];
70
+ }
71
+
72
+ export interface ComputerUseConfig {
73
+ enabled: boolean;
74
+ imagePolicy: 'single-adaptive';
75
+ maxImagePixels: number;
76
+ /** Compatibility field. Product mode always clamps this to one frame. */
77
+ observationFrames: number;
78
+ }
79
+
80
+ export interface ToolConfigs {
81
+ webSearch?: { provider: 'tavily'; apiKey: string };
82
+ imageGeneration?: {
83
+ provider: string;
84
+ baseURL: string;
85
+ apiKey: string;
86
+ model: string;
87
+ protocol?: ImageProtocol;
88
+ };
89
+ openRouter?: { apiKey: string };
90
+ textToSpeech?: { provider: 'minimax'; apiKey: string; groupId: string };
91
+ }
92
+
93
+ export interface MinimalAgentConfig {
94
+ version: 4;
95
+ models: ModelConfig[];
96
+ /** 当前被标记为基座(主循环)的模型 id;null = 尚未选定。 */
97
+ baseModelId: string | null;
98
+ compactRatio?: number;
99
+ advisor?: AdvisorConfig;
100
+ subagents: SubagentsConfig;
101
+ browser?: BrowserConfig;
102
+ computerUse?: ComputerUseConfig;
103
+ tools: ToolConfigs;
104
+ /** Secure, incomplete settings that must not replace the last runnable config. */
105
+ drafts?: ConfigDrafts;
106
+ savedAt: number;
107
+ }
108
+
109
+ /**
110
+ * 加模型是提交式弹窗、顾问改字段都是有界枚举/数字——不会出现「输入中途非法值」,
111
+ * 所以 v3 时代的 provider/advisor 草稿桶随 schema 一起删除,只剩 tools(imageGeneration/
112
+ * textToSpeech 仍是多字段实时录入表单,可能中途非法)。
113
+ */
114
+ export interface ConfigDrafts {
115
+ tools?: Record<string, Record<string, unknown>>;
116
+ }
117
+
118
+ const DEFAULT_CONTEXT_WINDOW = 128_000;
119
+ const LOCK_STALE_MS = 120_000;
120
+ const LOCK_WAIT_MS = 15_000;
121
+
122
+ /**
123
+ * 建议的单次输出 token 上限:min(32768, max(4096, contextWindow×0.25))。
124
+ * 上限 32768(非更保守的 16384)呼应 .env.example 的实测教训——16384 偏紧,写大 JSON
125
+ * 输出时会被截断;maxTokens 是上限不是预订额度,设大不额外花钱。
126
+ */
127
+ export function defaultMaxTokens(contextWindow: number): number {
128
+ return Math.min(32_768, Math.max(4_096, Math.round(contextWindow * 0.25)));
129
+ }
130
+
131
+ function homeConfigPath(): string {
132
+ return join(process.env.MINIMAL_AGENT_HOME ?? homedir(), '.minimal-agent', 'config.json');
133
+ }
134
+
135
+ export function getConfigFilePath(): string {
136
+ return process.env.MINIMAL_AGENT_CONFIG_FILE ?? homeConfigPath();
137
+ }
138
+
139
+ function positiveNumber(value: unknown): number | undefined {
140
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : undefined;
141
+ }
142
+
143
+ /** compactRatio 是可选字段:缺省/非法一律返回 undefined(让消费方用自己的默认比例),
144
+ * 存在时按比例语义规整到 4 位小数——`0.8500000000000001` 这种步进累加出来的浮点脏值
145
+ * 会在下次读盘时被抹平,不会一路带进 provider。 */
146
+ function parseCompactRatio(value: unknown): number | undefined {
147
+ if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value > 1) return undefined;
148
+ return normalizeLimited(value, CONFIG_LIMITS.compactRatio, CONFIG_LIMITS.compactRatio.max);
149
+ }
150
+
151
+ function nonEmpty(value: unknown): string | undefined {
152
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
153
+ }
154
+
155
+ function defaultPromptCacheMode(baseURL: string): PromptCacheConfig['mode'] {
156
+ try {
157
+ const hostname = new URL(baseURL).hostname.toLowerCase();
158
+ return hostname === 'api.moonshot.cn' || hostname.endsWith('.moonshot.cn')
159
+ ? 'session-key'
160
+ : 'auto';
161
+ } catch {
162
+ return 'auto';
163
+ }
164
+ }
165
+
166
+ // ---------------- v4(当前)解析 ----------------
167
+
168
+ function parseModel(value: unknown): ModelConfig | null {
169
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
170
+ const raw = value as Record<string, unknown>;
171
+ const id = nonEmpty(raw.id);
172
+ const baseURL = nonEmpty(raw.baseURL);
173
+ const model = nonEmpty(raw.model);
174
+ if (!id || !baseURL || !model) return null;
175
+ const apiKey = typeof raw.apiKey === 'string' ? raw.apiKey.trim() : '';
176
+ const displayName = nonEmpty(raw.displayName);
177
+ const now = Date.now();
178
+ return {
179
+ id,
180
+ apiFormat: raw.apiFormat === 'anthropic' ? 'anthropic' : 'openai',
181
+ ...(displayName ? { displayName } : {}),
182
+ baseURL,
183
+ apiKey,
184
+ model,
185
+ contextWindow: positiveNumber(raw.contextWindow) ?? DEFAULT_CONTEXT_WINDOW,
186
+ ...(positiveNumber(raw.maxTokens) !== undefined ? { maxTokens: positiveNumber(raw.maxTokens) } : {}),
187
+ supportsVision: raw.supportsVision !== false,
188
+ promptCache: {
189
+ mode: normalizePromptCacheMode(
190
+ (raw.promptCache as Record<string, unknown> | undefined)?.mode
191
+ ?? defaultPromptCacheMode(baseURL),
192
+ ),
193
+ },
194
+ enabled: raw.enabled !== false,
195
+ createdAt: positiveNumber(raw.createdAt) ?? now,
196
+ updatedAt: positiveNumber(raw.updatedAt) ?? now,
197
+ };
198
+ }
199
+
200
+ function parseModels(value: unknown): ModelConfig[] {
201
+ if (!Array.isArray(value)) return [];
202
+ const out: ModelConfig[] = [];
203
+ for (const item of value) {
204
+ const model = parseModel(item);
205
+ if (model) out.push(model);
206
+ }
207
+ return out;
208
+ }
209
+
210
+ function parseTools(value: unknown): ToolConfigs {
211
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
212
+ const raw = value as Record<string, unknown>;
213
+ const out: ToolConfigs = {};
214
+ const search = raw.webSearch as Record<string, unknown> | undefined;
215
+ const searchKey = nonEmpty(search?.apiKey);
216
+ if (searchKey) out.webSearch = { provider: 'tavily', apiKey: searchKey };
217
+ const image = raw.imageGeneration as Record<string, unknown> | undefined;
218
+ const imageBaseURL = nonEmpty(image?.baseURL);
219
+ const imageApiKey = nonEmpty(image?.apiKey);
220
+ const imageModel = nonEmpty(image?.model);
221
+ if (imageBaseURL && imageApiKey && imageModel) {
222
+ const protocol = image?.protocol === 'chat' || image?.protocol === 'images' ? image.protocol : 'auto';
223
+ out.imageGeneration = {
224
+ provider: nonEmpty(image?.provider) ?? 'openai',
225
+ baseURL: imageBaseURL,
226
+ apiKey: imageApiKey,
227
+ model: imageModel,
228
+ protocol,
229
+ };
230
+ }
231
+ const openRouterKey = nonEmpty((raw.openRouter as Record<string, unknown> | undefined)?.apiKey);
232
+ if (openRouterKey) out.openRouter = { apiKey: openRouterKey };
233
+ const speech = raw.textToSpeech as Record<string, unknown> | undefined;
234
+ const speechKey = nonEmpty(speech?.apiKey);
235
+ const groupId = nonEmpty(speech?.groupId);
236
+ if (speechKey && groupId) out.textToSpeech = { provider: 'minimax', apiKey: speechKey, groupId };
237
+ return out;
238
+ }
239
+
240
+ function parseAdvisor(value: unknown): AdvisorConfig | undefined {
241
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
242
+ const raw = value as Record<string, unknown>;
243
+ return {
244
+ enabled: raw.enabled !== false,
245
+ mode: raw.mode === 'manual' ? 'manual' : 'auto',
246
+ modelId: nonEmpty(raw.modelId) ?? null,
247
+ contextPolicy: 'full-relevant',
248
+ shareSensitive: raw.shareSensitive === true,
249
+ inputTokenBudget: normalizeLimited(raw.inputTokenBudget, CONFIG_LIMITS.advisorInputTokenBudget, 128_000),
250
+ timeoutMs: normalizeLimited(raw.timeoutMs, CONFIG_LIMITS.advisorTimeoutMs, 180_000),
251
+ };
252
+ }
253
+
254
+ function parseSubagents(value: unknown): SubagentsConfig {
255
+ const raw = value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
256
+ return {
257
+ enabled: raw.enabled !== false,
258
+ maxCallsPerTurn: normalizeLimited(raw.maxCallsPerTurn, CONFIG_LIMITS.subagentMaxCallsPerTurn, 6),
259
+ maxConcurrent: normalizeLimited(raw.maxConcurrent, CONFIG_LIMITS.subagentMaxConcurrent, 3),
260
+ defaultWriteIsolation: raw.defaultWriteIsolation === 'worktree' ? 'worktree' : 'none',
261
+ timeoutMs: normalizeLimited(raw.timeoutMs, CONFIG_LIMITS.subagentTimeoutMs, 1_800_000),
262
+ exploreMaxTurns: normalizeLimited(raw.exploreMaxTurns, CONFIG_LIMITS.subagentExploreMaxTurns, 50),
263
+ generalMaxTurns: normalizeLimited(raw.generalMaxTurns, CONFIG_LIMITS.subagentGeneralMaxTurns, 100),
264
+ };
265
+ }
266
+
267
+ function parseBrowser(value: unknown): BrowserConfig {
268
+ const raw = value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
269
+ return {
270
+ enabled: raw.enabled !== false,
271
+ profileId: 'default',
272
+ developerMode: raw.developerMode === true,
273
+ allowedCdpOrigins: Array.isArray(raw.allowedCdpOrigins)
274
+ ? raw.allowedCdpOrigins.filter((item): item is string => typeof item === 'string' && /^https?:\/\//i.test(item)).slice(0, 100)
275
+ : [],
276
+ };
277
+ }
278
+
279
+ function parseComputerUse(value: unknown): ComputerUseConfig {
280
+ const raw = value && typeof value === 'object' && !Array.isArray(value)
281
+ ? value as Record<string, unknown>
282
+ : {};
283
+ return {
284
+ enabled: raw.enabled !== false,
285
+ imagePolicy: 'single-adaptive',
286
+ maxImagePixels: normalizeLimited(raw.maxImagePixels, CONFIG_LIMITS.computerUseMaxImagePixels, 1_600_000),
287
+ observationFrames: 1,
288
+ };
289
+ }
290
+
291
+ function parseCurrent(data: Record<string, unknown>): MinimalAgentConfig | null {
292
+ if (data.version !== 4) return null;
293
+ const advisor = parseAdvisor(data.advisor);
294
+ const compactRatio = parseCompactRatio(data.compactRatio);
295
+ return {
296
+ version: 4,
297
+ models: parseModels(data.models),
298
+ baseModelId: nonEmpty(data.baseModelId) ?? null,
299
+ ...(compactRatio !== undefined ? { compactRatio } : {}),
300
+ ...(advisor ? { advisor } : {}),
301
+ subagents: parseSubagents(data.subagents),
302
+ browser: parseBrowser(data.browser),
303
+ computerUse: parseComputerUse(data.computerUse),
304
+ tools: parseTools(data.tools),
305
+ ...(data.drafts && typeof data.drafts === 'object' && !Array.isArray(data.drafts)
306
+ ? { drafts: structuredClone(data.drafts as ConfigDrafts) }
307
+ : {}),
308
+ savedAt: typeof data.savedAt === 'number' ? data.savedAt : 0,
309
+ };
310
+ }
311
+
312
+ // ---------------- v1/v2/v3(旧版)迁移专用解析 ----------------
313
+ // 这些类型/函数只服务迁移路径,不对外导出——v4 是唯一的当前 schema。
314
+
315
+ interface LegacyProviderConfig {
316
+ name: string;
317
+ baseURL: string;
318
+ apiKey: string;
319
+ model: string;
320
+ contextWindow: number;
321
+ compactRatio?: number;
322
+ maxTokens?: number;
323
+ }
324
+
325
+ interface LegacyAdvisorConfig {
326
+ enabled: boolean;
327
+ mode: 'auto' | 'manual';
328
+ provider: LegacyProviderConfig;
329
+ supportsVision: boolean;
330
+ shareSensitive: boolean;
331
+ inputTokenBudget: number;
332
+ timeoutMs: number;
333
+ }
334
+
335
+ function parseLegacyProviderConfig(value: unknown): LegacyProviderConfig | null {
336
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
337
+ const raw = value as Record<string, unknown>;
338
+ const baseURL = nonEmpty(raw.baseURL);
339
+ const apiKey = typeof raw.apiKey === 'string' ? raw.apiKey.trim() : undefined;
340
+ const model = nonEmpty(raw.model);
341
+ if (!baseURL || apiKey === undefined || !model) return null;
342
+ const compactRatio = parseCompactRatio(raw.compactRatio);
343
+ return {
344
+ name: nonEmpty(raw.name) ?? 'env',
345
+ baseURL,
346
+ apiKey,
347
+ model,
348
+ contextWindow: positiveNumber(raw.contextWindow) ?? DEFAULT_CONTEXT_WINDOW,
349
+ ...(compactRatio !== undefined ? { compactRatio } : {}),
350
+ ...(positiveNumber(raw.maxTokens) !== undefined ? { maxTokens: positiveNumber(raw.maxTokens) } : {}),
351
+ };
352
+ }
353
+
354
+ function parseLegacyAdvisorConfig(value: unknown): LegacyAdvisorConfig | undefined {
355
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
356
+ const raw = value as Record<string, unknown>;
357
+ const provider = parseLegacyProviderConfig(raw.provider);
358
+ if (!provider) return undefined;
359
+ return {
360
+ enabled: raw.enabled !== false,
361
+ mode: raw.mode === 'manual' ? 'manual' : 'auto',
362
+ provider,
363
+ supportsVision: raw.supportsVision === true,
364
+ shareSensitive: raw.shareSensitive === true,
365
+ inputTokenBudget: normalizeLimited(raw.inputTokenBudget, CONFIG_LIMITS.advisorInputTokenBudget, 128_000),
366
+ timeoutMs: normalizeLimited(raw.timeoutMs, CONFIG_LIMITS.advisorTimeoutMs, 180_000),
367
+ };
368
+ }
369
+
370
+ /**
371
+ * 把「单一基座 provider + 可选单一顾问 provider」的旧形状构造成 v4 的模型列表形状。
372
+ * v1(无 version 字段的扁平文件)与 v2/v3(provider/advisor 单对象)迁移路径共用这一个函数。
373
+ */
374
+ function buildV4FromLegacy(
375
+ legacyProvider: LegacyProviderConfig,
376
+ legacyAdvisor: LegacyAdvisorConfig | undefined,
377
+ subagents: SubagentsConfig,
378
+ tools: ToolConfigs,
379
+ savedAt: number,
380
+ ): MinimalAgentConfig {
381
+ const now = Date.now();
382
+ const baseModel: ModelConfig = {
383
+ id: 'migrated-base',
384
+ apiFormat: 'openai',
385
+ ...(legacyProvider.name !== 'env' ? { displayName: legacyProvider.name } : {}),
386
+ baseURL: legacyProvider.baseURL,
387
+ apiKey: legacyProvider.apiKey,
388
+ model: legacyProvider.model,
389
+ contextWindow: legacyProvider.contextWindow,
390
+ ...(legacyProvider.maxTokens !== undefined ? { maxTokens: legacyProvider.maxTokens } : {}),
391
+ enabled: true,
392
+ createdAt: now,
393
+ updatedAt: now,
394
+ };
395
+ const models: ModelConfig[] = [baseModel];
396
+ let advisor: AdvisorConfig | undefined;
397
+ if (legacyAdvisor) {
398
+ const sameAsBase = legacyAdvisor.provider.baseURL === baseModel.baseURL
399
+ && legacyAdvisor.provider.apiKey === baseModel.apiKey
400
+ && legacyAdvisor.provider.model === baseModel.model;
401
+ let advisorModelId: string;
402
+ if (sameAsBase) {
403
+ if (legacyAdvisor.supportsVision) baseModel.supportsVision = true;
404
+ advisorModelId = baseModel.id;
405
+ } else {
406
+ const advisorModel: ModelConfig = {
407
+ id: 'migrated-advisor',
408
+ apiFormat: 'openai',
409
+ ...(legacyAdvisor.provider.name !== 'advisor' ? { displayName: legacyAdvisor.provider.name } : {}),
410
+ baseURL: legacyAdvisor.provider.baseURL,
411
+ apiKey: legacyAdvisor.provider.apiKey,
412
+ model: legacyAdvisor.provider.model,
413
+ contextWindow: legacyAdvisor.provider.contextWindow,
414
+ ...(legacyAdvisor.provider.maxTokens !== undefined ? { maxTokens: legacyAdvisor.provider.maxTokens } : {}),
415
+ ...(legacyAdvisor.supportsVision ? { supportsVision: true } : {}),
416
+ enabled: true,
417
+ createdAt: now,
418
+ updatedAt: now,
419
+ };
420
+ models.push(advisorModel);
421
+ advisorModelId = advisorModel.id;
422
+ }
423
+ advisor = {
424
+ enabled: legacyAdvisor.enabled,
425
+ mode: legacyAdvisor.mode,
426
+ modelId: advisorModelId,
427
+ contextPolicy: 'full-relevant',
428
+ shareSensitive: legacyAdvisor.shareSensitive,
429
+ inputTokenBudget: legacyAdvisor.inputTokenBudget,
430
+ timeoutMs: legacyAdvisor.timeoutMs,
431
+ };
432
+ }
433
+ return {
434
+ version: 4,
435
+ models,
436
+ baseModelId: baseModel.id,
437
+ ...(legacyProvider.compactRatio !== undefined ? { compactRatio: legacyProvider.compactRatio } : {}),
438
+ ...(advisor ? { advisor } : {}),
439
+ subagents,
440
+ browser: DEFAULT_BROWSER_CONFIG,
441
+ computerUse: DEFAULT_COMPUTER_USE_CONFIG,
442
+ tools,
443
+ savedAt,
444
+ };
445
+ }
446
+
447
+ function migrateV1(data: Record<string, unknown>): MinimalAgentConfig | null {
448
+ const provider = parseLegacyProviderConfig({
449
+ name: data.provider,
450
+ baseURL: data.baseURL,
451
+ apiKey: data.apiKey,
452
+ model: data.model,
453
+ contextWindow: data.contextWindow,
454
+ compactRatio: data.compactRatio,
455
+ maxTokens: data.maxTokens,
456
+ });
457
+ if (!provider) return null;
458
+ const tools: ToolConfigs = {};
459
+ const tavilyApiKey = nonEmpty(data.tavilyApiKey);
460
+ if (tavilyApiKey) tools.webSearch = { provider: 'tavily', apiKey: tavilyApiKey };
461
+ return buildV4FromLegacy(provider, undefined, DEFAULT_SUBAGENTS_CONFIG, tools, typeof data.savedAt === 'number' ? data.savedAt : 0);
462
+ }
463
+
464
+ function migrateV3(data: Record<string, unknown>): MinimalAgentConfig | null {
465
+ const provider = parseLegacyProviderConfig(data.provider);
466
+ if (!provider) return null;
467
+ const advisor = parseLegacyAdvisorConfig(data.advisor);
468
+ return buildV4FromLegacy(
469
+ provider,
470
+ advisor,
471
+ parseSubagents(data.subagents),
472
+ parseTools(data.tools),
473
+ typeof data.savedAt === 'number' ? data.savedAt : 0,
474
+ );
475
+ }
476
+
477
+ async function parseConfigFile(
478
+ file: string,
479
+ ): Promise<{ config: MinimalAgentConfig; migrated: boolean; fromVersion: number } | null> {
480
+ try {
481
+ const data = JSON.parse(await readFile(file, 'utf8')) as unknown;
482
+ if (!data || typeof data !== 'object' || Array.isArray(data)) return null;
483
+ const object = data as Record<string, unknown>;
484
+ const current = parseCurrent(object);
485
+ if (current) return { config: current, migrated: object.version !== 4, fromVersion: 4 };
486
+ if (object.version === 2 || object.version === 3) {
487
+ const migrated = migrateV3(object);
488
+ if (migrated) return { config: migrated, migrated: true, fromVersion: object.version };
489
+ }
490
+ const migrated = migrateV1(object);
491
+ return migrated ? { config: migrated, migrated: true, fromVersion: 1 } : null;
492
+ } catch {
493
+ return null;
494
+ }
495
+ }
496
+
497
+ function processIsAlive(pid: number): boolean {
498
+ try {
499
+ process.kill(pid, 0);
500
+ return true;
501
+ } catch (error) {
502
+ return (error as NodeJS.ErrnoException).code === 'EPERM';
503
+ }
504
+ }
505
+
506
+ async function acquireConfigLock(file: string): Promise<() => Promise<void>> {
507
+ const lockDir = `${file}.lock`;
508
+ const ownerFile = join(lockDir, 'owner.json');
509
+ const startedAt = Date.now();
510
+ await mkdir(dirname(file), { recursive: true, mode: 0o700 });
511
+ while (true) {
512
+ try {
513
+ await mkdir(lockDir, { mode: 0o700 });
514
+ await writeFile(ownerFile, JSON.stringify({ pid: process.pid, createdAt: Date.now() }), { mode: 0o600 });
515
+ return async () => { await rm(lockDir, { recursive: true, force: true }); };
516
+ } catch (error) {
517
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
518
+ let stale = false;
519
+ try {
520
+ const [ownerRaw, info] = await Promise.all([readFile(ownerFile, 'utf8'), stat(lockDir)]);
521
+ const owner = JSON.parse(ownerRaw) as { pid?: number };
522
+ stale = typeof owner.pid === 'number' ? !processIsAlive(owner.pid) : Date.now() - info.mtimeMs > LOCK_STALE_MS;
523
+ } catch {
524
+ try {
525
+ const info = await stat(lockDir);
526
+ stale = Date.now() - info.mtimeMs > LOCK_STALE_MS;
527
+ } catch {
528
+ stale = false;
529
+ }
530
+ }
531
+ if (stale) { await rm(lockDir, { recursive: true, force: true }); continue; }
532
+ if (Date.now() - startedAt > LOCK_WAIT_MS) throw new Error('等待配置文件锁超时');
533
+ await new Promise((resolve) => setTimeout(resolve, 40));
534
+ }
535
+ }
536
+ }
537
+
538
+ async function writeConfigFile(file: string, config: MinimalAgentConfig): Promise<void> {
539
+ const release = await acquireConfigLock(file);
540
+ try {
541
+ await writeConfigFileUnlocked(file, config);
542
+ } finally {
543
+ await release();
544
+ }
545
+ }
546
+
547
+ async function writeConfigFileUnlocked(file: string, config: MinimalAgentConfig): Promise<void> {
548
+ const tmp = `${file}.${process.pid}.${randomUUID()}.tmp`;
549
+ try {
550
+ await mkdir(dirname(file), { recursive: true, mode: 0o700 });
551
+ await writeFile(tmp, JSON.stringify(config, null, 2), { encoding: 'utf8', mode: 0o600 });
552
+ await rename(tmp, file);
553
+ await chmod(file, 0o600).catch(() => {});
554
+ } finally {
555
+ await unlink(tmp).catch(() => {});
556
+ }
557
+ }
558
+
559
+ function configCandidates(): string[] {
560
+ const override = process.env.MINIMAL_AGENT_CONFIG_FILE;
561
+ return override ? [override] : [homeConfigPath(), join(execSiblingDir(), 'config.json')];
562
+ }
563
+
564
+ export async function readMinimalAgentConfig(options: { migrate?: boolean } = {}): Promise<MinimalAgentConfig | null> {
565
+ for (const file of configCandidates()) {
566
+ const parsed = await parseConfigFile(file);
567
+ if (!parsed) continue;
568
+ if (options.migrate !== false && (parsed.migrated || file !== getConfigFilePath())) {
569
+ // 备份只在「原地迁移会覆盖源文件」时才有意义(file == 权威写入位)。
570
+ // exe 同级(U 盘预置)源文件永不被覆盖,且「绝不写 U 盘」是产品红线——
571
+ // 曾在此给 U 盘目录写出 config.json.v1.backup(绿色版冒烟实测抓到)。
572
+ // 备份文件名按来源版本号命名(而非固定 .v1.backup):一台机器可能先后经历
573
+ // v1→v3、又 v3→v4 两次迁移,固定文件名会让第二次覆盖第一次的备份。
574
+ if (parsed.migrated && file === getConfigFilePath()) {
575
+ await copyFile(file, `${file}.v${parsed.fromVersion}.backup`).catch(() => {});
576
+ }
577
+ // A schema-only migration must not invalidate a revision already returned to
578
+ // another process during startup.
579
+ await writeConfigFile(getConfigFilePath(), parsed.config);
580
+ }
581
+ return parsed.config;
582
+ }
583
+ return null;
584
+ }
585
+
586
+ export async function saveMinimalAgentConfig(
587
+ config: Omit<MinimalAgentConfig, 'version' | 'savedAt' | 'subagents'> & { subagents?: SubagentsConfig },
588
+ ): Promise<MinimalAgentConfig> {
589
+ const next: MinimalAgentConfig = {
590
+ version: 4,
591
+ models: config.models,
592
+ baseModelId: config.baseModelId,
593
+ ...(config.compactRatio !== undefined ? { compactRatio: config.compactRatio } : {}),
594
+ advisor: config.advisor,
595
+ subagents: config.subagents ?? DEFAULT_SUBAGENTS_CONFIG,
596
+ browser: config.browser ?? DEFAULT_BROWSER_CONFIG,
597
+ computerUse: config.computerUse ?? DEFAULT_COMPUTER_USE_CONFIG,
598
+ tools: config.tools,
599
+ drafts: config.drafts,
600
+ savedAt: Date.now(),
601
+ };
602
+ await writeConfigFile(getConfigFilePath(), next);
603
+ return next;
604
+ }
605
+
606
+ /** Cross-process read-modify-write. The mutator never sees a partially written file. */
607
+ export async function updateMinimalAgentConfig(
608
+ mutate: (current: MinimalAgentConfig | null) => Omit<MinimalAgentConfig, 'version' | 'savedAt'>,
609
+ ): Promise<MinimalAgentConfig> {
610
+ const file = getConfigFilePath();
611
+ const release = await acquireConfigLock(file);
612
+ try {
613
+ const current = (await parseConfigFile(file))?.config ?? null;
614
+ const value = mutate(current);
615
+ const next: MinimalAgentConfig = {
616
+ version: 4,
617
+ models: value.models,
618
+ baseModelId: value.baseModelId,
619
+ ...(value.compactRatio !== undefined ? { compactRatio: value.compactRatio } : {}),
620
+ advisor: value.advisor,
621
+ subagents: value.subagents ?? current?.subagents ?? DEFAULT_SUBAGENTS_CONFIG,
622
+ browser: value.browser ?? current?.browser ?? DEFAULT_BROWSER_CONFIG,
623
+ computerUse: value.computerUse ?? current?.computerUse ?? DEFAULT_COMPUTER_USE_CONFIG,
624
+ tools: value.tools,
625
+ drafts: value.drafts,
626
+ savedAt: Math.max(Date.now(), (current?.savedAt ?? 0) + 1),
627
+ };
628
+ await writeConfigFileUnlocked(file, next);
629
+ return next;
630
+ } finally {
631
+ await release();
632
+ }
633
+ }