@herbertgao/pi-extensions 2026.8.11 → 2026.8.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (476) hide show
  1. package/README.md +23 -10
  2. package/{node_modules/@herbertgao/pi-mermaid-open/LICENSE → THIRD_PARTY_NOTICES.md} +19 -1
  3. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +15 -0
  4. package/node_modules/@czottmann/pi-automode/README.md +5 -3
  5. package/node_modules/@czottmann/pi-automode/docs/adr/ADR-001-permission-precedence-and-trust-boundaries.md +2 -0
  6. package/node_modules/@czottmann/pi-automode/docs/adr/ADR-002-global-config-in-extension-data-directory.md +60 -0
  7. package/node_modules/@czottmann/pi-automode/docs/adr/INDEX.md +1 -0
  8. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +4 -4
  9. package/node_modules/@czottmann/pi-automode/docs/configuration.md +20 -4
  10. package/node_modules/@czottmann/pi-automode/docs/observability-logging.md +1 -1
  11. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/bash.ts +692 -0
  12. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +181 -3
  13. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +4 -1
  14. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +143 -36
  15. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +103 -148
  16. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +32 -6
  17. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +344 -22
  18. package/node_modules/@czottmann/pi-automode/extensions/auto-mode.ts +1 -0
  19. package/node_modules/@czottmann/pi-automode/package.json +4 -1
  20. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -1
  21. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -1
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +4 -0
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +21 -1
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +1 -1
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +19 -19
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +4 -2
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +2 -1
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +10 -2
  29. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  30. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +10 -0
  31. package/node_modules/@herbertgao/pi-subagents/README.md +21 -15
  32. package/node_modules/@herbertgao/pi-subagents/package.json +3 -3
  33. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +11 -8
  34. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +184 -106
  35. package/node_modules/@herbertgao/pi-subagents/src/cross-extension-rpc.ts +31 -11
  36. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +9 -1
  37. package/node_modules/@herbertgao/pi-subagents/src/index.ts +156 -31
  38. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +17 -0
  39. package/node_modules/@herbertgao/pi-subagents/src/model-resolver.ts +14 -0
  40. package/node_modules/@herbertgao/pi-subagents/src/schedule.ts +8 -0
  41. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +32 -1
  42. package/node_modules/@herbertgao/pi-subagents/src/types.ts +17 -2
  43. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +23 -2
  44. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +259 -24
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  46. package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +376 -0
  47. package/node_modules/@luxusai/pi-hindsight/README.md +92 -0
  48. package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +56 -0
  49. package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +169 -0
  50. package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +107 -0
  51. package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +89 -0
  52. package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +189 -0
  53. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
  54. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
  55. package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +54 -0
  56. package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +75 -0
  57. package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +300 -0
  58. package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +158 -0
  59. package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +164 -0
  60. package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +139 -0
  61. package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +74 -0
  62. package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +261 -0
  63. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +151 -0
  64. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +18 -0
  65. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +46 -0
  66. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +383 -0
  67. package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +240 -0
  68. package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +176 -0
  69. package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +178 -0
  70. package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +41 -0
  71. package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +386 -0
  72. package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +39 -0
  73. package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +34 -0
  74. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +140 -0
  75. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +61 -0
  76. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +925 -0
  77. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +247 -0
  78. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +547 -0
  79. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +480 -0
  80. package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +182 -0
  81. package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +86 -0
  82. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +1010 -0
  83. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +175 -0
  84. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +425 -0
  85. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +210 -0
  86. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +817 -0
  87. package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +25 -0
  88. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +319 -0
  89. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +196 -0
  90. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +189 -0
  91. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +65 -0
  92. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +260 -0
  93. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +244 -0
  94. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +59 -0
  95. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +82 -0
  96. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +45 -0
  97. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +331 -0
  98. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +354 -0
  99. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +66 -0
  100. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +132 -0
  101. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +228 -0
  102. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +74 -0
  103. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +16 -0
  104. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +648 -0
  105. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +105 -0
  106. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +68 -0
  107. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +71 -0
  108. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +13 -0
  109. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +162 -0
  110. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +131 -0
  111. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +104 -0
  112. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +37 -0
  113. package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +893 -0
  114. package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +35 -0
  115. package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +177 -0
  116. package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +7 -0
  117. package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +21 -0
  118. package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +112 -0
  119. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +111 -0
  120. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +214 -0
  121. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +70 -0
  122. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +392 -0
  123. package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +45 -0
  124. package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +9 -0
  125. package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +860 -0
  126. package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +105 -0
  127. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +189 -0
  128. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +299 -0
  129. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +201 -0
  130. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +47 -0
  131. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +239 -0
  132. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +50 -0
  133. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +259 -0
  134. package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +77 -0
  135. package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +534 -0
  136. package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +319 -0
  137. package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +325 -0
  138. package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +42 -0
  139. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +244 -0
  140. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +56 -0
  141. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +50 -0
  142. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +167 -0
  143. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +207 -0
  144. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +120 -0
  145. package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +8 -0
  146. package/node_modules/@luxusai/pi-hindsight/package.json +118 -0
  147. package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +75 -0
  148. package/node_modules/@narumitw/pi-btw/dist/index.ts +143 -46
  149. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  150. package/node_modules/@narumitw/pi-btw/package.json +6 -6
  151. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +136 -22
  152. package/node_modules/{@herbertgao → @tifan}/pi-copy-response/README.md +2 -4
  153. package/node_modules/{@herbertgao → @tifan}/pi-copy-response/package.json +5 -15
  154. package/node_modules/{@herbertgao → @tifan}/pi-handoff/README.md +4 -6
  155. package/node_modules/@tifan/pi-handoff/package.json +43 -0
  156. package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/index.ts +1 -1
  157. package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/README.md +2 -4
  158. package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/package.json +5 -15
  159. package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/README.md +5 -7
  160. package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/package.json +5 -19
  161. package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/README.md +4 -6
  162. package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/package.json +6 -16
  163. package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/src/index.ts +1 -6
  164. package/node_modules/{@herbertgao → @tifan}/pi-recap/README.md +4 -6
  165. package/node_modules/@tifan/pi-recap/package.json +42 -0
  166. package/node_modules/{@herbertgao → @tifan}/pi-recap/src/models.ts +1 -6
  167. package/node_modules/{@herbertgao → @tifan}/pi-rename/README.md +13 -8
  168. package/node_modules/@tifan/pi-rename/package.json +47 -0
  169. package/node_modules/@tifan/pi-rename/src/herdr-label.ts +4 -0
  170. package/node_modules/{@herbertgao → @tifan}/pi-rename/src/index.ts +4 -16
  171. package/node_modules/{@herbertgao → @tifan}/pi-rename/src/models.ts +1 -6
  172. package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/README.md +2 -4
  173. package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/package.json +5 -15
  174. package/node_modules/pi-lens/CHANGELOG.md +96 -1
  175. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -1
  176. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +14 -5
  177. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +48 -18
  178. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +1 -3
  179. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +16 -4
  180. package/node_modules/pi-lens/dist/clients/bash-file-access.js +3 -1
  181. package/node_modules/pi-lens/dist/clients/biome-client.js +1 -3
  182. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +21 -8
  183. package/node_modules/pi-lens/dist/clients/bounded-cache.js +24 -8
  184. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +4 -3
  185. package/node_modules/pi-lens/dist/clients/cache-manager.js +6 -7
  186. package/node_modules/pi-lens/dist/clients/cache-observability.js +461 -66
  187. package/node_modules/pi-lens/dist/clients/call-graph.js +238 -63
  188. package/node_modules/pi-lens/dist/clients/codebase-model.js +13 -5
  189. package/node_modules/pi-lens/dist/clients/complexity-client.js +1 -3
  190. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -8
  191. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +1 -2
  192. package/node_modules/pi-lens/dist/clients/deadline-utils.js +3 -1
  193. package/node_modules/pi-lens/dist/clients/debug-heap.js +8 -1
  194. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +1 -1
  195. package/node_modules/pi-lens/dist/clients/dependency-checker.js +11 -7
  196. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +39 -2
  197. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +5 -1
  198. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +2 -3
  199. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +13 -1
  200. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +3 -1
  201. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +2 -1
  202. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -4
  204. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +3 -1
  205. package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +6 -2
  206. package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +31 -7
  207. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +5 -2
  208. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +10 -14
  209. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +5 -1
  210. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +1 -1
  211. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +3 -1
  212. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +25 -6
  213. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +1 -1
  214. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +1 -1
  215. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +3 -1
  216. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +13 -8
  217. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +4 -1
  218. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +25 -10
  219. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +1 -1
  220. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +1 -1
  221. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +1 -1
  222. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +28 -4
  223. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +6 -8
  224. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +212 -11
  225. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +1 -1
  226. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +8 -4
  227. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +10 -6
  228. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +23 -14
  229. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -1
  230. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +7 -9
  231. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +5 -2
  232. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +7 -7
  233. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +6 -8
  234. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +13 -8
  235. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +13 -8
  236. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +17 -3
  237. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +7 -1
  238. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +24 -1
  239. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +35 -0
  240. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +1 -3
  241. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +6 -8
  242. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +1 -1
  243. package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +2 -1
  244. package/node_modules/pi-lens/dist/clients/dispatch/types.js +6 -1
  245. package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +6 -2
  246. package/node_modules/pi-lens/dist/clients/extension-log.js +5 -3
  247. package/node_modules/pi-lens/dist/clients/feature-hints.js +8 -1
  248. package/node_modules/pi-lens/dist/clients/file-kinds.js +34 -129
  249. package/node_modules/pi-lens/dist/clients/file-utils.js +17 -1
  250. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +57 -2
  251. package/node_modules/pi-lens/dist/clients/formatters.js +144 -27
  252. package/node_modules/pi-lens/dist/clients/freshness.js +38 -0
  253. package/node_modules/pi-lens/dist/clients/git-guard.js +43 -13
  254. package/node_modules/pi-lens/dist/clients/go-client.js +1 -3
  255. package/node_modules/pi-lens/dist/clients/grammar-source.js +1 -1
  256. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +30 -4
  257. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +1 -3
  258. package/node_modules/pi-lens/dist/clients/install-diagnostics.js +4 -2
  259. package/node_modules/pi-lens/dist/clients/installer/index.js +86 -77
  260. package/node_modules/pi-lens/dist/clients/instance-reaper.js +5 -2
  261. package/node_modules/pi-lens/dist/clients/instance-registry.js +4 -2
  262. package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
  263. package/node_modules/pi-lens/dist/clients/knip-client.js +29 -15
  264. package/node_modules/pi-lens/dist/clients/language-profile.js +4 -1
  265. package/node_modules/pi-lens/dist/clients/latency-logger.js +13 -2
  266. package/node_modules/pi-lens/dist/clients/lens-config.js +1 -1
  267. package/node_modules/pi-lens/dist/clients/lens-map.js +8 -3
  268. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +1 -1
  269. package/node_modules/pi-lens/dist/clients/lsp/config.js +4 -6
  270. package/node_modules/pi-lens/dist/clients/lsp/edits.js +119 -32
  271. package/node_modules/pi-lens/dist/clients/lsp/index.js +94 -35
  272. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +2 -1
  273. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +4 -1
  274. package/node_modules/pi-lens/dist/clients/lsp/launch.js +11 -3
  275. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +152 -0
  276. package/node_modules/pi-lens/dist/clients/lsp/server.js +19 -7
  277. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +10 -2
  278. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +9 -4
  279. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -3
  280. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +10 -20
  281. package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +1 -1
  282. package/node_modules/pi-lens/dist/clients/memory-sampler.js +73 -7
  283. package/node_modules/pi-lens/dist/clients/metrics-client.js +1 -3
  284. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +8 -3
  285. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +7 -1
  286. package/node_modules/pi-lens/dist/clients/module-report.js +14 -17
  287. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +3 -5
  288. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +220 -0
  289. package/node_modules/pi-lens/dist/clients/package-manager.js +6 -1
  290. package/node_modules/pi-lens/dist/clients/pipeline.js +57 -9
  291. package/node_modules/pi-lens/dist/clients/project-conventions.js +2 -8
  292. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +10 -7
  293. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +2 -1
  294. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +3 -1
  295. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +13 -9
  296. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +3 -1
  297. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -2
  298. package/node_modules/pi-lens/dist/clients/project-lens-config.js +12 -5
  299. package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +48 -0
  300. package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +15 -1
  301. package/node_modules/pi-lens/dist/clients/project-snapshot.js +472 -66
  302. package/node_modules/pi-lens/dist/clients/read-expansion.js +12 -2
  303. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +5 -4
  304. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +5 -2
  305. package/node_modules/pi-lens/dist/clients/read-guard.js +1 -1
  306. package/node_modules/pi-lens/dist/clients/resource-sampler.js +1 -1
  307. package/node_modules/pi-lens/dist/clients/reverse-deps.js +6 -5
  308. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +42 -17
  309. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +8 -2
  310. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +10 -1
  311. package/node_modules/pi-lens/dist/clients/review-graph/query.js +2 -1
  312. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +4 -2
  313. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +2 -2
  314. package/node_modules/pi-lens/dist/clients/ruff-client.js +6 -5
  315. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +63 -31
  316. package/node_modules/pi-lens/dist/clients/runtime-context.js +12 -4
  317. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +83 -4
  318. package/node_modules/pi-lens/dist/clients/runtime-session.js +14 -0
  319. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +59 -3
  320. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +148 -34
  321. package/node_modules/pi-lens/dist/clients/runtime-turn.js +147 -6
  322. package/node_modules/pi-lens/dist/clients/rust-client.js +1 -3
  323. package/node_modules/pi-lens/dist/clients/safe-spawn.js +150 -11
  324. package/node_modules/pi-lens/dist/clients/security-scan-client.js +1 -3
  325. package/node_modules/pi-lens/dist/clients/session-summary.js +3 -1
  326. package/node_modules/pi-lens/dist/clients/sg-runner.js +1 -3
  327. package/node_modules/pi-lens/dist/clients/source-filter.js +10 -6
  328. package/node_modules/pi-lens/dist/clients/source-walker.js +1 -1
  329. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +96 -0
  330. package/node_modules/pi-lens/dist/clients/startup-scan.js +4 -1
  331. package/node_modules/pi-lens/dist/clients/tool-definition.js +7 -0
  332. package/node_modules/pi-lens/dist/clients/tool-policy.js +26 -11
  333. package/node_modules/pi-lens/dist/clients/tool-render.js +2 -1
  334. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +10 -4
  335. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  336. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +12 -5
  337. package/node_modules/pi-lens/dist/clients/warm-attach.js +12 -5
  338. package/node_modules/pi-lens/dist/clients/widget-state.js +27 -11
  339. package/node_modules/pi-lens/dist/clients/word-index.js +3 -1
  340. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -2
  341. package/node_modules/pi-lens/dist/clients/zizmor-config.js +9 -1
  342. package/node_modules/pi-lens/dist/i18n.js +13 -5
  343. package/node_modules/pi-lens/dist/index.js +10797 -8627
  344. package/node_modules/pi-lens/dist/mcp/server.js +3 -1
  345. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +68 -12
  346. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +21 -7
  347. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +164 -35
  348. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +24 -7
  349. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
  350. package/node_modules/pi-lens/dist/tools/module-report.js +1 -1
  351. package/node_modules/pi-lens/dist/tools/project-report.js +1 -1
  352. package/node_modules/pi-lens/dist/tools/symbol-search.js +5 -2
  353. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +1 -1
  354. package/node_modules/pi-lens/docs/servercapabilities.md +1 -1
  355. package/node_modules/pi-lens/package.json +160 -156
  356. package/node_modules/pi-lens/rules/typos/_typos.toml +2 -2
  357. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +31 -13
  358. package/node_modules/pi-lens/scripts/download-grammars.js +9 -3
  359. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +30 -9
  360. package/node_modules/pi-mcp-adapter/CHANGELOG.md +47 -0
  361. package/node_modules/pi-mcp-adapter/README.md +42 -13
  362. package/node_modules/pi-mcp-adapter/commands.ts +4 -2
  363. package/node_modules/pi-mcp-adapter/config.ts +45 -5
  364. package/node_modules/pi-mcp-adapter/direct-tools.ts +103 -92
  365. package/node_modules/pi-mcp-adapter/dist/agent-dir.d.ts +10 -0
  366. package/node_modules/pi-mcp-adapter/dist/agent-dir.js +63 -0
  367. package/node_modules/pi-mcp-adapter/dist/agent-dir.js.map +1 -0
  368. package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.d.ts +8 -0
  369. package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js +356 -0
  370. package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js.map +1 -0
  371. package/node_modules/pi-mcp-adapter/dist/config.d.ts +123 -0
  372. package/node_modules/pi-mcp-adapter/dist/config.js +1087 -0
  373. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -0
  374. package/node_modules/pi-mcp-adapter/dist/metadata-cache.d.ts +19 -0
  375. package/node_modules/pi-mcp-adapter/dist/metadata-cache.js +319 -0
  376. package/node_modules/pi-mcp-adapter/dist/metadata-cache.js.map +1 -0
  377. package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.d.ts +2 -0
  378. package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js +156 -0
  379. package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js.map +1 -0
  380. package/node_modules/pi-mcp-adapter/dist/resource-tools.d.ts +1 -0
  381. package/node_modules/pi-mcp-adapter/dist/resource-tools.js +15 -0
  382. package/node_modules/pi-mcp-adapter/dist/resource-tools.js.map +1 -0
  383. package/node_modules/pi-mcp-adapter/dist/types.d.ts +564 -0
  384. package/node_modules/pi-mcp-adapter/dist/types.js +287 -0
  385. package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -0
  386. package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.d.ts +6 -0
  387. package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js +37 -0
  388. package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js.map +1 -0
  389. package/node_modules/pi-mcp-adapter/dist/ui-stream-types.d.ts +104 -0
  390. package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js +59 -0
  391. package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js.map +1 -0
  392. package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.d.ts +4 -0
  393. package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js +27 -0
  394. package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js.map +1 -0
  395. package/node_modules/pi-mcp-adapter/dist/utils.d.ts +32 -0
  396. package/node_modules/pi-mcp-adapter/dist/utils.js +338 -0
  397. package/node_modules/pi-mcp-adapter/dist/utils.js.map +1 -0
  398. package/node_modules/pi-mcp-adapter/failure-backoff.ts +23 -0
  399. package/node_modules/pi-mcp-adapter/index.ts +204 -82
  400. package/node_modules/pi-mcp-adapter/init.ts +41 -30
  401. package/node_modules/pi-mcp-adapter/lifecycle.ts +16 -8
  402. package/node_modules/pi-mcp-adapter/mcp-code.ts +2 -0
  403. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +66 -16
  404. package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +95 -7
  405. package/node_modules/pi-mcp-adapter/mcp-probe.ts +18 -4
  406. package/node_modules/pi-mcp-adapter/mcp-references.ts +368 -0
  407. package/node_modules/pi-mcp-adapter/mcp-status.ts +8 -19
  408. package/node_modules/pi-mcp-adapter/metadata-cache.ts +20 -12
  409. package/node_modules/pi-mcp-adapter/namespace-tools.ts +276 -0
  410. package/node_modules/pi-mcp-adapter/package.json +24 -6
  411. package/node_modules/pi-mcp-adapter/proxy-modes.ts +66 -10
  412. package/node_modules/pi-mcp-adapter/search-ranking.ts +2 -0
  413. package/node_modules/pi-mcp-adapter/server-manager.ts +63 -12
  414. package/node_modules/pi-mcp-adapter/state.ts +1 -3
  415. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +16 -2
  416. package/node_modules/pi-mcp-adapter/types.ts +48 -1
  417. package/node_modules/pi-mcp-adapter/ui-server.ts +3 -19
  418. package/node_modules/pi-mcp-adapter/ui-session.ts +2 -1
  419. package/node_modules/pi-mcp-adapter/utils.ts +21 -19
  420. package/node_modules/pi-web-access/CHANGELOG.md +26 -0
  421. package/node_modules/pi-web-access/README.md +71 -14
  422. package/node_modules/pi-web-access/chrome-cookies.ts +115 -35
  423. package/node_modules/pi-web-access/credential-source.ts +6 -2
  424. package/node_modules/pi-web-access/curator-page.ts +5 -3
  425. package/node_modules/pi-web-access/curator-server.ts +2 -1
  426. package/node_modules/pi-web-access/extract.ts +89 -9
  427. package/node_modules/pi-web-access/fetch-params.ts +13 -0
  428. package/node_modules/pi-web-access/gemini-adc.ts +298 -0
  429. package/node_modules/pi-web-access/gemini-api.ts +59 -15
  430. package/node_modules/pi-web-access/gemini-search.ts +58 -18
  431. package/node_modules/pi-web-access/gemini-url-context.ts +3 -2
  432. package/node_modules/pi-web-access/gemini-web-config.ts +57 -7
  433. package/node_modules/pi-web-access/gemini-web.ts +9 -3
  434. package/node_modules/pi-web-access/github-api.ts +5 -4
  435. package/node_modules/pi-web-access/github-issue-pr.ts +700 -0
  436. package/node_modules/pi-web-access/index.ts +403 -297
  437. package/node_modules/pi-web-access/kimi-search.ts +234 -0
  438. package/node_modules/pi-web-access/openai-search.ts +118 -21
  439. package/node_modules/pi-web-access/package.json +3 -2
  440. package/node_modules/pi-web-access/ssrf-protection.ts +4 -1
  441. package/node_modules/pi-web-access/utils.ts +307 -1
  442. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  443. package/package.json +46 -37
  444. package/node_modules/@herbertgao/pi-copy-response/CHANGELOG.md +0 -49
  445. package/node_modules/@herbertgao/pi-copy-response/LICENSE +0 -22
  446. package/node_modules/@herbertgao/pi-handoff/CHANGELOG.md +0 -83
  447. package/node_modules/@herbertgao/pi-handoff/LICENSE +0 -22
  448. package/node_modules/@herbertgao/pi-handoff/package.json +0 -60
  449. package/node_modules/@herbertgao/pi-inline-skills/CHANGELOG.md +0 -57
  450. package/node_modules/@herbertgao/pi-inline-skills/LICENSE +0 -22
  451. package/node_modules/@herbertgao/pi-mermaid-open/CHANGELOG.md +0 -39
  452. package/node_modules/@herbertgao/pi-preferred-thinking/CHANGELOG.md +0 -63
  453. package/node_modules/@herbertgao/pi-preferred-thinking/LICENSE +0 -22
  454. package/node_modules/@herbertgao/pi-recap/CHANGELOG.md +0 -101
  455. package/node_modules/@herbertgao/pi-recap/LICENSE +0 -22
  456. package/node_modules/@herbertgao/pi-recap/package.json +0 -55
  457. package/node_modules/@herbertgao/pi-rename/CHANGELOG.md +0 -49
  458. package/node_modules/@herbertgao/pi-rename/LICENSE +0 -22
  459. package/node_modules/@herbertgao/pi-rename/package.json +0 -63
  460. package/node_modules/@herbertgao/pi-titlebar-spinner/CHANGELOG.md +0 -25
  461. package/node_modules/@herbertgao/pi-titlebar-spinner/LICENSE +0 -22
  462. /package/node_modules/{@herbertgao → @tifan}/pi-copy-response/src/index.ts +0 -0
  463. /package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/complete-text.ts +0 -0
  464. /package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/session-query.ts +0 -0
  465. /package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/src/index.ts +0 -0
  466. /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/herdr-plugin/herdr-plugin.toml +0 -0
  467. /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/herdr-plugin/viewer.mjs +0 -0
  468. /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/src/index.ts +0 -0
  469. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/index.ts +0 -0
  470. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/model-picker.ts +0 -0
  471. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/sanitize.ts +0 -0
  472. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/tui.ts +0 -0
  473. /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/model-picker.ts +0 -0
  474. /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/naming.ts +0 -0
  475. /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/sanitize.ts +0 -0
  476. /package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/src/index.ts +0 -0
