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,306 +1,177 @@
1
- import { createHash, randomBytes, randomUUID } from 'node:crypto';
2
- import { existsSync, readFileSync, readdirSync } from 'node:fs';
1
+ /**
2
+ * ============================================================
3
+ * plugins/workflow-runner/src/taskIdentity.ts —— 任务身份与状态目录布局
4
+ * ------------------------------------------------------------
5
+ * 状态根目录优先级:
6
+ * 1. 环境变量 MINIMAL_AGENT_WORKFLOW_STATE_DIR(全局统一目录)
7
+ * 2. <cwd>/.minimal-agent-workflow/(默认)
8
+ *
9
+ * 目录结构(四级):
10
+ * <stateRoot>/<workflow>/<sessionId>/<taskId>/ ← 活动任务实例
11
+ * <stateRoot>/<workflow>/<sessionId>/<taskId>.done/ ← 成功归档
12
+ * <stateRoot>/<workflow>/<sessionId>/<taskId>.trash/ ← 产物淘汰回收
13
+ *
14
+ * ------------------------------------------------------------
15
+ * ⚠️ **为什么是 session 与 taskId 两层,而不是二选一**(这条改之前务必读完)
16
+ *
17
+ * legacy 的演进history是这样的:
18
+ * - Phase 2:加 taskId 实例层,根治「同名工作流跑新任务把旧状态清掉」
19
+ * - Phase 3:实例目录名**改成** sessionId,taskId 降级为记录字段,换来多
20
+ * session 真正并行
21
+ *
22
+ * Phase 3 用「换掉」而不是「叠加」,于是 Phase 2 解决的问题原样回来了:同一个
23
+ * session 里连着研究两个不同主题,第二次启动时前一次的声明产物会被归档进
24
+ * `.trash/`,工作目录里只剩最新一份。
25
+ *
26
+ * legacy 对此的补偿是「宿主没给显式 session 就自动生成一个带时间戳的」,但那让
27
+ * resume 彻底失效 —— 时间每次都不同,于是**每次运行都是一个新 session**,
28
+ * 断点续跑、completed 判定、语义仲裁全部够不着。实测复现过:同一个 goal 连跑
29
+ * 两次,产生两个独立的 `.done` 目录,第二次没有命中 completed 而是从头重跑。
30
+ *
31
+ * 两个维度本来就是正交的,不该二选一:
32
+ * - sessionId 管**并行隔离**(不同会话互不干扰)
33
+ * - taskId 管**任务身份**(同一件事 → 同一个目录 → 能 resume)
34
+ * 两层都留下,三个需求同时成立:
35
+ * 同 session 同 goal → 同目录 → resume ✓
36
+ * 同 session 换主题 → 不同目录 → **不覆盖** ✓
37
+ * 不同 session → 不同目录 → 并行 ✓
38
+ *
39
+ * 相应地本文件**不提供** `generateDefaultSessionId`:宿主给什么 session 就用
40
+ * 什么(含 `default`)。它当初存在的理由已由 taskId 层从结构上消除。
41
+ * ============================================================
42
+ */
43
+ import { createHash, randomUUID } from 'node:crypto';
3
44
  import { isAbsolute, join, resolve } from 'node:path';
4
45
  import { chat as sdkChat, executeTool as sdkExecuteTool, } from '../../../src/plugin-sdk.js';
5
- import { truncate } from './agentCommon.js';
6
46
  import { extractJsonPayload } from './jsonPayload.js';
