@cjhyy/code-shell-core 0.5.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (484) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/dist/agent/coordinator.d.ts +49 -0
  4. package/dist/agent/coordinator.js +77 -0
  5. package/dist/arena/arena.d.ts +43 -0
  6. package/dist/arena/arena.js +334 -0
  7. package/dist/arena/context/context-tools.d.ts +16 -0
  8. package/dist/arena/context/context-tools.js +231 -0
  9. package/dist/arena/detect-mode.d.ts +20 -0
  10. package/dist/arena/detect-mode.js +78 -0
  11. package/dist/arena/digest-builder.d.ts +25 -0
  12. package/dist/arena/digest-builder.js +120 -0
  13. package/dist/arena/index.d.ts +29 -0
  14. package/dist/arena/index.js +29 -0
  15. package/dist/arena/iterate/convergence.d.ts +25 -0
  16. package/dist/arena/iterate/convergence.js +103 -0
  17. package/dist/arena/iterate/formats/index.d.ts +22 -0
  18. package/dist/arena/iterate/formats/index.js +283 -0
  19. package/dist/arena/iterate/index.d.ts +11 -0
  20. package/dist/arena/iterate/index.js +9 -0
  21. package/dist/arena/iterate/iterative-arena.d.ts +23 -0
  22. package/dist/arena/iterate/iterative-arena.js +237 -0
  23. package/dist/arena/iterate/parse.d.ts +42 -0
  24. package/dist/arena/iterate/parse.js +123 -0
  25. package/dist/arena/iterate/phases/argue.d.ts +22 -0
  26. package/dist/arena/iterate/phases/argue.js +159 -0
  27. package/dist/arena/iterate/phases/revise.d.ts +16 -0
  28. package/dist/arena/iterate/phases/revise.js +62 -0
  29. package/dist/arena/iterate/phases/tournament.d.ts +34 -0
  30. package/dist/arena/iterate/phases/tournament.js +113 -0
  31. package/dist/arena/iterate/tools/web-tools.d.ts +13 -0
  32. package/dist/arena/iterate/tools/web-tools.js +54 -0
  33. package/dist/arena/iterate/types.d.ts +152 -0
  34. package/dist/arena/iterate/types.js +8 -0
  35. package/dist/arena/ledger.d.ts +47 -0
  36. package/dist/arena/ledger.js +151 -0
  37. package/dist/arena/lenses/architecture.d.ts +5 -0
  38. package/dist/arena/lenses/architecture.js +22 -0
  39. package/dist/arena/lenses/engineering.d.ts +5 -0
  40. package/dist/arena/lenses/engineering.js +22 -0
  41. package/dist/arena/lenses/general.d.ts +5 -0
  42. package/dist/arena/lenses/general.js +20 -0
  43. package/dist/arena/lenses/index.d.ts +16 -0
  44. package/dist/arena/lenses/index.js +47 -0
  45. package/dist/arena/lenses/product.d.ts +5 -0
  46. package/dist/arena/lenses/product.js +22 -0
  47. package/dist/arena/model-presets.d.ts +23 -0
  48. package/dist/arena/model-presets.js +44 -0
  49. package/dist/arena/phases/adjudication.d.ts +24 -0
  50. package/dist/arena/phases/adjudication.js +144 -0
  51. package/dist/arena/phases/build-consensus.d.ts +29 -0
  52. package/dist/arena/phases/build-consensus.js +86 -0
  53. package/dist/arena/phases/claim-registry.d.ts +26 -0
  54. package/dist/arena/phases/claim-registry.js +60 -0
  55. package/dist/arena/phases/cross-review.d.ts +45 -0
  56. package/dist/arena/phases/cross-review.js +224 -0
  57. package/dist/arena/phases/debate-rounds.d.ts +27 -0
  58. package/dist/arena/phases/debate-rounds.js +162 -0
  59. package/dist/arena/phases/participant-research.d.ts +38 -0
  60. package/dist/arena/phases/participant-research.js +317 -0
  61. package/dist/arena/phases/planning-detail-expansion.d.ts +38 -0
  62. package/dist/arena/phases/planning-detail-expansion.js +121 -0
  63. package/dist/arena/planner.d.ts +27 -0
  64. package/dist/arena/planner.js +312 -0
  65. package/dist/arena/providers/docs.d.ts +6 -0
  66. package/dist/arena/providers/docs.js +108 -0
  67. package/dist/arena/providers/git.d.ts +8 -0
  68. package/dist/arena/providers/git.js +174 -0
  69. package/dist/arena/providers/index.d.ts +32 -0
  70. package/dist/arena/providers/index.js +132 -0
  71. package/dist/arena/providers/none.d.ts +7 -0
  72. package/dist/arena/providers/none.js +11 -0
  73. package/dist/arena/providers/repo.d.ts +6 -0
  74. package/dist/arena/providers/repo.js +255 -0
  75. package/dist/arena/render/session.d.ts +17 -0
  76. package/dist/arena/render/session.js +190 -0
  77. package/dist/arena/render/terminal.d.ts +34 -0
  78. package/dist/arena/render/terminal.js +286 -0
  79. package/dist/arena/strategies/discussion.d.ts +25 -0
  80. package/dist/arena/strategies/discussion.js +143 -0
  81. package/dist/arena/strategies/index.d.ts +15 -0
  82. package/dist/arena/strategies/index.js +28 -0
  83. package/dist/arena/strategies/language-wrapper.d.ts +17 -0
  84. package/dist/arena/strategies/language-wrapper.js +102 -0
  85. package/dist/arena/strategies/lens-wrapper.d.ts +16 -0
  86. package/dist/arena/strategies/lens-wrapper.js +236 -0
  87. package/dist/arena/strategies/planning.d.ts +30 -0
  88. package/dist/arena/strategies/planning.js +225 -0
  89. package/dist/arena/strategies/review.d.ts +26 -0
  90. package/dist/arena/strategies/review.js +168 -0
  91. package/dist/arena/strategies/utils.d.ts +39 -0
  92. package/dist/arena/strategies/utils.js +647 -0
  93. package/dist/arena/tools/selector.d.ts +17 -0
  94. package/dist/arena/tools/selector.js +61 -0
  95. package/dist/arena/transitions.d.ts +48 -0
  96. package/dist/arena/transitions.js +92 -0
  97. package/dist/arena/types.d.ts +508 -0
  98. package/dist/arena/types.js +27 -0
  99. package/dist/cli/agent-server-stdio.d.ts +32 -0
  100. package/dist/cli/agent-server-stdio.js +116 -0
  101. package/dist/colorizer.d.ts +23 -0
  102. package/dist/colorizer.js +12 -0
  103. package/dist/context/compaction.d.ts +116 -0
  104. package/dist/context/compaction.js +534 -0
  105. package/dist/context/manager.d.ts +145 -0
  106. package/dist/context/manager.js +423 -0
  107. package/dist/context/token-counter.d.ts +27 -0
  108. package/dist/context/token-counter.js +91 -0
  109. package/dist/context/tool-result-storage.d.ts +96 -0
  110. package/dist/context/tool-result-storage.js +296 -0
  111. package/dist/cost-tracker.d.ts +64 -0
  112. package/dist/cost-tracker.js +314 -0
  113. package/dist/cron/scheduler.d.ts +34 -0
  114. package/dist/cron/scheduler.js +115 -0
  115. package/dist/data/deepseek-models.json +46 -0
  116. package/dist/data/gemini-models.json +67 -0
  117. package/dist/data/openai-models.json +175 -0
  118. package/dist/data/openrouter-models.d.ts +35 -0
  119. package/dist/data/openrouter-models.js +36 -0
  120. package/dist/data/openrouter-models.json +4626 -0
  121. package/dist/data/openrouter-sync.d.ts +16 -0
  122. package/dist/data/openrouter-sync.js +53 -0
  123. package/dist/data/static-catalogs.d.ts +25 -0
  124. package/dist/data/static-catalogs.js +26 -0
  125. package/dist/data/zai-models.json +56 -0
  126. package/dist/engine/cost-store.d.ts +22 -0
  127. package/dist/engine/cost-store.js +15 -0
  128. package/dist/engine/engine.d.ts +295 -0
  129. package/dist/engine/engine.js +1467 -0
  130. package/dist/engine/model-facade.d.ts +24 -0
  131. package/dist/engine/model-facade.js +194 -0
  132. package/dist/engine/parse-task.d.ts +59 -0
  133. package/dist/engine/parse-task.js +135 -0
  134. package/dist/engine/patch-orphaned-tools.d.ts +52 -0
  135. package/dist/engine/patch-orphaned-tools.js +95 -0
  136. package/dist/engine/query.d.ts +50 -0
  137. package/dist/engine/query.js +107 -0
  138. package/dist/engine/runtime.d.ts +50 -0
  139. package/dist/engine/runtime.js +59 -0
  140. package/dist/engine/streaming-tool-queue.d.ts +34 -0
  141. package/dist/engine/streaming-tool-queue.js +61 -0
  142. package/dist/engine/token-budget.d.ts +22 -0
  143. package/dist/engine/token-budget.js +40 -0
  144. package/dist/engine/tool-summary.d.ts +13 -0
  145. package/dist/engine/tool-summary.js +35 -0
  146. package/dist/engine/turn-loop.d.ts +122 -0
  147. package/dist/engine/turn-loop.js +602 -0
  148. package/dist/engine/turn-state.d.ts +20 -0
  149. package/dist/engine/turn-state.js +17 -0
  150. package/dist/exceptions.d.ts +61 -0
  151. package/dist/exceptions.js +116 -0
  152. package/dist/git/utils.d.ts +35 -0
  153. package/dist/git/utils.js +98 -0
  154. package/dist/git/worktree.d.ts +36 -0
  155. package/dist/git/worktree.js +139 -0
  156. package/dist/hooks/events.d.ts +114 -0
  157. package/dist/hooks/events.js +4 -0
  158. package/dist/hooks/inject.d.ts +19 -0
  159. package/dist/hooks/inject.js +29 -0
  160. package/dist/hooks/registry.d.ts +15 -0
  161. package/dist/hooks/registry.js +84 -0
  162. package/dist/hooks/shell-runner.d.ts +45 -0
  163. package/dist/hooks/shell-runner.js +173 -0
  164. package/dist/index.d.ts +117 -0
  165. package/dist/index.js +145 -0
  166. package/dist/llm/api-key-sanitize.d.ts +22 -0
  167. package/dist/llm/api-key-sanitize.js +81 -0
  168. package/dist/llm/capabilities/index.d.ts +20 -0
  169. package/dist/llm/capabilities/index.js +39 -0
  170. package/dist/llm/capabilities/rules.d.ts +26 -0
  171. package/dist/llm/capabilities/rules.js +215 -0
  172. package/dist/llm/capabilities/types.d.ts +114 -0
  173. package/dist/llm/capabilities/types.js +24 -0
  174. package/dist/llm/client-base.d.ts +29 -0
  175. package/dist/llm/client-base.js +137 -0
  176. package/dist/llm/client-factory.d.ts +10 -0
  177. package/dist/llm/client-factory.js +29 -0
  178. package/dist/llm/model-cache.d.ts +21 -0
  179. package/dist/llm/model-cache.js +46 -0
  180. package/dist/llm/model-fetcher.d.ts +29 -0
  181. package/dist/llm/model-fetcher.js +282 -0
  182. package/dist/llm/model-pool.d.ts +95 -0
  183. package/dist/llm/model-pool.js +246 -0
  184. package/dist/llm/provider-catalog.d.ts +32 -0
  185. package/dist/llm/provider-catalog.js +50 -0
  186. package/dist/llm/provider-kinds.d.ts +21 -0
  187. package/dist/llm/provider-kinds.js +132 -0
  188. package/dist/llm/providers/anthropic.d.ts +19 -0
  189. package/dist/llm/providers/anthropic.js +267 -0
  190. package/dist/llm/providers/openai.d.ts +64 -0
  191. package/dist/llm/providers/openai.js +606 -0
  192. package/dist/llm/retry.d.ts +12 -0
  193. package/dist/llm/retry.js +17 -0
  194. package/dist/llm/stream-watchdog.d.ts +42 -0
  195. package/dist/llm/stream-watchdog.js +70 -0
  196. package/dist/llm/token-counter.d.ts +17 -0
  197. package/dist/llm/token-counter.js +36 -0
  198. package/dist/llm/types.d.ts +37 -0
  199. package/dist/llm/types.js +4 -0
  200. package/dist/logging/logger.d.ts +109 -0
  201. package/dist/logging/logger.js +390 -0
  202. package/dist/logging/sanitize-messages.d.ts +42 -0
  203. package/dist/logging/sanitize-messages.js +150 -0
  204. package/dist/logging/session-recorder.d.ts +90 -0
  205. package/dist/logging/session-recorder.js +304 -0
  206. package/dist/lsp/client.d.ts +41 -0
  207. package/dist/lsp/client.js +166 -0
  208. package/dist/lsp/manager.d.ts +40 -0
  209. package/dist/lsp/manager.js +124 -0
  210. package/dist/lsp/servers.d.ts +16 -0
  211. package/dist/lsp/servers.js +60 -0
  212. package/dist/migrate-models.d.ts +45 -0
  213. package/dist/migrate-models.js +179 -0
  214. package/dist/onboarding.d.ts +189 -0
  215. package/dist/onboarding.js +625 -0
  216. package/dist/plugins/gitOps.d.ts +32 -0
  217. package/dist/plugins/gitOps.js +62 -0
  218. package/dist/plugins/installedPlugins.d.ts +15 -0
  219. package/dist/plugins/installedPlugins.js +56 -0
  220. package/dist/plugins/knownMarketplaces.d.ts +10 -0
  221. package/dist/plugins/knownMarketplaces.js +46 -0
  222. package/dist/plugins/loadPluginHooks.d.ts +52 -0
  223. package/dist/plugins/loadPluginHooks.js +162 -0
  224. package/dist/plugins/marketplaceManager.d.ts +49 -0
  225. package/dist/plugins/marketplaceManager.js +142 -0
  226. package/dist/plugins/parseMarketplaceInput.d.ts +16 -0
  227. package/dist/plugins/parseMarketplaceInput.js +73 -0
  228. package/dist/plugins/pluginCommandHook.d.ts +54 -0
  229. package/dist/plugins/pluginCommandHook.js +180 -0
  230. package/dist/plugins/pluginCommandsLoader.d.ts +25 -0
  231. package/dist/plugins/pluginCommandsLoader.js +114 -0
  232. package/dist/plugins/pluginInstaller.d.ts +36 -0
  233. package/dist/plugins/pluginInstaller.js +228 -0
  234. package/dist/plugins/schemas.d.ts +9 -0
  235. package/dist/plugins/schemas.js +136 -0
  236. package/dist/plugins/types.d.ts +75 -0
  237. package/dist/plugins/types.js +7 -0
  238. package/dist/plugins/varRewrite.d.ts +42 -0
  239. package/dist/plugins/varRewrite.js +129 -0
  240. package/dist/preset/index.d.ts +60 -0
  241. package/dist/preset/index.js +159 -0
  242. package/dist/product/define.d.ts +60 -0
  243. package/dist/product/define.js +123 -0
  244. package/dist/product/index.d.ts +5 -0
  245. package/dist/product/index.js +4 -0
  246. package/dist/product/types.d.ts +111 -0
  247. package/dist/product/types.js +21 -0
  248. package/dist/prompt/composer.d.ts +49 -0
  249. package/dist/prompt/composer.js +168 -0
  250. package/dist/prompt/instruction-scanner.d.ts +54 -0
  251. package/dist/prompt/instruction-scanner.js +187 -0
  252. package/dist/prompt/section-cache.d.ts +15 -0
  253. package/dist/prompt/section-cache.js +31 -0
  254. package/dist/prompt/section-loader.d.ts +26 -0
  255. package/dist/prompt/section-loader.js +53 -0
  256. package/dist/prompt/sections/base.md +35 -0
  257. package/dist/prompt/sections/coding.md +31 -0
  258. package/dist/prompt/sections/orchestration.md +16 -0
  259. package/dist/prompt/sections/tone.md +9 -0
  260. package/dist/protocol/chat-session-manager.d.ts +33 -0
  261. package/dist/protocol/chat-session-manager.js +65 -0
  262. package/dist/protocol/chat-session.d.ts +49 -0
  263. package/dist/protocol/chat-session.js +86 -0
  264. package/dist/protocol/client.d.ts +104 -0
  265. package/dist/protocol/client.js +260 -0
  266. package/dist/protocol/factories.d.ts +93 -0
  267. package/dist/protocol/factories.js +64 -0
  268. package/dist/protocol/helpers.d.ts +42 -0
  269. package/dist/protocol/helpers.js +55 -0
  270. package/dist/protocol/index.d.ts +4 -0
  271. package/dist/protocol/index.js +4 -0
  272. package/dist/protocol/server.d.ts +80 -0
  273. package/dist/protocol/server.js +855 -0
  274. package/dist/protocol/transport.d.ts +37 -0
  275. package/dist/protocol/transport.js +85 -0
  276. package/dist/protocol/types.d.ts +221 -0
  277. package/dist/protocol/types.js +61 -0
  278. package/dist/remote/bridge.d.ts +37 -0
  279. package/dist/remote/bridge.js +103 -0
  280. package/dist/run/ArtifactTracker.d.ts +38 -0
  281. package/dist/run/ArtifactTracker.js +132 -0
  282. package/dist/run/CheckpointWriter.d.ts +53 -0
  283. package/dist/run/CheckpointWriter.js +135 -0
  284. package/dist/run/EngineRunner.d.ts +72 -0
  285. package/dist/run/EngineRunner.js +119 -0
  286. package/dist/run/Evaluator.d.ts +56 -0
  287. package/dist/run/Evaluator.js +62 -0
  288. package/dist/run/FileRunStore.d.ts +39 -0
  289. package/dist/run/FileRunStore.js +176 -0
  290. package/dist/run/Heartbeat.d.ts +54 -0
  291. package/dist/run/Heartbeat.js +123 -0
  292. package/dist/run/RunApprovalBackend.d.ts +53 -0
  293. package/dist/run/RunApprovalBackend.js +85 -0
  294. package/dist/run/RunLock.d.ts +40 -0
  295. package/dist/run/RunLock.js +98 -0
  296. package/dist/run/RunManager.d.ts +82 -0
  297. package/dist/run/RunManager.js +587 -0
  298. package/dist/run/RunQueue.d.ts +27 -0
  299. package/dist/run/RunQueue.js +73 -0
  300. package/dist/run/RunStore.d.ts +25 -0
  301. package/dist/run/RunStore.js +9 -0
  302. package/dist/run/factory.d.ts +63 -0
  303. package/dist/run/factory.js +51 -0
  304. package/dist/run/index.d.ts +17 -0
  305. package/dist/run/index.js +23 -0
  306. package/dist/run/types.d.ts +136 -0
  307. package/dist/run/types.js +28 -0
  308. package/dist/runtime/safe-spawn.d.ts +118 -0
  309. package/dist/runtime/safe-spawn.js +268 -0
  310. package/dist/services/analytics.d.ts +29 -0
  311. package/dist/services/analytics.js +84 -0
  312. package/dist/services/auto-dream.d.ts +49 -0
  313. package/dist/services/auto-dream.js +124 -0
  314. package/dist/services/diagnostics.d.ts +41 -0
  315. package/dist/services/diagnostics.js +99 -0
  316. package/dist/services/extract-memories.d.ts +31 -0
  317. package/dist/services/extract-memories.js +71 -0
  318. package/dist/services/index.d.ts +11 -0
  319. package/dist/services/index.js +17 -0
  320. package/dist/services/memory-orchestrator.d.ts +60 -0
  321. package/dist/services/memory-orchestrator.js +133 -0
  322. package/dist/services/notifier.d.ts +25 -0
  323. package/dist/services/notifier.js +69 -0
  324. package/dist/services/oauth.d.ts +27 -0
  325. package/dist/services/oauth.js +153 -0
  326. package/dist/services/session-memory.d.ts +37 -0
  327. package/dist/services/session-memory.js +81 -0
  328. package/dist/session/file-history.d.ts +41 -0
  329. package/dist/session/file-history.js +115 -0
  330. package/dist/session/memory.d.ts +104 -0
  331. package/dist/session/memory.js +310 -0
  332. package/dist/session/session-manager.d.ts +41 -0
  333. package/dist/session/session-manager.js +261 -0
  334. package/dist/session/transcript.d.ts +39 -0
  335. package/dist/session/transcript.js +186 -0
  336. package/dist/settings/manager.d.ts +36 -0
  337. package/dist/settings/manager.js +187 -0
  338. package/dist/settings/schema.d.ts +1219 -0
  339. package/dist/settings/schema.js +246 -0
  340. package/dist/skills/frontmatter.d.ts +13 -0
  341. package/dist/skills/frontmatter.js +70 -0
  342. package/dist/skills/index.d.ts +7 -0
  343. package/dist/skills/index.js +6 -0
  344. package/dist/skills/scanner.d.ts +31 -0
  345. package/dist/skills/scanner.js +171 -0
  346. package/dist/state.d.ts +160 -0
  347. package/dist/state.js +267 -0
  348. package/dist/tool-system/builtin/agent-notifications.d.ts +84 -0
  349. package/dist/tool-system/builtin/agent-notifications.js +197 -0
  350. package/dist/tool-system/builtin/agent-registry.d.ts +67 -0
  351. package/dist/tool-system/builtin/agent-registry.js +126 -0
  352. package/dist/tool-system/builtin/agent-transcript-translator.d.ts +22 -0
  353. package/dist/tool-system/builtin/agent-transcript-translator.js +160 -0
  354. package/dist/tool-system/builtin/agent.d.ts +16 -0
  355. package/dist/tool-system/builtin/agent.js +364 -0
  356. package/dist/tool-system/builtin/apply-patch/applier.d.ts +26 -0
  357. package/dist/tool-system/builtin/apply-patch/applier.js +250 -0
  358. package/dist/tool-system/builtin/apply-patch/index.d.ts +20 -0
  359. package/dist/tool-system/builtin/apply-patch/index.js +101 -0
  360. package/dist/tool-system/builtin/apply-patch/parser.d.ts +17 -0
  361. package/dist/tool-system/builtin/apply-patch/parser.js +212 -0
  362. package/dist/tool-system/builtin/apply-patch/seek-sequence.d.ts +18 -0
  363. package/dist/tool-system/builtin/apply-patch/seek-sequence.js +123 -0
  364. package/dist/tool-system/builtin/apply-patch/types.d.ts +49 -0
  365. package/dist/tool-system/builtin/apply-patch/types.js +13 -0
  366. package/dist/tool-system/builtin/arena.d.ts +31 -0
  367. package/dist/tool-system/builtin/arena.js +416 -0
  368. package/dist/tool-system/builtin/ask-user.d.ts +20 -0
  369. package/dist/tool-system/builtin/ask-user.js +99 -0
  370. package/dist/tool-system/builtin/bash.d.ts +21 -0
  371. package/dist/tool-system/builtin/bash.js +149 -0
  372. package/dist/tool-system/builtin/brief.d.ts +6 -0
  373. package/dist/tool-system/builtin/brief.js +41 -0
  374. package/dist/tool-system/builtin/config.d.ts +6 -0
  375. package/dist/tool-system/builtin/config.js +67 -0
  376. package/dist/tool-system/builtin/cron.d.ts +10 -0
  377. package/dist/tool-system/builtin/cron.js +61 -0
  378. package/dist/tool-system/builtin/edit.d.ts +6 -0
  379. package/dist/tool-system/builtin/edit.js +100 -0
  380. package/dist/tool-system/builtin/file-cache.d.ts +11 -0
  381. package/dist/tool-system/builtin/file-cache.js +39 -0
  382. package/dist/tool-system/builtin/glob.d.ts +7 -0
  383. package/dist/tool-system/builtin/glob.js +76 -0
  384. package/dist/tool-system/builtin/grep.d.ts +7 -0
  385. package/dist/tool-system/builtin/grep.js +150 -0
  386. package/dist/tool-system/builtin/index.d.ts +20 -0
  387. package/dist/tool-system/builtin/index.js +449 -0
  388. package/dist/tool-system/builtin/lsp.d.ts +6 -0
  389. package/dist/tool-system/builtin/lsp.js +141 -0
  390. package/dist/tool-system/builtin/mcp-tools.d.ts +10 -0
  391. package/dist/tool-system/builtin/mcp-tools.js +102 -0
  392. package/dist/tool-system/builtin/memory.d.ts +26 -0
  393. package/dist/tool-system/builtin/memory.js +226 -0
  394. package/dist/tool-system/builtin/notebook-edit.d.ts +6 -0
  395. package/dist/tool-system/builtin/notebook-edit.js +124 -0
  396. package/dist/tool-system/builtin/plan.d.ts +15 -0
  397. package/dist/tool-system/builtin/plan.js +55 -0
  398. package/dist/tool-system/builtin/powershell.d.ts +11 -0
  399. package/dist/tool-system/builtin/powershell.js +64 -0
  400. package/dist/tool-system/builtin/read.d.ts +6 -0
  401. package/dist/tool-system/builtin/read.js +70 -0
  402. package/dist/tool-system/builtin/remote-trigger.d.ts +6 -0
  403. package/dist/tool-system/builtin/remote-trigger.js +54 -0
  404. package/dist/tool-system/builtin/repl.d.ts +11 -0
  405. package/dist/tool-system/builtin/repl.js +82 -0
  406. package/dist/tool-system/builtin/send-message.d.ts +6 -0
  407. package/dist/tool-system/builtin/send-message.js +47 -0
  408. package/dist/tool-system/builtin/skill-prompt.d.ts +20 -0
  409. package/dist/tool-system/builtin/skill-prompt.js +52 -0
  410. package/dist/tool-system/builtin/skill.d.ts +9 -0
  411. package/dist/tool-system/builtin/skill.js +55 -0
  412. package/dist/tool-system/builtin/sleep.d.ts +6 -0
  413. package/dist/tool-system/builtin/sleep.js +33 -0
  414. package/dist/tool-system/builtin/task.d.ts +48 -0
  415. package/dist/tool-system/builtin/task.js +139 -0
  416. package/dist/tool-system/builtin/tool-search.d.ts +11 -0
  417. package/dist/tool-system/builtin/tool-search.js +90 -0
  418. package/dist/tool-system/builtin/web-fetch.d.ts +10 -0
  419. package/dist/tool-system/builtin/web-fetch.js +328 -0
  420. package/dist/tool-system/builtin/web-search.d.ts +25 -0
  421. package/dist/tool-system/builtin/web-search.js +174 -0
  422. package/dist/tool-system/builtin/worktree.d.ts +10 -0
  423. package/dist/tool-system/builtin/worktree.js +95 -0
  424. package/dist/tool-system/builtin/write.d.ts +6 -0
  425. package/dist/tool-system/builtin/write.js +36 -0
  426. package/dist/tool-system/context.d.ts +165 -0
  427. package/dist/tool-system/context.js +18 -0
  428. package/dist/tool-system/executor.d.ts +49 -0
  429. package/dist/tool-system/executor.js +495 -0
  430. package/dist/tool-system/investigation-guard.d.ts +46 -0
  431. package/dist/tool-system/investigation-guard.js +155 -0
  432. package/dist/tool-system/mcp-manager.d.ts +67 -0
  433. package/dist/tool-system/mcp-manager.js +249 -0
  434. package/dist/tool-system/permission.d.ts +111 -0
  435. package/dist/tool-system/permission.js +774 -0
  436. package/dist/tool-system/registry.d.ts +32 -0
  437. package/dist/tool-system/registry.js +131 -0
  438. package/dist/tool-system/sandbox/bwrap.d.ts +13 -0
  439. package/dist/tool-system/sandbox/bwrap.js +63 -0
  440. package/dist/tool-system/sandbox/index.d.ts +78 -0
  441. package/dist/tool-system/sandbox/index.js +222 -0
  442. package/dist/tool-system/sandbox/off.d.ts +2 -0
  443. package/dist/tool-system/sandbox/off.js +8 -0
  444. package/dist/tool-system/sandbox/seatbelt.d.ts +18 -0
  445. package/dist/tool-system/sandbox/seatbelt.js +109 -0
  446. package/dist/tool-system/task-guard.d.ts +29 -0
  447. package/dist/tool-system/task-guard.js +70 -0
  448. package/dist/tool-system/validation.d.ts +5 -0
  449. package/dist/tool-system/validation.js +39 -0
  450. package/dist/types.d.ts +376 -0
  451. package/dist/types.js +4 -0
  452. package/dist/updater.d.ts +65 -0
  453. package/dist/updater.js +405 -0
  454. package/dist/utils/debug.d.ts +41 -0
  455. package/dist/utils/debug.js +110 -0
  456. package/dist/utils/earlyInput.d.ts +43 -0
  457. package/dist/utils/earlyInput.js +166 -0
  458. package/dist/utils/env.d.ts +24 -0
  459. package/dist/utils/env.js +104 -0
  460. package/dist/utils/envUtils.d.ts +51 -0
  461. package/dist/utils/envUtils.js +135 -0
  462. package/dist/utils/execFileNoThrow.d.ts +39 -0
  463. package/dist/utils/execFileNoThrow.js +74 -0
  464. package/dist/utils/format.d.ts +44 -0
  465. package/dist/utils/format.js +236 -0
  466. package/dist/utils/intl.d.ts +22 -0
  467. package/dist/utils/intl.js +83 -0
  468. package/dist/utils/lockfile.d.ts +15 -0
  469. package/dist/utils/lockfile.js +30 -0
  470. package/dist/utils/memoize.d.ts +19 -0
  471. package/dist/utils/memoize.js +14 -0
  472. package/dist/utils/semver.d.ts +6 -0
  473. package/dist/utils/semver.js +13 -0
  474. package/dist/utils/sliceAnsi.d.ts +12 -0
  475. package/dist/utils/sliceAnsi.js +62 -0
  476. package/dist/utils/systemTheme.d.ts +40 -0
  477. package/dist/utils/systemTheme.js +108 -0
  478. package/dist/utils/task-sanitizer.d.ts +6 -0
  479. package/dist/utils/task-sanitizer.js +24 -0
  480. package/dist/utils/theme.d.ts +93 -0
  481. package/dist/utils/theme.js +544 -0
  482. package/dist/utils/toolDisplay.d.ts +31 -0
  483. package/dist/utils/toolDisplay.js +188 -0
  484. package/package.json +72 -0
