@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,364 @@
1
+ /**
2
+ * Built-in GenerateVideo tool (TODO 7.1 块3) — text-to-video via a
3
+ * {@link VideoProvider} adapter (submit / poll / download).
4
+ *
5
+ * Video generation is always slow (tens of seconds to minutes), so this tool
6
+ * is fire-and-forget by design: it submits the job, returns a handle
7
+ * immediately, and polls in the BACKGROUND. When the job finishes it writes
8
+ * the file to `<cwd>/.code-shell/generated_videos/` and enqueues a completion
9
+ * notification (same notificationQueue path as background agents / shells), so
10
+ * the main turn is never blocked (consistent with TODO 3.2 / 4.1).
11
+ *
12
+ * Concrete provider adapters (Seedance / Kling) are not wired yet — their
13
+ * private APIs need confirmed docs. Until then the only resolvable provider is
14
+ * the test-injected fake, so in production this tool is dormant (returns a
15
+ * "no video provider configured" message) rather than half-working.
16
+ */
17
+ import { writeFile, mkdir } from "node:fs/promises";
18
+ import { join } from "node:path";
19
+ import { SettingsManager } from "../../settings/manager.js";
20
+ import { notificationQueue } from "./agent-notifications.js";
21
+ import { backgroundJobRegistry } from "./background-jobs.js";
22
+ import { logger } from "../../logging/logger.js";
23
+ import { getVideoProvider, DEFAULT_VIDEO_MODEL, } from "./video-providers.js";
24
+ import { getImageUploader } from "./image-uploader.js";
25
+ import { effectiveApiKey } from "./generate-image.js";
26
+ import { getMergedCatalog, findCatalogEntry } from "../../model-catalog/index.js";
27
+ import { genInstancesFromConnections } from "../../model-catalog/gen-connections.js";
28
+ const MAX_IMAGES = 9;
29
+ /**
30
+ * Normalize image inputs (URLs or local paths) into public URLs the provider
31
+ * can consume. `images[]` wins over the single `image`. Local paths are
32
+ * uploaded via the injected {@link ImageUploader}; URLs pass through. Caps at
33
+ * {@link MAX_IMAGES} (fal reference-to-video allows up to 9).
34
+ */
35
+ export async function __normalizeImagesForTests(images, image, uploader, creds, signal) {
36
+ const raw = images && images.length ? images : image ? [image] : [];
37
+ if (raw.length > MAX_IMAGES) {
38
+ return { ok: false, error: `too many images: ${raw.length} (max ${MAX_IMAGES})` };
39
+ }
40
+ const urls = [];
41
+ for (const item of raw) {
42
+ const r = await uploader.toUrl(item, creds, signal);
43
+ if (!r.ok)
44
+ return { ok: false, error: r.error };
45
+ urls.push(r.url);
46
+ }
47
+ return { ok: true, urls };
48
+ }
49
+ /** Video provider `kind`s that have an adapter, in resolution preference. */
50
+ const VIDEO_PROVIDER_KINDS = ["fal"];
51
+ const DEFAULT_POLL_INTERVAL_MS = 5_000;
52
+ /** Safety cap so a stuck job's background loop can't poll forever. */
53
+ const MAX_POLL_MS = 15 * 60 * 1000;
54
+ export const generateVideoToolDef = {
55
+ name: "GenerateVideo",
56
+ description: "Generate a video from a text prompt. The job runs in the background (video generation is " +
57
+ "slow); you are notified automatically when it finishes, and the .mp4 is saved into the " +
58
+ "workspace. Do NOT sleep or poll — continue with other work. Requires a configured video " +
59
+ "provider.",
60
+ inputSchema: {
61
+ type: "object",
62
+ properties: {
63
+ prompt: { type: "string", description: "Description of the video to generate" },
64
+ provider: {
65
+ type: "string",
66
+ description: "Video provider kind to use. Defaults to the first configured video provider.",
67
+ },
68
+ model: { type: "string", description: "Video model id. Defaults to the provider's default." },
69
+ image: { type: "string", description: "Image URL (http/https) for image-to-video. When set, an image-to-video model is used." },
70
+ images: {
71
+ type: "array",
72
+ items: { type: "string" },
73
+ description: "Image URLs or local file paths for image/reference-to-video. 1 image → image-to-video; 2+ → reference-to-video (max 9). Local paths are auto-uploaded. Refer to them in prompt as @Image1, @Image2.",
74
+ },
75
+ videos: {
76
+ type: "array",
77
+ items: { type: "string" },
78
+ description: "Reference/continuation video URLs (http/https only — NOT local paths) for video extension. Forces reference-to-video (Seedance model required). Up to 3. Refer to them in prompt as @Video1, @Video2 (e.g. \"continue from @Video1\").",
79
+ },
80
+ },
81
+ required: ["prompt"],
82
+ },
83
+ };
84
+ // Test seam: inject a VideoProvider so the background poll loop can be tested
85
+ // end-to-end without a real adapter or network. Null → normal resolution.
86
+ let injectedProvider = null;
87
+ export function __setVideoProviderForTests(p) {
88
+ injectedProvider = p;
89
+ availCache.clear();
90
+ }
91
+ /**
92
+ * Tool-visibility guard: GenerateVideo is hidden until a video provider is
93
+ * actually configured. With no real adapters wired yet (VIDEO_PROVIDER_KINDS
94
+ * is empty), this returns false in production so the model never sees a tool
95
+ * it can't use — the tool becomes visible automatically once an adapter +
96
+ * matching settings provider exist. Short TTL cache like GenerateImage.
97
+ */
98
+ const availCache = new Map();
99
+ const AVAIL_TTL_MS = 1000;
100
+ export function isGenerateVideoAvailable(cwd = process.cwd(), nowMs = Date.now()) {
101
+ if (injectedProvider)
102
+ return true;
103
+ const hit = availCache.get(cwd);
104
+ if (hit && nowMs - hit.at < AVAIL_TTL_MS)
105
+ return hit.value;
106
+ let value = false;
107
+ try {
108
+ value = resolveVideoProvider(cwd) !== null;
109
+ }
110
+ catch {
111
+ value = false;
112
+ }
113
+ availCache.set(cwd, { at: nowMs, value });
114
+ return value;
115
+ }
116
+ /**
117
+ * List configured, usable video providers (TODO 7.1). Returns [] when no
118
+ * adapters are wired (VIDEO_PROVIDER_KINDS empty) or none configured.
119
+ */
120
+ export function listConfiguredVideoProviders(cwd = process.cwd()) {
121
+ try {
122
+ const settings = new SettingsManager(cwd, "full").get();
123
+ // Canonical videoGen.providers[] first (TODO 7.1) — but only kinds that
124
+ // have a wired adapter (VIDEO_PROVIDER_KINDS); a configured-but-unadapted
125
+ // entry isn't usable yet.
126
+ const videoGen = settings.videoGen;
127
+ if (videoGen?.providers?.length) {
128
+ const list = videoGen.providers;
129
+ return list
130
+ .filter((p) => !!effectiveApiKey(p, list) && getVideoProvider(p.kind) !== null)
131
+ .map((p) => ({ id: p.id, kind: p.kind, catalogId: p.catalogId }));
132
+ }
133
+ // Legacy LLM providers[] back-compat (field dropped from schema; read via
134
+ // .passthrough() with an inline cast).
135
+ const legacyProviders = settings.providers ?? [];
136
+ return legacyProviders
137
+ .filter((p) => p.apiKey && VIDEO_PROVIDER_KINDS.includes(p.kind))
138
+ .map((p) => ({ kind: p.kind }));
139
+ }
140
+ catch {
141
+ return [];
142
+ }
143
+ }
144
+ /** Dynamic GenerateVideo description naming configured providers (TODO 7.1). */
145
+ export function generateVideoToolDefFor(cwd) {
146
+ const providers = listConfiguredVideoProviders(cwd);
147
+ if (providers.length === 0)
148
+ return generateVideoToolDef;
149
+ const names = providers.map((p) => p.id ?? p.kind).join(", ");
150
+ const catalog = getMergedCatalog();
151
+ const paramLines = providers
152
+ .map((p) => {
153
+ const entry = findCatalogEntry(catalog, p.catalogId, p.kind);
154
+ return entry?.paramsDoc ? ` - ${p.id ?? p.kind}: ${entry.paramsDoc}` : null;
155
+ })
156
+ .filter((x) => x !== null);
157
+ const paramsBlock = paramLines.length ? `\nParams per provider:\n${paramLines.join("\n")}` : "";
158
+ return {
159
+ ...generateVideoToolDef,
160
+ description: generateVideoToolDef.description +
161
+ ` Configured provider(s): ${names}. Pass \`provider\` to pick one.` +
162
+ paramsBlock,
163
+ };
164
+ }
165
+ function resolveVideoProvider(cwd, preferKind) {
166
+ const settings = new SettingsManager(cwd, "full").get();
167
+ // 0. Unified store (统一模型接入方案): modelConnections (tag=video) +
168
+ // credentials, taking precedence over legacy videoGen.providers[].
169
+ const conns = settings.modelConnections;
170
+ const creds = settings.credentials;
171
+ if (Array.isArray(conns) && conns.some((c) => c.tag === "video")) {
172
+ const list = genInstancesFromConnections(conns, (Array.isArray(creds) ? creds : []), getMergedCatalog(), "video");
173
+ const def = settings.defaults?.video;
174
+ const usable = (p) => !!p.apiKey && getVideoProvider(p.kind) !== null;
175
+ const credsOf = (p) => ({ baseUrl: p.baseUrl, apiKey: p.apiKey });
176
+ if (preferKind) {
177
+ const chosen = list.find((p) => (p.id === preferKind || p.kind === preferKind) && usable(p));
178
+ if (chosen)
179
+ return { kind: chosen.kind, creds: credsOf(chosen), defaultModel: chosen.defaultModel };
180
+ return null;
181
+ }
182
+ const preferred = def ? list.find((p) => p.id === def) : undefined;
183
+ const chosen = (preferred && usable(preferred) ? preferred : undefined) ?? list.find(usable);
184
+ if (chosen)
185
+ return { kind: chosen.kind, creds: credsOf(chosen), defaultModel: chosen.defaultModel };
186
+ if (def || preferKind)
187
+ return null;
188
+ }
189
+ const videoGen = settings.videoGen;
190
+ if (videoGen?.providers?.length) {
191
+ const list = videoGen.providers;
192
+ const usable = (p) => !!effectiveApiKey(p, list) && getVideoProvider(p.kind) !== null;
193
+ const credsOf = (p) => ({ baseUrl: p.baseUrl, apiKey: effectiveApiKey(p, list) });
194
+ if (preferKind) {
195
+ const chosen = list.find((p) => (p.id === preferKind || p.kind === preferKind) && usable(p));
196
+ if (chosen)
197
+ return { kind: chosen.kind, creds: credsOf(chosen), defaultModel: chosen.defaultModel };
198
+ return null;
199
+ }
200
+ const preferred = videoGen.defaultProvider
201
+ ? list.find((p) => p.id === videoGen.defaultProvider)
202
+ : undefined;
203
+ const chosen = (preferred && usable(preferred) ? preferred : undefined) ?? list.find(usable);
204
+ if (chosen)
205
+ return { kind: chosen.kind, creds: credsOf(chosen), defaultModel: chosen.defaultModel };
206
+ return null;
207
+ }
208
+ // Back-compat: scan legacy LLM providers[] for a video-capable kind. The
209
+ // field was dropped from the schema; un-migrated settings.json still carry it
210
+ // via .passthrough(), so read it through an inline cast.
211
+ const legacyProviders = settings.providers ?? [];
212
+ const kinds = preferKind ? [preferKind] : VIDEO_PROVIDER_KINDS;
213
+ for (const kind of kinds) {
214
+ const provider = legacyProviders.find((p) => p.kind === kind);
215
+ if (provider && provider.apiKey) {
216
+ return { kind, creds: { baseUrl: provider.baseUrl, apiKey: provider.apiKey } };
217
+ }
218
+ }
219
+ return null;
220
+ }
221
+ export function __resolveVideoProviderForTests(cwd, preferKind) {
222
+ return resolveVideoProvider(cwd, preferKind);
223
+ }
224
+ export async function generateVideoTool(args, ctx) {
225
+ const prompt = args.prompt;
226
+ if (typeof prompt !== "string" || !prompt.trim()) {
227
+ return "Error: prompt is required";
228
+ }
229
+ const cwd = ctx?.cwd ?? process.cwd();
230
+ const sessionId = ctx?.sessionId;
231
+ const preferKind = typeof args.provider === "string" && args.provider ? args.provider : undefined;
232
+ const overrideModel = typeof args.model === "string" && args.model ? args.model : undefined;
233
+ const image = typeof args.image === "string" && args.image ? args.image : undefined;
234
+ const imagesArg = Array.isArray(args.images)
235
+ ? args.images.filter((x) => typeof x === "string")
236
+ : undefined;
237
+ // videos pass through verbatim to fal's video_urls (no upload) — http(s) only.
238
+ const videosArg = Array.isArray(args.videos)
239
+ ? args.videos.filter((x) => typeof x === "string")
240
+ : undefined;
241
+ const pollIntervalMs = typeof args.pollIntervalMs === "number" && args.pollIntervalMs > 0
242
+ ? args.pollIntervalMs
243
+ : DEFAULT_POLL_INTERVAL_MS;
244
+ // Resolve the provider + adapter. The test seam wins so the background loop
245
+ // is exercised without a real adapter.
246
+ let kind;
247
+ let creds;
248
+ let adapter;
249
+ let defaultModel;
250
+ if (injectedProvider) {
251
+ adapter = injectedProvider;
252
+ kind = injectedProvider.kind;
253
+ creds = { baseUrl: "test", apiKey: "test" };
254
+ }
255
+ else {
256
+ const resolved = resolveVideoProvider(cwd, preferKind);
257
+ if (!resolved) {
258
+ return preferKind
259
+ ? `Error: no video provider of kind "${preferKind}" available. Configure it (with an apiKey) in settings.`
260
+ : "Error: no video provider available. Video generation requires a configured video provider (none are wired yet).";
261
+ }
262
+ const a = getVideoProvider(resolved.kind);
263
+ if (!a)
264
+ return `Error: video generation is not supported for provider kind "${resolved.kind}".`;
265
+ adapter = a;
266
+ kind = resolved.kind;
267
+ creds = resolved.creds;
268
+ defaultModel = resolved.defaultModel;
269
+ }
270
+ const model = overrideModel ?? defaultModel ?? DEFAULT_VIDEO_MODEL[kind] ?? kind;
271
+ // Normalize image inputs (local paths → uploaded URLs) before submit. The
272
+ // provider only ever receives URLs and routes t2v/i2v/ref2v by count.
273
+ const uploader = getImageUploader(kind) ?? getImageUploader("fal");
274
+ const norm = await __normalizeImagesForTests(imagesArg, image, uploader, { baseUrl: creds.baseUrl, apiKey: creds.apiKey }, ctx?.signal);
275
+ if (!norm.ok)
276
+ return `Error: ${norm.error}`;
277
+ const submit = await adapter.submit({ prompt, model, image: undefined, images: norm.urls, videos: videosArg, creds, signal: ctx?.signal });
278
+ if (!submit.ok) {
279
+ return `Error submitting video job: ${submit.error}`;
280
+ }
281
+ const jobId = submit.jobId;
282
+ // Background poll → download → write → notify. Fire-and-forget: never await.
283
+ // Register the job so it shows up in the goal judge's background-task list
284
+ // (so the judge can tell "waiting on a finite render" from "nothing left to
285
+ // do" — the s-mqe0ox7n-a8d11c26 busy-loop bug). The video does NOT park the
286
+ // engine anymore; its completion notification wakes the idle session (server
287
+ // maybeWakeIdleSession). The registry is keyed by the fal jobId namespaced so
288
+ // two providers can't collide.
289
+ const jobKey = `video-${jobId}`;
290
+ const promptPreview = prompt.length > 80 ? `${prompt.slice(0, 80)}…` : prompt;
291
+ backgroundJobRegistry.start(jobKey, sessionId ?? "", `生成视频中:${promptPreview}`);
292
+ void pollToCompletion(adapter, jobId, creds, cwd, sessionId, prompt, pollIntervalMs)
293
+ .then((outcome) => backgroundJobRegistry.finish(jobKey, outcome))
294
+ .catch((err) => backgroundJobRegistry.finish(jobKey, { status: "failed", finalText: err.message }));
295
+ return [
296
+ `Video generation started in the background.`,
297
+ `job_id: ${jobId} (internal — do not show to user)`,
298
+ `prompt: ${prompt}`,
299
+ ``,
300
+ `It will be saved into .code-shell/generated_videos/ and you'll be notified automatically when it finishes.`,
301
+ `If you have no other work right now, END YOUR TURN — the system wakes you when the video is ready. NEVER run \`sleep\` or poll the provider yourself.`,
302
+ ].join("\n");
303
+ }
304
+ async function pollToCompletion(adapter, jobId, creds, cwd, sessionId, prompt, pollIntervalMs) {
305
+ const started = Date.now();
306
+ try {
307
+ for (;;) {
308
+ if (Date.now() - started > MAX_POLL_MS) {
309
+ const msg = `video job ${jobId} timed out after ${Math.round(MAX_POLL_MS / 1000)}s`;
310
+ notifyVideo(sessionId, "failed", prompt, undefined, msg);
311
+ return { status: "failed", finalText: msg };
312
+ }
313
+ const res = await adapter.poll({ jobId, creds });
314
+ if (!res.ok) {
315
+ notifyVideo(sessionId, "failed", prompt, undefined, res.error);
316
+ return { status: "failed", finalText: res.error };
317
+ }
318
+ if (res.status === "failed") {
319
+ notifyVideo(sessionId, "failed", prompt, undefined, res.error);
320
+ return { status: "failed", finalText: res.error };
321
+ }
322
+ if (res.status === "succeeded")
323
+ break;
324
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
325
+ }
326
+ const dl = await adapter.download({ jobId, creds });
327
+ if (!dl.ok) {
328
+ notifyVideo(sessionId, "failed", prompt, undefined, dl.error);
329
+ return { status: "failed", finalText: dl.error };
330
+ }
331
+ const dir = join(cwd, ".code-shell", "generated_videos");
332
+ await mkdir(dir, { recursive: true });
333
+ const path = join(dir, `${Date.now()}.${dl.ext || "mp4"}`);
334
+ await writeFile(path, dl.bytes);
335
+ notifyVideo(sessionId, "completed", prompt, path, undefined, dl.url);
336
+ return { status: "completed", finalText: path };
337
+ }
338
+ catch (err) {
339
+ notifyVideo(sessionId, "failed", prompt, undefined, err.message);
340
+ return { status: "failed", finalText: err.message };
341
+ }
342
+ }
343
+ function notifyVideo(sessionId, status, prompt, path, error, url) {
344
+ if (!sessionId) {
345
+ logger.warn("video_completion_without_session", { status });
346
+ return;
347
+ }
348
+ // Include the fal-hosted URL on success so the model can pass it to a follow-up
349
+ // GenerateVideo `videos:[url]` to extend this clip (no re-upload). Expiring URL.
350
+ const extendHint = url
351
+ ? ` To extend this video, call GenerateVideo again with videos:["${url}"] (Seedance model). URL may expire.`
352
+ : "";
353
+ notificationQueue.enqueue({
354
+ agentId: `video-${Date.now()}`,
355
+ name: "video generation",
356
+ description: status === "completed"
357
+ ? `Video generated: ${path} (prompt: ${prompt})`
358
+ : `Video generation failed (prompt: ${prompt})`,
359
+ status,
360
+ finalText: status === "completed" ? `Video saved to ${path}.${extendHint}` : undefined,
361
+ error: status === "failed" ? error : undefined,
362
+ enqueuedAt: Date.now(),
363
+ }, sessionId);
364
+ }
@@ -4,7 +4,6 @@
4
4
  import { glob } from "glob";
