minimal-agent 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/README.md +6 -5
  2. package/package.json +62 -5
  3. package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
  4. package/plugins/cron-runner/src/commands.js +4 -4
  5. package/plugins/cron-runner/src/commands.ts +4 -4
  6. package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
  7. package/plugins/workflow-runner/commands/workflow.md +57 -90
  8. package/plugins/workflow-runner/commands/workflows.md +4 -4
  9. package/plugins/workflow-runner/plugin.js +24 -13
  10. package/plugins/workflow-runner/plugin.ts +28 -13
  11. package/plugins/workflow-runner/src/discovery.js +160 -0
  12. package/plugins/workflow-runner/src/discovery.ts +194 -0
  13. package/plugins/workflow-runner/src/goalAnchor.js +53 -0
  14. package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
  15. package/plugins/workflow-runner/src/index.js +204 -185
  16. package/plugins/workflow-runner/src/index.ts +249 -224
  17. package/plugins/workflow-runner/src/inputCoercion.js +40 -0
  18. package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
  19. package/plugins/workflow-runner/src/journal.js +224 -0
  20. package/plugins/workflow-runner/src/journal.ts +274 -0
  21. package/plugins/workflow-runner/src/jsonSchema.js +132 -0
  22. package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
  23. package/plugins/workflow-runner/src/moduleResolution.js +64 -0
  24. package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
  25. package/plugins/workflow-runner/src/resourceInputs.js +220 -0
  26. package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
  27. package/plugins/workflow-runner/src/runner.js +404 -467
  28. package/plugins/workflow-runner/src/runner.ts +491 -535
  29. package/plugins/workflow-runner/src/runtime/config.js +46 -0
  30. package/plugins/workflow-runner/src/runtime/config.ts +69 -0
  31. package/plugins/workflow-runner/src/runtime/context.js +1304 -0
  32. package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
  33. package/plugins/workflow-runner/src/runtime/exec.js +34 -0
  34. package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
  35. package/plugins/workflow-runner/src/runtime/flow.js +8 -0
  36. package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
  37. package/plugins/workflow-runner/src/runtime/fs.js +27 -0
  38. package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
  39. package/plugins/workflow-runner/src/runtime/model.js +6 -0
  40. package/plugins/workflow-runner/src/runtime/model.ts +8 -0
  41. package/plugins/workflow-runner/src/runtime/output.js +7 -0
  42. package/plugins/workflow-runner/src/runtime/output.ts +21 -0
  43. package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
  44. package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
  45. package/plugins/workflow-runner/src/runtime/state.js +16 -0
  46. package/plugins/workflow-runner/src/runtime/state.ts +19 -0
  47. package/plugins/workflow-runner/src/runtime/tools.js +3 -0
  48. package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
  49. package/plugins/workflow-runner/src/sandbox.js +141 -0
  50. package/plugins/workflow-runner/src/sandbox.ts +182 -0
  51. package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
  52. package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
  53. package/plugins/workflow-runner/src/taskIdentity.js +172 -152
  54. package/plugins/workflow-runner/src/taskIdentity.ts +233 -200
  55. package/plugins/workflow-runner/src/toolRuntime.js +29 -8
  56. package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
  57. package/plugins/workflow-runner/src/types.js +7 -105
  58. package/plugins/workflow-runner/src/types.ts +155 -520
  59. package/plugins/workflow-runner/src/util.js +47 -0
  60. package/plugins/workflow-runner/src/util.ts +51 -0
  61. package/plugins/workflow-runner/src/workflowContext.js +20 -0
  62. package/plugins/workflow-runner/src/workflowContext.ts +58 -0
  63. package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
  64. package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
  65. package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
  66. package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
  67. package/plugins/workflow-runner/src/workflowState.js +542 -469
  68. package/plugins/workflow-runner/src/workflowState.ts +639 -589
  69. package/plugins/workflow-runner/test/api.test.ts +223 -0
  70. package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
  71. package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
  72. package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
  73. package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
  74. package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
  75. package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
  76. package/plugins/workflow-runner/test/discovery.test.ts +234 -0
  77. package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
  78. package/plugins/workflow-runner/test/goalController.test.ts +317 -0
  79. package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
  80. package/plugins/workflow-runner/test/listResources.test.ts +106 -0
  81. package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
  82. package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
  83. package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
  84. package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
  85. package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
  86. package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
  87. package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
  88. package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
  89. package/skills/pptx/examples/assets/cover-bg.svg +23 -0
  90. package/skills/pptx/examples/assets/icon-spark.svg +5 -0
  91. package/skills/skill-creator/scripts/run_eval.py +3 -2
  92. package/skills/ui-design/SKILL.md +5 -2
  93. package/skills/ui-design/references/layout-composition.md +1 -1
  94. package/skills/workflow-creator/SKILL.md +344 -0
  95. package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
  96. package/skills/workflow-creator/references/api.md +216 -0
  97. package/skills/workflow-creator/references/migration.md +103 -0
  98. package/skills/workflow-creator/references/patterns.md +779 -0
  99. package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
  100. package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
  101. package/skills/workflow-creator/scripts/validate.mjs +275 -0
  102. package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
  103. package/src/advisor/advisorCoordinator.js +503 -0
  104. package/src/advisor/advisorCoordinator.ts +657 -0
  105. package/src/cli/configMenu.tsx +0 -1
  106. package/src/cli/print.js +44 -6
  107. package/src/cli/print.ts +73 -4
  108. package/src/cli/streamJson.js +5 -6
  109. package/src/cli/streamJson.ts +8 -4
  110. package/src/cli/wizards/advisorWizard.js +4 -2
  111. package/src/cli/wizards/advisorWizard.tsx +8 -2
  112. package/src/cli/wizards/contextAdvisorWizard.js +5 -4
  113. package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
  114. package/src/collaboration/taskFlow.js +115 -0
  115. package/src/collaboration/taskFlow.ts +169 -0
  116. package/src/config/configDefaults.js +41 -1
  117. package/src/config/configDefaults.ts +43 -1
  118. package/src/config/configDiagnostics.ts +2 -2
  119. package/src/config/configFile.js +40 -9
  120. package/src/config/configFile.ts +60 -11
  121. package/src/config/configService.js +443 -74
  122. package/src/config/configService.ts +723 -142
  123. package/src/config/numericLimits.js +22 -0
  124. package/src/config/numericLimits.ts +24 -0
  125. package/src/config.js +1 -0
  126. package/src/config.ts +3 -2
  127. package/src/context/budgetPlan.js +1 -1
  128. package/src/context/budgetPlan.ts +1 -1
  129. package/src/context/compact.js +7 -2
  130. package/src/context/compact.ts +9 -2
  131. package/src/context/microCompactLite.js +212 -13
  132. package/src/context/microCompactLite.ts +261 -12
  133. package/src/context/sessionRegistry.js +8 -1
  134. package/src/context/sessionRegistry.ts +18 -5
  135. package/src/context/toolResultArchive.js +72 -0
  136. package/src/context/toolResultArchive.ts +95 -0
  137. package/src/goal/errors.js +8 -0
  138. package/src/goal/errors.ts +18 -0
  139. package/src/goal/goalRuntime.js +344 -0
  140. package/src/goal/goalRuntime.ts +466 -0
  141. package/src/goal/goalStore.js +200 -0
  142. package/src/goal/goalStore.ts +219 -0
  143. package/src/goal/index.js +4 -0
  144. package/src/goal/index.ts +4 -0
  145. package/src/goal/statusMapping.js +71 -0
  146. package/src/goal/statusMapping.ts +95 -0
  147. package/src/goal/types.js +7 -0
  148. package/src/goal/types.ts +179 -0
  149. package/src/goal/workflowController.js +306 -0
  150. package/src/goal/workflowController.ts +360 -0
  151. package/src/harness/evidenceLedger.js +2 -0
  152. package/src/harness/evidenceLedger.ts +1 -0
  153. package/src/harness/stallGuard.js +9 -3
  154. package/src/harness/stallGuard.ts +7 -3
  155. package/src/images/imageResolver.js +161 -0
  156. package/src/images/imageResolver.ts +189 -0
  157. package/src/infra/verification.js +56 -20
  158. package/src/infra/verification.ts +87 -20
  159. package/src/llm/client.js +79 -21
  160. package/src/llm/client.ts +182 -35
  161. package/src/llm/gatewayClient.js +201 -0
  162. package/src/llm/gatewayClient.ts +299 -0
  163. package/src/llm/messageProvenance.js +46 -0
  164. package/src/llm/messageProvenance.ts +58 -0
  165. package/src/llm/modelProvider.js +1 -0
  166. package/src/llm/modelProvider.ts +22 -0
  167. package/src/llm/providers/anthropicFormat.js +108 -1
  168. package/src/llm/providers/anthropicFormat.ts +115 -4
  169. package/src/llm/providers/openaiFormat.js +9 -0
  170. package/src/llm/providers/openaiFormat.ts +12 -3
  171. package/src/loop.js +319 -54
  172. package/src/loop.ts +373 -57
  173. package/src/main.js +4 -1
  174. package/src/main.tsx +4 -1
  175. package/src/managedExecution/index.js +2 -0
  176. package/src/managedExecution/index.ts +2 -0
  177. package/src/managedExecution/registry.js +15 -0
  178. package/src/managedExecution/registry.ts +20 -0
  179. package/src/managedExecution/types.js +1 -0
  180. package/src/managedExecution/types.ts +86 -0
  181. package/src/modelRef.js +9 -0
  182. package/src/modelRef.ts +31 -0
  183. package/src/multimodal.js +14 -28
  184. package/src/multimodal.ts +17 -33
  185. package/src/net/lanDiscovery.js +65 -0
  186. package/src/net/lanDiscovery.ts +82 -0
  187. package/src/plugin-sdk.js +28 -3
  188. package/src/plugin-sdk.ts +39 -6
  189. package/src/plugins/pluginApi.ts +16 -1
  190. package/src/plugins/pluginLoader.js +4 -0
  191. package/src/plugins/pluginLoader.ts +6 -0
  192. package/src/plugins/pluginRunner.js +152 -9
  193. package/src/plugins/pluginRunner.ts +186 -8
  194. package/src/plugins/slashFallback.js +113 -0
  195. package/src/plugins/slashFallback.ts +137 -0
  196. package/src/prompts/policyRegistry.js +15 -8
  197. package/src/prompts/policyRegistry.ts +18 -8
  198. package/src/prompts/promptAssembly.js +103 -12
  199. package/src/prompts/promptAssembly.ts +114 -12
  200. package/src/prompts/skillList.js +21 -3
  201. package/src/prompts/skillList.ts +27 -2
  202. package/src/prompts/stableSystemPrompt.js +18 -0
  203. package/src/prompts/stableSystemPrompt.ts +33 -0
  204. package/src/runtime/agentRuntimeContext.js +8 -2
  205. package/src/runtime/agentRuntimeContext.ts +17 -4
  206. package/src/session/runAgentTurn.js +78 -12
  207. package/src/session/runAgentTurn.ts +105 -23
  208. package/src/session/runTurn.js +10 -9
  209. package/src/session/runTurn.ts +17 -8
  210. package/src/session/sessionState.ts +2 -0
  211. package/src/team/auth.js +186 -0
  212. package/src/team/auth.ts +298 -0
  213. package/src/team/cli.js +105 -0
  214. package/src/team/cli.ts +122 -0
  215. package/src/team/onlineToolContract.js +64 -0
  216. package/src/team/onlineToolContract.ts +109 -0
  217. package/src/team/toolPolicy.js +36 -0
  218. package/src/team/toolPolicy.ts +75 -0
  219. package/src/tools/advisor/advisor.js +148 -37
  220. package/src/tools/advisor/advisor.ts +161 -38
  221. package/src/tools/advisor/advisorDecision.js +71 -0
  222. package/src/tools/advisor/advisorDecision.ts +85 -0
  223. package/src/tools/agent/agent.js +18 -5
  224. package/src/tools/agent/agent.ts +21 -7
  225. package/src/tools/agent/agentControl.js +246 -0
  226. package/src/tools/agent/agentControl.ts +284 -0
  227. package/src/tools/agent/agentTaskService.js +40 -1
  228. package/src/tools/agent/agentTaskService.ts +44 -1
  229. package/src/tools/agent/agentTypes.js +14 -2
  230. package/src/tools/agent/agentTypes.ts +15 -2
  231. package/src/tools/applypatch/applyPatch.js +362 -0
  232. package/src/tools/applypatch/applyPatch.ts +428 -0
  233. package/src/tools/bash/bash.js +70 -18
  234. package/src/tools/bash/bash.ts +75 -20
  235. package/src/tools/capabilities.js +10 -5
  236. package/src/tools/capabilities.ts +10 -5
  237. package/src/tools/computeruse/computerIntent.js +32 -0
  238. package/src/tools/computeruse/computerIntent.ts +46 -0
  239. package/src/tools/computeruse/telemetryLog.js +12 -0
  240. package/src/tools/computeruse/telemetryLog.ts +16 -0
  241. package/src/tools/computeruse/vision/telemetry.js +1 -0
  242. package/src/tools/computeruse/vision/telemetry.ts +1 -0
  243. package/src/tools/effectiveToolSet.js +4 -0
  244. package/src/tools/effectiveToolSet.ts +4 -0
  245. package/src/tools/index.js +21 -2
  246. package/src/tools/index.ts +33 -2
  247. package/src/tools/powershell/powershell.js +18 -1
  248. package/src/tools/powershell/powershell.ts +18 -2
  249. package/src/tools/presentresult/presentresult.js +6 -15
  250. package/src/tools/presentresult/presentresult.ts +6 -15
  251. package/src/tools/read/read.js +69 -11
  252. package/src/tools/read/read.ts +70 -11
  253. package/src/tools/shared/fileUtils.js +1 -2
  254. package/src/tools/shared/fileUtils.ts +1 -2
  255. package/src/tools/shared/permanentDeleteGuard.js +34 -3
  256. package/src/tools/shared/permanentDeleteGuard.ts +43 -6
  257. package/src/tools/shared/toolOutputStore.js +23 -1
  258. package/src/tools/shared/toolOutputStore.ts +22 -1
  259. package/src/tools/skill/skill.js +9 -3
  260. package/src/tools/skill/skill.ts +8 -4
  261. package/src/tools/toolPromptGuides.js +117 -0
  262. package/src/tools/toolPromptGuides.ts +137 -0
  263. package/src/tools/webbrowser/agentBrowserPath.js +19 -9
  264. package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
  265. package/src/tools/webbrowser/browser.js +27 -11
  266. package/src/tools/webbrowser/browser.ts +68 -24
  267. package/src/tools/webbrowser/browserAutomation.js +329 -0
  268. package/src/tools/webbrowser/browserAutomation.ts +467 -0
  269. package/src/tools/webbrowser/browserAxState.js +97 -0
  270. package/src/tools/webbrowser/browserAxState.ts +142 -0
  271. package/src/tools/webbrowser/browserBatch.js +1578 -0
  272. package/src/tools/webbrowser/browserBatch.ts +2382 -0
  273. package/src/tools/webbrowser/browserCdp.js +208 -0
  274. package/src/tools/webbrowser/browserCdp.ts +255 -0
  275. package/src/tools/webbrowser/browserConditions.js +24 -0
  276. package/src/tools/webbrowser/browserConditions.ts +45 -0
  277. package/src/tools/webbrowser/browserContent.js +281 -0
  278. package/src/tools/webbrowser/browserContent.ts +438 -0
  279. package/src/tools/webbrowser/browserControl.js +131 -30
  280. package/src/tools/webbrowser/browserControl.ts +210 -50
  281. package/src/tools/webbrowser/browserDaemon.js +22 -14
  282. package/src/tools/webbrowser/browserDaemon.ts +54 -21
  283. package/src/tools/webbrowser/browserFrames.js +47 -0
  284. package/src/tools/webbrowser/browserFrames.ts +54 -0
  285. package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
  286. package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
  287. package/src/tools/webbrowser/browserLocator.js +140 -0
  288. package/src/tools/webbrowser/browserLocator.ts +194 -0
  289. package/src/tools/webbrowser/browserOutcome.js +10 -17
  290. package/src/tools/webbrowser/browserOutcome.ts +48 -31
  291. package/src/tools/webbrowser/browserRecovery.js +58 -0
  292. package/src/tools/webbrowser/browserRecovery.ts +83 -0
  293. package/src/tools/webbrowser/browserRunRegistry.js +35 -3
  294. package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
  295. package/src/tools/webbrowser/browserService.js +28 -2
  296. package/src/tools/webbrowser/browserService.ts +49 -9
  297. package/src/tools/webbrowser/browserStream.js +132 -33
  298. package/src/tools/webbrowser/browserStream.ts +224 -50
  299. package/src/tools/webbrowser/browserTabs.js +48 -15
  300. package/src/tools/webbrowser/browserTabs.ts +178 -96
  301. package/src/tools/webbrowser/webbrowser.js +683 -144
  302. package/src/tools/webbrowser/webbrowser.ts +979 -236
  303. package/src/tools/webfetch/preapproved.js +0 -2
  304. package/src/tools/webfetch/preapproved.ts +0 -2
  305. package/src/tools/websearch/websearch.js +129 -8
  306. package/src/tools/websearch/websearch.ts +138 -6
  307. package/src/types.ts +450 -42
  308. package/src/ui/App.js +377 -58
  309. package/src/ui/App.tsx +576 -111
  310. package/src/ui/LiveArea.js +14 -8
  311. package/src/ui/LiveArea.tsx +14 -10
  312. package/src/ui/MessageList.js +63 -20
  313. package/src/ui/MessageList.tsx +101 -16
  314. package/src/ui/ModelPicker.js +7 -4
  315. package/src/ui/ModelPicker.tsx +55 -8
  316. package/src/ui/hooks/useChat.js +152 -163
  317. package/src/ui/hooks/useChat.ts +396 -334
  318. package/src/ui/loginCommand.js +56 -0
  319. package/src/ui/loginCommand.ts +63 -0
  320. package/src/ui/toolProgress.js +41 -0
  321. package/src/ui/toolProgress.ts +44 -0
  322. package/src/web/browserBridge.js +20 -2
  323. package/src/web/browserBridge.ts +20 -2
  324. package/src/web/protocol.ts +1897 -194
  325. package/workflows/adaptive-research/workflow.mjs +86 -0
  326. package/workflows/e2e-capabilities/workflow.mjs +102 -0
  327. package/workflows/e2e-llm/workflow.mjs +52 -0
  328. package/workflows/e2e-smoke/workflow.mjs +59 -0
  329. package/workflows/e2e-vision-schema/README.md +48 -0
  330. package/workflows/e2e-vision-schema/workflow.mjs +149 -0
  331. package/workflows/e2e-write-greet/workflow.mjs +37 -0
  332. package/workflows/file-census/README.md +14 -0
  333. package/workflows/file-census/scripts/.gitkeep +2 -0
  334. package/workflows/file-census/workflow.mjs +110 -0
  335. package/workflows/goal/README.md +166 -0
  336. package/workflows/goal/prompts/audit.md +57 -0
  337. package/workflows/goal/prompts/contract.md +56 -0
  338. package/workflows/goal/prompts/iterate.md +37 -0
  339. package/workflows/goal/workflow.mjs +501 -0
  340. package/workflows/illustrate/README.md +90 -0
  341. package/workflows/illustrate/assets/engines.json +20 -0
  342. package/workflows/illustrate/assets/extract-baidu.js +21 -0
  343. package/workflows/illustrate/assets/extract-bing.js +33 -0
  344. package/workflows/illustrate/assets/extract-google.js +31 -0
  345. package/workflows/illustrate/workflow.mjs +301 -0
  346. package/workflows/plan/workflow.mjs +389 -0
  347. package/workflows/question-crops/README.md +35 -0
  348. package/workflows/question-crops/prompts/inventory.md +23 -0
  349. package/workflows/question-crops/scripts/image-ops.mjs +305 -0
  350. package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
  351. package/workflows/question-crops/workflow.mjs +172 -0
  352. package/workflows/ralph-loop/workflow.mjs +162 -0
  353. package/workflows/research/DESIGN.md +449 -0
  354. package/workflows/research/README.md +74 -0
  355. package/workflows/research/prompts/s0-goal.md +43 -0
  356. package/workflows/research/prompts/s1-early-exit.md +22 -0
  357. package/workflows/research/prompts/s1-map.md +58 -0
  358. package/workflows/research/prompts/s2-options.md +53 -0
  359. package/workflows/research/prompts/s3-deep-dive.md +56 -0
  360. package/workflows/research/prompts/s4-verify.md +74 -0
  361. package/workflows/research/prompts/s5-synthesis.md +81 -0
  362. package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
  363. package/workflows/research/prompts/s6-package.md +32 -0
  364. package/workflows/research/scripts/build-report.mjs +98 -0
  365. package/workflows/research/workflow.mjs +288 -0
  366. package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
  367. package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
  368. package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
  369. package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
  370. package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
  371. package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
  372. package/workflows/wechat-reading-shots/workflow.mjs +266 -0
  373. package/workflows/wrong-question-docx/README.md +55 -0
  374. package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
  375. package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
  376. package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
  377. package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
  378. package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
  379. package/workflows/wrong-question-docx/workflow.mjs +485 -0
  380. package/plugins/workflow-runner/src/agentCommon.js +0 -354
  381. package/plugins/workflow-runner/src/agentCommon.ts +0 -435
  382. package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
  383. package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
  384. package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
  385. package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
  386. package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
  387. package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
  388. package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
  389. package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
  390. package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
  391. package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
  392. package/plugins/workflow-runner/src/dslManifest.js +0 -72
  393. package/plugins/workflow-runner/src/dslManifest.ts +0 -127
  394. package/plugins/workflow-runner/src/expressions.js +0 -618
  395. package/plugins/workflow-runner/src/expressions.ts +0 -622
  396. package/plugins/workflow-runner/src/loader.js +0 -573
  397. package/plugins/workflow-runner/src/loader.ts +0 -721
  398. package/plugins/workflow-runner/src/multimodal.js +0 -38
  399. package/plugins/workflow-runner/src/multimodal.ts +0 -64
  400. package/plugins/workflow-runner/src/recovery.js +0 -119
  401. package/plugins/workflow-runner/src/recovery.ts +0 -149
  402. package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
  403. package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
  404. package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -41
  405. package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -50
  406. package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
  407. package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
  408. package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
  409. package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
  410. package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
  411. package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
  412. package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
  413. package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
  414. package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -208
  415. package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -256
  416. package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -58
  417. package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -68
  418. package/plugins/workflow-runner/src/stepPipeline.js +0 -277
  419. package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
  420. package/plugins/workflow-runner/test/agent.test.ts +0 -587
  421. package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
  422. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
  423. package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
  424. package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -147
  425. package/plugins/workflow-runner/test/context-files.test.ts +0 -320
  426. package/plugins/workflow-runner/test/e2e.test.ts +0 -370
  427. package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -226
  428. package/plugins/workflow-runner/test/expressions.test.ts +0 -397
  429. package/plugins/workflow-runner/test/foundation.test.ts +0 -126
  430. package/plugins/workflow-runner/test/graceful.test.ts +0 -139
  431. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
  432. package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
  433. package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
  434. package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
  435. package/plugins/workflow-runner/test/loader.test.ts +0 -617
  436. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
  437. package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
  438. package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
  439. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -87
  440. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
  441. package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
  442. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -220
  443. package/plugins/workflow-runner/test/recovery.test.ts +0 -265
  444. package/plugins/workflow-runner/test/runner.test.ts +0 -911
  445. package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -379
  446. package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
  447. package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -150
  448. package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
  449. package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -338
  450. package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -270
  451. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
  452. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
  453. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
  454. package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
  455. package/plugins/workflow-runner/test/word.load.test.ts +0 -208
  456. package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
  457. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
  458. package/src/advisorState.js +0 -22
  459. package/src/cli/configWizard.js +0 -293
  460. package/src/tools/toolProgressBridge.js +0 -48
  461. package/workflows/adaptive-research.yaml +0 -50
  462. package/workflows/book-review-short.yaml +0 -125
  463. package/workflows/e2e-write-greet.yaml +0 -31
  464. package/workflows/illustrate.yaml +0 -227
  465. package/workflows/plan.yaml +0 -470
  466. package/workflows/ppt.yaml +0 -1670
  467. package/workflows/ralph-loop.yaml +0 -199
  468. package/workflows/schema.json +0 -176
  469. package/workflows/webppt.yaml +0 -496
  470. package/workflows/wechat-reading-shots.yaml +0 -315
  471. package/workflows/word.yaml +0 -720
  472. package/workflows/youtube-shorts.yaml +0 -202
