@f5-sales-demo/xcsh 19.51.2

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 (899) hide show
  1. package/CHANGELOG.md +7073 -0
  2. package/README.md +14 -0
  3. package/examples/README.md +21 -0
  4. package/examples/custom-tools/README.md +109 -0
  5. package/examples/custom-tools/hello/index.ts +20 -0
  6. package/examples/custom-tools/todo/index.ts +206 -0
  7. package/examples/extensions/README.md +143 -0
  8. package/examples/extensions/api-demo.ts +89 -0
  9. package/examples/extensions/chalk-logger.ts +25 -0
  10. package/examples/extensions/hello.ts +32 -0
  11. package/examples/extensions/pirate.ts +43 -0
  12. package/examples/extensions/plan-mode.ts +551 -0
  13. package/examples/extensions/reload-runtime.ts +37 -0
  14. package/examples/extensions/todo.ts +296 -0
  15. package/examples/extensions/tools.ts +145 -0
  16. package/examples/extensions/with-deps/index.ts +36 -0
  17. package/examples/extensions/with-deps/package-lock.json +31 -0
  18. package/examples/extensions/with-deps/package.json +16 -0
  19. package/examples/hooks/README.md +56 -0
  20. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  21. package/examples/hooks/confirm-destructive.ts +58 -0
  22. package/examples/hooks/custom-compaction.ts +115 -0
  23. package/examples/hooks/dirty-repo-guard.ts +51 -0
  24. package/examples/hooks/file-trigger.ts +40 -0
  25. package/examples/hooks/git-checkpoint.ts +52 -0
  26. package/examples/hooks/handoff.ts +149 -0
  27. package/examples/hooks/permission-gate.ts +33 -0
  28. package/examples/hooks/protected-paths.ts +29 -0
  29. package/examples/hooks/qna.ts +118 -0
  30. package/examples/hooks/status-line.ts +39 -0
  31. package/examples/sdk/01-minimal.ts +21 -0
  32. package/examples/sdk/02-custom-model.ts +49 -0
  33. package/examples/sdk/03-custom-prompt.ts +43 -0
  34. package/examples/sdk/04-skills.ts +43 -0
  35. package/examples/sdk/06-extensions.ts +80 -0
  36. package/examples/sdk/06-hooks.ts +61 -0
  37. package/examples/sdk/07-context-files.ts +35 -0
  38. package/examples/sdk/08-prompt-templates.ts +41 -0
  39. package/examples/sdk/08-slash-commands.ts +46 -0
  40. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  41. package/examples/sdk/11-sessions.ts +47 -0
  42. package/examples/sdk/README.md +172 -0
  43. package/package.json +539 -0
  44. package/scripts/build-info/resolvers.ts +51 -0
  45. package/scripts/capture-ax-fixture.ts +84 -0
  46. package/scripts/capture-login-fixture.ts +30 -0
  47. package/scripts/check-workflow-field-coverage.ts +101 -0
  48. package/scripts/extension-uat-harness.ts +340 -0
  49. package/scripts/format-prompts.ts +68 -0
  50. package/scripts/generate-api-spec-index.ts +580 -0
  51. package/scripts/generate-branding-index.ts +81 -0
  52. package/scripts/generate-build-info.ts +106 -0
  53. package/scripts/generate-console-catalog.ts +88 -0
  54. package/scripts/generate-console-field-metadata.ts +74 -0
  55. package/scripts/generate-docs-index.ts +40 -0
  56. package/scripts/generate-template.ts +32 -0
  57. package/scripts/generate-terraform-index.ts +81 -0
  58. package/scripts/mermaid-gallery.ts +132 -0
  59. package/scripts/uat-matrix-modalities.ts +451 -0
  60. package/scripts/uat-matrix-report.ts +175 -0
  61. package/scripts/uat-matrix.ts +353 -0
  62. package/src/async/index.ts +2 -0
  63. package/src/async/job-manager.ts +416 -0
  64. package/src/async/support.ts +5 -0
  65. package/src/autoresearch/apply-contract-to-state.ts +1 -0
  66. package/src/autoresearch/command-resume.md +17 -0
  67. package/src/autoresearch/contract.ts +205 -0
  68. package/src/autoresearch/dashboard.ts +374 -0
  69. package/src/autoresearch/git.ts +191 -0
  70. package/src/autoresearch/helpers.ts +328 -0
  71. package/src/autoresearch/index.ts +402 -0
  72. package/src/autoresearch/prompt.md +236 -0
  73. package/src/autoresearch/resume-message.md +16 -0
  74. package/src/autoresearch/state.ts +303 -0
  75. package/src/autoresearch/tools/init-experiment.ts +400 -0
  76. package/src/autoresearch/tools/log-experiment.ts +793 -0
  77. package/src/autoresearch/tools/run-experiment.ts +679 -0
  78. package/src/autoresearch/types.ts +114 -0
  79. package/src/browser/acquire.ts +249 -0
  80. package/src/browser/actions.ts +126 -0
  81. package/src/browser/auth.ts +176 -0
  82. package/src/browser/ax.ts +134 -0
  83. package/src/browser/cdp-core.ts +45 -0
  84. package/src/browser/cdp-page-actions.ts +51 -0
  85. package/src/browser/chrome-locate.ts +66 -0
  86. package/src/browser/dom-context.ts +89 -0
  87. package/src/browser/dt-context.ts +100 -0
  88. package/src/browser/extension-bridge.ts +182 -0
  89. package/src/browser/extension-page-actions.ts +397 -0
  90. package/src/browser/extension-provider.ts +373 -0
  91. package/src/browser/index.ts +17 -0
  92. package/src/browser/input-commit.ts +74 -0
  93. package/src/browser/native-messaging.ts +20 -0
  94. package/src/browser/page-actions.ts +18 -0
  95. package/src/browser/provider.ts +155 -0
  96. package/src/browser/resolver.ts +126 -0
  97. package/src/browser/selector.ts +40 -0
  98. package/src/bun-imports.d.ts +22 -0
  99. package/src/capability/context-file.ts +43 -0
  100. package/src/capability/extension-module.ts +34 -0
  101. package/src/capability/extension.ts +47 -0
  102. package/src/capability/fs.ts +107 -0
  103. package/src/capability/hook.ts +40 -0
  104. package/src/capability/index.ts +438 -0
  105. package/src/capability/instruction.ts +37 -0
  106. package/src/capability/mcp.ts +74 -0
  107. package/src/capability/prompt.ts +35 -0
  108. package/src/capability/rule.ts +224 -0
  109. package/src/capability/settings.ts +34 -0
  110. package/src/capability/skill.ts +50 -0
  111. package/src/capability/slash-command.ts +40 -0
  112. package/src/capability/ssh.ts +41 -0
  113. package/src/capability/system-prompt.ts +34 -0
  114. package/src/capability/tool.ts +38 -0
  115. package/src/capability/types.ts +168 -0
  116. package/src/cli/agents-cli.ts +138 -0
  117. package/src/cli/args.ts +286 -0
  118. package/src/cli/chrome-cli.ts +88 -0
  119. package/src/cli/classify-install-target.ts +50 -0
  120. package/src/cli/commands/init-xdg.ts +27 -0
  121. package/src/cli/config-cli.ts +418 -0
  122. package/src/cli/file-processor.ts +121 -0
  123. package/src/cli/grep-cli.ts +160 -0
  124. package/src/cli/grievances-cli.ts +78 -0
  125. package/src/cli/initial-message.ts +58 -0
  126. package/src/cli/jupyter-cli.ts +106 -0
  127. package/src/cli/list-models.ts +128 -0
  128. package/src/cli/plugin-cli.ts +964 -0
  129. package/src/cli/read-cli.ts +68 -0
  130. package/src/cli/session-picker.ts +52 -0
  131. package/src/cli/setup-cli.ts +479 -0
  132. package/src/cli/shell-cli.ts +174 -0
  133. package/src/cli/ssh-cli.ts +179 -0
  134. package/src/cli/stats-cli.ts +182 -0
  135. package/src/cli/update-cli.ts +446 -0
  136. package/src/cli/web-search-cli.ts +203 -0
  137. package/src/cli.ts +123 -0
  138. package/src/commands/agents.ts +57 -0
  139. package/src/commands/chrome-host.ts +71 -0
  140. package/src/commands/chrome.ts +28 -0
  141. package/src/commands/commit.ts +36 -0
  142. package/src/commands/config.ts +51 -0
  143. package/src/commands/grep.ts +48 -0
  144. package/src/commands/grievances.ts +20 -0
  145. package/src/commands/jupyter.ts +32 -0
  146. package/src/commands/launch.ts +144 -0
  147. package/src/commands/plugin.ts +78 -0
  148. package/src/commands/read.ts +33 -0
  149. package/src/commands/setup.ts +42 -0
  150. package/src/commands/shell.ts +29 -0
  151. package/src/commands/ssh.ts +60 -0
  152. package/src/commands/stats.ts +29 -0
  153. package/src/commands/update.ts +21 -0
  154. package/src/commands/web-search.ts +46 -0
  155. package/src/commit/agentic/agent.ts +314 -0
  156. package/src/commit/agentic/fallback.ts +96 -0
  157. package/src/commit/agentic/index.ts +354 -0
  158. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  159. package/src/commit/agentic/prompts/session-user.md +25 -0
  160. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  161. package/src/commit/agentic/prompts/system.md +38 -0
  162. package/src/commit/agentic/state.ts +60 -0
  163. package/src/commit/agentic/tools/analyze-file.ts +127 -0
  164. package/src/commit/agentic/tools/git-file-diff.ts +191 -0
  165. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  166. package/src/commit/agentic/tools/git-overview.ts +81 -0
  167. package/src/commit/agentic/tools/index.ts +54 -0
  168. package/src/commit/agentic/tools/propose-changelog.ts +139 -0
  169. package/src/commit/agentic/tools/propose-commit.ts +122 -0
  170. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  171. package/src/commit/agentic/tools/schemas.ts +31 -0
  172. package/src/commit/agentic/tools/split-commit.ts +251 -0
  173. package/src/commit/agentic/topo-sort.ts +44 -0
  174. package/src/commit/agentic/trivial.ts +51 -0
  175. package/src/commit/agentic/validation.ts +183 -0
  176. package/src/commit/analysis/conventional.ts +122 -0
  177. package/src/commit/analysis/index.ts +4 -0
  178. package/src/commit/analysis/scope.ts +242 -0
  179. package/src/commit/analysis/summary.ts +105 -0
  180. package/src/commit/analysis/validation.ts +66 -0
  181. package/src/commit/changelog/detect.ts +40 -0
  182. package/src/commit/changelog/generate.ts +101 -0
  183. package/src/commit/changelog/index.ts +234 -0
  184. package/src/commit/changelog/parse.ts +44 -0
  185. package/src/commit/cli.ts +85 -0
  186. package/src/commit/git/diff.ts +148 -0
  187. package/src/commit/index.ts +5 -0
  188. package/src/commit/map-reduce/index.ts +69 -0
  189. package/src/commit/map-reduce/map-phase.ts +193 -0
  190. package/src/commit/map-reduce/reduce-phase.ts +108 -0
  191. package/src/commit/map-reduce/utils.ts +9 -0
  192. package/src/commit/message.ts +11 -0
  193. package/src/commit/model-selection.ts +66 -0
  194. package/src/commit/pipeline.ts +242 -0
  195. package/src/commit/prompts/analysis-system.md +148 -0
  196. package/src/commit/prompts/analysis-user.md +38 -0
  197. package/src/commit/prompts/changelog-system.md +50 -0
  198. package/src/commit/prompts/changelog-user.md +18 -0
  199. package/src/commit/prompts/file-observer-system.md +24 -0
  200. package/src/commit/prompts/file-observer-user.md +8 -0
  201. package/src/commit/prompts/reduce-system.md +50 -0
  202. package/src/commit/prompts/reduce-user.md +17 -0
  203. package/src/commit/prompts/summary-retry.md +3 -0
  204. package/src/commit/prompts/summary-system.md +38 -0
  205. package/src/commit/prompts/summary-user.md +13 -0
  206. package/src/commit/prompts/types-description.md +2 -0
  207. package/src/commit/types.ts +118 -0
  208. package/src/commit/utils/exclusions.ts +42 -0
  209. package/src/commit/utils.ts +44 -0
  210. package/src/config/auto-config.ts +698 -0
  211. package/src/config/context-schema.json +115 -0
  212. package/src/config/file-lock.ts +121 -0
  213. package/src/config/keybindings.ts +493 -0
  214. package/src/config/mcp-schema.json +230 -0
  215. package/src/config/model-equivalence.ts +675 -0
  216. package/src/config/model-registry.ts +2265 -0
  217. package/src/config/model-resolver.ts +1289 -0
  218. package/src/config/prompt-templates.ts +271 -0
  219. package/src/config/resolve-config-value.ts +110 -0
  220. package/src/config/settings-schema.ts +2155 -0
  221. package/src/config/settings.ts +730 -0
  222. package/src/config.ts +418 -0
  223. package/src/cursor.ts +323 -0
  224. package/src/dap/client.ts +674 -0
  225. package/src/dap/config.ts +150 -0
  226. package/src/dap/defaults.json +211 -0
  227. package/src/dap/index.ts +4 -0
  228. package/src/dap/session.ts +1255 -0
  229. package/src/dap/types.ts +600 -0
  230. package/src/debug/index.ts +440 -0
  231. package/src/debug/log-formatting.ts +58 -0
  232. package/src/debug/log-viewer.ts +908 -0
  233. package/src/debug/profiler.ts +158 -0
  234. package/src/debug/report-bundle.ts +365 -0
  235. package/src/debug/system-info.ts +107 -0
  236. package/src/discovery/agents-md.ts +67 -0
  237. package/src/discovery/agents.ts +219 -0
  238. package/src/discovery/builtin.ts +851 -0
  239. package/src/discovery/claude-plugins.ts +297 -0
  240. package/src/discovery/claude.ts +543 -0
  241. package/src/discovery/cline.ts +83 -0
  242. package/src/discovery/codex.ts +535 -0
  243. package/src/discovery/cursor.ts +220 -0
  244. package/src/discovery/gemini.ts +396 -0
  245. package/src/discovery/github.ts +118 -0
  246. package/src/discovery/helpers.ts +934 -0
  247. package/src/discovery/index.ts +79 -0
  248. package/src/discovery/language.ts +46 -0
  249. package/src/discovery/mcp-json.ts +171 -0
  250. package/src/discovery/opencode.ts +393 -0
  251. package/src/discovery/plugin-dir-roots.ts +28 -0
  252. package/src/discovery/ssh.ts +153 -0
  253. package/src/discovery/substitute-plugin-root.ts +29 -0
  254. package/src/discovery/vscode.ts +105 -0
  255. package/src/discovery/windsurf.ts +147 -0
  256. package/src/edit/diff.ts +818 -0
  257. package/src/edit/index.ts +481 -0
  258. package/src/edit/line-hash.ts +67 -0
  259. package/src/edit/modes/chunk.ts +762 -0
  260. package/src/edit/modes/hashline.ts +1366 -0
  261. package/src/edit/modes/patch.ts +1791 -0
  262. package/src/edit/modes/replace.ts +1115 -0
  263. package/src/edit/normalize.ts +375 -0
  264. package/src/edit/renderer.ts +627 -0
  265. package/src/exa/factory.ts +61 -0
  266. package/src/exa/index.ts +27 -0
  267. package/src/exa/mcp-client.ts +293 -0
  268. package/src/exa/render.ts +232 -0
  269. package/src/exa/researcher.ts +47 -0
  270. package/src/exa/search.ts +86 -0
  271. package/src/exa/types.ts +166 -0
  272. package/src/exa/websets.ts +247 -0
  273. package/src/exec/bash-executor.ts +332 -0
  274. package/src/exec/exec.ts +53 -0
  275. package/src/exec/idle-timeout-watchdog.ts +126 -0
  276. package/src/exec/non-interactive-env.ts +48 -0
  277. package/src/export/custom-share.ts +65 -0
  278. package/src/export/html/index.ts +161 -0
  279. package/src/export/html/template.css +1001 -0
  280. package/src/export/html/template.generated.ts +2 -0
  281. package/src/export/html/template.html +46 -0
  282. package/src/export/html/template.js +1950 -0
  283. package/src/export/html/template.macro.ts +24 -0
  284. package/src/export/html/vendor/highlight.min.js +1213 -0
  285. package/src/export/html/vendor/marked.min.js +6 -0
  286. package/src/export/ttsr.ts +434 -0
  287. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +25 -0
  288. package/src/extensibility/custom-commands/bundled/review/index.ts +456 -0
  289. package/src/extensibility/custom-commands/index.ts +2 -0
  290. package/src/extensibility/custom-commands/loader.ts +236 -0
  291. package/src/extensibility/custom-commands/types.ts +111 -0
  292. package/src/extensibility/custom-tools/index.ts +7 -0
  293. package/src/extensibility/custom-tools/loader.ts +238 -0
  294. package/src/extensibility/custom-tools/types.ts +251 -0
  295. package/src/extensibility/custom-tools/wrapper.ts +47 -0
  296. package/src/extensibility/extensions/index.ts +15 -0
  297. package/src/extensibility/extensions/loader.ts +572 -0
  298. package/src/extensibility/extensions/runner.ts +848 -0
  299. package/src/extensibility/extensions/types.ts +1394 -0
  300. package/src/extensibility/extensions/wrapper.ts +191 -0
  301. package/src/extensibility/hooks/index.ts +5 -0
  302. package/src/extensibility/hooks/loader.ts +255 -0
  303. package/src/extensibility/hooks/runner.ts +425 -0
  304. package/src/extensibility/hooks/tool-wrapper.ts +108 -0
  305. package/src/extensibility/hooks/types.ts +826 -0
  306. package/src/extensibility/plugins/doctor.ts +66 -0
  307. package/src/extensibility/plugins/git-url.ts +281 -0
  308. package/src/extensibility/plugins/index.ts +9 -0
  309. package/src/extensibility/plugins/installer.ts +192 -0
  310. package/src/extensibility/plugins/loader.ts +267 -0
  311. package/src/extensibility/plugins/manager.ts +731 -0
  312. package/src/extensibility/plugins/marketplace/cache.ts +136 -0
  313. package/src/extensibility/plugins/marketplace/fetcher.ts +318 -0
  314. package/src/extensibility/plugins/marketplace/index.ts +6 -0
  315. package/src/extensibility/plugins/marketplace/manager.ts +781 -0
  316. package/src/extensibility/plugins/marketplace/prerequisites.ts +255 -0
  317. package/src/extensibility/plugins/marketplace/registry.ts +196 -0
  318. package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
  319. package/src/extensibility/plugins/marketplace/types.ts +201 -0
  320. package/src/extensibility/plugins/parser.ts +105 -0
  321. package/src/extensibility/plugins/types.ts +194 -0
  322. package/src/extensibility/skills.ts +269 -0
  323. package/src/extensibility/slash-commands.ts +246 -0
  324. package/src/extensibility/tool-proxy.ts +25 -0
  325. package/src/extensibility/utils.ts +38 -0
  326. package/src/index.ts +61 -0
  327. package/src/internal-urls/agent-protocol.ts +136 -0
  328. package/src/internal-urls/api-catalog-resolve.ts +495 -0
  329. package/src/internal-urls/api-catalog-types.ts +94 -0
  330. package/src/internal-urls/api-spec-resolve.ts +1036 -0
  331. package/src/internal-urls/api-spec-types.ts +239 -0
  332. package/src/internal-urls/artifact-protocol.ts +97 -0
  333. package/src/internal-urls/branding-index.generated.ts +88 -0
  334. package/src/internal-urls/build-info-runtime.ts +302 -0
  335. package/src/internal-urls/build-info.generated.ts +33 -0
  336. package/src/internal-urls/computer-profile.ts +692 -0
  337. package/src/internal-urls/console-catalog-types.ts +21 -0
  338. package/src/internal-urls/console-catalog.generated.ts +532 -0
  339. package/src/internal-urls/console-field-metadata-types.ts +59 -0
  340. package/src/internal-urls/console-field-metadata.generated.ts +4841 -0
  341. package/src/internal-urls/console-resolve.ts +178 -0
  342. package/src/internal-urls/docs-index.generated.ts +698 -0
  343. package/src/internal-urls/extension-api.md +147 -0
  344. package/src/internal-urls/index.ts +46 -0
  345. package/src/internal-urls/jobs-protocol.ts +119 -0
  346. package/src/internal-urls/json-query.ts +126 -0
  347. package/src/internal-urls/local-protocol.ts +206 -0
  348. package/src/internal-urls/mcp-protocol.ts +156 -0
  349. package/src/internal-urls/memory-protocol.ts +133 -0
  350. package/src/internal-urls/parse.ts +72 -0
  351. package/src/internal-urls/profile-collectors.ts +106 -0
  352. package/src/internal-urls/router.ts +55 -0
  353. package/src/internal-urls/rule-protocol.ts +55 -0
  354. package/src/internal-urls/skill-protocol.ts +111 -0
  355. package/src/internal-urls/terraform-index.generated.ts +1852 -0
  356. package/src/internal-urls/terraform-resolve.ts +184 -0
  357. package/src/internal-urls/terraform-types.ts +45 -0
  358. package/src/internal-urls/types.ts +52 -0
  359. package/src/internal-urls/user-profile.ts +363 -0
  360. package/src/internal-urls/xcsh-protocol.ts +644 -0
  361. package/src/ipy/cancellation.ts +28 -0
  362. package/src/ipy/executor.ts +1155 -0
  363. package/src/ipy/gateway-coordinator.ts +424 -0
  364. package/src/ipy/kernel.ts +1091 -0
  365. package/src/ipy/modules.ts +144 -0
  366. package/src/ipy/prelude.py +849 -0
  367. package/src/ipy/prelude.ts +3 -0
  368. package/src/ipy/runtime.ts +221 -0
  369. package/src/locales/ar.json +512 -0
  370. package/src/locales/de.json +512 -0
  371. package/src/locales/en.json +549 -0
  372. package/src/locales/es.json +512 -0
  373. package/src/locales/fr.json +512 -0
  374. package/src/locales/hi.json +512 -0
  375. package/src/locales/index.ts +29 -0
  376. package/src/locales/it.json +512 -0
  377. package/src/locales/ja.json +512 -0
  378. package/src/locales/ko.json +512 -0
  379. package/src/locales/pt-br.json +512 -0
  380. package/src/locales/th.json +512 -0
  381. package/src/locales/zh-cn.json +512 -0
  382. package/src/locales/zh-tw.json +512 -0
  383. package/src/lsp/client.ts +941 -0
  384. package/src/lsp/clients/biome-client.ts +202 -0
  385. package/src/lsp/clients/index.ts +50 -0
  386. package/src/lsp/clients/lsp-linter-client.ts +93 -0
  387. package/src/lsp/clients/swiftlint-client.ts +120 -0
  388. package/src/lsp/config.ts +418 -0
  389. package/src/lsp/defaults.json +999 -0
  390. package/src/lsp/edits.ts +109 -0
  391. package/src/lsp/index.ts +1746 -0
  392. package/src/lsp/lspmux.ts +233 -0
  393. package/src/lsp/render.ts +692 -0
  394. package/src/lsp/startup-events.ts +13 -0
  395. package/src/lsp/types.ts +444 -0
  396. package/src/lsp/utils.ts +691 -0
  397. package/src/main.ts +944 -0
  398. package/src/mcp/client.ts +482 -0
  399. package/src/mcp/config-writer.ts +225 -0
  400. package/src/mcp/config.ts +365 -0
  401. package/src/mcp/discoverable-tool-metadata.ts +202 -0
  402. package/src/mcp/index.ts +29 -0
  403. package/src/mcp/json-rpc.ts +84 -0
  404. package/src/mcp/loader.ts +124 -0
  405. package/src/mcp/manager.ts +1152 -0
  406. package/src/mcp/oauth-discovery.ts +349 -0
  407. package/src/mcp/oauth-flow.ts +387 -0
  408. package/src/mcp/render.ts +157 -0
  409. package/src/mcp/smithery-auth.ts +104 -0
  410. package/src/mcp/smithery-connect.ts +145 -0
  411. package/src/mcp/smithery-registry.ts +477 -0
  412. package/src/mcp/tool-bridge.ts +417 -0
  413. package/src/mcp/tool-cache.ts +117 -0
  414. package/src/mcp/transports/http.ts +475 -0
  415. package/src/mcp/transports/index.ts +6 -0
  416. package/src/mcp/transports/stdio.ts +325 -0
  417. package/src/mcp/types.ts +423 -0
  418. package/src/memories/index.ts +1115 -0
  419. package/src/memories/storage.ts +577 -0
  420. package/src/modes/acp/acp-agent.ts +1361 -0
  421. package/src/modes/acp/acp-event-mapper.ts +538 -0
  422. package/src/modes/acp/acp-mode.ts +15 -0
  423. package/src/modes/acp/index.ts +2 -0
  424. package/src/modes/components/agent-dashboard.ts +1124 -0
  425. package/src/modes/components/assistant-message.ts +213 -0
  426. package/src/modes/components/bash-execution.ts +345 -0
  427. package/src/modes/components/bordered-loader.ts +41 -0
  428. package/src/modes/components/branch-summary-message.ts +45 -0
  429. package/src/modes/components/btw-panel.ts +103 -0
  430. package/src/modes/components/compaction-summary-message.ts +51 -0
  431. package/src/modes/components/context-add-wizard.ts +534 -0
  432. package/src/modes/components/countdown-timer.ts +75 -0
  433. package/src/modes/components/custom-editor.ts +238 -0
  434. package/src/modes/components/custom-message.ts +91 -0
  435. package/src/modes/components/diff.ts +210 -0
  436. package/src/modes/components/dynamic-border.ts +25 -0
  437. package/src/modes/components/extensions/extension-dashboard.ts +326 -0
  438. package/src/modes/components/extensions/extension-list.ts +492 -0
  439. package/src/modes/components/extensions/index.ts +9 -0
  440. package/src/modes/components/extensions/inspector-panel.ts +317 -0
  441. package/src/modes/components/extensions/state-manager.ts +587 -0
  442. package/src/modes/components/extensions/types.ts +191 -0
  443. package/src/modes/components/footer.ts +262 -0
  444. package/src/modes/components/gutter-block.ts +315 -0
  445. package/src/modes/components/history-search.ts +158 -0
  446. package/src/modes/components/hook-editor.ts +151 -0
  447. package/src/modes/components/hook-input.ts +81 -0
  448. package/src/modes/components/hook-message.ts +100 -0
  449. package/src/modes/components/hook-selector.ts +192 -0
  450. package/src/modes/components/index.ts +36 -0
  451. package/src/modes/components/keybinding-hints.ts +65 -0
  452. package/src/modes/components/login-dialog.ts +164 -0
  453. package/src/modes/components/mcp-add-wizard.ts +1314 -0
  454. package/src/modes/components/model-selector.ts +946 -0
  455. package/src/modes/components/oauth-selector.ts +211 -0
  456. package/src/modes/components/plugin-selector.ts +95 -0
  457. package/src/modes/components/plugin-settings.ts +477 -0
  458. package/src/modes/components/plugins/index.ts +5 -0
  459. package/src/modes/components/plugins/plugin-dashboard.ts +523 -0
  460. package/src/modes/components/plugins/plugin-inspector-pane.ts +84 -0
  461. package/src/modes/components/plugins/plugin-list-pane.ts +109 -0
  462. package/src/modes/components/plugins/state-manager.ts +229 -0
  463. package/src/modes/components/plugins/types.ts +51 -0
  464. package/src/modes/components/plugins/utils.ts +6 -0
  465. package/src/modes/components/python-execution.ts +244 -0
  466. package/src/modes/components/queue-mode-selector.ts +56 -0
  467. package/src/modes/components/read-tool-group.ts +137 -0
  468. package/src/modes/components/session-observer-overlay.ts +483 -0
  469. package/src/modes/components/session-selector.ts +343 -0
  470. package/src/modes/components/settings-defs.ts +568 -0
  471. package/src/modes/components/settings-selector.ts +641 -0
  472. package/src/modes/components/show-images-selector.ts +45 -0
  473. package/src/modes/components/skill-message.ts +90 -0
  474. package/src/modes/components/status-line/context-gradient.ts +64 -0
  475. package/src/modes/components/status-line/git-utils.ts +42 -0
  476. package/src/modes/components/status-line/hex-ansi.ts +29 -0
  477. package/src/modes/components/status-line/index.ts +4 -0
  478. package/src/modes/components/status-line/presets.ts +174 -0
  479. package/src/modes/components/status-line/segments.ts +494 -0
  480. package/src/modes/components/status-line/separators.ts +55 -0
  481. package/src/modes/components/status-line/token-rate.ts +66 -0
  482. package/src/modes/components/status-line/types.ts +96 -0
  483. package/src/modes/components/status-line-segment-editor.ts +361 -0
  484. package/src/modes/components/status-line.ts +729 -0
  485. package/src/modes/components/theme-selector.ts +63 -0
  486. package/src/modes/components/thinking-selector.ts +52 -0
  487. package/src/modes/components/todo-reminder.ts +54 -0
  488. package/src/modes/components/tool-execution.ts +781 -0
  489. package/src/modes/components/tree-selector.ts +908 -0
  490. package/src/modes/components/ttsr-notification.ts +80 -0
  491. package/src/modes/components/user-message-selector.ts +141 -0
  492. package/src/modes/components/user-message.ts +100 -0
  493. package/src/modes/components/visual-truncate.ts +63 -0
  494. package/src/modes/components/welcome-checks.ts +313 -0
  495. package/src/modes/components/welcome.ts +270 -0
  496. package/src/modes/controllers/btw-controller.ts +193 -0
  497. package/src/modes/controllers/chord-routing.ts +37 -0
  498. package/src/modes/controllers/command-controller.ts +1293 -0
  499. package/src/modes/controllers/context-command-controller.ts +61 -0
  500. package/src/modes/controllers/event-controller.ts +743 -0
  501. package/src/modes/controllers/extension-ui-controller.ts +983 -0
  502. package/src/modes/controllers/input-controller.ts +877 -0
  503. package/src/modes/controllers/mcp-command-controller.ts +1940 -0
  504. package/src/modes/controllers/selector-controller.ts +1292 -0
  505. package/src/modes/controllers/ssh-command-controller.ts +421 -0
  506. package/src/modes/index.ts +34 -0
  507. package/src/modes/interactive-mode.ts +1781 -0
  508. package/src/modes/oauth-manual-input.ts +42 -0
  509. package/src/modes/print-mode.ts +196 -0
  510. package/src/modes/prompt-action-autocomplete.ts +231 -0
  511. package/src/modes/rpc/host-tools.ts +186 -0
  512. package/src/modes/rpc/rpc-client.ts +762 -0
  513. package/src/modes/rpc/rpc-mode.ts +877 -0
  514. package/src/modes/rpc/rpc-types.ts +322 -0
  515. package/src/modes/session-observer-registry.ts +146 -0
  516. package/src/modes/shared.ts +30 -0
  517. package/src/modes/theme/defaults/index.ts +7 -0
  518. package/src/modes/theme/defaults/xcsh-dark.json +113 -0
  519. package/src/modes/theme/defaults/xcsh-light.json +115 -0
  520. package/src/modes/theme/mermaid-cache.ts +151 -0
  521. package/src/modes/theme/mermaid-palette.ts +39 -0
  522. package/src/modes/theme/theme-schema.json +553 -0
  523. package/src/modes/theme/theme.ts +2563 -0
  524. package/src/modes/types.ts +273 -0
  525. package/src/modes/utils/hotkeys-markdown.ts +60 -0
  526. package/src/modes/utils/keybinding-matchers.ts +30 -0
  527. package/src/modes/utils/read-group-outcome-aggregator.ts +55 -0
  528. package/src/modes/utils/sanitize-error-message.ts +60 -0
  529. package/src/modes/utils/tools-markdown.ts +28 -0
  530. package/src/modes/utils/ui-helpers.ts +734 -0
  531. package/src/plan-mode/approved-plan.ts +55 -0
  532. package/src/plan-mode/state.ts +6 -0
  533. package/src/priority.json +37 -0
  534. package/src/prompts/agents/designer.md +67 -0
  535. package/src/prompts/agents/explore.md +61 -0
  536. package/src/prompts/agents/frontmatter.md +10 -0
  537. package/src/prompts/agents/init.md +36 -0
  538. package/src/prompts/agents/librarian.md +121 -0
  539. package/src/prompts/agents/plan.md +49 -0
  540. package/src/prompts/agents/reviewer.md +127 -0
  541. package/src/prompts/agents/task.md +16 -0
  542. package/src/prompts/ci-green-request.md +35 -0
  543. package/src/prompts/compaction/branch-summary-context.md +5 -0
  544. package/src/prompts/compaction/branch-summary-preamble.md +2 -0
  545. package/src/prompts/compaction/branch-summary.md +30 -0
  546. package/src/prompts/compaction/compaction-short-summary.md +9 -0
  547. package/src/prompts/compaction/compaction-summary-context.md +5 -0
  548. package/src/prompts/compaction/compaction-summary.md +38 -0
  549. package/src/prompts/compaction/compaction-turn-prefix.md +17 -0
  550. package/src/prompts/compaction/compaction-update-summary.md +45 -0
  551. package/src/prompts/memories/consolidation.md +30 -0
  552. package/src/prompts/memories/read-path.md +11 -0
  553. package/src/prompts/memories/stage_one_input.md +6 -0
  554. package/src/prompts/memories/stage_one_system.md +21 -0
  555. package/src/prompts/review-request.md +70 -0
  556. package/src/prompts/system/agent-creation-architect.md +65 -0
  557. package/src/prompts/system/agent-creation-user.md +6 -0
  558. package/src/prompts/system/auto-handoff-threshold-focus.md +1 -0
  559. package/src/prompts/system/btw-user.md +8 -0
  560. package/src/prompts/system/commit-message-system.md +2 -0
  561. package/src/prompts/system/custom-system-prompt.md +82 -0
  562. package/src/prompts/system/eager-todo.md +13 -0
  563. package/src/prompts/system/file-operations.md +10 -0
  564. package/src/prompts/system/handoff-document.md +48 -0
  565. package/src/prompts/system/plan-mode-active.md +114 -0
  566. package/src/prompts/system/plan-mode-approved.md +23 -0
  567. package/src/prompts/system/plan-mode-reference.md +14 -0
  568. package/src/prompts/system/plan-mode-subagent.md +34 -0
  569. package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
  570. package/src/prompts/system/subagent-submit-reminder.md +7 -0
  571. package/src/prompts/system/subagent-system-prompt.md +36 -0
  572. package/src/prompts/system/subagent-user-prompt.md +12 -0
  573. package/src/prompts/system/summarization-system.md +3 -0
  574. package/src/prompts/system/system-prompt.md +698 -0
  575. package/src/prompts/system/title-system.md +2 -0
  576. package/src/prompts/system/ttsr-interrupt.md +7 -0
  577. package/src/prompts/system/web-search.md +28 -0
  578. package/src/prompts/tools/ask.md +28 -0
  579. package/src/prompts/tools/ast-edit.md +48 -0
  580. package/src/prompts/tools/ast-grep.md +54 -0
  581. package/src/prompts/tools/async-result.md +5 -0
  582. package/src/prompts/tools/bash.md +67 -0
  583. package/src/prompts/tools/browser.md +26 -0
  584. package/src/prompts/tools/calculator.md +10 -0
  585. package/src/prompts/tools/cancel-job.md +5 -0
  586. package/src/prompts/tools/catalog-workflow-runner.md +13 -0
  587. package/src/prompts/tools/checkpoint.md +16 -0
  588. package/src/prompts/tools/chunk-edit.md +279 -0
  589. package/src/prompts/tools/debug.md +43 -0
  590. package/src/prompts/tools/display-image.md +23 -0
  591. package/src/prompts/tools/exit-plan-mode.md +41 -0
  592. package/src/prompts/tools/find.md +26 -0
  593. package/src/prompts/tools/gemini-image.md +7 -0
  594. package/src/prompts/tools/grep.md +29 -0
  595. package/src/prompts/tools/hashline.md +131 -0
  596. package/src/prompts/tools/inspect-image-system.md +20 -0
  597. package/src/prompts/tools/inspect-image.md +32 -0
  598. package/src/prompts/tools/lsp.md +33 -0
  599. package/src/prompts/tools/patch.md +77 -0
  600. package/src/prompts/tools/poll.md +5 -0
  601. package/src/prompts/tools/python.md +60 -0
  602. package/src/prompts/tools/read-chunk.md +35 -0
  603. package/src/prompts/tools/read.md +63 -0
  604. package/src/prompts/tools/render-mermaid.md +9 -0
  605. package/src/prompts/tools/replace.md +36 -0
  606. package/src/prompts/tools/resolve.md +8 -0
  607. package/src/prompts/tools/rewind.md +13 -0
  608. package/src/prompts/tools/search-tool-bm25.md +34 -0
  609. package/src/prompts/tools/ssh.md +44 -0
  610. package/src/prompts/tools/task-summary.md +28 -0
  611. package/src/prompts/tools/task.md +131 -0
  612. package/src/prompts/tools/todo-write.md +89 -0
  613. package/src/prompts/tools/vim.md +98 -0
  614. package/src/prompts/tools/web-search.md +10 -0
  615. package/src/prompts/tools/write.md +14 -0
  616. package/src/prompts/tools/xcsh-api.md +283 -0
  617. package/src/resource-management/index.ts +42 -0
  618. package/src/sdk.ts +2123 -0
  619. package/src/secrets/index.ts +146 -0
  620. package/src/secrets/obfuscator.ts +290 -0
  621. package/src/secrets/regex.ts +21 -0
  622. package/src/services/context-env.ts +102 -0
  623. package/src/services/xcsh-api-client.ts +230 -0
  624. package/src/services/xcsh-context-command.ts +803 -0
  625. package/src/services/xcsh-context-display.ts +49 -0
  626. package/src/services/xcsh-context-indicators.ts +17 -0
  627. package/src/services/xcsh-context-segment.ts +39 -0
  628. package/src/services/xcsh-context.ts +1410 -0
  629. package/src/services/xcsh-env.ts +107 -0
  630. package/src/services/xcsh-knowledge.ts +177 -0
  631. package/src/services/xcsh-table.ts +164 -0
  632. package/src/session/agent-session.ts +6690 -0
  633. package/src/session/agent-storage.ts +451 -0
  634. package/src/session/artifacts.ts +132 -0
  635. package/src/session/auth-storage.ts +17 -0
  636. package/src/session/blob-store.ts +135 -0
  637. package/src/session/compaction/branch-summarization.ts +322 -0
  638. package/src/session/compaction/compaction.ts +1388 -0
  639. package/src/session/compaction/index.ts +7 -0
  640. package/src/session/compaction/pruning.ts +91 -0
  641. package/src/session/compaction/utils.ts +184 -0
  642. package/src/session/history-storage.ts +253 -0
  643. package/src/session/messages.ts +597 -0
  644. package/src/session/session-dump-format.ts +203 -0
  645. package/src/session/session-manager.ts +2816 -0
  646. package/src/session/session-storage.ts +370 -0
  647. package/src/session/streaming-output.ts +770 -0
  648. package/src/session/tool-choice-queue.ts +213 -0
  649. package/src/slash-commands/builtin-registry.ts +1614 -0
  650. package/src/slash-commands/export-command.ts +178 -0
  651. package/src/slash-commands/marketplace-install-parser.ts +99 -0
  652. package/src/slash-commands/resource-commands.ts +250 -0
  653. package/src/ssh/config-writer.ts +183 -0
  654. package/src/ssh/connection-manager.ts +445 -0
  655. package/src/ssh/ssh-executor.ts +131 -0
  656. package/src/ssh/sshfs-mount.ts +137 -0
  657. package/src/ssh/utils.ts +8 -0
  658. package/src/stt/downloader.ts +71 -0
  659. package/src/stt/index.ts +3 -0
  660. package/src/stt/recorder.ts +351 -0
  661. package/src/stt/setup.ts +52 -0
  662. package/src/stt/stt-controller.ts +160 -0
  663. package/src/stt/transcribe.py +70 -0
  664. package/src/stt/transcriber.ts +91 -0
  665. package/src/system-prompt.ts +688 -0
  666. package/src/task/agents.ts +166 -0
  667. package/src/task/commands.ts +131 -0
  668. package/src/task/discovery.ts +126 -0
  669. package/src/task/executor.ts +1324 -0
  670. package/src/task/index.ts +1197 -0
  671. package/src/task/isolation-backend.ts +72 -0
  672. package/src/task/name-generator.ts +1577 -0
  673. package/src/task/output-manager.ts +107 -0
  674. package/src/task/parallel.ts +116 -0
  675. package/src/task/render.ts +1035 -0
  676. package/src/task/subprocess-tool-registry.ts +88 -0
  677. package/src/task/template.ts +33 -0
  678. package/src/task/types.ts +230 -0
  679. package/src/task/worktree.ts +580 -0
  680. package/src/task/xcsh-command.ts +26 -0
  681. package/src/thinking.ts +87 -0
  682. package/src/tools/action-renderer.ts +198 -0
  683. package/src/tools/archive-reader.ts +315 -0
  684. package/src/tools/ask.ts +791 -0
  685. package/src/tools/ast-edit.ts +492 -0
  686. package/src/tools/ast-grep.ts +453 -0
  687. package/src/tools/auto-generated-guard.ts +305 -0
  688. package/src/tools/bash-interactive.ts +383 -0
  689. package/src/tools/bash-interceptor.ts +67 -0
  690. package/src/tools/bash-normalize.ts +107 -0
  691. package/src/tools/bash-skill-urls.ts +246 -0
  692. package/src/tools/bash.ts +878 -0
  693. package/src/tools/browser-renderer.ts +129 -0
  694. package/src/tools/browser.ts +1366 -0
  695. package/src/tools/calculator.ts +543 -0
  696. package/src/tools/cancel-job.ts +100 -0
  697. package/src/tools/catalog-workflow-runner.ts +718 -0
  698. package/src/tools/checkpoint.ts +133 -0
  699. package/src/tools/context.ts +39 -0
  700. package/src/tools/debug.ts +1004 -0
  701. package/src/tools/display-image-renderer.ts +96 -0
  702. package/src/tools/display-image.ts +109 -0
  703. package/src/tools/exit-plan-mode.ts +101 -0
  704. package/src/tools/fetch.ts +1462 -0
  705. package/src/tools/find.ts +437 -0
  706. package/src/tools/fs-cache-invalidation.ts +33 -0
  707. package/src/tools/gemini-image.ts +1004 -0
  708. package/src/tools/grep.ts +705 -0
  709. package/src/tools/index.ts +442 -0
  710. package/src/tools/inspect-image-renderer.ts +101 -0
  711. package/src/tools/inspect-image.ts +176 -0
  712. package/src/tools/json-tree.ts +266 -0
  713. package/src/tools/jtd-to-json-schema.ts +199 -0
  714. package/src/tools/jtd-to-typescript.ts +136 -0
  715. package/src/tools/jtd-utils.ts +102 -0
  716. package/src/tools/list-limit.ts +40 -0
  717. package/src/tools/mermaid-renderer.ts +127 -0
  718. package/src/tools/notebook.ts +288 -0
  719. package/src/tools/output-meta.ts +588 -0
  720. package/src/tools/path-utils.ts +560 -0
  721. package/src/tools/plan-mode-guard.ts +41 -0
  722. package/src/tools/poll-tool.ts +177 -0
  723. package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
  724. package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
  725. package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
  726. package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
  727. package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
  728. package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
  729. package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
  730. package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
  731. package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
  732. package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
  733. package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
  734. package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
  735. package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
  736. package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
  737. package/src/tools/python.ts +1184 -0
  738. package/src/tools/read.ts +1591 -0
  739. package/src/tools/render-mermaid.ts +93 -0
  740. package/src/tools/render-utils.ts +677 -0
  741. package/src/tools/renderers.ts +82 -0
  742. package/src/tools/report-tool-issue.ts +80 -0
  743. package/src/tools/resolve.ts +196 -0
  744. package/src/tools/review.ts +238 -0
  745. package/src/tools/search-tool-bm25.ts +288 -0
  746. package/src/tools/sqlite-reader.ts +623 -0
  747. package/src/tools/ssh.ts +322 -0
  748. package/src/tools/submit-result.ts +229 -0
  749. package/src/tools/todo-render.ts +33 -0
  750. package/src/tools/todo-write.ts +440 -0
  751. package/src/tools/tool-errors.ts +62 -0
  752. package/src/tools/tool-result.ts +93 -0
  753. package/src/tools/tool-timeouts.ts +30 -0
  754. package/src/tools/vim.ts +981 -0
  755. package/src/tools/write.ts +671 -0
  756. package/src/tools/xcsh-api-renderer.ts +402 -0
  757. package/src/tools/xcsh-api.ts +887 -0
  758. package/src/tui/code-cell.ts +101 -0
  759. package/src/tui/file-list.ts +47 -0
  760. package/src/tui/index.ts +11 -0
  761. package/src/tui/output-block.ts +175 -0
  762. package/src/tui/status-line.ts +39 -0
  763. package/src/tui/tree-list.ts +84 -0
  764. package/src/tui/types.ts +15 -0
  765. package/src/tui/utils.ts +103 -0
  766. package/src/utils/changelog.ts +98 -0
  767. package/src/utils/clipboard.ts +80 -0
  768. package/src/utils/command-args.ts +76 -0
  769. package/src/utils/commit-message-generator.ts +137 -0
  770. package/src/utils/edit-mode.ts +50 -0
  771. package/src/utils/event-bus.ts +33 -0
  772. package/src/utils/external-editor.ts +65 -0
  773. package/src/utils/file-display-mode.ts +37 -0
  774. package/src/utils/file-mentions.ts +376 -0
  775. package/src/utils/fuzzy.ts +108 -0
  776. package/src/utils/git.ts +1495 -0
  777. package/src/utils/gitstatus.ts +140 -0
  778. package/src/utils/image-convert.ts +27 -0
  779. package/src/utils/image-loading.ts +98 -0
  780. package/src/utils/image-passthrough.ts +118 -0
  781. package/src/utils/image-resize.ts +272 -0
  782. package/src/utils/image-viewer.ts +17 -0
  783. package/src/utils/lang-from-path.ts +239 -0
  784. package/src/utils/markit.ts +81 -0
  785. package/src/utils/open.ts +20 -0
  786. package/src/utils/session-color.ts +55 -0
  787. package/src/utils/shell-snapshot.ts +188 -0
  788. package/src/utils/title-generator.ts +193 -0
  789. package/src/utils/tool-choice.ts +28 -0
  790. package/src/utils/tools-manager.ts +356 -0
  791. package/src/vim/buffer.ts +309 -0
  792. package/src/vim/commands.ts +382 -0
  793. package/src/vim/engine.ts +2426 -0
  794. package/src/vim/parser.ts +151 -0
  795. package/src/vim/render.ts +252 -0
  796. package/src/vim/types.ts +197 -0
  797. package/src/web/kagi.ts +174 -0
  798. package/src/web/parallel.ts +346 -0
  799. package/src/web/scrapers/artifacthub.ts +207 -0
  800. package/src/web/scrapers/arxiv.ts +83 -0
  801. package/src/web/scrapers/aur.ts +162 -0
  802. package/src/web/scrapers/biorxiv.ts +133 -0
  803. package/src/web/scrapers/bluesky.ts +262 -0
  804. package/src/web/scrapers/brew.ts +172 -0
  805. package/src/web/scrapers/cheatsh.ts +68 -0
  806. package/src/web/scrapers/chocolatey.ts +196 -0
  807. package/src/web/scrapers/choosealicense.ts +95 -0
  808. package/src/web/scrapers/cisa-kev.ts +87 -0
  809. package/src/web/scrapers/clojars.ts +154 -0
  810. package/src/web/scrapers/coingecko.ts +177 -0
  811. package/src/web/scrapers/crates-io.ts +110 -0
  812. package/src/web/scrapers/crossref.ts +136 -0
  813. package/src/web/scrapers/devto.ts +147 -0
  814. package/src/web/scrapers/discogs.ts +306 -0
  815. package/src/web/scrapers/discourse.ts +197 -0
  816. package/src/web/scrapers/dockerhub.ts +138 -0
  817. package/src/web/scrapers/docs-rs.ts +653 -0
  818. package/src/web/scrapers/fdroid.ts +134 -0
  819. package/src/web/scrapers/firefox-addons.ts +191 -0
  820. package/src/web/scrapers/flathub.ts +223 -0
  821. package/src/web/scrapers/github-gist.ts +58 -0
  822. package/src/web/scrapers/github.ts +452 -0
  823. package/src/web/scrapers/gitlab.ts +401 -0
  824. package/src/web/scrapers/go-pkg.ts +266 -0
  825. package/src/web/scrapers/hackage.ts +140 -0
  826. package/src/web/scrapers/hackernews.ts +189 -0
  827. package/src/web/scrapers/hex.ts +105 -0
  828. package/src/web/scrapers/huggingface.ts +321 -0
  829. package/src/web/scrapers/iacr.ts +89 -0
  830. package/src/web/scrapers/index.ts +252 -0
  831. package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
  832. package/src/web/scrapers/lemmy.ts +203 -0
  833. package/src/web/scrapers/lobsters.ts +175 -0
  834. package/src/web/scrapers/mastodon.ts +292 -0
  835. package/src/web/scrapers/maven.ts +138 -0
  836. package/src/web/scrapers/mdn.ts +169 -0
  837. package/src/web/scrapers/metacpan.ts +222 -0
  838. package/src/web/scrapers/musicbrainz.ts +250 -0
  839. package/src/web/scrapers/npm.ts +98 -0
  840. package/src/web/scrapers/nuget.ts +183 -0
  841. package/src/web/scrapers/nvd.ts +222 -0
  842. package/src/web/scrapers/ollama.ts +239 -0
  843. package/src/web/scrapers/open-vsx.ts +106 -0
  844. package/src/web/scrapers/opencorporates.ts +292 -0
  845. package/src/web/scrapers/openlibrary.ts +336 -0
  846. package/src/web/scrapers/orcid.ts +286 -0
  847. package/src/web/scrapers/osv.ts +176 -0
  848. package/src/web/scrapers/packagist.ts +160 -0
  849. package/src/web/scrapers/pub-dev.ts +143 -0
  850. package/src/web/scrapers/pubmed.ts +211 -0
  851. package/src/web/scrapers/pypi.ts +112 -0
  852. package/src/web/scrapers/rawg.ts +110 -0
  853. package/src/web/scrapers/readthedocs.ts +120 -0
  854. package/src/web/scrapers/reddit.ts +95 -0
  855. package/src/web/scrapers/repology.ts +251 -0
  856. package/src/web/scrapers/rfc.ts +201 -0
  857. package/src/web/scrapers/rubygems.ts +103 -0
  858. package/src/web/scrapers/searchcode.ts +190 -0
  859. package/src/web/scrapers/sec-edgar.ts +261 -0
  860. package/src/web/scrapers/semantic-scholar.ts +171 -0
  861. package/src/web/scrapers/snapcraft.ts +187 -0
  862. package/src/web/scrapers/sourcegraph.ts +336 -0
  863. package/src/web/scrapers/spdx.ts +108 -0
  864. package/src/web/scrapers/spotify.ts +198 -0
  865. package/src/web/scrapers/stackoverflow.ts +120 -0
  866. package/src/web/scrapers/terraform.ts +277 -0
  867. package/src/web/scrapers/tldr.ts +47 -0
  868. package/src/web/scrapers/twitter.ts +93 -0
  869. package/src/web/scrapers/types.ts +267 -0
  870. package/src/web/scrapers/utils.ts +109 -0
  871. package/src/web/scrapers/vimeo.ts +133 -0
  872. package/src/web/scrapers/vscode-marketplace.ts +187 -0
  873. package/src/web/scrapers/w3c.ts +156 -0
  874. package/src/web/scrapers/wikidata.ts +344 -0
  875. package/src/web/scrapers/wikipedia.ts +84 -0
  876. package/src/web/scrapers/youtube.ts +313 -0
  877. package/src/web/search/errors.ts +120 -0
  878. package/src/web/search/index.ts +346 -0
  879. package/src/web/search/params.ts +129 -0
  880. package/src/web/search/provider.ts +97 -0
  881. package/src/web/search/providers/anthropic.ts +339 -0
  882. package/src/web/search/providers/base.ts +35 -0
  883. package/src/web/search/providers/brave.ts +150 -0
  884. package/src/web/search/providers/codex.ts +402 -0
  885. package/src/web/search/providers/exa.ts +264 -0
  886. package/src/web/search/providers/firecrawl.ts +115 -0
  887. package/src/web/search/providers/gemini.ts +566 -0
  888. package/src/web/search/providers/jina.ts +99 -0
  889. package/src/web/search/providers/kagi.ts +70 -0
  890. package/src/web/search/providers/kimi.ts +157 -0
  891. package/src/web/search/providers/parallel.ts +63 -0
  892. package/src/web/search/providers/perplexity.ts +546 -0
  893. package/src/web/search/providers/synthetic.ts +113 -0
  894. package/src/web/search/providers/tavily.ts +162 -0
  895. package/src/web/search/providers/utils.ts +36 -0
  896. package/src/web/search/providers/zai.ts +311 -0
  897. package/src/web/search/render.ts +348 -0
  898. package/src/web/search/types.ts +438 -0
  899. package/src/web/search/utils.ts +17 -0
