@herbertgao/pi-extensions 2026.8.2 → 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 (272) hide show
  1. package/README.md +9 -2
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +6 -8
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +5 -7
  4. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +101 -0
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +1 -17
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +5 -3
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{utils/agent-summary.ts → feature/agent-summary/core.ts} +37 -35
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-summary.ts → agent-summary/index.ts} +2 -6
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference.ts → reference/index.ts} +1 -1
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-autocomplete.ts → reference/subagent.ts} +19 -9
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-startup-header.ts → shell/startup-header.ts} +1 -1
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{working-message.ts → shell/working-message.ts} +5 -2
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +32 -28
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +379 -85
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +727 -0
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +65 -775
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{feature → renderer}/markdown-enhance.ts +35 -31
  18. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-hover.ts → mouse/hover.ts} +5 -5
  19. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-interaction.ts → mouse/interaction.ts} +34 -35
  20. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-layout.ts → mouse/layout.ts} +5 -21
  21. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-packets.ts → mouse/packets.ts} +1 -1
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-scroll.ts → mouse/scroll.ts} +48 -37
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ansi-utils.ts +56 -1
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-renderer.ts +2 -2
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/line-width-safety.ts +1 -1
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +12 -0
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/write-execution.ts +1 -2
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-grouping.ts → tool/grouping.ts} +18 -8
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-result.ts → tool/result.ts} +4 -13
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +0 -27
  31. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  32. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  33. package/node_modules/@herbertgao/pi-subagents/package.json +2 -2
  34. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +1 -0
  35. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +3 -1
  36. package/node_modules/@herbertgao/pi-subagents/src/index.ts +33 -35
  37. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +26 -35
  38. package/node_modules/@herbertgao/pi-subagents/src/prompts.ts +15 -1
  39. package/node_modules/pi-mcp-adapter/CHANGELOG.md +19 -0
  40. package/node_modules/pi-mcp-adapter/README.md +19 -4
  41. package/node_modules/pi-mcp-adapter/agent-dir.ts +40 -0
  42. package/node_modules/pi-mcp-adapter/commands.ts +12 -2
  43. package/node_modules/pi-mcp-adapter/direct-tools.ts +6 -8
  44. package/node_modules/pi-mcp-adapter/index.ts +15 -3
  45. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +87 -4
  46. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +138 -61
  47. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +43 -4
  48. package/node_modules/pi-mcp-adapter/oauth.ts +48 -0
  49. package/node_modules/pi-mcp-adapter/package.json +8 -2
  50. package/node_modules/pi-mcp-adapter/proxy-modes.ts +8 -10
  51. package/node_modules/pi-mcp-adapter/tool-registrar.ts +187 -3
  52. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  53. package/node_modules/pi-web-access/CHANGELOG.md +45 -0
  54. package/node_modules/pi-web-access/README.md +104 -29
  55. package/node_modules/pi-web-access/bocha.ts +221 -0
  56. package/node_modules/pi-web-access/curator-page.ts +113 -12
  57. package/node_modules/pi-web-access/curator-server.ts +56 -20
  58. package/node_modules/pi-web-access/datalab-pdf-extract.ts +568 -0
  59. package/node_modules/pi-web-access/duckduckgo.ts +123 -0
  60. package/node_modules/pi-web-access/extract.ts +288 -143
  61. package/node_modules/pi-web-access/feature-config.ts +29 -0
  62. package/node_modules/pi-web-access/gemini-api.ts +4 -0
  63. package/node_modules/pi-web-access/gemini-search.ts +45 -12
  64. package/node_modules/pi-web-access/gemini-web.ts +68 -3
  65. package/node_modules/pi-web-access/index.ts +160 -71
  66. package/node_modules/pi-web-access/jina-search.ts +284 -0
  67. package/node_modules/pi-web-access/kagi.ts +12 -12
  68. package/node_modules/pi-web-access/openai-search.ts +12 -2
  69. package/node_modules/pi-web-access/package.json +4 -3
  70. package/node_modules/pi-web-access/pdf-extract.ts +327 -228
  71. package/node_modules/pi-web-access/storage.ts +440 -2
  72. package/node_modules/pi-web-access/summary-model-scope.ts +1 -1
  73. package/node_modules/pi-web-access/summary-review.ts +4 -2
  74. package/node_modules/pi-web-access/video-extract.ts +10 -8
  75. package/node_modules/pi-web-access/xai-search.ts +12 -2
  76. package/node_modules/pi-web-access/youtube-extract.ts +2 -1
  77. package/node_modules/remote-pi/LICENSE +21 -0
  78. package/node_modules/remote-pi/README.md +675 -0
  79. package/node_modules/remote-pi/dist/actions/handlers.d.ts +122 -0
  80. package/node_modules/remote-pi/dist/actions/handlers.js +160 -0
  81. package/node_modules/remote-pi/dist/actions/handlers.js.map +1 -0
  82. package/node_modules/remote-pi/dist/actions/registry.d.ts +39 -0
  83. package/node_modules/remote-pi/dist/actions/registry.js +58 -0
  84. package/node_modules/remote-pi/dist/actions/registry.js.map +1 -0
  85. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +2 -0
  86. package/node_modules/remote-pi/dist/bin/supervisord.js +86 -0
  87. package/node_modules/remote-pi/dist/bin/supervisord.js.map +1 -0
  88. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +58 -0
  89. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +71 -0
  90. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +1 -0
  91. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +60 -0
  92. package/node_modules/remote-pi/dist/commands/list_commands.js +73 -0
  93. package/node_modules/remote-pi/dist/commands/list_commands.js.map +1 -0
  94. package/node_modules/remote-pi/dist/config.d.ts +62 -0
  95. package/node_modules/remote-pi/dist/config.js +115 -0
  96. package/node_modules/remote-pi/dist/config.js.map +1 -0
  97. package/node_modules/remote-pi/dist/daemon/client.d.ts +20 -0
  98. package/node_modules/remote-pi/dist/daemon/client.js +131 -0
  99. package/node_modules/remote-pi/dist/daemon/client.js.map +1 -0
  100. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +176 -0
  101. package/node_modules/remote-pi/dist/daemon/control_protocol.js +63 -0
  102. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +1 -0
  103. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +45 -0
  104. package/node_modules/remote-pi/dist/daemon/cron_log.js +72 -0
  105. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +1 -0
  106. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +80 -0
  107. package/node_modules/remote-pi/dist/daemon/cron_registry.js +194 -0
  108. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +1 -0
  109. package/node_modules/remote-pi/dist/daemon/id.d.ts +24 -0
  110. package/node_modules/remote-pi/dist/daemon/id.js +36 -0
  111. package/node_modules/remote-pi/dist/daemon/id.js.map +1 -0
  112. package/node_modules/remote-pi/dist/daemon/install.d.ts +170 -0
  113. package/node_modules/remote-pi/dist/daemon/install.js +547 -0
  114. package/node_modules/remote-pi/dist/daemon/install.js.map +1 -0
  115. package/node_modules/remote-pi/dist/daemon/registry.d.ts +63 -0
  116. package/node_modules/remote-pi/dist/daemon/registry.js +153 -0
  117. package/node_modules/remote-pi/dist/daemon/registry.js.map +1 -0
  118. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +194 -0
  119. package/node_modules/remote-pi/dist/daemon/rpc_child.js +389 -0
  120. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +1 -0
  121. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +93 -0
  122. package/node_modules/remote-pi/dist/daemon/supervisor.js +596 -0
  123. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +1 -0
  124. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +26 -0
  125. package/node_modules/remote-pi/dist/extension_ui_bridge.js +341 -0
  126. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +1 -0
  127. package/node_modules/remote-pi/dist/index.d.ts +213 -0
  128. package/node_modules/remote-pi/dist/index.js +4996 -0
  129. package/node_modules/remote-pi/dist/index.js.map +1 -0
  130. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +10 -0
  131. package/node_modules/remote-pi/dist/mcp/mesh_result.js +42 -0
  132. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +1 -0
  133. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +16 -0
  134. package/node_modules/remote-pi/dist/mcp/mesh_server.js +327 -0
  135. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +1 -0
  136. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +30 -0
  137. package/node_modules/remote-pi/dist/mesh/canonical.js +61 -0
  138. package/node_modules/remote-pi/dist/mesh/canonical.js.map +1 -0
  139. package/node_modules/remote-pi/dist/mesh/client.d.ts +18 -0
  140. package/node_modules/remote-pi/dist/mesh/client.js +125 -0
  141. package/node_modules/remote-pi/dist/mesh/client.js.map +1 -0
  142. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +38 -0
  143. package/node_modules/remote-pi/dist/mesh/encoding.js +216 -0
  144. package/node_modules/remote-pi/dist/mesh/encoding.js.map +1 -0
  145. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +73 -0
  146. package/node_modules/remote-pi/dist/mesh/self_revoke.js +401 -0
  147. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +1 -0
  148. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +49 -0
  149. package/node_modules/remote-pi/dist/mesh/siblings.js +159 -0
  150. package/node_modules/remote-pi/dist/mesh/siblings.js.map +1 -0
  151. package/node_modules/remote-pi/dist/mesh/types.d.ts +34 -0
  152. package/node_modules/remote-pi/dist/mesh/types.js +11 -0
  153. package/node_modules/remote-pi/dist/mesh/types.js.map +1 -0
  154. package/node_modules/remote-pi/dist/mesh/verify.d.ts +17 -0
  155. package/node_modules/remote-pi/dist/mesh/verify.js +86 -0
  156. package/node_modules/remote-pi/dist/mesh/verify.js.map +1 -0
  157. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +8 -0
  158. package/node_modules/remote-pi/dist/pairing/crypto.js +22 -0
  159. package/node_modules/remote-pi/dist/pairing/crypto.js.map +1 -0
  160. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +28 -0
  161. package/node_modules/remote-pi/dist/pairing/handshake.js +113 -0
  162. package/node_modules/remote-pi/dist/pairing/handshake.js.map +1 -0
  163. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +16 -0
  164. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +103 -0
  165. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +1 -0
  166. package/node_modules/remote-pi/dist/pairing/qr.d.ts +59 -0
  167. package/node_modules/remote-pi/dist/pairing/qr.js +125 -0
  168. package/node_modules/remote-pi/dist/pairing/qr.js.map +1 -0
  169. package/node_modules/remote-pi/dist/pairing/storage.d.ts +120 -0
  170. package/node_modules/remote-pi/dist/pairing/storage.js +604 -0
  171. package/node_modules/remote-pi/dist/pairing/storage.js.map +1 -0
  172. package/node_modules/remote-pi/dist/protocol/codec.d.ts +7 -0
  173. package/node_modules/remote-pi/dist/protocol/codec.js +49 -0
  174. package/node_modules/remote-pi/dist/protocol/codec.js.map +1 -0
  175. package/node_modules/remote-pi/dist/protocol/types.d.ts +393 -0
  176. package/node_modules/remote-pi/dist/protocol/types.js +2 -0
  177. package/node_modules/remote-pi/dist/protocol/types.js.map +1 -0
  178. package/node_modules/remote-pi/dist/rooms.d.ts +29 -0
  179. package/node_modules/remote-pi/dist/rooms.js +57 -0
  180. package/node_modules/remote-pi/dist/rooms.js.map +1 -0
  181. package/node_modules/remote-pi/dist/session/address.d.ts +49 -0
  182. package/node_modules/remote-pi/dist/session/address.js +58 -0
  183. package/node_modules/remote-pi/dist/session/address.js.map +1 -0
  184. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +55 -0
  185. package/node_modules/remote-pi/dist/session/agent_bridge.js +146 -0
  186. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +1 -0
  187. package/node_modules/remote-pi/dist/session/bridge.d.ts +37 -0
  188. package/node_modules/remote-pi/dist/session/bridge.js +130 -0
  189. package/node_modules/remote-pi/dist/session/bridge.js.map +1 -0
  190. package/node_modules/remote-pi/dist/session/broker.d.ts +189 -0
  191. package/node_modules/remote-pi/dist/session/broker.js +507 -0
  192. package/node_modules/remote-pi/dist/session/broker.js.map +1 -0
  193. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +74 -0
  194. package/node_modules/remote-pi/dist/session/broker_remote.js +583 -0
  195. package/node_modules/remote-pi/dist/session/broker_remote.js.map +1 -0
  196. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +33 -0
  197. package/node_modules/remote-pi/dist/session/cwd_lock.js +126 -0
  198. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +1 -0
  199. package/node_modules/remote-pi/dist/session/envelope.d.ts +33 -0
  200. package/node_modules/remote-pi/dist/session/envelope.js +117 -0
  201. package/node_modules/remote-pi/dist/session/envelope.js.map +1 -0
  202. package/node_modules/remote-pi/dist/session/global_config.d.ts +33 -0
  203. package/node_modules/remote-pi/dist/session/global_config.js +73 -0
  204. package/node_modules/remote-pi/dist/session/global_config.js.map +1 -0
  205. package/node_modules/remote-pi/dist/session/ipc.d.ts +27 -0
  206. package/node_modules/remote-pi/dist/session/ipc.js +22 -0
  207. package/node_modules/remote-pi/dist/session/ipc.js.map +1 -0
  208. package/node_modules/remote-pi/dist/session/leader_election.d.ts +32 -0
  209. package/node_modules/remote-pi/dist/session/leader_election.js +106 -0
  210. package/node_modules/remote-pi/dist/session/leader_election.js.map +1 -0
  211. package/node_modules/remote-pi/dist/session/local_config.d.ts +55 -0
  212. package/node_modules/remote-pi/dist/session/local_config.js +157 -0
  213. package/node_modules/remote-pi/dist/session/local_config.js.map +1 -0
  214. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +20 -0
  215. package/node_modules/remote-pi/dist/session/mesh_message.js +28 -0
  216. package/node_modules/remote-pi/dist/session/mesh_message.js.map +1 -0
  217. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +116 -0
  218. package/node_modules/remote-pi/dist/session/mesh_node.js +573 -0
  219. package/node_modules/remote-pi/dist/session/mesh_node.js.map +1 -0
  220. package/node_modules/remote-pi/dist/session/peer.d.ts +139 -0
  221. package/node_modules/remote-pi/dist/session/peer.js +451 -0
  222. package/node_modules/remote-pi/dist/session/peer.js.map +1 -0
  223. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +13 -0
  224. package/node_modules/remote-pi/dist/session/peer_inventory.js +48 -0
  225. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +1 -0
  226. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +9 -0
  227. package/node_modules/remote-pi/dist/session/peer_limits.js +21 -0
  228. package/node_modules/remote-pi/dist/session/peer_limits.js.map +1 -0
  229. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +39 -0
  230. package/node_modules/remote-pi/dist/session/setup_wizard.js +63 -0
  231. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +1 -0
  232. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +5 -0
  233. package/node_modules/remote-pi/dist/session/tool_gate.js +11 -0
  234. package/node_modules/remote-pi/dist/session/tool_gate.js.map +1 -0
  235. package/node_modules/remote-pi/dist/session/tools.d.ts +24 -0
  236. package/node_modules/remote-pi/dist/session/tools.js +252 -0
  237. package/node_modules/remote-pi/dist/session/tools.js.map +1 -0
  238. package/node_modules/remote-pi/dist/session/wizard.d.ts +13 -0
  239. package/node_modules/remote-pi/dist/session/wizard.js +20 -0
  240. package/node_modules/remote-pi/dist/session/wizard.js.map +1 -0
  241. package/node_modules/remote-pi/dist/settings.d.ts +15 -0
  242. package/node_modules/remote-pi/dist/settings.js +52 -0
  243. package/node_modules/remote-pi/dist/settings.js.map +1 -0
  244. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +37 -0
  245. package/node_modules/remote-pi/dist/transport/peer_channel.js +98 -0
  246. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +1 -0
  247. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +29 -0
  248. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +62 -0
  249. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +1 -0
  250. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +91 -0
  251. package/node_modules/remote-pi/dist/transport/relay_client.js +206 -0
  252. package/node_modules/remote-pi/dist/transport/relay_client.js.map +1 -0
  253. package/node_modules/remote-pi/dist/ui/footer.d.ts +32 -0
  254. package/node_modules/remote-pi/dist/ui/footer.js +34 -0
  255. package/node_modules/remote-pi/dist/ui/footer.js.map +1 -0
  256. package/node_modules/remote-pi/docs/daemon.md +289 -0
  257. package/node_modules/remote-pi/package.json +88 -0
  258. package/node_modules/remote-pi/service-templates/launchd.plist.template +35 -0
  259. package/node_modules/remote-pi/service-templates/systemd.service.template +19 -0
  260. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +10 -0
  261. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +38 -0
  262. package/node_modules/remote-pi/skills/agent-network/SKILL.md +310 -0
  263. package/package.json +19 -9
  264. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/write-display-utils.ts +0 -29
  265. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference-core.ts → reference/session.ts} +0 -0
  266. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-aliases.ts → shell/aliases.ts} +0 -0
  267. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ATTRIBUTION.md +0 -0
  268. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-presentation.ts +0 -0
  269. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/index.ts +0 -0
  270. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/render-utils.ts +0 -0
  271. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/shiki-highlight.ts +0 -0
  272. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/types.ts +0 -0
