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
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================
3
- * src/context/microCompactLite.ts —— 工具结果微压缩(v2)
3
+ * src/context/microCompactLite.ts —— 工具结果微压缩(v4)
4
4
  * ------------------------------------------------------------
5
5
  * 对齐 kakadeai 的 microCompact 设计,纯规则实现,零 LLM 调用。
6
6
  *
@@ -8,15 +8,32 @@
8
8
  * 工具执行结果在写入 history 前经过一道"单向检查门",
9
9
  * 用规则压缩冗余内容,减少 token 消耗但不丢失关键信息。
10
10
  *
11
- * 对齐 kakadeai 的两条有效路径:
11
+ * 对齐 kakadeai 的三条有效路径(本实现对高价值工具更保守):
12
12
  * Path A: Cached MC → 本方案用 SHA-1 进程缓存 + 计数淘汰实现
13
13
  * Path B: Time-based MC → 本方案用轮次衰减实现
14
+ * Path C: 批量回收 → reclaimOldToolResults:低风险工具结果数超阈值时,
15
+ * 只保留最近几条的完整内容,其余按模式处理:
16
+ * - inline(默认,全 provider 可用):本地 history 原地替换为占位符。
17
+ * wire token 真实减少,代价是前缀缓存从被改消息起失效。
18
+ * - cacheEdits(v4,Anthropic 格式 + provider.cacheEdits 开启):本地
19
+ * history **不改**,删除目标记入 CacheEditsState,由 anthropicFormat
20
+ * 组装 cache_edits block 随请求发送——对齐 kakadeai Cached MC 的
21
+ * triggerThreshold/keepRecent 语义与服务端原地删除效果:模型看到
22
+ * 的上下文变短、前缀缓存完整保留。
14
23
  *
15
24
  * kakadeai 已删除 Legacy LLM path → 本方案不实现 LLM 压缩
25
+ *
26
+ * 为什么需要 Path C:SHA-1 去重要求内容完全相同才计数,
27
+ * 而真实工具结果带行号/时间戳/文件内容,几乎不会完全重复——去重路径
28
+ * 形同虚设,工具结果实际全部永久留在 history 里。这是 minimal-agent
29
+ * 上下文膨胀比 kakadeai 快得多的根因(工具结果 token 占比 60-80%)。
16
30
  * ============================================================
17
31
  */
18
32
 
19
- import { createHash } from 'node:crypto';
33
+ import { createHash, randomUUID } from 'node:crypto';
34
+ import type { CacheEditsState, ToolResultPolicy } from '../types.js';
35
+ import { countTextTokens } from './tokenCounter.js';
36
+ import { clearToolResultArchive } from './toolResultArchive.js';
20
37
 
21
38
  // ==================== 配置常量 ====================
22
39
 
@@ -38,6 +55,19 @@ const MAX_KEEP_ROUNDS = 10;
38
55
  /** 短内容阈值:不超过此长度的内容直接放行(不做任何处理) */
39
56
  const SHORT_CONTENT_THRESHOLD = 200;
40
57
 
58
+ /** Path C 批量回收:可回收工具结果的存活数量超过该值时触发回收 */
59
+ const RECLAIM_TRIGGER_THRESHOLD = 8;
60
+
61
+ /** Path C 批量回收:触发后保留最近多少条完整内容(热尾部) */
62
+ const RECLAIM_KEEP_RECENT = 4;
63
+
64
+ /**
65
+ * Path C 批量回收的占位符前缀。用 startsWith 检测已回收的消息,
66
+ * 保证幂等(不会重复计数、重复替换)。刻意与去重标记 `[↑ `、
67
+ * 截断标记 `[... 省略了` 不同字形,避免互相误判。
68
+ */
69
+ const RECLAIM_MARKER_PREFIX = '[旧工具结果已回收';
70
+
41
71
  // ==================== 类型定义 ====================
42
72
 
