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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (484) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/dist/agent/coordinator.d.ts +49 -0
  4. package/dist/agent/coordinator.js +77 -0
  5. package/dist/arena/arena.d.ts +43 -0
  6. package/dist/arena/arena.js +334 -0
  7. package/dist/arena/context/context-tools.d.ts +16 -0
  8. package/dist/arena/context/context-tools.js +231 -0
  9. package/dist/arena/detect-mode.d.ts +20 -0
  10. package/dist/arena/detect-mode.js +78 -0
  11. package/dist/arena/digest-builder.d.ts +25 -0
  12. package/dist/arena/digest-builder.js +120 -0
  13. package/dist/arena/index.d.ts +29 -0
  14. package/dist/arena/index.js +29 -0
  15. package/dist/arena/iterate/convergence.d.ts +25 -0
  16. package/dist/arena/iterate/convergence.js +103 -0
  17. package/dist/arena/iterate/formats/index.d.ts +22 -0
  18. package/dist/arena/iterate/formats/index.js +283 -0
  19. package/dist/arena/iterate/index.d.ts +11 -0
  20. package/dist/arena/iterate/index.js +9 -0
  21. package/dist/arena/iterate/iterative-arena.d.ts +23 -0
  22. package/dist/arena/iterate/iterative-arena.js +237 -0
  23. package/dist/arena/iterate/parse.d.ts +42 -0
  24. package/dist/arena/iterate/parse.js +123 -0
  25. package/dist/arena/iterate/phases/argue.d.ts +22 -0
  26. package/dist/arena/iterate/phases/argue.js +159 -0
  27. package/dist/arena/iterate/phases/revise.d.ts +16 -0
  28. package/dist/arena/iterate/phases/revise.js +62 -0
  29. package/dist/arena/iterate/phases/tournament.d.ts +34 -0
  30. package/dist/arena/iterate/phases/tournament.js +113 -0
  31. package/dist/arena/iterate/tools/web-tools.d.ts +13 -0
  32. package/dist/arena/iterate/tools/web-tools.js +54 -0
  33. package/dist/arena/iterate/types.d.ts +152 -0
  34. package/dist/arena/iterate/types.js +8 -0
  35. package/dist/arena/ledger.d.ts +47 -0
  36. package/dist/arena/ledger.js +151 -0
  37. package/dist/arena/lenses/architecture.d.ts +5 -0
  38. package/dist/arena/lenses/architecture.js +22 -0
  39. package/dist/arena/lenses/engineering.d.ts +5 -0
  40. package/dist/arena/lenses/engineering.js +22 -0
  41. package/dist/arena/lenses/general.d.ts +5 -0
  42. package/dist/arena/lenses/general.js +20 -0
  43. package/dist/arena/lenses/index.d.ts +16 -0
  44. package/dist/arena/lenses/index.js +47 -0
  45. package/dist/arena/lenses/product.d.ts +5 -0
  46. package/dist/arena/lenses/product.js +22 -0
  47. package/dist/arena/model-presets.d.ts +23 -0
  48. package/dist/arena/model-presets.js +44 -0
  49. package/dist/arena/phases/adjudication.d.ts +24 -0
  50. package/dist/arena/phases/adjudication.js +144 -0
  51. package/dist/arena/phases/build-consensus.d.ts +29 -0
  52. package/dist/arena/phases/build-consensus.js +86 -0
  53. package/dist/arena/phases/claim-registry.d.ts +26 -0
  54. package/dist/arena/phases/claim-registry.js +60 -0
  55. package/dist/arena/phases/cross-review.d.ts +45 -0
  56. package/dist/arena/phases/cross-review.js +224 -0
  57. package/dist/arena/phases/debate-rounds.d.ts +27 -0
  58. package/dist/arena/phases/debate-rounds.js +162 -0
  59. package/dist/arena/phases/participant-research.d.ts +38 -0
  60. package/dist/arena/phases/participant-research.js +317 -0
  61. package/dist/arena/phases/planning-detail-expansion.d.ts +38 -0
  62. package/dist/arena/phases/planning-detail-expansion.js +121 -0
  63. package/dist/arena/planner.d.ts +27 -0
  64. package/dist/arena/planner.js +312 -0
  65. package/dist/arena/providers/docs.d.ts +6 -0
  66. package/dist/arena/providers/docs.js +108 -0
  67. package/dist/arena/providers/git.d.ts +8 -0
  68. package/dist/arena/providers/git.js +174 -0
  69. package/dist/arena/providers/index.d.ts +32 -0
  70. package/dist/arena/providers/index.js +132 -0
  71. package/dist/arena/providers/none.d.ts +7 -0
  72. package/dist/arena/providers/none.js +11 -0
  73. package/dist/arena/providers/repo.d.ts +6 -0
  74. package/dist/arena/providers/repo.js +255 -0
  75. package/dist/arena/render/session.d.ts +17 -0
  76. package/dist/arena/render/session.js +190 -0
  77. package/dist/arena/render/terminal.d.ts +34 -0
  78. package/dist/arena/render/terminal.js +286 -0
  79. package/dist/arena/strategies/discussion.d.ts +25 -0
  80. package/dist/arena/strategies/discussion.js +143 -0
  81. package/dist/arena/strategies/index.d.ts +15 -0
  82. package/dist/arena/strategies/index.js +28 -0
  83. package/dist/arena/strategies/language-wrapper.d.ts +17 -0
  84. package/dist/arena/strategies/language-wrapper.js +102 -0
  85. package/dist/arena/strategies/lens-wrapper.d.ts +16 -0
  86. package/dist/arena/strategies/lens-wrapper.js +236 -0
  87. package/dist/arena/strategies/planning.d.ts +30 -0
  88. package/dist/arena/strategies/planning.js +225 -0
  89. package/dist/arena/strategies/review.d.ts +26 -0
  90. package/dist/arena/strategies/review.js +168 -0
  91. package/dist/arena/strategies/utils.d.ts +39 -0
  92. package/dist/arena/strategies/utils.js +647 -0
  93. package/dist/arena/tools/selector.d.ts +17 -0
  94. package/dist/arena/tools/selector.js +61 -0
  95. package/dist/arena/transitions.d.ts +48 -0
  96. package/dist/arena/transitions.js +92 -0
  97. package/dist/arena/types.d.ts +508 -0
  98. package/dist/arena/types.js +27 -0
  99. package/dist/cli/agent-server-stdio.d.ts +32 -0
  100. package/dist/cli/agent-server-stdio.js +116 -0
  101. package/dist/colorizer.d.ts +23 -0
  102. package/dist/colorizer.js +12 -0
  103. package/dist/context/compaction.d.ts +116 -0
  104. package/dist/context/compaction.js +534 -0
  105. package/dist/context/manager.d.ts +145 -0
  106. package/dist/context/manager.js +423 -0
  107. package/dist/context/token-counter.d.ts +27 -0
  108. package/dist/context/token-counter.js +91 -0
  109. package/dist/context/tool-result-storage.d.ts +96 -0
  110. package/dist/context/tool-result-storage.js +296 -0
  111. package/dist/cost-tracker.d.ts +64 -0
  112. package/dist/cost-tracker.js +314 -0
  113. package/dist/cron/scheduler.d.ts +34 -0
  114. package/dist/cron/scheduler.js +115 -0
  115. package/dist/data/deepseek-models.json +46 -0
  116. package/dist/data/gemini-models.json +67 -0
  117. package/dist/data/openai-models.json +175 -0
  118. package/dist/data/openrouter-models.d.ts +35 -0
  119. package/dist/data/openrouter-models.js +36 -0
  120. package/dist/data/openrouter-models.json +4626 -0
  121. package/dist/data/openrouter-sync.d.ts +16 -0
  122. package/dist/data/openrouter-sync.js +53 -0
  123. package/dist/data/static-catalogs.d.ts +25 -0
  124. package/dist/data/static-catalogs.js +26 -0
  125. package/dist/data/zai-models.json +56 -0
  126. package/dist/engine/cost-store.d.ts +22 -0
  127. package/dist/engine/cost-store.js +15 -0
  128. package/dist/engine/engine.d.ts +295 -0
  129. package/dist/engine/engine.js +1467 -0
  130. package/dist/engine/model-facade.d.ts +24 -0
  131. package/dist/engine/model-facade.js +194 -0
  132. package/dist/engine/parse-task.d.ts +59 -0
  133. package/dist/engine/parse-task.js +135 -0
  134. package/dist/engine/patch-orphaned-tools.d.ts +52 -0
  135. package/dist/engine/patch-orphaned-tools.js +95 -0
  136. package/dist/engine/query.d.ts +50 -0
  137. package/dist/engine/query.js +107 -0
  138. package/dist/engine/runtime.d.ts +50 -0
  139. package/dist/engine/runtime.js +59 -0
  140. package/dist/engine/streaming-tool-queue.d.ts +34 -0
  141. package/dist/engine/streaming-tool-queue.js +61 -0
  142. package/dist/engine/token-budget.d.ts +22 -0
  143. package/dist/engine/token-budget.js +40 -0
  144. package/dist/engine/tool-summary.d.ts +13 -0
  145. package/dist/engine/tool-summary.js +35 -0
  146. package/dist/engine/turn-loop.d.ts +122 -0
  147. package/dist/engine/turn-loop.js +602 -0
  148. package/dist/engine/turn-state.d.ts +20 -0
  149. package/dist/engine/turn-state.js +17 -0
  150. package/dist/exceptions.d.ts +61 -0
  151. package/dist/exceptions.js +116 -0
  152. package/dist/git/utils.d.ts +35 -0
  153. package/dist/git/utils.js +98 -0
  154. package/dist/git/worktree.d.ts +36 -0
  155. package/dist/git/worktree.js +139 -0
  156. package/dist/hooks/events.d.ts +114 -0
  157. package/dist/hooks/events.js +4 -0
  158. package/dist/hooks/inject.d.ts +19 -0
  159. package/dist/hooks/inject.js +29 -0
  160. package/dist/hooks/registry.d.ts +15 -0
  161. package/dist/hooks/registry.js +84 -0
  162. package/dist/hooks/shell-runner.d.ts +45 -0
  163. package/dist/hooks/shell-runner.js +173 -0
  164. package/dist/index.d.ts +117 -0
  165. package/dist/index.js +145 -0
  166. package/dist/llm/api-key-sanitize.d.ts +22 -0
  167. package/dist/llm/api-key-sanitize.js +81 -0
  168. package/dist/llm/capabilities/index.d.ts +20 -0
  169. package/dist/llm/capabilities/index.js +39 -0
  170. package/dist/llm/capabilities/rules.d.ts +26 -0
  171. package/dist/llm/capabilities/rules.js +215 -0
  172. package/dist/llm/capabilities/types.d.ts +114 -0
  173. package/dist/llm/capabilities/types.js +24 -0
  174. package/dist/llm/client-base.d.ts +29 -0
  175. package/dist/llm/client-base.js +137 -0
  176. package/dist/llm/client-factory.d.ts +10 -0
  177. package/dist/llm/client-factory.js +29 -0
  178. package/dist/llm/model-cache.d.ts +21 -0
  179. package/dist/llm/model-cache.js +46 -0
  180. package/dist/llm/model-fetcher.d.ts +29 -0
  181. package/dist/llm/model-fetcher.js +282 -0
  182. package/dist/llm/model-pool.d.ts +95 -0
  183. package/dist/llm/model-pool.js +246 -0
  184. package/dist/llm/provider-catalog.d.ts +32 -0
  185. package/dist/llm/provider-catalog.js +50 -0
  186. package/dist/llm/provider-kinds.d.ts +21 -0
  187. package/dist/llm/provider-kinds.js +132 -0
  188. package/dist/llm/providers/anthropic.d.ts +19 -0
  189. package/dist/llm/providers/anthropic.js +267 -0
  190. package/dist/llm/providers/openai.d.ts +64 -0
  191. package/dist/llm/providers/openai.js +606 -0
  192. package/dist/llm/retry.d.ts +12 -0
  193. package/dist/llm/retry.js +17 -0
  194. package/dist/llm/stream-watchdog.d.ts +42 -0
  195. package/dist/llm/stream-watchdog.js +70 -0
  196. package/dist/llm/token-counter.d.ts +17 -0
  197. package/dist/llm/token-counter.js +36 -0
  198. package/dist/llm/types.d.ts +37 -0
  199. package/dist/llm/types.js +4 -0
  200. package/dist/logging/logger.d.ts +109 -0
  201. package/dist/logging/logger.js +390 -0
  202. package/dist/logging/sanitize-messages.d.ts +42 -0
  203. package/dist/logging/sanitize-messages.js +150 -0
  204. package/dist/logging/session-recorder.d.ts +90 -0
  205. package/dist/logging/session-recorder.js +304 -0
  206. package/dist/lsp/client.d.ts +41 -0
  207. package/dist/lsp/client.js +166 -0
  208. package/dist/lsp/manager.d.ts +40 -0
  209. package/dist/lsp/manager.js +124 -0
  210. package/dist/lsp/servers.d.ts +16 -0
  211. package/dist/lsp/servers.js +60 -0
  212. package/dist/migrate-models.d.ts +45 -0
  213. package/dist/migrate-models.js +179 -0
  214. package/dist/onboarding.d.ts +189 -0
  215. package/dist/onboarding.js +625 -0
  216. package/dist/plugins/gitOps.d.ts +32 -0
  217. package/dist/plugins/gitOps.js +62 -0
  218. package/dist/plugins/installedPlugins.d.ts +15 -0
  219. package/dist/plugins/installedPlugins.js +56 -0
  220. package/dist/plugins/knownMarketplaces.d.ts +10 -0
  221. package/dist/plugins/knownMarketplaces.js +46 -0
  222. package/dist/plugins/loadPluginHooks.d.ts +52 -0
  223. package/dist/plugins/loadPluginHooks.js +162 -0
  224. package/dist/plugins/marketplaceManager.d.ts +49 -0
  225. package/dist/plugins/marketplaceManager.js +142 -0
  226. package/dist/plugins/parseMarketplaceInput.d.ts +16 -0
  227. package/dist/plugins/parseMarketplaceInput.js +73 -0
  228. package/dist/plugins/pluginCommandHook.d.ts +54 -0
  229. package/dist/plugins/pluginCommandHook.js +180 -0
  230. package/dist/plugins/pluginCommandsLoader.d.ts +25 -0
  231. package/dist/plugins/pluginCommandsLoader.js +114 -0
  232. package/dist/plugins/pluginInstaller.d.ts +36 -0
  233. package/dist/plugins/pluginInstaller.js +228 -0
  234. package/dist/plugins/schemas.d.ts +9 -0
  235. package/dist/plugins/schemas.js +136 -0
  236. package/dist/plugins/types.d.ts +75 -0
  237. package/dist/plugins/types.js +7 -0
  238. package/dist/plugins/varRewrite.d.ts +42 -0
  239. package/dist/plugins/varRewrite.js +129 -0
  240. package/dist/preset/index.d.ts +60 -0
  241. package/dist/preset/index.js +159 -0
  242. package/dist/product/define.d.ts +60 -0
  243. package/dist/product/define.js +123 -0
  244. package/dist/product/index.d.ts +5 -0
  245. package/dist/product/index.js +4 -0
  246. package/dist/product/types.d.ts +111 -0
  247. package/dist/product/types.js +21 -0
  248. package/dist/prompt/composer.d.ts +49 -0
  249. package/dist/prompt/composer.js +168 -0
  250. package/dist/prompt/instruction-scanner.d.ts +54 -0
  251. package/dist/prompt/instruction-scanner.js +187 -0
  252. package/dist/prompt/section-cache.d.ts +15 -0
  253. package/dist/prompt/section-cache.js +31 -0
  254. package/dist/prompt/section-loader.d.ts +26 -0
  255. package/dist/prompt/section-loader.js +53 -0
  256. package/dist/prompt/sections/base.md +35 -0
  257. package/dist/prompt/sections/coding.md +31 -0
  258. package/dist/prompt/sections/orchestration.md +16 -0
  259. package/dist/prompt/sections/tone.md +9 -0
  260. package/dist/protocol/chat-session-manager.d.ts +33 -0
  261. package/dist/protocol/chat-session-manager.js +65 -0
  262. package/dist/protocol/chat-session.d.ts +49 -0
  263. package/dist/protocol/chat-session.js +86 -0
  264. package/dist/protocol/client.d.ts +104 -0
  265. package/dist/protocol/client.js +260 -0
  266. package/dist/protocol/factories.d.ts +93 -0
  267. package/dist/protocol/factories.js +64 -0
  268. package/dist/protocol/helpers.d.ts +42 -0
  269. package/dist/protocol/helpers.js +55 -0
  270. package/dist/protocol/index.d.ts +4 -0
  271. package/dist/protocol/index.js +4 -0
  272. package/dist/protocol/server.d.ts +80 -0
  273. package/dist/protocol/server.js +855 -0
  274. package/dist/protocol/transport.d.ts +37 -0
  275. package/dist/protocol/transport.js +85 -0
  276. package/dist/protocol/types.d.ts +221 -0
  277. package/dist/protocol/types.js +61 -0
  278. package/dist/remote/bridge.d.ts +37 -0
  279. package/dist/remote/bridge.js +103 -0
  280. package/dist/run/ArtifactTracker.d.ts +38 -0
  281. package/dist/run/ArtifactTracker.js +132 -0
  282. package/dist/run/CheckpointWriter.d.ts +53 -0
  283. package/dist/run/CheckpointWriter.js +135 -0
  284. package/dist/run/EngineRunner.d.ts +72 -0
  285. package/dist/run/EngineRunner.js +119 -0
  286. package/dist/run/Evaluator.d.ts +56 -0
  287. package/dist/run/Evaluator.js +62 -0
  288. package/dist/run/FileRunStore.d.ts +39 -0
  289. package/dist/run/FileRunStore.js +176 -0
  290. package/dist/run/Heartbeat.d.ts +54 -0
  291. package/dist/run/Heartbeat.js +123 -0
  292. package/dist/run/RunApprovalBackend.d.ts +53 -0
  293. package/dist/run/RunApprovalBackend.js +85 -0
  294. package/dist/run/RunLock.d.ts +40 -0
  295. package/dist/run/RunLock.js +98 -0
  296. package/dist/run/RunManager.d.ts +82 -0
  297. package/dist/run/RunManager.js +587 -0
  298. package/dist/run/RunQueue.d.ts +27 -0
  299. package/dist/run/RunQueue.js +73 -0
  300. package/dist/run/RunStore.d.ts +25 -0
  301. package/dist/run/RunStore.js +9 -0
  302. package/dist/run/factory.d.ts +63 -0
  303. package/dist/run/factory.js +51 -0
  304. package/dist/run/index.d.ts +17 -0
  305. package/dist/run/index.js +23 -0
  306. package/dist/run/types.d.ts +136 -0
  307. package/dist/run/types.js +28 -0
  308. package/dist/runtime/safe-spawn.d.ts +118 -0
  309. package/dist/runtime/safe-spawn.js +268 -0
  310. package/dist/services/analytics.d.ts +29 -0
  311. package/dist/services/analytics.js +84 -0
  312. package/dist/services/auto-dream.d.ts +49 -0
  313. package/dist/services/auto-dream.js +124 -0
  314. package/dist/services/diagnostics.d.ts +41 -0
  315. package/dist/services/diagnostics.js +99 -0
  316. package/dist/services/extract-memories.d.ts +31 -0
  317. package/dist/services/extract-memories.js +71 -0
  318. package/dist/services/index.d.ts +11 -0
  319. package/dist/services/index.js +17 -0
  320. package/dist/services/memory-orchestrator.d.ts +60 -0
  321. package/dist/services/memory-orchestrator.js +133 -0
  322. package/dist/services/notifier.d.ts +25 -0
  323. package/dist/services/notifier.js +69 -0
  324. package/dist/services/oauth.d.ts +27 -0
  325. package/dist/services/oauth.js +153 -0
  326. package/dist/services/session-memory.d.ts +37 -0
  327. package/dist/services/session-memory.js +81 -0
  328. package/dist/session/file-history.d.ts +41 -0
  329. package/dist/session/file-history.js +115 -0
  330. package/dist/session/memory.d.ts +104 -0
  331. package/dist/session/memory.js +310 -0
  332. package/dist/session/session-manager.d.ts +41 -0
  333. package/dist/session/session-manager.js +261 -0
  334. package/dist/session/transcript.d.ts +39 -0
  335. package/dist/session/transcript.js +186 -0
  336. package/dist/settings/manager.d.ts +36 -0
  337. package/dist/settings/manager.js +187 -0
  338. package/dist/settings/schema.d.ts +1219 -0
  339. package/dist/settings/schema.js +246 -0
  340. package/dist/skills/frontmatter.d.ts +13 -0
  341. package/dist/skills/frontmatter.js +70 -0
  342. package/dist/skills/index.d.ts +7 -0
  343. package/dist/skills/index.js +6 -0
  344. package/dist/skills/scanner.d.ts +31 -0
  345. package/dist/skills/scanner.js +171 -0
  346. package/dist/state.d.ts +160 -0
  347. package/dist/state.js +267 -0
  348. package/dist/tool-system/builtin/agent-notifications.d.ts +84 -0
  349. package/dist/tool-system/builtin/agent-notifications.js +197 -0
  350. package/dist/tool-system/builtin/agent-registry.d.ts +67 -0
  351. package/dist/tool-system/builtin/agent-registry.js +126 -0
  352. package/dist/tool-system/builtin/agent-transcript-translator.d.ts +22 -0
  353. package/dist/tool-system/builtin/agent-transcript-translator.js +160 -0
  354. package/dist/tool-system/builtin/agent.d.ts +16 -0
  355. package/dist/tool-system/builtin/agent.js +364 -0
  356. package/dist/tool-system/builtin/apply-patch/applier.d.ts +26 -0
  357. package/dist/tool-system/builtin/apply-patch/applier.js +250 -0
  358. package/dist/tool-system/builtin/apply-patch/index.d.ts +20 -0
  359. package/dist/tool-system/builtin/apply-patch/index.js +101 -0
  360. package/dist/tool-system/builtin/apply-patch/parser.d.ts +17 -0
  361. package/dist/tool-system/builtin/apply-patch/parser.js +212 -0
  362. package/dist/tool-system/builtin/apply-patch/seek-sequence.d.ts +18 -0
  363. package/dist/tool-system/builtin/apply-patch/seek-sequence.js +123 -0
  364. package/dist/tool-system/builtin/apply-patch/types.d.ts +49 -0
  365. package/dist/tool-system/builtin/apply-patch/types.js +13 -0
  366. package/dist/tool-system/builtin/arena.d.ts +31 -0
  367. package/dist/tool-system/builtin/arena.js +416 -0
  368. package/dist/tool-system/builtin/ask-user.d.ts +20 -0
  369. package/dist/tool-system/builtin/ask-user.js +99 -0
  370. package/dist/tool-system/builtin/bash.d.ts +21 -0
  371. package/dist/tool-system/builtin/bash.js +149 -0
  372. package/dist/tool-system/builtin/brief.d.ts +6 -0
  373. package/dist/tool-system/builtin/brief.js +41 -0
  374. package/dist/tool-system/builtin/config.d.ts +6 -0
  375. package/dist/tool-system/builtin/config.js +67 -0
  376. package/dist/tool-system/builtin/cron.d.ts +10 -0
  377. package/dist/tool-system/builtin/cron.js +61 -0
  378. package/dist/tool-system/builtin/edit.d.ts +6 -0
  379. package/dist/tool-system/builtin/edit.js +100 -0
  380. package/dist/tool-system/builtin/file-cache.d.ts +11 -0
  381. package/dist/tool-system/builtin/file-cache.js +39 -0
  382. package/dist/tool-system/builtin/glob.d.ts +7 -0
  383. package/dist/tool-system/builtin/glob.js +76 -0
  384. package/dist/tool-system/builtin/grep.d.ts +7 -0
  385. package/dist/tool-system/builtin/grep.js +150 -0
  386. package/dist/tool-system/builtin/index.d.ts +20 -0
  387. package/dist/tool-system/builtin/index.js +449 -0
  388. package/dist/tool-system/builtin/lsp.d.ts +6 -0
  389. package/dist/tool-system/builtin/lsp.js +141 -0
  390. package/dist/tool-system/builtin/mcp-tools.d.ts +10 -0
  391. package/dist/tool-system/builtin/mcp-tools.js +102 -0
  392. package/dist/tool-system/builtin/memory.d.ts +26 -0
  393. package/dist/tool-system/builtin/memory.js +226 -0
  394. package/dist/tool-system/builtin/notebook-edit.d.ts +6 -0
  395. package/dist/tool-system/builtin/notebook-edit.js +124 -0
  396. package/dist/tool-system/builtin/plan.d.ts +15 -0
  397. package/dist/tool-system/builtin/plan.js +55 -0
  398. package/dist/tool-system/builtin/powershell.d.ts +11 -0
  399. package/dist/tool-system/builtin/powershell.js +64 -0
  400. package/dist/tool-system/builtin/read.d.ts +6 -0
  401. package/dist/tool-system/builtin/read.js +70 -0
  402. package/dist/tool-system/builtin/remote-trigger.d.ts +6 -0
  403. package/dist/tool-system/builtin/remote-trigger.js +54 -0
  404. package/dist/tool-system/builtin/repl.d.ts +11 -0
  405. package/dist/tool-system/builtin/repl.js +82 -0
  406. package/dist/tool-system/builtin/send-message.d.ts +6 -0
  407. package/dist/tool-system/builtin/send-message.js +47 -0
  408. package/dist/tool-system/builtin/skill-prompt.d.ts +20 -0
  409. package/dist/tool-system/builtin/skill-prompt.js +52 -0
  410. package/dist/tool-system/builtin/skill.d.ts +9 -0
  411. package/dist/tool-system/builtin/skill.js +55 -0
  412. package/dist/tool-system/builtin/sleep.d.ts +6 -0
  413. package/dist/tool-system/builtin/sleep.js +33 -0
  414. package/dist/tool-system/builtin/task.d.ts +48 -0
  415. package/dist/tool-system/builtin/task.js +139 -0
  416. package/dist/tool-system/builtin/tool-search.d.ts +11 -0
  417. package/dist/tool-system/builtin/tool-search.js +90 -0
  418. package/dist/tool-system/builtin/web-fetch.d.ts +10 -0
  419. package/dist/tool-system/builtin/web-fetch.js +328 -0
  420. package/dist/tool-system/builtin/web-search.d.ts +25 -0
  421. package/dist/tool-system/builtin/web-search.js +174 -0
  422. package/dist/tool-system/builtin/worktree.d.ts +10 -0
  423. package/dist/tool-system/builtin/worktree.js +95 -0
  424. package/dist/tool-system/builtin/write.d.ts +6 -0
  425. package/dist/tool-system/builtin/write.js +36 -0
  426. package/dist/tool-system/context.d.ts +165 -0
  427. package/dist/tool-system/context.js +18 -0
  428. package/dist/tool-system/executor.d.ts +49 -0
  429. package/dist/tool-system/executor.js +495 -0
  430. package/dist/tool-system/investigation-guard.d.ts +46 -0
  431. package/dist/tool-system/investigation-guard.js +155 -0
  432. package/dist/tool-system/mcp-manager.d.ts +67 -0
  433. package/dist/tool-system/mcp-manager.js +249 -0
  434. package/dist/tool-system/permission.d.ts +111 -0
  435. package/dist/tool-system/permission.js +774 -0
  436. package/dist/tool-system/registry.d.ts +32 -0
  437. package/dist/tool-system/registry.js +131 -0
  438. package/dist/tool-system/sandbox/bwrap.d.ts +13 -0
  439. package/dist/tool-system/sandbox/bwrap.js +63 -0
  440. package/dist/tool-system/sandbox/index.d.ts +78 -0
  441. package/dist/tool-system/sandbox/index.js +222 -0
  442. package/dist/tool-system/sandbox/off.d.ts +2 -0
  443. package/dist/tool-system/sandbox/off.js +8 -0
  444. package/dist/tool-system/sandbox/seatbelt.d.ts +18 -0
  445. package/dist/tool-system/sandbox/seatbelt.js +109 -0
  446. package/dist/tool-system/task-guard.d.ts +29 -0
  447. package/dist/tool-system/task-guard.js +70 -0
  448. package/dist/tool-system/validation.d.ts +5 -0
  449. package/dist/tool-system/validation.js +39 -0
  450. package/dist/types.d.ts +376 -0
  451. package/dist/types.js +4 -0
  452. package/dist/updater.d.ts +65 -0
  453. package/dist/updater.js +405 -0
  454. package/dist/utils/debug.d.ts +41 -0
  455. package/dist/utils/debug.js +110 -0
  456. package/dist/utils/earlyInput.d.ts +43 -0
  457. package/dist/utils/earlyInput.js +166 -0
  458. package/dist/utils/env.d.ts +24 -0
  459. package/dist/utils/env.js +104 -0
  460. package/dist/utils/envUtils.d.ts +51 -0
  461. package/dist/utils/envUtils.js +135 -0
  462. package/dist/utils/execFileNoThrow.d.ts +39 -0
  463. package/dist/utils/execFileNoThrow.js +74 -0
  464. package/dist/utils/format.d.ts +44 -0
  465. package/dist/utils/format.js +236 -0
  466. package/dist/utils/intl.d.ts +22 -0
  467. package/dist/utils/intl.js +83 -0
  468. package/dist/utils/lockfile.d.ts +15 -0
  469. package/dist/utils/lockfile.js +30 -0
  470. package/dist/utils/memoize.d.ts +19 -0
  471. package/dist/utils/memoize.js +14 -0
  472. package/dist/utils/semver.d.ts +6 -0
  473. package/dist/utils/semver.js +13 -0
  474. package/dist/utils/sliceAnsi.d.ts +12 -0
  475. package/dist/utils/sliceAnsi.js +62 -0
  476. package/dist/utils/systemTheme.d.ts +40 -0
  477. package/dist/utils/systemTheme.js +108 -0
  478. package/dist/utils/task-sanitizer.d.ts +6 -0
  479. package/dist/utils/task-sanitizer.js +24 -0
  480. package/dist/utils/theme.d.ts +93 -0
  481. package/dist/utils/theme.js +544 -0
  482. package/dist/utils/toolDisplay.d.ts +31 -0
  483. package/dist/utils/toolDisplay.js +188 -0
  484. package/package.json +72 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Built-in task management — a single TodoWrite tool, cc-style.
