@herbertgao/pi-extensions 2026.8.6 → 2026.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
- 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 +272 -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 +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -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 +1 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +26 -10
- 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 +2 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -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 +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
- 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-lens/CHANGELOG.md +909 -0
- package/node_modules/pi-lens/README.md +11 -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 +285 -8
- 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 +119 -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 +192 -0
- 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 +62 -13
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -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/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/helm-render.js +895 -0
- 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 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
- 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 +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
- 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 +72 -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 +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
- 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 +294 -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 +82 -6
- 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 +2 -0
- 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 +102 -7
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1516 -159
- 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 +2274 -147
- 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 +8 -0
- 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 +5 -0
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- 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 +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +184 -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/query.js +52 -4
- 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 +308 -45
- package/node_modules/pi-lens/dist/clients/runtime-session.js +332 -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 +575 -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/sg-runner.js +116 -22
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- 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-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 +67738 -55739
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +466 -54
- 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 +10 -6
- 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/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 +23 -0
- package/node_modules/pi-web-access/README.md +27 -9
- 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/curator-page.ts +6 -3
- package/node_modules/pi-web-access/curator-server.ts +4 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +28 -3
- package/node_modules/pi-web-access/gemini-search.ts +16 -4
- package/node_modules/pi-web-access/index.ts +41 -57
- 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/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- 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 +13 -13
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { activityMonitor } from "./activity.ts";
|
|
3
|
+
import type { SearchOptions, SearchResponse } from "./perplexity.ts";
|
|
4
|
+
import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.ts";
|
|
5
|
+
import { getWebSearchConfigPath } from "./utils.ts";
|
|
6
|
+
|
|
7
|
+
const SERPER_SEARCH_URL = "https://google.serper.dev/search";
|
|
8
|
+
const CONFIG_PATH = getWebSearchConfigPath();
|
|
9
|
+
const SEARCH_TIMEOUT_MS = 60_000;
|
|
10
|
+
const RECENCY_TBS: Record<NonNullable<SearchOptions["recencyFilter"]>, string> = {
|
|
11
|
+
day: "qdr:d",
|
|
12
|
+
week: "qdr:w",
|
|
13
|
+
month: "qdr:m",
|
|
14
|
+
year: "qdr:y",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
interface WebSearchConfig {
|
|
18
|
+
serperApiKey?: unknown;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface SerperResult {
|
|
22
|
+
title?: unknown;
|
|
23
|
+
link?: unknown;
|
|
24
|
+
snippet?: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let cachedConfig: WebSearchConfig | null = null;
|
|
28
|
+
|
|
29
|
+
function loadConfig(): WebSearchConfig {
|
|
30
|
+
if (cachedConfig) return cachedConfig;
|
|
31
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
32
|
+
cachedConfig = {};
|
|
33
|
+
return cachedConfig;
|
|
34
|
+
}
|
|
35
|
+
const raw = readFileSync(CONFIG_PATH, "utf-8");
|
|
36
|
+
let parsed: unknown;
|
|
37
|
+
try {
|
|
38
|
+
parsed = JSON.parse(raw);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
41
|
+
throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
|
|
42
|
+
}
|
|
43
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
44
|
+
throw new Error(`Invalid config in ${CONFIG_PATH}: expected a JSON object`);
|
|
45
|
+
}
|
|
46
|
+
cachedConfig = parsed as WebSearchConfig;
|
|
47
|
+
return cachedConfig;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function getApiKey(signal?: AbortSignal): Promise<string | null> {
|
|
51
|
+
return resolveCredential({
|
|
52
|
+
provider: "Serper",
|
|
53
|
+
configuredValue: loadConfig().serperApiKey,
|
|
54
|
+
environmentValue: process.env.SERPER_API_KEY,
|
|
55
|
+
signal,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function requireApiKey(signal?: AbortSignal): Promise<string> {
|
|
60
|
+
const apiKey = await getApiKey(signal);
|
|
61
|
+
if (!apiKey) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
"Serper API key not found. Either:\n" +
|
|
64
|
+
` 1. Create ${CONFIG_PATH} with { "serperApiKey": "your-key" }\n` +
|
|
65
|
+
" 2. Set SERPER_API_KEY environment variable\n" +
|
|
66
|
+
"Get a key at https://serper.dev",
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return apiKey;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeCount(value: number | undefined): number {
|
|
73
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 5;
|
|
74
|
+
return Math.max(1, Math.min(Math.floor(value), 20));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface DomainFilters {
|
|
78
|
+
include: string[];
|
|
79
|
+
exclude: string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeDomain(value: string): string | null {
|
|
83
|
+
let input = value.trim().toLowerCase();
|
|
84
|
+
if (!input) return null;
|
|
85
|
+
if (input.startsWith("-")) input = input.slice(1).trim();
|
|
86
|
+
if (!input) return null;
|
|
87
|
+
try {
|
|
88
|
+
const parsed = input.includes("://") ? new URL(input) : new URL(`https://${input}`);
|
|
89
|
+
input = parsed.hostname;
|
|
90
|
+
} catch {
|
|
91
|
+
input = input.split("/")[0]?.split(":")[0] ?? "";
|
|
92
|
+
}
|
|
93
|
+
input = input.replace(/^\.+|\.+$/g, "");
|
|
94
|
+
return /^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(input) ? input : null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function parseDomainFilter(domainFilter: string[] | undefined): DomainFilters {
|
|
98
|
+
const filters: DomainFilters = { include: [], exclude: [] };
|
|
99
|
+
for (const raw of domainFilter ?? []) {
|
|
100
|
+
const domain = normalizeDomain(raw);
|
|
101
|
+
if (!domain) continue;
|
|
102
|
+
const target = raw.trim().startsWith("-") ? filters.exclude : filters.include;
|
|
103
|
+
if (!target.includes(domain)) target.push(domain);
|
|
104
|
+
}
|
|
105
|
+
return filters;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function passesDomainFilters(url: string, filters: DomainFilters): boolean {
|
|
109
|
+
if (filters.include.length === 0 && filters.exclude.length === 0) return true;
|
|
110
|
+
let hostname: string;
|
|
111
|
+
try {
|
|
112
|
+
hostname = new URL(url).hostname.toLowerCase();
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const matches = (domain: string) => hostname === domain || hostname.endsWith(`.${domain}`);
|
|
117
|
+
if (filters.exclude.some(matches)) return false;
|
|
118
|
+
return filters.include.length === 0 || filters.include.some(matches);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function buildQuery(query: string, filters: DomainFilters): string {
|
|
122
|
+
const parts = [query];
|
|
123
|
+
if (filters.include.length === 1) parts.push(`site:${filters.include[0]}`);
|
|
124
|
+
if (filters.include.length > 1) parts.push(`(${filters.include.map(domain => `site:${domain}`).join(" OR ")})`);
|
|
125
|
+
for (const domain of filters.exclude) parts.push(`-site:${domain}`);
|
|
126
|
+
return parts.join(" ");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function errorMessage(err: unknown): string {
|
|
130
|
+
return err instanceof Error ? err.message : String(err);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function invalidResponse(message: string): Error {
|
|
134
|
+
return new Error(`Serper API returned invalid response: ${message}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function parseResponse(value: unknown): SerperResult[] {
|
|
138
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw invalidResponse("expected an object envelope");
|
|
139
|
+
const envelope = value as Record<string, unknown>;
|
|
140
|
+
if (!Array.isArray(envelope.organic)) throw invalidResponse("expected organic array");
|
|
141
|
+
return envelope.organic as SerperResult[];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function buildAnswer(results: SearchResponse["results"]): string {
|
|
145
|
+
return results.map((result) => result.snippet
|
|
146
|
+
? `${result.snippet}\nSource: ${result.title} (${result.url})`
|
|
147
|
+
: `Source: ${result.title} (${result.url})`).join("\n\n");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function isSerperAvailable(): boolean {
|
|
151
|
+
return hasCredentialSource({ provider: "Serper", configuredValue: loadConfig().serperApiKey, environmentValue: process.env.SERPER_API_KEY });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export async function searchWithSerper(query: string, options: SearchOptions = {}): Promise<SearchResponse> {
|
|
155
|
+
const apiKey = await requireApiKey(options.signal);
|
|
156
|
+
const numResults = normalizeCount(options.numResults);
|
|
157
|
+
const filters = parseDomainFilter(options.domainFilter);
|
|
158
|
+
const requestCount = options.domainFilter?.length ? Math.min(20, numResults + 5) : numResults;
|
|
159
|
+
const activityId = activityMonitor.logStart({ type: "api", query });
|
|
160
|
+
let response: Response;
|
|
161
|
+
try {
|
|
162
|
+
response = await fetch(SERPER_SEARCH_URL, {
|
|
163
|
+
method: "POST",
|
|
164
|
+
headers: { "X-API-KEY": apiKey, "Content-Type": "application/json", Accept: "application/json" },
|
|
165
|
+
body: JSON.stringify({ q: buildQuery(query, filters), num: requestCount, ...(options.recencyFilter ? { tbs: RECENCY_TBS[options.recencyFilter] } : {}) }),
|
|
166
|
+
signal: options.signal ? AbortSignal.any([AbortSignal.timeout(SEARCH_TIMEOUT_MS), options.signal]) : AbortSignal.timeout(SEARCH_TIMEOUT_MS),
|
|
167
|
+
});
|
|
168
|
+
} catch (err) {
|
|
169
|
+
const message = errorMessage(err);
|
|
170
|
+
const redactedMessage = redactCredential(message, apiKey);
|
|
171
|
+
if (redactedMessage.toLowerCase().includes("abort")) activityMonitor.logComplete(activityId, 0);
|
|
172
|
+
else activityMonitor.logError(activityId, redactedMessage);
|
|
173
|
+
if (redactedMessage === message) throw err;
|
|
174
|
+
const redactedError = new Error(redactedMessage);
|
|
175
|
+
if (err instanceof Error) redactedError.name = err.name;
|
|
176
|
+
throw redactedError;
|
|
177
|
+
}
|
|
178
|
+
if (!response.ok) {
|
|
179
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
180
|
+
const errorText = redactCredential(await response.text(), apiKey);
|
|
181
|
+
throw new Error(`Serper API error ${response.status}: ${errorText.slice(0, 300)}`);
|
|
182
|
+
}
|
|
183
|
+
let rawData: unknown;
|
|
184
|
+
try {
|
|
185
|
+
rawData = await response.json();
|
|
186
|
+
} catch (err) {
|
|
187
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
188
|
+
throw new Error(`Serper API returned invalid JSON: ${errorMessage(err)}`);
|
|
189
|
+
}
|
|
190
|
+
let entries: SerperResult[];
|
|
191
|
+
try {
|
|
192
|
+
entries = parseResponse(rawData);
|
|
193
|
+
} catch (err) {
|
|
194
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
198
|
+
const results: SearchResponse["results"] = [];
|
|
199
|
+
for (const entry of entries) {
|
|
200
|
+
if (!entry || typeof entry !== "object") continue;
|
|
201
|
+
if (typeof entry.link !== "string" || !entry.link) continue;
|
|
202
|
+
if (!passesDomainFilters(entry.link, filters)) continue;
|
|
203
|
+
results.push({
|
|
204
|
+
title: typeof entry.title === "string" && entry.title.trim() ? entry.title.trim() : `Source ${results.length + 1}`,
|
|
205
|
+
url: entry.link,
|
|
206
|
+
snippet: typeof entry.snippet === "string" ? entry.snippet : "",
|
|
207
|
+
});
|
|
208
|
+
if (results.length >= numResults) break;
|
|
209
|
+
}
|
|
210
|
+
return { answer: buildAnswer(results), results };
|
|
211
|
+
}
|
|
@@ -4,6 +4,8 @@ import { join } from "node:path";
|
|
|
4
4
|
|
|
5
5
|
const THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
6
6
|
|
|
7
|
+
export type SummaryThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
8
|
+
|
|
7
9
|
interface SummaryModelScopeContext {
|
|
8
10
|
cwd: string;
|
|
9
11
|
isProjectTrusted(): boolean;
|
|
@@ -78,11 +80,17 @@ export function summaryModelValue(model: ModelLike): string {
|
|
|
78
80
|
return `${model.provider}/${model.id}`;
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
export function splitThinkingSuffix(value: string): { value: string; thinkingLevel?: SummaryThinkingLevel } {
|
|
84
|
+
const index = value.lastIndexOf(":");
|
|
85
|
+
if (index < 0) return { value };
|
|
86
|
+
const suffix = value.slice(index + 1);
|
|
87
|
+
return THINKING_LEVELS.has(suffix)
|
|
88
|
+
? { value: value.slice(0, index), thinkingLevel: suffix as SummaryThinkingLevel }
|
|
89
|
+
: { value };
|
|
90
|
+
}
|
|
91
|
+
|
|
81
92
|
function stripThinkingSuffix(pattern: string): string {
|
|
82
|
-
|
|
83
|
-
if (index < 0) return pattern;
|
|
84
|
-
const suffix = pattern.slice(index + 1);
|
|
85
|
-
return THINKING_LEVELS.has(suffix) ? pattern.slice(0, index) : pattern;
|
|
93
|
+
return splitThinkingSuffix(pattern).value;
|
|
86
94
|
}
|
|
87
95
|
|
|
88
96
|
function globToRegExp(pattern: string): RegExp {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { clampThinkingLevel, type ModelThinkingLevel, type ThinkingLevel } from "@earendil-works/pi-ai";
|
|
2
|
+
import { complete, completeSimple, type Api, type Message, type Model } from "@earendil-works/pi-ai/compat";
|
|
2
3
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns } from "./summary-model-scope.ts";
|
|
4
|
+
import { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns, splitThinkingSuffix, type SummaryThinkingLevel } from "./summary-model-scope.ts";
|
|
4
5
|
import type { QueryResultData } from "./storage.ts";
|
|
5
6
|
|
|
6
7
|
type ProviderHeaders = Record<string, string | null>;
|
|
@@ -9,7 +10,11 @@ type SummaryModelRegistry = SummaryGenerationContext["modelRegistry"] & { comple
|
|
|
9
10
|
|
|
10
11
|
const PREFERRED_SUMMARY_MODELS = [
|
|
11
12
|
{ provider: "anthropic", id: "claude-haiku-4-5" },
|
|
12
|
-
{ provider: "openai-codex", id: "gpt-5.
|
|
13
|
+
{ provider: "openai-codex", id: "gpt-5.6-luna" },
|
|
14
|
+
{ provider: "openai-codex", id: "gpt-5.6-terra" },
|
|
15
|
+
{ provider: "google", id: "gemini-3.6-flash" },
|
|
16
|
+
{ provider: "openai", id: "gpt-5-mini" },
|
|
17
|
+
{ provider: "deepseek", id: "deepseek-v4-flash" },
|
|
13
18
|
] as const;
|
|
14
19
|
|
|
15
20
|
export const SUMMARY_GENERATION_DEADLINE_MS = 30_000;
|
|
@@ -186,28 +191,35 @@ export function buildDeterministicSummary(results: QueryResultData[]): { summary
|
|
|
186
191
|
};
|
|
187
192
|
}
|
|
188
193
|
|
|
189
|
-
function parseModelSelector(value: string): { provider: string; id: string } {
|
|
190
|
-
const
|
|
191
|
-
|
|
194
|
+
function parseModelSelector(value: string): { provider: string; id: string; thinkingLevel?: SummaryThinkingLevel } {
|
|
195
|
+
const selector = splitThinkingSuffix(value);
|
|
196
|
+
const slashIndex = selector.value.indexOf("/");
|
|
197
|
+
if (slashIndex <= 0 || slashIndex >= selector.value.length - 1) {
|
|
192
198
|
throw new Error(`Invalid summary model: ${value}. Use provider/model-id.`);
|
|
193
199
|
}
|
|
194
200
|
return {
|
|
195
|
-
provider: value.slice(0, slashIndex),
|
|
196
|
-
id: value.slice(slashIndex + 1),
|
|
201
|
+
provider: selector.value.slice(0, slashIndex),
|
|
202
|
+
id: selector.value.slice(slashIndex + 1),
|
|
203
|
+
thinkingLevel: selector.thinkingLevel,
|
|
197
204
|
};
|
|
198
205
|
}
|
|
199
206
|
|
|
207
|
+
function resolveThinkingLevel(model: Model<Api>, requested?: SummaryThinkingLevel): ModelThinkingLevel | undefined {
|
|
208
|
+
if (!requested) return undefined;
|
|
209
|
+
return clampThinkingLevel(model, requested as ModelThinkingLevel);
|
|
210
|
+
}
|
|
211
|
+
|
|
200
212
|
async function resolveSummaryModelCandidates(
|
|
201
213
|
ctx: SummaryGenerationContext,
|
|
202
214
|
modelOverride?: string,
|
|
203
|
-
): Promise<{ candidates: Array<{ model: Model<Api>; apiKey?: string; headers?: ProviderHeaders }>; errors: string[] }> {
|
|
215
|
+
): Promise<{ candidates: Array<{ model: Model<Api>; apiKey?: string; headers?: ProviderHeaders; thinkingLevel?: SummaryThinkingLevel }>; errors: string[] }> {
|
|
204
216
|
const enabledModelPatterns = loadEnabledModelPatterns(ctx);
|
|
205
|
-
const specs: Array<{ provider: string; id: string }> = [];
|
|
217
|
+
const specs: Array<{ provider: string; id: string; thinkingLevel?: SummaryThinkingLevel }> = [];
|
|
206
218
|
const normalizedOverride = typeof modelOverride === "string" ? modelOverride.trim() : "";
|
|
207
219
|
if (normalizedOverride.length > 0) specs.push(parseModelSelector(normalizedOverride));
|
|
208
220
|
specs.push(...PREFERRED_SUMMARY_MODELS);
|
|
209
221
|
|
|
210
|
-
const candidates: Array<{ model: Model<Api>; apiKey?: string; headers?: ProviderHeaders }> = [];
|
|
222
|
+
const candidates: Array<{ model: Model<Api>; apiKey?: string; headers?: ProviderHeaders; thinkingLevel?: SummaryThinkingLevel }> = [];
|
|
211
223
|
const errors: string[] = [];
|
|
212
224
|
const seen = new Set<string>();
|
|
213
225
|
for (const spec of specs) {
|
|
@@ -229,7 +241,7 @@ async function resolveSummaryModelCandidates(
|
|
|
229
241
|
errors.push(`No API key available for summary model ${value}`);
|
|
230
242
|
continue;
|
|
231
243
|
}
|
|
232
|
-
candidates.push({ model, apiKey: auth.apiKey, headers: auth.headers });
|
|
244
|
+
candidates.push({ model, apiKey: auth.apiKey, headers: auth.headers, thinkingLevel: spec.thinkingLevel });
|
|
233
245
|
}
|
|
234
246
|
return { candidates, errors };
|
|
235
247
|
}
|
|
@@ -285,7 +297,8 @@ export async function generateSummaryDraft(
|
|
|
285
297
|
}
|
|
286
298
|
|
|
287
299
|
const registry = ctx.modelRegistry as SummaryModelRegistry;
|
|
288
|
-
const
|
|
300
|
+
const customCompleteFn = completeFn !== undefined;
|
|
301
|
+
const usesRegistryComplete = !customCompleteFn && typeof registry.complete === "function";
|
|
289
302
|
completeFn ??= usesRegistryComplete ? registry.complete!.bind(registry) as CompleteFunction : complete;
|
|
290
303
|
|
|
291
304
|
const generationStartedAt = Date.now();
|
|
@@ -343,7 +356,7 @@ export async function generateSummaryDraft(
|
|
|
343
356
|
}
|
|
344
357
|
|
|
345
358
|
let lastError = resolved.errors.at(-1);
|
|
346
|
-
for (const { model, apiKey, headers } of resolved.candidates) {
|
|
359
|
+
for (const { model, apiKey, headers, thinkingLevel } of resolved.candidates) {
|
|
347
360
|
const startedAt = Date.now();
|
|
348
361
|
try {
|
|
349
362
|
const userMessage: Message = {
|
|
@@ -351,25 +364,34 @@ export async function generateSummaryDraft(
|
|
|
351
364
|
content: [{ type: "text", text: prompt }],
|
|
352
365
|
timestamp: Date.now(),
|
|
353
366
|
};
|
|
367
|
+
const requestedThinkingLevel = resolveThinkingLevel(model, thinkingLevel);
|
|
368
|
+
const enabledThinkingLevel = requestedThinkingLevel && requestedThinkingLevel !== "off"
|
|
369
|
+
? requestedThinkingLevel as ThinkingLevel
|
|
370
|
+
: undefined;
|
|
371
|
+
const completionOptions = {
|
|
372
|
+
...(usesRegistryComplete ? {} : { apiKey, headers }),
|
|
373
|
+
signal: completionSignal,
|
|
374
|
+
...(requestedThinkingLevel ? { reasoning: requestedThinkingLevel } : {}),
|
|
375
|
+
...(enabledThinkingLevel ? { reasoningEffort: enabledThinkingLevel } : {}),
|
|
376
|
+
};
|
|
377
|
+
const completion = thinkingLevel !== undefined && !customCompleteFn && !usesRegistryComplete
|
|
378
|
+
? completeSimple(model, { messages: [userMessage] }, { apiKey, headers, signal: completionSignal, ...(enabledThinkingLevel ? { reasoning: enabledThinkingLevel } : {}) })
|
|
379
|
+
: completeFn(model, { messages: [userMessage] }, completionOptions);
|
|
354
380
|
|
|
355
|
-
const response = await raceSummaryOperation(Promise.resolve(
|
|
356
|
-
model,
|
|
357
|
-
{ messages: [userMessage] },
|
|
358
|
-
usesRegistryComplete ? { signal: completionSignal } : { apiKey, headers, signal: completionSignal },
|
|
359
|
-
)));
|
|
381
|
+
const response = await raceSummaryOperation(Promise.resolve(completion));
|
|
360
382
|
if (response.stopReason === "aborted") {
|
|
361
383
|
throw new Error("Aborted");
|
|
362
384
|
}
|
|
363
385
|
|
|
364
386
|
const contentParts = Array.isArray(response.content) ? response.content : [];
|
|
365
387
|
const summary = contentParts
|
|
366
|
-
.map(part => getTextFromContentPart(part))
|
|
367
|
-
.filter(text => text.trim().length > 0)
|
|
388
|
+
.map((part: unknown) => getTextFromContentPart(part))
|
|
389
|
+
.filter((text: string) => text.trim().length > 0)
|
|
368
390
|
.join("\n")
|
|
369
391
|
.trim();
|
|
370
392
|
|
|
371
393
|
if (summary.length === 0) {
|
|
372
|
-
const partTypes = contentParts.map(part => getContentPartType(part));
|
|
394
|
+
const partTypes = contentParts.map((part: unknown) => getContentPartType(part));
|
|
373
395
|
const typesLabel = partTypes.length > 0 ? partTypes.join(", ") : "none";
|
|
374
396
|
throw new Error(`Summary model returned empty response (content parts: ${typesLabel})`);
|
|
375
397
|
}
|
|
@@ -3,14 +3,15 @@ import { activityMonitor } from "./activity.ts";
|
|
|
3
3
|
import type { ExtractedContent } from "./extract.ts";
|
|
4
4
|
import type { SearchOptions, SearchResponse } from "./perplexity.ts";
|
|
5
5
|
import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.ts";
|
|
6
|
-
import { getWebSearchConfigPath } from "./utils.ts";
|
|
6
|
+
import { fetchWithCredentialRedirects, getWebSearchConfigPath, resolveApiBaseUrl } from "./utils.ts";
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const TAVILY_API_BASE_URL = "https://api.tavily.com";
|
|
9
9
|
const CONFIG_PATH = getWebSearchConfigPath();
|
|
10
10
|
const SEARCH_TIMEOUT_MS = 60_000;
|
|
11
11
|
|
|
12
12
|
interface WebSearchConfig {
|
|
13
13
|
tavilyApiKey?: unknown;
|
|
14
|
+
tavilyBaseUrl?: unknown;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
interface TavilyResult {
|
|
@@ -57,6 +58,16 @@ async function getApiKey(signal?: AbortSignal): Promise<string | null> {
|
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
function getApiUrl(): string {
|
|
62
|
+
return `${resolveApiBaseUrl({
|
|
63
|
+
configKey: "tavilyBaseUrl",
|
|
64
|
+
configuredValue: loadConfig().tavilyBaseUrl,
|
|
65
|
+
defaultValue: TAVILY_API_BASE_URL,
|
|
66
|
+
environmentKey: "TAVILY_BASE_URL",
|
|
67
|
+
environmentValue: process.env.TAVILY_BASE_URL,
|
|
68
|
+
})}/search`;
|
|
69
|
+
}
|
|
70
|
+
|
|
60
71
|
async function requireApiKey(signal?: AbortSignal): Promise<string> {
|
|
61
72
|
const apiKey = await getApiKey(signal);
|
|
62
73
|
if (!apiKey) {
|
|
@@ -152,6 +163,7 @@ export function isTavilyAvailable(): boolean {
|
|
|
152
163
|
}
|
|
153
164
|
|
|
154
165
|
export async function searchWithTavily(query: string, options: TavilySearchOptions = {}): Promise<SearchResponse> {
|
|
166
|
+
const apiUrl = getApiUrl();
|
|
155
167
|
const apiKey = await requireApiKey(options.signal);
|
|
156
168
|
const numResults = normalizeCount(options.numResults);
|
|
157
169
|
const body: Record<string, unknown> = {
|
|
@@ -167,7 +179,7 @@ export async function searchWithTavily(query: string, options: TavilySearchOptio
|
|
|
167
179
|
const activityId = activityMonitor.logStart({ type: "api", query });
|
|
168
180
|
let response: Response;
|
|
169
181
|
try {
|
|
170
|
-
response = await
|
|
182
|
+
response = await fetchWithCredentialRedirects(apiUrl, {
|
|
171
183
|
method: "POST",
|
|
172
184
|
headers: {
|
|
173
185
|
"Authorization": `Bearer ${apiKey}`,
|
|
@@ -175,7 +187,7 @@ export async function searchWithTavily(query: string, options: TavilySearchOptio
|
|
|
175
187
|
},
|
|
176
188
|
body: JSON.stringify(body),
|
|
177
189
|
signal: requestSignal(options.signal),
|
|
178
|
-
});
|
|
190
|
+
}, ["Authorization"]);
|
|
179
191
|
} catch (err) {
|
|
180
192
|
const message = errorMessage(err);
|
|
181
193
|
const redactedMessage = redactCredential(message, apiKey);
|
|
@@ -12,6 +12,93 @@ export function getWebSearchConfigPath(): string {
|
|
|
12
12
|
return join(getWebSearchConfigDir(), "web-search.json");
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
interface ApiBaseUrlOptions {
|
|
16
|
+
configKey: string;
|
|
17
|
+
configuredValue: unknown;
|
|
18
|
+
defaultValue: string;
|
|
19
|
+
environmentKey: string;
|
|
20
|
+
environmentValue: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resolveApiBaseUrl(options: ApiBaseUrlOptions): string {
|
|
24
|
+
const fromEnvironment = options.environmentValue !== undefined;
|
|
25
|
+
const value = fromEnvironment ? options.environmentValue : options.configuredValue;
|
|
26
|
+
if (value === undefined) return options.defaultValue;
|
|
27
|
+
|
|
28
|
+
const source = fromEnvironment
|
|
29
|
+
? options.environmentKey
|
|
30
|
+
: `${options.configKey} in ${getWebSearchConfigPath()}`;
|
|
31
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
32
|
+
throw new Error(`${source} must be an absolute HTTP(S) URL`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let url: URL;
|
|
36
|
+
try {
|
|
37
|
+
url = new URL(value.trim());
|
|
38
|
+
} catch {
|
|
39
|
+
throw new Error(`${source} must be an absolute HTTP(S) URL`);
|
|
40
|
+
}
|
|
41
|
+
if (url.protocol !== "https:") {
|
|
42
|
+
throw new Error(`${source} must be an absolute HTTPS URL`);
|
|
43
|
+
}
|
|
44
|
+
if (url.username || url.password) {
|
|
45
|
+
throw new Error(`${source} must not include credentials`);
|
|
46
|
+
}
|
|
47
|
+
if (url.search || url.hash) {
|
|
48
|
+
throw new Error(`${source} must not include query parameters or fragments`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
url.search = "";
|
|
52
|
+
url.hash = "";
|
|
53
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
54
|
+
return url.toString().replace(/\/+$/, "");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const API_REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
|
|
58
|
+
const API_REQUEST_BODY_HEADERS = ["Content-Encoding", "Content-Language", "Content-Location", "Content-Type"];
|
|
59
|
+
const MAX_API_REDIRECTS = 5;
|
|
60
|
+
|
|
61
|
+
export async function fetchWithCredentialRedirects(
|
|
62
|
+
url: string,
|
|
63
|
+
init: RequestInit,
|
|
64
|
+
credentialHeaders: readonly string[],
|
|
65
|
+
): Promise<Response> {
|
|
66
|
+
let current = new URL(url);
|
|
67
|
+
let requestInit = init;
|
|
68
|
+
|
|
69
|
+
for (let redirects = 0; ; redirects++) {
|
|
70
|
+
const response = await fetch(current, { ...requestInit, redirect: "manual" });
|
|
71
|
+
if (!API_REDIRECT_STATUSES.has(response.status)) return response;
|
|
72
|
+
|
|
73
|
+
const location = response.headers.get("location");
|
|
74
|
+
if (!location) return response;
|
|
75
|
+
if (redirects === MAX_API_REDIRECTS) {
|
|
76
|
+
throw new Error(`Too many API redirects from ${url}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const next = new URL(location, current);
|
|
80
|
+
if (next.protocol !== "http:" && next.protocol !== "https:") {
|
|
81
|
+
throw new Error(`API redirect from ${current.origin} must use HTTP(S)`);
|
|
82
|
+
}
|
|
83
|
+
const method = requestInit.method?.toUpperCase() ?? "GET";
|
|
84
|
+
if (
|
|
85
|
+
((response.status === 301 || response.status === 302) && method === "POST")
|
|
86
|
+
|| (response.status === 303 && method !== "GET" && method !== "HEAD")
|
|
87
|
+
) {
|
|
88
|
+
const headers = new Headers(requestInit.headers);
|
|
89
|
+
for (const name of API_REQUEST_BODY_HEADERS) headers.delete(name);
|
|
90
|
+
const { body: _body, ...withoutBody } = requestInit;
|
|
91
|
+
requestInit = { ...withoutBody, method: "GET", headers };
|
|
92
|
+
}
|
|
93
|
+
if (next.origin !== current.origin) {
|
|
94
|
+
const headers = new Headers(requestInit.headers);
|
|
95
|
+
for (const name of credentialHeaders) headers.delete(name);
|
|
96
|
+
requestInit = { ...requestInit, headers };
|
|
97
|
+
}
|
|
98
|
+
current = next;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
15
102
|
export interface CuratorNetworkConfig {
|
|
16
103
|
/** Whether remote access was opted into via curatorRemote. */
|
|
17
104
|
enabled: boolean;
|