@@ -6,9 +6,9 @@ import {
6
6
  visibleWidth,
7
7
  type Component,
8
8
  } from "@earendil-works/pi-tui";
9
- import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "../utils/tool-loading-icon.ts";
10
- import { sanitizeToolResultText } from "../utils/tool-result-sanitize.ts";
11
- import { showMoreHintText } from "./show-more-hint.ts";
9
+ import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "../../utils/tool-loading-icon.ts";
10
+ import { sanitizeToolResultText } from "../../utils/tool-result-sanitize.ts";
11
+ import { showMoreHintText } from "../show-more-hint.ts";
12
12
 
13
13
  const PATCH_KEY = Symbol.for("pi.ccstyle.tool-grouping-patch");
14
14
  const PARENT_KEY = Symbol.for("pi.ccstyle.tool-grouping-parent");
@@ -101,7 +101,7 @@ function stripLeadingStatusIcon(line: string): string {
101
101
  );
102
102
  }
103
103
 
104
- function stripBackgroundAnsi(line: string): string {
104
+ export function stripBackgroundAnsi(line: string): string {
105
105
  return line.replace(/\x1b\[(?:4[0-9]|10[0-7]|48(?:(?:;|:)[0-9]+)+|49)m/g, "");
106
106
  }
107
107
 
@@ -126,15 +126,25 @@ function stripLeadingSpaces(line: string, count: number): string {
126
126
  return ansi + line.slice(offset);
127
127
  }
128
128
 
129
- function paddedBackgroundRow(theme: any, slot: string, content: string, width: number): string {
129
+ /** 生成一行铺满 width slot 背景行;bgAnsiOverride 可替换背景 ANSI(用于提亮等)。 */
130
+ export function paddedBackgroundRow(
131
+ theme: any,
132
+ slot: string,
133
+ content: string,
134
+ width: number,
135
+ bgAnsiOverride?: string,
136
+ ): string {
130
137
  const innerWidth = Math.max(0, width - 2);
131
138
  const clipped = truncateToWidth(stripBackgroundAnsi(content), innerWidth, "");
132
139
  const row = ` ${clipped}${" ".repeat(Math.max(0, innerWidth - visibleWidth(clipped)))} `;
133
- if (typeof theme?.bg !== "function") return row;
134
140
  const bgAnsi =
135
- theme.getBgAnsi?.(slot) ?? theme.bg(slot, "").match(/^\x1b\[[0-?]*[ -/]*[@-~]/)?.[0] ?? "";
141
+ bgAnsiOverride ||
142
+ (typeof theme?.bg === "function"
143
+ ? theme.getBgAnsi?.(slot) || theme.bg(slot, "").match(/^\x1b\[[0-?]*[ -/]*[@-~]/)?.[0] || ""
144
+ : "");
136
145
  const stable = bgAnsi ? row.replace(/\x1b\[(?:0)?m/g, (reset) => reset + bgAnsi) : row;
137
- return theme.bg(slot, stable);
146
+ if (!bgAnsi) return typeof theme?.bg === "function" ? theme.bg(slot, stable) : row;
147
+ return `${bgAnsi}${stable}\x1b[49m`;
138
148
  }
139
149
 
140
150
  function oneLine(value: unknown, max = 96): string {
@@ -1,9 +1,9 @@
1
1
  import { Text, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
2
2
  import { inspect } from "node:util";
3
- import { config } from "../config/config.ts";
4
- import { showMoreHintText } from "./show-more-hint.ts";
5
- import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "../utils/tool-loading-icon.ts";
6
- import { sanitizeToolResultText } from "../utils/tool-result-sanitize.ts";
3
+ import { config } from "../../config/config.ts";
4
+ import { showMoreHintText } from "../show-more-hint.ts";
5
+ import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "../../utils/tool-loading-icon.ts";
6
+ import { sanitizeToolResultText } from "../../utils/tool-result-sanitize.ts";
7
7
 
8
8
  const TOOL_VIEWPORT_WIDTH_RATIO = 0.8;
9
9
 
@@ -677,15 +677,6 @@ export function renderExpandedToolResult(
677
677
  return new Text(theme.fg(color, renderCollapsedToolResult("Done")), 0, 0);
678
678
  }
679
679
 
680
- export function formatExpandHint(lineCount: number): string {
681
- return ` (${lineCount} more line${lineCount === 1 ? "" : "s"} / click)`;
682
- }
683
-
684
- function expandHint(theme: any, lineCount: number, hovered = false): string {
685
- // Keep interaction guidance neutral; it should not inherit success/error
686
- // coloring from the tool result surrounding it.
687
- return theme.fg(hovered ? "text" : "muted", formatExpandHint(lineCount));
688
- }
689
680
  const toolViewMarker = (id: number, section: ToolIoSection) =>
690
681
  `_cc:v${id}:${section === "input" ? "i" : "o"}`;
691
682
 
@@ -1,7 +1,3 @@
1
- // fullscreen 模式探测:官方 fullscreen TUI(TuiAltScreen)由官方独占布局,
2
- // 插件渲染层应让位。检测方式是瞬时 setWidget 拿 tui 实例检查 mode。
3
- const PROBE_KEY = "cc-fullscreen-probe";
4
-
5
1
  /**
6
2
  * 官方 0.84+ 的 tui 引用是惰性 Proxy(createInteractiveTuiReference):
7
3
  * 函数属性每次 get 都返回新包装,执行时才解析到当前实现。
@@ -13,26 +9,3 @@ export function isLazyProxyTui(tui: any): boolean {
13
9
  const probe = tui.requestRender;
14
10
  return typeof probe === "function" && probe !== tui.requestRender;
15
11
  }
16
-
17
- /**
18
- * 探测当前会话的 TUI 是否运行在官方 fullscreen 模式。
19
- * 无 setWidget 的环境(测试 mock)视为 regular。
20
- */
21
- export function isFullscreenUi(ctx: { ui?: any }): boolean {
22
- if (typeof ctx?.ui?.setWidget !== "function") return false;
23
- let fullscreen = false;
24
- try {
25
- ctx.ui.setWidget(PROBE_KEY, (tui: any) => {
26
- fullscreen = tui?.mode === "fullscreen";
27
- return { render: () => 0, invalidate() {} };
28
- });
29
- } catch {
30
- // 探测失败按 regular 处理,避免误伤
31
- }
32
- try {
33
- ctx.ui.setWidget(PROBE_KEY, undefined);
34
- } catch {
35
- // 忽略
36
- }
37
- return fullscreen;
38
- }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herbertgao/pi-cc-extensions",
3
- "version": "0.8.52",
3
+ "version": "0.8.53",
4
4
  "license": "MIT",
5
5
  "description": "A Pi productivity suite with Claude Code-style UI, context inspection, and agent/session references",
6
6
  "type": "module",
@@ -69,8 +69,8 @@
69
69
  },
70
70
  "x-upstream": {
71
71
  "package": "pi-cc-extensions",
72
- "version": "0.8.53",
72
+ "version": "0.8.54",
73
73
  "repository": "https://github.com/minuque/pi-cc-extensions",
74
- "commit": "a7aaf6d4136af44a1ef7ac4993d5754fe084c921"
74
+ "commit": "8372ecaff7f079ab4452db47328c3238620a4885"
75
75
  }
76
76
  }
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.15.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#27](https://github.com/HerbertGao/pi-extensions/pull/27) [`32bb76c`](https://github.com/HerbertGao/pi-extensions/commit/32bb76c117971de27db9c2b521d19df3ba9ea322) Thanks [@HerbertGao](https://github.com/HerbertGao)! - Keep worktree-isolated agents inside their copy, preserve real Agent tool startup errors, and render unknown or failed Agent results without misleading completion status.
8
+
3
9
  ## 0.15.2
4
10
 
5
11
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herbertgao/pi-subagents",
3
- "version": "0.15.2",
3
+ "version": "0.15.3",
4
4
  "description": "Claude Code-style autonomous subagents for Pi, with HerbertGao-maintained UI extensions.",
5
5
  "keywords": [
6
6
  "agent",
@@ -66,6 +66,6 @@
66
66
  "package": "@tintinweb/pi-subagents",
67
67
  "version": "0.15.0",
68
68
  "repository": "https://github.com/tintinweb/pi-subagents",
69
- "commit": "140324c"
69
+ "commit": "c83dd82"
70
70
  }
71
71
  }
@@ -321,6 +321,7 @@ export class AgentManager {
321
321
  // stay undefined otherwise so plain worktree runs keep resolving config
322
322
  // (incl. relative extension paths and memory) inside the worktree copy.
323
323
  cwd: worktreeCwd ?? customCwd,
324
+ worktreeBase: worktreeCwd ? baseCwd : undefined,
324
325
  configCwd:
325
326
  options.configCwd ?? (customCwd !== undefined ? ctx.cwd : undefined),
326
327
  signal: record.abortController!.signal,
@@ -414,6 +414,8 @@ export interface RunOptions {
414
414
  thinkingLevel?: ThinkingLevel
415
415
  /** Override working directory (e.g. for worktree isolation). */
416
416
  cwd?: string
417
+ /** Original checkout path when cwd is an isolated worktree copy. */
418
+ worktreeBase?: string
417
419
  /**
418
420
  * Where .pi config is discovered (project extensions, skills, pi settings,
419
421
  * agent memory). Default: same as the working directory. The manager sets
@@ -602,7 +604,7 @@ export async function runAgent(
602
604
  const parentSystemPrompt = ctx.getSystemPrompt()
603
605
 
604
606
  // Build prompt extras (memory, skill preloading)
605
- const extras: PromptExtras = {}
607
+ const extras: PromptExtras = { worktreeBase: options.worktreeBase }
606
608
 
607
609
  // Resolve extensions/skills: isolated overrides to false
608
610
  const extensions = options.isolated ? false : config.extensions
@@ -1241,12 +1241,12 @@ Terse command-style prompts produce shallow, generic work.
1241
1241
  )
1242
1242
  },
1243
1243
 
1244
- renderResult(result, { expanded, isPartial }, theme) {
1244
+ renderResult(result, { expanded, isPartial }, theme, renderContext) {
1245
+ const resultText =
1246
+ result.content[0]?.type === "text" ? result.content[0].text : ""
1245
1247
  const details = result.details as AgentDetails | undefined
1246
- if (!details) {
1247
- const text =
1248
- result.content[0]?.type === "text" ? result.content[0].text : ""
1249
- return new Text(text, 0, 0)
1248
+ if (renderContext?.isError || !details?.status) {
1249
+ return new Text(resultText, 0, 0)
1250
1250
  }
1251
1251
 
1252
1252
  // Helper: build "haiku · thinking: high · ↻5≤30 · 3 tool uses · 33.8k tokens" stats string
@@ -1301,8 +1301,6 @@ Terse command-style prompts produce shallow, generic work.
1301
1301
  line += " " + theme.fg("dim", "·") + " " + theme.fg("dim", duration)
1302
1302
 
1303
1303
  if (expanded) {
1304
- const resultText =
1305
- result.content[0]?.type === "text" ? result.content[0].text : ""
1306
1304
  if (resultText) {
1307
1305
  const lines = resultText.split("\n").slice(0, 50)
1308
1306
  for (const l of lines) {
@@ -1332,6 +1330,10 @@ Terse command-style prompts produce shallow, generic work.
1332
1330
  return new Text(line, 0, 0)
1333
1331
  }
1334
1332
 
1333
+ if (details.status !== "error" && details.status !== "aborted") {
1334
+ return new Text(resultText, 0, 0)
1335
+ }
1336
+
1335
1337
  // ---- Error / Aborted (hard max_turns) ----
1336
1338
  const s = stats(details)
1337
1339
  let line = theme.fg("error", "✗") + (s ? " " + s : "")
@@ -1598,23 +1600,22 @@ Terse command-style prompts produce shallow, generic work.
1598
1600
  }
1599
1601
  }
1600
1602
 
1601
- try {
1602
- id = manager.spawn(pi, ctx, subagentType, params.prompt, {
1603
- description: params.description,
1604
- model,
1605
- maxTurns: effectiveMaxTurns,
1606
- isolated,
1607
- inheritContext,
1608
- thinkingLevel: thinking,
1609
- isBackground: true,
1610
- isolation,
1611
- invocation: agentInvocation,
1612
- rootSessionId: ctx.sessionManager.getSessionId(),
1613
- ...bgCallbacks,
1614
- })
1615
- } catch (err) {
1616
- return textResult(err instanceof Error ? err.message : String(err))
1617
- }
1603
+ // A throw here means the agent never started. Let it out: Pi marks a
1604
+ // tool call failed only when execute throws, while a returned message
1605
+ // reads to the model as a subagent that ran and reported this.
1606
+ id = manager.spawn(pi, ctx, subagentType, params.prompt, {
1607
+ description: params.description,
1608
+ model,
1609
+ maxTurns: effectiveMaxTurns,
1610
+ isolated,
1611
+ inheritContext,
1612
+ thinkingLevel: thinking,
1613
+ isBackground: true,
1614
+ isolation,
1615
+ invocation: agentInvocation,
1616
+ rootSessionId: ctx.sessionManager.getSessionId(),
1617
+ ...bgCallbacks,
1618
+ })
1618
1619
 
1619
1620
  // Set output file + join mode synchronously after spawn, before the
1620
1621
  // event loop yields — onSessionCreated is async so this is safe.
@@ -1774,18 +1775,15 @@ Terse command-style prompts produce shallow, generic work.
1774
1775
  },
1775
1776
  )
1776
1777
  record = fgResult.record
1777
- } catch (err) {
1778
+ } finally {
1779
+ // A startup throw propagates as a failed tool call without leaving
1780
+ // the spinner running or a finished agent in the widget.
1778
1781
  clearInterval(spinnerInterval)
1779
- return textResult(err instanceof Error ? err.message : String(err))
1780
- }
1781
-
1782
- clearInterval(spinnerInterval)
1783
-
1784
- // Clean up foreground agent from widget
1785
- if (fgId) {
1786
- agentActivity.delete(fgId)
1787
- widget.markFinished(fgId)
1788
- fleet.onAgentFinished(fgId)
1782
+ if (fgId) {
1783
+ agentActivity.delete(fgId)
1784
+ widget.markFinished(fgId)
1785
+ fleet.onAgentFinished(fgId)
1786
+ }
1789
1787
  }
1790
1788
 
1791
1789
  // Get final token count
@@ -399,47 +399,38 @@ export function createNestedSubagentTools(
399
399
  // one earlier would silently give a grandchild the wrong worktree base, the
400
400
  // wrong conversation under inherit_context, and the wrong inherited model.
401
401
  //
402
- // spawn() throws on strict worktree-isolation failure and cwd validation
403
- // report it as a tool error, like the top-level Agent tool does, instead of
404
- // letting it escape into the child's turn.
405
- try {
406
- if (invocation.runInBackground) {
407
- const id = context.manager.spawn(
408
- context.pi,
409
- ctx,
410
- resolvedType,
411
- params.prompt,
412
- {
413
- ...options,
414
- isBackground: true,
415
- },
416
- )
417
- // Synchronous, before the event loop yields — onSessionCreated fires
418
- // asynchronously inside runAgent, so the file is attached in time.
419
- attachTranscript(id)
420
- return textResult(
421
- `Nested agent started in background. Agent ID: ${id}`,
422
- )
423
- }
424
-
425
- const { record } = await context.manager.spawnAndWait(
402
+ // Startup throws mean no child ran. Let them propagate so Pi marks the
403
+ // nested Agent tool call failed instead of presenting the message as a
404
+ // successful child result.
405
+ if (invocation.runInBackground) {
406
+ const id = context.manager.spawn(
426
407
  context.pi,
427
408
  ctx,
428
409
  resolvedType,
429
410
  params.prompt,
430
- { ...options, signal },
431
- attachTranscript,
432
- )
433
- return textResult(
434
- formatRecord(record, "inline"),
435
- record.status === "error",
436
- )
437
- } catch (err) {
438
- return textResult(
439
- err instanceof Error ? err.message : String(err),
440
- true,
411
+ {
412
+ ...options,
413
+ isBackground: true,
414
+ },
441
415
  )
416
+ // Synchronous, before the event loop yields — onSessionCreated fires
417
+ // asynchronously inside runAgent, so the file is attached in time.
418
+ attachTranscript(id)
419
+ return textResult(`Nested agent started in background. Agent ID: ${id}`)
442
420
  }
421
+
422
+ const { record } = await context.manager.spawnAndWait(
423
+ context.pi,
424
+ ctx,
425
+ resolvedType,
426
+ params.prompt,
427
+ { ...options, signal },
428
+ attachTranscript,
429
+ )
430
+ return textResult(
431
+ formatRecord(record, "inline"),
432
+ record.status === "error",
433
+ )
443
434
  },
444
435
  })
445
436
 
@@ -10,6 +10,8 @@ export interface PromptExtras {
10
10
  memoryBlock?: string
11
11
  /** Preloaded skill contents to inject. */
12
12
  skillBlocks?: { name: string; content: string }[]
13
+ /** Original checkout path when cwd is an isolated worktree copy. */
14
+ worktreeBase?: string
13
15
  }
14
16
 
15
17
  /**
@@ -55,6 +57,12 @@ Platform: ${env.platform}`
55
57
  }
56
58
  const extrasSuffix =
57
59
  extraSections.length > 0 ? "\n\n" + extraSections.join("\n") : ""
60
+ const worktreeSection = extras?.worktreeBase
61
+ ? `\n\n<worktree_isolation>
62
+ Your working directory is an isolated git worktree copy of ${extras.worktreeBase}.
63
+ Work only inside it — never in ${extras.worktreeBase}, even if other instructions name that path as your working directory.
64
+ </worktree_isolation>`
65
+ : ""
58
66
 
59
67
  if (config.promptMode === "append") {
60
68
  const identity = parentSystemPrompt || genericBase
@@ -88,6 +96,7 @@ You are operating as a sub-agent invoked to handle a specific task.
88
96
  "\n\n" +
89
97
  activeAgentTag +
90
98
  envBlock +
99
+ worktreeSection +
91
100
  customSection +
92
101
  extrasSuffix
93
102
  )
@@ -100,7 +109,12 @@ You have been invoked to handle a specific task autonomously.
100
109
  ${envBlock}`
101
110
 
102
111
  return (
103
- activeAgentTag + replaceHeader + "\n\n" + config.systemPrompt + extrasSuffix
112
+ activeAgentTag +
113
+ replaceHeader +
114
+ worktreeSection +
115
+ "\n\n" +
116
+ config.systemPrompt +
117
+ extrasSuffix
104
118
  )
105
119
  }
106
120
 
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.23.0] - 2026-08-11
11
+
12
+ ### Added
13
+ - Added interactive callback URL pasting to `/mcp-auth` for OAuth flows running on remote or headless machines. Thanks @trevorleibert-mixpanel for PR #330.
14
+
15
+ ### Fixed
16
+ - Stopped load-time MCP initialization from printing a TUI startup error when Pi action methods are not bound yet. Thanks @21307369 for issue #327.
17
+ - Kept interactive OAuth authorization URLs clickable as a single terminal hyperlink. Thanks @rfccg for PR #329.
18
+
19
+ ## [2.22.0] - 2026-08-11
20
+
21
+ ### Added
22
+ - Added the `pi-mcp-adapter/oauth` subpath for URL-bound OAuth token reuse by cooperating Pi extensions. Thanks @ThePhoenixCoding for issue #323.
23
+ - Added `oauth.logoUri` for OAuth Dynamic Client Registration, with validation that requires an absolute HTTP(S) URL. Thanks @grinich for PR #321.
24
+
25
+ ### Fixed
26
+ - Materialized binary MCP resources as private temporary files before model-facing output, with bounded per-session cleanup. Thanks @zenworr and @shaworr for PR #324.
27
+ - Named OAuth callback pages and dynamic client registrations after rebranded Pi hosts, while preserving stock Pi defaults and avoiding guessed client homepages. Thanks @grinich for PR #320.
28
+
10
29
  ## [2.21.2] - 2026-08-09
11
30
 
12
31
  ### Fixed
@@ -153,6 +153,17 @@ A supplied `config` is a complete, isolated snapshot. It is not merged with file
153
153
 
154
154
  With `configPath` and no `config`, the adapter keeps normal file merge behavior, and that path takes precedence over argv and `--mcp-config`. The default export keeps the normal file-based behavior. OAuth credentials are stored in the operating system credential store and keyed by the configured server name; URL binding prevents credentials from being accepted for a different server URL. `settings.oauthDir` and `MCP_OAUTH_DIR` are used only as legacy plaintext import locations for older `tokens.json` files, not as credential namespaces. CSRF state and PKCE verifiers are flow-local, so concurrent authorization flows do not share transient secrets.
155
155
 
156
+ Cooperating Pi extensions can use `pi-mcp-adapter/oauth` to reuse URL-bound OAuth tokens without deep-importing private files:
157
+
158
+ ```ts
159
+ import { getMcpOAuthTokensForUrl, updateMcpOAuthTokensForUrl } from "pi-mcp-adapter/oauth";
160
+
161
+ const tokens = await getMcpOAuthTokensForUrl("jira", "https://jira.example.com/mcp");
162
+ updateMcpOAuthTokensForUrl("jira", "https://jira.example.com/mcp", { accessToken: "..." });
163
+ ```
164
+
165
+ The public subpath exposes only token read/update helpers plus a status helper. The async read path uses the adapter's refresh logic before it returns tokens. The helpers keep secure-store storage, URL binding, refresh persistence, chunk handling, legacy import, and fail-closed credential-store errors. They do not expose client registration secrets, PKCE verifiers, or OAuth state.
166
+
156
167
  ### Runtime status snapshots
157
168
 
158
169
  Extensions can subscribe to the adapter's versioned shared event-bus channel instead of parsing `/mcp` or `mcp({})` output:
@@ -203,7 +214,8 @@ In the configuration examples below, `30000` is illustrative only. If `requestTi
203
214
  | `oauth.scope` | Requested OAuth scopes |
204
215
  | `oauth.redirectUri` | Exact localhost redirect URI for browser OAuth, including port and path, for providers that pre-register callbacks |
205
216
  | `oauth.clientName` | Client display name advertised during Dynamic Client Registration fallback |
206
- | `oauth.clientUri` | Client homepage URI advertised during Dynamic Client Registration fallback |
217
+ | `oauth.clientUri` | Client homepage URI advertised during Dynamic Client Registration fallback. Defaults to `piConfig.clientUri` from the host's manifest when set, and is omitted rather than guessed under a rebranded host |
218
+ | `oauth.logoUri` | Client logo URL advertised during Dynamic Client Registration fallback (RFC 7591 `logo_uri`). Must be an absolute `http(s)` URL — consent screens fetch it server-side, so local paths render nothing. Omitted from the registration request when unset |
207
219
  | `oauth.skipIssuerMetadataValidation` | `true` disables the OAuth authorization-server metadata issuer check for this server. This weakens OAuth mix-up protection and should only be used for known-misconfigured internal servers while their metadata is being fixed. |
208
220
  | `bearerToken` / `bearerTokenEnv` | Token or env var name; `bearerToken` supports `${VAR}` and `$env:VAR` interpolation. A leading `!` in `bearerToken` runs a command when the HTTP server connects; use `!!` for a literal leading `!`. |
209
221
  | `lifecycle` | `"lazy"` (default), `"eager"`, `"keep-alive"`, or `"lazy-keep-alive"` |
@@ -253,7 +265,9 @@ The adapter owns only its client socket and closes that connection when the Pi r
253
265
 
254
266
  ### Remote/headless OAuth
255
267
 
256
- If Pi is running on a remote server and cannot open a local browser, start OAuth through the proxy tool. Persistent OAuth still requires an available OS credential store; on headless Linux that usually means an unlocked Secret Service/libsecret keyring. The adapter fails closed instead of falling back to plaintext credentials when the secure store is unavailable.
268
+ If Pi is running on a remote server, `/mcp-auth <server>` prints the authorization URL and opens a callback input. Open the URL in your local browser. After approval, the browser may fail to load the localhost callback page because localhost refers to your workstation; copy the full URL from its address bar and paste it into Pi. The input closes automatically instead when the browser can reach Pi's callback directly.
269
+
270
+ The same flow is available through the proxy tool for non-interactive clients. Persistent OAuth still requires an available OS credential store; on headless Linux that usually means an unlocked Secret Service/libsecret keyring. The adapter fails closed instead of falling back to plaintext credentials when the secure store is unavailable.
257
271
 
258
272
  On Linux, if credential access fails because Pi inherited a revoked session keyring, the adapter uses a best-effort recovery path through `keyctl session - node <packaged helper>` so explicit re-authentication can write fresh credentials without killing a long-lived tmux server. This path requires `keyctl` and `node` on `PATH`; missing, locked, or otherwise unavailable credential stores still fail closed.
259
273
 
@@ -372,9 +386,10 @@ Oversized MCP tool/resource results are guarded by default so a single huge resp
372
386
 
373
387
  - Inline text output is capped at **50 KiB / 2,000 lines** (matching Pi's built-in `bash` guard). Larger output is truncated to a head preview and the full text is saved to a temp file whose path is included in the result, so the agent can `read`/`grep` it.
374
388
  - **Image content blocks pass through unchanged** — only text output is guarded. Images are delivered to the provider as native image content.
389
+ - Binary resource blobs up to **10 MiB** are decoded to private temp files and replaced with file references. Each session is limited to **100 MiB** and **10,000 files**. The files are removed at session teardown.
375
390
  - In proxy mode, `details.mcpResult` is kept raw when its JSON is **≤ 16 KiB**; larger results are replaced with a compact summary (block counts, sizes, key previews) and the raw JSON is saved to a temp file. Direct tools keep their lean details and never carry `mcpResult`.
376
391
 
377
- Tune the limits with the object form:
392
+ Tune the text and details limits with the object form:
378
393
 
379
394
  ```json
380
395
  {
@@ -384,7 +399,7 @@ Tune the limits with the object form:
384
399
  }
385
400
  ```
386
401
 
387
- Set `"outputGuard": false` — or the env kill switch `MCP_OUTPUT_GUARD=0` — to disable the guard and restore raw output behavior. Saved temp files are created with mode `0600` under the system temp directory and are not cleaned up automatically; note that spilled MCP output may contain sensitive data.
402
+ Set `"outputGuard": false` — or the env kill switch `MCP_OUTPUT_GUARD=0` — to disable text and details guarding. Binary resource materialization and its safety limits remain active. Output-guard spill files are created with mode `0600` under the system temp directory and are not cleaned up automatically; note that spilled MCP output may contain sensitive data.
388
403
 
389
404
  ### MCP Scripting
390
405
 
@@ -1,3 +1,4 @@
1
+ import { readFileSync } from "node:fs";
1
2
  import { homedir } from "node:os";
2
3
  import { join, resolve } from "node:path";
3
4
 
@@ -18,3 +19,42 @@ export function getAgentDir(): string {
18
19
  export function getAgentPath(...segments: string[]): string {
19
20
  return join(getAgentDir(), ...segments);
20
21
  }
22
+
23
+ /**
24
+ * What the host calls itself.
25
+ *
26
+ * pi supports rebranding through `piConfig.name` in the package.json that its
27
+ * `getPackageDir()` resolves, and distributions built on pi (arc, tau, …) point
28
+ * `PI_PACKAGE_DIR` at their own manifest. Read that manifest directly rather
29
+ * than importing pi: this package deliberately depends on pi-ai and pi-tui
30
+ * only, and `getAgentDir()` above reads its env var the same self-contained way.
31
+ *
32
+ * Falls back to "pi", which is what pi's own APP_NAME resolves to.
33
+ */
34
+ function readPiConfig(): { name?: unknown; clientUri?: unknown } | undefined {
35
+ const dir = process.env.PI_PACKAGE_DIR?.trim()
36
+ if (!dir) return undefined
37
+ try {
38
+ const manifest = JSON.parse(readFileSync(join(resolve(dir), "package.json"), "utf8")) as {
39
+ piConfig?: { name?: unknown; clientUri?: unknown }
40
+ }
41
+ return manifest.piConfig
42
+ } catch {
43
+ return undefined
44
+ }
45
+ }
46
+
47
+ export function getAppName(): string {
48
+ const name = readPiConfig()?.name
49
+ return typeof name === "string" && name.trim() ? name.trim() : "pi"
50
+ }
51
+
52
+ /**
53
+ * Home page the host declares for itself, via `piConfig.clientUri` in the same
54
+ * manifest that carries `piConfig.name`. Only the distribution knows its own
55
+ * URL, so this is the one place it can come from without guessing.
56
+ */
57
+ export function getAppClientUri(): string | undefined {
58
+ const uri = readPiConfig()?.clientUri
59
+ return typeof uri === "string" && uri.trim() ? uri.trim() : undefined
60
+ }
@@ -25,6 +25,10 @@ import { loadOnboardingState, markSetupCompleted as persistSetupCompleted, markS
25
25
  import { openPath, resolveServerUrl, sanitizeTerminalText } from "./utils.ts";
26
26
  import { isAbortError } from "./runtime-owner.ts";
27
27
 
28
+ function terminalHyperlink(label: string, url: string): string {
29
+ return `\u001B]8;;${sanitizeTerminalText(url)}\u001B\\${sanitizeTerminalText(label)}\u001B]8;;\u001B\\`;
30
+ }
31
+
28
32
  export async function showStatus(state: McpExtensionState, ctx: ExtensionContext): Promise<void> {
29
33
  if (!ctx.hasUI) return;
30
34
 
@@ -273,11 +277,17 @@ export async function authenticateServer(
273
277
  ...(authStorageOptions.baseDir ? { authStorageOptions } : {}),
274
278
  onAuthorizationUrl: (authorizationUrl) => {
275
279
  ui.notify(
276
- `Open this URL to authenticate ${serverName}:\n\n${authorizationUrl}\n\n` +
277
- "After approving, return to Pi; the local callback will complete automatically.",
280
+ `Open this URL to authenticate ${serverName}:\n\n${terminalHyperlink(authorizationUrl, authorizationUrl)}\n\n` +
281
+ "After approving, Pi will complete automatically if the browser can reach its localhost callback. " +
282
+ "On a remote machine, copy the full localhost URL from the browser address bar and paste it into Pi.",
278
283
  "info"
279
284
  );
280
285
  },
286
+ onAuthorizationInput: (_authorizationUrl, inputSignal) => ui.input(
287
+ `Complete ${serverName} OAuth`,
288
+ "Paste the full callback URL, or wait for automatic completion",
289
+ { signal: inputSignal },
290
+ ),
281
291
  ...(signal ? { signal } : {}),
282
292
  ...(runtime ? { runtime } : {}),
283
293
  });
@@ -8,7 +8,7 @@ import { abortable, throwIfAborted } from "./abort.ts";
8
8
  import { isServerCacheValid, parseDirectToolSelectors } from "./metadata-cache.ts";
9
9
  export { getMissingConfiguredDirectToolServers } from "./metadata-cache.ts";
10
10
  import { formatSchema } from "./tool-metadata.ts";
11
- import { resolveMcpResultContent, transformMcpContent } from "./tool-registrar.ts";
11
+ import { resolveMcpResultContent, transformMcpContent, transformMcpResourceContents } from "./tool-registrar.ts";
12
12
  import { guardMcpOutput, guardedMcpDetails, resolveMcpOutputGuardOptions } from "./mcp-output-guard.ts";
13
13
  import { maybeStartUiSession, summarizeUiSessionResult, type UiSessionRuntime } from "./ui-session.ts";
14
14
  import { formatToolName, isServerDisabled, isToolAllowed, resolveToolPrefix } from "./types.ts";
@@ -20,6 +20,7 @@ import { combineAbortSignals, isAbortError } from "./runtime-owner.ts";
20
20
  import { ensureToolCallApproved } from "./tool-approval.ts";
21
21
 
22
22
  type ClientCallToolResult = Awaited<ReturnType<Client["callTool"]>>;
23
+ type ClientReadResourceResult = Awaited<ReturnType<Client["readResource"]>>;
23
24
 
24
25
  const BUILTIN_NAMES = new Set(["read", "bash", "edit", "write", "grep", "find", "ls", "mcp"]);
25
26
  const INSTRUCTIONS_SNIPPET_LENGTH = 150;
@@ -435,7 +436,7 @@ export function createDirectToolExecutor(
435
436
  state.manager.incrementInFlight(spec.serverName);
436
437
 
437
438
  if (spec.resourceUri) {
438
- const result = await withSessionRecovery(
439
+ const result = await withSessionRecovery<ClientReadResourceResult>(
439
440
  {
440
441
  manager: state.manager,
441
442
  config: state.config,
@@ -445,10 +446,7 @@ export function createDirectToolExecutor(
445
446
  spec.serverName,
446
447
  (conn) => conn.client.readResource({ uri: spec.resourceUri! }, requestOptions),
447
448
  );
448
- const content = (result.contents ?? []).map(c => ({
449
- type: "text" as const,
450
- text: "text" in c ? c.text : ("blob" in c ? `[Binary data: ${(c as { mimeType?: string }).mimeType ?? "unknown"}]` : JSON.stringify(c)),
451
- }));
449
+ const content = transformMcpResourceContents(result.contents ?? [], state.owner?.signal);
452
450
  const guarded = await guardMcpOutput(content.length > 0 ? content : [{ type: "text" as const, text: "(empty resource)" }], outputGuardOptions);
453
451
  return {
454
452
  content: guarded.content,
@@ -487,7 +485,7 @@ export function createDirectToolExecutor(
487
485
 
488
486
  if (result.isError) {
489
487
  const mcpContent = (result.content ?? []) as McpContent[];
490
- const content = transformMcpContent(mcpContent);
488
+ const content = transformMcpContent(mcpContent, state.owner?.signal);
491
489
  const outputContent = content.length > 0 ? content : [{ type: "text" as const, text: "(empty result)" }];
492
490
  const schemaText = spec.inputSchema ? `\n\nExpected parameters:\n${formatSchema(spec.inputSchema)}` : "";
493
491
  const guarded = await guardMcpOutput(outputContent, { ...outputGuardOptions, prefix: "Error: ", suffix: schemaText, emptyTextFallback: "Tool execution failed" });
@@ -497,7 +495,7 @@ export function createDirectToolExecutor(
497
495
  };
498
496
  }
499
497
 
500
- const content = resolveMcpResultContent(result as Record<string, unknown>);
498
+ const content = resolveMcpResultContent(result as Record<string, unknown>, state.owner?.signal);
501
499
  const outputContent = content.length > 0 ? content : [{ type: "text" as const, text: "(empty result)" }];
502
500
  if (hasUi) {
503
501
  const uiSummary = summarizeUiSessionResult(uiSession);