@humain/terminal 0.0.5 → 0.0.7

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 (476) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +10 -4
  3. package/dist/bundle/chunks/anthropic-messages-GDMGYAZB.js +6 -0
  4. package/dist/bundle/chunks/anthropic.js +75 -0
  5. package/dist/bundle/chunks/azure-openai-responses-ZN52D6YC.js +2 -0
  6. package/dist/bundle/chunks/bedrock-converse-stream.js +77 -0
  7. package/dist/bundle/chunks/chunk-3D3G2X5U.js +6 -0
  8. package/dist/bundle/chunks/chunk-3VI4HD7D.js +31 -0
  9. package/dist/bundle/chunks/chunk-6R7UI4IY.js +2 -0
  10. package/dist/bundle/chunks/chunk-72KO4OMP.js +2 -0
  11. package/dist/bundle/chunks/chunk-7DDKXS2Q.js +2 -0
  12. package/dist/bundle/chunks/chunk-ARF4OE6X.js +105 -0
  13. package/dist/bundle/chunks/chunk-BOW22LW6.js +42 -0
  14. package/dist/bundle/chunks/chunk-BZQ53VGE.js +70 -0
  15. package/dist/bundle/chunks/chunk-CBHC6EL7.js +133 -0
  16. package/dist/bundle/chunks/chunk-D7XAASZM.js +4 -0
  17. package/dist/bundle/chunks/chunk-ECWELYBH.js +17 -0
  18. package/dist/bundle/chunks/chunk-GMWCTUPB.js +2 -0
  19. package/dist/bundle/chunks/chunk-H2O6VOXT.js +11 -0
  20. package/dist/bundle/chunks/chunk-HNVPY4LZ.js +5995 -0
  21. package/dist/bundle/chunks/chunk-JUUC5AZY.js +2 -0
  22. package/dist/bundle/chunks/chunk-JXWJOIH4.js +2 -0
  23. package/dist/bundle/chunks/chunk-L2ZPNLYD.js +2 -0
  24. package/dist/bundle/chunks/chunk-LVVZS2NO.js +12 -0
  25. package/dist/bundle/chunks/chunk-M2GYSCCG.js +44 -0
  26. package/dist/bundle/chunks/chunk-MA4UUCYX.js +18 -0
  27. package/dist/bundle/chunks/chunk-PGXDEOVV.js +2 -0
  28. package/dist/bundle/chunks/chunk-PJACZCIR.js +2 -0
  29. package/dist/bundle/chunks/chunk-PRRNXB7S.js +2 -0
  30. package/dist/bundle/chunks/chunk-SGTGQNNP.js +4 -0
  31. package/dist/bundle/chunks/chunk-TEPMHNKQ.js +2 -0
  32. package/dist/bundle/chunks/chunk-TXBQIIIO.js +2 -0
  33. package/dist/bundle/chunks/chunk-WJZ4KLGL.js +2 -0
  34. package/dist/bundle/chunks/chunk-XNGRGP62.js +2 -0
  35. package/dist/bundle/chunks/dist-7DSXV2TY.js +9 -0
  36. package/dist/bundle/chunks/dist-BXEVYJIO.js +7 -0
  37. package/dist/bundle/chunks/es-P2KNSZYR.js +9 -0
  38. package/dist/bundle/chunks/es5-FGTJ7ZFW.js +2 -0
  39. package/dist/bundle/chunks/event-streams-JOSQQSII.js +2 -0
  40. package/dist/bundle/chunks/extract-2L7ZVN7F.js +106 -0
  41. package/dist/bundle/chunks/github-copilot.js +2 -0
  42. package/dist/bundle/chunks/google-generative-ai-XT7NQJ2E.js +2 -0
  43. package/dist/bundle/chunks/google-vertex-IDQ6Z53W.js +2 -0
  44. package/dist/bundle/chunks/image-resize-worker.js +2 -0
  45. package/dist/bundle/chunks/kimi-coding.js +2 -0
  46. package/dist/bundle/chunks/mistral-conversations-HHDZ4FHR.js +5 -0
  47. package/dist/bundle/chunks/multipart-parser-FMGQ7342.js +3 -0
  48. package/dist/bundle/chunks/node-XY3CBY3C.js +294 -0
  49. package/dist/bundle/chunks/node-ZHY4LHC7.js +15 -0
  50. package/dist/bundle/chunks/openai-codex-responses-HRVCDY54.js +8 -0
  51. package/dist/bundle/chunks/openai-codex.js +75 -0
  52. package/dist/bundle/chunks/openai-completions-6QBYTBXY.js +7 -0
  53. package/dist/bundle/chunks/openai-responses-4MWLJHIK.js +2 -0
  54. package/dist/bundle/chunks/openrouter-images-Y2PCWAQH.js +2 -0
  55. package/dist/bundle/chunks/openrouter.js +75 -0
  56. package/dist/bundle/chunks/pdfjs-UKQT3OWY.js +107 -0
  57. package/dist/bundle/chunks/pi-messages-57K43J6J.js +8 -0
  58. package/dist/bundle/chunks/promise-J5TGSM32.js +2 -0
  59. package/dist/bundle/chunks/radius.js +75 -0
  60. package/dist/bundle/chunks/src-QKGKDFSU.js +4 -0
  61. package/dist/bundle/chunks/undici-D6ABF53Q.js +2 -0
  62. package/dist/bundle/chunks/webapi-AFS5FDJJ.js +5 -0
  63. package/dist/bundle/chunks/xai.js +2 -0
  64. package/dist/bundle/cli.js +3 -0
  65. package/dist/bundle/client.js +2 -0
  66. package/dist/bundle/index.js +2 -0
  67. package/dist/bundle/rpc-entry.js +3 -0
  68. package/dist/cli/args.d.ts +1 -0
  69. package/dist/cli/args.d.ts.map +1 -1
  70. package/dist/cli/args.js +29 -9
  71. package/dist/cli/args.js.map +1 -1
  72. package/dist/cli/file-processor.d.ts.map +1 -1
  73. package/dist/cli/file-processor.js +2 -1
  74. package/dist/cli/file-processor.js.map +1 -1
  75. package/dist/config.d.ts +1 -0
  76. package/dist/config.d.ts.map +1 -1
  77. package/dist/config.js +19 -11
  78. package/dist/config.js.map +1 -1
  79. package/dist/core/agent-session.d.ts +50 -14
  80. package/dist/core/agent-session.d.ts.map +1 -1
  81. package/dist/core/agent-session.js +163 -95
  82. package/dist/core/agent-session.js.map +1 -1
  83. package/dist/core/auth-storage.d.ts.map +1 -1
  84. package/dist/core/auth-storage.js +6 -7
  85. package/dist/core/auth-storage.js.map +1 -1
  86. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  87. package/dist/core/compaction/branch-summarization.js +3 -0
  88. package/dist/core/compaction/branch-summarization.js.map +1 -1
  89. package/dist/core/compaction/compaction.d.ts +4 -3
  90. package/dist/core/compaction/compaction.d.ts.map +1 -1
  91. package/dist/core/compaction/compaction.js +41 -32
  92. package/dist/core/compaction/compaction.js.map +1 -1
  93. package/dist/core/defaults.d.ts +1 -0
  94. package/dist/core/defaults.d.ts.map +1 -1
  95. package/dist/core/defaults.js +9 -0
  96. package/dist/core/defaults.js.map +1 -1
  97. package/dist/core/extensions/index.d.ts +2 -2
  98. package/dist/core/extensions/index.d.ts.map +1 -1
  99. package/dist/core/extensions/index.js +1 -1
  100. package/dist/core/extensions/index.js.map +1 -1
  101. package/dist/core/extensions/loader.d.ts.map +1 -1
  102. package/dist/core/extensions/loader.js +115 -48
  103. package/dist/core/extensions/loader.js.map +1 -1
  104. package/dist/core/extensions/types.d.ts +38 -9
  105. package/dist/core/extensions/types.d.ts.map +1 -1
  106. package/dist/core/extensions/types.js +3 -0
  107. package/dist/core/extensions/types.js.map +1 -1
  108. package/dist/core/footer-data-provider.d.ts +1 -1
  109. package/dist/core/footer-data-provider.js +1 -1
  110. package/dist/core/footer-data-provider.js.map +1 -1
  111. package/dist/core/keybindings.d.ts +20 -19
  112. package/dist/core/keybindings.d.ts.map +1 -1
  113. package/dist/core/keybindings.js +26 -5
  114. package/dist/core/keybindings.js.map +1 -1
  115. package/dist/core/model-config.d.ts +5 -0
  116. package/dist/core/model-config.d.ts.map +1 -1
  117. package/dist/core/model-config.js +3 -1
  118. package/dist/core/model-config.js.map +1 -1
  119. package/dist/core/model-resolver.d.ts +1 -0
  120. package/dist/core/model-resolver.d.ts.map +1 -1
  121. package/dist/core/model-resolver.js +18 -8
  122. package/dist/core/model-resolver.js.map +1 -1
  123. package/dist/core/models-store.d.ts.map +1 -1
  124. package/dist/core/models-store.js +2 -1
  125. package/dist/core/models-store.js.map +1 -1
  126. package/dist/core/package-manager.d.ts.map +1 -1
  127. package/dist/core/package-manager.js +22 -14
  128. package/dist/core/package-manager.js.map +1 -1
  129. package/dist/core/pi-manifest.d.ts.map +1 -1
  130. package/dist/core/pi-manifest.js +2 -1
  131. package/dist/core/pi-manifest.js.map +1 -1
  132. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  133. package/dist/core/remote-catalog-provider.js +2 -1
  134. package/dist/core/remote-catalog-provider.js.map +1 -1
  135. package/dist/core/resource-loader.d.ts.map +1 -1
  136. package/dist/core/resource-loader.js +3 -2
  137. package/dist/core/resource-loader.js.map +1 -1
  138. package/dist/core/sdk.d.ts +2 -2
  139. package/dist/core/sdk.d.ts.map +1 -1
  140. package/dist/core/sdk.js +10 -3
  141. package/dist/core/sdk.js.map +1 -1
  142. package/dist/core/session-export.d.ts +4 -0
  143. package/dist/core/session-export.d.ts.map +1 -0
  144. package/dist/core/session-export.js +32 -0
  145. package/dist/core/session-export.js.map +1 -0
  146. package/dist/core/session-manager.d.ts.map +1 -1
  147. package/dist/core/session-manager.js +2 -1
  148. package/dist/core/session-manager.js.map +1 -1
  149. package/dist/core/settings-diagnostics.d.ts +9 -0
  150. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  151. package/dist/core/settings-diagnostics.js +21 -0
  152. package/dist/core/settings-diagnostics.js.map +1 -0
  153. package/dist/core/settings-manager.d.ts +9 -0
  154. package/dist/core/settings-manager.d.ts.map +1 -1
  155. package/dist/core/settings-manager.js +51 -10
  156. package/dist/core/settings-manager.js.map +1 -1
  157. package/dist/core/skills.d.ts.map +1 -1
  158. package/dist/core/skills.js +51 -33
  159. package/dist/core/skills.js.map +1 -1
  160. package/dist/core/slash-commands.d.ts.map +1 -1
  161. package/dist/core/slash-commands.js +2 -1
  162. package/dist/core/slash-commands.js.map +1 -1
  163. package/dist/core/system-prompt.d.ts.map +1 -1
  164. package/dist/core/system-prompt.js +11 -2
  165. package/dist/core/system-prompt.js.map +1 -1
  166. package/dist/core/tools/bash.d.ts +16 -3
  167. package/dist/core/tools/bash.d.ts.map +1 -1
  168. package/dist/core/tools/bash.js +62 -29
  169. package/dist/core/tools/bash.js.map +1 -1
  170. package/dist/core/tools/edit-diff.d.ts +0 -5
  171. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  172. package/dist/core/tools/edit-diff.js +2 -5
  173. package/dist/core/tools/edit-diff.js.map +1 -1
  174. package/dist/core/tools/edit.d.ts.map +1 -1
  175. package/dist/core/tools/edit.js +20 -4
  176. package/dist/core/tools/edit.js.map +1 -1
  177. package/dist/core/tools/index.d.ts +4 -1
  178. package/dist/core/tools/index.d.ts.map +1 -1
  179. package/dist/core/tools/index.js +19 -1
  180. package/dist/core/tools/index.js.map +1 -1
  181. package/dist/core/tools/powershell.d.ts +16 -0
  182. package/dist/core/tools/powershell.d.ts.map +1 -0
  183. package/dist/core/tools/powershell.js +39 -0
  184. package/dist/core/tools/powershell.js.map +1 -0
  185. package/dist/core/tools/subagent.d.ts.map +1 -1
  186. package/dist/core/tools/subagent.js +21 -28
  187. package/dist/core/tools/subagent.js.map +1 -1
  188. package/dist/core/trust-manager.d.ts.map +1 -1
  189. package/dist/core/trust-manager.js +2 -1
  190. package/dist/core/trust-manager.js.map +1 -1
  191. package/dist/extensions/llama/index.d.ts.map +1 -1
  192. package/dist/extensions/llama/index.js +2 -0
  193. package/dist/extensions/llama/index.js.map +1 -1
  194. package/dist/extensions/llama/provider.d.ts.map +1 -1
  195. package/dist/extensions/llama/provider.js +6 -2
  196. package/dist/extensions/llama/provider.js.map +1 -1
  197. package/dist/humain/agent-execution-protocol.d.ts +1 -217
  198. package/dist/humain/agent-execution-protocol.d.ts.map +1 -1
  199. package/dist/humain/agent-execution-protocol.js +1 -203
  200. package/dist/humain/agent-execution-protocol.js.map +1 -1
  201. package/dist/humain/bedrock-provider/vendor/index.bundle.mjs +107 -16
  202. package/dist/humain/mcp-adapter/vendor/CHANGELOG.md +82 -0
  203. package/dist/humain/mcp-adapter/vendor/README.md +47 -15
  204. package/dist/humain/mcp-adapter/vendor/cli.js +4 -4
  205. package/dist/humain/mcp-adapter/vendor/commands.ts +5 -3
  206. package/dist/humain/mcp-adapter/vendor/config.ts +46 -5
  207. package/dist/humain/mcp-adapter/vendor/direct-tools.ts +103 -92
  208. package/dist/humain/mcp-adapter/vendor/dist/agent-dir.d.ts +10 -0
  209. package/dist/humain/mcp-adapter/vendor/dist/agent-dir.js +63 -0
  210. package/dist/humain/mcp-adapter/vendor/dist/agent-dir.js.map +1 -0
  211. package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-loader.d.ts +8 -0
  212. package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-loader.js +356 -0
  213. package/dist/humain/mcp-adapter/vendor/dist/agent-plugin-loader.js.map +1 -0
  214. package/dist/humain/mcp-adapter/vendor/dist/config.d.ts +123 -0
  215. package/dist/humain/mcp-adapter/vendor/dist/config.js +1088 -0
  216. package/dist/humain/mcp-adapter/vendor/dist/config.js.map +1 -0
  217. package/dist/humain/mcp-adapter/vendor/dist/mcp-bearer-store.d.ts +24 -0
  218. package/dist/humain/mcp-adapter/vendor/dist/mcp-bearer-store.js +336 -0
  219. package/dist/humain/mcp-adapter/vendor/dist/mcp-bearer-store.js.map +1 -0
  220. package/dist/humain/mcp-adapter/vendor/dist/metadata-cache.d.ts +19 -0
  221. package/dist/humain/mcp-adapter/vendor/dist/metadata-cache.js +319 -0
  222. package/dist/humain/mcp-adapter/vendor/dist/metadata-cache.js.map +1 -0
  223. package/dist/humain/mcp-adapter/vendor/dist/package-mcp-loader.d.ts +2 -0
  224. package/dist/humain/mcp-adapter/vendor/dist/package-mcp-loader.js +156 -0
  225. package/dist/humain/mcp-adapter/vendor/dist/package-mcp-loader.js.map +1 -0
  226. package/dist/humain/mcp-adapter/vendor/dist/resource-tools.d.ts +1 -0
  227. package/dist/humain/mcp-adapter/vendor/dist/resource-tools.js +15 -0
  228. package/dist/humain/mcp-adapter/vendor/dist/resource-tools.js.map +1 -0
  229. package/dist/humain/mcp-adapter/vendor/dist/types.d.ts +566 -0
  230. package/dist/humain/mcp-adapter/vendor/dist/types.js +287 -0
  231. package/dist/humain/mcp-adapter/vendor/dist/types.js.map +1 -0
  232. package/dist/humain/mcp-adapter/vendor/dist/ui-app-bridge-helpers.d.ts +6 -0
  233. package/dist/humain/mcp-adapter/vendor/dist/ui-app-bridge-helpers.js +37 -0
  234. package/dist/humain/mcp-adapter/vendor/dist/ui-app-bridge-helpers.js.map +1 -0
  235. package/dist/humain/mcp-adapter/vendor/dist/ui-stream-types.d.ts +104 -0
  236. package/dist/humain/mcp-adapter/vendor/dist/ui-stream-types.js +59 -0
  237. package/dist/humain/mcp-adapter/vendor/dist/ui-stream-types.js.map +1 -0
  238. package/dist/humain/mcp-adapter/vendor/dist/ui-tool-visibility.d.ts +4 -0
  239. package/dist/humain/mcp-adapter/vendor/dist/ui-tool-visibility.js +27 -0
  240. package/dist/humain/mcp-adapter/vendor/dist/ui-tool-visibility.js.map +1 -0
  241. package/dist/humain/mcp-adapter/vendor/dist/utils.d.ts +32 -0
  242. package/dist/humain/mcp-adapter/vendor/dist/utils.js +338 -0
  243. package/dist/humain/mcp-adapter/vendor/dist/utils.js.map +1 -0
  244. package/dist/humain/mcp-adapter/vendor/failure-backoff.ts +23 -0
  245. package/dist/humain/mcp-adapter/vendor/index.bundle.mjs +4870 -3878
  246. package/dist/humain/mcp-adapter/vendor/index.ts +294 -88
  247. package/dist/humain/mcp-adapter/vendor/init.ts +41 -30
  248. package/dist/humain/mcp-adapter/vendor/lifecycle.ts +16 -8
  249. package/dist/humain/mcp-adapter/vendor/mcp-auth-flow.ts +124 -32
  250. package/dist/humain/mcp-adapter/vendor/mcp-bearer-store.ts +0 -2
  251. package/dist/humain/mcp-adapter/vendor/mcp-code.ts +2 -0
  252. package/dist/humain/mcp-adapter/vendor/mcp-oauth-provider.ts +155 -16
  253. package/dist/humain/mcp-adapter/vendor/mcp-output-guard.ts +95 -7
  254. package/dist/humain/mcp-adapter/vendor/mcp-probe.ts +18 -4
  255. package/dist/humain/mcp-adapter/vendor/mcp-references.ts +376 -0
  256. package/dist/humain/mcp-adapter/vendor/mcp-status.ts +8 -19
  257. package/dist/humain/mcp-adapter/vendor/metadata-cache.ts +20 -12
  258. package/dist/humain/mcp-adapter/vendor/namespace-tools.ts +276 -0
  259. package/dist/humain/mcp-adapter/vendor/package.json +26 -8
  260. package/dist/humain/mcp-adapter/vendor/proxy-modes.ts +128 -32
  261. package/dist/humain/mcp-adapter/vendor/request-headers-command.ts +1 -1
  262. package/dist/humain/mcp-adapter/vendor/search-ranking.ts +2 -0
  263. package/dist/humain/mcp-adapter/vendor/server-manager.ts +69 -12
  264. package/dist/humain/mcp-adapter/vendor/state.ts +1 -3
  265. package/dist/humain/mcp-adapter/vendor/tool-result-renderer.ts +16 -2
  266. package/dist/humain/mcp-adapter/vendor/types.ts +51 -2
  267. package/dist/humain/mcp-adapter/vendor/ui-server.ts +3 -19
  268. package/dist/humain/mcp-adapter/vendor/ui-session.ts +2 -1
  269. package/dist/humain/mcp-adapter/vendor/utils.ts +21 -19
  270. package/dist/humain/mcp-adapter.d.ts.map +1 -1
  271. package/dist/humain/mcp-adapter.js +4 -0
  272. package/dist/humain/mcp-adapter.js.map +1 -1
  273. package/dist/humain/web-access/vendor/chrome-cookies.d.ts +14 -0
  274. package/dist/humain/web-access/vendor/chrome-cookies.d.ts.map +1 -1
  275. package/dist/humain/web-access/vendor/chrome-cookies.js +85 -33
  276. package/dist/humain/web-access/vendor/chrome-cookies.js.map +1 -1
  277. package/dist/humain/web-access/vendor/credential-source.d.ts +1 -1
  278. package/dist/humain/web-access/vendor/credential-source.d.ts.map +1 -1
  279. package/dist/humain/web-access/vendor/credential-source.js +3 -1
  280. package/dist/humain/web-access/vendor/credential-source.js.map +1 -1
  281. package/dist/humain/web-access/vendor/curator-page.d.ts +2 -29
  282. package/dist/humain/web-access/vendor/curator-page.d.ts.map +1 -1
  283. package/dist/humain/web-access/vendor/curator-page.js +10 -1
  284. package/dist/humain/web-access/vendor/curator-page.js.map +1 -1
  285. package/dist/humain/web-access/vendor/curator-server.d.ts +2 -29
  286. package/dist/humain/web-access/vendor/curator-server.d.ts.map +1 -1
  287. package/dist/humain/web-access/vendor/curator-server.js +4 -0
  288. package/dist/humain/web-access/vendor/curator-server.js.map +1 -1
  289. package/dist/humain/web-access/vendor/extract.d.ts +11 -0
  290. package/dist/humain/web-access/vendor/extract.d.ts.map +1 -1
  291. package/dist/humain/web-access/vendor/extract.js +163 -20
  292. package/dist/humain/web-access/vendor/extract.js.map +1 -1
  293. package/dist/humain/web-access/vendor/fetch-params.d.ts +2 -0
  294. package/dist/humain/web-access/vendor/fetch-params.d.ts.map +1 -1
  295. package/dist/humain/web-access/vendor/fetch-params.js +11 -0
  296. package/dist/humain/web-access/vendor/fetch-params.js.map +1 -1
  297. package/dist/humain/web-access/vendor/gemini-adc.d.ts +10 -0
  298. package/dist/humain/web-access/vendor/gemini-adc.d.ts.map +1 -0
  299. package/dist/humain/web-access/vendor/gemini-adc.js +268 -0
  300. package/dist/humain/web-access/vendor/gemini-adc.js.map +1 -0
  301. package/dist/humain/web-access/vendor/gemini-api.d.ts +7 -1
  302. package/dist/humain/web-access/vendor/gemini-api.d.ts.map +1 -1
  303. package/dist/humain/web-access/vendor/gemini-api.js +51 -15
  304. package/dist/humain/web-access/vendor/gemini-api.js.map +1 -1
  305. package/dist/humain/web-access/vendor/gemini-search.d.ts +9 -4
  306. package/dist/humain/web-access/vendor/gemini-search.d.ts.map +1 -1
  307. package/dist/humain/web-access/vendor/gemini-search.js +63 -21
  308. package/dist/humain/web-access/vendor/gemini-search.js.map +1 -1
  309. package/dist/humain/web-access/vendor/gemini-url-context.d.ts.map +1 -1
  310. package/dist/humain/web-access/vendor/gemini-url-context.js +3 -2
  311. package/dist/humain/web-access/vendor/gemini-url-context.js.map +1 -1
  312. package/dist/humain/web-access/vendor/gemini-web-config.d.ts +8 -1
  313. package/dist/humain/web-access/vendor/gemini-web-config.d.ts.map +1 -1
  314. package/dist/humain/web-access/vendor/gemini-web-config.js +49 -4
  315. package/dist/humain/web-access/vendor/gemini-web-config.js.map +1 -1
  316. package/dist/humain/web-access/vendor/gemini-web.d.ts +2 -1
  317. package/dist/humain/web-access/vendor/gemini-web.d.ts.map +1 -1
  318. package/dist/humain/web-access/vendor/gemini-web.js +8 -3
  319. package/dist/humain/web-access/vendor/gemini-web.js.map +1 -1
  320. package/dist/humain/web-access/vendor/github-api.d.ts +1 -1
  321. package/dist/humain/web-access/vendor/github-api.d.ts.map +1 -1
  322. package/dist/humain/web-access/vendor/github-api.js +6 -4
  323. package/dist/humain/web-access/vendor/github-api.js.map +1 -1
  324. package/dist/humain/web-access/vendor/github-extract.d.ts.map +1 -1
  325. package/dist/humain/web-access/vendor/github-extract.js +50 -3
  326. package/dist/humain/web-access/vendor/github-extract.js.map +1 -1
  327. package/dist/humain/web-access/vendor/github-issue-pr.d.ts +27 -0
  328. package/dist/humain/web-access/vendor/github-issue-pr.d.ts.map +1 -0
  329. package/dist/humain/web-access/vendor/github-issue-pr.js +701 -0
  330. package/dist/humain/web-access/vendor/github-issue-pr.js.map +1 -0
  331. package/dist/humain/web-access/vendor/index.d.ts +3 -31
  332. package/dist/humain/web-access/vendor/index.d.ts.map +1 -1
  333. package/dist/humain/web-access/vendor/index.js +643 -530
  334. package/dist/humain/web-access/vendor/index.js.map +1 -1
  335. package/dist/humain/web-access/vendor/kimi-search.d.ts +5 -0
  336. package/dist/humain/web-access/vendor/kimi-search.d.ts.map +1 -0
  337. package/dist/humain/web-access/vendor/kimi-search.js +228 -0
  338. package/dist/humain/web-access/vendor/kimi-search.js.map +1 -0
  339. package/dist/humain/web-access/vendor/openai-search.d.ts +3 -0
  340. package/dist/humain/web-access/vendor/openai-search.d.ts.map +1 -1
  341. package/dist/humain/web-access/vendor/openai-search.js +89 -16
  342. package/dist/humain/web-access/vendor/openai-search.js.map +1 -1
  343. package/dist/humain/web-access/vendor/page-query.d.ts.map +1 -1
  344. package/dist/humain/web-access/vendor/page-query.js +56 -8
  345. package/dist/humain/web-access/vendor/page-query.js.map +1 -1
  346. package/dist/humain/web-access/vendor/ssrf-protection.js +6 -1
  347. package/dist/humain/web-access/vendor/ssrf-protection.js.map +1 -1
  348. package/dist/humain/web-access/vendor/utils.d.ts +12 -0
  349. package/dist/humain/web-access/vendor/utils.d.ts.map +1 -1
  350. package/dist/humain/web-access/vendor/utils.js +343 -1
  351. package/dist/humain/web-access/vendor/utils.js.map +1 -1
  352. package/dist/humain/web-access/vendor/xcrawl.d.ts +4 -0
  353. package/dist/humain/web-access/vendor/xcrawl.d.ts.map +1 -0
  354. package/dist/humain/web-access/vendor/xcrawl.js +251 -0
  355. package/dist/humain/web-access/vendor/xcrawl.js.map +1 -0
  356. package/dist/humain/web-access/vendor/youtube-extract.js +2 -2
  357. package/dist/humain/web-access/vendor/youtube-extract.js.map +1 -1
  358. package/dist/index.d.ts +5 -5
  359. package/dist/index.d.ts.map +1 -1
  360. package/dist/index.js +4 -4
  361. package/dist/index.js.map +1 -1
  362. package/dist/main.d.ts +3 -0
  363. package/dist/main.d.ts.map +1 -1
  364. package/dist/main.js +17 -14
  365. package/dist/main.js.map +1 -1
  366. package/dist/migrations.d.ts.map +1 -1
  367. package/dist/migrations.js +4 -3
  368. package/dist/migrations.js.map +1 -1
  369. package/dist/modes/interactive/components/model-selector.d.ts +9 -3
  370. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  371. package/dist/modes/interactive/components/model-selector.js +53 -10
  372. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  373. package/dist/modes/interactive/components/settings-selector.d.ts +7 -2
  374. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  375. package/dist/modes/interactive/components/settings-selector.js +118 -55
  376. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  377. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  378. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  379. package/dist/modes/interactive/components/settings-submenu.js +151 -0
  380. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  381. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  382. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  383. package/dist/modes/interactive/components/thinking-selector.js +68 -17
  384. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  385. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  386. package/dist/modes/interactive/external-editor.js +2 -1
  387. package/dist/modes/interactive/external-editor.js.map +1 -1
  388. package/dist/modes/interactive/interactive-mode.d.ts +11 -0
  389. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  390. package/dist/modes/interactive/interactive-mode.js +176 -106
  391. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  392. package/dist/modes/interactive/session-share.d.ts +16 -0
  393. package/dist/modes/interactive/session-share.d.ts.map +1 -0
  394. package/dist/modes/interactive/session-share.js +188 -0
  395. package/dist/modes/interactive/session-share.js.map +1 -0
  396. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  397. package/dist/modes/interactive/theme/theme.js +6 -5
  398. package/dist/modes/interactive/theme/theme.js.map +1 -1
  399. package/dist/modes/json-event.d.ts +16 -13
  400. package/dist/modes/json-event.d.ts.map +1 -1
  401. package/dist/modes/json-event.js +20 -6
  402. package/dist/modes/json-event.js.map +1 -1
  403. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  404. package/dist/modes/rpc/rpc-client.js +3 -2
  405. package/dist/modes/rpc/rpc-client.js.map +1 -1
  406. package/dist/modes/rpc/rpc-types.d.ts +2 -7
  407. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  408. package/dist/modes/rpc/rpc-types.js.map +1 -1
  409. package/dist/package-manager-cli.d.ts +1 -0
  410. package/dist/package-manager-cli.d.ts.map +1 -1
  411. package/dist/package-manager-cli.js +189 -3
  412. package/dist/package-manager-cli.js.map +1 -1
  413. package/dist/utils/changelog.d.ts +1 -1
  414. package/dist/utils/changelog.d.ts.map +1 -1
  415. package/dist/utils/changelog.js +12 -2
  416. package/dist/utils/changelog.js.map +1 -1
  417. package/dist/utils/frontmatter.d.ts.map +1 -1
  418. package/dist/utils/frontmatter.js +2 -1
  419. package/dist/utils/frontmatter.js.map +1 -1
  420. package/dist/utils/management-http.d.ts +5 -3
  421. package/dist/utils/management-http.d.ts.map +1 -1
  422. package/dist/utils/management-http.js +14 -10
  423. package/dist/utils/management-http.js.map +1 -1
  424. package/dist/utils/shell.d.ts +3 -0
  425. package/dist/utils/shell.d.ts.map +1 -1
  426. package/dist/utils/shell.js +17 -5
  427. package/dist/utils/shell.js.map +1 -1
  428. package/dist/utils/syntax-highlight.d.ts +1 -0
  429. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  430. package/dist/utils/syntax-highlight.js +64 -1
  431. package/dist/utils/syntax-highlight.js.map +1 -1
  432. package/dist/utils/text.d.ts +8 -0
  433. package/dist/utils/text.d.ts.map +1 -0
  434. package/dist/utils/text.js +9 -0
  435. package/dist/utils/text.js.map +1 -0
  436. package/dist/utils/version-check.js +4 -4
  437. package/dist/utils/version-check.js.map +1 -1
  438. package/docs/compaction.md +15 -0
  439. package/docs/custom-provider.md +5 -2
  440. package/docs/environment-variables.md +7 -7
  441. package/docs/extensions.md +17 -7
  442. package/docs/json.md +7 -2
  443. package/docs/keybindings.md +8 -8
  444. package/docs/llama-cpp.md +2 -0
  445. package/docs/models.md +8 -2
  446. package/docs/packages.md +2 -2
  447. package/docs/rpc.md +9 -3
  448. package/docs/sdk.md +7 -2
  449. package/docs/settings.md +11 -3
  450. package/docs/skills.md +3 -2
  451. package/docs/terminal-setup.md +8 -0
  452. package/docs/usage.md +10 -7
  453. package/docs/windows.md +15 -1
  454. package/examples/extensions/border-status-editor.ts +1 -1
  455. package/examples/extensions/custom-provider-anthropic/package-lock.json +70 -8
  456. package/examples/extensions/custom-provider-anthropic/package.json +2 -2
  457. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  458. package/examples/extensions/git-checkpoint.ts +2 -2
  459. package/examples/extensions/gondolin/package-lock.json +2 -2
  460. package/examples/extensions/gondolin/package.json +1 -1
  461. package/examples/extensions/notify.ts +3 -1
  462. package/examples/extensions/sandbox/package-lock.json +14 -40
  463. package/examples/extensions/sandbox/package.json +2 -2
  464. package/examples/extensions/titlebar-spinner.ts +1 -1
  465. package/examples/extensions/with-deps/package-lock.json +4 -4
  466. package/examples/extensions/with-deps/package.json +1 -1
  467. package/examples/rpc-extension-ui.ts +1 -1
  468. package/examples/sdk/06-extensions.ts +1 -1
  469. package/examples/sdk/README.md +1 -1
  470. package/npm-shrinkwrap.json +293 -221
  471. package/package.json +21 -18
  472. package/src/humain/mcp-adapter/UPSTREAM.md +15 -5
  473. package/src/humain/mcp-adapter/upstream.json +4 -3
  474. package/src/humain/mcp-adapter/vendor/CHANGELOG.md +82 -0
  475. package/src/humain/web-access/UPSTREAM.md +1 -1
  476. package/src/humain/web-access/upstream.json +6 -4
