DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins
W

dsh-web-search-opencode-ds

Web Search Opencode Ds

Use OpenCode as DeepSeek Harness's web search provider: call DeepSeek's server-side web_search via OpenCode's Anthropic-compatible Messages API, with a built-in settings page.

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:lldwb/dsh-web-search-opencode-ds#42cc5719d91523cdc2076bb73a0c042a2beaf98e
READMECompatibilityVersions

Compatibility and provenance

Web Search Opencode Ds is published as dsh-web-search-opencode-ds and currently resolves to version 0.3.4. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
github
Registry updated
10/27/2026

Versions

0.3.4stable
10/27/2026
0.3.2stable
10/19/2026
0.3.0stable
10/18/2026
Show 1 more versionCollapse versions
0.2.0stable
10/8/2026

Related plugins

Loading related plugins…

Latest
0.3.4
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
web
License
MIT
Source
github
GitHub
★ 0
Weekly downloads
0
Last push
11/5/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in search-research.

Browser Skill Dsh Plugin@wxg-prc-cpg/browser-skill-dsh-pluginDeepSeek Harness tool plugin that exposes BrowserSkill browser automation (bsk_* tools) to the modelAb Wechat Scrapedsh-ab-wechat-scrape--- description: "The out-of-tree wechat_scrape tool: retrieves WeChat official-account articles, filters promotional blocks, and saves each as a Markdown file, for maintainers of this machine's dsh profiles." kind: "package-reference" ---Ab Market Researchdsh-ab-market-researchAn out-of-tree DeepSeek Harness plugin: the trade_search tool (multi-round aspect-broadening query planning, publisher-tier ranking, source diversity, JSON snapshot) and its browser card in one package.Arisdsh-arisARIS research-workflow skills as a DeepSeek Harness bundle: 82 skills plus cross-model adversarial review through Codex.

README

dsh-web-search-opencode-ds

把 OpenCode 注册为 DeepSeek Harness (dsh) 的网页搜索提供方: 让模型侧的 web_search 工具经 OpenCode 调用 DeepSeek 官方的服务端搜索, 并在 dsh 的 设置 → 插件 里提供一张和官方「网页搜索」同款的配置页。

本仓库是一个 dsh bundle,而且是双面的一个:index.js 是 Host 半(注册 opencode-ds provider、 声明 Config schema),client.js 是浏览器半(画那张设置页)。两半由同一个包交付。

名字里没有 go:Go 套餐只是同一个端点家族里的一个选项(goMode 开关),不是这个插件的身份。

包名 / Loader 行 iddsh-web-search-opencode-ds
provider idopencode-ds
设置命名空间web-search-opencode-ds(= Loader 行 id)
默认端点https://opencode.ai/zen/go/v1(Go 套餐,包月)
备用端点https://opencode.ai/zen/v1(按量计费)

为什么是 bundle 而不是另写一个搜索后端:在 dsh 里,ctx.web 这一层已经定义了 WebSearchProvider 契约,DeepSeek 的服务端 web_search 又是 OpenCode 直接提供的原生工具。 插件要做的只是把请求按 OpenCode 的 wire format 发出去、把结果映射回 WebSearchSource, 而不是自己去抓网页、拼摘要。


1. 实测验证结论

以下每一条都是当天对线上端点实打实请求出来的,不是从文档抄的。分两次:初次验证(2026-10-02) 与 DSH 升级到 0.2.1-rc.2 之后的复验(2026-10-18)。

1.1 初次验证(2026-10-02)

观察结果
POST /zen/go/v1/messages + model: deepseek-v4-flash + tools:[{type:'web_search_20250305'}]HTTP 200,返回真实 web_search_tool_result(本次 10 条结果)
同上,但不带 x-opencode-sessionHTTP 400 {"type":"error","error":{"type":"MissingSessionID",...}}
只带 Authorization: Bearer(不带 x-api-key)HTTP 401 AuthError: Missing API key.
只带 x-api-keyHTTP 200
GET /zen/go/v1/models200,43 个模型,含 deepseek-v4-flash
GET /zen/v1/models200,85 个模型,也含 deepseek-v4-flash
POST /zen/v1/messages(按量计费端点)HTTP 402 Insufficient account funds —— 路由与鉴权都通,只是这个账户没有按量余额
候选会话创建路径(/sessions、/session、/zen/v1/sessions…)全部 404 —— 根本没有「先建会话」这一步

