minimal-agent 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/README.md +6 -5
  2. package/package.json +62 -5
  3. package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
  4. package/plugins/cron-runner/src/commands.js +4 -4
  5. package/plugins/cron-runner/src/commands.ts +4 -4
  6. package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
  7. package/plugins/workflow-runner/commands/workflow.md +57 -90
  8. package/plugins/workflow-runner/commands/workflows.md +4 -4
  9. package/plugins/workflow-runner/plugin.js +24 -13
  10. package/plugins/workflow-runner/plugin.ts +28 -13
  11. package/plugins/workflow-runner/src/discovery.js +160 -0
  12. package/plugins/workflow-runner/src/discovery.ts +194 -0
  13. package/plugins/workflow-runner/src/goalAnchor.js +53 -0
  14. package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
  15. package/plugins/workflow-runner/src/index.js +204 -185
  16. package/plugins/workflow-runner/src/index.ts +249 -224
  17. package/plugins/workflow-runner/src/inputCoercion.js +40 -0
  18. package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
  19. package/plugins/workflow-runner/src/journal.js +224 -0
  20. package/plugins/workflow-runner/src/journal.ts +274 -0
  21. package/plugins/workflow-runner/src/jsonSchema.js +132 -0
  22. package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
  23. package/plugins/workflow-runner/src/moduleResolution.js +64 -0
  24. package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
  25. package/plugins/workflow-runner/src/resourceInputs.js +220 -0
  26. package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
  27. package/plugins/workflow-runner/src/runner.js +404 -467
  28. package/plugins/workflow-runner/src/runner.ts +491 -535
  29. package/plugins/workflow-runner/src/runtime/config.js +46 -0
  30. package/plugins/workflow-runner/src/runtime/config.ts +69 -0
  31. package/plugins/workflow-runner/src/runtime/context.js +1304 -0
  32. package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
  33. package/plugins/workflow-runner/src/runtime/exec.js +34 -0
  34. package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
  35. package/plugins/workflow-runner/src/runtime/flow.js +8 -0
  36. package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
  37. package/plugins/workflow-runner/src/runtime/fs.js +27 -0
  38. package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
  39. package/plugins/workflow-runner/src/runtime/model.js +6 -0
  40. package/plugins/workflow-runner/src/runtime/model.ts +8 -0
  41. package/plugins/workflow-runner/src/runtime/output.js +7 -0
  42. package/plugins/workflow-runner/src/runtime/output.ts +21 -0
  43. package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
  44. package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
  45. package/plugins/workflow-runner/src/runtime/state.js +16 -0
  46. package/plugins/workflow-runner/src/runtime/state.ts +19 -0
  47. package/plugins/workflow-runner/src/runtime/tools.js +3 -0
  48. package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
  49. package/plugins/workflow-runner/src/sandbox.js +141 -0
  50. package/plugins/workflow-runner/src/sandbox.ts +182 -0
  51. package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
  52. package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
  53. package/plugins/workflow-runner/src/taskIdentity.js +172 -152
  54. package/plugins/workflow-runner/src/taskIdentity.ts +233 -200
  55. package/plugins/workflow-runner/src/toolRuntime.js +29 -8
  56. package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
  57. package/plugins/workflow-runner/src/types.js +7 -105
  58. package/plugins/workflow-runner/src/types.ts +155 -520
  59. package/plugins/workflow-runner/src/util.js +47 -0
  60. package/plugins/workflow-runner/src/util.ts +51 -0
  61. package/plugins/workflow-runner/src/workflowContext.js +20 -0
  62. package/plugins/workflow-runner/src/workflowContext.ts +58 -0
  63. package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
  64. package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
  65. package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
  66. package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
  67. package/plugins/workflow-runner/src/workflowState.js +542 -469
  68. package/plugins/workflow-runner/src/workflowState.ts +639 -589
  69. package/plugins/workflow-runner/test/api.test.ts +223 -0
  70. package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
  71. package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
  72. package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
  73. package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
  74. package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
  75. package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
  76. package/plugins/workflow-runner/test/discovery.test.ts +234 -0
  77. package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
  78. package/plugins/workflow-runner/test/goalController.test.ts +317 -0
  79. package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
  80. package/plugins/workflow-runner/test/listResources.test.ts +106 -0
  81. package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
  82. package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
  83. package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
  84. package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
  85. package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
  86. package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
  87. package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
  88. package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
  89. package/skills/pptx/examples/assets/cover-bg.svg +23 -0
  90. package/skills/pptx/examples/assets/icon-spark.svg +5 -0
  91. package/skills/skill-creator/scripts/run_eval.py +3 -2
  92. package/skills/ui-design/SKILL.md +5 -2
  93. package/skills/ui-design/references/layout-composition.md +1 -1
  94. package/skills/workflow-creator/SKILL.md +344 -0
  95. package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
  96. package/skills/workflow-creator/references/api.md +216 -0
  97. package/skills/workflow-creator/references/migration.md +103 -0
  98. package/skills/workflow-creator/references/patterns.md +779 -0
  99. package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
  100. package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
  101. package/skills/workflow-creator/scripts/validate.mjs +275 -0
  102. package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
  103. package/src/advisor/advisorCoordinator.js +503 -0
  104. package/src/advisor/advisorCoordinator.ts +657 -0
  105. package/src/cli/configMenu.tsx +0 -1
  106. package/src/cli/print.js +44 -6
  107. package/src/cli/print.ts +73 -4
  108. package/src/cli/streamJson.js +5 -6
  109. package/src/cli/streamJson.ts +8 -4
  110. package/src/cli/wizards/advisorWizard.js +4 -2
  111. package/src/cli/wizards/advisorWizard.tsx +8 -2
  112. package/src/cli/wizards/contextAdvisorWizard.js +5 -4
  113. package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
  114. package/src/collaboration/taskFlow.js +115 -0
  115. package/src/collaboration/taskFlow.ts +169 -0
  116. package/src/config/configDefaults.js +41 -1
  117. package/src/config/configDefaults.ts +43 -1
  118. package/src/config/configDiagnostics.ts +2 -2
  119. package/src/config/configFile.js +40 -9
  120. package/src/config/configFile.ts +60 -11
  121. package/src/config/configService.js +443 -74
  122. package/src/config/configService.ts +723 -142
  123. package/src/config/numericLimits.js +22 -0
  124. package/src/config/numericLimits.ts +24 -0
  125. package/src/config.js +1 -0
  126. package/src/config.ts +3 -2
  127. package/src/context/budgetPlan.js +1 -1
  128. package/src/context/budgetPlan.ts +1 -1
  129. package/src/context/compact.js +7 -2
  130. package/src/context/compact.ts +9 -2
  131. package/src/context/microCompactLite.js +212 -13
  132. package/src/context/microCompactLite.ts +261 -12
  133. package/src/context/sessionRegistry.js +8 -1
  134. package/src/context/sessionRegistry.ts +18 -5
  135. package/src/context/toolResultArchive.js +72 -0
  136. package/src/context/toolResultArchive.ts +95 -0
  137. package/src/goal/errors.js +8 -0
  138. package/src/goal/errors.ts +18 -0
  139. package/src/goal/goalRuntime.js +344 -0
  140. package/src/goal/goalRuntime.ts +466 -0
  141. package/src/goal/goalStore.js +200 -0
  142. package/src/goal/goalStore.ts +219 -0
  143. package/src/goal/index.js +4 -0
  144. package/src/goal/index.ts +4 -0
  145. package/src/goal/statusMapping.js +71 -0
  146. package/src/goal/statusMapping.ts +95 -0
  147. package/src/goal/types.js +7 -0
  148. package/src/goal/types.ts +179 -0
  149. package/src/goal/workflowController.js +306 -0
  150. package/src/goal/workflowController.ts +360 -0
  151. package/src/harness/evidenceLedger.js +2 -0
  152. package/src/harness/evidenceLedger.ts +1 -0
  153. package/src/harness/stallGuard.js +9 -3
  154. package/src/harness/stallGuard.ts +7 -3
  155. package/src/images/imageResolver.js +161 -0
  156. package/src/images/imageResolver.ts +189 -0
  157. package/src/infra/verification.js +56 -20
  158. package/src/infra/verification.ts +87 -20
  159. package/src/llm/client.js +79 -21
  160. package/src/llm/client.ts +182 -35
  161. package/src/llm/gatewayClient.js +201 -0
  162. package/src/llm/gatewayClient.ts +299 -0
  163. package/src/llm/messageProvenance.js +46 -0
  164. package/src/llm/messageProvenance.ts +58 -0
  165. package/src/llm/modelProvider.js +1 -0
  166. package/src/llm/modelProvider.ts +22 -0
  167. package/src/llm/providers/anthropicFormat.js +108 -1
  168. package/src/llm/providers/anthropicFormat.ts +115 -4
  169. package/src/llm/providers/openaiFormat.js +9 -0
  170. package/src/llm/providers/openaiFormat.ts +12 -3
  171. package/src/loop.js +319 -54
  172. package/src/loop.ts +373 -57
  173. package/src/main.js +4 -1
  174. package/src/main.tsx +4 -1
  175. package/src/managedExecution/index.js +2 -0
  176. package/src/managedExecution/index.ts +2 -0
  177. package/src/managedExecution/registry.js +15 -0
  178. package/src/managedExecution/registry.ts +20 -0
  179. package/src/managedExecution/types.js +1 -0
  180. package/src/managedExecution/types.ts +86 -0
  181. package/src/modelRef.js +9 -0
  182. package/src/modelRef.ts +31 -0
  183. package/src/multimodal.js +14 -28
  184. package/src/multimodal.ts +17 -33
  185. package/src/net/lanDiscovery.js +65 -0
  186. package/src/net/lanDiscovery.ts +82 -0
  187. package/src/plugin-sdk.js +28 -3
  188. package/src/plugin-sdk.ts +39 -6
  189. package/src/plugins/pluginApi.ts +16 -1
  190. package/src/plugins/pluginLoader.js +4 -0
  191. package/src/plugins/pluginLoader.ts +6 -0
  192. package/src/plugins/pluginRunner.js +152 -9
  193. package/src/plugins/pluginRunner.ts +186 -8
  194. package/src/plugins/slashFallback.js +113 -0
  195. package/src/plugins/slashFallback.ts +137 -0
  196. package/src/prompts/policyRegistry.js +15 -8
  197. package/src/prompts/policyRegistry.ts +18 -8
  198. package/src/prompts/promptAssembly.js +103 -12
  199. package/src/prompts/promptAssembly.ts +114 -12
  200. package/src/prompts/skillList.js +21 -3
  201. package/src/prompts/skillList.ts +27 -2
  202. package/src/prompts/stableSystemPrompt.js +18 -0
  203. package/src/prompts/stableSystemPrompt.ts +33 -0
  204. package/src/runtime/agentRuntimeContext.js +8 -2
  205. package/src/runtime/agentRuntimeContext.ts +17 -4
  206. package/src/session/runAgentTurn.js +78 -12
  207. package/src/session/runAgentTurn.ts +105 -23
  208. package/src/session/runTurn.js +10 -9
  209. package/src/session/runTurn.ts +17 -8
  210. package/src/session/sessionState.ts +2 -0
  211. package/src/team/auth.js +186 -0
  212. package/src/team/auth.ts +298 -0
  213. package/src/team/cli.js +105 -0
  214. package/src/team/cli.ts +122 -0
  215. package/src/team/onlineToolContract.js +64 -0
  216. package/src/team/onlineToolContract.ts +109 -0
  217. package/src/team/toolPolicy.js +36 -0
  218. package/src/team/toolPolicy.ts +75 -0
  219. package/src/tools/advisor/advisor.js +148 -37
  220. package/src/tools/advisor/advisor.ts +161 -38
  221. package/src/tools/advisor/advisorDecision.js +71 -0
  222. package/src/tools/advisor/advisorDecision.ts +85 -0
  223. package/src/tools/agent/agent.js +18 -5
  224. package/src/tools/agent/agent.ts +21 -7
  225. package/src/tools/agent/agentControl.js +246 -0
  226. package/src/tools/agent/agentControl.ts +284 -0
  227. package/src/tools/agent/agentTaskService.js +40 -1
  228. package/src/tools/agent/agentTaskService.ts +44 -1
  229. package/src/tools/agent/agentTypes.js +14 -2
  230. package/src/tools/agent/agentTypes.ts +15 -2
  231. package/src/tools/applypatch/applyPatch.js +362 -0
  232. package/src/tools/applypatch/applyPatch.ts +428 -0
  233. package/src/tools/bash/bash.js +70 -18
  234. package/src/tools/bash/bash.ts +75 -20
  235. package/src/tools/capabilities.js +10 -5
  236. package/src/tools/capabilities.ts +10 -5
  237. package/src/tools/computeruse/computerIntent.js +32 -0
  238. package/src/tools/computeruse/computerIntent.ts +46 -0
  239. package/src/tools/computeruse/telemetryLog.js +12 -0
  240. package/src/tools/computeruse/telemetryLog.ts +16 -0
  241. package/src/tools/computeruse/vision/telemetry.js +1 -0
  242. package/src/tools/computeruse/vision/telemetry.ts +1 -0
  243. package/src/tools/effectiveToolSet.js +4 -0
  244. package/src/tools/effectiveToolSet.ts +4 -0
  245. package/src/tools/index.js +21 -2
  246. package/src/tools/index.ts +33 -2
  247. package/src/tools/powershell/powershell.js +18 -1
  248. package/src/tools/powershell/powershell.ts +18 -2
  249. package/src/tools/presentresult/presentresult.js +6 -15
  250. package/src/tools/presentresult/presentresult.ts +6 -15
  251. package/src/tools/read/read.js +69 -11
  252. package/src/tools/read/read.ts +70 -11
  253. package/src/tools/shared/fileUtils.js +1 -2
  254. package/src/tools/shared/fileUtils.ts +1 -2
  255. package/src/tools/shared/permanentDeleteGuard.js +34 -3
  256. package/src/tools/shared/permanentDeleteGuard.ts +43 -6
  257. package/src/tools/shared/toolOutputStore.js +23 -1
  258. package/src/tools/shared/toolOutputStore.ts +22 -1
  259. package/src/tools/skill/skill.js +9 -3
  260. package/src/tools/skill/skill.ts +8 -4
  261. package/src/tools/toolPromptGuides.js +117 -0
  262. package/src/tools/toolPromptGuides.ts +137 -0
  263. package/src/tools/webbrowser/agentBrowserPath.js +19 -9
  264. package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
  265. package/src/tools/webbrowser/browser.js +27 -11
  266. package/src/tools/webbrowser/browser.ts +68 -24
  267. package/src/tools/webbrowser/browserAutomation.js +329 -0
  268. package/src/tools/webbrowser/browserAutomation.ts +467 -0
  269. package/src/tools/webbrowser/browserAxState.js +97 -0
  270. package/src/tools/webbrowser/browserAxState.ts +142 -0
  271. package/src/tools/webbrowser/browserBatch.js +1578 -0
  272. package/src/tools/webbrowser/browserBatch.ts +2382 -0
  273. package/src/tools/webbrowser/browserCdp.js +208 -0
  274. package/src/tools/webbrowser/browserCdp.ts +255 -0
  275. package/src/tools/webbrowser/browserConditions.js +24 -0
  276. package/src/tools/webbrowser/browserConditions.ts +45 -0
  277. package/src/tools/webbrowser/browserContent.js +281 -0
  278. package/src/tools/webbrowser/browserContent.ts +438 -0
  279. package/src/tools/webbrowser/browserControl.js +131 -30
  280. package/src/tools/webbrowser/browserControl.ts +210 -50
  281. package/src/tools/webbrowser/browserDaemon.js +22 -14
  282. package/src/tools/webbrowser/browserDaemon.ts +54 -21
  283. package/src/tools/webbrowser/browserFrames.js +47 -0
  284. package/src/tools/webbrowser/browserFrames.ts +54 -0
  285. package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
  286. package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
  287. package/src/tools/webbrowser/browserLocator.js +140 -0
  288. package/src/tools/webbrowser/browserLocator.ts +194 -0
  289. package/src/tools/webbrowser/browserOutcome.js +10 -17
  290. package/src/tools/webbrowser/browserOutcome.ts +48 -31
  291. package/src/tools/webbrowser/browserRecovery.js +58 -0
  292. package/src/tools/webbrowser/browserRecovery.ts +83 -0
  293. package/src/tools/webbrowser/browserRunRegistry.js +35 -3
  294. package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
  295. package/src/tools/webbrowser/browserService.js +28 -2
  296. package/src/tools/webbrowser/browserService.ts +49 -9
  297. package/src/tools/webbrowser/browserStream.js +132 -33
  298. package/src/tools/webbrowser/browserStream.ts +224 -50
  299. package/src/tools/webbrowser/browserTabs.js +48 -15
  300. package/src/tools/webbrowser/browserTabs.ts +178 -96
  301. package/src/tools/webbrowser/webbrowser.js +683 -144
  302. package/src/tools/webbrowser/webbrowser.ts +979 -236
  303. package/src/tools/webfetch/preapproved.js +0 -2
  304. package/src/tools/webfetch/preapproved.ts +0 -2
  305. package/src/tools/websearch/websearch.js +129 -8
  306. package/src/tools/websearch/websearch.ts +138 -6
  307. package/src/types.ts +450 -42
  308. package/src/ui/App.js +377 -58
  309. package/src/ui/App.tsx +576 -111
  310. package/src/ui/LiveArea.js +14 -8
  311. package/src/ui/LiveArea.tsx +14 -10
  312. package/src/ui/MessageList.js +63 -20
  313. package/src/ui/MessageList.tsx +101 -16
  314. package/src/ui/ModelPicker.js +7 -4
  315. package/src/ui/ModelPicker.tsx +55 -8
  316. package/src/ui/hooks/useChat.js +152 -163
  317. package/src/ui/hooks/useChat.ts +396 -334
  318. package/src/ui/loginCommand.js +56 -0
  319. package/src/ui/loginCommand.ts +63 -0
  320. package/src/ui/toolProgress.js +41 -0
  321. package/src/ui/toolProgress.ts +44 -0
  322. package/src/web/browserBridge.js +20 -2
  323. package/src/web/browserBridge.ts +20 -2
  324. package/src/web/protocol.ts +1897 -194
  325. package/workflows/adaptive-research/workflow.mjs +86 -0
  326. package/workflows/e2e-capabilities/workflow.mjs +102 -0
  327. package/workflows/e2e-llm/workflow.mjs +52 -0
  328. package/workflows/e2e-smoke/workflow.mjs +59 -0
  329. package/workflows/e2e-vision-schema/README.md +48 -0
  330. package/workflows/e2e-vision-schema/workflow.mjs +149 -0
  331. package/workflows/e2e-write-greet/workflow.mjs +37 -0
  332. package/workflows/file-census/README.md +14 -0
  333. package/workflows/file-census/scripts/.gitkeep +2 -0
  334. package/workflows/file-census/workflow.mjs +110 -0
  335. package/workflows/goal/README.md +166 -0
  336. package/workflows/goal/prompts/audit.md +57 -0
  337. package/workflows/goal/prompts/contract.md +56 -0
  338. package/workflows/goal/prompts/iterate.md +37 -0
  339. package/workflows/goal/workflow.mjs +501 -0
  340. package/workflows/illustrate/README.md +90 -0
  341. package/workflows/illustrate/assets/engines.json +20 -0
  342. package/workflows/illustrate/assets/extract-baidu.js +21 -0
  343. package/workflows/illustrate/assets/extract-bing.js +33 -0
  344. package/workflows/illustrate/assets/extract-google.js +31 -0
  345. package/workflows/illustrate/workflow.mjs +301 -0
  346. package/workflows/plan/workflow.mjs +389 -0
  347. package/workflows/question-crops/README.md +35 -0
  348. package/workflows/question-crops/prompts/inventory.md +23 -0
  349. package/workflows/question-crops/scripts/image-ops.mjs +305 -0
  350. package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
  351. package/workflows/question-crops/workflow.mjs +172 -0
  352. package/workflows/ralph-loop/workflow.mjs +162 -0
  353. package/workflows/research/DESIGN.md +449 -0
  354. package/workflows/research/README.md +74 -0
  355. package/workflows/research/prompts/s0-goal.md +43 -0
  356. package/workflows/research/prompts/s1-early-exit.md +22 -0
  357. package/workflows/research/prompts/s1-map.md +58 -0
  358. package/workflows/research/prompts/s2-options.md +53 -0
  359. package/workflows/research/prompts/s3-deep-dive.md +56 -0
  360. package/workflows/research/prompts/s4-verify.md +74 -0
  361. package/workflows/research/prompts/s5-synthesis.md +81 -0
  362. package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
  363. package/workflows/research/prompts/s6-package.md +32 -0
  364. package/workflows/research/scripts/build-report.mjs +98 -0
  365. package/workflows/research/workflow.mjs +288 -0
  366. package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
  367. package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
  368. package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
  369. package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
  370. package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
  371. package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
  372. package/workflows/wechat-reading-shots/workflow.mjs +266 -0
  373. package/workflows/wrong-question-docx/README.md +55 -0
  374. package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
  375. package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
  376. package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
  377. package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
  378. package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
  379. package/workflows/wrong-question-docx/workflow.mjs +485 -0
  380. package/plugins/workflow-runner/src/agentCommon.js +0 -354
  381. package/plugins/workflow-runner/src/agentCommon.ts +0 -435
  382. package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
  383. package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
  384. package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
  385. package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
  386. package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
  387. package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
  388. package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
  389. package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
  390. package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
  391. package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
  392. package/plugins/workflow-runner/src/dslManifest.js +0 -72
  393. package/plugins/workflow-runner/src/dslManifest.ts +0 -127
  394. package/plugins/workflow-runner/src/expressions.js +0 -618
  395. package/plugins/workflow-runner/src/expressions.ts +0 -622
  396. package/plugins/workflow-runner/src/loader.js +0 -573
  397. package/plugins/workflow-runner/src/loader.ts +0 -721
  398. package/plugins/workflow-runner/src/multimodal.js +0 -38
  399. package/plugins/workflow-runner/src/multimodal.ts +0 -64
  400. package/plugins/workflow-runner/src/recovery.js +0 -119
  401. package/plugins/workflow-runner/src/recovery.ts +0 -149
  402. package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
  403. package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
  404. package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -41
  405. package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -50
  406. package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
  407. package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
  408. package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
  409. package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
  410. package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
  411. package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
  412. package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
  413. package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
  414. package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -208
  415. package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -256
  416. package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -58
  417. package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -68
  418. package/plugins/workflow-runner/src/stepPipeline.js +0 -277
  419. package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
  420. package/plugins/workflow-runner/test/agent.test.ts +0 -587
  421. package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
  422. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
  423. package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
  424. package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -147
  425. package/plugins/workflow-runner/test/context-files.test.ts +0 -320
  426. package/plugins/workflow-runner/test/e2e.test.ts +0 -370
  427. package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -226
  428. package/plugins/workflow-runner/test/expressions.test.ts +0 -397
  429. package/plugins/workflow-runner/test/foundation.test.ts +0 -126
  430. package/plugins/workflow-runner/test/graceful.test.ts +0 -139
  431. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
  432. package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
  433. package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
  434. package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
  435. package/plugins/workflow-runner/test/loader.test.ts +0 -617
  436. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
  437. package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
  438. package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
  439. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -87
  440. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
  441. package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
  442. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -220
  443. package/plugins/workflow-runner/test/recovery.test.ts +0 -265
  444. package/plugins/workflow-runner/test/runner.test.ts +0 -911
  445. package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -379
  446. package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
  447. package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -150
  448. package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
  449. package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -338
  450. package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -270
  451. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
  452. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
  453. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
  454. package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
  455. package/plugins/workflow-runner/test/word.load.test.ts +0 -208
  456. package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
  457. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
  458. package/src/advisorState.js +0 -22
  459. package/src/cli/configWizard.js +0 -293
  460. package/src/tools/toolProgressBridge.js +0 -48
  461. package/workflows/adaptive-research.yaml +0 -50
  462. package/workflows/book-review-short.yaml +0 -125
  463. package/workflows/e2e-write-greet.yaml +0 -31
  464. package/workflows/illustrate.yaml +0 -227
  465. package/workflows/plan.yaml +0 -470
  466. package/workflows/ppt.yaml +0 -1670
  467. package/workflows/ralph-loop.yaml +0 -199
  468. package/workflows/schema.json +0 -176
  469. package/workflows/webppt.yaml +0 -496
  470. package/workflows/wechat-reading-shots.yaml +0 -315
  471. package/workflows/word.yaml +0 -720
  472. package/workflows/youtube-shorts.yaml +0 -202
