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
@@ -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
 
@@ -1,5 +1,5 @@
1
+ import { BrowserControlCoordinator, } from './browserControl.js';
1
2
  import { getStreamPort, waitForStreamPort } from './browserDaemon.js';
2
- import { BrowserControlCoordinator } from './browserControl.js';
3
3
  class SessionBrowserStream {
4
4
  sessionName;
5
5
  socket = null;
@@ -8,26 +8,39 @@ class SessionBrowserStream {
8
8
  port = null;
9
9
  mode = 'browse';
10
10
  currentUrl = 'about:blank';
11
- status = { connected: false, screencasting: false };
11
+ status = {
12
+ connected: false,
13
+ screencasting: false,
14
+ };
15
+ reconnectTimer = null;
16
+ reconnectAttempt = 0;
17
+ disposed = false;
12
18
  picker;
13
19
  marked = [];
14
20
  control;
15
21
  constructor(sessionName) {
16
22
  this.sessionName = sessionName;
17
- this.control = new BrowserControlCoordinator({ conversationKey: `browser:${sessionName}` });
23
+ this.control = new BrowserControlCoordinator({
24
+ conversationKey: `browser:${sessionName}`,
25
+ });
18
26
  this.control.subscribe((state) => this.emit({ type: 'control_state', state }));
19
27
  this.control.subscribeInterventions((event) => this.emit({
20
- type: event.type === 'started' ? 'human_intervention_started' : 'human_intervention_resolved',
28
+ type: event.type === 'started'
29
+ ? 'human_intervention_started'
30
+ : 'human_intervention_resolved',
21
31
  event,
22
32
  }));
23
33
  }
24
34
  async connect() {
35
+ this.disposed = false;
25
36
  if (this.socket?.readyState === WebSocket.OPEN)
26
37
  return;
27
38
  if (this.connecting)
28
39
  return this.connecting;
29
40
  this.connecting = (async () => {
30
- this.port = (await getStreamPort(this.sessionName)) ?? (await waitForStreamPort(this.sessionName));
41
+ this.port =
42
+ (await getStreamPort(this.sessionName)) ??
43
+ (await waitForStreamPort(this.sessionName));
31
44
  await new Promise((resolve, reject) => {
32
45
  const socket = new WebSocket(`ws://127.0.0.1:${this.port}`);
33
46
  const timer = setTimeout(() => {
@@ -37,6 +50,7 @@ class SessionBrowserStream {
37
50
  socket.onopen = () => {
38
51
  clearTimeout(timer);
39
52
  this.socket = socket;
53
+ this.reconnectAttempt = 0;
40
54
  this.status = { ...this.status, connected: true };
41
55
  this.emit({ type: 'status', ...this.status });
42
56
  resolve();
@@ -48,16 +62,33 @@ class SessionBrowserStream {
48
62
  socket.onclose = () => {
49
63
  if (this.socket === socket)
50
64
  this.socket = null;
51
- this.status = { ...this.status, connected: false, screencasting: false };
65
+ this.status = {
66
+ ...this.status,
67
+ connected: false,
68
+ screencasting: false,
69
+ };
52
70
  this.emit({ type: 'status', ...this.status });
71
+ this.scheduleReconnect();
53
72
  };
54
73
  socket.onmessage = (event) => this.onMessage(event.data);
55
74
  });
56
- })().finally(() => { this.connecting = null; });
75
+ })().finally(() => {
76
+ this.connecting = null;
77
+ });
57
78
  return this.connecting;
58
79
  }
59
- disconnect() { this.socket?.close(); this.socket = null; }
60
- dispose() { this.disconnect(); this.listeners.clear(); }
80
+ disconnect() {
81
+ this.disposed = true;
82
+ if (this.reconnectTimer)
83
+ clearTimeout(this.reconnectTimer);
84
+ this.reconnectTimer = null;
85
+ this.socket?.close();
86
+ this.socket = null;
87
+ }
88
+ dispose() {
89
+ this.disconnect();
90
+ this.listeners.clear();
91
+ }
61
92
  send(frame) {
62
93
  if (this.socket?.readyState === WebSocket.OPEN)
63
94
  this.socket.send(JSON.stringify(frame));
@@ -66,22 +97,49 @@ class SessionBrowserStream {
66
97
  this.listeners.add(listener);
67
98
  return () => this.listeners.delete(listener);
68
99
  }
69
- getStatus() { return this.status; }
70
- getPort() { return this.port; }
71
- isConnected() { return this.socket?.readyState === WebSocket.OPEN; }
72
- setMode(mode) { this.mode = mode; this.emit({ type: 'mode', mode }); }
73
- getMode() { return this.mode; }
74
- getCurrentUrl() { return this.currentUrl; }
75
- setCurrentUrl(url) { this.currentUrl = url; this.emit({ type: 'url', url }); }
76
- setPicker(picker) { this.picker = picker; }
100
+ publish(event) {
101
+ this.emit(event);
102
+ }
103
+ getStatus() {
104
+ return this.status;
105
+ }
106
+ getPort() {
107
+ return this.port;
108
+ }
109
+ isConnected() {
110
+ return this.socket?.readyState === WebSocket.OPEN;
111
+ }
112
+ setMode(mode) {
113
+ this.mode = mode;
114
+ this.emit({ type: 'mode', mode });
115
+ }
116
+ getMode() {
117
+ return this.mode;
118
+ }
119
+ getCurrentUrl() {
120
+ return this.currentUrl;
121
+ }
122
+ setCurrentUrl(url) {
123
+ this.currentUrl = url;
124
+ this.emit({ type: 'url', url });
125
+ }
126
+ setPicker(picker) {
127
+ this.picker = picker;
128
+ }
77
129
  pickElement(x, y) {
78
130
  return this.picker ? this.picker(x, y) : Promise.resolve(null);
79
131
  }
80
132
  markElement(element) {
81
- this.marked = [...this.marked.filter((item) => item.ref !== element.ref), element];
133
+ this.marked = [
134
+ ...this.marked.filter((item) => item.ref !== element.ref),
135
+ element,
136
+ ];
82
137
  this.emit({ type: 'marked_elements', elements: this.marked });
83
138
  }
84
- clearMarkedElements() { this.marked = []; this.emit({ type: 'marked_elements', elements: [] }); }
139
+ clearMarkedElements() {
140
+ this.marked = [];
141
+ this.emit({ type: 'marked_elements', elements: [] });
142
+ }
85
143
  unmarkElement(ref) {
86
144
  const next = this.marked.filter((item) => item.ref !== ref);
87
145
  if (next.length !== this.marked.length) {
@@ -102,29 +160,67 @@ class SessionBrowserStream {
102
160
  // 只发语义终点(目标元素中心 + 动作类型);平滑移动交给面板 CSS 过渡,内核不做逐帧插值(避免 WS 洪泛 + 双层缓动打架)。
103
161
  this.emit({ type: 'agent_pointer', ...pointer });
104
162
  }
105
- getControlState() { return this.control.getState(); }
106
- subscribeControl(listener) { return this.control.subscribe(listener); }
107
- subscribeInterventions(listener) { return this.control.subscribeInterventions(listener); }
108
- beginAgentAction(input) { this.control.beginAgent(input); }
109
- finishAgentAction(runId) { this.control.finishAgent(runId); }
110
- requestHuman(reason, input) { return this.control.requestHuman(reason, input); }
111
- takeControl(input) { this.control.takeInteractive(input); }
112
- resumeControl(token) { return this.control.resume(token); }
113
- stopControl(token) { return this.control.stop(token); }
163
+ getControlState() {
164
+ return this.control.getState();
165
+ }
166
+ subscribeControl(listener) {
167
+ return this.control.subscribe(listener);
168
+ }
169
+ subscribeInterventions(listener) {
170
+ return this.control.subscribeInterventions(listener);
171
+ }
172
+ beginAgentAction(input) {
173
+ this.control.beginAgent(input);
174
+ }
175
+ finishAgentAction(runId) {
176
+ this.control.finishAgent(runId);
177
+ }
178
+ requestHuman(reason, input) {
179
+ return this.control.requestHuman(reason, input);
180
+ }
181
+ takeControl(input) {
182
+ this.control.takeInteractive(input);
183
+ }
184
+ resumeControl(token) {
185
+ return this.control.resume(token);
186
+ }
187
+ stopControl(token) {
188
+ return this.control.stop(token);
189
+ }
114
190
  resumeIntervention(credential) {
115
191
  return this.control.resumeIntervention(credential);
116
192
  }
117
193
  cancelIntervention(credential) {
118
194
  return this.control.cancelIntervention(credential);
119
195
  }
120
- waitForHuman(signal) { return this.control.waitForHuman(signal); }
196
+ waitForHuman(signal) {
197
+ return this.control.waitForHuman(signal);
198
+ }
121
199
  emit(event) {
122
- for (const listener of this.listeners)
123
- listener(event);
200
+ for (const listener of this.listeners) {
201
+ try {
202
+ listener(event);
203
+ }
204
+ catch {
205
+ // A disconnected/failed UI observer must not break browser control or
206
+ // prevent the remaining observers from receiving the event.
207
+ }
208
+ }
209
+ }
210
+ scheduleReconnect() {
211
+ if (this.disposed || !this.listeners.size || this.reconnectTimer)
212
+ return;
213
+ const delay = Math.min(10_000, 250 * 2 ** this.reconnectAttempt++);
214
+ this.reconnectTimer = setTimeout(() => {
215
+ this.reconnectTimer = null;
216
+ void this.connect().catch(() => this.scheduleReconnect());
217
+ }, delay);
124
218
  }
125
219
  onMessage(data) {
126
220
  try {
127
- const text = typeof data === 'string' ? data : Buffer.from(data).toString('utf8');
221
+ const text = typeof data === 'string'
222
+ ? data
223
+ : Buffer.from(data).toString('utf8');
128
224
  const event = JSON.parse(text);
129
225
  if (event.type === 'url' && typeof event.url === 'string')
130
226
  this.currentUrl = event.url;
@@ -133,7 +229,10 @@ class SessionBrowserStream {
133
229
  this.emit(event);
134
230
  }
135
231
  catch (error) {
136
- this.emit({ type: 'error', message: error instanceof Error ? error.message : String(error) });
232
+ this.emit({
233
+ type: 'error',
234
+ message: error instanceof Error ? error.message : String(error),
235
+ });
137
236
  }
138
237
  }
139
238
  }