1.2 复验(2026-10-18,DSH 0.2.1-rc.2)

观察结果
GET /zen/go/v1/models200,37 个模型(上次 43);deepseek 只剩 deepseek-v4.1-flash、deepseek-v4-pro
GET /zen/v1/models200,85 个模型;含 deepseek-v4-flash、deepseek-v4.1-flash、deepseek-v4.1-flash-free、deepseek-v4-pro
专用检索 API:/search、/fetch、/tools/search、/tools/fetch、/web/search、/web/fetch(两条路由 × GET/POST)全部 404 —— OpenCode 没有 DeepSeek 那套专用 Search/Fetch API
v2 路由家族(/zen/go/v2/models、/zen/v2/models)404 —— 没有版本跃迁
/responses(OpenAI Responses 风格端点)端点存在且返回 200(object: "response"),但不透传原生 web_search
tools:[{type:"web_search"}] / web_search_preview 打到 /responses❌ output 里没有 web_search_call,不搜索
旧凭据已过期(This API key has expired.);换用有效凭据后重测了下面几行

1.3 模型与工具的有效性(2026-10-18,有效凭据)

同一提示词、web_search_20250305、max_uses: 5,只换模型:

模型在 /models 目录里实际执行服务端搜索结果数
deepseek-v4-pro✅✅ 1–2 次 server_tool_use10–20
deepseek-v4.1-flash✅❌ 0 次(只 thinking + text,两种提示词都是 0)0
deepseek-v4-flash❌ 已移除✅ 仍可用20

这两条反直觉结论是本插件最容易踩的坑,务必记住:

  1. 在目录里 ≠ 能用。 deepseek-v4.1-flash 明明在 /models 里,配它却完全不会搜索 —— 模型只是思考一下就作答,静默失去联网能力(不会报错)。
  2. 不在目录里 ≠ 不能用。 deepseek-v4-flash 已从 /models 移除,却仍能搜出 20 条结果。

所以默认值只能取「目录里有 且 实测能搜」的交集 —— 只有 deepseek-v4-pro 满足。 不能用 /models 来推断可用性。

其它同时确认的事实:

  • max_tokens 给太小会毁掉结论:1024 会被 thinking 吃光(stop_reason: max_tokens), 看起来「搜不到结果」其实根本没轮到工具。测搜索至少给 4096。
  • 按量计费(/zen/v1)在无余额账户上对所有模型都返回 402。
  • 工具版本阶梯(web_search_20250415…web_search_20251015、裸 web_search)全部返回 200, 但响应里是 tool_use(客户端工具) 而不是 server_tool_use —— 说明只有 web_search_20250305 被识别为原生服务端工具,其它拼写会被当成普通 function tool。 因此不能用「HTTP 200」来判断某个工具版本是否被支持。

1.4 沙箱回归(2026-10-19,dsh-plugin-devkit)

按工作区约定(AGENTS.md §3),改动插件后要在 devkit 沙箱里做一次真实 Loader 回归——单测(mock ctx)看不见"插件到底加载没有、工具在真实注册表里叫不叫得动"。

环境:devkit_sandbox_create(headless 模板,独立 DSH_HOME,模型 overlay 抄自真实 desktop profile),devkit_sandbox_install 装本插件,未改动真实 ~/.dsh。

第 3 步 · 合成校验(零 token):devkit_sandbox_compose → exit 0 · 431 行,命中 4 行:

# == @deepseek-ai/dsh-base, patched by dsh-web-search-opencode-ds
# == dsh-web-search-opencode-ds
- id: web-search-opencode-ds
  name: dsh-web-search-opencode-ds

第 4 步 · 真实 Loader 回归:在沙箱里跑一轮 headless 任务,逼模型调用 web_search 并逐字回贴原始结果。结果:成功返回 8 条结果(标题 + URL,无错误),凭据注入 DEEPSEEK_API_KEY / OPENCODE_API_KEY / OPENCODE_GO_API_KEY / SUB2API_API_KEY。