@@ -0,0 +1,1366 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import type {
5
+ AgentTool,
6
+ AgentToolContext,
7
+ AgentToolResult,
8
+ AgentToolUpdateCallback,
9
+ } from "@f5xc-salesdemos/pi-agent-core";
10
+ import { StringEnum } from "@f5xc-salesdemos/pi-ai";
11
+ import { getPuppeteerDir, logger, prompt, Snowflake, untilAborted } from "@f5xc-salesdemos/pi-utils";
12
+ import { Readability } from "@mozilla/readability";
13
+ import { type Static, Type } from "@sinclair/typebox";
14
+ import { type HTMLElement, parseHTML } from "linkedom";
15
+ import type {
16
+ Browser,
17
+ CDPSession,
18
+ ElementHandle,
19
+ KeyInput,
20
+ Page,
21
+ default as Puppeteer,
22
+ SerializedAXNode,
23
+ } from "puppeteer";
24
+ import { click, fill, resolve, scrollIntoView, waitFor } from "../browser";
25
+ import { locateChrome } from "../browser/chrome-locate";
26
+ import browserDescription from "../prompts/tools/browser.md" with { type: "text" };
27
+ import type { ToolSession } from "../sdk";
28
+ import { resizeImage } from "../utils/image-resize";
29
+ import { htmlToBasicMarkdown } from "../web/scrapers/types";
30
+ import type { OutputMeta } from "./output-meta";
31
+ import { expandPath, resolveToCwd } from "./path-utils";
32
+ import stealthTamperingScript from "./puppeteer/00_stealth_tampering.txt" with { type: "text" };
33
+ import stealthActivityScript from "./puppeteer/01_stealth_activity.txt" with { type: "text" };
34
+ import stealthHairlineScript from "./puppeteer/02_stealth_hairline.txt" with { type: "text" };
35
+ import stealthBotdScript from "./puppeteer/03_stealth_botd.txt" with { type: "text" };
36
+ import stealthIframeScript from "./puppeteer/04_stealth_iframe.txt" with { type: "text" };
37
+ import stealthWebglScript from "./puppeteer/05_stealth_webgl.txt" with { type: "text" };
38
+ import stealthScreenScript from "./puppeteer/06_stealth_screen.txt" with { type: "text" };
39
+ import stealthFontsScript from "./puppeteer/07_stealth_fonts.txt" with { type: "text" };
40
+ import stealthAudioScript from "./puppeteer/08_stealth_audio.txt" with { type: "text" };
41
+ import stealthLocaleScript from "./puppeteer/09_stealth_locale.txt" with { type: "text" };
42
+ import stealthPluginsScript from "./puppeteer/10_stealth_plugins.txt" with { type: "text" };
43
+ import stealthHardwareScript from "./puppeteer/11_stealth_hardware.txt" with { type: "text" };
44
+ import stealthCodecsScript from "./puppeteer/12_stealth_codecs.txt" with { type: "text" };
45
+ import stealthWorkerScript from "./puppeteer/13_stealth_worker.txt" with { type: "text" };
46
+ import { formatScreenshot } from "./render-utils";
47
+ import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
48
+ import { toolResult } from "./tool-result";
49
+ import { clampTimeout } from "./tool-timeouts";
50
+
51
+ /**
52
+ * Returns the `browser.connectUrl` setting value if it is a non-empty string,
53
+ * otherwise undefined. When set, the browser tool will attach to the
54
+ * already-running Chrome instance instead of launching a new one.
55
+ */
56
+ export function resolveBrowserConnectUrl(settings: { get(key: string): unknown }): string | undefined {
57
+ const v = settings.get("browser.connectUrl");
58
+ return typeof v === "string" && v.trim() ? v.trim() : undefined;
59
+ }
60
+
61
+ /**
62
+ * Asserts that a browser connect URL is a loopback http: URL.
63
+ * Throws with a descriptive message if the URL is invalid, uses a non-http
64
+ * protocol, or points to a non-loopback address.
65
+ *
66
+ * This is intentionally NOT called from `resolveBrowserConnectUrl` (which must
67
+ * stay pure/non-throwing for use in `#closeBrowser`). Call this guard at the
68
+ * connect site before `puppeteer.connect`.
69
+ */
70
+ export function assertLoopbackBrowserUrl(url: string): void {
71
+ let parsed: URL;
72
+ try {
73
+ parsed = new URL(url);
74
+ } catch {
75
+ throw new Error(`browser.connectUrl is not a valid URL: ${url}`);
76
+ }
77
+ if (parsed.protocol !== "http:") throw new Error(`browser.connectUrl must use http: (got ${parsed.protocol})`);
78
+ const host = parsed.hostname
79
+ .toLowerCase()
80
+ .replace(/^\[|\]$/g, "")
81
+ .replace(/\.+$/, "");
82
+ if (host !== "localhost" && host !== "127.0.0.1" && host !== "::1") {
83
+ throw new Error(
84
+ `browser.connectUrl must be a loopback address (localhost, 127.0.0.1, or ::1) — got ${parsed.hostname}`,
85
+ );
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Returns the index of the best page to reuse when co-driving the human's
91
+ * Chrome. Scans backwards for the last non-`about:blank` page (i.e. the
92
+ * active foreground tab); falls back to index 0 when every page is blank.
93
+ */
94
+ export function pickCoDrivePage(pages: { url(): string }[]): number {
95
+ for (let i = pages.length - 1; i >= 0; i--) {
96
+ if (pages[i].url() !== "about:blank") return i;
97
+ }
98
+ return 0;
99
+ }
100
+
101
+ /**
102
+ * Lazy-import puppeteer from a safe CWD so cosmiconfig doesn't choke
103
+ * on malformed package.json files in the user's project tree.
104
+ */
105
+ let puppeteerModule: typeof Puppeteer | undefined;
106
+ async function loadPuppeteer(): Promise<typeof Puppeteer> {
107
+ if (puppeteerModule) return puppeteerModule;
108
+ const prev = process.cwd();
109
+ const safeDir = getPuppeteerDir();
110
+ await Bun.write(path.join(safeDir, "package.json"), "{}");
111
+ try {
112
+ process.chdir(safeDir);
113
+ puppeteerModule = (await import("puppeteer")).default;
114
+ return puppeteerModule;
115
+ } finally {
116
+ process.chdir(prev);
117
+ }
118
+ }
119
+
120
+ const DEFAULT_VIEWPORT = { width: 1365, height: 768, deviceScaleFactor: 1.25 };
121
+ const STEALTH_IGNORE_DEFAULT_ARGS = [
122
+ "--disable-extensions",
123
+ "--disable-default-apps",
124
+ "--disable-component-extensions-with-background-pages",
125
+ ];
126
+ const STEALTH_ACCEPT_LANGUAGE = "en-US,en";
127
+ const PUPPETEER_SOURCE_URL_SUFFIX = "//# sourceURL=__puppeteer_evaluation_script__";
128
+ const INTERACTIVE_AX_ROLES = new Set([
129
+ "button",
130
+ "link",
131
+ "textbox",
132
+ "combobox",
133
+ "listbox",
134
+ "option",
135
+ "checkbox",
136
+ "radio",
137
+ "switch",
138
+ "tab",
139
+ "menuitem",
140
+ "menuitemcheckbox",
141
+ "menuitemradio",
142
+ "slider",
143
+ "spinbutton",
144
+ "searchbox",
145
+ "treeitem",
146
+ ]);
147
+
148
+ /**
149
+ * Stealth init scripts for Puppeteer.
150
+ */
151
+
152
+ type PuppeteerCdpClient = {
153
+ send: (method: string, params?: Record<string, unknown>) => Promise<unknown>;
154
+ };
155
+
156
+ type UserAgentOverride = {
157
+ userAgent: string;
158
+ platform: string;
159
+ acceptLanguage: string;
160
+ userAgentMetadata: {
161
+ brands: Array<{ brand: string; version: string }>;
162
+ fullVersion: string;
163
+ platform: string;
164
+ platformVersion: string;
165
+ architecture: string;
166
+ model: string;
167
+ mobile: boolean;
168
+ };
169
+ };
170
+
171
+ function resolvePageClient(page: Page): PuppeteerCdpClient | null {
172
+ const pageWithClient = page as Page & {
173
+ _client?: (() => PuppeteerCdpClient) | PuppeteerCdpClient;
174
+ };
175
+ if (!pageWithClient._client) return null;
176
+ return typeof pageWithClient._client === "function" ? pageWithClient._client() : pageWithClient._client;
177
+ }
178
+
179
+ const puppeteerGetArgsSchema = Type.Array(
180
+ Type.Object({
181
+ selector: Type.String({
182
+ description:
183
+ "Selector for the target element (CSS, or puppeteer query handler like aria/, text/, xpath/, pierce/; also accepts legacy p- prefixes)",
184
+ }),
185
+ attribute: Type.Optional(Type.String({ description: "Attribute name (get_attribute)" })),
186
+ }),
187
+ { description: "Batch arguments for get_* actions", minItems: 1 },
188
+ );
189
+
190
+ const browserSchema = Type.Object({
191
+ action: StringEnum(
192
+ [
193
+ "open",
194
+ "goto",
195
+ "observe",
196
+ "click",
197
+ "click_id",
198
+ "type",
199
+ "type_id",
200
+ "fill",
201
+ "fill_id",
202
+ "press",
203
+ "scroll",
204
+ "drag",
205
+ "wait_for_selector",
206
+ "evaluate",
207
+ "get_text",
208
+ "get_html",
209
+ "get_attribute",
210
+ "extract_readable",
211
+ "screenshot",
212
+ "close",
213
+ ],
214
+ { description: "Action to perform" },
215
+ ),
216
+ url: Type.Optional(Type.String({ description: "URL to navigate to (goto)" })),
217
+ selector: Type.Optional(
218
+ Type.String({
219
+ description:
220
+ "Selector for the target element (CSS, or puppeteer query handler like aria/, text/, xpath/, pierce/; also accepts legacy p- prefixes)",
221
+ }),
222
+ ),
223
+ element_id: Type.Optional(Type.Number({ description: "Element ID from observe" })),
224
+ include_all: Type.Optional(Type.Boolean({ description: "Include non-interactive nodes in observe" })),
225
+ viewport_only: Type.Optional(Type.Boolean({ description: "Limit observe output to elements in the viewport" })),
226
+ args: Type.Optional(puppeteerGetArgsSchema),
227
+ script: Type.Optional(Type.String({ description: "JavaScript to evaluate (evaluate)" })),
228
+ text: Type.Optional(Type.String({ description: "Text to type (type)" })),
229
+ value: Type.Optional(Type.String({ description: "Value to set (fill)" })),
230
+ attribute: Type.Optional(Type.String({ description: "Attribute name to read (get_attribute)" })),
231
+ key: Type.Optional(Type.String({ description: "Keyboard key to press (press)" })),
232
+ timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 30)" })),
233
+ wait_until: Type.Optional(
234
+ StringEnum(["load", "domcontentloaded", "networkidle0", "networkidle2"], {
235
+ description: "Navigation wait condition (goto)",
236
+ }),
237
+ ),
238
+ full_page: Type.Optional(Type.Boolean({ description: "Capture full page screenshot (screenshot)" })),
239
+ format: Type.Optional(
240
+ StringEnum(["text", "markdown"], {
241
+ description: "Output format for extract_readable (text/markdown)",
242
+ }),
243
+ ),
244
+ path: Type.Optional(Type.String({ description: "Optional path to save screenshot (relative to cwd)" })),
245
+ viewport: Type.Optional(
246
+ Type.Object({
247
+ width: Type.Number({ description: "Viewport width in pixels" }),
248
+ height: Type.Number({ description: "Viewport height in pixels" }),
249
+ device_scale_factor: Type.Optional(Type.Number({ description: "Device scale factor" })),
250
+ }),
251
+ ),
252
+ delta_x: Type.Optional(Type.Number({ description: "Scroll delta X (scroll)" })),
253
+ delta_y: Type.Optional(Type.Number({ description: "Scroll delta Y (scroll)" })),
254
+ from_selector: Type.Optional(
255
+ Type.String({
256
+ description:
257
+ "Drag start selector (CSS, or puppeteer query handler like aria/, text/, xpath/, pierce/; also accepts legacy p- prefixes)",
258
+ }),
259
+ ),
260
+ to_selector: Type.Optional(
261
+ Type.String({
262
+ description:
263
+ "Drag end selector (CSS, or puppeteer query handler like aria/, text/, xpath/, pierce/; also accepts legacy p- prefixes)",
264
+ }),
265
+ ),
266
+ });
267
+
268
+ /** Input schema for the Puppeteer tool. */
269
+ export type BrowserParams = Static<typeof browserSchema>;
270
+
271
+ /** Details describing a Puppeteer tool execution result. */
272
+ export interface BrowserToolDetails {
273
+ action: BrowserParams["action"];
274
+ url?: string;
275
+ selector?: string;
276
+ elementId?: number;
277
+ result?: string | string[];
278
+ screenshotPath?: string;
279
+ mimeType?: string;
280
+ bytes?: number;
281
+ viewport?: { width: number; height: number; deviceScaleFactor?: number };
282
+ observation?: Observation;
283
+ readable?: ReadableResult;
284
+ meta?: OutputMeta;
285
+ }
286
+
287
+ export interface ObservationEntry {
288
+ id: number;
289
+ role: string;
290
+ name?: string;
291
+ value?: string | number;
292
+ description?: string;
293
+ keyshortcuts?: string;
294
+ states: string[];
295
+ }
296
+
297
+ export interface Observation {
298
+ url: string;
299
+ title?: string;
300
+ viewport: { width: number; height: number; deviceScaleFactor?: number };
301
+ scroll: {
302
+ x: number;
303
+ y: number;
304
+ width: number;
305
+ height: number;
306
+ scrollWidth: number;
307
+ scrollHeight: number;
308
+ };
309
+ elements: ObservationEntry[];
310
+ }
311
+
312
+ export interface ReadableResult {
313
+ url: string;
314
+ title?: string;
315
+ byline?: string;
316
+ excerpt?: string;
317
+ contentLength: number;
318
+ text?: string;
319
+ markdown?: string;
320
+ }
321
+
322
+ type ReadableFormat = "text" | "markdown";
323
+
324
+ /** Trim to non-empty string or undefined. */
325
+ function normalize(text: string | null | undefined): string | undefined {
326
+ const trimmed = text?.trim();
327
+ return trimmed || undefined;
328
+ }
329
+
330
+ /**
331
+ * Extract readable content from raw HTML.
332
+ * Tries Readability (article-isolation scoring) first, then falls back to a
333
+ * CSS selector chain over the same pre-parsed DOM. Returns null if neither
334
+ * path yields usable content.
335
+ */
336
+ export function extractReadableFromHtml(html: string, url: string, format: ReadableFormat): ReadableResult | null {
337
+ const { document } = parseHTML(html);
338
+
339
+ // --- Primary: Readability article extraction ---
340
+ const article = new Readability(document).parse();
341
+ if (article) {
342
+ const result = toReadableResult(url, format, article.textContent, article.content, {
343
+ title: article.title,
344
+ byline: article.byline,
345
+ excerpt: article.excerpt,
346
+ length: article.length,
347
+ });
348
+ if (result) return result;
349
+ }
350
+
351
+ // --- Fallback: CSS selector chain ---
352
+ const candidates = [
353
+ document.querySelector("[data-pagefind-body]"),
354
+ document.querySelector("main article"),
355
+ document.querySelector("article"),
356
+ document.querySelector("main"),
357
+ document.querySelector("[role='main']"),
358
+ document.body,
359
+ ];
360
+ for (const el of candidates) {
361
+ if (!el) continue;
362
+ const innerHTML = el.innerHTML?.trim();
363
+ const textContent = el.textContent?.trim();
364
+ if (!innerHTML || !textContent) continue;
365
+ const result = toReadableResult(url, format, textContent, innerHTML, {
366
+ title: document.title,
367
+ excerpt: textContent.slice(0, 240),
368
+ length: textContent.length,
369
+ });
370
+ if (result) return result;
371
+ }
372
+
373
+ return null;
374
+ }
375
+
376
+ /** Shared builder for both extraction paths. */
377
+ function toReadableResult(
378
+ url: string,
379
+ format: ReadableFormat,
380
+ textContent: string | null | undefined,
381
+ htmlContent: string | null | undefined,
382
+ meta: { title?: string | null; byline?: string | null; excerpt?: string | null; length?: number | null },
383
+ ): ReadableResult | null {
384
+ const text = normalize(textContent);
385
+ const markdown = format === "markdown" ? (normalize(htmlToBasicMarkdown(htmlContent ?? "")) ?? text) : undefined;
386
+ const normalizedText = format === "text" ? text : undefined;
387
+ if (!normalizedText && !markdown) return null;
388
+ return {
389
+ url,
390
+ title: normalize(meta.title),
391
+ byline: normalize(meta.byline),
392
+ excerpt: normalize(meta.excerpt),
393
+ contentLength: meta.length ?? text?.length ?? markdown?.length ?? 0,
394
+ text: normalizedText,
395
+ markdown,
396
+ };
397
+ }
398
+
399
+ function ensureParam<T>(value: T | undefined, name: string, action: string): T {
400
+ if (value === undefined || value === null || value === "") {
401
+ throw new ToolError(`Missing required parameter '${name}' for action '${action}'.`);
402
+ }
403
+ return value;
404
+ }
405
+
406
+ function formatEvaluateResult(value: unknown): string {
407
+ if (typeof value === "string") return value;
408
+ if (value === undefined) return "undefined";
409
+ try {
410
+ const serialized = JSON.stringify(value, null, 2);
411
+ return serialized ?? "undefined";
412
+ } catch {
413
+ return String(value);
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Puppeteer tool for headless browser automation.
419
+ */
420
+ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolDetails> {
421
+ readonly name = "puppeteer";
422
+ readonly label = "Puppeteer";
423
+ readonly description: string;
424
+ readonly parameters = browserSchema;
425
+ readonly strict = true;
426
+ #browser: Browser | null = null;
427
+ #page: Page | null = null;
428
+ #currentHeadless: boolean | null = null;
429
+ #browserSession: CDPSession | null = null;
430
+ #userAgentOverride: UserAgentOverride | null = null;
431
+ #elementIdCounter = 0;
432
+ readonly #elementCache = new Map<number, ElementHandle>();
433
+ readonly #patchedClients = new WeakSet<object>();
434
+
435
+ constructor(private readonly session: ToolSession) {
436
+ this.description = prompt.render(browserDescription, {});
437
+ }
438
+
439
+ async #closeBrowser(): Promise<void> {
440
+ await this.#clearElementCache();
441
+ const connectUrl = resolveBrowserConnectUrl(this.session.settings);
442
+ if (connectUrl) {
443
+ // Attached mode: detach the CDP connection without touching the human's
444
+ // Chrome instance or their active tab.
445
+ if (this.#browser?.connected) {
446
+ this.#browser.disconnect();
447
+ }
448
+ } else {
449
+ // Launched mode: we own the browser process — close the page then the browser.
450
+ if (this.#page && !this.#page.isClosed()) {
451
+ await this.#page.close();
452
+ }
453
+ if (this.#browser?.connected) {
454
+ await this.#browser.close();
455
+ }
456
+ }
457
+ this.#page = null;
458
+ this.#browser = null;
459
+ this.#browserSession = null;
460
+ this.#userAgentOverride = null;
461
+ }
462
+
463
+ async #resetBrowser(params?: BrowserParams): Promise<Page> {
464
+ await this.#closeBrowser();
465
+ this.#currentHeadless = this.session.settings.get("browser.headless");
466
+ const vp = params?.viewport;
467
+ const initialViewport = vp
468
+ ? {
469
+ width: vp.width,
470
+ height: vp.height,
471
+ deviceScaleFactor: vp.device_scale_factor ?? DEFAULT_VIEWPORT.deviceScaleFactor,
472
+ }
473
+ : DEFAULT_VIEWPORT;
474
+ const puppeteer = await loadPuppeteer();
475
+ const connectUrl = resolveBrowserConnectUrl(this.session.settings);
476
+ if (connectUrl) {
477
+ assertLoopbackBrowserUrl(connectUrl);
478
+ try {
479
+ this.#browser = await puppeteer.connect({ browserURL: connectUrl });
480
+ } catch (err) {
481
+ const msg = err instanceof Error ? err.message : String(err);
482
+ throw new Error(
483
+ `Could not attach to Chrome at ${connectUrl}: ${msg}. Start Chrome with --remote-debugging-port=9222 and log into your tenant, then retry.`,
484
+ );
485
+ }
486
+ } else {
487
+ const launchArgs = [
488
+ "--no-sandbox",
489
+ "--disable-setuid-sandbox",
490
+ "--disable-blink-features=AutomationControlled",
491
+ `--window-size=${initialViewport.width},${initialViewport.height}`,
492
+ ];
493
+ const proxy = process.env.PUPPETEER_PROXY;
494
+ if (proxy) {
495
+ launchArgs.push(`--proxy-server=${proxy}`);
496
+ // Chrome (since v72) bypasses proxies for localhost by default. When PUPPETEER_PROXY_BYPASS_LOOPBACK
497
+ // is true, add <-loopback> so traffic to localhost reaches the proxy (e.g. for mitmdump/auth capture).
498
+ const bypassLoopback = process.env.PUPPETEER_PROXY_BYPASS_LOOPBACK?.toLowerCase();
499
+ if (
500
+ bypassLoopback === "true" ||
501
+ bypassLoopback === "1" ||
502
+ bypassLoopback === "yes" ||
503
+ bypassLoopback === "on"
504
+ ) {
505
+ launchArgs.push("--proxy-bypass-list=<-loopback>");
506
+ }
507
+ }
508
+ const ignoreCert = process.env.PUPPETEER_PROXY_IGNORE_CERT_ERRORS?.toLowerCase();
509
+ if (ignoreCert === "true" || ignoreCert === "1" || ignoreCert === "yes" || ignoreCert === "on") {
510
+ launchArgs.push("--ignore-certificate-errors");
511
+ }
512
+ this.#browser = await puppeteer.launch({
513
+ headless: this.#currentHeadless,
514
+ defaultViewport: this.#currentHeadless ? initialViewport : null,
515
+ executablePath: locateChrome()?.path,
516
+ args: launchArgs,
517
+ ignoreDefaultArgs: [...STEALTH_IGNORE_DEFAULT_ARGS],
518
+ });
519
+ }
520
+ if (connectUrl) {
521
+ // Co-drive mode: reuse the human's existing foreground tab.
522
+ const pages = await this.#browser.pages();
523
+ this.#page = pages.length ? pages[pickCoDrivePage(pages)] : await this.#browser.newPage();
524
+ } else {
525
+ this.#page = await this.#browser.newPage();
526
+ }
527
+ await this.#applyStealthPatches(this.#page);
528
+ if (this.#currentHeadless || params?.viewport) {
529
+ await this.#applyViewport(this.#page, params?.viewport);
530
+ }
531
+ return this.#page;
532
+ }
533
+
534
+ async #ensurePage(params?: BrowserParams): Promise<Page> {
535
+ const desiredHeadless = this.session.settings.get("browser.headless");
536
+ if (this.#currentHeadless !== null && this.#currentHeadless !== desiredHeadless) {
537
+ return this.#resetBrowser(params);
538
+ }
539
+ if (this.#page && !this.#page.isClosed()) {
540
+ return this.#page;
541
+ }
542
+ if (!this.#browser?.isConnected()) {
543
+ return this.#resetBrowser(params);
544
+ }
545
+ // co-drive: reuse the human's current tab when attached, else a fresh page
546
+ const connectUrl = resolveBrowserConnectUrl(this.session.settings);
547
+ if (connectUrl) {
548
+ const pages = await this.#browser.pages();
549
+ this.#page = pages.length ? pages[pickCoDrivePage(pages)] : await this.#browser.newPage();
550
+ } else {
551
+ this.#page = await this.#browser.newPage();
552
+ }
553
+ await this.#applyStealthPatches(this.#page);
554
+ if (this.#currentHeadless || params?.viewport) {
555
+ await this.#applyViewport(this.#page, params?.viewport);
556
+ }
557
+ return this.#page;
558
+ }
559
+
560
+ async #applyViewport(page: Page, viewport?: BrowserParams["viewport"]): Promise<void> {
561
+ if (!viewport) {
562
+ await page.setViewport(DEFAULT_VIEWPORT);
563
+ return;
564
+ }
565
+ await page.setViewport({
566
+ width: viewport.width,
567
+ height: viewport.height,
568
+ deviceScaleFactor: viewport.device_scale_factor ?? DEFAULT_VIEWPORT.deviceScaleFactor,
569
+ });
570
+ }
571
+
572
+ async #clearElementCache(): Promise<void> {
573
+ if (this.#elementCache.size === 0) {
574
+ this.#elementIdCounter = 0;
575
+ return;
576
+ }
577
+ const handles = Array.from(this.#elementCache.values());
578
+ this.#elementCache.clear();
579
+ this.#elementIdCounter = 0;
580
+ await Promise.all(
581
+ handles.map(async handle => {
582
+ try {
583
+ await handle.dispose();
584
+ } catch {
585
+ return;
586
+ }
587
+ }),
588
+ );
589
+ }
590
+
591
+ async #resolveCachedHandle(id: number): Promise<ElementHandle> {
592
+ const handle = this.#elementCache.get(id);
593
+ if (!handle) {
594
+ throw new ToolError(`Unknown element_id ${id}. Run observe to refresh the element list.`);
595
+ }
596
+ try {
597
+ const isConnected = (await handle.evaluate(el => el.isConnected)) as boolean;
598
+ if (!isConnected) {
599
+ await this.#clearElementCache();
600
+ throw new ToolError(`Element_id ${id} is stale. Run observe again.`);
601
+ }
602
+ } catch {
603
+ await this.#clearElementCache();
604
+ throw new ToolError(`Element_id ${id} is stale. Run observe again.`);
605
+ }
606
+ return handle;
607
+ }
608
+
609
+ #isInteractiveNode(node: SerializedAXNode): boolean {
610
+ if (INTERACTIVE_AX_ROLES.has(node.role)) return true;
611
+ return (
612
+ node.checked !== undefined ||
613
+ node.pressed !== undefined ||
614
+ node.selected !== undefined ||
615
+ node.expanded !== undefined ||
616
+ node.focused === true
617
+ );
618
+ }
619
+
620
+ async #collectObservationEntries(
621
+ node: SerializedAXNode,
622
+ entries: ObservationEntry[],
623
+ options: { viewportOnly: boolean; includeAll: boolean },
624
+ ): Promise<void> {
625
+ if (options.includeAll || this.#isInteractiveNode(node)) {
626
+ const handle = await node.elementHandle();
627
+ if (handle) {
628
+ let inViewport = true;
629
+ if (options.viewportOnly) {
630
+ try {
631
+ inViewport = await handle.isIntersectingViewport();
632
+ } catch {
633
+ inViewport = false;
634
+ }
635
+ }
636
+ if (inViewport) {
637
+ const id = ++this.#elementIdCounter;
638
+ const states: string[] = [];
639
+ if (node.disabled) states.push("disabled");
640
+ if (node.checked !== undefined) states.push(`checked=${String(node.checked)}`);
641
+ if (node.pressed !== undefined) states.push(`pressed=${String(node.pressed)}`);
642
+ if (node.selected !== undefined) states.push(`selected=${String(node.selected)}`);
643
+ if (node.expanded !== undefined) states.push(`expanded=${String(node.expanded)}`);
644
+ if (node.required) states.push("required");
645
+ if (node.readonly) states.push("readonly");
646
+ if (node.multiselectable) states.push("multiselectable");
647
+ if (node.multiline) states.push("multiline");
648
+ if (node.modal) states.push("modal");
649
+ if (node.focused) states.push("focused");
650
+ this.#elementCache.set(id, handle);
651
+ entries.push({
652
+ id,
653
+ role: node.role,
654
+ name: node.name,
655
+ value: node.value,
656
+ description: node.description,
657
+ keyshortcuts: node.keyshortcuts,
658
+ states,
659
+ });
660
+ } else {
661
+ await handle.dispose();
662
+ }
663
+ }
664
+ }
665
+ for (const child of node.children ?? []) {
666
+ await this.#collectObservationEntries(child, entries, options);
667
+ }
668
+ }
669
+
670
+ #formatObservation(observation: Observation): string {
671
+ const viewport = `${observation.viewport.width}x${observation.viewport.height}`;
672
+ const scroll = `x=${observation.scroll.x} y=${observation.scroll.y} viewport=${observation.scroll.width}x${observation.scroll.height} doc=${observation.scroll.scrollWidth}x${observation.scroll.scrollHeight}`;
673
+ const lines = [
674
+ `URL: ${observation.url}`,
675
+ observation.title ? `Title: ${observation.title}` : "Title:",
676
+ `Viewport: ${viewport}`,
677
+ `Scroll: ${scroll}`,
678
+ "Elements:",
679
+ ];
680
+ for (const entry of observation.elements) {
681
+ const name = entry.name ? ` "${entry.name}"` : "";
682
+ const value = entry.value !== undefined ? ` value=${JSON.stringify(entry.value)}` : "";
683
+ const description = entry.description ? ` desc=${JSON.stringify(entry.description)}` : "";
684
+ const shortcuts = entry.keyshortcuts ? ` shortcuts=${JSON.stringify(entry.keyshortcuts)}` : "";
685
+ const state = entry.states.length ? ` (${entry.states.join(", ")})` : "";
686
+ lines.push(`${entry.id}. ${entry.role}${name}${value}${description}${shortcuts}${state}`);
687
+ }
688
+ return lines.join("\n");
689
+ }
690
+
691
+ /**
692
+ * Restart the browser to apply changes like headless mode.
693
+ */
694
+ async restartForModeChange(): Promise<void> {
695
+ await this.#resetBrowser();
696
+ }
697
+
698
+ async #applyStealthPatches(page: Page): Promise<void> {
699
+ this.#patchSourceUrl(page);
700
+ await this.#applyUserAgentOverride(page);
701
+ await this.#injectStealthScripts(page);
702
+ }
703
+
704
+ async #applyUserAgentOverride(page: Page): Promise<void> {
705
+ const client = resolvePageClient(page);
706
+ if (!client) return;
707
+ const override = await this.#resolveUserAgentOverride(page);
708
+ await this.#sendUserAgentOverride(client, override);
709
+ await this.#configureUserAgentTargets(override);
710
+ }
711
+
712
+ async #resolveUserAgentOverride(page: Page): Promise<UserAgentOverride> {
713
+ if (this.#userAgentOverride) return this.#userAgentOverride;
714
+ const rawUserAgent = await page.browser().userAgent();
715
+ let userAgent = rawUserAgent.replace("HeadlessChrome/", "Chrome/");
716
+ if (userAgent.includes("Linux") && !userAgent.includes("Android")) {
717
+ userAgent = userAgent.replace(/\(([^)]+)\)/, "(Windows NT 10.0; Win64; x64)");
718
+ }
719
+
720
+ const uaVersionMatch = userAgent.match(/Chrome\/([\d|.]+)/);
721
+ const fallbackVersionMatch = uaVersionMatch ?? (await page.browser().version()).match(/\/([\d|.]+)/);
722
+ const uaVersion = fallbackVersionMatch?.[1] ?? "0";
723
+ const majorVersion = Number.parseInt(uaVersion.split(".")[0] ?? "0", 10) || 0;
724
+ const isAndroid = userAgent.includes("Android");
725
+ const platform = userAgent.includes("Mac OS X")
726
+ ? "MacIntel"
727
+ : isAndroid
728
+ ? "Android"
729
+ : userAgent.includes("Linux")
730
+ ? "Linux"
731
+ : "Win32";
732
+ const platformFull = userAgent.includes("Mac OS X")
733
+ ? "Mac OS X"
734
+ : isAndroid
735
+ ? "Android"
736
+ : userAgent.includes("Linux")
737
+ ? "Linux"
738
+ : "Windows";
739
+ const platformVersion = userAgent.includes("Mac OS X ")
740
+ ? (userAgent.match(/Mac OS X ([^)]+)/)?.[1] ?? "")
741
+ : userAgent.includes("Android ")
742
+ ? (userAgent.match(/Android ([^;]+)/)?.[1] ?? "")
743
+ : userAgent.includes("Windows ")
744
+ ? (userAgent.match(/Windows .*?([\d|.]+);?/)?.[1] ?? "")
745
+ : "";
746
+ const architecture = isAndroid ? "" : "x86";
747
+ const model = isAndroid ? (userAgent.match(/Android.*?;\s([^)]+)/)?.[1] ?? "") : "";
748
+
749
+ const brandOrders = [
750
+ [0, 1, 2],
751
+ [0, 2, 1],
752
+ [1, 0, 2],
753
+ [1, 2, 0],
754
+ [2, 0, 1],
755
+ [2, 1, 0],
756
+ ];
757
+ const order = brandOrders[majorVersion % brandOrders.length] ?? brandOrders[0];
758
+ const escapedChars = [" ", " ", ";"];
759
+ const greaseyBrand = `${escapedChars[order[0]]}Not${escapedChars[order[1]]}A${escapedChars[order[2]]}Brand`;
760
+ const brands: { brand: string; version: string }[] = [];
761
+ brands[order[0]] = { brand: greaseyBrand, version: "99" };
762
+ brands[order[1]] = { brand: "Chromium", version: String(majorVersion) };
763
+ brands[order[2]] = { brand: "Google Chrome", version: String(majorVersion) };
764
+
765
+ this.#userAgentOverride = {
766
+ userAgent,
767
+ platform,
768
+ acceptLanguage: STEALTH_ACCEPT_LANGUAGE,
769
+ userAgentMetadata: {
770
+ brands,
771
+ fullVersion: uaVersion,
772
+ platform: platformFull,
773
+ platformVersion,
774
+ architecture,
775
+ model,
776
+ mobile: isAndroid,
777
+ },
778
+ };
779
+ return this.#userAgentOverride;
780
+ }
781
+
782
+ async #configureUserAgentTargets(override: UserAgentOverride): Promise<void> {
783
+ if (!this.#browser) return;
784
+ if (!this.#browserSession) {
785
+ this.#browserSession = await this.#browser.target().createCDPSession();
786
+ await this.#browserSession.send("Target.setAutoAttach", {
787
+ autoAttach: true,
788
+ waitForDebuggerOnStart: false,
789
+ flatten: true,
790
+ });
791
+ this.#browserSession.on("Target.attachedToTarget", async (event: { sessionId: string }) => {
792
+ const connection = this.#browserSession?.connection();
793
+ const session = connection?.session(event.sessionId);
794
+ if (!session || !this.#userAgentOverride) return;
795
+ await this.#sendUserAgentOverride(this.#wrapSession(session), this.#userAgentOverride);
796
+ });
797
+ }
798
+
799
+ const targets = this.#browser.targets();
800
+ await Promise.all(
801
+ targets.map(async target => {
802
+ const session = await target.createCDPSession();
803
+ await this.#sendUserAgentOverride(this.#wrapSession(session), override);
804
+ }),
805
+ );
806
+ }
807
+
808
+ #wrapSession(session: CDPSession): PuppeteerCdpClient {
809
+ return {
810
+ send: async (method, params) => session.send(method as never, params as never),
811
+ };
812
+ }
813
+
814
+ async #sendUserAgentOverride(client: PuppeteerCdpClient, override: UserAgentOverride): Promise<void> {
815
+ try {
816
+ await client.send("Network.enable");
817
+ } catch {}
818
+ try {
819
+ await client.send("Network.setUserAgentOverride", override);
820
+ } catch (error) {
821
+ logger.debug("Failed to apply Network user agent override", {
822
+ error: error instanceof Error ? error.message : String(error),
823
+ });
824
+ }
825
+ try {
826
+ await client.send("Emulation.setUserAgentOverride", override);
827
+ } catch (error) {
828
+ logger.debug("Failed to apply Emulation user agent override", {
829
+ error: error instanceof Error ? error.message : String(error),
830
+ });
831
+ }
832
+ }
833
+
834
+ #patchSourceUrl(page: Page): void {
835
+ const client = resolvePageClient(page);
836
+ if (!client) return;
837
+ const clientKey = client as object;
838
+ if (this.#patchedClients.has(clientKey)) return;
839
+ this.#patchedClients.add(clientKey);
840
+ const originalSend = client.send.bind(client);
841
+ client.send = async (method: string, params?: Record<string, unknown>) => {
842
+ const next = async (payload?: Record<string, unknown>) => {
843
+ try {
844
+ return await originalSend(method, payload);
845
+ } catch (error) {
846
+ if (
847
+ error instanceof Error &&
848
+ error.message.includes(
849
+ "Protocol error (Network.getResponseBody): No resource with given identifier found",
850
+ )
851
+ ) {
852
+ return undefined;
853
+ }
854
+ throw error;
855
+ }
856
+ };
857
+ if (!method || !params) {
858
+ return next(params);
859
+ }
860
+ const key =
861
+ method === "Runtime.evaluate"
862
+ ? "expression"
863
+ : method === "Runtime.callFunctionOn"
864
+ ? "functionDeclaration"
865
+ : null;
866
+ if (!key) {
867
+ return next(params);
868
+ }
869
+ const value = params[key];
870
+ if (typeof value !== "string" || !value.includes(PUPPETEER_SOURCE_URL_SUFFIX)) {
871
+ return next(params);
872
+ }
873
+ const patchedParams = { ...params, [key]: value.replace(PUPPETEER_SOURCE_URL_SUFFIX, "") };
874
+ return next(patchedParams);
875
+ };
876
+ }
877
+
878
+ /** Injects stealth scripts that cover common puppeteer detection surfaces. */
879
+ async #injectStealthScripts(page: Page): Promise<void> {
880
+ const scripts = [
881
+ stealthTamperingScript,
882
+ stealthActivityScript,
883
+ stealthHairlineScript,
884
+ stealthBotdScript,
885
+ stealthIframeScript,
886
+ stealthWebglScript,
887
+ stealthScreenScript,
888
+ stealthFontsScript,
889
+ stealthAudioScript,
890
+ stealthLocaleScript,
891
+ stealthPluginsScript,
892
+ stealthHardwareScript,
893
+ stealthCodecsScript,
894
+ stealthWorkerScript,
895
+ ];
896
+
897
+ const joint = scripts
898
+ .map(
899
+ script => `
900
+ try {
901
+ ${script};
902
+ } catch (e) {}
903
+ `,
904
+ )
905
+ .join(";\n");
906
+
907
+ await page.evaluateOnNewDocument(`(() => {
908
+ // Native function cache - captured before any tampering
909
+ const iframe = document.createElement("iframe");
910
+ iframe.style.display = "none";
911
+ document.head.appendChild(iframe);
912
+ const nativeWindow = iframe.contentWindow;
913
+ if (!nativeWindow) return;
914
+
915
+ // Cache pristine native functions
916
+ const Function_toString = nativeWindow.Function.prototype.toString;
917
+ const Object_getOwnPropertyDescriptor = nativeWindow.Object.getOwnPropertyDescriptor;
918
+ const Object_getOwnPropertyDescriptors = nativeWindow.Object.getOwnPropertyDescriptors;
919
+ const Object_getPrototypeOf = nativeWindow.Object.getPrototypeOf;
920
+ const Object_defineProperty = nativeWindow.Object.defineProperty;
921
+ const Object_getOwnPropertyDescriptorOriginal = nativeWindow.Object.getOwnPropertyDescriptor;
922
+ const Object_create = nativeWindow.Object.create;
923
+ const Object_keys = nativeWindow.Object.keys;
924
+ const Object_getOwnPropertyNames = nativeWindow.Object.getOwnPropertyNames;
925
+ const Object_entries = nativeWindow.Object.entries;
926
+ const Object_setPrototypeOf = nativeWindow.Object.setPrototypeOf;
927
+ const Object_assign = nativeWindow.Object.assign;
928
+ const Window_setTimeout = nativeWindow.setTimeout;
929
+ const Math_random = nativeWindow.Math.random;
930
+ const Math_floor = nativeWindow.Math.floor;
931
+ const Math_max = nativeWindow.Math.max;
932
+ const Math_min = nativeWindow.Math.min;
933
+ const Window_Event = nativeWindow.Event;
934
+ const Promise_resolve = nativeWindow.Promise.resolve.bind(nativeWindow.Promise);
935
+ const Window_Blob = nativeWindow.Blob;
936
+ const Window_Proxy = nativeWindow.Proxy;
937
+ const Intl_DateTimeFormat = nativeWindow.Intl.DateTimeFormat;
938
+ const Date_constructor = nativeWindow.Date;
939
+
940
+
941
+ ${joint}
942
+
943
+ document.head.removeChild(iframe);})();`);
944
+ }
945
+
946
+ async execute(
947
+ _toolCallId: string,
948
+ params: BrowserParams,
949
+ signal?: AbortSignal,
950
+ _onUpdate?: AgentToolUpdateCallback<BrowserToolDetails>,
951
+ _ctx?: AgentToolContext,
952
+ ): Promise<AgentToolResult<BrowserToolDetails>> {
953
+ try {
954
+ throwIfAborted(signal);
955
+ const timeoutSeconds = clampTimeout("browser", params.timeout);
956
+ const timeoutMs = timeoutSeconds * 1000;
957
+ const details: BrowserToolDetails = { action: params.action };
958
+
959
+ switch (params.action) {
960
+ case "open": {
961
+ const page = await untilAborted(signal, () => this.#resetBrowser(params));
962
+ const viewport = page.viewport();
963
+ details.viewport = viewport ?? DEFAULT_VIEWPORT;
964
+ const mode = this.#currentHeadless ? "headless" : "visible";
965
+ return toolResult(details).text(`Opened browser session (${mode})`).done();
966
+ }
967
+ case "close": {
968
+ await untilAborted(signal, () => this.#closeBrowser());
969
+ return toolResult(details).text("Closed browser session").done();
970
+ }
971
+ case "goto": {
972
+ const url = ensureParam(params.url, "url", params.action);
973
+ details.url = url;
974
+ const page = await this.#ensurePage(params);
975
+ const waitUntil = params.wait_until ?? "networkidle2";
976
+ await this.#clearElementCache();
977
+ await untilAborted(signal, () => page.goto(url, { waitUntil, timeout: timeoutMs }));
978
+ const finalUrl = page.url();
979
+ const title = (await untilAborted(signal, () => page.title())) as string;
980
+ details.url = finalUrl;
981
+ details.result = title;
982
+ return toolResult(details)
983
+ .text(`Navigated to ${finalUrl}${title ? `\nTitle: ${title}` : ""}`)
984
+ .done();
985
+ }
986
+ case "observe": {
987
+ const page = await this.#ensurePage(params);
988
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
989
+ const observeSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
990
+ await this.#clearElementCache();
991
+ const snapshot = (await untilAborted(observeSignal, () =>
992
+ page.accessibility.snapshot({ interestingOnly: !(params.include_all ?? false) }),
993
+ )) as SerializedAXNode | null;
994
+ if (!snapshot) {
995
+ throw new ToolError("Accessibility snapshot unavailable");
996
+ }
997
+ const entries: ObservationEntry[] = [];
998
+ await this.#collectObservationEntries(snapshot, entries, {
999
+ viewportOnly: params.viewport_only ?? false,
1000
+ includeAll: params.include_all ?? false,
1001
+ });
1002
+ const scroll = (await untilAborted(observeSignal, () =>
1003
+ page.evaluate(() => {
1004
+ const win = globalThis as unknown as {
1005
+ scrollX: number;
1006
+ scrollY: number;
1007
+ innerWidth: number;
1008
+ innerHeight: number;
1009
+ document: { documentElement: { scrollWidth: number; scrollHeight: number } };
1010
+ };
1011
+ const doc = win.document.documentElement;
1012
+ return {
1013
+ x: win.scrollX,
1014
+ y: win.scrollY,
1015
+ width: win.innerWidth,
1016
+ height: win.innerHeight,
1017
+ scrollWidth: doc.scrollWidth,
1018
+ scrollHeight: doc.scrollHeight,
1019
+ };
1020
+ }),
1021
+ )) as Observation["scroll"];
1022
+ const url = page.url();
1023
+ const title = (await untilAborted(observeSignal, () => page.title())) as string;
1024
+ const viewport = page.viewport() ?? DEFAULT_VIEWPORT;
1025
+ const observation: Observation = {
1026
+ url,
1027
+ title,
1028
+ viewport,
1029
+ scroll,
1030
+ elements: entries,
1031
+ };
1032
+ details.url = url;
1033
+ details.viewport = viewport;
1034
+ details.observation = observation;
1035
+ details.result = `${entries.length} elements`;
1036
+ return toolResult(details).text(this.#formatObservation(observation)).done();
1037
+ }
1038
+ case "click": {
1039
+ const selector = ensureParam(params.selector, "selector", params.action);
1040
+ details.selector = selector;
1041
+ const page = await this.#ensurePage(params);
1042
+ await untilAborted(signal, () => click(page, selector));
1043
+ return toolResult(details).text(`Clicked ${selector}`).done();
1044
+ }
1045
+ case "click_id": {
1046
+ const elementId = ensureParam(params.element_id, "element_id", params.action);
1047
+ details.elementId = elementId;
1048
+ const handle = await this.#resolveCachedHandle(elementId);
1049
+ try {
1050
+ await untilAborted(signal, () => handle.click());
1051
+ } catch {
1052
+ await this.#clearElementCache();
1053
+ throw new ToolError(`Element_id ${elementId} is stale. Run observe again.`);
1054
+ }
1055
+ return toolResult(details).text(`Clicked element ${elementId}`).done();
1056
+ }
1057
+ case "type": {
1058
+ const selector = ensureParam(params.selector, "selector", params.action);
1059
+ const text = ensureParam(params.text, "text", params.action);
1060
+ details.selector = selector;
1061
+ const page = await this.#ensurePage(params);
1062
+ const h = await untilAborted(signal, () => resolve(page, selector));
1063
+ await untilAborted(signal, () => h.focus());
1064
+ await untilAborted(signal, () => page.keyboard.type(text, { delay: 0 }));
1065
+ await h.dispose();
1066
+ return toolResult(details).text(`Typed into ${selector}`).done();
1067
+ }
1068
+ case "type_id": {
1069
+ const elementId = ensureParam(params.element_id, "element_id", params.action);
1070
+ const text = ensureParam(params.text, "text", params.action);
1071
+ details.elementId = elementId;
1072
+ const page = await this.#ensurePage(params);
1073
+ const handle = await this.#resolveCachedHandle(elementId);
1074
+ try {
1075
+ await untilAborted(signal, () => handle.focus());
1076
+ await untilAborted(signal, () => page.keyboard.type(text, { delay: 0 }));
1077
+ } catch {
1078
+ await this.#clearElementCache();
1079
+ throw new ToolError(`Element_id ${elementId} is stale. Run observe again.`);
1080
+ }
1081
+ return toolResult(details).text(`Typed into element ${elementId}`).done();
1082
+ }
1083
+ case "fill": {
1084
+ const selector = ensureParam(params.selector, "selector", params.action);
1085
+ const value = ensureParam(params.value, "value", params.action);
1086
+ details.selector = selector;
1087
+ const page = await this.#ensurePage(params);
1088
+ await untilAborted(signal, () => fill(page, selector, value));
1089
+ return toolResult(details).text(`Filled ${selector}`).done();
1090
+ }
1091
+ case "fill_id": {
1092
+ const elementId = ensureParam(params.element_id, "element_id", params.action);
1093
+ const value = ensureParam(params.value, "value", params.action);
1094
+ details.elementId = elementId;
1095
+ const handle = await this.#resolveCachedHandle(elementId);
1096
+ try {
1097
+ await untilAborted(signal, () =>
1098
+ handle.evaluate((el, inputValue) => {
1099
+ const element = el as { value?: string; dispatchEvent: (event: Event) => boolean };
1100
+ if (!("value" in element)) {
1101
+ throw new Error("Target element is not a form input");
1102
+ }
1103
+ element.value = String(inputValue);
1104
+ element.dispatchEvent(new Event("input", { bubbles: true }));
1105
+ element.dispatchEvent(new Event("change", { bubbles: true }));
1106
+ }, value),
1107
+ );
1108
+ } catch {
1109
+ await this.#clearElementCache();
1110
+ throw new ToolError(`Element_id ${elementId} is stale. Run observe again.`);
1111
+ }
1112
+ return toolResult(details).text(`Filled element ${elementId}`).done();
1113
+ }
1114
+ case "press": {
1115
+ const key = ensureParam(params.key, "key", params.action) as KeyInput;
1116
+ const page = await this.#ensurePage(params);
1117
+ if (params.selector) {
1118
+ const h = await resolve(page, params.selector as string);
1119
+ await untilAborted(signal, () => h.focus());
1120
+ await h.dispose();
1121
+ }
1122
+ await untilAborted(signal, () => page.keyboard.press(key));
1123
+ return toolResult(details).text(`Pressed ${key}`).done();
1124
+ }
1125
+ case "scroll": {
1126
+ const page = await this.#ensurePage(params);
1127
+ if (params.selector) {
1128
+ await untilAborted(signal, () => scrollIntoView(page, params.selector as string));
1129
+ return toolResult(details).text(`Scrolled ${params.selector} into view`).done();
1130
+ }
1131
+ const deltaY = ensureParam(params.delta_y, "delta_y", params.action);
1132
+ const deltaX = params.delta_x ?? 0;
1133
+ await untilAborted(signal, () => page.mouse.wheel({ deltaX, deltaY }));
1134
+ return toolResult(details).text(`Scrolled by ${deltaX}, ${deltaY}`).done();
1135
+ }
1136
+ case "drag": {
1137
+ const fromSelector = ensureParam(params.from_selector, "from_selector", params.action);
1138
+ const toSelector = ensureParam(params.to_selector, "to_selector", params.action);
1139
+ const page = await this.#ensurePage(params);
1140
+ const fromHandle = await untilAborted(signal, () => resolve(page, fromSelector));
1141
+ const toHandle = await untilAborted(signal, () => resolve(page, toSelector));
1142
+ if (!fromHandle || !toHandle) {
1143
+ throw new ToolError("Drag selectors did not resolve to elements");
1144
+ }
1145
+ const fromBox = (await untilAborted(signal, () => fromHandle.boundingBox())) as {
1146
+ x: number;
1147
+ y: number;
1148
+ width: number;
1149
+ height: number;
1150
+ } | null;
1151
+ const toBox = (await untilAborted(signal, () => toHandle.boundingBox())) as {
1152
+ x: number;
1153
+ y: number;
1154
+ width: number;
1155
+ height: number;
1156
+ } | null;
1157
+ await fromHandle.dispose();
1158
+ await toHandle.dispose();
1159
+ if (!fromBox || !toBox) {
1160
+ throw new ToolError("Drag elements are not visible");
1161
+ }
1162
+ const startX = fromBox.x + fromBox.width / 2;
1163
+ const startY = fromBox.y + fromBox.height / 2;
1164
+ const endX = toBox.x + toBox.width / 2;
1165
+ const endY = toBox.y + toBox.height / 2;
1166
+ await untilAborted(signal, () => page.mouse.move(startX, startY));
1167
+ await untilAborted(signal, () => page.mouse.down());
1168
+ await untilAborted(signal, () => page.mouse.move(endX, endY, { steps: 12 }));
1169
+ await untilAborted(signal, () => page.mouse.up());
1170
+ return toolResult(details).text(`Dragged from ${fromSelector} to ${toSelector}`).done();
1171
+ }
1172
+ case "wait_for_selector": {
1173
+ const selector = ensureParam(params.selector, "selector", params.action);
1174
+ details.selector = selector;
1175
+ const page = await this.#ensurePage(params);
1176
+ await untilAborted(signal, () => waitFor(page, selector, undefined, timeoutMs));
1177
+ return toolResult(details).text(`Selector ready: ${selector}`).done();
1178
+ }
1179
+ case "evaluate": {
1180
+ const script = ensureParam(params.script, "script", params.action);
1181
+ const page = await this.#ensurePage(params);
1182
+ const value = (await untilAborted(signal, () =>
1183
+ page.evaluate(async (source: string) => {
1184
+ try {
1185
+ return await new Function(`return (async () => (${source}))();`)();
1186
+ } catch {
1187
+ return await new Function(`return (async () => { ${source} })();`)();
1188
+ }
1189
+ }, script),
1190
+ )) as unknown;
1191
+ const output = formatEvaluateResult(value);
1192
+ details.result = output;
1193
+ return toolResult(details).text(output).done();
1194
+ }
1195
+ case "get_text": {
1196
+ const page = await this.#ensurePage(params);
1197
+ if (params.args?.length) {
1198
+ const values = (await Promise.all(
1199
+ params.args.map(async (arg, index) => {
1200
+ const selector = ensureParam(arg.selector, `args[${index}].selector`, params.action);
1201
+ const h = await untilAborted(signal, () => resolve(page, selector));
1202
+ const t = (await h.evaluate(
1203
+ el => (el as unknown as { innerText: string }).innerText,
1204
+ )) as string;
1205
+ await h.dispose();
1206
+ return t;
1207
+ }),
1208
+ )) as string[];
1209
+ details.result = values;
1210
+ return toolResult(details)
1211
+ .text(JSON.stringify(values, null, 2))
1212
+ .done();
1213
+ }
1214
+ const selector = ensureParam(params.selector, "selector", params.action);
1215
+ details.selector = selector;
1216
+ const h = await untilAborted(signal, () => resolve(page, selector));
1217
+ const value = (await h.evaluate(el => (el as unknown as { innerText: string }).innerText)) as string;
1218
+ await h.dispose();
1219
+ details.result = value;
1220
+ return toolResult(details).text(value).done();
1221
+ }
1222
+ case "get_html": {
1223
+ const page = await this.#ensurePage(params);
1224
+ if (params.args?.length) {
1225
+ const values = (await Promise.all(
1226
+ params.args.map(async (arg, index) => {
1227
+ const selector = ensureParam(arg.selector, `args[${index}].selector`, params.action);
1228
+ const h = await untilAborted(signal, () => resolve(page, selector));
1229
+ const t = (await h.evaluate(
1230
+ el => (el as unknown as { innerHTML: string }).innerHTML,
1231
+ )) as string;
1232
+ await h.dispose();
1233
+ return t;
1234
+ }),
1235
+ )) as string[];
1236
+ details.result = values;
1237
+ return toolResult(details)
1238
+ .text(JSON.stringify(values, null, 2))
1239
+ .done();
1240
+ }
1241
+ const selector = ensureParam(params.selector, "selector", params.action);
1242
+ details.selector = selector;
1243
+ const h = await untilAborted(signal, () => resolve(page, selector));
1244
+ const value = (await h.evaluate(el => (el as unknown as { innerHTML: string }).innerHTML)) as string;
1245
+ await h.dispose();
1246
+ details.result = value;
1247
+ return toolResult(details).text(value).done();
1248
+ }
1249
+ case "get_attribute": {
1250
+ const page = await this.#ensurePage(params);
1251
+ if (params.args?.length) {
1252
+ const values = (await Promise.all(
1253
+ params.args.map(async (arg, index) => {
1254
+ const selector = ensureParam(arg.selector, `args[${index}].selector`, params.action);
1255
+ const attr = ensureParam(arg.attribute, `args[${index}].attribute`, params.action);
1256
+ const h = await resolve(page, selector);
1257
+ const v = await h.evaluate((el, a) => (el as HTMLElement).getAttribute(a), attr);
1258
+ await h.dispose();
1259
+ return v;
1260
+ }),
1261
+ )) as string[];
1262
+ details.result = values;
1263
+ return toolResult(details)
1264
+ .text(JSON.stringify(values, null, 2))
1265
+ .done();
1266
+ }
1267
+ const selector = ensureParam(params.selector, "selector", params.action);
1268
+ const attribute = ensureParam(params.attribute, "attribute", params.action);
1269
+ details.selector = selector;
1270
+ const h = await resolve(page, selector);
1271
+ const value = (await h.evaluate((el, a) => (el as HTMLElement).getAttribute(a), attribute)) as
1272
+ | string
1273
+ | null;
1274
+ await h.dispose();
1275
+ const output = value ?? "";
1276
+ details.result = output;
1277
+ return toolResult(details).text(output).done();
1278
+ }
1279
+ case "extract_readable": {
1280
+ const page = await this.#ensurePage(params);
1281
+ const format = params.format ?? "markdown";
1282
+ const html = (await untilAborted(signal, () => page.content())) as string;
1283
+ const url = page.url();
1284
+ const readable = extractReadableFromHtml(html, url, format);
1285
+ if (!readable) {
1286
+ throw new ToolError("Readable content not found");
1287
+ }
1288
+ details.url = url;
1289
+ details.readable = readable;
1290
+ details.result = format === "markdown" ? (readable.markdown ?? "") : (readable.text ?? "");
1291
+ return toolResult(details)
1292
+ .text(JSON.stringify(readable, null, 2))
1293
+ .done();
1294
+ }
1295
+ case "screenshot": {
1296
+ const page = await this.#ensurePage(params);
1297
+ const fullPage = params.selector ? false : (params.full_page ?? false);
1298
+ let buffer: Buffer;
1299
+
1300
+ if (params.selector) {
1301
+ const handle = await untilAborted(signal, () => resolve(page, params.selector as string));
1302
+ buffer = (await untilAborted(signal, () => handle.screenshot({ type: "png" }))) as Buffer;
1303
+ await handle.dispose();
1304
+ details.selector = params.selector;
1305
+ } else {
1306
+ buffer = (await untilAborted(signal, () => page.screenshot({ type: "png", fullPage }))) as Buffer;
1307
+ }
1308
+
1309
+ // Compress aggressively for API content — screenshots are the most
1310
+ // frequent image source and land directly in the next LLM request.
1311
+ // 1024px is plenty for OCR/UI inspection; 150KB keeps payloads lean.
1312
+ const resized = await resizeImage(
1313
+ { type: "image", data: buffer.toBase64(), mimeType: "image/png" },
1314
+ { maxWidth: 1024, maxHeight: 1024, maxBytes: 150 * 1024, jpegQuality: 70 },
1315
+ );
1316
+ // Resolve destination: user-defined path > screenshotDir (auto-named) > temp file.
1317
+ const screenshotDir = (() => {
1318
+ const v = this.session.settings.get("browser.screenshotDir") as string | undefined;
1319
+ return v ? expandPath(v) : undefined;
1320
+ })();
1321
+ const paramPath = params.path ? resolveToCwd(params.path as string, this.session.cwd) : undefined;
1322
+ let dest: string;
1323
+ if (paramPath) {
1324
+ dest = paramPath;
1325
+ } else if (screenshotDir) {
1326
+ const ts = new Date().toISOString().replace(/[:.]/g, "-").slice(0, -1);
1327
+ dest = path.join(screenshotDir, `screenshot-${ts}.png`);
1328
+ } else {
1329
+ dest = path.join(os.tmpdir(), `xcsh-sshots-${Snowflake.next()}.png`);
1330
+ }
1331
+ await fs.promises.mkdir(path.dirname(dest), { recursive: true });
1332
+ // Full-res buffer when saving to a user-defined location; resized (API copy) for temp-only.
1333
+ const saveFullRes = !!(paramPath || screenshotDir);
1334
+ const savedBuffer = saveFullRes ? buffer : resized.buffer;
1335
+ const savedMimeType = saveFullRes ? "image/png" : resized.mimeType;
1336
+ await Bun.write(dest, savedBuffer);
1337
+ details.screenshotPath = dest;
1338
+ details.mimeType = savedMimeType;
1339
+ details.bytes = savedBuffer.length;
1340
+
1341
+ const lines = formatScreenshot({
1342
+ saveFullRes,
1343
+ savedMimeType,
1344
+ savedByteLength: savedBuffer.length,
1345
+ dest,
1346
+ resized,
1347
+ });
1348
+ return toolResult(details)
1349
+ .content([
1350
+ { type: "text", text: lines.join("\n") },
1351
+ { type: "image", data: resized.data, mimeType: resized.mimeType },
1352
+ ])
1353
+ .done();
1354
+ }
1355
+ default:
1356
+ throw new ToolError(`Unsupported action: ${params.action}`);
1357
+ }
1358
+ } catch (error) {
1359
+ if (error instanceof ToolAbortError) throw error;
1360
+ if (error instanceof Error && error.name === "AbortError") {
1361
+ throw new ToolAbortError();
1362
+ }
1363
+ throw error;
1364
+ }
1365
+ }
1366
+ }