@ashx-j/lunr-dev 0.2.13-dev.8.1 → 0.2.16-dev.9.1

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 (339) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/dist/builtin-extensions/ashxj-tui.d.ts +14 -0
  3. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  4. package/dist/builtin-extensions/ashxj-tui.js +37 -3
  5. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  6. package/dist/builtin-extensions/index.d.ts +1 -15
  7. package/dist/builtin-extensions/index.d.ts.map +1 -1
  8. package/dist/builtin-extensions/index.js +7 -21
  9. package/dist/builtin-extensions/index.js.map +1 -1
  10. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
  11. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +6 -2
  12. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
  13. package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts +4 -0
  14. package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
  15. package/dist/builtin-extensions/pi-lsp-extension/src/index.js +163 -169
  16. package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
  17. package/dist/builtin-extensions/pi-lsp-extension/src/runtime.d.ts +46 -0
  18. package/dist/builtin-extensions/pi-lsp-extension/src/runtime.d.ts.map +1 -0
  19. package/dist/builtin-extensions/pi-lsp-extension/src/runtime.js +109 -0
  20. package/dist/builtin-extensions/pi-lsp-extension/src/runtime.js.map +1 -0
  21. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts +7 -0
  22. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -0
  23. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +211 -0
  24. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -0
  25. package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts +0 -5
  26. package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts.map +1 -1
  27. package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js +0 -202
  28. package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js.map +1 -1
  29. package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
  30. package/dist/builtin-extensions/pi-mcp-adapter/index.js +251 -94
  31. package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
  32. package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts +4 -1
  33. package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
  34. package/dist/builtin-extensions/pi-mcp-adapter/init.js +10 -3
  35. package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
  36. package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.d.ts.map +1 -1
  37. package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.js +6 -1
  38. package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.js.map +1 -1
  39. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
  40. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +2 -1
  41. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
  42. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts +1 -1
  43. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
  44. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +1 -1
  45. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
  46. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +1 -1
  47. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  48. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +38 -14
  49. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  50. package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.d.ts.map +1 -1
  51. package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.js +1 -1
  52. package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.js.map +1 -1
  53. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +1 -1
  54. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
  55. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +1 -1
  56. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
  57. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +1 -1
  58. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  59. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +2 -1
  60. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  61. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts +1 -1
  62. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
  63. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +2 -1
  64. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
  65. package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
  66. package/dist/builtin-extensions/pi-web-access/extract.js +105 -54
  67. package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
  68. package/dist/builtin-extensions/pi-web-access/gemini-search.d.ts +1 -1
  69. package/dist/builtin-extensions/pi-web-access/gemini-search.d.ts.map +1 -1
  70. package/dist/builtin-extensions/pi-web-access/gemini-search.js +82 -65
  71. package/dist/builtin-extensions/pi-web-access/gemini-search.js.map +1 -1
  72. package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
  73. package/dist/builtin-extensions/pi-web-access/github-extract.js +2 -0
  74. package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
  75. package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
  76. package/dist/builtin-extensions/pi-web-access/index.js +167 -77
  77. package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
  78. package/dist/builtin-extensions/pi-web-access/lazy.d.ts +18 -0
  79. package/dist/builtin-extensions/pi-web-access/lazy.d.ts.map +1 -0
  80. package/dist/builtin-extensions/pi-web-access/lazy.js +19 -0
  81. package/dist/builtin-extensions/pi-web-access/lazy.js.map +1 -0
  82. package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +6 -0
  83. package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -0
  84. package/dist/builtin-extensions/pi-web-access/session-cleanup.js +17 -0
  85. package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -0
  86. package/dist/catalog/publication.json +12 -12
  87. package/dist/cli-runtime.d.ts +3 -0
  88. package/dist/cli-runtime.d.ts.map +1 -0
  89. package/dist/cli-runtime.js +73 -0
  90. package/dist/cli-runtime.js.map +1 -0
  91. package/dist/cli.d.ts.map +1 -1
  92. package/dist/cli.js +14 -53
  93. package/dist/cli.js.map +1 -1
  94. package/dist/config.d.ts +1 -0
  95. package/dist/config.d.ts.map +1 -1
  96. package/dist/config.js +1 -0
  97. package/dist/config.js.map +1 -1
  98. package/dist/core/agent-session.d.ts +3 -0
  99. package/dist/core/agent-session.d.ts.map +1 -1
  100. package/dist/core/agent-session.js +56 -12
  101. package/dist/core/agent-session.js.map +1 -1
  102. package/dist/core/compaction/compaction.d.ts.map +1 -1
  103. package/dist/core/compaction/compaction.js +10 -5
  104. package/dist/core/compaction/compaction.js.map +1 -1
  105. package/dist/core/extensions/host-static.d.ts.map +1 -1
  106. package/dist/core/extensions/host-static.js +34 -12
  107. package/dist/core/extensions/host-static.js.map +1 -1
  108. package/dist/core/extensions/loader.d.ts +1 -2
  109. package/dist/core/extensions/loader.d.ts.map +1 -1
  110. package/dist/core/extensions/loader.js +10 -64
  111. package/dist/core/extensions/loader.js.map +1 -1
  112. package/dist/core/extensions/types.d.ts +2 -2
  113. package/dist/core/extensions/types.d.ts.map +1 -1
  114. package/dist/core/extensions/types.js.map +1 -1
  115. package/dist/core/footer-data-provider.d.ts.map +1 -1
  116. package/dist/core/footer-data-provider.js +2 -0
  117. package/dist/core/footer-data-provider.js.map +1 -1
  118. package/dist/core/permissions.d.ts +0 -2
  119. package/dist/core/permissions.d.ts.map +1 -1
  120. package/dist/core/permissions.js +10 -18
  121. package/dist/core/permissions.js.map +1 -1
  122. package/dist/core/sdk.d.ts +1 -1
  123. package/dist/core/sdk.d.ts.map +1 -1
  124. package/dist/core/sdk.js +1 -1
  125. package/dist/core/sdk.js.map +1 -1
  126. package/dist/core/slash-commands.d.ts.map +1 -1
  127. package/dist/core/slash-commands.js +0 -1
  128. package/dist/core/slash-commands.js.map +1 -1
  129. package/dist/core/timings.d.ts +2 -1
  130. package/dist/core/timings.d.ts.map +1 -1
  131. package/dist/core/timings.js +17 -2
  132. package/dist/core/timings.js.map +1 -1
  133. package/dist/gateway/commands.d.ts +1 -1
  134. package/dist/gateway/commands.d.ts.map +1 -1
  135. package/dist/gateway/commands.js +1 -18
  136. package/dist/gateway/commands.js.map +1 -1
  137. package/dist/main.d.ts +2 -2
  138. package/dist/main.d.ts.map +1 -1
  139. package/dist/main.js +57 -48
  140. package/dist/main.js.map +1 -1
  141. package/dist/modes/interactive/interactive-mode.d.ts +12 -21
  142. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  143. package/dist/modes/interactive/interactive-mode.js +120 -144
  144. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  145. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  146. package/dist/modes/interactive/theme/theme.js +95 -84
  147. package/dist/modes/interactive/theme/theme.js.map +1 -1
  148. package/dist/node-runtime/agent-session-services-IFJMSX7Z.js +36 -0
  149. package/dist/node-runtime/benchmark-request-UC7PD3V3.js +57 -0
  150. package/dist/node-runtime/brave-UWDA3I22.js +143 -0
  151. package/dist/node-runtime/builtin-extensions-LYZ3BFWQ.js +28 -0
  152. package/dist/node-runtime/chunk-2QKLKMKC.js +173 -0
  153. package/dist/node-runtime/chunk-3GHXVUAK.js +21 -0
  154. package/dist/node-runtime/chunk-3RCWXWAY.js +466 -0
  155. package/dist/node-runtime/chunk-3WFAEVKS.js +115 -0
  156. package/dist/node-runtime/chunk-3X3KZKMI.js +49 -0
  157. package/dist/node-runtime/chunk-4HXHDDCF.js +16 -0
  158. package/dist/node-runtime/chunk-4TLO4PO2.js +3427 -0
  159. package/dist/node-runtime/chunk-4ZWGEEAS.js +69 -0
  160. package/dist/node-runtime/chunk-53YJI7JU.js +137 -0
  161. package/dist/node-runtime/chunk-5P7PZMRU.js +610 -0
  162. package/dist/node-runtime/chunk-7N3YHTYF.js +26 -0
  163. package/dist/node-runtime/chunk-7NBZ5P7I.js +24 -0
  164. package/dist/node-runtime/chunk-A2KQXUJD.js +105 -0
  165. package/dist/node-runtime/chunk-AD6VXAO7.js +342 -0
  166. package/dist/node-runtime/chunk-AHSTJBIB.js +64 -0
  167. package/dist/node-runtime/chunk-AJX357CL.js +20211 -0
  168. package/dist/node-runtime/chunk-ARC4KOTI.js +11178 -0
  169. package/dist/node-runtime/chunk-AUXVGBO4.js +19 -0
  170. package/dist/node-runtime/chunk-AY6CAJSL.js +30 -0
  171. package/dist/node-runtime/chunk-BHO7O6E5.js +296 -0
  172. package/dist/node-runtime/chunk-BI3DHYCN.js +8845 -0
  173. package/dist/node-runtime/chunk-BR7YDUKA.js +109 -0
  174. package/dist/node-runtime/chunk-BRJX2PRC.js +53 -0
  175. package/dist/node-runtime/chunk-C4O2RSQQ.js +37 -0
  176. package/dist/node-runtime/chunk-CCZJ3PYY.js +17 -0
  177. package/dist/node-runtime/chunk-CDFZEWPU.js +8388 -0
  178. package/dist/node-runtime/chunk-CGBXPL3E.js +1171 -0
  179. package/dist/node-runtime/chunk-CJECDQKP.js +19 -0
  180. package/dist/node-runtime/chunk-DAPBPCBP.js +53 -0
  181. package/dist/node-runtime/chunk-DDGMHUOW.js +305 -0
  182. package/dist/node-runtime/chunk-DMMTJQMQ.js +43 -0
  183. package/dist/node-runtime/chunk-DVGWF6A2.js +1180 -0
  184. package/dist/node-runtime/chunk-ECD5L7YI.js +1369 -0
  185. package/dist/node-runtime/chunk-EDBJNRSM.js +46 -0
  186. package/dist/node-runtime/chunk-F4VQFJI2.js +1269 -0
  187. package/dist/node-runtime/chunk-FVDOXRUD.js +96 -0
  188. package/dist/node-runtime/chunk-GZQHMEWL.js +126 -0
  189. package/dist/node-runtime/chunk-H2EOZYMP.js +57 -0
  190. package/dist/node-runtime/chunk-HQCKFM2O.js +56 -0
  191. package/dist/node-runtime/chunk-HYHJIRDW.js +19 -0
  192. package/dist/node-runtime/chunk-I6R76AG7.js +22 -0
  193. package/dist/node-runtime/chunk-IPA627GP.js +296 -0
  194. package/dist/node-runtime/chunk-JHYKXIMM.js +378 -0
  195. package/dist/node-runtime/chunk-KKSIVROT.js +88 -0
  196. package/dist/node-runtime/chunk-LZAW4ZX3.js +633 -0
  197. package/dist/node-runtime/chunk-M3LZLQW2.js +98 -0
  198. package/dist/node-runtime/chunk-MGWCVY6Y.js +47 -0
  199. package/dist/node-runtime/chunk-MJQDX3BA.js +72 -0
  200. package/dist/node-runtime/chunk-MKKH27PH.js +8956 -0
  201. package/dist/node-runtime/chunk-MX3GJNXJ.js +986 -0
  202. package/dist/node-runtime/chunk-N77MY65L.js +966 -0
  203. package/dist/node-runtime/chunk-NUIFPZWX.js +209 -0
  204. package/dist/node-runtime/chunk-OBMWP3I5.js +68 -0
  205. package/dist/node-runtime/chunk-OR4L3IB2.js +42 -0
  206. package/dist/node-runtime/chunk-PBC2Y6GS.js +903 -0
  207. package/dist/node-runtime/chunk-PCGNAY2O.js +1324 -0
  208. package/dist/node-runtime/chunk-PCIGF2XK.js +250 -0
  209. package/dist/node-runtime/chunk-PGAJ5MO5.js +7558 -0
  210. package/dist/node-runtime/chunk-PL6JKXWI.js +3622 -0
  211. package/dist/node-runtime/chunk-PVCDKGR6.js +55 -0
  212. package/dist/node-runtime/chunk-Q4Q6D4IY.js +364 -0
  213. package/dist/node-runtime/chunk-Q5ZU2VRT.js +83 -0
  214. package/dist/node-runtime/chunk-QFWG2RVL.js +5477 -0
  215. package/dist/node-runtime/chunk-QH42CDX3.js +9451 -0
  216. package/dist/node-runtime/chunk-RQ2Q5IYR.js +72 -0
  217. package/dist/node-runtime/chunk-RQ5DNUNG.js +429 -0
  218. package/dist/node-runtime/chunk-RRIZE6QQ.js +17 -0
  219. package/dist/node-runtime/chunk-SN6G4OU6.js +4753 -0
  220. package/dist/node-runtime/chunk-SWSN6HAB.js +53 -0
  221. package/dist/node-runtime/chunk-TQNSPERU.js +949 -0
  222. package/dist/node-runtime/chunk-VRB2IYGG.js +53 -0
  223. package/dist/node-runtime/chunk-VSELYKWU.js +772 -0
  224. package/dist/node-runtime/chunk-W3CVH5RX.js +62 -0
  225. package/dist/node-runtime/chunk-WJVFOIB5.js +24921 -0
  226. package/dist/node-runtime/chunk-WOVTOV5P.js +5653 -0
  227. package/dist/node-runtime/chunk-XBWPBZBN.js +125 -0
  228. package/dist/node-runtime/chunk-XTHQ7XR7.js +509 -0
  229. package/dist/node-runtime/chunk-YF63P5ZT.js +864 -0
  230. package/dist/node-runtime/chunk-YSKSB3BJ.js +295 -0
  231. package/dist/node-runtime/chunk-ZCHMJ34K.js +130 -0
  232. package/dist/node-runtime/chunk-ZKMFUD3Q.js +2834 -0
  233. package/dist/node-runtime/cli-runtime.js +81 -0
  234. package/dist/node-runtime/clipboard-OZ63BBC6.js +12 -0
  235. package/dist/node-runtime/clipboard-image-7QR6GXVA.js +13 -0
  236. package/dist/node-runtime/command-L3LAHDIH.js +13 -0
  237. package/dist/node-runtime/commands-RL5YBW5I.js +291 -0
  238. package/dist/node-runtime/config-FMEOZSWE.js +77 -0
  239. package/dist/node-runtime/curator-server-XCM43VEF.js +3816 -0
  240. package/dist/node-runtime/customize-XEXL2DGQ.js +12 -0
  241. package/dist/node-runtime/direct-tool-executor-JVDXTO7A.js +188 -0
  242. package/dist/node-runtime/exa-LL64QZR2.js +329 -0
  243. package/dist/node-runtime/export-html-KR26FOUZ.js +150 -0
  244. package/dist/node-runtime/extension-input-RV5N6APL.js +14 -0
  245. package/dist/node-runtime/extension-selector-CPD5LWDD.js +14 -0
  246. package/dist/node-runtime/extract-XLQSJN4V.js +17 -0
  247. package/dist/node-runtime/file-sync-HCOP2QVF.js +117 -0
  248. package/dist/node-runtime/gateway-X2TI5X7F.js +2484 -0
  249. package/dist/node-runtime/gemini-api-QE7ANVDZ.js +29 -0
  250. package/dist/node-runtime/gemini-search-U7SUMPQA.js +330 -0
  251. package/dist/node-runtime/gemini-url-context-PXWCBQHK.js +103 -0
  252. package/dist/node-runtime/gemini-web-MM446SEH.js +14 -0
  253. package/dist/node-runtime/github-extract-F64RZOEA.js +619 -0
  254. package/dist/node-runtime/goal-CZQ7FFQJ.js +1831 -0
  255. package/dist/node-runtime/index.js +859 -0
  256. package/dist/node-runtime/init-NJV2T55N.js +28 -0
  257. package/dist/node-runtime/install-cli-TH7IJJQQ.js +308 -0
  258. package/dist/node-runtime/interactive-mode-KOM35YF2.js +56 -0
  259. package/dist/node-runtime/interactive-view-U6OILKF3.js +249 -0
  260. package/dist/node-runtime/lsp-manager-3ASUSO4T.js +1104 -0
  261. package/dist/node-runtime/lunr-cron-YJ5UHYRD.js +268 -0
  262. package/dist/node-runtime/main.js +54 -0
  263. package/dist/node-runtime/mcp-auth-flow-QIXHW6QN.js +32 -0
  264. package/dist/node-runtime/mcp-panel-IIGMXQGP.js +501 -0
  265. package/dist/node-runtime/mcp-setup-panel-FEK7663I.js +355 -0
  266. package/dist/node-runtime/memory-cap-3S4XRLNP.js +20 -0
  267. package/dist/node-runtime/model-tiers-NJLHUEGK.js +20 -0
  268. package/dist/node-runtime/openai-search-B3MSNCQA.js +288 -0
  269. package/dist/node-runtime/package-manager-cli-TZZHJCVI.js +749 -0
  270. package/dist/node-runtime/parallel-AI2MNSCU.js +265 -0
  271. package/dist/node-runtime/parser-manager-TQHWYDH7.js +152 -0
  272. package/dist/node-runtime/pdf-extract-IPE6H4HP.js +57 -0
  273. package/dist/node-runtime/perplexity-QVJW3I35.js +12 -0
  274. package/dist/node-runtime/pi-intercom-EEMBGQID.js +2215 -0
  275. package/dist/node-runtime/pi-mcp-adapter-QFB37AVN.js +422 -0
  276. package/dist/node-runtime/pi-ollama-cloud-4VZRMULP.js +1440 -0
  277. package/dist/node-runtime/pi-prompt-template-model-4QT4LLXF.js +3946 -0
  278. package/dist/node-runtime/pi-subagents-7XJ4H53P.js +6023 -0
  279. package/dist/node-runtime/pi-web-access-DPGAVKOA.js +1864 -0
  280. package/dist/node-runtime/print-mode-VWIRFDAC.js +12 -0
  281. package/dist/node-runtime/proxy-modes-HEEFK4KC.js +685 -0
  282. package/dist/node-runtime/rpc-entry.js +62 -0
  283. package/dist/node-runtime/rpc-mode-SJNSOA4G.js +13 -0
  284. package/dist/node-runtime/runtime-bridges-PPHNBFWL.js +14 -0
  285. package/dist/node-runtime/session-manager-MQNBYACL.js +35 -0
  286. package/dist/node-runtime/session-selector-5AZLJS6U.js +14 -0
  287. package/dist/node-runtime/settings-manager-5LF2VQWC.js +21 -0
  288. package/dist/node-runtime/src-Y7JSCK64.js +2407 -0
  289. package/dist/node-runtime/subagent-executor-5RNDEPIJ.js +8483 -0
  290. package/dist/node-runtime/summary-review-YSMXKITO.js +228 -0
  291. package/dist/node-runtime/tavily-WEZFVFP7.js +154 -0
  292. package/dist/node-runtime/timings-UAD5BEAS.js +16 -0
  293. package/dist/node-runtime/tool-renderer-MV5O452K.js +204 -0
  294. package/dist/node-runtime/update-cli-A253SZ7P.js +73 -0
  295. package/dist/node-runtime/v4-5MNC3H7G.js +424 -0
  296. package/dist/node-runtime/video-extract-OYT5WKS5.js +304 -0
  297. package/dist/node-runtime/workspace-index-6DBXSTJ6.js +163 -0
  298. package/dist/node-runtime/youtube-extract-TQYKGFGS.js +264 -0
  299. package/dist/startup/benchmark-request.d.ts +3 -0
  300. package/dist/startup/benchmark-request.d.ts.map +1 -0
  301. package/dist/startup/benchmark-request.js +65 -0
  302. package/dist/startup/benchmark-request.js.map +1 -0
  303. package/dist/startup/interactive-view.d.ts +55 -0
  304. package/dist/startup/interactive-view.d.ts.map +1 -0
  305. package/dist/startup/interactive-view.js +332 -0
  306. package/dist/startup/interactive-view.js.map +1 -0
  307. package/dist/startup/launch-routing.d.ts.map +1 -1
  308. package/dist/startup/launch-routing.js +9 -10
  309. package/dist/startup/launch-routing.js.map +1 -1
  310. package/dist/startup/startup-milestones.d.ts +1 -1
  311. package/dist/startup/startup-milestones.d.ts.map +1 -1
  312. package/dist/startup/startup-milestones.js +3 -2
  313. package/dist/startup/startup-milestones.js.map +1 -1
  314. package/dist/utils/await-with-abort.d.ts +2 -0
  315. package/dist/utils/await-with-abort.d.ts.map +1 -0
  316. package/dist/utils/await-with-abort.js +19 -0
  317. package/dist/utils/await-with-abort.js.map +1 -0
  318. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  319. package/dist/utils/syntax-highlight.js +10 -4
  320. package/dist/utils/syntax-highlight.js.map +1 -1
  321. package/docs/compaction.md +5 -3
  322. package/docs/development.md +5 -1
  323. package/docs/features.md +0 -1
  324. package/docs/interactive-startup.md +98 -0
  325. package/docs/usage.md +0 -1
  326. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  327. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  328. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  329. package/examples/extensions/gondolin/package-lock.json +2 -2
  330. package/examples/extensions/gondolin/package.json +1 -1
  331. package/examples/extensions/sandbox/package-lock.json +2 -2
  332. package/examples/extensions/sandbox/package.json +1 -1
  333. package/examples/extensions/with-deps/package-lock.json +2 -2
  334. package/examples/extensions/with-deps/package.json +1 -1
  335. package/package.json +20 -20
  336. package/dist/core/swarm.d.ts +0 -23
  337. package/dist/core/swarm.d.ts.map +0 -1
  338. package/dist/core/swarm.js +0 -45
  339. package/dist/core/swarm.js.map +0 -1
