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
@@ -104,8 +104,6 @@ const PREAPPROVED_HOSTS = new Set([
104
104
  'dev.mysql.com',
105
105
  'docs.mongodb.com',
106
106
  'www.mongodb.com',
107
- 'sqlite.org',
108
- 'www.sqlite.org',
109
107
  // 工具类
110
108
  'regex101.com',
111
109
  'ihateregex.io',
@@ -105,8 +105,6 @@ const PREAPPROVED_HOSTS = new Set<string>([
105
105
  'dev.mysql.com',
106
106
  'docs.mongodb.com',
107
107
  'www.mongodb.com',
108
- 'sqlite.org',
109
- 'www.sqlite.org',
110
108
  // 工具类
111
109
  'regex101.com',
112
110
  'ihateregex.io',
@@ -21,6 +21,9 @@
21
21
  * max_results 可选,默认 5(Tavily 上限 20)
22
22
  * search_depth 可选 'basic' | 'advanced',默认 basic
23
23
  * topic 可选 'general' | 'news',默认 general
24
+ * recency 可选自动/相对/本年时间窗口
25
+ * start_date 可选 YYYY-MM-DD 绝对起始日期
26
+ * end_date 可选 YYYY-MM-DD 绝对结束日期
24
27
  *
25
28
  * 输出:
26
29
  * 每条结果一段:
@@ -35,6 +38,16 @@ import { configService } from '../../config/configService.js';
35
38
  import { DEFAULT_MAX_RESULT_SIZE_CHARS } from '../types.js';
36
39
  import { toToolParameters } from '../../utils/zodToJson.js';
37
40
  // ---------------- 1. Zod 输入 schema ----------------
41
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
42
+ function isValidIsoDate(value) {
43
+ if (!ISO_DATE.test(value))
44
+ return false;
45
+ const [year, month, day] = value.split('-').map(Number);
46
+ const date = new Date(Date.UTC(year, month - 1, day));
47
+ return date.getUTCFullYear() === year
48
+ && date.getUTCMonth() === month - 1
49
+ && date.getUTCDate() === day;
50
+ }
38
51
  const inputSchema = z.object({
39
52
  query: z
40
53
  .string()
@@ -56,6 +69,27 @@ const inputSchema = z.object({
56
69
  .enum(['general', 'news'])
57
70
  .optional()
58
71
  .describe('general=通用网页;news=偏新闻源;默认 general'),
72
+ recency: z
73
+ .enum(['auto', 'none', 'day', 'week', 'month', 'year', 'current_year'])
74
+ .optional()
75
+ .describe('时效范围:auto 根据查询识别;none 不限时间;day/week/month/year 为相对窗口;current_year 今年优先且结果不足时扩展到去年'),
76
+ start_date: z
77
+ .string()
78
+ .refine(isValidIsoDate, 'start_date 必须是有效的 YYYY-MM-DD 日期')
79
+ .optional()
80
+ .describe('只返回该日期之后发布或更新的结果;与 end_date 配合使用'),
81
+ end_date: z
82
+ .string()
83
+ .refine(isValidIsoDate, 'end_date 必须是有效的 YYYY-MM-DD 日期')
84
+ .optional()
85
+ .describe('只返回该日期之前发布或更新的结果;与 start_date 配合使用'),
86
+ }).superRefine((input, ctx) => {
87
+ if (input.start_date && input.end_date && input.start_date > input.end_date) {
88
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['end_date'], message: 'end_date 不能早于 start_date' });
89
+ }
90
+ if ((input.start_date || input.end_date) && input.recency && !['auto', 'none'].includes(input.recency)) {
91
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['recency'], message: '绝对日期范围与相对 recency 不能同时使用' });
92
+ }
59
93
  });
60
94
  // ---------------- 2. JSON Schema(由 Zod 自动派生) ----------------
61
95
  const parameters = toToolParameters(inputSchema);
@@ -65,9 +99,62 @@ const description = `Searches the public web through Tavily and returns structur
65
99
  - It does not open or operate a browser. Use returned URLs with WebFetch when full source text is needed.
66
100
  - Results contain title, URL and a text snippet. They are discovery evidence, not a substitute for reading a high-value source in full.
67
101
  - Keep each query focused on one sub-question. When another search is needed, change the keywords, source, or angle instead of repeating the same query.
68
- - Add an explicit date or year only when recency matters; stable concepts and exact names should remain exact.
102
+ - Stable concepts and explicit historical queries remain unfiltered. Use recency="day" for today, and recency="current_year" for current/latest/this-year facts; current-year searches widen to the previous year only when the current-year result set is sparse. recency="auto" is a convenience classifier when the query still contains its temporal wording.
103
+ - Use recency="none" to explicitly disable automatic freshness, or start_date/end_date for an exact historical range.
69
104
  - \`search_depth: "advanced"\` is slower but can return a synthesized answer; \`topic: "news"\` favors news sources.
70
105
  - Requires a configured Tavily API key.`;
106
+ function localIsoDate(now) {
107
+ const parts = new Intl.DateTimeFormat('en-CA', {
108
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
109
+ year: 'numeric', month: '2-digit', day: '2-digit',
110
+ }).formatToParts(now);
111
+ const part = (type) => parts.find((item) => item.type === type)?.value ?? '';
112
+ return `${part('year')}-${part('month')}-${part('day')}`;
113
+ }
114
+ function resolveSearchWindow(input, now) {
115
+ if (input.start_date || input.end_date) {
116
+ return {
117
+ ...(input.start_date ? { startDate: input.start_date } : {}),
118
+ ...(input.end_date ? { endDate: input.end_date } : {}),
119
+ label: `${input.start_date ?? '最早'} 至 ${input.end_date ?? '今天'}`,
120
+ source: 'explicit',
121
+ };
122
+ }
123
+ const requested = input.recency ?? 'auto';
124
+ if (requested === 'none')
125
+ return { source: 'none' };
126
+ const today = localIsoDate(now);
127
+ const year = Number(today.slice(0, 4));
128
+ const makeCurrentYear = (source) => ({
129
+ startDate: `${year}-01-01`, endDate: today,
130
+ currentYearFallback: { expandedStartDate: `${year - 1}-01-01`, minimumResults: 3 },
131
+ label: `${year}-01-01 至 ${today}`,
132
+ source,
133
+ });
134
+ if (requested === 'current_year')
135
+ return makeCurrentYear('explicit');
136
+ const relativeLabel = (range) => ({
137
+ day: '最近一天', week: '最近一周', month: '最近一个月', year: '最近一年',
138
+ })[range];
139
+ if (requested !== 'auto')
140
+ return { timeRange: requested, label: relativeLabel(requested), source: 'explicit' };
141
+ const query = input.query.toLowerCase();
142
+ // An explicit past year means the user is asking about history; do not let a
143
+ // nearby word such as "current comparison" erase that requested period.
144
+ const explicitYears = [...query.matchAll(/(?:19|20)\d{2}/g)].map((match) => Number(match[0]));
145
+ if (explicitYears.some((value) => value !== year))
146
+ return { source: 'none' };
147
+ if (/(?:今天|今日|当天|\btoday\b)/i.test(query))
148
+ return { timeRange: 'day', label: relativeLabel('day'), source: 'auto' };
149
+ if (/(?:本周|这周|最近一周|\bthis week\b|\bpast week\b)/i.test(query))
150
+ return { timeRange: 'week', label: relativeLabel('week'), source: 'auto' };
151
+ if (/(?:本月|这个月|最近一个月|近期|\bthis month\b|\bpast month\b|\brecent(?:ly)?\b)/i.test(query))
152
+ return { timeRange: 'month', label: relativeLabel('month'), source: 'auto' };
153
+ if (/(?:今年|本年|当前|目前|现任|最新|最新版|实时|截至现在|\bthis year\b|\bcurrent\b|\blatest\b|\bnewest\b|\bup[- ]to[- ]date\b)/i.test(query)) {
154
+ return makeCurrentYear('auto');
155
+ }
156
+ return { source: 'none' };
157
+ }
71
158
  // ---------------- 4b. 退避重试 ----------------
72
159
  //
73
160
  // 为什么需要(与单发 fetch 零重试的旧行为对比):WebSearch 是 isConcurrencySafe 工具,
@@ -180,7 +267,7 @@ async function fetchTavilyWithRetry(body, signal) {
180
267
  };
181
268
  }
182
269
  // ---------------- 5. call() ----------------
183
- async function call(input, signal, getApiKey = async () => (await configService.tool('webSearch'))?.apiKey) {
270
+ async function call(input, signal, getApiKey = async () => (await configService.tool('webSearch'))?.apiKey, now = () => new Date()) {
184
271
  // 5a. 取 key
185
272
  const apiKey = await getApiKey();
186
273
  if (!apiKey) {
@@ -190,7 +277,8 @@ async function call(input, signal, getApiKey = async () => (await configService.
190
277
  '请在 KakadeAI 设置 → 工具 → 联网搜索中配置,或在 env 模式提供 TAVILY_API_KEY。',
191
278
  };
192
279
  }
193
- const body = {
280
+ const window = resolveSearchWindow(input, now());
281
+ const baseBody = {
194
282
  api_key: apiKey,
195
283
  // Preserve the exact research query. The prompt layer adds a date only
196
284
  // when recency matters; unconditional mutation hurts exact names,
@@ -202,21 +290,53 @@ async function call(input, signal, getApiKey = async () => (await configService.
202
290
  // 让 advanced 模式自动给 answer;basic 不会有
203
291
  include_answer: input.search_depth === 'advanced',
204
292
  };
293
+ const buildBody = (selected) => ({
294
+ ...baseBody,
295
+ ...(selected.timeRange ? { time_range: selected.timeRange } : {}),
296
+ ...(selected.startDate ? { start_date: selected.startDate } : {}),
297
+ ...(selected.endDate ? { end_date: selected.endDate } : {}),
298
+ });
205
299
  // 5c. 发请求(带退避重试,吸收 dev/免费 key 在并发突发下的 socket 切断 + 429/5xx)
206
- const fetched = await fetchTavilyWithRetry(body, signal);
300
+ const fetched = await fetchTavilyWithRetry(buildBody(window), signal);
207
301
  if (!fetched.ok)
208
302
  return { ok: false, error: fetched.error };
209
- const data = fetched.data;
303
+ let data = fetched.data;
304
+ let effectiveWindow = window;
305
+ let fallbackNote = '';
306
+ const fallback = window.currentYearFallback;
307
+ const minimumResults = Math.min(fallback?.minimumResults ?? 0, input.max_results ?? 5);
308
+ if (fallback && (data.results?.length ?? 0) < minimumResults) {
309
+ const expanded = {
310
+ ...window,
311
+ startDate: fallback.expandedStartDate,
312
+ label: `${fallback.expandedStartDate} 至 ${window.endDate}`,
313
+ };
314
+ const wider = await fetchTavilyWithRetry(buildBody(expanded), signal);
315
+ if (wider.ok) {
316
+ data = wider.data;
317
+ effectiveWindow = expanded;
318
+ fallbackNote = `今年结果不足 ${minimumResults} 条,已自动扩展到上一年。`;
319
+ }
320
+ else if ((data.results?.length ?? 0) === 0) {
321
+ return { ok: false, error: wider.error };
322
+ }
323
+ else {
324
+ fallbackNote = `今年结果不足 ${minimumResults} 条;扩展到上一年的检索失败,以下保留今年结果。`;
325
+ }
326
+ }
210
327
  // 5d. 取结果
211
328
  const results = data.results ?? [];
212
329
  if (results.length === 0) {
213
330
  return {
214
331
  ok: true,
215
- content: `(no results for "${input.query}")`,
332
+ content: `${effectiveWindow.label ? `【检索时间范围】${effectiveWindow.label}\n` : ''}(no results for "${input.query}")`,
216
333
  };
217
334
  }
218
335
  // 5e. 拼输出
219
336
  const parts = [];
337
+ if (effectiveWindow.label) {
338
+ parts.push(`【检索时间范围】${effectiveWindow.label}${fallbackNote ? `;${fallbackNote}` : ''}`);
339
+ }
220
340
  if (data.answer) {
221
341
  parts.push(`【综合回答】\n${data.answer}\n`);
222
342
  }
@@ -234,12 +354,13 @@ async function call(input, signal, getApiKey = async () => (await configService.
234
354
  return { ok: true, content };
235
355
  }
236
356
  // ---------------- 6. 导出 ----------------
237
- export function createWebSearchTool(getApiKey = async () => (await configService.tool('webSearch'))?.apiKey) {
357
+ export function createWebSearchTool(getApiKey = async () => (await configService.tool('webSearch'))?.apiKey, now = () => new Date()) {
238
358
  return {
239
359
  name: 'WebSearch', description, inputSchema, parameters, isReadOnly: true,
240
360
  isConcurrencySafe: true, maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
241
361
  interruptBehavior: () => 'cancel',
242
- call: (input, signal) => call(input, signal, getApiKey),
362
+ call: (input, signal) => call(input, signal, getApiKey, now),
243
363
  };
244
364
  }
245
365
  export const webSearchTool = createWebSearchTool();
366
+ export { resolveSearchWindow as _resolveSearchWindow };
@@ -21,6 +21,9 @@
21
21
  * max_results 可选,默认 5(Tavily 上限 20)
22
22
  * search_depth 可选 'basic' | 'advanced',默认 basic
23
23
  * topic 可选 'general' | 'news',默认 general
24
+ * recency 可选自动/相对/本年时间窗口
25
+ * start_date 可选 YYYY-MM-DD 绝对起始日期
26
+ * end_date 可选 YYYY-MM-DD 绝对结束日期
24
27
  *
25
28
  * 输出:
26
29
  * 每条结果一段:
@@ -39,6 +42,17 @@ import { DEFAULT_MAX_RESULT_SIZE_CHARS } from '../types.js';
39
42
  import { toToolParameters } from '../../utils/zodToJson.js';
40
43
 
41
44
  // ---------------- 1. Zod 输入 schema ----------------
45
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
46
+
47
+ function isValidIsoDate(value: string): boolean {
48
+ if (!ISO_DATE.test(value)) return false;
49
+ const [year, month, day] = value.split('-').map(Number);
50
+ const date = new Date(Date.UTC(year!, month! - 1, day));
51
+ return date.getUTCFullYear() === year
52
+ && date.getUTCMonth() === month! - 1
53
+ && date.getUTCDate() === day;
54
+ }
55
+
42
56
  const inputSchema = z.object({
43
57
  query: z
44
58
  .string()
@@ -60,6 +74,27 @@ const inputSchema = z.object({
60
74
  .enum(['general', 'news'])
61
75
  .optional()
62
76
  .describe('general=通用网页;news=偏新闻源;默认 general'),
77
+ recency: z
78
+ .enum(['auto', 'none', 'day', 'week', 'month', 'year', 'current_year'])
79
+ .optional()
80
+ .describe('时效范围:auto 根据查询识别;none 不限时间;day/week/month/year 为相对窗口;current_year 今年优先且结果不足时扩展到去年'),
81
+ start_date: z
82
+ .string()
83
+ .refine(isValidIsoDate, 'start_date 必须是有效的 YYYY-MM-DD 日期')
84
+ .optional()
85
+ .describe('只返回该日期之后发布或更新的结果;与 end_date 配合使用'),
86
+ end_date: z
87
+ .string()
88
+ .refine(isValidIsoDate, 'end_date 必须是有效的 YYYY-MM-DD 日期')
89
+ .optional()
90
+ .describe('只返回该日期之前发布或更新的结果;与 start_date 配合使用'),
91
+ }).superRefine((input, ctx) => {
92
+ if (input.start_date && input.end_date && input.start_date > input.end_date) {
93
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['end_date'], message: 'end_date 不能早于 start_date' });
94
+ }
95
+ if ((input.start_date || input.end_date) && input.recency && !['auto', 'none'].includes(input.recency)) {
96
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['recency'], message: '绝对日期范围与相对 recency 不能同时使用' });
97
+ }
63
98
  });
64
99
  type WebSearchInput = z.infer<typeof inputSchema>;
65
100
 
@@ -72,7 +107,8 @@ const description = `Searches the public web through Tavily and returns structur
72
107
  - It does not open or operate a browser. Use returned URLs with WebFetch when full source text is needed.
73
108
  - Results contain title, URL and a text snippet. They are discovery evidence, not a substitute for reading a high-value source in full.
74
109
  - Keep each query focused on one sub-question. When another search is needed, change the keywords, source, or angle instead of repeating the same query.
75
- - Add an explicit date or year only when recency matters; stable concepts and exact names should remain exact.
110
+ - Stable concepts and explicit historical queries remain unfiltered. Use recency="day" for today, and recency="current_year" for current/latest/this-year facts; current-year searches widen to the previous year only when the current-year result set is sparse. recency="auto" is a convenience classifier when the query still contains its temporal wording.
111
+ - Use recency="none" to explicitly disable automatic freshness, or start_date/end_date for an exact historical range.
76
112
  - \`search_depth: "advanced"\` is slower but can return a synthesized answer; \`topic: "news"\` favors news sources.
77
113
  - Requires a configured Tavily API key.`;
78
114
 
@@ -90,6 +126,65 @@ interface TavilyResponse {
90
126
  results?: TavilyResult[];
91
127
  }
92
128
 
129
+ type RelativeRange = 'day' | 'week' | 'month' | 'year';
130
+ type SearchWindow = {
131
+ timeRange?: RelativeRange;
132
+ startDate?: string;
133
+ endDate?: string;
134
+ currentYearFallback?: { expandedStartDate: string; minimumResults: number };
135
+ label?: string;
136
+ source: 'none' | 'auto' | 'explicit';
137
+ };
138
+
139
+ function localIsoDate(now: Date): string {
140
+ const parts = new Intl.DateTimeFormat('en-CA', {
141
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
142
+ year: 'numeric', month: '2-digit', day: '2-digit',
143
+ }).formatToParts(now);
144
+ const part = (type: Intl.DateTimeFormatPartTypes): string =>
145
+ parts.find((item) => item.type === type)?.value ?? '';
146
+ return `${part('year')}-${part('month')}-${part('day')}`;
147
+ }
148
+
149
+ function resolveSearchWindow(input: WebSearchInput, now: Date): SearchWindow {
150
+ if (input.start_date || input.end_date) {
151
+ return {
152
+ ...(input.start_date ? { startDate: input.start_date } : {}),
153
+ ...(input.end_date ? { endDate: input.end_date } : {}),
154
+ label: `${input.start_date ?? '最早'} 至 ${input.end_date ?? '今天'}`,
155
+ source: 'explicit',
156
+ };
157
+ }
158
+ const requested = input.recency ?? 'auto';
159
+ if (requested === 'none') return { source: 'none' };
160
+ const today = localIsoDate(now);
161
+ const year = Number(today.slice(0, 4));
162
+ const makeCurrentYear = (source: SearchWindow['source']): SearchWindow => ({
163
+ startDate: `${year}-01-01`, endDate: today,
164
+ currentYearFallback: { expandedStartDate: `${year - 1}-01-01`, minimumResults: 3 },
165
+ label: `${year}-01-01 至 ${today}`,
166
+ source,
167
+ });
168
+ if (requested === 'current_year') return makeCurrentYear('explicit');
169
+ const relativeLabel = (range: RelativeRange): string => ({
170
+ day: '最近一天', week: '最近一周', month: '最近一个月', year: '最近一年',
171
+ })[range];
172
+ if (requested !== 'auto') return { timeRange: requested, label: relativeLabel(requested), source: 'explicit' };
173
+
174
+ const query = input.query.toLowerCase();
175
+ // An explicit past year means the user is asking about history; do not let a
176
+ // nearby word such as "current comparison" erase that requested period.
177
+ const explicitYears = [...query.matchAll(/(?:19|20)\d{2}/g)].map((match) => Number(match[0]));
178
+ if (explicitYears.some((value) => value !== year)) return { source: 'none' };
179
+ if (/(?:今天|今日|当天|\btoday\b)/i.test(query)) return { timeRange: 'day', label: relativeLabel('day'), source: 'auto' };
180
+ if (/(?:本周|这周|最近一周|\bthis week\b|\bpast week\b)/i.test(query)) return { timeRange: 'week', label: relativeLabel('week'), source: 'auto' };
181
+ if (/(?:本月|这个月|最近一个月|近期|\bthis month\b|\bpast month\b|\brecent(?:ly)?\b)/i.test(query)) return { timeRange: 'month', label: relativeLabel('month'), source: 'auto' };
182
+ if (/(?:今年|本年|当前|目前|现任|最新|最新版|实时|截至现在|\bthis year\b|\bcurrent\b|\blatest\b|\bnewest\b|\bup[- ]to[- ]date\b)/i.test(query)) {
183
+ return makeCurrentYear('auto');
184
+ }
185
+ return { source: 'none' };
186
+ }
187
+
93
188
  // ---------------- 4b. 退避重试 ----------------
94
189
  //
95
190
  // 为什么需要(与单发 fetch 零重试的旧行为对比):WebSearch 是 isConcurrencySafe 工具,
@@ -215,6 +310,7 @@ async function call(
215
310
  input: WebSearchInput,
216
311
  signal?: AbortSignal,
217
312
  getApiKey: () => Promise<string | undefined> = async () => (await configService.tool('webSearch'))?.apiKey,
313
+ now: () => Date = () => new Date(),
218
314
  ): Promise<ToolResult> {
219
315
  // 5a. 取 key
220
316
  const apiKey = await getApiKey();
@@ -227,7 +323,8 @@ async function call(
227
323
  };
228
324
  }
229
325
 
230
- const body = {
326
+ const window = resolveSearchWindow(input, now());
327
+ const baseBody = {
231
328
  api_key: apiKey,
232
329
  // Preserve the exact research query. The prompt layer adds a date only
233
330
  // when recency matters; unconditional mutation hurts exact names,
@@ -240,22 +337,54 @@ async function call(
240
337
  include_answer: input.search_depth === 'advanced',
241
338
  };
242
339
 
340
+ const buildBody = (selected: SearchWindow): Record<string, unknown> => ({
341
+ ...baseBody,
342
+ ...(selected.timeRange ? { time_range: selected.timeRange } : {}),
343
+ ...(selected.startDate ? { start_date: selected.startDate } : {}),
344
+ ...(selected.endDate ? { end_date: selected.endDate } : {}),
345
+ });
346
+
243
347
  // 5c. 发请求(带退避重试,吸收 dev/免费 key 在并发突发下的 socket 切断 + 429/5xx)
244
- const fetched = await fetchTavilyWithRetry(body, signal);
348
+ const fetched = await fetchTavilyWithRetry(buildBody(window), signal);
245
349
  if (!fetched.ok) return { ok: false, error: fetched.error };
246
- const data = fetched.data;
350
+ let data = fetched.data;
351
+ let effectiveWindow = window;
352
+ let fallbackNote = '';
353
+
354
+ const fallback = window.currentYearFallback;
355
+ const minimumResults = Math.min(fallback?.minimumResults ?? 0, input.max_results ?? 5);
356
+ if (fallback && (data.results?.length ?? 0) < minimumResults) {
357
+ const expanded: SearchWindow = {
358
+ ...window,
359
+ startDate: fallback.expandedStartDate,
360
+ label: `${fallback.expandedStartDate} 至 ${window.endDate}`,
361
+ };
362
+ const wider = await fetchTavilyWithRetry(buildBody(expanded), signal);
363
+ if (wider.ok) {
364
+ data = wider.data;
365
+ effectiveWindow = expanded;
366
+ fallbackNote = `今年结果不足 ${minimumResults} 条,已自动扩展到上一年。`;
367
+ } else if ((data.results?.length ?? 0) === 0) {
368
+ return { ok: false, error: wider.error };
369
+ } else {
370
+ fallbackNote = `今年结果不足 ${minimumResults} 条;扩展到上一年的检索失败,以下保留今年结果。`;
371
+ }
372
+ }
247
373
 
248
374
  // 5d. 取结果
249
375
  const results = data.results ?? [];
250
376
  if (results.length === 0) {
251
377
  return {
252
378
  ok: true,
253
- content: `(no results for "${input.query}")`,
379
+ content: `${effectiveWindow.label ? `【检索时间范围】${effectiveWindow.label}\n` : ''}(no results for "${input.query}")`,
254
380
  };
255
381
  }
256
382
 
257
383
  // 5e. 拼输出
258
384
  const parts: string[] = [];
385
+ if (effectiveWindow.label) {
386
+ parts.push(`【检索时间范围】${effectiveWindow.label}${fallbackNote ? `;${fallbackNote}` : ''}`);
387
+ }
259
388
  if (data.answer) {
260
389
  parts.push(`【综合回答】\n${data.answer}\n`);
261
390
  }
@@ -280,13 +409,16 @@ async function call(
280
409
  // ---------------- 6. 导出 ----------------
281
410
  export function createWebSearchTool(
282
411
  getApiKey: () => Promise<string | undefined> = async () => (await configService.tool('webSearch'))?.apiKey,
412
+ now: () => Date = () => new Date(),
283
413
  ): Tool<WebSearchInput> {
284
414
  return {
285
415
  name: 'WebSearch', description, inputSchema, parameters, isReadOnly: true,
286
416
  isConcurrencySafe: true, maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
287
417
  interruptBehavior: () => 'cancel',
288
- call: (input, signal) => call(input, signal, getApiKey),
418
+ call: (input, signal) => call(input, signal, getApiKey, now),
289
419
  };
290
420
  }
291
421
 
292
422
  export const webSearchTool = createWebSearchTool();
423
+
424
+ export { resolveSearchWindow as _resolveSearchWindow };