@@ -15,8 +15,8 @@ import { findContent } from "./content-find.js";
15
15
  import { answerFromPage } from "./page-query.js";
16
16
  import { rewriteSearchQuery } from "./query-rewrite.js";
17
17
  import { clearCloneCache } from "./github-extract.js";
18
- import { getConfiguredSearchRouting, normalizeSearchProviderSelection, RESOLVED_SEARCH_PROVIDERS, SEARCH_PROVIDERS, search } from "./gemini-search.js";
19
- import { formatSeconds, getWebSearchConfigDir, getWebSearchConfigPath, resolveCuratorNetworkConfig } from "./utils.js";
18
+ import { ALL_SEARCH_PROVIDERS, getConfiguredSearchRouting, normalizeSearchProviderSelection, RESOLVED_SEARCH_PROVIDERS, SEARCH_PROVIDERS, search } from "./gemini-search.js";
19
+ import { formatSeconds, getWebSearchConfigDir, getWebSearchConfigPath, installGlobalProxyFetch, resolveCuratorNetworkConfig, runWithProxy } from "./utils.js";
20
20
  import { clearResults, deleteResult, generateId, getAllResults, getResult, restoreFromSession, storeFetchedContentResult, storeResult, } from "./storage.js";
21
21
  import { activityMonitor } from "./activity.js";