5
5
  import { stat } from "node:fs/promises";
6
6
  import * as path from "node:path";
7
- import { enforcePathPolicy } from "../path-policy.js";
8
7
  export const globToolDef = {
9
8
  name: "Glob",
10
9
  description: "Fast file pattern matching. Returns matching file paths sorted by modification time. " +
@@ -32,12 +31,6 @@ export async function globTool(args, ctx) {
32
31
  ? argPath
33
32
  : path.resolve(baseDir, argPath)
34
33
  : baseDir;
35
- // Path policy: refuse to glob a search root the policy classifies as
36
- // ask/deny for read. Stops a Glob("**/*", "/home/user/.ssh") from
37
- // enumerating the keys directory under the existence-check radar.
38
- const blocked = enforcePathPolicy(cwd, "read", ctx?.cwd);
39
- if (blocked)
40
- return blocked;
41
34
  try {
42
35
  const matches = await glob(pattern, {
43
36
  cwd,
@@ -3,5 +3,14 @@
3
3
  */
4
4
  import type { ToolDefinition } from "../../types.js";
5
5
  import type { ToolContext } from "../context.js";
6
+ type ExecFileForGrep = (file: string, args: readonly string[], options: {
7
+ maxBuffer: number;
8
+ timeout: number;
9
+ }) => Promise<{
10
+ stdout: string;
11
+ stderr: string;
12
+ }>;
13
+ export declare function _setGrepExecFileForTest(fn?: ExecFileForGrep): void;
6
14
  export declare const grepToolDef: ToolDefinition;
7
15
  export declare function grepTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
16
+ export {};
@@ -2,10 +2,14 @@
2
2
  * Built-in Grep content search tool.
3
3
  */
4
4
  import { execFile } from "node:child_process";
5
+ import { readdir, readFile, stat } from "node:fs/promises";
5
6
  import { promisify } from "node:util";
6
- import { resolve, sep, isAbsolute } from "node:path";
7
- import { enforcePathPolicy } from "../path-policy.js";
7
+ import { basename, join, relative, resolve, sep, isAbsolute } from "node:path";
8
8
  const execFileAsync = promisify(execFile);
9
+ let execFileForTest = execFileAsync;
10
+ export function _setGrepExecFileForTest(fn) {
11
+ execFileForTest = fn ?? execFileAsync;
12
+ }
9
13
  /**
10
14
  * Strip the cwd prefix from each line so results display as relative
11
15
  * paths (e.g. "src/ui/App.tsx" instead of an absolute path). Lines that
@@ -59,11 +63,6 @@ export async function grepTool(args, ctx) {
59
63
  ? argPath
60
64
  : resolve(baseDir, argPath)
61
65
  : baseDir;
62
- // Path policy: reading file contents under ~/.ssh, ~/.aws etc. by way
63
- // of grep is the same exfiltration risk as Read. Gate the search root.
64
- const blocked = enforcePathPolicy(searchPath, "read", ctx?.cwd);
65
- if (blocked)
66
- return blocked;
67
66
  const fileGlob = args.glob;
68
67
  const context = args.context || 0;
69
68
  const maxResults = args.max_results || 50;
@@ -77,15 +76,21 @@ export async function grepTool(args, ctx) {
77
76
  if (isNoMatchExit(rgErr))
78
77
  return "No matches found.";
79
78
  try {
80
- return await runGrep(pattern, searchPath, context, maxResults, outputMode, caseInsensitive);
79
+ return await runGrep(pattern, searchPath, fileGlob, context, maxResults, outputMode, caseInsensitive);
81
80
  }
82
81
  catch (grepErr) {
83
82
  if (isNoMatchExit(grepErr))
84
83
  return "No matches found.";
84
+ if (isCommandNotFound(rgErr) || isCommandNotFound(grepErr)) {
85
+ return await runNodeGrep(pattern, searchPath, fileGlob, context, maxResults, outputMode, caseInsensitive);
86
+ }
85
87
  return `Error in search: ${grepErr.message}`;
86
88
  }
87
89
  }
88
90
  }
91
+ function isCommandNotFound(err) {
92
+ return err?.code === "ENOENT";
93
+ }
89
94
  async function runRipgrep(pattern, path, fileGlob, context, maxResults, outputMode, caseInsensitive) {
90
95
  const args = ["--color=never"];
91
96
  if (caseInsensitive)
@@ -108,7 +113,7 @@ async function runRipgrep(pattern, path, fileGlob, context, maxResults, outputMo
108
113
  // Ignore common non-source dirs
109
114
  args.push("--glob", "!node_modules", "--glob", "!.git", "--glob", "!dist", "--glob", "!coverage");
110
115
  args.push("--", pattern, path);
111
- const { stdout } = await execFileAsync("rg", args, {
116
+ const { stdout } = await execFileForTest("rg", args, {
112
117
  maxBuffer: 10 * 1024 * 1024,
113
118
  timeout: 30_000,
114
119
  });
@@ -122,7 +127,7 @@ async function runRipgrep(pattern, path, fileGlob, context, maxResults, outputMo
122
127
  }
123
128
  return result;
124
129
  }
125
- async function runGrep(pattern, path, context, maxResults, outputMode, caseInsensitive) {
130
+ async function runGrep(pattern, path, fileGlob, context, maxResults, outputMode, caseInsensitive) {
126
131
  const args = ["-r", "--color=never"];
127
132
  if (caseInsensitive)
128
133
  args.push("-i");
@@ -138,10 +143,14 @@ async function runGrep(pattern, path, context, maxResults, outputMode, caseInsen
138
143
  args.push("-C", String(context));
139
144
  }
140
145
  args.push("-m", String(maxResults));
146
+ // Honor the file glob so the grep fallback matches ripgrep's --glob behavior
147
+ // (previously the glob was silently dropped on the fallback path).
148
+ if (fileGlob)
149
+ args.push(`--include=${fileGlob}`);
141
150
  args.push("-E", pattern);
142
151
  args.push("--exclude-dir=node_modules", "--exclude-dir=.git", "--exclude-dir=dist");
143
152
  args.push(path);
144
- const { stdout } = await execFileAsync("grep", args, {
153
+ const { stdout } = await execFileForTest("grep", args, {
145
154
  maxBuffer: 10 * 1024 * 1024,
146
155
  timeout: 30_000,
147
156
  });
@@ -154,3 +163,89 @@ async function runGrep(pattern, path, context, maxResults, outputMode, caseInsen
154
163
  }
155
164
  return result;
156
165
  }
166
+ const IGNORED_DIRS = new Set(["node_modules", ".git", "dist", "coverage"]);
167
+ async function runNodeGrep(pattern, path, fileGlob, context, maxResults, outputMode, caseInsensitive) {
168
+ let regex;
169
+ try {
170
+ regex = new RegExp(pattern, caseInsensitive ? "i" : "");
171
+ }
172
+ catch (err) {
173
+ return `Error in search: ${err.message}`;
174
+ }
175
+ const matches = [];
176
+ await walkTextFiles(path, fileGlob, async (file) => {
177
+ if (matches.length >= maxResults)
178
+ return;
179
+ let text;
180
+ try {
181
+ text = await readFile(file, "utf8");
182
+ }
183
+ catch {
184
+ return;
185
+ }
186
+ const lines = text.split(/\r?\n/);
187
+ const matchingLines = [];
188
+ for (let i = 0; i < lines.length; i++) {
189
+ regex.lastIndex = 0;
190
+ if (regex.test(lines[i]))
191
+ matchingLines.push(i);
192
+ }
193
+ if (matchingLines.length === 0)
194
+ return;
195
+ const rel = relative(path, file) || basename(file);
196
+ if (outputMode === "files_with_matches") {
197
+ matches.push(rel);
198
+ }
199
+ else if (outputMode === "count") {
200
+ matches.push(`${rel}:${matchingLines.length}`);
201
+ }
202
+ else {
203
+ const emitted = new Set();
204
+ for (const lineIndex of matchingLines) {
205
+ const start = Math.max(0, lineIndex - context);
206
+ const end = Math.min(lines.length - 1, lineIndex + context);
207
+ for (let i = start; i <= end; i++) {
208
+ if (emitted.has(i))
209
+ continue;
210
+ emitted.add(i);
211
+ matches.push(`${rel}:${i + 1}:${lines[i]}`);
212
+ if (matches.length >= maxResults)
213
+ return;
214
+ }
215
+ }
216
+ }
217
+ });
218
+ if (matches.length === 0)
219
+ return "No matches found.";
220
+ if (matches.length > 200)
221
+ return matches.slice(0, 200).join("\n") + `\n\n... ${matches.length - 200} more results`;
222
+ return matches.join("\n");
223
+ }
224
+ async function walkTextFiles(root, fileGlob, visit) {
225
+ const info = await stat(root).catch(() => null);
226
+ if (!info)
227
+ return;
228
+ if (info.isFile()) {
229
+ if (!fileGlob || matchesSimpleGlob(basename(root), fileGlob))
230
+ await visit(root);
231
+ return;
232
+ }
233
+ if (!info.isDirectory())
234
+ return;
235
+ const entries = await readdir(root, { withFileTypes: true }).catch(() => []);
236
+ for (const entry of entries) {
237
+ if (entry.isDirectory()) {
238
+ if (IGNORED_DIRS.has(entry.name))
239
+ continue;
240
+ await walkTextFiles(join(root, entry.name), fileGlob, visit);
241
+ }
242
+ else if (entry.isFile()) {
243
+ if (!fileGlob || matchesSimpleGlob(entry.name, fileGlob))
244
+ await visit(join(root, entry.name));
245
+ }
246
+ }
247
+ }
248
+ function matchesSimpleGlob(name, glob) {
249
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
250
+ return new RegExp(`^${escaped}$`).test(name);
251
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * ImageProvider — adapter interface for text-to-image generation (TODO 7.1).
3
+ *
4
+ * Lifts the hardcoded OpenAI `gpt-image-2` call out of generate-image.ts so
5
+ * GenerateImage can target multiple providers (OpenAI now; Gemini next) and
6
+ * take an optional provider/model. Each adapter takes resolved credentials +
7
+ * the request and returns a base64 PNG (or an error string). The tool layer
8
+ * owns credential resolution from settings, file writing, and user-facing
9
+ * formatting — adapters are pure "creds + prompt → bytes".
10
+ *
11
+ * `fetch` is injected so adapters are unit-testable without network. Production
12
+ * call sites pass the global `fetch`.
13
+ */
14
+ export interface ImageProviderCreds {
15
+ baseUrl: string;
16
+ apiKey: string;
17
+ }
18
+ /** A decoded reference/input image for image-to-image (edit) requests. */
19
+ export interface InputImage {
20
+ /** File name hint (used as the multipart part filename). */
21
+ filename: string;
22
+ /** MIME type, e.g. "image/png" | "image/jpeg" | "image/webp". */
23
+ mimeType: string;
24
+ /** Raw bytes (NOT base64) — the tool layer decodes the workspace file. */
25
+ bytes: Uint8Array;
26
+ }
27
+ export interface ImageGenerateRequest {
28
+ prompt: string;
29
+ size: string;
30
+ quality: string;
31
+ model: string;
32
+ creds: ImageProviderCreds;
33
+ /**
34
+ * Optional reference images. When present, this becomes an image-TO-image
35
+ * (edit) request rather than text-to-image: the provider conditions the
36
+ * output on these inputs (OpenAI `/images/edits`, Gemini inline_data parts).
37
+ * Empty/undefined = plain text-to-image.
38
+ */
39
+ inputImages?: InputImage[];
40
+ /** Optional cancellation signal forwarded to fetch. */
41
+ signal?: AbortSignal;
42
+ }
43
+ export type ImageGenerateResult = {
44
+ ok: true;
45
+ b64: string;
46
+ } | {
47
+ ok: false;
48
+ error: string;
49
+ };
50
+ export interface ImageProvider {
51
+ /** Adapter id — matches the settings provider `kind` it consumes. */
52
+ readonly kind: string;
53
+ generate(req: ImageGenerateRequest): Promise<ImageGenerateResult>;
54
+ }
55
+ /**
56
+ * OpenAI Images API adapter (`/images/generations`, b64_json). Identical wire
57
+ * behavior to the pre-refactor generate-image.ts: POST model/prompt/size/
58
+ * quality/n, read data[0].b64_json.
59
+ */
60
+ export declare class OpenAIImageProvider implements ImageProvider {
61
+ private readonly fetchImpl;
62
+ readonly kind = "openai";
63
+ constructor(fetchImpl?: typeof fetch);
64
+ generate(req: ImageGenerateRequest): Promise<ImageGenerateResult>;
65
+ }
66
+ /**
67
+ * Gemini Images adapter — the `generateContent` endpoint
68
+ * (`/models/{model}:generateContent`), API key in the `x-goog-api-key`
69
+ * header, prompt as a text content-part, PNG returned base64 in
70
+ * `candidates[].content.parts[].inline_data.data` (REST may camelCase it to
71
+ * `inlineData`; we read both). Wire shape per ai.google.dev image-generation
72
+ * docs. The OpenAI-style `size`/`quality` args don't map 1:1 to Gemini's
73
+ * aspectRatio/imageSize, so we only forward the prompt + IMAGE modality and
74
+ * leave Gemini's own defaults for dimensions (a later pass can map size →
75
+ * aspectRatio if needed).
76
+ */
77
+ export declare class GeminiImageProvider implements ImageProvider {
78
+ private readonly fetchImpl;
79
+ readonly kind = "google";
80
+ constructor(fetchImpl?: typeof fetch);
81
+ generate(req: ImageGenerateRequest): Promise<ImageGenerateResult>;
82
+ }
83
+ /** Default model per provider kind when the caller doesn't specify one. */
84
+ export declare const DEFAULT_IMAGE_MODEL: Record<string, string>;
85
+ /** Registry of available image-provider adapters, keyed by `kind`. */
86
+ export declare function getImageProvider(kind: string, fetchImpl?: typeof fetch): ImageProvider | null;