package/src/llm/client.ts CHANGED
@@ -24,7 +24,14 @@
24
24
  * ============================================================
25
25
  */
26
26
 
27
- import type { LlmStreamEvent, Message, Provider, Tool } from '../types.js';
27
+ import type {
28
+ GatewayProvider,
29
+ LlmStreamEvent,
30
+ LocalProvider,
31
+ Message,
32
+ Provider,
33
+ Tool,
34
+ } from '../types.js';
28
35
  import { dlog } from '../utils/debugLog.js';
29
36
  import {
30
37
  buildOpenAIRequest,
@@ -35,7 +42,13 @@ import {
35
42
  isResponseFormatUnsupported,
36
43
  } from './providers/openaiFormat.js';
37
44
  import { promptMaterialHash, shortCacheScope } from './promptCache.js';
38
- import { buildAnthropicRequest, createAnthropicSSEParser } from './providers/anthropicFormat.js';
45
+ import {
46
+ buildAnthropicRequest,
47
+ createAnthropicSSEParser,
48
+ } from './providers/anthropicFormat.js';
49
+ import { chatGateway } from './gatewayClient.js';
50
+ import type { ModelProvider } from './modelProvider.js';
51
+ import { prepareMessagesForProvider } from './messageProvenance.js';
39
52
 
40
53
  /**
41
54
  * 记住"这个 baseURL::model 不支持 response_format.json_schema",避免每次都先撞 400。
@@ -132,7 +145,8 @@ let backoffTier = 0;
132
145
  * retryAfterMs 有值(429 的 Retry-After 头)时听服务器的(仍封顶 60s、仍抬档)。
133
146
  */
134
147
  function nextRetryDelayMs(retryAfterMs?: number): number {
135
- const tierMs = BACKOFF_TIERS_MS[Math.min(backoffTier, BACKOFF_TIERS_MS.length - 1)];
148
+ const tierMs =
149
+ BACKOFF_TIERS_MS[Math.min(backoffTier, BACKOFF_TIERS_MS.length - 1)];
136
150
  backoffTier = Math.min(backoffTier + 1, BACKOFF_TIERS_MS.length - 1);
137
151
  if (retryAfterMs !== undefined) return Math.min(retryAfterMs, 60_000);
138
152
  const jittered = tierMs * (0.85 + Math.random() * 0.3);
@@ -158,7 +172,12 @@ export function _nextRetryDelayMs(retryAfterMs?: number): number {
158
172
 
159
173
  /** 可重试的 HTTP 状态码。4xx 除 408/425/429 外不重试(重试无意义)。 */
160
174
  function isRetryableStatus(status: number): boolean {
161
- return status === 408 || status === 425 || status === 429 || (status >= 500 && status < 600);
175
+ return (
176
+ status === 408 ||
177
+ status === 425 ||
178
+ status === 429 ||
179
+ (status >= 500 && status < 600)
180
+ );
162
181
  }
163
182
 
164
183
  /** 鉴权类错误:明确告诉用户"key 失效/过期/无权访问",避免重试无意义。 */
@@ -253,7 +272,9 @@ export function createIdleWatchdog(
253
272
  };
254
273
  arm();
255
274
  return {
256
- signal: userSignal ? AbortSignal.any([userSignal, ctrl.signal]) : ctrl.signal,
275
+ signal: userSignal
276
+ ? AbortSignal.any([userSignal, ctrl.signal])
277
+ : ctrl.signal,
257
278
  kick: arm,
258
279
  dispose: () => {
259
280
  if (timer) clearTimeout(timer);
@@ -281,8 +302,42 @@ export function createIdleWatchdog(
281
302
  * }
282
303
  * ```
283
304
  */
284
- export async function* chat(args: {
285
- provider: Provider;
305
+ export function createModelProvider(provider: Provider): ModelProvider {
306
+ return {
307
+ source: provider.source === 'gateway' ? 'gateway' : 'local',
308
+ descriptor: provider,
309
+ stream: (request) => {
310
+ if (provider.source === 'gateway') {
311
+ if (
312
+ request.responseFormat &&
313
+ provider.gatewayCapabilities?.structuredOutput === false
314
+ ) {
315
+ throw new Error('当前在线模型不支持结构化输出');
316
+ }
317
+ return chatGateway({
318
+ provider: provider as GatewayProvider,
319
+ messages: request.messages,
320
+ tools: request.tools,
321
+ signal: request.signal,
322
+ conversationMode: request.conversationMode,
323
+ responseFormat: request.responseFormat,
324
+ });
325
+ }
326
+ return streamLocal({ ...request, provider: provider as LocalProvider });
327
+ },
328
+ };
329
+ }
330
+
331
+ export async function* chat(
332
+ args: Omit<Parameters<typeof streamLocal>[0], 'provider'> & {
333
+ provider: Provider;
334
+ },
335
+ ): AsyncGenerator<LlmStreamEvent, void, void> {
336
+ yield* createModelProvider(args.provider).stream(args);
337
+ }
338
+
339
+ async function* streamLocal(args: {
340
+ provider: LocalProvider;
286
341
  messages: Message[];
287
342
  tools: Tool[];
288
343
  signal?: AbortSignal;
@@ -292,12 +347,36 @@ export async function* chat(args: {
292
347
  * 传了会在发请求前直接抛错,不浪费一次网络往返。*/
293
348
  responseFormat?: {
294
349
  type: 'json_schema';
295
- json_schema: { name: string; schema: Record<string, unknown>; strict?: boolean };
350
+ json_schema: {
351
+ name: string;
352
+ schema: Record<string, unknown>;
353
+ strict?: boolean;
354
+ };
296
355
  };
297
356
  promptCache?: import('../types.js').PromptCacheContext;
357
+ /** 当前调用所在会话模式;群聊下未标注的旧历史按私聊数据处理。 */
358
+ conversationMode?: import('../runtime/agentRuntimeContext.js').ConversationMode;
359
+ /**
360
+ * cache_edits 式回收状态(Anthropic 格式 + provider.cacheEdits 开启时由 loop 传入)。
361
+ * 传入后 buildAnthropicRequest 会随请求发送删除指令并维护 pinned/pendingNew——
362
+ * 该状态有跨轮生命周期(pinned 每轮重发),调用方必须传同一份会话级对象,
363
+ * 并在 compact 后调 resetCacheEdits。OpenAI 格式忽略此参数。
364
+ */
365
+ cacheEdits?: import('../types.js').CacheEditsState;
366
+ visionPolicy?: 'required' | 'optional-history';
298
367
  }): AsyncGenerator<LlmStreamEvent, void, void> {
299
- const { provider, messages, tools, signal, responseFormat, promptCache } = args;
300
- const format: 'openai' | 'anthropic' = provider.apiFormat === 'anthropic' ? 'anthropic' : 'openai';
368
+ const {
369
+ provider,
370
+ messages,
371
+ tools,
372
+ signal,
373
+ responseFormat,
374
+ promptCache,
375
+ conversationMode,
376
+ cacheEdits,
377
+ } = args;
378
+ const format: 'openai' | 'anthropic' =
379
+ provider.apiFormat === 'anthropic' ? 'anthropic' : 'openai';
301
380
 
302
381
  if (format === 'anthropic' && responseFormat) {
303
382
  throw new Error(
@@ -310,12 +389,10 @@ export async function* chat(args: {
310
389
  // 过滤并清洁 messages 数组,剥离 timestamp, id, _ephemeral 等本地渲染元数据。
311
390
  // 两种格式共用同一份清洗后的内部形状——Anthropic 分支的翻译(system 摘除/
312
391
  // tool_calls→tool_use 等)在 buildAnthropicRequest 内部做,不在这里做。
313
- let cleanedMessages: Message[] = messages
314
- .filter((m) => !(m.role === 'system' && (m as { _ephemeral?: boolean })._ephemeral === true))
315
- .map((m) => {
316
- const { id, timestamp, attachments, browserReferences, display_metadata, ...rest } = m as any;
317
- return rest;
318
- });
392
+ let cleanedMessages: Message[] = prepareMessagesForProvider(
393
+ messages,
394
+ conversationMode ?? 'private',
395
+ );
319
396
 
320
397
  const includeUsage = process.env.MINIMAL_AGENT_DISABLE_USAGE !== '1';
321
398
  const rfKey = `${provider.baseURL}::${provider.model}`;
@@ -323,7 +400,9 @@ export async function* chat(args: {
323
400
  // OpenAI 分支专属的「就地退化」状态;Anthropic 无此概念,两个 flag 恒为 false,
324
401
  // 下面的退化 if 分支天然不会进入(不需要额外的 format 判断散落各处)。
325
402
  let wantResponseFormat =
326
- format === 'openai' && responseFormat !== undefined && !rfUnsupported.has(rfKey);
403
+ format === 'openai' &&
404
+ responseFormat !== undefined &&
405
+ !rfUnsupported.has(rfKey);
327
406
  let wantMaxTokens =
328
407
  format === 'openai' &&
329
408
  typeof provider.maxTokens === 'number' &&
@@ -341,9 +420,15 @@ export async function* chat(args: {
341
420
  const c = (m as { content?: unknown }).content;
342
421
  if (!Array.isArray(c)) return m;
343
422
  const textOnly = c.filter((p: any) => p.type === 'text');
344
- const newContent = textOnly.length > 0
345
- ? textOnly.map((p: any) => ({ type: 'text' as const, text: p.text }))
346
- : [{ type: 'text' as const, text: '[图片内容未发送:当前模型不支持图片输入]' }];
423
+ const newContent =
424
+ textOnly.length > 0
425
+ ? textOnly.map((p: any) => ({ type: 'text' as const, text: p.text }))
426
+ : [
427
+ {
428
+ type: 'text' as const,
429
+ text: '[图片内容未发送:当前模型不支持图片输入]',
430
+ },
431
+ ];
347
432
  return { ...m, content: newContent } as Message;
348
433
  });
349
434
 
@@ -354,7 +439,12 @@ export async function* chat(args: {
354
439
  const postChat = (): Promise<Response> => {
355
440
  const req =
356
441
  format === 'anthropic'
357
- ? buildAnthropicRequest({ provider, cleanedMessages, tools })
442
+ ? buildAnthropicRequest({
443
+ provider,
444
+ cleanedMessages,
445
+ tools,
446
+ ...(cacheEdits ? { cacheEdits } : {}),
447
+ })
358
448
  : buildOpenAIRequest({
359
449
  provider,
360
450
  cleanedMessages,
@@ -366,7 +456,12 @@ export async function* chat(args: {
366
456
  withPromptCacheKey: wantPromptCacheKey,
367
457
  responseFormat,
368
458
  });
369
- return fetch(req.url, { method: 'POST', headers: req.headers, body: req.body, signal: watchdog.signal });
459
+ return fetch(req.url, {
460
+ method: 'POST',
461
+ headers: req.headers,
462
+ body: req.body,
463
+ signal: watchdog.signal,
464
+ });
370
465
  };
371
466
 
372
467
  const reqId = ++reqCounter;
@@ -387,7 +482,10 @@ export async function* chat(args: {
387
482
  toolSchemaHash: promptMaterialHash(
388
483
  tools.map((tool) => ({
389
484
  name: tool.name,
390
- description: typeof tool.description === 'function' ? tool.description() : tool.description,
485
+ description:
486
+ typeof tool.description === 'function'
487
+ ? tool.description()
488
+ : tool.description,
391
489
  parameters: tool.parameters,
392
490
  })),
393
491
  ),
@@ -409,11 +507,16 @@ export async function* chat(args: {
409
507
  ): Promise<'break' | 'retry'> => {
410
508
  if (signal?.aborted) throw makeAbortError();
411
509
  const err = e as Error;
412
- lastErr = new Error(`LLM 网络错误 [${err.name ?? 'NetworkError'}]:${err.message}`);
510
+ lastErr = new Error(
511
+ `LLM 网络错误 [${err.name ?? 'NetworkError'}]:${err.message}`,
512
+ );
413
513
  dlog('llm', `req#${reqId} attempt ${attempt} ${label}:`, err.message);
414
514
  if (attempt >= DEFAULT_RETRY.maxAttempts) return 'break';
415
515
  const delay = nextRetryDelayMs();
416
- dlog('llm', `req#${reqId} retry in ${delay}ms (attempt ${attempt + 1}/${DEFAULT_RETRY.maxAttempts}, tier=${backoffTier})`);
516
+ dlog(
517
+ 'llm',
518
+ `req#${reqId} retry in ${delay}ms (attempt ${attempt + 1}/${DEFAULT_RETRY.maxAttempts}, tier=${backoffTier})`,
519
+ );
417
520
  await sleep(delay, signal);
418
521
  return 'retry';
419
522
  };
@@ -435,7 +538,10 @@ export async function* chat(args: {
435
538
  // 1) 用户主动 abort → handleNetworkError 里立刻抛
436
539
  // 2) AbortSignal.timeout 触发的超时(Fetch 空闲超时还没回)
437
540
  // 3) 其它网络错误(ECONNRESET / fetch failed / DNS 失败)→ 全部按"可重试"处理
438
- if ((await handleNetworkError(e, attempt, 'failed (network)')) === 'break') break;
541
+ if (
542
+ (await handleNetworkError(e, attempt, 'failed (network)')) === 'break'
543
+ )
544
+ break;
439
545
  continue;
440
546
  }
441
547
 
@@ -456,7 +562,14 @@ export async function* chat(args: {
456
562
  resp = await postChat();
457
563
  watchdog.kick();
458
564
  } catch (e) {
459
- if ((await handleNetworkError(e, attempt, 'cache-key fallback fetch failed')) === 'break') break;
565
+ if (
566
+ (await handleNetworkError(
567
+ e,
568
+ attempt,
569
+ 'cache-key fallback fetch failed',
570
+ )) === 'break'
571
+ )
572
+ break;
460
573
  continue;
461
574
  }
462
575
  if (resp.ok) break;
@@ -464,7 +577,10 @@ export async function* chat(args: {
464
577
  }
465
578
 
466
579
  // 退化①:response_format.json_schema 不支持
467
- if (wantResponseFormat && isResponseFormatUnsupported(resp.status, errText)) {
580
+ if (
581
+ wantResponseFormat &&
582
+ isResponseFormatUnsupported(resp.status, errText)
583
+ ) {
468
584
  // 记住该 model 不支持,单进程内后续结构化调用直接跳过 response_format(不再撞 400)。
469
585
  rfUnsupported.add(rfKey);
470
586
  console.warn(
@@ -478,7 +594,11 @@ export async function* chat(args: {
478
594
  watchdog.kick();
479
595
  } catch (e) {
480
596
  // 退化二次 fetch 也网络错:把错误外提到外层重试循环
481
- if ((await handleNetworkError(e, attempt, 'fallback fetch failed')) === 'break') break;
597
+ if (
598
+ (await handleNetworkError(e, attempt, 'fallback fetch failed')) ===
599
+ 'break'
600
+ )
601
+ break;
482
602
  continue;
483
603
  }
484
604
  if (resp.ok) break;
@@ -498,7 +618,11 @@ export async function* chat(args: {
498
618
  resp = await postChat();
499
619
  watchdog.kick();
500
620
  } catch (e) {
501
- if ((await handleNetworkError(e, attempt, 'fallback fetch failed')) === 'break') break;
621
+ if (
622
+ (await handleNetworkError(e, attempt, 'fallback fetch failed')) ===
623
+ 'break'
624
+ )
625
+ break;
502
626
  continue;
503
627
  }
504
628
  if (resp.ok) break;
@@ -510,9 +634,18 @@ export async function* chat(args: {
510
634
  if (!imagesStripped && isImageInputRejected(resp.status, errText)) {
511
635
  const hasImages = cleanedMessages.some((m) => {
512
636
  const c = (m as { content?: unknown }).content;
513
- return Array.isArray(c) && c.some((p: any) => p.type === 'image_url' || p.type === 'video_url');
637
+ return (
638
+ Array.isArray(c) &&
639
+ c.some((p: any) => p.type === 'image_url' || p.type === 'video_url')
640
+ );
514
641
  });
515
642
  if (hasImages) {
643
+ if (args.visionPolicy === 'required') {
644
+ throw new Error(
645
+ `当前模型拒绝了本轮必需的图片输入,已停止而未删除图片重试。` +
646
+ `请改用支持视觉的模型或检查模型配置。Provider 返回:${errText.slice(0, 500)}`,
647
+ );
648
+ }
516
649
  console.warn(
517
650
  `[minimal-agent] provider model "${provider.model}" 不支持图片输入,` +
518
651
  `已剥离图片重发(仅保留文本内容)。如该模型实际支持视觉,请在模型设置中确认。`,
@@ -523,7 +656,11 @@ export async function* chat(args: {
523
656
  resp = await postChat();
524
657
  watchdog.kick();
525
658
  } catch (e) {
526
- if ((await handleNetworkError(e, attempt, 'fallback fetch failed')) === 'break') break;
659
+ if (
660
+ (await handleNetworkError(e, attempt, 'fallback fetch failed')) ===
661
+ 'break'
662
+ )
663
+ break;
527
664
  continue;
528
665
  }
529
666
  if (resp.ok) break;
@@ -538,13 +675,20 @@ export async function* chat(args: {
538
675
 
539
676
  // 5xx / 408 / 425 / 429:可重试
540
677
  lastErr = buildChatError(resp, errText);
541
- dlog('llm', `req#${reqId} attempt ${attempt} failed [${resp.status}]:`, errText.slice(0, 200));
678
+ dlog(
679
+ 'llm',
680
+ `req#${reqId} attempt ${attempt} failed [${resp.status}]:`,
681
+ errText.slice(0, 200),
682
+ );
542
683
  if (attempt >= DEFAULT_RETRY.maxAttempts) break;
543
684
 
544
685
  // 429 优先用 Retry-After 头,否则走自适应档位退避
545
686
  const retryAfterMs = parseRetryAfter(resp);
546
687
  const delay = nextRetryDelayMs(retryAfterMs);
547
- dlog('llm', `req#${reqId} retry in ${delay}ms (attempt ${attempt + 1}/${DEFAULT_RETRY.maxAttempts}, tier=${backoffTier}, retryAfter=${retryAfterMs ?? 'tier'})`);
688
+ dlog(
689
+ 'llm',
690
+ `req#${reqId} retry in ${delay}ms (attempt ${attempt + 1}/${DEFAULT_RETRY.maxAttempts}, tier=${backoffTier}, retryAfter=${retryAfterMs ?? 'tier'})`,
691
+ );
548
692
  await sleep(delay, signal);
549
693
  }
550
694
 
@@ -567,7 +711,10 @@ export async function* chat(args: {
567
711
  const decoder = new TextDecoder();
568
712
  /** 临时缓冲:HTTP chunk 不一定按行切,要自己拼 */
569
713
  let buffer = '';
570
- const parser = format === 'anthropic' ? createAnthropicSSEParser() : createOpenAISSEParser();
714
+ const parser =
715
+ format === 'anthropic'
716
+ ? createAnthropicSSEParser()
717
+ : createOpenAISSEParser();
571
718
 
572
719
  try {
573
720
  while (true) {
@@ -0,0 +1,201 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { onlineToolCategory } from '../team/toolPolicy.js';
3
+ import { prepareMessagesForProvider } from './messageProvenance.js';
4
+ const IDLE_TIMEOUT_MS = Number(process.env.MINIMAL_AGENT_GATEWAY_IDLE_TIMEOUT_MS) > 0
5
+ ? Number(process.env.MINIMAL_AGENT_GATEWAY_IDLE_TIMEOUT_MS)
6
+ : 120_000;
7
+ const DEADLINE_MS = Number(process.env.MINIMAL_AGENT_GATEWAY_DEADLINE_MS) > 0
8
+ ? Number(process.env.MINIMAL_AGENT_GATEWAY_DEADLINE_MS)
9
+ : 15 * 60_000;
10
+ function gatewayTools(tools, allowedCategories) {
11
+ const allowed = allowedCategories ? new Set(allowedCategories) : null;
12
+ return tools.flatMap((tool) => {
13
+ const category = onlineToolCategory(tool.name);
14
+ if (!category || (allowed && !allowed.has(category)))
15
+ return [];
16
+ return [
17
+ {
18
+ type: 'function',
19
+ category,
20
+ function: {
21
+ name: tool.name,
22
+ ...(typeof tool.description === 'string'
23
+ ? { description: tool.description }
24
+ : {}),
25
+ parameters: tool.parameters,
26
+ },
27
+ },
28
+ ];
29
+ });
30
+ }
31
+ function safeMessages(messages) {
32
+ return messages.map((message) => {
33
+ const { id: _id, timestamp: _timestamp, attachments: _attachments, browserReferences: _browserReferences, provenance: _provenance, display_metadata: _displayMetadata, tool_result_policy: _toolResultPolicy, _ephemeral, ...safe } = message;
34
+ return safe;
35
+ });
36
+ }
37
+ export class GatewayClientError extends Error {
38
+ code;
39
+ retryable;
40
+ constructor(code, retryable, message) {
41
+ // ⚠️ code 必须烘进 message 文本,不能只存在 .code 属性上:这个错误穿过
42
+ // loop.ts(只取 `.message`)→ 引擎 runner(转成纯字符串 blocker)→
43
+ // workflowController 的 isUsageLimitError 文本兜底,每一层都只剩字符串。
44
+ // 不烘进去,`isUsageLimitError` 的「同时认 code 与文本」这句注释就是一句空话——
45
+ // 之前正是这样:message 是纯人话(如「设备授权不存在或已过期」),从不包含
46
+ // DEVICE_REVOKED 之类的码,文本兜底恒为 false,usage_limited 在真实调用链上
47
+ // 从未被触发过。
48
+ super(`[${code}] ${message}`);
49
+ this.code = code;
50
+ this.retryable = retryable;
51
+ this.name = 'GatewayClientError';
52
+ }
53
+ }
54
+ /**
55
+ * 服务端「配额/授权不给了」的错误码 —— **唯一真值**。
56
+ *
57
+ * 与 Goal 的 `usage_limited` 状态一一对应,也正是它与 `budget_limited` 的分界:
58
+ * · `budget_limited` = **用户自己设的** token 额度用完 → 加预算即可继续。
59
+ * · `usage_limited` = **服务端配额/授权**不给了 → 加预算没用,要去解决授权或等周期重置。
60
+ * 两者都可 resume,但恢复动作完全不同,所以不能合并成一个状态。
61
+ *
62
+ * 服务端侧的产出点:`minimal-server/src/core/gatewayInference.ts`。
63
+ */
64
+ const USAGE_LIMIT_CODES = new Set([
65
+ 'REQUEST_QUOTA_EXHAUSTED',
66
+ 'TOKEN_QUOTA_EXHAUSTED',
67
+ 'CONCURRENCY_EXCEEDED',
68
+ 'GRANT_REVOKED',
69
+ 'DEVICE_REVOKED',
70
+ ]);
71
+ /**
72
+ * 判断一个错误是不是「服务端配额/授权不给了」。
73
+ *
74
+ * ⚠️ 同时认 `GatewayClientError.code` 与错误文本:错误穿过 loop → 引擎 → 工作流
75
+ * 这几层之后往往只剩一个字符串(`llm_error` 事件带的是 message),
76
+ * 只认 code 会在真实链路上恒为 false。
77
+ */
78
+ export function isUsageLimitError(error) {
79
+ if (error instanceof GatewayClientError)
80
+ return USAGE_LIMIT_CODES.has(error.code);
81
+ const text = error instanceof Error ? error.message : String(error ?? '');
82
+ return [...USAGE_LIMIT_CODES].some((code) => text.includes(code));
83
+ }
84
+ function combinedSignal(signal) {
85
+ const timeout = AbortSignal.timeout(DEADLINE_MS);
86
+ return signal ? AbortSignal.any([signal, timeout]) : timeout;
87
+ }
88
+ async function readWithIdleTimeout(promise, signal) {
89
+ let timer;
90
+ let onAbort;
91
+ try {
92
+ return await Promise.race([
93
+ promise,
94
+ new Promise((_, reject) => {
95
+ timer = setTimeout(() => reject(new GatewayClientError('PROVIDER_TIMEOUT', true, '在线模型流式响应超时')), IDLE_TIMEOUT_MS);
96
+ onAbort = () => reject(new DOMException('aborted', 'AbortError'));
97
+ signal.addEventListener('abort', onAbort, { once: true });
98
+ }),
99
+ ]);
100
+ }
101
+ finally {
102
+ if (timer)
103
+ clearTimeout(timer);
104
+ if (onAbort)
105
+ signal.removeEventListener('abort', onAbort);
106
+ }
107
+ }
108
+ /** Gateway transport only. The caller retains the existing Agent/tool loop. */
109
+ export async function* chatGateway(args) {
110
+ const { provider, messages, tools, signal: parentSignal, conversationMode, responseFormat, } = args;
111
+ if (!provider.gatewayUrl || !provider.gatewayToken || !provider.remoteModelId)
112
+ throw new GatewayClientError('DEVICE_REVOKED', false, '在线模型设备授权不存在或已过期');
113
+ const url = `${provider.gatewayUrl.replace(/\/$/, '')}/api/inference/chat/stream`;
114
+ const payload = JSON.stringify({
115
+ requestId: randomUUID(),
116
+ modelId: provider.remoteModelId,
117
+ // Keep the gateway payload limited to provider-facing protocol content.
118
+ // Session IDs, provenance metadata, display metadata, and local-only
119
+ // fields must never cross the remote inference boundary.
120
+ messages: safeMessages(prepareMessagesForProvider(messages, conversationMode ?? 'private')),
121
+ tools: gatewayTools(tools, provider.gatewayToolCategories ??
122
+ provider.gatewayDefaultToolCategories ??
123
+ provider.gatewayMaxToolCategories),
124
+ toolCategories: provider.gatewayToolCategories ?? provider.gatewayMaxToolCategories ?? [],
125
+ ...(responseFormat ? { responseFormat } : {}),
126
+ });
127
+ let activeReader;
128
+ const signal = combinedSignal(parentSignal);
129
+ try {
130
+ const response = await fetch(url, {
131
+ method: 'POST',
132
+ signal,
133
+ headers: {
134
+ 'content-type': 'application/json',
135
+ authorization: `Bearer ${provider.gatewayToken}`,
136
+ },
137
+ body: payload,
138
+ });
139
+ if (!response.ok) {
140
+ const body = (await response.json().catch(() => ({})));
141
+ // ⚠️ 服务端**明确表态**优先,状态码只是它没表态时的推断。
142
+ // 曾经写成 `body.retryable === true || status === 429 || …`:服务端发
143
+ // `{code:'TOKEN_QUOTA_EXHAUSTED', retryable:false}` 配 HTTP 429 时,
144
+ // `retryable:false` 被 `|| 429` 直接覆盖 —— 于是「额度用完了,别重试」
145
+ // 被当成限流反复重试,既拖慢失败反馈,也把真正的原因埋掉。
146
+ // 配额耗尽与限流是两回事:前者要去加额度,后者等一会儿就好。
147
+ const retryable = typeof body.retryable === 'boolean'
148
+ ? body.retryable
149
+ : response.status === 408 ||
150
+ response.status === 425 ||
151
+ response.status === 429 ||
152
+ response.status >= 500;
153
+ throw new GatewayClientError(body.code ?? `HTTP_${response.status}`, retryable, body.error ?? `在线模型请求失败(${response.status})`);
154
+ }
155
+ if (!response.body)
156
+ throw new GatewayClientError('PROVIDER_UNAVAILABLE', true, '在线模型未返回流式响应');
157
+ const reader = response.body.getReader();
158
+ activeReader = reader;
159
+ const decoder = new TextDecoder();
160
+ let buffer = '';
161
+ while (true) {
162
+ const next = await readWithIdleTimeout(reader.read(), signal);
163
+ if (next.done)
164
+ break;
165
+ buffer += decoder.decode(next.value, { stream: true });
166
+ const frames = buffer.split(/\n\n/);
167
+ buffer = frames.pop() ?? '';
168
+ for (const frame of frames) {
169
+ const line = frame
170
+ .split(/\r?\n/)
171
+ .find((item) => item.startsWith('data:'));
172
+ if (!line)
173
+ continue;
174
+ let event;
175
+ try {
176
+ event = JSON.parse(line.slice(5).trim());
177
+ }
178
+ catch {
179
+ continue;
180
+ }
181
+ if (event.type === 'error')
182
+ throw new GatewayClientError(event.code ?? 'PROVIDER_UNAVAILABLE', event.retryable === true, event.message ?? '在线模型流式调用失败');
183
+ if (event.type === 'usage') {
184
+ yield event;
185
+ continue;
186
+ }
187
+ if (event.type === 'done')
188
+ return;
189
+ yield event;
190
+ }
191
+ }
192
+ return;
193
+ }
194
+ finally {
195
+ // Release the response body on both normal `done` and error/abort paths.
196
+ // Without this, a completed stream can keep an HTTP connection/socket
197
+ // alive until the runtime's garbage collector notices it.
198
+ await activeReader?.cancel().catch(() => { });
199
+ activeReader = undefined;
200
+ }
201
+ }