@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
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  UI-agnostic agent orchestration framework. The headless core of [`code-shell`](https://github.com/cjhyy/codeshell): turn an LLM + a tool registry + a transcript into a multi-turn agent loop, with permissions, hooks, MCP support, and pluggable approval backends.
4
4
 
5
- Compatible with Anthropic and OpenAI-protocol providers (Claude, DeepSeek, GPT, Gemini, Qwen, …). No terminal UI dependencies — embed it in a CLI, a web service, a Slack bot, a desktop app.
5
+ Compatible with Anthropic and OpenAI-protocol providers (Claude, DeepSeek, GPT, Gemini, Qwen, …). No terminal UI dependencies — embed it in a CLI, a web service, a Slack bot, a desktop app. This is a Node.js runtime package, not a browser SDK.
6
6
 
7
7
  ## Install
8
8
 
@@ -12,7 +12,7 @@ npm install @cjhyy/code-shell-core
12
12
  bun add @cjhyy/code-shell-core
13
13
  ```
14
14
 
15
- Requires Node ≥ 20.10.
15
+ Requires Node ≥ 20.10 and an ESM-capable runtime.
16
16
 
17
17
  ## Quickstart
18
18
 
@@ -29,8 +29,10 @@ const engine = new Engine({
29
29
  apiKey: process.env.ANTHROPIC_API_KEY!,
30
30
  },
31
31
  cwd: process.cwd(),
32
- // Headless approval — "approve-all" trusts the model fully; use
33
- // "approve-read-only" or a custom ApprovalBackend in production.
32
+ // Headless approval — "approve-all" trusts the model fully and is
33
+ // only appropriate for local/trusted prompts. Use "approve-read-only",
34
+ // explicit permissionMode/tool allowlists, or a custom ApprovalBackend
35
+ // in production.
34
36
  approvalBackend: new HeadlessApprovalBackend("approve-all"),
35
37
  headless: true,
36
38
  });
@@ -78,7 +80,7 @@ new Engine({
78
80
 
79
81
  ## Designing for production
80
82
 
81
- - **Approval backend.** `HeadlessApprovalBackend("approve-all")` is fine for trusted prompts. For untrusted input, implement `ApprovalBackend` to gate destructive tools (Write/Edit/Bash) through your own auth.
83
+ - **Approval backend.** `HeadlessApprovalBackend("approve-all")` is fine only for local/trusted prompts. For untrusted input, explicitly set `permissionMode`, restrict tools with `enabledBuiltinTools` / `disabledBuiltinTools`, or implement `ApprovalBackend` to gate destructive tools (Write/Edit/Bash) through your own auth.
82
84
  - **Permission rules.** Pass `permissionRules` to the engine for fine-grained allow/deny per tool + args pattern. Bash commands additionally get a built-in safety classifier (dangerous commands like `rm -rf` always require explicit approval).
83
85
  - **Hooks** are the right place for audit logging, prompt rewriting, and policy enforcement.
84
86
  - **Memory.** The engine ships with an extract-memories + auto-dream pipeline that persists to `~/.code-shell/memory/`. Disable by passing a no-op `MemoryOrchestrator`, or scope to a project by setting `CODE_SHELL_HOME`.
@@ -144,11 +146,12 @@ These exports are covered by the stability promise:
144
146
 
145
147
  ### Treat as internal
146
148
 
147
- Anything not in the table above — including direct imports from
148
- `./engine/...`, `./tool-system/...`, `./session/...`, `./hooks/...`
149
- subpaths and APIs marked `@internal` in their docstrings may change
150
- between minor versions without notice. Plan to migrate via the protocol
151
- surface instead of deep-importing.
149
+ Anything not in the table above — including root-entry exports for
150
+ product/runtime helpers and direct imports from `./engine/...`,
151
+ `./tool-system/...`, `./session/...`, `./hooks/...` subpathsis
152
+ experimental/internal unless explicitly documented otherwise, and may
153
+ change between minor versions without notice. Plan to migrate via the
154
+ protocol surface instead of deep-importing.
152
155
 
153
156
  ## Stability
154
157
 
@@ -1,13 +1,31 @@
1
1
  import { type AgentDefinition } from "./agent-definition.js";
2
+ export interface AgentSourceDir {
3
+ dir: string;
4
+ source: "project" | "user" | "plugin";
5
+ /** For source === "plugin": the owning plugin name, carried onto each def
6
+ * so the spawn layer can namespace bare skill allowlists. */
7
+ pluginName?: string;
8
+ }
2
9
  /**
3
- * Loads reusable sub-agent role definitions from a directory of `*.md` files.
4
- * Non-recursive. Malformed files are skipped with a warning rather than
5
- * failing the whole load — one bad role file must not break the agent system.
10
+ * Loads reusable sub-agent role definitions from one or more directories
11
+ * of `*.md` files. Non-recursive. Malformed files are skipped with a
12
+ * warning rather than failing the whole load — one bad role file must
13
+ * not break the agent system.
14
+ *
15
+ * When multiple dirs define the same role name, the LAST dir wins. This is
16
+ * pure mechanism — the project>user POLICY lives in the caller's dir ORDER
17
+ * (loadAgentDefinitionsForCwd passes [user, ...plugins, project] so a repo's
18
+ * in-tree agent wins; see spec §7.2). The shadowing def is marked
19
+ * `override: true` and records the sources it shadowed in `shadowedSources`.
20
+ * Names in `disabled` are filtered out entirely after the merge, so the LLM
21
+ * never sees them.
6
22
  */
7
23
  export declare class AgentDefinitionRegistry {
8
24
  private defs;
9
25
  readonly warnings: string[];
26
+ /** Back-compat single-dir loader (project source, no disabled filter). */
10
27
  static loadFromDir(dir: string): AgentDefinitionRegistry;
28
+ static loadFromDirs(dirs: AgentSourceDir[], disabled: string[]): AgentDefinitionRegistry;
11
29
  has(name: string): boolean;
12
30
  get(name: string): AgentDefinition | undefined;
13
31
  list(): AgentDefinition[];
@@ -2,35 +2,66 @@ import { readdirSync, readFileSync, existsSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { parseAgentDefinition } from "./agent-definition.js";
4
4
  /**
5
- * Loads reusable sub-agent role definitions from a directory of `*.md` files.
6
- * Non-recursive. Malformed files are skipped with a warning rather than
7
- * failing the whole load — one bad role file must not break the agent system.
5
+ * Loads reusable sub-agent role definitions from one or more directories
6
+ * of `*.md` files. Non-recursive. Malformed files are skipped with a
7
+ * warning rather than failing the whole load — one bad role file must
8
+ * not break the agent system.
9
+ *
10
+ * When multiple dirs define the same role name, the LAST dir wins. This is
11
+ * pure mechanism — the project>user POLICY lives in the caller's dir ORDER
12
+ * (loadAgentDefinitionsForCwd passes [user, ...plugins, project] so a repo's
13
+ * in-tree agent wins; see spec §7.2). The shadowing def is marked
14
+ * `override: true` and records the sources it shadowed in `shadowedSources`.
15
+ * Names in `disabled` are filtered out entirely after the merge, so the LLM
16
+ * never sees them.
8
17
  */
9
18
  export class AgentDefinitionRegistry {
10
19
  defs = new Map();
11
20
  warnings = [];
21
+ /** Back-compat single-dir loader (project source, no disabled filter). */
12
22
  static loadFromDir(dir) {
23
+ return AgentDefinitionRegistry.loadFromDirs([{ dir, source: "project" }], []);
24
+ }
25
+ static loadFromDirs(dirs, disabled) {
13
26
  const reg = new AgentDefinitionRegistry();
14
- if (!existsSync(dir) || !statSync(dir).isDirectory())
15
- return reg;
16
- for (const entry of readdirSync(dir).sort()) {
17
- if (!entry.endsWith(".md"))
27
+ for (const { dir, source, pluginName } of dirs) {
28
+ if (!existsSync(dir) || !statSync(dir).isDirectory())
18
29
  continue;
19
- const full = join(dir, entry);
20
- try {
21
- if (!statSync(full).isFile())
22
- continue;
23
- const def = parseAgentDefinition(readFileSync(full, "utf8"), entry);
24
- if (reg.defs.has(def.name)) {
25
- reg.warnings.push(`${entry}: duplicate agent name '${def.name}' ignored (first definition wins)`);
30
+ for (const entry of readdirSync(dir).sort()) {
31
+ if (!entry.endsWith(".md"))
26
32
  continue;
33
+ const full = join(dir, entry);
34
+ try {
35
+ if (!statSync(full).isFile())
36
+ continue;
37
+ const def = parseAgentDefinition(readFileSync(full, "utf8"), entry);
38
+ def.source = source;
39
+ if (pluginName)
40
+ def.pluginName = pluginName;
41
+ def.filePath = full;
42
+ // A later dir overriding an earlier one. Carry forward the shadowed
43
+ // sources (the prev def's own source + anything it already shadowed)
44
+ // so a project def that shadows a plugin def that shadowed a user
45
+ // def reports both.
46
+ const prev = reg.defs.get(def.name);
47
+ if (prev) {
48
+ def.override = true;
49
+ const shadowed = new Set(prev.shadowedSources ?? []);
50
+ if (prev.source)
51
+ shadowed.add(prev.source);
52
+ def.shadowedSources = [...shadowed];
53
+ }
54
+ reg.defs.set(def.name, def);
55
+ }
56
+ catch (err) {
57
+ reg.warnings.push(`${entry}: ${err.message}`);
27
58
  }
28
- reg.defs.set(def.name, def);
29
- }
30
- catch (err) {
31
- reg.warnings.push(`${entry}: ${err.message}`);
32
59
  }
33
60
  }
61
+ // Filter disabled roles after the merge so a user override of a
62
+ // disabled name is still removed.
63
+ for (const name of new Set(disabled))
64
+ reg.defs.delete(name);
34
65
  return reg;
35
66
  }
36
67
  has(name) {
@@ -10,11 +10,43 @@ export interface AgentDefinition {
10
10
  maxTurns?: number;
11
11
  /** Optional tool allowlist. Undefined → inherit parent's full tool set. */
12
12
  tools?: string[];
13
+ /**
14
+ * Optional skill allowlist. Undefined → inherit the parent's full skill
15
+ * pool (every non-disabled skill in the project). When set, the sub-agent
16
+ * physically only sees these skills: they're the only ones listed in its
17
+ * system prompt and the only ones it may invoke. Empty array → no skills.
18
+ */
19
+ skills?: string[];
13
20
  /** Markdown body — becomes the child Engine's appendSystemPrompt. */
14
21
  systemPrompt: string;
22
+ /** Where this def was loaded from. Runtime-only; never serialized. */
23
+ source?: "project" | "user" | "plugin";
24
+ /**
25
+ * Owning plugin name when source === "plugin" (e.g. "mimi-video"). Runtime-
26
+ * only; never serialized. Used to namespace this agent's bare skill
27
+ * allowlist into `<pluginName>:<skill>` at spawn time, since a plugin's
28
+ * frontmatter references its own skills by bare name (CC convention) but
29
+ * the scanner registers them namespaced. Undefined for project/user agents.
30
+ */
31
+ pluginName?: string;
32
+ /** Absolute path of the file it came from. Runtime-only. */
33
+ filePath?: string;
34
+ /** True when this def shadows a same-named def from a lower-priority dir. */
35
+ override?: boolean;
36
+ /** Sources whose same-named def this one shadows (runtime-only). Drives the
37
+ * UI "this project overrides your user version" warning (spec §7.2). */
38
+ shadowedSources?: Array<"project" | "user" | "plugin">;
15
39
  }
16
40
  /**
17
41
  * Parse a Markdown agent-definition file (YAML frontmatter + body).
18
42
  * Pure: no filesystem access. `sourceName` is only used in error messages.
19
43
  */
20
44
  export declare function parseAgentDefinition(raw: string, sourceName: string): AgentDefinition;
45
+ /**
46
+ * Serialize an AgentDefinition back to a Markdown file body (YAML
47
+ * frontmatter + system prompt). Inverse of parseAgentDefinition.
48
+ * Optional fields that are unset are omitted entirely so an inheriting
49
+ * role (e.g. model undefined → inherit parent) stays clean on disk.
50
+ * Runtime-only metadata (source/filePath/override) is never written.
51
+ */
52
+ export declare function serializeAgentDefinition(def: AgentDefinition): string;
@@ -1,4 +1,23 @@
1
- import { parse as parseYaml } from "yaml";
1
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
2
+ /**
3
+ * Normalize a frontmatter `tools:` / `skills:` value into a string[].
4
+ * Accepts both a YAML list (`[a, b]`) and a comma/whitespace-separated
5
+ * string (`a, b`) — CC-lineage agent files use either form. Returns
6
+ * undefined when the field is absent or not a usable shape, so the caller
7
+ * keeps the "inherit parent" default.
8
+ */
9
+ function normalizeNameList(value) {
10
+ if (Array.isArray(value)) {
11
+ return value.filter((t) => typeof t === "string");
12
+ }
13
+ if (typeof value === "string") {
14
+ return value
15
+ .split(/[,\s]+/)
16
+ .map((s) => s.trim())
17
+ .filter((s) => s.length > 0);
18
+ }
19
+ return undefined;
20
+ }
2
21
  /**
3
22
  * Parse a Markdown agent-definition file (YAML frontmatter + body).
4
23
  * Pure: no filesystem access. `sourceName` is only used in error messages.
@@ -31,8 +50,34 @@ export function parseAgentDefinition(raw, sourceName) {
31
50
  def.model = fm.model.trim();
32
51
  if (typeof fm.maxTurns === "number")
33
52
  def.maxTurns = fm.maxTurns;
34
- if (Array.isArray(fm.tools)) {
35
- def.tools = fm.tools.filter((t) => typeof t === "string");
36
- }
53
+ const tools = normalizeNameList(fm.tools);
54
+ if (tools !== undefined)
55
+ def.tools = tools;
56
+ const skills = normalizeNameList(fm.skills);
57
+ if (skills !== undefined)
58
+ def.skills = skills;
37
59
  return def;
38
60
  }
61
+ /**
62
+ * Serialize an AgentDefinition back to a Markdown file body (YAML
63
+ * frontmatter + system prompt). Inverse of parseAgentDefinition.
64
+ * Optional fields that are unset are omitted entirely so an inheriting
65
+ * role (e.g. model undefined → inherit parent) stays clean on disk.
66
+ * Runtime-only metadata (source/filePath/override) is never written.
67
+ */
68
+ export function serializeAgentDefinition(def) {
69
+ const fm = {
70
+ name: def.name,
71
+ description: def.description,
72
+ };
73
+ if (def.model !== undefined)
74
+ fm.model = def.model;
75
+ if (def.maxTurns !== undefined)
76
+ fm.maxTurns = def.maxTurns;
77
+ if (def.tools !== undefined)
78
+ fm.tools = def.tools;
79
+ if (def.skills !== undefined)
80
+ fm.skills = def.skills;
81
+ const yaml = stringifyYaml(fm).trimEnd();
82
+ return `---\n${yaml}\n---\n${def.systemPrompt}\n`;
83
+ }
@@ -28,6 +28,15 @@ import { runDetailExpansion } from "./phases/planning-detail-expansion.js";
28
28
  import { withLanguage } from "./strategies/language-wrapper.js";
29
29
  import { ArenaLedger } from "./ledger.js";
30
30
  import { registerClaims } from "./phases/claim-registry.js";
31
+ /** Bucket claims by status. Shared by the two consensus phases. */
32
+ function summarizeClaims(all) {
33
+ return {
34
+ verified: all.filter((c) => c.status === "verified"),
35
+ contested: all.filter((c) => c.status === "contested"),
36
+ unresolved: all.filter((c) => c.status === "unresolved"),
37
+ rejected: all.filter((c) => c.status === "rejected"),
38
+ };
39
+ }
31
40
  export class Arena {
32
41
  config;
33
42
  strategy;
@@ -192,12 +201,7 @@ export class Arena {
192
201
  signal?.throwIfAborted();
193
202
  // ─── Roadmap Consensus ───────────────────────────────────
194
203
  const allClaims = ledger.getAllClaims();
195
- const claimSummary = {
196
- verified: allClaims.filter((c) => c.status === "verified"),
197
- contested: allClaims.filter((c) => c.status === "contested"),
198
- unresolved: allClaims.filter((c) => c.status === "unresolved"),
199
- rejected: allClaims.filter((c) => c.status === "rejected"),
200
- };
204
+ const claimSummary = summarizeClaims(allClaims);
201
205
  logger.info("arena.planning_consensus_phase", { concluder: concluder.name });
202
206
  const consensus = await buildConsensus({
203
207
  concluder,
@@ -295,12 +299,7 @@ export class Arena {
295
299
  signal?.throwIfAborted();
296
300
  // ─── Consensus (claim-aware) ─────────────────────────────
297
301
  const allClaims = ledger.getAllClaims();
298
- const claimSummary = {
299
- verified: allClaims.filter((c) => c.status === "verified"),
300
- contested: allClaims.filter((c) => c.status === "contested"),
301
- unresolved: allClaims.filter((c) => c.status === "unresolved"),
302
- rejected: allClaims.filter((c) => c.status === "rejected"),
303
- };
302
+ const claimSummary = summarizeClaims(allClaims);
304
303
  logger.info("arena.consensus_phase", {
305
304
  concluder: concluder.name,
306
305
  verified: claimSummary.verified.length,
@@ -6,9 +6,10 @@
6
6
  * to prevent command injection. File paths are validated against the
7
7
  * repository boundary to prevent path traversal.
8
8
  */
9
- import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
9
+ import { readFileSync, existsSync, readdirSync, statSync, realpathSync } from "node:fs";
10
10
  import { execFileSync } from "node:child_process";
11
- import { resolve, join } from "node:path";
11
+ import { resolve, join, dirname, basename } from "node:path";
12
+ import { isWithinRoot } from "./within-root.js";
12
13
  export const MAX_TOOL_RESULT = 15_000; // chars per tool call
13
14
  // 5 was too generous for thinking-mode models — they'd spend the
14
15
  // entire budget on read_file with textLen=0, then need an
@@ -112,10 +113,41 @@ export function executeContextTool(tc) {
112
113
  */
113
114
  function validatePath(filePath) {
114
115
  const resolved = resolve(filePath);
115
- if (resolved === REPO_ROOT || resolved.startsWith(REPO_ROOT + "/")) {
116
- return resolved;
116
+ // Realpath both sides so a symlink planted inside the repo that points OUTSIDE
117
+ // can't pass a lexical containment check (isWithinRoot assumes resolved input
118
+ // and won't catch a link escape on its own). Matches fs-service's discipline.
119
+ // Reads are read-only, but a contained-looking symlink would still leak an
120
+ // out-of-repo file into arena context. Fall back to the lexical check when the
121
+ // target doesn't exist yet (a read of a missing file fails downstream anyway).
122
+ let realRoot;
123
+ try {
124
+ realRoot = realpathSync(REPO_ROOT);
125
+ }
126
+ catch {
127
+ realRoot = REPO_ROOT;
128
+ }
129
+ let realTarget;
130
+ try {
131
+ realTarget = realpathSync(resolved);
132
+ }
133
+ catch {
134
+ // Target doesn't exist yet. Compare REAL-vs-REAL by resolving the existing
135
+ // parent dir's realpath and rejoining the missing leaf — comparing realRoot
136
+ // (a real path) against a lexical `resolved` would false-reject valid files
137
+ // whenever REPO_ROOT (or any ancestor) is reached through a symlink.
138
+ let realParent;
139
+ try {
140
+ realParent = realpathSync(dirname(resolved));
141
+ }
142
+ catch {
143
+ // Parent missing too — no realpath available; fall back to lexical-vs-lexical
144
+ // so both operands share the same (unresolved) mode.
145
+ return isWithinRoot(resolve(REPO_ROOT), resolved) ? resolved : null;
146
+ }
147
+ const candidate = join(realParent, basename(resolved));
148
+ return isWithinRoot(realRoot, candidate) ? candidate : null;
117
149
  }
118
- return null;
150
+ return isWithinRoot(realRoot, realTarget) ? realTarget : null;
119
151
  }
120
152
  function executeReadFile(args) {
121
153
  const filePath = args.path;
@@ -126,7 +158,16 @@ function executeReadFile(args) {
126
158
  const raw = readFileSync(filePath, "utf-8");
127
159
  const lines = raw.split("\n");
128
160
  const offset = Math.max(0, (args.offset ?? 1) - 1);
129
- const limit = args.limit ?? 200;
161
+ // Guard `limit` before slicing: a negative limit makes lines.slice count
162
+ // "from the end" and silently drop the file's tail (reader thinks the file is
163
+ // shorter). A missing/NaN limit falls back to the default; a non-positive
164
+ // limit reads nothing.
165
+ const rawLimit = args.limit;
166
+ const limit = rawLimit === undefined || !Number.isFinite(rawLimit)
167
+ ? 200
168
+ : rawLimit > 0
169
+ ? Math.floor(rawLimit)
170
+ : 0;
130
171
  const slice = lines.slice(offset, offset + limit);
131
172
  const numbered = slice.map((l, i) => `${offset + i + 1}\t${l}`).join("\n");
132
173
  return truncateResult(numbered);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * True when `target` is the root itself or a descendant of it. Uses the
3
+ * platform separator (the old check hardcoded "/", which broke on Windows)
4
+ * and avoids the sibling-prefix trap (/repo vs /repo-evil). Both inputs are
5
+ * assumed already resolved to absolute paths.
6
+ */
7
+ export declare function isWithinRoot(root: string, target: string): boolean;
@@ -0,0 +1,15 @@
1
+ import * as path from "node:path";
2
+ /**
3
+ * True when `target` is the root itself or a descendant of it. Uses the
4
+ * platform separator (the old check hardcoded "/", which broke on Windows)
5
+ * and avoids the sibling-prefix trap (/repo vs /repo-evil). Both inputs are
6
+ * assumed already resolved to absolute paths.
7
+ */
8
+ export function isWithinRoot(root, target) {
9
+ if (target === root)
10
+ return true;
11
+ const rel = path.relative(root, target);
12
+ // Outside the root iff the relative path climbs out ("..") or is absolute
13
+ // (different drive on Windows).
14
+ return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel);
15
+ }
@@ -61,7 +61,7 @@ export async function runArgueRound(args) {
61
61
  // ─── Single-shot argue (no tools) ─────────────────────────────────────
62
62
  async function argueSingleShot(args) {
63
63
  const { critic, subject, format, draft, idPrefix, signal } = args;
64
- const client = await createLLMClient({ ...critic.llm, enableStreaming: false });
64
+ const client = await createLLMClient(critic.llm, critic.clientDefaults);
65
65
  const resp = await client.createMessage({
66
66
  systemPrompt: format.argueSystem(format.format),
67
67
  messages: [{ role: "user", content: format.argueUser(subject, draft) }],
@@ -78,7 +78,7 @@ async function argueSingleShot(args) {
78
78
  // ─── Tool-use argue (with web search) ─────────────────────────────────
79
79
  async function argueWithToolLoop(args) {
80
80
  const { critic, subject, format, draft, idPrefix, maxToolRounds, signal } = args;
81
- const client = await createLLMClient({ ...critic.llm, enableStreaming: false });
81
+ const client = await createLLMClient(critic.llm, critic.clientDefaults);
82
82
  const messages = [
83
83
  { role: "user", content: format.argueUser(subject, draft) },
84
84
  ];
@@ -149,6 +149,12 @@ async function argueWithToolLoop(args) {
149
149
  });
150
150
  finalText = resp.text;
151
151
  }
152
+ if (!finalText) {
153
+ // Both the tool loop and the force-conclude fallback produced no text
154
+ // (e.g. repeated max-token truncation). Log it — parseCritiquesResponse
155
+ // will just yield no critiques rather than silently parsing "".
156
+ logger.warn("arena.iterate.argue_empty_response", { critic: critic.name, toolRounds });
157
+ }
152
158
  const critiques = parseCritiquesResponse(finalText, critic.name, idPrefix);
153
159
  logger.info("arena.iterate.argue_toolloop", {
154
160
  critic: critic.name,
@@ -7,7 +7,7 @@ import { parseReviseResponse } from "../parse.js";
7
7
  export async function runRevise(args) {
8
8
  const { subject, format, previous, critiques, author, minDraftLength, signal, onProgress } = args;
9
9
  onProgress?.({ type: "revise_start", round: previous.version + 1, participant: author.name });
10
- const client = await createLLMClient({ ...author.llm, enableStreaming: false });
10
+ const client = await createLLMClient(author.llm, author.clientDefaults);
11
11
  const prompt = format.revisePrompt(subject, previous, critiques, minDraftLength);
12
12
  const resp = await client.createMessage({
13
13
  systemPrompt: prompt,
@@ -11,7 +11,7 @@ export async function runTournamentCandidates(args) {
11
11
  onProgress?.({ type: "v1_tournament_start", data: { count: participants.length } });
12
12
  const tasks = participants.map(async (p, idx) => {
13
13
  signal?.throwIfAborted();
14
- const client = await createLLMClient({ ...p.llm, enableStreaming: false });
14
+ const client = await createLLMClient(p.llm, p.clientDefaults);
15
15
  const prompt = format.draftPrompt(subject, minDraftLength);
16
16
  // Long-form generation needs a high output ceiling; the default 8k from
17
17
  // LLMClientBase is too low for a substantive PRD or design doc and
@@ -66,7 +66,7 @@ export async function runTournamentCandidates(args) {
66
66
  export async function mergeCandidatesToV1(args) {
67
67
  const { subject, format, author, candidates, minDraftLength, signal, onProgress } = args;
68
68
  onProgress?.({ type: "v1_merge_start", participant: author.name });
69
- const client = await createLLMClient({ ...author.llm, enableStreaming: false });
69
+ const client = await createLLMClient(author.llm, author.clientDefaults);
70
70
  const prompt = format.mergePrompt(subject, candidates, minDraftLength);
71
71
  const resp = await client.createMessage({
72
72
  systemPrompt: prompt,
@@ -95,7 +95,7 @@ export async function mergeCandidatesToV1(args) {
95
95
  /** Single-author v1 (tournament disabled) — author writes alone. */
96
96
  export async function singleAuthorV1(args) {
97
97
  const { subject, format, author, minDraftLength, signal } = args;
98
- const client = await createLLMClient({ ...author.llm, enableStreaming: false });
98
+ const client = await createLLMClient(author.llm, author.clientDefaults);
99
99
  const prompt = format.draftPrompt(subject, minDraftLength);
100
100
  const resp = await client.createMessage({
101
101
  systemPrompt: prompt,
@@ -68,11 +68,16 @@ export class ArenaLedger {
68
68
  }
69
69
  appendChallenge(challenge) {
70
70
  this.ledger.challenges.push(challenge);
71
- // Also add to the claim's challenges array
71
+ // Also add to the claim's challenges array. If the claimId doesn't resolve
72
+ // the challenge still lives in ledger.challenges but is invisible to any
73
+ // code reading claim.challenges directly — surface that inconsistency.
72
74
  const claim = this.index.claimsById.get(challenge.claimId);
73
75
  if (claim) {
74
76
  claim.challenges.push(challenge);
75
77
  }
78
+ else {
79
+ logger.warn("arena.challenge_for_unknown_claim", { claimId: challenge.claimId });
80
+ }
76
81
  this.checkGrowth();
77
82
  // Register any requested checks
78
83
  if (challenge.requestedChecks) {
@@ -94,6 +99,9 @@ export class ArenaLedger {
94
99
  if (claim) {
95
100
  claim.adjudication = adjudication;
96
101
  }
102
+ else {
103
+ logger.warn("arena.adjudication_for_unknown_claim", { claimId: adjudication.claimId });
104
+ }
97
105
  }
98
106
  // ─── Query operations ───────────────────────────────────────
99
107
  /**
@@ -33,10 +33,7 @@ export async function runAdjudication(options) {
33
33
  verified: verifiedClaims.length,
34
34
  });
35
35
  // Adjudicate contested claims via LLM
36
- const client = await createLLMClient({
37
- ...concluder.llm,
38
- enableStreaming: false,
39
- });
36
+ const client = await createLLMClient(concluder.llm, concluder.clientDefaults);
40
37
  for (const claim of contestedClaims) {
41
38
  signal?.throwIfAborted();
42
39
  const digest = buildDigest(ledger, {
@@ -18,10 +18,7 @@ import { isStrategyV2 } from "../types.js";
18
18
  export async function buildConsensus(options) {
19
19
  const { concluder, strategy, topic, reports, reviews, claimSummary, signal, onProgress } = options;
20
20
  onProgress?.({ type: "consensus_start" });
21
- const client = await createLLMClient({
22
- ...concluder.llm,
23
- enableStreaming: false,
24
- });
21
+ const client = await createLLMClient(concluder.llm, concluder.clientDefaults);
25
22
  const systemPrompt = strategy.consensusSystemPrompt();
26
23
  // Choose user prompt: claim-aware (V2) or standard (V1)
27
24
  let userContent;
@@ -28,10 +28,7 @@ export async function runCrossReview(options) {
28
28
  return [];
29
29
  if (!myReport)
30
30
  return [];
31
- const client = await createLLMClient({
32
- ...p.llm,
33
- enableStreaming: false,
34
- });
31
+ const client = await createLLMClient(p.llm, p.clientDefaults);
35
32
  const systemPrompt = strategy.crossReviewSystemPrompt(p.name);
36
33
  const userContent = strategy.crossReviewUserPrompt(topic, myReport, otherReports);
37
34
  let response = await client.createMessage({
@@ -110,10 +107,7 @@ export async function runVerificationReview(options) {
110
107
  const myClaims = claimsToReview.filter((c) => c.owner !== p.name);
111
108
  if (myClaims.length === 0)
112
109
  return;
113
- const client = await createLLMClient({
114
- ...p.llm,
115
- enableStreaming: false,
116
- });
110
+ const client = await createLLMClient(p.llm, p.clientDefaults);
117
111
  const systemPrompt = strategy.crossReviewSystemPrompt(p.name);
118
112
  if (v2) {
119
113
  // V2 path: claim-aware verification review (or merge review for planning)
@@ -75,10 +75,7 @@ async function debateClaim(options) {
75
75
  });
76
76
  // Each debater produces a turn
77
77
  const turnPromises = debaters.map(async (p) => {
78
- const client = await createLLMClient({
79
- ...p.llm,
80
- enableStreaming: false,
81
- });
78
+ const client = await createLLMClient(p.llm, p.clientDefaults);
82
79
  const systemPrompt = strategy.crossReviewSystemPrompt(p.name);
83
80
  let userContent;
84
81
  if (v2) {
@@ -27,10 +27,7 @@ export async function runParticipantResearchWithDossiers(options) {
27
27
  const tools = enableContextTools ? (contextTools ?? CONTEXT_TOOLS) : undefined;
28
28
  const tasks = participants.map(async (p) => {
29
29
  onProgress?.({ type: "research_start", participant: p.name });
30
- const client = await createLLMClient({
31
- ...p.llm,
32
- enableStreaming: false,
33
- });
30
+ const client = await createLLMClient(p.llm, p.clientDefaults);
34
31
  const messages = [
35
32
  {
36
33
  role: "user",
@@ -31,10 +31,7 @@ export async function runDetailExpansion(options) {
31
31
  onProgress?.({ type: "roadmap_expansion_start", phaseCount: toExpand.length });
32
32
  logger.info("arena.detail_expansion_start", { phaseCount: toExpand.length });
33
33
  const details = [];
34
- const client = await createLLMClient({
35
- ...concluder.llm,
36
- enableStreaming: false,
37
- });
34
+ const client = await createLLMClient(concluder.llm, concluder.clientDefaults);
38
35
  const systemPrompt = strategy.detailExpansionSystemPrompt();
39
36
  // Expand each phase sequentially
40
37
  for (const phase of toExpand) {
@@ -45,13 +42,18 @@ export async function runDetailExpansion(options) {
45
42
  const userContent = strategy.detailExpansionUserPrompt(topic, phase, digest);
46
43
  const messages = [{ role: "user", content: userContent }];
47
44
  let finalText = "";
48
- // Tool-use loop: allow LLM to verify file paths and interfaces
45
+ // Tool-use loop: allow LLM to verify file paths and interfaces.
46
+ // `maxRounds` bounds the number of tool rounds; after the last one we make
47
+ // one final call (without tools) so the model produces text FROM the tool
48
+ // results rather than the empty text it emitted alongside the tool calls.
49
49
  const maxRounds = tools ? Math.min(MAX_EXPANSION_TOOL_ROUNDS, MAX_TOOL_ROUNDS) : 0;
50
50
  for (let round = 0; round <= maxRounds; round++) {
51
+ // On the final round, stop offering tools so the model must answer.
52
+ const offerTools = round < maxRounds ? tools : undefined;
51
53
  const response = await client.createMessage({
52
54
  systemPrompt,
53
55
  messages,
54
- tools,
56
+ tools: offerTools,
55
57
  signal,
56
58
  });
57
59
  const hasTools = response.toolCalls && response.toolCalls.length > 0;
@@ -90,10 +92,6 @@ export async function runDetailExpansion(options) {
90
92
  });
91
93
  }
92
94
  messages.push({ role: "user", content: toolResultBlocks });
93
- // Last round: force a text response
94
- if (round === maxRounds) {
95
- finalText = response.text;
96
- }
97
95
  }
98
96
  // Retry if truncated
99
97
  if (!finalText) {