@@ -0,0 +1,774 @@
1
+ /**
2
+ * Permission system — classifier + approval backend.
3
+ */
4
+ import { logger as rootPermLogger } from "../logging/logger.js";
5
+ export class HeadlessApprovalBackend {
6
+ mode;
7
+ constructor(mode) {
8
+ this.mode = mode;
9
+ }
10
+ async requestApproval(req) {
11
+ switch (this.mode) {
12
+ case "approve-all":
13
+ return { approved: true };
14
+ case "deny-all":
15
+ return { approved: false, reason: "headless deny-all mode" };
16
+ case "approve-read-only": {
17
+ const readOnlyTools = ["Read", "Glob", "Grep", "WebSearch", "WebFetch", "ToolSearch"];
18
+ if (readOnlyTools.includes(req.toolName)) {
19
+ return { approved: true };
20
+ }
21
+ return { approved: false, reason: "read-only mode: write operations denied" };
22
+ }
23
+ }
24
+ }
25
+ }
26
+ /**
27
+ * Auto approval backend — uses fast-path heuristics to auto-approve safe operations.
28
+ * Falls back to a delegate backend for uncertain cases.
29
+ */
30
+ export class AutoApprovalBackend {
31
+ delegate;
32
+ constructor(delegate) {
33
+ this.delegate = delegate;
34
+ }
35
+ async requestApproval(req) {
36
+ // Fast-path: auto-approve low-risk operations
37
+ if (req.riskLevel === "low") {
38
+ return { approved: true };
39
+ }
40
+ // Auto-approve common safe patterns
41
+ if (this.isSafeOperation(req)) {
42
+ return { approved: true };
43
+ }
44
+ // Auto-deny high-risk dangerous commands
45
+ if (req.riskLevel === "high") {
46
+ if (this.delegate) {
47
+ return this.delegate.requestApproval(req);
48
+ }
49
+ return {
50
+ approved: false,
51
+ reason: "auto mode: high-risk operation denied (no interactive approval available)",
52
+ };
53
+ }
54
+ // Medium risk: delegate if available, otherwise approve
55
+ if (this.delegate) {
56
+ return this.delegate.requestApproval(req);
57
+ }
58
+ return { approved: true };
59
+ }
60
+ isSafeOperation(req) {
61
+ const { toolName, args } = req;
62
+ // Write/Edit to known safe paths
63
+ if (toolName === "Write" || toolName === "Edit") {
64
+ const filePath = String(args.file_path ?? "");
65
+ // Allow writes to source code, tests, config, docs
66
+ if (/\.(ts|js|tsx|jsx|py|rs|go|java|c|cpp|h|css|html|json|yaml|yml|toml|md|txt|sh)$/.test(filePath)) {
67
+ return true;
68
+ }
69
+ }
70
+ // Safe bash commands
71
+ if (toolName === "Bash") {
72
+ 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
+ }
106
+ }
107
+ return false;
108
+ }
109
+ }
110
+ /**
111
+ * Interactive approval backend — prompts the user via a callback.
112
+ *
113
+ * Stores "always allow" decisions at two scopes:
114
+ * - session: in-memory map, lost when the process exits
115
+ * - project: persisted to <cwd>/.code-shell/settings.local.json so the
116
+ * same project remembers the rule across REPL restarts
117
+ *
118
+ * The classifier reads project rules from settings on session start (see
119
+ * Engine.buildPermissionConfig), so persisted rules become "auto-allow"
120
+ * the next time the user opens the project.
121
+ */
122
+ export class InteractiveApprovalBackend {
123
+ sessionRules = new Map();
124
+ promptFn = null;
125
+ cwd = null;
126
+ // Project rules saved during this session. Kept in-memory (not re-read from
127
+ // settings.local.json) so a stream of approvals all stay applied — the
128
+ // previous version passed only the freshly-added rule to the classifier,
129
+ // which silently dropped earlier approvals from the live classifier within
130
+ // the same session. We accumulate here and hand the full list to the callback.
131
+ savedProjectRules = [];
132
+ onProjectRules = null;
133
+ setPromptFn(fn) {
134
+ this.promptFn = fn;
135
+ }
136
+ /**
137
+ * Has someone installed a real prompt callback? Engine consults this
138
+ * to decide whether to use the interactive backend (= talk to a UI)
139
+ * or fall back to HeadlessApprovalBackend (= deny-all). Without it,
140
+ * the agent-server-stdio entry — which wires setInteractiveApprovalFn
141
+ * during server boot — would still fall through to deny-all because
142
+ * the engine couldn't tell the difference. */
143
+ hasPromptFn() {
144
+ return this.promptFn !== null;
145
+ }
146
+ /** Inject the project root so persistence writes to the right settings file. */
147
+ setCwd(cwd) {
148
+ this.cwd = cwd;
149
+ }
150
+ /**
151
+ * Inject a callback fired when the user approves "for this project". The
152
+ * callback receives the *full* accumulated list of project rules saved in
153
+ * this session, so the classifier can be reconfigured without losing
154
+ * earlier approvals.
155
+ */
156
+ setOnProjectRules(fn) {
157
+ this.onProjectRules = fn;
158
+ }
159
+ 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")
165
+ return { approved: false };
166
+ if (!this.promptFn) {
167
+ return { approved: false, reason: "interactive approval backend has no prompt function" };
168
+ }
169
+ const result = await this.promptFn(req);
170
+ const scope = result.scope ?? (result.always ? "session" : "once");
171
+ if (scope === "session" && result.always) {
172
+ this.sessionRules.set(req.toolName, result.approved ? "allow" : "deny");
173
+ }
174
+ else if (scope === "project" && result.approved) {
175
+ // Project-scope: persist a PermissionRule to settings.local.json.
176
+ // We only persist allow rules — denies stay session-only because a
177
+ // persisted deny is harder to recover from than a session deny.
178
+ const rule = buildProjectRule(req.toolName, req.args);
179
+ if (rule && this.cwd) {
180
+ try {
181
+ persistProjectRule(this.cwd, rule);
182
+ // Dedup against the in-memory list using the same equality used by
183
+ // persistProjectRule so re-prompts on the same tool/argsPattern
184
+ // don't bloat the live rule set.
185
+ const dup = this.savedProjectRules.some((r) => r.tool === rule.tool &&
186
+ r.decision === rule.decision &&
187
+ JSON.stringify(r.argsPattern) === JSON.stringify(rule.argsPattern));
188
+ if (!dup)
189
+ this.savedProjectRules.push(rule);
190
+ this.onProjectRules?.(this.savedProjectRules);
191
+ rootPermLogger.info("permission.persist", {
192
+ cat: "permission",
193
+ tool: rule.tool,
194
+ decision: rule.decision,
195
+ totalProjectRules: this.savedProjectRules.length,
196
+ duplicate: dup,
197
+ });
198
+ }
199
+ catch (err) {
200
+ rootPermLogger.error("permission.persist_failed", {
201
+ cat: "permission",
202
+ tool: rule.tool,
203
+ error: err.message,
204
+ });
205
+ // eslint-disable-next-line no-console
206
+ console.error("Failed to persist project permission rule:", err.message);
207
+ }
208
+ }
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");
212
+ }
213
+ return result;
214
+ }
215
+ }
216
+ /**
217
+ * Build a PermissionRule from a single approval. Bash narrows to the head
218
+ * 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.
221
+ */
222
+ function buildProjectRule(toolName, args) {
223
+ if (toolName === "Bash") {
224
+ const cmd = String(args.command ?? "").trim();
225
+ const head = cmd.split(/\s+/)[0];
226
+ if (!head)
227
+ return null;
228
+ return {
229
+ tool: "Bash",
230
+ argsPattern: { command: `^${escapeRegex(head)}(\\s|$)` },
231
+ decision: "allow",
232
+ reason: `Allowed via permission prompt: ${head}`,
233
+ };
234
+ }
235
+ return {
236
+ tool: toolName,
237
+ decision: "allow",
238
+ reason: "Allowed via permission prompt",
239
+ };
240
+ }
241
+ function escapeRegex(s) {
242
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
243
+ }
244
+ /**
245
+ * Append a rule to <cwd>/.code-shell/settings.local.json. Local file because
246
+ * permission grants are per-developer and shouldn't be checked into git.
247
+ * Idempotent: skips if an equivalent rule is already present.
248
+ */
249
+ function persistProjectRule(cwd, rule) {
250
+ const dir = `${cwd}/.code-shell`;
251
+ 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 });
258
+ let settings = {};
259
+ if (fs.existsSync(file)) {
260
+ try {
261
+ settings = JSON.parse(fs.readFileSync(file, "utf-8"));
262
+ }
263
+ catch {
264
+ /* start fresh on parse error */
265
+ }
266
+ }
267
+ if (!settings.permissions)
268
+ settings.permissions = {};
269
+ if (!settings.permissions.rules)
270
+ settings.permissions.rules = [];
271
+ // Dedup: same tool + same argsPattern.command (or no pattern) is a match
272
+ const exists = settings.permissions.rules.some((r) => r.tool === rule.tool &&
273
+ r.decision === "allow" &&
274
+ JSON.stringify(r.argsPattern) === JSON.stringify(rule.argsPattern));
275
+ if (exists)
276
+ return;
277
+ settings.permissions.rules.push(rule);
278
+ // Atomic write: stage to .tmp, then rename, so a crash mid-write can't
279
+ // 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);
283
+ }
284
+ // Singleton interactive backend for use by the UI
285
+ let _interactiveBackend = null;
286
+ export function getInteractiveApprovalBackend() {
287
+ if (!_interactiveBackend) {
288
+ _interactiveBackend = new InteractiveApprovalBackend();
289
+ }
290
+ return _interactiveBackend;
291
+ }
292
+ export function setInteractiveApprovalFn(fn) {
293
+ getInteractiveApprovalBackend().setPromptFn(fn);
294
+ }
295
+ const DANGEROUS_PATTERNS = [
296
+ /rm\s+-rf/,
297
+ /rm\s+-r\s+\//,
298
+ />\s*\/dev\/sd/,
299
+ /mkfs\./,
300
+ /dd\s+if=/,
301
+ /:(){ :|:& };:/,
302
+ /chmod\s+-R\s+777/,
303
+ /curl.*\|\s*(ba)?sh/,
304
+ /wget.*\|\s*(ba)?sh/,
305
+ /git\s+push\s+--force/,
306
+ /git\s+reset\s+--hard/,
307
+ /git\s+clean\s+-[fd]/,
308
+ />\s*\/etc\//,
309
+ /sudo\s+rm/,
310
+ /pkill\s+-9/,
311
+ /kill\s+-9/,
312
+ /shutdown/,
313
+ /reboot/,
314
+ /systemctl\s+(stop|disable|mask)/,
315
+ /DROP\s+(TABLE|DATABASE)/i,
316
+ /TRUNCATE\s+TABLE/i,
317
+ ];
318
+ const SAFE_READ_PATTERNS = [
319
+ /^(cat|head|tail|less|more|wc|file|stat|du|df)\s/,
320
+ /^(ls|tree|find|locate|which|whereis|type)\s/,
321
+ /^(grep|rg|ag|ack|fgrep|egrep)\s/,
322
+ /^(git\s+(status|log|diff|branch|show|blame|remote|tag|stash\s+list|rev-parse|describe))/,
323
+ /^(node|python|ruby|go|rustc|java|javac)\s+--version/,
324
+ /^(npm|pnpm|yarn|cargo|pip|gem|brew)\s+(list|ls|info|show|view|outdated|audit)/,
325
+ /^pwd$/,
326
+ /^whoami$/,
327
+ /^date$/,
328
+ /^(uname|hostname|id)\b/,
329
+ /^echo\s/,
330
+ /^env$/,
331
+ /^printenv/,
332
+ ];
333
+ const SAFE_WRITE_PATTERNS = [
334
+ /^(git\s+(add|commit|stash\s+(save|push)))/,
335
+ /^mkdir\s/,
336
+ /^touch\s/,
337
+ /^(npm|pnpm|yarn)\s+(install|add|remove|uninstall|run|test|build|lint|format)/,
338
+ /^(cargo|go|pip|gem)\s+(build|test|install|run|fmt|clippy|vet)/,
339
+ /^(tsc|eslint|prettier|vitest|jest|mocha|pytest)\b/,
340
+ /^make\b/,
341
+ /^(cp|mv)\s/,
342
+ ];
343
+ // ─── Shell metacharacter scanner ──────────────────────────────────
344
+ //
345
+ // A1 hardening: classifyBashCommand previously ran SAFE_READ_PATTERNS
346
+ // against the whole command string, so `ls -la; rm -rf /` would
347
+ // match /^ls\s/ and be returned as safe-read. We now scan the command
348
+ // once with quote/escape awareness:
349
+ //
350
+ // - top-level `;`, `&&`, `||`, newline split the command into
351
+ // segments. Every segment must independently classify as safe
352
+ // for the whole command to be safe.
353
+ // - unquoted command substitution (` ` ` or `$(` ), redirection
354
+ // (`>` `>>` `<` `<<`), process substitution (`<(` `>(`), and
355
+ // pipe-to-shell (`| sh`, `| bash`, ...) flag the command as
356
+ // dangerous.
357
+ // - quoted (`'…'`, `"…"`) and backslash-escaped characters are
358
+ // ignored — `echo "a; b"` is a single segment.
359
+ //
360
+ // This is intentionally detection-only, not a full shell parser. We
361
+ // only need to find unquoted metacharacters and split safely.
362
+ const PIPE_TO_SHELL_RE = /\|\s*(sh|bash|zsh|dash|ksh|fish|python|python3|node|nodejs|ruby|perl|php)\b/;
363
+ function scanShellCommand(input) {
364
+ const segments = [];
365
+ let buf = "";
366
+ let dangerous = false;
367
+ let i = 0;
368
+ // Track top-level quote state. We do NOT recurse into nested
369
+ // command substitutions for classification — the presence of `$(`
370
+ // or backticks already marks the command dangerous.
371
+ let quote = null;
372
+ const flush = () => {
373
+ const s = buf.trim();
374
+ if (s.length > 0)
375
+ segments.push(s);
376
+ buf = "";
377
+ };
378
+ while (i < input.length) {
379
+ const ch = input[i];
380
+ const next = input[i + 1];
381
+ if (quote === null) {
382
+ // Escape: skip the next character
383
+ if (ch === "\\" && next !== undefined) {
384
+ buf += ch + next;
385
+ i += 2;
386
+ continue;
387
+ }
388
+ // Enter quote
389
+ if (ch === "'" || ch === '"') {
390
+ quote = ch;
391
+ buf += ch;
392
+ i++;
393
+ continue;
394
+ }
395
+ // Command substitution: backtick or $(
396
+ if (ch === "`" || (ch === "$" && next === "(")) {
397
+ dangerous = true;
398
+ buf += ch;
399
+ i++;
400
+ continue;
401
+ }
402
+ // Process substitution: <( or >(
403
+ if ((ch === "<" || ch === ">") && next === "(") {
404
+ dangerous = true;
405
+ buf += ch;
406
+ i++;
407
+ continue;
408
+ }
409
+ // Redirection
410
+ if (ch === ">" || ch === "<") {
411
+ dangerous = true;
412
+ buf += ch;
413
+ i++;
414
+ continue;
415
+ }
416
+ // Top-level separators
417
+ if (ch === ";" || ch === "\n") {
418
+ flush();
419
+ i++;
420
+ continue;
421
+ }
422
+ if ((ch === "&" && next === "&") || (ch === "|" && next === "|")) {
423
+ flush();
424
+ i += 2;
425
+ continue;
426
+ }
427
+ // Background `&` is treated like `;`
428
+ if (ch === "&") {
429
+ flush();
430
+ i++;
431
+ continue;
432
+ }
433
+ buf += ch;
434
+ i++;
435
+ continue;
436
+ }
437
+ // Inside a quote
438
+ if (ch === "\\" && next !== undefined) {
439
+ buf += ch + next;
440
+ i += 2;
441
+ continue;
442
+ }
443
+ if (ch === quote) {
444
+ quote = null;
445
+ buf += ch;
446
+ i++;
447
+ continue;
448
+ }
449
+ buf += ch;
450
+ i++;
451
+ }
452
+ flush();
453
+ return { segments, dangerous };
454
+ }
455
+ function classifySegment(segment) {
456
+ if (DANGEROUS_PATTERNS.some((p) => p.test(segment)))
457
+ 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.
466
+ if (segment.includes("|")) {
467
+ const parts = segment.split("|").map((p) => p.trim());
468
+ if (parts.every((p) => SAFE_READ_PATTERNS.some((re) => re.test(p)))) {
469
+ return "safe-read";
470
+ }
471
+ }
472
+ return "unsafe";
473
+ }
474
+ const SAFETY_RANK = {
475
+ "safe-read": 3,
476
+ "safe-write": 2,
477
+ unsafe: 1,
478
+ dangerous: 0,
479
+ };
480
+ function minSafety(a, b) {
481
+ return SAFETY_RANK[a] <= SAFETY_RANK[b] ? a : b;
482
+ }
483
+ export function classifyBashCommand(command) {
484
+ const trimmed = command.trim();
485
+ if (trimmed.length === 0)
486
+ return "unsafe";
487
+ // Whole-command dangerous patterns (e.g. `curl ... | sh`) need to
488
+ // be checked against the raw text because some of them span
489
+ // separators we would split on.
490
+ if (DANGEROUS_PATTERNS.some((p) => p.test(trimmed)))
491
+ return "dangerous";
492
+ if (PIPE_TO_SHELL_RE.test(trimmed))
493
+ return "dangerous";
494
+ const scan = scanShellCommand(trimmed);
495
+ if (scan.dangerous)
496
+ return "dangerous";
497
+ if (scan.segments.length === 0)
498
+ return "unsafe";
499
+ // Every top-level segment must independently classify. The whole
500
+ // command's safety is the minimum across segments.
501
+ let overall = "safe-read";
502
+ for (const seg of scan.segments) {
503
+ overall = minSafety(overall, classifySegment(seg));
504
+ if (overall === "dangerous")
505
+ return "dangerous";
506
+ }
507
+ return overall;
508
+ }
509
+ export class DenialTracker {
510
+ denials = new Map();
511
+ periodMs;
512
+ maxDenials;
513
+ constructor(opts) {
514
+ this.periodMs = opts?.periodMs ?? 60 * 60 * 1000; // 1 hour
515
+ this.maxDenials = opts?.maxDenials ?? 5;
516
+ }
517
+ record(toolName) {
518
+ const now = Date.now();
519
+ const existing = this.denials.get(toolName);
520
+ if (existing && now - existing.firstAt < this.periodMs) {
521
+ existing.count++;
522
+ existing.lastAt = now;
523
+ }
524
+ else {
525
+ this.denials.set(toolName, { count: 1, firstAt: now, lastAt: now });
526
+ }
527
+ }
528
+ recordSuccess(toolName) {
529
+ this.denials.delete(toolName);
530
+ }
531
+ shouldWarn(toolName) {
532
+ const record = this.denials.get(toolName);
533
+ if (!record)
534
+ return false;
535
+ if (Date.now() - record.firstAt > this.periodMs) {
536
+ this.denials.delete(toolName);
537
+ return false;
538
+ }
539
+ return record.count >= this.maxDenials;
540
+ }
541
+ getWarningMessage(toolName) {
542
+ const record = this.denials.get(toolName);
543
+ if (!record)
544
+ return "";
545
+ return (`Tool "${toolName}" has been denied ${record.count} times in the last hour. ` +
546
+ `Please stop attempting this tool and try an alternative approach.`);
547
+ }
548
+ clear() {
549
+ this.denials.clear();
550
+ }
551
+ }
552
+ // A1 hardening: tools that `acceptEdits` mode will auto-allow without
553
+ // asking. Everything else falls through to `ask`. Read-only tools
554
+ // (Read/Glob/Grep) reach `allow` through rule matching or default
555
+ // classification; they do not need to be in this list.
556
+ //
557
+ // Keep this set tight. Adding a tool here means "the user is OK with
558
+ // this tool running silently when they opt into acceptEdits".
559
+ export const ACCEPT_EDITS_ALLOWLIST = new Set([
560
+ "Write",
561
+ "Edit",
562
+ "ApplyPatch",
563
+ "NotebookEdit",
564
+ "TodoWrite",
565
+ ]);
566
+ export class PermissionClassifier {
567
+ rules;
568
+ defaultMode;
569
+ approvalBackend;
570
+ denialTracker = new DenialTracker();
571
+ /**
572
+ * Turn-scoped logger so permission events (ask/decision/auto-deny) are
573
+ * tagged with the current turn/turnId. Falls back to the root logger so
574
+ * tests and ad-hoc usages still write log lines.
575
+ */
576
+ log = rootPermLogger;
577
+ constructor(rules, defaultMode = "default", approvalBackend = new HeadlessApprovalBackend("deny-all")) {
578
+ this.rules = rules;
579
+ this.defaultMode = defaultMode;
580
+ this.approvalBackend = approvalBackend;
581
+ }
582
+ setLogger(log) {
583
+ this.log = log;
584
+ }
585
+ /** Replace mode + backend in place so live tool execution sees the change. */
586
+ reconfigure(mode, approvalBackend, rules) {
587
+ this.defaultMode = mode;
588
+ this.approvalBackend = approvalBackend;
589
+ if (rules)
590
+ this.rules = rules;
591
+ }
592
+ getMode() {
593
+ return this.defaultMode;
594
+ }
595
+ classify(toolName, args) {
596
+ // bypassPermissions mode overrides everything
597
+ if (this.defaultMode === "bypassPermissions")
598
+ return "allow";
599
+ // Check explicit rules (ordered by specificity)
600
+ for (const rule of this.rules) {
601
+ if (this.matchesRule(rule, toolName, args)) {
602
+ return rule.decision;
603
+ }
604
+ }
605
+ // YOLO classifier for Bash commands
606
+ if (toolName === "Bash") {
607
+ const level = classifyBashCommand(String(args.command ?? ""));
608
+ if (level === "dangerous")
609
+ return "ask";
610
+ if (level === "safe-read")
611
+ return "allow";
612
+ if (level === "safe-write" &&
613
+ (this.defaultMode === "acceptEdits" || this.defaultMode === "auto")) {
614
+ return "allow";
615
+ }
616
+ if (level === "unsafe")
617
+ return "ask";
618
+ }
619
+ // Fall back to default mode
620
+ switch (this.defaultMode) {
621
+ case "dontAsk":
622
+ return "deny";
623
+ case "acceptEdits":
624
+ // A1 hardening: acceptEdits is an allowlist, not allow-all.
625
+ // Only auto-allow tools that mutate the workspace in ways the
626
+ // user has explicitly opted in to. Everything else (network,
627
+ // shell, MCP, etc.) still requires an interactive prompt.
628
+ return ACCEPT_EDITS_ALLOWLIST.has(toolName) ? "allow" : "ask";
629
+ default:
630
+ return "ask";
631
+ }
632
+ }
633
+ async handleAsk(toolName, args, reason) {
634
+ if (this.defaultMode === "dontAsk") {
635
+ this.log.info("permission.auto_deny", {
636
+ cat: "permission",
637
+ tool: toolName,
638
+ reason: "dontAsk_mode",
639
+ });
640
+ return false;
641
+ }
642
+ if (this.defaultMode === "bypassPermissions") {
643
+ this.log.info("permission.auto_allow", {
644
+ cat: "permission",
645
+ tool: toolName,
646
+ reason: "bypassPermissions_mode",
647
+ });
648
+ return true;
649
+ }
650
+ // Check denial tracker — if too many denials, auto-deny
651
+ if (this.denialTracker.shouldWarn(toolName)) {
652
+ this.log.warn("permission.auto_deny", {
653
+ cat: "permission",
654
+ tool: toolName,
655
+ reason: "denial_tracker_threshold",
656
+ });
657
+ return false;
658
+ }
659
+ const riskLevel = this.assessRisk(toolName, args);
660
+ const span = this.log.span("permission.ask", {
661
+ cat: "permission",
662
+ tool: toolName,
663
+ riskLevel,
664
+ });
665
+ let result;
666
+ try {
667
+ const baseDescription = this.describeToolCall(toolName, args);
668
+ const description = reason
669
+ ? `${baseDescription}\n\nReason (from pre_tool_use hook): ${reason}`
670
+ : baseDescription;
671
+ result = await this.approvalBackend.requestApproval({
672
+ toolName,
673
+ args,
674
+ description,
675
+ riskLevel,
676
+ });
677
+ }
678
+ catch (err) {
679
+ span.fail(err, { tool: toolName });
680
+ throw err;
681
+ }
682
+ if (result.approved) {
683
+ this.denialTracker.recordSuccess(toolName);
684
+ }
685
+ else {
686
+ this.denialTracker.record(toolName);
687
+ }
688
+ span.end({
689
+ tool: toolName,
690
+ approved: result.approved,
691
+ // `scope` is set by the interactive backend when the user picks
692
+ // "for this session" / "for this project"; absent for one-shot answers.
693
+ scope: result.scope,
694
+ reason: !result.approved ? result.reason : undefined,
695
+ });
696
+ return result.approved;
697
+ }
698
+ /** Get denial warning message if the model keeps getting denied. */
699
+ getDenialWarning(toolName) {
700
+ if (this.denialTracker.shouldWarn(toolName)) {
701
+ return this.denialTracker.getWarningMessage(toolName);
702
+ }
703
+ return undefined;
704
+ }
705
+ 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;
743
+ }
744
+ isDangerousCommand(args) {
745
+ const command = String(args.command ?? "");
746
+ return DANGEROUS_PATTERNS.some((p) => p.test(command));
747
+ }
748
+ assessRisk(toolName, args) {
749
+ if (toolName === "Bash" && this.isDangerousCommand(args))
750
+ return "high";
751
+ if (["Write", "Edit"].includes(toolName))
752
+ return "medium";
753
+ if (toolName === "Bash")
754
+ return "medium";
755
+ return "low";
756
+ }
757
+ /**
758
+ * Short, human-friendly subtitle. The UI shows this under the tool title;
759
+ * full args (command bodies, diffs, etc.) come from the args field, so this
760
+ * stays compact — no truncation here, just a label.
761
+ */
762
+ describeToolCall(toolName, args) {
763
+ switch (toolName) {
764
+ case "Bash":
765
+ return String(args.description ?? "Run shell command");
766
+ case "Write":
767
+ return `Create or overwrite ${args.file_path}`;
768
+ case "Edit":
769
+ return `Modify ${args.file_path}`;
770
+ default:
771
+ return toolName;
772
+ }
773
+ }
774
+ }