@herbertgao/pi-extensions 2026.9.18 → 2026.9.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +19 -20
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +21 -6
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +15 -4
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +43 -7
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +7 -2
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +1 -1
  8. package/node_modules/@herbertgao/pi-cc-extensions/package.json +2 -2
  9. package/node_modules/pi-antigravity/README.md +29 -6
  10. package/node_modules/pi-antigravity/package.json +12 -7
  11. package/node_modules/pi-antigravity/src/auth/accounts.ts +327 -0
  12. package/node_modules/pi-antigravity/src/auth/index.ts +9 -0
  13. package/node_modules/pi-antigravity/src/client/client.ts +1 -1
  14. package/node_modules/pi-antigravity/src/image/image.ts +4 -4
  15. package/node_modules/pi-antigravity/src/index.ts +203 -13
  16. package/node_modules/pi-antigravity/src/search/index.ts +1 -0
  17. package/node_modules/pi-antigravity/src/search/search.ts +325 -0
  18. package/node_modules/pi-antigravity/src/stream/stream.ts +191 -14
  19. package/node_modules/pi-antigravity/src/types/enums.ts +45 -36
  20. package/node_modules/pi-antigravity/src/types/types.ts +5 -12
  21. package/node_modules/pi-antigravity/src/utils/http.ts +7 -2
  22. package/node_modules/pi-antigravity/src/utils/util.ts +31 -3
  23. package/node_modules/pi-typesafe/README.md +2 -0
  24. package/node_modules/pi-typesafe/package.json +2 -1
  25. package/node_modules/pi-web-access/CHANGELOG.md +72 -0
  26. package/node_modules/pi-web-access/README.md +34 -13
  27. package/node_modules/pi-web-access/chrome-cookies.ts +79 -25
  28. package/node_modules/pi-web-access/curator-page.ts +5 -1
  29. package/node_modules/pi-web-access/curator-server.ts +2 -0
  30. package/node_modules/pi-web-access/dist/index.js +1337 -1009
  31. package/node_modules/pi-web-access/exa.ts +20 -52
  32. package/node_modules/pi-web-access/extract.ts +41 -4
  33. package/node_modules/pi-web-access/gemini-search.ts +12 -4
  34. package/node_modules/pi-web-access/index.ts +29 -37
  35. package/node_modules/pi-web-access/openai-search.ts +35 -7
  36. package/node_modules/pi-web-access/package.json +6 -5
  37. package/node_modules/pi-web-access/page-query.ts +2 -2
  38. package/node_modules/pi-web-access/query-rewrite.ts +2 -3
  39. package/node_modules/pi-web-access/ssrf-protection.ts +27 -10
  40. package/node_modules/pi-web-access/storage.ts +48 -77
  41. package/node_modules/pi-web-access/summary-model-scope.ts +6 -77
  42. package/node_modules/pi-web-access/summary-review.ts +3 -4
  43. package/node_modules/pi-web-access/tavily.ts +67 -20
  44. package/node_modules/pi-web-access/tool-activation.ts +53 -33
  45. package/node_modules/pi-web-access/utils.ts +1 -1
  46. package/node_modules/pi-web-access/you.ts +164 -0
  47. package/node_modules/pi-web-access/zai.ts +161 -0
  48. package/package.json +6 -6
@@ -1,42 +1,51 @@
1
- export enum ThinkingEffort {
2
- Off = "off",
3
- Minimal = "minimal",
4
- Low = "low",
5
- Medium = "medium",
6
- High = "high",
7
- Xhigh = "xhigh",
8
- }
1
+ export const ThinkingEffort = {
2
+ Off: "off",
3
+ Minimal: "minimal",
4
+ Low: "low",
5
+ Medium: "medium",
6
+ High: "high",
7
+ Xhigh: "xhigh",
8
+ } as const;
9
+ export type ThinkingEffort = (typeof ThinkingEffort)[keyof typeof ThinkingEffort];
9
10
 
10
- export enum ToolChoice {
11
- Auto = "auto",
12
- None = "none",
13
- Any = "any",
14
- Required = "required",
15
- }
11
+ export const ToolChoice = {
12
+ Auto: "auto",
13
+ None: "none",
14
+ Any: "any",
15
+ Required: "required",
16
+ } as const;
17
+ export type ToolChoice = (typeof ToolChoice)[keyof typeof ToolChoice];
16
18
 
17
- export enum GeminiToolCallingMode {
18
- None = "NONE",
19
- Any = "ANY",
20
- Auto = "AUTO",
21
- Validated = "VALIDATED",
22
- }
19
+ export const GeminiToolCallingMode = {
20
+ None: "NONE",
21
+ Any: "ANY",
22
+ Auto: "AUTO",
23
+ Validated: "VALIDATED",
24
+ } as const;
25
+ export type GeminiToolCallingMode =
26
+ (typeof GeminiToolCallingMode)[keyof typeof GeminiToolCallingMode];
23
27
 
24
- export enum GeminiRole {
25
- User = "user",
26
- Model = "model",
27
- }
28
+ export const GeminiRole = {
29
+ User: "user",
30
+ Model: "model",
31
+ } as const;
32
+ export type GeminiRole = (typeof GeminiRole)[keyof typeof GeminiRole];
28
33
 
29
- export enum AntigravityRequestType {
30
- Agent = "agent",
31
- }
34
+ export const AntigravityRequestType = {
35
+ Agent: "agent",
36
+ } as const;
37
+ export type AntigravityRequestType =
38
+ (typeof AntigravityRequestType)[keyof typeof AntigravityRequestType];
32
39
 
33
- export enum AntigravityUserAgent {
34
- Antigravity = "antigravity",
35
- }
40
+ export const AntigravityUserAgent = {
41
+ Antigravity: "antigravity",
42
+ } as const;
43
+ export type AntigravityUserAgent = (typeof AntigravityUserAgent)[keyof typeof AntigravityUserAgent];
36
44
 
37
- export enum StopReason {
38
- Stop = "stop",
39
- Length = "length",
40
- ToolUse = "toolUse",
41
- Error = "error",
42
- }
45
+ export const StopReason = {
46
+ Stop: "stop",
47
+ Length: "length",
48
+ ToolUse: "toolUse",
49
+ Error: "error",
50
+ } as const;
51
+ export type StopReason = (typeof StopReason)[keyof typeof StopReason];
@@ -6,14 +6,7 @@ import type {
6
6
  ThinkingContent,
7
7
  ToolCall,
8
8
  } from "@earendil-works/pi-ai";
9
- import type {
10
- AntigravityRequestType,
11
- AntigravityUserAgent,
12
- GeminiRole,
13
- GeminiToolCallingMode,
14
- ThinkingEffort,
15
- ToolChoice,
16
- } from "./enums.js";
9
+ import type { GeminiRole, GeminiToolCallingMode, ThinkingEffort, ToolChoice } from "./enums.js";
17
10
 
18
11
  // OAuth & Auth Types
