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

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
@@ -2,9 +2,10 @@
2
2
  * EnterWorktree / ExitWorktree tools — create and manage isolated git worktrees.
3
3
  */
4
4
  import type { ToolDefinition } from "../../types.js";
5
+ import type { ToolContext } from "../context.js";
5
6
  import { type WorktreeSession } from "../../git/worktree.js";
6
7
  export declare function getActiveWorktree(): WorktreeSession | undefined;
7
8
  export declare const enterWorktreeToolDef: ToolDefinition;
8
- export declare function enterWorktreeTool(args: Record<string, unknown>): Promise<string>;
9
+ export declare function enterWorktreeTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
9
10
  export declare const exitWorktreeToolDef: ToolDefinition;
10
11
  export declare function exitWorktreeTool(args: Record<string, unknown>): Promise<string>;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * EnterWorktree / ExitWorktree tools — create and manage isolated git worktrees.
3
3
  */
4
- import { createWorktree, removeWorktree, validateWorktreeSlug, } from "../../git/worktree.js";
4
+ import { createWorktree, removeWorktree, validateWorktreeSlug, selectPlatformScript, runWorktreeSetup, } from "../../git/worktree.js";
5
5
  // Global worktree state
6
6
  let _activeWorktree;
7
7
  export function getActiveWorktree() {
@@ -25,7 +25,7 @@ export const enterWorktreeToolDef = {
25
25
  required: ["slug"],
26
26
  },
27
27
  };
