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,291 @@
1
+ /**
2
+ * ============================================================
3
+ * waitFor.ts —— 语义等待(wait_for_element / text / window)
4
+ * ------------------------------------------------------------
5
+ * ★ **零协议改动、零 vendor 重编。** 这三个动作本质是「轮询观测直到条件
6
+ * 成立」,而观测与 AX 树原生侧早已提供 —— 编排是 TS 的事,符合
7
+ * 「Rust 出像素,TS 做判断」总纲。
8
+ *
9
+ * 它们解决的是一个真实的成本问题:在此之前模型只能
10
+ * `observe → 看 → observe` 地轮询,而每次 observe 都要组装图(约 1100 token)。
11
+ * 这里改用**元数据观测**(`includeScreenshot:false`,`list_apps` 已经在走这条
12
+ * 路径),一轮几乎不花 token,命中才返回。
13
+ *
14
+ * 三条刻意的边界,缺任一条都会变成新的故障源:
15
+ *
16
+ * ① **超时不抛异常,而且要说清现场。** 只回一句「等了 5 秒没等到」,模型
17
+ * 只能盲试;带上「现在前台是谁、有哪些窗口、AX 里有哪些角色」,它才有
18
+ * 下一步可走。
19
+ * ② **AX 不可用时立刻返回**,不空转到超时。微信这类自绘应用的树整棵是空的,
20
+ * `wait_for_element` 在那里等多久都不会成立 —— 等 5 秒再报一个含糊的超时
21
+ * 是纯粹的浪费,且会把「这个能力用不了」误导成「这次没等到」。
22
+ * ③ **条件已成立就首轮返回**,不为了「像在等」而先睡一轮。
23
+ * ============================================================
24
+ */
25
+
26
+ import {
27
+ containsFold,
28
+ describeNode,
29
+ findByRole,
30
+ flattenAx,
31
+ matchesText,
32
+ type AxSearchNode,
33
+ } from './axSearch.js';
34
+ import type { ComputerState, ComputerUseAction } from './protocol.js';
35
+
36
+ /** 本模块接管的动作。 */
37
+ export const WAIT_ACTIONS = [
38
+ 'wait',
39
+ 'wait_for_element',
40
+ 'wait_for_text',
41
+ 'wait_for_window',
42
+ ] as const satisfies readonly ComputerUseAction[];
43
+
44
+ export type WaitAction = (typeof WAIT_ACTIONS)[number];
45
+
46
+ export function isWaitAction(action: ComputerUseAction): action is WaitAction {
47
+ return (WAIT_ACTIONS as readonly string[]).includes(action);
48
+ }
49
+
50
+ export function isSimpleWaitAction(action: ComputerUseAction): action is 'wait' {
51
+ return action === 'wait';
52
+ }
53
+
54
+ /** 轮询间隔。200ms 是「人眼觉得即时」与「别把守护进程压满」之间的常用折中。 */
55
+ export const WAIT_POLL_INTERVAL_MS = 200;
56
+ /** 默认等待上限。模型可用 `timeoutMs` 覆盖(schema 限 100–120000)。 */
57
+ export const WAIT_DEFAULT_TIMEOUT_MS = 5_000;
58
+
59
+ export interface WaitInput {
60
+ action: WaitAction;
61
+ /** `wait_for_element` 必填:AX 角色,如 `AXButton`。 */
62
+ role?: string;
63
+ /** 名字/文本片段。element 时可选,text/window 时必填。 */
64
+ text?: string;
65
+ /** 等待时间(毫秒),仅用于简单 `wait` 动作,默认 1000ms。 */
66
+ waitMs?: number;
67
+ timeoutMs?: number;
68
+ }
69
+
70
+ export interface WaitOutcome {
71
+ ok: boolean;
72
+ content: string;
73
+ /** 命中的控件 id(`wait_for_element` / `wait_for_text` 命中可点控件时才有)。 */
74
+ elementId?: string;
75
+ /** 实际等了多久,供遥测。 */
76
+ waitedMs: number;
77
+ /** 轮询了几次。 */
78
+ polls: number;
79
+ }
80
+
81
+ /** 一次观测的判定结果。`unavailable` = 这条判据在此应用上根本用不了。 */
82
+ type Verdict =
83
+ | { kind: 'hit'; description: string; elementId?: string }
84
+ | { kind: 'miss' }
85
+ | { kind: 'unavailable'; reason: string };
86
+
87
+ function axNodes(state: ComputerState): AxSearchNode[] {
88
+ return flattenAx(state.accessibility?.root as AxSearchNode | undefined);
89
+ }
90
+
91
+ function axUsable(state: ComputerState): boolean {
92
+ return state.accessibility !== undefined && state.accessibility.truncated !== true;
93
+ }
94
+
95
+ const AX_BLIND_HINT =
96
+ '这个应用没有可用的无障碍树(自绘/Electron 应用的常态),按控件等待在这里永远不会成立。'
97
+ + '改用 `settle` 判断界面稳没稳,再 `observe` 看图确认。';
98
+
99
+ function evaluate(input: WaitInput, state: ComputerState): Verdict {
100
+ switch (input.action) {
101
+ case 'wait':
102
+ // 简单 wait 不需要评估条件,直接返回 hit(它只 sleep)
103
+ return { kind: 'hit', description: '等待完成' };
104
+ case 'wait_for_window': {
105
+ // 窗口列表不依赖 AX 树的完整性,自绘应用上照样可用 —— 这是三个 wait
106
+ // 里唯一对微信这类应用有效的那个。
107
+ const needle = input.text ?? '';
108
+ const hit = state.windows.find((window) => containsFold(window.title, needle));
109
+ if (hit) {
110
+ return { kind: 'hit', description: `窗口「${hit.title ?? '(无标题)'}」(${hit.appId})` };
111
+ }
112
+ return { kind: 'miss' };
113
+ }
114
+ case 'wait_for_element': {
115
+ if (!axUsable(state)) return { kind: 'unavailable', reason: AX_BLIND_HINT };
116
+ const matches = findByRole(axNodes(state), input.role ?? '', input.text);
117
+ const hit = matches[0];
118
+ if (!hit) return { kind: 'miss' };
119
+ return {
120
+ kind: 'hit',
121
+ description: describeNode(hit)
122
+ + (matches.length > 1 ? `(另有 ${matches.length - 1} 个同样匹配的控件)` : ''),
123
+ ...(hit.elementId ? { elementId: hit.elementId } : {}),
124
+ };
125
+ }
126
+ case 'wait_for_text': {
127
+ if (!axUsable(state)) return { kind: 'unavailable', reason: AX_BLIND_HINT };
128
+ const hit = axNodes(state).find((node) => matchesText(node, input.text ?? ''));
129
+ if (!hit) return { kind: 'miss' };
130
+ return {
131
+ kind: 'hit',
132
+ description: describeNode(hit),
133
+ ...(hit.elementId ? { elementId: hit.elementId } : {}),
134
+ };
135
+ }
136
+ default:
137
+ return { kind: 'miss' };
138
+ }
139
+ }
140
+
141
+ /**
142
+ * 超时时给模型看的现场。
143
+ *
144
+ * 这段是本模块最容易被写敷衍的地方,也是最要紧的:模型拿不到现场就只能重试
145
+ * 同一件事,而重试同一件事正是熔断要拦的行为。
146
+ */
147
+ function describeScene(input: WaitInput, state: ComputerState | null): string[] {
148
+ if (!state) return [' 等待期间一次观测都没成功 —— 先检查权限与守护进程。'];
149
+ const lines = [
150
+ ` 当前前台:${state.activeApp?.name ?? '未知'}(${state.activeApp?.id ?? '无标识'})`,
151
+ ` 窗口:${state.windows.length > 0
152
+ ? state.windows.map((w) => `「${w.title ?? '(无标题)'}」`).slice(0, 6).join('、')
153
+ : '(一个都没有)'}`,
154
+ ];
155
+ if (input.action !== 'wait_for_window') {
156
+ const nodes = axNodes(state);
157
+ const roles = [...new Set(nodes.map((node) => node.role).filter(Boolean))].slice(0, 12);
158
+ lines.push(` 无障碍树:${axUsable(state) ? `${nodes.length} 个节点` : '不可用'}`);
159
+ if (roles.length > 0) lines.push(` 树里出现过的角色:${roles.join('、')}`);
160
+ }
161
+ return lines;
162
+ }
163
+
164
+ function requirementOf(input: WaitInput): string {
165
+ switch (input.action) {
166
+ case 'wait':
167
+ return '等待时间结束';
168
+ case 'wait_for_element':
169
+ return `控件 ${input.role}${input.text ? `(名字含「${input.text}」)` : ''}`;
170
+ case 'wait_for_text':
171
+ return `文本「${input.text}」`;
172
+ case 'wait_for_window':
173
+ return `标题含「${input.text}」的窗口`;
174
+ default:
175
+ return '未知条件';
176
+ }
177
+ }
178
+
179
+ /** 入参校验。返回非 null 表示参数本身就不成立,不必去等。 */
180
+ export function validateWaitInput(input: WaitInput): string | null {
181
+ // 简单 wait 不需要任何参数,只需要可选的等待时间
182
+ if (input.action === 'wait') {
183
+ if (input.waitMs !== undefined && (input.waitMs < 10 || input.waitMs > 30000)) {
184
+ return 'wait 的 waitMs 参数必须在 10-30000 毫秒之间(默认 1000ms)。';
185
+ }
186
+ return null;
187
+ }
188
+ if (input.action === 'wait_for_element' && !input.role?.trim()) {
189
+ return 'wait_for_element 需要 role(AX 角色,如 AXButton);只给文本请用 wait_for_text。';
190
+ }
191
+ if (input.action !== 'wait_for_element' && !input.text?.trim()) {
192
+ return `${input.action} 需要 text(要等的文本/窗口标题片段)。`;
193
+ }
194
+ return null;
195
+ }
196
+
197
+ /**
198
+ * 轮询直到条件成立或超时。
199
+ *
200
+ * `observeOnce` 由调用方注入(生产里是一次元数据观测),便于单测用合成状态
201
+ * 驱动而不碰真实守护进程。
202
+ */
203
+ export async function waitForCondition(input: WaitInput, deps: {
204
+ observeOnce: () => Promise<ComputerState | null>;
205
+ now?: () => number;
206
+ sleep?: (ms: number) => Promise<void>;
207
+ signal?: AbortSignal;
208
+ }): Promise<WaitOutcome> {
209
+ const invalid = validateWaitInput(input);
210
+ if (invalid) return { ok: false, content: invalid, waitedMs: 0, polls: 0 };
211
+
212
+ const now = deps.now ?? (() => Date.now());
213
+ const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));
214
+
215
+ // 简单 wait:纯 sleep,不截图不查 AX 树
216
+ if (input.action === 'wait') {
217
+ const waitMs = input.waitMs ?? 2000;
218
+ const startedAt = now();
219
+ if (deps.signal) {
220
+ // 支持中断:用 Promise.race 实现可取消的 sleep
221
+ await Promise.race([
222
+ sleep(waitMs),
223
+ new Promise<void>((_, reject) => {
224
+ deps.signal?.addEventListener('abort', () => reject(new Error('aborted')), { once: true });
225
+ }).catch(() => {}),
226
+ ]);
227
+ } else {
228
+ await sleep(waitMs);
229
+ }
230
+ const waitedMs = now() - startedAt;
231
+ if (deps.signal?.aborted) {
232
+ return { ok: false, content: '等待被中断。', waitedMs, polls: 0 };
233
+ }
234
+ return {
235
+ ok: true,
236
+ content: `已等待 ${waitMs}ms`,
237
+ waitedMs,
238
+ polls: 0,
239
+ };
240
+ }
241
+
242
+ const timeoutMs = input.timeoutMs ?? WAIT_DEFAULT_TIMEOUT_MS;
243
+ const startedAt = now();
244
+
245
+ let polls = 0;
246
+ let last: ComputerState | null = null;
247
+
248
+ // do-while:**先判一次再睡**。条件已经成立时不该白等一个间隔。
249
+ do {
250
+ if (deps.signal?.aborted) {
251
+ return { ok: false, content: '等待被中断。', waitedMs: now() - startedAt, polls };
252
+ }
253
+ polls += 1;
254
+ last = await deps.observeOnce();
255
+ if (last) {
256
+ const verdict = evaluate(input, last);
257
+ if (verdict.kind === 'hit') {
258
+ return {
259
+ ok: true,
260
+ content: `已等到${requirementOf(input)}:${verdict.description}(用时 ${now() - startedAt}ms)`,
261
+ ...(verdict.elementId ? { elementId: verdict.elementId } : {}),
262
+ waitedMs: now() - startedAt,
263
+ polls,
264
+ };
265
+ }
266
+ if (verdict.kind === 'unavailable') {
267
+ // 立刻返回,不空转到超时 —— 等下去也不会变成立。
268
+ return {
269
+ ok: false,
270
+ content: `无法等待${requirementOf(input)}:${verdict.reason}`,
271
+ waitedMs: now() - startedAt,
272
+ polls,
273
+ };
274
+ }
275
+ }
276
+ if (now() - startedAt >= timeoutMs) break;
277
+ await sleep(WAIT_POLL_INTERVAL_MS);
278
+ } while (now() - startedAt < timeoutMs);
279
+
280
+ const waitedMs = now() - startedAt;
281
+ return {
282
+ ok: false,
283
+ content: [
284
+ `等了 ${waitedMs}ms(${polls} 次观测)仍未出现${requirementOf(input)}。当前实际看到的是:`,
285
+ ...describeScene(input, last),
286
+ '别用同样的参数再等一次 —— 要么条件写错了,要么这一步的前置操作没生效。',
287
+ ].join('\n'),
288
+ waitedMs,
289
+ polls,
290
+ };
291
+ }
@@ -19,11 +19,13 @@
19
19
  * 可测:fetch 经 deps 注入(不连真网);落盘走真实 fs(测试用 tmp 目录)。
