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
@@ -0,0 +1,837 @@
1
+ /**
2
+ * ============================================================
3
+ * src/tools/agent/agent.ts —— Agent 工具(fork 子 agent / 多 agent 并行)
4
+ * ------------------------------------------------------------
5
+ * 借鉴 Claude Code 官方 AgentTool/ForkAgent 的核心思想,按 minimal-agent
6
+ * 内核架构做可恢复的前台/后台等效实现:
7
+ * - 官方 fork = 「隔离 context 嵌套跑同一主循环 + 摘要回传(完整历史不进
8
+ * 父上下文)」。本工具在隔离 history 中嵌套运行同一主循环,并将私有历史、
9
+ * 进度和结果写入会话运行存储;前台、后台和重启后续接共用同一执行内核。
10
+ * - 同轮多个 Agent 调用仍由 loop.ts 的并行 worker 执行;write scopes 与可选
11
+ * worktree 负责隔离真正可并行的写任务。
12
+ *
13
+ * 已知限制(如实声明):
14
+ * - 子 agent 的 LoopEvent 不外泄:-p / stream-json 宿主只看到本工具的一对
15
+ * tool_start/tool_end;步内进度通过 ToolCallContext.reportProgress 回到宿主。
16
+ * - llm/client.ts 的网络退避档位是进程级共享:N 个并行分身同时失败会交错
17
+ * 抬档、任一成功清零(同 provider 拥塞场景下方向正确,本轮不隔离)。
18
+ *
19
+ * ⚠️ 循环依赖纪律:本文件与 loop.ts / tools/index.ts 构成模块环
20
+ * (tools/index → agent → loop → tools/index)。环内模块的**值**一律走下面的
21
+ * loadCore() 动态 import 懒加载——静态 import 防不住「agent.ts 自身作为环入口」
22
+ * 的方向(测试/消费者直接 import 本文件时,tools/index 的 ALL_TOOLS 数组求值
23
+ * 早于本文件完成初始化,撞 TDZ ReferenceError,已真实发生)。type-only import
24
+ * 编译期擦除,不受此限。loop.ts 侧的 TOOLS_SCHEMA_TOKENS 懒化是同一纪律的配合。
25
+ * ============================================================
26
+ */
27
+
28
+ import { randomUUID } from 'node:crypto';
29
+ import { EventEmitter } from 'node:events';
30
+ import { access } from 'node:fs/promises';
31
+ import { withAskSource } from '../../askBridge.js';
32
+ import { isAbsolute, relative, resolve, sep } from 'node:path';
33
+ import { z } from 'zod';
34
+
35
+ import { getWorkingDir, withWorkingDir } from '../../bootstrap/workingDir.js';
36
+ import { derivePromptCacheContext } from '../../llm/promptCache.js';
37
+ import type { RunQueryOptions } from '../../loop.js';
38
+ import { createSessionState } from '../../session/sessionState.js';
39
+ import {
40
+ assembleStablePrompt,
41
+ buildTransientPromptBundle,
42
+ } from '../../prompts/promptAssembly.js';
43
+ import {
44
+ createAgentRuntimeContext,
45
+ filterToolsForRuntime,
46
+ } from '../../runtime/agentRuntimeContext.js';
47
+ import type { Tool, ToolCallContext, ToolResult } from '../../types.js';
48
+ import { stripThinkBlocks } from '../../utils/stripThink.js';
49
+ import { toToolParameters } from '../../utils/zodToJson.js';
50
+ import { DEFAULT_MAX_RESULT_SIZE_CHARS } from '../types.js';
51
+ import { type AgentTypeDef, SUBAGENT_EXCLUDED_TOOLS, resolveAgentType } from './agentTypes.js';
52
+ import { acquireSubagentLease, normalizeWriteScopes } from './coordinator.js';
53
+ import {
54
+ type AgentWorktree,
55
+ commitWorktreeChanges,
56
+ createAgentWorktree,
57
+ hasWorktreeChanges,
58
+ removeAgentWorktree,
59
+ worktreeChangedFiles,
60
+ worktreeDiffStat,
61
+ } from './worktree.js';
62
+ import { appendAgentEvent, loadAgentRecord, saveAgentRecord, type AgentTaskRecord, type LiveAgentTask } from './agentTaskService.js';
63
+
64
+ // ---------------- 0. 环内模块懒加载(见文件头「循环依赖纪律」) ----------------
65
+
66
+ interface CoreModules {
67
+ runQuery: typeof import('../../loop.js')['runQuery'];
68
+ ALL_TOOLS: typeof import('../index.js')['ALL_TOOLS'];
69
+ executeTool: typeof import('../index.js')['executeTool'];
70
+ }
71
+ let corePromise: Promise<CoreModules> | null = null;
72
+
73
+ /** 首次调用时才真正加载 loop/tools——此刻模块图早已初始化完毕,环安全。 */
74
+ function loadCore(): Promise<CoreModules> {
75
+ if (!corePromise) {
76
+ corePromise = Promise.all([import('../../loop.js'), import('../index.js')]).then(
77
+ ([loop, tools]) => ({
78
+ runQuery: loop.runQuery,
79
+ ALL_TOOLS: tools.ALL_TOOLS,
80
+ executeTool: tools.executeTool,
81
+ }),
82
+ );
83
+ }
84
+ return corePromise;
85
+ }
86
+
87
+ // ---------------- 1. Zod 输入 schema ----------------
88
+
89
+ const inputSchema = z.object({
90
+ description: z
91
+ .string()
92
+ .min(1)
93
+ .max(60, 'description 太长(>60 字符)——它是进度显示用的短标签')
94
+ .describe('子任务短标签(3-8 个词,进度显示用,如「检索路由实现」)'),
95
+ prompt: z
96
+ .string()
97
+ .min(1)
98
+ .describe(
99
+ '完整自包含的任务书。子 agent 是无状态的、看不到当前对话——' +
100
+ '所有背景、涉及的文件路径、期望的产出格式都必须写进来',
101
+ ),
102
+ agent_type: z
103
+ .enum(['explore', 'general'])
104
+ .optional()
105
+ .describe('explore=只读收集调研(默认,可并行放心用);general=可读写执行'),
106
+ isolation: z
107
+ .enum(['none', 'worktree'])
108
+ .optional()
109
+ .describe(
110
+ 'worktree=在独立 git worktree 分支中执行(仅 general 生效;并行写同一批文件时用,' +
111
+ '完成后主 agent 审阅并合并分支)',
112
+ ),
113
+ write_scopes: z
114
+ .array(z.string().min(1))
115
+ .max(32)
116
+ .optional()
117
+ .describe(
118
+ 'general 预计修改的工作目录相对路径。范围互不重叠时可并行;缺省时按整个工作目录串行。explore 不需要。',
119
+ ),
120
+ run_in_background: z.boolean().optional().describe('立即返回 agent_id,子任务在后台运行;用 TaskOutput/TaskStop 管理'),
121
+ resume_agent_id: z.string().regex(/^agent_[0-9a-f-]{36}$/i).optional().describe('继续当前会话中此前完成、中断或失败的 Agent 历史'),
122
+ });
123
+ type AgentInput = z.infer<typeof inputSchema>;
124
+
125
+ const parameters = toToolParameters(inputSchema);
126
+
127
+ // ---------------- 2. Description(给 LLM 看的工具说明) ----------------
128
+ // 借鉴官方 AgentTool prompt 文案(任务书自包含 / 同轮并行 / 何时不用)。
129
+
130
+ const description = `- Launches an isolated sub-agent for one self-contained task. Every run returns an agent_id; run_in_background=true continues without blocking, and resume_agent_id continues its persisted private history across user turns or an app restart.
131
+ - The sub-agent CANNOT see this conversation. The \`prompt\` must be fully self-contained: background, exact file paths, what to look for / do, and the expected output format.
132
+ - **To parallelize, emit multiple Agent calls in ONE assistant message** — they run concurrently. This is the main value of this tool.
133
+ - Background Agent progress/results are read with TaskOutput and stopped with TaskStop. Do not poll: TaskOutput(block=true) waits efficiently.
134
+ - Use for: exploring/researching several independent areas of a codebase at once, parallel information gathering from multiple sources, independent analyses that will be synthesized afterwards, and (with agent_type:'general') genuinely parallelizable write/execute work.
135
+ - Do NOT use for: single targeted lookups that one or two direct tool calls would answer (a fork starts cold and re-derives context — slower and costlier), or strictly sequential work. Only general execution agents may ask the user, and only when genuinely blocked.
136
+ - agent_type: 'explore' (default) = strictly read-only research; 'general' = read-write execution. For direct writes, declare relative write_scopes; disjoint scopes may run concurrently, overlapping or omitted scopes are serialized.
137
+ - isolation:'worktree' (general only): the sub-agent sees the repo's last COMMITTED state. Never commit the user's work merely to expose uncommitted changes; instead keep the task in the current workspace or make the task self-contained. On completion its changes are committed only to a temporary branch; review before applying it to the user's workspace.
138
+ - After sub-agents return you MUST synthesize: cross-check contradictions between reports, integrate into one conclusion — never just forward raw reports. A failed/timed-out fork is not a blocker: proceed with what you have and state the gap.
139
+ - Each sub-task result carries coveredFiles: the actual files/URLs the sub-agent read, extracted from its tool-call history (not self-reported). Before trusting a conclusion, audit its coveredFiles for gaps/overlaps — if coverage looks too thin for what it claims, treat that conclusion as low-confidence.`;
140
+
141
+ // ---------------- 3. 子 agent 系统提示词(memoize,提示词缓存友好) ----------------
142
+
143
+ /** 任务回传契约。能力与权限已经由 assembleBasePrompt profile 精确注入。 */
144
+ const SUBAGENT_COMMON_APPENDIX = `# 你是子 agent(fork 分身)
145
+ 你是主 agent 派生出的子 agent,正在执行一个自包含的子任务:
146
+ - 你看不到主对话;任务书(用户消息)就是你的全部输入。
147
+ - 你的**最后一条回复就是唯一回传主 agent 的报告**,必须自包含:结论 + 关键证据(文件路径/行号)+ 未尽事项。
148
+ 不要输出「我完成了」这类无信息量收尾,也不要在报告里向主 agent 提问——它不会回复你。`;
149
+
150
+ /**
151
+ * 提示词缓存友好:同回合 N 个同类型并行分身必须拿到**逐字节相同**的 system
152
+ * 消息,OpenAI 兼容 provider 的自动前缀缓存才吃得到(Layer 1 含日期、Layer 3
153
+ * 技能目录实时扫描——不 memoize 的话并行的多次求值可能产生字节差异,打散前缀)。
154
+ * 任务差异只落在末尾的 user 消息(任务书)——这是官方 fork「占位符字节一致」
155
+ * 技巧在自动前缀缓存下的原则等价物。
156
+ */
157
+ const PROMPT_CACHE_TTL_MS = 60_000;
158
+ const promptCache = new Map<string, { value: string; at: number }>();
159
+
160
+ async function buildSubSystemPrompt(
161
+ cwd: string,
162
+ def: AgentTypeDef,
163
+ allTools: Tool[],
164
+ ): Promise<string> {
165
+ // Effective capabilities are part of prompt identity; a child must never
166
+ // reuse a cached parent-capability prompt (notably Desktop WebBrowser).
167
+ const key = `${cwd}::${def.type}::${allTools.map((tool) => tool.name).join(',')}`;
168
+ const hit = promptCache.get(key);
169
+ if (hit && Date.now() - hit.at < PROMPT_CACHE_TTL_MS) return hit.value;
170
+
171
+ const allowedNames = def.resolveAllowedTools(allTools.map((t) => t.name));
172
+ const subTools = allTools.filter((t) => allowedNames.includes(t.name));
173
+ const base = await assembleStablePrompt({
174
+ cwd,
175
+ tools: subTools,
176
+ profile: def.type === 'general' ? 'subagent-general' : 'subagent-explore',
177
+ includeProjectInstructions: true, // 子 agent 在同一仓库干活,项目约定必须继承
178
+ includePlugins: false, // 插件是宿主命令面,子 agent 不该被引去激活插件
179
+ });
180
+ const value = [base, SUBAGENT_COMMON_APPENDIX, def.promptAppendix].join('\n\n');
181
+ promptCache.set(key, { value, at: Date.now() });
182
+ return value;
183
+ }
184
+
185
+ // ---------------- 3.5 覆盖度提取(客观,从工具调用历史推导) ----------------
186
+
187
+ /**
188
+ * 从子任务 history 中提取「实际读取/调研过的文件/目标」清单,
189
+ * 用于父 agent / 宿主做覆盖度自检,避免「以为覆盖了其实没覆盖」的幻觉。
190
+ */
191
+ export function extractCoveredFiles(
192
+ history: readonly import('../../types.js').Message[],
193
+ startIdx: number,
194
+ ): string[] {
195
+ const seen = new Set<string>();
196
+ for (let i = startIdx; i < history.length; i++) {
197
+ const msg = history[i]!;
198
+ if (msg.role !== 'assistant' || !msg.tool_calls) continue;
199
+ for (const tc of msg.tool_calls) {
200
+ const name = tc.function.name;
201
+ let args: Record<string, unknown> | undefined;
202
+ try { args = JSON.parse(tc.function.arguments); } catch { continue; }
203
+ if (!args) continue;
204
+ if (name === 'Read' && typeof args.file_path === 'string') {
205
+ seen.add(args.file_path);
206
+ } else if (name === 'Glob' && typeof args.pattern === 'string') {
207
+ seen.add(`glob:${args.pattern}`);
208
+ } else if (name === 'Grep') {
209
+ const parts = ['grep:' + (typeof args.pattern === 'string' ? args.pattern : '?')];
210
+ if (typeof args.path === 'string') parts.push(`path=${args.path}`);
211
+ if (typeof args.glob === 'string') parts.push(`glob=${args.glob}`);
212
+ seen.add(parts.join(' '));
213
+ } else if (name === 'WebFetch' && typeof args.url === 'string') {
214
+ seen.add(args.url);
215
+ } else if (name === 'WebBrowser' && typeof args.url === 'string') {
216
+ seen.add(`browser:${args.url}`);
217
+ }
218
+ }
219
+ }
220
+ return [...seen].sort();
221
+ }
222
+
223
+ // ---------------- 4. 执行逻辑(导出供测试 DI) ----------------
224
+
225
+ /** 测试注入缝(镜像 runAdvisor 的 chatFn 模式):生产不传,走真实模块。 */
226
+ export interface AgentTaskDeps {
227
+ chat?: RunQueryOptions['chat'];
228
+ executeTool?: RunQueryOptions['executeTool'];
229
+ initialHistory?: RunQueryOptions['history'];
230
+ captureHistory?: (history:RunQueryOptions['history'])=>void|Promise<void>;
231
+ existingWorktree?: AgentWorktree;
232
+ onWorktreeCreated?: (worktree: AgentWorktree) => void | Promise<void>;
233
+ }
234
+
235
+ function positiveInt(value: string | undefined): number | undefined {
236
+ const n = Number.parseInt(value ?? '', 10);
237
+ return Number.isFinite(n) && n > 0 ? n : undefined;
238
+ }
239
+
240
+ /** worktree 收尾:无改动清理;有改动代提交 + 生成给主 agent 的收口指引尾巴。 */
241
+ async function finalizeWorktree(wt: AgentWorktree, label: string): Promise<string> {
242
+ const changed = await hasWorktreeChanges(wt.path, wt.baseSha);
243
+ if (!changed) {
244
+ await removeAgentWorktree(wt.path, wt.branch, wt.gitRoot);
245
+ return '';
246
+ }
247
+ const commit = await commitWorktreeChanges(wt.path, `agent fork: ${label}`);
248
+ const stat = await worktreeDiffStat(wt.path, wt.baseSha);
249
+ return [
250
+ '',
251
+ '———',
252
+ '⚠️ 子任务在独立 worktree 分支产生了改动(尚未合并回主工作区):',
253
+ `- 分支:${wt.branch}`,
254
+ `- 隔离任务:${wt.branch.replace(/^agent\//, '')}`,
255
+ commit.ok ? '' : `- ⚠️ 自动提交失败(${commit.error}),改动仍留在 worktree 工作区未入分支`,
256
+ stat ? `- 改动摘要:\n${stat}` : '',
257
+ '主 agent 收口步骤:',
258
+ `1. 审阅改动:\`git diff ${wt.baseSha.slice(0, 12)} ${wt.branch}\`;`,
259
+ `2. 确认后在主工作区合并:\`git merge --squash ${wt.branch}\` 后提交(或按需 cherry-pick);`,
260
+ '3. 在 KakadeAI 子任务卡中应用或放弃该隔离变更。',
261
+ ]
262
+ .filter(Boolean)
263
+ .join('\n');
264
+ }
265
+
266
+ /**
267
+ * 真正的执行逻辑:在隔离的 history + 独立 sessionState 上嵌套跑一个完整
268
+ * runQuery 子循环,把最终报告文本作为 ToolResult 回传。
269
+ *
270
+ * 刻意**不走 runTurn/runAgentTurn**:它们会 acquireContextLock(非重入锁,
271
+ * 主 turn 全程持锁 → 同 sessionFile 必死锁)并 saveContext 落盘——子 agent
272
+ * 应该零磁盘足迹、用完即弃。
273
+ */
274
+ export async function runAgentTask(
275
+ input: AgentInput,
276
+ signal: AbortSignal | undefined,
277
+ ctx: ToolCallContext,
278
+ deps: AgentTaskDeps = {},
279
+ ): Promise<ToolResult> {
280
+ const startedAt = Date.now();
281
+ // ---- 前置检查 ----
282
+ if (!ctx.provider) {
283
+ return {
284
+ ok: false,
285
+ error: '子任务功能不可用(内核未透传 provider),请自己直接执行该任务。',
286
+ };
287
+ }
288
+ if (ctx.agentCalls && ctx.agentCalls.used >= ctx.agentCalls.max) {
289
+ return {
290
+ ok: false,
291
+ error: `本回合子任务派生已达上限(${ctx.agentCalls.max} 次),剩余工作请自己直接完成。`,
292
+ };
293
+ }
294
+ if (ctx.agentCalls) ctx.agentCalls.used++;
295
+
296
+ const { runQuery, ALL_TOOLS, executeTool } = await loadCore();
297
+
298
+ const def = resolveAgentType(input.agent_type);
299
+ // 轮数上限:AGENT_TOOL_MAX_TURNS 可统一覆盖两种类型(默认 explore 50 / general 100)。
300
+ const maxTurns = positiveInt(process.env.AGENT_TOOL_MAX_TURNS)
301
+ ?? (def.type === 'explore' ? ctx.subagentConfig?.exploreMaxTurns : ctx.subagentConfig?.generalMaxTurns)
302
+ ?? def.maxTurns;
303
+ // allowedTools 只裁「下发给模型的清单」;guardedExecute 是真正的硬防线(见下)。
304
+ const allowedTools = def.resolveAllowedTools(ALL_TOOLS.map((t) => t.name));
305
+
306
+ // ---- worktree 隔离(仅 general 显式要求时) ----
307
+ let wt: AgentWorktree | null = deps.existingWorktree ?? null;
308
+ const isolation = input.isolation ?? ctx.subagentConfig?.defaultWriteIsolation ?? 'none';
309
+ if (def.type === 'general' && isolation === 'worktree' && !wt) {
310
+ const created = await createAgentWorktree(`fork-${randomUUID().slice(0, 8)}`, ctx.cwd);
311
+ if (!created.ok) {
312
+ return {
313
+ ok: false,
314
+ error: `worktree 隔离创建失败:${created.error}。可改用 isolation:'none'(确保各子任务写不相交的文件)或自己串行执行。`,
315
+ };
316
+ }
317
+ wt = created;
318
+ await deps.onWorktreeCreated?.(created);
319
+ }
320
+ const cwd = wt?.path ?? ctx.cwd ?? getWorkingDir();
321
+ let writeScopes: string[];
322
+ try {
323
+ writeScopes = def.type === 'general'
324
+ ? await normalizeWriteScopes(cwd, wt ? ['.'] : input.write_scopes)
325
+ : [];
326
+ } catch (error) {
327
+ if (wt) await removeAgentWorktree(wt.path, wt.branch, wt.gitRoot);
328
+ return { ok: false, error: (error as Error).message };
329
+ }
330
+ const rootScope = await normalizeWriteScopes(cwd, undefined);
331
+ const childMemoryAccess = ctx.memoryAccess === 'denied' || !ctx.memoryAccess
332
+ ? 'denied'
333
+ : 'read-only';
334
+ const childRuntime = createAgentRuntimeContext({
335
+ surface: 'subagent',
336
+ profile: def.type === 'general' ? 'subagent-general' : 'subagent-explore',
337
+ cwd,
338
+ workspaceId: ctx.workspaceId,
339
+ sessionId: ctx.sessionId,
340
+ owner: childMemoryAccess === 'denied' ? 'untrusted' : 'local-owner',
341
+ memoryAccess: childMemoryAccess,
342
+ interactiveAsk: def.type === 'general',
343
+ toolGate: ctx.toolGate,
344
+ });
345
+ const subTools = filterToolsForRuntime(
346
+ ALL_TOOLS.filter((tool) => allowedTools.includes(tool.name)),
347
+ childRuntime,
348
+ );
349
+ const subToolNames = subTools.map((tool) => tool.name);
350
+ const childTransientBundle = await buildTransientPromptBundle({
351
+ cwd,
352
+ tools: subTools,
353
+ profile: childRuntime.profile,
354
+ includeProjectInstructions: true,
355
+ includePlugins: false,
356
+ runtime: {
357
+ surface: 'subagent',
358
+ cwd,
359
+ workspaceId: ctx.workspaceId,
360
+ sessionId: ctx.sessionId,
361
+ interactiveAsk: childRuntime.interactiveAsk,
362
+ memoryAccess: childMemoryAccess,
363
+ currentUserInput: input.prompt,
364
+ },
365
+ }).catch(() => undefined);
366
+
367
+ let lastPublishAt = 0;
368
+ const label = input.description;
369
+ const publish = (line: string, detail?: import('../../types.js').ToolProgressDetail, force = false): void => {
370
+ const now = Date.now();
371
+ if (!force && now - lastPublishAt < 200) return;
372
+ lastPublishAt = now;
373
+ ctx.reportProgress?.(line, detail);
374
+ };
375
+
376
+ // ---- 子 history:一条 system(压缩按 role 定位,必须存在)+ runQuery 自己 push 任务书 ----
377
+ const history: RunQueryOptions['history'] = deps.initialHistory?.length ? [...deps.initialHistory] : [
378
+ { role: 'system', content: await buildSubSystemPrompt(cwd, def, subTools) },
379
+ ];
380
+ const historyLenBefore = history.length;
381
+ const maxConcurrent = positiveInt(process.env.AGENT_TOOL_MAX_CONCURRENT) ?? ctx.subagentConfig?.maxConcurrent ?? 3;
382
+ let releaseLease: (() => void) | null = null;
383
+ try {
384
+ releaseLease = await acquireSubagentLease(
385
+ cwd,
386
+ def.type === 'general' ? writeScopes : [`__read__/${randomUUID()}`],
387
+ maxConcurrent,
388
+ {
389
+ signal,
390
+ onStatus: (status) => {
391
+ if (status.phase === 'queued') {
392
+ publish(`排队中${status.queuePosition ? ` · 第 ${status.queuePosition} 位` : ''}`, {
393
+ kind: 'subagent',
394
+ phase: 'queued',
395
+ queuePosition: status.queuePosition,
396
+ activeCount: status.activeCount,
397
+ maxConcurrent: status.maxConcurrent,
398
+ }, true);
399
+ } else {
400
+ publish('准备上下文', {
401
+ kind: 'subagent', phase: 'preparing', activeCount: status.activeCount, maxConcurrent: status.maxConcurrent,
402
+ }, true);
403
+ }
404
+ },
405
+ },
406
+ );
407
+ } catch (error) {
408
+ if ((error as Error).name === 'AbortError') return { ok: false, error: 'aborted' };
409
+ return { ok: false, error: `子任务「${label}」进入执行队列失败:${(error as Error).message}` };
410
+ }
411
+
412
+ // ---- 超时兜底(仿 Advisor 模式;默认 30 分钟——50/100 轮的真实审计/执行型
413
+ // 子任务 10 分钟跑不完,超时是兜底不是预期时长,env 可调低) ----
414
+ const timeoutMs = positiveInt(process.env.AGENT_TOOL_TIMEOUT_MS) ?? ctx.subagentConfig?.timeoutMs ?? 1_800_000;
415
+ const timeoutController = new AbortController();
416
+ const ownerCancelController = new AbortController();
417
+ let remainingTimeoutMs = timeoutMs;
418
+ let timerStartedAt = 0;
419
+ let timer: ReturnType<typeof setTimeout> | null = null;
420
+ const armTimeout = (): void => {
421
+ if (timer || timeoutController.signal.aborted) return;
422
+ if (remainingTimeoutMs <= 0) {
423
+ timeoutController.abort(new Error('agent-task-timeout'));
424
+ return;
425
+ }
426
+ timerStartedAt = Date.now();
427
+ timer = setTimeout(() => {
428
+ timer = null;
429
+ remainingTimeoutMs = 0;
430
+ timeoutController.abort(new Error('agent-task-timeout'));
431
+ }, remainingTimeoutMs);
432
+ };
433
+ const pauseTimeout = (): void => {
434
+ if (!timer) return;
435
+ clearTimeout(timer);
436
+ timer = null;
437
+ remainingTimeoutMs = Math.max(0, remainingTimeoutMs - (Date.now() - timerStartedAt));
438
+ };
439
+ const resumeTimeout = (): void => {
440
+ if (!ownerCancelController.signal.aborted) armTimeout();
441
+ };
442
+ const stopTimeout = (): void => {
443
+ if (timer) clearTimeout(timer);
444
+ timer = null;
445
+ };
446
+ armTimeout();
447
+ const combinedSignal = signal
448
+ ? AbortSignal.any([signal, timeoutController.signal, ownerCancelController.signal])
449
+ : AbortSignal.any([timeoutController.signal, ownerCancelController.signal]);
450
+
451
+ // ---- 递归硬防线:allowedTools 只影响模型可见清单,不拦 executeTool 的
452
+ // 全局调度口(loop.ts 注释明示);这里把排除工具在执行层直接拒绝。 ----
453
+ const baseExecute = deps.executeTool ?? executeTool;
454
+ const changedFiles = new Set<string>();
455
+ let askCalls = 0;
456
+ const guardedExecute: NonNullable<RunQueryOptions['executeTool']> = async (name, args, s, c) => {
457
+ if (!allowedTools.includes(name) || SUBAGENT_EXCLUDED_TOOLS.includes(name)) {
458
+ return { ok: false as const, error: `子 agent 类型 ${def.type} 不允许调用 ${name}。` };
459
+ }
460
+ if (name === 'AskUserQuestion') {
461
+ askCalls++;
462
+ if (def.type !== 'general') {
463
+ return { ok: false as const, error: 'explore 子 agent 不能向用户提问,请在报告中记录假设和信息缺口。' };
464
+ }
465
+ if (askCalls > 2) {
466
+ return { ok: false as const, error: '当前子任务最多向用户提问两次,请基于已有回答完成任务或在报告中说明剩余缺口。' };
467
+ }
468
+ }
469
+ let parsed: Record<string, unknown> = {};
470
+ try { parsed = JSON.parse(args) as Record<string, unknown>; } catch { /* executeTool will report malformed input */ }
471
+ const rawPath = typeof parsed.file_path === 'string' ? parsed.file_path : typeof parsed.path === 'string' ? parsed.path : undefined;
472
+ if (def.type === 'general' && !wt && name === 'Bash' && writeScopes[0] !== rootScope[0]) {
473
+ return { ok: false as const, error: '声明了局部 write_scopes 时禁止 Bash:无法可靠证明命令只写入批准范围。请改用文件工具或使用整个工作目录范围。' };
474
+ }
475
+ if (def.type === 'general' && rawPath) {
476
+ if (isAbsolute(rawPath)) return { ok: false as const, error: `${name} 必须使用工作目录相对路径:${rawPath}` };
477
+ let physical: string;
478
+ try { physical = (await normalizeWriteScopes(cwd, [rawPath]))[0]!; }
479
+ catch { return { ok: false as const, error: `${name} 路径越过工作目录或符号链接边界:${rawPath}` }; }
480
+ const inside = writeScopes.some((scope) => physical === scope || physical.startsWith(`${scope}${sep}`));
481
+ if (!inside) return { ok: false as const, error: `${name} 路径超出 write_scopes:${rawPath}` };
482
+ if (['Edit', 'MultiEdit', 'Write', 'Download'].includes(name)) {
483
+ changedFiles.add(relative(cwd, resolve(cwd, rawPath)).split(sep).join('/'));
484
+ }
485
+ }
486
+ return baseExecute(name, args, s, c);
487
+ };
488
+
489
+ // ---- 事件消费:子 LoopEvent 一律不外泄,折算成统计 + 步内进度 ----
490
+ let turns = 0;
491
+ let toolOk = 0;
492
+ let toolFail = 0;
493
+ let textChars = 0;
494
+ let sawLlmError = false;
495
+ let llmErrorDetail = '';
496
+ let sawMaxTurns = false;
497
+
498
+ const consumeRun = async (): Promise<void> => {
499
+ publish('正在分析任务', { kind: 'subagent', phase: 'analyzing' }, true);
500
+ const events = runQuery(input.prompt, {
501
+ provider: ctx.provider!,
502
+ runtimeContext: childRuntime,
503
+ history,
504
+ maxTurns,
505
+ allowedTools: subToolNames,
506
+ signal: combinedSignal,
507
+ sessionState: createSessionState(), // 独立会话态,不与主循环/其他分身串扰
508
+ toolGate: ctx.toolGate, // 继承主循环审批策略(不给可写子 agent 开审批后门)
509
+ chat: deps.chat,
510
+ executeTool: guardedExecute,
511
+ memoryContext: {
512
+ access: childMemoryAccess,
513
+ workspaceId: ctx.workspaceId,
514
+ sessionId: ctx.sessionId,
515
+ },
516
+ transientBundle: childTransientBundle,
517
+ promptCache: derivePromptCacheContext(
518
+ ctx.promptCache,
519
+ `subagent:${ctx.toolCallId ?? input.description}`,
520
+ ),
521
+ // 不传 transientContext:任务书必须自包含,父回合的瞬时上下文不该隐式泄入
522
+ });
523
+ for await (const ev of events) {
524
+ switch (ev.type) {
525
+ case 'assistant_message':
526
+ turns++;
527
+ break;
528
+ case 'tool_start':
529
+ publish(`正在执行 · ${ev.argsFriendly ?? ev.toolName}`, {
530
+ kind: 'subagent',
531
+ phase: 'using_tool',
532
+ turn: Math.max(turns, 1),
533
+ toolName: ev.toolName,
534
+ toolDescription: ev.argsFriendly,
535
+ }, true);
536
+ break;
537
+ case 'tool_end':
538
+ if (ev.ok) toolOk++;
539
+ else toolFail++;
540
+ break;
541
+ case 'text':
542
+ textChars += ev.delta.length;
543
+ publish(`正在整理结论 · ${textChars} 字`, {
544
+ kind: 'subagent', phase: 'drafting', turn: Math.max(turns, 1), outputChars: textChars,
545
+ });
546
+ break;
547
+ case 'error':
548
+ // 中断(code:'aborted')不在这里判——结局映射直接查 signal 状态更可靠
549
+ // (中断在 loop 里有三种浮出形态:入口 error / AbortError catch / interrupted 事件)。
550
+ if (ev.code === 'llm_error') {
551
+ sawLlmError = true;
552
+ llmErrorDetail = ev.error;
553
+ } else if (ev.code === 'max_turns') sawMaxTurns = true;
554
+ // compact_failed 非致命:loop 自己会硬上,这里无需处理
555
+ break;
556
+ default:
557
+ break; // 其余事件(text 流/压缩/context_usage…)对父层不可见
558
+ }
559
+ }
560
+ };
561
+
562
+ try {
563
+ // worktree 模式下整个子执行在 ALS 作用域内以 worktree 为 cwd(相对路径
564
+ // 工具全部落进隔离目录);并行的多个分身各自的 ALS 作用域互不干扰。
565
+ const execute = () => withAskSource(
566
+ { kind: 'subagent', parentToolCallId: ctx.toolCallId, description: label },
567
+ {
568
+ onWaitStart: () => {
569
+ pauseTimeout();
570
+ publish('等待你的回答', { kind: 'subagent', phase: 'waiting_owner' }, true);
571
+ },
572
+ onWaitEnd: () => {
573
+ resumeTimeout();
574
+ publish('已回答,继续分析', { kind: 'subagent', phase: 'analyzing' }, true);
575
+ },
576
+ onCancel: () => ownerCancelController.abort(new Error('owner-cancelled-subagent')),
577
+ },
578
+ consumeRun,
579
+ );
580
+ if (wt) await withWorkingDir(wt.path, execute);
581
+ else await execute();
582
+ } catch (e) {
583
+ stopTimeout();
584
+ if (wt) await finalizeWorktree(wt, label); // fail-closed:有改动会保留分支
585
+ if (ownerCancelController.signal.aborted) {
586
+ return { ok: false, error: `子任务「${label}」已由 Agent 所有者取消。` };
587
+ }
588
+ if ((e as Error).name === 'AbortError' || signal?.aborted) {
589
+ return { ok: false, error: 'aborted' };
590
+ }
591
+ return { ok: false, error: `子任务「${label}」执行异常:${(e as Error).message}` };
592
+ } finally {
593
+ stopTimeout();
594
+ releaseLease?.();
595
+ await deps.captureHistory?.(history);
596
+ }
597
+
598
+ // ---- 结局映射(直接查 signal 状态,不依赖事件旗标) ----
599
+ if (ownerCancelController.signal.aborted) {
600
+ if (wt) await finalizeWorktree(wt, label);
601
+ return { ok: false, error: `子任务「${label}」已由 Agent 所有者取消。` };
602
+ }
603
+ // 用户中断:回传 loop 认识的字面量 'aborted'(它据此把整轮判为 interrupted)。
604
+ if (signal?.aborted) {
605
+ if (wt) await finalizeWorktree(wt, label);
606
+ return { ok: false, error: 'aborted' };
607
+ }
608
+ // 超时(timeoutController 触发而非用户中断)
609
+ if (timeoutController.signal.aborted) {
610
+ const wtTail = wt ? await finalizeWorktree(wt, label) : '';
611
+ return {
612
+ ok: false,
613
+ error:
614
+ `子任务「${label}」超过 ${Math.round(timeoutMs / 1000)}s 未完成,已终止。` +
615
+ `可缩小任务范围重试,或自己直接执行。${wtTail}`,
616
+ };
617
+ }
618
+
619
+ // ---- 产出提取:history 尾部倒扫最后一条非空 assistant 文本(剥 <think>) ----
620
+ let report = '';
621
+ for (let i = history.length - 1; i >= historyLenBefore; i--) {
622
+ const msg = history[i]!;
623
+ if (msg.role !== 'assistant' || !msg.content) continue;
624
+ const cleaned = stripThinkBlocks(msg.content);
625
+ if (cleaned) {
626
+ report = cleaned;
627
+ break;
628
+ }
629
+ }
630
+
631
+ if (!report && sawLlmError) {
632
+ const wtTail = wt ? await finalizeWorktree(wt, label) : '';
633
+ return { ok: false, error: `子任务「${label}」LLM 调用失败:${llmErrorDetail}${wtTail}` };
634
+ }
635
+ if (!report && sawMaxTurns) {
636
+ const wtTail = wt ? await finalizeWorktree(wt, label) : '';
637
+ return {
638
+ ok: false,
639
+ error: `子任务「${label}」达到轮数上限(${maxTurns} 轮)被截断且没有产出。可把任务拆得更小重试。${wtTail}`,
640
+ };
641
+ }
642
+ if (!report) {
643
+ const wtTail = wt ? await finalizeWorktree(wt, label) : '';
644
+ return { ok: false, error: `子任务「${label}」没有产出任何文本。${wtTail}` };
645
+ }
646
+
647
+ if (sawMaxTurns) {
648
+ report = `⚠️ 子任务达到轮数上限(${maxTurns} 轮)被截断,以下是截断时的最后输出(可能不完整):\n\n${report}`;
649
+ }
650
+
651
+ // ---- 统计尾巴:主 agent 收口时评估报告可信度用 ----
652
+ const toolTotal = toolOk + toolFail;
653
+ report += `\n\n———\n(子任务「${label}」[${def.type}]:${turns} 轮 · ${toolTotal} 次工具调用${toolFail > 0 ? `,${toolFail} 次失败` : ''})`;
654
+
655
+ if (wt) {
656
+ for (const path of await worktreeChangedFiles(wt.path, wt.baseSha)) changedFiles.add(path);
657
+ report += await finalizeWorktree(wt, label);
658
+ }
659
+
660
+ // 覆盖度提取(客观,从 tool call 历史推导)——在截断之前,不受报告长度影响。
661
+ const coveredFiles = extractCoveredFiles(history, historyLenBefore);
662
+ if (report.length > DEFAULT_MAX_RESULT_SIZE_CHARS) {
663
+ report =
664
+ report.slice(0, DEFAULT_MAX_RESULT_SIZE_CHARS) +
665
+ `\n\n... (报告超过 ${DEFAULT_MAX_RESULT_SIZE_CHARS} 字符,已截断)`;
666
+ }
667
+ return {
668
+ ok: true,
669
+ content: report,
670
+ metadata: {
671
+ kind: 'agent',
672
+ agentType: def.type,
673
+ description: label,
674
+ isolation: wt ? 'worktree' : 'none',
675
+ turns,
676
+ toolCalls: toolTotal,
677
+ failedToolCalls: toolFail,
678
+ durationMs: Date.now() - startedAt,
679
+ changedFiles: [...changedFiles].sort(),
680
+ coveredFiles,
681
+ ...(wt && changedFiles.size > 0 ? {
682
+ worktree: {
683
+ id: wt.branch.replace(/^agent\//, ''),
684
+ branch: wt.branch,
685
+ baseSha: wt.baseSha,
686
+ status: 'ready' as const,
687
+ },
688
+ } : {}),
689
+ },
690
+ };
691
+ }
692
+
693
+ // 无宿主上下文的裸 call:拿不到 provider,走统一的缺失错误路径。
694
+ async function call(input: AgentInput, signal?: AbortSignal): Promise<ToolResult> {
695
+ return runAgentTask(input, signal, {});
696
+ }
697
+
698
+ export async function runManagedAgentTask(
699
+ input: AgentInput,
700
+ signal: AbortSignal | undefined,
701
+ ctx: ToolCallContext,
702
+ deps: AgentTaskDeps = {},
703
+ ): Promise<ToolResult> {
704
+ if (!ctx.sessionState) return runAgentTask(input, signal, ctx);
705
+ const id = input.resume_agent_id ?? `agent_${randomUUID()}`;
706
+ let initialHistory: RunQueryOptions['history'] | undefined;
707
+ let resumedWorktree: AgentWorktree | undefined;
708
+ let coreInput = { ...input, run_in_background: undefined, resume_agent_id: undefined };
709
+ if (input.resume_agent_id) {
710
+ const live = ctx.sessionState.agentTasks.tasks.get(id);
711
+ if (live?.status === 'running') return { ok: false, error: `Agent ${id} 正在运行,不能并发恢复` };
712
+ const saved = await loadAgentRecord(ctx, id);
713
+ if (!saved) return { ok: false, error: `找不到可恢复 Agent:${id}` };
714
+ initialHistory = saved.history;
715
+ resumedWorktree = saved.worktree && await access(saved.worktree.path)
716
+ .then(() => saved.worktree)
717
+ .catch(() => undefined);
718
+ coreInput = {
719
+ ...(saved.input as AgentInput),
720
+ prompt: input.prompt,
721
+ description: input.description || saved.description,
722
+ run_in_background: undefined,
723
+ resume_agent_id: undefined,
724
+ };
725
+ }
726
+ const abort = new AbortController();
727
+ const combined = signal ? AbortSignal.any([signal, abort.signal]) : abort.signal;
728
+ let settle!: () => void;
729
+ const completion = new Promise<void>((resolvePromise) => { settle = resolvePromise; });
730
+ const task: LiveAgentTask = {
731
+ id,
732
+ status: 'running',
733
+ description: coreInput.description,
734
+ input: coreInput as unknown as Record<string, unknown>,
735
+ createdAt: Date.now(),
736
+ updatedAt: Date.now(),
737
+ abort,
738
+ completion,
739
+ events: new EventEmitter(),
740
+ };
741
+ ctx.sessionState.agentTasks.tasks.set(id, task);
742
+ await saveAgentRecord(ctx, task);
743
+ await appendAgentEvent(ctx, id, { phase: 'started', resumed: Boolean(input.resume_agent_id) });
744
+
745
+ const run = async (): Promise<ToolResult> => {
746
+ let captured = initialHistory;
747
+ let lastPersistedProgressAt = 0;
748
+ const managedContext: ToolCallContext = {
749
+ ...ctx,
750
+ reportProgress: (line, detail) => {
751
+ ctx.reportProgress?.(line, detail);
752
+ task.progress = line;
753
+ task.events.emit('change');
754
+ if (Date.now() - lastPersistedProgressAt >= 1_000) {
755
+ lastPersistedProgressAt = Date.now();
756
+ void saveAgentRecord(ctx, task).catch(() => undefined);
757
+ void appendAgentEvent(ctx, id, { phase: 'progress', message: line })
758
+ .catch(() => undefined);
759
+ }
760
+ },
761
+ };
762
+ try {
763
+ const result = await runAgentTask(coreInput, combined, managedContext, {
764
+ ...deps,
765
+ initialHistory,
766
+ existingWorktree: resumedWorktree,
767
+ onWorktreeCreated: async (worktree) => {
768
+ task.worktree = worktree;
769
+ await saveAgentRecord(ctx, task);
770
+ },
771
+ captureHistory: (history) => { captured = [...history]; },
772
+ });
773
+ task.result = result;
774
+ task.history = captured;
775
+ task.status = abort.signal.aborted ? 'stopped' : result.ok ? 'completed' : 'failed';
776
+ if (task.worktree && !await access(task.worktree.path).then(() => true).catch(() => false)) {
777
+ task.worktree = undefined;
778
+ }
779
+ task.updatedAt = Date.now();
780
+ await saveAgentRecord(ctx, task);
781
+ await appendAgentEvent(ctx, id, { phase: task.status, ok: result.ok });
782
+ return result;
783
+ } catch (error) {
784
+ task.status = abort.signal.aborted ? 'stopped' : 'failed';
785
+ task.result = { ok: false, error: (error as Error).message };
786
+ task.history = captured;
787
+ task.updatedAt = Date.now();
788
+ await saveAgentRecord(ctx, task).catch(() => undefined);
789
+ await appendAgentEvent(ctx, id, { phase: task.status, error: (error as Error).message })
790
+ .catch(() => undefined);
791
+ return task.result;
792
+ } finally {
793
+ task.events.emit('change');
794
+ settle();
795
+ }
796
+ };
797
+
798
+ if (input.run_in_background) {
799
+ void run();
800
+ return {
801
+ ok: true,
802
+ content: `后台 Agent 已启动。agent_id=${id}\n使用 TaskOutput 获取进度/结果,TaskStop 停止。`,
803
+ metadata: {
804
+ kind: 'agent', agentId: id, status: 'running',
805
+ agentType: input.agent_type ?? 'explore', description: input.description,
806
+ isolation: input.isolation ?? 'none', turns: 0, toolCalls: 0,
807
+ failedToolCalls: 0, durationMs: 0, changedFiles: [],
808
+ },
809
+ };
810
+ }
811
+ const result = await run();
812
+ if (result.ok && result.metadata?.kind === 'agent') {
813
+ result.metadata.agentId = id;
814
+ result.metadata.status = 'completed';
815
+ result.content = `agent_id=${id}\n${result.content}`;
816
+ } else if (!result.ok) {
817
+ result.error = `agent_id=${id}\n${result.error}`;
818
+ }
819
+ return result;
820
+ }
821
+
822
+ // ---------------- 5. 导出 ----------------
823
+
824
+ export const agentTool: Tool<AgentInput> = {
825
+ name: 'Agent',
826
+ description,
827
+ inputSchema,
828
+ parameters,
829
+ // isReadOnly=false:general 分身可写盘。isConcurrencySafe=true 是并行的全部
830
+ // 来源——同轮多个 Agent 调用走 loop.ts 的并行 worker 队列。
831
+ isReadOnly: false,
832
+ isConcurrencySafe: true,
833
+ maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
834
+ interruptBehavior: () => 'block',
835
+ call,
836
+ callWithContext: runManagedAgentTask,
837
+ };