minimal-agent 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/README.md +6 -5
  2. package/package.json +62 -5
  3. package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
  4. package/plugins/cron-runner/src/commands.js +4 -4
  5. package/plugins/cron-runner/src/commands.ts +4 -4
  6. package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
  7. package/plugins/workflow-runner/commands/workflow.md +57 -90
  8. package/plugins/workflow-runner/commands/workflows.md +4 -4
  9. package/plugins/workflow-runner/plugin.js +24 -13
  10. package/plugins/workflow-runner/plugin.ts +28 -13
  11. package/plugins/workflow-runner/src/discovery.js +160 -0
  12. package/plugins/workflow-runner/src/discovery.ts +194 -0
  13. package/plugins/workflow-runner/src/goalAnchor.js +53 -0
  14. package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
  15. package/plugins/workflow-runner/src/index.js +204 -185
  16. package/plugins/workflow-runner/src/index.ts +249 -224
  17. package/plugins/workflow-runner/src/inputCoercion.js +40 -0
  18. package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
  19. package/plugins/workflow-runner/src/journal.js +224 -0
  20. package/plugins/workflow-runner/src/journal.ts +274 -0
  21. package/plugins/workflow-runner/src/jsonSchema.js +132 -0
  22. package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
  23. package/plugins/workflow-runner/src/moduleResolution.js +64 -0
  24. package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
  25. package/plugins/workflow-runner/src/resourceInputs.js +220 -0
  26. package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
  27. package/plugins/workflow-runner/src/runner.js +404 -467
  28. package/plugins/workflow-runner/src/runner.ts +491 -535
  29. package/plugins/workflow-runner/src/runtime/config.js +46 -0
  30. package/plugins/workflow-runner/src/runtime/config.ts +69 -0
  31. package/plugins/workflow-runner/src/runtime/context.js +1304 -0
  32. package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
  33. package/plugins/workflow-runner/src/runtime/exec.js +34 -0
  34. package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
  35. package/plugins/workflow-runner/src/runtime/flow.js +8 -0
  36. package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
  37. package/plugins/workflow-runner/src/runtime/fs.js +27 -0
  38. package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
  39. package/plugins/workflow-runner/src/runtime/model.js +6 -0
  40. package/plugins/workflow-runner/src/runtime/model.ts +8 -0
  41. package/plugins/workflow-runner/src/runtime/output.js +7 -0
  42. package/plugins/workflow-runner/src/runtime/output.ts +21 -0
  43. package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
  44. package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
  45. package/plugins/workflow-runner/src/runtime/state.js +16 -0
  46. package/plugins/workflow-runner/src/runtime/state.ts +19 -0
  47. package/plugins/workflow-runner/src/runtime/tools.js +3 -0
  48. package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
  49. package/plugins/workflow-runner/src/sandbox.js +141 -0
  50. package/plugins/workflow-runner/src/sandbox.ts +182 -0
  51. package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
  52. package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
  53. package/plugins/workflow-runner/src/taskIdentity.js +172 -152
  54. package/plugins/workflow-runner/src/taskIdentity.ts +233 -200
  55. package/plugins/workflow-runner/src/toolRuntime.js +29 -8
  56. package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
  57. package/plugins/workflow-runner/src/types.js +7 -105
  58. package/plugins/workflow-runner/src/types.ts +155 -520
  59. package/plugins/workflow-runner/src/util.js +47 -0
  60. package/plugins/workflow-runner/src/util.ts +51 -0
  61. package/plugins/workflow-runner/src/workflowContext.js +20 -0
  62. package/plugins/workflow-runner/src/workflowContext.ts +58 -0
  63. package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
  64. package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
  65. package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
  66. package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
  67. package/plugins/workflow-runner/src/workflowState.js +542 -469
  68. package/plugins/workflow-runner/src/workflowState.ts +639 -589
  69. package/plugins/workflow-runner/test/api.test.ts +223 -0
  70. package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
  71. package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
  72. package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
  73. package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
  74. package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
  75. package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
  76. package/plugins/workflow-runner/test/discovery.test.ts +234 -0
  77. package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
  78. package/plugins/workflow-runner/test/goalController.test.ts +317 -0
  79. package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
  80. package/plugins/workflow-runner/test/listResources.test.ts +106 -0
  81. package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
  82. package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
  83. package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
  84. package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
  85. package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
  86. package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
  87. package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
  88. package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
  89. package/skills/pptx/examples/assets/cover-bg.svg +23 -0
  90. package/skills/pptx/examples/assets/icon-spark.svg +5 -0
  91. package/skills/skill-creator/scripts/run_eval.py +3 -2
  92. package/skills/ui-design/SKILL.md +5 -2
  93. package/skills/ui-design/references/layout-composition.md +1 -1
  94. package/skills/workflow-creator/SKILL.md +344 -0
  95. package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
  96. package/skills/workflow-creator/references/api.md +216 -0
  97. package/skills/workflow-creator/references/migration.md +103 -0
  98. package/skills/workflow-creator/references/patterns.md +779 -0
  99. package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
  100. package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
  101. package/skills/workflow-creator/scripts/validate.mjs +275 -0
  102. package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
  103. package/src/advisor/advisorCoordinator.js +503 -0
  104. package/src/advisor/advisorCoordinator.ts +657 -0
  105. package/src/cli/configMenu.tsx +0 -1
  106. package/src/cli/print.js +44 -6
  107. package/src/cli/print.ts +73 -4
  108. package/src/cli/streamJson.js +5 -6
  109. package/src/cli/streamJson.ts +8 -4
  110. package/src/cli/wizards/advisorWizard.js +4 -2
  111. package/src/cli/wizards/advisorWizard.tsx +8 -2
  112. package/src/cli/wizards/contextAdvisorWizard.js +5 -4
  113. package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
  114. package/src/collaboration/taskFlow.js +115 -0
  115. package/src/collaboration/taskFlow.ts +169 -0
  116. package/src/config/configDefaults.js +41 -1
  117. package/src/config/configDefaults.ts +43 -1
  118. package/src/config/configDiagnostics.ts +2 -2
  119. package/src/config/configFile.js +40 -9
  120. package/src/config/configFile.ts +60 -11
  121. package/src/config/configService.js +443 -74
  122. package/src/config/configService.ts +723 -142
  123. package/src/config/numericLimits.js +22 -0
  124. package/src/config/numericLimits.ts +24 -0
  125. package/src/config.js +1 -0
  126. package/src/config.ts +3 -2
  127. package/src/context/budgetPlan.js +1 -1
  128. package/src/context/budgetPlan.ts +1 -1
  129. package/src/context/compact.js +7 -2
  130. package/src/context/compact.ts +9 -2
  131. package/src/context/microCompactLite.js +212 -13
  132. package/src/context/microCompactLite.ts +261 -12
  133. package/src/context/sessionRegistry.js +8 -1
  134. package/src/context/sessionRegistry.ts +18 -5
  135. package/src/context/toolResultArchive.js +72 -0
  136. package/src/context/toolResultArchive.ts +95 -0
  137. package/src/goal/errors.js +8 -0
  138. package/src/goal/errors.ts +18 -0
  139. package/src/goal/goalRuntime.js +344 -0
  140. package/src/goal/goalRuntime.ts +466 -0
  141. package/src/goal/goalStore.js +200 -0
  142. package/src/goal/goalStore.ts +219 -0
  143. package/src/goal/index.js +4 -0
  144. package/src/goal/index.ts +4 -0
  145. package/src/goal/statusMapping.js +71 -0
  146. package/src/goal/statusMapping.ts +95 -0
  147. package/src/goal/types.js +7 -0
  148. package/src/goal/types.ts +179 -0
  149. package/src/goal/workflowController.js +306 -0
  150. package/src/goal/workflowController.ts +360 -0
  151. package/src/harness/evidenceLedger.js +2 -0
  152. package/src/harness/evidenceLedger.ts +1 -0
  153. package/src/harness/stallGuard.js +9 -3
  154. package/src/harness/stallGuard.ts +7 -3
  155. package/src/images/imageResolver.js +161 -0
  156. package/src/images/imageResolver.ts +189 -0
  157. package/src/infra/verification.js +56 -20
  158. package/src/infra/verification.ts +87 -20
  159. package/src/llm/client.js +79 -21
  160. package/src/llm/client.ts +182 -35
  161. package/src/llm/gatewayClient.js +201 -0
  162. package/src/llm/gatewayClient.ts +299 -0
  163. package/src/llm/messageProvenance.js +46 -0
  164. package/src/llm/messageProvenance.ts +58 -0
  165. package/src/llm/modelProvider.js +1 -0
  166. package/src/llm/modelProvider.ts +22 -0
  167. package/src/llm/providers/anthropicFormat.js +108 -1
  168. package/src/llm/providers/anthropicFormat.ts +115 -4
  169. package/src/llm/providers/openaiFormat.js +9 -0
  170. package/src/llm/providers/openaiFormat.ts +12 -3
  171. package/src/loop.js +319 -54
  172. package/src/loop.ts +373 -57
  173. package/src/main.js +4 -1
  174. package/src/main.tsx +4 -1
  175. package/src/managedExecution/index.js +2 -0
  176. package/src/managedExecution/index.ts +2 -0
  177. package/src/managedExecution/registry.js +15 -0
  178. package/src/managedExecution/registry.ts +20 -0
  179. package/src/managedExecution/types.js +1 -0
  180. package/src/managedExecution/types.ts +86 -0
  181. package/src/modelRef.js +9 -0
  182. package/src/modelRef.ts +31 -0
  183. package/src/multimodal.js +14 -28
  184. package/src/multimodal.ts +17 -33
  185. package/src/net/lanDiscovery.js +65 -0
  186. package/src/net/lanDiscovery.ts +82 -0
  187. package/src/plugin-sdk.js +28 -3
  188. package/src/plugin-sdk.ts +39 -6
  189. package/src/plugins/pluginApi.ts +16 -1
  190. package/src/plugins/pluginLoader.js +4 -0
  191. package/src/plugins/pluginLoader.ts +6 -0
  192. package/src/plugins/pluginRunner.js +152 -9
  193. package/src/plugins/pluginRunner.ts +186 -8
  194. package/src/plugins/slashFallback.js +113 -0
  195. package/src/plugins/slashFallback.ts +137 -0
  196. package/src/prompts/policyRegistry.js +15 -8
  197. package/src/prompts/policyRegistry.ts +18 -8
  198. package/src/prompts/promptAssembly.js +103 -12
  199. package/src/prompts/promptAssembly.ts +114 -12
  200. package/src/prompts/skillList.js +21 -3
  201. package/src/prompts/skillList.ts +27 -2
  202. package/src/prompts/stableSystemPrompt.js +18 -0
  203. package/src/prompts/stableSystemPrompt.ts +33 -0
  204. package/src/runtime/agentRuntimeContext.js +8 -2
  205. package/src/runtime/agentRuntimeContext.ts +17 -4
  206. package/src/session/runAgentTurn.js +78 -12
  207. package/src/session/runAgentTurn.ts +105 -23
  208. package/src/session/runTurn.js +10 -9
  209. package/src/session/runTurn.ts +17 -8
  210. package/src/session/sessionState.ts +2 -0
  211. package/src/team/auth.js +186 -0
  212. package/src/team/auth.ts +298 -0
  213. package/src/team/cli.js +105 -0
  214. package/src/team/cli.ts +122 -0
  215. package/src/team/onlineToolContract.js +64 -0
  216. package/src/team/onlineToolContract.ts +109 -0
  217. package/src/team/toolPolicy.js +36 -0
  218. package/src/team/toolPolicy.ts +75 -0
  219. package/src/tools/advisor/advisor.js +148 -37
  220. package/src/tools/advisor/advisor.ts +161 -38
  221. package/src/tools/advisor/advisorDecision.js +71 -0
  222. package/src/tools/advisor/advisorDecision.ts +85 -0
  223. package/src/tools/agent/agent.js +18 -5
  224. package/src/tools/agent/agent.ts +21 -7
  225. package/src/tools/agent/agentControl.js +246 -0
  226. package/src/tools/agent/agentControl.ts +284 -0
  227. package/src/tools/agent/agentTaskService.js +40 -1
  228. package/src/tools/agent/agentTaskService.ts +44 -1
  229. package/src/tools/agent/agentTypes.js +14 -2
  230. package/src/tools/agent/agentTypes.ts +15 -2
  231. package/src/tools/applypatch/applyPatch.js +362 -0
  232. package/src/tools/applypatch/applyPatch.ts +428 -0
  233. package/src/tools/bash/bash.js +70 -18
  234. package/src/tools/bash/bash.ts +75 -20
  235. package/src/tools/capabilities.js +10 -5
  236. package/src/tools/capabilities.ts +10 -5
  237. package/src/tools/computeruse/computerIntent.js +32 -0
  238. package/src/tools/computeruse/computerIntent.ts +46 -0
  239. package/src/tools/computeruse/telemetryLog.js +12 -0
  240. package/src/tools/computeruse/telemetryLog.ts +16 -0
  241. package/src/tools/computeruse/vision/telemetry.js +1 -0
  242. package/src/tools/computeruse/vision/telemetry.ts +1 -0
  243. package/src/tools/effectiveToolSet.js +4 -0
  244. package/src/tools/effectiveToolSet.ts +4 -0
  245. package/src/tools/index.js +21 -2
  246. package/src/tools/index.ts +33 -2
  247. package/src/tools/powershell/powershell.js +18 -1
  248. package/src/tools/powershell/powershell.ts +18 -2
  249. package/src/tools/presentresult/presentresult.js +6 -15
  250. package/src/tools/presentresult/presentresult.ts +6 -15
  251. package/src/tools/read/read.js +69 -11
  252. package/src/tools/read/read.ts +70 -11
  253. package/src/tools/shared/fileUtils.js +1 -2
  254. package/src/tools/shared/fileUtils.ts +1 -2
  255. package/src/tools/shared/permanentDeleteGuard.js +34 -3
  256. package/src/tools/shared/permanentDeleteGuard.ts +43 -6
  257. package/src/tools/shared/toolOutputStore.js +23 -1
  258. package/src/tools/shared/toolOutputStore.ts +22 -1
  259. package/src/tools/skill/skill.js +9 -3
  260. package/src/tools/skill/skill.ts +8 -4
  261. package/src/tools/toolPromptGuides.js +117 -0
  262. package/src/tools/toolPromptGuides.ts +137 -0
  263. package/src/tools/webbrowser/agentBrowserPath.js +19 -9
  264. package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
  265. package/src/tools/webbrowser/browser.js +27 -11
  266. package/src/tools/webbrowser/browser.ts +68 -24
  267. package/src/tools/webbrowser/browserAutomation.js +329 -0
  268. package/src/tools/webbrowser/browserAutomation.ts +467 -0
  269. package/src/tools/webbrowser/browserAxState.js +97 -0
  270. package/src/tools/webbrowser/browserAxState.ts +142 -0
  271. package/src/tools/webbrowser/browserBatch.js +1578 -0
  272. package/src/tools/webbrowser/browserBatch.ts +2382 -0
  273. package/src/tools/webbrowser/browserCdp.js +208 -0
  274. package/src/tools/webbrowser/browserCdp.ts +255 -0
  275. package/src/tools/webbrowser/browserConditions.js +24 -0
  276. package/src/tools/webbrowser/browserConditions.ts +45 -0
  277. package/src/tools/webbrowser/browserContent.js +281 -0
  278. package/src/tools/webbrowser/browserContent.ts +438 -0
  279. package/src/tools/webbrowser/browserControl.js +131 -30
  280. package/src/tools/webbrowser/browserControl.ts +210 -50
  281. package/src/tools/webbrowser/browserDaemon.js +22 -14
  282. package/src/tools/webbrowser/browserDaemon.ts +54 -21
  283. package/src/tools/webbrowser/browserFrames.js +47 -0
  284. package/src/tools/webbrowser/browserFrames.ts +54 -0
  285. package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
  286. package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
  287. package/src/tools/webbrowser/browserLocator.js +140 -0
  288. package/src/tools/webbrowser/browserLocator.ts +194 -0
  289. package/src/tools/webbrowser/browserOutcome.js +10 -17
  290. package/src/tools/webbrowser/browserOutcome.ts +48 -31
  291. package/src/tools/webbrowser/browserRecovery.js +58 -0
  292. package/src/tools/webbrowser/browserRecovery.ts +83 -0
  293. package/src/tools/webbrowser/browserRunRegistry.js +35 -3
  294. package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
  295. package/src/tools/webbrowser/browserService.js +28 -2
  296. package/src/tools/webbrowser/browserService.ts +49 -9
  297. package/src/tools/webbrowser/browserStream.js +132 -33
  298. package/src/tools/webbrowser/browserStream.ts +224 -50
  299. package/src/tools/webbrowser/browserTabs.js +48 -15
  300. package/src/tools/webbrowser/browserTabs.ts +178 -96
  301. package/src/tools/webbrowser/webbrowser.js +683 -144
  302. package/src/tools/webbrowser/webbrowser.ts +979 -236
  303. package/src/tools/webfetch/preapproved.js +0 -2
  304. package/src/tools/webfetch/preapproved.ts +0 -2
  305. package/src/tools/websearch/websearch.js +129 -8
  306. package/src/tools/websearch/websearch.ts +138 -6
  307. package/src/types.ts +450 -42
  308. package/src/ui/App.js +377 -58
  309. package/src/ui/App.tsx +576 -111
  310. package/src/ui/LiveArea.js +14 -8
  311. package/src/ui/LiveArea.tsx +14 -10
  312. package/src/ui/MessageList.js +63 -20
  313. package/src/ui/MessageList.tsx +101 -16
  314. package/src/ui/ModelPicker.js +7 -4
  315. package/src/ui/ModelPicker.tsx +55 -8
  316. package/src/ui/hooks/useChat.js +152 -163
  317. package/src/ui/hooks/useChat.ts +396 -334
  318. package/src/ui/loginCommand.js +56 -0
  319. package/src/ui/loginCommand.ts +63 -0
  320. package/src/ui/toolProgress.js +41 -0
  321. package/src/ui/toolProgress.ts +44 -0
  322. package/src/web/browserBridge.js +20 -2
  323. package/src/web/browserBridge.ts +20 -2
  324. package/src/web/protocol.ts +1897 -194
  325. package/workflows/adaptive-research/workflow.mjs +86 -0
  326. package/workflows/e2e-capabilities/workflow.mjs +102 -0
  327. package/workflows/e2e-llm/workflow.mjs +52 -0
  328. package/workflows/e2e-smoke/workflow.mjs +59 -0
  329. package/workflows/e2e-vision-schema/README.md +48 -0
  330. package/workflows/e2e-vision-schema/workflow.mjs +149 -0
  331. package/workflows/e2e-write-greet/workflow.mjs +37 -0
  332. package/workflows/file-census/README.md +14 -0
  333. package/workflows/file-census/scripts/.gitkeep +2 -0
  334. package/workflows/file-census/workflow.mjs +110 -0
  335. package/workflows/goal/README.md +166 -0
  336. package/workflows/goal/prompts/audit.md +57 -0
  337. package/workflows/goal/prompts/contract.md +56 -0
  338. package/workflows/goal/prompts/iterate.md +37 -0
  339. package/workflows/goal/workflow.mjs +501 -0
  340. package/workflows/illustrate/README.md +90 -0
  341. package/workflows/illustrate/assets/engines.json +20 -0
  342. package/workflows/illustrate/assets/extract-baidu.js +21 -0
  343. package/workflows/illustrate/assets/extract-bing.js +33 -0
  344. package/workflows/illustrate/assets/extract-google.js +31 -0
  345. package/workflows/illustrate/workflow.mjs +301 -0
  346. package/workflows/plan/workflow.mjs +389 -0
  347. package/workflows/question-crops/README.md +35 -0
  348. package/workflows/question-crops/prompts/inventory.md +23 -0
  349. package/workflows/question-crops/scripts/image-ops.mjs +305 -0
  350. package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
  351. package/workflows/question-crops/workflow.mjs +172 -0
  352. package/workflows/ralph-loop/workflow.mjs +162 -0
  353. package/workflows/research/DESIGN.md +449 -0
  354. package/workflows/research/README.md +74 -0
  355. package/workflows/research/prompts/s0-goal.md +43 -0
  356. package/workflows/research/prompts/s1-early-exit.md +22 -0
  357. package/workflows/research/prompts/s1-map.md +58 -0
  358. package/workflows/research/prompts/s2-options.md +53 -0
  359. package/workflows/research/prompts/s3-deep-dive.md +56 -0
  360. package/workflows/research/prompts/s4-verify.md +74 -0
  361. package/workflows/research/prompts/s5-synthesis.md +81 -0
  362. package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
  363. package/workflows/research/prompts/s6-package.md +32 -0
  364. package/workflows/research/scripts/build-report.mjs +98 -0
  365. package/workflows/research/workflow.mjs +288 -0
  366. package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
  367. package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
  368. package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
  369. package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
  370. package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
  371. package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
  372. package/workflows/wechat-reading-shots/workflow.mjs +266 -0
  373. package/workflows/wrong-question-docx/README.md +55 -0
  374. package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
  375. package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
  376. package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
  377. package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
  378. package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
  379. package/workflows/wrong-question-docx/workflow.mjs +485 -0
  380. package/plugins/workflow-runner/src/agentCommon.js +0 -354
  381. package/plugins/workflow-runner/src/agentCommon.ts +0 -435
  382. package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
  383. package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
  384. package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
  385. package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
  386. package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
  387. package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
  388. package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
  389. package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
  390. package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
  391. package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
  392. package/plugins/workflow-runner/src/dslManifest.js +0 -72
  393. package/plugins/workflow-runner/src/dslManifest.ts +0 -127
  394. package/plugins/workflow-runner/src/expressions.js +0 -618
  395. package/plugins/workflow-runner/src/expressions.ts +0 -622
  396. package/plugins/workflow-runner/src/loader.js +0 -573
  397. package/plugins/workflow-runner/src/loader.ts +0 -721
  398. package/plugins/workflow-runner/src/multimodal.js +0 -38
  399. package/plugins/workflow-runner/src/multimodal.ts +0 -64
  400. package/plugins/workflow-runner/src/recovery.js +0 -119
  401. package/plugins/workflow-runner/src/recovery.ts +0 -149
  402. package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
  403. package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
  404. package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -41
  405. package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -50
  406. package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
  407. package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
  408. package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
  409. package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
  410. package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
  411. package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
  412. package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
  413. package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
  414. package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -208
  415. package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -256
  416. package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -58
  417. package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -68
  418. package/plugins/workflow-runner/src/stepPipeline.js +0 -277
  419. package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
  420. package/plugins/workflow-runner/test/agent.test.ts +0 -587
  421. package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
  422. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
  423. package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
  424. package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -147
  425. package/plugins/workflow-runner/test/context-files.test.ts +0 -320
  426. package/plugins/workflow-runner/test/e2e.test.ts +0 -370
  427. package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -226
  428. package/plugins/workflow-runner/test/expressions.test.ts +0 -397
  429. package/plugins/workflow-runner/test/foundation.test.ts +0 -126
  430. package/plugins/workflow-runner/test/graceful.test.ts +0 -139
  431. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
  432. package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
  433. package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
  434. package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
  435. package/plugins/workflow-runner/test/loader.test.ts +0 -617
  436. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
  437. package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
  438. package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
  439. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -87
  440. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
  441. package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
  442. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -220
  443. package/plugins/workflow-runner/test/recovery.test.ts +0 -265
  444. package/plugins/workflow-runner/test/runner.test.ts +0 -911
  445. package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -379
  446. package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
  447. package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -150
  448. package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
  449. package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -338
  450. package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -270
  451. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
  452. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
  453. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
  454. package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
  455. package/plugins/workflow-runner/test/word.load.test.ts +0 -208
  456. package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
  457. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
  458. package/src/advisorState.js +0 -22
  459. package/src/cli/configWizard.js +0 -293
  460. package/src/tools/toolProgressBridge.js +0 -48
  461. package/workflows/adaptive-research.yaml +0 -50
  462. package/workflows/book-review-short.yaml +0 -125
  463. package/workflows/e2e-write-greet.yaml +0 -31
  464. package/workflows/illustrate.yaml +0 -227
  465. package/workflows/plan.yaml +0 -470
  466. package/workflows/ppt.yaml +0 -1670
  467. package/workflows/ralph-loop.yaml +0 -199
  468. package/workflows/schema.json +0 -176
  469. package/workflows/webppt.yaml +0 -496
  470. package/workflows/wechat-reading-shots.yaml +0 -315
  471. package/workflows/word.yaml +0 -720
  472. package/workflows/youtube-shorts.yaml +0 -202