20
20
  * ============================================================
21
21
  */
22
- import { mkdir, writeFile } from 'node:fs/promises';
23
- import { dirname, isAbsolute, join, resolve } from 'node:path';
22
+ import { isAbsolute, join, resolve } from 'node:path';
24
23
  import { z } from 'zod';
25
24
  import { getWorkingDir } from '../../bootstrap/workingDir.js';
26
25
  import { toToolParameters } from '../../utils/zodToJson.js';
26
+ import { fileActivityMetadata } from '../shared/fileActivity.js';
27
+ import { atomicWriteFile } from '../shared/fileUtils.js';
28
+ import { emitFileMutation } from '../shared/fileMutationBus.js';
27
29
  const DEFAULT_TIMEOUT_MS = 60_000;
28
30
  const DEFAULT_MAX_BYTES = 25 * 1024 * 1024;
29
31
  // ---------------- 1. Zod 输入 schema ----------------
@@ -70,11 +72,11 @@ const defaultDeps = {
70
72
  fetchImpl: globalThis.fetch,
71
73
  };
72
74
  // ---------------- 3. call() ----------------
73
- async function call(input, signal, deps = defaultDeps) {
75
+ async function call(input, signal, deps = defaultDeps, cwd = getWorkingDir()) {
74
76
  const { url, path } = input;
75
77
  const timeout = input.timeout ?? DEFAULT_TIMEOUT_MS;
76
78
  const maxBytes = input.max_bytes ?? DEFAULT_MAX_BYTES;
77
- const abs = isAbsolute(path) ? path : resolve(join(getWorkingDir(), path));
79
+ const abs = isAbsolute(path) ? path : resolve(join(cwd, path));
78
80
  // 合并外部 signal 与超时
79
81
  const ctrl = new AbortController();
80
82
  const timer = setTimeout(() => ctrl.abort(), timeout);
@@ -118,12 +120,13 @@ async function call(input, signal, deps = defaultDeps) {
118
120
  error: `[Download] 下载 ${buf.byteLength} 字节超过上限 ${maxBytes}(${url})`,
119
121
  };
120
122
  }
121
- await mkdir(dirname(abs), { recursive: true });
122
- await writeFile(abs, buf);
123
+ await atomicWriteFile(abs, buf);
124
+ emitFileMutation(abs);
123
125
  const ctype = res.headers.get('content-type') ?? 'unknown';
124
126
  return {
125
127
  ok: true,
126
128
  content: `DOWNLOADED: ${buf.byteLength} 字节 → ${abs}(${ctype})`,
129
+ metadata: await fileActivityMetadata(abs, 'generated', 'not_run', 'support', cwd),
127
130
  };
128
131
  }
