@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
@@ -12,6 +12,7 @@ import { isYouTubeURL, isYouTubeEnabled, extractYouTube, extractYouTubeFrame, ex
12
12
  import { CredentialResolutionError } from "./credential-source.ts";
13
13
  import { extractWithUrlContext, extractWithGeminiWeb } from "./gemini-url-context.ts";
14
14
  import { extractWithParallel, isParallelAvailable } from "./parallel.ts";
15
+ import { extractWithParallelMcp } from "./parallel-mcp.ts";
15
16
  import { extractWithTinyFish, isTinyFishAvailable } from "./tinyfish.ts";
16
17
  import { extractWithSearch1API, isSearch1APIAvailable } from "./search1api.ts";
17
18
  import { extractWithQuerit, isQueritAvailable } from "./querit.ts";
@@ -21,9 +22,11 @@ import { extractWithFirecrawl, isFirecrawlAvailable } from "./firecrawl.ts";
21
22
  import { extractWithBrightDataUnlocker, isBrightDataUnlockerAvailable } from "./brightdata-unlocker.ts";
22
23
  import { isVideoFile, extractVideo, extractVideoFrame, getLocalVideoDuration } from "./video-extract.ts";
23
24
  import { appendDeclaredWebLinks, discoverDeclaredWebLinks, type DeclaredWebLink } from "./declared-web-links.ts";
24
- import { fetchRemoteUrl, loadFetchContentDomainPolicy, loadSsrfConfig, validateRemoteUrl, type Lookup } from "./ssrf-protection.ts";
25
+ import { fetchRemoteUrl, loadFetchContentDomainPolicy, loadSsrfConfig, validateRemoteUrl, type DomainPolicy, type Lookup, type SsrfConfig } from "./ssrf-protection.ts";
25
26
  import { formatSeconds, getWebSearchConfigPath } from "./utils.ts";
26
27
  import { isImageEnabled } from "./feature-config.ts";
28
+ import { assertAuthFetchUrl, authFetchRedirectGuard, type AuthFetchProfile } from "./auth-fetch.ts";
29
+ import { getBrowserCookiesForHosts, getLastBrowserCookieDiagnostic } from "./chrome-cookies.ts";
27
30
 
28
31
  const DEFAULT_TIMEOUT_MS = 30000;
29
32
  const CONCURRENT_LIMIT = 3;
@@ -32,11 +35,11 @@ const NON_RECOVERABLE_ERRORS = ["Unsupported content type", "Response too large"
32
35
  const MIN_USEFUL_CONTENT = 500;
33
36
  const SUPPORTED_IMAGE_TYPES = new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
34
37
  const WEB_SEARCH_CONFIG_PATH = getWebSearchConfigPath();
35
- const FETCH_PROVIDERS = ["http", "firecrawl", "jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "brightdata", "gemini"] as const;
38
+ const FETCH_PROVIDERS = ["http", "firecrawl", "jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "parallel-mcp", "brightdata", "gemini"] as const;
36
39
  type FetchProvider = typeof FETCH_PROVIDERS[number];
37
40
  type FetchRouting = { providers: FetchProvider[]; allowRemoteHostedProviders: boolean };
38
41
  const DEFAULT_FETCH_PROVIDER_ORDER: FetchProvider[] = ["http", "firecrawl", "jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "brightdata", "gemini"];
39
- const REMOTE_HOSTED_FETCH_PROVIDERS = new Set<FetchProvider>(["jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "brightdata", "gemini"]);
42
+ const REMOTE_HOSTED_FETCH_PROVIDERS = new Set<FetchProvider>(["jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "parallel-mcp", "brightdata", "gemini"]);
40
43
 
41
44
  export { loadSsrfConfig } from "./ssrf-protection.ts";
42
45
 
@@ -57,7 +60,8 @@ function isAbortError(err: unknown): boolean {
57
60
  }
58
61
 
59
62
  function isRedirectPolicyError(message: string): boolean {
60
- return message.startsWith("Blocked internal ") ||
63
+ return message.startsWith("Authenticated fetch refused cross-origin redirect") ||
64
+ message.startsWith("Blocked internal ") ||
61
65
  message.startsWith("Blocked hostname by fetch_content domain policy") ||
62
66
  message.startsWith("Hostname not allowed by fetch_content domain policy") ||
63
67
  message.startsWith("Too many redirects fetching ") ||
@@ -74,6 +78,56 @@ function imageGateError(): string | null {
74
78
  }
75
79
  }
76
80
 
81
+ async function resolveAuthCookieHeader(url: string | URL, profile: AuthFetchProfile): Promise<string> {
82
+ const parsed = assertAuthFetchUrl(profile, url.toString());
83
+ const result = await getBrowserCookiesForHosts({ hosts: [parsed.hostname], profile: profile.chromeProfile, requestUrl: parsed });
84
+ if (result?.cookieHeader) return result.cookieHeader;
85
+ if (!result) {
86
+ const diagnostic = getLastBrowserCookieDiagnostic();
87
+ throw new Error(`Authenticated fetch profile ${profile.name} could not read browser cookies${diagnostic ? `: ${diagnostic}` : ""}`);
88
+ }
89
+ throw new Error(`Authenticated fetch profile ${profile.name} could not build a cookie header`);
90
+ }
91
+
92
+ const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
93
+
94
+ async function fetchAuthenticatedRemoteUrl(
95
+ url: string,
96
+ init: RequestInit,
97
+ validationOptions: { ssrf: SsrfConfig; domainPolicy: DomainPolicy; lookup?: Lookup },
98
+ profile: AuthFetchProfile,
99
+ ): Promise<Response> {
100
+ let current = await validateRemoteUrl(url, {
101
+ allowRanges: validationOptions.ssrf.allowRanges,
102
+ trustEnvProxy: validationOptions.ssrf.trustEnvProxy,
103
+ domainPolicy: validationOptions.domainPolicy,
104
+ ...(validationOptions.lookup ? { lookup: validationOptions.lookup } : {}),
105
+ });
106
+ let requestInit = init;
107
+ for (let redirects = 0; redirects <= 5; redirects++) {
108
+ const cookieHeader = await resolveAuthCookieHeader(current, profile);
109
+ const headers = { ...(requestInit.headers as Record<string, string>), cookie: cookieHeader };
110
+ const response = await fetch(current, { ...requestInit, headers, redirect: "manual" });
111
+ if (!REDIRECT_STATUSES.has(response.status)) return response;
112
+ const location = response.headers.get("location");
113
+ if (!location) return response;
114
+ if (redirects === 5) throw new Error(`Too many redirects fetching ${current.toString()}`);
115
+ const from = current;
116
+ current = await validateRemoteUrl(new URL(location, current), {
117
+ allowRanges: validationOptions.ssrf.allowRanges,
118
+ trustEnvProxy: validationOptions.ssrf.trustEnvProxy,
119
+ domainPolicy: validationOptions.domainPolicy,
120
+ ...(validationOptions.lookup ? { lookup: validationOptions.lookup } : {}),
121
+ });
122
+ authFetchRedirectGuard(profile, from, current);
123
+ if (response.status === 303 || ((response.status === 301 || response.status === 302) && requestInit.method?.toUpperCase() === "POST")) {
124
+ const { body: _body, ...nextInit } = requestInit;
125
+ requestInit = { ...nextInit, method: "GET" };
126
+ }
127
+ }
128
+ throw new Error(`Too many redirects fetching ${current.toString()}`);
129
+ }
130
+
77
131
  function loadFetchRouting(): FetchRouting {
78
132
  if (!existsSync(WEB_SEARCH_CONFIG_PATH)) {
79
133
  return { providers: DEFAULT_FETCH_PROVIDER_ORDER, allowRemoteHostedProviders: false };
@@ -171,6 +225,7 @@ export interface ExtractOptions {
171
225
  model?: string;
172
226
  mode?: "readable" | "raw" | "answer";
173
227
  answerModel?: string;
228
+ authFetchProfile?: AuthFetchProfile;
174
229
  /** Custom DNS resolver used for SSRF validation. Primarily a test seam. */
175
230
  lookup?: Lookup;
176
231
  }
@@ -313,11 +368,17 @@ async function extractLocalFrames(
313
368
  return { frames, error: frames.length === 0 && firstError ? firstError.error : null };
314
369
  }
315
370
 
316
- function safeVideoInfo(url: string): { info: ReturnType<typeof isVideoFile>; error?: string } {
371
+ type LocalVideoInfoResult =
372
+ | { status: "video"; info: NonNullable<ReturnType<typeof isVideoFile>> }
373
+ | { status: "not-video" }
374
+ | { status: "invalid"; error: string };
375
+
376
+ function safeVideoInfo(url: string): LocalVideoInfoResult {
317
377
  try {
318
- return { info: isVideoFile(url) };
378
+ const info = isVideoFile(url);
379
+ return info ? { status: "video", info } : { status: "not-video" };
319
380
  } catch (err) {
320
- return { info: null, error: errorMessage(err) };
381
+ return { status: "invalid", error: errorMessage(err) };
321
382
  }
322
383
  }
323
384
 
@@ -351,6 +412,14 @@ export async function extractContent(
351
412
  }
352
413
  }
353
414
 
415
+ if (options?.authFetchProfile) {
416
+ try {
417
+ return await extractViaHttp(url, signal, options);
418
+ } catch (err) {
419
+ return { url, title: "", content: "", error: errorMessage(err) };
420
+ }
421
+ }
422
+
354
423
  if (options?.mode === "raw") {
355
424
  return extractViaHttp(url, signal, options);
356
425
  }
@@ -380,10 +449,10 @@ export async function extractContent(
380
449
  }
381
450
 
382
451
  const localVideo = safeVideoInfo(url);
383
- if (localVideo.error) {
452
+ if (localVideo.status === "invalid") {
384
453
  return { url, title: "", content: "", error: localVideo.error };
385
454
  }
386
- if (localVideo.info) {
455
+ if (localVideo.status === "video") {
387
456
  const durationResult = await getLocalVideoDuration(localVideo.info.absolutePath);
388
457
  if (typeof durationResult !== "number") {
389
458
  return { url, title: "Frames", content: durationResult.error, error: durationResult.error };
@@ -463,10 +532,10 @@ export async function extractContent(
463
532
  }
464
533
 
465
534
  const localVideo = safeVideoInfo(url);
466
- if (localVideo.error) {
535
+ if (localVideo.status === "invalid") {
467
536
  return { url, title: "", content: "", error: localVideo.error };
468
537
  }
469
- if (localVideo.info) {
538
+ if (localVideo.status === "video") {
470
539
  if (spec.type === "range") {
471
540
  const timestamps = frameCount
472
541
  ? computeRangeTimestamps(spec.start, spec.end, frameCount)
@@ -495,10 +564,10 @@ export async function extractContent(
495
564
  }
496
565
 
497
566
  const localVideo = safeVideoInfo(url);
498
- if (localVideo.error) {
567
+ if (localVideo.status === "invalid") {
499
568
  return { url, title: "", content: "", error: localVideo.error };
500
569
  }
501
- if (localVideo.info) {
570
+ if (localVideo.status === "video") {
502
571
  try {
503
572
  const result = await extractVideo(localVideo.info, signal, options);
504
573
  if (signal?.aborted) return abortedResult(url);
@@ -548,7 +617,7 @@ export async function extractContent(
548
617
  url,
549
618
  title: "",
550
619
  content: "",
551
- error: "Could not extract YouTube video content. Sign into Google in Chrome for automatic access, or set GEMINI_API_KEY.",
620
+ error: "Could not extract YouTube video content. Sign into Google in a supported Chromium browser for automatic access, or set GEMINI_API_KEY.",
552
621
  };
553
622
  }
554
623
 
@@ -600,6 +669,7 @@ export async function extractContent(
600
669
  let kagiError: string | null = null;
601
670
  let ollamaError: string | null = null;
602
671
  let parallelError: string | null = null;
672
+ let parallelMcpError: string | null = null;
603
673
  let brightdataError: string | null = null;
604
674
 
605
675
  if (remoteUrl && providerOrder[0] !== "http") {
@@ -735,6 +805,18 @@ export async function extractContent(
735
805
  continue;
736
806
  }
737
807
 
808
+ if (provider === "parallel-mcp") {
809
+ try {
810
+ const parallelMcpResult = await extractWithParallelMcp(url, signal, options);
811
+ if (parallelMcpResult) return withDeclaredLinks(parallelMcpResult);
812
+ } catch (err) {
813
+ if (isAbortError(err)) return abortedResult(url);
814
+ parallelMcpError = errorMessage(err);
815
+ if (isConfigParseError(err)) return parseErrorResult(parallelMcpError);
816
+ }
817
+ continue;
818
+ }
819
+
738
820
  if (provider === "brightdata") {
739
821
  try {
740
822
  if (isBrightDataUnlockerAvailable()) {
@@ -782,6 +864,7 @@ export async function extractContent(
782
864
  ...(kagiError ? [`Kagi fallback failed: ${kagiError}`] : []),
783
865
  ...(ollamaError ? [`Ollama fallback failed: ${ollamaError}`] : []),
784
866
  ...(parallelError ? [`Parallel fallback failed: ${parallelError}`] : []),
867
+ ...(parallelMcpError ? [`Parallel MCP fallback failed: ${parallelMcpError}`] : []),
785
868
  ...(brightdataError ? [`Bright Data fallback failed: ${brightdataError}`] : []),
786
869
  "",
787
870
  "Fallback options:",
@@ -914,29 +997,33 @@ async function extractViaHttp(
914
997
  try {
915
998
  const ssrf = loadSsrfConfig();
916
999
  const domainPolicy = loadFetchContentDomainPolicy();
917
- const response = await fetchRemoteUrl(
918
- url,
919
- {
920
- signal: controller.signal,
921
- headers: {
922
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
923
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
924
- "Accept-Language": "en-US,en;q=0.9",
925
- "Cache-Control": "no-cache",
926
- "Sec-Fetch-Dest": "document",
927
- "Sec-Fetch-Mode": "navigate",
928
- "Sec-Fetch-Site": "none",
929
- "Sec-Fetch-User": "?1",
930
- "Upgrade-Insecure-Requests": "1",
931
- },
932
- },
933
- {
934
- allowRanges: ssrf.allowRanges,
935
- trustEnvProxy: ssrf.trustEnvProxy,
936
- domainPolicy,
937
- ...(options?.lookup ? { lookup: options.lookup } : {}),
1000
+ const authProfile = options?.authFetchProfile;
1001
+ const requestInit = {
1002
+ signal: controller.signal,
1003
+ headers: {
1004
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
1005
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
1006
+ "Accept-Language": "en-US,en;q=0.9",
1007
+ "Cache-Control": "no-cache",
1008
+ "Sec-Fetch-Dest": "document",
1009
+ "Sec-Fetch-Mode": "navigate",
1010
+ "Sec-Fetch-Site": "none",
1011
+ "Sec-Fetch-User": "?1",
1012
+ "Upgrade-Insecure-Requests": "1",
938
1013
  },
939
- );
1014
+ };
1015
+ const response = authProfile
1016
+ ? await fetchAuthenticatedRemoteUrl(url, requestInit, { ssrf, domainPolicy, ...(options?.lookup ? { lookup: options.lookup } : {}) }, authProfile)
1017
+ : await fetchRemoteUrl(
1018
+ url,
1019
+ requestInit,
1020
+ {
1021
+ allowRanges: ssrf.allowRanges,
1022
+ trustEnvProxy: ssrf.trustEnvProxy,
1023
+ domainPolicy,
1024
+ ...(options?.lookup ? { lookup: options.lookup } : {}),
1025
+ },
1026
+ );
940
1027
 
941
1028
  if (!response.ok && options?.mode !== "raw") {
942
1029
  activityMonitor.logComplete(activityId, response.status);
@@ -1072,7 +1159,7 @@ async function extractViaHttp(
1072
1159
 
1073
1160
  if (!article) {
1074
1161
  const rscResult = extractRSCContent(text);
1075
- if (rscResult) {
1162
+ if (rscResult && rscResult.content.length >= MIN_USEFUL_CONTENT) {
1076
1163
  activityMonitor.logComplete(activityId, response.status);
1077
1164
  return {
1078
1165
  url,
@@ -1105,6 +1192,16 @@ async function extractViaHttp(
1105
1192
  activityMonitor.logComplete(activityId, response.status);
1106
1193
 
1107
1194
  if (markdown.length < MIN_USEFUL_CONTENT) {
1195
+ const rscResult = extractRSCContent(text);
1196
+ if (rscResult && rscResult.content.length >= MIN_USEFUL_CONTENT) {
1197
+ return {
1198
+ url,
1199
+ title: rscResult.title,
1200
+ content: appendDeclaredWebLinks(rscResult.content, declaredLinks),
1201
+ error: null,
1202
+ declaredLinks,
1203
+ };
1204
+ }
1108
1205
  return {
1109
1206
  url,
1110
1207
  title: article.title || documentTitle,
@@ -8,6 +8,7 @@ export interface FetchContentParams {
8
8
  model?: unknown;
9
9
  mode?: unknown;
10
10
  answerModel?: unknown;
11
+ auth?: unknown;
11
12
  }
12
13
 
13
14
  export interface NormalizedFetchContentParams {
@@ -20,6 +21,7 @@ export interface NormalizedFetchContentParams {
20
21
  model?: string;
21
22
  mode?: "readable" | "raw" | "answer";
22
23
  answerModel?: string;
24
+ auth?: true | string;
23
25
  };
24
26
  }
25
27
 
@@ -28,7 +30,7 @@ export function normalizeFetchContentParams(params: FetchContentParams): Normali
28
30
  const urlList = normalizedUrls.length > 0 ? normalizedUrls : normalizeSingleUrl(params.url);
29
31
  const prompt = normalizeOptionalString(params.prompt);
30
32
  const timestamp = normalizeOptionalString(params.timestamp);
31
- const frames = normalizeOptionalInteger(params.frames);
33
+ const frames = normalizeOptionalFrameCount(params.frames);
32
34
 
33
35
  const shouldIncludeFrames = frames !== undefined && (timestamp !== undefined || frames > 1);
34
36
 
@@ -36,6 +38,7 @@ export function normalizeFetchContentParams(params: FetchContentParams): Normali
36
38
  const model = normalizeOptionalString(params.model);
37
39
  const mode = normalizeMode(params.mode);
38
40
  const answerModel = normalizeOptionalString(params.answerModel);
41
+ const auth = normalizeAuth(params.auth);
39
42
 
40
43
  return {
41
44
  urlList,
@@ -47,6 +50,7 @@ export function normalizeFetchContentParams(params: FetchContentParams): Normali
47
50
  ...(model !== undefined ? { model } : {}),
48
51
  ...(mode !== undefined ? { mode } : {}),
49
52
  ...(answerModel !== undefined ? { answerModel } : {}),
53
+ ...(auth !== undefined ? { auth } : {}),
50
54
  },
51
55
  };
52
56
  }
@@ -74,8 +78,18 @@ function normalizeMode(value: unknown): "readable" | "raw" | "answer" | undefine
74
78
  throw new Error('mode must be "readable", "raw", or "answer"');
75
79
  }
76
80
 
77
- function normalizeOptionalInteger(value: unknown): number | undefined {
78
- if (typeof value !== "number" || !Number.isInteger(value) || value < 1) return undefined;
81
+ function normalizeAuth(value: unknown): true | string | undefined {
82
+ if (value === undefined || value === false) return undefined;
83
+ if (value === true) return true;
84
+ if (typeof value === "string") {
85
+ const trimmed = value.trim();
86
+ if (trimmed) return trimmed;
87
+ }
88
+ throw new Error("auth must be a profile name, true, or false");
89
+ }
90
+
91
+ function normalizeOptionalFrameCount(value: unknown): number | undefined {
92
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 12) return undefined;
79
93
  return value;
80
94
  }
81
95
 
@@ -2,12 +2,14 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { activityMonitor } from "./activity.ts";
3
3
  import { redactCredential, resolveCredential } from "./credential-source.ts";
4
4
  import type { ExtractedContent, ExtractOptions } from "./extract.ts";
5
- import { validateRemoteUrl, type Lookup } from "./ssrf-protection.ts";
5
+ import type { SearchOptions, SearchResponse } from "./perplexity.ts";
6
+ import { loadSsrfConfig, validateRemoteUrl, type Lookup } from "./ssrf-protection.ts";
6
7
  import { getWebSearchConfigPath } from "./utils.ts";
7
8
 
8
9
  const CONFIG_PATH = getWebSearchConfigPath();
9
10
  const DEFAULT_API_VERSION = "v2";
10
11
  const EXTRACT_TIMEOUT_MS = 60_000;
12
+ const SEARCH_TIMEOUT_MS = 60_000;
11
13
  const DEFAULT_MAX_REDIRECTS = 5;
12
14
  const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
13
15
  const SUPPORTED_API_VERSIONS = ["v1", "v2"] as const;
@@ -22,6 +24,11 @@ export interface FirecrawlExtractOptions extends Pick<ExtractOptions, "timeoutMs
22
24
  ssrf?: FirecrawlSsrfOptions;
23
25
  }
24
26
 
27
+ interface FirecrawlSearchOptions extends SearchOptions, Pick<ExtractOptions, "timeoutMs" | "lookup"> {
28
+ includeContent?: boolean;
29
+ ssrf?: FirecrawlSsrfOptions;
30
+ }
31
+
25
32
  interface FirecrawlConfig {
26
33
  firecrawlBaseUrl?: unknown;
27
34
  firecrawlApiKey?: unknown;
@@ -35,6 +42,25 @@ interface FirecrawlScrapeData {
35
42
  metadata?: { title?: unknown };
36
43
  }
37
44
 
45
+ interface FirecrawlSearchItem {
46
+ title?: unknown;
47
+ description?: unknown;
48
+ snippet?: unknown;
49
+ url?: unknown;
50
+ markdown?: unknown;
51
+ metadata?: {
52
+ title?: unknown;
53
+ description?: unknown;
54
+ sourceURL?: unknown;
55
+ url?: unknown;
56
+ };
57
+ }
58
+
59
+ interface DomainFilters {
60
+ include: string[];
61
+ exclude: string[];
62
+ }
63
+
38
64
  let cachedConfig: FirecrawlConfig | null = null;
39
65
 
40
66
  function loadConfig(): FirecrawlConfig {
@@ -149,10 +175,11 @@ function isAbortError(err: unknown): boolean {
149
175
  return errorMessage(err).toLowerCase().includes("abort");
150
176
  }
151
177
 
152
- function ssrfOptions(options?: FirecrawlExtractOptions): { lookup?: Lookup; allowRanges: string[]; trustEnvProxy: boolean } {
178
+ function ssrfOptions(options?: FirecrawlExtractOptions | FirecrawlSearchOptions): { lookup?: Lookup; allowRanges: string[]; trustEnvProxy: boolean } {
179
+ const config = loadSsrfConfig();
153
180
  return {
154
- allowRanges: options?.ssrf?.allowRanges ?? [],
155
- trustEnvProxy: options?.ssrf?.trustEnvProxy ?? false,
181
+ allowRanges: options?.ssrf?.allowRanges ?? config.allowRanges,
182
+ trustEnvProxy: options?.ssrf?.trustEnvProxy ?? config.trustEnvProxy,
156
183
  ...(options?.lookup ? { lookup: options.lookup } : {}),
157
184
  };
158
185
  }
@@ -171,7 +198,7 @@ function withoutSensitiveHeaders(headers: Record<string, string>): Record<string
171
198
  async function fetchFirecrawlApi(
172
199
  url: string,
173
200
  init: { method: string; headers: Record<string, string>; body: string; signal: AbortSignal },
174
- options: FirecrawlExtractOptions | undefined,
201
+ options: FirecrawlExtractOptions | FirecrawlSearchOptions | undefined,
175
202
  ): Promise<Response> {
176
203
  let current = await validateRemoteUrl(url, ssrfOptions(options));
177
204
  let headers = init.headers;
@@ -199,11 +226,126 @@ function scrapeBody(url: string): Record<string, unknown> {
199
226
  };
200
227
  }
201
228
 
229
+ function normalizeCount(value: number | undefined): number {
230
+ if (typeof value !== "number" || !Number.isFinite(value)) return 5;
231
+ return Math.max(1, Math.min(Math.floor(value), 20));
232
+ }
233
+
234
+ function normalizeDomain(value: string): string | null {
235
+ let input = value.trim().toLowerCase();
236
+ if (!input) return null;
237
+ if (input.startsWith("-")) input = input.slice(1).trim();
238
+ if (!input) return null;
239
+ try {
240
+ const parsed = input.includes("://") ? new URL(input) : new URL(`https://${input}`);
241
+ input = parsed.hostname;
242
+ } catch {
243
+ input = input.split("/")[0]?.split(":")[0] ?? "";
244
+ }
245
+ input = input.replace(/^\.+|\.+$/g, "");
246
+ return /^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(input) ? input : null;
247
+ }
248
+
249
+ function parseDomainFilter(domainFilter: string[] | undefined): DomainFilters {
250
+ const filters: DomainFilters = { include: [], exclude: [] };
251
+ for (const raw of domainFilter ?? []) {
252
+ const domain = normalizeDomain(raw);
253
+ if (!domain) continue;
254
+ const target = raw.trim().startsWith("-") ? filters.exclude : filters.include;
255
+ if (!target.includes(domain)) target.push(domain);
256
+ }
257
+ return filters;
258
+ }
259
+
260
+ function passesDomainFilters(url: string, filters: DomainFilters): boolean {
261
+ if (filters.include.length === 0 && filters.exclude.length === 0) return true;
262
+ let hostname: string;
263
+ try {
264
+ hostname = new URL(url).hostname.toLowerCase();
265
+ } catch {
266
+ return false;
267
+ }
268
+ const matches = (domain: string) => hostname === domain || hostname.endsWith(`.${domain}`);
269
+ if (filters.exclude.some(matches)) return false;
270
+ return filters.include.length === 0 || filters.include.some(matches);
271
+ }
272
+
273
+ function mapRecencyFilter(value: SearchOptions["recencyFilter"]): string | undefined {
274
+ switch (value) {
275
+ case "day": return "qdr:d";
276
+ case "week": return "qdr:w";
277
+ case "month": return "qdr:m";
278
+ case "year": return "qdr:y";
279
+ default: return undefined;
280
+ }
281
+ }
282
+
283
+ function searchBody(query: string, options: FirecrawlSearchOptions, numResults: number, filters: DomainFilters): Record<string, unknown> {
284
+ return {
285
+ query,
286
+ limit: numResults,
287
+ sources: ["web"],
288
+ ...(filters.include.length > 0 ? { includeDomains: filters.include } : {}),
289
+ ...(filters.include.length === 0 && filters.exclude.length > 0 ? { excludeDomains: filters.exclude } : {}),
290
+ ...(mapRecencyFilter(options.recencyFilter) ? { tbs: mapRecencyFilter(options.recencyFilter) } : {}),
291
+ ...(options.includeContent ? {
292
+ scrapeOptions: {
293
+ formats: ["markdown"],
294
+ onlyMainContent: true,
295
+ ...(allowFreshScrape() ? {} : { lockdown: true }),
296
+ },
297
+ } : {}),
298
+ };
299
+ }
300
+
301
+ function firstString(...values: unknown[]): string | null {
302
+ for (const value of values) {
303
+ if (typeof value === "string" && value.trim()) return value.trim();
304
+ }
305
+ return null;
306
+ }
307
+
308
+ function buildAnswer(results: SearchResponse["results"]): string {
309
+ return results.map((result) => {
310
+ if (result.snippet) return `${result.snippet}\nSource: ${result.title} (${result.url})`;
311
+ return `Source: ${result.title} (${result.url})`;
312
+ }).join("\n\n");
313
+ }
314
+
315
+ function mapSearchResults(data: unknown, numResults: number, filters: DomainFilters): {
316
+ results: SearchResponse["results"];
317
+ inlineContent: ExtractedContent[];
318
+ } {
319
+ const web = Array.isArray(data)
320
+ ? data
321
+ : data && typeof data === "object" ? (data as Record<string, unknown>).web : undefined;
322
+ if (!Array.isArray(web)) throw new Error("Firecrawl search returned an unexpected web result shape");
323
+ const results: SearchResponse["results"] = [];
324
+ const inlineContent: ExtractedContent[] = [];
325
+ const seen = new Set<string>();
326
+ for (const rawItem of web) {
327
+ if (!rawItem || typeof rawItem !== "object" || Array.isArray(rawItem)) continue;
328
+ const item = rawItem as FirecrawlSearchItem;
329
+ const url = firstString(item.url, item.metadata?.sourceURL, item.metadata?.url);
330
+ if (!url || seen.has(url) || !passesDomainFilters(url, filters)) continue;
331
+ seen.add(url);
332
+ const title = firstString(item.title, item.metadata?.title) ?? url;
333
+ const snippet = firstString(item.description, item.snippet, item.metadata?.description) ?? "";
334
+ results.push({ title, url, snippet });
335
+ const markdown = firstString(item.markdown);
336
+ if (markdown) inlineContent.push({ url, title, content: markdown, error: null });
337
+ if (results.length >= numResults) break;
338
+ }
339
+ return { results, inlineContent };
340
+ }
341
+
202
342
  async function firecrawlFetch(
203
343
  endpoint: string,
204
344
  body: Record<string, unknown>,
205
345
  signal: AbortSignal | undefined,
206
- options: FirecrawlExtractOptions | undefined,
346
+ options: FirecrawlExtractOptions | FirecrawlSearchOptions | undefined,
347
+ label = endpoint,
348
+ activity: { type: "api"; query: string } | { type: "fetch"; url: string } | undefined = undefined,
207
349
  ): Promise<Record<string, unknown>> {
208
350
  const baseUrl = requireBaseUrl();
209
351
  const version = getApiVersion();
@@ -211,7 +353,7 @@ async function firecrawlFetch(
211
353
  const headers: Record<string, string> = { "Content-Type": "application/json" };
212
354
  if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
213
355
  const requestUrl = `${baseUrl}/${version}/${endpoint}`;
214
- const activityId = activityMonitor.logStart({ type: "fetch", url: requestUrl });
356
+ const activityId = activityMonitor.logStart(activity ?? { type: "fetch", url: requestUrl });
215
357
  try {
216
358
  const response = await fetchFirecrawlApi(requestUrl, {
217
359
  method: "POST",
@@ -221,24 +363,24 @@ async function firecrawlFetch(
221
363
  }, options);
222
364
  if (!response.ok) {
223
365
  const text = await response.text().catch(() => "");
224
- throw new Error(`Firecrawl scrape error ${response.status}: ${redactCredential(text.slice(0, 300), apiKey)}`);
366
+ throw new Error(`Firecrawl ${label} error ${response.status}: ${redactCredential(text.slice(0, 300), apiKey)}`);
225
367
  }
226
368
  let data: unknown;
227
369
  try {
228
370
  data = await response.json();
229
371
  } catch (err) {
230
- throw new Error(`Firecrawl scrape returned invalid JSON: ${errorMessage(err)}`);
372
+ throw new Error(`Firecrawl ${label} returned invalid JSON: ${errorMessage(err)}`);
231
373
  }
232
374
  if (!data || typeof data !== "object" || Array.isArray(data)) {
233
- throw new Error("Firecrawl scrape returned an unexpected response shape");
375
+ throw new Error(`Firecrawl ${label} returned an unexpected response shape`);
234
376
  }
235
377
  const envelope = data as Record<string, unknown>;
236
378
  if (envelope.success === false) {
237
379
  const reason = typeof envelope.error === "string" && envelope.error.trim() ? envelope.error : "unknown error";
238
- throw new Error(`Firecrawl scrape unsuccessful: ${redactCredential(reason, apiKey)}`);
380
+ throw new Error(`Firecrawl ${label} unsuccessful: ${redactCredential(reason, apiKey)}`);
239
381
  }
240
382
  if (envelope.success !== true) {
241
- throw new Error("Firecrawl scrape returned an unexpected response shape");
383
+ throw new Error(`Firecrawl ${label} returned an unexpected response shape`);
242
384
  }
243
385
  activityMonitor.logComplete(activityId, response.status);
244
386
  return envelope;
@@ -253,6 +395,24 @@ export function isFirecrawlAvailable(): boolean {
253
395
  return getBaseUrl() !== null;
254
396
  }
255
397
 
398
+ export async function searchWithFirecrawl(query: string, options: FirecrawlSearchOptions = {}): Promise<SearchResponse> {
399
+ requireBaseUrl();
400
+ const numResults = normalizeCount(options.numResults);
401
+ const filters = parseDomainFilter(options.domainFilter);
402
+ const envelope = await firecrawlFetch(
403
+ "search",
404
+ searchBody(query, options, numResults, filters),
405
+ options.signal,
406
+ { ...options, timeoutMs: options.timeoutMs ?? SEARCH_TIMEOUT_MS },
407
+ "search",
408
+ { type: "api", query },
409
+ );
410
+ const mapped = mapSearchResults(envelope.data, numResults, filters);
411
+ const response: SearchResponse = { answer: buildAnswer(mapped.results), results: mapped.results };
412
+ if (options.includeContent && mapped.inlineContent.length > 0) response.inlineContent = mapped.inlineContent;
413
+ return response;
414
+ }
415
+
256
416
  export async function extractWithFirecrawl(
257
417
  url: string,
258
418
  signal?: AbortSignal,