22
22
  import { startCuratorServer } from "./curator-server.js";
@@ -30,10 +30,10 @@ import { join } from "node:path";
30
30
  import { isPerplexityAvailable } from "./perplexity.js";
31
31
  import { isExaAvailable } from "./exa.js";
32
32
  import { isGeminiApiAvailable } from "./gemini-api.js";
33
- import { getActiveGoogleEmail, getGeminiWebAvailabilityDiagnostic, isGeminiWebAvailable } from "./gemini-web.js";
33
+ import { getActiveGoogleEmail, getGeminiWebAvailabilityDiagnostic, getGeminiWebAvailabilityDiagnosticDetails, isGeminiWebAvailable } from "./gemini-web.js";
34
34
  import { isBrowserCookieAccessAllowed } from "./gemini-web-config.js";
35
35
  import { isBraveAvailable } from "./brave.js";
36
- import { isOpenAISearchAvailable } from "./openai-search.js";
36
+ import { isCurrentModelHostedSearchEligible, isOpenAISearchAvailable } from "./openai-search.js";
37
37
  import { isParallelAvailable } from "./parallel.js";
38
38
  import { isParallelMcpAvailable } from "./parallel-mcp.js";
39
39
  import { isTinyFishAvailable } from "./tinyfish.js";
@@ -51,10 +51,12 @@ import { isSearXNGAvailable } from "./searxng.js";
51
51
  import { isDuckDuckGoAvailable } from "./duckduckgo.js";
52
52
  import { isAnySearchAvailable } from "./anysearch.js";
53
53
  import { isXaiSearchAvailable } from "./xai-search.js";
54
+ import { isKimiSearchAvailable } from "./kimi-search.js";
54
55
  import { isBrightDataAvailable } from "./brightdata.js";
55
56
  import { isSerpBaseAvailable } from "./serpbase.js";
56
57
  import { isSerperAvailable } from "./serper.js";
57
58
  import { isValyuAvailable } from "./valyu.js";
59
+ import { isXcrawlAvailable } from "./xcrawl.js";
58
60
  import { buildSearchErrorPlan } from "./render-search-error.js";
59
61
  import { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns, splitThinkingSuffix } from "./summary-model-scope.js";
60
62
  import { buildResearchArtifact, withClaimAssessment, storeResearchArtifact, getResearchArtifact, } from "./source-check.js";
@@ -64,6 +66,13 @@ async function fetchAllContent(urls, signal, options) {
64
66
  const extractModule = await (extractModulePromise ??= import("./extract.js"));
65
67
  return extractModule.fetchAllContent(urls, signal, options);
66
68
  }
69
+ function withRegisteredFetchOptions(options, toolNames, proxy) {
70
+ return {
71
+ ...(options ?? {}),
72
+ toolNames,
73
+ ...(proxy !== undefined ? { proxy } : {}),
74
+ };
75
+ }
67
76
  function isAbortError(err) {
68
77
  return (err instanceof Error ? err.message : String(err)).toLowerCase().includes("abort");
69
78
  }
@@ -248,6 +257,34 @@ function normalizeQueryList(queryList) {
248
257
  }
249
258
  return normalized;
250
259
  }
