minimal-agent 0.13.0 → 0.15.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 (474) 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 +536 -0
  103. package/src/advisor/advisorCoordinator.ts +693 -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/quickModelSetup.js +12 -1
  108. package/src/cli/quickModelSetup.tsx +15 -3
  109. package/src/cli/streamJson.js +5 -6
  110. package/src/cli/streamJson.ts +8 -4
  111. package/src/cli/wizards/advisorWizard.js +4 -2
  112. package/src/cli/wizards/advisorWizard.tsx +8 -2
  113. package/src/cli/wizards/contextAdvisorWizard.js +5 -4
  114. package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
  115. package/src/cli/wizards/providerPresets.js +18 -8
  116. package/src/cli/wizards/providerPresets.ts +18 -8
  117. package/src/collaboration/taskFlow.js +115 -0
  118. package/src/collaboration/taskFlow.ts +169 -0
  119. package/src/config/configDefaults.js +41 -1
  120. package/src/config/configDefaults.ts +43 -1
  121. package/src/config/configDiagnostics.ts +2 -2
  122. package/src/config/configFile.js +40 -9
  123. package/src/config/configFile.ts +60 -11
  124. package/src/config/configService.js +419 -74
  125. package/src/config/configService.ts +703 -145
  126. package/src/config/numericLimits.js +22 -0
  127. package/src/config/numericLimits.ts +24 -0
  128. package/src/config.js +1 -0
  129. package/src/config.ts +3 -2
  130. package/src/context/budgetPlan.js +1 -1
  131. package/src/context/budgetPlan.ts +1 -1
  132. package/src/context/compact.js +7 -2
  133. package/src/context/compact.ts +9 -2
  134. package/src/context/microCompactLite.js +212 -13
  135. package/src/context/microCompactLite.ts +261 -12
  136. package/src/context/sessionRegistry.js +8 -1
  137. package/src/context/sessionRegistry.ts +18 -5
  138. package/src/context/toolResultArchive.js +72 -0
  139. package/src/context/toolResultArchive.ts +95 -0
  140. package/src/goal/errors.js +8 -0
  141. package/src/goal/errors.ts +18 -0
  142. package/src/goal/goalRuntime.js +344 -0
  143. package/src/goal/goalRuntime.ts +466 -0
  144. package/src/goal/goalStore.js +200 -0
  145. package/src/goal/goalStore.ts +219 -0
  146. package/src/goal/index.js +4 -0
  147. package/src/goal/index.ts +4 -0
  148. package/src/goal/statusMapping.js +71 -0
  149. package/src/goal/statusMapping.ts +95 -0
  150. package/src/goal/types.js +7 -0
  151. package/src/goal/types.ts +179 -0
  152. package/src/goal/workflowController.js +306 -0
  153. package/src/goal/workflowController.ts +360 -0
  154. package/src/harness/evidenceLedger.js +2 -0
  155. package/src/harness/evidenceLedger.ts +1 -0
  156. package/src/harness/stallGuard.js +9 -3
  157. package/src/harness/stallGuard.ts +7 -3
  158. package/src/images/imageResolver.js +161 -0
  159. package/src/images/imageResolver.ts +189 -0
  160. package/src/infra/verification.js +34 -5
  161. package/src/infra/verification.ts +39 -4
  162. package/src/llm/client.js +79 -21
  163. package/src/llm/client.ts +182 -35
  164. package/src/llm/gatewayClient.js +201 -0
  165. package/src/llm/gatewayClient.ts +299 -0
  166. package/src/llm/messageProvenance.js +46 -0
  167. package/src/llm/messageProvenance.ts +58 -0
  168. package/src/llm/modelProvider.js +1 -0
  169. package/src/llm/modelProvider.ts +22 -0
  170. package/src/llm/providers/anthropicFormat.js +108 -1
  171. package/src/llm/providers/anthropicFormat.ts +115 -4
  172. package/src/llm/providers/openaiFormat.js +9 -0
  173. package/src/llm/providers/openaiFormat.ts +12 -3
  174. package/src/loop.js +328 -55
  175. package/src/loop.ts +382 -58
  176. package/src/main.js +4 -1
  177. package/src/main.tsx +4 -1
  178. package/src/managedExecution/index.js +2 -0
  179. package/src/managedExecution/index.ts +2 -0
  180. package/src/managedExecution/registry.js +15 -0
  181. package/src/managedExecution/registry.ts +20 -0
  182. package/src/managedExecution/types.js +1 -0
  183. package/src/managedExecution/types.ts +86 -0
  184. package/src/modelRef.js +9 -0
  185. package/src/modelRef.ts +31 -0
  186. package/src/multimodal.js +14 -28
  187. package/src/multimodal.ts +17 -33
  188. package/src/net/lanDiscovery.js +65 -0
  189. package/src/net/lanDiscovery.ts +82 -0
  190. package/src/plugin-sdk.js +28 -3
  191. package/src/plugin-sdk.ts +39 -6
  192. package/src/plugins/pluginApi.ts +16 -1
  193. package/src/plugins/pluginLoader.js +4 -0
  194. package/src/plugins/pluginLoader.ts +6 -0
  195. package/src/plugins/pluginRunner.js +139 -7
  196. package/src/plugins/pluginRunner.ts +173 -9
  197. package/src/plugins/slashFallback.js +113 -0
  198. package/src/plugins/slashFallback.ts +137 -0
  199. package/src/prompts/policyRegistry.js +12 -32
  200. package/src/prompts/policyRegistry.ts +15 -32
  201. package/src/prompts/promptAssembly.js +103 -12
  202. package/src/prompts/promptAssembly.ts +114 -12
  203. package/src/prompts/skillList.js +21 -3
  204. package/src/prompts/skillList.ts +27 -2
  205. package/src/prompts/stableSystemPrompt.js +18 -0
  206. package/src/prompts/stableSystemPrompt.ts +33 -0
  207. package/src/runtime/agentRuntimeContext.js +8 -2
  208. package/src/runtime/agentRuntimeContext.ts +17 -4
  209. package/src/session/runAgentTurn.js +78 -12
  210. package/src/session/runAgentTurn.ts +105 -23
  211. package/src/session/runTurn.js +10 -9
  212. package/src/session/runTurn.ts +17 -8
  213. package/src/session/sessionState.ts +2 -0
  214. package/src/team/auth.js +186 -0
  215. package/src/team/auth.ts +298 -0
  216. package/src/team/cli.js +105 -0
  217. package/src/team/cli.ts +122 -0
  218. package/src/team/onlineToolContract.js +64 -0
  219. package/src/team/onlineToolContract.ts +109 -0
  220. package/src/team/toolPolicy.js +36 -0
  221. package/src/team/toolPolicy.ts +75 -0
  222. package/src/tools/advisor/advisor.js +115 -50
  223. package/src/tools/advisor/advisor.ts +124 -51
  224. package/src/tools/advisor/advisorDecision.js +71 -0
  225. package/src/tools/advisor/advisorDecision.ts +85 -0
  226. package/src/tools/agent/agent.js +18 -5
  227. package/src/tools/agent/agent.ts +21 -7
  228. package/src/tools/agent/agentControl.js +246 -0
  229. package/src/tools/agent/agentControl.ts +284 -0
  230. package/src/tools/agent/agentTaskService.js +40 -1
  231. package/src/tools/agent/agentTaskService.ts +44 -1
  232. package/src/tools/agent/agentTypes.js +14 -2
  233. package/src/tools/agent/agentTypes.ts +15 -2
  234. package/src/tools/applypatch/applyPatch.js +362 -0
  235. package/src/tools/applypatch/applyPatch.ts +428 -0
  236. package/src/tools/bash/bash.js +22 -15
  237. package/src/tools/bash/bash.ts +23 -17
  238. package/src/tools/capabilities.js +10 -5
  239. package/src/tools/capabilities.ts +10 -5
  240. package/src/tools/computeruse/computerIntent.js +32 -0
  241. package/src/tools/computeruse/computerIntent.ts +46 -0
  242. package/src/tools/computeruse/telemetryLog.js +12 -0
  243. package/src/tools/computeruse/telemetryLog.ts +16 -0
  244. package/src/tools/computeruse/vision/telemetry.js +1 -0
  245. package/src/tools/computeruse/vision/telemetry.ts +1 -0
  246. package/src/tools/effectiveToolSet.js +4 -0
  247. package/src/tools/effectiveToolSet.ts +4 -0
  248. package/src/tools/index.js +21 -2
  249. package/src/tools/index.ts +33 -2
  250. package/src/tools/presentresult/presentresult.js +6 -15
  251. package/src/tools/presentresult/presentresult.ts +6 -15
  252. package/src/tools/read/read.js +62 -9
  253. package/src/tools/read/read.ts +63 -9
  254. package/src/tools/shared/fileUtils.js +1 -2
  255. package/src/tools/shared/fileUtils.ts +1 -2
  256. package/src/tools/shared/toolOutputStore.js +23 -1
  257. package/src/tools/shared/toolOutputStore.ts +22 -1
  258. package/src/tools/skill/skill.js +9 -3
  259. package/src/tools/skill/skill.ts +8 -4
  260. package/src/tools/toolPromptGuides.js +125 -0
  261. package/src/tools/toolPromptGuides.ts +146 -0
  262. package/src/tools/webbrowser/agentBrowserPath.js +19 -9
  263. package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
  264. package/src/tools/webbrowser/browser.js +27 -11
  265. package/src/tools/webbrowser/browser.ts +68 -24
  266. package/src/tools/webbrowser/browserAutomation.js +329 -0
  267. package/src/tools/webbrowser/browserAutomation.ts +467 -0
  268. package/src/tools/webbrowser/browserAxState.js +97 -0
  269. package/src/tools/webbrowser/browserAxState.ts +142 -0
  270. package/src/tools/webbrowser/browserBatch.js +1578 -0
  271. package/src/tools/webbrowser/browserBatch.ts +2382 -0
  272. package/src/tools/webbrowser/browserCdp.js +208 -0
  273. package/src/tools/webbrowser/browserCdp.ts +255 -0
  274. package/src/tools/webbrowser/browserConditions.js +24 -0
  275. package/src/tools/webbrowser/browserConditions.ts +45 -0
  276. package/src/tools/webbrowser/browserContent.js +281 -0
  277. package/src/tools/webbrowser/browserContent.ts +438 -0
  278. package/src/tools/webbrowser/browserControl.js +131 -34
  279. package/src/tools/webbrowser/browserControl.ts +210 -56
  280. package/src/tools/webbrowser/browserDaemon.js +22 -14
  281. package/src/tools/webbrowser/browserDaemon.ts +54 -21
  282. package/src/tools/webbrowser/browserFrames.js +47 -0
  283. package/src/tools/webbrowser/browserFrames.ts +54 -0
  284. package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
  285. package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
  286. package/src/tools/webbrowser/browserLocator.js +140 -0
  287. package/src/tools/webbrowser/browserLocator.ts +194 -0
  288. package/src/tools/webbrowser/browserOutcome.js +10 -17
  289. package/src/tools/webbrowser/browserOutcome.ts +48 -31
  290. package/src/tools/webbrowser/browserRecovery.js +58 -0
  291. package/src/tools/webbrowser/browserRecovery.ts +83 -0
  292. package/src/tools/webbrowser/browserRunRegistry.js +35 -3
  293. package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
  294. package/src/tools/webbrowser/browserService.js +28 -2
  295. package/src/tools/webbrowser/browserService.ts +49 -9
  296. package/src/tools/webbrowser/browserStream.js +132 -33
  297. package/src/tools/webbrowser/browserStream.ts +224 -50
  298. package/src/tools/webbrowser/browserTabs.js +48 -15
  299. package/src/tools/webbrowser/browserTabs.ts +178 -96
  300. package/src/tools/webbrowser/webbrowser.js +679 -144
  301. package/src/tools/webbrowser/webbrowser.ts +975 -236
  302. package/src/tools/webfetch/preapproved.js +0 -2
  303. package/src/tools/webfetch/preapproved.ts +0 -2
  304. package/src/tools/websearch/websearch.js +129 -8
  305. package/src/tools/websearch/websearch.ts +138 -6
  306. package/src/types.ts +459 -40
  307. package/src/ui/App.js +377 -58
  308. package/src/ui/App.tsx +576 -111
  309. package/src/ui/LiveArea.js +14 -8
  310. package/src/ui/LiveArea.tsx +14 -10
  311. package/src/ui/MessageList.js +63 -20
  312. package/src/ui/MessageList.tsx +101 -16
  313. package/src/ui/ModelPicker.js +7 -4
  314. package/src/ui/ModelPicker.tsx +55 -8
  315. package/src/ui/hooks/useChat.js +152 -163
  316. package/src/ui/hooks/useChat.ts +396 -334
  317. package/src/ui/loginCommand.js +56 -0
  318. package/src/ui/loginCommand.ts +63 -0
  319. package/src/ui/toolProgress.js +41 -0
  320. package/src/ui/toolProgress.ts +44 -0
  321. package/src/web/browserBridge.js +20 -2
  322. package/src/web/browserBridge.ts +20 -2
  323. package/src/web/protocol.ts +1897 -194
  324. package/workflows/adaptive-research/workflow.mjs +86 -0
  325. package/workflows/e2e-capabilities/workflow.mjs +102 -0
  326. package/workflows/e2e-llm/workflow.mjs +52 -0
  327. package/workflows/e2e-smoke/workflow.mjs +59 -0
  328. package/workflows/e2e-vision-schema/README.md +48 -0
  329. package/workflows/e2e-vision-schema/workflow.mjs +149 -0
  330. package/workflows/e2e-write-greet/workflow.mjs +37 -0
  331. package/workflows/file-census/README.md +14 -0
  332. package/workflows/file-census/scripts/.gitkeep +2 -0
  333. package/workflows/file-census/workflow.mjs +110 -0
  334. package/workflows/goal/README.md +166 -0
  335. package/workflows/goal/prompts/audit.md +57 -0
  336. package/workflows/goal/prompts/contract.md +56 -0
  337. package/workflows/goal/prompts/iterate.md +37 -0
  338. package/workflows/goal/workflow.mjs +501 -0
  339. package/workflows/illustrate/README.md +90 -0
  340. package/workflows/illustrate/assets/engines.json +20 -0
  341. package/workflows/illustrate/assets/extract-baidu.js +21 -0
  342. package/workflows/illustrate/assets/extract-bing.js +33 -0
  343. package/workflows/illustrate/assets/extract-google.js +31 -0
  344. package/workflows/illustrate/workflow.mjs +301 -0
  345. package/workflows/plan/workflow.mjs +389 -0
  346. package/workflows/question-crops/README.md +35 -0
  347. package/workflows/question-crops/prompts/inventory.md +23 -0
  348. package/workflows/question-crops/scripts/image-ops.mjs +305 -0
  349. package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
  350. package/workflows/question-crops/workflow.mjs +172 -0
  351. package/workflows/ralph-loop/workflow.mjs +162 -0
  352. package/workflows/research/README.md +74 -0
  353. package/workflows/research/prompts/s0-goal.md +43 -0
  354. package/workflows/research/prompts/s1-early-exit.md +22 -0
  355. package/workflows/research/prompts/s1-map.md +58 -0
  356. package/workflows/research/prompts/s2-options.md +53 -0
  357. package/workflows/research/prompts/s3-deep-dive.md +56 -0
  358. package/workflows/research/prompts/s4-verify.md +74 -0
  359. package/workflows/research/prompts/s5-synthesis.md +81 -0
  360. package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
  361. package/workflows/research/prompts/s6-package.md +32 -0
  362. package/workflows/research/scripts/build-report.mjs +98 -0
  363. package/workflows/research/workflow.mjs +288 -0
  364. package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
  365. package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
  366. package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
  367. package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
  368. package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
  369. package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
  370. package/workflows/wechat-reading-shots/workflow.mjs +266 -0
  371. package/workflows/wrong-question-docx/README.md +55 -0
  372. package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
  373. package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
  374. package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
  375. package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
  376. package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
  377. package/workflows/wrong-question-docx/workflow.mjs +485 -0
  378. package/plugins/workflow-runner/src/agentCommon.js +0 -354
  379. package/plugins/workflow-runner/src/agentCommon.ts +0 -435
  380. package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
  381. package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
  382. package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
  383. package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
  384. package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
  385. package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
  386. package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
  387. package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
  388. package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
  389. package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
  390. package/plugins/workflow-runner/src/dslManifest.js +0 -72
  391. package/plugins/workflow-runner/src/dslManifest.ts +0 -127
  392. package/plugins/workflow-runner/src/expressions.js +0 -639
  393. package/plugins/workflow-runner/src/expressions.ts +0 -640
  394. package/plugins/workflow-runner/src/loader.js +0 -573
  395. package/plugins/workflow-runner/src/loader.ts +0 -721
  396. package/plugins/workflow-runner/src/multimodal.js +0 -38
  397. package/plugins/workflow-runner/src/multimodal.ts +0 -64
  398. package/plugins/workflow-runner/src/recovery.js +0 -126
  399. package/plugins/workflow-runner/src/recovery.ts +0 -156
  400. package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
  401. package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
  402. package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -42
  403. package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -52
  404. package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
  405. package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
  406. package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
  407. package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
  408. package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
  409. package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
  410. package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
  411. package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
  412. package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -227
  413. package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -274
  414. package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -61
  415. package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -72
  416. package/plugins/workflow-runner/src/stepPipeline.js +0 -277
  417. package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
  418. package/plugins/workflow-runner/test/agent.test.ts +0 -587
  419. package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
  420. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
  421. package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
  422. package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -167
  423. package/plugins/workflow-runner/test/context-files.test.ts +0 -320
  424. package/plugins/workflow-runner/test/e2e.test.ts +0 -370
  425. package/plugins/workflow-runner/test/e2eNewLayout.test.ts +0 -314
  426. package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -234
  427. package/plugins/workflow-runner/test/expressions.test.ts +0 -439
  428. package/plugins/workflow-runner/test/foundation.test.ts +0 -126
  429. package/plugins/workflow-runner/test/graceful.test.ts +0 -139
  430. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
  431. package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
  432. package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
  433. package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
  434. package/plugins/workflow-runner/test/loader.test.ts +0 -617
  435. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
  436. package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
  437. package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
  438. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -106
  439. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
  440. package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
  441. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -221
  442. package/plugins/workflow-runner/test/recovery.test.ts +0 -265
  443. package/plugins/workflow-runner/test/research.load.test.ts +0 -328
  444. package/plugins/workflow-runner/test/runner.test.ts +0 -911
  445. package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -436
  446. package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
  447. package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -198
  448. package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
  449. package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -354
  450. package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -324
  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 -201
  468. package/workflows/research.yaml +0 -873
  469. package/workflows/schema.json +0 -176
  470. package/workflows/webppt.yaml +0 -496
  471. package/workflows/wechat-reading-shots.yaml +0 -315
  472. package/workflows/word.yaml +0 -720
  473. package/workflows/youtube-shorts.yaml +0 -202
  474. /package/workflows/{research.md → research/DESIGN.md} +0 -0
