minimal-agent 0.10.0 → 0.12.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 (573) hide show
  1. package/README.md +11 -3
  2. package/package.json +52 -5
  3. package/plugins/computer_use/.claude-plugin/plugin.json +6 -0
  4. package/plugins/computer_use/README.md +94 -0
  5. package/plugins/computer_use/app/_template.md +142 -0
  6. package/plugins/computer_use/app/chrome.md +165 -0
  7. package/plugins/computer_use/app/finder.md +76 -0
  8. package/plugins/computer_use/app/macos-system-settings.md +81 -0
  9. package/plugins/computer_use/app/microsoft-excel.md +80 -0
  10. package/plugins/computer_use/app/microsoft-powerpoint.md +80 -0
  11. package/plugins/computer_use/app/microsoft-word.md +79 -0
  12. package/plugins/computer_use/app/wechat.md +413 -0
  13. package/plugins/computer_use/app/weread.md +319 -0
  14. package/plugins/computer_use/commands/computeruse.md +25 -0
  15. package/plugins/computer_use/commands/cu.md +9 -0
  16. package/plugins/computer_use/plugin.js +26 -0
  17. package/plugins/computer_use/plugin.ts +30 -0
  18. package/plugins/computer_use/src/capture.js +90 -0
  19. package/plugins/computer_use/src/capture.ts +104 -0
  20. package/plugins/computer_use/src/commands.js +91 -0
  21. package/plugins/computer_use/src/commands.ts +103 -0
  22. package/plugins/computer_use/src/daemon.js +134 -0
  23. package/plugins/computer_use/src/daemon.ts +161 -0
  24. package/plugins/computer_use/src/manuals.js +135 -0
  25. package/plugins/computer_use/src/manuals.ts +159 -0
  26. package/plugins/computer_use/src/verify.js +168 -0
  27. package/plugins/computer_use/src/verify.ts +219 -0
  28. package/plugins/computer_use/test/commands.test.ts +73 -0
  29. package/plugins/cron-runner/plugin.ts +38 -0
  30. package/plugins/cron-runner/src/commands.ts +373 -0
  31. package/plugins/cron-runner/src/cronTime.ts +155 -0
  32. package/plugins/cron-runner/src/lock.ts +79 -0
  33. package/plugins/cron-runner/src/runs.ts +99 -0
  34. package/plugins/cron-runner/src/scheduleEngine.ts +76 -0
  35. package/plugins/cron-runner/src/scheduler.ts +376 -0
  36. package/plugins/cron-runner/src/schema.ts +139 -0
  37. package/plugins/cron-runner/src/service.ts +193 -0
  38. package/plugins/cron-runner/src/store.ts +333 -0
  39. package/plugins/cron-runner/src/types.ts +141 -0
  40. package/plugins/cron-runner/test/cronTime.test.ts +41 -0
  41. package/plugins/cron-runner/test/plugin-integration.test.ts +47 -0
  42. package/plugins/cron-runner/test/scheduleEngine.test.ts +41 -0
  43. package/plugins/cron-runner/test/service.test.ts +76 -0
  44. package/plugins/cron-runner/test/store-and-scheduler.test.ts +501 -0
  45. package/plugins/workflow-runner/plugin.js +56 -2
  46. package/plugins/workflow-runner/plugin.ts +113 -0
  47. package/plugins/workflow-runner/src/agentCommon.js +43 -4
  48. package/plugins/workflow-runner/src/agentCommon.ts +435 -0
  49. package/plugins/workflow-runner/src/controlFlow/branch.js +40 -0
  50. package/plugins/workflow-runner/src/controlFlow/branch.ts +46 -0
  51. package/plugins/workflow-runner/src/controlFlow/loop.js +211 -0
  52. package/plugins/workflow-runner/src/controlFlow/loop.ts +248 -0
  53. package/plugins/workflow-runner/src/controlFlow/pause.js +129 -0
  54. package/plugins/workflow-runner/src/controlFlow/pause.ts +141 -0
  55. package/plugins/workflow-runner/src/controlFlow/signal.js +21 -0
  56. package/plugins/workflow-runner/src/controlFlow/signal.ts +29 -0
  57. package/plugins/workflow-runner/src/controlFlow/types.js +18 -0
  58. package/plugins/workflow-runner/src/controlFlow/types.ts +63 -0
  59. package/plugins/workflow-runner/src/dslManifest.ts +127 -0
  60. package/plugins/workflow-runner/src/expressions.ts +622 -0
  61. package/plugins/workflow-runner/src/index.js +13 -4
  62. package/plugins/workflow-runner/src/index.ts +338 -0
  63. package/plugins/workflow-runner/src/jsonPayload.ts +84 -0
  64. package/plugins/workflow-runner/src/loader.js +23 -0
  65. package/plugins/workflow-runner/src/loader.ts +721 -0
  66. package/plugins/workflow-runner/src/multimodal.js +2 -1
  67. package/plugins/workflow-runner/src/multimodal.ts +64 -0
  68. package/plugins/workflow-runner/src/recovery.ts +149 -0
  69. package/plugins/workflow-runner/src/runner.js +75 -659
  70. package/plugins/workflow-runner/src/runner.ts +604 -0
  71. package/plugins/workflow-runner/src/stepExecutors/agent.ts +234 -0
  72. package/plugins/workflow-runner/src/stepExecutors/assert.ts +50 -0
  73. package/plugins/workflow-runner/src/stepExecutors/echo.js +31 -0
  74. package/plugins/workflow-runner/src/stepExecutors/echo.ts +38 -0
  75. package/plugins/workflow-runner/src/stepExecutors/llm.ts +133 -0
  76. package/plugins/workflow-runner/src/stepExecutors/set.js +49 -0
  77. package/plugins/workflow-runner/src/stepExecutors/set.ts +56 -0
  78. package/plugins/workflow-runner/src/stepExecutors/skill.js +5 -6
  79. package/plugins/workflow-runner/src/stepExecutors/skill.ts +140 -0
  80. package/plugins/workflow-runner/src/stepExecutors/tool.js +80 -8
  81. package/plugins/workflow-runner/src/stepExecutors/tool.ts +256 -0
  82. package/plugins/workflow-runner/src/stepExecutors/verify.ts +68 -0
  83. package/plugins/workflow-runner/src/stepPipeline.js +277 -0
  84. package/plugins/workflow-runner/src/stepPipeline.ts +366 -0
  85. package/plugins/workflow-runner/src/taskIdentity.ts +405 -0
  86. package/plugins/workflow-runner/src/toolRuntime.js +48 -0
  87. package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
  88. package/plugins/workflow-runner/src/types.js +37 -1
  89. package/plugins/workflow-runner/src/types.ts +602 -0
  90. package/plugins/workflow-runner/src/workflowState.ts +733 -0
  91. package/plugins/workflow-runner/test/agent.test.ts +587 -0
  92. package/plugins/workflow-runner/test/agentLlmError.test.ts +373 -0
  93. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +65 -0
  94. package/plugins/workflow-runner/test/captureMisbind.test.ts +99 -0
  95. package/plugins/workflow-runner/test/cli.e2e.test.ts +147 -0
  96. package/plugins/workflow-runner/test/context-files.test.ts +320 -0
  97. package/plugins/workflow-runner/test/e2e.test.ts +370 -0
  98. package/plugins/workflow-runner/test/engineRobustness.test.ts +226 -0
  99. package/plugins/workflow-runner/test/expressions.test.ts +397 -0
  100. package/plugins/workflow-runner/test/foundation.test.ts +126 -0
  101. package/plugins/workflow-runner/test/graceful.test.ts +139 -0
  102. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +47 -0
  103. package/plugins/workflow-runner/test/hostCapability.test.ts +108 -0
  104. package/plugins/workflow-runner/test/illustrate.load.test.ts +183 -0
  105. package/plugins/workflow-runner/test/jsonPayload.test.ts +83 -0
  106. package/plugins/workflow-runner/test/loader.test.ts +617 -0
  107. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +33 -0
  108. package/plugins/workflow-runner/test/multimodal.test.ts +342 -0
  109. package/plugins/workflow-runner/test/output-schema.test.ts +213 -0
  110. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +87 -0
  111. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +269 -0
  112. package/plugins/workflow-runner/test/ppt.load.test.ts +1220 -0
  113. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +220 -0
  114. package/plugins/workflow-runner/test/recovery.test.ts +265 -0
  115. package/plugins/workflow-runner/test/runner.test.ts +911 -0
  116. package/plugins/workflow-runner/test/runnerPreflight.test.ts +379 -0
  117. package/plugins/workflow-runner/test/skillTurnDone.test.ts +71 -0
  118. package/plugins/workflow-runner/test/stateLifecycle.test.ts +150 -0
  119. package/plugins/workflow-runner/test/stepPipeline.test.ts +114 -0
  120. package/plugins/workflow-runner/test/taskArbitration.test.ts +338 -0
  121. package/plugins/workflow-runner/test/taskIdentity.test.ts +237 -0
  122. package/plugins/workflow-runner/test/taskLifecycle.test.ts +270 -0
  123. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +221 -0
  124. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +29 -0
  125. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
  126. package/plugins/workflow-runner/test/webppt.load.test.ts +250 -0
  127. package/plugins/workflow-runner/test/word.load.test.ts +208 -0
  128. package/plugins/workflow-runner/test/workflows.smoke.test.ts +76 -0
  129. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +207 -0
  130. package/skills/browser-automation/SKILL.md +9 -19
  131. package/skills/web-crawler/SKILL.md +9 -11
  132. package/src/askBridge.ts +249 -0
  133. package/src/bootstrap/cwdArg.js +10 -3
  134. package/src/bootstrap/cwdArg.ts +31 -0
  135. package/src/bootstrap/workingDir.ts +48 -0
  136. package/src/cli/args.js +70 -7
  137. package/src/cli/args.ts +146 -0
  138. package/src/cli/computerUse.js +185 -0
  139. package/src/cli/computerUse.ts +221 -0
  140. package/src/cli/configMenu.js +274 -202
  141. package/src/cli/configMenu.tsx +521 -0
  142. package/src/cli/memoryAccess.js +45 -0
  143. package/src/cli/memoryAccess.ts +53 -0
  144. package/src/cli/print.js +119 -5
  145. package/src/cli/print.ts +597 -0
  146. package/src/cli/quickModelSetup.js +60 -0
  147. package/src/cli/quickModelSetup.tsx +96 -0
  148. package/src/cli/streamJson.js +80 -4
  149. package/src/cli/streamJson.ts +433 -0
  150. package/src/cli/wizards/advisorWizard.js +1 -0
  151. package/src/cli/wizards/advisorWizard.tsx +195 -0
  152. package/src/cli/wizards/agentRuntimeWizard.js +84 -0
  153. package/src/cli/wizards/agentRuntimeWizard.tsx +134 -0
  154. package/src/cli/wizards/browserWizard.js +55 -0
  155. package/src/cli/wizards/browserWizard.tsx +77 -0
  156. package/src/cli/wizards/contextAdvisorWizard.js +84 -0
  157. package/src/cli/wizards/contextAdvisorWizard.tsx +124 -0
  158. package/src/cli/wizards/fieldForm.js +7 -4
  159. package/src/cli/wizards/fieldForm.tsx +250 -0
  160. package/src/cli/wizards/imageWizard.js +4 -5
  161. package/src/cli/wizards/imageWizard.tsx +96 -0
  162. package/src/cli/wizards/openRouterWizard.tsx +48 -0
  163. package/src/cli/wizards/providerPresets.js +1 -8
  164. package/src/cli/wizards/providerPresets.ts +106 -0
  165. package/src/cli/wizards/providerWizard.js +152 -205
  166. package/src/cli/wizards/providerWizard.tsx +266 -0
  167. package/src/cli/wizards/searchWizard.tsx +47 -0
  168. package/src/cli/wizards/ttsWizard.js +4 -3
  169. package/src/cli/wizards/ttsWizard.tsx +61 -0
  170. package/src/config/configDefaults.js +25 -0
  171. package/src/config/configDefaults.ts +33 -0
  172. package/src/config/configDiagnostics.js +117 -0
  173. package/src/config/configDiagnostics.ts +166 -0
  174. package/src/config/configFile.js +52 -32
  175. package/src/config/configFile.ts +633 -0
  176. package/src/config/configManagement.js +126 -0
  177. package/src/config/configManagement.ts +159 -0
  178. package/src/config/configService.js +66 -11
  179. package/src/config/configService.ts +554 -0
  180. package/src/config/numericLimits.js +61 -0
  181. package/src/config/numericLimits.ts +80 -0
  182. package/src/config.ts +109 -0
  183. package/src/context/agentHome.ts +11 -0
  184. package/src/context/archive.ts +144 -0
  185. package/src/context/budgetPlan.js +54 -0
  186. package/src/context/budgetPlan.ts +88 -0
  187. package/src/context/compact.js +179 -106
  188. package/src/context/compact.ts +584 -0
  189. package/src/context/contextBundle.js +119 -0
  190. package/src/context/contextBundle.ts +176 -0
  191. package/src/context/contextLock.ts +84 -0
  192. package/src/context/microCompactLite.ts +218 -0
  193. package/src/context/persistContext.ts +177 -0
  194. package/src/context/reactiveCompact.js +19 -4
  195. package/src/context/reactiveCompact.ts +240 -0
  196. package/src/context/recentDirs.ts +42 -0
  197. package/src/context/sessionPath.ts +206 -0
  198. package/src/context/sessionRegistry.js +8 -2
  199. package/src/context/sessionRegistry.ts +414 -0
  200. package/src/context/snipCompact.ts +163 -0
  201. package/src/context/tokenCounter.ts +106 -0
  202. package/src/context/workspaceIndex.js +2 -2
  203. package/src/context/workspaceIndex.ts +751 -0
  204. package/src/entry.compiled.tsx +20 -0
  205. package/src/harness/claimValidation.js +9 -0
  206. package/src/harness/claimValidation.ts +22 -0
  207. package/src/harness/evidenceLedger.js +92 -0
  208. package/src/harness/evidenceLedger.ts +96 -0
  209. package/src/harness/stallGuard.js +60 -0
  210. package/src/harness/stallGuard.ts +81 -0
  211. package/src/humanIntervention.js +129 -0
  212. package/src/humanIntervention.ts +216 -0
  213. package/src/infra/verification.ts +295 -0
  214. package/src/irreversible/rules.js +216 -0
  215. package/src/irreversible/rules.ts +261 -0
  216. package/src/llm/client.js +40 -3
  217. package/src/llm/client.ts +621 -0
  218. package/src/llm/promptCache.js +27 -0
  219. package/src/llm/promptCache.ts +41 -0
  220. package/src/llm/providers/anthropicFormat.js +41 -5
  221. package/src/llm/providers/anthropicFormat.ts +290 -0
  222. package/src/llm/providers/openaiFormat.js +186 -8
  223. package/src/llm/providers/openaiFormat.ts +428 -0
  224. package/src/loop.js +614 -63
  225. package/src/loop.ts +1454 -0
  226. package/src/main.js +67 -10
  227. package/src/main.tsx +344 -0
  228. package/src/memory/codec.js +204 -0
  229. package/src/memory/codec.ts +249 -0
  230. package/src/memory/constants.js +34 -0
  231. package/src/memory/constants.ts +50 -0
  232. package/src/memory/episodicMemory.js +684 -0
  233. package/src/memory/episodicMemory.ts +815 -0
  234. package/src/memory/fileStore.js +148 -0
  235. package/src/memory/fileStore.ts +184 -0
  236. package/src/memory/memoryService.js +570 -0
  237. package/src/memory/memoryService.ts +859 -0
  238. package/src/memory/migration.js +128 -0
  239. package/src/memory/migration.ts +160 -0
  240. package/src/memory/repository.js +789 -0
  241. package/src/memory/repository.ts +893 -0
  242. package/src/memory/retrieval.js +108 -0
  243. package/src/memory/retrieval.ts +145 -0
  244. package/src/memory/searchIndex.js +107 -0
  245. package/src/memory/searchIndex.ts +153 -0
  246. package/src/memory/topicView.js +82 -0
  247. package/src/memory/topicView.ts +95 -0
  248. package/src/memory/turnJournal.js +95 -0
  249. package/src/memory/turnJournal.ts +120 -0
  250. package/src/memory/types.js +2 -0
  251. package/src/memory/types.ts +186 -0
  252. package/src/messageQueue.ts +123 -0
  253. package/src/multimodal.js +128 -46
  254. package/src/multimodal.ts +349 -0
  255. package/src/plugin-sdk.js +21 -1
  256. package/src/plugin-sdk.ts +134 -0
  257. package/src/plugins/builtinPluginIds.js +47 -0
  258. package/src/plugins/builtinPluginIds.ts +51 -0
  259. package/src/plugins/builtinRegistry.ts +43 -0
  260. package/src/plugins/commandRouter.js +40 -26
  261. package/src/plugins/commandRouter.ts +261 -0
  262. package/src/plugins/hookEngine.ts +341 -0
  263. package/src/plugins/pluginApi.ts +133 -0
  264. package/src/plugins/pluginLifecycle.ts +34 -0
  265. package/src/plugins/pluginLoader.ts +147 -0
  266. package/src/plugins/pluginManifest.js +127 -0
  267. package/src/plugins/pluginManifest.ts +152 -0
  268. package/src/plugins/pluginRunner.js +47 -2
  269. package/src/plugins/pluginRunner.ts +207 -0
  270. package/src/plugins/transcript.ts +208 -0
  271. package/src/prompts/lint.js +169 -0
  272. package/src/prompts/lint.ts +220 -0
  273. package/src/prompts/pluginList.js +32 -16
  274. package/src/prompts/pluginList.ts +86 -0
  275. package/src/prompts/policyRegistry.js +318 -0
  276. package/src/prompts/policyRegistry.ts +358 -0
  277. package/src/prompts/projectInstructions.js +24 -0
  278. package/src/prompts/projectInstructions.ts +81 -0
  279. package/src/prompts/promptAssembly.js +311 -202
  280. package/src/prompts/promptAssembly.ts +510 -0
  281. package/src/prompts/skillList.js +106 -45
  282. package/src/prompts/skillList.ts +198 -0
  283. package/src/prompts/system.js +31 -6
  284. package/src/prompts/system.ts +81 -0
  285. package/src/runtime/agentRuntimeContext.js +53 -0
  286. package/src/runtime/agentRuntimeContext.ts +112 -0
  287. package/src/session/runAgentTurn.js +71 -12
  288. package/src/session/runAgentTurn.ts +181 -0
  289. package/src/session/runTurn.js +29 -0
  290. package/src/session/runTurn.ts +160 -0
  291. package/src/session/runtimeStore.js +149 -0
  292. package/src/session/runtimeStore.ts +197 -0
  293. package/src/session/sessionState.js +79 -3
  294. package/src/session/sessionState.ts +134 -0
  295. package/src/toolGate.ts +50 -0
  296. package/src/tools/advisor/advisor.js +76 -15
  297. package/src/tools/advisor/advisor.ts +512 -0
  298. package/src/tools/agent/agent.js +245 -29
  299. package/src/tools/agent/agent.ts +837 -0
  300. package/src/tools/agent/agentTaskService.js +60 -0
  301. package/src/tools/agent/agentTaskService.ts +113 -0
  302. package/src/tools/agent/agentTypes.js +20 -8
  303. package/src/tools/agent/agentTypes.ts +94 -0
  304. package/src/tools/agent/coordinator.ts +111 -0
  305. package/src/tools/agent/worktree.ts +296 -0
  306. package/src/tools/ask/ask.js +17 -0
  307. package/src/tools/ask/ask.ts +122 -0
  308. package/src/tools/bash/attachImage.js +223 -0
  309. package/src/tools/bash/attachImage.ts +281 -0
  310. package/src/tools/bash/bash.js +132 -14
  311. package/src/tools/bash/bash.ts +642 -0
  312. package/src/tools/bash/semantics.ts +103 -0
  313. package/src/tools/bash/warnings.js +17 -11
  314. package/src/tools/bash/warnings.ts +117 -0
  315. package/src/tools/capabilities.js +42 -0
  316. package/src/tools/capabilities.ts +69 -0
  317. package/src/tools/computeruse/accessibilityPermission.js +261 -0
  318. package/src/tools/computeruse/accessibilityPermission.ts +294 -0
  319. package/src/tools/computeruse/axSearch.js +96 -0
  320. package/src/tools/computeruse/axSearch.ts +120 -0
  321. package/src/tools/computeruse/axSnap.js +272 -0
  322. package/src/tools/computeruse/axSnap.ts +349 -0
  323. package/src/tools/computeruse/capabilityLabels.js +95 -0
  324. package/src/tools/computeruse/capabilityLabels.ts +115 -0
  325. package/src/tools/computeruse/computerUseService.js +2235 -0
  326. package/src/tools/computeruse/computerUseService.ts +2686 -0
  327. package/src/tools/computeruse/computeruse.js +2742 -0
  328. package/src/tools/computeruse/computeruse.ts +3265 -0
  329. package/src/tools/computeruse/diagnosticTrace.js +154 -0
  330. package/src/tools/computeruse/diagnosticTrace.ts +195 -0
  331. package/src/tools/computeruse/diagnostics.js +369 -0
  332. package/src/tools/computeruse/diagnostics.ts +453 -0
  333. package/src/tools/computeruse/grants.js +53 -0
  334. package/src/tools/computeruse/grants.ts +70 -0
  335. package/src/tools/computeruse/humanBoundary.js +159 -0
  336. package/src/tools/computeruse/humanBoundary.ts +199 -0
  337. package/src/tools/computeruse/intervention.js +77 -0
  338. package/src/tools/computeruse/intervention.ts +110 -0
  339. package/src/tools/computeruse/manualSchema.js +709 -0
  340. package/src/tools/computeruse/manualSchema.ts +916 -0
  341. package/src/tools/computeruse/observationImages.js +705 -0
  342. package/src/tools/computeruse/observationImages.ts +889 -0
  343. package/src/tools/computeruse/protocol.js +60 -0
  344. package/src/tools/computeruse/protocol.ts +515 -0
  345. package/src/tools/computeruse/telemetryLog.js +57 -0
  346. package/src/tools/computeruse/telemetryLog.ts +67 -0
  347. package/src/tools/computeruse/vision/appGuide.js +325 -0
  348. package/src/tools/computeruse/vision/appGuide.ts +482 -0
  349. package/src/tools/computeruse/vision/focus.js +291 -0
  350. package/src/tools/computeruse/vision/focus.ts +382 -0
  351. package/src/tools/computeruse/vision/frames.js +248 -0
  352. package/src/tools/computeruse/vision/frames.ts +329 -0
  353. package/src/tools/computeruse/vision/geometry.js +131 -0
  354. package/src/tools/computeruse/vision/geometry.ts +163 -0
  355. package/src/tools/computeruse/vision/imaging.js +201 -0
  356. package/src/tools/computeruse/vision/imaging.ts +281 -0
  357. package/src/tools/computeruse/vision/png.js +274 -0
  358. package/src/tools/computeruse/vision/png.ts +317 -0
  359. package/src/tools/computeruse/vision/raster.js +320 -0
  360. package/src/tools/computeruse/vision/raster.ts +408 -0
  361. package/src/tools/computeruse/vision/settle.js +131 -0
  362. package/src/tools/computeruse/vision/settle.ts +220 -0
  363. package/src/tools/computeruse/vision/telemetry.js +196 -0
  364. package/src/tools/computeruse/vision/telemetry.ts +241 -0
  365. package/src/tools/computeruse/visionOcr.js +115 -0
  366. package/src/tools/computeruse/visionOcr.ts +128 -0
  367. package/src/tools/computeruse/waitFor.js +237 -0
  368. package/src/tools/computeruse/waitFor.ts +291 -0
  369. package/src/tools/download/download.js +10 -6
  370. package/src/tools/download/download.ts +187 -0
  371. package/src/tools/edit/edit.js +30 -18
  372. package/src/tools/edit/edit.ts +349 -0
  373. package/src/tools/edit/multi-edit.js +25 -14
  374. package/src/tools/edit/multi-edit.ts +223 -0
  375. package/src/tools/effectiveToolSet.js +73 -0
  376. package/src/tools/effectiveToolSet.ts +91 -0
  377. package/src/tools/genimage/genimage.ts +257 -0
  378. package/src/tools/genspeech/genspeech.ts +83 -0
  379. package/src/tools/glob/glob.js +176 -29
  380. package/src/tools/glob/glob.ts +283 -0
  381. package/src/tools/grep/grep.js +61 -19
  382. package/src/tools/grep/grep.ts +239 -0
  383. package/src/tools/grep/rgPath.ts +206 -0
  384. package/src/tools/index.js +99 -5
  385. package/src/tools/index.ts +293 -0
  386. package/src/tools/lsp/config.js +113 -0
  387. package/src/tools/lsp/config.ts +113 -0
  388. package/src/tools/lsp/lsp.js +256 -0
  389. package/src/tools/lsp/lsp.ts +274 -0
  390. package/src/tools/lsp/service.js +303 -0
  391. package/src/tools/lsp/service.ts +296 -0
  392. package/src/tools/memory/memory.js +520 -0
  393. package/src/tools/memory/memory.ts +625 -0
  394. package/src/tools/memory/policy.js +53 -0
  395. package/src/tools/memory/policy.ts +72 -0
  396. package/src/tools/notebook/notebookEdit.js +114 -0
  397. package/src/tools/notebook/notebookEdit.ts +126 -0
  398. package/src/tools/powershell/powershell.js +210 -0
  399. package/src/tools/powershell/powershell.ts +239 -0
  400. package/src/tools/presentresult/presentresult.js +145 -0
  401. package/src/tools/presentresult/presentresult.ts +156 -0
  402. package/src/tools/read/read.js +371 -163
  403. package/src/tools/read/read.ts +558 -0
  404. package/src/tools/shared/diffSummary.js +44 -0
  405. package/src/tools/shared/diffSummary.ts +63 -0
  406. package/src/tools/shared/fileActivity.js +51 -0
  407. package/src/tools/shared/fileActivity.ts +66 -0
  408. package/src/tools/shared/fileMutationBus.js +13 -0
  409. package/src/tools/shared/fileMutationBus.ts +14 -0
  410. package/src/tools/shared/fileState.js +73 -65
  411. package/src/tools/shared/fileState.ts +145 -0
  412. package/src/tools/shared/fileUtils.js +81 -4
  413. package/src/tools/shared/fileUtils.ts +504 -0
  414. package/src/tools/shared/killTree.ts +50 -0
  415. package/src/tools/shared/permanentDeleteGuard.js +26 -0
  416. package/src/tools/shared/permanentDeleteGuard.ts +34 -0
  417. package/src/tools/shared/schemas.ts +20 -0
  418. package/src/tools/shared/toolOutputStore.js +97 -0
  419. package/src/tools/shared/toolOutputStore.ts +110 -0
  420. package/src/tools/skill/skill.js +86 -0
  421. package/src/tools/skill/skill.ts +94 -0
  422. package/src/tools/snip/snip.js +67 -0
  423. package/src/tools/snip/snip.ts +80 -0
  424. package/src/tools/tasks/backgroundTaskService.js +275 -0
  425. package/src/tools/tasks/backgroundTaskService.ts +369 -0
  426. package/src/tools/tasks/processEvidence.js +24 -0
  427. package/src/tools/tasks/processEvidence.ts +34 -0
  428. package/src/tools/tasks/taskManagement.js +163 -0
  429. package/src/tools/tasks/taskManagement.ts +180 -0
  430. package/src/tools/tasks/taskOutput.js +113 -0
  431. package/src/tools/tasks/taskOutput.ts +145 -0
  432. package/src/tools/tasks/taskStop.js +74 -0
  433. package/src/tools/tasks/taskStop.ts +99 -0
  434. package/src/tools/tasks/taskStore.js +46 -0
  435. package/src/tools/tasks/taskStore.ts +77 -0
  436. package/src/tools/terminal/terminalCapture.js +167 -0
  437. package/src/tools/terminal/terminalCapture.ts +179 -0
  438. package/src/tools/trash/trash.js +233 -0
  439. package/src/tools/trash/trash.ts +283 -0
  440. package/src/tools/types.js +1 -1
  441. package/src/tools/types.ts +24 -0
  442. package/src/tools/webbrowser/agentBrowserPath.ts +286 -0
  443. package/src/tools/webbrowser/browser.js +214 -11
  444. package/src/tools/webbrowser/browser.ts +293 -0
  445. package/src/tools/webbrowser/browserControl.js +153 -48
  446. package/src/tools/webbrowser/browserControl.ts +262 -0
  447. package/src/tools/webbrowser/browserDaemon.js +147 -39
  448. package/src/tools/webbrowser/browserDaemon.ts +270 -0
  449. package/src/tools/webbrowser/browserOutcome.js +273 -0
  450. package/src/tools/webbrowser/browserOutcome.ts +306 -0
  451. package/src/tools/webbrowser/browserRunRegistry.js +150 -0
  452. package/src/tools/webbrowser/browserRunRegistry.ts +179 -0
  453. package/src/tools/webbrowser/browserService.js +20 -6
  454. package/src/tools/webbrowser/browserService.ts +56 -0
  455. package/src/tools/webbrowser/browserStream.js +30 -1
  456. package/src/tools/webbrowser/browserStream.ts +216 -0
  457. package/src/tools/webbrowser/browserTabs.js +143 -0
  458. package/src/tools/webbrowser/browserTabs.ts +210 -0
  459. package/src/tools/webbrowser/webbrowser.js +831 -150
  460. package/src/tools/webbrowser/webbrowser.ts +1537 -0
  461. package/src/tools/webfetch/preapproved.ts +274 -0
  462. package/src/tools/webfetch/webfetch.js +24 -23
  463. package/src/tools/webfetch/webfetch.ts +383 -0
  464. package/src/tools/websearch/websearch.js +12 -11
  465. package/src/tools/websearch/websearch.ts +292 -0
  466. package/src/tools/worktree/sessionWorktree.js +128 -0
  467. package/src/tools/worktree/sessionWorktree.ts +158 -0
  468. package/src/tools/write/write.js +27 -44
  469. package/src/tools/write/write.ts +136 -0
  470. package/src/types.ts +960 -0
  471. package/src/ui/App.js +216 -12
  472. package/src/ui/App.tsx +879 -0
  473. package/src/ui/AskQuestion.tsx +300 -0
  474. package/src/ui/InputBox.js +77 -1
  475. package/src/ui/InputBox.tsx +567 -0
  476. package/src/ui/LiveArea.tsx +344 -0
  477. package/src/ui/MessageList.js +27 -6
  478. package/src/ui/MessageList.tsx +559 -0
  479. package/src/ui/ModelPicker.tsx +107 -0
  480. package/src/ui/QueuedMessagesBanner.tsx +51 -0
  481. package/src/ui/Root.js +6 -8
  482. package/src/ui/Root.tsx +100 -0
  483. package/src/ui/SessionPicker.tsx +169 -0
  484. package/src/ui/StatusLine.js +22 -1
  485. package/src/ui/StatusLine.tsx +145 -0
  486. package/src/ui/WorkspacePicker.tsx +84 -0
  487. package/src/ui/advisorCommand.js +54 -0
  488. package/src/ui/advisorCommand.ts +63 -0
  489. package/src/ui/computerCommand.js +19 -0
  490. package/src/ui/computerCommand.ts +35 -0
  491. package/src/ui/figures.ts +45 -0
  492. package/src/ui/hooks/askSlot.ts +112 -0
  493. package/src/ui/hooks/useChat.js +271 -7
  494. package/src/ui/hooks/useChat.ts +1578 -0
  495. package/src/ui/hooks/useInputHistory.ts +235 -0
  496. package/src/ui/hooks/usePasteHandler.ts +220 -0
  497. package/src/ui/hooks/useTerminalSize.ts +41 -0
  498. package/src/ui/hooks/useTextBuffer.ts +108 -0
  499. package/src/ui/hooks/useTokenUsage.ts +44 -0
  500. package/src/ui/liveViewport.ts +189 -0
  501. package/src/ui/markdown.ts +230 -0
  502. package/src/ui/textBuffer.ts +239 -0
  503. package/src/ui/theme.ts +35 -0
  504. package/src/utils/clipboardImage.js +358 -0
  505. package/src/utils/clipboardImage.ts +420 -0
  506. package/src/utils/debugLog.ts +50 -0
  507. package/src/utils/greenRoot.ts +35 -0
  508. package/src/utils/packageRoot.ts +40 -0
  509. package/src/utils/pathContains.js +28 -0
  510. package/src/utils/pathContains.ts +31 -0
  511. package/src/utils/resourcePaths.ts +71 -0
  512. package/src/utils/stripThink.ts +49 -0
  513. package/src/utils/zodToJson.ts +73 -0
  514. package/src/web/browserBridge.js +365 -66
  515. package/src/web/browserBridge.ts +672 -0
  516. package/src/web/protocol.ts +1804 -0
  517. package/vendor/computer-use/arm64-darwin/cu-helperd +0 -0
  518. package/workflows/ppt.yaml +13 -2
  519. package/workflows/wechat-reading-shots.yaml +315 -0
  520. package/workflows/word.yaml +11 -1
  521. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  522. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  523. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  524. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  525. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  526. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  527. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  528. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  529. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  530. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  531. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  532. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  533. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  534. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  535. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  536. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  537. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  538. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  539. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  540. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  541. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  542. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  543. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  544. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  545. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  546. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  547. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  548. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  549. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  550. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  551. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  552. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  553. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  554. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  555. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  556. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  557. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  558. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  559. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  560. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  561. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  562. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  563. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  564. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  565. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  566. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  567. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  568. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  569. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  570. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  571. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  572. /package/skills/ai-image/scripts/{generate_test.mjs → generate.selftest.mjs} +0 -0
  573. /package/skills/ai-image/scripts/{generate_from_plan_test.mjs → generate_from_plan.selftest.mjs} +0 -0
