@herbertgao/pi-extensions 2026.9.10 → 2026.9.12
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 +1 -0
- package/THIRD_PARTY_NOTICES.md +26 -0
- package/node_modules/@herbertgao/pi-bark/package.json +2 -2
- 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/package.json +3 -2
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +2 -2
- package/node_modules/@herbertgao/resume-from/package.json +1 -1
- package/node_modules/@herbertgao/sol-pi/README.md +3 -3
- package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +4 -4
- package/node_modules/@herbertgao/sol-pi/agents-install.md +4 -4
- package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +6 -6
- package/node_modules/@herbertgao/sol-pi/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@narumitw/pi-btw/dist/index.ts +39 -89
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
- package/node_modules/@narumitw/pi-btw/package.json +4 -4
- package/node_modules/@narumitw/pi-btw/src/btw.ts +28 -87
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +8 -0
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +40 -37
- package/node_modules/@narumitw/pi-caffeinate/README.md +21 -66
- package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +10 -41
- package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +2 -2
- package/node_modules/@narumitw/pi-caffeinate/package.json +50 -51
- package/node_modules/@narumitw/pi-caffeinate/src/caffeinate.ts +637 -663
- package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +114 -120
- package/node_modules/@narumitw/pi-caffeinate/src/inhibitor-process.ts +29 -29
- package/node_modules/@narumitw/pi-caffeinate/src/inhibitors.ts +108 -126
- package/node_modules/@narumitw/pi-caffeinate/src/settings.ts +124 -128
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +62 -0
- package/node_modules/pi-mcp-adapter/README.md +75 -7
- package/node_modules/pi-mcp-adapter/bearer-command-resolver.ts +193 -0
- package/node_modules/pi-mcp-adapter/cli.js +56 -5
- package/node_modules/pi-mcp-adapter/commands.ts +96 -5
- package/node_modules/pi-mcp-adapter/config.ts +73 -6
- package/node_modules/pi-mcp-adapter/direct-tool-surface.ts +22 -28
- package/node_modules/pi-mcp-adapter/direct-tools.ts +13 -5
- package/node_modules/pi-mcp-adapter/dist/abort.d.ts +2 -0
- package/node_modules/pi-mcp-adapter/dist/abort.js +36 -0
- package/node_modules/pi-mcp-adapter/dist/abort.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.d.ts +6 -0
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.js +194 -0
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/config.d.ts +5 -0
- package/node_modules/pi-mcp-adapter/dist/config.js +75 -6
- package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/consent-manager.d.ts +18 -0
- package/node_modules/pi-mcp-adapter/dist/consent-manager.js +88 -0
- package/node_modules/pi-mcp-adapter/dist/consent-manager.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.d.ts +17 -0
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.js +316 -0
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/errors.d.ts +131 -0
- package/node_modules/pi-mcp-adapter/dist/errors.js +278 -0
- package/node_modules/pi-mcp-adapter/dist/errors.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/http-ca.d.ts +8 -0
- package/node_modules/pi-mcp-adapter/dist/http-ca.js +88 -0
- package/node_modules/pi-mcp-adapter/dist/http-ca.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/jev-client.d.ts +15 -0
- package/node_modules/pi-mcp-adapter/dist/jev-client.js +340 -0
- package/node_modules/pi-mcp-adapter/dist/jev-client.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.d.ts +77 -0
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.js +2 -0
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.d.ts +22 -0
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.js +85 -0
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.d.ts +2 -0
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js +55 -0
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/lifecycle.d.ts +56 -0
- package/node_modules/pi-mcp-adapter/dist/lifecycle.js +410 -0
- package/node_modules/pi-mcp-adapter/dist/lifecycle.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/logger.d.ts +51 -0
- package/node_modules/pi-mcp-adapter/dist/logger.js +131 -0
- package/node_modules/pi-mcp-adapter/dist/logger.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.d.ts +26 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.js +103 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.d.ts +118 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.js +1098 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.d.ts +168 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.js +1117 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js +7 -140
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.d.ts +53 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.js +441 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.d.ts +148 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.js +689 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.d.ts +6 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.js +166 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.d.ts +102 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.js +369 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.d.ts +95 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.js +242 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js +2 -11
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.d.ts +6 -0
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.js +505 -0
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.d.ts +10 -0
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.js +312 -0
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.d.ts +13 -0
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.js +97 -0
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.d.ts +17 -0
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.js +203 -0
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.d.ts +11 -0
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.js +172 -0
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/server-manager.d.ts +157 -0
- package/node_modules/pi-mcp-adapter/dist/server-manager.js +1743 -0
- package/node_modules/pi-mcp-adapter/dist/server-manager.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/session-approvals.d.ts +34 -0
- package/node_modules/pi-mcp-adapter/dist/session-approvals.js +140 -0
- package/node_modules/pi-mcp-adapter/dist/session-approvals.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/session-recovery.d.ts +26 -0
- package/node_modules/pi-mcp-adapter/dist/session-recovery.js +142 -0
- package/node_modules/pi-mcp-adapter/dist/session-recovery.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/state.d.ts +73 -0
- package/node_modules/pi-mcp-adapter/dist/state.js +2 -0
- package/node_modules/pi-mcp-adapter/dist/state.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/types.d.ts +36 -0
- package/node_modules/pi-mcp-adapter/dist/types.js +18 -0
- package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.d.ts +17 -0
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.js +219 -0
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.d.ts +15 -0
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.js +85 -0
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/utils.d.ts +1 -0
- package/node_modules/pi-mcp-adapter/dist/utils.js +13 -0
- package/node_modules/pi-mcp-adapter/dist/utils.js.map +1 -1
- package/node_modules/pi-mcp-adapter/elicitation-handler.ts +29 -19
- package/node_modules/pi-mcp-adapter/examples/jev-accessibility-loop.mjs +85 -0
- package/node_modules/pi-mcp-adapter/examples/jev-semantic-filter.mjs +34 -0
- package/node_modules/pi-mcp-adapter/index.ts +129 -6
- package/node_modules/pi-mcp-adapter/init.ts +6 -19
- package/node_modules/pi-mcp-adapter/jev-client.ts +273 -0
- package/node_modules/pi-mcp-adapter/jev-contracts.ts +53 -0
- package/node_modules/pi-mcp-adapter/jev-key-store.ts +79 -0
- package/node_modules/pi-mcp-adapter/lifecycle.ts +14 -3
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +7 -142
- package/node_modules/pi-mcp-adapter/mcp-code.ts +119 -11
- package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-references.ts +5 -3
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +5 -0
- package/node_modules/pi-mcp-adapter/mcp-tasks.ts +467 -0
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +2 -13
- package/node_modules/pi-mcp-adapter/namespace-tools.ts +1 -1
- package/node_modules/pi-mcp-adapter/package.json +17 -9
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +319 -166
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +6 -3
- package/node_modules/pi-mcp-adapter/search-ranking.ts +38 -7
- package/node_modules/pi-mcp-adapter/secure-keyring.ts +170 -0
- package/node_modules/pi-mcp-adapter/semantic-search.ts +186 -0
- package/node_modules/pi-mcp-adapter/server-manager.ts +293 -50
- package/node_modules/pi-mcp-adapter/session-approvals.ts +14 -0
- package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +2 -0
- package/node_modules/pi-mcp-adapter/state.ts +3 -1
- package/node_modules/pi-mcp-adapter/tool-approval.ts +26 -4
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +6 -14
- package/node_modules/pi-mcp-adapter/tool-registrar.ts +8 -6
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +4 -1
- package/node_modules/pi-mcp-adapter/types.ts +53 -0
- package/node_modules/pi-mcp-adapter/ui-server.ts +17 -9
- package/node_modules/pi-mcp-adapter/utils.ts +16 -0
- package/node_modules/pi-multi-account/CHANGELOG.md +1209 -0
- package/node_modules/pi-multi-account/CONTRIBUTING.md +61 -0
- package/node_modules/pi-multi-account/LICENSE +21 -0
- package/node_modules/pi-multi-account/README.md +197 -0
- package/node_modules/pi-multi-account/SECURITY.md +27 -0
- package/node_modules/pi-multi-account/auth-file-transaction.ts +56 -0
- package/node_modules/pi-multi-account/child-usability.ts +233 -0
- package/node_modules/pi-multi-account/compaction-summary.ts +32 -0
- package/node_modules/pi-multi-account/completion-route-planner.ts +224 -0
- package/node_modules/pi-multi-account/context-guard.ts +420 -0
- package/node_modules/pi-multi-account/cursor/LICENSE +21 -0
- package/node_modules/pi-multi-account/cursor/NOTICE +2 -0
- package/node_modules/pi-multi-account/cursor/auth.ts +165 -0
- package/node_modules/pi-multi-account/cursor/bridge-handle.ts +155 -0
- package/node_modules/pi-multi-account/cursor/conversation-registry.ts +104 -0
- package/node_modules/pi-multi-account/cursor/cursor-models-raw.json +611 -0
- package/node_modules/pi-multi-account/cursor/cursor-shared.ts +192 -0
- package/node_modules/pi-multi-account/cursor/h2-bridge.mjs +175 -0
- package/node_modules/pi-multi-account/cursor/index.ts +572 -0
- package/node_modules/pi-multi-account/cursor/message-parsing.ts +323 -0
- package/node_modules/pi-multi-account/cursor/prompt-usage.ts +53 -0
- package/node_modules/pi-multi-account/cursor/proto/agent_pb.ts +15294 -0
- package/node_modules/pi-multi-account/cursor/proxy.ts +2510 -0
- package/node_modules/pi-multi-account/cursor/session-lifecycle.ts +40 -0
- package/node_modules/pi-multi-account/cursor/sse-keepalive.ts +24 -0
- package/node_modules/pi-multi-account/cursor/stream-lifecycle.ts +193 -0
- package/node_modules/pi-multi-account/cursor/upstream-watchdog.ts +88 -0
- package/node_modules/pi-multi-account/cursor-bridge.ts +240 -0
- package/node_modules/pi-multi-account/cursor-model-name.ts +12 -0
- package/node_modules/pi-multi-account/index.ts +11825 -0
- package/node_modules/pi-multi-account/model-catalog.ts +354 -0
- package/node_modules/pi-multi-account/package.json +101 -0
- package/node_modules/pi-multi-account/pi-contract.ts +281 -0
- package/node_modules/pi-multi-account/provider-payload-stream.ts +44 -0
- package/node_modules/pi-multi-account/provider-priority.ts +189 -0
- package/node_modules/pi-multi-account/slot-proxy-auth.ts +167 -0
- package/node_modules/pi-multi-account/slot-proxy.ts +344 -0
- package/node_modules/pi-multi-account/state-file-transaction.ts +67 -0
- package/node_modules/pi-multi-account/usage.ts +1099 -0
- package/node_modules/pi-typesafe/README.md +6 -2
- package/node_modules/pi-typesafe/dist/client.d.ts +11 -0
- package/node_modules/pi-typesafe/dist/client.js +45 -10
- package/node_modules/pi-typesafe/dist/index.d.ts +2 -2
- package/node_modules/pi-typesafe/dist/index.js +1 -1
- package/node_modules/pi-typesafe/package.json +2 -2
- package/node_modules/pi-web-access/CHANGELOG.md +36 -0
- package/node_modules/pi-web-access/README.md +75 -18
- package/node_modules/pi-web-access/anysearch.ts +4 -15
- package/node_modules/pi-web-access/bocha.ts +3 -22
- package/node_modules/pi-web-access/brave.ts +3 -21
- package/node_modules/pi-web-access/brightdata.ts +5 -32
- package/node_modules/pi-web-access/content-find.ts +168 -53
- package/node_modules/pi-web-access/curator-page.ts +4 -1
- package/node_modules/pi-web-access/curator-run.ts +2 -1
- package/node_modules/pi-web-access/curator-server.ts +1 -0
- package/node_modules/pi-web-access/dist/index.js +24620 -0
- package/node_modules/pi-web-access/domain-filter-normalization.ts +14 -0
- package/node_modules/pi-web-access/duckduckgo.ts +3 -21
- package/node_modules/pi-web-access/extract.ts +3 -1
- package/node_modules/pi-web-access/firecrawl.ts +5 -29
- package/node_modules/pi-web-access/gemini-search.ts +81 -32
- package/node_modules/pi-web-access/index.ts +149 -148
- package/node_modules/pi-web-access/jina-search.ts +4 -15
- package/node_modules/pi-web-access/kagi.ts +4 -13
- package/node_modules/pi-web-access/kimi-search.ts +5 -30
- package/node_modules/pi-web-access/mistral-search.ts +1 -15
- package/node_modules/pi-web-access/ollama.ts +2 -7
- package/node_modules/pi-web-access/openai-search.ts +174 -36
- package/node_modules/pi-web-access/opencode-session-headers.ts +24 -0
- package/node_modules/pi-web-access/package.json +10 -4
- package/node_modules/pi-web-access/page-query.ts +10 -2
- package/node_modules/pi-web-access/parallel.ts +1 -15
- package/node_modules/pi-web-access/pdf-extract.ts +3 -0
- package/node_modules/pi-web-access/querit.ts +5 -29
- package/node_modules/pi-web-access/search-answer-formatting.ts +11 -0
- package/node_modules/pi-web-access/search-result-count-normalization.ts +4 -0
- package/node_modules/pi-web-access/search1api.ts +5 -29
- package/node_modules/pi-web-access/searchinfinity.ts +5 -29
- package/node_modules/pi-web-access/searxng.ts +3 -21
- package/node_modules/pi-web-access/serpapi.ts +5 -28
- package/node_modules/pi-web-access/serpbase.ts +3 -22
- package/node_modules/pi-web-access/serpdive.ts +3 -21
- package/node_modules/pi-web-access/serper.ts +5 -28
- package/node_modules/pi-web-access/serply.ts +197 -0
- package/node_modules/pi-web-access/source-check.ts +11 -47
- package/node_modules/pi-web-access/summary-review.ts +7 -3
- package/node_modules/pi-web-access/tavily.ts +3 -21
- package/node_modules/pi-web-access/tinyfish.ts +5 -29
- package/node_modules/pi-web-access/utils.ts +9 -1
- package/node_modules/pi-web-access/valyu.ts +5 -28
- package/node_modules/pi-web-access/xai-search.ts +1 -15
- package/node_modules/pi-web-access/xcrawl.ts +5 -32
- package/package.json +23 -16
|
@@ -10,10 +10,10 @@ import { findContent, type FindMode } from "./content-find.ts";
|
|
|
10
10
|
import { answerFromPage } from "./page-query.ts";
|
|
11
11
|
import { rewriteSearchQuery } from "./query-rewrite.ts";
|
|
12
12
|
import { clearCloneCache } from "./github-extract.ts";
|
|
13
|
-
import { ALL_SEARCH_PROVIDERS, getConfiguredSearchRouting, normalizeSearchProviderSelection,
|
|
13
|
+
import { ALL_SEARCH_PROVIDERS, assertSearchProviderSelectionAllowed, getAllowedSearchProviders, getConfiguredSearchRouting, normalizeSearchProviderSelection, providerLabel, RESOLVED_SEARCH_PROVIDERS, search, type AttributedSearchResponse, type ProviderAvailability, type SearchProvider, type SearchProviderSelection, type ResolvedSearchProvider } from "./gemini-search.ts";
|
|
14
14
|
export type { ProviderAvailability } from "./gemini-search.ts";
|
|
15
15
|
import type { SearchResult } from "./perplexity.ts";
|
|
16
|
-
import { formatSeconds, getWebSearchConfigDir, getWebSearchConfigPath,
|
|
16
|
+
import { formatSeconds, getWebSearchConfigDir, getWebSearchConfigPath, resolveCuratorNetworkConfig, runWithProxy } from "./utils.ts";
|
|
17
17
|
import {
|
|
18
18
|
clearResults,
|
|
19
19
|
deleteResult,
|
|
@@ -71,6 +71,7 @@ import { isBrightDataAvailable } from "./brightdata.ts";
|
|
|
71
71
|
import { isSerpBaseAvailable } from "./serpbase.ts";
|
|
72
72
|
import { isSerpApiAvailable } from "./serpapi.ts";
|
|
73
73
|
import { isSerperAvailable } from "./serper.ts";
|
|
74
|
+
import { isSerplyAvailable } from "./serply.ts";
|
|
74
75
|
import { isValyuAvailable } from "./valyu.ts";
|
|
75
76
|
import { isXcrawlAvailable } from "./xcrawl.ts";
|
|
76
77
|
import { buildSearchErrorPlan, type SearchErrorDetails, type SearchErrorPlan } from "./render-search-error.ts";
|
|
@@ -153,6 +154,7 @@ interface WebSearchConfig {
|
|
|
153
154
|
serpbaseApiKey?: unknown;
|
|
154
155
|
serpapiApiKey?: unknown;
|
|
155
156
|
serperApiKey?: unknown;
|
|
157
|
+
serplyApiKey?: unknown;
|
|
156
158
|
tinyfishApiKey?: unknown;
|
|
157
159
|
valyuApiKey?: unknown;
|
|
158
160
|
xaiApiKey?: unknown;
|
|
@@ -165,8 +167,13 @@ interface WebSearchConfig {
|
|
|
165
167
|
summaryModel?: string;
|
|
166
168
|
summaryGenerationDeadlineMs?: unknown;
|
|
167
169
|
maxInlineContentChars?: unknown;
|
|
170
|
+
fetch?: {
|
|
171
|
+
defaultMode?: unknown;
|
|
172
|
+
allowedModes?: unknown;
|
|
173
|
+
};
|
|
168
174
|
webSearch?: {
|
|
169
175
|
enabled?: boolean;
|
|
176
|
+
allowedProviders?: unknown;
|
|
170
177
|
};
|
|
171
178
|
tools?: Partial<Record<keyof ToolNames, { enabled?: boolean }>>;
|
|
172
179
|
commands?: Partial<Record<"websearch" | "curator" | "search" | "google-account", { enabled?: boolean }>>;
|
|
@@ -184,12 +191,12 @@ interface WebSearchConfig {
|
|
|
184
191
|
}
|
|
185
192
|
|
|
186
193
|
type CuratorWorkflow = "summary-review";
|
|
187
|
-
export type CuratorProvider =
|
|
194
|
+
export type CuratorProvider = SearchProvider;
|
|
188
195
|
type SummaryWorkflow = "summary-review" | "auto-summary";
|
|
189
196
|
|
|
190
197
|
interface CuratorBootstrap {
|
|
191
198
|
availableProviders: ProviderAvailability;
|
|
192
|
-
defaultProvider:
|
|
199
|
+
defaultProvider: SearchProvider;
|
|
193
200
|
timeoutSeconds: number;
|
|
194
201
|
}
|
|
195
202
|
|
|
@@ -244,6 +251,30 @@ const DEFAULT_REMOTE_CURATOR_TIMEOUT_SECONDS = 60;
|
|
|
244
251
|
const MAX_CURATOR_TIMEOUT_SECONDS = 600;
|
|
245
252
|
const MAX_SUMMARY_GENERATION_DEADLINE_MS = 600_000;
|
|
246
253
|
const SEARCH_QUERY_CONCURRENCY = 3;
|
|
254
|
+
const FETCH_MODES = ["readable", "raw", "answer"] as const;
|
|
255
|
+
type FetchMode = typeof FETCH_MODES[number];
|
|
256
|
+
const FETCH_MODE_DESCRIPTIONS: Record<FetchMode, string> = {
|
|
257
|
+
readable: "extract readable content as markdown",
|
|
258
|
+
raw: "return the exact textual body using direct HTTP only",
|
|
259
|
+
answer: "answer a prompt using only fetched content",
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
function resolveFetchModeConfig(config: WebSearchConfig): { defaultMode: FetchMode; allowedModes: FetchMode[] } {
|
|
263
|
+
const configuredModes = config.fetch?.allowedModes ?? FETCH_MODES;
|
|
264
|
+
if (!Array.isArray(configuredModes) || configuredModes.length === 0 || configuredModes.some(mode => !FETCH_MODES.includes(mode as FetchMode))) {
|
|
265
|
+
throw new Error(`fetch.allowedModes in ${WEB_SEARCH_CONFIG_PATH} must be a non-empty array containing only "readable", "raw", or "answer"`);
|
|
266
|
+
}
|
|
267
|
+
const allowedModes = configuredModes as FetchMode[];
|
|
268
|
+
const duplicateMode = allowedModes.find((mode, index) => allowedModes.indexOf(mode) !== index);
|
|
269
|
+
if (duplicateMode) {
|
|
270
|
+
throw new Error(`fetch.allowedModes in ${WEB_SEARCH_CONFIG_PATH} must not contain duplicates: "${duplicateMode}"`);
|
|
271
|
+
}
|
|
272
|
+
const defaultMode = config.fetch?.defaultMode ?? "readable";
|
|
273
|
+
if (!allowedModes.includes(defaultMode as FetchMode)) {
|
|
274
|
+
throw new Error(`fetch.defaultMode in ${WEB_SEARCH_CONFIG_PATH} must be one of fetch.allowedModes`);
|
|
275
|
+
}
|
|
276
|
+
return { defaultMode: defaultMode as FetchMode, allowedModes };
|
|
277
|
+
}
|
|
247
278
|
|
|
248
279
|
// Limit each batch independently so separate Pi tool calls can still run in parallel.
|
|
249
280
|
function runSearchQueries<T>(queries: string[], run: (query: string, index: number) => Promise<T>): Promise<T[]> {
|
|
@@ -261,10 +292,10 @@ function curatorResultIndexCapacity(queryCount: number): number {
|
|
|
261
292
|
return queryCount * RESOLVED_SEARCH_PROVIDERS.length;
|
|
262
293
|
}
|
|
263
294
|
|
|
264
|
-
function searchProviderSchema(description: string) {
|
|
295
|
+
function searchProviderSchema(description: string, allowedProviders: readonly ResolvedSearchProvider[]) {
|
|
265
296
|
return Type.Union([
|
|
266
|
-
StringEnum([...
|
|
267
|
-
Type.Array(StringEnum([...
|
|
297
|
+
StringEnum(["auto", "all", ...allowedProviders]),
|
|
298
|
+
Type.Array(StringEnum([...allowedProviders]), { minItems: 1 }),
|
|
268
299
|
], { description });
|
|
269
300
|
}
|
|
270
301
|
|
|
@@ -329,12 +360,17 @@ function normalizeProviderInput(value: unknown, label = "provider"): SearchProvi
|
|
|
329
360
|
|
|
330
361
|
function resolveRequestedProvider(requested: unknown): SearchProviderSelection {
|
|
331
362
|
const normalizedRequested = normalizeProviderInput(requested);
|
|
332
|
-
if (normalizedRequested && normalizedRequested !== "auto")
|
|
363
|
+
if (normalizedRequested && normalizedRequested !== "auto") {
|
|
364
|
+
assertSearchProviderSelectionAllowed(normalizedRequested, "Requested provider");
|
|
365
|
+
return normalizedRequested;
|
|
366
|
+
}
|
|
333
367
|
const config = loadConfig();
|
|
334
|
-
|
|
368
|
+
const provider = normalizeProviderInput(config.searchProvider ?? config.provider, `provider in ${WEB_SEARCH_CONFIG_PATH}`) ?? "auto";
|
|
369
|
+
assertSearchProviderSelectionAllowed(provider, `configured provider in ${WEB_SEARCH_CONFIG_PATH}`);
|
|
370
|
+
return provider;
|
|
335
371
|
}
|
|
336
372
|
|
|
337
|
-
function toCuratorProvider(provider: SearchProviderSelection):
|
|
373
|
+
function toCuratorProvider(provider: SearchProviderSelection): SearchProvider | undefined {
|
|
338
374
|
if (Array.isArray(provider)) return "all";
|
|
339
375
|
return provider === "auto" ? undefined : provider;
|
|
340
376
|
}
|
|
@@ -342,8 +378,7 @@ function toCuratorProvider(provider: SearchProviderSelection): CuratorProvider |
|
|
|
342
378
|
function resolveCuratorSearchProvider(requested: unknown, current: SearchProviderSelection): SearchProviderSelection {
|
|
343
379
|
const normalized = normalizeProviderInput(requested);
|
|
344
380
|
if (!normalized || normalized === "auto") return current;
|
|
345
|
-
|
|
346
|
-
return normalized;
|
|
381
|
+
return normalized === "all" && Array.isArray(current) ? current : normalized;
|
|
347
382
|
}
|
|
348
383
|
|
|
349
384
|
function normalizeRecencyFilter(value: unknown): RecencyFilter | undefined {
|
|
@@ -419,43 +454,44 @@ function shouldAutoOpenCuratorBrowser(config: WebSearchConfig): boolean {
|
|
|
419
454
|
}
|
|
420
455
|
|
|
421
456
|
async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderAvailability> {
|
|
422
|
-
const
|
|
423
|
-
const
|
|
457
|
+
const allowedProviders = new Set(getAllowedSearchProviders());
|
|
458
|
+
const geminiWebAvail = allowedProviders.has("gemini") ? await getOptionalGeminiWebAvailability() : null;
|
|
459
|
+
const geminiApiAvail = allowedProviders.has("gemini") && isGeminiApiAvailable();
|
|
424
460
|
const providers = {
|
|
425
|
-
openai: await isOpenAISearchAvailable(ctx),
|
|
426
|
-
brave: isBraveAvailable(),
|
|
427
|
-
parallel: isParallelAvailable(),
|
|
428
|
-
"parallel-mcp": isParallelMcpAvailable(),
|
|
429
|
-
tinyfish: isTinyFishAvailable(),
|
|
430
|
-
search1api: isSearch1APIAvailable(),
|
|
431
|
-
searchinfinity: isSearchinfinityAvailable(),
|
|
432
|
-
querit: isQueritAvailable(),
|
|
433
|
-
tavily: isTavilyAvailable(),
|
|
434
|
-
firecrawl: isFirecrawlAvailable(),
|
|
435
|
-
jina: isJinaSearchAvailable(),
|
|
436
|
-
serpdive: isSerpdiveAvailable(),
|
|
437
|
-
kagi: isKagiAvailable(),
|
|
438
|
-
bocha: isBochaAvailable(),
|
|
439
|
-
ollama: isOllamaAvailable(),
|
|
440
|
-
searxng: isSearXNGAvailable(),
|
|
441
|
-
duckduckgo: isDuckDuckGoAvailable(),
|
|
442
|
-
perplexity: isPerplexityAvailable(),
|
|
443
|
-
exa: isExaAvailable(),
|
|
461
|
+
openai: allowedProviders.has("openai") && await isOpenAISearchAvailable(ctx),
|
|
462
|
+
brave: allowedProviders.has("brave") && isBraveAvailable(),
|
|
463
|
+
parallel: allowedProviders.has("parallel") && isParallelAvailable(),
|
|
464
|
+
"parallel-mcp": allowedProviders.has("parallel-mcp") && isParallelMcpAvailable(),
|
|
465
|
+
tinyfish: allowedProviders.has("tinyfish") && isTinyFishAvailable(),
|
|
466
|
+
search1api: allowedProviders.has("search1api") && isSearch1APIAvailable(),
|
|
467
|
+
searchinfinity: allowedProviders.has("searchinfinity") && isSearchinfinityAvailable(),
|
|
468
|
+
querit: allowedProviders.has("querit") && isQueritAvailable(),
|
|
469
|
+
tavily: allowedProviders.has("tavily") && isTavilyAvailable(),
|
|
470
|
+
firecrawl: allowedProviders.has("firecrawl") && isFirecrawlAvailable(),
|
|
471
|
+
jina: allowedProviders.has("jina") && isJinaSearchAvailable(),
|
|
472
|
+
serpdive: allowedProviders.has("serpdive") && isSerpdiveAvailable(),
|
|
473
|
+
kagi: allowedProviders.has("kagi") && isKagiAvailable(),
|
|
474
|
+
bocha: allowedProviders.has("bocha") && isBochaAvailable(),
|
|
475
|
+
ollama: allowedProviders.has("ollama") && isOllamaAvailable(),
|
|
476
|
+
searxng: allowedProviders.has("searxng") && isSearXNGAvailable(),
|
|
477
|
+
duckduckgo: allowedProviders.has("duckduckgo") && isDuckDuckGoAvailable(),
|
|
478
|
+
perplexity: allowedProviders.has("perplexity") && isPerplexityAvailable(),
|
|
479
|
+
exa: allowedProviders.has("exa") && isExaAvailable(),
|
|
444
480
|
gemini: geminiApiAvail || !!geminiWebAvail,
|
|
445
|
-
kimi: await isKimiSearchAvailable(ctx),
|
|
446
|
-
anysearch: isAnySearchAvailable(),
|
|
447
|
-
xcrawl: isXcrawlAvailable(),
|
|
448
|
-
xai: await isXaiSearchAvailable(ctx),
|
|
449
|
-
mistral: isMistralAvailable(),
|
|
450
|
-
brightdata: isBrightDataAvailable(),
|
|
451
|
-
serpbase: isSerpBaseAvailable(),
|
|
452
|
-
serpapi: isSerpApiAvailable(),
|
|
453
|
-
serper: isSerperAvailable(),
|
|
454
|
-
|
|
481
|
+
kimi: allowedProviders.has("kimi") && await isKimiSearchAvailable(ctx),
|
|
482
|
+
anysearch: allowedProviders.has("anysearch") && isAnySearchAvailable(),
|
|
483
|
+
xcrawl: allowedProviders.has("xcrawl") && isXcrawlAvailable(),
|
|
484
|
+
xai: allowedProviders.has("xai") && await isXaiSearchAvailable(ctx),
|
|
485
|
+
mistral: allowedProviders.has("mistral") && isMistralAvailable(),
|
|
486
|
+
brightdata: allowedProviders.has("brightdata") && isBrightDataAvailable(),
|
|
487
|
+
serpbase: allowedProviders.has("serpbase") && isSerpBaseAvailable(),
|
|
488
|
+
serpapi: allowedProviders.has("serpapi") && isSerpApiAvailable(),
|
|
489
|
+
serper: allowedProviders.has("serper") && isSerperAvailable(),
|
|
490
|
+
serply: allowedProviders.has("serply") && isSerplyAvailable(),
|
|
491
|
+
valyu: allowedProviders.has("valyu") && isValyuAvailable(),
|
|
455
492
|
};
|
|
456
|
-
const allSearchProviders = new Set<ResolvedSearchProvider>(ALL_SEARCH_PROVIDERS);
|
|
457
493
|
return {
|
|
458
|
-
all:
|
|
494
|
+
all: ALL_SEARCH_PROVIDERS.some(provider => provider === "gemini" ? geminiApiAvail : providers[provider]),
|
|
459
495
|
...providers,
|
|
460
496
|
};
|
|
461
497
|
}
|
|
@@ -500,31 +536,20 @@ export function resolveCuratorDefaultProvider(
|
|
|
500
536
|
available: ProviderAvailability,
|
|
501
537
|
ctx?: Pick<ExtensionContext, "model">,
|
|
502
538
|
options?: Pick<PendingCurate, "numResults" | "recencyFilter">,
|
|
503
|
-
):
|
|
539
|
+
): SearchProvider {
|
|
504
540
|
return resolveProvider(provider, available, options, shouldUseOpenAICodexDefault(ctx), ctx);
|
|
505
541
|
}
|
|
506
542
|
|
|
507
|
-
function firstAvailableProvider(available: ProviderAvailability, preferOpenAI: boolean, fallback: ResolvedSearchProvider): ResolvedSearchProvider {
|
|
543
|
+
function firstAvailableProvider(available: ProviderAvailability, preferOpenAI: boolean, fallback: ResolvedSearchProvider): ResolvedSearchProvider | "auto" {
|
|
508
544
|
if (available.searxng) return "searxng";
|
|
509
545
|
if (preferOpenAI && available.openai) return "openai";
|
|
510
546
|
if (available.exa) return "exa";
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (available.querit) return "querit";
|
|
518
|
-
if (available.tavily) return "tavily";
|
|
519
|
-
if (available.firecrawl) return "firecrawl";
|
|
520
|
-
if (available.jina) return "jina";
|
|
521
|
-
if (available.serpdive) return "serpdive";
|
|
522
|
-
if (available.kagi) return "kagi";
|
|
523
|
-
if (available.bocha) return "bocha";
|
|
524
|
-
if (available.ollama) return "ollama";
|
|
525
|
-
if (available.perplexity) return "perplexity";
|
|
526
|
-
if (available.gemini) return "gemini";
|
|
527
|
-
return fallback;
|
|
547
|
+
for (const provider of ALL_SEARCH_PROVIDERS) {
|
|
548
|
+
if (provider === "ollama" && available.bocha) return "bocha";
|
|
549
|
+
if (available[provider]) return provider;
|
|
550
|
+
}
|
|
551
|
+
const allowed = getAllowedSearchProviders();
|
|
552
|
+
return allowed.includes(fallback) ? fallback : "auto";
|
|
528
553
|
}
|
|
529
554
|
|
|
530
555
|
function resolveProvider(
|
|
@@ -533,7 +558,7 @@ function resolveProvider(
|
|
|
533
558
|
options?: Pick<PendingCurate, "numResults" | "recencyFilter">,
|
|
534
559
|
preferOpenAICodexDefault = false,
|
|
535
560
|
ctx?: Pick<ExtensionContext, "model">,
|
|
536
|
-
):
|
|
561
|
+
): SearchProvider {
|
|
537
562
|
if (Array.isArray(provider)) return "all";
|
|
538
563
|
const preferOpenAI = shouldPreferOpenAI(options, preferOpenAICodexDefault);
|
|
539
564
|
|
|
@@ -548,62 +573,11 @@ function resolveProvider(
|
|
|
548
573
|
}
|
|
549
574
|
return firstAvailableProvider(available, preferOpenAI, "exa");
|
|
550
575
|
}
|
|
551
|
-
if (provider === "all"
|
|
552
|
-
return firstAvailableProvider(available, preferOpenAI, "exa");
|
|
553
|
-
}
|
|
554
|
-
if (provider === "openai" && !available.openai) {
|
|
555
|
-
return firstAvailableProvider(available, false, "openai");
|
|
556
|
-
}
|
|
557
|
-
if (provider === "brave" && !available.brave) {
|
|
558
|
-
return firstAvailableProvider(available, preferOpenAI, "brave");
|
|
559
|
-
}
|
|
560
|
-
if (provider === "parallel" && !available.parallel) {
|
|
561
|
-
return firstAvailableProvider(available, preferOpenAI, "parallel");
|
|
562
|
-
}
|
|
563
|
-
if (provider === "tinyfish" && !available.tinyfish) {
|
|
564
|
-
return firstAvailableProvider(available, preferOpenAI, "tinyfish");
|
|
565
|
-
}
|
|
566
|
-
if (provider === "search1api" && !available.search1api) {
|
|
567
|
-
return firstAvailableProvider(available, preferOpenAI, "search1api");
|
|
568
|
-
}
|
|
569
|
-
if (provider === "searchinfinity" && !available.searchinfinity) {
|
|
570
|
-
return firstAvailableProvider(available, preferOpenAI, "searchinfinity");
|
|
571
|
-
}
|
|
572
|
-
if (provider === "querit" && !available.querit) {
|
|
573
|
-
return firstAvailableProvider(available, preferOpenAI, "querit");
|
|
574
|
-
}
|
|
575
|
-
if (provider === "tavily" && !available.tavily) {
|
|
576
|
-
return firstAvailableProvider(available, preferOpenAI, "tavily");
|
|
577
|
-
}
|
|
578
|
-
if (provider === "firecrawl" && !available.firecrawl) {
|
|
579
|
-
return firstAvailableProvider(available, preferOpenAI, "firecrawl");
|
|
580
|
-
}
|
|
581
|
-
if (provider === "jina" && !available.jina) {
|
|
582
|
-
return firstAvailableProvider(available, preferOpenAI, "jina");
|
|
583
|
-
}
|
|
584
|
-
if (provider === "serpdive" && !available.serpdive) {
|
|
585
|
-
return firstAvailableProvider(available, preferOpenAI, "serpdive");
|
|
586
|
-
}
|
|
587
|
-
if (provider === "kagi" && !available.kagi) {
|
|
588
|
-
return firstAvailableProvider(available, preferOpenAI, "kagi");
|
|
589
|
-
}
|
|
590
|
-
if (provider === "bocha" && !available.bocha) {
|
|
591
|
-
return firstAvailableProvider(available, preferOpenAI, "bocha");
|
|
592
|
-
}
|
|
593
|
-
if (provider === "ollama" && !available.ollama) {
|
|
594
|
-
return firstAvailableProvider(available, preferOpenAI, "ollama");
|
|
595
|
-
}
|
|
596
|
-
if (provider === "searxng" && !available.searxng) {
|
|
597
|
-
return firstAvailableProvider(available, preferOpenAI, "searxng");
|
|
598
|
-
}
|
|
599
|
-
if (provider === "exa" && !available.exa) {
|
|
600
|
-
return firstAvailableProvider(available, preferOpenAI, "exa");
|
|
601
|
-
}
|
|
602
|
-
if (provider === "perplexity" && !available.perplexity) {
|
|
603
|
-
return firstAvailableProvider(available, preferOpenAI, "perplexity");
|
|
576
|
+
if (provider === "all") {
|
|
577
|
+
return available.all ? "all" : firstAvailableProvider(available, preferOpenAI, "exa");
|
|
604
578
|
}
|
|
605
|
-
if (provider
|
|
606
|
-
return firstAvailableProvider(available, preferOpenAI,
|
|
579
|
+
if (ALL_SEARCH_PROVIDERS.includes(provider) && !available[provider]) {
|
|
580
|
+
return firstAvailableProvider(available, provider === "openai" ? false : preferOpenAI, provider);
|
|
607
581
|
}
|
|
608
582
|
return provider;
|
|
609
583
|
}
|
|
@@ -629,7 +603,7 @@ interface PendingCurate {
|
|
|
629
603
|
recencyFilter?: "day" | "week" | "month" | "year";
|
|
630
604
|
domainFilter?: string[];
|
|
631
605
|
availableProviders: ProviderAvailability;
|
|
632
|
-
defaultProvider:
|
|
606
|
+
defaultProvider: SearchProvider;
|
|
633
607
|
searchProvider: SearchProviderSelection;
|
|
634
608
|
summaryModels: Array<{ value: string; label: string }>;
|
|
635
609
|
defaultSummaryModel: string | null;
|
|
@@ -1063,8 +1037,16 @@ function handleSessionChange(ctx: ExtensionContext): void {
|
|
|
1063
1037
|
|
|
1064
1038
|
export default function (pi: ExtensionAPI) {
|
|
1065
1039
|
const initConfig = loadConfigForExtensionInit();
|
|
1040
|
+
const fetchModeConfig = resolveFetchModeConfig(initConfig);
|
|
1041
|
+
const allowedSearchProviders = initConfig.webSearch?.allowedProviders === undefined ? RESOLVED_SEARCH_PROVIDERS : getAllowedSearchProviders();
|
|
1042
|
+
const allEligibleProviders = allowedSearchProviders.filter(provider => ALL_SEARCH_PROVIDERS.includes(provider));
|
|
1043
|
+
const allExcludedProviders = allowedSearchProviders.filter(provider => !ALL_SEARCH_PROVIDERS.includes(provider));
|
|
1044
|
+
const allPolicyDescription = allEligibleProviders.length === 0
|
|
1045
|
+
? `all has no eligible allowed providers; explicit-only allowed providers (${allExcludedProviders.map(providerLabel).join(", ")}) remain excluded`
|
|
1046
|
+
: allExcludedProviders.length > 0
|
|
1047
|
+
? `all searches eligible allowed providers (${allEligibleProviders.map(providerLabel).join(", ")}); explicit-only allowed providers (${allExcludedProviders.map(providerLabel).join(", ")}) remain excluded`
|
|
1048
|
+
: `all searches every eligible allowed provider (${allEligibleProviders.map(providerLabel).join(", ")})`;
|
|
1066
1049
|
const curatorRunState = registerCuratorRunLifecycle(pi);
|
|
1067
|
-
installGlobalProxyFetch();
|
|
1068
1050
|
const toolNames = resolveToolNames(initConfig);
|
|
1069
1051
|
const webSearchEnabled = isToolEnabled(initConfig, "webSearch");
|
|
1070
1052
|
const sourceCheckEnabled = isToolEnabled(initConfig, "sourceCheck");
|
|
@@ -1087,6 +1069,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
1087
1069
|
const fetchContentStorageNote = getSearchContentEnabled
|
|
1088
1070
|
? `Full original content is stored for retrieval with ${toolNames.getSearchContent}.`
|
|
1089
1071
|
: "Full original content is stored internally, but the retrieval tool is not registered.";
|
|
1072
|
+
const fetchModeDescription = fetchModeConfig.allowedModes
|
|
1073
|
+
.map(mode => `${mode}${mode === fetchModeConfig.defaultMode ? " (default)" : ""}: ${FETCH_MODE_DESCRIPTIONS[mode]}`)
|
|
1074
|
+
.join("; ");
|
|
1090
1075
|
const curateKey = initConfig.shortcuts?.curate || DEFAULT_SHORTCUTS.curate;
|
|
1091
1076
|
const activityKey = initConfig.shortcuts?.activity || DEFAULT_SHORTCUTS.activity;
|
|
1092
1077
|
|
|
@@ -1790,22 +1775,22 @@ export default function (pi: ExtensionAPI) {
|
|
|
1790
1775
|
name: toolNames.webSearch,
|
|
1791
1776
|
label: "Web Search",
|
|
1792
1777
|
description:
|
|
1793
|
-
`Search the web
|
|
1778
|
+
`Search the web with ${allowedSearchProviders.map(providerLabel).join(", ")}. Provider arrays run simultaneously; ${allPolicyDescription}. By default, returns source-linked search results or provider answers. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query. When includeContent is true, full page content is fetched in the background. Searches return without the interactive browser curator by default; set workflow to "summary-review" to open the curator with an auto-generated summary draft or "auto-summary" to generate a summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it.`,
|
|
1794
1779
|
promptSnippet:
|
|
1795
1780
|
"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.",
|
|
1796
1781
|
parameters: Type.Object({
|
|
1797
1782
|
query: Type.Optional(Type.String({ description: "Single search query. For research tasks, prefer 'queries' with multiple varied angles instead." })),
|
|
1798
|
-
queries: Type.Optional(Type.Array(Type.String(), { description: "Multiple queries searched concurrently (up to three at a time), each returning
|
|
1783
|
+
queries: Type.Optional(Type.Array(Type.String(), { description: "Multiple queries searched concurrently (up to three at a time), each returning source-linked search results or a provider answer. Prefer this for research — vary phrasing, scope, and angle across 2-4 queries to maximize coverage. Good: ['React vs Vue performance benchmarks 2026', 'React vs Vue developer experience comparison', 'React ecosystem size vs Vue ecosystem']. Bad: ['React vs Vue', 'React vs Vue comparison', 'React vs Vue review'] (too similar, redundant results)." })),
|
|
1799
1784
|
numResults: Type.Optional(Type.Integer({ minimum: 1, maximum: 20, description: "Results per query (default: 5, max: 20)" })),
|
|
1800
1785
|
includeContent: Type.Optional(Type.Boolean({ description: "Fetch full page content (async)" })),
|
|
1801
1786
|
recencyFilter: Type.Optional(
|
|
1802
1787
|
StringEnum(["day", "week", "month", "year"], { description: "Filter by recency" }),
|
|
1803
1788
|
),
|
|
1804
1789
|
domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
|
|
1805
|
-
provider: Type.Optional(searchProviderSchema(
|
|
1790
|
+
provider: Type.Optional(searchProviderSchema(`Search provider or non-empty list of allowed providers to search simultaneously; ${allPolicyDescription}; omit this field to use the configured provider, or use auto when none is configured`, allowedSearchProviders)),
|
|
1806
1791
|
workflow: Type.Optional(
|
|
1807
1792
|
StringEnum(["none", "summary-review", "auto-summary"], {
|
|
1808
|
-
description: "Search workflow mode: none = no curator, summary-review = open curator with auto summary draft
|
|
1793
|
+
description: "Search workflow mode: none = no curator (default), summary-review = open curator with auto summary draft, auto-summary = generate summary without opening curator",
|
|
1809
1794
|
}),
|
|
1810
1795
|
),
|
|
1811
1796
|
proxy: Type.Optional(Type.String({
|
|
@@ -1869,6 +1854,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1869
1854
|
const summaryContext: SummaryGenerationContext = {
|
|
1870
1855
|
model: ctx.model,
|
|
1871
1856
|
modelRegistry: ctx.modelRegistry,
|
|
1857
|
+
sessionManager: ctx.sessionManager,
|
|
1872
1858
|
cwd: ctx.cwd,
|
|
1873
1859
|
isProjectTrusted: () => ctx.isProjectTrusted(),
|
|
1874
1860
|
};
|
|
@@ -2097,6 +2083,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2097
2083
|
const summaryContext: SummaryGenerationContext = {
|
|
2098
2084
|
model: ctx.model,
|
|
2099
2085
|
modelRegistry: ctx.modelRegistry,
|
|
2086
|
+
sessionManager: ctx.sessionManager,
|
|
2100
2087
|
cwd: ctx.cwd,
|
|
2101
2088
|
isProjectTrusted: () => ctx.isProjectTrusted(),
|
|
2102
2089
|
};
|
|
@@ -2387,16 +2374,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
2387
2374
|
if (sourceCheckEnabled) pi.registerTool({
|
|
2388
2375
|
name: toolNames.sourceCheck,
|
|
2389
2376
|
label: "Source Check",
|
|
2390
|
-
description: "
|
|
2391
|
-
promptSnippet: "
|
|
2377
|
+
description: "Gather web sources for a claim and return a bounded machine-readable research artifact with exact passage citations for manual review.",
|
|
2378
|
+
promptSnippet: "Gather structured source evidence and passage-level citations for manual semantic review of a claim.",
|
|
2392
2379
|
parameters: Type.Object({
|
|
2393
|
-
claim: Type.String({ description: "The assertion to
|
|
2380
|
+
claim: Type.String({ description: "The assertion to gather web sources for." }),
|
|
2394
2381
|
queries: Type.Optional(Type.Array(Type.String(), { description: "Search queries (default: the claim)." })),
|
|
2395
2382
|
numResults: Type.Optional(Type.Integer({ minimum: 1, maximum: 20, description: "Results per query (default: 5, max: 20)." })),
|
|
2396
2383
|
fetchContent: Type.Optional(Type.Boolean({ description: "Fetch up to 5 result pages for exact passage extraction." })),
|
|
2397
2384
|
recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency." })),
|
|
2398
2385
|
domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains; prefix with - to exclude." })),
|
|
2399
|
-
provider: Type.Optional(searchProviderSchema(
|
|
2386
|
+
provider: Type.Optional(searchProviderSchema(`Search provider or non-empty list of allowed providers to search simultaneously; ${allPolicyDescription}`, allowedSearchProviders)),
|
|
2400
2387
|
proxy: Type.Optional(Type.String({
|
|
2401
2388
|
description: "http(s) or socks proxy URL (e.g. http://host:port or socks5h://host:port) used for every outbound request in this call (search APIs and result-page fetches). Empty string forces direct access.",
|
|
2402
2389
|
})),
|
|
@@ -2486,9 +2473,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
2486
2473
|
if (fetchContentEnabled) pi.registerTool({
|
|
2487
2474
|
name: toolNames.fetchContent,
|
|
2488
2475
|
label: "Fetch Content",
|
|
2489
|
-
description: `Fetch URL(s)
|
|
2476
|
+
description: `Fetch URL(s). Available modes: ${fetchModeDescription}. Direct image URLs return resized image content when supported by the selected mode. Supports YouTube transcripts, GitHub repositories, PDFs, and local videos when supported by the selected mode. ${fetchContentStorageNote}`,
|
|
2490
2477
|
promptSnippet:
|
|
2491
|
-
"Use to fetch
|
|
2478
|
+
"Use to fetch URL content, direct images, GitHub repos, and videos.",
|
|
2492
2479
|
parameters: Type.Object({
|
|
2493
2480
|
url: Type.Optional(Type.String({ description: "Single URL to fetch" })),
|
|
2494
2481
|
urls: Type.Optional(Type.Array(Type.String(), { description: "Multiple URLs (parallel)" })),
|
|
@@ -2496,14 +2483,18 @@ export default function (pi: ExtensionAPI) {
|
|
|
2496
2483
|
description: "Force cloning large GitHub repositories that exceed the size threshold",
|
|
2497
2484
|
})),
|
|
2498
2485
|
prompt: Type.Optional(Type.String({
|
|
2499
|
-
description: "
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
description: "Fetch mode: readable (default extraction), raw (exact textual HTTP body), or answer (answer prompt using only fetched content).",
|
|
2486
|
+
description: fetchModeConfig.allowedModes.includes("answer")
|
|
2487
|
+
? "Question or instruction for video analysis, or the page-local question required by answer mode."
|
|
2488
|
+
: "Question or instruction for video analysis.",
|
|
2503
2489
|
})),
|
|
2504
|
-
|
|
2505
|
-
description:
|
|
2490
|
+
mode: Type.Optional(StringEnum(fetchModeConfig.allowedModes, {
|
|
2491
|
+
description: `Fetch mode. ${fetchModeDescription}.`,
|
|
2506
2492
|
})),
|
|
2493
|
+
...(fetchModeConfig.allowedModes.includes("answer") ? {
|
|
2494
|
+
answerModel: Type.Optional(Type.String({
|
|
2495
|
+
description: "Optional provider/model-id override for answer mode. Defaults to fetch.answerProvider + fetch.answerModel when configured, otherwise the current Pi model.",
|
|
2496
|
+
})),
|
|
2497
|
+
} : {}),
|
|
2507
2498
|
timestamp: Type.Optional(Type.String({
|
|
2508
2499
|
description: "Extract video frame(s) at a timestamp or time range. Single: '1:23:45', '23:45', or '85' (seconds). Range: '23:41-25:00' extracts evenly-spaced frames across that span (default 6). Use frames with ranges to control density; single+frames uses a fixed 5s interval. YouTube requires yt-dlp + ffmpeg; local videos require ffmpeg. Use a range when you know the approximate area but not the exact moment — you'll get a contact sheet to visually identify the right frame.",
|
|
2509
2500
|
})),
|
|
@@ -2532,8 +2523,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
2532
2523
|
return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
|
|
2533
2524
|
}
|
|
2534
2525
|
const { urlList, options } = normalized;
|
|
2526
|
+
const mode = options.mode ?? fetchModeConfig.defaultMode;
|
|
2527
|
+
if (!fetchModeConfig.allowedModes.includes(mode)) {
|
|
2528
|
+
const error = `Fetch mode "${mode}" is disabled by fetch.allowedModes.`;
|
|
2529
|
+
return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
|
|
2530
|
+
}
|
|
2535
2531
|
return runWithProxy(options.proxy, async () => {
|
|
2536
|
-
const mode = options.mode ?? "readable";
|
|
2537
2532
|
if (mode === "answer" && !options.prompt) {
|
|
2538
2533
|
return { content: [{ type: "text", text: "Error: mode answer requires prompt." }], details: { error: "mode answer requires prompt" } };
|
|
2539
2534
|
}
|
|
@@ -2570,13 +2565,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
2570
2565
|
details: { phase: "fetch", progress: 0 },
|
|
2571
2566
|
});
|
|
2572
2567
|
|
|
2573
|
-
const { answerModel: _answerModel, auth: _auth, ...extractionOptions } = options;
|
|
2574
|
-
const
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
: { ...extractionOptions, ...(authFetchProfile ? { authFetchProfile } : {}) };
|
|
2568
|
+
const { answerModel: _answerModel, auth: _auth, ...extractionOptions } = { ...options, mode };
|
|
2569
|
+
const { prompt: _prompt, ...answerExtractionOptions } = extractionOptions;
|
|
2570
|
+
const fetchOptions = {
|
|
2571
|
+
...(mode === "answer" ? answerExtractionOptions : extractionOptions),
|
|
2572
|
+
...(authFetchProfile ? { authFetchProfile } : {}),
|
|
2573
|
+
};
|
|
2580
2574
|
const fetchResults = await fetchAllContent(urlList, signal, withRegisteredFetchOptions(fetchOptions, registeredToolNames, options.proxy));
|
|
2581
2575
|
const presentedResults = mode === "answer"
|
|
2582
2576
|
? await Promise.all(fetchResults.map(async result => {
|
|
@@ -2693,7 +2687,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
2693
2687
|
},
|
|
2694
2688
|
|
|
2695
2689
|
renderCall(args, theme) {
|
|
2696
|
-
|
|
2690
|
+
let normalized: ReturnType<typeof normalizeFetchContentParams>;
|
|
2691
|
+
try {
|
|
2692
|
+
normalized = normalizeFetchContentParams(args);
|
|
2693
|
+
} catch {
|
|
2694
|
+
return new Text(theme.fg("toolTitle", theme.bold("fetch ")) + theme.fg("error", "(invalid parameters)"), 0, 0);
|
|
2695
|
+
}
|
|
2696
|
+
const { urlList, options } = normalized;
|
|
2697
2697
|
const { prompt, timestamp, frames, model, mode, answerModel, auth } = options;
|
|
2698
2698
|
if (urlList.length === 0) {
|
|
2699
2699
|
return new Text(theme.fg("toolTitle", theme.bold("fetch ")) + theme.fg("error", "(no URL)"), 0, 0);
|
|
@@ -3184,7 +3184,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3184
3184
|
const availableProviders = bootstrap.availableProviders;
|
|
3185
3185
|
const initialProvider = bootstrap.defaultProvider;
|
|
3186
3186
|
const curatorTimeoutSeconds = bootstrap.timeoutSeconds;
|
|
3187
|
-
let currentProvider:
|
|
3187
|
+
let currentProvider: SearchProvider = initialProvider;
|
|
3188
3188
|
const commandConfig = loadConfig();
|
|
3189
3189
|
const rawSearchProvider = normalizeProviderInput(
|
|
3190
3190
|
commandConfig.searchProvider ?? commandConfig.provider ?? "auto",
|
|
@@ -3196,6 +3196,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3196
3196
|
const summaryContext: SummaryGenerationContext = {
|
|
3197
3197
|
model: ctx.model,
|
|
3198
3198
|
modelRegistry: ctx.modelRegistry,
|
|
3199
|
+
sessionManager: ctx.sessionManager,
|
|
3199
3200
|
cwd: ctx.cwd,
|
|
3200
3201
|
isProjectTrusted: () => ctx.isProjectTrusted(),
|
|
3201
3202
|
};
|
|
@@ -3,6 +3,8 @@ import { activityMonitor } from "./activity.ts";
|
|
|
3
3
|
import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.ts";
|
|
4
4
|
import type { ExtractedContent } from "./extract.ts";
|
|
5
5
|
import type { SearchOptions, SearchResponse } from "./perplexity.ts";
|
|
6
|
+
import { formatSearchResultsAsAnswer } from "./search-answer-formatting.ts";
|
|
7
|
+
import { normalizeSearchResultCount } from "./search-result-count-normalization.ts";
|
|
6
8
|
import { getWebSearchConfigPath } from "./utils.ts";
|
|
7
9
|
|
|
8
10
|
const JINA_SEARCH_BASE_URL = "https://s.jina.ai/";
|
|
@@ -74,11 +76,6 @@ async function requireApiKey(signal?: AbortSignal): Promise<string> {
|
|
|
74
76
|
return apiKey;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
function normalizeCount(value: number | undefined): number {
|
|
78
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return 5;
|
|
79
|
-
return Math.max(1, Math.min(Math.floor(value), 20));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
79
|
function normalizeDomain(value: string): string | null {
|
|
83
80
|
let input = value.trim().toLowerCase();
|
|
84
81
|
if (input.startsWith("-")) input = input.slice(1).trim();
|
|
@@ -181,14 +178,6 @@ function mapItems(items: JinaSearchItem[], numResults: number, filters: ReturnTy
|
|
|
181
178
|
return { results, content };
|
|
182
179
|
}
|
|
183
180
|
|
|
184
|
-
function buildAnswer(results: SearchResponse["results"]): string {
|
|
185
|
-
return results
|
|
186
|
-
.map((result) => result.snippet
|
|
187
|
-
? `${result.snippet}\nSource: ${result.title} (${result.url})`
|
|
188
|
-
: `Source: ${result.title} (${result.url})`)
|
|
189
|
-
.join("\n\n");
|
|
190
|
-
}
|
|
191
|
-
|
|
192
181
|
function rethrowRequestError(
|
|
193
182
|
err: unknown,
|
|
194
183
|
apiKey: string,
|
|
@@ -220,7 +209,7 @@ export function isJinaSearchAvailable(): boolean {
|
|
|
220
209
|
|
|
221
210
|
export async function searchWithJina(query: string, options: JinaSearchOptions = {}): Promise<SearchResponse> {
|
|
222
211
|
const apiKey = await requireApiKey(options.signal);
|
|
223
|
-
const numResults =
|
|
212
|
+
const numResults = normalizeSearchResultCount(options.numResults);
|
|
224
213
|
const { url, filters } = buildSearchRequest(query, options, numResults);
|
|
225
214
|
const activityId = activityMonitor.logStart({ type: "api", query });
|
|
226
215
|
const request = requestSignal(options.signal);
|
|
@@ -276,7 +265,7 @@ export async function searchWithJina(query: string, options: JinaSearchOptions =
|
|
|
276
265
|
}
|
|
277
266
|
activityMonitor.logComplete(activityId, response.status);
|
|
278
267
|
const result: SearchResponse = {
|
|
279
|
-
answer:
|
|
268
|
+
answer: formatSearchResultsAsAnswer(mapped.results),
|
|
280
269
|
results: mapped.results,
|
|
281
270
|
};
|
|
282
271
|
if (options.includeContent && mapped.content.length > 0) result.inlineContent = mapped.content;
|
|
@@ -2,6 +2,8 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { activityMonitor } from "./activity.ts";
|
|
3
3
|
import type { ExtractedContent, ExtractOptions } from "./extract.ts";
|
|
4
4
|
import type { SearchOptions, SearchResponse } from "./perplexity.ts";
|
|
5
|
+
import { formatSearchResultsAsAnswer } from "./search-answer-formatting.ts";
|
|
6
|
+
import { normalizeSearchResultCount } from "./search-result-count-normalization.ts";
|
|
5
7
|
import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.ts";
|
|
6
8
|
import { fetchRemoteUrl, loadFetchContentDomainPolicy, loadSsrfConfig, validateRemoteUrl, type SsrfConfig } from "./ssrf-protection.ts";
|
|
7
9
|
import { getWebSearchConfigPath } from "./utils.ts";
|
|
@@ -68,11 +70,6 @@ async function requireApiKey(signal?: AbortSignal): Promise<string> {
|
|
|
68
70
|
return apiKey;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
function normalizeCount(value: number | undefined): number {
|
|
72
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return 5;
|
|
73
|
-
return Math.max(1, Math.min(Math.floor(value), 20));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
73
|
function errorMessage(err: unknown): string {
|
|
77
74
|
return err instanceof Error ? err.message : String(err);
|
|
78
75
|
}
|
|
@@ -153,19 +150,13 @@ function parseExtractResponse(value: unknown, requestedUrl: string): ExtractedCo
|
|
|
153
150
|
return null;
|
|
154
151
|
}
|
|
155
152
|
|
|
156
|
-
function buildAnswer(results: SearchResponse["results"]): string {
|
|
157
|
-
return results.map((result) => result.snippet
|
|
158
|
-
? `${result.snippet}\nSource: ${result.title} (${result.url})`
|
|
159
|
-
: `Source: ${result.title} (${result.url})`).join("\n\n");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
153
|
export function isKagiAvailable(): boolean {
|
|
163
154
|
return hasCredentialSource({ provider: "Kagi", configuredValue: loadConfig().kagiApiKey, environmentValue: process.env.KAGI_API_KEY });
|
|
164
155
|
}
|
|
165
156
|
|
|
166
157
|
export async function searchWithKagi(query: string, options: KagiSearchOptions = {}): Promise<SearchResponse> {
|
|
167
158
|
const apiKey = await requireApiKey(options.signal);
|
|
168
|
-
const numResults =
|
|
159
|
+
const numResults = normalizeSearchResultCount(options.numResults);
|
|
169
160
|
const activityId = activityMonitor.logStart({ type: "api", query });
|
|
170
161
|
let response: Response;
|
|
171
162
|
try {
|
|
@@ -200,7 +191,7 @@ export async function searchWithKagi(query: string, options: KagiSearchOptions =
|
|
|
200
191
|
const parsed = parseSearchResponse(rawData);
|
|
201
192
|
activityMonitor.logComplete(activityId, response.status);
|
|
202
193
|
const results = parsed.results.slice(0, numResults);
|
|
203
|
-
const mapped: SearchResponse = { answer:
|
|
194
|
+
const mapped: SearchResponse = { answer: formatSearchResultsAsAnswer(results), results };
|
|
204
195
|
if (options.includeContent) {
|
|
205
196
|
const urls = new Set(results.map(result => result.url));
|
|
206
197
|
const inlineContent = parsed.inlineContent.filter(content => urls.has(content.url));
|