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
package/src/loop.ts CHANGED
@@ -18,7 +18,7 @@
18
18
  * d. 否则并行执行所有工具调用(OpenAI 协议要求 tool 消息按 tool_calls 顺序回填)
19
19
  * e. 进入下一轮(让模型看到 tool_result 后继续推理)
20
20
  *
21
- * 失控保护:maxTurns(默认 50)。
21
+ * 失控保护:maxTurns(默认 200)。
22
22
  * 中断支持:AbortSignal 透传到 chat() 和 tool.call()。
23
23
  *
24
24
  * "工作过程显示"事件:
@@ -29,12 +29,20 @@
29
29
 
30
30
  import crypto from 'node:crypto';
31
31
  import { autoCompactIfNeeded, getCompactThreshold } from './context/compact.js';
32
- import { microCompact, incrementTurn, expireOldEntries } from './context/microCompactLite.js';
32
+ import {
33
+ microCompact,
34
+ incrementTurn,
35
+ expireOldEntries,
36
+ reclaimOldToolResults,
37
+ resetCacheEdits,
38
+ getToolResultPolicy,
39
+ } from './context/microCompactLite.js';
33
40
  import {
34
41
  isPromptTooLongError,
35
42
  reactiveCompactIfApplicable,
36
43
  } from './context/reactiveCompact.js';
37
44
  import { snipCompactIfNeeded } from './context/snipCompact.js';