43
73
  interface CacheEntry {
@@ -57,10 +87,36 @@ interface CacheEntry {
57
87
  export interface MicroCompactState {
58
88
  turn: number;
59
89
  cache: Map<string, CacheEntry>;
90
+ /** Path C 批量回收累计:已回收的结果条数 */
91
+ reclaimedCount: number;
92
+ /**
93
+ * Path C 批量回收累计:被替换掉的上下文字符数(观测用)。
94
+ * 口径 = 回收时 history 里实际占着的字符数(可能已被 Rule 3 截断过),
95
+ * 不含占位符自身 ~60 字符的成本——精确净节省 ≈ reclaimedChars − 60 × reclaimedCount。
96
+ */
97
+ reclaimedChars: number;
98
+ /** 每个会话独立的工具结果归档命名空间。 */
99
+ archiveKey: string;
100
+ /**
101
+ * cacheEdits 模式的运行时状态(inline 模式下恒为空对象,不参与任何逻辑)。
102
+ * 生命周期:同一会话持续累积;autoCompact / clearCache 时随 resetCacheEdits 清空。
103
+ */
104
+ cacheEdits: CacheEditsState;
105
+ }
106
+
107
+ function createEmptyCacheEdits(): CacheEditsState {
108
+ return { deletedToolIds: new Set(), pinned: [], pendingNew: [], activeDeletedTokens: 0 };
60
109
  }
61
110
 
62
111
  export function createMicroCompactState(): MicroCompactState {
63
- return { turn: 0, cache: new Map() };
112
+ return {
113
+ turn: 0,
114
+ cache: new Map(),
115
+ reclaimedCount: 0,
116
+ reclaimedChars: 0,
117
+ archiveKey: randomUUID(),
118
+ cacheEdits: createEmptyCacheEdits(),
119
+ };
64
120
  }
65
121
 
66
122
  const defaultState: MicroCompactState = createMicroCompactState();
@@ -81,6 +137,10 @@ export interface MicroCompactStats {
81
137
  expiredCount: number;
82
138
  truncatedCount: number;
83
139
  totalCharsSaved: number;
140
+ /** Path C 批量回收累计条数 */
141
+ reclaimedCount: number;
142
+ /** Path C 批量回收累计:被替换掉的上下文字符数(口径见 MicroCompactState 同名字段) */
143
+ reclaimedChars: number;
84
144
  }
85
145
 
86
146
  /** 获取当前缓存统计 */
@@ -102,26 +162,54 @@ export function getCacheStats(state: MicroCompactState = defaultState): MicroCom
102
162
  }
103
163
  }
104
164
 
105
- return { totalEntries: state.cache.size, repeatedCount, compressedCount, expiredCount, truncatedCount, totalCharsSaved };
165
+ return {
166
+ totalEntries: state.cache.size,
167
+ repeatedCount,
168
+ compressedCount,
169
+ expiredCount,
170
+ truncatedCount,
171
+ totalCharsSaved,
172
+ reclaimedCount: state.reclaimedCount,
173
+ reclaimedChars: state.reclaimedChars,
174
+ };
106
175
  }
107
176
 
108
177
  /** 清空缓存(供 /new 命令或测试使用) */
109
178
  export function clearCache(state: MicroCompactState = defaultState): void {
110
179
  state.cache.clear();
111
180
  state.turn = 0;
181
+ state.reclaimedCount = 0;
182
+ state.reclaimedChars = 0;
183
+ void clearToolResultArchive(state.archiveKey);
184
+ state.archiveKey = randomUUID();
185
+ resetCacheEdits(state);
186
+ }
187
+
188
+ /**
189
+ * 重置 cacheEdits 状态。调用时机(漏掉任何一个都会造成下标错位或重复删除):
190
+ * - autoCompact / 手动 compact 之后:history 被砍头重写,pinned 的翻译后下标
191
+ * 全部失义,服务端缓存前缀也已失效,编辑指令没有继续存在的意义。
192
+ * - /new 新会话(clearCache 内已调用)。
193
+ */
194
+ export function resetCacheEdits(state: MicroCompactState = defaultState): void {
195
+ state.cacheEdits.deletedToolIds.clear();
196
+ state.cacheEdits.pinned.length = 0;
197
+ state.cacheEdits.pendingNew.length = 0;
198
+ // 有效删除量一并清零:compact 后历史被重写,旧删除账目对应的内容可能已不在
199
+ // 新历史里,不清零会让 autoCompact 校正过度(判据虚低 -> 该压不压撑爆上下文)。
200
+ state.cacheEdits.activeDeletedTokens = 0;
112
201
  }
113
202
 
114
203
  // ==================== 工具白名单 ====================
115
204
  /**
116
- * 只有这些工具的返回值参与微压缩(长度截断 + SHA-1 去重)。
205
+ * 只有低风险观察工具的返回值参与长度截断(SHA-1 去重对所有工具生效)。
117
206
  *
118
- * 排除 Read/Edit/Write 的原因:输出本身就短,有独立的大小限制。
119
- * 排除 WebSearch 的原因:搜索结果是"一次性知识注入",首次必须完整展示给模型;
120
- * 后续重复时由 SHA-1 去重处理,但不做长度截断(避免丢失关键信息)。
207
+ * Read/Edit/Write/Bash/WebSearch 默认不截断:它们可能包含源码中段、diff、
208
+ * 一次性诊断或不可复现证据。超大结果由 toolResultArchive 先保存原文并提供可恢复引用。
209
+ * 低风险工具仍保留头尾截断,以控制普通搜索结果的上下文成本。
121
210
  */
122
211
  const COMPRESSIBLE_TOOLS = new Set([
123
212
  'Grep',
124
- 'Bash',
125
213
  'WebFetch',
126
214
  'Glob',
127
215
  ]);
