@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
package/src/sdk.ts ADDED
@@ -0,0 +1,2123 @@
1
+ import {
2
+ Agent,
3
+ type AgentEvent,
4
+ type AgentMessage,
5
+ type AgentTool,
6
+ INTENT_FIELD,
7
+ type ThinkingLevel,
8
+ } from "@f5xc-salesdemos/pi-agent-core";
9
+ import type { Message, Model } from "@f5xc-salesdemos/pi-ai";
10
+ import {
11
+ getOpenAICodexTransportDetails,
12
+ prewarmOpenAICodexResponses,
13
+ } from "@f5xc-salesdemos/pi-ai/providers/openai-codex-responses";
14
+ import type { Component } from "@f5xc-salesdemos/pi-tui";
15
+ import {
16
+ $env,
17
+ $flag,
18
+ getAgentDbPath,
19
+ getAgentDir,
20
+ getLocale,
21
+ getLocaleDisplayName,
22
+ getProjectDir,
23
+ logger,
24
+ postmortem,
25
+ prompt,
26
+ Snowflake,
27
+ } from "@f5xc-salesdemos/pi-utils";
28
+ import { AsyncJobManager, isBackgroundJobSupportEnabled } from "./async";
29
+ import { createAutoresearchExtension } from "./autoresearch";
30
+ import { loadCapability } from "./capability";
31
+ import { type Rule, ruleCapability } from "./capability/rule";
32
+ import { hasLiteLLMEnv } from "./config/auto-config";
33
+ import { ModelRegistry } from "./config/model-registry";
34
+ import { formatModelString, parseModelPattern, parseModelString, resolveModelRoleValue } from "./config/model-resolver";
35
+ import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./config/prompt-templates";
36
+ import { Settings, type SkillsSettings } from "./config/settings";
37
+ import { CursorExecHandlers } from "./cursor";
38
+ import "./discovery";
39
+ import { resolveConfigValue } from "./config/resolve-config-value";
40
+ import { initializeWithSettings } from "./discovery";
41
+ import { TtsrManager } from "./export/ttsr";
42
+ import {
43
+ type CustomCommandsLoadResult,
44
+ type LoadedCustomCommand,
45
+ loadCustomCommands as loadCustomCommandsInternal,
46
+ } from "./extensibility/custom-commands";
47
+ import { discoverAndLoadCustomTools } from "./extensibility/custom-tools";
48
+ import type { CustomTool, CustomToolContext, CustomToolSessionEvent } from "./extensibility/custom-tools/types";
49
+ import { CustomToolAdapter } from "./extensibility/custom-tools/wrapper";
50
+ import {
51
+ discoverAndLoadExtensions,
52
+ type ExtensionContext,
53
+ type ExtensionFactory,
54
+ ExtensionRunner,
55
+ ExtensionToolWrapper,
56
+ type ExtensionUIContext,
57
+ type LoadExtensionsResult,
58
+ loadExtensionFromFactory,
59
+ loadExtensions,
60
+ type ToolDefinition,
61
+ wrapRegisteredTools,
62
+ } from "./extensibility/extensions";
63
+ import { loadSkills as loadSkillsInternal, type Skill, type SkillWarning } from "./extensibility/skills";
64
+ import { type FileSlashCommand, loadSlashCommands as loadSlashCommandsInternal } from "./extensibility/slash-commands";
65
+ import {
66
+ AgentProtocolHandler,
67
+ ArtifactProtocolHandler,
68
+ InternalDocsProtocolHandler,
69
+ InternalUrlRouter,
70
+ JobsProtocolHandler,
71
+ LocalProtocolHandler,
72
+ McpProtocolHandler,
73
+ MemoryProtocolHandler,
74
+ RuleProtocolHandler,
75
+ SkillProtocolHandler,
76
+ } from "./internal-urls";
77
+ import { buildComputerHint, loadComputerProfile } from "./internal-urls/computer-profile";
78
+ import { loadProfile, type UserProfile } from "./internal-urls/user-profile";
79
+ import { disposeAllKernelSessions, disposeKernelSessionsByOwner } from "./ipy/executor";
80
+ import { LSP_STARTUP_EVENT_CHANNEL, type LspStartupEvent } from "./lsp/startup-events";
81
+ import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp";
82
+ import {
83
+ collectDiscoverableMCPTools,
84
+ formatDiscoverableMCPToolServerSummary,
85
+ selectDiscoverableMCPToolNamesByServer,
86
+ summarizeDiscoverableMCPTools,
87
+ } from "./mcp/discoverable-tool-metadata";
88
+ import { buildMemoryToolDeveloperInstructions, getMemoryRoot, startMemoryStartupTask } from "./memories";
89
+ import asyncResultTemplate from "./prompts/tools/async-result.md" with { type: "text" };
90
+ import {
91
+ collectEnvSecrets,
92
+ deobfuscateSessionContext,
93
+ loadSecrets,
94
+ obfuscateMessages,
95
+ SECRET_ENV_PATTERNS,
96
+ type SecretEntry,
97
+ SecretObfuscator,
98
+ } from "./secrets";
99
+ import { createContextEnv } from "./services/context-env";
100
+ import { AgentSession } from "./session/agent-session";
101
+ import { AuthStorage } from "./session/auth-storage";
102
+ import { convertToLlm } from "./session/messages";
103
+ import { SessionManager } from "./session/session-manager";
104
+ import { closeAllConnections } from "./ssh/connection-manager";
105
+ import { unmountAll } from "./ssh/sshfs-mount";
106
+ import {
107
+ buildSystemPrompt as buildSystemPromptInternal,
108
+ buildSystemPromptToolMetadata,
109
+ loadProjectContextFiles as loadContextFilesInternal,
110
+ } from "./system-prompt";
111
+ import { AgentOutputManager } from "./task/output-manager";
112
+ import { parseThinkingLevel, resolveThinkingLevelForModel, toReasoningEffort } from "./thinking";
113
+ import {
114
+ BashTool,
115
+ BUILTIN_TOOLS,
116
+ createTools,
117
+ discoverStartupLspServers,
118
+ EditTool,
119
+ FindTool,
120
+ GrepTool,
121
+ getSearchTools,
122
+ HIDDEN_TOOLS,
123
+ isSearchProviderPreference,
124
+ type LspStartupServerInfo,
125
+ loadSshTool,
126
+ PythonTool,
127
+ ReadTool,
128
+ ResolveTool,
129
+ renderSearchToolBm25Description,
130
+ setPreferredImageProvider,
131
+ setPreferredSearchProvider,
132
+ type Tool,
133
+ type ToolSession,
134
+ WriteTool,
135
+ warmupLspServers,
136
+ } from "./tools";
137
+ import { ToolContextStore } from "./tools/context";
138
+ import { getGeminiImageTools } from "./tools/gemini-image";
139
+ import { wrapToolWithMetaNotice } from "./tools/output-meta";
140
+ import { queueResolveHandler } from "./tools/resolve";
141
+ import { EventBus } from "./utils/event-bus";
142
+ import { buildNamedToolChoice } from "./utils/tool-choice";
143
+
144
+ // Types
145
+ export interface CreateAgentSessionOptions {
146
+ /** Working directory for project-local discovery. Default: getProjectDir() */
147
+ cwd?: string;
148
+ /** Global config directory. Default: ~/.omp/agent */
149
+ agentDir?: string;
150
+ /** Spawns to allow. Default: "*" */
151
+ spawns?: string;
152
+
153
+ /** Auth storage for credentials. Default: discoverAuthStorage(agentDir) */
154
+ authStorage?: AuthStorage;
155
+ /** Model registry. Default: discoverModels(authStorage, agentDir) */
156
+ modelRegistry?: ModelRegistry;
157
+
158
+ /** Model to use. Default: from settings, else first available */
159
+ model?: Model;
160
+ /** Raw model pattern string (e.g. from --model CLI flag) to resolve after extensions load.
161
+ * Used when model lookup is deferred because extension-provided models aren't registered yet. */
162
+ modelPattern?: string;
163
+ /** Thinking selector. Default: from settings, else unset */
164
+ thinkingLevel?: ThinkingLevel;
165
+ /** Models available for cycling (Ctrl+P in interactive mode) */
166
+ scopedModels?: Array<{ model: Model; thinkingLevel?: ThinkingLevel }>;
167
+
168
+ /** System prompt. String replaces default, function receives default and returns final. */
169
+ systemPrompt?: string | ((defaultPrompt: string) => string);
170
+ /** Optional provider-facing session identifier for prompt caches and sticky auth selection.
171
+ * Keeps persisted session files isolated while reusing provider-side caches. */
172
+ providerSessionId?: string;
173
+
174
+ /** Custom tools to register (in addition to built-in tools). Accepts both CustomTool and ToolDefinition. */
175
+ customTools?: (CustomTool | ToolDefinition)[];
176
+ /** Inline extensions (merged with discovery). */
177
+ extensions?: ExtensionFactory[];
178
+ /** Additional extension paths to load (merged with discovery). */
179
+ additionalExtensionPaths?: string[];
180
+ /** Disable extension discovery (explicit paths still load). */
181
+ disableExtensionDiscovery?: boolean;
182
+ /**
183
+ * Pre-loaded extensions (skips file discovery).
184
+ * @internal Used by CLI when extensions are loaded early to parse custom flags.
185
+ */
186
+ preloadedExtensions?: LoadExtensionsResult;
187
+
188
+ /** Shared event bus for tool/extension communication. Default: creates new bus. */
189
+ eventBus?: EventBus;
190
+
191
+ /** Skills. Default: discovered from multiple locations */
192
+ skills?: Skill[];
193
+ /** Rules. Default: discovered from multiple locations */
194
+ rules?: Rule[];
195
+ /** Context files (XCSH.md content). Default: discovered walking up from cwd */
196
+ contextFiles?: Array<{ path: string; content: string }>;
197
+ /** Prompt templates. Default: discovered from cwd/.omp/prompts/ + agentDir/prompts/ */
198
+ promptTemplates?: PromptTemplate[];
199
+ /** File-based slash commands. Default: discovered from commands/ directories */
200
+ slashCommands?: FileSlashCommand[];
201
+
202
+ /** Enable MCP server discovery from .mcp.json files. Default: true */
203
+ enableMCP?: boolean;
204
+
205
+ /** Enable LSP integration (tool, formatting, diagnostics, warmup). Default: true */
206
+ enableLsp?: boolean;
207
+ /** Skip Python kernel availability check and prelude warmup */
208
+ skipPythonPreflight?: boolean;
209
+ /** Force Python prelude warmup even when test env would normally skip it */
210
+ forcePythonWarmup?: boolean;
211
+
212
+ /** Tool names explicitly requested (enables disabled-by-default tools) */
213
+ toolNames?: string[];
214
+
215
+ /** Output schema for structured completion (subagents) */
216
+ outputSchema?: unknown;
217
+ /** Whether to include the submit_result tool by default */
218
+ requireSubmitResultTool?: boolean;
219
+ /** Task recursion depth (for subagent sessions). Default: 0 */
220
+ taskDepth?: number;
221
+ /** Parent task ID prefix for nested artifact naming (e.g., "6-Extensions") */
222
+ parentTaskPrefix?: string;
223
+
224
+ /** Session manager. Default: session stored under the configured agentDir sessions root */
225
+ sessionManager?: SessionManager;
226
+
227
+ /** Settings instance. Default: Settings.init({ cwd, agentDir }) */
228
+ settings?: Settings;
229
+
230
+ /** Whether UI is available (enables interactive tools like ask). Default: false */
231
+ hasUI?: boolean;
232
+
233
+ /** Opaque fuzzy-search database handle (fork-specific). */
234
+ searchDb?: unknown;
235
+ }
236
+
237
+ /** Result from createAgentSession */
238
+ export interface CreateAgentSessionResult {
239
+ /** The created session */
240
+ session: AgentSession;
241
+ /** Extensions result (loaded extensions + runtime) */
242
+ extensionsResult: LoadExtensionsResult;
243
+ /** Update tool UI context (interactive mode) */
244
+ setToolUIContext: (uiContext: ExtensionUIContext, hasUI: boolean) => void;
245
+ /** MCP manager for server lifecycle management (undefined if MCP disabled) */
246
+ mcpManager?: MCPManager;
247
+ /** Warning if session was restored with a different model than saved */
248
+ modelFallbackMessage?: string;
249
+ /** LSP servers detected for startup; warmup may continue in the background */
250
+ lspServers?: LspStartupServerInfo[];
251
+ /** Shared event bus for tool/extension communication */
252
+ eventBus: EventBus;
253
+ }
254
+
255
+ // Re-exports
256
+
257
+ export type { PromptTemplate } from "./config/prompt-templates";
258
+ export { Settings, type SkillsSettings } from "./config/settings";
259
+ export type { CustomCommand, CustomCommandFactory } from "./extensibility/custom-commands/types";
260
+ export type { CustomTool, CustomToolFactory } from "./extensibility/custom-tools/types";
261
+ export type * from "./extensibility/extensions";
262
+ export type { Skill } from "./extensibility/skills";
263
+ export type { FileSlashCommand } from "./extensibility/slash-commands";
264
+ export type { MCPManager, MCPServerConfig, MCPServerConnection, MCPToolsLoadResult } from "./mcp";
265
+ export type { Tool } from "./tools";
266
+
267
+ export {
268
+ // Individual tool classes (for custom usage)
269
+ BashTool,
270
+ // Tool classes and factories
271
+ BUILTIN_TOOLS,
272
+ createTools,
273
+ EditTool,
274
+ FindTool,
275
+ GrepTool,
276
+ HIDDEN_TOOLS,
277
+ loadSshTool,
278
+ PythonTool,
279
+ ReadTool,
280
+ ResolveTool,
281
+ type ToolSession,
282
+ WriteTool,
283
+ };
284
+
285
+ // Helper Functions
286
+
287
+ function getDefaultAgentDir(): string {
288
+ return getAgentDir();
289
+ }
290
+
291
+ // Discovery Functions
292
+
293
+ /**
294
+ * Create an AuthStorage instance with fallback support.
295
+ * Reads from primary path first, then falls back to legacy paths (.pi, .codex).
296
+ */
297
+ export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir()): Promise<AuthStorage> {
298
+ const dbPath = getAgentDbPath(agentDir);
299
+ logger.debug("discoverAuthStorage", { agentDir, dbPath });
300
+
301
+ const storage = await AuthStorage.create(dbPath, { configValueResolver: resolveConfigValue });
302
+ await storage.reload();
303
+ return storage;
304
+ }
305
+
306
+ /**
307
+ * Discover extensions from cwd.
308
+ */
309
+ export async function discoverExtensions(cwd?: string): Promise<LoadExtensionsResult> {
310
+ const resolvedCwd = cwd ?? getProjectDir();
311
+
312
+ return discoverAndLoadExtensions([], resolvedCwd);
313
+ }
314
+
315
+ /**
316
+ * Discover skills from cwd and agentDir.
317
+ */
318
+ export async function discoverSkills(
319
+ cwd?: string,
320
+ _agentDir?: string,
321
+ settings?: SkillsSettings,
322
+ ): Promise<{ skills: Skill[]; warnings: SkillWarning[] }> {
323
+ return await loadSkillsInternal({
324
+ ...settings,
325
+ cwd: cwd ?? getProjectDir(),
326
+ });
327
+ }
328
+
329
+ /**
330
+ * Discover context files (XCSH.md) walking up from cwd.
331
+ * Returns files sorted by depth (farther from cwd first, so closer files appear last/more prominent).
332
+ */
333
+ export async function discoverContextFiles(
334
+ cwd?: string,
335
+ _agentDir?: string,
336
+ ): Promise<Array<{ path: string; content: string; depth?: number }>> {
337
+ return await loadContextFilesInternal({
338
+ cwd: cwd ?? getProjectDir(),
339
+ });
340
+ }
341
+
342
+ /**
343
+ * Discover prompt templates from cwd and agentDir.
344
+ */
345
+ export async function discoverPromptTemplates(cwd?: string, agentDir?: string): Promise<PromptTemplate[]> {
346
+ return await loadPromptTemplatesInternal({
347
+ cwd: cwd ?? getProjectDir(),
348
+ agentDir: agentDir ?? getDefaultAgentDir(),
349
+ });
350
+ }
351
+
352
+ /**
353
+ * Discover file-based slash commands from commands/ directories.
354
+ */
355
+ export async function discoverSlashCommands(cwd?: string): Promise<FileSlashCommand[]> {
356
+ return loadSlashCommandsInternal({ cwd: cwd ?? getProjectDir() });
357
+ }
358
+
359
+ /**
360
+ * Discover custom commands (TypeScript slash commands) from cwd and agentDir.
361
+ */
362
+ export async function discoverCustomTSCommands(cwd?: string, agentDir?: string): Promise<CustomCommandsLoadResult> {
363
+ const resolvedCwd = cwd ?? getProjectDir();
364
+ const resolvedAgentDir = agentDir ?? getDefaultAgentDir();
365
+
366
+ return loadCustomCommandsInternal({
367
+ cwd: resolvedCwd,
368
+ agentDir: resolvedAgentDir,
369
+ });
370
+ }
371
+
372
+ /**
373
+ * Discover MCP servers from .mcp.json files.
374
+ * Returns the manager and loaded tools.
375
+ */
376
+ export async function discoverMCPServers(cwd?: string): Promise<MCPToolsLoadResult> {
377
+ const resolvedCwd = cwd ?? getProjectDir();
378
+ return discoverAndLoadMCPTools(resolvedCwd);
379
+ }
380
+
381
+ // API Key Helpers
382
+
383
+ // System Prompt
384
+
385
+ export interface BuildSystemPromptOptions {
386
+ tools?: Tool[];
387
+ skills?: Skill[];
388
+ contextFiles?: Array<{ path: string; content: string }>;
389
+ cwd?: string;
390
+ appendPrompt?: string;
391
+ repeatToolDescriptions?: boolean;
392
+ }
393
+
394
+ /**
395
+ * Build the default system prompt.
396
+ */
397
+ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}): Promise<string> {
398
+ return await buildSystemPromptInternal({
399
+ cwd: options.cwd,
400
+ skills: options.skills,
401
+ contextFiles: options.contextFiles,
402
+ appendSystemPrompt: options.appendPrompt,
403
+ repeatToolDescriptions: options.repeatToolDescriptions,
404
+ });
405
+ }
406
+
407
+ // Internal Helpers
408
+
409
+ function createCustomToolContext(ctx: ExtensionContext): CustomToolContext {
410
+ return {
411
+ sessionManager: ctx.sessionManager,
412
+ modelRegistry: ctx.modelRegistry,
413
+ model: ctx.model,
414
+ isIdle: ctx.isIdle,
415
+ hasQueuedMessages: ctx.hasPendingMessages,
416
+ abort: ctx.abort,
417
+ };
418
+ }
419
+
420
+ function isCustomTool(tool: CustomTool | ToolDefinition): tool is CustomTool {
421
+ // To distinguish, we mark converted tools with a hidden symbol property.
422
+ // If the tool doesn't have this marker, it's a CustomTool that needs conversion.
423
+ return !(tool as any).__isToolDefinition;
424
+ }
425
+
426
+ const TOOL_DEFINITION_MARKER = Symbol("__isToolDefinition");
427
+
428
+ let sshCleanupRegistered = false;
429
+
430
+ async function cleanupSshResources(): Promise<void> {
431
+ const results = await Promise.allSettled([closeAllConnections(), unmountAll()]);
432
+ for (const result of results) {
433
+ if (result.status === "rejected") {
434
+ logger.warn("SSH cleanup failed", { error: String(result.reason) });
435
+ }
436
+ }
437
+ }
438
+
439
+ function registerSshCleanup(): void {
440
+ if (sshCleanupRegistered) return;
441
+ sshCleanupRegistered = true;
442
+ postmortem.register("ssh-cleanup", cleanupSshResources);
443
+ }
444
+
445
+ let pythonCleanupRegistered = false;
446
+
447
+ function registerPythonCleanup(): void {
448
+ if (pythonCleanupRegistered) return;
449
+ pythonCleanupRegistered = true;
450
+ postmortem.register("python-cleanup", disposeAllKernelSessions);
451
+ }
452
+
453
+ function customToolToDefinition(tool: CustomTool): ToolDefinition {
454
+ const definition: ToolDefinition & { [TOOL_DEFINITION_MARKER]: true } = {
455
+ name: tool.name,
456
+ label: tool.label,
457
+ description: tool.description,
458
+ parameters: tool.parameters,
459
+ hidden: tool.hidden,
460
+ deferrable: tool.deferrable,
461
+ mcpServerName: tool.mcpServerName,
462
+ mcpToolName: tool.mcpToolName,
463
+ execute: (toolCallId, params, signal, onUpdate, ctx) =>
464
+ tool.execute(toolCallId, params, onUpdate, createCustomToolContext(ctx), signal),
465
+ onSession: tool.onSession ? (event, ctx) => tool.onSession?.(event, createCustomToolContext(ctx)) : undefined,
466
+ renderCall: tool.renderCall,
467
+ renderResult: tool.renderResult
468
+ ? (result, options, theme): Component => {
469
+ const component = tool.renderResult?.(
470
+ result,
471
+ { expanded: options.expanded, isPartial: options.isPartial, spinnerFrame: options.spinnerFrame },
472
+ theme,
473
+ );
474
+ // Return empty component if undefined to match Component type requirement
475
+ return component ?? ({ render: () => [] } as unknown as Component);
476
+ }
477
+ : undefined,
478
+ [TOOL_DEFINITION_MARKER]: true,
479
+ };
480
+ return definition;
481
+ }
482
+
483
+ function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory {
484
+ return api => {
485
+ for (const tool of tools) {
486
+ api.registerTool(customToolToDefinition(tool));
487
+ }
488
+
489
+ const runOnSession = async (event: CustomToolSessionEvent, ctx: ExtensionContext) => {
490
+ for (const tool of tools) {
491
+ if (!tool.onSession) continue;
492
+ try {
493
+ await tool.onSession(event, createCustomToolContext(ctx));
494
+ } catch (err) {
495
+ logger.warn("Custom tool onSession error", { tool: tool.name, error: String(err) });
496
+ }
497
+ }
498
+ };
499
+
500
+ api.on("session_start", async (_event, ctx) =>
501
+ runOnSession({ reason: "start", previousSessionFile: undefined }, ctx),
502
+ );
503
+ api.on("session_switch", async (event, ctx) =>
504
+ runOnSession({ reason: "switch", previousSessionFile: event.previousSessionFile }, ctx),
505
+ );
506
+ api.on("session_branch", async (event, ctx) =>
507
+ runOnSession({ reason: "branch", previousSessionFile: event.previousSessionFile }, ctx),
508
+ );
509
+ api.on("session_tree", async (_event, ctx) =>
510
+ runOnSession({ reason: "tree", previousSessionFile: undefined }, ctx),
511
+ );
512
+ api.on("session_shutdown", async (_event, ctx) =>
513
+ runOnSession({ reason: "shutdown", previousSessionFile: undefined }, ctx),
514
+ );
515
+ api.on("auto_compaction_start", async (event, ctx) =>
516
+ runOnSession({ reason: "auto_compaction_start", trigger: event.reason, action: event.action }, ctx),
517
+ );
518
+ api.on("auto_compaction_end", async (event, ctx) =>
519
+ runOnSession(
520
+ {
521
+ reason: "auto_compaction_end",
522
+ action: event.action,
523
+ result: event.result,
524
+ aborted: event.aborted,
525
+ willRetry: event.willRetry,
526
+ errorMessage: event.errorMessage,
527
+ },
528
+ ctx,
529
+ ),
530
+ );
531
+ api.on("auto_retry_start", async (event, ctx) =>
532
+ runOnSession(
533
+ {
534
+ reason: "auto_retry_start",
535
+ attempt: event.attempt,
536
+ maxAttempts: event.maxAttempts,
537
+ delayMs: event.delayMs,
538
+ errorMessage: event.errorMessage,
539
+ },
540
+ ctx,
541
+ ),
542
+ );
543
+ api.on("auto_retry_end", async (event, ctx) =>
544
+ runOnSession(
545
+ {
546
+ reason: "auto_retry_end",
547
+ success: event.success,
548
+ attempt: event.attempt,
549
+ finalError: event.finalError,
550
+ },
551
+ ctx,
552
+ ),
553
+ );
554
+ api.on("ttsr_triggered", async (event, ctx) =>
555
+ runOnSession({ reason: "ttsr_triggered", rules: event.rules }, ctx),
556
+ );
557
+ api.on("todo_reminder", async (event, ctx) =>
558
+ runOnSession(
559
+ {
560
+ reason: "todo_reminder",
561
+ todos: event.todos,
562
+ attempt: event.attempt,
563
+ maxAttempts: event.maxAttempts,
564
+ },
565
+ ctx,
566
+ ),
567
+ );
568
+ };
569
+ }
570
+
571
+ // Factory
572
+
573
+ /**
574
+ * Build LoadedCustomCommand entries for all MCP prompts across connected servers.
575
+ * These are re-created whenever prompts change (setOnPromptsChanged callback).
576
+ */
577
+ function buildMCPPromptCommands(manager: MCPManager): LoadedCustomCommand[] {
578
+ const commands: LoadedCustomCommand[] = [];
579
+ for (const serverName of manager.getConnectedServers()) {
580
+ const prompts = manager.getServerPrompts(serverName);
581
+ if (!prompts?.length) continue;
582
+ for (const prompt of prompts) {
583
+ const commandName = `${serverName}:${prompt.name}`;
584
+ commands.push({
585
+ path: `mcp:${commandName}`,
586
+ resolvedPath: `mcp:${commandName}`,
587
+ source: "bundled",
588
+ command: {
589
+ name: commandName,
590
+ description: prompt.description ?? `MCP prompt from ${serverName}`,
591
+ async execute(args: string[]) {
592
+ const promptArgs: Record<string, string> = {};
593
+ for (const arg of args) {
594
+ const eqIdx = arg.indexOf("=");
595
+ if (eqIdx > 0) {
596
+ promptArgs[arg.slice(0, eqIdx)] = arg.slice(eqIdx + 1);
597
+ }
598
+ }
599
+ const result = await manager.executePrompt(serverName, prompt.name, promptArgs);
600
+ if (!result) return "";
601
+ const parts: string[] = [];
602
+ for (const msg of result.messages) {
603
+ const contentItems = Array.isArray(msg.content) ? msg.content : [msg.content];
604
+ for (const item of contentItems) {
605
+ if (item.type === "text") {
606
+ parts.push(item.text);
607
+ } else if (item.type === "resource") {
608
+ const resource = item.resource;
609
+ if (resource.text) parts.push(resource.text);
610
+ }
611
+ }
612
+ }
613
+ return parts.join("\n\n");
614
+ },
615
+ },
616
+ });
617
+ }
618
+ }
619
+ return commands;
620
+ }
621
+ /**
622
+ * Create an AgentSession with the specified options.
623
+ *
624
+ * @example
625
+ * ```typescript
626
+ * // Minimal - uses defaults
627
+ * const { session } = await createAgentSession();
628
+ *
629
+ * // With explicit model
630
+ * import { getModel } from '@f5xc-salesdemos/pi-ai';
631
+ * const { session } = await createAgentSession({
632
+ * model: getModel('anthropic', 'claude-opus-4-5'),
633
+ * thinkingLevel: 'high',
634
+ * });
635
+ *
636
+ * // Continue previous session
637
+ * const { session, modelFallbackMessage } = await createAgentSession({
638
+ * continueSession: true,
639
+ * });
640
+ *
641
+ * // Full control
642
+ * const { session } = await createAgentSession({
643
+ * model: myModel,
644
+ * getApiKey: async () => Bun.env.MY_KEY,
645
+ * systemPrompt: 'You are helpful.',
646
+ * tools: codingTools({ cwd: getProjectDir() }),
647
+ * skills: [],
648
+ * sessionManager: SessionManager.inMemory(),
649
+ * });
650
+ * ```
651
+ */
652
+ export async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {
653
+ const cwd = options.cwd ?? getProjectDir();
654
+ const agentDir = options.agentDir ?? getDefaultAgentDir();
655
+ const eventBus = options.eventBus ?? new EventBus();
656
+
657
+ registerSshCleanup();
658
+ registerPythonCleanup();
659
+
660
+ // Use provided or create AuthStorage and ModelRegistry
661
+ const authStorage = options.authStorage ?? (await logger.time("discoverModels", discoverAuthStorage, agentDir));
662
+ const modelRegistry = options.modelRegistry ?? new ModelRegistry(authStorage);
663
+
664
+ const settings = options.settings ?? (await logger.time("settings", Settings.init, { cwd, agentDir }));
665
+ logger.time("initializeWithSettings");
666
+ initializeWithSettings(settings);
667
+ if (!options.modelRegistry) {
668
+ modelRegistry.refreshInBackground();
669
+ }
670
+ const skillsSettings = settings.getGroup("skills");
671
+ const disabledExtensionIds = settings.get("disabledExtensions") ?? [];
672
+ const discoveredSkillsPromise =
673
+ options.skills === undefined
674
+ ? discoverSkills(cwd, agentDir, { ...skillsSettings, disabledExtensions: disabledExtensionIds })
675
+ : undefined;
676
+
677
+ // Initialize provider preferences from settings
678
+ const webSearchProvider = settings.get("providers.webSearch");
679
+ if (typeof webSearchProvider === "string" && isSearchProviderPreference(webSearchProvider)) {
680
+ setPreferredSearchProvider(webSearchProvider);
681
+ }
682
+
683
+ const imageProvider = settings.get("providers.image");
684
+ if (imageProvider === "auto" || imageProvider === "gemini" || imageProvider === "openrouter") {
685
+ setPreferredImageProvider(imageProvider);
686
+ }
687
+
688
+ const sessionManager =
689
+ options.sessionManager ??
690
+ logger.time("sessionManager", () =>
691
+ SessionManager.create(cwd, SessionManager.getDefaultSessionDir(cwd, agentDir)),
692
+ );
693
+ const providerSessionId = options.providerSessionId ?? sessionManager.getSessionId();
694
+ const modelApiKeyAvailability = new Map<string, boolean>();
695
+ const getModelAvailabilityKey = (candidate: Model): string =>
696
+ `${candidate.provider}\u0000${candidate.baseUrl ?? ""}`;
697
+ const hasModelApiKey = async (candidate: Model): Promise<boolean> => {
698
+ const availabilityKey = getModelAvailabilityKey(candidate);
699
+ const cached = modelApiKeyAvailability.get(availabilityKey);
700
+ if (cached !== undefined) {
701
+ return cached;
702
+ }
703
+
704
+ const hasKey = !!(await modelRegistry.getApiKey(candidate, providerSessionId));
705
+ modelApiKeyAvailability.set(availabilityKey, hasKey);
706
+ return hasKey;
707
+ };
708
+
709
+ // Load and create secret obfuscator early so resumed session state and prompt warnings
710
+ // reflect actual loaded secrets, not just the setting toggle.
711
+ // Env-based secrets are always collected (hardcoded masking — no opt-in required).
712
+ // File-based secrets (secrets.yml) remain opt-in behind the secrets.enabled setting.
713
+ let obfuscator: SecretObfuscator | undefined;
714
+ {
715
+ // Collect context-sensitive values (context loads before session in main.ts).
716
+ let contextSensitiveValues: string[] | undefined;
717
+ try {
718
+ const { ContextService } = await import("./services/xcsh-context");
719
+ contextSensitiveValues = ContextService.getSensitiveContextValues();
720
+ } catch {
721
+ // ContextService not initialized — skip (SDK consumers, tests, etc.)
722
+ }
723
+ // Scan both process.env AND bash.environment (context-injected values)
724
+ // for env vars matching sensitive name patterns.
725
+ const bashEnv = (settings.get("bash.environment") ?? {}) as Record<string, string>;
726
+ const envEntries = collectEnvSecrets({
727
+ additionalEnv: bashEnv,
728
+ additionalValues: contextSensitiveValues,
729
+ });
730
+ let fileEntries: SecretEntry[] = [];
731
+ if (settings.get("secrets.enabled")) {
732
+ fileEntries = await logger.time("loadSecrets", loadSecrets, cwd, agentDir);
733
+ }
734
+ // File entries MUST come first to preserve placeholder index stability
735
+ // for resumed sessions that persisted #HASH# tokens from secrets.yml.
736
+ const allEntries = [...fileEntries, ...envEntries];
737
+ if (allEntries.length > 0) {
738
+ obfuscator = new SecretObfuscator(allEntries);
739
+ }
740
+ }
741
+ const secretsEnabled = obfuscator?.hasSecrets() === true;
742
+
743
+ // Capture ContextService reference for sync consumers (e.g., InternalDocsProtocolHandler's
744
+ // getContextStatus getter below). Null when ContextService isn't available (SDK consumers, tests).
745
+ let contextServiceRef: typeof import("./services/xcsh-context").ContextService | null = null;
746
+ let knowledgeServiceRef: typeof import("./services/xcsh-knowledge").KnowledgeService | null = null;
747
+
748
+ // Capture ContextService reference for sync consumers (rebuildSystemPrompt context resolution,
749
+ // InternalDocsProtocolHandler getContextStatus). The context-change listener itself is
750
+ // registered later, atomically with its addDisposeHook cleanup, AFTER session construction
751
+ // succeeds — registering it here would leak on createAgentSession failures.
752
+ try {
753
+ const { ContextService } = await import("./services/xcsh-context");
754
+ contextServiceRef = ContextService;
755
+ } catch {
756
+ // ContextService not available (SDK consumers, tests). Skip.
757
+ }
758
+ try {
759
+ const { KnowledgeService } = await import("./services/xcsh-knowledge");
760
+ const { getXCSHConfigDir } = await import("@f5xc-salesdemos/pi-utils");
761
+ knowledgeServiceRef = KnowledgeService;
762
+ if (!KnowledgeService._hasInstance()) {
763
+ KnowledgeService.init(getXCSHConfigDir());
764
+ KnowledgeService.instance.loadCache();
765
+ }
766
+ } catch {
767
+ // KnowledgeService not available — skip.
768
+ }
769
+
770
+ // Check if session has existing data to restore
771
+ const existingSession = logger.time("loadSessionContext", () =>
772
+ deobfuscateSessionContext(sessionManager.buildSessionContext(), obfuscator),
773
+ );
774
+ const existingBranch = logger.time("getSessionBranch", () => sessionManager.getBranch());
775
+ const hasExistingSession = existingBranch.length > 0;
776
+ const hasThinkingEntry = existingBranch.some(entry => entry.type === "thinking_level_change");
777
+ const hasServiceTierEntry = existingBranch.some(entry => entry.type === "service_tier_change");
778
+
779
+ const hasExplicitModel = options.model !== undefined || options.modelPattern !== undefined;
780
+ const modelMatchPreferences = {
781
+ usageOrder: settings.getStorage()?.getModelUsageOrder(),
782
+ };
783
+ // When LiteLLM is configured and no model cache exists yet (first run),
784
+ // await the background refresh so model discovery from the proxy completes
785
+ // before we select a default model. Bounded by the 3s probe timeout.
786
+ if (!options.modelRegistry && hasLiteLLMEnv() && modelRegistry.hasUncachedDiscoverableProviders()) {
787
+ await logger.time("awaitLiteLLMDiscovery", () => modelRegistry.awaitBackgroundRefresh());
788
+ }
789
+
790
+ const defaultRoleSpec = logger.time("resolveDefaultModelRole", () =>
791
+ resolveModelRoleValue(settings.getModelRole("default"), modelRegistry.getAvailable(), {
792
+ settings,
793
+ matchPreferences: modelMatchPreferences,
794
+ modelRegistry,
795
+ }),
796
+ );
797
+ let model = options.model;
798
+ let modelFallbackMessage: string | undefined;
799
+ // If session has data, try to restore model from it.
800
+ // Skip restore when an explicit model was requested.
801
+ const defaultModelStr = existingSession.models.default;
802
+ if (!hasExplicitModel && !model && hasExistingSession && defaultModelStr) {
803
+ await logger.time("restoreSessionModel", async () => {
804
+ const parsedModel = parseModelString(defaultModelStr);
805
+ if (parsedModel) {
806
+ const restoredModel = modelRegistry.find(parsedModel.provider, parsedModel.id);
807
+ if (restoredModel && (await hasModelApiKey(restoredModel))) {
808
+ model = restoredModel;
809
+ }
810
+ }
811
+ if (!model) {
812
+ modelFallbackMessage = `Could not restore model ${defaultModelStr}`;
813
+ }
814
+ });
815
+ }
816
+
817
+ // If still no model, try settings default.
818
+ // Skip settings fallback when an explicit model was requested.
819
+ if (!hasExplicitModel && !model && defaultRoleSpec.model) {
820
+ const settingsDefaultModel = defaultRoleSpec.model;
821
+ logger.time("resolveSettingsDefaultModel", () => {
822
+ // defaultRoleSpec.model already comes from modelRegistry.getAvailable(),
823
+ // so re-validating auth here just repeats the expensive lookup path.
824
+ model = settingsDefaultModel;
825
+ });
826
+ }
827
+
828
+ const taskDepth = options.taskDepth ?? 0;
829
+
830
+ let thinkingLevel = options.thinkingLevel;
831
+
832
+ // If session has data and includes a thinking entry, restore it
833
+ if (thinkingLevel === undefined && hasExistingSession && hasThinkingEntry) {
834
+ thinkingLevel = parseThinkingLevel(existingSession.thinkingLevel);
835
+ }
836
+
837
+ if (thinkingLevel === undefined && !hasExplicitModel && !hasThinkingEntry && defaultRoleSpec.explicitThinkingLevel) {
838
+ thinkingLevel = defaultRoleSpec.thinkingLevel;
839
+ }
840
+
841
+ // Fall back to settings default
842
+ if (thinkingLevel === undefined) {
843
+ thinkingLevel = settings.get("defaultThinkingLevel");
844
+ }
845
+ if (model) {
846
+ const resolvedModel = model;
847
+ thinkingLevel = logger.time("resolveThinkingLevelForModel", () =>
848
+ resolveThinkingLevelForModel(resolvedModel, thinkingLevel),
849
+ );
850
+ }
851
+
852
+ let skills: Skill[];
853
+ let skillWarnings: SkillWarning[];
854
+ if (options.skills !== undefined) {
855
+ skills = options.skills;
856
+ skillWarnings = [];
857
+ } else {
858
+ const discovered = await logger.time(
859
+ "discoverSkills",
860
+ () => discoveredSkillsPromise ?? Promise.resolve({ skills: [], warnings: [] }),
861
+ );
862
+ skills = discovered.skills;
863
+ skillWarnings = discovered.warnings;
864
+ }
865
+
866
+ // Discover rules and bucket them in one pass to avoid repeated scans over large rule sets.
867
+ const { ttsrManager, rulebookRules, alwaysApplyRules } = await logger.time("discoverTtsrRules", async () => {
868
+ const ttsrSettings = settings.getGroup("ttsr");
869
+ const ttsrManager = new TtsrManager(ttsrSettings);
870
+ const rulesResult =
871
+ options.rules !== undefined
872
+ ? { items: options.rules, warnings: undefined }
873
+ : await loadCapability<Rule>(ruleCapability.id, { cwd });
874
+ const rulebookRules: Rule[] = [];
875
+ const alwaysApplyRules: Rule[] = [];
876
+ for (const rule of rulesResult.items) {
877
+ const isTtsrRule = rule.condition && rule.condition.length > 0 ? ttsrManager.addRule(rule) : false;
878
+ if (isTtsrRule) {
879
+ continue;
880
+ }
881
+ if (rule.alwaysApply === true) {
882
+ alwaysApplyRules.push(rule);
883
+ continue;
884
+ }
885
+ if (rule.description) {
886
+ rulebookRules.push(rule);
887
+ }
888
+ }
889
+ if (existingSession.injectedTtsrRules.length > 0) {
890
+ ttsrManager.restoreInjected(existingSession.injectedTtsrRules);
891
+ }
892
+ return { ttsrManager, rulebookRules, alwaysApplyRules };
893
+ });
894
+
895
+ const contextFiles = await logger.time(
896
+ "discoverContextFiles",
897
+ async () => options.contextFiles ?? (await discoverContextFiles(cwd, agentDir)),
898
+ );
899
+
900
+ let agent: Agent;
901
+ let session!: AgentSession;
902
+ let hasSession = false;
903
+ // LSP is opt-in: a language server initialized against a very large workspace
904
+ // (e.g. terraform-ls on a multi-repo tree) can stream unbounded output that is
905
+ // buffered without limit and exhausts process memory (OOM). `lsp.enabled` is
906
+ // the master switch — it gates the startup warmup and write/edit diagnostics.
907
+ const enableLsp = options.enableLsp ?? settings.get("lsp.enabled") ?? false;
908
+ const backgroundJobsEnabled = isBackgroundJobSupportEnabled(settings);
909
+ const asyncMaxJobs = Math.min(100, Math.max(1, settings.get("async.maxJobs") ?? 100));
910
+ const ASYNC_INLINE_RESULT_MAX_CHARS = 12_000;
911
+ const ASYNC_PREVIEW_MAX_CHARS = 4_000;
912
+ const formatAsyncResultForFollowUp = async (result: string): Promise<string> => {
913
+ if (result.length <= ASYNC_INLINE_RESULT_MAX_CHARS) {
914
+ return result;
915
+ }
916
+
917
+ const preview = `${result.slice(0, ASYNC_PREVIEW_MAX_CHARS)}\n\n[Output truncated. Showing first ${ASYNC_PREVIEW_MAX_CHARS.toLocaleString()} characters.]`;
918
+ try {
919
+ const { path: artifactPath, id: artifactId } = await sessionManager.allocateArtifactPath("async");
920
+ if (artifactPath && artifactId) {
921
+ await Bun.write(artifactPath, result);
922
+ return `${preview}\nFull output: artifact://${artifactId}`;
923
+ }
924
+ } catch (error) {
925
+ logger.warn("Failed to persist async follow-up artifact", {
926
+ error: error instanceof Error ? error.message : String(error),
927
+ });
928
+ }
929
+
930
+ return preview;
931
+ };
932
+ const asyncJobManager = backgroundJobsEnabled
933
+ ? new AsyncJobManager({
934
+ maxRunningJobs: asyncMaxJobs,
935
+ onJobComplete: async (jobId, result, job) => {
936
+ if (!session || asyncJobManager!.isDeliverySuppressed(jobId)) return;
937
+ const formattedResult = await formatAsyncResultForFollowUp(result);
938
+ if (asyncJobManager!.isDeliverySuppressed(jobId)) return;
939
+
940
+ const message = prompt.render(asyncResultTemplate, { jobId, result: formattedResult });
941
+ const durationMs = job ? Math.max(0, Date.now() - job.startTime) : undefined;
942
+ await session.sendCustomMessage(
943
+ {
944
+ customType: "async-result",
945
+ content: message,
946
+ display: true,
947
+ attribution: "agent",
948
+ details: {
949
+ jobId,
950
+ type: job?.type,
951
+ label: job?.label,
952
+ durationMs,
953
+ },
954
+ },
955
+ { deliverAs: "followUp", triggerTurn: true },
956
+ );
957
+ },
958
+ })
959
+ : undefined;
960
+
961
+ const pythonKernelOwnerId = `agent-session:${Snowflake.next()}`;
962
+
963
+ try {
964
+ const getActiveModelString = (): string | undefined => {
965
+ const activeModel = agent?.state.model;
966
+ if (activeModel) return formatModelString(activeModel);
967
+ if (model) return formatModelString(model);
968
+ return undefined;
969
+ };
970
+ const toolSession: ToolSession = {
971
+ cwd,
972
+ hasUI: options.hasUI ?? false,
973
+ enableLsp,
974
+ get hasEditTool() {
975
+ const requestedToolNames = options.toolNames
976
+ ? [...new Set(options.toolNames.map(name => name.toLowerCase()))]
977
+ : undefined;
978
+ return !requestedToolNames || requestedToolNames.includes("edit");
979
+ },
980
+ skipPythonPreflight: options.skipPythonPreflight,
981
+ forcePythonWarmup: options.forcePythonWarmup,
982
+ contextFiles,
983
+ skills,
984
+ eventBus,
985
+ outputSchema: options.outputSchema,
986
+ requireSubmitResultTool: options.requireSubmitResultTool,
987
+ taskDepth: options.taskDepth ?? 0,
988
+ getSessionFile: () => sessionManager.getSessionFile() ?? null,
989
+ getPythonKernelOwnerId: () => pythonKernelOwnerId,
990
+ assertPythonExecutionAllowed: () => session?.assertPythonExecutionAllowed(),
991
+ trackPythonExecution: (execution, abortController) =>
992
+ session ? session.trackPythonExecution(execution, abortController) : execution,
993
+ getSessionId: () => sessionManager.getSessionId?.() ?? null,
994
+ getSessionSpawns: () => options.spawns ?? "*",
995
+ getModelString: () => (hasExplicitModel && model ? formatModelString(model) : undefined),
996
+ getActiveModelString,
997
+ getPlanModeState: () => session.getPlanModeState(),
998
+ getCompactContext: () => session.formatCompactContext(),
999
+ getTodoPhases: () => session.getTodoPhases(),
1000
+ setTodoPhases: phases => session.setTodoPhases(phases),
1001
+ isMCPDiscoveryEnabled: () => session.isMCPDiscoveryEnabled(),
1002
+ getDiscoverableMCPTools: () => session.getDiscoverableMCPTools(),
1003
+ getDiscoverableMCPSearchIndex: () => session.getDiscoverableMCPSearchIndex(),
1004
+ getSelectedMCPToolNames: () => session.getSelectedMCPToolNames(),
1005
+ activateDiscoveredMCPTools: toolNames => session.activateDiscoveredMCPTools(toolNames),
1006
+ getCheckpointState: () => session.getCheckpointState(),
1007
+ setCheckpointState: state => session.setCheckpointState(state ?? undefined),
1008
+ getToolChoiceQueue: () => session.toolChoiceQueue,
1009
+ buildToolChoice: name => {
1010
+ const m = session.model;
1011
+ return m ? buildNamedToolChoice(name, m) : undefined;
1012
+ },
1013
+ steer: msg =>
1014
+ session.agent.steer({
1015
+ role: "custom",
1016
+ customType: msg.customType,
1017
+ content: msg.content,
1018
+ display: false,
1019
+ details: msg.details,
1020
+ attribution: "agent",
1021
+ timestamp: Date.now(),
1022
+ }),
1023
+ peekQueueInvoker: () => session.peekQueueInvoker(),
1024
+ allocateOutputArtifact: async toolType => {
1025
+ try {
1026
+ return await sessionManager.allocateArtifactPath(toolType);
1027
+ } catch {
1028
+ return {};
1029
+ }
1030
+ },
1031
+ settings,
1032
+ authStorage,
1033
+ modelRegistry,
1034
+ asyncJobManager,
1035
+ };
1036
+
1037
+ // Initialize internal URL router for internal protocols (agent://, artifact://, memory://, skill://, rule://, mcp://, local://)
1038
+ const internalRouter = new InternalUrlRouter();
1039
+ const getArtifactsDir = () => sessionManager.getArtifactsDir();
1040
+ internalRouter.register(new AgentProtocolHandler({ getArtifactsDir }));
1041
+ internalRouter.register(new ArtifactProtocolHandler({ getArtifactsDir }));
1042
+ internalRouter.register(
1043
+ new MemoryProtocolHandler({
1044
+ getMemoryRoot: () => getMemoryRoot(agentDir, settings.getCwd()),
1045
+ }),
1046
+ );
1047
+ internalRouter.register(
1048
+ new LocalProtocolHandler({
1049
+ getArtifactsDir,
1050
+ getSessionId: () => sessionManager.getSessionId(),
1051
+ }),
1052
+ );
1053
+ internalRouter.register(
1054
+ new SkillProtocolHandler({
1055
+ getSkills: () => skills,
1056
+ }),
1057
+ );
1058
+ internalRouter.register(
1059
+ new RuleProtocolHandler({
1060
+ getRules: () => [...rulebookRules, ...alwaysApplyRules],
1061
+ }),
1062
+ );
1063
+ internalRouter.register(
1064
+ new InternalDocsProtocolHandler({
1065
+ getContextStatus: () => {
1066
+ try {
1067
+ return contextServiceRef?.instance?.getStatus() ?? null;
1068
+ } catch {
1069
+ // ContextService.instance throws if not initialized; ignore.
1070
+ return null;
1071
+ }
1072
+ },
1073
+ }),
1074
+ );
1075
+ internalRouter.register(new JobsProtocolHandler({ getAsyncJobManager: () => asyncJobManager }));
1076
+ internalRouter.register(new McpProtocolHandler({ getMcpManager: () => mcpManager }));
1077
+ toolSession.internalRouter = internalRouter;
1078
+ toolSession.getArtifactsDir = getArtifactsDir;
1079
+ toolSession.agentOutputManager = new AgentOutputManager(
1080
+ getArtifactsDir,
1081
+ options.parentTaskPrefix ? { parentPrefix: options.parentTaskPrefix } : undefined,
1082
+ );
1083
+
1084
+ // Create built-in tools (already wrapped with meta notice formatting)
1085
+ const builtinTools = await logger.time("createAllTools", createTools, toolSession, options.toolNames);
1086
+
1087
+ // Discover MCP tools from .mcp.json files
1088
+ let mcpManager: MCPManager | undefined;
1089
+ const enableMCP = options.enableMCP ?? true;
1090
+ const customTools: CustomTool[] = [];
1091
+ if (enableMCP) {
1092
+ const mcpResult = await logger.time("discoverAndLoadMCPTools", discoverAndLoadMCPTools, cwd, {
1093
+ onConnecting: serverNames => {
1094
+ if (serverNames.length > 0) {
1095
+ logger.debug("Connecting to MCP servers", { servers: serverNames });
1096
+ }
1097
+ },
1098
+ enableProjectConfig: settings.get("mcp.enableProjectConfig") ?? true,
1099
+ // Always filter Exa - we have native integration
1100
+ filterExa: true,
1101
+ // Filter browser MCP servers when builtin browser tool is active
1102
+ filterBrowser: settings.get("browser.enabled") ?? false,
1103
+ cacheStorage: settings.getStorage(),
1104
+ authStorage,
1105
+ });
1106
+ mcpManager = mcpResult.manager;
1107
+ toolSession.mcpManager = mcpManager;
1108
+
1109
+ if (settings.get("mcp.notifications")) {
1110
+ mcpManager.setNotificationsEnabled(true);
1111
+ }
1112
+ // If we extracted Exa API keys from MCP configs and EXA_API_KEY isn't set, use the first one
1113
+ if (mcpResult.exaApiKeys.length > 0 && !$env.EXA_API_KEY) {
1114
+ Bun.env.EXA_API_KEY = mcpResult.exaApiKeys[0];
1115
+ }
1116
+
1117
+ // Log MCP errors
1118
+ for (const { path, error } of mcpResult.errors) {
1119
+ logger.error("MCP tool load failed", { path, error });
1120
+ }
1121
+
1122
+ if (mcpResult.tools.length > 0) {
1123
+ // MCP tools are LoadedCustomTool, extract the tool property
1124
+ customTools.push(...mcpResult.tools.map(loaded => loaded.tool));
1125
+ }
1126
+ }
1127
+
1128
+ // Add Gemini image tools if GEMINI_API_KEY (or GOOGLE_API_KEY) is available
1129
+ const geminiImageTools = await logger.time("getGeminiImageTools", getGeminiImageTools);
1130
+ if (geminiImageTools.length > 0) {
1131
+ customTools.push(...(geminiImageTools as unknown as CustomTool[]));
1132
+ }
1133
+
1134
+ // Add web search tools
1135
+ if (options.toolNames?.includes("web_search")) {
1136
+ customTools.push(...getSearchTools());
1137
+ }
1138
+
1139
+ // Discover and load custom tools from .omp/tools/, .xcsh/tools/, etc.
1140
+ const builtInToolNames = builtinTools.map(t => t.name);
1141
+ const discoveredCustomTools = await logger.time(
1142
+ "discoverAndLoadCustomTools",
1143
+ discoverAndLoadCustomTools,
1144
+ [],
1145
+ cwd,
1146
+ builtInToolNames,
1147
+ action => queueResolveHandler(toolSession, action),
1148
+ );
1149
+ for (const { path, error } of discoveredCustomTools.errors) {
1150
+ logger.error("Custom tool load failed", { path, error });
1151
+ }
1152
+ if (discoveredCustomTools.tools.length > 0) {
1153
+ customTools.push(...discoveredCustomTools.tools.map(loaded => loaded.tool));
1154
+ }
1155
+
1156
+ const inlineExtensions: ExtensionFactory[] = options.extensions ? [...options.extensions] : [];
1157
+ inlineExtensions.push(createAutoresearchExtension);
1158
+ if (customTools.length > 0) {
1159
+ inlineExtensions.push(createCustomToolsExtension(customTools));
1160
+ }
1161
+
1162
+ // Load extensions (discovers from standard locations + configured paths)
1163
+ let extensionsResult: LoadExtensionsResult;
1164
+ if (options.disableExtensionDiscovery) {
1165
+ const configuredPaths = options.additionalExtensionPaths ?? [];
1166
+ extensionsResult = await logger.time("loadExtensions", loadExtensions, configuredPaths, cwd, eventBus);
1167
+ for (const { path, error } of extensionsResult.errors) {
1168
+ logger.error("Failed to load extension", { path, error });
1169
+ }
1170
+ } else if (options.preloadedExtensions) {
1171
+ extensionsResult = options.preloadedExtensions;
1172
+ } else {
1173
+ // Merge CLI extension paths with settings extension paths
1174
+ const configuredPaths = [...(options.additionalExtensionPaths ?? []), ...(settings.get("extensions") ?? [])];
1175
+ const disabledExtensionIds = settings.get("disabledExtensions") ?? [];
1176
+ extensionsResult = await logger.time(
1177
+ "discoverAndLoadExtensions",
1178
+ discoverAndLoadExtensions,
1179
+ configuredPaths,
1180
+ cwd,
1181
+ eventBus,
1182
+ disabledExtensionIds,
1183
+ );
1184
+ for (const { path, error } of extensionsResult.errors) {
1185
+ logger.error("Failed to load extension", { path, error });
1186
+ }
1187
+ }
1188
+
1189
+ // Load inline extensions from factories
1190
+ if (inlineExtensions.length > 0) {
1191
+ for (let i = 0; i < inlineExtensions.length; i++) {
1192
+ const factory = inlineExtensions[i];
1193
+ const loaded = await loadExtensionFromFactory(
1194
+ factory,
1195
+ cwd,
1196
+ eventBus,
1197
+ extensionsResult.runtime,
1198
+ `<inline-${i}>`,
1199
+ );
1200
+ extensionsResult.extensions.push(loaded);
1201
+ }
1202
+ }
1203
+
1204
+ // Process provider registrations queued during extension loading.
1205
+ // This must happen before the runner is created so that models registered by
1206
+ // extensions are available for model selection on session resume / fallback.
1207
+ const activeExtensionSources = extensionsResult.extensions.map(extension => extension.path);
1208
+ modelRegistry.syncExtensionSources(activeExtensionSources);
1209
+ for (const sourceId of new Set(activeExtensionSources)) {
1210
+ modelRegistry.clearSourceRegistrations(sourceId);
1211
+ }
1212
+ if (extensionsResult.runtime.pendingProviderRegistrations.length > 0) {
1213
+ for (const { name, config, sourceId } of extensionsResult.runtime.pendingProviderRegistrations) {
1214
+ modelRegistry.registerProvider(name, config, sourceId);
1215
+ }
1216
+ extensionsResult.runtime.pendingProviderRegistrations = [];
1217
+ }
1218
+
1219
+ // Resolve deferred --model pattern now that extension models are registered.
1220
+ if (!model && options.modelPattern) {
1221
+ const availableModels = modelRegistry.getAll();
1222
+ const matchPreferences = {
1223
+ usageOrder: settings.getStorage()?.getModelUsageOrder(),
1224
+ };
1225
+ const { model: resolved } = parseModelPattern(options.modelPattern, availableModels, matchPreferences, {
1226
+ modelRegistry,
1227
+ });
1228
+ if (resolved) {
1229
+ model = resolved;
1230
+ modelFallbackMessage = undefined;
1231
+ } else {
1232
+ modelFallbackMessage = `Model "${options.modelPattern}" not found`;
1233
+ }
1234
+ }
1235
+
1236
+ // Fall back to first available model with a valid API key.
1237
+ // Skip fallback if the user explicitly requested a model via --model that wasn't found.
1238
+ if (!model && !options.modelPattern) {
1239
+ const allModels = modelRegistry.getAll();
1240
+ for (const candidate of allModels) {
1241
+ if (await hasModelApiKey(candidate)) {
1242
+ model = candidate;
1243
+ break;
1244
+ }
1245
+ }
1246
+ if (model) {
1247
+ if (modelFallbackMessage) {
1248
+ modelFallbackMessage += `. Using ${model.provider}/${model.id}`;
1249
+ }
1250
+ } else {
1251
+ modelFallbackMessage =
1252
+ "No models available. Use /login or set an API key environment variable. Then use /model to select a model.";
1253
+ }
1254
+ }
1255
+
1256
+ // Discover custom commands (TypeScript slash commands)
1257
+ const customCommandsResult: CustomCommandsLoadResult = options.disableExtensionDiscovery
1258
+ ? { commands: [], errors: [] }
1259
+ : await logger.time("discoverCustomCommands", loadCustomCommandsInternal, { cwd, agentDir });
1260
+ if (!options.disableExtensionDiscovery) {
1261
+ for (const { path, error } of customCommandsResult.errors) {
1262
+ logger.error("Failed to load custom command", { path, error });
1263
+ }
1264
+ }
1265
+
1266
+ let extensionRunner: ExtensionRunner | undefined;
1267
+ if (extensionsResult.extensions.length > 0) {
1268
+ extensionRunner = new ExtensionRunner(
1269
+ extensionsResult.extensions,
1270
+ extensionsResult.runtime,
1271
+ cwd,
1272
+ sessionManager,
1273
+ modelRegistry,
1274
+ );
1275
+ }
1276
+
1277
+ const getSessionContext = () => ({
1278
+ sessionManager,
1279
+ modelRegistry,
1280
+ model: agent.state.model,
1281
+ isIdle: () => !session.isStreaming,
1282
+ hasQueuedMessages: () => session.queuedMessageCount > 0,
1283
+ abort: () => {
1284
+ session.abort();
1285
+ },
1286
+ settings,
1287
+ });
1288
+ const toolContextStore = new ToolContextStore(getSessionContext);
1289
+
1290
+ const registeredTools = extensionRunner?.getAllRegisteredTools() ?? [];
1291
+ let wrappedExtensionTools: Tool[];
1292
+
1293
+ if (extensionRunner) {
1294
+ // With extension runner: convert CustomTools to ToolDefinitions and wrap all together
1295
+ const allCustomTools = [
1296
+ ...registeredTools,
1297
+ ...(options.customTools?.map(tool => {
1298
+ const definition = isCustomTool(tool) ? customToolToDefinition(tool) : tool;
1299
+ return { definition, extensionPath: "<sdk>" };
1300
+ }) ?? []),
1301
+ ];
1302
+ wrappedExtensionTools = wrapRegisteredTools(allCustomTools, extensionRunner);
1303
+ } else {
1304
+ // Without extension runner: wrap CustomTools directly with CustomToolAdapter
1305
+ // ToolDefinition items require ExtensionContext and cannot be used without a runner
1306
+ const customToolContext = (): CustomToolContext => ({
1307
+ sessionManager,
1308
+ modelRegistry,
1309
+ model: agent?.state.model,
1310
+ isIdle: () => !session?.isStreaming,
1311
+ hasQueuedMessages: () => (session?.queuedMessageCount ?? 0) > 0,
1312
+ abort: () => session?.abort(),
1313
+ settings,
1314
+ });
1315
+ wrappedExtensionTools = (options.customTools ?? [])
1316
+ .filter(isCustomTool)
1317
+ .map(tool => CustomToolAdapter.wrap(tool, customToolContext));
1318
+ }
1319
+
1320
+ // All built-in tools are active (conditional tools like git/ask return null from factory if disabled)
1321
+ const toolRegistry = new Map<string, Tool>();
1322
+ for (const tool of builtinTools) {
1323
+ toolRegistry.set(tool.name, tool);
1324
+ }
1325
+ for (const tool of wrappedExtensionTools) {
1326
+ toolRegistry.set(tool.name, tool);
1327
+ }
1328
+ if (extensionRunner) {
1329
+ for (const tool of toolRegistry.values()) {
1330
+ toolRegistry.set(tool.name, new ExtensionToolWrapper(tool, extensionRunner));
1331
+ }
1332
+ }
1333
+ if (model?.provider === "cursor") {
1334
+ toolRegistry.delete("edit");
1335
+ }
1336
+
1337
+ const hasDeferrableTools = Array.from(toolRegistry.values()).some(tool => tool.deferrable === true);
1338
+ if (!hasDeferrableTools) {
1339
+ toolRegistry.delete("resolve");
1340
+ } else if (!toolRegistry.has("resolve")) {
1341
+ const resolveTool = await logger.time("createTools:resolve:session", HIDDEN_TOOLS.resolve, toolSession);
1342
+ if (resolveTool) {
1343
+ toolRegistry.set(resolveTool.name, wrapToolWithMetaNotice(resolveTool));
1344
+ }
1345
+ }
1346
+
1347
+ let cursorEventEmitter: ((event: AgentEvent) => void) | undefined;
1348
+ const cursorExecHandlers = new CursorExecHandlers({
1349
+ cwd,
1350
+ tools: toolRegistry,
1351
+ getToolContext: () => toolContextStore.getContext(),
1352
+ emitEvent: event => cursorEventEmitter?.(event),
1353
+ });
1354
+
1355
+ const repeatToolDescriptions = settings.get("repeatToolDescriptions");
1356
+ const eagerTasks = settings.get("task.eager");
1357
+ const intentField = settings.get("tools.intentTracing") || $flag("PI_INTENT_TRACING") ? INTENT_FIELD : undefined;
1358
+ const rebuildSystemPrompt = async (toolNames: string[], tools: Map<string, AgentTool>): Promise<string> => {
1359
+ toolContextStore.setToolNames(toolNames);
1360
+ const discoverableMCPTools = mcpDiscoveryEnabled ? collectDiscoverableMCPTools(tools.values()) : [];
1361
+ const discoverableMCPSummary = summarizeDiscoverableMCPTools(discoverableMCPTools);
1362
+ const hasDiscoverableMCPTools =
1363
+ mcpDiscoveryEnabled && toolNames.includes("search_tool_bm25") && discoverableMCPTools.length > 0;
1364
+ const promptTools = buildSystemPromptToolMetadata(tools, {
1365
+ search_tool_bm25: { description: renderSearchToolBm25Description(discoverableMCPTools) },
1366
+ });
1367
+ const memoryInstructions = await buildMemoryToolDeveloperInstructions(agentDir, settings);
1368
+
1369
+ // Resolve F5 XC context for the prompt. Read fresh each rebuild so tool-triggered
1370
+ // rebuilds reflect the most recent /context activate. Mid-session context changes without a
1371
+ // tool change are handled via custom_message injection in the onContextChange listener.
1372
+ let contextForPrompt:
1373
+ | {
1374
+ tenant: string;
1375
+ namespace: string;
1376
+ credentialSource: string;
1377
+ authStatus: string;
1378
+ apiUrl?: string;
1379
+ envVars?: Record<string, string>;
1380
+ }
1381
+ | undefined;
1382
+ try {
1383
+ const status = contextServiceRef?.instance?.getStatus();
1384
+ // The LLM needs to anchor on tenant + namespace regardless of whether credentials
1385
+ // come from a named context or from XCSH_API_URL/XCSH_API_TOKEN env vars. For the
1386
+ // env-only path, activeContextName is null but activeContextTenant (derived from
1387
+ // XCSH_API_URL) is still set and credentialSource is "environment". Guard on
1388
+ // tenant, not name, so env-backed deployments also get the prompt anchor.
1389
+ if (status?.isConfigured && status.activeContextTenant) {
1390
+ contextForPrompt = {
1391
+ tenant: status.activeContextTenant,
1392
+ namespace: status.activeContextNamespace ?? "default",
1393
+ credentialSource: status.credentialSource,
1394
+ authStatus: status.authStatus,
1395
+ apiUrl: status.activeContextUrl ?? undefined,
1396
+ };
1397
+ const sensitiveKeys = contextServiceRef?.instance?.getActiveSensitiveKeys();
1398
+ const ctxEnv = createContextEnv(settings, sensitiveKeys?.size ? { sensitiveKeys } : undefined);
1399
+ const envVars = ctxEnv.getNonSensitiveVars();
1400
+ if (Object.keys(envVars).length > 0) {
1401
+ contextForPrompt.envVars = envVars;
1402
+ }
1403
+ }
1404
+ } catch {
1405
+ // ContextService not available or not initialized — leave contextForPrompt undefined.
1406
+ }
1407
+
1408
+ let knowledgeTopics: string | undefined;
1409
+ try {
1410
+ if (knowledgeServiceRef) {
1411
+ const svc = knowledgeServiceRef.instance;
1412
+ let cached = svc.getIndex();
1413
+ if (!cached) {
1414
+ await svc.getOrRefreshIndex();
1415
+ cached = svc.getIndex();
1416
+ } else {
1417
+ void svc.getOrRefreshIndex();
1418
+ }
1419
+ if (cached && cached.products.length > 0) {
1420
+ knowledgeTopics = cached.products
1421
+ .map(p => p.name)
1422
+ .sort()
1423
+ .join(", ");
1424
+ }
1425
+ }
1426
+ } catch {
1427
+ // KnowledgeService not available — leave undefined.
1428
+ }
1429
+
1430
+ let contextSkillDirs: string[] | undefined;
1431
+ let contextIncludeSkills: string[] | undefined;
1432
+ let contextExcludeSkills: string[] | undefined;
1433
+ try {
1434
+ if (contextServiceRef?.instance) {
1435
+ const skillConfig = contextServiceRef.instance.getActiveContextSkillConfig();
1436
+ if (skillConfig.skillDirs.length > 0) contextSkillDirs = skillConfig.skillDirs;
1437
+ if (skillConfig.includeSkills.length > 0) contextIncludeSkills = skillConfig.includeSkills;
1438
+ if (skillConfig.excludeSkills.length > 0) contextExcludeSkills = skillConfig.excludeSkills;
1439
+ }
1440
+ } catch {
1441
+ // ContextService not available — leave undefined.
1442
+ }
1443
+
1444
+ // Build combined append prompt: memory instructions + MCP server instructions
1445
+ const serverInstructions = mcpManager?.getServerInstructions();
1446
+ let appendPrompt: string | undefined = memoryInstructions ?? undefined;
1447
+ if (serverInstructions && serverInstructions.size > 0) {
1448
+ const MAX_INSTRUCTIONS_LENGTH = 4000;
1449
+ const parts: string[] = [];
1450
+ if (appendPrompt) parts.push(appendPrompt);
1451
+ parts.push(
1452
+ "## MCP Server Instructions\n\nThe following instructions are provided by connected MCP servers. They are server-controlled and may not be verified.",
1453
+ );
1454
+ for (const [srvName, srvInstructions] of serverInstructions) {
1455
+ const truncated =
1456
+ srvInstructions.length > MAX_INSTRUCTIONS_LENGTH
1457
+ ? `${srvInstructions.slice(0, MAX_INSTRUCTIONS_LENGTH)}\n[truncated]`
1458
+ : srvInstructions;
1459
+ parts.push(`### ${srvName}\n${truncated}`);
1460
+ }
1461
+ appendPrompt = parts.join("\n\n");
1462
+ }
1463
+ // Load user profile — used for system prompt hint
1464
+ let _profile: UserProfile;
1465
+ try {
1466
+ _profile = await loadProfile();
1467
+ } catch {
1468
+ _profile = {};
1469
+ }
1470
+ let userProfile: { name: string; role: string; org: string } | undefined;
1471
+ if (_profile.givenName || _profile.familyName) {
1472
+ const _name = [_profile.givenName, _profile.familyName].filter(Boolean).join(" ");
1473
+ if (_name) {
1474
+ userProfile = {
1475
+ name: _name,
1476
+ role: _profile.role ?? _profile.jobTitle ?? "",
1477
+ org:
1478
+ typeof _profile.worksFor === "string"
1479
+ ? _profile.worksFor
1480
+ : ((_profile.worksFor as { name?: string } | undefined)?.name ?? ""),
1481
+ };
1482
+ }
1483
+ }
1484
+
1485
+ // Load compact computer profile hint for system prompt
1486
+ let computerProfile:
1487
+ | {
1488
+ ramGB: number;
1489
+ cpu: string;
1490
+ os: string;
1491
+ cores?: number;
1492
+ shell?: string;
1493
+ diskFree?: string;
1494
+ model?: string;
1495
+ }
1496
+ | undefined;
1497
+ try {
1498
+ const _computerProfile = await loadComputerProfile();
1499
+ computerProfile = buildComputerHint(_computerProfile) ?? undefined;
1500
+ } catch {
1501
+ // No computer profile — hint block omitted
1502
+ }
1503
+
1504
+ const currentLocale = getLocale();
1505
+ const localeName = currentLocale !== "en" ? getLocaleDisplayName(currentLocale) : undefined;
1506
+ const localeForPrompt = localeName ? { code: currentLocale, name: localeName } : undefined;
1507
+
1508
+ const defaultPrompt = await buildSystemPromptInternal({
1509
+ cwd,
1510
+ skills,
1511
+ contextFiles,
1512
+ tools: promptTools,
1513
+ toolNames,
1514
+ rules: rulebookRules,
1515
+ alwaysApplyRules,
1516
+ skillsSettings: settings.getGroup("skills"),
1517
+ appendSystemPrompt: appendPrompt,
1518
+ repeatToolDescriptions,
1519
+ intentField,
1520
+ mcpDiscoveryMode: hasDiscoverableMCPTools,
1521
+ mcpDiscoveryServerSummaries: discoverableMCPSummary.servers.map(formatDiscoverableMCPToolServerSummary),
1522
+ eagerTasks,
1523
+ secretsEnabled,
1524
+ context: contextForPrompt,
1525
+ locale: localeForPrompt,
1526
+ userProfile,
1527
+ computerProfile,
1528
+ knowledgeTopics,
1529
+ contextSkillDirs,
1530
+ contextIncludeSkills,
1531
+ contextExcludeSkills,
1532
+ });
1533
+
1534
+ if (options.systemPrompt === undefined) {
1535
+ return defaultPrompt;
1536
+ }
1537
+ if (typeof options.systemPrompt === "string") {
1538
+ return await buildSystemPromptInternal({
1539
+ cwd,
1540
+ skills,
1541
+ contextFiles,
1542
+ tools: promptTools,
1543
+ toolNames,
1544
+ rules: rulebookRules,
1545
+ alwaysApplyRules,
1546
+ skillsSettings: settings.getGroup("skills"),
1547
+ customPrompt: options.systemPrompt,
1548
+ appendSystemPrompt: appendPrompt,
1549
+ repeatToolDescriptions,
1550
+ intentField,
1551
+ mcpDiscoveryMode: hasDiscoverableMCPTools,
1552
+ mcpDiscoveryServerSummaries: discoverableMCPSummary.servers.map(formatDiscoverableMCPToolServerSummary),
1553
+ eagerTasks,
1554
+ secretsEnabled,
1555
+ context: contextForPrompt,
1556
+ locale: localeForPrompt,
1557
+ userProfile,
1558
+ computerProfile,
1559
+ knowledgeTopics,
1560
+ contextSkillDirs,
1561
+ contextIncludeSkills,
1562
+ contextExcludeSkills,
1563
+ });
1564
+ }
1565
+ return options.systemPrompt(defaultPrompt);
1566
+ };
1567
+
1568
+ const toolNamesFromRegistry = Array.from(toolRegistry.keys());
1569
+ const requestedToolNames =
1570
+ (options.toolNames ? [...new Set(options.toolNames.map(name => name.toLowerCase()))] : undefined) ??
1571
+ toolNamesFromRegistry;
1572
+ const normalizedRequested = requestedToolNames.filter(name => toolRegistry.has(name));
1573
+ const includeExitPlanMode = requestedToolNames.includes("exit_plan_mode");
1574
+ const mcpDiscoveryEnabled = settings.get("mcp.discoveryMode") ?? false;
1575
+ const defaultInactiveToolNames = new Set(
1576
+ registeredTools.filter(tool => tool.definition.defaultInactive).map(tool => tool.definition.name),
1577
+ );
1578
+ const requestedActiveToolNames = includeExitPlanMode
1579
+ ? normalizedRequested
1580
+ : normalizedRequested.filter(name => name !== "exit_plan_mode");
1581
+ const initialRequestedActiveToolNames = options.toolNames
1582
+ ? requestedActiveToolNames
1583
+ : requestedActiveToolNames.filter(name => !defaultInactiveToolNames.has(name));
1584
+ const explicitlyRequestedMCPToolNames = options.toolNames
1585
+ ? requestedActiveToolNames.filter(name => name.startsWith("mcp_"))
1586
+ : [];
1587
+ const discoveryDefaultServers = new Set(
1588
+ (settings.get("mcp.discoveryDefaultServers") ?? []).map(serverName => serverName.trim()).filter(Boolean),
1589
+ );
1590
+ const discoveryDefaultServerToolNames = mcpDiscoveryEnabled
1591
+ ? selectDiscoverableMCPToolNamesByServer(
1592
+ collectDiscoverableMCPTools(toolRegistry.values()),
1593
+ discoveryDefaultServers,
1594
+ )
1595
+ : [];
1596
+ let initialSelectedMCPToolNames: string[] = [];
1597
+ let defaultSelectedMCPToolNames: string[] = [];
1598
+ let initialToolNames = [...initialRequestedActiveToolNames];
1599
+ if (mcpDiscoveryEnabled) {
1600
+ const restoredSelectedMCPToolNames = existingSession.selectedMCPToolNames.filter(name =>
1601
+ toolRegistry.has(name),
1602
+ );
1603
+ defaultSelectedMCPToolNames = [
1604
+ ...new Set([...discoveryDefaultServerToolNames, ...explicitlyRequestedMCPToolNames]),
1605
+ ];
1606
+ initialSelectedMCPToolNames = existingSession.hasPersistedMCPToolSelection
1607
+ ? restoredSelectedMCPToolNames
1608
+ : [...new Set([...restoredSelectedMCPToolNames, ...defaultSelectedMCPToolNames])];
1609
+ initialToolNames = [
1610
+ ...new Set([
1611
+ ...initialRequestedActiveToolNames.filter(name => !name.startsWith("mcp_")),
1612
+ ...initialSelectedMCPToolNames,
1613
+ ]),
1614
+ ];
1615
+ }
1616
+
1617
+ // Custom tools and extension-registered tools are always included regardless of toolNames filter
1618
+ const alwaysInclude: string[] = [
1619
+ ...(options.customTools?.map(t => (isCustomTool(t) ? t.name : t.name)) ?? []),
1620
+ ...registeredTools.filter(t => !t.definition.defaultInactive).map(t => t.definition.name),
1621
+ ];
1622
+ for (const name of alwaysInclude) {
1623
+ if (mcpDiscoveryEnabled && name.startsWith("mcp_")) {
1624
+ continue;
1625
+ }
1626
+ if (toolRegistry.has(name) && !initialToolNames.includes(name)) {
1627
+ initialToolNames.push(name);
1628
+ }
1629
+ }
1630
+
1631
+ const systemPrompt = await logger.time("buildSystemPrompt", rebuildSystemPrompt, initialToolNames, toolRegistry);
1632
+
1633
+ const promptTemplates =
1634
+ options.promptTemplates ??
1635
+ (await logger.time("discoverPromptTemplates", discoverPromptTemplates, cwd, agentDir));
1636
+ toolSession.promptTemplates = promptTemplates;
1637
+
1638
+ const slashCommands =
1639
+ options.slashCommands ?? (await logger.time("discoverSlashCommands", discoverSlashCommands, cwd));
1640
+
1641
+ // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)
1642
+ const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {
1643
+ const converted = convertToLlm(messages);
1644
+ // Check setting dynamically so mid-session changes take effect
1645
+ if (!settings.get("images.blockImages")) {
1646
+ return converted;
1647
+ }
1648
+ // Filter out ImageContent from all messages, replacing with text placeholder
1649
+ return converted.map(msg => {
1650
+ if (msg.role === "user" || msg.role === "toolResult") {
1651
+ const content = msg.content;
1652
+ if (Array.isArray(content)) {
1653
+ const hasImages = content.some(c => c.type === "image");
1654
+ if (hasImages) {
1655
+ const filteredContent = content
1656
+ .map(c =>
1657
+ c.type === "image" ? { type: "text" as const, text: "Image reading is disabled." } : c,
1658
+ )
1659
+ .filter((c, i, arr) => {
1660
+ // Dedupe consecutive "Image reading is disabled." texts
1661
+ if (!(c.type === "text" && c.text === "Image reading is disabled." && i > 0)) return true;
1662
+ const prev = arr[i - 1];
1663
+ return !(prev.type === "text" && prev.text === "Image reading is disabled.");
1664
+ });
1665
+ return { ...msg, content: filteredContent };
1666
+ }
1667
+ }
1668
+ }
1669
+ return msg;
1670
+ });
1671
+ };
1672
+
1673
+ // Final convertToLlm: chain block-images filter with secret obfuscation
1674
+ const convertToLlmFinal = (messages: AgentMessage[]): Message[] => {
1675
+ const converted = convertToLlmWithBlockImages(messages);
1676
+ if (!obfuscator?.hasSecrets()) return converted;
1677
+ return obfuscateMessages(obfuscator, converted);
1678
+ };
1679
+ const transformContext = extensionRunner
1680
+ ? async (messages: AgentMessage[], _signal?: AbortSignal) => {
1681
+ return await extensionRunner.emitContext(messages);
1682
+ }
1683
+ : undefined;
1684
+ const onPayload = extensionRunner
1685
+ ? async (payload: unknown, _model?: Model) => {
1686
+ return await extensionRunner.emitBeforeProviderRequest(payload);
1687
+ }
1688
+ : undefined;
1689
+
1690
+ const setToolUIContext = (uiContext: ExtensionUIContext, hasUI: boolean) => {
1691
+ toolContextStore.setUIContext(uiContext, hasUI);
1692
+ };
1693
+
1694
+ const initialTools = initialToolNames
1695
+ .map(name => toolRegistry.get(name))
1696
+ .filter((tool): tool is AgentTool => tool !== undefined);
1697
+
1698
+ const openaiWebsocketSetting = settings.get("providers.openaiWebsockets") ?? "off";
1699
+ const preferOpenAICodexWebsockets =
1700
+ openaiWebsocketSetting === "on" ? true : openaiWebsocketSetting === "off" ? false : undefined;
1701
+ const serviceTierSetting = settings.get("serviceTier");
1702
+
1703
+ const initialServiceTier = hasServiceTierEntry
1704
+ ? existingSession.serviceTier
1705
+ : serviceTierSetting === "none"
1706
+ ? undefined
1707
+ : serviceTierSetting;
1708
+
1709
+ agent = new Agent({
1710
+ initialState: {
1711
+ systemPrompt,
1712
+ model,
1713
+ thinkingLevel: toReasoningEffort(thinkingLevel),
1714
+ tools: initialTools,
1715
+ },
1716
+ convertToLlm: convertToLlmFinal,
1717
+ onPayload,
1718
+ sessionId: providerSessionId,
1719
+ transformContext,
1720
+ steeringMode: settings.get("steeringMode") ?? "one-at-a-time",
1721
+ followUpMode: settings.get("followUpMode") ?? "one-at-a-time",
1722
+ interruptMode: settings.get("interruptMode") ?? "wait",
1723
+ thinkingBudgets: settings.getGroup("thinkingBudgets"),
1724
+ temperature: settings.get("temperature") >= 0 ? settings.get("temperature") : undefined,
1725
+ topP: settings.get("topP") >= 0 ? settings.get("topP") : undefined,
1726
+ topK: settings.get("topK") >= 0 ? settings.get("topK") : undefined,
1727
+ minP: settings.get("minP") >= 0 ? settings.get("minP") : undefined,
1728
+ presencePenalty: settings.get("presencePenalty") >= 0 ? settings.get("presencePenalty") : undefined,
1729
+ repetitionPenalty: settings.get("repetitionPenalty") >= 0 ? settings.get("repetitionPenalty") : undefined,
1730
+ serviceTier: initialServiceTier,
1731
+ kimiApiFormat: settings.get("providers.kimiApiFormat") ?? "anthropic",
1732
+ preferWebsockets: preferOpenAICodexWebsockets,
1733
+ getToolContext: tc => toolContextStore.getContext(tc),
1734
+ getApiKey: async provider => {
1735
+ // Use the provider-facing session id for sticky credential selection so cache keys
1736
+ // and provider auth affinity stay aligned across fresh benchmark sessions.
1737
+ const key = await modelRegistry.getApiKeyForProvider(provider, providerSessionId);
1738
+ if (!key) {
1739
+ throw new Error(`No API key found for provider "${provider}"`);
1740
+ }
1741
+ return key;
1742
+ },
1743
+ cursorExecHandlers,
1744
+ transformToolCallArguments: (args, _toolName) => {
1745
+ let result = args;
1746
+ const maxTimeout = settings.get("tools.maxTimeout");
1747
+ if (maxTimeout > 0 && typeof result.timeout === "number") {
1748
+ result = { ...result, timeout: Math.min(result.timeout, maxTimeout) };
1749
+ }
1750
+ if (obfuscator?.hasSecrets()) {
1751
+ result = obfuscator.deobfuscateObject(result);
1752
+ }
1753
+ return result;
1754
+ },
1755
+ intentTracing: !!intentField,
1756
+ getToolChoice: () => session?.nextToolChoice(),
1757
+ });
1758
+
1759
+ cursorEventEmitter = event => agent.emitExternalEvent(event);
1760
+
1761
+ // Restore messages if session has existing data
1762
+ if (hasExistingSession) {
1763
+ agent.replaceMessages(existingSession.messages);
1764
+ } else {
1765
+ // Save initial model and thinking level for new sessions so they can be restored on resume
1766
+ if (model) {
1767
+ sessionManager.appendModelChange(`${model.provider}/${model.id}`);
1768
+ }
1769
+ sessionManager.appendThinkingLevelChange(thinkingLevel);
1770
+ // Save active context (if any) so resumed sessions know their platform context.
1771
+ try {
1772
+ const { ContextService } = await import("./services/xcsh-context");
1773
+ const status = ContextService.instance?.getStatus();
1774
+ if (status?.isConfigured && status.activeContextName && status.activeContextTenant) {
1775
+ sessionManager.appendContextChange(
1776
+ status.activeContextName,
1777
+ status.activeContextTenant,
1778
+ status.activeContextNamespace ?? "default",
1779
+ );
1780
+ }
1781
+ } catch {
1782
+ // ContextService not available (SDK consumers, tests). Skip.
1783
+ }
1784
+ }
1785
+
1786
+ session = new AgentSession({
1787
+ agent,
1788
+ thinkingLevel,
1789
+ sessionManager,
1790
+ settings,
1791
+ pythonKernelOwnerId,
1792
+ scopedModels: options.scopedModels,
1793
+ promptTemplates,
1794
+ slashCommands,
1795
+ extensionRunner,
1796
+ customCommands: customCommandsResult.commands,
1797
+ skills,
1798
+ skillWarnings,
1799
+ skillsSettings: settings.getGroup("skills"),
1800
+ modelRegistry,
1801
+ toolRegistry,
1802
+ transformContext,
1803
+ onPayload,
1804
+ convertToLlm: convertToLlmFinal,
1805
+ rebuildSystemPrompt,
1806
+ mcpDiscoveryEnabled,
1807
+ initialSelectedMCPToolNames,
1808
+ defaultSelectedMCPToolNames,
1809
+ persistInitialMCPToolSelection: !hasExistingSession,
1810
+ defaultSelectedMCPServerNames: [...discoveryDefaultServers],
1811
+ ttsrManager,
1812
+ obfuscator,
1813
+ asyncJobManager,
1814
+ });
1815
+ hasSession = true;
1816
+
1817
+ // Register the context-change listener now that the session exists. Registering here
1818
+ // (atomically with addDisposeHook below) means a failed createAgentSession leaves no
1819
+ // leaked listener, and the listener closes over a fully-constructed session.
1820
+ //
1821
+ // The listener fires on every #applyToSettings call in ContextService (activate,
1822
+ // setNamespace, setEnvVars, unsetEnvVars, loadActive). It serves two roles:
1823
+ // 1. Refresh the secret obfuscator when context env/token changes.
1824
+ // 2. Notify the LLM when context the prompt block mirrors (name OR namespace)
1825
+ // changes — settings-only events (env var mutations) do not emit the notice.
1826
+ //
1827
+ // Wrapped in try so that tests and SDK consumers that never initialize ContextService
1828
+ // don't trip on .instance throwing. In those paths we skip listener registration
1829
+ // entirely — there's no context to track.
1830
+ try {
1831
+ if (!contextServiceRef) throw new Error("no ContextService");
1832
+ const service = contextServiceRef;
1833
+
1834
+ // Track both name and namespace — they both appear in the system-prompt anchor block
1835
+ // and both affect F5 XC operation targeting, so either changing is an LLM-visible event.
1836
+ // Seed from current state so re-activating the same context, or firing for env-only
1837
+ // mutations, does not produce a spurious "Context switched" directive.
1838
+ const seedStatus = service.instance.getStatus();
1839
+ let lastEmittedContext: { name: string; namespace: string } | undefined =
1840
+ seedStatus.activeContextName && seedStatus.activeContextTenant
1841
+ ? {
1842
+ name: seedStatus.activeContextName,
1843
+ namespace: seedStatus.activeContextNamespace ?? "default",
1844
+ }
1845
+ : undefined;
1846
+
1847
+ const listener: (ctx: import("./services/xcsh-context").XCSHContext) => void = ctx => {
1848
+ // Role 1: obfuscator refresh on credential change.
1849
+ const newValues: string[] = [ctx.apiToken];
1850
+ if (ctx.sensitiveKeys && ctx.env) {
1851
+ for (const key of ctx.sensitiveKeys) {
1852
+ const v = ctx.env[key];
1853
+ if (v) newValues.push(v);
1854
+ }
1855
+ }
1856
+ const bashEnv = (settings.get("bash.environment") ?? {}) as Record<string, string>;
1857
+ for (const [name, value] of Object.entries(bashEnv)) {
1858
+ if (value && SECRET_ENV_PATTERNS.test(name)) newValues.push(value);
1859
+ }
1860
+ if (obfuscator) {
1861
+ obfuscator.addPlainSecrets(newValues);
1862
+ } else {
1863
+ // Obfuscator was undefined at session start (no secrets detected).
1864
+ // Create one now so late context activations are still masked.
1865
+ const entries: SecretEntry[] = newValues
1866
+ .filter(v => v.length > 0)
1867
+ .map(v => ({ type: "plain" as const, content: v, mode: "obfuscate" as const }));
1868
+ if (entries.length > 0) {
1869
+ obfuscator = new SecretObfuscator(entries);
1870
+ }
1871
+ }
1872
+
1873
+ // Role 2: notify the LLM when name OR namespace changes. Read
1874
+ // ContextService.getStatus() (not the callback arg) to honor the XCSH_NAMESPACE
1875
+ // env override consistently with session-start emission.
1876
+ try {
1877
+ const currentStatus = service.instance?.getStatus();
1878
+ const currentName = currentStatus?.activeContextName ?? undefined;
1879
+ const currentTenant = currentStatus?.activeContextTenant ?? undefined;
1880
+ if (!currentName || !currentTenant) return;
1881
+ const currentNamespace = currentStatus.activeContextNamespace ?? "default";
1882
+
1883
+ const changed =
1884
+ !lastEmittedContext ||
1885
+ currentName !== lastEmittedContext.name ||
1886
+ currentNamespace !== lastEmittedContext.namespace;
1887
+ if (!changed) return;
1888
+
1889
+ // Append the context_change entry for replay/resume state reconstruction.
1890
+ sessionManager.appendContextChange(currentName, currentTenant, currentNamespace);
1891
+
1892
+ // Push a custom_message into BOTH agent.state.messages AND the session log so
1893
+ // the LLM sees the directive on its next turn. sendCustomMessage handles
1894
+ // streaming vs non-streaming correctly (queues via steer/followUp/nextTurn
1895
+ // when a turn is in flight).
1896
+ const nameChanged = !lastEmittedContext || currentName !== lastEmittedContext.name;
1897
+ const prefix = nameChanged
1898
+ ? `[Context switched to ${currentName}]`
1899
+ : `[F5 XC namespace changed to ${currentNamespace}]`;
1900
+ void session.sendCustomMessage({
1901
+ customType: "context_change_notice",
1902
+ content:
1903
+ `${prefix} Tenant: ${currentTenant}, ` +
1904
+ `namespace: ${currentNamespace}. Target this tenant and namespace ` +
1905
+ `for subsequent F5 XC operations.`,
1906
+ display: true,
1907
+ attribution: "agent",
1908
+ });
1909
+ lastEmittedContext = { name: currentName, namespace: currentNamespace };
1910
+ void session.refreshBaseSystemPrompt();
1911
+ // Role 3: background-validate credentials after context switch so the
1912
+ // LLM knows whether the new context is usable. Fire-and-forget — do
1913
+ // not block the context change notification.
1914
+ void (async () => {
1915
+ try {
1916
+ const { status: authStatus, latencyMs } = await service.instance.validateToken({
1917
+ timeoutMs: 5000,
1918
+ });
1919
+ const qualifier =
1920
+ authStatus === "connected"
1921
+ ? `connected${latencyMs ? ` (${latencyMs}ms)` : ""}`
1922
+ : authStatus === "auth_error"
1923
+ ? "credential error -- token may be invalid or expired"
1924
+ : "unreachable -- network error or tenant offline";
1925
+ void session.sendCustomMessage({
1926
+ customType: "context_validation_result",
1927
+ content: `[Auth status: ${authStatus}] Credentials for ${currentTenant}: ${qualifier}.`,
1928
+ display: true,
1929
+ attribution: "agent",
1930
+ });
1931
+ } catch {
1932
+ // Validation failed (e.g., no credentials configured) -- skip silently.
1933
+ }
1934
+ })();
1935
+ } catch {
1936
+ // ContextService.instance throws if not initialized; skip.
1937
+ }
1938
+ };
1939
+
1940
+ service.onContextChange(listener);
1941
+ session.addDisposeHook(() => service.offContextChange(listener));
1942
+ const authListener = (prev: string, current: string) => {
1943
+ if (prev === current) return;
1944
+ const isDegradation = current === "auth_error" || current === "offline";
1945
+ if (!isDegradation && prev === "unknown") return;
1946
+ const content = isDegradation
1947
+ ? `[Auth status: ${prev} → ${current}] F5 XC credentials may be stale. Run /context validate to check.`
1948
+ : `[Auth status: ${current}] F5 XC credentials are valid again.`;
1949
+ void session.sendCustomMessage({
1950
+ customType: "auth_status_change",
1951
+ content,
1952
+ display: true,
1953
+ attribution: "agent",
1954
+ });
1955
+ };
1956
+ service.onAuthStatusChange(authListener);
1957
+ session.addDisposeHook(() => service.offAuthStatusChange(authListener));
1958
+ const tokenHealthListener = (_prev: string, current: string) => {
1959
+ if (current === "expiring") {
1960
+ void session.sendCustomMessage({
1961
+ customType: "token_health_change",
1962
+ content: "[Token expiring] F5 XC API token expires soon. Run /context create to rotate.",
1963
+ display: true,
1964
+ attribution: "agent",
1965
+ });
1966
+ } else if (current === "expired") {
1967
+ void session.sendCustomMessage({
1968
+ customType: "token_health_change",
1969
+ content: "[Token expired] F5 XC API token has expired. Run /context create to replace it.",
1970
+ display: true,
1971
+ attribution: "agent",
1972
+ });
1973
+ }
1974
+ };
1975
+ service.onTokenHealthChange(tokenHealthListener);
1976
+ session.addDisposeHook(() => service.offTokenHealthChange(tokenHealthListener));
1977
+ } catch {
1978
+ // ContextService not initialized — skip listener registration entirely.
1979
+ // Tests and SDK consumers that don't use contexts won't reach this branch.
1980
+ }
1981
+
1982
+ if (model?.api === "openai-codex-responses") {
1983
+ const codexModel = model;
1984
+ const codexTransport = getOpenAICodexTransportDetails(codexModel, {
1985
+ sessionId: providerSessionId,
1986
+ baseUrl: codexModel.baseUrl,
1987
+ preferWebsockets: preferOpenAICodexWebsockets,
1988
+ providerSessionState: session.providerSessionState,
1989
+ });
1990
+ if (codexTransport.websocketPreferred) {
1991
+ void (async () => {
1992
+ try {
1993
+ const codexPrewarmApiKey = await modelRegistry.getApiKey(codexModel, providerSessionId);
1994
+ if (!codexPrewarmApiKey) return;
1995
+ await logger.time("prewarmOpenAICodexResponses", prewarmOpenAICodexResponses, codexModel, {
1996
+ apiKey: codexPrewarmApiKey,
1997
+ sessionId: providerSessionId,
1998
+ preferWebsockets: preferOpenAICodexWebsockets,
1999
+ providerSessionState: session.providerSessionState,
2000
+ });
2001
+ } catch (error) {
2002
+ const errorMessage = error instanceof Error ? error.message : String(error);
2003
+ logger.debug("Codex websocket prewarm failed", {
2004
+ error: errorMessage,
2005
+ provider: codexModel.provider,
2006
+ model: codexModel.id,
2007
+ });
2008
+ }
2009
+ })();
2010
+ }
2011
+ }
2012
+
2013
+ // Start LSP warmup in the background so startup does not block on language server initialization.
2014
+ let lspServers: CreateAgentSessionResult["lspServers"];
2015
+ if (enableLsp && settings.get("lsp.diagnosticsOnWrite")) {
2016
+ lspServers = discoverStartupLspServers(cwd);
2017
+ if (lspServers.length > 0) {
2018
+ void (async () => {
2019
+ try {
2020
+ const result = await logger.time("warmupLspServers", warmupLspServers, cwd);
2021
+ const serversByName = new Map(result.servers.map(server => [server.name, server] as const));
2022
+ for (const server of lspServers ?? []) {
2023
+ const next = serversByName.get(server.name);
2024
+ if (!next) continue;
2025
+ server.status = next.status;
2026
+ server.fileTypes = next.fileTypes;
2027
+ server.error = next.error;
2028
+ }
2029
+ const event: LspStartupEvent = {
2030
+ type: "completed",
2031
+ servers: result.servers,
2032
+ };
2033
+ eventBus.emit(LSP_STARTUP_EVENT_CHANNEL, event);
2034
+ } catch (error) {
2035
+ const errorMessage = error instanceof Error ? error.message : String(error);
2036
+ logger.warn("LSP server warmup failed", { cwd, error: errorMessage });
2037
+ for (const server of lspServers ?? []) {
2038
+ server.status = "error";
2039
+ server.error = errorMessage;
2040
+ }
2041
+ const event: LspStartupEvent = {
2042
+ type: "failed",
2043
+ error: errorMessage,
2044
+ };
2045
+ eventBus.emit(LSP_STARTUP_EVENT_CHANNEL, event);
2046
+ }
2047
+ })();
2048
+ }
2049
+ }
2050
+
2051
+ logger.time("startMemoryStartupTask", () =>
2052
+ startMemoryStartupTask({
2053
+ session,
2054
+ settings,
2055
+ modelRegistry,
2056
+ agentDir,
2057
+ taskDepth,
2058
+ }),
2059
+ );
2060
+
2061
+ // Wire MCP manager callbacks to session for reactive tool updates
2062
+ if (mcpManager) {
2063
+ mcpManager.setOnToolsChanged(tools => {
2064
+ void session.refreshMCPTools(tools);
2065
+ });
2066
+ // Wire prompt refresh → rebuild MCP prompt slash commands
2067
+ mcpManager.setOnPromptsChanged(serverName => {
2068
+ const promptCommands = buildMCPPromptCommands(mcpManager);
2069
+ session.setMCPPromptCommands(promptCommands);
2070
+ logger.debug("MCP prompt commands refreshed", { path: `mcp:${serverName}` });
2071
+ });
2072
+ const notificationDebounceTimers = new Map<string, Timer>();
2073
+ const clearDebounceTimers = () => {
2074
+ for (const timer of notificationDebounceTimers.values()) clearTimeout(timer);
2075
+ notificationDebounceTimers.clear();
2076
+ };
2077
+ postmortem.register("mcp-notification-cleanup", clearDebounceTimers);
2078
+ mcpManager.setOnResourcesChanged((serverName, uri) => {
2079
+ logger.debug("MCP resources changed", { path: `mcp:${serverName}`, uri });
2080
+ if (!settings.get("mcp.notifications")) return;
2081
+ const debounceMs = settings.get("mcp.notificationDebounceMs");
2082
+ const key = `${serverName}:${uri}`;
2083
+ const existing = notificationDebounceTimers.get(key);
2084
+ if (existing) clearTimeout(existing);
2085
+ notificationDebounceTimers.set(
2086
+ key,
2087
+ setTimeout(() => {
2088
+ notificationDebounceTimers.delete(key);
2089
+ // Re-check: user may have disabled notifications during the debounce window
2090
+ if (!settings.get("mcp.notifications")) return;
2091
+ void session.followUp(
2092
+ `[MCP notification] Server "${serverName}" reports resource \`${uri}\` was updated. Use read(path="mcp://${uri}") to inspect if relevant.`,
2093
+ );
2094
+ }, debounceMs),
2095
+ );
2096
+ });
2097
+ }
2098
+
2099
+ logger.time("createAgentSession:return");
2100
+ return {
2101
+ session,
2102
+ extensionsResult,
2103
+ setToolUIContext,
2104
+ mcpManager,
2105
+ modelFallbackMessage,
2106
+ lspServers,
2107
+ eventBus,
2108
+ };
2109
+ } catch (error) {
2110
+ try {
2111
+ if (hasSession) {
2112
+ await session.dispose();
2113
+ } else {
2114
+ await disposeKernelSessionsByOwner(pythonKernelOwnerId);
2115
+ }
2116
+ } catch (cleanupError) {
2117
+ logger.warn("Failed to clean up createAgentSession resources after startup error", {
2118
+ error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError),
2119
+ });
2120
+ }
2121
+ throw error;
2122
+ }
2123
+ }