@herbertgao/pi-extensions 2026.9.1 → 2026.9.3
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.
- package/README.md +3 -2
- package/THIRD_PARTY_NOTICES.md +24 -0
- package/node_modules/@herbertgao/sol-pi/LICENSE +19 -0
- package/node_modules/@herbertgao/sol-pi/README.md +159 -0
- package/node_modules/@herbertgao/sol-pi/SECURITY.md +26 -0
- package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +19 -0
- package/node_modules/@herbertgao/sol-pi/agents-install.md +150 -0
- package/node_modules/@herbertgao/sol-pi/assets/sol-pi-hero.png +0 -0
- package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +69 -0
- package/node_modules/@herbertgao/sol-pi/docs/configuration.md +75 -0
- package/node_modules/@herbertgao/sol-pi/package.json +76 -0
- package/node_modules/@herbertgao/sol-pi/scripts/check-pi-compat.mjs +32 -0
- package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +120 -0
- package/node_modules/@herbertgao/sol-pi/sol-pi.example.json +10 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +135 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +74 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +185 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +128 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/archive.ts +53 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/candidate.ts +101 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/config.ts +71 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/index.ts +220 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/journal.ts +25 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/provider.ts +164 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/receipt.ts +177 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/index.ts +227 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/ledger.ts +20 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +252 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +237 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +455 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +49 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +79 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +208 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +100 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/index.ts +42 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +17 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/tui.ts +71 -0
- package/node_modules/@narumitw/pi-btw/README.md +57 -141
- package/node_modules/@narumitw/pi-btw/dist/index.ts +478 -43
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
- package/node_modules/@narumitw/pi-btw/docs/workflows.md +74 -0
- package/node_modules/@narumitw/pi-btw/package.json +2 -1
- package/node_modules/@narumitw/pi-btw/src/btw.ts +28 -3
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +32 -2
- package/node_modules/@narumitw/pi-btw/src/keybindings.ts +330 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +136 -19
- package/node_modules/@narumitw/pi-btw/src/settings.ts +32 -0
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +57 -5
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +52 -30
- package/node_modules/pi-lens/CHANGELOG.md +260 -0
- package/node_modules/pi-lens/README.md +13 -8
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -1
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
- package/node_modules/pi-lens/dist/clients/analysed-root.js +1 -0
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +1 -8
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +2 -0
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +133 -1
- package/node_modules/pi-lens/dist/clients/biome-client.js +13 -2
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -6
- package/node_modules/pi-lens/dist/clients/bootstrap.js +4 -4
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +1 -1
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +2 -5
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +1 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +122 -6
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +2 -2
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
- package/node_modules/pi-lens/dist/clients/complexity-client.js +5 -0
- package/node_modules/pi-lens/dist/clients/config-core/index.js +11 -8
- package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +1 -1
- package/node_modules/pi-lens/dist/clients/config-core/provenance.js +2 -9
- package/node_modules/pi-lens/dist/clients/config-core/records.js +1 -1
- package/node_modules/pi-lens/dist/clients/config-core/schema.js +3 -5
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +3 -1
- package/node_modules/pi-lens/dist/clients/config-resolve.js +10 -7
- package/node_modules/pi-lens/dist/clients/config-schema.js +56 -2
- package/node_modules/pi-lens/dist/clients/config-warn.js +2 -2
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +8 -1
- package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -4
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +5 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +1 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +38 -41
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +18 -1
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -6
- package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -3
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +7 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +19 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +15 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +3 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +12 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +5 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +1 -64
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +8 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -21
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +2 -20
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +5 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +2 -2
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/effective-config.js +12 -2
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +2 -2
- package/node_modules/pi-lens/dist/clients/extension-log.js +2 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +1 -38
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -1
- package/node_modules/pi-lens/dist/clients/file-time.js +1 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +5 -1
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +4 -0
- package/node_modules/pi-lens/dist/clients/format-service.js +0 -1
- package/node_modules/pi-lens/dist/clients/formatters.js +66 -93
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +3 -4
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +5 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +42 -6
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +48 -7
- package/node_modules/pi-lens/dist/clients/grammar-source.js +3 -3
- package/node_modules/pi-lens/dist/clients/installer/index.js +416 -61
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +6 -6
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +9 -1
- package/node_modules/pi-lens/dist/clients/knip-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +12 -11
- package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
- package/node_modules/pi-lens/dist/clients/latency-logger.js +7 -7
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +23 -0
- package/node_modules/pi-lens/dist/clients/lens-engine.js +3 -4
- package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +18 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -36
- package/node_modules/pi-lens/dist/clients/lsp/client.js +4 -3
- package/node_modules/pi-lens/dist/clients/lsp/config.js +37 -7
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +24 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +79 -1
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +3 -3
- package/node_modules/pi-lens/dist/clients/lsp/index.js +491 -64
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -8
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -2
- package/node_modules/pi-lens/dist/clients/lsp/server.js +62 -15
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -1
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +5 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +6 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +37 -4
- package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -1
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +3 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +2 -0
- package/node_modules/pi-lens/dist/clients/metrics-history.js +28 -107
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +1 -1
- package/node_modules/pi-lens/dist/clients/module-report.js +1 -1
- package/node_modules/pi-lens/dist/clients/mutating-tool.js +3 -3
- package/node_modules/pi-lens/dist/clients/mutation-attribution.js +3 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -4
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +9 -9
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +2 -6
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +2 -0
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +1 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +2 -1
- package/node_modules/pi-lens/dist/clients/path-utils.js +23 -2
- package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +9 -4
- package/node_modules/pi-lens/dist/clients/pipeline.js +5 -4
- package/node_modules/pi-lens/dist/clients/project-changes.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +38 -10
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +10 -9
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +49 -1
- package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
- package/node_modules/pi-lens/dist/clients/python-provenance.js +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +3 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +5 -2
- package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +3 -5
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +2 -0
- package/node_modules/pi-lens/dist/clients/ruff-client.js +18 -9
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +10 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +5 -0
- package/node_modules/pi-lens/dist/clients/runtime-session.js +58 -14
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +11 -1
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +4 -1
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +48 -1
- package/node_modules/pi-lens/dist/clients/sanitize.js +0 -11
- package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -2
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +69 -1
- package/node_modules/pi-lens/dist/clients/session-summary.js +0 -24
- package/node_modules/pi-lens/dist/clients/sg-runner.js +2 -1
- package/node_modules/pi-lens/dist/clients/sgconfig.js +1 -5
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
- package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +108 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
- package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +291 -76
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +77 -72
- package/node_modules/pi-lens/dist/clients/tool-config.js +199 -0
- package/node_modules/pi-lens/dist/clients/tool-cwd.js +299 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
- package/node_modules/pi-lens/dist/clients/tool-probe.js +53 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +42 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +1 -52
- package/node_modules/pi-lens/dist/clients/trivy-client.js +9 -1
- package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -4
- package/node_modules/pi-lens/dist/clients/turn-context.js +52 -0
- package/node_modules/pi-lens/dist/clients/user-notify.js +0 -4
- package/node_modules/pi-lens/dist/clients/widget-state.js +4 -3
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -7
- package/node_modules/pi-lens/dist/clients/word-index-store.js +3 -7
- package/node_modules/pi-lens/dist/clients/word-index.js +9 -16
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +1 -1
- package/node_modules/pi-lens/dist/index.js +19969 -17481
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -2
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -1
- package/node_modules/pi-lens/dist/mcp/server.js +135 -159
- package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +1 -1
- package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +1 -1
- package/node_modules/pi-lens/dist/tools/activate-tools.js +10 -7
- package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +2 -13
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +8 -13
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +83 -39
- package/node_modules/pi-lens/dist/tools/effective-config.js +4 -3
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +1 -10
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +354 -86
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +54 -112
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +20 -44
- package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +5 -9
- package/node_modules/pi-lens/dist/tools/project-report.js +9 -17
- package/node_modules/pi-lens/dist/tools/render-compact.js +281 -2
- package/node_modules/pi-lens/dist/tools/symbol-search.js +3 -4
- package/node_modules/pi-lens/docs/agent-guide.md +3 -4
- package/node_modules/pi-lens/docs/agent-tools.md +46 -22
- package/node_modules/pi-lens/docs/configuration.md +9 -0
- package/node_modules/pi-lens/docs/environment-variables.md +0 -5
- package/node_modules/pi-lens/docs/features.md +5 -5
- package/node_modules/pi-lens/docs/globalconfig.md +31 -1
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
- package/node_modules/pi-lens/docs/mcp.md +13 -1
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +71 -0
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +15 -0
- package/node_modules/pi-lens/docs/pi-lens-monitor.md +88 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +41 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +21 -2
- package/node_modules/pi-lens/docs/public-api-stability.md +1 -0
- package/node_modules/pi-lens/docs/real-harness.md +46 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +17 -5
- package/node_modules/pi-lens/docs/servercapabilities.md +2 -3
- package/node_modules/pi-lens/docs/settings.md +37 -2
- package/node_modules/pi-lens/docs/tools_improvement2.md +4 -4
- package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
- package/node_modules/pi-lens/docs/usage.md +13 -3
- package/node_modules/pi-lens/docs/word-index.md +1 -1
- package/node_modules/pi-lens/package.json +15 -15
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +7 -3
- package/node_modules/pi-lens/rules/typos/_typos.toml +4 -2
- package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +1 -1
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +1 -1
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +8 -6
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +19 -9
- package/node_modules/pi-web-access/CHANGELOG.md +29 -0
- package/node_modules/pi-web-access/README.md +33 -22
- package/node_modules/pi-web-access/abortable.ts +17 -0
- package/node_modules/pi-web-access/crawl4ai.ts +204 -0
- package/node_modules/pi-web-access/credential-source.ts +1 -0
- package/node_modules/pi-web-access/curator-page.ts +20 -2
- package/node_modules/pi-web-access/curator-run.ts +44 -0
- package/node_modules/pi-web-access/curator-server.ts +3 -1
- package/node_modules/pi-web-access/duckduckgo.ts +1 -1
- package/node_modules/pi-web-access/extract.ts +57 -12
- package/node_modules/pi-web-access/fetch-params.ts +1 -1
- package/node_modules/pi-web-access/gemini-search.ts +9 -5
- package/node_modules/pi-web-access/index.ts +25 -18
- package/node_modules/pi-web-access/openai-search.ts +35 -13
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +5 -2
- package/node_modules/pi-web-access/query-rewrite.ts +5 -2
- package/node_modules/pi-web-access/serpapi.ts +220 -0
- package/node_modules/pi-web-access/ssrf-protection.ts +5 -1
- package/node_modules/pi-web-access/storage.ts +10 -0
- package/node_modules/pi-web-access/summary-review.ts +34 -21
- package/node_modules/pi-web-access/utils.ts +6 -4
- package/package.json +7 -4
- package/node_modules/pi-lens/dist/tools/ast-dump.js +0 -103
|
@@ -43,6 +43,7 @@ function buildProviderButtons(
|
|
|
43
43
|
{ value: "mistral", label: "Mistral", available: available.mistral },
|
|
44
44
|
{ value: "brightdata", label: "Bright Data", available: available.brightdata },
|
|
45
45
|
{ value: "serpbase", label: "SerpBase", available: available.serpbase },
|
|
46
|
+
{ value: "serpapi", label: "SerpApi", available: available.serpapi },
|
|
46
47
|
{ value: "serper", label: "Serper", available: available.serper },
|
|
47
48
|
{ value: "valyu", label: "Valyu", available: available.valyu },
|
|
48
49
|
];
|
|
@@ -149,6 +150,7 @@ ${CSS}
|
|
|
149
150
|
<button class="btn btn-secondary" id="btn-summary-regenerate">Regenerate</button>
|
|
150
151
|
<button class="btn btn-secondary" id="btn-summary-preview" title="Preview rendered summary">Preview</button>
|
|
151
152
|
<button class="btn btn-submit" id="btn-summary-approve">Approve</button>
|
|
153
|
+
<button class="btn btn-submit" id="btn-summary-approve-remaining">Approve + auto-summary remaining searches for this prompt</button>
|
|
152
154
|
</div>
|
|
153
155
|
</section>
|
|
154
156
|
</main>
|
|
@@ -1197,6 +1199,7 @@ main {
|
|
|
1197
1199
|
}
|
|
1198
1200
|
.summary-actions {
|
|
1199
1201
|
display: flex;
|
|
1202
|
+
flex-wrap: wrap;
|
|
1200
1203
|
align-items: center;
|
|
1201
1204
|
justify-content: flex-end;
|
|
1202
1205
|
gap: 8px;
|
|
@@ -1469,7 +1472,7 @@ const SCRIPT = `(function() {
|
|
|
1469
1472
|
var token = DATA.sessionToken;
|
|
1470
1473
|
var timeoutSec = DATA.timeout;
|
|
1471
1474
|
var queries = Array.isArray(DATA.queries) ? DATA.queries : [];
|
|
1472
|
-
var providers = ["all", "openai", "exa", "brave", "parallel", "parallel-mcp", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "serpdive", "kagi", "bocha", "ollama", "searxng", "duckduckgo", "perplexity", "gemini", "kimi", "anysearch", "xcrawl", "xai", "mistral", "brightdata", "serpbase", "serper", "valyu"];
|
|
1475
|
+
var providers = ["all", "openai", "exa", "brave", "parallel", "parallel-mcp", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "serpdive", "kagi", "bocha", "ollama", "searxng", "duckduckgo", "perplexity", "gemini", "kimi", "anysearch", "xcrawl", "xai", "mistral", "brightdata", "serpbase", "serpapi", "serper", "valyu"];
|
|
1473
1476
|
var availProviders = DATA.availableProviders && typeof DATA.availableProviders === "object" ? DATA.availableProviders : {};
|
|
1474
1477
|
var workflow = "summary-review";
|
|
1475
1478
|
var initialDefaultProvider = typeof DATA.defaultProvider === "string" ? DATA.defaultProvider : "exa";
|
|
@@ -1534,6 +1537,7 @@ const SCRIPT = `(function() {
|
|
|
1534
1537
|
var btnSummaryRegenerate = document.getElementById("btn-summary-regenerate");
|
|
1535
1538
|
var btnSummaryPreview = document.getElementById("btn-summary-preview");
|
|
1536
1539
|
var btnSummaryApprove = document.getElementById("btn-summary-approve");
|
|
1540
|
+
var btnSummaryApproveRemaining = document.getElementById("btn-summary-approve-remaining");
|
|
1537
1541
|
var successOverlay = document.getElementById("success-overlay");
|
|
1538
1542
|
var successText = document.getElementById("success-text");
|
|
1539
1543
|
var expiredOverlay = document.getElementById("expired-overlay");
|
|
@@ -1699,6 +1703,9 @@ const SCRIPT = `(function() {
|
|
|
1699
1703
|
if (provider === "mistral") return "Mistral";
|
|
1700
1704
|
if (provider === "brightdata") return "Bright Data";
|
|
1701
1705
|
if (provider === "serpbase") return "SerpBase";
|
|
1706
|
+
if (provider === "serpapi") return "SerpApi";
|
|
1707
|
+
if (provider === "serper") return "Serper";
|
|
1708
|
+
if (provider === "valyu") return "Valyu";
|
|
1702
1709
|
return "Unknown";
|
|
1703
1710
|
}
|
|
1704
1711
|
|
|
@@ -2114,6 +2121,9 @@ const SCRIPT = `(function() {
|
|
|
2114
2121
|
if (btnSummaryApprove) {
|
|
2115
2122
|
btnSummaryApprove.disabled = submitted || timerExpired || submitInFlight || stage === "generating-summary" || isRegenerating || !hasSelection || !hasDraft;
|
|
2116
2123
|
}
|
|
2124
|
+
if (btnSummaryApproveRemaining) {
|
|
2125
|
+
btnSummaryApproveRemaining.disabled = submitted || timerExpired || submitInFlight || stage === "generating-summary" || isRegenerating || !hasSelection || !hasDraft;
|
|
2126
|
+
}
|
|
2117
2127
|
|
|
2118
2128
|
applyProviderInterlocks();
|
|
2119
2129
|
}
|
|
@@ -3205,7 +3215,7 @@ const SCRIPT = `(function() {
|
|
|
3205
3215
|
requestSummary(selected);
|
|
3206
3216
|
}
|
|
3207
3217
|
|
|
3208
|
-
function doApprove() {
|
|
3218
|
+
function doApprove(autoApproveRemainingSearches) {
|
|
3209
3219
|
if (submitted || timerExpired || submitInFlight || stage !== "summary-review") return;
|
|
3210
3220
|
|
|
3211
3221
|
var selected = getSelectedIndices();
|
|
@@ -3217,6 +3227,7 @@ const SCRIPT = `(function() {
|
|
|
3217
3227
|
|
|
3218
3228
|
var draft = getSummaryDraftText();
|
|
3219
3229
|
var payload = { selected: selected };
|
|
3230
|
+
if (autoApproveRemainingSearches === true) payload.autoApproveRemainingSearches = true;
|
|
3220
3231
|
if (draft.length > 0) {
|
|
3221
3232
|
payload.summary = draft;
|
|
3222
3233
|
payload.summaryMeta = normalizeSummaryMeta(summaryMeta, summaryMeta && summaryMeta.edited === true);
|
|
@@ -3455,6 +3466,13 @@ const SCRIPT = `(function() {
|
|
|
3455
3466
|
});
|
|
3456
3467
|
}
|
|
3457
3468
|
|
|
3469
|
+
if (btnSummaryApproveRemaining) {
|
|
3470
|
+
btnSummaryApproveRemaining.addEventListener("click", function() {
|
|
3471
|
+
doApprove(true);
|
|
3472
|
+
resetTimer();
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3458
3476
|
if (summaryInput) {
|
|
3459
3477
|
summaryInput.addEventListener("input", function() {
|
|
3460
3478
|
if (!summaryMeta || typeof summaryMeta !== "object") {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
export type WebSearchWorkflow = "none" | "summary-review" | "auto-summary";
|
|
4
|
+
|
|
5
|
+
export function resolveWebSearchWorkflow(input: unknown, hasUI: boolean): WebSearchWorkflow {
|
|
6
|
+
const normalized = typeof input === "string" ? input.trim().toLowerCase() : "";
|
|
7
|
+
if (normalized === "auto-summary") return "auto-summary";
|
|
8
|
+
if (!hasUI) return "none";
|
|
9
|
+
if (normalized === "none") return "none";
|
|
10
|
+
return "summary-review";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class CuratorRunState {
|
|
14
|
+
private autoApproveRemaining = false;
|
|
15
|
+
|
|
16
|
+
approveRemainingSearches(): void {
|
|
17
|
+
this.autoApproveRemaining = true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
reset(): void {
|
|
21
|
+
this.autoApproveRemaining = false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
resolve(requestedWorkflow: unknown, configuredWorkflow: unknown, hasUI: boolean): WebSearchWorkflow {
|
|
25
|
+
const inherited = requestedWorkflow === undefined;
|
|
26
|
+
const workflow = resolveWebSearchWorkflow(
|
|
27
|
+
inherited ? configuredWorkflow : requestedWorkflow,
|
|
28
|
+
hasUI,
|
|
29
|
+
);
|
|
30
|
+
return inherited && this.autoApproveRemaining && workflow === "summary-review"
|
|
31
|
+
? "auto-summary"
|
|
32
|
+
: workflow;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function registerCuratorRunLifecycle(pi: Pick<ExtensionAPI, "on">): CuratorRunState {
|
|
37
|
+
const state = new CuratorRunState();
|
|
38
|
+
pi.on("before_agent_start", () => state.reset());
|
|
39
|
+
pi.on("agent_settled", () => state.reset());
|
|
40
|
+
pi.on("session_start", () => state.reset());
|
|
41
|
+
pi.on("session_tree", () => state.reset());
|
|
42
|
+
pi.on("session_shutdown", () => state.reset());
|
|
43
|
+
return state;
|
|
44
|
+
}
|
|
@@ -41,7 +41,7 @@ export interface IndexedCuratorSearchEntry extends CuratorSearchEntry {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export interface CuratorServerCallbacks {
|
|
44
|
-
onSubmit: (payload: { selectedQueryIndices: number[]; summary?: string; summaryMeta?: SummaryMeta; rawResults?: boolean }) => void;
|
|
44
|
+
onSubmit: (payload: { selectedQueryIndices: number[]; summary?: string; summaryMeta?: SummaryMeta; rawResults?: boolean; autoApproveRemainingSearches?: boolean }) => void;
|
|
45
45
|
onCancel: (reason: "user" | "timeout" | "stale") => void;
|
|
46
46
|
onProviderChange: (provider: string) => void;
|
|
47
47
|
onAddSearch: (query: string, provider?: string) => Promise<CuratorSearchEntry[]>;
|
|
@@ -642,12 +642,14 @@ export function startCuratorServer(
|
|
|
642
642
|
return;
|
|
643
643
|
}
|
|
644
644
|
const rawResults = (body as { rawResults?: unknown }).rawResults === true;
|
|
645
|
+
const autoApproveRemainingSearches = (body as { autoApproveRemainingSearches?: unknown }).autoApproveRemainingSearches === true;
|
|
645
646
|
sendJson(res, 200, { ok: true });
|
|
646
647
|
setImmediate(() => callbacks.onSubmit({
|
|
647
648
|
selectedQueryIndices: parsed.indices,
|
|
648
649
|
...(summary !== undefined ? { summary } : {}),
|
|
649
650
|
...(summaryMeta !== undefined ? { summaryMeta } : {}),
|
|
650
651
|
rawResults,
|
|
652
|
+
...(autoApproveRemainingSearches ? { autoApproveRemainingSearches: true } : {}),
|
|
651
653
|
}));
|
|
652
654
|
return;
|
|
653
655
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { parseHTML } from "linkedom";
|
|
2
1
|
import { activityMonitor } from "./activity.ts";
|
|
3
2
|
import type { SearchOptions, SearchResult, SearchResponse } from "./perplexity.ts";
|
|
4
3
|
|
|
@@ -88,6 +87,7 @@ export async function searchWithDuckDuckGo(query: string, options: SearchOptions
|
|
|
88
87
|
throw new Error(`DuckDuckGo search error ${response.status}: ${body.slice(0, 300)}`);
|
|
89
88
|
}
|
|
90
89
|
|
|
90
|
+
const { parseHTML } = await import("linkedom");
|
|
91
91
|
const { document } = parseHTML(await response.text());
|
|
92
92
|
const filters = normalizeDomainFilters(options.domainFilter);
|
|
93
93
|
const results: SearchResult[] = [];
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import
|
|
3
|
-
import { resizeImage } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
import { parseHTML } from "linkedom";
|
|
5
|
-
import TurndownService from "turndown";
|
|
2
|
+
import type TurndownService from "turndown";
|
|
6
3
|
import pLimit from "p-limit";
|
|
7
4
|
import { activityMonitor } from "./activity.ts";
|
|
8
5
|
import { extractRSCContent } from "./rsc-extract.ts";
|
|
@@ -20,6 +17,7 @@ import { extractWithQuerit, isQueritAvailable } from "./querit.ts";
|
|
|
20
17
|
import { extractWithKagi, isKagiExtractAvailable } from "./kagi.ts";
|
|
21
18
|
import { extractWithOllama, isOllamaFetchAvailable } from "./ollama.ts";
|
|
22
19
|
import { extractWithFirecrawl, isFirecrawlAvailable } from "./firecrawl.ts";
|
|
20
|
+
import { extractWithCrawl4ai, isCrawl4aiAvailable } from "./crawl4ai.ts";
|
|
23
21
|
import { extractWithBrightDataUnlocker, isBrightDataUnlockerAvailable } from "./brightdata-unlocker.ts";
|
|
24
22
|
import { isVideoFile, extractVideo, extractVideoFrame, getLocalVideoDuration } from "./video-extract.ts";
|
|
25
23
|
import { appendDeclaredWebLinks, discoverDeclaredWebLinks, type DeclaredWebLink } from "./declared-web-links.ts";
|
|
@@ -70,10 +68,10 @@ function loadFetchTimeoutMs(): number {
|
|
|
70
68
|
const NON_RECOVERABLE_ERRORS = ["Unsupported content type", "Response too large", "PDF extraction is disabled", "Image fetching is disabled"];
|
|
71
69
|
const MIN_USEFUL_CONTENT = 500;
|
|
72
70
|
const SUPPORTED_IMAGE_TYPES = new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
|
|
73
|
-
const FETCH_PROVIDERS = ["http", "firecrawl", "jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "parallel-mcp", "brightdata", "gemini"] as const;
|
|
71
|
+
const FETCH_PROVIDERS = ["http", "firecrawl", "crawl4ai", "jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "parallel-mcp", "brightdata", "gemini"] as const;
|
|
74
72
|
type FetchProvider = typeof FETCH_PROVIDERS[number];
|
|
75
73
|
type FetchRouting = { providers: FetchProvider[]; allowRemoteHostedProviders: boolean };
|
|
76
|
-
const DEFAULT_FETCH_PROVIDER_ORDER: FetchProvider[] = ["http", "firecrawl", "jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "brightdata", "gemini"];
|
|
74
|
+
const DEFAULT_FETCH_PROVIDER_ORDER: FetchProvider[] = ["http", "firecrawl", "crawl4ai", "jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "brightdata", "gemini"];
|
|
77
75
|
const REMOTE_HOSTED_FETCH_PROVIDERS = new Set<FetchProvider>(["jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "parallel-mcp", "brightdata", "gemini"]);
|
|
78
76
|
|
|
79
77
|
function isDefuddleConsoleError(args: Parameters<typeof console.error>): boolean {
|
|
@@ -83,6 +81,7 @@ function isDefuddleConsoleError(args: Parameters<typeof console.error>): boolean
|
|
|
83
81
|
|
|
84
82
|
async function extractWithDefuddle(text: string, url: string): Promise<{ title: string; content: string } | null> {
|
|
85
83
|
const { Defuddle } = await import("defuddle/node");
|
|
84
|
+
const { parseHTML } = await import("linkedom");
|
|
86
85
|
const { document } = parseHTML(text);
|
|
87
86
|
Object.defineProperty(document, "location", {
|
|
88
87
|
value: new URL(url),
|
|
@@ -135,6 +134,10 @@ function isAbortError(err: unknown): boolean {
|
|
|
135
134
|
return errorMessage(err).toLowerCase().includes("abort");
|
|
136
135
|
}
|
|
137
136
|
|
|
137
|
+
function isAbortException(err: unknown): boolean {
|
|
138
|
+
return err instanceof DOMException && (err.name === "AbortError" || err.name === "TimeoutError");
|
|
139
|
+
}
|
|
140
|
+
|
|
138
141
|
function isRedirectPolicyError(message: string): boolean {
|
|
139
142
|
return message.startsWith("Authenticated fetch refused cross-origin redirect") ||
|
|
140
143
|
message.startsWith("Blocked internal ") ||
|
|
@@ -288,10 +291,19 @@ function abortedResult(url: string): ExtractedContent {
|
|
|
288
291
|
return { url, title: "", content: "", error: "Aborted" };
|
|
289
292
|
}
|
|
290
293
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
});
|
|
294
|
+
// Share first-use initialization without loading Turndown at extension startup.
|
|
295
|
+
let turndownInstance: Promise<TurndownService> | undefined;
|
|
296
|
+
async function loadTurndown(): Promise<TurndownService> {
|
|
297
|
+
const { default: TurndownService } = await import("turndown");
|
|
298
|
+
return new TurndownService({
|
|
299
|
+
headingStyle: "atx",
|
|
300
|
+
codeBlockStyle: "fenced",
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
function getTurndown(): Promise<TurndownService> {
|
|
304
|
+
turndownInstance ??= loadTurndown();
|
|
305
|
+
return turndownInstance;
|
|
306
|
+
}
|
|
295
307
|
|
|
296
308
|
const fetchLimit = pLimit(CONCURRENT_LIMIT);
|
|
297
309
|
|
|
@@ -329,7 +341,7 @@ export interface ExtractOptions {
|
|
|
329
341
|
answerModel?: string;
|
|
330
342
|
authFetchProfile?: AuthFetchProfile;
|
|
331
343
|
toolNames?: RegisteredToolNames;
|
|
332
|
-
/** Optional
|
|
344
|
+
/** Optional HTTP(S) or SOCKS proxy URL; routed through the curl-backed transport. */
|
|
333
345
|
proxy?: string;
|
|
334
346
|
/** Custom DNS resolver used for SSRF validation. Primarily a test seam. */
|
|
335
347
|
lookup?: Lookup;
|
|
@@ -788,6 +800,7 @@ export async function extractContent(
|
|
|
788
800
|
};
|
|
789
801
|
|
|
790
802
|
let firecrawlError: string | null = null;
|
|
803
|
+
let crawl4aiError: string | null = null;
|
|
791
804
|
let tinyfishError: string | null = null;
|
|
792
805
|
let search1apiError: string | null = null;
|
|
793
806
|
let queritError: string | null = null;
|
|
@@ -830,6 +843,25 @@ export async function extractContent(
|
|
|
830
843
|
continue;
|
|
831
844
|
}
|
|
832
845
|
|
|
846
|
+
if (provider === "crawl4ai") {
|
|
847
|
+
try {
|
|
848
|
+
if (isCrawl4aiAvailable()) {
|
|
849
|
+
const ssrf = loadSsrfConfig();
|
|
850
|
+
const crawl4aiResult = await extractWithCrawl4ai(url, signal, {
|
|
851
|
+
timeoutMs: options?.timeoutMs,
|
|
852
|
+
...(options?.lookup ? { lookup: options.lookup } : {}),
|
|
853
|
+
ssrf,
|
|
854
|
+
});
|
|
855
|
+
if (crawl4aiResult) return withDeclaredLinks(crawl4aiResult);
|
|
856
|
+
}
|
|
857
|
+
} catch (err) {
|
|
858
|
+
if (signal?.aborted || isAbortException(err)) return abortedResult(url);
|
|
859
|
+
crawl4aiError = errorMessage(err);
|
|
860
|
+
if (isConfigParseError(err)) return parseErrorResult(crawl4aiError);
|
|
861
|
+
}
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
|
|
833
865
|
if (provider === "jina") {
|
|
834
866
|
const jinaResult = await extractWithJinaReader(url, fetchTimeoutMs, signal, options?.lookup);
|
|
835
867
|
if (jinaResult) return withDeclaredLinks(jinaResult);
|
|
@@ -991,6 +1023,7 @@ export async function extractContent(
|
|
|
991
1023
|
const guidance = [
|
|
992
1024
|
finalHttpResult?.error ?? "No fetch_content provider returned content",
|
|
993
1025
|
...(firecrawlError ? [`Firecrawl fallback failed: ${firecrawlError}`] : []),
|
|
1026
|
+
...(crawl4aiError ? [`Crawl4AI fallback failed: ${crawl4aiError}`] : []),
|
|
994
1027
|
...(tinyfishError ? [`TinyFish fallback failed: ${tinyfishError}`] : []),
|
|
995
1028
|
...(search1apiError ? [`Search1API fallback failed: ${search1apiError}`] : []),
|
|
996
1029
|
...(queritError ? [`Querit fallback failed: ${queritError}`] : []),
|
|
@@ -1002,6 +1035,7 @@ export async function extractContent(
|
|
|
1002
1035
|
"",
|
|
1003
1036
|
"Fallback options:",
|
|
1004
1037
|
` • Set firecrawlBaseUrl in ${WEB_SEARCH_CONFIG_PATH} to a self-hosted Firecrawl instance`,
|
|
1038
|
+
` • Set crawl4aiBaseUrl in ${WEB_SEARCH_CONFIG_PATH} to a self-hosted Crawl4AI instance`,
|
|
1005
1039
|
` • Set tinyfishApiKey in ${WEB_SEARCH_CONFIG_PATH} or TINYFISH_API_KEY`,
|
|
1006
1040
|
` • Set search1apiApiKey in ${WEB_SEARCH_CONFIG_PATH} or SEARCH1API_KEY`,
|
|
1007
1041
|
` • Set queritApiKey in ${WEB_SEARCH_CONFIG_PATH} or QUERIT_API_KEY`,
|
|
@@ -1118,6 +1152,7 @@ async function extractViaHttp(
|
|
|
1118
1152
|
const activityId = activityMonitor.logStart({ type: "fetch", url });
|
|
1119
1153
|
|
|
1120
1154
|
const controller = new AbortController();
|
|
1155
|
+
const startedAt = Date.now();
|
|
1121
1156
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
1122
1157
|
|
|
1123
1158
|
const onAbort = () => controller.abort();
|
|
@@ -1210,6 +1245,7 @@ async function extractViaHttp(
|
|
|
1210
1245
|
}
|
|
1211
1246
|
try {
|
|
1212
1247
|
const buffer = await readResponseBufferWithLimit(response, maxResponseSize, () => responseSizeLimitError(maxResponseSize));
|
|
1248
|
+
const { resizeImage } = await import("@earendil-works/pi-coding-agent");
|
|
1213
1249
|
const resized = await resizeImage(new Uint8Array(buffer), mimeType, { maxWidth: 2000, maxHeight: 2000 });
|
|
1214
1250
|
activityMonitor.logComplete(activityId, response.status);
|
|
1215
1251
|
if (!resized) return { url, title: "", content: "", error: `Could not decode image: ${mimeType}`, mimeType, status: response.status };
|
|
@@ -1278,6 +1314,7 @@ async function extractViaHttp(
|
|
|
1278
1314
|
return { url, title, content: text, error: null };
|
|
1279
1315
|
}
|
|
1280
1316
|
|
|
1317
|
+
const { parseHTML } = await import("linkedom");
|
|
1281
1318
|
const { document } = parseHTML(text);
|
|
1282
1319
|
const documentTitle = document.title?.trim() ?? "";
|
|
1283
1320
|
const declaredLinks = discoverDeclaredWebLinks(
|
|
@@ -1285,6 +1322,7 @@ async function extractViaHttp(
|
|
|
1285
1322
|
response.headers.get("link"),
|
|
1286
1323
|
response.url || url,
|
|
1287
1324
|
);
|
|
1325
|
+
const { Readability } = await import("@mozilla/readability");
|
|
1288
1326
|
const reader = new Readability(document as unknown as Document);
|
|
1289
1327
|
const article = reader.parse();
|
|
1290
1328
|
|
|
@@ -1332,7 +1370,7 @@ async function extractViaHttp(
|
|
|
1332
1370
|
if (typeof article.content !== "string") {
|
|
1333
1371
|
throw new Error("Readability returned invalid article content");
|
|
1334
1372
|
}
|
|
1335
|
-
const markdown =
|
|
1373
|
+
const markdown = (await getTurndown()).turndown(article.content);
|
|
1336
1374
|
activityMonitor.logComplete(activityId, response.status);
|
|
1337
1375
|
|
|
1338
1376
|
if (markdown.length < MIN_USEFUL_CONTENT) {
|
|
@@ -1387,6 +1425,13 @@ async function extractViaHttp(
|
|
|
1387
1425
|
} finally {
|
|
1388
1426
|
clearTimeout(timeoutId);
|
|
1389
1427
|
signal?.removeEventListener("abort", onAbort);
|
|
1428
|
+
// Imports and CPU-bound processing need not observe the fetch signal, and
|
|
1429
|
+
// can finish before an expired timer gets a turn. Guard every exit, with
|
|
1430
|
+
// caller cancellation taking precedence over the internal deadline.
|
|
1431
|
+
if (signal?.aborted) return abortedResult(url);
|
|
1432
|
+
if (controller.signal.aborted || Date.now() - startedAt >= timeoutMs) {
|
|
1433
|
+
return { url, title: "", content: "", error: "The operation was aborted." };
|
|
1434
|
+
}
|
|
1390
1435
|
}
|
|
1391
1436
|
}
|
|
1392
1437
|
|
|
@@ -96,7 +96,7 @@ function normalizeAuth(value: unknown): true | string | undefined {
|
|
|
96
96
|
|
|
97
97
|
function normalizeProxy(value: unknown): string | undefined {
|
|
98
98
|
if (value === undefined || value === false) return undefined;
|
|
99
|
-
if (value === null) throw new Error("proxy must be an http(s) proxy URL string");
|
|
99
|
+
if (value === null) throw new Error("proxy must be an http(s) or socks proxy URL string");
|
|
100
100
|
const normalized = normalizeProxyUrl(value, "proxy");
|
|
101
101
|
return normalized ?? "";
|
|
102
102
|
}
|
|
@@ -34,13 +34,14 @@ import { isXcrawlAvailable, searchWithXCrawl } from "./xcrawl.ts";
|
|
|
34
34
|
import { isXaiSearchAvailable, searchWithXai } from "./xai-search.ts";
|
|
35
35
|
import { isBrightDataAvailable, searchWithBrightData } from "./brightdata.ts";
|
|
36
36
|
import { isSerpBaseAvailable, searchWithSerpBase } from "./serpbase.ts";
|
|
37
|
+
import { isSerpApiAvailable, searchWithSerpApi } from "./serpapi.ts";
|
|
37
38
|
import { isSerperAvailable, searchWithSerper } from "./serper.ts";
|
|
38
39
|
import { isValyuAvailable, searchWithValyu } from "./valyu.ts";
|
|
39
40
|
import { isKimiSearchAvailable, searchWithKimi } from "./kimi-search.ts";
|
|
40
41
|
import { isMistralAvailable, searchWithMistral } from "./mistral-search.ts";
|
|
41
42
|
import { getWebSearchConfigPath } from "./utils.ts";
|
|
42
43
|
|
|
43
|
-
export const RESOLVED_SEARCH_PROVIDERS = ["openai", "brave", "parallel", "parallel-mcp", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "searxng", "duckduckgo", "perplexity", "gemini", "kimi", "exa", "serpdive", "kagi", "ollama", "anysearch", "xai", "mistral", "brightdata", "serpbase", "serper", "valyu", "bocha", "xcrawl"] as const;
|
|
44
|
+
export const RESOLVED_SEARCH_PROVIDERS = ["openai", "brave", "parallel", "parallel-mcp", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "searxng", "duckduckgo", "perplexity", "gemini", "kimi", "exa", "serpdive", "kagi", "ollama", "anysearch", "xai", "mistral", "brightdata", "serpbase", "serpapi", "serper", "valyu", "bocha", "xcrawl"] as const;
|
|
44
45
|
export const SEARCH_PROVIDERS = ["auto", "all", ...RESOLVED_SEARCH_PROVIDERS] as const;
|
|
45
46
|
|
|
46
47
|
export type ResolvedSearchProvider = typeof RESOLVED_SEARCH_PROVIDERS[number];
|
|
@@ -105,7 +106,7 @@ export interface AttributedSearchResponse extends SearchResponse {
|
|
|
105
106
|
|
|
106
107
|
const CONFIG_PATH = getWebSearchConfigPath();
|
|
107
108
|
const DEFAULT_SEARCH_MODEL = "gemini-3.6-flash";
|
|
108
|
-
// Explicit-only providers (Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, xAI, Mistral, Bright Data, SerpBase, Serper, Valyu) are deliberately absent:
|
|
109
|
+
// Explicit-only providers (Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, xAI, Mistral, Bright Data, SerpBase, SerpApi, Serper, Valyu) are deliberately absent:
|
|
109
110
|
// `all` must never fan out to an opt-in or paid provider without the user asking for it.
|
|
110
111
|
export const ALL_SEARCH_PROVIDERS: ResolvedSearchProvider[] = ["searxng", "openai", "exa", "brave", "parallel", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "serpdive", "kagi", "ollama", "perplexity", "gemini", "bocha"];
|
|
111
112
|
const VALID_ROUTING_KINDS = ["transient", "quota", "network", "invalid-response", "unsupported"] as const;
|
|
@@ -311,7 +312,7 @@ function classifyProviderError(provider: ResolvedSearchProvider, err: unknown):
|
|
|
311
312
|
kind = "unsupported";
|
|
312
313
|
} else if (status === 400 || status === 422) {
|
|
313
314
|
kind = "invalid-request";
|
|
314
|
-
} else if (status === 402 || status === 429) {
|
|
315
|
+
} else if (status === 402 || status === 429 || (provider === "tavily" && status === 432)) {
|
|
315
316
|
kind = "quota";
|
|
316
317
|
} else if (status !== undefined && (status === 408 || status === 425 || status >= 500)) {
|
|
317
318
|
kind = "transient";
|
|
@@ -364,6 +365,7 @@ async function searchWithResolvedProvider(
|
|
|
364
365
|
if (provider === "mistral") return { ...(await searchWithMistral(query, options)), provider };
|
|
365
366
|
if (provider === "brightdata") return { ...(await searchWithBrightData(query, options)), provider };
|
|
366
367
|
if (provider === "serpbase") return { ...(await searchWithSerpBase(query, options)), provider };
|
|
368
|
+
if (provider === "serpapi") return { ...(await searchWithSerpApi(query, options)), provider };
|
|
367
369
|
if (provider === "serper") return { ...(await searchWithSerper(query, options)), provider };
|
|
368
370
|
if (provider === "valyu") return { ...(await searchWithValyu(query, options)), provider };
|
|
369
371
|
if (provider === "xcrawl") return { ...(await searchWithXCrawl(query, options)), provider };
|
|
@@ -412,6 +414,7 @@ async function isResolvedProviderAvailable(provider: ResolvedSearchProvider, opt
|
|
|
412
414
|
if (provider === "mistral") return isMistralAvailable();
|
|
413
415
|
if (provider === "brightdata") return isBrightDataAvailable();
|
|
414
416
|
if (provider === "serpbase") return isSerpBaseAvailable();
|
|
417
|
+
if (provider === "serpapi") return isSerpApiAvailable();
|
|
415
418
|
if (provider === "serper") return isSerperAvailable();
|
|
416
419
|
if (provider === "valyu") return isValyuAvailable();
|
|
417
420
|
if (provider === "xcrawl") return isXcrawlAvailable();
|
|
@@ -451,6 +454,7 @@ function providerLabel(provider: ResolvedSearchProvider): string {
|
|
|
451
454
|
if (provider === "mistral") return "Mistral";
|
|
452
455
|
if (provider === "brightdata") return "Bright Data";
|
|
453
456
|
if (provider === "serpbase") return "SerpBase";
|
|
457
|
+
if (provider === "serpapi") return "SerpApi";
|
|
454
458
|
if (provider === "serper") return "Serper";
|
|
455
459
|
if (provider === "valyu") return "Valyu";
|
|
456
460
|
return provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
@@ -479,7 +483,7 @@ async function searchWithProviders(
|
|
|
479
483
|
: await isResolvedProviderAvailable(provider, options),
|
|
480
484
|
})))).filter((entry) => entry.available).map((entry) => entry.provider);
|
|
481
485
|
if (providers.length === 0) {
|
|
482
|
-
throw new Error("No configured search provider available for provider \"all\". Parallel MCP, DuckDuckGo, Kimi, AnySearch, xAI, Mistral, Bright Data, SerpBase, Serper, Valyu, and XCrawl are excluded.");
|
|
486
|
+
throw new Error("No configured search provider available for provider \"all\". Parallel MCP, DuckDuckGo, Kimi, AnySearch, xAI, Mistral, Bright Data, SerpBase, SerpApi, Serper, Valyu, and XCrawl are excluded.");
|
|
483
487
|
}
|
|
484
488
|
|
|
485
489
|
const settled = await Promise.allSettled(
|
|
@@ -772,7 +776,7 @@ export async function search(query: string, options: FullSearchOptions = {}): Pr
|
|
|
772
776
|
" 3. Set OPENAI_API_KEY, BRAVE_API_KEY, PARALLEL_API_KEY, TINYFISH_API_KEY, SEARCH1API_KEY, SEARCHINFINITY_API_KEY, QUERIT_API_KEY, TAVILY_API_KEY, FIRECRAWL_BASE_URL, JINA_API_KEY, SERPDIVE_API_KEY, KAGI_API_KEY, BOCHA_API_KEY, OLLAMA_API_KEY, SEARXNG_BASE_URL, EXA_API_KEY, PERPLEXITY_API_KEY, GEMINI_API_KEY, or CLOUDFLARE_API_KEY env vars\n" +
|
|
773
777
|
" 4. Set GOOGLE_GEMINI_BASE_URL with CLOUDFLARE_API_KEY for Cloudflare AI Gateway routing\n" +
|
|
774
778
|
" 5. Sign into gemini.google.com in a supported Chromium-based browser\n" +
|
|
775
|
-
" 6. Explicitly select provider: \"anysearch\" for anonymous AnySearch, \"xcrawl\" for XCrawl, \"xai\" for Grok, \"mistral\" for Mistral Conversations web search, \"brightdata\" with brightdataSerpZone for paid Bright Data SERP, \"serpbase\" or \"serper\" for Google SERP, or \"valyu\" for research search"
|
|
779
|
+
" 6. Explicitly select provider: \"anysearch\" for anonymous AnySearch, \"xcrawl\" for XCrawl, \"xai\" for Grok, \"mistral\" for Mistral Conversations web search, \"brightdata\" with brightdataSerpZone for paid Bright Data SERP, \"serpbase\", \"serpapi\", or \"serper\" for Google SERP, or \"valyu\" for research search"
|
|
776
780
|
);
|
|
777
781
|
}
|
|
778
782
|
|
|
@@ -2,7 +2,7 @@ import type { AgentToolResult, ExtensionAPI, ExtensionContext } from "@earendil-
|
|
|
2
2
|
import { Box, Text, truncateToWidth, type KeyId } from "@earendil-works/pi-tui";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
4
|
import pLimit from "p-limit";
|
|
5
|
-
import {
|
|
5
|
+
import type { ImageContent, TextContent } from "@earendil-works/pi-ai/compat";
|
|
6
6
|
import type { ExtractedContent, ExtractOptions } from "./extract.ts";
|
|
7
7
|
import { normalizeFetchContentParams } from "./fetch-params.ts";
|
|
8
8
|
import { resolveAuthFetchProfile, type AuthFetchProfile } from "./auth-fetch.ts";
|
|
@@ -69,11 +69,13 @@ import { isMistralAvailable } from "./mistral-search.ts";
|
|
|
69
69
|
import { isKimiSearchAvailable } from "./kimi-search.ts";
|
|
70
70
|
import { isBrightDataAvailable } from "./brightdata.ts";
|
|
71
71
|
import { isSerpBaseAvailable } from "./serpbase.ts";
|
|
72
|
+
import { isSerpApiAvailable } from "./serpapi.ts";
|
|
72
73
|
import { isSerperAvailable } from "./serper.ts";
|
|
73
74
|
import { isValyuAvailable } from "./valyu.ts";
|
|
74
75
|
import { isXcrawlAvailable } from "./xcrawl.ts";
|
|
75
76
|
import { buildSearchErrorPlan, type SearchErrorDetails, type SearchErrorPlan } from "./render-search-error.ts";
|
|
76
77
|
import { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns, splitThinkingSuffix } from "./summary-model-scope.ts";
|
|
78
|
+
import { registerCuratorRunLifecycle, resolveWebSearchWorkflow, type WebSearchWorkflow } from "./curator-run.ts";
|
|
77
79
|
import {
|
|
78
80
|
buildResearchArtifact,
|
|
79
81
|
withClaimAssessment,
|
|
@@ -83,6 +85,16 @@ import {
|
|
|
83
85
|
type ResearchArtifact,
|
|
84
86
|
} from "./source-check.ts";
|
|
85
87
|
|
|
88
|
+
// Match pi-ai's StringEnum without loading its compat barrel during registration.
|
|
89
|
+
function StringEnum<T extends string[]>(values: T, options?: { description?: string; default?: T[number] }) {
|
|
90
|
+
return Type.Unsafe<T[number]>({
|
|
91
|
+
type: "string",
|
|
92
|
+
enum: values,
|
|
93
|
+
...(options?.description && { description: options.description }),
|
|
94
|
+
...(options?.default && { default: options.default }),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
86
98
|
type ExtensionTheme = ExtensionContext["ui"]["theme"];
|
|
87
99
|
|
|
88
100
|
const WEB_SEARCH_CONFIG_PATH = getWebSearchConfigPath();
|
|
@@ -139,6 +151,7 @@ interface WebSearchConfig {
|
|
|
139
151
|
kagiApiKey?: unknown;
|
|
140
152
|
ollamaApiKey?: unknown;
|
|
141
153
|
serpbaseApiKey?: unknown;
|
|
154
|
+
serpapiApiKey?: unknown;
|
|
142
155
|
serperApiKey?: unknown;
|
|
143
156
|
tinyfishApiKey?: unknown;
|
|
144
157
|
valyuApiKey?: unknown;
|
|
@@ -170,7 +183,6 @@ interface WebSearchConfig {
|
|
|
170
183
|
};
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
type WebSearchWorkflow = "none" | "summary-review" | "auto-summary";
|
|
174
186
|
type CuratorWorkflow = "summary-review";
|
|
175
187
|
export type CuratorProvider = Exclude<SearchProvider, "auto">;
|
|
176
188
|
type SummaryWorkflow = "summary-review" | "auto-summary";
|
|
@@ -347,14 +359,6 @@ function normalizeCuratorTimeoutSeconds(value: unknown): number | undefined {
|
|
|
347
359
|
return Math.min(normalized, MAX_CURATOR_TIMEOUT_SECONDS);
|
|
348
360
|
}
|
|
349
361
|
|
|
350
|
-
function resolveWorkflow(input: unknown, hasUI: boolean): WebSearchWorkflow {
|
|
351
|
-
const normalized = typeof input === "string" ? input.trim().toLowerCase() : "";
|
|
352
|
-
if (normalized === "auto-summary") return "auto-summary";
|
|
353
|
-
if (!hasUI) return "none";
|
|
354
|
-
if (normalized === "none") return "none";
|
|
355
|
-
return "summary-review";
|
|
356
|
-
}
|
|
357
|
-
|
|
358
362
|
function normalizeQueryList(queryList: unknown[]): string[] {
|
|
359
363
|
const normalized: string[] = [];
|
|
360
364
|
for (const query of queryList) {
|
|
@@ -445,6 +449,7 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
|
|
|
445
449
|
mistral: isMistralAvailable(),
|
|
446
450
|
brightdata: isBrightDataAvailable(),
|
|
447
451
|
serpbase: isSerpBaseAvailable(),
|
|
452
|
+
serpapi: isSerpApiAvailable(),
|
|
448
453
|
serper: isSerperAvailable(),
|
|
449
454
|
valyu: isValyuAvailable(),
|
|
450
455
|
};
|
|
@@ -1058,6 +1063,7 @@ function handleSessionChange(ctx: ExtensionContext): void {
|
|
|
1058
1063
|
|
|
1059
1064
|
export default function (pi: ExtensionAPI) {
|
|
1060
1065
|
const initConfig = loadConfigForExtensionInit();
|
|
1066
|
+
const curatorRunState = registerCuratorRunLifecycle(pi);
|
|
1061
1067
|
installGlobalProxyFetch();
|
|
1062
1068
|
const toolNames = resolveToolNames(initConfig);
|
|
1063
1069
|
const webSearchEnabled = isToolEnabled(initConfig, "webSearch");
|
|
@@ -1559,6 +1565,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1559
1565
|
},
|
|
1560
1566
|
onSubmit(payload) {
|
|
1561
1567
|
if (pendingCurates.get(callId) !== pc) return;
|
|
1568
|
+
if (payload.autoApproveRemainingSearches) curatorRunState.approveRemainingSearches();
|
|
1562
1569
|
searchAbort.abort();
|
|
1563
1570
|
const filtered = payload.selectedQueryIndices.length > 0
|
|
1564
1571
|
? filterByQueryIndices(payload.selectedQueryIndices, pc.searchResults)
|
|
@@ -1783,7 +1790,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1783
1790
|
name: toolNames.webSearch,
|
|
1784
1791
|
label: "Web Search",
|
|
1785
1792
|
description:
|
|
1786
|
-
`Search the web using OpenAI, Brave, Parallel, Parallel MCP, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, or Serper. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, and Serper. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; Kimi search uses a Kimi Code Plan authenticated through /login kimi-coding; xAI search uses a SuperGrok/X Premium subscription or xAI API key; Mistral search uses a Mistral API key. Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, and Serper are available only when explicitly selected. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation or "auto-summary" for a model-generated summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it. Without a configured provider, SearXNG is preferred first for local/private search. When the active Pi model is openai-codex, Codex-backed OpenAI search is preferred next. Otherwise Exa is preferred before OpenAI, then Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web.`,
|
|
1793
|
+
`Search the web using OpenAI, Brave, Parallel, Parallel MCP, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, or Serper. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, and Serper. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; Kimi search uses a Kimi Code Plan authenticated through /login kimi-coding; xAI search uses a SuperGrok/X Premium subscription or xAI API key; Mistral search uses a Mistral API key. Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, and Serper are available only when explicitly selected. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation or "auto-summary" for a model-generated summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it. Without a configured provider, SearXNG is preferred first for local/private search. When the active Pi model is openai-codex, Codex-backed OpenAI search is preferred next. Otherwise Exa is preferred before OpenAI, then Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web.`,
|
|
1787
1794
|
promptSnippet:
|
|
1788
1795
|
"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.",
|
|
1789
1796
|
parameters: Type.Object({
|
|
@@ -1795,14 +1802,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
1795
1802
|
StringEnum(["day", "week", "month", "year"], { description: "Filter by recency" }),
|
|
1796
1803
|
),
|
|
1797
1804
|
domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
|
|
1798
|
-
provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, and Serper, omit this field to use the configured provider, or use auto when none is configured")),
|
|
1805
|
+
provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, and Serper, omit this field to use the configured provider, or use auto when none is configured")),
|
|
1799
1806
|
workflow: Type.Optional(
|
|
1800
1807
|
StringEnum(["none", "summary-review", "auto-summary"], {
|
|
1801
1808
|
description: "Search workflow mode: none = no curator, summary-review = open curator with auto summary draft (default), auto-summary = generate summary without opening curator",
|
|
1802
1809
|
}),
|
|
1803
1810
|
),
|
|
1804
1811
|
proxy: Type.Optional(Type.String({
|
|
1805
|
-
description: "http(s) proxy URL (e.g. http://host:port) used for every outbound request in this call (search APIs and content fetches). Node fetch ignores HTTP(S)_PROXY env vars, so set this (or `proxy` in web-search.json) when direct access is blocked; empty string forces direct access.",
|
|
1812
|
+
description: "http(s) or socks proxy URL (e.g. http://host:port or socks5h://host:port) used for every outbound request in this call (search APIs and content fetches). Node fetch ignores HTTP(S)_PROXY env vars, so set this (or `proxy` in web-search.json) when direct access is blocked; empty string forces direct access.",
|
|
1806
1813
|
})),
|
|
1807
1814
|
}),
|
|
1808
1815
|
|
|
@@ -1813,7 +1820,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1813
1820
|
: (params.query !== undefined ? expandQueryString(params.query) : []);
|
|
1814
1821
|
const queryList = normalizeQueryList(rawQueryList);
|
|
1815
1822
|
const configWorkflow = loadConfigForExtensionInit().workflow;
|
|
1816
|
-
const workflow =
|
|
1823
|
+
const workflow = curatorRunState.resolve(params.workflow, configWorkflow, ctx?.hasUI !== false);
|
|
1817
1824
|
const shouldCurate = workflow === "summary-review";
|
|
1818
1825
|
const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
|
|
1819
1826
|
|
|
@@ -2389,9 +2396,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
2389
2396
|
fetchContent: Type.Optional(Type.Boolean({ description: "Fetch up to 5 result pages for exact passage extraction." })),
|
|
2390
2397
|
recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency." })),
|
|
2391
2398
|
domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains; prefix with - to exclude." })),
|
|
2392
|
-
provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; all searches every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, and Serper")),
|
|
2399
|
+
provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; all searches every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, and Serper")),
|
|
2393
2400
|
proxy: Type.Optional(Type.String({
|
|
2394
|
-
description: "http(s) proxy URL (e.g. http://host:port) used for every outbound request in this call (search APIs and result-page fetches). Empty string forces direct access.",
|
|
2401
|
+
description: "http(s) or socks proxy URL (e.g. http://host:port or socks5h://host:port) used for every outbound request in this call (search APIs and result-page fetches). Empty string forces direct access.",
|
|
2395
2402
|
})),
|
|
2396
2403
|
}),
|
|
2397
2404
|
async execute(_callId, params, signal, _onUpdate, ctx) {
|
|
@@ -2512,7 +2519,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2512
2519
|
description: "Opt into an authFetch profile for local browser-cookie fetching. Use a profile name, or true only when exactly one profile exists.",
|
|
2513
2520
|
})),
|
|
2514
2521
|
proxy: Type.Optional(Type.String({
|
|
2515
|
-
description: "http(s) proxy URL (e.g. http://host:port) used for this fetch. Needed when the target is unreachable directly; localhost and NO_PROXY hosts always bypass the proxy. Empty string forces direct access.",
|
|
2522
|
+
description: "http(s) or socks proxy URL (e.g. http://host:port or socks5h://host:port) used for this fetch. Needed when the target is unreachable directly; localhost and NO_PROXY hosts always bypass the proxy. Empty string forces direct access.",
|
|
2516
2523
|
})),
|
|
2517
2524
|
}),
|
|
2518
2525
|
|
|
@@ -3424,7 +3431,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3424
3431
|
|
|
3425
3432
|
let newWorkflow: WebSearchWorkflow;
|
|
3426
3433
|
if (arg.length === 0) {
|
|
3427
|
-
const current =
|
|
3434
|
+
const current = resolveWebSearchWorkflow(loadConfigForExtensionInit().workflow, true);
|
|
3428
3435
|
newWorkflow = current === "none" ? "summary-review" : "none";
|
|
3429
3436
|
} else if (arg === "on") {
|
|
3430
3437
|
newWorkflow = "summary-review";
|