@dumbmice/pi-deepseek-websearch 0.1.4 → 0.1.6
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/AGENTS.md +12 -6
- package/CHANGELOG.md +24 -1
- package/README.md +10 -4
- package/docs/docs-alignment.md +6 -3
- package/docs/pi-api-alignment.md +42 -0
- package/package.json +3 -3
- package/src/index.ts +60 -40
package/AGENTS.md
CHANGED
|
@@ -10,6 +10,7 @@ Instructions for anyone — human or coding agent — changing
|
|
|
10
10
|
src/index.ts the extension: two tool registrations, transport, formatting, guards
|
|
11
11
|
test/index.test.ts node:test suite; global fetch is stubbed, nothing touches the network
|
|
12
12
|
docs/docs-alignment.md every client-side deviation from DeepSeek's published API contract
|
|
13
|
+
docs/pi-api-alignment.md which pi release this package is verified against
|
|
13
14
|
README.md user-facing documentation
|
|
14
15
|
AGENTS.md this file
|
|
15
16
|
CONTRIBUTING.md four-line pointer to this file (GitHub convention)
|
|
@@ -21,8 +22,8 @@ tsconfig.json the strict / noEmit settings used by typecheck
|
|
|
21
22
|
|
|
22
23
|
## Requirements and commands
|
|
23
24
|
|
|
24
|
-
Node >= 22.
|
|
25
|
-
`--experimental-strip-types`). devDependencies stay limited to `typescript` and `@types/node`; the
|
|
25
|
+
Node >= 22.19 (`engines`, the floor pi 1.5.1 itself requires; the entry point is TypeScript and
|
|
26
|
+
the test runner uses `--experimental-strip-types`). devDependencies stay limited to `typescript` and `@types/node`; the
|
|
26
27
|
publish workflow installs with `npm ci`, the test matrix with `npm install`.
|
|
27
28
|
|
|
28
29
|
```bash
|
|
@@ -45,10 +46,12 @@ README text, the matching `docs/docs-alignment.md` entry (or marking that item s
|
|
|
45
46
|
no-runtime-dependencies rule. Do not replace them with `Type.Object` imports.
|
|
46
47
|
- **A fitting query is never rewritten.** `fitQuery` returns the caller's text untouched when it fits,
|
|
47
48
|
apart from stripped leading/trailing whitespace. Only an over-long query is tokenised and trimmed.
|
|
48
|
-
- **
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
- **Filters are constraints; words are expendable.** When the assembled query exceeds 128 characters,
|
|
50
|
+
keep every filter and remove trailing words; cut one indivisible token by code point only if it
|
|
51
|
+
alone exceeds the remaining budget. If the filters leave no room for search text, fail before the
|
|
52
|
+
request. Drop a dangling operator or unmatched quote, and record every removal in `dropped`.
|
|
53
|
+
Whitespace stripped from `query`, `ori_question` and `localization` before use is silent
|
|
54
|
+
(register item 3).
|
|
52
55
|
- **Cost follows the raw response.** `estimatedCostUsd` is keyed to `rawResults.length`, not to what
|
|
53
56
|
survives client-side filtering, because DeepSeek bills a non-empty result set.
|
|
54
57
|
- **A malformed 200 is an error.** A body without a `results` array is rejected; an empty array is a
|
|
@@ -116,6 +119,9 @@ is enough to check rendering. `npm test` needs neither key nor network.
|
|
|
116
119
|
and constant and at the top of the register — keep them in step.
|
|
117
120
|
- `CHANGELOG.md` follows Keep a Changelog. Put unreleased work under `[Unreleased]` with
|
|
118
121
|
Added/Changed/Fixed headings; add the version heading when the release is tagged.
|
|
122
|
+
- `docs/pi-api-alignment.md` records the pi release and Node version this package is verified
|
|
123
|
+
against. `PI_API_SNAPSHOT` in `src/index.ts`, that file and the README *Install* line move together;
|
|
124
|
+
`test/docs.test.ts` fails when they drift.
|
|
119
125
|
- `AGENTS.md` (this file) holds contributor, architecture and release material.
|
|
120
126
|
|
|
121
127
|
## Release process
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,27 @@ All notable changes to this extension are documented in this file. The format fo
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.1.6] - 2026-11-06
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `docs/pi-api-alignment.md`: a tested-version table recording the pi release (`1.5.1`) and Node
|
|
14
|
+
version this package is verified against, pinned by `PI_API_SNAPSHOT` and a docs test so the claim
|
|
15
|
+
cannot drift from the code.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- `engines.node` raised to `>=22.19`, matching pi 1.5.1's own floor. The README now states the pi
|
|
20
|
+
version it was tested with.
|
|
21
|
+
|
|
22
|
+
## [0.1.5] - 2026-11-06
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Over-length queries no longer lose their filters. Trimming keeps `allowed_domains`, `blocked_domains`,
|
|
27
|
+
`lang` and `recency` and removes trailing words instead; a filter set that leaves no room for search
|
|
28
|
+
text fails before the request rather than sending the query with those filters stripped.
|
|
29
|
+
|
|
9
30
|
## [0.1.4] - 2026-10-30
|
|
10
31
|
|
|
11
32
|
### Added
|
|
@@ -72,7 +93,9 @@ results are not billed. Validated against the DeepSeek API documentation as of 2
|
|
|
72
93
|
- `deepseek_fetch` rejects local and private targets and never unescapes inside code.
|
|
73
94
|
|
|
74
95
|
|
|
75
|
-
[Unreleased]: https://github.com/DumbMice/pi-deepseek-websearch/compare/v0.1.
|
|
96
|
+
[Unreleased]: https://github.com/DumbMice/pi-deepseek-websearch/compare/v0.1.6...HEAD
|
|
97
|
+
[0.1.6]: https://github.com/DumbMice/pi-deepseek-websearch/compare/v0.1.5...v0.1.6
|
|
98
|
+
[0.1.5]: https://github.com/DumbMice/pi-deepseek-websearch/compare/v0.1.4...v0.1.5
|
|
76
99
|
[0.1.4]: https://github.com/DumbMice/pi-deepseek-websearch/compare/v0.1.3...v0.1.4
|
|
77
100
|
[0.1.3]: https://github.com/DumbMice/pi-deepseek-websearch/compare/v0.1.2...v0.1.3
|
|
78
101
|
[0.1.2]: https://github.com/DumbMice/pi-deepseek-websearch/compare/v0.1.1...v0.1.2
|
package/README.md
CHANGED
|
@@ -46,13 +46,17 @@ pi install git:github.com/DumbMice/pi-deepseek-websearch
|
|
|
46
46
|
|
|
47
47
|
Requirements:
|
|
48
48
|
|
|
49
|
-
-
|
|
49
|
+
- **pi 1.5.1** (tested).
|
|
50
|
+
- Node >= 22.19, the floor pi itself requires.
|
|
50
51
|
- A DeepSeek key, from pi's `deepseek` provider (`/login`) or the `DEEPSEEK_API_KEY` variable. Create
|
|
51
52
|
one at <https://platform.deepseek.com/api_keys>.
|
|
52
53
|
- Optional, for a compatible gateway: set `PI_DEEPSEEK_BASE_URL` or `DEEPSEEK_SEARCH_BASE_URL`. A
|
|
53
54
|
trailing slash or `/v1` suffix is stripped. The bare `DEEPSEEK_BASE_URL` is ignored on purpose,
|
|
54
55
|
because other tools point it at an OpenAI-compatible `/v1` root.
|
|
55
56
|
|
|
57
|
+
Last verified 2026-11-06; the table and what was checked is in
|
|
58
|
+
[docs/pi-api-alignment.md](https://github.com/DumbMice/pi-deepseek-websearch/blob/main/docs/pi-api-alignment.md).
|
|
59
|
+
|
|
56
60
|
Confirm registration by starting `pi` and asking *"use deepseek_search to look up the DeepSeek API
|
|
57
61
|
pricing"*; the reply begins with a `DeepSeek search · N result(s)` header.
|
|
58
62
|
|
|
@@ -104,9 +108,11 @@ an error, not as an empty success.
|
|
|
104
108
|
|
|
105
109
|
## Behaviour to expect
|
|
106
110
|
|
|
107
|
-
- **Long queries are trimmed, not rejected.** The 128-character cap includes operators
|
|
108
|
-
is shortened,
|
|
109
|
-
|
|
111
|
+
- **Long queries are trimmed, not rejected.** The 128-character cap includes operators. When a query
|
|
112
|
+
is shortened, trailing words are removed and the filters are kept (a single word or quoted
|
|
113
|
+
phrase longer than the remaining room is cut); the reply names what went and prints the final query. If the filters alone
|
|
114
|
+
leave no room for search text, the call fails before any request — shorten the domain list or
|
|
115
|
+
remove a filter to send it.
|
|
110
116
|
- **Transient failures are retried.** `408`, `429`, any `5xx`, network failures and timeouts get up
|
|
111
117
|
to three attempts, honouring `retry-after` (seconds or an HTTP date) capped at 5 seconds. Every
|
|
112
118
|
other `4xx` fails immediately with DeepSeek's own message. Each attempt has its own deadline: 30
|
package/docs/docs-alignment.md
CHANGED
|
@@ -11,8 +11,9 @@ extension can emit is always a valid request.
|
|
|
11
11
|
Client-side deviations and transport details, listed in full. Items that produce no signal in the
|
|
12
12
|
output are marked _(silent)_; everything else is observable in the reply or in `details`.
|
|
13
13
|
|
|
14
|
-
1. over-length queries are trimmed rather than rejected, and every removal is reported
|
|
15
|
-
token longer than the
|
|
14
|
+
1. over-length queries are trimmed rather than rejected, and every removal is reported. Filters are
|
|
15
|
+
kept and trailing words are removed; a single token longer than the remaining budget is cut
|
|
16
|
+
mid-token. If the filters alone leave no room for search text, the call fails before the request;
|
|
16
17
|
2. query whitespace is normalised only when the query must be rewritten, and that is reported in the
|
|
17
18
|
same line;
|
|
18
19
|
3. leading and trailing whitespace is stripped from `query`, `ori_question` and `localization` before
|
|
@@ -63,7 +64,9 @@ output are marked _(silent)_; everything else is observable in the reply or in `
|
|
|
63
64
|
23. result fields of an unexpected type are ignored rather than passed through, so one malformed
|
|
64
65
|
result degrades instead of failing the call _(silent)_;
|
|
65
66
|
24. `recency` windows are fixed client-side at 1 / 7 / 30 / 365 days for `day` / `week` / `month` /
|
|
66
|
-
`year
|
|
67
|
+
`year`;
|
|
68
|
+
25. domain list entries that are empty or contain whitespace or a quote are dropped before the query is
|
|
69
|
+
assembled _(silent)_.
|
|
67
70
|
|
|
68
71
|
Transport details: requests send `Accept: application/json` alongside the two documented headers;
|
|
69
72
|
retry backoff is 500 ms then 1 s when no `retry-after` is present (two sleeps for three attempts); and
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# pi extension API alignment
|
|
2
|
+
|
|
3
|
+
How this extension's use of pi's extension API relates to the documented contract, and which exact
|
|
4
|
+
versions that was established against.
|
|
5
|
+
|
|
6
|
+
| Component | Tested version | Recorded in |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| `@earendil-works/pi-coding-agent` | **1.5.1** | `PI_API_SNAPSHOT` in `src/index.ts` |
|
|
9
|
+
| `@earendil-works/pi-schema` (tool validation) | ships with pi 1.5.1 | this file (notes below) |
|
|
10
|
+
| Node | 22.23.2 (tested); floor 22.19, the floor pi itself requires | `package.json` `engines`, README *Install* |
|
|
11
|
+
| DeepSeek Search & Fetch API | docs snapshot 2026-10-30 | `DOCS_SNAPSHOT`; see `docs/docs-alignment.md` |
|
|
12
|
+
|
|
13
|
+
Last verified **2026-11-06**. Bump a row here, its constant where one exists, and the README
|
|
14
|
+
*Install* line in the same commit as the change that depends on it.
|
|
15
|
+
|
|
16
|
+
## What this extension actually uses
|
|
17
|
+
|
|
18
|
+
The contract is followed where it is documented, and nothing here calls pi internals:
|
|
19
|
+
|
|
20
|
+
- `pi.registerTool()` with a hand-written JSON Schema `parameters` object. pi validates any JSON
|
|
21
|
+
Schema; the `typebox` import in the docs is a convenience, and avoiding it keeps the package
|
|
22
|
+
dependency-free.
|
|
23
|
+
- `execute(toolCallId, params, signal, onUpdate, ctx)` — the five-argument form.
|
|
24
|
+
- `ctx.modelRegistry.getApiKeyForProvider("deepseek")` for credentials, falling back to
|
|
25
|
+
`DEEPSEEK_API_KEY`.
|
|
26
|
+
- The `pi.extensions` manifest entry in `package.json`.
|
|
27
|
+
|
|
28
|
+
It registers no commands, hooks, renderers or UI, so a release that only changes those surfaces
|
|
29
|
+
cannot affect this package.
|
|
30
|
+
|
|
31
|
+
## Notes on the 1.5.x line
|
|
32
|
+
|
|
33
|
+
1. **Tool validation moved to `@earendil-works/pi-schema` in 1.5.0.** TypeBox features outside JSON
|
|
34
|
+
Schema no longer apply; plain JSON Schema tools now get the same coercion as TypeBox tools
|
|
35
|
+
(numeric strings to numbers, `"true"` to `true`, a single value to a one-item array, `null` in an
|
|
36
|
+
optional property dropped). This package uses only JSON Schema keywords, so nothing was lost, and
|
|
37
|
+
the coercion is a net improvement: `allowed_domains: "example.com"` now arrives as
|
|
38
|
+
`["example.com"]` instead of being rejected.
|
|
39
|
+
2. **Package changelogs are read from the package root by 1.5.x.** `CHANGELOG.md` uses the required
|
|
40
|
+
`## [x.y.z]` headings, so extension updates surface our notes without a `pi.changelogPath` entry.
|
|
41
|
+
3. **Node floor.** pi 1.5.1 declares `>=22.19.0`; this package declares the same floor, because it
|
|
42
|
+
only runs inside pi.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dumbmice/pi-deepseek-websearch",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "DeepSeek web search and page fetch tools for the pi coding agent: publication dates, authority, rerank scores, ori_question, count 1-50.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
]
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
|
-
"test": "node --experimental-strip-types --no-warnings --test test/index.test.ts",
|
|
44
|
+
"test": "node --experimental-strip-types --no-warnings --test test/index.test.ts test/docs.test.ts",
|
|
45
45
|
"typecheck": "tsc -p tsconfig.json",
|
|
46
46
|
"pack:check": "npm pack --dry-run"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
|
-
"node": ">=22.
|
|
49
|
+
"node": ">=22.19"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "^22.20.5",
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { isIP } from "node:net";
|
|
22
22
|
|
|
23
23
|
export const DOCS_SNAPSHOT = "2026-10-30";
|
|
24
|
+
export const PI_API_SNAPSHOT = "2026-11-06";
|
|
24
25
|
export const PRICE_PER_SUCCESSFUL_REQUEST_USD = 0.00075;
|
|
25
26
|
export const MAX_QUERY_CHARS = 128;
|
|
26
27
|
|
|
@@ -242,10 +243,9 @@ async function postDeepSeek<T>(
|
|
|
242
243
|
/* ------------------------------------------------------------------- query */
|
|
243
244
|
|
|
244
245
|
/**
|
|
245
|
-
*
|
|
246
|
-
* operators). Filters are
|
|
247
|
-
*
|
|
248
|
-
* the request differs from what was asked for.
|
|
246
|
+
* Fit the assembled query into the API's hard limit (docs: 1-128 Unicode characters,
|
|
247
|
+
* operators included). Filters are explicit constraints, so they are kept and the
|
|
248
|
+
* free-text part is shortened instead. Every removal is reported.
|
|
249
249
|
*/
|
|
250
250
|
export function fitQuery(options: SearchOptions): { query: string; dropped: string[] } {
|
|
251
251
|
const dropped: string[] = [];
|
|
@@ -272,33 +272,36 @@ export function fitQuery(options: SearchOptions): { query: string; dropped: stri
|
|
|
272
272
|
const untouched = [raw, ...filters].filter(Boolean).join(" ");
|
|
273
273
|
if (charLength(untouched) <= MAX_QUERY_CHARS) return { query: untouched, dropped };
|
|
274
274
|
|
|
275
|
-
//
|
|
275
|
+
// The filters occupy the tail of the string, so they set the budget for the words.
|
|
276
|
+
const filterText = filters.join(" ");
|
|
277
|
+
const filterCost = filterText ? charLength(filterText) + 1 : 0;
|
|
278
|
+
const available = MAX_QUERY_CHARS - filterCost;
|
|
279
|
+
if (available < 1) {
|
|
280
|
+
const names = [
|
|
281
|
+
options.allowedDomains.length > 0 ? "allowed_domains" : "",
|
|
282
|
+
options.blockedDomains.length > 0 ? "blocked_domains" : "",
|
|
283
|
+
options.lang ? "lang" : "",
|
|
284
|
+
options.recency ? "recency" : "",
|
|
285
|
+
].filter(Boolean);
|
|
286
|
+
const named =
|
|
287
|
+
names.length > 1 ? `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}` : names[0]!;
|
|
288
|
+
throw new Error(
|
|
289
|
+
`The ${named} ${names.length > 1 ? "filters need" : "filter needs"} ${filterCost} characters ` +
|
|
290
|
+
"of the 128-character query limit, leaving no room for the search text. " +
|
|
291
|
+
"Shorten the filter values or remove a filter.",
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
276
295
|
// An unescaped quoted phrase is one token: slicing mid-phrase would change its meaning.
|
|
277
296
|
// Backslash-escaped quotes are not special-cased (only reachable on over-length queries).
|
|
278
297
|
const matchedTokens = raw.match(/"[^"]*"|\S+/g);
|
|
279
|
-
|
|
298
|
+
let words: string[] = matchedTokens ? [...matchedTokens] : [raw];
|
|
280
299
|
const quotesWereBalanced = ((raw.match(/"/g) ?? []).length % 2) === 0;
|
|
281
|
-
if (raw !==
|
|
282
|
-
let words = tokens;
|
|
283
|
-
let active = [...filters];
|
|
284
|
-
|
|
285
|
-
const overBudget = () => charLength(join(words, active)) > MAX_QUERY_CHARS;
|
|
286
|
-
const dropFilter = (predicate: (filter: string) => boolean, label: (filter: string) => string): void => {
|
|
287
|
-
const index = active.findIndex(predicate);
|
|
288
|
-
if (index < 0 || !overBudget()) return;
|
|
289
|
-
const [removed] = active.splice(index, 1);
|
|
290
|
-
dropped.push(label(removed!));
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
dropFilter((f) => f.startsWith("after:"), (f) => `recency (${f})`);
|
|
294
|
-
dropFilter((f) => f.startsWith("lang:"), (f) => `language (${f})`);
|
|
295
|
-
while (overBudget() && active.length > 0) {
|
|
296
|
-
const removed = active.pop()!;
|
|
297
|
-
dropped.push(`domain filter (${removed.slice(0, 40)})`);
|
|
298
|
-
}
|
|
300
|
+
if (raw !== words.join(" ")) dropped.push("query whitespace normalised");
|
|
299
301
|
|
|
300
302
|
const wordsBefore = words.join(" ");
|
|
301
|
-
while (words.length > 1 &&
|
|
303
|
+
while (words.length > 1 && charLength(words.join(" ")) > available) words = words.slice(0, -1);
|
|
304
|
+
|
|
302
305
|
// Never leave a dangling operator (`site:`, `-site:`, `after:`, a bare `-`) or an
|
|
303
306
|
// unbalanced quote behind: DeepSeek would answer 400 and no retry would help.
|
|
304
307
|
const danglingOperator = /^(?:-|-[A-Za-z]+:|[A-Za-z]+:)$/;
|
|
@@ -313,20 +316,37 @@ export function fitQuery(options: SearchOptions): { query: string; dropped: stri
|
|
|
313
316
|
dropped.push("trailing query words");
|
|
314
317
|
}
|
|
315
318
|
|
|
316
|
-
let query = join(words,
|
|
319
|
+
let query = join(words, filters);
|
|
317
320
|
if (charLength(query) > MAX_QUERY_CHARS) {
|
|
318
|
-
//
|
|
319
|
-
// by code point
|
|
320
|
-
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
321
|
+
// One indivisible token longer than the remaining budget: cutting it is unavoidable,
|
|
322
|
+
// so do that by code point rather than dropping a filter.
|
|
323
|
+
let cut = Array.from(words.join(" ")).slice(0, available).join("").trim();
|
|
324
|
+
// Repeat until stable: splicing an unmatched quote can expose a trailing `-`/`:`.
|
|
325
|
+
for (let changed = true; changed; ) {
|
|
326
|
+
changed = false;
|
|
327
|
+
const stripped = cut.replace(/[-:]+$/, "").trimEnd();
|
|
328
|
+
if (stripped !== cut) {
|
|
329
|
+
cut = stripped;
|
|
330
|
+
changed = true;
|
|
331
|
+
}
|
|
332
|
+
if (quotesWereBalanced && (cut.match(/"/g) ?? []).length % 2 === 1) {
|
|
333
|
+
const balanced = Array.from(cut);
|
|
334
|
+
const lastQuote = balanced.lastIndexOf('"');
|
|
335
|
+
if (lastQuote >= 0) balanced.splice(lastQuote, 1);
|
|
336
|
+
const next = balanced.join("").trimEnd();
|
|
337
|
+
if (next !== cut) {
|
|
338
|
+
cut = next;
|
|
339
|
+
changed = true;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (cut === "" && filters.length === 0) {
|
|
344
|
+
throw new Error(
|
|
345
|
+
"The query has no searchable text within the 128-character limit — it consists only of " +
|
|
346
|
+
"operators or punctuation. Add search terms.",
|
|
347
|
+
);
|
|
329
348
|
}
|
|
349
|
+
query = join([cut], filters);
|
|
330
350
|
dropped.push("query tail");
|
|
331
351
|
}
|
|
332
352
|
return { query, dropped };
|
|
@@ -502,7 +522,7 @@ const SEARCH_SCHEMA = {
|
|
|
502
522
|
type: "string",
|
|
503
523
|
minLength: 1,
|
|
504
524
|
description:
|
|
505
|
-
'Search query. Supports operators: "exact phrase", -word, -"phrase", site:a.com OR site:b.com, -site:c.com, lang:en,zh, after:2025-01-01, before:2026-06-01. Queries are capped at 128 chars including operators; anything longer is trimmed automatically (
|
|
525
|
+
'Search query. Supports operators: "exact phrase", -word, -"phrase", site:a.com OR site:b.com, -site:c.com, lang:en,zh, after:2025-01-01, before:2026-06-01. Queries are capped at 128 chars including operators; anything longer is trimmed automatically (trailing words are removed, filters are kept) and the removals are reported in the output — do not pre-shorten.',
|
|
506
526
|
},
|
|
507
527
|
count: {
|
|
508
528
|
type: "integer",
|
|
@@ -524,13 +544,13 @@ const SEARCH_SCHEMA = {
|
|
|
524
544
|
type: "array",
|
|
525
545
|
items: { type: "string" },
|
|
526
546
|
description:
|
|
527
|
-
"Restrict results to these domains (compiled into site: OR site:). Counts toward the 128-char query cap
|
|
547
|
+
"Restrict results to these domains (compiled into site: OR site:). Counts toward the 128-char query cap and is never dropped by trimming; if the filters leave no room for the query text, the call fails before any request.",
|
|
528
548
|
},
|
|
529
549
|
blocked_domains: {
|
|
530
550
|
type: "array",
|
|
531
551
|
items: { type: "string" },
|
|
532
552
|
description:
|
|
533
|
-
"Exclude these domains (compiled into -site:). Counts toward the 128-char query cap and
|
|
553
|
+
"Exclude these domains (compiled into -site:). Counts toward the 128-char query cap and is never dropped by trimming; if the filters leave no room for the query text, the call fails before any request.",
|
|
534
554
|
},
|
|
535
555
|
lang: { type: "string", description: 'Language codes, e.g. "zh,en" (becomes lang:).' },
|
|
536
556
|
recency: {
|