minimal-agent 0.13.0 → 0.15.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 (474) hide show
  1. package/README.md +6 -5
  2. package/package.json +60 -4
  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 +197 -206
  16. package/plugins/workflow-runner/src/index.ts +248 -249
  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 +400 -489
  28. package/plugins/workflow-runner/src/runner.ts +488 -560
  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 +151 -307
  54. package/plugins/workflow-runner/src/taskIdentity.ts +207 -355
  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 -211
  58. package/plugins/workflow-runner/src/types.ts +154 -623
  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 +474 -802
  68. package/plugins/workflow-runner/src/workflowState.ts +575 -970
  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/ui-design/SKILL.md +5 -2
  92. package/skills/ui-design/references/layout-composition.md +1 -1
  93. package/skills/workflow-creator/SKILL.md +344 -0
  94. package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
  95. package/skills/workflow-creator/references/api.md +216 -0
  96. package/skills/workflow-creator/references/migration.md +103 -0
  97. package/skills/workflow-creator/references/patterns.md +779 -0
  98. package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
  99. package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
  100. package/skills/workflow-creator/scripts/validate.mjs +275 -0
  101. package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
  102. package/src/advisor/advisorCoordinator.js +536 -0
  103. package/src/advisor/advisorCoordinator.ts +693 -0
  104. package/src/cli/configMenu.tsx +0 -1
  105. package/src/cli/print.js +27 -6
  106. package/src/cli/print.ts +48 -4
  107. package/src/cli/quickModelSetup.js +12 -1
  108. package/src/cli/quickModelSetup.tsx +15 -3
  109. package/src/cli/streamJson.js +5 -6
  110. package/src/cli/streamJson.ts +8 -4
  111. package/src/cli/wizards/advisorWizard.js +4 -2
  112. package/src/cli/wizards/advisorWizard.tsx +8 -2
  113. package/src/cli/wizards/contextAdvisorWizard.js +5 -4
  114. package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
  115. package/src/cli/wizards/providerPresets.js +18 -8
  116. package/src/cli/wizards/providerPresets.ts +18 -8
  117. package/src/collaboration/taskFlow.js +115 -0
  118. package/src/collaboration/taskFlow.ts +169 -0
  119. package/src/config/configDefaults.js +41 -1
  120. package/src/config/configDefaults.ts +43 -1
  121. package/src/config/configDiagnostics.ts +2 -2
  122. package/src/config/configFile.js +40 -9
  123. package/src/config/configFile.ts +60 -11
  124. package/src/config/configService.js +419 -74
  125. package/src/config/configService.ts +703 -145
  126. package/src/config/numericLimits.js +22 -0
  127. package/src/config/numericLimits.ts +24 -0
  128. package/src/config.js +1 -0
  129. package/src/config.ts +3 -2
  130. package/src/context/budgetPlan.js +1 -1
  131. package/src/context/budgetPlan.ts +1 -1
  132. package/src/context/compact.js +7 -2
  133. package/src/context/compact.ts +9 -2
  134. package/src/context/microCompactLite.js +212 -13
  135. package/src/context/microCompactLite.ts +261 -12
  136. package/src/context/sessionRegistry.js +8 -1
  137. package/src/context/sessionRegistry.ts +18 -5
  138. package/src/context/toolResultArchive.js +72 -0
  139. package/src/context/toolResultArchive.ts +95 -0
  140. package/src/goal/errors.js +8 -0
  141. package/src/goal/errors.ts +18 -0
  142. package/src/goal/goalRuntime.js +344 -0
  143. package/src/goal/goalRuntime.ts +466 -0
  144. package/src/goal/goalStore.js +200 -0
  145. package/src/goal/goalStore.ts +219 -0
  146. package/src/goal/index.js +4 -0
  147. package/src/goal/index.ts +4 -0
  148. package/src/goal/statusMapping.js +71 -0
  149. package/src/goal/statusMapping.ts +95 -0
  150. package/src/goal/types.js +7 -0
  151. package/src/goal/types.ts +179 -0
  152. package/src/goal/workflowController.js +306 -0
  153. package/src/goal/workflowController.ts +360 -0
  154. package/src/harness/evidenceLedger.js +2 -0
  155. package/src/harness/evidenceLedger.ts +1 -0
  156. package/src/harness/stallGuard.js +9 -3
  157. package/src/harness/stallGuard.ts +7 -3
  158. package/src/images/imageResolver.js +161 -0
  159. package/src/images/imageResolver.ts +189 -0
  160. package/src/infra/verification.js +34 -5
  161. package/src/infra/verification.ts +39 -4
  162. package/src/llm/client.js +79 -21
  163. package/src/llm/client.ts +182 -35
  164. package/src/llm/gatewayClient.js +201 -0
  165. package/src/llm/gatewayClient.ts +299 -0
  166. package/src/llm/messageProvenance.js +46 -0
  167. package/src/llm/messageProvenance.ts +58 -0
  168. package/src/llm/modelProvider.js +1 -0
  169. package/src/llm/modelProvider.ts +22 -0
  170. package/src/llm/providers/anthropicFormat.js +108 -1
  171. package/src/llm/providers/anthropicFormat.ts +115 -4
  172. package/src/llm/providers/openaiFormat.js +9 -0
  173. package/src/llm/providers/openaiFormat.ts +12 -3
  174. package/src/loop.js +328 -55
  175. package/src/loop.ts +382 -58
  176. package/src/main.js +4 -1
  177. package/src/main.tsx +4 -1
  178. package/src/managedExecution/index.js +2 -0
  179. package/src/managedExecution/index.ts +2 -0
  180. package/src/managedExecution/registry.js +15 -0
  181. package/src/managedExecution/registry.ts +20 -0
  182. package/src/managedExecution/types.js +1 -0
  183. package/src/managedExecution/types.ts +86 -0
  184. package/src/modelRef.js +9 -0
  185. package/src/modelRef.ts +31 -0
  186. package/src/multimodal.js +14 -28
  187. package/src/multimodal.ts +17 -33
  188. package/src/net/lanDiscovery.js +65 -0
  189. package/src/net/lanDiscovery.ts +82 -0
  190. package/src/plugin-sdk.js +28 -3
  191. package/src/plugin-sdk.ts +39 -6
  192. package/src/plugins/pluginApi.ts +16 -1
  193. package/src/plugins/pluginLoader.js +4 -0
  194. package/src/plugins/pluginLoader.ts +6 -0
  195. package/src/plugins/pluginRunner.js +139 -7
  196. package/src/plugins/pluginRunner.ts +173 -9
  197. package/src/plugins/slashFallback.js +113 -0
  198. package/src/plugins/slashFallback.ts +137 -0
  199. package/src/prompts/policyRegistry.js +12 -32
  200. package/src/prompts/policyRegistry.ts +15 -32
  201. package/src/prompts/promptAssembly.js +103 -12
  202. package/src/prompts/promptAssembly.ts +114 -12
  203. package/src/prompts/skillList.js +21 -3
  204. package/src/prompts/skillList.ts +27 -2
  205. package/src/prompts/stableSystemPrompt.js +18 -0
  206. package/src/prompts/stableSystemPrompt.ts +33 -0
  207. package/src/runtime/agentRuntimeContext.js +8 -2
  208. package/src/runtime/agentRuntimeContext.ts +17 -4
  209. package/src/session/runAgentTurn.js +78 -12
  210. package/src/session/runAgentTurn.ts +105 -23
  211. package/src/session/runTurn.js +10 -9
  212. package/src/session/runTurn.ts +17 -8
  213. package/src/session/sessionState.ts +2 -0
  214. package/src/team/auth.js +186 -0
  215. package/src/team/auth.ts +298 -0
  216. package/src/team/cli.js +105 -0
  217. package/src/team/cli.ts +122 -0
  218. package/src/team/onlineToolContract.js +64 -0
  219. package/src/team/onlineToolContract.ts +109 -0
  220. package/src/team/toolPolicy.js +36 -0
  221. package/src/team/toolPolicy.ts +75 -0
  222. package/src/tools/advisor/advisor.js +115 -50
  223. package/src/tools/advisor/advisor.ts +124 -51
  224. package/src/tools/advisor/advisorDecision.js +71 -0
  225. package/src/tools/advisor/advisorDecision.ts +85 -0
  226. package/src/tools/agent/agent.js +18 -5
  227. package/src/tools/agent/agent.ts +21 -7
  228. package/src/tools/agent/agentControl.js +246 -0
  229. package/src/tools/agent/agentControl.ts +284 -0
  230. package/src/tools/agent/agentTaskService.js +40 -1
  231. package/src/tools/agent/agentTaskService.ts +44 -1
  232. package/src/tools/agent/agentTypes.js +14 -2
  233. package/src/tools/agent/agentTypes.ts +15 -2
  234. package/src/tools/applypatch/applyPatch.js +362 -0
  235. package/src/tools/applypatch/applyPatch.ts +428 -0
  236. package/src/tools/bash/bash.js +22 -15
  237. package/src/tools/bash/bash.ts +23 -17
  238. package/src/tools/capabilities.js +10 -5
  239. package/src/tools/capabilities.ts +10 -5
  240. package/src/tools/computeruse/computerIntent.js +32 -0
  241. package/src/tools/computeruse/computerIntent.ts +46 -0
  242. package/src/tools/computeruse/telemetryLog.js +12 -0
  243. package/src/tools/computeruse/telemetryLog.ts +16 -0
  244. package/src/tools/computeruse/vision/telemetry.js +1 -0
  245. package/src/tools/computeruse/vision/telemetry.ts +1 -0
  246. package/src/tools/effectiveToolSet.js +4 -0
  247. package/src/tools/effectiveToolSet.ts +4 -0
  248. package/src/tools/index.js +21 -2
  249. package/src/tools/index.ts +33 -2
  250. package/src/tools/presentresult/presentresult.js +6 -15
  251. package/src/tools/presentresult/presentresult.ts +6 -15
  252. package/src/tools/read/read.js +62 -9
  253. package/src/tools/read/read.ts +63 -9
  254. package/src/tools/shared/fileUtils.js +1 -2
  255. package/src/tools/shared/fileUtils.ts +1 -2
  256. package/src/tools/shared/toolOutputStore.js +23 -1
  257. package/src/tools/shared/toolOutputStore.ts +22 -1
  258. package/src/tools/skill/skill.js +9 -3
  259. package/src/tools/skill/skill.ts +8 -4
  260. package/src/tools/toolPromptGuides.js +125 -0
  261. package/src/tools/toolPromptGuides.ts +146 -0
  262. package/src/tools/webbrowser/agentBrowserPath.js +19 -9
  263. package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
  264. package/src/tools/webbrowser/browser.js +27 -11
  265. package/src/tools/webbrowser/browser.ts +68 -24
  266. package/src/tools/webbrowser/browserAutomation.js +329 -0
  267. package/src/tools/webbrowser/browserAutomation.ts +467 -0
  268. package/src/tools/webbrowser/browserAxState.js +97 -0
  269. package/src/tools/webbrowser/browserAxState.ts +142 -0
  270. package/src/tools/webbrowser/browserBatch.js +1578 -0
  271. package/src/tools/webbrowser/browserBatch.ts +2382 -0
  272. package/src/tools/webbrowser/browserCdp.js +208 -0
  273. package/src/tools/webbrowser/browserCdp.ts +255 -0
  274. package/src/tools/webbrowser/browserConditions.js +24 -0
  275. package/src/tools/webbrowser/browserConditions.ts +45 -0
  276. package/src/tools/webbrowser/browserContent.js +281 -0
  277. package/src/tools/webbrowser/browserContent.ts +438 -0
  278. package/src/tools/webbrowser/browserControl.js +131 -34
  279. package/src/tools/webbrowser/browserControl.ts +210 -56
  280. package/src/tools/webbrowser/browserDaemon.js +22 -14
  281. package/src/tools/webbrowser/browserDaemon.ts +54 -21
  282. package/src/tools/webbrowser/browserFrames.js +47 -0
  283. package/src/tools/webbrowser/browserFrames.ts +54 -0
  284. package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
  285. package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
  286. package/src/tools/webbrowser/browserLocator.js +140 -0
  287. package/src/tools/webbrowser/browserLocator.ts +194 -0
  288. package/src/tools/webbrowser/browserOutcome.js +10 -17
  289. package/src/tools/webbrowser/browserOutcome.ts +48 -31
  290. package/src/tools/webbrowser/browserRecovery.js +58 -0
  291. package/src/tools/webbrowser/browserRecovery.ts +83 -0
  292. package/src/tools/webbrowser/browserRunRegistry.js +35 -3
  293. package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
  294. package/src/tools/webbrowser/browserService.js +28 -2
  295. package/src/tools/webbrowser/browserService.ts +49 -9
  296. package/src/tools/webbrowser/browserStream.js +132 -33
  297. package/src/tools/webbrowser/browserStream.ts +224 -50
  298. package/src/tools/webbrowser/browserTabs.js +48 -15
  299. package/src/tools/webbrowser/browserTabs.ts +178 -96
  300. package/src/tools/webbrowser/webbrowser.js +679 -144
  301. package/src/tools/webbrowser/webbrowser.ts +975 -236
  302. package/src/tools/webfetch/preapproved.js +0 -2
  303. package/src/tools/webfetch/preapproved.ts +0 -2
  304. package/src/tools/websearch/websearch.js +129 -8
  305. package/src/tools/websearch/websearch.ts +138 -6
  306. package/src/types.ts +459 -40
  307. package/src/ui/App.js +377 -58
  308. package/src/ui/App.tsx +576 -111
  309. package/src/ui/LiveArea.js +14 -8
  310. package/src/ui/LiveArea.tsx +14 -10
  311. package/src/ui/MessageList.js +63 -20
  312. package/src/ui/MessageList.tsx +101 -16
  313. package/src/ui/ModelPicker.js +7 -4
  314. package/src/ui/ModelPicker.tsx +55 -8
  315. package/src/ui/hooks/useChat.js +152 -163
  316. package/src/ui/hooks/useChat.ts +396 -334
  317. package/src/ui/loginCommand.js +56 -0
  318. package/src/ui/loginCommand.ts +63 -0
  319. package/src/ui/toolProgress.js +41 -0
  320. package/src/ui/toolProgress.ts +44 -0
  321. package/src/web/browserBridge.js +20 -2
  322. package/src/web/browserBridge.ts +20 -2
  323. package/src/web/protocol.ts +1897 -194
  324. package/workflows/adaptive-research/workflow.mjs +86 -0
  325. package/workflows/e2e-capabilities/workflow.mjs +102 -0
  326. package/workflows/e2e-llm/workflow.mjs +52 -0
  327. package/workflows/e2e-smoke/workflow.mjs +59 -0
  328. package/workflows/e2e-vision-schema/README.md +48 -0
  329. package/workflows/e2e-vision-schema/workflow.mjs +149 -0
  330. package/workflows/e2e-write-greet/workflow.mjs +37 -0
  331. package/workflows/file-census/README.md +14 -0
  332. package/workflows/file-census/scripts/.gitkeep +2 -0
  333. package/workflows/file-census/workflow.mjs +110 -0
  334. package/workflows/goal/README.md +166 -0
  335. package/workflows/goal/prompts/audit.md +57 -0
  336. package/workflows/goal/prompts/contract.md +56 -0
  337. package/workflows/goal/prompts/iterate.md +37 -0
  338. package/workflows/goal/workflow.mjs +501 -0
  339. package/workflows/illustrate/README.md +90 -0
  340. package/workflows/illustrate/assets/engines.json +20 -0
  341. package/workflows/illustrate/assets/extract-baidu.js +21 -0
  342. package/workflows/illustrate/assets/extract-bing.js +33 -0
  343. package/workflows/illustrate/assets/extract-google.js +31 -0
  344. package/workflows/illustrate/workflow.mjs +301 -0
  345. package/workflows/plan/workflow.mjs +389 -0
  346. package/workflows/question-crops/README.md +35 -0
  347. package/workflows/question-crops/prompts/inventory.md +23 -0
  348. package/workflows/question-crops/scripts/image-ops.mjs +305 -0
  349. package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
  350. package/workflows/question-crops/workflow.mjs +172 -0
  351. package/workflows/ralph-loop/workflow.mjs +162 -0
  352. package/workflows/research/README.md +74 -0
  353. package/workflows/research/prompts/s0-goal.md +43 -0
  354. package/workflows/research/prompts/s1-early-exit.md +22 -0
  355. package/workflows/research/prompts/s1-map.md +58 -0
  356. package/workflows/research/prompts/s2-options.md +53 -0
  357. package/workflows/research/prompts/s3-deep-dive.md +56 -0
  358. package/workflows/research/prompts/s4-verify.md +74 -0
  359. package/workflows/research/prompts/s5-synthesis.md +81 -0
  360. package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
  361. package/workflows/research/prompts/s6-package.md +32 -0
  362. package/workflows/research/scripts/build-report.mjs +98 -0
  363. package/workflows/research/workflow.mjs +288 -0
  364. package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
  365. package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
  366. package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
  367. package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
  368. package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
  369. package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
  370. package/workflows/wechat-reading-shots/workflow.mjs +266 -0
  371. package/workflows/wrong-question-docx/README.md +55 -0
  372. package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
  373. package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
  374. package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
  375. package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
  376. package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
  377. package/workflows/wrong-question-docx/workflow.mjs +485 -0
  378. package/plugins/workflow-runner/src/agentCommon.js +0 -354
  379. package/plugins/workflow-runner/src/agentCommon.ts +0 -435
  380. package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
  381. package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
  382. package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
  383. package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
  384. package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
  385. package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
  386. package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
  387. package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
  388. package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
  389. package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
  390. package/plugins/workflow-runner/src/dslManifest.js +0 -72
  391. package/plugins/workflow-runner/src/dslManifest.ts +0 -127
  392. package/plugins/workflow-runner/src/expressions.js +0 -639
  393. package/plugins/workflow-runner/src/expressions.ts +0 -640
  394. package/plugins/workflow-runner/src/loader.js +0 -573
  395. package/plugins/workflow-runner/src/loader.ts +0 -721
  396. package/plugins/workflow-runner/src/multimodal.js +0 -38
  397. package/plugins/workflow-runner/src/multimodal.ts +0 -64
  398. package/plugins/workflow-runner/src/recovery.js +0 -126
  399. package/plugins/workflow-runner/src/recovery.ts +0 -156
  400. package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
  401. package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
  402. package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -42
  403. package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -52
  404. package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
  405. package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
  406. package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
  407. package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
  408. package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
  409. package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
  410. package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
  411. package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
  412. package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -227
  413. package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -274
  414. package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -61
  415. package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -72
  416. package/plugins/workflow-runner/src/stepPipeline.js +0 -277
  417. package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
  418. package/plugins/workflow-runner/test/agent.test.ts +0 -587
  419. package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
  420. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
  421. package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
  422. package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -167
  423. package/plugins/workflow-runner/test/context-files.test.ts +0 -320
  424. package/plugins/workflow-runner/test/e2e.test.ts +0 -370
  425. package/plugins/workflow-runner/test/e2eNewLayout.test.ts +0 -314
  426. package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -234
  427. package/plugins/workflow-runner/test/expressions.test.ts +0 -439
  428. package/plugins/workflow-runner/test/foundation.test.ts +0 -126
  429. package/plugins/workflow-runner/test/graceful.test.ts +0 -139
  430. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
  431. package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
  432. package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
  433. package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
  434. package/plugins/workflow-runner/test/loader.test.ts +0 -617
  435. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
  436. package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
  437. package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
  438. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -106
  439. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
  440. package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
  441. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -221
  442. package/plugins/workflow-runner/test/recovery.test.ts +0 -265
  443. package/plugins/workflow-runner/test/research.load.test.ts +0 -328
  444. package/plugins/workflow-runner/test/runner.test.ts +0 -911
  445. package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -436
  446. package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
  447. package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -198
  448. package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
  449. package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -354
  450. package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -324
  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 -201
  468. package/workflows/research.yaml +0 -873
  469. package/workflows/schema.json +0 -176
  470. package/workflows/webppt.yaml +0 -496
  471. package/workflows/wechat-reading-shots.yaml +0 -315
  472. package/workflows/word.yaml +0 -720
  473. package/workflows/youtube-shorts.yaml +0 -202
  474. /package/workflows/{research.md → research/DESIGN.md} +0 -0
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Browser locators shared by BrowserBatch and future visible-DOM adapters.
3
+ *
4
+ * This deliberately emits a small, self-contained browser expression instead
5
+ * of retaining DOM node handles. A locator is resolved again for every step,
6
+ * so a navigation or React re-render cannot silently reuse a stale element.
7
+ */
8
+
9
+ export type BrowserLocator = {
10
+ by:
11
+ | 'css'
12
+ | 'ax'
13
+ | 'role'
14
+ | 'text'
15
+ | 'label'
16
+ | 'placeholder'
17
+ | 'alt'
18
+ | 'title'
19
+ | 'testid';
20
+ value: string;
21
+ /** AX node id (or numeric index printed by the latest snapshot). */
22
+ generation?: number;
23
+ name?: string;
24
+ exact?: boolean;
25
+ strict?: boolean;
26
+ nth?: number;
27
+ within?: BrowserLocator;
28
+ has_text?: string;
29
+ frame?: BrowserLocator[];
30
+ framePath?: BrowserLocator[];
31
+ frame_path?: BrowserLocator[];
32
+ /** CDP frame id returned by frameInfo; enables cross-origin frame context. */
33
+ frameId?: string;
34
+ frame_id?: string;
35
+ };
36
+
37
+ const quote = (value: string) => JSON.stringify(value);
38
+
39
+ /** Build an expression returning `Element[]` in the selected document. */
40
+ export function buildLocatorExpression(
41
+ locator: BrowserLocator | undefined,
42
+ selector: string | undefined,
43
+ rootExpression = 'document',
44
+ ): string {
45
+ if (selector) {
46
+ return `Array.from(${rootExpression}.querySelectorAll(${quote(selector)}))`;
47
+ }
48
+ if (!locator) throw new Error('step requires selector or locator');
49
+
50
+ if (locator.by === 'ax') {
51
+ throw new Error(
52
+ 'AX locator must be resolved against the live accessibility tree first',
53
+ );
54
+ }
55
+
56
+ const root = locator.within
57
+ ? `${buildLocatorExpression(locator.within, undefined, rootExpression)}.flatMap((scope) => Array.from(scope.querySelectorAll('*')))`
58
+ : `Array.from(${rootExpression}.querySelectorAll('*'))`;
59
+ const value = quote(locator.value);
60
+ const exact = locator.exact === true;
61
+ const compare = (expression: string, expected = value) =>
62
+ exact
63
+ ? `${expression}.trim()===${expected}`
64
+ : `${expression}.includes(${expected})`;
65
+
66
+ let predicate: string;
67
+ switch (locator.by) {
68
+ case 'css':
69
+ return applyFilters(
70
+ `Array.from(${rootExpression}.querySelectorAll(${value}))`,
71
+ locator,
72
+ );
73
+ case 'role': {
74
+ const name = locator.name ? quote(locator.name) : value;
75
+ const nameCompare = locator.name
76
+ ? `(function(name){return ${exact ? `name.trim()===${name}` : `name.includes(${name})`};})(__name)`
77
+ : 'true';
78
+ predicate = `(__role===${value}&&${nameCompare})`;
79
+ break;
80
+ }
81
+ case 'label':
82
+ predicate = `Array.from(__el.labels||[]).some((label)=>${compare("(label.textContent||'')")})`;
83
+ break;
84
+ case 'placeholder':
85
+ predicate = compare(`__el.getAttribute('placeholder')||''`);
86
+ break;
87
+ case 'alt':
88
+ predicate = compare(`__el.getAttribute('alt')||''`);
89
+ break;
90
+ case 'title':
91
+ predicate = compare(`__el.getAttribute('title')||''`);
92
+ break;
93
+ case 'testid':
94
+ predicate = compare(`__el.getAttribute('data-testid')||''`);
95
+ break;
96
+ case 'text':
97
+ predicate = `(!__el.children.length&&${compare("(__el.textContent||'')")})`;
98
+ break;
99
+ }
100
+
101
+ const expression = `(() => {
102
+ const __accessibleName = (el) => {
103
+ const labelled = el.getAttribute('aria-label');
104
+ if (labelled) return labelled;
105
+ const ids = (el.getAttribute('aria-labelledby')||'').split(/\\s+/).filter(Boolean);
106
+ if (ids.length) return ids.map((id)=>${rootExpression}.getElementById(id)?.textContent||'').join(' ').trim();
107
+ if (el.labels?.length) return Array.from(el.labels).map((label)=>label.textContent||'').join(' ').trim();
108
+ return (el.innerText||el.textContent||'').trim();
109
+ };
110
+ const __implicitRole = (el) => {
111
+ const explicit = el.getAttribute('role');
112
+ if (explicit) return explicit;
113
+ const tag = el.tagName.toLowerCase();
114
+ if (tag === 'button') return 'button';
115
+ if (tag === 'a' && el.hasAttribute('href')) return 'link';
116
+ if (tag === 'textarea') return 'textbox';
117
+ if (tag === 'select') return 'combobox';
118
+ if (tag === 'input') {
119
+ const type = (el.getAttribute('type')||'text').toLowerCase();
120
+ if (type === 'checkbox') return 'checkbox';
121
+ if (type === 'radio') return 'radio';
122
+ if (type === 'button' || type === 'submit' || type === 'reset') return 'button';
123
+ return 'textbox';
124
+ }
125
+ return '';
126
+ };
127
+ return ${root}.filter((__el) => {
128
+ const __role = __implicitRole(__el);
129
+ const __name = __accessibleName(__el);
130
+ return ${predicate};
131
+ });
132
+ })()`;
133
+
134
+ const filtered = applyFilters(expression, locator);
135
+ const framePath = locator.frame ?? locator.framePath ?? locator.frame_path;
136
+ if (!framePath?.length) return filtered;
137
+
138
+ // Same-origin frame traversal is explicit. Cross-origin frames cannot be
139
+ // inspected through Runtime.evaluate and must be handled by a backend that
140
+ // can attach to the child target; never pretend the parent matched instead.
141
+ let frameRoot = rootExpression;
142
+ for (const frame of framePath) {
143
+ const frameSelector =
144
+ frame.by === 'css'
145
+ ? quote(frame.value)
146
+ : quote(`iframe[title*="${frame.value.replaceAll('"', '\\"')}"]`);
147
+ frameRoot = `(() => { const frame = ${frameRoot}.querySelector(${frameSelector}); if (!frame || !frame.contentDocument) throw new Error('frameLocator could not resolve a same-origin iframe'); return frame.contentDocument; })()`;
148
+ }
149
+ return buildLocatorExpression(
150
+ {
151
+ ...locator,
152
+ frame: undefined,
153
+ framePath: undefined,
154
+ frame_path: undefined,
155
+ frameId: undefined,
156
+ frame_id: undefined,
157
+ },
158
+ selector,
159
+ frameRoot,
160
+ );
161
+ }
162
+
163
+ function applyFilters(expression: string, locator: BrowserLocator): string {
164
+ const filters: string[] = [];
165
+ if (locator.has_text)
166
+ filters.push(`(__el.textContent||'').includes(${quote(locator.has_text)})`);
167
+ return filters.length
168
+ ? `Array.from(${expression}).filter((__el)=>${filters.join('&&')})`
169
+ : expression;
170
+ }
171
+
172
+ export function buildTargetExpression(
173
+ locator: BrowserLocator | undefined,
174
+ selector: string | undefined,
175
+ ): string {
176
+ const elements = buildLocatorExpression(locator, selector);
177
+ const strict = locator?.strict !== false;
178
+ const nth = locator?.nth ?? 0;
179
+ return `(() => {
180
+ const __matches = ${elements}.filter((__candidate) => {
181
+ const __style = getComputedStyle(__candidate);
182
+ const __rect = __candidate.getBoundingClientRect();
183
+ return __style.display!=='none' && __style.visibility!=='hidden' && __style.pointerEvents!=='none' && (!!__rect.width || !!__rect.height);
184
+ });
185
+ if (!__matches.length) throw new Error('Locator matched no elements');
186
+ if (__matches.length > 1 && ${strict}) throw new Error('Locator matched '+__matches.length+' elements; use nth or strict:false');
187
+ const __el = __matches[${nth}];
188
+ if (!__el) throw new Error('Locator nth is outside the matched elements');
189
+ const __style = getComputedStyle(__el);
190
+ const __rect = __el.getBoundingClientRect();
191
+ if (__style.display==='none'||__style.visibility==='hidden'||__style.pointerEvents==='none'||(!__rect.width&&!__rect.height)) throw new Error('Locator matched a hidden element');
192
+ return __el;
193
+ })()`;
194
+ }
@@ -85,16 +85,9 @@ export function describeEffect(verdict, action) {
85
85
  case 'changed':
86
86
  return null; // 正常路径不加噪声
87
87
  case 'unchanged':
88
- return (`⚠️ 动作已发出,但页面结构在动作前后**完全没有变化**。\n`
89
- + `这通常意味着三种情况之一:${action} 命中的不是可交互元素、点击被遮挡层拦住了、`
90
- + `或者这个操作本来就不改变页面(比如点了一个纯展示文本)。\n`
91
- + `请对照下面的最新快照确认目标是否正确,不要用同一个选择器直接重试。`
92
- // 豁免了行才提。这不是免责声明,是让模型知道这个结论有多硬:
93
- // 被豁免的位置上,「自己在跳」和「被我改了又在跳」不可区分。
94
- + (verdict.maskedLines > 0
95
- ? `\n(说明:页面上有 ${verdict.maskedLines} 行在持续自行变化(时钟/轮询一类),`
96
- + `已从判定中排除。若你这次操作本该影响的正是这些位置,这个结论可能不准。)`
97
- : ''));
88
+ return `⚠️ 动作已发出,但页面结构在动作前后**完全没有变化**。\n这通常意味着三种情况之一:${action} 命中的不是可交互元素、点击被遮挡层拦住了、或者这个操作本来就不改变页面(比如点了一个纯展示文本)。\n请对照下面的最新快照确认目标是否正确,不要用同一个选择器直接重试。${verdict.maskedLines > 0
89
+ ? `\n(说明:页面上有 ${verdict.maskedLines} 行在持续自行变化(时钟/轮询一类),已从判定中排除。若你这次操作本该影响的正是这些位置,这个结论可能不准。)`
90
+ : ''}`;
98
91
  case 'unknown':
99
92
  return '(未能取得动作前后的页面快照,无法判断这次操作是否生效——请自行核对。)';
100
93
  }