@@ -1,30 +1,20 @@
1
- /**
2
- * ============================================================
3
- * src/tools/read.ts —— Read 工具(读文件)
4
- * ------------------------------------------------------------
5
- * 对应 kakadeai 主仓库的 FileReadTool。
6
- * prompt 文本和 Zod schema 与 kakadeai 一致,让"模型可见接口"完全对齐;
7
- * call() 实现用最简单的 fs/promises,剥离了 UI/权限/PDF/Notebook/图片支持。
8
- *
9
- * 输入:
10
- * file_path 必填,绝对路径(建议)或相对项目根
11
- * offset 可选,1-indexed 起始行号
12
- * limit 可选,最大读多少行(默认 2000)
13
- *
14
- * 输出:
15
- * 带行号前缀的文本(格式:"${line}\t${content}"),与 Read 工具的 prompt 约定一致
16
- * ============================================================
17
- */
18
1
  import { createReadStream } from 'node:fs';
19
- import { readFile, stat } from 'node:fs/promises';
2
+ import { open, readFile, stat, writeFile } from 'node:fs/promises';
3
+ import { extname } from 'node:path';
20
4
  import { createInterface } from 'node:readline';
5
+ import { execFile } from 'node:child_process';
6
+ import { mkdir } from 'node:fs/promises';
7
+ import { join } from 'node:path';
8
+ import { promisify } from 'node:util';
9
+ import { randomUUID } from 'node:crypto';
21
10
  import { z } from 'zod';