28
- export async function enterWorktreeTool(args) {
28
+ export async function enterWorktreeTool(args, ctx) {
29
29
  const slug = args.slug;
30
30
  if (!slug)
31
31
  return "Error: slug is required";
@@ -41,18 +41,45 @@ export async function enterWorktreeTool(args) {
41
41
  try {
42
42
  // Use a placeholder session ID (the agent should set this properly)
43
43
  const sessionId = args.__sessionId ?? `wt-${Date.now()}`;
44
- const cwd = args.__cwd ?? process.cwd();
44
+ const cwd = args.__cwd ?? ctx?.cwd ?? process.cwd();
45
45
  _activeWorktree = createWorktree(cwd, slug, sessionId);
46
+ // Run the project's localEnvironment.setupScripts once in the new
47
+ // worktree root (Beta decision 2026-06-08: setup belongs to the worktree
48
+ // lifecycle, not the conversation). Failure warns-but-continues — a broken
49
+ // setup script must not strand the agent outside a worktree it just made.
50
+ let setupNote = "";
51
+ const setupScripts = ctx?.engine?.readWorktreeSetupScripts(cwd);
52
+ const script = selectPlatformScript(setupScripts);
53
+ if (script) {
54
+ const setup = await runWorktreeSetup(_activeWorktree.worktreePath, script, {
55
+ sandbox: ctx?.sandbox,
56
+ shellEnv: ctx?.shellEnv,
57
+ signal: ctx?.signal,
58
+ });
59
+ if (setup.ok) {
60
+ setupNote = `\n\nRan setup script (exit 0).${setup.output ? `\n${truncate(setup.output)}` : ""}`;
61
+ }
62
+ else {
63
+ setupNote =
64
+ `\n\n⚠️ Setup script failed (exit ${setup.exitCode ?? "?"}) — continuing anyway. ` +
65
+ `You may need to run setup manually.${setup.output ? `\n${truncate(setup.output)}` : ""}`;
66
+ }
67
+ }
46
68
  return (`Worktree created:\n` +
47
69
  ` Path: ${_activeWorktree.worktreePath}\n` +
48
70
  ` Branch: ${_activeWorktree.worktreeBranch}\n` +
49
71
  ` From: ${_activeWorktree.originalBranch ?? "HEAD"}\n\n` +
50
- `You are now working in an isolated copy. Changes here won't affect the main repo.`);
72
+ `You are now working in an isolated copy. Changes here won't affect the main repo.` +
73
+ setupNote);
51
74
  }
52
75
  catch (err) {
53
76
  return `Error creating worktree: ${err.message}`;
54
77
  }
55
78
  }
79
+ /** Keep setup output from bloating the tool result — head+tail-ish trim. */
80
+ function truncate(s, max = 2000) {
81
+ return s.length <= max ? s : `${s.slice(0, max)}\n…(${s.length - max} more chars)`;
82
+ }
56
83
  export const exitWorktreeToolDef = {
57
84
  name: "ExitWorktree",
58
85
  description: "Exit the current worktree and return to the main working directory. " +
@@ -4,7 +4,6 @@
4
4
  import { writeFile, mkdir } from "node:fs/promises";
5
5
  import { dirname } from "node:path";
6
6
  import { fileCache } from "./file-cache.js";
7
- import { enforcePathPolicy } from "../path-policy.js";
8
7
  export const writeToolDef = {
9
8
  name: "Write",
10
9
  description: "Write content to a file. Creates the file if it doesn't exist, " +
@@ -25,11 +24,6 @@ export async function writeTool(args, ctx) {
25
24
  return "Error: file_path is required";
26
25
  if (content === undefined)
27
26
  return "Error: content is required";
28
- // Path policy gate: deny writes to sensitive paths, refuse writes outside
29
- // workspace until approved. See tool-system/path-policy.ts.
30
- const blocked = enforcePathPolicy(filePath, "write", ctx?.cwd);
31
- if (blocked)
32
- return blocked;
33
27
  try {
34
28
  await mkdir(dirname(filePath), { recursive: true });
35
29
  await writeFile(filePath, content, "utf-8");
@@ -18,11 +18,39 @@ import type { ToolRegistry } from "./registry.js";
18
18
  import type { AgentPresetName } from "../preset/index.js";
19
19
  import type { SandboxBackend } from "./sandbox/index.js";
20
20
  import type { HookRegistry } from "../hooks/registry.js";
21
- import type { Engine } from "../engine/engine.js";
21
+ /**
22
+ * Narrow view of the owning Engine that tools are allowed to call back into.
23
+ * Defined here (in the low-level tool-system) rather than importing the
24
+ * concrete `Engine` type so tool-system does NOT depend on the top-level
25
+ * engine module — that import created a tool-system ↔ engine cycle. The Engine
26
+ * class satisfies this structurally (no `implements` needed); this interface
27
+ * lists exactly the members tools use today (see plan.ts / worktree.ts).
28
+ */
29
+ export interface ToolRuntimeHost {
30
+ /** Whether the owning Engine is currently in plan mode. */
31
+ readonly planMode: boolean;
32
+ /** Toggle plan mode (EnterPlanMode / ExitPlanMode tools). */
33
+ setPlanMode(value: boolean): void;
34
+ /** Per-worktree setup scripts, or undefined for sub-agents / no cwd. */
35
+ readWorktreeSetupScripts(cwd?: string): {
36
+ default?: string;
37
+ macos?: string;
38
+ linux?: string;
39
+ windows?: string;
40
+ } | undefined;
41
+ }
22
42
  /** One choice in a multiple-choice AskUserQuestion. */
23
43
  export interface AskUserChoice {
24
44
  label: string;
25
45
  description: string;
46
+ /**
47
+ * Optional semantic hint for how the UI colors this choice and its resolved
48
+ * echo — `ok` (affirmative / allow → green ✓), `danger` (negative / deny →
49
+ * red ✕), `neutral` (no coloring). Only set by trusted first-party callers
50
+ * with fixed option sets (e.g. the credential-use gate); the LLM-facing
51
+ * AskUserQuestion tool strips it so model-authored prompts stay neutral.
52
+ */
53
+ tone?: "ok" | "danger" | "neutral";
26
54
  }
27
55
  /**
28
56
  * Optional rendering hints / multiple-choice options carried alongside the
@@ -35,6 +63,14 @@ export interface AskUserOptions {
35
63
  options?: AskUserChoice[];
36
64
  /** Allow the user to pick more than one option. Defaults to false. */
37
65
  multiSelect?: boolean;
66
+ /**
67
+ * When true, the UI must offer ONLY the given `options` — no "其它…"
68
+ * free-text escape hatch. Used by closed-set decisions (e.g. a path-permission
69
+ * 允许本次/拒绝 prompt) where the answer is interpreted by an exact label
70
+ * match, so a free-typed answer like "允许" would never match and silently
71
+ * deny. Ignored unless `options` is non-empty.
72
+ */
73
+ optionsOnly?: boolean;
38
74
  }
39
75
  /**
40
76
  * Function the host UI / server uses to ask the user a question.
@@ -76,6 +112,12 @@ export interface SubAgentSpawnRequest {
76
112
  * Undefined → child inherits the parent's full tool set (current behavior).
77
113
  */
78
114
  toolAllowlist?: string[];
115
+ /**
116
+ * Optional skill-name allowlist for the child (hard isolation). When set,
117
+ * the child only sees/invokes these skills in its system prompt and Skill
118
+ * tool. Undefined → child inherits the parent's full skill pool.
119
+ */
120
+ skillAllowlist?: string[];
79
121
  /**
80
122
  * Optional per-call system prompt appended to the child Engine's prompt
81
123
  * (the role definition's Markdown body). Undefined → child inherits only
@@ -84,10 +126,34 @@ export interface SubAgentSpawnRequest {
84
126
  appendSystemPrompt?: string;
85
127
  /** True for read-only reviewer/researcher children; tunes investigation guard reminders. */
86
128
  readOnlySession?: boolean;
129
+ /**
130
+ * Resume an existing child session instead of cold-starting a new one.
131
+ * When set, the child Engine runs with `sessionId: resumeSessionId`, which
132
+ * loads that session's full transcript (all prior tool calls + results) and
133
+ * appends this turn's prompt — i.e. transcript replay, the CC model. When
134
+ * omitted, the child cold-starts under its own `agentId` as the session id
135
+ * (so `agentId === childSid`, enabling later resume with no extra mapping).
136
+ * See AgentSendInput in builtin/agent.ts.
137
+ */
138
+ resumeSessionId?: string;
87
139
  }
88
140
  export interface SubAgentSpawner {
89
- /** Run a sub-agent synchronously and return its text output. */
90
- spawn(req: SubAgentSpawnRequest): Promise<string>;
141
+ /**
142
+ * Run a sub-agent synchronously. Returns its text output and the child's
143
+ * session id. The session id equals the request's `agentId` on a cold start
144
+ * (or `resumeSessionId` when resuming), so the parent can address follow-up
145
+ * input at the same session via AgentSendInput (transcript replay).
146
+ */
147
+ spawn(req: SubAgentSpawnRequest): Promise<{
148
+ text: string;
149
+ sessionId: string;
150
+ }>;
151
+ /**
152
+ * Whether a child session id exists on disk. Lets AgentSendInput resume an
153
+ * agent across a process restart (when the in-memory registry is empty but
154
+ * the session transcript is still persisted). Optional for legacy callers.
155
+ */
156
+ sessionExists?(sessionId: string): boolean;
91
157
  /** Stream callback to forward sub-agent events back to the parent UI. */
92
158
  parentStream?: StreamCallback;
93
159
  /** Static config used to describe what the spawned engine looks like. */
@@ -156,9 +222,18 @@ export interface ToolContext {
156
222
  /** Whether the owning Engine is currently in plan mode. Replaces the
157
223
  * removed module-level `isInPlanMode()` singleton. */
158
224
  planMode: boolean;
159
- /** The Engine that built this context. Lets tools call back into the engine
160
- * (e.g. ctx.engine.setPlanMode(true/false)) without a module-level singleton. */
161
- engine: Engine;
225
+ /**
226
+ * The owning Engine's permission mode. Path policy reads this so that
227
+ * `bypassPermissions` (the user's "完全访问") skips the path-approval prompt
228
+ * entirely — matching the tool-permission backend and CC, where bypass
229
+ * skips ALL checks including path validation. Undefined → treat as
230
+ * "default" (always enforce). */
231
+ permissionMode?: string;
232
+ /** The Engine that built this context, as the narrow {@link ToolRuntimeHost}
233
+ * view. Lets tools call back into the engine (e.g. ctx.engine.setPlanMode)
234
+ * without a module-level singleton — and without tool-system depending on
235
+ * the concrete Engine type (which would re-introduce the import cycle). */
236
+ engine: ToolRuntimeHost;
162
237
  /**
163
238
  * sessionId of the Engine.run() turn this context was built for. Tools
164
239
  * that emit session-scoped side effects (today: background-agent
@@ -183,6 +258,28 @@ export interface ToolContext {
183
258
  * `settings.disabledPlugins` by Engine.run().
184
259
  */
185
260
  disabledPlugins?: string[];
261
+ /**
262
+ * Sub-agent skill allowlist (hard isolation). When set, this sub-agent may
263
+ * only see/invoke skills in this list — applied on top of the disabled
264
+ * lists. Undefined → inherit the parent's full pool (current behavior).
265
+ * Comes from the agent role definition's `skills:` frontmatter, threaded
266
+ * through SubAgentSpawnRequest.skillAllowlist into the child Engine.
267
+ */
268
+ skillAllowlist?: string[];
269
+ /**
270
+ * Background-shell manager (Bash run_in_background + BashOutput / KillShell
271
+ * / ListShells). Undefined → the tools fall back to the process-local
272
+ * `backgroundShellManager` singleton. Threaded explicitly so tests (and
273
+ * any future per-engine isolation) can inject their own instance.
274
+ */
275
+ backgroundShells?: import("../runtime/background-shell.js").BackgroundShellManager;
276
+ /**
277
+ * False in unattended automation/cron runs — `Bash(run_in_background=true)`
278
+ * is rejected and the three background-shell tools are stripped (design
279
+ * §5.5): no one is watching to reap a long-lived dev server. Defaults to
280
+ * true (interactive sessions allow it).
281
+ */
282
+ allowBackgroundShells?: boolean;
186
283
  /**
187
284
  * Builtin tool names the project's capabilityOverrides marked `off` for
188
285
  * this cwd. Engine.run() already HIDES these from the LLM-visible tool
@@ -193,7 +290,58 @@ export interface ToolContext {
193
290
  * sub-agents and no-cwd contexts (same as readBuiltinOverride).
194
291
  */
195
292
  disabledBuiltins?: Set<string>;
293
+ /**
294
+ * MCP servers THIS session's merged config enables (keys of
295
+ * config.mcpServers, enabled!==false). The pool + registry are
296
+ * worker-shared (B1), so tools registered by another project's session live
297
+ * in the same registry — the engine hides them from this session's tool
298
+ * list and the executor rejects calls to them via this set. Undefined =
299
+ * no gating (sub-agents / tests whose registries carry no MCP tools).
300
+ */
301
+ allowedMcpServers?: Set<string>;
302
+ /**
303
+ * The engine's settings scope, so tools that read disk config directly (e.g.
304
+ * the credential tools) can honor host-isolation instead of always merging
305
+ * the host user's ~/.code-shell. "full" = host app (user + project); "project"
306
+ * / "isolated" = must not surface host user credentials or autoApprove.
307
+ * Undefined = legacy/no-cwd → treated as "full" by consumers for compat.
308
+ */
309
+ settingsScope?: import("../settings/manager.js").SettingsScope;
310
+ /**
311
+ * Project-scoped extra environment variables (the user's
312
+ * `localEnvironment.env` KEY=VALUE pairs for this cwd). Layered on top of
313
+ * the shell env that the Bash tool and background shells build, so a project
314
+ * can inject e.g. `DATABASE_URL` / `NODE_ENV` into every command it runs.
315
+ * Unlike the sandbox allowlist, these bypass the deny regex — the user put
316
+ * them in project settings deliberately. Undefined for sub-agents and
317
+ * no-cwd contexts (populated from settings by Engine.buildToolContext()).
318
+ */
319
+ shellEnv?: Record<string, string>;
320
+ /**
321
+ * Browser automation bridge (browser_snapshot / click / type / navigate /
322
+ * scroll). The renderer implements it on top of the webview's CDP
323
+ * (Accessibility.getFullAXTree + Input.dispatchMouseEvent). Undefined →
324
+ * headless / no browser panel → the browser tools degrade with a clear error.
325
+ * See tool-system/browser-bridge.ts and the MVP spec
326
+ * docs/superpowers/specs/2026-06-16-browser-automation-mvp.md.
327
+ */
328
+ browser?: import("./browser-bridge.js").BrowserBridge;
329
+ /**
330
+ * Inject a stored cookie credential into the built-in browser (restore its
331
+ * login state so the AI can drive the page as that account). The host
332
+ * (desktop main) implements it on top of `restoreCookiesToBrowser`. Undefined
333
+ * → headless / no browser → the InjectCredential tool degrades with a clear
334
+ * error. Mirrors the askUser cross-process callback shape. The credentialId
335
+ * names a `type:"cookie"` credential; result counts cookies written.
336
+ */
337
+ injectCredentialToBrowser?: InjectCredentialFn;
196
338
  }
339
+ /** Inject a cookie credential into the built-in browser (host-implemented). */
340
+ export type InjectCredentialFn = (credentialId: string) => Promise<{
341
+ ok: boolean;
342
+ count?: number;
343
+ error?: string;
344
+ }>;
197
345
  /**
198
346
  * Per-Engine container that produces a fresh ToolContext on demand.
199
347
  * Engine creates one of these in run() and passes it to ToolExecutor.
@@ -39,7 +39,9 @@ export declare class ToolExecutor {
39
39
  /** Check if a tool is safe for concurrent execution (read-only). */
40
40
  isConcurrencySafe(toolName: string): boolean;
41
41
  executeSingle(callIn: ToolCall): Promise<ToolResult>;
42
- private isReadOnlyBashCommand;
42
+ private resolvePolicyOperation;
43
+ private enforceDeclaredPathPolicy;
44
+ private resolvePathPolicyTargets;
43
45
  /**
44
46
  * Convert tool results into Message entries for the transcript.
45
47
  */
@@ -2,10 +2,15 @@
2
2
  * Tool executor — orchestrates permission checks, hooks, and execution.
3
3
  */
4
4
  import { setMaxListeners } from "node:events";
5
+ import { resolve as resolvePath, isAbsolute as isAbsolutePath } from "node:path";
6
+ import { classifyBashCommand } from "./permission.js";
7
+ import { ToolNotFoundError } from "../exceptions.js";
5
8
  import { logger as rootLogger, getCurrentSid } from "../logging/logger.js";
6
9
  import { recordToolCall, recordToolResult } from "../logging/session-recorder.js";
7
10
  import { validateToolArgs } from "./validation.js";
8
11
  import { PLAN_MODE_ALLOWED_TOOLS } from "./plan-mode-allowlist.js";
12
+ import { enforcePathPolicyWithApproval } from "./path-policy.js";
13
+ import { parsePatch } from "./builtin/apply-patch/parser.js";
9
14
  // A1 hardening: hooks must never promote a non-`allow` classifier
10
15
  // decision to `allow`. They may otherwise adjust the decision freely
11
16
  // (e.g. tighten `allow` to `deny`/`ask`, or relax `deny` to `ask` to
@@ -114,22 +119,77 @@ export class ToolExecutor {
114
119
  isError: true,
115
120
  };
116
121
  }
117
- // 0. Plan mode: only allow read-only tools no file writes at all
118
- if (this.toolCtx?.planMode) {
119
- // Shared with engine.ts's tool-visibility filter so the set the model
120
- // SEES and the set the executor RUNS can't drift (they had).
121
- if (!PLAN_MODE_ALLOWED_TOOLS.has(call.toolName)) {
122
- if (call.toolName === "Bash" && this.isReadOnlyBashCommand(call.args)) {
123
- // Read-only bash is fine
122
+ // Same gate for MCP tools: the registry is worker-shared, so it can hold
123
+ // tools from servers OTHER sessions enabled. Visibility filtering hides
124
+ // them from this session's tool list; this rejects a direct call anyway.
125
+ if (this.toolCtx?.allowedMcpServers) {
126
+ const allowed = this.toolCtx.allowedMcpServers;
127
+ const reg = this.registry.getTool(call.toolName);
128
+ if (reg?.source === "mcp" && !allowed.has(reg.serverName ?? "")) {
129
+ return {
130
+ id: call.id,
131
+ toolName: call.toolName,
132
+ error: `Tool ${call.toolName} belongs to an MCP server that is not enabled for this project. Do NOT retry this tool call.`,
133
+ isError: true,
134
+ };
135
+ }
136
+ // The generic MCP builtins (MCPTool / ReadMcpResource) take the target
137
+ // server as an ARGUMENT and call MCPManager directly, so the source==="mcp"
138
+ // check above can't see them — without this a session could invoke a tool
139
+ // or read a resource on a server only ANOTHER session enabled. Gate on the
140
+ // server arg. ListMcpResources has an optional server filter and would
141
+ // otherwise enumerate every connected server's resources, so we inject the
142
+ // allowlist for it to filter by (see mcp-tools.ts).
143
+ if (call.toolName === "MCPTool" || call.toolName === "ReadMcpResource") {
144
+ const server = String(call.args.server ?? "");
145
+ if (!allowed.has(server)) {
146
+ return {
147
+ id: call.id,
148
+ toolName: call.toolName,
149
+ error: `MCP server "${server}" is not enabled for this project. Do NOT retry this tool call.`,
150
+ isError: true,
151
+ };
124
152
  }
125
- else {
153
+ }
154
+ else if (call.toolName === "ListMcpResources") {
155
+ const server = String(call.args.server ?? "");
156
+ if (server && !allowed.has(server)) {
126
157
  return {
127
158
  id: call.id,
128
159
  toolName: call.toolName,
129
- error: `Plan mode: ${call.toolName} is blocked. Only read-only tools (Read, Glob, Grep, WebSearch, WebFetch, Bash read-only) are allowed. You MUST output your plan as text in the conversation instead. Do NOT retry this tool call.`,
160
+ error: `MCP server "${server}" is not enabled for this project. Do NOT retry this tool call.`,
130
161
  isError: true,
131
162
  };
132
163
  }
164
+ call.args.__allowedMcpServers = allowed;
165
+ }
166
+ }
167
+ // 0. Plan mode: only allow read-only tools — no file writes at all
168
+ if (this.toolCtx?.planMode) {
169
+ // Shared with engine.ts's tool-visibility filter so the set the model
170
+ // SEES and the set the executor RUNS can't drift (they had).
171
+ // Bash is in the allow-list so the model can SEE it for read-only probing,
172
+ // but membership alone must NOT grant write access: a Bash command that
173
+ // modifies files (echo >, sed -i, mv, ...) has to be blocked here too, or it
174
+ // would slip past plan mode into the normal permission flow (where the user
175
+ // could approve it) AND leave no diff, since it never touches Write/Edit.
176
+ const allowed = PLAN_MODE_ALLOWED_TOOLS.has(call.toolName);
177
+ // Defer to the canonical shell classifier (the same one the permission
178
+ // flow uses) instead of a second hand-rolled allowlist that drifted and
179
+ // let find -delete / awk system() / process substitution / git difftool
180
+ // through. Plan mode is strictly read-only, so ONLY "safe-read" passes —
181
+ // safe-write (mkdir/touch/cp), unsafe, and dangerous are all blocked.
182
+ const bashWrite = call.toolName === "Bash" &&
183
+ classifyBashCommand(String(call.args.command ?? "")) !== "safe-read";
184
+ if (!allowed || bashWrite) {
185
+ return {
186
+ id: call.id,
187
+ toolName: call.toolName,
188
+ error: allowed && bashWrite
189
+ ? `Plan mode is read-only: this Bash command writes or modifies files, which is not allowed while planning. To carry it out, first call ExitPlanMode (after the user approves your plan), then run it. Do NOT retry this command in plan mode.`
190
+ : `Plan mode: ${call.toolName} is blocked. Only read-only tools (Read, Glob, Grep, WebSearch, WebFetch, Bash read-only) are allowed. You MUST output your plan as text in the conversation instead. Do NOT retry this tool call.`,
191
+ isError: true,
192
+ };
133
193
  }
134
194
  }
135
195
  // 0.5. Validate tool args
@@ -182,6 +242,19 @@ export class ToolExecutor {
182
242
  toolCallId: call.id,
183
243
  });
184
244
  }
245
+ // 0.65. Centralized file path policy. Tool handlers declare their file
246
+ // path surface on RegisteredTool.pathPolicy; the executor enforces it
247
+ // after hooks have had a chance to rewrite args, but before permission
248
+ // classification or the handler can touch the filesystem.
249
+ const pathPolicyError = await this.enforceDeclaredPathPolicy(toolDef, call.args);
250
+ if (pathPolicyError) {
251
+ return {
252
+ id: call.id,
253
+ toolName: call.toolName,
254
+ error: pathPolicyError,
255
+ isError: true,
256
+ };
257
+ }
185
258
  // A1 hardening: pre_tool_use can no longer pre-approve a tool via
186
259
  // `decision === "allow"`. Hooks may relax `allow` and may force
187
260
  // `ask`/`deny`, but they cannot promote a `deny`/`ask` decision to
@@ -321,6 +394,18 @@ export class ToolExecutor {
321
394
  durationMs: Date.now() - toolStartedAt,
322
395
  error: err instanceof Error ? err.stack ?? err.message : String(err),
323
396
  });
397
+ // A model calling a tool that isn't in the registry (hallucinated name,
398
+ // or a builtin missing from the active preset's whitelist) must not kill
399
+ // the turn. Feed it back as a normal tool error so the model can retry
400
+ // or pick a different tool — same path as permission denials above.
401
+ if (err instanceof ToolNotFoundError) {
402
+ return {
403
+ id: call.id,
404
+ toolName: call.toolName,
405
+ error: `Tool not found: ${call.toolName}. It is not available in this session — do not call it again; use a different tool.`,
406
+ isError: true,
407
+ };
408
+ }
324
409
  throw err;
325
410
  }
326
411
  result.id = call.id;
@@ -386,86 +471,85 @@ export class ToolExecutor {
386
471
  }
387
472
  return result;
388
473
  }
389
- isReadOnlyBashCommand(args) {
390
- const cmd = String(args.command ?? "").trim();
391
- // Reject shell metacharacters that could chain a write command after a
392
- // read-only prefix: ; && || ` $(...) redirections. A single pipe (|) is
393
- // still allowed the per-part whitelist further down validates it.
394
- const DANGEROUS = /;|&&|\|\||`|\$\(|>/;
395
- if (DANGEROUS.test(cmd))
396
- return false;
397
- // Extract the base command (first word, ignoring env vars like VAR=val)
398
- const baseCmd = cmd.replace(/^(\w+=\S+\s+)*/, "").split(/\s/)[0];
399
- const readOnlyCommands = new Set([
400
- "ls",
401
- "find",
402
- "cat",
403
- "head",
404
- "tail",
405
- "wc",
406
- "file",
407
- "stat",
408
- "tree",
409
- "du",
410
- "df",
411
- "pwd",
412
- "echo",
413
- "which",
414
- "type",
415
- "env",
416
- "printenv",
417
- "grep",
418
- "rg",
419
- "ag",
420
- "awk",
421
- "less",
422
- "more",
423
- "sort",
424
- "uniq",
425
- "diff",
426
- "readlink",
427
- "realpath",
428
- "basename",
429
- "dirname",
430
- "date",
431
- "whoami",
432
- "uname",
433
- "hostname",
434
- "id",
435
- "groups",
436
- "locale",
437
- "uptime",
438
- ]);
439
- // Whitelisted read-only git/tool subcommands.
440
- // Deliberately exclude `node -e`, `python -c`, `sed -n`, etc. those can
441
- // execute arbitrary code even though they "look read-only".
442
- const readOnlyPrefixes = [
443
- "git log",
444
- "git status",
445
- "git diff",
446
- "git branch",
447
- "git show",
448
- "git blame",
449
- "git remote",
450
- "git tag",
451
- "git stash list",
452
- "git rev-parse",
453
- "npx tsc --noEmit",
454
- ];
455
- if (readOnlyCommands.has(baseCmd))
456
- return true;
457
- if (readOnlyPrefixes.some((p) => cmd.startsWith(p)))
458
- return true;
459
- // Allow piped commands if all parts are read-only. Redirections were
460
- // already rejected above, so a bare `|` here is a real pipeline.
461
- if (cmd.includes("|")) {
462
- const parts = cmd.split("|").map((p) => p.trim());
463
- return parts.every((part) => {
464
- const partBase = part.replace(/^(\w+=\S+\s+)*/, "").split(/\s/)[0];
465
- return readOnlyCommands.has(partBase);
466
- });
474
+ resolvePolicyOperation(op, args) {
475
+ if (op === "read" || op === "write")
476
+ return op;
477
+ const raw = args[op.fromArg];
478
+ const value = typeof raw === "string" ? raw : String(raw ?? "");
479
+ if (op.readValues?.includes(value))
480
+ return "read";
481
+ if (op.writeValues?.includes(value))
482
+ return "write";
483
+ return op.default;
484
+ }
485
+ async enforceDeclaredPathPolicy(tool, args) {
486
+ const policies = tool?.pathPolicy;
487
+ if (!policies?.length)
488
+ return null;
489
+ for (const policy of policies) {
490
+ const targets = this.resolvePathPolicyTargets(policy, args);
491
+ if (typeof targets === "string")
492
+ return targets;
493
+ const operation = this.resolvePolicyOperation(policy.operation, args);
494
+ for (const target of targets) {
495
+ const blocked = await enforcePathPolicyWithApproval(target, operation, this.toolCtx);
496
+ if (blocked)
497
+ return blocked;
498
+ }
499
+ }
500
+ return null;
501
+ }
502
+ resolvePathPolicyTargets(policy, args) {
503
+ if (policy.kind === "arg") {
504
+ const raw = args[policy.arg];
505
+ if (typeof raw === "string" && raw.length > 0) {
506
+ // Resolve a RELATIVE single-string path arg against ctx.cwd before
507
+ // classification, mirroring the array branch below and the apply_patch
508
+ // branch — otherwise classifyPath's process.cwd()-based resolution
509
+ // mis-places an in-workspace relative path (e.g. Read "src/x.ts") as
510
+ // "outside workspace" and over-prompts. Absolute paths pass through.
511
+ const cwd = this.toolCtx?.cwd ?? process.cwd();
512
+ return [isAbsolutePath(raw) ? raw : resolvePath(cwd, raw)];
513
+ }
514
+ // Array path args (e.g. GenerateImage `referenceImages`, GenerateVideo
515
+ // `images`): enforce EVERY element. Without this an array yielded zero
516
+ // targets, so out-of-workspace reads bypassed the path-policy "ask" gate
517
+ // that Read/Write enforce. http(s) URLs aren't file reads → skip them.
518
+ // Relative paths resolve against ctx.cwd (the workspace) before
519
+ // classification, like the apply_patch branch — otherwise classifyPath's
520
+ // process.cwd()-based resolution would mis-place an in-workspace relative
521
+ // path as "outside" and over-prompt. Non-string / empty elements skipped.
522
+ if (Array.isArray(raw)) {
523
+ const cwd = this.toolCtx?.cwd ?? process.cwd();
524
+ const paths = raw
525
+ .filter((v) => typeof v === "string" && v.length > 0)
526
+ .filter((v) => !/^https?:\/\//i.test(v))
527
+ .map((v) => (isAbsolutePath(v) ? v : resolvePath(cwd, v)));
528
+ if (paths.length > 0)
529
+ return paths;
530
+ }
531
+ if (policy.defaultToCwd && this.toolCtx?.cwd)
532
+ return [this.toolCtx.cwd];
533
+ return [];
534
+ }
535
+ const rawPatch = args[policy.arg];
536
+ if (typeof rawPatch !== "string" || rawPatch.length === 0)
537
+ return [];
538
+ try {
539
+ const parsed = parsePatch(rawPatch, "lenient");
540
+ const cwd = this.toolCtx?.cwd ?? process.cwd();
541
+ const targets = [];
542
+ for (const hunk of parsed.hunks) {
543
+ targets.push(resolvePath(cwd, hunk.path));
544
+ if (hunk.kind === "update" && hunk.movePath) {
545
+ targets.push(resolvePath(cwd, hunk.movePath));
546
+ }
547
+ }
548
+ return targets;
549
+ }
550
+ catch (err) {
551
+ return `Error: ${err.message}`;
467
552
  }
468
- return false;
469
553
  }
470
554
  /**
471
555
  * Convert tool results into Message entries for the transcript.
@@ -16,7 +16,7 @@
16
16
  *
17
17
  * Everything decays naturally on any write/Bash/AskUser tool (state reset).
18
18
  */
19
- const READ_ONLY_TOOLS = new Set(["Read", "Grep", "Glob", "WebFetch", "WebSearch", "ToolSearch"]);
19
+ import { READ_ONLY_TOOLS } from "./plan-mode-allowlist.js";
20
20
  const READ_BUDGET = 3;
21
21
  const SILENT_TURN_BUDGET = 3;
22
22
  const DEDUPE_OFFSET_BUCKET = 50;