@@ -11,7 +11,7 @@ import { rewriteSearchQuery } from "./query-rewrite.ts";
11
11
  import { clearCloneCache } from "./github-extract.ts";
12
12
  import { getConfiguredSearchRouting, normalizeSearchProviderSelection, RESOLVED_SEARCH_PROVIDERS, SEARCH_PROVIDERS, search, type AttributedSearchResponse, type SearchProvider, type SearchProviderSelection, type ResolvedSearchProvider } from "./gemini-search.ts";
13
13
  import type { SearchResult } from "./perplexity.ts";
14
- import { formatSeconds, getWebSearchConfigDir, getWebSearchConfigPath, resolveCuratorNetworkConfig } from "./utils.ts";
14
+ import { formatSeconds, getWebSearchConfigDir, getWebSearchConfigPath, installGlobalProxyFetch, resolveCuratorNetworkConfig, runWithProxy } from "./utils.ts";
15
15
  import {
16
16
  clearResults,
17
17
  deleteResult,
@@ -42,10 +42,10 @@ import { join } from "node:path";
42
42
  import { isPerplexityAvailable } from "./perplexity.ts";
43
43
  import { isExaAvailable } from "./exa.ts";
44
44
  import { isGeminiApiAvailable } from "./gemini-api.ts";
45
- import { getActiveGoogleEmail, getGeminiWebAvailabilityDiagnostic, isGeminiWebAvailable } from "./gemini-web.ts";
45
+ import { getActiveGoogleEmail, getGeminiWebAvailabilityDiagnostic, getGeminiWebAvailabilityDiagnosticDetails, isGeminiWebAvailable } from "./gemini-web.ts";
46
46
  import { isBrowserCookieAccessAllowed } from "./gemini-web-config.ts";
47
47
  import { isBraveAvailable } from "./brave.ts";
48
- import { isOpenAISearchAvailable } from "./openai-search.ts";
48
+ import { isCurrentModelHostedSearchEligible, isOpenAISearchAvailable } from "./openai-search.ts";
49
49
  import { isParallelAvailable } from "./parallel.ts";
50
50
  import { isParallelMcpAvailable } from "./parallel-mcp.ts";
51
51
  import { isTinyFishAvailable } from "./tinyfish.ts";
@@ -63,6 +63,7 @@ import { isSearXNGAvailable } from "./searxng.ts";
63
63
  import { isDuckDuckGoAvailable } from "./duckduckgo.ts";
64
64
  import { isAnySearchAvailable } from "./anysearch.ts";
65
65
  import { isXaiSearchAvailable } from "./xai-search.ts";
66
+ import { isKimiSearchAvailable } from "./kimi-search.ts";
66
67
  import { isBrightDataAvailable } from "./brightdata.ts";
67
68
  import { isSerpBaseAvailable } from "./serpbase.ts";
68
69
  import { isSerperAvailable } from "./serper.ts";
@@ -92,6 +93,18 @@ async function fetchAllContent(
92
93
  return extractModule.fetchAllContent(urls, signal, options);
93
94
  }
94
95
 
96
+ function withRegisteredFetchOptions(
97
+ options: ExtractOptions | undefined,
98
+ toolNames: ExtractOptions["toolNames"],
99
+ proxy?: string,
100
+ ): ExtractOptions {
101
+ return {
102
+ ...(options ?? {}),
103
+ toolNames,
104
+ ...(proxy !== undefined ? { proxy } : {}),
105
+ };
106
+ }
107
+
95
108
  function isAbortError(err: unknown): boolean {
96
109
  return (err instanceof Error ? err.message : String(err)).toLowerCase().includes("abort");
97
110
  }
@@ -171,6 +184,7 @@ export interface ProviderAvailability {
171
184
  perplexity: boolean;
172
185
  exa: boolean;
173
186
  gemini: boolean;
187
+ kimi: boolean;
174
188
  kagi: boolean;
175
189
  bocha: boolean;
176
190
  ollama: boolean;
@@ -383,7 +397,7 @@ function shouldAutoOpenCuratorBrowser(config: WebSearchConfig): boolean {
383
397
  }
384
398
 
385
399
  async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderAvailability> {
386
- const geminiWebAvail = await isGeminiWebAvailable();
400
+ const geminiWebAvail = await getOptionalGeminiWebAvailability();
387
401
  const geminiApiAvail = isGeminiApiAvailable();
388
402
  const providers = {
389
403
  openai: await isOpenAISearchAvailable(ctx),
@@ -406,6 +420,7 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
406
420
  perplexity: isPerplexityAvailable(),
407
421
  exa: isExaAvailable(),
408
422
  gemini: geminiApiAvail || !!geminiWebAvail,
423
+ kimi: await isKimiSearchAvailable(ctx),
409
424
  anysearch: isAnySearchAvailable(),
410
425
  xai: await isXaiSearchAvailable(ctx),
411
426
  brightdata: isBrightDataAvailable(),
@@ -414,12 +429,20 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
414
429
  valyu: isValyuAvailable(),
415
430
  };
416
431
  return {
417
- // Parallel MCP, DuckDuckGo, AnySearch, xAI, Bright Data, SerpBase, Serper, and Valyu are explicit-only, so they never make `all` eligible.
418
- all: Object.entries(providers).some(([provider, available]) => provider !== "parallel-mcp" && provider !== "duckduckgo" && provider !== "anysearch" && provider !== "xai" && provider !== "brightdata" && provider !== "serpbase" && provider !== "serper" && provider !== "valyu" && provider !== "gemini" && available) || geminiApiAvail,
432
+ // Parallel MCP, DuckDuckGo, Kimi, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper are explicit-only, so they never make `all` eligible.
433
+ all: Object.entries(providers).some(([provider, available]) => provider !== "parallel-mcp" && provider !== "duckduckgo" && provider !== "kimi" && provider !== "anysearch" && provider !== "valyu" && provider !== "xai" && provider !== "brightdata" && provider !== "serpbase" && provider !== "serper" && provider !== "gemini" && available) || geminiApiAvail,
419
434
  ...providers,
420
435
  };
421
436
  }
422
437
 
438
+ async function getOptionalGeminiWebAvailability() {
439
+ try {
440
+ return await isGeminiWebAvailable();
441
+ } catch {
442
+ return null;
443
+ }
444
+ }
445
+
423
446
  function shouldUseOpenAICodexDefault(ctx?: Pick<ExtensionContext, "model">): boolean {
424
447
  return ctx?.model?.provider === "openai-codex";
425
448
  }
@@ -453,7 +476,7 @@ export function resolveCuratorDefaultProvider(
453
476
  ctx?: Pick<ExtensionContext, "model">,
454
477
  options?: Pick<PendingCurate, "numResults" | "recencyFilter">,
455
478
  ): CuratorProvider {
456
- return resolveProvider(provider, available, options, shouldUseOpenAICodexDefault(ctx));
479
+ return resolveProvider(provider, available, options, shouldUseOpenAICodexDefault(ctx), ctx);
457
480
  }
458
481
 
459
482
  function firstAvailableProvider(available: ProviderAvailability, preferOpenAI: boolean, fallback: ResolvedSearchProvider): ResolvedSearchProvider {
@@ -484,6 +507,7 @@ function resolveProvider(
484
507
  available: ProviderAvailability,
485
508
  options?: Pick<PendingCurate, "numResults" | "recencyFilter">,
486
509
  preferOpenAICodexDefault = false,
510
+ ctx?: Pick<ExtensionContext, "model">,
487
511
  ): CuratorProvider {
488
512
  if (Array.isArray(provider)) return "all";
489
513
  const preferOpenAI = shouldPreferOpenAI(options, preferOpenAICodexDefault);
@@ -492,9 +516,10 @@ function resolveProvider(
492
516
  const routing = getConfiguredSearchRouting();
493
517
  if (routing) {
494
518
  for (const candidate of routing.providers) {
519
+ if (candidate === "openai" && routing.useCurrentModel === true && !isCurrentModelHostedSearchEligible(ctx)) continue;
495
520
  if (available[candidate]) return candidate;
496
521
  }
497
- return routing.providers[0];
522
+ return routing.providers.find(candidate => candidate !== "openai" || routing.useCurrentModel !== true || isCurrentModelHostedSearchEligible(ctx)) ?? routing.providers[0];
498
523
  }
499
524
  return firstAvailableProvider(available, preferOpenAI, "exa");
500
525
  }
@@ -584,6 +609,7 @@ interface PendingCurate {
584
609
  summaryModels: Array<{ value: string; label: string }>;
585
610
  defaultSummaryModel: string | null;
586
611
  timeoutSeconds: number;
612
+ proxy?: string;
587
613
  curatorUrl?: string;
588
614
  onUpdate: ((update: { content: Array<{ type: string; text: string }>; details?: Record<string, unknown> }) => void) | undefined;
589
615
  signal: AbortSignal | undefined;
@@ -646,6 +672,40 @@ function normalizeFindQueries(value: string | string[]): string[] {
646
672
  return queries;
647
673
  }
648
674
 
675
+ interface GetSearchContentParams {
676
+ responseId: string;
677
+ query?: string;
678
+ queryIndex?: number;
679
+ url?: string;
680
+ urlIndex?: number;
681
+ offset?: number;
682
+ limit?: number;
683
+ findText?: string | string[];
684
+ findMode?: FindMode;
685
+ }
686
+
687
+ type RawGetSearchContentParams = Omit<GetSearchContentParams, "findMode"> & { findMode?: unknown };
688
+
689
+ function normalizeFindMode(value: unknown): FindMode | undefined {
690
+ if (value === undefined) return undefined;
691
+ if (value === "exact" || value === "case-insensitive" || value === "fuzzy") return value;
692
+ throw new Error('findMode must be "exact", "case-insensitive", or "fuzzy"');
693
+ }
694
+
695
+ function normalizeGetSearchContentParams(params: RawGetSearchContentParams): GetSearchContentParams {
696
+ const normalized: GetSearchContentParams = { ...params, findMode: normalizeFindMode(params.findMode) };
697
+
698
+ if (normalized.query?.trim() === "") delete normalized.query;
699
+ if (normalized.url?.trim() === "") delete normalized.url;
700
+
701
+ if (normalized.findText !== undefined) {
702
+ delete normalized.offset;
703
+ delete normalized.limit;
704
+ }
705
+
706
+ return normalized;
707
+ }
708
+
649
709
  function formatInputValue(value: unknown): string {
650
710
  if (typeof value === "string") return JSON.stringify(value);
651
711
  if (typeof value === "number") return Number.isNaN(value) ? "NaN" : String(value);
@@ -978,11 +1038,18 @@ function handleSessionChange(ctx: ExtensionContext): void {
978
1038
 
979
1039
  export default function (pi: ExtensionAPI) {
980
1040
  const initConfig = loadConfigForExtensionInit();
1041
+ installGlobalProxyFetch();
981
1042
  const toolNames = resolveToolNames(initConfig);
982
1043
  const webSearchEnabled = isToolEnabled(initConfig, "webSearch");
983
1044
  const sourceCheckEnabled = isToolEnabled(initConfig, "sourceCheck");
984
1045
  const fetchContentEnabled = isToolEnabled(initConfig, "fetchContent");
985
1046
  const getSearchContentEnabled = isToolEnabled(initConfig, "getSearchContent");
1047
+ // Names as registered this session, so fetch failure guidance never points
1048
+ // at tools that are disabled or were renamed after init.
1049
+ const registeredToolNames = {
1050
+ ...(webSearchEnabled ? { webSearch: toolNames.webSearch } : {}),
1051
+ ...(fetchContentEnabled ? { fetchContent: toolNames.fetchContent } : {}),
1052
+ };
986
1053
  const storedContentSources = joinToolNames([
987
1054
  ...(webSearchEnabled ? [toolNames.webSearch] : []),
988
1055
  ...(sourceCheckEnabled ? [toolNames.sourceCheck] : []),
@@ -997,12 +1064,12 @@ export default function (pi: ExtensionAPI) {
997
1064
  const curateKey = initConfig.shortcuts?.curate || DEFAULT_SHORTCUTS.curate;
998
1065
  const activityKey = initConfig.shortcuts?.activity || DEFAULT_SHORTCUTS.activity;
999
1066
 
1000
- function startBackgroundFetch(urls: string[]): string | null {
1067
+ function startBackgroundFetch(urls: string[], proxy?: string): string | null {
1001
1068
  if (urls.length === 0) return null;
1002
1069
  const fetchId = generateId();
1003
1070
  const controller = new AbortController();
1004
1071
  pendingFetches.set(fetchId, controller);
1005
- fetchAllContent(urls, controller.signal)
1072
+ runWithProxy(proxy, () => fetchAllContent(urls, controller.signal, withRegisteredFetchOptions(undefined, registeredToolNames, proxy)))
1006
1073
  .then((fetched) => {
1007
1074
  if (!sessionActive || !pendingFetches.has(fetchId)) return;
1008
1075
  const data = {
@@ -1067,6 +1134,7 @@ export default function (pi: ExtensionAPI) {
1067
1134
  workflow?: SummaryWorkflow;
1068
1135
  approvedSummary?: string;
1069
1136
  summaryMeta?: SummaryMeta;
1137
+ proxy?: string;
1070
1138
  }
1071
1139
 
1072
1140
  function normalizeSummaryMeta(meta: SummaryMeta | undefined, summaryText: string): SummaryMeta {
@@ -1319,7 +1387,7 @@ export default function (pi: ExtensionAPI) {
1319
1387
  output += `---\nFull content for ${opts.inlineContent.length} sources available [${fetchId}].`;
1320
1388
  }
1321
1389
  } else if (opts.includeContent) {
1322
- fetchId = startBackgroundFetch(opts.urls);
1390
+ fetchId = startBackgroundFetch(opts.urls, opts.proxy);
1323
1391
  if (fetchId && !hasApprovedSummary) {
1324
1392
  output += `---\nContent fetching in background [${fetchId}]. Will notify when ready.`;
1325
1393
  }
@@ -1434,25 +1502,27 @@ export default function (pi: ExtensionAPI) {
1434
1502
  },
1435
1503
  {
1436
1504
  async onSummarize(selectedQueryIndices, summarizeSignal, model, feedback) {
1437
- if (pendingCurates.get(callId) !== pc) throw new Error("Curator session is no longer active.");
1438
- pc.onUpdate?.({
1439
- content: [{ type: "text", text: "Generating summary draft..." }],
1440
- details: { phase: "generating-summary", progress: 0.9, curatorUrl: pc.curatorUrl, timeoutSeconds: pc.timeoutSeconds, shortcut: curateKey },
1441
- });
1442
- const draft = await generateSummaryForSelectedIndices(
1443
- selectedQueryIndices,
1444
- pc.searchResults,
1445
- pc.summaryContext,
1446
- summarizeSignal,
1447
- model,
1448
- feedback,
1449
- );
1450
- if (pendingCurates.get(callId) !== pc) throw new Error("Curator session is no longer active.");
1451
- pc.onUpdate?.({
1452
- content: [{ type: "text", text: "Summary draft ready — waiting for approval..." }],
1453
- details: { phase: "waiting-for-approval", progress: 1, curatorUrl: pc.curatorUrl, timeoutSeconds: pc.timeoutSeconds, shortcut: curateKey },
1505
+ return runWithProxy(pc.proxy, async () => {
1506
+ if (pendingCurates.get(callId) !== pc) throw new Error("Curator session is no longer active.");
1507
+ pc.onUpdate?.({
1508
+ content: [{ type: "text", text: "Generating summary draft..." }],
1509
+ details: { phase: "generating-summary", progress: 0.9, curatorUrl: pc.curatorUrl, timeoutSeconds: pc.timeoutSeconds, shortcut: curateKey },
1510
+ });
1511
+ const draft = await generateSummaryForSelectedIndices(
1512
+ selectedQueryIndices,
1513
+ pc.searchResults,
1514
+ pc.summaryContext,
1515
+ summarizeSignal,
1516
+ model,
1517
+ feedback,
1518
+ );
1519
+ if (pendingCurates.get(callId) !== pc) throw new Error("Curator session is no longer active.");
1520
+ pc.onUpdate?.({
1521
+ content: [{ type: "text", text: "Summary draft ready waiting for approval..." }],
1522
+ details: { phase: "waiting-for-approval", progress: 1, curatorUrl: pc.curatorUrl, timeoutSeconds: pc.timeoutSeconds, shortcut: curateKey },
1523
+ });
1524
+ return draft;
1454
1525
  });
1455
- return draft;
1456
1526
  },
1457
1527
  onSubmit(payload) {
1458
1528
  if (pendingCurates.get(callId) !== pc) return;
@@ -1469,6 +1539,7 @@ export default function (pi: ExtensionAPI) {
1469
1539
  inlineContent: filteredInline.length > 0 ? filteredInline : undefined,
1470
1540
  curated: true,
1471
1541
  curatedFrom: pc.searchResults.size,
1542
+ proxy: pc.proxy,
1472
1543
  };
1473
1544
  if (!payload.rawResults) {
1474
1545
  const resolvedSummary = resolveSummaryForSubmit(payload, pc.searchResults);
@@ -1497,6 +1568,7 @@ export default function (pi: ExtensionAPI) {
1497
1568
  workflow: pc.workflow,
1498
1569
  approvedSummary: resolvedSummary.approvedSummary,
1499
1570
  summaryMeta: resolvedSummary.summaryMeta,
1571
+ proxy: pc.proxy,
1500
1572
  }));
1501
1573
  } else {
1502
1574
  const conn = activeCurators.get(callId)?.getConnectionState();
@@ -1525,20 +1597,22 @@ export default function (pi: ExtensionAPI) {
1525
1597
  }
1526
1598
  },
1527
1599
  async onAddSearch(query, provider) {
1528
- if (pendingCurates.get(callId) !== pc) throw new Error("Curator session is no longer active.");
1529
- const requestedProvider = resolveCuratorSearchProvider(provider, pc.searchProvider);
1530
- const response = await search(query, {
1531
- provider: requestedProvider,
1532
- numResults: pc.numResults,
1533
- recencyFilter: pc.recencyFilter,
1534
- domainFilter: pc.domainFilter,
1535
- includeContent: pc.includeContent,
1536
- signal: addSearchSignal,
1537
- extensionContext: ctx,
1600
+ return runWithProxy(pc.proxy, async () => {
1601
+ if (pendingCurates.get(callId) !== pc) throw new Error("Curator session is no longer active.");
1602
+ const requestedProvider = resolveCuratorSearchProvider(provider, pc.searchProvider);
1603
+ const response = await search(query, {
1604
+ provider: requestedProvider,
1605
+ numResults: pc.numResults,
1606
+ recencyFilter: pc.recencyFilter,
1607
+ domainFilter: pc.domainFilter,
1608
+ includeContent: pc.includeContent,
1609
+ signal: addSearchSignal,
1610
+ extensionContext: ctx,
1611
+ });
1612
+ if (pendingCurates.get(callId) !== pc) throw new Error("Curator session is no longer active.");
1613
+ if (response.inlineContent) pc.allInlineContent.push(...response.inlineContent);
1614
+ return toCuratorSearchEntries(response);
1538
1615
  });
1539
- if (pendingCurates.get(callId) !== pc) throw new Error("Curator session is no longer active.");
1540
- if (response.inlineContent) pc.allInlineContent.push(...response.inlineContent);
1541
- return toCuratorSearchEntries(response);
1542
1616
  },
1543
1617
  onAddSearchResults(entries) {
1544
1618
  if (pendingCurates.get(callId) !== pc) return;
@@ -1547,8 +1621,10 @@ export default function (pi: ExtensionAPI) {
1547
1621
  }
1548
1622
  },
1549
1623
  async onRewriteQuery(query, rewriteSignal) {
1550
- if (pendingCurates.get(callId) !== pc) throw new Error("Curator session is no longer active.");
1551
- return rewriteSearchQuery(query, pc.summaryContext, rewriteSignal);
1624
+ return runWithProxy(pc.proxy, async () => {
1625
+ if (pendingCurates.get(callId) !== pc) throw new Error("Curator session is no longer active.");
1626
+ return rewriteSearchQuery(query, pc.summaryContext, rewriteSignal);
1627
+ });
1552
1628
  },
1553
1629
  },
1554
1630
  );
@@ -1674,7 +1750,7 @@ export default function (pi: ExtensionAPI) {
1674
1750
  name: toolNames.webSearch,
1675
1751
  label: "Web Search",
1676
1752
  description:
1677
- `Search the web using OpenAI, Brave, Parallel, Parallel MCP, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, AnySearch, Valyu, xAI, Bright Data, SerpBase, or Serper. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except Parallel MCP, DuckDuckGo, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; xAI search uses a SuperGrok/X Premium subscription or xAI API key. Parallel MCP, DuckDuckGo, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper are available only when explicitly selected. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation or "auto-summary" for a model-generated summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it. Without a configured provider, SearXNG is preferred first for local/private search. When the active Pi model is openai-codex, Codex-backed OpenAI search is preferred next. Otherwise Exa is preferred before OpenAI, then Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web.`,
1753
+ `Search the web using OpenAI, Brave, Parallel, Parallel MCP, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, Kimi, AnySearch, Valyu, xAI, Bright Data, SerpBase, or Serper. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; Kimi search uses a Kimi Code Plan authenticated through /login kimi-coding; xAI search uses a SuperGrok/X Premium subscription or xAI API key. Parallel MCP, DuckDuckGo, Kimi, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper are available only when explicitly selected. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation or "auto-summary" for a model-generated summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it. Without a configured provider, SearXNG is preferred first for local/private search. When the active Pi model is openai-codex, Codex-backed OpenAI search is preferred next. Otherwise Exa is preferred before OpenAI, then Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web.`,
1678
1754
  promptSnippet:
1679
1755
  "Use for web research questions. Prefer {queries:[...]} with 2-4 varied angles over a single query for broader coverage. Omit provider unless explicitly overriding the configured default.",
1680
1756
  parameters: Type.Object({
@@ -1686,39 +1762,43 @@ export default function (pi: ExtensionAPI) {
1686
1762
  StringEnum(["day", "week", "month", "year"], { description: "Filter by recency" }),
1687
1763
  ),
1688
1764
  domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
1689
- provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except Parallel MCP, DuckDuckGo, AnySearch, xAI, Bright Data, SerpBase, Serper, and Valyu, omit this field to use the configured provider, or use auto when none is configured")),
1765
+ provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper, omit this field to use the configured provider, or use auto when none is configured")),
1690
1766
  workflow: Type.Optional(
1691
1767
  StringEnum(["none", "summary-review", "auto-summary"], {
1692
1768
  description: "Search workflow mode: none = no curator, summary-review = open curator with auto summary draft (default), auto-summary = generate summary without opening curator",
1693
1769
  }),
1694
1770
  ),
1771
+ proxy: Type.Optional(Type.String({
1772
+ description: "http(s) proxy URL (e.g. http://host:port) used for every outbound request in this call (search APIs and content fetches). Node fetch ignores HTTP(S)_PROXY env vars, so set this (or `proxy` in web-search.json) when direct access is blocked; empty string forces direct access.",
1773
+ })),
1695
1774
  }),
1696
1775
 
1697
1776
  async execute(callId, params, signal, onUpdate, ctx) {
1698
- const rawQueryList: unknown[] = Array.isArray(params.queries)
1699
- ? params.queries
1700
- : (params.query !== undefined ? [params.query] : []);
1701
- const queryList = normalizeQueryList(rawQueryList);
1702
- const configWorkflow = loadConfigForExtensionInit().workflow;
1703
- const workflow = resolveWorkflow(params.workflow ?? configWorkflow, ctx?.hasUI !== false);
1704
- const shouldCurate = workflow === "summary-review";
1705
- const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
1706
-
1707
- if (queryList.length === 0) {
1708
- return {
1709
- content: [{ type: "text", text: "Error: No query provided. Use 'query' or 'queries' parameter." }],
1710
- details: { error: "No query provided" },
1711
- };
1712
- }
1777
+ return runWithProxy(typeof params.proxy === "string" ? params.proxy : undefined, async () => {
1778
+ const rawQueryList: unknown[] = Array.isArray(params.queries)
1779
+ ? params.queries
1780
+ : (params.query !== undefined ? [params.query] : []);
1781
+ const queryList = normalizeQueryList(rawQueryList);
1782
+ const configWorkflow = loadConfigForExtensionInit().workflow;
1783
+ const workflow = resolveWorkflow(params.workflow ?? configWorkflow, ctx?.hasUI !== false);
1784
+ const shouldCurate = workflow === "summary-review";
1785
+ const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
1786
+
1787
+ if (queryList.length === 0) {
1788
+ return {
1789
+ content: [{ type: "text", text: "Error: No query provided. Use 'query' or 'queries' parameter." }],
1790
+ details: { error: "No query provided" },
1791
+ };
1792
+ }
1713
1793
 
1714
- if (shouldCurate && !ctx) {
1715
- return {
1716
- content: [{ type: "text", text: "Error: Curation requires an active extension context." }],
1717
- details: { error: "Missing extension context" },
1718
- };
1719
- }
1794
+ if (shouldCurate && !ctx) {
1795
+ return {
1796
+ content: [{ type: "text", text: "Error: Curation requires an active extension context." }],
1797
+ details: { error: "Missing extension context" },
1798
+ };
1799
+ }
1720
1800
 
1721
- if (shouldCurate) {
1801
+ if (shouldCurate) {
1722
1802
  closeCurator(callId);
1723
1803
 
1724
1804
  let resolvePromise: (value: AgentToolResult<Record<string, unknown>>) => void = () => {};
@@ -1773,6 +1853,7 @@ export default function (pi: ExtensionAPI) {
1773
1853
  summaryModels: summaryModelChoices.summaryModels,
1774
1854
  defaultSummaryModel: summaryModelChoices.defaultSummaryModel,
1775
1855
  timeoutSeconds: curatorTimeoutSeconds,
1856
+ proxy: typeof params.proxy === "string" ? params.proxy : undefined,
1776
1857
  onUpdate: onUpdate as PendingCurate["onUpdate"],
1777
1858
  signal,
1778
1859
  abortSearches: () => {
@@ -1983,6 +2064,8 @@ export default function (pi: ExtensionAPI) {
1983
2064
  workflow: workflow === "auto-summary" ? "auto-summary" : undefined,
1984
2065
  approvedSummary,
1985
2066
  summaryMeta,
2067
+ proxy: typeof params.proxy === "string" ? params.proxy : undefined,
2068
+ });
1986
2069
  });
1987
2070
  },
1988
2071
 
@@ -2254,85 +2337,90 @@ export default function (pi: ExtensionAPI) {
2254
2337
  fetchContent: Type.Optional(Type.Boolean({ description: "Fetch up to 5 result pages for exact passage extraction." })),
2255
2338
  recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency." })),
2256
2339
  domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains; prefix with - to exclude." })),
2257
- provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; all searches every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, SerpBase, Serper, and Valyu")),
2340
+ provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; all searches every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper")),
2341
+ proxy: Type.Optional(Type.String({
2342
+ description: "http(s) proxy URL (e.g. http://host:port) used for every outbound request in this call (search APIs and result-page fetches). Empty string forces direct access.",
2343
+ })),
2258
2344
  }),
2259
2345
  async execute(_callId, params, signal, _onUpdate, ctx) {
2260
- const claim = typeof params.claim === "string" ? params.claim.trim() : "";
2261
- if (!claim) {
2262
- return { content: [{ type: "text", text: "Error: 'claim' is required." }], details: { error: "Missing claim" } };
2263
- }
2346
+ return runWithProxy(typeof params.proxy === "string" ? params.proxy : undefined, async () => {
2347
+ const claim = typeof params.claim === "string" ? params.claim.trim() : "";
2348
+ if (!claim) {
2349
+ return { content: [{ type: "text", text: "Error: 'claim' is required." }], details: { error: "Missing claim" } };
2350
+ }
2264
2351
 
2265
- const requestedQueries = Array.isArray(params.queries)
2266
- ? params.queries.filter((query): query is string => typeof query === "string").map((query) => query.trim()).filter(Boolean)
2267
- : [];
2268
- const queries = (requestedQueries.length > 0 ? requestedQueries : [claim]).slice(0, 8);
2269
- const numResults = typeof params.numResults === "number" && Number.isFinite(params.numResults)
2270
- ? Math.min(20, Math.max(1, Math.floor(params.numResults)))
2271
- : 5;
2272
- const domainFilter = Array.isArray(params.domainFilter)
2273
- ? params.domainFilter.filter((domain): domain is string => typeof domain === "string")
2274
- : undefined;
2275
- const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
2276
- const resultsByUrl = new Map<string, SearchResult>();
2277
- const summaries: string[] = [];
2278
- const errors: Array<{ query: string; error: string }> = [];
2279
- let provider: string | undefined;
2280
-
2281
- for (const query of queries) {
2282
- if (signal?.aborted) break;
2283
- try {
2284
- const response = await search(query, {
2285
- provider: resolveRequestedProvider(params.provider),
2286
- numResults,
2287
- recencyFilter,
2288
- domainFilter,
2289
- signal,
2290
- extensionContext: ctx,
2291
- });
2352
+ const requestedQueries = Array.isArray(params.queries)
2353
+ ? params.queries.filter((query): query is string => typeof query === "string").map((query) => query.trim()).filter(Boolean)
2354
+ : [];
2355
+ const queries = (requestedQueries.length > 0 ? requestedQueries : [claim]).slice(0, 8);
2356
+ const numResults = typeof params.numResults === "number" && Number.isFinite(params.numResults)
2357
+ ? Math.min(20, Math.max(1, Math.floor(params.numResults)))
2358
+ : 5;
2359
+ const domainFilter = Array.isArray(params.domainFilter)
2360
+ ? params.domainFilter.filter((domain): domain is string => typeof domain === "string")
2361
+ : undefined;
2362
+ const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
2363
+ const resultsByUrl = new Map<string, SearchResult>();
2364
+ const summaries: string[] = [];
2365
+ const errors: Array<{ query: string; error: string }> = [];
2366
+ let provider: string | undefined;
2367
+
2368
+ for (const query of queries) {
2292
2369
  if (signal?.aborted) break;
2293
- provider ??= response.provider;
2294
- if (response.answer) summaries.push(`${query}: ${response.answer}`);
2295
- for (const result of response.results) {
2296
- if (!resultsByUrl.has(result.url)) resultsByUrl.set(result.url, result);
2370
+ try {
2371
+ const response = await search(query, {
2372
+ provider: resolveRequestedProvider(params.provider),
2373
+ numResults,
2374
+ recencyFilter,
2375
+ domainFilter,
2376
+ signal,
2377
+ extensionContext: ctx,
2378
+ });
2379
+ if (signal?.aborted) break;
2380
+ provider ??= response.provider;
2381
+ if (response.answer) summaries.push(`${query}: ${response.answer}`);
2382
+ for (const result of response.results) {
2383
+ if (!resultsByUrl.has(result.url)) resultsByUrl.set(result.url, result);
2384
+ }
2385
+ } catch (err) {
2386
+ if (signal?.aborted || isAbortError(err)) break;
2387
+ errors.push({ query, error: err instanceof Error ? err.message : String(err) });
2297
2388
  }
2298
- } catch (err) {
2299
- if (signal?.aborted || isAbortError(err)) break;
2300
- errors.push({ query, error: err instanceof Error ? err.message : String(err) });
2301
2389
  }
2302
- }
2303
2390
 
2304
- const results = [...resultsByUrl.values()].slice(0, 20).map((result, index) => ({ ...result, rank: index + 1 }));
2305
- let fetched: ExtractedContent[] = [];
2306
- if (params.fetchContent && results.length > 0) {
2307
- const urls = results.slice(0, 5).map((result) => result.url);
2308
- try {
2309
- fetched = await fetchAllContent(urls, signal);
2310
- } catch (err) {
2311
- if (signal?.aborted || isAbortError(err)) throw err;
2312
- fetched = urls.map((url) => ({ url, title: "", content: "", error: err instanceof Error ? err.message : String(err) }));
2313
- }
2314
- }
2315
- const artifact = withClaimAssessment(buildResearchArtifact({
2316
- query: claim,
2317
- provider,
2318
- summary: summaries.length > 0 ? summaries.join("\n\n") : undefined,
2319
- results,
2320
- fetched,
2321
- recency: recencyFilter,
2322
- domainFilter,
2323
- }), [claim]);
2324
- if (errors.length > 0) artifact.errors = errors;
2325
- storeResearchArtifact(artifact);
2326
- pi.appendEntry("web-search-results", {
2327
- id: artifact.id,
2328
- type: "research",
2329
- timestamp: artifact.timestamp,
2330
- artifact,
2391
+ const results = [...resultsByUrl.values()].slice(0, 20).map((result, index) => ({ ...result, rank: index + 1 }));
2392
+ let fetched: ExtractedContent[] = [];
2393
+ if (params.fetchContent && results.length > 0) {
2394
+ const urls = results.slice(0, 5).map((result) => result.url);
2395
+ try {
2396
+ fetched = await fetchAllContent(urls, signal, withRegisteredFetchOptions(undefined, registeredToolNames, typeof params.proxy === "string" ? params.proxy : undefined));
2397
+ } catch (err) {
2398
+ if (signal?.aborted || isAbortError(err)) throw err;
2399
+ fetched = urls.map((url) => ({ url, title: "", content: "", error: err instanceof Error ? err.message : String(err) }));
2400
+ }
2401
+ }
2402
+ const artifact = withClaimAssessment(buildResearchArtifact({
2403
+ query: claim,
2404
+ provider,
2405
+ summary: summaries.length > 0 ? summaries.join("\n\n") : undefined,
2406
+ results,
2407
+ fetched,
2408
+ recency: recencyFilter,
2409
+ domainFilter,
2410
+ }), [claim]);
2411
+ if (errors.length > 0) artifact.errors = errors;
2412
+ storeResearchArtifact(artifact);
2413
+ pi.appendEntry("web-search-results", {
2414
+ id: artifact.id,
2415
+ type: "research",
2416
+ timestamp: artifact.timestamp,
2417
+ artifact,
2418
+ });
2419
+ return {
2420
+ content: [{ type: "text", text: formatSourceCheckResult(artifact, getSearchContentEnabled ? toolNames.getSearchContent : null) }],
2421
+ details: { responseId: artifact.id, artifact, sourceCount: artifact.sources.length, passageCount: artifact.passages.length },
2422
+ };
2331
2423
  });
2332
- return {
2333
- content: [{ type: "text", text: formatSourceCheckResult(artifact, getSearchContentEnabled ? toolNames.getSearchContent : null) }],
2334
- details: { responseId: artifact.id, artifact, sourceCount: artifact.sources.length, passageCount: artifact.passages.length },
2335
- };
2336
2424
  },
2337
2425
  });
2338
2426
 
@@ -2371,6 +2459,9 @@ export default function (pi: ExtensionAPI) {
2371
2459
  auth: Type.Optional(Type.Union([Type.String(), Type.Boolean()], {
2372
2460
  description: "Opt into an authFetch profile for local browser-cookie fetching. Use a profile name, or true only when exactly one profile exists.",
2373
2461
  })),
2462
+ proxy: Type.Optional(Type.String({
2463
+ description: "http(s) proxy URL (e.g. http://host:port) used for this fetch. Needed when the target is unreachable directly; localhost and NO_PROXY hosts always bypass the proxy. Empty string forces direct access.",
2464
+ })),
2374
2465
  }),
2375
2466
 
2376
2467
  async execute(_toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<Record<string, unknown>>> {
@@ -2382,164 +2473,164 @@ export default function (pi: ExtensionAPI) {
2382
2473
  return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
2383
2474
  }
2384
2475
  const { urlList, options } = normalized;
2385
- const mode = options.mode ?? "readable";
2386
- if (mode === "answer" && !options.prompt) {
2387
- return { content: [{ type: "text", text: "Error: mode answer requires prompt." }], details: { error: "mode answer requires prompt" } };
2388
- }
2389
- if (mode === "raw" && (options.forceClone === true || options.timestamp || options.frames || options.prompt || options.model || options.answerModel)) {
2390
- return { content: [{ type: "text", text: "Error: mode raw cannot be combined with forceClone, prompt, timestamp, frames, model, or answerModel." }], details: { error: "Incompatible raw mode options" } };
2391
- }
2392
- if (mode !== "answer" && options.answerModel) {
2393
- return { content: [{ type: "text", text: "Error: answerModel requires mode answer." }], details: { error: "answerModel requires mode answer" } };
2394
- }
2395
- if (mode === "answer" && options.model) {
2396
- return { content: [{ type: "text", text: "Error: use answerModel, not model, with mode answer." }], details: { error: "model is incompatible with mode answer" } };
2397
- }
2398
- if (mode === "answer" && options.auth !== undefined) {
2399
- return { content: [{ type: "text", text: "Error: auth cannot be combined with mode answer." }], details: { error: "auth cannot be combined with mode answer" } };
2400
- }
2401
- let authFetchProfile: AuthFetchProfile | undefined;
2402
- if (options.auth !== undefined) {
2403
- try {
2404
- authFetchProfile = resolveAuthFetchProfile(options.auth);
2405
- } catch (err) {
2406
- const error = err instanceof Error ? err.message : String(err);
2407
- return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
2476
+ return runWithProxy(options.proxy, async () => {
2477
+ const mode = options.mode ?? "readable";
2478
+ if (mode === "answer" && !options.prompt) {
2479
+ return { content: [{ type: "text", text: "Error: mode answer requires prompt." }], details: { error: "mode answer requires prompt" } };
2408
2480
  }
2409
- }
2410
- if (urlList.length === 0) {
2411
- return {
2412
- content: [{ type: "text", text: "Error: No URL provided." }],
2413
- details: { error: "No URL provided" },
2414
- };
2415
- }
2416
-
2417
- onUpdate?.({
2418
- content: [{ type: "text", text: `Fetching ${urlList.length} URL(s)...` }],
2419
- details: { phase: "fetch", progress: 0 },
2420
- });
2421
-
2422
- const { answerModel: _answerModel, auth: _auth, ...extractionOptions } = options;
2423
- const fetchOptions = mode === "answer"
2424
- ? (() => {
2425
- const { prompt: _prompt, ...rest } = extractionOptions;
2426
- return { ...rest, ...(authFetchProfile ? { authFetchProfile } : {}) };
2427
- })()
2428
- : { ...extractionOptions, ...(authFetchProfile ? { authFetchProfile } : {}) };
2429
- const fetchResults = await fetchAllContent(urlList, signal, fetchOptions);
2430
- const presentedResults = mode === "answer"
2431
- ? await Promise.all(fetchResults.map(async result => {
2432
- if (result.error) return result;
2433
- if (result.thumbnail || result.mimeType?.startsWith("image/")) {
2434
- return { ...result, error: "Page answer requires textual fetched content" };
2435
- }
2481
+ if (mode === "raw" && (options.forceClone === true || options.timestamp || options.frames || options.prompt || options.model || options.answerModel)) {
2482
+ return { content: [{ type: "text", text: "Error: mode raw cannot be combined with forceClone, prompt, timestamp, frames, model, or answerModel." }], details: { error: "Incompatible raw mode options" } };
2483
+ }
2484
+ if (mode !== "answer" && options.answerModel) {
2485
+ return { content: [{ type: "text", text: "Error: answerModel requires mode answer." }], details: { error: "answerModel requires mode answer" } };
2486
+ }
2487
+ if (mode === "answer" && options.model) {
2488
+ return { content: [{ type: "text", text: "Error: use answerModel, not model, with mode answer." }], details: { error: "model is incompatible with mode answer" } };
2489
+ }
2490
+ if (mode === "answer" && options.auth !== undefined) {
2491
+ return { content: [{ type: "text", text: "Error: auth cannot be combined with mode answer." }], details: { error: "auth cannot be combined with mode answer" } };
2492
+ }
2493
+ let authFetchProfile: AuthFetchProfile | undefined;
2494
+ if (options.auth !== undefined) {
2436
2495
  try {
2437
- const answer = await answerFromPage({
2438
- question: options.prompt!,
2439
- pageText: result.content,
2440
- sourceUrl: result.url,
2441
- ...(options.answerModel ? { model: options.answerModel } : {}),
2442
- }, ctx, signal);
2443
- return { ...result, content: answer.text };
2496
+ authFetchProfile = resolveAuthFetchProfile(options.auth);
2444
2497
  } catch (err) {
2445
- return { ...result, error: `Page answer failed: ${err instanceof Error ? err.message : String(err)}` };
2498
+ const error = err instanceof Error ? err.message : String(err);
2499
+ return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
2446
2500
  }
2447
- }))
2448
- : fetchResults;
2449
- const successful = presentedResults.filter((r) => !r.error).length;
2450
- const totalChars = presentedResults.reduce((sum, r) => sum + r.content.length, 0);
2451
-
2452
- const responseId = generateId();
2453
- const data = {
2454
- id: responseId,
2455
- type: "fetch",
2456
- timestamp: Date.now(),
2457
- urls: stripThumbnails(fetchResults),
2458
- } satisfies StoredSearchData & { type: "fetch"; urls: ExtractedContent[] };
2459
- const storedContent = storeFetchResult(pi, responseId, data, authFetchProfile);
2460
-
2461
- // Single URL: return content directly (possibly truncated) with responseId
2462
- if (urlList.length === 1) {
2463
- const result = presentedResults[0];
2464
- if (result.error) {
2501
+ }
2502
+ if (urlList.length === 0) {
2465
2503
  return {
2466
- content: [{ type: "text", text: `Error: ${result.error}` }],
2467
- details: { urls: urlList, urlCount: 1, successful: 0, error: result.error, ...(storedContent ? { responseId } : {}), prompt: params.prompt, timestamp: params.timestamp, frames: params.frames },
2504
+ content: [{ type: "text", text: "Error: No URL provided." }],
2505
+ details: { error: "No URL provided" },
2468
2506
  };
2469
2507
  }
2470
2508
 
2471
- const fullLength = result.content.length;
2472
- const slice = initialContentSlice(result.content, getMaxInlineContentChars());
2473
- const truncated = slice.endOffset < fullLength;
2474
- let output = slice.text;
2509
+ onUpdate?.({
2510
+ content: [{ type: "text", text: `Fetching ${urlList.length} URL(s)...` }],
2511
+ details: { phase: "fetch", progress: 0 },
2512
+ });
2513
+
2514
+ const { answerModel: _answerModel, auth: _auth, ...extractionOptions } = options;
2515
+ const fetchOptions = mode === "answer"
2516
+ ? (() => {
2517
+ const { prompt: _prompt, ...rest } = extractionOptions;
2518
+ return { ...rest, ...(authFetchProfile ? { authFetchProfile } : {}) };
2519
+ })()
2520
+ : { ...extractionOptions, ...(authFetchProfile ? { authFetchProfile } : {}) };
2521
+ const fetchResults = await fetchAllContent(urlList, signal, withRegisteredFetchOptions(fetchOptions, registeredToolNames, options.proxy));
2522
+ const presentedResults = mode === "answer"
2523
+ ? await Promise.all(fetchResults.map(async result => {
2524
+ if (result.error) return result;
2525
+ if (result.thumbnail || result.mimeType?.startsWith("image/")) {
2526
+ return { ...result, error: "Page answer requires textual fetched content" };
2527
+ }
2528
+ try {
2529
+ const answer = await answerFromPage({
2530
+ question: options.prompt!,
2531
+ pageText: result.content,
2532
+ sourceUrl: result.url,
2533
+ ...(options.answerModel ? { model: options.answerModel } : {}),
2534
+ }, ctx, signal);
2535
+ return { ...result, content: answer.text };
2536
+ } catch (err) {
2537
+ return { ...result, error: `Page answer failed: ${err instanceof Error ? err.message : String(err)}` };
2538
+ }
2539
+ }))
2540
+ : fetchResults;
2541
+ const successful = presentedResults.filter((r) => !r.error).length;
2542
+ const totalChars = presentedResults.reduce((sum, r) => sum + r.content.length, 0);
2543
+
2544
+ const responseId = generateId();
2545
+ const data = {
2546
+ id: responseId,
2547
+ type: "fetch",
2548
+ timestamp: Date.now(),
2549
+ urls: stripThumbnails(fetchResults),
2550
+ } satisfies StoredSearchData & { type: "fetch"; urls: ExtractedContent[] };
2551
+ const storedContent = storeFetchResult(pi, responseId, data, authFetchProfile);
2552
+
2553
+ if (urlList.length === 1) {
2554
+ const result = presentedResults[0];
2555
+ if (result.error) {
2556
+ return {
2557
+ content: [{ type: "text", text: `Error: ${result.error}` }],
2558
+ details: { urls: urlList, urlCount: 1, successful: 0, error: result.error, ...(storedContent ? { responseId } : {}), prompt: params.prompt, timestamp: params.timestamp, frames: params.frames },
2559
+ };
2560
+ }
2561
+
2562
+ const fullLength = result.content.length;
2563
+ const slice = initialContentSlice(result.content, getMaxInlineContentChars());
2564
+ const truncated = slice.endOffset < fullLength;
2565
+ let output = slice.text;
2566
+
2567
+ if (truncated) {
2568
+ output += `\n\n---\nShowing ${slice.endOffset} of ${fullLength} chars, ${slice.shownBytes} of ${slice.totalBytes} bytes, and ${slice.shownLines} of ${slice.totalLines} lines. `;
2569
+ output += storedContent
2570
+ ? getSearchContentEnabled
2571
+ ? `Use ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0, offset: ${slice.endOffset} }) for the next slice.`
2572
+ : "Content retrieval is not registered."
2573
+ : "Authenticated fetch cache is off; repeat the fetch to read more.";
2574
+ }
2575
+
2576
+ const content: Array<TextContent | ImageContent> = [];
2577
+ if (result.frames?.length) {
2578
+ for (const frame of result.frames) {
2579
+ content.push({ type: "image", data: frame.data, mimeType: frame.mimeType });
2580
+ content.push({ type: "text", text: `Frame at ${frame.timestamp}` });
2581
+ }
2582
+ } else if (result.thumbnail) {
2583
+ content.push({ type: "image", data: result.thumbnail.data, mimeType: result.thumbnail.mimeType });
2584
+ }
2585
+ content.push({ type: "text", text: output });
2475
2586
 
2476
- if (truncated) {
2477
- output += `\n\n---\nShowing ${slice.endOffset} of ${fullLength} chars, ${slice.shownBytes} of ${slice.totalBytes} bytes, and ${slice.shownLines} of ${slice.totalLines} lines. `;
2478
- output += storedContent
2479
- ? getSearchContentEnabled
2480
- ? `Use ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0, offset: ${slice.endOffset} }) for the next slice.`
2481
- : "Content retrieval is not registered."
2482
- : "Authenticated fetch cache is off; repeat the fetch to read more.";
2587
+ const imageCount = (result.frames?.length ?? 0) + (result.thumbnail ? 1 : 0);
2588
+ return {
2589
+ content,
2590
+ details: {
2591
+ urls: urlList,
2592
+ urlCount: 1,
2593
+ successful: 1,
2594
+ totalChars: fullLength,
2595
+ title: result.title,
2596
+ ...(storedContent ? { responseId } : {}),
2597
+ truncated,
2598
+ hasImage: imageCount > 0,
2599
+ imageCount,
2600
+ prompt: params.prompt,
2601
+ timestamp: params.timestamp,
2602
+ frames: params.frames,
2603
+ duration: result.duration,
2604
+ mode,
2605
+ mimeType: result.mimeType,
2606
+ status: result.status,
2607
+ totalBytes: slice.totalBytes,
2608
+ totalLines: slice.totalLines,
2609
+ shownBytes: slice.shownBytes,
2610
+ shownLines: slice.shownLines,
2611
+ },
2612
+ };
2483
2613
  }
2484
2614
 
2485
- const content: Array<TextContent | ImageContent> = [];
2486
- if (result.frames?.length) {
2487
- for (const frame of result.frames) {
2488
- content.push({ type: "image", data: frame.data, mimeType: frame.mimeType });
2489
- content.push({ type: "text", text: `Frame at ${frame.timestamp}` });
2615
+ let output = "## Fetched URLs\n\n";
2616
+ for (const { url, title, content, error } of presentedResults) {
2617
+ if (error) {
2618
+ output += `- ${url}: Error - ${error}\n`;
2619
+ } else {
2620
+ output += `- ${title || url} (${content.length} chars)\n`;
2490
2621
  }
2491
- } else if (result.thumbnail) {
2492
- content.push({ type: "image", data: result.thumbnail.data, mimeType: result.thumbnail.mimeType });
2493
2622
  }
2494
- content.push({ type: "text", text: output });
2623
+ output += storedContent
2624
+ ? getSearchContentEnabled
2625
+ ? `\n---\nUse ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0 }) to retrieve bounded content slices.`
2626
+ : "\n---\nContent retrieval is not registered."
2627
+ : "\n---\nAuthenticated fetch cache is off; repeat the fetch to read content.";
2495
2628
 
2496
- const imageCount = (result.frames?.length ?? 0) + (result.thumbnail ? 1 : 0);
2497
2629
  return {
2498
- content,
2499
- details: {
2500
- urls: urlList,
2501
- urlCount: 1,
2502
- successful: 1,
2503
- totalChars: fullLength,
2504
- title: result.title,
2505
- ...(storedContent ? { responseId } : {}),
2506
- truncated,
2507
- hasImage: imageCount > 0,
2508
- imageCount,
2509
- prompt: params.prompt,
2510
- timestamp: params.timestamp,
2511
- frames: params.frames,
2512
- duration: result.duration,
2513
- mode,
2514
- mimeType: result.mimeType,
2515
- status: result.status,
2516
- totalBytes: slice.totalBytes,
2517
- totalLines: slice.totalLines,
2518
- shownBytes: slice.shownBytes,
2519
- shownLines: slice.shownLines,
2520
- },
2630
+ content: [{ type: "text", text: output }],
2631
+ details: { urls: urlList, urlCount: urlList.length, successful, totalChars, ...(storedContent ? { responseId } : {}) },
2521
2632
  };
2522
- }
2523
-
2524
- // Multi-URL: existing behavior (summary + responseId)
2525
- let output = "## Fetched URLs\n\n";
2526
- for (const { url, title, content, error } of presentedResults) {
2527
- if (error) {
2528
- output += `- ${url}: Error - ${error}\n`;
2529
- } else {
2530
- output += `- ${title || url} (${content.length} chars)\n`;
2531
- }
2532
- }
2533
- output += storedContent
2534
- ? getSearchContentEnabled
2535
- ? `\n---\nUse ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0 }) to retrieve bounded content slices.`
2536
- : "\n---\nContent retrieval is not registered."
2537
- : "\n---\nAuthenticated fetch cache is off; repeat the fetch to read content.";
2538
-
2539
- return {
2540
- content: [{ type: "text", text: output }],
2541
- details: { urls: urlList, urlCount: urlList.length, successful, totalChars, ...(storedContent ? { responseId } : {}) },
2542
- };
2633
+ });
2543
2634
  },
2544
2635
 
2545
2636
  renderCall(args, theme) {
@@ -2689,24 +2780,17 @@ export default function (pi: ExtensionAPI) {
2689
2780
  queryIndex: Type.Optional(Type.Integer({ minimum: 0, description: "Get content for query at index" })),
2690
2781
  url: Type.Optional(Type.String({ description: "Get content for this URL" })),
2691
2782
  urlIndex: Type.Optional(Type.Integer({ minimum: 0, description: "Get content for URL at index" })),
2692
- offset: Type.Optional(Type.Integer({ minimum: 0, description: "Character offset for fetched URL content slices (default 0). Cannot be combined with findText." })),
2693
- limit: Type.Optional(Type.Integer({ minimum: 1, maximum: maxInlineContentChars, description: "Maximum characters to return for fetched URL content slices (default and max are set by maxInlineContentChars). Cannot be combined with findText." })),
2783
+ offset: Type.Optional(Type.Integer({ minimum: 0, description: "Character offset for fetched URL content slices (default 0). Ignored when findText is supplied." })),
2784
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: maxInlineContentChars, description: "Maximum characters to return for fetched URL content slices (default and max are set by maxInlineContentChars). Ignored when findText is supplied." })),
2694
2785
  findText: Type.Optional(Type.Union([
2695
2786
  Type.String({ minLength: 1, maxLength: 500 }),
2696
2787
  Type.Array(Type.String({ minLength: 1, maxLength: 500 }), { minItems: 1, maxItems: 10 }),
2697
- ], { description: "Text or texts to find in the selected stored content. Cannot be combined with offset or limit." })),
2788
+ ], { description: "Text or texts to find in the selected stored content. When supplied, offset and limit are ignored." })),
2698
2789
  findMode: Type.Optional(StringEnum(["exact", "case-insensitive", "fuzzy"], { description: "Matching mode for findText (default: case-insensitive). Requires findText." })),
2699
2790
  }),
2700
2791
 
2701
- async execute(_toolCallId, params): Promise<AgentToolResult<Record<string, unknown>>> {
2702
- if (params.findText !== undefined && (params.offset !== undefined || params.limit !== undefined)) {
2703
- const offset = formatInputValue(params.offset);
2704
- const limit = formatInputValue(params.limit);
2705
- return {
2706
- content: [{ type: "text", text: `findText cannot be combined with offset or limit. Received offset=${offset}, limit=${limit}; omit offset and limit when using findText.` }],
2707
- details: { error: "Incompatible find options" },
2708
- };
2709
- }
2792
+ async execute(_toolCallId, rawParams): Promise<AgentToolResult<Record<string, unknown>>> {
2793
+ const params = normalizeGetSearchContentParams(rawParams);
2710
2794
  if (params.findMode !== undefined && params.findText === undefined) {
2711
2795
  return {
2712
2796
  content: [{ type: "text", text: `findMode ${formatInputValue(params.findMode)} requires findText; provide findText or omit findMode.` }],
@@ -2730,6 +2814,22 @@ export default function (pi: ExtensionAPI) {
2730
2814
  };
2731
2815
  }
2732
2816
  const serialized = JSON.stringify(artifact, null, 2);
2817
+ if (params.findText !== undefined) {
2818
+ try {
2819
+ const found = findContent(serialized, normalizeFindQueries(params.findText), params.findMode ?? "case-insensitive");
2820
+ const { text, ...findDetails } = found;
2821
+ return {
2822
+ content: [{ type: "text", text }],
2823
+ details: { responseId: artifact.id, type: "research", contentLength: serialized.length, findMode: params.findMode ?? "case-insensitive", ...findDetails },
2824
+ };
2825
+ } catch (err) {
2826
+ const error = err instanceof Error ? err.message : String(err);
2827
+ return {
2828
+ content: [{ type: "text", text: `Unable to find ${formatInputValue(params.findText)} in research artifact for responseId ${formatInputValue(params.responseId)}: ${error}. Check findText and use a supported findMode.` }],
2829
+ details: { error, responseId: params.responseId, type: "research" },
2830
+ };
2831
+ }
2832
+ }
2733
2833
  const offset = params.offset ?? 0;
2734
2834
  const limit = params.limit ?? maxInlineContentChars;
2735
2835
  if (!Number.isInteger(offset) || offset < 0) {
@@ -2798,7 +2898,7 @@ export default function (pi: ExtensionAPI) {
2798
2898
  const fullResults = formatFullResults(queryData);
2799
2899
  if (params.findText !== undefined) {
2800
2900
  try {
2801
- const found = findContent(fullResults, normalizeFindQueries(params.findText), (params.findMode ?? "case-insensitive") as FindMode);
2901
+ const found = findContent(fullResults, normalizeFindQueries(params.findText), params.findMode ?? "case-insensitive");
2802
2902
  const { text, ...findDetails } = found;
2803
2903
  return {
2804
2904
  content: [{ type: "text", text }],
@@ -2860,7 +2960,7 @@ export default function (pi: ExtensionAPI) {
2860
2960
 
2861
2961
  if (params.findText !== undefined) {
2862
2962
  try {
2863
- const found = findContent(urlData.content, normalizeFindQueries(params.findText), (params.findMode ?? "case-insensitive") as FindMode);
2963
+ const found = findContent(urlData.content, normalizeFindQueries(params.findText), params.findMode ?? "case-insensitive");
2864
2964
  const { text, ...findDetails } = found;
2865
2965
  return {
2866
2966
  content: [{ type: "text", text: `# ${urlData.title || urlData.url}\n\n${text}` }],
@@ -3321,14 +3421,16 @@ export default function (pi: ExtensionAPI) {
3321
3421
  const cookies = await isGeminiWebAvailable();
3322
3422
  if (!cookies) {
3323
3423
  const diagnostic = getGeminiWebAvailabilityDiagnostic();
3424
+ const diagnosticDetails = getGeminiWebAvailabilityDiagnosticDetails();
3425
+ const attempted = formatCookieAttempts(diagnosticDetails?.attempts ?? []);
3324
3426
  const text = diagnostic
3325
- ? `Gemini Web is unavailable: ${diagnostic}`
3427
+ ? `Gemini Web is unavailable: ${diagnostic}${attempted ? ` Attempted browser profiles: ${attempted}.` : ""}`
3326
3428
  : "Gemini Web is unavailable. Sign into gemini.google.com in a supported Chromium-based browser.";
3327
3429
  pi.sendMessage({
3328
3430
  customType: "google-account",
3329
3431
  content: [{ type: "text", text }],
3330
3432
  display: true,
3331
- details: { available: false, cookieAccessAllowed: true, diagnostic },
3433
+ details: { available: false, cookieAccessAllowed: true, diagnostic, cookieDiagnostic: diagnosticDetails },
3332
3434
  }, { triggerTurn: true, deliverAs: "followUp" });
3333
3435
  return;
3334
3436
  }
@@ -3347,6 +3449,10 @@ export default function (pi: ExtensionAPI) {
3347
3449
  },
3348
3450
  });
3349
3451
 
3452
+ function formatCookieAttempts(attempts: { browser: string; profile: string; status: string }[]): string {
3453
+ return attempts.map(({ browser, profile, status }) => `${browser}/${profile} (${status})`).join(", ");
3454
+ }
3455
+
3350
3456
  if (isCommandEnabled(initConfig, "search")) pi.registerCommand("search", {
3351
3457
  description: "Browse stored web search results",
3352
3458
  handler: async (_args, ctx) => {