@@ -147,6 +235,7 @@ export function microCompact(
147
235
  toolName: string,
148
236
  content: string,
149
237
  state: MicroCompactState = defaultState,
238
+ policy: ToolResultPolicy = getToolResultPolicy(toolName),
150
239
  ): string {
151
240
  if (!content) return content;
152
241
 
@@ -177,8 +266,8 @@ export function microCompact(
177
266
 
178
267
  // Rule 2: 不需要——首次出现不存在时间衰减问题
179
268
 
180
- // Rule 3: 长度截断(仅白名单工具)
181
- if (content.length > MAX_RESULT_SIZE && COMPRESSIBLE_TOOLS.has(toolName)) {
269
+ // Rule 3: 长度截断(仅低风险工具;高价值结果禁止不可逆中段丢弃)
270
+ if (!policy.highValue && content.length > MAX_RESULT_SIZE && COMPRESSIBLE_TOOLS.has(toolName)) {
182
271
  return truncateContent(content);
183
272
  }
184
273
 
@@ -201,12 +290,172 @@ export function expireOldEntries(state: MicroCompactState = defaultState): numbe
201
290
  return expired;
202
291
  }
203
292
 
293
+ // ==================== Path C: 批量回收旧工具结果 ====================
294
+ /**
295
+ * 可回收的工具集合:仅保留低风险观察工具。即便这些工具通常可重放,
296
+ * 高价值结果也可通过 tool_result_policy 显式保护,不会因为工具名而强行清掉。
297
+ *
298
+ * 刻意排除高价值/不可稳定重放的工具:
299
+ * - Bash / WebSearch:一次性诊断和外部搜索结果可能无法复现
300
+ * - Read / Edit / Write:源码中段、diff 或生成内容可能是后续推理证据
301
+ * - Ask / AskUserQuestion:用户答案不可重放,清了就永远丢了
302
+ * - Task / agent 系:子任务结论昂贵且常被后续轮次引用
303
+ * - PresentResult:最终交付物清单,是本轮对话的核心产出
304
+ */
305
+ const RECLAIMABLE_TOOLS = new Set([
306
+ 'Grep',
307
+ 'Glob',
308
+ 'WebFetch',
309
+ ]);
310
+
311
+ /**
312
+ * 批量回收所需的最小 history 消息结构(结构化类型,真实 Message 天然满足)。
313
+ * 不直接 import Message 是为了让本模块保持零项目内依赖、测试可用裸对象。
314
+ */
315
+ export interface ReclaimableMessage {
316
+ role: string;
317
+ /** 真实 Message 里 user 消息可为多模态数组;本函数只消费 string 形态 */
318
+ content: string | null | readonly unknown[];
319
+ tool_calls?: Array<{ id?: string; function?: { name?: string } }>;
320
+ tool_call_id?: string;
321
+ tool_result_policy?: ToolResultPolicy;
322
+ }
323
+
324
+ /** 一次批量回收的结果统计 */
325
+ export interface ReclaimStats {
326
+ reclaimedCount: number;
327
+ reclaimedChars: number;
328
+ }
329
+
330
+ const EMPTY_RECLAIM_STATS: ReclaimStats = { reclaimedCount: 0, reclaimedChars: 0 };
331
+
332
+ /**
333
+ * Path C:批量回收旧工具结果(每轮 LLM 调用前执行)。
334
+ *
335
+ * 语义(对齐 kakadeai Cached MC 的 triggerThreshold / keepRecent):
336
+ * history 中"存活"的可回收工具结果数量 > RECLAIM_TRIGGER_THRESHOLD 时,
337
+ * 只保留最近 RECLAIM_KEEP_RECENT 条的完整内容(热尾部),其余按 mode 处理:
338
+ * - 'inline'(默认):原地替换为占位符(含工具名与被替换的字符数,
339
+ * 模型需要时可重新调用该工具)。
340
+ * - 'cacheEdits':**不改 history**——删除目标(tool_call_id)记入
341
+ * state.cacheEdits.pendingNew,由 anthropicFormat 组装 cache_edits block
342
+ * 随请求发送,服务端在 KV 缓存内原地删除。
343
+ *
344
+ * 设计约束与取舍:
345
+ * 1. inline 只改 tool 消息的 content,tool_call_id / display_metadata 原样保留——
346
+ * API 协议完整性不受影响,且 display_metadata 在发送 Provider 前本就
347
+ * 被剥离(仅本地展示用),所以 UI 渲染不受影响。
348
+ * 2. 幂等:inline 用占位符前缀(RECLAIM_MARKER_PREFIX)检测已回收消息;
349
+ * cacheEdits 用 deletedToolIds 集合检测。已回收的不计入候选、不二次处理。
350
+ * 触发频率 = 每攒 (阈值-保留数) 条新结果触发一次。
351
+ * 3. 跳过 Error 结果(短且常是排障线索)和 ≤200 字符的短结果
352
+ * (占位符本身就有 ~60 字符,清短结果纯亏)。
353
+ * 4. 缓存代价(模式差异的核心):
354
+ * inline 直接改 history,前缀缓存从被改消息起失效——换 wire token 真实
355
+ * 削减,一次缓存重建几轮就回本,适用于无 cache_edits 能力的端点(含全部
356
+ * OpenAI 格式 provider)。
357
+ * cacheEdits 本地不动、服务端原地删——前缀 hash 不变、缓存命中完整保留;
358
+ * 代价是 wire 上完整内容仍要发送(省的是服务端 KV 驻留与模型注意力窗口,
359
+ * 不是请求体积)。reclaimedChars 在此模式下统计的是"服务端删除的内容量"。
360
+ * 5. tool_call_id → 工具名的映射来自 assistant 消息的 tool_calls 数组,
361
+ * 扫一遍 history 即得,不需要额外状态。
362
+ * 6. cacheEdits 模式下本函数只做决策不做协议——它不认识 Anthropic 消息形状,
363
+ * 翻译后下标由 anthropicFormat 的组装函数负责回填(见其 pinned 逻辑)。
364
+ *
365
+ * @param history 完整会话历史(inline 模式原地修改命中的 tool 消息;cacheEdits 模式只读)
366
+ * @param state 微压缩状态(累计统计写入 reclaimedCount / reclaimedChars;cacheEdits 模式另写 pendingNew)
367
+ * @param mode 'inline'(默认)| 'cacheEdits'(Anthropic 格式 + provider.cacheEdits)
368
+ * @returns 本次回收的条数与字符数(未触发时全为 0)
369
+ */
370
+ export function reclaimOldToolResults(
371
+ history: ReclaimableMessage[],
372
+ state: MicroCompactState = defaultState,
373
+ mode: 'inline' | 'cacheEdits' = 'inline',
374
+ ): ReclaimStats {
375
+ // 1. 建立 tool_call_id → 工具名 映射
376
+ const toolNameById = new Map<string, string>();
377
+ for (const msg of history) {
378
+ if (msg.role !== 'assistant' || !msg.tool_calls) continue;
379
+ for (const tc of msg.tool_calls) {
380
+ if (tc.id && tc.function?.name) toolNameById.set(tc.id, tc.function.name);
381
+ }
382
+ }
383
+
384
+ // 2. 收集可回收候选:白名单工具 + 存活(未回收过)+ 非错误 + 非短内容
385
+ // 存活判据按模式分叉:inline 看占位符前缀,cacheEdits 看 deletedToolIds。
386
+ const candidates: Array<{ msg: ReclaimableMessage & { content: string }; toolCallId: string; chars: number }> = [];
387
+ for (const msg of history) {
388
+ if (msg.role !== 'tool' || typeof msg.content !== 'string') continue;
389
+ const content = msg.content;
390
+ if (content.startsWith(RECLAIM_MARKER_PREFIX)) continue;
391
+ if (content.startsWith('Error:') || content.startsWith('错误')) continue;
392
+ if (content.length <= SHORT_CONTENT_THRESHOLD) continue;
393
+ const toolCallId = msg.tool_call_id;
394
+ const toolName = toolCallId ? toolNameById.get(toolCallId) : undefined;
395
+ const policy = msg.tool_result_policy ?? (toolName ? getToolResultPolicy(toolName) : undefined);
396
+ if (
397
+ !toolCallId
398
+ || !toolName
399
+ || !RECLAIMABLE_TOOLS.has(toolName)
400
+ || !policy?.reclaimable
401
+ || policy.highValue
402
+ ) continue;
403
+ if (mode === 'cacheEdits' && state.cacheEdits.deletedToolIds.has(toolCallId)) continue;
404
+ candidates.push({ msg: msg as ReclaimableMessage & { content: string }, toolCallId, chars: content.length });
405
+ }
406
+
407
+ // 3. 未超阈值不动(> 而非 >=:恰好等于阈值视为健康水位)
408
+ if (candidates.length <= RECLAIM_TRIGGER_THRESHOLD) {
409
+ return EMPTY_RECLAIM_STATS;
410
+ }
411
+
412
+ // 4. 保留热尾部(按消息顺序的最后 KEEP_RECENT 条),其余按模式处理
413
+ const toClear = candidates.slice(0, candidates.length - RECLAIM_KEEP_RECENT);
414
+ let reclaimedChars = 0;
415
+ if (mode === 'cacheEdits') {
416
+ for (const { msg, toolCallId, chars } of toClear) {
417
+ state.cacheEdits.deletedToolIds.add(toolCallId);
418
+ state.cacheEdits.pendingNew.push(toolCallId);
419
+ // 累计有效删除量(token 估算,与 tokenCounter 同一套系数):
420
+ // autoCompact 判据用它校正「本地 history 未瘦身」造成的虚高估算。
421
+ state.cacheEdits.activeDeletedTokens += countTextTokens(msg.content);
422
+ reclaimedChars += chars;
423
+ }
424
+ } else {
425
+ for (const { msg, toolCallId, chars } of toClear) {
426
+ const toolName = toolNameById.get(toolCallId) ?? 'tool';
427
+ reclaimedChars += chars;
428
+ const archiveHint = msg.tool_result_policy?.archiveRef
429
+ ? ` | 完整原文可用 Read 读取:${msg.tool_result_policy.archiveRef}`
430
+ : '';
431
+ msg.content =
432
+ `${RECLAIM_MARKER_PREFIX} | ${toolName} | 原本 ${chars.toLocaleString()} 字符${archiveHint} | ` +
433
+ `如需再次查看请重新调用该工具]`;
434
+ }
435
+ }
436
+
437
+ // 5. 累计统计(getCacheStats 可查,观测回收效果)
438
+ state.reclaimedCount += toClear.length;
439
+ state.reclaimedChars += reclaimedChars;
440
+ return { reclaimedCount: toClear.length, reclaimedChars };
441
+ }
442
+
204
443
  // ==================== 内部函数 ====================
205
444
 
206
445
  function sha1(str: string): string {
207
446
  return createHash('sha1').update(str).digest('hex');
208
447
  }
209
448
 
449
+ export function getToolResultPolicy(toolName: string): ToolResultPolicy {
450
+ // Bash/WebSearch/Read/Edit/Write 等结果可能包含一次性诊断、关键源码或不可复现证据,
451
+ // 默认高价值且不可回收;低风险观察工具才进入 Path C。
452
+ const reclaimable = RECLAIMABLE_TOOLS.has(toolName);
453
+ return {
454
+ reclaimable,
455
+ highValue: !reclaimable,
456
+ };
457
+ }
458
+
210
459
  function truncateContent(content: string): string {
211
460
  const omitted = content.length - HEAD_KEEP_CHARS - TAIL_KEEP_CHARS;
212
461
 
@@ -310,6 +310,8 @@ async function createSessionUnlocked(cwd, options) {
310
310
  createdAt: now,
311
311
  updatedAt: now,
312
312
  ...(options.modelId?.trim() ? { modelId: options.modelId.trim() } : {}),
313
+ ...(options.preferredModel ? { preferredModel: options.preferredModel, restoreWhenAvailable: true } : {}),
314
+ ...(options.onlineToolCategories ? { onlineToolCategories: [...options.onlineToolCategories] } : {}),
313
315
  };
314
316
  metadata[hash] = { ...group, [id]: summary };
315
317
  await saveMetadata(metadata);
@@ -334,6 +336,11 @@ async function updateSessionMetadataUnlocked(cwd, sessionId, patch) {
334
336
  ...(patch.title !== undefined ? { title: patch.title.trim() || previous.title } : {}),
335
337
  ...(patch.lastMessagePreview !== undefined ? { lastMessagePreview: patch.lastMessagePreview.slice(0, 240) } : {}),
336
338
  ...(patch.modelId === null ? { modelId: undefined } : patch.modelId !== undefined ? { modelId: patch.modelId.trim() } : {}),
339
+ ...(patch.preferredModel === null ? { preferredModel: undefined } : patch.preferredModel !== undefined ? { preferredModel: patch.preferredModel } : {}),
340
+ ...(patch.effectiveModel === null ? { effectiveModel: undefined } : patch.effectiveModel !== undefined ? { effectiveModel: patch.effectiveModel } : {}),
341
+ ...(patch.fallbackReason !== undefined ? { fallbackReason: patch.fallbackReason } : {}),
342
+ ...(patch.restoreWhenAvailable !== undefined ? { restoreWhenAvailable: patch.restoreWhenAvailable } : {}),
343
+ ...(patch.onlineToolCategories !== undefined ? { onlineToolCategories: [...patch.onlineToolCategories] } : {}),
337
344
  updatedAt: now,
338
345
  };
339
346
  metadata[hash] = { ...group, [id]: next };
@@ -351,7 +358,7 @@ export async function copySession(cwd, sourceSessionId, title) {
351
358
  return enqueueMutation(async () => {
352
359
  const sourceId = validateSessionId(sourceSessionId);
353
360
  const source = (await listSessionSummaries(cwd)).find((session) => session.id === sourceId);
354
- const target = await createSessionUnlocked(cwd, { title: title ?? '会话副本', ...(source?.modelId ? { modelId: source.modelId } : {}) });
361
+ const target = await createSessionUnlocked(cwd, { title: title ?? '会话副本', ...(source?.modelId ? { modelId: source.modelId } : {}), ...(source?.preferredModel ? { preferredModel: source.preferredModel } : {}), ...(source?.onlineToolCategories ? { onlineToolCategories: source.onlineToolCategories } : {}) });
355
362
  try {
356
363
  await mkdir(sessionsDirPath(), { recursive: true });
357
364
  await copyFile(await resolveSessionFile(cwd, sourceId), sessionFileFor(cwd, target.id));
@@ -26,6 +26,7 @@ import { homedir } from 'node:os';
26
26
  import { join } from 'node:path';
27
27
  import { resolveSessionFile, sessionFileFor, validateSessionId } from './sessionPath.js';
28
28
  import { workspaceIndexService } from './workspaceIndex.js';
29
+ import type { ModelFallbackReason, ModelRef } from '../modelRef.js';
29
30
  export { validateSessionId } from './sessionPath.js';
30
31
 
31
32
  /** 解析根目录,支持 MINIMAL_AGENT_HOME 测试覆盖 */
@@ -61,6 +62,11 @@ export interface SessionSummary {
61
62
  lastMessagePreview?: string;
62
63
  /** Session 级模型绑定;缺省时回退到全局基座模型。 */
63
64
  modelId?: string;
65
+ preferredModel?: ModelRef;
66
+ effectiveModel?: ModelRef;
67
+ fallbackReason?: ModelFallbackReason;
68
+ restoreWhenAvailable?: boolean;
69
+ onlineToolCategories?: string[];
64
70
  }
65
71
 
66
72
  interface SessionMetadataFile {
@@ -307,7 +313,7 @@ export async function listSessionSummaries(cwd: string): Promise<SessionSummary[
307
313
 
308
314
  async function createSessionUnlocked(
309
315
  cwd: string,
310
- options: { id?: string; title?: string; modelId?: string },
316
+ options: { id?: string; title?: string; modelId?: string; preferredModel?: ModelRef; onlineToolCategories?: string[] },
311
317
  ): Promise<SessionSummary> {
312
318
  const id = validateSessionId(options.id ?? randomUUID());
313
319
  const metadata = await loadMetadata();
@@ -321,6 +327,8 @@ async function createSessionUnlocked(
321
327
  createdAt: now,
322
328
  updatedAt: now,
323
329
  ...(options.modelId?.trim() ? { modelId: options.modelId.trim() } : {}),
330
+ ...(options.preferredModel ? { preferredModel: options.preferredModel, restoreWhenAvailable: true } : {}),
331
+ ...(options.onlineToolCategories ? { onlineToolCategories: [...options.onlineToolCategories] } : {}),
324
332
  };
325
333
  metadata[hash] = { ...group, [id]: summary };
326
334
  await saveMetadata(metadata);
@@ -333,7 +341,7 @@ async function createSessionUnlocked(
333
341
 
334
342
  export async function createSession(
335
343
  cwd: string,
336
- options: { id?: string; title?: string; modelId?: string } = {},
344
+ options: { id?: string; title?: string; modelId?: string; preferredModel?: ModelRef; onlineToolCategories?: string[] } = {},
337
345
  ): Promise<SessionSummary> {
338
346
  return enqueueMutation(() => createSessionUnlocked(cwd, options));
339
347
  }
@@ -341,7 +349,7 @@ export async function createSession(
341
349
  async function updateSessionMetadataUnlocked(
342
350
  cwd: string,
343
351
  sessionId: string,
344
- patch: { title?: string; lastMessagePreview?: string; modelId?: string | null },
352
+ patch: { title?: string; lastMessagePreview?: string; modelId?: string | null; preferredModel?: ModelRef | null; effectiveModel?: ModelRef | null; fallbackReason?: ModelFallbackReason; restoreWhenAvailable?: boolean; onlineToolCategories?: string[] },
345
353
  ): Promise<SessionSummary> {
346
354
  const id = validateSessionId(sessionId);
347
355
  const metadata = await loadMetadata();
@@ -354,6 +362,11 @@ async function updateSessionMetadataUnlocked(
354
362
  ...(patch.title !== undefined ? { title: patch.title.trim() || previous.title } : {}),
355
363
  ...(patch.lastMessagePreview !== undefined ? { lastMessagePreview: patch.lastMessagePreview.slice(0, 240) } : {}),
356
364
  ...(patch.modelId === null ? { modelId: undefined } : patch.modelId !== undefined ? { modelId: patch.modelId.trim() } : {}),
365
+ ...(patch.preferredModel === null ? { preferredModel: undefined } : patch.preferredModel !== undefined ? { preferredModel: patch.preferredModel } : {}),
366
+ ...(patch.effectiveModel === null ? { effectiveModel: undefined } : patch.effectiveModel !== undefined ? { effectiveModel: patch.effectiveModel } : {}),
367
+ ...(patch.fallbackReason !== undefined ? { fallbackReason: patch.fallbackReason } : {}),
368
+ ...(patch.restoreWhenAvailable !== undefined ? { restoreWhenAvailable: patch.restoreWhenAvailable } : {}),
369
+ ...(patch.onlineToolCategories !== undefined ? { onlineToolCategories: [...patch.onlineToolCategories] } : {}),
357
370
  updatedAt: now,
358
371
  };
359
372
  metadata[hash] = { ...group, [id]: next };
@@ -368,7 +381,7 @@ async function updateSessionMetadataUnlocked(
368
381
  export async function updateSessionMetadata(
369
382
  cwd: string,
370
383
  sessionId: string,
371
- patch: { title?: string; lastMessagePreview?: string; modelId?: string | null },
384
+ patch: { title?: string; lastMessagePreview?: string; modelId?: string | null; preferredModel?: ModelRef | null; effectiveModel?: ModelRef | null; fallbackReason?: ModelFallbackReason; restoreWhenAvailable?: boolean; onlineToolCategories?: string[] },
372
385
  ): Promise<SessionSummary> {
373
386
  return enqueueMutation(() => updateSessionMetadataUnlocked(cwd, sessionId, patch));
374
387
  }
@@ -377,7 +390,7 @@ export async function copySession(cwd: string, sourceSessionId: string, title?:
377
390
  return enqueueMutation(async () => {
378
391
  const sourceId = validateSessionId(sourceSessionId);
379
392
  const source = (await listSessionSummaries(cwd)).find((session) => session.id === sourceId);
380
- const target = await createSessionUnlocked(cwd, { title: title ?? '会话副本', ...(source?.modelId ? { modelId: source.modelId } : {}) });
393
+ const target = await createSessionUnlocked(cwd, { title: title ?? '会话副本', ...(source?.modelId ? { modelId: source.modelId } : {}), ...(source?.preferredModel ? { preferredModel: source.preferredModel } : {}), ...(source?.onlineToolCategories ? { onlineToolCategories: source.onlineToolCategories } : {}) });
381
394
  try {
382
395
  await mkdir(sessionsDirPath(), { recursive: true });
383
396
  await copyFile(await resolveSessionFile(cwd, sourceId), sessionFileFor(cwd, target.id));
@@ -0,0 +1,72 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { mkdir, readdir, stat, unlink, writeFile, rename, rm } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ const MAX_ARCHIVE_RESULT_CHARS = 12_000;
6
+ const MAX_SESSION_ARCHIVE_BYTES = 64 * 1024 * 1024;
7
+ function agentHome() {
8
+ return process.env.MINIMAL_AGENT_HOME ?? homedir();
9
+ }
10
+ function archiveDir(sessionKey) {
11
+ const safeKey = createHash('sha256').update(sessionKey).digest('hex').slice(0, 24);
12
+ return join(agentHome(), '.minimal-agent', 'tool-results', safeKey);
13
+ }
14
+ /**
15
+ * 将超大工具结果写入当前会话的受控归档目录,并返回可供模型 Read 的引用预览。
16
+ *
17
+ * 失败时原样返回:归档只能增加可恢复性,不能成为破坏性压缩的前置条件。
18
+ * 文件名只使用随机 UUID,避免用户输入进入路径;写入采用临时文件 + rename。
19
+ */
20
+ export async function archiveToolResult(content, sessionKey, toolName) {
21
+ if (content.length <= MAX_ARCHIVE_RESULT_CHARS) {
22
+ return { content, originalChars: content.length };
23
+ }
24
+ const dir = archiveDir(sessionKey);
25
+ const id = randomUUID();
26
+ const finalPath = join(dir, `${id}.txt`);
27
+ const tempPath = join(dir, `.${id}.tmp`);
28
+ try {
29
+ await mkdir(dir, { recursive: true, mode: 0o700 });
30
+ await writeFile(tempPath, content, { encoding: 'utf8', mode: 0o600 });
31
+ await rename(tempPath, finalPath);
32
+ if (!(await archiveFitsWithinLimit(dir))) {
33
+ // 不淘汰旧文件:它们可能仍被 history 里的引用预览指着,删了模型就再也读不回来。
34
+ // 超限时只撤销当前这一次归档并保留原文 —— 数据不丢,但**功能从此不再起作用**,
35
+ // 所以必须带上 degraded 让调用方报一次 warning,不能静默。
36
+ await unlink(finalPath);
37
+ return { content, originalChars: content.length, degraded: 'session-limit' };
38
+ }
39
+ const preview = `${content.slice(0, 2_000)}\n\n` +
40
+ `[工具结果已归档:${toolName},原文 ${content.length.toLocaleString()} 字符。` +
41
+ `需要完整内容时,请使用 Read 读取:${finalPath}]`;
42
+ return { content: preview, archiveRef: finalPath, originalChars: content.length };
43
+ }
44
+ catch {
45
+ // 清理可能留下的临时文件;清理失败也不能覆盖原始结果。
46
+ try {
47
+ await unlink(tempPath);
48
+ }
49
+ catch { /* best effort */ }
50
+ return { content, originalChars: content.length, degraded: 'io-error' };
51
+ }
52
+ }
53
+ async function archiveFitsWithinLimit(dir) {
54
+ try {
55
+ const names = (await readdir(dir)).filter((name) => name.endsWith('.txt'));
56
+ let total = 0;
57
+ for (const name of names) {
58
+ total += (await stat(join(dir, name))).size;
59
+ if (total > MAX_SESSION_ARCHIVE_BYTES)
60
+ return false;
61
+ }
62
+ return true;
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ }
68
+ /** /new 或显式 session 清理时调用;不会删除仍在使用的其它 session 目录。 */
69
+ export async function clearToolResultArchive(sessionKey) {
70
+ await rm(archiveDir(sessionKey), { recursive: true, force: true });
71
+ }
72
+ export const TOOL_RESULT_ARCHIVE_MAX_CHARS = MAX_ARCHIVE_RESULT_CHARS;
@@ -0,0 +1,95 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { mkdir, readdir, stat, unlink, writeFile, rename, rm } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+
6
+ const MAX_ARCHIVE_RESULT_CHARS = 12_000;
7
+ const MAX_SESSION_ARCHIVE_BYTES = 64 * 1024 * 1024;
8
+
9
+ export interface ArchivedToolResult {
10
+ content: string;
11
+ archiveRef?: string;
12
+ originalChars: number;
13
+ /**
14
+ * 本次归档没能落盘的原因(成功时不设)。
15
+ *
16
+ * ⚠️ 撞会话上限之后归档就**静默失效**了:功能还在跑,但每一条超长结果都退回
17
+ * 原文塞进上下文 —— 也就是这个模块本来要解决的问题原样回来了,而没有任何信号。
18
+ * 调用方拿到这个字段应当往上报一次 warning(与引擎「零工具调用」信号同款纪律:
19
+ * 该说的话不静默)。
20
+ */
21
+ degraded?: 'session-limit' | 'io-error';
22
+ }
23
+
24
+ function agentHome(): string {
25
+ return process.env.MINIMAL_AGENT_HOME ?? homedir();
26
+ }
27
+
28
+ function archiveDir(sessionKey: string): string {
29
+ const safeKey = createHash('sha256').update(sessionKey).digest('hex').slice(0, 24);
30
+ return join(agentHome(), '.minimal-agent', 'tool-results', safeKey);
31
+ }
32
+
33
+ /**
34
+ * 将超大工具结果写入当前会话的受控归档目录,并返回可供模型 Read 的引用预览。
35
+ *
36
+ * 失败时原样返回:归档只能增加可恢复性,不能成为破坏性压缩的前置条件。
37
+ * 文件名只使用随机 UUID,避免用户输入进入路径;写入采用临时文件 + rename。
38
+ */
39
+ export async function archiveToolResult(
40
+ content: string,
41
+ sessionKey: string,
42
+ toolName: string,
43
+ ): Promise<ArchivedToolResult> {
44
+ if (content.length <= MAX_ARCHIVE_RESULT_CHARS) {
45
+ return { content, originalChars: content.length };
46
+ }
47
+
48
+ const dir = archiveDir(sessionKey);
49
+ const id = randomUUID();
50
+ const finalPath = join(dir, `${id}.txt`);
51
+ const tempPath = join(dir, `.${id}.tmp`);
52
+
53
+ try {
54
+ await mkdir(dir, { recursive: true, mode: 0o700 });
55
+ await writeFile(tempPath, content, { encoding: 'utf8', mode: 0o600 });
56
+ await rename(tempPath, finalPath);
57
+ if (!(await archiveFitsWithinLimit(dir))) {
58
+ // 不淘汰旧文件:它们可能仍被 history 里的引用预览指着,删了模型就再也读不回来。
59
+ // 超限时只撤销当前这一次归档并保留原文 —— 数据不丢,但**功能从此不再起作用**,
60
+ // 所以必须带上 degraded 让调用方报一次 warning,不能静默。
61
+ await unlink(finalPath);
62
+ return { content, originalChars: content.length, degraded: 'session-limit' };
63
+ }
64
+
65
+ const preview = `${content.slice(0, 2_000)}\n\n` +
66
+ `[工具结果已归档:${toolName},原文 ${content.length.toLocaleString()} 字符。` +
67
+ `需要完整内容时,请使用 Read 读取:${finalPath}]`;
68
+ return { content: preview, archiveRef: finalPath, originalChars: content.length };
69
+ } catch {
70
+ // 清理可能留下的临时文件;清理失败也不能覆盖原始结果。
71
+ try { await unlink(tempPath); } catch { /* best effort */ }
72
+ return { content, originalChars: content.length, degraded: 'io-error' };
73
+ }
74
+ }
75
+
76
+ async function archiveFitsWithinLimit(dir: string): Promise<boolean> {
77
+ try {
78
+ const names = (await readdir(dir)).filter((name) => name.endsWith('.txt'));
79
+ let total = 0;
80
+ for (const name of names) {
81
+ total += (await stat(join(dir, name))).size;
82
+ if (total > MAX_SESSION_ARCHIVE_BYTES) return false;
83
+ }
84
+ return true;
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+
90
+ /** /new 或显式 session 清理时调用;不会删除仍在使用的其它 session 目录。 */
91
+ export async function clearToolResultArchive(sessionKey: string): Promise<void> {
92
+ await rm(archiveDir(sessionKey), { recursive: true, force: true });
93
+ }
94
+
95
+ export const TOOL_RESULT_ARCHIVE_MAX_CHARS = MAX_ARCHIVE_RESULT_CHARS;
@@ -0,0 +1,8 @@
1
+ export class GoalRuntimeError extends Error {
2
+ code;
3
+ constructor(code, message) {
4
+ super(message);
5
+ this.code = code;
6
+ this.name = 'GoalRuntimeError';
7
+ }
8
+ }
@@ -0,0 +1,18 @@
1
+ export type GoalRuntimeErrorCode =
2
+ | 'GOAL_CONFLICT'
3
+ | 'GOAL_NOT_FOUND'
4
+ | 'GOAL_REVISION_CONFLICT'
5
+ | 'GOAL_INVALID_TRANSITION'
6
+ | 'GOAL_ACCEPTOR_REQUIRED'
7
+ | 'GOAL_ATTEMPT_CONFLICT'
8
+ | 'GOAL_STORE_LOCKED';
9
+
10
+ export class GoalRuntimeError extends Error {
11
+ constructor(
12
+ readonly code: GoalRuntimeErrorCode,
13
+ message: string,
14
+ ) {
15
+ super(message);
16
+ this.name = 'GoalRuntimeError';
17
+ }
18
+ }