@cjhyy/code-shell-core 0.5.0-rc.2 → 0.6.0-rc.2

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 (387) hide show
  1. package/README.md +13 -10
  2. package/dist/agent/agent-definition-registry.d.ts +3 -0
  3. package/dist/agent/agent-definition-registry.js +3 -1
  4. package/dist/agent/agent-definition.d.ts +15 -0
  5. package/dist/agent/agent-definition.js +27 -3
  6. package/dist/arena/context/context-tools.js +47 -4
  7. package/dist/arena/ledger.js +9 -1
  8. package/dist/arena/strategies/utils.d.ts +1 -4
  9. package/dist/arena/strategies/utils.js +7 -84
  10. package/dist/automation/cron-expr.d.ts +10 -0
  11. package/dist/automation/cron-expr.js +23 -5
  12. package/dist/automation/runner.d.ts +21 -4
  13. package/dist/automation/runner.js +48 -12
  14. package/dist/automation/scheduler.d.ts +38 -0
  15. package/dist/automation/scheduler.js +89 -12
  16. package/dist/automation/store.js +5 -2
  17. package/dist/capability-control/disabled-lists.d.ts +25 -0
  18. package/dist/capability-control/disabled-lists.js +57 -0
  19. package/dist/capability-control/overlay.d.ts +15 -0
  20. package/dist/capability-control/overlay.js +27 -0
  21. package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
  22. package/dist/cc-orchestrator/agent-adapter.js +133 -0
  23. package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
  24. package/dist/cc-orchestrator/cc-capability.js +53 -0
  25. package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
  26. package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
  27. package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
  28. package/dist/cc-orchestrator/codex-session-history.js +187 -0
  29. package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
  30. package/dist/cc-orchestrator/external-agent-changes.js +214 -0
  31. package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
  32. package/dist/cc-orchestrator/external-agent-driver.js +69 -0
  33. package/dist/cc-orchestrator/index.d.ts +8 -0
  34. package/dist/cc-orchestrator/index.js +8 -0
  35. package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
  36. package/dist/cc-orchestrator/relevance-judge.js +29 -0
  37. package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
  38. package/dist/cc-orchestrator/session-discovery.js +125 -0
  39. package/dist/cc-orchestrator/session-history.d.ts +19 -0
  40. package/dist/cc-orchestrator/session-history.js +67 -0
  41. package/dist/cli/agent-server-stdio.d.ts +15 -1
  42. package/dist/cli/agent-server-stdio.js +115 -17
  43. package/dist/cli/agent-server-tcp.js +40 -26
  44. package/dist/context/compaction.d.ts +56 -0
  45. package/dist/context/compaction.js +186 -0
  46. package/dist/context/manager.js +25 -4
  47. package/dist/cost-tracker.js +5 -61
  48. package/dist/credentials/cipher.d.ts +49 -0
  49. package/dist/credentials/cipher.js +45 -0
  50. package/dist/credentials/cookie-jar.d.ts +24 -0
  51. package/dist/credentials/cookie-jar.js +40 -0
  52. package/dist/credentials/index.d.ts +7 -0
  53. package/dist/credentials/index.js +5 -0
  54. package/dist/credentials/inject-credential-tool.d.ts +20 -0
  55. package/dist/credentials/inject-credential-tool.js +130 -0
  56. package/dist/credentials/store.d.ts +72 -0
  57. package/dist/credentials/store.js +184 -0
  58. package/dist/credentials/types.d.ts +56 -0
  59. package/dist/credentials/use-credential-tool.d.ts +29 -0
  60. package/dist/credentials/use-credential-tool.js +205 -0
  61. package/dist/credentials/use-gate.d.ts +56 -0
  62. package/dist/credentials/use-gate.js +52 -0
  63. package/dist/data/model-metadata.d.ts +77 -0
  64. package/dist/data/model-metadata.js +56 -0
  65. package/dist/data/model-metadata.json +216 -0
  66. package/dist/data/openrouter-models.d.ts +18 -7
  67. package/dist/data/openrouter-models.js +35 -8
  68. package/dist/engine/aux-key.d.ts +10 -0
  69. package/dist/engine/aux-key.js +11 -0
  70. package/dist/engine/dynamic-tool-defs.d.ts +19 -0
  71. package/dist/engine/dynamic-tool-defs.js +36 -0
  72. package/dist/engine/engine.d.ts +267 -138
  73. package/dist/engine/engine.js +957 -191
  74. package/dist/engine/friendly-error.d.ts +18 -0
  75. package/dist/engine/friendly-error.js +63 -0
  76. package/dist/engine/goal.d.ts +145 -0
  77. package/dist/engine/goal.js +144 -0
  78. package/dist/engine/image-policy.d.ts +13 -0
  79. package/dist/engine/image-policy.js +24 -0
  80. package/dist/engine/model-connections-pool.d.ts +17 -0
  81. package/dist/engine/model-connections-pool.js +66 -0
  82. package/dist/engine/model-facade.d.ts +10 -0
  83. package/dist/engine/model-facade.js +25 -0
  84. package/dist/engine/patch-orphaned-tools.js +3 -0
  85. package/dist/engine/resolve-llm-config.d.ts +16 -0
  86. package/dist/engine/resolve-llm-config.js +44 -0
  87. package/dist/engine/runtime.js +2 -1
  88. package/dist/engine/sandbox-cache-key.d.ts +10 -0
  89. package/dist/engine/sandbox-cache-key.js +9 -0
  90. package/dist/engine/sandbox-config.d.ts +31 -0
  91. package/dist/engine/sandbox-config.js +38 -0
  92. package/dist/engine/session-usage.d.ts +19 -0
  93. package/dist/engine/session-usage.js +25 -0
  94. package/dist/engine/steer-queue.d.ts +32 -0
  95. package/dist/engine/steer-queue.js +26 -0
  96. package/dist/engine/streaming-tool-queue.d.ts +12 -0
  97. package/dist/engine/streaming-tool-queue.js +50 -10
  98. package/dist/engine/turn-loop.d.ts +67 -3
  99. package/dist/engine/turn-loop.js +292 -41
  100. package/dist/engine/types.d.ts +175 -0
  101. package/dist/engine/types.js +13 -0
  102. package/dist/external-agents/config.d.ts +2 -0
  103. package/dist/external-agents/config.js +15 -0
  104. package/dist/external-agents/types.d.ts +31 -0
  105. package/dist/external-agents/types.js +1 -0
  106. package/dist/git/utils.js +11 -6
  107. package/dist/git/worktree.d.ts +48 -0
  108. package/dist/git/worktree.js +86 -10
  109. package/dist/hooks/goal-stop-hook.d.ts +28 -0
  110. package/dist/hooks/goal-stop-hook.js +186 -9
  111. package/dist/hooks/registry.d.ts +8 -0
  112. package/dist/hooks/registry.js +19 -0
  113. package/dist/hooks/shell-runner.js +10 -21
  114. package/dist/index.d.ts +49 -6
  115. package/dist/index.js +60 -4
  116. package/dist/llm/capabilities/param-specs.d.ts +14 -0
  117. package/dist/llm/capabilities/param-specs.js +62 -0
  118. package/dist/llm/capabilities/rules.js +4 -0
  119. package/dist/llm/capabilities/types.d.ts +10 -0
  120. package/dist/llm/client-base.d.ts +28 -1
  121. package/dist/llm/client-base.js +119 -13
  122. package/dist/llm/model-cache.js +4 -2
  123. package/dist/llm/model-pool.d.ts +14 -0
  124. package/dist/llm/model-pool.js +13 -0
  125. package/dist/llm/provider-auth.d.ts +41 -0
  126. package/dist/llm/provider-auth.js +76 -0
  127. package/dist/llm/provider-catalog.d.ts +4 -0
  128. package/dist/llm/providers/anthropic.js +64 -14
  129. package/dist/llm/providers/openai.d.ts +36 -0
  130. package/dist/llm/providers/openai.js +257 -51
  131. package/dist/llm/reasoning-setting.d.ts +3 -3
  132. package/dist/llm/reasoning-setting.js +9 -1
  133. package/dist/llm/stream-watchdog.js +5 -1
  134. package/dist/llm/token-counter.js +9 -2
  135. package/dist/llm/types.d.ts +4 -0
  136. package/dist/logging/sanitize-messages.js +22 -0
  137. package/dist/lsp/manager.d.ts +1 -1
  138. package/dist/lsp/manager.js +40 -10
  139. package/dist/model-catalog/builtin.d.ts +12 -0
  140. package/dist/model-catalog/builtin.js +407 -0
  141. package/dist/model-catalog/gen-connections.d.ts +20 -0
  142. package/dist/model-catalog/gen-connections.js +28 -0
  143. package/dist/model-catalog/index.d.ts +41 -0
  144. package/dist/model-catalog/index.js +90 -0
  145. package/dist/model-catalog/params.d.ts +20 -0
  146. package/dist/model-catalog/params.js +45 -0
  147. package/dist/model-catalog/resolve.d.ts +48 -0
  148. package/dist/model-catalog/resolve.js +33 -0
  149. package/dist/model-catalog/save-entry.d.ts +32 -0
  150. package/dist/model-catalog/save-entry.js +104 -0
  151. package/dist/model-catalog/types.d.ts +561 -0
  152. package/dist/model-catalog/types.js +93 -0
  153. package/dist/model-catalog/upsert.d.ts +9 -0
  154. package/dist/model-catalog/upsert.js +8 -0
  155. package/dist/onboarding.d.ts +12 -82
  156. package/dist/onboarding.js +61 -326
  157. package/dist/plugins/gitOps.d.ts +19 -0
  158. package/dist/plugins/gitOps.js +73 -4
  159. package/dist/plugins/installer/checkUpdate.d.ts +16 -0
  160. package/dist/plugins/installer/checkUpdate.js +32 -0
  161. package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
  162. package/dist/plugins/installer/codex/convertCommands.js +46 -0
  163. package/dist/plugins/installer/codex/convertMcp.d.ts +5 -2
  164. package/dist/plugins/installer/codex/convertMcp.js +45 -5
  165. package/dist/plugins/installer/install.js +25 -1
  166. package/dist/plugins/installer/installFromArchive.d.ts +43 -0
  167. package/dist/plugins/installer/installFromArchive.js +134 -0
  168. package/dist/plugins/installer/installFromSource.js +8 -2
  169. package/dist/plugins/installer/loadPluginAgents.js +6 -2
  170. package/dist/plugins/installer/loadPluginMcp.d.ts +9 -2
  171. package/dist/plugins/installer/loadPluginMcp.js +35 -2
  172. package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
  173. package/dist/plugins/installer/pruneDisabled.js +73 -0
  174. package/dist/plugins/installer/types.d.ts +5 -2
  175. package/dist/plugins/installer/types.js +1 -0
  176. package/dist/plugins/installer/uninstall.js +4 -0
  177. package/dist/plugins/installer/unzip.d.ts +14 -0
  178. package/dist/plugins/installer/unzip.js +82 -0
  179. package/dist/plugins/installer/update.d.ts +14 -0
  180. package/dist/plugins/installer/update.js +53 -21
  181. package/dist/plugins/loadPluginHooks.d.ts +47 -1
  182. package/dist/plugins/loadPluginHooks.js +73 -1
  183. package/dist/plugins/marketplaceManager.d.ts +7 -0
  184. package/dist/plugins/marketplaceManager.js +20 -0
  185. package/dist/plugins/pluginCommandHook.js +4 -18
  186. package/dist/plugins/pluginContent.d.ts +30 -0
  187. package/dist/plugins/pluginContent.js +83 -0
  188. package/dist/plugins/pluginInstaller.d.ts +13 -0
  189. package/dist/plugins/pluginInstaller.js +42 -8
  190. package/dist/plugins/schemas.js +1 -0
  191. package/dist/plugins/types.d.ts +6 -0
  192. package/dist/preset/index.d.ts +7 -1
  193. package/dist/preset/index.js +96 -6
  194. package/dist/product/types.d.ts +1 -1
  195. package/dist/prompt/composer.d.ts +27 -0
  196. package/dist/prompt/composer.js +67 -21
  197. package/dist/prompt/instruction-scanner.js +5 -3
  198. package/dist/prompt/section-loader.js +1 -0
  199. package/dist/prompt/sections/base.md +1 -0
  200. package/dist/prompt/sections/browser.md +10 -0
  201. package/dist/prompt/sections/coding.md +4 -0
  202. package/dist/protocol/chat-session-manager.d.ts +8 -2
  203. package/dist/protocol/chat-session-manager.js +36 -0
  204. package/dist/protocol/chat-session.d.ts +65 -0
  205. package/dist/protocol/chat-session.js +94 -2
  206. package/dist/protocol/client.d.ts +27 -0
  207. package/dist/protocol/client.js +38 -0
  208. package/dist/protocol/server.d.ts +112 -0
  209. package/dist/protocol/server.js +586 -16
  210. package/dist/protocol/transport.js +3 -2
  211. package/dist/protocol/types.d.ts +46 -0
  212. package/dist/protocol/types.js +16 -0
  213. package/dist/quota/credentials.d.ts +3 -0
  214. package/dist/quota/credentials.js +80 -0
  215. package/dist/quota/index.d.ts +36 -0
  216. package/dist/quota/index.js +155 -0
  217. package/dist/quota/types.d.ts +48 -0
  218. package/dist/quota/types.js +13 -0
  219. package/dist/review/review-prompt.d.ts +28 -0
  220. package/dist/review/review-prompt.js +81 -0
  221. package/dist/run/FileRunStore.js +8 -3
  222. package/dist/run/RunApprovalBackend.js +25 -5
  223. package/dist/run/RunManager.d.ts +12 -0
  224. package/dist/run/RunManager.js +35 -0
  225. package/dist/run/factory.d.ts +1 -1
  226. package/dist/runtime/background-shell.d.ts +139 -0
  227. package/dist/runtime/background-shell.js +495 -0
  228. package/dist/runtime/output-clean.d.ts +24 -0
  229. package/dist/runtime/output-clean.js +41 -0
  230. package/dist/runtime/ring-file.d.ts +64 -0
  231. package/dist/runtime/ring-file.js +174 -0
  232. package/dist/runtime/safe-spawn.js +15 -34
  233. package/dist/runtime/spawn-common.d.ts +148 -0
  234. package/dist/runtime/spawn-common.js +303 -0
  235. package/dist/runtime/truncate-output.d.ts +22 -0
  236. package/dist/runtime/truncate-output.js +49 -0
  237. package/dist/runtime/utf8-cut.d.ts +11 -0
  238. package/dist/runtime/utf8-cut.js +23 -0
  239. package/dist/services/auto-dream.d.ts +4 -0
  240. package/dist/services/auto-dream.js +26 -26
  241. package/dist/services/diagnostics.d.ts +1 -2
  242. package/dist/services/diagnostics.js +12 -7
  243. package/dist/services/extract-memories.d.ts +14 -1
  244. package/dist/services/extract-memories.js +45 -6
  245. package/dist/services/memory-orchestrator.d.ts +21 -0
  246. package/dist/services/memory-orchestrator.js +125 -50
  247. package/dist/services/session-memory.js +24 -12
  248. package/dist/session/file-history.d.ts +124 -1
  249. package/dist/session/file-history.js +222 -6
  250. package/dist/session/memory.d.ts +116 -2
  251. package/dist/session/memory.js +250 -28
  252. package/dist/session/session-manager.d.ts +40 -0
  253. package/dist/session/session-manager.js +113 -3
  254. package/dist/session/simple-diff.d.ts +23 -0
  255. package/dist/session/simple-diff.js +84 -0
  256. package/dist/session/transcript.d.ts +25 -1
  257. package/dist/session/transcript.js +35 -2
  258. package/dist/session/undo-target.d.ts +67 -0
  259. package/dist/session/undo-target.js +144 -0
  260. package/dist/settings/disk-defaults.d.ts +9 -2
  261. package/dist/settings/disk-defaults.js +11 -2
  262. package/dist/settings/feature-flags.d.ts +64 -0
  263. package/dist/settings/feature-flags.js +61 -0
  264. package/dist/settings/manager.d.ts +68 -1
  265. package/dist/settings/manager.js +266 -29
  266. package/dist/settings/migrate-config.d.ts +45 -0
  267. package/dist/settings/migrate-config.js +125 -0
  268. package/dist/settings/schema-export.d.ts +25 -0
  269. package/dist/settings/schema-export.js +38 -0
  270. package/dist/settings/schema.d.ts +1186 -771
  271. package/dist/settings/schema.js +268 -97
  272. package/dist/skills/scanner.d.ts +9 -0
  273. package/dist/skills/scanner.js +30 -2
  274. package/dist/stt/resolve-transcribe.d.ts +31 -0
  275. package/dist/stt/resolve-transcribe.js +108 -0
  276. package/dist/stt/transcribe.d.ts +51 -0
  277. package/dist/stt/transcribe.js +65 -0
  278. package/dist/tool-system/browser-bridge.d.ts +226 -0
  279. package/dist/tool-system/browser-bridge.js +163 -0
  280. package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
  281. package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
  282. package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
  283. package/dist/tool-system/builtin/agent-notifications.js +9 -3
  284. package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
  285. package/dist/tool-system/builtin/agent-output-file.js +72 -0
  286. package/dist/tool-system/builtin/agent-registry.d.ts +12 -0
  287. package/dist/tool-system/builtin/agent-registry.js +8 -0
  288. package/dist/tool-system/builtin/agent.d.ts +21 -1
  289. package/dist/tool-system/builtin/agent.js +489 -42
  290. package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
  291. package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
  292. package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
  293. package/dist/tool-system/builtin/apply-patch/index.js +0 -15
  294. package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
  295. package/dist/tool-system/builtin/background-jobs.js +124 -0
  296. package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
  297. package/dist/tool-system/builtin/background-shell-tools.js +108 -0
  298. package/dist/tool-system/builtin/background-work.d.ts +67 -0
  299. package/dist/tool-system/builtin/background-work.js +86 -0
  300. package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
  301. package/dist/tool-system/builtin/bash-output-style.js +40 -0
  302. package/dist/tool-system/builtin/bash.d.ts +5 -2
  303. package/dist/tool-system/builtin/bash.js +98 -62
  304. package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
  305. package/dist/tool-system/builtin/browser-tools.js +312 -0
  306. package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
  307. package/dist/tool-system/builtin/cancel-goal.js +64 -0
  308. package/dist/tool-system/builtin/check-quota.d.ts +15 -0
  309. package/dist/tool-system/builtin/check-quota.js +34 -0
  310. package/dist/tool-system/builtin/config.js +7 -0
  311. package/dist/tool-system/builtin/cron.d.ts +7 -0
  312. package/dist/tool-system/builtin/cron.js +55 -3
  313. package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
  314. package/dist/tool-system/builtin/drive-claude-code.js +157 -0
  315. package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
  316. package/dist/tool-system/builtin/edit-model-catalog.js +94 -0
  317. package/dist/tool-system/builtin/edit.js +20 -16
  318. package/dist/tool-system/builtin/eol.d.ts +29 -0
  319. package/dist/tool-system/builtin/eol.js +37 -0
  320. package/dist/tool-system/builtin/file-cache.d.ts +6 -0
  321. package/dist/tool-system/builtin/file-cache.js +8 -0
  322. package/dist/tool-system/builtin/generate-image.d.ts +35 -0
  323. package/dist/tool-system/builtin/generate-image.js +278 -50
  324. package/dist/tool-system/builtin/generate-video.d.ts +55 -0
  325. package/dist/tool-system/builtin/generate-video.js +361 -0
  326. package/dist/tool-system/builtin/glob.js +0 -7
  327. package/dist/tool-system/builtin/grep.js +6 -8
  328. package/dist/tool-system/builtin/image-providers.d.ts +86 -0
  329. package/dist/tool-system/builtin/image-providers.js +190 -0
  330. package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
  331. package/dist/tool-system/builtin/image-uploader.js +74 -0
  332. package/dist/tool-system/builtin/index.d.ts +7 -1
  333. package/dist/tool-system/builtin/index.js +249 -25
  334. package/dist/tool-system/builtin/mcp-tools.js +23 -3
  335. package/dist/tool-system/builtin/memory.js +45 -7
  336. package/dist/tool-system/builtin/notebook-edit.js +0 -7
  337. package/dist/tool-system/builtin/powershell.js +4 -1
  338. package/dist/tool-system/builtin/read.js +10 -10
  339. package/dist/tool-system/builtin/repl.js +4 -1
  340. package/dist/tool-system/builtin/skill.js +9 -0
  341. package/dist/tool-system/builtin/sleep.js +3 -2
  342. package/dist/tool-system/builtin/tool-search.js +25 -7
  343. package/dist/tool-system/builtin/video-providers.d.ts +154 -0
  344. package/dist/tool-system/builtin/video-providers.js +235 -0
  345. package/dist/tool-system/builtin/web-fetch.js +12 -2
  346. package/dist/tool-system/builtin/web-search.js +21 -7
  347. package/dist/tool-system/builtin/worktree.d.ts +2 -1
  348. package/dist/tool-system/builtin/worktree.js +31 -4
  349. package/dist/tool-system/builtin/write.js +0 -6
  350. package/dist/tool-system/context.d.ts +154 -6
  351. package/dist/tool-system/executor.d.ts +3 -1
  352. package/dist/tool-system/executor.js +172 -88
  353. package/dist/tool-system/investigation-guard.js +1 -1
  354. package/dist/tool-system/mcp-manager.d.ts +42 -4
  355. package/dist/tool-system/mcp-manager.js +186 -20
  356. package/dist/tool-system/path-policy.d.ts +5 -0
  357. package/dist/tool-system/path-policy.js +307 -8
  358. package/dist/tool-system/permission.d.ts +36 -1
  359. package/dist/tool-system/permission.js +281 -69
  360. package/dist/tool-system/plan-mode-allowlist.d.ts +13 -2
  361. package/dist/tool-system/plan-mode-allowlist.js +24 -2
  362. package/dist/tool-system/registry.d.ts +1 -0
  363. package/dist/tool-system/registry.js +16 -2
  364. package/dist/tool-system/sandbox/index.d.ts +8 -0
  365. package/dist/tool-system/sandbox/index.js +7 -2
  366. package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
  367. package/dist/tool-system/validate-tool-metadata.js +63 -0
  368. package/dist/types.d.ts +217 -6
  369. package/dist/updater.js +20 -9
  370. package/dist/utils/envUtils.d.ts +0 -9
  371. package/dist/utils/envUtils.js +3 -28
  372. package/dist/utils/exec.d.ts +40 -0
  373. package/dist/utils/exec.js +144 -0
  374. package/dist/utils/json.d.ts +12 -0
  375. package/dist/utils/json.js +92 -0
  376. package/dist/utils/theme.d.ts +1 -1
  377. package/dist/utils/theme.js +1 -1
  378. package/dist/utils/toolDisplay.js +0 -1
  379. package/package.json +13 -7
  380. package/dist/agent/coordinator.d.ts +0 -49
  381. package/dist/agent/coordinator.js +0 -77
  382. package/dist/settings/manager.test.js +0 -73
  383. package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
  384. package/dist/tool-system/builtin/remote-trigger.js +0 -54
  385. package/dist/tool-system/builtin/send-message.d.ts +0 -6
  386. package/dist/tool-system/builtin/send-message.js +0 -47
  387. /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