这一次回归直接验证了 §1.3 的修正:默认模型 deepseek-v4-pro 真的会执行服务端搜索。若默认值仍是 deepseek-v4.1-flash,回归的表现会是"工具返回 200 但 0 条结果"——即 §1.3 记录的静默失效。

第 5 步:devkit_sandbox_destroy 已清理,devkit-sandboxes 下无残留。

复现(创造模式,需要 dsh-plugin-devkit):

devkit_sandbox_create  { name: "websearch-regress", template: "headless" }
devkit_sandbox_install { name: "websearch-regress", target: "E:\\dsh-plugins\\dsh-web-search-opencode-ds" }
devkit_sandbox_compose { name: "websearch-regress", grep: "web-search-opencode" }
devkit_sandbox_run     { name: "websearch-regress", task: "调用 web_search 查询「DeepSeek Harness 插件」,把原始结果逐字贴出来" }
devkit_sandbox_destroy { name: "websearch-regress" }

由此得到的硬结论

  1. x-opencode-session 是强制头,而且由客户端自己生成。 默认每次搜索新生成一个 UUID v4 (sessionMode: 'per-request'),'stable' 则全程复用同一个。既然没有注册端点、响应里也没有 会话 id,「先创建会话」这条分支不存在,不需要实现。
  2. 真正鉴权的是 x-api-key。 最初的需求写的是只用 Authorization: Bearer,但那样会被 401 拒绝。 本插件两个头都发,这样网关两种行为都活得下来。
  3. 两个端点共用一套 wire format。 goMode 只改 base URL,请求体、头都不变。
  4. 默认模型取 deepseek-v4-pro。 理由见 §1.3:它是唯一同时满足「目录里有」与「实测能搜索」的。
  5. 按量计费端点要账户有余额。 本机账户只有 Go 套餐,所以 /zen/v1 稳定返回 402。 这是账户事实,不是插件 bug —— 插件会把 402 单独翻译成「余额不足,要么充值、要么把 goMode 打回开启」的提示,而不是笼统报一个 HTTP 错误。
  6. 密钥会过期。 过期时正文是 This API key has expired.,去 OpenCode Console 换一把即可; 插件会在 401 文案里点名这一点。

一个必须知道的限制:响应里没有 snippet

web_search_result 条目只带 title、url、page_age,外加一个不透明的 encrypted_content, 没有摘要文本。摘要只可能出现在 text 块的 citations[].cited_text 里。

插件的处理:有 cited_text 就按 url 拼成 snippet,没有就干脆不返回 snippet 字段, 而不是拿标题或正文去伪造一段。搜索结果里 snippet 缺失是预期行为。


2. 安装

需要 dsh 桌面版,且 plugin_manager 可用。

2.1 克隆到本地再安装(推荐,最稳)

git clone https://github.com/lldwb/dsh-web-search-opencode-ds.git
cd dsh-web-search-opencode-ds
pnpm install          # 装唯一的运行时依赖 @deepseek-ai/schemastery,见 §10
plugin_manager { "action": "install_bundle", "target": "<你克隆到的绝对路径>" }

plugin_manager 会把这个包写进当前 profile 的 dsh.profile.bundles,并跑一次 pnpm。

2.2 为什么必须自己跑一次 pnpm install

这是本插件与纯配置型 bundle 最大的不同:它有真实依赖。

plugin_manager install_bundle 对 link: 形式的依赖不会安装被链包自己的依赖, 而 dsh 的 @deepseek-ai/* 包又不从 profile 目录解析(见 §10)。所以 @deepseek-ai/schemastery 必须装进这个包自己的 node_modules,Node 才能相对 index.js 找到它。 忘了这一步的症状是启动时报 Cannot find module '@deepseek-ai/schemastery'。

pnpm-lock.yaml 已随仓库提交,所以装到的版本是确定的。

2.3 本地开发时

profile 用 link: 依赖指向工作副本:

// ~/.dsh/profiles/<profile>/package.json
"dependencies": { "dsh-web-search-opencode-ds": "link:E:/dsh-plugins/dsh-web-search-opencode-ds" },
"dsh": { "profile": { "bundles": [ /* … */ "dsh-web-search-opencode-ds" ] } }

