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
@@ -0,0 +1,438 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { basename, extname, join } from 'node:path';
3
+
4
+ import type { CdpConnection } from './browserAutomation.js';
5
+
6
+ export type BrowserPageAsset = {
7
+ kind:
8
+ | 'image'
9
+ | 'video'
10
+ | 'audio'
11
+ | 'stylesheet'
12
+ | 'script'
13
+ | 'font'
14
+ | 'icon'
15
+ | 'other';
16
+ url: string;
17
+ source: 'dom' | 'performance' | 'inline';
18
+ name?: string;
19
+ alt?: string;
20
+ type?: string;
21
+ width?: number;
22
+ height?: number;
23
+ inline?: boolean;
24
+ content?: string;
25
+ };
26
+
27
+ export type BrowserPageContent = {
28
+ url: string;
29
+ title: string;
30
+ text: string;
31
+ html?: string;
32
+ format:
33
+ | 'text'
34
+ | 'txt'
35
+ | 'markdown'
36
+ | 'md'
37
+ | 'html'
38
+ | 'json'
39
+ | 'page'
40
+ | 'google-workspace'
41
+ | 'youtube-transcript'
42
+ | 'pdf'
43
+ | 'docx'
44
+ | 'xlsx'
45
+ | 'csv'
46
+ | 'pptx';
47
+ };
48
+
49
+ export type BrowserWorkspaceExportFormat =
50
+ | 'pdf'
51
+ | 'docx'
52
+ | 'xlsx'
53
+ | 'csv'
54
+ | 'pptx';
55
+
56
+ /**
57
+ * The browser evaluates these fixed expressions. They are deliberately not
58
+ * user supplied JavaScript: page inspection and clipboard are first-class
59
+ * browser capabilities, while arbitrary eval remains a separate developer
60
+ * action.
61
+ */
62
+ export const PAGE_ASSETS_EXPRESSION = `(() => {
63
+ const absolute = (value) => { try { return new URL(value, location.href).href; } catch { return ''; } };
64
+ const result = [];
65
+ const add = (kind, value, source, extra = {}) => {
66
+ const url = absolute(value);
67
+ if (!url || url.startsWith('javascript:')) return;
68
+ result.push({ kind, url, source, ...extra });
69
+ };
70
+ for (const el of document.images) add('image', el.currentSrc || el.src, 'dom', {
71
+ name: el.getAttribute('name') || undefined,
72
+ alt: el.alt || undefined,
73
+ width: Math.round(el.naturalWidth || el.getBoundingClientRect().width || 0) || undefined,
74
+ height: Math.round(el.naturalHeight || el.getBoundingClientRect().height || 0) || undefined,
75
+ });
76
+ for (const el of document.querySelectorAll('video, audio')) add(el.tagName.toLowerCase(), el.currentSrc || el.src, 'dom', {
77
+ type: el.getAttribute('type') || undefined,
78
+ width: Math.round(el.getBoundingClientRect().width || 0) || undefined,
79
+ height: Math.round(el.getBoundingClientRect().height || 0) || undefined,
80
+ });
81
+ for (const el of document.querySelectorAll('source')) add(el.parentElement?.tagName.toLowerCase() === 'audio' ? 'audio' : 'video', el.src, 'dom', { type: el.type || undefined });
82
+ for (const el of document.querySelectorAll('link[rel~="stylesheet"], link[rel~="icon"], link[rel="preload"]')) {
83
+ const rel = (el.rel || '').toLowerCase();
84
+ add(rel.includes('icon') ? 'icon' : rel.includes('stylesheet') ? 'stylesheet' : 'other', el.href, 'dom', { type: el.as || el.type || undefined });
85
+ }
86
+ for (const el of document.querySelectorAll('script[src]')) add('script', el.src, 'dom', { type: el.type || undefined });
87
+ for (const el of document.querySelectorAll('style')) {
88
+ const text = el.textContent || '';
89
+ if (text) result.push({ kind: 'stylesheet', url: 'inline:style-' + result.length, source: 'inline', inline: true, type: 'text/css', content: text.slice(0, 200000) });
90
+ }
91
+ for (const entry of performance.getEntriesByType('resource')) {
92
+ const name = entry.name || '';
93
+ if (/\\.(?:woff2?|ttf|otf)(?:[?#]|$)/i.test(name)) add('font', name, 'performance');
94
+ }
95
+ const seen = new Set();
96
+ return result.filter((item) => { const key = item.kind + '|' + item.url; if (seen.has(key)) return false; seen.add(key); return true; });
97
+ })()`;
98
+
99
+ export const CLIPBOARD_READ_TEXT_EXPRESSION = 'navigator.clipboard.readText()';
100
+
101
+ /** Fetch one page asset inside the authenticated browser context. */
102
+ export function browserAssetFetchExpression(url: string): string {
103
+ return `(async () => {
104
+ const response = await fetch(${JSON.stringify(url)}, { credentials: 'include' });
105
+ const bytes = new Uint8Array(await response.arrayBuffer());
106
+ let binary = '';
107
+ const chunk = 0x8000;
108
+ for (let i = 0; i < bytes.length; i += chunk) binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
109
+ return { ok: response.ok, status: response.status, type: response.headers.get('content-type') || '', base64: btoa(binary) };
110
+ })()`;
111
+ }
112
+
113
+ export const CLIPBOARD_READ_EXPRESSION = `(() => navigator.clipboard.read().then(async (items) => {
114
+ const result = [];
115
+ for (const item of items) {
116
+ for (const type of item.types) {
117
+ const blob = await item.getType(type);
118
+ const bytes = new Uint8Array(await blob.arrayBuffer());
119
+ let binary = '';
120
+ const chunk = 0x8000;
121
+ for (let i = 0; i < bytes.length; i += chunk) binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
122
+ result.push({ type, base64: btoa(binary), size: bytes.length });
123
+ }
124
+ }
125
+ return result;
126
+ }))()`;
127
+
128
+ export function clipboardWriteExpression(
129
+ items: Array<{ type: string; base64: string }>,
130
+ ): string {
131
+ return `(async () => {
132
+ const items = ${JSON.stringify(items)};
133
+ await navigator.clipboard.write([new ClipboardItem(Object.fromEntries(items.map((item) => [item.type, fetch('data:' + item.type + ';base64,' + item.base64).then((response) => response.blob())])))]);
134
+ return true;
135
+ })()`;
136
+ }
137
+
138
+ export function clipboardWriteTextExpression(value: string): string {
139
+ return `navigator.clipboard.writeText(${JSON.stringify(value)}).then(() => true)`;
140
+ }
141
+
142
+ export function pageContentExpression(
143
+ format: BrowserPageContent['format'],
144
+ selector?: string,
145
+ ): string {
146
+ if (['pdf', 'docx', 'xlsx', 'csv', 'pptx'].includes(format)) {
147
+ return googleWorkspaceExportExpression(
148
+ format as BrowserWorkspaceExportFormat,
149
+ );
150
+ }
151
+ const selectorLiteral = JSON.stringify(selector || 'body');
152
+ return `(() => {
153
+ const root = document.querySelector(${selectorLiteral}) || document.body;
154
+ const text = (root.innerText || root.textContent || '').trim();
155
+ const html = root.outerHTML || '';
156
+ const title = document.title || '';
157
+ const url = location.href;
158
+ const value = { url, title, text, html, format: ${JSON.stringify(format)} };
159
+ if (${JSON.stringify(format)} === 'youtube-transcript') {
160
+ value.text = Array.from(document.querySelectorAll('ytd-transcript-segment-renderer, [data-purpose="transcript-segment"], .transcript-segment')).map((el) => (el.innerText || el.textContent || '').trim()).filter(Boolean).join('\\n') || text;
161
+ }
162
+ if (${JSON.stringify(format)} === 'google-workspace') {
163
+ value.text = Array.from(root.querySelectorAll('[contenteditable="true"], [role="textbox"], p, li, table')).map((el) => (el.innerText || el.textContent || '').trim()).filter(Boolean).join('\\n');
164
+ }
165
+ if (['text', 'txt', 'markdown', 'md', 'youtube-transcript', 'google-workspace'].includes(${JSON.stringify(format)})) delete value.html;
166
+ return value;
167
+ })()`;
168
+ }
169
+
170
+ /**
171
+ * Export an authenticated Google Workspace document from the current tab.
172
+ * The browser performs the request so the current login/session cookies are
173
+ * preserved; the agent never receives or stores cookies itself. The result is
174
+ * a compact base64 envelope consumed by BrowserBatch's deliverable writer.
175
+ */
176
+ export function googleWorkspaceExportExpression(
177
+ format: BrowserWorkspaceExportFormat,
178
+ ): string {
179
+ return `(() => {
180
+ const href = location.href;
181
+ const match = href.match(/https?:\\/\\/(?:docs|sheets|slides)\\.google\\.com\\/(document|spreadsheets|presentation)\\/d\\/([^/]+)/i);
182
+ if (!match) return { error: '当前页面不是可导出的 Google Workspace 文档' };
183
+ const kind = match[1];
184
+ const id = match[2];
185
+ const format = ${JSON.stringify(format)};
186
+ const base = kind === 'document'
187
+ ? 'https://docs.google.com/document/d/' + id + '/export?format=' + format
188
+ : kind === 'spreadsheets'
189
+ ? 'https://docs.google.com/spreadsheets/d/' + id + '/export?format=' + format
190
+ : format === 'pptx'
191
+ ? 'https://docs.google.com/presentation/d/' + id + '/export/pptx'
192
+ : 'https://docs.google.com/presentation/d/' + id + '/export?format=' + format;
193
+ return fetch(base, { credentials: 'include' }).then(async (response) => {
194
+ if (!response.ok) return { error: 'Workspace 导出失败 HTTP ' + response.status, url: base };
195
+ const bytes = new Uint8Array(await response.arrayBuffer());
196
+ let binary = '';
197
+ const chunk = 0x8000;
198
+ for (let i = 0; i < bytes.length; i += chunk) binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
199
+ return { __browser_binary_base64: btoa(binary), format, url: base, size: bytes.length };
200
+ });
201
+ })()`;
202
+ }
203
+
204
+ export function markdownFromPageContent(
205
+ content: Pick<BrowserPageContent, 'title' | 'url' | 'text'>,
206
+ ): string {
207
+ const heading = content.title ? `# ${content.title}\n\n` : '';
208
+ return `${`${heading}${content.text}${content.url ? `\n\nSource: ${content.url}` : ''}`.trim()}\n`;
209
+ }
210
+
211
+ export function assetFileName(asset: BrowserPageAsset, index: number): string {
212
+ if (asset.url.startsWith('inline:'))
213
+ return `${asset.url.slice('inline:'.length)}.css`;
214
+ try {
215
+ const parsed = new URL(asset.url);
216
+ const name = basename(parsed.pathname) || `asset-${index}`;
217
+ const extension =
218
+ extname(name) ||
219
+ (asset.kind === 'stylesheet'
220
+ ? '.css'
221
+ : asset.kind === 'script'
222
+ ? '.js'
223
+ : '');
224
+ return `${String(index + 1).padStart(3, '0')}-${name.replace(/[^a-zA-Z0-9._-]/g, '_')}${extension && !name.endsWith(extension) ? extension : ''}`;
225
+ } catch {
226
+ return `${String(index + 1).padStart(3, '0')}-asset`;
227
+ }
228
+ }
229
+
230
+ export function assetBundleManifest(
231
+ assets: BrowserPageAsset[],
232
+ directory: string,
233
+ ): string {
234
+ return `${JSON.stringify(
235
+ { version: 1, directory: join(directory, 'assets'), assets },
236
+ null,
237
+ 2,
238
+ )}\n`;
239
+ }
240
+
241
+ export async function bundlePageAssets(
242
+ assets: BrowserPageAsset[],
243
+ directory: string,
244
+ fetchImpl: (url: string) => Promise<Response> = (url) => fetch(url),
245
+ ): Promise<{
246
+ directory: string;
247
+ files: string[];
248
+ failures: Array<{ url: string; error: string }>;
249
+ }> {
250
+ const assetDirectory = join(directory, 'assets');
251
+ await mkdir(assetDirectory, { recursive: true, mode: 0o700 });
252
+ const files: string[] = [];
253
+ const failures: Array<{ url: string; error: string }> = [];
254
+ for (const [index, asset] of assets.entries()) {
255
+ if (asset.inline && asset.content) {
256
+ const path = join(assetDirectory, assetFileName(asset, index));
257
+ await writeFile(path, asset.content, { mode: 0o600 });
258
+ files.push(path);
259
+ continue;
260
+ }
261
+ if (!/^https?:\/\//i.test(asset.url)) continue;
262
+ try {
263
+ const response = await fetchImpl(asset.url);
264
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
265
+ const path = join(assetDirectory, assetFileName(asset, index));
266
+ await writeFile(path, Buffer.from(await response.arrayBuffer()), {
267
+ mode: 0o600,
268
+ });
269
+ files.push(path);
270
+ } catch (error) {
271
+ failures.push({
272
+ url: asset.url,
273
+ error: error instanceof Error ? error.message : String(error),
274
+ });
275
+ }
276
+ }
277
+ await writeFile(
278
+ join(directory, 'manifest.json'),
279
+ assetBundleManifest(assets, directory),
280
+ { mode: 0o600 },
281
+ );
282
+ return { directory, files, failures };
283
+ }
284
+
285
+ function cdpValue(result: unknown): unknown {
286
+ if (!result || typeof result !== 'object') return undefined;
287
+ const value = (result as { result?: { value?: unknown } }).result?.value;
288
+ return value;
289
+ }
290
+
291
+ /** Read URLs in short-lived background targets and always close those targets. */
292
+ export async function readUrlsInBackground(
293
+ cdp: CdpConnection,
294
+ urls: string[],
295
+ timeoutMs: number,
296
+ contentType: 'text' | 'html' | 'domSnapshot' = 'text',
297
+ concurrency = 3,
298
+ signal?: AbortSignal,
299
+ ): Promise<
300
+ Array<{
301
+ url: string;
302
+ finalUrl?: string;
303
+ title?: string;
304
+ text?: string;
305
+ error?: string;
306
+ }>
307
+ > {
308
+ const results: Array<{
309
+ url: string;
310
+ finalUrl?: string;
311
+ title?: string;
312
+ text?: string;
313
+ error?: string;
314
+ }> = [];
315
+ let next = 0;
316
+ const worker = async () => {
317
+ while (true) {
318
+ const index = next++;
319
+ if (index >= urls.length) return;
320
+ if (signal?.aborted) return;
321
+ const url = urls[index]!;
322
+ let targetId = '';
323
+ try {
324
+ const created = (await cdp.send(
325
+ 'Target.createTarget',
326
+ { url },
327
+ undefined,
328
+ timeoutMs,
329
+ signal,
330
+ )) as {
331
+ targetId?: string;
332
+ };
333
+ targetId = created.targetId ?? '';
334
+ if (!targetId) throw new Error('浏览器未返回后台标签 ID');
335
+ const attached = (await cdp.send(
336
+ 'Target.attachToTarget',
337
+ { targetId, flatten: true },
338
+ undefined,
339
+ timeoutMs,
340
+ signal,
341
+ )) as { sessionId?: string };
342
+ const sessionId = attached.sessionId;
343
+ if (!sessionId) throw new Error('无法连接后台标签');
344
+ const deadline = Date.now() + timeoutMs;
345
+ let state: unknown;
346
+ while (Date.now() < deadline) {
347
+ state = cdpValue(
348
+ await cdp
349
+ .send(
350
+ 'Runtime.evaluate',
351
+ {
352
+ expression:
353
+ '({ready: document.readyState, url: location.href, title: document.title})',
354
+ returnByValue: true,
355
+ },
356
+ sessionId,
357
+ timeoutMs,
358
+ signal,
359
+ )
360
+ .catch(() => ({})),
361
+ );
362
+ if (
363
+ state &&
364
+ typeof state === 'object' &&
365
+ ['interactive', 'complete'].includes(
366
+ String((state as { ready?: unknown }).ready),
367
+ )
368
+ )
369
+ break;
370
+ await new Promise((resolve) => setTimeout(resolve, 100));
371
+ }
372
+ let page: unknown;
373
+ if (contentType === 'domSnapshot') {
374
+ const ax = await cdp
375
+ .send('Accessibility.getFullAXTree', {}, sessionId)
376
+ .catch(() => undefined);
377
+ page = {
378
+ url: String((state as { url?: unknown } | undefined)?.url ?? url),
379
+ title: String(
380
+ (state as { title?: unknown } | undefined)?.title ?? '',
381
+ ),
382
+ text: JSON.stringify(ax ?? { nodes: [] }).slice(0, 300000),
383
+ };
384
+ } else {
385
+ const expression =
386
+ contentType === 'html'
387
+ ? `({url: location.href, title: document.title, text: document.documentElement?.outerHTML?.slice(0, 300000) || ''})`
388
+ : `({url: location.href, title: document.title, text: (document.body?.innerText || document.body?.textContent || '').slice(0, 200000)})`;
389
+ page = cdpValue(
390
+ await cdp.send(
391
+ 'Runtime.evaluate',
392
+ {
393
+ expression,
394
+ returnByValue: true,
395
+ },
396
+ sessionId,
397
+ timeoutMs,
398
+ signal,
399
+ ),
400
+ );
401
+ }
402
+ if (!page || typeof page !== 'object')
403
+ throw new Error('无法读取后台页面内容');
404
+ results[index] = {
405
+ url,
406
+ finalUrl: String((page as { url?: unknown }).url ?? ''),
407
+ title: String((page as { title?: unknown }).title ?? ''),
408
+ text: String((page as { text?: unknown }).text ?? ''),
409
+ };
410
+ } catch (error) {
411
+ results[index] = {
412
+ url,
413
+ error: error instanceof Error ? error.message : String(error),
414
+ };
415
+ } finally {
416
+ if (targetId)
417
+ await cdp
418
+ .send(
419
+ 'Target.closeTarget',
420
+ { targetId },
421
+ undefined,
422
+ timeoutMs,
423
+ signal,
424
+ )
425
+ .catch(() => undefined);
426
+ }
427
+ }
428
+ };
429
+ await Promise.all(
430
+ Array.from(
431
+ { length: Math.max(1, Math.min(concurrency, urls.length)) },
432
+ () => worker(),
433
+ ),
434
+ );
435
+ return results.filter((result): result is NonNullable<typeof result> =>
436
+ Boolean(result),
437
+ );
438
+ }
@@ -10,22 +10,32 @@ const CAPTCHA_PATTERNS = [
10
10
  /press\s+&?\s*hold|click\s+to\s+verify|verifying\s+you/i,
11
11
  /点击验证|按住滑块|图片验证|行为验证/i,
12
12
  ];
13
- const OTP_PATTERNS = [/一次性密码|短信验证码|邮件验证码|动态口令|\botp\b|one[- ]time (?:password|code)/i];
13
+ const OTP_PATTERNS = [
14
+ /一次性密码|短信验证码|邮件验证码|动态口令|\botp\b|one[- ]time (?:password|code)/i,
15
+ ];
14
16
  const PASSWORD_PATTERNS = [
15
17
  /请输入.{0,12}(?:密码|口令)|(?:密码|口令).{0,12}(?:必填|不能为空)/i,
16
18
  /(?:enter|type).{0,20}(?:password|passcode)|(?:password|passcode).{0,20}(?:required|cannot be empty)/i,
17
19
  ];
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
+ const QR_LOGIN_PATTERNS = [
21
+ /扫码登录|二维码登录|scan (?:the )?qr|qr code.{0,20}(?:login|sign in)/i,
22
+ ];
23
+ const LOGIN_PATTERNS = [
24
+ /请先登录|登录后继续|扫码登录|sign in to continue|log in to continue/i,
20
25
  /please\s+(?:log|sign)\s+in/i,
21
26
  ];
22
- const BLOCK_PATTERNS = [/操作存在异常|访问请求.{0,8}(暂停|拦截)|请求过于频繁|访问受限|access denied|unusual traffic|temporarily blocked/i,
27
+ const BLOCK_PATTERNS = [
28
+ /操作存在异常|访问请求.{0,8}(暂停|拦截)|请求过于频繁|访问受限|access denied|unusual traffic|temporarily blocked/i,
23
29
  // 403/429 必须落在 HTTP/错误语境里再判风控,避免把「售价 $429」「forbidden fruit」这类正常正文误判成拦截。
24
30
  /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
31
  /被封禁|风控拦截|系统异常/i,
26
32
  ];
27
- const PERMISSION_PATTERNS = [/需要授权|请求权限|permission required|allow access/i];
28
- const SECURITY_WARNING_PATTERNS = [/安全警告|风险提示|security warning|untrusted (?:application|site|publisher)/i];
33
+ const PERMISSION_PATTERNS = [
34
+ /需要授权|请求权限|permission required|allow access/i,
35
+ ];
36
+ const SECURITY_WARNING_PATTERNS = [
37
+ /安全警告|风险提示|security warning|untrusted (?:application|site|publisher)/i,
38
+ ];
29
39
  /** Classifies only explicit page signals. It never attempts to solve or bypass a challenge. */
30
40
  export function detectBrowserChallenge(text) {
31
41
  const normalized = text.replace(/\s+/g, ' ').slice(0, 200_000);
@@ -47,6 +57,18 @@ export function detectBrowserChallenge(text) {
47
57
  return { reason: 'permission', summary: '页面需要人工确认权限' };
48
58
  return null;
49
59
  }
60
+ /**
61
+ * 判定「持有者已经死了」的阈值。
62
+ *
63
+ * 取值有依据,不是拍脑袋:单次动作的 `timeout` 在 zod schema 里是 `.max(300_000)`,
64
+ * 语义是**整个序列**(前置探测 + 动作 + 收尾重探)的总时长。所以任何合法的 running
65
+ * 都有 300s 的硬上界,取 2× 作阈值 —— 合法长动作绝不会被误接管,而泄漏的锁在一个
66
+ * 动作周期后自动可用,不再是「本进程剩余生命周期内浏览器永久不可用」。
67
+ *
68
+ * ⚠️ 改小之前先确认 webbrowser.ts 里 preTimer 与 runSequence 各自的计时器在最长路径上
69
+ * 不会叠加;`browserControl.test.ts` 用假时钟锁住了这个不变量的行为面。
70
+ */
71
+ export const DEFAULT_STALE_AFTER_MS = 600_000;
50
72
  export class BrowserControlCoordinator {
51
73
  state = { owner: 'none', status: 'idle' };
52
74
  listeners = new Set();
@@ -55,11 +77,29 @@ export class BrowserControlCoordinator {
55
77
  interventions = new HumanInterventionCoordinator();
56
78
  defaultOwnerId;
57
79
  defaultConversationKey;
80
+ staleAfterMs;
81
+ now;
82
+ onStaleTakeover;
83
+ /** 进入 running 的时刻;由 setState 统一维护,所以 resume 之后也会重新计时。 */
84
+ runningSince = 0;
58
85
  constructor(input = {}) {
59
86
  this.defaultOwnerId = input.ownerId ?? 'local-owner';
60
87
  this.defaultConversationKey = input.conversationKey ?? 'browser:default';
88
+ this.now = input.now ?? (() => Date.now());
89
+ this.staleAfterMs = input.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
90
+ // 接管**绝不能静默**:不留痕的话,「这把锁曾经泄漏过」就再也查不到了,
91
+ // 而下一次排查会从一个已经自愈的现场开始 —— 那比报错更难查。
92
+ this.onStaleTakeover =
93
+ input.onStaleTakeover ??
94
+ ((info) => {
95
+ process.stderr.write(`[browser] 上一个持有者(runId=${info.previousRunId ?? '?'}` +
96
+ `${info.previousAction ? `, action=${info.previousAction}` : ''})` +
97
+ `已持锁 ${Math.round(info.heldMs / 1000)}s,超过 ${Math.round(this.staleAfterMs / 1000)}s 判为泄漏,已接管。\n`);
98
+ });
99
+ }
100
+ getState() {
101
+ return this.state;
61
102
  }
62
- getState() { return this.state; }
63
103
  subscribe(listener) {
64
104
  this.listeners.add(listener);
65
105
  listener(this.state);
@@ -75,7 +115,24 @@ export class BrowserControlCoordinator {
75
115
  throw new Error('浏览器正在等待另一个任务的人工处理');
76
116
  }
77
117
  if (this.state.status === 'running' && this.state.runId !== input.runId) {
78
- throw new Error('浏览器正在执行另一个会话的原子操作');
118
+ const heldMs = this.now() - this.runningSince;
119
+ if (heldMs < this.staleAfterMs) {
120
+ // 带上持锁者身份与已持有时长:这把锁是进程级单例,一旦泄漏,后续每次调用都撞
121
+ // 同一句话,而"另一个会话"是谁、卡在哪个动作上、卡了多久,现场早就没了。
122
+ // 时长尤其关键 —— 它把「真并发」和「已泄漏」在报错里一眼分开。
123
+ throw new Error(`浏览器正在执行另一个会话的原子操作(持有者 runId=${this.state.runId ?? '?'}` +
124
+ `${this.state.action ? `, action=${this.state.action}` : ''}` +
125
+ `${this.activeIdentity?.conversationKey ? `, conversation=${this.activeIdentity.conversationKey}` : ''}` +
126
+ `, 已持有 ${Math.round(heldMs / 1000)}s)`);
127
+ }
128
+ // 超过任何合法动作的上界 —— 持有者的 finally 没能跑(进程被外部掐断、
129
+ // finally 自身抛错、或将来某条还没被想到的路径)。不接管的话,这个进程里的
130
+ // 浏览器就永久不可用了。接管并留痕,让「失败一次」不再等于「彻底报废」。
131
+ this.onStaleTakeover({
132
+ previousRunId: this.state.runId,
133
+ previousAction: this.state.action,
134
+ heldMs,
135
+ });
79
136
  }
80
137
  this.activeIdentity = {
81
138
  ownerId: this.defaultOwnerId,
@@ -99,11 +156,14 @@ export class BrowserControlCoordinator {
99
156
  const running = this.state.status === 'running' ? this.state : null;
100
157
  const identity = {
101
158
  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(),
159
+ conversationKey: input?.conversationKey ??
160
+ this.activeIdentity?.conversationKey ??
161
+ running?.conversationKey ??
162
+ this.defaultConversationKey,
163
+ runId: input?.runId ??
164
+ this.activeIdentity?.runId ??
165
+ running?.runId ??
166
+ randomUUID(),
107
167
  };
108
168
  const intervention = this.interventions.request({
109
169
  ...identity,
@@ -131,21 +191,37 @@ export class BrowserControlCoordinator {
131
191
  }
132
192
  this.activeIntervention = null;
133
193
  this.activeIdentity = null;
134
- this.setState({ owner: 'user', status: 'interactive', runId: input?.runId, conversationKey: input?.conversationKey });
194
+ this.setState({
195
+ owner: 'user',
196
+ status: 'interactive',
197
+ runId: input?.runId,
198
+ conversationKey: input?.conversationKey,
199
+ });
135
200
  }
136
201
  resume(token) {
137
- if (this.state.status !== 'waiting_human' || !token || !this.activeIntervention || !this.activeIdentity)
202
+ if (this.state.status !== 'waiting_human' ||
203
+ !token ||
204
+ !this.activeIntervention ||
205
+ !this.activeIdentity)
138
206
  return false;
139
207
  const previous = this.state;
140
208
  const ok = this.interventions.resume(this.credential(this.activeIntervention, this.activeIdentity, token));
141
209
  if (!ok)
142
210
  return false;
143
211
  this.activeIntervention = null;
144
- this.setState({ owner: 'agent', status: 'running', runId: previous.runId, conversationKey: previous.conversationKey, action: '正在检查人工操作结果' });
212
+ this.setState({
213
+ owner: 'agent',
214
+ status: 'running',
215
+ runId: previous.runId,
216
+ conversationKey: previous.conversationKey,
217
+ action: '正在检查人工操作结果',
218
+ });
145
219
  return true;
146
220
  }
147
221
  resumeIntervention(credential) {
148
- if (this.state.status !== 'waiting_human' || !this.activeIntervention || !this.activeIdentity)
222
+ if (this.state.status !== 'waiting_human' ||
223
+ !this.activeIntervention ||
224
+ !this.activeIdentity)
149
225
  return false;
150
226
  const previous = this.state;
151
227
  if (!this.interventions.resume(credential))
@@ -175,7 +251,9 @@ export class BrowserControlCoordinator {
175
251
  return true;
176
252
  }
177
253
  cancelIntervention(credential) {
178
- if (this.state.status !== 'waiting_human' || !this.activeIntervention || !this.activeIdentity)
254
+ if (this.state.status !== 'waiting_human' ||
255
+ !this.activeIntervention ||
256
+ !this.activeIdentity)
179
257
  return false;
180
258
  if (!this.interventions.cancel(credential))
181
259
  return false;
@@ -191,12 +269,26 @@ export class BrowserControlCoordinator {
191
269
  if (!intervention)
192
270
  return Promise.resolve('stopped');
193
271
  const waiter = { interventionId: intervention.id };
194
- return this.interventions.wait(waiter.interventionId, signal).then((resolution) => (resolution.status === 'resumed' ? 'resumed' : 'stopped'));
272
+ return this.interventions
273
+ .wait(waiter.interventionId, signal)
274
+ .then((resolution) => resolution.status === 'resumed' ? 'resumed' : 'stopped');
195
275
  }
196
276
  setState(state) {
277
+ // 所有进入 running 的路径都在这里重新计时(beginAgent 之外,还有 resume /
278
+ // resumeIntervention 从人工处理回到 agent 手里)。⚠️ 少了这一条,人工处理
279
+ // 花掉的时间会被算进持锁时长 —— 用户认真填了十分钟验证码,恢复的瞬间就被判成
280
+ // 「泄漏」让别人接管,正好惩罚了最该被保护的那条路径。
281
+ if (state.status === 'running')
282
+ this.runningSince = this.now();
197
283
  this.state = state;
198
- for (const listener of this.listeners)
199
- listener(state);
284
+ for (const listener of this.listeners) {
285
+ try {
286
+ listener(state);
287
+ }
288
+ catch {
289
+ // Observers are telemetry/UI extensions; control state must remain authoritative.
290
+ }
291
+ }
200
292
  }
201
293
  credential(intervention, identity, resumeToken = intervention.resumeToken) {
202
294
  return {
@@ -210,14 +302,23 @@ export class BrowserControlCoordinator {
210
302
  }
211
303
  function summaryForReason(reason) {
212
304
  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 '用户已接管浏览器';
305
+ case 'captcha':
306
+ return '页面需要人工完成安全验证';
307
+ case 'otp':
308
+ return '页面需要人工输入一次性验证码';
309
+ case 'password':
310
+ return '页面需要人工输入密码';
311
+ case 'qr_login':
312
+ return '页面需要人工扫码登录';
313
+ case 'login':
314
+ return '页面需要人工登录';
315
+ case 'permission':
316
+ return '页面需要人工确认权限';
317
+ case 'security_warning':
318
+ return '页面需要人工处理安全警告';
319
+ case 'blocked':
320
+ return '网站已暂停或拦截当前访问';
321
+ case 'manual':
322
+ return '用户已接管浏览器';
222
323
  }
223
324
  }