@@ -7,6 +7,8 @@
7
7
  * Supports onStream for real-time output passthrough.
8
8
  */
9
9
  import { asyncAgentRegistry, MAX_BACKGROUND_AGENTS } from "./agent-registry.js";
10
+ import { ensureAgentHeartbeat } from "./agent-heartbeat.js";
11
+ import { writeAgentOutputFile } from "./agent-output-file.js";
10
12
  import { createTranscriptTranslator } from "./agent-transcript-translator.js";
11
13
  import { notificationQueue } from "./agent-notifications.js";
12
14
  import { nanoid } from "nanoid";
@@ -66,9 +68,32 @@ export function resolveAgentTypeOverrides(agentType, registry) {
66
68
  model: def.model,
67
69
  maxTurns: def.maxTurns,
68
70
  toolAllowlist: def.tools,
71
+ skillAllowlist: namespacePluginSkills(def.skills, def.source, def.pluginName),
69
72
  appendSystemPrompt: def.systemPrompt,
70
73
  };
71
74
  }
75
+ /**
76
+ * A plugin-bundled agent's frontmatter lists its own skills by BARE name
77
+ * (`skills: director-skill`), matching Claude Code's convention where a
78
+ * plugin agent references same-plugin skills unqualified. But the skill
79
+ * scanner registers plugin skills under their NAMESPACED name
80
+ * (`mimi-video:director-skill`), and the allowlist filter is an exact match —
81
+ * so a bare entry never matches and the sub-agent reports the skill "not
82
+ * found", forcing it to fall back to reading SKILL.md by hand.
83
+ *
84
+ * Bridge the two conventions: for a plugin-source agent, rewrite each bare
85
+ * skill name to `<pluginName>:<skill>`. Names that already carry a namespace
86
+ * (contain ":") are left untouched, so an agent may still reference another
87
+ * plugin's skill explicitly. undefined (inherit full pool) and [] (no skills)
88
+ * pass through unchanged. Non-plugin agents are never rewritten.
89
+ */
90
+ export function namespacePluginSkills(skills, source, pluginName) {
91
+ if (skills === undefined)
92
+ return undefined;
93
+ if (source !== "plugin" || !pluginName)
94
+ return skills;
95
+ return skills.map((s) => (s.includes(":") ? s : `${pluginName}:${s}`));
96
+ }
72
97
  /**
73
98
  * Render the "Available agent types" block injected into the Agent tool's
74
99
  * description, listing the roles defined in .code-shell/agents/*.md so the
@@ -84,7 +109,12 @@ export function buildAgentTypesBlock(registry) {
84
109
  return "";
85
110
  const lines = defs.map((d) => {
86
111
  const tools = d.tools && d.tools.length > 0 ? d.tools.join(", ") : "all parent tools";
87
- return `- ${d.name}: ${d.description} (tools: ${tools})`;
112
+ // Only surface skills when the role restricts them — most roles inherit
113
+ // the full pool and listing "all skills" everywhere is noise.
114
+ const skillsNote = d.skills !== undefined
115
+ ? `; skills: ${d.skills.length > 0 ? d.skills.join(", ") : "none"}`
116
+ : "";
117
+ return `- ${d.name}: ${d.description} (tools: ${tools}${skillsNote})`;
88
118
  });
89
119
  return [
90
120
  "",
@@ -101,9 +131,28 @@ export function buildAgentTypesBlock(registry) {
101
131
  */