2.4 改动如何生效

  • Host 半 / patch 改动:plugin_manager set_bundle 先 enabled:false 再 enabled:true, 会返回 "application":"applied" 并热应用,不需要重启。 (install_bundle 在无变化时返回 "restart-required",那个不会热应用。)
  • Client 半改动:client 插件的 HMR 接收器是活的,会直接重载;只有需要重建 bundle 时才要刷新页面。

3. 凭据配置

provider 在每次搜索前重新解析密钥,所以轮换密钥不需要重启。

方式 A:写进 DSH 凭据库(推荐)

在插件的设置页里填 API Key 并保存即可 —— 它走的是 DSH 的 credentials 域,不会写进设置文件。

也可以直接写 ~/.dsh/.credentials.yaml 的 refs::

refs:
  OPENCODE_API_KEY: sk-...

两个引用名都会被尝试,顺序是:设置里 apiKeyEnv 指定的那个 → OPENCODE_API_KEY → OPENCODE_GO_API_KEY。所以旧版本存在 OPENCODE_GO_API_KEY 下的密钥改名后照样能用。

方式 B:进程环境变量

export OPENCODE_API_KEY=sk-...        # 或 OPENCODE_GO_API_KEY

方式 C:插件 config 字面量

在 cordis.patch.yml 里给这一行加 apiKey: sk-...。字面量优先级最高,但会明文落在配置文件里, 不建议。


4. 设置页(GUI)

装好后打开 设置 → 插件,官方「网页搜索」卡片下面会多出一张 「网页搜索(OpenCode)」(plugins.item 槽位,order: 41,官方那张是 40)。

页面上有五个控件:

控件字段说明
API KeyapiKey只写不读。写进凭据库;留空表示不改动现有密钥
模型model决定实际用哪个模型;留空用默认 deepseek-v4-pro
接口地址baseURL留空则按下面的模式自动决定
单次请求最多搜索次数maxUses一次请求在必须作答前最多搜几次
覆盖官方网页搜索配置overrideOfficial见 §4.1
使用 OpenCode Go 套餐goMode见 §4.2

页面只在 Host 真的提供了这个命名空间时才挂载,所以没装插件的部署上看不到任何痕迹。

为什么有「模型」这一格:OpenCode 的模型目录会换代。2026-10-18 实测 Go 套餐已下架 deepseek-v4-flash,旧默认值直接失效。把模型放进 UI,换代时你自己就能改,不必去编辑 cordis.patch.yml。

4.1 开关一:是否覆盖官方配置(overrideOfficial)

这个开关不需要重启,立即生效,因为 provider 在每次搜索时才决定怎么走:

取值行为
开(默认)用本页配置的端点与密钥,自己调 OpenCode
关把搜索原样转交给随附的 deepseek-official provider