129
132
  finally {
@@ -139,6 +142,7 @@ export const downloadTool = {
139
142
  isReadOnly: false,
140
143
  isConcurrencySafe: true,
141
144
  call,
145
+ callWithContext: (input, signal, context) => call(input, signal, defaultDeps, context.cwd),
142
146
  };
143
147
  // 供单测:直接调 call 注入 deps。
144
148
  export const _downloadCall = call;
@@ -0,0 +1,187 @@
1
+ /**
2
+ * ============================================================
3
+ * src/tools/download/download.ts —— Download 工具(URL → 文件)
4
+ * ------------------------------------------------------------
5
+ * 通用基础能力:把一个 URL 的字节流下载落盘。填补 WebFetch(HTML→markdown,
6
+ * 读文本用)与 Bash(curl/Invoke-WebRequest,跨平台脆)之间的空白——纯 YAML
7
+ * 工作流要「把图片/资产存盘」时用它(如 illustrate 配图工作流逐图下载候选)。
8
+ *
9
+ * 输入:
10
+ * url 必填,http(s) 资源地址
11
+ * path 必填,落盘路径(相对 getWorkingDir 锚定;绝对路径原样)
12
+ * headers 可选,自定义请求头(如 Referer/User-Agent,绕部分防盗链)
13
+ * timeout 可选,毫秒,默认 60000(无新字节判死由 fetch/AbortSignal 控)
14
+ * max_bytes 可选,体积上限(默认 25MB;超限不写、报错,防失控)
15
+ *
16
+ * 输出:成功 = { ok, content: "DOWNLOADED: <bytes> → <path> (<content-type>)" };
17
+ * 失败 = { ok:false, error } —— 不抛异常,error 喂回 LLM(同全项目工具约定)。
18
+ *
19
+ * 可测:fetch 经 deps 注入(不连真网);落盘走真实 fs(测试用 tmp 目录)。
20
+ * ============================================================
21
+ */
22
+
23
+ import { isAbsolute, join, resolve } from 'node:path';
24
+
25
+ import { z } from 'zod';
26
+
27
+ import type { Tool, ToolCallContext, ToolResult } from '../../types.js';
28
+ import { getWorkingDir } from '../../bootstrap/workingDir.js';
29
+ import { toToolParameters } from '../../utils/zodToJson.js';
30
+ import { fileActivityMetadata } from '../shared/fileActivity.js';
31
+ import { atomicWriteFile } from '../shared/fileUtils.js';
32
+ import { emitFileMutation } from '../shared/fileMutationBus.js';
33
+
34
+ const DEFAULT_TIMEOUT_MS = 60_000;
35
+ const DEFAULT_MAX_BYTES = 25 * 1024 * 1024;
36
+
37
+ // ---------------- 1. Zod 输入 schema ----------------
38
+ const inputSchema = z.object({
39
+ url: z.string().url().describe('http(s) resource URL to download'),
40
+ path: z
41
+ .string()
42
+ .describe('Destination file path (relative paths anchor to the working dir)'),
43
+ headers: z
44
+ .record(z.string())
45
+ .optional()
46
+ .describe('Optional request headers (e.g. Referer/User-Agent to bypass hotlink guards)'),
47
+ timeout: z
48
+ .number()
49
+ .int()
50
+ .positive()
51
+ .optional()
52
+ .describe('Timeout in milliseconds (default: 60000)'),
53
+ max_bytes: z
54
+ .number()
55
+ .int()
56
+ .positive()
57
+ .optional()
58
+ .describe('Max size in bytes; over this the download is rejected (default: 25MB)'),
59
+ });
60
+ type DownloadInput = z.infer<typeof inputSchema>;
61
+
62
+ const parameters = toToolParameters(inputSchema);
63
+
64
+ const description = `Download a URL to a local file (raw bytes).
65
+
66
+ When to use Download vs WebFetch:
67
+ - WebFetch: read a web page as markdown text (for the model to consume).
68
+ - Download: save the raw bytes of a resource (image / pdf / asset) to disk.
69
+
70
+ Input parameters:
71
+ - url (required): http(s) URL.
72
+ - path (required): destination file path (relative paths anchor to the working dir; parent dirs are created).
73
+ - headers (optional): request headers (e.g. Referer/User-Agent to bypass hotlink protection).
74
+ - timeout (optional): milliseconds (default 60000).
75
+ - max_bytes (optional): reject downloads larger than this (default 25MB).
76
+
77
+ Notes:
78
+ - Returns an error (not an exception) on HTTP failure / timeout / oversize.
79
+ - Overwrites an existing file at path.`;
80
+
81
+ // ---------------- 2. 依赖注入(仅测试) ----------------
82
+ interface FetchResponseLike {
83
+ ok: boolean;
84
+ status: number;
85
+ statusText: string;
86
+ headers: { get(name: string): string | null };
87
+ arrayBuffer(): Promise<ArrayBuffer>;
88
+ }
89
+ type FetchLike = (
90
+ url: string,
91
+ init?: { headers?: Record<string, string>; signal?: AbortSignal },
92
+ ) => Promise<FetchResponseLike>;
93
+
94
+ export interface DownloadDeps {
95
+ fetchImpl: FetchLike;
96
+ }
97
+
98
+ const defaultDeps: DownloadDeps = {
99
+ fetchImpl: globalThis.fetch as unknown as FetchLike,
100
+ };
101
+
102
+ // ---------------- 3. call() ----------------
103
+ async function call(
104
+ input: DownloadInput,
105
+ signal?: AbortSignal,
106
+ deps: DownloadDeps = defaultDeps,
107
+ cwd = getWorkingDir(),
108
+ ): Promise<ToolResult> {
109
+ const { url, path } = input;
110
+ const timeout = input.timeout ?? DEFAULT_TIMEOUT_MS;
111
+ const maxBytes = input.max_bytes ?? DEFAULT_MAX_BYTES;
112
+
113
+ const abs = isAbsolute(path) ? path : resolve(join(cwd, path));
114
+
115
+ // 合并外部 signal 与超时
116
+ const ctrl = new AbortController();
117
+ const timer = setTimeout(() => ctrl.abort(), timeout);
118
+ if (signal) {
119
+ if (signal.aborted) ctrl.abort();
120
+ else signal.addEventListener('abort', () => ctrl.abort(), { once: true });
121
+ }
122
+
123
+ try {
124
+ let res: FetchResponseLike;
125
+ try {
126
+ res = await deps.fetchImpl(url, { headers: input.headers, signal: ctrl.signal });
127
+ } catch (e) {
128
+ const msg = ctrl.signal.aborted
129
+ ? `下载超时(${timeout}ms)或被中断`
130
+ : e instanceof Error
131
+ ? e.message
132
+ : String(e);
133
+ return { ok: false, error: `[Download] 请求失败:${msg}(${url})` };
134
+ }
135
+
136
+ if (!res.ok) {
137
+ return {
138
+ ok: false,
139
+ error: `[Download] HTTP ${res.status} ${res.statusText}(${url})`,
140
+ };
141
+ }
142
+
143
+ // content-length 预检(有则提前拒绝超大文件,省下载)
144
+ const declared = Number(res.headers.get('content-length') ?? '');
145
+ if (Number.isFinite(declared) && declared > maxBytes) {
146
+ return {
147
+ ok: false,
148
+ error: `[Download] 体积 ${declared} 字节超过上限 ${maxBytes}(${url})`,
149
+ };
150
+ }
151
+
152
+ const buf = Buffer.from(await res.arrayBuffer());
153
+ if (buf.byteLength > maxBytes) {
154
+ return {
155
+ ok: false,
156
+ error: `[Download] 下载 ${buf.byteLength} 字节超过上限 ${maxBytes}(${url})`,
157
+ };
158
+ }
159
+
160
+ await atomicWriteFile(abs, buf);
161
+ emitFileMutation(abs);
162
+
163
+ const ctype = res.headers.get('content-type') ?? 'unknown';
164
+ return {
165
+ ok: true,
166
+ content: `DOWNLOADED: ${buf.byteLength} 字节 → ${abs}(${ctype})`,
167
+ metadata: await fileActivityMetadata(abs, 'generated', 'not_run', 'support', cwd),
168
+ };
169
+ } finally {
170
+ clearTimeout(timer);
171
+ }
172
+ }
173
+
174
+ // ---------------- 4. 导出 ----------------
175
+ export const downloadTool: Tool<DownloadInput> = {
176
+ name: 'Download',
177
+ description,
178
+ inputSchema,
179
+ parameters,
180
+ isReadOnly: false,
181
+ isConcurrencySafe: true,
182
+ call,
183
+ callWithContext: (input, signal, context: ToolCallContext) => call(input, signal, defaultDeps, context.cwd),
184
+ };
185
+
186
+ // 供单测:直接调 call 注入 deps。
187
+ export const _downloadCall = call;
@@ -11,15 +11,16 @@
11
11
  * - old_string 找不到时提供模糊匹配提示(最常见模型错误)
12
12
  * ============================================================
13
13
  */
14
- import { readFile, writeFile, mkdir } from 'node:fs/promises';
15
14
  import { existsSync } from 'node:fs';
16
- import { dirname } from 'node:path';
17
15
  import { z } from 'zod';
18
16
  import { DEFAULT_MAX_RESULT_SIZE_CHARS } from '../types.js';
19
17
  import { toToolParameters } from '../../utils/zodToJson.js';
20
- import { resolveToolPath, findActualString, preserveQuoteStyle, detectLineEndingsForString, applyLineEnding, countOccurrences, splitReplaceAll, tryAutoSnapshot, } from '../shared/fileUtils.js';
18
+ import { resolveToolPath, findActualString, preserveQuoteStyle, detectLineEndingsForString, applyLineEnding, countOccurrences, splitReplaceAll, tryAutoSnapshot, atomicWriteFile, encodeTextFile, readTextFile, } from '../shared/fileUtils.js';
21
19
  import { filePathField } from '../shared/schemas.js';
22
- import { assertFresh, recordRead, wasTruncated } from '../shared/fileState.js';
20
+ import { assertFresh, hashFileBytes, recordRead, wasTruncated } from '../shared/fileState.js';
21
+ import { fileActivityMetadata } from '../shared/fileActivity.js';
22
+ import { emitFileMutation } from '../shared/fileMutationBus.js';
23
+ import { buildPatchSummary } from '../shared/diffSummary.js';
23
24
  /** 编辑文件大小上限:1GB。防止模型试图 Edit 超大文件撑爆内存。 */
24
25
  const MAX_EDIT_FILE_SIZE_BYTES = 1024 * 1024 * 1024;
25
26
  // ---------------- 1. Zod 输入 schema ----------------
@@ -50,23 +51,23 @@ Usage:
50
51
  - Use \`replace_all\` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.
51
52
  - Preserve exact indentation (tabs/spaces).`;
52
53
  // ---------------- 4. call() ----------------
53
- async function call(input) {
54
+ async function call(input, state, cwd) {
54
55
  const { old_string, new_string } = input;
55
56
  const replaceAll = input.replace_all ?? false;
56
57
  // 5a. 路径规范化 + 安全校验(含 null byte / UNC / 设备文件拦截 / ~ 展开)
57
- const pathResult = resolveToolPath(input.file_path);
58
+ const pathResult = resolveToolPath(input.file_path, cwd);
58
59
  if (!pathResult.ok)
59
60
  return pathResult;
60
61
  const filePath = pathResult.resolvedPath;
61
62
  // 5a-bis. Pre-read guard:文件已存在则必须先 Read(创建新文件场景下 assertFresh 返回 ok:true)
62
63
  // 拦截路径自动注入 snippet:让模型在同一轮 tool result 里看到当前内容,无需多绕一次 Read。
63
64
  // 安全语义保留:模型必须显式重发 Edit 才会落盘;snippet 只是给模型看的,不静默放行。
64
- const freshness = assertFresh(filePath);
65
+ const freshness = await assertFresh(filePath, {}, state);
65
66
  if (!freshness.ok) {
66
67
  const snap = await tryAutoSnapshot(filePath);
67
68
  if (snap.ok) {
68
69
  // 关键:truncated=true 时 wasTruncated 会守住 Write 路径,防止基于残缺 snippet 重写整文件
69
- recordRead(filePath, { truncated: snap.truncated });
70
+ recordRead(filePath, { truncated: snap.truncated, partial: snap.truncated }, state);
70
71
  const tail = snap.truncated
71
72
  ? `\n\n⚠️ 仅注入了前 200 行(文件共 ${snap.totalLines} 行 / 上限 32KB)。若要改的位置在范围外,请显式 Read offset=X limit=Y 后再 Edit。`
72
73
  : '';
@@ -93,12 +94,14 @@ async function call(input) {
93
94
  // 5b. 创建新文件路径:old_string === '' 且文件不存在
94
95
  if (old_string === '' && !existsSync(filePath)) {
95
96
  try {
96
- await mkdir(dirname(filePath), { recursive: true });
97
- await writeFile(filePath, new_string, 'utf8');
98
- recordRead(filePath);
97
+ await atomicWriteFile(filePath, new_string);
98
+ recordRead(filePath, { contentHash: hashFileBytes(encodeTextFile(new_string, 'utf8')) }, state);
99
+ emitFileMutation(filePath);
100
+ const patch = buildPatchSummary('', new_string, input.file_path);
99
101
  return {
100
102
  ok: true,
101
- content: `已创建新文件:${filePath}(${new_string.length} 字符)`,
103
+ content: `已创建新文件:${filePath}(${new_string.length} 字符)\n${patch.unifiedDiff ?? '[diff omitted: too large]'}`,
104
+ metadata: await fileActivityMetadata(filePath, 'created', 'not_run', 'support', cwd, patch),
102
105
  };
103
106
  }
104
107
  catch (e) {
@@ -113,8 +116,11 @@ async function call(input) {
113
116
  };
114
117
  }
115
118
  let original;
119
+ let sourceEncoding;
116
120
  try {
117
- original = await readFile(filePath, 'utf8');
121
+ const snapshot = await readTextFile(filePath);
122
+ original = snapshot.content;
123
+ sourceEncoding = snapshot.encoding;
118
124
  }
119
125
  catch (e) {
120
126
  return { ok: false, error: `读取失败:${e.message}` };
@@ -146,7 +152,7 @@ async function call(input) {
146
152
  }
147
153
  // splitCount = 出现次数
148
154
  const occurrences = countOccurrences(original, searchTarget);
149
- const truncHint = wasTruncated(filePath)
155
+ const truncHint = wasTruncated(filePath, state)
150
156
  ? `\n\n⚠️ 上次 Read 该文件时被截断了(只看到部分内容)。当前 old_string 可能在截断范围外。请用更小的 limit + offset 精确读取目标行号附近后再 Edit。`
151
157
  : '';
152
158
  if (occurrences === 0) {
@@ -170,8 +176,11 @@ async function call(input) {
170
176
  // 5f. 保持原文件行尾符风格
171
177
  const normalizedReplaced = applyLineEnding(replaced, originalLineEnding);
172
178
  try {
173
- await writeFile(filePath, normalizedReplaced, 'utf8');
174
- recordRead(filePath);
179
+ await atomicWriteFile(filePath, normalizedReplaced, sourceEncoding);
180
+ recordRead(filePath, {
181
+ contentHash: hashFileBytes(encodeTextFile(normalizedReplaced, sourceEncoding)),
182
+ }, state);
183
+ emitFileMutation(filePath);
175
184
  }
176
185
  catch (e) {
177
186
  return { ok: false, error: `写入失败:${e.message}` };
@@ -179,11 +188,13 @@ async function call(input) {
179
188
  // 输出:报告改了几处 + 行变化
180
189
  const linesBefore = original.split('\n').length;
181
190
  const linesAfter = replaced.split('\n').length;
191
+ const patch = buildPatchSummary(original, normalizedReplaced, input.file_path);
182
192
  return {
183
193
  ok: true,
184
194
  content: `已编辑 ${filePath}\n` +
185
195
  `替换次数:${replaceAll ? occurrences : 1}\n` +
186
- `行数:${linesBefore} → ${linesAfter}`,
196
+ `行数:${linesBefore} → ${linesAfter}\n` + (patch.unifiedDiff ?? '[diff omitted: too large]'),
197
+ metadata: await fileActivityMetadata(filePath, 'modified', 'not_run', 'support', cwd, patch),
187
198
  };
188
199
  }
189
200
  // ---------------- 5. 模糊匹配提示 ----------------
@@ -277,5 +288,6 @@ export const editTool = {
277
288
  isConcurrencySafe: false,
278
289
  maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
279
290
  interruptBehavior: () => 'block',
280
- call,
291
+ call: (input) => call(input),
292
+ callWithContext: (input, _signal, context) => call(input, context.sessionState?.fileReads, context.cwd),
281
293
  };