102
132
  export function agentToolDefWithTypes(registry) {
103
133
  const block = buildAgentTypesBlock(registry);
104
- if (!block)
134
+ const names = registry?.list().map((d) => d.name) ?? [];
135
+ if (!block && names.length === 0)
105
136
  return agentToolDef;
106
- return { ...agentToolDef, description: agentToolDef.description + "\n" + block };
137
+ // Constrain agent_type to the loaded kind names so the model can't invent a
138
+ // role that doesn't exist (resolveAgentTypeOverrides would throw, wasting a
139
+ // turn). A free string still slipped through before; an enum surfaces the
140
+ // valid set directly in the schema the model sees. We rebuild the property
141
+ // (not mutate the shared const) so each engine's registry stays isolated.
142
+ const baseProps = agentToolDef.inputSchema.properties ?? {};
143
+ const baseAgentType = baseProps.agent_type ?? {};
144
+ const inputSchema = {
145
+ ...agentToolDef.inputSchema,
146
+ properties: {
147
+ ...baseProps,
148
+ agent_type: names.length > 0 ? { ...baseAgentType, enum: names } : baseAgentType,
149
+ },
150
+ };
151
+ return {
152
+ ...agentToolDef,
153
+ description: block ? agentToolDef.description + "\n" + block : agentToolDef.description,
154
+ inputSchema,
155
+ };
107
156
  }