22
- import { DEFAULT_MAX_RESULT_SIZE_CHARS, MAX_FILE_SIZE_BYTES, MAX_LINES_TO_READ } from '../types.js';
23
11
  import { toToolParameters } from '../../utils/zodToJson.js';
24
- import { hasBinaryExtension, isBlockedDevicePath, resolveToolPath } from '../shared/fileUtils.js';
12
+ import { DEFAULT_MAX_RESULT_SIZE_CHARS, MAX_FILE_SIZE_BYTES, MAX_LINES_TO_READ, } from '../types.js';
13
+ import { fileActivityMetadata } from '../shared/fileActivity.js';
14
+ import { hashFileBytes, recordRead, } from '../shared/fileState.js';
15
+ import { hasBinaryExtension, isBlockedDevicePath, readTextFile, resolveToolPath, } from '../shared/fileUtils.js';
25
16
  import { filePathField } from '../shared/schemas.js';
26
- import { recordRead } from '../shared/fileState.js';
27
- // ---------------- 1. Zod 输入 schema ----------------
17
+ import { pruneRuntimeOutputs, sessionRuntimeDir, } from '../../session/runtimeStore.js';
28
18
  const inputSchema = z.object({
29
19
  file_path: filePathField('读取'),
30
20
  offset: z
@@ -32,186 +22,404 @@ const inputSchema = z.object({
32
22
  .int()
33
23
  .positive()
34
24
  .optional()
35
- .describe('起始行号(1-indexed);不填则从文件开头读'),
25
+ .describe('文本文件为 1-indexed 起始行;Jupyter Notebook 为 1-indexed 起始 cell'),
36
26
  limit: z
37
27
  .number()
38
28
  .int()
39
29
  .positive()
40
30
  .optional()
41
- .describe(`最多读多少行;不填则用默认值 ${MAX_LINES_TO_READ}`),
31
+ .describe(`最多读取的文本行或 Notebook cells;默认 ${MAX_LINES_TO_READ}`),
32
+ pages: z
33
+ .string()
34
+ .regex(/^\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*$/)
35
+ .optional()
36
+ .describe('PDF 页码,例如 "1-5,8";每次最多 20 页'),
37
+ pdf_mode: z
38
+ .enum(['auto', 'text', 'visual', 'both'])
39
+ .default('auto')
40
+ .optional(),
42
41
  });
43
- // ---------------- 2. JSON Schema(给 LLM 看) ----------------
44
- //
45
- // 与上面的 Zod schema 保持一致;改动时两边都要改。
46
- // ---------------- 2. JSON Schema(由 Zod 自动派生) ----------------
47
- const parameters = toToolParameters(inputSchema);
48
- // ---------------- 3. Description(给 LLM 看的工具说明) ----------------
49
- //
50
- // 这段文本直接搬自 kakadeai/tools/FileReadTool/prompt.ts 的 DESCRIPTION,
51
- // 只是去掉了和 PDF/Notebook/图片相关的能力描述(最小版不支持)。
52
- const description = `Reads a file from the local filesystem. You can access any file directly by using this tool.
53
- Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.
42
+ const IMAGE_EXTENSIONS = new Set([
43
+ '.png',
44
+ '.jpg',
45
+ '.jpeg',
46
+ '.gif',
47
+ '.webp',
48
+ '.bmp',
49
+ ]);
50
+ const MAX_IMAGE_BYTES = 25 * 1024 * 1024;
51
+ const MAX_NOTEBOOK_BYTES = 32 * 1024 * 1024;
52
+ const SMALL_TEXT_BYTES = 16 * 1024 * 1024;
53
+ const description = `Reads local text files, images, and Jupyter notebooks.
54
54
 
55
55
  Usage:
56
- - The file_path parameter is preferably an absolute path. Relative paths are accepted and will be resolved against the agent's working directory (locked at startup via \`-d <dir>\` or process cwd).
57
- - By default, it reads up to ${MAX_LINES_TO_READ} lines starting from the beginning of the file
58
- - You can optionally specify a line offset and limit (especially handy for long files)
59
- - Results are returned using cat -n format, with line numbers starting at 1
60
- - This tool can only read text files, not directories. To read a directory, use the Glob tool.
61
- - If you read a file that exists but has empty contents you will receive a warning in place of file contents.
62
- - This tool cannot read binary files. Files whose extensions are on the binary blocklist (images: .png/.jpg/.gif/.webp/..., documents: .pdf/.docx/.xlsx/..., executables: .exe/.dll/.so/..., archives: .zip/.tar/.gz/..., and others) will be rejected with a clear error. If the file is actually text despite the extension (e.g., a misnamed log), rename it or use Bash \`cat\` to read it directly — do not retry Read with the same path.`;
63
- // ---------------- 4. call() —— 真正干活 ----------------
64
- const STREAM_THRESHOLD = 1024 * 1024;
65
- /** "同区重读空转"提示:同一区间 + 文件未变 + 第二次起,前置到结果头部(仅提示,不拦截)。 */
66
- const REREAD_HINT = '⚠️ 你本轮已经读过该文件的这一区间,且文件未发生变化——请直接基于上次读到的内容操作,不要重复读取空转。如果你其实在找别的内容,改用不同的 offset/limit,或用 Grep 精确定位。\n\n';
67
- async function call(input) {
56
+ - Relative paths resolve against the current working directory.
57
+ - Text results use 1-based line numbers. Use offset/limit for targeted ranges.
58
+ - PNG, JPEG, WebP, GIF, and BMP files are attached for visual inspection.
59
+ - For .ipynb files, offset/limit select cells; results include cell id, type, source, and bounded outputs.
60
+ - PDF supports page-selected text extraction and optional visual page rendering; office files use the matching project Skill.
61
+ - This tool reads files, not directories; use Glob for directory discovery.`;
62
+ function notebookSource(value) {
63
+ if (Array.isArray(value))
64
+ return value
65
+ .filter((item) => typeof item === 'string')
66
+ .join('');
67
+ return typeof value === 'string' ? value : '';
68
+ }
69
+ function renderNotebook(raw, offset, limit) {
70
+ const parsed = JSON.parse(raw);
71
+ if (!Array.isArray(parsed.cells))
72
+ throw new Error('Notebook 缺少 cells 数组');
73
+ const start = offset - 1;
74
+ const cells = parsed.cells.slice(start, start + limit);
75
+ const parts = cells.map((value, index) => {
76
+ const cell = value && typeof value === 'object'
77
+ ? value
78
+ : {};
79
+ const outputs = Array.isArray(cell.outputs) ? cell.outputs : [];
80
+ const renderedOutputs = outputs
81
+ .slice(0, 8)
82
+ .map((output) => {
83
+ if (!output || typeof output !== 'object')
84
+ return String(output ?? '');
85
+ const item = output;
86
+ const text = item.text ??
87
+ (item.data && typeof item.data === 'object'
88
+ ? item.data['text/plain']
89
+ : undefined);
90
+ return notebookSource(text).slice(0, 2_000);
91
+ })
92
+ .filter(Boolean)
93
+ .join('\n');
94
+ return [
95
+ `## Cell ${start + index + 1} [selector=cell-${start + index + 1}${typeof cell.id === 'string' ? `; id=${cell.id}` : ''}; type=${String(cell.cell_type ?? 'unknown')}]`,
96
+ notebookSource(cell.source),
97
+ ...(renderedOutputs ? [`\nOutput:\n${renderedOutputs}`] : []),
98
+ ].join('\n');
99
+ });
100
+ const end = start + cells.length;
101
+ if (end < parsed.cells.length)
102
+ parts.push(`... (${parsed.cells.length - end} cells remain; continue with offset=${end + 1})`);
103
+ return {
104
+ content: parts.join('\n\n'),
105
+ total: parsed.cells.length,
106
+ partial: start > 0 || end < parsed.cells.length,
107
+ };
108
+ }
109
+ async function readStreamedText(filePath, offset, limit) {
110
+ const handle = await open(filePath, 'r');
111
+ const prefix = Buffer.alloc(3);
112
+ const { bytesRead } = await handle.read(prefix, 0, 3, 0);
113
+ await handle.close();
114
+ const utf16 = bytesRead >= 2 && prefix[0] === 0xff && prefix[1] === 0xfe;
115
+ const stream = createReadStream(filePath, {
116
+ encoding: utf16 ? 'utf16le' : 'utf8',
117
+ start: utf16
118
+ ? 2
119
+ : bytesRead >= 3 &&
120
+ prefix[0] === 0xef &&
121
+ prefix[1] === 0xbb &&
122
+ prefix[2] === 0xbf
123
+ ? 3
124
+ : 0,
125
+ });
126
+ const rl = createInterface({ input: stream, crlfDelay: Infinity });
127
+ const lines = [];
128
+ let line = 0;
129
+ let hasMore = false;
130
+ for await (const value of rl) {
131
+ line += 1;
132
+ if (line >= offset && lines.length < limit)
133
+ lines.push(`${line}\t${value}`);
134
+ else if (line >= offset + limit) {
135
+ hasMore = true;
136
+ rl.close();
137
+ stream.destroy();
138
+ break;
139
+ }
140
+ }
141
+ return { numbered: lines.join('\n'), totalKnown: line, hasMore };
142
+ }
143
+ async function call(input, state, cwd, runtimeIdentity) {
68
144
  const offset = input.offset ?? 1;
69
145
  const limit = input.limit ?? MAX_LINES_TO_READ;
70
- // 4a. 路径规范化 + 安全校验(替换原来的手动 resolve + validatePath)
71
- const pathResult = resolveToolPath(input.file_path);
72
- if (!pathResult.ok)
73
- return pathResult;
74
- const filePath = pathResult.resolvedPath;
75
- // 4b. 设备文件双重拦截(虽然 validateAndResolvePath 已包含,但双重保险)
76
- if (isBlockedDevicePath(filePath)) {
77
- return { ok: false, error: `不允许读取设备文件:${filePath}。该路径可能产生无限输出或阻塞进程。` };
78
- }
79
- // 4b'. 二进制扩展名黑名单:在 stat 之前快速短路,避免把 .png/.exe/.pdf 等塞回 LLM
80
- if (hasBinaryExtension(filePath)) {
146
+ const resolved = resolveToolPath(input.file_path, cwd);
147
+ if (!resolved.ok)
148
+ return resolved;
149
+ const filePath = resolved.resolvedPath;
150
+ if (isBlockedDevicePath(filePath))
151
+ return { ok: false, error: `不允许读取设备文件:${filePath}` };
152
+ const info = await stat(filePath).catch((error) => ({ error }));
153
+ if ('error' in info)
81
154
  return {
82
155
  ok: false,
83
- error: `不支持读取二进制文件:${filePath}(扩展名命中二进制黑名单)。若该文件实际为文本,可改后缀或用 Bash cat 旁路。`,
156
+ error: `文件不存在或无法读取:${filePath}(${info.error.message})`,
84
157
  };
158
+ if (!info.isFile())
159
+ return { ok: false, error: `${filePath} 不是文件;目录请用 Glob。` };
160
+ const ext = extname(filePath).toLowerCase();
161
+ if (ext === '.pdf') {
162
+ try {
163
+ const bytes = await readFile(filePath);
164
+ if (bytes.subarray(0, 5).toString() !== '%PDF-') {
165
+ return {
166
+ ok: false,
167
+ error: '文件扩展名是 PDF,但缺少有效 %PDF- 文件头',
168
+ };
169
+ }
170
+ const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');
171
+ const document = await pdfjs.getDocument({
172
+ data: new Uint8Array(bytes),
173
+ useSystemFonts: true,
174
+ }).promise;
175
+ try {
176
+ const requested = parsePdfPages(input.pages, document.numPages);
177
+ if (requested.length > 20)
178
+ return { ok: false, error: '一次最多读取 20 个 PDF 页面' };
179
+ const chunks = [];
180
+ for (const pageNumber of requested) {
181
+ const page = await document.getPage(pageNumber);
182
+ const text = await page.getTextContent();
183
+ chunks.push(`## Page ${pageNumber}\n${text.items
184
+ .map((item) => 'str' in item && typeof item.str === 'string' ? item.str : '')
185
+ .filter(Boolean)
186
+ .join(' ')}`);
187
+ }
188
+ const mode = input.pdf_mode ?? 'auto';
189
+ const sparse = chunks.join('').replace(/\s/g, '').length < requested.length * 80;
190
+ const wantVisual = mode === 'visual' || mode === 'both' || (mode === 'auto' && sparse);
191
+ const images = wantVisual
192
+ ? await renderPdfPages(document, filePath, requested.slice(0, 10), runtimeIdentity)
193
+ : [];
194
+ recordRead(filePath, { contentHash: hashFileBytes(bytes) }, state);
195
+ const rawText = mode === 'visual' ? '[visual-only mode]' : chunks.join('\n\n');
196
+ const text = rawText.length > DEFAULT_MAX_RESULT_SIZE_CHARS
197
+ ? `${rawText.slice(0, DEFAULT_MAX_RESULT_SIZE_CHARS)}\n... (PDF 文本输出已截断;请缩小 pages 范围继续读取)`
198
+ : rawText;
199
+ return {
200
+ ok: true,
201
+ content: `PDF ${document.numPages} pages; selected ${requested.join(', ')}.\n\n${text}${wantVisual && !images.length ? '\n\n视觉渲染不可用(Canvas 与 pdftoppm 均不可用);文本提取结果仍可用。' : ''}`,
202
+ metadata: await fileActivityMetadata(filePath, 'read', 'not_run', 'source', cwd),
203
+ ...(images.length
204
+ ? {
205
+ visionImages: images.map((source) => ({ source })),
206
+ visionNote: '这是所选 PDF 页面的视觉渲染,请检查版式、图表和扫描内容。',
207
+ }
208
+ : {}),
209
+ };
210
+ }
211
+ finally {
212
+ await document.destroy().catch(() => undefined);
213
+ }
214
+ }
215
+ catch (error) {
216
+ const message = error.message;
217
+ return {
218
+ ok: false,
219
+ error: `PDF 解析失败${/password/i.test(message) ? '(文件可能已加密)' : ''}:${message}`,
220
+ };
221
+ }
85
222
  }
86
- // 4c. 文件存在性 / 大小检查
87
- let st;
88
- try {
89
- st = await stat(filePath);
223
+ if (IMAGE_EXTENSIONS.has(ext)) {
224
+ if (info.size > MAX_IMAGE_BYTES)
225
+ return { ok: false, error: `图片超过 ${MAX_IMAGE_BYTES} 字节上限` };
226
+ const bytes = await readFile(filePath);
227
+ recordRead(filePath, { contentHash: hashFileBytes(bytes) }, state);
228
+ return {
229
+ ok: true,
230
+ content: `已读取图片:${filePath}(${info.size} 字节)。图像已附加供视觉模型检查;非视觉模型只能确认文件元数据,不能解读像素内容。`,
231
+ metadata: await fileActivityMetadata(filePath, 'read', 'not_run', 'source', cwd),
232
+ visionImages: [{ source: filePath }],
233
+ visionNote: '这是 Read 工具读取的本地图片,请直接检查图像内容。',
234
+ };
235
+ }
236
+ if (ext === '.ipynb') {
237
+ if (info.size > MAX_NOTEBOOK_BYTES)
238
+ return {
239
+ ok: false,
240
+ error: `Notebook 超过 ${MAX_NOTEBOOK_BYTES} 字节上限`,
241
+ };
242
+ try {
243
+ const snapshot = await readTextFile(filePath);
244
+ const rendered = renderNotebook(snapshot.content, offset, Math.min(limit, 200));
245
+ let content = rendered.content;
246
+ let truncated = false;
247
+ if (content.length > DEFAULT_MAX_RESULT_SIZE_CHARS) {
248
+ content = `${content.slice(0, DEFAULT_MAX_RESULT_SIZE_CHARS)}\n\n... (Notebook 输出已截断)`;
249
+ truncated = true;
250
+ }
251
+ recordRead(filePath, {
252
+ offset,
253
+ limit,
254
+ partial: rendered.partial,
255
+ truncated,
256
+ ...(!rendered.partial && !truncated
257
+ ? { contentHash: hashFileBytes(snapshot.bytes) }
258
+ : {}),
259
+ }, state);
260
+ return {
261
+ ok: true,
262
+ content,
263
+ metadata: await fileActivityMetadata(filePath, 'read', 'not_run', 'source', cwd),
264
+ };
265
+ }
266
+ catch (error) {
267
+ return {
268
+ ok: false,
269
+ error: `Notebook 解析失败:${error.message}`,
270
+ };
271
+ }
90
272
  }
91
- catch (e) {
273
+ if (hasBinaryExtension(filePath)) {
92
274
  return {
93
275
  ok: false,
94
- error: `读取失败:${filePath} 不存在或无法访问(${e.message})`,
276
+ error: `不支持直接读取 ${ext || '该二进制格式'};PDF/Office 文件请使用对应 Skill。`,
95
277
  };
96
278
  }
97
- if (!st.isFile()) {
98
- return { ok: false, error: `${filePath} 不是文件(可能是目录)。要列目录用 Glob 工具。` };
99
- }
100
- if (st.size > MAX_FILE_SIZE_BYTES) {
279
+ if (info.size > MAX_FILE_SIZE_BYTES) {
101
280
  return {
102
281
  ok: false,
103
- error: `文件过大(${st.size} 字节 > ${MAX_FILE_SIZE_BYTES})。请用 offset/limit 分段读。`,
282
+ error: `文件超过 ${MAX_FILE_SIZE_BYTES} 字节硬上限。请用 Grep 定位,或用 Bash 做流式处理。`,
104
283
  };
105
284
  }
106
- // 4d. 根据文件大小选择读取策略
107
- let numbered;
108
- let totalLines;
109
- if (st.size <= STREAM_THRESHOLD) {
110
- // 小文件:原有 readFile 路径
111
- const result = await readSmallFile(filePath, offset, limit);
112
- numbered = result.numbered;
113
- totalLines = result.totalLines;
114
- if (result.isEmpty) {
115
- recordRead(filePath);
116
- return { ok: true, content: '<file is empty>' };
285
+ try {
286
+ let content;
287
+ let totalLines;
288
+ let hasMore;
289
+ let completeBytes;
290
+ if (info.size <= SMALL_TEXT_BYTES) {
291
+ const snapshot = await readTextFile(filePath);
292
+ completeBytes = snapshot.bytes;
293
+ const all = snapshot.content.length === 0 ? [] : snapshot.content.split('\n');
294
+ const start = offset - 1;
295
+ const selected = all.slice(start, start + limit);
296
+ content = selected
297
+ .map((line, index) => `${start + index + 1}\t${line}`)
298
+ .join('\n');
299
+ totalLines = all.length;
300
+ hasMore = start + selected.length < all.length;
301
+ }
302
+ else {
303
+ const streamed = await readStreamedText(filePath, offset, limit);
304
+ content = streamed.numbered;
305
+ totalLines = streamed.totalKnown;
306
+ hasMore = streamed.hasMore;
307
+ }
308
+ if (!content)
309
+ content = '<file is empty>';
310
+ let truncated = false;
311
+ if (content.length > DEFAULT_MAX_RESULT_SIZE_CHARS) {
312
+ content = `${content.slice(0, DEFAULT_MAX_RESULT_SIZE_CHARS)}\n\n⚠️ 重要:Read 输出已截断;截断范围外仍有内容。需要整文件覆盖时必须分段完整读取,精确修改可继续使用 Edit。`;
313
+ truncated = true;
117
314
  }
315
+ const partial = offset !== 1 || hasMore;
316
+ if (hasMore)
317
+ content += `\n\n... (还有更多内容;继续使用 offset=${offset + limit})`;
318
+ const reads = recordRead(filePath, {
319
+ offset,
320
+ limit,
321
+ partial,
322
+ truncated,
323
+ ...(!partial && !truncated && completeBytes
324
+ ? { contentHash: hashFileBytes(completeBytes) }
325
+ : {}),
326
+ }, state);
327
+ if (info.size > 100 * 1024 && offset === 1) {
328
+ content = `⚠️ 大文件:结果受行数和输出字符上限约束,请按需分段读取。\n\n${content}`;
329
+ }
330
+ if (reads >= 2)
331
+ content = `⚠️ 重复读取空转:该区间未变化,前一次 Read 结果仍然有效。\n\n${content}`;
332
+ return {
333
+ ok: true,
334
+ content,
335
+ metadata: await fileActivityMetadata(filePath, 'read', 'not_run', 'source', cwd),
336
+ };
118
337
  }
119
- else {
120
- // 大文件(>1MB 且 <= MAX_FILE_SIZE_BYTES):流式读取
121
- const result = await readLargeFileStream(filePath, offset, limit);
122
- numbered = result.numbered;
123
- totalLines = result.totalLines;
338
+ catch (error) {
339
+ return { ok: false, error: `读取失败:${error.message}` };
124
340
  }
125
- // 4e. 空文件处理
126
- if (totalLines === 0 || !numbered) {
127
- recordRead(filePath);
128
- return { ok: true, content: '<file is empty>' };
341
+ }
342
+ async function renderPdfPages(document, filePath, pages, identity) {
343
+ const outputDir = join(sessionRuntimeDir(identity ?? {}), 'outputs');
344
+ const outputPrefix = `pdf_${randomUUID()}`;
345
+ await mkdir(outputDir, { recursive: true, mode: 0o700 });
346
+ const images = [];
347
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
348
+ let createCanvas;
349
+ try {
350
+ ({ createCanvas } = await dynamicImport('@napi-rs/canvas'));
129
351
  }
130
- // 4f. 大小兜底 + 自动分段提示
131
- let content = numbered;
132
- let truncated = false;
133
- if (content.length > DEFAULT_MAX_RESULT_SIZE_CHARS) {
134
- content =
135
- content.slice(0, DEFAULT_MAX_RESULT_SIZE_CHARS) +
136
- `\n\n... (输出超过 ${DEFAULT_MAX_RESULT_SIZE_CHARS} 字符,已截断)` +
137
- `\n\n⚠️ **重要:本次返回的是截断内容**。直接基于这部分内容写 Edit/Write 会失败 —— old_string 可能在截断范围外,Write 会丢失截断范围外的内容。必须做以下之一:(1) 用更小的 limit + 精确的 offset 缩小读取范围;(2) 已知改动位置时直接 offset 到目标行号附近;(3) 整文件覆盖请用多次 Edit 而非 Write。`;
138
- truncated = true;
352
+ catch {
353
+ /* optional adapter */
139
354
  }
140
- const startIdx = Math.max(0, offset - 1);
141
- const endIdx = Math.min(totalLines, startIdx + limit);
142
- if (endIdx < totalLines) {
143
- const nextOffset = endIdx + 1;
144
- const returnedLines = content.split('\n').filter(l => l.trim()).length;
145
- content += `\n\n... (本次返回 ${returnedLines} 行 / 文件共 ${totalLines} 行;用 offset=${nextOffset} 继续读)`;
355
+ if (createCanvas) {
356
+ try {
357
+ for (const pageNumber of pages) {
358
+ const page = await document.getPage(pageNumber);
359
+ const viewport = page.getViewport({ scale: 2 });
360
+ const canvas = createCanvas(Math.ceil(viewport.width), Math.ceil(viewport.height));
361
+ await page.render({
362
+ canvasContext: canvas.getContext('2d'),
363
+ viewport,
364
+ }).promise;
365
+ const target = join(outputDir, `${outputPrefix}-page-${pageNumber}.png`);
366
+ await writeFile(target, canvas.toBuffer('image/png'), { mode: 0o600 });
367
+ images.push(target);
368
+ }
369
+ void pruneRuntimeOutputs();
370
+ return images;
371
+ }
372
+ catch {
373
+ images.length = 0;
374
+ }
146
375
  }
147
- if (st.size > 100 * 1024 && offset === 1) {
148
- content += `\n\n⚠️ 注意:这是一个大文件(${(st.size / 1024).toFixed(1)} KB)。建议用 offset/limit 分段读取,例如先读关键部分(imports、exports、函数签名)。`;
376
+ for (const pageNumber of pages) {
377
+ const prefix = join(outputDir, `${outputPrefix}-page-${pageNumber}`);
378
+ try {
379
+ await promisify(execFile)('pdftoppm', [
380
+ '-f',
381
+ String(pageNumber),
382
+ '-l',
383
+ String(pageNumber),
384
+ '-singlefile',
385
+ '-png',
386
+ '-r',
387
+ '144',
388
+ filePath,
389
+ prefix,
390
+ ], { maxBuffer: 1024 * 1024 });
391
+ images.push(`${prefix}.png`);
392
+ }
393
+ catch {
394
+ break;
395
+ }
149
396
  }
150
- const readCount = recordRead(filePath, { truncated, offset, limit });
151
- if (readCount >= 2)
152
- content = REREAD_HINT + content;
153
- return { ok: true, content };
397
+ void pruneRuntimeOutputs();
398
+ return images;
154
399
  }
155
- async function readSmallFile(filePath, offset, limit) {
156
- const raw = await readFile(filePath, 'utf8');
157
- if (raw.length === 0) {
158
- return { numbered: '', totalLines: 0, isEmpty: true };
400
+ function parsePdfPages(spec, total) {
401
+ if (!spec)
402
+ return Array.from({ length: Math.min(total, 20) }, (_, index) => index + 1);
403
+ const pages = new Set();
404
+ for (const part of spec.split(',')) {
405
+ const [first, last] = part.split('-').map(Number);
406
+ for (let page = first; page <= (last ?? first); page += 1) {
407
+ if (page < 1 || page > total)
408
+ throw new Error(`PDF 页码超出范围:${page}(共 ${total} 页)`);
409
+ pages.add(page);
410
+ }
159
411
  }
160
- const allLines = raw.split('\n');
161
- const totalLines = allLines.length;
162
- const startIdx = Math.max(0, offset - 1);
163
- const endIdx = Math.min(totalLines, startIdx + limit);
164
- const slice = allLines.slice(startIdx, endIdx);
165
- const numbered = slice
166
- .map((line, i) => `${(startIdx + i + 1).toString()}\t${line}`)
167
- .join('\n');
168
- return { numbered, totalLines, isEmpty: false };
169
- }
170
- async function readLargeFileStream(filePath, offset, limit) {
171
- return new Promise((resolvePromise, reject) => {
172
- const lines = [];
173
- let currentLine = 0;
174
- const startIdx = Math.max(0, offset - 1);
175
- const endLine = startIdx + limit;
176
- const input = createReadStream(filePath, { encoding: 'utf8' });
177
- const rl = createInterface({
178
- input,
179
- crlfDelay: Infinity,
180
- });
181
- input.on('error', (err) => {
182
- reject(err);
183
- });
184
- rl.on('line', (line) => {
185
- currentLine++;
186
- if (currentLine > endLine) {
187
- rl.close();
188
- rl.removeAllListeners();
189
- return;
190
- }
191
- if (currentLine >= offset) {
192
- lines.push(`${currentLine}\t${line}`);
193
- }
194
- });
195
- rl.on('close', () => {
196
- resolvePromise({
197
- numbered: lines.join('\n'),
198
- totalLines: currentLine,
199
- });
200
- });
201
- rl.on('error', (err) => {
202
- reject(err);
203
- });
204
- });
412
+ return [...pages].sort((left, right) => left - right);
205
413
  }
206
- // ---------------- 5. 导出工具对象 ----------------
207
414
  export const readTool = {
208
415
  name: 'Read',
209
416
  description,
210
417
  inputSchema,
211
- parameters,
418
+ parameters: toToolParameters(inputSchema),
212
419
  isReadOnly: true,
213
420
  isConcurrencySafe: true,
214
421
  maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
215
422
  interruptBehavior: () => 'cancel',
216
- call,
423
+ call: (input) => call(input),
424
+ callWithContext: (input, _signal, context) => call(input, context.sessionState?.fileReads, context.cwd, context),
217
425
  };