package/src/loop.js 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
  * "工作过程显示"事件:
@@ -28,9 +28,10 @@
28
28
  */
29
29
  import crypto from 'node:crypto';
30
30
  import { autoCompactIfNeeded, getCompactThreshold } from './context/compact.js';
31
- import { microCompact, incrementTurn, expireOldEntries } from './context/microCompactLite.js';
31
+ import { microCompact, incrementTurn, expireOldEntries, reclaimOldToolResults, resetCacheEdits, getToolResultPolicy, } from './context/microCompactLite.js';
32
32
  import { isPromptTooLongError, reactiveCompactIfApplicable, } from './context/reactiveCompact.js';
33
33
  import { snipCompactIfNeeded } from './context/snipCompact.js';
34
+ import { archiveToolResult } from './context/toolResultArchive.js';
34
35
  import { countMessagesTokens, countTextTokens, estimateToolsTokens, } from './context/tokenCounter.js';
35
36
  import { buildContextBudgetPlan, fitToolsForEmergency } from './context/budgetPlan.js';
36
37
  import { resizeContextBundle } from './context/contextBundle.js';
@@ -40,20 +41,26 @@ import { ALL_TOOLS, executeTool as defaultExecuteTool, getToolByName, getToolGat
40
41
  import { configService } from './config/configService.js';
41
42
  import { capabilityForToolName } from './tools/capabilities.js';
42
43
  import { createEffectiveToolSet } from './tools/effectiveToolSet.js';
44
+ import { classifyComputerAutomationIntent } from './tools/computeruse/computerIntent.js';
45
+ import { recordComputerUseExposure } from './tools/computeruse/telemetryLog.js';
46
+ import { filterToolsForProvider } from './team/toolPolicy.js';
43
47
  import { hydrateSessionWorktree } from './tools/worktree/sessionWorktree.js';
44
48
  import { capabilitiesForTools, enabledPolicyIds, } from './prompts/policyRegistry.js';
45
49
  import { EvidenceLedger } from './harness/evidenceLedger.js';
46
50
  import { StallGuard } from './harness/stallGuard.js';
47
51
  import { validateFinalClaims } from './harness/claimValidation.js';
52
+ import { AdvisorCoordinator, isWorkspaceMutationTool, } from './advisor/advisorCoordinator.js';
53
+ import { classifyCommand } from './irreversible/rules.js';
48
54
  import { createAgentRuntimeContext, } from './runtime/agentRuntimeContext.js';
49
- const DELIVERY_ARTIFACT = '(?:文件|合同|文档|表格|报告|图片|图像|海报|幻灯片|PPT|PDF|代码|脚本|压缩包|网页|页面|HTML|DOCX|XLSX|CSV|ZIP)';
50
- const DELIVERY_ACTION = '(?:发|发送|交付|提交|给|传|制作|生成|创建|导出|输出|修改|更新|完成)';
51
- const ENGLISH_DELIVERY_ARTIFACT = '(?:file|document|report|spreadsheet|workbook|slides?|presentation|pdf|image|code|script|archive|zip|html|webpage|website)';
52
- const ENGLISH_DELIVERY_ACTION = '(?:send|deliver|submit|provide|create|generate|export|update|modify|finish|produce)';
55
+ import { stripThinkBlocks } from './utils/stripThink.js';
56
+ const DELIVERY_ARTIFACT = '(?:文件|合同|协议|方案|文档|表格|报告|图片|图像|海报|幻灯片|PPT|PDF|代码|脚本|压缩包|网页|页面|HTML|DOCX|XLSX|CSV|ZIP)';
57
+ const DELIVERY_ACTION = '(?:发送|(?<![开研])发|交付|提交|(?<!宣)传(?!统)|制作|生成|创建|导出|输出|修改|更新|完成|写|起草|做|设计|整理)';
58
+ const ENGLISH_DELIVERY_ARTIFACT = '(?:file|contract|document|report|spreadsheet|workbook|slides?|presentation|pdf|image|code|script|archive|zip|html|webpage|website)';
59
+ const ENGLISH_DELIVERY_ACTION = '(?:send|deliver|submit|provide|create|generate|export|update|modify|finish|produce|draft|write|make|build)';
53
60
  /** Whether this turn explicitly asks for a concrete artifact that should be formally delivered. */
54
61
  export function expectsFormalDelivery(input) {
55
62
  const compact = input.replace(/\s+/g, '');
56
- const chinese = new RegExp(`${DELIVERY_ACTION}(?:给)?我?.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}${DELIVERY_ACTION}(?:给)?我?`, 'i');
63
+ const chinese = new RegExp(`${DELIVERY_ACTION}(?:给)?我?.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}${DELIVERY_ACTION}(?:给)?我?|给我.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}给我`, 'i');
57
64
  const english = new RegExp(`\\b${ENGLISH_DELIVERY_ACTION}\\b.{0,80}\\b${ENGLISH_DELIVERY_ARTIFACT}\\b|\\b${ENGLISH_DELIVERY_ARTIFACT}\\b.{0,80}\\b${ENGLISH_DELIVERY_ACTION}\\b`, 'i');
58
65
  return chinese.test(compact) || english.test(input);
59
66
  }
@@ -92,7 +99,15 @@ function getToolsSchemaTokens(tools, signature) {
92
99
  return cached;
93
100
  }
94
101
  /**
95
- * 顾问咨询配额:基线 1 次,之后每累计 N 次工具调用解锁 1 次,封顶 3 次。
102
+ * 主工具循环的默认轮数上限 —— **唯一真值**。
103
+ *
104
+ * 2026-08 从 50 提到 200(4 倍)。它此前是 `options.maxTurns ?? 200` 里的一个裸字面量,
105
+ * 没有名字也就没有引用点,于是「跟着它一起动的东西」(顾问配额封顶)改不动 ——
106
+ * 见下面的 `ADVISOR_QUOTA_MAX`。
107
+ */
108
+ export const DEFAULT_MAX_TURNS = 200;
109
+ /**
110
+ * 顾问咨询配额:基线 1 次,之后每累计 N 次工具调用解锁 1 次,封顶见 ADVISOR_QUOTA_MAX。
96
111
  *
97
112
  * 曾经是硬编码 1 次/回合。那个数字隐含「一个用户回合 = 一个决策点」的假设,
98
113
  * 在短问答里成立,在「继续,完成后续所有工作」这类一句话跑几十步的长回合里
@@ -105,11 +120,48 @@ function getToolsSchemaTokens(tools, signature) {
105
120
  * 刷工具调用换配额的成本远高于收益。
106
121
  */
107
122
  const ADVISOR_QUOTA_BASE = 1;
108
- const ADVISOR_QUOTA_MAX = 3;
109
123
  const ADVISOR_QUOTA_UNLOCK_EVERY_TOOL_CALLS = 15;
124
+ /**
125
+ * 配额封顶**由 `DEFAULT_MAX_TURNS` 推导,不是独立的魔术数字**。
126
+ *
127
+ * 上面那段注释的论据是「一个长回合里真正值钱的咨询点至少三个」——那是按当时
128
+ * `maxTurns = 50` 算的。轮数上限后来提到 200(4 倍工作量塞进同一个回合),
129
+ * 「高风险批次做完」这类节点随之变多,封顶却没跟着动,于是配额重新变成瓶颈:
130
+ * 这正是它当年从「硬编码 1 次」被改掉的同一个毛病,只是换了个刻度。
131
+ *
132
+ * 用公式而不是再写一个字面量,两者才会一起动:
133
+ * 50 轮 → 3(复现历史值) · 200 轮 → 5 · 下限恒为 3
134
+ */
135
+ const ADVISOR_QUOTA_MAX = Math.max(3, Math.round(DEFAULT_MAX_TURNS / 40));
110
136
  export function advisorQuotaFor(executedToolCalls) {
111
137
  return Math.min(ADVISOR_QUOTA_MAX, ADVISOR_QUOTA_BASE + Math.floor(executedToolCalls / ADVISOR_QUOTA_UNLOCK_EVERY_TOOL_CALLS));
112
138
  }
139
+ function advisorSemanticsFor(call) {
140
+ let parsed = {};
141
+ try {
142
+ parsed = JSON.parse(call.function.arguments);
143
+ }
144
+ catch { /* zod layer reports malformed input */ }
145
+ const mutatesWorkspace = isWorkspaceMutationTool(call.function.name);
146
+ if (call.function.name === 'Bash' || call.function.name === 'PowerShell') {
147
+ const command = typeof parsed.command === 'string' ? parsed.command : '';
148
+ const risk = command ? classifyCommand(command) : null;
149
+ return { mutatesWorkspace, highRisk: risk?.riskLevel === 'critical', riskReason: risk?.why };
150
+ }
151
+ if (call.function.name === 'ComputerUse') {
152
+ const highRisk = parsed.intent === 'submit'
153
+ || (parsed.action === 'window' && parsed.windowOperation === 'close');
154
+ return { mutatesWorkspace, highRisk, ...(highRisk ? { riskReason: '原生界面操作不可逆或可能提交外部动作' } : {}) };
155
+ }
156
+ if (call.function.name === 'WebBrowser' || call.function.name === 'BrowserBatch') {
157
+ const highRisk = parsed.intent === 'submit'
158
+ || (call.function.name === 'BrowserBatch'
159
+ && Array.isArray(parsed.steps)
160
+ && parsed.steps.some((step) => typeof step === 'object' && step !== null && step.intent === 'submit'));
161
+ return { mutatesWorkspace, highRisk, ...(highRisk ? { riskReason: '浏览器动作将进行发布、购买、删除或最终提交' } : {}) };
162
+ }
163
+ return { mutatesWorkspace, highRisk: false };
164
+ }
113
165
  /**
114
166
  * 执行一次"用户输入 → 模型回答完成"的完整流程。
115
167
  *
@@ -120,8 +172,8 @@ export async function* runQuery(userInput, options) {
120
172
  // Tool screenshots are transient evidence for the tool continuation that produced them.
121
173
  // Never carry a prior turn's base64 image into a new user request.
122
174
  evictPriorVisionShots(history);
123
- const maxTurns = options.maxTurns ?? 50;
124
- const runtimeContext = options.runtimeContext ?? createAgentRuntimeContext({
175
+ const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
176
+ const baseRuntimeContext = options.runtimeContext ?? createAgentRuntimeContext({
125
177
  surface: 'headless',
126
178
  profile: 'main',
127
179
  cwd: process.cwd(),
@@ -135,10 +187,22 @@ export async function* runQuery(userInput, options) {
135
187
  computerIntervention: options.computerIntervention,
136
188
  toolGate: options.toolGate,
137
189
  });
190
+ const computerIntent = baseRuntimeContext.computerIntent
191
+ ?? classifyComputerAutomationIntent(userInput, sessionState?.computerAutomation);
192
+ const runtimeContext = createAgentRuntimeContext({
193
+ ...baseRuntimeContext,
194
+ computerIntent,
195
+ });
196
+ if (runtimeContext.computer) {
197
+ void recordComputerUseExposure(computerIntent.needed, computerIntent.source);
198
+ }
199
+ if (!computerIntent.needed && sessionState)
200
+ delete sessionState.computerAutomation;
138
201
  // DI fallback:测试可传 stub,生产走真实模块单例
139
202
  const chatFn = options.chat ?? defaultChat;
140
203
  const executeToolFn = options.executeTool ?? defaultExecuteTool;
141
204
  const explicitFileDeliveryRequested = expectsFormalDelivery(userInput);
205
+ const currentUserMessageId = options.userMessageId ?? crypto.randomUUID();
142
206
  const transientFor = (tools) => {
143
207
  const opaque = options.transientContext?.trim() ?? '';
144
208
  const bundle = options.transientBundle;
@@ -170,7 +234,15 @@ export async function* runQuery(userInput, options) {
170
234
  history,
171
235
  sessionState,
172
236
  // 配额随本回合工作量增长(见 advisorQuotaFor);下面每轮工具执行完后上调。
173
- advisorCalls: { used: 0, max: advisorQuotaFor(0) },
237
+ // mandatoryUsed/mandatoryMax 是独立的"强制检查点"配额池,不随工具调用数
238
+ // 增长——它是安全闸门的固定配额,不是"干得越多奖励越多"的自愿咨询配额;
239
+ // 复用 ADVISOR_QUOTA_MAX 而不新增字面量,避免和自愿池的封顶各写各的漂移。
240
+ advisorCalls: {
241
+ used: 0,
242
+ max: advisorQuotaFor(0),
243
+ mandatoryUsed: 0,
244
+ mandatoryMax: ADVISOR_QUOTA_MAX,
245
+ },
174
246
  advisorInvocation: 'agent',
175
247
  // Agent 等派生工具起嵌套子查询所需的运行态:与主循环同一 provider、
176
248
  // 同一审批闸门(子 agent 不绕过宿主策略)。agentCalls 与 advisorCalls 同构,
@@ -185,6 +257,8 @@ export async function* runQuery(userInput, options) {
185
257
  currentUserInput: userInput,
186
258
  userMessageId: options.userMessageId,
187
259
  memoryAccess: runtimeContext.memoryAccess,
260
+ conversationMode: runtimeContext.conversationMode ?? 'private',
261
+ surface: runtimeContext.surface,
188
262
  workspaceId: runtimeContext.workspaceId,
189
263
  sessionId: runtimeContext.sessionId,
190
264
  cwd: runtimeContext.cwd,
@@ -195,9 +269,10 @@ export async function* runQuery(userInput, options) {
195
269
  // 实际下发给模型的工具集:allowedTools 未传时给全量;
196
270
  // 传入数组时严格按数组过滤,[] 表示本轮不下发任何工具。
197
271
  // 仅影响呈现给 LLM 的工具清单,不影响 executeTool 的内部调度能力。
198
- const hostTools = options.availableTools ?? ALL_TOOLS;
272
+ const hostTools = filterToolsForProvider(options.availableTools ?? ALL_TOOLS, provider);
199
273
  const availableTools = hostTools.filter((tool) => (tool.name !== 'Agent' || subagents.enabled)
200
- && (tool.name !== 'ComputerUse' || runtimeContext.computer !== undefined));
274
+ && (tool.name !== 'ComputerUse'
275
+ || (runtimeContext.computer !== undefined && runtimeContext.computerIntent?.needed === true)));
201
276
  const effectiveToolSet = createEffectiveToolSet({
202
277
  availableTools,
203
278
  runtime: runtimeContext,
@@ -210,6 +285,30 @@ export async function* runQuery(userInput, options) {
210
285
  && runtimeContext.surface !== 'workflow'
211
286
  && effectiveToolSet.names.has('PresentResult');
212
287
  let currentEffectiveToolSet = effectiveToolSet;
288
+ const advisorCoordinator = new AdvisorCoordinator({
289
+ history,
290
+ currentRequest: userInput,
291
+ currentRequestMessageId: currentUserMessageId,
292
+ advisorAvailable: effectiveToolSet.names.has('Advisor')
293
+ && effectiveToolSet.names.has('AdvisorDecision'),
294
+ });
295
+ toolCallContext.advisorRuntime = advisorCoordinator;
296
+ // 组装每轮请求的易变上下文。三个构建点(首轮 / autoCompact 后 / snip 后)共用,
297
+ // 避免 Advisor 相关提醒在复制粘贴中三处漂移。提醒均为 transient:只进当轮请求副本,
298
+ // 不进会话历史(与 DELIVERY_CORRECTION_REMINDER 同构,见 buildModelMessages)。
299
+ const buildRequestTransientContext = () => {
300
+ const advisorAvailable = currentEffectiveToolSet.names.has('Advisor')
301
+ && currentEffectiveToolSet.names.has('AdvisorDecision');
302
+ const parts = [
303
+ transientFor(activeTools),
304
+ deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
305
+ advisorAvailable ? advisorCoordinator.transientReminder() : '',
306
+ advisorAvailable
307
+ ? advisorCoordinator.usageLine(toolCallContext.advisorCalls.used, toolCallContext.advisorCalls.max)
308
+ : '',
309
+ ];
310
+ return parts.filter(Boolean).join('\n\n');
311
+ };
213
312
  let lastRequestTransientContext = [options.transientBundle?.text, options.transientContext]
214
313
  .filter(Boolean).join('\n\n');
215
314
  const evidenceLedger = new EvidenceLedger();
@@ -269,6 +368,16 @@ export async function* runQuery(userInput, options) {
269
368
  message: `未作为图片附加:${skipped.source}(${skipped.reason}),已按普通文本处理。`,
270
369
  };
271
370
  }
371
+ let visionRequiredThisTurn = Array.isArray(userContent)
372
+ && userContent.some((part) => part.type === 'image_url' || part.type === 'video_url');
373
+ if (visionRequiredThisTurn && provider.supportsVision === false) {
374
+ yield {
375
+ type: 'error',
376
+ error: '当前模型已配置为不支持视觉输入,图片未发送。请切换到支持图片的模型。',
377
+ code: 'llm_error',
378
+ };
379
+ return;
380
+ }
272
381
  // 1. 用户消息入栈(v2:填 timestamp,让 MessageList 的 turn 边界横线
273
382
  // 能拿到首条 user 的真实时间,否则 fallback Date.now() 会丢失"提交时刻"语义)
274
383
  const userMessage = {
@@ -277,12 +386,16 @@ export async function* runQuery(userInput, options) {
277
386
  ...(options.userAttachments?.length ? { attachments: options.userAttachments } : {}),
278
387
  ...(options.userBrowserReferences?.length ? { browserReferences: options.userBrowserReferences } : {}),
279
388
  timestamp: Date.now(),
280
- id: options.userMessageId ?? crypto.randomUUID(),
389
+ id: currentUserMessageId,
390
+ ...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
281
391
  };
282
- history.push(userMessage);
283
- // 立即通知 UI:用户消息已入栈,触发 bump 让 <Static> 马上把它 commit 进 scrollback,
284
- // 不必等整轮 assistant 落定(保证用户输入第一时间置顶,符合 T-A-O-R 展示顺序)。
285
- yield { type: 'user_message_committed', message: userMessage };
392
+ const alreadyCommitted = history.some((message) => message.role === 'user' && message.id === userMessage.id);
393
+ if (!alreadyCommitted) {
394
+ history.push(userMessage);
395
+ // 立即通知 UI:用户消息已入栈,触发 bump 让 <Static> 马上把它 commit 进 scrollback,
396
+ // 不必等整轮 assistant 落定(保证用户输入第一时间置顶,符合 T-A-O-R 展示顺序)。
397
+ yield { type: 'user_message_committed', message: userMessage };
398
+ }
286
399
  // 反应式压缩自救:本 runQuery 内最多触发 MAX_REACTIVE_PER_QUERY 次(防本轮死循环);
287
400
  // 跨 runQuery 的连续失败由 reactiveCompact.ts 的 circuit breaker(consecutiveFailures)兜底。
288
401
  let reactiveCount = 0;
@@ -290,6 +403,13 @@ export async function* runQuery(userInput, options) {
290
403
  // 上一轮 LLM 返回的真实 prompt token 数(done.promptTokens;provider 开 include_usage 才有)。
291
404
  // 作为 autoCompact 的权威判据,校正本地估算的系统性低估;压缩后清空(历史已变,旧值失真)。
292
405
  let lastPromptTokens;
406
+ // ── 回合级资源账本 ────────────────────────────────────────────────
407
+ // ⚠️ 与 lastPromptTokens 生命周期**故意不同**:那个是「当前 history 有多大」的快照,
408
+ // 压缩后必须清零;这个是「本回合总共花了多少」的累计,压缩不影响已经花掉的钱。
409
+ // ⚠️ 它只记账,**不参与任何控制流** —— loop 绝不因预算中断进行中的工具循环。
410
+ const turnUsage = { inputTokens: 0, outputTokens: 0, totalCalls: 0, reportedCalls: 0 };
411
+ // 归档退化只报一次:每条超长结果都报会把上下文刷满,反而盖掉别的信号。
412
+ let archiveDegradedWarned = false;
293
413
  // 提示词缓存观测位(纯展示,不参与任何控制流)。与 lastPromptTokens 同生命周期:
294
414
  // 压缩后一并清空,避免把压缩前的命中数据挂到压缩后的新历史上误导排障。
295
415
  let lastCachedPromptTokens;
@@ -302,6 +422,15 @@ export async function* runQuery(userInput, options) {
302
422
  turn++;
303
423
  incrementTurn(sessionState?.microCompact);
304
424
  expireOldEntries(sessionState?.microCompact);
425
+ // Path C 批量回收旧工具结果:可重放的观察类工具结果(Read/Grep/Bash…)
426
+ // 超过阈值时只保留热尾部。必须在 buildContextBudgetPlan 之前跑——预算估算和
427
+ // autoCompact 判据直接看到瘦身后的历史,避免无谓的全量压缩。
428
+ // cacheEdits 模式(Anthropic 格式 + provider.cacheEdits):不改 history,删除目标
429
+ // 记入状态、由请求层组装 cache_edits 发送--服务端 KV 原地删除,前缀缓存完整保留。
430
+ // 本地估算的虚高部分(服务端已删内容)由 autoCompactIfNeeded 的 serverDeletedTokens
431
+ // 校正,判据不会因此过早触发压缩。
432
+ const cacheEditsMode = provider.apiFormat === 'anthropic' && provider.cacheEdits === true;
433
+ reclaimOldToolResults(history, sessionState?.microCompact, cacheEditsMode ? 'cacheEdits' : 'inline');
305
434
  if (signal?.aborted) {
306
435
  pushManualInterruptMarker(history);
307
436
  if (!workStarted) {
@@ -324,10 +453,7 @@ export async function* runQuery(userInput, options) {
324
453
  }
325
454
  // 2. 自动压缩(判据 = max(上一轮真实 usage, 历史估算 + 工具 schema),
326
455
  // 阈值 provider 无关、按 contextWindow × compactRatio 比例触发)
327
- let requestTransientContext = [
328
- transientFor(activeTools),
329
- deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
330
- ].filter(Boolean).join('\n\n');
456
+ let requestTransientContext = buildRequestTransientContext();
331
457
  lastRequestTransientContext = requestTransientContext;
332
458
  const preCompactBudget = buildContextBudgetPlan({
333
459
  provider,
@@ -341,6 +467,11 @@ export async function* runQuery(userInput, options) {
341
467
  actualPromptTokens: lastPromptTokens,
342
468
  toolsTokens: getToolsSchemaTokens(activeTools, effectiveToolSet.signature),
343
469
  transientTokens: countTextTokens(requestTransientContext),
470
+ // cacheEdits 判据校正:本地 history 未瘦身,扣掉服务端已删部分的估算,
471
+ // 防止虚高估算劫持 Math.max 导致压缩过早触发(压缩会重建整个前缀缓存)。
472
+ ...(cacheEditsMode && sessionState?.microCompact
473
+ ? { serverDeletedTokens: sessionState.microCompact.cacheEdits.activeDeletedTokens }
474
+ : {}),
344
475
  signal,
345
476
  promptCache: options.promptCache,
346
477
  chat: chatFn,
@@ -350,6 +481,10 @@ export async function* runQuery(userInput, options) {
350
481
  yield { type: 'stage_change', stage: 'compacting' };
351
482
  history.length = 0;
352
483
  history.push(...compact.messages);
484
+ // cacheEdits 状态随压缩作废:pinned 的翻译后下标指向已不存在的消息序列,
485
+ // 服务端缓存前缀也已失效——不重置会导致删除指令插错位置甚至重复删除。
486
+ if (sessionState?.microCompact)
487
+ resetCacheEdits(sessionState.microCompact);
353
488
  lastPromptTokens = undefined;
354
489
  lastCachedPromptTokens = undefined;
355
490
  lastCacheMissTokens = undefined;
@@ -380,6 +515,9 @@ export async function* runQuery(userInput, options) {
380
515
  if (snipped.messagesRemoved > 0) {
381
516
  history.length = 0;
382
517
  history.push(...snipped.messages);
518
+ // 同 compact:snip 砍头后 pinned 下标失位,cacheEdits 必须重置
519
+ if (sessionState?.microCompact)
520
+ resetCacheEdits(sessionState.microCompact);
383
521
  lastPromptTokens = undefined;
384
522
  lastCachedPromptTokens = undefined;
385
523
  lastCacheMissTokens = undefined;
@@ -415,10 +553,7 @@ export async function* runQuery(userInput, options) {
415
553
  }
416
554
  // Re-open transient capacity after compaction. If the compacted tail is still
417
555
  // too large, trim old history before sacrificing any model-visible ability.
418
- requestTransientContext = [
419
- transientFor(activeTools),
420
- deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
421
- ].filter(Boolean).join('\n\n');
556
+ requestTransientContext = buildRequestTransientContext();
422
557
  lastRequestTransientContext = requestTransientContext;
423
558
  let postCompactBudget = buildContextBudgetPlan({
424
559
  provider,
@@ -443,10 +578,7 @@ export async function* runQuery(userInput, options) {
443
578
  messagesRemoved: snipped.messagesRemoved,
444
579
  tokensFreed: snipped.tokensFreed,
445
580
  };
446
- requestTransientContext = [
447
- transientFor(activeTools),
448
- deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
449
- ].filter(Boolean).join('\n\n');
581
+ requestTransientContext = buildRequestTransientContext();
450
582
  lastRequestTransientContext = requestTransientContext;
451
583
  postCompactBudget = buildContextBudgetPlan({
452
584
  provider,
@@ -470,6 +602,8 @@ export async function* runQuery(userInput, options) {
470
602
  runtime: runtimeContext,
471
603
  requiredNames: [...effectiveToolSet.requiredNames],
472
604
  });
605
+ advisorCoordinator.setAvailable(currentEffectiveToolSet.names.has('Advisor')
606
+ && currentEffectiveToolSet.names.has('AdvisorDecision'));
473
607
  if (emergencyFit.removedNames.length > 0) {
474
608
  yield {
475
609
  type: 'warning',
@@ -509,12 +643,22 @@ export async function* runQuery(userInput, options) {
509
643
  workStarted = true;
510
644
  try {
511
645
  const modelMessages = buildModelMessages(history, requestTransientContext, userMessage.id);
646
+ advisorCoordinator.markTransientDelivered();
512
647
  for await (const ev of chatFn({
513
648
  provider,
514
649
  messages: modelMessages,
515
650
  tools: currentEffectiveToolSet.tools,
516
651
  signal,
652
+ conversationMode: runtimeContext.conversationMode ?? 'private',
517
653
  promptCache: options.promptCache,
654
+ visionPolicy: visionRequiredThisTurn ? 'required' : 'optional-history',
655
+ // cacheEdits 回收状态(同一会话级对象跨轮复用;compact 后由 resetCacheEdits 清空)
656
+ ...(cacheEditsMode && sessionState?.microCompact
657
+ ? { cacheEdits: sessionState.microCompact.cacheEdits }
658
+ : {}),
659
+ ...(options.responseFormat
660
+ ? { responseFormat: options.responseFormat }
661
+ : {}),
518
662
  })) {
519
663
  if (ev.type === 'text_delta') {
520
664
  if (!stageStreamingYielded) {
@@ -541,6 +685,10 @@ export async function* runQuery(userInput, options) {
541
685
  reasoningDetails.push(...ev.items);
542
686
  }
543
687
  }
688
+ else if (ev.type === 'usage') {
689
+ if (typeof ev.promptTokens === 'number')
690
+ lastPromptTokens = ev.promptTokens;
691
+ }
544
692
  else if (ev.type === 'done') {
545
693
  // R2:记下流的终止原因。'length'(max_tokens 截断)会在本轮收尾时触发 warning。
546
694
  lastFinishReason = ev.stopReason;
@@ -556,6 +704,17 @@ export async function* runQuery(userInput, options) {
556
704
  lastCacheWriteTokens = ev.cacheWriteTokens;
557
705
  if (ev.promptCacheUsage)
558
706
  lastPromptCacheUsage = ev.promptCacheUsage;
707
+ // 回合级记账(与上面的 lastPromptTokens 不同:那个压缩后要清零,这个不清)。
708
+ // ⚠️ 只有 provider 真报了才计入 reportedCalls —— 「没人告诉我」不能被当成
709
+ // 「真的是 0」,否则预算闸永远不触发。覆盖率由 trustworthy 如实暴露。
710
+ turnUsage.totalCalls += 1;
711
+ const reportedIn = typeof ev.promptTokens === 'number' ? ev.promptTokens : undefined;
712
+ const reportedOut = typeof ev.outputTokens === 'number' ? ev.outputTokens : undefined;
713
+ if (reportedIn !== undefined || reportedOut !== undefined) {
714
+ turnUsage.reportedCalls += 1;
715
+ turnUsage.inputTokens += reportedIn ?? 0;
716
+ turnUsage.outputTokens += reportedOut ?? 0;
717
+ }
559
718
  }
560
719
  }
561
720
  }
@@ -644,21 +803,53 @@ export async function* runQuery(userInput, options) {
644
803
  ...(reasoningDetails.length > 0 ? { reasoning_details: reasoningDetails } : {}),
645
804
  timestamp: Date.now(),
646
805
  id: crypto.randomUUID(),
806
+ ...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
647
807
  };
648
- history.push(assistantMsg);
649
- yield { type: 'assistant_message', message: assistantMsg };
650
808
  // 4. 没有工具调用 → 整轮交互结束
651
809
  if (toolCallsByIndex.length === 0) {
810
+ const emptyResponse = stripThinkBlocks(assistantText).length === 0;
652
811
  // A model occasionally writes a path/link in prose and stops without formally
653
812
  // presenting it. Give it one model-only corrective pass; the reminder is
654
813
  // transient and therefore never pollutes persisted conversation history.
655
- if (formalDeliveryRequired
814
+ if (!emptyResponse && formalDeliveryRequired
656
815
  && !resultPresented
657
816
  && !deliveryCorrectionAttempted
658
817
  && turn < maxTurns) {
659
818
  deliveryCorrectionAttempted = true;
819
+ yield {
820
+ type: 'assistant_draft_discarded',
821
+ reason: 'formal_delivery',
822
+ textLength: assistantText.length,
823
+ };
660
824
  continue;
661
825
  }
826
+ const deliveryCheckpoint = emptyResponse
827
+ ? { allow: true }
828
+ : advisorCoordinator.beforeDelivery();
829
+ if (!deliveryCheckpoint.allow && turn < maxTurns) {
830
+ yield {
831
+ type: 'assistant_draft_discarded',
832
+ reason: 'advisor_checkpoint',
833
+ textLength: assistantText.length,
834
+ };
835
+ continue;
836
+ }
837
+ if (emptyResponse) {
838
+ // A no-tool response is already the model's terminal signal. Do not turn it
839
+ // into another model round, and do not persist/broadcast an empty bubble.
840
+ yield {
841
+ type: 'assistant_draft_discarded',
842
+ reason: 'empty_response',
843
+ textLength: assistantText.length,
844
+ };
845
+ }
846
+ else {
847
+ history.push(assistantMsg);
848
+ yield {
849
+ type: 'assistant_message',
850
+ message: assistantMsg,
851
+ };
852
+ }
662
853
  // R2:最终回答被 provider 因 max_tokens 截断(finish_reason='length')时,
663
854
  // 不能静默当正常 end_turn 收尾 —— 否则 -p 宿主会拿到半截答案却以为成功。
664
855
  // 走通用 warning(text/json 都落 stderr,不污染 json 那一行 stdout 契约)。
@@ -698,28 +889,42 @@ export async function* runQuery(userInput, options) {
698
889
  : {}),
699
890
  promptCacheUsage: lastPromptCacheUsage,
700
891
  };
892
+ // 资源账本挂到**每一条** turn_done 出口上。⚠️ 别只挂在 'completed' 那条:
893
+ // 失败、交付不全、声明未核实的回合照样烧了 token,漏掉它们会让预算系统性少算,
894
+ // 而少算的方向恰好是「让循环继续跑」—— 这正是本仓库记过账的
895
+ // 「turn_done.completionStatus 被 print.ts 整条丢弃」同一类错误的反面。
896
+ const usage = {
897
+ ...turnUsage,
898
+ trustworthy: turnUsage.totalCalls > 0 && turnUsage.reportedCalls === turnUsage.totalCalls,
899
+ };
701
900
  if (formalDeliveryRequired && !resultPresented) {
702
901
  const warning = '交付未完成:用户明确要求提供文件,但本轮没有成功提交可打开的文件结果。';
703
902
  yield { type: 'warning', message: warning };
704
- yield { type: 'turn_done', completionStatus: 'delivery_incomplete', warning };
903
+ yield { type: 'turn_done', completionStatus: 'delivery_incomplete', warning, usage };
705
904
  }
706
905
  else if (resultPresented && presentedResultUnverified) {
707
906
  const warning = '结果已提交,但至少一个文件尚未通过结构化验证。';
708
907
  yield { type: 'warning', message: warning };
709
- yield { type: 'turn_done', completionStatus: 'delivery_unverified', warning };
908
+ yield { type: 'turn_done', completionStatus: 'delivery_unverified', warning, usage };
710
909
  }
711
910
  else if (!claimValidation.verified) {
712
911
  yield {
713
912
  type: 'turn_done',
714
913
  completionStatus: 'claims_unverified',
715
914
  warning: claimValidation.warnings.join('\n'),
915
+ usage,
716
916
  };
717
917
  }
718
918
  else {
719
- yield { type: 'turn_done', completionStatus: 'completed' };
919
+ yield { type: 'turn_done', completionStatus: 'completed', usage };
720
920
  }
721
921
  return;
722
922
  }
923
+ history.push(assistantMsg);
924
+ yield {
925
+ type: 'assistant_message',
926
+ message: assistantMsg,
927
+ };
723
928
  // 5. 执行工具调用
724
929
  //
725
930
  // R1:按 isConcurrencySafe 决定并发策略 —— 工具早就声明了这个字段,此前却没有
@@ -762,6 +967,10 @@ export async function* runQuery(userInput, options) {
762
967
  },
763
968
  };
764
969
  }
970
+ const checkpoint = advisorCoordinator.beforeTool(tc, advisorSemanticsFor(tc));
971
+ if (!checkpoint.allow) {
972
+ return { tc, result: checkpoint.deferred };
973
+ }
765
974
  // 工具执行闸门:宿主注入后覆盖所有工具,包括只读工具。deny → 把 reason 当
766
975
  // tool_result 回填,模型据此改道/如实报告,不抛异常不中断整轮。宿主异常一律 deny。
767
976
  const isReadOnly = getToolByName(tc.function.name)?.isReadOnly === true;
@@ -788,16 +997,29 @@ export async function* runQuery(userInput, options) {
788
997
  options.onToolStart?.(tc.function.name, tc.id);
789
998
  // 浅拷贝填入本次调用的 toolCallId:进度按调用实例路由(并行同名工具不串扰)。
790
999
  // advisorCalls/agentCalls 是共享对象引用,计数仍全轮共享,不受拷贝影响。
791
- let result = await executeToolFn(tc.function.name, tc.function.arguments, signal, { ...toolCallContext, cwd: sessionState?.activeWorktree?.path ?? toolCallContext.cwd, toolCallId: tc.id, reportProgress });
792
- if (tc.function.name === 'PresentResult'
793
- && result.ok
794
- && result.metadata?.kind === 'result_manifest'
795
- && options.commitFormalDelivery) {
1000
+ let result = await executeToolFn(tc.function.name, tc.function.arguments, signal, {
1001
+ ...toolCallContext,
1002
+ cwd: sessionState?.activeWorktree?.path ?? toolCallContext.cwd,
1003
+ toolCallId: tc.id,
1004
+ reportProgress,
1005
+ });
1006
+ if (tc.function.name === 'ComputerUse' && sessionState) {
1007
+ sessionState.computerAutomation = { lastUsedAt: Date.now() };
1008
+ }
1009
+ const downloadableArtifact = result.ok && result.metadata?.kind === 'browser_download'
1010
+ ? result.metadata.artifact
1011
+ : undefined;
1012
+ const formalManifest = result.ok && result.metadata?.kind === 'result_manifest'
1013
+ ? result.metadata.manifest
1014
+ : downloadableArtifact;
1015
+ if (formalManifest &&
1016
+ (tc.function.name === 'PresentResult' || downloadableArtifact) &&
1017
+ options.commitFormalDelivery) {
796
1018
  try {
797
1019
  await options.commitFormalDelivery({
798
1020
  toolCallId: tc.id,
799
1021
  turnId: userMessage.id ?? options.userMessageId ?? 'turn:unknown',
800
- manifest: result.metadata.manifest,
1022
+ manifest: formalManifest,
801
1023
  });
802
1024
  }
803
1025
  catch (error) {
@@ -809,6 +1031,10 @@ export async function* runQuery(userInput, options) {
809
1031
  };
810
1032
  }
811
1033
  }
1034
+ if (tc.function.name === 'Advisor' || tc.function.name === 'AdvisorDecision') {
1035
+ // 串行批次内下一项可能就是 AdvisorDecision/受保护写入,必须立即推进协调器状态。
1036
+ advisorCoordinator.observeToolResult(tc, result);
1037
+ }
812
1038
  options.onToolEnd?.(tc.id, false);
813
1039
  return { tc, result };
814
1040
  }
@@ -920,36 +1146,79 @@ export async function* runQuery(userInput, options) {
920
1146
  }
921
1147
  }
922
1148
  // 本回合工作量累计 → 上调顾问配额(只增不减;已用掉的次数不会因此回退)。
923
- executedToolCalls += settled.length;
1149
+ executedToolCalls += settled.filter(({ result }) => result.ok || result.disposition !== 'deferred').length;
924
1150
  toolCallContext.advisorCalls.max = advisorQuotaFor(executedToolCalls);
925
1151
  // 严格按 toolCallsByIndex 顺序把 tool 消息回填 history(settled 已保持该顺序)。
926
1152
  let anyAborted = false;
927
1153
  for (const { tc, result } of settled) {
928
- evidenceLedger.record(tc.function.name, tc.function.arguments, result);
1154
+ if (tc.function.name !== 'Advisor' && tc.function.name !== 'AdvisorDecision') {
1155
+ advisorCoordinator.observeToolResult(tc, result);
1156
+ }
1157
+ if (result.ok || result.disposition !== 'deferred') {
1158
+ evidenceLedger.record(tc.function.name, tc.function.arguments, result);
1159
+ }
929
1160
  if (!result.ok && result.error === 'aborted')
930
1161
  anyAborted = true;
931
- if (tc.function.name === 'PresentResult' && result.ok && result.metadata?.kind === 'result_manifest') {
1162
+ const browserArtifact = result.ok && result.metadata?.kind === 'browser_download'
1163
+ ? result.metadata.artifact
1164
+ : undefined;
1165
+ if (result.ok &&
1166
+ ((tc.function.name === 'PresentResult' &&
1167
+ result.metadata?.kind === 'result_manifest') ||
1168
+ browserArtifact)) {
932
1169
  resultPresented = true;
933
- presentedResultUnverified ||= result.metadata.manifest.files.some((file) => file.verification !== 'passed');
1170
+ // `not_run` only says that no optional, format-specific validator was
1171
+ // invoked. The PresentResult boundary has still checked the file and
1172
+ // persisted its fingerprint, so it must not downgrade a normal delivery.
1173
+ const manifest = result.metadata?.kind === 'result_manifest'
1174
+ ? result.metadata.manifest
1175
+ : browserArtifact;
1176
+ presentedResultUnverified ||=
1177
+ manifest?.files.some((file) => file.verification === 'failed') ??
1178
+ false;
934
1179
  }
935
1180
  const rawContent = result.ok ? result.content : `Error: ${result.error}`;
936
- // 微压缩仅对成功结果走(失败结果通常很短,不需要压缩)
1181
+ const basePolicy = result.policy ?? getToolResultPolicy(tc.function.name);
1182
+ // 超大结果先归档再压缩:归档失败时保留原文,绝不因为磁盘问题破坏上下文。
1183
+ const archived = result.ok && sessionState
1184
+ ? await archiveToolResult(rawContent, sessionState.microCompact.archiveKey, tc.function.name)
1185
+ : { content: rawContent, originalChars: rawContent.length };
1186
+ // 归档退化必须发声:撞会话上限后每条超长结果都退回原文塞进上下文,
1187
+ // 也就是这个机制本来要解决的问题原样回来了 —— 静默的话没人会发现。
1188
+ if (archived.degraded && !archiveDegradedWarned) {
1189
+ archiveDegradedWarned = true;
1190
+ yield {
1191
+ type: 'warning',
1192
+ message: archived.degraded === 'session-limit'
1193
+ ? '工具结果归档已达本会话上限,后续超长结果将直接进入上下文(归档不再生效)。建议 /new 开新会话。'
1194
+ : '工具结果归档写盘失败,后续超长结果将直接进入上下文。请检查磁盘空间与权限。',
1195
+ };
1196
+ }
1197
+ const policy = archived.archiveRef
1198
+ ? { ...basePolicy, archiveRef: archived.archiveRef, originalChars: archived.originalChars }
1199
+ : basePolicy;
1200
+ // 微压缩仅对成功结果走;policy 会保护高价值结果,避免不可逆中段丢失。
937
1201
  const content = result.ok
938
- ? microCompact(tc.function.name, rawContent, sessionState?.microCompact)
1202
+ ? microCompact(tc.function.name, archived.content, sessionState?.microCompact, policy)
939
1203
  : rawContent;
940
1204
  history.push({
941
1205
  role: 'tool',
942
1206
  content,
943
1207
  tool_call_id: tc.id,
1208
+ tool_result_policy: policy,
944
1209
  ...(result.metadata ? { display_metadata: result.metadata } : {}),
945
1210
  timestamp: Date.now(),
946
1211
  id: crypto.randomUUID(),
1212
+ ...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
947
1213
  });
948
1214
  }
949
1215
  // If the third identical observation is also the configured final turn, retain the
950
1216
  // established max_turns termination contract. The stall guard is valuable only when
951
1217
  // it can stop a failing path before the caller's explicit turn budget does.
952
1218
  const preemptedRepeatedFailure = settled.some(({ result }) => !result.ok && result.error.includes('第三次调用已在执行前拒绝'));
1219
+ const preemptedSignature = preemptedRepeatedFailure
1220
+ ? settled.find(({ result }) => !result.ok && result.error.includes('第三次调用已在执行前拒绝'))?.tc
1221
+ : undefined;
953
1222
  const stall = preemptedRepeatedFailure
954
1223
  ? ({
955
1224
  stalled: true,
@@ -960,12 +1229,13 @@ export async function* runQuery(userInput, options) {
960
1229
  : { stalled: false };
961
1230
  if (stall.stalled) {
962
1231
  const message = stall.reason === 'repeated_failure'
963
- ? '检测到同一工具以相同参数连续返回相同失败,已停止重复重试。'
964
- : '检测到连续工具轮次没有产生新文本或新结果,已停止无进展循环。';
1232
+ ? '检测到同一工具以相同参数连续返回相同失败;请停止原样重试,改做只读诊断、切换路径或咨询 Advisor。'
1233
+ : '检测到连续工具轮次没有产生新文本或新结果;请切换路径或咨询 Advisor。';
1234
+ advisorCoordinator.notifyStalled(stall.signature
1235
+ ?? (preemptedSignature
1236
+ ? `${preemptedSignature.function.name}\0${preemptedSignature.function.arguments}`
1237
+ : stall.reason));
965
1238
  yield { type: 'warning', message };
966
- yield { type: 'harness_diagnostics', diagnostics: harnessDiagnostics('stalled') };
967
- yield { type: 'error', error: message, code: 'stalled' };
968
- return;
969
1239
  }
970
1240
  // 关键节点截图回填:把工具附带的实况截图作为一条带图 user 消息喂给(视觉)模型自我核验,
971
1241
  // 并 yield injected_message 让宿主在对话里即时展示这张实况截图。
@@ -978,6 +1248,7 @@ export async function* runQuery(userInput, options) {
978
1248
  // The history copy is intentionally downgraded after the turn. Emit a detached snapshot
979
1249
  // so TUI/Desktop stores do not see their already-rendered image mutate underneath them.
980
1250
  if (injectedShot) {
1251
+ visionRequiredThisTurn = true;
981
1252
  yield { type: 'injected_message', message: structuredClone(injectedShot) };
982
1253
  }
983
1254
  if (anyAborted || signal?.aborted) {
@@ -1070,7 +1341,7 @@ function isVisionShotText(text) {
1070
1341
  * 把失败伪装成成功——见 types.ts 里 ToolResult 那段注释。
1071
1342
  */
1072
1343
  async function injectVisionFollowups(history, settled, provider) {
1073
- if (provider.supportsVision !== true)
1344
+ if (provider.supportsVision === false)
1074
1345
  return null;
1075
1346
  const images = settled.flatMap((s) => s.result.visionImages ?? []);
1076
1347
  if (images.length === 0)
@@ -1196,6 +1467,8 @@ function friendlyToolDescription(toolName, rawArgsJson) {
1196
1467
  return `Fetching ...`;
1197
1468
  }
1198
1469
  }
1470
+ case 'BrowserBatch': return `Browser batch (${Array.isArray(args.steps) ? args.steps.length : 0} steps)`;
1471
+ case 'BrowserCDP': return `Browser CDP ${truncate(String(args.method ?? args.operation ?? ''), 45)}`;
1199
1472
  case 'WebSearch': return `Searching \`${truncate(args.query, 50)}\``;
1200
1473
  case 'Agent': {
1201
1474
  const t = typeof args.agent_type === 'string' ? args.agent_type : 'explore';