@akon-labs/nexus-cli 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +1 -0
  3. package/README.md +3 -2
  4. package/dist/app-bridge.bundle.js +67 -0
  5. package/dist/cli.js +20263 -2158
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +1 -1
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/auto-model.d.ts +1 -1
  10. package/dist/core/auto-model.d.ts.map +1 -1
  11. package/dist/core/auto-model.js +1 -1
  12. package/dist/core/auto-model.js.map +1 -1
  13. package/dist/core/model-resolver.d.ts +2 -2
  14. package/dist/core/model-resolver.d.ts.map +1 -1
  15. package/dist/core/model-resolver.js +3 -3
  16. package/dist/core/model-resolver.js.map +1 -1
  17. package/dist/core/package-manager.d.ts +15 -0
  18. package/dist/core/package-manager.d.ts.map +1 -1
  19. package/dist/core/package-manager.js +50 -0
  20. package/dist/core/package-manager.js.map +1 -1
  21. package/dist/core/sdk.d.ts.map +1 -1
  22. package/dist/core/sdk.js +1 -1
  23. package/dist/core/sdk.js.map +1 -1
  24. package/dist/core/slash-commands.d.ts.map +1 -1
  25. package/dist/core/slash-commands.js +3 -3
  26. package/dist/core/slash-commands.js.map +1 -1
  27. package/dist/core/system-prompt.d.ts.map +1 -1
  28. package/dist/core/system-prompt.js +1 -1
  29. package/dist/core/system-prompt.js.map +1 -1
  30. package/dist/core/tools/index.d.ts +1 -1
  31. package/dist/core/tools/index.d.ts.map +1 -1
  32. package/dist/core/tools/index.js +5 -5
  33. package/dist/core/tools/index.js.map +1 -1
  34. package/dist/core/tools/websearch.d.ts.map +1 -1
  35. package/dist/core/tools/websearch.js +3 -3
  36. package/dist/core/tools/websearch.js.map +1 -1
  37. package/dist/extensions/gitnexus/gateway.d.ts +6 -6
  38. package/dist/extensions/gitnexus/gateway.d.ts.map +1 -1
  39. package/dist/extensions/gitnexus/gateway.js +4 -4
  40. package/dist/extensions/gitnexus/gateway.js.map +1 -1
  41. package/dist/extensions/gitnexus/index.d.ts.map +1 -1
  42. package/dist/extensions/gitnexus/index.js +4 -2
  43. package/dist/extensions/gitnexus/index.js.map +1 -1
  44. package/dist/extensions/mcp/abort.d.ts +3 -0
  45. package/dist/extensions/mcp/abort.d.ts.map +1 -0
  46. package/dist/extensions/mcp/abort.js +41 -0
  47. package/dist/extensions/mcp/abort.js.map +1 -0
  48. package/dist/extensions/mcp/agent-dir.d.ts +6 -0
  49. package/dist/extensions/mcp/agent-dir.d.ts.map +1 -0
  50. package/dist/extensions/mcp/agent-dir.js +18 -0
  51. package/dist/extensions/mcp/agent-dir.js.map +1 -0
  52. package/dist/extensions/mcp/agent-plugin-loader.d.ts +9 -0
  53. package/dist/extensions/mcp/agent-plugin-loader.d.ts.map +1 -0
  54. package/dist/extensions/mcp/agent-plugin-loader.js +384 -0
  55. package/dist/extensions/mcp/agent-plugin-loader.js.map +1 -0
  56. package/dist/extensions/mcp/app-bridge.bundle.js +67 -0
  57. package/dist/extensions/mcp/commands.d.ts +27 -0
  58. package/dist/extensions/mcp/commands.d.ts.map +1 -0
  59. package/dist/extensions/mcp/commands.js +629 -0
  60. package/dist/extensions/mcp/commands.js.map +1 -0
  61. package/dist/extensions/mcp/config.d.ts +121 -38
  62. package/dist/extensions/mcp/config.d.ts.map +1 -1
  63. package/dist/extensions/mcp/config.js +1030 -70
  64. package/dist/extensions/mcp/config.js.map +1 -1
  65. package/dist/extensions/mcp/consent-manager.d.ts +14 -0
  66. package/dist/extensions/mcp/consent-manager.d.ts.map +1 -0
  67. package/dist/extensions/mcp/consent-manager.js +60 -0
  68. package/dist/extensions/mcp/consent-manager.js.map +1 -0
  69. package/dist/extensions/mcp/direct-tools.d.ts +11 -0
  70. package/dist/extensions/mcp/direct-tools.d.ts.map +1 -0
  71. package/dist/extensions/mcp/direct-tools.js +562 -0
  72. package/dist/extensions/mcp/direct-tools.js.map +1 -0
  73. package/dist/extensions/mcp/elicitation-handler.d.ts +18 -0
  74. package/dist/extensions/mcp/elicitation-handler.d.ts.map +1 -0
  75. package/dist/extensions/mcp/elicitation-handler.js +320 -0
  76. package/dist/extensions/mcp/elicitation-handler.js.map +1 -0
  77. package/dist/extensions/mcp/error-signal.d.ts +16 -0
  78. package/dist/extensions/mcp/error-signal.d.ts.map +1 -0
  79. package/dist/extensions/mcp/error-signal.js +22 -0
  80. package/dist/extensions/mcp/error-signal.js.map +1 -0
  81. package/dist/extensions/mcp/errors.d.ts +104 -0
  82. package/dist/extensions/mcp/errors.d.ts.map +1 -0
  83. package/dist/extensions/mcp/errors.js +171 -0
  84. package/dist/extensions/mcp/errors.js.map +1 -0
  85. package/dist/extensions/mcp/glimpse-ui.d.ts +10 -0
  86. package/dist/extensions/mcp/glimpse-ui.d.ts.map +1 -0
  87. package/dist/extensions/mcp/glimpse-ui.js +67 -0
  88. package/dist/extensions/mcp/glimpse-ui.js.map +1 -0
  89. package/dist/extensions/mcp/host-html-template.d.ts +17 -0
  90. package/dist/extensions/mcp/host-html-template.d.ts.map +1 -0
  91. package/dist/extensions/mcp/host-html-template.js +442 -0
  92. package/dist/extensions/mcp/host-html-template.js.map +1 -0
  93. package/dist/extensions/mcp/index.d.ts +20 -15
  94. package/dist/extensions/mcp/index.d.ts.map +1 -1
  95. package/dist/extensions/mcp/index.js +1064 -82
  96. package/dist/extensions/mcp/index.js.map +1 -1
  97. package/dist/extensions/mcp/init.d.ts +28 -0
  98. package/dist/extensions/mcp/init.d.ts.map +1 -0
  99. package/dist/extensions/mcp/init.js +631 -0
  100. package/dist/extensions/mcp/init.js.map +1 -0
  101. package/dist/extensions/mcp/json-schema-validator.d.ts +3 -0
  102. package/dist/extensions/mcp/json-schema-validator.d.ts.map +1 -0
  103. package/dist/extensions/mcp/json-schema-validator.js +53 -0
  104. package/dist/extensions/mcp/json-schema-validator.js.map +1 -0
  105. package/dist/extensions/mcp/lifecycle.d.ts +56 -0
  106. package/dist/extensions/mcp/lifecycle.d.ts.map +1 -0
  107. package/dist/extensions/mcp/lifecycle.js +395 -0
  108. package/dist/extensions/mcp/lifecycle.js.map +1 -0
  109. package/dist/extensions/mcp/logger.d.ts +52 -0
  110. package/dist/extensions/mcp/logger.d.ts.map +1 -0
  111. package/dist/extensions/mcp/logger.js +134 -0
  112. package/dist/extensions/mcp/logger.js.map +1 -0
  113. package/dist/extensions/mcp/mcp-auth-flow.d.ts +117 -0
  114. package/dist/extensions/mcp/mcp-auth-flow.d.ts.map +1 -0
  115. package/dist/extensions/mcp/mcp-auth-flow.js +869 -0
  116. package/dist/extensions/mcp/mcp-auth-flow.js.map +1 -0
  117. package/dist/extensions/mcp/mcp-auth.d.ts +162 -0
  118. package/dist/extensions/mcp/mcp-auth.d.ts.map +1 -0
  119. package/dist/extensions/mcp/mcp-auth.js +897 -0
  120. package/dist/extensions/mcp/mcp-auth.js.map +1 -0
  121. package/dist/extensions/mcp/mcp-bearer-store.d.ts +25 -0
  122. package/dist/extensions/mcp/mcp-bearer-store.d.ts.map +1 -0
  123. package/dist/extensions/mcp/mcp-bearer-store.js +350 -0
  124. package/dist/extensions/mcp/mcp-bearer-store.js.map +1 -0
  125. package/dist/extensions/mcp/mcp-callback-server.d.ts +54 -0
  126. package/dist/extensions/mcp/mcp-callback-server.d.ts.map +1 -0
  127. package/dist/extensions/mcp/mcp-callback-server.js +454 -0
  128. package/dist/extensions/mcp/mcp-callback-server.js.map +1 -0
  129. package/dist/extensions/mcp/mcp-code.d.ts +48 -0
  130. package/dist/extensions/mcp/mcp-code.d.ts.map +1 -0
  131. package/dist/extensions/mcp/mcp-code.js +358 -0
  132. package/dist/extensions/mcp/mcp-code.js.map +1 -0
  133. package/dist/extensions/mcp/mcp-keyring-helper.cjs +89 -0
  134. package/dist/extensions/mcp/mcp-oauth-provider.d.ts +135 -0
  135. package/dist/extensions/mcp/mcp-oauth-provider.d.ts.map +1 -0
  136. package/dist/extensions/mcp/mcp-oauth-provider.js +491 -0
  137. package/dist/extensions/mcp/mcp-oauth-provider.js.map +1 -0
  138. package/dist/extensions/mcp/mcp-output-guard.d.ts +59 -0
  139. package/dist/extensions/mcp/mcp-output-guard.d.ts.map +1 -0
  140. package/dist/extensions/mcp/mcp-output-guard.js +343 -0
  141. package/dist/extensions/mcp/mcp-output-guard.js.map +1 -0
  142. package/dist/extensions/mcp/mcp-panel.d.ts +71 -0
  143. package/dist/extensions/mcp/mcp-panel.d.ts.map +1 -0
  144. package/dist/extensions/mcp/mcp-panel.js +973 -0
  145. package/dist/extensions/mcp/mcp-panel.js.map +1 -0
  146. package/dist/extensions/mcp/mcp-probe.d.ts +7 -0
  147. package/dist/extensions/mcp/mcp-probe.d.ts.map +1 -0
  148. package/dist/extensions/mcp/mcp-probe.js +155 -0
  149. package/dist/extensions/mcp/mcp-probe.js.map +1 -0
  150. package/dist/extensions/mcp/mcp-script-worker.mjs +130 -0
  151. package/dist/extensions/mcp/mcp-setup-panel.d.ts +85 -0
  152. package/dist/extensions/mcp/mcp-setup-panel.d.ts.map +1 -0
  153. package/dist/extensions/mcp/mcp-setup-panel.js +618 -0
  154. package/dist/extensions/mcp/mcp-setup-panel.js.map +1 -0
  155. package/dist/extensions/mcp/mcp-status.d.ts +12 -0
  156. package/dist/extensions/mcp/mcp-status.d.ts.map +1 -0
  157. package/dist/extensions/mcp/mcp-status.js +98 -0
  158. package/dist/extensions/mcp/mcp-status.js.map +1 -0
  159. package/dist/extensions/mcp/mcp-trace.d.ts +95 -0
  160. package/dist/extensions/mcp/mcp-trace.d.ts.map +1 -0
  161. package/dist/extensions/mcp/mcp-trace.js +252 -0
  162. package/dist/extensions/mcp/mcp-trace.js.map +1 -0
  163. package/dist/extensions/mcp/metadata-cache.d.ts +20 -0
  164. package/dist/extensions/mcp/metadata-cache.d.ts.map +1 -0
  165. package/dist/extensions/mcp/metadata-cache.js +318 -0
  166. package/dist/extensions/mcp/metadata-cache.js.map +1 -0
  167. package/dist/extensions/mcp/npx-resolver.d.ts +7 -0
  168. package/dist/extensions/mcp/npx-resolver.d.ts.map +1 -0
  169. package/dist/extensions/mcp/npx-resolver.js +505 -0
  170. package/dist/extensions/mcp/npx-resolver.js.map +1 -0
  171. package/dist/extensions/mcp/oauth.d.ts +21 -0
  172. package/dist/extensions/mcp/oauth.d.ts.map +1 -0
  173. package/dist/extensions/mcp/oauth.js +15 -0
  174. package/dist/extensions/mcp/oauth.js.map +1 -0
  175. package/dist/extensions/mcp/onboarding-state.d.ts +13 -0
  176. package/dist/extensions/mcp/onboarding-state.d.ts.map +1 -0
  177. package/dist/extensions/mcp/onboarding-state.js +65 -0
  178. package/dist/extensions/mcp/onboarding-state.js.map +1 -0
  179. package/dist/extensions/mcp/package-mcp-loader.d.ts +3 -0
  180. package/dist/extensions/mcp/package-mcp-loader.d.ts.map +1 -0
  181. package/dist/extensions/mcp/package-mcp-loader.js +170 -0
  182. package/dist/extensions/mcp/package-mcp-loader.js.map +1 -0
  183. package/dist/extensions/mcp/panel-keys.d.ts +21 -0
  184. package/dist/extensions/mcp/panel-keys.d.ts.map +1 -0
  185. package/dist/extensions/mcp/panel-keys.js +29 -0
  186. package/dist/extensions/mcp/panel-keys.js.map +1 -0
  187. package/dist/extensions/mcp/prompts.d.ts +63 -0
  188. package/dist/extensions/mcp/prompts.d.ts.map +1 -0
  189. package/dist/extensions/mcp/prompts.js +297 -0
  190. package/dist/extensions/mcp/prompts.js.map +1 -0
  191. package/dist/extensions/mcp/proxy-modes.d.ts +15 -0
  192. package/dist/extensions/mcp/proxy-modes.d.ts.map +1 -0
  193. package/dist/extensions/mcp/proxy-modes.js +1421 -0
  194. package/dist/extensions/mcp/proxy-modes.js.map +1 -0
  195. package/dist/extensions/mcp/request-headers-command.d.ts +11 -0
  196. package/dist/extensions/mcp/request-headers-command.d.ts.map +1 -0
  197. package/dist/extensions/mcp/request-headers-command.js +334 -0
  198. package/dist/extensions/mcp/request-headers-command.js.map +1 -0
  199. package/dist/extensions/mcp/resource-tools.d.ts +2 -0
  200. package/dist/extensions/mcp/resource-tools.d.ts.map +1 -0
  201. package/dist/extensions/mcp/resource-tools.js +15 -0
  202. package/dist/extensions/mcp/resource-tools.js.map +1 -0
  203. package/dist/extensions/mcp/runtime-owner.d.ts +14 -0
  204. package/dist/extensions/mcp/runtime-owner.d.ts.map +1 -0
  205. package/dist/extensions/mcp/runtime-owner.js +84 -0
  206. package/dist/extensions/mcp/runtime-owner.js.map +1 -0
  207. package/dist/extensions/mcp/sampling-handler.d.ts +18 -0
  208. package/dist/extensions/mcp/sampling-handler.d.ts.map +1 -0
  209. package/dist/extensions/mcp/sampling-handler.js +229 -0
  210. package/dist/extensions/mcp/sampling-handler.js.map +1 -0
  211. package/dist/extensions/mcp/search-ranking.d.ts +25 -0
  212. package/dist/extensions/mcp/search-ranking.d.ts.map +1 -0
  213. package/dist/extensions/mcp/search-ranking.js +234 -0
  214. package/dist/extensions/mcp/search-ranking.js.map +1 -0
  215. package/dist/extensions/mcp/server-manager.d.ts +116 -0
  216. package/dist/extensions/mcp/server-manager.d.ts.map +1 -0
  217. package/dist/extensions/mcp/server-manager.js +1052 -0
  218. package/dist/extensions/mcp/server-manager.js.map +1 -0
  219. package/dist/extensions/mcp/session-recovery.d.ts +27 -0
  220. package/dist/extensions/mcp/session-recovery.d.ts.map +1 -0
  221. package/dist/extensions/mcp/session-recovery.js +140 -0
  222. package/dist/extensions/mcp/session-recovery.js.map +1 -0
  223. package/dist/extensions/mcp/skills/mcp-scripting/SKILL.md +38 -0
  224. package/dist/extensions/mcp/state.d.ts +62 -0
  225. package/dist/extensions/mcp/state.d.ts.map +1 -0
  226. package/dist/extensions/mcp/state.js +2 -0
  227. package/dist/extensions/mcp/state.js.map +1 -0
  228. package/dist/extensions/mcp/tool-approval.d.ts +11 -0
  229. package/dist/extensions/mcp/tool-approval.d.ts.map +1 -0
  230. package/dist/extensions/mcp/tool-approval.js +156 -0
  231. package/dist/extensions/mcp/tool-approval.js.map +1 -0
  232. package/dist/extensions/mcp/tool-metadata.d.ts +11 -0
  233. package/dist/extensions/mcp/tool-metadata.d.ts.map +1 -0
  234. package/dist/extensions/mcp/tool-metadata.js +264 -0
  235. package/dist/extensions/mcp/tool-metadata.js.map +1 -0
  236. package/dist/extensions/mcp/tool-registrar.d.ts +21 -0
  237. package/dist/extensions/mcp/tool-registrar.d.ts.map +1 -0
  238. package/dist/extensions/mcp/tool-registrar.js +229 -0
  239. package/dist/extensions/mcp/tool-registrar.js.map +1 -0
  240. package/dist/extensions/mcp/tool-result-renderer.d.ts +83 -0
  241. package/dist/extensions/mcp/tool-result-renderer.d.ts.map +1 -0
  242. package/dist/extensions/mcp/tool-result-renderer.js +345 -0
  243. package/dist/extensions/mcp/tool-result-renderer.js.map +1 -0
  244. package/dist/extensions/mcp/ts-shape.d.ts +3 -0
  245. package/dist/extensions/mcp/ts-shape.d.ts.map +1 -0
  246. package/dist/extensions/mcp/ts-shape.js +160 -0
  247. package/dist/extensions/mcp/ts-shape.js.map +1 -0
  248. package/dist/extensions/mcp/types.d.ts +521 -0
  249. package/dist/extensions/mcp/types.d.ts.map +1 -0
  250. package/dist/extensions/mcp/types.js +294 -0
  251. package/dist/extensions/mcp/types.js.map +1 -0
  252. package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts +7 -0
  253. package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts.map +1 -0
  254. package/dist/extensions/mcp/ui-app-bridge-helpers.js +37 -0
  255. package/dist/extensions/mcp/ui-app-bridge-helpers.js.map +1 -0
  256. package/dist/extensions/mcp/ui-resource-handler.d.ts +18 -0
  257. package/dist/extensions/mcp/ui-resource-handler.d.ts.map +1 -0
  258. package/dist/extensions/mcp/ui-resource-handler.js +210 -0
  259. package/dist/extensions/mcp/ui-resource-handler.js.map +1 -0
  260. package/dist/extensions/mcp/ui-server.d.ts +52 -0
  261. package/dist/extensions/mcp/ui-server.d.ts.map +1 -0
  262. package/dist/extensions/mcp/ui-server.js +740 -0
  263. package/dist/extensions/mcp/ui-server.js.map +1 -0
  264. package/dist/extensions/mcp/ui-session.d.ts +40 -0
  265. package/dist/extensions/mcp/ui-session.d.ts.map +1 -0
  266. package/dist/extensions/mcp/ui-session.js +477 -0
  267. package/dist/extensions/mcp/ui-session.js.map +1 -0
  268. package/dist/extensions/mcp/ui-stream-types.d.ts +105 -0
  269. package/dist/extensions/mcp/ui-stream-types.d.ts.map +1 -0
  270. package/dist/extensions/mcp/ui-stream-types.js +61 -0
  271. package/dist/extensions/mcp/ui-stream-types.js.map +1 -0
  272. package/dist/extensions/mcp/ui-tool-visibility.d.ts +5 -0
  273. package/dist/extensions/mcp/ui-tool-visibility.d.ts.map +1 -0
  274. package/dist/extensions/mcp/ui-tool-visibility.js +27 -0
  275. package/dist/extensions/mcp/ui-tool-visibility.js.map +1 -0
  276. package/dist/extensions/mcp/unix-socket-transport.d.ts +15 -0
  277. package/dist/extensions/mcp/unix-socket-transport.d.ts.map +1 -0
  278. package/dist/extensions/mcp/unix-socket-transport.js +85 -0
  279. package/dist/extensions/mcp/unix-socket-transport.js.map +1 -0
  280. package/dist/extensions/mcp/utils.d.ts +32 -0
  281. package/dist/extensions/mcp/utils.d.ts.map +1 -0
  282. package/dist/extensions/mcp/utils.js +336 -0
  283. package/dist/extensions/mcp/utils.js.map +1 -0
  284. package/dist/extensions/subagent/index.d.ts +61 -1
  285. package/dist/extensions/subagent/index.d.ts.map +1 -1
  286. package/dist/extensions/subagent/index.js +177 -4
  287. package/dist/extensions/subagent/index.js.map +1 -1
  288. package/dist/extensions/web-access.d.ts.map +1 -1
  289. package/dist/extensions/web-access.js +1 -1
  290. package/dist/extensions/web-access.js.map +1 -1
  291. package/dist/index.d.ts +2 -0
  292. package/dist/index.d.ts.map +1 -1
  293. package/dist/index.js +20057 -1947
  294. package/dist/index.js.map +1 -1
  295. package/dist/mcp-keyring-helper.cjs +89 -0
  296. package/dist/mcp-script-worker.mjs +130 -0
  297. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  298. package/dist/modes/interactive/components/model-selector.js +1 -1
  299. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  300. package/dist/modes/interactive/components/response-separator.d.ts +1 -1
  301. package/dist/modes/interactive/components/response-separator.d.ts.map +1 -1
  302. package/dist/modes/interactive/components/response-separator.js +1 -1
  303. package/dist/modes/interactive/components/response-separator.js.map +1 -1
  304. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  305. package/dist/modes/interactive/interactive-mode.js +7 -5
  306. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  307. package/dist/rpc-entry.js +20263 -2158
  308. package/dist/skills/mcp-scripting/SKILL.md +38 -0
  309. package/dist/vendor/ai.js +280 -205
  310. package/docs/mcp.md +178 -0
  311. package/docs/pi-subagents.md +75 -0
  312. package/docs/quickstart.md +1 -1
  313. package/docs/usage.md +2 -1
  314. package/examples/extensions/pi-subagents-worker-policy.ts +86 -0
  315. package/npm-shrinkwrap.json +763 -6
  316. package/package.json +15 -4
  317. package/dist/extensions/mcp/client.d.ts +0 -86
  318. package/dist/extensions/mcp/client.d.ts.map +0 -1
  319. package/dist/extensions/mcp/client.js +0 -275
  320. package/dist/extensions/mcp/client.js.map +0 -1
