import type { HostContext } from './types.js'; /** 本 provider 在官方 web seam 注册表中的 id。 */ export declare const ZH_WEB_SEARCH_PROVIDER_ID = "dsh-zh-web"; /** * 官方/第三方搜索 provider id(供设置页展示与优先级默认值使用)。 * ZHIPU_SEARCH_PROVIDER_ID 与 OFFICIAL_DEEPSEEK_PROVIDER_ID 保留为导出常量, * 因为设置页要用它们给用户展示「这一项指的是哪个后端」,而不是硬编码字符串。 */ export declare const ZHIPU_SEARCH_PROVIDER_ID = "zhipu-web-search-prime"; /** 官方内置 DeepSeek 搜索 provider id(dsh-web-search-deepseek)。 */ export declare const OFFICIAL_DEEPSEEK_PROVIDER_ID = "deepseek-official"; /** 传输层请求(duck-type 足够窄,便于测试注入 mock)。 */ export interface WebSearchTransportRequest { method: 'GET' | 'POST'; url: string; /** POST body 字符串(application/x-www-form-urlencoded)。 */ body?: string; headers?: Record; /** 重定向跟随上限(默认 0 = 不跟随,与官方 web fetch 的 redirect:'error' 语义对齐)。 */ maxRedirects?: number; timeoutMs?: number; signal?: AbortSignal; } export interface WebSearchTransportResponse { status: number; /** 最终响应 URL(未跟随重定向时即请求 URL)。 */ url: string; headers: Record; body: string; } export interface WebSearchTransport { request(request: WebSearchTransportRequest): Promise; } /** 生产传输(Chrome 指纹 + 手动解压 + 手动跟随重定向);verify-websearch.cjs 注入 mock。 */ export declare const webSearchTransport: WebSearchTransport; interface WebSearchSourceLike { url: string; title?: string; snippet?: string; publishedAt?: string; } /** * 解析 html 端点结果页。结构(2026-09-21 实测): * * lite 端点为表单行结构,用同一解析函数兜底(result-link / result-snippet)。 */ export declare function parseDdgResults(html: string): Array<{ url: string; title?: string; snippet?: string; }>; /** * 还原 Bing 结果链接的真实 URL。Bing 2026-09 起把所有结果链接包进 * `bing.com/ck/a?...&u=a1` 跳转(学习 ddgs engines/bing.py 的 * unwrap_bing_url:u 值去掉 `a1` 前缀做 base64url 解码)。 * 广告(aclick)与无法还原的链接返回空串(上层丢弃)。 */ export declare function unwrapBingUrl(href: string): string; /** * 解析 Bing 结果页。结构(2026-09 实测,b_algo 块内含大量 噪声): *
  • 标题

    *

    摘要

  • */ export declare function parseBingResults(html: string): Array<{ url: string; title?: string; snippet?: string; }>; /** * 解析 Bing RSS 结果(`&format=rss`,2026-09-23 实测)。RSS 通道是 Bing 的稳定 * 通道:体积约 4KB、结构固定、link 为真实 URL(无需 ck/a 解包),且不像 HTML * 端点那样在部分查询上偶发「200 但 0 条 b_algo」的空页。 * 结构:…https://…… * RFC822(pubDate 是本地化星期/月份名,解析不出 ISO 时省略)。 */ export declare function parseBingRss(body: string): Array<{ url: string; title?: string; snippet?: string; publishedAt?: string; }>; /** * 解析 Yandex `/search/site/` 结果页。结构(2026-09-23 实测): *
  • *

    标题

    *
    摘要
  • * * 两个关键点: * - href 就是**真实 URL**(跳转只藏在 onmousedown 的 clck/jsredir 里),不需要 * Bing 那样的 ck/a 解包;但仍过滤 clck/jsredir 形态以防页面结构回退。 * - 命中词用 `` **逐字**包住中文(`深圳天气`)。 * cleanText 用空串剥标签 → 「深圳天气」;若换成空格就会变成「深 圳 天 气」, * 中文查询会被相关性闸门的 CJK 二元组匹配整批误杀。 */ export declare function parseYandexResults(html: string): Array<{ url: string; title?: string; snippet?: string; }>; /** * 解析 Tavily `/search` 的 JSON:`{ results: [{ url, title, content, score }], answer }`。 * content 是正文片段,按 `TAVILY_SNIPPET_MAX` 截断(不截会把上下文撑爆)。 * answer(basic 深度下通常为 null)不作为 source,本 provider 只回来源列表。 */ export declare function parseTavilyJson(body: string): Array<{ url: string; title?: string; snippet?: string; }>; /** 解析 MediaWiki opensearch JSON:[搜索词, 标题数组, 摘要数组, URL 数组]。 */ export declare function parseOpensearchJson(body: string): ReadonlyArray<{ url: string; title?: string; snippet?: string; }>; /** 测试钩子:重置 DDG 限流记忆与排队链、Tavily 退避、缺简介换后端计数(仅 verify-websearch.cjs 使用)。 */ export declare function resetWebSearchEngineState(): void; /** 形状同构官方 WebError 的本插件错误(码语义见 ZhipuError 同款约定)。 */ export declare const ZH_WEB_PROVIDER_ERROR_CODE = "WEB_PROVIDER_ERROR"; export declare class ZhWebError extends Error { readonly code: string; constructor(message: string, options?: ErrorOptions); } /** * 结果是否「带简介」到可用程度:snippet 非空的条数占比 ≥ {@link SNIPPET_COVERAGE_MIN}。 * * 用途是识别「只有标题和链接」的后端降级结果——典型是智谱插件被禁用后其 provider * 仍 available()(回退内置 DeepSeek 搜索),而 DeepSeek 的 web_search_result 项只有 * title/url/encrypted_content/page_age(正文加密、本地不可解),text 块也不带 * citations,因此整批结果零简介(2026-10-16 实测 10/10 条缺失)。用户看不到每条 * 具体讲什么,只能逐个点开——这正是「不如智谱三件套的搜索」的直接原因。 * * 空结果集返回 true(「无结果」是合法结局,不该因此换后端重试)。 */ export declare function hasUsableSnippets(sources: ReadonlyArray): boolean; /** 一次「智谱失败 → 免费后端」降级的记录。 */ export interface WebSearchFallbackRecord { /** 降级发生时间(ISO-8601)。 */ at: string; /** 智谱失败的错误码(如 ZHIPU_CONTENT_FILTERED;无码时为 'error')。 */ code: string; /** 失败消息(截断到 200 字符)。 */ message: string; /** 触发降级的查询(截断到 200 字符;不记录搜索结果)。 */ query: string; } export interface WebSearchFallbackState { /** 本进程内累计降级次数。 */ count: number; /** 最近一次降级记录。 */ last?: WebSearchFallbackRecord; } /** 读取降级运行态(诊断用;绝不影响搜索)。 */ export declare function getWebSearchFallbackState(): WebSearchFallbackState; /** * 一次「智谱结果缺简介 → 改走免费多引擎后端」的记录。 * 与「智谱失败降级」分开计数:前者智谱并未失败(禁用态回退 DeepSeek 原生搜索 * 仍算成功),只是结果不含摘要,混在一起会让诊断看不出真实成因。 */ export interface WebSearchSnippetFallbackRecord { /** 触发时间(ISO-8601)。 */ at: string; /** 触发时的查询(截断到 200 字符;不记录搜索结果)。 */ query: string; /** 智谱返回的来源条数(全部缺简介)。 */ sourceCount: number; } export interface WebSearchSnippetFallbackState { /** 本进程内累计次数。 */ count: number; /** 最近一次记录。 */ last?: WebSearchSnippetFallbackRecord; } /** 读取「缺简介换后端」运行态(诊断用;绝不影响搜索)。 */ export declare function getWebSearchSnippetFallbackState(): WebSearchSnippetFallbackState; export interface WebSearchInstallDeps { /** 读取 webSearchEnabled 开关(dsh-zh 命名空间,默认 true)。 */ isEnabled(): boolean; /** 读取后端优先级列表(有序 provider id,空数组 = 不接管)。 */ searchOrder(): readonly string[]; } /** * 把官方 web_search 的后端选择切到本插件的复合 provider(active=true), * 或恢复为接管前的值(active=false)。 * * 为什么必须改选路而不只是注册 provider:官方 resolveProvider 严格按 * `web` 行的 configuredId 选后端——configured 存在但 available()=false 会直接 * 报 WEB_PROVIDER_CONFIGURED_UNAVAILABLE,**不会**回落到其它 provider。因此 * 仅注册不选中就永远不会被调用。 * * 写通道按宿主代际自动选择: * 1. `ctx.settings.update('web', { searchProvider })`——官方支持、持久化、 * volatile 即时生效(DSH ≥ 0.2.1 的 searchProvider 是 volatile 字段); * 2. 旧宿主(≤ 0.2.0)的实例字段 `web.searchProviderId`; * 3. 兜底写 volatile 引用 `config.searchProvider`(不持久化,重启即丢, * 仅在 settings 服务缺席时使用)。 * * @returns 本次调用后后端选择是否指向本插件的 provider。 */ export declare function applyWebSearchSelection(ctx: HostContext, active: boolean, order?: readonly string[]): boolean; /** * 最近一次「把 web.searchProvider 切到本插件」的尝试结果。 * 存于 globalThis 品牌符号:写入发生在任意实例,行重建后的新实例查询诊断 * 也要能看到(模块级变量会随实例消失)。 * * 之所以要记录:写入通道 1 是**异步**的(settings.update 返回 Promise), * 失败只会变成一条 warn,而 warn 在长跑进程里不落盘。没有这份记录就无法 * 区分「写入成功但被别处改回」「写入被拒」与「根本没走通道」。 */ export interface WebSearchSelectionRecord { /** 尝试时间(ISO-8601)。 */ at: string; /** 目标 provider id。 */ target: string; /** 实际走的通道。 */ channel: 'settings' | 'legacy-field' | 'volatile' | 'none'; /** 通道 1 的异步结果;其它通道走完即为 settled。 */ outcome: 'pending' | 'ok' | 'error'; /** 失败原因(已截断)。 */ error?: string; /** 写入前的既有值。 */ before: string | null; /** 记录到的接管前原值(null/缺省 = 未知,卸载时走清空/回落)。 */ original?: string | null; } /** 读取最近一次选路写入结果(诊断用;绝不影响搜索)。 */ export declare function getWebSearchSelectionState(): WebSearchSelectionRecord | undefined; /** * 实时读取当前实际生效的搜索后端 id(诊断用)。 * * 与 selection 记录的区别:记录说的是「上次尝试写了什么」,这个说的是 * 「此刻 WebRuntime 解析到的 configuredId 是什么」。判定接管是否生效必须看它, * 因为写通道可能走 volatile(不落盘到 profile patch),只查 patch 文件会得出 * 「没接管」的错误结论(2026-10-17 实测踩过)。 */ export declare function getConfiguredWebSearchProvider(ctx: HostContext): string | null; /** 本插件 provider 注册是否真的在 WebRuntime 注册表里(诊断用)。 */ export declare function isZhWebProviderRegistered(ctx: HostContext): boolean; /** 读取实时选路快照(诊断用)。 */ export declare function getLiveWebSearchSelection(): { configured: string | null; registered: boolean; at: string; } | undefined; /** * 注册组合搜索 provider。可用性(同步、不发网络请求,per 官方契约): * 开关开启且优先级列表非空即可用。 * @returns 卸载函数;web 服务缺失时返回 undefined。 */ export declare function installWebSearchProvider(ctx: HostContext, deps: WebSearchInstallDeps): (() => void) | undefined; export {}; //# sourceMappingURL=web-search.d.ts.map