@herbertgao/pi-extensions 2026.8.5 → 2026.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (389) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
  19. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  20. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  21. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  24. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
  25. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  26. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  27. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  28. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  29. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  30. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  32. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  33. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
  34. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  35. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  51. package/node_modules/@narumitw/pi-btw/README.md +119 -103
  52. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  53. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  54. package/node_modules/@narumitw/pi-btw/package.json +13 -9
  55. package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
  56. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  57. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  58. package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
  59. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  60. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  61. package/node_modules/pi-lens/CHANGELOG.md +1004 -0
  62. package/node_modules/pi-lens/README.md +11 -3
  63. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  64. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  65. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
  66. package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
  67. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  68. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  69. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  70. package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
  71. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  72. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  73. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  74. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  75. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  76. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  77. package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
  78. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  79. package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
  80. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  81. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  82. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  83. package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
  84. package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
  85. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  86. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  87. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  88. package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
  89. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
  90. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  91. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
  93. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
  94. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  95. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  129. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  130. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  131. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  132. package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
  133. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  134. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  135. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  136. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  137. package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
  138. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  139. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  140. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  141. package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
  142. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  143. package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
  144. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
  145. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  146. package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
  147. package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
  148. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  149. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  150. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  151. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
  154. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  155. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
  157. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  158. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  159. package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
  160. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  161. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
  162. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  163. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
  164. package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
  165. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
  166. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  167. package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
  168. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  169. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  170. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  171. package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
  172. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  173. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
  175. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  176. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
  177. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  178. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  179. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  180. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  181. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
  182. package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
  183. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  184. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  185. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  186. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  187. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  188. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  189. package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
  190. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  191. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  195. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  198. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  199. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  200. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  201. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  204. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  205. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  206. package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  217. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  218. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  219. package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
  220. package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
  221. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  222. package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
  223. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  224. package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
  225. package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
  226. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  227. package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
  228. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  229. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  230. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  233. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  234. package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
  235. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  236. package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
  237. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  238. package/node_modules/pi-lens/dist/index.js +64677 -49484
  239. package/node_modules/pi-lens/dist/mcp/server.js +8 -2
  240. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  241. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  242. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  243. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
  244. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
  245. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  246. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  247. package/node_modules/pi-lens/docs/agent-guide.md +44 -14
  248. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
  249. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  250. package/node_modules/pi-lens/docs/features.md +33 -6
  251. package/node_modules/pi-lens/docs/globalconfig.md +14 -0
  252. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  253. package/node_modules/pi-lens/docs/mcp.md +2 -2
  254. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  255. package/node_modules/pi-lens/docs/settings.md +25 -1
  256. package/node_modules/pi-lens/docs/usage.md +24 -5
  257. package/node_modules/pi-lens/docs/word-index.md +35 -0
  258. package/node_modules/pi-lens/package.json +10 -6
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  319. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  320. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  321. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  322. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  323. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  324. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
  325. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  326. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  327. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
  328. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  329. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
  330. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
  331. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  332. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  333. package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
  334. package/node_modules/pi-mcp-adapter/README.md +51 -6
  335. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  336. package/node_modules/pi-mcp-adapter/cli.js +188 -5
  337. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  338. package/node_modules/pi-mcp-adapter/config.ts +28 -10
  339. package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
  340. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  341. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  342. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  343. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  344. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  345. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  346. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  347. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  348. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  349. package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
  350. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  351. package/node_modules/pi-mcp-adapter/package.json +4 -1
  352. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  353. package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
  354. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  355. package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
  356. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  357. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  358. package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
  359. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  360. package/node_modules/pi-mcp-adapter/types.ts +95 -10
  361. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  362. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  363. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  364. package/node_modules/pi-web-access/README.md +38 -14
  365. package/node_modules/pi-web-access/anysearch.ts +4 -2
  366. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  367. package/node_modules/pi-web-access/brave.ts +16 -4
  368. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  369. package/node_modules/pi-web-access/curator-page.ts +8 -3
  370. package/node_modules/pi-web-access/curator-server.ts +5 -1
  371. package/node_modules/pi-web-access/exa.ts +18 -7
  372. package/node_modules/pi-web-access/extract.ts +134 -37
  373. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  374. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  375. package/node_modules/pi-web-access/gemini-search.ts +33 -7
  376. package/node_modules/pi-web-access/index.ts +160 -104
  377. package/node_modules/pi-web-access/package.json +2 -2
  378. package/node_modules/pi-web-access/page-query.ts +4 -2
  379. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  380. package/node_modules/pi-web-access/parallel.ts +2 -2
  381. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  382. package/node_modules/pi-web-access/serper.ts +211 -0
  383. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  384. package/node_modules/pi-web-access/summary-review.ts +51 -23
  385. package/node_modules/pi-web-access/tavily.ts +16 -4
  386. package/node_modules/pi-web-access/utils.ts +87 -0
  387. package/node_modules/pi-web-access/valyu.ts +199 -0
  388. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  389. package/package.json +13 -13
