@herbertgao/pi-extensions 2026.8.2 → 2026.8.4

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 (392) hide show
  1. package/README.md +50 -2
  2. package/examples/pi-footer.json +275 -0
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +6 -8
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.md +5 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +101 -0
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +1 -17
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +5 -3
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{utils/agent-summary.ts → feature/agent-summary/core.ts} +37 -35
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-summary.ts → agent-summary/index.ts} +2 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference.ts → reference/index.ts} +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-autocomplete.ts → reference/subagent.ts} +19 -9
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-startup-header.ts → shell/startup-header.ts} +1 -1
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{working-message.ts → shell/working-message.ts} +5 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +32 -28
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +379 -85
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +727 -0
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +65 -775
  18. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{feature → renderer}/markdown-enhance.ts +35 -31
  19. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-hover.ts → mouse/hover.ts} +5 -5
  20. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-interaction.ts → mouse/interaction.ts} +34 -35
  21. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-layout.ts → mouse/layout.ts} +5 -21
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-packets.ts → mouse/packets.ts} +1 -1
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-scroll.ts → mouse/scroll.ts} +48 -37
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ansi-utils.ts +56 -1
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-renderer.ts +2 -2
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/line-width-safety.ts +1 -1
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +12 -0
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/write-execution.ts +1 -2
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-grouping.ts → tool/grouping.ts} +18 -8
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-result.ts → tool/result.ts} +4 -13
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +0 -27
  32. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  33. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  34. package/node_modules/@herbertgao/pi-subagents/package.json +2 -2
  35. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +1 -0
  36. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +3 -1
  37. package/node_modules/@herbertgao/pi-subagents/src/index.ts +33 -35
  38. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +26 -35
  39. package/node_modules/@herbertgao/pi-subagents/src/prompts.ts +15 -1
  40. package/node_modules/pi-footer/CHANGELOG.md +145 -0
  41. package/node_modules/pi-footer/LICENSE +21 -0
  42. package/node_modules/pi-footer/README.md +354 -0
  43. package/node_modules/pi-footer/package.json +68 -0
  44. package/node_modules/pi-footer/src/cache.ts +106 -0
  45. package/node_modules/pi-footer/src/colors.ts +237 -0
  46. package/node_modules/pi-footer/src/config.ts +202 -0
  47. package/node_modules/pi-footer/src/event-widgets.ts +41 -0
  48. package/node_modules/pi-footer/src/extension-statuses.ts +102 -0
  49. package/node_modules/pi-footer/src/git.ts +135 -0
  50. package/node_modules/pi-footer/src/index.ts +268 -0
  51. package/node_modules/pi-footer/src/metrics.ts +141 -0
  52. package/node_modules/pi-footer/src/presets.ts +383 -0
  53. package/node_modules/pi-footer/src/render.ts +114 -0
  54. package/node_modules/pi-footer/src/separators.ts +52 -0
  55. package/node_modules/pi-footer/src/types.ts +122 -0
  56. package/node_modules/pi-footer/src/ui/color-level-confirm.ts +22 -0
  57. package/node_modules/pi-footer/src/ui/color-options.ts +195 -0
  58. package/node_modules/pi-footer/src/ui/config-lifecycle.ts +61 -0
  59. package/node_modules/pi-footer/src/ui/edit-colors.ts +13 -0
  60. package/node_modules/pi-footer/src/ui/events.ts +35 -0
  61. package/node_modules/pi-footer/src/ui/extension-status-picker.ts +60 -0
  62. package/node_modules/pi-footer/src/ui/extension-statuses.ts +73 -0
  63. package/node_modules/pi-footer/src/ui/fields.ts +252 -0
  64. package/node_modules/pi-footer/src/ui/global-menu.ts +126 -0
  65. package/node_modules/pi-footer/src/ui/helpers.ts +44 -0
  66. package/node_modules/pi-footer/src/ui/layout.ts +17 -0
  67. package/node_modules/pi-footer/src/ui/line-list.ts +19 -0
  68. package/node_modules/pi-footer/src/ui/model.ts +64 -0
  69. package/node_modules/pi-footer/src/ui/navigation.ts +29 -0
  70. package/node_modules/pi-footer/src/ui/option-edit.ts +93 -0
  71. package/node_modules/pi-footer/src/ui/overlay-render.ts +103 -0
  72. package/node_modules/pi-footer/src/ui/screen-context.ts +18 -0
  73. package/node_modules/pi-footer/src/ui/screen-controller.ts +27 -0
  74. package/node_modules/pi-footer/src/ui/screen-render.ts +32 -0
  75. package/node_modules/pi-footer/src/ui/screen-state.ts +20 -0
  76. package/node_modules/pi-footer/src/ui/screen.ts +243 -0
  77. package/node_modules/pi-footer/src/ui/screens/add-widget.ts +92 -0
  78. package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +52 -0
  79. package/node_modules/pi-footer/src/ui/screens/controller.ts +12 -0
  80. package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +68 -0
  81. package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +115 -0
  82. package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +61 -0
  83. package/node_modules/pi-footer/src/ui/screens/global.ts +75 -0
  84. package/node_modules/pi-footer/src/ui/screens/line-list.ts +108 -0
  85. package/node_modules/pi-footer/src/ui/screens/main.ts +99 -0
  86. package/node_modules/pi-footer/src/ui/screens/terminal.ts +121 -0
  87. package/node_modules/pi-footer/src/ui/screens/widget-list.ts +154 -0
  88. package/node_modules/pi-footer/src/ui/terminal-menu.ts +32 -0
  89. package/node_modules/pi-footer/src/ui/theme.ts +48 -0
  90. package/node_modules/pi-footer/src/ui/title-bar.ts +100 -0
  91. package/node_modules/pi-footer/src/ui/widget-actions.ts +76 -0
  92. package/node_modules/pi-footer/src/ui.ts +66 -0
  93. package/node_modules/pi-footer/src/widgets/context.ts +28 -0
  94. package/node_modules/pi-footer/src/widgets/core/active-tools.ts +17 -0
  95. package/node_modules/pi-footer/src/widgets/core/context-window.ts +24 -0
  96. package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +19 -0
  97. package/node_modules/pi-footer/src/widgets/core/cwd.ts +108 -0
  98. package/node_modules/pi-footer/src/widgets/core/event.ts +34 -0
  99. package/node_modules/pi-footer/src/widgets/core/external-status.ts +102 -0
  100. package/node_modules/pi-footer/src/widgets/core/model-provider.ts +20 -0
  101. package/node_modules/pi-footer/src/widgets/core/model.ts +35 -0
  102. package/node_modules/pi-footer/src/widgets/core/provider.ts +17 -0
  103. package/node_modules/pi-footer/src/widgets/core/session-name.ts +17 -0
  104. package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +17 -0
  105. package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +17 -0
  106. package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +17 -0
  107. package/node_modules/pi-footer/src/widgets/git/branch.ts +81 -0
  108. package/node_modules/pi-footer/src/widgets/git/clean.ts +19 -0
  109. package/node_modules/pi-footer/src/widgets/git/deletions.ts +17 -0
  110. package/node_modules/pi-footer/src/widgets/git/diff.ts +38 -0
  111. package/node_modules/pi-footer/src/widgets/git/insertions.ts +17 -0
  112. package/node_modules/pi-footer/src/widgets/git/remote.ts +17 -0
  113. package/node_modules/pi-footer/src/widgets/git/root.ts +17 -0
  114. package/node_modules/pi-footer/src/widgets/git/sha.ts +17 -0
  115. package/node_modules/pi-footer/src/widgets/git/staged.ts +17 -0
  116. package/node_modules/pi-footer/src/widgets/git/status.ts +19 -0
  117. package/node_modules/pi-footer/src/widgets/git/unstaged.ts +17 -0
  118. package/node_modules/pi-footer/src/widgets/git/untracked.ts +17 -0
  119. package/node_modules/pi-footer/src/widgets/instance.ts +118 -0
  120. package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +18 -0
  121. package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +32 -0
  122. package/node_modules/pi-footer/src/widgets/layout/separator.ts +57 -0
  123. package/node_modules/pi-footer/src/widgets/layout/spacer.ts +31 -0
  124. package/node_modules/pi-footer/src/widgets/options.ts +122 -0
  125. package/node_modules/pi-footer/src/widgets/project/runtime.ts +361 -0
  126. package/node_modules/pi-footer/src/widgets/registry.ts +223 -0
  127. package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +17 -0
  128. package/node_modules/pi-footer/src/widgets/session/compactions.ts +17 -0
  129. package/node_modules/pi-footer/src/widgets/session/elapsed.ts +18 -0
  130. package/node_modules/pi-footer/src/widgets/session/last-activity.ts +18 -0
  131. package/node_modules/pi-footer/src/widgets/session/messages.ts +19 -0
  132. package/node_modules/pi-footer/src/widgets/session/session-id.ts +17 -0
  133. package/node_modules/pi-footer/src/widgets/session/session-start.ts +18 -0
  134. package/node_modules/pi-footer/src/widgets/session/tool-results.ts +17 -0
  135. package/node_modules/pi-footer/src/widgets/session/total-messages.ts +19 -0
  136. package/node_modules/pi-footer/src/widgets/session/total-time.ts +18 -0
  137. package/node_modules/pi-footer/src/widgets/session/user-messages.ts +17 -0
  138. package/node_modules/pi-footer/src/widgets/store.ts +26 -0
  139. package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +55 -0
  140. package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +18 -0
  141. package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +18 -0
  142. package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +108 -0
  143. package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +26 -0
  144. package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +29 -0
  145. package/node_modules/pi-footer/src/widgets/tokens/context.ts +29 -0
  146. package/node_modules/pi-footer/src/widgets/tokens/cost.ts +51 -0
  147. package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +25 -0
  148. package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +18 -0
  149. package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +25 -0
  150. package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +18 -0
  151. package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +20 -0
  152. package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +25 -0
  153. package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +18 -0
  154. package/node_modules/pi-footer/src/widgets/types.ts +181 -0
  155. package/node_modules/pi-footer/src/widgets/utils/colors.ts +11 -0
  156. package/node_modules/pi-footer/src/widgets/utils/context.ts +112 -0
  157. package/node_modules/pi-footer/src/widgets/utils/session.ts +19 -0
  158. package/node_modules/pi-footer/src/widgets/utils/token-format.ts +78 -0
  159. package/node_modules/pi-mcp-adapter/CHANGELOG.md +19 -0
  160. package/node_modules/pi-mcp-adapter/README.md +19 -4
  161. package/node_modules/pi-mcp-adapter/agent-dir.ts +40 -0
  162. package/node_modules/pi-mcp-adapter/commands.ts +12 -2
  163. package/node_modules/pi-mcp-adapter/direct-tools.ts +6 -8
  164. package/node_modules/pi-mcp-adapter/index.ts +15 -3
  165. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +87 -4
  166. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +138 -61
  167. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +43 -4
  168. package/node_modules/pi-mcp-adapter/oauth.ts +48 -0
  169. package/node_modules/pi-mcp-adapter/package.json +8 -2
  170. package/node_modules/pi-mcp-adapter/proxy-modes.ts +8 -10
  171. package/node_modules/pi-mcp-adapter/tool-registrar.ts +187 -3
  172. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  173. package/node_modules/pi-web-access/CHANGELOG.md +45 -0
  174. package/node_modules/pi-web-access/README.md +104 -29
  175. package/node_modules/pi-web-access/bocha.ts +221 -0
  176. package/node_modules/pi-web-access/curator-page.ts +113 -12
  177. package/node_modules/pi-web-access/curator-server.ts +56 -20
  178. package/node_modules/pi-web-access/datalab-pdf-extract.ts +568 -0
  179. package/node_modules/pi-web-access/duckduckgo.ts +123 -0
  180. package/node_modules/pi-web-access/extract.ts +288 -143
  181. package/node_modules/pi-web-access/feature-config.ts +29 -0
  182. package/node_modules/pi-web-access/gemini-api.ts +4 -0
  183. package/node_modules/pi-web-access/gemini-search.ts +45 -12
  184. package/node_modules/pi-web-access/gemini-web.ts +68 -3
  185. package/node_modules/pi-web-access/index.ts +160 -71
  186. package/node_modules/pi-web-access/jina-search.ts +284 -0
  187. package/node_modules/pi-web-access/kagi.ts +12 -12
  188. package/node_modules/pi-web-access/openai-search.ts +12 -2
  189. package/node_modules/pi-web-access/package.json +4 -3
  190. package/node_modules/pi-web-access/pdf-extract.ts +327 -228
  191. package/node_modules/pi-web-access/storage.ts +440 -2
  192. package/node_modules/pi-web-access/summary-model-scope.ts +1 -1
  193. package/node_modules/pi-web-access/summary-review.ts +4 -2
  194. package/node_modules/pi-web-access/video-extract.ts +10 -8
  195. package/node_modules/pi-web-access/xai-search.ts +12 -2
  196. package/node_modules/pi-web-access/youtube-extract.ts +2 -1
  197. package/node_modules/remote-pi/LICENSE +21 -0
  198. package/node_modules/remote-pi/README.md +675 -0
  199. package/node_modules/remote-pi/dist/actions/handlers.d.ts +122 -0
  200. package/node_modules/remote-pi/dist/actions/handlers.js +160 -0
  201. package/node_modules/remote-pi/dist/actions/handlers.js.map +1 -0
  202. package/node_modules/remote-pi/dist/actions/registry.d.ts +39 -0
  203. package/node_modules/remote-pi/dist/actions/registry.js +58 -0
  204. package/node_modules/remote-pi/dist/actions/registry.js.map +1 -0
  205. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +2 -0
  206. package/node_modules/remote-pi/dist/bin/supervisord.js +86 -0
  207. package/node_modules/remote-pi/dist/bin/supervisord.js.map +1 -0
  208. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +58 -0
  209. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +71 -0
  210. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +1 -0
  211. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +60 -0
  212. package/node_modules/remote-pi/dist/commands/list_commands.js +73 -0
  213. package/node_modules/remote-pi/dist/commands/list_commands.js.map +1 -0
  214. package/node_modules/remote-pi/dist/config.d.ts +62 -0
  215. package/node_modules/remote-pi/dist/config.js +115 -0
  216. package/node_modules/remote-pi/dist/config.js.map +1 -0
  217. package/node_modules/remote-pi/dist/daemon/client.d.ts +20 -0
  218. package/node_modules/remote-pi/dist/daemon/client.js +131 -0
  219. package/node_modules/remote-pi/dist/daemon/client.js.map +1 -0
  220. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +176 -0
  221. package/node_modules/remote-pi/dist/daemon/control_protocol.js +63 -0
  222. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +1 -0
  223. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +45 -0
  224. package/node_modules/remote-pi/dist/daemon/cron_log.js +72 -0
  225. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +1 -0
  226. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +80 -0
  227. package/node_modules/remote-pi/dist/daemon/cron_registry.js +194 -0
  228. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +1 -0
  229. package/node_modules/remote-pi/dist/daemon/id.d.ts +24 -0
  230. package/node_modules/remote-pi/dist/daemon/id.js +36 -0
  231. package/node_modules/remote-pi/dist/daemon/id.js.map +1 -0
  232. package/node_modules/remote-pi/dist/daemon/install.d.ts +170 -0
  233. package/node_modules/remote-pi/dist/daemon/install.js +547 -0
  234. package/node_modules/remote-pi/dist/daemon/install.js.map +1 -0
  235. package/node_modules/remote-pi/dist/daemon/registry.d.ts +63 -0
  236. package/node_modules/remote-pi/dist/daemon/registry.js +153 -0
  237. package/node_modules/remote-pi/dist/daemon/registry.js.map +1 -0
  238. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +194 -0
  239. package/node_modules/remote-pi/dist/daemon/rpc_child.js +389 -0
  240. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +1 -0
  241. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +93 -0
  242. package/node_modules/remote-pi/dist/daemon/supervisor.js +596 -0
  243. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +1 -0
  244. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +26 -0
  245. package/node_modules/remote-pi/dist/extension_ui_bridge.js +341 -0
  246. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +1 -0
  247. package/node_modules/remote-pi/dist/index.d.ts +213 -0
  248. package/node_modules/remote-pi/dist/index.js +4996 -0
  249. package/node_modules/remote-pi/dist/index.js.map +1 -0
  250. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +10 -0
  251. package/node_modules/remote-pi/dist/mcp/mesh_result.js +42 -0
  252. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +1 -0
  253. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +16 -0
  254. package/node_modules/remote-pi/dist/mcp/mesh_server.js +327 -0
  255. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +1 -0
  256. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +30 -0
  257. package/node_modules/remote-pi/dist/mesh/canonical.js +61 -0
  258. package/node_modules/remote-pi/dist/mesh/canonical.js.map +1 -0
  259. package/node_modules/remote-pi/dist/mesh/client.d.ts +18 -0
  260. package/node_modules/remote-pi/dist/mesh/client.js +125 -0
  261. package/node_modules/remote-pi/dist/mesh/client.js.map +1 -0
  262. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +38 -0
  263. package/node_modules/remote-pi/dist/mesh/encoding.js +216 -0
  264. package/node_modules/remote-pi/dist/mesh/encoding.js.map +1 -0
  265. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +73 -0
  266. package/node_modules/remote-pi/dist/mesh/self_revoke.js +401 -0
  267. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +1 -0
  268. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +49 -0
  269. package/node_modules/remote-pi/dist/mesh/siblings.js +159 -0
  270. package/node_modules/remote-pi/dist/mesh/siblings.js.map +1 -0
  271. package/node_modules/remote-pi/dist/mesh/types.d.ts +34 -0
  272. package/node_modules/remote-pi/dist/mesh/types.js +11 -0
  273. package/node_modules/remote-pi/dist/mesh/types.js.map +1 -0
  274. package/node_modules/remote-pi/dist/mesh/verify.d.ts +17 -0
  275. package/node_modules/remote-pi/dist/mesh/verify.js +86 -0
  276. package/node_modules/remote-pi/dist/mesh/verify.js.map +1 -0
  277. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +8 -0
  278. package/node_modules/remote-pi/dist/pairing/crypto.js +22 -0
  279. package/node_modules/remote-pi/dist/pairing/crypto.js.map +1 -0
  280. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +28 -0
  281. package/node_modules/remote-pi/dist/pairing/handshake.js +113 -0
  282. package/node_modules/remote-pi/dist/pairing/handshake.js.map +1 -0
  283. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +16 -0
  284. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +103 -0
  285. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +1 -0
  286. package/node_modules/remote-pi/dist/pairing/qr.d.ts +59 -0
  287. package/node_modules/remote-pi/dist/pairing/qr.js +125 -0
  288. package/node_modules/remote-pi/dist/pairing/qr.js.map +1 -0
  289. package/node_modules/remote-pi/dist/pairing/storage.d.ts +120 -0
  290. package/node_modules/remote-pi/dist/pairing/storage.js +604 -0
  291. package/node_modules/remote-pi/dist/pairing/storage.js.map +1 -0
  292. package/node_modules/remote-pi/dist/protocol/codec.d.ts +7 -0
  293. package/node_modules/remote-pi/dist/protocol/codec.js +49 -0
  294. package/node_modules/remote-pi/dist/protocol/codec.js.map +1 -0
  295. package/node_modules/remote-pi/dist/protocol/types.d.ts +393 -0
  296. package/node_modules/remote-pi/dist/protocol/types.js +2 -0
  297. package/node_modules/remote-pi/dist/protocol/types.js.map +1 -0
  298. package/node_modules/remote-pi/dist/rooms.d.ts +29 -0
  299. package/node_modules/remote-pi/dist/rooms.js +57 -0
  300. package/node_modules/remote-pi/dist/rooms.js.map +1 -0
  301. package/node_modules/remote-pi/dist/session/address.d.ts +49 -0
  302. package/node_modules/remote-pi/dist/session/address.js +58 -0
  303. package/node_modules/remote-pi/dist/session/address.js.map +1 -0
  304. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +55 -0
  305. package/node_modules/remote-pi/dist/session/agent_bridge.js +146 -0
  306. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +1 -0
  307. package/node_modules/remote-pi/dist/session/bridge.d.ts +37 -0
  308. package/node_modules/remote-pi/dist/session/bridge.js +130 -0
  309. package/node_modules/remote-pi/dist/session/bridge.js.map +1 -0
  310. package/node_modules/remote-pi/dist/session/broker.d.ts +189 -0
  311. package/node_modules/remote-pi/dist/session/broker.js +507 -0
  312. package/node_modules/remote-pi/dist/session/broker.js.map +1 -0
  313. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +74 -0
  314. package/node_modules/remote-pi/dist/session/broker_remote.js +583 -0
  315. package/node_modules/remote-pi/dist/session/broker_remote.js.map +1 -0
  316. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +33 -0
  317. package/node_modules/remote-pi/dist/session/cwd_lock.js +126 -0
  318. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +1 -0
  319. package/node_modules/remote-pi/dist/session/envelope.d.ts +33 -0
  320. package/node_modules/remote-pi/dist/session/envelope.js +117 -0
  321. package/node_modules/remote-pi/dist/session/envelope.js.map +1 -0
  322. package/node_modules/remote-pi/dist/session/global_config.d.ts +33 -0
  323. package/node_modules/remote-pi/dist/session/global_config.js +73 -0
  324. package/node_modules/remote-pi/dist/session/global_config.js.map +1 -0
  325. package/node_modules/remote-pi/dist/session/ipc.d.ts +27 -0
  326. package/node_modules/remote-pi/dist/session/ipc.js +22 -0
  327. package/node_modules/remote-pi/dist/session/ipc.js.map +1 -0
  328. package/node_modules/remote-pi/dist/session/leader_election.d.ts +32 -0
  329. package/node_modules/remote-pi/dist/session/leader_election.js +106 -0
  330. package/node_modules/remote-pi/dist/session/leader_election.js.map +1 -0
  331. package/node_modules/remote-pi/dist/session/local_config.d.ts +55 -0
  332. package/node_modules/remote-pi/dist/session/local_config.js +157 -0
  333. package/node_modules/remote-pi/dist/session/local_config.js.map +1 -0
  334. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +20 -0
  335. package/node_modules/remote-pi/dist/session/mesh_message.js +28 -0
  336. package/node_modules/remote-pi/dist/session/mesh_message.js.map +1 -0
  337. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +116 -0
  338. package/node_modules/remote-pi/dist/session/mesh_node.js +573 -0
  339. package/node_modules/remote-pi/dist/session/mesh_node.js.map +1 -0
  340. package/node_modules/remote-pi/dist/session/peer.d.ts +139 -0
  341. package/node_modules/remote-pi/dist/session/peer.js +451 -0
  342. package/node_modules/remote-pi/dist/session/peer.js.map +1 -0
  343. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +13 -0
  344. package/node_modules/remote-pi/dist/session/peer_inventory.js +48 -0
  345. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +1 -0
  346. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +9 -0
  347. package/node_modules/remote-pi/dist/session/peer_limits.js +21 -0
  348. package/node_modules/remote-pi/dist/session/peer_limits.js.map +1 -0
  349. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +39 -0
  350. package/node_modules/remote-pi/dist/session/setup_wizard.js +63 -0
  351. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +1 -0
  352. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +5 -0
  353. package/node_modules/remote-pi/dist/session/tool_gate.js +11 -0
  354. package/node_modules/remote-pi/dist/session/tool_gate.js.map +1 -0
  355. package/node_modules/remote-pi/dist/session/tools.d.ts +24 -0
  356. package/node_modules/remote-pi/dist/session/tools.js +252 -0
  357. package/node_modules/remote-pi/dist/session/tools.js.map +1 -0
  358. package/node_modules/remote-pi/dist/session/wizard.d.ts +13 -0
  359. package/node_modules/remote-pi/dist/session/wizard.js +20 -0
  360. package/node_modules/remote-pi/dist/session/wizard.js.map +1 -0
  361. package/node_modules/remote-pi/dist/settings.d.ts +15 -0
  362. package/node_modules/remote-pi/dist/settings.js +52 -0
  363. package/node_modules/remote-pi/dist/settings.js.map +1 -0
  364. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +37 -0
  365. package/node_modules/remote-pi/dist/transport/peer_channel.js +98 -0
  366. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +1 -0
  367. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +29 -0
  368. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +62 -0
  369. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +1 -0
  370. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +91 -0
  371. package/node_modules/remote-pi/dist/transport/relay_client.js +206 -0
  372. package/node_modules/remote-pi/dist/transport/relay_client.js.map +1 -0
  373. package/node_modules/remote-pi/dist/ui/footer.d.ts +32 -0
  374. package/node_modules/remote-pi/dist/ui/footer.js +34 -0
  375. package/node_modules/remote-pi/dist/ui/footer.js.map +1 -0
  376. package/node_modules/remote-pi/docs/daemon.md +289 -0
  377. package/node_modules/remote-pi/package.json +88 -0
  378. package/node_modules/remote-pi/service-templates/launchd.plist.template +35 -0
  379. package/node_modules/remote-pi/service-templates/systemd.service.template +19 -0
  380. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +10 -0
  381. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +38 -0
  382. package/node_modules/remote-pi/skills/agent-network/SKILL.md +310 -0
  383. package/package.json +25 -10
  384. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/write-display-utils.ts +0 -29
  385. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference-core.ts → reference/session.ts} +0 -0
  386. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-aliases.ts → shell/aliases.ts} +0 -0
  387. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ATTRIBUTION.md +0 -0
  388. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-presentation.ts +0 -0
  389. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/index.ts +0 -0
  390. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/render-utils.ts +0 -0
  391. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/shiki-highlight.ts +0 -0
  392. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/types.ts +0 -0