@@ -39,34 +39,52 @@ const CAPTCHA_PATTERNS = [
39
39
  /press\s+&?\s*hold|click\s+to\s+verify|verifying\s+you/i,
40
40
  /点击验证|按住滑块|图片验证|行为验证/i,
41
41
  ];
42
- const OTP_PATTERNS = [/一次性密码|短信验证码|邮件验证码|动态口令|\botp\b|one[- ]time (?:password|code)/i];
42
+ const OTP_PATTERNS = [
43
+ /一次性密码|短信验证码|邮件验证码|动态口令|\botp\b|one[- ]time (?:password|code)/i,
44
+ ];
43
45
  const PASSWORD_PATTERNS = [
44
46
  /请输入.{0,12}(?:密码|口令)|(?:密码|口令).{0,12}(?:必填|不能为空)/i,
45
47
  /(?:enter|type).{0,20}(?:password|passcode)|(?:password|passcode).{0,20}(?:required|cannot be empty)/i,
46
48
  ];
47
- const QR_LOGIN_PATTERNS = [/扫码登录|二维码登录|scan (?:the )?qr|qr code.{0,20}(?:login|sign in)/i];
48
- const LOGIN_PATTERNS = [/请先登录|登录后继续|扫码登录|sign in to continue|log in to continue/i,
49
+ const QR_LOGIN_PATTERNS = [
50
+ /扫码登录|二维码登录|scan (?:the )?qr|qr code.{0,20}(?:login|sign in)/i,
51
+ ];
52
+ const LOGIN_PATTERNS = [
53
+ /请先登录|登录后继续|扫码登录|sign in to continue|log in to continue/i,
49
54
  /please\s+(?:log|sign)\s+in/i,
50
55
  ];
51
- const BLOCK_PATTERNS = [/操作存在异常|访问请求.{0,8}(暂停|拦截)|请求过于频繁|访问受限|access denied|unusual traffic|temporarily blocked/i,
56
+ const BLOCK_PATTERNS = [
57
+ /操作存在异常|访问请求.{0,8}(暂停|拦截)|请求过于频繁|访问受限|access denied|unusual traffic|temporarily blocked/i,
52
58
  // 403/429 必须落在 HTTP/错误语境里再判风控,避免把「售价 $429」「forbidden fruit」这类正常正文误判成拦截。
53
59
  /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,
54
60
  /被封禁|风控拦截|系统异常/i,
55
61
  ];
56
- const PERMISSION_PATTERNS = [/需要授权|请求权限|permission required|allow access/i];
57
- const SECURITY_WARNING_PATTERNS = [/安全警告|风险提示|security warning|untrusted (?:application|site|publisher)/i];
62
+ const PERMISSION_PATTERNS = [
63
+ /需要授权|请求权限|permission required|allow access/i,
64
+ ];
65
+ const SECURITY_WARNING_PATTERNS = [
66
+ /安全警告|风险提示|security warning|untrusted (?:application|site|publisher)/i,
67
+ ];
58
68
 
59
69
  /** Classifies only explicit page signals. It never attempts to solve or bypass a challenge. */
60
70
  export function detectBrowserChallenge(text: string): BrowserChallenge | null {
61
71
  const normalized = text.replace(/\s+/g, ' ').slice(0, 200_000);
62
- if (OTP_PATTERNS.some((pattern) => pattern.test(normalized))) return { reason: 'otp', summary: '页面需要人工输入一次性验证码' };
63
- if (CAPTCHA_PATTERNS.some((pattern) => pattern.test(normalized))) return { reason: 'captcha', summary: '页面需要人工完成安全验证' };
64
- if (QR_LOGIN_PATTERNS.some((pattern) => pattern.test(normalized))) return { reason: 'qr_login', summary: '页面需要人工扫码登录' };
65
- if (PASSWORD_PATTERNS.some((pattern) => pattern.test(normalized))) return { reason: 'password', summary: '页面需要人工输入密码' };
66
- if (LOGIN_PATTERNS.some((pattern) => pattern.test(normalized))) return { reason: 'login', summary: '页面需要人工登录' };
67
- if (BLOCK_PATTERNS.some((pattern) => pattern.test(normalized))) return { reason: 'blocked', summary: '网站已暂停或拦截当前访问' };
68
- if (SECURITY_WARNING_PATTERNS.some((pattern) => pattern.test(normalized))) return { reason: 'security_warning', summary: '页面需要人工处理安全警告' };
69
- if (PERMISSION_PATTERNS.some((pattern) => pattern.test(normalized))) return { reason: 'permission', summary: '页面需要人工确认权限' };
72
+ if (OTP_PATTERNS.some((pattern) => pattern.test(normalized)))
73
+ return { reason: 'otp', summary: '页面需要人工输入一次性验证码' };
74
+ if (CAPTCHA_PATTERNS.some((pattern) => pattern.test(normalized)))
75
+ return { reason: 'captcha', summary: '页面需要人工完成安全验证' };
76
+ if (QR_LOGIN_PATTERNS.some((pattern) => pattern.test(normalized)))
77
+ return { reason: 'qr_login', summary: '页面需要人工扫码登录' };
78
+ if (PASSWORD_PATTERNS.some((pattern) => pattern.test(normalized)))
79
+ return { reason: 'password', summary: '页面需要人工输入密码' };
80
+ if (LOGIN_PATTERNS.some((pattern) => pattern.test(normalized)))
81
+ return { reason: 'login', summary: '页面需要人工登录' };
82
+ if (BLOCK_PATTERNS.some((pattern) => pattern.test(normalized)))
83
+ return { reason: 'blocked', summary: '网站已暂停或拦截当前访问' };
84
+ if (SECURITY_WARNING_PATTERNS.some((pattern) => pattern.test(normalized)))
85
+ return { reason: 'security_warning', summary: '页面需要人工处理安全警告' };
86
+ if (PERMISSION_PATTERNS.some((pattern) => pattern.test(normalized)))
87
+ return { reason: 'permission', summary: '页面需要人工确认权限' };
70
88
  return null;
71
89
  }
72
90
 
@@ -74,6 +92,25 @@ type HumanWaiter = {
74
92
  interventionId: string;
75
93
  };
76
94
 
95
+ /**
96
+ * 判定「持有者已经死了」的阈值。
97
+ *
98
+ * 取值有依据,不是拍脑袋:单次动作的 `timeout` 在 zod schema 里是 `.max(300_000)`,
99
+ * 语义是**整个序列**(前置探测 + 动作 + 收尾重探)的总时长。所以任何合法的 running
100
+ * 都有 300s 的硬上界,取 2× 作阈值 —— 合法长动作绝不会被误接管,而泄漏的锁在一个
101
+ * 动作周期后自动可用,不再是「本进程剩余生命周期内浏览器永久不可用」。
102
+ *
103
+ * ⚠️ 改小之前先确认 webbrowser.ts 里 preTimer 与 runSequence 各自的计时器在最长路径上
104
+ * 不会叠加;`browserControl.test.ts` 用假时钟锁住了这个不变量的行为面。
105
+ */
106
+ export const DEFAULT_STALE_AFTER_MS = 600_000;
107
+
108
+ export interface StaleTakeover {
109
+ previousRunId?: string;
110
+ previousAction?: string;
111
+ heldMs: number;
112
+ }
113
+
77
114
  export class BrowserControlCoordinator {
78
115
  private state: BrowserControlState = { owner: 'none', status: 'idle' };
79
116
  private listeners = new Set<(state: BrowserControlState) => void>();
@@ -82,13 +119,42 @@ export class BrowserControlCoordinator {
82
119
  private readonly interventions = new HumanInterventionCoordinator();
83
120
  private readonly defaultOwnerId: string;
84
121
  private readonly defaultConversationKey: string;
122
+ private readonly staleAfterMs: number;
123
+ private readonly now: () => number;
124
+ private readonly onStaleTakeover: (info: StaleTakeover) => void;
125
+ /** 进入 running 的时刻;由 setState 统一维护,所以 resume 之后也会重新计时。 */
126
+ private runningSince = 0;
85
127
 
86
- constructor(input: { ownerId?: string; conversationKey?: string } = {}) {
128
+ constructor(
129
+ input: {
130
+ ownerId?: string;
131
+ conversationKey?: string;
132
+ /** 注入时钟:让「超过阈值」在测试里用假时钟验证,而不是真等 10 分钟。 */
133
+ now?: () => number;
134
+ staleAfterMs?: number;
135
+ onStaleTakeover?: (info: StaleTakeover) => void;
136
+ } = {},
137
+ ) {
87
138
  this.defaultOwnerId = input.ownerId ?? 'local-owner';
88
139
  this.defaultConversationKey = input.conversationKey ?? 'browser:default';
140
+ this.now = input.now ?? (() => Date.now());
141
+ this.staleAfterMs = input.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
142
+ // 接管**绝不能静默**:不留痕的话,「这把锁曾经泄漏过」就再也查不到了,
143
+ // 而下一次排查会从一个已经自愈的现场开始 —— 那比报错更难查。
144
+ this.onStaleTakeover =
145
+ input.onStaleTakeover ??
146
+ ((info) => {
147
+ process.stderr.write(
148
+ `[browser] 上一个持有者(runId=${info.previousRunId ?? '?'}` +
149
+ `${info.previousAction ? `, action=${info.previousAction}` : ''})` +
150
+ `已持锁 ${Math.round(info.heldMs / 1000)}s,超过 ${Math.round(this.staleAfterMs / 1000)}s 判为泄漏,已接管。\n`,
151
+ );
152
+ });
89
153
  }
90
154
 
91
- getState(): BrowserControlState { return this.state; }
155
+ getState(): BrowserControlState {
156
+ return this.state;
157
+ }
92
158
 
93
159
  subscribe(listener: (state: BrowserControlState) => void): () => void {
94
160
  this.listeners.add(listener);
@@ -96,17 +162,42 @@ export class BrowserControlCoordinator {
96
162
  return () => this.listeners.delete(listener);
97
163
  }
98
164
 
99
- subscribeInterventions(listener: (event: HumanInterventionEvent) => void): () => void {
165
+ subscribeInterventions(
166
+ listener: (event: HumanInterventionEvent) => void,
167
+ ): () => void {
100
168
  return this.interventions.subscribe(listener);
101
169
  }
102
170
 
103
- beginAgent(input: { runId: string; conversationKey?: string; action?: string }): void {
171
+ beginAgent(input: {
172
+ runId: string;
173
+ conversationKey?: string;
174
+ action?: string;
175
+ }): void {
104
176
  if (this.state.status === 'waiting_human') {
105
177
  if (this.state.runId === input.runId) return;
106
178
  throw new Error('浏览器正在等待另一个任务的人工处理');
107
179
  }
108
180
  if (this.state.status === 'running' && this.state.runId !== input.runId) {
109
- throw new Error('浏览器正在执行另一个会话的原子操作');
181
+ const heldMs = this.now() - this.runningSince;
182
+ if (heldMs < this.staleAfterMs) {
183
+ // 带上持锁者身份与已持有时长:这把锁是进程级单例,一旦泄漏,后续每次调用都撞
184
+ // 同一句话,而"另一个会话"是谁、卡在哪个动作上、卡了多久,现场早就没了。
185
+ // 时长尤其关键 —— 它把「真并发」和「已泄漏」在报错里一眼分开。
186
+ throw new Error(
187
+ `浏览器正在执行另一个会话的原子操作(持有者 runId=${this.state.runId ?? '?'}` +
188
+ `${this.state.action ? `, action=${this.state.action}` : ''}` +
189
+ `${this.activeIdentity?.conversationKey ? `, conversation=${this.activeIdentity.conversationKey}` : ''}` +
190
+ `, 已持有 ${Math.round(heldMs / 1000)}s)`,
191
+ );
192
+ }
193
+ // 超过任何合法动作的上界 —— 持有者的 finally 没能跑(进程被外部掐断、
194
+ // finally 自身抛错、或将来某条还没被想到的路径)。不接管的话,这个进程里的
195
+ // 浏览器就永久不可用了。接管并留痕,让「失败一次」不再等于「彻底报废」。
196
+ this.onStaleTakeover({
197
+ previousRunId: this.state.runId,
198
+ previousAction: this.state.action,
199
+ heldMs,
200
+ });
110
201
  }
111
202
  this.activeIdentity = {
112
203
  ownerId: this.defaultOwnerId,
@@ -125,20 +216,32 @@ export class BrowserControlCoordinator {
125
216
 
126
217
  requestHuman(
127
218
  reason: BrowserHumanReason,
128
- input?: { runId?: string; conversationKey?: string; ownerId?: string; summary?: string },
219
+ input?: {
220
+ runId?: string;
221
+ conversationKey?: string;
222
+ ownerId?: string;
223
+ summary?: string;
224
+ },
129
225
  ): string {
130
226
  if (this.state.status === 'waiting_human') {
131
- if (!input?.runId || this.state.runId === input.runId) return this.state.resumeToken;
227
+ if (!input?.runId || this.state.runId === input.runId)
228
+ return this.state.resumeToken;
132
229
  throw new Error('另一个浏览器任务正在等待人工处理');
133
230
  }
134
231
  const running = this.state.status === 'running' ? this.state : null;
135
232
  const identity: HumanInterventionIdentity = {
136
- ownerId: input?.ownerId ?? this.activeIdentity?.ownerId ?? this.defaultOwnerId,
137
- conversationKey: input?.conversationKey
138
- ?? this.activeIdentity?.conversationKey
139
- ?? running?.conversationKey
140
- ?? this.defaultConversationKey,
141
- runId: input?.runId ?? this.activeIdentity?.runId ?? running?.runId ?? randomUUID(),
233
+ ownerId:
234
+ input?.ownerId ?? this.activeIdentity?.ownerId ?? this.defaultOwnerId,
235
+ conversationKey:
236
+ input?.conversationKey ??
237
+ this.activeIdentity?.conversationKey ??
238
+ running?.conversationKey ??
239
+ this.defaultConversationKey,
240
+ runId:
241
+ input?.runId ??
242
+ this.activeIdentity?.runId ??
243
+ running?.runId ??
244
+ randomUUID(),
142
245
  };
143
246
  const intervention = this.interventions.request({
144
247
  ...identity,
@@ -163,25 +266,51 @@ export class BrowserControlCoordinator {
163
266
 
164
267
  takeInteractive(input?: { runId?: string; conversationKey?: string }): void {
165
268
  if (this.activeIntervention && this.activeIdentity) {
166
- this.interventions.cancel(this.credential(this.activeIntervention, this.activeIdentity));
269
+ this.interventions.cancel(
270
+ this.credential(this.activeIntervention, this.activeIdentity),
271
+ );
167
272
  }
168
273
  this.activeIntervention = null;
169
274
  this.activeIdentity = null;
170
- this.setState({ owner: 'user', status: 'interactive', runId: input?.runId, conversationKey: input?.conversationKey });
275
+ this.setState({
276
+ owner: 'user',
277
+ status: 'interactive',
278
+ runId: input?.runId,
279
+ conversationKey: input?.conversationKey,
280
+ });
171
281
  }
172
282
 
173
283
  resume(token?: string): boolean {
174
- if (this.state.status !== 'waiting_human' || !token || !this.activeIntervention || !this.activeIdentity) return false;
284
+ if (
285
+ this.state.status !== 'waiting_human' ||
286
+ !token ||
287
+ !this.activeIntervention ||
288
+ !this.activeIdentity
289
+ )
290
+ return false;
175
291
  const previous = this.state;
176
- const ok = this.interventions.resume(this.credential(this.activeIntervention, this.activeIdentity, token));
292
+ const ok = this.interventions.resume(
293
+ this.credential(this.activeIntervention, this.activeIdentity, token),
294
+ );
177
295
  if (!ok) return false;
178
296
  this.activeIntervention = null;
179
- this.setState({ owner: 'agent', status: 'running', runId: previous.runId, conversationKey: previous.conversationKey, action: '正在检查人工操作结果' });
297
+ this.setState({
298
+ owner: 'agent',
299
+ status: 'running',
300
+ runId: previous.runId,
301
+ conversationKey: previous.conversationKey,
302
+ action: '正在检查人工操作结果',
303
+ });
180
304
  return true;
181
305
  }
182
306
 
183
307
  resumeIntervention(credential: HumanInterventionCredential): boolean {
184
- if (this.state.status !== 'waiting_human' || !this.activeIntervention || !this.activeIdentity) return false;
308
+ if (
309
+ this.state.status !== 'waiting_human' ||
310
+ !this.activeIntervention ||
311
+ !this.activeIdentity
312
+ )
313
+ return false;
185
314
  const previous = this.state;
186
315
  if (!this.interventions.resume(credential)) return false;
187
316
  this.activeIntervention = null;
@@ -198,8 +327,11 @@ export class BrowserControlCoordinator {
198
327
 
199
328
  stop(token?: string): boolean {
200
329
  if (this.state.status === 'waiting_human') {
201
- if (!token || !this.activeIntervention || !this.activeIdentity) return false;
202
- const ok = this.interventions.cancel(this.credential(this.activeIntervention, this.activeIdentity, token));
330
+ if (!token || !this.activeIntervention || !this.activeIdentity)
331
+ return false;
332
+ const ok = this.interventions.cancel(
333
+ this.credential(this.activeIntervention, this.activeIdentity, token),
334
+ );
203
335
  if (!ok) return false;
204
336
  }
205
337
  this.activeIntervention = null;
@@ -209,7 +341,12 @@ export class BrowserControlCoordinator {
209
341
  }
210
342
 
211
343
  cancelIntervention(credential: HumanInterventionCredential): boolean {
212
- if (this.state.status !== 'waiting_human' || !this.activeIntervention || !this.activeIdentity) return false;
344
+ if (
345
+ this.state.status !== 'waiting_human' ||
346
+ !this.activeIntervention ||
347
+ !this.activeIdentity
348
+ )
349
+ return false;
213
350
  if (!this.interventions.cancel(credential)) return false;
214
351
  this.activeIntervention = null;
215
352
  this.activeIdentity = null;
@@ -218,18 +355,32 @@ export class BrowserControlCoordinator {
218
355
  }
219
356
 
220
357
  waitForHuman(signal?: AbortSignal): Promise<'resumed' | 'stopped'> {
221
- if (this.state.status !== 'waiting_human') return Promise.resolve('resumed');
358
+ if (this.state.status !== 'waiting_human')
359
+ return Promise.resolve('resumed');
222
360
  const intervention = this.activeIntervention;
223
361
  if (!intervention) return Promise.resolve('stopped');
224
362
  const waiter: HumanWaiter = { interventionId: intervention.id };
225
- return this.interventions.wait(waiter.interventionId, signal).then((resolution) => (
226
- resolution.status === 'resumed' ? 'resumed' : 'stopped'
227
- ));
363
+ return this.interventions
364
+ .wait(waiter.interventionId, signal)
365
+ .then((resolution) =>
366
+ resolution.status === 'resumed' ? 'resumed' : 'stopped',
367
+ );
228
368
  }
229
369
 
230
370
  private setState(state: BrowserControlState): void {
371
+ // 所有进入 running 的路径都在这里重新计时(beginAgent 之外,还有 resume /
372
+ // resumeIntervention 从人工处理回到 agent 手里)。⚠️ 少了这一条,人工处理
373
+ // 花掉的时间会被算进持锁时长 —— 用户认真填了十分钟验证码,恢复的瞬间就被判成
374
+ // 「泄漏」让别人接管,正好惩罚了最该被保护的那条路径。
375
+ if (state.status === 'running') this.runningSince = this.now();
231
376
  this.state = state;
232
- for (const listener of this.listeners) listener(state);
377
+ for (const listener of this.listeners) {
378
+ try {
379
+ listener(state);
380
+ } catch {
381
+ // Observers are telemetry/UI extensions; control state must remain authoritative.
382
+ }
383
+ }
233
384
  }
234
385
 
235
386
  private credential(
@@ -249,14 +400,23 @@ export class BrowserControlCoordinator {
249
400
 
250
401
  function summaryForReason(reason: BrowserHumanReason): string {
251
402
  switch (reason) {
252
- case 'captcha': return '页面需要人工完成安全验证';
253
- case 'otp': return '页面需要人工输入一次性验证码';
254
- case 'password': return '页面需要人工输入密码';
255
- case 'qr_login': return '页面需要人工扫码登录';
256
- case 'login': return '页面需要人工登录';
257
- case 'permission': return '页面需要人工确认权限';
258
- case 'security_warning': return '页面需要人工处理安全警告';
259
- case 'blocked': return '网站已暂停或拦截当前访问';
260
- case 'manual': return '用户已接管浏览器';
403
+ case 'captcha':
404
+ return '页面需要人工完成安全验证';
405
+ case 'otp':
406
+ return '页面需要人工输入一次性验证码';
407
+ case 'password':
408
+ return '页面需要人工输入密码';
409
+ case 'qr_login':
410
+ return '页面需要人工扫码登录';
411
+ case 'login':
412
+ return '页面需要人工登录';
413
+ case 'permission':
414
+ return '页面需要人工确认权限';
415
+ case 'security_warning':
416
+ return '页面需要人工处理安全警告';
417
+ case 'blocked':
418
+ return '网站已暂停或拦截当前访问';
419
+ case 'manual':
420
+ return '用户已接管浏览器';
261
421
  }
262
422
  }
@@ -1,6 +1,6 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { dirname, isAbsolute } from 'node:path';
3
- import { resolveAgentBrowserPath, resolveBundledChromium, resolveSystemBrowser } from './agentBrowserPath.js';
3
+ import { resolveAgentBrowserPath, resolveBundledChromium, resolveSystemBrowser, } from './agentBrowserPath.js';
4
4
  import { browserGlobalArgs, runBounded, sessionArgs, spawnRunner, } from './browser.js';
5
5
  /** Daemon 启动(拉起 Chrome + CDP 就绪)总预算:超时即掐断并抛可操作错误,绝不无限挂。 */
6
6
  export const DAEMON_BRINGUP_TIMEOUT_MS = 45_000;
@@ -50,17 +50,7 @@ export async function reclaimStaleDaemons(deps = {}) {
50
50
  const root = dir.toLowerCase().replace(/'/g, "''");
51
51
  const ps =
52
52
  // (a) 本 App 路径下的 agent-browser daemon + 其 Chrome 子树。
53
- `Get-CimInstance Win32_Process -Filter "Name='agent-browser.exe'" | ` +
54
- `Where-Object { $_.ExecutablePath -and $_.ExecutablePath.ToLower().StartsWith('${root}') } | ` +
55
- `ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null | Out-Null }; ` +
56
- // (b) 真孤儿 agent-browser Chrome(父进程已死)。daemon 崩溃后残留、上面的 agent-browser 树
57
- // 够不着,堆积起来会把新 daemon 的 Chrome 拉起拖到几十秒(实测 18 棵孤儿 → 卡 43s;
58
- // 清空后同一启动仅 1s)。**只杀父进程已不在的孤儿**,绝不误伤有活父进程的正常浏览器
59
- // (安装版 App 的活浏览器父 agent-browser 健在 → 不匹配,天然豁免)。
60
- `$alive=@{}; Get-CimInstance Win32_Process | ForEach-Object { $alive[[int]$_.ProcessId]=$true }; ` +
61
- `Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" | ` +
62
- `Where-Object { ($_.CommandLine -match 'agent-browser-chrome' -or $_.CommandLine -match '\\.minimal-agent.{0,40}profiles') -and -not $alive.ContainsKey([int]$_.ParentProcessId) } | ` +
63
- `ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null | Out-Null }`;
53
+ `Get-CimInstance Win32_Process -Filter "Name='agent-browser.exe'" | Where-Object { $_.ExecutablePath -and $_.ExecutablePath.ToLower().StartsWith('${root}') } | ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null | Out-Null }; $alive=@{}; Get-CimInstance Win32_Process | ForEach-Object { $alive[[int]$_.ProcessId]=$true }; Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" | Where-Object { ($_.CommandLine -match 'agent-browser-chrome' -or $_.CommandLine -match '\\.minimal-agent.{0,40}profiles') -and -not $alive.ContainsKey([int]$_.ParentProcessId) } | ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null | Out-Null }`;
64
54
  await exec('powershell', ['-NoProfile', '-NonInteractive', '-Command', ps]);
65
55
  }
66
56
  else {
@@ -94,6 +84,17 @@ export async function ensureDaemon(sessionName = 'minimal-agent', opts = {}, dep
94
84
  const bin = await deps.resolveBin();
95
85
  if (!bin)
96
86
  throw new Error('未找到 agent-browser 可执行文件');
87
+ if (opts.forceRestart) {
88
+ // The shipped wrapper may launch a daemon from a different resolved
89
+ // global path. In that case executable-prefix process cleanup is
90
+ // intentionally conservative and cannot see the daemon. Ask the
91
+ // session manager to close its own browser first, then apply the scoped
92
+ // process cleanup as a second line of defense.
93
+ await deps
94
+ .runner(bin, [...sessionArgs(sessionName), 'close', '--all'], opts.signal)
95
+ .catch(() => undefined);
96
+ await reclaimStaleDaemons(opts.reclaim);
97
+ }
97
98
  const status = await readStatus(bin, sessionName, { signal: opts.signal }, deps);
98
99
  if (status.port && status.connected) {
99
100
  ports.set(sessionName, status.port);
@@ -143,10 +144,17 @@ export async function ensureDaemon(sessionName = 'minimal-agent', opts = {}, dep
143
144
  ]);
144
145
  if (ac.signal.aborted)
145
146
  throw bringUpError();
146
- if (opened.code !== 0 && browserRuntimeMissing(`${opened.stderr}\n${opened.stdout}`)) {
147
+ if (opened.code !== 0 &&
148
+ browserRuntimeMissing(`${opened.stderr}\n${opened.stdout}`)) {
147
149
  const chromium = resolveBundledChromium();
148
150
  if (chromium) {
149
- opened = await run([...globals, '--executable-path', chromium, 'open', 'about:blank']);
151
+ opened = await run([
152
+ ...globals,
153
+ '--executable-path',
154
+ chromium,
155
+ 'open',
156
+ 'about:blank',
157
+ ]);
150
158
  }
151
159
  else {
152
160
  const installed = await run(['install']);
@@ -1,13 +1,17 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { dirname, isAbsolute } from 'node:path';
3
3
 
4
- import { resolveAgentBrowserPath, resolveBundledChromium, resolveSystemBrowser } from './agentBrowserPath.js';
5
4
  import {
5
+ resolveAgentBrowserPath,
6
+ resolveBundledChromium,
7
+ resolveSystemBrowser,
8
+ } from './agentBrowserPath.js';
9
+ import {
10
+ type AgentBrowserRunner,
6
11
  browserGlobalArgs,
7
12
  runBounded,
8
13
  sessionArgs,
9
14
  spawnRunner,
10
- type AgentBrowserRunner,
11
15
  } from './browser.js';
12
16
 
13
17
  export interface BrowserDaemonInfo {
@@ -30,6 +34,8 @@ export interface EnsureDaemonOptions {
30
34
  signal?: AbortSignal;
31
35
  timeoutMs?: number;
32
36
  reclaim?: ReclaimDeps;
37
+ /** Forcefully rebuild a browser whose CDP endpoint has gone stale. */
38
+ forceRestart?: boolean;
33
39
  }
34
40
 
35
41
  /**
@@ -78,7 +84,9 @@ export interface ReclaimDeps {
78
84
  * 或系统上别处的 agent-browser 用法(对标 scripts/dev-browser.ts::reclaimStalePort)。无法作用域限定
79
85
  * (PATH 上裸二进制 / 没装)时保守放弃,不做全局杀。幂等、best-effort(失败静默)。
80
86
  */
81
- export async function reclaimStaleDaemons(deps: ReclaimDeps = {}): Promise<void> {
87
+ export async function reclaimStaleDaemons(
88
+ deps: ReclaimDeps = {},
89
+ ): Promise<void> {
82
90
  const platform = deps.platform ?? process.platform;
83
91
  const exec = deps.exec ?? realReclaimExec;
84
92
  const resolveDir =
@@ -94,17 +102,7 @@ export async function reclaimStaleDaemons(deps: ReclaimDeps = {}): Promise<void>
94
102
  const root = dir.toLowerCase().replace(/'/g, "''");
95
103
  const ps =
96
104
  // (a) 本 App 路径下的 agent-browser daemon + 其 Chrome 子树。
97
- `Get-CimInstance Win32_Process -Filter "Name='agent-browser.exe'" | ` +
98
- `Where-Object { $_.ExecutablePath -and $_.ExecutablePath.ToLower().StartsWith('${root}') } | ` +
99
- `ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null | Out-Null }; ` +
100
- // (b) 真孤儿 agent-browser Chrome(父进程已死)。daemon 崩溃后残留、上面的 agent-browser 树
101
- // 够不着,堆积起来会把新 daemon 的 Chrome 拉起拖到几十秒(实测 18 棵孤儿 → 卡 43s;
102
- // 清空后同一启动仅 1s)。**只杀父进程已不在的孤儿**,绝不误伤有活父进程的正常浏览器
103
- // (安装版 App 的活浏览器父 agent-browser 健在 → 不匹配,天然豁免)。
104
- `$alive=@{}; Get-CimInstance Win32_Process | ForEach-Object { $alive[[int]$_.ProcessId]=$true }; ` +
105
- `Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" | ` +
106
- `Where-Object { ($_.CommandLine -match 'agent-browser-chrome' -or $_.CommandLine -match '\\.minimal-agent.{0,40}profiles') -and -not $alive.ContainsKey([int]$_.ParentProcessId) } | ` +
107
- `ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null | Out-Null }`;
105
+ `Get-CimInstance Win32_Process -Filter "Name='agent-browser.exe'" | Where-Object { $_.ExecutablePath -and $_.ExecutablePath.ToLower().StartsWith('${root}') } | ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null | Out-Null }; $alive=@{}; Get-CimInstance Win32_Process | ForEach-Object { $alive[[int]$_.ProcessId]=$true }; Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" | Where-Object { ($_.CommandLine -match 'agent-browser-chrome' -or $_.CommandLine -match '\\.minimal-agent.{0,40}profiles') -and -not $alive.ContainsKey([int]$_.ParentProcessId) } | ForEach-Object { taskkill /F /T /PID $_.ProcessId 2>$null | Out-Null }`;
108
106
  await exec('powershell', ['-NoProfile', '-NonInteractive', '-Command', ps]);
109
107
  } else {
110
108
  // unix:按可执行路径前缀 pkill agent-browser(其 Chrome 子进程随之收尾)。unix 孤儿会被
@@ -121,7 +119,9 @@ function parsePort(text: string): number | null {
121
119
  }
122
120
 
123
121
  function browserRuntimeMissing(text: string): boolean {
124
- return /browser\s+(?:not found|was not found)|chrom(?:e|ium).*(?:not found|missing)|executable.*(?:not found|does not exist)|run ['\"]?agent-browser install/i.test(text);
122
+ return /browser\s+(?:not found|was not found)|chrom(?:e|ium).*(?:not found|missing)|executable.*(?:not found|does not exist)|run ['\"]?agent-browser install/i.test(
123
+ text,
124
+ );
125
125
  }
126
126
 
127
127
  export { browserRuntimeMissing as _browserRuntimeMissing };
@@ -153,7 +153,23 @@ export async function ensureDaemon(
153
153
  ): Promise<BrowserDaemonInfo> {
154
154
  const bin = await deps.resolveBin();
155
155
  if (!bin) throw new Error('未找到 agent-browser 可执行文件');
156
- const status = await readStatus(bin, sessionName, { signal: opts.signal }, deps);
156
+ if (opts.forceRestart) {
157
+ // The shipped wrapper may launch a daemon from a different resolved
158
+ // global path. In that case executable-prefix process cleanup is
159
+ // intentionally conservative and cannot see the daemon. Ask the
160
+ // session manager to close its own browser first, then apply the scoped
161
+ // process cleanup as a second line of defense.
162
+ await deps
163
+ .runner(bin, [...sessionArgs(sessionName), 'close', '--all'], opts.signal)
164
+ .catch(() => undefined);
165
+ await reclaimStaleDaemons(opts.reclaim);
166
+ }
167
+ const status = await readStatus(
168
+ bin,
169
+ sessionName,
170
+ { signal: opts.signal },
171
+ deps,
172
+ );
157
173
  if (status.port && status.connected) {
158
174
  ports.set(sessionName, status.port);
159
175
  return { sessionName, streamPort: status.port, alreadyRunning: true };
@@ -202,18 +218,30 @@ export async function ensureDaemon(
202
218
  'about:blank',
203
219
  ]);
204
220
  if (ac.signal.aborted) throw bringUpError();
205
- if (opened.code !== 0 && browserRuntimeMissing(`${opened.stderr}\n${opened.stdout}`)) {
221
+ if (
222
+ opened.code !== 0 &&
223
+ browserRuntimeMissing(`${opened.stderr}\n${opened.stdout}`)
224
+ ) {
206
225
  const chromium = resolveBundledChromium();
207
226
  if (chromium) {
208
- opened = await run([...globals, '--executable-path', chromium, 'open', 'about:blank']);
227
+ opened = await run([
228
+ ...globals,
229
+ '--executable-path',
230
+ chromium,
231
+ 'open',
232
+ 'about:blank',
233
+ ]);
209
234
  } else {
210
235
  const installed = await run(['install']);
211
- if (installed.code === 0) opened = await run([...globals, 'open', 'about:blank']);
236
+ if (installed.code === 0)
237
+ opened = await run([...globals, 'open', 'about:blank']);
212
238
  }
213
239
  if (ac.signal.aborted) throw bringUpError();
214
240
  }
215
241
  if (opened.code !== 0) {
216
- throw new Error((opened.stderr || opened.stdout || '浏览器启动失败').trim());
242
+ throw new Error(
243
+ (opened.stderr || opened.stdout || '浏览器启动失败').trim(),
244
+ );
217
245
  }
218
246
  const streamPort = await waitForStreamPort(sessionName, 10_000, deps);
219
247
  ports.set(sessionName, streamPort);
@@ -235,7 +263,12 @@ export async function stopDaemon(
235
263
  const bin = await deps.resolveBin();
236
264
  ports.delete(sessionName);
237
265
  if (bin) {
238
- await runBounded(bin, [...sessionArgs(sessionName), 'close'], { timeoutMs: STATUS_TIMEOUT_MS }, deps.runner);
266
+ await runBounded(
267
+ bin,
268
+ [...sessionArgs(sessionName), 'close'],
269
+ { timeoutMs: STATUS_TIMEOUT_MS },
270
+ deps.runner,
271
+ );
239
272
  }
240
273
  // 优雅 close 之外再 tree-kill 一遍本 App 路径下的残留:崩溃/close 不彻底时仍杜绝临时 profile
241
274
  // 孤儿 Chrome 堆积(实测同时存活 ≥2 棵孤儿 Chrome 树的根因)。
@@ -0,0 +1,47 @@
1
+ /** Fixed, read-only expression for observing nested same-origin frame trees. */
2
+ export const FRAME_INFO_EXPRESSION = `(() => { const walk = (doc, parentId = '', path = '') => Array.from(doc.querySelectorAll('iframe,frame')).flatMap((el, index) => { const id = 'dom-frame-' + (path ? path + '-' : '') + index; const item = { id, ...(parentId ? { parentId } : {}), url: el.src || '', name: el.name || undefined }; try { return [item, ...walk(el.contentDocument, id, id.replace('dom-frame-', ''))]; } catch { return [item]; } }); return walk(document); })()`;
3
+ /** Normalize the recursive, browser-owned frame tree returned by CDP. */
4
+ export function normalizeFrameTree(value) {
5
+ const frames = [];
6
+ const walk = (node, parentId) => {
7
+ if (!node || typeof node !== 'object')
8
+ return;
9
+ const item = node;
10
+ if (typeof item.frame?.id !== 'string' || !item.frame.id)
11
+ return;
12
+ const id = item.frame.id;
13
+ frames.push({
14
+ id,
15
+ ...(parentId ? { parentId } : {}),
16
+ url: typeof item.frame.url === 'string' ? item.frame.url : '',
17
+ ...(typeof item.frame.name === 'string' && item.frame.name
18
+ ? { name: item.frame.name }
19
+ : {}),
20
+ });
21
+ for (const child of item.childFrames ?? [])
22
+ walk(child, id);
23
+ };
24
+ walk(value);
25
+ return frames;
26
+ }
27
+ export function normalizeFrameInfo(value) {
28
+ if (!Array.isArray(value))
29
+ return [];
30
+ return value.flatMap((item) => {
31
+ if (!item || typeof item !== 'object')
32
+ return [];
33
+ const frame = item;
34
+ if (typeof frame.id !== 'string' || typeof frame.url !== 'string')
35
+ return [];
36
+ return [
37
+ {
38
+ id: frame.id,
39
+ url: frame.url,
40
+ ...(typeof frame.parentId === 'string'
41
+ ? { parentId: frame.parentId }
42
+ : {}),
43
+ ...(typeof frame.name === 'string' ? { name: frame.name } : {}),
44
+ },
45
+ ];
46
+ });
47
+ }