3
+ *
4
+ * Design (2026-05-24 simplification, replacing the legacy 6-tool API):
5
+ *
6
+ * - One tool, one shape: TodoWrite({ todos: [{content, status, activeForm}] }).
7
+ * Each call replaces the entire snapshot — no per-item update API.
8
+ * - The transcript IS the task store. The LLM reads its own last
9
+ * TodoWrite tool_use input to know "where am I in the list".
10
+ * - No module singleton. No in-memory map. No file persistence.
11
+ * Resume hydrates by scanning the loaded transcript for the most
12
+ * recent TodoWrite tool_use.
13
+ * - Sub-agent isolation: when called from a spawned sub-agent, the
14
+ * emitted task_update event carries the sub-agent's id so the UI
15
+ * can keep the main and sub-agent todo lists separate (cc bucketing
16
+ * by `context.agentId ?? sessionId`).
17
+ *
18
+ * Why no TaskManager:
19
+ * The legacy TaskCreate/Update/Stop/Get/Output/List sextet stored
20
+ * state behind a module-level TaskManager singleton. Desktop spawns
21
+ * one worker process per agent/run, so the singleton vanished after
22
+ * each run — the next run's `TaskList` returned "No tasks" even
23
+ * though the transcript still had the full plan. Replacing the
24
+ * sextet with one snapshot tool whose output lives in the transcript
25
+ * makes the worker's lifecycle irrelevant: every restart re-reads
26
+ * the snapshot for free.
27
+ */
28
+ // ─── Tool definition ─────────────────────────────────────────────
29
+ export const todoWriteToolDef = {
30
+ name: "TodoWrite",
31
+ description: "Manage the session todo list. Pass the COMPLETE list each call — there's no per-item update API, you rewrite the entire snapshot. " +
32
+ "Use proactively for tasks with 3+ steps. Exactly one item should be in_progress at a time. " +
33
+ "Always provide both `content` (imperative) and `activeForm` (present continuous) so the UI can show what's happening right now.",
34
+ inputSchema: {
35
+ type: "object",
36
+ properties: {
37
+ todos: {
38
+ type: "array",
39
+ description: "The updated todo list. Replaces any previous list.",
40
+ items: {
41
+ type: "object",
42
+ properties: {
43
+ content: {
44
+ type: "string",
45
+ description: "Imperative form, e.g. 'Run tests'.",
46
+ },
47
+ status: {
48
+ type: "string",
49
+ enum: ["pending", "in_progress", "completed"],
50
+ },
51
+ activeForm: {
52
+ type: "string",
53
+ description: "Present continuous form, e.g. 'Running tests'.",
54
+ },
55
+ },
56
+ required: ["content", "status", "activeForm"],
57
+ },
58
+ },
59
+ },
60
+ required: ["todos"],
61
+ },
62
+ };
63
+ // ─── Tool implementation ─────────────────────────────────────────
64
+ export async function todoWriteTool(args, ctx) {
65
+ const raw = args.todos;
66
+ if (!Array.isArray(raw)) {
67
+ return "Error: `todos` must be an array.";
68
+ }
69
+ const todos = parseTodos(raw);
70
+ // cc's "everything done → clear the panel" behaviour: when the last
71
+ // item flips to completed, drop the list so the UI doesn't keep
72
+ // showing a stale "all done" panel. The model can still see its
73
+ // history in the transcript if it needs to reference what was done.
74
+ const allDone = todos.length > 0 && todos.every((t) => t.status === "completed");
75
+ const effective = allDone ? [] : todos;
76
+ emitTaskUpdate(ctx, toTaskInfos(effective));
77
+ // Short, cc-style confirmation. The model already has the new snapshot
78
+ // in its own input — no point echoing the full list back here.
79
+ return "Todos have been updated. Continue to use TodoWrite to track progress.";
80
+ }
81
+ function parseTodos(raw) {
82
+ const out = [];
83
+ for (const r of raw) {
84
+ if (!r || typeof r !== "object")
85
+ continue;
86
+ const obj = r;
87
+ if (typeof obj.content !== "string")
88
+ continue;
89
+ const status = obj.status;
90
+ if (status !== "pending" && status !== "in_progress" && status !== "completed")
91
+ continue;
92
+ const activeForm = typeof obj.activeForm === "string" ? obj.activeForm : obj.content;
93
+ out.push({ content: obj.content, status, activeForm });
94
+ }
95
+ return out;
96
+ }
97
+ function toTaskInfos(todos) {
98
+ // Position-based ids — stable React keys for the UI; LLMs never see them.
99
+ return todos.map((t, i) => ({
100
+ id: String(i + 1),
101
+ subject: t.content,
102
+ activeForm: t.activeForm,
103
+ status: t.status,
104
+ }));
105
+ }
106
+ function emitTaskUpdate(ctx, tasks) {
107
+ const cb = ctx?.streamCallback;
108
+ cb?.({ type: "task_update", tasks });
109
+ }
110
+ // ─── Transcript replay (used by Engine on session resume) ────────
111
+ /**
112
+ * Pull the latest todo snapshot from a transcript's event stream.
113
+ *
114
+ * Walks newest-first looking for a `tool_use` event whose toolName is
115
+ * TodoWrite — its args carry the canonical snapshot. Returns null if
116
+ * no TodoWrite call exists in the transcript (e.g. fresh session, or
117
+ * a session where the agent never used the tool).
118
+ */
119
+ export function readLastTodoSnapshot(events) {
120
+ for (let i = events.length - 1; i >= 0; i--) {
121
+ const e = events[i];
122
+ if (e.type !== "tool_use")
123
+ continue;
124
+ const d = e.data ?? {};
125
+ if (d.toolName !== "TodoWrite")
126
+ continue;
127
+ const args = (d.args ?? {});
128
+ const todos = args.todos;
129
+ if (!Array.isArray(todos))
130
+ continue;
131
+ const parsed = parseTodos(todos);
132
+ // Honour the same "all done → clear" rule as the live tool, so a
133
+ // session that finished with everything completed restores to an
134
+ // empty pinned panel.
135
+ const allDone = parsed.length > 0 && parsed.every((t) => t.status === "completed");
136
+ return allDone ? [] : toTaskInfos(parsed);
137
+ }
138
+ return null;
139
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Built-in ToolSearch tool — deferred tool schema discovery.
3
+ *
4
+ * MCP tools are initially registered with name-only (deferred).
5
+ * The model uses ToolSearch to discover full schemas on demand,
6
+ * saving context by not loading all MCP tool schemas upfront.
7
+ */
8
+ import type { ToolDefinition } from "../../types.js";
9
+ import type { ToolContext } from "../context.js";
10
+ export declare const toolSearchToolDef: ToolDefinition;
11
+ export declare function toolSearchTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Built-in ToolSearch tool — deferred tool schema discovery.
3
+ *
4
+ * MCP tools are initially registered with name-only (deferred).
5
+ * The model uses ToolSearch to discover full schemas on demand,
6
+ * saving context by not loading all MCP tool schemas upfront.
7
+ */
8
+ export const toolSearchToolDef = {
9
+ name: "ToolSearch",
10
+ description: "Search for and discover available tools by name or keyword. " +
11
+ "Some tools (especially from MCP servers) are deferred — their full schemas " +
12
+ "are only loaded when you search for them. Use this to find the right tool.",
13
+ inputSchema: {
14
+ type: "object",
15
+ properties: {
16
+ query: {
17
+ type: "string",
18
+ description: 'Query to find tools. Use "select:ToolName" for exact match, or keywords to search.',
19
+ },
20
+ max_results: {
21
+ type: "number",
22
+ description: "Maximum results to return (default: 5)",
23
+ },
24
+ },
25
+ required: ["query"],
26
+ },
27
+ };
28
+ export async function toolSearchTool(args, ctx) {
29
+ const query = args.query;
30
+ if (!query)
31
+ return "Error: query is required";
32
+ if (!ctx?.toolRegistry)
33
+ return "Error: ToolSearch is not configured (no registry in ctx)";
34
+ const maxResults = Math.min(args.max_results || 5, 20);
35
+ // "select:Name1,Name2" → exact match
36
+ if (query.startsWith("select:")) {
37
+ const names = query.slice(7).split(",").map((n) => n.trim());
38
+ return matchExact(ctx.toolRegistry, names);
39
+ }
40
+ // Keyword search
41
+ return searchByKeyword(ctx.toolRegistry, query, maxResults);
42
+ }
43
+ function matchExact(registry, names) {
44
+ const results = [];
45
+ for (const name of names) {
46
+ const tool = registry.getTool(name);
47
+ if (tool) {
48
+ results.push(formatTool(tool));
49
+ }
50
+ else {
51
+ results.push(`Tool "${name}" not found.`);
52
+ }
53
+ }
54
+ return results.join("\n\n---\n\n");
55
+ }
56
+ function searchByKeyword(registry, query, maxResults) {
57
+ const allTools = registry.listToolsDetailed();
58
+ const queryLower = query.toLowerCase();
59
+ const keywords = queryLower.split(/\s+/);
60
+ // Score each tool
61
+ const scored = allTools.map((tool) => {
62
+ let score = 0;
63
+ const nameLower = tool.name.toLowerCase();
64
+ const descLower = tool.description.toLowerCase();
65
+ for (const kw of keywords) {
66
+ if (nameLower.includes(kw))
67
+ score += 10;
68
+ if (descLower.includes(kw))
69
+ score += 3;
70
+ }
71
+ // Exact name match bonus
72
+ if (nameLower === queryLower)
73
+ score += 50;
74
+ return { tool, score };
75
+ });
76
+ const matches = scored
77
+ .filter((s) => s.score > 0)
78
+ .sort((a, b) => b.score - a.score)
79
+ .slice(0, maxResults);
80
+ if (matches.length === 0) {
81
+ return `No tools matching "${query}". Available tools: ${allTools.map((t) => t.name).join(", ")}`;
82
+ }
83
+ return matches.map((m) => formatTool(m.tool)).join("\n\n---\n\n");
84
+ }
85
+ function formatTool(tool) {
86
+ return (`### ${tool.name}\n` +
87
+ `Source: ${tool.source}${tool.serverName ? ` (${tool.serverName})` : ""}\n` +
88
+ `${tool.description}\n` +
89
+ `Parameters: ${JSON.stringify(tool.inputSchema, null, 2)}`);
90
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Built-in WebFetch tool — fetch a URL and extract readable text content.
3
+ */
4
+ import type { ToolDefinition } from "../../types.js";
5
+ export declare const webFetchToolDef: ToolDefinition;
6
+ type DnsLookup = (hostname: string) => Promise<string[]>;
7
+ /** Test hook: replace the DNS resolver. Pass `null` to restore default. */
8
+ export declare function __setDnsLookupForTests(impl: DnsLookup | null): void;
9
+ export declare function webFetchTool(args: Record<string, unknown>): Promise<string>;
10
+ export {};
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Built-in WebFetch tool — fetch a URL and extract readable text content.
3
+ */
4
+ import * as dns from "node:dns/promises";
5
+ import * as net from "node:net";
6
+ export const webFetchToolDef = {
7
+ name: "WebFetch",
8
+ description: "Fetch a web page and return its text content. Strips HTML tags to return readable text. " +
9
+ "Use this to read articles, documentation, or any web page.",
10
+ inputSchema: {
11
+ type: "object",
12
+ properties: {
13
+ url: { type: "string", description: "The URL to fetch" },
14
+ max_length: {
15
+ type: "number",
16
+ description: "Maximum characters to return (default: 50000)",
17
+ },
18
+ headers: {
19
+ type: "object",
20
+ description: "Optional HTTP headers to send",
21
+ },
22
+ },
23
+ required: ["url"],
24
+ },
25
+ };
26
+ const MAX_OUTPUT = 100_000;
27
+ const DEFAULT_MAX = 50_000;
28
+ // Header names that must not be overridable via args.headers (SSRF / auth injection)
29
+ const BLOCKED_REQUEST_HEADERS = new Set([
30
+ "host", "authorization", "cookie", "proxy-authorization",
31
+ "x-forwarded-for", "x-real-ip", "x-forwarded-host",
32
+ ]);
33
+ // Hostname patterns that block SSRF to internal/metadata services
34
+ const BLOCKED_HOST_PATTERNS = [
35
+ /^localhost$/i,
36
+ /^127\./,
37
+ /^0\.0\.0\.0$/,
38
+ /^10\./,
39
+ /^169\.254\./, // link-local + AWS/GCP metadata (169.254.169.254)
40
+ /^192\.168\./,
41
+ /^172\.(1[6-9]|2[0-9]|3[01])\./, // 172.16.0.0/12
42
+ /^::1$/,
43
+ /^fc00:/i, /^fd00:/i, // IPv6 ULA
44
+ /^fe80:/i, // IPv6 link-local
45
+ ];
46
+ function isBlockedHost(hostname) {
47
+ // `new URL("http://[::1]/").hostname` returns "[::1]" (with brackets);
48
+ // strip them so IPv6 patterns match.
49
+ const stripped = hostname.startsWith("[") && hostname.endsWith("]")
50
+ ? hostname.slice(1, -1)
51
+ : hostname;
52
+ return BLOCKED_HOST_PATTERNS.some((re) => re.test(stripped));
53
+ }
54
+ // A3 hardening: per-IP block list. Used after DNS resolution to catch
55
+ // public-looking hostnames that resolve to private/loopback addresses
56
+ // (DNS rebinding, AWS metadata via DNS, etc.). See spec
57
+ // docs/superpowers/specs/2026-05-26-a3-webfetch-ssrf-design.md.
58
+ function isBlockedIp(ip) {
59
+ // Normalize IPv4-mapped IPv6 (e.g. ::ffff:127.0.0.1) so the IPv4
60
+ // checks below catch it.
61
+ const mapped = ip.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i);
62
+ if (mapped)
63
+ return isBlockedIpv4(mapped[1]);
64
+ if (net.isIPv4(ip))
65
+ return isBlockedIpv4(ip);
66
+ if (net.isIPv6(ip))
67
+ return isBlockedIpv6(ip);
68
+ // Unknown format — refuse rather than allow.
69
+ return true;
70
+ }
71
+ function isBlockedIpv4(ip) {
72
+ const parts = ip.split(".").map(Number);
73
+ if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) {
74
+ return true;
75
+ }
76
+ const [a, b] = parts;
77
+ // 0.0.0.0/8 (current network), 10.0.0.0/8, 127.0.0.0/8
78
+ if (a === 0 || a === 10 || a === 127)
79
+ return true;
80
+ // 100.64.0.0/10 — CGNAT
81
+ if (a === 100 && b >= 64 && b <= 127)
82
+ return true;
83
+ // 169.254.0.0/16 — link-local + cloud metadata
84
+ if (a === 169 && b === 254)
85
+ return true;
86
+ // 172.16.0.0/12
87
+ if (a === 172 && b >= 16 && b <= 31)
88
+ return true;
89
+ // 192.168.0.0/16
90
+ if (a === 192 && b === 168)
91
+ return true;
92
+ // 224.0.0.0/4 multicast, 240.0.0.0/4 reserved/broadcast
93
+ if (a >= 224)
94
+ return true;
95
+ return false;
96
+ }
97
+ function isBlockedIpv6(ip) {
98
+ const lower = ip.toLowerCase();
99
+ // Loopback ::1
100
+ if (lower === "::1")
101
+ return true;
102
+ // Unique-local fc00::/7 (covers fc00: and fd00:)
103
+ if (/^f[cd][0-9a-f]{2}:/.test(lower))
104
+ return true;
105
+ // Link-local fe80::/10
106
+ if (/^fe[89ab][0-9a-f]:/.test(lower))
107
+ return true;
108
+ // Documentation 2001:db8::/32
109
+ if (/^2001:0?db8:/.test(lower))
110
+ return true;
111
+ // Discard prefix 100::/64
112
+ if (/^100:0{0,4}(:|$)/.test(lower))
113
+ return true;
114
+ return false;
115
+ }
116
+ // Hop limit for redirect chains.
117
+ const MAX_REDIRECTS = 5;
118
+ // Headers that must be stripped when a redirect crosses origins.
119
+ // Same policy fetch(... redirect: "follow") applies internally; we
120
+ // re-implement it here because we drive redirects manually.
121
+ const CROSS_ORIGIN_STRIP_HEADERS = new Set([
122
+ "authorization",
123
+ "cookie",
124
+ "proxy-authorization",
125
+ ]);
126
+ async function defaultDnsLookup(hostname) {
127
+ // For literal IPs, lookup returns the IP itself (Node behavior),
128
+ // which still flows through the block-list check above.
129
+ const records = await dns.lookup(hostname, { all: true, verbatim: true });
130
+ return records.map((r) => r.address);
131
+ }
132
+ let dnsLookupImpl = defaultDnsLookup;
133
+ /** Test hook: replace the DNS resolver. Pass `null` to restore default. */
134
+ export function __setDnsLookupForTests(impl) {
135
+ dnsLookupImpl = impl ?? defaultDnsLookup;
136
+ }
137
+ async function validateHopHost(u) {
138
+ if (u.protocol !== "http:" && u.protocol !== "https:") {
139
+ return { ok: false, reason: `protocol "${u.protocol}" is not allowed` };
140
+ }
141
+ if (isBlockedHost(u.hostname)) {
142
+ return { ok: false, reason: `host "${u.hostname}" is on the block list` };
143
+ }
144
+ let ips;
145
+ try {
146
+ ips = await dnsLookupImpl(u.hostname);
147
+ }
148
+ catch (err) {
149
+ return { ok: false, reason: `DNS lookup failed for "${u.hostname}": ${err.message}` };
150
+ }
151
+ if (ips.length === 0) {
152
+ return { ok: false, reason: `DNS returned no addresses for "${u.hostname}"` };
153
+ }
154
+ for (const ip of ips) {
155
+ if (isBlockedIp(ip)) {
156
+ return {
157
+ ok: false,
158
+ reason: `"${u.hostname}" resolves to blocked IP ${ip}`,
159
+ };
160
+ }
161
+ }
162
+ return { ok: true, ips };
163
+ }
164
+ function sameOrigin(a, b) {
165
+ return a.protocol === b.protocol && a.host === b.host;
166
+ }
167
+ export async function webFetchTool(args) {
168
+ const url = args.url;
169
+ if (!url)
170
+ return "Error: url is required";
171
+ // Validate URL
172
+ let parsed;
173
+ try {
174
+ parsed = new URL(url);
175
+ }
176
+ catch {
177
+ return `Error: invalid URL "${url}"`;
178
+ }
179
+ const maxLength = Math.min(args.max_length || DEFAULT_MAX, MAX_OUTPUT);
180
+ const rawHeaders = args.headers ?? {};
181
+ const baseHeaders = {
182
+ "User-Agent": "Mozilla/5.0 (compatible; code-shell/0.1; +https://github.com/nicepkg/code-shell)",
183
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,text/plain;q=0.8,*/*;q=0.7",
184
+ };
185
+ // Strip dangerous user-supplied headers up-front.
186
+ for (const [k, v] of Object.entries(rawHeaders)) {
187
+ if (!BLOCKED_REQUEST_HEADERS.has(k.toLowerCase())) {
188
+ baseHeaders[k] = v;
189
+ }
190
+ }
191
+ // A3 hardening: manual redirect loop. Each hop revalidates the URL,
192
+ // host, and DNS-resolved IPs against the block lists. fetch() runs
193
+ // with redirect: "manual" so the runtime cannot silently follow a
194
+ // redirect into private/loopback/metadata space.
195
+ const timeoutSignal = AbortSignal.timeout(30_000);
196
+ let currentUrl = parsed;
197
+ let currentHeaders = { ...baseHeaders };
198
+ const originalOrigin = parsed;
199
+ try {
200
+ for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
201
+ const check = await validateHopHost(currentUrl);
202
+ if (!check.ok) {
203
+ const verb = hop === 0 ? "fetch" : `follow redirect to`;
204
+ return `Error: refusing to ${verb} ${currentUrl.href} — ${check.reason}`;
205
+ }
206
+ const res = await fetch(currentUrl, {
207
+ headers: currentHeaders,
208
+ redirect: "manual",
209
+ signal: timeoutSignal,
210
+ });
211
+ // Not a redirect: return body (or HTTP error).
212
+ if (res.status < 300 || res.status >= 400 || !res.headers.get("location")) {
213
+ if (!res.ok) {
214
+ return `Error: HTTP ${res.status} ${res.statusText}`;
215
+ }
216
+ return await readAndTruncateBody(res, maxLength);
217
+ }
218
+ // Redirect: resolve target, drop credentials on cross-origin,
219
+ // and loop.
220
+ const location = res.headers.get("location");
221
+ let nextUrl;
222
+ try {
223
+ nextUrl = new URL(location, currentUrl);
224
+ }
225
+ catch {
226
+ return `Error: redirect target "${location}" is not a valid URL`;
227
+ }
228
+ if (!sameOrigin(nextUrl, originalOrigin)) {
229
+ currentHeaders = stripCrossOriginHeaders(currentHeaders);
230
+ }
231
+ currentUrl = nextUrl;
232
+ }
233
+ return `Error: too many redirects (max ${MAX_REDIRECTS})`;
234
+ }
235
+ catch (err) {
236
+ const msg = err.message;
237
+ if (msg.includes("abort") || msg.includes("timeout")) {
238
+ return "Error: request timed out after 30s";
239
+ }
240
+ return `Fetch error: ${msg}`;
241
+ }
242
+ }
243
+ async function readAndTruncateBody(res, maxLength) {
244
+ const contentType = res.headers.get("content-type") ?? "";
245
+ const body = await res.text();
246
+ let text;
247
+ if (contentType.includes("text/html") || contentType.includes("application/xhtml")) {
248
+ text = extractTextFromHTML(body);
249
+ }
250
+ else {
251
+ text = body;
252
+ }
253
+ if (text.length > maxLength) {
254
+ text = text.slice(0, maxLength) + `\n\n... content truncated (${text.length} chars total)`;
255
+ }
256
+ return text || "(page returned empty content)";
257
+ }
258
+ function stripCrossOriginHeaders(headers) {
259
+ const stripped = {};
260
+ for (const [k, v] of Object.entries(headers)) {
261
+ if (!CROSS_ORIGIN_STRIP_HEADERS.has(k.toLowerCase())) {
262
+ stripped[k] = v;
263
+ }
264
+ }
265
+ return stripped;
266
+ }
267
+ /**
268
+ * Simple HTML → text extraction.
269
+ * Strips tags, decodes common entities, collapses whitespace.
270
+ * Not perfect, but good enough for most web pages.
271
+ */
272
+ function extractTextFromHTML(html) {
273
+ let text = html;
274
+ // Remove script and style blocks
275
+ text = text.replace(/<script[\s\S]*?<\/script>/gi, "");
276
+ text = text.replace(/<style[\s\S]*?<\/style>/gi, "");
277
+ text = text.replace(/<noscript[\s\S]*?<\/noscript>/gi, "");
278
+ // Extract title
279
+ const titleMatch = text.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
280
+ const title = titleMatch ? decodeEntities(titleMatch[1].trim()) : "";
281
+ // Try to extract main content area
282
+ const mainMatch = text.match(/<main[\s\S]*?<\/main>/i) ??
283
+ text.match(/<article[\s\S]*?<\/article>/i) ??
284
+ text.match(/<div[^>]*(?:role="main"|id="content"|class="[^"]*content[^"]*")[^>]*>[\s\S]*?<\/div>/i);
285
+ if (mainMatch) {
286
+ text = mainMatch[0];
287
+ }
288
+ else {
289
+ // Fall back to body
290
+ const bodyMatch = text.match(/<body[\s\S]*?<\/body>/i);
291
+ if (bodyMatch)
292
+ text = bodyMatch[0];
293
+ }
294
+ // Remove nav, header, footer, aside
295
+ text = text.replace(/<(nav|header|footer|aside)[\s\S]*?<\/\1>/gi, "");
296
+ // Convert block elements to newlines
297
+ text = text.replace(/<(p|div|h[1-6]|li|br|tr|blockquote)[^>]*>/gi, "\n");
298
+ text = text.replace(/<\/(p|div|h[1-6]|li|tr|blockquote)>/gi, "\n");
299
+ text = text.replace(/<hr[^>]*>/gi, "\n---\n");
300
+ // Remove all remaining tags
301
+ text = text.replace(/<[^>]+>/g, "");
302
+ // Decode entities
303
+ text = decodeEntities(text);
304
+ // Collapse whitespace
305
+ text = text
306
+ .split("\n")
307
+ .map((line) => line.trim())
308
+ .filter((line) => line.length > 0)
309
+ .join("\n");
310
+ // Remove excessive blank lines
311
+ text = text.replace(/\n{3,}/g, "\n\n");
312
+ if (title) {
313
+ text = `# ${title}\n\n${text}`;
314
+ }
315
+ return text.trim();
316
+ }
317
+ function decodeEntities(text) {
318
+ return text
319
+ .replace(/&amp;/g, "&")
320
+ .replace(/&lt;/g, "<")
321
+ .replace(/&gt;/g, ">")
322
+ .replace(/&quot;/g, '"')
323
+ .replace(/&#39;/g, "'")
324
+ .replace(/&apos;/g, "'")
325
+ .replace(/&nbsp;/g, " ")
326
+ .replace(/&#(\d+);/g, (_, n) => String.fromCharCode(Number(n)))
327
+ .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
328
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Built-in WebSearch tool — search the web via Serper, Tavily, or SearXNG.
3
+ *
4
+ * Provider selection / credentials are read in this precedence:
5
+ * 1. settings.search.{provider, apiKey, baseUrl} from ~/.code-shell/settings.json
6
+ * (set via the desktop "连接" UI or hand-edited)
7
+ * 2. env vars SERPER_API_KEY / TAVILY_API_KEY / SEARXNG_URL (legacy / CI override)
8
+ * Settings take precedence so the desktop UI's "保存连接" + "测试连接" is the
9
+ * single source of truth for end users.
10
+ */
11
+ import type { ToolDefinition } from "../../types.js";
12
+ export type SearchProvider = "serper" | "tavily" | "searxng";
13
+ export interface ResolvedSearchConfig {
14
+ provider: SearchProvider;
15
+ apiKey?: string;
16
+ baseUrl?: string;
17
+ source: "settings" | "env" | "none";
18
+ }
19
+ /**
20
+ * Resolve the active search config. Returns `source` so callers can show
21
+ * the user where the value came from when debugging.
22
+ */
23
+ export declare function resolveSearchConfig(cwd?: string): ResolvedSearchConfig;
24
+ export declare const webSearchToolDef: ToolDefinition;
25
+ export declare function webSearchTool(args: Record<string, unknown>): Promise<string>;