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,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)
@@ -1,12 +1,21 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
 
3
3
  export type BrowserActionGrant = 'observe' | 'interact' | 'upload' | 'submit';
4
+ export type TabRetention =
5
+ | 'ephemeral'
6
+ | 'conversation'
7
+ | 'handoff'
8
+ | 'deliverable';
4
9
 
5
- const NEGATED_ACTION_PREFIX = /(?:不要|不需(?:要)?|不能|不可|别|禁止|避免|无需|切勿)(?:再|自动|直接|帮我|替我|进行)?[\s\S]{0,8}$|(?:do\s+not|don't|never|must\s+not|should\s+not|without)(?:\s+\w+){0,4}\s*$/i;
10
+ const NEGATED_ACTION_PREFIX =
11
+ /(?:不要|不需(?:要)?|不能|不可|别|禁止|避免|无需|切勿)(?:再|自动|直接|帮我|替我|进行)?[\s\S]{0,8}$|(?:do\s+not|don't|never|must\s+not|should\s+not|without)(?:\s+\w+){0,4}\s*$/i;
6
12
 
7
13
  function hasUnnegatedAction(text: string, pattern: RegExp): boolean {
8
14
  for (const match of text.matchAll(pattern)) {
9
- const prefix = text.slice(Math.max(0, (match.index ?? 0) - 32), match.index);
15
+ const prefix = text.slice(
16
+ Math.max(0, (match.index ?? 0) - 32),
17
+ match.index,
18
+ );
10
19
  if (!NEGATED_ACTION_PREFIX.test(prefix)) return true;
11
20
  }
12
21
  return false;
@@ -19,11 +28,20 @@ function hasUnnegatedAction(text: string, pattern: RegExp): boolean {
19
28
  */
20
29
  export function inferBrowserTaskGrants(text: string): BrowserActionGrant[] {
21
30
  const grants: BrowserActionGrant[] = ['observe', 'interact'];
22
- if (hasUnnegatedAction(text, /\b(?:upload|attach)\b(?!\s+(?:page|button|history|settings))|上传(?!页|页面|按钮|记录|历史|设置)|选择文件|添加文件/giu)) grants.push('upload');
23
- if (hasUnnegatedAction(
24
- text,
25
- /\b(?:publish|submit|purchase|buy|delete|place\s+(?:an?\s+)?order|send)\b(?!\s+(?:page|button|history|record|settings))|发布(?!页|页面|按钮|记录|历史|设置)|发表(?!页|页面|按钮|记录|历史|设置)|提交(?!页|页面|按钮|记录|历史|设置)|购买|下单|删除|发送/giu,
26
- )) grants.push('submit');
31
+ if (
32
+ hasUnnegatedAction(
33
+ text,
34
+ /\b(?:upload|attach)\b(?!\s+(?:page|button|history|settings))|上传(?!页|页面|按钮|记录|历史|设置)|选择文件|添加文件/giu,
35
+ )
36
+ )
37
+ grants.push('upload');
38
+ if (
39
+ hasUnnegatedAction(
40
+ text,
41
+ /\b(?:publish|submit|purchase|buy|delete|place\s+(?:an?\s+)?order|send)\b(?!\s+(?:page|button|history|record|settings))|发布(?!页|页面|按钮|记录|历史|设置)|发表(?!页|页面|按钮|记录|历史|设置)|提交(?!页|页面|按钮|记录|历史|设置)|购买|下单|删除|发送/giu,
42
+ )
43
+ )
44
+ grants.push('submit');
27
45
  return grants;
28
46
  }
29
47
 
@@ -31,8 +49,14 @@ export function inferBrowserTaskGrants(text: string): BrowserActionGrant[] {
31
49
  export function isLikelyIrreversibleBrowserTarget(label: string): boolean {
32
50
  const normalized = label.replace(/\s+/g, ' ').trim();
33
51
  if (!normalized) return false;
34
- return /(?:^|[\s"'=:_-])(?:publish|submit|purchase|buy now|place order|delete|confirm delete|send)(?:$|[\s"'=:_-])/i.test(normalized)
35
- || /(?:发布|发表|提交|立即购买|确认购买|确认下单|删除|确认删除|发送)(?:$|[\s::))】\]])/.test(normalized);
52
+ return (
53
+ /(?:^|[\s"'=:_-])(?:publish|submit|purchase|buy now|place order|delete|confirm delete|send)(?:$|[\s"'=:_-])/i.test(
54
+ normalized,
55
+ ) ||
56
+ /(?:发布|发表|提交|立即购买|确认购买|确认下单|删除|确认删除|发送)(?:$|[\s::))】\]])/.test(
57
+ normalized,
58
+ )
59
+ );
36
60
  }
37
61
 
38
62
  export interface BrowserRunContext {
@@ -47,6 +71,13 @@ export interface BrowserRunContext {
47
71
  allowedUploadRoots: string[];
48
72
  }
49
73
 
74
+ export interface BrowserTabMetadata {
75
+ tabId: string;
76
+ retention: TabRetention;
77
+ title?: string;
78
+ updatedAt: number;
79
+ }
80
+
50
81
  type BrowserRunInput = {
51
82
  conversationKey: string;
52
83
  runId?: string;
@@ -67,14 +98,17 @@ export class BrowserRunRegistry {
67
98
  private readonly runs = new Map<string, BrowserRunContext>();
68
99
  private readonly conversationTabs = new Map<string, Set<string>>();
69
100
  private readonly tabOwners = new Map<string, string>();
101
+ private readonly tabMetadata = new Map<string, BrowserTabMetadata>();
70
102
 
71
103
  ensure(input: BrowserRunInput): BrowserRunContext {
72
104
  const key = this.key(input.conversationKey, input.runId ?? 'workbench');
73
105
  const existing = this.runs.get(key);
74
106
  if (existing) {
75
107
  if (input.grants) existing.grants = [...new Set(input.grants)];
76
- if (input.taskGrantId !== undefined) existing.taskGrantId = input.taskGrantId;
77
- if (input.allowedUploadRoots) existing.allowedUploadRoots = [...new Set(input.allowedUploadRoots)];
108
+ if (input.taskGrantId !== undefined)
109
+ existing.taskGrantId = input.taskGrantId;
110
+ if (input.allowedUploadRoots)
111
+ existing.allowedUploadRoots = [...new Set(input.allowedUploadRoots)];
78
112
  return existing;
79
113
  }
80
114
  const context: BrowserRunContext = {
@@ -82,7 +116,9 @@ export class BrowserRunRegistry {
82
116
  runId: input.runId ?? randomUUID(),
83
117
  profileId: input.profileId ?? 'default',
84
118
  claimedTabIds: [],
85
- grants: [...new Set<BrowserActionGrant>(input.grants ?? ['observe', 'interact'])],
119
+ grants: [
120
+ ...new Set<BrowserActionGrant>(input.grants ?? ['observe', 'interact']),
121
+ ],
86
122
  allowedUploadRoots: [...new Set(input.allowedUploadRoots ?? [])],
87
123
  ...(input.taskGrantId ? { taskGrantId: input.taskGrantId } : {}),
88
124
  };
@@ -92,7 +128,9 @@ export class BrowserRunRegistry {
92
128
 
93
129
  find(conversationKey: string, runId?: string): BrowserRunContext | undefined {
94
130
  if (runId) return this.runs.get(this.key(conversationKey, runId));
95
- return [...this.runs.values()].find((item) => item.conversationKey === conversationKey);
131
+ return [...this.runs.values()].find(
132
+ (item) => item.conversationKey === conversationKey,
133
+ );
96
134
  }
97
135
 
98
136
  claimedTabs(conversationKey: string): string[] {
@@ -103,13 +141,33 @@ export class BrowserRunRegistry {
103
141
  return this.tabOwners.get(tabId);
104
142
  }
105
143
 
144
+ markTab(
145
+ context: BrowserRunContext,
146
+ tabId: string,
147
+ metadata: Pick<BrowserTabMetadata, 'retention' | 'title'>,
148
+ ): BrowserTabMetadata {
149
+ this.assertTab(context, tabId);
150
+ const next: BrowserTabMetadata = {
151
+ tabId,
152
+ ...metadata,
153
+ updatedAt: Date.now(),
154
+ };
155
+ this.tabMetadata.set(tabId, next);
156
+ return next;
157
+ }
158
+
159
+ tabInfo(tabId: string): BrowserTabMetadata | undefined {
160
+ return this.tabMetadata.get(tabId);
161
+ }
162
+
106
163
  claimTab(context: BrowserRunContext, tabId: string): void {
107
164
  const owner = this.tabOwners.get(tabId);
108
165
  if (owner && owner !== context.conversationKey) {
109
166
  throw new Error('该浏览器标签属于另一个会话,已阻止跨会话操作');
110
167
  }
111
168
  this.tabOwners.set(tabId, context.conversationKey);
112
- const claimed = this.conversationTabs.get(context.conversationKey) ?? new Set<string>();
169
+ const claimed =
170
+ this.conversationTabs.get(context.conversationKey) ?? new Set<string>();
113
171
  claimed.add(tabId);
114
172
  this.conversationTabs.set(context.conversationKey, claimed);
115
173
  context.claimedTabIds = [...claimed];
@@ -124,16 +182,34 @@ export class BrowserRunRegistry {
124
182
  * 测试里先撞上(基准集跑完不清理,把 browserTabs 的用例挤挂了),但这是生产同款问题。
125
183
  */
126
184
  forgetConversation(conversationKey: string): void {
127
- for (const tabId of this.claimedTabs(conversationKey)) this.releaseTab(conversationKey, tabId);
185
+ for (const tabId of this.claimedTabs(conversationKey))
186
+ this.releaseTab(conversationKey, tabId);
128
187
  this.conversationTabs.delete(conversationKey);
129
188
  for (const [key, run] of [...this.runs.entries()]) {
130
189
  if (run.conversationKey === conversationKey) this.runs.delete(key);
131
190
  }
132
191
  }
133
192
 
193
+ /**
194
+ * Invalidate every tab lease after the browser process is rebuilt. Tab ids
195
+ * are process-local and can be reused by the replacement daemon; retaining
196
+ * old owners would either block the new owner or route it across sessions.
197
+ * Run contexts remain alive so the active task can claim a replacement tab.
198
+ */
199
+ invalidateAllTabs(): void {
200
+ this.tabOwners.clear();
201
+ this.conversationTabs.clear();
202
+ this.tabMetadata.clear();
203
+ for (const context of this.runs.values()) {
204
+ context.claimedTabIds = [];
205
+ context.activeTabId = undefined;
206
+ }
207
+ }
208
+
134
209
  releaseTab(conversationKey: string, tabId: string): void {
135
210
  if (this.tabOwners.get(tabId) !== conversationKey) return;
136
211
  this.tabOwners.delete(tabId);
212
+ this.tabMetadata.delete(tabId);
137
213
  const claimed = this.conversationTabs.get(conversationKey);
138
214
  claimed?.delete(tabId);
139
215
  if (claimed?.size === 0) this.conversationTabs.delete(conversationKey);
@@ -5,10 +5,36 @@
5
5
  */
6
6
  export class BrowserService {
7
7
  tails = new Map();
8
+ async waitForPrevious(previous, signal) {
9
+ if (!signal)
10
+ return previous.catch(() => undefined);
11
+ if (signal.aborted)
12
+ throw signal.reason instanceof Error
13
+ ? signal.reason
14
+ : new Error('Browser action cancelled');
15
+ await new Promise((resolve, reject) => {
16
+ let settled = false;
17
+ const cleanup = () => signal.removeEventListener('abort', onAbort);
18
+ const finish = (fn) => {
19
+ if (settled)
20
+ return;
21
+ settled = true;
22
+ cleanup();
23
+ fn();
24
+ };
25
+ const onAbort = () => finish(() => reject(signal.reason instanceof Error
26
+ ? signal.reason
27
+ : new Error('Browser action cancelled')));
28
+ signal.addEventListener('abort', onAbort, { once: true });
29
+ void previous.then(() => finish(resolve), () => finish(resolve));
30
+ });
31
+ }
8
32
  async exclusive(input, task) {
9
33
  const previous = this.tails.get(input.sessionName) ?? Promise.resolve();
10
34
  let release;
11
- const current = new Promise((resolve) => { release = resolve; });
35
+ const current = new Promise((resolve) => {
36
+ release = resolve;
37
+ });
12
38
  // 链上永远接一个已消化的 previous:任何一环 reject 都不能顺着队列传下去,
13
39
  // 否则后来者在 `await previous` 处抛出。
14
40
  const tail = previous.catch(() => undefined).then(() => current);
@@ -19,7 +45,7 @@ export class BrowserService {
19
45
  // 当前 current 只 resolve 不 reject,所以实际安全 —— 但那是一条没写下来的不变量,
20
46
  // 谁往链上加一个 .then 都可能打破它。把 await 挪进来,安全性就不再依赖它。
21
47
  try {
22
- await previous.catch(() => undefined);
48
+ await this.waitForPrevious(previous, input.signal);
23
49
  const run = async (args) => {
24
50
  if (input.signal?.aborted) {
25
51
  return { code: -1, stdout: '', stderr: 'Browser action cancelled' };
@@ -8,15 +8,54 @@ import type { AgentBrowserRun, AgentBrowserRunner } from './browser.js';
8
8
  export class BrowserService {
9
9
  private tails = new Map<string, Promise<void>>();
10
10
 
11
- async exclusive<T>(input: {
12
- sessionName: string;
13
- bin: string;
14
- runner: AgentBrowserRunner;
15
- signal?: AbortSignal;
16
- }, task: (run: (args: string[]) => Promise<AgentBrowserRun>) => Promise<T>): Promise<T> {
11
+ private async waitForPrevious(
12
+ previous: Promise<void>,
13
+ signal?: AbortSignal,
14
+ ): Promise<void> {
15
+ if (!signal) return previous.catch(() => undefined);
16
+ if (signal.aborted)
17
+ throw signal.reason instanceof Error
18
+ ? signal.reason
19
+ : new Error('Browser action cancelled');
20
+ await new Promise<void>((resolve, reject) => {
21
+ let settled = false;
22
+ const cleanup = () => signal.removeEventListener('abort', onAbort);
23
+ const finish = (fn: () => void) => {
24
+ if (settled) return;
25
+ settled = true;
26
+ cleanup();
27
+ fn();
28
+ };
29
+ const onAbort = () =>
30
+ finish(() =>
31
+ reject(
32
+ signal.reason instanceof Error
33
+ ? signal.reason
34
+ : new Error('Browser action cancelled'),
35
+ ),
36
+ );
37
+ signal.addEventListener('abort', onAbort, { once: true });
38
+ void previous.then(
39
+ () => finish(resolve),
40
+ () => finish(resolve),
41
+ );
42
+ });
43
+ }
44
+
45
+ async exclusive<T>(
46
+ input: {
47
+ sessionName: string;
48
+ bin: string;
49
+ runner: AgentBrowserRunner;
50
+ signal?: AbortSignal;
51
+ },
52
+ task: (run: (args: string[]) => Promise<AgentBrowserRun>) => Promise<T>,
53
+ ): Promise<T> {
17
54
  const previous = this.tails.get(input.sessionName) ?? Promise.resolve();
18
55
  let release!: () => void;
19
- const current = new Promise<void>((resolve) => { release = resolve; });
56
+ const current = new Promise<void>((resolve) => {
57
+ release = resolve;
58
+ });
20
59
  // 链上永远接一个已消化的 previous:任何一环 reject 都不能顺着队列传下去,
21
60
  // 否则后来者在 `await previous` 处抛出。
22
61
  const tail = previous.catch(() => undefined).then(() => current);
@@ -28,7 +67,7 @@ export class BrowserService {
28
67
  // 当前 current 只 resolve 不 reject,所以实际安全 —— 但那是一条没写下来的不变量,
29
68
  // 谁往链上加一个 .then 都可能打破它。把 await 挪进来,安全性就不再依赖它。
30
69
  try {
31
- await previous.catch(() => undefined);
70
+ await this.waitForPrevious(previous, input.signal);
32
71
  const run = async (args: string[]): Promise<AgentBrowserRun> => {
33
72
  if (input.signal?.aborted) {
34
73
  return { code: -1, stdout: '', stderr: 'Browser action cancelled' };
@@ -38,7 +77,8 @@ export class BrowserService {
38
77
  return await task(run);
39
78
  } finally {
40
79
  release();
41
- if (this.tails.get(input.sessionName) === tail) this.tails.delete(input.sessionName);
80
+ if (this.tails.get(input.sessionName) === tail)
81
+ this.tails.delete(input.sessionName);
42
82
  }
43
83
  }
44
84