@@ -1,11 +1,13 @@
1
1
  import type { AgentToolResult, ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import { Box, Text, truncateToWidth, type KeyId } from "@earendil-works/pi-tui";
3
3
  import { Type } from "typebox";
4
- import { StringEnum, complete, type Api, type ImageContent, type Model, type TextContent } from "@earendil-works/pi-ai/compat";
4
+ import { StringEnum, type ImageContent, type TextContent } from "@earendil-works/pi-ai/compat";
5
5
  import type { ExtractedContent, ExtractOptions } from "./extract.ts";
6
6
  import { normalizeFetchContentParams } from "./fetch-params.ts";
7
+ import { resolveAuthFetchProfile, type AuthFetchProfile } from "./auth-fetch.ts";
7
8
  import { findContent, type FindMode } from "./content-find.ts";
8
9
  import { answerFromPage } from "./page-query.ts";
10
+ import { rewriteSearchQuery } from "./query-rewrite.ts";
9
11
  import { clearCloneCache } from "./github-extract.ts";
10
12
  import { getConfiguredSearchRouting, normalizeSearchProviderSelection, RESOLVED_SEARCH_PROVIDERS, SEARCH_PROVIDERS, search, type AttributedSearchResponse, type SearchProvider, type SearchProviderSelection, type ResolvedSearchProvider } from "./gemini-search.ts";
11
13
  import type { SearchResult } from "./perplexity.ts";
@@ -45,11 +47,13 @@ import { isBrowserCookieAccessAllowed } from "./gemini-web-config.ts";
45
47
  import { isBraveAvailable } from "./brave.ts";
46
48
  import { isOpenAISearchAvailable } from "./openai-search.ts";
47
49
  import { isParallelAvailable } from "./parallel.ts";
50
+ import { isParallelMcpAvailable } from "./parallel-mcp.ts";
48
51
  import { isTinyFishAvailable } from "./tinyfish.ts";
49
52
  import { isSearch1APIAvailable } from "./search1api.ts";
50
53
  import { isSearchinfinityAvailable } from "./searchinfinity.ts";
51
54
  import { isQueritAvailable } from "./querit.ts";
52
55
  import { isTavilyAvailable } from "./tavily.ts";
56
+ import { isFirecrawlAvailable } from "./firecrawl.ts";
53
57
  import { isJinaSearchAvailable } from "./jina-search.ts";
54
58
  import { isSerpdiveAvailable } from "./serpdive.ts";
55
59
  import { isKagiAvailable } from "./kagi.ts";
@@ -61,8 +65,10 @@ import { isAnySearchAvailable } from "./anysearch.ts";
61
65
  import { isXaiSearchAvailable } from "./xai-search.ts";
62
66
  import { isBrightDataAvailable } from "./brightdata.ts";
63
67
  import { isSerpBaseAvailable } from "./serpbase.ts";
68
+ import { isSerperAvailable } from "./serper.ts";
69
+ import { isValyuAvailable } from "./valyu.ts";
64
70
  import { buildSearchErrorPlan, type SearchErrorDetails, type SearchErrorPlan } from "./render-search-error.ts";
65
- import { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns } from "./summary-model-scope.ts";
71
+ import { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns, splitThinkingSuffix } from "./summary-model-scope.ts";
66
72
  import {
67
73
  buildResearchArtifact,
68
74
  withClaimAssessment,
@@ -115,7 +121,9 @@ interface WebSearchConfig {
115
121
  kagiApiKey?: unknown;
116
122
  ollamaApiKey?: unknown;
117
123
  serpbaseApiKey?: unknown;
124
+ serperApiKey?: unknown;
118
125
  tinyfishApiKey?: unknown;
126
+ valyuApiKey?: unknown;
119
127
  xaiApiKey?: unknown;
120
128
  provider?: unknown;
121
129
  searchProvider?: unknown;
@@ -149,11 +157,13 @@ interface ProviderAvailability {
149
157
  openai: boolean;
150
158
  brave: boolean;
151
159
  parallel: boolean;
160
+ "parallel-mcp": boolean;
152
161
  tinyfish: boolean;
153
162
  search1api: boolean;
154
163
  searchinfinity: boolean;
155
164
  querit: boolean;
156
165
  tavily: boolean;
166
+ firecrawl: boolean;
157
167
  jina: boolean;
158
168
  serpdive: boolean;
159
169
  searxng: boolean;
@@ -168,6 +178,8 @@ interface ProviderAvailability {
168
178
  xai: boolean;
169
179
  brightdata: boolean;
170
180
  serpbase: boolean;
181
+ serper: boolean;
182
+ valyu: boolean;
171
183
  }
172
184
 
173
185
  type WebSearchWorkflow = "none" | "summary-review" | "auto-summary";
@@ -219,8 +231,6 @@ type ToolNames = {
219
231
  getSearchContent: string;
220
232
  };
221
233
 
222
- type ProviderHeaders = Record<string, string | null>;
223
-
224
234
  const DEFAULT_TOOL_NAMES: ToolNames = {
225
235
  webSearch: "web_search",
226
236
  sourceCheck: "source_check",
@@ -379,11 +389,13 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
379
389
  openai: await isOpenAISearchAvailable(ctx),
380
390
  brave: isBraveAvailable(),
381
391
  parallel: isParallelAvailable(),
392
+ "parallel-mcp": isParallelMcpAvailable(),
382
393
  tinyfish: isTinyFishAvailable(),
383
394
  search1api: isSearch1APIAvailable(),
384
395
  searchinfinity: isSearchinfinityAvailable(),
385
396
  querit: isQueritAvailable(),
386
397
  tavily: isTavilyAvailable(),
398
+ firecrawl: isFirecrawlAvailable(),
387
399
  jina: isJinaSearchAvailable(),
388
400
  serpdive: isSerpdiveAvailable(),
389
401
  kagi: isKagiAvailable(),
@@ -398,10 +410,12 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
398
410
  xai: await isXaiSearchAvailable(ctx),
399
411
  brightdata: isBrightDataAvailable(),
400
412
  serpbase: isSerpBaseAvailable(),
413
+ serper: isSerperAvailable(),
414
+ valyu: isValyuAvailable(),
401
415
  };
402
416
  return {
403
- // DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase are explicit-only, so they never make `all` eligible.
404
- all: Object.entries(providers).some(([provider, available]) => provider !== "duckduckgo" && provider !== "anysearch" && provider !== "xai" && provider !== "brightdata" && provider !== "serpbase" && provider !== "gemini" && available) || geminiApiAvail,
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,
405
419
  ...providers,
406
420
  };
407
421
  }
@@ -441,6 +455,7 @@ function firstAvailableProvider(available: ProviderAvailability, preferOpenAI: b
441
455
  if (available.searchinfinity) return "searchinfinity";
442
456
  if (available.querit) return "querit";
443
457
  if (available.tavily) return "tavily";
458
+ if (available.firecrawl) return "firecrawl";
444
459
  if (available.jina) return "jina";
445
460
  if (available.serpdive) return "serpdive";
446
461
  if (available.kagi) return "kagi";
@@ -496,6 +511,9 @@ function resolveProvider(
496
511
  if (provider === "tavily" && !available.tavily) {
497
512
  return firstAvailableProvider(available, preferOpenAI, "tavily");
498
513
  }
514
+ if (provider === "firecrawl" && !available.firecrawl) {
515
+ return firstAvailableProvider(available, preferOpenAI, "firecrawl");
516
+ }
499
517
  if (provider === "jina" && !available.jina) {
500
518
  return firstAvailableProvider(available, preferOpenAI, "jina");
501
519
  }
@@ -566,8 +584,8 @@ interface PendingCurate {
566
584
  const DEFAULT_MAX_INLINE_CONTENT_CHARS = 30_000;
567
585
  const MAX_INLINE_CONTENT_CHARS = 200_000;
568
586
 
569
- function getMaxInlineContentChars(): number {
570
- const value = loadConfig().maxInlineContentChars;
587
+ function getMaxInlineContentChars(config = loadConfig()): number {
588
+ const value = config.maxInlineContentChars;
571
589
  if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
572
590
  return DEFAULT_MAX_INLINE_CONTENT_CHARS;
573
591
  }
@@ -578,6 +596,12 @@ function stripThumbnails(results: ExtractedContent[]): ExtractedContent[] {
578
596
  return results.map(({ thumbnail, frames, ...rest }) => rest);
579
597
  }
580
598
 
599
+ function storeFetchResult(pi: { appendEntry(type: string, data: unknown): void }, responseId: string, data: StoredSearchData & { type: "fetch"; urls: ExtractedContent[] }, authProfile?: AuthFetchProfile): boolean {
600
+ if (authProfile?.cache === "off") return false;
601
+ pi.appendEntry("web-search-results", storeFetchedContentResult(responseId, data));
602
+ return true;
603
+ }
604
+
581
605
  function initialContentSlice(content: string, maxChars: number): {
582
606
  text: string;
583
607
  endOffset: number;
@@ -608,6 +632,17 @@ function normalizeFindQueries(value: string | string[]): string[] {
608
632
  return queries;
609
633
  }
610
634
 
635
+ function formatInputValue(value: unknown): string {
636
+ if (typeof value === "string") return JSON.stringify(value);
637
+ if (typeof value === "number") return Number.isNaN(value) ? "NaN" : String(value);
638
+ try {
639
+ const serialized = JSON.stringify(value);
640
+ return serialized === undefined ? String(value) : serialized;
641
+ } catch {
642
+ return String(value);
643
+ }
644
+ }
645
+
611
646
  function formatSearchSummary(results: SearchResult[], answer: string): string {
612
647
  if (results.length === 0) {
613
648
  return answer ? `${answer}\n\n---\n\n**Sources:**\nNo sources returned.` : "No results found.";
@@ -949,10 +984,15 @@ export default function (pi: ExtensionAPI) {
949
984
  } satisfies StoredSearchData & { type: "fetch"; urls: ExtractedContent[] };
950
985
  pi.appendEntry("web-search-results", storeFetchedContentResult(fetchId, data));
951
986
  const ok = fetched.filter(f => !f.error).length;
987
+ const availability = ok === fetched.length
988
+ ? "Full page content now available."
989
+ : ok > 0
990
+ ? "Partial page content now available."
991
+ : "No page content was fetched. Stored fetch diagnostics are available.";
952
992
  pi.sendMessage(
953
993
  {
954
994
  customType: "web-search-content-ready",
955
- content: `Content fetched for ${ok}/${fetched.length} URLs [${fetchId}]. Full page content now available.`,
995
+ content: `Content fetched for ${ok}/${fetched.length} URLs [${fetchId}]. ${availability}`,
956
996
  display: true,
957
997
  },
958
998
  { triggerTurn: true },
@@ -1063,47 +1103,6 @@ export default function (pi: ExtensionAPI) {
1063
1103
  };
1064
1104
  }
1065
1105
 
1066
- async function resolveFirstAvailableModel(
1067
- ctx: SummaryGenerationContext,
1068
- candidates: Array<{ provider: string; id: string }>,
1069
- ): Promise<{ model: Model<Api>; apiKey: string; headers?: ProviderHeaders }> {
1070
- const enabledModelPatterns = loadEnabledModelPatterns(ctx);
1071
- for (const { provider, id } of candidates) {
1072
- const model = findModelWithProviderRouting(ctx.modelRegistry, provider, id);
1073
- if (!model || !modelMatchesEnabledPatterns(model, enabledModelPatterns)) continue;
1074
- const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
1075
- if (auth.ok && auth.apiKey) return { model, apiKey: auth.apiKey, headers: auth.headers };
1076
- }
1077
- throw new Error(`No enabled model available: ${candidates.map(c => `${c.provider}/${c.id}`).join(", ")}`);
1078
- }
1079
-
1080
- async function rewriteSearchQuery(query: string, ctx: SummaryGenerationContext, signal: AbortSignal): Promise<string> {
1081
- const { model, apiKey, headers } = await resolveFirstAvailableModel(ctx, [
1082
- { provider: "anthropic", id: "claude-haiku-4-5" },
1083
- { provider: "google", id: "gemini-3.6-flash" },
1084
- { provider: "openai", id: "gpt-4.1-mini" },
1085
- ]);
1086
- const response = await complete(
1087
- model,
1088
- {
1089
- messages: [{
1090
- role: "user",
1091
- content: [{ type: "text", text: `Rewrite this web search query to get better, more specific results. Add relevant year qualifiers, precise technical terms, and specificity. Return ONLY the improved query text, nothing else.\n\nQuery: ${query}` }],
1092
- timestamp: Date.now(),
1093
- }],
1094
- },
1095
- { apiKey, headers, signal },
1096
- );
1097
- if (response.stopReason === "aborted") throw new Error("Aborted");
1098
- const contentParts = Array.isArray(response.content) ? response.content : [];
1099
- const text = contentParts
1100
- .map(part => part.type === "text" ? part.text : "")
1101
- .join("")
1102
- .trim();
1103
- if (!text) throw new Error("Rewrite returned empty response");
1104
- return text;
1105
- }
1106
-
1107
1106
  async function generateSummaryForSelectedIndices(
1108
1107
  selectedQueryIndices: number[],
1109
1108
  resultsByIndex: Map<number, QueryResultData>,
@@ -1186,20 +1185,30 @@ export default function (pi: ExtensionAPI) {
1186
1185
  const configuredSummaryModel = typeof config.summaryModel === "string" ? config.summaryModel.trim() : "";
1187
1186
  const preferredDefaults = [
1188
1187
  { provider: "anthropic", id: "claude-haiku-4-5" },
1189
- { provider: "openai-codex", id: "gpt-5.3-codex-spark" },
1188
+ { provider: "openai-codex", id: "gpt-5.6-luna" },
1189
+ { provider: "openai-codex", id: "gpt-5.6-terra" },
1190
+ { provider: "google", id: "gemini-3.6-flash" },
1191
+ { provider: "openai", id: "gpt-5-mini" },
1192
+ { provider: "deepseek", id: "deepseek-v4-flash" },
1190
1193
  ];
1191
1194
 
1192
1195
  const resolveAvailableModelValue = (selector: string): string | null => {
1193
- const slashIndex = selector.indexOf("/");
1194
- if (slashIndex <= 0 || slashIndex >= selector.length - 1) return null;
1196
+ const parsed = splitThinkingSuffix(selector);
1197
+ const slashIndex = parsed.value.indexOf("/");
1198
+ if (slashIndex <= 0 || slashIndex >= parsed.value.length - 1) return null;
1195
1199
  const model = findModelWithProviderRouting(
1196
1200
  summaryContext.modelRegistry,
1197
- selector.slice(0, slashIndex),
1198
- selector.slice(slashIndex + 1),
1201
+ parsed.value.slice(0, slashIndex),
1202
+ parsed.value.slice(slashIndex + 1),
1199
1203
  );
1200
1204
  if (!model) return null;
1201
1205
  const value = `${model.provider}/${model.id}`;
1202
- return availableValues.has(value) ? value : null;
1206
+ if (!availableValues.has(value)) return null;
1207
+ if (selector !== value && !seen.has(selector)) {
1208
+ seen.add(selector);
1209
+ summaryModels.push({ value: selector, label: selector });
1210
+ }
1211
+ return selector;
1203
1212
  };
1204
1213
 
1205
1214
  let defaultSummaryModel: string | null = null;
@@ -1636,19 +1645,19 @@ export default function (pi: ExtensionAPI) {
1636
1645
  name: toolNames.webSearch,
1637
1646
  label: "Web Search",
1638
1647
  description:
1639
- `Search the web using OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, AnySearch, xAI, Bright Data, or SerpBase. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; xAI search uses a SuperGrok/X Premium subscription or xAI API key. DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase are available only when explicitly selected. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation or "auto-summary" for a model-generated summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it. Without a configured provider, auto-selects OpenAI when suitable and available, then Exa, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web. When SearXNG is configured, it is preferred first for local/private search.`,
1648
+ `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, auto-selects OpenAI when suitable and available, then Exa, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web. When SearXNG is configured, it is preferred first for local/private search.`,
1640
1649
  promptSnippet:
1641
1650
  "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.",
1642
1651
  parameters: Type.Object({
1643
1652
  query: Type.Optional(Type.String({ description: "Single search query. For research tasks, prefer 'queries' with multiple varied angles instead." })),
1644
1653
  queries: Type.Optional(Type.Array(Type.String(), { description: "Multiple queries searched in sequence, each returning its own synthesized answer. Prefer this for research — vary phrasing, scope, and angle across 2-4 queries to maximize coverage. Good: ['React vs Vue performance benchmarks 2026', 'React vs Vue developer experience comparison', 'React ecosystem size vs Vue ecosystem']. Bad: ['React vs Vue', 'React vs Vue comparison', 'React vs Vue review'] (too similar, redundant results)." })),
1645
- numResults: Type.Optional(Type.Number({ description: "Results per query (default: 5, max: 20)" })),
1654
+ numResults: Type.Optional(Type.Integer({ minimum: 1, maximum: 20, description: "Results per query (default: 5, max: 20)" })),
1646
1655
  includeContent: Type.Optional(Type.Boolean({ description: "Fetch full page content (async)" })),
1647
1656
  recencyFilter: Type.Optional(
1648
1657
  StringEnum(["day", "week", "month", "year"], { description: "Filter by recency" }),
1649
1658
  ),
1650
1659
  domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
1651
- provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase, omit this field to use the configured provider, or use auto when none is configured")),
1660
+ 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")),
1652
1661
  workflow: Type.Optional(
1653
1662
  StringEnum(["none", "summary-review", "auto-summary"], {
1654
1663
  description: "Search workflow mode: none = no curator, summary-review = open curator with auto summary draft (default), auto-summary = generate summary without opening curator",
@@ -2212,11 +2221,11 @@ export default function (pi: ExtensionAPI) {
2212
2221
  parameters: Type.Object({
2213
2222
  claim: Type.String({ description: "The assertion to check against web sources." }),
2214
2223
  queries: Type.Optional(Type.Array(Type.String(), { description: "Search queries (default: the claim)." })),
2215
- numResults: Type.Optional(Type.Number({ description: "Results per query (default: 5, max: 20)." })),
2224
+ numResults: Type.Optional(Type.Integer({ minimum: 1, maximum: 20, description: "Results per query (default: 5, max: 20)." })),
2216
2225
  fetchContent: Type.Optional(Type.Boolean({ description: "Fetch up to 5 result pages for exact passage extraction." })),
2217
2226
  recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency." })),
2218
2227
  domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains; prefix with - to exclude." })),
2219
- provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; all searches every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase")),
2228
+ 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")),
2220
2229
  }),
2221
2230
  async execute(_callId, params, signal, _onUpdate, ctx) {
2222
2231
  const claim = typeof params.claim === "string" ? params.claim.trim() : "";
@@ -2330,6 +2339,9 @@ export default function (pi: ExtensionAPI) {
2330
2339
  model: Type.Optional(Type.String({
2331
2340
  description: "Override the Gemini model for video/YouTube analysis (e.g. 'gemini-3.6-flash'). Defaults to config or gemini-3.6-flash.",
2332
2341
  })),
2342
+ auth: Type.Optional(Type.Union([Type.String(), Type.Boolean()], {
2343
+ description: "Opt into an authFetch profile for local browser-cookie fetching. Use a profile name, or true only when exactly one profile exists.",
2344
+ })),
2333
2345
  }),
2334
2346
 
2335
2347
  async execute(_toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<Record<string, unknown>>> {
@@ -2354,6 +2366,18 @@ export default function (pi: ExtensionAPI) {
2354
2366
  if (mode === "answer" && options.model) {
2355
2367
  return { content: [{ type: "text", text: "Error: use answerModel, not model, with mode answer." }], details: { error: "model is incompatible with mode answer" } };
2356
2368
  }
2369
+ if (mode === "answer" && options.auth !== undefined) {
2370
+ return { content: [{ type: "text", text: "Error: auth cannot be combined with mode answer." }], details: { error: "auth cannot be combined with mode answer" } };
2371
+ }
2372
+ let authFetchProfile: AuthFetchProfile | undefined;
2373
+ if (options.auth !== undefined) {
2374
+ try {
2375
+ authFetchProfile = resolveAuthFetchProfile(options.auth);
2376
+ } catch (err) {
2377
+ const error = err instanceof Error ? err.message : String(err);
2378
+ return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
2379
+ }
2380
+ }
2357
2381
  if (urlList.length === 0) {
2358
2382
  return {
2359
2383
  content: [{ type: "text", text: "Error: No URL provided." }],
@@ -2366,13 +2390,13 @@ export default function (pi: ExtensionAPI) {
2366
2390
  details: { phase: "fetch", progress: 0 },
2367
2391
  });
2368
2392
 
2369
- const { answerModel: _answerModel, ...extractionOptions } = options;
2393
+ const { answerModel: _answerModel, auth: _auth, ...extractionOptions } = options;
2370
2394
  const fetchOptions = mode === "answer"
2371
2395
  ? (() => {
2372
2396
  const { prompt: _prompt, ...rest } = extractionOptions;
2373
- return rest;
2397
+ return { ...rest, ...(authFetchProfile ? { authFetchProfile } : {}) };
2374
2398
  })()
2375
- : extractionOptions;
2399
+ : { ...extractionOptions, ...(authFetchProfile ? { authFetchProfile } : {}) };
2376
2400
  const fetchResults = await fetchAllContent(urlList, signal, fetchOptions);
2377
2401
  const presentedResults = mode === "answer"
2378
2402
  ? await Promise.all(fetchResults.map(async result => {
@@ -2396,7 +2420,6 @@ export default function (pi: ExtensionAPI) {
2396
2420
  const successful = presentedResults.filter((r) => !r.error).length;
2397
2421
  const totalChars = presentedResults.reduce((sum, r) => sum + r.content.length, 0);
2398
2422
 
2399
- // ALWAYS store results (even for single URL)
2400
2423
  const responseId = generateId();
2401
2424
  const data = {
2402
2425
  id: responseId,
@@ -2404,7 +2427,7 @@ export default function (pi: ExtensionAPI) {
2404
2427
  timestamp: Date.now(),
2405
2428
  urls: stripThumbnails(fetchResults),
2406
2429
  } satisfies StoredSearchData & { type: "fetch"; urls: ExtractedContent[] };
2407
- pi.appendEntry("web-search-results", storeFetchedContentResult(responseId, data));
2430
+ const storedContent = storeFetchResult(pi, responseId, data, authFetchProfile);
2408
2431
 
2409
2432
  // Single URL: return content directly (possibly truncated) with responseId
2410
2433
  if (urlList.length === 1) {
@@ -2412,7 +2435,7 @@ export default function (pi: ExtensionAPI) {
2412
2435
  if (result.error) {
2413
2436
  return {
2414
2437
  content: [{ type: "text", text: `Error: ${result.error}` }],
2415
- details: { urls: urlList, urlCount: 1, successful: 0, error: result.error, responseId, prompt: params.prompt, timestamp: params.timestamp, frames: params.frames },
2438
+ details: { urls: urlList, urlCount: 1, successful: 0, error: result.error, ...(storedContent ? { responseId } : {}), prompt: params.prompt, timestamp: params.timestamp, frames: params.frames },
2416
2439
  };
2417
2440
  }
2418
2441
 
@@ -2423,9 +2446,11 @@ export default function (pi: ExtensionAPI) {
2423
2446
 
2424
2447
  if (truncated) {
2425
2448
  output += `\n\n---\nShowing ${slice.endOffset} of ${fullLength} chars, ${slice.shownBytes} of ${slice.totalBytes} bytes, and ${slice.shownLines} of ${slice.totalLines} lines. `;
2426
- output += getSearchContentEnabled
2427
- ? `Use ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0, offset: ${slice.endOffset} }) for the next slice.`
2428
- : "Content retrieval is not registered.";
2449
+ output += storedContent
2450
+ ? getSearchContentEnabled
2451
+ ? `Use ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0, offset: ${slice.endOffset} }) for the next slice.`
2452
+ : "Content retrieval is not registered."
2453
+ : "Authenticated fetch cache is off; repeat the fetch to read more.";
2429
2454
  }
2430
2455
 
2431
2456
  const content: Array<TextContent | ImageContent> = [];
@@ -2448,7 +2473,7 @@ export default function (pi: ExtensionAPI) {
2448
2473
  successful: 1,
2449
2474
  totalChars: fullLength,
2450
2475
  title: result.title,
2451
- responseId,
2476
+ ...(storedContent ? { responseId } : {}),
2452
2477
  truncated,
2453
2478
  hasImage: imageCount > 0,
2454
2479
  imageCount,
@@ -2476,19 +2501,21 @@ export default function (pi: ExtensionAPI) {
2476
2501
  output += `- ${title || url} (${content.length} chars)\n`;
2477
2502
  }
2478
2503
  }
2479
- output += getSearchContentEnabled
2480
- ? `\n---\nUse ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0 }) to retrieve bounded content slices.`
2481
- : "\n---\nContent retrieval is not registered.";
2504
+ output += storedContent
2505
+ ? getSearchContentEnabled
2506
+ ? `\n---\nUse ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0 }) to retrieve bounded content slices.`
2507
+ : "\n---\nContent retrieval is not registered."
2508
+ : "\n---\nAuthenticated fetch cache is off; repeat the fetch to read content.";
2482
2509
 
2483
2510
  return {
2484
2511
  content: [{ type: "text", text: output }],
2485
- details: { urls: urlList, urlCount: urlList.length, successful, totalChars, responseId },
2512
+ details: { urls: urlList, urlCount: urlList.length, successful, totalChars, ...(storedContent ? { responseId } : {}) },
2486
2513
  };
2487
2514
  },
2488
2515
 
2489
2516
  renderCall(args, theme) {
2490
2517
  const { urlList, options } = normalizeFetchContentParams(args);
2491
- const { prompt, timestamp, frames, model, mode, answerModel } = options;
2518
+ const { prompt, timestamp, frames, model, mode, answerModel, auth } = options;
2492
2519
  if (urlList.length === 0) {
2493
2520
  return new Text(theme.fg("toolTitle", theme.bold("fetch ")) + theme.fg("error", "(no URL)"), 0, 0);
2494
2521
  }
@@ -2525,6 +2552,9 @@ export default function (pi: ExtensionAPI) {
2525
2552
  if (answerModel) {
2526
2553
  lines.push(theme.fg("dim", " answer model: ") + theme.fg("warning", answerModel));
2527
2554
  }
2555
+ if (auth !== undefined) {
2556
+ lines.push(theme.fg("dim", " auth: ") + theme.fg("warning", auth === true ? "true" : auth));
2557
+ }
2528
2558
  return new Text(lines.join("\n"), 0, 0);
2529
2559
  },
2530
2560
 
@@ -2616,7 +2646,9 @@ export default function (pi: ExtensionAPI) {
2616
2646
  },
2617
2647
  });
2618
2648
 
2619
- if (getSearchContentEnabled) pi.registerTool({
2649
+ if (getSearchContentEnabled) {
2650
+ const maxInlineContentChars = getMaxInlineContentChars(initConfig);
2651
+ pi.registerTool({
2620
2652
  name: toolNames.getSearchContent,
2621
2653
  label: "Get Search Content",
2622
2654
  description: `Retrieve bounded content slices or find matching passages in a previous ${storedContentSources} call.`,
@@ -2625,11 +2657,11 @@ export default function (pi: ExtensionAPI) {
2625
2657
  parameters: Type.Object({
2626
2658
  responseId: Type.String({ description: `The responseId from ${storedContentSources}` }),
2627
2659
  query: Type.Optional(Type.String({ description: searchQueryDescription })),
2628
- queryIndex: Type.Optional(Type.Number({ description: "Get content for query at index" })),
2660
+ queryIndex: Type.Optional(Type.Integer({ minimum: 0, description: "Get content for query at index" })),
2629
2661
  url: Type.Optional(Type.String({ description: "Get content for this URL" })),
2630
- urlIndex: Type.Optional(Type.Number({ description: "Get content for URL at index" })),
2631
- offset: Type.Optional(Type.Number({ description: "Character offset for fetched URL content slices (default 0). Cannot be combined with findText." })),
2632
- limit: Type.Optional(Type.Number({ description: "Maximum characters to return for fetched URL content slices (default and max are set by maxInlineContentChars). Cannot be combined with findText." })),
2662
+ urlIndex: Type.Optional(Type.Integer({ minimum: 0, description: "Get content for URL at index" })),
2663
+ offset: Type.Optional(Type.Integer({ minimum: 0, description: "Character offset for fetched URL content slices (default 0). Cannot be combined with findText." })),
2664
+ 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." })),
2633
2665
  findText: Type.Optional(Type.Union([
2634
2666
  Type.String({ minLength: 1, maxLength: 500 }),
2635
2667
  Type.Array(Type.String({ minLength: 1, maxLength: 500 }), { minItems: 1, maxItems: 10 }),
@@ -2639,15 +2671,23 @@ export default function (pi: ExtensionAPI) {
2639
2671
 
2640
2672
  async execute(_toolCallId, params): Promise<AgentToolResult<Record<string, unknown>>> {
2641
2673
  if (params.findText !== undefined && (params.offset !== undefined || params.limit !== undefined)) {
2642
- return { content: [{ type: "text", text: "findText cannot be combined with offset or limit" }], details: { error: "Incompatible find options" } };
2674
+ const offset = formatInputValue(params.offset);
2675
+ const limit = formatInputValue(params.limit);
2676
+ return {
2677
+ content: [{ type: "text", text: `findText cannot be combined with offset or limit. Received offset=${offset}, limit=${limit}; omit offset and limit when using findText.` }],
2678
+ details: { error: "Incompatible find options" },
2679
+ };
2643
2680
  }
2644
2681
  if (params.findMode !== undefined && params.findText === undefined) {
2645
- return { content: [{ type: "text", text: "findMode requires findText" }], details: { error: "findMode requires findText" } };
2682
+ return {
2683
+ content: [{ type: "text", text: `findMode ${formatInputValue(params.findMode)} requires findText; provide findText or omit findMode.` }],
2684
+ details: { error: "findMode requires findText" },
2685
+ };
2646
2686
  }
2647
2687
  const data = getResult(params.responseId);
2648
2688
  if (!data) {
2649
2689
  return {
2650
- content: [{ type: "text", text: `Error: No stored results for "${params.responseId}"` }],
2690
+ content: [{ type: "text", text: `Error: No stored results for responseId ${formatInputValue(params.responseId)}. Use a responseId returned by ${storedContentSources}.` }],
2651
2691
  details: { error: "Not found", responseId: params.responseId },
2652
2692
  };
2653
2693
  }
@@ -2656,22 +2696,30 @@ export default function (pi: ExtensionAPI) {
2656
2696
  const artifact = getResearchArtifact(params.responseId);
2657
2697
  if (!artifact) {
2658
2698
  return {
2659
- content: [{ type: "text", text: `Error: artifact ${params.responseId} not found` }],
2699
+ content: [{ type: "text", text: `Error: stored research artifact for responseId ${formatInputValue(params.responseId)} was not found. Use a responseId returned by ${storedContentSources}.` }],
2660
2700
  details: { error: "Artifact not found", responseId: params.responseId },
2661
2701
  };
2662
2702
  }
2663
2703
  const serialized = JSON.stringify(artifact, null, 2);
2664
- const maxInlineContentChars = getMaxInlineContentChars();
2665
2704
  const offset = params.offset ?? 0;
2666
2705
  const limit = params.limit ?? maxInlineContentChars;
2667
2706
  if (!Number.isInteger(offset) || offset < 0) {
2668
- return { content: [{ type: "text", text: "offset must be a non-negative integer" }], details: { error: "Invalid offset", offset } };
2707
+ return {
2708
+ content: [{ type: "text", text: `Invalid offset: received ${formatInputValue(offset)} for responseId ${formatInputValue(params.responseId)}; offset must be a non-negative integer. Use 0 or a larger integer.` }],
2709
+ details: { error: "Invalid offset", offset },
2710
+ };
2669
2711
  }
2670
2712
  if (!Number.isInteger(limit) || limit <= 0 || limit > maxInlineContentChars) {
2671
- return { content: [{ type: "text", text: `limit must be an integer from 1 to ${maxInlineContentChars}` }], details: { error: "Invalid limit", limit, maxLimit: maxInlineContentChars } };
2713
+ return {
2714
+ content: [{ type: "text", text: `Invalid limit: received ${formatInputValue(limit)} for responseId ${formatInputValue(params.responseId)}; limit must be an integer from 1 to ${maxInlineContentChars}. Use a value in that range.` }],
2715
+ details: { error: "Invalid limit", limit, maxLimit: maxInlineContentChars },
2716
+ };
2672
2717
  }
2673
2718
  if (offset > serialized.length) {
2674
- return { content: [{ type: "text", text: `offset ${offset} is out of range (0-${serialized.length})` }], details: { error: "Offset out of range", offset, contentLength: serialized.length } };
2719
+ return {
2720
+ content: [{ type: "text", text: `Offset ${offset} is out of range for responseId ${formatInputValue(params.responseId)}. Received offset ${offset}; valid range is 0-${serialized.length}. Use an offset within that range.` }],
2721
+ details: { error: "Offset out of range", offset, contentLength: serialized.length },
2722
+ };
2675
2723
  }
2676
2724
  const endOffset = Math.min(offset + limit, serialized.length);
2677
2725
  const artifactSlice = serialized.slice(offset, endOffset);
@@ -2690,29 +2738,30 @@ export default function (pi: ExtensionAPI) {
2690
2738
  if (!queryData) {
2691
2739
  const available = data.queries.map((q) => `"${q.query}"`).join(", ");
2692
2740
  return {
2693
- content: [{ type: "text", text: `Query "${params.query}" not found. Available: ${available}` }],
2741
+ content: [{ type: "text", text: `Query ${formatInputValue(params.query)} was not found for responseId ${formatInputValue(params.responseId)}. Received query=${formatInputValue(params.query)}. Available queries: ${available || "none"}. Use one of the available queries or queryIndex.` }],
2694
2742
  details: { error: "Query not found" },
2695
2743
  };
2696
2744
  }
2697
2745
  } else if (params.queryIndex !== undefined) {
2698
2746
  queryData = data.queries[params.queryIndex];
2699
2747
  if (!queryData) {
2748
+ const available = data.queries.map((q, i) => `${i}: "${q.query}"`).join(", ");
2700
2749
  return {
2701
- content: [{ type: "text", text: `Index ${params.queryIndex} out of range (0-${data.queries.length - 1})` }],
2750
+ content: [{ type: "text", text: `Query index ${formatInputValue(params.queryIndex)} is out of range for responseId ${formatInputValue(params.responseId)}. Received queryIndex=${formatInputValue(params.queryIndex)}; valid indexes are 0-${data.queries.length - 1}. Available queries: ${available || "none"}. Use one of the available indexes.` }],
2702
2751
  details: { error: "Index out of range" },
2703
2752
  };
2704
2753
  }
2705
2754
  } else {
2706
2755
  const available = data.queries.map((q, i) => `${i}: "${q.query}"`).join(", ");
2707
2756
  return {
2708
- content: [{ type: "text", text: `Specify query or queryIndex. Available: ${available}` }],
2757
+ content: [{ type: "text", text: `Specify query or queryIndex for responseId ${formatInputValue(params.responseId)}. Available queries: ${available || "none"}.` }],
2709
2758
  details: { error: "No query specified" },
2710
2759
  };
2711
2760
  }
2712
2761
 
2713
2762
  if (queryData.error) {
2714
2763
  return {
2715
- content: [{ type: "text", text: `Error for "${queryData.query}": ${queryData.error}` }],
2764
+ content: [{ type: "text", text: `Error retrieving query ${formatInputValue(queryData.query)} from responseId ${formatInputValue(params.responseId)}: ${queryData.error}. Check the stored search result and retry with another query or queryIndex if needed.` }],
2716
2765
  details: { error: queryData.error, query: queryData.query },
2717
2766
  };
2718
2767
  }
@@ -2728,7 +2777,10 @@ export default function (pi: ExtensionAPI) {
2728
2777
  };
2729
2778
  } catch (err) {
2730
2779
  const error = err instanceof Error ? err.message : String(err);
2731
- return { content: [{ type: "text", text: error }], details: { error, query: queryData.query } };
2780
+ return {
2781
+ content: [{ type: "text", text: `Unable to find ${formatInputValue(params.findText)} in query ${formatInputValue(queryData.query)} for responseId ${formatInputValue(params.responseId)}: ${error}. Check findText and use a supported findMode.` }],
2782
+ details: { error, query: queryData.query },
2783
+ };
2732
2784
  }
2733
2785
  }
2734
2786
 
@@ -2748,7 +2800,7 @@ export default function (pi: ExtensionAPI) {
2748
2800
  if (!urlData) {
2749
2801
  const available = data.urls.map((u) => u.url).join("\n ");
2750
2802
  return {
2751
- content: [{ type: "text", text: `URL not found. Available:\n ${available}` }],
2803
+ content: [{ type: "text", text: `URL ${formatInputValue(params.url)} was not found for responseId ${formatInputValue(params.responseId)}. Received url=${formatInputValue(params.url)}. Available URLs:\n ${available || " none"}\nUse one of the available URLs or urlIndex.` }],
2752
2804
  details: { error: "URL not found" },
2753
2805
  };
2754
2806
  }
@@ -2756,22 +2808,23 @@ export default function (pi: ExtensionAPI) {
2756
2808
  selectedUrlIndex = params.urlIndex;
2757
2809
  urlData = data.urls[selectedUrlIndex];
2758
2810
  if (!urlData) {
2811
+ const available = data.urls.map((u, i) => `${i}: ${u.url}`).join("\n ");
2759
2812
  return {
2760
- content: [{ type: "text", text: `Index ${params.urlIndex} out of range (0-${data.urls.length - 1})` }],
2813
+ content: [{ type: "text", text: `URL index ${formatInputValue(params.urlIndex)} is out of range for responseId ${formatInputValue(params.responseId)}. Received urlIndex=${formatInputValue(params.urlIndex)}; valid indexes are 0-${data.urls.length - 1}. Available URLs:\n ${available || " none"}\nUse one of the available indexes.` }],
2761
2814
  details: { error: "Index out of range" },
2762
2815
  };
2763
2816
  }
2764
2817
  } else {
2765
2818
  const available = data.urls.map((u, i) => `${i}: ${u.url}`).join("\n ");
2766
2819
  return {
2767
- content: [{ type: "text", text: `Specify url or urlIndex. Available:\n ${available}` }],
2820
+ content: [{ type: "text", text: `Specify url or urlIndex for responseId ${formatInputValue(params.responseId)}. Available URLs:\n ${available || " none"}` }],
2768
2821
  details: { error: "No URL specified" },
2769
2822
  };
2770
2823
  }
2771
2824
 
2772
2825
  if (urlData.error) {
2773
2826
  return {
2774
- content: [{ type: "text", text: `Error for ${urlData.url}: ${urlData.error}` }],
2827
+ content: [{ type: "text", text: `Error retrieving URL ${formatInputValue(urlData.url)} from responseId ${formatInputValue(params.responseId)}: ${urlData.error}. Check the stored fetch result and retry with another URL or urlIndex if needed.` }],
2775
2828
  details: { error: urlData.error, url: urlData.url },
2776
2829
  };
2777
2830
  }
@@ -2786,28 +2839,30 @@ export default function (pi: ExtensionAPI) {
2786
2839
  };
2787
2840
  } catch (err) {
2788
2841
  const error = err instanceof Error ? err.message : String(err);
2789
- return { content: [{ type: "text", text: error }], details: { error, url: urlData.url } };
2842
+ return {
2843
+ content: [{ type: "text", text: `Unable to find ${formatInputValue(params.findText)} in URL ${formatInputValue(urlData.url)} for responseId ${formatInputValue(params.responseId)}: ${error}. Check findText and use a supported findMode.` }],
2844
+ details: { error, url: urlData.url },
2845
+ };
2790
2846
  }
2791
2847
  }
2792
2848
 
2793
- const maxInlineContentChars = getMaxInlineContentChars();
2794
2849
  const offset = params.offset ?? 0;
2795
2850
  const limit = params.limit ?? maxInlineContentChars;
2796
2851
  if (!Number.isInteger(offset) || offset < 0) {
2797
2852
  return {
2798
- content: [{ type: "text", text: "offset must be a non-negative integer" }],
2853
+ content: [{ type: "text", text: `Invalid offset: received ${formatInputValue(offset)} for URL ${formatInputValue(urlData.url)}; offset must be a non-negative integer. Use 0 or a larger integer.` }],
2799
2854
  details: { error: "Invalid offset", offset },
2800
2855
  };
2801
2856
  }
2802
2857
  if (!Number.isInteger(limit) || limit <= 0 || limit > maxInlineContentChars) {
2803
2858
  return {
2804
- content: [{ type: "text", text: `limit must be an integer from 1 to ${maxInlineContentChars}` }],
2859
+ content: [{ type: "text", text: `Invalid limit: received ${formatInputValue(limit)} for URL ${formatInputValue(urlData.url)}; limit must be an integer from 1 to ${maxInlineContentChars}. Use a value in that range.` }],
2805
2860
  details: { error: "Invalid limit", limit, maxLimit: maxInlineContentChars },
2806
2861
  };
2807
2862
  }
2808
2863
  if (offset > urlData.content.length) {
2809
2864
  return {
2810
- content: [{ type: "text", text: `offset ${offset} is out of range (0-${urlData.content.length})` }],
2865
+ content: [{ type: "text", text: `Offset ${offset} is out of range for URL ${formatInputValue(urlData.url)} in responseId ${formatInputValue(params.responseId)}. Received offset ${offset}; valid range is 0-${urlData.content.length}. Use an offset within that range.` }],
2811
2866
  details: { error: "Offset out of range", offset, contentLength: urlData.content.length },
2812
2867
  };
2813
2868
  }
@@ -2839,7 +2894,7 @@ export default function (pi: ExtensionAPI) {
2839
2894
  }
2840
2895
 
2841
2896
  return {
2842
- content: [{ type: "text", text: "Invalid stored data format" }],
2897
+ content: [{ type: "text", text: `Invalid stored data for responseId ${formatInputValue(params.responseId)}: received type ${formatInputValue(data.type)}. Use a responseId returned by ${storedContentSources}.` }],
2843
2898
  details: { error: "Invalid data" },
2844
2899
  };
2845
2900
  },
@@ -2916,6 +2971,7 @@ export default function (pi: ExtensionAPI) {
2916
2971
  return new Text(statusLine + "\n" + theme.fg("dim", preview), 0, 0);
2917
2972
  },
2918
2973
  });
2974
+ }
2919
2975
 
2920
2976
  if (isCommandEnabled(initConfig, "websearch")) pi.registerCommand("websearch", {
2921
2977
  description: "Open web search curator",