@herbertgao/pi-extensions 2026.8.6 → 2026.8.8
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 +8 -6
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +320 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
- package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
- package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +5 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +140 -97
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
- package/node_modules/@narumitw/pi-btw/README.md +120 -111
- package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
- package/node_modules/@narumitw/pi-btw/package.json +10 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
- package/node_modules/pi-lens/CHANGELOG.md +999 -0
- package/node_modules/pi-lens/README.md +117 -3
- package/node_modules/pi-lens/config/ruff/core.toml +42 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
- package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
- package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
- package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
- package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
- package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
- package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
- package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
- package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
- package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
- package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
- package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
- package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
- package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
- package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
- package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
- package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
- package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
- package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
- package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
- package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +61407 -48577
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/docs/agent-guide.md +6 -1
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +19 -5
- package/node_modules/pi-lens/docs/globalconfig.md +3 -0
- package/node_modules/pi-lens/docs/language-coverage.md +71 -5
- package/node_modules/pi-lens/docs/mcp.md +2 -2
- package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
- package/node_modules/pi-lens/docs/settings.md +19 -1
- package/node_modules/pi-lens/docs/usage.md +1 -0
- package/node_modules/pi-lens/package.json +27 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
- package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
- package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
- package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
- package/node_modules/pi-mcp-adapter/README.md +47 -5
- package/node_modules/pi-mcp-adapter/cli.js +163 -1
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +25 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
- package/node_modules/pi-mcp-adapter/index.ts +219 -32
- package/node_modules/pi-mcp-adapter/init.ts +36 -4
- package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
- package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +3 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
- package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
- package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +2 -0
- package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
- package/node_modules/pi-mcp-adapter/utils.ts +70 -0
- package/node_modules/pi-web-access/CHANGELOG.md +37 -0
- package/node_modules/pi-web-access/README.md +32 -13
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
- package/node_modules/pi-web-access/curator-page.ts +6 -3
- package/node_modules/pi-web-access/curator-server.ts +4 -1
- package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +40 -5
- package/node_modules/pi-web-access/firecrawl.ts +18 -2
- package/node_modules/pi-web-access/gemini-search.ts +16 -4
- package/node_modules/pi-web-access/github-extract.ts +65 -36
- package/node_modules/pi-web-access/index.ts +60 -61
- package/node_modules/pi-web-access/openai-search.ts +17 -6
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/page-query.ts +4 -2
- package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
- package/node_modules/pi-web-access/parallel.ts +2 -2
- package/node_modules/pi-web-access/pdf-extract.ts +18 -5
- package/node_modules/pi-web-access/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +44 -22
- package/node_modules/pi-web-access/tavily.ts +16 -4
- package/node_modules/pi-web-access/utils.ts +87 -0
- package/node_modules/pi-web-access/valyu.ts +199 -0
- package/package.json +14 -14
|
@@ -9,6 +9,7 @@ import { isExaAvailable, searchWithExa } from "./exa.ts";
|
|
|
9
9
|
import { isBraveAvailable, searchWithBrave } from "./brave.ts";
|
|
10
10
|
import { isOpenAISearchAvailable, searchWithOpenAI } from "./openai-search.ts";
|
|
11
11
|
import { isParallelAvailable, searchWithParallel } from "./parallel.ts";
|
|
12
|
+
import { isParallelMcpAvailable, searchWithParallelMcp } from "./parallel-mcp.ts";
|
|
12
13
|
import { isTinyFishAvailable, searchWithTinyFish } from "./tinyfish.ts";
|
|
13
14
|
import { isSearch1APIAvailable, searchWithSearch1API } from "./search1api.ts";
|
|
14
15
|
import { isSearchinfinityAvailable, searchWithSearchinfinity } from "./searchinfinity.ts";
|
|
@@ -26,9 +27,11 @@ import { isAnySearchAvailable, searchWithAnySearch } from "./anysearch.ts";
|
|
|
26
27
|
import { isXaiSearchAvailable, searchWithXai } from "./xai-search.ts";
|
|
27
28
|
import { isBrightDataAvailable, searchWithBrightData } from "./brightdata.ts";
|
|
28
29
|
import { isSerpBaseAvailable, searchWithSerpBase } from "./serpbase.ts";
|
|
30
|
+
import { isSerperAvailable, searchWithSerper } from "./serper.ts";
|
|
31
|
+
import { isValyuAvailable, searchWithValyu } from "./valyu.ts";
|
|
29
32
|
import { getWebSearchConfigPath } from "./utils.ts";
|
|
30
33
|
|
|
31
|
-
export const RESOLVED_SEARCH_PROVIDERS = ["openai", "brave", "parallel", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "searxng", "duckduckgo", "perplexity", "gemini", "exa", "serpdive", "kagi", "ollama", "anysearch", "xai", "brightdata", "serpbase", "bocha"] as const;
|
|
34
|
+
export const RESOLVED_SEARCH_PROVIDERS = ["openai", "brave", "parallel", "parallel-mcp", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "searxng", "duckduckgo", "perplexity", "gemini", "exa", "serpdive", "kagi", "ollama", "anysearch", "xai", "brightdata", "serpbase", "serper", "valyu", "bocha"] as const;
|
|
32
35
|
export const SEARCH_PROVIDERS = ["auto", "all", ...RESOLVED_SEARCH_PROVIDERS] as const;
|
|
33
36
|
|
|
34
37
|
export type ResolvedSearchProvider = typeof RESOLVED_SEARCH_PROVIDERS[number];
|
|
@@ -90,7 +93,7 @@ export interface AttributedSearchResponse extends SearchResponse {
|
|
|
90
93
|
|
|
91
94
|
const CONFIG_PATH = getWebSearchConfigPath();
|
|
92
95
|
const DEFAULT_SEARCH_MODEL = "gemini-3.6-flash";
|
|
93
|
-
// Explicit-only providers (DuckDuckGo, AnySearch, xAI, Bright Data, SerpBase) are deliberately absent:
|
|
96
|
+
// Explicit-only providers (Parallel MCP, DuckDuckGo, AnySearch, xAI, Bright Data, SerpBase, Serper, Valyu) are deliberately absent:
|
|
94
97
|
// `all` must never fan out to an opt-in or paid provider without the user asking for it.
|
|
95
98
|
const ALL_SEARCH_PROVIDERS: ResolvedSearchProvider[] = ["searxng", "openai", "exa", "brave", "parallel", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "serpdive", "kagi", "ollama", "perplexity", "gemini", "bocha"];
|
|
96
99
|
const VALID_ROUTING_KINDS = ["transient", "quota", "network", "invalid-response"] as const;
|
|
@@ -300,6 +303,7 @@ async function searchWithResolvedProvider(
|
|
|
300
303
|
}
|
|
301
304
|
if (provider === "brave") return { ...(await searchWithBrave(query, options)), provider };
|
|
302
305
|
if (provider === "parallel") return { ...(await searchWithParallel(query, options)), provider };
|
|
306
|
+
if (provider === "parallel-mcp") return { ...(await searchWithParallelMcp(query, options)), provider };
|
|
303
307
|
if (provider === "tinyfish") return { ...(await searchWithTinyFish(query, options)), provider };
|
|
304
308
|
if (provider === "search1api") return { ...(await searchWithSearch1API(query, options)), provider };
|
|
305
309
|
if (provider === "searchinfinity") return { ...(await searchWithSearchinfinity(query, options)), provider };
|
|
@@ -315,6 +319,8 @@ async function searchWithResolvedProvider(
|
|
|
315
319
|
if (provider === "xai") return { ...(await searchWithXai(query, options, options.extensionContext)), provider };
|
|
316
320
|
if (provider === "brightdata") return { ...(await searchWithBrightData(query, options)), provider };
|
|
317
321
|
if (provider === "serpbase") return { ...(await searchWithSerpBase(query, options)), provider };
|
|
322
|
+
if (provider === "serper") return { ...(await searchWithSerper(query, options)), provider };
|
|
323
|
+
if (provider === "valyu") return { ...(await searchWithValyu(query, options)), provider };
|
|
318
324
|
if (provider === "perplexity") return { ...(await searchWithPerplexity(query, options)), provider };
|
|
319
325
|
if (provider === "searxng") return { ...(await searchWithSearXNG(query, options)), provider };
|
|
320
326
|
if (provider === "duckduckgo") return { ...(await searchWithDuckDuckGo(query, options)), provider };
|
|
@@ -337,6 +343,7 @@ async function isResolvedProviderAvailable(provider: ResolvedSearchProvider, opt
|
|
|
337
343
|
if (provider === "openai") return isOpenAISearchAvailable(options.extensionContext);
|
|
338
344
|
if (provider === "brave") return isBraveAvailable();
|
|
339
345
|
if (provider === "parallel") return isParallelAvailable();
|
|
346
|
+
if (provider === "parallel-mcp") return isParallelMcpAvailable();
|
|
340
347
|
if (provider === "tinyfish") return isTinyFishAvailable();
|
|
341
348
|
if (provider === "search1api") return isSearch1APIAvailable();
|
|
342
349
|
if (provider === "searchinfinity") return isSearchinfinityAvailable();
|
|
@@ -352,6 +359,8 @@ async function isResolvedProviderAvailable(provider: ResolvedSearchProvider, opt
|
|
|
352
359
|
if (provider === "xai") return isXaiSearchAvailable(options.extensionContext);
|
|
353
360
|
if (provider === "brightdata") return isBrightDataAvailable();
|
|
354
361
|
if (provider === "serpbase") return isSerpBaseAvailable();
|
|
362
|
+
if (provider === "serper") return isSerperAvailable();
|
|
363
|
+
if (provider === "valyu") return isValyuAvailable();
|
|
355
364
|
if (provider === "perplexity") return isPerplexityAvailable();
|
|
356
365
|
if (provider === "searxng") return isSearXNGAvailable();
|
|
357
366
|
if (provider === "duckduckgo") return isDuckDuckGoAvailable();
|
|
@@ -361,6 +370,7 @@ async function isResolvedProviderAvailable(provider: ResolvedSearchProvider, opt
|
|
|
361
370
|
|
|
362
371
|
function providerLabel(provider: ResolvedSearchProvider): string {
|
|
363
372
|
if (provider === "openai") return "OpenAI";
|
|
373
|
+
if (provider === "parallel-mcp") return "Parallel MCP";
|
|
364
374
|
if (provider === "tinyfish") return "TinyFish";
|
|
365
375
|
if (provider === "search1api") return "Search1API";
|
|
366
376
|
if (provider === "searchinfinity") return "Searchinfinity";
|
|
@@ -375,6 +385,8 @@ function providerLabel(provider: ResolvedSearchProvider): string {
|
|
|
375
385
|
if (provider === "xai") return "xAI";
|
|
376
386
|
if (provider === "brightdata") return "Bright Data";
|
|
377
387
|
if (provider === "serpbase") return "SerpBase";
|
|
388
|
+
if (provider === "serper") return "Serper";
|
|
389
|
+
if (provider === "valyu") return "Valyu";
|
|
378
390
|
return provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
379
391
|
}
|
|
380
392
|
|
|
@@ -401,7 +413,7 @@ async function searchWithProviders(
|
|
|
401
413
|
: await isResolvedProviderAvailable(provider, options),
|
|
402
414
|
})))).filter((entry) => entry.available).map((entry) => entry.provider);
|
|
403
415
|
if (providers.length === 0) {
|
|
404
|
-
throw new Error("No configured search provider available for provider \"all\". AnySearch, xAI, Bright Data, and
|
|
416
|
+
throw new Error("No configured search provider available for provider \"all\". Parallel MCP, AnySearch, xAI, Bright Data, SerpBase, Serper, and Valyu are excluded.");
|
|
405
417
|
}
|
|
406
418
|
|
|
407
419
|
const settled = await Promise.allSettled(
|
|
@@ -693,7 +705,7 @@ export async function search(query: string, options: FullSearchOptions = {}): Pr
|
|
|
693
705
|
" 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" +
|
|
694
706
|
" 4. Set GOOGLE_GEMINI_BASE_URL with CLOUDFLARE_API_KEY for Cloudflare AI Gateway routing\n" +
|
|
695
707
|
" 5. Sign into gemini.google.com in a supported Chromium-based browser\n" +
|
|
696
|
-
" 6. Explicitly select provider: \"anysearch\" for anonymous AnySearch, \"xai\" for Grok, \"brightdata\" with brightdataSerpZone for paid Bright Data SERP,
|
|
708
|
+
" 6. Explicitly select provider: \"anysearch\" for anonymous AnySearch, \"xai\" for Grok, \"brightdata\" with brightdataSerpZone for paid Bright Data SERP, \"serpbase\" or \"serper\" for Google SERP, or \"valyu\" for research search"
|
|
697
709
|
);
|
|
698
710
|
}
|
|
699
711
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, rmSync, statSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { execFile, spawn, type ChildProcess } from "node:child_process";
|
|
3
|
-
import {
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { basename, dirname, extname, join, resolve as resolvePath, sep as pathSep } from "node:path";
|
|
4
5
|
import { activityMonitor } from "./activity.ts";
|
|
5
6
|
import type { ExtractedContent } from "./extract.ts";
|
|
6
7
|
import { checkGhAvailable, checkRepoSize, fetchViaApi, showGhHint } from "./github-api.ts";
|
|
@@ -39,10 +40,15 @@ export interface GitHubUrlInfo {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
interface CachedClone {
|
|
42
|
-
|
|
43
|
+
destination: CloneDestination;
|
|
43
44
|
clonePromise: Promise<string | null>;
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
interface CloneDestination {
|
|
48
|
+
rootPath: string;
|
|
49
|
+
localPath: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
interface GitHubCloneConfig {
|
|
47
53
|
enabled: boolean;
|
|
48
54
|
maxRepoSizeMB: number;
|
|
@@ -137,20 +143,21 @@ export function parseGitHubUrl(url: string): GitHubUrlInfo | null {
|
|
|
137
143
|
const host = parsed.hostname.toLowerCase();
|
|
138
144
|
if (host !== "github.com" && host !== "www.github.com") return null;
|
|
139
145
|
|
|
140
|
-
const segments =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
});
|
|
146
|
+
const segments: string[] = [];
|
|
147
|
+
for (const segment of parsed.pathname.split("/").filter(Boolean)) {
|
|
148
|
+
try {
|
|
149
|
+
segments.push(decodeURIComponent(segment));
|
|
150
|
+
} catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
150
154
|
if (segments.length < 2) return null;
|
|
151
155
|
|
|
152
156
|
const owner = segments[0];
|
|
153
157
|
const repo = segments[1].replace(/\.git$/, "");
|
|
158
|
+
if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(owner)) return null;
|
|
159
|
+
if (owner.includes("--")) return null;
|
|
160
|
+
if (!/^[A-Za-z0-9._-]{1,100}$/.test(repo) || repo === "." || repo === "..") return null;
|
|
154
161
|
|
|
155
162
|
if (NON_CODE_SEGMENTS.has(segments[2]?.toLowerCase())) return null;
|
|
156
163
|
|
|
@@ -163,6 +170,7 @@ export function parseGitHubUrl(url: string): GitHubUrlInfo | null {
|
|
|
163
170
|
if (segments.length < 4) return null;
|
|
164
171
|
|
|
165
172
|
const ref = segments[3];
|
|
173
|
+
if (ref.length === 0 || ref.length > 1024 || /[\0-\x1f\x7f]/.test(ref)) return null;
|
|
166
174
|
const refIsFullSha = /^[0-9a-f]{40}$/.test(ref);
|
|
167
175
|
const pathParts = segments.slice(4);
|
|
168
176
|
const path = pathParts.length > 0 ? pathParts.join("/") : "";
|
|
@@ -181,9 +189,32 @@ function cacheKey(owner: string, repo: string, ref?: string): string {
|
|
|
181
189
|
return ref ? `${owner}/${repo}@${ref}` : `${owner}/${repo}`;
|
|
182
190
|
}
|
|
183
191
|
|
|
184
|
-
function
|
|
185
|
-
|
|
186
|
-
|
|
192
|
+
function cloneDestination(config: GitHubCloneConfig, owner: string, repo: string, ref?: string): CloneDestination | null {
|
|
193
|
+
try {
|
|
194
|
+
mkdirSync(resolvePath(config.clonePath), { recursive: true });
|
|
195
|
+
const rootPath = realpathSync(resolvePath(config.clonePath));
|
|
196
|
+
const digest = createHash("sha256").update(JSON.stringify([owner, repo, ref ?? null])).digest("hex");
|
|
197
|
+
const localPath = resolvePath(rootPath, digest);
|
|
198
|
+
if (dirname(localPath) !== rootPath) return null;
|
|
199
|
+
return { rootPath, localPath };
|
|
200
|
+
} catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function removeCloneDestination(destination: CloneDestination): boolean {
|
|
206
|
+
const rootPath = resolvePath(destination.rootPath);
|
|
207
|
+
const localPath = resolvePath(destination.localPath);
|
|
208
|
+
if (dirname(localPath) !== rootPath || !/^[0-9a-f]{64}$/.test(basename(localPath))) return false;
|
|
209
|
+
try {
|
|
210
|
+
const entry = lstatSync(localPath);
|
|
211
|
+
if (entry.isSymbolicLink()) unlinkSync(localPath);
|
|
212
|
+
else rmSync(localPath, { recursive: true, force: true });
|
|
213
|
+
return true;
|
|
214
|
+
} catch (err) {
|
|
215
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") return true;
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
187
218
|
}
|
|
188
219
|
|
|
189
220
|
const PROCESS_KILL_GRACE_MS = 3000;
|
|
@@ -225,8 +256,9 @@ function terminateProcessTree(child: ChildProcess): void {
|
|
|
225
256
|
forceKill.unref();
|
|
226
257
|
}
|
|
227
258
|
|
|
228
|
-
function execClone(args: string[],
|
|
259
|
+
function execClone(args: string[], destination: CloneDestination, timeoutMs: number, signal?: AbortSignal): Promise<string | null> {
|
|
229
260
|
return new Promise((resolve) => {
|
|
261
|
+
const { localPath } = destination;
|
|
230
262
|
let settled = false;
|
|
231
263
|
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
232
264
|
let onAbort: (() => void) | undefined;
|
|
@@ -238,10 +270,7 @@ function execClone(args: string[], localPath: string, timeoutMs: number, signal?
|
|
|
238
270
|
if (signal && onAbort) signal.removeEventListener("abort", onAbort);
|
|
239
271
|
|
|
240
272
|
if (!success) {
|
|
241
|
-
|
|
242
|
-
rmSync(localPath, { recursive: true, force: true });
|
|
243
|
-
} catch {
|
|
244
|
-
}
|
|
273
|
+
removeCloneDestination(destination);
|
|
245
274
|
resolve(null);
|
|
246
275
|
return;
|
|
247
276
|
}
|
|
@@ -282,14 +311,11 @@ async function cloneRepo(
|
|
|
282
311
|
repo: string,
|
|
283
312
|
ref: string | undefined,
|
|
284
313
|
config: GitHubCloneConfig,
|
|
314
|
+
destination: CloneDestination,
|
|
285
315
|
signal?: AbortSignal,
|
|
286
316
|
): Promise<string | null> {
|
|
287
|
-
const localPath =
|
|
288
|
-
|
|
289
|
-
try {
|
|
290
|
-
rmSync(localPath, { recursive: true, force: true });
|
|
291
|
-
} catch {
|
|
292
|
-
}
|
|
317
|
+
const { localPath } = destination;
|
|
318
|
+
if (!removeCloneDestination(destination)) return null;
|
|
293
319
|
|
|
294
320
|
const timeoutMs = config.cloneTimeoutSeconds * 1000;
|
|
295
321
|
const hasGh = await checkGhAvailable();
|
|
@@ -297,7 +323,7 @@ async function cloneRepo(
|
|
|
297
323
|
if (hasGh) {
|
|
298
324
|
const args = ["gh", "repo", "clone", `${owner}/${repo}`, localPath, "--", "--depth", "1", "--single-branch"];
|
|
299
325
|
if (ref) args.push("--branch", ref);
|
|
300
|
-
return execClone(args,
|
|
326
|
+
return execClone(args, destination, timeoutMs, signal);
|
|
301
327
|
}
|
|
302
328
|
|
|
303
329
|
showGhHint();
|
|
@@ -306,7 +332,7 @@ async function cloneRepo(
|
|
|
306
332
|
const args = ["git", "clone", "--depth", "1", "--single-branch"];
|
|
307
333
|
if (ref) args.push("--branch", ref);
|
|
308
334
|
args.push(gitUrl, localPath);
|
|
309
|
-
return execClone(args,
|
|
335
|
+
return execClone(args, destination, timeoutMs, signal);
|
|
310
336
|
}
|
|
311
337
|
|
|
312
338
|
function isBinaryFile(filePath: string): boolean {
|
|
@@ -671,9 +697,15 @@ export async function extractGitHub(
|
|
|
671
697
|
return cachedResult;
|
|
672
698
|
}
|
|
673
699
|
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
700
|
+
const destination = cloneDestination(config, owner, repo, info.ref);
|
|
701
|
+
if (!destination) {
|
|
702
|
+
const apiFallback = await fetchViaApi(url, owner, repo, info);
|
|
703
|
+
if (apiFallback) activityMonitor.logComplete(activityId, 200);
|
|
704
|
+
else activityMonitor.logError(activityId, "invalid clone destination");
|
|
705
|
+
return apiFallback;
|
|
706
|
+
}
|
|
707
|
+
const clonePromise = cloneRepo(owner, repo, info.ref, config, destination, signal);
|
|
708
|
+
cloneCache.set(key, { destination, clonePromise });
|
|
677
709
|
|
|
678
710
|
const result = await clonePromise;
|
|
679
711
|
if (signal?.aborted) {
|
|
@@ -707,10 +739,7 @@ export async function extractGitHub(
|
|
|
707
739
|
|
|
708
740
|
export function clearCloneCache(): void {
|
|
709
741
|
for (const entry of cloneCache.values()) {
|
|
710
|
-
|
|
711
|
-
rmSync(entry.localPath, { recursive: true, force: true });
|
|
712
|
-
} catch {
|
|
713
|
-
}
|
|
742
|
+
removeCloneDestination(entry.destination);
|
|
714
743
|
}
|
|
715
744
|
cloneCache.clear();
|
|
716
745
|
cachedConfig = null;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { AgentToolResult, ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Box, Text, truncateToWidth, type KeyId } from "@earendil-works/pi-tui";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
|
-
import { StringEnum,
|
|
4
|
+
import { StringEnum, type ImageContent, type TextContent } from "@earendil-works/pi-ai/compat";
|
|
5
5
|
import type { ExtractedContent, ExtractOptions } from "./extract.ts";
|
|
6
6
|
import { normalizeFetchContentParams } from "./fetch-params.ts";
|
|
7
7
|
import { resolveAuthFetchProfile, type AuthFetchProfile } from "./auth-fetch.ts";
|
|
8
8
|
import { findContent, type FindMode } from "./content-find.ts";
|
|
9
9
|
import { answerFromPage } from "./page-query.ts";
|
|
10
|
+
import { rewriteSearchQuery } from "./query-rewrite.ts";
|
|
10
11
|
import { clearCloneCache } from "./github-extract.ts";
|
|
11
12
|
import { getConfiguredSearchRouting, normalizeSearchProviderSelection, RESOLVED_SEARCH_PROVIDERS, SEARCH_PROVIDERS, search, type AttributedSearchResponse, type SearchProvider, type SearchProviderSelection, type ResolvedSearchProvider } from "./gemini-search.ts";
|
|
12
13
|
import type { SearchResult } from "./perplexity.ts";
|
|
@@ -33,7 +34,7 @@ import {
|
|
|
33
34
|
type SummaryMeta,
|
|
34
35
|
} from "./summary-review.ts";
|
|
35
36
|
import { randomUUID } from "node:crypto";
|
|
36
|
-
import { execFileSync } from "node:child_process";
|
|
37
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
37
38
|
import { createRequire } from "node:module";
|
|
38
39
|
import { platform } from "node:os";
|
|
39
40
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
@@ -46,6 +47,7 @@ import { isBrowserCookieAccessAllowed } from "./gemini-web-config.ts";
|
|
|
46
47
|
import { isBraveAvailable } from "./brave.ts";
|
|
47
48
|
import { isOpenAISearchAvailable } from "./openai-search.ts";
|
|
48
49
|
import { isParallelAvailable } from "./parallel.ts";
|
|
50
|
+
import { isParallelMcpAvailable } from "./parallel-mcp.ts";
|
|
49
51
|
import { isTinyFishAvailable } from "./tinyfish.ts";
|
|
50
52
|
import { isSearch1APIAvailable } from "./search1api.ts";
|
|
51
53
|
import { isSearchinfinityAvailable } from "./searchinfinity.ts";
|
|
@@ -63,8 +65,10 @@ import { isAnySearchAvailable } from "./anysearch.ts";
|
|
|
63
65
|
import { isXaiSearchAvailable } from "./xai-search.ts";
|
|
64
66
|
import { isBrightDataAvailable } from "./brightdata.ts";
|
|
65
67
|
import { isSerpBaseAvailable } from "./serpbase.ts";
|
|
68
|
+
import { isSerperAvailable } from "./serper.ts";
|
|
69
|
+
import { isValyuAvailable } from "./valyu.ts";
|
|
66
70
|
import { buildSearchErrorPlan, type SearchErrorDetails, type SearchErrorPlan } from "./render-search-error.ts";
|
|
67
|
-
import { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns } from "./summary-model-scope.ts";
|
|
71
|
+
import { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns, splitThinkingSuffix } from "./summary-model-scope.ts";
|
|
68
72
|
import {
|
|
69
73
|
buildResearchArtifact,
|
|
70
74
|
withClaimAssessment,
|
|
@@ -117,7 +121,9 @@ interface WebSearchConfig {
|
|
|
117
121
|
kagiApiKey?: unknown;
|
|
118
122
|
ollamaApiKey?: unknown;
|
|
119
123
|
serpbaseApiKey?: unknown;
|
|
124
|
+
serperApiKey?: unknown;
|
|
120
125
|
tinyfishApiKey?: unknown;
|
|
126
|
+
valyuApiKey?: unknown;
|
|
121
127
|
xaiApiKey?: unknown;
|
|
122
128
|
provider?: unknown;
|
|
123
129
|
searchProvider?: unknown;
|
|
@@ -151,6 +157,7 @@ interface ProviderAvailability {
|
|
|
151
157
|
openai: boolean;
|
|
152
158
|
brave: boolean;
|
|
153
159
|
parallel: boolean;
|
|
160
|
+
"parallel-mcp": boolean;
|
|
154
161
|
tinyfish: boolean;
|
|
155
162
|
search1api: boolean;
|
|
156
163
|
searchinfinity: boolean;
|
|
@@ -171,6 +178,8 @@ interface ProviderAvailability {
|
|
|
171
178
|
xai: boolean;
|
|
172
179
|
brightdata: boolean;
|
|
173
180
|
serpbase: boolean;
|
|
181
|
+
serper: boolean;
|
|
182
|
+
valyu: boolean;
|
|
174
183
|
}
|
|
175
184
|
|
|
176
185
|
type WebSearchWorkflow = "none" | "summary-review" | "auto-summary";
|
|
@@ -222,8 +231,6 @@ type ToolNames = {
|
|
|
222
231
|
getSearchContent: string;
|
|
223
232
|
};
|
|
224
233
|
|
|
225
|
-
type ProviderHeaders = Record<string, string | null>;
|
|
226
|
-
|
|
227
234
|
const DEFAULT_TOOL_NAMES: ToolNames = {
|
|
228
235
|
webSearch: "web_search",
|
|
229
236
|
sourceCheck: "source_check",
|
|
@@ -382,6 +389,7 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
|
|
|
382
389
|
openai: await isOpenAISearchAvailable(ctx),
|
|
383
390
|
brave: isBraveAvailable(),
|
|
384
391
|
parallel: isParallelAvailable(),
|
|
392
|
+
"parallel-mcp": isParallelMcpAvailable(),
|
|
385
393
|
tinyfish: isTinyFishAvailable(),
|
|
386
394
|
search1api: isSearch1APIAvailable(),
|
|
387
395
|
searchinfinity: isSearchinfinityAvailable(),
|
|
@@ -402,10 +410,12 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
|
|
|
402
410
|
xai: await isXaiSearchAvailable(ctx),
|
|
403
411
|
brightdata: isBrightDataAvailable(),
|
|
404
412
|
serpbase: isSerpBaseAvailable(),
|
|
413
|
+
serper: isSerperAvailable(),
|
|
414
|
+
valyu: isValyuAvailable(),
|
|
405
415
|
};
|
|
406
416
|
return {
|
|
407
|
-
// DuckDuckGo, AnySearch, xAI, Bright Data, and
|
|
408
|
-
all: Object.entries(providers).some(([provider, available]) => provider !== "duckduckgo" && provider !== "anysearch" && provider !== "xai" && provider !== "brightdata" && provider !== "serpbase" && provider !== "gemini" && available) || geminiApiAvail,
|
|
417
|
+
// Parallel MCP, DuckDuckGo, AnySearch, xAI, Bright Data, SerpBase, Serper, and Valyu are explicit-only, so they never make `all` eligible.
|
|
418
|
+
all: Object.entries(providers).some(([provider, available]) => provider !== "parallel-mcp" && provider !== "duckduckgo" && provider !== "anysearch" && provider !== "xai" && provider !== "brightdata" && provider !== "serpbase" && provider !== "serper" && provider !== "valyu" && provider !== "gemini" && available) || geminiApiAvail,
|
|
409
419
|
...providers,
|
|
410
420
|
};
|
|
411
421
|
}
|
|
@@ -734,11 +744,26 @@ function closeCurator(callId?: string): void {
|
|
|
734
744
|
|
|
735
745
|
async function openInBrowser(pi: ExtensionAPI, url: string): Promise<void> {
|
|
736
746
|
const plat = platform();
|
|
747
|
+
if (plat !== "darwin" && plat !== "win32") {
|
|
748
|
+
await new Promise<void>((resolve, reject) => {
|
|
749
|
+
const child = spawn("xdg-open", [url], { detached: true, stdio: "ignore" });
|
|
750
|
+
const timer = setTimeout(resolve, 100);
|
|
751
|
+
child.once("error", (err) => {
|
|
752
|
+
clearTimeout(timer);
|
|
753
|
+
reject(err);
|
|
754
|
+
});
|
|
755
|
+
child.once("exit", (code) => {
|
|
756
|
+
clearTimeout(timer);
|
|
757
|
+
if (code === 0) resolve();
|
|
758
|
+
else reject(new Error(`Failed to open browser (exit code ${code ?? "unknown"})`));
|
|
759
|
+
});
|
|
760
|
+
child.unref();
|
|
761
|
+
});
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
737
764
|
const result = plat === "darwin"
|
|
738
765
|
? await pi.exec("open", [url])
|
|
739
|
-
:
|
|
740
|
-
? await pi.exec("cmd", ["/c", "start", "", url])
|
|
741
|
-
: await pi.exec("xdg-open", [url]);
|
|
766
|
+
: await pi.exec("cmd", ["/c", "start", "", url]);
|
|
742
767
|
if (result.code !== 0) {
|
|
743
768
|
throw new Error(result.stderr || `Failed to open browser (exit code ${result.code})`);
|
|
744
769
|
}
|
|
@@ -974,10 +999,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
974
999
|
} satisfies StoredSearchData & { type: "fetch"; urls: ExtractedContent[] };
|
|
975
1000
|
pi.appendEntry("web-search-results", storeFetchedContentResult(fetchId, data));
|
|
976
1001
|
const ok = fetched.filter(f => !f.error).length;
|
|
1002
|
+
const availability = ok === fetched.length
|
|
1003
|
+
? "Full page content now available."
|
|
1004
|
+
: ok > 0
|
|
1005
|
+
? "Partial page content now available."
|
|
1006
|
+
: "No page content was fetched. Stored fetch diagnostics are available.";
|
|
977
1007
|
pi.sendMessage(
|
|
978
1008
|
{
|
|
979
1009
|
customType: "web-search-content-ready",
|
|
980
|
-
content: `Content fetched for ${ok}/${fetched.length} URLs [${fetchId}].
|
|
1010
|
+
content: `Content fetched for ${ok}/${fetched.length} URLs [${fetchId}]. ${availability}`,
|
|
981
1011
|
display: true,
|
|
982
1012
|
},
|
|
983
1013
|
{ triggerTurn: true },
|
|
@@ -1088,47 +1118,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
1088
1118
|
};
|
|
1089
1119
|
}
|
|
1090
1120
|
|
|
1091
|
-
async function resolveFirstAvailableModel(
|
|
1092
|
-
ctx: SummaryGenerationContext,
|
|
1093
|
-
candidates: Array<{ provider: string; id: string }>,
|
|
1094
|
-
): Promise<{ model: Model<Api>; apiKey: string; headers?: ProviderHeaders }> {
|
|
1095
|
-
const enabledModelPatterns = loadEnabledModelPatterns(ctx);
|
|
1096
|
-
for (const { provider, id } of candidates) {
|
|
1097
|
-
const model = findModelWithProviderRouting(ctx.modelRegistry, provider, id);
|
|
1098
|
-
if (!model || !modelMatchesEnabledPatterns(model, enabledModelPatterns)) continue;
|
|
1099
|
-
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
1100
|
-
if (auth.ok && auth.apiKey) return { model, apiKey: auth.apiKey, headers: auth.headers };
|
|
1101
|
-
}
|
|
1102
|
-
throw new Error(`No enabled model available: ${candidates.map(c => `${c.provider}/${c.id}`).join(", ")}`);
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
async function rewriteSearchQuery(query: string, ctx: SummaryGenerationContext, signal: AbortSignal): Promise<string> {
|
|
1106
|
-
const { model, apiKey, headers } = await resolveFirstAvailableModel(ctx, [
|
|
1107
|
-
{ provider: "anthropic", id: "claude-haiku-4-5" },
|
|
1108
|
-
{ provider: "google", id: "gemini-3.6-flash" },
|
|
1109
|
-
{ provider: "openai", id: "gpt-4.1-mini" },
|
|
1110
|
-
]);
|
|
1111
|
-
const response = await complete(
|
|
1112
|
-
model,
|
|
1113
|
-
{
|
|
1114
|
-
messages: [{
|
|
1115
|
-
role: "user",
|
|
1116
|
-
content: [{ type: "text", text: `Rewrite this web search query to get better, more specific results. Add relevant year qualifiers, precise technical terms, and specificity. Return ONLY the improved query text, nothing else.\n\nQuery: ${query}` }],
|
|
1117
|
-
timestamp: Date.now(),
|
|
1118
|
-
}],
|
|
1119
|
-
},
|
|
1120
|
-
{ apiKey, headers, signal },
|
|
1121
|
-
);
|
|
1122
|
-
if (response.stopReason === "aborted") throw new Error("Aborted");
|
|
1123
|
-
const contentParts = Array.isArray(response.content) ? response.content : [];
|
|
1124
|
-
const text = contentParts
|
|
1125
|
-
.map(part => part.type === "text" ? part.text : "")
|
|
1126
|
-
.join("")
|
|
1127
|
-
.trim();
|
|
1128
|
-
if (!text) throw new Error("Rewrite returned empty response");
|
|
1129
|
-
return text;
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
1121
|
async function generateSummaryForSelectedIndices(
|
|
1133
1122
|
selectedQueryIndices: number[],
|
|
1134
1123
|
resultsByIndex: Map<number, QueryResultData>,
|
|
@@ -1211,20 +1200,30 @@ export default function (pi: ExtensionAPI) {
|
|
|
1211
1200
|
const configuredSummaryModel = typeof config.summaryModel === "string" ? config.summaryModel.trim() : "";
|
|
1212
1201
|
const preferredDefaults = [
|
|
1213
1202
|
{ provider: "anthropic", id: "claude-haiku-4-5" },
|
|
1214
|
-
{ provider: "openai-codex", id: "gpt-5.
|
|
1203
|
+
{ provider: "openai-codex", id: "gpt-5.6-luna" },
|
|
1204
|
+
{ provider: "openai-codex", id: "gpt-5.6-terra" },
|
|
1205
|
+
{ provider: "google", id: "gemini-3.6-flash" },
|
|
1206
|
+
{ provider: "openai", id: "gpt-5-mini" },
|
|
1207
|
+
{ provider: "deepseek", id: "deepseek-v4-flash" },
|
|
1215
1208
|
];
|
|
1216
1209
|
|
|
1217
1210
|
const resolveAvailableModelValue = (selector: string): string | null => {
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1211
|
+
const parsed = splitThinkingSuffix(selector);
|
|
1212
|
+
const slashIndex = parsed.value.indexOf("/");
|
|
1213
|
+
if (slashIndex <= 0 || slashIndex >= parsed.value.length - 1) return null;
|
|
1220
1214
|
const model = findModelWithProviderRouting(
|
|
1221
1215
|
summaryContext.modelRegistry,
|
|
1222
|
-
|
|
1223
|
-
|
|
1216
|
+
parsed.value.slice(0, slashIndex),
|
|
1217
|
+
parsed.value.slice(slashIndex + 1),
|
|
1224
1218
|
);
|
|
1225
1219
|
if (!model) return null;
|
|
1226
1220
|
const value = `${model.provider}/${model.id}`;
|
|
1227
|
-
|
|
1221
|
+
if (!availableValues.has(value)) return null;
|
|
1222
|
+
if (selector !== value && !seen.has(selector)) {
|
|
1223
|
+
seen.add(selector);
|
|
1224
|
+
summaryModels.push({ value: selector, label: selector });
|
|
1225
|
+
}
|
|
1226
|
+
return selector;
|
|
1228
1227
|
};
|
|
1229
1228
|
|
|
1230
1229
|
let defaultSummaryModel: string | null = null;
|
|
@@ -1661,7 +1660,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1661
1660
|
name: toolNames.webSearch,
|
|
1662
1661
|
label: "Web Search",
|
|
1663
1662
|
description:
|
|
1664
|
-
`Search the web using OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, AnySearch, xAI, Bright Data, or
|
|
1663
|
+
`Search the web using OpenAI, Brave, Parallel, Parallel MCP, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, AnySearch, Valyu, xAI, Bright Data, SerpBase, or Serper. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except Parallel MCP, DuckDuckGo, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; xAI search uses a SuperGrok/X Premium subscription or xAI API key. Parallel MCP, DuckDuckGo, AnySearch, Valyu, xAI, Bright Data, SerpBase, and Serper are available only when explicitly selected. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation or "auto-summary" for a model-generated summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it. Without a configured provider, auto-selects OpenAI when suitable and available, then Exa, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web. When SearXNG is configured, it is preferred first for local/private search.`,
|
|
1665
1664
|
promptSnippet:
|
|
1666
1665
|
"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.",
|
|
1667
1666
|
parameters: Type.Object({
|
|
@@ -1673,7 +1672,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1673
1672
|
StringEnum(["day", "week", "month", "year"], { description: "Filter by recency" }),
|
|
1674
1673
|
),
|
|
1675
1674
|
domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
|
|
1676
|
-
provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, and
|
|
1675
|
+
provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; use all to search every eligible provider except Parallel MCP, DuckDuckGo, AnySearch, xAI, Bright Data, SerpBase, Serper, and Valyu, omit this field to use the configured provider, or use auto when none is configured")),
|
|
1677
1676
|
workflow: Type.Optional(
|
|
1678
1677
|
StringEnum(["none", "summary-review", "auto-summary"], {
|
|
1679
1678
|
description: "Search workflow mode: none = no curator, summary-review = open curator with auto summary draft (default), auto-summary = generate summary without opening curator",
|
|
@@ -2241,7 +2240,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2241
2240
|
fetchContent: Type.Optional(Type.Boolean({ description: "Fetch up to 5 result pages for exact passage extraction." })),
|
|
2242
2241
|
recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency." })),
|
|
2243
2242
|
domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains; prefix with - to exclude." })),
|
|
2244
|
-
provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; all searches every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, and
|
|
2243
|
+
provider: Type.Optional(searchProviderSchema("Search provider or non-empty list of providers to search simultaneously; all searches every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, SerpBase, Serper, and Valyu")),
|
|
2245
2244
|
}),
|
|
2246
2245
|
async execute(_callId, params, signal, _onUpdate, ctx) {
|
|
2247
2246
|
const claim = typeof params.claim === "string" ? params.claim.trim() : "";
|
|
@@ -19,7 +19,7 @@ const MODEL_PREFERENCE = [
|
|
|
19
19
|
(id: string) => id.includes("terra"),
|
|
20
20
|
(id: string) => /^gpt-\d+(\.\d+)?$/.test(id),
|
|
21
21
|
];
|
|
22
|
-
const
|
|
22
|
+
const DEFAULT_SEARCH_PROVIDERS: readonly string[] = ["openai-codex", "openai"];
|
|
23
23
|
|
|
24
24
|
function pickSearchModel<T extends { id: string }>(models: readonly T[]): T | undefined {
|
|
25
25
|
const candidates = models
|
|
@@ -36,12 +36,13 @@ interface WebSearchConfig {
|
|
|
36
36
|
openaiApiKey?: unknown;
|
|
37
37
|
openaiResponsesUrl?: unknown;
|
|
38
38
|
openaiSearchModel?: unknown;
|
|
39
|
+
openaiSearchProviders?: unknown;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
type ProviderHeaders = Record<string, string | null>;
|
|
42
43
|
|
|
43
44
|
interface OpenAIAuth {
|
|
44
|
-
provider:
|
|
45
|
+
provider: string;
|
|
45
46
|
apiKey: string;
|
|
46
47
|
model: string;
|
|
47
48
|
headers: ProviderHeaders;
|
|
@@ -149,6 +150,14 @@ function resolveConfiguredResponsesUrl(value: unknown): string {
|
|
|
149
150
|
return url.toString();
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
function resolveConfiguredSearchProviders(value: unknown): readonly string[] {
|
|
154
|
+
if (value === undefined) return DEFAULT_SEARCH_PROVIDERS;
|
|
155
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.trim().length === 0)) {
|
|
156
|
+
throw new Error(`openaiSearchProviders in ${CONFIG_PATH} must be an array of non-empty Pi provider ids`);
|
|
157
|
+
}
|
|
158
|
+
return value.map((entry) => entry.trim());
|
|
159
|
+
}
|
|
160
|
+
|
|
152
161
|
function resolveConfiguredSearchModel(value: unknown): string | undefined {
|
|
153
162
|
if (value == null) return undefined;
|
|
154
163
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
@@ -165,14 +174,14 @@ function toRequestHeaders(headers: ProviderHeaders): Record<string, string> {
|
|
|
165
174
|
return requestHeaders;
|
|
166
175
|
}
|
|
167
176
|
|
|
168
|
-
async function resolvePiAuth(ctx: ExtensionContext, responsesUrl: string, modelOverride?: string): Promise<OpenAIAuth | undefined> {
|
|
177
|
+
async function resolvePiAuth(ctx: ExtensionContext, responsesUrl: string, providers: readonly string[], modelOverride?: string): Promise<OpenAIAuth | undefined> {
|
|
169
178
|
let models: ReturnType<typeof ctx.modelRegistry.getAll>;
|
|
170
179
|
try {
|
|
171
180
|
models = ctx.modelRegistry.getAll();
|
|
172
181
|
} catch {
|
|
173
182
|
return undefined;
|
|
174
183
|
}
|
|
175
|
-
for (const provider of
|
|
184
|
+
for (const provider of providers) {
|
|
176
185
|
const preferred = pickSearchModel(models.filter((model) => model.provider === provider));
|
|
177
186
|
if (!preferred) continue;
|
|
178
187
|
try {
|
|
@@ -196,8 +205,9 @@ export async function resolveOpenAIAuth(ctx?: ExtensionContext, signal?: AbortSi
|
|
|
196
205
|
const config = loadConfig();
|
|
197
206
|
const responsesUrl = resolveConfiguredResponsesUrl(config.openaiResponsesUrl);
|
|
198
207
|
const modelOverride = resolveConfiguredSearchModel(config.openaiSearchModel);
|
|
208
|
+
const providers = resolveConfiguredSearchProviders(config.openaiSearchProviders);
|
|
199
209
|
if (ctx) {
|
|
200
|
-
const auth = await resolvePiAuth(ctx, responsesUrl, modelOverride);
|
|
210
|
+
const auth = await resolvePiAuth(ctx, responsesUrl, providers, modelOverride);
|
|
201
211
|
if (auth) return auth;
|
|
202
212
|
}
|
|
203
213
|
|
|
@@ -221,7 +231,8 @@ export async function resolveOpenAIAuth(ctx?: ExtensionContext, signal?: AbortSi
|
|
|
221
231
|
export async function isOpenAISearchAvailable(ctx?: ExtensionContext): Promise<boolean> {
|
|
222
232
|
const config = loadConfig();
|
|
223
233
|
const responsesUrl = resolveConfiguredResponsesUrl(config.openaiResponsesUrl);
|
|
224
|
-
|
|
234
|
+
const providers = resolveConfiguredSearchProviders(config.openaiSearchProviders);
|
|
235
|
+
if (ctx && await resolvePiAuth(ctx, responsesUrl, providers)) return true;
|
|
225
236
|
return hasCredentialSource({
|
|
226
237
|
provider: "OpenAI",
|
|
227
238
|
configuredValue: config.openaiApiKey,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-web-access",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.1",
|
|
4
4
|
"description": "Web search, URL fetching, GitHub repo cloning, PDF extraction, YouTube video understanding, and local video analysis for Pi coding agent. Supports OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Ollama, AnySearch, Bright Data SERP, SerpBase, SearXNG, Exa, Perplexity, and Gemini.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|