minimal-agent 0.13.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 (470) 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 +503 -0
  103. package/src/advisor/advisorCoordinator.ts +657 -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/streamJson.js +5 -6
  108. package/src/cli/streamJson.ts +8 -4
  109. package/src/cli/wizards/advisorWizard.js +4 -2
  110. package/src/cli/wizards/advisorWizard.tsx +8 -2
  111. package/src/cli/wizards/contextAdvisorWizard.js +5 -4
  112. package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
  113. package/src/collaboration/taskFlow.js +115 -0
  114. package/src/collaboration/taskFlow.ts +169 -0
  115. package/src/config/configDefaults.js +41 -1
  116. package/src/config/configDefaults.ts +43 -1
  117. package/src/config/configDiagnostics.ts +2 -2
  118. package/src/config/configFile.js +40 -9
  119. package/src/config/configFile.ts +60 -11
  120. package/src/config/configService.js +419 -74
  121. package/src/config/configService.ts +703 -145
  122. package/src/config/numericLimits.js +22 -0
  123. package/src/config/numericLimits.ts +24 -0
  124. package/src/config.js +1 -0
  125. package/src/config.ts +3 -2
  126. package/src/context/budgetPlan.js +1 -1
  127. package/src/context/budgetPlan.ts +1 -1
  128. package/src/context/compact.js +7 -2
  129. package/src/context/compact.ts +9 -2
  130. package/src/context/microCompactLite.js +212 -13
  131. package/src/context/microCompactLite.ts +261 -12
  132. package/src/context/sessionRegistry.js +8 -1
  133. package/src/context/sessionRegistry.ts +18 -5
  134. package/src/context/toolResultArchive.js +72 -0
  135. package/src/context/toolResultArchive.ts +95 -0
  136. package/src/goal/errors.js +8 -0
  137. package/src/goal/errors.ts +18 -0
  138. package/src/goal/goalRuntime.js +344 -0
  139. package/src/goal/goalRuntime.ts +466 -0
  140. package/src/goal/goalStore.js +200 -0
  141. package/src/goal/goalStore.ts +219 -0
  142. package/src/goal/index.js +4 -0
  143. package/src/goal/index.ts +4 -0
  144. package/src/goal/statusMapping.js +71 -0
  145. package/src/goal/statusMapping.ts +95 -0
  146. package/src/goal/types.js +7 -0
  147. package/src/goal/types.ts +179 -0
  148. package/src/goal/workflowController.js +306 -0
  149. package/src/goal/workflowController.ts +360 -0
  150. package/src/harness/evidenceLedger.js +2 -0
  151. package/src/harness/evidenceLedger.ts +1 -0
  152. package/src/harness/stallGuard.js +9 -3
  153. package/src/harness/stallGuard.ts +7 -3
  154. package/src/images/imageResolver.js +161 -0
  155. package/src/images/imageResolver.ts +189 -0
  156. package/src/infra/verification.js +34 -5
  157. package/src/infra/verification.ts +39 -4
  158. package/src/llm/client.js +79 -21
  159. package/src/llm/client.ts +182 -35
  160. package/src/llm/gatewayClient.js +201 -0
  161. package/src/llm/gatewayClient.ts +299 -0
  162. package/src/llm/messageProvenance.js +46 -0
  163. package/src/llm/messageProvenance.ts +58 -0
  164. package/src/llm/modelProvider.js +1 -0
  165. package/src/llm/modelProvider.ts +22 -0
  166. package/src/llm/providers/anthropicFormat.js +108 -1
  167. package/src/llm/providers/anthropicFormat.ts +115 -4
  168. package/src/llm/providers/openaiFormat.js +9 -0
  169. package/src/llm/providers/openaiFormat.ts +12 -3
  170. package/src/loop.js +319 -54
  171. package/src/loop.ts +373 -57
  172. package/src/main.js +4 -1
  173. package/src/main.tsx +4 -1
  174. package/src/managedExecution/index.js +2 -0
  175. package/src/managedExecution/index.ts +2 -0
  176. package/src/managedExecution/registry.js +15 -0
  177. package/src/managedExecution/registry.ts +20 -0
  178. package/src/managedExecution/types.js +1 -0
  179. package/src/managedExecution/types.ts +86 -0
  180. package/src/modelRef.js +9 -0
  181. package/src/modelRef.ts +31 -0
  182. package/src/multimodal.js +14 -28
  183. package/src/multimodal.ts +17 -33
  184. package/src/net/lanDiscovery.js +65 -0
  185. package/src/net/lanDiscovery.ts +82 -0
  186. package/src/plugin-sdk.js +28 -3
  187. package/src/plugin-sdk.ts +39 -6
  188. package/src/plugins/pluginApi.ts +16 -1
  189. package/src/plugins/pluginLoader.js +4 -0
  190. package/src/plugins/pluginLoader.ts +6 -0
  191. package/src/plugins/pluginRunner.js +139 -7
  192. package/src/plugins/pluginRunner.ts +173 -9
  193. package/src/plugins/slashFallback.js +113 -0
  194. package/src/plugins/slashFallback.ts +137 -0
  195. package/src/prompts/policyRegistry.js +12 -32
  196. package/src/prompts/policyRegistry.ts +15 -32
  197. package/src/prompts/promptAssembly.js +103 -12
  198. package/src/prompts/promptAssembly.ts +114 -12
  199. package/src/prompts/skillList.js +21 -3
  200. package/src/prompts/skillList.ts +27 -2
  201. package/src/prompts/stableSystemPrompt.js +18 -0
  202. package/src/prompts/stableSystemPrompt.ts +33 -0
  203. package/src/runtime/agentRuntimeContext.js +8 -2
  204. package/src/runtime/agentRuntimeContext.ts +17 -4
  205. package/src/session/runAgentTurn.js +78 -12
  206. package/src/session/runAgentTurn.ts +105 -23
  207. package/src/session/runTurn.js +10 -9
  208. package/src/session/runTurn.ts +17 -8
  209. package/src/session/sessionState.ts +2 -0
  210. package/src/team/auth.js +186 -0
  211. package/src/team/auth.ts +298 -0
  212. package/src/team/cli.js +105 -0
  213. package/src/team/cli.ts +122 -0
  214. package/src/team/onlineToolContract.js +64 -0
  215. package/src/team/onlineToolContract.ts +109 -0
  216. package/src/team/toolPolicy.js +36 -0
  217. package/src/team/toolPolicy.ts +75 -0
  218. package/src/tools/advisor/advisor.js +90 -46
  219. package/src/tools/advisor/advisor.ts +100 -47
  220. package/src/tools/advisor/advisorDecision.js +71 -0
  221. package/src/tools/advisor/advisorDecision.ts +85 -0
  222. package/src/tools/agent/agent.js +18 -5
  223. package/src/tools/agent/agent.ts +21 -7
  224. package/src/tools/agent/agentControl.js +246 -0
  225. package/src/tools/agent/agentControl.ts +284 -0
  226. package/src/tools/agent/agentTaskService.js +40 -1
  227. package/src/tools/agent/agentTaskService.ts +44 -1
  228. package/src/tools/agent/agentTypes.js +14 -2
  229. package/src/tools/agent/agentTypes.ts +15 -2
  230. package/src/tools/applypatch/applyPatch.js +362 -0
  231. package/src/tools/applypatch/applyPatch.ts +428 -0
  232. package/src/tools/bash/bash.js +22 -15
  233. package/src/tools/bash/bash.ts +23 -17
  234. package/src/tools/capabilities.js +10 -5
  235. package/src/tools/capabilities.ts +10 -5
  236. package/src/tools/computeruse/computerIntent.js +32 -0
  237. package/src/tools/computeruse/computerIntent.ts +46 -0
  238. package/src/tools/computeruse/telemetryLog.js +12 -0
  239. package/src/tools/computeruse/telemetryLog.ts +16 -0
  240. package/src/tools/computeruse/vision/telemetry.js +1 -0
  241. package/src/tools/computeruse/vision/telemetry.ts +1 -0
  242. package/src/tools/effectiveToolSet.js +4 -0
  243. package/src/tools/effectiveToolSet.ts +4 -0
  244. package/src/tools/index.js +21 -2
  245. package/src/tools/index.ts +33 -2
  246. package/src/tools/presentresult/presentresult.js +6 -15
  247. package/src/tools/presentresult/presentresult.ts +6 -15
  248. package/src/tools/read/read.js +62 -9
  249. package/src/tools/read/read.ts +63 -9
  250. package/src/tools/shared/fileUtils.js +1 -2
  251. package/src/tools/shared/fileUtils.ts +1 -2
  252. package/src/tools/shared/toolOutputStore.js +23 -1
  253. package/src/tools/shared/toolOutputStore.ts +22 -1
  254. package/src/tools/skill/skill.js +9 -3
  255. package/src/tools/skill/skill.ts +8 -4
  256. package/src/tools/toolPromptGuides.js +117 -0
  257. package/src/tools/toolPromptGuides.ts +137 -0
  258. package/src/tools/webbrowser/agentBrowserPath.js +19 -9
  259. package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
  260. package/src/tools/webbrowser/browser.js +27 -11
  261. package/src/tools/webbrowser/browser.ts +68 -24
  262. package/src/tools/webbrowser/browserAutomation.js +329 -0
  263. package/src/tools/webbrowser/browserAutomation.ts +467 -0
  264. package/src/tools/webbrowser/browserAxState.js +97 -0
  265. package/src/tools/webbrowser/browserAxState.ts +142 -0
  266. package/src/tools/webbrowser/browserBatch.js +1578 -0
  267. package/src/tools/webbrowser/browserBatch.ts +2382 -0
  268. package/src/tools/webbrowser/browserCdp.js +208 -0
  269. package/src/tools/webbrowser/browserCdp.ts +255 -0
  270. package/src/tools/webbrowser/browserConditions.js +24 -0
  271. package/src/tools/webbrowser/browserConditions.ts +45 -0
  272. package/src/tools/webbrowser/browserContent.js +281 -0
  273. package/src/tools/webbrowser/browserContent.ts +438 -0
  274. package/src/tools/webbrowser/browserControl.js +131 -34
  275. package/src/tools/webbrowser/browserControl.ts +210 -56
  276. package/src/tools/webbrowser/browserDaemon.js +22 -14
  277. package/src/tools/webbrowser/browserDaemon.ts +54 -21
  278. package/src/tools/webbrowser/browserFrames.js +47 -0
  279. package/src/tools/webbrowser/browserFrames.ts +54 -0
  280. package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
  281. package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
  282. package/src/tools/webbrowser/browserLocator.js +140 -0
  283. package/src/tools/webbrowser/browserLocator.ts +194 -0
  284. package/src/tools/webbrowser/browserOutcome.js +10 -17
  285. package/src/tools/webbrowser/browserOutcome.ts +48 -31
  286. package/src/tools/webbrowser/browserRecovery.js +58 -0
  287. package/src/tools/webbrowser/browserRecovery.ts +83 -0
  288. package/src/tools/webbrowser/browserRunRegistry.js +35 -3
  289. package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
  290. package/src/tools/webbrowser/browserService.js +28 -2
  291. package/src/tools/webbrowser/browserService.ts +49 -9
  292. package/src/tools/webbrowser/browserStream.js +132 -33
  293. package/src/tools/webbrowser/browserStream.ts +224 -50
  294. package/src/tools/webbrowser/browserTabs.js +48 -15
  295. package/src/tools/webbrowser/browserTabs.ts +178 -96
  296. package/src/tools/webbrowser/webbrowser.js +679 -144
  297. package/src/tools/webbrowser/webbrowser.ts +975 -236
  298. package/src/tools/webfetch/preapproved.js +0 -2
  299. package/src/tools/webfetch/preapproved.ts +0 -2
  300. package/src/tools/websearch/websearch.js +129 -8
  301. package/src/tools/websearch/websearch.ts +138 -6
  302. package/src/types.ts +440 -38
  303. package/src/ui/App.js +377 -58
  304. package/src/ui/App.tsx +576 -111
  305. package/src/ui/LiveArea.js +14 -8
  306. package/src/ui/LiveArea.tsx +14 -10
  307. package/src/ui/MessageList.js +63 -20
  308. package/src/ui/MessageList.tsx +101 -16
  309. package/src/ui/ModelPicker.js +7 -4
  310. package/src/ui/ModelPicker.tsx +55 -8
  311. package/src/ui/hooks/useChat.js +152 -163
  312. package/src/ui/hooks/useChat.ts +396 -334
  313. package/src/ui/loginCommand.js +56 -0
  314. package/src/ui/loginCommand.ts +63 -0
  315. package/src/ui/toolProgress.js +41 -0
  316. package/src/ui/toolProgress.ts +44 -0
  317. package/src/web/browserBridge.js +20 -2
  318. package/src/web/browserBridge.ts +20 -2
  319. package/src/web/protocol.ts +1897 -194
  320. package/workflows/adaptive-research/workflow.mjs +86 -0
  321. package/workflows/e2e-capabilities/workflow.mjs +102 -0
  322. package/workflows/e2e-llm/workflow.mjs +52 -0
  323. package/workflows/e2e-smoke/workflow.mjs +59 -0
  324. package/workflows/e2e-vision-schema/README.md +48 -0
  325. package/workflows/e2e-vision-schema/workflow.mjs +149 -0
  326. package/workflows/e2e-write-greet/workflow.mjs +37 -0
  327. package/workflows/file-census/README.md +14 -0
  328. package/workflows/file-census/scripts/.gitkeep +2 -0
  329. package/workflows/file-census/workflow.mjs +110 -0
  330. package/workflows/goal/README.md +166 -0
  331. package/workflows/goal/prompts/audit.md +57 -0
  332. package/workflows/goal/prompts/contract.md +56 -0
  333. package/workflows/goal/prompts/iterate.md +37 -0
  334. package/workflows/goal/workflow.mjs +501 -0
  335. package/workflows/illustrate/README.md +90 -0
  336. package/workflows/illustrate/assets/engines.json +20 -0
  337. package/workflows/illustrate/assets/extract-baidu.js +21 -0
  338. package/workflows/illustrate/assets/extract-bing.js +33 -0
  339. package/workflows/illustrate/assets/extract-google.js +31 -0
  340. package/workflows/illustrate/workflow.mjs +301 -0
  341. package/workflows/plan/workflow.mjs +389 -0
  342. package/workflows/question-crops/README.md +35 -0
  343. package/workflows/question-crops/prompts/inventory.md +23 -0
  344. package/workflows/question-crops/scripts/image-ops.mjs +305 -0
  345. package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
  346. package/workflows/question-crops/workflow.mjs +172 -0
  347. package/workflows/ralph-loop/workflow.mjs +162 -0
  348. package/workflows/research/README.md +74 -0
  349. package/workflows/research/prompts/s0-goal.md +43 -0
  350. package/workflows/research/prompts/s1-early-exit.md +22 -0
  351. package/workflows/research/prompts/s1-map.md +58 -0
  352. package/workflows/research/prompts/s2-options.md +53 -0
  353. package/workflows/research/prompts/s3-deep-dive.md +56 -0
  354. package/workflows/research/prompts/s4-verify.md +74 -0
  355. package/workflows/research/prompts/s5-synthesis.md +81 -0
  356. package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
  357. package/workflows/research/prompts/s6-package.md +32 -0
  358. package/workflows/research/scripts/build-report.mjs +98 -0
  359. package/workflows/research/workflow.mjs +288 -0
  360. package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
  361. package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
  362. package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
  363. package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
  364. package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
  365. package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
  366. package/workflows/wechat-reading-shots/workflow.mjs +266 -0
  367. package/workflows/wrong-question-docx/README.md +55 -0
  368. package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
  369. package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
  370. package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
  371. package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
  372. package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
  373. package/workflows/wrong-question-docx/workflow.mjs +485 -0
  374. package/plugins/workflow-runner/src/agentCommon.js +0 -354
  375. package/plugins/workflow-runner/src/agentCommon.ts +0 -435
  376. package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
  377. package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
  378. package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
  379. package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
  380. package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
  381. package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
  382. package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
  383. package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
  384. package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
  385. package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
  386. package/plugins/workflow-runner/src/dslManifest.js +0 -72
  387. package/plugins/workflow-runner/src/dslManifest.ts +0 -127
  388. package/plugins/workflow-runner/src/expressions.js +0 -639
  389. package/plugins/workflow-runner/src/expressions.ts +0 -640
  390. package/plugins/workflow-runner/src/loader.js +0 -573
  391. package/plugins/workflow-runner/src/loader.ts +0 -721
  392. package/plugins/workflow-runner/src/multimodal.js +0 -38
  393. package/plugins/workflow-runner/src/multimodal.ts +0 -64
  394. package/plugins/workflow-runner/src/recovery.js +0 -126
  395. package/plugins/workflow-runner/src/recovery.ts +0 -156
  396. package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
  397. package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
  398. package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -42
  399. package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -52
  400. package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
  401. package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
  402. package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
  403. package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
  404. package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
  405. package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
  406. package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
  407. package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
  408. package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -227
  409. package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -274
  410. package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -61
  411. package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -72
  412. package/plugins/workflow-runner/src/stepPipeline.js +0 -277
  413. package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
  414. package/plugins/workflow-runner/test/agent.test.ts +0 -587
  415. package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
  416. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
  417. package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
  418. package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -167
  419. package/plugins/workflow-runner/test/context-files.test.ts +0 -320
  420. package/plugins/workflow-runner/test/e2e.test.ts +0 -370
  421. package/plugins/workflow-runner/test/e2eNewLayout.test.ts +0 -314
  422. package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -234
  423. package/plugins/workflow-runner/test/expressions.test.ts +0 -439
  424. package/plugins/workflow-runner/test/foundation.test.ts +0 -126
  425. package/plugins/workflow-runner/test/graceful.test.ts +0 -139
  426. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
  427. package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
  428. package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
  429. package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
  430. package/plugins/workflow-runner/test/loader.test.ts +0 -617
  431. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
  432. package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
  433. package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
  434. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -106
  435. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
  436. package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
  437. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -221
  438. package/plugins/workflow-runner/test/recovery.test.ts +0 -265
  439. package/plugins/workflow-runner/test/research.load.test.ts +0 -328
  440. package/plugins/workflow-runner/test/runner.test.ts +0 -911
  441. package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -436
  442. package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
  443. package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -198
  444. package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
  445. package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -354
  446. package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -324
  447. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
  448. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
  449. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
  450. package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
  451. package/plugins/workflow-runner/test/word.load.test.ts +0 -208
  452. package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
  453. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
  454. package/src/advisorState.js +0 -22
  455. package/src/cli/configWizard.js +0 -293
  456. package/src/tools/toolProgressBridge.js +0 -48
  457. package/workflows/adaptive-research.yaml +0 -50
  458. package/workflows/book-review-short.yaml +0 -125
  459. package/workflows/e2e-write-greet.yaml +0 -31
  460. package/workflows/illustrate.yaml +0 -227
  461. package/workflows/plan.yaml +0 -470
  462. package/workflows/ppt.yaml +0 -1670
  463. package/workflows/ralph-loop.yaml +0 -201
  464. package/workflows/research.yaml +0 -873
  465. package/workflows/schema.json +0 -176
  466. package/workflows/webppt.yaml +0 -496
  467. package/workflows/wechat-reading-shots.yaml +0 -315
  468. package/workflows/word.yaml +0 -720
  469. package/workflows/youtube-shorts.yaml +0 -202
  470. /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
  }