@@ -91,21 +91,12 @@ async function call(input) {
91
91
  return { ok: false, error: '网页结果只支持 http 或 https 链接' };
92
92
  }
93
93
  const hasFailedFile = files.some((file) => file.verification === 'failed');
94
- const allFilesVerified = files.length > 0 && files.every((file) => file.verification === 'passed');
95
- // `final` means the result is safe to present as the completed delivery. A file
96
- // with `not_run` verification is still useful, but it must remain an honest draft.
97
- const status = hasFailedFile
98
- ? 'draft'
99
- : allFilesVerified
100
- ? 'final'
101
- : files.length === 0 && links.length > 0
102
- ? 'final'
103
- : 'draft';
104
- const warnings = files.some((file) => file.verification === 'not_run')
105
- ? ['文件已提交,但尚未通过结构化验证。']
106
- : hasFailedFile
107
- ? ['至少一个文件未通过结构化验证。']
108
- : [];
94
+ // `not_run` means no *additional* format/content validator was available. It
95
+ // is not a failed delivery: this tool has already verified that the file exists,
96
+ // is inside the workspace, and captured its size and SHA-256. Only an explicit
97
+ // failed validator should turn an otherwise usable result into a warning.
98
+ const status = hasFailedFile ? 'draft' : 'final';
99
+ const warnings = hasFailedFile ? ['至少一个文件未通过结构化验证。'] : [];
109
100
  const manifest = {
110
101
  status,
111
102
  summary: input.summary?.trim() || (files.length ? `已提交 ${files.length} 个文件` : `已提交 ${links.length} 个网页结果`),
@@ -101,21 +101,12 @@ async function call(input: PresentResultInput): Promise<ToolResult> {
101
101
  return { ok: false, error: '网页结果只支持 http 或 https 链接' };
102
102
  }
103
103
  const hasFailedFile = files.some((file) => file.verification === 'failed');
104
- const allFilesVerified = files.length > 0 && files.every((file) => file.verification === 'passed');
105
- // `final` means the result is safe to present as the completed delivery. A file
106
- // with `not_run` verification is still useful, but it must remain an honest draft.
107
- const status = hasFailedFile
108
- ? 'draft'
109
- : allFilesVerified
110
- ? 'final'
111
- : files.length === 0 && links.length > 0
112
- ? 'final'
113
- : 'draft';
114
- const warnings = files.some((file) => file.verification === 'not_run')
115
- ? ['文件已提交,但尚未通过结构化验证。']
116
- : hasFailedFile
117
- ? ['至少一个文件未通过结构化验证。']
118
- : [];
104
+ // `not_run` means no *additional* format/content validator was available. It
105
+ // is not a failed delivery: this tool has already verified that the file exists,
106
+ // is inside the workspace, and captured its size and SHA-256. Only an explicit
107
+ // failed validator should turn an otherwise usable result into a warning.
108
+ const status = hasFailedFile ? 'draft' : 'final';
109
+ const warnings = hasFailedFile ? ['至少一个文件未通过结构化验证。'] : [];
119
110
  const manifest: DeliverableManifest = {
120
111
  status,
121
112
  summary: input.summary?.trim() || (files.length ? `已提交 ${files.length} 个文件` : `已提交 ${links.length} 个网页结果`),
@@ -8,6 +8,7 @@ import { join } from 'node:path';
8
8
  import { promisify } from 'node:util';
9
9
  import { randomUUID } from 'node:crypto';
10
10
  import { z } from 'zod';
11
+ import { dlog } from '../../utils/debugLog.js';
11
12
  import { toToolParameters } from '../../utils/zodToJson.js';
12
13
  import { DEFAULT_MAX_RESULT_SIZE_CHARS, MAX_FILE_SIZE_BYTES, MAX_LINES_TO_READ, } from '../types.js';
13
14
  import { fileActivityMetadata } from '../shared/fileActivity.js';
@@ -15,6 +16,7 @@ import { hashFileBytes, recordRead, } from '../shared/fileState.js';
15
16
  import { hasBinaryExtension, isBlockedDevicePath, readTextFile, resolveToolPath, } from '../shared/fileUtils.js';
16
17
  import { filePathField } from '../shared/schemas.js';
17
18
  import { pruneRuntimeOutputs, sessionRuntimeDir, } from '../../session/runtimeStore.js';
19
+ import { detectImageMime, resolveLocalImage, } from '../../images/imageResolver.js';
18
20
  const inputSchema = z.object({
19
21
  file_path: filePathField('读取'),
20
22
  offset: z
@@ -46,6 +48,12 @@ const IMAGE_EXTENSIONS = new Set([
46
48
  '.gif',
47
49
  '.webp',
48
50
  '.bmp',
51
+ '.tif',
52
+ '.tiff',
53
+ '.avif',
54
+ '.heic',
55
+ '.heif',
56
+ '.svg',
49
57
  ]);
50
58
  const MAX_IMAGE_BYTES = 25 * 1024 * 1024;
51
59
  const MAX_NOTEBOOK_BYTES = 32 * 1024 * 1024;
@@ -168,10 +176,15 @@ async function call(input, state, cwd, runtimeIdentity) {
168
176
  };
169
177
  }
170
178
  const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');
171
- const document = await pdfjs.getDocument({
179
+ // ⚠️ 释放要调**加载任务**的 destroy,不是文档代理的:pdfjs v6 的
180
+ // PDFDocumentProxy 上没有 destroy(只有 cleanup),写成 document.destroy()
181
+ // 会在 finally 里抛 TypeError,被外层 catch 成「PDF 解析失败」——
182
+ // 于是每一次 PDF 读取都失败,且错误文案完全指不向真因。
183
+ const loadingTask = pdfjs.getDocument({
172
184
  data: new Uint8Array(bytes),
173
185
  useSystemFonts: true,
174
- }).promise;
186
+ });
187
+ const document = await loadingTask.promise;
175
188
  try {
176
189
  const requested = parsePdfPages(input.pages, document.numPages);
177
190
  if (requested.length > 20)
@@ -209,7 +222,7 @@ async function call(input, state, cwd, runtimeIdentity) {
209
222
  };
210
223
  }
211
224
  finally {
212
- await document.destroy().catch(() => undefined);
225
+ await loadingTask.destroy().catch(() => undefined);
213
226
  }
214
227
  }
215
228
  catch (error) {
@@ -220,16 +233,29 @@ async function call(input, state, cwd, runtimeIdentity) {
220
233
  };
221
234
  }
222
235
  }
223
- if (IMAGE_EXTENSIONS.has(ext)) {
236
+ const imageBytes = info.size <= MAX_IMAGE_BYTES
237
+ ? await readFile(filePath).catch(() => null)
238
+ : null;
239
+ const detectedImage = imageBytes ? detectImageMime(imageBytes) : null;
240
+ if (IMAGE_EXTENSIONS.has(ext) || detectedImage) {
224
241
  if (info.size > MAX_IMAGE_BYTES)
225
242
  return { ok: false, error: `图片超过 ${MAX_IMAGE_BYTES} 字节上限` };
226
- const bytes = await readFile(filePath);
243
+ let resolvedImage;
244
+ try {
245
+ resolvedImage = await resolveLocalImage(filePath, {
246
+ maxInputBytes: MAX_IMAGE_BYTES,
247
+ });
248
+ }
249
+ catch (error) {
250
+ return { ok: false, error: `图片读取失败:${error.message}` };
251
+ }
252
+ const bytes = imageBytes ?? await readFile(filePath);
227
253
  recordRead(filePath, { contentHash: hashFileBytes(bytes) }, state);
228
254
  return {
229
255
  ok: true,
230
- content: `已读取图片:${filePath}(${info.size} 字节)。图像已附加供视觉模型检查;非视觉模型只能确认文件元数据,不能解读像素内容。`,
256
+ content: `已读取图片:${filePath}(${info.size} 字节,实际格式 ${resolvedImage.originalMime})${resolvedImage.transformed ? `;已规范化为 ${resolvedImage.mime}` : ''}。图像已附加供视觉模型直接检查。`,
231
257
  metadata: await fileActivityMetadata(filePath, 'read', 'not_run', 'source', cwd),
232
- visionImages: [{ source: filePath }],
258
+ visionImages: [{ source: resolvedImage.path }],
233
259
  visionNote: '这是 Read 工具读取的本地图片,请直接检查图像内容。',
234
260
  };
235
261
  }
@@ -347,10 +373,30 @@ async function renderPdfPages(document, filePath, pages, identity) {
347
373
  const dynamicImport = new Function('specifier', 'return import(specifier)');
348
374
  let createCanvas;
349
375
  try {
350
- ({ createCanvas } = await dynamicImport('@napi-rs/canvas'));
376
+ const napi = await dynamicImport('@napi-rs/canvas');
377
+ createCanvas = napi.createCanvas;
378
+ // ⚠️ **必须在 pdfjs 之前把这几个全局占住**,否则 PDF 转图 100% 失败。
379
+ //
380
+ // pdfjs 的 legacy 构建在 Node 环境下自己往 globalThis 装一套 Path2D /
381
+ // DOMMatrix / ImageData polyfill。渲染时它 `new Path2D()` 拿到的是**它自己那份**,
382
+ // 转手调 `ctx.fill(path, 'evenodd')` 交给 @napi-rs/canvas —— 而 napi 的原生绑定
383
+ // 只认自己的 `Path`,于是每一页都抛
384
+ // `Value is none of these types \`String\`, \`Path\``
385
+ // 两份 polyfill 撞车,谁都没错,就是不是同一个对象。
386
+ //
387
+ // 实测:五种 render 参数组合(canvas / canvasContext / canvas:null / 带不带
388
+ // standardFontDataUrl)全部同样失败 —— 也就是说 `canvas` 那个类型错**不是**病因,
389
+ // 修类型并不能让它跑起来。把全局换成 napi 的实现之后第 1 页立刻渲染出 137KB PNG。
390
+ for (const name of ['Path2D', 'DOMMatrix', 'ImageData']) {
391
+ const impl = napi[name];
392
+ if (impl)
393
+ globalThis[name] = impl;
394
+ }
351
395
  }
352
- catch {
353
- /* optional adapter */
396
+ catch (e) {
397
+ // optional adapter:没装 @napi-rs/canvas 就回落 pdftoppm。
398
+ // 但「没装」和「装了却加载失败」对用户是两件事,别都吞成前者。
399
+ dlog('read:pdf', `@napi-rs/canvas 不可用:${e instanceof Error ? e.message : e}`);
354
400
  }
355
401
  if (createCanvas) {
356
402
  try {
@@ -358,8 +404,13 @@ async function renderPdfPages(document, filePath, pages, identity) {
358
404
  const page = await document.getPage(pageNumber);
359
405
  const viewport = page.getViewport({ scale: 2 });
360
406
  const canvas = createCanvas(Math.ceil(viewport.width), Math.ceil(viewport.height));
407
+ // ⚠️ pdfjs v6 的 RenderParameters 里 `canvas` 是**必填**、`canvasContext`
408
+ // 反而降级为可选且不推荐;文档明说「若必须用 context,canvas 必须为 null」。
409
+ // 只给 canvasContext 会类型不过(`bun run typecheck` 唯一的红)。
410
+ // 运行时它有默认值 `canvas = canvasContext.canvas` 所以不炸 —— 但类型要对。
361
411
  await page.render({
362
412
  canvasContext: canvas.getContext('2d'),
413
+ canvas: null,
363
414
  viewport,
364
415
  }).promise;
365
416
  const target = join(outputDir, `${outputPrefix}-page-${pageNumber}.png`);
@@ -369,7 +420,14 @@ async function renderPdfPages(document, filePath, pages, identity) {
369
420
  void pruneRuntimeOutputs();
370
421
  return images;
371
422
  }
372
- catch {
423
+ catch (e) {
424
+ // ⚠️ 别把这里改回裸 catch。上面那个 Path2D 冲突就是被它藏住的:
425
+ // Canvas 每页都抛 → images 清空 → 回落 pdftoppm。装了 poppler 的机器照样
426
+ // 出图(只是分辨率更低、多一次进程),**完全看不出来**;没装 poppler 的机器
427
+ // (多数 Windows、绿色版 U 盘)则返回 `ok:true` + 0 张图 + 一句
428
+ // 「视觉渲染不可用(Canvas 与 pdftoppm 均不可用)」——而 Canvas 明明可用,
429
+ // 只是抛了。错误文案把人指向「装依赖」,真因一个字都查不到。
430
+ dlog('read:pdf', `Canvas 渲染失败:${e instanceof Error ? e.message : e}`);
373
431
  images.length = 0;
374
432
  }
375
433
  }
@@ -10,6 +10,7 @@ import { randomUUID } from 'node:crypto';
10
10
  import { z } from 'zod';
11
11
 
12
12
  import type { Tool, ToolCallContext, ToolResult } from '../../types.js';
13
+ import { dlog } from '../../utils/debugLog.js';
13
14
  import { toToolParameters } from '../../utils/zodToJson.js';
14
15
  import {
15
16
  DEFAULT_MAX_RESULT_SIZE_CHARS,
@@ -34,6 +35,10 @@ import {
34
35
  sessionRuntimeDir,
35
36
  type RuntimeIdentity,
36
37
  } from '../../session/runtimeStore.js';
38
+ import {
39
+ detectImageMime,
40
+ resolveLocalImage,
41
+ } from '../../images/imageResolver.js';
37
42
 
38
43
  const inputSchema = z.object({
39
44
  file_path: filePathField('读取'),
@@ -70,6 +75,12 @@ const IMAGE_EXTENSIONS = new Set([
70
75
  '.gif',
71
76
  '.webp',
72
77
  '.bmp',
78
+ '.tif',
79
+ '.tiff',
80
+ '.avif',
81
+ '.heic',
82
+ '.heif',
83
+ '.svg',
73
84
  ]);
74
85
  const MAX_IMAGE_BYTES = 25 * 1024 * 1024;
75
86
  const MAX_NOTEBOOK_BYTES = 32 * 1024 * 1024;
@@ -219,10 +230,15 @@ async function call(
219
230
  };
220
231
  }
221
232
  const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');
222
- const document = await pdfjs.getDocument({
233
+ // ⚠️ 释放要调**加载任务**的 destroy,不是文档代理的:pdfjs v6 的
234
+ // PDFDocumentProxy 上没有 destroy(只有 cleanup),写成 document.destroy()
235
+ // 会在 finally 里抛 TypeError,被外层 catch 成「PDF 解析失败」——
236
+ // 于是每一次 PDF 读取都失败,且错误文案完全指不向真因。
237
+ const loadingTask = pdfjs.getDocument({
223
238
  data: new Uint8Array(bytes),
224
239
  useSystemFonts: true,
225
- }).promise;
240
+ });
241
+ const document = await loadingTask.promise;
226
242
  try {
227
243
  const requested = parsePdfPages(input.pages, document.numPages);
228
244
  if (requested.length > 20)
@@ -279,7 +295,7 @@ async function call(
279
295
  : {}),
280
296
  };
281
297
  } finally {
282
- await document.destroy().catch(() => undefined);
298
+ await loadingTask.destroy().catch(() => undefined);
283
299
  }
284
300
  } catch (error) {
285
301
  const message = (error as Error).message;
@@ -290,14 +306,26 @@ async function call(
290
306
  }
291
307
  }
292
308
 
293
- if (IMAGE_EXTENSIONS.has(ext)) {
309
+ const imageBytes = info.size <= MAX_IMAGE_BYTES
310
+ ? await readFile(filePath).catch(() => null)
311
+ : null;
312
+ const detectedImage = imageBytes ? detectImageMime(imageBytes) : null;
313
+ if (IMAGE_EXTENSIONS.has(ext) || detectedImage) {
294
314
  if (info.size > MAX_IMAGE_BYTES)
295
315
  return { ok: false, error: `图片超过 ${MAX_IMAGE_BYTES} 字节上限` };
296
- const bytes = await readFile(filePath);
316
+ let resolvedImage;
317
+ try {
318
+ resolvedImage = await resolveLocalImage(filePath, {
319
+ maxInputBytes: MAX_IMAGE_BYTES,
320
+ });
321
+ } catch (error) {
322
+ return { ok: false, error: `图片读取失败:${(error as Error).message}` };
323
+ }
324
+ const bytes = imageBytes ?? await readFile(filePath);
297
325
  recordRead(filePath, { contentHash: hashFileBytes(bytes) }, state);
298
326
  return {
299
327
  ok: true,
300
- content: `已读取图片:${filePath}(${info.size} 字节)。图像已附加供视觉模型检查;非视觉模型只能确认文件元数据,不能解读像素内容。`,
328
+ content: `已读取图片:${filePath}(${info.size} 字节,实际格式 ${resolvedImage.originalMime})${resolvedImage.transformed ? `;已规范化为 ${resolvedImage.mime}` : ''}。图像已附加供视觉模型直接检查。`,
301
329
  metadata: await fileActivityMetadata(
302
330
  filePath,
303
331
  'read',
@@ -305,7 +333,7 @@ async function call(
305
333
  'source',
306
334
  cwd,
307
335
  ),
308
- visionImages: [{ source: filePath }],
336
+ visionImages: [{ source: resolvedImage.path }],
309
337
  visionNote: '这是 Read 工具读取的本地图片,请直接检查图像内容。',
310
338
  };
311
339
  }
@@ -470,9 +498,28 @@ async function renderPdfPages(
470
498
  }>;
471
499
  let createCanvas: Awaited<ReturnType<typeof dynamicImport>>['createCanvas'];
472
500
  try {
473
- ({ createCanvas } = await dynamicImport('@napi-rs/canvas'));
474
- } catch {
475
- /* optional adapter */
501
+ const napi = await dynamicImport('@napi-rs/canvas');
502
+ createCanvas = napi.createCanvas;
503
+ // ⚠️ **必须在 pdfjs 之前把这几个全局占住**,否则 PDF 转图 100% 失败。
504
+ //
505
+ // pdfjs 的 legacy 构建在 Node 环境下自己往 globalThis 装一套 Path2D /
506
+ // DOMMatrix / ImageData polyfill。渲染时它 `new Path2D()` 拿到的是**它自己那份**,
507
+ // 转手调 `ctx.fill(path, 'evenodd')` 交给 @napi-rs/canvas —— 而 napi 的原生绑定
508
+ // 只认自己的 `Path`,于是每一页都抛
509
+ // `Value is none of these types \`String\`, \`Path\``
510
+ // 两份 polyfill 撞车,谁都没错,就是不是同一个对象。
511
+ //
512
+ // 实测:五种 render 参数组合(canvas / canvasContext / canvas:null / 带不带
513
+ // standardFontDataUrl)全部同样失败 —— 也就是说 `canvas` 那个类型错**不是**病因,
514
+ // 修类型并不能让它跑起来。把全局换成 napi 的实现之后第 1 页立刻渲染出 137KB PNG。
515
+ for (const name of ['Path2D', 'DOMMatrix', 'ImageData'] as const) {
516
+ const impl = (napi as Record<string, unknown>)[name];
517
+ if (impl) (globalThis as Record<string, unknown>)[name] = impl;
518
+ }
519
+ } catch (e) {
520
+ // optional adapter:没装 @napi-rs/canvas 就回落 pdftoppm。
521
+ // 但「没装」和「装了却加载失败」对用户是两件事,别都吞成前者。
522
+ dlog('read:pdf', `@napi-rs/canvas 不可用:${e instanceof Error ? e.message : e}`);
476
523
  }
477
524
  if (createCanvas) {
478
525
  try {
@@ -483,8 +530,13 @@ async function renderPdfPages(
483
530
  Math.ceil(viewport.width),
484
531
  Math.ceil(viewport.height),
485
532
  );
533
+ // ⚠️ pdfjs v6 的 RenderParameters 里 `canvas` 是**必填**、`canvasContext`
534
+ // 反而降级为可选且不推荐;文档明说「若必须用 context,canvas 必须为 null」。
535
+ // 只给 canvasContext 会类型不过(`bun run typecheck` 唯一的红)。
536
+ // 运行时它有默认值 `canvas = canvasContext.canvas` 所以不炸 —— 但类型要对。
486
537
  await page.render({
487
538
  canvasContext: canvas.getContext('2d') as never,
539
+ canvas: null,
488
540
  viewport,
489
541
  }).promise;
490
542
  const target = join(
@@ -496,7 +548,14 @@ async function renderPdfPages(
496
548
  }
497
549
  void pruneRuntimeOutputs();
498
550
  return images;
499
- } catch {
551
+ } catch (e) {
552
+ // ⚠️ 别把这里改回裸 catch。上面那个 Path2D 冲突就是被它藏住的:
553
+ // Canvas 每页都抛 → images 清空 → 回落 pdftoppm。装了 poppler 的机器照样
554
+ // 出图(只是分辨率更低、多一次进程),**完全看不出来**;没装 poppler 的机器
555
+ // (多数 Windows、绿色版 U 盘)则返回 `ok:true` + 0 张图 + 一句
556
+ // 「视觉渲染不可用(Canvas 与 pdftoppm 均不可用)」——而 Canvas 明明可用,
557
+ // 只是抛了。错误文案把人指向「装依赖」,真因一个字都查不到。
558
+ dlog('read:pdf', `Canvas 渲染失败:${e instanceof Error ? e.message : e}`);
500
559
  images.length = 0;
501
560
  }
502
561
  }
@@ -115,7 +115,7 @@ export function applyLineEnding(content, ending) {
115
115
  // -------------------- 3.5 二进制扩展名拦截 --------------------
116
116
  // 上游 D:\code\kakadeai\constants\files.ts:5-112 BINARY_EXTENSIONS Set 的裁剪版:
117
117
  // 故意剔除 .log / .csv / .tsv / .dat / .data / .lock —— 这些经常被人当文本读。
118
- // 保留纯二进制:图片 / 文档 / 可执行 / 编译产物 / 压缩包 / 多媒体 / 字体 / 数据库 / wasm。
118
+ // 保留纯二进制:图片 / 文档 / 可执行 / 编译产物 / 压缩包 / 多媒体 / 字体 / wasm。
119
119
  export const BINARY_EXTENSIONS = new Set([
120
120
  '.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.ico', '.tiff', '.tif',
121
121
  '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
@@ -124,7 +124,6 @@ export const BINARY_EXTENSIONS = new Set([
124
124
  '.zip', '.tar', '.gz', '.bz2', '.7z', '.rar', '.iso',
125
125
  '.mp3', '.mp4', '.mov', '.avi', '.mkv', '.wav', '.flac', '.ogg',
126
126
  '.ttf', '.otf', '.woff', '.woff2',
127
- '.sqlite', '.sqlite3', '.db',
128
127
  '.psd', '.ai', '.bin', '.wasm',
129
128
  ]);
130
129
  export function hasBinaryExtension(filePath) {
@@ -152,7 +152,7 @@ export function applyLineEnding(content: string, ending: LineEndingType): string
152
152
 
153
153
  // 上游 D:\code\kakadeai\constants\files.ts:5-112 BINARY_EXTENSIONS Set 的裁剪版:
154
154
  // 故意剔除 .log / .csv / .tsv / .dat / .data / .lock —— 这些经常被人当文本读。
155
- // 保留纯二进制:图片 / 文档 / 可执行 / 编译产物 / 压缩包 / 多媒体 / 字体 / 数据库 / wasm。
155
+ // 保留纯二进制:图片 / 文档 / 可执行 / 编译产物 / 压缩包 / 多媒体 / 字体 / wasm。
156
156
  export const BINARY_EXTENSIONS = new Set<string>([
157
157
  '.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.ico', '.tiff', '.tif',
158
158
  '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
@@ -161,7 +161,6 @@ export const BINARY_EXTENSIONS = new Set<string>([
161
161
  '.zip', '.tar', '.gz', '.bz2', '.7z', '.rar', '.iso',
162
162
  '.mp3', '.mp4', '.mov', '.avi', '.mkv', '.wav', '.flac', '.ogg',
163
163
  '.ttf', '.otf', '.woff', '.woff2',
164
- '.sqlite', '.sqlite3', '.db',
165
164
  '.psd', '.ai', '.bin', '.wasm',
166
165
  ]);
167
166
 
@@ -1,17 +1,48 @@
1
- /** Minimal recoverability boundary for model-issued shell commands. */
1
+ /**
2
+ * 永久删除守卫:把「绕过回收站的删除」从 shell 里挡回去,逼模型改用可恢复的 Trash。
3
+ *
4
+ * ⚠️ 可靠性边界(与 src/irreversible/rules.ts 同一标准,别高估它):
5
+ * 这是**防模型误删**的护栏,不是对抗性沙盒。判据是对命令文本做正则匹配,
6
+ * 因此下列写法一律漏过,且**有意不追**(越追越像军备竞赛,误伤率先爆):
7
+ * · 绝对路径调用 /bin/rm -rf x
8
+ * · 间接执行 xargs rm / find -exec rm / echo $(rm x)
9
+ * · 换语言 python -c "import shutil; shutil.rmtree('x')"
10
+ * · 跨 shell 转发 cmd 里 powershell -c "Remove-Item x"
11
+ * 真正兜底的是「模型没有恶意」+ Trash 提供了更顺手的替代路径,不是这张正则表。
12
+ *
13
+ * ⚠️ `shell` 必须是**实际执行命令的那个 shell**,不是调用它的工具名。
14
+ * Bash 工具在 win32 找不到 Git Bash 时会回落 cmd.exe(见 tools/bash/bash.ts 的
15
+ * WIN_BASH 探测,且它的 description 会主动告诉模型「这里是 cmd」),那时模型写的是
16
+ * cmd 语法(del / rd),若仍按 'bash' 匹配 —— 三层防护全部落空、文件直接永久丢失。
17
+ */
18
+ /** POSIX shell:删除动词在命令位置(串首或 ; & | 换行之后)。 */
2
19
  const BASH_DELETE = [
3
20
  /(?:^|[;&|\n])\s*(?:sudo\s+)?(?:rm|unlink|rmdir)\b/i,
4
21
  /(?:^|[;&|\n])\s*find\b[^\n;&|]*\s-delete\b/i,
5
22
  ];
23
+ /**
24
+ * cmd.exe:del / erase 删文件,rd / rmdir 删目录,一律不进回收站。
25
+ * 分隔符**必须含 `&`** —— cmd 用 `&` / `&&` 串命令,这正是 POWERSHELL_DELETE
26
+ * 的分隔符类 `[;|\n]` 不能直接复用的原因。
27
+ * `rm` 一并收进来:Git 的 usr\bin 可能在 PATH 上(此时 rm.exe 可用),而 cmd 本身没有 rm。
28
+ */
29
+ const CMD_DELETE = [/(?:^|[;&|\n])\s*(?:del|erase|rd|rmdir|rm)\b/i];
6
30
  const GIT_CLEAN = /(?:^|[;&|\n])\s*git\b[^\n;&|]*\bclean\b([^\n;&|]*)/i;
7
31
  const POWERSHELL_DELETE = [
8
32
  /(?:^|[;|\n])\s*(?:Remove-Item|Clear-RecycleBin)\b/i,
9
33
  /(?:^|[;|\n])\s*(?:del|erase|rd|rmdir|rm|ri)\b/i,
10
34
  /\.Delete\s*\(/i,
11
35
  ];
36
+ const RULES = {
37
+ bash: BASH_DELETE,
38
+ cmd: CMD_DELETE,
39
+ powershell: POWERSHELL_DELETE,
40
+ };
12
41
  export function permanentDeleteReason(command, shell) {
13
- let hit = (shell === 'bash' ? BASH_DELETE : POWERSHELL_DELETE).some((pattern) => pattern.test(command));
14
- if (!hit && shell === 'bash') {
42
+ let hit = RULES[shell].some((pattern) => pattern.test(command));
43
+ // `git clean -f` 三种 shell 里都能跑、都永久删除,所以不按 shell 分支。
44
+ // `-n` / `--dry-run` 是只读预演,必须放行(否则模型连"先看看会删什么"都做不到)。
45
+ if (!hit) {
15
46
  const gitClean = GIT_CLEAN.exec(command);
16
47
  const options = gitClean?.[1] ?? '';
17
48
  const dryRun = /--dry-run\b/.test(options) ||
@@ -1,22 +1,59 @@
1
- /** Minimal recoverability boundary for model-issued shell commands. */
1
+ /**
2
+ * 永久删除守卫:把「绕过回收站的删除」从 shell 里挡回去,逼模型改用可恢复的 Trash。
3
+ *
4
+ * ⚠️ 可靠性边界(与 src/irreversible/rules.ts 同一标准,别高估它):
5
+ * 这是**防模型误删**的护栏,不是对抗性沙盒。判据是对命令文本做正则匹配,
6
+ * 因此下列写法一律漏过,且**有意不追**(越追越像军备竞赛,误伤率先爆):
7
+ * · 绝对路径调用 /bin/rm -rf x
8
+ * · 间接执行 xargs rm / find -exec rm / echo $(rm x)
9
+ * · 换语言 python -c "import shutil; shutil.rmtree('x')"
10
+ * · 跨 shell 转发 cmd 里 powershell -c "Remove-Item x"
11
+ * 真正兜底的是「模型没有恶意」+ Trash 提供了更顺手的替代路径,不是这张正则表。
12
+ *
13
+ * ⚠️ `shell` 必须是**实际执行命令的那个 shell**,不是调用它的工具名。
14
+ * Bash 工具在 win32 找不到 Git Bash 时会回落 cmd.exe(见 tools/bash/bash.ts 的
15
+ * WIN_BASH 探测,且它的 description 会主动告诉模型「这里是 cmd」),那时模型写的是
16
+ * cmd 语法(del / rd),若仍按 'bash' 匹配 —— 三层防护全部落空、文件直接永久丢失。
17
+ */
18
+
19
+ /** POSIX shell:删除动词在命令位置(串首或 ; & | 换行之后)。 */
2
20
  const BASH_DELETE = [
3
21
  /(?:^|[;&|\n])\s*(?:sudo\s+)?(?:rm|unlink|rmdir)\b/i,
4
22
  /(?:^|[;&|\n])\s*find\b[^\n;&|]*\s-delete\b/i,
5
23
  ];
24
+
25
+ /**
26
+ * cmd.exe:del / erase 删文件,rd / rmdir 删目录,一律不进回收站。
27
+ * 分隔符**必须含 `&`** —— cmd 用 `&` / `&&` 串命令,这正是 POWERSHELL_DELETE
28
+ * 的分隔符类 `[;|\n]` 不能直接复用的原因。
29
+ * `rm` 一并收进来:Git 的 usr\bin 可能在 PATH 上(此时 rm.exe 可用),而 cmd 本身没有 rm。
30
+ */
31
+ const CMD_DELETE = [/(?:^|[;&|\n])\s*(?:del|erase|rd|rmdir|rm)\b/i];
32
+
6
33
  const GIT_CLEAN = /(?:^|[;&|\n])\s*git\b[^\n;&|]*\bclean\b([^\n;&|]*)/i;
34
+
7
35
  const POWERSHELL_DELETE = [
8
36
  /(?:^|[;|\n])\s*(?:Remove-Item|Clear-RecycleBin)\b/i,
9
37
  /(?:^|[;|\n])\s*(?:del|erase|rd|rmdir|rm|ri)\b/i,
10
38
  /\.Delete\s*\(/i,
11
39
  ];
40
+
41
+ export type ShellKind = 'bash' | 'cmd' | 'powershell';
42
+
43
+ const RULES: Readonly<Record<ShellKind, RegExp[]>> = {
44
+ bash: BASH_DELETE,
45
+ cmd: CMD_DELETE,
46
+ powershell: POWERSHELL_DELETE,
47
+ };
48
+
12
49
  export function permanentDeleteReason(
13
50
  command: string,
14
- shell: 'bash' | 'powershell',
51
+ shell: ShellKind,
15
52
  ): string | undefined {
16
- let hit = (shell === 'bash' ? BASH_DELETE : POWERSHELL_DELETE).some(
17
- (pattern) => pattern.test(command),
18
- );
19
- if (!hit && shell === 'bash') {
53
+ let hit = RULES[shell].some((pattern) => pattern.test(command));
54
+ // `git clean -f` 三种 shell 里都能跑、都永久删除,所以不按 shell 分支。
55
+ // `-n` / `--dry-run` 是只读预演,必须放行(否则模型连"先看看会删什么"都做不到)。
56
+ if (!hit) {
20
57
  const gitClean = GIT_CLEAN.exec(command);
21
58
  const options = gitClean?.[1] ?? '';
22
59
  const dryRun =
@@ -15,6 +15,10 @@ export class ToolOutputStore {
15
15
  diskBytes = 0;
16
16
  bytes = 0;
17
17
  complete = true;
18
+ /** finish() 已调用:hash 已 digest,此后不能再 update(见 append 头注)。 */
19
+ sealed = false;
20
+ /** finish() 的结果缓存 —— digest 只能算一次,二次调用必须拿到同一份。 */
21
+ settled;
18
22
  path;
19
23
  constructor(identity) {
20
24
  const dir = join(sessionRuntimeDir(identity), 'outputs');
@@ -22,6 +26,18 @@ export class ToolOutputStore {
22
26
  this.path = join(dir, `procout_${randomUUID()}.log`);
23
27
  }
24
28
  append(source, value) {
29
+ // ⚠️ finish() 之后到达的迟到数据:**绝不能抛**。
30
+ //
31
+ // `bash.ts` 的 spawnRunner 按 `'exit'` 结算(不能等 `'close'`,否则被 daemon
32
+ // 继承的 pipe 会把它拖成无限挂),代价就是 exit 之后仍可能有 chunk 到达。
33
+ // 那时 hash 已经 digest 过,`hash.update()` 会抛 ERR_CRYPTO_HASH_FINALIZED,
34
+ // 而这个抛错发生在 stdout 的 'data' 回调里 —— 没有任何人接得住。
35
+ //
36
+ // 正确做法是如实记成不完整(数据确实没被算进已报出的结果),然后安静返回。
37
+ if (this.sealed) {
38
+ this.complete = false;
39
+ return true;
40
+ }
25
41
  const raw = Buffer.from(value);
26
42
  if (this.bytes >= MAX_BYTES) {
27
43
  this.complete = false;
@@ -70,6 +86,11 @@ export class ToolOutputStore {
70
86
  this.stream?.once('drain', callback);
71
87
  }
72
88
  async finish() {
89
+ // 幂等:digest 只能算一次。二次调用返回同一份快照,但 `complete` 取最新值 ——
90
+ // 如果之间来过迟到数据,调用方有权知道这份输出其实不完整了。
91
+ if (this.settled)
92
+ return { ...this.settled, complete: this.complete };
93
+ this.sealed = true;
73
94
  if (this.stream) {
74
95
  try {
75
96
  await new Promise((resolve, reject) => {
@@ -85,7 +106,7 @@ export class ToolOutputStore {
85
106
  const preview = (head, tail) => this.stream && tail && !head.endsWith(tail)
86
107
  ? `${head}\n... [middle stored on disk] ...\n${tail}`
87
108
  : head;
88
- return {
109
+ this.settled = {
89
110
  stdout: preview(this.stdout, this.stdoutTail),
90
111
  stderr: preview(this.stderr, this.stderrTail),
91
112
  ...(this.stream ? { path: this.path } : {}),
@@ -93,5 +114,6 @@ export class ToolOutputStore {
93
114
  sha256: this.stream ? this.diskHash.digest('hex') : this.hash.digest('hex'),
94
115
  complete: this.complete,
95
116
  };
117
+ return this.settled;
96
118
  }
97
119
  }
@@ -32,6 +32,10 @@ export class ToolOutputStore {
32
32
  private diskBytes = 0;
33
33
  private bytes = 0;
34
34
  private complete = true;
35
+ /** finish() 已调用:hash 已 digest,此后不能再 update(见 append 头注)。 */
36
+ private sealed = false;
37
+ /** finish() 的结果缓存 —— digest 只能算一次,二次调用必须拿到同一份。 */
38
+ private settled?: StoredOutput;
35
39
  readonly path: string;
36
40
  constructor(identity: RuntimeIdentity) {
37
41
  const dir = join(sessionRuntimeDir(identity), 'outputs');
@@ -39,6 +43,18 @@ export class ToolOutputStore {
39
43
  this.path = join(dir, `procout_${randomUUID()}.log`);
40
44
  }
41
45
  append(source: 'stdout' | 'stderr', value: Buffer | string): boolean {
46
+ // ⚠️ finish() 之后到达的迟到数据:**绝不能抛**。
47
+ //
48
+ // `bash.ts` 的 spawnRunner 按 `'exit'` 结算(不能等 `'close'`,否则被 daemon
49
+ // 继承的 pipe 会把它拖成无限挂),代价就是 exit 之后仍可能有 chunk 到达。
50
+ // 那时 hash 已经 digest 过,`hash.update()` 会抛 ERR_CRYPTO_HASH_FINALIZED,
51
+ // 而这个抛错发生在 stdout 的 'data' 回调里 —— 没有任何人接得住。
52
+ //
53
+ // 正确做法是如实记成不完整(数据确实没被算进已报出的结果),然后安静返回。
54
+ if (this.sealed) {
55
+ this.complete = false;
56
+ return true;
57
+ }
42
58
  const raw = Buffer.from(value);
43
59
  if (this.bytes >= MAX_BYTES) {
44
60
  this.complete = false;
@@ -83,6 +99,10 @@ export class ToolOutputStore {
83
99
  this.stream?.once('drain', callback);
84
100
  }
85
101
  async finish(): Promise<StoredOutput> {
102
+ // 幂等:digest 只能算一次。二次调用返回同一份快照,但 `complete` 取最新值 ——
103
+ // 如果之间来过迟到数据,调用方有权知道这份输出其实不完整了。
104
+ if (this.settled) return { ...this.settled, complete: this.complete };
105
+ this.sealed = true;
86
106
  if (this.stream) {
87
107
  try {
88
108
  await new Promise<void>((resolve, reject) => {
@@ -98,7 +118,7 @@ export class ToolOutputStore {
98
118
  this.stream && tail && !head.endsWith(tail)
99
119
  ? `${head}\n... [middle stored on disk] ...\n${tail}`
100
120
  : head;
101
- return {
121
+ this.settled = {
102
122
  stdout: preview(this.stdout, this.stdoutTail),
103
123
  stderr: preview(this.stderr, this.stderrTail),
104
124
  ...(this.stream ? { path: this.path } : {}),
@@ -106,5 +126,6 @@ export class ToolOutputStore {
106
126
  sha256: this.stream ? this.diskHash!.digest('hex') : this.hash.digest('hex'),
107
127
  complete: this.complete,
108
128
  };
129
+ return this.settled;
109
130
  }
110
131
  }