108
157
  /**
109
158
  * Emit a sub-agent lifecycle event via the existing `notification` hook,
@@ -132,7 +181,10 @@ export const agentToolDef = {
132
181
  "You can optionally run an agent in the background using the run_in_background " +
133
182
  "parameter. When it runs in the background, you will be automatically notified when " +
134
183
  "it completes — do NOT sleep, poll, or proactively check on its progress. Continue " +
135
- "with other work or respond to the user instead.",
184
+ "with other work or respond to the user instead. " +
185
+ "Even a synchronous agent that runs longer than ~2 minutes is automatically moved to " +
186
+ "the background and notifies you on completion the same way — so a long delegation never " +
187
+ "stalls you; just continue or end your turn when told it has moved to the background.",
136
188
  inputSchema: {
137
189
  type: "object",
138
190
  properties: {
@@ -175,6 +227,20 @@ export const agentToolDef = {
175
227
  required: ["description", "prompt"],
176
228
  },
177
229
  };
230
+ /**
231
+ * Auto-background threshold (ms): a synchronous agent still running after this
232
+ * long is detached into the background (not killed) so the main turn isn't
233
+ * blocked for up to the 30min tool cap (TODO 4.1). Default 120s, matching
234
+ * Claude Code; overridable via CODE_SHELL_AGENT_BG_MS for tests. The agent
235
+ * keeps running on the same signal — we just stop awaiting it inline and let it
236
+ * notify on finish. Shared by the first-spawn (agentTool) and continuation
237
+ * (agentSendInputTool) paths.
238
+ */
239
+ function resolveAutoBgMs() {
240
+ const raw = process.env.CODE_SHELL_AGENT_BG_MS;
241
+ const n = raw !== undefined ? Number(raw) : NaN;
242
+ return Number.isFinite(n) && n >= 0 ? n : 120_000;
243
+ }
178
244
  /**
179
245
  * Build and run a sub-agent. Returns the produced text or throws.
180
246
  * Used by both the synchronous and background paths.
@@ -203,7 +269,7 @@ streamOverride) {
203
269
  // that no longer exists. The child Engine is a fresh instance; plan-mode
204
270
  // isolation between parent and child is enforced via separate Engine
205
271
  // instances (finalized in T6).
206
- const text = await spawner.spawn({ ...opts, streamOverride });
272
+ const { text } = await spawner.spawn({ ...opts, streamOverride });
207
273
  const finalText = text || `Agent completed but produced no text output.`;
208
274
  safeEmit(startEndSink, { type: "agent_end", agentId, name, description, text: finalText, agentType });
209
275
  emitSubAgentHook(opts.hooks, "subagent_finish", { agentId, description, text: finalText });
@@ -213,7 +279,9 @@ export async function agentTool(args, ctx) {
213
279
  const prompt = args.prompt;
214
280
  const description = args.description || "sub-agent";
215
281
  const rawName = args.name?.trim();
216
- const name = rawName && rawName.length > 0 ? rawName : undefined;
282
+ // Resolved below: when the model omits `name`, fall back to the resolved
283
+ // kind name so the dock shows a meaningful label instead of bare "Agent".
284
+ let name = rawName && rawName.length > 0 ? rawName : undefined;
217
285
  if (!prompt)
218
286
  return "Error: prompt is required";
219
287
  if (!ctx?.subAgentSpawner) {
@@ -240,10 +308,18 @@ export async function agentTool(args, ctx) {
240
308
  catch (err) {
241
309
  return `Error: ${err.message}`;
242
310
  }
311
+ // Dock label fallback: an omitted `name` defaults to the resolved kind
312
+ // (e.g. "explorer") so background agents are identifiable in the dock even
313
+ // when the model didn't pass an explicit label. True ephemeral agents
314
+ // (empty registry, no resolvedType) keep name undefined → dock shows "Agent".
315
+ if (name === undefined && overrides.resolvedType) {
316
+ name = overrides.resolvedType;
317
+ }
243
318
  const maxTurns = args.max_turns || overrides.maxTurns || 15;
244
319
  const runInBackground = args.run_in_background === true;
245
320
  const agentId = nanoid(8);
246
321
  const parentStream = spawner.parentStream;
322
+ const autoBgMs = resolveAutoBgMs();
247
323
  // ─── Background path ───────────────────────────────────────────
248
324
  // Register the agent, kick off execution detached from the current turn,
249
325
  // return immediately with the agent_id. Parent abort does NOT cascade
@@ -267,6 +343,8 @@ export async function agentTool(args, ctx) {
267
343
  startedAt: Date.now(),
268
344
  abort: () => controller.abort(),
269
345
  });
346
+ // B: liveness heartbeat for this explicit background agent too.
347
+ ensureAgentHeartbeat();
270
348
  // Translate this background agent's per-event stream into ChatEntry-
271
349
  // shaped rows inside its own transcript. The dock detail view reuses
272
350
  // App.renderEntry which only understands ChatEntry types — storing
@@ -290,6 +368,7 @@ export async function agentTool(args, ctx) {
290
368
  maxTurns,
291
369
  model: overrides.model,
292
370
  toolAllowlist: overrides.toolAllowlist,
371
+ skillAllowlist: overrides.skillAllowlist,
293
372
  appendSystemPrompt: overrides.appendSystemPrompt,
294
373
  readOnlySession: overrides.resolvedType === "researcher" || overrides.resolvedType === "explorer",
295
374
  hooks: ctx?.hooks,
@@ -298,6 +377,15 @@ export async function agentTool(args, ctx) {
298
377
  transcriptSink)
299
378
  .then((text) => {
300
379
  asyncAgentRegistry.markCompleted(agentId);
380
+ // External-readable copy of the result (tail / cross-session history).
381
+ // Best-effort; never blocks the completion path.
382
+ void writeAgentOutputFile(agentId, {
383
+ status: "completed",
384
+ body: text,
385
+ description,
386
+ name,
387
+ onError: (m, meta) => logger.warn(m, meta),
388
+ });
301
389
  // B2 / Gate 1: attribute completion to the session that spawned
302
390
  // this agent so concurrent sessions don't drain each other's
303
391
  // notifications. Engine.run() always populates ctx.sessionId; the
@@ -334,6 +422,19 @@ export async function agentTool(args, ctx) {
334
422
  });
335
423
  })
336
424
  .catch((err) => {
425
+ // runSubAgent emits its terminal agent_end only on the RESOLVE path
426
+ // (line ~344). On fail/cancel it threw before that, so the main-feed
427
+ // card started by agent_start is still 'working' — seal it here or the
428
+ // TUI spins forever. Emit once for BOTH branches (mirrors the sync→bg
429
+ // detach path ~885), then branch on abort for the notification only.
430
+ safeEmit(parentStream, {
431
+ type: "agent_end",
432
+ agentId,
433
+ name,
434
+ description,
435
+ error: err.message,
436
+ agentType: overrides.resolvedType,
437
+ });
337
438
  if (controller.signal.aborted) {
338
439
  // User-initiated cancel: mark status but do NOT enqueue —
339
440
  // the main agent doesn't need a follow-up turn. Dock still
@@ -348,6 +449,13 @@ export async function agentTool(args, ctx) {
348
449
  return;
349
450
  }
350
451
  asyncAgentRegistry.markFailed(agentId);
452
+ void writeAgentOutputFile(agentId, {
453
+ status: "failed",
454
+ body: err.message,
455
+ description,
456
+ name,
457
+ onError: (m, meta) => logger.warn(m, meta),
458
+ });
351
459
  if (ctx?.sessionId) {
352
460
  notificationQueue.enqueue({
353
461
  agentId,
@@ -383,38 +491,159 @@ export async function agentTool(args, ctx) {
383
491
  `If you need to stop it: AgentCancel(agent_id="${agentId}").`,
384
492
  ].join("\n");
385
493
  }
386
- // ─── Synchronous path ──────────────────────────────────────────
387
- // No wall-clock timeout on the sub-agent lifecycle — that matches Claude
388
- // Code / Codex, where a sub-agent (Task) is bounded by maxTurns + per-tool
389
- // timeouts (Bash etc. carry their own) + parent/user abort, NOT by a global
390
- // countdown that kills legitimate heavy work mid-task. The old
391
- // runWithTimeout(5min) both (a) murdered a "read 40 files" agent at 5:00 and
392
- // (b) raced with the normal completion path: the timeout aborted the child,
393
- // the catch emitted agent_end{error}, AND the aborted child still let
394
- // runSubAgent reach its line-280 agent_end{text} — two agent_end events for
395
- // one agent, the later text one overwriting the error so a timed-out agent
396
- // rendered as "done". Dropping the timeout removes the race entirely: a
397
- // genuine spawn error throws BEFORE the success emit, so only the catch
398
- // fires; a clean run emits agent_end{text} exactly once. (fix:
399
- // subagent-timeout-double-agent-end)
494
+ // ─── Synchronous path (with auto-background handoff) ────────────
495
+ return runSyncSubAgent({
496
+ spawner,
497
+ agentId,
498
+ name,
499
+ description,
500
+ prompt,
501
+ maxTurns,
502
+ overrides,
503
+ autoBgMs,
504
+ parentSignal,
505
+ parentStream,
506
+ ctx,
507
+ });
508
+ }
509
+ /**
510
+ * Record a finished SYNCHRONOUS sub-agent in the registry so AgentSendInput's
511
+ * fast path can later find it — its agentType (for role-scope re-resolution on
512
+ * resume) and dock label. Background agents already register on spawn; sync
513
+ * agents historically didn't (they returned text and were forgotten), which
514
+ * left continuation unable to recover the role's tool/skill constraints.
515
+ *
516
+ * This is a SILENT entry: status already `completed`, no notification enqueued,
517
+ * so it doesn't flash a dock card or wake the parent. It's pure lookup state.
518
+ * Keyed by agentId === childSid, so it agrees with the on-disk session that the
519
+ * cross-restart disk-probe finds. Skipped when the agent moved to the
520
+ * background (that path registers its own live entry) or was aborted.
521
+ */
522
+ function recordCompletedSyncAgent(opts) {
523
+ // Don't clobber a live/handed-off entry of the same id.
524
+ if (asyncAgentRegistry.get(opts.agentId))
525
+ return;
526
+ const now = Date.now();
527
+ asyncAgentRegistry.register({
528
+ agentId: opts.agentId,
529
+ name: opts.name,
530
+ agentType: opts.agentType,
531
+ description: opts.description,
532
+ sessionId: opts.parentSessionId,
533
+ // agent_id === childSid: the resumable session lives at sessions/<agentId>/.
534
+ childSessionId: opts.agentId,
535
+ status: "completed",
536
+ startedAt: now,
537
+ finishedAt: now,
538
+ // No fade window / no notification: this entry exists only so
539
+ // AgentSendInput can resolve the role on continuation, not to render.
540
+ abort: () => { },
541
+ });
542
+ }
543
+ /**
544
+ * The synchronous sub-agent run path, shared by `agentTool` (first spawn) and
545
+ * `agentSendInputTool` (resume/continuation). Races the run against the
546
+ * auto-background timer and hands off to the background registry if it runs
547
+ * long. `resumeSessionId`, when set, resumes an existing child session
548
+ * (transcript replay) instead of cold-starting.
549
+ *
550
+ * No wall-clock timeout on the lifecycle — matches Claude Code / Codex, where a
551
+ * sub-agent is bounded by maxTurns + per-tool timeouts + parent/user abort, NOT
552
+ * a global countdown that kills legitimate heavy work mid-task. (The old
553
+ * runWithTimeout(5min) murdered slow agents AND raced the completion path into
554
+ * a double agent_end; dropping it removed the race — fix
555
+ * subagent-timeout-double-agent-end.)
556
+ */
557
+ async function runSyncSubAgent(args) {
558
+ const { spawner, agentId, name, description, prompt, maxTurns, overrides, autoBgMs, parentSignal, parentStream, ctx, resumeSessionId, } = args;
400
559
  const syncController = new AbortController();
401
560
  const onParentAbort = () => syncController.abort();
402
561
  parentSignal?.addEventListener("abort", onParentAbort, { once: true });
562
+ // Per-call flag (NOT module-level): true once we've detached the agent into
563
+ // the background, so the finally block leaves the parent-abort listener with
564
+ // the background handlers instead of removing it.
565
+ let handedOff = false;
566
+ // Run the agent, but don't necessarily await it to completion: race the run
567
+ // against the auto-background timer. The run promise is shared between the
568
+ // inline await and (if we hand off) the background completion handlers, so
569
+ // the agent is never started twice and never killed by the handoff.
570
+ const runPromise = runSubAgent(spawner, {
571
+ agentId,
572
+ name,
573
+ description,
574
+ agentType: overrides.resolvedType,
575
+ prompt,
576
+ maxTurns,
577
+ model: overrides.model,
578
+ toolAllowlist: overrides.toolAllowlist,
579
+ skillAllowlist: overrides.skillAllowlist,
580
+ appendSystemPrompt: overrides.appendSystemPrompt,
581
+ readOnlySession: overrides.resolvedType === "researcher" || overrides.resolvedType === "explorer",
582
+ resumeSessionId,
583
+ hooks: ctx?.hooks,
584
+ signal: syncController.signal,
585
+ });
586
+ // Sentinel that the timer resolves with, so we can tell "agent finished" from
587
+ // "threshold elapsed" without rejecting either side.
588
+ const BG_HANDOFF = Symbol("bg-handoff");
589
+ let bgTimer;
590
+ const timerPromise = autoBgMs > 0 && autoBgMs !== Infinity
591
+ ? new Promise((resolve) => {
592
+ bgTimer = setTimeout(() => resolve(BG_HANDOFF), autoBgMs);
593
+ })
594
+ : null;
403
595
  try {
404
- return await runSubAgent(spawner, {
405
- agentId,
406
- name,
407
- description,
408
- agentType: overrides.resolvedType,
409
- prompt,
410
- maxTurns,
411
- model: overrides.model,
412
- toolAllowlist: overrides.toolAllowlist,
413
- appendSystemPrompt: overrides.appendSystemPrompt,
414
- readOnlySession: overrides.resolvedType === "researcher" || overrides.resolvedType === "explorer",
415
- hooks: ctx?.hooks,
416
- signal: syncController.signal,
417
- });
596
+ // If there's no timer (threshold 0/∞ disabled), just await as before.
597
+ const winner = timerPromise
598
+ ? await Promise.race([runPromise.then((t) => ({ ok: true, text: t })).catch((e) => ({ ok: false, err: e })), timerPromise])
599
+ : { ok: true, text: await runPromise };
600
+ if (winner === BG_HANDOFF) {
601
+ // Threshold elapsed while the agent is STILL running. Detach it into the
602
+ // background registry (it keeps executing on syncController.signal — we
603
+ // do NOT abort it) and wire its eventual completion into the
604
+ // notification queue, exactly like an explicit run_in_background agent.
605
+ handedOff = true;
606
+ handoffToBackground(runPromise, syncController, {
607
+ agentId,
608
+ name,
609
+ description,
610
+ agentType: overrides.resolvedType,
611
+ sessionId: ctx?.sessionId,
612
+ hooks: ctx?.hooks,
613
+ parentSignal,
614
+ onParentAbort,
615
+ // The agent_start fired in runSubAgent put the UI card into 'working'.
616
+ // runSubAgent's own agent_end (line ~299) only fires if spawn() RESOLVES;
617
+ // on failure/cancel it throws before that, so the card would hang
618
+ // 'working' forever. Hand the UI sink to the background completion
619
+ // handlers so they emit the terminal agent_end the card needs.
620
+ uiStream: parentStream,
621
+ });
622
+ return [
623
+ `Task is taking a while (>${Math.round(autoBgMs / 1000)}s) — moved it to the background so I'm not blocked.`,
624
+ `agent_id: ${agentId} (internal — do not show to user)`,
625
+ `description: ${description}`,
626
+ ``,
627
+ `It is still running and you will be notified automatically when it completes.`,
628
+ `Briefly tell the user it's running in the background and either continue with other work or end your response. Do not sleep or poll.`,
629
+ `If you need to stop it: AgentCancel(agent_id="${agentId}").`,
630
+ ].join("\n");
631
+ }
632
+ // Agent finished within the threshold. Record a silent completed entry so
633
+ // AgentSendInput can continue this sub-agent (recover its role scope). Both
634
+ // a first spawn and a resume land here; re-registering a resumed id is a
635
+ // no-op (recordCompletedSyncAgent skips when an entry already exists).
636
+ if (winner.ok) {
637
+ recordCompletedSyncAgent({
638
+ agentId,
639
+ name,
640
+ agentType: overrides.resolvedType,
641
+ description,
642
+ parentSessionId: ctx?.sessionId,
643
+ });
644
+ return winner.text;
645
+ }
646
+ throw winner.err;
418
647
  }
419
648
  catch (err) {
420
649
  emitSubAgentHook(ctx?.hooks, "subagent_error", { agentId, description, error: err.message });
@@ -424,32 +653,131 @@ export async function agentTool(args, ctx) {
424
653
  return `Agent error: ${err.message}`;
425
654
  }
426
655
  finally {
427
- parentSignal?.removeEventListener("abort", onParentAbort);
656
+ if (bgTimer)
657
+ clearTimeout(bgTimer);
658
+ // Only detach the parent-abort listener when we awaited to completion.
659
+ // On handoff, handoffToBackground already detached it (the agent is still
660
+ // running and now follows the background contract, not parent-turn abort).
661
+ if (!handedOff)
662
+ parentSignal?.removeEventListener("abort", onParentAbort);
428
663
  }
429
664
  }
665
+ /**
666
+ * Detach a still-running synchronous sub-agent into the background registry
667
+ * (TODO 4.1). The agent KEEPS running on its existing signal — we attach
668
+ * completion handlers that mirror the explicit run_in_background path so its
669
+ * result/error arrives via the notification queue and the registry shows it as
670
+ * a running (then completed/failed) background agent. Never aborts the agent.
671
+ */
672
+ function handoffToBackground(runPromise, controller, meta) {
673
+ const { agentId, name, description, agentType, sessionId, hooks, uiStream } = meta;
674
+ // The agent outlives the spawning turn now, so parent-turn abort must NOT
675
+ // cascade to it (same contract as an explicit background agent). Detach the
676
+ // parent listener; cancellation from here on goes through AgentCancel.
677
+ meta.parentSignal?.removeEventListener("abort", meta.onParentAbort);
678
+ asyncAgentRegistry.register({
679
+ agentId,
680
+ name,
681
+ agentType,
682
+ description,
683
+ sessionId,
684
+ status: "running",
685
+ startedAt: Date.now(),
686
+ abort: () => controller.abort(),
687
+ });
688
+ // Tell the UI this agent is now running in the BACKGROUND (still working,
689
+ // just detached from the parent turn). Without this the card's only signals
690
+ // are agent_start (looks foreground) and the eventual agent_end — so during
691
+ // the handoff→completion gap turn_complete's done-sweep marks it done and the
692
+ // card collapses / shows no "running". (A: background-agent-visibility)
693
+ safeEmit(uiStream, { type: "agent_backgrounded", agentId, name, description, agentType });
694
+ // B: keep a liveness heartbeat going while this (and any sibling) background
695
+ // agent runs, so the UI shows "alive" even through long LLM-request silence.
696
+ // Idempotent; self-stops when no agent remains.
697
+ ensureAgentHeartbeat();
698
+ runPromise
699
+ .then((text) => {
700
+ asyncAgentRegistry.markCompleted(agentId);
701
+ void writeAgentOutputFile(agentId, {
702
+ status: "completed",
703
+ body: text,
704
+ description,
705
+ name,
706
+ onError: (m, meta) => logger.warn(m, meta),
707
+ });
708
+ if (sessionId) {
709
+ notificationQueue.enqueue({ agentId, name, description, status: "completed", finalText: text, enqueuedAt: Date.now() }, sessionId);
710
+ }
711
+ else {
712
+ logger.warn("agent_completion_without_session", { agentId, name, status: "completed" });
713
+ }
714
+ void hooks?.emit("notification", { kind: "agent_completed", agentId, name, description, finalText: text });
715
+ })
716
+ .catch((err) => {
717
+ // runSubAgent threw before its success agent_end, so the UI card is still
718
+ // 'working'. Close it out with a terminal agent_end{error} (mirrors the
719
+ // synchronous catch at the agentTool level).
720
+ safeEmit(uiStream, { type: "agent_end", agentId, name, description, error: err.message, agentType });
721
+ if (controller.signal.aborted) {
722
+ asyncAgentRegistry.markCancelled(agentId);
723
+ void hooks?.emit("notification", { kind: "agent_cancelled", agentId, name, description });
724
+ return;
725
+ }
726
+ asyncAgentRegistry.markFailed(agentId);
727
+ void writeAgentOutputFile(agentId, {
728
+ status: "failed",
729
+ body: err.message,
730
+ description,
731
+ name,
732
+ onError: (m, meta) => logger.warn(m, meta),
733
+ });
734
+ if (sessionId) {
735
+ notificationQueue.enqueue({ agentId, name, description, status: "failed", error: err.message, enqueuedAt: Date.now() }, sessionId);
736
+ }
737
+ else {
738
+ logger.warn("agent_completion_without_session", { agentId, name, status: "failed" });
739
+ }
740
+ void hooks?.emit("notification", { kind: "agent_failed", agentId, name, description, error: err.message });
741
+ });
742
+ }
430
743
  // ─── AgentStatus / AgentCancel — companions to run_in_background ─
431
744
  export const agentStatusToolDef = {
432
745
  name: "AgentStatus",
433
746
  description: "Check the status of a background agent launched with Agent(run_in_background=true). " +
434
747
  "Returns running / completed / failed / cancelled, plus the result text once finished. " +
435
- "Omit agent_id to list all background agents in this process.",
748
+ "Omit agent_id to list this session's background agents (pass all=true for every session in the process). " +
749
+ "Results arrive automatically when an agent finishes — use this for an on-demand check, not to poll.",
436
750
  inputSchema: {
437
751
  type: "object",
438
752
  properties: {
439
753
  agent_id: {
440
754
  type: "string",
441
- description: "The agent_id returned by Agent(run_in_background=true). Omit to list all.",
755
+ description: "The agent_id returned by Agent(run_in_background=true). Omit to list.",
756
+ },
757
+ all: {
758
+ type: "boolean",
759
+ description: "When listing (no agent_id): true lists background agents from every session in this process; " +
760
+ "default (false) lists only the current session's.",
442
761
  },
443
762
  },
444
763
  },
445
764
  };
446
- export async function agentStatusTool(args) {
765
+ export async function agentStatusTool(args, ctx) {
447
766
  const agentId = args.agent_id;
448
767
  if (!agentId) {
449
- const all = asyncAgentRegistry.list();
450
- if (all.length === 0)
451
- return "No background agents in this process.";
452
- return all
768
+ const all = args.all === true;
769
+ // Default to the current session's agents so concurrent sessions don't
770
+ // leak into each other's listings; fall back to process-wide only when
771
+ // there's no session context or the caller explicitly asks for `all`.
772
+ const list = all || !ctx?.sessionId
773
+ ? asyncAgentRegistry.list()
774
+ : asyncAgentRegistry.listForSession(ctx.sessionId);
775
+ if (list.length === 0) {
776
+ return all || !ctx?.sessionId
777
+ ? "No background agents in this process."
778
+ : "No background agents in this session.";
779
+ }
780
+ return list
453
781
  .map((e) => {
454
782
  const dur = ((e.finishedAt ?? Date.now()) - e.startedAt) / 1000;
455
783
  return `${e.agentId} [${e.status}] ${e.description} (${dur.toFixed(1)}s)`;
@@ -510,3 +838,122 @@ export async function agentCancelTool(args) {
510
838
  ? `Agent ${agentId} cancelled.`
511
839
  : `Failed to cancel agent ${agentId}.`;
512
840
  }
841
+ // ─── AgentSendInput — continue a previously-spawned sub-agent ────
842
+ export const agentSendInputToolDef = {
843
+ name: "AgentSendInput",
844
+ description: "Continue a sub-agent you previously launched with Agent — send it a follow-up " +
845
+ "instruction that it answers WITH FULL MEMORY of its earlier work (its prior " +
846
+ "messages, tool calls, and results). Use this instead of spawning a fresh agent " +
847
+ "with a re-pasted context dump: it resumes the same conversation by replaying the " +
848
+ "agent's saved transcript, so nothing is lost and you don't re-send what it already " +
849
+ "knows.\n\n" +
850
+ "Typical use: you got a partial result or a review verdict from a sub-agent, and now " +
851
+ "you want it to revise / answer a follow-up / act on feedback. Pass only the " +
852
+ "incremental instruction.\n\n" +
853
+ "Pass the agent_id the Agent tool returned. Works for both synchronous and background " +
854
+ "agents, and even after a process restart (the transcript is persisted on disk). " +
855
+ "Returns the agent's new reply, synchronously (a long continuation auto-moves to the " +
856
+ "background and notifies you on completion, like Agent does).",
857
+ inputSchema: {
858
+ type: "object",
859
+ properties: {
860
+ agent_id: {
861
+ type: "string",
862
+ description: "The agent_id returned by a prior Agent(...) call.",
863
+ },
864
+ prompt: {
865
+ type: "string",
866
+ description: "The follow-up instruction. The agent already remembers its earlier work — " +
867
+ "send only what's new (feedback, the next step, a question).",
868
+ },
869
+ },
870
+ required: ["agent_id", "prompt"],
871
+ },
872
+ };
873
+ export async function agentSendInputTool(args, ctx) {
874
+ const agentId = args.agent_id?.trim();
875
+ const prompt = args.prompt;
876
+ if (!agentId)
877
+ return "Error: agent_id is required.";
878
+ if (!prompt)
879
+ return "Error: prompt is required.";
880
+ if (!ctx?.subAgentSpawner) {
881
+ return "Error: Agent tool is not configured (no subAgentSpawner in ctx).";
882
+ }
883
+ // Same flat-hierarchy rule as Agent: a sub-agent cannot continue (or spawn)
884
+ // another sub-agent. Layered with the NESTED_AGENT_TOOLS strip in engine.ts.
885
+ if (ctx.isSubAgent === true) {
886
+ return "Error: nested agents are not supported. Sub-agents cannot send input to other sub-agents. Complete the task directly using your available tools.";
887
+ }
888
+ const spawner = ctx.subAgentSpawner;
889
+ const parentSignal = args.__signal;
890
+ if (parentSignal?.aborted) {
891
+ return "Agent aborted before resuming.";
892
+ }
893
+ // Resolve the child session to resume. With the agent_id===childSid
894
+ // convention the child session is stored at sessions/<agent_id>/, so the
895
+ // agent_id IS the resume target. Confirm it's resumable:
896
+ // 1. in-memory registry (fast path, same process) — gives us the recorded
897
+ // childSessionId (== agentId) and the dock label/agentType;
898
+ // 2. on-disk probe (cross-restart) — the registry is gone but the session
899
+ // transcript persists.
900
+ // Both miss → the agent never existed or was cleaned up.
901
+ const entry = asyncAgentRegistry.get(agentId);
902
+ const resumeSessionId = entry?.childSessionId ?? agentId;
903
+ const onDisk = spawner.sessionExists?.(resumeSessionId) ?? false;
904
+ if (!entry && !onDisk) {
905
+ return (`Error: no resumable sub-agent with agent_id "${agentId}". ` +
906
+ `It may never have run or its session was cleaned up. ` +
907
+ `Re-spawn with Agent(...) and a complete task description.`);
908
+ }
909
+ // Concurrency guard: a sub-agent that's still running (e.g. it auto-moved to
910
+ // the background and hasn't finished) owns its child session. Resuming now
911
+ // would build a SECOND child Engine that resumes the SAME session and appends
912
+ // to the SAME transcript concurrently — sub-agents have no per-session
913
+ // `active` lock (the protocol layer's serialization only covers top-level
914
+ // sessions), so the two writers interleave and can corrupt the transcript.
915
+ // Refuse until it finishes (or is cancelled).
916
+ if (entry?.status === "running") {
917
+ return (`Error: sub-agent "${agentId}" is still running. ` +
918
+ `Wait for it to finish (poll with AgentStatus), or cancel it with ` +
919
+ `AgentCancel, before sending it more input.`);
920
+ }
921
+ const name = entry?.name;
922
+ const description = entry?.description ?? "sub-agent continuation";
923
+ // Reconstruct the role's overrides from the recorded agentType. The child
924
+ // Engine is built FRESH on every spawn (engine.ts spawn closure) — resuming a
925
+ // session replays its transcript but the new Engine's tool/skill SCOPE comes
926
+ // from req.toolAllowlist / req.skillAllowlist, NOT from the persisted session.
927
+ // So if we didn't re-resolve these, a restricted role (e.g. a read-only
928
+ // reviewer) would silently regain the parent's full tool set on continuation.
929
+ // Re-resolving from agentType keeps the resumed turn under the same
930
+ // tool/skill/model constraints as the original spawn. An ephemeral agent
931
+ // (no agentType) resolves to empty overrides → inherits parent scope, same as
932
+ // its first turn did.
933
+ let overrides;
934
+ try {
935
+ overrides = resolveAgentTypeOverrides(entry?.agentType, ctx?.agentDefinitions);
936
+ }
937
+ catch {
938
+ // Role no longer exists (renamed/removed since spawn). Fall back to a bare
939
+ // continuation rather than refusing — the transcript still carries the
940
+ // role's system prompt, and replaying it is more useful than an error.
941
+ overrides = { resolvedType: entry?.agentType };
942
+ }
943
+ const maxTurns = overrides.maxTurns ?? 15;
944
+ const autoBgMs = resolveAutoBgMs();
945
+ return runSyncSubAgent({
946
+ spawner,
947
+ agentId,
948
+ name,
949
+ description,
950
+ prompt,
951
+ maxTurns,
952
+ overrides,
953
+ autoBgMs,
954
+ parentSignal,
955
+ parentStream: spawner.parentStream,
956
+ ctx,
957
+ resumeSessionId,
958
+ });
959
+ }