7
- // ============================================================
8
- // 工作流状态目录布局(Phase 1 + 2 + 3 升级)
9
- // ------------------------------------------------------------
10
- // 状态根目录优先级:
11
- // 1. 环境变量 MINIMAL_AGENT_WORKFLOW_STATE_DIR(全局统一目录)
12
- // 2. <cwd>/.minimal-agent-workflow/(默认,与历史行为一致)
13
- //
14
- // 目录结构(三级:工作流名 / session 实例 / taskId 记录):
15
- // <stateRoot>/<workflowName>/<sessionId>/ ← 活动任务实例(session 即任务实例)
16
- // <stateRoot>/<workflowName>/<sessionId>.done/ ← 成功完成的实例归档
17
- // <stateRoot>/<workflowName>/<sessionId>.trash/ ← 清理回收(A4 归档)
18
- //
19
- // Phase 3(session 化):实例目录名 = sessionId(一个 session 同时只跑一个
20
- // 同名工作流,session 即天然的任务实例划分单元)。taskId(goal 内容 hash)
21
- // 降级为 current.json 里的【记录字段】——用于同 session 内识别「换主题 →
22
- // new-task 归档重建 / 同主题 → resume/completed」。不同 session 完全隔离,
23
- // 同主题也可并行互不干扰。
24
- //
25
- // 演进历史:
26
- // - 最初:<cwd>/.minimal-agent-workflow/ 单例(所有工作流共用)
27
- // - E6:按 workflow 名隔离,<cwd>/.minimal-agent-workflow/<name>/
28
- // - Phase 1:状态根目录可配置(环境变量指定全局位置,解决 cwd 切换找不到)
29
- // - Phase 2:新增 taskId 实例层,同名 workflow 多任务并存(根治新任务清掉旧状态)
30
- // - Phase 3:实例目录名改为 sessionId,taskId 降级为记录字段(多 session 真正并行)
31
- // ============================================================
32
- /** 工作流状态目录的父目录名(默认落在 cwd 下时使用)。 */
47
+ import { truncate } from './util.js';
48
+ /** 工作流状态目录的父目录名(落在 cwd 下时使用)。 */
33
49
  export const WF_STATE_PARENT = '.minimal-agent-workflow';
34
50
  /** 全局统一状态目录的环境变量名。 */
35
51
  export const WF_STATE_DIR_ENV = 'MINIMAL_AGENT_WORKFLOW_STATE_DIR';
36
- /** 缺省 sessionId(宿主未显式传 session 时的兜底判定值;真正落盘的 session 由 generateDefaultSessionId 自动命名)。 */
52
+ /** 宿主未指定 session 时的缺省名。**不再**据此自动生成带时间戳的 session。 */
37
53
  export const DEFAULT_SESSION_ID = 'default';
38
- /**
39
- * 工作流引擎的默认 session 自动命名:宿主未显式提供有意义 session(缺失或为
40
- * DEFAULT_SESSION_ID)时,由引擎自行生成唯一可读的 session 名
41
- * `YYYYMMDD-HHmmss-xxxxxx`(日期时间 + 6 位随机 hex)。
42
- *
43
- * 语义:每次工作流运行(CLI -p 默认 / TUI default 会话)都自动落到独立的
44
- * `<out>/<workflow>/<session>/` 产物目录与 `<stateRoot>/<workflow>/<session>/`
45
- * 状态目录——多 session 并行互不覆盖;时间前缀便于人识别与排序,随机后缀
46
- * 防同一秒并行冲突。宿主显式传 `-s <id>`(非 default)时仍优先使用宿主 id。
47
- */
48
- export function generateDefaultSessionId(now = new Date()) {
49
- const pad = (n, w = 2) => String(n).padStart(w, '0');
50
- const date = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`;
51
- const time = `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
52
- const rand = randomBytes(3).toString('hex');
53
- return `${date}-${time}-${rand}`;
54
- }
55
54
  /**
56
55
  * 解析状态根目录(绝对路径)。
57
- * 优先级:环境变量 MINIMAL_AGENT_WORKFLOW_STATE_DIR > cwd 下默认目录。
58
- * 环境变量允许相对路径——基于 process.cwd() 展开(用户可能写 `.wflow` 这类相对值)。
59
- * 传入 workingDir 是为了 cwd 默认模式的相对路径基准。
56
+ * 环境变量允许写相对路径(用户可能写 `.wflow` 这类值),基于 process.cwd() 展开。
60
57
  */