@@ -0,0 +1,229 @@
1
+ // tool-registrar.ts - MCP content transformation
2
+ // NOTE: Tools are NOT registered with Nexus - only the unified `mcp` proxy tool is registered.
3
+ // This keeps the LLM context small (1 tool instead of 100s).
4
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { join } from "node:path";
7
+ const MAX_BINARY_RESOURCE_BYTES = 10 * 1024 * 1024;
8
+ const MAX_SESSION_RESOURCE_BYTES = 100 * 1024 * 1024;
9
+ const MAX_SESSION_RESOURCE_FILES = 10_000; // Bounds metadata from empty or tiny resources.
10
+ const CLEANUP_RETRY_DELAY_MS = 30_000;
11
+ const MAX_CLEANUP_RETRY_ATTEMPTS = 3;
12
+ function createMaterializedResourceSession() {
13
+ return {
14
+ directory: undefined,
15
+ bytes: 0,
16
+ files: 0,
17
+ sequence: 0,
18
+ };
19
+ }
20
+ const defaultMaterializedResourceSession = createMaterializedResourceSession();
21
+ const scopedMaterializedResourceSessions = new WeakMap();
22
+ const pendingCleanupDirectories = new Set();
23
+ const cleanupRetryAttempts = new Map();
24
+ let pendingCleanupRetry;
25
+ function isAbortedScope(scope) {
26
+ return !!scope && "aborted" in scope && scope.aborted === true;
27
+ }
28
+ function getMaterializedResourceSession(scope) {
29
+ if (isAbortedScope(scope))
30
+ return undefined;
31
+ if (!scope)
32
+ return defaultMaterializedResourceSession;
33
+ let session = scopedMaterializedResourceSessions.get(scope);
34
+ if (!session) {
35
+ session = createMaterializedResourceSession();
36
+ scopedMaterializedResourceSessions.set(scope, session);
37
+ }
38
+ return session;
39
+ }
40
+ function hasRetryableCleanupDirectory() {
41
+ for (const directory of pendingCleanupDirectories) {
42
+ if ((cleanupRetryAttempts.get(directory) ?? 0) < MAX_CLEANUP_RETRY_ATTEMPTS)
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+ function schedulePendingCleanupRetry() {
48
+ if (pendingCleanupRetry || !hasRetryableCleanupDirectory())
49
+ return;
50
+ for (const directory of pendingCleanupDirectories) {
51
+ const attempts = cleanupRetryAttempts.get(directory) ?? 0;
52
+ if (attempts < MAX_CLEANUP_RETRY_ATTEMPTS)
53
+ cleanupRetryAttempts.set(directory, attempts + 1);
54
+ }
55
+ pendingCleanupRetry = setTimeout(() => {
56
+ pendingCleanupRetry = undefined;
57
+ try {
58
+ drainPendingCleanupDirectories();
59
+ }
60
+ catch {
61
+ // drainPendingCleanupDirectories already retained the paths and rescheduled another retry.
62
+ }
63
+ }, CLEANUP_RETRY_DELAY_MS);
64
+ }
65
+ function drainPendingCleanupDirectories() {
66
+ const failures = [];
67
+ for (const directory of Array.from(pendingCleanupDirectories)) {
68
+ try {
69
+ rmSync(directory, { recursive: true, force: true });
70
+ pendingCleanupDirectories.delete(directory);
71
+ cleanupRetryAttempts.delete(directory);
72
+ }
73
+ catch (error) {
74
+ failures.push(error);
75
+ }
76
+ }
77
+ if (pendingCleanupDirectories.size === 0 && pendingCleanupRetry) {
78
+ clearTimeout(pendingCleanupRetry);
79
+ pendingCleanupRetry = undefined;
80
+ }
81
+ if (failures.length > 0) {
82
+ schedulePendingCleanupRetry();
83
+ throw new AggregateError(failures, "Failed to clean materialized MCP resources");
84
+ }
85
+ }
86
+ export function cleanupMaterializedBinaryResources(scope) {
87
+ const session = scope ? scopedMaterializedResourceSessions.get(scope) : defaultMaterializedResourceSession;
88
+ if (session?.directory)
89
+ pendingCleanupDirectories.add(session.directory);
90
+ if (session) {
91
+ session.directory = undefined;
92
+ session.bytes = 0;
93
+ session.files = 0;
94
+ session.sequence = 0;
95
+ if (scope)
96
+ scopedMaterializedResourceSessions.delete(scope);
97
+ }
98
+ drainPendingCleanupDirectories();
99
+ }
100
+ function replaceBlob(resource, text) {
101
+ delete resource.blob;
102
+ resource.text = text;
103
+ return text;
104
+ }
105
+ function omitBinaryResource(resource, reason) {
106
+ return replaceBlob(resource, [
107
+ `[Resource: ${resource.uri ?? "(no URI)"}]`,
108
+ `Binary content omitted: ${reason}`,
109
+ `MIME type: ${resource.mimeType ?? "application/octet-stream"}`,
110
+ ].join("\n"));
111
+ }
112
+ function materializeBinaryResource(resource, scope) {
113
+ const session = getMaterializedResourceSession(scope);
114
+ if (!session)
115
+ return omitBinaryResource(resource, "runtime stopped");
116
+ const decodedBytes = Buffer.byteLength(resource.blob, "base64");
117
+ if (decodedBytes > MAX_BINARY_RESOURCE_BYTES) {
118
+ return omitBinaryResource(resource, "decoded size exceeds 10 MiB");
119
+ }
120
+ if (session.bytes + decodedBytes > MAX_SESSION_RESOURCE_BYTES || session.files >= MAX_SESSION_RESOURCE_FILES) {
121
+ return omitBinaryResource(resource, "session resource limit reached");
122
+ }
123
+ try {
124
+ session.directory ??= mkdtempSync(join(tmpdir(), "nexus-mcp-resource-"));
125
+ }
126
+ catch {
127
+ return omitBinaryResource(resource, "could not be saved");
128
+ }
129
+ const filePath = join(session.directory, `resource-${++session.sequence}.bin`);
130
+ session.bytes += decodedBytes;
131
+ session.files += 1;
132
+ try {
133
+ writeFileSync(filePath, Buffer.from(resource.blob, "base64"), { flag: "wx", mode: 0o600 });
134
+ }
135
+ catch {
136
+ try {
137
+ rmSync(filePath, { force: true });
138
+ session.bytes -= decodedBytes;
139
+ session.files -= 1;
140
+ }
141
+ catch {
142
+ // Keep the reservation when a partial file cannot be removed.
143
+ }
144
+ return omitBinaryResource(resource, "could not be saved");
145
+ }
146
+ return replaceBlob(resource, [
147
+ `[Resource: ${resource.uri ?? "(no URI)"}]`,
148
+ `Binary content saved to ${filePath}`,
149
+ `MIME type: ${resource.mimeType ?? "application/octet-stream"}`,
150
+ ].join("\n"));
151
+ }
152
+ /**
153
+ * Transform MCP content types to Nexus content blocks.
154
+ */
155
+ export function transformMcpResourceContents(contents, scope) {
156
+ return contents.map((resource) => {
157
+ if (typeof resource.text === "string")
158
+ return { type: "text", text: resource.text };
159
+ if (typeof resource.blob === "string")
160
+ return { type: "text", text: materializeBinaryResource(resource, scope) };
161
+ return { type: "text", text: JSON.stringify(resource) };
162
+ });
163
+ }
164
+ export function transformMcpContent(content, scope) {
165
+ return content.map((c) => {
166
+ if (c.type === "text") {
167
+ return { type: "text", text: c.text ?? "" };
168
+ }
169
+ if (c.type === "image") {
170
+ return {
171
+ type: "image",
172
+ data: c.data ?? "",
173
+ mimeType: c.mimeType ?? "image/png",
174
+ };
175
+ }
176
+ if (c.type === "resource") {
177
+ const resourceUri = c.resource?.uri ?? "(no URI)";
178
+ if (c.resource && "blob" in c.resource && typeof c.resource.blob === "string") {
179
+ const binaryResource = c.resource;
180
+ return {
181
+ type: "text",
182
+ text: materializeBinaryResource(binaryResource, scope),
183
+ };
184
+ }
185
+ const resourceContent = c.resource?.text ?? (c.resource ? JSON.stringify(c.resource) : "(no content)");
186
+ return {
187
+ type: "text",
188
+ text: `[Resource: ${resourceUri}]\n${resourceContent}`,
189
+ };
190
+ }
191
+ if (c.type === "resource_link") {
192
+ const linkName = c.name ?? c.uri ?? "unknown";
193
+ const linkUri = c.uri ?? "(no URI)";
194
+ return {
195
+ type: "text",
196
+ text: `[Resource Link: ${linkName}]\nURI: ${linkUri}`,
197
+ };
198
+ }
199
+ if (c.type === "audio") {
200
+ return {
201
+ type: "text",
202
+ text: `[Audio content: ${c.mimeType ?? "audio/*"}]`,
203
+ };
204
+ }
205
+ return { type: "text", text: JSON.stringify(c) };
206
+ });
207
+ }
208
+ /**
209
+ * Resolve a tool result's content blocks, falling back to structuredContent
210
+ * when content is empty.
211
+ */
212
+ export function resolveMcpResultContent(result, scope) {
213
+ const blocks = transformMcpContent((Array.isArray(result.content) ? result.content : []), scope);
214
+ if (blocks.length > 0)
215
+ return blocks;
216
+ if (result.structuredContent !== undefined && result.structuredContent !== null) {
217
+ return [{ type: "text", text: stringifyStructuredContent(result.structuredContent) }];
218
+ }
219
+ return [];
220
+ }
221
+ function stringifyStructuredContent(value) {
222
+ try {
223
+ return JSON.stringify(value, null, 2) ?? String(value);
224
+ }
225
+ catch {
226
+ return String(value);
227
+ }
228
+ }
229
+ //# sourceMappingURL=tool-registrar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-registrar.js","sourceRoot":"","sources":["../../../src/extensions/mcp/tool-registrar.ts"],"names":[],"mappings":"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","sourcesContent":["// tool-registrar.ts - MCP content transformation\n// NOTE: Tools are NOT registered with Nexus - only the unified `mcp` proxy tool is registered.\n// This keeps the LLM context small (1 tool instead of 100s).\n\nimport { mkdtempSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ContentBlock, McpContent } from \"./types.ts\";\n\nconst MAX_BINARY_RESOURCE_BYTES = 10 * 1024 * 1024;\nconst MAX_SESSION_RESOURCE_BYTES = 100 * 1024 * 1024;\nconst MAX_SESSION_RESOURCE_FILES = 10_000; // Bounds metadata from empty or tiny resources.\nconst CLEANUP_RETRY_DELAY_MS = 30_000;\nconst MAX_CLEANUP_RETRY_ATTEMPTS = 3;\ntype MaterializedResourceSession = {\n\tdirectory: string | undefined;\n\tbytes: number;\n\tfiles: number;\n\tsequence: number;\n};\n\nfunction createMaterializedResourceSession(): MaterializedResourceSession {\n\treturn {\n\t\tdirectory: undefined,\n\t\tbytes: 0,\n\t\tfiles: 0,\n\t\tsequence: 0,\n\t};\n}\n\nconst defaultMaterializedResourceSession = createMaterializedResourceSession();\nconst scopedMaterializedResourceSessions = new WeakMap<object, MaterializedResourceSession>();\nconst pendingCleanupDirectories = new Set<string>();\nconst cleanupRetryAttempts = new Map<string, number>();\nlet pendingCleanupRetry: ReturnType<typeof setTimeout> | undefined;\n\nfunction isAbortedScope(scope: object | undefined): boolean {\n\treturn !!scope && \"aborted\" in scope && (scope as { aborted?: unknown }).aborted === true;\n}\n\nfunction getMaterializedResourceSession(scope?: object): MaterializedResourceSession | undefined {\n\tif (isAbortedScope(scope)) return undefined;\n\tif (!scope) return defaultMaterializedResourceSession;\n\tlet session = scopedMaterializedResourceSessions.get(scope);\n\tif (!session) {\n\t\tsession = createMaterializedResourceSession();\n\t\tscopedMaterializedResourceSessions.set(scope, session);\n\t}\n\treturn session;\n}\n\ntype BinaryResource = {\n\turi?: string | undefined;\n\ttext?: string | undefined;\n\tmimeType?: string | undefined;\n\tblob: string;\n};\ntype McpResourceContent = {\n\turi: string;\n\ttext?: string | undefined;\n\tblob?: string | undefined;\n\tmimeType?: string | undefined;\n\t[key: string]: unknown;\n};\n\nfunction hasRetryableCleanupDirectory(): boolean {\n\tfor (const directory of pendingCleanupDirectories) {\n\t\tif ((cleanupRetryAttempts.get(directory) ?? 0) < MAX_CLEANUP_RETRY_ATTEMPTS) return true;\n\t}\n\treturn false;\n}\n\nfunction schedulePendingCleanupRetry(): void {\n\tif (pendingCleanupRetry || !hasRetryableCleanupDirectory()) return;\n\tfor (const directory of pendingCleanupDirectories) {\n\t\tconst attempts = cleanupRetryAttempts.get(directory) ?? 0;\n\t\tif (attempts < MAX_CLEANUP_RETRY_ATTEMPTS) cleanupRetryAttempts.set(directory, attempts + 1);\n\t}\n\tpendingCleanupRetry = setTimeout(() => {\n\t\tpendingCleanupRetry = undefined;\n\t\ttry {\n\t\t\tdrainPendingCleanupDirectories();\n\t\t} catch {\n\t\t\t// drainPendingCleanupDirectories already retained the paths and rescheduled another retry.\n\t\t}\n\t}, CLEANUP_RETRY_DELAY_MS);\n}\n\nfunction drainPendingCleanupDirectories(): void {\n\tconst failures: unknown[] = [];\n\tfor (const directory of Array.from(pendingCleanupDirectories)) {\n\t\ttry {\n\t\t\trmSync(directory, { recursive: true, force: true });\n\t\t\tpendingCleanupDirectories.delete(directory);\n\t\t\tcleanupRetryAttempts.delete(directory);\n\t\t} catch (error) {\n\t\t\tfailures.push(error);\n\t\t}\n\t}\n\tif (pendingCleanupDirectories.size === 0 && pendingCleanupRetry) {\n\t\tclearTimeout(pendingCleanupRetry);\n\t\tpendingCleanupRetry = undefined;\n\t}\n\tif (failures.length > 0) {\n\t\tschedulePendingCleanupRetry();\n\t\tthrow new AggregateError(failures, \"Failed to clean materialized MCP resources\");\n\t}\n}\n\nexport function cleanupMaterializedBinaryResources(scope?: object): void {\n\tconst session = scope ? scopedMaterializedResourceSessions.get(scope) : defaultMaterializedResourceSession;\n\tif (session?.directory) pendingCleanupDirectories.add(session.directory);\n\tif (session) {\n\t\tsession.directory = undefined;\n\t\tsession.bytes = 0;\n\t\tsession.files = 0;\n\t\tsession.sequence = 0;\n\t\tif (scope) scopedMaterializedResourceSessions.delete(scope);\n\t}\n\n\tdrainPendingCleanupDirectories();\n}\n\nfunction replaceBlob(resource: BinaryResource, text: string): string {\n\tdelete (resource as Partial<BinaryResource>).blob;\n\tresource.text = text;\n\treturn text;\n}\n\nfunction omitBinaryResource(resource: BinaryResource, reason: string): string {\n\treturn replaceBlob(\n\t\tresource,\n\t\t[\n\t\t\t`[Resource: ${resource.uri ?? \"(no URI)\"}]`,\n\t\t\t`Binary content omitted: ${reason}`,\n\t\t\t`MIME type: ${resource.mimeType ?? \"application/octet-stream\"}`,\n\t\t].join(\"\\n\"),\n\t);\n}\n\nfunction materializeBinaryResource(resource: BinaryResource, scope?: object): string {\n\tconst session = getMaterializedResourceSession(scope);\n\tif (!session) return omitBinaryResource(resource, \"runtime stopped\");\n\tconst decodedBytes = Buffer.byteLength(resource.blob, \"base64\");\n\tif (decodedBytes > MAX_BINARY_RESOURCE_BYTES) {\n\t\treturn omitBinaryResource(resource, \"decoded size exceeds 10 MiB\");\n\t}\n\tif (session.bytes + decodedBytes > MAX_SESSION_RESOURCE_BYTES || session.files >= MAX_SESSION_RESOURCE_FILES) {\n\t\treturn omitBinaryResource(resource, \"session resource limit reached\");\n\t}\n\n\ttry {\n\t\tsession.directory ??= mkdtempSync(join(tmpdir(), \"nexus-mcp-resource-\"));\n\t} catch {\n\t\treturn omitBinaryResource(resource, \"could not be saved\");\n\t}\n\n\tconst filePath = join(session.directory, `resource-${++session.sequence}.bin`);\n\tsession.bytes += decodedBytes;\n\tsession.files += 1;\n\ttry {\n\t\twriteFileSync(filePath, Buffer.from(resource.blob, \"base64\"), { flag: \"wx\", mode: 0o600 });\n\t} catch {\n\t\ttry {\n\t\t\trmSync(filePath, { force: true });\n\t\t\tsession.bytes -= decodedBytes;\n\t\t\tsession.files -= 1;\n\t\t} catch {\n\t\t\t// Keep the reservation when a partial file cannot be removed.\n\t\t}\n\t\treturn omitBinaryResource(resource, \"could not be saved\");\n\t}\n\n\treturn replaceBlob(\n\t\tresource,\n\t\t[\n\t\t\t`[Resource: ${resource.uri ?? \"(no URI)\"}]`,\n\t\t\t`Binary content saved to ${filePath}`,\n\t\t\t`MIME type: ${resource.mimeType ?? \"application/octet-stream\"}`,\n\t\t].join(\"\\n\"),\n\t);\n}\n\n/**\n * Transform MCP content types to Nexus content blocks.\n */\nexport function transformMcpResourceContents(contents: McpResourceContent[], scope?: object): ContentBlock[] {\n\treturn contents.map((resource) => {\n\t\tif (typeof resource.text === \"string\") return { type: \"text\" as const, text: resource.text };\n\t\tif (typeof resource.blob === \"string\")\n\t\t\treturn { type: \"text\" as const, text: materializeBinaryResource(resource as BinaryResource, scope) };\n\t\treturn { type: \"text\" as const, text: JSON.stringify(resource) };\n\t});\n}\n\nexport function transformMcpContent(content: McpContent[], scope?: object): ContentBlock[] {\n\treturn content.map((c) => {\n\t\tif (c.type === \"text\") {\n\t\t\treturn { type: \"text\" as const, text: c.text ?? \"\" };\n\t\t}\n\t\tif (c.type === \"image\") {\n\t\t\treturn {\n\t\t\t\ttype: \"image\" as const,\n\t\t\t\tdata: c.data ?? \"\",\n\t\t\t\tmimeType: c.mimeType ?? \"image/png\",\n\t\t\t};\n\t\t}\n\t\tif (c.type === \"resource\") {\n\t\t\tconst resourceUri = c.resource?.uri ?? \"(no URI)\";\n\t\t\tif (c.resource && \"blob\" in c.resource && typeof c.resource.blob === \"string\") {\n\t\t\t\tconst binaryResource = c.resource as typeof c.resource & { mimeType?: string; blob: string };\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\ttext: materializeBinaryResource(binaryResource, scope),\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst resourceContent = c.resource?.text ?? (c.resource ? JSON.stringify(c.resource) : \"(no content)\");\n\t\t\treturn {\n\t\t\t\ttype: \"text\" as const,\n\t\t\t\ttext: `[Resource: ${resourceUri}]\\n${resourceContent}`,\n\t\t\t};\n\t\t}\n\t\tif (c.type === \"resource_link\") {\n\t\t\tconst linkName = c.name ?? c.uri ?? \"unknown\";\n\t\t\tconst linkUri = c.uri ?? \"(no URI)\";\n\t\t\treturn {\n\t\t\t\ttype: \"text\" as const,\n\t\t\t\ttext: `[Resource Link: ${linkName}]\\nURI: ${linkUri}`,\n\t\t\t};\n\t\t}\n\t\tif (c.type === \"audio\") {\n\t\t\treturn {\n\t\t\t\ttype: \"text\" as const,\n\t\t\t\ttext: `[Audio content: ${c.mimeType ?? \"audio/*\"}]`,\n\t\t\t};\n\t\t}\n\t\treturn { type: \"text\" as const, text: JSON.stringify(c) };\n\t});\n}\n\n/**\n * Resolve a tool result's content blocks, falling back to structuredContent\n * when content is empty.\n */\nexport function resolveMcpResultContent(result: Record<string, unknown>, scope?: object): ContentBlock[] {\n\tconst blocks = transformMcpContent((Array.isArray(result.content) ? result.content : []) as McpContent[], scope);\n\tif (blocks.length > 0) return blocks;\n\n\tif (result.structuredContent !== undefined && result.structuredContent !== null) {\n\t\treturn [{ type: \"text\" as const, text: stringifyStructuredContent(result.structuredContent) }];\n\t}\n\n\treturn [];\n}\n\nfunction stringifyStructuredContent(value: unknown): string {\n\ttry {\n\t\treturn JSON.stringify(value, null, 2) ?? String(value);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n"]}
@@ -0,0 +1,83 @@
1
+ import { type Component, Text } from "@earendil-works/pi-tui";
2
+ import type { AgentToolResult, ToolRenderResultOptions } from "../../core/extensions/types.ts";
3
+ type McpToolResultDetails = Record<string, unknown> & {
4
+ error?: unknown;
5
+ };
6
+ interface RenderTheme {
7
+ fg: (name: string, text: string) => string;
8
+ bold?: (text: string) => string;
9
+ }
10
+ export interface McpProxyToolCallInput {
11
+ tool?: string;
12
+ args?: string | Record<string, unknown>;
13
+ connect?: string;
14
+ describe?: string;
15
+ search?: string;
16
+ regex?: boolean;
17
+ includeSchemas?: boolean;
18
+ server?: string;
19
+ action?: string;
20
+ }
21
+ interface McpToolRenderState {
22
+ compactTitle?: string;
23
+ compactInputPreview?: string;
24
+ }
25
+ interface McpToolRenderContext {
26
+ isError: boolean;
27
+ isPartial?: boolean;
28
+ expanded?: boolean;
29
+ state?: McpToolRenderState;
30
+ }
31
+ export type McpToolResultRendering = "compact" | "boxed";
32
+ export interface McpToolRenderOptions {
33
+ resultRendering: McpToolResultRendering;
34
+ collapsedResultLines: 1 | 2 | 3;
35
+ }
36
+ export interface McpToolRenderSettings {
37
+ toolResultRendering?: unknown;
38
+ collapsedResultLines?: unknown;
39
+ }
40
+ export interface McpToolResultDisplay {
41
+ lines: string[];
42
+ truncated: boolean;
43
+ }
44
+ declare class EmptyComponent implements Component {
45
+ render(): string[];
46
+ invalidate(): void;
47
+ }
48
+ declare class CompactMcpToolResult implements Component {
49
+ private rendered;
50
+ private readonly title;
51
+ private readonly inputPreview;
52
+ private readonly display;
53
+ private readonly theme;
54
+ constructor(title: string, inputPreview: string, display: McpToolResultDisplay, theme: RenderTheme);
55
+ render(width: number): string[];
56
+ invalidate(): void;
57
+ private renderPrefix;
58
+ }
59
+ declare class CollapsibleText implements Component {
60
+ private readonly fullText;
61
+ private readonly footerText;
62
+ private collapsedText;
63
+ private collapsedRender;
64
+ private readonly text;
65
+ private readonly expanded;
66
+ private readonly maxCollapsedLines;
67
+ private readonly preTruncated;
68
+ constructor(text: string, expanded: boolean, maxCollapsedLines: number, ellipsis: string, expandHint: string, preTruncated?: boolean);
69
+ render(width: number): string[];
70
+ invalidate(): void;
71
+ }
72
+ export declare function formatMcpProxyToolCallLines(args: McpProxyToolCallInput, maxInputChars?: number): string[];
73
+ export declare function formatMcpDirectToolCallLines(displayName: string, args: Record<string, unknown>, maxInputChars?: number): string[];
74
+ export declare function resolveMcpToolRenderOptions(settings?: McpToolRenderSettings): McpToolRenderOptions;
75
+ export declare function renderMcpProxyToolCall(args: McpProxyToolCallInput, theme?: RenderTheme, context?: McpToolRenderContext): EmptyComponent | Text;
76
+ export declare function createMcpProxyToolCallRenderer(options: McpToolRenderOptions): (args: McpProxyToolCallInput, theme?: RenderTheme | undefined, context?: McpToolRenderContext | undefined) => EmptyComponent | Text;
77
+ export declare function createMcpDirectToolCallRenderer(displayName: string, options?: McpToolRenderOptions): (args: Record<string, unknown>, theme?: RenderTheme | undefined, context?: McpToolRenderContext | undefined) => EmptyComponent | Text;
78
+ export declare function formatMcpToolResultIdentity(details: McpToolResultDetails | undefined): string | null;
79
+ export declare function formatMcpToolResultLines(result: Pick<AgentToolResult<McpToolResultDetails>, "content">, expanded: boolean, maxCollapsedLines?: number, maxCollapsedChars?: number): McpToolResultDisplay;
80
+ export declare function renderMcpToolResult(result: AgentToolResult<McpToolResultDetails>, options: ToolRenderResultOptions, theme?: RenderTheme, context?: McpToolRenderContext, renderOptions?: McpToolRenderOptions): CollapsibleText | CompactMcpToolResult | Text;
81
+ export declare function createMcpToolResultRenderer(renderOptions: McpToolRenderOptions): (result: AgentToolResult<McpToolResultDetails>, options: ToolRenderResultOptions, theme?: RenderTheme | undefined, context?: McpToolRenderContext | undefined) => CollapsibleText | CompactMcpToolResult | Text;
82
+ export {};
83
+ //# sourceMappingURL=tool-result-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-result-renderer.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/tool-result-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, Text, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { AgentToolResult, ToolRenderResultOptions } from \"../../core/extensions/types.ts\";\n\ntype McpToolResultDetails = Record<string, unknown> & { error?: unknown };\ntype McpToolContentBlock = AgentToolResult<McpToolResultDetails>[\"content\"][number];\n\ninterface RenderTheme {\n\tfg: (name: string, text: string) => string;\n\tbold?: (text: string) => string;\n}\n\nconst plainTheme: RenderTheme = { fg: (_name, text) => text };\n\nexport interface McpProxyToolCallInput {\n\ttool?: string;\n\targs?: string | Record<string, unknown>;\n\tconnect?: string;\n\tdescribe?: string;\n\tsearch?: string;\n\tregex?: boolean;\n\tincludeSchemas?: boolean;\n\tserver?: string;\n\taction?: string;\n}\n\ninterface McpToolRenderState {\n\tcompactTitle?: string;\n\tcompactInputPreview?: string;\n}\n\ninterface McpToolRenderContext {\n\tisError: boolean;\n\tisPartial?: boolean;\n\texpanded?: boolean;\n\tstate?: McpToolRenderState;\n}\n\nexport type McpToolResultRendering = \"compact\" | \"boxed\";\n\nexport interface McpToolRenderOptions {\n\tresultRendering: McpToolResultRendering;\n\tcollapsedResultLines: 1 | 2 | 3;\n}\n\nexport interface McpToolRenderSettings {\n\ttoolResultRendering?: unknown;\n\tcollapsedResultLines?: unknown;\n}\n\nexport interface McpToolResultDisplay {\n\tlines: string[];\n\ttruncated: boolean;\n}\n\nconst DEFAULT_MAX_CALL_INPUT_CHARS = 1500;\nconst DEFAULT_MAX_COMPACT_INPUT_CHARS = 240;\nconst DEFAULT_BOXED_COLLAPSED_LINES = 3;\nconst DEFAULT_COMPACT_COLLAPSED_LINES = 1;\nconst DEFAULT_MAX_COLLAPSED_CHARS = 8000;\nconst COLLAPSED_RENDER_CHAR_SLACK = 8;\n\nclass EmptyComponent implements Component {\n\trender(): string[] {\n\t\treturn [];\n\t}\n\n\tinvalidate(): void {}\n}\n\nclass CompactMcpToolResult implements Component {\n\tprivate rendered: { width: number; lines: string[] } | null = null;\n\tprivate readonly title: string;\n\tprivate readonly inputPreview: string;\n\tprivate readonly display: McpToolResultDisplay;\n\tprivate readonly theme: RenderTheme;\n\n\tconstructor(title: string, inputPreview: string, display: McpToolResultDisplay, theme: RenderTheme) {\n\t\tthis.title = title;\n\t\tthis.inputPreview = inputPreview;\n\t\tthis.display = display;\n\t\tthis.theme = theme;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst safeWidth = Math.max(1, Math.floor(width));\n\t\tif (this.rendered?.width === safeWidth) return this.rendered.lines;\n\n\t\tconst resultLines = this.display.lines.filter((line, index, lines) => {\n\t\t\treturn !(this.display.truncated && index === lines.length - 1 && line === \"…\");\n\t\t});\n\t\tconst lines = resultLines.length > 0 ? resultLines : [\"\"];\n\t\tconst bodies = lines.map((line, index) => {\n\t\t\tconst prefix = index === 0 ? this.renderPrefix(safeWidth) : \"\";\n\t\t\treturn `${prefix}${this.theme.fg(\"toolOutput\", line)}`;\n\t\t});\n\t\tconst hiddenText = this.display.truncated || bodies.some((body) => visibleWidth(body) > safeWidth);\n\t\tconst rendered = bodies.map((body, index) => {\n\t\t\tconst suffix = hiddenText && index === bodies.length - 1 ? \" … (Ctrl+O to expand)\" : \"\";\n\t\t\tif (!suffix) return truncateToWidth(body, safeWidth, \"…\");\n\t\t\tif (safeWidth >= suffix.length + 20) {\n\t\t\t\treturn `${truncateToWidth(body, safeWidth - suffix.length, \"…\")}${this.theme.fg(\"muted\", suffix)}`;\n\t\t\t}\n\t\t\tconst shortSuffix = \" (Ctrl+O)\";\n\t\t\tif (safeWidth >= shortSuffix.length + 5) {\n\t\t\t\treturn `${truncateToWidth(body, safeWidth - shortSuffix.length, \"…\")}${this.theme.fg(\"muted\", shortSuffix)}`;\n\t\t\t}\n\t\t\treturn truncateToWidth(this.theme.fg(\"muted\", shortSuffix.trim()), safeWidth, \"…\");\n\t\t});\n\t\tthis.rendered = { width: safeWidth, lines: rendered };\n\t\treturn rendered;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.rendered = null;\n\t}\n\n\tprivate renderPrefix(width: number): string {\n\t\tif (!this.title) return \"\";\n\t\tconst arrow = \" → \";\n\t\tif (!this.inputPreview) return `${this.theme.fg(\"toolTitle\", this.title)}${arrow}`;\n\n\t\tconst maxPrefixWidth = Math.max(12, Math.floor(width * 0.55));\n\t\tconst titleWidth = visibleWidth(this.title);\n\t\tconst inputWidth = Math.max(0, maxPrefixWidth - titleWidth - 1);\n\t\tif (inputWidth <= 3) {\n\t\t\treturn `${this.theme.fg(\"toolTitle\", truncateToWidth(this.title, maxPrefixWidth, \"…\"))}${arrow}`;\n\t\t}\n\n\t\tconst input = truncateToWidth(this.inputPreview, inputWidth, \"…\");\n\t\treturn `${this.theme.fg(\"toolTitle\", this.title)} ${this.theme.fg(\"muted\", input)}${arrow}`;\n\t}\n}\n\nclass CollapsibleText implements Component {\n\tprivate readonly fullText: Text;\n\tprivate readonly footerText: Text;\n\tprivate collapsedText: { charBudget: number; fullyIncluded: boolean; text: Text } | null = null;\n\tprivate collapsedRender: { width: number; charBudget: number; lines: string[] } | null = null;\n\tprivate readonly text: string;\n\tprivate readonly expanded: boolean;\n\tprivate readonly maxCollapsedLines: number;\n\tprivate readonly preTruncated: boolean;\n\n\tconstructor(\n\t\ttext: string,\n\t\texpanded: boolean,\n\t\tmaxCollapsedLines: number,\n\t\tellipsis: string,\n\t\texpandHint: string,\n\t\tpreTruncated = false,\n\t) {\n\t\tthis.text = text;\n\t\tthis.expanded = expanded;\n\t\tthis.maxCollapsedLines = maxCollapsedLines;\n\t\tthis.preTruncated = preTruncated;\n\t\tthis.fullText = new Text(text, 0, 0);\n\t\tthis.footerText = new Text(`${ellipsis}\\n${expandHint}`, 0, 0);\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.expanded) {\n\t\t\treturn this.fullText.render(width);\n\t\t}\n\n\t\tconst safeWidth = Math.max(1, Math.floor(width));\n\t\tconst charBudget = safeWidth * (this.maxCollapsedLines + 1) * COLLAPSED_RENDER_CHAR_SLACK;\n\t\tif (!this.collapsedText || this.collapsedText.charBudget !== charBudget) {\n\t\t\tconst prefix = this.text.length > charBudget ? this.text.slice(0, charBudget) : this.text;\n\t\t\tthis.collapsedText = {\n\t\t\t\tcharBudget,\n\t\t\t\tfullyIncluded: prefix === this.text,\n\t\t\t\ttext: new Text(prefix, 0, 0),\n\t\t\t};\n\t\t\tthis.collapsedRender = null;\n\t\t}\n\n\t\tconst lines = this.collapsedText.text.render(width);\n\t\tif (!this.preTruncated && this.collapsedText.fullyIncluded && lines.length <= this.maxCollapsedLines)\n\t\t\treturn lines;\n\t\tif (this.collapsedRender?.width === width && this.collapsedRender.charBudget === charBudget) {\n\t\t\treturn this.collapsedRender.lines;\n\t\t}\n\n\t\tconst rendered = [...lines.slice(0, this.maxCollapsedLines), ...this.footerText.render(width)];\n\t\tthis.collapsedRender = { width, charBudget, lines: rendered };\n\t\treturn rendered;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.fullText.invalidate();\n\t\tthis.footerText.invalidate();\n\t\tthis.collapsedText?.text.invalidate();\n\t\tthis.collapsedRender = null;\n\t}\n}\n\nfunction truncateText(value: string, maxChars: number): string {\n\tif (value.length <= maxChars) return value;\n\treturn `${value.slice(0, Math.max(0, maxChars - 1))}…`;\n}\n\nfunction formatJsonish(value: unknown, maxChars: number): string {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn truncateText(JSON.stringify(JSON.parse(value), null, 2), maxChars);\n\t\t} catch {\n\t\t\treturn truncateText(value, maxChars);\n\t\t}\n\t}\n\n\ttry {\n\t\treturn truncateText(JSON.stringify(value, null, 2), maxChars);\n\t} catch {\n\t\treturn truncateText(String(value), maxChars);\n\t}\n}\n\nfunction hasUsefulObjectContent(value: unknown): boolean {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value) && Object.keys(value).length > 0;\n}\n\nexport function formatMcpProxyToolCallLines(\n\targs: McpProxyToolCallInput,\n\tmaxInputChars = DEFAULT_MAX_CALL_INPUT_CHARS,\n): string[] {\n\tif (args.action === \"ui-messages\") return [`mcp ${args.action}`];\n\n\tif (args.tool) {\n\t\tconst target = args.server ? `${args.tool} @ ${args.server}` : args.tool;\n\t\tconst lines = [`mcp call ${target}`];\n\t\tif (args.args) lines.push(formatJsonish(args.args, maxInputChars));\n\t\treturn lines;\n\t}\n\n\tif (args.connect) return [`mcp connect ${args.connect}`];\n\tif (args.describe) return [`mcp describe ${args.describe}`];\n\n\tif (args.search) {\n\t\tlet line = `mcp search ${args.search}`;\n\t\tif (args.server) line += ` @ ${args.server}`;\n\t\tif (args.regex === true) line += \" (regex)\";\n\t\tif (args.includeSchemas === false) line += \" (schemas hidden)\";\n\t\treturn [line];\n\t}\n\n\tif (args.server) return [`mcp list ${args.server}`];\n\tif (args.action) return [`mcp ${args.action}`];\n\n\treturn [\"mcp status\"];\n}\n\nexport function formatMcpDirectToolCallLines(\n\tdisplayName: string,\n\targs: Record<string, unknown>,\n\tmaxInputChars = DEFAULT_MAX_CALL_INPUT_CHARS,\n): string[] {\n\tif (!hasUsefulObjectContent(args)) return [displayName];\n\treturn [displayName, formatJsonish(args, maxInputChars)];\n}\n\nfunction renderToolCallLines(lines: string[], theme?: RenderTheme) {\n\tconst activeTheme = theme ?? plainTheme;\n\tconst [title = \"mcp\", ...rest] = lines;\n\tconst styledTitle = activeTheme.fg(\"toolTitle\", activeTheme.bold ? activeTheme.bold(title) : title);\n\tconst styledRest = rest.map((line) => activeTheme.fg(\"muted\", line));\n\treturn new Text([styledTitle, ...styledRest].join(\"\\n\"), 0, 0);\n}\n\nfunction formatCompactInputPreview(lines: string[], maxChars = DEFAULT_MAX_COMPACT_INPUT_CHARS): string {\n\treturn truncateText(lines.slice(1).join(\" \").replace(/\\s+/g, \" \").trim(), maxChars);\n}\n\nexport function resolveMcpToolRenderOptions(settings?: McpToolRenderSettings): McpToolRenderOptions {\n\tconst resultRendering = settings?.toolResultRendering === \"boxed\" ? \"boxed\" : \"compact\";\n\tconst collapsedLines = settings?.collapsedResultLines;\n\tconst defaultLines = resultRendering === \"boxed\" ? DEFAULT_BOXED_COLLAPSED_LINES : DEFAULT_COMPACT_COLLAPSED_LINES;\n\treturn {\n\t\tresultRendering,\n\t\tcollapsedResultLines:\n\t\t\tcollapsedLines === 1 || collapsedLines === 2 || collapsedLines === 3 ? collapsedLines : defaultLines,\n\t};\n}\n\nfunction shouldUseCompactFinalRender(options: McpToolRenderOptions, context?: McpToolRenderContext): boolean {\n\treturn (\n\t\toptions.resultRendering === \"compact\" &&\n\t\tcontext !== undefined &&\n\t\tcontext.isPartial === false &&\n\t\tcontext.expanded !== true &&\n\t\tcontext.isError !== true\n\t);\n}\n\nfunction renderToolCall(\n\tlines: string[],\n\ttheme: RenderTheme | undefined,\n\tcontext: McpToolRenderContext | undefined,\n\toptions: McpToolRenderOptions,\n) {\n\tif (context?.state) {\n\t\tcontext.state.compactTitle = lines[0] ?? \"mcp\";\n\t\tcontext.state.compactInputPreview = formatCompactInputPreview(lines);\n\t}\n\tif (shouldUseCompactFinalRender(options, context)) return new EmptyComponent();\n\treturn renderToolCallLines(lines, theme);\n}\n\nexport function renderMcpProxyToolCall(\n\targs: McpProxyToolCallInput,\n\ttheme?: RenderTheme,\n\tcontext?: McpToolRenderContext,\n) {\n\treturn renderToolCall(formatMcpProxyToolCallLines(args), theme, context, resolveMcpToolRenderOptions());\n}\n\nexport function createMcpProxyToolCallRenderer(options: McpToolRenderOptions) {\n\treturn (args: McpProxyToolCallInput, theme?: RenderTheme, context?: McpToolRenderContext) => {\n\t\treturn renderToolCall(formatMcpProxyToolCallLines(args), theme, context, options);\n\t};\n}\n\nexport function createMcpDirectToolCallRenderer(displayName: string, options = resolveMcpToolRenderOptions()) {\n\treturn (args: Record<string, unknown>, theme?: RenderTheme, context?: McpToolRenderContext) => {\n\t\treturn renderToolCall(formatMcpDirectToolCallLines(displayName, args), theme, context, options);\n\t};\n}\n\nfunction blockToLines(block: McpToolContentBlock): string[] {\n\tif (block.type === \"text\") {\n\t\treturn block.text.split(\"\\n\");\n\t}\n\treturn [`[image: ${block.mimeType}]`];\n}\n\nfunction collectCollapsedResultLines(\n\tcontent: AgentToolResult<McpToolResultDetails>[\"content\"],\n\tmaxLines: number,\n\tmaxChars: number,\n): McpToolResultDisplay {\n\tif (content.length === 0) return { lines: [\"(empty result)\"], truncated: false };\n\n\tconst lines: string[] = [];\n\tlet remainingChars = maxChars;\n\tlet truncated = false;\n\n\tconst appendLine = (line: string) => {\n\t\tif (lines.length === 0) {\n\t\t\tconst previewWidth = Math.min(line.length, remainingChars);\n\t\t\tif (line.slice(0, previewWidth).trim() === \"\") {\n\t\t\t\tif (line.length >= remainingChars) {\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\tremainingChars = 0;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tremainingChars -= line.length + 1;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tif (lines.length >= maxLines || remainingChars <= 0) {\n\t\t\ttruncated = true;\n\t\t\treturn false;\n\t\t}\n\n\t\tif (line.length > remainingChars) {\n\t\t\tlines.push(line.slice(0, remainingChars));\n\t\t\ttruncated = true;\n\t\t\tremainingChars = 0;\n\t\t\treturn false;\n\t\t}\n\n\t\tlines.push(line);\n\t\tremainingChars -= line.length + 1;\n\t\treturn true;\n\t};\n\n\tfor (const block of content) {\n\t\tif (block.type !== \"text\") {\n\t\t\tif (!appendLine(`[image: ${block.mimeType}]`)) break;\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet start = 0;\n\t\twhile (start <= block.text.length) {\n\t\t\tconst newline = block.text.indexOf(\"\\n\", start);\n\t\t\tconst line = newline === -1 ? block.text.slice(start) : block.text.slice(start, newline);\n\t\t\tif (!appendLine(line)) break;\n\t\t\tif (newline === -1) break;\n\t\t\tstart = newline + 1;\n\t\t}\n\n\t\tif (truncated) break;\n\t}\n\n\tif (lines.length === 0) lines.push(truncated ? \"(leading blank output omitted)\" : \"\");\n\tif (truncated && lines.length >= maxLines) lines.push(\"…\");\n\treturn { lines, truncated };\n}\n\nexport function formatMcpToolResultIdentity(details: McpToolResultDetails | undefined): string | null {\n\tif (details?.mode !== \"call\") return null;\n\tconst server =\n\t\ttypeof details.server === \"string\"\n\t\t\t? details.server\n\t\t\t: typeof details.hintServer === \"string\"\n\t\t\t\t? details.hintServer\n\t\t\t\t: null;\n\tif (!server) return null;\n\tif (typeof details.tool === \"string\") return `MCP ${server}/${details.tool}`;\n\tif (typeof details.resourceUri === \"string\") return `MCP ${server} resource ${details.resourceUri}`;\n\tif (typeof details.requestedTool === \"string\") return `MCP ${server}/${details.requestedTool}`;\n\treturn null;\n}\n\nexport function formatMcpToolResultLines(\n\tresult: Pick<AgentToolResult<McpToolResultDetails>, \"content\">,\n\texpanded: boolean,\n\tmaxCollapsedLines = DEFAULT_BOXED_COLLAPSED_LINES,\n\tmaxCollapsedChars = DEFAULT_MAX_COLLAPSED_CHARS,\n): McpToolResultDisplay {\n\tif (!expanded) {\n\t\treturn collectCollapsedResultLines(result.content, maxCollapsedLines, maxCollapsedChars);\n\t}\n\n\tconst allLines = result.content.flatMap(blockToLines);\n\tconst lines = allLines.length > 0 ? allLines : [\"(empty result)\"];\n\treturn { lines, truncated: false };\n}\n\nexport function renderMcpToolResult(\n\tresult: AgentToolResult<McpToolResultDetails>,\n\toptions: ToolRenderResultOptions,\n\ttheme?: RenderTheme,\n\tcontext?: McpToolRenderContext,\n\trenderOptions = resolveMcpToolRenderOptions(),\n) {\n\tconst activeTheme = theme ?? plainTheme;\n\tif (options.isPartial) {\n\t\treturn new Text(activeTheme.fg(\"warning\", \"Running MCP tool...\"), 0, 0);\n\t}\n\n\tconst hasErrorDetails = Boolean(result.details.error);\n\tconst expanded = options.expanded || context?.isError === true || hasErrorDetails;\n\tif (!expanded && renderOptions.resultRendering === \"compact\") {\n\t\tconst display = formatMcpToolResultLines(result, false, renderOptions.collapsedResultLines);\n\t\tconst title = context?.state?.compactTitle ?? formatMcpToolResultIdentity(result.details) ?? \"\";\n\t\tconst inputPreview = context?.state?.compactInputPreview ?? \"\";\n\t\treturn new CompactMcpToolResult(title, inputPreview, display, activeTheme);\n\t}\n\n\tconst display = formatMcpToolResultLines(result, expanded, renderOptions.collapsedResultLines);\n\tconst identity = formatMcpToolResultIdentity(result.details);\n\tconst output = [\n\t\t...(identity ? [activeTheme.fg(\"muted\", identity)] : []),\n\t\t...display.lines.map((line) => activeTheme.fg(\"toolOutput\", line)),\n\t].join(\"\\n\");\n\n\treturn new CollapsibleText(\n\t\toutput,\n\t\texpanded,\n\t\trenderOptions.collapsedResultLines + (identity ? 1 : 0),\n\t\tactiveTheme.fg(\"muted\", \"…\"),\n\t\tactiveTheme.fg(\"muted\", \"(Ctrl+O to expand)\"),\n\t\tdisplay.truncated,\n\t);\n}\n\nexport function createMcpToolResultRenderer(renderOptions: McpToolRenderOptions) {\n\treturn (\n\t\tresult: AgentToolResult<McpToolResultDetails>,\n\t\toptions: ToolRenderResultOptions,\n\t\ttheme?: RenderTheme,\n\t\tcontext?: McpToolRenderContext,\n\t) => renderMcpToolResult(result, options, theme, context, renderOptions);\n}\n"]}