@@ -0,0 +1,1864 @@
1
+ import { createRequire as __lunrRequire } from "node:module"; const require = __lunrRequire(import.meta.url);
2
+ import {
3
+ runSessionCleanups
4
+ } from "./chunk-AUXVGBO4.js";
5
+ import {
6
+ isBrowserCookieAccessAllowed
7
+ } from "./chunk-DMMTJQMQ.js";
8
+ import {
9
+ loadBrave,
10
+ loadCuratorServer,
11
+ loadExa,
12
+ loadExtract,
13
+ loadGeminiApi,
14
+ loadGeminiSearch,
15
+ loadGeminiWeb,
16
+ loadOpenAISearch,
17
+ loadParallel,
18
+ loadPerplexity,
19
+ loadSummaryReview,
20
+ loadTavily
21
+ } from "./chunk-7NBZ5P7I.js";
22
+ import {
23
+ activityMonitor
24
+ } from "./chunk-DAPBPCBP.js";
25
+ import {
26
+ getWebSearchConfigDir,
27
+ getWebSearchConfigPath
28
+ } from "./chunk-VRB2IYGG.js";
29
+ import {
30
+ loadEnabledModelPatterns,
31
+ modelMatchesEnabledPatterns
32
+ } from "./chunk-4ZWGEEAS.js";
33
+ import {
34
+ formatGroupedCall,
35
+ toolGroupTree,
36
+ toolStatusDotFromContext
37
+ } from "./chunk-GZQHMEWL.js";
38
+ import "./chunk-ZCHMJ34K.js";
39
+ import "./chunk-PCIGF2XK.js";
40
+ import "./chunk-Q4Q6D4IY.js";
41
+ import "./chunk-BRJX2PRC.js";
42
+
43
+ // packages/coding-agent/dist/builtin-extensions/pi-web-access/index.js
44
+ import { Box, Text, truncateToWidth } from "@ashx-j/lunr-tui";
45
+ import { Type } from "typebox";
46
+ import { StringEnum, complete } from "@ashx-j/lunr-ai/compat";
47
+
48
+ // packages/coding-agent/dist/builtin-extensions/pi-web-access/fetch-params.js
49
+ function normalizeFetchContentParams(params) {
50
+ let normalizedUrls = uniqueUrls(normalizeUrlArray(params.urls)), urlList = normalizedUrls.length > 0 ? normalizedUrls : normalizeSingleUrl(params.url), prompt = normalizeOptionalString(params.prompt), timestamp = normalizeOptionalString(params.timestamp), frames = normalizeOptionalInteger(params.frames), shouldIncludeFrames = frames !== void 0 && (timestamp !== void 0 || frames > 1);
51
+ return {
52
+ urlList,
53
+ options: {
54
+ forceClone: typeof params.forceClone == "boolean" ? params.forceClone : void 0,
55
+ prompt,
56
+ timestamp,
57
+ frames: shouldIncludeFrames ? frames : void 0,
58
+ model: normalizeOptionalString(params.model)
59
+ }
60
+ };
61
+ }
62
+ function normalizeUrlArray(value) {
63
+ return Array.isArray(value) ? value.flatMap(normalizeSingleUrl) : [];
64
+ }
65
+ function normalizeSingleUrl(value) {
66
+ if (typeof value != "string")
67
+ return [];
68
+ let trimmed = value.trim();
69
+ return trimmed ? [trimmed] : [];
70
+ }
71
+ function normalizeOptionalString(value) {
72
+ return typeof value != "string" ? void 0 : value.trim() || void 0;
73
+ }
74
+ function normalizeOptionalInteger(value) {
75
+ if (!(typeof value != "number" || !Number.isInteger(value) || value < 1))
76
+ return value;
77
+ }
78
+ function uniqueUrls(urls) {
79
+ return [...new Set(urls)];
80
+ }
81
+
82
+ // packages/coding-agent/dist/builtin-extensions/pi-web-access/storage.js
83
+ var storedResults = /* @__PURE__ */ new Map();
84
+ function generateId() {
85
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
86
+ }
87
+ function storeResult(id, data) {
88
+ storedResults.set(id, data);
89
+ }
90
+ function getResult(id) {
91
+ return storedResults.get(id) ?? null;
92
+ }
93
+ function getAllResults() {
94
+ return Array.from(storedResults.values());
95
+ }
96
+ function deleteResult(id) {
97
+ return storedResults.delete(id);
98
+ }
99
+ function clearResults() {
100
+ storedResults.clear();
101
+ }
102
+ function isValidStoredData(data) {
103
+ if (!data || typeof data != "object")
104
+ return !1;
105
+ let d = data;
106
+ return !(typeof d.id != "string" || !d.id || d.type !== "search" && d.type !== "fetch" || typeof d.timestamp != "number" || d.type === "search" && !Array.isArray(d.queries) || d.type === "fetch" && !Array.isArray(d.urls));
107
+ }
108
+ function restoreFromSession(ctx2) {
109
+ storedResults.clear();
110
+ let now = Date.now();
111
+ for (let entry of ctx2.sessionManager.getBranch())
112
+ if (entry.type === "custom" && entry.customType === "web-search-results") {
113
+ let data = entry.data;
114
+ isValidStoredData(data) && now - data.timestamp < 36e5 && storedResults.set(data.id, data);
115
+ }
116
+ }
117
+
118
+ // packages/coding-agent/dist/builtin-extensions/pi-web-access/index.js
119
+ import { randomUUID } from "node:crypto";
120
+ import { execFileSync } from "node:child_process";
121
+ import { createRequire } from "node:module";
122
+ import { platform } from "node:os";
123
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
124
+ import { join } from "node:path";
125
+
126
+ // packages/coding-agent/dist/builtin-extensions/pi-web-access/render-search-error.js
127
+ function truncate(text, max) {
128
+ return text.length > max ? text.slice(0, max - 1) + "\u2026" : text;
129
+ }
130
+ function buildSearchErrorPlan(details) {
131
+ if (!details || !details.error && !details.cancelled)
132
+ return null;
133
+ let headline = details.error ?? "Search cancelled.", queries = details.cancelledQueries ?? [], queryCount = typeof details.queryCount == "number" && details.queryCount > 0 ? details.queryCount : queries.length, done = queries.length, errored = queries.filter((q) => q.error).length, extras = details.extraLines ?? [];
134
+ if (!(details.cancelled === !0 || queries.length > 0 || extras.length > 0))
135
+ return { expanded: [headline], collapsed: [], expandHint: null };
136
+ let expanded = [headline, ""];
137
+ if (details.cancelled === !0 || queries.length > 0) {
138
+ let diag = [];
139
+ details.cancelled && diag.push(`cancel reason : ${details.cancelReason ?? "unknown"}`);
140
+ let browserLabel = details.browserConnected === void 0 ? "unknown" : details.browserConnected ? "connected" : "never connected";
141
+ diag.push(`browser : ${browserLabel}`), typeof details.lastHeartbeatAgeMs == "number" && Number.isFinite(details.lastHeartbeatAgeMs) && diag.push(`last heartbeat : ${Math.round(details.lastHeartbeatAgeMs / 1e3)}s ago`), queryCount > 0 && (diag.push(`queries started : ${queryCount}`), diag.push(`queries done : ${done}`), errored > 0 && diag.push(`queries errored : ${errored}`)), expanded.push("Diagnostics:");
142
+ for (let line of diag)
143
+ expanded.push(` ${line}`);
144
+ }
145
+ if (queries.length > 0) {
146
+ expanded.push(""), expanded.push("Per-query results (gathered before cancel):");
147
+ for (let q of queries) {
148
+ let dq = truncate(q.query, 52), tag = q.error ? "[err] " : "[ok] ", provider = q.provider ? ` (${q.provider})` : "", tail = q.error ? `\u2014 ${truncate(q.error, 60)}` : `\u2014 ${q.resultCount} source${q.resultCount === 1 ? "" : "s"}`;
149
+ expanded.push(` ${tag}"${dq}"${provider} ${tail}`);
150
+ }
151
+ }
152
+ if (extras.length > 0) {
153
+ expanded.push(""), expanded.push("Details:");
154
+ for (let e of extras)
155
+ expanded.push(` ${e}`);
156
+ }
157
+ let collapsed = [], parts = [];
158
+ if (queryCount > 0 && parts.push(`${done}/${queryCount} queries completed`), errored > 0 && parts.push(`${errored} errored`), details.browserConnected === !1 ? parts.push("browser never connected") : details.cancelReason && parts.push(`reason: ${details.cancelReason}`), parts.length > 0 && collapsed.push(parts.join("; ") + "."), collapsed.length === 0 && extras.length > 0)
159
+ for (let e of extras.slice(0, 2))
160
+ collapsed.push(truncate(e, 100));
161
+ let hiddenLines = Math.max(0, expanded.length - (1 + collapsed.length)), expandHint = hiddenLines > 0 ? `... (${hiddenLines} more lines, ${expanded.length} total)` : null;
162
+ return { expanded, collapsed, expandHint };
163
+ }
164
+
165
+ // packages/coding-agent/dist/builtin-extensions/pi-web-access/render-search-chrome.js
166
+ function normalizeSearchQueryList(queryList) {
167
+ let normalized = [];
168
+ for (let query of queryList) {
169
+ if (typeof query != "string")
170
+ continue;
171
+ let trimmed = query.trim();
172
+ trimmed.length > 0 && normalized.push(trimmed);
173
+ }
174
+ return normalized;
175
+ }
176
+ function collectSearchQueries(args) {
177
+ let input = args ?? {}, rawQueryList = Array.isArray(input.queries) ? input.queries : input.query !== void 0 ? [input.query] : [];
178
+ return normalizeSearchQueryList(rawQueryList);
179
+ }
180
+ function collectFetchUrls(args) {
181
+ let input = args ?? {};
182
+ return Array.isArray(input.urls) ? input.urls.filter((url) => typeof url == "string" && url.length > 0) : typeof input.url == "string" && input.url.length > 0 ? [input.url] : [];
183
+ }
184
+ function formatSearchCallTitle(queries) {
185
+ if (queries.length === 0)
186
+ return { title: "search (no query)", empty: !0 };
187
+ if (queries.length === 1) {
188
+ let q = queries[0];
189
+ return { title: `search "${q.length > 60 ? `${q.slice(0, 57)}...` : q}"`, empty: !1 };
190
+ }
191
+ return { title: `search ${queries.length} queries`, empty: !1 };
192
+ }
193
+ function formatFetchCallTitle(urls) {
194
+ return urls.length === 0 ? { title: "fetch (no URL)", empty: !0 } : urls.length === 1 ? { title: `fetch ${urls[0].length > 60 ? `${urls[0].slice(0, 57)}...` : urls[0]}`, empty: !1 } : { title: `fetch ${urls.length} URLs`, empty: !1 };
195
+ }
196
+ function formatSearchStatusLine(details) {
197
+ let statusLine = `${details?.queryCount === 1 ? "" : `${details?.successfulQueries}/${details?.queryCount} queries, `}${details?.totalResults ?? 0} sources`;
198
+ return details?.curated && details?.curatedFrom && (statusLine += ` (${details.queryCount}/${details.curatedFrom} queries curated)`), details?.fetchId && details?.fetchUrls ? statusLine += ` (fetching ${details.fetchUrls.length} URLs)` : details?.fetchId && (statusLine += " (content ready)"), statusLine;
199
+ }
200
+ function formatFetchStatusLine(details) {
201
+ let urlCount = details?.urlCount ?? 0;
202
+ return `${details?.successful ?? (urlCount === 1 ? 1 : 0)}/${urlCount} URLs`;
203
+ }
204
+ function formatSearchDetail(queries, details) {
205
+ let rest = formatSearchCallTitle(queries).title.replace(/^search\s+/, ""), status = details ? formatSearchStatusLine(details) : "";
206
+ return status ? `${rest} \xB7 ${status}` : rest;
207
+ }
208
+ function formatFetchDetail(urls, details) {
209
+ let rest = formatFetchCallTitle(urls).title.replace(/^fetch\s+/, ""), status = details ? formatFetchStatusLine(details) : "";
210
+ return status ? `${rest} \xB7 ${status}` : rest;
211
+ }
212
+ function formatSearchChrome(options) {
213
+ let call = formatSearchCallTitle(options.queries);
214
+ if (options.expanded)
215
+ return { call: call.title, result: options.queries.slice() };
216
+ let status = options.details ? formatSearchStatusLine(options.details) : "";
217
+ return { call: status ? `${call.title} \xB7 ${status}` : call.title, result: [] };
218
+ }
219
+ function formatFetchChrome(options) {
220
+ let call = formatFetchCallTitle(options.urls);
221
+ if (options.expanded)
222
+ return { call: call.title, result: options.urls.slice() };
223
+ let status = options.details ? formatFetchStatusLine(options.details) : "";
224
+ return { call: status ? `${call.title} \xB7 ${status}` : call.title, result: [] };
225
+ }
226
+
227
+ // packages/coding-agent/dist/builtin-extensions/pi-web-access/index.js
228
+ var __rewriteRelativeImportExtension = function(path, preserveJsx) {
229
+ return typeof path == "string" && /^\.\.?\//.test(path) ? path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
230
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
231
+ }) : path;
232
+ }, WEB_SEARCH_CONFIG_PATH = getWebSearchConfigPath();
233
+ function renderSearchErrorPlan(plan, expanded, theme) {
234
+ if (expanded)
235
+ return new Text(plan.expanded.map((l, i) => i === 0 ? theme.fg("error", l) : theme.fg("toolOutput", l)).join(`
236
+ `), 0, 0);
237
+ let box = new Box(1, 0, (t) => theme.bg("toolErrorBg", t));
238
+ box.addChild(new Text(theme.fg("error", plan.expanded[0]), 0, 0));
239
+ for (let line of plan.collapsed)
240
+ box.addChild(new Text(theme.fg("dim", line), 0, 0));
241
+ return plan.expandHint && box.addChild(new Text(theme.fg("muted", plan.expandHint), 0, 0)), box;
242
+ }
243
+ function loadConfig() {
244
+ if (!existsSync(WEB_SEARCH_CONFIG_PATH))
245
+ return {};
246
+ let raw = readFileSync(WEB_SEARCH_CONFIG_PATH, "utf-8");
247
+ try {
248
+ return JSON.parse(raw);
249
+ } catch (err) {
250
+ let message = err instanceof Error ? err.message : String(err);
251
+ throw new Error(`Failed to parse ${WEB_SEARCH_CONFIG_PATH}: ${message}`);
252
+ }
253
+ }
254
+ function saveConfig(updates) {
255
+ let config = {};
256
+ if (existsSync(WEB_SEARCH_CONFIG_PATH)) {
257
+ let raw = readFileSync(WEB_SEARCH_CONFIG_PATH, "utf-8");
258
+ try {
259
+ config = JSON.parse(raw);
260
+ } catch (err) {
261
+ let message = err instanceof Error ? err.message : String(err);
262
+ throw new Error(`Failed to parse ${WEB_SEARCH_CONFIG_PATH}: ${message}`);
263
+ }
264
+ }
265
+ Object.assign(config, updates);
266
+ let dir = getWebSearchConfigDir();
267
+ existsSync(dir) || mkdirSync(dir, { recursive: !0 }), writeFileSync(WEB_SEARCH_CONFIG_PATH, JSON.stringify(config, null, 2) + `
268
+ `);
269
+ }
270
+ var DEFAULT_SHORTCUTS = { curate: "ctrl+shift+s", activity: "ctrl+shift+w" }, DEFAULT_CURATOR_TIMEOUT_SECONDS = 20, MAX_CURATOR_TIMEOUT_SECONDS = 600;
271
+ function loadConfigForExtensionInit() {
272
+ try {
273
+ return loadConfig();
274
+ } catch (err) {
275
+ let message = err instanceof Error ? err.message : String(err);
276
+ return console.error(`[pi-web-access] ${message}`), {};
277
+ }
278
+ }
279
+ function normalizeProviderInput(value) {
280
+ if (value === void 0)
281
+ return;
282
+ if (typeof value != "string")
283
+ return "auto";
284
+ let normalized = value.trim().toLowerCase();
285
+ return ["auto", "openai", "brave", "parallel", "tavily", "exa", "perplexity", "gemini"].includes(normalized) ? normalized : "auto";
286
+ }
287
+ function normalizeCuratorTimeoutSeconds(value) {
288
+ if (typeof value != "number" || !Number.isFinite(value))
289
+ return;
290
+ let normalized = Math.floor(value);
291
+ if (!(normalized < 1))
292
+ return Math.min(normalized, MAX_CURATOR_TIMEOUT_SECONDS);
293
+ }
294
+ function resolveWorkflow(input, hasUI) {
295
+ let normalized = typeof input == "string" ? input.trim().toLowerCase() : "";
296
+ return normalized === "auto-summary" ? "auto-summary" : !hasUI || normalized === "none" ? "none" : "summary-review";
297
+ }
298
+ function normalizeQueryList(queryList) {
299
+ return collectSearchQueries({ queries: queryList });
300
+ }
301
+ function getCuratorTimeoutSeconds() {
302
+ let source = loadConfig();
303
+ return normalizeCuratorTimeoutSeconds(source.curatorTimeoutSeconds) ?? DEFAULT_CURATOR_TIMEOUT_SECONDS;
304
+ }
305
+ async function getProviderAvailability(ctx2) {
306
+ let [openaiMod, braveMod, parallelMod, tavilyMod, perplexityMod, exaMod, geminiApiMod] = await Promise.all([
307
+ loadOpenAISearch(),
308
+ loadBrave(),
309
+ loadParallel(),
310
+ loadTavily(),
311
+ loadPerplexity(),
312
+ loadExa(),
313
+ loadGeminiApi()
314
+ ]), geminiWebAvail = null;
315
+ return isBrowserCookieAccessAllowed() && (geminiWebAvail = await (await loadGeminiWeb()).isGeminiWebAvailable()), {
316
+ openai: await openaiMod.isOpenAISearchAvailable(ctx2),
317
+ brave: braveMod.isBraveAvailable(),
318
+ parallel: parallelMod.isParallelAvailable(),
319
+ tavily: tavilyMod.isTavilyAvailable(),
320
+ perplexity: perplexityMod.isPerplexityAvailable(),
321
+ exa: exaMod.isExaAvailable(),
322
+ gemini: geminiApiMod.isGeminiApiAvailable() || !!geminiWebAvail
323
+ };
324
+ }
325
+ function shouldPreferOpenAI(options) {
326
+ return options ? !(options.recencyFilter || typeof options.numResults == "number" && Number.isFinite(options.numResults) && Math.floor(options.numResults) !== 5) : !0;
327
+ }
328
+ async function loadCuratorBootstrap(requestedProvider, ctx2, options) {
329
+ let availableProviders = await getProviderAvailability(ctx2);
330
+ return {
331
+ availableProviders,
332
+ defaultProvider: resolveProvider(requestedProvider, availableProviders, options),
333
+ timeoutSeconds: getCuratorTimeoutSeconds()
334
+ };
335
+ }
336
+ function firstAvailableProvider(available, preferOpenAI, fallback) {
337
+ return preferOpenAI && available.openai ? "openai" : available.exa ? "exa" : available.brave ? "brave" : available.parallel ? "parallel" : available.tavily ? "tavily" : available.perplexity ? "perplexity" : available.gemini ? "gemini" : fallback;
338
+ }
339
+ function resolveProvider(requested, available, options) {
340
+ let provider = normalizeProviderInput(requested ?? loadConfig().provider ?? "auto") ?? "auto", preferOpenAI = shouldPreferOpenAI(options);
341
+ return provider === "auto" ? firstAvailableProvider(available, preferOpenAI, "exa") : provider === "openai" && !available.openai ? firstAvailableProvider(available, !1, "openai") : provider === "brave" && !available.brave ? firstAvailableProvider(available, preferOpenAI, "brave") : provider === "parallel" && !available.parallel ? firstAvailableProvider(available, preferOpenAI, "parallel") : provider === "tavily" && !available.tavily ? firstAvailableProvider(available, preferOpenAI, "tavily") : provider === "exa" && !available.exa ? firstAvailableProvider(available, preferOpenAI, "exa") : provider === "perplexity" && !available.perplexity ? firstAvailableProvider(available, preferOpenAI, "perplexity") : provider === "gemini" && !available.gemini ? firstAvailableProvider(available, preferOpenAI, "gemini") : provider;
342
+ }
343
+ var pendingFetches = /* @__PURE__ */ new Map(), sessionActive = !1, sessionGeneration = 0, sessionAbort = new AbortController();
344
+ function assertActiveRequest(generation, signal) {
345
+ if (signal?.throwIfAborted(), generation !== sessionGeneration)
346
+ throw new Error("Web request cancelled: session changed");
347
+ }
348
+ var widgetVisible = !1, widgetUnsubscribe = null, pendingCurates = /* @__PURE__ */ new Map(), activeCurators = /* @__PURE__ */ new Map(), glimpseWins = /* @__PURE__ */ new Map(), MAX_INLINE_CONTENT = 3e4;
349
+ function stripThumbnails(results) {
350
+ return results.map(({ thumbnail, frames, ...rest }) => rest);
351
+ }
352
+ function formatSearchSummary(results, answer) {
353
+ let output = answer ? `${answer}
354
+
355
+ ---
356
+
357
+ **Sources:**
358
+ ` : "";
359
+ return output += results.map((r, i) => `${i + 1}. ${r.title}
360
+ ${r.url}`).join(`
361
+
362
+ `), output;
363
+ }
364
+ function duplicateQuerySet(results) {
365
+ let counts = /* @__PURE__ */ new Map();
366
+ for (let result of results)
367
+ counts.set(result.query, (counts.get(result.query) ?? 0) + 1);
368
+ let duplicates = /* @__PURE__ */ new Set();
369
+ for (let [query, count] of counts)
370
+ count > 1 && duplicates.add(query);
371
+ return duplicates;
372
+ }
373
+ function formatQueryHeader(query, provider, duplicateQueries) {
374
+ let suffix = duplicateQueries.has(query) && provider ? ` (${provider})` : "";
375
+ return `## Query: "${query}"${suffix}
376
+
377
+ `;
378
+ }
379
+ function hasFullInlineCoverage(urls, inlineContent) {
380
+ if (!inlineContent || inlineContent.length === 0)
381
+ return !1;
382
+ let coveredUrls = new Set(inlineContent.map((c) => c.url));
383
+ return urls.every((url) => coveredUrls.has(url));
384
+ }
385
+ function formatFullResults(queryData) {
386
+ let output = `## Results for: "${queryData.query}"
387
+
388
+ `;
389
+ queryData.answer && (output += `${queryData.answer}
390
+
391
+ ---
392
+
393
+ `);
394
+ for (let r of queryData.results)
395
+ output += `### ${r.title}
396
+ ${r.url}
397
+
398
+ `;
399
+ return output;
400
+ }
401
+ function abortPendingFetches() {
402
+ for (let controller of pendingFetches.values())
403
+ controller.abort();
404
+ pendingFetches.clear();
405
+ }
406
+ function closeCurator(callId) {
407
+ if (callId !== void 0) {
408
+ let win = glimpseWins.get(callId);
409
+ glimpseWins.delete(callId);
410
+ try {
411
+ win?.close();
412
+ } catch {
413
+ }
414
+ pendingCurates.get(callId)?.cancel("stale"), pendingCurates.delete(callId);
415
+ let curator = activeCurators.get(callId);
416
+ activeCurators.delete(callId);
417
+ try {
418
+ curator?.close();
419
+ } catch {
420
+ }
421
+ return;
422
+ }
423
+ for (let win of glimpseWins.values())
424
+ try {
425
+ win.close();
426
+ } catch {
427
+ }
428
+ glimpseWins.clear();
429
+ for (let pc of pendingCurates.values())
430
+ try {
431
+ pc.cancel("stale");
432
+ } catch {
433
+ }
434
+ pendingCurates.clear();
435
+ for (let curator of activeCurators.values())
436
+ try {
437
+ curator.close();
438
+ } catch {
439
+ }
440
+ activeCurators.clear();
441
+ }
442
+ async function openInBrowser(pi, url) {
443
+ let plat = platform(), result = plat === "darwin" ? await pi.exec("open", [url]) : plat === "win32" ? await pi.exec("cmd", ["/c", "start", "", url]) : await pi.exec("xdg-open", [url]);
444
+ if (result.code !== 0)
445
+ throw new Error(result.stderr || `Failed to open browser (exit code ${result.code})`);
446
+ }
447
+ var glimpseOpen;
448
+ function findGlimpseMjs() {
449
+ try {
450
+ return createRequire(new URL("../builtin-extensions/pi-web-access/index.js", import.meta.url).href).resolve("glimpseui");
451
+ } catch {
452
+ }
453
+ try {
454
+ let globalRoot = execFileSync("npm", ["root", "-g"], { encoding: "utf-8" }).trim(), entry = join(globalRoot, "glimpseui", "src", "glimpse.mjs");
455
+ if (existsSync(entry))
456
+ return entry;
457
+ } catch {
458
+ }
459
+ return null;
460
+ }
461
+ async function getGlimpseOpen() {
462
+ if (glimpseOpen !== void 0)
463
+ return glimpseOpen;
464
+ let resolved = findGlimpseMjs();
465
+ if (resolved)
466
+ try {
467
+ return glimpseOpen = (await import(__rewriteRelativeImportExtension(resolved))).open, glimpseOpen;
468
+ } catch {
469
+ }
470
+ return glimpseOpen = null, glimpseOpen;
471
+ }
472
+ function openInGlimpse(open, url, title) {
473
+ let shellHTML = `<!DOCTYPE html>
474
+ <html>
475
+ <head><meta charset="UTF-8"><title>${title}</title></head>
476
+ <body style="margin:0; background:#1a1a2e;">
477
+ <script>window.location.replace(${JSON.stringify(url)});</script>
478
+ </body>
479
+ </html>`, win = open(shellHTML, {
480
+ width: 800,
481
+ height: 900,
482
+ title
483
+ }), maxHeight = 1200;
484
+ return win.on("ready", (info) => {
485
+ let visibleHeight = info?.screen?.visibleHeight;
486
+ typeof visibleHeight == "number" && visibleHeight > 0 && (maxHeight = Math.floor(visibleHeight * 0.85));
487
+ }), win.on("message", (data) => {
488
+ if (!data || typeof data != "object")
489
+ return;
490
+ let msg = data;
491
+ if (msg.type !== "resize" || typeof msg.height != "number")
492
+ return;
493
+ let clamped = Math.max(400, Math.min(Math.round(msg.height), maxHeight));
494
+ win._write({ type: "resize", width: 800, height: clamped });
495
+ }), win;
496
+ }
497
+ function extractDomain(url) {
498
+ try {
499
+ return new URL(url).hostname;
500
+ } catch {
501
+ return url;
502
+ }
503
+ }
504
+ function updateWidget(ctx2) {
505
+ let theme = ctx2.ui.theme, entries = activityMonitor.getEntries(), lines = [];
506
+ if (lines.push(theme.fg("accent", "\u2500\u2500\u2500 Web Search Activity " + "\u2500".repeat(36))), entries.length === 0)
507
+ lines.push(theme.fg("muted", " No activity yet"));
508
+ else
509
+ for (let e of entries)
510
+ lines.push(" " + formatEntryLine(e, theme));
511
+ lines.push(theme.fg("accent", "\u2500".repeat(60)));
512
+ let rateInfo = activityMonitor.getRateLimitInfo(), resetMs = rateInfo.oldestTimestamp ? Math.max(0, rateInfo.oldestTimestamp + rateInfo.windowMs - Date.now()) : 0, resetSec = Math.ceil(resetMs / 1e3);
513
+ lines.push(theme.fg("muted", `Rate: ${rateInfo.used}/${rateInfo.max}`) + (resetMs > 0 ? theme.fg("dim", ` (resets in ${resetSec}s)`) : "")), ctx2.ui.setWidget("web-activity", new Text(lines.join(`
514
+ `), 0, 0));
515
+ }
516
+ function formatEntryLine(entry, theme) {
517
+ let typeStr = entry.type === "api" ? "API" : "GET", target = entry.type === "api" ? `"${truncateToWidth(entry.query || "", 28, "")}"` : truncateToWidth(entry.url?.replace(/^https?:\/\//, "") || "", 30, ""), duration = entry.endTime ? `${((entry.endTime - entry.startTime) / 1e3).toFixed(1)}s` : `${((Date.now() - entry.startTime) / 1e3).toFixed(1)}s`, statusStr, indicator;
518
+ return entry.error ? (statusStr = "err", indicator = theme.fg("error", "\u2717")) : entry.status === null ? (statusStr = "...", indicator = theme.fg("warning", "\u22EF")) : entry.status === 0 ? (statusStr = "abort", indicator = theme.fg("muted", "\u25CB")) : (statusStr = String(entry.status), indicator = entry.status >= 200 && entry.status < 300 ? theme.fg("success", "\u2713") : theme.fg("error", "\u2717")), `${typeStr.padEnd(4)} ${target.padEnd(32)} ${statusStr.padStart(5)} ${duration.padStart(5)} ${indicator}`;
519
+ }
520
+ function handleSessionChange(ctx2) {
521
+ sessionGeneration++, sessionAbort.abort(), sessionAbort = new AbortController(), abortPendingFetches(), closeCurator(), runSessionCleanups(), sessionActive = !0, restoreFromSession(ctx2), widgetUnsubscribe?.(), widgetUnsubscribe = null, activityMonitor.clear(), widgetVisible && (widgetUnsubscribe = activityMonitor.onUpdate(() => updateWidget(ctx2)), updateWidget(ctx2));
522
+ }
523
+ function pi_web_access_default(pi) {
524
+ let initConfig = loadConfigForExtensionInit(), curateKey = initConfig.shortcuts?.curate || DEFAULT_SHORTCUTS.curate, activityKey = initConfig.shortcuts?.activity || DEFAULT_SHORTCUTS.activity, warmSummaryReview = null;
525
+ async function ensureSummaryReview() {
526
+ return warmSummaryReview || (warmSummaryReview = await loadSummaryReview()), warmSummaryReview;
527
+ }
528
+ async function runSearch(query, options) {
529
+ let { search } = await loadGeminiSearch();
530
+ return search(query, options);
531
+ }
532
+ globalThis[/* @__PURE__ */ Symbol.for("@lunr/search-curator")] = {
533
+ getWorkflow() {
534
+ return resolveWorkflow(loadConfigForExtensionInit().workflow, !0);
535
+ },
536
+ setWorkflow(workflow) {
537
+ saveConfig({ workflow });
538
+ }
539
+ };
540
+ function startBackgroundFetch(urls) {
541
+ if (urls.length === 0)
542
+ return null;
543
+ let fetchId = generateId(), controller = new AbortController();
544
+ return pendingFetches.set(fetchId, controller), (async () => {
545
+ try {
546
+ let { fetchAllContent } = await loadExtract();
547
+ if (!sessionActive || !pendingFetches.has(fetchId))
548
+ return;
549
+ let fetched = await fetchAllContent(urls, controller.signal);
550
+ if (!sessionActive || !pendingFetches.has(fetchId))
551
+ return;
552
+ let data = {
553
+ id: fetchId,
554
+ type: "fetch",
555
+ timestamp: Date.now(),
556
+ urls: stripThumbnails(fetched)
557
+ };
558
+ storeResult(fetchId, data), pi.appendEntry("web-search-results", data);
559
+ let ok = fetched.filter((f) => !f.error).length;
560
+ pi.sendMessage({
561
+ customType: "web-search-content-ready",
562
+ content: `Content fetched for ${ok}/${fetched.length} URLs [${fetchId}]. Full page content now available.`,
563
+ display: !0
564
+ }, { triggerTurn: !0 });
565
+ } catch (err) {
566
+ if (!sessionActive || !pendingFetches.has(fetchId))
567
+ return;
568
+ let message = err instanceof Error ? err.message : String(err);
569
+ err instanceof Error && err.name === "AbortError" || message.toLowerCase().includes("abort") || pi.sendMessage({
570
+ customType: "web-search-error",
571
+ content: `Content fetch failed [${fetchId}]: ${message}`,
572
+ display: !0
573
+ }, { triggerTurn: !1 });
574
+ } finally {
575
+ pendingFetches.delete(fetchId);
576
+ }
577
+ })(), fetchId;
578
+ }
579
+ function storeAndPublishSearch(results) {
580
+ let id = generateId(), data = {
581
+ id,
582
+ type: "search",
583
+ timestamp: Date.now(),
584
+ queries: results
585
+ };
586
+ return storeResult(id, data), pi.appendEntry("web-search-results", data), id;
587
+ }
588
+ function normalizeSummaryMeta(meta, summaryText) {
589
+ let normalizedText = summaryText.trim();
590
+ return meta ? {
591
+ model: meta.model,
592
+ durationMs: Number.isFinite(meta.durationMs) && meta.durationMs >= 0 ? meta.durationMs : 0,
593
+ tokenEstimate: Number.isFinite(meta.tokenEstimate) && meta.tokenEstimate >= 0 ? meta.tokenEstimate : normalizedText.length > 0 ? Math.max(1, Math.ceil(normalizedText.length / 4)) : 0,
594
+ fallbackUsed: meta.fallbackUsed === !0,
595
+ fallbackReason: meta.fallbackReason,
596
+ edited: meta.edited === !0
597
+ } : {
598
+ model: null,
599
+ durationMs: 0,
600
+ tokenEstimate: normalizedText.length > 0 ? Math.max(1, Math.ceil(normalizedText.length / 4)) : 0,
601
+ fallbackUsed: !1,
602
+ edited: !1
603
+ };
604
+ }
605
+ function buildCurationCancelledReturn(reason, partial) {
606
+ let message = `Search curation cancelled (${reason}).`, cancelledQueries = partial?.queries?.length ? partial.queries.map((q) => ({
607
+ query: q.query,
608
+ provider: q.provider ?? null,
609
+ error: q.error,
610
+ resultCount: q.results?.length ?? 0
611
+ })) : void 0, extraLines = [];
612
+ return partial?.curatorUrl && extraLines.push(`curator: ${partial.curatorUrl}`), partial?.browserOpenError && extraLines.push(`browser open error: ${partial.browserOpenError}`), {
613
+ content: [{ type: "text", text: message }],
614
+ details: {
615
+ error: message,
616
+ cancelled: !0,
617
+ cancelReason: reason,
618
+ browserConnected: partial?.browserConnected,
619
+ lastHeartbeatAgeMs: partial?.lastHeartbeatAgeMs,
620
+ queryCount: partial?.queryCount,
621
+ cancelledQueries,
622
+ extraLines: extraLines.length > 0 ? extraLines : void 0
623
+ }
624
+ };
625
+ }
626
+ async function resolveFirstAvailableModel(ctx2, candidates) {
627
+ let enabledModelPatterns = loadEnabledModelPatterns(ctx2);
628
+ for (let { provider, id } of candidates) {
629
+ let model = ctx2.modelRegistry.find(provider, id);
630
+ if (!model || !modelMatchesEnabledPatterns(model, enabledModelPatterns))
631
+ continue;
632
+ let auth = await ctx2.modelRegistry.getApiKeyAndHeaders(model);
633
+ if (auth.ok && auth.apiKey)
634
+ return { model, apiKey: auth.apiKey, headers: auth.headers };
635
+ }
636
+ throw new Error(`No enabled model available: ${candidates.map((c) => `${c.provider}/${c.id}`).join(", ")}`);
637
+ }
638
+ async function rewriteSearchQuery(query, ctx2, signal) {
639
+ let { model, apiKey, headers } = await resolveFirstAvailableModel(ctx2, [
640
+ { provider: "anthropic", id: "claude-haiku-4-5" },
641
+ { provider: "google", id: "gemini-2.5-flash" },
642
+ { provider: "openai", id: "gpt-4.1-mini" }
643
+ ]), response = await complete(model, {
644
+ messages: [{
645
+ role: "user",
646
+ content: [{ type: "text", text: `Rewrite this web search query to get better, more specific results. Add relevant year qualifiers, precise technical terms, and specificity. Return ONLY the improved query text, nothing else.
647
+
648
+ Query: ${query}` }],
649
+ timestamp: Date.now()
650
+ }]
651
+ }, { apiKey, headers, signal });
652
+ if (response.stopReason === "aborted")
653
+ throw new Error("Aborted");
654
+ let text = (Array.isArray(response.content) ? response.content : []).map((p) => {
655
+ if (!p || typeof p != "object")
656
+ return "";
657
+ let part = p;
658
+ return typeof part.text == "string" ? part.text : "";
659
+ }).join("").trim();
660
+ if (!text)
661
+ throw new Error("Rewrite returned empty response");
662
+ return text;
663
+ }
664
+ async function generateSummaryForSelectedIndices(selectedQueryIndices, resultsByIndex, summaryContext, signal, modelOverride, feedback) {
665
+ let selectedResults = [];
666
+ for (let qi of selectedQueryIndices) {
667
+ let result = resultsByIndex.get(qi);
668
+ result && selectedResults.push(result);
669
+ }
670
+ if (selectedResults.length === 0)
671
+ throw new Error("No selected results available for summary generation");
672
+ let summaryReview = await ensureSummaryReview();
673
+ try {
674
+ return await summaryReview.generateSummaryDraft(selectedResults, summaryContext, signal, modelOverride, feedback);
675
+ } catch (err) {
676
+ if (!(err instanceof Error && err.message.includes("Summary model returned empty response")))
677
+ throw err;
678
+ let deterministic = summaryReview.buildDeterministicSummary(selectedResults);
679
+ return {
680
+ summary: deterministic.summary,
681
+ meta: {
682
+ ...deterministic.meta,
683
+ fallbackReason: "summary-model-empty-response"
684
+ }
685
+ };
686
+ }
687
+ }
688
+ async function loadSummaryModelChoices(summaryContext) {
689
+ let summaryModels = [], seen = /* @__PURE__ */ new Set(), availableValues = /* @__PURE__ */ new Set(), addModel = (provider, id) => {
690
+ let value = `${provider}/${id}`;
691
+ seen.has(value) || (seen.add(value), summaryModels.push({ value, label: value }));
692
+ }, enabledModelPatterns = null, scopeLoaded = !0;
693
+ try {
694
+ enabledModelPatterns = loadEnabledModelPatterns(summaryContext);
695
+ let availableModels = summaryContext.modelRegistry.getAvailable();
696
+ for (let model of availableModels) {
697
+ if (!modelMatchesEnabledPatterns(model, enabledModelPatterns))
698
+ continue;
699
+ let value = `${model.provider}/${model.id}`;
700
+ availableValues.add(value), addModel(model.provider, model.id);
701
+ }
702
+ } catch (err) {
703
+ scopeLoaded = !1;
704
+ let message = err instanceof Error ? err.message : String(err);
705
+ console.error(`Failed to load summary models: ${message}`);
706
+ }
707
+ let currentModelValue = summaryContext.model ? `${summaryContext.model.provider}/${summaryContext.model.id}` : null;
708
+ scopeLoaded && summaryContext.model && currentModelValue && !seen.has(currentModelValue) && modelMatchesEnabledPatterns(summaryContext.model, enabledModelPatterns) && addModel(summaryContext.model.provider, summaryContext.model.id);
709
+ let config = loadConfig(), configuredSummaryModel = typeof config.summaryModel == "string" ? config.summaryModel.trim() : "", preferredDefaults = [
710
+ "anthropic/claude-haiku-4-5",
711
+ "openai-codex/gpt-5.3-codex-spark"
712
+ ], defaultSummaryModel = null;
713
+ if (configuredSummaryModel.length > 0 && availableValues.has(configuredSummaryModel) && (defaultSummaryModel = configuredSummaryModel), !defaultSummaryModel) {
714
+ for (let preferred of preferredDefaults)
715
+ if (availableValues.has(preferred)) {
716
+ defaultSummaryModel = preferred;
717
+ break;
718
+ }
719
+ }
720
+ return { summaryModels, defaultSummaryModel };
721
+ }
722
+ function resolveSummaryForSubmit(payload, resultsByIndex) {
723
+ let submittedSummary = typeof payload.summary == "string" ? payload.summary.trim() : "";
724
+ if (submittedSummary.length > 0)
725
+ return {
726
+ approvedSummary: submittedSummary,
727
+ summaryMeta: normalizeSummaryMeta(payload.summaryMeta, submittedSummary)
728
+ };
729
+ let selected = filterByQueryIndices(payload.selectedQueryIndices, resultsByIndex).results, fallbackResults = selected.length > 0 ? selected : [...resultsByIndex.values()];
730
+ if (!warmSummaryReview)
731
+ throw new Error("Summary review module is not loaded");
732
+ let deterministic = warmSummaryReview.buildDeterministicSummary(fallbackResults);
733
+ return {
734
+ approvedSummary: deterministic.summary,
735
+ summaryMeta: deterministic.meta
736
+ };
737
+ }
738
+ function buildSearchReturn(opts) {
739
+ let sc = opts.results.filter((r) => !r.error).length, tr = opts.results.reduce((sum, r) => sum + r.results.length, 0), hasApprovedSummary = typeof opts.approvedSummary == "string" && opts.approvedSummary.trim().length > 0, output = "";
740
+ if (hasApprovedSummary)
741
+ output = opts.approvedSummary.trim();
742
+ else {
743
+ opts.curated && (output += `[These results were manually curated by the user in the browser. Use them as-is \u2014 do not re-search or discard.]
744
+
745
+ `);
746
+ let duplicateQueries = opts.curated ? duplicateQuerySet(opts.results) : /* @__PURE__ */ new Set();
747
+ for (let { query, answer, results, error, provider } of opts.results)
748
+ opts.queryList.length > 1 && (output += opts.curated ? formatQueryHeader(query, provider, duplicateQueries) : `## Query: "${query}"
749
+
750
+ `), error ? output += `Error: ${error}
751
+
752
+ ` : results.length === 0 ? output += `No results found.
753
+
754
+ ` : output += formatSearchSummary(results, answer) + `
755
+
756
+ `;
757
+ }
758
+ let hasInlineReady = hasFullInlineCoverage(opts.urls, opts.inlineContent), fetchId = null;
759
+ if (hasInlineReady && opts.inlineContent) {
760
+ fetchId = generateId();
761
+ let data = {
762
+ id: fetchId,
763
+ type: "fetch",
764
+ timestamp: Date.now(),
765
+ urls: opts.inlineContent
766
+ };
767
+ storeResult(fetchId, data), pi.appendEntry("web-search-results", data), hasApprovedSummary || (output += `---
768
+ Full content for ${opts.inlineContent.length} sources available [${fetchId}].`);
769
+ } else opts.includeContent && (fetchId = startBackgroundFetch(opts.urls), fetchId && !hasApprovedSummary && (output += `---
770
+ Content fetching in background [${fetchId}]. Will notify when ready.`));
771
+ let searchId = storeAndPublishSearch(opts.results), isBackgroundFetch = fetchId !== null && !hasInlineReady;
772
+ return {
773
+ content: [{ type: "text", text: output.trim() }],
774
+ details: {
775
+ queries: opts.queryList,
776
+ queryCount: opts.queryList.length,
777
+ successfulQueries: sc,
778
+ totalResults: tr,
779
+ includeContent: opts.includeContent,
780
+ fetchId,
781
+ fetchUrls: isBackgroundFetch ? opts.urls : void 0,
782
+ searchId,
783
+ ...opts.curated ? {
784
+ curated: !0,
785
+ curatedFrom: opts.curatedFrom,
786
+ curatedQueries: opts.results.map((r) => ({
787
+ query: r.query,
788
+ provider: r.provider || null,
789
+ answer: r.answer || null,
790
+ sources: r.results.map((s) => ({ title: s.title, url: s.url })),
791
+ error: r.error
792
+ }))
793
+ } : {},
794
+ ...opts.workflow && hasApprovedSummary ? {
795
+ summary: {
796
+ text: opts.approvedSummary.trim(),
797
+ workflow: opts.workflow,
798
+ model: opts.summaryMeta?.model ?? null,
799
+ durationMs: opts.summaryMeta?.durationMs ?? 0,
800
+ tokenEstimate: opts.summaryMeta?.tokenEstimate ?? 0,
801
+ fallbackUsed: opts.summaryMeta?.fallbackUsed === !0,
802
+ fallbackReason: opts.summaryMeta?.fallbackReason,
803
+ edited: opts.summaryMeta?.edited === !0
804
+ }
805
+ } : {}
806
+ }
807
+ };
808
+ }
809
+ function filterByQueryIndices(selectedQueryIndices, results) {
810
+ let filteredResults = [], filteredUrls = [];
811
+ for (let qi of selectedQueryIndices) {
812
+ let r = results.get(qi);
813
+ if (r) {
814
+ filteredResults.push(r);
815
+ for (let res of r.results)
816
+ filteredUrls.includes(res.url) || filteredUrls.push(res.url);
817
+ }
818
+ }
819
+ return { results: filteredResults, urls: filteredUrls };
820
+ }
821
+ function collectAllResultsAndUrls(resultsByIndex) {
822
+ let results = [...resultsByIndex.values()], urls = [];
823
+ for (let result of results)
824
+ for (let source of result.results)
825
+ urls.includes(source.url) || urls.push(source.url);
826
+ return { results, urls };
827
+ }
828
+ async function openCuratorBrowser(callId, pc, searchesComplete = !0) {
829
+ if (pendingCurates.get(callId) !== pc)
830
+ return;
831
+ let handle = null;
832
+ try {
833
+ pc.phase = "curating";
834
+ let searchAbort = new AbortController(), addSearchSignal = pc.signal ? AbortSignal.any([pc.signal, searchAbort.signal]) : searchAbort.signal, sessionToken = randomUUID(), [{ startCuratorServer }] = await Promise.all([
835
+ loadCuratorServer(),
836
+ ensureSummaryReview()
837
+ ]);
838
+ if (pendingCurates.get(callId) !== pc)
839
+ return;
840
+ if (handle = await startCuratorServer({
841
+ queries: pc.queryList,
842
+ sessionToken,
843
+ timeout: pc.timeoutSeconds,
844
+ availableProviders: pc.availableProviders,
845
+ defaultProvider: pc.defaultProvider,
846
+ searchProvider: pc.searchProvider,
847
+ summaryModels: pc.summaryModels,
848
+ defaultSummaryModel: pc.defaultSummaryModel
849
+ }, {
850
+ async onSummarize(selectedQueryIndices, summarizeSignal, model, feedback) {
851
+ if (pendingCurates.get(callId) !== pc)
852
+ throw new Error("Curator session is no longer active.");
853
+ pc.onUpdate?.({
854
+ content: [{ type: "text", text: "Generating summary draft..." }],
855
+ details: { phase: "generating-summary", progress: 0.9, curatorUrl: pc.curatorUrl, timeoutSeconds: pc.timeoutSeconds, shortcut: curateKey }
856
+ });
857
+ let draft = await generateSummaryForSelectedIndices(selectedQueryIndices, pc.searchResults, pc.summaryContext, summarizeSignal, model, feedback);
858
+ if (pendingCurates.get(callId) !== pc)
859
+ throw new Error("Curator session is no longer active.");
860
+ return pc.onUpdate?.({
861
+ content: [{ type: "text", text: "Summary draft ready \u2014 waiting for approval..." }],
862
+ details: { phase: "waiting-for-approval", progress: 1, curatorUrl: pc.curatorUrl, timeoutSeconds: pc.timeoutSeconds, shortcut: curateKey }
863
+ }), draft;
864
+ },
865
+ onSubmit(payload) {
866
+ if (pendingCurates.get(callId) !== pc)
867
+ return;
868
+ searchAbort.abort();
869
+ let filtered = payload.selectedQueryIndices.length > 0 ? filterByQueryIndices(payload.selectedQueryIndices, pc.searchResults) : collectAllResultsAndUrls(pc.searchResults), filteredInline = pc.allInlineContent.filter((c) => filtered.urls.includes(c.url)), base = {
870
+ queryList: filtered.results.map((r) => r.query),
871
+ results: filtered.results,
872
+ urls: filtered.urls,
873
+ includeContent: pc.includeContent,
874
+ inlineContent: filteredInline.length > 0 ? filteredInline : void 0,
875
+ curated: !0,
876
+ curatedFrom: pc.searchResults.size
877
+ };
878
+ if (!payload.rawResults) {
879
+ let resolvedSummary = resolveSummaryForSubmit(payload, pc.searchResults);
880
+ base.workflow = pc.workflow, base.approvedSummary = resolvedSummary.approvedSummary, base.summaryMeta = resolvedSummary.summaryMeta;
881
+ }
882
+ pc.finish(buildSearchReturn(base)), closeCurator(callId);
883
+ },
884
+ onCancel(reason) {
885
+ if (pendingCurates.get(callId) === pc) {
886
+ if (searchAbort.abort(), reason === "timeout") {
887
+ let resolvedSummary = resolveSummaryForSubmit({ selectedQueryIndices: [], summary: void 0, summaryMeta: void 0 }, pc.searchResults), all = collectAllResultsAndUrls(pc.searchResults), filteredInline = pc.allInlineContent.filter((c) => all.urls.includes(c.url));
888
+ pc.finish(buildSearchReturn({
889
+ queryList: all.results.map((r) => r.query),
890
+ results: all.results,
891
+ urls: all.urls,
892
+ includeContent: pc.includeContent,
893
+ inlineContent: filteredInline.length > 0 ? filteredInline : void 0,
894
+ curated: !0,
895
+ curatedFrom: pc.searchResults.size,
896
+ workflow: pc.workflow,
897
+ approvedSummary: resolvedSummary.approvedSummary,
898
+ summaryMeta: resolvedSummary.summaryMeta
899
+ }));
900
+ } else {
901
+ let conn = activeCurators.get(callId)?.getConnectionState();
902
+ pc.finish(buildCurationCancelledReturn(reason, {
903
+ queries: Array.from(pc.searchResults.values()),
904
+ queryCount: pc.queryList.length,
905
+ browserConnected: conn?.browserConnected,
906
+ lastHeartbeatAgeMs: conn?.lastHeartbeatAgeMs,
907
+ curatorUrl: pc.curatorUrl,
908
+ browserOpenError: pc.browserOpenError
909
+ }));
910
+ }
911
+ closeCurator(callId);
912
+ }
913
+ },
914
+ onProviderChange(provider) {
915
+ if (pendingCurates.get(callId) !== pc)
916
+ return;
917
+ let normalized = normalizeProviderInput(provider);
918
+ if (!(!normalized || normalized === "auto")) {
919
+ pc.defaultProvider = normalized, pc.searchProvider = normalized;
920
+ try {
921
+ saveConfig({ provider: normalized });
922
+ } catch (err) {
923
+ let message = err instanceof Error ? err.message : String(err);
924
+ console.error(`Failed to persist default provider: ${message}`);
925
+ }
926
+ }
927
+ },
928
+ async onAddSearch(query, queryIndex, provider) {
929
+ if (pendingCurates.get(callId) !== pc)
930
+ throw new Error("Curator session is no longer active.");
931
+ let normalizedProvider = normalizeProviderInput(provider), requestedProvider = !normalizedProvider || normalizedProvider === "auto" ? pc.searchProvider : normalizedProvider;
932
+ try {
933
+ let { answer, results, inlineContent, provider: actualProvider } = await runSearch(query, {
934
+ provider: requestedProvider,
935
+ numResults: pc.numResults,
936
+ recencyFilter: pc.recencyFilter,
937
+ domainFilter: pc.domainFilter,
938
+ includeContent: pc.includeContent,
939
+ signal: addSearchSignal,
940
+ extensionContext: ctx
941
+ });
942
+ if (pendingCurates.get(callId) !== pc)
943
+ throw new Error("Curator session is no longer active.");
944
+ return pc.searchResults.set(queryIndex, { query, answer, results, error: null, provider: actualProvider }), inlineContent && pc.allInlineContent.push(...inlineContent), {
945
+ answer,
946
+ results: results.map((r) => ({ title: r.title, url: r.url, domain: extractDomain(r.url) })),
947
+ provider: actualProvider
948
+ };
949
+ } catch (err) {
950
+ let message = err instanceof Error ? err.message : String(err);
951
+ throw pendingCurates.get(callId) === pc && pc.searchResults.set(queryIndex, { query, answer: "", results: [], error: message, provider: requestedProvider }), err;
952
+ }
953
+ },
954
+ async onRewriteQuery(query, rewriteSignal) {
955
+ if (pendingCurates.get(callId) !== pc)
956
+ throw new Error("Curator session is no longer active.");
957
+ return rewriteSearchQuery(query, pc.summaryContext, rewriteSignal);
958
+ }
959
+ }), pendingCurates.get(callId) !== pc) {
960
+ handle.close();
961
+ return;
962
+ }
963
+ activeCurators.set(callId, handle), pc.curatorUrl = handle.url;
964
+ for (let [qi, data] of pc.searchResults)
965
+ data.error ? handle.pushError(qi, data.error, data.provider) : handle.pushResult(qi, {
966
+ answer: data.answer,
967
+ results: data.results.map((r) => ({ title: r.title, url: r.url, domain: extractDomain(r.url) })),
968
+ provider: data.provider || pc.defaultProvider
969
+ });
970
+ searchesComplete && handle.searchesDone();
971
+ let sendCuratorFallbackUpdate2 = (message) => {
972
+ pc.onUpdate?.({
973
+ content: [{ type: "text", text: `${message}
974
+ Open manually: ${handle.url}` }],
975
+ details: {
976
+ phase: "curator-fallback",
977
+ progress: searchesComplete ? 1 : 0.5,
978
+ curatorUrl: handle.url,
979
+ timeoutSeconds: pc.timeoutSeconds,
980
+ shortcut: curateKey,
981
+ browserOpenError: pc.browserOpenError
982
+ }
983
+ });
984
+ };
985
+ pc.onUpdate?.({
986
+ content: [{ type: "text", text: searchesComplete ? "Waiting for summary approval in browser..." : "Searches streaming to browser..." }],
987
+ details: {
988
+ phase: "curating",
989
+ progress: searchesComplete ? 1 : 0.5,
990
+ curatorUrl: handle.url,
991
+ timeoutSeconds: pc.timeoutSeconds,
992
+ shortcut: curateKey
993
+ }
994
+ });
995
+ let open = platform() === "darwin" ? await getGlimpseOpen() : null;
996
+ if (open)
997
+ try {
998
+ let win = openInGlimpse(open, handle.url, "Search Curator");
999
+ glimpseWins.set(callId, win), win.on("closed", () => {
1000
+ glimpseWins.get(callId) === win && (glimpseWins.delete(callId), closeCurator(callId));
1001
+ });
1002
+ return;
1003
+ } catch (err) {
1004
+ let message = err instanceof Error ? err.message : String(err);
1005
+ console.error(`Failed to open Glimpse curator window: ${message}`), glimpseWins.delete(callId);
1006
+ }
1007
+ await openInBrowser(pi, handle.url);
1008
+ } catch (err) {
1009
+ let message = err instanceof Error ? err.message : String(err);
1010
+ console.error(`Failed to open curator UI: ${message}`), handle && activeCurators.get(callId) === handle && pendingCurates.get(callId) === pc ? (pc.browserOpenError = message, sendCuratorFallbackUpdate("Search curator is running, but the browser did not open automatically.")) : (pendingCurates.get(callId) === pc || handle && activeCurators.get(callId) === handle) && closeCurator(callId);
1011
+ }
1012
+ }
1013
+ pi.registerShortcut(curateKey, {
1014
+ description: "Review search results",
1015
+ handler: async (ctx2) => {
1016
+ let entries = [...pendingCurates.entries()];
1017
+ if (entries.length === 0)
1018
+ return;
1019
+ let [callId, pc] = entries[entries.length - 1];
1020
+ if (pc.phase === "searching") {
1021
+ pc.browserPromise = openCuratorBrowser(callId, pc, !1), ctx2.ui.notify("Opening curator \u2014 remaining searches will stream in", "info");
1022
+ return;
1023
+ }
1024
+ }
1025
+ }), pi.registerShortcut(activityKey, {
1026
+ description: "Toggle web search activity",
1027
+ handler: async (ctx2) => {
1028
+ widgetVisible = !widgetVisible, widgetVisible ? (widgetUnsubscribe = activityMonitor.onUpdate(() => updateWidget(ctx2)), updateWidget(ctx2)) : (widgetUnsubscribe?.(), widgetUnsubscribe = null, ctx2.ui.setWidget("web-activity", void 0));
1029
+ }
1030
+ }), pi.on("session_start", async (_event, ctx2) => handleSessionChange(ctx2)), pi.on("session_tree", async (_event, ctx2) => handleSessionChange(ctx2)), pi.on("session_shutdown", () => {
1031
+ sessionGeneration++, sessionAbort.abort(), sessionActive = !1, abortPendingFetches(), closeCurator(), runSessionCleanups(), clearResults(), widgetUnsubscribe?.(), widgetUnsubscribe = null, activityMonitor.clear(), widgetVisible = !1;
1032
+ }), initConfig.webSearch?.enabled !== !1 && pi.registerTool({
1033
+ name: "web_search",
1034
+ label: "Web Search",
1035
+ description: 'Search the web using OpenAI, Brave, Parallel, Tavily, Exa, Perplexity, or Gemini. Returns an AI-synthesized answer with source citations. OpenAI web_search uses a Codex subscription or OpenAI API key. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query \u2014 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. Provider auto-selects: OpenAI when suitable and available, then Exa, Brave, Parallel, Tavily, Perplexity, Gemini API, then Gemini Web.',
1036
+ promptSnippet: "Use for web research questions. Prefer {queries:[...]} with 2-4 varied angles over a single query for broader coverage.",
1037
+ parameters: Type.Object({
1038
+ query: Type.Optional(Type.String({ description: "Single search query. For research tasks, prefer 'queries' with multiple varied angles instead." })),
1039
+ queries: Type.Optional(Type.Array(Type.String(), { description: "Multiple queries searched in sequence, each returning its own synthesized answer. Prefer this for research \u2014 vary phrasing, scope, and angle across 2-4 queries to maximize coverage. Good: ['React vs Vue performance benchmarks 2026', 'React vs Vue developer experience comparison', 'React ecosystem size vs Vue ecosystem']. Bad: ['React vs Vue', 'React vs Vue comparison', 'React vs Vue review'] (too similar, redundant results)." })),
1040
+ numResults: Type.Optional(Type.Number({ description: "Results per query (default: 5, max: 20)" })),
1041
+ includeContent: Type.Optional(Type.Boolean({ description: "Fetch full page content (async)" })),
1042
+ recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency" })),
1043
+ domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
1044
+ provider: Type.Optional(StringEnum(["auto", "openai", "brave", "parallel", "tavily", "exa", "perplexity", "gemini"], { description: "Search provider (default: auto)" })),
1045
+ workflow: Type.Optional(StringEnum(["none", "summary-review", "auto-summary"], {
1046
+ description: "Search workflow mode: none = no curator, summary-review = open curator with auto summary draft (default), auto-summary = generate summary without opening curator"
1047
+ }))
1048
+ }),
1049
+ async execute(callId, params, incomingSignal, onUpdate, ctx2) {
1050
+ let generation = sessionGeneration, signal = incomingSignal ? AbortSignal.any([incomingSignal, sessionAbort.signal]) : sessionAbort.signal;
1051
+ assertActiveRequest(generation, signal);
1052
+ let rawQueryList = Array.isArray(params.queries) ? params.queries : params.query !== void 0 ? [params.query] : [], queryList = normalizeQueryList(rawQueryList), configWorkflow = loadConfigForExtensionInit().workflow, workflow = resolveWorkflow(params.workflow ?? configWorkflow, ctx2?.hasUI !== !1), shouldCurate = workflow === "summary-review";
1053
+ if (queryList.length === 0)
1054
+ return {
1055
+ content: [{ type: "text", text: "Error: No query provided. Use 'query' or 'queries' parameter." }],
1056
+ details: { error: "No query provided" }
1057
+ };
1058
+ if (shouldCurate && !ctx2)
1059
+ return {
1060
+ content: [{ type: "text", text: "Error: Curation requires an active extension context." }],
1061
+ details: { error: "Missing extension context" }
1062
+ };
1063
+ if (shouldCurate) {
1064
+ closeCurator(callId);
1065
+ let resolvePromise = () => {
1066
+ }, promise = new Promise((resolve) => {
1067
+ resolvePromise = resolve;
1068
+ }), includeContent = params.includeContent ?? !1, searchResults2 = /* @__PURE__ */ new Map(), allInlineContent2 = [], searchAbort = new AbortController(), searchSignal = signal ? AbortSignal.any([signal, searchAbort.signal]) : searchAbort.signal, cancelled = !1, bootstrap = await loadCuratorBootstrap(params.provider, ctx2, {
1069
+ numResults: params.numResults,
1070
+ recencyFilter: params.recencyFilter
1071
+ });
1072
+ assertActiveRequest(generation, signal);
1073
+ let availableProviders = bootstrap.availableProviders, defaultProvider = bootstrap.defaultProvider, searchProvider = (normalizeProviderInput(params.provider ?? loadConfig().provider ?? "auto") ?? "auto") === "auto" ? "auto" : defaultProvider, curatorTimeoutSeconds = bootstrap.timeoutSeconds, curatorWorkflow = "summary-review", summaryContext = {
1074
+ model: ctx2.model,
1075
+ modelRegistry: ctx2.modelRegistry,
1076
+ cwd: ctx2.cwd,
1077
+ isProjectTrusted: () => ctx2.isProjectTrusted()
1078
+ }, summaryModelChoices = await loadSummaryModelChoices(summaryContext);
1079
+ assertActiveRequest(generation, signal);
1080
+ let pc = {
1081
+ phase: "searching",
1082
+ workflow: curatorWorkflow,
1083
+ summaryContext,
1084
+ searchResults: searchResults2,
1085
+ allInlineContent: allInlineContent2,
1086
+ queryList,
1087
+ includeContent,
1088
+ numResults: params.numResults,
1089
+ recencyFilter: params.recencyFilter,
1090
+ domainFilter: params.domainFilter,
1091
+ availableProviders,
1092
+ defaultProvider,
1093
+ searchProvider,
1094
+ summaryModels: summaryModelChoices.summaryModels,
1095
+ defaultSummaryModel: summaryModelChoices.defaultSummaryModel,
1096
+ timeoutSeconds: curatorTimeoutSeconds,
1097
+ onUpdate,
1098
+ signal,
1099
+ abortSearches: () => {
1100
+ searchAbort.signal.aborted || searchAbort.abort();
1101
+ },
1102
+ finish: () => {
1103
+ },
1104
+ cancel: () => {
1105
+ }
1106
+ }, finish = (value) => {
1107
+ cancelled || (cancelled = !0, pc.abortSearches(), signal?.removeEventListener("abort", onAbort), pendingCurates.delete(callId), resolvePromise(value));
1108
+ }, cancel = (reason = "stale") => {
1109
+ if (cancelled)
1110
+ return;
1111
+ let conn = activeCurators.get(callId)?.getConnectionState();
1112
+ finish(buildCurationCancelledReturn(reason, {
1113
+ queries: Array.from(searchResults2.values()),
1114
+ queryCount: queryList.length,
1115
+ browserConnected: conn?.browserConnected,
1116
+ lastHeartbeatAgeMs: conn?.lastHeartbeatAgeMs,
1117
+ curatorUrl: pc.curatorUrl,
1118
+ browserOpenError: pc.browserOpenError
1119
+ }));
1120
+ };
1121
+ pc.finish = finish, pc.cancel = cancel;
1122
+ let onAbort = () => closeCurator(callId);
1123
+ pendingCurates.set(callId, pc), signal?.addEventListener("abort", onAbort, { once: !0 }), pc.browserPromise = openCuratorBrowser(callId, pc, !1);
1124
+ for (let qi = 0; qi < queryList.length && !(signal?.aborted || cancelled || searchAbort.signal.aborted); qi++) {
1125
+ onUpdate?.({
1126
+ content: [{ type: "text", text: `Searching ${qi + 1}/${queryList.length}: "${queryList[qi]}"...` }],
1127
+ details: { phase: "searching", progress: qi / queryList.length, currentQuery: queryList[qi] }
1128
+ });
1129
+ let requestedProvider = pc.searchProvider;
1130
+ try {
1131
+ let { answer, results, inlineContent, provider } = await runSearch(queryList[qi], {
1132
+ provider: requestedProvider,
1133
+ numResults: params.numResults,
1134
+ recencyFilter: params.recencyFilter,
1135
+ domainFilter: params.domainFilter,
1136
+ includeContent: params.includeContent,
1137
+ signal: searchSignal,
1138
+ extensionContext: ctx2
1139
+ });
1140
+ if (signal?.aborted || cancelled || searchAbort.signal.aborted)
1141
+ break;
1142
+ searchResults2.set(qi, { query: queryList[qi], answer, results, error: null, provider }), inlineContent && allInlineContent2.push(...inlineContent);
1143
+ let curator2 = activeCurators.get(callId);
1144
+ curator2 && curator2.pushResult(qi, {
1145
+ answer,
1146
+ results: results.map((r) => ({ title: r.title, url: r.url, domain: extractDomain(r.url) })),
1147
+ provider
1148
+ });
1149
+ } catch (err) {
1150
+ if (signal?.aborted || cancelled || searchAbort.signal.aborted)
1151
+ break;
1152
+ let message = err instanceof Error ? err.message : String(err);
1153
+ searchResults2.set(qi, { query: queryList[qi], answer: "", results: [], error: message, provider: requestedProvider });
1154
+ let curator2 = activeCurators.get(callId);
1155
+ curator2 && curator2.pushError(qi, message, requestedProvider);
1156
+ }
1157
+ }
1158
+ if (signal?.aborted || cancelled || searchAbort.signal.aborted)
1159
+ return cancel(), promise;
1160
+ await pc.browserPromise;
1161
+ let curator = activeCurators.get(callId);
1162
+ return curator && !cancelled && (curator.searchesDone(), pc.browserOpenError ? pc.onUpdate?.({
1163
+ content: [{ type: "text", text: `All searches complete. Open the curator manually: ${pc.curatorUrl}` }],
1164
+ details: {
1165
+ phase: "curator-fallback",
1166
+ progress: 1,
1167
+ curatorUrl: pc.curatorUrl,
1168
+ timeoutSeconds: pc.timeoutSeconds,
1169
+ shortcut: curateKey,
1170
+ browserOpenError: pc.browserOpenError
1171
+ }
1172
+ }) : pc.onUpdate?.({
1173
+ content: [{ type: "text", text: "All searches complete \u2014 waiting for summary approval in browser..." }],
1174
+ details: {
1175
+ phase: "curating",
1176
+ progress: 1,
1177
+ curatorUrl: pc.curatorUrl,
1178
+ timeoutSeconds: pc.timeoutSeconds,
1179
+ shortcut: curateKey
1180
+ }
1181
+ })), promise;
1182
+ }
1183
+ let searchResults = [], allUrls = [], allInlineContent = [], resolvedProvider = normalizeProviderInput(params.provider ?? loadConfig().provider);
1184
+ for (let i = 0; i < queryList.length; i++) {
1185
+ assertActiveRequest(generation, signal);
1186
+ let query = queryList[i];
1187
+ onUpdate?.({
1188
+ content: [{ type: "text", text: `Searching ${i + 1}/${queryList.length}: "${query}"...` }],
1189
+ details: { phase: "search", progress: i / queryList.length, currentQuery: query }
1190
+ });
1191
+ try {
1192
+ let { answer, results, inlineContent, provider } = await runSearch(query, {
1193
+ provider: resolvedProvider,
1194
+ numResults: params.numResults,
1195
+ recencyFilter: params.recencyFilter,
1196
+ domainFilter: params.domainFilter,
1197
+ includeContent: params.includeContent,
1198
+ signal,
1199
+ extensionContext: ctx2
1200
+ });
1201
+ assertActiveRequest(generation, signal), searchResults.push({ query, answer, results, error: null, provider });
1202
+ for (let r of results)
1203
+ allUrls.includes(r.url) || allUrls.push(r.url);
1204
+ inlineContent && allInlineContent.push(...inlineContent);
1205
+ } catch (err) {
1206
+ assertActiveRequest(generation, signal);
1207
+ let message = err instanceof Error ? err.message : String(err), requestedProvider = typeof resolvedProvider == "string" && resolvedProvider !== "auto" ? resolvedProvider : void 0;
1208
+ searchResults.push({ query, answer: "", results: [], error: message, provider: requestedProvider });
1209
+ }
1210
+ }
1211
+ let approvedSummary, summaryMeta;
1212
+ if (workflow === "auto-summary") {
1213
+ if (!ctx2)
1214
+ return {
1215
+ content: [{ type: "text", text: "Error: Auto-summary requires an active extension context." }],
1216
+ details: { error: "Missing extension context" }
1217
+ };
1218
+ onUpdate?.({
1219
+ content: [{ type: "text", text: "Generating summary..." }],
1220
+ details: { phase: "generating-summary", progress: 1 }
1221
+ });
1222
+ let summaryContext = {
1223
+ model: ctx2.model,
1224
+ modelRegistry: ctx2.modelRegistry,
1225
+ cwd: ctx2.cwd,
1226
+ isProjectTrusted: () => ctx2.isProjectTrusted()
1227
+ }, summaryModelChoices = await loadSummaryModelChoices(summaryContext), generated = await (await ensureSummaryReview()).generateSummaryDraft(searchResults, summaryContext, signal, summaryModelChoices.defaultSummaryModel ?? void 0);
1228
+ approvedSummary = generated.summary, summaryMeta = generated.meta;
1229
+ }
1230
+ return assertActiveRequest(generation, signal), buildSearchReturn({
1231
+ queryList,
1232
+ results: searchResults,
1233
+ urls: allUrls,
1234
+ includeContent: params.includeContent ?? !1,
1235
+ inlineContent: allInlineContent.length > 0 ? allInlineContent : void 0,
1236
+ workflow: workflow === "auto-summary" ? "auto-summary" : void 0,
1237
+ approvedSummary,
1238
+ summaryMeta
1239
+ });
1240
+ },
1241
+ renderCall(args, theme, context) {
1242
+ let queryList = collectSearchQueries(args), compact = !!(context && !context.isPartial && !context.expanded && !context.isError);
1243
+ if (formatSearchCallTitle(queryList).empty)
1244
+ return new Text(theme.fg("toolTitle", theme.bold("search ")) + theme.fg("error", "(no query)"), 0, 0);
1245
+ let detail = formatSearchDetail(queryList, compact ? context?.result?.details : void 0);
1246
+ return new Text(formatGroupedCall({
1247
+ role: context?.groupRole ?? "singleton",
1248
+ compact,
1249
+ tree: context ? toolGroupTree(context) : !1,
1250
+ dot: context ? toolStatusDotFromContext(context, theme) : theme.fg("success", "\u25CF"),
1251
+ title: theme.fg("toolTitle", theme.bold("search")),
1252
+ detail: theme.fg("accent", detail)
1253
+ }), 0, 0);
1254
+ },
1255
+ renderResult(result, { expanded, isPartial }, theme, context) {
1256
+ let details = result.details;
1257
+ if (isPartial) {
1258
+ if (details?.phase === "curator-fallback") {
1259
+ let lines = [theme.fg("warning", "Open the search curator manually:")];
1260
+ details?.curatorUrl && lines.push(theme.fg("muted", ` ${details.curatorUrl}`)), details?.browserOpenError && lines.push(theme.fg("dim", ` auto-open failed: ${details.browserOpenError}`));
1261
+ let timeout = typeof details?.timeoutSeconds == "number" ? details.timeoutSeconds : void 0, shortcut = typeof details?.shortcut == "string" ? details.shortcut : curateKey;
1262
+ return lines.push(theme.fg("dim", timeout ? ` auto-submits after ${timeout}s idle; ${shortcut} reopens` : ` ${shortcut} reopens`)), new Text(lines.join(`
1263
+ `), 0, 0);
1264
+ }
1265
+ if (details?.phase === "curating" || details?.phase === "waiting-for-approval" || details?.phase === "generating-summary") {
1266
+ let phaseText = details?.phase === "generating-summary" ? "generating summary draft..." : details?.phase === "waiting-for-approval" ? "summary draft ready; approve in browser..." : "waiting for summary approval in browser...", lines = [theme.fg("accent", phaseText)];
1267
+ details?.curatorUrl && lines.push(theme.fg("muted", ` ${details.curatorUrl}`));
1268
+ let timeout = typeof details?.timeoutSeconds == "number" ? details.timeoutSeconds : void 0, shortcut = typeof details?.shortcut == "string" ? details.shortcut : curateKey;
1269
+ return timeout ? lines.push(theme.fg("dim", ` auto-submits after ${timeout}s idle; ${shortcut} reopens`)) : lines.push(theme.fg("dim", ` ${shortcut} reopens`)), new Text(lines.join(`
1270
+ `), 0, 0);
1271
+ }
1272
+ if (details?.phase === "searching") {
1273
+ let progress2 = details?.progress ?? 0, bar2 = "\u2588".repeat(Math.floor(progress2 * 10)) + "\u2591".repeat(10 - Math.floor(progress2 * 10)), query = details?.currentQuery || "", display = query.length > 40 ? query.slice(0, 37) + "..." : query;
1274
+ return new Text(theme.fg("accent", `[${bar2}] ${display}`), 0, 0);
1275
+ }
1276
+ let progress = details?.progress ?? 0, bar = "\u2588".repeat(Math.floor(progress * 10)) + "\u2591".repeat(10 - Math.floor(progress * 10));
1277
+ return new Text(theme.fg("accent", `[${bar}] ${details?.phase || "searching"}`), 0, 0);
1278
+ }
1279
+ if (details?.error) {
1280
+ let plan = buildSearchErrorPlan(details);
1281
+ return plan ? renderSearchErrorPlan(plan, expanded, theme) : new Text(theme.fg("error", `Error: ${details.error}`), 0, 0);
1282
+ }
1283
+ let queryList = collectSearchQueries(context?.args), fallbackQueries = (details?.curatedQueries ?? []).map((cq) => cq.query).filter(Boolean), chrome = formatSearchChrome({
1284
+ queries: queryList.length > 0 ? queryList : fallbackQueries,
1285
+ details,
1286
+ expanded
1287
+ }), statusColor = expanded ? "accent" : "success";
1288
+ return new Text(chrome.result.map((line) => theme.fg(statusColor, line)).join(`
1289
+ `), 0, 0);
1290
+ }
1291
+ }), pi.registerTool({
1292
+ name: "fetch_content",
1293
+ label: "Fetch Content",
1294
+ description: "Fetch URL(s) and extract readable content as markdown. Supports YouTube video transcripts (with thumbnail), GitHub repository contents, and local video files (with frame thumbnail). Video frames can be extracted via timestamp/range or sampled across the entire video with frames alone. Falls back to Gemini for pages that block bots or fail Readability extraction. For YouTube and video files: ALWAYS pass the user's specific question via the prompt parameter \u2014 this directs the AI to focus on that aspect of the video, producing much better results than a generic extraction. Content is always stored and can be retrieved with get_search_content.",
1295
+ promptSnippet: "Use to extract readable content from URL(s), YouTube, GitHub repos, or local videos. For video questions, pass the user's exact question in prompt.",
1296
+ parameters: Type.Object({
1297
+ url: Type.Optional(Type.String({ description: "Single URL to fetch" })),
1298
+ urls: Type.Optional(Type.Array(Type.String(), { description: "Multiple URLs (parallel)" })),
1299
+ forceClone: Type.Optional(Type.Boolean({
1300
+ description: "Force cloning large GitHub repositories that exceed the size threshold"
1301
+ })),
1302
+ prompt: Type.Optional(Type.String({
1303
+ description: "Question or instruction for video analysis (YouTube and video files). Pass the user's specific question here \u2014 e.g. 'describe the book shown at the advice for beginners section'. Without this, a generic transcript extraction is used which may miss what the user is asking about."
1304
+ })),
1305
+ timestamp: Type.Optional(Type.String({
1306
+ 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 \u2014 you'll get a contact sheet to visually identify the right frame."
1307
+ })),
1308
+ frames: Type.Optional(Type.Integer({
1309
+ minimum: 1,
1310
+ maximum: 12,
1311
+ description: "Number of frames to extract. Use with timestamp range for custom density, with single timestamp to get N frames at 5s intervals, or alone to sample across the entire video. Requires yt-dlp + ffmpeg for YouTube, ffmpeg for local video."
1312
+ })),
1313
+ model: Type.Optional(Type.String({
1314
+ description: "Override the Gemini model for video/YouTube analysis (e.g. 'gemini-2.5-flash', 'gemini-3-flash-preview'). Defaults to config or gemini-3-flash-preview."
1315
+ }))
1316
+ }),
1317
+ async execute(_toolCallId, params, incomingSignal, onUpdate) {
1318
+ let generation = sessionGeneration, signal = incomingSignal ? AbortSignal.any([incomingSignal, sessionAbort.signal]) : sessionAbort.signal;
1319
+ assertActiveRequest(generation, signal);
1320
+ let { urlList, options } = normalizeFetchContentParams(params);
1321
+ if (urlList.length === 0)
1322
+ return {
1323
+ content: [{ type: "text", text: "Error: No URL provided." }],
1324
+ details: { error: "No URL provided" }
1325
+ };
1326
+ onUpdate?.({
1327
+ content: [{ type: "text", text: `Fetching ${urlList.length} URL(s)...` }],
1328
+ details: { phase: "fetch", progress: 0 }
1329
+ });
1330
+ let { fetchAllContent } = await loadExtract();
1331
+ if (signal?.aborted)
1332
+ return {
1333
+ content: [{ type: "text", text: "Error: Aborted" }],
1334
+ details: { error: "Aborted", urls: urlList, urlCount: urlList.length, successful: 0 }
1335
+ };
1336
+ let fetchResults = await fetchAllContent(urlList, signal, options);
1337
+ assertActiveRequest(generation, signal);
1338
+ let successful = fetchResults.filter((r) => !r.error).length, totalChars = fetchResults.reduce((sum, r) => sum + r.content.length, 0), responseId = generateId(), data = {
1339
+ id: responseId,
1340
+ type: "fetch",
1341
+ timestamp: Date.now(),
1342
+ urls: stripThumbnails(fetchResults)
1343
+ };
1344
+ if (storeResult(responseId, data), pi.appendEntry("web-search-results", data), urlList.length === 1) {
1345
+ let result = fetchResults[0];
1346
+ if (result.error)
1347
+ return {
1348
+ content: [{ type: "text", text: `Error: ${result.error}` }],
1349
+ details: { urls: urlList, urlCount: 1, successful: 0, error: result.error, responseId, prompt: params.prompt, timestamp: params.timestamp, frames: params.frames }
1350
+ };
1351
+ let fullLength = result.content.length, truncated = fullLength > MAX_INLINE_CONTENT, output2 = truncated ? result.content.slice(0, MAX_INLINE_CONTENT) + `
1352
+
1353
+ [Content truncated...]` : result.content;
1354
+ truncated && (output2 += `
1355
+
1356
+ ---
1357
+ Showing ${MAX_INLINE_CONTENT} of ${fullLength} chars. Use get_search_content({ responseId: "${responseId}", urlIndex: 0 }) for full content.`);
1358
+ let content = [];
1359
+ if (result.frames?.length)
1360
+ for (let frame of result.frames)
1361
+ content.push({ type: "image", data: frame.data, mimeType: frame.mimeType }), content.push({ type: "text", text: `Frame at ${frame.timestamp}` });
1362
+ else result.thumbnail && content.push({ type: "image", data: result.thumbnail.data, mimeType: result.thumbnail.mimeType });
1363
+ content.push({ type: "text", text: output2 });
1364
+ let imageCount = (result.frames?.length ?? 0) + (result.thumbnail ? 1 : 0);
1365
+ return {
1366
+ content,
1367
+ details: {
1368
+ urls: urlList,
1369
+ urlCount: 1,
1370
+ successful: 1,
1371
+ totalChars: fullLength,
1372
+ title: result.title,
1373
+ responseId,
1374
+ truncated,
1375
+ hasImage: imageCount > 0,
1376
+ imageCount,
1377
+ prompt: params.prompt,
1378
+ timestamp: params.timestamp,
1379
+ frames: params.frames,
1380
+ duration: result.duration
1381
+ }
1382
+ };
1383
+ }
1384
+ let output = `## Fetched URLs
1385
+
1386
+ `;
1387
+ for (let { url, title, content, error } of fetchResults)
1388
+ error ? output += `- ${url}: Error - ${error}
1389
+ ` : output += `- ${title || url} (${content.length} chars)
1390
+ `;
1391
+ return output += `
1392
+ ---
1393
+ Use get_search_content({ responseId: "${responseId}", urlIndex: 0 }) to retrieve full content.`, {
1394
+ content: [{ type: "text", text: output }],
1395
+ details: { urls: urlList, urlCount: urlList.length, successful, totalChars, responseId }
1396
+ };
1397
+ },
1398
+ renderCall(args, theme, context) {
1399
+ let urlList = collectFetchUrls(args), compact = !!(context && !context.isPartial && !context.expanded && !context.isError);
1400
+ if (formatFetchCallTitle(urlList).empty)
1401
+ return new Text(theme.fg("toolTitle", theme.bold("fetch ")) + theme.fg("error", "(no URL)"), 0, 0);
1402
+ let detail = formatFetchDetail(urlList, compact ? context?.result?.details : void 0);
1403
+ return new Text(formatGroupedCall({
1404
+ role: context?.groupRole ?? "singleton",
1405
+ compact,
1406
+ tree: context ? toolGroupTree(context) : !1,
1407
+ dot: context ? toolStatusDotFromContext(context, theme) : theme.fg("success", "\u25CF"),
1408
+ title: theme.fg("toolTitle", theme.bold("fetch")),
1409
+ detail: theme.fg("accent", detail)
1410
+ }), 0, 0);
1411
+ },
1412
+ renderResult(result, { expanded, isPartial }, theme, context) {
1413
+ let details = result.details;
1414
+ if (isPartial) {
1415
+ let progress = details?.progress ?? 0, bar = "\u2588".repeat(Math.floor(progress * 10)) + "\u2591".repeat(10 - Math.floor(progress * 10));
1416
+ return new Text(theme.fg("accent", `[${bar}] ${details?.phase || "fetching"}`), 0, 0);
1417
+ }
1418
+ if (details?.error) {
1419
+ let fd = details, extras = [];
1420
+ if ((typeof fd.urlCount == "number" || typeof fd.successful == "number") && extras.push(`urls: ${fd.successful ?? 0}/${fd.urlCount ?? 0} succeeded`), fd.responseId && extras.push(`response id: ${fd.responseId}`), fd.urls && fd.urls.length > 0) {
1421
+ for (let u of fd.urls.slice(0, 8))
1422
+ extras.push(` \u25B8 ${u}`);
1423
+ fd.urls.length > 8 && extras.push(` ... and ${fd.urls.length - 8} more`);
1424
+ }
1425
+ let plan = buildSearchErrorPlan({ error: details.error, extraLines: extras });
1426
+ return plan ? renderSearchErrorPlan(plan, expanded, theme) : new Text(theme.fg("error", `Error: ${details.error}`), 0, 0);
1427
+ }
1428
+ let urlList = collectFetchUrls(context?.args), fallbackUrls = Array.isArray(details?.urls) ? details.urls : [], chrome = formatFetchChrome({
1429
+ urls: urlList.length > 0 ? urlList : fallbackUrls,
1430
+ details,
1431
+ expanded
1432
+ }), statusColor = expanded ? "accent" : (details?.successful ?? 0) > 0 || details?.urlCount === 1 ? "success" : "error";
1433
+ return new Text(chrome.result.map((line) => theme.fg(statusColor, line)).join(`
1434
+ `), 0, 0);
1435
+ }
1436
+ }), pi.registerTool({
1437
+ name: "get_search_content",
1438
+ label: "Get Search Content",
1439
+ description: "Retrieve full content from a previous web_search or fetch_content call.",
1440
+ promptSnippet: "Use after web_search/fetch_content when full stored content is needed via responseId plus query/url selectors.",
1441
+ parameters: Type.Object({
1442
+ responseId: Type.String({ description: "The responseId from web_search or fetch_content" }),
1443
+ query: Type.Optional(Type.String({ description: "Get content for this query (web_search)" })),
1444
+ queryIndex: Type.Optional(Type.Number({ description: "Get content for query at index" })),
1445
+ url: Type.Optional(Type.String({ description: "Get content for this URL" })),
1446
+ urlIndex: Type.Optional(Type.Number({ description: "Get content for URL at index" }))
1447
+ }),
1448
+ async execute(_toolCallId, params) {
1449
+ let data = getResult(params.responseId);
1450
+ if (!data)
1451
+ return {
1452
+ content: [{ type: "text", text: `Error: No stored results for "${params.responseId}"` }],
1453
+ details: { error: "Not found", responseId: params.responseId }
1454
+ };
1455
+ if (data.type === "search" && data.queries) {
1456
+ let queryData;
1457
+ if (params.query !== void 0) {
1458
+ if (queryData = data.queries.find((q) => q.query === params.query), !queryData) {
1459
+ let available = data.queries.map((q) => `"${q.query}"`).join(", ");
1460
+ return {
1461
+ content: [{ type: "text", text: `Query "${params.query}" not found. Available: ${available}` }],
1462
+ details: { error: "Query not found" }
1463
+ };
1464
+ }
1465
+ } else if (params.queryIndex !== void 0) {
1466
+ if (queryData = data.queries[params.queryIndex], !queryData)
1467
+ return {
1468
+ content: [{ type: "text", text: `Index ${params.queryIndex} out of range (0-${data.queries.length - 1})` }],
1469
+ details: { error: "Index out of range" }
1470
+ };
1471
+ } else
1472
+ return {
1473
+ content: [{ type: "text", text: `Specify query or queryIndex. Available: ${data.queries.map((q, i) => `${i}: "${q.query}"`).join(", ")}` }],
1474
+ details: { error: "No query specified" }
1475
+ };
1476
+ return queryData.error ? {
1477
+ content: [{ type: "text", text: `Error for "${queryData.query}": ${queryData.error}` }],
1478
+ details: { error: queryData.error, query: queryData.query }
1479
+ } : {
1480
+ content: [{ type: "text", text: formatFullResults(queryData) }],
1481
+ details: { query: queryData.query, resultCount: queryData.results.length }
1482
+ };
1483
+ }
1484
+ if (data.type === "fetch" && data.urls) {
1485
+ let urlData;
1486
+ if (params.url !== void 0) {
1487
+ if (urlData = data.urls.find((u) => u.url === params.url), !urlData)
1488
+ return {
1489
+ content: [{ type: "text", text: `URL not found. Available:
1490
+ ${data.urls.map((u) => u.url).join(`
1491
+ `)}` }],
1492
+ details: { error: "URL not found" }
1493
+ };
1494
+ } else if (params.urlIndex !== void 0) {
1495
+ if (urlData = data.urls[params.urlIndex], !urlData)
1496
+ return {
1497
+ content: [{ type: "text", text: `Index ${params.urlIndex} out of range (0-${data.urls.length - 1})` }],
1498
+ details: { error: "Index out of range" }
1499
+ };
1500
+ } else
1501
+ return {
1502
+ content: [{ type: "text", text: `Specify url or urlIndex. Available:
1503
+ ${data.urls.map((u, i) => `${i}: ${u.url}`).join(`
1504
+ `)}` }],
1505
+ details: { error: "No URL specified" }
1506
+ };
1507
+ return urlData.error ? {
1508
+ content: [{ type: "text", text: `Error for ${urlData.url}: ${urlData.error}` }],
1509
+ details: { error: urlData.error, url: urlData.url }
1510
+ } : {
1511
+ content: [{ type: "text", text: `# ${urlData.title}
1512
+
1513
+ ${urlData.content}` }],
1514
+ details: { url: urlData.url, title: urlData.title, contentLength: urlData.content.length }
1515
+ };
1516
+ }
1517
+ return {
1518
+ content: [{ type: "text", text: "Invalid stored data format" }],
1519
+ details: { error: "Invalid data" }
1520
+ };
1521
+ },
1522
+ renderCall(args, theme) {
1523
+ let { responseId, query, queryIndex, url, urlIndex } = args, target = "";
1524
+ return query ? target = `query="${query}"` : queryIndex !== void 0 ? target = `queryIndex=${queryIndex}` : url ? target = url.length > 30 ? url.slice(0, 27) + "..." : url : urlIndex !== void 0 && (target = `urlIndex=${urlIndex}`), new Text(theme.fg("toolTitle", theme.bold("get_content ")) + theme.fg("accent", target || responseId.slice(0, 8)), 0, 0);
1525
+ },
1526
+ renderResult(result, { expanded }, theme) {
1527
+ let details = result.details;
1528
+ if (details?.error) {
1529
+ let extras = [];
1530
+ details.query && extras.push(`query: ${details.query}`), details.url ? extras.push(`url: ${details.url}`) : details.title && extras.push(`resource: ${details.title}`);
1531
+ let plan = buildSearchErrorPlan({ error: details.error, extraLines: extras });
1532
+ return plan ? renderSearchErrorPlan(plan, expanded, theme) : new Text(theme.fg("error", `Error: ${details.error}`), 0, 0);
1533
+ }
1534
+ let statusLine;
1535
+ if (details?.query ? statusLine = theme.fg("success", `"${details.query}"`) + theme.fg("muted", ` (${details.resultCount} results)`) : statusLine = theme.fg("success", details?.title || "Content") + theme.fg("muted", ` (${details?.contentLength ?? 0} chars)`), !expanded)
1536
+ return new Text(statusLine, 0, 0);
1537
+ let textContent = result.content.find((c) => c.type === "text")?.text || "", preview = textContent.length > 500 ? textContent.slice(0, 500) + "..." : textContent;
1538
+ return new Text(statusLine + `
1539
+ ` + theme.fg("dim", preview), 0, 0);
1540
+ }
1541
+ }), pi.registerCommand("websearch", {
1542
+ description: "Open web search curator",
1543
+ handler: async (args, ctx2) => {
1544
+ let generation = sessionGeneration, sessionSignal = sessionAbort.signal, sessionToken = randomUUID(), commandCallId = `cmd:${sessionToken}`;
1545
+ closeCurator(commandCallId);
1546
+ let raw = args.trim(), queries = raw.length > 0 ? normalizeQueryList(raw.split(",")) : [], bootstrap;
1547
+ try {
1548
+ bootstrap = await loadCuratorBootstrap(void 0, ctx2);
1549
+ } catch (err) {
1550
+ if (generation !== sessionGeneration)
1551
+ return;
1552
+ let message = err instanceof Error ? err.message : String(err);
1553
+ ctx2.ui.notify(`Failed to load web search config: ${message}`, "error");
1554
+ return;
1555
+ }
1556
+ if (generation !== sessionGeneration)
1557
+ return;
1558
+ let availableProviders = bootstrap.availableProviders, initialProvider = bootstrap.defaultProvider, curatorTimeoutSeconds = bootstrap.timeoutSeconds, currentProvider = initialProvider, currentSearchProvider = (normalizeProviderInput(loadConfig().provider ?? "auto") ?? "auto") === "auto" ? "auto" : initialProvider, summaryContext = {
1559
+ model: ctx2.model,
1560
+ modelRegistry: ctx2.modelRegistry,
1561
+ cwd: ctx2.cwd,
1562
+ isProjectTrusted: () => ctx2.isProjectTrusted()
1563
+ }, summaryModelChoices = await loadSummaryModelChoices(summaryContext);
1564
+ if (generation !== sessionGeneration)
1565
+ return;
1566
+ ctx2.ui.notify("Opening web search curator...", "info");
1567
+ let collected = /* @__PURE__ */ new Map(), searchAbort = new AbortController(), searchSignal = AbortSignal.any([searchAbort.signal, sessionSignal]), aborted = !1, commandHandle = null, isCommandActive = () => generation === sessionGeneration && (commandHandle === null || activeCurators.get(commandCallId) === commandHandle);
1568
+ function sendFollowUpFromReturn(payload) {
1569
+ pi.sendMessage({
1570
+ customType: "web-search-results",
1571
+ content: payload.content,
1572
+ display: !0,
1573
+ details: payload.details
1574
+ }, { triggerTurn: !0, deliverAs: "followUp" });
1575
+ }
1576
+ try {
1577
+ let [{ startCuratorServer }] = await Promise.all([
1578
+ loadCuratorServer(),
1579
+ ensureSummaryReview()
1580
+ ]);
1581
+ if (!isCommandActive())
1582
+ return;
1583
+ let handle = await startCuratorServer({
1584
+ queries,
1585
+ sessionToken,
1586
+ timeout: curatorTimeoutSeconds,
1587
+ availableProviders,
1588
+ defaultProvider: initialProvider,
1589
+ searchProvider: currentSearchProvider,
1590
+ summaryModels: summaryModelChoices.summaryModels,
1591
+ defaultSummaryModel: summaryModelChoices.defaultSummaryModel
1592
+ }, {
1593
+ async onSummarize(selectedQueryIndices, summarizeSignal, model, feedback) {
1594
+ if (!isCommandActive())
1595
+ throw new Error("Curator session is no longer active.");
1596
+ return generateSummaryForSelectedIndices(selectedQueryIndices, collected, summaryContext, summarizeSignal, model, feedback);
1597
+ },
1598
+ onSubmit(payload) {
1599
+ if (!isCommandActive())
1600
+ return;
1601
+ aborted = !0, searchAbort.abort();
1602
+ let filtered = payload.selectedQueryIndices.length > 0 ? filterByQueryIndices(payload.selectedQueryIndices, collected) : collectAllResultsAndUrls(collected), base = {
1603
+ queryList: filtered.results.map((r) => r.query),
1604
+ results: filtered.results,
1605
+ urls: filtered.urls,
1606
+ includeContent: !1,
1607
+ curated: !0,
1608
+ curatedFrom: collected.size
1609
+ };
1610
+ if (!payload.rawResults) {
1611
+ let resolvedSummary = resolveSummaryForSubmit(payload, collected);
1612
+ base.workflow = "summary-review", base.approvedSummary = resolvedSummary.approvedSummary, base.summaryMeta = resolvedSummary.summaryMeta;
1613
+ }
1614
+ sendFollowUpFromReturn(buildSearchReturn(base)), closeCurator(commandCallId);
1615
+ },
1616
+ onCancel(reason) {
1617
+ if (isCommandActive()) {
1618
+ if (aborted = !0, searchAbort.abort(), reason === "timeout") {
1619
+ let all = collectAllResultsAndUrls(collected), resolvedSummary = resolveSummaryForSubmit({ selectedQueryIndices: [], summary: void 0, summaryMeta: void 0 }, collected);
1620
+ sendFollowUpFromReturn(buildSearchReturn({
1621
+ queryList: all.results.map((r) => r.query),
1622
+ results: all.results,
1623
+ urls: all.urls,
1624
+ includeContent: !1,
1625
+ curated: !0,
1626
+ curatedFrom: collected.size,
1627
+ workflow: "summary-review",
1628
+ approvedSummary: resolvedSummary.approvedSummary,
1629
+ summaryMeta: resolvedSummary.summaryMeta
1630
+ }));
1631
+ }
1632
+ closeCurator(commandCallId);
1633
+ }
1634
+ },
1635
+ onProviderChange(provider) {
1636
+ if (!isCommandActive())
1637
+ return;
1638
+ let normalized = normalizeProviderInput(provider);
1639
+ if (!(!normalized || normalized === "auto")) {
1640
+ currentProvider = normalized, currentSearchProvider = normalized;
1641
+ try {
1642
+ saveConfig({ provider: normalized });
1643
+ } catch (err) {
1644
+ let message = err instanceof Error ? err.message : String(err);
1645
+ console.error(`Failed to persist default provider: ${message}`);
1646
+ }
1647
+ }
1648
+ },
1649
+ async onAddSearch(query, queryIndex, provider) {
1650
+ if (!isCommandActive())
1651
+ throw new Error("Curator session is no longer active.");
1652
+ let normalizedProvider = normalizeProviderInput(provider), requestedProvider = !normalizedProvider || normalizedProvider === "auto" ? currentSearchProvider : normalizedProvider;
1653
+ try {
1654
+ let { answer, results, provider: actualProvider } = await runSearch(query, {
1655
+ provider: requestedProvider,
1656
+ signal: searchSignal,
1657
+ extensionContext: ctx2
1658
+ });
1659
+ if (!isCommandActive())
1660
+ throw new Error("Curator session is no longer active.");
1661
+ return collected.set(queryIndex, { query, answer, results, error: null, provider: actualProvider }), {
1662
+ answer,
1663
+ results: results.map((r) => ({ title: r.title, url: r.url, domain: extractDomain(r.url) })),
1664
+ provider: actualProvider
1665
+ };
1666
+ } catch (err) {
1667
+ let message = err instanceof Error ? err.message : String(err);
1668
+ throw isCommandActive() && collected.set(queryIndex, { query, answer: "", results: [], error: message, provider: requestedProvider }), err;
1669
+ }
1670
+ },
1671
+ async onRewriteQuery(query, rewriteSignal) {
1672
+ if (!isCommandActive())
1673
+ throw new Error("Curator session is no longer active.");
1674
+ return rewriteSearchQuery(query, summaryContext, rewriteSignal);
1675
+ }
1676
+ });
1677
+ if (!isCommandActive()) {
1678
+ handle.close();
1679
+ return;
1680
+ }
1681
+ commandHandle = handle, activeCurators.set(commandCallId, handle);
1682
+ let open = platform() === "darwin" ? await getGlimpseOpen() : null;
1683
+ if (!isCommandActive())
1684
+ return;
1685
+ let browserOpenError = null;
1686
+ if (open)
1687
+ try {
1688
+ let win = openInGlimpse(open, handle.url, "Search Curator");
1689
+ glimpseWins.set(commandCallId, win), win.on("closed", () => {
1690
+ glimpseWins.get(commandCallId) === win && (glimpseWins.delete(commandCallId), closeCurator(commandCallId));
1691
+ });
1692
+ } catch (err) {
1693
+ let message = err instanceof Error ? err.message : String(err);
1694
+ console.error(`Failed to open Glimpse curator window: ${message}`), glimpseWins.delete(commandCallId);
1695
+ try {
1696
+ await openInBrowser(pi, handle.url);
1697
+ } catch (browserErr) {
1698
+ browserOpenError = browserErr instanceof Error ? browserErr.message : String(browserErr);
1699
+ }
1700
+ }
1701
+ else
1702
+ try {
1703
+ await openInBrowser(pi, handle.url);
1704
+ } catch (browserErr) {
1705
+ browserOpenError = browserErr instanceof Error ? browserErr.message : String(browserErr);
1706
+ }
1707
+ if (!isCommandActive())
1708
+ return;
1709
+ browserOpenError && (console.error(`Failed to open curator UI: ${browserOpenError}`), ctx2.ui.notify(`Search curator is running, but the browser did not open automatically. Open manually: ${handle.url}`, "info")), queries.length > 0 ? (async () => {
1710
+ for (let qi = 0; qi < queries.length && !(aborted || !isCommandActive()); qi++) {
1711
+ let requestedProvider = currentSearchProvider;
1712
+ try {
1713
+ let { answer, results, provider } = await runSearch(queries[qi], {
1714
+ provider: requestedProvider,
1715
+ signal: searchSignal,
1716
+ extensionContext: ctx2
1717
+ });
1718
+ if (aborted || !isCommandActive())
1719
+ break;
1720
+ handle.pushResult(qi, {
1721
+ answer,
1722
+ results: results.map((r) => ({ title: r.title, url: r.url, domain: extractDomain(r.url) })),
1723
+ provider
1724
+ }), collected.set(qi, { query: queries[qi], answer, results, error: null, provider });
1725
+ } catch (err) {
1726
+ if (aborted || !isCommandActive())
1727
+ break;
1728
+ let message = err instanceof Error ? err.message : String(err);
1729
+ handle.pushError(qi, message, requestedProvider), collected.set(qi, { query: queries[qi], answer: "", results: [], error: message, provider: requestedProvider });
1730
+ }
1731
+ }
1732
+ !aborted && isCommandActive() && handle.searchesDone();
1733
+ })() : isCommandActive() && handle.searchesDone();
1734
+ } catch (err) {
1735
+ if (closeCurator(commandCallId), generation !== sessionGeneration)
1736
+ return;
1737
+ let message = err instanceof Error ? err.message : String(err);
1738
+ ctx2.ui.notify(`Failed to open curator: ${message}`, "error");
1739
+ }
1740
+ }
1741
+ }), pi.registerCommand("curator", {
1742
+ description: "Toggle or configure the search curator workflow",
1743
+ handler: async (args, ctx2) => {
1744
+ let arg = args.trim().toLowerCase(), newWorkflow;
1745
+ if (arg.length === 0)
1746
+ newWorkflow = resolveWorkflow(loadConfigForExtensionInit().workflow, !0) === "none" ? "summary-review" : "none";
1747
+ else if (arg === "on")
1748
+ newWorkflow = "summary-review";
1749
+ else if (arg === "off")
1750
+ newWorkflow = "none";
1751
+ else if (arg === "none" || arg === "summary-review" || arg === "auto-summary")
1752
+ newWorkflow = arg;
1753
+ else {
1754
+ ctx2.ui.notify(`Unknown option: ${arg}. Use on, off, summary-review, or auto-summary.`, "error");
1755
+ return;
1756
+ }
1757
+ try {
1758
+ saveConfig({ workflow: newWorkflow });
1759
+ } catch (err) {
1760
+ let message = err instanceof Error ? err.message : String(err);
1761
+ ctx2.ui.notify(`Failed to save config: ${message}`, "error");
1762
+ return;
1763
+ }
1764
+ let label = newWorkflow === "none" ? "Curator disabled \u2014 web_search will return raw results" : newWorkflow === "auto-summary" ? "Auto-summary enabled \u2014 web_search will generate a summary without opening the curator" : "Curator enabled \u2014 web_search will open curator and auto-generate a summary draft";
1765
+ pi.sendMessage({
1766
+ customType: "curator-config",
1767
+ content: [{ type: "text", text: label }],
1768
+ display: !0,
1769
+ details: { workflow: newWorkflow }
1770
+ }, { triggerTurn: !1, deliverAs: "followUp" });
1771
+ }
1772
+ }), pi.registerCommand("google-account", {
1773
+ description: "Show the active Google account for Gemini Web",
1774
+ handler: async () => {
1775
+ if (!isBrowserCookieAccessAllowed()) {
1776
+ pi.sendMessage({
1777
+ customType: "google-account",
1778
+ content: [{ type: "text", text: `Gemini Web browser cookie access is disabled. Set allowBrowserCookies: true in ${WEB_SEARCH_CONFIG_PATH} to enable it.` }],
1779
+ display: !0,
1780
+ details: { available: !1, cookieAccessAllowed: !1 }
1781
+ }, { triggerTurn: !0, deliverAs: "followUp" });
1782
+ return;
1783
+ }
1784
+ let geminiWeb = await loadGeminiWeb(), cookies = await geminiWeb.isGeminiWebAvailable();
1785
+ if (!cookies) {
1786
+ pi.sendMessage({
1787
+ customType: "google-account",
1788
+ content: [{ type: "text", text: "Gemini Web is unavailable. Sign into gemini.google.com in a supported Chromium-based browser." }],
1789
+ display: !0,
1790
+ details: { available: !1, cookieAccessAllowed: !0 }
1791
+ }, { triggerTurn: !0, deliverAs: "followUp" });
1792
+ return;
1793
+ }
1794
+ let email = await geminiWeb.getActiveGoogleEmail(cookies), text = email ? `Active Google account: ${email}` : "Gemini Web is available, but the active Google account could not be determined.";
1795
+ pi.sendMessage({
1796
+ customType: "google-account",
1797
+ content: [{ type: "text", text }],
1798
+ display: !0,
1799
+ details: { available: !0, email: email ?? null }
1800
+ }, { triggerTurn: !0, deliverAs: "followUp" });
1801
+ }
1802
+ }), pi.registerCommand("search", {
1803
+ description: "Browse stored web search results",
1804
+ handler: async (_args, ctx2) => {
1805
+ let results = getAllResults();
1806
+ if (results.length === 0) {
1807
+ ctx2.ui.notify("No stored search results", "info");
1808
+ return;
1809
+ }
1810
+ let options = results.map((r) => {
1811
+ let age = Math.floor((Date.now() - r.timestamp) / 6e4), ageStr = age < 60 ? `${age}m ago` : `${Math.floor(age / 60)}h ago`;
1812
+ if (r.type === "search" && r.queries) {
1813
+ let query = r.queries[0]?.query || "unknown";
1814
+ return `[${r.id.slice(0, 6)}] "${query}" (${r.queries.length} queries) - ${ageStr}`;
1815
+ }
1816
+ return r.type === "fetch" && r.urls ? `[${r.id.slice(0, 6)}] ${r.urls.length} URLs fetched - ${ageStr}` : `[${r.id.slice(0, 6)}] ${r.type} - ${ageStr}`;
1817
+ }), choice = await ctx2.ui.select("Stored Search Results", options);
1818
+ if (!choice)
1819
+ return;
1820
+ let match = choice.match(/^\[([a-z0-9]+)\]/);
1821
+ if (!match)
1822
+ return;
1823
+ let selected = results.find((r) => r.id.startsWith(match[1]));
1824
+ if (!selected)
1825
+ return;
1826
+ let actions = ["View details", "Delete"], action = await ctx2.ui.select(`Result ${selected.id.slice(0, 6)}`, actions);
1827
+ if (action === "Delete")
1828
+ deleteResult(selected.id), ctx2.ui.notify(`Deleted ${selected.id.slice(0, 6)}`, "info");
1829
+ else if (action === "View details") {
1830
+ let info = `ID: ${selected.id}
1831
+ Type: ${selected.type}
1832
+ Age: ${Math.floor((Date.now() - selected.timestamp) / 6e4)}m
1833
+
1834
+ `;
1835
+ if (selected.type === "search" && selected.queries) {
1836
+ info += `Queries:
1837
+ `;
1838
+ let queries = selected.queries.slice(0, 10);
1839
+ for (let q of queries)
1840
+ info += `- "${q.query}" (${q.results.length} results)
1841
+ `;
1842
+ selected.queries.length > 10 && (info += `... and ${selected.queries.length - 10} more
1843
+ `);
1844
+ }
1845
+ if (selected.type === "fetch" && selected.urls) {
1846
+ info += `URLs:
1847
+ `;
1848
+ let urls = selected.urls.slice(0, 10);
1849
+ for (let u of urls) {
1850
+ let urlDisplay = u.url.length > 50 ? u.url.slice(0, 47) + "..." : u.url;
1851
+ info += `- ${urlDisplay} (${u.error || `${u.content.length} chars`})
1852
+ `;
1853
+ }
1854
+ selected.urls.length > 10 && (info += `... and ${selected.urls.length - 10} more
1855
+ `);
1856
+ }
1857
+ ctx2.ui.notify(info, "info");
1858
+ }
1859
+ }
1860
+ });
1861
+ }
1862
+ export {
1863
+ pi_web_access_default as default
1864
+ };