@@ -179,7 +172,7 @@ export function suggestSelectorCandidates(snapshot, selector, limit = 5) {
179
172
  export class BrowserAttemptLedger {
180
173
  counts = new Map();
181
174
  static key(conversationKey, action, selector) {
182
- return `${conversationKey}${action}${selector ?? ''}`;
175
+ return `${conversationKey}\u0000${action}\u0000${selector ?? ''}`;
183
176
  }
184
177
  recordFailure(conversationKey, action, selector) {
185
178
  const key = BrowserAttemptLedger.key(conversationKey, action, selector);
@@ -191,12 +184,12 @@ export class BrowserAttemptLedger {
191
184
  this.counts.delete(BrowserAttemptLedger.key(conversationKey, action, selector));
192
185
  }
193
186
  failures(conversationKey, action, selector) {
194
- return this.counts.get(BrowserAttemptLedger.key(conversationKey, action, selector)) ?? 0;
187
+ return (this.counts.get(BrowserAttemptLedger.key(conversationKey, action, selector)) ?? 0);
195
188
  }
196
189
  /** 会话结束时清账,避免长期驻留。 */
197
190
  forget(conversationKey) {
198
191
  for (const key of [...this.counts.keys()]) {
199
- if (key.startsWith(`${conversationKey}`))
192
+ if (key.startsWith(`${conversationKey}\u0000`))
200
193
  this.counts.delete(key);
201
194
  }
202
195
  }
@@ -228,7 +221,9 @@ export function budgetSnapshot(snapshot, opts = {}) {
228
221
  if (lines.length <= maxLines && snapshot.length <= maxChars)
229
222
  return snapshot;
230
223
  const hasRef = (line) => /@e\d+|\bref=e\d+/.test(line);
231
- const clip = (line) => line.length <= MAX_SNAPSHOT_LINE_CHARS ? line : `${line.slice(0, MAX_SNAPSHOT_LINE_CHARS)}…`;
224
+ const clip = (line) => line.length <= MAX_SNAPSHOT_LINE_CHARS
225
+ ? line
226
+ : `${line.slice(0, MAX_SNAPSHOT_LINE_CHARS)}…`;
232
227
  // 两轮**只挑不排**:先在原序上标记要留的可操作行,再用余量补纯文本行。
233
228
  const keep = new Array(lines.length).fill(false);
234
229
  let kept = 0;
@@ -267,7 +262,5 @@ export function budgetSnapshot(snapshot, opts = {}) {
267
262
  }
268
263
  flushGap();
269
264
  const dropped = lines.length - kept;
270
- return (`${out.join('\n')}\n`
271
- + `… 为控制上下文,共省略 ${dropped} 行(按原顺序保留,优先保住可引用的元素)。`
272
- + '需要完整页面请单独调用 snapshot,或用 getContent 读取正文。');
265
+ return `${out.join('\n')}\n… 为控制上下文,共省略 ${dropped} 行(按原顺序保留,优先保住可引用的元素)。需要完整页面请单独调用 snapshot,或用 getContent 读取正文。`;
273
266
  }
@@ -82,36 +82,35 @@ export function classifyPageEffect(
82
82
  after: string | undefined,
83
83
  noiseAfter?: string,
84
84
  ): BrowserEffectVerdict {
85
- if (before === undefined || after === undefined) return { effect: 'unknown', maskedLines: 0 };
85
+ if (before === undefined || after === undefined)
86
+ return { effect: 'unknown', maskedLines: 0 };
86
87
  const actual = differingLineIndexes(before, after);
87
88
  if (actual === null) return { effect: 'changed', maskedLines: 0 }; // 行数变了 = 结构变了
88
89
  if (actual.size === 0) return { effect: 'unchanged', maskedLines: 0 };
89
- const noisy = noiseAfter !== undefined ? differingLineIndexes(after, noiseAfter) : null;
90
+ const noisy =
91
+ noiseAfter !== undefined ? differingLineIndexes(after, noiseAfter) : null;
90
92
  // 没有可用的噪声基线时不做扣除:宁可把「生效了」判宽,也不能把一次真实变化
91
93
  // 误报成没变——那会让模型放弃一条本来走通了的路。
92
94
  if (noisy === null) return { effect: 'changed', maskedLines: 0 };
93
- for (const index of actual) if (!noisy.has(index)) return { effect: 'changed', maskedLines: 0 };
95
+ for (const index of actual)
96
+ if (!noisy.has(index)) return { effect: 'changed', maskedLines: 0 };
94
97
  return { effect: 'unchanged', maskedLines: actual.size };
95
98
  }
96
99
 
97
100
  /** 给模型的效果说明。措辞要让它知道**下一步该干什么**,不只是知道结果。 */
98
- export function describeEffect(verdict: BrowserEffectVerdict, action: string): string | null {
101
+ export function describeEffect(
102
+ verdict: BrowserEffectVerdict,
103
+ action: string,
104
+ ): string | null {
99
105
  switch (verdict.effect) {
100
106
  case 'changed':
101
107
  return null; // 正常路径不加噪声
102
108
  case 'unchanged':
103
- return (
104
- `⚠️ 动作已发出,但页面结构在动作前后**完全没有变化**。\n`
105
- + `这通常意味着三种情况之一:${action} 命中的不是可交互元素、点击被遮挡层拦住了、`
106
- + `或者这个操作本来就不改变页面(比如点了一个纯展示文本)。\n`
107
- + `请对照下面的最新快照确认目标是否正确,不要用同一个选择器直接重试。`
108
- // 豁免了行才提。这不是免责声明,是让模型知道这个结论有多硬:
109
- // 被豁免的位置上,「自己在跳」和「被我改了又在跳」不可区分。
110
- + (verdict.maskedLines > 0
111
- ? `\n(说明:页面上有 ${verdict.maskedLines} 行在持续自行变化(时钟/轮询一类),`
112
- + `已从判定中排除。若你这次操作本该影响的正是这些位置,这个结论可能不准。)`
113
- : '')
114
- );
109
+ return `⚠️ 动作已发出,但页面结构在动作前后**完全没有变化**。\n这通常意味着三种情况之一:${action} 命中的不是可交互元素、点击被遮挡层拦住了、或者这个操作本来就不改变页面(比如点了一个纯展示文本)。\n请对照下面的最新快照确认目标是否正确,不要用同一个选择器直接重试。${
110
+ verdict.maskedLines > 0
111
+ ? `\n(说明:页面上有 ${verdict.maskedLines} 行在持续自行变化(时钟/轮询一类),已从判定中排除。若你这次操作本该影响的正是这些位置,这个结论可能不准。)`
112
+ : ''
113
+ }`;
115
114
  case 'unknown':
116
115
  return '(未能取得动作前后的页面快照,无法判断这次操作是否生效——请自行核对。)';
117
116
  }
@@ -206,29 +205,47 @@ export function suggestSelectorCandidates(
206
205
  export class BrowserAttemptLedger {
207
206
  private counts = new Map<string, number>();
208
207
 
209
- private static key(conversationKey: string, action: string, selector?: string): string {
210
- return `${conversationKey}${action}${selector ?? ''}`;
208
+ private static key(
209
+ conversationKey: string,
210
+ action: string,
211
+ selector?: string,
212
+ ): string {
213
+ return `${conversationKey}\u0000${action}\u0000${selector ?? ''}`;
211
214
  }
212
215
 
213
- recordFailure(conversationKey: string, action: string, selector?: string): number {
216
+ recordFailure(
217
+ conversationKey: string,
218
+ action: string,
219
+ selector?: string,
220
+ ): number {
214
221
  const key = BrowserAttemptLedger.key(conversationKey, action, selector);
215
222
  const next = (this.counts.get(key) ?? 0) + 1;
216
223
  this.counts.set(key, next);
217
224
  return next;
218
225
  }
219
226
 
220
- recordSuccess(conversationKey: string, action: string, selector?: string): void {
221
- this.counts.delete(BrowserAttemptLedger.key(conversationKey, action, selector));
227
+ recordSuccess(
228
+ conversationKey: string,
229
+ action: string,
230
+ selector?: string,
231
+ ): void {
232
+ this.counts.delete(
233
+ BrowserAttemptLedger.key(conversationKey, action, selector),
234
+ );
222
235
  }
223
236
 
224
237
  failures(conversationKey: string, action: string, selector?: string): number {
225
- return this.counts.get(BrowserAttemptLedger.key(conversationKey, action, selector)) ?? 0;
238
+ return (
239
+ this.counts.get(
240
+ BrowserAttemptLedger.key(conversationKey, action, selector),
241
+ ) ?? 0
242
+ );
226
243
  }
227
244
 
228
245
  /** 会话结束时清账,避免长期驻留。 */
229
246
  forget(conversationKey: string): void {
230
247
  for (const key of [...this.counts.keys()]) {
231
- if (key.startsWith(`${conversationKey}`)) this.counts.delete(key);
248
+ if (key.startsWith(`${conversationKey}\u0000`)) this.counts.delete(key);
232
249
  }
233
250
  }
234
251
  }
@@ -267,7 +284,9 @@ export function budgetSnapshot(
267
284
 
268
285
  const hasRef = (line: string): boolean => /@e\d+|\bref=e\d+/.test(line);
269
286
  const clip = (line: string): string =>
270
- line.length <= MAX_SNAPSHOT_LINE_CHARS ? line : `${line.slice(0, MAX_SNAPSHOT_LINE_CHARS)}…`;
287
+ line.length <= MAX_SNAPSHOT_LINE_CHARS
288
+ ? line
289
+ : `${line.slice(0, MAX_SNAPSHOT_LINE_CHARS)}…`;
271
290
 
272
291
  // 两轮**只挑不排**:先在原序上标记要留的可操作行,再用余量补纯文本行。
273
292
  const keep = new Array<boolean>(lines.length).fill(false);
@@ -292,15 +311,13 @@ export function budgetSnapshot(
292
311
  gap = 0;
293
312
  };
294
313
  for (let i = 0; i < lines.length; i += 1) {
295
- if (keep[i]) { flushGap(); out.push(clip(lines[i])); }
296
- else gap += 1;
314
+ if (keep[i]) {
315
+ flushGap();
316
+ out.push(clip(lines[i]));
317
+ } else gap += 1;
297
318
  }
298
319
  flushGap();
299
320
 
300
321
  const dropped = lines.length - kept;
301
- return (
302
- `${out.join('\n')}\n`
303
- + `… 为控制上下文,共省略 ${dropped} 行(按原顺序保留,优先保住可引用的元素)。`
304
- + '需要完整页面请单独调用 snapshot,或用 getContent 读取正文。'
305
- );
322
+ return `${out.join('\n')}\n… 为控制上下文,共省略 ${dropped} 行(按原顺序保留,优先保住可引用的元素)。需要完整页面请单独调用 snapshot,或用 getContent 读取正文。`;
306
323
  }
@@ -0,0 +1,58 @@
1
+ export function classifyBrowserFailure(error) {
2
+ const message = error instanceof Error ? error.message : String(error);
3
+ // Do not treat a generic "element not found" / selector miss as a stale
4
+ // browser target. Those are normal locator failures and must return the
5
+ // page evidence to the model instead of replaying the same action.
6
+ if (/\b(?:tab|target|frame)\b.*(?:closed|crashed|detached|destroyed|not found|invalid)/i.test(message) ||
7
+ /execution context.*(?:destroyed|not found|closed)/i.test(message) ||
8
+ /(?:page|window).*closed/i.test(message))
9
+ return 'stale-tab';
10
+ if (/cdp|socket|websocket|daemon|connection|disconnected|closed/i.test(message))
11
+ return 'browser-disconnected';
12
+ return 'unknown';
13
+ }
14
+ /**
15
+ * agent-browser reports many daemon/target failures as an exit code instead
16
+ * of rejecting the runner promise. Convert only those known recoverable
17
+ * failures into exceptions so the runtime recovery wrapper can see them;
18
+ * ordinary selector/business errors must remain normal tool results.
19
+ */
20
+ export function recoverableAgentBrowserRunFailure(result) {
21
+ if (result.code === 0)
22
+ return undefined;
23
+ const message = result.stderr?.trim() || result.stdout?.trim() || `exit ${result.code}`;
24
+ const error = new Error(message);
25
+ return classifyBrowserFailure(error) === 'unknown' ? undefined : error;
26
+ }
27
+ /** Retry only recoverable browser failures, rebuilding the connection/tab before retrying. */
28
+ export async function withBrowserRecovery(operation, hooks = {}) {
29
+ const maxAttempts = Math.max(1, Math.min(hooks.maxAttempts ?? 3, 4));
30
+ let attempt = 0;
31
+ let recoveredConnection = false;
32
+ let reboundTab = false;
33
+ while (true) {
34
+ if (hooks.signal?.aborted)
35
+ throw hooks.signal.reason ?? new Error('浏览器任务已中断');
36
+ try {
37
+ return await operation();
38
+ }
39
+ catch (error) {
40
+ attempt += 1;
41
+ const kind = classifyBrowserFailure(error);
42
+ if (kind === 'unknown' || attempt >= maxAttempts)
43
+ throw error;
44
+ if (kind === 'browser-disconnected' && !recoveredConnection) {
45
+ await hooks.reconnect?.();
46
+ recoveredConnection = true;
47
+ reboundTab = false;
48
+ continue;
49
+ }
50
+ if (kind === 'stale-tab' && !reboundTab) {
51
+ await hooks.rebindTab?.();
52
+ reboundTab = true;
53
+ continue;
54
+ }
55
+ throw error;
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,83 @@
1
+ export type BrowserRecoveryKind =
2
+ | 'stale-tab'
3
+ | 'browser-disconnected'
4
+ | 'unknown';
5
+
6
+ export function classifyBrowserFailure(error: unknown): BrowserRecoveryKind {
7
+ const message = error instanceof Error ? error.message : String(error);
8
+ // Do not treat a generic "element not found" / selector miss as a stale
9
+ // browser target. Those are normal locator failures and must return the
10
+ // page evidence to the model instead of replaying the same action.
11
+ if (
12
+ /\b(?:tab|target|frame)\b.*(?:closed|crashed|detached|destroyed|not found|invalid)/i.test(
13
+ message,
14
+ ) ||
15
+ /execution context.*(?:destroyed|not found|closed)/i.test(message) ||
16
+ /(?:page|window).*closed/i.test(message)
17
+ )
18
+ return 'stale-tab';
19
+ if (
20
+ /cdp|socket|websocket|daemon|connection|disconnected|closed/i.test(message)
21
+ )
22
+ return 'browser-disconnected';
23
+ return 'unknown';
24
+ }
25
+
26
+ /**
27
+ * agent-browser reports many daemon/target failures as an exit code instead
28
+ * of rejecting the runner promise. Convert only those known recoverable
29
+ * failures into exceptions so the runtime recovery wrapper can see them;
30
+ * ordinary selector/business errors must remain normal tool results.
31
+ */
32
+ export function recoverableAgentBrowserRunFailure(result: {
33
+ code: number;
34
+ stdout?: string;
35
+ stderr?: string;
36
+ }): Error | undefined {
37
+ if (result.code === 0) return undefined;
38
+ const message =
39
+ result.stderr?.trim() || result.stdout?.trim() || `exit ${result.code}`;
40
+ const error = new Error(message);
41
+ return classifyBrowserFailure(error) === 'unknown' ? undefined : error;
42
+ }
43
+
44
+ export type BrowserRecoveryHooks<T> = {
45
+ reconnect?: () => Promise<void>;
46
+ rebindTab?: () => Promise<void>;
47
+ maxAttempts?: number;
48
+ signal?: AbortSignal;
49
+ };
50
+
51
+ /** Retry only recoverable browser failures, rebuilding the connection/tab before retrying. */
52
+ export async function withBrowserRecovery<T>(
53
+ operation: () => Promise<T>,
54
+ hooks: BrowserRecoveryHooks<T> = {},
55
+ ): Promise<T> {
56
+ const maxAttempts = Math.max(1, Math.min(hooks.maxAttempts ?? 3, 4));
57
+ let attempt = 0;
58
+ let recoveredConnection = false;
59
+ let reboundTab = false;
60
+ while (true) {
61
+ if (hooks.signal?.aborted)
62
+ throw hooks.signal.reason ?? new Error('浏览器任务已中断');
63
+ try {
64
+ return await operation();
65
+ } catch (error) {
66
+ attempt += 1;
67
+ const kind = classifyBrowserFailure(error);
68
+ if (kind === 'unknown' || attempt >= maxAttempts) throw error;
69
+ if (kind === 'browser-disconnected' && !recoveredConnection) {
70
+ await hooks.reconnect?.();
71
+ recoveredConnection = true;
72
+ reboundTab = false;
73
+ continue;
74
+ }
75
+ if (kind === 'stale-tab' && !reboundTab) {
76
+ await hooks.rebindTab?.();
77
+ reboundTab = true;
78
+ continue;
79
+ }
80
+ throw error;
81
+ }
82
+ }
83
+ }
@@ -26,8 +26,8 @@ export function isLikelyIrreversibleBrowserTarget(label) {
26
26
  const normalized = label.replace(/\s+/g, ' ').trim();
27
27
  if (!normalized)
28
28
  return false;
29
- return /(?:^|[\s"'=:_-])(?:publish|submit|purchase|buy now|place order|delete|confirm delete|send)(?:$|[\s"'=:_-])/i.test(normalized)
30
- || /(?:发布|发表|提交|立即购买|确认购买|确认下单|删除|确认删除|发送)(?:$|[\s::))】\]])/.test(normalized);
29
+ return (/(?:^|[\s"'=:_-])(?:publish|submit|purchase|buy now|place order|delete|confirm delete|send)(?:$|[\s"'=:_-])/i.test(normalized) ||
30
+ /(?:发布|发表|提交|立即购买|确认购买|确认下单|删除|确认删除|发送)(?:$|[\s::))】\]])/.test(normalized));
31
31
  }
32
32
  /**
33
33
  * Process-wide ownership registry for the shared browser profile.
@@ -40,6 +40,7 @@ export class BrowserRunRegistry {
40
40
  runs = new Map();
41
41
  conversationTabs = new Map();
42
42
  tabOwners = new Map();
43
+ tabMetadata = new Map();
43
44
  ensure(input) {
44
45
  const key = this.key(input.conversationKey, input.runId ?? 'workbench');
45
46
  const existing = this.runs.get(key);
@@ -57,7 +58,9 @@ export class BrowserRunRegistry {
57
58
  runId: input.runId ?? randomUUID(),
58
59
  profileId: input.profileId ?? 'default',
59
60
  claimedTabIds: [],
60
- grants: [...new Set(input.grants ?? ['observe', 'interact'])],
61
+ grants: [
62
+ ...new Set(input.grants ?? ['observe', 'interact']),
63
+ ],
61
64
  allowedUploadRoots: [...new Set(input.allowedUploadRoots ?? [])],
62
65
  ...(input.taskGrantId ? { taskGrantId: input.taskGrantId } : {}),
63
66
  };
@@ -75,6 +78,19 @@ export class BrowserRunRegistry {
75
78
  ownerOf(tabId) {
76
79
  return this.tabOwners.get(tabId);
77
80
  }
81
+ markTab(context, tabId, metadata) {
82
+ this.assertTab(context, tabId);
83
+ const next = {
84
+ tabId,
85
+ ...metadata,
86
+ updatedAt: Date.now(),
87
+ };
88
+ this.tabMetadata.set(tabId, next);
89
+ return next;
90
+ }
91
+ tabInfo(tabId) {
92
+ return this.tabMetadata.get(tabId);
93
+ }
78
94
  claimTab(context, tabId) {
79
95
  const owner = this.tabOwners.get(tabId);
80
96
  if (owner && owner !== context.conversationKey) {
@@ -103,10 +119,26 @@ export class BrowserRunRegistry {
103
119
  this.runs.delete(key);
104
120
  }
105
121
  }
122
+ /**
123
+ * Invalidate every tab lease after the browser process is rebuilt. Tab ids
124
+ * are process-local and can be reused by the replacement daemon; retaining
125
+ * old owners would either block the new owner or route it across sessions.
126
+ * Run contexts remain alive so the active task can claim a replacement tab.
127
+ */
128
+ invalidateAllTabs() {
129
+ this.tabOwners.clear();
130
+ this.conversationTabs.clear();
131
+ this.tabMetadata.clear();
132
+ for (const context of this.runs.values()) {
133
+ context.claimedTabIds = [];
134
+ context.activeTabId = undefined;
135
+ }
136
+ }
106
137
  releaseTab(conversationKey, tabId) {
107
138
  if (this.tabOwners.get(tabId) !== conversationKey)
108
139
  return;
109
140
  this.tabOwners.delete(tabId);
141
+ this.tabMetadata.delete(tabId);
110
142
  const claimed = this.conversationTabs.get(conversationKey);
111
143
  claimed?.delete(tabId);
112
144
  if (claimed?.size === 0)