@herbertgao/pi-extensions 2026.8.1 → 2026.8.3

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 (403) hide show
  1. package/README.md +13 -3
  2. package/node_modules/@czottmann/pi-automode/README.md +16 -2
  3. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +35 -0
  4. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +18 -9
  5. package/node_modules/@czottmann/pi-automode/docs/defaults.md +6 -2
  6. package/node_modules/@czottmann/pi-automode/examples/automode.local.json +6 -0
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +11 -7
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +100 -0
  9. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +32 -0
  10. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +78 -3
  11. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +16 -4
  12. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +23 -1
  13. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +22 -1
  14. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/types.ts +18 -2
  15. package/node_modules/@czottmann/pi-automode/package.json +4 -4
  16. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +5 -0
  17. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +5 -0
  18. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +5 -0
  19. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +5 -0
  20. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +5 -0
  21. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +5 -0
  22. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail-frontmatter.cjs +23 -0
  23. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail.mjs +99 -0
  24. package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +30 -0
  25. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +16 -0
  26. package/node_modules/@dietrichgebert/ponytail/AGENTS.md +32 -0
  27. package/node_modules/@dietrichgebert/ponytail/LICENSE +21 -0
  28. package/node_modules/@dietrichgebert/ponytail/README.es.md +298 -0
  29. package/node_modules/@dietrichgebert/ponytail/README.ko.md +315 -0
  30. package/node_modules/@dietrichgebert/ponytail/README.md +351 -0
  31. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-3model.svg +21 -0
  32. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-agentic.svg +62 -0
  33. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.png +0 -0
  34. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.svg +115 -0
  35. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt-dark.svg +27 -0
  36. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt.svg +27 -0
  37. package/node_modules/@dietrichgebert/ponytail/assets/logo.png +0 -0
  38. package/node_modules/@dietrichgebert/ponytail/assets/social-preview.png +0 -0
  39. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-es.png +0 -0
  40. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-ko.png +0 -0
  41. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner.png +0 -0
  42. package/node_modules/@dietrichgebert/ponytail/hooks/claude-codex-hooks.json +44 -0
  43. package/node_modules/@dietrichgebert/ponytail/hooks/copilot-hooks.json +21 -0
  44. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +96 -0
  45. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-config.js +169 -0
  46. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +98 -0
  47. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-mode-tracker.js +130 -0
  48. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-runtime.js +85 -0
  49. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-statusline.ps1 +24 -0
  50. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-statusline.sh +18 -0
  51. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-subagent.js +77 -0
  52. package/node_modules/@dietrichgebert/ponytail/hooks/qoder-hooks.json +26 -0
  53. package/node_modules/@dietrichgebert/ponytail/package.json +47 -0
  54. package/node_modules/@dietrichgebert/ponytail/pi-extension/index.js +211 -0
  55. package/node_modules/@dietrichgebert/ponytail/pi-extension/package.json +8 -0
  56. package/node_modules/@dietrichgebert/ponytail/scripts/uninstall.js +60 -0
  57. package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +120 -0
  58. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +41 -0
  59. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +44 -0
  60. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +50 -0
  61. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +71 -0
  62. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +57 -0
  63. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +6 -10
  64. package/node_modules/@herbertgao/pi-cc-extensions/README.md +5 -9
  65. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +101 -0
  66. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +1 -17
  67. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +15 -9
  68. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{utils/agent-summary.ts → feature/agent-summary/core.ts} +37 -35
  69. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-summary.ts → agent-summary/index.ts} +3 -3
  70. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -83
  71. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +1 -1
  72. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference.ts → reference/index.ts} +1 -1
  73. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-autocomplete.ts → reference/subagent.ts} +19 -9
  74. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-startup-header.ts → shell/startup-header.ts} +1 -1
  75. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{working-message.ts → shell/working-message.ts} +5 -2
  76. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +32 -28
  77. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +452 -94
  78. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +727 -0
  79. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +66 -776
  80. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{feature → renderer}/markdown-enhance.ts +134 -11
  81. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +130 -0
  82. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-interaction.ts → mouse/interaction.ts} +90 -568
  83. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +144 -0
  84. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +143 -0
  85. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +195 -0
  86. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ansi-utils.ts +56 -1
  87. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-renderer.ts +4 -4
  88. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/line-width-safety.ts +1 -1
  89. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +12 -0
  90. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/write-execution.ts +1 -2
  91. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-grouping.ts → tool/grouping.ts} +27 -10
  92. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-result.ts → tool/result.ts} +4 -13
  93. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +91 -0
  94. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +0 -27
  95. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  96. package/node_modules/@herbertgao/pi-cc-extensions/themes/cc-dark.json +1 -0
  97. package/node_modules/@herbertgao/pi-cc-extensions/themes/cc-light.json +2 -2
  98. package/node_modules/@herbertgao/pi-stash/README.md +9 -9
  99. package/node_modules/@herbertgao/pi-stash/package.json +1 -1
  100. package/node_modules/@herbertgao/pi-stash/src/index.ts +2 -1
  101. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +20 -0
  102. package/node_modules/@herbertgao/pi-subagents/README.md +6 -2
  103. package/node_modules/@herbertgao/pi-subagents/package.json +3 -3
  104. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +1 -0
  105. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +3 -1
  106. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +85 -11
  107. package/node_modules/@herbertgao/pi-subagents/src/index.ts +61 -39
  108. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +26 -35
  109. package/node_modules/@herbertgao/pi-subagents/src/prompts.ts +15 -1
  110. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +12 -0
  111. package/node_modules/@herbertgao/pi-subagents/src/types.ts +2 -0
  112. package/node_modules/@narumitw/pi-btw/LICENSE +21 -0
  113. package/node_modules/@narumitw/pi-btw/README.md +210 -0
  114. package/node_modules/@narumitw/pi-btw/package.json +53 -0
  115. package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +632 -0
  116. package/node_modules/@narumitw/pi-btw/src/btw.ts +996 -0
  117. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +339 -0
  118. package/node_modules/@narumitw/pi-btw/src/index.ts +1 -0
  119. package/node_modules/@narumitw/pi-btw/src/menu.ts +296 -0
  120. package/node_modules/@narumitw/pi-btw/src/settings.ts +237 -0
  121. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +238 -0
  122. package/node_modules/@narumitw/pi-btw/src/text.ts +10 -0
  123. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +620 -0
  124. package/node_modules/@pi-plugins/fast-mode/LICENSE +21 -0
  125. package/node_modules/@pi-plugins/fast-mode/README.md +92 -0
  126. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts +6 -0
  127. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -0
  128. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +2026 -0
  129. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -0
  130. package/node_modules/@pi-plugins/fast-mode/package.json +73 -0
  131. package/node_modules/pi-mcp-adapter/CHANGELOG.md +78 -0
  132. package/node_modules/pi-mcp-adapter/README.md +80 -16
  133. package/node_modules/pi-mcp-adapter/agent-dir.ts +40 -0
  134. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +377 -0
  135. package/node_modules/pi-mcp-adapter/commands.ts +21 -9
  136. package/node_modules/pi-mcp-adapter/config.ts +110 -49
  137. package/node_modules/pi-mcp-adapter/direct-tools.ts +40 -27
  138. package/node_modules/pi-mcp-adapter/elicitation-handler.ts +37 -36
  139. package/node_modules/pi-mcp-adapter/errors.ts +21 -17
  140. package/node_modules/pi-mcp-adapter/host-html-template.ts +13 -7
  141. package/node_modules/pi-mcp-adapter/index.ts +52 -17
  142. package/node_modules/pi-mcp-adapter/init.ts +9 -7
  143. package/node_modules/pi-mcp-adapter/json-schema-validator.ts +2 -2
  144. package/node_modules/pi-mcp-adapter/lifecycle.ts +8 -8
  145. package/node_modules/pi-mcp-adapter/logger.ts +1 -1
  146. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +122 -25
  147. package/node_modules/pi-mcp-adapter/mcp-auth.ts +120 -2
  148. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +138 -61
  149. package/node_modules/pi-mcp-adapter/mcp-code.ts +13 -11
  150. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +62 -17
  151. package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +15 -11
  152. package/node_modules/pi-mcp-adapter/mcp-panel.ts +34 -15
  153. package/node_modules/pi-mcp-adapter/mcp-probe.ts +131 -34
  154. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +2 -2
  155. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +7 -4
  156. package/node_modules/pi-mcp-adapter/mcp-trace.ts +59 -64
  157. package/node_modules/pi-mcp-adapter/metadata-cache.ts +34 -26
  158. package/node_modules/pi-mcp-adapter/npx-resolver.ts +6 -4
  159. package/node_modules/pi-mcp-adapter/oauth-handler.ts +1 -1
  160. package/node_modules/pi-mcp-adapter/oauth.ts +48 -0
  161. package/node_modules/pi-mcp-adapter/onboarding-state.ts +19 -11
  162. package/node_modules/pi-mcp-adapter/package.json +20 -9
  163. package/node_modules/pi-mcp-adapter/panel-keys.ts +20 -4
  164. package/node_modules/pi-mcp-adapter/prompts.ts +2 -2
  165. package/node_modules/pi-mcp-adapter/proxy-modes.ts +56 -21
  166. package/node_modules/pi-mcp-adapter/sampling-handler.ts +22 -15
  167. package/node_modules/pi-mcp-adapter/search-ranking.ts +2 -1
  168. package/node_modules/pi-mcp-adapter/server-manager.ts +165 -113
  169. package/node_modules/pi-mcp-adapter/session-recovery.ts +11 -12
  170. package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +3 -3
  171. package/node_modules/pi-mcp-adapter/state.ts +3 -1
  172. package/node_modules/pi-mcp-adapter/tool-approval.ts +67 -4
  173. package/node_modules/pi-mcp-adapter/tool-metadata.ts +6 -5
  174. package/node_modules/pi-mcp-adapter/tool-registrar.ts +187 -3
  175. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +14 -2
  176. package/node_modules/pi-mcp-adapter/ts-shape.ts +9 -2
  177. package/node_modules/pi-mcp-adapter/types.ts +111 -22
  178. package/node_modules/pi-mcp-adapter/ui-app-bridge-helpers.ts +40 -0
  179. package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +28 -19
  180. package/node_modules/pi-mcp-adapter/ui-server.ts +90 -58
  181. package/node_modules/pi-mcp-adapter/ui-session.ts +16 -14
  182. package/node_modules/pi-mcp-adapter/unix-socket-transport.ts +4 -4
  183. package/node_modules/pi-mcp-adapter/utils.ts +23 -9
  184. package/node_modules/pi-web-access/CHANGELOG.md +45 -0
  185. package/node_modules/pi-web-access/README.md +104 -29
  186. package/node_modules/pi-web-access/bocha.ts +221 -0
  187. package/node_modules/pi-web-access/curator-page.ts +113 -12
  188. package/node_modules/pi-web-access/curator-server.ts +56 -20
  189. package/node_modules/pi-web-access/datalab-pdf-extract.ts +568 -0
  190. package/node_modules/pi-web-access/duckduckgo.ts +123 -0
  191. package/node_modules/pi-web-access/extract.ts +288 -143
  192. package/node_modules/pi-web-access/feature-config.ts +29 -0
  193. package/node_modules/pi-web-access/gemini-api.ts +4 -0
  194. package/node_modules/pi-web-access/gemini-search.ts +45 -12
  195. package/node_modules/pi-web-access/gemini-web.ts +68 -3
  196. package/node_modules/pi-web-access/index.ts +160 -71
  197. package/node_modules/pi-web-access/jina-search.ts +284 -0
  198. package/node_modules/pi-web-access/kagi.ts +12 -12
  199. package/node_modules/pi-web-access/openai-search.ts +12 -2
  200. package/node_modules/pi-web-access/package.json +4 -3
  201. package/node_modules/pi-web-access/pdf-extract.ts +327 -228
  202. package/node_modules/pi-web-access/storage.ts +440 -2
  203. package/node_modules/pi-web-access/summary-model-scope.ts +1 -1
  204. package/node_modules/pi-web-access/summary-review.ts +4 -2
  205. package/node_modules/pi-web-access/video-extract.ts +10 -8
  206. package/node_modules/pi-web-access/xai-search.ts +12 -2
  207. package/node_modules/pi-web-access/youtube-extract.ts +2 -1
  208. package/node_modules/remote-pi/LICENSE +21 -0
  209. package/node_modules/remote-pi/README.md +675 -0
  210. package/node_modules/remote-pi/dist/actions/handlers.d.ts +122 -0
  211. package/node_modules/remote-pi/dist/actions/handlers.js +160 -0
  212. package/node_modules/remote-pi/dist/actions/handlers.js.map +1 -0
  213. package/node_modules/remote-pi/dist/actions/registry.d.ts +39 -0
  214. package/node_modules/remote-pi/dist/actions/registry.js +58 -0
  215. package/node_modules/remote-pi/dist/actions/registry.js.map +1 -0
  216. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +2 -0
  217. package/node_modules/remote-pi/dist/bin/supervisord.js +86 -0
  218. package/node_modules/remote-pi/dist/bin/supervisord.js.map +1 -0
  219. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +58 -0
  220. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +71 -0
  221. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +1 -0
  222. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +60 -0
  223. package/node_modules/remote-pi/dist/commands/list_commands.js +73 -0
  224. package/node_modules/remote-pi/dist/commands/list_commands.js.map +1 -0
  225. package/node_modules/remote-pi/dist/config.d.ts +62 -0
  226. package/node_modules/remote-pi/dist/config.js +115 -0
  227. package/node_modules/remote-pi/dist/config.js.map +1 -0
  228. package/node_modules/remote-pi/dist/daemon/client.d.ts +20 -0
  229. package/node_modules/remote-pi/dist/daemon/client.js +131 -0
  230. package/node_modules/remote-pi/dist/daemon/client.js.map +1 -0
  231. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +176 -0
  232. package/node_modules/remote-pi/dist/daemon/control_protocol.js +63 -0
  233. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +1 -0
  234. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +45 -0
  235. package/node_modules/remote-pi/dist/daemon/cron_log.js +72 -0
  236. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +1 -0
  237. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +80 -0
  238. package/node_modules/remote-pi/dist/daemon/cron_registry.js +194 -0
  239. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +1 -0
  240. package/node_modules/remote-pi/dist/daemon/id.d.ts +24 -0
  241. package/node_modules/remote-pi/dist/daemon/id.js +36 -0
  242. package/node_modules/remote-pi/dist/daemon/id.js.map +1 -0
  243. package/node_modules/remote-pi/dist/daemon/install.d.ts +170 -0
  244. package/node_modules/remote-pi/dist/daemon/install.js +547 -0
  245. package/node_modules/remote-pi/dist/daemon/install.js.map +1 -0
  246. package/node_modules/remote-pi/dist/daemon/registry.d.ts +63 -0
  247. package/node_modules/remote-pi/dist/daemon/registry.js +153 -0
  248. package/node_modules/remote-pi/dist/daemon/registry.js.map +1 -0
  249. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +194 -0
  250. package/node_modules/remote-pi/dist/daemon/rpc_child.js +389 -0
  251. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +1 -0
  252. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +93 -0
  253. package/node_modules/remote-pi/dist/daemon/supervisor.js +596 -0
  254. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +1 -0
  255. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +26 -0
  256. package/node_modules/remote-pi/dist/extension_ui_bridge.js +341 -0
  257. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +1 -0
  258. package/node_modules/remote-pi/dist/index.d.ts +213 -0
  259. package/node_modules/remote-pi/dist/index.js +4996 -0
  260. package/node_modules/remote-pi/dist/index.js.map +1 -0
  261. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +10 -0
  262. package/node_modules/remote-pi/dist/mcp/mesh_result.js +42 -0
  263. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +1 -0
  264. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +16 -0
  265. package/node_modules/remote-pi/dist/mcp/mesh_server.js +327 -0
  266. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +1 -0
  267. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +30 -0
  268. package/node_modules/remote-pi/dist/mesh/canonical.js +61 -0
  269. package/node_modules/remote-pi/dist/mesh/canonical.js.map +1 -0
  270. package/node_modules/remote-pi/dist/mesh/client.d.ts +18 -0
  271. package/node_modules/remote-pi/dist/mesh/client.js +125 -0
  272. package/node_modules/remote-pi/dist/mesh/client.js.map +1 -0
  273. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +38 -0
  274. package/node_modules/remote-pi/dist/mesh/encoding.js +216 -0
  275. package/node_modules/remote-pi/dist/mesh/encoding.js.map +1 -0
  276. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +73 -0
  277. package/node_modules/remote-pi/dist/mesh/self_revoke.js +401 -0
  278. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +1 -0
  279. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +49 -0
  280. package/node_modules/remote-pi/dist/mesh/siblings.js +159 -0
  281. package/node_modules/remote-pi/dist/mesh/siblings.js.map +1 -0
  282. package/node_modules/remote-pi/dist/mesh/types.d.ts +34 -0
  283. package/node_modules/remote-pi/dist/mesh/types.js +11 -0
  284. package/node_modules/remote-pi/dist/mesh/types.js.map +1 -0
  285. package/node_modules/remote-pi/dist/mesh/verify.d.ts +17 -0
  286. package/node_modules/remote-pi/dist/mesh/verify.js +86 -0
  287. package/node_modules/remote-pi/dist/mesh/verify.js.map +1 -0
  288. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +8 -0
  289. package/node_modules/remote-pi/dist/pairing/crypto.js +22 -0
  290. package/node_modules/remote-pi/dist/pairing/crypto.js.map +1 -0
  291. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +28 -0
  292. package/node_modules/remote-pi/dist/pairing/handshake.js +113 -0
  293. package/node_modules/remote-pi/dist/pairing/handshake.js.map +1 -0
  294. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +16 -0
  295. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +103 -0
  296. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +1 -0
  297. package/node_modules/remote-pi/dist/pairing/qr.d.ts +59 -0
  298. package/node_modules/remote-pi/dist/pairing/qr.js +125 -0
  299. package/node_modules/remote-pi/dist/pairing/qr.js.map +1 -0
  300. package/node_modules/remote-pi/dist/pairing/storage.d.ts +120 -0
  301. package/node_modules/remote-pi/dist/pairing/storage.js +604 -0
  302. package/node_modules/remote-pi/dist/pairing/storage.js.map +1 -0
  303. package/node_modules/remote-pi/dist/protocol/codec.d.ts +7 -0
  304. package/node_modules/remote-pi/dist/protocol/codec.js +49 -0
  305. package/node_modules/remote-pi/dist/protocol/codec.js.map +1 -0
  306. package/node_modules/remote-pi/dist/protocol/types.d.ts +393 -0
  307. package/node_modules/remote-pi/dist/protocol/types.js +2 -0
  308. package/node_modules/remote-pi/dist/protocol/types.js.map +1 -0
  309. package/node_modules/remote-pi/dist/rooms.d.ts +29 -0
  310. package/node_modules/remote-pi/dist/rooms.js +57 -0
  311. package/node_modules/remote-pi/dist/rooms.js.map +1 -0
  312. package/node_modules/remote-pi/dist/session/address.d.ts +49 -0
  313. package/node_modules/remote-pi/dist/session/address.js +58 -0
  314. package/node_modules/remote-pi/dist/session/address.js.map +1 -0
  315. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +55 -0
  316. package/node_modules/remote-pi/dist/session/agent_bridge.js +146 -0
  317. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +1 -0
  318. package/node_modules/remote-pi/dist/session/bridge.d.ts +37 -0
  319. package/node_modules/remote-pi/dist/session/bridge.js +130 -0
  320. package/node_modules/remote-pi/dist/session/bridge.js.map +1 -0
  321. package/node_modules/remote-pi/dist/session/broker.d.ts +189 -0
  322. package/node_modules/remote-pi/dist/session/broker.js +507 -0
  323. package/node_modules/remote-pi/dist/session/broker.js.map +1 -0
  324. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +74 -0
  325. package/node_modules/remote-pi/dist/session/broker_remote.js +583 -0
  326. package/node_modules/remote-pi/dist/session/broker_remote.js.map +1 -0
  327. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +33 -0
  328. package/node_modules/remote-pi/dist/session/cwd_lock.js +126 -0
  329. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +1 -0
  330. package/node_modules/remote-pi/dist/session/envelope.d.ts +33 -0
  331. package/node_modules/remote-pi/dist/session/envelope.js +117 -0
  332. package/node_modules/remote-pi/dist/session/envelope.js.map +1 -0
  333. package/node_modules/remote-pi/dist/session/global_config.d.ts +33 -0
  334. package/node_modules/remote-pi/dist/session/global_config.js +73 -0
  335. package/node_modules/remote-pi/dist/session/global_config.js.map +1 -0
  336. package/node_modules/remote-pi/dist/session/ipc.d.ts +27 -0
  337. package/node_modules/remote-pi/dist/session/ipc.js +22 -0
  338. package/node_modules/remote-pi/dist/session/ipc.js.map +1 -0
  339. package/node_modules/remote-pi/dist/session/leader_election.d.ts +32 -0
  340. package/node_modules/remote-pi/dist/session/leader_election.js +106 -0
  341. package/node_modules/remote-pi/dist/session/leader_election.js.map +1 -0
  342. package/node_modules/remote-pi/dist/session/local_config.d.ts +55 -0
  343. package/node_modules/remote-pi/dist/session/local_config.js +157 -0
  344. package/node_modules/remote-pi/dist/session/local_config.js.map +1 -0
  345. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +20 -0
  346. package/node_modules/remote-pi/dist/session/mesh_message.js +28 -0
  347. package/node_modules/remote-pi/dist/session/mesh_message.js.map +1 -0
  348. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +116 -0
  349. package/node_modules/remote-pi/dist/session/mesh_node.js +573 -0
  350. package/node_modules/remote-pi/dist/session/mesh_node.js.map +1 -0
  351. package/node_modules/remote-pi/dist/session/peer.d.ts +139 -0
  352. package/node_modules/remote-pi/dist/session/peer.js +451 -0
  353. package/node_modules/remote-pi/dist/session/peer.js.map +1 -0
  354. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +13 -0
  355. package/node_modules/remote-pi/dist/session/peer_inventory.js +48 -0
  356. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +1 -0
  357. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +9 -0
  358. package/node_modules/remote-pi/dist/session/peer_limits.js +21 -0
  359. package/node_modules/remote-pi/dist/session/peer_limits.js.map +1 -0
  360. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +39 -0
  361. package/node_modules/remote-pi/dist/session/setup_wizard.js +63 -0
  362. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +1 -0
  363. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +5 -0
  364. package/node_modules/remote-pi/dist/session/tool_gate.js +11 -0
  365. package/node_modules/remote-pi/dist/session/tool_gate.js.map +1 -0
  366. package/node_modules/remote-pi/dist/session/tools.d.ts +24 -0
  367. package/node_modules/remote-pi/dist/session/tools.js +252 -0
  368. package/node_modules/remote-pi/dist/session/tools.js.map +1 -0
  369. package/node_modules/remote-pi/dist/session/wizard.d.ts +13 -0
  370. package/node_modules/remote-pi/dist/session/wizard.js +20 -0
  371. package/node_modules/remote-pi/dist/session/wizard.js.map +1 -0
  372. package/node_modules/remote-pi/dist/settings.d.ts +15 -0
  373. package/node_modules/remote-pi/dist/settings.js +52 -0
  374. package/node_modules/remote-pi/dist/settings.js.map +1 -0
  375. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +37 -0
  376. package/node_modules/remote-pi/dist/transport/peer_channel.js +98 -0
  377. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +1 -0
  378. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +29 -0
  379. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +62 -0
  380. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +1 -0
  381. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +91 -0
  382. package/node_modules/remote-pi/dist/transport/relay_client.js +206 -0
  383. package/node_modules/remote-pi/dist/transport/relay_client.js.map +1 -0
  384. package/node_modules/remote-pi/dist/ui/footer.d.ts +32 -0
  385. package/node_modules/remote-pi/dist/ui/footer.js +34 -0
  386. package/node_modules/remote-pi/dist/ui/footer.js.map +1 -0
  387. package/node_modules/remote-pi/docs/daemon.md +289 -0
  388. package/node_modules/remote-pi/package.json +88 -0
  389. package/node_modules/remote-pi/service-templates/launchd.plist.template +35 -0
  390. package/node_modules/remote-pi/service-templates/systemd.service.template +19 -0
  391. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +10 -0
  392. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +38 -0
  393. package/node_modules/remote-pi/skills/agent-network/SKILL.md +310 -0
  394. package/package.json +37 -12
  395. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/write-display-utils.ts +0 -29
  396. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference-core.ts → reference/session.ts} +0 -0
  397. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-aliases.ts → shell/aliases.ts} +0 -0
  398. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ATTRIBUTION.md +0 -0
  399. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-presentation.ts +0 -0
  400. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/index.ts +0 -0
  401. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/render-utils.ts +0 -0
  402. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/shiki-highlight.ts +0 -0
  403. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/types.ts +0 -0
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import { abortable } from "./abort.ts";
2
3
  import { combineAbortSignals } from "./runtime-owner.ts";