「关」的意义是:官方那套配置(接口地址 https://api.deepseek.com/anthropic、DEEPSEEK_API_KEY、 它自己的搜索预算)继续说了算,本插件完全不插手。

实现方式是在 search() 里把请求委托给 ctx.web.searchProviders.get('deepseek-official'), 而不是去改 web.searchProvider。

关于 web.searchProvider 的一个重要更新。 在 DSH 0.2.0 里它是普通 z.string(),启动时读一次 就固化,运行时改不动 —— 这是当初选择「委托」的原因之一。DSH 0.2.1 起它变成了 z.string().volatile(),并且每次调用时用 this.config.searchProvider.get() 重新读取, 也就是说现在可以直接热改了。

即便如此,这里仍然保留委托:委托不改用户配置、不需要写 web 行、也不会在切换时留下 一个指向本插件 id 的持久值。代价是同一个开关有两种实现路径,所以 §12 写了它们与官方设置页开关的 交互。

如果 deepseek-official 没有挂载,search() 会明确报错说明「没有可转交的对象」,而不是静默失败。

4.2 开关二:Go 模式 vs 按量计费(goMode)

取值端点计费
开(默认)https://opencode.ai/zen/go/v1OpenCode Go 包月套餐
关https://opencode.ai/zen/v1默认按量计费 API,按次扣费

注意:按量计费端点需要账户有余额。没有余额时它会回 HTTP 402,插件会把这条错误单独 翻译成「余额不足」的说明。如果你的账户只有 Go 套餐,就保持这个开关开启。

接口地址栏如果填了具体值,永远优先于这个开关(留空才是「按模式决定」)。


5. 配置项参考

设置页只能改 .volatile() 字段;下表其余项是加载期配置,只能写 cordis.patch.yml。

字段默认值volatile说明
apiKey—✅(secret)字面量密钥,优先级最高,不会被表单持久化
apiKeyEnvOPENCODE_API_KEY✅(credential-ref)凭据引用名
baseURL空✅空 = 按 goMode 决定
modeldeepseek-v4-pro✅Anthropic 格式模型名;唯一「目录内有且实测能搜索」的
apiVersion2023-06-01✅anthropic-version 头
maxTokens4096✅生成上限
maxUses5✅服务端搜索次数上限
timeoutMs30000✅单次尝试超时
includeAnswerfalse✅是否把模型正文放进 content
overrideOfficialtrue✅见 §4.1
goModetrue✅见 §4.2
retries1❌首次之外的重试次数
sessionModeper-request❌per-request | stable
queryTemplatePerform a web search for the query: {query}❌提示词模板

重试策略

重试的状态码:408 409 425 429 500 502 503 504。网络错误、超时,以及 400 MissingSessionID 也算可重试。401/403 和 402 不重试 —— 重试不会让密钥变好,也不会让账户有钱。 退避为 500ms × 尝试序号,且可被调用方的 AbortSignal 打断。


6. DSH 如何选中这个 provider

ctx.web 在每次调用时解析提供方:

  • 配置了 id 且已注册且 available() → 用它
  • 配置了 id 但没注册 → WEB_PROVIDER_CONFIGURED_MISSING
  • 配置了 id 但不可用 → WEB_PROVIDER_CONFIGURED_UNAVAILABLE
  • 没配 id,恰好一个可用 → 用它
  • 没配 id,多个可用 → WEB_PROVIDER_AMBIGUOUS
  • 没配 id,没有可用 → WEB_PROVIDER_UNAVAILABLE

本插件的 bundle patch 把 web.searchProvider 初始化为 opencode-ds,所以开箱即用就是它; 「用不用官方配置」再交给 overrideOfficial 决定。

注意:DSH 0.2.1 起 searchProvider 是 .volatile(),运行时可以改。所以这个固定值只是 初始值,不是不可动摇的恒等式 —— 用户(或官方网页搜索页的开关)随时可能把它改掉。 见 §12.1。

⚠️ id 定向 patch 会整体替换该行的 config,不做深合并。 随附的 web 行目前只声明了 searchProvider / fetchProvider 两个键,所以只写 searchProvider 不会丢东西。但如果更高 优先级的层(比如 profile 自己的 cordis.patch.yml)给 web 行加了 fetchProvider, 那一层必须同时重写两个字段。

层序:所有 bundle patch → profile 自己的 cordis.patch.yml → --patch 覆盖。 所以 profile 里若也写了 web 行,会覆盖本 bundle 的固定值。

环境变量覆盖

DSH_WEB_SEARCH_PROVIDER=opencode-ds 与配置里的 searchProvider 等价(不是隐藏的优先级链)。


7. 回退到随附的 deepseek-official

三种方式,任选一种:

  1. 最省事:设置页里把「覆盖官方网页搜索配置」关掉。官方的端点与密钥立刻重新生效。
  2. 把 profile 的 cordis.patch.yml 里加上 web 行并把 searchProvider 改成 deepseek-official。
  3. plugin_manager set_bundle dsh-web-search-opencode-ds enabled:false 整个停用。

8. 自检

端点验证脚本

# bash / Git Bash / WSL
OPENCODE_API_KEY=sk-... ./scripts/probe-opencode.sh "你的查询" go
OPENCODE_API_KEY=sk-... ./scripts/probe-opencode.sh "你的查询" metered
# Windows PowerShell
$env:OPENCODE_API_KEY = 'sk-...'
.\scripts\probe-opencode.ps1 "你的查询" go
.\scripts\probe-opencode.ps1 "你的查询" metered

脚本会断言 200 / 400 / 401 三种情形,再检查响应里有没有 web_search_tool_result、 server_tool_use、usage.server_tool_use.web_search_requests。

  • 退出码 0 = 全部符合预期(按量模式下的 402 会单独标成 UNFUNDED(402) 并仍然退 0)
  • 1 = 有偏差
  • 2 = 缺凭据或缺工具

单元测试(不需要网络)

node --test test/parse.test.mjs      # 40 个用例

确认某次搜索到底是谁服务的

随附的 deepseek-official 在派发前会往会话里追加 web/deepseek-search-llm-request 事件, 本插件从不写这个事件。所以在会话日志里 (~/.dsh/sessions/<slug>/<sessionId>/session.v4.jsonl.zstd,注意是多帧 zstd, 每个 append 一帧,必须按帧魔数 28 B5 2F FD 切开分别解压):

搜索产生了结果,而这次调用没有对应的 web/deepseek-search-llm-request ⇒ 就是本插件服务的。


9. 故障排查

现象原因与处理
页面压根不出现Host 没提供命名空间。Config.listConfigs 里该行 status 必须是 "schema"(不是 "absent")。"absent" 意味着插件没声明 Config。
页面出现但插件列表里没有Loader 没重读 profile。set_bundle 关再开一次。
WEB_PROVIDER_CONFIGURED_MISSING固定了 opencode-ds 但插件没注册上 —— 看 Host 日志里插件是否加载失败。
401密钥不对,或者用错了模式(Go 套餐的密钥不等于按量计费可用)。
402按量计费端点余额不足。充值,或把 goMode 打回开启。
400 MissingSessionIDx-opencode-session 被中途剥掉了。插件每次都生成,看到这个说明有代理在改请求。
抓不到 snippet预期行为,见 §1。响应里本来就没有摘要。
Cannot find module '@deepseek-ai/schemastery'插件目录里没跑过 pnpm install,见 §2。

10. 设计说明

为什么除了 schemastery 之外必须是零 @deepseek-ai/* 依赖。 DSH 自己的包不从 profile 目录解析(@deepseek-ai/dsh-web、@deepseek-ai/dsh-credentials 在 profile 下都是 MODULE_NOT_FOUND),所以 profile 装的插件 import 不到它们。 @deepseek-ai/schemastery 是公开发布的 MIT 包,装进本包自己的 node_modules, 是 Node 相对本文件唯一能解析到的那个 —— 而声明 Config 又必须要它。

为什么 Config 用 .volatile()。 DSH 的设置服务只把活跃 profile 条目上的 volatile 字段投影成表单,并通过 profile 的 Cordis patch 持久化。没有 Config → 没有命名空间 → 设置页无从挂载;字段不 volatile → 表单里看不到。 所以「能不能配」完全由 schema 上的 .volatile() 决定。

为什么 configure({ auto: false })。 自带页面的插件要显式关掉 schema 自动生成页面。它注册在 ctx.inject(['settings'], …) 的可选子级里 —— 这样没有 settings 服务的 profile 也能正常跑, 而且策略绑在插件自己的 fiber 上,Settings 服务迟到或换实例也会采用。

为什么「覆盖官方配置」用委托而不是改配置。 最初(DSH 0.2.0)web.searchProvider 不是 volatile、构造时固化、运行时改不动,改成别的 id 还会在 两个 provider 都可用时撞上 WEB_PROVIDER_AMBIGUOUS,所以委托是唯一可行解。DSH 0.2.1 起它变成 了 volatile(每次调用重新读取),改配置这条路已可行,但委托仍然更好:它不动用户的 web 行、 切换后不留下一个指向本插件 id 的持久值,而且「谁在服务」这件事始终由本插件自己的开关决定。 代价是要在 §12.1 里把两条路径的交互讲清楚。

为什么结果不二次截断。 maxResults 的裁剪归 ctx.web 这一层管(capSources),所以插件的 truncated 恒为 false, 不会把本来正确的长度再砍一刀。


11. 参考

  • OpenCode Go 文档:https://opencode.ai/docs/go/
  • OpenCode Zen 文档:https://opencode.ai/docs/zen/
  • web_search_20250305 工具语义(Anthropic Messages)
  • DSH ctx.web 服务契约(WebSearchProvider / WebSearchSource)
  • DSH 设置子系统:@deepseek-ai/dsh-settings、@deepseek-ai/dsh-client-ui-settings-web-search

12. 已知限制

12.1 与官方「网页搜索」设置页的交互(DSH 0.2.1 起)

DSH 0.2.1 重写了官方网页搜索设置页:它现在多了一个 「使用 Search API」开关,而这个开关 直接写 web 行的 searchProvider / fetchProvider:

开关位置写入的 searchProvider写入的 fetchProvider
关(Messages 路径)deepseek-officialhttp
开(Search API 路径)deepseek-search-apideepseek-fetch-api

这带来两个后果,务必知道:

  1. 本插件的 pin 会被这个开关覆盖。 开关的判定是 web.searchProvider === "deepseek-search-api"。本插件把该值设为 opencode-ds,所以官方页面 会把它显示成「关」;你在那个页面上拨动一次开关,searchProvider 就会被改成 deepseek-*, 本插件随即不再被选中(搜索会改走官方后端,而不是报错)。
    • 想切回本插件:把 web.searchProvider 改回 opencode-ds(设置 → 插件里编辑,或写 profile 的 `cordis.patch.yml)。
    • 只想临时用官方配置、又不放弃本插件:用本插件的 overrideOfficial 关掉(§4.1), 它会把请求委托给 deepseek-official,且不改 web 行。
  2. 「使用 Search API」这个开关对本插件无效。 它选的是 DSH 新加的 dsh-web-deepseek-api 后端 —— 那是 DeepSeek 官方的专用 Search/Fetch API (https://api.deepseek.com/v1/tools + /search、/fetch,不走完整模型轮次)。 OpenCode 没有等价的专用 API:§1.2 里所有候选路径实测都是 404。所以本插件只能用 Messages + web_search_20250305 这一条路。

12.2 其它限制

  • 配置整块替换:Cordis 的 id 定向 patch 会替换该行的整个 config,不做深合并。 bundle 里把 web.searchProvider 初始化为 opencode-ds;如果你自己的 cordis.patch.yml 里也有 web 行,它层级更高,会整体覆盖本 bundle 的值(包括把 fetchProvider 一起抹掉)。 此时请把 searchProvider 一并写进你自己的那一行,或删掉自己的行交给本 bundle 管。
  • 响应里没有摘要:OpenCode 返回的 web_search_result 只有 title / url / page_age, 没有 snippet。插件不会伪造摘要,所以结果里 snippet 缺失是预期行为(见 §1)。 想要摘要就把 includeAnswer 打开,模型正文会作为 WebSearchResult.content 返回。
  • 按量计费端点要余额:goMode: false 走 /zen/v1,账户没钱会返回 HTTP 402。 只有 Go 套餐的账号请保持 goMode 开启。
  • 模型会换代,且目录不可信:目录里有不代表会搜索(deepseek-v4.1-flash 就不搜),不在目录里也可能仍能用(deepseek-v4-flash 仍可)。换模型后请确认搜索真的发生了(看结果里有没有 Sources)。 插件不预先校验,写错的模型要到实际调用才报错 —— 但那时错误信息会点名当前模型、当前端点, 并让你去查该端点的 /models。日常改模型用设置页的「模型」一格即可。
  • 密钥会过期:过期正文是 This API key has expired.,去 OpenCode Console 换一把。
  • Go / 按量是两个独立权益:同一个 OpenCode 账号上,Go 套餐的密钥不等于按量计费可用,反之亦然。
  • 端点行为、模型目录都可能随时变动;§1 的实测结论标注了日期,请以你的实际请求为准。

License

MIT — 见 LICENSE。