260
+ // Some local models serialize a multi-query list into the single-string `query`
261
+ // field as a JSON array (query: "[\"a\", \"b\"]") instead of using the
262
+ // `queries` parameter. Forwarding that raw string verbatim makes every backend
263
+ // search for the literal array text and return zero results, with no signal to
264
+ // the model that its argument shape was wrong. Expand a string that parses as a
265
+ // JSON array of strings so each element is searched independently.
266
+ function expandQueryString(query) {
267
+ if (typeof query !== "string")
268
+ return [];
269
+ const trimmed = query.trim();
270
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
271
+ try {
272
+ const parsed = JSON.parse(trimmed);
273
+ // Only expand an unambiguously string-only array. Mixed or non-string
274
+ // arrays are kept as the literal query so we never silently drop
275
+ // members or collapse them into an empty search.
276
+ if (Array.isArray(parsed) && parsed.every((entry) => typeof entry === "string")) {
277
+ return parsed
278
+ .map((entry) => entry.trim())
279
+ .filter((entry) => entry.length > 0);
280
+ }
281
+ }
282
+ catch {
283
+ // Not JSON — treat as a literal query string.
284
+ }
285
+ }
286
+ return [query];
287
+ }
251
288
  function getCuratorTimeoutSeconds() {
252
289
  const source = loadConfig();
253
290
  const explicit = normalizeCuratorTimeoutSeconds(source.curatorTimeoutSeconds);
@@ -271,7 +308,7 @@ function shouldAutoOpenCuratorBrowser(config) {
271
308
  return true;
272
309
  }
273
310
  async function getProviderAvailability(ctx) {
274
- const geminiWebAvail = await isGeminiWebAvailable();
311
+ const geminiWebAvail = await getOptionalGeminiWebAvailability();
275
312
  const geminiApiAvail = isGeminiApiAvailable();
276
313
  const providers = {
277
314
  openai: await isOpenAISearchAvailable(ctx),
@@ -294,19 +331,29 @@ async function getProviderAvailability(ctx) {
294
331
  perplexity: isPerplexityAvailable(),
295
332
  exa: isExaAvailable(),
296
333
  gemini: geminiApiAvail || !!geminiWebAvail,
334
+ kimi: await isKimiSearchAvailable(ctx),
297
335
  anysearch: isAnySearchAvailable(),
336
+ xcrawl: isXcrawlAvailable(),
298
337
  xai: await isXaiSearchAvailable(ctx),
299
338
  brightdata: isBrightDataAvailable(),
300
339
  serpbase: isSerpBaseAvailable(),
301
340
  serper: isSerperAvailable(),
302
341
  valyu: isValyuAvailable(),
303
342
  };
343
+ const allSearchProviders = new Set(ALL_SEARCH_PROVIDERS);
304
344
  return {
305
- // Parallel MCP, DuckDuckGo, AnySearch, xAI, Bright Data, SerpBase, Serper, and Valyu are explicit-only, so they never make `all` eligible.
306
- all: Object.entries(providers).some(([provider, available]) => provider !== "parallel-mcp" && provider !== "duckduckgo" && provider !== "anysearch" && provider !== "xai" && provider !== "brightdata" && provider !== "serpbase" && provider !== "serper" && provider !== "valyu" && provider !== "gemini" && available) || geminiApiAvail,
345
+ all: Object.entries(providers).some(([provider, available]) => provider !== "gemini" && allSearchProviders.has(provider) && available) || geminiApiAvail,
307
346
  ...providers,
308
347
  };
309
348
  }
349
+ async function getOptionalGeminiWebAvailability() {
350
+ try {
351
+ return await isGeminiWebAvailable();
352
+ }
353
+ catch {
354
+ return null;
355
+ }
356
+ }
310
357
  function shouldUseOpenAICodexDefault(ctx) {
311
358
  return ctx?.model?.provider === "openai-codex";
312
359
  }
@@ -330,7 +377,7 @@ async function loadCuratorBootstrap(requestedProvider, ctx, options) {
330
377
  };
331
378
  }
332
379
  export function resolveCuratorDefaultProvider(provider, available, ctx, options) {
333
- return resolveProvider(provider, available, options, shouldUseOpenAICodexDefault(ctx));
380
+ return resolveProvider(provider, available, options, shouldUseOpenAICodexDefault(ctx), ctx);
334
381
  }
335
382
  function firstAvailableProvider(available, preferOpenAI, fallback) {
336
383
  if (available.searxng)
@@ -373,7 +420,7 @@ function firstAvailableProvider(available, preferOpenAI, fallback) {
373
420
  return "gemini";
374
421
  return fallback;
375
422
  }
376
- function resolveProvider(provider, available, options, preferOpenAICodexDefault = false) {
423
+ function resolveProvider(provider, available, options, preferOpenAICodexDefault = false, ctx) {
377
424
  if (Array.isArray(provider))
378
425
  return "all";
379
426
  const preferOpenAI = shouldPreferOpenAI(options, preferOpenAICodexDefault);
@@ -381,10 +428,12 @@ function resolveProvider(provider, available, options, preferOpenAICodexDefault
381
428
  const routing = getConfiguredSearchRouting();
382
429
  if (routing) {
383
430
  for (const candidate of routing.providers) {
431
+ if (candidate === "openai" && routing.useCurrentModel === true && !isCurrentModelHostedSearchEligible(ctx))
432
+ continue;
384
433
  if (available[candidate])
385
434
  return candidate;
386
435
  }
387
- return routing.providers[0];
436
+ return routing.providers.find(candidate => candidate !== "openai" || routing.useCurrentModel !== true || isCurrentModelHostedSearchEligible(ctx)) ?? routing.providers[0];
388
437
  }
389
438
  return firstAvailableProvider(available, preferOpenAI, "exa");
390
439
  }
@@ -495,6 +544,25 @@ function normalizeFindQueries(value) {
495
544
  throw new Error("findText must contain at least one non-empty string");
496
545
  return queries;
497
546
  }
547
+ function normalizeFindMode(value) {
548
+ if (value === undefined)
549
+ return undefined;
550
+ if (value === "exact" || value === "case-insensitive" || value === "fuzzy")
551
+ return value;
552
+ throw new Error('findMode must be "exact", "case-insensitive", or "fuzzy"');
553
+ }
554
+ function normalizeGetSearchContentParams(params) {
555
+ const normalized = { ...params, findMode: normalizeFindMode(params.findMode) };
556
+ if (normalized.query?.trim() === "")
557
+ delete normalized.query;
558
+ if (normalized.url?.trim() === "")
559
+ delete normalized.url;
560
+ if (normalized.findText !== undefined) {
561
+ delete normalized.offset;
562
+ delete normalized.limit;
563
+ }
564
+ return normalized;
565
+ }
498
566
  function formatInputValue(value) {
499
567
  if (typeof value === "string")
500
568
  return JSON.stringify(value);
@@ -819,11 +887,18 @@ function handleSessionChange(ctx) {
819
887
  }
820
888
  export default function (pi) {
821
889
  const initConfig = loadConfigForExtensionInit();
890
+ installGlobalProxyFetch();
822
891
  const toolNames = resolveToolNames(initConfig);
823
892
  const webSearchEnabled = isToolEnabled(initConfig, "webSearch");
824
893
  const sourceCheckEnabled = isToolEnabled(initConfig, "sourceCheck");
825
894
  const fetchContentEnabled = isToolEnabled(initConfig, "fetchContent");
826
895
  const getSearchContentEnabled = isToolEnabled(initConfig, "getSearchContent");
896
+ // Names as registered this session, so fetch failure guidance never points
897
+ // at tools that are disabled or were renamed after init.
898
+ const registeredToolNames = {
899
+ ...(webSearchEnabled ? { webSearch: toolNames.webSearch } : {}),
900
+ ...(fetchContentEnabled ? { fetchContent: toolNames.fetchContent } : {}),
901
+ };
827
902
  const storedContentSources = joinToolNames([
828
903
  ...(webSearchEnabled ? [toolNames.webSearch] : []),
829
904
  ...(sourceCheckEnabled ? [toolNames.sourceCheck] : []),
@@ -837,13 +912,13 @@ export default function (pi) {
837
912
  : "Full original content is stored internally, but the retrieval tool is not registered.";
838
913
  const curateKey = initConfig.shortcuts?.curate || DEFAULT_SHORTCUTS.curate;
839
914
  const activityKey = initConfig.shortcuts?.activity || DEFAULT_SHORTCUTS.activity;
840
- function startBackgroundFetch(urls) {
915
+ function startBackgroundFetch(urls, proxy) {
841
916
  if (urls.length === 0)
842
917
  return null;
843
918
  const fetchId = generateId();
844
919
  const controller = new AbortController();
845
920
  pendingFetches.set(fetchId, controller);
846
- fetchAllContent(urls, controller.signal)
921
+ runWithProxy(proxy, () => fetchAllContent(urls, controller.signal, withRegisteredFetchOptions(undefined, registeredToolNames, proxy)))
847
922
  .then((fetched) => {
848
923
  if (!sessionActive || !pendingFetches.has(fetchId))
849
924
  return;
@@ -1107,7 +1182,7 @@ export default function (pi) {
1107
1182
  }
1108
1183
  }
1109
1184
  else if (opts.includeContent) {
1110
- fetchId = startBackgroundFetch(opts.urls);
1185
+ fetchId = startBackgroundFetch(opts.urls, opts.proxy);
1111
1186
  if (fetchId && !hasApprovedSummary) {
1112
1187
  output += `---\nContent fetching in background [${fetchId}]. Will notify when ready.`;
1113
1188
  }
@@ -1217,20 +1292,22 @@ export default function (pi) {
1217
1292
  defaultSummaryModel: pc.defaultSummaryModel,
1218
1293
  }, {
1219
1294
  async onSummarize(selectedQueryIndices, summarizeSignal, model, feedback) {
1220
- if (pendingCurates.get(callId) !== pc)
1221
- throw new Error("Curator session is no longer active.");
1222
- pc.onUpdate?.({
1223
- content: [{ type: "text", text: "Generating summary draft..." }],
1224
- details: { phase: "generating-summary", progress: 0.9, curatorUrl: pc.curatorUrl, timeoutSeconds: pc.timeoutSeconds, shortcut: curateKey },
1225
- });
1226
- const draft = await generateSummaryForSelectedIndices(selectedQueryIndices, pc.searchResults, pc.summaryContext, summarizeSignal, model, feedback);
1227
- if (pendingCurates.get(callId) !== pc)
1228
- throw new Error("Curator session is no longer active.");
1229
- pc.onUpdate?.({
1230
- content: [{ type: "text", text: "Summary draft ready — waiting for approval..." }],
1231
- details: { phase: "waiting-for-approval", progress: 1, curatorUrl: pc.curatorUrl, timeoutSeconds: pc.timeoutSeconds, shortcut: curateKey },
1295
+ return runWithProxy(pc.proxy, async () => {
1296
+ if (pendingCurates.get(callId) !== pc)
1297
+ throw new Error("Curator session is no longer active.");
1298
+ pc.onUpdate?.({
1299
+ content: [{ type: "text", text: "Generating summary draft..." }],
1300
+ details: { phase: "generating-summary", progress: 0.9, curatorUrl: pc.curatorUrl, timeoutSeconds: pc.timeoutSeconds, shortcut: curateKey },
1301
+ });
1302
+ const draft = await generateSummaryForSelectedIndices(selectedQueryIndices, pc.searchResults, pc.summaryContext, summarizeSignal, model, feedback);
1303
+ if (pendingCurates.get(callId) !== pc)
1304
+ throw new Error("Curator session is no longer active.");
1305
+ pc.onUpdate?.({
1306
+ content: [{ type: "text", text: "Summary draft ready waiting for approval..." }],
1307
+ details: { phase: "waiting-for-approval", progress: 1, curatorUrl: pc.curatorUrl, timeoutSeconds: pc.timeoutSeconds, shortcut: curateKey },
1308
+ });
1309
+ return draft;
1232
1310
  });
1233
- return draft;
1234
1311
  },
1235
1312
  onSubmit(payload) {
1236
1313
  if (pendingCurates.get(callId) !== pc)
@@ -1248,6 +1325,7 @@ export default function (pi) {
1248
1325
  inlineContent: filteredInline.length > 0 ? filteredInline : undefined,
1249
1326
  curated: true,
1250
1327
  curatedFrom: pc.searchResults.size,
1328
+ proxy: pc.proxy,
1251
1329
  };
1252
1330
  if (!payload.rawResults) {
1253
1331
  const resolvedSummary = resolveSummaryForSubmit(payload, pc.searchResults);
@@ -1277,6 +1355,7 @@ export default function (pi) {
1277
1355
  workflow: pc.workflow,
1278
1356
  approvedSummary: resolvedSummary.approvedSummary,
1279
1357
  summaryMeta: resolvedSummary.summaryMeta,
1358
+ proxy: pc.proxy,
1280
1359
  }));
1281
1360
  }
1282
1361
  else {
@@ -1309,23 +1388,25 @@ export default function (pi) {
1309
1388
  }
1310
1389
  },
1311
1390
  async onAddSearch(query, provider) {
1312
- if (pendingCurates.get(callId) !== pc)
1313
- throw new Error("Curator session is no longer active.");
1314
- const requestedProvider = resolveCuratorSearchProvider(provider, pc.searchProvider);
1315
- const response = await search(query, {
1316
- provider: requestedProvider,
1317
- numResults: pc.numResults,
1318
- recencyFilter: pc.recencyFilter,
1319
- domainFilter: pc.domainFilter,
1320
- includeContent: pc.includeContent,
1321
- signal: addSearchSignal,
1322
- extensionContext: ctx,
1391
+ return runWithProxy(pc.proxy, async () => {
1392
+ if (pendingCurates.get(callId) !== pc)
1393
+ throw new Error("Curator session is no longer active.");
1394
+ const requestedProvider = resolveCuratorSearchProvider(provider, pc.searchProvider);
1395
+ const response = await search(query, {
1396
+ provider: requestedProvider,
1397
+ numResults: pc.numResults,
1398
+ recencyFilter: pc.recencyFilter,
1399
+ domainFilter: pc.domainFilter,
1400
+ includeContent: pc.includeContent,
1401
+ signal: addSearchSignal,
1402
+ extensionContext: ctx,
1403
+ });
1404
+ if (pendingCurates.get(callId) !== pc)
1405
+ throw new Error("Curator session is no longer active.");
1406
+ if (response.inlineContent)
1407
+ pc.allInlineContent.push(...response.inlineContent);
1408
+ return toCuratorSearchEntries(response);
1323
1409
  });
1324
- if (pendingCurates.get(callId) !== pc)
1325
- throw new Error("Curator session is no longer active.");
1326
- if (response.inlineContent)
1327
- pc.allInlineContent.push(...response.inlineContent);
1328
- return toCuratorSearchEntries(response);
1329
1410
  },
1330
1411
  onAddSearchResults(entries) {
1331
1412
  if (pendingCurates.get(callId) !== pc)
@@ -1335,9 +1416,11 @@ export default function (pi) {
1335
1416
  }
1336
1417
  },
1337
1418
  async onRewriteQuery(query, rewriteSignal) {
1338
- if (pendingCurates.get(callId) !== pc)
1339
- throw new Error("Curator session is no longer active.");
1340
- return rewriteSearchQuery(query, pc.summaryContext, rewriteSignal);
1419
+ return runWithProxy(pc.proxy, async () => {
1420
+ if (pendingCurates.get(callId) !== pc)
1421
+ throw new Error("Curator session is no longer active.");
1422
+ return rewriteSearchQuery(query, pc.summaryContext, rewriteSignal);
1423
+ });
1341
1424
  },
1342
1425
  });
1343
1426
  if (pendingCurates.get(callId) !== pc) {
@@ -1457,7 +1540,7 @@ export default function (pi) {
1457
1540
  pi.registerTool({
1458
1541
  name: toolNames.webSearch,
1459
1542
  label: "Web Search",
1460
- description: `Search the web using OpenAI, Brave, Parallel, Parallel MCP, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, AnySearch, Valyu, xAI, Bright Data, SerpBase, or Serper. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except Parallel MCP, DuckDuckGo, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper. 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. Parallel MCP, DuckDuckGo, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper 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, SearXNG is preferred first for local/private search. When the active Pi model is openai-codex, Codex-backed OpenAI search is preferred next. Otherwise Exa is preferred before OpenAI, then Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web.`,
1543
+ description: `Search the web using OpenAI, Brave, Parallel, Parallel MCP, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, Kimi, AnySearch, XCrawl, Valyu, xAI, Bright Data, SerpBase, or Serper. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Bright Data, SerpBase, and Serper. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; Kimi search uses a Kimi Code Plan authenticated through /login kimi-coding; xAI search uses a SuperGrok/X Premium subscription or xAI API key. Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Bright Data, SerpBase, and Serper 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, SearXNG is preferred first for local/private search. When the active Pi model is openai-codex, Codex-backed OpenAI search is preferred next. Otherwise Exa is preferred before OpenAI, then Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web.`,
1461
1544
  promptSnippet: "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.",
1462
1545
  parameters: Type.Object({
1463
1546
  query: Type.Optional(Type.String({ description: "Single search query. For research tasks, prefer 'queries' with multiple varied angles instead." })),
@@ -1466,290 +1549,297 @@ export default function (pi) {
1466
1549
  includeContent: Type.Optional(Type.Boolean({ description: "Fetch full page content (async)" })),
1467
1550
  recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency" })),
1468
1551
  domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
1469
- provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except Parallel MCP, DuckDuckGo, AnySearch, xAI, Bright Data, SerpBase, Serper, and Valyu, omit this field to use the configured provider, or use auto when none is configured")),
1552
+ provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Bright Data, SerpBase, and Serper, omit this field to use the configured provider, or use auto when none is configured")),
1470
1553
  workflow: Type.Optional(StringEnum(["none", "summary-review", "auto-summary"], {
1471
1554
  description: "Search workflow mode: none = no curator, summary-review = open curator with auto summary draft (default), auto-summary = generate summary without opening curator",
1472
1555
  })),
1556
+ proxy: Type.Optional(Type.String({
1557
+ description: "http(s) proxy URL (e.g. http://host:port) used for every outbound request in this call (search APIs and content fetches). Node fetch ignores HTTP(S)_PROXY env vars, so set this (or `proxy` in web-search.json) when direct access is blocked; empty string forces direct access.",
1558
+ })),
1473
1559
  }),
1474
1560
  async execute(callId, params, signal, onUpdate, ctx) {
1475
- const rawQueryList = Array.isArray(params.queries)
1476
- ? params.queries
1477
- : (params.query !== undefined ? [params.query] : []);
1478
- const queryList = normalizeQueryList(rawQueryList);
1479
- const configWorkflow = loadConfigForExtensionInit().workflow;
1480
- const workflow = resolveWorkflow(params.workflow ?? configWorkflow, ctx?.hasUI !== false);
1481
- const shouldCurate = workflow === "summary-review";
1482
- const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
1483
- if (queryList.length === 0) {
1484
- return {
1485
- content: [{ type: "text", text: "Error: No query provided. Use 'query' or 'queries' parameter." }],
1486
- details: { error: "No query provided" },
1487
- };
1488
- }
1489
- if (shouldCurate && !ctx) {
1490
- return {
1491
- content: [{ type: "text", text: "Error: Curation requires an active extension context." }],
1492
- details: { error: "Missing extension context" },
1493
- };
1494
- }
1495
- if (shouldCurate) {
1496
- closeCurator(callId);
1497
- let resolvePromise = () => { };
1498
- const promise = new Promise((resolve) => {
1499
- resolvePromise = resolve;
1500
- });
1501
- const includeContent = params.includeContent ?? false;
1502
- const searchResults = new Map();
1503
- const resultSlots = new Map();
1561
+ return runWithProxy(typeof params.proxy === "string" ? params.proxy : undefined, async () => {
1562
+ const rawQueryList = Array.isArray(params.queries)
1563
+ ? params.queries
1564
+ : (params.query !== undefined ? expandQueryString(params.query) : []);
1565
+ const queryList = normalizeQueryList(rawQueryList);
1566
+ const configWorkflow = loadConfigForExtensionInit().workflow;
1567
+ const workflow = resolveWorkflow(params.workflow ?? configWorkflow, ctx?.hasUI !== false);
1568
+ const shouldCurate = workflow === "summary-review";
1569
+ const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
1570
+ if (queryList.length === 0) {
1571
+ return {
1572
+ content: [{ type: "text", text: "Error: No query provided. Use 'query' or 'queries' parameter." }],
1573
+ details: { error: "No query provided" },
1574
+ };
1575
+ }
1576
+ if (shouldCurate && !ctx) {
1577
+ return {
1578
+ content: [{ type: "text", text: "Error: Curation requires an active extension context." }],
1579
+ details: { error: "Missing extension context" },
1580
+ };
1581
+ }
1582
+ if (shouldCurate) {
1583
+ closeCurator(callId);
1584
+ let resolvePromise = () => { };
1585
+ const promise = new Promise((resolve) => {
1586
+ resolvePromise = resolve;
1587
+ });
1588
+ const includeContent = params.includeContent ?? false;
1589
+ const searchResults = new Map();
1590
+ const resultSlots = new Map();
1591
+ const allInlineContent = [];
1592
+ let nextResultIndex = queryList.length;
1593
+ const searchAbort = new AbortController();
1594
+ const searchSignal = signal
1595
+ ? AbortSignal.any([signal, searchAbort.signal])
1596
+ : searchAbort.signal;
1597
+ let cancelled = false;
1598
+ const requestedProvider = resolveRequestedProvider(params.provider);
1599
+ const bootstrap = await loadCuratorBootstrap(requestedProvider, ctx, {
1600
+ numResults: params.numResults,
1601
+ recencyFilter,
1602
+ });
1603
+ const availableProviders = bootstrap.availableProviders;
1604
+ const defaultProvider = bootstrap.defaultProvider;
1605
+ const searchProvider = requestedProvider;
1606
+ const curatorTimeoutSeconds = bootstrap.timeoutSeconds;
1607
+ const curatorWorkflow = "summary-review";
1608
+ const summaryContext = {
1609
+ model: ctx.model,
1610
+ modelRegistry: ctx.modelRegistry,
1611
+ cwd: ctx.cwd,
1612
+ isProjectTrusted: () => ctx.isProjectTrusted(),
1613
+ };
1614
+ const summaryModelChoices = await loadSummaryModelChoices(summaryContext);
1615
+ const pc = {
1616
+ phase: "searching",
1617
+ workflow: curatorWorkflow,
1618
+ summaryContext,
1619
+ searchResults,
1620
+ resultSlots,
1621
+ allInlineContent,
1622
+ queryList,
1623
+ includeContent,
1624
+ numResults: params.numResults,
1625
+ recencyFilter,
1626
+ domainFilter: params.domainFilter,
1627
+ availableProviders,
1628
+ defaultProvider,
1629
+ searchProvider,
1630
+ summaryModels: summaryModelChoices.summaryModels,
1631
+ defaultSummaryModel: summaryModelChoices.defaultSummaryModel,
1632
+ timeoutSeconds: curatorTimeoutSeconds,
1633
+ proxy: typeof params.proxy === "string" ? params.proxy : undefined,
1634
+ onUpdate: onUpdate,
1635
+ signal,
1636
+ abortSearches: () => {
1637
+ if (!searchAbort.signal.aborted)
1638
+ searchAbort.abort();
1639
+ },
1640
+ finish: () => { },
1641
+ cancel: () => { },
1642
+ };
1643
+ const finish = (value) => {
1644
+ if (cancelled)
1645
+ return;
1646
+ cancelled = true;
1647
+ pc.abortSearches();
1648
+ signal?.removeEventListener("abort", onAbort);
1649
+ pendingCurates.delete(callId);
1650
+ resolvePromise(value);
1651
+ };
1652
+ const cancel = (reason = "stale") => {
1653
+ if (cancelled)
1654
+ return;
1655
+ const conn = activeCurators.get(callId)?.getConnectionState();
1656
+ finish(buildCurationCancelledReturn(reason, {
1657
+ queries: Array.from(searchResults.values()),
1658
+ queryCount: queryList.length,
1659
+ browserConnected: conn?.browserConnected,
1660
+ lastHeartbeatAgeMs: conn?.lastHeartbeatAgeMs,
1661
+ curatorUrl: pc.curatorUrl,
1662
+ browserOpenError: pc.browserOpenError,
1663
+ }));
1664
+ };
1665
+ pc.finish = finish;
1666
+ pc.cancel = cancel;
1667
+ const onAbort = () => closeCurator(callId);
1668
+ pendingCurates.set(callId, pc);
1669
+ signal?.addEventListener("abort", onAbort, { once: true });
1670
+ pc.browserPromise = openCuratorBrowser(callId, pc, ctx, false);
1671
+ for (let qi = 0; qi < queryList.length; qi++) {
1672
+ if (signal?.aborted || cancelled || searchAbort.signal.aborted)
1673
+ break;
1674
+ onUpdate?.({
1675
+ content: [{ type: "text", text: `Searching ${qi + 1}/${queryList.length}: "${queryList[qi]}"...` }],
1676
+ details: { phase: "searching", progress: qi / queryList.length, currentQuery: queryList[qi] },
1677
+ });
1678
+ const requestedProvider = pc.searchProvider;
1679
+ try {
1680
+ const response = await search(queryList[qi], {
1681
+ provider: requestedProvider,
1682
+ numResults: params.numResults,
1683
+ recencyFilter,
1684
+ domainFilter: params.domainFilter,
1685
+ includeContent: params.includeContent,
1686
+ signal: searchSignal,
1687
+ extensionContext: ctx,
1688
+ });
1689
+ if (signal?.aborted || cancelled || searchAbort.signal.aborted)
1690
+ break;
1691
+ if (response.inlineContent)
1692
+ allInlineContent.push(...response.inlineContent);
1693
+ const entries = toCuratorSearchEntries(response);
1694
+ const curator = activeCurators.get(callId);
1695
+ for (let entryIndex = 0; entryIndex < entries.length; entryIndex++) {
1696
+ const entry = entries[entryIndex];
1697
+ const resultIndex = entryIndex === 0 ? qi : nextResultIndex++;
1698
+ const indexedEntry = {
1699
+ ...entry,
1700
+ queryIndex: resultIndex,
1701
+ query: queryList[qi],
1702
+ };
1703
+ searchResults.set(resultIndex, indexedCuratorEntryToQueryResult(indexedEntry));
1704
+ resultSlots.set(resultIndex, qi);
1705
+ if (curator) {
1706
+ if (entry.error) {
1707
+ curator.pushError(resultIndex, entry.error, entry.provider, { query: queryList[qi], slotIndex: qi });
1708
+ }
1709
+ else {
1710
+ curator.pushResult(resultIndex, { ...entry, query: queryList[qi], slotIndex: qi });
1711
+ }
1712
+ }
1713
+ }
1714
+ }
1715
+ catch (err) {
1716
+ if (signal?.aborted || cancelled || searchAbort.signal.aborted)
1717
+ break;
1718
+ const message = err instanceof Error ? err.message : String(err);
1719
+ const failedProvider = toCuratorProvider(requestedProvider);
1720
+ searchResults.set(qi, { query: queryList[qi], answer: "", results: [], error: message, provider: failedProvider });
1721
+ resultSlots.set(qi, qi);
1722
+ const curator = activeCurators.get(callId);
1723
+ if (curator) {
1724
+ curator.pushError(qi, message, failedProvider, { query: queryList[qi], slotIndex: qi });
1725
+ }
1726
+ }
1727
+ }
1728
+ if (signal?.aborted || cancelled || searchAbort.signal.aborted) {
1729
+ cancel();
1730
+ return promise;
1731
+ }
1732
+ await pc.browserPromise;
1733
+ const curator = activeCurators.get(callId);
1734
+ if (curator && !cancelled) {
1735
+ curator.searchesDone();
1736
+ if (pc.browserOpenError) {
1737
+ pc.onUpdate?.({
1738
+ content: [{ type: "text", text: `All searches complete. Open the curator manually: ${pc.curatorUrl}` }],
1739
+ details: {
1740
+ phase: "curator-fallback",
1741
+ progress: 1,
1742
+ curatorUrl: pc.curatorUrl,
1743
+ timeoutSeconds: pc.timeoutSeconds,
1744
+ shortcut: curateKey,
1745
+ browserOpenError: pc.browserOpenError,
1746
+ },
1747
+ });
1748
+ }
1749
+ else {
1750
+ pc.onUpdate?.({
1751
+ content: [{ type: "text", text: "All searches complete — waiting for summary approval in browser..." }],
1752
+ details: {
1753
+ phase: "curating",
1754
+ progress: 1,
1755
+ curatorUrl: pc.curatorUrl,
1756
+ timeoutSeconds: pc.timeoutSeconds,
1757
+ shortcut: curateKey,
1758
+ },
1759
+ });
1760
+ }
1761
+ }
1762
+ return promise;
1763
+ }
1764
+ const searchResults = [];
1765
+ const allUrls = [];
1504
1766
  const allInlineContent = [];
1505
- let nextResultIndex = queryList.length;
1506
- const searchAbort = new AbortController();
1507
- const searchSignal = signal
1508
- ? AbortSignal.any([signal, searchAbort.signal])
1509
- : searchAbort.signal;
1510
- let cancelled = false;
1511
- const requestedProvider = resolveRequestedProvider(params.provider);
1512
- const bootstrap = await loadCuratorBootstrap(requestedProvider, ctx, {
1513
- numResults: params.numResults,
1514
- recencyFilter,
1515
- });
1516
- const availableProviders = bootstrap.availableProviders;
1517
- const defaultProvider = bootstrap.defaultProvider;
1518
- const searchProvider = requestedProvider;
1519
- const curatorTimeoutSeconds = bootstrap.timeoutSeconds;
1520
- const curatorWorkflow = "summary-review";
1521
- const summaryContext = {
1522
- model: ctx.model,
1523
- modelRegistry: ctx.modelRegistry,
1524
- cwd: ctx.cwd,
1525
- isProjectTrusted: () => ctx.isProjectTrusted(),
1526
- };
1527
- const summaryModelChoices = await loadSummaryModelChoices(summaryContext);
1528
- const pc = {
1529
- phase: "searching",
1530
- workflow: curatorWorkflow,
1531
- summaryContext,
1532
- searchResults,
1533
- resultSlots,
1534
- allInlineContent,
1535
- queryList,
1536
- includeContent,
1537
- numResults: params.numResults,
1538
- recencyFilter,
1539
- domainFilter: params.domainFilter,
1540
- availableProviders,
1541
- defaultProvider,
1542
- searchProvider,
1543
- summaryModels: summaryModelChoices.summaryModels,
1544
- defaultSummaryModel: summaryModelChoices.defaultSummaryModel,
1545
- timeoutSeconds: curatorTimeoutSeconds,
1546
- onUpdate: onUpdate,
1547
- signal,
1548
- abortSearches: () => {
1549
- if (!searchAbort.signal.aborted)
1550
- searchAbort.abort();
1551
- },
1552
- finish: () => { },
1553
- cancel: () => { },
1554
- };
1555
- const finish = (value) => {
1556
- if (cancelled)
1557
- return;
1558
- cancelled = true;
1559
- pc.abortSearches();
1560
- signal?.removeEventListener("abort", onAbort);
1561
- pendingCurates.delete(callId);
1562
- resolvePromise(value);
1563
- };
1564
- const cancel = (reason = "stale") => {
1565
- if (cancelled)
1566
- return;
1567
- const conn = activeCurators.get(callId)?.getConnectionState();
1568
- finish(buildCurationCancelledReturn(reason, {
1569
- queries: Array.from(searchResults.values()),
1570
- queryCount: queryList.length,
1571
- browserConnected: conn?.browserConnected,
1572
- lastHeartbeatAgeMs: conn?.lastHeartbeatAgeMs,
1573
- curatorUrl: pc.curatorUrl,
1574
- browserOpenError: pc.browserOpenError,
1575
- }));
1576
- };
1577
- pc.finish = finish;
1578
- pc.cancel = cancel;
1579
- const onAbort = () => closeCurator(callId);
1580
- pendingCurates.set(callId, pc);
1581
- signal?.addEventListener("abort", onAbort, { once: true });
1582
- pc.browserPromise = openCuratorBrowser(callId, pc, ctx, false);
1583
- for (let qi = 0; qi < queryList.length; qi++) {
1584
- if (signal?.aborted || cancelled || searchAbort.signal.aborted)
1585
- break;
1767
+ const resolvedProvider = resolveRequestedProvider(params.provider);
1768
+ for (let i = 0; i < queryList.length; i++) {
1769
+ const query = queryList[i];
1586
1770
  onUpdate?.({
1587
- content: [{ type: "text", text: `Searching ${qi + 1}/${queryList.length}: "${queryList[qi]}"...` }],
1588
- details: { phase: "searching", progress: qi / queryList.length, currentQuery: queryList[qi] },
1771
+ content: [{ type: "text", text: `Searching ${i + 1}/${queryList.length}: "${query}"...` }],
1772
+ details: { phase: "search", progress: i / queryList.length, currentQuery: query },
1589
1773
  });
1590
- const requestedProvider = pc.searchProvider;
1591
1774
  try {
1592
- const response = await search(queryList[qi], {
1593
- provider: requestedProvider,
1775
+ const { answer, results, inlineContent, provider } = await search(query, {
1776
+ provider: resolvedProvider,
1594
1777
  numResults: params.numResults,
1595
1778
  recencyFilter,
1596
1779
  domainFilter: params.domainFilter,
1597
1780
  includeContent: params.includeContent,
1598
- signal: searchSignal,
1781
+ signal,
1599
1782
  extensionContext: ctx,
1600
1783
  });
1601
- if (signal?.aborted || cancelled || searchAbort.signal.aborted)
1602
- break;
1603
- if (response.inlineContent)
1604
- allInlineContent.push(...response.inlineContent);
1605
- const entries = toCuratorSearchEntries(response);
1606
- const curator = activeCurators.get(callId);
1607
- for (let entryIndex = 0; entryIndex < entries.length; entryIndex++) {
1608
- const entry = entries[entryIndex];
1609
- const resultIndex = entryIndex === 0 ? qi : nextResultIndex++;
1610
- const indexedEntry = {
1611
- ...entry,
1612
- queryIndex: resultIndex,
1613
- query: queryList[qi],
1614
- };
1615
- searchResults.set(resultIndex, indexedCuratorEntryToQueryResult(indexedEntry));
1616
- resultSlots.set(resultIndex, qi);
1617
- if (curator) {
1618
- if (entry.error) {
1619
- curator.pushError(resultIndex, entry.error, entry.provider, { query: queryList[qi], slotIndex: qi });
1620
- }
1621
- else {
1622
- curator.pushResult(resultIndex, { ...entry, query: queryList[qi], slotIndex: qi });
1623
- }
1784
+ searchResults.push({ query, answer, results, error: null, provider });
1785
+ for (const r of results) {
1786
+ if (!allUrls.includes(r.url)) {
1787
+ allUrls.push(r.url);
1624
1788
  }
1625
1789
  }
1790
+ if (inlineContent)
1791
+ allInlineContent.push(...inlineContent);
1626
1792
  }
1627
1793
  catch (err) {
1628
- if (signal?.aborted || cancelled || searchAbort.signal.aborted)
1629
- break;
1794
+ if (signal?.aborted || isAbortError(err))
1795
+ throw err;
1630
1796
  const message = err instanceof Error ? err.message : String(err);
1631
- const failedProvider = toCuratorProvider(requestedProvider);
1632
- searchResults.set(qi, { query: queryList[qi], answer: "", results: [], error: message, provider: failedProvider });
1633
- resultSlots.set(qi, qi);
1634
- const curator = activeCurators.get(callId);
1635
- if (curator) {
1636
- curator.pushError(qi, message, failedProvider, { query: queryList[qi], slotIndex: qi });
1637
- }
1797
+ const requestedProvider = toCuratorProvider(resolvedProvider);
1798
+ searchResults.push({ query, answer: "", results: [], error: message, provider: requestedProvider });
1638
1799
  }
1639
1800
  }
1640
- if (signal?.aborted || cancelled || searchAbort.signal.aborted) {
1641
- cancel();
1642
- return promise;
1643
- }
1644
- await pc.browserPromise;
1645
- const curator = activeCurators.get(callId);
1646
- if (curator && !cancelled) {
1647
- curator.searchesDone();
1648
- if (pc.browserOpenError) {
1649
- pc.onUpdate?.({
1650
- content: [{ type: "text", text: `All searches complete. Open the curator manually: ${pc.curatorUrl}` }],
1651
- details: {
1652
- phase: "curator-fallback",
1653
- progress: 1,
1654
- curatorUrl: pc.curatorUrl,
1655
- timeoutSeconds: pc.timeoutSeconds,
1656
- shortcut: curateKey,
1657
- browserOpenError: pc.browserOpenError,
1658
- },
1659
- });
1660
- }
1661
- else {
1662
- pc.onUpdate?.({
1663
- content: [{ type: "text", text: "All searches complete — waiting for summary approval in browser..." }],
1664
- details: {
1665
- phase: "curating",
1666
- progress: 1,
1667
- curatorUrl: pc.curatorUrl,
1668
- timeoutSeconds: pc.timeoutSeconds,
1669
- shortcut: curateKey,
1670
- },
1671
- });
1801
+ let approvedSummary;
1802
+ let summaryMeta;
1803
+ if (workflow === "auto-summary") {
1804
+ if (!ctx) {
1805
+ return {
1806
+ content: [{ type: "text", text: "Error: Auto-summary requires an active extension context." }],
1807
+ details: { error: "Missing extension context" },
1808
+ };
1672
1809
  }
1673
- }
1674
- return promise;
1675
- }
1676
- const searchResults = [];
1677
- const allUrls = [];
1678
- const allInlineContent = [];
1679
- const resolvedProvider = resolveRequestedProvider(params.provider);
1680
- for (let i = 0; i < queryList.length; i++) {
1681
- const query = queryList[i];
1682
- onUpdate?.({
1683
- content: [{ type: "text", text: `Searching ${i + 1}/${queryList.length}: "${query}"...` }],
1684
- details: { phase: "search", progress: i / queryList.length, currentQuery: query },
1685
- });
1686
- try {
1687
- const { answer, results, inlineContent, provider } = await search(query, {
1688
- provider: resolvedProvider,
1689
- numResults: params.numResults,
1690
- recencyFilter,
1691
- domainFilter: params.domainFilter,
1692
- includeContent: params.includeContent,
1693
- signal,
1694
- extensionContext: ctx,
1810
+ onUpdate?.({
1811
+ content: [{ type: "text", text: "Generating summary..." }],
1812
+ details: { phase: "generating-summary", progress: 1 },
1695
1813
  });
1696
- searchResults.push({ query, answer, results, error: null, provider });
1697
- for (const r of results) {
1698
- if (!allUrls.includes(r.url)) {
1699
- allUrls.push(r.url);
1700
- }
1701
- }
1702
- if (inlineContent)
1703
- allInlineContent.push(...inlineContent);
1704
- }
1705
- catch (err) {
1706
- if (signal?.aborted || isAbortError(err))
1707
- throw err;
1708
- const message = err instanceof Error ? err.message : String(err);
1709
- const requestedProvider = toCuratorProvider(resolvedProvider);
1710
- searchResults.push({ query, answer: "", results: [], error: message, provider: requestedProvider });
1711
- }
1712
- }
1713
- let approvedSummary;
1714
- let summaryMeta;
1715
- if (workflow === "auto-summary") {
1716
- if (!ctx) {
1717
- return {
1718
- content: [{ type: "text", text: "Error: Auto-summary requires an active extension context." }],
1719
- details: { error: "Missing extension context" },
1814
+ const summaryContext = {
1815
+ model: ctx.model,
1816
+ modelRegistry: ctx.modelRegistry,
1817
+ cwd: ctx.cwd,
1818
+ isProjectTrusted: () => ctx.isProjectTrusted(),
1720
1819
  };
1820
+ const summaryModelChoices = await loadSummaryModelChoices(summaryContext);
1821
+ const generated = await generateSummaryDraft(searchResults, summaryContext, signal, summaryModelChoices.defaultSummaryModel ?? undefined, undefined, undefined, getSummaryGenerationDeadlineMs());
1822
+ approvedSummary = generated.summary;
1823
+ summaryMeta = generated.meta;
1721
1824
  }
1722
- onUpdate?.({
1723
- content: [{ type: "text", text: "Generating summary..." }],
1724
- details: { phase: "generating-summary", progress: 1 },
1825
+ return buildSearchReturn({
1826
+ queryList,
1827
+ results: searchResults,
1828
+ urls: allUrls,
1829
+ includeContent: params.includeContent ?? false,
1830
+ inlineContent: allInlineContent.length > 0 ? allInlineContent : undefined,
1831
+ workflow: workflow === "auto-summary" ? "auto-summary" : undefined,
1832
+ approvedSummary,
1833
+ summaryMeta,
1834
+ proxy: typeof params.proxy === "string" ? params.proxy : undefined,
1725
1835
  });
1726
- const summaryContext = {
1727
- model: ctx.model,
1728
- modelRegistry: ctx.modelRegistry,
1729
- cwd: ctx.cwd,
1730
- isProjectTrusted: () => ctx.isProjectTrusted(),
1731
- };
1732
- const summaryModelChoices = await loadSummaryModelChoices(summaryContext);
1733
- const generated = await generateSummaryDraft(searchResults, summaryContext, signal, summaryModelChoices.defaultSummaryModel ?? undefined, undefined, undefined, getSummaryGenerationDeadlineMs());
1734
- approvedSummary = generated.summary;
1735
- summaryMeta = generated.meta;
1736
- }
1737
- return buildSearchReturn({
1738
- queryList,
1739
- results: searchResults,
1740
- urls: allUrls,
1741
- includeContent: params.includeContent ?? false,
1742
- inlineContent: allInlineContent.length > 0 ? allInlineContent : undefined,
1743
- workflow: workflow === "auto-summary" ? "auto-summary" : undefined,
1744
- approvedSummary,
1745
- summaryMeta,
1746
1836
  });
1747
1837
  },
1748
1838
  renderCall(args, theme) {
1749
1839
  const input = args;
1750
1840
  const rawQueryList = Array.isArray(input.queries)
1751
1841
  ? input.queries
1752
- : (input.query !== undefined ? [input.query] : []);
1842
+ : (input.query !== undefined ? expandQueryString(input.query) : []);
1753
1843
  const queryList = normalizeQueryList(rawQueryList);
1754
1844
  if (queryList.length === 0) {
1755
1845
  return new Text(theme.fg("toolTitle", theme.bold("search ")) + theme.fg("error", "(no query)"), 0, 0);
@@ -1969,91 +2059,96 @@ export default function (pi) {
1969
2059
  fetchContent: Type.Optional(Type.Boolean({ description: "Fetch up to 5 result pages for exact passage extraction." })),
1970
2060
  recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency." })),
1971
2061
  domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains; prefix with - to exclude." })),
1972
- 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, SerpBase, Serper, and Valyu")),
2062
+ provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; all searches every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Bright Data, SerpBase, and Serper")),
2063
+ proxy: Type.Optional(Type.String({
2064
+ description: "http(s) proxy URL (e.g. http://host:port) used for every outbound request in this call (search APIs and result-page fetches). Empty string forces direct access.",
2065
+ })),
1973
2066
  }),
1974
2067
  async execute(_callId, params, signal, _onUpdate, ctx) {
1975
- const claim = typeof params.claim === "string" ? params.claim.trim() : "";
1976
- if (!claim) {
1977
- return { content: [{ type: "text", text: "Error: 'claim' is required." }], details: { error: "Missing claim" } };
1978
- }
1979
- const requestedQueries = Array.isArray(params.queries)
1980
- ? params.queries.filter((query) => typeof query === "string").map((query) => query.trim()).filter(Boolean)
1981
- : [];
1982
- const queries = (requestedQueries.length > 0 ? requestedQueries : [claim]).slice(0, 8);
1983
- const numResults = typeof params.numResults === "number" && Number.isFinite(params.numResults)
1984
- ? Math.min(20, Math.max(1, Math.floor(params.numResults)))
1985
- : 5;
1986
- const domainFilter = Array.isArray(params.domainFilter)
1987
- ? params.domainFilter.filter((domain) => typeof domain === "string")
1988
- : undefined;
1989
- const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
1990
- const resultsByUrl = new Map();
1991
- const summaries = [];
1992
- const errors = [];
1993
- let provider;
1994
- for (const query of queries) {
1995
- if (signal?.aborted)
1996
- break;
1997
- try {
1998
- const response = await search(query, {
1999
- provider: resolveRequestedProvider(params.provider),
2000
- numResults,
2001
- recencyFilter,
2002
- domainFilter,
2003
- signal,
2004
- extensionContext: ctx,
2005
- });
2068
+ return runWithProxy(typeof params.proxy === "string" ? params.proxy : undefined, async () => {
2069
+ const claim = typeof params.claim === "string" ? params.claim.trim() : "";
2070
+ if (!claim) {
2071
+ return { content: [{ type: "text", text: "Error: 'claim' is required." }], details: { error: "Missing claim" } };
2072
+ }
2073
+ const requestedQueries = Array.isArray(params.queries)
2074
+ ? params.queries.filter((query) => typeof query === "string").map((query) => query.trim()).filter(Boolean)
2075
+ : [];
2076
+ const queries = (requestedQueries.length > 0 ? requestedQueries : [claim]).slice(0, 8);
2077
+ const numResults = typeof params.numResults === "number" && Number.isFinite(params.numResults)
2078
+ ? Math.min(20, Math.max(1, Math.floor(params.numResults)))
2079
+ : 5;
2080
+ const domainFilter = Array.isArray(params.domainFilter)
2081
+ ? params.domainFilter.filter((domain) => typeof domain === "string")
2082
+ : undefined;
2083
+ const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
2084
+ const resultsByUrl = new Map();
2085
+ const summaries = [];
2086
+ const errors = [];
2087
+ let provider;
2088
+ for (const query of queries) {
2006
2089
  if (signal?.aborted)
2007
2090
  break;
2008
- provider ??= response.provider;
2009
- if (response.answer)
2010
- summaries.push(`${query}: ${response.answer}`);
2011
- for (const result of response.results) {
2012
- if (!resultsByUrl.has(result.url))
2013
- resultsByUrl.set(result.url, result);
2091
+ try {
2092
+ const response = await search(query, {
2093
+ provider: resolveRequestedProvider(params.provider),
2094
+ numResults,
2095
+ recencyFilter,
2096
+ domainFilter,
2097
+ signal,
2098
+ extensionContext: ctx,
2099
+ });
2100
+ if (signal?.aborted)
2101
+ break;
2102
+ provider ??= response.provider;
2103
+ if (response.answer)
2104
+ summaries.push(`${query}: ${response.answer}`);
2105
+ for (const result of response.results) {
2106
+ if (!resultsByUrl.has(result.url))
2107
+ resultsByUrl.set(result.url, result);
2108
+ }
2109
+ }
2110
+ catch (err) {
2111
+ if (signal?.aborted || isAbortError(err))
2112
+ break;
2113
+ errors.push({ query, error: err instanceof Error ? err.message : String(err) });
2014
2114
  }
2015
2115
  }
2016
- catch (err) {
2017
- if (signal?.aborted || isAbortError(err))
2018
- break;
2019
- errors.push({ query, error: err instanceof Error ? err.message : String(err) });
2020
- }
2021
- }
2022
- const results = [...resultsByUrl.values()].slice(0, 20).map((result, index) => ({ ...result, rank: index + 1 }));
2023
- let fetched = [];
2024
- if (params.fetchContent && results.length > 0) {
2025
- const urls = results.slice(0, 5).map((result) => result.url);
2026
- try {
2027
- fetched = await fetchAllContent(urls, signal);
2116
+ const results = [...resultsByUrl.values()].slice(0, 20).map((result, index) => ({ ...result, rank: index + 1 }));
2117
+ let fetched = [];
2118
+ if (params.fetchContent && results.length > 0) {
2119
+ const urls = results.slice(0, 5).map((result) => result.url);
2120
+ try {
2121
+ fetched = await fetchAllContent(urls, signal, withRegisteredFetchOptions(undefined, registeredToolNames, typeof params.proxy === "string" ? params.proxy : undefined));
2122
+ }
2123
+ catch (err) {
2124
+ if (signal?.aborted || isAbortError(err))
2125
+ throw err;
2126
+ fetched = urls.map((url) => ({ url, title: "", content: "", error: err instanceof Error ? err.message : String(err) }));
2127
+ }
2028
2128
  }
2029
- catch (err) {
2030
- if (signal?.aborted || isAbortError(err))
2031
- throw err;
2032
- fetched = urls.map((url) => ({ url, title: "", content: "", error: err instanceof Error ? err.message : String(err) }));
2033
- }
2034
- }
2035
- const artifact = withClaimAssessment(buildResearchArtifact({
2036
- query: claim,
2037
- provider,
2038
- summary: summaries.length > 0 ? summaries.join("\n\n") : undefined,
2039
- results,
2040
- fetched,
2041
- recency: recencyFilter,
2042
- domainFilter,
2043
- }), [claim]);
2044
- if (errors.length > 0)
2045
- artifact.errors = errors;
2046
- storeResearchArtifact(artifact);
2047
- pi.appendEntry("web-search-results", {
2048
- id: artifact.id,
2049
- type: "research",
2050
- timestamp: artifact.timestamp,
2051
- artifact,
2129
+ const artifact = withClaimAssessment(buildResearchArtifact({
2130
+ query: claim,
2131
+ provider,
2132
+ summary: summaries.length > 0 ? summaries.join("\n\n") : undefined,
2133
+ results,
2134
+ fetched,
2135
+ recency: recencyFilter,
2136
+ domainFilter,
2137
+ }), [claim]);
2138
+ if (errors.length > 0)
2139
+ artifact.errors = errors;
2140
+ storeResearchArtifact(artifact);
2141
+ pi.appendEntry("web-search-results", {
2142
+ id: artifact.id,
2143
+ type: "research",
2144
+ timestamp: artifact.timestamp,
2145
+ artifact,
2146
+ });
2147
+ return {
2148
+ content: [{ type: "text", text: formatSourceCheckResult(artifact, getSearchContentEnabled ? toolNames.getSearchContent : null) }],
2149
+ details: { responseId: artifact.id, artifact, sourceCount: artifact.sources.length, passageCount: artifact.passages.length },
2150
+ };
2052
2151
  });
2053
- return {
2054
- content: [{ type: "text", text: formatSourceCheckResult(artifact, getSearchContentEnabled ? toolNames.getSearchContent : null) }],
2055
- details: { responseId: artifact.id, artifact, sourceCount: artifact.sources.length, passageCount: artifact.passages.length },
2056
- };
2057
2152
  },
2058
2153
  });
2059
2154
  if (fetchContentEnabled)
@@ -2075,7 +2170,7 @@ export default function (pi) {
2075
2170
  description: "Fetch mode: readable (default extraction), raw (exact textual HTTP body), or answer (answer prompt using only fetched content).",
2076
2171
  })),
2077
2172
  answerModel: Type.Optional(Type.String({
2078
- description: "Optional provider/model-id override for mode answer. Defaults to the current Pi model.",
2173
+ description: "Optional provider/model-id override for mode answer. Defaults to fetch.answerProvider + fetch.answerModel when configured, otherwise the current Pi model.",
2079
2174
  })),
2080
2175
  timestamp: Type.Optional(Type.String({
2081
2176
  description: "Extract video frame(s) at a timestamp or time range. Single: '1:23:45', '23:45', or '85' (seconds). Range: '23:41-25:00' extracts evenly-spaced frames across that span (default 6). Use frames with ranges to control density; single+frames uses a fixed 5s interval. YouTube requires yt-dlp + ffmpeg; local videos require ffmpeg. Use a range when you know the approximate area but not the exact moment — you'll get a contact sheet to visually identify the right frame.",
@@ -2091,6 +2186,9 @@ export default function (pi) {
2091
2186
  auth: Type.Optional(Type.Union([Type.String(), Type.Boolean()], {
2092
2187
  description: "Opt into an authFetch profile for local browser-cookie fetching. Use a profile name, or true only when exactly one profile exists.",
2093
2188
  })),
2189
+ proxy: Type.Optional(Type.String({
2190
+ description: "http(s) proxy URL (e.g. http://host:port) used for this fetch. Needed when the target is unreachable directly; localhost and NO_PROXY hosts always bypass the proxy. Empty string forces direct access.",
2191
+ })),
2094
2192
  }),
2095
2193
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
2096
2194
  let normalized;
@@ -2102,159 +2200,159 @@ export default function (pi) {
2102
2200
  return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
2103
2201
  }
2104
2202
  const { urlList, options } = normalized;
2105
- const mode = options.mode ?? "readable";
2106
- if (mode === "answer" && !options.prompt) {
2107
- return { content: [{ type: "text", text: "Error: mode answer requires prompt." }], details: { error: "mode answer requires prompt" } };
2108
- }
2109
- if (mode === "raw" && (options.forceClone === true || options.timestamp || options.frames || options.prompt || options.model || options.answerModel)) {
2110
- return { content: [{ type: "text", text: "Error: mode raw cannot be combined with forceClone, prompt, timestamp, frames, model, or answerModel." }], details: { error: "Incompatible raw mode options" } };
2111
- }
2112
- if (mode !== "answer" && options.answerModel) {
2113
- return { content: [{ type: "text", text: "Error: answerModel requires mode answer." }], details: { error: "answerModel requires mode answer" } };
2114
- }
2115
- if (mode === "answer" && options.model) {
2116
- return { content: [{ type: "text", text: "Error: use answerModel, not model, with mode answer." }], details: { error: "model is incompatible with mode answer" } };
2117
- }
2118
- if (mode === "answer" && options.auth !== undefined) {
2119
- return { content: [{ type: "text", text: "Error: auth cannot be combined with mode answer." }], details: { error: "auth cannot be combined with mode answer" } };
2120
- }
2121
- let authFetchProfile;
2122
- if (options.auth !== undefined) {
2123
- try {
2124
- authFetchProfile = resolveAuthFetchProfile(options.auth);
2203
+ return runWithProxy(options.proxy, async () => {
2204
+ const mode = options.mode ?? "readable";
2205
+ if (mode === "answer" && !options.prompt) {
2206
+ return { content: [{ type: "text", text: "Error: mode answer requires prompt." }], details: { error: "mode answer requires prompt" } };
2125
2207
  }
2126
- catch (err) {
2127
- const error = err instanceof Error ? err.message : String(err);
2128
- return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
2208
+ if (mode === "raw" && (options.forceClone === true || options.timestamp || options.frames || options.prompt || options.model || options.answerModel)) {
2209
+ return { content: [{ type: "text", text: "Error: mode raw cannot be combined with forceClone, prompt, timestamp, frames, model, or answerModel." }], details: { error: "Incompatible raw mode options" } };
2129
2210
  }
2130
- }
2131
- if (urlList.length === 0) {
2132
- return {
2133
- content: [{ type: "text", text: "Error: No URL provided." }],
2134
- details: { error: "No URL provided" },
2135
- };
2136
- }
2137
- onUpdate?.({
2138
- content: [{ type: "text", text: `Fetching ${urlList.length} URL(s)...` }],
2139
- details: { phase: "fetch", progress: 0 },
2140
- });
2141
- const { answerModel: _answerModel, auth: _auth, ...extractionOptions } = options;
2142
- const fetchOptions = mode === "answer"
2143
- ? (() => {
2144
- const { prompt: _prompt, ...rest } = extractionOptions;
2145
- return { ...rest, ...(authFetchProfile ? { authFetchProfile } : {}) };
2146
- })()
2147
- : { ...extractionOptions, ...(authFetchProfile ? { authFetchProfile } : {}) };
2148
- const fetchResults = await fetchAllContent(urlList, signal, fetchOptions);
2149
- const presentedResults = mode === "answer"
2150
- ? await Promise.all(fetchResults.map(async (result) => {
2151
- if (result.error)
2152
- return result;
2153
- if (result.thumbnail || result.mimeType?.startsWith("image/")) {
2154
- return { ...result, error: "Page answer requires textual fetched content" };
2155
- }
2211
+ if (mode !== "answer" && options.answerModel) {
2212
+ return { content: [{ type: "text", text: "Error: answerModel requires mode answer." }], details: { error: "answerModel requires mode answer" } };
2213
+ }
2214
+ if (mode === "answer" && options.model) {
2215
+ return { content: [{ type: "text", text: "Error: use answerModel, not model, with mode answer." }], details: { error: "model is incompatible with mode answer" } };
2216
+ }
2217
+ if (mode === "answer" && options.auth !== undefined) {
2218
+ return { content: [{ type: "text", text: "Error: auth cannot be combined with mode answer." }], details: { error: "auth cannot be combined with mode answer" } };
2219
+ }
2220
+ let authFetchProfile;
2221
+ if (options.auth !== undefined) {
2156
2222
  try {
2157
- const answer = await answerFromPage({
2158
- question: options.prompt,
2159
- pageText: result.content,
2160
- sourceUrl: result.url,
2161
- ...(options.answerModel ? { model: options.answerModel } : {}),
2162
- }, ctx, signal);
2163
- return { ...result, content: answer.text };
2223
+ authFetchProfile = resolveAuthFetchProfile(options.auth);
2164
2224
  }
2165
2225
  catch (err) {
2166
- return { ...result, error: `Page answer failed: ${err instanceof Error ? err.message : String(err)}` };
2226
+ const error = err instanceof Error ? err.message : String(err);
2227
+ return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
2167
2228
  }
2168
- }))
2169
- : fetchResults;
2170
- const successful = presentedResults.filter((r) => !r.error).length;
2171
- const totalChars = presentedResults.reduce((sum, r) => sum + r.content.length, 0);
2172
- const responseId = generateId();
2173
- const data = {
2174
- id: responseId,
2175
- type: "fetch",
2176
- timestamp: Date.now(),
2177
- urls: stripThumbnails(fetchResults),
2178
- };
2179
- const storedContent = storeFetchResult(pi, responseId, data, authFetchProfile);
2180
- // Single URL: return content directly (possibly truncated) with responseId
2181
- if (urlList.length === 1) {
2182
- const result = presentedResults[0];
2183
- if (result.error) {
2229
+ }
2230
+ if (urlList.length === 0) {
2184
2231
  return {
2185
- content: [{ type: "text", text: `Error: ${result.error}` }],
2186
- details: { urls: urlList, urlCount: 1, successful: 0, error: result.error, ...(storedContent ? { responseId } : {}), prompt: params.prompt, timestamp: params.timestamp, frames: params.frames },
2232
+ content: [{ type: "text", text: "Error: No URL provided." }],
2233
+ details: { error: "No URL provided" },
2187
2234
  };
2188
2235
  }
2189
- const fullLength = result.content.length;
2190
- const slice = initialContentSlice(result.content, getMaxInlineContentChars());
2191
- const truncated = slice.endOffset < fullLength;
2192
- let output = slice.text;
2193
- if (truncated) {
2194
- output += `\n\n---\nShowing ${slice.endOffset} of ${fullLength} chars, ${slice.shownBytes} of ${slice.totalBytes} bytes, and ${slice.shownLines} of ${slice.totalLines} lines. `;
2195
- output += storedContent
2196
- ? getSearchContentEnabled
2197
- ? `Use ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0, offset: ${slice.endOffset} }) for the next slice.`
2198
- : "Content retrieval is not registered."
2199
- : "Authenticated fetch cache is off; repeat the fetch to read more.";
2200
- }
2201
- const content = [];
2202
- if (result.frames?.length) {
2203
- for (const frame of result.frames) {
2204
- content.push({ type: "image", data: frame.data, mimeType: frame.mimeType });
2205
- content.push({ type: "text", text: `Frame at ${frame.timestamp}` });
2236
+ onUpdate?.({
2237
+ content: [{ type: "text", text: `Fetching ${urlList.length} URL(s)...` }],
2238
+ details: { phase: "fetch", progress: 0 },
2239
+ });
2240
+ const { answerModel: _answerModel, auth: _auth, ...extractionOptions } = options;
2241
+ const fetchOptions = mode === "answer"
2242
+ ? (() => {
2243
+ const { prompt: _prompt, ...rest } = extractionOptions;
2244
+ return { ...rest, ...(authFetchProfile ? { authFetchProfile } : {}) };
2245
+ })()
2246
+ : { ...extractionOptions, ...(authFetchProfile ? { authFetchProfile } : {}) };
2247
+ const fetchResults = await fetchAllContent(urlList, signal, withRegisteredFetchOptions(fetchOptions, registeredToolNames, options.proxy));
2248
+ const presentedResults = mode === "answer"
2249
+ ? await Promise.all(fetchResults.map(async (result) => {
2250
+ if (result.error)
2251
+ return result;
2252
+ if (result.thumbnail || result.mimeType?.startsWith("image/")) {
2253
+ return { ...result, error: "Page answer requires textual fetched content" };
2254
+ }
2255
+ try {
2256
+ const answer = await answerFromPage({
2257
+ question: options.prompt,
2258
+ pageText: result.content,
2259
+ sourceUrl: result.url,
2260
+ ...(options.answerModel ? { model: options.answerModel } : {}),
2261
+ }, ctx, signal);
2262
+ return { ...result, content: answer.text };
2263
+ }
2264
+ catch (err) {
2265
+ return { ...result, error: `Page answer failed: ${err instanceof Error ? err.message : String(err)}` };
2266
+ }
2267
+ }))
2268
+ : fetchResults;
2269
+ const successful = presentedResults.filter((r) => !r.error).length;
2270
+ const totalChars = presentedResults.reduce((sum, r) => sum + r.content.length, 0);
2271
+ const responseId = generateId();
2272
+ const data = {
2273
+ id: responseId,
2274
+ type: "fetch",
2275
+ timestamp: Date.now(),
2276
+ urls: stripThumbnails(fetchResults),
2277
+ };
2278
+ const storedContent = storeFetchResult(pi, responseId, data, authFetchProfile);
2279
+ if (urlList.length === 1) {
2280
+ const result = presentedResults[0];
2281
+ if (result.error) {
2282
+ return {
2283
+ content: [{ type: "text", text: `Error: ${result.error}` }],
2284
+ details: { urls: urlList, urlCount: 1, successful: 0, error: result.error, ...(storedContent ? { responseId } : {}), prompt: params.prompt, timestamp: params.timestamp, frames: params.frames },
2285
+ };
2286
+ }
2287
+ const fullLength = result.content.length;
2288
+ const slice = initialContentSlice(result.content, getMaxInlineContentChars());
2289
+ const truncated = slice.endOffset < fullLength;
2290
+ let output = slice.text;
2291
+ if (truncated) {
2292
+ output += `\n\n---\nShowing ${slice.endOffset} of ${fullLength} chars, ${slice.shownBytes} of ${slice.totalBytes} bytes, and ${slice.shownLines} of ${slice.totalLines} lines. `;
2293
+ output += storedContent
2294
+ ? getSearchContentEnabled
2295
+ ? `Use ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0, offset: ${slice.endOffset} }) for the next slice.`
2296
+ : "Content retrieval is not registered."
2297
+ : "Authenticated fetch cache is off; repeat the fetch to read more.";
2206
2298
  }
2299
+ const content = [];
2300
+ if (result.frames?.length) {
2301
+ for (const frame of result.frames) {
2302
+ content.push({ type: "image", data: frame.data, mimeType: frame.mimeType });
2303
+ content.push({ type: "text", text: `Frame at ${frame.timestamp}` });
2304
+ }
2305
+ }
2306
+ else if (result.thumbnail) {
2307
+ content.push({ type: "image", data: result.thumbnail.data, mimeType: result.thumbnail.mimeType });
2308
+ }
2309
+ content.push({ type: "text", text: output });
2310
+ const imageCount = (result.frames?.length ?? 0) + (result.thumbnail ? 1 : 0);
2311
+ return {
2312
+ content,
2313
+ details: {
2314
+ urls: urlList,
2315
+ urlCount: 1,
2316
+ successful: 1,
2317
+ totalChars: fullLength,
2318
+ title: result.title,
2319
+ ...(storedContent ? { responseId } : {}),
2320
+ truncated,
2321
+ hasImage: imageCount > 0,
2322
+ imageCount,
2323
+ prompt: params.prompt,
2324
+ timestamp: params.timestamp,
2325
+ frames: params.frames,
2326
+ duration: result.duration,
2327
+ mode,
2328
+ mimeType: result.mimeType,
2329
+ status: result.status,
2330
+ totalBytes: slice.totalBytes,
2331
+ totalLines: slice.totalLines,
2332
+ shownBytes: slice.shownBytes,
2333
+ shownLines: slice.shownLines,
2334
+ },
2335
+ };
2207
2336
  }
2208
- else if (result.thumbnail) {
2209
- content.push({ type: "image", data: result.thumbnail.data, mimeType: result.thumbnail.mimeType });
2337
+ let output = "## Fetched URLs\n\n";
2338
+ for (const { url, title, content, error } of presentedResults) {
2339
+ if (error) {
2340
+ output += `- ${url}: Error - ${error}\n`;
2341
+ }
2342
+ else {
2343
+ output += `- ${title || url} (${content.length} chars)\n`;
2344
+ }
2210
2345
  }
2211
- content.push({ type: "text", text: output });
2212
- const imageCount = (result.frames?.length ?? 0) + (result.thumbnail ? 1 : 0);
2346
+ output += storedContent
2347
+ ? getSearchContentEnabled
2348
+ ? `\n---\nUse ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0 }) to retrieve bounded content slices.`
2349
+ : "\n---\nContent retrieval is not registered."
2350
+ : "\n---\nAuthenticated fetch cache is off; repeat the fetch to read content.";
2213
2351
  return {
2214
- content,
2215
- details: {
2216
- urls: urlList,
2217
- urlCount: 1,
2218
- successful: 1,
2219
- totalChars: fullLength,
2220
- title: result.title,
2221
- ...(storedContent ? { responseId } : {}),
2222
- truncated,
2223
- hasImage: imageCount > 0,
2224
- imageCount,
2225
- prompt: params.prompt,
2226
- timestamp: params.timestamp,
2227
- frames: params.frames,
2228
- duration: result.duration,
2229
- mode,
2230
- mimeType: result.mimeType,
2231
- status: result.status,
2232
- totalBytes: slice.totalBytes,
2233
- totalLines: slice.totalLines,
2234
- shownBytes: slice.shownBytes,
2235
- shownLines: slice.shownLines,
2236
- },
2352
+ content: [{ type: "text", text: output }],
2353
+ details: { urls: urlList, urlCount: urlList.length, successful, totalChars, ...(storedContent ? { responseId } : {}) },
2237
2354
  };
2238
- }
2239
- // Multi-URL: existing behavior (summary + responseId)
2240
- let output = "## Fetched URLs\n\n";
2241
- for (const { url, title, content, error } of presentedResults) {
2242
- if (error) {
2243
- output += `- ${url}: Error - ${error}\n`;
2244
- }
2245
- else {
2246
- output += `- ${title || url} (${content.length} chars)\n`;
2247
- }
2248
- }
2249
- output += storedContent
2250
- ? getSearchContentEnabled
2251
- ? `\n---\nUse ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0 }) to retrieve bounded content slices.`
2252
- : "\n---\nContent retrieval is not registered."
2253
- : "\n---\nAuthenticated fetch cache is off; repeat the fetch to read content.";
2254
- return {
2255
- content: [{ type: "text", text: output }],
2256
- details: { urls: urlList, urlCount: urlList.length, successful, totalChars, ...(storedContent ? { responseId } : {}) },
2257
- };
2355
+ });
2258
2356
  },
2259
2357
  renderCall(args, theme) {
2260
2358
  const { urlList, options } = normalizeFetchContentParams(args);
@@ -2385,23 +2483,16 @@ export default function (pi) {
2385
2483
  queryIndex: Type.Optional(Type.Integer({ minimum: 0, description: "Get content for query at index" })),
2386
2484
  url: Type.Optional(Type.String({ description: "Get content for this URL" })),
2387
2485
  urlIndex: Type.Optional(Type.Integer({ minimum: 0, description: "Get content for URL at index" })),
2388
- offset: Type.Optional(Type.Integer({ minimum: 0, description: "Character offset for fetched URL content slices (default 0). Cannot be combined with findText." })),
2389
- limit: Type.Optional(Type.Integer({ minimum: 1, maximum: maxInlineContentChars, description: "Maximum characters to return for fetched URL content slices (default and max are set by maxInlineContentChars). Cannot be combined with findText." })),
2486
+ offset: Type.Optional(Type.Integer({ minimum: 0, description: "Character offset for fetched URL content slices (default 0). Ignored when findText is supplied." })),
2487
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: maxInlineContentChars, description: "Maximum characters to return for fetched URL content slices (default and max are set by maxInlineContentChars). Ignored when findText is supplied." })),
2390
2488
  findText: Type.Optional(Type.Union([
2391
2489
  Type.String({ minLength: 1, maxLength: 500 }),
2392
2490
  Type.Array(Type.String({ minLength: 1, maxLength: 500 }), { minItems: 1, maxItems: 10 }),
2393
- ], { description: "Text or texts to find in the selected stored content. Cannot be combined with offset or limit." })),
2491
+ ], { description: "Text or texts to find in the selected stored content. When supplied, offset and limit are ignored." })),
2394
2492
  findMode: Type.Optional(StringEnum(["exact", "case-insensitive", "fuzzy"], { description: "Matching mode for findText (default: case-insensitive). Requires findText." })),
2395
2493
  }),
2396
- async execute(_toolCallId, params) {
2397
- if (params.findText !== undefined && (params.offset !== undefined || params.limit !== undefined)) {
2398
- const offset = formatInputValue(params.offset);
2399
- const limit = formatInputValue(params.limit);
2400
- return {
2401
- content: [{ type: "text", text: `findText cannot be combined with offset or limit. Received offset=${offset}, limit=${limit}; omit offset and limit when using findText.` }],
2402
- details: { error: "Incompatible find options" },
2403
- };
2404
- }
2494
+ async execute(_toolCallId, rawParams) {
2495
+ const params = normalizeGetSearchContentParams(rawParams);
2405
2496
  if (params.findMode !== undefined && params.findText === undefined) {
2406
2497
  return {
2407
2498
  content: [{ type: "text", text: `findMode ${formatInputValue(params.findMode)} requires findText; provide findText or omit findMode.` }],
@@ -2424,6 +2515,23 @@ export default function (pi) {
2424
2515
  };
2425
2516
  }
2426
2517
  const serialized = JSON.stringify(artifact, null, 2);
2518
+ if (params.findText !== undefined) {
2519
+ try {
2520
+ const found = findContent(serialized, normalizeFindQueries(params.findText), params.findMode ?? "case-insensitive");
2521
+ const { text, ...findDetails } = found;
2522
+ return {
2523
+ content: [{ type: "text", text }],
2524
+ details: { responseId: artifact.id, type: "research", contentLength: serialized.length, findMode: params.findMode ?? "case-insensitive", ...findDetails },
2525
+ };
2526
+ }
2527
+ catch (err) {
2528
+ const error = err instanceof Error ? err.message : String(err);
2529
+ return {
2530
+ content: [{ type: "text", text: `Unable to find ${formatInputValue(params.findText)} in research artifact for responseId ${formatInputValue(params.responseId)}: ${error}. Check findText and use a supported findMode.` }],
2531
+ details: { error, responseId: params.responseId, type: "research" },
2532
+ };
2533
+ }
2534
+ }
2427
2535
  const offset = params.offset ?? 0;
2428
2536
  const limit = params.limit ?? maxInlineContentChars;
2429
2537
  if (!Number.isInteger(offset) || offset < 0) {
@@ -2490,7 +2598,7 @@ export default function (pi) {
2490
2598
  const fullResults = formatFullResults(queryData);
2491
2599
  if (params.findText !== undefined) {
2492
2600
  try {
2493
- const found = findContent(fullResults, normalizeFindQueries(params.findText), (params.findMode ?? "case-insensitive"));
2601
+ const found = findContent(fullResults, normalizeFindQueries(params.findText), params.findMode ?? "case-insensitive");
2494
2602
  const { text, ...findDetails } = found;
2495
2603
  return {
2496
2604
  content: [{ type: "text", text }],
@@ -2550,7 +2658,7 @@ export default function (pi) {
2550
2658
  }
2551
2659
  if (params.findText !== undefined) {
2552
2660
  try {
2553
- const found = findContent(urlData.content, normalizeFindQueries(params.findText), (params.findMode ?? "case-insensitive"));
2661
+ const found = findContent(urlData.content, normalizeFindQueries(params.findText), params.findMode ?? "case-insensitive");
2554
2662
  const { text, ...findDetails } = found;
2555
2663
  return {
2556
2664
  content: [{ type: "text", text: `# ${urlData.title || urlData.url}\n\n${text}` }],
@@ -2994,14 +3102,16 @@ export default function (pi) {
2994
3102
  const cookies = await isGeminiWebAvailable();
2995
3103
  if (!cookies) {
2996
3104
  const diagnostic = getGeminiWebAvailabilityDiagnostic();
3105
+ const diagnosticDetails = getGeminiWebAvailabilityDiagnosticDetails();
3106
+ const attempted = formatCookieAttempts(diagnosticDetails?.attempts ?? []);
2997
3107
  const text = diagnostic
2998
- ? `Gemini Web is unavailable: ${diagnostic}`
3108
+ ? `Gemini Web is unavailable: ${diagnostic}${attempted ? ` Attempted browser profiles: ${attempted}.` : ""}`
2999
3109
  : "Gemini Web is unavailable. Sign into gemini.google.com in a supported Chromium-based browser.";
3000
3110
  pi.sendMessage({
3001
3111
  customType: "google-account",
3002
3112
  content: [{ type: "text", text }],
3003
3113
  display: true,
3004
- details: { available: false, cookieAccessAllowed: true, diagnostic },
3114
+ details: { available: false, cookieAccessAllowed: true, diagnostic, cookieDiagnostic: diagnosticDetails },
3005
3115
  }, { triggerTurn: true, deliverAs: "followUp" });
3006
3116
  return;
3007
3117
  }
@@ -3017,6 +3127,9 @@ export default function (pi) {
3017
3127
  }, { triggerTurn: true, deliverAs: "followUp" });
3018
3128
  },
3019
3129
  });
3130
+ function formatCookieAttempts(attempts) {
3131
+ return attempts.map(({ browser, profile, status }) => `${browser}/${profile} (${status})`).join(", ");
3132
+ }
3020
3133
  if (isCommandEnabled(initConfig, "search"))
3021
3134
  pi.registerCommand("search", {
3022
3135
  description: "Browse stored web search results",