61
58
  export function resolveStateRoot(workingDir) {
62
59
  const envDir = process.env[WF_STATE_DIR_ENV];
63
- if (envDir && envDir.trim()) {
60
+ if (envDir?.trim()) {
64
61
  const p = envDir.trim();
65
62
  return isAbsolute(p) ? p : resolve(p);
66
63
  }
67
64
  return join(workingDir, WF_STATE_PARENT);
68
65
  }
69
- /** 把 workflow 名 / sessionId 清洗成安全的目录名(保留字母数字._-,其余替换为 -)。 */
66
+ /** 清洗成安全目录名(保留字母数字 . _ -,其余换成 -)。 */
70
67
  export function sanitizeWorkflowDirName(name) {
71
68
  return name.replace(/[^\w.-]/g, '-');
72
69
  }
73
- /**
74
- * 某 workflow 的状态目录绝对路径(workflow 级目录,下含多个 session 实例)。
75
- * workflowState / taskIdentity 共用唯一权威实现。
76
- */
70
+ /** 某工作流的状态目录(其下按 session 再分)。 */
77
71
  export function workflowStateDir(workingDir, workflowName) {
78
72
  return join(resolveStateRoot(workingDir), sanitizeWorkflowDirName(workflowName));
79
73
  }
80
- /**
81
- * 具体任务实例(= session)的状态目录。
82
- * 成功后改名为 <sessionId>.done/;清理归档目录为 <sessionId>.trash/。
83
- */
84
- export function taskInstanceDir(workingDir, workflowName, sessionId) {
74
+ /** 某工作流在某 session 下的目录(其下按 taskId 再分)。 */
75
+ export function sessionStateDir(workingDir, workflowName, sessionId) {
85
76
  return join(workflowStateDir(workingDir, workflowName), sanitizeWorkflowDirName(sessionId));
86
77
  }
87
78
  /**
88
- * 计算任务身份 hash。
89
- *
90
- * 策略(内容 hash 是【唯一权威信号】):
91
- * - 对「workflow 名 + 归一化 goal」做 SHA256 → 取前 16 位 = taskId。
92
- * - 同 hash → 同一任务(无论隔了多久);hash 不同 → 不同任务。
93
- * - recency(上次 startedAt 的时间窗口)【不】参与身份判定:30 分钟内换一个
94
- * 完全不同的 goal 重跑同名 workflow,必须判「全新任务」清理重开,而不是
95
- * 错误地 resume 旧任务把旧变量混进新目标(需求 3.2「不相关新任务自动清理重开」)。
96
- * 旧版「时间窗口覆盖 hash 差异」是 bug,已移除。
97
- *
98
- * 归一化规则:
99
- * - 转小写
100
- * - 去除所有标点符号(保留中文、英文、数字、空格)
101
- * - 去除多余空格(连续空格→单空格,首尾 trim)
102
- * - 截断到 200 字符(防止超长输入导致不必要的差异)
103
- *
104
- * 参数:
105
- * - workingDir:不传只返回 hash(纯计算,不读盘)。
106
- * - sessionId:Phase 3 后传入——实例查找限定到「当前 session」的实例目录
107
- * (<wfDir>/<sessionId>/ 与 <sessionId>.done/),读其 current.json 的 taskId
108
- * 记录与本次 hash 比较。缺省时退化为旧行为(扫 wfDir 下所有 taskId 实例),
109
- * 兼容历史调用与测试。
79
+ * 一个具体任务实例的状态目录。
80
+ * 成功后改名 `<taskId>.done/`;产物淘汰归档到 `<taskId>.trash/`。
110
81
  */
111
- export function computeTaskId(workflowName, inputs, workingDir, sessionId) {
112
- // 1. 提取 goal 文本(与 buildGoalAnchor 同策略)
113
- const rawGoal = extractGoal(inputs);
114
- // 2. 归一化 goal
115
- const normalizedGoal = normalizeGoal(rawGoal);
116
- // 3. 计算 contentHash = SHA256(`${workflowName}::${normalizedGoal}`)[:16]
117
- const contentHash = createHash('sha256')
118
- .update(`${workflowName}::${normalizedGoal}`)
119
- .digest('hex')
120
- .slice(0, 16);
121
- // 4. 如果没有提供 workingDir,只返回 hash,不做比较
122
- if (!workingDir) {
123
- return {
124
- hash: contentHash,
125
- isSameTask: false,
126
- reason: 'no_previous',
127
- };
128
- }
129
- // 5. 找当前任务对应的实例目录,读其 current.json 记录与本次 hash 比较
130
- // (Phase 3:session 语义——只查当前 session 的实例;taskId 是记录字段)
131
- const wfDir = workflowStateDir(workingDir, workflowName);
132
- const candidateDirs = findTaskInstanceDirs(wfDir, contentHash, sessionId);
133
- if (candidateDirs.length === 0) {
134
- // 无精确匹配。但若 wfDir 下存在其他任务实例(不同 goal / 已归档),
135
- // 语义仍是「新任务 + 旧任务在场」——返回 hash_diff 而非 no_previous,
136
- // 让 workflowState.init 能触发仲裁/清理旧产物(旧布局靠读 wfDir/current.json
137
- // 天然得到这个信号,新布局必须显式扫描)。
138
- return {
139
- hash: contentHash,
140
- isSameTask: false,
141
- reason: hasAnyTaskInstanceInDir(wfDir, sessionId) ? 'hash_diff' : 'no_previous',
142
- };
143
- }
144
- // 取第一个匹配的(hash 一样就是同一任务,不应有多个;有多个也只取第一个)
145
- const stateFilePath = join(candidateDirs[0], 'current.json');
146
- if (!existsSync(stateFilePath)) {
147
- return {
148
- hash: contentHash,
149
- isSameTask: false,
150
- reason: 'no_previous',
151
- };
152
- }
153
- try {
154
- const stateContent = readFileSync(stateFilePath, 'utf8');
155
- const prevState = JSON.parse(stateContent);
156
- // a. 不同 workflow 名 → 不同任务(理论上不会发生,因为已经按目录隔离了,防御性检查)
157
- if (prevState.name && prevState.name !== workflowName) {
158
- return {
159
- hash: contentHash,
160
- isSameTask: false,
161
- reason: 'different_workflow',
162
- };
163
- }
164
- // b. 内容 hash 一致 → 同一任务(唯一权威信号,与时间无关)
165
- if (prevState.taskId === contentHash) {
166
- return {
167
- hash: contentHash,
168
- isSameTask: true,
169
- reason: 'hash_match',
170
- };
171
- }
172
- // c. hash 不一致 → 不同任务(goal 变了)。recency 不覆盖此判定(见函数头注)。
173
- return {
174
- hash: contentHash,
175
- isSameTask: false,
176
- reason: 'hash_diff',
177
- };
178
- }
179
- catch {
180
- // 解析失败时返回默认结果
181
- return {
182
- hash: contentHash,
183
- isSameTask: false,
184
- reason: 'no_previous',
185
- };
186
- }
82
+ export function taskInstanceDir(workingDir, workflowName, sessionId, taskId) {
83
+ return join(sessionStateDir(workingDir, workflowName, sessionId), sanitizeWorkflowDirName(taskId));
187
84
  }
188
85
  /**
189
- * 在 workflow 级目录下,找到当前任务对应的实例目录。
190
- * 支持两种布局:
191
- * - 新布局(Phase 3,传 sessionId):<wfDir>/<sessionId>/current.json(活动)
192
- * 与 <wfDir>/<sessionId>.done/current.json(已完成)
193
- * - 旧布局(Phase 2,不传 sessionId):<wfDir>/<taskId>/current.json
194
- * 与 <wfDir>/<taskId>.done/current.json
195
- * - 旧布局兼容(无 session 层):<wfDir>/current.json —— 把整个 wfDir 当成一个实例
196
- * 返回绝对路径数组。
86
+ * 归一化 goal:小写 → 去标点(保留中日韩/字母/数字/空格)→ 压缩空格 → 截断 200。
87
+ *
88
+ * 截断是刻意的:超长输入里靠后的细微差异不该让「同一件事」算成两个任务。
197
89
  */
198
- function findTaskInstanceDirs(wfDir, taskId, sessionId) {
199
- const results = [];
200
- if (sessionId) {
201
- // Phase 3:session 语义 —— 只找当前 session 的实例(taskId 是记录字段,目录名是 sessionId)
202
- const activeDir = join(wfDir, sanitizeWorkflowDirName(sessionId));
203
- const doneDir = join(wfDir, `${sanitizeWorkflowDirName(sessionId)}.done`);
204
- if (existsSync(activeDir))
205
- results.push(activeDir);
206
- if (existsSync(doneDir))
207
- results.push(doneDir);
208
- }
209
- else {
210
- // Phase 2 旧行为:<wfDir>/<taskId>/ 和 <wfDir>/<taskId>.done/
211
- const activeDir = join(wfDir, taskId);
212
- const doneDir = join(wfDir, `${taskId}.done`);
213
- if (existsSync(activeDir))
214
- results.push(activeDir);
215
- if (existsSync(doneDir))
216
- results.push(doneDir);
217
- }
218
- if (results.length > 0)
219
- return results;
220
- // 旧布局兼容:wfDir 本身就是实例目录(current.json 直接在 wfDir 下)
221
- // 检测方法:wfDir/current.json 存在且 wfDir 下没有子目录是 taskId 格式
222
- if (existsSync(join(wfDir, 'current.json'))) {
223
- // 确认这是旧布局(不是某个子实例目录)
224
- try {
225
- const entries = readdirSync(wfDir, { withFileTypes: true });
226
- const hasInstanceSubdir = entries.some((e) => e.isDirectory() && /^[0-9a-f]{16}$/.test(e.name));
227
- if (!hasInstanceSubdir) {
228
- results.push(wfDir);
229
- }
230
- }
231
- catch {
232
- // 读不到就当旧布局
233
- results.push(wfDir);
234
- }
235
- }
236
- return results;
90
+ function normalizeGoal(raw) {
91
+ return raw
92
+ .toLowerCase()
93
+ .replace(/[^\p{L}\p{N}\s]/gu, '')
94
+ .replace(/\s+/g, ' ')
95
+ .trim()
96
+ .slice(0, 200);
237
97
  }
238
98
  /**
239
- * 检查 wfDir 下是否存在任何任务实例(活动态 / .done 归档态 / 旧布局 current.json)。
240
- * 不含 .trash 回收目录。用于 computeTaskId 在无精确匹配时区分
241
- * hash_diff(有其他旧任务)与 no_previous(从未跑过)。
242
- * 传 sessionId 时只统计当前 session 的实例(Phase 3)。
99
+ * 任务身份 = SHA256(工作流名 + 归一化 goal) 前 16 位。
100
+ *
101
+ * **纯函数,不读盘** —— 这是四级布局带来的简化:taskId 就是目录名,「是不是同一个
102
+ * 任务」等价于「那个目录在不在」,不必再扫描实例目录逐个比对记录字段。
103
+ *
104
+ * ⚠️ 时间**不参与**身份判定。半小时内换一个完全不同的 goal 重跑同名工作流,
105
+ * 必须判成全新任务,而不是 resume 旧任务把旧变量混进新目标里。legacy 早期
106
+ * 「时间窗口覆盖 hash 差异」的写法是 bug,已移除,别加回来。
243
107
  */
244
- function hasAnyTaskInstanceInDir(wfDir, sessionId) {
245
- if (!existsSync(wfDir))
246
- return false;
247
- try {
248
- if (sessionId) {
249
- // Phase 3:只看当前 session 的实例
250
- const active = join(wfDir, sanitizeWorkflowDirName(sessionId));
251
- const done = join(wfDir, `${sanitizeWorkflowDirName(sessionId)}.done`);
252
- if (existsSync(active))
253
- return true;
254
- if (existsSync(done))
255
- return true;
256
- // 旧布局:wfDir 本身就是实例目录
257
- if (existsSync(join(wfDir, 'current.json')))
258
- return true;
259
- if (existsSync(join(wfDir + '.done', 'current.json')))
260
- return true;
261
- return false;
262
- }
263
- const entries = readdirSync(wfDir, { withFileTypes: true });
264
- for (const e of entries) {
265
- if (!e.isDirectory())
266
- continue;
267
- if (/^[0-9a-f]{16}$/.test(e.name))
268
- return true; // 活动实例
269
- if (/^[0-9a-f]{16}\.done$/.test(e.name))
270
- return true; // 归档实例
271
- }
272
- // 旧布局:wfDir 本身就是实例目录
273
- if (existsSync(join(wfDir, 'current.json')))
274
- return true;
275
- if (existsSync(join(wfDir + '.done', 'current.json')))
276
- return true;
277
- return false;
278
- }
279
- catch {
280
- return false;
281
- }
108
+ export function computeTaskId(workflowName, inputs, defs) {
109
+ const goal = normalizeGoal(extractGoal(inputs, defs));
110
+ const resourceFingerprint = typeof inputs.__resource_fingerprint === 'string'
111
+ ? inputs.__resource_fingerprint.trim()
112
+ : '';
113
+ // Typed resource workflows also have processing parameters (format, range,
114
+ // language, etc.). Equal source bytes with different parameters must not hit
115
+ // completed before the script can observe the new arguments. Keep the legacy
116
+ // goal-only identity for workflows without resource bindings.
117
+ const parameters = resourceFingerprint
118
+ ? (defs ?? [])
119
+ .filter((d) => d.type !== 'resource' && !['goal', 'requestText'].includes(d.name))
120
+ .map((d) => [d.name, inputs[d.name] ?? d.default ?? null])
121
+ .sort(([a], [b]) => a.localeCompare(b))
122
+ : [];
123
+ // 资源是任务身份的一部分,而不是目标锚的一部分。这样模型仍只看到人类可读
124
+ // 目标,但同一句“帮我整理成 Word”配不同附件不会误命中上一份已完成产物。
125
+ const identity = resourceFingerprint
126
+ ? `${workflowName} ${goal}\nresource:${resourceFingerprint}${parameters.length ? '\nparameters:' + JSON.stringify(parameters) : ''}`
127
+ : `${workflowName} ${goal}`;
128
+ return createHash('sha256').update(identity).digest('hex').slice(0, 16);
282
129
  }
283
130
  /**
284
- * 从 inputs 中提取 goal 文本
131
+ * 从 inputs 里提取「目标」文本。**任务身份与目标锚共用这一份规则** ——
132
+ * 两处各写一套会让「拿来算 taskId 的目标」和「注入给模型的目标」指向不同的东西。
285
133
  *
286
- * 策略:
287
- * - 优先 inputs.goal
288
- * - 其次第一个 length > 2 的 string 值
289
- * - 最后 JSON.stringify 整个 inputs
134
+ * 取值顺序:
135
+ * ① 名为 `goal` 的输入
136
+ * ② 第一个 **required** 的字符串输入(作者用 required 表达了「这是主要输入」)
137
+ * ③ 第一个非空字符串输入
138
+ * ④ 整体 JSON(保证恒有确定值)
139
+ *
140
+ * ⚠️ 第 ③ 条曾经写的是「长度 **> 2** 的字符串」。那条阈值是照拉丁文本想的
141
+ * (跳过 `ab`、语言代码之类),**对中文直接失效** —— `世界`、`调研` 这种两个字的
142
+ * 目标太常见了,会被跳过去落到 ④,于是 current.json 里记的目标变成一坨 JSON、
143
+ * 仲裁时喂给 LLM 的也是那坨 JSON。端到端实跑复现过(/workflow e2e-smoke 世界)。
290
144
  */
291
- export function extractGoal(inputs) {
292
- // 优先级1:直接使用 inputs.goal
293
- if (typeof inputs.goal === 'string') {
145
+ export function extractGoal(inputs, defs) {
146
+ if (typeof inputs.goal === 'string' && inputs.goal.trim()) {
294
147
  return inputs.goal;
295
148
  }
296
- // 优先级2:查找第一个长度 > 2 的字符串值
297
- for (const value of Object.values(inputs)) {
298
- if (typeof value === 'string' && value.length > 2) {
149
+ for (const def of defs ?? []) {
150
+ const v = inputs[def.name];
151
+ if (def.type !== 'resource' &&
152
+ def.required &&
153
+ typeof v === 'string' &&
154
+ v.trim())
155
+ return v;
156
+ }
157
+ if (typeof inputs.requestText === 'string' && inputs.requestText.trim())
158
+ return inputs.requestText;
159
+ const resourceNames = new Set((defs ?? []).filter((d) => d.type === 'resource').map((d) => d.name));
160
+ for (const [name, value] of Object.entries(inputs)) {
161
+ if (resourceNames.has(name) || name === '__resource_fingerprint')
162
+ continue;
163
+ if (typeof value === 'string' && value.trim())
299
164
  return value;
300
- }
301
165
  }
302
- // 优先级3:JSON 序列化整个 inputs
303
- return JSON.stringify(inputs);
166
+ if (typeof inputs.__resource_fingerprint === 'string' &&
167
+ inputs.__resource_fingerprint.trim()) {
168
+ return `resource:${inputs.__resource_fingerprint}`;
169
+ }
170
+ const sanitized = { ...inputs };
171
+ for (const name of resourceNames)
172
+ if (name in sanitized)
173
+ sanitized[name] = '<resource>';
174
+ return JSON.stringify(sanitized);
304
175
  }
305
176
  /** 延续短语白名单(归一化后精确匹配;短输入再放宽到前缀匹配)。 */
306
177
  const CONTINUATION_PHRASES = new Set([
@@ -322,49 +193,42 @@ const CONTINUATION_PHRASES = new Set([
322
193
  'keep going',
323
194
  'carry on',
324
195
  ]);
325
- /** 新输入是否是「延续上次任务」的指令(仲裁快路径,不花 LLM)。 */
196
+ /** 新输入是否是「延续上次任务」的指令(快路径,不花 LLM)。 */
326
197
  export function isContinuationPhrase(rawGoal) {
327
198
  const norm = normalizeGoal(rawGoal);
328
199
  if (!norm)
329
200
  return false;
330
201
  if (CONTINUATION_PHRASES.has(norm))
331
202
  return true;
332
- // 短输入且以延续词开头(如「继续干活」「continue please」)也算
333
- if (norm.length <= 12 &&
203
+ // 短输入且以延续词开头(「继续干活」「continue please」)也算
204
+ return (norm.length <= 12 &&
334
205
  (norm.startsWith('继续') ||
335
206
  norm.startsWith('接着') ||
336
207
  norm.startsWith('continue') ||
337
- norm.startsWith('resume'))) {
338
- return true;
339
- }
340
- return false;
208
+ norm.startsWith('resume')));
341
209
  }
342
210
  /**
343
- * 构建复合仲裁器(短语快路径 → LLM 单轮仲裁 → AskUserQuestion → 兜底 new)。
344
- * runner 在 state.init 前构建并传入;任何一级抛错都降级到下一级,绝不让仲裁本身 halt 工作流。
211
+ * 构建复合仲裁器。任何一级抛错都降级到下一级 —— **仲裁本身绝不 halt 工作流**。
345
212
  */
346
213
  export function buildTaskArbitrator(deps) {
347
214
  const note = (m) => {
348
215
  deps.notes?.push(m);
349
216
  };
350
217
  return async (actx) => {
351
- // a. 延续短语快路径
352
218
  if (isContinuationPhrase(actx.newGoal)) {
353
219
  note(`任务仲裁:新输入「${truncate(actx.newGoal, 40)}」命中延续短语 → 继续旧任务(目标:${truncate(actx.prevGoal, 60)})`);
354
220
  return 'resume';
355
221
  }
356
- // b. LLM 单轮仲裁
357
222
  try {
358
223
  const verdict = await llmArbitrate(deps, actx);
359
224
  if (verdict !== 'unsure') {
360
- note(`任务仲裁:LLM 判定新输入${verdict === 'resume' ? '是旧任务的延续 → 续跑' : '是全新任务 → 清理旧产物重开'}`);
225
+ note(`任务仲裁:LLM 判定新输入${verdict === 'resume' ? '是旧任务的延续 → 续跑' : '是全新任务 → 归档旧产物重开'}`);
361
226
  return verdict;
362
227
  }
363
228
  }
364
229
  catch {
365
230
  // LLM 不可用 → 降级到提问
366
231
  }
367
- // c. AskUserQuestion(TUI 弹界面;非交互宿主 askBridge 自动选推荐项=全新开始)
368
232
  try {
369
233
  const verdict = await askUserArbitrate(deps, actx);
370
234
  note(`任务仲裁:经用户确认 → ${verdict === 'resume' ? '继续旧任务' : '全新开始'}`);
@@ -373,21 +237,19 @@ export function buildTaskArbitrator(deps) {
373
237
  catch {
374
238
  // 提问被取消/不可用 → 兜底
375
239
  }
376
- // d. 兜底 = 旧红线行为(hash 不等 → new-task),最小惊讶
377
240
  note('任务仲裁:无法判定(LLM 与提问均不可用)→ 按全新任务处理');
378
241
  return 'new';
379
242
  };
380
243
  }
381
- /** LLM 单轮仲裁:旧 goal + 新输入 + 进度摘要 → {verdict, reason}。解析失败按 unsure。 */
244
+ /** LLM 单轮仲裁 → {verdict, reason}。解析失败一律按 unsure,不猜。 */
382
245
  async function llmArbitrate(deps, actx) {
383
246
  const chatFn = deps.chat ?? sdkChat;
384
- const progress = `${actx.completedStepIds.length}/${actx.totalSteps}`;
385
247
  const prompt = [
386
248
  `你是工作流引擎的任务身份仲裁器。同名工作流「${actx.workflowName}」存在一个未完成的旧任务,`,
387
- `用户刚发起了一次新调用,新输入的文本与旧任务目标不同。请判断新输入是「旧任务的延续指令」还是「全新任务」。`,
249
+ '用户刚发起了一次新调用,新输入的文本与旧任务目标不同。请判断新输入是「旧任务的延续指令」还是「全新任务」。',
388
250
  '',
389
251
  `旧任务目标:${truncate(actx.prevGoal, 300)}`,
390
- `旧任务进度:已完成 ${progress} 步${actx.completedStepIds.length ? `(${actx.completedStepIds.join(', ')})` : ''}`,
252
+ `旧任务进度:已完成 ${actx.completedCalls} 次调用`,
391
253
  actx.artifacts.length ? `旧任务产物:${actx.artifacts.join(', ')}` : '',
392
254
  `新输入:${truncate(actx.newGoal, 300)}`,
393
255
  '',
@@ -401,7 +263,12 @@ async function llmArbitrate(deps, actx) {
401
263
  for await (const ev of chatFn({
402
264
  provider: deps.provider,
403
265
  messages: [
404
- { role: 'user', content: prompt, timestamp: Date.now(), id: randomUUID() },
266
+ {
267
+ role: 'user',
268
+ content: prompt,
269
+ timestamp: Date.now(),
270
+ id: randomUUID(),
271
+ },
405
272
  ],
406
273
  tools: [],
407
274
  signal: deps.signal,
@@ -420,20 +287,19 @@ async function llmArbitrate(deps, actx) {
420
287
  }
421
288
  return 'unsure';
422
289
  }
423
- /** 经 AskUserQuestion 问用户(推荐项=全新开始,与兜底语义一致;非交互自动按推荐项)。 */
290
+ /** 问用户。推荐项 = 全新开始(与兜底语义一致);非交互宿主自动按推荐项。 */
424
291
  async function askUserArbitrate(deps, actx) {
425
292
  const exec = deps.executeTool ?? sdkExecuteTool;
426
- const progress = `${actx.completedStepIds.length}/${actx.totalSteps}`;
427
293
  const args = {
428
294
  questions: [
429
295
  {
430
296
  question: `检测到工作流「${actx.workflowName}」有一个未完成的任务(目标:${truncate(actx.prevGoal, 80)},` +
431
- `进度 ${progress} 步)。本次输入「${truncate(actx.newGoal, 80)}」是要继续该任务,还是开始全新任务?`,
297
+ `已完成 ${actx.completedCalls} 次调用)。本次输入「${truncate(actx.newGoal, 80)}」是要继续该任务,还是开始全新任务?`,
432
298
  header: '任务仲裁',
433
299
  options: [
434
300
  {
435
301
  label: '全新开始 (推荐)',
436
- description: '清理旧任务的中间产物,按本次输入重新规划执行',
302
+ description: '归档旧任务的中间产物,按本次输入重新规划执行',
437
303
  },
438
304
  {
439
305
  label: '继续上次任务',
@@ -448,25 +314,3 @@ async function askUserArbitrate(deps, actx) {
448
314
  throw new Error(r.error);
449
315
  return r.content.includes('继续上次任务') ? 'resume' : 'new';
450
316
  }
451
- /**
452
- * 归一化 goal 字符串
453
- *
454
- * 规则:
455
- * - toLowerCase()
456
- * - 去除所有标点符号(保留中文、英文、数字、空格)
457
- * - 去除多余空格(连续空格→单空格)
458
- * - 首尾 trim
459
- * - 截断到 200 字符
460
- */
461
- function normalizeGoal(raw) {
462
- return (raw
463
- .toLowerCase()
464
- // 保留中文、英文、数字、空格,去除其他字符(标点等)
465
- .replace(/[^\p{L}\p{N}\s]/gu, '')
466
- // 连续空格替换为单空格
467
- .replace(/\s+/g, ' ')
468
- // 首尾去空格
469
- .trim()
470
- // 截断到 200 字符
471
- .slice(0, 200));
472
- }