45
+ import { archiveToolResult } from './context/toolResultArchive.js';
38
46
  import {
39
47
  countMessagesTokens,
40
48
  countTextTokens,
@@ -63,10 +71,14 @@ import type {
63
71
  ToolCall,
64
72
  ToolCallContext,
65
73
  ToolResult,
74
+ TurnResourceUsage,
66
75
  } from './types.js';
67
76
  import { configService } from './config/configService.js';
68
77
  import { capabilityForToolName } from './tools/capabilities.js';
69
78
  import { createEffectiveToolSet } from './tools/effectiveToolSet.js';
79
+ import { classifyComputerAutomationIntent } from './tools/computeruse/computerIntent.js';
80
+ import { recordComputerUseExposure } from './tools/computeruse/telemetryLog.js';
81
+ import { filterToolsForProvider } from './team/toolPolicy.js';
70
82
  import { hydrateSessionWorktree } from './tools/worktree/sessionWorktree.js';
71
83
  import {
72
84
  capabilitiesForTools,
@@ -75,21 +87,28 @@ import {
75
87
  import { EvidenceLedger } from './harness/evidenceLedger.js';
76
88
  import { StallGuard } from './harness/stallGuard.js';
77
89
  import { validateFinalClaims } from './harness/claimValidation.js';
90
+ import {
91
+ AdvisorCoordinator,
92
+ isWorkspaceMutationTool,
93
+ type AdvisorToolSemantics,
94
+ } from './advisor/advisorCoordinator.js';
95
+ import { classifyCommand } from './irreversible/rules.js';
78
96
  import {
79
97
  createAgentRuntimeContext,
80
98
  type AgentRuntimeContext,
81
99
  } from './runtime/agentRuntimeContext.js';
100
+ import { stripThinkBlocks } from './utils/stripThink.js';
82
101
 
83
- const DELIVERY_ARTIFACT = '(?:文件|合同|文档|表格|报告|图片|图像|海报|幻灯片|PPT|PDF|代码|脚本|压缩包|网页|页面|HTML|DOCX|XLSX|CSV|ZIP)';
84
- const DELIVERY_ACTION = '(?:发|发送|交付|提交|给|传|制作|生成|创建|导出|输出|修改|更新|完成)';
85
- const ENGLISH_DELIVERY_ARTIFACT = '(?:file|document|report|spreadsheet|workbook|slides?|presentation|pdf|image|code|script|archive|zip|html|webpage|website)';
86
- const ENGLISH_DELIVERY_ACTION = '(?:send|deliver|submit|provide|create|generate|export|update|modify|finish|produce)';
102
+ const DELIVERY_ARTIFACT = '(?:文件|合同|协议|方案|文档|表格|报告|图片|图像|海报|幻灯片|PPT|PDF|代码|脚本|压缩包|网页|页面|HTML|DOCX|XLSX|CSV|ZIP)';
103
+ const DELIVERY_ACTION = '(?:发送|(?<![开研])发|交付|提交|(?<!宣)传(?!统)|制作|生成|创建|导出|输出|修改|更新|完成|写|起草|做|设计|整理)';
104
+ const ENGLISH_DELIVERY_ARTIFACT = '(?:file|contract|document|report|spreadsheet|workbook|slides?|presentation|pdf|image|code|script|archive|zip|html|webpage|website)';
105
+ const ENGLISH_DELIVERY_ACTION = '(?:send|deliver|submit|provide|create|generate|export|update|modify|finish|produce|draft|write|make|build)';
87
106
 
88
107
  /** Whether this turn explicitly asks for a concrete artifact that should be formally delivered. */
89
108
  export function expectsFormalDelivery(input: string): boolean {
90
109
  const compact = input.replace(/\s+/g, '');
91
110
  const chinese = new RegExp(
92
- `${DELIVERY_ACTION}(?:给)?我?.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}${DELIVERY_ACTION}(?:给)?我?`,
111
+ `${DELIVERY_ACTION}(?:给)?我?.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}${DELIVERY_ACTION}(?:给)?我?|给我.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}给我`,
93
112
  'i',
94
113
  );
95
114
  const english = new RegExp(
@@ -136,7 +155,16 @@ function getToolsSchemaTokens(tools: readonly Tool[], signature?: string): numbe
136
155
  }
137
156
 
138
157
  /**
139
- * 顾问咨询配额:基线 1 次,之后每累计 N 次工具调用解锁 1 次,封顶 3 次。
158
+ * 主工具循环的默认轮数上限 —— **唯一真值**。
159
+ *
160
+ * 2026-08 从 50 提到 200(4 倍)。它此前是 `options.maxTurns ?? 200` 里的一个裸字面量,
161
+ * 没有名字也就没有引用点,于是「跟着它一起动的东西」(顾问配额封顶)改不动 ——
162
+ * 见下面的 `ADVISOR_QUOTA_MAX`。
163
+ */
164
+ export const DEFAULT_MAX_TURNS = 200;
165
+
166
+ /**
167
+ * 顾问咨询配额:基线 1 次,之后每累计 N 次工具调用解锁 1 次,封顶见 ADVISOR_QUOTA_MAX。
140
168
  *
141
169
  * 曾经是硬编码 1 次/回合。那个数字隐含「一个用户回合 = 一个决策点」的假设,
142
170
  * 在短问答里成立,在「继续,完成后续所有工作」这类一句话跑几十步的长回合里
@@ -149,9 +177,21 @@ function getToolsSchemaTokens(tools: readonly Tool[], signature?: string): numbe
149
177
  * 刷工具调用换配额的成本远高于收益。
150
178
  */
151
179
  const ADVISOR_QUOTA_BASE = 1;
152
- const ADVISOR_QUOTA_MAX = 3;
153
180
  const ADVISOR_QUOTA_UNLOCK_EVERY_TOOL_CALLS = 15;
154
181
 
182
+ /**
183
+ * 配额封顶**由 `DEFAULT_MAX_TURNS` 推导,不是独立的魔术数字**。
184
+ *
185
+ * 上面那段注释的论据是「一个长回合里真正值钱的咨询点至少三个」——那是按当时
186
+ * `maxTurns = 50` 算的。轮数上限后来提到 200(4 倍工作量塞进同一个回合),
187
+ * 「高风险批次做完」这类节点随之变多,封顶却没跟着动,于是配额重新变成瓶颈:
188
+ * 这正是它当年从「硬编码 1 次」被改掉的同一个毛病,只是换了个刻度。
189
+ *
190
+ * 用公式而不是再写一个字面量,两者才会一起动:
191
+ * 50 轮 → 3(复现历史值) · 200 轮 → 5 · 下限恒为 3
192
+ */
193
+ const ADVISOR_QUOTA_MAX = Math.max(3, Math.round(DEFAULT_MAX_TURNS / 40));
194
+
155
195
  export function advisorQuotaFor(executedToolCalls: number): number {
156
196
  return Math.min(
157
197
  ADVISOR_QUOTA_MAX,
@@ -159,6 +199,30 @@ export function advisorQuotaFor(executedToolCalls: number): number {
159
199
  );
160
200
  }
161
201
 
202
+ function advisorSemanticsFor(call: ToolCall): AdvisorToolSemantics {
203
+ let parsed: Record<string, unknown> = {};
204
+ try { parsed = JSON.parse(call.function.arguments) as Record<string, unknown>; } catch { /* zod layer reports malformed input */ }
205
+ const mutatesWorkspace = isWorkspaceMutationTool(call.function.name);
206
+ if (call.function.name === 'Bash' || call.function.name === 'PowerShell') {
207
+ const command = typeof parsed.command === 'string' ? parsed.command : '';
208
+ const risk = command ? classifyCommand(command) : null;
209
+ return { mutatesWorkspace, highRisk: risk?.riskLevel === 'critical', riskReason: risk?.why };
210
+ }
211
+ if (call.function.name === 'ComputerUse') {
212
+ const highRisk = parsed.intent === 'submit'
213
+ || (parsed.action === 'window' && parsed.windowOperation === 'close');
214
+ return { mutatesWorkspace, highRisk, ...(highRisk ? { riskReason: '原生界面操作不可逆或可能提交外部动作' } : {}) };
215
+ }
216
+ if (call.function.name === 'WebBrowser' || call.function.name === 'BrowserBatch') {
217
+ const highRisk = parsed.intent === 'submit'
218
+ || (call.function.name === 'BrowserBatch'
219
+ && Array.isArray(parsed.steps)
220
+ && parsed.steps.some((step) => typeof step === 'object' && step !== null && (step as Record<string, unknown>).intent === 'submit'));
221
+ return { mutatesWorkspace, highRisk, ...(highRisk ? { riskReason: '浏览器动作将进行发布、购买、删除或最终提交' } : {}) };
222
+ }
223
+ return { mutatesWorkspace, highRisk: false };
224
+ }
225
+
162
226
  export interface RunQueryOptions {
163
227
  provider: Provider;
164
228
  /** Canonical host-issued trust context. Legacy fields below remain adapters. */
@@ -174,6 +238,25 @@ export interface RunQueryOptions {
174
238
  allowedTools?: string[];
175
239
  /** Tools the host/workflow explicitly requires; emergency schema reduction preserves them. */
176
240
  requiredTools?: string[];
241
+ /**
242
+ * provider 原生结构化输出(`response_format.json_schema`),原样透传给 `chat()`。
243
+ *
244
+ * ⚠️ **只在本轮没有工具时才该传**:response_format 会强制**每一条** assistant
245
+ * 消息都是 JSON,而工具调用轮的 assistant 消息带的是 tool_calls —— 两者互斥,
246
+ * 同时给会让带工具的循环直接崩。工作流引擎因此只在 `llm()`(零工具)和
247
+ * schema 修复轮(零工具)上传它;`agent()` 的结构化输出走「跑完再修复」那条路。
248
+ *
249
+ * Anthropic 格式的 provider 不支持,`chat()` 会在发请求前抛错 —— 所以它是
250
+ * **加成**而不是地基,地基是引擎侧的「校验 + 有界修复」。
251
+ */
252
+ responseFormat?: {
253
+ type: 'json_schema';
254
+ json_schema: {
255
+ name: string;
256
+ schema: Record<string, unknown>;
257
+ strict?: boolean;
258
+ };
259
+ };
177
260
  /**
178
261
  * Host-provided tool catalog. Production hosts normally omit this and use ALL_TOOLS;
179
262
  * tests and embedders with custom executors must provide matching schemas so the
@@ -244,8 +327,12 @@ export interface RunQueryOptions {
244
327
  promptCache?: import('./types.js').PromptCacheContext;
245
328
  /** 仅供宿主 UI 恢复附件卡片;不会发送给 LLM。 */
246
329
  userAttachments?: import('./types.js').MessageAttachmentRef[];
330
+ /** Host-authorized files for typed workflow inputs; runtime-only paths. */
331
+ inputResources?: readonly import('./types.js').InvocationResource[];
247
332
  /** Stable host-provided identity for the user message of this turn. */
248
333
  userMessageId?: string;
334
+ /** Trusted local origin for every message created by this run. */
335
+ messageProvenance?: import('./types.js').ConversationProvenance;
249
336
  /** Browser element chips are persisted for UI but stripped before provider calls. */
250
337
  userBrowserReferences?: import('./types.js').MessageBrowserReference[];
251
338
  /** 显式传入的图片附件(-p --image / stream-json images)。 */
@@ -282,8 +369,8 @@ export async function* runQuery(
282
369
  // Tool screenshots are transient evidence for the tool continuation that produced them.
283
370
  // Never carry a prior turn's base64 image into a new user request.
284
371
  evictPriorVisionShots(history);
285
- const maxTurns = options.maxTurns ?? 50;
286
- const runtimeContext = options.runtimeContext ?? createAgentRuntimeContext({
372
+ const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
373
+ const baseRuntimeContext = options.runtimeContext ?? createAgentRuntimeContext({
287
374
  surface: 'headless',
288
375
  profile: 'main',
289
376
  cwd: process.cwd(),
@@ -297,10 +384,24 @@ export async function* runQuery(
297
384
  computerIntervention: options.computerIntervention,
298
385
  toolGate: options.toolGate,
299
386
  });
387
+ const computerIntent = baseRuntimeContext.computerIntent
388
+ ?? classifyComputerAutomationIntent(userInput, sessionState?.computerAutomation);
389
+ const runtimeContext = createAgentRuntimeContext({
390
+ ...baseRuntimeContext,
391
+ computerIntent,
392
+ });
393
+ if (runtimeContext.computer) {
394
+ void recordComputerUseExposure(
395
+ computerIntent.needed,
396
+ computerIntent.source,
397
+ );
398
+ }
399
+ if (!computerIntent.needed && sessionState) delete sessionState.computerAutomation;
300
400
  // DI fallback:测试可传 stub,生产走真实模块单例
301
401
  const chatFn = options.chat ?? defaultChat;
302
402
  const executeToolFn = options.executeTool ?? defaultExecuteTool;
303
403
  const explicitFileDeliveryRequested = expectsFormalDelivery(userInput);
404
+ const currentUserMessageId = options.userMessageId ?? crypto.randomUUID();
304
405
 
305
406
  const transientFor = (tools: readonly Tool[]): string => {
306
407
  const opaque = options.transientContext?.trim() ?? '';
@@ -350,6 +451,8 @@ export async function* runQuery(
350
451
  currentUserInput: userInput,
351
452
  userMessageId: options.userMessageId,
352
453
  memoryAccess: runtimeContext.memoryAccess,
454
+ conversationMode: runtimeContext.conversationMode ?? 'private',
455
+ surface: runtimeContext.surface,
353
456
  workspaceId: runtimeContext.workspaceId,
354
457
  sessionId: runtimeContext.sessionId,
355
458
  cwd: runtimeContext.cwd,
@@ -362,10 +465,11 @@ export async function* runQuery(
362
465
  // 实际下发给模型的工具集:allowedTools 未传时给全量;
363
466
  // 传入数组时严格按数组过滤,[] 表示本轮不下发任何工具。
364
467
  // 仅影响呈现给 LLM 的工具清单,不影响 executeTool 的内部调度能力。
365
- const hostTools = options.availableTools ?? ALL_TOOLS;
468
+ const hostTools = filterToolsForProvider(options.availableTools ?? ALL_TOOLS, provider);
366
469
  const availableTools = hostTools.filter((tool) =>
367
470
  (tool.name !== 'Agent' || subagents.enabled)
368
- && (tool.name !== 'ComputerUse' || runtimeContext.computer !== undefined));
471
+ && (tool.name !== 'ComputerUse'
472
+ || (runtimeContext.computer !== undefined && runtimeContext.computerIntent?.needed === true)));
369
473
  const effectiveToolSet = createEffectiveToolSet({
370
474
  availableTools,
371
475
  runtime: runtimeContext,
@@ -378,6 +482,30 @@ export async function* runQuery(
378
482
  && runtimeContext.surface !== 'workflow'
379
483
  && effectiveToolSet.names.has('PresentResult');
380
484
  let currentEffectiveToolSet = effectiveToolSet;
485
+ const advisorCoordinator = new AdvisorCoordinator({
486
+ history,
487
+ currentRequest: userInput,
488
+ currentRequestMessageId: currentUserMessageId,
489
+ advisorAvailable: effectiveToolSet.names.has('Advisor')
490
+ && effectiveToolSet.names.has('AdvisorDecision'),
491
+ });
492
+ toolCallContext.advisorRuntime = advisorCoordinator;
493
+ // 组装每轮请求的易变上下文。三个构建点(首轮 / autoCompact 后 / snip 后)共用,
494
+ // 避免 Advisor 相关提醒在复制粘贴中三处漂移。提醒均为 transient:只进当轮请求副本,
495
+ // 不进会话历史(与 DELIVERY_CORRECTION_REMINDER 同构,见 buildModelMessages)。
496
+ const buildRequestTransientContext = (): string => {
497
+ const advisorAvailable = currentEffectiveToolSet.names.has('Advisor')
498
+ && currentEffectiveToolSet.names.has('AdvisorDecision');
499
+ const parts = [
500
+ transientFor(activeTools),
501
+ deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
502
+ advisorAvailable ? advisorCoordinator.transientReminder() : '',
503
+ advisorAvailable
504
+ ? advisorCoordinator.usageLine(toolCallContext.advisorCalls!.used, toolCallContext.advisorCalls!.max)
505
+ : '',
506
+ ];
507
+ return parts.filter(Boolean).join('\n\n');
508
+ };
381
509
  let lastRequestTransientContext = [options.transientBundle?.text, options.transientContext]
382
510
  .filter(Boolean).join('\n\n');
383
511
  const evidenceLedger = new EvidenceLedger();
@@ -439,6 +567,16 @@ export async function* runQuery(
439
567
  message: `未作为图片附加:${skipped.source}(${skipped.reason}),已按普通文本处理。`,
440
568
  };
441
569
  }
570
+ let visionRequiredThisTurn = Array.isArray(userContent)
571
+ && userContent.some((part) => part.type === 'image_url' || part.type === 'video_url');
572
+ if (visionRequiredThisTurn && provider.supportsVision === false) {
573
+ yield {
574
+ type: 'error',
575
+ error: '当前模型已配置为不支持视觉输入,图片未发送。请切换到支持图片的模型。',
576
+ code: 'llm_error',
577
+ };
578
+ return;
579
+ }
442
580
 
443
581
  // 1. 用户消息入栈(v2:填 timestamp,让 MessageList 的 turn 边界横线
444
582
  // 能拿到首条 user 的真实时间,否则 fallback Date.now() 会丢失"提交时刻"语义)
@@ -448,12 +586,16 @@ export async function* runQuery(
448
586
  ...(options.userAttachments?.length ? { attachments: options.userAttachments } : {}),
449
587
  ...(options.userBrowserReferences?.length ? { browserReferences: options.userBrowserReferences } : {}),
450
588
  timestamp: Date.now(),
451
- id: options.userMessageId ?? crypto.randomUUID(),
589
+ id: currentUserMessageId,
590
+ ...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
452
591
  };
453
- history.push(userMessage);
454
- // 立即通知 UI:用户消息已入栈,触发 bump 让 <Static> 马上把它 commit 进 scrollback,
455
- // 不必等整轮 assistant 落定(保证用户输入第一时间置顶,符合 T-A-O-R 展示顺序)。
456
- yield { type: 'user_message_committed', message: userMessage };
592
+ const alreadyCommitted = history.some((message) => message.role === 'user' && message.id === userMessage.id);
593
+ if (!alreadyCommitted) {
594
+ history.push(userMessage);
595
+ // 立即通知 UI:用户消息已入栈,触发 bump 让 <Static> 马上把它 commit 进 scrollback,
596
+ // 不必等整轮 assistant 落定(保证用户输入第一时间置顶,符合 T-A-O-R 展示顺序)。
597
+ yield { type: 'user_message_committed', message: userMessage };
598
+ }
457
599
 
458
600
  // 反应式压缩自救:本 runQuery 内最多触发 MAX_REACTIVE_PER_QUERY 次(防本轮死循环);
459
601
  // 跨 runQuery 的连续失败由 reactiveCompact.ts 的 circuit breaker(consecutiveFailures)兜底。
@@ -463,6 +605,15 @@ export async function* runQuery(
463
605
  // 上一轮 LLM 返回的真实 prompt token 数(done.promptTokens;provider 开 include_usage 才有)。
464
606
  // 作为 autoCompact 的权威判据,校正本地估算的系统性低估;压缩后清空(历史已变,旧值失真)。
465
607
  let lastPromptTokens: number | undefined;
608
+
609
+ // ── 回合级资源账本 ────────────────────────────────────────────────
610
+ // ⚠️ 与 lastPromptTokens 生命周期**故意不同**:那个是「当前 history 有多大」的快照,
611
+ // 压缩后必须清零;这个是「本回合总共花了多少」的累计,压缩不影响已经花掉的钱。
612
+ // ⚠️ 它只记账,**不参与任何控制流** —— loop 绝不因预算中断进行中的工具循环。
613
+ const turnUsage = { inputTokens: 0, outputTokens: 0, totalCalls: 0, reportedCalls: 0 };
614
+
615
+ // 归档退化只报一次:每条超长结果都报会把上下文刷满,反而盖掉别的信号。
616
+ let archiveDegradedWarned = false;
466
617
  // 提示词缓存观测位(纯展示,不参与任何控制流)。与 lastPromptTokens 同生命周期:
467
618
  // 压缩后一并清空,避免把压缩前的命中数据挂到压缩后的新历史上误导排障。
468
619
  let lastCachedPromptTokens: number | undefined;
@@ -472,10 +623,24 @@ export async function* runQuery(
472
623
 
473
624
  let turn = 0;
474
625
  let workStarted = false;
626
+
475
627
  while (turn < maxTurns) {
476
628
  turn++;
477
629
  incrementTurn(sessionState?.microCompact);
478
630
  expireOldEntries(sessionState?.microCompact);
631
+ // Path C 批量回收旧工具结果:可重放的观察类工具结果(Read/Grep/Bash…)
632
+ // 超过阈值时只保留热尾部。必须在 buildContextBudgetPlan 之前跑——预算估算和
633
+ // autoCompact 判据直接看到瘦身后的历史,避免无谓的全量压缩。
634
+ // cacheEdits 模式(Anthropic 格式 + provider.cacheEdits):不改 history,删除目标
635
+ // 记入状态、由请求层组装 cache_edits 发送--服务端 KV 原地删除,前缀缓存完整保留。
636
+ // 本地估算的虚高部分(服务端已删内容)由 autoCompactIfNeeded 的 serverDeletedTokens
637
+ // 校正,判据不会因此过早触发压缩。
638
+ const cacheEditsMode = provider.apiFormat === 'anthropic' && provider.cacheEdits === true;
639
+ reclaimOldToolResults(
640
+ history,
641
+ sessionState?.microCompact,
642
+ cacheEditsMode ? 'cacheEdits' : 'inline',
643
+ );
479
644
 
480
645
  if (signal?.aborted) {
481
646
  pushManualInterruptMarker(history);
@@ -499,10 +664,7 @@ export async function* runQuery(
499
664
 
500
665
  // 2. 自动压缩(判据 = max(上一轮真实 usage, 历史估算 + 工具 schema),
501
666
  // 阈值 provider 无关、按 contextWindow × compactRatio 比例触发)
502
- let requestTransientContext = [
503
- transientFor(activeTools),
504
- deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
505
- ].filter(Boolean).join('\n\n');
667
+ let requestTransientContext = buildRequestTransientContext();
506
668
  lastRequestTransientContext = requestTransientContext;
507
669
  const preCompactBudget = buildContextBudgetPlan({
508
670
  provider,
@@ -516,6 +678,11 @@ export async function* runQuery(
516
678
  actualPromptTokens: lastPromptTokens,
517
679
  toolsTokens: getToolsSchemaTokens(activeTools, effectiveToolSet.signature),
518
680
  transientTokens: countTextTokens(requestTransientContext),
681
+ // cacheEdits 判据校正:本地 history 未瘦身,扣掉服务端已删部分的估算,
682
+ // 防止虚高估算劫持 Math.max 导致压缩过早触发(压缩会重建整个前缀缓存)。
683
+ ...(cacheEditsMode && sessionState?.microCompact
684
+ ? { serverDeletedTokens: sessionState.microCompact.cacheEdits.activeDeletedTokens }
685
+ : {}),
519
686
  signal,
520
687
  promptCache: options.promptCache,
521
688
  chat: chatFn,
@@ -525,6 +692,9 @@ export async function* runQuery(
525
692
  yield { type: 'stage_change', stage: 'compacting' };
526
693
  history.length = 0;
527
694
  history.push(...compact.messages);
695
+ // cacheEdits 状态随压缩作废:pinned 的翻译后下标指向已不存在的消息序列,
696
+ // 服务端缓存前缀也已失效——不重置会导致删除指令插错位置甚至重复删除。
697
+ if (sessionState?.microCompact) resetCacheEdits(sessionState.microCompact);
528
698
  lastPromptTokens = undefined;
529
699
  lastCachedPromptTokens = undefined;
530
700
  lastCacheMissTokens = undefined;
@@ -553,6 +723,8 @@ export async function* runQuery(
553
723
  if (snipped.messagesRemoved > 0) {
554
724
  history.length = 0;
555
725
  history.push(...snipped.messages);
726
+ // 同 compact:snip 砍头后 pinned 下标失位,cacheEdits 必须重置
727
+ if (sessionState?.microCompact) resetCacheEdits(sessionState.microCompact);
556
728
  lastPromptTokens = undefined;
557
729
  lastCachedPromptTokens = undefined;
558
730
  lastCacheMissTokens = undefined;
@@ -588,10 +760,7 @@ export async function* runQuery(
588
760
 
589
761
  // Re-open transient capacity after compaction. If the compacted tail is still
590
762
  // too large, trim old history before sacrificing any model-visible ability.
591
- requestTransientContext = [
592
- transientFor(activeTools),
593
- deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
594
- ].filter(Boolean).join('\n\n');
763
+ requestTransientContext = buildRequestTransientContext();
595
764
  lastRequestTransientContext = requestTransientContext;
596
765
  let postCompactBudget = buildContextBudgetPlan({
597
766
  provider,
@@ -616,10 +785,7 @@ export async function* runQuery(
616
785
  messagesRemoved: snipped.messagesRemoved,
617
786
  tokensFreed: snipped.tokensFreed,
618
787
  };
619
- requestTransientContext = [
620
- transientFor(activeTools),
621
- deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
622
- ].filter(Boolean).join('\n\n');
788
+ requestTransientContext = buildRequestTransientContext();
623
789
  lastRequestTransientContext = requestTransientContext;
624
790
  postCompactBudget = buildContextBudgetPlan({
625
791
  provider,
@@ -644,6 +810,10 @@ export async function* runQuery(
644
810
  runtime: runtimeContext,
645
811
  requiredNames: [...effectiveToolSet.requiredNames],
646
812
  });
813
+ advisorCoordinator.setAvailable(
814
+ currentEffectiveToolSet.names.has('Advisor')
815
+ && currentEffectiveToolSet.names.has('AdvisorDecision'),
816
+ );
647
817
  if (emergencyFit.removedNames.length > 0) {
648
818
  yield {
649
819
  type: 'warning',
@@ -689,12 +859,22 @@ export async function* runQuery(
689
859
 
690
860
  try {
691
861
  const modelMessages = buildModelMessages(history, requestTransientContext, userMessage.id);
862
+ advisorCoordinator.markTransientDelivered();
692
863
  for await (const ev of chatFn({
693
864
  provider,
694
865
  messages: modelMessages,
695
866
  tools: currentEffectiveToolSet.tools,
696
867
  signal,
868
+ conversationMode: runtimeContext.conversationMode ?? 'private',
697
869
  promptCache: options.promptCache,
870
+ visionPolicy: visionRequiredThisTurn ? 'required' : 'optional-history',
871
+ // cacheEdits 回收状态(同一会话级对象跨轮复用;compact 后由 resetCacheEdits 清空)
872
+ ...(cacheEditsMode && sessionState?.microCompact
873
+ ? { cacheEdits: sessionState.microCompact.cacheEdits }
874
+ : {}),
875
+ ...(options.responseFormat
876
+ ? { responseFormat: options.responseFormat }
877
+ : {}),
698
878
  })) {
699
879
  if (ev.type === 'text_delta') {
700
880
  if (!stageStreamingYielded) {
@@ -716,6 +896,8 @@ export async function* runQuery(
716
896
  // reasoning_details 是结构化数组,没有 delta 字符串,跳过 yield
717
897
  reasoningDetails.push(...ev.items);
718
898
  }
899
+ } else if (ev.type === 'usage') {
900
+ if (typeof ev.promptTokens === 'number') lastPromptTokens = ev.promptTokens;
719
901
  } else if (ev.type === 'done') {
720
902
  // R2:记下流的终止原因。'length'(max_tokens 截断)会在本轮收尾时触发 warning。
721
903
  lastFinishReason = ev.stopReason;
@@ -726,6 +908,18 @@ export async function* runQuery(
726
908
  if (typeof ev.cacheMissTokens === 'number') lastCacheMissTokens = ev.cacheMissTokens;
727
909
  if (typeof ev.cacheWriteTokens === 'number') lastCacheWriteTokens = ev.cacheWriteTokens;
728
910
  if (ev.promptCacheUsage) lastPromptCacheUsage = ev.promptCacheUsage;
911
+
912
+ // 回合级记账(与上面的 lastPromptTokens 不同:那个压缩后要清零,这个不清)。
913
+ // ⚠️ 只有 provider 真报了才计入 reportedCalls —— 「没人告诉我」不能被当成
914
+ // 「真的是 0」,否则预算闸永远不触发。覆盖率由 trustworthy 如实暴露。
915
+ turnUsage.totalCalls += 1;
916
+ const reportedIn = typeof ev.promptTokens === 'number' ? ev.promptTokens : undefined;
917
+ const reportedOut = typeof ev.outputTokens === 'number' ? ev.outputTokens : undefined;
918
+ if (reportedIn !== undefined || reportedOut !== undefined) {
919
+ turnUsage.reportedCalls += 1;
920
+ turnUsage.inputTokens += reportedIn ?? 0;
921
+ turnUsage.outputTokens += reportedOut ?? 0;
922
+ }
729
923
  }
730
924
  }
731
925
  } catch (e) {
@@ -824,24 +1018,54 @@ export async function* runQuery(
824
1018
  ...(reasoningDetails.length > 0 ? { reasoning_details: reasoningDetails } : {}),
825
1019
  timestamp: Date.now(),
826
1020
  id: crypto.randomUUID(),
1021
+ ...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
827
1022
  };
828
- history.push(assistantMsg);
829
- yield { type: 'assistant_message', message: assistantMsg as Extract<Message, { role: 'assistant' }> };
830
-
831
1023
  // 4. 没有工具调用 → 整轮交互结束
832
1024
  if (toolCallsByIndex.length === 0) {
1025
+ const emptyResponse = stripThinkBlocks(assistantText).length === 0;
833
1026
  // A model occasionally writes a path/link in prose and stops without formally
834
1027
  // presenting it. Give it one model-only corrective pass; the reminder is
835
1028
  // transient and therefore never pollutes persisted conversation history.
836
1029
  if (
837
- formalDeliveryRequired
1030
+ !emptyResponse && formalDeliveryRequired
838
1031
  && !resultPresented
839
1032
  && !deliveryCorrectionAttempted
840
1033
  && turn < maxTurns
841
1034
  ) {
842
1035
  deliveryCorrectionAttempted = true;
1036
+ yield {
1037
+ type: 'assistant_draft_discarded',
1038
+ reason: 'formal_delivery',
1039
+ textLength: assistantText.length,
1040
+ };
1041
+ continue;
1042
+ }
1043
+ const deliveryCheckpoint = emptyResponse
1044
+ ? { allow: true as const }
1045
+ : advisorCoordinator.beforeDelivery();
1046
+ if (!deliveryCheckpoint.allow && turn < maxTurns) {
1047
+ yield {
1048
+ type: 'assistant_draft_discarded',
1049
+ reason: 'advisor_checkpoint',
1050
+ textLength: assistantText.length,
1051
+ };
843
1052
  continue;
844
1053
  }
1054
+ if (emptyResponse) {
1055
+ // A no-tool response is already the model's terminal signal. Do not turn it
1056
+ // into another model round, and do not persist/broadcast an empty bubble.
1057
+ yield {
1058
+ type: 'assistant_draft_discarded',
1059
+ reason: 'empty_response',
1060
+ textLength: assistantText.length,
1061
+ };
1062
+ } else {
1063
+ history.push(assistantMsg);
1064
+ yield {
1065
+ type: 'assistant_message',
1066
+ message: assistantMsg as Extract<Message, { role: 'assistant' }>,
1067
+ };
1068
+ }
845
1069
  // R2:最终回答被 provider 因 max_tokens 截断(finish_reason='length')时,
846
1070
  // 不能静默当正常 end_turn 收尾 —— 否则 -p 宿主会拿到半截答案却以为成功。
847
1071
  // 走通用 warning(text/json 都落 stderr,不污染 json 那一行 stdout 契约)。
@@ -884,26 +1108,41 @@ export async function* runQuery(
884
1108
  : {}),
885
1109
  promptCacheUsage: lastPromptCacheUsage,
886
1110
  };
1111
+ // 资源账本挂到**每一条** turn_done 出口上。⚠️ 别只挂在 'completed' 那条:
1112
+ // 失败、交付不全、声明未核实的回合照样烧了 token,漏掉它们会让预算系统性少算,
1113
+ // 而少算的方向恰好是「让循环继续跑」—— 这正是本仓库记过账的
1114
+ // 「turn_done.completionStatus 被 print.ts 整条丢弃」同一类错误的反面。
1115
+ const usage: TurnResourceUsage = {
1116
+ ...turnUsage,
1117
+ trustworthy: turnUsage.totalCalls > 0 && turnUsage.reportedCalls === turnUsage.totalCalls,
1118
+ };
887
1119
  if (formalDeliveryRequired && !resultPresented) {
888
1120
  const warning = '交付未完成:用户明确要求提供文件,但本轮没有成功提交可打开的文件结果。';
889
1121
  yield { type: 'warning', message: warning };
890
- yield { type: 'turn_done', completionStatus: 'delivery_incomplete', warning };
1122
+ yield { type: 'turn_done', completionStatus: 'delivery_incomplete', warning, usage };
891
1123
  } else if (resultPresented && presentedResultUnverified) {
892
1124
  const warning = '结果已提交,但至少一个文件尚未通过结构化验证。';
893
1125
  yield { type: 'warning', message: warning };
894
- yield { type: 'turn_done', completionStatus: 'delivery_unverified', warning };
1126
+ yield { type: 'turn_done', completionStatus: 'delivery_unverified', warning, usage };
895
1127
  } else if (!claimValidation.verified) {
896
1128
  yield {
897
1129
  type: 'turn_done',
898
1130
  completionStatus: 'claims_unverified',
899
1131
  warning: claimValidation.warnings.join('\n'),
1132
+ usage,
900
1133
  };
901
1134
  } else {
902
- yield { type: 'turn_done', completionStatus: 'completed' };
1135
+ yield { type: 'turn_done', completionStatus: 'completed', usage };
903
1136
  }
904
1137
  return;
905
1138
  }
906
1139
 
1140
+ history.push(assistantMsg);
1141
+ yield {
1142
+ type: 'assistant_message',
1143
+ message: assistantMsg as Extract<Message, { role: 'assistant' }>,
1144
+ };
1145
+
907
1146
  // 5. 执行工具调用
908
1147
  //
909
1148
  // R1:按 isConcurrencySafe 决定并发策略 —— 工具早就声明了这个字段,此前却没有
@@ -956,6 +1195,10 @@ export async function* runQuery(
956
1195
  },
957
1196
  };
958
1197
  }
1198
+ const checkpoint = advisorCoordinator.beforeTool(tc, advisorSemanticsFor(tc));
1199
+ if (!checkpoint.allow) {
1200
+ return { tc, result: checkpoint.deferred! };
1201
+ }
959
1202
  // 工具执行闸门:宿主注入后覆盖所有工具,包括只读工具。deny → 把 reason 当
960
1203
  // tool_result 回填,模型据此改道/如实报告,不抛异常不中断整轮。宿主异常一律 deny。
961
1204
  const isReadOnly = getToolByName(tc.function.name)?.isReadOnly === true;
@@ -992,19 +1235,35 @@ export async function* runQuery(
992
1235
  tc.function.name,
993
1236
  tc.function.arguments,
994
1237
  signal,
995
- { ...toolCallContext, cwd: sessionState?.activeWorktree?.path ?? toolCallContext.cwd, toolCallId: tc.id, reportProgress },
1238
+ {
1239
+ ...toolCallContext,
1240
+ cwd:
1241
+ sessionState?.activeWorktree?.path ?? toolCallContext.cwd,
1242
+ toolCallId: tc.id,
1243
+ reportProgress,
1244
+ },
996
1245
  );
1246
+ if (tc.function.name === 'ComputerUse' && sessionState) {
1247
+ sessionState.computerAutomation = { lastUsedAt: Date.now() };
1248
+ }
1249
+ const downloadableArtifact =
1250
+ result.ok && result.metadata?.kind === 'browser_download'
1251
+ ? result.metadata.artifact
1252
+ : undefined;
1253
+ const formalManifest =
1254
+ result.ok && result.metadata?.kind === 'result_manifest'
1255
+ ? result.metadata.manifest
1256
+ : downloadableArtifact;
997
1257
  if (
998
- tc.function.name === 'PresentResult'
999
- && result.ok
1000
- && result.metadata?.kind === 'result_manifest'
1001
- && options.commitFormalDelivery
1258
+ formalManifest &&
1259
+ (tc.function.name === 'PresentResult' || downloadableArtifact) &&
1260
+ options.commitFormalDelivery
1002
1261
  ) {
1003
1262
  try {
1004
1263
  await options.commitFormalDelivery({
1005
1264
  toolCallId: tc.id,
1006
1265
  turnId: userMessage.id ?? options.userMessageId ?? 'turn:unknown',
1007
- manifest: result.metadata.manifest,
1266
+ manifest: formalManifest,
1008
1267
  });
1009
1268
  } catch (error) {
1010
1269
  result = {
@@ -1015,6 +1274,10 @@ export async function* runQuery(
1015
1274
  };
1016
1275
  }
1017
1276
  }
1277
+ if (tc.function.name === 'Advisor' || tc.function.name === 'AdvisorDecision') {
1278
+ // 串行批次内下一项可能就是 AdvisorDecision/受保护写入,必须立即推进协调器状态。
1279
+ advisorCoordinator.observeToolResult(tc, result);
1280
+ }
1018
1281
  options.onToolEnd?.(tc.id, false);
1019
1282
  return { tc, result };
1020
1283
  } catch (e) {
@@ -1133,32 +1396,75 @@ export async function* runQuery(
1133
1396
  }
1134
1397
 
1135
1398
  // 本回合工作量累计 → 上调顾问配额(只增不减;已用掉的次数不会因此回退)。
1136
- executedToolCalls += settled.length;
1399
+ executedToolCalls += settled.filter(({ result }) =>
1400
+ result.ok || result.disposition !== 'deferred').length;
1137
1401
  toolCallContext.advisorCalls!.max = advisorQuotaFor(executedToolCalls);
1138
1402
 
1139
1403
  // 严格按 toolCallsByIndex 顺序把 tool 消息回填 history(settled 已保持该顺序)。
1140
1404
  let anyAborted = false;
1141
1405
  for (const { tc, result } of settled) {
1142
- evidenceLedger.record(tc.function.name, tc.function.arguments, result);
1406
+ if (tc.function.name !== 'Advisor' && tc.function.name !== 'AdvisorDecision') {
1407
+ advisorCoordinator.observeToolResult(tc, result);
1408
+ }
1409
+ if (result.ok || result.disposition !== 'deferred') {
1410
+ evidenceLedger.record(tc.function.name, tc.function.arguments, result);
1411
+ }
1143
1412
  if (!result.ok && result.error === 'aborted') anyAborted = true;
1144
- if (tc.function.name === 'PresentResult' && result.ok && result.metadata?.kind === 'result_manifest') {
1413
+ const browserArtifact =
1414
+ result.ok && result.metadata?.kind === 'browser_download'
1415
+ ? result.metadata.artifact
1416
+ : undefined;
1417
+ if (
1418
+ result.ok &&
1419
+ ((tc.function.name === 'PresentResult' &&
1420
+ result.metadata?.kind === 'result_manifest') ||
1421
+ browserArtifact)
1422
+ ) {
1145
1423
  resultPresented = true;
1146
- presentedResultUnverified ||= result.metadata.manifest.files.some(
1147
- (file) => file.verification !== 'passed',
1148
- );
1424
+ // `not_run` only says that no optional, format-specific validator was
1425
+ // invoked. The PresentResult boundary has still checked the file and
1426
+ // persisted its fingerprint, so it must not downgrade a normal delivery.
1427
+ const manifest =
1428
+ result.metadata?.kind === 'result_manifest'
1429
+ ? result.metadata.manifest
1430
+ : browserArtifact;
1431
+ presentedResultUnverified ||=
1432
+ manifest?.files.some((file) => file.verification === 'failed') ??
1433
+ false;
1149
1434
  }
1150
1435
  const rawContent = result.ok ? result.content : `Error: ${result.error}`;
1151
- // 微压缩仅对成功结果走(失败结果通常很短,不需要压缩)
1436
+ const basePolicy = result.policy ?? getToolResultPolicy(tc.function.name);
1437
+ // 超大结果先归档再压缩:归档失败时保留原文,绝不因为磁盘问题破坏上下文。
1438
+ const archived = result.ok && sessionState
1439
+ ? await archiveToolResult(rawContent, sessionState.microCompact.archiveKey, tc.function.name)
1440
+ : { content: rawContent, originalChars: rawContent.length };
1441
+ // 归档退化必须发声:撞会话上限后每条超长结果都退回原文塞进上下文,
1442
+ // 也就是这个机制本来要解决的问题原样回来了 —— 静默的话没人会发现。
1443
+ if (archived.degraded && !archiveDegradedWarned) {
1444
+ archiveDegradedWarned = true;
1445
+ yield {
1446
+ type: 'warning',
1447
+ message: archived.degraded === 'session-limit'
1448
+ ? '工具结果归档已达本会话上限,后续超长结果将直接进入上下文(归档不再生效)。建议 /new 开新会话。'
1449
+ : '工具结果归档写盘失败,后续超长结果将直接进入上下文。请检查磁盘空间与权限。',
1450
+ };
1451
+ }
1452
+ const policy = archived.archiveRef
1453
+ ? { ...basePolicy, archiveRef: archived.archiveRef, originalChars: archived.originalChars }
1454
+ : basePolicy;
1455
+ // 微压缩仅对成功结果走;policy 会保护高价值结果,避免不可逆中段丢失。
1152
1456
  const content = result.ok
1153
- ? microCompact(tc.function.name, rawContent, sessionState?.microCompact)
1457
+ ? microCompact(tc.function.name, archived.content, sessionState?.microCompact, policy)
1154
1458
  : rawContent;
1155
1459
  history.push({
1156
1460
  role: 'tool',
1157
1461
  content,
1158
1462
  tool_call_id: tc.id,
1463
+ tool_result_policy: policy,
1159
1464
  ...(result.metadata ? { display_metadata: result.metadata } : {}),
1160
1465
  timestamp: Date.now(),
1161
1466
  id: crypto.randomUUID(),
1467
+ ...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
1162
1468
  });
1163
1469
  }
1164
1470
 
@@ -1169,6 +1475,10 @@ export async function* runQuery(
1169
1475
  ({ result }) =>
1170
1476
  !result.ok && result.error.includes('第三次调用已在执行前拒绝'),
1171
1477
  );
1478
+ const preemptedSignature = preemptedRepeatedFailure
1479
+ ? settled.find(({ result }) =>
1480
+ !result.ok && result.error.includes('第三次调用已在执行前拒绝'))?.tc
1481
+ : undefined;
1172
1482
  const stall = preemptedRepeatedFailure
1173
1483
  ? ({
1174
1484
  stalled: true as const,
@@ -1183,12 +1493,15 @@ export async function* runQuery(
1183
1493
  : { stalled: false as const };
1184
1494
  if (stall.stalled) {
1185
1495
  const message = stall.reason === 'repeated_failure'
1186
- ? '检测到同一工具以相同参数连续返回相同失败,已停止重复重试。'
1187
- : '检测到连续工具轮次没有产生新文本或新结果,已停止无进展循环。';
1496
+ ? '检测到同一工具以相同参数连续返回相同失败;请停止原样重试,改做只读诊断、切换路径或咨询 Advisor。'
1497
+ : '检测到连续工具轮次没有产生新文本或新结果;请切换路径或咨询 Advisor。';
1498
+ advisorCoordinator.notifyStalled(
1499
+ stall.signature
1500
+ ?? (preemptedSignature
1501
+ ? `${preemptedSignature.function.name}\0${preemptedSignature.function.arguments}`
1502
+ : stall.reason),
1503
+ );
1188
1504
  yield { type: 'warning', message };
1189
- yield { type: 'harness_diagnostics', diagnostics: harnessDiagnostics('stalled') };
1190
- yield { type: 'error', error: message, code: 'stalled' };
1191
- return;
1192
1505
  }
1193
1506
 
1194
1507
  // 关键节点截图回填:把工具附带的实况截图作为一条带图 user 消息喂给(视觉)模型自我核验,
@@ -1201,6 +1514,7 @@ export async function* runQuery(
1201
1514
  // The history copy is intentionally downgraded after the turn. Emit a detached snapshot
1202
1515
  // so TUI/Desktop stores do not see their already-rendered image mutate underneath them.
1203
1516
  if (injectedShot) {
1517
+ visionRequiredThisTurn = true;
1204
1518
  yield { type: 'injected_message', message: structuredClone(injectedShot) };
1205
1519
  }
1206
1520
 
@@ -1311,7 +1625,7 @@ async function injectVisionFollowups(
1311
1625
  settled: { result: ToolResult }[],
1312
1626
  provider: Provider,
1313
1627
  ): Promise<Message | null> {
1314
- if (provider.supportsVision !== true) return null;
1628
+ if (provider.supportsVision === false) return null;
1315
1629
  const images = settled.flatMap((s) => s.result.visionImages ?? []);
1316
1630
  if (images.length === 0) return null;
1317
1631
  evictPriorVisionShots(history);
@@ -1441,6 +1755,8 @@ function friendlyToolDescription(toolName: string, rawArgsJson: string): string
1441
1755
  return `Fetching ...`;
1442
1756
  }
1443
1757
  }
1758
+ case 'BrowserBatch': return `Browser batch (${Array.isArray(args.steps) ? args.steps.length : 0} steps)`;
1759
+ case 'BrowserCDP': return `Browser CDP ${truncate(String(args.method ?? args.operation ?? ''), 45)}`;
1444
1760
  case 'WebSearch': return `Searching \`${truncate(args.query, 50)}\``;
1445
1761
  case 'Agent': {
1446
1762
  const t = typeof args.agent_type === 'string' ? args.agent_type : 'explore';