3
4
  import type { McpExtensionState } from "./state.ts";
@@ -5,7 +6,12 @@ import {
5
6
  getToolNameCandidates,
6
7
  matchesToolPattern,
7
8
  resolveToolPrefix,
9
+ MCP_TOOL_APPROVAL_REQUEST_EVENT,
8
10
  type McpConfig,
11
+ type McpToolApprovalDecision,
12
+ type McpToolApprovalHandler,
13
+ type McpToolApprovalOrigin,
14
+ type McpToolApprovalRequest,
9
15
  type ToolMetadata,
10
16
  } from "./types.ts";
11
17
  import { sanitizeTerminalText } from "./utils.ts";
@@ -34,19 +40,76 @@ export function isToolCallApprovalRequired(
34
40
  );
35
41
  }
36
42
 
43
+ function isMcpToolApprovalDecision(value: unknown): value is McpToolApprovalDecision {
44
+ return value === "allow_once"
45
+ || value === "allow_for_session"
46
+ || value === "deny"
47
+ || value === "abstain";
48
+ }
49
+
50
+ async function requestBrokerApproval(
51
+ state: McpExtensionState,
52
+ serverName: string,
53
+ toolMeta: ToolMetadata,
54
+ args: Record<string, unknown> | undefined,
55
+ origin: McpToolApprovalOrigin,
56
+ signal?: AbortSignal,
57
+ ): Promise<McpToolApprovalDecision> {
58
+ if (!state.approvalEvents) return "abstain";
59
+
60
+ let acceptingClaim = true;
61
+ let handler: McpToolApprovalHandler | undefined;
62
+ const request: McpToolApprovalRequest = {
63
+ requestId: randomUUID(),
64
+ serverName,
65
+ originalToolName: toolMeta.originalName,
66
+ prefixedToolName: toolMeta.name,
67
+ args: args ?? {},
68
+ origin,
69
+ ...(signal !== undefined ? { signal } : {}),
70
+ claim(candidate: McpToolApprovalHandler) {
71
+ if (!acceptingClaim || handler) return false;
72
+ handler = candidate;
73
+ return true;
74
+ },
75
+ };
76
+
77
+ state.approvalEvents.emit(MCP_TOOL_APPROVAL_REQUEST_EVENT, request);
78
+ acceptingClaim = false;
79
+ if (!handler) return "abstain";
80
+
81
+ try {
82
+ const decision = await abortable(Promise.resolve().then(handler), signal);
83
+ return isMcpToolApprovalDecision(decision) ? decision : "deny";
84
+ } catch (error) {
85
+ if (signal?.aborted) throw error;
86
+ return "deny";
87
+ }
88
+ }
89
+
37
90
  export async function ensureToolCallApproved(
38
91
  state: McpExtensionState,
39
92
  serverName: string,
40
93
  toolMeta: ToolMetadata,
41
94
  args: Record<string, unknown> | undefined,
42
95
  signal?: AbortSignal,
96
+ origin: McpToolApprovalOrigin = toolMeta.resourceUri ? "resource" : "proxy",
43
97
  ): Promise<ToolCallApprovalResult> {
44
- if (!isToolCallApprovalRequired(state.config, serverName, toolMeta)) {
98
+ const cacheKey = `${serverName}\u0000${toolMeta.originalName}`;
99
+ const approvedToolCalls = state.approvedToolCalls ??= new Map<string, true>();
100
+ if (approvedToolCalls.has(cacheKey)) {
45
101
  return { ok: true };
46
102
  }
47
103
 
48
- const cacheKey = `${serverName}\u0000${toolMeta.originalName}`;
49
- if (state.approvedToolCalls.has(cacheKey)) {
104
+ const brokerDecision = await requestBrokerApproval(state, serverName, toolMeta, args, origin, signal);
105
+ if (brokerDecision === "allow_once") return { ok: true };
106
+ if (brokerDecision === "allow_for_session") {
107
+ approvedToolCalls.set(cacheKey, true);
108
+ return { ok: true };
109
+ }
110
+ if (brokerDecision === "deny") return { ok: false, reason: "denied" };
111
+
112
+ if (!isToolCallApprovalRequired(state.config, serverName, toolMeta)) {
50
113
  return { ok: true };
51
114
  }
52
115
 
@@ -71,7 +134,7 @@ export async function ensureToolCallApproved(
71
134
  return { ok: true };
72
135
  }
73
136
  if (decision === "Allow for session") {
74
- state.approvedToolCalls.set(cacheKey, true);
137
+ approvedToolCalls.set(cacheKey, true);
75
138
  return { ok: true };
76
139
  }
77
140
  return { ok: false, reason: "denied" };
@@ -1,4 +1,4 @@
1
- import { getToolUiResourceUri } from "@modelcontextprotocol/ext-apps/app-bridge";
1
+ import { getToolUiResourceUri } from "./ui-app-bridge-helpers.ts";
2
2
  import type { McpExtensionState } from "./state.ts";
3
3
  import type { ToolMetadata, McpTool, McpResource, ServerEntry, ToolPrefix } from "./types.ts";
4
4
  import { formatToolName, isToolAllowed, resolveToolPrefix } from "./types.ts";
@@ -44,14 +44,15 @@ export function buildToolMetadata(
44
44
  } catch {
45
45
  failedTools.push(tool.name);
46
46
  }
47
+ const uiStreamMode = extractToolUiStreamMode(tool._meta);
47
48
  metadata.push({
48
49
  name,
49
50
  originalName: tool.name,
50
51
  description: tool.description ?? "",
51
- inputSchema: tool.inputSchema,
52
- uiResourceUri,
53
- uiVisibility,
54
- uiStreamMode: extractToolUiStreamMode(tool._meta),
52
+ ...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),
53
+ ...(uiResourceUri !== undefined ? { uiResourceUri } : {}),
54
+ ...(uiVisibility !== undefined ? { uiVisibility } : {}),
55
+ ...(uiStreamMode !== undefined ? { uiStreamMode } : {}),
55
56
  });
56
57
  }
57
58
 
@@ -2,12 +2,189 @@
2
2
  // NOTE: Tools are NOT registered with Pi - only the unified `mcp` proxy tool is registered.
3
3
  // This keeps the LLM context small (1 tool instead of 100s).
4
4
 
5
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
5
8
  import type { McpContent, ContentBlock } from "./types.ts";
6
9
 
10
+ const MAX_BINARY_RESOURCE_BYTES = 10 * 1024 * 1024;
11
+ const MAX_SESSION_RESOURCE_BYTES = 100 * 1024 * 1024;
12
+ const MAX_SESSION_RESOURCE_FILES = 10_000; // Bounds metadata from empty or tiny resources.
13
+ const CLEANUP_RETRY_DELAY_MS = 30_000;
14
+ const MAX_CLEANUP_RETRY_ATTEMPTS = 3;
15
+ type MaterializedResourceSession = {
16
+ directory: string | undefined;
17
+ bytes: number;
18
+ files: number;
19
+ sequence: number;
20
+ };
21
+
22
+ function createMaterializedResourceSession(): MaterializedResourceSession {
23
+ return {
24
+ directory: undefined,
25
+ bytes: 0,
26
+ files: 0,
27
+ sequence: 0,
28
+ };
29
+ }
30
+
31
+ const defaultMaterializedResourceSession = createMaterializedResourceSession();
32
+ const scopedMaterializedResourceSessions = new WeakMap<object, MaterializedResourceSession>();
33
+ const pendingCleanupDirectories = new Set<string>();
34
+ const cleanupRetryAttempts = new Map<string, number>();
35
+ let pendingCleanupRetry: ReturnType<typeof setTimeout> | undefined;
36
+
37
+ function isAbortedScope(scope: object | undefined): boolean {
38
+ return !!scope && "aborted" in scope && (scope as { aborted?: unknown }).aborted === true;
39
+ }
40
+
41
+ function getMaterializedResourceSession(scope?: object): MaterializedResourceSession | undefined {
42
+ if (isAbortedScope(scope)) return undefined;
43
+ if (!scope) return defaultMaterializedResourceSession;
44
+ let session = scopedMaterializedResourceSessions.get(scope);
45
+ if (!session) {
46
+ session = createMaterializedResourceSession();
47
+ scopedMaterializedResourceSessions.set(scope, session);
48
+ }
49
+ return session;
50
+ }
51
+
52
+ type BinaryResource = { uri?: string | undefined; text?: string | undefined; mimeType?: string | undefined; blob: string };
53
+ type McpResourceContent = {
54
+ uri: string;
55
+ text?: string | undefined;
56
+ blob?: string | undefined;
57
+ mimeType?: string | undefined;
58
+ [key: string]: unknown;
59
+ };
60
+
61
+ function hasRetryableCleanupDirectory(): boolean {
62
+ for (const directory of pendingCleanupDirectories) {
63
+ if ((cleanupRetryAttempts.get(directory) ?? 0) < MAX_CLEANUP_RETRY_ATTEMPTS) return true;
64
+ }
65
+ return false;
66
+ }
67
+
68
+ function schedulePendingCleanupRetry(): void {
69
+ if (pendingCleanupRetry || !hasRetryableCleanupDirectory()) return;
70
+ for (const directory of pendingCleanupDirectories) {
71
+ const attempts = cleanupRetryAttempts.get(directory) ?? 0;
72
+ if (attempts < MAX_CLEANUP_RETRY_ATTEMPTS) cleanupRetryAttempts.set(directory, attempts + 1);
73
+ }
74
+ pendingCleanupRetry = setTimeout(() => {
75
+ pendingCleanupRetry = undefined;
76
+ try {
77
+ drainPendingCleanupDirectories();
78
+ } catch {
79
+ // drainPendingCleanupDirectories already retained the paths and rescheduled another retry.
80
+ }
81
+ }, CLEANUP_RETRY_DELAY_MS);
82
+ }
83
+
84
+ function drainPendingCleanupDirectories(): void {
85
+ const failures: unknown[] = [];
86
+ for (const directory of Array.from(pendingCleanupDirectories)) {
87
+ try {
88
+ rmSync(directory, { recursive: true, force: true });
89
+ pendingCleanupDirectories.delete(directory);
90
+ cleanupRetryAttempts.delete(directory);
91
+ } catch (error) {
92
+ failures.push(error);
93
+ }
94
+ }
95
+ if (pendingCleanupDirectories.size === 0 && pendingCleanupRetry) {
96
+ clearTimeout(pendingCleanupRetry);
97
+ pendingCleanupRetry = undefined;
98
+ }
99
+ if (failures.length > 0) {
100
+ schedulePendingCleanupRetry();
101
+ throw new AggregateError(failures, "Failed to clean materialized MCP resources");
102
+ }
103
+ }
104
+
105
+ export function cleanupMaterializedBinaryResources(scope?: object): void {
106
+ const session = scope ? scopedMaterializedResourceSessions.get(scope) : defaultMaterializedResourceSession;
107
+ if (session?.directory) pendingCleanupDirectories.add(session.directory);
108
+ if (session) {
109
+ session.directory = undefined;
110
+ session.bytes = 0;
111
+ session.files = 0;
112
+ session.sequence = 0;
113
+ if (scope) scopedMaterializedResourceSessions.delete(scope);
114
+ }
115
+
116
+ drainPendingCleanupDirectories();
117
+ }
118
+
119
+ function replaceBlob(resource: BinaryResource, text: string): string {
120
+ delete (resource as Partial<BinaryResource>).blob;
121
+ resource.text = text;
122
+ return text;
123
+ }
124
+
125
+ function omitBinaryResource(resource: BinaryResource, reason: string): string {
126
+ return replaceBlob(resource, [
127
+ `[Resource: ${resource.uri ?? "(no URI)"}]`,
128
+ `Binary content omitted: ${reason}`,
129
+ `MIME type: ${resource.mimeType ?? "application/octet-stream"}`,
130
+ ].join("\n"));
131
+ }
132
+
133
+ function materializeBinaryResource(resource: BinaryResource, scope?: object): string {
134
+ const session = getMaterializedResourceSession(scope);
135
+ if (!session) return omitBinaryResource(resource, "runtime stopped");
136
+ const decodedBytes = Buffer.byteLength(resource.blob, "base64");
137
+ if (decodedBytes > MAX_BINARY_RESOURCE_BYTES) {
138
+ return omitBinaryResource(resource, "decoded size exceeds 10 MiB");
139
+ }
140
+ if (
141
+ session.bytes + decodedBytes > MAX_SESSION_RESOURCE_BYTES ||
142
+ session.files >= MAX_SESSION_RESOURCE_FILES
143
+ ) {
144
+ return omitBinaryResource(resource, "session resource limit reached");
145
+ }
146
+
147
+ try {
148
+ session.directory ??= mkdtempSync(join(tmpdir(), "pi-mcp-resource-"));
149
+ } catch {
150
+ return omitBinaryResource(resource, "could not be saved");
151
+ }
152
+
153
+ const filePath = join(session.directory, `resource-${++session.sequence}.bin`);
154
+ session.bytes += decodedBytes;
155
+ session.files += 1;
156
+ try {
157
+ writeFileSync(filePath, Buffer.from(resource.blob, "base64"), { flag: "wx", mode: 0o600 });
158
+ } catch {
159
+ try {
160
+ rmSync(filePath, { force: true });
161
+ session.bytes -= decodedBytes;
162
+ session.files -= 1;
163
+ } catch {
164
+ // Keep the reservation when a partial file cannot be removed.
165
+ }
166
+ return omitBinaryResource(resource, "could not be saved");
167
+ }
168
+
169
+ return replaceBlob(resource, [
170
+ `[Resource: ${resource.uri ?? "(no URI)"}]`,
171
+ `Binary content saved to ${filePath}`,
172
+ `MIME type: ${resource.mimeType ?? "application/octet-stream"}`,
173
+ ].join("\n"));
174
+ }
175
+
7
176
  /**
8
177
  * Transform MCP content types to Pi content blocks.
9
178
  */
10
- export function transformMcpContent(content: McpContent[]): ContentBlock[] {
179
+ export function transformMcpResourceContents(contents: McpResourceContent[], scope?: object): ContentBlock[] {
180
+ return contents.map(resource => {
181
+ if (typeof resource.text === "string") return { type: "text" as const, text: resource.text };
182
+ if (typeof resource.blob === "string") return { type: "text" as const, text: materializeBinaryResource(resource as BinaryResource, scope) };
183
+ return { type: "text" as const, text: JSON.stringify(resource) };
184
+ });
185
+ }
186
+
187
+ export function transformMcpContent(content: McpContent[], scope?: object): ContentBlock[] {
11
188
  return content.map(c => {
12
189
  if (c.type === "text") {
13
190
  return { type: "text" as const, text: c.text ?? "" };
@@ -21,6 +198,13 @@ export function transformMcpContent(content: McpContent[]): ContentBlock[] {
21
198
  }
22
199
  if (c.type === "resource") {
23
200
  const resourceUri = c.resource?.uri ?? "(no URI)";
201
+ if (c.resource && "blob" in c.resource && typeof c.resource.blob === "string") {
202
+ const binaryResource = c.resource as typeof c.resource & { mimeType?: string; blob: string };
203
+ return {
204
+ type: "text" as const,
205
+ text: materializeBinaryResource(binaryResource, scope),
206
+ };
207
+ }
24
208
  const resourceContent = c.resource?.text ?? (c.resource ? JSON.stringify(c.resource) : "(no content)");
25
209
  return {
26
210
  type: "text" as const,
@@ -49,8 +233,8 @@ export function transformMcpContent(content: McpContent[]): ContentBlock[] {
49
233
  * Resolve a tool result's content blocks, falling back to structuredContent
50
234
  * when content is empty.
51
235
  */
52
- export function resolveMcpResultContent(result: Record<string, unknown>): ContentBlock[] {
53
- const blocks = transformMcpContent((Array.isArray(result.content) ? result.content : []) as McpContent[]);
236
+ export function resolveMcpResultContent(result: Record<string, unknown>, scope?: object): ContentBlock[] {
237
+ const blocks = transformMcpContent((Array.isArray(result.content) ? result.content : []) as McpContent[], scope);
54
238
  if (blocks.length > 0) return blocks;
55
239
 
56
240
  if (result.structuredContent !== undefined && result.structuredContent !== null) {
@@ -41,6 +41,7 @@ class CollapsibleText implements Component {
41
41
  private readonly fullText: Text;
42
42
  private readonly footerText: Text;
43
43
  private collapsedText: { charBudget: number; fullyIncluded: boolean; text: Text } | null = null;
44
+ private collapsedRender: { width: number; charBudget: number; lines: string[] } | null = null;
44
45
 
45
46
  constructor(
46
47
  private readonly text: string,
@@ -70,18 +71,29 @@ class CollapsibleText implements Component {
70
71
  fullyIncluded: prefix === this.text,
71
72
  text: new Text(prefix, 0, 0),
72
73
  };
74
+ this.collapsedRender = null;
73
75
  }
74
76
 
75
77
  const lines = this.collapsedText.text.render(width);
76
78
  if (!this.preTruncated && this.collapsedText.fullyIncluded && lines.length <= this.maxCollapsedLines) return lines;
79
+ if (this.collapsedRender?.width === width && this.collapsedRender.charBudget === charBudget) {
80
+ return this.collapsedRender.lines;
81
+ }
77
82
 
78
- return [
83
+ const rendered = [
79
84
  ...lines.slice(0, this.maxCollapsedLines),
80
85
  ...this.footerText.render(width),
81
86
  ];
87
+ this.collapsedRender = { width, charBudget, lines: rendered };
88
+ return rendered;
82
89
  }
83
90
 
84
- invalidate(): void {}
91
+ invalidate(): void {
92
+ this.fullText.invalidate();
93
+ this.footerText.invalidate();
94
+ this.collapsedText?.text.invalidate();
95
+ this.collapsedRender = null;
96
+ }
85
97
  }
86
98
 
87
99
  function truncateText(value: string, maxChars: number): string {
@@ -39,7 +39,10 @@ export function renderTsShape(inputSchema: unknown): string | null {
39
39
  if (typeof schema.$ref !== "string") return null;
40
40
  const match = schema.$ref.match(/^#\/(\$defs|definitions)\/([^/]+)$/);
41
41
  if (!match) return null;
42
- const definitionKey = `${match[1]}/${decodePointerToken(match[2])}`;
42
+ const definitionGroup = match[1];
43
+ const definitionName = match[2];
44
+ if (definitionGroup === undefined || definitionName === undefined) return null;
45
+ const definitionKey = `${definitionGroup}/${decodePointerToken(definitionName)}`;
43
46
  if (!definitions.has(definitionKey)) return null;
44
47
  return aliasFor(definitionKey);
45
48
  }
@@ -108,7 +111,11 @@ function isSchema(value: unknown): value is Schema {
108
111
  }
109
112
 
110
113
  function hasUnsupportedKeyword(schema: Schema): boolean {
111
- return UNSUPPORTED_KEYWORDS.some(keyword => Object.hasOwn(schema, keyword));
114
+ return UNSUPPORTED_KEYWORDS.some(keyword => {
115
+ if (!Object.hasOwn(schema, keyword)) return false;
116
+ // `additionalProperties: false` is a closed-object constraint, not a shape that this renderer needs to understand.
117
+ return keyword !== "additionalProperties" || schema.additionalProperties !== false;
118
+ });
112
119
  }
113
120
 
114
121
  function decodePointerToken(token: string): string {
@@ -1,6 +1,11 @@
1
1
  // types.ts - Core type definitions
2
- import type { Transport as McpTransport } from "@modelcontextprotocol/sdk/shared/transport.js";
3
- import type { ContentBlock as McpContentBlock } from "@modelcontextprotocol/sdk/types.js";
2
+ import type {
3
+ ContentBlock as McpContentBlock,
4
+ ListPromptsResult,
5
+ ListResourcesResult,
6
+ ListToolsResult,
7
+ Transport as McpTransport,
8
+ } from "@modelcontextprotocol/client";
4
9
  import type { TextContent, ImageContent } from "@earendil-works/pi-ai";
5
10
  import type { UiStreamMode } from "./ui-stream-types.ts";
6
11
  import type { UiToolVisibility } from "./ui-tool-visibility.ts";
@@ -48,36 +53,41 @@ export type ImportKind =
48
53
  | "windsurf"
49
54
  | "vscode";
50
55
 
51
- // Tool definition from MCP server
56
+ type SdkTool = ListToolsResult["tools"][number];
57
+ type SdkResource = ListResourcesResult["resources"][number];
58
+ type SdkPrompt = ListPromptsResult["prompts"][number];
59
+ type SdkPromptArgument = NonNullable<SdkPrompt["arguments"]>[number];
60
+
61
+ // MCP wire definitions derive their field types from the installed SDK while
62
+ // retaining the adapter's deliberately smaller public surface.
52
63
  export interface McpTool {
53
- name: string;
54
- title?: string;
55
- description?: string;
56
- inputSchema?: unknown; // JSON Schema
57
- _meta?: Record<string, unknown>;
64
+ name: SdkTool["name"];
65
+ title?: SdkTool["title"];
66
+ description?: SdkTool["description"];
67
+ inputSchema?: SdkTool["inputSchema"]; // JSON Schema
68
+ _meta?: SdkTool["_meta"];
58
69
  }
59
70
 
60
- // Resource definition from MCP server
61
71
  export interface McpResource {
62
- uri: string;
63
- name: string;
64
- description?: string;
65
- mimeType?: string;
66
- _meta?: Record<string, unknown>;
72
+ uri: SdkResource["uri"];
73
+ name: SdkResource["name"];
74
+ description?: SdkResource["description"];
75
+ mimeType?: SdkResource["mimeType"];
76
+ _meta?: SdkResource["_meta"];
67
77
  }
68
78
 
69
79
  export interface McpPromptArgument {
70
- name: string;
71
- description?: string;
72
- required?: boolean;
80
+ name: SdkPromptArgument["name"];
81
+ description?: SdkPromptArgument["description"];
82
+ required?: SdkPromptArgument["required"];
73
83
  }
74
84
 
75
85
  export interface McpPrompt {
76
- name: string;
77
- title?: string;
78
- description?: string;
79
- arguments?: McpPromptArgument[];
80
- _meta?: Record<string, unknown>;
86
+ name: SdkPrompt["name"];
87
+ title?: SdkPrompt["title"];
88
+ description?: SdkPrompt["description"];
89
+ arguments?: SdkPrompt["arguments"];
90
+ _meta?: SdkPrompt["_meta"];
81
91
  }
82
92
 
83
93
  export interface UiResourceMeta {
@@ -340,6 +350,10 @@ export interface OAuthConfig {
340
350
  clientName?: string;
341
351
  /** Client homepage URI for dynamic registration */
342
352
  clientUri?: string;
353
+ /** Client logo URL for dynamic registration; shown on consent screens */
354
+ logoUri?: string;
355
+ /** Security-weakening escape hatch for known-misconfigured authorization servers. */
356
+ skipIssuerMetadataValidation?: boolean;
343
357
  }
344
358
 
345
359
  // Server configuration
@@ -387,6 +401,20 @@ export interface ServerEntry {
387
401
  debug?: boolean; // Show server stderr (default: false)
388
402
  /** Enable metadata-only JSONL protocol tracing for this server. */
389
403
  trace?: boolean;
404
+ /** Force a specific HTTP MCP transport. Used by Agent Plugins, whose `type` declares the transport and forbids client fallback. */
405
+ httpTransport?: "streamable-http" | "sse";
406
+ /** Client-managed persistent data directory for Agent Plugin stdio servers. */
407
+ pluginDataDir?: string;
408
+ /** Treat env values as already resolved literals. Used for Agent Plugin env rules. */
409
+ literalEnv?: boolean;
410
+ /**
411
+ * MCP protocol era negotiation for this server. Defaults to `"legacy"`
412
+ * (byte-equivalent to pre-2026 behavior — no `versionNegotiation` is sent).
413
+ * `"auto"` offers the SDK's default 2026-07-28+ modern versions with
414
+ * legacy fallback; `"2026-07-28"` pins the connection to that revision
415
+ * with no fallback. `auto` and `2026-07-28` must be set explicitly.
416
+ */
417
+ protocolVersion?: "legacy" | "auto" | "2026-07-28";
390
418
  // Keep configuration visible without allowing connections or execution.
391
419
  disabled?: boolean;
392
420
  }
@@ -422,6 +450,23 @@ export interface McpTraceSettings {
422
450
  maxEvents?: number;
423
451
  }
424
452
 
453
+ export const MCP_TOOL_APPROVAL_REQUEST_EVENT = "pi-mcp-adapter:tool-approval-request" as const;
454
+
455
+ export type McpToolApprovalOrigin = "proxy" | "direct" | "script" | "resource" | "iframe";
456
+ export type McpToolApprovalDecision = "allow_once" | "allow_for_session" | "deny" | "abstain";
457
+ export type McpToolApprovalHandler = () => McpToolApprovalDecision | Promise<McpToolApprovalDecision>;
458
+
459
+ export interface McpToolApprovalRequest {
460
+ requestId: string;
461
+ serverName: string;
462
+ originalToolName: string;
463
+ prefixedToolName: string;
464
+ args: Record<string, unknown>;
465
+ origin: McpToolApprovalOrigin;
466
+ signal?: AbortSignal;
467
+ claim(handler: McpToolApprovalHandler): boolean;
468
+ }
469
+
425
470
  export interface McpSettings {
426
471
  toolPrefix?: ToolPrefix;
427
472
  /** Show the plug prefix in MCP status and connection text (default: true). Set to false to disable it. */
@@ -430,6 +475,8 @@ export interface McpSettings {
430
475
  mcpFooterStatus?: McpFooterStatus;
431
476
  /** Discover detected host-specific MCP configs only when explicitly enabled. */
432
477
  hostConfigDiscovery?: HostConfigDiscovery;
478
+ /** Agent Plugin package directories to load MCP servers from. */
479
+ agentPluginPaths?: string[];
433
480
  idleTimeout?: number; // minutes, default 10, 0 to disable
434
481
  requestTimeoutMs?: number; // milliseconds, overrides the SDK request timeout when > 0
435
482
  directTools?: boolean;
@@ -622,6 +669,48 @@ export function resolveToolPrefix(
622
669
  return definition?.toolPrefix ?? globalPrefix ?? "server";
623
670
  }
624
671
 
672
+
673
+ /**
674
+ * Resolve a configured MCP server name from a prefixed tool name.
675
+ *
676
+ * When the proxy tool is addressed with a fully-qualified name such as
677
+ * `searxng_searxng_web_search`, downstream policy systems (for example a
678
+ * permission gate) need to recover the owning server so they can evaluate
679
+ * server-scoped rules against the bare server name. This performs the inverse
680
+ * of {@link getServerPrefix}: it finds the longest configured server prefix
681
+ * that the tool name starts with and returns that server's name.
682
+ *
683
+ * @param toolName - the tool name as passed to the proxy `mcp({ tool })` call.
684
+ * @param serverNames - the configured MCP server names (keys of `mcpServers`).
685
+ * @param prefix - the active tool-prefix mode.
686
+ * @returns the resolved server name, or `undefined` when no prefix matches or
687
+ * the prefix mode is `"none"`.
688
+ */
689
+ export function resolveServerFromToolName(
690
+ toolName: string,
691
+ serverNames: Iterable<string>,
692
+ prefix: ToolPrefix,
693
+ ): string | undefined {
694
+ if (prefix === "none") return undefined;
695
+ const candidates: { name: string; prefix: string }[] = [];
696
+ for (const name of serverNames) {
697
+ const p = getServerPrefix(name, prefix);
698
+ if (p && toolName.startsWith(p + "_")) candidates.push({ name, prefix: p });
699
+ }
700
+ if (candidates.length === 0) return undefined;
701
+ candidates.sort((a, b) => b.prefix.length - a.prefix.length);
702
+ const best = candidates[0];
703
+ // Fail safe: two distinct server names can normalize to the same prefix
704
+ // (e.g. my-server and my_server both -> my_server under "server" mode).
705
+ // When that happens the owning server is ambiguous; return undefined so a
706
+ // downstream permission gate falls back to its existing wildcard path rather
707
+ // than enforcing a rule against the wrong server.
708
+ if (candidates.some((c) => c.prefix === best!.prefix && c.name !== best!.name)) {
709
+ return undefined;
710
+ }
711
+ return best?.name;
712
+ }
713
+
625
714
  export function sanitizePromptName(name: string): string {
626
715
  const cleaned = name.replace(/[^A-Za-z0-9_-]+/g, "_").replace(/^[_-]+|[_-]+$/g, "");
627
716
  if (!cleaned) return "prompt";
@@ -0,0 +1,40 @@
1
+ import type { UiResourcePermissions } from "./types.ts";
2
+
3
+ export const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
4
+
5
+ const RESOURCE_URI_META_KEY = "ui/resourceUri";
6
+
7
+ export function getToolUiResourceUri(tool: { _meta?: Record<string, unknown> | undefined }): string | undefined {
8
+ const meta = tool._meta;
9
+ let resourceUri = getNestedResourceUri(meta);
10
+ if (resourceUri === undefined) {
11
+ resourceUri = meta?.[RESOURCE_URI_META_KEY];
12
+ }
13
+
14
+ if (typeof resourceUri === "string" && resourceUri.startsWith("ui://")) {
15
+ return resourceUri;
16
+ }
17
+
18
+ if (resourceUri !== undefined) {
19
+ throw new Error(`Invalid UI resource URI: ${JSON.stringify(resourceUri)}`);
20
+ }
21
+
22
+ return undefined;
23
+ }
24
+
25
+ export function buildAllowAttribute(permissions: UiResourcePermissions | undefined): string {
26
+ if (!permissions) return "";
27
+
28
+ const allowed: string[] = [];
29
+ if (permissions.camera) allowed.push("camera");
30
+ if (permissions.microphone) allowed.push("microphone");
31
+ if (permissions.geolocation) allowed.push("geolocation");
32
+ if (permissions.clipboardWrite) allowed.push("clipboard-write");
33
+ return allowed.join("; ");
34
+ }
35
+
36
+ function getNestedResourceUri(meta: Record<string, unknown> | undefined): unknown {
37
+ const ui = meta?.ui;
38
+ if (!ui || typeof ui !== "object") return undefined;
39
+ return (ui as Record<string, unknown>).resourceUri;
40
+ }