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,293 @@
1
+ /**
2
+ * ============================================================
3
+ * src/tools/webbrowser/browser.ts —— agent-browser 子进程运行器
4
+ * ------------------------------------------------------------
5
+ * 内置 agent 浏览器底座的执行层:把命令发给 agent-browser CLI(原生 Rust + CDP,
6
+ * daemon 模型——多次调用复用同一后台浏览器进程)。取代原 playwright 单例。
7
+ *
8
+ * 二进制定位、Chrome 分层降级见 `agentBrowserPath.ts`。本文件只负责 spawn + 收集输出。
9
+ * 全局 flag(--session / --executable-path)必须排在 subcommand 之前(agent-browser 约定)。
10
+ * ============================================================
11
+ */
12
+
13
+ import { spawn } from 'node:child_process';
14
+ import { randomUUID } from 'node:crypto';
15
+ import { chmod, mkdir, readdir, realpath, rm, stat } from 'node:fs/promises';
16
+ import os from 'node:os';
17
+ import path from 'node:path';
18
+
19
+ import { sanitizeCredentialEnv } from '../../config/configService.js';
20
+ import { pathContains, pathContainedByAny } from '../../utils/pathContains.js';
21
+
22
+ export interface AgentBrowserRun {
23
+ code: number;
24
+ stdout: string;
25
+ stderr: string;
26
+ }
27
+
28
+ /** 运行器签名:给 bin + 完整 args(含全局 flag),返回退出码与输出。可被测试注入。 */
29
+ export type AgentBrowserRunner = (
30
+ bin: string,
31
+ args: string[],
32
+ signal?: AbortSignal,
33
+ ) => Promise<AgentBrowserRun>;
34
+
35
+ /**
36
+ * minimal-agent 进程级固定 session 名:隔离机器上其它 agent-browser 用法,
37
+ * 同时让本进程多次调用复用同一浏览器(与原 playwright「一 session 一 page」语义一致)。
38
+ */
39
+ export const BROWSER_SESSION = 'minimal-agent';
40
+
41
+ /** 全局 session flag(排在 subcommand 之前)。 */
42
+ export function sessionArgs(sessionName = BROWSER_SESSION): string[] {
43
+ return ['--session', sessionName];
44
+ }
45
+
46
+ /** KakadeAI-owned persistent browser profile. It is never the user's system Chrome profile. */
47
+ export function browserProfilePath(profileId = 'default'): string {
48
+ const safe = /^[a-zA-Z0-9._-]+$/.test(profileId) ? profileId : 'default';
49
+ return path.join(process.env.MINIMAL_AGENT_HOME ?? os.homedir(), '.minimal-agent', 'browser', 'profiles', safe);
50
+ }
51
+
52
+ export async function ensureBrowserProfile(profileId = 'default'): Promise<string> {
53
+ const profile = browserProfilePath(profileId);
54
+ await mkdir(profile, { recursive: true, mode: 0o700 });
55
+ if (process.platform !== 'win32') await chmod(profile, 0o700).catch(() => undefined);
56
+ return profile;
57
+ }
58
+
59
+ export async function browserGlobalArgs(sessionName = BROWSER_SESSION, profileId = 'default'): Promise<string[]> {
60
+ return [...sessionArgs(sessionName), '--profile', await ensureBrowserProfile(profileId)];
61
+ }
62
+
63
+ /**
64
+ * 默认 spawn 运行器:收集 stdout/stderr 后结算;spawn 抛错也 resolve 成失败。
65
+ *
66
+ * ⚠️ 以 **'exit'(进程退出)为准结算,而非死等 'close'(全部 stdio 流关闭)**——这是「桌面首次
67
+ * 用浏览器卡死」的真根因修复:agent-browser 首次 `open` 会拉起一个 **detached daemon**,daemon
68
+ * 继承并长期攥着本进程给 client 的 stdout/stderr **pipe** 写端 → Node 的 `'close'` 事件**永不触发**
69
+ * → 若死等 'close',spawnRunner 永久挂起(实测 client 已 `exit code=0 @800ms`,`close` 却 never),
70
+ * 连 AbortSignal 杀了 client 都没用(close 仍在等被 daemon 攥着的 pipe)。
71
+ * 正常命令(连既有 daemon、不派生)'close' 紧随 'exit' → 走 'close' 拿完整输出;只有拉 daemon 的
72
+ * 命令走 'exit' + 短宽限兜底(此时输出已写完、daemon 只是攥着空 pipe)。abort 同理经 'exit' 及时结算。
73
+ */
74
+ export const spawnRunner: AgentBrowserRunner = (bin, args, signal) =>
75
+ new Promise<AgentBrowserRun>((resolveP) => {
76
+ let stdout = '';
77
+ let stderr = '';
78
+ let settled = false;
79
+ let graceTimer: ReturnType<typeof setTimeout> | null = null;
80
+ let child: ReturnType<typeof spawn>;
81
+ const finish = (code: number | null, errMsg?: string) => {
82
+ if (settled) return;
83
+ settled = true;
84
+ if (graceTimer) clearTimeout(graceTimer);
85
+ // 主动释放父侧 pipe 读端(daemon 攥着写端、我们已拿到结果,不再依赖它关闭)。
86
+ try { child?.stdout?.destroy(); } catch {}
87
+ try { child?.stderr?.destroy(); } catch {}
88
+ resolveP({ code: code ?? -1, stdout, stderr: stderr || errMsg || '' });
89
+ };
90
+ try {
91
+ child = spawn(bin, args, {
92
+ stdio: ['ignore', 'pipe', 'pipe'],
93
+ env: sanitizeCredentialEnv(),
94
+ windowsHide: true,
95
+ signal,
96
+ });
97
+ } catch (e) {
98
+ resolveP({ code: -1, stdout: '', stderr: e instanceof Error ? e.message : String(e) });
99
+ return;
100
+ }
101
+ child.stdout?.setEncoding('utf8');
102
+ child.stdout?.on('data', (c) => (stdout += c));
103
+ child.stderr?.setEncoding('utf8');
104
+ child.stderr?.on('data', (c) => (stderr += c));
105
+ child.on('error', (e) => finish(-1, e instanceof Error ? e.message : String(e)));
106
+ // 正常路径:'close' 紧随 'exit'(pipe 关闭)→ 以完整输出立即结算,并清掉宽限计时器。
107
+ child.on('close', (code) => finish(code));
108
+ // 拉 daemon 的命令:daemon 继承 pipe → 'close' 永不来。以 'exit' 为准,给一小段宽限等 'close'
109
+ // (正常命令会在宽限内更早经 'close' 结算,不丢输出);超时则强制结算,不再无限等 pipe。
110
+ child.on('exit', (code) => {
111
+ if (settled) return;
112
+ graceTimer = setTimeout(() => finish(code), 500);
113
+ });
114
+ });
115
+
116
+ /**
117
+ * 有界执行一次 agent-browser 命令:在 spawnRunner 之上加「超时 + 可中断」。
118
+ * 到 timeoutMs 或父 signal abort 即掐断子进程(spawnRunner 拿到 aborted signal → kill →
119
+ * close → resolve,不会挂),返回结果额外带 timedOut/aborted 标记,供调用方产出可操作错误
120
+ * 而不是把「被超时/中断杀掉」误当普通失败。timeoutMs 省略或 ≤0 则只受父 signal 约束。
121
+ */
122
+ export async function runBounded(
123
+ bin: string,
124
+ args: string[],
125
+ opts: { signal?: AbortSignal; timeoutMs?: number } = {},
126
+ runner: AgentBrowserRunner = spawnRunner,
127
+ ): Promise<AgentBrowserRun & { timedOut?: boolean; aborted?: boolean }> {
128
+ const ac = new AbortController();
129
+ let timedOut = false;
130
+ const onParent = () => ac.abort(opts.signal?.reason);
131
+ const timer =
132
+ opts.timeoutMs && opts.timeoutMs > 0
133
+ ? setTimeout(() => {
134
+ timedOut = true;
135
+ ac.abort();
136
+ }, opts.timeoutMs)
137
+ : null;
138
+ if (opts.signal) {
139
+ if (opts.signal.aborted) ac.abort(opts.signal.reason);
140
+ else opts.signal.addEventListener('abort', onParent, { once: true });
141
+ }
142
+ try {
143
+ const r = await runner(bin, args, ac.signal);
144
+ const abortedByParent = Boolean(opts.signal?.aborted) && !timedOut;
145
+ return {
146
+ ...r,
147
+ ...(timedOut ? { timedOut: true } : {}),
148
+ ...(abortedByParent ? { aborted: true } : {}),
149
+ };
150
+ } finally {
151
+ if (timer) clearTimeout(timer);
152
+ opts.signal?.removeEventListener('abort', onParent);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * 浏览器产物(截图 / PDF / 录像 / trace)的托管目录。
158
+ *
159
+ * 单独一层目录而不是直接扔 `os.tmpdir()`,是为了让「这些文件归我们管」这件事成立:
160
+ * 有明确边界才谈得上定期清理,也才谈得上把它当成输出路径的默认允许根。
161
+ */
162
+ export function browserArtifactDir(): string {
163
+ return path.join(os.tmpdir(), 'kakadeai-browser-artifacts');
164
+ }
165
+
166
+ /** 产物保留时长。超过就在下一次生成产物时顺手清掉。 */
167
+ const ARTIFACT_TTL_MS = 30 * 60_000;
168
+
169
+ /**
170
+ * 清掉过期产物。
171
+ *
172
+ * 在**生成前**做而不是退出时做:进程可能被 kill、可能崩,「退出时清理」恰好在这些情况下
173
+ * 不执行,于是磁盘上留下的正是最不该留的那批。失败一律忽略——清理问题绝不能让一次截图失败。
174
+ *
175
+ * ⚠️ 这里之前**完全没有清理**:开了识图后每次 navigate/snapshot 都写一张 PNG 到 tmp,
176
+ * 永不删除。而浏览器截图拍的是用户已登录的页面,敏感度不低于桌面截图。
177
+ */
178
+ async function pruneStaleArtifacts(dir: string): Promise<void> {
179
+ try {
180
+ const cutoff = Date.now() - ARTIFACT_TTL_MS;
181
+ const entries = await readdir(dir);
182
+ await Promise.all(
183
+ entries.map(async (name) => {
184
+ const target = path.join(dir, name);
185
+ try {
186
+ const info = await stat(target);
187
+ if (info.mtimeMs < cutoff) await rm(target, { force: true, recursive: true });
188
+ } catch { /* 单个文件清不掉不影响其它 */ }
189
+ }),
190
+ );
191
+ } catch { /* 目录不存在或不可读:没什么可清的 */ }
192
+ }
193
+
194
+ /**
195
+ * 生成一个托管的产物输出路径,并确保目录存在、旧产物已清理。
196
+ *
197
+ * 文件名带 pid + 随机数:`Date.now()` 单独用会在同一毫秒内两次调用时撞名
198
+ * (两个并发截图互相覆盖,症状是「截出来的图不是那一刻的画面」)。
199
+ */
200
+ export async function createArtifactPath(prefix = 'browser', ext = 'png'): Promise<string> {
201
+ const dir = browserArtifactDir();
202
+ await mkdir(dir, { recursive: true, mode: 0o700 });
203
+ if (process.platform !== 'win32') await chmod(dir, 0o700).catch(() => undefined);
204
+ await pruneStaleArtifacts(dir);
205
+ const unique = `${Date.now()}-${process.pid}-${randomUUID().slice(0, 8)}`;
206
+ return path.join(dir, `${prefix}-${unique}.${ext}`);
207
+ }
208
+
209
+ /**
210
+ * 允许 `navigate` / `tab new` 打开的 scheme。
211
+ *
212
+ * 白名单而不是黑名单:新 scheme 层出不穷,漏一个就等于放行。
213
+ * 明确拒绝的两类各有理由——
214
+ * · `javascript:` 是在页面里执行代码,那条路必须走 `eval`(受 Developer Mode 约束),
215
+ * 不能靠一个看起来像"打开网址"的动作绕过去;
216
+ * · `data:` 可以直接构造一个带脚本的文档,等价于同一件事。
217
+ */
218
+ const ALLOWED_URL_SCHEMES = new Set(['http', 'https', 'about', 'file']);
219
+
220
+ /**
221
+ * 校验模型给的**输出**路径(截图 / PDF / 录像 / trace 落在哪)。
222
+ *
223
+ * ⚠️ 这里之前完全没有校验:`input.path` 由模型给出、直达子进程,等于一个不受限的
224
+ * 任意文件写入原语——可以覆盖项目文件、写进 `~/.ssh/`、覆盖配置。刺眼的是**同一个
225
+ * 文件里读方向做得很好**(`upload` 有 realpath + 包含判定),写方向一点没管。这个
226
+ * 不对称是无意的:防的时候只想到了"读进来"。
227
+ *
228
+ * 两道判据:
229
+ * ① 必须落在允许根之内(托管产物目录 / 工作目录 / 本次任务批准的文件范围);
230
+ * ② **不允许静默覆盖托管目录之外的已有文件**——把截图写成 `package.json` 不该是
231
+ * 一次工具调用能顺手做到的事。
232
+ *
233
+ * 目标文件通常还不存在,所以 realpath 走的是**父目录**;父目录不存在直接拒绝,
234
+ * 而不是替调用方创建——凭一个路径参数就造目录树同样是越权。
235
+ */
236
+ export async function validateArtifactOutputPath(
237
+ candidate: string,
238
+ allowedRoots: string[],
239
+ ): Promise<{ ok: true; path: string } | { ok: false; error: string }> {
240
+ const absolute = path.isAbsolute(candidate) ? candidate : path.resolve(candidate);
241
+ const parent = path.dirname(absolute);
242
+ let realParent: string;
243
+ try {
244
+ realParent = await realpath(parent);
245
+ } catch {
246
+ return { ok: false, error: `输出目录不存在:${parent}` };
247
+ }
248
+ const roots = await Promise.all(
249
+ allowedRoots.map(async (root) => {
250
+ try { return await realpath(root); } catch { return path.resolve(root); }
251
+ }),
252
+ );
253
+ if (!pathContainedByAny(roots, realParent)) {
254
+ return {
255
+ ok: false,
256
+ error:
257
+ `输出路径超出允许范围:${absolute}\n`
258
+ + `允许写入:${roots.join('、')}\n`
259
+ + '省略 path 参数即可写到托管的临时目录。',
260
+ };
261
+ }
262
+ const managed = await realpath(browserArtifactDir()).catch(() => browserArtifactDir());
263
+ if (!pathContains(managed, realParent)) {
264
+ const exists = await stat(path.join(realParent, path.basename(absolute)))
265
+ .then(() => true)
266
+ .catch(() => false);
267
+ if (exists) {
268
+ return {
269
+ ok: false,
270
+ error: `目标文件已存在,拒绝静默覆盖:${absolute}。请换一个文件名,或省略 path 写到临时目录。`,
271
+ };
272
+ }
273
+ }
274
+ return { ok: true, path: path.join(realParent, path.basename(absolute)) };
275
+ }
276
+
277
+ export function isAllowedBrowserScheme(value: string): boolean {
278
+ const match = /^([a-z][a-z0-9+.-]*):/i.exec(value.trim());
279
+ if (!match) return true; // 无 scheme → normalizeUrl 会补 https://
280
+ return ALLOWED_URL_SCHEMES.has(match[1].toLowerCase());
281
+ }
282
+
283
+ /**
284
+ * 把用户/模型给的地址归一化成 agent-browser `open`/`tab new` 能吃的完整 URL:
285
+ * 已带 scheme(http:/https:/about:/file: 等)原样返回;否则补 `https://`。
286
+ * 工具(navigate/tabNew)与 web 桥(browser/navigate)共用这一份,杜绝「两处两套行为」
287
+ * ——曾出现桥归一化、工具原样透传 `open www.baidu.com` 导致无 scheme 不导航。
288
+ */
289
+ export function normalizeUrl(value: string): string {
290
+ const input = value.trim();
291
+ if (/^[a-z][a-z0-9+.-]*:/i.test(input)) return input;
292
+ return `https://${input}`;
293
+ }
@@ -1,13 +1,31 @@
1
1
  import { randomUUID } from 'node:crypto';
2
+ import { HumanInterventionCoordinator, } from '../../humanIntervention.js';
2
3
  const CAPTCHA_PATTERNS = [
3
4
  /captcha/i,
4
5
  /验证码|安全验证|人机验证|完成.{0,8}验证|拖动.{0,12}(滑块|拼图)|滑动.{0,12}(滑块|拼图)/i,
5
6
  /verify\s+(?:you are|that you are)\s+(?:a\s+)?human/i,
7
+ // Additional patterns: reCAPTCHA / hCaptcha / Turnstile / Cloudflare / etc.
8
+ /i\s*'?m\s+not\s+a\s+robot/i,
9
+ /checking\s+your\s+browser/i,
10
+ /press\s+&?\s*hold|click\s+to\s+verify|verifying\s+you/i,
11
+ /点击验证|按住滑块|图片验证|行为验证/i,
6
12
  ];
7
13
  const OTP_PATTERNS = [/一次性密码|短信验证码|邮件验证码|动态口令|\botp\b|one[- ]time (?:password|code)/i];
8
- const LOGIN_PATTERNS = [/请先登录|登录后继续|扫码登录|sign in to continue|log in to continue/i];
9
- const BLOCK_PATTERNS = [/操作存在异常|访问请求.{0,8}(暂停|拦截)|请求过于频繁|访问受限|access denied|unusual traffic|temporarily blocked/i];
14
+ const PASSWORD_PATTERNS = [
15
+ /请输入.{0,12}(?:密码|口令)|(?:密码|口令).{0,12}(?:必填|不能为空)/i,
16
+ /(?:enter|type).{0,20}(?:password|passcode)|(?:password|passcode).{0,20}(?:required|cannot be empty)/i,
17
+ ];
18
+ const QR_LOGIN_PATTERNS = [/扫码登录|二维码登录|scan (?:the )?qr|qr code.{0,20}(?:login|sign in)/i];
19
+ const LOGIN_PATTERNS = [/请先登录|登录后继续|扫码登录|sign in to continue|log in to continue/i,
20
+ /please\s+(?:log|sign)\s+in/i,
21
+ ];
22
+ const BLOCK_PATTERNS = [/操作存在异常|访问请求.{0,8}(暂停|拦截)|请求过于频繁|访问受限|access denied|unusual traffic|temporarily blocked/i,
23
+ // 403/429 必须落在 HTTP/错误语境里再判风控,避免把「售价 $429」「forbidden fruit」这类正常正文误判成拦截。
24
+ /rate\s+limit(?:ed)?|too\s+many\s+requests|\b403\s+forbidden\b|http[^\n]{0,10}\b(?:403|429)\b|\b(?:error|status)\s*(?:code)?\s*[::]?\s*(?:403|429)\b/i,
25
+ /被封禁|风控拦截|系统异常/i,
26
+ ];
10
27
  const PERMISSION_PATTERNS = [/需要授权|请求权限|permission required|allow access/i];
28
+ const SECURITY_WARNING_PATTERNS = [/安全警告|风险提示|security warning|untrusted (?:application|site|publisher)/i];
11
29
  /** Classifies only explicit page signals. It never attempts to solve or bypass a challenge. */
12
30
  export function detectBrowserChallenge(text) {
13
31
  const normalized = text.replace(/\s+/g, ' ').slice(0, 200_000);
@@ -15,10 +33,16 @@ export function detectBrowserChallenge(text) {
15
33
  return { reason: 'otp', summary: '页面需要人工输入一次性验证码' };
16
34
  if (CAPTCHA_PATTERNS.some((pattern) => pattern.test(normalized)))
17
35
  return { reason: 'captcha', summary: '页面需要人工完成安全验证' };
36
+ if (QR_LOGIN_PATTERNS.some((pattern) => pattern.test(normalized)))
37
+ return { reason: 'qr_login', summary: '页面需要人工扫码登录' };
38
+ if (PASSWORD_PATTERNS.some((pattern) => pattern.test(normalized)))
39
+ return { reason: 'password', summary: '页面需要人工输入密码' };
18
40
  if (LOGIN_PATTERNS.some((pattern) => pattern.test(normalized)))
19
41
  return { reason: 'login', summary: '页面需要人工登录' };
20
42
  if (BLOCK_PATTERNS.some((pattern) => pattern.test(normalized)))
21
43
  return { reason: 'blocked', summary: '网站已暂停或拦截当前访问' };
44
+ if (SECURITY_WARNING_PATTERNS.some((pattern) => pattern.test(normalized)))
45
+ return { reason: 'security_warning', summary: '页面需要人工处理安全警告' };
22
46
  if (PERMISSION_PATTERNS.some((pattern) => pattern.test(normalized)))
23
47
  return { reason: 'permission', summary: '页面需要人工确认权限' };
24
48
  return null;
@@ -26,93 +50,174 @@ export function detectBrowserChallenge(text) {
26
50
  export class BrowserControlCoordinator {
27
51
  state = { owner: 'none', status: 'idle' };
28
52
  listeners = new Set();
29
- waiter = null;
53
+ activeIdentity = null;
54
+ activeIntervention = null;
55
+ interventions = new HumanInterventionCoordinator();
56
+ defaultOwnerId;
57
+ defaultConversationKey;
58
+ constructor(input = {}) {
59
+ this.defaultOwnerId = input.ownerId ?? 'local-owner';
60
+ this.defaultConversationKey = input.conversationKey ?? 'browser:default';
61
+ }
30
62
  getState() { return this.state; }
31
63
  subscribe(listener) {
32
64
  this.listeners.add(listener);
33
65
  listener(this.state);
34
66
  return () => this.listeners.delete(listener);
35
67
  }
68
+ subscribeInterventions(listener) {
69
+ return this.interventions.subscribe(listener);
70
+ }
36
71
  beginAgent(input) {
37
- if (this.state.status === 'waiting_human')
38
- return;
72
+ if (this.state.status === 'waiting_human') {
73
+ if (this.state.runId === input.runId)
74
+ return;
75
+ throw new Error('浏览器正在等待另一个任务的人工处理');
76
+ }
77
+ if (this.state.status === 'running' && this.state.runId !== input.runId) {
78
+ throw new Error('浏览器正在执行另一个会话的原子操作');
79
+ }
80
+ this.activeIdentity = {
81
+ ownerId: this.defaultOwnerId,
82
+ conversationKey: input.conversationKey ?? this.defaultConversationKey,
83
+ runId: input.runId,
84
+ };
39
85
  this.setState({ owner: 'agent', status: 'running', ...input });
40
86
  }
41
87
  finishAgent(runId) {
42
- if (this.state.status === 'running' && this.state.runId === runId)
88
+ if (this.state.status === 'running' && this.state.runId === runId) {
89
+ this.activeIdentity = null;
43
90
  this.setState({ owner: 'none', status: 'idle' });
91
+ }
44
92
  }
45
93
  requestHuman(reason, input) {
46
- if (this.state.status === 'waiting_human')
47
- return this.state.resumeToken;
94
+ if (this.state.status === 'waiting_human') {
95
+ if (!input?.runId || this.state.runId === input.runId)
96
+ return this.state.resumeToken;
97
+ throw new Error('另一个浏览器任务正在等待人工处理');
98
+ }
48
99
  const running = this.state.status === 'running' ? this.state : null;
49
- const resumeToken = randomUUID();
100
+ const identity = {
101
+ ownerId: input?.ownerId ?? this.activeIdentity?.ownerId ?? this.defaultOwnerId,
102
+ conversationKey: input?.conversationKey
103
+ ?? this.activeIdentity?.conversationKey
104
+ ?? running?.conversationKey
105
+ ?? this.defaultConversationKey,
106
+ runId: input?.runId ?? this.activeIdentity?.runId ?? running?.runId ?? randomUUID(),
107
+ };
108
+ const intervention = this.interventions.request({
109
+ ...identity,
110
+ surface: 'browser',
111
+ reason,
112
+ sanitizedSummary: input?.summary ?? summaryForReason(reason),
113
+ });
114
+ this.activeIdentity = identity;
115
+ this.activeIntervention = intervention;
50
116
  this.setState({
51
117
  owner: 'user',
52
118
  status: 'waiting_human',
53
- runId: input?.runId ?? running?.runId ?? randomUUID(),
54
- conversationKey: input?.conversationKey ?? running?.conversationKey,
119
+ runId: identity.runId,
120
+ conversationKey: identity.conversationKey,
55
121
  reason,
56
- resumeToken,
122
+ interventionId: intervention.id,
123
+ resumeToken: intervention.resumeToken,
124
+ ownerId: identity.ownerId,
57
125
  });
58
- return resumeToken;
126
+ return intervention.resumeToken;
59
127
  }
60
128
  takeInteractive(input) {
61
- this.resolveWaiter('resumed');
129
+ if (this.activeIntervention && this.activeIdentity) {
130
+ this.interventions.cancel(this.credential(this.activeIntervention, this.activeIdentity));
131
+ }
132
+ this.activeIntervention = null;
133
+ this.activeIdentity = null;
62
134
  this.setState({ owner: 'user', status: 'interactive', runId: input?.runId, conversationKey: input?.conversationKey });
63
135
  }
64
136
  resume(token) {
65
- if (this.state.status !== 'waiting_human' || (token && token !== this.state.resumeToken))
137
+ if (this.state.status !== 'waiting_human' || !token || !this.activeIntervention || !this.activeIdentity)
66
138
  return false;
67
139
  const previous = this.state;
68
- this.resolveWaiter('resumed');
140
+ const ok = this.interventions.resume(this.credential(this.activeIntervention, this.activeIdentity, token));
141
+ if (!ok)
142
+ return false;
143
+ this.activeIntervention = null;
69
144
  this.setState({ owner: 'agent', status: 'running', runId: previous.runId, conversationKey: previous.conversationKey, action: '正在检查人工操作结果' });
70
145
  return true;
71
146
  }
147
+ resumeIntervention(credential) {
148
+ if (this.state.status !== 'waiting_human' || !this.activeIntervention || !this.activeIdentity)
149
+ return false;
150
+ const previous = this.state;
151
+ if (!this.interventions.resume(credential))
152
+ return false;
153
+ this.activeIntervention = null;
154
+ this.activeIdentity = null;
155
+ this.setState({
156
+ owner: 'agent',
157
+ status: 'running',
158
+ runId: previous.runId,
159
+ conversationKey: previous.conversationKey,
160
+ action: '正在检查人工操作结果',
161
+ });
162
+ return true;
163
+ }
72
164
  stop(token) {
73
- if (this.state.status === 'waiting_human' && token && token !== this.state.resumeToken)
165
+ if (this.state.status === 'waiting_human') {
166
+ if (!token || !this.activeIntervention || !this.activeIdentity)
167
+ return false;
168
+ const ok = this.interventions.cancel(this.credential(this.activeIntervention, this.activeIdentity, token));
169
+ if (!ok)
170
+ return false;
171
+ }
172
+ this.activeIntervention = null;
173
+ this.activeIdentity = null;
174
+ this.setState({ owner: 'user', status: 'interactive' });
175
+ return true;
176
+ }
177
+ cancelIntervention(credential) {
178
+ if (this.state.status !== 'waiting_human' || !this.activeIntervention || !this.activeIdentity)
179
+ return false;
180
+ if (!this.interventions.cancel(credential))
74
181
  return false;
75
- this.resolveWaiter('stopped');
182
+ this.activeIntervention = null;
183
+ this.activeIdentity = null;
76
184
  this.setState({ owner: 'user', status: 'interactive' });
77
185
  return true;
78
186
  }
79
187
  waitForHuman(signal) {
80
188
  if (this.state.status !== 'waiting_human')
81
189
  return Promise.resolve('resumed');
82
- if (this.waiter)
83
- return new Promise((resolve) => {
84
- const unsubscribe = this.subscribe((state) => {
85
- if (state.status !== 'waiting_human') {
86
- unsubscribe();
87
- resolve(state.status === 'running' ? 'resumed' : 'stopped');
88
- }
89
- });
90
- });
91
- return new Promise((resolve, reject) => {
92
- const token = this.state.status === 'waiting_human' ? this.state.resumeToken : '';
93
- const onAbort = () => {
94
- cleanup();
95
- this.waiter = null;
96
- reject(new DOMException('aborted', 'AbortError'));
97
- };
98
- const cleanup = () => signal?.removeEventListener('abort', onAbort);
99
- this.waiter = { token, resolve, cleanup };
100
- signal?.addEventListener('abort', onAbort, { once: true });
101
- if (signal?.aborted)
102
- onAbort();
103
- });
104
- }
105
- resolveWaiter(result) {
106
- const waiter = this.waiter;
107
- if (!waiter)
108
- return;
109
- this.waiter = null;
110
- waiter.cleanup();
111
- waiter.resolve(result);
190
+ const intervention = this.activeIntervention;
191
+ if (!intervention)
192
+ return Promise.resolve('stopped');
193
+ const waiter = { interventionId: intervention.id };
194
+ return this.interventions.wait(waiter.interventionId, signal).then((resolution) => (resolution.status === 'resumed' ? 'resumed' : 'stopped'));
112
195
  }
113
196
  setState(state) {
114
197
  this.state = state;
115
198
  for (const listener of this.listeners)
116
199
  listener(state);
117
200
  }
201
+ credential(intervention, identity, resumeToken = intervention.resumeToken) {
202
+ return {
203
+ id: intervention.id,
204
+ resumeToken,
205
+ ownerId: identity.ownerId,
206
+ conversationKey: identity.conversationKey,
207
+ runId: identity.runId,
208
+ };
209
+ }
210
+ }
211
+ function summaryForReason(reason) {
212
+ switch (reason) {
213
+ case 'captcha': return '页面需要人工完成安全验证';
214
+ case 'otp': return '页面需要人工输入一次性验证码';
215
+ case 'password': return '页面需要人工输入密码';
216
+ case 'qr_login': return '页面需要人工扫码登录';
217
+ case 'login': return '页面需要人工登录';
218
+ case 'permission': return '页面需要人工确认权限';
219
+ case 'security_warning': return '页面需要人工处理安全警告';
220
+ case 'blocked': return '网站已暂停或拦截当前访问';
221
+ case 'manual': return '用户已接管浏览器';
222
+ }
118
223
  }