19
12
  export type AntigravityOAuthCredentials = OAuthCredentials & {
@@ -52,7 +45,7 @@ export const ANTIGRAVITY_API = "antigravity-api" as const;
52
45
  export type AntigravityApi = typeof ANTIGRAVITY_API;
53
46
 
54
47
  export type AntigravityStreamOptions = Omit<SimpleStreamOptions, "toolChoice"> & {
55
- toolChoice?: ToolChoice | `${ToolChoice}`;
48
+ toolChoice?: ToolChoice;
56
49
  };
57
50
 
58
51
  export type GeminiTextPart = { text: string; thoughtSignature?: string };
@@ -116,7 +109,7 @@ export type GeminiGenerationConfig = {
116
109
  export type GeminiRequestBody = {
117
110
  contents: GeminiContent[];
118
111
  systemInstruction: {
119
- role: GeminiRole.User;
112
+ role: "user";
120
113
  parts: GeminiTextPart[];
121
114
  };
122
115
  generationConfig?: GeminiGenerationConfig;
@@ -130,8 +123,8 @@ export type AntigravityGenerateRequest = {
130
123
  project: string;
131
124
  model: string;
132
125
  request: GeminiRequestBody;
133
- requestType: AntigravityRequestType.Agent;
134
- userAgent: AntigravityUserAgent.Antigravity;
126
+ requestType: "agent";
127
+ userAgent: "antigravity";
135
128
  requestId: string;
136
129
  };
137
130
 
@@ -23,6 +23,7 @@ const PREWARM_TIMEOUT_MS = 5_000;
23
23
  type DispatcherInit = RequestInit & { dispatcher?: unknown };
24
24
 
25
25
  let dispatcherPromise: Promise<unknown> | undefined;
26
+ let prewarmStarted = false;
26
27
 
27
28
  function hasProxyConfiguration(): boolean {
28
29
  return ["HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "http_proxy", "https_proxy", "all_proxy"].some(
@@ -71,11 +72,15 @@ export async function antigravityFetch(
71
72
  }
72
73
 
73
74
  /**
74
- * Open the TLS connection when the extension loads so the first message of a session
75
- * does not pay the handshake either. Best-effort: failures are ignored.
75
+ * Open the TLS connection on the first Antigravity request so that turn does not
76
+ * pay the handshake. Not called at extension load: an unauthenticated HEAD to the
77
+ * endpoint can stall Node's event loop until connect timeout and freeze Pi startup.
78
+ * Best-effort: failures are ignored. Subsequent calls are no-ops.
76
79
  */
77
80
  export function prewarmConnection(url: string): void {
78
81
  if (antigravityEnv("NO_PREWARM") === "1") return;
82
+ if (prewarmStarted) return;
83
+ prewarmStarted = true;
79
84
  void (async () => {
80
85
  try {
81
86
  const res = await antigravityFetch(url, {
@@ -52,14 +52,35 @@ export type AntigravityEnvelopeOptions = {
52
52
  userTurnIndex?: number;
53
53
  trajectoryId?: string;
54
54
  conversationId?: string;
55
+ lastExecutionId?: string;
55
56
  };
56
57
 
57
58
  const sessionTrajectoryMap = new Map<string, { conversationId: string; trajectoryId: string }>();
58
59
 
59
60
  /** Stable conversationId and trajectoryId within a multi-turn conversation session. */
60
- export function resolveSessionTrajectory(context?: {
61
- messages?: Array<{ role?: string; timestamp?: number; content?: unknown }>;
62
- }): { conversationId: string; trajectoryId: string } {
61
+ export function resolveSessionTrajectory(
62
+ context?: {
63
+ messages?: Array<{ role?: string; timestamp?: number; content?: unknown }>;
64
+ },
65
+ sessionId?: string,
66
+ ): { conversationId: string; trajectoryId: string } {
67
+ if (sessionId?.trim()) {
68
+ const seed = `session:${sessionId.trim()}`;
69
+ let entry = sessionTrajectoryMap.get(seed);
70
+ if (!entry) {
71
+ entry = {
72
+ conversationId: stableUuid(`antigravity:conv:${seed}`),
73
+ trajectoryId: stableUuid(`antigravity:traj:${seed}`),
74
+ };
75
+ sessionTrajectoryMap.set(seed, entry);
76
+ if (sessionTrajectoryMap.size > 64) {
77
+ const oldestKey = sessionTrajectoryMap.keys().next().value;
78
+ if (oldestKey !== undefined) sessionTrajectoryMap.delete(oldestKey);
79
+ }
80
+ }
81
+ return entry;
82
+ }
83
+
63
84
  const firstMsg = context?.messages?.[0];
64
85
  if (!firstMsg) {
65
86
  return { conversationId: crypto.randomUUID(), trajectoryId: crypto.randomUUID() };
@@ -124,6 +145,13 @@ export function antigravityRequestEnvelope(
124
145
  labels.model_enum = modelEnum;
125
146
  }
126
147
 
148
+ // Pure agy CLI wire alignment: last_execution_id is the execution ID of the previous turn.
149
+ // It is only present on subsequent turns (step > 1), never on the initial turn (step <= 1).
150
+ if (step > 1) {
151
+ labels.last_execution_id =
152
+ options.lastExecutionId || stableUuid(`antigravity:exec:${trajectoryId}:${step - 1}`);
153
+ }
154
+
127
155
  return {
128
156
  requestId: `agent/${agentId}/${Date.now()}/${trajectoryId}/${step}`,
129
157
  sessionId,
@@ -123,6 +123,8 @@ Per request: 32 questions and 64 KiB of JSON. Per session: 20 attempts, 15-secon
123
123
 
124
124
  Import the library from your own extension. It has no dependency on Pi and is safe in tests.
125
125
 
126
+ TypeBox is a required wildcard peer: Pi supplies it for managed extension installs; ordinary npm installs resolve it automatically. If your package manager does not install peers, install `typebox` alongside `pi-typesafe` for standalone use.
127
+
126
128
  ```ts
127
129
  import { ask, createTypeSafe, choice, noul, score } from "pi-typesafe";
128
130
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-typesafe",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "TypeSafe AI (Jev) decisions for Pi: batched Choice/Score/Noul evaluation tool, terminal playground, and a typed API other extensions build on.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -84,6 +84,7 @@
84
84
  "@earendil-works/pi-tui": "^0.86.1",
85
85
  "@types/node": "^22.0.0",
86
86
  "tsx": "^4.23.13",
87
+ "typebox": "^1.3.31",
87
88
  "typescript": "^7.0.2"
88
89
  }
89
90
  }
@@ -4,6 +4,78 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.34.0] - 2026-09-30
8
+
9
+ ### Highlights
10
+
11
+ - Turning on web tools no longer costs a prompt-cache miss on models like DeepSeek. New sessions now pick the activation style that suits the model.
12
+ - Search with Z.ai's GLM Coding Plan and use your plan quota instead of paying per call.
13
+ - `fetch_content` now gets past Cloudflare "Just a moment..." pages by handing off to your other fetch providers.
14
+ - Fetching works on networks where only your configured proxy can resolve hostnames.
15
+ - `undici` is updated to pick up fixes for known security advisories.
16
+
17
+ ### Added
18
+
19
+ - Search with Z.ai's GLM Coding Plan web search when you select `provider: "zai"`. Set `ZAI_API_KEY` or `zaiApiKey` to your plan key, and set `zaiEndpoint: "china"` if your key comes from bigmodel.cn. Searches use your plan quota instead of per-call billing. Thanks to [@nailuoGG](https://github.com/nailuoGG) for [issue #475](https://github.com/nicobailon/pi-web-access/issues/475) and for live-testing the China endpoint in [issue #479](https://github.com/nicobailon/pi-web-access/issues/479).
20
+
21
+ ### Changed
22
+
23
+ - `toolActivation` now defaults to `"auto"`. On models that can take tools added mid-conversation, new sessions start with `web_enable` as before. On other models, such as DeepSeek, every enabled web tool is available from the first request, so turning them on no longer makes Pi resend the whole conversation and miss the prompt cache. Set `"dynamic"` to always start with `web_enable`, or `"eager"` to never use it. Resumed sessions keep the tools they already had. The README's Tool activation section explains the trade-off. Thanks to [@tinoy1336](https://github.com/tinoy1336) for [issue #484](https://github.com/nicobailon/pi-web-access/issues/484) and [@jordi9](https://github.com/jordi9) for [issue #481](https://github.com/nicobailon/pi-web-access/issues/481).
24
+ - Keyed Exa searches with default options or `numResults: 5` now call Exa's `/search` endpoint instead of `/answer`, like every other keyed Exa search. Their answer text now comes from the search results instead of Exa's generated answer. If you route Exa through a custom `exaBaseUrl` gateway, it must support `/search`. Thanks to [@SuTang-vain](https://github.com/SuTang-vain) for [issue #470](https://github.com/nicobailon/pi-web-access/issues/470).
25
+ - When `fetch_content` can't get a page, its list of fallback options now explains how to turn on the keyless Jina Reader fallback. It names only the setting you still need and keeps your current or default provider order. The hint doesn't appear if Jina already ran for that fetch, and it warns that Jina's servers fetch the target URLs. Thanks to [@SuTang-vain](https://github.com/SuTang-vain) for [PR #471](https://github.com/nicobailon/pi-web-access/pull/471).
26
+
27
+ ### Fixed
28
+
29
+ - `fetch_content` now treats a Cloudflare "Just a moment..." challenge page returned with HTTP 200 as a failed fetch, so your configured fallback providers can retrieve the real page. Detection needs Cloudflare's `cf-mitigated: challenge` header or its challenge-page scripts, so a page that only says "Just a moment..." is unaffected. Raw mode still returns the response unchanged, and authenticated fetches report the challenge without falling back to other providers. Thanks to [@SuTang-vain](https://github.com/SuTang-vain) for [issue #472](https://github.com/nicobailon/pi-web-access/issues/472).
30
+ - With `ssrf.trustEnvProxy: true`, the `proxy` configured in `web-search.json` now resolves hostnames for web tools instead of your local DNS, so `fetch_content` works where only the proxy can resolve names. A per-call `proxy` with a different value is still checked against local DNS. When local resolution fails for a request going through the configured proxy, the error now names this setting. Thanks to [@ChenAuCarre](https://github.com/ChenAuCarre) for [issue #476](https://github.com/nicobailon/pi-web-access/issues/476).
31
+ - Exa results without a title are now labeled with their site's hostname, such as `cdn.jsdelivr.net`, instead of `Source N`. Results whose URL has no hostname, such as `mailto:` or `file:` links, still use `Source N`. Thanks to [@SuTang-vain](https://github.com/SuTang-vain) for [PR #469](https://github.com/nicobailon/pi-web-access/pull/469).
32
+ - `undici` is updated to 8.11.2. Earlier 8.x releases, including the 8.10.0 this package previously installed, have known security advisories such as decompression denial of service and a TLS certificate validation bypass. Thanks to [@setanta00](https://github.com/setanta00) for [issue #483](https://github.com/nicobailon/pi-web-access/issues/483).
33
+
34
+ ## [0.33.0] - 2026-09-27
35
+
36
+ ### Highlights
37
+
38
+ - Keep every web tool available from the first message with `"toolActivation": "eager"`, for models that rarely call `web_enable` on their own.
39
+ - Search with You.com when you select it explicitly.
40
+ - Give each Pi process its own fetched-page cache so parallel sessions stop evicting each other's pages.
41
+ - Model allowlists in `enabledModels` or `--models` now work with every pattern Pi accepts.
42
+ - Resumed older sessions keep the tools they had instead of gaining `web_enable`.
43
+
44
+ ### Added
45
+
46
+ - Set `"toolActivation": "eager"` in `web-search.json` to skip `web_enable` and keep every enabled web tool available from the first message. With the default `"dynamic"` setting, `web_enable` now tells the model to call it first whenever current, external, or linked information could help. Thanks to [@ackalker](https://github.com/ackalker) for [issue #458](https://github.com/nicobailon/pi-web-access/issues/458).
47
+ - Set `PI_WEB_ACCESS_CACHE_ROOT` to a directory to give a Pi process its own fetched-page cache. The cache goes in a `web-search-cache` folder inside that directory, and `web-search.json` stays where it is. Parallel sessions then stop evicting each other's cached pages. Thanks to [@goodman-b](https://github.com/goodman-b) for [issue #457](https://github.com/nicobailon/pi-web-access/issues/457).
48
+ - You.com is available as an optional search provider. Set `youApiKey` or `YDC_API_KEY`, then select `provider: "you"`, add it to a provider list, or list it in `searchRouting`. It also appears in the Curator. `auto` and `provider: "all"` never pick it. Thanks to [@mouse-value-add](https://github.com/mouse-value-add) for [PR #461](https://github.com/nicobailon/pi-web-access/pull/461).
49
+
50
+ ### Fixed
51
+
52
+ - Resuming a session that never had `web_enable`, for example one started before pi-web-access was installed or upgraded, no longer adds it partway through the conversation. The session keeps the tools it already had. Thanks to [@nilsoskar](https://github.com/nilsoskar) for [issue #462](https://github.com/nicobailon/pi-web-access/issues/462).
53
+ - Answer mode, query rewrite, and summaries now follow the model list Pi builds from `enabledModels` or `--models`, so every pattern Pi accepts works here too, including patterns such as `@(!(openai-codex))/*` and partial names such as `sonnet`. Before, an allowlist made of those patterns blocked every model. Thanks to [@IdrisGit](https://github.com/IdrisGit) for [issue #463](https://github.com/nicobailon/pi-web-access/issues/463) and [PR #464](https://github.com/nicobailon/pi-web-access/pull/464).
54
+ - `web_enable` now depends on the version of Pi you are running, not an older Pi package that may be installed next to the extension, so those setups no longer lose it. It needs Pi 0.86.0 or newer. On older Pi, every enabled web tool stays available from the start. Thanks to [@PhrZer](https://github.com/PhrZer) for [PR #456](https://github.com/nicobailon/pi-web-access/pull/456) and [@nguyenchiencong](https://github.com/nguyenchiencong) for [issue #444](https://github.com/nicobailon/pi-web-access/issues/444).
55
+
56
+ ## [0.32.0] - 2026-09-26
57
+
58
+ ### Highlights
59
+
60
+ - Spread Tavily searches across up to 20 API keys, with automatic failover when one runs out of quota.
61
+ - Use Gemini Web browser cookies on KDE Linux, where Chrome keeps its password in KWallet.
62
+ - Search with OpenAI through OpenCode and `opencode-go` without session errors or the wrong model.
63
+ - See full search queries and URLs in tool-call labels.
64
+ - Globally installed Pi no longer shows false version or duplicate TypeBox warnings.
65
+
66
+ ### Added
67
+
68
+ - Tavily can use a pool of up to 20 API keys. Set `TAVILY_API_KEY_1` through `TAVILY_API_KEY_20`, and when a key hits a quota or auth error the search moves on to the next one. `TAVILY_API_KEY_INDEX` picks the key to try first, and `TAVILY_API_KEY` / `tavilyApiKey` is tried last. Thanks to [@apoapostolov](https://github.com/apoapostolov) for [PR #430](https://github.com/nicobailon/pi-web-access/pull/430).
69
+
70
+ ### Fixed
71
+
72
+ - On KDE Linux, Chrome and Chromium cookies for Gemini Web now decrypt with the password stored in KWallet when Secret Service doesn't have it. Thanks to [@dianzuan](https://github.com/dianzuan) for [issue #436](https://github.com/nicobailon/pi-web-access/issues/436).
73
+ - OpenAI search requests to `opencode.ai` now include OpenCode's session headers, fixing `400 MissingSessionID` errors. Other destinations never receive these headers. Thanks to [@juliocc](https://github.com/juliocc) for [PR #445](https://github.com/nicobailon/pi-web-access/pull/445).
74
+ - When `openaiSearchModel` isn't set, OpenAI search through gateways such as `opencode-go` now picks the newest GPT model instead of a non-OpenAI model.
75
+ - Globally installed Pi no longer shows a false "requires Pi 0.86.1 or newer" warning, and dynamic tool activation now works there. Thanks to [@samsimsom](https://github.com/samsimsom) for [issue #428](https://github.com/nicobailon/pi-web-access/issues/428).
76
+ - TypeBox now comes from Pi instead of being installed separately, which avoids duplicate copies and Pi startup warnings. Thanks to [@ksreenivasan](https://github.com/ksreenivasan) for [issue #442](https://github.com/nicobailon/pi-web-access/issues/442) and [PR #443](https://github.com/nicobailon/pi-web-access/pull/443).
77
+ - Tool-call labels show full search queries and fetch URLs when the terminal is wide enough, instead of cutting them off at 60 characters. Thanks to [@TheBestPessimist](https://github.com/TheBestPessimist) for [issue #440](https://github.com/nicobailon/pi-web-access/issues/440).
78
+
7
79
  ## [0.31.0] - 2026-09-22
8
80
 
9
81
  ### Highlights
@@ -109,7 +109,7 @@ To route automatic searches through the active Pi model, configure an ordered ro
109
109
 
110
110
  With `useCurrentModel: true`, the automatic `openai` step uses Hosted `web_search` (or standalone search when `openaiUseAlphaSearch` is enabled) when the active model is a GPT model backed by an official OpenAI Responses endpoint: `openai`/`openai-responses` on HTTPS `api.openai.com`, or `openai-codex`/`openai-codex-responses` on the official ChatGPT Codex endpoint. Third-party gateways, Azure, and other models continue to the next route entry. A tool-level `provider` or top-level `provider` remains an explicit override; `provider: "openai"` keeps the existing independent OpenAI/Codex search-model behavior.
111
111
 
112
- For sandboxed networks that provide outbound proxy transport through environment variables, set `ssrf.trustEnvProxy` to `true` to skip local DNS preflight for proxied hostnames:
112
+ For sandboxed or corporate networks where only the proxy can resolve hostnames, set `ssrf.trustEnvProxy` to `true` to skip local DNS preflight for proxied hostnames:
113
113
 
114
114
  ```json
115
115
  {
@@ -119,7 +119,7 @@ For sandboxed networks that provide outbound proxy transport through environment
119
119
  }
120
120
  ```
121
121
 
122
- This is an opt-in DNS-preflight adjustment, not proxy transport configuration. `HTTP_PROXY`, `HTTPS_PROXY`, and `ALL_PROXY` are recognized; `NO_PROXY` hosts still undergo DNS validation, and localhost or literal private IP targets remain blocked.
122
+ This is an opt-in DNS-preflight adjustment, not proxy transport configuration. It applies to requests sent through the `proxy` set in `web-search.json` (HTTP(S), `socks5h`, or `socks4a`, including a per-call `proxy` with the same value). When no `proxy` is configured, it applies to hostnames covered by `HTTP_PROXY`, `HTTPS_PROXY`, or `ALL_PROXY`, for sandboxes that route outbound traffic through those variables themselves; the extension does not route requests through environment proxies. A per-call `proxy` that differs from the configured one still gets local DNS validation. `NO_PROXY` hosts still undergo DNS validation, and localhost or literal private IP targets remain blocked.
123
123
 
124
124
  Optional dependencies for video frame extraction:
125
125
 
@@ -163,7 +163,7 @@ fetch_content({ url: "/path/to/recording.mp4", prompt: "What error appears on sc
163
163
 
164
164
  ### web_search
165
165
 
166
- Search the web via OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data SERP, SerpBase, SerpApi, Serper, Serply, self-hosted SearXNG, keyless DuckDuckGo, Exa, Perplexity AI, Gemini, or Kimi. The default `none` workflow makes no summary-generation model call and opens no curator/browser: it returns bounded raw results, identifies the provider used for each query, and stores the full results for retrieval by responseId. The selected search provider may itself be model-backed.
166
+ Search the web via OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data SERP, SerpBase, SerpApi, Serper, Serply, You.com, self-hosted SearXNG, keyless DuckDuckGo, Exa, Perplexity AI, Gemini, or Kimi. The default `none` workflow makes no summary-generation model call and opens no curator/browser: it returns bounded raw results, identifies the provider used for each query, and stores the full results for retrieval by responseId. The selected search provider may itself be model-backed.
167
167
 
168
168
  ```typescript
169
169
  web_search({ query: "rust async programming" })
@@ -186,7 +186,7 @@ web_search({ queries: ["query 1", "query 2"], workflow: "auto-summary" })
186
186
  | `numResults` | Results per query (default: 5, max: 20) |
187
187
  | `recencyFilter` | `day`, `week`, `month`, or `year` |
188
188
  | `domainFilter` | Limit to domains (prefix with `-` to exclude) |
189
- | `provider` | Configured provider when omitted or set to `auto`; `all` searches every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, Serper, Serply, and Baizhi simultaneously; otherwise `openai`, `brave`, `parallel`, `parallel-mcp`, `tinyfish`, `search1api`, `searchinfinity`, `querit`, `tavily`, `firecrawl`, `jina`, `serpdive`, `kagi`, `bocha`, `ollama`, `anysearch`, `xcrawl`, `valyu`, `xai`, `mistral`, `brightdata`, `serpbase`, `serpapi`, `serper`, `serply`, `baizhi`, `searxng`, `duckduckgo`, `exa`, `perplexity`, `gemini`, or `kimi` (auto-selects when no provider or routing is configured; Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, Serper, Serply, and Baizhi are explicit-only) |
189
+ | `provider` | Configured provider when omitted or set to `auto`; `all` searches every eligible provider except Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, Serper, Serply, You.com, Baizhi, and Z.ai simultaneously; otherwise `openai`, `brave`, `parallel`, `parallel-mcp`, `tinyfish`, `search1api`, `searchinfinity`, `querit`, `tavily`, `you`, `firecrawl`, `jina`, `serpdive`, `kagi`, `bocha`, `ollama`, `anysearch`, `xcrawl`, `valyu`, `xai`, `mistral`, `brightdata`, `serpbase`, `serpapi`, `serper`, `serply`, `baizhi`, `zai`, `searxng`, `duckduckgo`, `exa`, `perplexity`, `gemini`, or `kimi` (auto-selects when no provider or routing is configured; Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, Serper, Serply, You.com, Baizhi, and Z.ai are explicit-only) |
190
190
  | `includeContent` | Fetch full page content from sources in background |
191
191
  | `workflow` | `none` (skip curator; fresh-install default), `summary-review` (open curator and auto-generate a summary draft), or `auto-summary` (generate a summary without opening the curator) |
192
192
 
@@ -226,7 +226,7 @@ Thanks to [@linuxtextadventurer](https://github.com/linuxtextadventurer) for PR
226
226
 
227
227
  ### get_search_content
228
228
 
229
- Retrieve stored content from previous searches or fetches. Search provider answers and every result remain available in full and can be paged with `offset` and `limit` or searched with `findText`. Fetched URL content is stored in full in a private `web-search-cache` directory under the Pi config directory, not in the session JSONL. This includes `fetch_content` answer mode, which stores the original page content. The cache has a one-hour lifetime and fixed limits of 128 entries and 128 MiB; when either limit is reached, the oldest entries are removed first. On macOS and Linux the cache directory and files are kept at permissions `0700` and `0600`, respectively. Use `findText` to locate bounded matching passages without paging through a large page, or use `offset` and `limit` to retrieve slices intentionally.
229
+ Retrieve stored content from previous searches or fetches. Search provider answers and every result remain available in full and can be paged with `offset` and `limit` or searched with `findText`. Fetched URL content is stored in full in a private `web-search-cache` directory under the Pi config directory, not in the session JSONL. This includes `fetch_content` answer mode, which stores the original page content. The cache has a one-hour lifetime and fixed limits of 128 entries and 128 MiB; when either limit is reached, the oldest entries are removed first. Concurrent Pi processes share this cache and its limits. To give a process its own cache without moving `web-search.json`, set `PI_WEB_ACCESS_CACHE_ROOT` to a directory; the cache then lives in `web-search-cache` inside it, and a session resumed without the same value cannot read content cached there. On macOS and Linux the cache directory and files are kept at permissions `0700` and `0600`, respectively. Use `findText` to locate bounded matching passages without paging through a large page, or use `offset` and `limit` to retrieve slices intentionally.
230
230
 
231
231
  ```typescript
232
232
  get_search_content({ responseId: "abc123", urlIndex: 0 })
@@ -433,6 +433,7 @@ Config defaults to `~/.pi/agent/web-search.json` when neither `PI_CODING_AGENT_D
433
433
  "search1apiApiKey": "...",
434
434
  "tavilyApiKey": "tvly-...",
435
435
  "tavilyBaseUrl": "https://gateway.example.com/tavily",
436
+ "youApiKey": "your-ydc-api-key",
436
437
  "jinaApiKey": "$JINA_API_KEY",
437
438
  "serpdiveApiKey": "sd_live_...",
438
439
  "serpdiveModel": "krill",
@@ -444,6 +445,7 @@ Config defaults to `~/.pi/agent/web-search.json` when neither `PI_CODING_AGENT_D
444
445
  "serperApiKey": "$SERPER_API_KEY",
445
446
  "serplyApiKey": "$SERPLY_API_KEY",
446
447
  "baizhiApiKey": "$BAIZHI_API_KEY",
448
+ "zaiApiKey": "$ZAI_API_KEY",
447
449
  "brightdataApiKey": "$BRIGHTDATA_API_KEY",
448
450
  "brightdataSerpZone": "pi_serp",
449
451
  "xaiApiKey": "xai-...",
@@ -497,6 +499,7 @@ Config defaults to `~/.pi/agent/web-search.json` when neither `PI_CODING_AGENT_D
497
499
  "fetchContent": { "enabled": true },
498
500
  "getSearchContent": { "enabled": true }
499
501
  },
502
+ "toolActivation": "auto",
500
503
  "commands": {
501
504
  "websearch": { "enabled": true },
502
505
  "curator": { "enabled": true },
@@ -568,7 +571,7 @@ Config defaults to `~/.pi/agent/web-search.json` when neither `PI_CODING_AGENT_D
568
571
 
569
572
  `summaryModel` accepts an optional thinking-level suffix, such as `anthropic/claude-haiku-4-5:low`. Supported suffixes are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`.
570
573
 
571
- All provider API-key fields (`openaiApiKey`, `braveApiKey`, `parallelApiKey`, `tinyfishApiKey`, `search1apiApiKey`, `searchinfinityApiKey`, `queritApiKey`, `tavilyApiKey`, `jinaApiKey`, `serpdiveApiKey`, `kagiApiKey`, `bochaApiKey`, `ollamaApiKey`, `valyuApiKey`, `serpbaseApiKey`, `serpapiApiKey`, `serperApiKey`, `serplyApiKey`, `baizhiApiKey`, `anysearchApiKey`, `xcrawlApiKey`, `xaiApiKey`, `mistralApiKey`, `brightdataApiKey`, `firecrawlApiKey`, `crawl4aiApiToken`, `exaApiKey`, `perplexityApiKey`, `geminiApiKey`, `datalabApiKey`, and `cloudflareApiKey`) accept explicit credential sources. Use `$NAME` or `${NAME}` to read one named environment variable, or prefix a trusted local shell command with `!` to resolve one value at provider request time. Escape `$$` as a literal leading `$` and `$!` as a literal leading `!`:
574
+ All provider API-key fields (`openaiApiKey`, `braveApiKey`, `parallelApiKey`, `tinyfishApiKey`, `search1apiApiKey`, `searchinfinityApiKey`, `queritApiKey`, `tavilyApiKey`, `youApiKey`, `jinaApiKey`, `serpdiveApiKey`, `kagiApiKey`, `bochaApiKey`, `ollamaApiKey`, `valyuApiKey`, `serpbaseApiKey`, `serpapiApiKey`, `serperApiKey`, `serplyApiKey`, `baizhiApiKey`, `zaiApiKey`, `anysearchApiKey`, `xcrawlApiKey`, `xaiApiKey`, `mistralApiKey`, `brightdataApiKey`, `firecrawlApiKey`, `crawl4aiApiToken`, `exaApiKey`, `perplexityApiKey`, `geminiApiKey`, `datalabApiKey`, and `cloudflareApiKey`) accept explicit credential sources. Use `$NAME` or `${NAME}` to read one named environment variable, or prefix a trusted local shell command with `!` to resolve one value at provider request time. Escape `$$` as a literal leading `$` and `$!` as a literal leading `!`:
572
575
 
573
576
  ```json
574
577
  {
@@ -579,13 +582,13 @@ All provider API-key fields (`openaiApiKey`, `braveApiKey`, `parallelApiKey`, `t
579
582
  }
580
583
  ```
581
584
 
582
- This syntax applies to provider credentials only; other configuration fields are not interpolated. `firecrawlApiKey`, `crawl4aiApiToken`, `kagiApiKey`, `ollamaApiKey`, `valyuApiKey`, `serpbaseApiKey`, `serpapiApiKey`, `serperApiKey`, `serplyApiKey`, `baizhiApiKey`, `mistralApiKey`, and `brightdataApiKey` use the same credential-source rules, while `braveBaseUrl`, `exaBaseUrl`, `tavilyBaseUrl`, `firecrawlBaseUrl`, `firecrawlApiVersion`, `firecrawlFreshScrape`, `crawl4aiBaseUrl`, `brightdataSerpZone`, and `brightdataUnlockerZone` are literal config values.
585
+ This syntax applies to provider credentials only; other configuration fields are not interpolated. `firecrawlApiKey`, `crawl4aiApiToken`, `kagiApiKey`, `ollamaApiKey`, `valyuApiKey`, `serpbaseApiKey`, `serpapiApiKey`, `serperApiKey`, `serplyApiKey`, `baizhiApiKey`, `zaiApiKey`, `mistralApiKey`, and `brightdataApiKey` use the same credential-source rules, while `braveBaseUrl`, `exaBaseUrl`, `tavilyBaseUrl`, `firecrawlBaseUrl`, `firecrawlApiVersion`, `firecrawlFreshScrape`, `crawl4aiBaseUrl`, `brightdataSerpZone`, `brightdataUnlockerZone`, and `zaiEndpoint` are literal config values.
583
586
 
584
587
  A command source is not run while the extension loads or registers tools. Each selected provider request runs it again with a five-second timeout, a 16 KiB output limit, a minimized environment, and a one-line non-empty stdout requirement. Command text and stderr are omitted from errors. These commands are trusted local configuration, not a same-user process isolation boundary; use absolute executable paths and protect the config file. `OP_SESSION_*` and `OP_SERVICE_ACCOUNT_TOKEN`, when present in Pi's environment, are forwarded to trusted resolver commands so 1Password CLI sessions and service accounts can be reused without storing their credentials in config. For example, `"braveApiKey": "!/absolute/path/to/op read 'op://Automation/Brave/credential'"` resolves that item after you replace the executable placeholder with your trusted installation's absolute path, but the `op://` argument is not an authorization boundary: every configured resolver command that receives the token can exercise all vault and item permissions granted to its service account. Prefer a narrowly scoped service account. An explicit source overrides legacy provider environment variables and fails that provider locally rather than falling back with a stale credential. Direct Google Gemini API requests send the resolved key only in the `x-goog-api-key` header, never in the URL.
585
588
 
586
589
  `mistralSearchModel` defaults to `mistral-small-latest` and must be a non-empty string. `mistralSearchTool` defaults to `web_search` and accepts only `web_search` or the opt-in `web_search_premium` tool.
587
590
 
588
- Set `braveBaseUrl`, `exaBaseUrl`, or `tavilyBaseUrl` to route those providers through a compatible HTTPS API gateway. `BRAVE_BASE_URL`, `EXA_BASE_URL`, and `TAVILY_BASE_URL` are the environment-variable equivalents and take precedence over config. Brave appends `/web/search`, Exa appends `/answer` or `/search`, and Tavily appends `/search`. Defaults remain the providers' official API roots. `exaBaseUrl` applies only to keyed direct API calls; zero-config Exa MCP search continues to use Exa's hosted MCP endpoint. Invalid overrides fail before a request is sent instead of falling back to an official endpoint, and credential headers are removed if a gateway redirects to another origin.
591
+ Set `braveBaseUrl`, `exaBaseUrl`, or `tavilyBaseUrl` to route those providers through a compatible HTTPS API gateway. `BRAVE_BASE_URL`, `EXA_BASE_URL`, and `TAVILY_BASE_URL` are the environment-variable equivalents and take precedence over config. Brave appends `/web/search`; Exa and Tavily append `/search`. Defaults remain the providers' official API roots. `exaBaseUrl` applies only to keyed direct API calls; zero-config Exa MCP search continues to use Exa's hosted MCP endpoint. Invalid overrides fail before a request is sent instead of falling back to an official endpoint, and credential headers are removed if a gateway redirects to another origin.
589
592
 
590
593
  `authFetch` configures named local browser-cookie auth profiles for explicit `fetch_content` calls. A profile can be a host array (`"work": ["docs.company.com"]`) or an object with `hosts`, optional `chromeProfile`, `redirects: "same-origin"`, and `cache: "session" | "off"`.
591
594
 
@@ -625,17 +628,23 @@ Bright Data Web Unlocker is a paid `fetch_content` fallback after Parallel and b
625
628
 
626
629
  **Serper.** Set `serperApiKey` or `SERPER_API_KEY` and select `provider: "serper"` to query Serper's Google Search API. Serper is explicit-only: it is never chosen by `auto` or `provider: "all"`, but it can be configured as the named provider or added to `searchRouting`.
627
630
 
631
+ **Tavily key pool.** Set `TAVILY_API_KEY_1` through `TAVILY_API_KEY_20` to try another key when Tavily returns HTTP 401, 402, 403, 429, or 432. `TAVILY_API_KEY_INDEX` picks the first slot to try (default `1`). Search tries each distinct numbered key once, wrapping around, then tries `tavilyApiKey` / `TAVILY_API_KEY`. Explicit `$VAR` and `!command` sources still fail closed. All attempts share the 60-second search deadline.
632
+
628
633
  **Baizhi.** Set `BAIZHI_API_KEY` (or `baizhiApiKey: "$BAIZHI_API_KEY"` in `web-search.json`) and explicitly select `provider: "baizhi"` to call [Baizhi Cloud Agent Toolkit](https://baizhi.cloud/landing/agent-toolkit)'s `websearch_search` over Streamable HTTP MCP. It is excluded from automatic selection and `all`; you may explicitly configure it as the default or in `searchRouting`. The client initializes an MCP session, sends your own key only in the Bearer header, and closes the session after each search. Requests and response bodies share a 60-second deadline and respect cancellation. Redirects are rejected. Queries and filters are sent to the hosted commercial service, and calls may consume credits; review your account permissions and balance. Domain include/exclude filters map to the tool's `filter` object; recency maps to `time_range`, which defaults to `month`. Generated summaries are disabled.
629
634
 
630
635
  The Baizhi input contract is based on the [published September 16 discovery snapshot](https://github.com/ct-jaryn/baizhi-agent-toolkit/blob/b774c117369b8da2a1e42c7d01bf9535bb9a41c6/tests/fixtures/tools-history-2026-09-16.json); it has no output schema. This first version preserves MCP text and structured content as the provider answer rather than guessing result item fields. Consequently it does not populate source cards or fetch pages with `includeContent`. Tests use a synthetic MCP backend; live service results and billing have not been validated. This integration does not imply the hosted backend is open source.
631
636
 
637
+ **Z.ai.** Set `ZAI_API_KEY` (or `zaiApiKey: "$ZAI_API_KEY"` in `web-search.json`) to a GLM Coding Plan key and explicitly select `provider: "zai"` to call the plan's [Web Search MCP server](https://docs.z.ai/devpack/mcp/search-mcp-server) (`webSearchPrime`). Searches count against your Coding Plan quota rather than per-call API billing. Keys are issued per site: the default `zaiEndpoint: "global"` uses `api.z.ai`, and `zaiEndpoint: "china"` uses `open.bigmodel.cn` for keys from bigmodel.cn. No other endpoint can be configured, redirects are rejected, and the key is sent only in the Bearer header. Z.ai is excluded from automatic selection and `all`; you may configure it as the default or in `searchRouting`. Recency maps to `search_recency_filter`; `domainFilter` accepts one included domain, because the tool takes a single `search_domain_filter` and has no exclusions. The tool has no result count, so results are trimmed to `numResults`. Z.ai does not publish the tool's input or output schema; the mapping follows established MCP clients, and tests use a synthetic MCP backend.
638
+
632
639
  **Serply.** Set `serplyApiKey` or `SERPLY_API_KEY` and select `provider: "serply"` to query [Serply](https://serply.io)'s Google Search API. Serply is explicit-only: it is never chosen by `auto` or `provider: "all"`, but it can be configured as the named provider or added to `searchRouting`. The key is sent in the `X-Api-Key` request header. Domain filters are sent as Google `site:` clauses and reapplied locally; recency maps to Google's `tbs` time filter. Each request consumes Serply search credits; see the [API documentation](https://serply.io/docs).
633
640
 
641
+ **You.com.** Set `youApiKey` or `YDC_API_KEY` and select `provider: "you"` to query the [You.com Web Search API](https://you.com/docs/guides/search). You.com is explicit-only: it is never chosen by `auto` or `provider: "all"`, but it can be configured as the named provider or added to `searchRouting`. The key is sent in the `X-API-Key` request header. Domain filters map to `include_domains` and `exclude_domains`, and recency maps to `freshness`. Each request consumes You.com API credits.
642
+
634
643
  **Parallel MCP.** Select `provider: "parallel-mcp"` to use Parallel Search MCP without an API key, or add it to `searchRouting`. It is explicit-only and is never chosen by `auto` or `provider: "all"`; the existing `parallel` provider remains the key-required REST API. A configured `parallelApiKey` or `PARALLEL_API_KEY` is sent as an optional Bearer token for higher MCP limits. To use MCP `web_fetch`, add `parallel-mcp` to `fetchRouting.providers` and set `fetchRouting.allowRemoteHostedProviders` to `true`; it is not part of the default fetch route.
635
644
 
636
- Without an explicit `$` or `!` source, `OPENAI_API_KEY`, `BRAVE_API_KEY`, `PARALLEL_API_KEY`, `TINYFISH_API_KEY`, `SEARCH1API_KEY`, `SEARCHINFINITY_API_KEY`, `QUERIT_API_KEY`, `TAVILY_API_KEY`, `JINA_API_KEY`, `SERPDIVE_API_KEY`, `KAGI_API_KEY`, `BOCHA_API_KEY`, `OLLAMA_API_KEY`, `SERPBASE_API_KEY`, `SERPAPI_KEY`, `SERPER_API_KEY`, `SERPLY_API_KEY`, `BAIZHI_API_KEY`, `ANYSEARCH_API_KEY`, `XCRAWL_API_KEY`, `VALYU_API_KEY`, `XAI_API_KEY`, `MISTRAL_API_KEY`, `BRIGHTDATA_API_KEY`, `FIRECRAWL_API_KEY`, `CRAWL4AI_API_TOKEN`, `EXA_API_KEY`, `GEMINI_API_KEY`, `DATALAB_API_KEY`, `DATALAB_PROCESSING_LOCATION`, `DATALAB_MODE`, `DATALAB_API_BASE`, `PERPLEXITY_API_KEY`, `GOOGLE_GEMINI_BASE_URL`, and `CLOUDFLARE_API_KEY` env vars retain their existing precedence over literal config file values. `openaiResponsesUrl` can point OpenAI `web_search` and `source_check` at a third-party gateway that supports the OpenAI Responses API and web search tool; it is an explicit endpoint override, not derived from Pi model provider settings, and defaults to `https://api.openai.com/v1/responses`. `openaiSearchModel` pins the model id used for OpenAI `web_search`, bypassing automatic selection (newest terra-tier model); the id is sent verbatim with whichever OpenAI auth resolves, so gateway-only model ids work too. `xaiSearchModel` similarly pins the xAI search model. `openaiSearchProviders` sets which Pi model providers OpenAI `web_search` resolves login credentials from, in priority order; it defaults to `["openai-codex", "openai"]`, entries that are not registered or not signed in are skipped, and an empty array skips Pi credentials entirely so the `openaiApiKey` / `OPENAI_API_KEY` fallback applies. Useful for choosing between multiple Codex accounts (for example a second account registered by an extension) or forcing API-key billing while signed into Codex. Configured Exa API keys use Exa's own account limits directly; any legacy local `exa-usage.json` file is ignored. `GOOGLE_GEMINI_BASE_URL` overrides the Gemini API host for Gemini generate-content calls such as search, URL context, YouTube, and local video analysis. Set it to a bare host with no trailing slash and no version segment, for example `https://my-gateway.example.com/gemini`; `geminiBaseUrl` is the config-file equivalent. When the configured host contains `gateway.ai.cloudflare.com`, authentication uses `cf-aig-authorization: Bearer <token>` from `CLOUDFLARE_API_KEY` or `cloudflareApiKey`, and `GEMINI_API_KEY` is not required for generate-content calls. Alternatively, set `geminiAuth` to `"adc"` to authenticate Gemini generate-content calls with Google Application Default Credentials (ADC) instead of an API key; calls go to the Vertex AI endpoint (`aiplatform.googleapis.com`) with an OAuth bearer token minted from the ADC file (`GOOGLE_APPLICATION_CREDENTIALS` or `~/.config/gcloud/application_default_credentials.json`, i.e. `gcloud auth application-default login`). `geminiProject`/`geminiLocation` set the Vertex project and location and fall back to the `GOOGLE_CLOUD_PROJECT`/`GOOGLE_CLOUD_LOCATION` (or `GCLOUD_PROJECT`) env vars; project and location are required. ADC supports `authorized_user` (OAuth refresh token) and `service_account` (JWT assertion) credential files, and tokens are cached and refreshed from expiry. ADC mode covers search, URL context, and PDF/inline-data extraction; YouTube and local video analysis still go through the Gemini Files API, so they fall back to Gemini Web unless a `GEMINI_API_KEY` is also configured. The access token is treated as a credential and is redacted from errors. Local video file upload still uses Google's Files API directly, so gateway-only video extraction falls back to Gemini Web unless a `GEMINI_API_KEY` is also configured. `provider` or `searchProvider` sets the default search provider and is used when a tool call omits `provider` or sends `"auto"`: `"all"`, `"openai"`, `"brave"`, `"parallel"`, `"parallel-mcp"`, `"tinyfish"`, `"search1api"`, `"searchinfinity"`, `"querit"`, `"tavily"`, `"firecrawl"`, `"jina"`, `"serpdive"`, `"kagi"`, `"bocha"`, `"ollama"`, `"anysearch"`, `"xcrawl"`, `"valyu"`, `"xai"`, `"mistral"`, `"brightdata"`, `"serpbase"`, `"serpapi"`, `"serper"`, `"serply"`, `"baizhi"`, `"searxng"`, `"exa"`, `"perplexity"`, or `"gemini"`. Parallel MCP, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, Serper, Serply, and Baizhi are never selected by `auto`; choose them explicitly or place them in `searchRouting`. If either single-provider field is configured, it takes precedence over `searchRouting`. Otherwise, `searchRouting` can opt into an ordered `providers` list and an explicit `fallbackOn` list containing `"transient"`, `"quota"`, `"network"`, and/or `"invalid-response"`; only those typed failures continue to the next available candidate. `"all"` is not valid inside `searchRouting.providers`, because that list defines sequential fallback rather than multi-provider aggregation. Named providers remain strict, and exhausted routes return per-provider diagnostics. `provider` can also be a non-empty array of named providers such as `["brave", "exa"]`; those providers run concurrently using the same aggregation path as `"all"`, while `"auto"` and `"all"` are invalid inside arrays. Random, weighted, sticky, and cooldown routing are not enabled. This is also updated automatically when you change the provider in the curator UI. Set `webSearch.enabled` to `false` to unregister the configured search and source-check tools while leaving fetch/content tools available. `toolNames` can opt into alternate public tool names for environments where another extension or model reserves the defaults, without changing behavior: `webSearch`, `sourceCheck`, `fetchContent`, and `getSearchContent` default to `web_search`, `source_check`, `fetch_content`, and `get_search_content`. `workflow` sets the default search workflow: `"none"` (default; raw results, no curator), `"summary-review"` (opens curator with an auto-generated summary draft), or `"auto-summary"` (returns a model-generated summary without opening the browser curator). Overridden per-call via the `workflow` parameter on the configured search tool, or toggled at runtime with `/curator`. `browserCookies.profile` pins Gemini Web cookie lookup to a specific Chromium profile. When omitted, detected Chromium profiles are scanned in stable order and the first profile containing the required Gemini cookies is used. macOS discovery supports Helium, Chrome, Brave, and Arc; Linux discovery supports Chromium and Chrome. `allowBrowserCookies` enables Chromium cookie extraction for Gemini Web; it defaults to `false` to avoid browser data access and surprise macOS Keychain prompts. You can also set `PI_ALLOW_BROWSER_COOKIES=1`. Cookie databases are copied to a temporary read-only working copy; the reader uses `node:sqlite` when available and otherwise tries the `sqlite3` CLI or Python's standard-library SQLite module. `searchModel` overrides the Gemini API model used by the configured search tool without changing URL, YouTube, or video extraction defaults. Gemini API grounded search uses `gemini-3.6-flash` by default; set `searchModel` to choose another model. Gemini Web browser-cookie fallback uses its separate `gemini-3.1-pro` default because Gemini Web relies on private header values; explicitly configured unsupported Web models fail instead of silently falling back to 2.5 Flash. `summaryModel` sets the default model used for generating summary drafts in the curator UI and `auto-summary` mode (e.g. `"anthropic/claude-haiku-4-5"`, `"openai-codex/gpt-5.3-codex-spark"`, or `"openrouter/nvidia/nemotron-3-super-120b-a12b:free"`). Preferred summary and query-rewrite models also resolve through routed provider registrations such as OpenRouter when the native provider is unavailable. When Pi `enabledModels` is configured, summaries are limited to that allowlist; if no enabled summary model is available, the tool returns a deterministic summary instead of calling an unrelated model. `summaryGenerationDeadlineMs` sets the maximum time for one summary model attempt in the curator UI and `auto-summary` mode. It defaults to `30000`, must be a positive integer, and is capped at `600000`. `maxInlineContentChars` sets the direct `fetch_content` content slice and the default and maximum `get_search_content` slice. It defaults to `30000`, must be a positive integer, and is capped at `200000`; full fetched content remains stored for later retrieval. `curatorTimeoutSeconds` controls the initial curator idle timeout (default `20`, max `600`); users can still adjust the timer in the curator UI. `ssrf.allowRanges` lists CIDR ranges (e.g. `"198.18.0.0/15"`, `"fd00::/8"`) exempted from the SSRF guard that otherwise blocks private/reserved IP ranges. This unblocks `fetch_content`/`web_search` on hosts whose network proxy runs in TUN + fake-IP mode (Surge, Clash, Mihomo, Stash, ...), where public domains resolve into a synthetic reserved range. It is **off by default** — the guard stays fully enabled unless you list ranges here. Use the narrowest range that covers your proxy's fake-IP pool. All-address CIDRs such as `0.0.0.0/0` and `::/0` are rejected. `ssrf.trustEnvProxy` is a separate opt-in for sandboxed environments with valid HTTP(S) proxy env vars; it skips local DNS preflight only for proxied hostnames and still blocks localhost, literal private IP targets, and `NO_PROXY` matches. It does not configure proxy transport.
637
- ### Kimi Code Plan
645
+ Without an explicit `$` or `!` source, `OPENAI_API_KEY`, `BRAVE_API_KEY`, `PARALLEL_API_KEY`, `TINYFISH_API_KEY`, `SEARCH1API_KEY`, `SEARCHINFINITY_API_KEY`, `QUERIT_API_KEY`, `TAVILY_API_KEY`, `YDC_API_KEY`, `JINA_API_KEY`, `SERPDIVE_API_KEY`, `KAGI_API_KEY`, `BOCHA_API_KEY`, `OLLAMA_API_KEY`, `SERPBASE_API_KEY`, `SERPAPI_KEY`, `SERPER_API_KEY`, `SERPLY_API_KEY`, `BAIZHI_API_KEY`, `ZAI_API_KEY`, `ANYSEARCH_API_KEY`, `XCRAWL_API_KEY`, `VALYU_API_KEY`, `XAI_API_KEY`, `MISTRAL_API_KEY`, `BRIGHTDATA_API_KEY`, `FIRECRAWL_API_KEY`, `CRAWL4AI_API_TOKEN`, `EXA_API_KEY`, `GEMINI_API_KEY`, `DATALAB_API_KEY`, `DATALAB_PROCESSING_LOCATION`, `DATALAB_MODE`, `DATALAB_API_BASE`, `PERPLEXITY_API_KEY`, `GOOGLE_GEMINI_BASE_URL`, and `CLOUDFLARE_API_KEY` env vars retain their existing precedence over literal config file values. `openaiResponsesUrl` can point OpenAI `web_search` and `source_check` at a third-party gateway that supports the OpenAI Responses API and web search tool; it is an explicit endpoint override, not derived from Pi model provider settings, and defaults to `https://api.openai.com/v1/responses`. `openaiSearchModel` pins the model id used for OpenAI `web_search`, bypassing automatic selection (newest terra-tier model, then newest bare `gpt-N` or `gpt-N.N` id, then newest other `gpt-<version>` id); the id is sent verbatim with whichever OpenAI auth resolves, so gateway-only model ids work too. `xaiSearchModel` similarly pins the xAI search model. `openaiSearchProviders` sets which Pi model providers OpenAI `web_search` resolves login credentials from, in priority order; it defaults to `["openai-codex", "openai"]`, entries that are not registered or not signed in are skipped, and an empty array skips Pi credentials entirely so the `openaiApiKey` / `OPENAI_API_KEY` fallback applies. Useful for choosing between multiple Codex accounts (for example a second account registered by an extension) or forcing API-key billing while signed into Codex. Configured Exa API keys use Exa's own account limits directly; any legacy local `exa-usage.json` file is ignored. `GOOGLE_GEMINI_BASE_URL` overrides the Gemini API host for Gemini generate-content calls such as search, URL context, YouTube, and local video analysis. Set it to a bare host with no trailing slash and no version segment, for example `https://my-gateway.example.com/gemini`; `geminiBaseUrl` is the config-file equivalent. When the configured host contains `gateway.ai.cloudflare.com`, authentication uses `cf-aig-authorization: Bearer <token>` from `CLOUDFLARE_API_KEY` or `cloudflareApiKey`, and `GEMINI_API_KEY` is not required for generate-content calls. Alternatively, set `geminiAuth` to `"adc"` to authenticate Gemini generate-content calls with Google Application Default Credentials (ADC) instead of an API key; calls go to the Vertex AI endpoint (`aiplatform.googleapis.com`) with an OAuth bearer token minted from the ADC file (`GOOGLE_APPLICATION_CREDENTIALS` or `~/.config/gcloud/application_default_credentials.json`, i.e. `gcloud auth application-default login`). `geminiProject`/`geminiLocation` set the Vertex project and location and fall back to the `GOOGLE_CLOUD_PROJECT`/`GOOGLE_CLOUD_LOCATION` (or `GCLOUD_PROJECT`) env vars; project and location are required. ADC supports `authorized_user` (OAuth refresh token) and `service_account` (JWT assertion) credential files, and tokens are cached and refreshed from expiry. ADC mode covers search, URL context, and PDF/inline-data extraction; YouTube and local video analysis still go through the Gemini Files API, so they fall back to Gemini Web unless a `GEMINI_API_KEY` is also configured. The access token is treated as a credential and is redacted from errors. Local video file upload still uses Google's Files API directly, so gateway-only video extraction falls back to Gemini Web unless a `GEMINI_API_KEY` is also configured. `provider` or `searchProvider` sets the default search provider and is used when a tool call omits `provider` or sends `"auto"`: `"all"`, `"openai"`, `"brave"`, `"parallel"`, `"parallel-mcp"`, `"tinyfish"`, `"search1api"`, `"searchinfinity"`, `"querit"`, `"tavily"`, `"firecrawl"`, `"jina"`, `"serpdive"`, `"kagi"`, `"bocha"`, `"ollama"`, `"anysearch"`, `"xcrawl"`, `"valyu"`, `"xai"`, `"mistral"`, `"brightdata"`, `"serpbase"`, `"serpapi"`, `"serper"`, `"serply"`, `"you"`, `"baizhi"`, `"zai"`, `"searxng"`, `"exa"`, `"perplexity"`, or `"gemini"`. Parallel MCP, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, Serper, Serply, You.com, Baizhi, and Z.ai are never selected by `auto`; choose them explicitly or place them in `searchRouting`. If either single-provider field is configured, it takes precedence over `searchRouting`. Otherwise, `searchRouting` can opt into an ordered `providers` list and an explicit `fallbackOn` list containing `"transient"`, `"quota"`, `"network"`, and/or `"invalid-response"`; only those typed failures continue to the next available candidate. `"all"` is not valid inside `searchRouting.providers`, because that list defines sequential fallback rather than multi-provider aggregation. Named providers remain strict, and exhausted routes return per-provider diagnostics. `provider` can also be a non-empty array of named providers such as `["brave", "exa"]`; those providers run concurrently using the same aggregation path as `"all"`, while `"auto"` and `"all"` are invalid inside arrays. Random, weighted, sticky, and cooldown routing are not enabled. This is also updated automatically when you change the provider in the curator UI. Set `webSearch.enabled` to `false` to unregister the configured search and source-check tools while leaving fetch/content tools available. `toolNames` can opt into alternate public tool names for environments where another extension or model reserves the defaults, without changing behavior: `webSearch`, `sourceCheck`, `fetchContent`, and `getSearchContent` default to `web_search`, `source_check`, `fetch_content`, and `get_search_content`. `workflow` sets the default search workflow: `"none"` (default; raw results, no curator), `"summary-review"` (opens curator with an auto-generated summary draft), or `"auto-summary"` (returns a model-generated summary without opening the browser curator). Overridden per-call via the `workflow` parameter on the configured search tool, or toggled at runtime with `/curator`. `browserCookies.profile` pins Gemini Web cookie lookup to a specific Chromium profile. When omitted, detected Chromium profiles are scanned in stable order and the first profile containing the required Gemini cookies is used. macOS discovery supports Helium, Chrome, Brave, and Arc; Linux discovery supports Chromium and Chrome. `allowBrowserCookies` enables Chromium cookie extraction for Gemini Web; it defaults to `false` to avoid browser data access and surprise macOS Keychain prompts. You can also set `PI_ALLOW_BROWSER_COOKIES=1`. Cookie databases are copied to a temporary read-only working copy; the reader uses `node:sqlite` when available and otherwise tries the `sqlite3` CLI or Python's standard-library SQLite module. `searchModel` overrides the Gemini API model used by the configured search tool without changing URL, YouTube, or video extraction defaults. Gemini API grounded search uses `gemini-3.6-flash` by default; set `searchModel` to choose another model. Gemini Web browser-cookie fallback uses its separate `gemini-3.1-pro` default because Gemini Web relies on private header values; explicitly configured unsupported Web models fail instead of silently falling back to 2.5 Flash. `summaryModel` sets the default model used for generating summary drafts in the curator UI and `auto-summary` mode (e.g. `"anthropic/claude-haiku-4-5"`, `"openai-codex/gpt-5.3-codex-spark"`, or `"openrouter/nvidia/nemotron-3-super-120b-a12b:free"`). Preferred summary and query-rewrite models also resolve through routed provider registrations such as OpenRouter when the native provider is unavailable. When Pi `enabledModels` is configured, summaries are limited to that allowlist; if no enabled summary model is available, the tool returns a deterministic summary instead of calling an unrelated model. `summaryGenerationDeadlineMs` sets the maximum time for one summary model attempt in the curator UI and `auto-summary` mode. It defaults to `30000`, must be a positive integer, and is capped at `600000`. `maxInlineContentChars` sets the direct `fetch_content` content slice and the default and maximum `get_search_content` slice. It defaults to `30000`, must be a positive integer, and is capped at `200000`; full fetched content remains stored for later retrieval. `curatorTimeoutSeconds` controls the initial curator idle timeout (default `20`, max `600`); users can still adjust the timer in the curator UI. `ssrf.allowRanges` lists CIDR ranges (e.g. `"198.18.0.0/15"`, `"fd00::/8"`) exempted from the SSRF guard that otherwise blocks private/reserved IP ranges. This unblocks `fetch_content`/`web_search` on hosts whose network proxy runs in TUN + fake-IP mode (Surge, Clash, Mihomo, Stash, ...), where public domains resolve into a synthetic reserved range. It is **off by default** — the guard stays fully enabled unless you list ranges here. Use the narrowest range that covers your proxy's fake-IP pool. All-address CIDRs such as `0.0.0.0/0` and `::/0` are rejected. `ssrf.trustEnvProxy` is a separate opt-in for networks where only the proxy can resolve hostnames; it skips local DNS preflight only for hostnames sent through the configured `proxy` or, when none is configured, hostnames covered by valid HTTP(S) proxy env vars in sandboxes that route through them, and still blocks localhost, literal private IP targets, and `NO_PROXY` matches. It does not configure proxy transport.
638
646
 
647
+ ### Kimi Code Plan
639
648
  Run `/login kimi-coding` in Pi and complete sign-in for an active Kimi Code Plan. Then select `provider: "kimi"`, include `"kimi"` in an explicit provider array, or add it to `searchRouting.providers`. The extension resolves a model with provider `kimi-coding` from Pi's model registry and reuses Pi's refreshed OAuth credential; no Moonshot Open Platform key is configured here.
640
649
 
641
650
  Kimi search calls `POST https://api.kimi.com/coding/v1/search` with the query as `text_query`. This is the Kimi Code Plan search service, not Moonshot Open Platform `$web_search`. The endpoint exposes no native recency parameter, so `recencyFilter` is unsupported and is not sent. Shared `domainFilter` and `numResults` behavior is enforced locally on the returned `search_results`.
@@ -644,7 +653,7 @@ Kimi is explicit-only: it is never chosen by `auto` and never participates in `p
644
653
 
645
654
  ### All providers
646
655
 
647
- Set `provider: "all"` on `web_search` or `source_check`, or configure `"provider": "all"` as the default, to run the same query against every eligible search provider simultaneously. Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, Serper, Serply, and Baizhi are always excluded because they are explicit-only; Bright Data, SerpBase, SerpApi, Serper, and Serply are paid Google SERP providers, while Baizhi, Kimi, and Mistral may consume account quota or paid search-tool usage, so `all` never spends either resource without an explicit request. Exa remains eligible through its zero-config MCP path, OpenAI can use Pi auth, and other API-backed search providers participate when their API key, local endpoint, or gateway makes them available. Browser-cookie access alone does not opt Gemini into `all`; select Gemini explicitly or configure its API/gateway.
656
+ Set `provider: "all"` on `web_search` or `source_check`, or configure `"provider": "all"` as the default, to run the same query against every eligible search provider simultaneously. Parallel MCP, DuckDuckGo, Kimi, AnySearch, XCrawl, Valyu, xAI, Mistral, Bright Data, SerpBase, SerpApi, Serper, Serply, You.com, Baizhi, and Z.ai are always excluded because they are explicit-only; Bright Data, SerpBase, SerpApi, Serper, Serply, and You.com are paid search APIs, while Baizhi, Z.ai, Kimi, and Mistral may consume account quota or paid search-tool usage, so `all` never spends either resource without an explicit request. Exa remains eligible through its zero-config MCP path, OpenAI can use Pi auth, and other API-backed search providers participate when their API key, local endpoint, or gateway makes them available. Browser-cookie access alone does not opt Gemini into `all`; select Gemini explicitly or configure its API/gateway.
648
657
 
649
658
  Successful provider answers are preserved separately while source URLs and inline content are deduplicated, and one provider failure does not discard the other results. If every participating provider fails, the tool returns per-provider diagnostics. Configured Firecrawl participates in `all` like other eligible providers. In the Curator, **All** can also be selected like the other provider buttons. Each participating provider gets its own result card, including a provider badge and independent selection checkbox; failed providers get their own disabled error card. The final summary is generated from the selected provider cards and is what Pi receives. Outside the Curator, the same provider answers remain available as labeled sections in one tool response.
650
659
 
@@ -985,7 +994,17 @@ When `false`, a requested Curator session never tries to open a Glimpse window o
985
994
 
986
995
  ### Tool activation
987
996
 
988
- Pi starts normal sessions with the compact `web_enable` tool. Its guidance lists only the web capabilities enabled in `web-search.json`. The model can call it without a human toggle; the configured search, source-check, fetch, and stored-content tools then appear on the immediately following model request. Disabled tools stay unregistered, and alternate `toolNames` are preserved. Warm and resumed sessions retain their recorded tool selection.
997
+ With dynamic activation, Pi starts a session with only the compact `web_enable` tool. Its guidance lists only the web capabilities enabled in `web-search.json`. The model can call it without a human toggle; the configured search, source-check, fetch, and stored-content tools then appear on the immediately following model request. Disabled tools stay unregistered, and alternate `toolNames` are preserved. Warm and resumed sessions retain their recorded tool selection, so a session recorded without `web_enable` does not gain it after an install or upgrade; start a new session to use web tools there.
998
+
999
+ Enabling the tools mid-conversation can cost a prompt-cache miss. Some models accept tools added partway through a conversation. For the others, Pi resends the whole conversation on the request after `web_enable`, which can invalidate the cached prefix for that request. Later requests can cache the updated conversation again.
1000
+
1001
+ `"toolActivation"` in `web-search.json` picks the behavior:
1002
+
1003
+ - `"auto"` (the default) uses dynamic activation when the session's model accepts added tools, according to Pi's model catalog. Otherwise every enabled web tool is available from the first request, so `web_enable` never causes that cache miss. The choice is made once, from the model selected when a new session starts; switching models later doesn't change it, and warm or resumed sessions keep their recorded tools.
1004
+ - `"dynamic"` starts every new session with `web_enable`, even on models where enabling the tools can cause a cache miss.
1005
+ - `"eager"` never uses `web_enable` and keeps every enabled web tool available from the first request. It also suits models that rarely call `web_enable` on their own.
1006
+
1007
+ Restart Pi after changing it. Dynamic activation needs Pi 0.86.0 or newer; older Pi logs a warning and keeps every enabled web tool available.
989
1008
 
990
1009
  ### Shortcuts
991
1010
 
@@ -1009,7 +1028,7 @@ Rate limits: Perplexity is capped at 10 requests/minute (client-side). Jina Sear
1009
1028
  ## Limitations
1010
1029
 
1011
1030
  - If the curator cannot open a browser automatically, such as in Docker, WSL, SSH, or headless environments, the running curator URL is shown in the tool output. Copy it into a browser that can reach the Pi host, or use a tunnel/port-forward when needed.
1012
- - Chromium cookie extraction for Gemini Web is opt-in via `allowBrowserCookies: true` or `PI_ALLOW_BROWSER_COOKIES=1`; no browser data or password store is touched while it is disabled. On macOS, enabling it may trigger a Keychain dialog. On Windows, Chrome and Edge v10 cookies use the current user's DPAPI key; v20 app-bound cookies are not supported. Required cookie names are checked before password-store access, and browser encryption passwords are cached only in-process. If `node:sqlite` is unavailable, the reader falls back to the `sqlite3` CLI or Python stdlib; `/google-account` reports sanitized browser/profile attempts and classifies SQLite, profile, missing-cookie, password-store, and decryption failures.
1031
+ - Chromium cookie extraction for Gemini Web is opt-in via `allowBrowserCookies: true` or `PI_ALLOW_BROWSER_COOKIES=1`; no browser data or password store is touched while it is disabled. On macOS, enabling it may trigger a Keychain dialog. On Linux, Chrome and Chromium check Secret Service first, then use `kwallet-query` for the standard KWallet safe-storage entry during a KDE session. On Windows, Chrome and Edge v10 cookies use the current user's DPAPI key; v20 app-bound cookies are not supported. Required cookie names are checked before password-store access, and browser encryption passwords are cached only in-process. If `node:sqlite` is unavailable, the reader falls back to the `sqlite3` CLI or Python stdlib; `/google-account` reports sanitized browser/profile attempts and classifies SQLite, profile, missing-cookie, password-store, and decryption failures.
1013
1032
  - YouTube private/age-restricted videos may fail on all extraction paths.
1014
1033
  - Gemini can process videos up to ~1 hour; longer videos may be truncated.
1015
1034
  - PDFs are text-extracted only (no OCR for scanned documents).
@@ -1034,6 +1053,7 @@ Rate limits: Perplexity is capped at 10 requests/minute (client-side). Jina Sear
1034
1053
  | `searchinfinity.ts` | Byteplus Searchinfinity search provider |
1035
1054
  | `querit.ts` | Querit Search and Contents API provider |
1036
1055
  | `tavily.ts` | Tavily Search API provider |
1056
+ | `you.ts` | You.com Search API provider |
1037
1057
  | `firecrawl.ts` | Firecrawl search provider and extraction fallback |
1038
1058
  | `crawl4ai.ts` | Self-hosted Crawl4AI extraction fallback |
1039
1059
  | `jina-search.ts` | Jina Search API provider |
@@ -1046,6 +1066,7 @@ Rate limits: Perplexity is capped at 10 requests/minute (client-side). Jina Sear
1046
1066
  | `serper.ts` | Explicit-only Serper Google SERP provider |
1047
1067
  | `serply.ts` | Explicit-only Serply Google SERP provider |
1048
1068
  | `baizhi.ts` | Explicit-only Baizhi Streamable HTTP MCP search provider |
1069
+ | `zai.ts` | Explicit-only Z.ai GLM Coding Plan MCP search provider |
1049
1070
  | `anysearch.ts` | Explicit-only AnySearch search provider |
1050
1071
  | `xcrawl.ts` | Explicit-only XCrawl search provider |
1051
1072
  | `valyu.ts` | Explicit-only Valyu research search provider |