@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
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  UI-agnostic agent orchestration framework. The headless core of [`code-shell`](https://github.com/cjhyy/codeshell): turn an LLM + a tool registry + a transcript into a multi-turn agent loop, with permissions, hooks, MCP support, and pluggable approval backends.
4
4
 
5
- Compatible with Anthropic and OpenAI-protocol providers (Claude, DeepSeek, GPT, Gemini, Qwen, …). No terminal UI dependencies — embed it in a CLI, a web service, a Slack bot, a desktop app.
5
+ Compatible with Anthropic and OpenAI-protocol providers (Claude, DeepSeek, GPT, Gemini, Qwen, …). No terminal UI dependencies — embed it in a CLI, a web service, a Slack bot, a desktop app. This is a Node.js runtime package, not a browser SDK.
6
6
 
7
7
  ## Install
8
8
 
@@ -12,7 +12,7 @@ npm install @cjhyy/code-shell-core
12
12
  bun add @cjhyy/code-shell-core
13
13
  ```
14
14
 
15
- Requires Node ≥ 20.10.
15
+ Requires Node ≥ 20.10 and an ESM-capable runtime.
16
16
 
17
17
  ## Quickstart
18
18
 
@@ -29,8 +29,10 @@ const engine = new Engine({
29
29
  apiKey: process.env.ANTHROPIC_API_KEY!,
30
30
  },
31
31
  cwd: process.cwd(),
32
- // Headless approval — "approve-all" trusts the model fully; use
33
- // "approve-read-only" or a custom ApprovalBackend in production.
32
+ // Headless approval — "approve-all" trusts the model fully and is
33
+ // only appropriate for local/trusted prompts. Use "approve-read-only",
34
+ // explicit permissionMode/tool allowlists, or a custom ApprovalBackend
35
+ // in production.
34
36
  approvalBackend: new HeadlessApprovalBackend("approve-all"),
35
37
  headless: true,
36
38
  });
@@ -78,7 +80,7 @@ new Engine({
78
80
 
79
81
  ## Designing for production
80
82
 
81
- - **Approval backend.** `HeadlessApprovalBackend("approve-all")` is fine for trusted prompts. For untrusted input, implement `ApprovalBackend` to gate destructive tools (Write/Edit/Bash) through your own auth.
83
+ - **Approval backend.** `HeadlessApprovalBackend("approve-all")` is fine only for local/trusted prompts. For untrusted input, explicitly set `permissionMode`, restrict tools with `enabledBuiltinTools` / `disabledBuiltinTools`, or implement `ApprovalBackend` to gate destructive tools (Write/Edit/Bash) through your own auth.
82
84
  - **Permission rules.** Pass `permissionRules` to the engine for fine-grained allow/deny per tool + args pattern. Bash commands additionally get a built-in safety classifier (dangerous commands like `rm -rf` always require explicit approval).
83
85
  - **Hooks** are the right place for audit logging, prompt rewriting, and policy enforcement.
84
86
  - **Memory.** The engine ships with an extract-memories + auto-dream pipeline that persists to `~/.code-shell/memory/`. Disable by passing a no-op `MemoryOrchestrator`, or scope to a project by setting `CODE_SHELL_HOME`.
@@ -144,11 +146,12 @@ These exports are covered by the stability promise:
144
146
 
145
147
  ### Treat as internal
146
148
 
147
- Anything not in the table above — including direct imports from
148
- `./engine/...`, `./tool-system/...`, `./session/...`, `./hooks/...`
149
- subpaths and APIs marked `@internal` in their docstrings may change
150
- between minor versions without notice. Plan to migrate via the protocol
151
- surface instead of deep-importing.
149
+ Anything not in the table above — including root-entry exports for
150
+ product/runtime helpers and direct imports from `./engine/...`,
151
+ `./tool-system/...`, `./session/...`, `./hooks/...` subpathsis
152
+ experimental/internal unless explicitly documented otherwise, and may
153
+ change between minor versions without notice. Plan to migrate via the
154
+ protocol surface instead of deep-importing.
152
155
 
153
156
  ## Stability
154
157
 
@@ -2,6 +2,9 @@ import { type AgentDefinition } from "./agent-definition.js";
2
2
  export interface AgentSourceDir {
3
3
  dir: string;
4
4
  source: "project" | "user" | "plugin";
5
+ /** For source === "plugin": the owning plugin name, carried onto each def
6
+ * so the spawn layer can namespace bare skill allowlists. */
7
+ pluginName?: string;
5
8
  }
6
9
  /**
7
10
  * Loads reusable sub-agent role definitions from one or more directories
@@ -24,7 +24,7 @@ export class AgentDefinitionRegistry {
24
24
  }
25
25
  static loadFromDirs(dirs, disabled) {
26
26
  const reg = new AgentDefinitionRegistry();
27
- for (const { dir, source } of dirs) {
27
+ for (const { dir, source, pluginName } of dirs) {
28
28
  if (!existsSync(dir) || !statSync(dir).isDirectory())
29
29
  continue;
30
30
  for (const entry of readdirSync(dir).sort()) {
@@ -36,6 +36,8 @@ export class AgentDefinitionRegistry {
36
36
  continue;
37
37
  const def = parseAgentDefinition(readFileSync(full, "utf8"), entry);
38
38
  def.source = source;
39
+ if (pluginName)
40
+ def.pluginName = pluginName;
39
41
  def.filePath = full;
40
42
  // A later dir overriding an earlier one. Carry forward the shadowed
41
43
  // sources (the prev def's own source + anything it already shadowed)
@@ -10,10 +10,25 @@ export interface AgentDefinition {
10
10
  maxTurns?: number;
11
11
  /** Optional tool allowlist. Undefined → inherit parent's full tool set. */
12
12
  tools?: string[];
13
+ /**
14
+ * Optional skill allowlist. Undefined → inherit the parent's full skill
15
+ * pool (every non-disabled skill in the project). When set, the sub-agent
16
+ * physically only sees these skills: they're the only ones listed in its
17
+ * system prompt and the only ones it may invoke. Empty array → no skills.
18
+ */
19
+ skills?: string[];
13
20
  /** Markdown body — becomes the child Engine's appendSystemPrompt. */
14
21
  systemPrompt: string;
15
22
  /** Where this def was loaded from. Runtime-only; never serialized. */
16
23
  source?: "project" | "user" | "plugin";
24
+ /**
25
+ * Owning plugin name when source === "plugin" (e.g. "mimi-video"). Runtime-
26
+ * only; never serialized. Used to namespace this agent's bare skill
27
+ * allowlist into `<pluginName>:<skill>` at spawn time, since a plugin's
28
+ * frontmatter references its own skills by bare name (CC convention) but
29
+ * the scanner registers them namespaced. Undefined for project/user agents.
30
+ */
31
+ pluginName?: string;
17
32
  /** Absolute path of the file it came from. Runtime-only. */
18
33
  filePath?: string;
19
34
  /** True when this def shadows a same-named def from a lower-priority dir. */
@@ -1,4 +1,23 @@
1
1
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
2
+ /**
3
+ * Normalize a frontmatter `tools:` / `skills:` value into a string[].
4
+ * Accepts both a YAML list (`[a, b]`) and a comma/whitespace-separated
5
+ * string (`a, b`) — CC-lineage agent files use either form. Returns
6
+ * undefined when the field is absent or not a usable shape, so the caller
7
+ * keeps the "inherit parent" default.
8
+ */
9
+ function normalizeNameList(value) {
10
+ if (Array.isArray(value)) {
11
+ return value.filter((t) => typeof t === "string");
12
+ }
13
+ if (typeof value === "string") {
14
+ return value
15
+ .split(/[,\s]+/)
16
+ .map((s) => s.trim())
17
+ .filter((s) => s.length > 0);
18
+ }
19
+ return undefined;
20
+ }
2
21
  /**
3
22
  * Parse a Markdown agent-definition file (YAML frontmatter + body).
4
23
  * Pure: no filesystem access. `sourceName` is only used in error messages.
@@ -31,9 +50,12 @@ export function parseAgentDefinition(raw, sourceName) {
31
50
  def.model = fm.model.trim();
32
51
  if (typeof fm.maxTurns === "number")
33
52
  def.maxTurns = fm.maxTurns;
34
- if (Array.isArray(fm.tools)) {
35
- def.tools = fm.tools.filter((t) => typeof t === "string");
36
- }
53
+ const tools = normalizeNameList(fm.tools);
54
+ if (tools !== undefined)
55
+ def.tools = tools;
56
+ const skills = normalizeNameList(fm.skills);
57
+ if (skills !== undefined)
58
+ def.skills = skills;
37
59
  return def;
38
60
  }
39
61
  /**
@@ -54,6 +76,8 @@ export function serializeAgentDefinition(def) {
54
76
  fm.maxTurns = def.maxTurns;
55
77
  if (def.tools !== undefined)
56
78
  fm.tools = def.tools;
79
+ if (def.skills !== undefined)
80
+ fm.skills = def.skills;
57
81
  const yaml = stringifyYaml(fm).trimEnd();
58
82
  return `---\n${yaml}\n---\n${def.systemPrompt}\n`;
59
83
  }
@@ -6,9 +6,9 @@
6
6
  * to prevent command injection. File paths are validated against the
7
7
  * repository boundary to prevent path traversal.
8
8
  */
9
- import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
9
+ import { readFileSync, existsSync, readdirSync, statSync, realpathSync } from "node:fs";
10
10
  import { execFileSync } from "node:child_process";
11
- import { resolve, join } from "node:path";
11
+ import { resolve, join, dirname, basename } from "node:path";
12
12
  import { isWithinRoot } from "./within-root.js";
13
13
  export const MAX_TOOL_RESULT = 15_000; // chars per tool call
14
14
  // 5 was too generous for thinking-mode models — they'd spend the
@@ -113,7 +113,41 @@ export function executeContextTool(tc) {
113
113
  */
114
114
  function validatePath(filePath) {
115
115
  const resolved = resolve(filePath);
116
- return isWithinRoot(REPO_ROOT, resolved) ? resolved : null;
116
+ // Realpath both sides so a symlink planted inside the repo that points OUTSIDE
117
+ // can't pass a lexical containment check (isWithinRoot assumes resolved input
118
+ // and won't catch a link escape on its own). Matches fs-service's discipline.
119
+ // Reads are read-only, but a contained-looking symlink would still leak an
120
+ // out-of-repo file into arena context. Fall back to the lexical check when the
121
+ // target doesn't exist yet (a read of a missing file fails downstream anyway).
122
+ let realRoot;
123
+ try {
124
+ realRoot = realpathSync(REPO_ROOT);
125
+ }
126
+ catch {
127
+ realRoot = REPO_ROOT;
128
+ }
129
+ let realTarget;
130
+ try {
131
+ realTarget = realpathSync(resolved);
132
+ }
133
+ catch {
134
+ // Target doesn't exist yet. Compare REAL-vs-REAL by resolving the existing
135
+ // parent dir's realpath and rejoining the missing leaf — comparing realRoot
136
+ // (a real path) against a lexical `resolved` would false-reject valid files
137
+ // whenever REPO_ROOT (or any ancestor) is reached through a symlink.
138
+ let realParent;
139
+ try {
140
+ realParent = realpathSync(dirname(resolved));
141
+ }
142
+ catch {
143
+ // Parent missing too — no realpath available; fall back to lexical-vs-lexical
144
+ // so both operands share the same (unresolved) mode.
145
+ return isWithinRoot(resolve(REPO_ROOT), resolved) ? resolved : null;
146
+ }
147
+ const candidate = join(realParent, basename(resolved));
148
+ return isWithinRoot(realRoot, candidate) ? candidate : null;
149
+ }
150
+ return isWithinRoot(realRoot, realTarget) ? realTarget : null;
117
151
  }
118
152
  function executeReadFile(args) {
119
153
  const filePath = args.path;
@@ -124,7 +158,16 @@ function executeReadFile(args) {
124
158
  const raw = readFileSync(filePath, "utf-8");
125
159
  const lines = raw.split("\n");
126
160
  const offset = Math.max(0, (args.offset ?? 1) - 1);
127
- const limit = args.limit ?? 200;
161
+ // Guard `limit` before slicing: a negative limit makes lines.slice count
162
+ // "from the end" and silently drop the file's tail (reader thinks the file is
163
+ // shorter). A missing/NaN limit falls back to the default; a non-positive
164
+ // limit reads nothing.
165
+ const rawLimit = args.limit;
166
+ const limit = rawLimit === undefined || !Number.isFinite(rawLimit)
167
+ ? 200
168
+ : rawLimit > 0
169
+ ? Math.floor(rawLimit)
170
+ : 0;
128
171
  const slice = lines.slice(offset, offset + limit);
129
172
  const numbered = slice.map((l, i) => `${offset + i + 1}\t${l}`).join("\n");
130
173
  return truncateResult(numbered);
@@ -68,11 +68,16 @@ export class ArenaLedger {
68
68
  }
69
69
  appendChallenge(challenge) {
70
70
  this.ledger.challenges.push(challenge);
71
- // Also add to the claim's challenges array
71
+ // Also add to the claim's challenges array. If the claimId doesn't resolve
72
+ // the challenge still lives in ledger.challenges but is invisible to any
73
+ // code reading claim.challenges directly — surface that inconsistency.
72
74
  const claim = this.index.claimsById.get(challenge.claimId);
73
75
  if (claim) {
74
76
  claim.challenges.push(challenge);
75
77
  }
78
+ else {
79
+ logger.warn("arena.challenge_for_unknown_claim", { claimId: challenge.claimId });
80
+ }
76
81
  this.checkGrowth();
77
82
  // Register any requested checks
78
83
  if (challenge.requestedChecks) {
@@ -94,6 +99,9 @@ export class ArenaLedger {
94
99
  if (claim) {
95
100
  claim.adjudication = adjudication;
96
101
  }
102
+ else {
103
+ logger.warn("arena.adjudication_for_unknown_claim", { claimId: adjudication.claimId });
104
+ }
97
105
  }
98
106
  // ─── Query operations ───────────────────────────────────────
99
107
  /**
@@ -1,9 +1,6 @@
1
1
  /** Shared utilities for arena strategy implementations. */
2
2
  import type { ArenaBaseContext, FindingReview, ParticipantReport, ArenaConsensus, ArenaRoadmapPhaseDetail, ClaimRecord, ClaimChallenge, ClaimAdjudication, ClaimStatusSummary, DebateTurn, RoundResearchDigest } from "../types.js";
3
- /** Extract JSON from text that might have markdown fences or surrounding text */
4
- export declare function extractJSON(text: string): string;
5
- /** Extract a JSON array from text */
6
- export declare function extractJSONArray(text: string): string;
3
+ export { extractJSON, extractJSONArray } from "../../utils/json.js";
7
4
  /** Format base context into a readable text block for LLM prompts */
8
5
  export declare function formatBaseContext(ctx: ArenaBaseContext): string;
9
6
  /** Format participant reports for cross-review or consensus prompts */
@@ -22,90 +22,13 @@ function sanitizeForPrompt(text) {
22
22
  .replace(/^(?:IGNORE|DISREGARD|FORGET|OVERRIDE|SYSTEM|INSTRUCTION)[:\s].*/gim, "")
23
23
  .slice(0, 3000);
24
24
  }
25
- /** Extract JSON from text that might have markdown fences or surrounding text */
26
- export function extractJSON(text) {
27
- // Try fenced code blocks — use GREEDY match to handle nested backticks
28
- const fenced = text.match(/```(?:json)?\s*\n([\s\S]*)\n\s*```/);
29
- if (fenced)
30
- return fenced[1].trim();
31
- // Try to find the outermost { ... } pair with balanced braces
32
- const start = text.indexOf("{");
33
- if (start !== -1) {
34
- let depth = 0;
35
- let inString = false;
36
- let escape = false;
37
- for (let i = start; i < text.length; i++) {
38
- const ch = text[i];
39
- if (escape) {
40
- escape = false;
41
- continue;
42
- }
43
- if (ch === "\\") {
44
- escape = true;
45
- continue;
46
- }
47
- if (ch === '"') {
48
- inString = !inString;
49
- continue;
50
- }
51
- if (inString)
52
- continue;
53
- if (ch === "{")
54
- depth++;
55
- else if (ch === "}") {
56
- depth--;
57
- if (depth === 0)
58
- return text.slice(start, i + 1);
59
- }
60
- }
61
- // Unbalanced — return from start to end as best effort
62
- return text.slice(start);
63
- }
64
- return text;
65
- }
66
- /** Extract a JSON array from text */
67
- export function extractJSONArray(text) {
68
- const fenced = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
69
- if (fenced)
70
- return fenced[1].trim();
71
- // Return the FIRST balanced top-level array. A greedy /\[[\s\S]*\]/ spanned
72
- // to the last ']', merging two arrays (or trailing prose) into one invalid
73
- // blob. Scan for bracket balance, ignoring brackets inside strings.
74
- const balanced = firstBalancedArray(text);
75
- if (balanced)
76
- return balanced;
77
- return text;
78
- }
79
- function firstBalancedArray(text) {
80
- const start = text.indexOf("[");
81
- if (start === -1)
82
- return undefined;
83
- let depth = 0;
84
- let inString = false;
85
- let escaped = false;
86
- for (let i = start; i < text.length; i++) {
87
- const ch = text[i];
88
- if (inString) {
89
- if (escaped)
90
- escaped = false;
91
- else if (ch === "\\")
92
- escaped = true;
93
- else if (ch === '"')
94
- inString = false;
95
- continue;
96
- }
97
- if (ch === '"')
98
- inString = true;
99
- else if (ch === "[")
100
- depth++;
101
- else if (ch === "]") {
102
- depth--;
103
- if (depth === 0)
104
- return text.slice(start, i + 1);
105
- }
106
- }
107
- return undefined; // unbalanced
108
- }
25
+ // extractJSON / extractJSONArray are generic JSON extractors with no Arena
26
+ // dependency. They moved to utils/json.ts so non-Arena consumers (e.g.
27
+ // services/memory-orchestrator) can use them without importing this Arena
28
+ // module that import made Arena a dependency of a core service. Re-exported
29
+ // here for back-compat with Arena's own callers and the existing test.
30
+ export { extractJSON, extractJSONArray } from "../../utils/json.js";
31
+ import { extractJSON, extractJSONArray } from "../../utils/json.js";
109
32
  /** Format base context into a readable text block for LLM prompts */
110
33
  export function formatBaseContext(ctx) {
111
34
  const sections = [];
@@ -18,6 +18,16 @@ export interface ParsedCron {
18
18
  daysOfMonth: Set<number>;
19
19
  months: Set<number>;
20
20
  daysOfWeek: Set<number>;
21
+ /**
22
+ * Whether the dayOfMonth / dayOfWeek FIELD was restricted (anything other
23
+ * than a bare "*"). Vixie-cron semantics: when BOTH day fields are
24
+ * restricted, a tick matches if EITHER matches (OR); when only one is
25
+ * restricted, the "*" one is ignored and the restricted one governs. We
26
+ * can't recover this from the value Set alone ("*" and an explicit full
27
+ * range both expand to the full set), so we capture it at parse time.
28
+ */
29
+ domRestricted: boolean;
30
+ dowRestricted: boolean;
21
31
  }
22
32
  /** Cheap check: is this string a 5-field cron expression (vs an interval like "5m")? */
23
33
  export declare function isCronExpression(s: string): boolean;
@@ -76,6 +76,10 @@ export function parseCronExpression(expr) {
76
76
  daysOfMonth: parseField(parts[2], SPECS[2]),
77
77
  months: parseField(parts[3], SPECS[3]),
78
78
  daysOfWeek: parseField(parts[4], SPECS[4]),
79
+ // A bare "*" is unrestricted; anything else (a number, range, list, or even
80
+ // "*/n") restricts the field for the OR rule below.
81
+ domRestricted: parts[2].trim() !== "*",
82
+ dowRestricted: parts[4].trim() !== "*",
79
83
  };
80
84
  }
81
85
  // Cache one formatter per timezone — constructing Intl.DateTimeFormat is costly.
@@ -123,11 +127,25 @@ function wallClockInZone(ms, timeZone) {
123
127
  };
124
128
  }
125
129
  function matches(cron, wc) {
126
- return (cron.minutes.has(wc.minute) &&
127
- cron.hours.has(wc.hour) &&
128
- cron.daysOfMonth.has(wc.dayOfMonth) &&
129
- cron.months.has(wc.month) &&
130
- cron.daysOfWeek.has(wc.dayOfWeek));
130
+ if (!cron.minutes.has(wc.minute))
131
+ return false;
132
+ if (!cron.hours.has(wc.hour))
133
+ return false;
134
+ if (!cron.months.has(wc.month))
135
+ return false;
136
+ // Day-of-month vs day-of-week per Vixie/POSIX cron:
137
+ // - both restricted → match if EITHER matches (OR)
138
+ // - only one restricted → that one governs (the "*" one is ignored)
139
+ // - neither restricted → matches every day
140
+ const domHit = cron.daysOfMonth.has(wc.dayOfMonth);
141
+ const dowHit = cron.daysOfWeek.has(wc.dayOfWeek);
142
+ if (cron.domRestricted && cron.dowRestricted)
143
+ return domHit || dowHit;
144
+ if (cron.domRestricted)
145
+ return domHit;
146
+ if (cron.dowRestricted)
147
+ return dowHit;
148
+ return true;
131
149
  }
132
150
  const MINUTE_MS = 60_000;
133
151
  // ~2 years of minutes — bounds the search for an unsatisfiable expression.
@@ -18,6 +18,7 @@ import type { CronJob } from "./scheduler.js";
18
18
  import type { CronScheduler } from "./scheduler.js";
19
19
  import type { PermissionMode } from "../types.js";
20
20
  import type { ApprovalBackend } from "../tool-system/permission.js";
21
+ import type { SandboxMode } from "../tool-system/sandbox/index.js";
21
22
  /** What the executor hands to the run backend for one fired job. */
22
23
  export interface CronRunRequest {
23
24
  job: CronJob;
@@ -27,6 +28,11 @@ export interface CronRunRequest {
27
28
  permissionMode: PermissionMode;
28
29
  /** Approval backend the run must install (resolved from the job's tier). */
29
30
  approvalBackend: ApprovalBackend;
31
+ /** Sandbox mode the run should confine writes/shell to (resolved from the
32
+ * job's tier). The host runner must forward this to `Engine({ sandbox })`
33
+ * so even a `full` tier can't escape the workspace — defense in depth on
34
+ * top of the approval backend. */
35
+ sandboxMode: SandboxMode;
30
36
  /** Abort signal for the run — tripped by `CronScheduler.abort(jobId)` when
31
37
  * the run must be cancelled mid-flight (e.g. the user deletes its session
32
38
  * while it's still executing). Forward to `Engine.run({ signal })`. */
@@ -35,16 +41,27 @@ export interface CronRunRequest {
35
41
  export interface CronRunResult {
36
42
  text: string;
37
43
  reason: string;
44
+ /**
45
+ * When present, this fire hit a PERMANENT failure that will recur every tick
46
+ * (e.g. a `resumeSessionId` whose session was deleted). The scheduler should
47
+ * auto-disable the job with `stop.reason` rather than silently retrying
48
+ * forever. Absent = transient/normal outcome; keep scheduling.
49
+ */
50
+ stop?: {
51
+ reason: string;
52
+ };
38
53
  }
39
54
  /** The pluggable run backend. Production wires this to a headless Engine run;
40
55
  * tests pass a stub. Phase 5 will swap this for a RunManager.submit() path. */
41
56
  export type CronRunner = (req: CronRunRequest) => Promise<CronRunResult>;
42
57
  /**
43
58
  * Install the cron executor. After this call, a fired job invokes `runner`
44
- * with a run request whose permission tier comes from the job. Errors from
45
- * `runner` are swallowed by the
46
- * scheduler's existing try/catch (scheduler.ts) so one bad run never stops
47
- * future ticks we still log here so failures are visible.
59
+ * with a run request whose permission tier comes from the job. A thrown error
60
+ * is logged (`logCronFailure`) and re-thrown so the scheduler's catch records
61
+ * stats and keeps ticking one transient bad run never stops future ticks. A
62
+ * PERMANENT failure the runner flags via `result.stop` (e.g. a resume target
63
+ * whose session was deleted) auto-disables the job with that reason, so it
64
+ * stops silently retrying forever.
48
65
  */
49
66
  export declare function bindCronToEngine(scheduler: CronScheduler, runner: CronRunner): void;
50
67
  /** Minimal structural view of RunManager.submit the executor needs. */
@@ -16,12 +16,30 @@
16
16
  */
17
17
  import { AUTOMATION_RUN_SOURCE } from "../run/EngineRunner.js";
18
18
  import { resolveWritePolicy } from "./write-policy.js";
19
+ import { logger } from "../logging/logger.js";
20
+ /**
21
+ * Log a cron executor failure, then re-throw so the scheduler's own try/catch
22
+ * still records run stats and keeps ticking. Without this the failure was
23
+ * silently swallowed (the scheduler catch is empty) — a job could fail every
24
+ * fire with zero trace, which is the exact opposite of what the executor
25
+ * docstrings claimed ("we still log here so failures are visible").
26
+ */
27
+ function logCronFailure(job, err) {
28
+ logger.warn("cron.executor_failed", {
29
+ cat: "automation",
30
+ jobId: job.id,
31
+ jobName: job.name,
32
+ error: err instanceof Error ? err.message : String(err),
33
+ });
34
+ }
19
35
  /**
20
36
  * Install the cron executor. After this call, a fired job invokes `runner`
21
- * with a run request whose permission tier comes from the job. Errors from
22
- * `runner` are swallowed by the
23
- * scheduler's existing try/catch (scheduler.ts) so one bad run never stops
24
- * future ticks we still log here so failures are visible.
37
+ * with a run request whose permission tier comes from the job. A thrown error
38
+ * is logged (`logCronFailure`) and re-thrown so the scheduler's catch records
39
+ * stats and keeps ticking one transient bad run never stops future ticks. A
40
+ * PERMANENT failure the runner flags via `result.stop` (e.g. a resume target
41
+ * whose session was deleted) auto-disables the job with that reason, so it
42
+ * stops silently retrying forever.
25
43
  */
26
44
  export function bindCronToEngine(scheduler, runner) {
27
45
  scheduler.setExecutor(async (job, signal) => {
@@ -31,9 +49,21 @@ export function bindCronToEngine(scheduler, runner) {
31
49
  prompt: job.prompt,
32
50
  permissionMode: policy.permissionMode,
33
51
  approvalBackend: policy.approvalBackend,
52
+ sandboxMode: policy.sandboxMode,
34
53
  signal,
35
54
  };
36
- await runner(req);
55
+ try {
56
+ const result = await runner(req);
57
+ // A PERMANENT failure the runner flags (e.g. a resume target whose session
58
+ // was deleted) auto-disables the job so it stops silently retrying.
59
+ if (result?.stop) {
60
+ scheduler.disableWithReason(job.id, result.stop.reason);
61
+ }
62
+ }
63
+ catch (err) {
64
+ logCronFailure(job, err);
65
+ throw err; // let the scheduler's catch record stats + keep ticking
66
+ }
37
67
  });
38
68
  }
39
69
  /**
@@ -45,12 +75,18 @@ export function bindCronToEngine(scheduler, runner) {
45
75
  */
46
76
  export function bindCronToRunManager(scheduler, runManager) {
47
77
  scheduler.setExecutor(async (job, _signal) => {
48
- const snapshot = await runManager.submit({
49
- objective: job.prompt,
50
- cwd: job.cwd,
51
- metadata: { source: AUTOMATION_RUN_SOURCE, cronJobId: job.id, cronJobName: job.name },
52
- });
53
- // Record the run id on the job so the UI can link to run history.
54
- job.lastRunId = snapshot.runId;
78
+ try {
79
+ const snapshot = await runManager.submit({
80
+ objective: job.prompt,
81
+ cwd: job.cwd,
82
+ metadata: { source: AUTOMATION_RUN_SOURCE, cronJobId: job.id, cronJobName: job.name },
83
+ });
84
+ // Record the run id on the job so the UI can link to run history.
85
+ job.lastRunId = snapshot.runId;
86
+ }
87
+ catch (err) {
88
+ logCronFailure(job, err);
89
+ throw err; // let the scheduler's catch record stats + keep ticking
90
+ }
55
91
  });
56
92
  }
@@ -5,6 +5,12 @@
5
5
  * with a predefined prompt on schedule.
6
6
  */
7
7
  import type { CronStore } from "./store.js";
8
+ /**
9
+ * True when a cron timer fired too far past its scheduled instant to be a
10
+ * legitimate on-time run — i.e. the host slept through the scheduled time and
11
+ * the timer fired on wake. Exported for unit testing the sleep/wake guard.
12
+ */
13
+ export declare function isCronMisfire(scheduledFor: number, now: number): boolean;
8
14
  /** Permission tier a scheduled job runs under (Phase 5 enforces write tiers). */
9
15
  export type CronPermissionLevel = "read-only" | "workspace-write" | "full";
10
16
  export interface CronJob {
@@ -25,12 +31,27 @@ export interface CronJob {
25
31
  permissionLevel?: CronPermissionLevel;
26
32
  /** RunStore run id of the most recent execution (Phase 2 RunManager path). */
27
33
  lastRunId?: string;
34
+ /** True = one-shot: delete the job after its first real execution so it never
35
+ * fires again (e.g. "in 10 minutes, do X once"). */
36
+ once?: boolean;
37
+ /** When set, the fired job RESUMES this codeshell chat session instead of
38
+ * starting a fresh one: the prompt is appended as a new user turn to that
39
+ * session (restored from disk if not in memory), carrying its transcript /
40
+ * goal / context. Unset = the default standalone behaviour (new session per
41
+ * fire). The host's executor honours this. */
42
+ resumeSessionId?: string;
43
+ /** Why the job was auto-disabled (set by `disableWithReason`), e.g. a resume
44
+ * target whose session no longer exists. Surfaced in the UI so a silently
45
+ * stopped job is explainable; cleared when the job is re-enabled. */
46
+ disabledReason?: string;
28
47
  }
29
48
  /** Optional metadata accepted by create(). */
30
49
  export interface CreateJobOptions {
31
50
  cwd?: string;
32
51
  timezone?: string;
33
52
  permissionLevel?: CronPermissionLevel;
53
+ once?: boolean;
54
+ resumeSessionId?: string;
34
55
  }
35
56
  /** Fields editable via update(). Any omitted field is left unchanged. */
36
57
  export interface UpdateJobPatch {
@@ -119,6 +140,14 @@ export declare class CronScheduler {
119
140
  list(): CronJob[];
120
141
  get(id: string): CronJob | undefined;
121
142
  pause(id: string): boolean;
143
+ /**
144
+ * Auto-disable a job and record WHY (e.g. its resume target session was
145
+ * deleted). Unlike `delete`, the job is retained (enabled=false) so the user
146
+ * can see it stopped and its reason in the UI, then delete or re-point it.
147
+ * Mirrors `pause` but also stamps `disabledReason`. Idempotent-safe: a
148
+ * subsequent `resume()` clears the reason.
149
+ */
150
+ disableWithReason(id: string, reason: string): boolean;
122
151
  resume(id: string): boolean;
123
152
  /**
124
153
  * Edit an existing job's fields (name/prompt/schedule/timezone/cwd/
@@ -153,4 +182,13 @@ export declare class CronScheduler {
153
182
  */
154
183
  private fire;
155
184
  }
185
+ /**
186
+ * Parse a schedule string into milliseconds.
187
+ * Supports: "30s", "5m", "1h", "1d", or raw (all-digit) milliseconds.
188
+ *
189
+ * Throws on anything else rather than silently falling back to a default —
190
+ * a typo like "5mn" should surface as an error, not quietly schedule every
191
+ * 10 minutes (review-2026-05-30).
192
+ */
193
+ export declare function parseSchedule(schedule: string): number;
156
194
  export declare const cronScheduler: CronScheduler;