@@ -17,6 +17,7 @@ import {
17
17
  getAllResults,
18
18
  getResult,
19
19
  restoreFromSession,
20
+ storeFetchedContentResult,
20
21
  storeResult,
21
22
  type QueryResultData,
22
23
  type StoredSearchData,
@@ -26,6 +27,7 @@ import { startCuratorServer, type CuratorSearchEntry, type CuratorServerHandle,
26
27
  import {
27
28
  buildDeterministicSummary,
28
29
  generateSummaryDraft,
30
+ SUMMARY_GENERATION_DEADLINE_MS,
29
31
  type SummaryGenerationContext,
30
32
  type SummaryMeta,
31
33
  } from "./summary-review.ts";
@@ -48,10 +50,13 @@ import { isSearch1APIAvailable } from "./search1api.ts";
48
50
  import { isSearchinfinityAvailable } from "./searchinfinity.ts";
49
51
  import { isQueritAvailable } from "./querit.ts";
50
52
  import { isTavilyAvailable } from "./tavily.ts";
53
+ import { isJinaSearchAvailable } from "./jina-search.ts";
51
54
  import { isSerpdiveAvailable } from "./serpdive.ts";
52
55
  import { isKagiAvailable } from "./kagi.ts";
56
+ import { isBochaAvailable } from "./bocha.ts";
53
57
  import { isOllamaAvailable } from "./ollama.ts";
54
58
  import { isSearXNGAvailable } from "./searxng.ts";
59
+ import { isDuckDuckGoAvailable } from "./duckduckgo.ts";
55
60
  import { isAnySearchAvailable } from "./anysearch.ts";
56
61
  import { isXaiSearchAvailable } from "./xai-search.ts";
57
62
  import { isBrightDataAvailable } from "./brightdata.ts";
@@ -119,9 +124,13 @@ interface WebSearchConfig {
119
124
  autoOpenBrowser?: unknown;
120
125
  curatorRemote?: unknown;
121
126
  summaryModel?: string;
127
+ summaryGenerationDeadlineMs?: unknown;
128
+ maxInlineContentChars?: unknown;
122
129
  webSearch?: {
123
130
  enabled?: boolean;
124
131
  };
132
+ tools?: Partial<Record<keyof ToolNames, { enabled?: boolean }>>;
133
+ commands?: Partial<Record<"websearch" | "curator" | "search" | "google-account", { enabled?: boolean }>>;
125
134
  toolNames?: Partial<ToolNames>;
126
135
  shortcuts?: {
127
136
  curate?: KeyId;
@@ -145,12 +154,15 @@ interface ProviderAvailability {
145
154
  searchinfinity: boolean;
146
155
  querit: boolean;
147
156
  tavily: boolean;
157
+ jina: boolean;
148
158
  serpdive: boolean;
149
159
  searxng: boolean;
160
+ duckduckgo: boolean;
150
161
  perplexity: boolean;
151
162
  exa: boolean;
152
163
  gemini: boolean;
153
164
  kagi: boolean;
165
+ bocha: boolean;
154
166
  ollama: boolean;
155
167
  anysearch: boolean;
156
168
  xai: boolean;
@@ -207,6 +219,8 @@ type ToolNames = {
207
219
  getSearchContent: string;
208
220
  };
209
221
 
222
+ type ProviderHeaders = Record<string, string | null>;
223
+
210
224
  const DEFAULT_TOOL_NAMES: ToolNames = {
211
225
  webSearch: "web_search",
212
226
  sourceCheck: "source_check",
@@ -218,6 +232,7 @@ const DEFAULT_SHORTCUTS = { curate: "ctrl+shift+s", activity: "ctrl+shift+w" } s
218
232
  const DEFAULT_CURATOR_TIMEOUT_SECONDS = 20;
219
233
  const DEFAULT_REMOTE_CURATOR_TIMEOUT_SECONDS = 60;
220
234
  const MAX_CURATOR_TIMEOUT_SECONDS = 600;
235
+ const MAX_SUMMARY_GENERATION_DEADLINE_MS = 600_000;
221
236
 
222
237
  function searchProviderSchema(description: string) {
223
238
  return Type.Union([
@@ -226,6 +241,23 @@ function searchProviderSchema(description: string) {
226
241
  ], { description });
227
242
  }
228
243
 
244
+ function isToolEnabled(config: WebSearchConfig, key: keyof ToolNames): boolean {
245
+ const override = config.tools?.[key]?.enabled;
246
+ if (typeof override === "boolean") return override;
247
+ return key !== "webSearch" && key !== "sourceCheck" || config.webSearch?.enabled !== false;
248
+ }
249
+
250
+ function isCommandEnabled(config: WebSearchConfig, name: "websearch" | "curator" | "search" | "google-account"): boolean {
251
+ return config.commands?.[name]?.enabled !== false;
252
+ }
253
+
254
+ function joinToolNames(names: string[]): string {
255
+ if (names.length === 0) return "stored content";
256
+ if (names.length === 1) return names[0];
257
+ if (names.length === 2) return `${names[0]} or ${names[1]}`;
258
+ return `${names.slice(0, -1).join(", ")}, or ${names[names.length - 1]}`;
259
+ }
260
+
229
261
  function resolveToolNames(config: WebSearchConfig): ToolNames {
230
262
  if (config.toolNames !== undefined && (!config.toolNames || typeof config.toolNames !== "object" || Array.isArray(config.toolNames))) {
231
263
  throw new Error(`toolNames in ${WEB_SEARCH_CONFIG_PATH} must be an object`);
@@ -241,9 +273,8 @@ function resolveToolNames(config: WebSearchConfig): ToolNames {
241
273
  }
242
274
  names[key] = trimmed;
243
275
  }
244
- const registeredKeys: Array<keyof ToolNames> = config.webSearch?.enabled === false
245
- ? ["fetchContent", "getSearchContent"]
246
- : ["webSearch", "sourceCheck", "fetchContent", "getSearchContent"];
276
+ const registeredKeys = (Object.keys(DEFAULT_TOOL_NAMES) as Array<keyof ToolNames>)
277
+ .filter(key => isToolEnabled(config, key));
247
278
  const seen = new Map<string, keyof ToolNames>();
248
279
  for (const key of registeredKeys) {
249
280
  const name = names[key];
@@ -327,6 +358,14 @@ function getCuratorTimeoutSeconds(): number {
327
358
  return resolveCuratorNetworkConfig().enabled ? DEFAULT_REMOTE_CURATOR_TIMEOUT_SECONDS : DEFAULT_CURATOR_TIMEOUT_SECONDS;
328
359
  }
329
360
 
361
+ export function getSummaryGenerationDeadlineMs(): number {
362
+ const value = loadConfig().summaryGenerationDeadlineMs;
363
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
364
+ return SUMMARY_GENERATION_DEADLINE_MS;
365
+ }
366
+ return Math.min(value, MAX_SUMMARY_GENERATION_DEADLINE_MS);
367
+ }
368
+
330
369
  function shouldAutoOpenCuratorBrowser(config: WebSearchConfig): boolean {
331
370
  if (config.autoOpenBrowser === false) return false;
332
371
  if (resolveCuratorNetworkConfig().enabled && config.autoOpenBrowser !== true) return false;
@@ -345,10 +384,13 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
345
384
  searchinfinity: isSearchinfinityAvailable(),
346
385
  querit: isQueritAvailable(),
347
386
  tavily: isTavilyAvailable(),
387
+ jina: isJinaSearchAvailable(),
348
388
  serpdive: isSerpdiveAvailable(),
349
389
  kagi: isKagiAvailable(),
390
+ bocha: isBochaAvailable(),
350
391
  ollama: isOllamaAvailable(),
351
392
  searxng: isSearXNGAvailable(),
393
+ duckduckgo: isDuckDuckGoAvailable(),
352
394
  perplexity: isPerplexityAvailable(),
353
395
  exa: isExaAvailable(),
354
396
  gemini: geminiApiAvail || !!geminiWebAvail,
@@ -358,8 +400,8 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
358
400
  serpbase: isSerpBaseAvailable(),
359
401
  };
360
402
  return {
361
- // AnySearch, xAI, Bright Data, and SerpBase are explicit-only, so they never make `all` eligible.
362
- all: Object.entries(providers).some(([provider, available]) => provider !== "anysearch" && provider !== "xai" && provider !== "brightdata" && provider !== "serpbase" && provider !== "gemini" && available) || geminiApiAvail,
403
+ // DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase are explicit-only, so they never make `all` eligible.
404
+ all: Object.entries(providers).some(([provider, available]) => provider !== "duckduckgo" && provider !== "anysearch" && provider !== "xai" && provider !== "brightdata" && provider !== "serpbase" && provider !== "gemini" && available) || geminiApiAvail,
363
405
  ...providers,
364
406
  };
365
407
  }
@@ -399,8 +441,10 @@ function firstAvailableProvider(available: ProviderAvailability, preferOpenAI: b
399
441
  if (available.searchinfinity) return "searchinfinity";
400
442
  if (available.querit) return "querit";
401
443
  if (available.tavily) return "tavily";
444
+ if (available.jina) return "jina";
402
445
  if (available.serpdive) return "serpdive";
403
446
  if (available.kagi) return "kagi";
447
+ if (available.bocha) return "bocha";
404
448
  if (available.ollama) return "ollama";
405
449
  if (available.perplexity) return "perplexity";
406
450
  if (available.gemini) return "gemini";
@@ -452,12 +496,18 @@ function resolveProvider(
452
496
  if (provider === "tavily" && !available.tavily) {
453
497
  return firstAvailableProvider(available, preferOpenAI, "tavily");
454
498
  }
499
+ if (provider === "jina" && !available.jina) {
500
+ return firstAvailableProvider(available, preferOpenAI, "jina");
501
+ }
455
502
  if (provider === "serpdive" && !available.serpdive) {
456
503
  return firstAvailableProvider(available, preferOpenAI, "serpdive");
457
504
  }
458
505
  if (provider === "kagi" && !available.kagi) {
459
506
  return firstAvailableProvider(available, preferOpenAI, "kagi");
460
507
  }
508
+ if (provider === "bocha" && !available.bocha) {
509
+ return firstAvailableProvider(available, preferOpenAI, "bocha");
510
+ }
461
511
  if (provider === "ollama" && !available.ollama) {
462
512
  return firstAvailableProvider(available, preferOpenAI, "ollama");
463
513
  }
@@ -513,15 +563,22 @@ interface PendingCurate {
513
563
  }
514
564
 
515
565
 
516
- const MAX_INLINE_CONTENT = 30000; // Content returned directly to agent
517
- const DEFAULT_CONTENT_SLICE_LENGTH = MAX_INLINE_CONTENT;
518
- const MAX_CONTENT_SLICE_LENGTH = MAX_INLINE_CONTENT;
566
+ const DEFAULT_MAX_INLINE_CONTENT_CHARS = 30_000;
567
+ const MAX_INLINE_CONTENT_CHARS = 200_000;
568
+
569
+ function getMaxInlineContentChars(): number {
570
+ const value = loadConfig().maxInlineContentChars;
571
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
572
+ return DEFAULT_MAX_INLINE_CONTENT_CHARS;
573
+ }
574
+ return Math.min(value, MAX_INLINE_CONTENT_CHARS);
575
+ }
519
576
 
520
577
  function stripThumbnails(results: ExtractedContent[]): ExtractedContent[] {
521
578
  return results.map(({ thumbnail, frames, ...rest }) => rest);
522
579
  }
523
580
 
524
- function initialContentSlice(content: string): {
581
+ function initialContentSlice(content: string, maxChars: number): {
525
582
  text: string;
526
583
  endOffset: number;
527
584
  totalBytes: number;
@@ -529,10 +586,10 @@ function initialContentSlice(content: string): {
529
586
  shownBytes: number;
530
587
  shownLines: number;
531
588
  } {
532
- let endOffset = Math.min(content.length, MAX_INLINE_CONTENT);
589
+ let endOffset = Math.min(content.length, maxChars);
533
590
  if (endOffset < content.length) {
534
591
  const lineBreak = content.lastIndexOf("\n", endOffset);
535
- if (lineBreak >= Math.floor(MAX_INLINE_CONTENT * 0.8)) endOffset = lineBreak + 1;
592
+ if (lineBreak >= Math.floor(maxChars * 0.8)) endOffset = lineBreak + 1;
536
593
  }
537
594
  const text = content.slice(0, endOffset);
538
595
  return {
@@ -560,7 +617,7 @@ function formatSearchSummary(results: SearchResult[], answer: string): string {
560
617
  return output;
561
618
  }
562
619
 
563
- function formatSourceCheckResult(artifact: ResearchArtifact, getSearchContentTool = DEFAULT_TOOL_NAMES.getSearchContent): string {
620
+ function formatSourceCheckResult(artifact: ResearchArtifact, getSearchContentTool: string | null = DEFAULT_TOOL_NAMES.getSearchContent): string {
564
621
  const assessment = artifact.claims?.[0];
565
622
  const lines = [`# Source check: ${artifact.query}`, ""];
566
623
  if (assessment) {
@@ -576,7 +633,9 @@ function formatSourceCheckResult(artifact: ResearchArtifact, getSearchContentToo
576
633
  lines.push("");
577
634
  }
578
635
  if (artifact.errors?.length) lines.push(`Search errors: ${artifact.errors.map((entry) => `${entry.query}: ${entry.error}`).join("; ")}`);
579
- lines.push(`Artifact responseId: ${artifact.id} (retrievable via ${getSearchContentTool}).`);
636
+ lines.push(getSearchContentTool
637
+ ? `Artifact responseId: ${artifact.id} (retrievable via ${getSearchContentTool}).`
638
+ : `Artifact responseId: ${artifact.id}. Content retrieval is not registered.`);
580
639
  return lines.join("\n");
581
640
  }
582
641
 
@@ -856,12 +915,21 @@ function handleSessionChange(ctx: ExtensionContext): void {
856
915
  export default function (pi: ExtensionAPI) {
857
916
  const initConfig = loadConfigForExtensionInit();
858
917
  const toolNames = resolveToolNames(initConfig);
859
- const storedContentSources = initConfig.webSearch?.enabled === false
860
- ? toolNames.fetchContent
861
- : `${toolNames.webSearch}, ${toolNames.sourceCheck}, or ${toolNames.fetchContent}`;
862
- const searchQueryDescription = initConfig.webSearch?.enabled === false
863
- ? "Get content for a stored search query"
864
- : `Get content for this query (${toolNames.webSearch})`;
918
+ const webSearchEnabled = isToolEnabled(initConfig, "webSearch");
919
+ const sourceCheckEnabled = isToolEnabled(initConfig, "sourceCheck");
920
+ const fetchContentEnabled = isToolEnabled(initConfig, "fetchContent");
921
+ const getSearchContentEnabled = isToolEnabled(initConfig, "getSearchContent");
922
+ const storedContentSources = joinToolNames([
923
+ ...(webSearchEnabled ? [toolNames.webSearch] : []),
924
+ ...(sourceCheckEnabled ? [toolNames.sourceCheck] : []),
925
+ ...(fetchContentEnabled ? [toolNames.fetchContent] : []),
926
+ ]);
927
+ const searchQueryDescription = webSearchEnabled
928
+ ? `Get content for this query (${toolNames.webSearch})`
929
+ : "Get content for a stored search query";
930
+ const fetchContentStorageNote = getSearchContentEnabled
931
+ ? `Full original content is stored for retrieval with ${toolNames.getSearchContent}.`
932
+ : "Full original content is stored internally, but the retrieval tool is not registered.";
865
933
  const curateKey = initConfig.shortcuts?.curate || DEFAULT_SHORTCUTS.curate;
866
934
  const activityKey = initConfig.shortcuts?.activity || DEFAULT_SHORTCUTS.activity;
867
935
 
@@ -873,14 +941,13 @@ export default function (pi: ExtensionAPI) {
873
941
  fetchAllContent(urls, controller.signal)
874
942
  .then((fetched) => {
875
943
  if (!sessionActive || !pendingFetches.has(fetchId)) return;
876
- const data: StoredSearchData = {
944
+ const data = {
877
945
  id: fetchId,
878
946
  type: "fetch",
879
947
  timestamp: Date.now(),
880
948
  urls: stripThumbnails(fetched),
881
- };
882
- storeResult(fetchId, data);
883
- pi.appendEntry("web-search-results", data);
949
+ } satisfies StoredSearchData & { type: "fetch"; urls: ExtractedContent[] };
950
+ pi.appendEntry("web-search-results", storeFetchedContentResult(fetchId, data));
884
951
  const ok = fetched.filter(f => !f.error).length;
885
952
  pi.sendMessage(
886
953
  {
@@ -999,7 +1066,7 @@ export default function (pi: ExtensionAPI) {
999
1066
  async function resolveFirstAvailableModel(
1000
1067
  ctx: SummaryGenerationContext,
1001
1068
  candidates: Array<{ provider: string; id: string }>,
1002
- ): Promise<{ model: Model<Api>; apiKey: string; headers?: Record<string, string> }> {
1069
+ ): Promise<{ model: Model<Api>; apiKey: string; headers?: ProviderHeaders }> {
1003
1070
  const enabledModelPatterns = loadEnabledModelPatterns(ctx);
1004
1071
  for (const { provider, id } of candidates) {
1005
1072
  const model = findModelWithProviderRouting(ctx.modelRegistry, provider, id);
@@ -1054,7 +1121,15 @@ export default function (pi: ExtensionAPI) {
1054
1121
  throw new Error("No selected results available for summary generation");
1055
1122
  }
1056
1123
  try {
1057
- return await generateSummaryDraft(selectedResults, summaryContext, signal, modelOverride, feedback);
1124
+ return await generateSummaryDraft(
1125
+ selectedResults,
1126
+ summaryContext,
1127
+ signal,
1128
+ modelOverride,
1129
+ feedback,
1130
+ undefined,
1131
+ getSummaryGenerationDeadlineMs(),
1132
+ );
1058
1133
  } catch (err) {
1059
1134
  const isEmptyResponse = err instanceof Error && err.message.includes("Summary model returned empty response");
1060
1135
  if (!isEmptyResponse) throw err;
@@ -1195,14 +1270,13 @@ export default function (pi: ExtensionAPI) {
1195
1270
  let fetchId: string | null = null;
1196
1271
  if (hasInlineReady && opts.inlineContent) {
1197
1272
  fetchId = generateId();
1198
- const data: StoredSearchData = {
1273
+ const data = {
1199
1274
  id: fetchId,
1200
1275
  type: "fetch",
1201
1276
  timestamp: Date.now(),
1202
1277
  urls: opts.inlineContent,
1203
- };
1204
- storeResult(fetchId, data);
1205
- pi.appendEntry("web-search-results", data);
1278
+ } satisfies StoredSearchData & { type: "fetch"; urls: ExtractedContent[] };
1279
+ pi.appendEntry("web-search-results", storeFetchedContentResult(fetchId, data));
1206
1280
  if (!hasApprovedSummary) {
1207
1281
  output += `---\nFull content for ${opts.inlineContent.length} sources available [${fetchId}].`;
1208
1282
  }
@@ -1558,11 +1632,11 @@ export default function (pi: ExtensionAPI) {
1558
1632
  widgetVisible = false;
1559
1633
  });
1560
1634
 
1561
- if (initConfig.webSearch?.enabled !== false) pi.registerTool({
1635
+ if (webSearchEnabled) pi.registerTool({
1562
1636
  name: toolNames.webSearch,
1563
1637
  label: "Web Search",
1564
1638
  description:
1565
- `Search the web using OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, SERPdive, Kagi, Ollama, SearXNG, Exa, Perplexity, Gemini, AnySearch, xAI, Bright Data, or SerpBase. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except AnySearch, xAI, Bright Data, and SerpBase. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; xAI search uses a SuperGrok/X Premium subscription or xAI API key. AnySearch, xAI, Bright Data, and SerpBase are available only when explicitly selected. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation or "auto-summary" for a model-generated summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it. Without a configured provider, auto-selects OpenAI when suitable and available, then Exa, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, SERPdive, Kagi, Ollama, Perplexity, Gemini API, or Gemini Web. When SearXNG is configured, it is preferred first for local/private search.`,
1639
+ `Search the web using OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, AnySearch, xAI, Bright Data, or SerpBase. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; xAI search uses a SuperGrok/X Premium subscription or xAI API key. DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase are available only when explicitly selected. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation or "auto-summary" for a model-generated summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it. Without a configured provider, auto-selects OpenAI when suitable and available, then Exa, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web. When SearXNG is configured, it is preferred first for local/private search.`,
1566
1640
  promptSnippet:
1567
1641
  "Use for web research questions. Prefer {queries:[...]} with 2-4 varied angles over a single query for broader coverage. Omit provider unless explicitly overriding the configured default.",
1568
1642
  parameters: Type.Object({
@@ -1574,7 +1648,7 @@ export default function (pi: ExtensionAPI) {
1574
1648
  StringEnum(["day", "week", "month", "year"], { description: "Filter by recency" }),
1575
1649
  ),
1576
1650
  domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
1577
- provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except AnySearch, xAI, Bright Data, and SerpBase, omit this field to use the configured provider, or use auto when none is configured")),
1651
+ provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase, omit this field to use the configured provider, or use auto when none is configured")),
1578
1652
  workflow: Type.Optional(
1579
1653
  StringEnum(["none", "summary-review", "auto-summary"], {
1580
1654
  description: "Search workflow mode: none = no curator, summary-review = open curator with auto summary draft (default), auto-summary = generate summary without opening curator",
@@ -1849,7 +1923,15 @@ export default function (pi: ExtensionAPI) {
1849
1923
  isProjectTrusted: () => ctx.isProjectTrusted(),
1850
1924
  };
1851
1925
  const summaryModelChoices = await loadSummaryModelChoices(summaryContext);
1852
- const generated = await generateSummaryDraft(searchResults, summaryContext, signal, summaryModelChoices.defaultSummaryModel ?? undefined);
1926
+ const generated = await generateSummaryDraft(
1927
+ searchResults,
1928
+ summaryContext,
1929
+ signal,
1930
+ summaryModelChoices.defaultSummaryModel ?? undefined,
1931
+ undefined,
1932
+ undefined,
1933
+ getSummaryGenerationDeadlineMs(),
1934
+ );
1853
1935
  approvedSummary = generated.summary;
1854
1936
  summaryMeta = generated.meta;
1855
1937
  }
@@ -2077,7 +2159,7 @@ export default function (pi: ExtensionAPI) {
2077
2159
  const totalLines = lines.length;
2078
2160
 
2079
2161
  if (!expanded) {
2080
- const box = new Box(1, 0, (t) => theme.bg("toolSuccessBg", t));
2162
+ const box = new Box(1, 0);
2081
2163
  box.addChild(new Text(statusLine, 0, 0));
2082
2164
 
2083
2165
  let collapsedLines = 1; // statusLine
@@ -2122,7 +2204,7 @@ export default function (pi: ExtensionAPI) {
2122
2204
  },
2123
2205
  });
2124
2206
 
2125
- if (initConfig.webSearch?.enabled !== false) pi.registerTool({
2207
+ if (sourceCheckEnabled) pi.registerTool({
2126
2208
  name: toolNames.sourceCheck,
2127
2209
  label: "Source Check",
2128
2210
  description: "Check a claim against web sources and return a bounded machine-readable research artifact with exact passage citations.",
@@ -2134,7 +2216,7 @@ export default function (pi: ExtensionAPI) {
2134
2216
  fetchContent: Type.Optional(Type.Boolean({ description: "Fetch up to 5 result pages for exact passage extraction." })),
2135
2217
  recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency." })),
2136
2218
  domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains; prefix with - to exclude." })),
2137
- provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; all searches every eligible provider except AnySearch, xAI, Bright Data, and SerpBase")),
2219
+ provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; all searches every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase")),
2138
2220
  }),
2139
2221
  async execute(_callId, params, signal, _onUpdate, ctx) {
2140
2222
  const claim = typeof params.claim === "string" ? params.claim.trim() : "";
@@ -2210,16 +2292,16 @@ export default function (pi: ExtensionAPI) {
2210
2292
  artifact,
2211
2293
  });
2212
2294
  return {
2213
- content: [{ type: "text", text: formatSourceCheckResult(artifact, toolNames.getSearchContent) }],
2295
+ content: [{ type: "text", text: formatSourceCheckResult(artifact, getSearchContentEnabled ? toolNames.getSearchContent : null) }],
2214
2296
  details: { responseId: artifact.id, artifact, sourceCount: artifact.sources.length, passageCount: artifact.passages.length },
2215
2297
  };
2216
2298
  },
2217
2299
  });
2218
2300
 
2219
- pi.registerTool({
2301
+ if (fetchContentEnabled) pi.registerTool({
2220
2302
  name: toolNames.fetchContent,
2221
2303
  label: "Fetch Content",
2222
- description: `Fetch URL(s) and extract readable content as markdown. Use mode "raw" for exact textual HTTP response bodies or mode "answer" with prompt to answer using only fetched content. Direct image URLs return resized image content. Supports YouTube transcripts, GitHub repositories, PDFs, and local videos. Full original content is stored for retrieval with ${toolNames.getSearchContent}.`,
2304
+ description: `Fetch URL(s) and extract readable content as markdown. Use mode "raw" for exact textual HTTP response bodies or mode "answer" with prompt to answer using only fetched content. Direct image URLs return resized image content. Supports YouTube transcripts, GitHub repositories, PDFs, and local videos. ${fetchContentStorageNote}`,
2223
2305
  promptSnippet:
2224
2306
  "Use to fetch readable or raw URL content, direct images, GitHub repos, and videos. Mode answer answers a prompt using only the fetched source.",
2225
2307
  parameters: Type.Object({
@@ -2316,14 +2398,13 @@ export default function (pi: ExtensionAPI) {
2316
2398
 
2317
2399
  // ALWAYS store results (even for single URL)
2318
2400
  const responseId = generateId();
2319
- const data: StoredSearchData = {
2401
+ const data = {
2320
2402
  id: responseId,
2321
2403
  type: "fetch",
2322
2404
  timestamp: Date.now(),
2323
2405
  urls: stripThumbnails(fetchResults),
2324
- };
2325
- storeResult(responseId, data);
2326
- pi.appendEntry("web-search-results", data);
2406
+ } satisfies StoredSearchData & { type: "fetch"; urls: ExtractedContent[] };
2407
+ pi.appendEntry("web-search-results", storeFetchedContentResult(responseId, data));
2327
2408
 
2328
2409
  // Single URL: return content directly (possibly truncated) with responseId
2329
2410
  if (urlList.length === 1) {
@@ -2336,13 +2417,15 @@ export default function (pi: ExtensionAPI) {
2336
2417
  }
2337
2418
 
2338
2419
  const fullLength = result.content.length;
2339
- const slice = initialContentSlice(result.content);
2420
+ const slice = initialContentSlice(result.content, getMaxInlineContentChars());
2340
2421
  const truncated = slice.endOffset < fullLength;
2341
2422
  let output = slice.text;
2342
2423
 
2343
2424
  if (truncated) {
2344
- output += `\n\n---\nShowing ${slice.endOffset} of ${fullLength} chars, ${slice.shownBytes} of ${slice.totalBytes} bytes, and ${slice.shownLines} of ${slice.totalLines} lines. ` +
2345
- `Use ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0, offset: ${slice.endOffset} }) for the next slice.`;
2425
+ output += `\n\n---\nShowing ${slice.endOffset} of ${fullLength} chars, ${slice.shownBytes} of ${slice.totalBytes} bytes, and ${slice.shownLines} of ${slice.totalLines} lines. `;
2426
+ output += getSearchContentEnabled
2427
+ ? `Use ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0, offset: ${slice.endOffset} }) for the next slice.`
2428
+ : "Content retrieval is not registered.";
2346
2429
  }
2347
2430
 
2348
2431
  const content: Array<TextContent | ImageContent> = [];
@@ -2393,7 +2476,9 @@ export default function (pi: ExtensionAPI) {
2393
2476
  output += `- ${title || url} (${content.length} chars)\n`;
2394
2477
  }
2395
2478
  }
2396
- output += `\n---\nUse ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0 }) to retrieve bounded content slices.`;
2479
+ output += getSearchContentEnabled
2480
+ ? `\n---\nUse ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0 }) to retrieve bounded content slices.`
2481
+ : "\n---\nContent retrieval is not registered.";
2397
2482
 
2398
2483
  return {
2399
2484
  content: [{ type: "text", text: output }],
@@ -2521,7 +2606,7 @@ export default function (pi: ExtensionAPI) {
2521
2606
  }
2522
2607
 
2523
2608
  const countColor = (details?.successful ?? 0) > 0 ? "success" : "error";
2524
- const statusLine = theme.fg(countColor, `${details?.successful}/${details?.urlCount} URLs`) + theme.fg("muted", " (content stored)");
2609
+ const statusLine = theme.fg(countColor, `${details?.successful}/${details?.urlCount} URLs`) + theme.fg("muted", getSearchContentEnabled ? " (content stored)" : " (content fetched)");
2525
2610
  if (!expanded) {
2526
2611
  return new Text(statusLine, 0, 0);
2527
2612
  }
@@ -2531,7 +2616,7 @@ export default function (pi: ExtensionAPI) {
2531
2616
  },
2532
2617
  });
2533
2618
 
2534
- pi.registerTool({
2619
+ if (getSearchContentEnabled) pi.registerTool({
2535
2620
  name: toolNames.getSearchContent,
2536
2621
  label: "Get Search Content",
2537
2622
  description: `Retrieve bounded content slices or find matching passages in a previous ${storedContentSources} call.`,
@@ -2543,13 +2628,13 @@ export default function (pi: ExtensionAPI) {
2543
2628
  queryIndex: Type.Optional(Type.Number({ description: "Get content for query at index" })),
2544
2629
  url: Type.Optional(Type.String({ description: "Get content for this URL" })),
2545
2630
  urlIndex: Type.Optional(Type.Number({ description: "Get content for URL at index" })),
2546
- offset: Type.Optional(Type.Number({ description: "Character offset for fetched URL content slices (default 0)" })),
2547
- limit: Type.Optional(Type.Number({ description: `Maximum characters to return for fetched URL content slices (default/max ${MAX_CONTENT_SLICE_LENGTH})` })),
2631
+ offset: Type.Optional(Type.Number({ description: "Character offset for fetched URL content slices (default 0). Cannot be combined with findText." })),
2632
+ limit: Type.Optional(Type.Number({ description: "Maximum characters to return for fetched URL content slices (default and max are set by maxInlineContentChars). Cannot be combined with findText." })),
2548
2633
  findText: Type.Optional(Type.Union([
2549
2634
  Type.String({ minLength: 1, maxLength: 500 }),
2550
2635
  Type.Array(Type.String({ minLength: 1, maxLength: 500 }), { minItems: 1, maxItems: 10 }),
2551
- ], { description: "Text or texts to find in the selected stored content." })),
2552
- findMode: Type.Optional(StringEnum(["exact", "case-insensitive", "fuzzy"], { description: "Matching mode for findText (default: case-insensitive)." })),
2636
+ ], { description: "Text or texts to find in the selected stored content. Cannot be combined with offset or limit." })),
2637
+ findMode: Type.Optional(StringEnum(["exact", "case-insensitive", "fuzzy"], { description: "Matching mode for findText (default: case-insensitive). Requires findText." })),
2553
2638
  }),
2554
2639
 
2555
2640
  async execute(_toolCallId, params): Promise<AgentToolResult<Record<string, unknown>>> {
@@ -2576,13 +2661,14 @@ export default function (pi: ExtensionAPI) {
2576
2661
  };
2577
2662
  }
2578
2663
  const serialized = JSON.stringify(artifact, null, 2);
2664
+ const maxInlineContentChars = getMaxInlineContentChars();
2579
2665
  const offset = params.offset ?? 0;
2580
- const limit = params.limit ?? MAX_CONTENT_SLICE_LENGTH;
2666
+ const limit = params.limit ?? maxInlineContentChars;
2581
2667
  if (!Number.isInteger(offset) || offset < 0) {
2582
2668
  return { content: [{ type: "text", text: "offset must be a non-negative integer" }], details: { error: "Invalid offset", offset } };
2583
2669
  }
2584
- if (!Number.isInteger(limit) || limit <= 0 || limit > MAX_CONTENT_SLICE_LENGTH) {
2585
- return { content: [{ type: "text", text: `limit must be an integer from 1 to ${MAX_CONTENT_SLICE_LENGTH}` }], details: { error: "Invalid limit", limit, maxLimit: MAX_CONTENT_SLICE_LENGTH } };
2670
+ if (!Number.isInteger(limit) || limit <= 0 || limit > maxInlineContentChars) {
2671
+ return { content: [{ type: "text", text: `limit must be an integer from 1 to ${maxInlineContentChars}` }], details: { error: "Invalid limit", limit, maxLimit: maxInlineContentChars } };
2586
2672
  }
2587
2673
  if (offset > serialized.length) {
2588
2674
  return { content: [{ type: "text", text: `offset ${offset} is out of range (0-${serialized.length})` }], details: { error: "Offset out of range", offset, contentLength: serialized.length } };
@@ -2704,18 +2790,19 @@ export default function (pi: ExtensionAPI) {
2704
2790
  }
2705
2791
  }
2706
2792
 
2793
+ const maxInlineContentChars = getMaxInlineContentChars();
2707
2794
  const offset = params.offset ?? 0;
2708
- const limit = params.limit ?? DEFAULT_CONTENT_SLICE_LENGTH;
2795
+ const limit = params.limit ?? maxInlineContentChars;
2709
2796
  if (!Number.isInteger(offset) || offset < 0) {
2710
2797
  return {
2711
2798
  content: [{ type: "text", text: "offset must be a non-negative integer" }],
2712
2799
  details: { error: "Invalid offset", offset },
2713
2800
  };
2714
2801
  }
2715
- if (!Number.isInteger(limit) || limit <= 0 || limit > MAX_CONTENT_SLICE_LENGTH) {
2802
+ if (!Number.isInteger(limit) || limit <= 0 || limit > maxInlineContentChars) {
2716
2803
  return {
2717
- content: [{ type: "text", text: `limit must be an integer from 1 to ${MAX_CONTENT_SLICE_LENGTH}` }],
2718
- details: { error: "Invalid limit", limit, maxLimit: MAX_CONTENT_SLICE_LENGTH },
2804
+ content: [{ type: "text", text: `limit must be an integer from 1 to ${maxInlineContentChars}` }],
2805
+ details: { error: "Invalid limit", limit, maxLimit: maxInlineContentChars },
2719
2806
  };
2720
2807
  }
2721
2808
  if (offset > urlData.content.length) {
@@ -2830,7 +2917,7 @@ export default function (pi: ExtensionAPI) {
2830
2917
  },
2831
2918
  });
2832
2919
 
2833
- pi.registerCommand("websearch", {
2920
+ if (isCommandEnabled(initConfig, "websearch")) pi.registerCommand("websearch", {
2834
2921
  description: "Open web search curator",
2835
2922
  handler: async (args, ctx) => {
2836
2923
  const sessionToken = randomUUID();
@@ -3091,7 +3178,7 @@ export default function (pi: ExtensionAPI) {
3091
3178
  },
3092
3179
  });
3093
3180
 
3094
- pi.registerCommand("curator", {
3181
+ if (isCommandEnabled(initConfig, "curator")) pi.registerCommand("curator", {
3095
3182
  description: "Toggle or configure the search curator workflow",
3096
3183
  handler: async (args, ctx) => {
3097
3184
  const arg = args.trim().toLowerCase();
@@ -3133,7 +3220,7 @@ export default function (pi: ExtensionAPI) {
3133
3220
  },
3134
3221
  });
3135
3222
 
3136
- pi.registerCommand("google-account", {
3223
+ if (isCommandEnabled(initConfig, "google-account")) pi.registerCommand("google-account", {
3137
3224
  description: "Show the active Google account for Gemini Web",
3138
3225
  handler: async () => {
3139
3226
  if (!isBrowserCookieAccessAllowed()) {
@@ -3175,7 +3262,7 @@ export default function (pi: ExtensionAPI) {
3175
3262
  },
3176
3263
  });
3177
3264
 
3178
- pi.registerCommand("search", {
3265
+ if (isCommandEnabled(initConfig, "search")) pi.registerCommand("search", {
3179
3266
  description: "Browse stored web search results",
3180
3267
  handler: async (_args, ctx) => {
3181
3268
  const results = getAllResults();
@@ -3192,8 +3279,8 @@ export default function (pi: ExtensionAPI) {
3192
3279
  const query = r.queries[0]?.query || "unknown";
3193
3280
  return `[${r.id.slice(0, 6)}] "${query}" (${r.queries.length} queries) - ${ageStr}`;
3194
3281
  }
3195
- if (r.type === "fetch" && r.urls) {
3196
- return `[${r.id.slice(0, 6)}] ${r.urls.length} URLs fetched - ${ageStr}`;
3282
+ if (r.type === "fetch" && (r.urls || r.urlMetadata)) {
3283
+ return `[${r.id.slice(0, 6)}] ${(r.urls ?? r.urlMetadata ?? []).length} URLs fetched - ${ageStr}`;
3197
3284
  }
3198
3285
  return `[${r.id.slice(0, 6)}] ${r.type} - ${ageStr}`;
3199
3286
  });
@@ -3225,15 +3312,17 @@ export default function (pi: ExtensionAPI) {
3225
3312
  info += `... and ${selected.queries.length - 10} more\n`;
3226
3313
  }
3227
3314
  }
3228
- if (selected.type === "fetch" && selected.urls) {
3315
+ if (selected.type === "fetch" && (selected.urls || selected.urlMetadata)) {
3229
3316
  info += "URLs:\n";
3230
- const urls = selected.urls.slice(0, 10);
3317
+ const urlItems = selected.urls ?? selected.urlMetadata ?? [];
3318
+ const urls = urlItems.slice(0, 10);
3231
3319
  for (const u of urls) {
3232
3320
  const urlDisplay = u.url.length > 50 ? u.url.slice(0, 47) + "..." : u.url;
3233
- info += `- ${urlDisplay} (${u.error || `${u.content.length} chars`})\n`;
3321
+ const contentLength = "content" in u ? u.content.length : u.contentLength;
3322
+ info += `- ${urlDisplay} (${u.error || `${contentLength} chars`})\n`;
3234
3323
  }
3235
- if (selected.urls.length > 10) {
3236
- info += `... and ${selected.urls.length - 10} more\n`;
3324
+ if (urlItems.length > 10) {
3325
+ info += `... and ${urlItems.length - 10} more\n`;
3237
3326
  }
3238
3327
  }
3239
3328
  ctx.ui.notify(info, "info");